summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorMaarten Lankhorst <maarten.lankhorst@linux.intel.com>2018-05-11 18:08:10 +0200
committerMaarten Lankhorst <maarten.lankhorst@linux.intel.com>2018-05-11 18:08:10 +0200
commit94cc2fde365fb4484080ea6675bb1e0c933f8002 (patch)
treea249c6f6b12ff2dbe39d78bfb050e9c28619bee9 /drivers
parent900aa8ad21587e909603f471b6cd81fd5338ec45 (diff)
parent8eb008c80841e3410ef2c043093478ea36bb5ff1 (diff)
downloadlinux-stable-94cc2fde365fb4484080ea6675bb1e0c933f8002.tar.gz
linux-stable-94cc2fde365fb4484080ea6675bb1e0c933f8002.tar.bz2
linux-stable-94cc2fde365fb4484080ea6675bb1e0c933f8002.zip
Merge remote-tracking branch 'drm/drm-next' into drm-misc-next
drm-misc-next is still based on v4.16-rc7, and was getting a bit stale. Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig4
-rw-r--r--drivers/acpi/Kconfig21
-rw-r--r--drivers/acpi/Makefile1
-rw-r--r--drivers/acpi/ac.c2
-rw-r--r--drivers/acpi/acpi_pad.c3
-rw-r--r--drivers/acpi/acpi_tad.c473
-rw-r--r--drivers/acpi/acpi_video.c27
-rw-r--r--drivers/acpi/acpi_watchdog.c59
-rw-r--r--drivers/acpi/acpica/acapps.h38
-rw-r--r--drivers/acpi/acpica/accommon.h38
-rw-r--r--drivers/acpi/acpica/acconvert.h38
-rw-r--r--drivers/acpi/acpica/acdebug.h38
-rw-r--r--drivers/acpi/acpica/acdispat.h38
-rw-r--r--drivers/acpi/acpica/acevents.h56
-rw-r--r--drivers/acpi/acpica/acglobal.h38
-rw-r--r--drivers/acpi/acpica/achware.h38
-rw-r--r--drivers/acpi/acpica/acinterp.h38
-rw-r--r--drivers/acpi/acpica/aclocal.h38
-rw-r--r--drivers/acpi/acpica/acmacros.h42
-rw-r--r--drivers/acpi/acpica/acnamesp.h38
-rw-r--r--drivers/acpi/acpica/acobject.h38
-rw-r--r--drivers/acpi/acpica/acopcode.h52
-rw-r--r--drivers/acpi/acpica/acparser.h38
-rw-r--r--drivers/acpi/acpica/acpredef.h38
-rw-r--r--drivers/acpi/acpica/acresrc.h38
-rw-r--r--drivers/acpi/acpica/acstruct.h38
-rw-r--r--drivers/acpi/acpica/actables.h38
-rw-r--r--drivers/acpi/acpica/acutils.h38
-rw-r--r--drivers/acpi/acpica/amlcode.h58
-rw-r--r--drivers/acpi/acpica/amlresrc.h38
-rw-r--r--drivers/acpi/acpica/dbcmds.c38
-rw-r--r--drivers/acpi/acpica/dbconvert.c38
-rw-r--r--drivers/acpi/acpica/dbdisply.c43
-rw-r--r--drivers/acpi/acpica/dbexec.c38
-rw-r--r--drivers/acpi/acpica/dbfileio.c38
-rw-r--r--drivers/acpi/acpica/dbhistry.c40
-rw-r--r--drivers/acpi/acpica/dbinput.c38
-rw-r--r--drivers/acpi/acpica/dbmethod.c38
-rw-r--r--drivers/acpi/acpica/dbnames.c38
-rw-r--r--drivers/acpi/acpica/dbobject.c38
-rw-r--r--drivers/acpi/acpica/dbstats.c38
-rw-r--r--drivers/acpi/acpica/dbtest.c38
-rw-r--r--drivers/acpi/acpica/dbutils.c38
-rw-r--r--drivers/acpi/acpica/dbxface.c38
-rw-r--r--drivers/acpi/acpica/dsargs.c41
-rw-r--r--drivers/acpi/acpica/dscontrol.c38
-rw-r--r--drivers/acpi/acpica/dsdebug.c38
-rw-r--r--drivers/acpi/acpica/dsfield.c40
-rw-r--r--drivers/acpi/acpica/dsinit.c38
-rw-r--r--drivers/acpi/acpica/dsmethod.c40
-rw-r--r--drivers/acpi/acpica/dsmthdat.c38
-rw-r--r--drivers/acpi/acpica/dsobject.c38
-rw-r--r--drivers/acpi/acpica/dsopcode.c44
-rw-r--r--drivers/acpi/acpica/dspkginit.c204
-rw-r--r--drivers/acpi/acpica/dsutils.c38
-rw-r--r--drivers/acpi/acpica/dswexec.c44
-rw-r--r--drivers/acpi/acpica/dswload.c40
-rw-r--r--drivers/acpi/acpica/dswload2.c38
-rw-r--r--drivers/acpi/acpica/dswscope.c40
-rw-r--r--drivers/acpi/acpica/dswstate.c40
-rw-r--r--drivers/acpi/acpica/evevent.c47
-rw-r--r--drivers/acpi/acpica/evglock.c38
-rw-r--r--drivers/acpi/acpica/evgpe.c282
-rw-r--r--drivers/acpi/acpica/evgpeblk.c62
-rw-r--r--drivers/acpi/acpica/evgpeinit.c38
-rw-r--r--drivers/acpi/acpica/evgpeutil.c38
-rw-r--r--drivers/acpi/acpica/evhandler.c38
-rw-r--r--drivers/acpi/acpica/evmisc.c38
-rw-r--r--drivers/acpi/acpica/evregion.c40
-rw-r--r--drivers/acpi/acpica/evrgnini.c45
-rw-r--r--drivers/acpi/acpica/evsci.c38
-rw-r--r--drivers/acpi/acpica/evxface.c49
-rw-r--r--drivers/acpi/acpica/evxfevnt.c40
-rw-r--r--drivers/acpi/acpica/evxfgpe.c61
-rw-r--r--drivers/acpi/acpica/evxfregn.c38
-rw-r--r--drivers/acpi/acpica/exconcat.c38
-rw-r--r--drivers/acpi/acpica/exconfig.c38
-rw-r--r--drivers/acpi/acpica/exconvrt.c39
-rw-r--r--drivers/acpi/acpica/excreate.c40
-rw-r--r--drivers/acpi/acpica/exdebug.c45
-rw-r--r--drivers/acpi/acpica/exdump.c38
-rw-r--r--drivers/acpi/acpica/exfield.c38
-rw-r--r--drivers/acpi/acpica/exfldio.c40
-rw-r--r--drivers/acpi/acpica/exmisc.c38
-rw-r--r--drivers/acpi/acpica/exmutex.c40
-rw-r--r--drivers/acpi/acpica/exnames.c45
-rw-r--r--drivers/acpi/acpica/exoparg1.c38
-rw-r--r--drivers/acpi/acpica/exoparg2.c38
-rw-r--r--drivers/acpi/acpica/exoparg3.c38
-rw-r--r--drivers/acpi/acpica/exoparg6.c38
-rw-r--r--drivers/acpi/acpica/exprep.c38
-rw-r--r--drivers/acpi/acpica/exregion.c40
-rw-r--r--drivers/acpi/acpica/exresnte.c38
-rw-r--r--drivers/acpi/acpica/exresolv.c38
-rw-r--r--drivers/acpi/acpica/exresop.c39
-rw-r--r--drivers/acpi/acpica/exstore.c38
-rw-r--r--drivers/acpi/acpica/exstoren.c38
-rw-r--r--drivers/acpi/acpica/exstorob.c38
-rw-r--r--drivers/acpi/acpica/exsystem.c40
-rw-r--r--drivers/acpi/acpica/extrace.c40
-rw-r--r--drivers/acpi/acpica/exutils.c40
-rw-r--r--drivers/acpi/acpica/hwacpi.c38
-rw-r--r--drivers/acpi/acpica/hwesleep.c40
-rw-r--r--drivers/acpi/acpica/hwgpe.c41
-rw-r--r--drivers/acpi/acpica/hwpci.c38
-rw-r--r--drivers/acpi/acpica/hwregs.c38
-rw-r--r--drivers/acpi/acpica/hwsleep.c49
-rw-r--r--drivers/acpi/acpica/hwtimer.c40
-rw-r--r--drivers/acpi/acpica/hwvalid.c38
-rw-r--r--drivers/acpi/acpica/hwxface.c40
-rw-r--r--drivers/acpi/acpica/hwxfsleep.c42
-rw-r--r--drivers/acpi/acpica/nsaccess.c38
-rw-r--r--drivers/acpi/acpica/nsalloc.c38
-rw-r--r--drivers/acpi/acpica/nsarguments.c38
-rw-r--r--drivers/acpi/acpica/nsconvert.c40
-rw-r--r--drivers/acpi/acpica/nsdump.c40
-rw-r--r--drivers/acpi/acpica/nsdumpdv.c43
-rw-r--r--drivers/acpi/acpica/nseval.c68
-rw-r--r--drivers/acpi/acpica/nsinit.c45
-rw-r--r--drivers/acpi/acpica/nsload.c62
-rw-r--r--drivers/acpi/acpica/nsnames.c41
-rw-r--r--drivers/acpi/acpica/nsobject.c38
-rw-r--r--drivers/acpi/acpica/nsparse.c74
-rw-r--r--drivers/acpi/acpica/nspredef.c38
-rw-r--r--drivers/acpi/acpica/nsprepkg.c38
-rw-r--r--drivers/acpi/acpica/nsrepair.c40
-rw-r--r--drivers/acpi/acpica/nsrepair2.c40
-rw-r--r--drivers/acpi/acpica/nssearch.c38
-rw-r--r--drivers/acpi/acpica/nsutils.c40
-rw-r--r--drivers/acpi/acpica/nswalk.c38
-rw-r--r--drivers/acpi/acpica/nsxfeval.c38
-rw-r--r--drivers/acpi/acpica/nsxfname.c59
-rw-r--r--drivers/acpi/acpica/nsxfobj.c38
-rw-r--r--drivers/acpi/acpica/psargs.c44
-rw-r--r--drivers/acpi/acpica/psloop.c66
-rw-r--r--drivers/acpi/acpica/psobject.c48
-rw-r--r--drivers/acpi/acpica/psopcode.c38
-rw-r--r--drivers/acpi/acpica/psopinfo.c38
-rw-r--r--drivers/acpi/acpica/psparse.c40
-rw-r--r--drivers/acpi/acpica/psscope.c40
-rw-r--r--drivers/acpi/acpica/pstree.c39
-rw-r--r--drivers/acpi/acpica/psutils.c40
-rw-r--r--drivers/acpi/acpica/pswalk.c38
-rw-r--r--drivers/acpi/acpica/psxface.c38
-rw-r--r--drivers/acpi/acpica/rsaddr.c38
-rw-r--r--drivers/acpi/acpica/rscalc.c38
-rw-r--r--drivers/acpi/acpica/rscreate.c38
-rw-r--r--drivers/acpi/acpica/rsdump.c38
-rw-r--r--drivers/acpi/acpica/rsdumpinfo.c38
-rw-r--r--drivers/acpi/acpica/rsinfo.c38
-rw-r--r--drivers/acpi/acpica/rsio.c38
-rw-r--r--drivers/acpi/acpica/rsirq.c38
-rw-r--r--drivers/acpi/acpica/rslist.c38
-rw-r--r--drivers/acpi/acpica/rsmemory.c38
-rw-r--r--drivers/acpi/acpica/rsmisc.c38
-rw-r--r--drivers/acpi/acpica/rsserial.c38
-rw-r--r--drivers/acpi/acpica/rsutils.c38
-rw-r--r--drivers/acpi/acpica/rsxface.c38
-rw-r--r--drivers/acpi/acpica/tbdata.c58
-rw-r--r--drivers/acpi/acpica/tbfadt.c40
-rw-r--r--drivers/acpi/acpica/tbfind.c38
-rw-r--r--drivers/acpi/acpica/tbinstal.c44
-rw-r--r--drivers/acpi/acpica/tbprint.c40
-rw-r--r--drivers/acpi/acpica/tbutils.c40
-rw-r--r--drivers/acpi/acpica/tbxface.c40
-rw-r--r--drivers/acpi/acpica/tbxfload.c51
-rw-r--r--drivers/acpi/acpica/tbxfroot.c38
-rw-r--r--drivers/acpi/acpica/utaddress.c38
-rw-r--r--drivers/acpi/acpica/utalloc.c38
-rw-r--r--drivers/acpi/acpica/utascii.c40
-rw-r--r--drivers/acpi/acpica/utbuffer.c38
-rw-r--r--drivers/acpi/acpica/utcache.c45
-rw-r--r--drivers/acpi/acpica/utcopy.c40
-rw-r--r--drivers/acpi/acpica/utdebug.c40
-rw-r--r--drivers/acpi/acpica/utdecode.c40
-rw-r--r--drivers/acpi/acpica/utdelete.c58
-rw-r--r--drivers/acpi/acpica/uterror.c38
-rw-r--r--drivers/acpi/acpica/uteval.c38
-rw-r--r--drivers/acpi/acpica/utexcep.c38
-rw-r--r--drivers/acpi/acpica/utglobal.c38
-rw-r--r--drivers/acpi/acpica/uthex.c40
-rw-r--r--drivers/acpi/acpica/utids.c38
-rw-r--r--drivers/acpi/acpica/utinit.c38
-rw-r--r--drivers/acpi/acpica/utlock.c40
-rw-r--r--drivers/acpi/acpica/utmath.c38
-rw-r--r--drivers/acpi/acpica/utmisc.c38
-rw-r--r--drivers/acpi/acpica/utmutex.c38
-rw-r--r--drivers/acpi/acpica/utnonansi.c38
-rw-r--r--drivers/acpi/acpica/utobject.c40
-rw-r--r--drivers/acpi/acpica/utosi.c40
-rw-r--r--drivers/acpi/acpica/utownerid.c38
-rw-r--r--drivers/acpi/acpica/utpredef.c40
-rw-r--r--drivers/acpi/acpica/utprint.c40
-rw-r--r--drivers/acpi/acpica/utresdecode.c38
-rw-r--r--drivers/acpi/acpica/utresrc.c38
-rw-r--r--drivers/acpi/acpica/utstate.c38
-rw-r--r--drivers/acpi/acpica/utstring.c38
-rw-r--r--drivers/acpi/acpica/utstrsuppt.c38
-rw-r--r--drivers/acpi/acpica/utstrtoul64.c38
-rw-r--r--drivers/acpi/acpica/uttrack.c40
-rw-r--r--drivers/acpi/acpica/utuuid.c38
-rw-r--r--drivers/acpi/acpica/utxface.c40
-rw-r--r--drivers/acpi/acpica/utxferror.c38
-rw-r--r--drivers/acpi/acpica/utxfinit.c86
-rw-r--r--drivers/acpi/acpica/utxfmutex.c38
-rw-r--r--drivers/acpi/arm64/iort.c123
-rw-r--r--drivers/acpi/battery.c174
-rw-r--r--drivers/acpi/battery.h11
-rw-r--r--drivers/acpi/bus.c6
-rw-r--r--drivers/acpi/button.c24
-rw-r--r--drivers/acpi/cppc_acpi.c27
-rw-r--r--drivers/acpi/custom_method.c2
-rw-r--r--drivers/acpi/device_pm.c11
-rw-r--r--drivers/acpi/fan.c2
-rw-r--r--drivers/acpi/nfit/core.c706
-rw-r--r--drivers/acpi/nfit/mce.c5
-rw-r--r--drivers/acpi/nfit/nfit.h22
-rw-r--r--drivers/acpi/osi.c9
-rw-r--r--drivers/acpi/osl.c5
-rw-r--r--drivers/acpi/pci_link.c4
-rw-r--r--drivers/acpi/pci_root.c21
-rw-r--r--drivers/acpi/pmic/tps68470_pmic.c10
-rw-r--r--drivers/acpi/processor_perflib.c11
-rw-r--r--drivers/acpi/sbs.c2
-rw-r--r--drivers/acpi/scan.c38
-rw-r--r--drivers/acpi/sleep.c41
-rw-r--r--drivers/acpi/tables.c2
-rw-r--r--drivers/acpi/video_detect.c9
-rw-r--r--drivers/amba/bus.c17
-rw-r--r--drivers/android/binder.c8
-rw-r--r--drivers/ata/Kconfig25
-rw-r--r--drivers/ata/Makefile2
-rw-r--r--drivers/ata/ahci.h1
-rw-r--r--drivers/ata/ahci_imx.c371
-rw-r--r--drivers/ata/ahci_tegra.c359
-rw-r--r--drivers/ata/libahci_platform.c24
-rw-r--r--drivers/ata/libata-core.c48
-rw-r--r--drivers/ata/libata-transport.c4
-rw-r--r--drivers/ata/libata.h2
-rw-r--r--drivers/ata/pata_arasan_cf.c6
-rw-r--r--drivers/ata/pata_bf54x.c1703
-rw-r--r--drivers/ata/pata_bk3710.c8
-rw-r--r--drivers/ata/pata_falcon.c8
-rw-r--r--drivers/ata/pata_gayle.c219
-rw-r--r--drivers/ata/pata_it821x.c6
-rw-r--r--drivers/ata/pata_macio.c12
-rw-r--r--drivers/ata/pata_mpc52xx.c1
-rw-r--r--drivers/ata/pata_samsung_cf.c4
-rw-r--r--drivers/ata/sata_dwc_460ex.c1
-rw-r--r--drivers/atm/idt77252.c12
-rw-r--r--drivers/atm/iphase.c6
-rw-r--r--drivers/base/Makefile5
-rw-r--r--drivers/base/arch_topology.c12
-rw-r--r--drivers/base/core.c25
-rw-r--r--drivers/base/cpu.c4
-rw-r--r--drivers/base/dd.c3
-rw-r--r--drivers/base/devcon.c136
-rw-r--r--drivers/base/devtmpfs.c11
-rw-r--r--drivers/base/dma-coherent.c5
-rw-r--r--drivers/base/dma-mapping.c6
-rw-r--r--drivers/base/firmware_class.c1940
-rw-r--r--drivers/base/firmware_loader/Makefile7
-rw-r--r--drivers/base/firmware_loader/fallback.c675
-rw-r--r--drivers/base/firmware_loader/fallback.h67
-rw-r--r--drivers/base/firmware_loader/fallback_table.c55
-rw-r--r--drivers/base/firmware_loader/firmware.h115
-rw-r--r--drivers/base/firmware_loader/main.c1243
-rw-r--r--drivers/base/memory.c47
-rw-r--r--drivers/base/node.c28
-rw-r--r--drivers/base/platform.c4
-rw-r--r--drivers/base/power/power.h1
-rw-r--r--drivers/base/power/wakeirq.c13
-rw-r--r--drivers/base/regmap/regmap-debugfs.c20
-rw-r--r--drivers/base/regmap/regmap-i2c.c4
-rw-r--r--drivers/base/regmap/regmap.c306
-rw-r--r--drivers/base/soc.c2
-rw-r--r--drivers/bcma/Kconfig2
-rw-r--r--drivers/bcma/driver_chipcommon_pmu.c2
-rw-r--r--drivers/bcma/host_pci.c1
-rw-r--r--drivers/block/brd.c1
-rw-r--r--drivers/block/drbd/drbd_main.c3
-rw-r--r--drivers/block/drbd/drbd_nl.c4
-rw-r--r--drivers/block/loop.c186
-rw-r--r--drivers/block/loop.h1
-rw-r--r--drivers/block/mtip32xx/mtip32xx.c8
-rw-r--r--drivers/block/nbd.c8
-rw-r--r--drivers/block/null_blk.c124
-rw-r--r--drivers/block/paride/pcd.c2
-rw-r--r--drivers/block/rbd.c2558
-rw-r--r--drivers/block/rsxx/dev.c6
-rw-r--r--drivers/block/skd_main.c4
-rw-r--r--drivers/block/swim.c49
-rw-r--r--drivers/block/swim3.c6
-rw-r--r--drivers/block/umem.c7
-rw-r--r--drivers/block/xen-blkfront.c10
-rw-r--r--drivers/block/zram/zram_drv.c17
-rw-r--r--drivers/block/zram/zram_drv.h17
-rw-r--r--drivers/bluetooth/Kconfig30
-rw-r--r--drivers/bluetooth/Makefile3
-rw-r--r--drivers/bluetooth/ath3k.c28
-rw-r--r--drivers/bluetooth/bpa10x.c2
-rw-r--r--drivers/bluetooth/btmrvl_main.c2
-rw-r--r--drivers/bluetooth/btmrvl_sdio.c4
-rw-r--r--drivers/bluetooth/btrsi.c187
-rw-r--r--drivers/bluetooth/btrtl.c119
-rw-r--r--drivers/bluetooth/btuart_cs.c675
-rw-r--r--drivers/bluetooth/btusb.c14
-rw-r--r--drivers/bluetooth/h4_recv.h160
-rw-r--r--drivers/bluetooth/hci_ath.c4
-rw-r--r--drivers/bluetooth/hci_bcm.c305
-rw-r--r--drivers/bluetooth/hci_ll.c224
-rw-r--r--drivers/bus/Kconfig47
-rw-r--r--drivers/bus/Makefile6
-rw-r--r--drivers/bus/arm-cci.c1763
-rw-r--r--drivers/bus/arm-ccn.c1597
-rw-r--r--drivers/bus/fsl-mc/Kconfig16
-rw-r--r--drivers/bus/fsl-mc/Makefile18
-rw-r--r--drivers/bus/fsl-mc/dpbp.c186
-rw-r--r--drivers/bus/fsl-mc/dpcon.c222
-rw-r--r--drivers/bus/fsl-mc/dpmcp.c99
-rw-r--r--drivers/bus/fsl-mc/dprc-driver.c809
-rw-r--r--drivers/bus/fsl-mc/dprc.c532
-rw-r--r--drivers/bus/fsl-mc/fsl-mc-allocator.c653
-rw-r--r--drivers/bus/fsl-mc/fsl-mc-bus.c948
-rw-r--r--drivers/bus/fsl-mc/fsl-mc-msi.c285
-rw-r--r--drivers/bus/fsl-mc/fsl-mc-private.h564
-rw-r--r--drivers/bus/fsl-mc/mc-io.c268
-rw-r--r--drivers/bus/fsl-mc/mc-sys.c296
-rw-r--r--drivers/bus/hisi_lpc.c615
-rw-r--r--drivers/bus/ti-sysc.c526
-rw-r--r--drivers/cdrom/cdrom.c5
-rw-r--r--drivers/cdrom/gdrom.c3
-rw-r--r--drivers/char/Kconfig50
-rw-r--r--drivers/char/Makefile3
-rw-r--r--drivers/char/bfin-otp.c237
-rw-r--r--drivers/char/ds1302.c357
-rw-r--r--drivers/char/hw_random/Kconfig7
-rw-r--r--drivers/char/hw_random/Makefile1
-rw-r--r--drivers/char/hw_random/bcm2835-rng.c2
-rw-r--r--drivers/char/hw_random/cavium-rng-vf.c2
-rw-r--r--drivers/char/hw_random/cavium-rng.c2
-rw-r--r--drivers/char/hw_random/imx-rngc.c2
-rw-r--r--drivers/char/hw_random/ks-sa-rng.c257
-rw-r--r--drivers/char/hw_random/mxc-rnga.c23
-rw-r--r--drivers/char/hw_random/omap-rng.c22
-rw-r--r--drivers/char/hw_random/stm32-rng.c44
-rw-r--r--drivers/char/ipmi/Kconfig15
-rw-r--r--drivers/char/ipmi/Makefile2
-rw-r--r--drivers/char/ipmi/bt-bmc.c6
-rw-r--r--drivers/char/ipmi/ipmi_bt_sm.c22
-rw-r--r--drivers/char/ipmi/ipmi_devintf.c22
-rw-r--r--drivers/char/ipmi/ipmi_dmi.c20
-rw-r--r--drivers/char/ipmi/ipmi_dmi.h2
-rw-r--r--drivers/char/ipmi/ipmi_kcs_sm.c22
-rw-r--r--drivers/char/ipmi/ipmi_msghandler.c22
-rw-r--r--drivers/char/ipmi/ipmi_powernv.c6
-rw-r--r--drivers/char/ipmi/ipmi_poweroff.c46
-rw-r--r--drivers/char/ipmi/ipmi_si.h1
-rw-r--r--drivers/char/ipmi/ipmi_si_hardcode.c1
-rw-r--r--drivers/char/ipmi/ipmi_si_hotmod.c1
-rw-r--r--drivers/char/ipmi/ipmi_si_intf.c192
-rw-r--r--drivers/char/ipmi/ipmi_si_mem_io.c1
-rw-r--r--drivers/char/ipmi/ipmi_si_parisc.c1
-rw-r--r--drivers/char/ipmi/ipmi_si_pci.c44
-rw-r--r--drivers/char/ipmi/ipmi_si_platform.c155
-rw-r--r--drivers/char/ipmi/ipmi_si_port_io.c1
-rw-r--r--drivers/char/ipmi/ipmi_si_sm.h22
-rw-r--r--drivers/char/ipmi/ipmi_smic_sm.c24
-rw-r--r--drivers/char/ipmi/ipmi_ssif.c115
-rw-r--r--drivers/char/ipmi/ipmi_watchdog.c22
-rw-r--r--drivers/char/ipmi/kcs_bmc.c467
-rw-r--r--drivers/char/ipmi/kcs_bmc.h108
-rw-r--r--drivers/char/ipmi/kcs_bmc_aspeed.c320
-rw-r--r--drivers/char/mem.c6
-rw-r--r--drivers/char/random.c230
-rw-r--r--drivers/char/rtc.c83
-rw-r--r--drivers/char/tile-srom.c475
-rw-r--r--drivers/char/tpm/tpm-interface.c158
-rw-r--r--drivers/char/tpm/tpm.h32
-rw-r--r--drivers/char/tpm/tpm2-cmd.c62
-rw-r--r--drivers/char/tpm/tpm_crb.c113
-rw-r--r--drivers/char/tpm/tpm_tis_core.c4
-rw-r--r--drivers/char/tpm/tpm_tis_core.h8
-rw-r--r--drivers/char/virtio_console.c157
-rw-r--r--drivers/char/xillybus/xillybus_pcie.c1
-rw-r--r--drivers/clk/Kconfig45
-rw-r--r--drivers/clk/Makefile9
-rw-r--r--drivers/clk/bcm/clk-bcm2835.c8
-rw-r--r--drivers/clk/clk-cs2000-cp.c2
-rw-r--r--drivers/clk/clk-divider.c58
-rw-r--r--drivers/clk/clk-gpio.c4
-rw-r--r--drivers/clk/clk-mux.c75
-rw-r--r--drivers/clk/clk-scmi.c194
-rw-r--r--drivers/clk/clk-si544.c411
-rw-r--r--drivers/clk/clk-stm32f4.c14
-rw-r--r--drivers/clk/clk-stm32mp1.c2117
-rw-r--r--drivers/clk/clk.c85
-rw-r--r--drivers/clk/davinci/Makefile21
-rw-r--r--drivers/clk/davinci/da8xx-cfgchip.c790
-rw-r--r--drivers/clk/davinci/pll-da830.c70
-rw-r--r--drivers/clk/davinci/pll-da850.c212
-rw-r--r--drivers/clk/davinci/pll-dm355.c79
-rw-r--r--drivers/clk/davinci/pll-dm365.c145
-rw-r--r--drivers/clk/davinci/pll-dm644x.c80
-rw-r--r--drivers/clk/davinci/pll-dm646x.c84
-rw-r--r--drivers/clk/davinci/pll.c899
-rw-r--r--drivers/clk/davinci/pll.h141
-rw-r--r--drivers/clk/davinci/psc-da830.c116
-rw-r--r--drivers/clk/davinci/psc-da850.c156
-rw-r--r--drivers/clk/davinci/psc-dm355.c88
-rw-r--r--drivers/clk/davinci/psc-dm365.c96
-rw-r--r--drivers/clk/davinci/psc-dm644x.c83
-rw-r--r--drivers/clk/davinci/psc-dm646x.c80
-rw-r--r--drivers/clk/davinci/psc.c551
-rw-r--r--drivers/clk/davinci/psc.h108
-rw-r--r--drivers/clk/hisilicon/Makefile2
-rw-r--r--drivers/clk/hisilicon/clk-hisi-phase.c121
-rw-r--r--drivers/clk/hisilicon/clk.c26
-rw-r--r--drivers/clk/hisilicon/clk.h19
-rw-r--r--drivers/clk/hisilicon/crg-hi3516cv300.c2
-rw-r--r--drivers/clk/hisilicon/crg-hi3798cv200.c100
-rw-r--r--drivers/clk/imx/Makefile1
-rw-r--r--drivers/clk/imx/clk-busy.c4
-rw-r--r--drivers/clk/imx/clk-imx6sll.c340
-rw-r--r--drivers/clk/imx/clk-imx6sx.c14
-rw-r--r--drivers/clk/imx/clk-imx6ul.c5
-rw-r--r--drivers/clk/imx/clk-imx7d.c116
-rw-r--r--drivers/clk/imx/clk-pllv2.c6
-rw-r--r--drivers/clk/imx/clk.h14
-rw-r--r--drivers/clk/keystone/sci-clk.c380
-rw-r--r--drivers/clk/mediatek/Kconfig6
-rw-r--r--drivers/clk/mediatek/Makefile1
-rw-r--r--drivers/clk/mediatek/clk-mt2701-aud.c186
-rw-r--r--drivers/clk/mediatek/clk-mt2701.c15
-rw-r--r--drivers/clk/mediatek/clk-mt2712.c69
-rw-r--r--drivers/clk/mediatek/clk-mt7622-aud.c15
-rw-r--r--drivers/clk/meson/Kconfig9
-rw-r--r--drivers/clk/meson/Makefile5
-rw-r--r--drivers/clk/meson/axg.c955
-rw-r--r--drivers/clk/meson/axg.h12
-rw-r--r--drivers/clk/meson/clk-audio-divider.c63
-rw-r--r--drivers/clk/meson/clk-cpu.c178
-rw-r--r--drivers/clk/meson/clk-mpll.c125
-rw-r--r--drivers/clk/meson/clk-pll.c306
-rw-r--r--drivers/clk/meson/clk-regmap.c166
-rw-r--r--drivers/clk/meson/clk-regmap.h111
-rw-r--r--drivers/clk/meson/clkc.h107
-rw-r--r--drivers/clk/meson/gxbb-aoclk-regmap.c46
-rw-r--r--drivers/clk/meson/gxbb-aoclk.c20
-rw-r--r--drivers/clk/meson/gxbb-aoclk.h11
-rw-r--r--drivers/clk/meson/gxbb.c1591
-rw-r--r--drivers/clk/meson/gxbb.h14
-rw-r--r--drivers/clk/meson/meson8b.c705
-rw-r--r--drivers/clk/meson/meson8b.h17
-rw-r--r--drivers/clk/mvebu/armada-38x.c14
-rw-r--r--drivers/clk/mvebu/cp110-system-controller.c94
-rw-r--r--drivers/clk/nxp/clk-lpc32xx.c1
-rw-r--r--drivers/clk/qcom/clk-regmap-divider.c20
-rw-r--r--drivers/clk/qcom/clk-rpm.c79
-rw-r--r--drivers/clk/qcom/clk-smd-rpm.c9
-rw-r--r--drivers/clk/qcom/gcc-msm8996.c8
-rw-r--r--drivers/clk/renesas/Kconfig13
-rw-r--r--drivers/clk/renesas/Makefile2
-rw-r--r--drivers/clk/renesas/clk-div6.c22
-rw-r--r--drivers/clk/renesas/clk-mstp.c4
-rw-r--r--drivers/clk/renesas/clk-r8a73a4.c11
-rw-r--r--drivers/clk/renesas/clk-r8a7740.c8
-rw-r--r--drivers/clk/renesas/clk-rcar-gen2.c17
-rw-r--r--drivers/clk/renesas/clk-rz.c4
-rw-r--r--drivers/clk/renesas/clk-sh73a0.c20
-rw-r--r--drivers/clk/renesas/r8a7743-cpg-mssr.c2
-rw-r--r--drivers/clk/renesas/r8a7745-cpg-mssr.c2
-rw-r--r--drivers/clk/renesas/r8a7790-cpg-mssr.c2
-rw-r--r--drivers/clk/renesas/r8a7791-cpg-mssr.c2
-rw-r--r--drivers/clk/renesas/r8a7792-cpg-mssr.c2
-rw-r--r--drivers/clk/renesas/r8a7794-cpg-mssr.c2
-rw-r--r--drivers/clk/renesas/r8a7795-cpg-mssr.c2
-rw-r--r--drivers/clk/renesas/r8a7796-cpg-mssr.c2
-rw-r--r--drivers/clk/renesas/r8a77965-cpg-mssr.c334
-rw-r--r--drivers/clk/renesas/r8a77980-cpg-mssr.c227
-rw-r--r--drivers/clk/renesas/rcar-gen3-cpg.c143
-rw-r--r--drivers/clk/renesas/rcar-gen3-cpg.h2
-rw-r--r--drivers/clk/renesas/renesas-cpg-mssr.c12
-rw-r--r--drivers/clk/renesas/renesas-cpg-mssr.h2
-rw-r--r--drivers/clk/rockchip/clk-mmc-phase.c78
-rw-r--r--drivers/clk/rockchip/clk-rk3228.c2
-rw-r--r--drivers/clk/rockchip/clk-rk3328.c83
-rw-r--r--drivers/clk/rockchip/clk-rk3399.c5
-rw-r--r--drivers/clk/rockchip/clk.c22
-rw-r--r--drivers/clk/samsung/Makefile2
-rw-r--r--drivers/clk/samsung/clk-exynos-audss.c4
-rw-r--r--drivers/clk/samsung/clk-exynos3250.c114
-rw-r--r--drivers/clk/samsung/clk-exynos4.c103
-rw-r--r--drivers/clk/samsung/clk-exynos5-subcmu.c189
-rw-r--r--drivers/clk/samsung/clk-exynos5-subcmu.h26
-rw-r--r--drivers/clk/samsung/clk-exynos5250.c111
-rw-r--r--drivers/clk/samsung/clk-exynos5260.c90
-rw-r--r--drivers/clk/samsung/clk-exynos5410.c20
-rw-r--r--drivers/clk/samsung/clk-exynos5420.c189
-rw-r--r--drivers/clk/samsung/clk-exynos5433.c121
-rw-r--r--drivers/clk/samsung/clk-exynos7.c2
-rw-r--r--drivers/clk/samsung/clk-pll.h48
-rw-r--r--drivers/clk/samsung/clk-s3c2410.c148
-rw-r--r--drivers/clk/samsung/clk-s3c2412.c25
-rw-r--r--drivers/clk/samsung/clk-s3c2443.c55
-rw-r--r--drivers/clk/samsung/clk-s3c64xx.c17
-rw-r--r--drivers/clk/socfpga/Makefile9
-rw-r--r--drivers/clk/socfpga/clk-gate-s10.c125
-rw-r--r--drivers/clk/socfpga/clk-periph-s10.c149
-rw-r--r--drivers/clk/socfpga/clk-pll-s10.c146
-rw-r--r--drivers/clk/socfpga/clk-s10.c345
-rw-r--r--drivers/clk/socfpga/clk.h4
-rw-r--r--drivers/clk/socfpga/stratix10-clk.h80
-rw-r--r--drivers/clk/sprd/sc9860-clk.c76
-rw-r--r--drivers/clk/sunxi-ng/Kconfig12
-rw-r--r--drivers/clk/sunxi-ng/Makefile1
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun50i-h6.c1211
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun50i-h6.h56
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun8i-h3.c32
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun8i-h3.h4
-rw-r--r--drivers/clk/sunxi-ng/ccu_nkmp.c58
-rw-r--r--drivers/clk/sunxi-ng/ccu_nkmp.h2
-rw-r--r--drivers/clk/sunxi-ng/ccu_nm.c7
-rw-r--r--drivers/clk/sunxi-ng/ccu_nm.h27
-rw-r--r--drivers/clk/tegra/clk-emc.c2
-rw-r--r--drivers/clk/tegra/clk-pll.c2
-rw-r--r--drivers/clk/tegra/clk-tegra-periph.c2
-rw-r--r--drivers/clk/tegra/clk-tegra-super-gen4.c8
-rw-r--r--drivers/clk/tegra/clk-tegra114.c4
-rw-r--r--drivers/clk/tegra/clk-tegra124.c9
-rw-r--r--drivers/clk/tegra/clk-tegra20.c24
-rw-r--r--drivers/clk/tegra/clk-tegra210.c361
-rw-r--r--drivers/clk/tegra/clk-tegra30.c15
-rw-r--r--drivers/clk/tegra/clk.h7
-rw-r--r--drivers/clk/ti/clk.c38
-rw-r--r--drivers/clk/ti/clock.h13
-rw-r--r--drivers/clk/ti/divider.c26
-rw-r--r--drivers/clk/ti/mux.c13
-rw-r--r--drivers/clk/uniphier/clk-uniphier-sys.c25
-rw-r--r--drivers/clk/ux500/Makefile2
-rw-r--r--drivers/clk/ux500/abx500-clk.c16
-rw-r--r--drivers/clk/ux500/u8540_clk.c597
-rw-r--r--drivers/clk/ux500/u9540_clk.c18
-rw-r--r--drivers/clk/versatile/clk-vexpress-osc.c5
-rw-r--r--drivers/clocksource/Kconfig25
-rw-r--r--drivers/clocksource/Makefile4
-rw-r--r--drivers/clocksource/metag_generic.c161
-rw-r--r--drivers/clocksource/timer-atcpit100.c266
-rw-r--r--drivers/clocksource/timer-imx-tpm.c45
-rw-r--r--drivers/clocksource/timer-npcm7xx.c215
-rw-r--r--drivers/clocksource/timer-ti-dm.c1000
-rw-r--r--drivers/cpufreq/Kconfig.arm23
-rw-r--r--drivers/cpufreq/Makefile4
-rw-r--r--drivers/cpufreq/acpi-cpufreq.c20
-rw-r--r--drivers/cpufreq/arm_big_little.c9
-rw-r--r--drivers/cpufreq/armada-37xx-cpufreq.c2
-rw-r--r--drivers/cpufreq/blackfin-cpufreq.c217
-rw-r--r--drivers/cpufreq/brcmstb-avs-cpufreq.c329
-rw-r--r--drivers/cpufreq/cppc_cpufreq.c27
-rw-r--r--drivers/cpufreq/cpufreq-dt.c8
-rw-r--r--drivers/cpufreq/cpufreq.c28
-rw-r--r--drivers/cpufreq/cris-artpec3-cpufreq.c93
-rw-r--r--drivers/cpufreq/cris-etraxfs-cpufreq.c92
-rw-r--r--drivers/cpufreq/e_powersaver.c8
-rw-r--r--drivers/cpufreq/elanfreq.c3
-rw-r--r--drivers/cpufreq/freq_table.c10
-rw-r--r--drivers/cpufreq/ia64-acpi-cpufreq.c7
-rw-r--r--drivers/cpufreq/imx6q-cpufreq.c7
-rw-r--r--drivers/cpufreq/intel_pstate.c1
-rw-r--r--drivers/cpufreq/longhaul.c3
-rw-r--r--drivers/cpufreq/mediatek-cpufreq.c13
-rw-r--r--drivers/cpufreq/p4-clockmod.c3
-rw-r--r--drivers/cpufreq/powernow-k6.c3
-rw-r--r--drivers/cpufreq/powernow-k7.c3
-rw-r--r--drivers/cpufreq/powernow-k8.c24
-rw-r--r--drivers/cpufreq/powernv-cpufreq.c25
-rw-r--r--drivers/cpufreq/ppc_cbe_cpufreq.c5
-rw-r--r--drivers/cpufreq/pxa2xx-cpufreq.c4
-rw-r--r--drivers/cpufreq/pxa3xx-cpufreq.c4
-rw-r--r--drivers/cpufreq/qoriq-cpufreq.c21
-rw-r--r--drivers/cpufreq/s3c24xx-cpufreq.c13
-rw-r--r--drivers/cpufreq/sc520_freq.c3
-rw-r--r--drivers/cpufreq/scmi-cpufreq.c256
-rw-r--r--drivers/cpufreq/scpi-cpufreq.c10
-rw-r--r--drivers/cpufreq/sfi-cpufreq.c3
-rw-r--r--drivers/cpufreq/sh-cpufreq.c22
-rw-r--r--drivers/cpufreq/sparc-us2e-cpufreq.c3
-rw-r--r--drivers/cpufreq/sparc-us3-cpufreq.c3
-rw-r--r--drivers/cpufreq/speedstep-centrino.c4
-rw-r--r--drivers/cpufreq/speedstep-ich.c4
-rw-r--r--drivers/cpufreq/speedstep-smi.c4
-rw-r--r--drivers/cpufreq/tegra186-cpufreq.c3
-rw-r--r--drivers/cpufreq/ti-cpufreq.c2
-rw-r--r--drivers/cpuidle/cpuidle-arm.c1
-rw-r--r--drivers/cpuidle/cpuidle-exynos.c3
-rw-r--r--drivers/cpuidle/cpuidle.c19
-rw-r--r--drivers/cpuidle/governors/ladder.c3
-rw-r--r--drivers/cpuidle/governors/menu.c113
-rw-r--r--drivers/cpuidle/poll_state.c17
-rw-r--r--drivers/cpuidle/sysfs.c54
-rw-r--r--drivers/crypto/Kconfig34
-rw-r--r--drivers/crypto/Makefile2
-rw-r--r--drivers/crypto/atmel-aes.c8
-rw-r--r--drivers/crypto/atmel-sha.c9
-rw-r--r--drivers/crypto/atmel-tdes.c9
-rw-r--r--drivers/crypto/bcm/cipher.c4
-rw-r--r--drivers/crypto/bcm/util.c1
-rw-r--r--drivers/crypto/bfin_crc.c743
-rw-r--r--drivers/crypto/bfin_crc.h124
-rw-r--r--drivers/crypto/caam/caamalg.c21
-rw-r--r--drivers/crypto/caam/caamalg_desc.c165
-rw-r--r--drivers/crypto/caam/caamalg_desc.h24
-rw-r--r--drivers/crypto/caam/caamalg_qi.c388
-rw-r--r--drivers/crypto/caam/ctrl.c42
-rw-r--r--drivers/crypto/caam/qi.c11
-rw-r--r--drivers/crypto/cavium/cpt/cptpf_main.c2
-rw-r--r--drivers/crypto/ccp/ccp-crypto-aes-cmac.c2
-rw-r--r--drivers/crypto/ccp/ccp-crypto-rsa.c7
-rw-r--r--drivers/crypto/ccp/ccp-crypto-sha.c2
-rw-r--r--drivers/crypto/ccp/ccp-debugfs.c7
-rw-r--r--drivers/crypto/ccp/ccp-dmaengine.c2
-rw-r--r--drivers/crypto/ccp/ccp-ops.c108
-rw-r--r--drivers/crypto/ccp/psp-dev.c15
-rw-r--r--drivers/crypto/ccp/sp-dev.c6
-rw-r--r--drivers/crypto/ccree/Makefile (renamed from drivers/staging/ccree/Makefile)0
-rw-r--r--drivers/crypto/ccree/cc_aead.c2718
-rw-r--r--drivers/crypto/ccree/cc_aead.h (renamed from drivers/staging/ccree/cc_aead.h)0
-rw-r--r--drivers/crypto/ccree/cc_buffer_mgr.c1651
-rw-r--r--drivers/crypto/ccree/cc_buffer_mgr.h71
-rw-r--r--drivers/crypto/ccree/cc_cipher.c1150
-rw-r--r--drivers/crypto/ccree/cc_cipher.h59
-rw-r--r--drivers/crypto/ccree/cc_crypto_ctx.h133
-rw-r--r--drivers/crypto/ccree/cc_debugfs.c (renamed from drivers/staging/ccree/cc_debugfs.c)0
-rw-r--r--drivers/crypto/ccree/cc_debugfs.h (renamed from drivers/staging/ccree/cc_debugfs.h)0
-rw-r--r--drivers/crypto/ccree/cc_driver.c518
-rw-r--r--drivers/crypto/ccree/cc_driver.h208
-rw-r--r--drivers/crypto/ccree/cc_fips.c120
-rw-r--r--drivers/crypto/ccree/cc_fips.h36
-rw-r--r--drivers/crypto/ccree/cc_hash.c2296
-rw-r--r--drivers/crypto/ccree/cc_hash.h109
-rw-r--r--drivers/crypto/ccree/cc_host_regs.h145
-rw-r--r--drivers/crypto/ccree/cc_hw_queue_defs.h576
-rw-r--r--drivers/crypto/ccree/cc_ivgen.c279
-rw-r--r--drivers/crypto/ccree/cc_ivgen.h (renamed from drivers/staging/ccree/cc_ivgen.h)0
-rw-r--r--drivers/crypto/ccree/cc_kernel_regs.h168
-rw-r--r--drivers/crypto/ccree/cc_lli_defs.h (renamed from drivers/staging/ccree/cc_lli_defs.h)0
-rw-r--r--drivers/crypto/ccree/cc_pm.c (renamed from drivers/staging/ccree/cc_pm.c)0
-rw-r--r--drivers/crypto/ccree/cc_pm.h56
-rw-r--r--drivers/crypto/ccree/cc_request_mgr.c711
-rw-r--r--drivers/crypto/ccree/cc_request_mgr.h (renamed from drivers/staging/ccree/cc_request_mgr.h)0
-rw-r--r--drivers/crypto/ccree/cc_sram_mgr.c120
-rw-r--r--drivers/crypto/ccree/cc_sram_mgr.h (renamed from drivers/staging/ccree/cc_sram_mgr.h)0
-rw-r--r--drivers/crypto/chelsio/Kconfig11
-rw-r--r--drivers/crypto/chelsio/Makefile1
-rw-r--r--drivers/crypto/chelsio/chcr_algo.c577
-rw-r--r--drivers/crypto/chelsio/chcr_algo.h53
-rw-r--r--drivers/crypto/chelsio/chcr_core.h61
-rw-r--r--drivers/crypto/chelsio/chcr_crypto.h31
-rw-r--r--drivers/crypto/chelsio/chcr_ipsec.c5
-rw-r--r--drivers/crypto/chelsio/chtls/Makefile4
-rw-r--r--drivers/crypto/chelsio/chtls/chtls.h482
-rw-r--r--drivers/crypto/chelsio/chtls/chtls_cm.c2126
-rw-r--r--drivers/crypto/chelsio/chtls/chtls_cm.h203
-rw-r--r--drivers/crypto/chelsio/chtls/chtls_hw.c412
-rw-r--r--drivers/crypto/chelsio/chtls/chtls_io.c1822
-rw-r--r--drivers/crypto/chelsio/chtls/chtls_main.c578
-rw-r--r--drivers/crypto/inside-secure/safexcel.c114
-rw-r--r--drivers/crypto/inside-secure/safexcel.h22
-rw-r--r--drivers/crypto/inside-secure/safexcel_cipher.c5
-rw-r--r--drivers/crypto/inside-secure/safexcel_hash.c258
-rw-r--r--drivers/crypto/ixp4xx_crypto.c2
-rw-r--r--drivers/crypto/marvell/cesa.c1
-rw-r--r--drivers/crypto/mxs-dcp.c14
-rw-r--r--drivers/crypto/n2_core.c12
-rw-r--r--drivers/crypto/nx/nx-842-pseries.c5
-rw-r--r--drivers/crypto/omap-aes.c112
-rw-r--r--drivers/crypto/omap-aes.h3
-rw-r--r--drivers/crypto/omap-crypto.c4
-rw-r--r--drivers/crypto/omap-des.c24
-rw-r--r--drivers/crypto/omap-sham.c106
-rw-r--r--drivers/crypto/picoxcell_crypto.c2
-rw-r--r--drivers/crypto/qat/qat_common/.gitignore1
-rw-r--r--drivers/crypto/qat/qat_common/qat_algs.c3
-rw-r--r--drivers/crypto/qat/qat_common/qat_asym_algs.c9
-rw-r--r--drivers/crypto/s5p-sss.c34
-rw-r--r--drivers/crypto/sahara.c6
-rw-r--r--drivers/crypto/stm32/stm32-cryp.c964
-rw-r--r--drivers/crypto/stm32/stm32-hash.c41
-rw-r--r--drivers/crypto/sunxi-ss/sun4i-ss-core.c1
-rw-r--r--drivers/crypto/talitos.c218
-rw-r--r--drivers/crypto/ux500/cryp/cryp_core.c14
-rw-r--r--drivers/crypto/ux500/hash/hash_core.c18
-rw-r--r--drivers/crypto/virtio/Kconfig1
-rw-r--r--drivers/crypto/virtio/virtio_crypto_algs.c16
-rw-r--r--drivers/crypto/virtio/virtio_crypto_common.h4
-rw-r--r--drivers/crypto/virtio/virtio_crypto_core.c3
-rw-r--r--drivers/dax/Kconfig5
-rw-r--r--drivers/dax/device.c50
-rw-r--r--drivers/dax/pmem.c18
-rw-r--r--drivers/dax/super.c15
-rw-r--r--drivers/dma/Kconfig12
-rw-r--r--drivers/dma/Makefile2
-rw-r--r--drivers/dma/at_xdmac.c4
-rw-r--r--drivers/dma/dmatest.c16
-rw-r--r--drivers/dma/dw-axi-dmac/Makefile1
-rw-r--r--drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c1008
-rw-r--r--drivers/dma/dw-axi-dmac/dw-axi-dmac.h334
-rw-r--r--drivers/dma/edma.c6
-rw-r--r--drivers/dma/imx-sdma.c21
-rw-r--r--drivers/dma/mediatek/Kconfig13
-rw-r--r--drivers/dma/mediatek/Makefile1
-rw-r--r--drivers/dma/mediatek/mtk-hsdma.c1056
-rw-r--r--drivers/dma/pl330.c6
-rw-r--r--drivers/dma/qcom/bam_dma.c59
-rw-r--r--drivers/dma/sh/rcar-dmac.c13
-rw-r--r--drivers/dma/stm32-dma.c287
-rw-r--r--drivers/edac/Kconfig15
-rw-r--r--drivers/edac/Makefile2
-rw-r--r--drivers/edac/edac_mc.c41
-rw-r--r--drivers/edac/edac_mc_sysfs.c26
-rw-r--r--drivers/edac/mce_amd.c11
-rw-r--r--drivers/edac/sb_edac.c12
-rw-r--r--drivers/edac/skx_edac.c67
-rw-r--r--drivers/edac/tile_edac.c265
-rw-r--r--drivers/extcon/Kconfig3
-rw-r--r--drivers/extcon/extcon-axp288.c176
-rw-r--r--drivers/extcon/extcon-gpio.c103
-rw-r--r--drivers/extcon/extcon-intel-cht-wc.c11
-rw-r--r--drivers/extcon/extcon-intel-int3496.c9
-rw-r--r--drivers/extcon/extcon.c44
-rw-r--r--drivers/firmware/Kconfig34
-rw-r--r--drivers/firmware/Makefile1
-rw-r--r--drivers/firmware/arm_scmi/Makefile5
-rw-r--r--drivers/firmware/arm_scmi/base.c253
-rw-r--r--drivers/firmware/arm_scmi/bus.c221
-rw-r--r--drivers/firmware/arm_scmi/clock.c343
-rw-r--r--drivers/firmware/arm_scmi/common.h105
-rw-r--r--drivers/firmware/arm_scmi/driver.c871
-rw-r--r--drivers/firmware/arm_scmi/perf.c481
-rw-r--r--drivers/firmware/arm_scmi/power.c221
-rw-r--r--drivers/firmware/arm_scmi/scmi_pm_domain.c129
-rw-r--r--drivers/firmware/arm_scmi/sensors.c291
-rw-r--r--drivers/firmware/arm_scpi.c211
-rw-r--r--drivers/firmware/broadcom/Kconfig1
-rw-r--r--drivers/firmware/broadcom/bcm47xx_sprom.c18
-rw-r--r--drivers/firmware/dmi_scan.c67
-rw-r--r--drivers/firmware/edd.c8
-rw-r--r--drivers/firmware/efi/apple-properties.c20
-rw-r--r--drivers/firmware/efi/arm-runtime.c17
-rw-r--r--drivers/firmware/efi/efi.c11
-rw-r--r--drivers/firmware/efi/esrt.c17
-rw-r--r--drivers/firmware/efi/libstub/Makefile2
-rw-r--r--drivers/firmware/efi/libstub/secureboot.c12
-rw-r--r--drivers/firmware/efi/libstub/tpm.c7
-rw-r--r--drivers/firmware/meson/meson_sm.c25
-rw-r--r--drivers/firmware/qemu_fw_cfg.c291
-rw-r--r--drivers/firmware/tegra/bpmp.c144
-rw-r--r--drivers/fmc/fmc-core.c2
-rw-r--r--drivers/fpga/altera-cvp.c2
-rw-r--r--drivers/fpga/altera-ps-spi.c2
-rw-r--r--drivers/fsi/Kconfig1
-rw-r--r--drivers/fsi/fsi-core.c129
-rw-r--r--drivers/fsi/fsi-master-gpio.c89
-rw-r--r--drivers/fsi/fsi-master-hub.c27
-rw-r--r--drivers/fsi/fsi-master.h17
-rw-r--r--drivers/gpio/Kconfig84
-rw-r--r--drivers/gpio/Makefile8
-rw-r--r--drivers/gpio/gpio-104-dio-48e.c47
-rw-r--r--drivers/gpio/gpio-104-idi-48.c47
-rw-r--r--drivers/gpio/gpio-104-idio-16.c15
-rw-r--r--drivers/gpio/gpio-ath79.c2
-rw-r--r--drivers/gpio/gpio-davinci.c6
-rw-r--r--drivers/gpio/gpio-dln2.c7
-rw-r--r--drivers/gpio/gpio-dwapb.c81
-rw-r--r--drivers/gpio/gpio-eic-sprd.c606
-rw-r--r--drivers/gpio/gpio-em.c2
-rw-r--r--drivers/gpio/gpio-etraxfs.c475
-rw-r--r--drivers/gpio/gpio-ftgpio010.c7
-rw-r--r--drivers/gpio/gpio-ge.c1
-rw-r--r--drivers/gpio/gpio-gpio-mm.c47
-rw-r--r--drivers/gpio/gpio-grgpio.c3
-rw-r--r--drivers/gpio/gpio-hlwd.c115
-rw-r--r--drivers/gpio/gpio-htc-egpio.c1
-rw-r--r--drivers/gpio/gpio-ich.c21
-rw-r--r--drivers/gpio/gpio-intel-mid.c4
-rw-r--r--drivers/gpio/gpio-it87.c2
-rw-r--r--drivers/gpio/gpio-janz-ttl.c15
-rw-r--r--drivers/gpio/gpio-kempld.c2
-rw-r--r--drivers/gpio/gpio-ks8695.c2
-rw-r--r--drivers/gpio/gpio-max3191x.c7
-rw-r--r--drivers/gpio/gpio-merrifield.c4
-rw-r--r--drivers/gpio/gpio-ml-ioh.c1
-rw-r--r--drivers/gpio/gpio-mockup.c6
-rw-r--r--drivers/gpio/gpio-omap.c6
-rw-r--r--drivers/gpio/gpio-pca953x.c4
-rw-r--r--drivers/gpio/gpio-pci-idio-16.c50
-rw-r--r--drivers/gpio/gpio-pcie-idio-24.c117
-rw-r--r--drivers/gpio/gpio-pmic-eic-sprd.c330
-rw-r--r--drivers/gpio/gpio-raspberrypi-exp.c252
-rw-r--r--drivers/gpio/gpio-rcar.c60
-rw-r--r--drivers/gpio/gpio-sprd.c290
-rw-r--r--drivers/gpio/gpio-tegra.c16
-rw-r--r--drivers/gpio/gpio-timberdale.c7
-rw-r--r--drivers/gpio/gpio-tps68470.c10
-rw-r--r--drivers/gpio/gpio-tz1090-pdc.c231
-rw-r--r--drivers/gpio/gpio-tz1090.c602
-rw-r--r--drivers/gpio/gpio-wm831x.c2
-rw-r--r--drivers/gpio/gpio-ws16c48.c47
-rw-r--r--drivers/gpio/gpio-xra1403.c8
-rw-r--r--drivers/gpio/gpiolib-of.c28
-rw-r--r--drivers/gpio/gpiolib.c68
-rw-r--r--drivers/gpio/gpiolib.h2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu.h4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c117
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c9
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c12
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c16
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_object.c18
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/cik_sdma.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c7
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c7
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c14
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c61
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/si.c67
-rw-r--r--drivers/gpu/drm/amd/amdgpu/si_dpm.c4
-rw-r--r--[-rwxr-xr-x]drivers/gpu/drm/amd/amdgpu/vce_v4_0.c0
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vega10_reg_init.c3
-rw-r--r--drivers/gpu/drm/amd/amdkfd/Kconfig1
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_chardev.c17
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c20
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_packet_manager.c3
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c99
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c7
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c5
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c54
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_link.c13
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_link.h2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.c67
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c13
-rw-r--r--drivers/gpu/drm/amd/include/atomfirmware.h12
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/Makefile2
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/ppatomfwctrl.c10
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/ppatomfwctrl.h10
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c19
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/smu8_hwmgr.c8
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/vega12_hwmgr.c23
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/vega12_powertune.c1364
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/vega12_powertune.h53
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/vega12_processpptables.c11
-rw-r--r--drivers/gpu/drm/amd/powerplay/inc/vega12/smu9_driver_if.h18
-rw-r--r--drivers/gpu/drm/amd/powerplay/smumgr/vega12_smumgr.c6
-rw-r--r--drivers/gpu/drm/bridge/sil-sii8620.c97
-rw-r--r--drivers/gpu/drm/drm_color_mgmt.c4
-rw-r--r--drivers/gpu/drm/drm_dp_aux_dev.c13
-rw-r--r--drivers/gpu/drm/drm_dp_dual_mode_helper.c39
-rw-r--r--drivers/gpu/drm/drm_edid.c11
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fb.c73
-rw-r--r--drivers/gpu/drm/i915/Kconfig.debug13
-rw-r--r--drivers/gpu/drm/i915/Makefile11
-rw-r--r--drivers/gpu/drm/i915/gvt/cmd_parser.c1
-rw-r--r--drivers/gpu/drm/i915/gvt/debugfs.c13
-rw-r--r--drivers/gpu/drm/i915/gvt/display.c10
-rw-r--r--drivers/gpu/drm/i915/gvt/dmabuf.c1
-rw-r--r--drivers/gpu/drm/i915/gvt/fb_decoder.c27
-rw-r--r--drivers/gpu/drm/i915/gvt/gtt.c52
-rw-r--r--drivers/gpu/drm/i915/gvt/gtt.h2
-rw-r--r--drivers/gpu/drm/i915/gvt/handlers.c1
-rw-r--r--drivers/gpu/drm/i915/gvt/kvmgt.c2
-rw-r--r--drivers/gpu/drm/i915/i915_debugfs.c318
-rw-r--r--drivers/gpu/drm/i915/i915_drv.c98
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h389
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c237
-rw-r--r--drivers/gpu/drm/i915/i915_gem.h7
-rw-r--r--drivers/gpu/drm/i915/i915_gem_batch_pool.c30
-rw-r--r--drivers/gpu/drm/i915/i915_gem_batch_pool.h29
-rw-r--r--drivers/gpu/drm/i915/i915_gem_context.c11
-rw-r--r--drivers/gpu/drm/i915/i915_gem_execbuffer.c31
-rw-r--r--drivers/gpu/drm/i915/i915_gem_stolen.c163
-rw-r--r--drivers/gpu/drm/i915/i915_gpu_error.c1
-rw-r--r--drivers/gpu/drm/i915/i915_gpu_error.h362
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c366
-rw-r--r--drivers/gpu/drm/i915/i915_oa_icl.c118
-rw-r--r--drivers/gpu/drm/i915/i915_oa_icl.h34
-rw-r--r--drivers/gpu/drm/i915/i915_params.h2
-rw-r--r--drivers/gpu/drm/i915/i915_pci.c1
-rw-r--r--drivers/gpu/drm/i915/i915_perf.c69
-rw-r--r--drivers/gpu/drm/i915/i915_pmu.c64
-rw-r--r--drivers/gpu/drm/i915/i915_pmu.h30
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h644
-rw-r--r--drivers/gpu/drm/i915/i915_request.c125
-rw-r--r--drivers/gpu/drm/i915/i915_utils.h4
-rw-r--r--drivers/gpu/drm/i915/intel_atomic.c19
-rw-r--r--drivers/gpu/drm/i915/intel_audio.c2
-rw-r--r--drivers/gpu/drm/i915/intel_bios.c28
-rw-r--r--drivers/gpu/drm/i915/intel_breadcrumbs.c21
-rw-r--r--drivers/gpu/drm/i915/intel_cdclk.c20
-rw-r--r--drivers/gpu/drm/i915/intel_ddi.c154
-rw-r--r--drivers/gpu/drm/i915/intel_device_info.c169
-rw-r--r--drivers/gpu/drm/i915/intel_device_info.h4
-rw-r--r--drivers/gpu/drm/i915/intel_display.c251
-rw-r--r--drivers/gpu/drm/i915/intel_dp.c21
-rw-r--r--drivers/gpu/drm/i915/intel_dp_mst.c8
-rw-r--r--drivers/gpu/drm/i915/intel_dpio_phy.c11
-rw-r--r--drivers/gpu/drm/i915/intel_dpll_mgr.c253
-rw-r--r--drivers/gpu/drm/i915/intel_dpll_mgr.h56
-rw-r--r--drivers/gpu/drm/i915/intel_drv.h62
-rw-r--r--drivers/gpu/drm/i915/intel_engine_cs.c796
-rw-r--r--drivers/gpu/drm/i915/intel_fbc.c28
-rw-r--r--drivers/gpu/drm/i915/intel_fbdev.c7
-rw-r--r--drivers/gpu/drm/i915/intel_frontbuffer.c2
-rw-r--r--drivers/gpu/drm/i915/intel_gpu_commands.h274
-rw-r--r--drivers/gpu/drm/i915/intel_guc.c211
-rw-r--r--drivers/gpu/drm/i915/intel_guc.h82
-rw-r--r--drivers/gpu/drm/i915/intel_guc_ads.c8
-rw-r--r--drivers/gpu/drm/i915/intel_guc_ct.c545
-rw-r--r--drivers/gpu/drm/i915/intel_guc_ct.h18
-rw-r--r--drivers/gpu/drm/i915/intel_guc_fw.c7
-rw-r--r--drivers/gpu/drm/i915/intel_guc_fwif.h157
-rw-r--r--drivers/gpu/drm/i915/intel_guc_log.c544
-rw-r--r--drivers/gpu/drm/i915/intel_guc_log.h59
-rw-r--r--drivers/gpu/drm/i915/intel_guc_reg.h14
-rw-r--r--drivers/gpu/drm/i915/intel_guc_submission.c47
-rw-r--r--drivers/gpu/drm/i915/intel_hangcheck.c13
-rw-r--r--drivers/gpu/drm/i915/intel_hdcp.c185
-rw-r--r--drivers/gpu/drm/i915/intel_hdmi.c40
-rw-r--r--drivers/gpu/drm/i915/intel_hotplug.c3
-rw-r--r--drivers/gpu/drm/i915/intel_huc.c30
-rw-r--r--drivers/gpu/drm/i915/intel_huc.h7
-rw-r--r--drivers/gpu/drm/i915/intel_huc_fw.c8
-rw-r--r--drivers/gpu/drm/i915/intel_lrc.c261
-rw-r--r--drivers/gpu/drm/i915/intel_overlay.c1
-rw-r--r--drivers/gpu/drm/i915/intel_pipe_crc.c53
-rw-r--r--drivers/gpu/drm/i915/intel_pm.c474
-rw-r--r--drivers/gpu/drm/i915/intel_psr.c327
-rw-r--r--drivers/gpu/drm/i915/intel_ringbuffer.c22
-rw-r--r--drivers/gpu/drm/i915/intel_ringbuffer.h43
-rw-r--r--drivers/gpu/drm/i915/intel_runtime_pm.c11
-rw-r--r--drivers/gpu/drm/i915/intel_sprite.c7
-rw-r--r--drivers/gpu/drm/i915/intel_uc.c132
-rw-r--r--drivers/gpu/drm/i915/intel_uc.h5
-rw-r--r--drivers/gpu/drm/i915/intel_uc_fw.c13
-rw-r--r--drivers/gpu/drm/i915/intel_uc_fw.h22
-rw-r--r--drivers/gpu/drm/i915/intel_uncore.c168
-rw-r--r--drivers/gpu/drm/i915/intel_uncore.h1
-rw-r--r--drivers/gpu/drm/i915/intel_wopcm.c275
-rw-r--r--drivers/gpu/drm/i915/intel_wopcm.h31
-rw-r--r--drivers/gpu/drm/i915/intel_workarounds.c856
-rw-r--r--drivers/gpu/drm/i915/intel_workarounds.h17
-rw-r--r--drivers/gpu/drm/i915/selftests/i915_live_selftests.h1
-rw-r--r--drivers/gpu/drm/i915/selftests/i915_mock_selftests.h1
-rw-r--r--drivers/gpu/drm/i915/selftests/intel_breadcrumbs.c14
-rw-r--r--drivers/gpu/drm/i915/selftests/intel_engine_cs.c58
-rw-r--r--drivers/gpu/drm/i915/selftests/intel_hangcheck.c314
-rw-r--r--drivers/gpu/drm/i915/selftests/intel_lrc.c507
-rw-r--r--drivers/gpu/drm/msm/adreno/a5xx_gpu.c4
-rw-r--r--drivers/gpu/drm/msm/disp/mdp4/mdp4_crtc.c1
-rw-r--r--drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c1
-rw-r--r--drivers/gpu/drm/msm/disp/mdp_format.c3
-rw-r--r--drivers/gpu/drm/msm/disp/mdp_kms.h2
-rw-r--r--drivers/gpu/drm/msm/dsi/dsi_host.c16
-rw-r--r--drivers/gpu/drm/msm/dsi/phy/dsi_phy.c109
-rw-r--r--drivers/gpu/drm/msm/dsi/phy/dsi_phy.h2
-rw-r--r--drivers/gpu/drm/msm/dsi/phy/dsi_phy_10nm.c28
-rw-r--r--drivers/gpu/drm/msm/msm_fb.c3
-rw-r--r--drivers/gpu/drm/msm/msm_fbdev.c11
-rw-r--r--drivers/gpu/drm/msm/msm_gem.c20
-rw-r--r--drivers/gpu/drm/msm/msm_kms.h5
-rw-r--r--drivers/gpu/drm/omapdrm/dss/dpi.c15
-rw-r--r--drivers/gpu/drm/qxl/qxl_cmd.c6
-rw-r--r--drivers/gpu/drm/qxl/qxl_drv.h1
-rw-r--r--drivers/gpu/drm/qxl/qxl_ioctl.c4
-rw-r--r--drivers/gpu/drm/qxl/qxl_release.c18
-rw-r--r--drivers/gpu/drm/radeon/radeon_device.c4
-rw-r--r--drivers/gpu/drm/radeon/si_dpm.c4
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_lvds.c55
-rw-r--r--drivers/gpu/drm/tegra/dc.c6
-rw-r--r--drivers/gpu/drm/vc4/vc4_bo.c2
-rw-r--r--drivers/gpu/drm/vc4/vc4_hdmi.c47
-rw-r--r--drivers/gpu/drm/vc4/vc4_validate_shaders.c1
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_vq.c4
-rw-r--r--drivers/hid/Kconfig20
-rw-r--r--drivers/hid/Makefile2
-rw-r--r--drivers/hid/hid-asus.c7
-rw-r--r--drivers/hid/hid-core.c47
-rw-r--r--drivers/hid/hid-corsair.c24
-rw-r--r--drivers/hid/hid-elan.c421
-rw-r--r--drivers/hid/hid-elecom.c32
-rw-r--r--drivers/hid/hid-generic.c33
-rw-r--r--drivers/hid/hid-google-hammer.c138
-rw-r--r--drivers/hid/hid-ids.h24
-rw-r--r--drivers/hid/hid-input.c37
-rw-r--r--drivers/hid/hid-multitouch.c68
-rw-r--r--drivers/hid/hid-ntrig.c12
-rw-r--r--drivers/hid/hid-quirks.c20
-rw-r--r--drivers/hid/hid-rmi.c4
-rw-r--r--drivers/hid/hid-sony.c131
-rw-r--r--drivers/hid/hid-uclogic.c1
-rw-r--r--drivers/hid/hidraw.c5
-rw-r--r--drivers/hid/i2c-hid/i2c-hid.c30
-rw-r--r--drivers/hid/uhid.c12
-rw-r--r--drivers/hid/usbhid/hid-core.c12
-rw-r--r--drivers/hid/wacom_sys.c4
-rw-r--r--drivers/hid/wacom_wac.c299
-rw-r--r--drivers/hid/wacom_wac.h4
-rw-r--r--drivers/hsi/clients/hsi_char.c5
-rw-r--r--drivers/hsi/clients/ssi_protocol.c12
-rw-r--r--drivers/hv/Kconfig2
-rw-r--r--drivers/hv/Makefile1
-rw-r--r--drivers/hv/channel_mgmt.c8
-rw-r--r--drivers/hv/connection.c1
-rw-r--r--drivers/hv/hv.c65
-rw-r--r--drivers/hv/hv_balloon.c121
-rw-r--r--drivers/hv/hv_trace.c2
-rw-r--r--drivers/hv/hv_trace.h2
-rw-r--r--drivers/hv/hv_trace_balloon.h48
-rw-r--r--drivers/hv/hyperv_vmbus.h5
-rw-r--r--drivers/hv/vmbus_drv.c1
-rw-r--r--drivers/hwmon/Kconfig17
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/g762.c53
-rw-r--r--drivers/hwmon/k10temp.c17
-rw-r--r--drivers/hwmon/lm92.c63
-rw-r--r--drivers/hwmon/nct6683.c4
-rw-r--r--drivers/hwmon/nct6775.c231
-rw-r--r--drivers/hwmon/pmbus/Kconfig4
-rw-r--r--drivers/hwmon/pmbus/adm1275.c71
-rw-r--r--drivers/hwmon/pmbus/max8688.c2
-rw-r--r--drivers/hwmon/pmbus/ucd9000.c350
-rw-r--r--drivers/hwmon/scmi-hwmon.c228
-rw-r--r--drivers/hwmon/sht21.c3
-rw-r--r--drivers/hwmon/via-cputemp.c31
-rw-r--r--drivers/hwtracing/Kconfig7
-rw-r--r--drivers/hwtracing/coresight/coresight-cpu-debug.c2
-rw-r--r--drivers/hwtracing/coresight/coresight-etm-perf.c59
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x-sysfs.c4
-rw-r--r--drivers/hwtracing/intel_th/Kconfig12
-rw-r--r--drivers/hwtracing/intel_th/Makefile3
-rw-r--r--drivers/hwtracing/intel_th/acpi.c79
-rw-r--r--drivers/hwtracing/intel_th/core.c31
-rw-r--r--drivers/hwtracing/intel_th/debug.c10
-rw-r--r--drivers/hwtracing/intel_th/debug.h10
-rw-r--r--drivers/hwtracing/intel_th/gth.c10
-rw-r--r--drivers/hwtracing/intel_th/gth.h10
-rw-r--r--drivers/hwtracing/intel_th/intel_th.h14
-rw-r--r--drivers/hwtracing/intel_th/msu.c10
-rw-r--r--drivers/hwtracing/intel_th/msu.h10
-rw-r--r--drivers/hwtracing/intel_th/pci.c10
-rw-r--r--drivers/hwtracing/intel_th/pti.c10
-rw-r--r--drivers/hwtracing/intel_th/pti.h10
-rw-r--r--drivers/hwtracing/intel_th/sth.c10
-rw-r--r--drivers/hwtracing/intel_th/sth.h10
-rw-r--r--drivers/hwtracing/stm/console.c10
-rw-r--r--drivers/hwtracing/stm/core.c10
-rw-r--r--drivers/hwtracing/stm/dummy_stm.c34
-rw-r--r--drivers/hwtracing/stm/heartbeat.c10
-rw-r--r--drivers/hwtracing/stm/policy.c10
-rw-r--r--drivers/hwtracing/stm/stm.h10
-rw-r--r--drivers/i2c/busses/Kconfig33
-rw-r--r--drivers/i2c/busses/Makefile2
-rw-r--r--drivers/i2c/busses/i2c-bfin-twi.c737
-rw-r--r--drivers/i2c/busses/i2c-designware-master.c2
-rw-r--r--drivers/i2c/busses/i2c-exynos5.c63
-rw-r--r--drivers/i2c/busses/i2c-i801.c16
-rw-r--r--drivers/i2c/busses/i2c-imx.c36
-rw-r--r--drivers/i2c/busses/i2c-mv64xxx.c8
-rw-r--r--drivers/i2c/busses/i2c-pca-platform.c34
-rw-r--r--drivers/i2c/busses/i2c-piix4.c61
-rw-r--r--drivers/i2c/busses/i2c-qup.c1471
-rw-r--r--drivers/i2c/busses/i2c-rcar.c4
-rw-r--r--drivers/i2c/busses/i2c-scmi.c35
-rw-r--r--drivers/i2c/busses/i2c-sprd.c22
-rw-r--r--drivers/i2c/busses/i2c-stm32f4.c2
-rw-r--r--drivers/i2c/busses/i2c-stm32f7.c5
-rw-r--r--drivers/i2c/busses/i2c-synquacer.c667
-rw-r--r--drivers/i2c/busses/i2c-xiic.c8
-rw-r--r--drivers/i2c/busses/i2c-xlp9xx.c78
-rw-r--r--drivers/i2c/i2c-core-base.c61
-rw-r--r--drivers/i2c/i2c-core-of.c30
-rw-r--r--drivers/i2c/i2c-core-smbus.c16
-rw-r--r--drivers/i2c/i2c-core.h1
-rw-r--r--drivers/i2c/i2c-dev.c2
-rw-r--r--drivers/i2c/muxes/i2c-mux-pca954x.c55
-rw-r--r--drivers/ide/Kconfig2
-rw-r--r--drivers/ide/ide-cd.c10
-rw-r--r--drivers/ide/ide-cd.h6
-rw-r--r--drivers/ide/ide-disk.c6
-rw-r--r--drivers/ide/ide-generic.c12
-rw-r--r--drivers/ide/ide-probe.c4
-rw-r--r--drivers/iio/accel/bmc150-accel-core.c6
-rw-r--r--drivers/iio/accel/hid-sensor-accel-3d.c2
-rw-r--r--drivers/iio/accel/st_accel_i2c.c3
-rw-r--r--drivers/iio/adc/Kconfig6
-rw-r--r--drivers/iio/adc/ad7476.c26
-rw-r--r--drivers/iio/adc/axp20x_adc.c168
-rw-r--r--drivers/iio/adc/ep93xx_adc.c4
-rw-r--r--drivers/iio/adc/ti-adc161s626.c16
-rw-r--r--drivers/iio/chemical/ams-iaq-core.c17
-rw-r--r--drivers/iio/chemical/atlas-ph-sensor.c16
-rw-r--r--drivers/iio/chemical/ccs811.c10
-rw-r--r--drivers/iio/chemical/vz89x.c17
-rw-r--r--drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c1
-rw-r--r--drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c49
-rw-r--r--drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h2
-rw-r--r--drivers/iio/counter/Kconfig3
-rw-r--r--drivers/iio/dac/Kconfig3
-rw-r--r--drivers/iio/dac/ad5380.c2
-rw-r--r--drivers/iio/dac/ad5764.c2
-rw-r--r--drivers/iio/dummy/Kconfig27
-rw-r--r--drivers/iio/gyro/hid-sensor-gyro-3d.c2
-rw-r--r--drivers/iio/health/max30100.c16
-rw-r--r--drivers/iio/humidity/Kconfig2
-rw-r--r--drivers/iio/humidity/dht11.c2
-rw-r--r--drivers/iio/humidity/hdc100x.c16
-rw-r--r--drivers/iio/humidity/hts221.h21
-rw-r--r--drivers/iio/humidity/hts221_buffer.c39
-rw-r--r--drivers/iio/humidity/hts221_core.c132
-rw-r--r--drivers/iio/humidity/hts221_i2c.c64
-rw-r--r--drivers/iio/humidity/hts221_spi.c81
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h29
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c161
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c104
-rw-r--r--drivers/iio/light/Kconfig10
-rw-r--r--drivers/iio/light/Makefile1
-rw-r--r--drivers/iio/light/apds9960.c16
-rw-r--r--drivers/iio/light/cros_ec_light_prox.c1
-rw-r--r--drivers/iio/light/hid-sensor-als.c2
-rw-r--r--drivers/iio/light/lm3533-als.c2
-rw-r--r--drivers/iio/light/lv0104cs.c531
-rw-r--r--drivers/iio/magnetometer/hid-sensor-magn-3d.c2
-rw-r--r--drivers/iio/potentiometer/Kconfig21
-rw-r--r--drivers/iio/potentiometer/Makefile2
-rw-r--r--drivers/iio/potentiometer/ad5272.c231
-rw-r--r--drivers/iio/potentiometer/ds1803.c2
-rw-r--r--drivers/iio/potentiometer/mcp4018.c194
-rw-r--r--drivers/iio/potentiometer/tpl0102.c16
-rw-r--r--drivers/iio/potentiostat/lmp91000.c16
-rw-r--r--drivers/iio/pressure/ms5611.h2
-rw-r--r--drivers/iio/proximity/as3935.c17
-rw-r--r--drivers/iio/proximity/pulsedlight-lidar-lite-v2.c16
-rw-r--r--drivers/iio/proximity/sx9500.c25
-rw-r--r--drivers/iio/temperature/Kconfig12
-rw-r--r--drivers/iio/temperature/Makefile1
-rw-r--r--drivers/iio/temperature/maxim_thermocouple.c16
-rw-r--r--drivers/iio/temperature/mlx90632.c752
-rw-r--r--drivers/infiniband/Kconfig11
-rw-r--r--drivers/infiniband/core/Makefile4
-rw-r--r--drivers/infiniband/core/addr.c38
-rw-r--r--drivers/infiniband/core/cache.c533
-rw-r--r--drivers/infiniband/core/cm.c67
-rw-r--r--drivers/infiniband/core/cma.c162
-rw-r--r--drivers/infiniband/core/cma_priv.h97
-rw-r--r--drivers/infiniband/core/core_priv.h11
-rw-r--r--drivers/infiniband/core/device.c21
-rw-r--r--drivers/infiniband/core/iwpm_util.c5
-rw-r--r--drivers/infiniband/core/multicast.c26
-rw-r--r--drivers/infiniband/core/nldev.c364
-rw-r--r--drivers/infiniband/core/rdma_core.c13
-rw-r--r--drivers/infiniband/core/restrack.c121
-rw-r--r--drivers/infiniband/core/roce_gid_mgmt.c2
-rw-r--r--drivers/infiniband/core/sa_query.c202
-rw-r--r--drivers/infiniband/core/sysfs.c53
-rw-r--r--drivers/infiniband/core/ucm.c18
-rw-r--r--drivers/infiniband/core/ucma.c87
-rw-r--r--drivers/infiniband/core/uverbs.h41
-rw-r--r--drivers/infiniband/core/uverbs_cmd.c279
-rw-r--r--drivers/infiniband/core/uverbs_ioctl.c60
-rw-r--r--drivers/infiniband/core/uverbs_ioctl_merge.c2
-rw-r--r--drivers/infiniband/core/uverbs_main.c218
-rw-r--r--drivers/infiniband/core/uverbs_std_types.c326
-rw-r--r--drivers/infiniband/core/uverbs_std_types_cq.c210
-rw-r--r--drivers/infiniband/core/uverbs_std_types_dm.c108
-rw-r--r--drivers/infiniband/core/uverbs_std_types_flow_action.c435
-rw-r--r--drivers/infiniband/core/uverbs_std_types_mr.c147
-rw-r--r--drivers/infiniband/core/verbs.c87
-rw-r--r--drivers/infiniband/hw/bnxt_re/ib_verbs.c20
-rw-r--r--drivers/infiniband/hw/bnxt_re/ib_verbs.h6
-rw-r--r--drivers/infiniband/hw/bnxt_re/main.c2
-rw-r--r--drivers/infiniband/hw/bnxt_re/qplib_sp.c2
-rw-r--r--drivers/infiniband/hw/cxgb3/Kconfig9
-rw-r--r--drivers/infiniband/hw/cxgb3/Makefile2
-rw-r--r--drivers/infiniband/hw/cxgb3/cxio_dbg.c206
-rw-r--r--drivers/infiniband/hw/cxgb3/cxio_hal.h9
-rw-r--r--drivers/infiniband/hw/cxgb3/iwch_cq.c6
-rw-r--r--drivers/infiniband/hw/cxgb3/iwch_provider.c5
-rw-r--r--drivers/infiniband/hw/cxgb4/device.c25
-rw-r--r--drivers/infiniband/hw/cxgb4/mem.c3
-rw-r--r--drivers/infiniband/hw/cxgb4/provider.c24
-rw-r--r--drivers/infiniband/hw/hfi1/driver.c1
-rw-r--r--drivers/infiniband/hw/hfi1/file_ops.c2
-rw-r--r--drivers/infiniband/hw/hfi1/hfi.h1
-rw-r--r--drivers/infiniband/hw/hfi1/qp.c1
-rw-r--r--drivers/infiniband/hw/hfi1/trace_ctxts.h12
-rw-r--r--drivers/infiniband/hw/hfi1/user_exp_rcv.c4
-rw-r--r--drivers/infiniband/hw/hfi1/verbs.c2
-rw-r--r--drivers/infiniband/hw/hns/Makefile2
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_ah.c8
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_cq.c54
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_db.c180
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_device.h59
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_hw_v1.c8
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_hw_v2.c82
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_hw_v2.h3
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_main.c41
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_mr.c2
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_pd.c5
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_qp.c70
-rw-r--r--drivers/infiniband/hw/i40iw/i40iw.h11
-rw-r--r--drivers/infiniband/hw/i40iw/i40iw_cm.c129
-rw-r--r--drivers/infiniband/hw/i40iw/i40iw_cm.h5
-rw-r--r--drivers/infiniband/hw/i40iw/i40iw_ctrl.c56
-rw-r--r--drivers/infiniband/hw/i40iw/i40iw_d.h5
-rw-r--r--drivers/infiniband/hw/i40iw/i40iw_hw.c35
-rw-r--r--drivers/infiniband/hw/i40iw/i40iw_main.c2
-rw-r--r--drivers/infiniband/hw/i40iw/i40iw_puda.c8
-rw-r--r--drivers/infiniband/hw/i40iw/i40iw_type.h11
-rw-r--r--drivers/infiniband/hw/i40iw/i40iw_ucontext.h107
-rw-r--r--drivers/infiniband/hw/i40iw/i40iw_utils.c10
-rw-r--r--drivers/infiniband/hw/i40iw/i40iw_verbs.c57
-rw-r--r--drivers/infiniband/hw/mlx4/ah.c10
-rw-r--r--drivers/infiniband/hw/mlx4/main.c102
-rw-r--r--drivers/infiniband/hw/mlx4/mlx4_ib.h19
-rw-r--r--drivers/infiniband/hw/mlx4/mr.c3
-rw-r--r--drivers/infiniband/hw/mlx4/qp.c27
-rw-r--r--drivers/infiniband/hw/mlx5/Makefile1
-rw-r--r--drivers/infiniband/hw/mlx5/ah.c12
-rw-r--r--drivers/infiniband/hw/mlx5/cmd.c104
-rw-r--r--drivers/infiniband/hw/mlx5/cmd.h4
-rw-r--r--drivers/infiniband/hw/mlx5/cq.c72
-rw-r--r--drivers/infiniband/hw/mlx5/ib_rep.c192
-rw-r--r--drivers/infiniband/hw/mlx5/ib_rep.h72
-rw-r--r--drivers/infiniband/hw/mlx5/main.c976
-rw-r--r--drivers/infiniband/hw/mlx5/mlx5_ib.h109
-rw-r--r--drivers/infiniband/hw/mlx5/mr.c265
-rw-r--r--drivers/infiniband/hw/mlx5/qp.c197
-rw-r--r--drivers/infiniband/hw/mthca/mthca_provider.c1
-rw-r--r--drivers/infiniband/hw/nes/nes_verbs.c1
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_ah.c8
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_hw.c22
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_main.c4
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_verbs.c34
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_verbs.h12
-rw-r--r--drivers/infiniband/hw/qedr/main.c16
-rw-r--r--drivers/infiniband/hw/qedr/qedr_hsi_rdma.h4
-rw-r--r--drivers/infiniband/hw/qedr/qedr_roce_cm.c16
-rw-r--r--drivers/infiniband/hw/qedr/verbs.c204
-rw-r--r--drivers/infiniband/hw/qedr/verbs.h6
-rw-r--r--drivers/infiniband/hw/qib/qib.h3
-rw-r--r--drivers/infiniband/hw/qib/qib_diag.c2
-rw-r--r--drivers/infiniband/hw/qib/qib_file_ops.c8
-rw-r--r--drivers/infiniband/hw/qib/qib_iba7322.c10
-rw-r--r--drivers/infiniband/hw/qib/qib_init.c4
-rw-r--r--drivers/infiniband/hw/qib/qib_sdma.c24
-rw-r--r--drivers/infiniband/hw/qib/qib_verbs.c2
-rw-r--r--drivers/infiniband/hw/usnic/usnic_ib_main.c29
-rw-r--r--drivers/infiniband/hw/usnic/usnic_transport.c9
-rw-r--r--drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c32
-rw-r--r--drivers/infiniband/hw/vmw_pvrdma/pvrdma_qp.c2
-rw-r--r--drivers/infiniband/sw/rdmavt/vt.c5
-rw-r--r--drivers/infiniband/sw/rdmavt/vt.h1
-rw-r--r--drivers/infiniband/sw/rxe/rxe.c4
-rw-r--r--drivers/infiniband/sw/rxe/rxe.h6
-rw-r--r--drivers/infiniband/sw/rxe/rxe_av.c5
-rw-r--r--drivers/infiniband/sw/rxe/rxe_cq.c15
-rw-r--r--drivers/infiniband/sw/rxe/rxe_loc.h20
-rw-r--r--drivers/infiniband/sw/rxe/rxe_net.c56
-rw-r--r--drivers/infiniband/sw/rxe/rxe_qp.c35
-rw-r--r--drivers/infiniband/sw/rxe/rxe_queue.c24
-rw-r--r--drivers/infiniband/sw/rxe/rxe_queue.h5
-rw-r--r--drivers/infiniband/sw/rxe/rxe_recv.c16
-rw-r--r--drivers/infiniband/sw/rxe/rxe_resp.c15
-rw-r--r--drivers/infiniband/sw/rxe/rxe_srq.c44
-rw-r--r--drivers/infiniband/sw/rxe/rxe_verbs.c101
-rw-r--r--drivers/infiniband/sw/rxe/rxe_verbs.h2
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib.h5
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_ib.c2
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.c181
-rw-r--r--drivers/infiniband/ulp/srpt/ib_srpt.c396
-rw-r--r--drivers/infiniband/ulp/srpt/ib_srpt.h8
-rw-r--r--drivers/input/joystick/Kconfig10
-rw-r--r--drivers/input/joystick/Makefile1
-rw-r--r--drivers/input/joystick/analog.c2
-rw-r--r--drivers/input/joystick/pxrc.c303
-rw-r--r--drivers/input/joystick/xpad.c9
-rw-r--r--drivers/input/keyboard/Kconfig9
-rw-r--r--drivers/input/keyboard/Makefile1
-rw-r--r--drivers/input/keyboard/bf54x-keys.c396
-rw-r--r--drivers/input/keyboard/gpio_keys.c145
-rw-r--r--drivers/input/keyboard/stmpe-keypad.c16
-rw-r--r--drivers/input/misc/Kconfig18
-rw-r--r--drivers/input/misc/Makefile2
-rw-r--r--drivers/input/misc/bfin_rotary.c294
-rw-r--r--drivers/input/misc/rave-sp-pwrbutton.c94
-rw-r--r--drivers/input/mouse/alps.c64
-rw-r--r--drivers/input/mouse/appletouch.c6
-rw-r--r--drivers/input/mouse/elantech.c40
-rw-r--r--drivers/input/mouse/lifebook.c62
-rw-r--r--drivers/input/mouse/logips2pp.c152
-rw-r--r--drivers/input/mouse/psmouse-base.c189
-rw-r--r--drivers/input/mouse/psmouse.h5
-rw-r--r--drivers/input/mouse/sentelic.c11
-rw-r--r--drivers/input/mouse/synaptics.c90
-rw-r--r--drivers/input/mouse/synaptics_usb.c31
-rw-r--r--drivers/input/mouse/trackpoint.c60
-rw-r--r--drivers/input/serio/gscps2.c11
-rw-r--r--drivers/input/serio/i8042-x86ia64io.h24
-rw-r--r--drivers/input/serio/libps2.c322
-rw-r--r--drivers/input/tablet/pegasus_notetaker.c32
-rw-r--r--drivers/input/touchscreen/atmel_mxt_ts.c231
-rw-r--r--drivers/input/touchscreen/s6sy761.c2
-rw-r--r--drivers/input/touchscreen/silead.c1
-rw-r--r--drivers/input/touchscreen/stmfts.c4
-rw-r--r--drivers/input/touchscreen/sur40.c178
-rw-r--r--drivers/input/touchscreen/usbtouchscreen.c24
-rw-r--r--drivers/iommu/Kconfig2
-rw-r--r--drivers/iommu/amd_iommu.c395
-rw-r--r--drivers/iommu/amd_iommu_init.c2
-rw-r--r--drivers/iommu/amd_iommu_types.h6
-rw-r--r--drivers/iommu/arm-smmu-v3.c542
-rw-r--r--drivers/iommu/dma-iommu.c8
-rw-r--r--drivers/iommu/dmar.c2
-rw-r--r--drivers/iommu/exynos-iommu.c14
-rw-r--r--drivers/iommu/intel-iommu.c71
-rw-r--r--drivers/iommu/intel-svm.c17
-rw-r--r--drivers/iommu/io-pgtable-arm-v7s.c21
-rw-r--r--drivers/iommu/io-pgtable-arm.c91
-rw-r--r--drivers/iommu/io-pgtable.h4
-rw-r--r--drivers/iommu/iommu.c6
-rw-r--r--drivers/iommu/irq_remapping.c4
-rw-r--r--drivers/iommu/mtk_iommu.c15
-rw-r--r--drivers/iommu/mtk_iommu.h1
-rw-r--r--drivers/iommu/mtk_iommu_v1.c55
-rw-r--r--drivers/iommu/omap-iommu.c2
-rw-r--r--drivers/iommu/rockchip-iommu.c592
-rw-r--r--drivers/irqchip/Kconfig20
-rw-r--r--drivers/irqchip/Makefile6
-rw-r--r--drivers/irqchip/irq-ativic32.c107
-rw-r--r--drivers/irqchip/irq-gic-common.c9
-rw-r--r--drivers/irqchip/irq-gic-v3-its-fsl-mc-msi.c100
-rw-r--r--drivers/irqchip/irq-gic-v3-its.c270
-rw-r--r--drivers/irqchip/irq-gic-v3.c99
-rw-r--r--drivers/irqchip/irq-gic.c44
-rw-r--r--drivers/irqchip/irq-metag-ext.c871
-rw-r--r--drivers/irqchip/irq-metag.c343
-rw-r--r--drivers/irqchip/irq-mscc-ocelot.c118
-rw-r--r--drivers/irqchip/irq-renesas-intc-irqpin.c40
-rw-r--r--drivers/irqchip/irq-renesas-irqc.c30
-rw-r--r--drivers/irqchip/qcom-pdc.c311
-rw-r--r--drivers/isdn/hisax/Kconfig10
-rw-r--r--drivers/isdn/mISDN/dsp_hwec.c8
-rw-r--r--drivers/isdn/mISDN/l1oip_core.c14
-rw-r--r--drivers/isdn/mISDN/socket.c5
-rw-r--r--drivers/leds/Kconfig29
-rw-r--r--drivers/leds/Makefile1
-rw-r--r--drivers/leds/leds-apu.c28
-rw-r--r--drivers/leds/leds-mlxreg.c281
-rw-r--r--drivers/leds/trigger/ledtrig-disk.c12
-rw-r--r--drivers/lightnvm/core.c240
-rw-r--r--drivers/lightnvm/pblk-cache.c4
-rw-r--r--drivers/lightnvm/pblk-core.c202
-rw-r--r--drivers/lightnvm/pblk-gc.c12
-rw-r--r--drivers/lightnvm/pblk-init.c820
-rw-r--r--drivers/lightnvm/pblk-map.c6
-rw-r--r--drivers/lightnvm/pblk-rb.c21
-rw-r--r--drivers/lightnvm/pblk-read.c2
-rw-r--r--drivers/lightnvm/pblk-recovery.c91
-rw-r--r--drivers/lightnvm/pblk-rl.c2
-rw-r--r--drivers/lightnvm/pblk-sysfs.c235
-rw-r--r--drivers/lightnvm/pblk-write.c2
-rw-r--r--drivers/lightnvm/pblk.h304
-rw-r--r--drivers/macintosh/adb-iop.c14
-rw-r--r--drivers/macintosh/ans-lcd.c1
-rw-r--r--drivers/macintosh/macio-adb.c15
-rw-r--r--drivers/macintosh/rack-meter.c6
-rw-r--r--drivers/macintosh/via-macii.c14
-rw-r--r--drivers/macintosh/via-pmu.c16
-rw-r--r--drivers/macintosh/via-pmu68k.c103
-rw-r--r--drivers/mailbox/Kconfig10
-rw-r--r--drivers/mailbox/Makefile2
-rw-r--r--drivers/mailbox/bcm-flexrm-mailbox.c3
-rw-r--r--drivers/mailbox/hi3660-mailbox.c312
-rw-r--r--drivers/mcb/mcb-pci.c1
-rw-r--r--drivers/md/Kconfig2
-rw-r--r--drivers/md/bcache/alloc.c3
-rw-r--r--drivers/md/bcache/bcache.h57
-rw-r--r--drivers/md/bcache/bset.c4
-rw-r--r--drivers/md/bcache/bset.h5
-rw-r--r--drivers/md/bcache/btree.c26
-rw-r--r--drivers/md/bcache/closure.c17
-rw-r--r--drivers/md/bcache/closure.h5
-rw-r--r--drivers/md/bcache/debug.c14
-rw-r--r--drivers/md/bcache/extents.c2
-rw-r--r--drivers/md/bcache/io.c16
-rw-r--r--drivers/md/bcache/journal.c8
-rw-r--r--drivers/md/bcache/request.c186
-rw-r--r--drivers/md/bcache/super.c160
-rw-r--r--drivers/md/bcache/sysfs.c55
-rw-r--r--drivers/md/bcache/util.c25
-rw-r--r--drivers/md/bcache/util.h6
-rw-r--r--drivers/md/bcache/writeback.c92
-rw-r--r--drivers/md/bcache/writeback.h4
-rw-r--r--drivers/md/dm-bufio.c279
-rw-r--r--drivers/md/dm-bufio.h148
-rw-r--r--drivers/md/dm-cache-target.c3
-rw-r--r--drivers/md/dm-crypt.c69
-rw-r--r--drivers/md/dm-era-target.c3
-rw-r--r--drivers/md/dm-flakey.c3
-rw-r--r--drivers/md/dm-integrity.c5
-rw-r--r--drivers/md/dm-ioctl.c2
-rw-r--r--drivers/md/dm-linear.c10
-rw-r--r--drivers/md/dm-log-writes.c112
-rw-r--r--drivers/md/dm-mpath.c10
-rw-r--r--drivers/md/dm-raid.c33
-rw-r--r--drivers/md/dm-snap-persistent.c2
-rw-r--r--drivers/md/dm-stripe.c18
-rw-r--r--drivers/md/dm-switch.c7
-rw-r--r--drivers/md/dm-table.c47
-rw-r--r--drivers/md/dm-target.c2
-rw-r--r--drivers/md/dm-thin.c3
-rw-r--r--drivers/md/dm-unstripe.c37
-rw-r--r--drivers/md/dm-verity-target.c71
-rw-r--r--drivers/md/dm-verity.h3
-rw-r--r--drivers/md/dm-zoned-target.c3
-rw-r--r--drivers/md/dm.c155
-rw-r--r--drivers/md/md-linear.c4
-rw-r--r--drivers/md/md.c16
-rw-r--r--drivers/md/persistent-data/dm-block-manager.c2
-rw-r--r--drivers/md/raid0.c4
-rw-r--r--drivers/md/raid1.c31
-rw-r--r--drivers/md/raid10.c6
-rw-r--r--drivers/md/raid5.c4
-rw-r--r--drivers/media/Kconfig1
-rw-r--r--drivers/media/cec/Kconfig6
-rw-r--r--drivers/media/cec/Makefile4
-rw-r--r--drivers/media/cec/cec-adap.c54
-rw-r--r--drivers/media/cec/cec-api.c14
-rw-r--r--drivers/media/cec/cec-core.c72
-rw-r--r--drivers/media/cec/cec-edid.c14
-rw-r--r--drivers/media/cec/cec-notifier.c14
-rw-r--r--drivers/media/cec/cec-pin-error-inj.c342
-rw-r--r--drivers/media/cec/cec-pin-priv.h148
-rw-r--r--drivers/media/cec/cec-pin.c678
-rw-r--r--drivers/media/cec/cec-priv.h14
-rw-r--r--drivers/media/common/siano/smscoreapi.c33
-rw-r--r--drivers/media/common/siano/smscoreapi.h2
-rw-r--r--drivers/media/common/v4l2-tpg/v4l2-tpg-colors.c14
-rw-r--r--drivers/media/common/v4l2-tpg/v4l2-tpg-core.c18
-rw-r--r--drivers/media/common/videobuf2/videobuf2-core.c9
-rw-r--r--drivers/media/common/videobuf2/videobuf2-vmalloc.c2
-rw-r--r--drivers/media/dvb-core/dvb_ca_en50221.c4
-rw-r--r--drivers/media/dvb-core/dvb_frontend.c6
-rw-r--r--drivers/media/dvb-core/dvbdev.c50
-rw-r--r--drivers/media/dvb-frontends/Kconfig32
-rw-r--r--drivers/media/dvb-frontends/Makefile2
-rw-r--r--drivers/media/dvb-frontends/af9013.c909
-rw-r--r--drivers/media/dvb-frontends/af9013.h48
-rw-r--r--drivers/media/dvb-frontends/af9013_priv.h1558
-rw-r--r--drivers/media/dvb-frontends/cxd2099.c704
-rw-r--r--drivers/media/dvb-frontends/cxd2099.h32
-rw-r--r--drivers/media/dvb-frontends/cxd2880/Kconfig8
-rw-r--r--drivers/media/dvb-frontends/cxd2880/Makefile18
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880.h29
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_common.c21
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_common.h19
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c129
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h23
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h29
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h74
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h385
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c72
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h27
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_io.c66
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_io.h54
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h34
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c113
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h26
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c3519
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h365
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h12
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c919
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h45
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c1217
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h65
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c1878
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h135
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c775
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h77
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c150
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h29
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_top.c1947
-rw-r--r--drivers/media/dvb-frontends/dib0090.c4
-rw-r--r--drivers/media/dvb-frontends/dib7000p.c2
-rw-r--r--drivers/media/dvb-frontends/dib8000.c2
-rw-r--r--drivers/media/dvb-frontends/dibx000_common.c2
-rw-r--r--drivers/media/dvb-frontends/dibx000_common.h2
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/bsp_i2c.h139
-rw-r--r--drivers/media/dvb-frontends/lgdt3306a.c69
-rw-r--r--drivers/media/dvb-frontends/mb86a16.c8
-rw-r--r--drivers/media/dvb-frontends/mxl5xx.c34
-rw-r--r--drivers/media/dvb-frontends/rtl2832.c4
-rw-r--r--drivers/media/dvb-frontends/s5h1409.c8
-rw-r--r--drivers/media/dvb-frontends/s5h1409.h8
-rw-r--r--drivers/media/dvb-frontends/s5h1411.c8
-rw-r--r--drivers/media/dvb-frontends/s5h1411.h8
-rw-r--r--drivers/media/dvb-frontends/s5h1432.h8
-rw-r--r--drivers/media/dvb-frontends/si2168.c49
-rw-r--r--drivers/media/dvb-frontends/si2168.h4
-rw-r--r--drivers/media/dvb-frontends/si2168_priv.h1
-rw-r--r--drivers/media/dvb-frontends/sp887x.c6
-rw-r--r--drivers/media/dvb-frontends/stb0899_reg.h8
-rw-r--r--drivers/media/dvb-frontends/stv0367_priv.h1
-rw-r--r--drivers/media/dvb-frontends/stv0900_priv.h1
-rw-r--r--drivers/media/dvb-frontends/stv0900_sw.c6
-rw-r--r--drivers/media/dvb-frontends/stv0910.c19
-rw-r--r--drivers/media/dvb-frontends/ves1820.c2
-rw-r--r--drivers/media/i2c/Kconfig66
-rw-r--r--drivers/media/i2c/Makefile6
-rw-r--r--drivers/media/i2c/ad9389b.c14
-rw-r--r--drivers/media/i2c/adv748x/adv748x-afe.c3
-rw-r--r--drivers/media/i2c/adv748x/adv748x-core.c187
-rw-r--r--drivers/media/i2c/adv748x/adv748x-hdmi.c3
-rw-r--r--drivers/media/i2c/adv748x/adv748x.h14
-rw-r--r--drivers/media/i2c/adv7511.c14
-rw-r--r--drivers/media/i2c/adv7604.c76
-rw-r--r--drivers/media/i2c/adv7842.c15
-rw-r--r--drivers/media/i2c/cx25840/cx25840-core.c28
-rw-r--r--drivers/media/i2c/dw9714.c14
-rw-r--r--drivers/media/i2c/imx274.c2
-rw-r--r--drivers/media/i2c/ir-kbd-i2c.c20
-rw-r--r--drivers/media/i2c/max2175.c2
-rw-r--r--drivers/media/i2c/msp3400-kthreads.c2
-rw-r--r--drivers/media/i2c/mt9t112.c1140
-rw-r--r--drivers/media/i2c/mt9v011.c29
-rw-r--r--drivers/media/i2c/ov13858.c72
-rw-r--r--drivers/media/i2c/ov2685.c845
-rw-r--r--drivers/media/i2c/ov5640.c137
-rw-r--r--drivers/media/i2c/ov5645.c29
-rw-r--r--drivers/media/i2c/ov5670.c59
-rw-r--r--drivers/media/i2c/ov5695.c1399
-rw-r--r--drivers/media/i2c/ov6650.c33
-rw-r--r--drivers/media/i2c/ov7670.c120
-rw-r--r--drivers/media/i2c/ov772x.c1356
-rw-r--r--drivers/media/i2c/ov7740.c31
-rw-r--r--drivers/media/i2c/ov9650.c134
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3-core.c6
-rw-r--r--drivers/media/i2c/saa6588.c4
-rw-r--r--drivers/media/i2c/soc_camera/Kconfig12
-rw-r--r--drivers/media/i2c/soc_camera/Makefile2
-rw-r--r--drivers/media/i2c/soc_camera/mt9t112.c2
-rw-r--r--drivers/media/i2c/sr030pc30.c7
-rw-r--r--drivers/media/i2c/tc358743.c15
-rw-r--r--drivers/media/i2c/tc358743_regs.h15
-rw-r--r--drivers/media/i2c/tda1997x.c2820
-rw-r--r--drivers/media/i2c/tda1997x_regs.h641
-rw-r--r--drivers/media/i2c/tda9840.c6
-rw-r--r--drivers/media/i2c/tvaudio.c92
-rw-r--r--drivers/media/i2c/tvp514x.c37
-rw-r--r--drivers/media/i2c/tw9910.c1027
-rw-r--r--drivers/media/i2c/vs6624.c27
-rw-r--r--drivers/media/media-device.c9
-rw-r--r--drivers/media/media-entity.c16
-rw-r--r--drivers/media/pci/bt8xx/bttv-driver.c4
-rw-r--r--drivers/media/pci/bt8xx/bttv-input.c6
-rw-r--r--drivers/media/pci/cobalt/Makefile1
-rw-r--r--drivers/media/pci/cobalt/cobalt-alsa-main.c14
-rw-r--r--drivers/media/pci/cobalt/cobalt-alsa-pcm.c14
-rw-r--r--drivers/media/pci/cobalt/cobalt-alsa-pcm.h14
-rw-r--r--drivers/media/pci/cobalt/cobalt-alsa.h14
-rw-r--r--drivers/media/pci/cobalt/cobalt-cpld.c14
-rw-r--r--drivers/media/pci/cobalt/cobalt-cpld.h14
-rw-r--r--drivers/media/pci/cobalt/cobalt-driver.c14
-rw-r--r--drivers/media/pci/cobalt/cobalt-driver.h14
-rw-r--r--drivers/media/pci/cobalt/cobalt-flash.c14
-rw-r--r--drivers/media/pci/cobalt/cobalt-flash.h14
-rw-r--r--drivers/media/pci/cobalt/cobalt-i2c.c14
-rw-r--r--drivers/media/pci/cobalt/cobalt-i2c.h14
-rw-r--r--drivers/media/pci/cobalt/cobalt-irq.c14
-rw-r--r--drivers/media/pci/cobalt/cobalt-irq.h14
-rw-r--r--drivers/media/pci/cobalt/cobalt-omnitek.c14
-rw-r--r--drivers/media/pci/cobalt/cobalt-omnitek.h14
-rw-r--r--drivers/media/pci/cobalt/cobalt-v4l2.c14
-rw-r--r--drivers/media/pci/cobalt/cobalt-v4l2.h14
-rw-r--r--drivers/media/pci/cobalt/m00233_video_measure_memmap_package.h14
-rw-r--r--drivers/media/pci/cobalt/m00235_fdma_packer_memmap_package.h14
-rw-r--r--drivers/media/pci/cobalt/m00389_cvi_memmap_package.h14
-rw-r--r--drivers/media/pci/cobalt/m00460_evcnt_memmap_package.h14
-rw-r--r--drivers/media/pci/cobalt/m00473_freewheel_memmap_package.h14
-rw-r--r--drivers/media/pci/cobalt/m00479_clk_loss_detector_memmap_package.h14
-rw-r--r--drivers/media/pci/cobalt/m00514_syncgen_flow_evcnt_memmap_package.h14
-rw-r--r--drivers/media/pci/cx18/cx18-alsa-main.c1
-rw-r--r--drivers/media/pci/cx18/cx18-alsa-mixer.c170
-rw-r--r--drivers/media/pci/cx18/cx18-alsa-mixer.h18
-rw-r--r--drivers/media/pci/cx18/cx18-dvb.c4
-rw-r--r--drivers/media/pci/cx23885/cx23885-alsa.c5
-rw-r--r--drivers/media/pci/cx23885/cx23885-cards.c108
-rw-r--r--drivers/media/pci/cx23885/cx23885-core.c26
-rw-r--r--drivers/media/pci/cx23885/cx23885-dvb.c90
-rw-r--r--drivers/media/pci/cx23885/cx23885-input.c3
-rw-r--r--drivers/media/pci/cx23885/cx23885-video.c5
-rw-r--r--drivers/media/pci/cx23885/cx23885.h4
-rw-r--r--drivers/media/pci/cx25821/cx25821-core.c7
-rw-r--r--drivers/media/pci/cx88/cx88-alsa.c8
-rw-r--r--drivers/media/pci/cx88/cx88-cards.c2
-rw-r--r--drivers/media/pci/cx88/cx88-dvb.c12
-rw-r--r--drivers/media/pci/cx88/cx88-input.c4
-rw-r--r--drivers/media/pci/ddbridge/Kconfig1
-rw-r--r--drivers/media/pci/ddbridge/Makefile3
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-ci.c53
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-core.c36
-rw-r--r--drivers/media/pci/ddbridge/ddbridge.h1
-rw-r--r--drivers/media/pci/intel/ipu3/ipu3-cio2.c16
-rw-r--r--drivers/media/pci/intel/ipu3/ipu3-cio2.h14
-rw-r--r--drivers/media/pci/ivtv/ivtv-alsa-main.c11
-rw-r--r--drivers/media/pci/ivtv/ivtv-alsa-mixer.c165
-rw-r--r--drivers/media/pci/ivtv/ivtv-alsa-mixer.h18
-rw-r--r--drivers/media/pci/ivtv/ivtvfb.c12
-rw-r--r--drivers/media/pci/mantis/mantis_vp3028.c38
-rw-r--r--drivers/media/pci/mantis/mantis_vp3028.h33
-rw-r--r--drivers/media/pci/ngene/Kconfig7
-rw-r--r--drivers/media/pci/ngene/Makefile3
-rw-r--r--drivers/media/pci/ngene/ngene-cards.c575
-rw-r--r--drivers/media/pci/ngene/ngene-core.c117
-rw-r--r--drivers/media/pci/ngene/ngene-dvb.c151
-rw-r--r--drivers/media/pci/ngene/ngene-i2c.c2
-rw-r--r--drivers/media/pci/ngene/ngene.h24
-rw-r--r--drivers/media/pci/saa7134/saa7134-alsa.c5
-rw-r--r--drivers/media/pci/saa7134/saa7134-dvb.c2
-rw-r--r--drivers/media/pci/saa7134/saa7134-input.c46
-rw-r--r--drivers/media/pci/saa7134/saa7134-video.c6
-rw-r--r--drivers/media/pci/saa7164/saa7164-dvb.c2
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-g723.c39
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-p2m.c7
-rw-r--r--drivers/media/pci/ttpci/ttpci-eeprom.c9
-rw-r--r--drivers/media/pci/zoran/zoran_driver.c4
-rw-r--r--drivers/media/platform/Kconfig33
-rw-r--r--drivers/media/platform/Makefile5
-rw-r--r--drivers/media/platform/arv.c884
-rw-r--r--drivers/media/platform/atmel/atmel-isc.c12
-rw-r--r--drivers/media/platform/atmel/atmel-isi.c12
-rw-r--r--drivers/media/platform/blackfin/Kconfig16
-rw-r--r--drivers/media/platform/blackfin/Makefile2
-rw-r--r--drivers/media/platform/blackfin/bfin_capture.c989
-rw-r--r--drivers/media/platform/blackfin/ppi.c361
-rw-r--r--drivers/media/platform/cec-gpio/cec-gpio.c14
-rw-r--r--drivers/media/platform/coda/coda-bit.c46
-rw-r--r--drivers/media/platform/coda/coda-common.c44
-rw-r--r--drivers/media/platform/coda/coda.h3
-rw-r--r--drivers/media/platform/davinci/vpss.c2
-rw-r--r--drivers/media/platform/exynos4-is/fimc-capture.c7
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-regs.c2
-rw-r--r--drivers/media/platform/marvell-ccic/mcam-core.c12
-rw-r--r--drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c4
-rw-r--r--drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c2
-rw-r--r--drivers/media/platform/omap/omap_vout_vrfb.c3
-rw-r--r--drivers/media/platform/qcom/venus/firmware.c2
-rw-r--r--drivers/media/platform/qcom/venus/hfi.c8
-rw-r--r--drivers/media/platform/qcom/venus/hfi_msgs.c4
-rw-r--r--drivers/media/platform/qcom/venus/vdec.c13
-rw-r--r--drivers/media/platform/qcom/venus/venc.c13
-rw-r--r--drivers/media/platform/rcar-vin/rcar-dma.c206
-rw-r--r--drivers/media/platform/rcar-vin/rcar-vin.h10
-rw-r--r--drivers/media/platform/rcar_drif.c3
-rw-r--r--drivers/media/platform/renesas-ceu.c1677
-rw-r--r--drivers/media/platform/rockchip/rga/rga-buf.c3
-rw-r--r--drivers/media/platform/rockchip/rga/rga.c2
-rw-r--r--drivers/media/platform/s3c-camif/camif-capture.c7
-rw-r--r--drivers/media/platform/s5p-mfc/regs-mfc-v10.h87
-rw-r--r--drivers/media/platform/s5p-mfc/regs-mfc-v8.h2
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc.c28
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c9
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_common.h68
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c8
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_dec.c48
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_enc.c599
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr.h14
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c397
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.h15
-rw-r--r--drivers/media/platform/sh_veu.c4
-rw-r--r--drivers/media/platform/soc_camera/soc_camera.c12
-rw-r--r--drivers/media/platform/stm32/stm32-cec.c5
-rw-r--r--drivers/media/platform/stm32/stm32-dcmi.c305
-rw-r--r--drivers/media/platform/via-camera.c4
-rw-r--r--drivers/media/platform/vimc/vimc-common.c4
-rw-r--r--drivers/media/platform/vimc/vimc-debayer.c2
-rw-r--r--drivers/media/platform/vimc/vimc-scaler.c2
-rw-r--r--drivers/media/platform/vimc/vimc-sensor.c10
-rw-r--r--drivers/media/platform/vivid/vivid-cec.c33
-rw-r--r--drivers/media/platform/vivid/vivid-cec.h14
-rw-r--r--drivers/media/platform/vivid/vivid-core.c14
-rw-r--r--drivers/media/platform/vivid/vivid-core.h14
-rw-r--r--drivers/media/platform/vivid/vivid-ctrls.c16
-rw-r--r--drivers/media/platform/vivid/vivid-ctrls.h14
-rw-r--r--drivers/media/platform/vivid/vivid-kthread-cap.c14
-rw-r--r--drivers/media/platform/vivid/vivid-kthread-cap.h14
-rw-r--r--drivers/media/platform/vivid/vivid-kthread-out.c14
-rw-r--r--drivers/media/platform/vivid/vivid-kthread-out.h14
-rw-r--r--drivers/media/platform/vivid/vivid-osd.c14
-rw-r--r--drivers/media/platform/vivid/vivid-osd.h14
-rw-r--r--drivers/media/platform/vivid/vivid-radio-common.c14
-rw-r--r--drivers/media/platform/vivid/vivid-radio-common.h14
-rw-r--r--drivers/media/platform/vivid/vivid-radio-rx.c16
-rw-r--r--drivers/media/platform/vivid/vivid-radio-rx.h14
-rw-r--r--drivers/media/platform/vivid/vivid-radio-tx.c14
-rw-r--r--drivers/media/platform/vivid/vivid-radio-tx.h14
-rw-r--r--drivers/media/platform/vivid/vivid-rds-gen.c14
-rw-r--r--drivers/media/platform/vivid/vivid-rds-gen.h14
-rw-r--r--drivers/media/platform/vivid/vivid-sdr-cap.c14
-rw-r--r--drivers/media/platform/vivid/vivid-sdr-cap.h14
-rw-r--r--drivers/media/platform/vivid/vivid-vbi-cap.c14
-rw-r--r--drivers/media/platform/vivid/vivid-vbi-cap.h14
-rw-r--r--drivers/media/platform/vivid/vivid-vbi-gen.c14
-rw-r--r--drivers/media/platform/vivid/vivid-vbi-gen.h14
-rw-r--r--drivers/media/platform/vivid/vivid-vbi-out.c14
-rw-r--r--drivers/media/platform/vivid/vivid-vbi-out.h14
-rw-r--r--drivers/media/platform/vivid/vivid-vid-cap.c20
-rw-r--r--drivers/media/platform/vivid/vivid-vid-cap.h14
-rw-r--r--drivers/media/platform/vivid/vivid-vid-common.c17
-rw-r--r--drivers/media/platform/vivid/vivid-vid-common.h14
-rw-r--r--drivers/media/platform/vivid/vivid-vid-out.c14
-rw-r--r--drivers/media/platform/vivid/vivid-vid-out.h14
-rw-r--r--drivers/media/platform/vsp1/vsp1_dl.c3
-rw-r--r--drivers/media/platform/vsp1/vsp1_drm.c30
-rw-r--r--drivers/media/platform/vsp1/vsp1_lif.c12
-rw-r--r--drivers/media/platform/vsp1/vsp1_regs.h8
-rw-r--r--drivers/media/platform/vsp1/vsp1_wpf.c2
-rw-r--r--drivers/media/radio/radio-mr800.c2
-rw-r--r--drivers/media/radio/radio-raremono.c14
-rw-r--r--drivers/media/radio/radio-wl1273.c2
-rw-r--r--drivers/media/radio/si470x/radio-si470x-common.c17
-rw-r--r--drivers/media/radio/si470x/radio-si470x-i2c.c32
-rw-r--r--drivers/media/radio/si470x/radio-si470x-usb.c2
-rw-r--r--drivers/media/radio/si470x/radio-si470x.h2
-rw-r--r--drivers/media/radio/si4713/radio-usb-si4713.c14
-rw-r--r--drivers/media/rc/Kconfig32
-rw-r--r--drivers/media/rc/Makefile2
-rw-r--r--drivers/media/rc/img-ir/Kconfig2
-rw-r--r--drivers/media/rc/imon.c170
-rw-r--r--drivers/media/rc/imon_raw.c199
-rw-r--r--drivers/media/rc/ir-hix5hd2.c35
-rw-r--r--drivers/media/rc/ir-imon-decoder.c193
-rw-r--r--drivers/media/rc/ir-jvc-decoder.c14
-rw-r--r--drivers/media/rc/ir-mce_kbd-decoder.c60
-rw-r--r--drivers/media/rc/ir-nec-decoder.c20
-rw-r--r--drivers/media/rc/ir-rc5-decoder.c12
-rw-r--r--drivers/media/rc/ir-rc6-decoder.c26
-rw-r--r--drivers/media/rc/ir-sanyo-decoder.c18
-rw-r--r--drivers/media/rc/ir-sharp-decoder.c17
-rw-r--r--drivers/media/rc/ir-sony-decoder.c14
-rw-r--r--drivers/media/rc/ir-spi.c24
-rw-r--r--drivers/media/rc/ir-xmp-decoder.c29
-rw-r--r--drivers/media/rc/keymaps/Makefile1
-rw-r--r--drivers/media/rc/keymaps/rc-imon-pad.c3
-rw-r--r--drivers/media/rc/keymaps/rc-imon-rsc.c81
-rw-r--r--drivers/media/rc/lirc_dev.c20
-rw-r--r--drivers/media/rc/mceusb.c160
-rw-r--r--drivers/media/rc/meson-ir.c7
-rw-r--r--drivers/media/rc/rc-core-priv.h18
-rw-r--r--drivers/media/rc/rc-ir-raw.c60
-rw-r--r--drivers/media/rc/rc-main.c100
-rw-r--r--drivers/media/rc/sunxi-cir.c19
-rw-r--r--drivers/media/spi/Kconfig14
-rw-r--r--drivers/media/spi/Makefile5
-rw-r--r--drivers/media/spi/cxd2880-spi.c670
-rw-r--r--drivers/media/tuners/e4000.c16
-rw-r--r--drivers/media/tuners/fc2580.c16
-rw-r--r--drivers/media/tuners/msi001.c19
-rw-r--r--drivers/media/tuners/r820t.c4
-rw-r--r--drivers/media/usb/au0828/Kconfig5
-rw-r--r--drivers/media/usb/au0828/au0828-video.c4
-rw-r--r--drivers/media/usb/cpia2/cpia2_usb.c3
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-cards.c87
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-dvb.c391
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-video.c2
-rw-r--r--drivers/media/usb/cx231xx/cx231xx.h3
-rw-r--r--drivers/media/usb/dvb-usb-v2/Kconfig3
-rw-r--r--drivers/media/usb/dvb-usb-v2/af9015.c985
-rw-r--r--drivers/media/usb/dvb-usb-v2/af9015.h20
-rw-r--r--drivers/media/usb/dvb-usb-v2/dvb_usb.h4
-rw-r--r--drivers/media/usb/dvb-usb-v2/dvb_usb_core.c24
-rw-r--r--drivers/media/usb/dvb-usb/cxusb.c141
-rw-r--r--drivers/media/usb/dvb-usb/dib0700_devices.c2
-rw-r--r--drivers/media/usb/em28xx/em28xx-audio.c116
-rw-r--r--drivers/media/usb/em28xx/em28xx-camera.c49
-rw-r--r--drivers/media/usb/em28xx/em28xx-cards.c905
-rw-r--r--drivers/media/usb/em28xx/em28xx-core.c231
-rw-r--r--drivers/media/usb/em28xx/em28xx-dvb.c1015
-rw-r--r--drivers/media/usb/em28xx/em28xx-i2c.c173
-rw-r--r--drivers/media/usb/em28xx/em28xx-input.c172
-rw-r--r--drivers/media/usb/em28xx/em28xx-reg.h52
-rw-r--r--drivers/media/usb/em28xx/em28xx-v4l.h27
-rw-r--r--drivers/media/usb/em28xx/em28xx-vbi.c39
-rw-r--r--drivers/media/usb/em28xx/em28xx-video.c391
-rw-r--r--drivers/media/usb/em28xx/em28xx.h396
-rw-r--r--drivers/media/usb/go7007/snd-go7007.c2
-rw-r--r--drivers/media/usb/gspca/Kconfig2
-rw-r--r--drivers/media/usb/gspca/dtcs033.c6
-rw-r--r--drivers/media/usb/s2255/s2255drv.c12
-rw-r--r--drivers/media/usb/siano/smsusb.c4
-rw-r--r--drivers/media/usb/tm6000/tm6000-cards.c2
-rw-r--r--drivers/media/usb/tm6000/tm6000-video.c5
-rw-r--r--drivers/media/usb/usbtv/usbtv-core.c3
-rw-r--r--drivers/media/usb/uvc/uvc_ctrl.c114
-rw-r--r--drivers/media/usb/uvc/uvc_driver.c97
-rw-r--r--drivers/media/usb/uvc/uvc_isight.c6
-rw-r--r--drivers/media/usb/uvc/uvc_status.c4
-rw-r--r--drivers/media/usb/uvc/uvc_v4l2.c141
-rw-r--r--drivers/media/usb/uvc/uvc_video.c47
-rw-r--r--drivers/media/usb/uvc/uvcvideo.h320
-rw-r--r--drivers/media/usb/zr364xx/zr364xx.c5
-rw-r--r--drivers/media/v4l2-core/tuner-core.c15
-rw-r--r--drivers/media/v4l2-core/v4l2-common.c82
-rw-r--r--drivers/media/v4l2-core/v4l2-compat-ioctl32.c4
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls.c119
-rw-r--r--drivers/media/v4l2-core/v4l2-dev.c8
-rw-r--r--drivers/media/v4l2-core/v4l2-dv-timings.c156
-rw-r--r--drivers/media/v4l2-core/v4l2-ioctl.c18
-rw-r--r--drivers/media/v4l2-core/v4l2-mc.c12
-rw-r--r--drivers/media/v4l2-core/v4l2-subdev.c50
-rw-r--r--drivers/media/v4l2-core/videobuf-dma-sg.c5
-rw-r--r--drivers/memory/emif-asm-offsets.c72
-rw-r--r--drivers/memory/emif.c2
-rw-r--r--drivers/memory/samsung/Kconfig1
-rw-r--r--drivers/memory/samsung/Makefile1
-rw-r--r--drivers/memory/samsung/exynos-srom.c18
-rw-r--r--drivers/memory/samsung/exynos-srom.h7
-rw-r--r--drivers/memory/ti-emif-pm.c1
-rw-r--r--drivers/message/fusion/mptsas.c3
-rw-r--r--drivers/mfd/cros_ec_dev.c31
-rw-r--r--drivers/misc/Kconfig1
-rw-r--r--drivers/misc/Makefile20
-rw-r--r--drivers/misc/aspeed-lpc-ctrl.c44
-rw-r--r--drivers/misc/cardreader/rts5260.c12
-rw-r--r--drivers/misc/cardreader/rtsx_pcr.c4
-rw-r--r--drivers/misc/cxl/cxl.h6
-rw-r--r--drivers/misc/cxl/cxllib.c87
-rw-r--r--drivers/misc/cxl/native.c11
-rw-r--r--drivers/misc/cxl/pci.c102
-rw-r--r--drivers/misc/cxl/sysfs.c12
-rw-r--r--drivers/misc/echo/echo.c73
-rw-r--r--drivers/misc/echo/fir.h50
-rw-r--r--drivers/misc/eeprom/at24.c293
-rw-r--r--drivers/misc/eeprom/at25.c2
-rw-r--r--drivers/misc/eeprom/digsy_mtc_eeprom.c29
-rw-r--r--drivers/misc/genwqe/card_utils.c4
-rw-r--r--drivers/misc/kgdbts.c8
-rw-r--r--drivers/misc/lkdtm/Makefile20
-rw-r--r--drivers/misc/lkdtm/bugs.c (renamed from drivers/misc/lkdtm_bugs.c)0
-rw-r--r--drivers/misc/lkdtm/core.c (renamed from drivers/misc/lkdtm_core.c)0
-rw-r--r--drivers/misc/lkdtm/heap.c (renamed from drivers/misc/lkdtm_heap.c)0
-rw-r--r--drivers/misc/lkdtm/lkdtm.h (renamed from drivers/misc/lkdtm.h)0
-rw-r--r--drivers/misc/lkdtm/perms.c (renamed from drivers/misc/lkdtm_perms.c)0
-rw-r--r--drivers/misc/lkdtm/refcount.c401
-rw-r--r--drivers/misc/lkdtm/rodata.c (renamed from drivers/misc/lkdtm_rodata.c)0
-rw-r--r--drivers/misc/lkdtm/usercopy.c (renamed from drivers/misc/lkdtm_usercopy.c)0
-rw-r--r--drivers/misc/lkdtm_refcount.c400
-rw-r--r--drivers/misc/mei/bus.c83
-rw-r--r--drivers/misc/mei/client.c87
-rw-r--r--drivers/misc/mei/debugfs.c7
-rw-r--r--drivers/misc/mei/init.c1
-rw-r--r--drivers/misc/mei/main.c66
-rw-r--r--drivers/misc/mei/mei_dev.h10
-rw-r--r--drivers/misc/mic/bus/vop_bus.c6
-rw-r--r--drivers/misc/ocxl/pci.c2
-rw-r--r--drivers/misc/pci_endpoint_test.c12
-rw-r--r--drivers/mmc/core/block.c18
-rw-r--r--drivers/mmc/core/core.c5
-rw-r--r--drivers/mmc/core/debugfs.c19
-rw-r--r--drivers/mmc/core/host.h3
-rw-r--r--drivers/mmc/core/mmc.c2
-rw-r--r--drivers/mmc/core/queue.c8
-rw-r--r--drivers/mmc/core/sd.c20
-rw-r--r--drivers/mmc/core/sdio.c9
-rw-r--r--drivers/mmc/core/sdio_irq.c4
-rw-r--r--drivers/mmc/core/slot-gpio.c23
-rw-r--r--drivers/mmc/host/Kconfig28
-rw-r--r--drivers/mmc/host/Makefile2
-rw-r--r--drivers/mmc/host/bfin_sdh.c679
-rw-r--r--drivers/mmc/host/dw_mmc-hi3798cv200.c202
-rw-r--r--drivers/mmc/host/dw_mmc-pci.c1
-rw-r--r--drivers/mmc/host/dw_mmc-rockchip.c4
-rw-r--r--drivers/mmc/host/dw_mmc.c48
-rw-r--r--drivers/mmc/host/dw_mmc.h27
-rw-r--r--drivers/mmc/host/jz4740_mmc.c2
-rw-r--r--drivers/mmc/host/mtk-sd.c12
-rw-r--r--drivers/mmc/host/renesas_sdhi_core.c6
-rw-r--r--drivers/mmc/host/renesas_sdhi_internal_dmac.c50
-rw-r--r--drivers/mmc/host/renesas_sdhi_sys_dmac.c17
-rw-r--r--drivers/mmc/host/sdhci-iproc.c1
-rw-r--r--drivers/mmc/host/sdhci-omap.c378
-rw-r--r--drivers/mmc/host/sdhci-pci-core.c68
-rw-r--r--drivers/mmc/host/sdhci.c19
-rw-r--r--drivers/mmc/host/sh_mmcif.c8
-rw-r--r--drivers/mmc/host/sunxi-mmc.c144
-rw-r--r--drivers/mmc/host/tmio_mmc_core.c68
-rw-r--r--drivers/mmc/host/ushc.c2
-rw-r--r--drivers/mtd/Kconfig2
-rw-r--r--drivers/mtd/Makefile2
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0001.c49
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0002.c35
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0020.c3
-rw-r--r--drivers/mtd/chips/jedec_probe.c34
-rw-r--r--drivers/mtd/chips/map_ram.c2
-rw-r--r--drivers/mtd/devices/bcm47xxsflash.c12
-rw-r--r--drivers/mtd/devices/block2mtd.c9
-rw-r--r--drivers/mtd/devices/docg3.c16
-rw-r--r--drivers/mtd/devices/lart.c6
-rw-r--r--drivers/mtd/devices/mtd_dataflash.c4
-rw-r--r--drivers/mtd/devices/mtdram.c3
-rw-r--r--drivers/mtd/devices/phram.c7
-rw-r--r--drivers/mtd/devices/pmc551.c2
-rw-r--r--drivers/mtd/devices/powernv_flash.c12
-rw-r--r--drivers/mtd/devices/slram.c7
-rw-r--r--drivers/mtd/devices/spear_smi.c3
-rw-r--r--drivers/mtd/devices/sst25l.c3
-rw-r--r--drivers/mtd/devices/st_spi_fsm.c7
-rw-r--r--drivers/mtd/ftl.c56
-rw-r--r--drivers/mtd/inftlmount.c8
-rw-r--r--drivers/mtd/lpddr/lpddr2_nvm.c10
-rw-r--r--drivers/mtd/lpddr/lpddr_cmds.c2
-rw-r--r--drivers/mtd/maps/Kconfig10
-rw-r--r--drivers/mtd/maps/Makefile1
-rw-r--r--drivers/mtd/maps/bfin-async-flash.c196
-rw-r--r--drivers/mtd/maps/physmap_of_core.c6
-rw-r--r--drivers/mtd/mtd_blkdevs.c6
-rw-r--r--drivers/mtd/mtdblock.c21
-rw-r--r--drivers/mtd/mtdchar.c34
-rw-r--r--drivers/mtd/mtdconcat.c48
-rw-r--r--drivers/mtd/mtdcore.c94
-rw-r--r--drivers/mtd/mtdoops.c20
-rw-r--r--drivers/mtd/mtdpart.c139
-rw-r--r--drivers/mtd/mtdswap.c34
-rw-r--r--drivers/mtd/nand/Kconfig580
-rw-r--r--drivers/mtd/nand/Makefile72
-rw-r--r--drivers/mtd/nand/ams-delta.c290
-rw-r--r--drivers/mtd/nand/atmel/nand-controller.c2565
-rw-r--r--drivers/mtd/nand/atmel/pmecc.c1012
-rw-r--r--drivers/mtd/nand/atmel/pmecc.h74
-rw-r--r--drivers/mtd/nand/au1550nd.c517
-rw-r--r--drivers/mtd/nand/bbt.c130
-rw-r--r--drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c456
-rw-r--r--drivers/mtd/nand/bf5xx_nand.c862
-rw-r--r--drivers/mtd/nand/brcmnand/brcmnand.c2620
-rw-r--r--drivers/mtd/nand/cafe_nand.c871
-rw-r--r--drivers/mtd/nand/cmx270_nand.c246
-rw-r--r--drivers/mtd/nand/core.c241
-rw-r--r--drivers/mtd/nand/cs553x_nand.c357
-rw-r--r--drivers/mtd/nand/davinci_nand.c879
-rw-r--r--drivers/mtd/nand/denali.c1408
-rw-r--r--drivers/mtd/nand/diskonchip.c1711
-rw-r--r--drivers/mtd/nand/docg4.c1421
-rw-r--r--drivers/mtd/nand/fsl_elbc_nand.c979
-rw-r--r--drivers/mtd/nand/fsl_ifc_nand.c1111
-rw-r--r--drivers/mtd/nand/fsmc_nand.c1175
-rw-r--r--drivers/mtd/nand/gpio.c329
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-lib.c1510
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-nand.c2182
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-nand.h315
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-regs.h187
-rw-r--r--drivers/mtd/nand/hisi504_nand.c896
-rw-r--r--drivers/mtd/nand/marvell_nand.c2896
-rw-r--r--drivers/mtd/nand/mpc5121_nfc.c857
-rw-r--r--drivers/mtd/nand/mxc_nand.c1853
-rw-r--r--drivers/mtd/nand/nand_base.c6582
-rw-r--r--drivers/mtd/nand/nand_bbt.c1452
-rw-r--r--drivers/mtd/nand/nand_bch.c234
-rw-r--r--drivers/mtd/nand/nand_ecc.c533
-rw-r--r--drivers/mtd/nand/nand_macronix.c30
-rw-r--r--drivers/mtd/nand/nand_micron.c289
-rw-r--r--drivers/mtd/nand/nand_timings.c324
-rw-r--r--drivers/mtd/nand/nand_toshiba.c51
-rw-r--r--drivers/mtd/nand/nandsim.c2392
-rw-r--r--drivers/mtd/nand/omap2.c2316
-rw-r--r--drivers/mtd/nand/onenand/Kconfig (renamed from drivers/mtd/onenand/Kconfig)0
-rw-r--r--drivers/mtd/nand/onenand/Makefile (renamed from drivers/mtd/onenand/Makefile)0
-rw-r--r--drivers/mtd/nand/onenand/generic.c116
-rw-r--r--drivers/mtd/nand/onenand/omap2.c660
-rw-r--r--drivers/mtd/nand/onenand/onenand_base.c4014
-rw-r--r--drivers/mtd/nand/onenand/onenand_bbt.c248
-rw-r--r--drivers/mtd/nand/onenand/samsung.c (renamed from drivers/mtd/onenand/samsung.c)0
-rw-r--r--drivers/mtd/nand/onenand/samsung.h (renamed from drivers/mtd/onenand/samsung.h)0
-rw-r--r--drivers/mtd/nand/orion_nand.c234
-rw-r--r--drivers/mtd/nand/pxa3xx_nand.c2105
-rw-r--r--drivers/mtd/nand/qcom_nandc.c2921
-rw-r--r--drivers/mtd/nand/r852.c1079
-rw-r--r--drivers/mtd/nand/r852.h160
-rw-r--r--drivers/mtd/nand/raw/Kconfig537
-rw-r--r--drivers/mtd/nand/raw/Makefile66
-rw-r--r--drivers/mtd/nand/raw/ams-delta.c291
-rw-r--r--drivers/mtd/nand/raw/atmel/Makefile (renamed from drivers/mtd/nand/atmel/Makefile)0
-rw-r--r--drivers/mtd/nand/raw/atmel/nand-controller.c2565
-rw-r--r--drivers/mtd/nand/raw/atmel/pmecc.c1012
-rw-r--r--drivers/mtd/nand/raw/atmel/pmecc.h74
-rw-r--r--drivers/mtd/nand/raw/au1550nd.c515
-rw-r--r--drivers/mtd/nand/raw/bcm47xxnflash/Makefile (renamed from drivers/mtd/nand/bcm47xxnflash/Makefile)0
-rw-r--r--drivers/mtd/nand/raw/bcm47xxnflash/bcm47xxnflash.h (renamed from drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h)0
-rw-r--r--drivers/mtd/nand/raw/bcm47xxnflash/main.c (renamed from drivers/mtd/nand/bcm47xxnflash/main.c)0
-rw-r--r--drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c456
-rw-r--r--drivers/mtd/nand/raw/brcmnand/Makefile (renamed from drivers/mtd/nand/brcmnand/Makefile)0
-rw-r--r--drivers/mtd/nand/raw/brcmnand/bcm63138_nand.c (renamed from drivers/mtd/nand/brcmnand/bcm63138_nand.c)0
-rw-r--r--drivers/mtd/nand/raw/brcmnand/bcm6368_nand.c (renamed from drivers/mtd/nand/brcmnand/bcm6368_nand.c)0
-rw-r--r--drivers/mtd/nand/raw/brcmnand/brcmnand.c2624
-rw-r--r--drivers/mtd/nand/raw/brcmnand/brcmnand.h (renamed from drivers/mtd/nand/brcmnand/brcmnand.h)0
-rw-r--r--drivers/mtd/nand/raw/brcmnand/brcmstb_nand.c (renamed from drivers/mtd/nand/brcmnand/brcmstb_nand.c)0
-rw-r--r--drivers/mtd/nand/raw/brcmnand/iproc_nand.c (renamed from drivers/mtd/nand/brcmnand/iproc_nand.c)0
-rw-r--r--drivers/mtd/nand/raw/cafe_nand.c875
-rw-r--r--drivers/mtd/nand/raw/cmx270_nand.c244
-rw-r--r--drivers/mtd/nand/raw/cs553x_nand.c356
-rw-r--r--drivers/mtd/nand/raw/davinci_nand.c882
-rw-r--r--drivers/mtd/nand/raw/denali.c1410
-rw-r--r--drivers/mtd/nand/raw/denali.h (renamed from drivers/mtd/nand/denali.h)0
-rw-r--r--drivers/mtd/nand/raw/denali_dt.c (renamed from drivers/mtd/nand/denali_dt.c)0
-rw-r--r--drivers/mtd/nand/raw/denali_pci.c (renamed from drivers/mtd/nand/denali_pci.c)0
-rw-r--r--drivers/mtd/nand/raw/diskonchip.c1713
-rw-r--r--drivers/mtd/nand/raw/docg4.c1421
-rw-r--r--drivers/mtd/nand/raw/fsl_elbc_nand.c979
-rw-r--r--drivers/mtd/nand/raw/fsl_ifc_nand.c1111
-rw-r--r--drivers/mtd/nand/raw/fsl_upm.c (renamed from drivers/mtd/nand/fsl_upm.c)0
-rw-r--r--drivers/mtd/nand/raw/fsmc_nand.c1207
-rw-r--r--drivers/mtd/nand/raw/gpio.c327
-rw-r--r--drivers/mtd/nand/raw/gpmi-nand/Makefile (renamed from drivers/mtd/nand/gpmi-nand/Makefile)0
-rw-r--r--drivers/mtd/nand/raw/gpmi-nand/bch-regs.h (renamed from drivers/mtd/nand/gpmi-nand/bch-regs.h)0
-rw-r--r--drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c943
-rw-r--r--drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c2164
-rw-r--r--drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h236
-rw-r--r--drivers/mtd/nand/raw/gpmi-nand/gpmi-regs.h192
-rw-r--r--drivers/mtd/nand/raw/hisi504_nand.c896
-rw-r--r--drivers/mtd/nand/raw/jz4740_nand.c (renamed from drivers/mtd/nand/jz4740_nand.c)0
-rw-r--r--drivers/mtd/nand/raw/jz4780_bch.c (renamed from drivers/mtd/nand/jz4780_bch.c)0
-rw-r--r--drivers/mtd/nand/raw/jz4780_bch.h (renamed from drivers/mtd/nand/jz4780_bch.h)0
-rw-r--r--drivers/mtd/nand/raw/jz4780_nand.c (renamed from drivers/mtd/nand/jz4780_nand.c)0
-rw-r--r--drivers/mtd/nand/raw/lpc32xx_mlc.c (renamed from drivers/mtd/nand/lpc32xx_mlc.c)0
-rw-r--r--drivers/mtd/nand/raw/lpc32xx_slc.c (renamed from drivers/mtd/nand/lpc32xx_slc.c)0
-rw-r--r--drivers/mtd/nand/raw/marvell_nand.c2927
-rw-r--r--drivers/mtd/nand/raw/mpc5121_nfc.c856
-rw-r--r--drivers/mtd/nand/raw/mtk_ecc.c (renamed from drivers/mtd/nand/mtk_ecc.c)0
-rw-r--r--drivers/mtd/nand/raw/mtk_ecc.h (renamed from drivers/mtd/nand/mtk_ecc.h)0
-rw-r--r--drivers/mtd/nand/raw/mtk_nand.c (renamed from drivers/mtd/nand/mtk_nand.c)0
-rw-r--r--drivers/mtd/nand/raw/mxc_nand.c1957
-rw-r--r--drivers/mtd/nand/raw/nand_amd.c (renamed from drivers/mtd/nand/nand_amd.c)0
-rw-r--r--drivers/mtd/nand/raw/nand_base.c6693
-rw-r--r--drivers/mtd/nand/raw/nand_bbt.c1451
-rw-r--r--drivers/mtd/nand/raw/nand_bch.c234
-rw-r--r--drivers/mtd/nand/raw/nand_ecc.c511
-rw-r--r--drivers/mtd/nand/raw/nand_hynix.c (renamed from drivers/mtd/nand/nand_hynix.c)0
-rw-r--r--drivers/mtd/nand/raw/nand_ids.c (renamed from drivers/mtd/nand/nand_ids.c)0
-rw-r--r--drivers/mtd/nand/raw/nand_macronix.c43
-rw-r--r--drivers/mtd/nand/raw/nand_micron.c292
-rw-r--r--drivers/mtd/nand/raw/nand_samsung.c (renamed from drivers/mtd/nand/nand_samsung.c)0
-rw-r--r--drivers/mtd/nand/raw/nand_timings.c324
-rw-r--r--drivers/mtd/nand/raw/nand_toshiba.c77
-rw-r--r--drivers/mtd/nand/raw/nandsim.c2391
-rw-r--r--drivers/mtd/nand/raw/ndfc.c (renamed from drivers/mtd/nand/ndfc.c)0
-rw-r--r--drivers/mtd/nand/raw/nuc900_nand.c (renamed from drivers/mtd/nand/nuc900_nand.c)0
-rw-r--r--drivers/mtd/nand/raw/omap2.c2319
-rw-r--r--drivers/mtd/nand/raw/omap_elm.c (renamed from drivers/mtd/nand/omap_elm.c)0
-rw-r--r--drivers/mtd/nand/raw/orion_nand.c232
-rw-r--r--drivers/mtd/nand/raw/oxnas_nand.c (renamed from drivers/mtd/nand/oxnas_nand.c)0
-rw-r--r--drivers/mtd/nand/raw/pasemi_nand.c (renamed from drivers/mtd/nand/pasemi_nand.c)0
-rw-r--r--drivers/mtd/nand/raw/plat_nand.c (renamed from drivers/mtd/nand/plat_nand.c)0
-rw-r--r--drivers/mtd/nand/raw/qcom_nandc.c2921
-rw-r--r--drivers/mtd/nand/raw/r852.c1082
-rw-r--r--drivers/mtd/nand/raw/r852.h157
-rw-r--r--drivers/mtd/nand/raw/s3c2410.c1295
-rw-r--r--drivers/mtd/nand/raw/sh_flctl.c1254
-rw-r--r--drivers/mtd/nand/raw/sharpsl.c233
-rw-r--r--drivers/mtd/nand/raw/sm_common.c201
-rw-r--r--drivers/mtd/nand/raw/sm_common.h (renamed from drivers/mtd/nand/sm_common.h)0
-rw-r--r--drivers/mtd/nand/raw/socrates_nand.c241
-rw-r--r--drivers/mtd/nand/raw/sunxi_nand.c2206
-rw-r--r--drivers/mtd/nand/raw/tango_nand.c690
-rw-r--r--drivers/mtd/nand/raw/tmio_nand.c (renamed from drivers/mtd/nand/tmio_nand.c)0
-rw-r--r--drivers/mtd/nand/raw/txx9ndfmc.c (renamed from drivers/mtd/nand/txx9ndfmc.c)0
-rw-r--r--drivers/mtd/nand/raw/vf610_nfc.c962
-rw-r--r--drivers/mtd/nand/raw/xway_nand.c (renamed from drivers/mtd/nand/xway_nand.c)0
-rw-r--r--drivers/mtd/nand/s3c2410.c1296
-rw-r--r--drivers/mtd/nand/sh_flctl.c1250
-rw-r--r--drivers/mtd/nand/sharpsl.c235
-rw-r--r--drivers/mtd/nand/sm_common.c202
-rw-r--r--drivers/mtd/nand/socrates_nand.c243
-rw-r--r--drivers/mtd/nand/sunxi_nand.c2321
-rw-r--r--drivers/mtd/nand/tango_nand.c688
-rw-r--r--drivers/mtd/nand/vf610_nfc.c845
-rw-r--r--drivers/mtd/nftlmount.c8
-rw-r--r--drivers/mtd/ofpart.c18
-rw-r--r--drivers/mtd/onenand/generic.c118
-rw-r--r--drivers/mtd/onenand/omap2.c662
-rw-r--r--drivers/mtd/onenand/onenand_base.c4033
-rw-r--r--drivers/mtd/onenand/onenand_bbt.c250
-rw-r--r--drivers/mtd/rfd_ftl.c93
-rw-r--r--drivers/mtd/sm_ftl.c21
-rw-r--r--drivers/mtd/sm_ftl.h4
-rw-r--r--drivers/mtd/spi-nor/cadence-quadspi.c19
-rw-r--r--drivers/mtd/spi-nor/fsl-quadspi.c19
-rw-r--r--drivers/mtd/spi-nor/spi-nor.c3
-rw-r--r--drivers/mtd/tests/mtd_test.c5
-rw-r--r--drivers/mtd/tests/pagetest.c10
-rw-r--r--drivers/mtd/tests/speedtest.c7
-rw-r--r--drivers/mtd/ubi/block.c2
-rw-r--r--drivers/mtd/ubi/build.c11
-rw-r--r--drivers/mtd/ubi/fastmap-wl.c1
-rw-r--r--drivers/mtd/ubi/gluebi.c3
-rw-r--r--drivers/mtd/ubi/io.c36
-rw-r--r--drivers/net/Kconfig5
-rw-r--r--drivers/net/Makefile1
-rw-r--r--drivers/net/Space.c6
-rw-r--r--drivers/net/bonding/bond_main.c76
-rw-r--r--drivers/net/bonding/bond_procfs.c2
-rw-r--r--drivers/net/bonding/bond_sysfs.c73
-rw-r--r--drivers/net/bonding/bond_sysfs_slave.c4
-rw-r--r--drivers/net/caif/caif_serial.c32
-rw-r--r--drivers/net/caif/caif_spi.c16
-rw-r--r--drivers/net/caif/caif_virtio.c16
-rw-r--r--drivers/net/can/Kconfig9
-rw-r--r--drivers/net/can/Makefile1
-rw-r--r--drivers/net/can/at91_can.c3
-rw-r--r--drivers/net/can/bfin_can.c784
-rw-r--r--drivers/net/can/cc770/cc770.c4
-rw-r--r--drivers/net/can/cc770/cc770_isa.c16
-rw-r--r--drivers/net/can/grcan.c4
-rw-r--r--drivers/net/can/janz-ican3.c6
-rw-r--r--drivers/net/can/sja1000/sja1000_isa.c14
-rw-r--r--drivers/net/can/softing/softing_main.c4
-rw-r--r--drivers/net/can/spi/mcp251x.c2
-rw-r--r--drivers/net/can/usb/esd_usb2.c6
-rw-r--r--drivers/net/can/vcan.c2
-rw-r--r--drivers/net/cris/Makefile1
-rw-r--r--drivers/net/cris/eth_v10.c1742
-rw-r--r--drivers/net/dsa/b53/b53_common.c2
-rw-r--r--drivers/net/dsa/b53/b53_mmap.c33
-rw-r--r--drivers/net/dsa/b53/b53_priv.h2
-rw-r--r--drivers/net/dsa/dsa_loop.c2
-rw-r--r--drivers/net/dsa/lan9303-core.c2
-rw-r--r--drivers/net/dsa/microchip/ksz_common.c2
-rw-r--r--drivers/net/dsa/mt7530.c10
-rw-r--r--drivers/net/dsa/mv88e6xxx/Kconfig10
-rw-r--r--drivers/net/dsa/mv88e6xxx/Makefile4
-rw-r--r--drivers/net/dsa/mv88e6xxx/chip.c437
-rw-r--r--drivers/net/dsa/mv88e6xxx/chip.h135
-rw-r--r--drivers/net/dsa/mv88e6xxx/global1_atu.c12
-rw-r--r--drivers/net/dsa/mv88e6xxx/global1_vtu.c11
-rw-r--r--drivers/net/dsa/mv88e6xxx/global2.c43
-rw-r--r--drivers/net/dsa/mv88e6xxx/global2.h115
-rw-r--r--drivers/net/dsa/mv88e6xxx/global2_avb.c193
-rw-r--r--drivers/net/dsa/mv88e6xxx/global2_scratch.c291
-rw-r--r--drivers/net/dsa/mv88e6xxx/hwtstamp.c584
-rw-r--r--drivers/net/dsa/mv88e6xxx/hwtstamp.h172
-rw-r--r--drivers/net/dsa/mv88e6xxx/ptp.c381
-rw-r--r--drivers/net/dsa/mv88e6xxx/ptp.h108
-rw-r--r--drivers/net/dsa/mv88e6xxx/serdes.c109
-rw-r--r--drivers/net/dsa/mv88e6xxx/serdes.h6
-rw-r--r--drivers/net/dsa/qca8k.c2
-rw-r--r--drivers/net/dummy.c2
-rw-r--r--drivers/net/ethernet/8390/Kconfig3
-rw-r--r--drivers/net/ethernet/8390/Makefile6
-rw-r--r--drivers/net/ethernet/8390/apne.c2
-rw-r--r--drivers/net/ethernet/8390/ax88796.c3
-rw-r--r--drivers/net/ethernet/8390/axnet_cs.c2
-rw-r--r--drivers/net/ethernet/8390/etherh.c17
-rw-r--r--drivers/net/ethernet/8390/hydra.c4
-rw-r--r--drivers/net/ethernet/8390/lib8390.c4
-rw-r--r--drivers/net/ethernet/8390/mac8390.c171
-rw-r--r--drivers/net/ethernet/8390/mcf8390.c4
-rw-r--r--drivers/net/ethernet/8390/ne.c27
-rw-r--r--drivers/net/ethernet/8390/ne2k-pci.c2
-rw-r--r--drivers/net/ethernet/8390/pcnet_cs.c4
-rw-r--r--drivers/net/ethernet/8390/smc-ultra.c2
-rw-r--r--drivers/net/ethernet/8390/stnic.c2
-rw-r--r--drivers/net/ethernet/8390/wd.c4
-rw-r--r--drivers/net/ethernet/8390/zorro8390.c5
-rw-r--r--drivers/net/ethernet/Kconfig3
-rw-r--r--drivers/net/ethernet/Makefile3
-rw-r--r--drivers/net/ethernet/adi/Kconfig66
-rw-r--r--drivers/net/ethernet/adi/Makefile5
-rw-r--r--drivers/net/ethernet/adi/bfin_mac.c1881
-rw-r--r--drivers/net/ethernet/adi/bfin_mac.h104
-rw-r--r--drivers/net/ethernet/altera/altera_tse_main.c6
-rw-r--r--drivers/net/ethernet/amazon/ena/ena_com.c8
-rw-r--r--drivers/net/ethernet/amazon/ena/ena_eth_com.h8
-rw-r--r--drivers/net/ethernet/amazon/ena/ena_netdev.c5
-rw-r--r--drivers/net/ethernet/amd/amd8111e.c2
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-common.h8
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c16
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-drv.c10
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-main.c3
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-mdio.c24
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-pci.c2
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c196
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe.h9
-rw-r--r--drivers/net/ethernet/apple/macmace.c25
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_nic.c8
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/hw_atl/Makefile2
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c18
-rw-r--r--drivers/net/ethernet/broadcom/bcmsysport.c141
-rw-r--r--drivers/net/ethernet/broadcom/bcmsysport.h13
-rw-r--r--drivers/net/ethernet/broadcom/bgmac.c7
-rw-r--r--drivers/net/ethernet/broadcom/bgmac.h6
-rw-r--r--drivers/net/ethernet/broadcom/bnx2.c2
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x.h12
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c5
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h4
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c5
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c16
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c4
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt.c345
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt.h30
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.h5
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c116
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h289
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_nvm_defs.h2
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c61
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.h3
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c63
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c144
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h22
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c30
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmgenet.c156
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmgenet.h12
-rw-r--r--drivers/net/ethernet/broadcom/sb1250-mac.c10
-rw-r--r--drivers/net/ethernet/broadcom/tg3.c6
-rw-r--r--drivers/net/ethernet/brocade/bna/bnad.c2
-rw-r--r--drivers/net/ethernet/brocade/bna/bnad_debugfs.c10
-rw-r--r--drivers/net/ethernet/cadence/macb_main.c96
-rw-r--r--[-rwxr-xr-x]drivers/net/ethernet/cadence/macb_ptp.c0
-rw-r--r--drivers/net/ethernet/cavium/common/cavium_ptp.c13
-rw-r--r--drivers/net/ethernet/cavium/liquidio/Makefile51
-rw-r--r--drivers/net/ethernet/cavium/liquidio/lio_core.c145
-rw-r--r--drivers/net/ethernet/cavium/liquidio/lio_ethtool.c24
-rw-r--r--drivers/net/ethernet/cavium/liquidio/lio_main.c440
-rw-r--r--drivers/net/ethernet/cavium/liquidio/lio_vf_main.c278
-rw-r--r--drivers/net/ethernet/cavium/liquidio/liquidio_common.h30
-rw-r--r--drivers/net/ethernet/cavium/liquidio/octeon_device.h2
-rw-r--r--drivers/net/ethernet/cavium/liquidio/octeon_droq.c83
-rw-r--r--drivers/net/ethernet/cavium/liquidio/octeon_droq.h11
-rw-r--r--drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c5
-rw-r--r--drivers/net/ethernet/cavium/liquidio/octeon_network.h73
-rw-r--r--drivers/net/ethernet/cavium/liquidio/request_manager.c8
-rw-r--r--drivers/net/ethernet/cavium/liquidio/response_manager.c6
-rw-r--r--drivers/net/ethernet/cavium/thunder/nic.h24
-rw-r--r--drivers/net/ethernet/cavium/thunder/nic_main.c45
-rw-r--r--drivers/net/ethernet/cavium/thunder/nicvf_main.c104
-rw-r--r--drivers/net/ethernet/cavium/thunder/thunder_bgx.c201
-rw-r--r--drivers/net/ethernet/cavium/thunder/thunder_bgx.h19
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c6
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/t3_hw.c8
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/Makefile2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.c86
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4.h23
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c122
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c24
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c6
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c333
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c3
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h13
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/sched.h4
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/sge.c107
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/srq.c138
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/srq.h65
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_hw.c215
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_msg.h193
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_regs.h2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h226
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c61
-rw-r--r--drivers/net/ethernet/cirrus/mac89x0.c158
-rw-r--r--drivers/net/ethernet/cisco/enic/enic.h3
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_ethtool.c36
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_main.c86
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_dev.c22
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_dev.h3
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_devcmd.h5
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_nic.h1
-rw-r--r--drivers/net/ethernet/davicom/Kconfig2
-rw-r--r--drivers/net/ethernet/ec_bhf.c2
-rw-r--r--drivers/net/ethernet/emulex/benet/be_cmds.c2
-rw-r--r--drivers/net/ethernet/emulex/benet/be_cmds.h2
-rw-r--r--drivers/net/ethernet/emulex/benet/be_main.c6
-rw-r--r--drivers/net/ethernet/faraday/Kconfig8
-rw-r--r--drivers/net/ethernet/freescale/dpaa/dpaa_eth.c65
-rw-r--r--drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c4
-rw-r--r--drivers/net/ethernet/freescale/fman/Kconfig1
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_dtsec.c19
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_dtsec.h1
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_memac.c32
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_memac.h1
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_tgec.c33
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_tgec.h1
-rw-r--r--drivers/net/ethernet/freescale/fman/mac.c4
-rw-r--r--drivers/net/ethernet/freescale/fman/mac.h2
-rw-r--r--drivers/net/ethernet/freescale/fsl_pq_mdio.c50
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hnae.h2
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c6
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hclge_mbx.h18
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hnae3.h18
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3_enet.c398
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3_enet.h20
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c153
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h4
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c16
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c559
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h27
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c94
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c6
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c76
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h8
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.c6
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.h2
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c475
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h35
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_mbx.c95
-rw-r--r--drivers/net/ethernet/ibm/ehea/ehea_main.c7
-rw-r--r--drivers/net/ethernet/ibm/ibmveth.c2
-rw-r--r--drivers/net/ethernet/ibm/ibmvnic.c837
-rw-r--r--drivers/net/ethernet/ibm/ibmvnic.h18
-rw-r--r--drivers/net/ethernet/intel/Kconfig14
-rw-r--r--drivers/net/ethernet/intel/Makefile1
-rw-r--r--drivers/net/ethernet/intel/e100.c1
-rw-r--r--drivers/net/ethernet/intel/e1000/Makefile1
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000.h1
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_ethtool.c1
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_hw.c1
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_hw.h1
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_main.c1
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_osdep.h1
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_param.c1
-rw-r--r--drivers/net/ethernet/intel/e1000e/80003es2lan.c1
-rw-r--r--drivers/net/ethernet/intel/e1000e/80003es2lan.h1
-rw-r--r--drivers/net/ethernet/intel/e1000e/82571.c1
-rw-r--r--drivers/net/ethernet/intel/e1000e/82571.h1
-rw-r--r--drivers/net/ethernet/intel/e1000e/Makefile1
-rw-r--r--drivers/net/ethernet/intel/e1000e/defines.h1
-rw-r--r--drivers/net/ethernet/intel/e1000e/e1000.h1
-rw-r--r--drivers/net/ethernet/intel/e1000e/ethtool.c1
-rw-r--r--drivers/net/ethernet/intel/e1000e/hw.h1
-rw-r--r--drivers/net/ethernet/intel/e1000e/ich8lan.c1
-rw-r--r--drivers/net/ethernet/intel/e1000e/ich8lan.h1
-rw-r--r--drivers/net/ethernet/intel/e1000e/mac.c1
-rw-r--r--drivers/net/ethernet/intel/e1000e/mac.h1
-rw-r--r--drivers/net/ethernet/intel/e1000e/manage.c1
-rw-r--r--drivers/net/ethernet/intel/e1000e/manage.h1
-rw-r--r--drivers/net/ethernet/intel/e1000e/netdev.c1
-rw-r--r--drivers/net/ethernet/intel/e1000e/nvm.c1
-rw-r--r--drivers/net/ethernet/intel/e1000e/nvm.h1
-rw-r--r--drivers/net/ethernet/intel/e1000e/param.c1
-rw-r--r--drivers/net/ethernet/intel/e1000e/phy.c1
-rw-r--r--drivers/net/ethernet/intel/e1000e/phy.h1
-rw-r--r--drivers/net/ethernet/intel/e1000e/ptp.c1
-rw-r--r--drivers/net/ethernet/intel/e1000e/regs.h1
-rw-r--r--drivers/net/ethernet/intel/fm10k/Makefile1
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k.h1
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_common.c6
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_common.h1
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_dcbnl.c1
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c1
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c1
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_iov.c1
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_main.c5
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_mbx.c1
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_mbx.h1
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_netdev.c11
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_pci.c101
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_pf.c5
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_pf.h1
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_tlv.c8
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_tlv.h1
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_type.h1
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_vf.c1
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_vf.h1
-rw-r--r--drivers/net/ethernet/intel/i40e/Makefile1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e.h82
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_adminq.c1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_adminq.h1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h38
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_alloc.h1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_client.c24
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_client.h1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_common.c64
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_dcb.c1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_dcb.h1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_debugfs.c53
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_devids.h1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_diag.c1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_diag.h1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_ethtool.c127
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_fcoe.c1571
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_fcoe.h127
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_hmc.c1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_hmc.h1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_lan_hmc.h1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_main.c465
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_nvm.c1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_osdep.h1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_prototype.h5
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_ptp.c1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_register.h1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_status.h1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_trace.h1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_txrx.c542
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_txrx.h73
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_type.h6
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c1091
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h21
-rw-r--r--drivers/net/ethernet/intel/i40evf/Makefile1
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_adminq.c1
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_adminq.h1
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h1
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_alloc.h1
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_common.c1
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_devids.h1
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_hmc.h1
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_lan_hmc.h1
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_osdep.h1
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_prototype.h1
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_register.h1
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_status.h1
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_trace.h1
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_txrx.c428
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_txrx.h68
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_type.h1
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40evf.h79
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c53
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40evf_main.c934
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c339
-rw-r--r--drivers/net/ethernet/intel/ice/Makefile17
-rw-r--r--drivers/net/ethernet/intel/ice/ice.h312
-rw-r--r--drivers/net/ethernet/intel/ice/ice_adminq_cmd.h1352
-rw-r--r--drivers/net/ethernet/intel/ice/ice_common.c2247
-rw-r--r--drivers/net/ethernet/intel/ice/ice_common.h86
-rw-r--r--drivers/net/ethernet/intel/ice/ice_controlq.c1066
-rw-r--r--drivers/net/ethernet/intel/ice/ice_controlq.h94
-rw-r--r--drivers/net/ethernet/intel/ice/ice_devids.h19
-rw-r--r--drivers/net/ethernet/intel/ice/ice_ethtool.c940
-rw-r--r--drivers/net/ethernet/intel/ice/ice_hw_autogen.h264
-rw-r--r--drivers/net/ethernet/intel/ice/ice_lan_tx_rx.h473
-rw-r--r--drivers/net/ethernet/intel/ice/ice_main.c5491
-rw-r--r--drivers/net/ethernet/intel/ice/ice_nvm.c236
-rw-r--r--drivers/net/ethernet/intel/ice/ice_osdep.h73
-rw-r--r--drivers/net/ethernet/intel/ice/ice_sched.c1659
-rw-r--r--drivers/net/ethernet/intel/ice/ice_sched.h43
-rw-r--r--drivers/net/ethernet/intel/ice/ice_status.h32
-rw-r--r--drivers/net/ethernet/intel/ice/ice_switch.c1883
-rw-r--r--drivers/net/ethernet/intel/ice/ice_switch.h161
-rw-r--r--drivers/net/ethernet/intel/ice/ice_txrx.c1782
-rw-r--r--drivers/net/ethernet/intel/ice/ice_txrx.h192
-rw-r--r--drivers/net/ethernet/intel/ice/ice_type.h394
-rw-r--r--drivers/net/ethernet/intel/igb/Makefile1
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_82575.c1
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_82575.h1
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_defines.h1
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_hw.h1
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_i210.c1
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_i210.h1
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_mac.c1
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_mac.h1
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_mbx.c1
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_mbx.h1
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_nvm.c1
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_nvm.h1
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_phy.c1
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_phy.h1
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_regs.h1
-rw-r--r--drivers/net/ethernet/intel/igb/igb.h2
-rw-r--r--drivers/net/ethernet/intel/igb/igb_ethtool.c1
-rw-r--r--drivers/net/ethernet/intel/igb/igb_hwmon.c3
-rw-r--r--drivers/net/ethernet/intel/igb/igb_main.c53
-rw-r--r--drivers/net/ethernet/intel/igb/igb_ptp.c1
-rw-r--r--drivers/net/ethernet/intel/igbvf/Makefile1
-rw-r--r--drivers/net/ethernet/intel/igbvf/defines.h1
-rw-r--r--drivers/net/ethernet/intel/igbvf/ethtool.c1
-rw-r--r--drivers/net/ethernet/intel/igbvf/igbvf.h1
-rw-r--r--drivers/net/ethernet/intel/igbvf/mbx.c1
-rw-r--r--drivers/net/ethernet/intel/igbvf/mbx.h1
-rw-r--r--drivers/net/ethernet/intel/igbvf/netdev.c1
-rw-r--r--drivers/net/ethernet/intel/igbvf/regs.h1
-rw-r--r--drivers/net/ethernet/intel/igbvf/vf.c1
-rw-r--r--drivers/net/ethernet/intel/igbvf/vf.h1
-rw-r--r--drivers/net/ethernet/intel/ixgb/Makefile1
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb.h1
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_ee.h1
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_hw.h1
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_ids.h1
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_osdep.h1
-rw-r--r--drivers/net/ethernet/intel/ixgbe/Makefile1
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe.h1
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c1
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c1
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_common.c1
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_common.h2
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h1
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.c1
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.h1
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h1
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c5
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h1
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c69
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.h2
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c1
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_main.c82
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h1
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_model.h1
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h1
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c6
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h1
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_sysfs.c2
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_type.h1
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_x540.h1
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c4
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/Makefile1
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/defines.h1
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ethtool.c116
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ixgbevf.h101
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c1216
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/mbx.h1
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/regs.h1
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/vf.h1
-rw-r--r--drivers/net/ethernet/marvell/mvneta.c189
-rw-r--r--drivers/net/ethernet/marvell/mvpp2.c1278
-rw-r--r--drivers/net/ethernet/marvell/skge.c2
-rw-r--r--drivers/net/ethernet/marvell/sky2.c4
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c72
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_ethtool.c47
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_main.c4
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_netdev.c8
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_port.c38
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_rx.c41
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/fw.c1
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/main.c98
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mlx4_en.h1
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mlx4_stats.h10
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/resource_tracker.c1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/Kconfig2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/accel/ipsec.c59
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/accel/ipsec.h98
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/alloc.c37
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/cmd.c4
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/cq.c113
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/dev.c8
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.c3
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.h6
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en.h138
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c323
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h24
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c39
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.h5
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_common.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c227
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_main.c735
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_rep.c49
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_rx.c306
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_stats.c132
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_stats.h10
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_tc.c34
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_tx.c27
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eq.c92
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eswitch.c54
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eswitch.h55
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c134
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fpga/conn.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.c1290
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.h76
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c217
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h72
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fs_core.c145
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fs_core.h8
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fw.c11
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c31
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/main.c53
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h33
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/port.c64
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/rl.c63
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/transobj.c46
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/vport.c26
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/wq.c18
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/wq.h22
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/Kconfig2
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/Makefile2
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core.c5
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core.h14
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c19
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h9
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c10
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/pci.c20
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/reg.h230
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum.c573
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum.h48
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c52
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_actions.c34
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_actions.h4
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c5
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c174
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h15
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_kvdl.c278
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c292
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.h9
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_mr_tcam.c105
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c206
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c222
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h1
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c824
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_span.h107
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c16
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/switchib.c1
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/switchx2.c9
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/trap.h1
-rw-r--r--drivers/net/ethernet/microchip/Kconfig10
-rw-r--r--drivers/net/ethernet/microchip/Makefile3
-rw-r--r--drivers/net/ethernet/microchip/lan743x_main.c2771
-rw-r--r--drivers/net/ethernet/microchip/lan743x_main.h597
-rw-r--r--drivers/net/ethernet/myricom/myri10ge/myri10ge.c32
-rw-r--r--drivers/net/ethernet/natsemi/jazzsonic.c32
-rw-r--r--drivers/net/ethernet/natsemi/macsonic.c244
-rw-r--r--drivers/net/ethernet/natsemi/sonic.c99
-rw-r--r--drivers/net/ethernet/natsemi/sonic.h2
-rw-r--r--drivers/net/ethernet/natsemi/xtsonic.c30
-rw-r--r--drivers/net/ethernet/netronome/nfp/bpf/Makefile2
-rw-r--r--drivers/net/ethernet/netronome/nfp/bpf/cmsg.c12
-rw-r--r--drivers/net/ethernet/netronome/nfp/bpf/fw.h1
-rw-r--r--drivers/net/ethernet/netronome/nfp/bpf/jit.c468
-rw-r--r--drivers/net/ethernet/netronome/nfp/bpf/main.c22
-rw-r--r--drivers/net/ethernet/netronome/nfp/bpf/main.h85
-rw-r--r--drivers/net/ethernet/netronome/nfp/bpf/offload.c45
-rw-r--r--drivers/net/ethernet/netronome/nfp/bpf/verifier.c217
-rw-r--r--drivers/net/ethernet/netronome/nfp/flower/Makefile2
-rw-r--r--drivers/net/ethernet/netronome/nfp/flower/cmsg.c85
-rw-r--r--drivers/net/ethernet/netronome/nfp/flower/cmsg.h37
-rw-r--r--drivers/net/ethernet/netronome/nfp/flower/main.c93
-rw-r--r--drivers/net/ethernet/netronome/nfp/flower/main.h28
-rw-r--r--drivers/net/ethernet/netronome/nfp/flower/match.c93
-rw-r--r--drivers/net/ethernet/netronome/nfp/flower/offload.c49
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_app.h25
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_asm.c2
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_asm.h7
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_main.c1
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net.h4
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_common.c4
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h280
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c6
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c16
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_repr.c13
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfpcore/Makefile2
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfpcore/nfp6000/Makefile2
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfpcore/nfp_mutex.c5
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c12
-rw-r--r--drivers/net/ethernet/netronome/nfp/nic/Makefile2
-rw-r--r--drivers/net/ethernet/ni/Kconfig27
-rw-r--r--drivers/net/ethernet/ni/Makefile1
-rw-r--r--drivers/net/ethernet/ni/nixge.c1310
-rw-r--r--drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c2
-rw-r--r--drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c14
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed.h15
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_debug.c415
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_dev.c38
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_hsi.h1617
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_init_fw_funcs.c103
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_int.c2
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_iwarp.c9
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_l2.c2
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_ll2.c13
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_main.c338
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_mcp.c219
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_mcp.h56
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_selftest.c9
-rw-r--r--drivers/net/ethernet/qlogic/qede/qede_ethtool.c9
-rw-r--r--drivers/net/ethernet/qlogic/qede/qede_fp.c20
-rw-r--r--drivers/net/ethernet/qlogic/qla3xxx.c5
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c2
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c30
-rw-r--r--drivers/net/ethernet/qlogic/qlge/qlge.h16
-rw-r--r--drivers/net/ethernet/qlogic/qlge/qlge_main.c3
-rw-r--r--drivers/net/ethernet/qualcomm/qca_debug.c2
-rw-r--r--drivers/net/ethernet/qualcomm/qca_spi.c1
-rw-r--r--drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c76
-rw-r--r--drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h2
-rw-r--r--drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c12
-rw-r--r--drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h8
-rw-r--r--drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c4
-rw-r--r--drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c5
-rw-r--r--drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h8
-rw-r--r--drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c2
-rw-r--r--drivers/net/ethernet/realtek/r8169.c1158
-rw-r--r--drivers/net/ethernet/renesas/ravb.h1
-rw-r--r--drivers/net/ethernet/renesas/ravb_main.c33
-rw-r--r--drivers/net/ethernet/renesas/sh_eth.c247
-rw-r--r--drivers/net/ethernet/renesas/sh_eth.h13
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c4
-rw-r--r--drivers/net/ethernet/sfc/ef10.c859
-rw-r--r--drivers/net/ethernet/sfc/efx.c234
-rw-r--r--drivers/net/ethernet/sfc/efx.h40
-rw-r--r--drivers/net/ethernet/sfc/ethtool.c218
-rw-r--r--drivers/net/ethernet/sfc/falcon/enum.h1
-rw-r--r--drivers/net/ethernet/sfc/falcon/mtd.c11
-rw-r--r--drivers/net/ethernet/sfc/farch.c128
-rw-r--r--drivers/net/ethernet/sfc/filter.h7
-rw-r--r--drivers/net/ethernet/sfc/mcdi.c2
-rw-r--r--drivers/net/ethernet/sfc/mcdi_mon.c2
-rw-r--r--drivers/net/ethernet/sfc/mcdi_pcol.h2822
-rw-r--r--drivers/net/ethernet/sfc/mcdi_port.c150
-rw-r--r--drivers/net/ethernet/sfc/mtd.c11
-rw-r--r--drivers/net/ethernet/sfc/net_driver.h129
-rw-r--r--drivers/net/ethernet/sfc/nic.h8
-rw-r--r--drivers/net/ethernet/sfc/rx.c183
-rw-r--r--drivers/net/ethernet/sfc/siena.c27
-rw-r--r--drivers/net/ethernet/smsc/Kconfig10
-rw-r--r--drivers/net/ethernet/smsc/smc91x.h26
-rw-r--r--drivers/net/ethernet/socionext/sni_ave.c8
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/Makefile2
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/common.h22
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c213
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4.h6
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c52
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c2
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c6
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac5.c298
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac5.h52
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac.h14
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c39
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c175
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c34
-rw-r--r--drivers/net/ethernet/sun/niu.c10
-rw-r--r--drivers/net/ethernet/ti/cpsw.c70
-rw-r--r--drivers/net/ethernet/ti/davinci_cpdma.c2
-rw-r--r--drivers/net/ethernet/ti/davinci_cpdma.h2
-rw-r--r--drivers/net/ethernet/ti/tlan.c2
-rw-r--r--drivers/net/ethernet/tile/Kconfig18
-rw-r--r--drivers/net/ethernet/tile/Makefile11
-rw-r--r--drivers/net/ethernet/tile/tilegx.c2279
-rw-r--r--drivers/net/ethernet/tile/tilepro.c2397
-rw-r--r--drivers/net/hamradio/bpqether.c3
-rw-r--r--drivers/net/hamradio/yam.c2
-rw-r--r--drivers/net/hyperv/Makefile2
-rw-r--r--drivers/net/hyperv/hyperv_net.h1
-rw-r--r--drivers/net/hyperv/netvsc.c109
-rw-r--r--drivers/net/hyperv/netvsc_drv.c6
-rw-r--r--drivers/net/hyperv/netvsc_trace.c7
-rw-r--r--drivers/net/hyperv/netvsc_trace.h182
-rw-r--r--drivers/net/hyperv/rndis_filter.c34
-rw-r--r--drivers/net/ieee802154/Kconfig11
-rw-r--r--drivers/net/ieee802154/Makefile1
-rw-r--r--drivers/net/ieee802154/at86rf230.c2
-rw-r--r--drivers/net/ieee802154/ca8210.c14
-rw-r--r--drivers/net/ieee802154/mcr20a.c1411
-rw-r--r--drivers/net/ieee802154/mcr20a.h498
-rw-r--r--drivers/net/ifb.c2
-rw-r--r--drivers/net/ipvlan/ipvlan.h7
-rw-r--r--drivers/net/ipvlan/ipvlan_core.c103
-rw-r--r--drivers/net/ipvlan/ipvlan_main.c118
-rw-r--r--drivers/net/macsec.c5
-rw-r--r--drivers/net/netdevsim/Makefile4
-rw-r--r--drivers/net/netdevsim/devlink.c293
-rw-r--r--drivers/net/netdevsim/fib.c263
-rw-r--r--drivers/net/netdevsim/netdev.c16
-rw-r--r--drivers/net/netdevsim/netdevsim.h44
-rw-r--r--drivers/net/phy/aquantia.c20
-rw-r--r--drivers/net/phy/bcm7xxx.c2
-rw-r--r--drivers/net/phy/cortina.c18
-rw-r--r--drivers/net/phy/dp83640.c18
-rw-r--r--drivers/net/phy/dp83867.c19
-rw-r--r--drivers/net/phy/intel-xway.c44
-rw-r--r--drivers/net/phy/marvell.c31
-rw-r--r--drivers/net/phy/marvell10g.c189
-rw-r--r--drivers/net/phy/mdio-bitbang.c2
-rw-r--r--drivers/net/phy/mdio-mux-mmioreg.c5
-rw-r--r--drivers/net/phy/microchip.c178
-rw-r--r--drivers/net/phy/phy-c45.c28
-rw-r--r--drivers/net/phy/phy-core.c4
-rw-r--r--drivers/net/phy/phy.c22
-rw-r--r--drivers/net/phy/phy_device.c2
-rw-r--r--drivers/net/phy/phylink.c77
-rw-r--r--drivers/net/phy/sfp-bus.c168
-rw-r--r--drivers/net/phy/sfp.c191
-rw-r--r--drivers/net/phy/spi_ks8995.c2
-rw-r--r--drivers/net/phy/teranetics.c32
-rw-r--r--drivers/net/ppp/ppp_generic.c2
-rw-r--r--drivers/net/ppp/pppoe.c12
-rw-r--r--drivers/net/ppp/pptp.c7
-rw-r--r--drivers/net/slip/slhc.c5
-rw-r--r--drivers/net/team/team.c66
-rw-r--r--drivers/net/tun.c131
-rw-r--r--drivers/net/usb/ax88179_178a.c1
-rw-r--r--drivers/net/usb/cdc_eem.c5
-rw-r--r--drivers/net/usb/cdc_ether.c6
-rw-r--r--drivers/net/usb/cdc_ncm.c12
-rw-r--r--drivers/net/usb/hso.c8
-rw-r--r--drivers/net/usb/kalmia.c14
-rw-r--r--drivers/net/usb/lan78xx.c74
-rw-r--r--drivers/net/usb/lg-vl600.c6
-rw-r--r--drivers/net/usb/qmi_wwan.c6
-rw-r--r--drivers/net/virtio_net.c81
-rw-r--r--drivers/net/vmxnet3/vmxnet3_drv.c17
-rw-r--r--drivers/net/vmxnet3/vmxnet3_int.h4
-rw-r--r--drivers/net/vrf.c17
-rw-r--r--drivers/net/wimax/i2400m/usb-rx.c3
-rw-r--r--drivers/net/wireless/admtek/Kconfig4
-rw-r--r--drivers/net/wireless/ath/Kconfig4
-rw-r--r--drivers/net/wireless/ath/ath.h2
-rw-r--r--drivers/net/wireless/ath/ath10k/core.c9
-rw-r--r--drivers/net/wireless/ath/ath10k/core.h88
-rw-r--r--drivers/net/wireless/ath/ath10k/coredump.c90
-rw-r--r--drivers/net/wireless/ath/ath10k/coredump.h2
-rw-r--r--drivers/net/wireless/ath/ath10k/debug.c154
-rw-r--r--drivers/net/wireless/ath/ath10k/debug.h41
-rw-r--r--drivers/net/wireless/ath/ath10k/debugfs_sta.c286
-rw-r--r--drivers/net/wireless/ath/ath10k/htt_rx.c113
-rw-r--r--drivers/net/wireless/ath/ath10k/mac.c54
-rw-r--r--drivers/net/wireless/ath/ath10k/pci.c101
-rw-r--r--drivers/net/wireless/ath/ath10k/trace.h12
-rw-r--r--drivers/net/wireless/ath/ath10k/txrx.c12
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi-ops.h56
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi-tlv.c116
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi-tlv.h18
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi.c462
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi.h94
-rw-r--r--drivers/net/wireless/ath/ath5k/attach.c2
-rw-r--r--drivers/net/wireless/ath/ath5k/base.c6
-rw-r--r--drivers/net/wireless/ath/ath5k/debug.c37
-rw-r--r--drivers/net/wireless/ath/ath5k/qcu.c2
-rw-r--r--drivers/net/wireless/ath/ath5k/sysfs.c8
-rw-r--r--drivers/net/wireless/ath/ath6kl/debug.c43
-rw-r--r--drivers/net/wireless/ath/ath9k/common-debug.c9
-rw-r--r--drivers/net/wireless/ath/ath9k/common-init.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/common-spectral.c22
-rw-r--r--drivers/net/wireless/ath/ath9k/debug.c40
-rw-r--r--drivers/net/wireless/ath/ath9k/debug_sta.c6
-rw-r--r--drivers/net/wireless/ath/ath9k/dfs_debug.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_debug.c16
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_init.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.c14
-rw-r--r--drivers/net/wireless/ath/ath9k/init.c11
-rw-r--r--drivers/net/wireless/ath/ath9k/tx99.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/xmit.c6
-rw-r--r--drivers/net/wireless/ath/carl9170/debug.c8
-rw-r--r--drivers/net/wireless/ath/carl9170/main.c4
-rw-r--r--drivers/net/wireless/ath/dfs_pattern_detector.c2
-rw-r--r--drivers/net/wireless/ath/wcn36xx/debug.c5
-rw-r--r--drivers/net/wireless/ath/wcn36xx/dxe.c69
-rw-r--r--drivers/net/wireless/ath/wcn36xx/dxe.h221
-rw-r--r--drivers/net/wireless/ath/wcn36xx/main.c14
-rw-r--r--drivers/net/wireless/ath/wcn36xx/smd.c115
-rw-r--r--drivers/net/wireless/ath/wcn36xx/txrx.c32
-rw-r--r--drivers/net/wireless/ath/wcn36xx/wcn36xx.h2
-rw-r--r--drivers/net/wireless/ath/wil6210/cfg80211.c744
-rw-r--r--drivers/net/wireless/ath/wil6210/debug.c9
-rw-r--r--drivers/net/wireless/ath/wil6210/debugfs.c117
-rw-r--r--drivers/net/wireless/ath/wil6210/ethtool.c4
-rw-r--r--drivers/net/wireless/ath/wil6210/fw.h38
-rw-r--r--drivers/net/wireless/ath/wil6210/fw_inc.c52
-rw-r--r--drivers/net/wireless/ath/wil6210/interrupt.c8
-rw-r--r--drivers/net/wireless/ath/wil6210/main.c333
-rw-r--r--drivers/net/wireless/ath/wil6210/netdev.c382
-rw-r--r--drivers/net/wireless/ath/wil6210/p2p.c175
-rw-r--r--drivers/net/wireless/ath/wil6210/pcie_bus.c57
-rw-r--r--drivers/net/wireless/ath/wil6210/pm.c132
-rw-r--r--drivers/net/wireless/ath/wil6210/pmc.c8
-rw-r--r--drivers/net/wireless/ath/wil6210/rx_reorder.c45
-rw-r--r--drivers/net/wireless/ath/wil6210/txrx.c177
-rw-r--r--drivers/net/wireless/ath/wil6210/txrx.h22
-rw-r--r--drivers/net/wireless/ath/wil6210/wil6210.h217
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.c460
-rw-r--r--drivers/net/wireless/atmel/Kconfig4
-rw-r--r--drivers/net/wireless/atmel/atmel.c2
-rw-r--r--drivers/net/wireless/broadcom/Kconfig4
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c6
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c2
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h8
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c91
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h17
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c14
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h3
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c85
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h1
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c208
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h4
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c42
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h17
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c3
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h7
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c242
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h82
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c3
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c11
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h1
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c8
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c2
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c177
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c3
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h7
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c244
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c129
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c1
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.c2
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c6
-rw-r--r--drivers/net/wireless/cisco/Kconfig6
-rw-r--r--drivers/net/wireless/cisco/airo.c6
-rw-r--r--drivers/net/wireless/intel/Kconfig4
-rw-r--r--drivers/net/wireless/intel/ipw2x00/ipw2100.c29
-rw-r--r--drivers/net/wireless/intel/ipw2x00/ipw2200.c51
-rw-r--r--drivers/net/wireless/intel/ipw2x00/libipw_module.c2
-rw-r--r--drivers/net/wireless/intel/iwlegacy/3945-mac.c35
-rw-r--r--drivers/net/wireless/intel/iwlegacy/4965-mac.c19
-rw-r--r--drivers/net/wireless/intel/iwlegacy/4965-rs.c8
-rw-r--r--drivers/net/wireless/intel/iwlegacy/common.c4
-rw-r--r--drivers/net/wireless/intel/iwlegacy/debug.c58
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/22000.c4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/9000.c66
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c78
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/main.c7
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/rs.c16
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h20
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/scan.h73
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/dbg.c10
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/debugfs.c26
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/debugfs.h5
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/file.h17
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-config.h5
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h39
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c1
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-drv.c43
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/constants.h2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c51
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c110
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw.c4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c24
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mvm.h51
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/ops.c6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c21
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/rs.c12
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c25
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/scan.c199
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/sta.c74
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/time-event.c15
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/tx.c168
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/utils.c26
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/drv.c195
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/trans.c12
-rw-r--r--drivers/net/wireless/intersil/Kconfig4
-rw-r--r--drivers/net/wireless/intersil/p54/main.c2
-rw-r--r--drivers/net/wireless/mac80211_hwsim.c103
-rw-r--r--drivers/net/wireless/mac80211_hwsim.h9
-rw-r--r--drivers/net/wireless/marvell/Kconfig4
-rw-r--r--drivers/net/wireless/marvell/mwifiex/11n.c32
-rw-r--r--drivers/net/wireless/marvell/mwifiex/cfg80211.c40
-rw-r--r--drivers/net/wireless/marvell/mwifiex/cmdevt.c3
-rw-r--r--drivers/net/wireless/marvell/mwifiex/decl.h17
-rw-r--r--drivers/net/wireless/marvell/mwifiex/fw.h7
-rw-r--r--drivers/net/wireless/marvell/mwifiex/main.c24
-rw-r--r--drivers/net/wireless/marvell/mwifiex/main.h29
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sta_cmd.c22
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c19
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sta_ioctl.c56
-rw-r--r--drivers/net/wireless/mediatek/Kconfig4
-rw-r--r--drivers/net/wireless/mediatek/mt76/debugfs.c10
-rw-r--r--drivers/net/wireless/mediatek/mt76/mac80211.c68
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76.h3
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2.h1
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2_debugfs.c8
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c12
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2_init.c3
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2_mac.c4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2_main.c47
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2_mcu.c13
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2_phy.c55
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2_regs.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2_tx.c5
-rw-r--r--drivers/net/wireless/mediatek/mt7601u/debugfs.c16
-rw-r--r--drivers/net/wireless/mediatek/mt7601u/eeprom.c26
-rw-r--r--drivers/net/wireless/mediatek/mt7601u/initvals.h1
-rw-r--r--drivers/net/wireless/mediatek/mt7601u/mac.c26
-rw-r--r--drivers/net/wireless/mediatek/mt7601u/mac.h1
-rw-r--r--drivers/net/wireless/mediatek/mt7601u/main.c3
-rw-r--r--drivers/net/wireless/mediatek/mt7601u/mcu.c9
-rw-r--r--drivers/net/wireless/mediatek/mt7601u/mt7601u.h3
-rw-r--r--drivers/net/wireless/mediatek/mt7601u/trace.h6
-rw-r--r--drivers/net/wireless/mediatek/mt7601u/usb.c52
-rw-r--r--drivers/net/wireless/quantenna/Kconfig4
-rw-r--r--drivers/net/wireless/quantenna/qtnfmac/bus.h3
-rw-r--r--drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c418
-rw-r--r--drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_ipc.h1
-rw-r--r--drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_regs_pearl.h1
-rw-r--r--drivers/net/wireless/ralink/Kconfig4
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2500usb.c2
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800pci.c2
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800soc.c2
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800usb.c2
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00debug.c64
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt61pci.c2
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt73usb.c2
-rw-r--r--drivers/net/wireless/ray_cs.c8
-rw-r--r--drivers/net/wireless/realtek/Kconfig4
-rw-r--r--drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c2
-rw-r--r--drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c2
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/base.c35
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c3
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c6
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c33
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c4
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8822bwifionly.c55
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8822bwifionly.h25
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c108
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h124
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/efuse.c13
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/efuse.h2
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/pci.c2
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rc.c55
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8188ee/pwrseq.h4
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192c/fw_common.c1
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192cu/rf.c3
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c1
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192ee/pwrseq.h4
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723ae/pwrseq.h4
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723be/pwrseq.h4
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8821ae/dm.c16
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8821ae/pwrseq.h4
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c1
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/wifi.h72
-rw-r--r--drivers/net/wireless/rsi/Kconfig15
-rw-r--r--drivers/net/wireless/rsi/Makefile1
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_coex.c179
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_core.c20
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_hal.c56
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_main.c129
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_mgmt.c2
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_sdio.c113
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_sdio_ops.c65
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_usb.c187
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_usb_ops.c31
-rw-r--r--drivers/net/wireless/rsi/rsi_coex.h37
-rw-r--r--drivers/net/wireless/rsi/rsi_common.h5
-rw-r--r--drivers/net/wireless/rsi/rsi_hal.h22
-rw-r--r--drivers/net/wireless/rsi/rsi_main.h21
-rw-r--r--drivers/net/wireless/rsi/rsi_mgmt.h3
-rw-r--r--drivers/net/wireless/rsi/rsi_sdio.h10
-rw-r--r--drivers/net/wireless/rsi/rsi_usb.h17
-rw-r--r--drivers/net/wireless/st/Kconfig4
-rw-r--r--drivers/net/wireless/st/cw1200/debug.c6
-rw-r--r--drivers/net/wireless/st/cw1200/main.c2
-rw-r--r--drivers/net/wireless/ti/Kconfig4
-rw-r--r--drivers/net/wireless/ti/wl1251/main.c102
-rw-r--r--drivers/net/wireless/ti/wl1251/tx.c4
-rw-r--r--drivers/net/wireless/ti/wl18xx/main.c27
-rw-r--r--drivers/net/wireless/ti/wlcore/main.c8
-rw-r--r--drivers/net/wireless/ti/wlcore/sdio.c2
-rw-r--r--drivers/net/wireless/ti/wlcore/sysfs.c7
-rw-r--r--drivers/net/wireless/zydas/Kconfig4
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_mac.c1
-rw-r--r--drivers/net/xen-netback/rx.c2
-rw-r--r--drivers/net/xen-netback/xenbus.c4
-rw-r--r--drivers/net/xen-netfront.c6
-rw-r--r--drivers/nvdimm/Kconfig12
-rw-r--r--drivers/nvdimm/Makefile1
-rw-r--r--drivers/nvdimm/blk.c2
-rw-r--r--drivers/nvdimm/btt.c2
-rw-r--r--drivers/nvdimm/btt_devs.c21
-rw-r--r--drivers/nvdimm/bus.c14
-rw-r--r--drivers/nvdimm/claim.c2
-rw-r--r--drivers/nvdimm/core.c6
-rw-r--r--drivers/nvdimm/dax_devs.c5
-rw-r--r--drivers/nvdimm/dimm.c8
-rw-r--r--drivers/nvdimm/dimm_devs.c29
-rw-r--r--drivers/nvdimm/label.c85
-rw-r--r--drivers/nvdimm/label.h2
-rw-r--r--drivers/nvdimm/namespace_devs.c42
-rw-r--r--drivers/nvdimm/nd.h2
-rw-r--r--drivers/nvdimm/of_pmem.c119
-rw-r--r--drivers/nvdimm/pfn_devs.c25
-rw-r--r--drivers/nvdimm/pmem.c20
-rw-r--r--drivers/nvdimm/region.c4
-rw-r--r--drivers/nvdimm/region_devs.c9
-rw-r--r--drivers/nvme/host/Makefile1
-rw-r--r--drivers/nvme/host/core.c150
-rw-r--r--drivers/nvme/host/fabrics.c83
-rw-r--r--drivers/nvme/host/fabrics.h33
-rw-r--r--drivers/nvme/host/fault_inject.c79
-rw-r--r--drivers/nvme/host/fc.c48
-rw-r--r--drivers/nvme/host/lightnvm.c757
-rw-r--r--drivers/nvme/host/multipath.c8
-rw-r--r--drivers/nvme/host/nvme.h37
-rw-r--r--drivers/nvme/host/pci.c57
-rw-r--r--drivers/nvme/host/rdma.c48
-rw-r--r--drivers/nvme/target/admin-cmd.c1
-rw-r--r--drivers/nvme/target/configfs.c65
-rw-r--r--drivers/nvme/target/core.c12
-rw-r--r--drivers/nvme/target/discovery.c32
-rw-r--r--drivers/nvme/target/fc.c23
-rw-r--r--drivers/nvme/target/io-cmd.c4
-rw-r--r--drivers/nvme/target/loop.c24
-rw-r--r--drivers/nvme/target/nvmet.h12
-rw-r--r--drivers/nvme/target/rdma.c72
-rw-r--r--drivers/nvmem/Kconfig6
-rw-r--r--drivers/nvmem/bcm-ocotp.c15
-rw-r--r--drivers/nvmem/core.c70
-rw-r--r--drivers/nvmem/imx-iim.c18
-rw-r--r--drivers/nvmem/imx-ocotp.c18
-rw-r--r--drivers/nvmem/lpc18xx_otp.c16
-rw-r--r--drivers/nvmem/meson-efuse.c16
-rw-r--r--drivers/nvmem/meson-mx-efuse.c16
-rw-r--r--drivers/nvmem/mtk-efuse.c16
-rw-r--r--drivers/nvmem/qfprom.c16
-rw-r--r--drivers/nvmem/rockchip-efuse.c32
-rw-r--r--drivers/nvmem/snvs_lpgpr.c53
-rw-r--r--drivers/nvmem/sunxi_sid.c71
-rw-r--r--drivers/nvmem/uniphier-efuse.c16
-rw-r--r--drivers/nvmem/vf610-ocotp.c24
-rw-r--r--drivers/of/address.c96
-rw-r--r--drivers/of/base.c270
-rw-r--r--drivers/of/dynamic.c21
-rw-r--r--drivers/of/fdt.c7
-rw-r--r--drivers/of/of_net.c40
-rw-r--r--drivers/of/of_private.h6
-rw-r--r--drivers/of/overlay.c20
-rw-r--r--drivers/of/resolver.c3
-rw-r--r--drivers/of/unittest-data/Makefile6
-rw-r--r--drivers/of/unittest-data/tests-phandle.dtsi25
-rw-r--r--drivers/of/unittest.c306
-rw-r--r--drivers/oprofile/oprofilefs.c3
-rw-r--r--drivers/parisc/lba_pci.c20
-rw-r--r--drivers/parisc/led.c4
-rw-r--r--drivers/parisc/power.c3
-rw-r--r--drivers/parport/parport_ax88796.c8
-rw-r--r--drivers/parport/parport_gsc.c2
-rw-r--r--drivers/parport/parport_pc.c4
-rw-r--r--drivers/parport/parport_serial.c125
-rw-r--r--drivers/pci/Makefile69
-rw-r--r--drivers/pci/access.c380
-rw-r--r--drivers/pci/ats.c10
-rw-r--r--drivers/pci/bus.c2
-rw-r--r--drivers/pci/cadence/pcie-cadence-ep.c15
-rw-r--r--drivers/pci/dwc/Kconfig1
-rw-r--r--drivers/pci/dwc/pci-exynos.c18
-rw-r--r--drivers/pci/dwc/pci-imx6.c18
-rw-r--r--drivers/pci/dwc/pci-keystone-dw.c91
-rw-r--r--drivers/pci/dwc/pci-keystone.c1
-rw-r--r--drivers/pci/dwc/pci-keystone.h4
-rw-r--r--drivers/pci/dwc/pci-layerscape.c3
-rw-r--r--drivers/pci/dwc/pcie-artpec6.c18
-rw-r--r--drivers/pci/dwc/pcie-designware-ep.c36
-rw-r--r--drivers/pci/dwc/pcie-designware-host.c396
-rw-r--r--drivers/pci/dwc/pcie-designware-plat.c16
-rw-r--r--drivers/pci/dwc/pcie-designware.h30
-rw-r--r--drivers/pci/dwc/pcie-histb.c43
-rw-r--r--drivers/pci/dwc/pcie-kirin.c5
-rw-r--r--drivers/pci/dwc/pcie-qcom.c91
-rw-r--r--drivers/pci/endpoint/functions/pci-epf-test.c28
-rw-r--r--drivers/pci/endpoint/pci-epc-core.c32
-rw-r--r--drivers/pci/endpoint/pci-epf-core.c56
-rw-r--r--drivers/pci/host-bridge.c2
-rw-r--r--drivers/pci/host/Kconfig2
-rw-r--r--drivers/pci/host/pci-aardvark.c53
-rw-r--r--drivers/pci/host/pci-ftpci100.c4
-rw-r--r--drivers/pci/host/pci-hyperv.c112
-rw-r--r--drivers/pci/host/pci-rcar-gen2.c1
-rw-r--r--drivers/pci/host/pci-tegra.c354
-rw-r--r--drivers/pci/host/pci-v3-semi.c2
-rw-r--r--drivers/pci/host/pci-xgene-msi.c2
-rw-r--r--drivers/pci/host/pcie-altera.c2
-rw-r--r--drivers/pci/host/pcie-iproc-bcma.c3
-rw-r--r--drivers/pci/host/pcie-iproc.c19
-rw-r--r--drivers/pci/host/pcie-iproc.h4
-rw-r--r--drivers/pci/host/pcie-rcar.c2
-rw-r--r--drivers/pci/host/pcie-xilinx-nwl.c4
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c23
-rw-r--r--drivers/pci/hotplug/cpqphp_ctrl.c12
-rw-r--r--drivers/pci/hotplug/pciehp.h3
-rw-r--r--drivers/pci/hotplug/pnv_php.c2
-rw-r--r--drivers/pci/iov.c50
-rw-r--r--drivers/pci/mmap.c2
-rw-r--r--drivers/pci/msi.c3
-rw-r--r--drivers/pci/pci-acpi.c3
-rw-r--r--drivers/pci/pci-driver.c101
-rw-r--r--drivers/pci/pci-label.c5
-rw-r--r--drivers/pci/pci-stub.c3
-rw-r--r--drivers/pci/pci-sysfs.c111
-rw-r--r--drivers/pci/pci.c384
-rw-r--r--drivers/pci/pci.h45
-rw-r--r--drivers/pci/pcie/Makefile19
-rw-r--r--drivers/pci/pcie/aer/aer_inject.c4
-rw-r--r--drivers/pci/pcie/aer/aerdrv.c10
-rw-r--r--drivers/pci/pcie/aer/aerdrv.h4
-rw-r--r--drivers/pci/pcie/aer/aerdrv_acpi.c1
-rw-r--r--drivers/pci/pcie/aer/aerdrv_core.c11
-rw-r--r--drivers/pci/pcie/aer/aerdrv_errprint.c3
-rw-r--r--drivers/pci/pcie/aer/ecrc.c8
-rw-r--r--drivers/pci/pcie/aspm.c23
-rw-r--r--drivers/pci/pcie/dpc.c307
-rw-r--r--drivers/pci/pcie/pcie-dpc.c306
-rw-r--r--drivers/pci/pcie/pme.c1
-rw-r--r--drivers/pci/pcie/portdrv.h87
-rw-r--r--drivers/pci/pcie/portdrv_acpi.c5
-rw-r--r--drivers/pci/pcie/portdrv_bus.c56
-rw-r--r--drivers/pci/pcie/portdrv_core.c84
-rw-r--r--drivers/pci/pcie/portdrv_pci.c61
-rw-r--r--drivers/pci/probe.c97
-rw-r--r--drivers/pci/proc.c4
-rw-r--r--drivers/pci/quirks.c201
-rw-r--r--drivers/pci/rom.c4
-rw-r--r--drivers/pci/search.c8
-rw-r--r--drivers/pci/setup-bus.c6
-rw-r--r--drivers/pci/setup-irq.c4
-rw-r--r--drivers/pci/setup-res.c12
-rw-r--r--drivers/pci/slot.c2
-rw-r--r--drivers/pci/syscall.c9
-rw-r--r--drivers/pci/vpd.c585
-rw-r--r--drivers/pci/xen-pcifront.c4
-rw-r--r--drivers/pcmcia/Kconfig26
-rw-r--r--drivers/pcmcia/Makefile7
-rw-r--r--drivers/pcmcia/bfin_cf_pcmcia.c316
-rw-r--r--drivers/pcmcia/cs.c10
-rw-r--r--drivers/pcmcia/cs_internal.h1
-rw-r--r--drivers/pcmcia/m32r_cfc.c786
-rw-r--r--drivers/pcmcia/m32r_cfc.h88
-rw-r--r--drivers/pcmcia/m32r_pcc.c763
-rw-r--r--drivers/pcmcia/m32r_pcc.h66
-rw-r--r--drivers/pcmcia/sa1100_assabet.c100
-rw-r--r--drivers/pcmcia/sa1100_cerf.c86
-rw-r--r--drivers/pcmcia/sa1100_generic.c115
-rw-r--r--drivers/pcmcia/sa1100_generic.h4
-rw-r--r--drivers/pcmcia/sa1100_h3600.c16
-rw-r--r--drivers/pcmcia/sa1100_nanoengine.c133
-rw-r--r--drivers/pcmcia/sa1100_shannon.c104
-rw-r--r--drivers/pcmcia/sa1100_simpad.c12
-rw-r--r--drivers/perf/Kconfig33
-rw-r--r--drivers/perf/Makefile2
-rw-r--r--drivers/perf/arm-cci.c1722
-rw-r--r--drivers/perf/arm-ccn.c1597
-rw-r--r--drivers/perf/arm_dsu_pmu.c2
-rw-r--r--drivers/perf/arm_pmu.c2
-rw-r--r--drivers/perf/arm_pmu_platform.c2
-rw-r--r--drivers/perf/arm_spe_pmu.c14
-rw-r--r--drivers/perf/hisilicon/hisi_uncore_pmu.c3
-rw-r--r--drivers/perf/qcom_l2_pmu.c7
-rw-r--r--drivers/perf/qcom_l3_pmu.c2
-rw-r--r--drivers/perf/xgene_pmu.c4
-rw-r--r--drivers/phy/allwinner/phy-sun4i-usb.c22
-rw-r--r--drivers/phy/amlogic/Kconfig13
-rw-r--r--drivers/phy/amlogic/Makefile1
-rw-r--r--drivers/phy/amlogic/phy-meson-gxl-usb2.c78
-rw-r--r--drivers/phy/amlogic/phy-meson-gxl-usb3.c282
-rw-r--r--drivers/phy/hisilicon/Kconfig20
-rw-r--r--drivers/phy/hisilicon/Makefile2
-rw-r--r--drivers/phy/hisilicon/phy-hisi-inno-usb2.c197
-rw-r--r--drivers/phy/hisilicon/phy-histb-combphy.c289
-rw-r--r--drivers/phy/marvell/phy-berlin-usb.c2
-rw-r--r--drivers/phy/mediatek/phy-mtk-tphy.c23
-rw-r--r--drivers/phy/motorola/Kconfig8
-rw-r--r--drivers/phy/motorola/Makefile1
-rw-r--r--drivers/phy/motorola/phy-mapphone-mdm6600.c542
-rw-r--r--drivers/phy/phy-core.c2
-rw-r--r--drivers/phy/phy-lpc18xx-usb-otg.c8
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp.c647
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp.h280
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qusb2.c418
-rw-r--r--drivers/phy/ralink/Kconfig1
-rw-r--r--drivers/phy/renesas/phy-rcar-gen3-usb2.c4
-rw-r--r--drivers/phy/rockchip/Kconfig1
-rw-r--r--drivers/phy/rockchip/phy-rockchip-emmc.c60
-rw-r--r--drivers/phy/rockchip/phy-rockchip-typec.c160
-rw-r--r--drivers/phy/samsung/Kconfig2
-rw-r--r--drivers/phy/st/Kconfig14
-rw-r--r--drivers/phy/st/Makefile1
-rw-r--r--drivers/phy/st/phy-stm32-usbphyc.c461
-rw-r--r--drivers/phy/tegra/xusb-tegra210.c6
-rw-r--r--drivers/phy/tegra/xusb.c2
-rw-r--r--drivers/phy/ti/phy-da8xx-usb.c16
-rw-r--r--drivers/pinctrl/Kconfig31
-rw-r--r--drivers/pinctrl/Makefile5
-rw-r--r--drivers/pinctrl/core.c109
-rw-r--r--drivers/pinctrl/devicetree.c6
-rw-r--r--drivers/pinctrl/freescale/Kconfig7
-rw-r--r--drivers/pinctrl/freescale/Makefile1
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx6sll.c360
-rw-r--r--drivers/pinctrl/intel/pinctrl-intel.c19
-rw-r--r--drivers/pinctrl/mediatek/Kconfig7
-rw-r--r--drivers/pinctrl/mediatek/Makefile1
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt2712.c633
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mtk-common.c12
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mtk-mt2701.h840
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mtk-mt2712.h1757
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mtk-mt6397.h123
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mtk-mt8127.h429
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mtk-mt8135.h609
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mtk-mt8173.h405
-rw-r--r--drivers/pinctrl/meson/pinctrl-meson8b.c20
-rw-r--r--drivers/pinctrl/nomadik/Kconfig12
-rw-r--r--drivers/pinctrl/nomadik/Makefile3
-rw-r--r--drivers/pinctrl/nomadik/pinctrl-ab8540.c408
-rw-r--r--drivers/pinctrl/nomadik/pinctrl-ab9540.c486
-rw-r--r--drivers/pinctrl/nomadik/pinctrl-abx500.c197
-rw-r--r--drivers/pinctrl/nomadik/pinctrl-abx500.h44
-rw-r--r--drivers/pinctrl/nomadik/pinctrl-nomadik-db8540.c1243
-rw-r--r--drivers/pinctrl/nomadik/pinctrl-nomadik-stn8815.c16
-rw-r--r--drivers/pinctrl/pinctrl-adi2-bf54x.c588
-rw-r--r--drivers/pinctrl/pinctrl-adi2-bf60x.c517
-rw-r--r--drivers/pinctrl/pinctrl-adi2.c1114
-rw-r--r--drivers/pinctrl/pinctrl-adi2.h75
-rw-r--r--drivers/pinctrl/pinctrl-amd.c23
-rw-r--r--drivers/pinctrl/pinctrl-artpec6.c66
-rw-r--r--drivers/pinctrl/pinctrl-mcp23s08.c54
-rw-r--r--drivers/pinctrl/pinctrl-ocelot.c4
-rw-r--r--drivers/pinctrl/pinctrl-single.c22
-rw-r--r--drivers/pinctrl/pinctrl-tz1090-pdc.c989
-rw-r--r--drivers/pinctrl/pinctrl-tz1090.c2005
-rw-r--r--drivers/pinctrl/qcom/Kconfig9
-rw-r--r--drivers/pinctrl/qcom/Makefile1
-rw-r--r--drivers/pinctrl/qcom/pinctrl-msm.c67
-rw-r--r--drivers/pinctrl/qcom/pinctrl-msm8998.c1
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sdm845.c1323
-rw-r--r--drivers/pinctrl/sh-pfc/Kconfig10
-rw-r--r--drivers/pinctrl/sh-pfc/Makefile2
-rw-r--r--drivers/pinctrl/sh-pfc/core.c12
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a7790.c8
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a7795-es1.c82
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a7795.c410
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a7796.c415
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a77965.c3243
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a77970.c98
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a77980.c2799
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a77995.c221
-rw-r--r--drivers/pinctrl/sh-pfc/pinctrl.c2
-rw-r--r--drivers/pinctrl/sh-pfc/sh_pfc.h15
-rw-r--r--drivers/pinctrl/stm32/Kconfig12
-rw-r--r--drivers/pinctrl/stm32/pinctrl-stm32.c3
-rw-r--r--drivers/pinctrl/sunxi/Kconfig4
-rw-r--r--drivers/pinctrl/sunxi/Makefile1
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun50i-h6.c614
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c4
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun8i-v3s.c4
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sunxi.c25
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sunxi.h40
-rw-r--r--drivers/pinctrl/uniphier/pinctrl-uniphier-ld11.c20
-rw-r--r--drivers/pinctrl/uniphier/pinctrl-uniphier-ld20.c20
-rw-r--r--drivers/pinctrl/uniphier/pinctrl-uniphier-ld4.c5
-rw-r--r--drivers/pinctrl/uniphier/pinctrl-uniphier-ld6b.c5
-rw-r--r--drivers/pinctrl/uniphier/pinctrl-uniphier-pro4.c5
-rw-r--r--drivers/pinctrl/uniphier/pinctrl-uniphier-pro5.c5
-rw-r--r--drivers/pinctrl/uniphier/pinctrl-uniphier-pxs2.c40
-rw-r--r--drivers/pinctrl/uniphier/pinctrl-uniphier-pxs3.c5
-rw-r--r--drivers/pinctrl/uniphier/pinctrl-uniphier-sld8.c5
-rw-r--r--drivers/platform/chrome/chromeos_laptop.c896
-rw-r--r--drivers/platform/chrome/cros_ec_debugfs.c76
-rw-r--r--drivers/platform/chrome/cros_ec_lpc.c16
-rw-r--r--drivers/platform/chrome/cros_ec_sysfs.c141
-rw-r--r--drivers/platform/mellanox/mlxreg-hotplug.c31
-rw-r--r--drivers/platform/x86/Kconfig4
-rw-r--r--drivers/platform/x86/dell-smbios-base.c4
-rw-r--r--drivers/platform/x86/eeepc-laptop.c2
-rw-r--r--drivers/platform/x86/fujitsu-laptop.c199
-rw-r--r--drivers/platform/x86/gpd-pocket-fan.c4
-rw-r--r--drivers/platform/x86/intel-hid.c14
-rw-r--r--drivers/platform/x86/intel_cht_int33fe.c24
-rw-r--r--drivers/platform/x86/intel_turbo_max_3.c3
-rw-r--r--drivers/platform/x86/mlx-platform.c68
-rw-r--r--drivers/platform/x86/silead_dmi.c17
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c397
-rw-r--r--drivers/platform/x86/topstar-laptop.c363
-rw-r--r--drivers/platform/x86/wmi.c23
-rw-r--r--drivers/power/avs/smartreflex.c41
-rw-r--r--drivers/power/reset/Kconfig16
-rw-r--r--drivers/power/reset/Makefile2
-rw-r--r--drivers/power/reset/at91-poweroff.c14
-rw-r--r--drivers/power/reset/at91-reset.c4
-rw-r--r--drivers/power/reset/gemini-poweroff.c30
-rw-r--r--drivers/power/reset/gpio-poweroff.c8
-rw-r--r--drivers/power/reset/ocelot-reset.c88
-rw-r--r--drivers/power/reset/sc27xx-poweroff.c66
-rw-r--r--drivers/power/supply/axp20x_battery.c136
-rw-r--r--drivers/power/supply/axp288_fuel_gauge.c53
-rw-r--r--drivers/power/supply/bq2415x_charger.c5
-rw-r--r--drivers/power/supply/bq27xxx_battery.c2
-rw-r--r--drivers/power/supply/da9150-fg.c4
-rw-r--r--drivers/power/supply/ds2780_battery.c5
-rw-r--r--drivers/power/supply/ds2781_battery.c5
-rw-r--r--drivers/power/supply/gpio-charger.c179
-rw-r--r--drivers/power/supply/ltc2941-battery-gauge.c67
-rw-r--r--drivers/power/supply/max17042_battery.c1
-rw-r--r--drivers/power/supply/power_supply_core.c2
-rw-r--r--drivers/powercap/intel_rapl.c1
-rw-r--r--drivers/pps/clients/pps_parport.c21
-rw-r--r--drivers/pps/generators/pps_gen_parport.c12
-rw-r--r--drivers/pwm/Kconfig17
-rw-r--r--drivers/pwm/Makefile1
-rw-r--r--drivers/pwm/pwm-atmel-tcb.c1
-rw-r--r--drivers/pwm/pwm-bfin.c157
-rw-r--r--drivers/pwm/pwm-imx.c3
-rw-r--r--drivers/pwm/pwm-jz4740.c41
-rw-r--r--drivers/pwm/pwm-mediatek.c36
-rw-r--r--drivers/pwm/pwm-omap-dmtimer.c68
-rw-r--r--drivers/pwm/pwm-puv3.c4
-rw-r--r--drivers/pwm/pwm-rcar.c58
-rw-r--r--drivers/pwm/pwm-stm32-lp.c5
-rw-r--r--drivers/pwm/pwm-stm32.c22
-rw-r--r--drivers/pwm/pwm-sun4i.c38
-rw-r--r--drivers/pwm/sysfs.c3
-rw-r--r--drivers/rapidio/devices/rio_mport_cdev.c137
-rw-r--r--drivers/rapidio/devices/tsi721.c5
-rw-r--r--drivers/rapidio/rio-scan.c6
-rw-r--r--drivers/regulator/88pg86x.c114
-rw-r--r--drivers/regulator/Kconfig9
-rw-r--r--drivers/regulator/Makefile1
-rw-r--r--drivers/regulator/core.c26
-rw-r--r--drivers/regulator/da9055-regulator.c4
-rw-r--r--drivers/regulator/da9211-regulator.c23
-rw-r--r--drivers/regulator/gpio-regulator.c17
-rw-r--r--drivers/regulator/of_regulator.c1
-rw-r--r--drivers/regulator/qcom_smd-regulator.c123
-rw-r--r--drivers/remoteproc/Kconfig19
-rw-r--r--drivers/remoteproc/Makefile1
-rw-r--r--drivers/remoteproc/imx_rproc.c23
-rw-r--r--drivers/remoteproc/qcom_adsp_pil.c20
-rw-r--r--drivers/remoteproc/qcom_common.c56
-rw-r--r--drivers/remoteproc/qcom_common.h23
-rw-r--r--drivers/remoteproc/qcom_q6v5_pil.c9
-rw-r--r--drivers/remoteproc/qcom_sysmon.c579
-rw-r--r--drivers/remoteproc/qcom_wcnss.c11
-rw-r--r--drivers/remoteproc/remoteproc_core.c152
-rw-r--r--drivers/remoteproc/remoteproc_internal.h7
-rw-r--r--drivers/reset/Kconfig17
-rw-r--r--drivers/reset/Makefile1
-rw-r--r--drivers/reset/core.c96
-rw-r--r--drivers/reset/reset-meson.c22
-rw-r--r--drivers/reset/reset-simple.c2
-rw-r--r--drivers/reset/reset-stm32mp1.c115
-rw-r--r--drivers/reset/reset-uniphier.c5
-rw-r--r--drivers/rpmsg/qcom_glink_native.c18
-rw-r--r--drivers/rpmsg/qcom_glink_smem.c3
-rw-r--r--drivers/rpmsg/qcom_smd.c51
-rw-r--r--drivers/rpmsg/rpmsg_core.c2
-rw-r--r--drivers/rtc/Kconfig32
-rw-r--r--drivers/rtc/Makefile3
-rw-r--r--drivers/rtc/class.c77
-rw-r--r--drivers/rtc/hctosys.c5
-rw-r--r--drivers/rtc/interface.c107
-rw-r--r--drivers/rtc/nvmem.c29
-rw-r--r--drivers/rtc/rtc-88pm80x.c4
-rw-r--r--drivers/rtc/rtc-88pm860x.c4
-rw-r--r--drivers/rtc/rtc-ab-b5ze-s3.c6
-rw-r--r--drivers/rtc/rtc-ab3100.c2
-rw-r--r--drivers/rtc/rtc-ab8500.c57
-rw-r--r--drivers/rtc/rtc-abx80x.c6
-rw-r--r--drivers/rtc/rtc-ac100.c26
-rw-r--r--drivers/rtc/rtc-at91sam9.c1
-rw-r--r--drivers/rtc/rtc-au1xxx.c2
-rw-r--r--drivers/rtc/rtc-bfin.c448
-rw-r--r--drivers/rtc/rtc-bq32k.c8
-rw-r--r--drivers/rtc/rtc-brcmstb-waketimer.c3
-rw-r--r--drivers/rtc/rtc-cmos.c89
-rw-r--r--drivers/rtc/rtc-coh901331.c2
-rw-r--r--drivers/rtc/rtc-core.h8
-rw-r--r--drivers/rtc/rtc-cpcap.c2
-rw-r--r--drivers/rtc/rtc-cros-ec.c8
-rw-r--r--drivers/rtc/rtc-da9052.c3
-rw-r--r--drivers/rtc/rtc-da9055.c2
-rw-r--r--drivers/rtc/rtc-da9063.c2
-rw-r--r--drivers/rtc/rtc-ds1216.c2
-rw-r--r--drivers/rtc/rtc-ds1286.c2
-rw-r--r--drivers/rtc/rtc-ds1302.c7
-rw-r--r--drivers/rtc/rtc-ds1305.c24
-rw-r--r--drivers/rtc/rtc-ds1307.c32
-rw-r--r--drivers/rtc/rtc-ds1343.c185
-rw-r--r--drivers/rtc/rtc-ds1347.c2
-rw-r--r--drivers/rtc/rtc-ds1390.c2
-rw-r--r--drivers/rtc/rtc-ds1511.c26
-rw-r--r--drivers/rtc/rtc-ds1553.c78
-rw-r--r--drivers/rtc/rtc-ds1685.c2
-rw-r--r--drivers/rtc/rtc-ds1742.c75
-rw-r--r--drivers/rtc/rtc-ds2404.c2
-rw-r--r--drivers/rtc/rtc-ds3232.c2
-rw-r--r--drivers/rtc/rtc-efi.c2
-rw-r--r--drivers/rtc/rtc-fm3130.c3
-rw-r--r--drivers/rtc/rtc-goldfish.c2
-rw-r--r--drivers/rtc/rtc-isl12022.c20
-rw-r--r--drivers/rtc/rtc-isl12026.c501
-rw-r--r--drivers/rtc/rtc-isl1208.c47
-rw-r--r--drivers/rtc/rtc-jz4740.c2
-rw-r--r--drivers/rtc/rtc-lib.c8
-rw-r--r--drivers/rtc/rtc-lpc24xx.c2
-rw-r--r--drivers/rtc/rtc-lpc32xx.c2
-rw-r--r--drivers/rtc/rtc-ls1x.c2
-rw-r--r--drivers/rtc/rtc-m41t80.c42
-rw-r--r--drivers/rtc/rtc-m41t93.c2
-rw-r--r--drivers/rtc/rtc-m41t94.c3
-rw-r--r--drivers/rtc/rtc-m48t35.c2
-rw-r--r--drivers/rtc/rtc-m48t59.c61
-rw-r--r--drivers/rtc/rtc-m48t86.c25
-rw-r--r--drivers/rtc/rtc-max6900.c19
-rw-r--r--drivers/rtc/rtc-max6902.c2
-rw-r--r--drivers/rtc/rtc-max6916.c2
-rw-r--r--drivers/rtc/rtc-max77686.c4
-rw-r--r--drivers/rtc/rtc-max8997.c2
-rw-r--r--drivers/rtc/rtc-max8998.c2
-rw-r--r--drivers/rtc/rtc-mc13xxx.c2
-rw-r--r--drivers/rtc/rtc-mcp795.c4
-rw-r--r--drivers/rtc/rtc-mpc5121.c2
-rw-r--r--drivers/rtc/rtc-mrst.c4
-rw-r--r--drivers/rtc/rtc-msm6242.c2
-rw-r--r--drivers/rtc/rtc-mt7622.c3
-rw-r--r--drivers/rtc/rtc-mv.c14
-rw-r--r--drivers/rtc/rtc-mxc_v2.c2
-rw-r--r--drivers/rtc/rtc-nuc900.c14
-rw-r--r--drivers/rtc/rtc-omap.c6
-rw-r--r--drivers/rtc/rtc-opal.c37
-rw-r--r--drivers/rtc/rtc-pcap.c2
-rw-r--r--drivers/rtc/rtc-pcf2123.c2
-rw-r--r--drivers/rtc/rtc-pcf2127.c2
-rw-r--r--drivers/rtc/rtc-pcf50633.c2
-rw-r--r--drivers/rtc/rtc-pcf85063.c20
-rw-r--r--drivers/rtc/rtc-pcf8523.c2
-rw-r--r--drivers/rtc/rtc-pcf85363.c205
-rw-r--r--drivers/rtc/rtc-pic32.c2
-rw-r--r--drivers/rtc/rtc-pm8xxx.c55
-rw-r--r--drivers/rtc/rtc-ps3.c2
-rw-r--r--drivers/rtc/rtc-r7301.c2
-rw-r--r--drivers/rtc/rtc-r9701.c2
-rw-r--r--drivers/rtc/rtc-rk808.c25
-rw-r--r--drivers/rtc/rtc-rp5c01.c67
-rw-r--r--drivers/rtc/rtc-rs5c348.c5
-rw-r--r--drivers/rtc/rtc-rs5c372.c24
-rw-r--r--drivers/rtc/rtc-rv8803.c33
-rw-r--r--drivers/rtc/rtc-rx4581.c6
-rw-r--r--drivers/rtc/rtc-rx6110.c2
-rw-r--r--drivers/rtc/rtc-rx8010.c2
-rw-r--r--drivers/rtc/rtc-rx8025.c2
-rw-r--r--drivers/rtc/rtc-rx8581.c6
-rw-r--r--drivers/rtc/rtc-s35390a.c38
-rw-r--r--drivers/rtc/rtc-s3c.c2
-rw-r--r--drivers/rtc/rtc-s5m.c27
-rw-r--r--drivers/rtc/rtc-sc27xx.c2
-rw-r--r--drivers/rtc/rtc-sh.c2
-rw-r--r--drivers/rtc/rtc-sirfsoc.c18
-rw-r--r--drivers/rtc/rtc-snvs.c15
-rw-r--r--drivers/rtc/rtc-spear.c12
-rw-r--r--drivers/rtc/rtc-st-lpc.c16
-rw-r--r--drivers/rtc/rtc-starfire.c2
-rw-r--r--drivers/rtc/rtc-stk17ta8.c74
-rw-r--r--drivers/rtc/rtc-sun6i.c2
-rw-r--r--drivers/rtc/rtc-sunxi.c2
-rw-r--r--drivers/rtc/rtc-sysfs.c12
-rw-r--r--drivers/rtc/rtc-tegra.c4
-rw-r--r--drivers/rtc/rtc-tile.c146
-rw-r--r--drivers/rtc/rtc-tps6586x.c2
-rw-r--r--drivers/rtc/rtc-tx4939.c95
-rw-r--r--drivers/rtc/rtc-wm831x.c2
-rw-r--r--drivers/rtc/rtc-xgene.c2
-rw-r--r--drivers/rtc/rtc-zynqmp.c2
-rw-r--r--drivers/rtc/systohc.c2
-rw-r--r--drivers/s390/Makefile2
-rw-r--r--drivers/s390/block/Kconfig2
-rw-r--r--drivers/s390/block/dasd.c13
-rw-r--r--drivers/s390/block/dasd_3990_erp.c17
-rw-r--r--drivers/s390/block/dasd_alias.c13
-rw-r--r--drivers/s390/block/dasd_devmap.c43
-rw-r--r--drivers/s390/block/dasd_diag.c1
-rw-r--r--drivers/s390/block/dasd_eckd.c27
-rw-r--r--drivers/s390/block/dcssblk.c2
-rw-r--r--drivers/s390/block/scm_blk.c4
-rw-r--r--drivers/s390/block/xpram.c4
-rw-r--r--drivers/s390/char/Makefile2
-rw-r--r--drivers/s390/char/defkeymap.c66
-rw-r--r--drivers/s390/char/keyboard.c32
-rw-r--r--drivers/s390/char/keyboard.h11
-rw-r--r--drivers/s390/char/sclp.c58
-rw-r--r--drivers/s390/char/sclp.h61
-rw-r--r--drivers/s390/char/sclp_early.c2
-rw-r--r--drivers/s390/char/sclp_early_core.c38
-rw-r--r--drivers/s390/char/sclp_sd.c569
-rw-r--r--drivers/s390/char/sclp_tty.c5
-rw-r--r--drivers/s390/cio/ccwgroup.c5
-rw-r--r--drivers/s390/cio/chp.c34
-rw-r--r--drivers/s390/cio/chp.h5
-rw-r--r--drivers/s390/cio/chsc.c73
-rw-r--r--drivers/s390/cio/chsc.h11
-rw-r--r--drivers/s390/cio/cio.c257
-rw-r--r--drivers/s390/cio/device.c16
-rw-r--r--drivers/s390/cio/device_ops.c4
-rw-r--r--drivers/s390/cio/ioasm.c24
-rw-r--r--drivers/s390/cio/ioasm.h1
-rw-r--r--drivers/s390/cio/qdio_main.c135
-rw-r--r--drivers/s390/cio/qdio_setup.c2
-rw-r--r--drivers/s390/cio/vfio_ccw_fsm.c24
-rw-r--r--drivers/s390/crypto/ap_bus.c32
-rw-r--r--drivers/s390/crypto/ap_bus.h5
-rw-r--r--drivers/s390/crypto/ap_debug.h3
-rw-r--r--drivers/s390/crypto/pkey_api.c41
-rw-r--r--drivers/s390/crypto/zcrypt_api.c471
-rw-r--r--drivers/s390/crypto/zcrypt_api.h26
-rw-r--r--drivers/s390/net/qeth_core.h10
-rw-r--r--drivers/s390/net/qeth_core_main.c200
-rw-r--r--drivers/s390/net/qeth_core_mpc.h12
-rw-r--r--drivers/s390/net/qeth_l2_main.c94
-rw-r--r--drivers/s390/net/qeth_l3.h34
-rw-r--r--drivers/s390/net/qeth_l3_main.c419
-rw-r--r--drivers/s390/net/qeth_l3_sys.c51
-rw-r--r--drivers/s390/net/smsgiucv.c2
-rw-r--r--drivers/s390/scsi/zfcp_fc.c4
-rw-r--r--drivers/sbus/char/Kconfig3
-rw-r--r--drivers/sbus/char/oradax.c2
-rw-r--r--drivers/scsi/Kconfig114
-rw-r--r--drivers/scsi/Makefile5
-rw-r--r--drivers/scsi/NCR53c406a.c1090
-rw-r--r--drivers/scsi/aacraid/aacraid.h5
-rw-r--r--drivers/scsi/aacraid/commsup.c4
-rw-r--r--drivers/scsi/aacraid/linit.c1
-rw-r--r--drivers/scsi/aacraid/src.c209
-rw-r--r--drivers/scsi/aha1740.c2
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_core.c8
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_seq.h_shipped3
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_core.c8
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_seq.h_shipped3
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm.c3
-rw-r--r--drivers/scsi/arcmsr/arcmsr.h4
-rw-r--r--drivers/scsi/arcmsr/arcmsr_hba.c90
-rw-r--r--drivers/scsi/atp870u.c4
-rw-r--r--drivers/scsi/bfa/bfad_bsg.c2
-rw-r--r--drivers/scsi/csiostor/csio_attr.c16
-rw-r--r--drivers/scsi/csiostor/csio_hw.c275
-rw-r--r--drivers/scsi/csiostor/csio_hw.h59
-rw-r--r--drivers/scsi/csiostor/csio_lnode.c8
-rw-r--r--drivers/scsi/csiostor/csio_mb.c70
-rw-r--r--drivers/scsi/csiostor/csio_mb.h9
-rw-r--r--drivers/scsi/cxgbi/cxgb4i/cxgb4i.c8
-rw-r--r--drivers/scsi/device_handler/scsi_dh_alua.c10
-rw-r--r--drivers/scsi/device_handler/scsi_dh_emc.c2
-rw-r--r--drivers/scsi/device_handler/scsi_dh_rdac.c2
-rw-r--r--drivers/scsi/dpt_i2o.c42
-rw-r--r--drivers/scsi/dpti.h1
-rw-r--r--drivers/scsi/eata.c2571
-rw-r--r--drivers/scsi/eata_generic.h401
-rw-r--r--drivers/scsi/eata_pio.c966
-rw-r--r--drivers/scsi/eata_pio.h54
-rw-r--r--drivers/scsi/esas2r/esas2r.h2
-rw-r--r--drivers/scsi/esas2r/esas2r_init.c21
-rw-r--r--drivers/scsi/esas2r/esas2r_main.c72
-rw-r--r--drivers/scsi/fdomain.c1783
-rw-r--r--drivers/scsi/fdomain.h24
-rw-r--r--drivers/scsi/fnic/fnic_trace.c2
-rw-r--r--drivers/scsi/gdth.h3
-rw-r--r--drivers/scsi/hisi_sas/Kconfig2
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas.h1
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas_main.c34
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas_v1_hw.c13
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas_v2_hw.c62
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas_v3_hw.c72
-rw-r--r--drivers/scsi/hosts.c38
-rw-r--r--drivers/scsi/hpsa.c73
-rw-r--r--drivers/scsi/hpsa.h1
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.c6
-rw-r--r--drivers/scsi/ipr.c53
-rw-r--r--drivers/scsi/ipr.h2
-rw-r--r--drivers/scsi/ips.c4
-rw-r--r--drivers/scsi/isci/host.c2
-rw-r--r--drivers/scsi/iscsi_tcp.c24
-rw-r--r--drivers/scsi/jazz_esp.c2
-rw-r--r--drivers/scsi/libfc/fc_disc.c2
-rw-r--r--drivers/scsi/libsas/sas_ata.c2
-rw-r--r--drivers/scsi/libsas/sas_discover.c13
-rw-r--r--drivers/scsi/libsas/sas_expander.c29
-rw-r--r--drivers/scsi/libsas/sas_init.c2
-rw-r--r--drivers/scsi/libsas/sas_port.c5
-rw-r--r--drivers/scsi/lpfc/lpfc.h23
-rw-r--r--drivers/scsi/lpfc/lpfc_attr.c107
-rw-r--r--drivers/scsi/lpfc/lpfc_bsg.c6
-rw-r--r--drivers/scsi/lpfc/lpfc_crtn.h5
-rw-r--r--drivers/scsi/lpfc/lpfc_ct.c8
-rw-r--r--drivers/scsi/lpfc/lpfc_debugfs.c22
-rw-r--r--drivers/scsi/lpfc/lpfc_debugfs.h13
-rw-r--r--drivers/scsi/lpfc/lpfc_els.c11
-rw-r--r--drivers/scsi/lpfc/lpfc_hbadisc.c13
-rw-r--r--drivers/scsi/lpfc/lpfc_hw.h15
-rw-r--r--drivers/scsi/lpfc/lpfc_hw4.h141
-rw-r--r--drivers/scsi/lpfc/lpfc_ids.h4
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c320
-rw-r--r--drivers/scsi/lpfc/lpfc_mbox.c10
-rw-r--r--drivers/scsi/lpfc/lpfc_mem.c12
-rw-r--r--drivers/scsi/lpfc/lpfc_nportdisc.c22
-rw-r--r--drivers/scsi/lpfc/lpfc_nvme.c427
-rw-r--r--drivers/scsi/lpfc/lpfc_nvme.h4
-rw-r--r--drivers/scsi/lpfc/lpfc_nvmet.c470
-rw-r--r--drivers/scsi/lpfc/lpfc_nvmet.h10
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.c64
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.h2
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.c692
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.h6
-rw-r--r--drivers/scsi/lpfc/lpfc_sli4.h42
-rw-r--r--drivers/scsi/lpfc/lpfc_version.h8
-rw-r--r--drivers/scsi/megaraid/megaraid_sas.h1
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_base.c44
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_fusion.c20
-rw-r--r--drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h1
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_base.c699
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_base.h19
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_config.c3
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_ctl.c22
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_scsih.c76
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_transport.c8
-rw-r--r--drivers/scsi/mvme147.c107
-rw-r--r--drivers/scsi/mvsas/mv_94xx.c23
-rw-r--r--drivers/scsi/pcmcia/Kconfig9
-rw-r--r--drivers/scsi/pcmcia/Makefile2
-rw-r--r--drivers/scsi/pcmcia/fdomain_core.c2
-rw-r--r--drivers/scsi/pcmcia/fdomain_stub.c209
-rw-r--r--drivers/scsi/pmcraid.c51
-rw-r--r--drivers/scsi/pmcraid.h3
-rw-r--r--drivers/scsi/qedf/qedf_dbg.c2
-rw-r--r--drivers/scsi/qedf/qedf_dbg.h17
-rw-r--r--drivers/scsi/qedf/qedf_debugfs.c6
-rw-r--r--drivers/scsi/qedf/qedf_io.c2
-rw-r--r--drivers/scsi/qedf/qedf_main.c8
-rw-r--r--drivers/scsi/qedi/qedi_dbg.h4
-rw-r--r--drivers/scsi/qedi/qedi_debugfs.c4
-rw-r--r--drivers/scsi/qedi/qedi_fw.c2
-rw-r--r--drivers/scsi/qedi/qedi_gbl.h4
-rw-r--r--drivers/scsi/qedi/qedi_main.c4
-rw-r--r--drivers/scsi/qla2xxx/qla_bsg.c3
-rw-r--r--drivers/scsi/qla2xxx/qla_dbg.c8
-rw-r--r--drivers/scsi/qla2xxx/qla_def.h12
-rw-r--r--drivers/scsi/qla2xxx/qla_gbl.h4
-rw-r--r--drivers/scsi/qla2xxx/qla_gs.c365
-rw-r--r--drivers/scsi/qla2xxx/qla_init.c113
-rw-r--r--drivers/scsi/qla2xxx/qla_inline.h3
-rw-r--r--drivers/scsi/qla2xxx/qla_iocb.c33
-rw-r--r--drivers/scsi/qla2xxx/qla_isr.c109
-rw-r--r--drivers/scsi/qla2xxx/qla_mbx.c41
-rw-r--r--drivers/scsi/qla2xxx/qla_mid.c8
-rw-r--r--drivers/scsi/qla2xxx/qla_mr.c41
-rw-r--r--drivers/scsi/qla2xxx/qla_nvme.c245
-rw-r--r--drivers/scsi/qla2xxx/qla_nvme.h9
-rw-r--r--drivers/scsi/qla2xxx/qla_nx.c7
-rw-r--r--drivers/scsi/qla2xxx/qla_nx2.c19
-rw-r--r--drivers/scsi/qla2xxx/qla_nx2.h4
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c60
-rw-r--r--drivers/scsi/qla2xxx/qla_sup.c1
-rw-r--r--drivers/scsi/qla2xxx/qla_target.c31
-rw-r--r--drivers/scsi/qla2xxx/qla_target.h2
-rw-r--r--drivers/scsi/qla2xxx/qla_version.h2
-rw-r--r--drivers/scsi/qla4xxx/ql4_mbx.c6
-rw-r--r--drivers/scsi/qla4xxx/ql4_nx.c2
-rw-r--r--drivers/scsi/qla4xxx/ql4_nx.h7
-rw-r--r--drivers/scsi/qla4xxx/ql4_os.c4
-rw-r--r--drivers/scsi/raid_class.c1
-rw-r--r--drivers/scsi/scsi.c2
-rw-r--r--drivers/scsi/scsi_debug.c258
-rw-r--r--drivers/scsi/scsi_devinfo.c15
-rw-r--r--drivers/scsi/scsi_dh.c3
-rw-r--r--drivers/scsi/scsi_error.c8
-rw-r--r--drivers/scsi/scsi_lib.c60
-rw-r--r--drivers/scsi/scsi_module.c73
-rw-r--r--drivers/scsi/scsi_sysfs.c14
-rw-r--r--drivers/scsi/scsi_transport_iscsi.c29
-rw-r--r--drivers/scsi/scsi_transport_sas.c3
-rw-r--r--drivers/scsi/scsi_transport_spi.c4
-rw-r--r--drivers/scsi/sd.c12
-rw-r--r--drivers/scsi/sd_zbc.c143
-rw-r--r--drivers/scsi/smartpqi/smartpqi_init.c91
-rw-r--r--drivers/scsi/sr.c21
-rw-r--r--drivers/scsi/sun3x_esp.c2
-rw-r--r--drivers/scsi/sym53c416.c844
-rw-r--r--drivers/scsi/sym53c416.h33
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_glue.c2
-rw-r--r--drivers/scsi/ufs/Makefile3
-rw-r--r--drivers/scsi/ufs/tc-dwc-g210-pci.c4
-rw-r--r--drivers/scsi/ufs/tc-dwc-g210-pltfrm.c2
-rw-r--r--drivers/scsi/ufs/ufs-sysfs.c817
-rw-r--r--drivers/scsi/ufs/ufs-sysfs.h17
-rw-r--r--drivers/scsi/ufs/ufs.h115
-rw-r--r--drivers/scsi/ufs/ufshcd-pci.c7
-rw-r--r--drivers/scsi/ufs/ufshcd.c378
-rw-r--r--drivers/scsi/ufs/ufshcd.h37
-rw-r--r--drivers/scsi/ufs/ufshci.h7
-rw-r--r--drivers/scsi/virtio_scsi.c129
-rw-r--r--drivers/siox/siox-core.c2
-rw-r--r--drivers/slimbus/core.c2
-rw-r--r--drivers/slimbus/messaging.c2
-rw-r--r--drivers/soc/amlogic/meson-gx-pwrc-vpu.c9
-rw-r--r--drivers/soc/amlogic/meson-gx-socinfo.c12
-rw-r--r--drivers/soc/amlogic/meson-mx-socinfo.c2
-rw-r--r--drivers/soc/bcm/raspberrypi-power.c2
-rw-r--r--drivers/soc/imx/gpc.c1
-rw-r--r--drivers/soc/mediatek/mtk-scpsys.c104
-rw-r--r--drivers/soc/qcom/Kconfig3
-rw-r--r--drivers/soc/qcom/mdt_loader.c7
-rw-r--r--drivers/soc/qcom/qmi_interface.c3
-rw-r--r--drivers/soc/qcom/rmtfs_mem.c34
-rw-r--r--drivers/soc/qcom/wcnss_ctrl.c2
-rw-r--r--drivers/soc/renesas/Kconfig14
-rw-r--r--drivers/soc/renesas/Makefile2
-rw-r--r--drivers/soc/renesas/r8a77965-sysc.c37
-rw-r--r--drivers/soc/renesas/r8a77970-sysc.c12
-rw-r--r--drivers/soc/renesas/r8a77980-sysc.c52
-rw-r--r--drivers/soc/renesas/rcar-rst.c37
-rw-r--r--drivers/soc/renesas/rcar-sysc.c8
-rw-r--r--drivers/soc/renesas/rcar-sysc.h2
-rw-r--r--drivers/soc/renesas/renesas-soc.c16
-rw-r--r--drivers/soc/rockchip/grf.c28
-rw-r--r--drivers/soc/rockchip/pm_domains.c95
-rw-r--r--drivers/soc/samsung/exynos-pmu.c7
-rw-r--r--drivers/soc/samsung/pm_domains.c11
-rw-r--r--drivers/soc/tegra/Kconfig10
-rw-r--r--drivers/soc/tegra/pmc.c98
-rw-r--r--drivers/soc/ti/Kconfig9
-rw-r--r--drivers/soc/ti/Makefile1
-rw-r--r--drivers/soc/ti/pm33xx.c349
-rw-r--r--drivers/spi/Kconfig23
-rw-r--r--drivers/spi/Makefile3
-rw-r--r--drivers/spi/spi-adi-v3.c984
-rw-r--r--drivers/spi/spi-atmel.c16
-rw-r--r--drivers/spi/spi-bcm-qspi.c4
-rw-r--r--drivers/spi/spi-bcm2835aux.c18
-rw-r--r--drivers/spi/spi-bfin-sport.c919
-rw-r--r--drivers/spi/spi-bfin5xx.c1462
-rw-r--r--drivers/spi/spi-dw-mid.c6
-rw-r--r--drivers/spi/spi-dw.c26
-rw-r--r--drivers/spi/spi-dw.h4
-rw-r--r--drivers/spi/spi-gpio.c270
-rw-r--r--drivers/spi/spi-jcore.c11
-rw-r--r--drivers/spi/spi-orion.c65
-rw-r--r--drivers/spi/spi-pxa2xx-dma.c4
-rw-r--r--drivers/spi/spi-pxa2xx.c56
-rw-r--r--drivers/spi/spi-pxa2xx.h2
-rw-r--r--drivers/spi/spi-rspi.c4
-rw-r--r--drivers/spi/spi-sh-msiof.c4
-rw-r--r--drivers/spi/spi-sprd-adi.c176
-rw-r--r--drivers/spi/spi-tegra20-slink.c4
-rw-r--r--drivers/spi/spi.c19
-rw-r--r--drivers/ssb/Kconfig2
-rw-r--r--drivers/ssb/main.c4
-rw-r--r--drivers/staging/Kconfig18
-rw-r--r--drivers/staging/Makefile11
-rw-r--r--drivers/staging/android/ashmem.c2
-rw-r--r--drivers/staging/android/ion/Kconfig2
-rw-r--r--drivers/staging/android/ion/ion.c26
-rw-r--r--drivers/staging/android/ion/ion.h22
-rw-r--r--drivers/staging/android/ion/ion_page_pool.c33
-rw-r--r--drivers/staging/android/ion/ion_system_heap.c76
-rw-r--r--drivers/staging/ccree/Kconfig27
-rw-r--r--drivers/staging/ccree/TODO10
-rw-r--r--drivers/staging/ccree/cc_aead.c2701
-rw-r--r--drivers/staging/ccree/cc_buffer_mgr.c1651
-rw-r--r--drivers/staging/ccree/cc_buffer_mgr.h74
-rw-r--r--drivers/staging/ccree/cc_cipher.c1164
-rw-r--r--drivers/staging/ccree/cc_cipher.h74
-rw-r--r--drivers/staging/ccree/cc_crypto_ctx.h170
-rw-r--r--drivers/staging/ccree/cc_driver.c474
-rw-r--r--drivers/staging/ccree/cc_driver.h194
-rw-r--r--drivers/staging/ccree/cc_fips.c111
-rw-r--r--drivers/staging/ccree/cc_fips.h37
-rw-r--r--drivers/staging/ccree/cc_hash.c2295
-rw-r--r--drivers/staging/ccree/cc_hash.h114
-rw-r--r--drivers/staging/ccree/cc_host_regs.h142
-rw-r--r--drivers/staging/ccree/cc_hw_queue_defs.h590
-rw-r--r--drivers/staging/ccree/cc_ivgen.c280
-rw-r--r--drivers/staging/ccree/cc_kernel_regs.h167
-rw-r--r--drivers/staging/ccree/cc_pm.h57
-rw-r--r--drivers/staging/ccree/cc_request_mgr.c713
-rw-r--r--drivers/staging/ccree/cc_sram_mgr.c107
-rw-r--r--drivers/staging/comedi/drivers/adl_pci6208.c3
-rw-r--r--drivers/staging/comedi/drivers/cb_pcidas64.c40
-rw-r--r--drivers/staging/comedi/drivers/das16.c2
-rw-r--r--drivers/staging/comedi/drivers/das16m1.c2
-rw-r--r--drivers/staging/comedi/drivers/jr3_pci.c1
-rw-r--r--drivers/staging/comedi/drivers/ni_atmio.c9
-rw-r--r--drivers/staging/comedi/drivers/ni_mio_common.c10
-rw-r--r--drivers/staging/comedi/drivers/ni_stc.h2
-rw-r--r--drivers/staging/comedi/drivers/quatech_daqp_cs.c2
-rw-r--r--drivers/staging/comedi/drivers/s626.c10
-rw-r--r--drivers/staging/emxx_udc/emxx_udc.c10
-rw-r--r--drivers/staging/fsl-dpaa2/Kconfig10
-rw-r--r--drivers/staging/fsl-dpaa2/Makefile1
-rw-r--r--drivers/staging/fsl-dpaa2/ethernet/README2
-rw-r--r--drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c69
-rw-r--r--drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h22
-rw-r--r--drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c26
-rw-r--r--drivers/staging/fsl-dpaa2/ethernet/dpni-cmd.h4
-rw-r--r--drivers/staging/fsl-dpaa2/ethernet/dpni.c86
-rw-r--r--drivers/staging/fsl-dpaa2/ethsw/Makefile10
-rw-r--r--drivers/staging/fsl-dpaa2/ethsw/README106
-rw-r--r--drivers/staging/fsl-dpaa2/ethsw/TODO14
-rw-r--r--drivers/staging/fsl-dpaa2/ethsw/dpsw-cmd.h346
-rw-r--r--drivers/staging/fsl-dpaa2/ethsw/dpsw.c1123
-rw-r--r--drivers/staging/fsl-dpaa2/ethsw/dpsw.h586
-rw-r--r--drivers/staging/fsl-dpaa2/ethsw/ethsw-ethtool.c182
-rw-r--r--drivers/staging/fsl-dpaa2/ethsw/ethsw.c1508
-rw-r--r--drivers/staging/fsl-dpaa2/ethsw/ethsw.h67
-rw-r--r--drivers/staging/fsl-mc/TODO18
-rw-r--r--drivers/staging/fsl-mc/bus/Kconfig12
-rw-r--r--drivers/staging/fsl-mc/bus/Makefile13
-rw-r--r--drivers/staging/fsl-mc/bus/dpbp-cmd.h54
-rw-r--r--drivers/staging/fsl-mc/bus/dpbp.c253
-rw-r--r--drivers/staging/fsl-mc/bus/dpcon-cmd.h53
-rw-r--r--drivers/staging/fsl-mc/bus/dpcon.c222
-rw-r--r--drivers/staging/fsl-mc/bus/dpio/Makefile2
-rw-r--r--drivers/staging/fsl-mc/bus/dpio/dpio-driver.c2
-rw-r--r--drivers/staging/fsl-mc/bus/dpio/dpio-service.c6
-rw-r--r--drivers/staging/fsl-mc/bus/dpio/dpio.c14
-rw-r--r--drivers/staging/fsl-mc/bus/dpio/qbman-portal.c27
-rw-r--r--drivers/staging/fsl-mc/bus/dpio/qbman-portal.h24
-rw-r--r--drivers/staging/fsl-mc/bus/dpmcp.c99
-rw-r--r--drivers/staging/fsl-mc/bus/dprc-driver.c809
-rw-r--r--drivers/staging/fsl-mc/bus/dprc.c531
-rw-r--r--drivers/staging/fsl-mc/bus/fsl-mc-allocator.c648
-rw-r--r--drivers/staging/fsl-mc/bus/fsl-mc-bus.c948
-rw-r--r--drivers/staging/fsl-mc/bus/fsl-mc-msi.c284
-rw-r--r--drivers/staging/fsl-mc/bus/fsl-mc-private.h475
-rw-r--r--drivers/staging/fsl-mc/bus/irq-gic-v3-its-fsl-mc-msi.c102
-rw-r--r--drivers/staging/fsl-mc/bus/mc-io.c268
-rw-r--r--drivers/staging/fsl-mc/bus/mc-sys.c296
-rw-r--r--drivers/staging/fsl-mc/include/dpaa2-fd.h6
-rw-r--r--drivers/staging/fsl-mc/include/dpaa2-io.h2
-rw-r--r--drivers/staging/fsl-mc/include/dpbp.h63
-rw-r--r--drivers/staging/fsl-mc/include/dpcon.h79
-rw-r--r--drivers/staging/fsl-mc/include/mc.h454
-rw-r--r--drivers/staging/fsl-mc/overview.rst404
-rw-r--r--drivers/staging/gdm724x/gdm_endian.c24
-rw-r--r--drivers/staging/gdm724x/gdm_endian.h13
-rw-r--r--drivers/staging/gdm724x/gdm_lte.c62
-rw-r--r--drivers/staging/gdm724x/gdm_lte.h2
-rw-r--r--drivers/staging/gdm724x/gdm_mux.c6
-rw-r--r--drivers/staging/gdm724x/gdm_tty.c29
-rw-r--r--drivers/staging/gdm724x/gdm_usb.c30
-rw-r--r--drivers/staging/gdm724x/gdm_usb.h2
-rw-r--r--drivers/staging/goldfish/goldfish_nand.c3
-rw-r--r--drivers/staging/iio/Kconfig1
-rw-r--r--drivers/staging/iio/Makefile1
-rw-r--r--drivers/staging/iio/accel/adis16201.c10
-rw-r--r--drivers/staging/iio/accel/adis16209.c272
-rw-r--r--drivers/staging/iio/adc/ad7192.c6
-rw-r--r--drivers/staging/iio/adc/ad7816.c2
-rw-r--r--drivers/staging/iio/addac/adt7316.c3
-rw-r--r--drivers/staging/iio/cdc/ad7150.c5
-rw-r--r--drivers/staging/iio/cdc/ad7152.c6
-rw-r--r--drivers/staging/iio/cdc/ad7746.c12
-rw-r--r--drivers/staging/iio/light/tsl2x7x.c290
-rw-r--r--drivers/staging/iio/light/tsl2x7x.h6
-rw-r--r--drivers/staging/iio/meter/ade7753.c18
-rw-r--r--drivers/staging/iio/meter/ade7754.c6
-rw-r--r--drivers/staging/iio/meter/ade7758.h2
-rw-r--r--drivers/staging/iio/meter/ade7758_core.c52
-rw-r--r--drivers/staging/iio/meter/ade7758_trigger.c8
-rw-r--r--drivers/staging/iio/meter/ade7759.c98
-rw-r--r--drivers/staging/iio/meter/ade7854-i2c.c28
-rw-r--r--drivers/staging/iio/meter/ade7854-spi.c60
-rw-r--r--drivers/staging/iio/meter/ade7854.h28
-rw-r--r--drivers/staging/iio/meter/meter.h3
-rw-r--r--drivers/staging/iio/resolver/ad2s1210.c20
-rw-r--r--drivers/staging/iio/trigger/Kconfig19
-rw-r--r--drivers/staging/iio/trigger/Makefile5
-rw-r--r--drivers/staging/iio/trigger/iio-trig-bfin-timer.c292
-rw-r--r--drivers/staging/iio/trigger/iio-trig-bfin-timer.h25
-rw-r--r--drivers/staging/ipx/af_ipx.c6
-rw-r--r--drivers/staging/irda/TODO4
-rw-r--r--drivers/staging/irda/drivers/Kconfig398
-rw-r--r--drivers/staging/irda/drivers/Makefile44
-rw-r--r--drivers/staging/irda/drivers/act200l-sir.c250
-rw-r--r--drivers/staging/irda/drivers/actisys-sir.c245
-rw-r--r--drivers/staging/irda/drivers/ali-ircc.c2217
-rw-r--r--drivers/staging/irda/drivers/ali-ircc.h227
-rw-r--r--drivers/staging/irda/drivers/au1k_ir.c985
-rw-r--r--drivers/staging/irda/drivers/bfin_sir.c819
-rw-r--r--drivers/staging/irda/drivers/bfin_sir.h93
-rw-r--r--drivers/staging/irda/drivers/donauboe.c1732
-rw-r--r--drivers/staging/irda/drivers/donauboe.h362
-rw-r--r--drivers/staging/irda/drivers/esi-sir.c157
-rw-r--r--drivers/staging/irda/drivers/girbil-sir.c252
-rw-r--r--drivers/staging/irda/drivers/irda-usb.c1906
-rw-r--r--drivers/staging/irda/drivers/irda-usb.h175
-rw-r--r--drivers/staging/irda/drivers/irtty-sir.c570
-rw-r--r--drivers/staging/irda/drivers/irtty-sir.h34
-rw-r--r--drivers/staging/irda/drivers/kingsun-sir.c634
-rw-r--r--drivers/staging/irda/drivers/ks959-sir.c912
-rw-r--r--drivers/staging/irda/drivers/ksdazzle-sir.c813
-rw-r--r--drivers/staging/irda/drivers/litelink-sir.c199
-rw-r--r--drivers/staging/irda/drivers/ma600-sir.c253
-rw-r--r--drivers/staging/irda/drivers/mcp2120-sir.c224
-rw-r--r--drivers/staging/irda/drivers/mcs7780.c990
-rw-r--r--drivers/staging/irda/drivers/mcs7780.h165
-rw-r--r--drivers/staging/irda/drivers/nsc-ircc.c2410
-rw-r--r--drivers/staging/irda/drivers/nsc-ircc.h281
-rw-r--r--drivers/staging/irda/drivers/old_belkin-sir.c146
-rw-r--r--drivers/staging/irda/drivers/pxaficp_ir.c1075
-rw-r--r--drivers/staging/irda/drivers/sa1100_ir.c1150
-rw-r--r--drivers/staging/irda/drivers/sh_sir.c810
-rw-r--r--drivers/staging/irda/drivers/sir-dev.h191
-rw-r--r--drivers/staging/irda/drivers/sir_dev.c987
-rw-r--r--drivers/staging/irda/drivers/sir_dongle.c133
-rw-r--r--drivers/staging/irda/drivers/smsc-ircc2.c3026
-rw-r--r--drivers/staging/irda/drivers/smsc-ircc2.h191
-rw-r--r--drivers/staging/irda/drivers/smsc-sio.h100
-rw-r--r--drivers/staging/irda/drivers/stir4200.c1134
-rw-r--r--drivers/staging/irda/drivers/tekram-sir.c225
-rw-r--r--drivers/staging/irda/drivers/toim3232-sir.c358
-rw-r--r--drivers/staging/irda/drivers/via-ircc.c1593
-rw-r--r--drivers/staging/irda/drivers/via-ircc.h846
-rw-r--r--drivers/staging/irda/drivers/vlsi_ir.c1872
-rw-r--r--drivers/staging/irda/drivers/vlsi_ir.h757
-rw-r--r--drivers/staging/irda/drivers/w83977af.h53
-rw-r--r--drivers/staging/irda/drivers/w83977af_ir.c1285
-rw-r--r--drivers/staging/irda/drivers/w83977af_ir.h198
-rw-r--r--drivers/staging/irda/include/net/irda/af_irda.h87
-rw-r--r--drivers/staging/irda/include/net/irda/crc.h29
-rw-r--r--drivers/staging/irda/include/net/irda/discovery.h95
-rw-r--r--drivers/staging/irda/include/net/irda/ircomm_core.h106
-rw-r--r--drivers/staging/irda/include/net/irda/ircomm_event.h83
-rw-r--r--drivers/staging/irda/include/net/irda/ircomm_lmp.h36
-rw-r--r--drivers/staging/irda/include/net/irda/ircomm_param.h147
-rw-r--r--drivers/staging/irda/include/net/irda/ircomm_ttp.h37
-rw-r--r--drivers/staging/irda/include/net/irda/ircomm_tty.h121
-rw-r--r--drivers/staging/irda/include/net/irda/ircomm_tty_attach.h92
-rw-r--r--drivers/staging/irda/include/net/irda/irda.h115
-rw-r--r--drivers/staging/irda/include/net/irda/irda_device.h285
-rw-r--r--drivers/staging/irda/include/net/irda/iriap.h108
-rw-r--r--drivers/staging/irda/include/net/irda/iriap_event.h85
-rw-r--r--drivers/staging/irda/include/net/irda/irias_object.h108
-rw-r--r--drivers/staging/irda/include/net/irda/irlan_client.h42
-rw-r--r--drivers/staging/irda/include/net/irda/irlan_common.h230
-rw-r--r--drivers/staging/irda/include/net/irda/irlan_eth.h32
-rw-r--r--drivers/staging/irda/include/net/irda/irlan_event.h81
-rw-r--r--drivers/staging/irda/include/net/irda/irlan_filter.h35
-rw-r--r--drivers/staging/irda/include/net/irda/irlan_provider.h52
-rw-r--r--drivers/staging/irda/include/net/irda/irlap.h311
-rw-r--r--drivers/staging/irda/include/net/irda/irlap_event.h129
-rw-r--r--drivers/staging/irda/include/net/irda/irlap_frame.h167
-rw-r--r--drivers/staging/irda/include/net/irda/irlmp.h295
-rw-r--r--drivers/staging/irda/include/net/irda/irlmp_event.h98
-rw-r--r--drivers/staging/irda/include/net/irda/irlmp_frame.h62
-rw-r--r--drivers/staging/irda/include/net/irda/irmod.h109
-rw-r--r--drivers/staging/irda/include/net/irda/irqueue.h96
-rw-r--r--drivers/staging/irda/include/net/irda/irttp.h210
-rw-r--r--drivers/staging/irda/include/net/irda/parameters.h100
-rw-r--r--drivers/staging/irda/include/net/irda/qos.h101
-rw-r--r--drivers/staging/irda/include/net/irda/timer.h102
-rw-r--r--drivers/staging/irda/include/net/irda/wrapper.h58
-rw-r--r--drivers/staging/irda/net/Kconfig96
-rw-r--r--drivers/staging/irda/net/Makefile17
-rw-r--r--drivers/staging/irda/net/af_irda.c2694
-rw-r--r--drivers/staging/irda/net/discovery.c417
-rw-r--r--drivers/staging/irda/net/ircomm/Kconfig12
-rw-r--r--drivers/staging/irda/net/ircomm/Makefile8
-rw-r--r--drivers/staging/irda/net/ircomm/ircomm_core.c563
-rw-r--r--drivers/staging/irda/net/ircomm/ircomm_event.c246
-rw-r--r--drivers/staging/irda/net/ircomm/ircomm_lmp.c350
-rw-r--r--drivers/staging/irda/net/ircomm/ircomm_param.c501
-rw-r--r--drivers/staging/irda/net/ircomm/ircomm_ttp.c350
-rw-r--r--drivers/staging/irda/net/ircomm/ircomm_tty.c1329
-rw-r--r--drivers/staging/irda/net/ircomm/ircomm_tty_attach.c987
-rw-r--r--drivers/staging/irda/net/ircomm/ircomm_tty_ioctl.c291
-rw-r--r--drivers/staging/irda/net/irda_device.c316
-rw-r--r--drivers/staging/irda/net/iriap.c1085
-rw-r--r--drivers/staging/irda/net/iriap_event.c496
-rw-r--r--drivers/staging/irda/net/irias_object.c555
-rw-r--r--drivers/staging/irda/net/irlan/Kconfig14
-rw-r--r--drivers/staging/irda/net/irlan/Makefile7
-rw-r--r--drivers/staging/irda/net/irlan/irlan_client.c559
-rw-r--r--drivers/staging/irda/net/irlan/irlan_client_event.c511
-rw-r--r--drivers/staging/irda/net/irlan/irlan_common.c1176
-rw-r--r--drivers/staging/irda/net/irlan/irlan_eth.c340
-rw-r--r--drivers/staging/irda/net/irlan/irlan_event.c60
-rw-r--r--drivers/staging/irda/net/irlan/irlan_filter.c240
-rw-r--r--drivers/staging/irda/net/irlan/irlan_provider.c408
-rw-r--r--drivers/staging/irda/net/irlan/irlan_provider_event.c233
-rw-r--r--drivers/staging/irda/net/irlap.c1207
-rw-r--r--drivers/staging/irda/net/irlap_event.c2316
-rw-r--r--drivers/staging/irda/net/irlap_frame.c1407
-rw-r--r--drivers/staging/irda/net/irlmp.c1996
-rw-r--r--drivers/staging/irda/net/irlmp_event.c886
-rw-r--r--drivers/staging/irda/net/irlmp_frame.c476
-rw-r--r--drivers/staging/irda/net/irmod.c199
-rw-r--r--drivers/staging/irda/net/irnet/Kconfig13
-rw-r--r--drivers/staging/irda/net/irnet/Makefile7
-rw-r--r--drivers/staging/irda/net/irnet/irnet.h522
-rw-r--r--drivers/staging/irda/net/irnet/irnet_irda.c1885
-rw-r--r--drivers/staging/irda/net/irnet/irnet_irda.h178
-rw-r--r--drivers/staging/irda/net/irnet/irnet_ppp.c1189
-rw-r--r--drivers/staging/irda/net/irnet/irnet_ppp.h116
-rw-r--r--drivers/staging/irda/net/irnetlink.c162
-rw-r--r--drivers/staging/irda/net/irproc.c96
-rw-r--r--drivers/staging/irda/net/irqueue.c912
-rw-r--r--drivers/staging/irda/net/irsysctl.c258
-rw-r--r--drivers/staging/irda/net/irttp.c1886
-rw-r--r--drivers/staging/irda/net/parameters.c584
-rw-r--r--drivers/staging/irda/net/qos.c771
-rw-r--r--drivers/staging/irda/net/timer.c231
-rw-r--r--drivers/staging/irda/net/wrapper.c492
-rw-r--r--drivers/staging/ks7010/Makefile1
-rw-r--r--drivers/staging/ks7010/eap_packet.h41
-rw-r--r--drivers/staging/ks7010/ks7010_sdio.c197
-rw-r--r--drivers/staging/ks7010/ks_hostif.c756
-rw-r--r--drivers/staging/ks7010/ks_hostif.h95
-rw-r--r--drivers/staging/ks7010/ks_wlan.h21
-rw-r--r--drivers/staging/ks7010/ks_wlan_ioctl.h1
-rw-r--r--drivers/staging/ks7010/ks_wlan_net.c117
-rw-r--r--drivers/staging/ks7010/michael_mic.c144
-rw-r--r--drivers/staging/ks7010/michael_mic.h19
-rw-r--r--drivers/staging/lustre/TODO310
-rw-r--r--drivers/staging/lustre/include/linux/libcfs/curproc.h37
-rw-r--r--drivers/staging/lustre/include/linux/libcfs/libcfs.h27
-rw-r--r--drivers/staging/lustre/include/linux/libcfs/libcfs_crypto.h11
-rw-r--r--drivers/staging/lustre/include/linux/libcfs/libcfs_debug.h4
-rw-r--r--drivers/staging/lustre/include/linux/libcfs/libcfs_time.h2
-rw-r--r--drivers/staging/lustre/include/linux/libcfs/linux/linux-time.h7
-rw-r--r--drivers/staging/lustre/include/linux/lnet/api.h1
-rw-r--r--drivers/staging/lustre/lnet/Kconfig2
-rw-r--r--drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.c8
-rw-r--r--drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd_cb.c10
-rw-r--r--drivers/staging/lustre/lnet/klnds/socklnd/socklnd.c6
-rw-r--r--drivers/staging/lustre/lnet/klnds/socklnd/socklnd.h11
-rw-r--r--drivers/staging/lustre/lnet/klnds/socklnd/socklnd_cb.c32
-rw-r--r--drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib.c2
-rw-r--r--drivers/staging/lustre/lnet/libcfs/Makefile4
-rw-r--r--drivers/staging/lustre/lnet/libcfs/debug.c4
-rw-r--r--drivers/staging/lustre/lnet/libcfs/fail.c2
-rw-r--r--drivers/staging/lustre/lnet/libcfs/linux/linux-cpu.c2
-rw-r--r--drivers/staging/lustre/lnet/libcfs/linux/linux-crypto.c29
-rw-r--r--drivers/staging/lustre/lnet/libcfs/linux/linux-curproc.c108
-rw-r--r--drivers/staging/lustre/lnet/libcfs/linux/linux-mem.c51
-rw-r--r--drivers/staging/lustre/lnet/libcfs/linux/linux-prim.c113
-rw-r--r--drivers/staging/lustre/lnet/libcfs/tracefile.c4
-rw-r--r--drivers/staging/lustre/lnet/lnet/acceptor.c6
-rw-r--r--drivers/staging/lustre/lnet/lnet/api-ni.c26
-rw-r--r--drivers/staging/lustre/lnet/lnet/lib-eq.c10
-rw-r--r--drivers/staging/lustre/lnet/lnet/lib-move.c4
-rw-r--r--drivers/staging/lustre/lnet/lnet/lib-ptl.c5
-rw-r--r--drivers/staging/lustre/lnet/lnet/lib-socket.c7
-rw-r--r--drivers/staging/lustre/lnet/lnet/net_fault.c14
-rw-r--r--drivers/staging/lustre/lnet/lnet/peer.c2
-rw-r--r--drivers/staging/lustre/lnet/lnet/router.c10
-rw-r--r--drivers/staging/lustre/lnet/selftest/conctl.c14
-rw-r--r--drivers/staging/lustre/lnet/selftest/conrpc.c4
-rw-r--r--drivers/staging/lustre/lnet/selftest/framework.c2
-rw-r--r--drivers/staging/lustre/lnet/selftest/rpc.c2
-rw-r--r--drivers/staging/lustre/lnet/selftest/selftest.h2
-rw-r--r--drivers/staging/lustre/lnet/selftest/timer.c4
-rw-r--r--drivers/staging/lustre/lustre/Kconfig1
-rw-r--r--drivers/staging/lustre/lustre/fid/fid_request.c107
-rw-r--r--drivers/staging/lustre/lustre/fid/lproc_fid.c44
-rw-r--r--drivers/staging/lustre/lustre/fld/fld_cache.c2
-rw-r--r--drivers/staging/lustre/lustre/include/cl_object.h2
-rw-r--r--drivers/staging/lustre/lustre/include/lprocfs_status.h2
-rw-r--r--drivers/staging/lustre/lustre/include/lu_object.h7
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_dlm.h2
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_export.h2
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_fid.h2
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_import.h4
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_lib.h296
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_lmv.h2
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_mdc.h2
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_net.h22
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_sec.h3
-rw-r--r--drivers/staging/lustre/lustre/include/obd.h2
-rw-r--r--drivers/staging/lustre/lustre/include/obd_class.h8
-rw-r--r--drivers/staging/lustre/lustre/include/obd_support.h2
-rw-r--r--drivers/staging/lustre/lustre/ldlm/ldlm_flock.c30
-rw-r--r--drivers/staging/lustre/lustre/ldlm/ldlm_lock.c14
-rw-r--r--drivers/staging/lustre/lustre/ldlm/ldlm_lockd.c24
-rw-r--r--drivers/staging/lustre/lustre/ldlm/ldlm_pool.c104
-rw-r--r--drivers/staging/lustre/lustre/ldlm/ldlm_request.c53
-rw-r--r--drivers/staging/lustre/lustre/ldlm/ldlm_resource.c14
-rw-r--r--drivers/staging/lustre/lustre/llite/dcache.c16
-rw-r--r--drivers/staging/lustre/lustre/llite/dir.c10
-rw-r--r--drivers/staging/lustre/lustre/llite/file.c12
-rw-r--r--drivers/staging/lustre/lustre/llite/glimpse.c2
-rw-r--r--drivers/staging/lustre/lustre/llite/llite_internal.h4
-rw-r--r--drivers/staging/lustre/lustre/llite/llite_lib.c60
-rw-r--r--drivers/staging/lustre/lustre/llite/llite_mmap.c8
-rw-r--r--drivers/staging/lustre/lustre/llite/namei.c60
-rw-r--r--drivers/staging/lustre/lustre/llite/statahead.c215
-rw-r--r--drivers/staging/lustre/lustre/llite/super25.c17
-rw-r--r--drivers/staging/lustre/lustre/llite/xattr.c21
-rw-r--r--drivers/staging/lustre/lustre/lmv/lmv_obd.c6
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_ea.c2
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_io.c4
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_lock.c2
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_object.c22
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_pack.c2
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_request.c12
-rw-r--r--drivers/staging/lustre/lustre/mdc/mdc_locks.c2
-rw-r--r--drivers/staging/lustre/lustre/mdc/mdc_request.c24
-rw-r--r--drivers/staging/lustre/lustre/mgc/mgc_request.c19
-rw-r--r--drivers/staging/lustre/lustre/obdclass/cl_io.c23
-rw-r--r--drivers/staging/lustre/lustre/obdclass/cl_lock.c2
-rw-r--r--drivers/staging/lustre/lustre/obdclass/cl_object.c2
-rw-r--r--drivers/staging/lustre/lustre/obdclass/genops.c211
-rw-r--r--drivers/staging/lustre/lustre/obdclass/linkea.c16
-rw-r--r--drivers/staging/lustre/lustre/obdclass/linux/linux-module.c4
-rw-r--r--drivers/staging/lustre/lustre/obdclass/llog.c22
-rw-r--r--drivers/staging/lustre/lustre/obdclass/llog_obd.c5
-rw-r--r--drivers/staging/lustre/lustre/obdclass/lprocfs_status.c4
-rw-r--r--drivers/staging/lustre/lustre/obdclass/lu_object.c89
-rw-r--r--drivers/staging/lustre/lustre/obdclass/lustre_handles.c4
-rw-r--r--drivers/staging/lustre/lustre/obdclass/obd_config.c2
-rw-r--r--drivers/staging/lustre/lustre/obdclass/obd_mount.c2
-rw-r--r--drivers/staging/lustre/lustre/obdecho/echo_client.c10
-rw-r--r--drivers/staging/lustre/lustre/osc/osc_cache.c34
-rw-r--r--drivers/staging/lustre/lustre/osc/osc_object.c6
-rw-r--r--drivers/staging/lustre/lustre/osc/osc_page.c8
-rw-r--r--drivers/staging/lustre/lustre/osc/osc_request.c8
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/client.c194
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/events.c7
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/import.c85
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/layout.c2
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/niobuf.c15
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/pack_generic.c9
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/pinger.c99
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/ptlrpc_internal.h2
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/ptlrpc_module.c56
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/ptlrpcd.c33
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/recover.c38
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/sec.c44
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/sec_bulk.c6
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/sec_gc.c99
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/sec_null.c8
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/sec_plain.c8
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/service.c111
-rw-r--r--drivers/staging/media/Kconfig6
-rw-r--r--drivers/staging/media/Makefile3
-rw-r--r--drivers/staging/media/atomisp/i2c/Kconfig12
-rw-r--r--drivers/staging/media/atomisp/i2c/atomisp-gc0310.c55
-rw-r--r--drivers/staging/media/atomisp/i2c/atomisp-gc2235.c53
-rw-r--r--drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c31
-rw-r--r--drivers/staging/media/atomisp/i2c/atomisp-ov2680.c74
-rw-r--r--drivers/staging/media/atomisp/i2c/atomisp-ov2722.c71
-rw-r--r--drivers/staging/media/atomisp/i2c/gc0310.h46
-rw-r--r--drivers/staging/media/atomisp/i2c/gc2235.h7
-rw-r--r--drivers/staging/media/atomisp/i2c/ov2680.h68
-rw-r--r--drivers/staging/media/atomisp/i2c/ov2722.h8
-rw-r--r--drivers/staging/media/atomisp/i2c/ov5693/atomisp-ov5693.c84
-rw-r--r--drivers/staging/media/atomisp/i2c/ov5693/ov5693.h8
-rw-r--r--drivers/staging/media/atomisp/i2c/ov8858.c2169
-rw-r--r--drivers/staging/media/atomisp/i2c/ov8858.h1474
-rw-r--r--drivers/staging/media/atomisp/i2c/ov8858_btns.h1276
-rw-r--r--drivers/staging/media/atomisp/include/linux/atomisp_platform.h4
-rw-r--r--drivers/staging/media/atomisp/include/linux/vlv2_plat_clock.h30
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/Makefile14
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/atomisp_cmd.c53
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/atomisp_cmd.h2
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/atomisp_compat.h20
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/atomisp_compat_css20.c60
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/atomisp_compat_css20.h3
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/atomisp_drvfs.c1
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/atomisp_file.c16
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/atomisp_fops.c20
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/atomisp_ioctl.c10
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/atomisp_subdev.c26
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/atomisp_subdev.h8
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/atomisp_tpg.c14
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/atomisp_v4l2.c1
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/camera/util/interface/ia_css_util.h6
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/camera/util/src/util.c72
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/css_2400_system/hrt/gp_regs_defs.h22
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/css_2400_system/hrt/sp_hrt.h24
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/css_2401_csi2p_system/hrt/gp_regs_defs.h22
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/css_2401_csi2p_system/hrt/sp_hrt.h24
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/css_2401_csi2p_system/system_global.h4
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/css_2401_system/hrt/gp_regs_defs.h22
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/css_2401_system/hrt/sp_hrt.h24
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/css_api_version.h673
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/debug.c2
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/gp_timer.c2
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/hive_isp_css_ddr_hrt_modified.h148
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/hive_isp_css_hrt_modified.h79
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/input_formatter.c5
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/input_system.c24
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/input_system_local.h2
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/input_system_private.h4
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/input_formatter_global.h16
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/resource_global.h35
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/system_global.h4
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/xmem_global.h20
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/bamem.h46
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/bbb_config.h27
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/cpu_mem_support.h59
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/input_system_public.h14
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp2400_config.h24
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp2500_config.h29
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp2600_config.h34
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp2601_config.h70
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp_config.h24
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp_op1w.h844
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp_op1w_types.h54
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp_op2w.h674
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp_op2w_types.h49
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp_op_count.h226
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/osys_public.h20
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/pipeline_public.h18
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/ref_vector_func.h1221
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/ref_vector_func_types.h385
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/mpmath.h329
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/osys.h47
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/stream_buffer.h47
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/vector_func.h38
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/vector_ops.h31
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/xmem.h46
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_shared/socket_global.h53
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_shared/stream_buffer_global.h26
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_frame_public.h29
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_input_port.h20
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_irq.h4
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_metadata.h4
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_mipi.h4
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_pipe.h113
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_pipe_public.h110
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_stream_format.h71
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_stream_public.h8
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_types.h64
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/aa/aa_2/ia_css_aa2_state.h41
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/bayer_ls/bayer_ls_1.0/ia_css_bayer_load_param.h20
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/bayer_ls/bayer_ls_1.0/ia_css_bayer_ls_param.h42
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/bayer_ls/bayer_ls_1.0/ia_css_bayer_store_param.h21
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/bnlm/ia_css_bnlm.host.h1
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/bnlm/ia_css_bnlm_default.host.c71
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/bnlm/ia_css_bnlm_default.host.h22
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/bnlm/ia_css_bnlm_state.h31
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/cnr/cnr_1.0/ia_css_cnr_state.h33
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/cnr/cnr_2/ia_css_cnr_state.h33
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/dp/dp_1.0/ia_css_dp_state.h36
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/dpc2/ia_css_dpc2.host.h1
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/dpc2/ia_css_dpc2_default.host.c26
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/dpc2/ia_css_dpc2_default.host.h23
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/dpc2/ia_css_dpc2_state.h30
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/eed1_8/ia_css_eed1_8.host.h1
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/eed1_8/ia_css_eed1_8_default.host.c94
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/eed1_8/ia_css_eed1_8_default.host.h22
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/eed1_8/ia_css_eed1_8_state.h40
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/io_ls/plane_io_ls/ia_css_plane_io_param.h22
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/io_ls/plane_io_ls/ia_css_plane_io_types.h30
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/io_ls/yuv420_io_ls/ia_css_yuv420_io_param.h22
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/io_ls/yuv420_io_ls/ia_css_yuv420_io_types.h22
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/ipu2_io_ls/plane_io_ls/ia_css_plane_io_param.h22
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/ipu2_io_ls/plane_io_ls/ia_css_plane_io_types.h30
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/ipu2_io_ls/yuv420_io_ls/ia_css_yuv420_io_param.h22
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/ipu2_io_ls/yuv420_io_ls/ia_css_yuv420_io_types.h22
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/norm/norm_1.0/ia_css_norm_types.h21
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/output/output_1.0/ia_css_output.host.c2
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/raw/raw_1.0/ia_css_raw.host.c42
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/raw/raw_1.0/ia_css_raw_types.h2
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/s3a/s3a_1.0/ia_css_s3a_types.h50
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/s3a_stat_ls/ia_css_s3a_stat_ls_param.h45
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/s3a_stat_ls/ia_css_s3a_stat_store_param.h21
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/scale/scale_1.0/ia_css_scale_param.h20
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/common/ia_css_sdis_common_types.h31
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/common/ia_css_sdis_param.h22
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c3
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_1.0/ia_css_sdis_param.h21
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_2/ia_css_sdis_param.h21
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/tdf/tdf_1.0/ia_css_tdf.host.c2
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/tdf/tdf_1.0/ia_css_tdf.host.h1
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/tdf/tdf_1.0/ia_css_tdf_default.host.c36
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/tdf/tdf_1.0/ia_css_tdf_default.host.h23
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/vf/vf_1.0/ia_css_vf.host.c10
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/xnr/xnr_3.0/ia_css_xnr3_wrapper_param.h20
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/yuv_ls/yuv_ls_1.0/ia_css_yuv_load_param.h20
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/yuv_ls/yuv_ls_1.0/ia_css_yuv_ls_param.h39
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/yuv_ls/yuv_ls_1.0/ia_css_yuv_store_param.h21
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/modes/interface/isp_exprs.h286
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/binary/interface/ia_css_binary.h94
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/binary/src/binary.c9
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/bufq/src/bufq.c9
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/src/ia_css_debug.c119
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/frame/src/frame.c2
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/ifmtr/src/ifmtr.c96
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/inputfifo/interface/ia_css_inputfifo.h6
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/inputfifo/src/inputfifo.c22
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isp_param/interface/ia_css_isp_param_types.h9
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isys/interface/ia_css_isys.h20
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isys/src/csi_rx_rmgr.c4
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isys/src/isys_init.c4
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isys/src/rx.c146
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isys/src/virtual_isys.c12
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/pipeline/interface/ia_css_pipeline.h24
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/pipeline/src/pipeline.c13
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/rmgr/src/rmgr_vbuf.c8
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c243
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_legacy.h11
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_metrics.h21
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_mipi.c58
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_params.c9
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_sp.c25
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_sp.h2
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_stream_format.c58
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_stream_format.h2
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/include/hmm/hmm_bo_dev.h126
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/include/mmu/isp_mmu.h4
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/include/mmu/sh_mmu.h72
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/mmu/isp_mmu.c15
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp2/mmu/sh_mmu_mrfld.c16
-rw-r--r--drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c8
-rw-r--r--drivers/staging/media/cxd2099/Kconfig12
-rw-r--r--drivers/staging/media/cxd2099/Makefile4
-rw-r--r--drivers/staging/media/cxd2099/TODO12
-rw-r--r--drivers/staging/media/cxd2099/cxd2099.c705
-rw-r--r--drivers/staging/media/cxd2099/cxd2099.h45
-rw-r--r--drivers/staging/media/davinci_vpfe/dm365_resizer.c4
-rw-r--r--drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c2
-rw-r--r--drivers/staging/media/imx/Kconfig4
-rw-r--r--drivers/staging/media/imx/imx-ic-prp.c1
-rw-r--r--drivers/staging/media/imx/imx-ic-prpencvf.c3
-rw-r--r--drivers/staging/media/imx/imx-media-capture.c3
-rw-r--r--drivers/staging/media/imx/imx-media-csi.c14
-rw-r--r--drivers/staging/media/imx/imx-media-internal-sd.c2
-rw-r--r--drivers/staging/media/imx/imx-media-utils.c118
-rw-r--r--drivers/staging/media/imx/imx-media-vdic.c3
-rw-r--r--drivers/staging/media/imx/imx-media.h2
-rw-r--r--drivers/staging/media/imx/imx6-mipi-csi2.c25
-rw-r--r--drivers/staging/media/imx074/Kconfig5
-rw-r--r--drivers/staging/media/imx074/Makefile1
-rw-r--r--drivers/staging/media/imx074/TODO5
-rw-r--r--drivers/staging/media/imx074/imx074.c (renamed from drivers/media/i2c/soc_camera/imx074.c)0
-rw-r--r--drivers/staging/media/mt9t031/Kconfig11
-rw-r--r--drivers/staging/media/mt9t031/Makefile1
-rw-r--r--drivers/staging/media/mt9t031/TODO5
-rw-r--r--drivers/staging/media/mt9t031/mt9t031.c (renamed from drivers/media/i2c/soc_camera/mt9t031.c)0
-rw-r--r--drivers/staging/most/core.c9
-rw-r--r--drivers/staging/most/core.h2
-rw-r--r--drivers/staging/mt29f_spinand/mt29f_spinand.c7
-rw-r--r--drivers/staging/mt7621-dma/Kconfig12
-rw-r--r--drivers/staging/mt7621-dma/Makefile4
-rw-r--r--drivers/staging/mt7621-dma/TODO5
-rw-r--r--drivers/staging/mt7621-dma/mtk-hsdma.c768
-rw-r--r--drivers/staging/mt7621-dma/ralink-gdma.c930
-rw-r--r--drivers/staging/mt7621-dts/Kconfig5
-rw-r--r--drivers/staging/mt7621-dts/Makefile3
-rw-r--r--drivers/staging/mt7621-dts/TODO5
-rw-r--r--drivers/staging/mt7621-dts/gbpc1.dts143
-rw-r--r--drivers/staging/mt7621-dts/mt7621.dtsi471
-rw-r--r--drivers/staging/mt7621-eth/Documentation/devicetree/bindings/net/mediatek-net-gsw.txt48
-rw-r--r--drivers/staging/mt7621-eth/Kconfig39
-rw-r--r--drivers/staging/mt7621-eth/Makefile14
-rw-r--r--drivers/staging/mt7621-eth/TODO13
-rw-r--r--drivers/staging/mt7621-eth/ethtool.c225
-rw-r--r--drivers/staging/mt7621-eth/ethtool.h22
-rw-r--r--drivers/staging/mt7621-eth/gsw_mt7620.h277
-rw-r--r--drivers/staging/mt7621-eth/gsw_mt7621.c298
-rw-r--r--drivers/staging/mt7621-eth/mdio.c271
-rw-r--r--drivers/staging/mt7621-eth/mdio.h27
-rw-r--r--drivers/staging/mt7621-eth/mdio_mt7620.c173
-rw-r--r--drivers/staging/mt7621-eth/mtk_eth_soc.c2178
-rw-r--r--drivers/staging/mt7621-eth/mtk_eth_soc.h721
-rw-r--r--drivers/staging/mt7621-eth/soc_mt7621.c160
-rw-r--r--drivers/staging/mt7621-gpio/Kconfig6
-rw-r--r--drivers/staging/mt7621-gpio/Makefile3
-rw-r--r--drivers/staging/mt7621-gpio/TODO5
-rw-r--r--drivers/staging/mt7621-gpio/gpio-mt7621.c352
-rw-r--r--drivers/staging/mt7621-mmc/Kconfig16
-rw-r--r--drivers/staging/mt7621-mmc/Makefile42
-rw-r--r--drivers/staging/mt7621-mmc/TODO8
-rw-r--r--drivers/staging/mt7621-mmc/board.h137
-rw-r--r--drivers/staging/mt7621-mmc/dbg.c347
-rw-r--r--drivers/staging/mt7621-mmc/dbg.h156
-rw-r--r--drivers/staging/mt7621-mmc/mt6575_sd.h1001
-rw-r--r--drivers/staging/mt7621-mmc/sd.c3074
-rw-r--r--drivers/staging/mt7621-pci/Makefile1
-rw-r--r--drivers/staging/mt7621-pci/TODO12
-rw-r--r--drivers/staging/mt7621-pci/pci-mt7621.c840
-rw-r--r--drivers/staging/mt7621-pinctrl/Kconfig4
-rw-r--r--drivers/staging/mt7621-pinctrl/Makefile3
-rw-r--r--drivers/staging/mt7621-pinctrl/TODO6
-rw-r--r--drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c472
-rw-r--r--drivers/staging/mt7621-spi/Kconfig6
-rw-r--r--drivers/staging/mt7621-spi/Makefile1
-rw-r--r--drivers/staging/mt7621-spi/TODO5
-rw-r--r--drivers/staging/mt7621-spi/spi-mt7621.c489
-rw-r--r--drivers/staging/netlogic/xlr_net.c3
-rw-r--r--drivers/staging/pi433/Documentation/pi433.txt28
-rw-r--r--drivers/staging/pi433/pi433_if.c87
-rw-r--r--drivers/staging/pi433/pi433_if.h12
-rw-r--r--drivers/staging/pi433/rf69.c178
-rw-r--r--drivers/staging/pi433/rf69.h28
-rw-r--r--drivers/staging/pi433/rf69_enum.h52
-rw-r--r--drivers/staging/rtl8188eu/Kconfig4
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_ieee80211.c16
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_ioctl_set.c14
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_mlme.c14
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_mlme_ext.c72
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_recv.c227
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_security.c450
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_sta_mgt.c4
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_wlan_util.c6
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_xmit.c125
-rw-r--r--drivers/staging/rtl8188eu/hal/fw.c2
-rw-r--r--drivers/staging/rtl8188eu/hal/rtl8188e_cmd.c5
-rw-r--r--drivers/staging/rtl8188eu/hal/rtl8188e_dm.c2
-rw-r--r--drivers/staging/rtl8188eu/hal/rtl8188e_hal_init.c8
-rw-r--r--drivers/staging/rtl8188eu/hal/rtl8188eu_xmit.c2
-rw-r--r--drivers/staging/rtl8188eu/hal/usb_halinit.c2
-rw-r--r--drivers/staging/rtl8188eu/include/rtw_security.h2
-rw-r--r--drivers/staging/rtl8188eu/include/xmit_osdep.h13
-rw-r--r--drivers/staging/rtl8188eu/os_dep/usb_intf.c2
-rw-r--r--drivers/staging/rtl8188eu/os_dep/xmit_linux.c37
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_wx.c51
-rw-r--r--drivers/staging/rtl8192e/rtl819x_BAProc.c2
-rw-r--r--drivers/staging/rtl8192e/rtllib_rx.c4
-rw-r--r--drivers/staging/rtl8192e/rtllib_wx.c3
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_crypt_ccmp.c22
-rw-r--r--drivers/staging/rtl8192u/ieee80211/rtl819x_BAProc.c7
-rw-r--r--drivers/staging/rtl8192u/r8192U_core.c2
-rw-r--r--drivers/staging/rtl8712/drv_types.h2
-rw-r--r--drivers/staging/rtl8712/ieee80211.c11
-rw-r--r--drivers/staging/rtl8712/ieee80211.h6
-rw-r--r--drivers/staging/rtl8712/mlme_linux.c2
-rw-r--r--drivers/staging/rtl8712/os_intfs.c3
-rw-r--r--drivers/staging/rtl8712/recv_linux.c4
-rw-r--r--drivers/staging/rtl8712/rtl8712_bitdef.h1
-rw-r--r--drivers/staging/rtl8712/rtl8712_cmd.c8
-rw-r--r--drivers/staging/rtl8712/rtl8712_cmd.h4
-rw-r--r--drivers/staging/rtl8712/rtl8712_cmdctrl_bitdef.h2
-rw-r--r--drivers/staging/rtl8712/rtl8712_cmdctrl_regdef.h2
-rw-r--r--drivers/staging/rtl8712/rtl8712_debugctrl_bitdef.h1
-rw-r--r--drivers/staging/rtl8712/rtl8712_debugctrl_regdef.h2
-rw-r--r--drivers/staging/rtl8712/rtl8712_fifoctrl_bitdef.h1
-rw-r--r--drivers/staging/rtl8712/rtl8712_fifoctrl_regdef.h2
-rw-r--r--drivers/staging/rtl8712/rtl8712_gp_bitdef.h2
-rw-r--r--drivers/staging/rtl8712/rtl8712_gp_regdef.h1
-rw-r--r--drivers/staging/rtl8712/rtl8712_interrupt_bitdef.h1
-rw-r--r--drivers/staging/rtl8712/rtl8712_led.c70
-rw-r--r--drivers/staging/rtl8712/rtl871x_mlme.c3
-rw-r--r--drivers/staging/rtl8712/rtl871x_xmit.c2
-rw-r--r--drivers/staging/rtl8723bs/core/rtw_cmd.c2
-rw-r--r--drivers/staging/rtl8723bs/core/rtw_ieee80211.c4
-rw-r--r--drivers/staging/rtl8723bs/core/rtw_mlme.c3
-rw-r--r--drivers/staging/rtl8723bs/core/rtw_mlme_ext.c8
-rw-r--r--drivers/staging/rtl8723bs/core/rtw_pwrctrl.c2
-rw-r--r--drivers/staging/rtl8723bs/core/rtw_recv.c2
-rw-r--r--drivers/staging/rtl8723bs/hal/hal_com.c10
-rw-r--r--drivers/staging/rtl8723bs/hal/odm.h90
-rw-r--r--drivers/staging/rtl8723bs/hal/odm_CfoTracking.c4
-rw-r--r--drivers/staging/rtl8723bs/hal/odm_HWConfig.c122
-rw-r--r--drivers/staging/rtl8723bs/hal/odm_HWConfig.h4
-rw-r--r--drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c3
-rw-r--r--drivers/staging/rtl8723bs/hal/rtl8723b_rf6052.c2
-rw-r--r--drivers/staging/rtl8723bs/hal/rtl8723bs_recv.c205
-rw-r--r--drivers/staging/rtl8723bs/include/drv_types.h4
-rw-r--r--drivers/staging/rtl8723bs/include/ieee80211.h154
-rw-r--r--drivers/staging/rtl8723bs/include/rtw_mlme.h2
-rw-r--r--drivers/staging/rtl8723bs/include/rtw_recv.h10
-rw-r--r--drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c13
-rw-r--r--drivers/staging/rtl8723bs/os_dep/ioctl_linux.c99
-rw-r--r--drivers/staging/rtl8723bs/os_dep/recv_linux.c7
-rw-r--r--drivers/staging/rtl8723bs/os_dep/rtw_proc.c18
-rw-r--r--drivers/staging/rtl8723bs/os_dep/sdio_intf.c2
-rw-r--r--drivers/staging/rtl8723bs/os_dep/xmit_linux.c24
-rw-r--r--drivers/staging/rtlwifi/base.c6
-rw-r--r--drivers/staging/rtlwifi/btcoexist/halbtc8822b2ant.c8
-rw-r--r--drivers/staging/rtlwifi/pci.c1
-rw-r--r--drivers/staging/rtlwifi/phydm/phydm_features.h2
-rw-r--r--drivers/staging/rtlwifi/phydm/phydm_kfree.h2
-rw-r--r--drivers/staging/rtlwifi/phydm/phydm_rainfo.c1
-rw-r--r--drivers/staging/rtlwifi/phydm/rtl8822b/phydm_iqk_8822b.c2
-rw-r--r--drivers/staging/rtlwifi/rtl8822be/phy.c2
-rw-r--r--drivers/staging/rtlwifi/wifi.h29
-rw-r--r--drivers/staging/rts5208/rtsx_chip.h12
-rw-r--r--drivers/staging/rts5208/rtsx_transport.c10
-rw-r--r--drivers/staging/sm750fb/ddk750_chip.c4
-rw-r--r--drivers/staging/sm750fb/ddk750_chip.h14
-rw-r--r--drivers/staging/sm750fb/ddk750_display.c2
-rw-r--r--drivers/staging/sm750fb/ddk750_display.h7
-rw-r--r--drivers/staging/sm750fb/ddk750_mode.c2
-rw-r--r--drivers/staging/sm750fb/ddk750_mode.h2
-rw-r--r--drivers/staging/sm750fb/sm750_hw.c4
-rw-r--r--drivers/staging/speakup/Kconfig2
-rw-r--r--drivers/staging/speakup/main.c8
-rw-r--r--drivers/staging/speakup/speakup_decpc.c6
-rw-r--r--drivers/staging/speakup/speakup_dectlk.c4
-rw-r--r--drivers/staging/speakup/speakup_dtlk.c25
-rw-r--r--drivers/staging/speakup/speakup_dummy.c2
-rw-r--r--drivers/staging/speakup/speakup_keypc.c4
-rw-r--r--drivers/staging/speakup/spk_priv.h1
-rw-r--r--drivers/staging/speakup/spk_ttyio.c21
-rw-r--r--drivers/staging/speakup/spk_types.h1
-rw-r--r--drivers/staging/speakup/synth.c25
-rw-r--r--drivers/staging/typec/tcpci.c127
-rw-r--r--drivers/staging/typec/tcpci.h14
-rw-r--r--drivers/staging/unisys/visorinput/Kconfig2
-rw-r--r--drivers/staging/unisys/visorinput/Makefile1
-rw-r--r--drivers/staging/unisys/visorinput/ultrainputreport.h57
-rw-r--r--drivers/staging/unisys/visorinput/visorinput.c169
-rw-r--r--drivers/staging/vc04_services/Makefile1
-rw-r--r--drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c6
-rw-r--r--drivers/staging/vc04_services/bcm2835-audio/bcm2835.c54
-rw-r--r--drivers/staging/vc04_services/bcm2835-camera/controls.c1
-rw-r--r--drivers/staging/vc04_services/interface/vchi/TODO45
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c20
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c352
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_build_info.h37
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_if.h10
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c431
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_memdrv.h59
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_pagelist.h6
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_version.c59
-rw-r--r--drivers/staging/vme/devices/vme_user.c4
-rw-r--r--drivers/staging/vt6655/baseband.h11
-rw-r--r--drivers/staging/vt6655/card.h13
-rw-r--r--drivers/staging/vt6655/device_cfg.h11
-rw-r--r--drivers/staging/vt6655/device_main.c6
-rw-r--r--drivers/staging/vt6655/rxtx.c12
-rw-r--r--drivers/staging/vt6656/usbpipe.c3
-rw-r--r--drivers/staging/wilc1000/coreconfigurator.c32
-rw-r--r--drivers/staging/wilc1000/host_interface.c2247
-rw-r--r--drivers/staging/wilc1000/host_interface.h2
-rw-r--r--drivers/staging/wilc1000/linux_mon.c12
-rw-r--r--drivers/staging/wilc1000/linux_wlan.c22
-rw-r--r--drivers/staging/wilc1000/wilc_sdio.c145
-rw-r--r--drivers/staging/wilc1000/wilc_spi.c551
-rw-r--r--drivers/staging/wilc1000/wilc_wfi_cfgoperations.c351
-rw-r--r--drivers/staging/wilc1000/wilc_wfi_netdevice.h2
-rw-r--r--drivers/staging/wilc1000/wilc_wlan.c501
-rw-r--r--drivers/staging/wilc1000/wilc_wlan.h5
-rw-r--r--drivers/staging/wilc1000/wilc_wlan_cfg.c21
-rw-r--r--drivers/staging/wlan-ng/prism2mgmt.c2
-rw-r--r--drivers/staging/xgifb/XGI_main_26.c59
-rw-r--r--drivers/target/iscsi/iscsi_target.c28
-rw-r--r--drivers/target/iscsi/iscsi_target_login.c18
-rw-r--r--drivers/target/loopback/tcm_loop.c2
-rw-r--r--drivers/target/target_core_pscsi.c2
-rw-r--r--drivers/tee/optee/core.c23
-rw-r--r--drivers/tee/optee/optee_smc.h10
-rw-r--r--drivers/tee/tee_core.c14
-rw-r--r--drivers/thermal/Kconfig7
-rw-r--r--drivers/thermal/imx_thermal.c6
-rw-r--r--drivers/thermal/thermal_core.c3
-rw-r--r--drivers/thermal/thermal_core.h10
-rw-r--r--drivers/thermal/thermal_helpers.c5
-rw-r--r--drivers/thermal/thermal_sysfs.c225
-rw-r--r--drivers/thunderbolt/dma_port.c28
-rw-r--r--drivers/thunderbolt/domain.c130
-rw-r--r--drivers/thunderbolt/icm.c764
-rw-r--r--drivers/thunderbolt/nhi.c5
-rw-r--r--drivers/thunderbolt/nhi.h5
-rw-r--r--drivers/thunderbolt/switch.c61
-rw-r--r--drivers/thunderbolt/tb.h14
-rw-r--r--drivers/thunderbolt/tb_msgs.h180
-rw-r--r--drivers/thunderbolt/xdomain.c47
-rw-r--r--drivers/tty/Kconfig28
-rw-r--r--drivers/tty/Makefile2
-rw-r--r--drivers/tty/bfin_jtag_comm.c353
-rw-r--r--drivers/tty/hvc/Kconfig13
-rw-r--r--drivers/tty/hvc/Makefile3
-rw-r--r--drivers/tty/hvc/hvc_bfin_jtag.c104
-rw-r--r--drivers/tty/hvc/hvc_riscv_sbi.c60
-rw-r--r--drivers/tty/hvc/hvc_tile.c196
-rw-r--r--drivers/tty/metag_da.c665
-rw-r--r--drivers/tty/n_gsm.c23
-rw-r--r--drivers/tty/serdev/core.c2
-rw-r--r--drivers/tty/serial/8250/8250_dw.c34
-rw-r--r--drivers/tty/serial/8250/8250_men_mcb.c125
-rw-r--r--drivers/tty/serial/8250/8250_of.c1
-rw-r--r--drivers/tty/serial/8250/8250_omap.c11
-rw-r--r--drivers/tty/serial/8250/8250_pci.c3
-rw-r--r--drivers/tty/serial/8250/8250_port.c36
-rw-r--r--drivers/tty/serial/8250/Kconfig5
-rw-r--r--drivers/tty/serial/Kconfig213
-rw-r--r--drivers/tty/serial/Makefile7
-rw-r--r--drivers/tty/serial/altera_uart.c58
-rw-r--r--drivers/tty/serial/arc_uart.c5
-rw-r--r--drivers/tty/serial/bfin_sport_uart.c937
-rw-r--r--drivers/tty/serial/bfin_sport_uart.h86
-rw-r--r--drivers/tty/serial/bfin_uart.c1551
-rw-r--r--drivers/tty/serial/crisv10.c4248
-rw-r--r--drivers/tty/serial/crisv10.h133
-rw-r--r--drivers/tty/serial/earlycon.c6
-rw-r--r--drivers/tty/serial/etraxfs-uart.c960
-rw-r--r--drivers/tty/serial/fsl_lpuart.c4
-rw-r--r--drivers/tty/serial/imx.c1226
-rw-r--r--drivers/tty/serial/m32r_sio.c1053
-rw-r--r--drivers/tty/serial/m32r_sio_reg.h150
-rw-r--r--drivers/tty/serial/max310x.c2
-rw-r--r--drivers/tty/serial/mvebu-uart.c5
-rw-r--r--drivers/tty/serial/mxs-auart.c40
-rw-r--r--drivers/tty/serial/pxa.c4
-rw-r--r--drivers/tty/serial/qcom_geni_serial.c1160
-rw-r--r--drivers/tty/serial/samsung.c4
-rw-r--r--drivers/tty/serial/serial_core.c2
-rw-r--r--drivers/tty/serial/sh-sci.c51
-rw-r--r--drivers/tty/serial/sirfsoc_uart.c5
-rw-r--r--drivers/tty/serial/st-asc.c4
-rw-r--r--drivers/tty/serial/stm32-usart.c134
-rw-r--r--drivers/tty/serial/stm32-usart.h3
-rw-r--r--drivers/tty/serial/tilegx.c689
-rw-r--r--drivers/tty/serial/xilinx_uartps.c4
-rw-r--r--drivers/tty/sysrq.c2
-rw-r--r--drivers/tty/tty_io.c5
-rw-r--r--drivers/tty/tty_ldisc.c29
-rw-r--r--drivers/tty/vt/vt.c6
-rw-r--r--drivers/tty/vt/vt_ioctl.c6
-rw-r--r--drivers/uio/uio_hv_generic.c73
-rw-r--r--drivers/usb/Kconfig6
-rw-r--r--drivers/usb/Makefile2
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.c15
-rw-r--r--drivers/usb/chipidea/debug.c65
-rw-r--r--drivers/usb/chipidea/host.c6
-rw-r--r--drivers/usb/chipidea/usbmisc_imx.c26
-rw-r--r--drivers/usb/common/Makefile1
-rw-r--r--drivers/usb/common/roles.c305
-rw-r--r--drivers/usb/core/Makefile2
-rw-r--r--drivers/usb/core/devio.c10
-rw-r--r--drivers/usb/core/generic.c9
-rw-r--r--drivers/usb/core/hcd.c71
-rw-r--r--drivers/usb/core/hub.c36
-rw-r--r--drivers/usb/core/hub.h1
-rw-r--r--drivers/usb/core/phy.c197
-rw-r--r--drivers/usb/core/phy.h27
-rw-r--r--drivers/usb/core/port.c10
-rw-r--r--drivers/usb/core/quirks.c189
-rw-r--r--drivers/usb/core/urb.c8
-rw-r--r--drivers/usb/core/usb-acpi.c4
-rw-r--r--drivers/usb/core/usb.c1
-rw-r--r--drivers/usb/core/usb.h1
-rw-r--r--drivers/usb/dwc2/core.c395
-rw-r--r--drivers/usb/dwc2/core.h136
-rw-r--r--drivers/usb/dwc2/core_intr.c304
-rw-r--r--drivers/usb/dwc2/debugfs.c91
-rw-r--r--drivers/usb/dwc2/gadget.c403
-rw-r--r--drivers/usb/dwc2/hcd.c445
-rw-r--r--drivers/usb/dwc2/hcd.h56
-rw-r--r--drivers/usb/dwc2/hw.h44
-rw-r--r--drivers/usb/dwc2/params.c91
-rw-r--r--drivers/usb/dwc2/pci.c27
-rw-r--r--drivers/usb/dwc2/platform.c16
-rw-r--r--drivers/usb/dwc3/Makefile2
-rw-r--r--drivers/usb/dwc3/core.c134
-rw-r--r--drivers/usb/dwc3/core.h146
-rw-r--r--drivers/usb/dwc3/debugfs.c84
-rw-r--r--drivers/usb/dwc3/drd.c489
-rw-r--r--drivers/usb/dwc3/dwc3-of-simple.c31
-rw-r--r--drivers/usb/dwc3/dwc3-pci.c2
-rw-r--r--drivers/usb/dwc3/ep0.c2
-rw-r--r--drivers/usb/dwc3/gadget.c82
-rw-r--r--drivers/usb/gadget/composite.c126
-rw-r--r--drivers/usb/gadget/function/f_eem.c1
-rw-r--r--drivers/usb/gadget/function/f_fs.c6
-rw-r--r--drivers/usb/gadget/function/f_midi.c3
-rw-r--r--drivers/usb/gadget/function/f_tcm.c2
-rw-r--r--drivers/usb/gadget/function/f_uac1_legacy.c2
-rw-r--r--drivers/usb/gadget/function/u_uac1_legacy.c2
-rw-r--r--drivers/usb/gadget/function/u_uac1_legacy.h2
-rw-r--r--drivers/usb/gadget/legacy/audio.c2
-rw-r--r--drivers/usb/gadget/legacy/mass_storage.c2
-rw-r--r--drivers/usb/gadget/u_f.h2
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.c158
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.h4
-rw-r--r--drivers/usb/gadget/udc/bcm63xx_udc.c33
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_ep.c3
-rw-r--r--drivers/usb/gadget/udc/core.c7
-rw-r--r--drivers/usb/gadget/udc/dummy_hcd.c2
-rw-r--r--drivers/usb/gadget/udc/goku_udc.h2
-rw-r--r--drivers/usb/gadget/udc/gr_udc.c17
-rw-r--r--drivers/usb/gadget/udc/pxa25x_udc.c20
-rw-r--r--drivers/usb/gadget/udc/pxa27x_udc.c42
-rw-r--r--drivers/usb/host/Kconfig26
-rw-r--r--drivers/usb/host/Makefile2
-rw-r--r--drivers/usb/host/ehci-fsl.c2
-rw-r--r--drivers/usb/host/ehci-hcd.c5
-rw-r--r--drivers/usb/host/ehci-mem.c3
-rw-r--r--drivers/usb/host/ehci-platform.c55
-rw-r--r--drivers/usb/host/ehci-sched.c6
-rw-r--r--drivers/usb/host/ehci-tegra.c1
-rw-r--r--drivers/usb/host/ehci-tilegx.c207
-rw-r--r--drivers/usb/host/fhci-dbg.c26
-rw-r--r--drivers/usb/host/imx21-dbg.c65
-rw-r--r--drivers/usb/host/isp116x-hcd.c15
-rw-r--r--drivers/usb/host/isp1362.h46
-rw-r--r--drivers/usb/host/ohci-hcd.c18
-rw-r--r--drivers/usb/host/ohci-omap.c1
-rw-r--r--drivers/usb/host/ohci-platform.c56
-rw-r--r--drivers/usb/host/ohci-tilegx.c196
-rw-r--r--drivers/usb/host/sl811-hcd.c17
-rw-r--r--drivers/usb/host/whci/debug.c48
-rw-r--r--drivers/usb/host/xhci-dbgtty.c13
-rw-r--r--drivers/usb/host/xhci-ext-caps.c90
-rw-r--r--drivers/usb/host/xhci-ext-caps.h7
-rw-r--r--drivers/usb/host/xhci-mem.c2
-rw-r--r--drivers/usb/host/xhci-mtk.c98
-rw-r--r--drivers/usb/host/xhci-pci.c10
-rw-r--r--drivers/usb/host/xhci-plat.c33
-rw-r--r--drivers/usb/host/xhci-ring.c27
-rw-r--r--drivers/usb/host/xhci.c139
-rw-r--r--drivers/usb/host/xhci.h7
-rw-r--r--drivers/usb/isp1760/isp1760-udc.c6
-rw-r--r--drivers/usb/misc/adutux.c4
-rw-r--r--drivers/usb/misc/chaoskey.c8
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb_con.c68
-rw-r--r--drivers/usb/misc/usbtest.c5
-rw-r--r--drivers/usb/misc/uss720.c7
-rw-r--r--drivers/usb/musb/Kconfig14
-rw-r--r--drivers/usb/musb/Makefile1
-rw-r--r--drivers/usb/musb/blackfin.c623
-rw-r--r--drivers/usb/musb/blackfin.h81
-rw-r--r--drivers/usb/musb/musb_core.c7
-rw-r--r--drivers/usb/musb/musb_core.h43
-rw-r--r--drivers/usb/musb/musb_debugfs.c15
-rw-r--r--drivers/usb/musb/musb_dma.h11
-rw-r--r--drivers/usb/musb/musb_dsps.c2
-rw-r--r--drivers/usb/musb/musb_gadget.c56
-rw-r--r--drivers/usb/musb/musb_gadget_ep0.c14
-rw-r--r--drivers/usb/musb/musb_host.c13
-rw-r--r--drivers/usb/musb/musb_regs.h182
-rw-r--r--drivers/usb/musb/musbhsdma.c5
-rw-r--r--drivers/usb/musb/musbhsdma.h64
-rw-r--r--drivers/usb/phy/phy-ab8500-usb.c508
-rw-r--r--drivers/usb/phy/phy-generic.c6
-rw-r--r--drivers/usb/phy/phy-mxs-usb.c2
-rw-r--r--drivers/usb/phy/phy-tegra-usb.c14
-rw-r--r--drivers/usb/roles/Kconfig14
-rw-r--r--drivers/usb/roles/Makefile1
-rw-r--r--drivers/usb/roles/intel-xhci-usb-role-switch.c192
-rw-r--r--drivers/usb/serial/Kconfig1
-rw-r--r--drivers/usb/serial/cp210x.c2
-rw-r--r--drivers/usb/serial/ftdi_sio.c5
-rw-r--r--drivers/usb/serial/ftdi_sio_ids.h9
-rw-r--r--drivers/usb/serial/option.c457
-rw-r--r--drivers/usb/serial/usb-serial-simple.c7
-rw-r--r--drivers/usb/typec/Kconfig3
-rw-r--r--drivers/usb/typec/Makefile2
-rw-r--r--drivers/usb/typec/class.c1447
-rw-r--r--drivers/usb/typec/fusb302/fusb302.c19
-rw-r--r--drivers/usb/typec/mux.c191
-rw-r--r--drivers/usb/typec/mux/Kconfig10
-rw-r--r--drivers/usb/typec/mux/Makefile3
-rw-r--r--drivers/usb/typec/mux/pi3usb30532.c178
-rw-r--r--drivers/usb/typec/tcpm.c96
-rw-r--r--drivers/usb/typec/tps6598x.c41
-rw-r--r--drivers/usb/typec/typec.c1365
-rw-r--r--drivers/usb/typec/typec_wcove.c1
-rw-r--r--drivers/usb/typec/ucsi/Makefile2
-rw-r--r--drivers/usb/typec/ucsi/ucsi.c46
-rw-r--r--drivers/usb/usb-skeleton.c2
-rw-r--r--drivers/usb/usbip/Kconfig2
-rw-r--r--drivers/usb/usbip/stub_main.c5
-rw-r--r--drivers/usb/usbip/usbip_common.h2
-rw-r--r--drivers/usb/usbip/usbip_event.c4
-rw-r--r--drivers/usb/usbip/vhci_hcd.c13
-rw-r--r--drivers/usb/wusbcore/crypto.c8
-rw-r--r--drivers/usb/wusbcore/wa-nep.c1
-rw-r--r--drivers/uwb/uwb-debug.c32
-rw-r--r--drivers/vfio/pci/vfio_pci.c35
-rw-r--r--drivers/vfio/pci/vfio_pci_private.h19
-rw-r--r--drivers/vfio/pci/vfio_pci_rdwr.c184
-rw-r--r--drivers/vfio/vfio_iommu_type1.c151
-rw-r--r--drivers/vhost/net.c19
-rw-r--r--drivers/vhost/vhost.c85
-rw-r--r--drivers/vhost/vhost.h8
-rw-r--r--drivers/vhost/vsock.c15
-rw-r--r--drivers/video/Kconfig8
-rw-r--r--drivers/video/console/Kconfig9
-rw-r--r--drivers/video/console/dummycon.c69
-rw-r--r--drivers/video/console/newport_con.c8
-rw-r--r--drivers/video/console/sticore.c4
-rw-r--r--drivers/video/console/vgacon.c20
-rw-r--r--drivers/video/fbdev/Kconfig111
-rw-r--r--drivers/video/fbdev/Makefile5
-rw-r--r--drivers/video/fbdev/amba-clcd.c3
-rw-r--r--drivers/video/fbdev/atmel_lcdfb.c31
-rw-r--r--drivers/video/fbdev/aty/aty128fb.c2
-rw-r--r--drivers/video/fbdev/aty/mach64_ct.c2
-rw-r--r--drivers/video/fbdev/aty/radeon_base.c21
-rw-r--r--drivers/video/fbdev/au1100fb.c9
-rw-r--r--drivers/video/fbdev/bf537-lq035.c891
-rw-r--r--drivers/video/fbdev/bf54x-lq043fb.c764
-rw-r--r--drivers/video/fbdev/bfin-lq035q1-fb.c864
-rw-r--r--drivers/video/fbdev/bfin-t350mcqb-fb.c669
-rw-r--r--drivers/video/fbdev/bfin_adv7393fb.c828
-rw-r--r--drivers/video/fbdev/bfin_adv7393fb.h319
-rw-r--r--drivers/video/fbdev/core/fbcon.c3
-rw-r--r--drivers/video/fbdev/fsl-diu-fb.c6
-rw-r--r--drivers/video/fbdev/matrox/matroxfb_crtc2.c5
-rw-r--r--drivers/video/fbdev/offb.c2
-rw-r--r--drivers/video/fbdev/s1d13xxxfb.c10
-rw-r--r--drivers/video/fbdev/s3c-fb.c168
-rw-r--r--drivers/video/fbdev/sis/init.h76
-rw-r--r--drivers/video/fbdev/sis/init301.c326
-rw-r--r--drivers/video/fbdev/sis/init301.h320
-rw-r--r--drivers/video/fbdev/sis/sis.h131
-rw-r--r--drivers/video/fbdev/sis/sis_main.c51
-rw-r--r--drivers/video/fbdev/sis/sis_main.h117
-rw-r--r--drivers/video/fbdev/smscufx.c59
-rw-r--r--drivers/video/fbdev/ssd1307fb.c3
-rw-r--r--drivers/video/fbdev/stifb.c8
-rw-r--r--drivers/video/fbdev/udlfb.c39
-rw-r--r--drivers/video/fbdev/vermilion/vermilion.c2
-rw-r--r--drivers/video/fbdev/via/via_aux_sii164.c2
-rw-r--r--drivers/video/fbdev/via/via_aux_vt1631.c2
-rw-r--r--drivers/video/fbdev/via/via_aux_vt1632.c2
-rw-r--r--drivers/video/fbdev/via/via_aux_vt1636.c2
-rw-r--r--drivers/video/logo/Kconfig15
-rw-r--r--drivers/video/logo/Makefile3
-rw-r--r--drivers/video/logo/logo.c12
-rw-r--r--drivers/video/logo/logo_blackfin_clut224.ppm1127
-rw-r--r--drivers/video/logo/logo_blackfin_vga16.ppm1127
-rw-r--r--drivers/video/logo/logo_m32r_clut224.ppm1292
-rw-r--r--drivers/video/of_display_timing.c20
-rw-r--r--drivers/virt/vboxguest/vboxguest_core.c70
-rw-r--r--drivers/virt/vboxguest/vboxguest_core.h9
-rw-r--r--drivers/virt/vboxguest/vboxguest_linux.c19
-rw-r--r--drivers/virt/vboxguest/vboxguest_utils.c17
-rw-r--r--drivers/virtio/virtio_balloon.c6
-rw-r--r--drivers/virtio/virtio_ring.c1
-rw-r--r--drivers/w1/w1.c1
-rw-r--r--drivers/watchdog/Kconfig36
-rw-r--r--drivers/watchdog/Makefile10
-rw-r--r--drivers/watchdog/ar7_wdt.c14
-rw-r--r--drivers/watchdog/asm9260_wdt.c8
-rw-r--r--drivers/watchdog/aspeed_wdt.c20
-rw-r--r--drivers/watchdog/at91rm9200_wdt.c5
-rw-r--r--drivers/watchdog/at91sam9_wdt.c5
-rw-r--r--drivers/watchdog/at91sam9_wdt.h5
-rw-r--r--drivers/watchdog/bcm2835_wdt.c5
-rw-r--r--drivers/watchdog/bcm47xx_wdt.c5
-rw-r--r--drivers/watchdog/bcm63xx_wdt.c5
-rw-r--r--drivers/watchdog/bcm7038_wdt.c12
-rw-r--r--drivers/watchdog/bcm_kona_wdt.c9
-rw-r--r--drivers/watchdog/bfin_wdt.c476
-rw-r--r--drivers/watchdog/cadence_wdt.c5
-rw-r--r--drivers/watchdog/coh901327_wdt.c18
-rw-r--r--drivers/watchdog/da9052_wdt.c6
-rw-r--r--drivers/watchdog/da9055_wdt.c6
-rw-r--r--drivers/watchdog/da9062_wdt.c10
-rw-r--r--drivers/watchdog/da9063_wdt.c5
-rw-r--r--drivers/watchdog/davinci_wdt.c15
-rw-r--r--drivers/watchdog/digicolor_wdt.c5
-rw-r--r--drivers/watchdog/dw_wdt.c32
-rw-r--r--drivers/watchdog/ebc-c384_wdt.c1
-rw-r--r--drivers/watchdog/f71808e_wdt.c2
-rw-r--r--drivers/watchdog/gpio_wdt.c4
-rw-r--r--drivers/watchdog/hpwdt.c312
-rw-r--r--drivers/watchdog/imx2_wdt.c8
-rw-r--r--drivers/watchdog/lpc18xx_wdt.c2
-rw-r--r--drivers/watchdog/mei_wdt.c12
-rw-r--r--drivers/watchdog/mena21_wdt.c4
-rw-r--r--drivers/watchdog/meson_gxbb_wdt.c50
-rw-r--r--drivers/watchdog/meson_wdt.c2
-rw-r--r--drivers/watchdog/mtk_wdt.c13
-rw-r--r--drivers/watchdog/mtx-1_wdt.c11
-rw-r--r--drivers/watchdog/npcm_wdt.c254
-rw-r--r--drivers/watchdog/of_xilinx_wdt.c8
-rw-r--r--drivers/watchdog/omap_wdt.c4
-rw-r--r--drivers/watchdog/pnx4008_wdt.c2
-rw-r--r--drivers/watchdog/renesas_wdt.c93
-rw-r--r--drivers/watchdog/sama5d4_wdt.c6
-rw-r--r--drivers/watchdog/sch311x_wdt.c2
-rw-r--r--drivers/watchdog/sirfsoc_wdt.c2
-rw-r--r--drivers/watchdog/sprd_wdt.c4
-rw-r--r--drivers/watchdog/st_lpc_wdt.c6
-rw-r--r--drivers/watchdog/sunxi_wdt.c2
-rw-r--r--drivers/watchdog/tangox_wdt.c6
-rw-r--r--drivers/watchdog/tegra_wdt.c10
-rw-r--r--drivers/watchdog/uniphier_wdt.c15
-rw-r--r--drivers/watchdog/w83977f_wdt.c2
-rw-r--r--drivers/watchdog/wafer5823wdt.c2
-rw-r--r--drivers/watchdog/wm831x_wdt.c5
-rw-r--r--drivers/watchdog/wm8350_wdt.c5
-rw-r--r--drivers/xen/swiotlb-xen.c16
-rw-r--r--drivers/xen/xen-acpi-processor.c30
-rw-r--r--drivers/xen/xen-pciback/conf_space_quirks.c2
-rw-r--r--drivers/xen/xen-pciback/pci_stub.c8
-rw-r--r--drivers/xen/xenbus/xenbus_dev_frontend.c19
-rw-r--r--drivers/xen/xenbus/xenbus_xs.c4
-rw-r--r--drivers/zorro/zorro.c12
4960 files changed, 327008 insertions, 319768 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 879dc0604cba..95b9ccc08165 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -199,9 +199,7 @@ source "drivers/dax/Kconfig"
source "drivers/nvmem/Kconfig"
-source "drivers/hwtracing/stm/Kconfig"
-
-source "drivers/hwtracing/intel_th/Kconfig"
+source "drivers/hwtracing/Kconfig"
source "drivers/fpga/Kconfig"
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index f505e9a01b2d..516d7b36d6fb 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -105,14 +105,14 @@ config ACPI_PROCFS_POWER
deprecated power /proc/acpi/ directories to exist, even when
they have been replaced by functions in /sys.
The deprecated directories (and their replacements) include:
- /proc/acpi/battery/* (/sys/class/power_supply/*)
- /proc/acpi/ac_adapter/* (sys/class/power_supply/*)
+ /proc/acpi/battery/* (/sys/class/power_supply/*) and
+ /proc/acpi/ac_adapter/* (sys/class/power_supply/*).
This option has no effect on /proc/acpi/ directories
- and functions, which do not yet exist in /sys
+ and functions which do not yet exist in /sys.
This option, together with the proc directories, will be
deleted in the future.
- Say N to delete power /proc/acpi/ directories that have moved to /sys/
+ Say N to delete power /proc/acpi/ directories that have moved to /sys.
config ACPI_REV_OVERRIDE_POSSIBLE
bool "Allow supported ACPI revision to be overridden"
@@ -217,6 +217,19 @@ config ACPI_FAN
To compile this driver as a module, choose M here:
the module will be called fan.
+config ACPI_TAD
+ tristate "ACPI Time and Alarm (TAD) Device Support"
+ depends on SYSFS && PM_SLEEP
+ help
+ The ACPI Time and Alarm (TAD) device is an alternative to the Real
+ Time Clock (RTC). Its wake timers allow the system to transition from
+ the S3 (or optionally S4/S5) state to S0 state after a time period
+ elapses. In comparison with the RTC Alarm, the TAD provides a larger
+ scale of flexibility in the wake timers. The time capabilities of the
+ TAD maintain the time of day information across platform power
+ transitions, and keep track of time even when the platform is turned
+ off.
+
config ACPI_DOCK
bool "Dock"
help
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 41954a601989..48e202752754 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -70,6 +70,7 @@ obj-$(CONFIG_ACPI_AC) += ac.o
obj-$(CONFIG_ACPI_BUTTON) += button.o
obj-$(CONFIG_ACPI_FAN) += fan.o
obj-$(CONFIG_ACPI_VIDEO) += video.o
+obj-$(CONFIG_ACPI_TAD) += acpi_tad.o
obj-$(CONFIG_ACPI_PCI_SLOT) += pci_slot.o
obj-$(CONFIG_ACPI_PROCESSOR) += processor.o
obj-$(CONFIG_ACPI) += container.o
diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c
index 47a7ed557bd6..2d8de2f8c1ed 100644
--- a/drivers/acpi/ac.c
+++ b/drivers/acpi/ac.c
@@ -33,7 +33,7 @@
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/acpi.h>
-#include "battery.h"
+#include <acpi/battery.h>
#define PREFIX "ACPI: "
diff --git a/drivers/acpi/acpi_pad.c b/drivers/acpi/acpi_pad.c
index 754431031282..552c1f725b6c 100644
--- a/drivers/acpi/acpi_pad.c
+++ b/drivers/acpi/acpi_pad.c
@@ -110,6 +110,7 @@ static void round_robin_cpu(unsigned int tsk_index)
cpumask_andnot(tmp, cpu_online_mask, pad_busy_cpus);
if (cpumask_empty(tmp)) {
mutex_unlock(&round_robin_lock);
+ free_cpumask_var(tmp);
return;
}
for_each_cpu(cpu, tmp) {
@@ -127,6 +128,8 @@ static void round_robin_cpu(unsigned int tsk_index)
mutex_unlock(&round_robin_lock);
set_cpus_allowed_ptr(current, cpumask_of(preferred_cpu));
+
+ free_cpumask_var(tmp);
}
static void exit_round_robin(unsigned int tsk_index)
diff --git a/drivers/acpi/acpi_tad.c b/drivers/acpi/acpi_tad.c
new file mode 100644
index 000000000000..e99c4ed7e677
--- /dev/null
+++ b/drivers/acpi/acpi_tad.c
@@ -0,0 +1,473 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ACPI Time and Alarm (TAD) Device Driver
+ *
+ * Copyright (C) 2018 Intel Corporation
+ * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+ *
+ * This driver is based on Section 9.18 of the ACPI 6.2 specification revision.
+ *
+ * It only supports the system wakeup capabilities of the TAD.
+ *
+ * Provided are sysfs attributes, available under the TAD platform device,
+ * allowing user space to manage the AC and DC wakeup timers of the TAD:
+ * set and read their values, set and check their expire timer wake policies,
+ * check and clear their status and check the capabilities of the TAD reported
+ * by AML. The DC timer attributes are only present if the TAD supports a
+ * separate DC alarm timer.
+ *
+ * The wakeup events handling and power management of the TAD is expected to
+ * be taken care of by the ACPI PM domain attached to its platform device.
+ */
+
+#include <linux/acpi.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/suspend.h>
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Rafael J. Wysocki");
+
+/* ACPI TAD capability flags (ACPI 6.2, Section 9.18.2) */
+#define ACPI_TAD_AC_WAKE BIT(0)
+#define ACPI_TAD_DC_WAKE BIT(1)
+#define ACPI_TAD_RT BIT(2)
+#define ACPI_TAD_RT_IN_MS BIT(3)
+#define ACPI_TAD_S4_S5__GWS BIT(4)
+#define ACPI_TAD_AC_S4_WAKE BIT(5)
+#define ACPI_TAD_AC_S5_WAKE BIT(6)
+#define ACPI_TAD_DC_S4_WAKE BIT(7)
+#define ACPI_TAD_DC_S5_WAKE BIT(8)
+
+/* ACPI TAD alarm timer selection */
+#define ACPI_TAD_AC_TIMER (u32)0
+#define ACPI_TAD_DC_TIMER (u32)1
+
+/* Special value for disabled timer or expired timer wake policy. */
+#define ACPI_TAD_WAKE_DISABLED (~(u32)0)
+
+struct acpi_tad_driver_data {
+ u32 capabilities;
+};
+
+static int acpi_tad_wake_set(struct device *dev, char *method, u32 timer_id,
+ u32 value)
+{
+ acpi_handle handle = ACPI_HANDLE(dev);
+ union acpi_object args[] = {
+ { .type = ACPI_TYPE_INTEGER, },
+ { .type = ACPI_TYPE_INTEGER, },
+ };
+ struct acpi_object_list arg_list = {
+ .pointer = args,
+ .count = ARRAY_SIZE(args),
+ };
+ unsigned long long retval;
+ acpi_status status;
+
+ args[0].integer.value = timer_id;
+ args[1].integer.value = value;
+
+ pm_runtime_get_sync(dev);
+
+ status = acpi_evaluate_integer(handle, method, &arg_list, &retval);
+
+ pm_runtime_put_sync(dev);
+
+ if (ACPI_FAILURE(status) || retval)
+ return -EIO;
+
+ return 0;
+}
+
+static int acpi_tad_wake_write(struct device *dev, const char *buf, char *method,
+ u32 timer_id, const char *specval)
+{
+ u32 value;
+
+ if (sysfs_streq(buf, specval)) {
+ value = ACPI_TAD_WAKE_DISABLED;
+ } else {
+ int ret = kstrtou32(buf, 0, &value);
+
+ if (ret)
+ return ret;
+
+ if (value == ACPI_TAD_WAKE_DISABLED)
+ return -EINVAL;
+ }
+
+ return acpi_tad_wake_set(dev, method, timer_id, value);
+}
+
+static ssize_t acpi_tad_wake_read(struct device *dev, char *buf, char *method,
+ u32 timer_id, const char *specval)
+{
+ acpi_handle handle = ACPI_HANDLE(dev);
+ union acpi_object args[] = {
+ { .type = ACPI_TYPE_INTEGER, },
+ };
+ struct acpi_object_list arg_list = {
+ .pointer = args,
+ .count = ARRAY_SIZE(args),
+ };
+ unsigned long long retval;
+ acpi_status status;
+
+ args[0].integer.value = timer_id;
+
+ pm_runtime_get_sync(dev);
+
+ status = acpi_evaluate_integer(handle, method, &arg_list, &retval);
+
+ pm_runtime_put_sync(dev);
+
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ if ((u32)retval == ACPI_TAD_WAKE_DISABLED)
+ return sprintf(buf, "%s\n", specval);
+
+ return sprintf(buf, "%u\n", (u32)retval);
+}
+
+static const char *alarm_specval = "disabled";
+
+static int acpi_tad_alarm_write(struct device *dev, const char *buf,
+ u32 timer_id)
+{
+ return acpi_tad_wake_write(dev, buf, "_STV", timer_id, alarm_specval);
+}
+
+static ssize_t acpi_tad_alarm_read(struct device *dev, char *buf, u32 timer_id)
+{
+ return acpi_tad_wake_read(dev, buf, "_TIV", timer_id, alarm_specval);
+}
+
+static const char *policy_specval = "never";
+
+static int acpi_tad_policy_write(struct device *dev, const char *buf,
+ u32 timer_id)
+{
+ return acpi_tad_wake_write(dev, buf, "_STP", timer_id, policy_specval);
+}
+
+static ssize_t acpi_tad_policy_read(struct device *dev, char *buf, u32 timer_id)
+{
+ return acpi_tad_wake_read(dev, buf, "_TIP", timer_id, policy_specval);
+}
+
+static int acpi_tad_clear_status(struct device *dev, u32 timer_id)
+{
+ acpi_handle handle = ACPI_HANDLE(dev);
+ union acpi_object args[] = {
+ { .type = ACPI_TYPE_INTEGER, },
+ };
+ struct acpi_object_list arg_list = {
+ .pointer = args,
+ .count = ARRAY_SIZE(args),
+ };
+ unsigned long long retval;
+ acpi_status status;
+
+ args[0].integer.value = timer_id;
+
+ pm_runtime_get_sync(dev);
+
+ status = acpi_evaluate_integer(handle, "_CWS", &arg_list, &retval);
+
+ pm_runtime_put_sync(dev);
+
+ if (ACPI_FAILURE(status) || retval)
+ return -EIO;
+
+ return 0;
+}
+
+static int acpi_tad_status_write(struct device *dev, const char *buf, u32 timer_id)
+{
+ int ret, value;
+
+ ret = kstrtoint(buf, 0, &value);
+ if (ret)
+ return ret;
+
+ if (value)
+ return -EINVAL;
+
+ return acpi_tad_clear_status(dev, timer_id);
+}
+
+static ssize_t acpi_tad_status_read(struct device *dev, char *buf, u32 timer_id)
+{
+ acpi_handle handle = ACPI_HANDLE(dev);
+ union acpi_object args[] = {
+ { .type = ACPI_TYPE_INTEGER, },
+ };
+ struct acpi_object_list arg_list = {
+ .pointer = args,
+ .count = ARRAY_SIZE(args),
+ };
+ unsigned long long retval;
+ acpi_status status;
+
+ args[0].integer.value = timer_id;
+
+ pm_runtime_get_sync(dev);
+
+ status = acpi_evaluate_integer(handle, "_GWS", &arg_list, &retval);
+
+ pm_runtime_put_sync(dev);
+
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ return sprintf(buf, "0x%02X\n", (u32)retval);
+}
+
+static ssize_t caps_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct acpi_tad_driver_data *dd = dev_get_drvdata(dev);
+
+ return sprintf(buf, "0x%02X\n", dd->capabilities);
+}
+
+static DEVICE_ATTR_RO(caps);
+
+static ssize_t ac_alarm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret = acpi_tad_alarm_write(dev, buf, ACPI_TAD_AC_TIMER);
+
+ return ret ? ret : count;
+}
+
+static ssize_t ac_alarm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return acpi_tad_alarm_read(dev, buf, ACPI_TAD_AC_TIMER);
+}
+
+static DEVICE_ATTR(ac_alarm, S_IRUSR | S_IWUSR, ac_alarm_show, ac_alarm_store);
+
+static ssize_t ac_policy_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret = acpi_tad_policy_write(dev, buf, ACPI_TAD_AC_TIMER);
+
+ return ret ? ret : count;
+}
+
+static ssize_t ac_policy_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return acpi_tad_policy_read(dev, buf, ACPI_TAD_AC_TIMER);
+}
+
+static DEVICE_ATTR(ac_policy, S_IRUSR | S_IWUSR, ac_policy_show, ac_policy_store);
+
+static ssize_t ac_status_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret = acpi_tad_status_write(dev, buf, ACPI_TAD_AC_TIMER);
+
+ return ret ? ret : count;
+}
+
+static ssize_t ac_status_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return acpi_tad_status_read(dev, buf, ACPI_TAD_AC_TIMER);
+}
+
+static DEVICE_ATTR(ac_status, S_IRUSR | S_IWUSR, ac_status_show, ac_status_store);
+
+static struct attribute *acpi_tad_attrs[] = {
+ &dev_attr_caps.attr,
+ &dev_attr_ac_alarm.attr,
+ &dev_attr_ac_policy.attr,
+ &dev_attr_ac_status.attr,
+ NULL,
+};
+static const struct attribute_group acpi_tad_attr_group = {
+ .attrs = acpi_tad_attrs,
+};
+
+static ssize_t dc_alarm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret = acpi_tad_alarm_write(dev, buf, ACPI_TAD_DC_TIMER);
+
+ return ret ? ret : count;
+}
+
+static ssize_t dc_alarm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return acpi_tad_alarm_read(dev, buf, ACPI_TAD_DC_TIMER);
+}
+
+static DEVICE_ATTR(dc_alarm, S_IRUSR | S_IWUSR, dc_alarm_show, dc_alarm_store);
+
+static ssize_t dc_policy_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret = acpi_tad_policy_write(dev, buf, ACPI_TAD_DC_TIMER);
+
+ return ret ? ret : count;
+}
+
+static ssize_t dc_policy_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return acpi_tad_policy_read(dev, buf, ACPI_TAD_DC_TIMER);
+}
+
+static DEVICE_ATTR(dc_policy, S_IRUSR | S_IWUSR, dc_policy_show, dc_policy_store);
+
+static ssize_t dc_status_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret = acpi_tad_status_write(dev, buf, ACPI_TAD_DC_TIMER);
+
+ return ret ? ret : count;
+}
+
+static ssize_t dc_status_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return acpi_tad_status_read(dev, buf, ACPI_TAD_DC_TIMER);
+}
+
+static DEVICE_ATTR(dc_status, S_IRUSR | S_IWUSR, dc_status_show, dc_status_store);
+
+static struct attribute *acpi_tad_dc_attrs[] = {
+ &dev_attr_dc_alarm.attr,
+ &dev_attr_dc_policy.attr,
+ &dev_attr_dc_status.attr,
+ NULL,
+};
+static const struct attribute_group acpi_tad_dc_attr_group = {
+ .attrs = acpi_tad_dc_attrs,
+};
+
+static int acpi_tad_disable_timer(struct device *dev, u32 timer_id)
+{
+ return acpi_tad_wake_set(dev, "_STV", timer_id, ACPI_TAD_WAKE_DISABLED);
+}
+
+static int acpi_tad_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acpi_tad_driver_data *dd = dev_get_drvdata(dev);
+
+ device_init_wakeup(dev, false);
+
+ pm_runtime_get_sync(dev);
+
+ if (dd->capabilities & ACPI_TAD_DC_WAKE)
+ sysfs_remove_group(&dev->kobj, &acpi_tad_dc_attr_group);
+
+ sysfs_remove_group(&dev->kobj, &acpi_tad_attr_group);
+
+ acpi_tad_disable_timer(dev, ACPI_TAD_AC_TIMER);
+ acpi_tad_clear_status(dev, ACPI_TAD_AC_TIMER);
+ if (dd->capabilities & ACPI_TAD_DC_WAKE) {
+ acpi_tad_disable_timer(dev, ACPI_TAD_DC_TIMER);
+ acpi_tad_clear_status(dev, ACPI_TAD_DC_TIMER);
+ }
+
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
+ return 0;
+}
+
+static int acpi_tad_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ acpi_handle handle = ACPI_HANDLE(dev);
+ struct acpi_tad_driver_data *dd;
+ acpi_status status;
+ unsigned long long caps;
+ int ret;
+
+ /*
+ * Initialization failure messages are mostly about firmware issues, so
+ * print them at the "info" level.
+ */
+ status = acpi_evaluate_integer(handle, "_GCP", NULL, &caps);
+ if (ACPI_FAILURE(status)) {
+ dev_info(dev, "Unable to get capabilities\n");
+ return -ENODEV;
+ }
+
+ if (!(caps & ACPI_TAD_AC_WAKE)) {
+ dev_info(dev, "Unsupported capabilities\n");
+ return -ENODEV;
+ }
+
+ if (!acpi_has_method(handle, "_PRW")) {
+ dev_info(dev, "Missing _PRW\n");
+ return -ENODEV;
+ }
+
+ dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL);
+ if (!dd)
+ return -ENOMEM;
+
+ dd->capabilities = caps;
+ dev_set_drvdata(dev, dd);
+
+ /*
+ * Assume that the ACPI PM domain has been attached to the device and
+ * simply enable system wakeup and runtime PM and put the device into
+ * runtime suspend. Everything else should be taken care of by the ACPI
+ * PM domain callbacks.
+ */
+ device_init_wakeup(dev, true);
+ dev_pm_set_driver_flags(dev, DPM_FLAG_SMART_SUSPEND |
+ DPM_FLAG_LEAVE_SUSPENDED);
+ /*
+ * The platform bus type layer tells the ACPI PM domain powers up the
+ * device, so set the runtime PM status of it to "active".
+ */
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_suspend(dev);
+
+ ret = sysfs_create_group(&dev->kobj, &acpi_tad_attr_group);
+ if (ret)
+ goto fail;
+
+ if (caps & ACPI_TAD_DC_WAKE) {
+ ret = sysfs_create_group(&dev->kobj, &acpi_tad_dc_attr_group);
+ if (ret)
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ acpi_tad_remove(pdev);
+ return ret;
+}
+
+static const struct acpi_device_id acpi_tad_ids[] = {
+ {"ACPI000E", 0},
+ {}
+};
+
+static struct platform_driver acpi_tad_driver = {
+ .driver = {
+ .name = "acpi-tad",
+ .acpi_match_table = acpi_tad_ids,
+ },
+ .probe = acpi_tad_probe,
+ .remove = acpi_tad_remove,
+};
+MODULE_DEVICE_TABLE(acpi, acpi_tad_ids);
+
+module_platform_driver(acpi_tad_driver);
diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c
index 76fb96966f7b..2f2e737be0f8 100644
--- a/drivers/acpi/acpi_video.c
+++ b/drivers/acpi/acpi_video.c
@@ -2123,6 +2123,25 @@ static int __init intel_opregion_present(void)
return opregion;
}
+static bool dmi_is_desktop(void)
+{
+ const char *chassis_type;
+
+ chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE);
+ if (!chassis_type)
+ return false;
+
+ if (!strcmp(chassis_type, "3") || /* 3: Desktop */
+ !strcmp(chassis_type, "4") || /* 4: Low Profile Desktop */
+ !strcmp(chassis_type, "5") || /* 5: Pizza Box */
+ !strcmp(chassis_type, "6") || /* 6: Mini Tower */
+ !strcmp(chassis_type, "7") || /* 7: Tower */
+ !strcmp(chassis_type, "11")) /* 11: Main Server Chassis */
+ return true;
+
+ return false;
+}
+
int acpi_video_register(void)
{
int ret = 0;
@@ -2143,8 +2162,12 @@ int acpi_video_register(void)
* win8 ready (where we also prefer the native backlight driver, so
* normally the acpi_video code should not register there anyways).
*/
- if (only_lcd == -1)
- only_lcd = acpi_osi_is_win8();
+ if (only_lcd == -1) {
+ if (dmi_is_desktop() && acpi_osi_is_win8())
+ only_lcd = true;
+ else
+ only_lcd = false;
+ }
dmi_check_system(video_dmi_table);
diff --git a/drivers/acpi/acpi_watchdog.c b/drivers/acpi/acpi_watchdog.c
index ebb626ffb5fa..4bde16fb97d8 100644
--- a/drivers/acpi/acpi_watchdog.c
+++ b/drivers/acpi/acpi_watchdog.c
@@ -12,23 +12,64 @@
#define pr_fmt(fmt) "ACPI: watchdog: " fmt
#include <linux/acpi.h>
+#include <linux/dmi.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include "internal.h"
+static const struct dmi_system_id acpi_watchdog_skip[] = {
+ {
+ /*
+ * On Lenovo Z50-70 there are two issues with the WDAT
+ * table. First some of the instructions use RTC SRAM
+ * to store persistent information. This does not work well
+ * with Linux RTC driver. Second, more important thing is
+ * that the instructions do not actually reset the system.
+ *
+ * On this particular system iTCO_wdt seems to work just
+ * fine so we prefer that over WDAT for now.
+ *
+ * See also https://bugzilla.kernel.org/show_bug.cgi?id=199033.
+ */
+ .ident = "Lenovo Z50-70",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "20354"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Z50-70"),
+ },
+ },
+ {}
+};
+
+static const struct acpi_table_wdat *acpi_watchdog_get_wdat(void)
+{
+ const struct acpi_table_wdat *wdat = NULL;
+ acpi_status status;
+
+ if (acpi_disabled)
+ return NULL;
+
+ if (dmi_check_system(acpi_watchdog_skip))
+ return NULL;
+
+ status = acpi_get_table(ACPI_SIG_WDAT, 0,
+ (struct acpi_table_header **)&wdat);
+ if (ACPI_FAILURE(status)) {
+ /* It is fine if there is no WDAT */
+ return NULL;
+ }
+
+ return wdat;
+}
+
/**
* Returns true if this system should prefer ACPI based watchdog instead of
* the native one (which are typically the same hardware).
*/
bool acpi_has_watchdog(void)
{
- struct acpi_table_header hdr;
-
- if (acpi_disabled)
- return false;
-
- return ACPI_SUCCESS(acpi_get_table_header(ACPI_SIG_WDAT, 0, &hdr));
+ return !!acpi_watchdog_get_wdat();
}
EXPORT_SYMBOL_GPL(acpi_has_watchdog);
@@ -41,12 +82,10 @@ void __init acpi_watchdog_init(void)
struct platform_device *pdev;
struct resource *resources;
size_t nresources = 0;
- acpi_status status;
int i;
- status = acpi_get_table(ACPI_SIG_WDAT, 0,
- (struct acpi_table_header **)&wdat);
- if (ACPI_FAILURE(status)) {
+ wdat = acpi_watchdog_get_wdat();
+ if (!wdat) {
/* It is fine if there is no WDAT */
return;
}
diff --git a/drivers/acpi/acpica/acapps.h b/drivers/acpi/acpica/acapps.h
index e65478593f9a..a2a85122fafe 100644
--- a/drivers/acpi/acpica/acapps.h
+++ b/drivers/acpi/acpica/acapps.h
@@ -1,45 +1,11 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/******************************************************************************
*
* Module Name: acapps - common include for ACPI applications/tools
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#ifndef _ACAPPS
#define _ACAPPS
diff --git a/drivers/acpi/acpica/accommon.h b/drivers/acpi/acpica/accommon.h
index c349ffdf5557..8bc935977d8e 100644
--- a/drivers/acpi/acpica/accommon.h
+++ b/drivers/acpi/acpica/accommon.h
@@ -1,45 +1,11 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/******************************************************************************
*
* Name: accommon.h - Common include files for generation of ACPICA source
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#ifndef __ACCOMMON_H__
#define __ACCOMMON_H__
diff --git a/drivers/acpi/acpica/acconvert.h b/drivers/acpi/acpica/acconvert.h
index ce6e8db83e27..4ebe18826646 100644
--- a/drivers/acpi/acpica/acconvert.h
+++ b/drivers/acpi/acpica/acconvert.h
@@ -1,45 +1,11 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/******************************************************************************
*
* Module Name: acapps - common include for ACPI applications/tools
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#ifndef _ACCONVERT
#define _ACCONVERT
diff --git a/drivers/acpi/acpica/acdebug.h b/drivers/acpi/acpica/acdebug.h
index 8b2cca5a717b..57d9495e5933 100644
--- a/drivers/acpi/acpica/acdebug.h
+++ b/drivers/acpi/acpica/acdebug.h
@@ -1,45 +1,11 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/******************************************************************************
*
* Name: acdebug.h - ACPI/AML debugger
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#ifndef __ACDEBUG_H__
#define __ACDEBUG_H__
diff --git a/drivers/acpi/acpica/acdispat.h b/drivers/acpi/acpica/acdispat.h
index fab590bc5fd3..e577f3a40e6a 100644
--- a/drivers/acpi/acpica/acdispat.h
+++ b/drivers/acpi/acpica/acdispat.h
@@ -1,45 +1,11 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/******************************************************************************
*
* Name: acdispat.h - dispatcher (parser to interpreter interface)
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#ifndef _ACDISPAT_H_
#define _ACDISPAT_H_
diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h
index 1b0269f6ac2d..704bebbd35b0 100644
--- a/drivers/acpi/acpica/acevents.h
+++ b/drivers/acpi/acpica/acevents.h
@@ -1,50 +1,30 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/******************************************************************************
*
* Name: acevents.h - Event subcomponent prototypes and defines
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#ifndef __ACEVENTS_H__
#define __ACEVENTS_H__
/*
+ * Conditions to trigger post enabling GPE polling:
+ * It is not sufficient to trigger edge-triggered GPE with specific GPE
+ * chips, software need to poll once after enabling.
+ */
+#ifdef ACPI_USE_GPE_POLLING
+#define ACPI_GPE_IS_POLLING_NEEDED(__gpe__) \
+ ((__gpe__)->runtime_count == 1 && \
+ (__gpe__)->flags & ACPI_GPE_INITIALIZED && \
+ ((__gpe__)->flags & ACPI_GPE_XRUPT_TYPE_MASK) == ACPI_GPE_EDGE_TRIGGERED)
+#else
+#define ACPI_GPE_IS_POLLING_NEEDED(__gpe__) FALSE
+#endif
+
+/*
* evevent
*/
acpi_status acpi_ev_initialize_events(void);
@@ -103,6 +83,10 @@ struct acpi_gpe_event_info *acpi_ev_low_get_gpe_info(u32 gpe_number,
acpi_status acpi_ev_finish_gpe(struct acpi_gpe_event_info *gpe_event_info);
+u32
+acpi_ev_detect_gpe(struct acpi_namespace_node *gpe_device,
+ struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number);
+
/*
* evgpeblk - Upper-level GPE block support
*/
diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h
index 27f322b2fed1..0bc550072a21 100644
--- a/drivers/acpi/acpica/acglobal.h
+++ b/drivers/acpi/acpica/acglobal.h
@@ -1,45 +1,11 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/******************************************************************************
*
* Name: acglobal.h - Declarations for global variables
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#ifndef __ACGLOBAL_H__
#define __ACGLOBAL_H__
diff --git a/drivers/acpi/acpica/achware.h b/drivers/acpi/acpica/achware.h
index 3569aa3bf5ee..43ce67a9da1f 100644
--- a/drivers/acpi/acpica/achware.h
+++ b/drivers/acpi/acpica/achware.h
@@ -1,45 +1,11 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/******************************************************************************
*
* Name: achware.h -- hardware specific interfaces
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#ifndef __ACHWARE_H__
#define __ACHWARE_H__
diff --git a/drivers/acpi/acpica/acinterp.h b/drivers/acpi/acpica/acinterp.h
index 744374ab9285..9613b0115dad 100644
--- a/drivers/acpi/acpica/acinterp.h
+++ b/drivers/acpi/acpica/acinterp.h
@@ -1,45 +1,11 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/******************************************************************************
*
* Name: acinterp.h - Interpreter subcomponent prototypes and defines
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#ifndef __ACINTERP_H__
#define __ACINTERP_H__
diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h
index 3ba3ff0f1c04..51c386bf230d 100644
--- a/drivers/acpi/acpica/aclocal.h
+++ b/drivers/acpi/acpica/aclocal.h
@@ -1,45 +1,11 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/******************************************************************************
*
* Name: aclocal.h - Internal data types used across the ACPI subsystem
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#ifndef __ACLOCAL_H__
#define __ACLOCAL_H__
diff --git a/drivers/acpi/acpica/acmacros.h b/drivers/acpi/acpica/acmacros.h
index 6463340c4522..de52cd6e868a 100644
--- a/drivers/acpi/acpica/acmacros.h
+++ b/drivers/acpi/acpica/acmacros.h
@@ -1,45 +1,11 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/******************************************************************************
*
* Name: acmacros.h - C macros for the entire subsystem.
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#ifndef __ACMACROS_H__
#define __ACMACROS_H__
@@ -460,16 +426,18 @@
#define ACPI_WARN_PREDEFINED(plist) acpi_ut_predefined_warning plist
#define ACPI_INFO_PREDEFINED(plist) acpi_ut_predefined_info plist
#define ACPI_BIOS_ERROR_PREDEFINED(plist) acpi_ut_predefined_bios_error plist
+#define ACPI_ERROR_ONLY(s) s
#else
/* No error messages */
-#define ACPI_ERROR_NAMESPACE(s, e)
+#define ACPI_ERROR_NAMESPACE(s, p, e)
#define ACPI_ERROR_METHOD(s, n, p, e)
#define ACPI_WARN_PREDEFINED(plist)
#define ACPI_INFO_PREDEFINED(plist)
#define ACPI_BIOS_ERROR_PREDEFINED(plist)
+#define ACPI_ERROR_ONLY(s)
#endif /* ACPI_NO_ERROR_MESSAGES */
diff --git a/drivers/acpi/acpica/acnamesp.h b/drivers/acpi/acpica/acnamesp.h
index 6c8f364fe2fc..514aaf948ea9 100644
--- a/drivers/acpi/acpica/acnamesp.h
+++ b/drivers/acpi/acpica/acnamesp.h
@@ -1,45 +1,11 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/******************************************************************************
*
* Name: acnamesp.h - Namespace subcomponent prototypes and defines
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#ifndef __ACNAMESP_H__
#define __ACNAMESP_H__
diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h
index a1f4d3f385c8..ac992b6ebce9 100644
--- a/drivers/acpi/acpica/acobject.h
+++ b/drivers/acpi/acpica/acobject.h
@@ -1,45 +1,11 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/******************************************************************************
*
* Name: acobject.h - Definition of union acpi_operand_object (Internal object only)
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#ifndef _ACOBJECT_H
#define _ACOBJECT_H
diff --git a/drivers/acpi/acpica/acopcode.h b/drivers/acpi/acpica/acopcode.h
index 36c2c5825986..818eba413614 100644
--- a/drivers/acpi/acpica/acopcode.h
+++ b/drivers/acpi/acpica/acopcode.h
@@ -1,45 +1,11 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/******************************************************************************
*
* Name: acopcode.h - AML opcode information for the AML parser and interpreter
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#ifndef __ACOPCODE_H__
#define __ACOPCODE_H__
@@ -251,7 +217,7 @@
#define ARGI_FIELD_OP ARGI_INVALID_OPCODE
#define ARGI_FIND_SET_LEFT_BIT_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_TARGETREF)
#define ARGI_FIND_SET_RIGHT_BIT_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_TARGETREF)
-#define ARGI_FROM_BCD_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_TARGETREF)
+#define ARGI_FROM_BCD_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_FIXED_TARGET)
#define ARGI_IF_OP ARGI_INVALID_OPCODE
#define ARGI_INCREMENT_OP ARGI_LIST1 (ARGI_TARGETREF)
#define ARGI_INDEX_FIELD_OP ARGI_INVALID_OPCODE
@@ -315,12 +281,12 @@
#define ARGI_SUBTRACT_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF)
#define ARGI_THERMAL_ZONE_OP ARGI_INVALID_OPCODE
#define ARGI_TIMER_OP ARG_NONE
-#define ARGI_TO_BCD_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_TARGETREF)
-#define ARGI_TO_BUFFER_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_TARGETREF)
-#define ARGI_TO_DEC_STR_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_TARGETREF)
-#define ARGI_TO_HEX_STR_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_TARGETREF)
-#define ARGI_TO_INTEGER_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_TARGETREF)
-#define ARGI_TO_STRING_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_TARGETREF)
+#define ARGI_TO_BCD_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_FIXED_TARGET)
+#define ARGI_TO_BUFFER_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_FIXED_TARGET)
+#define ARGI_TO_DEC_STR_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_FIXED_TARGET)
+#define ARGI_TO_HEX_STR_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_FIXED_TARGET)
+#define ARGI_TO_INTEGER_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_FIXED_TARGET)
+#define ARGI_TO_STRING_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_FIXED_TARGET)
#define ARGI_UNLOAD_OP ARGI_LIST1 (ARGI_DDBHANDLE)
#define ARGI_VAR_PACKAGE_OP ARGI_LIST1 (ARGI_INTEGER)
#define ARGI_WAIT_OP ARGI_LIST2 (ARGI_EVENT, ARGI_INTEGER)
diff --git a/drivers/acpi/acpica/acparser.h b/drivers/acpi/acpica/acparser.h
index e25634951d03..ab48196ae55e 100644
--- a/drivers/acpi/acpica/acparser.h
+++ b/drivers/acpi/acpica/acparser.h
@@ -1,45 +1,11 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/******************************************************************************
*
* Module Name: acparser.h - AML Parser subcomponent prototypes and defines
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#ifndef __ACPARSER_H__
#define __ACPARSER_H__
diff --git a/drivers/acpi/acpica/acpredef.h b/drivers/acpi/acpica/acpredef.h
index 7c27bcee6ac7..d31bb04facb6 100644
--- a/drivers/acpi/acpica/acpredef.h
+++ b/drivers/acpi/acpica/acpredef.h
@@ -1,45 +1,11 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/******************************************************************************
*
* Name: acpredef - Information table for ACPI predefined methods and objects
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#ifndef __ACPREDEF_H__
#define __ACPREDEF_H__
diff --git a/drivers/acpi/acpica/acresrc.h b/drivers/acpi/acpica/acresrc.h
index 20f36949928a..59ae8b1a6e40 100644
--- a/drivers/acpi/acpica/acresrc.h
+++ b/drivers/acpi/acpica/acresrc.h
@@ -1,45 +1,11 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/******************************************************************************
*
* Name: acresrc.h - Resource Manager function prototypes
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#ifndef __ACRESRC_H__
#define __ACRESRC_H__
diff --git a/drivers/acpi/acpica/acstruct.h b/drivers/acpi/acpica/acstruct.h
index 0338ac32f9c6..acf27156dbd4 100644
--- a/drivers/acpi/acpica/acstruct.h
+++ b/drivers/acpi/acpica/acstruct.h
@@ -1,45 +1,11 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/******************************************************************************
*
* Name: acstruct.h - Internal structs
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#ifndef __ACSTRUCT_H__
#define __ACSTRUCT_H__
diff --git a/drivers/acpi/acpica/actables.h b/drivers/acpi/acpica/actables.h
index 15b23414245a..12fac33ce77e 100644
--- a/drivers/acpi/acpica/actables.h
+++ b/drivers/acpi/acpica/actables.h
@@ -1,45 +1,11 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/******************************************************************************
*
* Name: actables.h - ACPI table management
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#ifndef __ACTABLES_H__
#define __ACTABLES_H__
diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h
index 00d21d2f766e..2733cd4e418c 100644
--- a/drivers/acpi/acpica/acutils.h
+++ b/drivers/acpi/acpica/acutils.h
@@ -1,45 +1,11 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/******************************************************************************
*
* Name: acutils.h -- prototypes for the common (subsystem-wide) procedures
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#ifndef _ACUTILS_H
#define _ACUTILS_H
diff --git a/drivers/acpi/acpica/amlcode.h b/drivers/acpi/acpica/amlcode.h
index b0e9492a6297..250dba02bab6 100644
--- a/drivers/acpi/acpica/amlcode.h
+++ b/drivers/acpi/acpica/amlcode.h
@@ -1,47 +1,13 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/******************************************************************************
*
* Name: amlcode.h - Definitions for AML, as included in "definition blocks"
* Declarations and definitions contained herein are derived
* directly from the ACPI specification.
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
*
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#ifndef __AMLCODE_H__
#define __AMLCODE_H__
@@ -274,23 +240,9 @@
#define ARGI_DEVICE_REF 0x0D
#define ARGI_REFERENCE 0x0E
#define ARGI_TARGETREF 0x0F /* Target, subject to implicit conversion */
-#define ARGI_SIMPLE_TARGET 0x10 /* Name, Local, Arg -- no implicit conversion */
-#define ARGI_STORE_TARGET 0x11 /* Target for store is TARGETREF + package objects */
-/*
- * #define ARGI_FIXED_TARGET 0x10 Target, no implicit conversion
- *
- * Removed 10/2016. ARGI_FIXED_TARGET was used for these operators:
- * from_BCD
- * to_BCD
- * to_decimal_string
- * to_hex_string
- * to_integer
- * to_buffer
- * The purpose of this type was to disable "implicit result conversion",
- * but this was incorrect per the ACPI spec and other ACPI implementations.
- * These operators now have the target operand defined as a normal
- * ARGI_TARGETREF.
- */
+#define ARGI_FIXED_TARGET 0x10 /* Target, no implicit conversion */
+#define ARGI_SIMPLE_TARGET 0x11 /* Name, Local, Arg -- no implicit conversion */
+#define ARGI_STORE_TARGET 0x12 /* Target for store is TARGETREF + package objects */
/* Multiple/complex types */
diff --git a/drivers/acpi/acpica/amlresrc.h b/drivers/acpi/acpica/amlresrc.h
index b680c229ddd5..cdb590176e9d 100644
--- a/drivers/acpi/acpica/amlresrc.h
+++ b/drivers/acpi/acpica/amlresrc.h
@@ -1,45 +1,11 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/******************************************************************************
*
* Module Name: amlresrc.h - AML resource descriptors
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
/* acpisrc:struct_defs -- for acpisrc conversion */
diff --git a/drivers/acpi/acpica/dbcmds.c b/drivers/acpi/acpica/dbcmds.c
index 4112c85f2aab..9eb68e0751c7 100644
--- a/drivers/acpi/acpica/dbcmds.c
+++ b/drivers/acpi/acpica/dbcmds.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: dbcmds - Miscellaneous debug commands and output routines
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acevents.h"
diff --git a/drivers/acpi/acpica/dbconvert.c b/drivers/acpi/acpica/dbconvert.c
index 27236a6c51ff..9fd9a98a9cbe 100644
--- a/drivers/acpi/acpica/dbconvert.c
+++ b/drivers/acpi/acpica/dbconvert.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: dbconvert - debugger miscellaneous conversion routines
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acdebug.h"
diff --git a/drivers/acpi/acpica/dbdisply.c b/drivers/acpi/acpica/dbdisply.c
index 7df920cda77d..9fcb8ec64681 100644
--- a/drivers/acpi/acpica/dbdisply.c
+++ b/drivers/acpi/acpica/dbdisply.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: dbdisply - debug display commands
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "amlcode.h"
@@ -642,9 +606,8 @@ void acpi_db_display_object_type(char *object_arg)
return;
}
- acpi_os_printf("ADR: %8.8X%8.8X, STA: %8.8X, Flags: %X\n",
- ACPI_FORMAT_UINT64(info->address),
- info->current_status, info->flags);
+ acpi_os_printf("ADR: %8.8X%8.8X, Flags: %X\n",
+ ACPI_FORMAT_UINT64(info->address), info->flags);
acpi_os_printf("S1D-%2.2X S2D-%2.2X S3D-%2.2X S4D-%2.2X\n",
info->highest_dstates[0], info->highest_dstates[1],
diff --git a/drivers/acpi/acpica/dbexec.c b/drivers/acpi/acpica/dbexec.c
index 8ad9e6d9e54b..6abb6b834d97 100644
--- a/drivers/acpi/acpica/dbexec.c
+++ b/drivers/acpi/acpica/dbexec.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: dbexec - debugger control method execution
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acdebug.h"
diff --git a/drivers/acpi/acpica/dbfileio.c b/drivers/acpi/acpica/dbfileio.c
index 084bb332f8e2..c6e25734dc5c 100644
--- a/drivers/acpi/acpica/dbfileio.c
+++ b/drivers/acpi/acpica/dbfileio.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: dbfileio - Debugger file I/O commands. These can't usually
@@ -5,43 +6,6 @@
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acdebug.h"
diff --git a/drivers/acpi/acpica/dbhistry.c b/drivers/acpi/acpica/dbhistry.c
index 55c0f2742339..b0b9a26c7db5 100644
--- a/drivers/acpi/acpica/dbhistry.c
+++ b/drivers/acpi/acpica/dbhistry.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: dbhistry - debugger HISTORY command
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/dbinput.c b/drivers/acpi/acpica/dbinput.c
index f7c661e06f37..556ff59bbbfc 100644
--- a/drivers/acpi/acpica/dbinput.c
+++ b/drivers/acpi/acpica/dbinput.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: dbinput - user front-end to the AML debugger
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acdebug.h"
diff --git a/drivers/acpi/acpica/dbmethod.c b/drivers/acpi/acpica/dbmethod.c
index 2cda0bff6f2c..9fcecf104ba0 100644
--- a/drivers/acpi/acpica/dbmethod.c
+++ b/drivers/acpi/acpica/dbmethod.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: dbmethod - Debug commands for control methods
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acdispat.h"
diff --git a/drivers/acpi/acpica/dbnames.c b/drivers/acpi/acpica/dbnames.c
index 8796fc1e0360..170802c62179 100644
--- a/drivers/acpi/acpica/dbnames.c
+++ b/drivers/acpi/acpica/dbnames.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: dbnames - Debugger commands for the acpi namespace
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acnamesp.h"
diff --git a/drivers/acpi/acpica/dbobject.c b/drivers/acpi/acpica/dbobject.c
index d2063cbab39a..58c3253b533a 100644
--- a/drivers/acpi/acpica/dbobject.c
+++ b/drivers/acpi/acpica/dbobject.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: dbobject - ACPI object decode and display
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acnamesp.h"
diff --git a/drivers/acpi/acpica/dbstats.c b/drivers/acpi/acpica/dbstats.c
index d6aaef54e369..bf620937c79b 100644
--- a/drivers/acpi/acpica/dbstats.c
+++ b/drivers/acpi/acpica/dbstats.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: dbstats - Generation and display of ACPI table statistics
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acdebug.h"
diff --git a/drivers/acpi/acpica/dbtest.c b/drivers/acpi/acpica/dbtest.c
index 56e446b89d18..3892680a5258 100644
--- a/drivers/acpi/acpica/dbtest.c
+++ b/drivers/acpi/acpica/dbtest.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: dbtest - Various debug-related tests
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acdebug.h"
diff --git a/drivers/acpi/acpica/dbutils.c b/drivers/acpi/acpica/dbutils.c
index cd40854ee9be..58b039dd7d90 100644
--- a/drivers/acpi/acpica/dbutils.c
+++ b/drivers/acpi/acpica/dbutils.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: dbutils - AML debugger utilities
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acnamesp.h"
diff --git a/drivers/acpi/acpica/dbxface.c b/drivers/acpi/acpica/dbxface.c
index 77bbfa97cf91..4647aa8efecb 100644
--- a/drivers/acpi/acpica/dbxface.c
+++ b/drivers/acpi/acpica/dbxface.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: dbxface - AML Debugger external interfaces
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "amlcode.h"
diff --git a/drivers/acpi/acpica/dsargs.c b/drivers/acpi/acpica/dsargs.c
index 04a9f60e7ad1..6b15625e8099 100644
--- a/drivers/acpi/acpica/dsargs.c
+++ b/drivers/acpi/acpica/dsargs.c
@@ -1,46 +1,12 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: dsargs - Support for execution of dynamic arguments for static
* objects (regions, fields, buffer fields, etc.)
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
@@ -346,6 +312,7 @@ acpi_status acpi_ds_get_package_arguments(union acpi_operand_object *obj_desc)
status = acpi_ds_execute_arguments(node, node,
obj_desc->package.aml_length,
obj_desc->package.aml_start);
+
return_ACPI_STATUS(status);
}
diff --git a/drivers/acpi/acpica/dscontrol.c b/drivers/acpi/acpica/dscontrol.c
index 606697e741a5..0da96268deb5 100644
--- a/drivers/acpi/acpica/dscontrol.c
+++ b/drivers/acpi/acpica/dscontrol.c
@@ -1,46 +1,12 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: dscontrol - Support for execution control opcodes -
* if/else/while/return
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/dsdebug.c b/drivers/acpi/acpica/dsdebug.c
index 14ec52eba408..70a2fca60306 100644
--- a/drivers/acpi/acpica/dsdebug.c
+++ b/drivers/acpi/acpica/dsdebug.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: dsdebug - Parser/Interpreter interface - debugging
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/dsfield.c b/drivers/acpi/acpica/dsfield.c
index 95ea639a9424..7c937595dfcb 100644
--- a/drivers/acpi/acpica/dsfield.c
+++ b/drivers/acpi/acpica/dsfield.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: dsfield - Dispatcher field routines
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/dsinit.c b/drivers/acpi/acpica/dsinit.c
index 946ff2e130d9..e8de1b0ce2f5 100644
--- a/drivers/acpi/acpica/dsinit.c
+++ b/drivers/acpi/acpica/dsinit.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: dsinit - Object initialization namespace walk
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/dsmethod.c b/drivers/acpi/acpica/dsmethod.c
index b9c460c2d763..dd4deb678d13 100644
--- a/drivers/acpi/acpica/dsmethod.c
+++ b/drivers/acpi/acpica/dsmethod.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: dsmethod - Parser/Interpreter interface - control method parsing
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/dsmthdat.c b/drivers/acpi/acpica/dsmthdat.c
index 157f1645d91a..eca50517ad82 100644
--- a/drivers/acpi/acpica/dsmthdat.c
+++ b/drivers/acpi/acpica/dsmthdat.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: dsmthdat - control method arguments and local variables
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acdispat.h"
diff --git a/drivers/acpi/acpica/dsobject.c b/drivers/acpi/acpica/dsobject.c
index 4fa3400a95ba..6992c8d5ab43 100644
--- a/drivers/acpi/acpica/dsobject.c
+++ b/drivers/acpi/acpica/dsobject.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: dsobject - Dispatcher object management routines
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/dsopcode.c b/drivers/acpi/acpica/dsopcode.c
index 0181cd317751..e9fb0bf3c8d2 100644
--- a/drivers/acpi/acpica/dsopcode.c
+++ b/drivers/acpi/acpica/dsopcode.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: dsopcode - Dispatcher support for regions and fields
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
@@ -603,8 +569,8 @@ acpi_ds_eval_data_object_operands(struct acpi_walk_state *walk_state,
if (!op->common.value.arg) {
ACPI_ERROR((AE_INFO,
- "Dispatch: Missing child while executing TermArg for %X",
- op->common.aml_opcode));
+ "Missing child while evaluating opcode %4.4X, Op %p",
+ op->common.aml_opcode, op));
return_ACPI_STATUS(AE_OK);
}
diff --git a/drivers/acpi/acpica/dspkginit.c b/drivers/acpi/acpica/dspkginit.c
index 902bee78036c..d703a5594a02 100644
--- a/drivers/acpi/acpica/dspkginit.c
+++ b/drivers/acpi/acpica/dspkginit.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: dspkginit - Completion of deferred package initialization
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
@@ -47,6 +13,7 @@
#include "amlcode.h"
#include "acdispat.h"
#include "acinterp.h"
+#include "acparser.h"
#define _COMPONENT ACPI_NAMESPACE
ACPI_MODULE_NAME("dspkginit")
@@ -94,12 +61,19 @@ acpi_ds_build_internal_package_obj(struct acpi_walk_state *walk_state,
union acpi_parse_object *parent;
union acpi_operand_object *obj_desc = NULL;
acpi_status status = AE_OK;
+ u8 module_level_code = FALSE;
u16 reference_count;
u32 index;
u32 i;
ACPI_FUNCTION_TRACE(ds_build_internal_package_obj);
+ /* Check if we are executing module level code */
+
+ if (walk_state->parse_flags & ACPI_PARSE_MODULE_LEVEL) {
+ module_level_code = TRUE;
+ }
+
/* Find the parent of a possibly nested package */
parent = op->common.parent;
@@ -130,24 +104,44 @@ acpi_ds_build_internal_package_obj(struct acpi_walk_state *walk_state,
/*
* Allocate the element array (array of pointers to the individual
- * objects) based on the num_elements parameter. Add an extra pointer slot
- * so that the list is always null terminated.
+ * objects) if necessary. the count is based on the num_elements
+ * parameter. Add an extra pointer slot so that the list is always
+ * null terminated.
*/
- obj_desc->package.elements = ACPI_ALLOCATE_ZEROED(((acpi_size)
- element_count +
- 1) * sizeof(void *));
-
if (!obj_desc->package.elements) {
- acpi_ut_delete_object_desc(obj_desc);
- return_ACPI_STATUS(AE_NO_MEMORY);
+ obj_desc->package.elements = ACPI_ALLOCATE_ZEROED(((acpi_size)
+ element_count
+ +
+ 1) *
+ sizeof(void
+ *));
+
+ if (!obj_desc->package.elements) {
+ acpi_ut_delete_object_desc(obj_desc);
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ obj_desc->package.count = element_count;
}
- obj_desc->package.count = element_count;
+ /* First arg is element count. Second arg begins the initializer list */
+
arg = op->common.value.arg;
arg = arg->common.next;
- if (arg) {
- obj_desc->package.flags |= AOPOBJ_DATA_VALID;
+ /*
+ * If we are executing module-level code, we will defer the
+ * full resolution of the package elements in order to support
+ * forward references from the elements. This provides
+ * compatibility with other ACPI implementations.
+ */
+ if (module_level_code) {
+ obj_desc->package.aml_start = walk_state->aml;
+ obj_desc->package.aml_length = 0;
+
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_PARSE,
+ "%s: Deferring resolution of Package elements\n",
+ ACPI_GET_FUNCTION_NAME));
}
/*
@@ -187,15 +181,19 @@ acpi_ds_build_internal_package_obj(struct acpi_walk_state *walk_state,
"****DS namepath not found"));
}
- /*
- * Initialize this package element. This function handles the
- * resolution of named references within the package.
- */
- acpi_ds_init_package_element(0,
- obj_desc->package.
- elements[i], NULL,
- &obj_desc->package.
- elements[i]);
+ if (!module_level_code) {
+ /*
+ * Initialize this package element. This function handles the
+ * resolution of named references within the package.
+ * Forward references from module-level code are deferred
+ * until all ACPI tables are loaded.
+ */
+ acpi_ds_init_package_element(0,
+ obj_desc->package.
+ elements[i], NULL,
+ &obj_desc->package.
+ elements[i]);
+ }
}
if (*obj_desc_ptr) {
@@ -265,15 +263,21 @@ acpi_ds_build_internal_package_obj(struct acpi_walk_state *walk_state,
* num_elements count.
*
* Note: this is not an error, the package is padded out
- * with NULLs.
+ * with NULLs as per the ACPI specification.
*/
- ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "Package List length (%u) smaller than NumElements "
- "count (%u), padded with null elements\n",
- i, element_count));
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_INFO,
+ "%s: Package List length (%u) smaller than NumElements "
+ "count (%u), padded with null elements\n",
+ ACPI_GET_FUNCTION_NAME, i,
+ element_count));
+ }
+
+ /* Module-level packages will be resolved later */
+
+ if (!module_level_code) {
+ obj_desc->package.flags |= AOPOBJ_DATA_VALID;
}
- obj_desc->package.flags |= AOPOBJ_DATA_VALID;
op->common.node = ACPI_CAST_PTR(struct acpi_namespace_node, obj_desc);
return_ACPI_STATUS(status);
}
@@ -351,11 +355,12 @@ static void
acpi_ds_resolve_package_element(union acpi_operand_object **element_ptr)
{
acpi_status status;
+ acpi_status status2;
union acpi_generic_state scope_info;
union acpi_operand_object *element = *element_ptr;
struct acpi_namespace_node *resolved_node;
struct acpi_namespace_node *original_node;
- char *external_path = NULL;
+ char *external_path = "";
acpi_object_type type;
ACPI_FUNCTION_TRACE(ds_resolve_package_element);
@@ -363,6 +368,10 @@ acpi_ds_resolve_package_element(union acpi_operand_object **element_ptr)
/* Check if reference element is already resolved */
if (element->reference.resolved) {
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_PARSE,
+ "%s: Package element is already resolved\n",
+ ACPI_GET_FUNCTION_NAME));
+
return_VOID;
}
@@ -370,20 +379,46 @@ acpi_ds_resolve_package_element(union acpi_operand_object **element_ptr)
scope_info.scope.node = element->reference.node; /* Prefix node */
- status = acpi_ns_lookup(&scope_info, (char *)element->reference.aml, /* Pointer to AML path */
+ status = acpi_ns_lookup(&scope_info, (char *)element->reference.aml,
ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE,
ACPI_NS_SEARCH_PARENT | ACPI_NS_DONT_OPEN_SCOPE,
NULL, &resolved_node);
if (ACPI_FAILURE(status)) {
- status = acpi_ns_externalize_name(ACPI_UINT32_MAX,
- (char *)element->reference.
- aml, NULL, &external_path);
+ if ((status == AE_NOT_FOUND)
+ && acpi_gbl_ignore_package_resolution_errors) {
+ /*
+ * Optionally be silent about the NOT_FOUND case for the referenced
+ * name. Although this is potentially a serious problem,
+ * it can generate a lot of noise/errors on platforms whose
+ * firmware carries around a bunch of unused Package objects.
+ * To disable these errors, set this global to TRUE:
+ * acpi_gbl_ignore_package_resolution_errors
+ *
+ * If the AML actually tries to use such a package, the unresolved
+ * element(s) will be replaced with NULL elements.
+ */
+
+ /* Referenced name not found, set the element to NULL */
+
+ acpi_ut_remove_reference(*element_ptr);
+ *element_ptr = NULL;
+ return_VOID;
+ }
+
+ status2 = acpi_ns_externalize_name(ACPI_UINT32_MAX,
+ (char *)element->reference.
+ aml, NULL, &external_path);
ACPI_EXCEPTION((AE_INFO, status,
- "Could not find/resolve named package element: %s",
+ "While resolving a named reference package element - %s",
external_path));
+ if (ACPI_SUCCESS(status2)) {
+ ACPI_FREE(external_path);
+ }
+
+ /* Could not resolve name, set the element to NULL */
- ACPI_FREE(external_path);
+ acpi_ut_remove_reference(*element_ptr);
*element_ptr = NULL;
return_VOID;
} else if (resolved_node->type == ACPI_TYPE_ANY) {
@@ -397,23 +432,6 @@ acpi_ds_resolve_package_element(union acpi_operand_object **element_ptr)
*element_ptr = NULL;
return_VOID;
}
-#if 0
- else if (resolved_node->flags & ANOBJ_TEMPORARY) {
- /*
- * A temporary node found here indicates that the reference is
- * to a node that was created within this method. We are not
- * going to allow it (especially if the package is returned
- * from the method) -- the temporary node will be deleted out
- * from under the method. (05/2017).
- */
- ACPI_ERROR((AE_INFO,
- "Package element refers to a temporary name [%4.4s], "
- "inserting a NULL element",
- resolved_node->name.ascii));
- *element_ptr = NULL;
- return_VOID;
- }
-#endif
/*
* Special handling for Alias objects. We need resolved_node to point
@@ -449,20 +467,6 @@ acpi_ds_resolve_package_element(union acpi_operand_object **element_ptr)
if (ACPI_FAILURE(status)) {
return_VOID;
}
-#if 0
-/* TBD - alias support */
- /*
- * Special handling for Alias objects. We need to setup the type
- * and the Op->Common.Node to point to the Alias target. Note,
- * Alias has at most one level of indirection internally.
- */
- type = op->common.node->type;
- if (type == ACPI_TYPE_LOCAL_ALIAS) {
- type = obj_desc->common.type;
- op->common.node = ACPI_CAST_PTR(struct acpi_namespace_node,
- op->common.node->object);
- }
-#endif
switch (type) {
/*
diff --git a/drivers/acpi/acpica/dsutils.c b/drivers/acpi/acpica/dsutils.c
index a4ce0b4a55a6..8d1b75400515 100644
--- a/drivers/acpi/acpica/dsutils.c
+++ b/drivers/acpi/acpica/dsutils.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: dsutils - Dispatcher utilities
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acparser.h"
diff --git a/drivers/acpi/acpica/dswexec.c b/drivers/acpi/acpica/dswexec.c
index 2c07d220a50f..1504b93cc5f4 100644
--- a/drivers/acpi/acpica/dswexec.c
+++ b/drivers/acpi/acpica/dswexec.c
@@ -1,46 +1,12 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: dswexec - Dispatcher method execution callbacks;
* dispatch to interpreter.
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
@@ -576,8 +542,10 @@ acpi_status acpi_ds_exec_end_op(struct acpi_walk_state *walk_state)
case AML_TYPE_CREATE_OBJECT:
ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
- "Executing CreateObject (Buffer/Package) Op=%p AMLPtr=%p\n",
- op, op->named.data));
+ "Executing CreateObject (Buffer/Package) Op=%p Child=%p ParentOpcode=%4.4X\n",
+ op, op->named.value.arg,
+ op->common.parent->common.
+ aml_opcode));
switch (op->common.parent->common.aml_opcode) {
case AML_NAME_OP:
diff --git a/drivers/acpi/acpica/dswload.c b/drivers/acpi/acpica/dswload.c
index fa4ef9229e17..d06c41446282 100644
--- a/drivers/acpi/acpica/dswload.c
+++ b/drivers/acpi/acpica/dswload.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: dswload - Dispatcher first pass namespace load callbacks
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
@@ -147,7 +113,7 @@ acpi_ds_load1_begin_op(struct acpi_walk_state *walk_state,
char *path;
u32 flags;
- ACPI_FUNCTION_TRACE(ds_load1_begin_op);
+ ACPI_FUNCTION_TRACE_PTR(ds_load1_begin_op, walk_state->op);
op = walk_state->op;
ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, "Op=%p State=%p\n", op,
diff --git a/drivers/acpi/acpica/dswload2.c b/drivers/acpi/acpica/dswload2.c
index 3b1313ba60d0..b4685bb5f071 100644
--- a/drivers/acpi/acpica/dswload2.c
+++ b/drivers/acpi/acpica/dswload2.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: dswload2 - Dispatcher second pass namespace load callbacks
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/dswscope.c b/drivers/acpi/acpica/dswscope.c
index 8b5c3613c060..d1422f984f6e 100644
--- a/drivers/acpi/acpica/dswscope.c
+++ b/drivers/acpi/acpica/dswscope.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: dswscope - Scope stack manipulation
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/dswstate.c b/drivers/acpi/acpica/dswstate.c
index ee002d17526e..c879380e5ce1 100644
--- a/drivers/acpi/acpica/dswstate.c
+++ b/drivers/acpi/acpica/dswstate.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: dswstate - Dispatcher parse tree walk management routines
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/evevent.c b/drivers/acpi/acpica/evevent.c
index 4b2b0b44a16b..b3d07cc14d75 100644
--- a/drivers/acpi/acpica/evevent.c
+++ b/drivers/acpi/acpica/evevent.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: evevent - Fixed Event handling and dispatch
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
*
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
@@ -204,6 +170,7 @@ u32 acpi_ev_fixed_event_detect(void)
u32 fixed_status;
u32 fixed_enable;
u32 i;
+ acpi_status status;
ACPI_FUNCTION_NAME(ev_fixed_event_detect);
@@ -211,8 +178,12 @@ u32 acpi_ev_fixed_event_detect(void)
* Read the fixed feature status and enable registers, as all the cases
* depend on their values. Ignore errors here.
*/
- (void)acpi_hw_register_read(ACPI_REGISTER_PM1_STATUS, &fixed_status);
- (void)acpi_hw_register_read(ACPI_REGISTER_PM1_ENABLE, &fixed_enable);
+ status = acpi_hw_register_read(ACPI_REGISTER_PM1_STATUS, &fixed_status);
+ status |=
+ acpi_hw_register_read(ACPI_REGISTER_PM1_ENABLE, &fixed_enable);
+ if (ACPI_FAILURE(status)) {
+ return (int_status);
+ }
ACPI_DEBUG_PRINT((ACPI_DB_INTERRUPTS,
"Fixed Event Block: Enable %08X Status %08X\n",
diff --git a/drivers/acpi/acpica/evglock.c b/drivers/acpi/acpica/evglock.c
index 012b80de1501..1b8a662a14a9 100644
--- a/drivers/acpi/acpica/evglock.c
+++ b/drivers/acpi/acpica/evglock.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: evglock - Global Lock support
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c
index 410a3907c051..abbd59063906 100644
--- a/drivers/acpi/acpica/evgpe.c
+++ b/drivers/acpi/acpica/evgpe.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: evgpe - General Purpose Event handling and dispatch
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
@@ -105,7 +71,7 @@ acpi_ev_update_gpe_enable_mask(struct acpi_gpe_event_info *gpe_event_info)
*
* RETURN: Status
*
- * DESCRIPTION: Clear a GPE of stale events and enable it.
+ * DESCRIPTION: Enable a GPE.
*
******************************************************************************/
@@ -115,13 +81,6 @@ acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info)
ACPI_FUNCTION_TRACE(ev_enable_gpe);
- /* Clear the GPE (of stale events) */
-
- status = acpi_hw_clear_gpe(gpe_event_info);
- if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);
- }
-
/* Enable the requested GPE */
status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_ENABLE);
@@ -381,17 +340,12 @@ struct acpi_gpe_event_info *acpi_ev_get_gpe_event_info(acpi_handle gpe_device,
u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info *gpe_xrupt_list)
{
- acpi_status status;
struct acpi_gpe_block_info *gpe_block;
struct acpi_namespace_node *gpe_device;
struct acpi_gpe_register_info *gpe_register_info;
struct acpi_gpe_event_info *gpe_event_info;
u32 gpe_number;
- struct acpi_gpe_handler_info *gpe_handler_info;
u32 int_status = ACPI_INTERRUPT_NOT_HANDLED;
- u8 enabled_status_byte;
- u64 status_reg;
- u64 enable_reg;
acpi_cpu_flags flags;
u32 i;
u32 j;
@@ -448,49 +402,11 @@ u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info *gpe_xrupt_list)
continue;
}
- /* Read the Status Register */
-
- status =
- acpi_hw_read(&status_reg,
- &gpe_register_info->status_address);
- if (ACPI_FAILURE(status)) {
- goto unlock_and_exit;
- }
-
- /* Read the Enable Register */
-
- status =
- acpi_hw_read(&enable_reg,
- &gpe_register_info->enable_address);
- if (ACPI_FAILURE(status)) {
- goto unlock_and_exit;
- }
-
- ACPI_DEBUG_PRINT((ACPI_DB_INTERRUPTS,
- "Read registers for GPE %02X-%02X: Status=%02X, Enable=%02X, "
- "RunEnable=%02X, WakeEnable=%02X\n",
- gpe_register_info->base_gpe_number,
- gpe_register_info->base_gpe_number +
- (ACPI_GPE_REGISTER_WIDTH - 1),
- (u32)status_reg, (u32)enable_reg,
- gpe_register_info->enable_for_run,
- gpe_register_info->enable_for_wake));
-
- /* Check if there is anything active at all in this register */
-
- enabled_status_byte = (u8)(status_reg & enable_reg);
- if (!enabled_status_byte) {
-
- /* No active GPEs in this register, move on */
-
- continue;
- }
-
/* Now look at the individual GPEs in this byte register */
for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) {
- /* Examine one GPE bit */
+ /* Detect and dispatch one GPE bit */
gpe_event_info =
&gpe_block->
@@ -498,71 +414,18 @@ u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info *gpe_xrupt_list)
ACPI_GPE_REGISTER_WIDTH) + j];
gpe_number =
j + gpe_register_info->base_gpe_number;
-
- if (enabled_status_byte & (1 << j)) {
-
- /* Invoke global event handler if present */
-
- acpi_gpe_count++;
- if (acpi_gbl_global_event_handler) {
- acpi_gbl_global_event_handler
- (ACPI_EVENT_TYPE_GPE,
- gpe_device, gpe_number,
- acpi_gbl_global_event_handler_context);
- }
-
- /* Found an active GPE */
-
- if (ACPI_GPE_DISPATCH_TYPE
- (gpe_event_info->flags) ==
- ACPI_GPE_DISPATCH_RAW_HANDLER) {
-
- /* Dispatch the event to a raw handler */
-
- gpe_handler_info =
- gpe_event_info->dispatch.
- handler;
-
- /*
- * There is no protection around the namespace node
- * and the GPE handler to ensure a safe destruction
- * because:
- * 1. The namespace node is expected to always
- * exist after loading a table.
- * 2. The GPE handler is expected to be flushed by
- * acpi_os_wait_events_complete() before the
- * destruction.
- */
- acpi_os_release_lock
- (acpi_gbl_gpe_lock, flags);
- int_status |=
- gpe_handler_info->
- address(gpe_device,
- gpe_number,
- gpe_handler_info->
- context);
- flags =
- acpi_os_acquire_lock
- (acpi_gbl_gpe_lock);
- } else {
- /*
- * Dispatch the event to a standard handler or
- * method.
- */
- int_status |=
- acpi_ev_gpe_dispatch
- (gpe_device, gpe_event_info,
- gpe_number);
- }
- }
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+ int_status |=
+ acpi_ev_detect_gpe(gpe_device,
+ gpe_event_info,
+ gpe_number);
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
}
}
gpe_block = gpe_block->next;
}
-unlock_and_exit:
-
acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
return (int_status);
}
@@ -735,6 +598,127 @@ acpi_status acpi_ev_finish_gpe(struct acpi_gpe_event_info *gpe_event_info)
/*******************************************************************************
*
+ * FUNCTION: acpi_ev_detect_gpe
+ *
+ * PARAMETERS: gpe_device - Device node. NULL for GPE0/GPE1
+ * gpe_event_info - Info for this GPE
+ * gpe_number - Number relative to the parent GPE block
+ *
+ * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED
+ *
+ * DESCRIPTION: Detect and dispatch a General Purpose Event to either a function
+ * (e.g. EC) or method (e.g. _Lxx/_Exx) handler.
+ * NOTE: GPE is W1C, so it is possible to handle a single GPE from both
+ * task and irq context in parallel as long as the process to
+ * detect and mask the GPE is atomic.
+ * However the atomicity of ACPI_GPE_DISPATCH_RAW_HANDLER is
+ * dependent on the raw handler itself.
+ *
+ ******************************************************************************/
+
+u32
+acpi_ev_detect_gpe(struct acpi_namespace_node *gpe_device,
+ struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number)
+{
+ u32 int_status = ACPI_INTERRUPT_NOT_HANDLED;
+ u8 enabled_status_byte;
+ u64 status_reg;
+ u64 enable_reg;
+ u32 register_bit;
+ struct acpi_gpe_register_info *gpe_register_info;
+ struct acpi_gpe_handler_info *gpe_handler_info;
+ acpi_cpu_flags flags;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ev_gpe_detect);
+
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+
+ /* Get the info block for the entire GPE register */
+
+ gpe_register_info = gpe_event_info->register_info;
+
+ /* Get the register bitmask for this GPE */
+
+ register_bit = acpi_hw_get_gpe_register_bit(gpe_event_info);
+
+ /* GPE currently enabled (enable bit == 1)? */
+
+ status = acpi_hw_read(&enable_reg, &gpe_register_info->enable_address);
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }
+
+ /* GPE currently active (status bit == 1)? */
+
+ status = acpi_hw_read(&status_reg, &gpe_register_info->status_address);
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }
+
+ /* Check if there is anything active at all in this GPE */
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INTERRUPTS,
+ "Read registers for GPE %02X: Status=%02X, Enable=%02X, "
+ "RunEnable=%02X, WakeEnable=%02X\n",
+ gpe_number,
+ (u32)(status_reg & register_bit),
+ (u32)(enable_reg & register_bit),
+ gpe_register_info->enable_for_run,
+ gpe_register_info->enable_for_wake));
+
+ enabled_status_byte = (u8)(status_reg & enable_reg);
+ if (!(enabled_status_byte & register_bit)) {
+ goto error_exit;
+ }
+
+ /* Invoke global event handler if present */
+
+ acpi_gpe_count++;
+ if (acpi_gbl_global_event_handler) {
+ acpi_gbl_global_event_handler(ACPI_EVENT_TYPE_GPE,
+ gpe_device, gpe_number,
+ acpi_gbl_global_event_handler_context);
+ }
+
+ /* Found an active GPE */
+
+ if (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) ==
+ ACPI_GPE_DISPATCH_RAW_HANDLER) {
+
+ /* Dispatch the event to a raw handler */
+
+ gpe_handler_info = gpe_event_info->dispatch.handler;
+
+ /*
+ * There is no protection around the namespace node
+ * and the GPE handler to ensure a safe destruction
+ * because:
+ * 1. The namespace node is expected to always
+ * exist after loading a table.
+ * 2. The GPE handler is expected to be flushed by
+ * acpi_os_wait_events_complete() before the
+ * destruction.
+ */
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+ int_status |=
+ gpe_handler_info->address(gpe_device, gpe_number,
+ gpe_handler_info->context);
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+ } else {
+ /* Dispatch the event to a standard handler or method. */
+
+ int_status |= acpi_ev_gpe_dispatch(gpe_device,
+ gpe_event_info, gpe_number);
+ }
+
+error_exit:
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+ return (int_status);
+}
+
+/*******************************************************************************
+ *
* FUNCTION: acpi_ev_gpe_dispatch
*
* PARAMETERS: gpe_device - Device node. NULL for GPE0/GPE1
@@ -746,8 +730,6 @@ acpi_status acpi_ev_finish_gpe(struct acpi_gpe_event_info *gpe_event_info)
* DESCRIPTION: Dispatch a General Purpose Event to either a function (e.g. EC)
* or method (e.g. _Lxx/_Exx) handler.
*
- * This function executes at interrupt level.
- *
******************************************************************************/
u32
diff --git a/drivers/acpi/acpica/evgpeblk.c b/drivers/acpi/acpica/evgpeblk.c
index 7ce756cc28ab..b253063b09d3 100644
--- a/drivers/acpi/acpica/evgpeblk.c
+++ b/drivers/acpi/acpica/evgpeblk.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: evgpeblk - GPE block creation and initialization.
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
@@ -437,16 +403,16 @@ acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device,
acpi_status
acpi_ev_initialize_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
struct acpi_gpe_block_info *gpe_block,
- void *ignored)
+ void *context)
{
acpi_status status;
- acpi_event_status event_status;
struct acpi_gpe_event_info *gpe_event_info;
u32 gpe_enabled_count;
u32 gpe_index;
- u32 gpe_number;
u32 i;
u32 j;
+ u8 *is_polling_needed = context;
+ ACPI_ERROR_ONLY(u32 gpe_number);
ACPI_FUNCTION_TRACE(ev_initialize_gpe_block);
@@ -472,7 +438,10 @@ acpi_ev_initialize_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
gpe_index = (i * ACPI_GPE_REGISTER_WIDTH) + j;
gpe_event_info = &gpe_block->event_info[gpe_index];
- gpe_number = gpe_block->block_base_number + gpe_index;
+ ACPI_ERROR_ONLY(gpe_number =
+ gpe_block->block_base_number +
+ gpe_index);
+ gpe_event_info->flags |= ACPI_GPE_INITIALIZED;
/*
* Ignore GPEs that have no corresponding _Lxx/_Exx method
@@ -484,10 +453,6 @@ acpi_ev_initialize_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
continue;
}
- event_status = 0;
- (void)acpi_hw_get_gpe_status(gpe_event_info,
- &event_status);
-
status = acpi_ev_add_gpe_reference(gpe_event_info);
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status,
@@ -498,12 +463,9 @@ acpi_ev_initialize_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
gpe_event_info->flags |= ACPI_GPE_AUTO_ENABLED;
- if (event_status & ACPI_EVENT_FLAG_STATUS_SET) {
- ACPI_INFO(("GPE 0x%02X active on init",
- gpe_number));
- (void)acpi_ev_gpe_dispatch(gpe_block->node,
- gpe_event_info,
- gpe_number);
+ if (is_polling_needed &&
+ ACPI_GPE_IS_POLLING_NEEDED(gpe_event_info)) {
+ *is_polling_needed = TRUE;
}
gpe_enabled_count++;
diff --git a/drivers/acpi/acpica/evgpeinit.c b/drivers/acpi/acpica/evgpeinit.c
index 8ad4816c9950..1f686750bb1a 100644
--- a/drivers/acpi/acpica/evgpeinit.c
+++ b/drivers/acpi/acpica/evgpeinit.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: evgpeinit - System GPE initialization and update
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/evgpeutil.c b/drivers/acpi/acpica/evgpeutil.c
index 729a8960a3af..0fb6c70f44ed 100644
--- a/drivers/acpi/acpica/evgpeutil.c
+++ b/drivers/acpi/acpica/evgpeutil.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: evgpeutil - GPE utilities
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/evhandler.c b/drivers/acpi/acpica/evhandler.c
index 20fb51c06b8d..d319ee33d040 100644
--- a/drivers/acpi/acpica/evhandler.c
+++ b/drivers/acpi/acpica/evhandler.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: evhandler - Support for Address Space handlers
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/evmisc.c b/drivers/acpi/acpica/evmisc.c
index 40d0b1f541a0..baadd635b5af 100644
--- a/drivers/acpi/acpica/evmisc.c
+++ b/drivers/acpi/acpica/evmisc.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: evmisc - Miscellaneous event manager support functions
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/evregion.c b/drivers/acpi/acpica/evregion.c
index de196c8e3f30..70c2bd169f66 100644
--- a/drivers/acpi/acpica/evregion.c
+++ b/drivers/acpi/acpica/evregion.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: evregion - Operation Region support
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/evrgnini.c b/drivers/acpi/acpica/evrgnini.c
index 4187f563fede..39284deedd88 100644
--- a/drivers/acpi/acpica/evrgnini.c
+++ b/drivers/acpi/acpica/evrgnini.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: evrgnini- ACPI address_space (op_region) init
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
@@ -560,9 +526,12 @@ acpi_status acpi_ev_initialize_region(union acpi_operand_object *region_obj)
* Node's object was replaced by this Method object and we
* saved the handler in the method object.
*
+ * Note: Only used for the legacy MLC support. Will
+ * be removed in the future.
+ *
* See acpi_ns_exec_module_code
*/
- if (!acpi_gbl_parse_table_as_term_list &&
+ if (!acpi_gbl_execute_tables_as_methods &&
obj_desc->method.
info_flags & ACPI_METHOD_MODULE_LEVEL) {
handler_obj =
diff --git a/drivers/acpi/acpica/evsci.c b/drivers/acpi/acpica/evsci.c
index d5594f79f877..3915ff61412b 100644
--- a/drivers/acpi/acpica/evsci.c
+++ b/drivers/acpi/acpica/evsci.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: evsci - System Control Interrupt configuration and
@@ -5,43 +6,6 @@
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acevents.h"
diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c
index 9b3c01bf1438..febc332b00ac 100644
--- a/drivers/acpi/acpica/evxface.c
+++ b/drivers/acpi/acpica/evxface.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: evxface - External interfaces for ACPI events
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#define EXPORT_ACPI_INTERFACES
@@ -1006,6 +972,15 @@ acpi_remove_gpe_handler(acpi_handle gpe_device,
(ACPI_GPE_DISPATCH_TYPE(handler->original_flags) ==
ACPI_GPE_DISPATCH_NOTIFY)) && handler->originally_enabled) {
(void)acpi_ev_add_gpe_reference(gpe_event_info);
+ if (ACPI_GPE_IS_POLLING_NEEDED(gpe_event_info)) {
+
+ /* Poll edge triggered GPEs to handle existing events */
+
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+ (void)acpi_ev_detect_gpe(gpe_device, gpe_event_info,
+ gpe_number);
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+ }
}
acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
diff --git a/drivers/acpi/acpica/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c
index 96c2520f9570..970e940bdb17 100644
--- a/drivers/acpi/acpica/evxfevnt.c
+++ b/drivers/acpi/acpica/evxfevnt.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: evxfevnt - External Interfaces, ACPI event disable/enable
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#define EXPORT_ACPI_INTERFACES
diff --git a/drivers/acpi/acpica/evxfgpe.c b/drivers/acpi/acpica/evxfgpe.c
index cbb1598df9dc..c80e3bdf4805 100644
--- a/drivers/acpi/acpica/evxfgpe.c
+++ b/drivers/acpi/acpica/evxfgpe.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: evxfgpe - External Interfaces for General Purpose Events (GPEs)
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#define EXPORT_ACPI_INTERFACES
@@ -77,6 +43,7 @@ ACPI_MODULE_NAME("evxfgpe")
acpi_status acpi_update_all_gpes(void)
{
acpi_status status;
+ u8 is_polling_needed = FALSE;
ACPI_FUNCTION_TRACE(acpi_update_all_gpes);
@@ -89,7 +56,8 @@ acpi_status acpi_update_all_gpes(void)
goto unlock_and_exit;
}
- status = acpi_ev_walk_gpe_list(acpi_ev_initialize_gpe_block, NULL);
+ status = acpi_ev_walk_gpe_list(acpi_ev_initialize_gpe_block,
+ &is_polling_needed);
if (ACPI_SUCCESS(status)) {
acpi_gbl_all_gpes_initialized = TRUE;
}
@@ -97,6 +65,12 @@ acpi_status acpi_update_all_gpes(void)
unlock_and_exit:
(void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+ if (is_polling_needed && acpi_gbl_all_gpes_initialized) {
+
+ /* Poll GPEs to handle already triggered events */
+
+ acpi_ev_gpe_detect(acpi_gbl_gpe_xrupt_list_head);
+ }
return_ACPI_STATUS(status);
}
@@ -135,6 +109,17 @@ acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number)
if (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) !=
ACPI_GPE_DISPATCH_NONE) {
status = acpi_ev_add_gpe_reference(gpe_event_info);
+ if (ACPI_SUCCESS(status) &&
+ ACPI_GPE_IS_POLLING_NEEDED(gpe_event_info)) {
+
+ /* Poll edge-triggered GPEs to handle existing events */
+
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+ (void)acpi_ev_detect_gpe(gpe_device,
+ gpe_event_info,
+ gpe_number);
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+ }
} else {
status = AE_NO_HANDLER;
}
diff --git a/drivers/acpi/acpica/evxfregn.c b/drivers/acpi/acpica/evxfregn.c
index 705fcd86151a..091415b14fbf 100644
--- a/drivers/acpi/acpica/evxfregn.c
+++ b/drivers/acpi/acpica/evxfregn.c
@@ -1,46 +1,12 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: evxfregn - External Interfaces, ACPI Operation Regions and
* Address Spaces.
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#define EXPORT_ACPI_INTERFACES
diff --git a/drivers/acpi/acpica/exconcat.c b/drivers/acpi/acpica/exconcat.c
index ea20e10dd1f2..5e75c510ca25 100644
--- a/drivers/acpi/acpica/exconcat.c
+++ b/drivers/acpi/acpica/exconcat.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: exconcat - Concatenate-type AML operators
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/exconfig.c b/drivers/acpi/acpica/exconfig.c
index 827f47b72663..99d92cb32803 100644
--- a/drivers/acpi/acpica/exconfig.c
+++ b/drivers/acpi/acpica/exconfig.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: exconfig - Namespace reconfiguration (Load/Unload opcodes)
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/exconvrt.c b/drivers/acpi/acpica/exconvrt.c
index 9abcc41a573b..98de48481776 100644
--- a/drivers/acpi/acpica/exconvrt.c
+++ b/drivers/acpi/acpica/exconvrt.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: exconvrt - Object conversion routines
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
@@ -598,6 +564,7 @@ acpi_ex_convert_to_target_type(acpi_object_type destination_type,
*/
switch (GET_CURRENT_ARG_TYPE(walk_state->op_info->runtime_args)) {
case ARGI_SIMPLE_TARGET:
+ case ARGI_FIXED_TARGET:
case ARGI_INTEGER_REF: /* Handles Increment, Decrement cases */
switch (destination_type) {
diff --git a/drivers/acpi/acpica/excreate.c b/drivers/acpi/acpica/excreate.c
index 3dece45dd997..e49fa3c1321a 100644
--- a/drivers/acpi/acpica/excreate.c
+++ b/drivers/acpi/acpica/excreate.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: excreate - Named object creation
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/exdebug.c b/drivers/acpi/acpica/exdebug.c
index d931a66a16e3..ebbc244039ab 100644
--- a/drivers/acpi/acpica/exdebug.c
+++ b/drivers/acpi/acpica/exdebug.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: exdebug - Support for stores to the AML Debug Object
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
@@ -88,14 +54,13 @@ acpi_ex_do_debug_object(union acpi_operand_object *source_desc,
return_VOID;
}
- /* Null string or newline -- don't emit the line header */
+ /* Newline -- don't emit the line header */
if (source_desc &&
(ACPI_GET_DESCRIPTOR_TYPE(source_desc) == ACPI_DESC_TYPE_OPERAND) &&
(source_desc->common.type == ACPI_TYPE_STRING)) {
- if ((source_desc->string.length == 0) ||
- ((source_desc->string.length == 1) &&
- (*source_desc->string.pointer == '\n'))) {
+ if ((source_desc->string.length == 1) &&
+ (*source_desc->string.pointer == '\n')) {
acpi_os_printf("\n");
return_VOID;
}
diff --git a/drivers/acpi/acpica/exdump.c b/drivers/acpi/acpica/exdump.c
index 4989ce9591ae..f71dfa1e90e1 100644
--- a/drivers/acpi/acpica/exdump.c
+++ b/drivers/acpi/acpica/exdump.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: exdump - Interpreter debug output routines
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/exfield.c b/drivers/acpi/acpica/exfield.c
index e3b0650e5bb6..b272c329d45d 100644
--- a/drivers/acpi/acpica/exfield.c
+++ b/drivers/acpi/acpica/exfield.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: exfield - ACPI AML (p-code) execution - field manipulation
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/exfldio.c b/drivers/acpi/acpica/exfldio.c
index 3d0f274be88b..516994133128 100644
--- a/drivers/acpi/acpica/exfldio.c
+++ b/drivers/acpi/acpica/exfldio.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: exfldio - Aml Field I/O
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/exmisc.c b/drivers/acpi/acpica/exmisc.c
index 1518fcb22ae1..d91f15cdf3ae 100644
--- a/drivers/acpi/acpica/exmisc.c
+++ b/drivers/acpi/acpica/exmisc.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: exmisc - ACPI AML (p-code) execution - specific opcodes
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/exmutex.c b/drivers/acpi/acpica/exmutex.c
index 24c9741dee48..c06079774bad 100644
--- a/drivers/acpi/acpica/exmutex.c
+++ b/drivers/acpi/acpica/exmutex.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: exmutex - ASL Mutex Acquire/Release functions
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/exnames.c b/drivers/acpi/acpica/exnames.c
index c7b249cda5c0..7eed79dcda83 100644
--- a/drivers/acpi/acpica/exnames.c
+++ b/drivers/acpi/acpica/exnames.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: exnames - interpreter/scanner name load/execute
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
*
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
@@ -174,13 +140,10 @@ static acpi_status acpi_ex_name_segment(u8 ** in_aml_address, char *name_string)
return_ACPI_STATUS(AE_CTRL_PENDING);
}
- ACPI_DEBUG_PRINT((ACPI_DB_LOAD, "Bytes from stream:\n"));
-
for (index = 0;
(index < ACPI_NAME_SIZE)
&& (acpi_ut_valid_name_char(*aml_address, 0)); index++) {
char_buf[index] = *aml_address++;
- ACPI_DEBUG_PRINT((ACPI_DB_LOAD, "%c\n", char_buf[index]));
}
/* Valid name segment */
@@ -192,9 +155,9 @@ static acpi_status acpi_ex_name_segment(u8 ** in_aml_address, char *name_string)
char_buf[4] = '\0';
if (name_string) {
- strcat(name_string, char_buf);
ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
- "Appended to - %s\n", name_string));
+ "Appending NameSeg %s\n", char_buf));
+ strcat(name_string, char_buf);
} else {
ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
"No Name string - %s\n", char_buf));
diff --git a/drivers/acpi/acpica/exoparg1.c b/drivers/acpi/acpica/exoparg1.c
index dae01c93e480..ba9fbae0cf91 100644
--- a/drivers/acpi/acpica/exoparg1.c
+++ b/drivers/acpi/acpica/exoparg1.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: exoparg1 - AML execution - opcodes with 1 argument
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/exoparg2.c b/drivers/acpi/acpica/exoparg2.c
index 3cafa1d6f31a..d5b3efd35a5b 100644
--- a/drivers/acpi/acpica/exoparg2.c
+++ b/drivers/acpi/acpica/exoparg2.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: exoparg2 - AML execution - opcodes with 2 arguments
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/exoparg3.c b/drivers/acpi/acpica/exoparg3.c
index f16c655121ff..764fa6f924ff 100644
--- a/drivers/acpi/acpica/exoparg3.c
+++ b/drivers/acpi/acpica/exoparg3.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: exoparg3 - AML execution - opcodes with 3 arguments
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/exoparg6.c b/drivers/acpi/acpica/exoparg6.c
index 8b39fffce6dc..3941525f3d6b 100644
--- a/drivers/acpi/acpica/exoparg6.c
+++ b/drivers/acpi/acpica/exoparg6.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: exoparg6 - AML execution - opcodes with 6 arguments
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/exprep.c b/drivers/acpi/acpica/exprep.c
index 1d1040f2e3f8..738f3c732363 100644
--- a/drivers/acpi/acpica/exprep.c
+++ b/drivers/acpi/acpica/exprep.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: exprep - ACPI AML field prep utilities
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/exregion.c b/drivers/acpi/acpica/exregion.c
index 387c438aa485..97bbfd07fcf7 100644
--- a/drivers/acpi/acpica/exregion.c
+++ b/drivers/acpi/acpica/exregion.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: exregion - ACPI default op_region (address space) handlers
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/exresnte.c b/drivers/acpi/acpica/exresnte.c
index 77fa8d9aa5bf..ea4b0fe674f1 100644
--- a/drivers/acpi/acpica/exresnte.c
+++ b/drivers/acpi/acpica/exresnte.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: exresnte - AML Interpreter object resolution
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/exresolv.c b/drivers/acpi/acpica/exresolv.c
index b104bc3ca809..5e42c7de46fa 100644
--- a/drivers/acpi/acpica/exresolv.c
+++ b/drivers/acpi/acpica/exresolv.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: exresolv - AML Interpreter object resolution
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/exresop.c b/drivers/acpi/acpica/exresop.c
index 2643d34f194d..d94190bc5985 100644
--- a/drivers/acpi/acpica/exresop.c
+++ b/drivers/acpi/acpica/exresop.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: exresop - AML Interpreter operand/object resolution
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
@@ -305,6 +271,7 @@ acpi_ex_resolve_operands(u16 opcode,
case ARGI_OBJECT_REF:
case ARGI_DEVICE_REF:
case ARGI_TARGETREF: /* Allows implicit conversion rules before store */
+ case ARGI_FIXED_TARGET: /* No implicit conversion before store to target */
case ARGI_SIMPLE_TARGET: /* Name, Local, or arg - no implicit conversion */
case ARGI_STORE_TARGET:
diff --git a/drivers/acpi/acpica/exstore.c b/drivers/acpi/acpica/exstore.c
index 8f106bdcad5f..75d5665b7b2f 100644
--- a/drivers/acpi/acpica/exstore.c
+++ b/drivers/acpi/acpica/exstore.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: exstore - AML Interpreter object store support
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/exstoren.c b/drivers/acpi/acpica/exstoren.c
index 3d458d1996b0..31cba19652ed 100644
--- a/drivers/acpi/acpica/exstoren.c
+++ b/drivers/acpi/acpica/exstoren.c
@@ -1,46 +1,12 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: exstoren - AML Interpreter object store support,
* Store to Node (namespace object)
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/exstorob.c b/drivers/acpi/acpica/exstorob.c
index 905443a3c28f..4cd82ff509bc 100644
--- a/drivers/acpi/acpica/exstorob.c
+++ b/drivers/acpi/acpica/exstorob.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: exstorob - AML object store support, store to object
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/exsystem.c b/drivers/acpi/acpica/exsystem.c
index 420d9b145d2e..ec8b5a22cad4 100644
--- a/drivers/acpi/acpica/exsystem.c
+++ b/drivers/acpi/acpica/exsystem.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: exsystem - Interface to OS services
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/extrace.c b/drivers/acpi/acpica/extrace.c
index 9a67d507a132..9bd3fa56b51a 100644
--- a/drivers/acpi/acpica/extrace.c
+++ b/drivers/acpi/acpica/extrace.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: extrace - Support for interpreter execution tracing
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/exutils.c b/drivers/acpi/acpica/exutils.c
index fb80d3f55d63..6ce307d5ce2a 100644
--- a/drivers/acpi/acpica/exutils.c
+++ b/drivers/acpi/acpica/exutils.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: exutils - interpreter/scanner utilities
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
/*
* DEFINE_AML_GLOBALS is tested in amlcode.h
diff --git a/drivers/acpi/acpica/hwacpi.c b/drivers/acpi/acpica/hwacpi.c
index 68e958d4c25f..525e6ea5c114 100644
--- a/drivers/acpi/acpica/hwacpi.c
+++ b/drivers/acpi/acpica/hwacpi.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: hwacpi - ACPI Hardware Initialization/Mode Interface
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/hwesleep.c b/drivers/acpi/acpica/hwesleep.c
index 64855b62a5ae..e0ad3f11142e 100644
--- a/drivers/acpi/acpica/hwesleep.c
+++ b/drivers/acpi/acpica/hwesleep.c
@@ -1,46 +1,12 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Name: hwesleep.c - ACPI Hardware Sleep/Wake Support functions for the
* extended FADT-V5 sleep registers.
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/hwgpe.c b/drivers/acpi/acpica/hwgpe.c
index c1c54af148d0..2d2e2e41a685 100644
--- a/drivers/acpi/acpica/hwgpe.c
+++ b/drivers/acpi/acpica/hwgpe.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: hwgpe - Low level GPE enable/disable/clear functions
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
@@ -497,7 +463,6 @@ acpi_status acpi_hw_disable_all_gpes(void)
ACPI_FUNCTION_TRACE(hw_disable_all_gpes);
status = acpi_ev_walk_gpe_list(acpi_hw_disable_gpe_block, NULL);
- status = acpi_ev_walk_gpe_list(acpi_hw_clear_gpe_block, NULL);
return_ACPI_STATUS(status);
}
diff --git a/drivers/acpi/acpica/hwpci.c b/drivers/acpi/acpica/hwpci.c
index faa2fa45eb1c..3bd5ebc9c080 100644
--- a/drivers/acpi/acpica/hwpci.c
+++ b/drivers/acpi/acpica/hwpci.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: hwpci - Obtain PCI bus, device, and function numbers
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/hwregs.c b/drivers/acpi/acpica/hwregs.c
index f3e7b7851a3a..27a86ad55b58 100644
--- a/drivers/acpi/acpica/hwregs.c
+++ b/drivers/acpi/acpica/hwregs.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: hwregs - Read/write access functions for the various ACPI
@@ -5,43 +6,6 @@
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acevents.h"
diff --git a/drivers/acpi/acpica/hwsleep.c b/drivers/acpi/acpica/hwsleep.c
index c85c373ecbc4..fc0c2e2328cd 100644
--- a/drivers/acpi/acpica/hwsleep.c
+++ b/drivers/acpi/acpica/hwsleep.c
@@ -1,46 +1,12 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Name: hwsleep.c - ACPI Hardware Sleep/Wake Support functions for the
* original/legacy sleep/PM registers.
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
@@ -85,15 +51,8 @@ acpi_status acpi_hw_legacy_sleep(u8 sleep_state)
return_ACPI_STATUS(status);
}
- /* Clear all fixed and general purpose status bits */
-
- status = acpi_hw_clear_acpi_status();
- if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);
- }
-
/*
- * 1) Disable/Clear all GPEs
+ * 1) Disable all GPEs
* 2) Enable all wakeup GPEs
*/
status = acpi_hw_disable_all_gpes();
@@ -299,7 +258,7 @@ acpi_status acpi_hw_legacy_wake(u8 sleep_state)
* might get fired there
*
* Restore the GPEs:
- * 1) Disable/Clear all GPEs
+ * 1) Disable all GPEs
* 2) Enable all runtime GPEs
*/
status = acpi_hw_disable_all_gpes();
diff --git a/drivers/acpi/acpica/hwtimer.c b/drivers/acpi/acpica/hwtimer.c
index 511e3b8ffc6d..5d5e27146fc2 100644
--- a/drivers/acpi/acpica/hwtimer.c
+++ b/drivers/acpi/acpica/hwtimer.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Name: hwtimer.c - ACPI Power Management Timer Interface
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#define EXPORT_ACPI_INTERFACES
diff --git a/drivers/acpi/acpica/hwvalid.c b/drivers/acpi/acpica/hwvalid.c
index 65d82e6add0b..24f9b61aa404 100644
--- a/drivers/acpi/acpica/hwvalid.c
+++ b/drivers/acpi/acpica/hwvalid.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: hwvalid - I/O request validation
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/hwxface.c b/drivers/acpi/acpica/hwxface.c
index d320b129b7d7..5d1396870bd0 100644
--- a/drivers/acpi/acpica/hwxface.c
+++ b/drivers/acpi/acpica/hwxface.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: hwxface - Public ACPICA hardware interfaces
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#define EXPORT_ACPI_INTERFACES
diff --git a/drivers/acpi/acpica/hwxfsleep.c b/drivers/acpi/acpica/hwxfsleep.c
index 589c774bbf9a..3f22f7dd4556 100644
--- a/drivers/acpi/acpica/hwxfsleep.c
+++ b/drivers/acpi/acpica/hwxfsleep.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Name: hwxfsleep.c - ACPI Hardware Sleep/Wake External Interfaces
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#define EXPORT_ACPI_INTERFACES
@@ -209,7 +175,7 @@ acpi_status acpi_enter_sleep_state_s4bios(void)
}
/*
- * 1) Disable/Clear all GPEs
+ * 1) Disable all GPEs
* 2) Enable all wakeup GPEs
*/
status = acpi_hw_disable_all_gpes();
diff --git a/drivers/acpi/acpica/nsaccess.c b/drivers/acpi/acpica/nsaccess.c
index 07f672b5a1d1..220a718fbce9 100644
--- a/drivers/acpi/acpica/nsaccess.c
+++ b/drivers/acpi/acpica/nsaccess.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: nsaccess - Top-level functions for accessing ACPI namespace
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "amlcode.h"
diff --git a/drivers/acpi/acpica/nsalloc.c b/drivers/acpi/acpica/nsalloc.c
index ce57ccf4c1bf..5470213b8e64 100644
--- a/drivers/acpi/acpica/nsalloc.c
+++ b/drivers/acpi/acpica/nsalloc.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: nsalloc - Namespace allocation and deletion utilities
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acnamesp.h"
diff --git a/drivers/acpi/acpica/nsarguments.c b/drivers/acpi/acpica/nsarguments.c
index ce296ac14cf0..b9ede797b654 100644
--- a/drivers/acpi/acpica/nsarguments.c
+++ b/drivers/acpi/acpica/nsarguments.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: nsarguments - Validation of args for ACPI predefined methods
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/nsconvert.c b/drivers/acpi/acpica/nsconvert.c
index 2f9d5d190fa9..f9527346b0f7 100644
--- a/drivers/acpi/acpica/nsconvert.c
+++ b/drivers/acpi/acpica/nsconvert.c
@@ -1,46 +1,12 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: nsconvert - Object conversions for objects returned by
* predefined methods
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/nsdump.c b/drivers/acpi/acpica/nsdump.c
index e2ac16818dc3..4bdbd1d8431b 100644
--- a/drivers/acpi/acpica/nsdump.c
+++ b/drivers/acpi/acpica/nsdump.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: nsdump - table dumping routines for debug
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/nsdumpdv.c b/drivers/acpi/acpica/nsdumpdv.c
index 09ac00dee450..2b291c500fb0 100644
--- a/drivers/acpi/acpica/nsdumpdv.c
+++ b/drivers/acpi/acpica/nsdumpdv.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: nsdump - table dumping routines for debug
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
@@ -88,10 +54,9 @@ acpi_ns_dump_one_device(acpi_handle obj_handle,
}
ACPI_DEBUG_PRINT_RAW((ACPI_DB_TABLES,
- " HID: %s, ADR: %8.8X%8.8X, Status: %X\n",
+ " HID: %s, ADR: %8.8X%8.8X\n",
info->hardware_id.value,
- ACPI_FORMAT_UINT64(info->address),
- info->current_status));
+ ACPI_FORMAT_UINT64(info->address)));
ACPI_FREE(info);
}
diff --git a/drivers/acpi/acpica/nseval.c b/drivers/acpi/acpica/nseval.c
index c2d883b8c45e..64ba80ede0ad 100644
--- a/drivers/acpi/acpica/nseval.c
+++ b/drivers/acpi/acpica/nseval.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: nseval - Object evaluation, includes control method execution
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acparser.h"
@@ -187,6 +151,7 @@ acpi_status acpi_ns_evaluate(struct acpi_evaluate_info *info)
* 3) The object is not a method -- just return it's current value
*/
switch (acpi_ns_get_type(info->node)) {
+ case ACPI_TYPE_ANY:
case ACPI_TYPE_DEVICE:
case ACPI_TYPE_EVENT:
case ACPI_TYPE_MUTEX:
@@ -194,11 +159,12 @@ acpi_status acpi_ns_evaluate(struct acpi_evaluate_info *info)
case ACPI_TYPE_THERMAL:
case ACPI_TYPE_LOCAL_SCOPE:
/*
- * 1) Disallow evaluation of certain object types. For these,
- * object evaluation is undefined and not supported.
+ * 1) Disallow evaluation of these object types. For these,
+ * object evaluation is undefined.
*/
ACPI_ERROR((AE_INFO,
- "%s: Evaluation of object type [%s] is not supported",
+ "%s: This object type [%s] "
+ "never contains data and cannot be evaluated",
info->full_pathname,
acpi_ut_get_type_name(info->node->type)));
@@ -308,6 +274,14 @@ acpi_status acpi_ns_evaluate(struct acpi_evaluate_info *info)
/* Map AE_CTRL_RETURN_VALUE to AE_OK, we are done with it */
status = AE_OK;
+ } else if (ACPI_FAILURE(status)) {
+
+ /* If return_object exists, delete it */
+
+ if (info->return_object) {
+ acpi_ut_remove_reference(info->return_object);
+ info->return_object = NULL;
+ }
}
ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
@@ -336,6 +310,17 @@ cleanup:
* DESCRIPTION: Execute all elements of the global module-level code list.
* Each element is executed as a single control method.
*
+ * NOTE: With this option enabled, each block of detected executable AML
+ * code that is outside of any control method is wrapped with a temporary
+ * control method object and placed on a global list. The methods on this
+ * list are executed below.
+ *
+ * This function executes the module-level code for all tables only after
+ * all of the tables have been loaded. It is a legacy option and is
+ * not compatible with other ACPI implementations. See acpi_ns_load_table.
+ *
+ * This function will be removed when the legacy option is removed.
+ *
******************************************************************************/
void acpi_ns_exec_module_code_list(void)
@@ -351,6 +336,9 @@ void acpi_ns_exec_module_code_list(void)
next = acpi_gbl_module_code_list;
if (!next) {
+ ACPI_DEBUG_PRINT((ACPI_DB_INIT_NAMES,
+ "Legacy MLC block list is empty\n"));
+
return_VOID;
}
diff --git a/drivers/acpi/acpica/nsinit.c b/drivers/acpi/acpica/nsinit.c
index c17af4a3ab67..77f2b5f4948a 100644
--- a/drivers/acpi/acpica/nsinit.c
+++ b/drivers/acpi/acpica/nsinit.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: nsinit - namespace initialization
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
*
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
@@ -286,7 +252,7 @@ error_exit:
* RETURN: Status
*
* DESCRIPTION: Callback from acpi_walk_namespace. Invoked for every object
- * within the namespace.
+ * within the namespace.
*
* Currently, the only objects that require initialization are:
* 1) Methods
@@ -400,6 +366,10 @@ acpi_ns_init_one_object(acpi_handle obj_handle,
break;
}
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_PARSE,
+ "%s: Completing resolution of Package elements\n",
+ ACPI_GET_FUNCTION_NAME));
+
/*
* Resolve all named references in package objects (and all
* sub-packages). This action has been deferred until the entire
@@ -409,6 +379,7 @@ acpi_ns_init_one_object(acpi_handle obj_handle,
status = acpi_ut_walk_package_tree(obj_desc, NULL,
acpi_ds_init_package_element,
NULL);
+
obj_desc->package.flags |= AOPOBJ_DATA_VALID;
break;
diff --git a/drivers/acpi/acpica/nsload.c b/drivers/acpi/acpica/nsload.c
index fdfe9309bd33..e291bb8cd369 100644
--- a/drivers/acpi/acpica/nsload.c
+++ b/drivers/acpi/acpica/nsload.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: nsload - namespace loading/expanding/contracting procedures
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
*
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
@@ -145,23 +111,17 @@ unlock:
"**** Completed Table Object Initialization\n"));
/*
- * Execute any module-level code that was detected during the table load
- * phase. Although illegal since ACPI 2.0, there are many machines that
- * contain this type of code. Each block of detected executable AML code
- * outside of any control method is wrapped with a temporary control
- * method object and placed on a global list. The methods on this list
- * are executed below.
+ * This case handles the legacy option that groups all module-level
+ * code blocks together and defers execution until all of the tables
+ * are loaded. Execute all of these blocks at this time.
+ * Execute any module-level code that was detected during the table
+ * load phase.
*
- * This case executes the module-level code for each table immediately
- * after the table has been loaded. This provides compatibility with
- * other ACPI implementations. Optionally, the execution can be deferred
- * until later, see acpi_initialize_objects.
+ * Note: this option is deprecated and will be eliminated in the
+ * future. Use of this option can cause problems with AML code that
+ * depends upon in-order immediate execution of module-level code.
*/
- if (!acpi_gbl_parse_table_as_term_list
- && !acpi_gbl_group_module_level_code) {
- acpi_ns_exec_module_code_list();
- }
-
+ acpi_ns_exec_module_code_list();
return_ACPI_STATUS(status);
}
diff --git a/drivers/acpi/acpica/nsnames.c b/drivers/acpi/acpica/nsnames.c
index c686eda7ca66..289c15bb8c6a 100644
--- a/drivers/acpi/acpica/nsnames.c
+++ b/drivers/acpi/acpica/nsnames.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: nsnames - Name manipulation and search
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "amlcode.h"
@@ -354,6 +318,9 @@ char *acpi_ns_get_normalized_pathname(struct acpi_namespace_node *node,
(void)acpi_ns_build_normalized_path(node, name_buffer, size,
no_trailing);
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_NAMES, "%s: Path \"%s\"\n",
+ ACPI_GET_FUNCTION_NAME, name_buffer));
+
return_PTR(name_buffer);
}
diff --git a/drivers/acpi/acpica/nsobject.c b/drivers/acpi/acpica/nsobject.c
index 757e44555ec3..8638f43cfc3d 100644
--- a/drivers/acpi/acpica/nsobject.c
+++ b/drivers/acpi/acpica/nsobject.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: nsobject - Utilities for objects attached to namespace
@@ -5,43 +6,6 @@
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acnamesp.h"
diff --git a/drivers/acpi/acpica/nsparse.c b/drivers/acpi/acpica/nsparse.c
index c5b22ea5b369..c9ef4949869f 100644
--- a/drivers/acpi/acpica/nsparse.c
+++ b/drivers/acpi/acpica/nsparse.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: nsparse - namespace interface to AML parser
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
*
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
@@ -61,8 +27,17 @@ ACPI_MODULE_NAME("nsparse")
*
* RETURN: Status
*
- * DESCRIPTION: Load ACPI/AML table by executing the entire table as a
- * term_list.
+ * DESCRIPTION: Load ACPI/AML table by executing the entire table as a single
+ * large control method.
+ *
+ * NOTE: The point of this is to execute any module-level code in-place
+ * as the table is parsed. Some AML code depends on this behavior.
+ *
+ * It is a run-time option at this time, but will eventually become
+ * the default.
+ *
+ * Note: This causes the table to only have a single-pass parse.
+ * However, this is compatible with other ACPI implementations.
*
******************************************************************************/
acpi_status
@@ -112,8 +87,10 @@ acpi_ns_execute_table(u32 table_index, struct acpi_namespace_node *start_node)
goto cleanup;
}
- ACPI_DEBUG_PRINT((ACPI_DB_PARSE,
- "Create table code block: %p\n", method_obj));
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_PARSE,
+ "%s: Create table pseudo-method for [%4.4s] @%p, method %p\n",
+ ACPI_GET_FUNCTION_NAME, table->signature, table,
+ method_obj));
method_obj->method.aml_start = aml_start;
method_obj->method.aml_length = aml_length;
@@ -264,8 +241,21 @@ acpi_ns_parse_table(u32 table_index, struct acpi_namespace_node *start_node)
ACPI_FUNCTION_TRACE(ns_parse_table);
- if (acpi_gbl_parse_table_as_term_list) {
- ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "**** Start load pass\n"));
+ if (acpi_gbl_execute_tables_as_methods) {
+ /*
+ * This case executes the AML table as one large control method.
+ * The point of this is to execute any module-level code in-place
+ * as the table is parsed. Some AML code depends on this behavior.
+ *
+ * It is a run-time option at this time, but will eventually become
+ * the default.
+ *
+ * Note: This causes the table to only have a single-pass parse.
+ * However, this is compatible with other ACPI implementations.
+ */
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_PARSE,
+ "%s: **** Start table execution pass\n",
+ ACPI_GET_FUNCTION_NAME));
status = acpi_ns_execute_table(table_index, start_node);
if (ACPI_FAILURE(status)) {
diff --git a/drivers/acpi/acpica/nspredef.c b/drivers/acpi/acpica/nspredef.c
index 4f1f6d6d9ddf..29c68b15a64f 100644
--- a/drivers/acpi/acpica/nspredef.c
+++ b/drivers/acpi/acpica/nspredef.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: nspredef - Validation of ACPI predefined methods and objects
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#define ACPI_CREATE_PREDEFINED_TABLE
diff --git a/drivers/acpi/acpica/nsprepkg.c b/drivers/acpi/acpica/nsprepkg.c
index 7805d5ce8127..51523473e7fe 100644
--- a/drivers/acpi/acpica/nsprepkg.c
+++ b/drivers/acpi/acpica/nsprepkg.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: nsprepkg - Validation of package objects for predefined names
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/nsrepair.c b/drivers/acpi/acpica/nsrepair.c
index 7b6b6d281f1c..ff2ab8fbec38 100644
--- a/drivers/acpi/acpica/nsrepair.c
+++ b/drivers/acpi/acpica/nsrepair.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: nsrepair - Repair for objects returned by predefined methods
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/nsrepair2.c b/drivers/acpi/acpica/nsrepair2.c
index 29c3973c7815..a3bd6280882c 100644
--- a/drivers/acpi/acpica/nsrepair2.c
+++ b/drivers/acpi/acpica/nsrepair2.c
@@ -1,46 +1,12 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: nsrepair2 - Repair for objects returned by specific
* predefined methods
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/nssearch.c b/drivers/acpi/acpica/nssearch.c
index a469447f5c02..e9c9a63bb6a4 100644
--- a/drivers/acpi/acpica/nssearch.c
+++ b/drivers/acpi/acpica/nssearch.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: nssearch - Namespace search
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acnamesp.h"
diff --git a/drivers/acpi/acpica/nsutils.c b/drivers/acpi/acpica/nsutils.c
index 0487fdb59b0e..a2bf4b2caa6c 100644
--- a/drivers/acpi/acpica/nsutils.c
+++ b/drivers/acpi/acpica/nsutils.c
@@ -1,46 +1,12 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: nsutils - Utilities for accessing ACPI namespace, accessing
* parents and siblings and Scope manipulation
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/nswalk.c b/drivers/acpi/acpica/nswalk.c
index dd7ae1bc8af8..e9a061da9bb2 100644
--- a/drivers/acpi/acpica/nswalk.c
+++ b/drivers/acpi/acpica/nswalk.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: nswalk - Functions for walking the ACPI namespace
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/nsxfeval.c b/drivers/acpi/acpica/nsxfeval.c
index 1075bd9541f5..f9d059647cc5 100644
--- a/drivers/acpi/acpica/nsxfeval.c
+++ b/drivers/acpi/acpica/nsxfeval.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: nsxfeval - Public interfaces to the ACPI subsystem
@@ -5,43 +6,6 @@
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#define EXPORT_ACPI_INTERFACES
#include <acpi/acpi.h>
diff --git a/drivers/acpi/acpica/nsxfname.c b/drivers/acpi/acpica/nsxfname.c
index e9603fc9586c..b2915c9cceaf 100644
--- a/drivers/acpi/acpica/nsxfname.c
+++ b/drivers/acpi/acpica/nsxfname.c
@@ -1,46 +1,12 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: nsxfname - Public interfaces to the ACPI subsystem
* ACPI Namespace oriented interfaces
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#define EXPORT_ACPI_INTERFACES
@@ -241,7 +207,7 @@ static char *acpi_ns_copy_device_id(struct acpi_pnp_device_id *dest,
* namespace node and possibly by running several standard
* control methods (Such as in the case of a device.)
*
- * For Device and Processor objects, run the Device _HID, _UID, _CID, _STA,
+ * For Device and Processor objects, run the Device _HID, _UID, _CID,
* _CLS, _ADR, _sx_w, and _sx_d methods.
*
* Note: Allocates the return buffer, must be freed by the caller.
@@ -250,8 +216,9 @@ static char *acpi_ns_copy_device_id(struct acpi_pnp_device_id *dest,
* discovery namespace traversal. Therefore, no complex methods can be
* executed, especially those that access operation regions. Therefore, do
* not add any additional methods that could cause problems in this area.
- * this was the fate of the _SUB method which was found to cause such
- * problems and was removed (11/2015).
+ * Because of this reason support for the following methods has been removed:
+ * 1) _SUB method was removed (11/2015)
+ * 2) _STA method was removed (02/2018)
*
******************************************************************************/
@@ -369,25 +336,13 @@ acpi_get_object_info(acpi_handle handle,
if ((type == ACPI_TYPE_DEVICE) || (type == ACPI_TYPE_PROCESSOR)) {
/*
* Get extra info for ACPI Device/Processor objects only:
- * Run the _STA, _ADR and, sx_w, and _sx_d methods.
+ * Run the _ADR and, sx_w, and _sx_d methods.
*
* Notes: none of these methods are required, so they may or may
* not be present for this device. The Info->Valid bitfield is used
* to indicate which methods were found and run successfully.
- *
- * For _STA, if the method does not exist, then (as per the ACPI
- * specification), the returned current_status flags will indicate
- * that the device is present/functional/enabled. Otherwise, the
- * current_status flags reflect the value returned from _STA.
*/
- /* Execute the Device._STA method */
-
- status = acpi_ut_execute_STA(node, &info->current_status);
- if (ACPI_SUCCESS(status)) {
- valid |= ACPI_VALID_STA;
- }
-
/* Execute the Device._ADR method */
status = acpi_ut_evaluate_numeric_object(METHOD_NAME__ADR, node,
diff --git a/drivers/acpi/acpica/nsxfobj.c b/drivers/acpi/acpica/nsxfobj.c
index ac1fbf767cac..c022bef263e5 100644
--- a/drivers/acpi/acpica/nsxfobj.c
+++ b/drivers/acpi/acpica/nsxfobj.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: nsxfobj - Public interfaces to the ACPI subsystem
@@ -5,43 +6,6 @@
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#define EXPORT_ACPI_INTERFACES
#include <acpi/acpi.h>
diff --git a/drivers/acpi/acpica/psargs.c b/drivers/acpi/acpica/psargs.c
index dbc51bc5fdd6..176d28d60125 100644
--- a/drivers/acpi/acpica/psargs.c
+++ b/drivers/acpi/acpica/psargs.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: psargs - Parse AML opcode arguments
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
@@ -890,6 +856,10 @@ acpi_ps_get_next_arg(struct acpi_walk_state *walk_state,
ACPI_POSSIBLE_METHOD_CALL);
if (arg->common.aml_opcode == AML_INT_METHODCALL_OP) {
+
+ /* Free method call op and corresponding namestring sub-ob */
+
+ acpi_ps_free_op(arg->common.value.arg);
acpi_ps_free_op(arg);
arg = NULL;
walk_state->arg_count = 1;
diff --git a/drivers/acpi/acpica/psloop.c b/drivers/acpi/acpica/psloop.c
index 7dca287d7690..68422afc365f 100644
--- a/drivers/acpi/acpica/psloop.c
+++ b/drivers/acpi/acpica/psloop.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: psloop - Main AML parse loop
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
*
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
/*
* Parse the AML and build an operation tree as most interpreters, (such as
@@ -165,15 +131,23 @@ acpi_ps_get_arguments(struct acpi_walk_state *walk_state,
}
ACPI_DEBUG_PRINT((ACPI_DB_PARSE,
- "Final argument count: %u pass %u\n",
+ "Final argument count: %8.8X pass %u\n",
walk_state->arg_count,
walk_state->pass_number));
/*
- * Handle executable code at "module-level". This refers to
- * executable opcodes that appear outside of any control method.
+ * This case handles the legacy option that groups all module-level
+ * code blocks together and defers execution until all of the tables
+ * are loaded. Execute all of these blocks at this time.
+ * Execute any module-level code that was detected during the table
+ * load phase.
+ *
+ * Note: this option is deprecated and will be eliminated in the
+ * future. Use of this option can cause problems with AML code that
+ * depends upon in-order immediate execution of module-level code.
*/
- if ((walk_state->pass_number <= ACPI_IMODE_LOAD_PASS2) &&
+ if (acpi_gbl_group_module_level_code &&
+ (walk_state->pass_number <= ACPI_IMODE_LOAD_PASS2) &&
((walk_state->parse_flags & ACPI_PARSE_DISASSEMBLE) == 0)) {
/*
* We want to skip If/Else/While constructs during Pass1 because we
@@ -340,6 +314,16 @@ acpi_ps_get_arguments(struct acpi_walk_state *walk_state,
* object to the global list. Note, the mutex field of the method
* object is used to link multiple module-level code objects.
*
+ * NOTE: In this legacy option, each block of detected executable AML
+ * code that is outside of any control method is wrapped with a temporary
+ * control method object and placed on a global list below.
+ *
+ * This function executes the module-level code for all tables only after
+ * all of the tables have been loaded. It is a legacy option and is
+ * not compatible with other ACPI implementations. See acpi_ns_load_table.
+ *
+ * This function will be removed when the legacy option is removed.
+ *
******************************************************************************/
static void
@@ -581,7 +565,7 @@ acpi_status acpi_ps_parse_loop(struct acpi_walk_state *walk_state)
/* Check for arguments that need to be processed */
ACPI_DEBUG_PRINT((ACPI_DB_PARSE,
- "Parseloop: argument count: %u\n",
+ "Parseloop: argument count: %8.8X\n",
walk_state->arg_count));
if (walk_state->arg_count) {
diff --git a/drivers/acpi/acpica/psobject.c b/drivers/acpi/acpica/psobject.c
index b18f1e048985..7d9d0151ee54 100644
--- a/drivers/acpi/acpica/psobject.c
+++ b/drivers/acpi/acpica/psobject.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: psobject - Support for parse objects
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
@@ -67,7 +33,7 @@ static acpi_status acpi_ps_get_aml_opcode(struct acpi_walk_state *walk_state);
static acpi_status acpi_ps_get_aml_opcode(struct acpi_walk_state *walk_state)
{
- u32 aml_offset;
+ ACPI_ERROR_ONLY(u32 aml_offset);
ACPI_FUNCTION_TRACE_PTR(ps_get_aml_opcode, walk_state);
@@ -98,9 +64,11 @@ static acpi_status acpi_ps_get_aml_opcode(struct acpi_walk_state *walk_state)
/* The opcode is unrecognized. Complain and skip unknown opcodes */
if (walk_state->pass_number == 2) {
- aml_offset = (u32)ACPI_PTR_DIFF(walk_state->aml,
- walk_state->
- parser_state.aml_start);
+ ACPI_ERROR_ONLY(aml_offset =
+ (u32)ACPI_PTR_DIFF(walk_state->aml,
+ walk_state->
+ parser_state.
+ aml_start));
ACPI_ERROR((AE_INFO,
"Unknown opcode 0x%.2X at table offset 0x%.4X, ignoring",
diff --git a/drivers/acpi/acpica/psopcode.c b/drivers/acpi/acpica/psopcode.c
index d31f3eb23225..8d7dc98bad17 100644
--- a/drivers/acpi/acpica/psopcode.c
+++ b/drivers/acpi/acpica/psopcode.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: psopcode - Parser/Interpreter opcode information table
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/psopinfo.c b/drivers/acpi/acpica/psopinfo.c
index 1dc1fc79297e..f310954eea59 100644
--- a/drivers/acpi/acpica/psopinfo.c
+++ b/drivers/acpi/acpica/psopinfo.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: psopinfo - AML opcode information functions and dispatch tables
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/psparse.c b/drivers/acpi/acpica/psparse.c
index 2474ff961294..a16a6ea5ae02 100644
--- a/drivers/acpi/acpica/psparse.c
+++ b/drivers/acpi/acpica/psparse.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: psparse - Parser top level AML parse routines
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
/*
* Parse the AML and build an operation tree as most interpreters,
@@ -454,7 +420,7 @@ acpi_status acpi_ps_parse_aml(struct acpi_walk_state *walk_state)
walk_state->parser_state.aml_size));
if (!walk_state->parser_state.aml) {
- return_ACPI_STATUS(AE_NULL_OBJECT);
+ return_ACPI_STATUS(AE_BAD_ADDRESS);
}
/* Create and initialize a new thread state */
diff --git a/drivers/acpi/acpica/psscope.c b/drivers/acpi/acpica/psscope.c
index f49cdcc65700..00c67bc249aa 100644
--- a/drivers/acpi/acpica/psscope.c
+++ b/drivers/acpi/acpica/psscope.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: psscope - Parser scope stack management routines
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/pstree.c b/drivers/acpi/acpica/pstree.c
index f9fa88c79b32..64a8329a17f1 100644
--- a/drivers/acpi/acpica/pstree.c
+++ b/drivers/acpi/acpica/pstree.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: pstree - Parser op tree manipulation/traversal/search
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
@@ -295,6 +261,7 @@ union acpi_parse_object *acpi_ps_get_child(union acpi_parse_object *op)
case AML_BUFFER_OP:
case AML_PACKAGE_OP:
+ case AML_VARIABLE_PACKAGE_OP:
case AML_METHOD_OP:
case AML_IF_OP:
case AML_WHILE_OP:
diff --git a/drivers/acpi/acpica/psutils.c b/drivers/acpi/acpica/psutils.c
index fe151f42de3a..ef8a5805a836 100644
--- a/drivers/acpi/acpica/psutils.c
+++ b/drivers/acpi/acpica/psutils.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: psutils - Parser miscellaneous utilities (Parser only)
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/pswalk.c b/drivers/acpi/acpica/pswalk.c
index bc5c779e54e8..e0a442b8648b 100644
--- a/drivers/acpi/acpica/pswalk.c
+++ b/drivers/acpi/acpica/pswalk.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: pswalk - Parser routines to walk parsed op tree(s)
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/psxface.c b/drivers/acpi/acpica/psxface.c
index d2270ade5cf8..f26bcbbc2c27 100644
--- a/drivers/acpi/acpica/psxface.c
+++ b/drivers/acpi/acpica/psxface.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: psxface - Parser external interfaces
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/rsaddr.c b/drivers/acpi/acpica/rsaddr.c
index 213bad89675b..5737c3af1902 100644
--- a/drivers/acpi/acpica/rsaddr.c
+++ b/drivers/acpi/acpica/rsaddr.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: rsaddr - Address resource descriptors (16/32/64)
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acresrc.h"
diff --git a/drivers/acpi/acpica/rscalc.c b/drivers/acpi/acpica/rscalc.c
index 576f7aae162b..fcf129d27baa 100644
--- a/drivers/acpi/acpica/rscalc.c
+++ b/drivers/acpi/acpica/rscalc.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: rscalc - Calculate stream and list lengths
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acresrc.h"
diff --git a/drivers/acpi/acpica/rscreate.c b/drivers/acpi/acpica/rscreate.c
index fe07001ea865..570ea0df8a1b 100644
--- a/drivers/acpi/acpica/rscreate.c
+++ b/drivers/acpi/acpica/rscreate.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: rscreate - Create resource lists/tables
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acresrc.h"
diff --git a/drivers/acpi/acpica/rsdump.c b/drivers/acpi/acpica/rsdump.c
index bc4c4755aeb9..b12a0b1cd9ce 100644
--- a/drivers/acpi/acpica/rsdump.c
+++ b/drivers/acpi/acpica/rsdump.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: rsdump - AML debugger support for resource structures.
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acresrc.h"
diff --git a/drivers/acpi/acpica/rsdumpinfo.c b/drivers/acpi/acpica/rsdumpinfo.c
index c4a2a08e31ac..77a3263169fa 100644
--- a/drivers/acpi/acpica/rsdumpinfo.c
+++ b/drivers/acpi/acpica/rsdumpinfo.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: rsdumpinfo - Tables used to display resource descriptors.
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acresrc.h"
diff --git a/drivers/acpi/acpica/rsinfo.c b/drivers/acpi/acpica/rsinfo.c
index e819bb0f45af..6e2e596902eb 100644
--- a/drivers/acpi/acpica/rsinfo.c
+++ b/drivers/acpi/acpica/rsinfo.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: rsinfo - Dispatch and Info tables
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acresrc.h"
diff --git a/drivers/acpi/acpica/rsio.c b/drivers/acpi/acpica/rsio.c
index eafd993592f6..687aaed9655a 100644
--- a/drivers/acpi/acpica/rsio.c
+++ b/drivers/acpi/acpica/rsio.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: rsio - IO and DMA resource descriptors
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acresrc.h"
diff --git a/drivers/acpi/acpica/rsirq.c b/drivers/acpi/acpica/rsirq.c
index aabd73298eb8..134b67cd48ee 100644
--- a/drivers/acpi/acpica/rsirq.c
+++ b/drivers/acpi/acpica/rsirq.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: rsirq - IRQ resource descriptors
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acresrc.h"
diff --git a/drivers/acpi/acpica/rslist.c b/drivers/acpi/acpica/rslist.c
index 11214780ea8f..0307675d37be 100644
--- a/drivers/acpi/acpica/rslist.c
+++ b/drivers/acpi/acpica/rslist.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: rslist - Linked list utilities
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acresrc.h"
diff --git a/drivers/acpi/acpica/rsmemory.c b/drivers/acpi/acpica/rsmemory.c
index 05e375abc6b5..44fa16dde9ba 100644
--- a/drivers/acpi/acpica/rsmemory.c
+++ b/drivers/acpi/acpica/rsmemory.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: rsmem24 - Memory resource descriptors
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acresrc.h"
diff --git a/drivers/acpi/acpica/rsmisc.c b/drivers/acpi/acpica/rsmisc.c
index 7b4627181cc6..1763a3dbc9b1 100644
--- a/drivers/acpi/acpica/rsmisc.c
+++ b/drivers/acpi/acpica/rsmisc.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: rsmisc - Miscellaneous resource descriptors
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acresrc.h"
diff --git a/drivers/acpi/acpica/rsserial.c b/drivers/acpi/acpica/rsserial.c
index 87dac2812072..d073ebb51f90 100644
--- a/drivers/acpi/acpica/rsserial.c
+++ b/drivers/acpi/acpica/rsserial.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: rsserial - GPIO/serial_bus resource descriptors
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acresrc.h"
diff --git a/drivers/acpi/acpica/rsutils.c b/drivers/acpi/acpica/rsutils.c
index 49ff7f851d58..8c1a68629b75 100644
--- a/drivers/acpi/acpica/rsutils.c
+++ b/drivers/acpi/acpica/rsutils.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: rsutils - Utilities for the resource manager
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acnamesp.h"
diff --git a/drivers/acpi/acpica/rsxface.c b/drivers/acpi/acpica/rsxface.c
index 3b481f0b81c5..1d6f136e4068 100644
--- a/drivers/acpi/acpica/rsxface.c
+++ b/drivers/acpi/acpica/rsxface.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: rsxface - Public interfaces to the resource manager
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#define EXPORT_ACPI_INTERFACES
#include <acpi/acpi.h>
diff --git a/drivers/acpi/acpica/tbdata.c b/drivers/acpi/acpica/tbdata.c
index ec69267f1447..51891f9fb057 100644
--- a/drivers/acpi/acpica/tbdata.c
+++ b/drivers/acpi/acpica/tbdata.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: tbdata - Table manager data structure functions
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
@@ -966,12 +932,18 @@ acpi_tb_load_table(u32 table_index, struct acpi_namespace_node *parent_node)
status = acpi_ns_load_table(table_index, parent_node);
- /* Execute any module-level code that was found in the table */
-
- if (!acpi_gbl_parse_table_as_term_list
- && acpi_gbl_group_module_level_code) {
- acpi_ns_exec_module_code_list();
- }
+ /*
+ * This case handles the legacy option that groups all module-level
+ * code blocks together and defers execution until all of the tables
+ * are loaded. Execute all of these blocks at this time.
+ * Execute any module-level code that was detected during the table
+ * load phase.
+ *
+ * Note: this option is deprecated and will be eliminated in the
+ * future. Use of this option can cause problems with AML code that
+ * depends upon in-order immediate execution of module-level code.
+ */
+ acpi_ns_exec_module_code_list();
/*
* Update GPEs for any new _Lxx/_Exx methods. Ignore errors. The host is
diff --git a/drivers/acpi/acpica/tbfadt.c b/drivers/acpi/acpica/tbfadt.c
index d1763c5e4e91..99d325a51816 100644
--- a/drivers/acpi/acpica/tbfadt.c
+++ b/drivers/acpi/acpica/tbfadt.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: tbfadt - FADT table utilities
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/tbfind.c b/drivers/acpi/acpica/tbfind.c
index 999a64a48e1a..f00694b1d000 100644
--- a/drivers/acpi/acpica/tbfind.c
+++ b/drivers/acpi/acpica/tbfind.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: tbfind - find table
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/tbinstal.c b/drivers/acpi/acpica/tbinstal.c
index 00be16da1ee2..c5085b7ae8c9 100644
--- a/drivers/acpi/acpica/tbinstal.c
+++ b/drivers/acpi/acpica/tbinstal.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: tbinstal - ACPI table installation and removal
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
@@ -231,11 +197,11 @@ release_and_exit:
void acpi_tb_override_table(struct acpi_table_desc *old_table_desc)
{
acpi_status status;
- char *override_type;
struct acpi_table_desc new_table_desc;
struct acpi_table_header *table;
acpi_physical_address address;
u32 length;
+ ACPI_ERROR_ONLY(char *override_type);
/* (1) Attempt logical override (returns a logical address) */
@@ -244,7 +210,7 @@ void acpi_tb_override_table(struct acpi_table_desc *old_table_desc)
acpi_tb_acquire_temp_table(&new_table_desc,
ACPI_PTR_TO_PHYSADDR(table),
ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL);
- override_type = "Logical";
+ ACPI_ERROR_ONLY(override_type = "Logical");
goto finish_override;
}
@@ -255,7 +221,7 @@ void acpi_tb_override_table(struct acpi_table_desc *old_table_desc)
if (ACPI_SUCCESS(status) && address && length) {
acpi_tb_acquire_temp_table(&new_table_desc, address,
ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL);
- override_type = "Physical";
+ ACPI_ERROR_ONLY(override_type = "Physical");
goto finish_override;
}
diff --git a/drivers/acpi/acpica/tbprint.c b/drivers/acpi/acpica/tbprint.c
index 8cdcdd2c4697..e303418a895b 100644
--- a/drivers/acpi/acpica/tbprint.c
+++ b/drivers/acpi/acpica/tbprint.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: tbprint - Table output utilities
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/tbutils.c b/drivers/acpi/acpica/tbutils.c
index 30d40ff8992b..b526096560b5 100644
--- a/drivers/acpi/acpica/tbutils.c
+++ b/drivers/acpi/acpica/tbutils.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: tbutils - ACPI Table utilities
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/tbxface.c b/drivers/acpi/acpica/tbxface.c
index dca91b6f8cc2..e4d0dc8948cd 100644
--- a/drivers/acpi/acpica/tbxface.c
+++ b/drivers/acpi/acpica/tbxface.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: tbxface - ACPI table-oriented external interfaces
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#define EXPORT_ACPI_INTERFACES
diff --git a/drivers/acpi/acpica/tbxfload.c b/drivers/acpi/acpica/tbxfload.c
index e09b4b26300e..2f40f71c06db 100644
--- a/drivers/acpi/acpica/tbxfload.c
+++ b/drivers/acpi/acpica/tbxfload.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: tbxfload - Table load/unload external interfaces
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#define EXPORT_ACPI_INTERFACES
@@ -103,13 +69,16 @@ acpi_status ACPI_INIT_FUNCTION acpi_load_tables(void)
"While loading namespace from ACPI tables"));
}
- if (acpi_gbl_parse_table_as_term_list
+ if (acpi_gbl_execute_tables_as_methods
|| !acpi_gbl_group_module_level_code) {
/*
- * Initialize the objects that remain uninitialized. This
- * runs the executable AML that may be part of the
- * declaration of these objects:
- * operation_regions, buffer_fields, Buffers, and Packages.
+ * If the module-level code support is enabled, initialize the objects
+ * in the namespace that remain uninitialized. This runs the executable
+ * AML that may be part of the declaration of these name objects:
+ * operation_regions, buffer_fields, Buffers, and Packages.
+ *
+ * Note: The module-level code is optional at this time, but will
+ * become the default in the future.
*/
status = acpi_ns_initialize_objects();
if (ACPI_FAILURE(status)) {
diff --git a/drivers/acpi/acpica/tbxfroot.c b/drivers/acpi/acpica/tbxfroot.c
index abf3c62e1e80..483d0ce5180a 100644
--- a/drivers/acpi/acpica/tbxfroot.c
+++ b/drivers/acpi/acpica/tbxfroot.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: tbxfroot - Find the root ACPI table (RSDT)
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/utaddress.c b/drivers/acpi/acpica/utaddress.c
index d8540f380ae5..dbabe680ff58 100644
--- a/drivers/acpi/acpica/utaddress.c
+++ b/drivers/acpi/acpica/utaddress.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: utaddress - op_region address range check
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/utalloc.c b/drivers/acpi/acpica/utalloc.c
index 12fbaddbfb0d..8cbcd7d6bd5e 100644
--- a/drivers/acpi/acpica/utalloc.c
+++ b/drivers/acpi/acpica/utalloc.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: utalloc - local memory allocation routines
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/utascii.c b/drivers/acpi/acpica/utascii.c
index 95565e46a695..04ff61e284f5 100644
--- a/drivers/acpi/acpica/utascii.c
+++ b/drivers/acpi/acpica/utascii.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: utascii - Utility ascii functions
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/utbuffer.c b/drivers/acpi/acpica/utbuffer.c
index 2c5a14c2f46b..148aeb84e561 100644
--- a/drivers/acpi/acpica/utbuffer.c
+++ b/drivers/acpi/acpica/utbuffer.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: utbuffer - Buffer dump routines
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/utcache.c b/drivers/acpi/acpica/utcache.c
index 08e6944404b3..97d6ec174c28 100644
--- a/drivers/acpi/acpica/utcache.c
+++ b/drivers/acpi/acpica/utcache.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: utcache - local cache allocation routines
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
@@ -269,9 +235,10 @@ void *acpi_os_acquire_object(struct acpi_memory_list *cache)
cache->current_depth--;
ACPI_MEM_TRACKING(cache->hits++);
- ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
- "Object %p from %s cache\n", object,
- cache->list_name));
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_EXEC,
+ "%s: Object %p from %s cache\n",
+ ACPI_GET_FUNCTION_NAME, object,
+ cache->list_name));
status = acpi_ut_release_mutex(ACPI_MTX_CACHES);
if (ACPI_FAILURE(status)) {
diff --git a/drivers/acpi/acpica/utcopy.c b/drivers/acpi/acpica/utcopy.c
index 01434af99035..a872ed7879ca 100644
--- a/drivers/acpi/acpica/utcopy.c
+++ b/drivers/acpi/acpica/utcopy.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: utcopy - Internal to external object translation utilities
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/utdebug.c b/drivers/acpi/acpica/utdebug.c
index 2201be1bf4c2..aabdc25effd9 100644
--- a/drivers/acpi/acpica/utdebug.c
+++ b/drivers/acpi/acpica/utdebug.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: utdebug - Debug print/trace routines
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#define EXPORT_ACPI_INTERFACES
diff --git a/drivers/acpi/acpica/utdecode.c b/drivers/acpi/acpica/utdecode.c
index 1a3f316a18a8..dad02b821e19 100644
--- a/drivers/acpi/acpica/utdecode.c
+++ b/drivers/acpi/acpica/utdecode.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: utdecode - Utility decoding routines (value-to-string)
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/utdelete.c b/drivers/acpi/acpica/utdelete.c
index 7d8d0208f0a3..118f3ff1fbb5 100644
--- a/drivers/acpi/acpica/utdelete.c
+++ b/drivers/acpi/acpica/utdelete.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: utdelete - object deletion and reference count utilities
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acinterp.h"
@@ -332,8 +296,10 @@ static void acpi_ut_delete_internal_obj(union acpi_operand_object *object)
/* Now the object can be safely deleted */
- ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, "Deleting Object %p [%s]\n",
- object, acpi_ut_get_object_type_name(object)));
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_ALLOCATIONS,
+ "%s: Deleting Object %p [%s]\n",
+ ACPI_GET_FUNCTION_NAME, object,
+ acpi_ut_get_object_type_name(object)));
acpi_ut_delete_object_desc(object);
return_VOID;
@@ -444,9 +410,10 @@ acpi_ut_update_ref_count(union acpi_operand_object *object, u32 action)
object));
}
- ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS,
- "Obj %p Type %.2X Refs %.2X [Decremented]\n",
- object, object->common.type, new_count));
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_ALLOCATIONS,
+ "%s: Obj %p Type %.2X Refs %.2X [Decremented]\n",
+ ACPI_GET_FUNCTION_NAME, object,
+ object->common.type, new_count));
/* Actually delete the object on a reference count of zero */
@@ -747,9 +714,10 @@ void acpi_ut_remove_reference(union acpi_operand_object *object)
return;
}
- ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS,
- "Obj %p Current Refs=%X [To Be Decremented]\n",
- object, object->common.reference_count));
+ ACPI_DEBUG_PRINT_RAW((ACPI_DB_ALLOCATIONS,
+ "%s: Obj %p Current Refs=%X [To Be Decremented]\n",
+ ACPI_GET_FUNCTION_NAME, object,
+ object->common.reference_count));
/*
* Decrement the reference count, and only actually delete the object
diff --git a/drivers/acpi/acpica/uterror.c b/drivers/acpi/acpica/uterror.c
index ce5e891291bf..12d4a0f6b8d2 100644
--- a/drivers/acpi/acpica/uterror.c
+++ b/drivers/acpi/acpica/uterror.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: uterror - Various internal error/warning output functions
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acnamesp.h"
diff --git a/drivers/acpi/acpica/uteval.c b/drivers/acpi/acpica/uteval.c
index b8be0b82a130..c56ae6e058d5 100644
--- a/drivers/acpi/acpica/uteval.c
+++ b/drivers/acpi/acpica/uteval.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: uteval - Object evaluation
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/utexcep.c b/drivers/acpi/acpica/utexcep.c
index e3dbad8b73e5..60fbdcde151b 100644
--- a/drivers/acpi/acpica/utexcep.c
+++ b/drivers/acpi/acpica/utexcep.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: utexcep - Exception code support
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#define EXPORT_ACPI_INTERFACES
#define ACPI_DEFINE_EXCEPTION_TABLE
diff --git a/drivers/acpi/acpica/utglobal.c b/drivers/acpi/acpica/utglobal.c
index 933595b0e594..fa674e9b0e62 100644
--- a/drivers/acpi/acpica/utglobal.c
+++ b/drivers/acpi/acpica/utglobal.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: utglobal - Global variables for the ACPI subsystem
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#define EXPORT_ACPI_INTERFACES
#define DEFINE_ACPI_GLOBALS
diff --git a/drivers/acpi/acpica/uthex.c b/drivers/acpi/acpica/uthex.c
index f5886d557a94..3d63a9e8da4f 100644
--- a/drivers/acpi/acpica/uthex.c
+++ b/drivers/acpi/acpica/uthex.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: uthex -- Hex/ASCII support functions
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/utids.c b/drivers/acpi/acpica/utids.c
index db3c3c1d33da..70e6bf1107a1 100644
--- a/drivers/acpi/acpica/utids.c
+++ b/drivers/acpi/acpica/utids.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: utids - support for device Ids - HID, UID, CID, SUB, CLS
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/utinit.c b/drivers/acpi/acpica/utinit.c
index a2005b030347..0646ed62b351 100644
--- a/drivers/acpi/acpica/utinit.c
+++ b/drivers/acpi/acpica/utinit.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: utinit - Common ACPI subsystem initialization
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/utlock.c b/drivers/acpi/acpica/utlock.c
index 0636074a4c23..d61e01bd01a3 100644
--- a/drivers/acpi/acpica/utlock.c
+++ b/drivers/acpi/acpica/utlock.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: utlock - Reader/Writer lock interfaces
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/utmath.c b/drivers/acpi/acpica/utmath.c
index eddf71990433..2c2c6bc1ff3f 100644
--- a/drivers/acpi/acpica/utmath.c
+++ b/drivers/acpi/acpica/utmath.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: utmath - Integer math support routines
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/utmisc.c b/drivers/acpi/acpica/utmisc.c
index a331313ad5fa..ed73d79b500e 100644
--- a/drivers/acpi/acpica/utmisc.c
+++ b/drivers/acpi/acpica/utmisc.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: utmisc - common utility procedures
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acnamesp.h"
diff --git a/drivers/acpi/acpica/utmutex.c b/drivers/acpi/acpica/utmutex.c
index 6767bd1626f7..d2d93e388f40 100644
--- a/drivers/acpi/acpica/utmutex.c
+++ b/drivers/acpi/acpica/utmutex.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: utmutex - local mutex support
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/utnonansi.c b/drivers/acpi/acpica/utnonansi.c
index 94219610e259..ff0802ace19b 100644
--- a/drivers/acpi/acpica/utnonansi.c
+++ b/drivers/acpi/acpica/utnonansi.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: utnonansi - Non-ansi C library functions
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/utobject.c b/drivers/acpi/acpica/utobject.c
index 375901c0a596..5b78fe08d7d7 100644
--- a/drivers/acpi/acpica/utobject.c
+++ b/drivers/acpi/acpica/utobject.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: utobject - ACPI object create/delete/size/cache routines
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/utosi.c b/drivers/acpi/acpica/utosi.c
index 00ea104f6a0a..1b415fa90cf8 100644
--- a/drivers/acpi/acpica/utosi.c
+++ b/drivers/acpi/acpica/utosi.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: utosi - Support for the _OSI predefined control method
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/utownerid.c b/drivers/acpi/acpica/utownerid.c
index 9923dfa708be..5eb8b76ce9d8 100644
--- a/drivers/acpi/acpica/utownerid.c
+++ b/drivers/acpi/acpica/utownerid.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: utownerid - Support for Table/Method Owner IDs
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acnamesp.h"
diff --git a/drivers/acpi/acpica/utpredef.c b/drivers/acpi/acpica/utpredef.c
index ae6fef02b692..65ca9807c2a8 100644
--- a/drivers/acpi/acpica/utpredef.c
+++ b/drivers/acpi/acpica/utpredef.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: utpredef - support functions for predefined names
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/utprint.c b/drivers/acpi/acpica/utprint.c
index ac07700f5b79..35ffd8d51c65 100644
--- a/drivers/acpi/acpica/utprint.c
+++ b/drivers/acpi/acpica/utprint.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: utprint - Formatted printing routines
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/utresdecode.c b/drivers/acpi/acpica/utresdecode.c
index 93fa3450ca88..0a9c337346e8 100644
--- a/drivers/acpi/acpica/utresdecode.c
+++ b/drivers/acpi/acpica/utresdecode.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: utresdecode - Resource descriptor keyword strings
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acresrc.h"
diff --git a/drivers/acpi/acpica/utresrc.c b/drivers/acpi/acpica/utresrc.c
index 4d289d9c734c..cba5505171da 100644
--- a/drivers/acpi/acpica/utresrc.c
+++ b/drivers/acpi/acpica/utresrc.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: utresrc - Resource management utilities
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acresrc.h"
diff --git a/drivers/acpi/acpica/utstate.c b/drivers/acpi/acpica/utstate.c
index 7750c48739d8..a2484556a6b5 100644
--- a/drivers/acpi/acpica/utstate.c
+++ b/drivers/acpi/acpica/utstate.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: utstate - state object support procedures
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/utstring.c b/drivers/acpi/acpica/utstring.c
index a9507d1976ff..bd57a77bbcb2 100644
--- a/drivers/acpi/acpica/utstring.c
+++ b/drivers/acpi/acpica/utstring.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: utstring - Common functions for strings and characters
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acnamesp.h"
diff --git a/drivers/acpi/acpica/utstrsuppt.c b/drivers/acpi/acpica/utstrsuppt.c
index 6fc76f0b60e9..954f8e3e35cd 100644
--- a/drivers/acpi/acpica/utstrsuppt.c
+++ b/drivers/acpi/acpica/utstrsuppt.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: utstrsuppt - Support functions for string-to-integer conversion
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/utstrtoul64.c b/drivers/acpi/acpica/utstrtoul64.c
index 9f7cef1de34a..8fadad242db6 100644
--- a/drivers/acpi/acpica/utstrtoul64.c
+++ b/drivers/acpi/acpica/utstrtoul64.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: utstrtoul64 - String-to-integer conversion support for both
@@ -5,43 +6,6 @@
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/uttrack.c b/drivers/acpi/acpica/uttrack.c
index 8cc70ca4e0fb..016a6621cc6f 100644
--- a/drivers/acpi/acpica/uttrack.c
+++ b/drivers/acpi/acpica/uttrack.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: uttrack - Memory allocation tracking routines (debug only)
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
/*
* These procedures are used for tracking memory leaks in the subsystem, and
diff --git a/drivers/acpi/acpica/utuuid.c b/drivers/acpi/acpica/utuuid.c
index 95946fdb55d5..59ae118092a3 100644
--- a/drivers/acpi/acpica/utuuid.c
+++ b/drivers/acpi/acpica/utuuid.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: utuuid -- UUID support functions
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
diff --git a/drivers/acpi/acpica/utxface.c b/drivers/acpi/acpica/utxface.c
index 25ef2ce64603..d2d6cc065181 100644
--- a/drivers/acpi/acpica/utxface.c
+++ b/drivers/acpi/acpica/utxface.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: utxface - External interfaces, miscellaneous utility functions
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *
+ *****************************************************************************/
#define EXPORT_ACPI_INTERFACES
diff --git a/drivers/acpi/acpica/utxferror.c b/drivers/acpi/acpica/utxferror.c
index a78861ded894..6bb85d691fcb 100644
--- a/drivers/acpi/acpica/utxferror.c
+++ b/drivers/acpi/acpica/utxferror.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: utxferror - Various error/warning output functions
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#define EXPORT_ACPI_INTERFACES
#include <acpi/acpi.h>
diff --git a/drivers/acpi/acpica/utxfinit.c b/drivers/acpi/acpica/utxfinit.c
index e727db52a55e..e3c60f57c9f0 100644
--- a/drivers/acpi/acpica/utxfinit.c
+++ b/drivers/acpi/acpica/utxfinit.c
@@ -1,45 +1,11 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: utxfinit - External interfaces for ACPICA initialization
*
- *****************************************************************************/
-
-/*
* Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
+ *****************************************************************************/
#define EXPORT_ACPI_INTERFACES
@@ -245,41 +211,29 @@ acpi_status ACPI_INIT_FUNCTION acpi_initialize_objects(u32 flags)
ACPI_FUNCTION_TRACE(acpi_initialize_objects);
-#ifdef ACPI_EXEC_APP
/*
- * This call implements the "initialization file" option for acpi_exec.
- * This is the precise point that we want to perform the overrides.
+ * This case handles the legacy option that groups all module-level
+ * code blocks together and defers execution until all of the tables
+ * are loaded. Execute all of these blocks at this time.
+ * Execute any module-level code that was detected during the table
+ * load phase.
+ *
+ * Note: this option is deprecated and will be eliminated in the
+ * future. Use of this option can cause problems with AML code that
+ * depends upon in-order immediate execution of module-level code.
*/
- ae_do_object_overrides();
-#endif
+ acpi_ns_exec_module_code_list();
/*
- * Execute any module-level code that was detected during the table load
- * phase. Although illegal since ACPI 2.0, there are many machines that
- * contain this type of code. Each block of detected executable AML code
- * outside of any control method is wrapped with a temporary control
- * method object and placed on a global list. The methods on this list
- * are executed below.
- *
- * This case executes the module-level code for all tables only after
- * all of the tables have been loaded. It is a legacy option and is
- * not compatible with other ACPI implementations. See acpi_ns_load_table.
+ * Initialize the objects that remain uninitialized. This
+ * runs the executable AML that may be part of the
+ * declaration of these objects:
+ * operation_regions, buffer_fields, Buffers, and Packages.
*/
- if (!acpi_gbl_parse_table_as_term_list
- && acpi_gbl_group_module_level_code) {
- acpi_ns_exec_module_code_list();
-
- /*
- * Initialize the objects that remain uninitialized. This
- * runs the executable AML that may be part of the
- * declaration of these objects:
- * operation_regions, buffer_fields, Buffers, and Packages.
- */
- if (!(flags & ACPI_NO_OBJECT_INIT)) {
- status = acpi_ns_initialize_objects();
- if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);
- }
+ if (!(flags & ACPI_NO_OBJECT_INIT)) {
+ status = acpi_ns_initialize_objects();
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
}
}
diff --git a/drivers/acpi/acpica/utxfmutex.c b/drivers/acpi/acpica/utxfmutex.c
index 764782fcf1bd..be24db2544ce 100644
--- a/drivers/acpi/acpica/utxfmutex.c
+++ b/drivers/acpi/acpica/utxfmutex.c
@@ -1,46 +1,10 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/*******************************************************************************
*
* Module Name: utxfmutex - external AML mutex access functions
*
******************************************************************************/
-/*
- * Copyright (C) 2000 - 2018, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- */
-
#include <acpi/acpi.h>
#include "accommon.h"
#include "acnamesp.h"
diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
index 95255ecfae7c..7a3a541046ed 100644
--- a/drivers/acpi/arm64/iort.c
+++ b/drivers/acpi/arm64/iort.c
@@ -31,14 +31,10 @@
#define IORT_IOMMU_TYPE ((1 << ACPI_IORT_NODE_SMMU) | \
(1 << ACPI_IORT_NODE_SMMU_V3))
-/* Until ACPICA headers cover IORT rev. C */
-#ifndef ACPI_IORT_SMMU_V3_CAVIUM_CN99XX
-#define ACPI_IORT_SMMU_V3_CAVIUM_CN99XX 0x2
-#endif
-
struct iort_its_msi_chip {
struct list_head list;
struct fwnode_handle *fw_node;
+ phys_addr_t base_addr;
u32 translation_id;
};
@@ -161,14 +157,16 @@ static LIST_HEAD(iort_msi_chip_list);
static DEFINE_SPINLOCK(iort_msi_chip_lock);
/**
- * iort_register_domain_token() - register domain token and related ITS ID
- * to the list from where we can get it back later on.
+ * iort_register_domain_token() - register domain token along with related
+ * ITS ID and base address to the list from where we can get it back later on.
* @trans_id: ITS ID.
+ * @base: ITS base address.
* @fw_node: Domain token.
*
* Returns: 0 on success, -ENOMEM if no memory when allocating list element
*/
-int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node)
+int iort_register_domain_token(int trans_id, phys_addr_t base,
+ struct fwnode_handle *fw_node)
{
struct iort_its_msi_chip *its_msi_chip;
@@ -178,6 +176,7 @@ int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node)
its_msi_chip->fw_node = fw_node;
its_msi_chip->translation_id = trans_id;
+ its_msi_chip->base_addr = base;
spin_lock(&iort_msi_chip_lock);
list_add(&its_msi_chip->list, &iort_msi_chip_list);
@@ -366,7 +365,6 @@ static struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node,
return NULL;
}
-#if (ACPI_CA_VERSION > 0x20170929)
static int iort_get_id_mapping_index(struct acpi_iort_node *node)
{
struct acpi_iort_smmu_v3 *smmu;
@@ -400,12 +398,6 @@ static int iort_get_id_mapping_index(struct acpi_iort_node *node)
return -EINVAL;
}
}
-#else
-static inline int iort_get_id_mapping_index(struct acpi_iort_node *node)
-{
- return -EINVAL;
-}
-#endif
static struct acpi_iort_node *iort_node_map_id(struct acpi_iort_node *node,
u32 id_in, u32 *id_out,
@@ -581,6 +573,24 @@ int iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id)
return -ENODEV;
}
+static int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base)
+{
+ struct iort_its_msi_chip *its_msi_chip;
+ int ret = -ENODEV;
+
+ spin_lock(&iort_msi_chip_lock);
+ list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) {
+ if (its_msi_chip->translation_id == its_id) {
+ *base = its_msi_chip->base_addr;
+ ret = 0;
+ break;
+ }
+ }
+ spin_unlock(&iort_msi_chip_lock);
+
+ return ret;
+}
+
/**
* iort_dev_find_its_id() - Find the ITS identifier for a device
* @dev: The device.
@@ -766,6 +776,24 @@ static inline bool iort_iommu_driver_enabled(u8 type)
}
#ifdef CONFIG_IOMMU_API
+static struct acpi_iort_node *iort_get_msi_resv_iommu(struct device *dev)
+{
+ struct acpi_iort_node *iommu;
+ struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+
+ iommu = iort_get_iort_node(fwspec->iommu_fwnode);
+
+ if (iommu && (iommu->type == ACPI_IORT_NODE_SMMU_V3)) {
+ struct acpi_iort_smmu_v3 *smmu;
+
+ smmu = (struct acpi_iort_smmu_v3 *)iommu->node_data;
+ if (smmu->model == ACPI_IORT_SMMU_V3_HISILICON_HI161X)
+ return iommu;
+ }
+
+ return NULL;
+}
+
static inline const struct iommu_ops *iort_fwspec_iommu_ops(
struct iommu_fwspec *fwspec)
{
@@ -782,6 +810,69 @@ static inline int iort_add_device_replay(const struct iommu_ops *ops,
return err;
}
+
+/**
+ * iort_iommu_msi_get_resv_regions - Reserved region driver helper
+ * @dev: Device from iommu_get_resv_regions()
+ * @head: Reserved region list from iommu_get_resv_regions()
+ *
+ * Returns: Number of msi reserved regions on success (0 if platform
+ * doesn't require the reservation or no associated msi regions),
+ * appropriate error value otherwise. The ITS interrupt translation
+ * spaces (ITS_base + SZ_64K, SZ_64K) associated with the device
+ * are the msi reserved regions.
+ */
+int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head)
+{
+ struct acpi_iort_its_group *its;
+ struct acpi_iort_node *iommu_node, *its_node = NULL;
+ int i, resv = 0;
+
+ iommu_node = iort_get_msi_resv_iommu(dev);
+ if (!iommu_node)
+ return 0;
+
+ /*
+ * Current logic to reserve ITS regions relies on HW topologies
+ * where a given PCI or named component maps its IDs to only one
+ * ITS group; if a PCI or named component can map its IDs to
+ * different ITS groups through IORT mappings this function has
+ * to be reworked to ensure we reserve regions for all ITS groups
+ * a given PCI or named component may map IDs to.
+ */
+
+ for (i = 0; i < dev->iommu_fwspec->num_ids; i++) {
+ its_node = iort_node_map_id(iommu_node,
+ dev->iommu_fwspec->ids[i],
+ NULL, IORT_MSI_TYPE);
+ if (its_node)
+ break;
+ }
+
+ if (!its_node)
+ return 0;
+
+ /* Move to ITS specific data */
+ its = (struct acpi_iort_its_group *)its_node->node_data;
+
+ for (i = 0; i < its->its_count; i++) {
+ phys_addr_t base;
+
+ if (!iort_find_its_base(its->identifiers[i], &base)) {
+ int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
+ struct iommu_resv_region *region;
+
+ region = iommu_alloc_resv_region(base + SZ_64K, SZ_64K,
+ prot, IOMMU_RESV_MSI);
+ if (region) {
+ list_add_tail(&region->list, head);
+ resv++;
+ }
+ }
+ }
+
+ return (resv == its->its_count) ? resv : -ENODEV;
+}
#else
static inline const struct iommu_ops *iort_fwspec_iommu_ops(
struct iommu_fwspec *fwspec)
@@ -789,6 +880,8 @@ static inline const struct iommu_ops *iort_fwspec_iommu_ops(
static inline int iort_add_device_replay(const struct iommu_ops *ops,
struct device *dev)
{ return 0; }
+int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head)
+{ return 0; }
#endif
static int iort_iommu_xlate(struct device *dev, struct acpi_iort_node *node,
diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c
index f2eb6c37ea0a..bdb24d636d9a 100644
--- a/drivers/acpi/battery.c
+++ b/drivers/acpi/battery.c
@@ -21,8 +21,12 @@
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
+#include <linux/list.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/jiffies.h>
@@ -42,7 +46,7 @@
#include <linux/acpi.h>
#include <linux/power_supply.h>
-#include "battery.h"
+#include <acpi/battery.h>
#define PREFIX "ACPI: "
@@ -115,6 +119,10 @@ enum {
post-1.29 BIOS), but as of Nov. 2012, no such update is
available for the 2010 models. */
ACPI_BATTERY_QUIRK_THINKPAD_MAH,
+ /* for batteries reporting current capacity with design capacity
+ * on a full charge, but showing degradation in full charge cap.
+ */
+ ACPI_BATTERY_QUIRK_DEGRADED_FULL_CHARGE,
};
struct acpi_battery {
@@ -124,6 +132,7 @@ struct acpi_battery {
struct power_supply_desc bat_desc;
struct acpi_device *device;
struct notifier_block pm_nb;
+ struct list_head list;
unsigned long update_time;
int revision;
int rate_now;
@@ -200,6 +209,12 @@ static int acpi_battery_is_charged(struct acpi_battery *battery)
return 0;
}
+static bool acpi_battery_is_degraded(struct acpi_battery *battery)
+{
+ return battery->full_charge_capacity && battery->design_capacity &&
+ battery->full_charge_capacity < battery->design_capacity;
+}
+
static int acpi_battery_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
@@ -471,6 +486,10 @@ static int extract_battery_info(const int use_bix,
it's impossible to tell if they would need an adjustment
or not if their values were higher. */
}
+ if (test_bit(ACPI_BATTERY_QUIRK_DEGRADED_FULL_CHARGE, &battery->flags) &&
+ battery->capacity_now > battery->full_charge_capacity)
+ battery->capacity_now = battery->full_charge_capacity;
+
return result;
}
@@ -563,6 +582,10 @@ static int acpi_battery_get_state(struct acpi_battery *battery)
battery->capacity_now = battery->capacity_now *
10000 / battery->design_voltage;
}
+ if (test_bit(ACPI_BATTERY_QUIRK_DEGRADED_FULL_CHARGE, &battery->flags) &&
+ battery->capacity_now > battery->full_charge_capacity)
+ battery->capacity_now = battery->full_charge_capacity;
+
return result;
}
@@ -626,6 +649,139 @@ static const struct device_attribute alarm_attr = {
.store = acpi_battery_alarm_store,
};
+/*
+ * The Battery Hooking API
+ *
+ * This API is used inside other drivers that need to expose
+ * platform-specific behaviour within the generic driver in a
+ * generic way.
+ *
+ */
+
+static LIST_HEAD(acpi_battery_list);
+static LIST_HEAD(battery_hook_list);
+static DEFINE_MUTEX(hook_mutex);
+
+static void __battery_hook_unregister(struct acpi_battery_hook *hook, int lock)
+{
+ struct acpi_battery *battery;
+ /*
+ * In order to remove a hook, we first need to
+ * de-register all the batteries that are registered.
+ */
+ if (lock)
+ mutex_lock(&hook_mutex);
+ list_for_each_entry(battery, &acpi_battery_list, list) {
+ hook->remove_battery(battery->bat);
+ }
+ list_del(&hook->list);
+ if (lock)
+ mutex_unlock(&hook_mutex);
+ pr_info("extension unregistered: %s\n", hook->name);
+}
+
+void battery_hook_unregister(struct acpi_battery_hook *hook)
+{
+ __battery_hook_unregister(hook, 1);
+}
+EXPORT_SYMBOL_GPL(battery_hook_unregister);
+
+void battery_hook_register(struct acpi_battery_hook *hook)
+{
+ struct acpi_battery *battery;
+
+ mutex_lock(&hook_mutex);
+ INIT_LIST_HEAD(&hook->list);
+ list_add(&hook->list, &battery_hook_list);
+ /*
+ * Now that the driver is registered, we need
+ * to notify the hook that a battery is available
+ * for each battery, so that the driver may add
+ * its attributes.
+ */
+ list_for_each_entry(battery, &acpi_battery_list, list) {
+ if (hook->add_battery(battery->bat)) {
+ /*
+ * If a add-battery returns non-zero,
+ * the registration of the extension has failed,
+ * and we will not add it to the list of loaded
+ * hooks.
+ */
+ pr_err("extension failed to load: %s", hook->name);
+ __battery_hook_unregister(hook, 0);
+ return;
+ }
+ }
+ pr_info("new extension: %s\n", hook->name);
+ mutex_unlock(&hook_mutex);
+}
+EXPORT_SYMBOL_GPL(battery_hook_register);
+
+/*
+ * This function gets called right after the battery sysfs
+ * attributes have been added, so that the drivers that
+ * define custom sysfs attributes can add their own.
+*/
+static void battery_hook_add_battery(struct acpi_battery *battery)
+{
+ struct acpi_battery_hook *hook_node;
+
+ mutex_lock(&hook_mutex);
+ INIT_LIST_HEAD(&battery->list);
+ list_add(&battery->list, &acpi_battery_list);
+ /*
+ * Since we added a new battery to the list, we need to
+ * iterate over the hooks and call add_battery for each
+ * hook that was registered. This usually happens
+ * when a battery gets hotplugged or initialized
+ * during the battery module initialization.
+ */
+ list_for_each_entry(hook_node, &battery_hook_list, list) {
+ if (hook_node->add_battery(battery->bat)) {
+ /*
+ * The notification of the extensions has failed, to
+ * prevent further errors we will unload the extension.
+ */
+ __battery_hook_unregister(hook_node, 0);
+ pr_err("error in extension, unloading: %s",
+ hook_node->name);
+ }
+ }
+ mutex_unlock(&hook_mutex);
+}
+
+static void battery_hook_remove_battery(struct acpi_battery *battery)
+{
+ struct acpi_battery_hook *hook;
+
+ mutex_lock(&hook_mutex);
+ /*
+ * Before removing the hook, we need to remove all
+ * custom attributes from the battery.
+ */
+ list_for_each_entry(hook, &battery_hook_list, list) {
+ hook->remove_battery(battery->bat);
+ }
+ /* Then, just remove the battery from the list */
+ list_del(&battery->list);
+ mutex_unlock(&hook_mutex);
+}
+
+static void __exit battery_hook_exit(void)
+{
+ struct acpi_battery_hook *hook;
+ struct acpi_battery_hook *ptr;
+ /*
+ * At this point, the acpi_bus_unregister_driver()
+ * has called remove for all batteries. We just
+ * need to remove the hooks.
+ */
+ list_for_each_entry_safe(hook, ptr, &battery_hook_list, list) {
+ __battery_hook_unregister(hook, 1);
+ }
+ mutex_destroy(&hook_mutex);
+}
+
static int sysfs_add_battery(struct acpi_battery *battery)
{
struct power_supply_config psy_cfg = { .drv_data = battery, };
@@ -653,6 +809,7 @@ static int sysfs_add_battery(struct acpi_battery *battery)
battery->bat = NULL;
return result;
}
+ battery_hook_add_battery(battery);
return device_create_file(&battery->bat->dev, &alarm_attr);
}
@@ -663,7 +820,7 @@ static void sysfs_remove_battery(struct acpi_battery *battery)
mutex_unlock(&battery->sysfs_lock);
return;
}
-
+ battery_hook_remove_battery(battery);
device_remove_file(&battery->bat->dev, &alarm_attr);
power_supply_unregister(battery->bat);
battery->bat = NULL;
@@ -739,6 +896,15 @@ static void acpi_battery_quirks(struct acpi_battery *battery)
}
}
}
+
+ if (test_bit(ACPI_BATTERY_QUIRK_DEGRADED_FULL_CHARGE, &battery->flags))
+ return;
+
+ if (acpi_battery_is_degraded(battery) &&
+ battery->capacity_now > battery->full_charge_capacity) {
+ set_bit(ACPI_BATTERY_QUIRK_DEGRADED_FULL_CHARGE, &battery->flags);
+ battery->capacity_now = battery->full_charge_capacity;
+ }
}
static int acpi_battery_update(struct acpi_battery *battery, bool resume)
@@ -1357,8 +1523,10 @@ static int __init acpi_battery_init(void)
static void __exit acpi_battery_exit(void)
{
async_synchronize_cookie(async_cookie + 1);
- if (battery_driver_registered)
+ if (battery_driver_registered) {
acpi_bus_unregister_driver(&acpi_battery_driver);
+ battery_hook_exit();
+ }
#ifdef CONFIG_ACPI_PROCFS_POWER
if (acpi_battery_dir)
acpi_unlock_battery_dir(acpi_battery_dir);
diff --git a/drivers/acpi/battery.h b/drivers/acpi/battery.h
deleted file mode 100644
index 225f493d4c27..000000000000
--- a/drivers/acpi/battery.h
+++ /dev/null
@@ -1,11 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef __ACPI_BATTERY_H
-#define __ACPI_BATTERY_H
-
-#define ACPI_BATTERY_CLASS "battery"
-
-#define ACPI_BATTERY_NOTIFY_STATUS 0x80
-#define ACPI_BATTERY_NOTIFY_INFO 0x81
-#define ACPI_BATTERY_NOTIFY_THRESHOLD 0x82
-
-#endif
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index 0dad0bd9327b..84b4a62018eb 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -68,7 +68,7 @@ static int set_copy_dsdt(const struct dmi_system_id *id)
#endif
static int set_gbl_term_list(const struct dmi_system_id *id)
{
- acpi_gbl_parse_table_as_term_list = 1;
+ acpi_gbl_execute_tables_as_methods = 1;
return 0;
}
@@ -1077,7 +1077,7 @@ void __init acpi_early_init(void)
goto error0;
}
- if (!acpi_gbl_parse_table_as_term_list &&
+ if (!acpi_gbl_execute_tables_as_methods &&
acpi_gbl_group_module_level_code) {
status = acpi_load_tables();
if (ACPI_FAILURE(status)) {
@@ -1167,7 +1167,7 @@ static int __init acpi_bus_init(void)
status = acpi_ec_ecdt_probe();
/* Ignore result. Not having an ECDT is not fatal. */
- if (acpi_gbl_parse_table_as_term_list ||
+ if (acpi_gbl_execute_tables_as_methods ||
!acpi_gbl_group_module_level_code) {
status = acpi_load_tables();
if (ACPI_FAILURE(status)) {
diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c
index e1eee7a60fad..f1cc4f9d31cd 100644
--- a/drivers/acpi/button.c
+++ b/drivers/acpi/button.c
@@ -635,4 +635,26 @@ module_param_call(lid_init_state,
NULL, 0644);
MODULE_PARM_DESC(lid_init_state, "Behavior for reporting LID initial state");
-module_acpi_driver(acpi_button_driver);
+static int acpi_button_register_driver(struct acpi_driver *driver)
+{
+ /*
+ * Modules such as nouveau.ko and i915.ko have a link time dependency
+ * on acpi_lid_open(), and would therefore not be loadable on ACPI
+ * capable kernels booted in non-ACPI mode if the return value of
+ * acpi_bus_register_driver() is returned from here with ACPI disabled
+ * when this driver is built as a module.
+ */
+ if (acpi_disabled)
+ return 0;
+
+ return acpi_bus_register_driver(driver);
+}
+
+static void acpi_button_unregister_driver(struct acpi_driver *driver)
+{
+ if (!acpi_disabled)
+ acpi_bus_unregister_driver(driver);
+}
+
+module_driver(acpi_button_driver, acpi_button_register_driver,
+ acpi_button_unregister_driver);
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 0afbb2658cbc..735c74a4cbdb 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -227,7 +227,8 @@ static int check_pcc_chan(int pcc_ss_id, bool chk_err_bit)
if (likely(!ret))
pcc_ss_data->platform_owns_pcc = false;
else
- pr_err("PCC check channel failed. Status=%x\n", status);
+ pr_err("PCC check channel failed for ss: %d. Status=%x\n",
+ pcc_ss_id, status);
return ret;
}
@@ -291,7 +292,8 @@ static int send_pcc_cmd(int pcc_ss_id, u16 cmd)
time_delta = ktime_ms_delta(ktime_get(),
pcc_ss_data->last_mpar_reset);
if ((time_delta < 60 * MSEC_PER_SEC) && pcc_ss_data->last_mpar_reset) {
- pr_debug("PCC cmd not sent due to MPAR limit");
+ pr_debug("PCC cmd for subspace %d not sent due to MPAR limit",
+ pcc_ss_id);
ret = -EIO;
goto end;
}
@@ -312,8 +314,8 @@ static int send_pcc_cmd(int pcc_ss_id, u16 cmd)
/* Ring doorbell */
ret = mbox_send_message(pcc_ss_data->pcc_channel, &cmd);
if (ret < 0) {
- pr_err("Err sending PCC mbox message. cmd:%d, ret:%d\n",
- cmd, ret);
+ pr_err("Err sending PCC mbox message. ss: %d cmd:%d, ret:%d\n",
+ pcc_ss_id, cmd, ret);
goto end;
}
@@ -553,7 +555,8 @@ static int register_pcc_channel(int pcc_ss_idx)
pcc_mbox_request_channel(&cppc_mbox_cl, pcc_ss_idx);
if (IS_ERR(pcc_data[pcc_ss_idx]->pcc_channel)) {
- pr_err("Failed to find PCC communication channel\n");
+ pr_err("Failed to find PCC channel for subspace %d\n",
+ pcc_ss_idx);
return -ENODEV;
}
@@ -566,7 +569,8 @@ static int register_pcc_channel(int pcc_ss_idx)
cppc_ss = (pcc_data[pcc_ss_idx]->pcc_channel)->con_priv;
if (!cppc_ss) {
- pr_err("No PCC subspace found for CPPC\n");
+ pr_err("No PCC subspace found for %d CPPC\n",
+ pcc_ss_idx);
return -ENODEV;
}
@@ -584,7 +588,8 @@ static int register_pcc_channel(int pcc_ss_idx)
pcc_data[pcc_ss_idx]->pcc_comm_addr =
acpi_os_ioremap(cppc_ss->base_address, cppc_ss->length);
if (!pcc_data[pcc_ss_idx]->pcc_comm_addr) {
- pr_err("Failed to ioremap PCC comm region mem\n");
+ pr_err("Failed to ioremap PCC comm region mem for %d\n",
+ pcc_ss_idx);
return -ENOMEM;
}
@@ -973,8 +978,8 @@ static int cpc_read(int cpu, struct cpc_register_resource *reg_res, u64 *val)
*val = readq_relaxed(vaddr);
break;
default:
- pr_debug("Error: Cannot read %u bit width from PCC\n",
- reg->bit_width);
+ pr_debug("Error: Cannot read %u bit width from PCC for ss: %d\n",
+ reg->bit_width, pcc_ss_id);
ret_val = -EFAULT;
}
@@ -1012,8 +1017,8 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
writeq_relaxed(val, vaddr);
break;
default:
- pr_debug("Error: Cannot write %u bit width to PCC\n",
- reg->bit_width);
+ pr_debug("Error: Cannot write %u bit width to PCC for ss: %d\n",
+ reg->bit_width, pcc_ss_id);
ret_val = -EFAULT;
break;
}
diff --git a/drivers/acpi/custom_method.c b/drivers/acpi/custom_method.c
index c68e72414a67..e967c1173ba3 100644
--- a/drivers/acpi/custom_method.c
+++ b/drivers/acpi/custom_method.c
@@ -94,7 +94,7 @@ static void __exit acpi_custom_method_exit(void)
{
if (cm_dentry)
debugfs_remove(cm_dentry);
- }
+}
module_init(acpi_custom_method_init);
module_exit(acpi_custom_method_exit);
diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c
index c4d0a1c912f0..3d96e4da2d98 100644
--- a/drivers/acpi/device_pm.c
+++ b/drivers/acpi/device_pm.c
@@ -543,6 +543,7 @@ static int acpi_dev_pm_get_state(struct device *dev, struct acpi_device *adev,
unsigned long long ret;
int d_min, d_max;
bool wakeup = false;
+ bool has_sxd = false;
acpi_status status;
/*
@@ -581,6 +582,10 @@ static int acpi_dev_pm_get_state(struct device *dev, struct acpi_device *adev,
else
return -ENODATA;
}
+
+ if (status == AE_OK)
+ has_sxd = true;
+
d_min = ret;
wakeup = device_may_wakeup(dev) && adev->wakeup.flags.valid
&& adev->wakeup.sleep_state >= target_state;
@@ -599,7 +604,11 @@ static int acpi_dev_pm_get_state(struct device *dev, struct acpi_device *adev,
method[3] = 'W';
status = acpi_evaluate_integer(handle, method, NULL, &ret);
if (status == AE_NOT_FOUND) {
- if (target_state > ACPI_STATE_S0)
+ /* No _SxW. In this case, the ACPI spec says that we
+ * must not go into any power state deeper than the
+ * value returned from _SxD.
+ */
+ if (has_sxd && target_state > ACPI_STATE_S0)
d_max = d_min;
} else if (ACPI_SUCCESS(status) && ret <= ACPI_STATE_D3_COLD) {
/* Fall back to D3cold if ret is not a valid state. */
diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c
index 6cf4988206f2..3563103590c6 100644
--- a/drivers/acpi/fan.c
+++ b/drivers/acpi/fan.c
@@ -219,7 +219,7 @@ fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
return fan_set_state_acpi4(device, state);
else
return fan_set_state(device, state);
- }
+}
static const struct thermal_cooling_device_ops fan_cooling_ops = {
.get_max_state = fan_get_max_state,
diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c
index eb09ef55c38a..e2235ed3e4be 100644
--- a/drivers/acpi/nfit/core.c
+++ b/drivers/acpi/nfit/core.c
@@ -23,6 +23,7 @@
#include <linux/io.h>
#include <linux/nd.h>
#include <asm/cacheflush.h>
+#include <acpi/nfit.h>
#include "nfit.h"
/*
@@ -35,16 +36,6 @@ static bool force_enable_dimms;
module_param(force_enable_dimms, bool, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(force_enable_dimms, "Ignore _STA (ACPI DIMM device) status");
-static unsigned int scrub_timeout = NFIT_ARS_TIMEOUT;
-module_param(scrub_timeout, uint, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(scrub_timeout, "Initial scrub timeout in seconds");
-
-/* after three payloads of overflow, it's dead jim */
-static unsigned int scrub_overflow_abort = 3;
-module_param(scrub_overflow_abort, uint, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(scrub_overflow_abort,
- "Number of times we overflow ARS results before abort");
-
static bool disable_vendor_specific;
module_param(disable_vendor_specific, bool, S_IRUGO);
MODULE_PARM_DESC(disable_vendor_specific,
@@ -59,6 +50,10 @@ module_param(default_dsm_family, int, S_IRUGO);
MODULE_PARM_DESC(default_dsm_family,
"Try this DSM type first when identifying NVDIMM family");
+static bool no_init_ars;
+module_param(no_init_ars, bool, 0644);
+MODULE_PARM_DESC(no_init_ars, "Skip ARS run at nfit init time");
+
LIST_HEAD(acpi_descs);
DEFINE_MUTEX(acpi_desc_lock);
@@ -196,7 +191,7 @@ static int xlat_nvdimm_status(struct nvdimm *nvdimm, void *buf, unsigned int cmd
* In the _LSI, _LSR, _LSW case the locked status is
* communicated via the read/write commands
*/
- if (nfit_mem->has_lsi)
+ if (nfit_mem->has_lsr)
break;
if (status >> 16 & ND_CONFIG_LOCKED)
@@ -476,14 +471,14 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
in_buf.buffer.length = call_pkg->nd_size_in;
}
- dev_dbg(dev, "%s:%s cmd: %d: func: %d input length: %d\n",
- __func__, dimm_name, cmd, func, in_buf.buffer.length);
+ dev_dbg(dev, "%s cmd: %d: func: %d input length: %d\n",
+ dimm_name, cmd, func, in_buf.buffer.length);
print_hex_dump_debug("nvdimm in ", DUMP_PREFIX_OFFSET, 4, 4,
in_buf.buffer.pointer,
min_t(u32, 256, in_buf.buffer.length), true);
/* call the BIOS, prefer the named methods over _DSM if available */
- if (nvdimm && cmd == ND_CMD_GET_CONFIG_SIZE && nfit_mem->has_lsi)
+ if (nvdimm && cmd == ND_CMD_GET_CONFIG_SIZE && nfit_mem->has_lsr)
out_obj = acpi_label_info(handle);
else if (nvdimm && cmd == ND_CMD_GET_CONFIG_DATA && nfit_mem->has_lsr) {
struct nd_cmd_get_config_data_hdr *p = buf;
@@ -506,8 +501,7 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
}
if (!out_obj) {
- dev_dbg(dev, "%s:%s _DSM failed cmd: %s\n", __func__, dimm_name,
- cmd_name);
+ dev_dbg(dev, "%s _DSM failed cmd: %s\n", dimm_name, cmd_name);
return -EINVAL;
}
@@ -528,13 +522,13 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
}
if (out_obj->package.type != ACPI_TYPE_BUFFER) {
- dev_dbg(dev, "%s:%s unexpected output object type cmd: %s type: %d\n",
- __func__, dimm_name, cmd_name, out_obj->type);
+ dev_dbg(dev, "%s unexpected output object type cmd: %s type: %d\n",
+ dimm_name, cmd_name, out_obj->type);
rc = -EINVAL;
goto out;
}
- dev_dbg(dev, "%s:%s cmd: %s output length: %d\n", __func__, dimm_name,
+ dev_dbg(dev, "%s cmd: %s output length: %d\n", dimm_name,
cmd_name, out_obj->buffer.length);
print_hex_dump_debug(cmd_name, DUMP_PREFIX_OFFSET, 4, 4,
out_obj->buffer.pointer,
@@ -546,14 +540,14 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
out_obj->buffer.length - offset);
if (offset + out_size > out_obj->buffer.length) {
- dev_dbg(dev, "%s:%s output object underflow cmd: %s field: %d\n",
- __func__, dimm_name, cmd_name, i);
+ dev_dbg(dev, "%s output object underflow cmd: %s field: %d\n",
+ dimm_name, cmd_name, i);
break;
}
if (in_buf.buffer.length + offset + out_size > buf_len) {
- dev_dbg(dev, "%s:%s output overrun cmd: %s field: %d\n",
- __func__, dimm_name, cmd_name, i);
+ dev_dbg(dev, "%s output overrun cmd: %s field: %d\n",
+ dimm_name, cmd_name, i);
rc = -ENXIO;
goto out;
}
@@ -655,7 +649,7 @@ static bool add_spa(struct acpi_nfit_desc *acpi_desc,
INIT_LIST_HEAD(&nfit_spa->list);
memcpy(nfit_spa->spa, spa, sizeof(*spa));
list_add_tail(&nfit_spa->list, &acpi_desc->spas);
- dev_dbg(dev, "%s: spa index: %d type: %s\n", __func__,
+ dev_dbg(dev, "spa index: %d type: %s\n",
spa->range_index,
spa_type_name(nfit_spa_type(spa)));
return true;
@@ -684,12 +678,38 @@ static bool add_memdev(struct acpi_nfit_desc *acpi_desc,
INIT_LIST_HEAD(&nfit_memdev->list);
memcpy(nfit_memdev->memdev, memdev, sizeof(*memdev));
list_add_tail(&nfit_memdev->list, &acpi_desc->memdevs);
- dev_dbg(dev, "%s: memdev handle: %#x spa: %d dcr: %d flags: %#x\n",
- __func__, memdev->device_handle, memdev->range_index,
+ dev_dbg(dev, "memdev handle: %#x spa: %d dcr: %d flags: %#x\n",
+ memdev->device_handle, memdev->range_index,
memdev->region_index, memdev->flags);
return true;
}
+int nfit_get_smbios_id(u32 device_handle, u16 *flags)
+{
+ struct acpi_nfit_memory_map *memdev;
+ struct acpi_nfit_desc *acpi_desc;
+ struct nfit_mem *nfit_mem;
+
+ mutex_lock(&acpi_desc_lock);
+ list_for_each_entry(acpi_desc, &acpi_descs, list) {
+ mutex_lock(&acpi_desc->init_mutex);
+ list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) {
+ memdev = __to_nfit_memdev(nfit_mem);
+ if (memdev->device_handle == device_handle) {
+ mutex_unlock(&acpi_desc->init_mutex);
+ mutex_unlock(&acpi_desc_lock);
+ *flags = memdev->flags;
+ return memdev->physical_id;
+ }
+ }
+ mutex_unlock(&acpi_desc->init_mutex);
+ }
+ mutex_unlock(&acpi_desc_lock);
+
+ return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(nfit_get_smbios_id);
+
/*
* An implementation may provide a truncated control region if no block windows
* are defined.
@@ -727,7 +747,7 @@ static bool add_dcr(struct acpi_nfit_desc *acpi_desc,
INIT_LIST_HEAD(&nfit_dcr->list);
memcpy(nfit_dcr->dcr, dcr, sizeof_dcr(dcr));
list_add_tail(&nfit_dcr->list, &acpi_desc->dcrs);
- dev_dbg(dev, "%s: dcr index: %d windows: %d\n", __func__,
+ dev_dbg(dev, "dcr index: %d windows: %d\n",
dcr->region_index, dcr->windows);
return true;
}
@@ -754,7 +774,7 @@ static bool add_bdw(struct acpi_nfit_desc *acpi_desc,
INIT_LIST_HEAD(&nfit_bdw->list);
memcpy(nfit_bdw->bdw, bdw, sizeof(*bdw));
list_add_tail(&nfit_bdw->list, &acpi_desc->bdws);
- dev_dbg(dev, "%s: bdw dcr: %d windows: %d\n", __func__,
+ dev_dbg(dev, "bdw dcr: %d windows: %d\n",
bdw->region_index, bdw->windows);
return true;
}
@@ -793,7 +813,7 @@ static bool add_idt(struct acpi_nfit_desc *acpi_desc,
INIT_LIST_HEAD(&nfit_idt->list);
memcpy(nfit_idt->idt, idt, sizeof_idt(idt));
list_add_tail(&nfit_idt->list, &acpi_desc->idts);
- dev_dbg(dev, "%s: idt index: %d num_lines: %d\n", __func__,
+ dev_dbg(dev, "idt index: %d num_lines: %d\n",
idt->interleave_index, idt->line_count);
return true;
}
@@ -833,7 +853,7 @@ static bool add_flush(struct acpi_nfit_desc *acpi_desc,
INIT_LIST_HEAD(&nfit_flush->list);
memcpy(nfit_flush->flush, flush, sizeof_flush(flush));
list_add_tail(&nfit_flush->list, &acpi_desc->flushes);
- dev_dbg(dev, "%s: nfit_flush handle: %d hint_count: %d\n", __func__,
+ dev_dbg(dev, "nfit_flush handle: %d hint_count: %d\n",
flush->device_handle, flush->hint_count);
return true;
}
@@ -846,7 +866,7 @@ static bool add_platform_cap(struct acpi_nfit_desc *acpi_desc,
mask = (1 << (pcap->highest_capability + 1)) - 1;
acpi_desc->platform_cap = pcap->capabilities & mask;
- dev_dbg(dev, "%s: cap: %#x\n", __func__, acpi_desc->platform_cap);
+ dev_dbg(dev, "cap: %#x\n", acpi_desc->platform_cap);
return true;
}
@@ -893,7 +913,7 @@ static void *add_table(struct acpi_nfit_desc *acpi_desc,
return err;
break;
case ACPI_NFIT_TYPE_SMBIOS:
- dev_dbg(dev, "%s: smbios\n", __func__);
+ dev_dbg(dev, "smbios\n");
break;
case ACPI_NFIT_TYPE_CAPABILITIES:
if (!add_platform_cap(acpi_desc, table))
@@ -1250,8 +1270,11 @@ static ssize_t scrub_show(struct device *dev,
if (nd_desc) {
struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
+ mutex_lock(&acpi_desc->init_mutex);
rc = sprintf(buf, "%d%s", acpi_desc->scrub_count,
- (work_busy(&acpi_desc->work)) ? "+\n" : "\n");
+ work_busy(&acpi_desc->dwork.work)
+ && !acpi_desc->cancel ? "+\n" : "\n");
+ mutex_unlock(&acpi_desc->init_mutex);
}
device_unlock(dev);
return rc;
@@ -1621,7 +1644,7 @@ void __acpi_nvdimm_notify(struct device *dev, u32 event)
struct nfit_mem *nfit_mem;
struct acpi_nfit_desc *acpi_desc;
- dev_dbg(dev->parent, "%s: %s: event: %d\n", dev_name(dev), __func__,
+ dev_dbg(dev->parent, "%s: event: %d\n", dev_name(dev),
event);
if (event != NFIT_NOTIFY_DIMM_HEALTH) {
@@ -1654,12 +1677,23 @@ static void acpi_nvdimm_notify(acpi_handle handle, u32 event, void *data)
device_unlock(dev->parent);
}
+static bool acpi_nvdimm_has_method(struct acpi_device *adev, char *method)
+{
+ acpi_handle handle;
+ acpi_status status;
+
+ status = acpi_get_handle(adev->handle, method, &handle);
+
+ if (ACPI_SUCCESS(status))
+ return true;
+ return false;
+}
+
static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
struct nfit_mem *nfit_mem, u32 device_handle)
{
struct acpi_device *adev, *adev_dimm;
struct device *dev = acpi_desc->dev;
- union acpi_object *obj;
unsigned long dsm_mask;
const guid_t *guid;
int i;
@@ -1732,25 +1766,15 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
1ULL << i))
set_bit(i, &nfit_mem->dsm_mask);
- obj = acpi_label_info(adev_dimm->handle);
- if (obj) {
- ACPI_FREE(obj);
- nfit_mem->has_lsi = 1;
- dev_dbg(dev, "%s: has _LSI\n", dev_name(&adev_dimm->dev));
- }
-
- obj = acpi_label_read(adev_dimm->handle, 0, 0);
- if (obj) {
- ACPI_FREE(obj);
- nfit_mem->has_lsr = 1;
+ if (acpi_nvdimm_has_method(adev_dimm, "_LSI")
+ && acpi_nvdimm_has_method(adev_dimm, "_LSR")) {
dev_dbg(dev, "%s: has _LSR\n", dev_name(&adev_dimm->dev));
+ nfit_mem->has_lsr = true;
}
- obj = acpi_label_write(adev_dimm->handle, 0, 0, NULL);
- if (obj) {
- ACPI_FREE(obj);
- nfit_mem->has_lsw = 1;
+ if (nfit_mem->has_lsr && acpi_nvdimm_has_method(adev_dimm, "_LSW")) {
dev_dbg(dev, "%s: has _LSW\n", dev_name(&adev_dimm->dev));
+ nfit_mem->has_lsw = true;
}
return 0;
@@ -1839,10 +1863,10 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
cmd_mask |= nfit_mem->dsm_mask & NVDIMM_STANDARD_CMDMASK;
}
- if (nfit_mem->has_lsi)
+ if (nfit_mem->has_lsr) {
set_bit(ND_CMD_GET_CONFIG_SIZE, &cmd_mask);
- if (nfit_mem->has_lsr)
set_bit(ND_CMD_GET_CONFIG_DATA, &cmd_mask);
+ }
if (nfit_mem->has_lsw)
set_bit(ND_CMD_SET_CONFIG_DATA, &cmd_mask);
@@ -2338,7 +2362,7 @@ static int acpi_nfit_blk_region_enable(struct nvdimm_bus *nvdimm_bus,
nvdimm = nd_blk_region_to_dimm(ndbr);
nfit_mem = nvdimm_provider_data(nvdimm);
if (!nfit_mem || !nfit_mem->dcr || !nfit_mem->bdw) {
- dev_dbg(dev, "%s: missing%s%s%s\n", __func__,
+ dev_dbg(dev, "missing%s%s%s\n",
nfit_mem ? "" : " nfit_mem",
(nfit_mem && nfit_mem->dcr) ? "" : " dcr",
(nfit_mem && nfit_mem->bdw) ? "" : " bdw");
@@ -2357,7 +2381,7 @@ static int acpi_nfit_blk_region_enable(struct nvdimm_bus *nvdimm_bus,
mmio->addr.base = devm_nvdimm_memremap(dev, nfit_mem->spa_bdw->address,
nfit_mem->spa_bdw->length, nd_blk_memremap_flags(ndbr));
if (!mmio->addr.base) {
- dev_dbg(dev, "%s: %s failed to map bdw\n", __func__,
+ dev_dbg(dev, "%s failed to map bdw\n",
nvdimm_name(nvdimm));
return -ENOMEM;
}
@@ -2368,8 +2392,8 @@ static int acpi_nfit_blk_region_enable(struct nvdimm_bus *nvdimm_bus,
rc = nfit_blk_init_interleave(mmio, nfit_mem->idt_bdw,
nfit_mem->memdev_bdw->interleave_ways);
if (rc) {
- dev_dbg(dev, "%s: %s failed to init bdw interleave\n",
- __func__, nvdimm_name(nvdimm));
+ dev_dbg(dev, "%s failed to init bdw interleave\n",
+ nvdimm_name(nvdimm));
return rc;
}
@@ -2380,7 +2404,7 @@ static int acpi_nfit_blk_region_enable(struct nvdimm_bus *nvdimm_bus,
mmio->addr.base = devm_nvdimm_ioremap(dev, nfit_mem->spa_dcr->address,
nfit_mem->spa_dcr->length);
if (!mmio->addr.base) {
- dev_dbg(dev, "%s: %s failed to map dcr\n", __func__,
+ dev_dbg(dev, "%s failed to map dcr\n",
nvdimm_name(nvdimm));
return -ENOMEM;
}
@@ -2391,15 +2415,15 @@ static int acpi_nfit_blk_region_enable(struct nvdimm_bus *nvdimm_bus,
rc = nfit_blk_init_interleave(mmio, nfit_mem->idt_dcr,
nfit_mem->memdev_dcr->interleave_ways);
if (rc) {
- dev_dbg(dev, "%s: %s failed to init dcr interleave\n",
- __func__, nvdimm_name(nvdimm));
+ dev_dbg(dev, "%s failed to init dcr interleave\n",
+ nvdimm_name(nvdimm));
return rc;
}
rc = acpi_nfit_blk_get_flags(nd_desc, nvdimm, nfit_blk);
if (rc < 0) {
- dev_dbg(dev, "%s: %s failed get DIMM flags\n",
- __func__, nvdimm_name(nvdimm));
+ dev_dbg(dev, "%s failed get DIMM flags\n",
+ nvdimm_name(nvdimm));
return rc;
}
@@ -2449,7 +2473,8 @@ static int ars_start(struct acpi_nfit_desc *acpi_desc, struct nfit_spa *nfit_spa
memset(&ars_start, 0, sizeof(ars_start));
ars_start.address = spa->address;
ars_start.length = spa->length;
- ars_start.flags = acpi_desc->ars_start_flags;
+ if (test_bit(ARS_SHORT, &nfit_spa->ars_state))
+ ars_start.flags = ND_ARS_RETURN_PREV_DATA;
if (nfit_spa_type(spa) == NFIT_SPA_PM)
ars_start.type = ND_ARS_PERSISTENT;
else if (nfit_spa_type(spa) == NFIT_SPA_VOLATILE)
@@ -2491,16 +2516,62 @@ static int ars_get_status(struct acpi_nfit_desc *acpi_desc)
int rc, cmd_rc;
rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_STATUS, ars_status,
- acpi_desc->ars_status_size, &cmd_rc);
+ acpi_desc->max_ars, &cmd_rc);
if (rc < 0)
return rc;
return cmd_rc;
}
-static int ars_status_process_records(struct acpi_nfit_desc *acpi_desc,
- struct nd_cmd_ars_status *ars_status)
+static void ars_complete(struct acpi_nfit_desc *acpi_desc,
+ struct nfit_spa *nfit_spa)
+{
+ struct nd_cmd_ars_status *ars_status = acpi_desc->ars_status;
+ struct acpi_nfit_system_address *spa = nfit_spa->spa;
+ struct nd_region *nd_region = nfit_spa->nd_region;
+ struct device *dev;
+
+ if ((ars_status->address >= spa->address && ars_status->address
+ < spa->address + spa->length)
+ || (ars_status->address < spa->address)) {
+ /*
+ * Assume that if a scrub starts at an offset from the
+ * start of nfit_spa that we are in the continuation
+ * case.
+ *
+ * Otherwise, if the scrub covers the spa range, mark
+ * any pending request complete.
+ */
+ if (ars_status->address + ars_status->length
+ >= spa->address + spa->length)
+ /* complete */;
+ else
+ return;
+ } else
+ return;
+
+ if (test_bit(ARS_DONE, &nfit_spa->ars_state))
+ return;
+
+ if (!test_and_clear_bit(ARS_REQ, &nfit_spa->ars_state))
+ return;
+
+ if (nd_region) {
+ dev = nd_region_dev(nd_region);
+ nvdimm_region_notify(nd_region, NVDIMM_REVALIDATE_POISON);
+ } else
+ dev = acpi_desc->dev;
+
+ dev_dbg(dev, "ARS: range %d %s complete\n", spa->range_index,
+ test_bit(ARS_SHORT, &nfit_spa->ars_state)
+ ? "short" : "long");
+ clear_bit(ARS_SHORT, &nfit_spa->ars_state);
+ set_bit(ARS_DONE, &nfit_spa->ars_state);
+}
+
+static int ars_status_process_records(struct acpi_nfit_desc *acpi_desc)
{
struct nvdimm_bus *nvdimm_bus = acpi_desc->nvdimm_bus;
+ struct nd_cmd_ars_status *ars_status = acpi_desc->ars_status;
int rc;
u32 i;
@@ -2579,7 +2650,7 @@ static int acpi_nfit_init_mapping(struct acpi_nfit_desc *acpi_desc,
struct acpi_nfit_system_address *spa = nfit_spa->spa;
struct nd_blk_region_desc *ndbr_desc;
struct nfit_mem *nfit_mem;
- int blk_valid = 0, rc;
+ int rc;
if (!nvdimm) {
dev_err(acpi_desc->dev, "spa%d dimm: %#x not found\n",
@@ -2599,15 +2670,14 @@ static int acpi_nfit_init_mapping(struct acpi_nfit_desc *acpi_desc,
if (!nfit_mem || !nfit_mem->bdw) {
dev_dbg(acpi_desc->dev, "spa%d %s missing bdw\n",
spa->range_index, nvdimm_name(nvdimm));
- } else {
- mapping->size = nfit_mem->bdw->capacity;
- mapping->start = nfit_mem->bdw->start_address;
- ndr_desc->num_lanes = nfit_mem->bdw->windows;
- blk_valid = 1;
+ break;
}
+ mapping->size = nfit_mem->bdw->capacity;
+ mapping->start = nfit_mem->bdw->start_address;
+ ndr_desc->num_lanes = nfit_mem->bdw->windows;
ndr_desc->mapping = mapping;
- ndr_desc->num_mappings = blk_valid;
+ ndr_desc->num_mappings = 1;
ndbr_desc = to_blk_region_desc(ndr_desc);
ndbr_desc->enable = acpi_nfit_blk_region_enable;
ndbr_desc->do_io = acpi_desc->blk_do_io;
@@ -2655,8 +2725,7 @@ static int acpi_nfit_register_region(struct acpi_nfit_desc *acpi_desc,
return 0;
if (spa->range_index == 0 && !nfit_spa_is_virtual(spa)) {
- dev_dbg(acpi_desc->dev, "%s: detected invalid spa index\n",
- __func__);
+ dev_dbg(acpi_desc->dev, "detected invalid spa index\n");
return 0;
}
@@ -2742,301 +2811,243 @@ static int acpi_nfit_register_region(struct acpi_nfit_desc *acpi_desc,
return rc;
}
-static int ars_status_alloc(struct acpi_nfit_desc *acpi_desc,
- u32 max_ars)
+static int ars_status_alloc(struct acpi_nfit_desc *acpi_desc)
{
struct device *dev = acpi_desc->dev;
struct nd_cmd_ars_status *ars_status;
- if (acpi_desc->ars_status && acpi_desc->ars_status_size >= max_ars) {
- memset(acpi_desc->ars_status, 0, acpi_desc->ars_status_size);
+ if (acpi_desc->ars_status) {
+ memset(acpi_desc->ars_status, 0, acpi_desc->max_ars);
return 0;
}
- if (acpi_desc->ars_status)
- devm_kfree(dev, acpi_desc->ars_status);
- acpi_desc->ars_status = NULL;
- ars_status = devm_kzalloc(dev, max_ars, GFP_KERNEL);
+ ars_status = devm_kzalloc(dev, acpi_desc->max_ars, GFP_KERNEL);
if (!ars_status)
return -ENOMEM;
acpi_desc->ars_status = ars_status;
- acpi_desc->ars_status_size = max_ars;
return 0;
}
-static int acpi_nfit_query_poison(struct acpi_nfit_desc *acpi_desc,
- struct nfit_spa *nfit_spa)
+static int acpi_nfit_query_poison(struct acpi_nfit_desc *acpi_desc)
{
- struct acpi_nfit_system_address *spa = nfit_spa->spa;
int rc;
- if (!nfit_spa->max_ars) {
- struct nd_cmd_ars_cap ars_cap;
-
- memset(&ars_cap, 0, sizeof(ars_cap));
- rc = ars_get_cap(acpi_desc, &ars_cap, nfit_spa);
- if (rc < 0)
- return rc;
- nfit_spa->max_ars = ars_cap.max_ars_out;
- nfit_spa->clear_err_unit = ars_cap.clear_err_unit;
- /* check that the supported scrub types match the spa type */
- if (nfit_spa_type(spa) == NFIT_SPA_VOLATILE &&
- ((ars_cap.status >> 16) & ND_ARS_VOLATILE) == 0)
- return -ENOTTY;
- else if (nfit_spa_type(spa) == NFIT_SPA_PM &&
- ((ars_cap.status >> 16) & ND_ARS_PERSISTENT) == 0)
- return -ENOTTY;
- }
-
- if (ars_status_alloc(acpi_desc, nfit_spa->max_ars))
+ if (ars_status_alloc(acpi_desc))
return -ENOMEM;
rc = ars_get_status(acpi_desc);
+
if (rc < 0 && rc != -ENOSPC)
return rc;
- if (ars_status_process_records(acpi_desc, acpi_desc->ars_status))
+ if (ars_status_process_records(acpi_desc))
return -ENOMEM;
return 0;
}
-static void acpi_nfit_async_scrub(struct acpi_nfit_desc *acpi_desc,
- struct nfit_spa *nfit_spa)
+static int ars_register(struct acpi_nfit_desc *acpi_desc, struct nfit_spa *nfit_spa,
+ int *query_rc)
{
- struct acpi_nfit_system_address *spa = nfit_spa->spa;
- unsigned int overflow_retry = scrub_overflow_abort;
- u64 init_ars_start = 0, init_ars_len = 0;
- struct device *dev = acpi_desc->dev;
- unsigned int tmo = scrub_timeout;
- int rc;
+ int rc = *query_rc;
- if (!nfit_spa->ars_required || !nfit_spa->nd_region)
- return;
-
- rc = ars_start(acpi_desc, nfit_spa);
- /*
- * If we timed out the initial scan we'll still be busy here,
- * and will wait another timeout before giving up permanently.
- */
- if (rc < 0 && rc != -EBUSY)
- return;
+ if (no_init_ars)
+ return acpi_nfit_register_region(acpi_desc, nfit_spa);
- do {
- u64 ars_start, ars_len;
-
- if (acpi_desc->cancel)
- break;
- rc = acpi_nfit_query_poison(acpi_desc, nfit_spa);
- if (rc == -ENOTTY)
- break;
- if (rc == -EBUSY && !tmo) {
- dev_warn(dev, "range %d ars timeout, aborting\n",
- spa->range_index);
- break;
- }
+ set_bit(ARS_REQ, &nfit_spa->ars_state);
+ set_bit(ARS_SHORT, &nfit_spa->ars_state);
+ switch (rc) {
+ case 0:
+ case -EAGAIN:
+ rc = ars_start(acpi_desc, nfit_spa);
if (rc == -EBUSY) {
- /*
- * Note, entries may be appended to the list
- * while the lock is dropped, but the workqueue
- * being active prevents entries being deleted /
- * freed.
- */
- mutex_unlock(&acpi_desc->init_mutex);
- ssleep(1);
- tmo--;
- mutex_lock(&acpi_desc->init_mutex);
- continue;
- }
-
- /* we got some results, but there are more pending... */
- if (rc == -ENOSPC && overflow_retry--) {
- if (!init_ars_len) {
- init_ars_len = acpi_desc->ars_status->length;
- init_ars_start = acpi_desc->ars_status->address;
- }
- rc = ars_continue(acpi_desc);
- }
-
- if (rc < 0) {
- dev_warn(dev, "range %d ars continuation failed\n",
- spa->range_index);
+ *query_rc = rc;
break;
- }
-
- if (init_ars_len) {
- ars_start = init_ars_start;
- ars_len = init_ars_len;
+ } else if (rc == 0) {
+ rc = acpi_nfit_query_poison(acpi_desc);
} else {
- ars_start = acpi_desc->ars_status->address;
- ars_len = acpi_desc->ars_status->length;
+ set_bit(ARS_FAILED, &nfit_spa->ars_state);
+ break;
}
- dev_dbg(dev, "spa range: %d ars from %#llx + %#llx complete\n",
- spa->range_index, ars_start, ars_len);
- /* notify the region about new poison entries */
- nvdimm_region_notify(nfit_spa->nd_region,
- NVDIMM_REVALIDATE_POISON);
+ if (rc == -EAGAIN)
+ clear_bit(ARS_SHORT, &nfit_spa->ars_state);
+ else if (rc == 0)
+ ars_complete(acpi_desc, nfit_spa);
break;
- } while (1);
+ case -EBUSY:
+ case -ENOSPC:
+ break;
+ default:
+ set_bit(ARS_FAILED, &nfit_spa->ars_state);
+ break;
+ }
+
+ if (test_and_clear_bit(ARS_DONE, &nfit_spa->ars_state))
+ set_bit(ARS_REQ, &nfit_spa->ars_state);
+
+ return acpi_nfit_register_region(acpi_desc, nfit_spa);
}
-static void acpi_nfit_scrub(struct work_struct *work)
+static void ars_complete_all(struct acpi_nfit_desc *acpi_desc)
{
- struct device *dev;
- u64 init_scrub_length = 0;
struct nfit_spa *nfit_spa;
- u64 init_scrub_address = 0;
- bool init_ars_done = false;
- struct acpi_nfit_desc *acpi_desc;
- unsigned int tmo = scrub_timeout;
- unsigned int overflow_retry = scrub_overflow_abort;
-
- acpi_desc = container_of(work, typeof(*acpi_desc), work);
- dev = acpi_desc->dev;
-
- /*
- * We scrub in 2 phases. The first phase waits for any platform
- * firmware initiated scrubs to complete and then we go search for the
- * affected spa regions to mark them scanned. In the second phase we
- * initiate a directed scrub for every range that was not scrubbed in
- * phase 1. If we're called for a 'rescan', we harmlessly pass through
- * the first phase, but really only care about running phase 2, where
- * regions can be notified of new poison.
- */
- /* process platform firmware initiated scrubs */
- retry:
- mutex_lock(&acpi_desc->init_mutex);
list_for_each_entry(nfit_spa, &acpi_desc->spas, list) {
- struct nd_cmd_ars_status *ars_status;
- struct acpi_nfit_system_address *spa;
- u64 ars_start, ars_len;
- int rc;
-
- if (acpi_desc->cancel)
- break;
-
- if (nfit_spa->nd_region)
- continue;
-
- if (init_ars_done) {
- /*
- * No need to re-query, we're now just
- * reconciling all the ranges covered by the
- * initial scrub
- */
- rc = 0;
- } else
- rc = acpi_nfit_query_poison(acpi_desc, nfit_spa);
-
- if (rc == -ENOTTY) {
- /* no ars capability, just register spa and move on */
- acpi_nfit_register_region(acpi_desc, nfit_spa);
+ if (test_bit(ARS_FAILED, &nfit_spa->ars_state))
continue;
- }
-
- if (rc == -EBUSY && !tmo) {
- /* fallthrough to directed scrub in phase 2 */
- dev_warn(dev, "timeout awaiting ars results, continuing...\n");
- break;
- } else if (rc == -EBUSY) {
- mutex_unlock(&acpi_desc->init_mutex);
- ssleep(1);
- tmo--;
- goto retry;
- }
+ ars_complete(acpi_desc, nfit_spa);
+ }
+}
- /* we got some results, but there are more pending... */
- if (rc == -ENOSPC && overflow_retry--) {
- ars_status = acpi_desc->ars_status;
- /*
- * Record the original scrub range, so that we
- * can recall all the ranges impacted by the
- * initial scrub.
- */
- if (!init_scrub_length) {
- init_scrub_length = ars_status->length;
- init_scrub_address = ars_status->address;
- }
- rc = ars_continue(acpi_desc);
- if (rc == 0) {
- mutex_unlock(&acpi_desc->init_mutex);
- goto retry;
- }
- }
+static unsigned int __acpi_nfit_scrub(struct acpi_nfit_desc *acpi_desc,
+ int query_rc)
+{
+ unsigned int tmo = acpi_desc->scrub_tmo;
+ struct device *dev = acpi_desc->dev;
+ struct nfit_spa *nfit_spa;
- if (rc < 0) {
- /*
- * Initial scrub failed, we'll give it one more
- * try below...
- */
- break;
- }
+ if (acpi_desc->cancel)
+ return 0;
- /* We got some final results, record completed ranges */
- ars_status = acpi_desc->ars_status;
- if (init_scrub_length) {
- ars_start = init_scrub_address;
- ars_len = ars_start + init_scrub_length;
- } else {
- ars_start = ars_status->address;
- ars_len = ars_status->length;
- }
- spa = nfit_spa->spa;
+ if (query_rc == -EBUSY) {
+ dev_dbg(dev, "ARS: ARS busy\n");
+ return min(30U * 60U, tmo * 2);
+ }
+ if (query_rc == -ENOSPC) {
+ dev_dbg(dev, "ARS: ARS continue\n");
+ ars_continue(acpi_desc);
+ return 1;
+ }
+ if (query_rc && query_rc != -EAGAIN) {
+ unsigned long long addr, end;
- if (!init_ars_done) {
- init_ars_done = true;
- dev_dbg(dev, "init scrub %#llx + %#llx complete\n",
- ars_start, ars_len);
- }
- if (ars_start <= spa->address && ars_start + ars_len
- >= spa->address + spa->length)
- acpi_nfit_register_region(acpi_desc, nfit_spa);
+ addr = acpi_desc->ars_status->address;
+ end = addr + acpi_desc->ars_status->length;
+ dev_dbg(dev, "ARS: %llx-%llx failed (%d)\n", addr, end,
+ query_rc);
}
- /*
- * For all the ranges not covered by an initial scrub we still
- * want to see if there are errors, but it's ok to discover them
- * asynchronously.
- */
+ ars_complete_all(acpi_desc);
list_for_each_entry(nfit_spa, &acpi_desc->spas, list) {
- /*
- * Flag all the ranges that still need scrubbing, but
- * register them now to make data available.
- */
- if (!nfit_spa->nd_region) {
- nfit_spa->ars_required = 1;
- acpi_nfit_register_region(acpi_desc, nfit_spa);
+ if (test_bit(ARS_FAILED, &nfit_spa->ars_state))
+ continue;
+ if (test_bit(ARS_REQ, &nfit_spa->ars_state)) {
+ int rc = ars_start(acpi_desc, nfit_spa);
+
+ clear_bit(ARS_DONE, &nfit_spa->ars_state);
+ dev = nd_region_dev(nfit_spa->nd_region);
+ dev_dbg(dev, "ARS: range %d ARS start (%d)\n",
+ nfit_spa->spa->range_index, rc);
+ if (rc == 0 || rc == -EBUSY)
+ return 1;
+ dev_err(dev, "ARS: range %d ARS failed (%d)\n",
+ nfit_spa->spa->range_index, rc);
+ set_bit(ARS_FAILED, &nfit_spa->ars_state);
}
}
- acpi_desc->init_complete = 1;
+ return 0;
+}
- list_for_each_entry(nfit_spa, &acpi_desc->spas, list)
- acpi_nfit_async_scrub(acpi_desc, nfit_spa);
- acpi_desc->scrub_count++;
- acpi_desc->ars_start_flags = 0;
- if (acpi_desc->scrub_count_state)
- sysfs_notify_dirent(acpi_desc->scrub_count_state);
+static void acpi_nfit_scrub(struct work_struct *work)
+{
+ struct acpi_nfit_desc *acpi_desc;
+ unsigned int tmo;
+ int query_rc;
+
+ acpi_desc = container_of(work, typeof(*acpi_desc), dwork.work);
+ mutex_lock(&acpi_desc->init_mutex);
+ query_rc = acpi_nfit_query_poison(acpi_desc);
+ tmo = __acpi_nfit_scrub(acpi_desc, query_rc);
+ if (tmo) {
+ queue_delayed_work(nfit_wq, &acpi_desc->dwork, tmo * HZ);
+ acpi_desc->scrub_tmo = tmo;
+ } else {
+ acpi_desc->scrub_count++;
+ if (acpi_desc->scrub_count_state)
+ sysfs_notify_dirent(acpi_desc->scrub_count_state);
+ }
+ memset(acpi_desc->ars_status, 0, acpi_desc->max_ars);
mutex_unlock(&acpi_desc->init_mutex);
}
+static void acpi_nfit_init_ars(struct acpi_nfit_desc *acpi_desc,
+ struct nfit_spa *nfit_spa)
+{
+ int type = nfit_spa_type(nfit_spa->spa);
+ struct nd_cmd_ars_cap ars_cap;
+ int rc;
+
+ memset(&ars_cap, 0, sizeof(ars_cap));
+ rc = ars_get_cap(acpi_desc, &ars_cap, nfit_spa);
+ if (rc < 0)
+ return;
+ /* check that the supported scrub types match the spa type */
+ if (type == NFIT_SPA_VOLATILE && ((ars_cap.status >> 16)
+ & ND_ARS_VOLATILE) == 0)
+ return;
+ if (type == NFIT_SPA_PM && ((ars_cap.status >> 16)
+ & ND_ARS_PERSISTENT) == 0)
+ return;
+
+ nfit_spa->max_ars = ars_cap.max_ars_out;
+ nfit_spa->clear_err_unit = ars_cap.clear_err_unit;
+ acpi_desc->max_ars = max(nfit_spa->max_ars, acpi_desc->max_ars);
+ clear_bit(ARS_FAILED, &nfit_spa->ars_state);
+ set_bit(ARS_REQ, &nfit_spa->ars_state);
+}
+
static int acpi_nfit_register_regions(struct acpi_nfit_desc *acpi_desc)
{
struct nfit_spa *nfit_spa;
- int rc;
+ int rc, query_rc;
+
+ list_for_each_entry(nfit_spa, &acpi_desc->spas, list) {
+ set_bit(ARS_FAILED, &nfit_spa->ars_state);
+ switch (nfit_spa_type(nfit_spa->spa)) {
+ case NFIT_SPA_VOLATILE:
+ case NFIT_SPA_PM:
+ acpi_nfit_init_ars(acpi_desc, nfit_spa);
+ break;
+ }
+ }
+
+ /*
+ * Reap any results that might be pending before starting new
+ * short requests.
+ */
+ query_rc = acpi_nfit_query_poison(acpi_desc);
+ if (query_rc == 0)
+ ars_complete_all(acpi_desc);
list_for_each_entry(nfit_spa, &acpi_desc->spas, list)
- if (nfit_spa_type(nfit_spa->spa) == NFIT_SPA_DCR) {
- /* BLK regions don't need to wait for ars results */
+ switch (nfit_spa_type(nfit_spa->spa)) {
+ case NFIT_SPA_VOLATILE:
+ case NFIT_SPA_PM:
+ /* register regions and kick off initial ARS run */
+ rc = ars_register(acpi_desc, nfit_spa, &query_rc);
+ if (rc)
+ return rc;
+ break;
+ case NFIT_SPA_BDW:
+ /* nothing to register */
+ break;
+ case NFIT_SPA_DCR:
+ case NFIT_SPA_VDISK:
+ case NFIT_SPA_VCD:
+ case NFIT_SPA_PDISK:
+ case NFIT_SPA_PCD:
+ /* register known regions that don't support ARS */
rc = acpi_nfit_register_region(acpi_desc, nfit_spa);
if (rc)
return rc;
+ break;
+ default:
+ /* don't register unknown regions */
+ break;
}
- acpi_desc->ars_start_flags = 0;
- if (!acpi_desc->cancel)
- queue_work(nfit_wq, &acpi_desc->work);
+ queue_delayed_work(nfit_wq, &acpi_desc->dwork, 0);
return 0;
}
@@ -3146,8 +3157,7 @@ int acpi_nfit_init(struct acpi_nfit_desc *acpi_desc, void *data, acpi_size sz)
data = add_table(acpi_desc, &prev, data, end);
if (IS_ERR(data)) {
- dev_dbg(dev, "%s: nfit table parsing error: %ld\n", __func__,
- PTR_ERR(data));
+ dev_dbg(dev, "nfit table parsing error: %ld\n", PTR_ERR(data));
rc = PTR_ERR(data);
goto out_unlock;
}
@@ -3172,49 +3182,20 @@ int acpi_nfit_init(struct acpi_nfit_desc *acpi_desc, void *data, acpi_size sz)
}
EXPORT_SYMBOL_GPL(acpi_nfit_init);
-struct acpi_nfit_flush_work {
- struct work_struct work;
- struct completion cmp;
-};
-
-static void flush_probe(struct work_struct *work)
-{
- struct acpi_nfit_flush_work *flush;
-
- flush = container_of(work, typeof(*flush), work);
- complete(&flush->cmp);
-}
-
static int acpi_nfit_flush_probe(struct nvdimm_bus_descriptor *nd_desc)
{
struct acpi_nfit_desc *acpi_desc = to_acpi_nfit_desc(nd_desc);
struct device *dev = acpi_desc->dev;
- struct acpi_nfit_flush_work flush;
- int rc;
- /* bounce the device lock to flush acpi_nfit_add / acpi_nfit_notify */
+ /* Bounce the device lock to flush acpi_nfit_add / acpi_nfit_notify */
device_lock(dev);
device_unlock(dev);
- /* bounce the init_mutex to make init_complete valid */
+ /* Bounce the init_mutex to complete initial registration */
mutex_lock(&acpi_desc->init_mutex);
- if (acpi_desc->cancel || acpi_desc->init_complete) {
- mutex_unlock(&acpi_desc->init_mutex);
- return 0;
- }
-
- /*
- * Scrub work could take 10s of seconds, userspace may give up so we
- * need to be interruptible while waiting.
- */
- INIT_WORK_ONSTACK(&flush.work, flush_probe);
- init_completion(&flush.cmp);
- queue_work(nfit_wq, &flush.work);
mutex_unlock(&acpi_desc->init_mutex);
- rc = wait_for_completion_interruptible(&flush.cmp);
- cancel_work_sync(&flush.work);
- return rc;
+ return 0;
}
static int acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor *nd_desc,
@@ -3233,20 +3214,18 @@ static int acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor *nd_desc,
* just needs guarantees that any ars it initiates are not
* interrupted by any intervening start reqeusts from userspace.
*/
- if (work_busy(&acpi_desc->work))
+ if (work_busy(&acpi_desc->dwork.work))
return -EBUSY;
return 0;
}
-int acpi_nfit_ars_rescan(struct acpi_nfit_desc *acpi_desc, u8 flags)
+int acpi_nfit_ars_rescan(struct acpi_nfit_desc *acpi_desc, unsigned long flags)
{
struct device *dev = acpi_desc->dev;
+ int scheduled = 0, busy = 0;
struct nfit_spa *nfit_spa;
- if (work_busy(&acpi_desc->work))
- return -EBUSY;
-
mutex_lock(&acpi_desc->init_mutex);
if (acpi_desc->cancel) {
mutex_unlock(&acpi_desc->init_mutex);
@@ -3254,19 +3233,32 @@ int acpi_nfit_ars_rescan(struct acpi_nfit_desc *acpi_desc, u8 flags)
}
list_for_each_entry(nfit_spa, &acpi_desc->spas, list) {
- struct acpi_nfit_system_address *spa = nfit_spa->spa;
+ int type = nfit_spa_type(nfit_spa->spa);
- if (nfit_spa_type(spa) != NFIT_SPA_PM)
+ if (type != NFIT_SPA_PM && type != NFIT_SPA_VOLATILE)
+ continue;
+ if (test_bit(ARS_FAILED, &nfit_spa->ars_state))
continue;
- nfit_spa->ars_required = 1;
+ if (test_and_set_bit(ARS_REQ, &nfit_spa->ars_state))
+ busy++;
+ else {
+ if (test_bit(ARS_SHORT, &flags))
+ set_bit(ARS_SHORT, &nfit_spa->ars_state);
+ scheduled++;
+ }
+ }
+ if (scheduled) {
+ queue_delayed_work(nfit_wq, &acpi_desc->dwork, 0);
+ dev_dbg(dev, "ars_scan triggered\n");
}
- acpi_desc->ars_start_flags = flags;
- queue_work(nfit_wq, &acpi_desc->work);
- dev_dbg(dev, "%s: ars_scan triggered\n", __func__);
mutex_unlock(&acpi_desc->init_mutex);
- return 0;
+ if (scheduled)
+ return 0;
+ if (busy)
+ return -EBUSY;
+ return -ENOTTY;
}
void acpi_nfit_desc_init(struct acpi_nfit_desc *acpi_desc, struct device *dev)
@@ -3293,7 +3285,8 @@ void acpi_nfit_desc_init(struct acpi_nfit_desc *acpi_desc, struct device *dev)
INIT_LIST_HEAD(&acpi_desc->dimms);
INIT_LIST_HEAD(&acpi_desc->list);
mutex_init(&acpi_desc->init_mutex);
- INIT_WORK(&acpi_desc->work, acpi_nfit_scrub);
+ acpi_desc->scrub_tmo = 1;
+ INIT_DELAYED_WORK(&acpi_desc->dwork, acpi_nfit_scrub);
}
EXPORT_SYMBOL_GPL(acpi_nfit_desc_init);
@@ -3317,6 +3310,7 @@ void acpi_nfit_shutdown(void *data)
mutex_lock(&acpi_desc->init_mutex);
acpi_desc->cancel = 1;
+ cancel_delayed_work_sync(&acpi_desc->dwork);
mutex_unlock(&acpi_desc->init_mutex);
/*
@@ -3370,8 +3364,8 @@ static int acpi_nfit_add(struct acpi_device *adev)
rc = acpi_nfit_init(acpi_desc, obj->buffer.pointer,
obj->buffer.length);
else
- dev_dbg(dev, "%s invalid type %d, ignoring _FIT\n",
- __func__, (int) obj->type);
+ dev_dbg(dev, "invalid type %d, ignoring _FIT\n",
+ (int) obj->type);
kfree(buf.pointer);
} else
/* skip over the lead-in header table */
@@ -3400,7 +3394,7 @@ static void acpi_nfit_update_notify(struct device *dev, acpi_handle handle)
if (!dev->driver) {
/* dev->driver may be null if we're being removed */
- dev_dbg(dev, "%s: no driver found for dev\n", __func__);
+ dev_dbg(dev, "no driver found for dev\n");
return;
}
@@ -3438,15 +3432,15 @@ static void acpi_nfit_update_notify(struct device *dev, acpi_handle handle)
static void acpi_nfit_uc_error_notify(struct device *dev, acpi_handle handle)
{
struct acpi_nfit_desc *acpi_desc = dev_get_drvdata(dev);
- u8 flags = (acpi_desc->scrub_mode == HW_ERROR_SCRUB_ON) ?
- 0 : ND_ARS_RETURN_PREV_DATA;
+ unsigned long flags = (acpi_desc->scrub_mode == HW_ERROR_SCRUB_ON) ?
+ 0 : 1 << ARS_SHORT;
acpi_nfit_ars_rescan(acpi_desc, flags);
}
void __acpi_nfit_notify(struct device *dev, acpi_handle handle, u32 event)
{
- dev_dbg(dev, "%s: event: 0x%x\n", __func__, event);
+ dev_dbg(dev, "event: 0x%x\n", event);
switch (event) {
case NFIT_NOTIFY_UPDATE:
diff --git a/drivers/acpi/nfit/mce.c b/drivers/acpi/nfit/mce.c
index b92921439657..e9626bf6ca29 100644
--- a/drivers/acpi/nfit/mce.c
+++ b/drivers/acpi/nfit/mce.c
@@ -51,9 +51,8 @@ static int nfit_handle_mce(struct notifier_block *nb, unsigned long val,
if ((spa->address + spa->length - 1) < mce->addr)
continue;
found_match = 1;
- dev_dbg(dev, "%s: addr in SPA %d (0x%llx, 0x%llx)\n",
- __func__, spa->range_index, spa->address,
- spa->length);
+ dev_dbg(dev, "addr in SPA %d (0x%llx, 0x%llx)\n",
+ spa->range_index, spa->address, spa->length);
/*
* We can break at the first match because we're going
* to rescan all the SPA ranges. There shouldn't be any
diff --git a/drivers/acpi/nfit/nfit.h b/drivers/acpi/nfit/nfit.h
index 50d36e166d70..7d15856a739f 100644
--- a/drivers/acpi/nfit/nfit.h
+++ b/drivers/acpi/nfit/nfit.h
@@ -117,10 +117,17 @@ enum nfit_dimm_notifiers {
NFIT_NOTIFY_DIMM_HEALTH = 0x81,
};
+enum nfit_ars_state {
+ ARS_REQ,
+ ARS_DONE,
+ ARS_SHORT,
+ ARS_FAILED,
+};
+
struct nfit_spa {
struct list_head list;
struct nd_region *nd_region;
- unsigned int ars_required:1;
+ unsigned long ars_state;
u32 clear_err_unit;
u32 max_ars;
struct acpi_nfit_system_address spa[0];
@@ -171,9 +178,8 @@ struct nfit_mem {
struct resource *flush_wpq;
unsigned long dsm_mask;
int family;
- u32 has_lsi:1;
- u32 has_lsr:1;
- u32 has_lsw:1;
+ bool has_lsr;
+ bool has_lsw;
};
struct acpi_nfit_desc {
@@ -191,18 +197,18 @@ struct acpi_nfit_desc {
struct device *dev;
u8 ars_start_flags;
struct nd_cmd_ars_status *ars_status;
- size_t ars_status_size;
- struct work_struct work;
+ struct delayed_work dwork;
struct list_head list;
struct kernfs_node *scrub_count_state;
+ unsigned int max_ars;
unsigned int scrub_count;
unsigned int scrub_mode;
unsigned int cancel:1;
- unsigned int init_complete:1;
unsigned long dimm_cmd_force_en;
unsigned long bus_cmd_force_en;
unsigned long bus_nfit_cmd_force_en;
unsigned int platform_cap;
+ unsigned int scrub_tmo;
int (*blk_do_io)(struct nd_blk_region *ndbr, resource_size_t dpa,
void *iobuf, u64 len, int rw);
};
@@ -244,7 +250,7 @@ struct nfit_blk {
extern struct list_head acpi_descs;
extern struct mutex acpi_desc_lock;
-int acpi_nfit_ars_rescan(struct acpi_nfit_desc *acpi_desc, u8 flags);
+int acpi_nfit_ars_rescan(struct acpi_nfit_desc *acpi_desc, unsigned long flags);
#ifdef CONFIG_X86_MCE
void nfit_mce_register(void);
diff --git a/drivers/acpi/osi.c b/drivers/acpi/osi.c
index 76998a51bf99..8a8f43568510 100644
--- a/drivers/acpi/osi.c
+++ b/drivers/acpi/osi.c
@@ -57,6 +57,15 @@ osi_setup_entries[OSI_STRING_ENTRIES_MAX] __initdata = {
{"Processor Device", true},
{"3.0 _SCP Extensions", true},
{"Processor Aggregator Device", true},
+ /*
+ * Linux-Dell-Video is used by BIOS to disable RTD3 for NVidia graphics
+ * cards as RTD3 is not supported by drivers now. Systems with NVidia
+ * cards will hang without RTD3 disabled.
+ *
+ * Once NVidia drivers officially support RTD3, this _OSI strings can
+ * be removed if both new and old graphics cards are supported.
+ */
+ {"Linux-Dell-Video", true},
};
static u32 acpi_osi_handler(acpi_string interface, u32 supported)
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 3bb46cb24a99..7ca41bf023c9 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -189,12 +189,15 @@ early_param("acpi_rsdp", setup_acpi_rsdp);
acpi_physical_address __init acpi_os_get_root_pointer(void)
{
- acpi_physical_address pa = 0;
+ acpi_physical_address pa;
#ifdef CONFIG_KEXEC
if (acpi_rsdp)
return acpi_rsdp;
#endif
+ pa = acpi_arch_get_root_pointer();
+ if (pa)
+ return pa;
if (efi_enabled(EFI_CONFIG_TABLES)) {
if (efi.acpi20 != EFI_INVALID_TABLE_ADDR)
diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c
index 85ad679390e3..d5eec352a6e1 100644
--- a/drivers/acpi/pci_link.c
+++ b/drivers/acpi/pci_link.c
@@ -173,8 +173,8 @@ static int acpi_pci_link_get_possible(struct acpi_pci_link *link)
status = acpi_walk_resources(link->device->handle, METHOD_NAME__PRS,
acpi_pci_link_check_possible, link);
if (ACPI_FAILURE(status)) {
- ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PRS"));
- return -ENODEV;
+ acpi_handle_debug(link->device->handle, "_PRS not present or invalid");
+ return 0;
}
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index 6fc204a52493..0da18bde6a16 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -729,7 +729,8 @@ next:
}
}
-static void acpi_pci_root_remap_iospace(struct resource_entry *entry)
+static void acpi_pci_root_remap_iospace(struct fwnode_handle *fwnode,
+ struct resource_entry *entry)
{
#ifdef PCI_IOBASE
struct resource *res = entry->res;
@@ -738,7 +739,7 @@ static void acpi_pci_root_remap_iospace(struct resource_entry *entry)
resource_size_t length = resource_size(res);
unsigned long port;
- if (pci_register_io_range(cpu_addr, length))
+ if (pci_register_io_range(fwnode, cpu_addr, length))
goto err;
port = pci_address_to_pio(cpu_addr);
@@ -780,7 +781,8 @@ int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info)
else {
resource_list_for_each_entry_safe(entry, tmp, list) {
if (entry->res->flags & IORESOURCE_IO)
- acpi_pci_root_remap_iospace(entry);
+ acpi_pci_root_remap_iospace(&device->fwnode,
+ entry);
if (entry->res->flags & IORESOURCE_DISABLED)
resource_list_destroy_entry(entry);
@@ -871,6 +873,7 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
struct acpi_device *device = root->device;
int node = acpi_get_node(device->handle);
struct pci_bus *bus;
+ struct pci_host_bridge *host_bridge;
info->root = root;
info->bridge = device;
@@ -895,9 +898,17 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
if (!bus)
goto out_release_info;
+ host_bridge = to_pci_host_bridge(bus->bridge);
+ if (!(root->osc_control_set & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL))
+ host_bridge->native_hotplug = 0;
+ if (!(root->osc_control_set & OSC_PCI_EXPRESS_AER_CONTROL))
+ host_bridge->native_aer = 0;
+ if (!(root->osc_control_set & OSC_PCI_EXPRESS_PME_CONTROL))
+ host_bridge->native_pme = 0;
+
pci_scan_child_bus(bus);
- pci_set_host_bridge_release(to_pci_host_bridge(bus->bridge),
- acpi_pci_root_release_info, info);
+ pci_set_host_bridge_release(host_bridge, acpi_pci_root_release_info,
+ info);
if (node != NUMA_NO_NODE)
dev_printk(KERN_DEBUG, &bus->dev, "on NUMA node %d\n", node);
return bus;
diff --git a/drivers/acpi/pmic/tps68470_pmic.c b/drivers/acpi/pmic/tps68470_pmic.c
index 7f3c567e8168..a083de507009 100644
--- a/drivers/acpi/pmic/tps68470_pmic.c
+++ b/drivers/acpi/pmic/tps68470_pmic.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* TI TPS68470 PMIC operation region driver
*
@@ -5,15 +6,6 @@
*
* Author: Rajmohan Mani <rajmohan.mani@intel.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.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
* Based on drivers/acpi/pmic/intel_pmic* drivers
*/
diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c
index c7cf48ad5cb9..a651ab3490d8 100644
--- a/drivers/acpi/processor_perflib.c
+++ b/drivers/acpi/processor_perflib.c
@@ -533,7 +533,7 @@ int acpi_processor_notify_smm(struct module *calling_module)
EXPORT_SYMBOL(acpi_processor_notify_smm);
-static int acpi_processor_get_psd(struct acpi_processor *pr)
+int acpi_processor_get_psd(acpi_handle handle, struct acpi_psd_package *pdomain)
{
int result = 0;
acpi_status status = AE_OK;
@@ -541,9 +541,8 @@ static int acpi_processor_get_psd(struct acpi_processor *pr)
struct acpi_buffer format = {sizeof("NNNNN"), "NNNNN"};
struct acpi_buffer state = {0, NULL};
union acpi_object *psd = NULL;
- struct acpi_psd_package *pdomain;
- status = acpi_evaluate_object(pr->handle, "_PSD", NULL, &buffer);
+ status = acpi_evaluate_object(handle, "_PSD", NULL, &buffer);
if (ACPI_FAILURE(status)) {
return -ENODEV;
}
@@ -561,8 +560,6 @@ static int acpi_processor_get_psd(struct acpi_processor *pr)
goto end;
}
- pdomain = &(pr->performance->domain_info);
-
state.length = sizeof(struct acpi_psd_package);
state.pointer = pdomain;
@@ -597,6 +594,7 @@ end:
kfree(buffer.pointer);
return result;
}
+EXPORT_SYMBOL(acpi_processor_get_psd);
int acpi_processor_preregister_performance(
struct acpi_processor_performance __percpu *performance)
@@ -645,7 +643,8 @@ int acpi_processor_preregister_performance(
pr->performance = per_cpu_ptr(performance, i);
cpumask_set_cpu(i, pr->performance->shared_cpu_map);
- if (acpi_processor_get_psd(pr)) {
+ pdomain = &(pr->performance->domain_info);
+ if (acpi_processor_get_psd(pr->handle, pdomain)) {
retval = -EINVAL;
continue;
}
diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c
index a2428e9462dd..295b59271189 100644
--- a/drivers/acpi/sbs.c
+++ b/drivers/acpi/sbs.c
@@ -32,9 +32,9 @@
#include <linux/delay.h>
#include <linux/power_supply.h>
#include <linux/platform_data/x86/apple.h>
+#include <acpi/battery.h>
#include "sbshc.h"
-#include "battery.h"
#define PREFIX "ACPI: "
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 8e63d937babb..970dd87d347c 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -116,6 +116,7 @@ bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent)
{
struct acpi_device_physical_node *pn;
bool offline = true;
+ char *envp[] = { "EVENT=offline", NULL };
/*
* acpi_container_offline() calls this for all of the container's
@@ -126,7 +127,7 @@ bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent)
list_for_each_entry(pn, &adev->physical_node_list, node)
if (device_supports_offline(pn->dev) && !pn->dev->offline) {
if (uevent)
- kobject_uevent(&pn->dev->kobj, KOBJ_CHANGE);
+ kobject_uevent_env(&pn->dev->kobj, KOBJ_CHANGE, envp);
offline = false;
break;
@@ -1524,11 +1525,25 @@ static int acpi_check_serial_bus_slave(struct acpi_resource *ares, void *data)
return -1;
}
-static bool acpi_is_serial_bus_slave(struct acpi_device *device)
+static bool acpi_is_indirect_io_slave(struct acpi_device *device)
+{
+ struct acpi_device *parent = device->parent;
+ const struct acpi_device_id indirect_io_hosts[] = {
+ {"HISI0191", 0},
+ {}
+ };
+
+ return parent && !acpi_match_device_ids(parent, indirect_io_hosts);
+}
+
+static bool acpi_device_enumeration_by_parent(struct acpi_device *device)
{
struct list_head resource_list;
bool is_serial_bus_slave = false;
+ if (acpi_is_indirect_io_slave(device))
+ return true;
+
/* Macs use device properties in lieu of _CRS resources */
if (x86_apple_machine &&
(fwnode_property_present(&device->fwnode, "spiSclkPeriod") ||
@@ -1560,7 +1575,8 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
acpi_bus_get_flags(device);
device->flags.match_driver = false;
device->flags.initialized = true;
- device->flags.serial_bus_slave = acpi_is_serial_bus_slave(device);
+ device->flags.enumeration_by_parent =
+ acpi_device_enumeration_by_parent(device);
acpi_device_clear_enumerated(device);
device_initialize(&device->dev);
dev_set_uevent_suppress(&device->dev, true);
@@ -1858,10 +1874,10 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,
static void acpi_default_enumeration(struct acpi_device *device)
{
/*
- * Do not enumerate SPI/I2C/UART slaves as they will be enumerated by
- * their respective parents.
+ * Do not enumerate devices with enumeration_by_parent flag set as
+ * they will be enumerated by their respective parents.
*/
- if (!device->flags.serial_bus_slave) {
+ if (!device->flags.enumeration_by_parent) {
acpi_create_platform_device(device, NULL);
acpi_device_set_enumerated(device);
} else {
@@ -1958,7 +1974,7 @@ static void acpi_bus_attach(struct acpi_device *device)
return;
device->flags.match_driver = true;
- if (ret > 0 && !device->flags.serial_bus_slave) {
+ if (ret > 0 && !device->flags.enumeration_by_parent) {
acpi_device_set_enumerated(device);
goto ok;
}
@@ -1967,10 +1983,10 @@ static void acpi_bus_attach(struct acpi_device *device)
if (ret < 0)
return;
- if (!device->pnp.type.platform_id && !device->flags.serial_bus_slave)
- acpi_device_set_enumerated(device);
- else
+ if (device->pnp.type.platform_id || device->flags.enumeration_by_parent)
acpi_default_enumeration(device);
+ else
+ acpi_device_set_enumerated(device);
ok:
list_for_each_entry(child, &device->children, node)
@@ -2150,10 +2166,10 @@ int __init acpi_scan_init(void)
acpi_cmos_rtc_init();
acpi_container_init();
acpi_memory_hotplug_init();
+ acpi_watchdog_init();
acpi_pnp_init();
acpi_int340x_thermal_init();
acpi_amba_init();
- acpi_watchdog_init();
acpi_init_lpit();
acpi_scan_add_handler(&generic_device_handler);
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index 46cde0912762..974e58457697 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -364,6 +364,19 @@ static const struct dmi_system_id acpisleep_dmi_table[] __initconst = {
DMI_MATCH(DMI_PRODUCT_NAME, "XPS 13 9360"),
},
},
+ /*
+ * ThinkPad X1 Tablet(2016) cannot do suspend-to-idle using
+ * the Low Power S0 Idle firmware interface (see
+ * https://bugzilla.kernel.org/show_bug.cgi?id=199057).
+ */
+ {
+ .callback = init_no_lps0,
+ .ident = "ThinkPad X1 Tablet(2016)",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "20GGA00L00"),
+ },
+ },
{},
};
@@ -376,12 +389,10 @@ void __init acpi_sleep_no_blacklist(void)
static void __init acpi_sleep_dmi_check(void)
{
- int year;
-
if (ignore_blacklist)
return;
- if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && year >= 2012)
+ if (dmi_get_bios_year() >= 2012)
acpi_nvs_nosave_s3();
dmi_check_system(acpisleep_dmi_table);
@@ -851,23 +862,25 @@ static void lpi_check_constraints(void)
int i;
for (i = 0; i < lpi_constraints_table_size; ++i) {
+ acpi_handle handle = lpi_constraints_table[i].handle;
struct acpi_device *adev;
- if (acpi_bus_get_device(lpi_constraints_table[i].handle, &adev))
+ if (!handle || acpi_bus_get_device(handle, &adev))
continue;
- acpi_handle_debug(adev->handle,
+ acpi_handle_debug(handle,
"LPI: required min power state:%s current power state:%s\n",
acpi_power_state_string(lpi_constraints_table[i].min_dstate),
acpi_power_state_string(adev->power.state));
if (!adev->flags.power_manageable) {
- acpi_handle_info(adev->handle, "LPI: Device not power manageble\n");
+ acpi_handle_info(handle, "LPI: Device not power manageable\n");
+ lpi_constraints_table[i].handle = NULL;
continue;
}
if (adev->power.state < lpi_constraints_table[i].min_dstate)
- acpi_handle_info(adev->handle,
+ acpi_handle_info(handle,
"LPI: Constraint not met; min power state:%s current power state:%s\n",
acpi_power_state_string(lpi_constraints_table[i].min_dstate),
acpi_power_state_string(adev->power.state));
@@ -953,15 +966,8 @@ static int acpi_s2idle_prepare(void)
if (lps0_device_handle) {
acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_OFF);
acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY);
- } else {
- /*
- * The configuration of GPEs is changed here to avoid spurious
- * wakeups, but that should not be necessary if this is a
- * "low-power S0" platform and the low-power S0 _DSM is present.
- */
- acpi_enable_all_wakeup_gpes();
- acpi_os_wait_events_complete();
}
+
if (acpi_sci_irq_valid())
enable_irq_wake(acpi_sci_irq);
@@ -994,8 +1000,9 @@ static void acpi_s2idle_sync(void)
* The EC driver uses the system workqueue and an additional special
* one, so those need to be flushed too.
*/
+ acpi_os_wait_events_complete(); /* synchronize SCI IRQ handling */
acpi_ec_flush_work();
- acpi_os_wait_events_complete();
+ acpi_os_wait_events_complete(); /* synchronize Notify handling */
s2idle_wakeup = false;
}
@@ -1007,8 +1014,6 @@ static void acpi_s2idle_restore(void)
if (lps0_device_handle) {
acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT);
acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_ON);
- } else {
- acpi_enable_all_runtime_gpes();
}
}
diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c
index 7bcb66ccccf3..849c4fb19b03 100644
--- a/drivers/acpi/tables.c
+++ b/drivers/acpi/tables.c
@@ -457,7 +457,7 @@ static const char * const table_sigs[] = {
ACPI_SIG_UEFI, ACPI_SIG_WAET, ACPI_SIG_WDAT, ACPI_SIG_WDDT,
ACPI_SIG_WDRT, ACPI_SIG_DSDT, ACPI_SIG_FADT, ACPI_SIG_PSDT,
ACPI_SIG_RSDT, ACPI_SIG_XSDT, ACPI_SIG_SSDT, ACPI_SIG_IORT,
- NULL };
+ ACPI_SIG_NFIT, ACPI_SIG_HMAT, NULL };
#define ACPI_HEADER_SIZE sizeof(struct acpi_table_header)
diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c
index 601e5d372887..43587ac680e4 100644
--- a/drivers/acpi/video_detect.c
+++ b/drivers/acpi/video_detect.c
@@ -220,6 +220,15 @@ static const struct dmi_system_id video_detect_dmi_table[] = {
},
},
{
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1557060 */
+ .callback = video_detect_force_video,
+ .ident = "SAMSUNG 670Z5E",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "670Z5E"),
+ },
+ },
+ {
/* https://bugzilla.redhat.com/show_bug.cgi?id=1094948 */
.callback = video_detect_force_video,
.ident = "SAMSUNG 730U3E/740U3E",
diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c
index 594c228d2f02..4a3ac31c07d0 100644
--- a/drivers/amba/bus.c
+++ b/drivers/amba/bus.c
@@ -69,11 +69,12 @@ static ssize_t driver_override_show(struct device *_dev,
struct device_attribute *attr, char *buf)
{
struct amba_device *dev = to_amba_device(_dev);
+ ssize_t len;
- if (!dev->driver_override)
- return 0;
-
- return sprintf(buf, "%s\n", dev->driver_override);
+ device_lock(_dev);
+ len = sprintf(buf, "%s\n", dev->driver_override);
+ device_unlock(_dev);
+ return len;
}
static ssize_t driver_override_store(struct device *_dev,
@@ -81,9 +82,10 @@ static ssize_t driver_override_store(struct device *_dev,
const char *buf, size_t count)
{
struct amba_device *dev = to_amba_device(_dev);
- char *driver_override, *old = dev->driver_override, *cp;
+ char *driver_override, *old, *cp;
- if (count > PATH_MAX)
+ /* We need to keep extra room for a newline */
+ if (count >= (PAGE_SIZE - 1))
return -EINVAL;
driver_override = kstrndup(buf, count, GFP_KERNEL);
@@ -94,12 +96,15 @@ static ssize_t driver_override_store(struct device *_dev,
if (cp)
*cp = '\0';
+ device_lock(_dev);
+ old = dev->driver_override;
if (strlen(driver_override)) {
dev->driver_override = driver_override;
} else {
kfree(driver_override);
dev->driver_override = NULL;
}
+ device_unlock(_dev);
kfree(old);
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index 764b63a5aade..e578eee31589 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -2839,6 +2839,14 @@ static void binder_transaction(struct binder_proc *proc,
else
return_error = BR_DEAD_REPLY;
mutex_unlock(&context->context_mgr_node_lock);
+ if (target_node && target_proc == proc) {
+ binder_user_error("%d:%d got transaction to context manager from process owning it\n",
+ proc->pid, thread->pid);
+ return_error = BR_FAILED_REPLY;
+ return_error_param = -EINVAL;
+ return_error_line = __LINE__;
+ goto err_invalid_target_handle;
+ }
}
if (!target_node) {
/*
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index a7120d621154..2b16e7c8fff3 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -211,10 +211,10 @@ config AHCI_SUNXI
If unsure, say N.
config AHCI_TEGRA
- tristate "NVIDIA Tegra124 AHCI SATA support"
+ tristate "NVIDIA Tegra AHCI SATA support"
depends on ARCH_TEGRA
help
- This option enables support for the NVIDIA Tegra124 SoC's
+ This option enables support for the NVIDIA Tegra SoC's
onboard AHCI SATA.
If unsure, say N.
@@ -549,15 +549,6 @@ config PATA_ATP867X
If unsure, say N.
-config PATA_BF54X
- tristate "Blackfin 54x ATAPI support"
- depends on BF542 || BF548 || BF549
- help
- This option enables support for the built-in ATAPI controller on
- Blackfin 54x family chips.
-
- If unsure, say N.
-
config PATA_BK3710
tristate "Palmchip BK3710 PATA support"
depends on ARCH_DAVINCI
@@ -963,6 +954,18 @@ config PATA_FALCON
If unsure, say N.
+config PATA_GAYLE
+ tristate "Amiga Gayle PATA support"
+ depends on M68K && AMIGA
+ help
+ This option enables support for the on-board IDE
+ interfaces on some Amiga models (A600, A1200,
+ A4000 and A4000T) and also for IDE interfaces on
+ the Zorro expansion bus (M-Tech E-Matrix 530
+ expansion card).
+
+ If unsure, say N.
+
config PATA_ISAPNP
tristate "ISA Plug and Play PATA support"
depends on ISAPNP
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index f1f5a3fbc777..d21cdd83f7ab 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -53,7 +53,6 @@ obj-$(CONFIG_PATA_AMD) += pata_amd.o
obj-$(CONFIG_PATA_ARTOP) += pata_artop.o
obj-$(CONFIG_PATA_ATIIXP) += pata_atiixp.o
obj-$(CONFIG_PATA_ATP867X) += pata_atp867x.o
-obj-$(CONFIG_PATA_BF54X) += pata_bf54x.o
obj-$(CONFIG_PATA_BK3710) += pata_bk3710.o
obj-$(CONFIG_PATA_CMD64X) += pata_cmd64x.o
obj-$(CONFIG_PATA_CS5520) += pata_cs5520.o
@@ -98,6 +97,7 @@ obj-$(CONFIG_PATA_WINBOND) += pata_sl82c105.o
# SFF PIO only
obj-$(CONFIG_PATA_CMD640_PCI) += pata_cmd640.o
obj-$(CONFIG_PATA_FALCON) += pata_falcon.o
+obj-$(CONFIG_PATA_GAYLE) += pata_gayle.o
obj-$(CONFIG_PATA_ISAPNP) += pata_isapnp.o
obj-$(CONFIG_PATA_IXP4XX_CF) += pata_ixp4xx_cf.o
obj-$(CONFIG_PATA_MPIIX) += pata_mpiix.o
diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
index a9d996e17d75..4356ef1d28a8 100644
--- a/drivers/ata/ahci.h
+++ b/drivers/ata/ahci.h
@@ -350,6 +350,7 @@ struct ahci_host_priv {
u32 em_msg_type; /* EM message type */
bool got_runtime_pm; /* Did we do pm_runtime_get? */
struct clk *clks[AHCI_MAX_CLKS]; /* Optional */
+ struct reset_control *rsts; /* Optional */
struct regulator **target_pwrs; /* Optional */
/*
* If platform uses PHYs. There is a 1:1 relation between the port number and
diff --git a/drivers/ata/ahci_imx.c b/drivers/ata/ahci_imx.c
index a58bcc069c54..6822e2f33f7e 100644
--- a/drivers/ata/ahci_imx.c
+++ b/drivers/ata/ahci_imx.c
@@ -23,6 +23,7 @@
#include <linux/regmap.h>
#include <linux/ahci_platform.h>
#include <linux/of_device.h>
+#include <linux/of_gpio.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
#include <linux/libata.h>
@@ -53,11 +54,49 @@ enum {
/* Clock Reset Register */
IMX_CLOCK_RESET = 0x7f3f,
IMX_CLOCK_RESET_RESET = 1 << 0,
+ /* IMX8QM HSIO AHCI definitions */
+ IMX8QM_SATA_PHY_RX_IMPED_RATIO_OFFSET = 0x03,
+ IMX8QM_SATA_PHY_TX_IMPED_RATIO_OFFSET = 0x09,
+ IMX8QM_SATA_PHY_IMPED_RATIO_85OHM = 0x6c,
+ IMX8QM_LPCG_PHYX2_OFFSET = 0x00000,
+ IMX8QM_CSR_PHYX2_OFFSET = 0x90000,
+ IMX8QM_CSR_PHYX1_OFFSET = 0xa0000,
+ IMX8QM_CSR_PHYX_STTS0_OFFSET = 0x4,
+ IMX8QM_CSR_PCIEA_OFFSET = 0xb0000,
+ IMX8QM_CSR_PCIEB_OFFSET = 0xc0000,
+ IMX8QM_CSR_SATA_OFFSET = 0xd0000,
+ IMX8QM_CSR_PCIE_CTRL2_OFFSET = 0x8,
+ IMX8QM_CSR_MISC_OFFSET = 0xe0000,
+
+ IMX8QM_LPCG_PHYX2_PCLK0_MASK = (0x3 << 16),
+ IMX8QM_LPCG_PHYX2_PCLK1_MASK = (0x3 << 20),
+ IMX8QM_PHY_APB_RSTN_0 = BIT(0),
+ IMX8QM_PHY_MODE_SATA = BIT(19),
+ IMX8QM_PHY_MODE_MASK = (0xf << 17),
+ IMX8QM_PHY_PIPE_RSTN_0 = BIT(24),
+ IMX8QM_PHY_PIPE_RSTN_OVERRIDE_0 = BIT(25),
+ IMX8QM_PHY_PIPE_RSTN_1 = BIT(26),
+ IMX8QM_PHY_PIPE_RSTN_OVERRIDE_1 = BIT(27),
+ IMX8QM_STTS0_LANE0_TX_PLL_LOCK = BIT(4),
+ IMX8QM_MISC_IOB_RXENA = BIT(0),
+ IMX8QM_MISC_IOB_TXENA = BIT(1),
+ IMX8QM_MISC_PHYX1_EPCS_SEL = BIT(12),
+ IMX8QM_MISC_CLKREQN_OUT_OVERRIDE_1 = BIT(24),
+ IMX8QM_MISC_CLKREQN_OUT_OVERRIDE_0 = BIT(25),
+ IMX8QM_MISC_CLKREQN_IN_OVERRIDE_1 = BIT(28),
+ IMX8QM_MISC_CLKREQN_IN_OVERRIDE_0 = BIT(29),
+ IMX8QM_SATA_CTRL_RESET_N = BIT(12),
+ IMX8QM_SATA_CTRL_EPCS_PHYRESET_N = BIT(7),
+ IMX8QM_CTRL_BUTTON_RST_N = BIT(21),
+ IMX8QM_CTRL_POWER_UP_RST_N = BIT(23),
+ IMX8QM_CTRL_LTSSM_ENABLE = BIT(4),
};
enum ahci_imx_type {
AHCI_IMX53,
AHCI_IMX6Q,
+ AHCI_IMX6QP,
+ AHCI_IMX8QM,
};
struct imx_ahci_priv {
@@ -66,10 +105,18 @@ struct imx_ahci_priv {
struct clk *sata_clk;
struct clk *sata_ref_clk;
struct clk *ahb_clk;
+ struct clk *epcs_tx_clk;
+ struct clk *epcs_rx_clk;
+ struct clk *phy_apbclk;
+ struct clk *phy_pclk0;
+ struct clk *phy_pclk1;
+ void __iomem *phy_base;
+ int clkreq_gpio;
struct regmap *gpr;
bool no_device;
bool first_time;
u32 phy_params;
+ u32 imped_ratio;
};
static int ahci_imx_hotplug;
@@ -188,11 +235,26 @@ static int imx_phy_reg_read(u16 *val, void __iomem *mmio)
static int imx_sata_phy_reset(struct ahci_host_priv *hpriv)
{
+ struct imx_ahci_priv *imxpriv = hpriv->plat_data;
void __iomem *mmio = hpriv->mmio;
int timeout = 10;
u16 val;
int ret;
+ if (imxpriv->type == AHCI_IMX6QP) {
+ /* 6qp adds the sata reset mechanism, use it for 6qp sata */
+ regmap_update_bits(imxpriv->gpr, IOMUXC_GPR5,
+ IMX6Q_GPR5_SATA_SW_PD, 0);
+
+ regmap_update_bits(imxpriv->gpr, IOMUXC_GPR5,
+ IMX6Q_GPR5_SATA_SW_RST, 0);
+ udelay(50);
+ regmap_update_bits(imxpriv->gpr, IOMUXC_GPR5,
+ IMX6Q_GPR5_SATA_SW_RST,
+ IMX6Q_GPR5_SATA_SW_RST);
+ return 0;
+ }
+
/* Reset SATA PHY by setting RESET bit of PHY register CLOCK_RESET */
ret = imx_phy_reg_addressing(IMX_CLOCK_RESET, mmio);
if (ret)
@@ -391,6 +453,207 @@ static struct attribute *fsl_sata_ahci_attrs[] = {
};
ATTRIBUTE_GROUPS(fsl_sata_ahci);
+static int imx8_sata_enable(struct ahci_host_priv *hpriv)
+{
+ u32 val, reg;
+ int i, ret;
+ struct imx_ahci_priv *imxpriv = hpriv->plat_data;
+ struct device *dev = &imxpriv->ahci_pdev->dev;
+
+ /* configure the hsio for sata */
+ ret = clk_prepare_enable(imxpriv->phy_pclk0);
+ if (ret < 0) {
+ dev_err(dev, "can't enable phy_pclk0.\n");
+ return ret;
+ }
+ ret = clk_prepare_enable(imxpriv->phy_pclk1);
+ if (ret < 0) {
+ dev_err(dev, "can't enable phy_pclk1.\n");
+ goto disable_phy_pclk0;
+ }
+ ret = clk_prepare_enable(imxpriv->epcs_tx_clk);
+ if (ret < 0) {
+ dev_err(dev, "can't enable epcs_tx_clk.\n");
+ goto disable_phy_pclk1;
+ }
+ ret = clk_prepare_enable(imxpriv->epcs_rx_clk);
+ if (ret < 0) {
+ dev_err(dev, "can't enable epcs_rx_clk.\n");
+ goto disable_epcs_tx_clk;
+ }
+ ret = clk_prepare_enable(imxpriv->phy_apbclk);
+ if (ret < 0) {
+ dev_err(dev, "can't enable phy_apbclk.\n");
+ goto disable_epcs_rx_clk;
+ }
+ /* Configure PHYx2 PIPE_RSTN */
+ regmap_read(imxpriv->gpr, IMX8QM_CSR_PCIEA_OFFSET +
+ IMX8QM_CSR_PCIE_CTRL2_OFFSET, &val);
+ if ((val & IMX8QM_CTRL_LTSSM_ENABLE) == 0) {
+ /* The link of the PCIEA of HSIO is down */
+ regmap_update_bits(imxpriv->gpr,
+ IMX8QM_CSR_PHYX2_OFFSET,
+ IMX8QM_PHY_PIPE_RSTN_0 |
+ IMX8QM_PHY_PIPE_RSTN_OVERRIDE_0,
+ IMX8QM_PHY_PIPE_RSTN_0 |
+ IMX8QM_PHY_PIPE_RSTN_OVERRIDE_0);
+ }
+ regmap_read(imxpriv->gpr, IMX8QM_CSR_PCIEB_OFFSET +
+ IMX8QM_CSR_PCIE_CTRL2_OFFSET, &reg);
+ if ((reg & IMX8QM_CTRL_LTSSM_ENABLE) == 0) {
+ /* The link of the PCIEB of HSIO is down */
+ regmap_update_bits(imxpriv->gpr,
+ IMX8QM_CSR_PHYX2_OFFSET,
+ IMX8QM_PHY_PIPE_RSTN_1 |
+ IMX8QM_PHY_PIPE_RSTN_OVERRIDE_1,
+ IMX8QM_PHY_PIPE_RSTN_1 |
+ IMX8QM_PHY_PIPE_RSTN_OVERRIDE_1);
+ }
+ if (((reg | val) & IMX8QM_CTRL_LTSSM_ENABLE) == 0) {
+ /* The links of both PCIA and PCIEB of HSIO are down */
+ regmap_update_bits(imxpriv->gpr,
+ IMX8QM_LPCG_PHYX2_OFFSET,
+ IMX8QM_LPCG_PHYX2_PCLK0_MASK |
+ IMX8QM_LPCG_PHYX2_PCLK1_MASK,
+ 0);
+ }
+
+ /* set PWR_RST and BT_RST of csr_pciea */
+ val = IMX8QM_CSR_PCIEA_OFFSET + IMX8QM_CSR_PCIE_CTRL2_OFFSET;
+ regmap_update_bits(imxpriv->gpr,
+ val,
+ IMX8QM_CTRL_BUTTON_RST_N,
+ IMX8QM_CTRL_BUTTON_RST_N);
+ regmap_update_bits(imxpriv->gpr,
+ val,
+ IMX8QM_CTRL_POWER_UP_RST_N,
+ IMX8QM_CTRL_POWER_UP_RST_N);
+
+ /* PHYX1_MODE to SATA */
+ regmap_update_bits(imxpriv->gpr,
+ IMX8QM_CSR_PHYX1_OFFSET,
+ IMX8QM_PHY_MODE_MASK,
+ IMX8QM_PHY_MODE_SATA);
+
+ /*
+ * BIT0 RXENA 1, BIT1 TXENA 0
+ * BIT12 PHY_X1_EPCS_SEL 1.
+ */
+ regmap_update_bits(imxpriv->gpr,
+ IMX8QM_CSR_MISC_OFFSET,
+ IMX8QM_MISC_IOB_RXENA,
+ IMX8QM_MISC_IOB_RXENA);
+ regmap_update_bits(imxpriv->gpr,
+ IMX8QM_CSR_MISC_OFFSET,
+ IMX8QM_MISC_IOB_TXENA,
+ 0);
+ regmap_update_bits(imxpriv->gpr,
+ IMX8QM_CSR_MISC_OFFSET,
+ IMX8QM_MISC_PHYX1_EPCS_SEL,
+ IMX8QM_MISC_PHYX1_EPCS_SEL);
+ /*
+ * It is possible, for PCIe and SATA are sharing
+ * the same clock source, HPLL or external oscillator.
+ * When PCIe is in low power modes (L1.X or L2 etc),
+ * the clock source can be turned off. In this case,
+ * if this clock source is required to be toggling by
+ * SATA, then SATA functions will be abnormal.
+ * Set the override here to avoid it.
+ */
+ regmap_update_bits(imxpriv->gpr,
+ IMX8QM_CSR_MISC_OFFSET,
+ IMX8QM_MISC_CLKREQN_OUT_OVERRIDE_1 |
+ IMX8QM_MISC_CLKREQN_OUT_OVERRIDE_0 |
+ IMX8QM_MISC_CLKREQN_IN_OVERRIDE_1 |
+ IMX8QM_MISC_CLKREQN_IN_OVERRIDE_0,
+ IMX8QM_MISC_CLKREQN_OUT_OVERRIDE_1 |
+ IMX8QM_MISC_CLKREQN_OUT_OVERRIDE_0 |
+ IMX8QM_MISC_CLKREQN_IN_OVERRIDE_1 |
+ IMX8QM_MISC_CLKREQN_IN_OVERRIDE_0);
+
+ /* clear PHY RST, then set it */
+ regmap_update_bits(imxpriv->gpr,
+ IMX8QM_CSR_SATA_OFFSET,
+ IMX8QM_SATA_CTRL_EPCS_PHYRESET_N,
+ 0);
+
+ regmap_update_bits(imxpriv->gpr,
+ IMX8QM_CSR_SATA_OFFSET,
+ IMX8QM_SATA_CTRL_EPCS_PHYRESET_N,
+ IMX8QM_SATA_CTRL_EPCS_PHYRESET_N);
+
+ /* CTRL RST: SET -> delay 1 us -> CLEAR -> SET */
+ regmap_update_bits(imxpriv->gpr,
+ IMX8QM_CSR_SATA_OFFSET,
+ IMX8QM_SATA_CTRL_RESET_N,
+ IMX8QM_SATA_CTRL_RESET_N);
+ udelay(1);
+ regmap_update_bits(imxpriv->gpr,
+ IMX8QM_CSR_SATA_OFFSET,
+ IMX8QM_SATA_CTRL_RESET_N,
+ 0);
+ regmap_update_bits(imxpriv->gpr,
+ IMX8QM_CSR_SATA_OFFSET,
+ IMX8QM_SATA_CTRL_RESET_N,
+ IMX8QM_SATA_CTRL_RESET_N);
+
+ /* APB reset */
+ regmap_update_bits(imxpriv->gpr,
+ IMX8QM_CSR_PHYX1_OFFSET,
+ IMX8QM_PHY_APB_RSTN_0,
+ IMX8QM_PHY_APB_RSTN_0);
+
+ for (i = 0; i < 100; i++) {
+ reg = IMX8QM_CSR_PHYX1_OFFSET +
+ IMX8QM_CSR_PHYX_STTS0_OFFSET;
+ regmap_read(imxpriv->gpr, reg, &val);
+ val &= IMX8QM_STTS0_LANE0_TX_PLL_LOCK;
+ if (val == IMX8QM_STTS0_LANE0_TX_PLL_LOCK)
+ break;
+ udelay(1);
+ }
+
+ if (val != IMX8QM_STTS0_LANE0_TX_PLL_LOCK) {
+ dev_err(dev, "TX PLL of the PHY is not locked\n");
+ ret = -ENODEV;
+ } else {
+ writeb(imxpriv->imped_ratio, imxpriv->phy_base +
+ IMX8QM_SATA_PHY_RX_IMPED_RATIO_OFFSET);
+ writeb(imxpriv->imped_ratio, imxpriv->phy_base +
+ IMX8QM_SATA_PHY_TX_IMPED_RATIO_OFFSET);
+ reg = readb(imxpriv->phy_base +
+ IMX8QM_SATA_PHY_RX_IMPED_RATIO_OFFSET);
+ if (unlikely(reg != imxpriv->imped_ratio))
+ dev_info(dev, "Can't set PHY RX impedance ratio.\n");
+ reg = readb(imxpriv->phy_base +
+ IMX8QM_SATA_PHY_TX_IMPED_RATIO_OFFSET);
+ if (unlikely(reg != imxpriv->imped_ratio))
+ dev_info(dev, "Can't set PHY TX impedance ratio.\n");
+ usleep_range(50, 100);
+
+ /*
+ * To reduce the power consumption, gate off
+ * the PHY clks
+ */
+ clk_disable_unprepare(imxpriv->phy_apbclk);
+ clk_disable_unprepare(imxpriv->phy_pclk1);
+ clk_disable_unprepare(imxpriv->phy_pclk0);
+ return ret;
+ }
+
+ clk_disable_unprepare(imxpriv->phy_apbclk);
+disable_epcs_rx_clk:
+ clk_disable_unprepare(imxpriv->epcs_rx_clk);
+disable_epcs_tx_clk:
+ clk_disable_unprepare(imxpriv->epcs_tx_clk);
+disable_phy_pclk1:
+ clk_disable_unprepare(imxpriv->phy_pclk1);
+disable_phy_pclk0:
+ clk_disable_unprepare(imxpriv->phy_pclk0);
+
+ return ret;
+}
+
static int imx_sata_enable(struct ahci_host_priv *hpriv)
{
struct imx_ahci_priv *imxpriv = hpriv->plat_data;
@@ -408,7 +671,7 @@ static int imx_sata_enable(struct ahci_host_priv *hpriv)
if (ret < 0)
goto disable_regulator;
- if (imxpriv->type == AHCI_IMX6Q) {
+ if (imxpriv->type == AHCI_IMX6Q || imxpriv->type == AHCI_IMX6QP) {
/*
* set PHY Paremeters, two steps to configure the GPR13,
* one write for rest of parameters, mask of first write
@@ -438,6 +701,8 @@ static int imx_sata_enable(struct ahci_host_priv *hpriv)
dev_err(dev, "failed to reset phy: %d\n", ret);
goto disable_clk;
}
+ } else if (imxpriv->type == AHCI_IMX8QM) {
+ ret = imx8_sata_enable(hpriv);
}
usleep_range(1000, 2000);
@@ -459,10 +724,29 @@ static void imx_sata_disable(struct ahci_host_priv *hpriv)
if (imxpriv->no_device)
return;
- if (imxpriv->type == AHCI_IMX6Q) {
+ switch (imxpriv->type) {
+ case AHCI_IMX6QP:
+ regmap_update_bits(imxpriv->gpr, IOMUXC_GPR5,
+ IMX6Q_GPR5_SATA_SW_PD,
+ IMX6Q_GPR5_SATA_SW_PD);
regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13,
IMX6Q_GPR13_SATA_MPLL_CLK_EN,
!IMX6Q_GPR13_SATA_MPLL_CLK_EN);
+ break;
+
+ case AHCI_IMX6Q:
+ regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13,
+ IMX6Q_GPR13_SATA_MPLL_CLK_EN,
+ !IMX6Q_GPR13_SATA_MPLL_CLK_EN);
+ break;
+
+ case AHCI_IMX8QM:
+ clk_disable_unprepare(imxpriv->epcs_rx_clk);
+ clk_disable_unprepare(imxpriv->epcs_tx_clk);
+ break;
+
+ default:
+ break;
}
clk_disable_unprepare(imxpriv->sata_ref_clk);
@@ -513,7 +797,7 @@ static int ahci_imx_softreset(struct ata_link *link, unsigned int *class,
if (imxpriv->type == AHCI_IMX53)
ret = ahci_pmp_retry_srst_ops.softreset(link, class, deadline);
- else if (imxpriv->type == AHCI_IMX6Q)
+ else
ret = ahci_ops.softreset(link, class, deadline);
return ret;
@@ -536,6 +820,8 @@ static const struct ata_port_info ahci_imx_port_info = {
static const struct of_device_id imx_ahci_of_match[] = {
{ .compatible = "fsl,imx53-ahci", .data = (void *)AHCI_IMX53 },
{ .compatible = "fsl,imx6q-ahci", .data = (void *)AHCI_IMX6Q },
+ { .compatible = "fsl,imx6qp-ahci", .data = (void *)AHCI_IMX6QP },
+ { .compatible = "fsl,imx8qm-ahci", .data = (void *)AHCI_IMX8QM },
{},
};
MODULE_DEVICE_TABLE(of, imx_ahci_of_match);
@@ -703,6 +989,79 @@ static struct scsi_host_template ahci_platform_sht = {
AHCI_SHT(DRV_NAME),
};
+static int imx8_sata_probe(struct device *dev, struct imx_ahci_priv *imxpriv)
+{
+ int ret;
+ struct resource *phy_res;
+ struct platform_device *pdev = imxpriv->ahci_pdev;
+ struct device_node *np = dev->of_node;
+
+ if (of_property_read_u32(np, "fsl,phy-imp", &imxpriv->imped_ratio))
+ imxpriv->imped_ratio = IMX8QM_SATA_PHY_IMPED_RATIO_85OHM;
+ phy_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy");
+ if (phy_res) {
+ imxpriv->phy_base = devm_ioremap(dev, phy_res->start,
+ resource_size(phy_res));
+ if (!imxpriv->phy_base) {
+ dev_err(dev, "error with ioremap\n");
+ return -ENOMEM;
+ }
+ } else {
+ dev_err(dev, "missing *phy* reg region.\n");
+ return -ENOMEM;
+ }
+ imxpriv->gpr =
+ syscon_regmap_lookup_by_phandle(np, "hsio");
+ if (IS_ERR(imxpriv->gpr)) {
+ dev_err(dev, "unable to find gpr registers\n");
+ return PTR_ERR(imxpriv->gpr);
+ }
+
+ imxpriv->epcs_tx_clk = devm_clk_get(dev, "epcs_tx");
+ if (IS_ERR(imxpriv->epcs_tx_clk)) {
+ dev_err(dev, "can't get epcs_tx_clk clock.\n");
+ return PTR_ERR(imxpriv->epcs_tx_clk);
+ }
+ imxpriv->epcs_rx_clk = devm_clk_get(dev, "epcs_rx");
+ if (IS_ERR(imxpriv->epcs_rx_clk)) {
+ dev_err(dev, "can't get epcs_rx_clk clock.\n");
+ return PTR_ERR(imxpriv->epcs_rx_clk);
+ }
+ imxpriv->phy_pclk0 = devm_clk_get(dev, "phy_pclk0");
+ if (IS_ERR(imxpriv->phy_pclk0)) {
+ dev_err(dev, "can't get phy_pclk0 clock.\n");
+ return PTR_ERR(imxpriv->phy_pclk0);
+ }
+ imxpriv->phy_pclk1 = devm_clk_get(dev, "phy_pclk1");
+ if (IS_ERR(imxpriv->phy_pclk1)) {
+ dev_err(dev, "can't get phy_pclk1 clock.\n");
+ return PTR_ERR(imxpriv->phy_pclk1);
+ }
+ imxpriv->phy_apbclk = devm_clk_get(dev, "phy_apbclk");
+ if (IS_ERR(imxpriv->phy_apbclk)) {
+ dev_err(dev, "can't get phy_apbclk clock.\n");
+ return PTR_ERR(imxpriv->phy_apbclk);
+ }
+
+ /* Fetch GPIO, then enable the external OSC */
+ imxpriv->clkreq_gpio = of_get_named_gpio(np, "clkreq-gpio", 0);
+ if (gpio_is_valid(imxpriv->clkreq_gpio)) {
+ ret = devm_gpio_request_one(dev, imxpriv->clkreq_gpio,
+ GPIOF_OUT_INIT_LOW,
+ "SATA CLKREQ");
+ if (ret == -EBUSY) {
+ dev_info(dev, "clkreq had been initialized.\n");
+ } else if (ret) {
+ dev_err(dev, "%d unable to get clkreq.\n", ret);
+ return ret;
+ }
+ } else if (imxpriv->clkreq_gpio == -EPROBE_DEFER) {
+ return imxpriv->clkreq_gpio;
+ }
+
+ return 0;
+}
+
static int imx_ahci_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -743,7 +1102,7 @@ static int imx_ahci_probe(struct platform_device *pdev)
return PTR_ERR(imxpriv->ahb_clk);
}
- if (imxpriv->type == AHCI_IMX6Q) {
+ if (imxpriv->type == AHCI_IMX6Q || imxpriv->type == AHCI_IMX6QP) {
u32 reg_value;
imxpriv->gpr = syscon_regmap_lookup_by_compatible(
@@ -762,6 +1121,10 @@ static int imx_ahci_probe(struct platform_device *pdev)
IMX6Q_GPR13_SATA_RX_DPLL_MODE_2P_4F |
IMX6Q_GPR13_SATA_SPD_MODE_3P0G |
reg_value;
+ } else if (imxpriv->type == AHCI_IMX8QM) {
+ ret = imx8_sata_probe(dev, imxpriv);
+ if (ret)
+ return ret;
}
hpriv = ahci_platform_get_resources(pdev);
diff --git a/drivers/ata/ahci_tegra.c b/drivers/ata/ahci_tegra.c
index 3a62eb246d80..64d848409fe2 100644
--- a/drivers/ata/ahci_tegra.c
+++ b/drivers/ata/ahci_tegra.c
@@ -34,7 +34,8 @@
#define DRV_NAME "tegra-ahci"
#define SATA_CONFIGURATION_0 0x180
-#define SATA_CONFIGURATION_EN_FPCI BIT(0)
+#define SATA_CONFIGURATION_0_EN_FPCI BIT(0)
+#define SATA_CONFIGURATION_0_CLK_OVERRIDE BIT(31)
#define SCFG_OFFSET 0x1000
@@ -45,17 +46,55 @@
#define T_SATA0_CFG_1_SERR BIT(8)
#define T_SATA0_CFG_9 0x24
-#define T_SATA0_CFG_9_BASE_ADDRESS_SHIFT 13
+#define T_SATA0_CFG_9_BASE_ADDRESS 0x40020000
#define SATA_FPCI_BAR5 0x94
-#define SATA_FPCI_BAR5_START_SHIFT 4
+#define SATA_FPCI_BAR5_START_MASK (0xfffffff << 4)
+#define SATA_FPCI_BAR5_START (0x0040020 << 4)
+#define SATA_FPCI_BAR5_ACCESS_TYPE (0x1)
#define SATA_INTR_MASK 0x188
#define SATA_INTR_MASK_IP_INT_MASK BIT(16)
+#define T_SATA0_CFG_35 0x94
+#define T_SATA0_CFG_35_IDP_INDEX_MASK (0x7ff << 2)
+#define T_SATA0_CFG_35_IDP_INDEX (0x2a << 2)
+
+#define T_SATA0_AHCI_IDP1 0x98
+#define T_SATA0_AHCI_IDP1_DATA (0x400040)
+
+#define T_SATA0_CFG_PHY_1 0x12c
+#define T_SATA0_CFG_PHY_1_PADS_IDDQ_EN BIT(23)
+#define T_SATA0_CFG_PHY_1_PAD_PLL_IDDQ_EN BIT(22)
+
+#define T_SATA0_NVOOB 0x114
+#define T_SATA0_NVOOB_COMMA_CNT_MASK (0xff << 16)
+#define T_SATA0_NVOOB_COMMA_CNT (0x07 << 16)
+#define T_SATA0_NVOOB_SQUELCH_FILTER_MODE_MASK (0x3 << 24)
+#define T_SATA0_NVOOB_SQUELCH_FILTER_MODE (0x1 << 24)
+#define T_SATA0_NVOOB_SQUELCH_FILTER_LENGTH_MASK (0x3 << 26)
+#define T_SATA0_NVOOB_SQUELCH_FILTER_LENGTH (0x3 << 26)
+
+#define T_SATA_CFG_PHY_0 0x120
+#define T_SATA_CFG_PHY_0_USE_7BIT_ALIGN_DET_FOR_SPD BIT(11)
+#define T_SATA_CFG_PHY_0_MASK_SQUELCH BIT(24)
+
+#define T_SATA0_CFG2NVOOB_2 0x134
+#define T_SATA0_CFG2NVOOB_2_COMWAKE_IDLE_CNT_LOW_MASK (0x1ff << 18)
+#define T_SATA0_CFG2NVOOB_2_COMWAKE_IDLE_CNT_LOW (0xc << 18)
+
#define T_SATA0_AHCI_HBA_CAP_BKDR 0x300
+#define T_SATA0_AHCI_HBA_CAP_BKDR_PARTIAL_ST_CAP BIT(13)
+#define T_SATA0_AHCI_HBA_CAP_BKDR_SLUMBER_ST_CAP BIT(14)
+#define T_SATA0_AHCI_HBA_CAP_BKDR_SALP BIT(26)
+#define T_SATA0_AHCI_HBA_CAP_BKDR_SUPP_PM BIT(17)
+#define T_SATA0_AHCI_HBA_CAP_BKDR_SNCQ BIT(30)
#define T_SATA0_BKDOOR_CC 0x4a4
+#define T_SATA0_BKDOOR_CC_CLASS_CODE_MASK (0xffff << 16)
+#define T_SATA0_BKDOOR_CC_CLASS_CODE (0x0106 << 16)
+#define T_SATA0_BKDOOR_CC_PROG_IF_MASK (0xff << 8)
+#define T_SATA0_BKDOOR_CC_PROG_IF (0x01 << 8)
#define T_SATA0_CFG_SATA 0x54c
#define T_SATA0_CFG_SATA_BACKDOOR_PROG_IF_EN BIT(12)
@@ -82,6 +121,27 @@
#define T_SATA0_CHX_PHY_CTRL11 0x6d0
#define T_SATA0_CHX_PHY_CTRL11_GEN2_RX_EQ (0x2800 << 16)
+#define T_SATA0_CHX_PHY_CTRL17_0 0x6e8
+#define T_SATA0_CHX_PHY_CTRL17_0_RX_EQ_CTRL_L_GEN1 0x55010000
+#define T_SATA0_CHX_PHY_CTRL18_0 0x6ec
+#define T_SATA0_CHX_PHY_CTRL18_0_RX_EQ_CTRL_L_GEN2 0x55010000
+#define T_SATA0_CHX_PHY_CTRL20_0 0x6f4
+#define T_SATA0_CHX_PHY_CTRL20_0_RX_EQ_CTRL_H_GEN1 0x1
+#define T_SATA0_CHX_PHY_CTRL21_0 0x6f8
+#define T_SATA0_CHX_PHY_CTRL21_0_RX_EQ_CTRL_H_GEN2 0x1
+
+/* AUX Registers */
+#define SATA_AUX_MISC_CNTL_1_0 0x8
+#define SATA_AUX_MISC_CNTL_1_0_DEVSLP_OVERRIDE BIT(17)
+#define SATA_AUX_MISC_CNTL_1_0_SDS_SUPPORT BIT(13)
+#define SATA_AUX_MISC_CNTL_1_0_DESO_SUPPORT BIT(15)
+
+#define SATA_AUX_RX_STAT_INT_0 0xc
+#define SATA_AUX_RX_STAT_INT_0_SATA_DEVSLP BIT(7)
+
+#define SATA_AUX_SPARE_CFG0_0 0x18
+#define SATA_AUX_SPARE_CFG0_0_MDAT_TIMER_AFTER_PG_VALID BIT(14)
+
#define FUSE_SATA_CALIB 0x124
#define FUSE_SATA_CALIB_MASK 0x3
@@ -99,23 +159,92 @@ static const struct sata_pad_calibration tegra124_pad_calibration[] = {
{0x14, 0x0e, 0x1a, 0x0e},
};
+struct tegra_ahci_ops {
+ int (*init)(struct ahci_host_priv *hpriv);
+};
+
+struct tegra_ahci_soc {
+ const char *const *supply_names;
+ u32 num_supplies;
+ bool supports_devslp;
+ const struct tegra_ahci_ops *ops;
+};
+
struct tegra_ahci_priv {
struct platform_device *pdev;
void __iomem *sata_regs;
+ void __iomem *sata_aux_regs;
struct reset_control *sata_rst;
struct reset_control *sata_oob_rst;
struct reset_control *sata_cold_rst;
/* Needs special handling, cannot use ahci_platform */
struct clk *sata_clk;
- struct regulator_bulk_data supplies[5];
+ struct regulator_bulk_data *supplies;
+ const struct tegra_ahci_soc *soc;
};
+static void tegra_ahci_handle_quirks(struct ahci_host_priv *hpriv)
+{
+ struct tegra_ahci_priv *tegra = hpriv->plat_data;
+ u32 val;
+
+ if (tegra->sata_aux_regs && !tegra->soc->supports_devslp) {
+ val = readl(tegra->sata_aux_regs + SATA_AUX_MISC_CNTL_1_0);
+ val &= ~SATA_AUX_MISC_CNTL_1_0_SDS_SUPPORT;
+ writel(val, tegra->sata_aux_regs + SATA_AUX_MISC_CNTL_1_0);
+ }
+}
+
+static int tegra124_ahci_init(struct ahci_host_priv *hpriv)
+{
+ struct tegra_ahci_priv *tegra = hpriv->plat_data;
+ struct sata_pad_calibration calib;
+ int ret;
+ u32 val;
+
+ /* Pad calibration */
+ ret = tegra_fuse_readl(FUSE_SATA_CALIB, &val);
+ if (ret)
+ return ret;
+
+ calib = tegra124_pad_calibration[val & FUSE_SATA_CALIB_MASK];
+
+ writel(BIT(0), tegra->sata_regs + SCFG_OFFSET + T_SATA0_INDEX);
+
+ val = readl(tegra->sata_regs +
+ SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL1_GEN1);
+ val &= ~T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_MASK;
+ val &= ~T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_MASK;
+ val |= calib.gen1_tx_amp << T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_SHIFT;
+ val |= calib.gen1_tx_peak << T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_SHIFT;
+ writel(val, tegra->sata_regs + SCFG_OFFSET +
+ T_SATA0_CHX_PHY_CTRL1_GEN1);
+
+ val = readl(tegra->sata_regs +
+ SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL1_GEN2);
+ val &= ~T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_MASK;
+ val &= ~T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_MASK;
+ val |= calib.gen2_tx_amp << T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_SHIFT;
+ val |= calib.gen2_tx_peak << T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_SHIFT;
+ writel(val, tegra->sata_regs + SCFG_OFFSET +
+ T_SATA0_CHX_PHY_CTRL1_GEN2);
+
+ writel(T_SATA0_CHX_PHY_CTRL11_GEN2_RX_EQ,
+ tegra->sata_regs + SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL11);
+ writel(T_SATA0_CHX_PHY_CTRL2_CDR_CNTL_GEN1,
+ tegra->sata_regs + SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL2);
+
+ writel(0, tegra->sata_regs + SCFG_OFFSET + T_SATA0_INDEX);
+
+ return 0;
+}
+
static int tegra_ahci_power_on(struct ahci_host_priv *hpriv)
{
struct tegra_ahci_priv *tegra = hpriv->plat_data;
int ret;
- ret = regulator_bulk_enable(ARRAY_SIZE(tegra->supplies),
+ ret = regulator_bulk_enable(tegra->soc->num_supplies,
tegra->supplies);
if (ret)
return ret;
@@ -144,7 +273,7 @@ disable_power:
tegra_powergate_power_off(TEGRA_POWERGATE_SATA);
disable_regulators:
- regulator_bulk_disable(ARRAY_SIZE(tegra->supplies), tegra->supplies);
+ regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
return ret;
}
@@ -162,15 +291,14 @@ static void tegra_ahci_power_off(struct ahci_host_priv *hpriv)
clk_disable_unprepare(tegra->sata_clk);
tegra_powergate_power_off(TEGRA_POWERGATE_SATA);
- regulator_bulk_disable(ARRAY_SIZE(tegra->supplies), tegra->supplies);
+ regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
}
static int tegra_ahci_controller_init(struct ahci_host_priv *hpriv)
{
struct tegra_ahci_priv *tegra = hpriv->plat_data;
int ret;
- unsigned int val;
- struct sata_pad_calibration calib;
+ u32 val;
ret = tegra_ahci_power_on(hpriv);
if (ret) {
@@ -179,78 +307,115 @@ static int tegra_ahci_controller_init(struct ahci_host_priv *hpriv)
return ret;
}
+ /*
+ * Program the following SATA IPFS registers to allow SW accesses to
+ * SATA's MMIO register range.
+ */
+ val = readl(tegra->sata_regs + SATA_FPCI_BAR5);
+ val &= ~(SATA_FPCI_BAR5_START_MASK | SATA_FPCI_BAR5_ACCESS_TYPE);
+ val |= SATA_FPCI_BAR5_START | SATA_FPCI_BAR5_ACCESS_TYPE;
+ writel(val, tegra->sata_regs + SATA_FPCI_BAR5);
+
+ /* Program the following SATA IPFS register to enable the SATA */
val = readl(tegra->sata_regs + SATA_CONFIGURATION_0);
- val |= SATA_CONFIGURATION_EN_FPCI;
+ val |= SATA_CONFIGURATION_0_EN_FPCI;
writel(val, tegra->sata_regs + SATA_CONFIGURATION_0);
- /* Pad calibration */
-
- ret = tegra_fuse_readl(FUSE_SATA_CALIB, &val);
- if (ret) {
- dev_err(&tegra->pdev->dev,
- "failed to read calibration fuse: %d\n", ret);
- return ret;
- }
-
- calib = tegra124_pad_calibration[val & FUSE_SATA_CALIB_MASK];
-
- writel(BIT(0), tegra->sata_regs + SCFG_OFFSET + T_SATA0_INDEX);
-
- val = readl(tegra->sata_regs +
- SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL1_GEN1);
- val &= ~T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_MASK;
- val &= ~T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_MASK;
- val |= calib.gen1_tx_amp <<
- T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_SHIFT;
- val |= calib.gen1_tx_peak <<
- T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_SHIFT;
- writel(val, tegra->sata_regs + SCFG_OFFSET +
- T_SATA0_CHX_PHY_CTRL1_GEN1);
-
- val = readl(tegra->sata_regs +
- SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL1_GEN2);
- val &= ~T_SATA0_CHX_PHY_CTRL1_GEN2_TX_AMP_MASK;
- val &= ~T_SATA0_CHX_PHY_CTRL1_GEN2_TX_PEAK_MASK;
- val |= calib.gen2_tx_amp <<
- T_SATA0_CHX_PHY_CTRL1_GEN1_TX_AMP_SHIFT;
- val |= calib.gen2_tx_peak <<
- T_SATA0_CHX_PHY_CTRL1_GEN1_TX_PEAK_SHIFT;
- writel(val, tegra->sata_regs + SCFG_OFFSET +
- T_SATA0_CHX_PHY_CTRL1_GEN2);
-
- writel(T_SATA0_CHX_PHY_CTRL11_GEN2_RX_EQ,
- tegra->sata_regs + SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL11);
- writel(T_SATA0_CHX_PHY_CTRL2_CDR_CNTL_GEN1,
- tegra->sata_regs + SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL2);
-
- writel(0, tegra->sata_regs + SCFG_OFFSET + T_SATA0_INDEX);
-
- /* Program controller device ID */
+ /* Electrical settings for better link stability */
+ val = T_SATA0_CHX_PHY_CTRL17_0_RX_EQ_CTRL_L_GEN1;
+ writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL17_0);
+ val = T_SATA0_CHX_PHY_CTRL18_0_RX_EQ_CTRL_L_GEN2;
+ writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL18_0);
+ val = T_SATA0_CHX_PHY_CTRL20_0_RX_EQ_CTRL_H_GEN1;
+ writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL20_0);
+ val = T_SATA0_CHX_PHY_CTRL21_0_RX_EQ_CTRL_H_GEN2;
+ writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CHX_PHY_CTRL21_0);
+
+ /* For SQUELCH Filter & Gen3 drive getting detected as Gen1 drive */
+
+ val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA_CFG_PHY_0);
+ val |= T_SATA_CFG_PHY_0_MASK_SQUELCH;
+ val &= ~T_SATA_CFG_PHY_0_USE_7BIT_ALIGN_DET_FOR_SPD;
+ writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA_CFG_PHY_0);
+
+ val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA0_NVOOB);
+ val &= ~(T_SATA0_NVOOB_COMMA_CNT_MASK |
+ T_SATA0_NVOOB_SQUELCH_FILTER_LENGTH_MASK |
+ T_SATA0_NVOOB_SQUELCH_FILTER_MODE_MASK);
+ val |= (T_SATA0_NVOOB_COMMA_CNT |
+ T_SATA0_NVOOB_SQUELCH_FILTER_LENGTH |
+ T_SATA0_NVOOB_SQUELCH_FILTER_MODE);
+ writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_NVOOB);
+
+ /*
+ * Change CFG2NVOOB_2_COMWAKE_IDLE_CNT_LOW from 83.3 ns to 58.8ns
+ */
+ val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG2NVOOB_2);
+ val &= ~T_SATA0_CFG2NVOOB_2_COMWAKE_IDLE_CNT_LOW_MASK;
+ val |= T_SATA0_CFG2NVOOB_2_COMWAKE_IDLE_CNT_LOW;
+ writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG2NVOOB_2);
+
+ if (tegra->soc->ops && tegra->soc->ops->init)
+ tegra->soc->ops->init(hpriv);
+
+ /*
+ * Program the following SATA configuration registers to
+ * initialize SATA
+ */
+ val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_1);
+ val |= (T_SATA0_CFG_1_IO_SPACE | T_SATA0_CFG_1_MEMORY_SPACE |
+ T_SATA0_CFG_1_BUS_MASTER | T_SATA0_CFG_1_SERR);
+ writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_1);
+ val = T_SATA0_CFG_9_BASE_ADDRESS;
+ writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_9);
+ /* Program Class Code and Programming interface for SATA */
val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_SATA);
val |= T_SATA0_CFG_SATA_BACKDOOR_PROG_IF_EN;
writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_SATA);
- writel(0x01060100, tegra->sata_regs + SCFG_OFFSET + T_SATA0_BKDOOR_CC);
+ val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA0_BKDOOR_CC);
+ val &=
+ ~(T_SATA0_BKDOOR_CC_CLASS_CODE_MASK |
+ T_SATA0_BKDOOR_CC_PROG_IF_MASK);
+ val |= T_SATA0_BKDOOR_CC_CLASS_CODE | T_SATA0_BKDOOR_CC_PROG_IF;
+ writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_BKDOOR_CC);
val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_SATA);
val &= ~T_SATA0_CFG_SATA_BACKDOOR_PROG_IF_EN;
writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_SATA);
- /* Enable IO & memory access, bus master mode */
-
- val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_1);
- val |= T_SATA0_CFG_1_IO_SPACE | T_SATA0_CFG_1_MEMORY_SPACE |
- T_SATA0_CFG_1_BUS_MASTER | T_SATA0_CFG_1_SERR;
- writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_1);
-
- /* Program SATA MMIO */
-
- writel(0x10000 << SATA_FPCI_BAR5_START_SHIFT,
- tegra->sata_regs + SATA_FPCI_BAR5);
+ /* Enabling LPM capabilities through Backdoor Programming */
+ val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA0_AHCI_HBA_CAP_BKDR);
+ val |= (T_SATA0_AHCI_HBA_CAP_BKDR_PARTIAL_ST_CAP |
+ T_SATA0_AHCI_HBA_CAP_BKDR_SLUMBER_ST_CAP |
+ T_SATA0_AHCI_HBA_CAP_BKDR_SALP |
+ T_SATA0_AHCI_HBA_CAP_BKDR_SUPP_PM);
+ writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_AHCI_HBA_CAP_BKDR);
+
+ /* SATA Second Level Clock Gating configuration
+ * Enabling Gating of Tx/Rx clocks and driving Pad IDDQ and Lane
+ * IDDQ Signals
+ */
+ val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_35);
+ val &= ~T_SATA0_CFG_35_IDP_INDEX_MASK;
+ val |= T_SATA0_CFG_35_IDP_INDEX;
+ writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_35);
+
+ val = T_SATA0_AHCI_IDP1_DATA;
+ writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_AHCI_IDP1);
+
+ val = readl(tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_PHY_1);
+ val |= (T_SATA0_CFG_PHY_1_PADS_IDDQ_EN |
+ T_SATA0_CFG_PHY_1_PAD_PLL_IDDQ_EN);
+ writel(val, tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_PHY_1);
+
+ /* Enabling IPFS Clock Gating */
+ val = readl(tegra->sata_regs + SATA_CONFIGURATION_0);
+ val &= ~SATA_CONFIGURATION_0_CLK_OVERRIDE;
+ writel(val, tegra->sata_regs + SATA_CONFIGURATION_0);
- writel(0x08000 << T_SATA0_CFG_9_BASE_ADDRESS_SHIFT,
- tegra->sata_regs + SCFG_OFFSET + T_SATA0_CFG_9);
+ tegra_ahci_handle_quirks(hpriv);
/* Unmask SATA interrupts */
@@ -279,14 +444,40 @@ static struct ata_port_operations ahci_tegra_port_ops = {
};
static const struct ata_port_info ahci_tegra_port_info = {
- .flags = AHCI_FLAG_COMMON,
+ .flags = AHCI_FLAG_COMMON | ATA_FLAG_NO_DIPM,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_tegra_port_ops,
};
+static const char *const tegra124_supply_names[] = {
+ "avdd", "hvdd", "vddio", "target-5v", "target-12v"
+};
+
+static const struct tegra_ahci_ops tegra124_ahci_ops = {
+ .init = tegra124_ahci_init,
+};
+
+static const struct tegra_ahci_soc tegra124_ahci_soc = {
+ .supply_names = tegra124_supply_names,
+ .num_supplies = ARRAY_SIZE(tegra124_supply_names),
+ .supports_devslp = false,
+ .ops = &tegra124_ahci_ops,
+};
+
+static const struct tegra_ahci_soc tegra210_ahci_soc = {
+ .supports_devslp = false,
+};
+
static const struct of_device_id tegra_ahci_of_match[] = {
- { .compatible = "nvidia,tegra124-ahci" },
+ {
+ .compatible = "nvidia,tegra124-ahci",
+ .data = &tegra124_ahci_soc
+ },
+ {
+ .compatible = "nvidia,tegra210-ahci",
+ .data = &tegra210_ahci_soc
+ },
{}
};
MODULE_DEVICE_TABLE(of, tegra_ahci_of_match);
@@ -301,6 +492,7 @@ static int tegra_ahci_probe(struct platform_device *pdev)
struct tegra_ahci_priv *tegra;
struct resource *res;
int ret;
+ unsigned int i;
hpriv = ahci_platform_get_resources(pdev);
if (IS_ERR(hpriv))
@@ -313,12 +505,23 @@ static int tegra_ahci_probe(struct platform_device *pdev)
hpriv->plat_data = tegra;
tegra->pdev = pdev;
+ tegra->soc = of_device_get_match_data(&pdev->dev);
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
tegra->sata_regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(tegra->sata_regs))
return PTR_ERR(tegra->sata_regs);
+ /*
+ * AUX registers is optional.
+ */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ if (res) {
+ tegra->sata_aux_regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(tegra->sata_aux_regs))
+ return PTR_ERR(tegra->sata_aux_regs);
+ }
+
tegra->sata_rst = devm_reset_control_get(&pdev->dev, "sata");
if (IS_ERR(tegra->sata_rst)) {
dev_err(&pdev->dev, "Failed to get sata reset\n");
@@ -343,13 +546,17 @@ static int tegra_ahci_probe(struct platform_device *pdev)
return PTR_ERR(tegra->sata_clk);
}
- tegra->supplies[0].supply = "avdd";
- tegra->supplies[1].supply = "hvdd";
- tegra->supplies[2].supply = "vddio";
- tegra->supplies[3].supply = "target-5v";
- tegra->supplies[4].supply = "target-12v";
+ tegra->supplies = devm_kcalloc(&pdev->dev,
+ tegra->soc->num_supplies,
+ sizeof(*tegra->supplies), GFP_KERNEL);
+ if (!tegra->supplies)
+ return -ENOMEM;
+
+ for (i = 0; i < tegra->soc->num_supplies; i++)
+ tegra->supplies[i].supply = tegra->soc->supply_names[i];
- ret = devm_regulator_bulk_get(&pdev->dev, ARRAY_SIZE(tegra->supplies),
+ ret = devm_regulator_bulk_get(&pdev->dev,
+ tegra->soc->num_supplies,
tegra->supplies);
if (ret) {
dev_err(&pdev->dev, "Failed to get regulators\n");
@@ -385,5 +592,5 @@ static struct platform_driver tegra_ahci_driver = {
module_platform_driver(tegra_ahci_driver);
MODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>");
-MODULE_DESCRIPTION("Tegra124 AHCI SATA driver");
+MODULE_DESCRIPTION("Tegra AHCI SATA driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c
index 30cc8f1a31e1..46a762442dc5 100644
--- a/drivers/ata/libahci_platform.c
+++ b/drivers/ata/libahci_platform.c
@@ -25,6 +25,7 @@
#include <linux/phy/phy.h>
#include <linux/pm_runtime.h>
#include <linux/of_platform.h>
+#include <linux/reset.h>
#include "ahci.h"
static void ahci_host_stop(struct ata_host *host);
@@ -195,7 +196,8 @@ EXPORT_SYMBOL_GPL(ahci_platform_disable_regulators);
* following order:
* 1) Regulator
* 2) Clocks (through ahci_platform_enable_clks)
- * 3) Phys
+ * 3) Resets
+ * 4) Phys
*
* If resource enabling fails at any point the previous enabled resources
* are disabled in reverse order.
@@ -215,12 +217,19 @@ int ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
if (rc)
goto disable_regulator;
- rc = ahci_platform_enable_phys(hpriv);
+ rc = reset_control_deassert(hpriv->rsts);
if (rc)
goto disable_clks;
+ rc = ahci_platform_enable_phys(hpriv);
+ if (rc)
+ goto disable_resets;
+
return 0;
+disable_resets:
+ reset_control_assert(hpriv->rsts);
+
disable_clks:
ahci_platform_disable_clks(hpriv);
@@ -239,12 +248,15 @@ EXPORT_SYMBOL_GPL(ahci_platform_enable_resources);
* following order:
* 1) Phys
* 2) Clocks (through ahci_platform_disable_clks)
- * 3) Regulator
+ * 3) Resets
+ * 4) Regulator
*/
void ahci_platform_disable_resources(struct ahci_host_priv *hpriv)
{
ahci_platform_disable_phys(hpriv);
+ reset_control_assert(hpriv->rsts);
+
ahci_platform_disable_clks(hpriv);
ahci_platform_disable_regulators(hpriv);
@@ -393,6 +405,12 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev)
hpriv->clks[i] = clk;
}
+ hpriv->rsts = devm_reset_control_array_get_optional_shared(dev);
+ if (IS_ERR(hpriv->rsts)) {
+ rc = PTR_ERR(hpriv->rsts);
+ goto err_out;
+ }
+
hpriv->nports = child_nodes = of_get_child_count(dev->of_node);
/*
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 7431ccd03316..8bc71ca61e7f 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -5240,7 +5240,7 @@ void ata_qc_complete(struct ata_queued_cmd *qc)
struct ata_port *ap = qc->ap;
/* Trigger the LED (if available) */
- ledtrig_disk_activity();
+ ledtrig_disk_activity(!!(qc->tf.flags & ATA_TFLAG_WRITE));
/* XXX: New EH and old EH use different mechanisms to
* synchronize EH with regular execution path.
@@ -6026,7 +6026,7 @@ struct ata_port *ata_port_alloc(struct ata_host *host)
return ap;
}
-static void ata_host_release(struct device *gendev, void *res)
+static void ata_devres_release(struct device *gendev, void *res)
{
struct ata_host *host = dev_get_drvdata(gendev);
int i;
@@ -6040,13 +6040,36 @@ static void ata_host_release(struct device *gendev, void *res)
if (ap->scsi_host)
scsi_host_put(ap->scsi_host);
+ }
+
+ dev_set_drvdata(gendev, NULL);
+ ata_host_put(host);
+}
+
+static void ata_host_release(struct kref *kref)
+{
+ struct ata_host *host = container_of(kref, struct ata_host, kref);
+ int i;
+
+ for (i = 0; i < host->n_ports; i++) {
+ struct ata_port *ap = host->ports[i];
+
kfree(ap->pmp_link);
kfree(ap->slave_link);
kfree(ap);
host->ports[i] = NULL;
}
+ kfree(host);
+}
- dev_set_drvdata(gendev, NULL);
+void ata_host_get(struct ata_host *host)
+{
+ kref_get(&host->kref);
+}
+
+void ata_host_put(struct ata_host *host)
+{
+ kref_put(&host->kref, ata_host_release);
}
/**
@@ -6074,26 +6097,31 @@ struct ata_host *ata_host_alloc(struct device *dev, int max_ports)
struct ata_host *host;
size_t sz;
int i;
+ void *dr;
DPRINTK("ENTER\n");
- if (!devres_open_group(dev, NULL, GFP_KERNEL))
- return NULL;
-
/* alloc a container for our list of ATA ports (buses) */
sz = sizeof(struct ata_host) + (max_ports + 1) * sizeof(void *);
- /* alloc a container for our list of ATA ports (buses) */
- host = devres_alloc(ata_host_release, sz, GFP_KERNEL);
+ host = kzalloc(sz, GFP_KERNEL);
if (!host)
+ return NULL;
+
+ if (!devres_open_group(dev, NULL, GFP_KERNEL))
+ goto err_free;
+
+ dr = devres_alloc(ata_devres_release, 0, GFP_KERNEL);
+ if (!dr)
goto err_out;
- devres_add(dev, host);
+ devres_add(dev, dr);
dev_set_drvdata(dev, host);
spin_lock_init(&host->lock);
mutex_init(&host->eh_mutex);
host->dev = dev;
host->n_ports = max_ports;
+ kref_init(&host->kref);
/* allocate ports bound to this host */
for (i = 0; i < max_ports; i++) {
@@ -6112,6 +6140,8 @@ struct ata_host *ata_host_alloc(struct device *dev, int max_ports)
err_out:
devres_release_group(dev, NULL);
+ err_free:
+ kfree(host);
return NULL;
}
diff --git a/drivers/ata/libata-transport.c b/drivers/ata/libata-transport.c
index 19e6e539a061..a0b0b4d986f2 100644
--- a/drivers/ata/libata-transport.c
+++ b/drivers/ata/libata-transport.c
@@ -224,6 +224,8 @@ static DECLARE_TRANSPORT_CLASS(ata_port_class,
static void ata_tport_release(struct device *dev)
{
+ struct ata_port *ap = tdev_to_port(dev);
+ ata_host_put(ap->host);
}
/**
@@ -284,6 +286,7 @@ int ata_tport_add(struct device *parent,
dev->type = &ata_port_type;
dev->parent = parent;
+ ata_host_get(ap->host);
dev->release = ata_tport_release;
dev_set_name(dev, "ata%d", ap->print_id);
transport_setup_device(dev);
@@ -314,6 +317,7 @@ int ata_tport_add(struct device *parent,
tport_err:
transport_destroy_device(dev);
put_device(dev);
+ ata_host_put(ap->host);
return error;
}
diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h
index f953cb4bb1ba..9e21c49cf6be 100644
--- a/drivers/ata/libata.h
+++ b/drivers/ata/libata.h
@@ -100,6 +100,8 @@ extern int ata_port_probe(struct ata_port *ap);
extern void __ata_port_probe(struct ata_port *ap);
extern unsigned int ata_read_log_page(struct ata_device *dev, u8 log,
u8 page, void *buf, unsigned int sectors);
+extern void ata_host_get(struct ata_host *host);
+extern void ata_host_put(struct ata_host *host);
#define to_ata_port(d) container_of(d, struct ata_port, tdev)
diff --git a/drivers/ata/pata_arasan_cf.c b/drivers/ata/pata_arasan_cf.c
index b4d54771c9fe..ebecab8c3f36 100644
--- a/drivers/ata/pata_arasan_cf.c
+++ b/drivers/ata/pata_arasan_cf.c
@@ -796,7 +796,7 @@ static int arasan_cf_probe(struct platform_device *pdev)
struct resource *res;
u32 quirk;
irq_handler_t irq_handler = NULL;
- int ret = 0;
+ int ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
@@ -809,10 +809,8 @@ static int arasan_cf_probe(struct platform_device *pdev)
}
acdev = devm_kzalloc(&pdev->dev, sizeof(*acdev), GFP_KERNEL);
- if (!acdev) {
- dev_warn(&pdev->dev, "kzalloc fail\n");
+ if (!acdev)
return -ENOMEM;
- }
if (pdata)
quirk = pdata->quirk;
diff --git a/drivers/ata/pata_bf54x.c b/drivers/ata/pata_bf54x.c
deleted file mode 100644
index 0e55a8da2748..000000000000
--- a/drivers/ata/pata_bf54x.c
+++ /dev/null
@@ -1,1703 +0,0 @@
-/*
- * File: drivers/ata/pata_bf54x.c
- * Author: Sonic Zhang <sonic.zhang@analog.com>
- *
- * Created:
- * Description: PATA Driver for blackfin 54x
- *
- * Modified:
- * Copyright 2007 Analog Devices Inc.
- *
- * Bugs: Enter bugs at http://blackfin.uclinux.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, see the file COPYING, or write
- * to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/blkdev.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <scsi/scsi_host.h>
-#include <linux/libata.h>
-#include <linux/platform_device.h>
-#include <linux/gpio.h>
-#include <asm/dma.h>
-#include <asm/portmux.h>
-
-#define DRV_NAME "pata-bf54x"
-#define DRV_VERSION "0.9"
-
-#define ATA_REG_CTRL 0x0E
-#define ATA_REG_ALTSTATUS ATA_REG_CTRL
-
-/* These are the offset of the controller's registers */
-#define ATAPI_OFFSET_CONTROL 0x00
-#define ATAPI_OFFSET_STATUS 0x04
-#define ATAPI_OFFSET_DEV_ADDR 0x08
-#define ATAPI_OFFSET_DEV_TXBUF 0x0c
-#define ATAPI_OFFSET_DEV_RXBUF 0x10
-#define ATAPI_OFFSET_INT_MASK 0x14
-#define ATAPI_OFFSET_INT_STATUS 0x18
-#define ATAPI_OFFSET_XFER_LEN 0x1c
-#define ATAPI_OFFSET_LINE_STATUS 0x20
-#define ATAPI_OFFSET_SM_STATE 0x24
-#define ATAPI_OFFSET_TERMINATE 0x28
-#define ATAPI_OFFSET_PIO_TFRCNT 0x2c
-#define ATAPI_OFFSET_DMA_TFRCNT 0x30
-#define ATAPI_OFFSET_UMAIN_TFRCNT 0x34
-#define ATAPI_OFFSET_UDMAOUT_TFRCNT 0x38
-#define ATAPI_OFFSET_REG_TIM_0 0x40
-#define ATAPI_OFFSET_PIO_TIM_0 0x44
-#define ATAPI_OFFSET_PIO_TIM_1 0x48
-#define ATAPI_OFFSET_MULTI_TIM_0 0x50
-#define ATAPI_OFFSET_MULTI_TIM_1 0x54
-#define ATAPI_OFFSET_MULTI_TIM_2 0x58
-#define ATAPI_OFFSET_ULTRA_TIM_0 0x60
-#define ATAPI_OFFSET_ULTRA_TIM_1 0x64
-#define ATAPI_OFFSET_ULTRA_TIM_2 0x68
-#define ATAPI_OFFSET_ULTRA_TIM_3 0x6c
-
-
-#define ATAPI_GET_CONTROL(base)\
- bfin_read16(base + ATAPI_OFFSET_CONTROL)
-#define ATAPI_SET_CONTROL(base, val)\
- bfin_write16(base + ATAPI_OFFSET_CONTROL, val)
-#define ATAPI_GET_STATUS(base)\
- bfin_read16(base + ATAPI_OFFSET_STATUS)
-#define ATAPI_GET_DEV_ADDR(base)\
- bfin_read16(base + ATAPI_OFFSET_DEV_ADDR)
-#define ATAPI_SET_DEV_ADDR(base, val)\
- bfin_write16(base + ATAPI_OFFSET_DEV_ADDR, val)
-#define ATAPI_GET_DEV_TXBUF(base)\
- bfin_read16(base + ATAPI_OFFSET_DEV_TXBUF)
-#define ATAPI_SET_DEV_TXBUF(base, val)\
- bfin_write16(base + ATAPI_OFFSET_DEV_TXBUF, val)
-#define ATAPI_GET_DEV_RXBUF(base)\
- bfin_read16(base + ATAPI_OFFSET_DEV_RXBUF)
-#define ATAPI_SET_DEV_RXBUF(base, val)\
- bfin_write16(base + ATAPI_OFFSET_DEV_RXBUF, val)
-#define ATAPI_GET_INT_MASK(base)\
- bfin_read16(base + ATAPI_OFFSET_INT_MASK)
-#define ATAPI_SET_INT_MASK(base, val)\
- bfin_write16(base + ATAPI_OFFSET_INT_MASK, val)
-#define ATAPI_GET_INT_STATUS(base)\
- bfin_read16(base + ATAPI_OFFSET_INT_STATUS)
-#define ATAPI_SET_INT_STATUS(base, val)\
- bfin_write16(base + ATAPI_OFFSET_INT_STATUS, val)
-#define ATAPI_GET_XFER_LEN(base)\
- bfin_read16(base + ATAPI_OFFSET_XFER_LEN)
-#define ATAPI_SET_XFER_LEN(base, val)\
- bfin_write16(base + ATAPI_OFFSET_XFER_LEN, val)
-#define ATAPI_GET_LINE_STATUS(base)\
- bfin_read16(base + ATAPI_OFFSET_LINE_STATUS)
-#define ATAPI_GET_SM_STATE(base)\
- bfin_read16(base + ATAPI_OFFSET_SM_STATE)
-#define ATAPI_GET_TERMINATE(base)\
- bfin_read16(base + ATAPI_OFFSET_TERMINATE)
-#define ATAPI_SET_TERMINATE(base, val)\
- bfin_write16(base + ATAPI_OFFSET_TERMINATE, val)
-#define ATAPI_GET_PIO_TFRCNT(base)\
- bfin_read16(base + ATAPI_OFFSET_PIO_TFRCNT)
-#define ATAPI_GET_DMA_TFRCNT(base)\
- bfin_read16(base + ATAPI_OFFSET_DMA_TFRCNT)
-#define ATAPI_GET_UMAIN_TFRCNT(base)\
- bfin_read16(base + ATAPI_OFFSET_UMAIN_TFRCNT)
-#define ATAPI_GET_UDMAOUT_TFRCNT(base)\
- bfin_read16(base + ATAPI_OFFSET_UDMAOUT_TFRCNT)
-#define ATAPI_GET_REG_TIM_0(base)\
- bfin_read16(base + ATAPI_OFFSET_REG_TIM_0)
-#define ATAPI_SET_REG_TIM_0(base, val)\
- bfin_write16(base + ATAPI_OFFSET_REG_TIM_0, val)
-#define ATAPI_GET_PIO_TIM_0(base)\
- bfin_read16(base + ATAPI_OFFSET_PIO_TIM_0)
-#define ATAPI_SET_PIO_TIM_0(base, val)\
- bfin_write16(base + ATAPI_OFFSET_PIO_TIM_0, val)
-#define ATAPI_GET_PIO_TIM_1(base)\
- bfin_read16(base + ATAPI_OFFSET_PIO_TIM_1)
-#define ATAPI_SET_PIO_TIM_1(base, val)\
- bfin_write16(base + ATAPI_OFFSET_PIO_TIM_1, val)
-#define ATAPI_GET_MULTI_TIM_0(base)\
- bfin_read16(base + ATAPI_OFFSET_MULTI_TIM_0)
-#define ATAPI_SET_MULTI_TIM_0(base, val)\
- bfin_write16(base + ATAPI_OFFSET_MULTI_TIM_0, val)
-#define ATAPI_GET_MULTI_TIM_1(base)\
- bfin_read16(base + ATAPI_OFFSET_MULTI_TIM_1)
-#define ATAPI_SET_MULTI_TIM_1(base, val)\
- bfin_write16(base + ATAPI_OFFSET_MULTI_TIM_1, val)
-#define ATAPI_GET_MULTI_TIM_2(base)\
- bfin_read16(base + ATAPI_OFFSET_MULTI_TIM_2)
-#define ATAPI_SET_MULTI_TIM_2(base, val)\
- bfin_write16(base + ATAPI_OFFSET_MULTI_TIM_2, val)
-#define ATAPI_GET_ULTRA_TIM_0(base)\
- bfin_read16(base + ATAPI_OFFSET_ULTRA_TIM_0)
-#define ATAPI_SET_ULTRA_TIM_0(base, val)\
- bfin_write16(base + ATAPI_OFFSET_ULTRA_TIM_0, val)
-#define ATAPI_GET_ULTRA_TIM_1(base)\
- bfin_read16(base + ATAPI_OFFSET_ULTRA_TIM_1)
-#define ATAPI_SET_ULTRA_TIM_1(base, val)\
- bfin_write16(base + ATAPI_OFFSET_ULTRA_TIM_1, val)
-#define ATAPI_GET_ULTRA_TIM_2(base)\
- bfin_read16(base + ATAPI_OFFSET_ULTRA_TIM_2)
-#define ATAPI_SET_ULTRA_TIM_2(base, val)\
- bfin_write16(base + ATAPI_OFFSET_ULTRA_TIM_2, val)
-#define ATAPI_GET_ULTRA_TIM_3(base)\
- bfin_read16(base + ATAPI_OFFSET_ULTRA_TIM_3)
-#define ATAPI_SET_ULTRA_TIM_3(base, val)\
- bfin_write16(base + ATAPI_OFFSET_ULTRA_TIM_3, val)
-
-/**
- * PIO Mode - Frequency compatibility
- */
-/* mode: 0 1 2 3 4 */
-static const u32 pio_fsclk[] =
-{ 33333333, 33333333, 33333333, 33333333, 33333333 };
-
-/**
- * MDMA Mode - Frequency compatibility
- */
-/* mode: 0 1 2 */
-static const u32 mdma_fsclk[] = { 33333333, 33333333, 33333333 };
-
-/**
- * UDMA Mode - Frequency compatibility
- *
- * UDMA5 - 100 MB/s - SCLK = 133 MHz
- * UDMA4 - 66 MB/s - SCLK >= 80 MHz
- * UDMA3 - 44.4 MB/s - SCLK >= 50 MHz
- * UDMA2 - 33 MB/s - SCLK >= 40 MHz
- */
-/* mode: 0 1 2 3 4 5 */
-static const u32 udma_fsclk[] =
-{ 33333333, 33333333, 40000000, 50000000, 80000000, 133333333 };
-
-/**
- * Register transfer timing table
- */
-/* mode: 0 1 2 3 4 */
-/* Cycle Time */
-static const u32 reg_t0min[] = { 600, 383, 330, 180, 120 };
-/* DIOR/DIOW to end cycle */
-static const u32 reg_t2min[] = { 290, 290, 290, 70, 25 };
-/* DIOR/DIOW asserted pulse width */
-static const u32 reg_teocmin[] = { 290, 290, 290, 80, 70 };
-
-/**
- * PIO timing table
- */
-/* mode: 0 1 2 3 4 */
-/* Cycle Time */
-static const u32 pio_t0min[] = { 600, 383, 240, 180, 120 };
-/* Address valid to DIOR/DIORW */
-static const u32 pio_t1min[] = { 70, 50, 30, 30, 25 };
-/* DIOR/DIOW to end cycle */
-static const u32 pio_t2min[] = { 165, 125, 100, 80, 70 };
-/* DIOR/DIOW asserted pulse width */
-static const u32 pio_teocmin[] = { 165, 125, 100, 70, 25 };
-/* DIOW data hold */
-static const u32 pio_t4min[] = { 30, 20, 15, 10, 10 };
-
-/* ******************************************************************
- * Multiword DMA timing table
- * ******************************************************************
- */
-/* mode: 0 1 2 */
-/* Cycle Time */
-static const u32 mdma_t0min[] = { 480, 150, 120 };
-/* DIOR/DIOW asserted pulse width */
-static const u32 mdma_tdmin[] = { 215, 80, 70 };
-/* DMACK to read data released */
-static const u32 mdma_thmin[] = { 20, 15, 10 };
-/* DIOR/DIOW to DMACK hold */
-static const u32 mdma_tjmin[] = { 20, 5, 5 };
-/* DIOR negated pulse width */
-static const u32 mdma_tkrmin[] = { 50, 50, 25 };
-/* DIOR negated pulse width */
-static const u32 mdma_tkwmin[] = { 215, 50, 25 };
-/* CS[1:0] valid to DIOR/DIOW */
-static const u32 mdma_tmmin[] = { 50, 30, 25 };
-/* DMACK to read data released */
-static const u32 mdma_tzmax[] = { 20, 25, 25 };
-
-/**
- * Ultra DMA timing table
- */
-/* mode: 0 1 2 3 4 5 */
-static const u32 udma_tcycmin[] = { 112, 73, 54, 39, 25, 17 };
-static const u32 udma_tdvsmin[] = { 70, 48, 31, 20, 7, 5 };
-static const u32 udma_tenvmax[] = { 70, 70, 70, 55, 55, 50 };
-static const u32 udma_trpmin[] = { 160, 125, 100, 100, 100, 85 };
-static const u32 udma_tmin[] = { 5, 5, 5, 5, 3, 3 };
-
-
-static const u32 udma_tmlimin = 20;
-static const u32 udma_tzahmin = 20;
-static const u32 udma_tenvmin = 20;
-static const u32 udma_tackmin = 20;
-static const u32 udma_tssmin = 50;
-
-#define BFIN_MAX_SG_SEGMENTS 4
-
-/**
- *
- * Function: num_clocks_min
- *
- * Description:
- * calculate number of SCLK cycles to meet minimum timing
- */
-static unsigned short num_clocks_min(unsigned long tmin,
- unsigned long fsclk)
-{
- unsigned long tmp ;
- unsigned short result;
-
- tmp = tmin * (fsclk/1000/1000) / 1000;
- result = (unsigned short)tmp;
- if ((tmp*1000*1000) < (tmin*(fsclk/1000))) {
- result++;
- }
-
- return result;
-}
-
-/**
- * bfin_set_piomode - Initialize host controller PATA PIO timings
- * @ap: Port whose timings we are configuring
- * @adev: um
- *
- * Set PIO mode for device.
- *
- * LOCKING:
- * None (inherited from caller).
- */
-
-static void bfin_set_piomode(struct ata_port *ap, struct ata_device *adev)
-{
- int mode = adev->pio_mode - XFER_PIO_0;
- void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr;
- unsigned int fsclk = get_sclk();
- unsigned short teoc_reg, t2_reg, teoc_pio;
- unsigned short t4_reg, t2_pio, t1_reg;
- unsigned short n0, n6, t6min = 5;
-
- /* the most restrictive timing value is t6 and tc, the DIOW - data hold
- * If one SCLK pulse is longer than this minimum value then register
- * transfers cannot be supported at this frequency.
- */
- n6 = num_clocks_min(t6min, fsclk);
- if (mode >= 0 && mode <= 4 && n6 >= 1) {
- dev_dbg(adev->link->ap->dev, "set piomode: mode=%d, fsclk=%ud\n", mode, fsclk);
- /* calculate the timing values for register transfers. */
- while (mode > 0 && pio_fsclk[mode] > fsclk)
- mode--;
-
- /* DIOR/DIOW to end cycle time */
- t2_reg = num_clocks_min(reg_t2min[mode], fsclk);
- /* DIOR/DIOW asserted pulse width */
- teoc_reg = num_clocks_min(reg_teocmin[mode], fsclk);
- /* Cycle Time */
- n0 = num_clocks_min(reg_t0min[mode], fsclk);
-
- /* increase t2 until we meed the minimum cycle length */
- if (t2_reg + teoc_reg < n0)
- t2_reg = n0 - teoc_reg;
-
- /* calculate the timing values for pio transfers. */
-
- /* DIOR/DIOW to end cycle time */
- t2_pio = num_clocks_min(pio_t2min[mode], fsclk);
- /* DIOR/DIOW asserted pulse width */
- teoc_pio = num_clocks_min(pio_teocmin[mode], fsclk);
- /* Cycle Time */
- n0 = num_clocks_min(pio_t0min[mode], fsclk);
-
- /* increase t2 until we meed the minimum cycle length */
- if (t2_pio + teoc_pio < n0)
- t2_pio = n0 - teoc_pio;
-
- /* Address valid to DIOR/DIORW */
- t1_reg = num_clocks_min(pio_t1min[mode], fsclk);
-
- /* DIOW data hold */
- t4_reg = num_clocks_min(pio_t4min[mode], fsclk);
-
- ATAPI_SET_REG_TIM_0(base, (teoc_reg<<8 | t2_reg));
- ATAPI_SET_PIO_TIM_0(base, (t4_reg<<12 | t2_pio<<4 | t1_reg));
- ATAPI_SET_PIO_TIM_1(base, teoc_pio);
- if (mode > 2) {
- ATAPI_SET_CONTROL(base,
- ATAPI_GET_CONTROL(base) | IORDY_EN);
- } else {
- ATAPI_SET_CONTROL(base,
- ATAPI_GET_CONTROL(base) & ~IORDY_EN);
- }
-
- /* Disable host ATAPI PIO interrupts */
- ATAPI_SET_INT_MASK(base, ATAPI_GET_INT_MASK(base)
- & ~(PIO_DONE_MASK | HOST_TERM_XFER_MASK));
- SSYNC();
- }
-}
-
-/**
- * bfin_set_dmamode - Initialize host controller PATA DMA timings
- * @ap: Port whose timings we are configuring
- * @adev: um
- *
- * Set UDMA mode for device.
- *
- * LOCKING:
- * None (inherited from caller).
- */
-
-static void bfin_set_dmamode(struct ata_port *ap, struct ata_device *adev)
-{
- int mode;
- void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr;
- unsigned long fsclk = get_sclk();
- unsigned short tenv, tack, tcyc_tdvs, tdvs, tmli, tss, trp, tzah;
- unsigned short tm, td, tkr, tkw, teoc, th;
- unsigned short n0, nf, tfmin = 5;
- unsigned short nmin, tcyc;
-
- mode = adev->dma_mode - XFER_UDMA_0;
- if (mode >= 0 && mode <= 5) {
- dev_dbg(adev->link->ap->dev, "set udmamode: mode=%d\n", mode);
- /* the most restrictive timing value is t6 and tc,
- * the DIOW - data hold. If one SCLK pulse is longer
- * than this minimum value then register
- * transfers cannot be supported at this frequency.
- */
- while (mode > 0 && udma_fsclk[mode] > fsclk)
- mode--;
-
- nmin = num_clocks_min(udma_tmin[mode], fsclk);
- if (nmin >= 1) {
- /* calculate the timing values for Ultra DMA. */
- tdvs = num_clocks_min(udma_tdvsmin[mode], fsclk);
- tcyc = num_clocks_min(udma_tcycmin[mode], fsclk);
- tcyc_tdvs = 2;
-
- /* increase tcyc - tdvs (tcyc_tdvs) until we meed
- * the minimum cycle length
- */
- if (tdvs + tcyc_tdvs < tcyc)
- tcyc_tdvs = tcyc - tdvs;
-
- /* Mow assign the values required for the timing
- * registers
- */
- if (tcyc_tdvs < 2)
- tcyc_tdvs = 2;
-
- if (tdvs < 2)
- tdvs = 2;
-
- tack = num_clocks_min(udma_tackmin, fsclk);
- tss = num_clocks_min(udma_tssmin, fsclk);
- tmli = num_clocks_min(udma_tmlimin, fsclk);
- tzah = num_clocks_min(udma_tzahmin, fsclk);
- trp = num_clocks_min(udma_trpmin[mode], fsclk);
- tenv = num_clocks_min(udma_tenvmin, fsclk);
- if (tenv <= udma_tenvmax[mode]) {
- ATAPI_SET_ULTRA_TIM_0(base, (tenv<<8 | tack));
- ATAPI_SET_ULTRA_TIM_1(base,
- (tcyc_tdvs<<8 | tdvs));
- ATAPI_SET_ULTRA_TIM_2(base, (tmli<<8 | tss));
- ATAPI_SET_ULTRA_TIM_3(base, (trp<<8 | tzah));
- }
- }
- }
-
- mode = adev->dma_mode - XFER_MW_DMA_0;
- if (mode >= 0 && mode <= 2) {
- dev_dbg(adev->link->ap->dev, "set mdmamode: mode=%d\n", mode);
- /* the most restrictive timing value is tf, the DMACK to
- * read data released. If one SCLK pulse is longer than
- * this maximum value then the MDMA mode
- * cannot be supported at this frequency.
- */
- while (mode > 0 && mdma_fsclk[mode] > fsclk)
- mode--;
-
- nf = num_clocks_min(tfmin, fsclk);
- if (nf >= 1) {
- /* calculate the timing values for Multi-word DMA. */
-
- /* DIOR/DIOW asserted pulse width */
- td = num_clocks_min(mdma_tdmin[mode], fsclk);
-
- /* DIOR negated pulse width */
- tkw = num_clocks_min(mdma_tkwmin[mode], fsclk);
-
- /* Cycle Time */
- n0 = num_clocks_min(mdma_t0min[mode], fsclk);
-
- /* increase tk until we meed the minimum cycle length */
- if (tkw + td < n0)
- tkw = n0 - td;
-
- /* DIOR negated pulse width - read */
- tkr = num_clocks_min(mdma_tkrmin[mode], fsclk);
- /* CS{1:0] valid to DIOR/DIOW */
- tm = num_clocks_min(mdma_tmmin[mode], fsclk);
- /* DIOR/DIOW to DMACK hold */
- teoc = num_clocks_min(mdma_tjmin[mode], fsclk);
- /* DIOW Data hold */
- th = num_clocks_min(mdma_thmin[mode], fsclk);
-
- ATAPI_SET_MULTI_TIM_0(base, (tm<<8 | td));
- ATAPI_SET_MULTI_TIM_1(base, (tkr<<8 | tkw));
- ATAPI_SET_MULTI_TIM_2(base, (teoc<<8 | th));
- SSYNC();
- }
- }
- return;
-}
-
-/**
- *
- * Function: wait_complete
- *
- * Description: Waits the interrupt from device
- *
- */
-static inline void wait_complete(void __iomem *base, unsigned short mask)
-{
- unsigned short status;
- unsigned int i = 0;
-
-#define PATA_BF54X_WAIT_TIMEOUT 10000
-
- for (i = 0; i < PATA_BF54X_WAIT_TIMEOUT; i++) {
- status = ATAPI_GET_INT_STATUS(base) & mask;
- if (status)
- break;
- }
-
- ATAPI_SET_INT_STATUS(base, mask);
-}
-
-/**
- *
- * Function: write_atapi_register
- *
- * Description: Writes to ATA Device Resgister
- *
- */
-
-static void write_atapi_register(void __iomem *base,
- unsigned long ata_reg, unsigned short value)
-{
- /* Program the ATA_DEV_TXBUF register with write data (to be
- * written into the device).
- */
- ATAPI_SET_DEV_TXBUF(base, value);
-
- /* Program the ATA_DEV_ADDR register with address of the
- * device register (0x01 to 0x0F).
- */
- ATAPI_SET_DEV_ADDR(base, ata_reg);
-
- /* Program the ATA_CTRL register with dir set to write (1)
- */
- ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) | XFER_DIR));
-
- /* ensure PIO DMA is not set */
- ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) & ~PIO_USE_DMA));
-
- /* and start the transfer */
- ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) | PIO_START));
-
- /* Wait for the interrupt to indicate the end of the transfer.
- * (We need to wait on and clear rhe ATA_DEV_INT interrupt status)
- */
- wait_complete(base, PIO_DONE_INT);
-}
-
-/**
- *
- * Function: read_atapi_register
- *
- *Description: Reads from ATA Device Resgister
- *
- */
-
-static unsigned short read_atapi_register(void __iomem *base,
- unsigned long ata_reg)
-{
- /* Program the ATA_DEV_ADDR register with address of the
- * device register (0x01 to 0x0F).
- */
- ATAPI_SET_DEV_ADDR(base, ata_reg);
-
- /* Program the ATA_CTRL register with dir set to read (0) and
- */
- ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) & ~XFER_DIR));
-
- /* ensure PIO DMA is not set */
- ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) & ~PIO_USE_DMA));
-
- /* and start the transfer */
- ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) | PIO_START));
-
- /* Wait for the interrupt to indicate the end of the transfer.
- * (PIO_DONE interrupt is set and it doesn't seem to matter
- * that we don't clear it)
- */
- wait_complete(base, PIO_DONE_INT);
-
- /* Read the ATA_DEV_RXBUF register with write data (to be
- * written into the device).
- */
- return ATAPI_GET_DEV_RXBUF(base);
-}
-
-/**
- *
- * Function: write_atapi_register_data
- *
- * Description: Writes to ATA Device Resgister
- *
- */
-
-static void write_atapi_data(void __iomem *base,
- int len, unsigned short *buf)
-{
- int i;
-
- /* Set transfer length to 1 */
- ATAPI_SET_XFER_LEN(base, 1);
-
- /* Program the ATA_DEV_ADDR register with address of the
- * ATA_REG_DATA
- */
- ATAPI_SET_DEV_ADDR(base, ATA_REG_DATA);
-
- /* Program the ATA_CTRL register with dir set to write (1)
- */
- ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) | XFER_DIR));
-
- /* ensure PIO DMA is not set */
- ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) & ~PIO_USE_DMA));
-
- for (i = 0; i < len; i++) {
- /* Program the ATA_DEV_TXBUF register with write data (to be
- * written into the device).
- */
- ATAPI_SET_DEV_TXBUF(base, buf[i]);
-
- /* and start the transfer */
- ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) | PIO_START));
-
- /* Wait for the interrupt to indicate the end of the transfer.
- * (We need to wait on and clear rhe ATA_DEV_INT
- * interrupt status)
- */
- wait_complete(base, PIO_DONE_INT);
- }
-}
-
-/**
- *
- * Function: read_atapi_register_data
- *
- * Description: Reads from ATA Device Resgister
- *
- */
-
-static void read_atapi_data(void __iomem *base,
- int len, unsigned short *buf)
-{
- int i;
-
- /* Set transfer length to 1 */
- ATAPI_SET_XFER_LEN(base, 1);
-
- /* Program the ATA_DEV_ADDR register with address of the
- * ATA_REG_DATA
- */
- ATAPI_SET_DEV_ADDR(base, ATA_REG_DATA);
-
- /* Program the ATA_CTRL register with dir set to read (0) and
- */
- ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) & ~XFER_DIR));
-
- /* ensure PIO DMA is not set */
- ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) & ~PIO_USE_DMA));
-
- for (i = 0; i < len; i++) {
- /* and start the transfer */
- ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) | PIO_START));
-
- /* Wait for the interrupt to indicate the end of the transfer.
- * (PIO_DONE interrupt is set and it doesn't seem to matter
- * that we don't clear it)
- */
- wait_complete(base, PIO_DONE_INT);
-
- /* Read the ATA_DEV_RXBUF register with write data (to be
- * written into the device).
- */
- buf[i] = ATAPI_GET_DEV_RXBUF(base);
- }
-}
-
-/**
- * bfin_tf_load - send taskfile registers to host controller
- * @ap: Port to which output is sent
- * @tf: ATA taskfile register set
- *
- * Note: Original code is ata_sff_tf_load().
- */
-
-static void bfin_tf_load(struct ata_port *ap, const struct ata_taskfile *tf)
-{
- void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr;
- unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR;
-
- if (tf->ctl != ap->last_ctl) {
- write_atapi_register(base, ATA_REG_CTRL, tf->ctl);
- ap->last_ctl = tf->ctl;
- ata_wait_idle(ap);
- }
-
- if (is_addr) {
- if (tf->flags & ATA_TFLAG_LBA48) {
- write_atapi_register(base, ATA_REG_FEATURE,
- tf->hob_feature);
- write_atapi_register(base, ATA_REG_NSECT,
- tf->hob_nsect);
- write_atapi_register(base, ATA_REG_LBAL, tf->hob_lbal);
- write_atapi_register(base, ATA_REG_LBAM, tf->hob_lbam);
- write_atapi_register(base, ATA_REG_LBAH, tf->hob_lbah);
- dev_dbg(ap->dev, "hob: feat 0x%X nsect 0x%X, lba 0x%X "
- "0x%X 0x%X\n",
- tf->hob_feature,
- tf->hob_nsect,
- tf->hob_lbal,
- tf->hob_lbam,
- tf->hob_lbah);
- }
-
- write_atapi_register(base, ATA_REG_FEATURE, tf->feature);
- write_atapi_register(base, ATA_REG_NSECT, tf->nsect);
- write_atapi_register(base, ATA_REG_LBAL, tf->lbal);
- write_atapi_register(base, ATA_REG_LBAM, tf->lbam);
- write_atapi_register(base, ATA_REG_LBAH, tf->lbah);
- dev_dbg(ap->dev, "feat 0x%X nsect 0x%X lba 0x%X 0x%X 0x%X\n",
- tf->feature,
- tf->nsect,
- tf->lbal,
- tf->lbam,
- tf->lbah);
- }
-
- if (tf->flags & ATA_TFLAG_DEVICE) {
- write_atapi_register(base, ATA_REG_DEVICE, tf->device);
- dev_dbg(ap->dev, "device 0x%X\n", tf->device);
- }
-
- ata_wait_idle(ap);
-}
-
-/**
- * bfin_check_status - Read device status reg & clear interrupt
- * @ap: port where the device is
- *
- * Note: Original code is ata_check_status().
- */
-
-static u8 bfin_check_status(struct ata_port *ap)
-{
- void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr;
- return read_atapi_register(base, ATA_REG_STATUS);
-}
-
-/**
- * bfin_tf_read - input device's ATA taskfile shadow registers
- * @ap: Port from which input is read
- * @tf: ATA taskfile register set for storing input
- *
- * Note: Original code is ata_sff_tf_read().
- */
-
-static void bfin_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
-{
- void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr;
-
- tf->command = bfin_check_status(ap);
- tf->feature = read_atapi_register(base, ATA_REG_ERR);
- tf->nsect = read_atapi_register(base, ATA_REG_NSECT);
- tf->lbal = read_atapi_register(base, ATA_REG_LBAL);
- tf->lbam = read_atapi_register(base, ATA_REG_LBAM);
- tf->lbah = read_atapi_register(base, ATA_REG_LBAH);
- tf->device = read_atapi_register(base, ATA_REG_DEVICE);
-
- if (tf->flags & ATA_TFLAG_LBA48) {
- write_atapi_register(base, ATA_REG_CTRL, tf->ctl | ATA_HOB);
- tf->hob_feature = read_atapi_register(base, ATA_REG_ERR);
- tf->hob_nsect = read_atapi_register(base, ATA_REG_NSECT);
- tf->hob_lbal = read_atapi_register(base, ATA_REG_LBAL);
- tf->hob_lbam = read_atapi_register(base, ATA_REG_LBAM);
- tf->hob_lbah = read_atapi_register(base, ATA_REG_LBAH);
- }
-}
-
-/**
- * bfin_exec_command - issue ATA command to host controller
- * @ap: port to which command is being issued
- * @tf: ATA taskfile register set
- *
- * Note: Original code is ata_sff_exec_command().
- */
-
-static void bfin_exec_command(struct ata_port *ap,
- const struct ata_taskfile *tf)
-{
- void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr;
- dev_dbg(ap->dev, "ata%u: cmd 0x%X\n", ap->print_id, tf->command);
-
- write_atapi_register(base, ATA_REG_CMD, tf->command);
- ata_sff_pause(ap);
-}
-
-/**
- * bfin_check_altstatus - Read device alternate status reg
- * @ap: port where the device is
- */
-
-static u8 bfin_check_altstatus(struct ata_port *ap)
-{
- void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr;
- return read_atapi_register(base, ATA_REG_ALTSTATUS);
-}
-
-/**
- * bfin_dev_select - Select device 0/1 on ATA bus
- * @ap: ATA channel to manipulate
- * @device: ATA device (numbered from zero) to select
- *
- * Note: Original code is ata_sff_dev_select().
- */
-
-static void bfin_dev_select(struct ata_port *ap, unsigned int device)
-{
- void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr;
- u8 tmp;
-
- if (device == 0)
- tmp = ATA_DEVICE_OBS;
- else
- tmp = ATA_DEVICE_OBS | ATA_DEV1;
-
- write_atapi_register(base, ATA_REG_DEVICE, tmp);
- ata_sff_pause(ap);
-}
-
-/**
- * bfin_set_devctl - Write device control reg
- * @ap: port where the device is
- * @ctl: value to write
- */
-
-static void bfin_set_devctl(struct ata_port *ap, u8 ctl)
-{
- void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr;
- write_atapi_register(base, ATA_REG_CTRL, ctl);
-}
-
-/**
- * bfin_bmdma_setup - Set up IDE DMA transaction
- * @qc: Info associated with this ATA transaction.
- *
- * Note: Original code is ata_bmdma_setup().
- */
-
-static void bfin_bmdma_setup(struct ata_queued_cmd *qc)
-{
- struct ata_port *ap = qc->ap;
- struct dma_desc_array *dma_desc_cpu = (struct dma_desc_array *)ap->bmdma_prd;
- void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr;
- unsigned short config = DMAFLOW_ARRAY | NDSIZE_5 | RESTART | WDSIZE_16 | DMAEN;
- struct scatterlist *sg;
- unsigned int si;
- unsigned int channel;
- unsigned int dir;
- unsigned int size = 0;
-
- dev_dbg(qc->ap->dev, "in atapi dma setup\n");
- /* Program the ATA_CTRL register with dir */
- if (qc->tf.flags & ATA_TFLAG_WRITE) {
- channel = CH_ATAPI_TX;
- dir = DMA_TO_DEVICE;
- } else {
- channel = CH_ATAPI_RX;
- dir = DMA_FROM_DEVICE;
- config |= WNR;
- }
-
- dma_map_sg(ap->dev, qc->sg, qc->n_elem, dir);
-
- /* fill the ATAPI DMA controller */
- for_each_sg(qc->sg, sg, qc->n_elem, si) {
- dma_desc_cpu[si].start_addr = sg_dma_address(sg);
- dma_desc_cpu[si].cfg = config;
- dma_desc_cpu[si].x_count = sg_dma_len(sg) >> 1;
- dma_desc_cpu[si].x_modify = 2;
- size += sg_dma_len(sg);
- }
-
- /* Set the last descriptor to stop mode */
- dma_desc_cpu[qc->n_elem - 1].cfg &= ~(DMAFLOW | NDSIZE);
-
- flush_dcache_range((unsigned int)dma_desc_cpu,
- (unsigned int)dma_desc_cpu +
- qc->n_elem * sizeof(struct dma_desc_array));
-
- /* Enable ATA DMA operation*/
- set_dma_curr_desc_addr(channel, (unsigned long *)ap->bmdma_prd_dma);
- set_dma_x_count(channel, 0);
- set_dma_x_modify(channel, 0);
- set_dma_config(channel, config);
-
- SSYNC();
-
- /* Send ATA DMA command */
- bfin_exec_command(ap, &qc->tf);
-
- if (qc->tf.flags & ATA_TFLAG_WRITE) {
- /* set ATA DMA write direction */
- ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base)
- | XFER_DIR));
- } else {
- /* set ATA DMA read direction */
- ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base)
- & ~XFER_DIR));
- }
-
- /* Reset all transfer count */
- ATAPI_SET_CONTROL(base, ATAPI_GET_CONTROL(base) | TFRCNT_RST);
-
- /* Set ATAPI state machine contorl in terminate sequence */
- ATAPI_SET_CONTROL(base, ATAPI_GET_CONTROL(base) | END_ON_TERM);
-
- /* Set transfer length to the total size of sg buffers */
- ATAPI_SET_XFER_LEN(base, size >> 1);
-}
-
-/**
- * bfin_bmdma_start - Start an IDE DMA transaction
- * @qc: Info associated with this ATA transaction.
- *
- * Note: Original code is ata_bmdma_start().
- */
-
-static void bfin_bmdma_start(struct ata_queued_cmd *qc)
-{
- struct ata_port *ap = qc->ap;
- void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr;
-
- dev_dbg(qc->ap->dev, "in atapi dma start\n");
-
- if (!(ap->udma_mask || ap->mwdma_mask))
- return;
-
- /* start ATAPI transfer*/
- if (ap->udma_mask)
- ATAPI_SET_CONTROL(base, ATAPI_GET_CONTROL(base)
- | ULTRA_START);
- else
- ATAPI_SET_CONTROL(base, ATAPI_GET_CONTROL(base)
- | MULTI_START);
-}
-
-/**
- * bfin_bmdma_stop - Stop IDE DMA transfer
- * @qc: Command we are ending DMA for
- */
-
-static void bfin_bmdma_stop(struct ata_queued_cmd *qc)
-{
- struct ata_port *ap = qc->ap;
- unsigned int dir;
-
- dev_dbg(qc->ap->dev, "in atapi dma stop\n");
-
- if (!(ap->udma_mask || ap->mwdma_mask))
- return;
-
- /* stop ATAPI DMA controller*/
- if (qc->tf.flags & ATA_TFLAG_WRITE) {
- dir = DMA_TO_DEVICE;
- disable_dma(CH_ATAPI_TX);
- } else {
- dir = DMA_FROM_DEVICE;
- disable_dma(CH_ATAPI_RX);
- }
-
- dma_unmap_sg(ap->dev, qc->sg, qc->n_elem, dir);
-}
-
-/**
- * bfin_devchk - PATA device presence detection
- * @ap: ATA channel to examine
- * @device: Device to examine (starting at zero)
- *
- * Note: Original code is ata_devchk().
- */
-
-static unsigned int bfin_devchk(struct ata_port *ap,
- unsigned int device)
-{
- void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr;
- u8 nsect, lbal;
-
- bfin_dev_select(ap, device);
-
- write_atapi_register(base, ATA_REG_NSECT, 0x55);
- write_atapi_register(base, ATA_REG_LBAL, 0xaa);
-
- write_atapi_register(base, ATA_REG_NSECT, 0xaa);
- write_atapi_register(base, ATA_REG_LBAL, 0x55);
-
- write_atapi_register(base, ATA_REG_NSECT, 0x55);
- write_atapi_register(base, ATA_REG_LBAL, 0xaa);
-
- nsect = read_atapi_register(base, ATA_REG_NSECT);
- lbal = read_atapi_register(base, ATA_REG_LBAL);
-
- if ((nsect == 0x55) && (lbal == 0xaa))
- return 1; /* we found a device */
-
- return 0; /* nothing found */
-}
-
-/**
- * bfin_bus_post_reset - PATA device post reset
- *
- * Note: Original code is ata_bus_post_reset().
- */
-
-static void bfin_bus_post_reset(struct ata_port *ap, unsigned int devmask)
-{
- void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr;
- unsigned int dev0 = devmask & (1 << 0);
- unsigned int dev1 = devmask & (1 << 1);
- unsigned long deadline;
-
- /* if device 0 was found in ata_devchk, wait for its
- * BSY bit to clear
- */
- if (dev0)
- ata_sff_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT);
-
- /* if device 1 was found in ata_devchk, wait for
- * register access, then wait for BSY to clear
- */
- deadline = ata_deadline(jiffies, ATA_TMOUT_BOOT);
- while (dev1) {
- u8 nsect, lbal;
-
- bfin_dev_select(ap, 1);
- nsect = read_atapi_register(base, ATA_REG_NSECT);
- lbal = read_atapi_register(base, ATA_REG_LBAL);
- if ((nsect == 1) && (lbal == 1))
- break;
- if (time_after(jiffies, deadline)) {
- dev1 = 0;
- break;
- }
- ata_msleep(ap, 50); /* give drive a breather */
- }
- if (dev1)
- ata_sff_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT);
-
- /* is all this really necessary? */
- bfin_dev_select(ap, 0);
- if (dev1)
- bfin_dev_select(ap, 1);
- if (dev0)
- bfin_dev_select(ap, 0);
-}
-
-/**
- * bfin_bus_softreset - PATA device software reset
- *
- * Note: Original code is ata_bus_softreset().
- */
-
-static unsigned int bfin_bus_softreset(struct ata_port *ap,
- unsigned int devmask)
-{
- void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr;
-
- /* software reset. causes dev0 to be selected */
- write_atapi_register(base, ATA_REG_CTRL, ap->ctl);
- udelay(20);
- write_atapi_register(base, ATA_REG_CTRL, ap->ctl | ATA_SRST);
- udelay(20);
- write_atapi_register(base, ATA_REG_CTRL, ap->ctl);
-
- /* spec mandates ">= 2ms" before checking status.
- * We wait 150ms, because that was the magic delay used for
- * ATAPI devices in Hale Landis's ATADRVR, for the period of time
- * between when the ATA command register is written, and then
- * status is checked. Because waiting for "a while" before
- * checking status is fine, post SRST, we perform this magic
- * delay here as well.
- *
- * Old drivers/ide uses the 2mS rule and then waits for ready
- */
- ata_msleep(ap, 150);
-
- /* Before we perform post reset processing we want to see if
- * the bus shows 0xFF because the odd clown forgets the D7
- * pulldown resistor.
- */
- if (bfin_check_status(ap) == 0xFF)
- return 0;
-
- bfin_bus_post_reset(ap, devmask);
-
- return 0;
-}
-
-/**
- * bfin_softreset - reset host port via ATA SRST
- * @ap: port to reset
- * @classes: resulting classes of attached devices
- *
- * Note: Original code is ata_sff_softreset().
- */
-
-static int bfin_softreset(struct ata_link *link, unsigned int *classes,
- unsigned long deadline)
-{
- struct ata_port *ap = link->ap;
- unsigned int slave_possible = ap->flags & ATA_FLAG_SLAVE_POSS;
- unsigned int devmask = 0, err_mask;
- u8 err;
-
- /* determine if device 0/1 are present */
- if (bfin_devchk(ap, 0))
- devmask |= (1 << 0);
- if (slave_possible && bfin_devchk(ap, 1))
- devmask |= (1 << 1);
-
- /* select device 0 again */
- bfin_dev_select(ap, 0);
-
- /* issue bus reset */
- err_mask = bfin_bus_softreset(ap, devmask);
- if (err_mask) {
- ata_port_err(ap, "SRST failed (err_mask=0x%x)\n",
- err_mask);
- return -EIO;
- }
-
- /* determine by signature whether we have ATA or ATAPI devices */
- classes[0] = ata_sff_dev_classify(&ap->link.device[0],
- devmask & (1 << 0), &err);
- if (slave_possible && err != 0x81)
- classes[1] = ata_sff_dev_classify(&ap->link.device[1],
- devmask & (1 << 1), &err);
-
- return 0;
-}
-
-/**
- * bfin_bmdma_status - Read IDE DMA status
- * @ap: Port associated with this ATA transaction.
- */
-
-static unsigned char bfin_bmdma_status(struct ata_port *ap)
-{
- unsigned char host_stat = 0;
- void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr;
-
- if (ATAPI_GET_STATUS(base) & (MULTI_XFER_ON | ULTRA_XFER_ON))
- host_stat |= ATA_DMA_ACTIVE;
- if (ATAPI_GET_INT_STATUS(base) & ATAPI_DEV_INT)
- host_stat |= ATA_DMA_INTR;
-
- dev_dbg(ap->dev, "ATAPI: host_stat=0x%x\n", host_stat);
-
- return host_stat;
-}
-
-/**
- * bfin_data_xfer - Transfer data by PIO
- * @qc: queued command
- * @buf: data buffer
- * @buflen: buffer length
- * @write_data: read/write
- *
- * Note: Original code is ata_sff_data_xfer().
- */
-
-static unsigned int bfin_data_xfer(struct ata_queued_cmd *qc,
- unsigned char *buf,
- unsigned int buflen, int rw)
-{
- struct ata_port *ap = qc->dev->link->ap;
- void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr;
- unsigned int words = buflen >> 1;
- unsigned short *buf16 = (u16 *)buf;
-
- /* Transfer multiple of 2 bytes */
- if (rw == READ)
- read_atapi_data(base, words, buf16);
- else
- write_atapi_data(base, words, buf16);
-
- /* Transfer trailing 1 byte, if any. */
- if (unlikely(buflen & 0x01)) {
- unsigned short align_buf[1] = { 0 };
- unsigned char *trailing_buf = buf + buflen - 1;
-
- if (rw == READ) {
- read_atapi_data(base, 1, align_buf);
- memcpy(trailing_buf, align_buf, 1);
- } else {
- memcpy(align_buf, trailing_buf, 1);
- write_atapi_data(base, 1, align_buf);
- }
- words++;
- }
-
- return words << 1;
-}
-
-/**
- * bfin_irq_clear - Clear ATAPI interrupt.
- * @ap: Port associated with this ATA transaction.
- *
- * Note: Original code is ata_bmdma_irq_clear().
- */
-
-static void bfin_irq_clear(struct ata_port *ap)
-{
- void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr;
-
- dev_dbg(ap->dev, "in atapi irq clear\n");
- ATAPI_SET_INT_STATUS(base, ATAPI_GET_INT_STATUS(base)|ATAPI_DEV_INT
- | MULTI_DONE_INT | UDMAIN_DONE_INT | UDMAOUT_DONE_INT
- | MULTI_TERM_INT | UDMAIN_TERM_INT | UDMAOUT_TERM_INT);
-}
-
-/**
- * bfin_thaw - Thaw DMA controller port
- * @ap: port to thaw
- *
- * Note: Original code is ata_sff_thaw().
- */
-
-void bfin_thaw(struct ata_port *ap)
-{
- dev_dbg(ap->dev, "in atapi dma thaw\n");
- bfin_check_status(ap);
- ata_sff_irq_on(ap);
-}
-
-/**
- * bfin_postreset - standard postreset callback
- * @ap: the target ata_port
- * @classes: classes of attached devices
- *
- * Note: Original code is ata_sff_postreset().
- */
-
-static void bfin_postreset(struct ata_link *link, unsigned int *classes)
-{
- struct ata_port *ap = link->ap;
- void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr;
-
- /* re-enable interrupts */
- ata_sff_irq_on(ap);
-
- /* is double-select really necessary? */
- if (classes[0] != ATA_DEV_NONE)
- bfin_dev_select(ap, 1);
- if (classes[1] != ATA_DEV_NONE)
- bfin_dev_select(ap, 0);
-
- /* bail out if no device is present */
- if (classes[0] == ATA_DEV_NONE && classes[1] == ATA_DEV_NONE) {
- return;
- }
-
- /* set up device control */
- write_atapi_register(base, ATA_REG_CTRL, ap->ctl);
-}
-
-static void bfin_port_stop(struct ata_port *ap)
-{
- dev_dbg(ap->dev, "in atapi port stop\n");
- if (ap->udma_mask != 0 || ap->mwdma_mask != 0) {
- dma_free_coherent(ap->dev,
- BFIN_MAX_SG_SEGMENTS * sizeof(struct dma_desc_array),
- ap->bmdma_prd,
- ap->bmdma_prd_dma);
-
- free_dma(CH_ATAPI_RX);
- free_dma(CH_ATAPI_TX);
- }
-}
-
-static int bfin_port_start(struct ata_port *ap)
-{
- dev_dbg(ap->dev, "in atapi port start\n");
- if (!(ap->udma_mask || ap->mwdma_mask))
- return 0;
-
- ap->bmdma_prd = dma_alloc_coherent(ap->dev,
- BFIN_MAX_SG_SEGMENTS * sizeof(struct dma_desc_array),
- &ap->bmdma_prd_dma,
- GFP_KERNEL);
-
- if (ap->bmdma_prd == NULL) {
- dev_info(ap->dev, "Unable to allocate DMA descriptor array.\n");
- goto out;
- }
-
- if (request_dma(CH_ATAPI_RX, "BFIN ATAPI RX DMA") >= 0) {
- if (request_dma(CH_ATAPI_TX,
- "BFIN ATAPI TX DMA") >= 0)
- return 0;
-
- free_dma(CH_ATAPI_RX);
- dma_free_coherent(ap->dev,
- BFIN_MAX_SG_SEGMENTS * sizeof(struct dma_desc_array),
- ap->bmdma_prd,
- ap->bmdma_prd_dma);
- }
-
-out:
- ap->udma_mask = 0;
- ap->mwdma_mask = 0;
- dev_err(ap->dev, "Unable to request ATAPI DMA!"
- " Continue in PIO mode.\n");
-
- return 0;
-}
-
-static unsigned int bfin_ata_host_intr(struct ata_port *ap,
- struct ata_queued_cmd *qc)
-{
- struct ata_eh_info *ehi = &ap->link.eh_info;
- u8 status, host_stat = 0;
-
- VPRINTK("ata%u: protocol %d task_state %d\n",
- ap->print_id, qc->tf.protocol, ap->hsm_task_state);
-
- /* Check whether we are expecting interrupt in this state */
- switch (ap->hsm_task_state) {
- case HSM_ST_FIRST:
- /* Some pre-ATAPI-4 devices assert INTRQ
- * at this state when ready to receive CDB.
- */
-
- /* Check the ATA_DFLAG_CDB_INTR flag is enough here.
- * The flag was turned on only for atapi devices.
- * No need to check is_atapi_taskfile(&qc->tf) again.
- */
- if (!(qc->dev->flags & ATA_DFLAG_CDB_INTR))
- goto idle_irq;
- break;
- case HSM_ST_LAST:
- if (qc->tf.protocol == ATA_PROT_DMA ||
- qc->tf.protocol == ATAPI_PROT_DMA) {
- /* check status of DMA engine */
- host_stat = ap->ops->bmdma_status(ap);
- VPRINTK("ata%u: host_stat 0x%X\n",
- ap->print_id, host_stat);
-
- /* if it's not our irq... */
- if (!(host_stat & ATA_DMA_INTR))
- goto idle_irq;
-
- /* before we do anything else, clear DMA-Start bit */
- ap->ops->bmdma_stop(qc);
-
- if (unlikely(host_stat & ATA_DMA_ERR)) {
- /* error when transferring data to/from memory */
- qc->err_mask |= AC_ERR_HOST_BUS;
- ap->hsm_task_state = HSM_ST_ERR;
- }
- }
- break;
- case HSM_ST:
- break;
- default:
- goto idle_irq;
- }
-
- /* check altstatus */
- status = ap->ops->sff_check_altstatus(ap);
- if (status & ATA_BUSY)
- goto busy_ata;
-
- /* check main status, clearing INTRQ */
- status = ap->ops->sff_check_status(ap);
- if (unlikely(status & ATA_BUSY))
- goto busy_ata;
-
- /* ack bmdma irq events */
- ap->ops->sff_irq_clear(ap);
-
- ata_sff_hsm_move(ap, qc, status, 0);
-
- if (unlikely(qc->err_mask) && (qc->tf.protocol == ATA_PROT_DMA ||
- qc->tf.protocol == ATAPI_PROT_DMA))
- ata_ehi_push_desc(ehi, "BMDMA stat 0x%x", host_stat);
-
-busy_ata:
- return 1; /* irq handled */
-
-idle_irq:
- ap->stats.idle_irq++;
-
-#ifdef ATA_IRQ_TRAP
- if ((ap->stats.idle_irq % 1000) == 0) {
- ap->ops->irq_ack(ap, 0); /* debug trap */
- ata_port_warn(ap, "irq trap\n");
- return 1;
- }
-#endif
- return 0; /* irq not handled */
-}
-
-static irqreturn_t bfin_ata_interrupt(int irq, void *dev_instance)
-{
- struct ata_host *host = dev_instance;
- unsigned int i;
- unsigned int handled = 0;
- unsigned long flags;
-
- /* TODO: make _irqsave conditional on x86 PCI IDE legacy mode */
- spin_lock_irqsave(&host->lock, flags);
-
- for (i = 0; i < host->n_ports; i++) {
- struct ata_port *ap = host->ports[i];
- struct ata_queued_cmd *qc;
-
- qc = ata_qc_from_tag(ap, ap->link.active_tag);
- if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING)))
- handled |= bfin_ata_host_intr(ap, qc);
- }
-
- spin_unlock_irqrestore(&host->lock, flags);
-
- return IRQ_RETVAL(handled);
-}
-
-
-static struct scsi_host_template bfin_sht = {
- ATA_BASE_SHT(DRV_NAME),
- .sg_tablesize = BFIN_MAX_SG_SEGMENTS,
- .dma_boundary = ATA_DMA_BOUNDARY,
-};
-
-static struct ata_port_operations bfin_pata_ops = {
- .inherits = &ata_bmdma_port_ops,
-
- .set_piomode = bfin_set_piomode,
- .set_dmamode = bfin_set_dmamode,
-
- .sff_tf_load = bfin_tf_load,
- .sff_tf_read = bfin_tf_read,
- .sff_exec_command = bfin_exec_command,
- .sff_check_status = bfin_check_status,
- .sff_check_altstatus = bfin_check_altstatus,
- .sff_dev_select = bfin_dev_select,
- .sff_set_devctl = bfin_set_devctl,
-
- .bmdma_setup = bfin_bmdma_setup,
- .bmdma_start = bfin_bmdma_start,
- .bmdma_stop = bfin_bmdma_stop,
- .bmdma_status = bfin_bmdma_status,
- .sff_data_xfer = bfin_data_xfer,
-
- .qc_prep = ata_noop_qc_prep,
-
- .thaw = bfin_thaw,
- .softreset = bfin_softreset,
- .postreset = bfin_postreset,
-
- .sff_irq_clear = bfin_irq_clear,
-
- .port_start = bfin_port_start,
- .port_stop = bfin_port_stop,
-};
-
-static struct ata_port_info bfin_port_info[] = {
- {
- .flags = ATA_FLAG_SLAVE_POSS,
- .pio_mask = ATA_PIO4,
- .mwdma_mask = 0,
- .udma_mask = 0,
- .port_ops = &bfin_pata_ops,
- },
-};
-
-/**
- * bfin_reset_controller - initialize BF54x ATAPI controller.
- */
-
-static int bfin_reset_controller(struct ata_host *host)
-{
- void __iomem *base = (void __iomem *)host->ports[0]->ioaddr.ctl_addr;
- int count;
- unsigned short status;
-
- /* Disable all ATAPI interrupts */
- ATAPI_SET_INT_MASK(base, 0);
- SSYNC();
-
- /* Assert the RESET signal 25us*/
- ATAPI_SET_CONTROL(base, ATAPI_GET_CONTROL(base) | DEV_RST);
- udelay(30);
-
- /* Negate the RESET signal for 2ms*/
- ATAPI_SET_CONTROL(base, ATAPI_GET_CONTROL(base) & ~DEV_RST);
- msleep(2);
-
- /* Wait on Busy flag to clear */
- count = 10000000;
- do {
- status = read_atapi_register(base, ATA_REG_STATUS);
- } while (--count && (status & ATA_BUSY));
-
- /* Enable only ATAPI Device interrupt */
- ATAPI_SET_INT_MASK(base, 1);
- SSYNC();
-
- return (!count);
-}
-
-/**
- * atapi_io_port - define atapi peripheral port pins.
- */
-static unsigned short atapi_io_port[] = {
- P_ATAPI_RESET,
- P_ATAPI_DIOR,
- P_ATAPI_DIOW,
- P_ATAPI_CS0,
- P_ATAPI_CS1,
- P_ATAPI_DMACK,
- P_ATAPI_DMARQ,
- P_ATAPI_INTRQ,
- P_ATAPI_IORDY,
- P_ATAPI_D0A,
- P_ATAPI_D1A,
- P_ATAPI_D2A,
- P_ATAPI_D3A,
- P_ATAPI_D4A,
- P_ATAPI_D5A,
- P_ATAPI_D6A,
- P_ATAPI_D7A,
- P_ATAPI_D8A,
- P_ATAPI_D9A,
- P_ATAPI_D10A,
- P_ATAPI_D11A,
- P_ATAPI_D12A,
- P_ATAPI_D13A,
- P_ATAPI_D14A,
- P_ATAPI_D15A,
- P_ATAPI_A0A,
- P_ATAPI_A1A,
- P_ATAPI_A2A,
- 0
-};
-
-/**
- * bfin_atapi_probe - attach a bfin atapi interface
- * @pdev: platform device
- *
- * Register a bfin atapi interface.
- *
- *
- * Platform devices are expected to contain 2 resources per port:
- *
- * - I/O Base (IORESOURCE_IO)
- * - IRQ (IORESOURCE_IRQ)
- *
- */
-static int bfin_atapi_probe(struct platform_device *pdev)
-{
- int board_idx = 0;
- struct resource *res;
- struct ata_host *host;
- unsigned int fsclk = get_sclk();
- int udma_mode = 5;
- const struct ata_port_info *ppi[] =
- { &bfin_port_info[board_idx], NULL };
-
- /*
- * Simple resource validation ..
- */
- if (unlikely(pdev->num_resources != 2)) {
- dev_err(&pdev->dev, "invalid number of resources\n");
- return -EINVAL;
- }
-
- /*
- * Get the register base first
- */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL)
- return -EINVAL;
-
- while (bfin_port_info[board_idx].udma_mask > 0 &&
- udma_fsclk[udma_mode] > fsclk) {
- udma_mode--;
- bfin_port_info[board_idx].udma_mask >>= 1;
- }
-
- /*
- * Now that that's out of the way, wire up the port..
- */
- host = ata_host_alloc_pinfo(&pdev->dev, ppi, 1);
- if (!host)
- return -ENOMEM;
-
- host->ports[0]->ioaddr.ctl_addr = (void *)res->start;
-
- if (peripheral_request_list(atapi_io_port, "atapi-io-port")) {
- dev_err(&pdev->dev, "Requesting Peripherals failed\n");
- return -EFAULT;
- }
-
- if (bfin_reset_controller(host)) {
- peripheral_free_list(atapi_io_port);
- dev_err(&pdev->dev, "Fail to reset ATAPI device\n");
- return -EFAULT;
- }
-
- if (ata_host_activate(host, platform_get_irq(pdev, 0),
- bfin_ata_interrupt, IRQF_SHARED, &bfin_sht) != 0) {
- peripheral_free_list(atapi_io_port);
- dev_err(&pdev->dev, "Fail to attach ATAPI device\n");
- return -ENODEV;
- }
-
- return 0;
-}
-
-/**
- * bfin_atapi_remove - unplug a bfin atapi interface
- * @pdev: platform device
- *
- * A bfin atapi device has been unplugged. Perform the needed
- * cleanup. Also called on module unload for any active devices.
- */
-static int bfin_atapi_remove(struct platform_device *pdev)
-{
- struct ata_host *host = platform_get_drvdata(pdev);
-
- ata_host_detach(host);
-
- peripheral_free_list(atapi_io_port);
-
- return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int bfin_atapi_suspend(struct platform_device *pdev, pm_message_t state)
-{
- struct ata_host *host = platform_get_drvdata(pdev);
- if (host)
- return ata_host_suspend(host, state);
- else
- return 0;
-}
-
-static int bfin_atapi_resume(struct platform_device *pdev)
-{
- struct ata_host *host = platform_get_drvdata(pdev);
- int ret;
-
- if (host) {
- ret = bfin_reset_controller(host);
- if (ret) {
- printk(KERN_ERR DRV_NAME ": Error during HW init\n");
- return ret;
- }
- ata_host_resume(host);
- }
-
- return 0;
-}
-#else
-#define bfin_atapi_suspend NULL
-#define bfin_atapi_resume NULL
-#endif
-
-static struct platform_driver bfin_atapi_driver = {
- .probe = bfin_atapi_probe,
- .remove = bfin_atapi_remove,
- .suspend = bfin_atapi_suspend,
- .resume = bfin_atapi_resume,
- .driver = {
- .name = DRV_NAME,
- },
-};
-
-#define ATAPI_MODE_SIZE 10
-static char bfin_atapi_mode[ATAPI_MODE_SIZE];
-
-static int __init bfin_atapi_init(void)
-{
- pr_info("register bfin atapi driver\n");
-
- switch(bfin_atapi_mode[0]) {
- case 'p':
- case 'P':
- break;
- case 'm':
- case 'M':
- bfin_port_info[0].mwdma_mask = ATA_MWDMA2;
- break;
- default:
- bfin_port_info[0].udma_mask = ATA_UDMA5;
- };
-
- return platform_driver_register(&bfin_atapi_driver);
-}
-
-static void __exit bfin_atapi_exit(void)
-{
- platform_driver_unregister(&bfin_atapi_driver);
-}
-
-module_init(bfin_atapi_init);
-module_exit(bfin_atapi_exit);
-/*
- * ATAPI mode:
- * pio/PIO
- * udma/UDMA (default)
- * mwdma/MWDMA
- */
-module_param_string(bfin_atapi_mode, bfin_atapi_mode, ATAPI_MODE_SIZE, 0);
-
-MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
-MODULE_DESCRIPTION("PATA driver for blackfin 54x ATAPI controller");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(DRV_VERSION);
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/ata/pata_bk3710.c b/drivers/ata/pata_bk3710.c
index 6c3bd5fae3e4..fad95cfecced 100644
--- a/drivers/ata/pata_bk3710.c
+++ b/drivers/ata/pata_bk3710.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0
+
/*
* Palmchip BK3710 PATA controller driver
*
@@ -8,10 +10,6 @@
*
* Copyright (C) 2006 Texas Instruments.
* Copyright (C) 2007 MontaVista Software, Inc., <source@mvista.com>
- *
- * 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/ata.h>
@@ -379,4 +377,4 @@ static int __init pata_bk3710_init(void)
}
module_init(pata_bk3710_init);
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/ata/pata_falcon.c b/drivers/ata/pata_falcon.c
index 5b0c57d1c59f..41e0d6a6cd05 100644
--- a/drivers/ata/pata_falcon.c
+++ b/drivers/ata/pata_falcon.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0
+
/*
* Atari Falcon PATA controller driver
*
@@ -7,10 +9,6 @@
* Based on falconide.c:
*
* Created 12 Jul 1997 by Geert Uytterhoeven
- *
- * 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/kernel.h>
@@ -180,5 +178,5 @@ module_init(pata_falcon_init_one);
MODULE_AUTHOR("Bartlomiej Zolnierkiewicz");
MODULE_DESCRIPTION("low-level driver for Atari Falcon PATA");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/ata/pata_gayle.c b/drivers/ata/pata_gayle.c
new file mode 100644
index 000000000000..65bc9f3042ce
--- /dev/null
+++ b/drivers/ata/pata_gayle.c
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Amiga Gayle PATA controller driver
+ *
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Based on gayle.c:
+ *
+ * Created 12 Jul 1997 by Geert Uytterhoeven
+ */
+
+#include <linux/ata.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/libata.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/zorro.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_host.h>
+
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+#include <asm/amigayle.h>
+#include <asm/ide.h>
+#include <asm/setup.h>
+
+#define DRV_NAME "pata_gayle"
+#define DRV_VERSION "0.1.0"
+
+#define GAYLE_CONTROL 0x101a
+
+static struct scsi_host_template pata_gayle_sht = {
+ ATA_PIO_SHT(DRV_NAME),
+};
+
+/* FIXME: is this needed? */
+static unsigned int pata_gayle_data_xfer(struct ata_queued_cmd *qc,
+ unsigned char *buf,
+ unsigned int buflen, int rw)
+{
+ struct ata_device *dev = qc->dev;
+ struct ata_port *ap = dev->link->ap;
+ void __iomem *data_addr = ap->ioaddr.data_addr;
+ unsigned int words = buflen >> 1;
+
+ /* Transfer multiple of 2 bytes */
+ if (rw == READ)
+ raw_insw((u16 *)data_addr, (u16 *)buf, words);
+ else
+ raw_outsw((u16 *)data_addr, (u16 *)buf, words);
+
+ /* Transfer trailing byte, if any. */
+ if (unlikely(buflen & 0x01)) {
+ unsigned char pad[2] = { };
+
+ /* Point buf to the tail of buffer */
+ buf += buflen - 1;
+
+ if (rw == READ) {
+ raw_insw((u16 *)data_addr, (u16 *)pad, 1);
+ *buf = pad[0];
+ } else {
+ pad[0] = *buf;
+ raw_outsw((u16 *)data_addr, (u16 *)pad, 1);
+ }
+ words++;
+ }
+
+ return words << 1;
+}
+
+/*
+ * Provide our own set_mode() as we don't want to change anything that has
+ * already been configured..
+ */
+static int pata_gayle_set_mode(struct ata_link *link,
+ struct ata_device **unused)
+{
+ struct ata_device *dev;
+
+ ata_for_each_dev(dev, link, ENABLED) {
+ /* We don't really care */
+ dev->pio_mode = dev->xfer_mode = XFER_PIO_0;
+ dev->xfer_shift = ATA_SHIFT_PIO;
+ dev->flags |= ATA_DFLAG_PIO;
+ ata_dev_info(dev, "configured for PIO\n");
+ }
+ return 0;
+}
+
+static bool pata_gayle_irq_check(struct ata_port *ap)
+{
+ u8 ch;
+
+ ch = z_readb((unsigned long)ap->private_data);
+
+ return !!(ch & GAYLE_IRQ_IDE);
+}
+
+static void pata_gayle_irq_clear(struct ata_port *ap)
+{
+ (void)z_readb((unsigned long)ap->ioaddr.status_addr);
+ z_writeb(0x7c, (unsigned long)ap->private_data);
+}
+
+static struct ata_port_operations pata_gayle_a1200_ops = {
+ .inherits = &ata_sff_port_ops,
+ .sff_data_xfer = pata_gayle_data_xfer,
+ .sff_irq_check = pata_gayle_irq_check,
+ .sff_irq_clear = pata_gayle_irq_clear,
+ .cable_detect = ata_cable_unknown,
+ .set_mode = pata_gayle_set_mode,
+};
+
+static struct ata_port_operations pata_gayle_a4000_ops = {
+ .inherits = &ata_sff_port_ops,
+ .sff_data_xfer = pata_gayle_data_xfer,
+ .cable_detect = ata_cable_unknown,
+ .set_mode = pata_gayle_set_mode,
+};
+
+static int __init pata_gayle_init_one(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct gayle_ide_platform_data *pdata;
+ struct ata_host *host;
+ struct ata_port *ap;
+ void __iomem *base;
+ int ret;
+
+ pdata = dev_get_platdata(&pdev->dev);
+
+ dev_info(&pdev->dev, "Amiga Gayle IDE controller (A%u style)\n",
+ pdata->explicit_ack ? 1200 : 4000);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ if (!devm_request_mem_region(&pdev->dev, res->start,
+ resource_size(res), DRV_NAME)) {
+ pr_err(DRV_NAME ": resources busy\n");
+ return -EBUSY;
+ }
+
+ /* allocate host */
+ host = ata_host_alloc(&pdev->dev, 1);
+ if (!host)
+ return -ENOMEM;
+
+ ap = host->ports[0];
+
+ if (pdata->explicit_ack)
+ ap->ops = &pata_gayle_a1200_ops;
+ else
+ ap->ops = &pata_gayle_a4000_ops;
+
+ ap->pio_mask = ATA_PIO4;
+ ap->flags |= ATA_FLAG_SLAVE_POSS | ATA_FLAG_NO_IORDY;
+
+ base = ZTWO_VADDR(pdata->base);
+ ap->ioaddr.data_addr = base;
+ ap->ioaddr.error_addr = base + 2 + 1 * 4;
+ ap->ioaddr.feature_addr = base + 2 + 1 * 4;
+ ap->ioaddr.nsect_addr = base + 2 + 2 * 4;
+ ap->ioaddr.lbal_addr = base + 2 + 3 * 4;
+ ap->ioaddr.lbam_addr = base + 2 + 4 * 4;
+ ap->ioaddr.lbah_addr = base + 2 + 5 * 4;
+ ap->ioaddr.device_addr = base + 2 + 6 * 4;
+ ap->ioaddr.status_addr = base + 2 + 7 * 4;
+ ap->ioaddr.command_addr = base + 2 + 7 * 4;
+
+ ap->ioaddr.altstatus_addr = base + GAYLE_CONTROL;
+ ap->ioaddr.ctl_addr = base + GAYLE_CONTROL;
+
+ ap->private_data = (void *)ZTWO_VADDR(pdata->irqport);
+
+ ata_port_desc(ap, "cmd 0x%lx ctl 0x%lx", pdata->base,
+ pdata->base + GAYLE_CONTROL);
+
+ ret = ata_host_activate(host, IRQ_AMIGA_PORTS, ata_sff_interrupt,
+ IRQF_SHARED, &pata_gayle_sht);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, host);
+
+ return 0;
+}
+
+static int __exit pata_gayle_remove_one(struct platform_device *pdev)
+{
+ struct ata_host *host = platform_get_drvdata(pdev);
+
+ ata_host_detach(host);
+
+ return 0;
+}
+
+static struct platform_driver pata_gayle_driver = {
+ .remove = __exit_p(pata_gayle_remove_one),
+ .driver = {
+ .name = "amiga-gayle-ide",
+ },
+};
+
+module_platform_driver_probe(pata_gayle_driver, pata_gayle_init_one);
+
+MODULE_AUTHOR("Bartlomiej Zolnierkiewicz");
+MODULE_DESCRIPTION("low-level driver for Amiga Gayle PATA");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:amiga-gayle-ide");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/ata/pata_it821x.c b/drivers/ata/pata_it821x.c
index 8468b300193b..9bac79edbc2c 100644
--- a/drivers/ata/pata_it821x.c
+++ b/drivers/ata/pata_it821x.c
@@ -658,10 +658,10 @@ static u8 *it821x_firmware_command(struct ata_port *ap, u8 cmd, int len)
u8 status;
int n = 0;
u16 *buf = kmalloc(len, GFP_KERNEL);
- if (buf == NULL) {
- printk(KERN_ERR "it821x_firmware_command: Out of memory\n");
+
+ if (!buf)
return NULL;
- }
+
/* This isn't quite a normal ATA command as we are talking to the
firmware not the drives */
ap->ctl |= ATA_NIEN;
diff --git a/drivers/ata/pata_macio.c b/drivers/ata/pata_macio.c
index 0adcb40d2794..9588e685d994 100644
--- a/drivers/ata/pata_macio.c
+++ b/drivers/ata/pata_macio.c
@@ -1131,11 +1131,9 @@ static int pata_macio_attach(struct macio_dev *mdev,
/* Allocate and init private data structure */
priv = devm_kzalloc(&mdev->ofdev.dev,
sizeof(struct pata_macio_priv), GFP_KERNEL);
- if (priv == NULL) {
- dev_err(&mdev->ofdev.dev,
- "Failed to allocate private memory\n");
+ if (!priv)
return -ENOMEM;
- }
+
priv->node = of_node_get(mdev->ofdev.dev.of_node);
priv->mdev = mdev;
priv->dev = &mdev->ofdev.dev;
@@ -1277,11 +1275,9 @@ static int pata_macio_pci_attach(struct pci_dev *pdev,
/* Allocate and init private data structure */
priv = devm_kzalloc(&pdev->dev,
sizeof(struct pata_macio_priv), GFP_KERNEL);
- if (priv == NULL) {
- dev_err(&pdev->dev,
- "Failed to allocate private memory\n");
+ if (!priv)
return -ENOMEM;
- }
+
priv->node = of_node_get(np);
priv->pdev = pdev;
priv->dev = &pdev->dev;
diff --git a/drivers/ata/pata_mpc52xx.c b/drivers/ata/pata_mpc52xx.c
index 9730125530f6..f1d352d5f128 100644
--- a/drivers/ata/pata_mpc52xx.c
+++ b/drivers/ata/pata_mpc52xx.c
@@ -738,7 +738,6 @@ static int mpc52xx_ata_probe(struct platform_device *op)
/* Prepare our private structure */
priv = devm_kzalloc(&op->dev, sizeof(*priv), GFP_ATOMIC);
if (!priv) {
- dev_err(&op->dev, "error allocating private structure\n");
rv = -ENOMEM;
goto err1;
}
diff --git a/drivers/ata/pata_samsung_cf.c b/drivers/ata/pata_samsung_cf.c
index 50801c40b029..bb96dc35950d 100644
--- a/drivers/ata/pata_samsung_cf.c
+++ b/drivers/ata/pata_samsung_cf.c
@@ -505,10 +505,8 @@ static int __init pata_s3c_probe(struct platform_device *pdev)
cpu_type = platform_get_device_id(pdev)->driver_data;
info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
- if (!info) {
- dev_err(dev, "failed to allocate memory for device data\n");
+ if (!info)
return -ENOMEM;
- }
info->irq = platform_get_irq(pdev, 0);
diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c
index 6af4ec3c88c3..ce3d6674ef80 100644
--- a/drivers/ata/sata_dwc_460ex.c
+++ b/drivers/ata/sata_dwc_460ex.c
@@ -901,7 +901,6 @@ static int sata_dwc_port_start(struct ata_port *ap)
/* Allocate Port Struct */
hsdevp = kzalloc(sizeof(*hsdevp), GFP_KERNEL);
if (!hsdevp) {
- dev_err(ap->dev, "%s: kmalloc failed for hsdevp\n", __func__);
err = -ENOMEM;
goto CLEANUP;
}
diff --git a/drivers/atm/idt77252.c b/drivers/atm/idt77252.c
index 0277f36be85b..6e737142ceaa 100644
--- a/drivers/atm/idt77252.c
+++ b/drivers/atm/idt77252.c
@@ -3173,14 +3173,10 @@ static void init_sram(struct idt77252_dev *card)
(u32) 0xffffffff);
}
- writel((SAR_FBQ0_LOW << 28) | 0x00000000 | 0x00000000 |
- (SAR_FB_SIZE_0 / 48), SAR_REG_FBQS0);
- writel((SAR_FBQ1_LOW << 28) | 0x00000000 | 0x00000000 |
- (SAR_FB_SIZE_1 / 48), SAR_REG_FBQS1);
- writel((SAR_FBQ2_LOW << 28) | 0x00000000 | 0x00000000 |
- (SAR_FB_SIZE_2 / 48), SAR_REG_FBQS2);
- writel((SAR_FBQ3_LOW << 28) | 0x00000000 | 0x00000000 |
- (SAR_FB_SIZE_3 / 48), SAR_REG_FBQS3);
+ writel((SAR_FBQ0_LOW << 28) | (SAR_FB_SIZE_0 / 48), SAR_REG_FBQS0);
+ writel((SAR_FBQ1_LOW << 28) | (SAR_FB_SIZE_1 / 48), SAR_REG_FBQS1);
+ writel((SAR_FBQ2_LOW << 28) | (SAR_FB_SIZE_2 / 48), SAR_REG_FBQS2);
+ writel((SAR_FBQ3_LOW << 28) | (SAR_FB_SIZE_3 / 48), SAR_REG_FBQS3);
/* Initialize rate table */
for (i = 0; i < 256; i++) {
diff --git a/drivers/atm/iphase.c b/drivers/atm/iphase.c
index 98a3a43484c8..be076606d30e 100644
--- a/drivers/atm/iphase.c
+++ b/drivers/atm/iphase.c
@@ -671,7 +671,7 @@ static void ia_tx_poll (IADEV *iadev) {
if ((vcc->pop) && (skb1->len != 0))
{
vcc->pop(vcc, skb1);
- IF_EVENT(printk("Tansmit Done - skb 0x%lx return\n",
+ IF_EVENT(printk("Transmit Done - skb 0x%lx return\n",
(long)skb1);)
}
else
@@ -1665,7 +1665,7 @@ static void tx_intr(struct atm_dev *dev)
status = readl(iadev->seg_reg+SEG_INTR_STATUS_REG);
if (status & TRANSMIT_DONE){
- IF_EVENT(printk("Tansmit Done Intr logic run\n");)
+ IF_EVENT(printk("Transmit Done Intr logic run\n");)
spin_lock_irqsave(&iadev->tx_lock, flags);
ia_tx_poll(iadev);
spin_unlock_irqrestore(&iadev->tx_lock, flags);
@@ -3147,7 +3147,7 @@ static int ia_proc_read(struct atm_dev *dev,loff_t *pos,char *page)
" Size of Tx Buffer : %u\n"
" Number of Rx Buffer: %u\n"
" Size of Rx Buffer : %u\n"
- " Packets Receiverd : %u\n"
+ " Packets Received : %u\n"
" Packets Transmitted: %u\n"
" Cells Received : %u\n"
" Cells Transmitted : %u\n"
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index e32a52490051..b074f242a435 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -5,14 +5,15 @@ obj-y := component.o core.o bus.o dd.o syscore.o \
driver.o class.o platform.o \
cpu.o firmware.o init.o map.o devres.o \
attribute_container.o transport_class.o \
- topology.o container.o property.o cacheinfo.o
+ topology.o container.o property.o cacheinfo.o \
+ devcon.o
obj-$(CONFIG_DEVTMPFS) += devtmpfs.o
obj-$(CONFIG_DMA_CMA) += dma-contiguous.o
obj-y += power/
obj-$(CONFIG_HAS_DMA) += dma-mapping.o
obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o
obj-$(CONFIG_ISA_BUS_API) += isa.o
-obj-$(CONFIG_FW_LOADER) += firmware_class.o
+obj-y += firmware_loader/
obj-$(CONFIG_NUMA) += node.o
obj-$(CONFIG_MEMORY_HOTPLUG_SPARSE) += memory.o
ifeq ($(CONFIG_SYSFS),y)
diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c
index 52ec5174bcb1..e7cb0c6ade81 100644
--- a/drivers/base/arch_topology.c
+++ b/drivers/base/arch_topology.c
@@ -169,11 +169,11 @@ bool __init topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu)
}
#ifdef CONFIG_CPU_FREQ
-static cpumask_var_t cpus_to_visit __initdata;
-static void __init parsing_done_workfn(struct work_struct *work);
-static __initdata DECLARE_WORK(parsing_done_work, parsing_done_workfn);
+static cpumask_var_t cpus_to_visit;
+static void parsing_done_workfn(struct work_struct *work);
+static DECLARE_WORK(parsing_done_work, parsing_done_workfn);
-static int __init
+static int
init_cpu_capacity_callback(struct notifier_block *nb,
unsigned long val,
void *data)
@@ -209,7 +209,7 @@ init_cpu_capacity_callback(struct notifier_block *nb,
return 0;
}
-static struct notifier_block init_cpu_capacity_notifier __initdata = {
+static struct notifier_block init_cpu_capacity_notifier = {
.notifier_call = init_cpu_capacity_callback,
};
@@ -242,7 +242,7 @@ static int __init register_cpufreq_notifier(void)
}
core_initcall(register_cpufreq_notifier);
-static void __init parsing_done_workfn(struct work_struct *work)
+static void parsing_done_workfn(struct work_struct *work)
{
cpufreq_unregister_notifier(&init_cpu_capacity_notifier,
CPUFREQ_POLICY_NOTIFIER);
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 5847364f25d9..b610816eb887 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -196,8 +196,10 @@ struct device_link *device_link_add(struct device *consumer,
}
list_for_each_entry(link, &supplier->links.consumers, s_node)
- if (link->consumer == consumer)
+ if (link->consumer == consumer) {
+ kref_get(&link->kref);
goto out;
+ }
link = kzalloc(sizeof(*link), GFP_KERNEL);
if (!link)
@@ -222,6 +224,7 @@ struct device_link *device_link_add(struct device *consumer,
link->consumer = consumer;
INIT_LIST_HEAD(&link->c_node);
link->flags = flags;
+ kref_init(&link->kref);
/* Determine the initial link state. */
if (flags & DL_FLAG_STATELESS) {
@@ -292,8 +295,10 @@ static void __device_link_free_srcu(struct rcu_head *rhead)
device_link_free(container_of(rhead, struct device_link, rcu_head));
}
-static void __device_link_del(struct device_link *link)
+static void __device_link_del(struct kref *kref)
{
+ struct device_link *link = container_of(kref, struct device_link, kref);
+
dev_info(link->consumer, "Dropping the link to %s\n",
dev_name(link->supplier));
@@ -305,8 +310,10 @@ static void __device_link_del(struct device_link *link)
call_srcu(&device_links_srcu, &link->rcu_head, __device_link_free_srcu);
}
#else /* !CONFIG_SRCU */
-static void __device_link_del(struct device_link *link)
+static void __device_link_del(struct kref *kref)
{
+ struct device_link *link = container_of(kref, struct device_link, kref);
+
dev_info(link->consumer, "Dropping the link to %s\n",
dev_name(link->supplier));
@@ -324,13 +331,15 @@ static void __device_link_del(struct device_link *link)
* @link: Device link to delete.
*
* The caller must ensure proper synchronization of this function with runtime
- * PM.
+ * PM. If the link was added multiple times, it needs to be deleted as often.
+ * Care is required for hotplugged devices: Their links are purged on removal
+ * and calling device_link_del() is then no longer allowed.
*/
void device_link_del(struct device_link *link)
{
device_links_write_lock();
device_pm_lock();
- __device_link_del(link);
+ kref_put(&link->kref, __device_link_del);
device_pm_unlock();
device_links_write_unlock();
}
@@ -444,7 +453,7 @@ static void __device_links_no_driver(struct device *dev)
continue;
if (link->flags & DL_FLAG_AUTOREMOVE)
- __device_link_del(link);
+ kref_put(&link->kref, __device_link_del);
else if (link->status != DL_STATE_SUPPLIER_UNBIND)
WRITE_ONCE(link->status, DL_STATE_AVAILABLE);
}
@@ -597,13 +606,13 @@ static void device_links_purge(struct device *dev)
list_for_each_entry_safe_reverse(link, ln, &dev->links.suppliers, c_node) {
WARN_ON(link->status == DL_STATE_ACTIVE);
- __device_link_del(link);
+ __device_link_del(&link->kref);
}
list_for_each_entry_safe_reverse(link, ln, &dev->links.consumers, s_node) {
WARN_ON(link->status != DL_STATE_DORMANT &&
link->status != DL_STATE_NONE);
- __device_link_del(link);
+ __device_link_del(&link->kref);
}
device_links_write_unlock();
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index d21a2d913107..2da998baa75c 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -382,8 +382,10 @@ int register_cpu(struct cpu *cpu, int num)
if (cpu->hotpluggable)
cpu->dev.groups = hotplugable_cpu_attr_groups;
error = device_register(&cpu->dev);
- if (error)
+ if (error) {
+ put_device(&cpu->dev);
return error;
+ }
per_cpu(cpu_sys_devices, num) = &cpu->dev;
register_cpu_under_node(num, cpu_to_node(num));
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index de6fd092bf2f..c9f54089429b 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -292,8 +292,7 @@ static ssize_t coredump_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
device_lock(dev);
- if (dev->driver->coredump)
- dev->driver->coredump(dev);
+ dev->driver->coredump(dev);
device_unlock(dev);
return count;
diff --git a/drivers/base/devcon.c b/drivers/base/devcon.c
new file mode 100644
index 000000000000..d427e806cd73
--- /dev/null
+++ b/drivers/base/devcon.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * Device connections
+ *
+ * Copyright (C) 2018 Intel Corporation
+ * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+ */
+
+#include <linux/device.h>
+
+static DEFINE_MUTEX(devcon_lock);
+static LIST_HEAD(devcon_list);
+
+/**
+ * device_connection_find_match - Find physical connection to a device
+ * @dev: Device with the connection
+ * @con_id: Identifier for the connection
+ * @data: Data for the match function
+ * @match: Function to check and convert the connection description
+ *
+ * Find a connection with unique identifier @con_id between @dev and another
+ * device. @match will be used to convert the connection description to data the
+ * caller is expecting to be returned.
+ */
+void *device_connection_find_match(struct device *dev, const char *con_id,
+ void *data,
+ void *(*match)(struct device_connection *con,
+ int ep, void *data))
+{
+ const char *devname = dev_name(dev);
+ struct device_connection *con;
+ void *ret = NULL;
+ int ep;
+
+ if (!match)
+ return NULL;
+
+ mutex_lock(&devcon_lock);
+
+ list_for_each_entry(con, &devcon_list, list) {
+ ep = match_string(con->endpoint, 2, devname);
+ if (ep < 0)
+ continue;
+
+ if (con_id && strcmp(con->id, con_id))
+ continue;
+
+ ret = match(con, !ep, data);
+ if (ret)
+ break;
+ }
+
+ mutex_unlock(&devcon_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(device_connection_find_match);
+
+extern struct bus_type platform_bus_type;
+extern struct bus_type pci_bus_type;
+extern struct bus_type i2c_bus_type;
+extern struct bus_type spi_bus_type;
+
+static struct bus_type *generic_match_buses[] = {
+ &platform_bus_type,
+#ifdef CONFIG_PCI
+ &pci_bus_type,
+#endif
+#ifdef CONFIG_I2C
+ &i2c_bus_type,
+#endif
+#ifdef CONFIG_SPI_MASTER
+ &spi_bus_type,
+#endif
+ NULL,
+};
+
+/* This tries to find the device from the most common bus types by name. */
+static void *generic_match(struct device_connection *con, int ep, void *data)
+{
+ struct bus_type *bus;
+ struct device *dev;
+
+ for (bus = generic_match_buses[0]; bus; bus++) {
+ dev = bus_find_device_by_name(bus, NULL, con->endpoint[ep]);
+ if (dev)
+ return dev;
+ }
+
+ /*
+ * We only get called if a connection was found, tell the caller to
+ * wait for the other device to show up.
+ */
+ return ERR_PTR(-EPROBE_DEFER);
+}
+
+/**
+ * device_connection_find - Find two devices connected together
+ * @dev: Device with the connection
+ * @con_id: Identifier for the connection
+ *
+ * Find a connection with unique identifier @con_id between @dev and
+ * another device. On success returns handle to the device that is connected
+ * to @dev, with the reference count for the found device incremented. Returns
+ * NULL if no matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a
+ * connection was found but the other device has not been enumerated yet.
+ */
+struct device *device_connection_find(struct device *dev, const char *con_id)
+{
+ return device_connection_find_match(dev, con_id, NULL, generic_match);
+}
+EXPORT_SYMBOL_GPL(device_connection_find);
+
+/**
+ * device_connection_add - Register a connection description
+ * @con: The connection description to be registered
+ */
+void device_connection_add(struct device_connection *con)
+{
+ mutex_lock(&devcon_lock);
+ list_add_tail(&con->list, &devcon_list);
+ mutex_unlock(&devcon_lock);
+}
+EXPORT_SYMBOL_GPL(device_connection_add);
+
+/**
+ * device_connections_remove - Unregister connection description
+ * @con: The connection description to be unregistered
+ */
+void device_connection_remove(struct device_connection *con)
+{
+ mutex_lock(&devcon_lock);
+ list_del(&con->list);
+ mutex_unlock(&devcon_lock);
+}
+EXPORT_SYMBOL_GPL(device_connection_remove);
diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c
index 50025d7959cb..f7768077e817 100644
--- a/drivers/base/devtmpfs.c
+++ b/drivers/base/devtmpfs.c
@@ -356,7 +356,8 @@ int devtmpfs_mount(const char *mntdir)
if (!thread)
return 0;
- err = sys_mount("devtmpfs", (char *)mntdir, "devtmpfs", MS_SILENT, NULL);
+ err = ksys_mount("devtmpfs", (char *)mntdir, "devtmpfs", MS_SILENT,
+ NULL);
if (err)
printk(KERN_INFO "devtmpfs: error mounting %i\n", err);
else
@@ -379,14 +380,14 @@ static int devtmpfsd(void *p)
{
char options[] = "mode=0755";
int *err = p;
- *err = sys_unshare(CLONE_NEWNS);
+ *err = ksys_unshare(CLONE_NEWNS);
if (*err)
goto out;
- *err = sys_mount("devtmpfs", "/", "devtmpfs", MS_SILENT, options);
+ *err = ksys_mount("devtmpfs", "/", "devtmpfs", MS_SILENT, options);
if (*err)
goto out;
- sys_chdir("/.."); /* will traverse into overmounted root */
- sys_chroot(".");
+ ksys_chdir("/.."); /* will traverse into overmounted root */
+ ksys_chroot(".");
complete(&setup_done);
while (1) {
spin_lock(&req_lock);
diff --git a/drivers/base/dma-coherent.c b/drivers/base/dma-coherent.c
index 1e6396bb807b..597d40893862 100644
--- a/drivers/base/dma-coherent.c
+++ b/drivers/base/dma-coherent.c
@@ -312,8 +312,9 @@ static int __dma_mmap_from_coherent(struct dma_coherent_mem *mem,
* This checks whether the memory was allocated from the per-device
* coherent memory pool and if so, maps that memory to the provided vma.
*
- * Returns 1 if we correctly mapped the memory, or 0 if the caller should
- * proceed with mapping memory from generic pools.
+ * Returns 1 if @vaddr belongs to the device coherent pool and the caller
+ * should return @ret, or 0 if they should proceed with mapping memory from
+ * generic areas.
*/
int dma_mmap_from_dev_coherent(struct device *dev, struct vm_area_struct *vma,
void *vaddr, size_t size, int *ret)
diff --git a/drivers/base/dma-mapping.c b/drivers/base/dma-mapping.c
index 3b118353ea17..d82566d6e237 100644
--- a/drivers/base/dma-mapping.c
+++ b/drivers/base/dma-mapping.c
@@ -226,7 +226,6 @@ int dma_common_mmap(struct device *dev, struct vm_area_struct *vma,
#ifndef CONFIG_ARCH_NO_COHERENT_DMA_MMAP
unsigned long user_count = vma_pages(vma);
unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT;
- unsigned long pfn = page_to_pfn(virt_to_page(cpu_addr));
unsigned long off = vma->vm_pgoff;
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
@@ -234,12 +233,11 @@ int dma_common_mmap(struct device *dev, struct vm_area_struct *vma,
if (dma_mmap_from_dev_coherent(dev, vma, cpu_addr, size, &ret))
return ret;
- if (off < count && user_count <= (count - off)) {
+ if (off < count && user_count <= (count - off))
ret = remap_pfn_range(vma, vma->vm_start,
- pfn + off,
+ page_to_pfn(virt_to_page(cpu_addr)) + off,
user_count << PAGE_SHIFT,
vma->vm_page_prot);
- }
#endif /* !CONFIG_ARCH_NO_COHERENT_DMA_MMAP */
return ret;
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
deleted file mode 100644
index 7dd36ace6152..000000000000
--- a/drivers/base/firmware_class.c
+++ /dev/null
@@ -1,1940 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * firmware_class.c - Multi purpose firmware loading support
- *
- * Copyright (c) 2003 Manuel Estrada Sainz
- *
- * Please see Documentation/firmware_class/ for more information.
- *
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/capability.h>
-#include <linux/device.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/timer.h>
-#include <linux/vmalloc.h>
-#include <linux/interrupt.h>
-#include <linux/bitops.h>
-#include <linux/mutex.h>
-#include <linux/workqueue.h>
-#include <linux/highmem.h>
-#include <linux/firmware.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/file.h>
-#include <linux/list.h>
-#include <linux/fs.h>
-#include <linux/async.h>
-#include <linux/pm.h>
-#include <linux/suspend.h>
-#include <linux/syscore_ops.h>
-#include <linux/reboot.h>
-#include <linux/security.h>
-
-#include <generated/utsrelease.h>
-
-#include "base.h"
-
-MODULE_AUTHOR("Manuel Estrada Sainz");
-MODULE_DESCRIPTION("Multi purpose firmware loading support");
-MODULE_LICENSE("GPL");
-
-enum fw_status {
- FW_STATUS_UNKNOWN,
- FW_STATUS_LOADING,
- FW_STATUS_DONE,
- FW_STATUS_ABORTED,
-};
-
-/*
- * Concurrent request_firmware() for the same firmware need to be
- * serialized. struct fw_state is simple state machine which hold the
- * state of the firmware loading.
- */
-struct fw_state {
- struct completion completion;
- enum fw_status status;
-};
-
-/* firmware behavior options */
-#define FW_OPT_UEVENT (1U << 0)
-#define FW_OPT_NOWAIT (1U << 1)
-#define FW_OPT_USERHELPER (1U << 2)
-#define FW_OPT_NO_WARN (1U << 3)
-#define FW_OPT_NOCACHE (1U << 4)
-#define FW_OPT_NOFALLBACK (1U << 5)
-
-struct firmware_cache {
- /* firmware_buf instance will be added into the below list */
- spinlock_t lock;
- struct list_head head;
- int state;
-
-#ifdef CONFIG_PM_SLEEP
- /*
- * Names of firmware images which have been cached successfully
- * will be added into the below list so that device uncache
- * helper can trace which firmware images have been cached
- * before.
- */
- spinlock_t name_lock;
- struct list_head fw_names;
-
- struct delayed_work work;
-
- struct notifier_block pm_notify;
-#endif
-};
-
-struct fw_priv {
- struct kref ref;
- struct list_head list;
- struct firmware_cache *fwc;
- struct fw_state fw_st;
- void *data;
- size_t size;
- size_t allocated_size;
-#ifdef CONFIG_FW_LOADER_USER_HELPER
- bool is_paged_buf;
- bool need_uevent;
- struct page **pages;
- int nr_pages;
- int page_array_size;
- struct list_head pending_list;
-#endif
- const char *fw_name;
-};
-
-struct fw_cache_entry {
- struct list_head list;
- const char *name;
-};
-
-struct fw_name_devm {
- unsigned long magic;
- const char *name;
-};
-
-static inline struct fw_priv *to_fw_priv(struct kref *ref)
-{
- return container_of(ref, struct fw_priv, ref);
-}
-
-#define FW_LOADER_NO_CACHE 0
-#define FW_LOADER_START_CACHE 1
-
-/* fw_lock could be moved to 'struct fw_sysfs' but since it is just
- * guarding for corner cases a global lock should be OK */
-static DEFINE_MUTEX(fw_lock);
-
-static struct firmware_cache fw_cache;
-
-/* Builtin firmware support */
-
-#ifdef CONFIG_FW_LOADER
-
-extern struct builtin_fw __start_builtin_fw[];
-extern struct builtin_fw __end_builtin_fw[];
-
-static void fw_copy_to_prealloc_buf(struct firmware *fw,
- void *buf, size_t size)
-{
- if (!buf || size < fw->size)
- return;
- memcpy(buf, fw->data, fw->size);
-}
-
-static bool fw_get_builtin_firmware(struct firmware *fw, const char *name,
- void *buf, size_t size)
-{
- struct builtin_fw *b_fw;
-
- for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++) {
- if (strcmp(name, b_fw->name) == 0) {
- fw->size = b_fw->size;
- fw->data = b_fw->data;
- fw_copy_to_prealloc_buf(fw, buf, size);
-
- return true;
- }
- }
-
- return false;
-}
-
-static bool fw_is_builtin_firmware(const struct firmware *fw)
-{
- struct builtin_fw *b_fw;
-
- for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++)
- if (fw->data == b_fw->data)
- return true;
-
- return false;
-}
-
-#else /* Module case - no builtin firmware support */
-
-static inline bool fw_get_builtin_firmware(struct firmware *fw,
- const char *name, void *buf,
- size_t size)
-{
- return false;
-}
-
-static inline bool fw_is_builtin_firmware(const struct firmware *fw)
-{
- return false;
-}
-#endif
-
-static int loading_timeout = 60; /* In seconds */
-
-static inline long firmware_loading_timeout(void)
-{
- return loading_timeout > 0 ? loading_timeout * HZ : MAX_JIFFY_OFFSET;
-}
-
-static void fw_state_init(struct fw_priv *fw_priv)
-{
- struct fw_state *fw_st = &fw_priv->fw_st;
-
- init_completion(&fw_st->completion);
- fw_st->status = FW_STATUS_UNKNOWN;
-}
-
-static int __fw_state_wait_common(struct fw_priv *fw_priv, long timeout)
-{
- struct fw_state *fw_st = &fw_priv->fw_st;
- long ret;
-
- ret = wait_for_completion_killable_timeout(&fw_st->completion, timeout);
- if (ret != 0 && fw_st->status == FW_STATUS_ABORTED)
- return -ENOENT;
- if (!ret)
- return -ETIMEDOUT;
-
- return ret < 0 ? ret : 0;
-}
-
-static void __fw_state_set(struct fw_priv *fw_priv,
- enum fw_status status)
-{
- struct fw_state *fw_st = &fw_priv->fw_st;
-
- WRITE_ONCE(fw_st->status, status);
-
- if (status == FW_STATUS_DONE || status == FW_STATUS_ABORTED)
- complete_all(&fw_st->completion);
-}
-
-static inline void fw_state_start(struct fw_priv *fw_priv)
-{
- __fw_state_set(fw_priv, FW_STATUS_LOADING);
-}
-
-static inline void fw_state_done(struct fw_priv *fw_priv)
-{
- __fw_state_set(fw_priv, FW_STATUS_DONE);
-}
-
-static inline void fw_state_aborted(struct fw_priv *fw_priv)
-{
- __fw_state_set(fw_priv, FW_STATUS_ABORTED);
-}
-
-static inline int fw_state_wait(struct fw_priv *fw_priv)
-{
- return __fw_state_wait_common(fw_priv, MAX_SCHEDULE_TIMEOUT);
-}
-
-static bool __fw_state_check(struct fw_priv *fw_priv,
- enum fw_status status)
-{
- struct fw_state *fw_st = &fw_priv->fw_st;
-
- return fw_st->status == status;
-}
-
-static inline bool fw_state_is_aborted(struct fw_priv *fw_priv)
-{
- return __fw_state_check(fw_priv, FW_STATUS_ABORTED);
-}
-
-#ifdef CONFIG_FW_LOADER_USER_HELPER
-
-static inline bool fw_sysfs_done(struct fw_priv *fw_priv)
-{
- return __fw_state_check(fw_priv, FW_STATUS_DONE);
-}
-
-static inline bool fw_sysfs_loading(struct fw_priv *fw_priv)
-{
- return __fw_state_check(fw_priv, FW_STATUS_LOADING);
-}
-
-static inline int fw_sysfs_wait_timeout(struct fw_priv *fw_priv, long timeout)
-{
- return __fw_state_wait_common(fw_priv, timeout);
-}
-
-#endif /* CONFIG_FW_LOADER_USER_HELPER */
-
-static int fw_cache_piggyback_on_request(const char *name);
-
-static struct fw_priv *__allocate_fw_priv(const char *fw_name,
- struct firmware_cache *fwc,
- void *dbuf, size_t size)
-{
- struct fw_priv *fw_priv;
-
- fw_priv = kzalloc(sizeof(*fw_priv), GFP_ATOMIC);
- if (!fw_priv)
- return NULL;
-
- fw_priv->fw_name = kstrdup_const(fw_name, GFP_ATOMIC);
- if (!fw_priv->fw_name) {
- kfree(fw_priv);
- return NULL;
- }
-
- kref_init(&fw_priv->ref);
- fw_priv->fwc = fwc;
- fw_priv->data = dbuf;
- fw_priv->allocated_size = size;
- fw_state_init(fw_priv);
-#ifdef CONFIG_FW_LOADER_USER_HELPER
- INIT_LIST_HEAD(&fw_priv->pending_list);
-#endif
-
- pr_debug("%s: fw-%s fw_priv=%p\n", __func__, fw_name, fw_priv);
-
- return fw_priv;
-}
-
-static struct fw_priv *__lookup_fw_priv(const char *fw_name)
-{
- struct fw_priv *tmp;
- struct firmware_cache *fwc = &fw_cache;
-
- list_for_each_entry(tmp, &fwc->head, list)
- if (!strcmp(tmp->fw_name, fw_name))
- return tmp;
- return NULL;
-}
-
-/* Returns 1 for batching firmware requests with the same name */
-static int alloc_lookup_fw_priv(const char *fw_name,
- struct firmware_cache *fwc,
- struct fw_priv **fw_priv, void *dbuf,
- size_t size)
-{
- struct fw_priv *tmp;
-
- spin_lock(&fwc->lock);
- tmp = __lookup_fw_priv(fw_name);
- if (tmp) {
- kref_get(&tmp->ref);
- spin_unlock(&fwc->lock);
- *fw_priv = tmp;
- pr_debug("batched request - sharing the same struct fw_priv and lookup for multiple requests\n");
- return 1;
- }
- tmp = __allocate_fw_priv(fw_name, fwc, dbuf, size);
- if (tmp)
- list_add(&tmp->list, &fwc->head);
- spin_unlock(&fwc->lock);
-
- *fw_priv = tmp;
-
- return tmp ? 0 : -ENOMEM;
-}
-
-static void __free_fw_priv(struct kref *ref)
- __releases(&fwc->lock)
-{
- struct fw_priv *fw_priv = to_fw_priv(ref);
- struct firmware_cache *fwc = fw_priv->fwc;
-
- pr_debug("%s: fw-%s fw_priv=%p data=%p size=%u\n",
- __func__, fw_priv->fw_name, fw_priv, fw_priv->data,
- (unsigned int)fw_priv->size);
-
- list_del(&fw_priv->list);
- spin_unlock(&fwc->lock);
-
-#ifdef CONFIG_FW_LOADER_USER_HELPER
- if (fw_priv->is_paged_buf) {
- int i;
- vunmap(fw_priv->data);
- for (i = 0; i < fw_priv->nr_pages; i++)
- __free_page(fw_priv->pages[i]);
- vfree(fw_priv->pages);
- } else
-#endif
- if (!fw_priv->allocated_size)
- vfree(fw_priv->data);
- kfree_const(fw_priv->fw_name);
- kfree(fw_priv);
-}
-
-static void free_fw_priv(struct fw_priv *fw_priv)
-{
- struct firmware_cache *fwc = fw_priv->fwc;
- spin_lock(&fwc->lock);
- if (!kref_put(&fw_priv->ref, __free_fw_priv))
- spin_unlock(&fwc->lock);
-}
-
-/* direct firmware loading support */
-static char fw_path_para[256];
-static const char * const fw_path[] = {
- fw_path_para,
- "/lib/firmware/updates/" UTS_RELEASE,
- "/lib/firmware/updates",
- "/lib/firmware/" UTS_RELEASE,
- "/lib/firmware"
-};
-
-/*
- * Typical usage is that passing 'firmware_class.path=$CUSTOMIZED_PATH'
- * from kernel command line because firmware_class is generally built in
- * kernel instead of module.
- */
-module_param_string(path, fw_path_para, sizeof(fw_path_para), 0644);
-MODULE_PARM_DESC(path, "customized firmware image search path with a higher priority than default path");
-
-static int
-fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv)
-{
- loff_t size;
- int i, len;
- int rc = -ENOENT;
- char *path;
- enum kernel_read_file_id id = READING_FIRMWARE;
- size_t msize = INT_MAX;
-
- /* Already populated data member means we're loading into a buffer */
- if (fw_priv->data) {
- id = READING_FIRMWARE_PREALLOC_BUFFER;
- msize = fw_priv->allocated_size;
- }
-
- path = __getname();
- if (!path)
- return -ENOMEM;
-
- for (i = 0; i < ARRAY_SIZE(fw_path); i++) {
- /* skip the unset customized path */
- if (!fw_path[i][0])
- continue;
-
- len = snprintf(path, PATH_MAX, "%s/%s",
- fw_path[i], fw_priv->fw_name);
- if (len >= PATH_MAX) {
- rc = -ENAMETOOLONG;
- break;
- }
-
- fw_priv->size = 0;
- rc = kernel_read_file_from_path(path, &fw_priv->data, &size,
- msize, id);
- if (rc) {
- if (rc == -ENOENT)
- dev_dbg(device, "loading %s failed with error %d\n",
- path, rc);
- else
- dev_warn(device, "loading %s failed with error %d\n",
- path, rc);
- continue;
- }
- dev_dbg(device, "direct-loading %s\n", fw_priv->fw_name);
- fw_priv->size = size;
- fw_state_done(fw_priv);
- break;
- }
- __putname(path);
-
- return rc;
-}
-
-/* firmware holds the ownership of pages */
-static void firmware_free_data(const struct firmware *fw)
-{
- /* Loaded directly? */
- if (!fw->priv) {
- vfree(fw->data);
- return;
- }
- free_fw_priv(fw->priv);
-}
-
-/* store the pages buffer info firmware from buf */
-static void fw_set_page_data(struct fw_priv *fw_priv, struct firmware *fw)
-{
- fw->priv = fw_priv;
-#ifdef CONFIG_FW_LOADER_USER_HELPER
- fw->pages = fw_priv->pages;
-#endif
- fw->size = fw_priv->size;
- fw->data = fw_priv->data;
-
- pr_debug("%s: fw-%s fw_priv=%p data=%p size=%u\n",
- __func__, fw_priv->fw_name, fw_priv, fw_priv->data,
- (unsigned int)fw_priv->size);
-}
-
-#ifdef CONFIG_PM_SLEEP
-static void fw_name_devm_release(struct device *dev, void *res)
-{
- struct fw_name_devm *fwn = res;
-
- if (fwn->magic == (unsigned long)&fw_cache)
- pr_debug("%s: fw_name-%s devm-%p released\n",
- __func__, fwn->name, res);
- kfree_const(fwn->name);
-}
-
-static int fw_devm_match(struct device *dev, void *res,
- void *match_data)
-{
- struct fw_name_devm *fwn = res;
-
- return (fwn->magic == (unsigned long)&fw_cache) &&
- !strcmp(fwn->name, match_data);
-}
-
-static struct fw_name_devm *fw_find_devm_name(struct device *dev,
- const char *name)
-{
- struct fw_name_devm *fwn;
-
- fwn = devres_find(dev, fw_name_devm_release,
- fw_devm_match, (void *)name);
- return fwn;
-}
-
-/* add firmware name into devres list */
-static int fw_add_devm_name(struct device *dev, const char *name)
-{
- struct fw_name_devm *fwn;
-
- fwn = fw_find_devm_name(dev, name);
- if (fwn)
- return 1;
-
- fwn = devres_alloc(fw_name_devm_release, sizeof(struct fw_name_devm),
- GFP_KERNEL);
- if (!fwn)
- return -ENOMEM;
- fwn->name = kstrdup_const(name, GFP_KERNEL);
- if (!fwn->name) {
- devres_free(fwn);
- return -ENOMEM;
- }
-
- fwn->magic = (unsigned long)&fw_cache;
- devres_add(dev, fwn);
-
- return 0;
-}
-#else
-static int fw_add_devm_name(struct device *dev, const char *name)
-{
- return 0;
-}
-#endif
-
-static int assign_fw(struct firmware *fw, struct device *device,
- unsigned int opt_flags)
-{
- struct fw_priv *fw_priv = fw->priv;
-
- mutex_lock(&fw_lock);
- if (!fw_priv->size || fw_state_is_aborted(fw_priv)) {
- mutex_unlock(&fw_lock);
- return -ENOENT;
- }
-
- /*
- * add firmware name into devres list so that we can auto cache
- * and uncache firmware for device.
- *
- * device may has been deleted already, but the problem
- * should be fixed in devres or driver core.
- */
- /* don't cache firmware handled without uevent */
- if (device && (opt_flags & FW_OPT_UEVENT) &&
- !(opt_flags & FW_OPT_NOCACHE))
- fw_add_devm_name(device, fw_priv->fw_name);
-
- /*
- * After caching firmware image is started, let it piggyback
- * on request firmware.
- */
- if (!(opt_flags & FW_OPT_NOCACHE) &&
- fw_priv->fwc->state == FW_LOADER_START_CACHE) {
- if (fw_cache_piggyback_on_request(fw_priv->fw_name))
- kref_get(&fw_priv->ref);
- }
-
- /* pass the pages buffer to driver at the last minute */
- fw_set_page_data(fw_priv, fw);
- mutex_unlock(&fw_lock);
- return 0;
-}
-
-/*
- * user-mode helper code
- */
-#ifdef CONFIG_FW_LOADER_USER_HELPER
-struct fw_sysfs {
- bool nowait;
- struct device dev;
- struct fw_priv *fw_priv;
- struct firmware *fw;
-};
-
-static struct fw_sysfs *to_fw_sysfs(struct device *dev)
-{
- return container_of(dev, struct fw_sysfs, dev);
-}
-
-static void __fw_load_abort(struct fw_priv *fw_priv)
-{
- /*
- * There is a small window in which user can write to 'loading'
- * between loading done and disappearance of 'loading'
- */
- if (fw_sysfs_done(fw_priv))
- return;
-
- list_del_init(&fw_priv->pending_list);
- fw_state_aborted(fw_priv);
-}
-
-static void fw_load_abort(struct fw_sysfs *fw_sysfs)
-{
- struct fw_priv *fw_priv = fw_sysfs->fw_priv;
-
- __fw_load_abort(fw_priv);
-}
-
-static LIST_HEAD(pending_fw_head);
-
-static void kill_pending_fw_fallback_reqs(bool only_kill_custom)
-{
- struct fw_priv *fw_priv;
- struct fw_priv *next;
-
- mutex_lock(&fw_lock);
- list_for_each_entry_safe(fw_priv, next, &pending_fw_head,
- pending_list) {
- if (!fw_priv->need_uevent || !only_kill_custom)
- __fw_load_abort(fw_priv);
- }
- mutex_unlock(&fw_lock);
-}
-
-static ssize_t timeout_show(struct class *class, struct class_attribute *attr,
- char *buf)
-{
- return sprintf(buf, "%d\n", loading_timeout);
-}
-
-/**
- * firmware_timeout_store - set number of seconds to wait for firmware
- * @class: device class pointer
- * @attr: device attribute pointer
- * @buf: buffer to scan for timeout value
- * @count: number of bytes in @buf
- *
- * Sets the number of seconds to wait for the firmware. Once
- * this expires an error will be returned to the driver and no
- * firmware will be provided.
- *
- * Note: zero means 'wait forever'.
- **/
-static ssize_t timeout_store(struct class *class, struct class_attribute *attr,
- const char *buf, size_t count)
-{
- loading_timeout = simple_strtol(buf, NULL, 10);
- if (loading_timeout < 0)
- loading_timeout = 0;
-
- return count;
-}
-static CLASS_ATTR_RW(timeout);
-
-static struct attribute *firmware_class_attrs[] = {
- &class_attr_timeout.attr,
- NULL,
-};
-ATTRIBUTE_GROUPS(firmware_class);
-
-static void fw_dev_release(struct device *dev)
-{
- struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
-
- kfree(fw_sysfs);
-}
-
-static int do_firmware_uevent(struct fw_sysfs *fw_sysfs, struct kobj_uevent_env *env)
-{
- if (add_uevent_var(env, "FIRMWARE=%s", fw_sysfs->fw_priv->fw_name))
- return -ENOMEM;
- if (add_uevent_var(env, "TIMEOUT=%i", loading_timeout))
- return -ENOMEM;
- if (add_uevent_var(env, "ASYNC=%d", fw_sysfs->nowait))
- return -ENOMEM;
-
- return 0;
-}
-
-static int firmware_uevent(struct device *dev, struct kobj_uevent_env *env)
-{
- struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
- int err = 0;
-
- mutex_lock(&fw_lock);
- if (fw_sysfs->fw_priv)
- err = do_firmware_uevent(fw_sysfs, env);
- mutex_unlock(&fw_lock);
- return err;
-}
-
-static struct class firmware_class = {
- .name = "firmware",
- .class_groups = firmware_class_groups,
- .dev_uevent = firmware_uevent,
- .dev_release = fw_dev_release,
-};
-
-static inline int register_sysfs_loader(void)
-{
- return class_register(&firmware_class);
-}
-
-static inline void unregister_sysfs_loader(void)
-{
- class_unregister(&firmware_class);
-}
-
-static ssize_t firmware_loading_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
- int loading = 0;
-
- mutex_lock(&fw_lock);
- if (fw_sysfs->fw_priv)
- loading = fw_sysfs_loading(fw_sysfs->fw_priv);
- mutex_unlock(&fw_lock);
-
- return sprintf(buf, "%d\n", loading);
-}
-
-/* Some architectures don't have PAGE_KERNEL_RO */
-#ifndef PAGE_KERNEL_RO
-#define PAGE_KERNEL_RO PAGE_KERNEL
-#endif
-
-/* one pages buffer should be mapped/unmapped only once */
-static int map_fw_priv_pages(struct fw_priv *fw_priv)
-{
- if (!fw_priv->is_paged_buf)
- return 0;
-
- vunmap(fw_priv->data);
- fw_priv->data = vmap(fw_priv->pages, fw_priv->nr_pages, 0,
- PAGE_KERNEL_RO);
- if (!fw_priv->data)
- return -ENOMEM;
- return 0;
-}
-
-/**
- * firmware_loading_store - set value in the 'loading' control file
- * @dev: device pointer
- * @attr: device attribute pointer
- * @buf: buffer to scan for loading control value
- * @count: number of bytes in @buf
- *
- * The relevant values are:
- *
- * 1: Start a load, discarding any previous partial load.
- * 0: Conclude the load and hand the data to the driver code.
- * -1: Conclude the load with an error and discard any written data.
- **/
-static ssize_t firmware_loading_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
- struct fw_priv *fw_priv;
- ssize_t written = count;
- int loading = simple_strtol(buf, NULL, 10);
- int i;
-
- mutex_lock(&fw_lock);
- fw_priv = fw_sysfs->fw_priv;
- if (fw_state_is_aborted(fw_priv))
- goto out;
-
- switch (loading) {
- case 1:
- /* discarding any previous partial load */
- if (!fw_sysfs_done(fw_priv)) {
- for (i = 0; i < fw_priv->nr_pages; i++)
- __free_page(fw_priv->pages[i]);
- vfree(fw_priv->pages);
- fw_priv->pages = NULL;
- fw_priv->page_array_size = 0;
- fw_priv->nr_pages = 0;
- fw_state_start(fw_priv);
- }
- break;
- case 0:
- if (fw_sysfs_loading(fw_priv)) {
- int rc;
-
- /*
- * Several loading requests may be pending on
- * one same firmware buf, so let all requests
- * see the mapped 'buf->data' once the loading
- * is completed.
- * */
- rc = map_fw_priv_pages(fw_priv);
- if (rc)
- dev_err(dev, "%s: map pages failed\n",
- __func__);
- else
- rc = security_kernel_post_read_file(NULL,
- fw_priv->data, fw_priv->size,
- READING_FIRMWARE);
-
- /*
- * Same logic as fw_load_abort, only the DONE bit
- * is ignored and we set ABORT only on failure.
- */
- list_del_init(&fw_priv->pending_list);
- if (rc) {
- fw_state_aborted(fw_priv);
- written = rc;
- } else {
- fw_state_done(fw_priv);
- }
- break;
- }
- /* fallthrough */
- default:
- dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading);
- /* fallthrough */
- case -1:
- fw_load_abort(fw_sysfs);
- break;
- }
-out:
- mutex_unlock(&fw_lock);
- return written;
-}
-
-static DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store);
-
-static void firmware_rw_data(struct fw_priv *fw_priv, char *buffer,
- loff_t offset, size_t count, bool read)
-{
- if (read)
- memcpy(buffer, fw_priv->data + offset, count);
- else
- memcpy(fw_priv->data + offset, buffer, count);
-}
-
-static void firmware_rw(struct fw_priv *fw_priv, char *buffer,
- loff_t offset, size_t count, bool read)
-{
- while (count) {
- void *page_data;
- int page_nr = offset >> PAGE_SHIFT;
- int page_ofs = offset & (PAGE_SIZE-1);
- int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
-
- page_data = kmap(fw_priv->pages[page_nr]);
-
- if (read)
- memcpy(buffer, page_data + page_ofs, page_cnt);
- else
- memcpy(page_data + page_ofs, buffer, page_cnt);
-
- kunmap(fw_priv->pages[page_nr]);
- buffer += page_cnt;
- offset += page_cnt;
- count -= page_cnt;
- }
-}
-
-static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
- char *buffer, loff_t offset, size_t count)
-{
- struct device *dev = kobj_to_dev(kobj);
- struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
- struct fw_priv *fw_priv;
- ssize_t ret_count;
-
- mutex_lock(&fw_lock);
- fw_priv = fw_sysfs->fw_priv;
- if (!fw_priv || fw_sysfs_done(fw_priv)) {
- ret_count = -ENODEV;
- goto out;
- }
- if (offset > fw_priv->size) {
- ret_count = 0;
- goto out;
- }
- if (count > fw_priv->size - offset)
- count = fw_priv->size - offset;
-
- ret_count = count;
-
- if (fw_priv->data)
- firmware_rw_data(fw_priv, buffer, offset, count, true);
- else
- firmware_rw(fw_priv, buffer, offset, count, true);
-
-out:
- mutex_unlock(&fw_lock);
- return ret_count;
-}
-
-static int fw_realloc_pages(struct fw_sysfs *fw_sysfs, int min_size)
-{
- struct fw_priv *fw_priv= fw_sysfs->fw_priv;
- int pages_needed = PAGE_ALIGN(min_size) >> PAGE_SHIFT;
-
- /* If the array of pages is too small, grow it... */
- if (fw_priv->page_array_size < pages_needed) {
- int new_array_size = max(pages_needed,
- fw_priv->page_array_size * 2);
- struct page **new_pages;
-
- new_pages = vmalloc(new_array_size * sizeof(void *));
- if (!new_pages) {
- fw_load_abort(fw_sysfs);
- return -ENOMEM;
- }
- memcpy(new_pages, fw_priv->pages,
- fw_priv->page_array_size * sizeof(void *));
- memset(&new_pages[fw_priv->page_array_size], 0, sizeof(void *) *
- (new_array_size - fw_priv->page_array_size));
- vfree(fw_priv->pages);
- fw_priv->pages = new_pages;
- fw_priv->page_array_size = new_array_size;
- }
-
- while (fw_priv->nr_pages < pages_needed) {
- fw_priv->pages[fw_priv->nr_pages] =
- alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
-
- if (!fw_priv->pages[fw_priv->nr_pages]) {
- fw_load_abort(fw_sysfs);
- return -ENOMEM;
- }
- fw_priv->nr_pages++;
- }
- return 0;
-}
-
-/**
- * firmware_data_write - write method for firmware
- * @filp: open sysfs file
- * @kobj: kobject for the device
- * @bin_attr: bin_attr structure
- * @buffer: buffer being written
- * @offset: buffer offset for write in total data store area
- * @count: buffer size
- *
- * Data written to the 'data' attribute will be later handed to
- * the driver as a firmware image.
- **/
-static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
- char *buffer, loff_t offset, size_t count)
-{
- struct device *dev = kobj_to_dev(kobj);
- struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
- struct fw_priv *fw_priv;
- ssize_t retval;
-
- if (!capable(CAP_SYS_RAWIO))
- return -EPERM;
-
- mutex_lock(&fw_lock);
- fw_priv = fw_sysfs->fw_priv;
- if (!fw_priv || fw_sysfs_done(fw_priv)) {
- retval = -ENODEV;
- goto out;
- }
-
- if (fw_priv->data) {
- if (offset + count > fw_priv->allocated_size) {
- retval = -ENOMEM;
- goto out;
- }
- firmware_rw_data(fw_priv, buffer, offset, count, false);
- retval = count;
- } else {
- retval = fw_realloc_pages(fw_sysfs, offset + count);
- if (retval)
- goto out;
-
- retval = count;
- firmware_rw(fw_priv, buffer, offset, count, false);
- }
-
- fw_priv->size = max_t(size_t, offset + count, fw_priv->size);
-out:
- mutex_unlock(&fw_lock);
- return retval;
-}
-
-static struct bin_attribute firmware_attr_data = {
- .attr = { .name = "data", .mode = 0644 },
- .size = 0,
- .read = firmware_data_read,
- .write = firmware_data_write,
-};
-
-static struct attribute *fw_dev_attrs[] = {
- &dev_attr_loading.attr,
- NULL
-};
-
-static struct bin_attribute *fw_dev_bin_attrs[] = {
- &firmware_attr_data,
- NULL
-};
-
-static const struct attribute_group fw_dev_attr_group = {
- .attrs = fw_dev_attrs,
- .bin_attrs = fw_dev_bin_attrs,
-};
-
-static const struct attribute_group *fw_dev_attr_groups[] = {
- &fw_dev_attr_group,
- NULL
-};
-
-static struct fw_sysfs *
-fw_create_instance(struct firmware *firmware, const char *fw_name,
- struct device *device, unsigned int opt_flags)
-{
- struct fw_sysfs *fw_sysfs;
- struct device *f_dev;
-
- fw_sysfs = kzalloc(sizeof(*fw_sysfs), GFP_KERNEL);
- if (!fw_sysfs) {
- fw_sysfs = ERR_PTR(-ENOMEM);
- goto exit;
- }
-
- fw_sysfs->nowait = !!(opt_flags & FW_OPT_NOWAIT);
- fw_sysfs->fw = firmware;
- f_dev = &fw_sysfs->dev;
-
- device_initialize(f_dev);
- dev_set_name(f_dev, "%s", fw_name);
- f_dev->parent = device;
- f_dev->class = &firmware_class;
- f_dev->groups = fw_dev_attr_groups;
-exit:
- return fw_sysfs;
-}
-
-/* load a firmware via user helper */
-static int _request_firmware_load(struct fw_sysfs *fw_sysfs,
- unsigned int opt_flags, long timeout)
-{
- int retval = 0;
- struct device *f_dev = &fw_sysfs->dev;
- struct fw_priv *fw_priv = fw_sysfs->fw_priv;
-
- /* fall back on userspace loading */
- if (!fw_priv->data)
- fw_priv->is_paged_buf = true;
-
- dev_set_uevent_suppress(f_dev, true);
-
- retval = device_add(f_dev);
- if (retval) {
- dev_err(f_dev, "%s: device_register failed\n", __func__);
- goto err_put_dev;
- }
-
- mutex_lock(&fw_lock);
- list_add(&fw_priv->pending_list, &pending_fw_head);
- mutex_unlock(&fw_lock);
-
- if (opt_flags & FW_OPT_UEVENT) {
- fw_priv->need_uevent = true;
- dev_set_uevent_suppress(f_dev, false);
- dev_dbg(f_dev, "firmware: requesting %s\n", fw_priv->fw_name);
- kobject_uevent(&fw_sysfs->dev.kobj, KOBJ_ADD);
- } else {
- timeout = MAX_JIFFY_OFFSET;
- }
-
- retval = fw_sysfs_wait_timeout(fw_priv, timeout);
- if (retval < 0) {
- mutex_lock(&fw_lock);
- fw_load_abort(fw_sysfs);
- mutex_unlock(&fw_lock);
- }
-
- if (fw_state_is_aborted(fw_priv)) {
- if (retval == -ERESTARTSYS)
- retval = -EINTR;
- else
- retval = -EAGAIN;
- } else if (fw_priv->is_paged_buf && !fw_priv->data)
- retval = -ENOMEM;
-
- device_del(f_dev);
-err_put_dev:
- put_device(f_dev);
- return retval;
-}
-
-static int fw_load_from_user_helper(struct firmware *firmware,
- const char *name, struct device *device,
- unsigned int opt_flags)
-{
- struct fw_sysfs *fw_sysfs;
- long timeout;
- int ret;
-
- timeout = firmware_loading_timeout();
- if (opt_flags & FW_OPT_NOWAIT) {
- timeout = usermodehelper_read_lock_wait(timeout);
- if (!timeout) {
- dev_dbg(device, "firmware: %s loading timed out\n",
- name);
- return -EBUSY;
- }
- } else {
- ret = usermodehelper_read_trylock();
- if (WARN_ON(ret)) {
- dev_err(device, "firmware: %s will not be loaded\n",
- name);
- return ret;
- }
- }
-
- fw_sysfs = fw_create_instance(firmware, name, device, opt_flags);
- if (IS_ERR(fw_sysfs)) {
- ret = PTR_ERR(fw_sysfs);
- goto out_unlock;
- }
-
- fw_sysfs->fw_priv = firmware->priv;
- ret = _request_firmware_load(fw_sysfs, opt_flags, timeout);
-
- if (!ret)
- ret = assign_fw(firmware, device, opt_flags);
-
-out_unlock:
- usermodehelper_read_unlock();
-
- return ret;
-}
-
-#ifdef CONFIG_FW_LOADER_USER_HELPER_FALLBACK
-static bool fw_force_sysfs_fallback(unsigned int opt_flags)
-{
- return true;
-}
-#else
-static bool fw_force_sysfs_fallback(unsigned int opt_flags)
-{
- if (!(opt_flags & FW_OPT_USERHELPER))
- return false;
- return true;
-}
-#endif
-
-static bool fw_run_sysfs_fallback(unsigned int opt_flags)
-{
- if ((opt_flags & FW_OPT_NOFALLBACK))
- return false;
-
- return fw_force_sysfs_fallback(opt_flags);
-}
-
-static int fw_sysfs_fallback(struct firmware *fw, const char *name,
- struct device *device,
- unsigned int opt_flags,
- int ret)
-{
- if (!fw_run_sysfs_fallback(opt_flags))
- return ret;
-
- dev_warn(device, "Falling back to user helper\n");
- return fw_load_from_user_helper(fw, name, device, opt_flags);
-}
-#else /* CONFIG_FW_LOADER_USER_HELPER */
-static int fw_sysfs_fallback(struct firmware *fw, const char *name,
- struct device *device,
- unsigned int opt_flags,
- int ret)
-{
- /* Keep carrying over the same error */
- return ret;
-}
-
-static inline void kill_pending_fw_fallback_reqs(bool only_kill_custom) { }
-
-static inline int register_sysfs_loader(void)
-{
- return 0;
-}
-
-static inline void unregister_sysfs_loader(void)
-{
-}
-
-#endif /* CONFIG_FW_LOADER_USER_HELPER */
-
-/* prepare firmware and firmware_buf structs;
- * return 0 if a firmware is already assigned, 1 if need to load one,
- * or a negative error code
- */
-static int
-_request_firmware_prepare(struct firmware **firmware_p, const char *name,
- struct device *device, void *dbuf, size_t size)
-{
- struct firmware *firmware;
- struct fw_priv *fw_priv;
- int ret;
-
- *firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL);
- if (!firmware) {
- dev_err(device, "%s: kmalloc(struct firmware) failed\n",
- __func__);
- return -ENOMEM;
- }
-
- if (fw_get_builtin_firmware(firmware, name, dbuf, size)) {
- dev_dbg(device, "using built-in %s\n", name);
- return 0; /* assigned */
- }
-
- ret = alloc_lookup_fw_priv(name, &fw_cache, &fw_priv, dbuf, size);
-
- /*
- * bind with 'priv' now to avoid warning in failure path
- * of requesting firmware.
- */
- firmware->priv = fw_priv;
-
- if (ret > 0) {
- ret = fw_state_wait(fw_priv);
- if (!ret) {
- fw_set_page_data(fw_priv, firmware);
- return 0; /* assigned */
- }
- }
-
- if (ret < 0)
- return ret;
- return 1; /* need to load */
-}
-
-/*
- * Batched requests need only one wake, we need to do this step last due to the
- * fallback mechanism. The buf is protected with kref_get(), and it won't be
- * released until the last user calls release_firmware().
- *
- * Failed batched requests are possible as well, in such cases we just share
- * the struct fw_priv and won't release it until all requests are woken
- * and have gone through this same path.
- */
-static void fw_abort_batch_reqs(struct firmware *fw)
-{
- struct fw_priv *fw_priv;
-
- /* Loaded directly? */
- if (!fw || !fw->priv)
- return;
-
- fw_priv = fw->priv;
- if (!fw_state_is_aborted(fw_priv))
- fw_state_aborted(fw_priv);
-}
-
-/* called from request_firmware() and request_firmware_work_func() */
-static int
-_request_firmware(const struct firmware **firmware_p, const char *name,
- struct device *device, void *buf, size_t size,
- unsigned int opt_flags)
-{
- struct firmware *fw = NULL;
- int ret;
-
- if (!firmware_p)
- return -EINVAL;
-
- if (!name || name[0] == '\0') {
- ret = -EINVAL;
- goto out;
- }
-
- ret = _request_firmware_prepare(&fw, name, device, buf, size);
- if (ret <= 0) /* error or already assigned */
- goto out;
-
- ret = fw_get_filesystem_firmware(device, fw->priv);
- if (ret) {
- if (!(opt_flags & FW_OPT_NO_WARN))
- dev_warn(device,
- "Direct firmware load for %s failed with error %d\n",
- name, ret);
- ret = fw_sysfs_fallback(fw, name, device, opt_flags, ret);
- } else
- ret = assign_fw(fw, device, opt_flags);
-
- out:
- if (ret < 0) {
- fw_abort_batch_reqs(fw);
- release_firmware(fw);
- fw = NULL;
- }
-
- *firmware_p = fw;
- return ret;
-}
-
-/**
- * request_firmware: - send firmware request and wait for it
- * @firmware_p: pointer to firmware image
- * @name: name of firmware file
- * @device: device for which firmware is being loaded
- *
- * @firmware_p will be used to return a firmware image by the name
- * of @name for device @device.
- *
- * Should be called from user context where sleeping is allowed.
- *
- * @name will be used as $FIRMWARE in the uevent environment and
- * should be distinctive enough not to be confused with any other
- * firmware image for this or any other device.
- *
- * Caller must hold the reference count of @device.
- *
- * The function can be called safely inside device's suspend and
- * resume callback.
- **/
-int
-request_firmware(const struct firmware **firmware_p, const char *name,
- struct device *device)
-{
- int ret;
-
- /* Need to pin this module until return */
- __module_get(THIS_MODULE);
- ret = _request_firmware(firmware_p, name, device, NULL, 0,
- FW_OPT_UEVENT);
- module_put(THIS_MODULE);
- return ret;
-}
-EXPORT_SYMBOL(request_firmware);
-
-/**
- * request_firmware_direct: - load firmware directly without usermode helper
- * @firmware_p: pointer to firmware image
- * @name: name of firmware file
- * @device: device for which firmware is being loaded
- *
- * This function works pretty much like request_firmware(), but this doesn't
- * fall back to usermode helper even if the firmware couldn't be loaded
- * directly from fs. Hence it's useful for loading optional firmwares, which
- * aren't always present, without extra long timeouts of udev.
- **/
-int request_firmware_direct(const struct firmware **firmware_p,
- const char *name, struct device *device)
-{
- int ret;
-
- __module_get(THIS_MODULE);
- ret = _request_firmware(firmware_p, name, device, NULL, 0,
- FW_OPT_UEVENT | FW_OPT_NO_WARN |
- FW_OPT_NOFALLBACK);
- module_put(THIS_MODULE);
- return ret;
-}
-EXPORT_SYMBOL_GPL(request_firmware_direct);
-
-/**
- * request_firmware_into_buf - load firmware into a previously allocated buffer
- * @firmware_p: pointer to firmware image
- * @name: name of firmware file
- * @device: device for which firmware is being loaded and DMA region allocated
- * @buf: address of buffer to load firmware into
- * @size: size of buffer
- *
- * This function works pretty much like request_firmware(), but it doesn't
- * allocate a buffer to hold the firmware data. Instead, the firmware
- * is loaded directly into the buffer pointed to by @buf and the @firmware_p
- * data member is pointed at @buf.
- *
- * This function doesn't cache firmware either.
- */
-int
-request_firmware_into_buf(const struct firmware **firmware_p, const char *name,
- struct device *device, void *buf, size_t size)
-{
- int ret;
-
- __module_get(THIS_MODULE);
- ret = _request_firmware(firmware_p, name, device, buf, size,
- FW_OPT_UEVENT | FW_OPT_NOCACHE);
- module_put(THIS_MODULE);
- return ret;
-}
-EXPORT_SYMBOL(request_firmware_into_buf);
-
-/**
- * release_firmware: - release the resource associated with a firmware image
- * @fw: firmware resource to release
- **/
-void release_firmware(const struct firmware *fw)
-{
- if (fw) {
- if (!fw_is_builtin_firmware(fw))
- firmware_free_data(fw);
- kfree(fw);
- }
-}
-EXPORT_SYMBOL(release_firmware);
-
-/* Async support */
-struct firmware_work {
- struct work_struct work;
- struct module *module;
- const char *name;
- struct device *device;
- void *context;
- void (*cont)(const struct firmware *fw, void *context);
- unsigned int opt_flags;
-};
-
-static void request_firmware_work_func(struct work_struct *work)
-{
- struct firmware_work *fw_work;
- const struct firmware *fw;
-
- fw_work = container_of(work, struct firmware_work, work);
-
- _request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0,
- fw_work->opt_flags);
- fw_work->cont(fw, fw_work->context);
- put_device(fw_work->device); /* taken in request_firmware_nowait() */
-
- module_put(fw_work->module);
- kfree_const(fw_work->name);
- kfree(fw_work);
-}
-
-/**
- * request_firmware_nowait - asynchronous version of request_firmware
- * @module: module requesting the firmware
- * @uevent: sends uevent to copy the firmware image if this flag
- * is non-zero else the firmware copy must be done manually.
- * @name: name of firmware file
- * @device: device for which firmware is being loaded
- * @gfp: allocation flags
- * @context: will be passed over to @cont, and
- * @fw may be %NULL if firmware request fails.
- * @cont: function will be called asynchronously when the firmware
- * request is over.
- *
- * Caller must hold the reference count of @device.
- *
- * Asynchronous variant of request_firmware() for user contexts:
- * - sleep for as small periods as possible since it may
- * increase kernel boot time of built-in device drivers
- * requesting firmware in their ->probe() methods, if
- * @gfp is GFP_KERNEL.
- *
- * - can't sleep at all if @gfp is GFP_ATOMIC.
- **/
-int
-request_firmware_nowait(
- struct module *module, bool uevent,
- const char *name, struct device *device, gfp_t gfp, void *context,
- void (*cont)(const struct firmware *fw, void *context))
-{
- struct firmware_work *fw_work;
-
- fw_work = kzalloc(sizeof(struct firmware_work), gfp);
- if (!fw_work)
- return -ENOMEM;
-
- fw_work->module = module;
- fw_work->name = kstrdup_const(name, gfp);
- if (!fw_work->name) {
- kfree(fw_work);
- return -ENOMEM;
- }
- fw_work->device = device;
- fw_work->context = context;
- fw_work->cont = cont;
- fw_work->opt_flags = FW_OPT_NOWAIT |
- (uevent ? FW_OPT_UEVENT : FW_OPT_USERHELPER);
-
- if (!try_module_get(module)) {
- kfree_const(fw_work->name);
- kfree(fw_work);
- return -EFAULT;
- }
-
- get_device(fw_work->device);
- INIT_WORK(&fw_work->work, request_firmware_work_func);
- schedule_work(&fw_work->work);
- return 0;
-}
-EXPORT_SYMBOL(request_firmware_nowait);
-
-#ifdef CONFIG_PM_SLEEP
-static ASYNC_DOMAIN_EXCLUSIVE(fw_cache_domain);
-
-/**
- * cache_firmware - cache one firmware image in kernel memory space
- * @fw_name: the firmware image name
- *
- * Cache firmware in kernel memory so that drivers can use it when
- * system isn't ready for them to request firmware image from userspace.
- * Once it returns successfully, driver can use request_firmware or its
- * nowait version to get the cached firmware without any interacting
- * with userspace
- *
- * Return 0 if the firmware image has been cached successfully
- * Return !0 otherwise
- *
- */
-static int cache_firmware(const char *fw_name)
-{
- int ret;
- const struct firmware *fw;
-
- pr_debug("%s: %s\n", __func__, fw_name);
-
- ret = request_firmware(&fw, fw_name, NULL);
- if (!ret)
- kfree(fw);
-
- pr_debug("%s: %s ret=%d\n", __func__, fw_name, ret);
-
- return ret;
-}
-
-static struct fw_priv *lookup_fw_priv(const char *fw_name)
-{
- struct fw_priv *tmp;
- struct firmware_cache *fwc = &fw_cache;
-
- spin_lock(&fwc->lock);
- tmp = __lookup_fw_priv(fw_name);
- spin_unlock(&fwc->lock);
-
- return tmp;
-}
-
-/**
- * uncache_firmware - remove one cached firmware image
- * @fw_name: the firmware image name
- *
- * Uncache one firmware image which has been cached successfully
- * before.
- *
- * Return 0 if the firmware cache has been removed successfully
- * Return !0 otherwise
- *
- */
-static int uncache_firmware(const char *fw_name)
-{
- struct fw_priv *fw_priv;
- struct firmware fw;
-
- pr_debug("%s: %s\n", __func__, fw_name);
-
- if (fw_get_builtin_firmware(&fw, fw_name, NULL, 0))
- return 0;
-
- fw_priv = lookup_fw_priv(fw_name);
- if (fw_priv) {
- free_fw_priv(fw_priv);
- return 0;
- }
-
- return -EINVAL;
-}
-
-static struct fw_cache_entry *alloc_fw_cache_entry(const char *name)
-{
- struct fw_cache_entry *fce;
-
- fce = kzalloc(sizeof(*fce), GFP_ATOMIC);
- if (!fce)
- goto exit;
-
- fce->name = kstrdup_const(name, GFP_ATOMIC);
- if (!fce->name) {
- kfree(fce);
- fce = NULL;
- goto exit;
- }
-exit:
- return fce;
-}
-
-static int __fw_entry_found(const char *name)
-{
- struct firmware_cache *fwc = &fw_cache;
- struct fw_cache_entry *fce;
-
- list_for_each_entry(fce, &fwc->fw_names, list) {
- if (!strcmp(fce->name, name))
- return 1;
- }
- return 0;
-}
-
-static int fw_cache_piggyback_on_request(const char *name)
-{
- struct firmware_cache *fwc = &fw_cache;
- struct fw_cache_entry *fce;
- int ret = 0;
-
- spin_lock(&fwc->name_lock);
- if (__fw_entry_found(name))
- goto found;
-
- fce = alloc_fw_cache_entry(name);
- if (fce) {
- ret = 1;
- list_add(&fce->list, &fwc->fw_names);
- pr_debug("%s: fw: %s\n", __func__, name);
- }
-found:
- spin_unlock(&fwc->name_lock);
- return ret;
-}
-
-static void free_fw_cache_entry(struct fw_cache_entry *fce)
-{
- kfree_const(fce->name);
- kfree(fce);
-}
-
-static void __async_dev_cache_fw_image(void *fw_entry,
- async_cookie_t cookie)
-{
- struct fw_cache_entry *fce = fw_entry;
- struct firmware_cache *fwc = &fw_cache;
- int ret;
-
- ret = cache_firmware(fce->name);
- if (ret) {
- spin_lock(&fwc->name_lock);
- list_del(&fce->list);
- spin_unlock(&fwc->name_lock);
-
- free_fw_cache_entry(fce);
- }
-}
-
-/* called with dev->devres_lock held */
-static void dev_create_fw_entry(struct device *dev, void *res,
- void *data)
-{
- struct fw_name_devm *fwn = res;
- const char *fw_name = fwn->name;
- struct list_head *head = data;
- struct fw_cache_entry *fce;
-
- fce = alloc_fw_cache_entry(fw_name);
- if (fce)
- list_add(&fce->list, head);
-}
-
-static int devm_name_match(struct device *dev, void *res,
- void *match_data)
-{
- struct fw_name_devm *fwn = res;
- return (fwn->magic == (unsigned long)match_data);
-}
-
-static void dev_cache_fw_image(struct device *dev, void *data)
-{
- LIST_HEAD(todo);
- struct fw_cache_entry *fce;
- struct fw_cache_entry *fce_next;
- struct firmware_cache *fwc = &fw_cache;
-
- devres_for_each_res(dev, fw_name_devm_release,
- devm_name_match, &fw_cache,
- dev_create_fw_entry, &todo);
-
- list_for_each_entry_safe(fce, fce_next, &todo, list) {
- list_del(&fce->list);
-
- spin_lock(&fwc->name_lock);
- /* only one cache entry for one firmware */
- if (!__fw_entry_found(fce->name)) {
- list_add(&fce->list, &fwc->fw_names);
- } else {
- free_fw_cache_entry(fce);
- fce = NULL;
- }
- spin_unlock(&fwc->name_lock);
-
- if (fce)
- async_schedule_domain(__async_dev_cache_fw_image,
- (void *)fce,
- &fw_cache_domain);
- }
-}
-
-static void __device_uncache_fw_images(void)
-{
- struct firmware_cache *fwc = &fw_cache;
- struct fw_cache_entry *fce;
-
- spin_lock(&fwc->name_lock);
- while (!list_empty(&fwc->fw_names)) {
- fce = list_entry(fwc->fw_names.next,
- struct fw_cache_entry, list);
- list_del(&fce->list);
- spin_unlock(&fwc->name_lock);
-
- uncache_firmware(fce->name);
- free_fw_cache_entry(fce);
-
- spin_lock(&fwc->name_lock);
- }
- spin_unlock(&fwc->name_lock);
-}
-
-/**
- * device_cache_fw_images - cache devices' firmware
- *
- * If one device called request_firmware or its nowait version
- * successfully before, the firmware names are recored into the
- * device's devres link list, so device_cache_fw_images can call
- * cache_firmware() to cache these firmwares for the device,
- * then the device driver can load its firmwares easily at
- * time when system is not ready to complete loading firmware.
- */
-static void device_cache_fw_images(void)
-{
- struct firmware_cache *fwc = &fw_cache;
- int old_timeout;
- DEFINE_WAIT(wait);
-
- pr_debug("%s\n", __func__);
-
- /* cancel uncache work */
- cancel_delayed_work_sync(&fwc->work);
-
- /*
- * use small loading timeout for caching devices' firmware
- * because all these firmware images have been loaded
- * successfully at lease once, also system is ready for
- * completing firmware loading now. The maximum size of
- * firmware in current distributions is about 2M bytes,
- * so 10 secs should be enough.
- */
- old_timeout = loading_timeout;
- loading_timeout = 10;
-
- mutex_lock(&fw_lock);
- fwc->state = FW_LOADER_START_CACHE;
- dpm_for_each_dev(NULL, dev_cache_fw_image);
- mutex_unlock(&fw_lock);
-
- /* wait for completion of caching firmware for all devices */
- async_synchronize_full_domain(&fw_cache_domain);
-
- loading_timeout = old_timeout;
-}
-
-/**
- * device_uncache_fw_images - uncache devices' firmware
- *
- * uncache all firmwares which have been cached successfully
- * by device_uncache_fw_images earlier
- */
-static void device_uncache_fw_images(void)
-{
- pr_debug("%s\n", __func__);
- __device_uncache_fw_images();
-}
-
-static void device_uncache_fw_images_work(struct work_struct *work)
-{
- device_uncache_fw_images();
-}
-
-/**
- * device_uncache_fw_images_delay - uncache devices firmwares
- * @delay: number of milliseconds to delay uncache device firmwares
- *
- * uncache all devices's firmwares which has been cached successfully
- * by device_cache_fw_images after @delay milliseconds.
- */
-static void device_uncache_fw_images_delay(unsigned long delay)
-{
- queue_delayed_work(system_power_efficient_wq, &fw_cache.work,
- msecs_to_jiffies(delay));
-}
-
-static int fw_pm_notify(struct notifier_block *notify_block,
- unsigned long mode, void *unused)
-{
- switch (mode) {
- case PM_HIBERNATION_PREPARE:
- case PM_SUSPEND_PREPARE:
- case PM_RESTORE_PREPARE:
- /*
- * kill pending fallback requests with a custom fallback
- * to avoid stalling suspend.
- */
- kill_pending_fw_fallback_reqs(true);
- device_cache_fw_images();
- break;
-
- case PM_POST_SUSPEND:
- case PM_POST_HIBERNATION:
- case PM_POST_RESTORE:
- /*
- * In case that system sleep failed and syscore_suspend is
- * not called.
- */
- mutex_lock(&fw_lock);
- fw_cache.state = FW_LOADER_NO_CACHE;
- mutex_unlock(&fw_lock);
-
- device_uncache_fw_images_delay(10 * MSEC_PER_SEC);
- break;
- }
-
- return 0;
-}
-
-/* stop caching firmware once syscore_suspend is reached */
-static int fw_suspend(void)
-{
- fw_cache.state = FW_LOADER_NO_CACHE;
- return 0;
-}
-
-static struct syscore_ops fw_syscore_ops = {
- .suspend = fw_suspend,
-};
-
-static int __init register_fw_pm_ops(void)
-{
- int ret;
-
- spin_lock_init(&fw_cache.name_lock);
- INIT_LIST_HEAD(&fw_cache.fw_names);
-
- INIT_DELAYED_WORK(&fw_cache.work,
- device_uncache_fw_images_work);
-
- fw_cache.pm_notify.notifier_call = fw_pm_notify;
- ret = register_pm_notifier(&fw_cache.pm_notify);
- if (ret)
- return ret;
-
- register_syscore_ops(&fw_syscore_ops);
-
- return ret;
-}
-
-static inline void unregister_fw_pm_ops(void)
-{
- unregister_syscore_ops(&fw_syscore_ops);
- unregister_pm_notifier(&fw_cache.pm_notify);
-}
-#else
-static int fw_cache_piggyback_on_request(const char *name)
-{
- return 0;
-}
-static inline int register_fw_pm_ops(void)
-{
- return 0;
-}
-static inline void unregister_fw_pm_ops(void)
-{
-}
-#endif
-
-static void __init fw_cache_init(void)
-{
- spin_lock_init(&fw_cache.lock);
- INIT_LIST_HEAD(&fw_cache.head);
- fw_cache.state = FW_LOADER_NO_CACHE;
-}
-
-static int fw_shutdown_notify(struct notifier_block *unused1,
- unsigned long unused2, void *unused3)
-{
- /*
- * Kill all pending fallback requests to avoid both stalling shutdown,
- * and avoid a deadlock with the usermode_lock.
- */
- kill_pending_fw_fallback_reqs(false);
-
- return NOTIFY_DONE;
-}
-
-static struct notifier_block fw_shutdown_nb = {
- .notifier_call = fw_shutdown_notify,
-};
-
-static int __init firmware_class_init(void)
-{
- int ret;
-
- /* No need to unfold these on exit */
- fw_cache_init();
-
- ret = register_fw_pm_ops();
- if (ret)
- return ret;
-
- ret = register_reboot_notifier(&fw_shutdown_nb);
- if (ret)
- goto out;
-
- return register_sysfs_loader();
-
-out:
- unregister_fw_pm_ops();
- return ret;
-}
-
-static void __exit firmware_class_exit(void)
-{
- unregister_fw_pm_ops();
- unregister_reboot_notifier(&fw_shutdown_nb);
- unregister_sysfs_loader();
-}
-
-fs_initcall(firmware_class_init);
-module_exit(firmware_class_exit);
diff --git a/drivers/base/firmware_loader/Makefile b/drivers/base/firmware_loader/Makefile
new file mode 100644
index 000000000000..a97eeb0be1d8
--- /dev/null
+++ b/drivers/base/firmware_loader/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+# Makefile for the Linux firmware loader
+
+obj-y := fallback_table.o
+obj-$(CONFIG_FW_LOADER) += firmware_class.o
+firmware_class-objs := main.o
+firmware_class-$(CONFIG_FW_LOADER_USER_HELPER) += fallback.o
diff --git a/drivers/base/firmware_loader/fallback.c b/drivers/base/firmware_loader/fallback.c
new file mode 100644
index 000000000000..358354148dec
--- /dev/null
+++ b/drivers/base/firmware_loader/fallback.c
@@ -0,0 +1,675 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/types.h>
+#include <linux/kconfig.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/security.h>
+#include <linux/highmem.h>
+#include <linux/umh.h>
+#include <linux/sysctl.h>
+#include <linux/vmalloc.h>
+
+#include "fallback.h"
+#include "firmware.h"
+
+/*
+ * firmware fallback mechanism
+ */
+
+extern struct firmware_fallback_config fw_fallback_config;
+
+/* These getters are vetted to use int properly */
+static inline int __firmware_loading_timeout(void)
+{
+ return fw_fallback_config.loading_timeout;
+}
+
+/* These setters are vetted to use int properly */
+static void __fw_fallback_set_timeout(int timeout)
+{
+ fw_fallback_config.loading_timeout = timeout;
+}
+
+/*
+ * use small loading timeout for caching devices' firmware because all these
+ * firmware images have been loaded successfully at lease once, also system is
+ * ready for completing firmware loading now. The maximum size of firmware in
+ * current distributions is about 2M bytes, so 10 secs should be enough.
+ */
+void fw_fallback_set_cache_timeout(void)
+{
+ fw_fallback_config.old_timeout = __firmware_loading_timeout();
+ __fw_fallback_set_timeout(10);
+}
+
+/* Restores the timeout to the value last configured during normal operation */
+void fw_fallback_set_default_timeout(void)
+{
+ __fw_fallback_set_timeout(fw_fallback_config.old_timeout);
+}
+
+static long firmware_loading_timeout(void)
+{
+ return __firmware_loading_timeout() > 0 ?
+ __firmware_loading_timeout() * HZ : MAX_JIFFY_OFFSET;
+}
+
+static inline bool fw_sysfs_done(struct fw_priv *fw_priv)
+{
+ return __fw_state_check(fw_priv, FW_STATUS_DONE);
+}
+
+static inline bool fw_sysfs_loading(struct fw_priv *fw_priv)
+{
+ return __fw_state_check(fw_priv, FW_STATUS_LOADING);
+}
+
+static inline int fw_sysfs_wait_timeout(struct fw_priv *fw_priv, long timeout)
+{
+ return __fw_state_wait_common(fw_priv, timeout);
+}
+
+struct fw_sysfs {
+ bool nowait;
+ struct device dev;
+ struct fw_priv *fw_priv;
+ struct firmware *fw;
+};
+
+static struct fw_sysfs *to_fw_sysfs(struct device *dev)
+{
+ return container_of(dev, struct fw_sysfs, dev);
+}
+
+static void __fw_load_abort(struct fw_priv *fw_priv)
+{
+ /*
+ * There is a small window in which user can write to 'loading'
+ * between loading done and disappearance of 'loading'
+ */
+ if (fw_sysfs_done(fw_priv))
+ return;
+
+ list_del_init(&fw_priv->pending_list);
+ fw_state_aborted(fw_priv);
+}
+
+static void fw_load_abort(struct fw_sysfs *fw_sysfs)
+{
+ struct fw_priv *fw_priv = fw_sysfs->fw_priv;
+
+ __fw_load_abort(fw_priv);
+}
+
+static LIST_HEAD(pending_fw_head);
+
+void kill_pending_fw_fallback_reqs(bool only_kill_custom)
+{
+ struct fw_priv *fw_priv;
+ struct fw_priv *next;
+
+ mutex_lock(&fw_lock);
+ list_for_each_entry_safe(fw_priv, next, &pending_fw_head,
+ pending_list) {
+ if (!fw_priv->need_uevent || !only_kill_custom)
+ __fw_load_abort(fw_priv);
+ }
+ mutex_unlock(&fw_lock);
+}
+
+static ssize_t timeout_show(struct class *class, struct class_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", __firmware_loading_timeout());
+}
+
+/**
+ * firmware_timeout_store - set number of seconds to wait for firmware
+ * @class: device class pointer
+ * @attr: device attribute pointer
+ * @buf: buffer to scan for timeout value
+ * @count: number of bytes in @buf
+ *
+ * Sets the number of seconds to wait for the firmware. Once
+ * this expires an error will be returned to the driver and no
+ * firmware will be provided.
+ *
+ * Note: zero means 'wait forever'.
+ **/
+static ssize_t timeout_store(struct class *class, struct class_attribute *attr,
+ const char *buf, size_t count)
+{
+ int tmp_loading_timeout = simple_strtol(buf, NULL, 10);
+
+ if (tmp_loading_timeout < 0)
+ tmp_loading_timeout = 0;
+
+ __fw_fallback_set_timeout(tmp_loading_timeout);
+
+ return count;
+}
+static CLASS_ATTR_RW(timeout);
+
+static struct attribute *firmware_class_attrs[] = {
+ &class_attr_timeout.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(firmware_class);
+
+static void fw_dev_release(struct device *dev)
+{
+ struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
+
+ kfree(fw_sysfs);
+}
+
+static int do_firmware_uevent(struct fw_sysfs *fw_sysfs, struct kobj_uevent_env *env)
+{
+ if (add_uevent_var(env, "FIRMWARE=%s", fw_sysfs->fw_priv->fw_name))
+ return -ENOMEM;
+ if (add_uevent_var(env, "TIMEOUT=%i", __firmware_loading_timeout()))
+ return -ENOMEM;
+ if (add_uevent_var(env, "ASYNC=%d", fw_sysfs->nowait))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int firmware_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
+ int err = 0;
+
+ mutex_lock(&fw_lock);
+ if (fw_sysfs->fw_priv)
+ err = do_firmware_uevent(fw_sysfs, env);
+ mutex_unlock(&fw_lock);
+ return err;
+}
+
+static struct class firmware_class = {
+ .name = "firmware",
+ .class_groups = firmware_class_groups,
+ .dev_uevent = firmware_uevent,
+ .dev_release = fw_dev_release,
+};
+
+int register_sysfs_loader(void)
+{
+ return class_register(&firmware_class);
+}
+
+void unregister_sysfs_loader(void)
+{
+ class_unregister(&firmware_class);
+}
+
+static ssize_t firmware_loading_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
+ int loading = 0;
+
+ mutex_lock(&fw_lock);
+ if (fw_sysfs->fw_priv)
+ loading = fw_sysfs_loading(fw_sysfs->fw_priv);
+ mutex_unlock(&fw_lock);
+
+ return sprintf(buf, "%d\n", loading);
+}
+
+/* Some architectures don't have PAGE_KERNEL_RO */
+#ifndef PAGE_KERNEL_RO
+#define PAGE_KERNEL_RO PAGE_KERNEL
+#endif
+
+/* one pages buffer should be mapped/unmapped only once */
+static int map_fw_priv_pages(struct fw_priv *fw_priv)
+{
+ if (!fw_priv->is_paged_buf)
+ return 0;
+
+ vunmap(fw_priv->data);
+ fw_priv->data = vmap(fw_priv->pages, fw_priv->nr_pages, 0,
+ PAGE_KERNEL_RO);
+ if (!fw_priv->data)
+ return -ENOMEM;
+ return 0;
+}
+
+/**
+ * firmware_loading_store - set value in the 'loading' control file
+ * @dev: device pointer
+ * @attr: device attribute pointer
+ * @buf: buffer to scan for loading control value
+ * @count: number of bytes in @buf
+ *
+ * The relevant values are:
+ *
+ * 1: Start a load, discarding any previous partial load.
+ * 0: Conclude the load and hand the data to the driver code.
+ * -1: Conclude the load with an error and discard any written data.
+ **/
+static ssize_t firmware_loading_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
+ struct fw_priv *fw_priv;
+ ssize_t written = count;
+ int loading = simple_strtol(buf, NULL, 10);
+ int i;
+
+ mutex_lock(&fw_lock);
+ fw_priv = fw_sysfs->fw_priv;
+ if (fw_state_is_aborted(fw_priv))
+ goto out;
+
+ switch (loading) {
+ case 1:
+ /* discarding any previous partial load */
+ if (!fw_sysfs_done(fw_priv)) {
+ for (i = 0; i < fw_priv->nr_pages; i++)
+ __free_page(fw_priv->pages[i]);
+ vfree(fw_priv->pages);
+ fw_priv->pages = NULL;
+ fw_priv->page_array_size = 0;
+ fw_priv->nr_pages = 0;
+ fw_state_start(fw_priv);
+ }
+ break;
+ case 0:
+ if (fw_sysfs_loading(fw_priv)) {
+ int rc;
+
+ /*
+ * Several loading requests may be pending on
+ * one same firmware buf, so let all requests
+ * see the mapped 'buf->data' once the loading
+ * is completed.
+ * */
+ rc = map_fw_priv_pages(fw_priv);
+ if (rc)
+ dev_err(dev, "%s: map pages failed\n",
+ __func__);
+ else
+ rc = security_kernel_post_read_file(NULL,
+ fw_priv->data, fw_priv->size,
+ READING_FIRMWARE);
+
+ /*
+ * Same logic as fw_load_abort, only the DONE bit
+ * is ignored and we set ABORT only on failure.
+ */
+ list_del_init(&fw_priv->pending_list);
+ if (rc) {
+ fw_state_aborted(fw_priv);
+ written = rc;
+ } else {
+ fw_state_done(fw_priv);
+ }
+ break;
+ }
+ /* fallthrough */
+ default:
+ dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading);
+ /* fallthrough */
+ case -1:
+ fw_load_abort(fw_sysfs);
+ break;
+ }
+out:
+ mutex_unlock(&fw_lock);
+ return written;
+}
+
+static DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store);
+
+static void firmware_rw_data(struct fw_priv *fw_priv, char *buffer,
+ loff_t offset, size_t count, bool read)
+{
+ if (read)
+ memcpy(buffer, fw_priv->data + offset, count);
+ else
+ memcpy(fw_priv->data + offset, buffer, count);
+}
+
+static void firmware_rw(struct fw_priv *fw_priv, char *buffer,
+ loff_t offset, size_t count, bool read)
+{
+ while (count) {
+ void *page_data;
+ int page_nr = offset >> PAGE_SHIFT;
+ int page_ofs = offset & (PAGE_SIZE-1);
+ int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
+
+ page_data = kmap(fw_priv->pages[page_nr]);
+
+ if (read)
+ memcpy(buffer, page_data + page_ofs, page_cnt);
+ else
+ memcpy(page_data + page_ofs, buffer, page_cnt);
+
+ kunmap(fw_priv->pages[page_nr]);
+ buffer += page_cnt;
+ offset += page_cnt;
+ count -= page_cnt;
+ }
+}
+
+static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buffer, loff_t offset, size_t count)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
+ struct fw_priv *fw_priv;
+ ssize_t ret_count;
+
+ mutex_lock(&fw_lock);
+ fw_priv = fw_sysfs->fw_priv;
+ if (!fw_priv || fw_sysfs_done(fw_priv)) {
+ ret_count = -ENODEV;
+ goto out;
+ }
+ if (offset > fw_priv->size) {
+ ret_count = 0;
+ goto out;
+ }
+ if (count > fw_priv->size - offset)
+ count = fw_priv->size - offset;
+
+ ret_count = count;
+
+ if (fw_priv->data)
+ firmware_rw_data(fw_priv, buffer, offset, count, true);
+ else
+ firmware_rw(fw_priv, buffer, offset, count, true);
+
+out:
+ mutex_unlock(&fw_lock);
+ return ret_count;
+}
+
+static int fw_realloc_pages(struct fw_sysfs *fw_sysfs, int min_size)
+{
+ struct fw_priv *fw_priv= fw_sysfs->fw_priv;
+ int pages_needed = PAGE_ALIGN(min_size) >> PAGE_SHIFT;
+
+ /* If the array of pages is too small, grow it... */
+ if (fw_priv->page_array_size < pages_needed) {
+ int new_array_size = max(pages_needed,
+ fw_priv->page_array_size * 2);
+ struct page **new_pages;
+
+ new_pages = vmalloc(new_array_size * sizeof(void *));
+ if (!new_pages) {
+ fw_load_abort(fw_sysfs);
+ return -ENOMEM;
+ }
+ memcpy(new_pages, fw_priv->pages,
+ fw_priv->page_array_size * sizeof(void *));
+ memset(&new_pages[fw_priv->page_array_size], 0, sizeof(void *) *
+ (new_array_size - fw_priv->page_array_size));
+ vfree(fw_priv->pages);
+ fw_priv->pages = new_pages;
+ fw_priv->page_array_size = new_array_size;
+ }
+
+ while (fw_priv->nr_pages < pages_needed) {
+ fw_priv->pages[fw_priv->nr_pages] =
+ alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
+
+ if (!fw_priv->pages[fw_priv->nr_pages]) {
+ fw_load_abort(fw_sysfs);
+ return -ENOMEM;
+ }
+ fw_priv->nr_pages++;
+ }
+ return 0;
+}
+
+/**
+ * firmware_data_write - write method for firmware
+ * @filp: open sysfs file
+ * @kobj: kobject for the device
+ * @bin_attr: bin_attr structure
+ * @buffer: buffer being written
+ * @offset: buffer offset for write in total data store area
+ * @count: buffer size
+ *
+ * Data written to the 'data' attribute will be later handed to
+ * the driver as a firmware image.
+ **/
+static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buffer, loff_t offset, size_t count)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
+ struct fw_priv *fw_priv;
+ ssize_t retval;
+
+ if (!capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+ mutex_lock(&fw_lock);
+ fw_priv = fw_sysfs->fw_priv;
+ if (!fw_priv || fw_sysfs_done(fw_priv)) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ if (fw_priv->data) {
+ if (offset + count > fw_priv->allocated_size) {
+ retval = -ENOMEM;
+ goto out;
+ }
+ firmware_rw_data(fw_priv, buffer, offset, count, false);
+ retval = count;
+ } else {
+ retval = fw_realloc_pages(fw_sysfs, offset + count);
+ if (retval)
+ goto out;
+
+ retval = count;
+ firmware_rw(fw_priv, buffer, offset, count, false);
+ }
+
+ fw_priv->size = max_t(size_t, offset + count, fw_priv->size);
+out:
+ mutex_unlock(&fw_lock);
+ return retval;
+}
+
+static struct bin_attribute firmware_attr_data = {
+ .attr = { .name = "data", .mode = 0644 },
+ .size = 0,
+ .read = firmware_data_read,
+ .write = firmware_data_write,
+};
+
+static struct attribute *fw_dev_attrs[] = {
+ &dev_attr_loading.attr,
+ NULL
+};
+
+static struct bin_attribute *fw_dev_bin_attrs[] = {
+ &firmware_attr_data,
+ NULL
+};
+
+static const struct attribute_group fw_dev_attr_group = {
+ .attrs = fw_dev_attrs,
+ .bin_attrs = fw_dev_bin_attrs,
+};
+
+static const struct attribute_group *fw_dev_attr_groups[] = {
+ &fw_dev_attr_group,
+ NULL
+};
+
+static struct fw_sysfs *
+fw_create_instance(struct firmware *firmware, const char *fw_name,
+ struct device *device, unsigned int opt_flags)
+{
+ struct fw_sysfs *fw_sysfs;
+ struct device *f_dev;
+
+ fw_sysfs = kzalloc(sizeof(*fw_sysfs), GFP_KERNEL);
+ if (!fw_sysfs) {
+ fw_sysfs = ERR_PTR(-ENOMEM);
+ goto exit;
+ }
+
+ fw_sysfs->nowait = !!(opt_flags & FW_OPT_NOWAIT);
+ fw_sysfs->fw = firmware;
+ f_dev = &fw_sysfs->dev;
+
+ device_initialize(f_dev);
+ dev_set_name(f_dev, "%s", fw_name);
+ f_dev->parent = device;
+ f_dev->class = &firmware_class;
+ f_dev->groups = fw_dev_attr_groups;
+exit:
+ return fw_sysfs;
+}
+
+/**
+ * fw_load_sysfs_fallback - load a firmware via the sysfs fallback mechanism
+ * @fw_sysfs: firmware sysfs information for the firmware to load
+ * @opt_flags: flags of options, FW_OPT_*
+ * @timeout: timeout to wait for the load
+ *
+ * In charge of constructing a sysfs fallback interface for firmware loading.
+ **/
+static int fw_load_sysfs_fallback(struct fw_sysfs *fw_sysfs,
+ unsigned int opt_flags, long timeout)
+{
+ int retval = 0;
+ struct device *f_dev = &fw_sysfs->dev;
+ struct fw_priv *fw_priv = fw_sysfs->fw_priv;
+
+ /* fall back on userspace loading */
+ if (!fw_priv->data)
+ fw_priv->is_paged_buf = true;
+
+ dev_set_uevent_suppress(f_dev, true);
+
+ retval = device_add(f_dev);
+ if (retval) {
+ dev_err(f_dev, "%s: device_register failed\n", __func__);
+ goto err_put_dev;
+ }
+
+ mutex_lock(&fw_lock);
+ list_add(&fw_priv->pending_list, &pending_fw_head);
+ mutex_unlock(&fw_lock);
+
+ if (opt_flags & FW_OPT_UEVENT) {
+ fw_priv->need_uevent = true;
+ dev_set_uevent_suppress(f_dev, false);
+ dev_dbg(f_dev, "firmware: requesting %s\n", fw_priv->fw_name);
+ kobject_uevent(&fw_sysfs->dev.kobj, KOBJ_ADD);
+ } else {
+ timeout = MAX_JIFFY_OFFSET;
+ }
+
+ retval = fw_sysfs_wait_timeout(fw_priv, timeout);
+ if (retval < 0) {
+ mutex_lock(&fw_lock);
+ fw_load_abort(fw_sysfs);
+ mutex_unlock(&fw_lock);
+ }
+
+ if (fw_state_is_aborted(fw_priv)) {
+ if (retval == -ERESTARTSYS)
+ retval = -EINTR;
+ else
+ retval = -EAGAIN;
+ } else if (fw_priv->is_paged_buf && !fw_priv->data)
+ retval = -ENOMEM;
+
+ device_del(f_dev);
+err_put_dev:
+ put_device(f_dev);
+ return retval;
+}
+
+static int fw_load_from_user_helper(struct firmware *firmware,
+ const char *name, struct device *device,
+ unsigned int opt_flags)
+{
+ struct fw_sysfs *fw_sysfs;
+ long timeout;
+ int ret;
+
+ timeout = firmware_loading_timeout();
+ if (opt_flags & FW_OPT_NOWAIT) {
+ timeout = usermodehelper_read_lock_wait(timeout);
+ if (!timeout) {
+ dev_dbg(device, "firmware: %s loading timed out\n",
+ name);
+ return -EBUSY;
+ }
+ } else {
+ ret = usermodehelper_read_trylock();
+ if (WARN_ON(ret)) {
+ dev_err(device, "firmware: %s will not be loaded\n",
+ name);
+ return ret;
+ }
+ }
+
+ fw_sysfs = fw_create_instance(firmware, name, device, opt_flags);
+ if (IS_ERR(fw_sysfs)) {
+ ret = PTR_ERR(fw_sysfs);
+ goto out_unlock;
+ }
+
+ fw_sysfs->fw_priv = firmware->priv;
+ ret = fw_load_sysfs_fallback(fw_sysfs, opt_flags, timeout);
+
+ if (!ret)
+ ret = assign_fw(firmware, device, opt_flags);
+
+out_unlock:
+ usermodehelper_read_unlock();
+
+ return ret;
+}
+
+static bool fw_force_sysfs_fallback(unsigned int opt_flags)
+{
+ if (fw_fallback_config.force_sysfs_fallback)
+ return true;
+ if (!(opt_flags & FW_OPT_USERHELPER))
+ return false;
+ return true;
+}
+
+static bool fw_run_sysfs_fallback(unsigned int opt_flags)
+{
+ if (fw_fallback_config.ignore_sysfs_fallback) {
+ pr_info_once("Ignoring firmware sysfs fallback due to sysctl knob\n");
+ return false;
+ }
+
+ if ((opt_flags & FW_OPT_NOFALLBACK))
+ return false;
+
+ return fw_force_sysfs_fallback(opt_flags);
+}
+
+int fw_sysfs_fallback(struct firmware *fw, const char *name,
+ struct device *device,
+ unsigned int opt_flags,
+ int ret)
+{
+ if (!fw_run_sysfs_fallback(opt_flags))
+ return ret;
+
+ dev_warn(device, "Falling back to user helper\n");
+ return fw_load_from_user_helper(fw, name, device, opt_flags);
+}
diff --git a/drivers/base/firmware_loader/fallback.h b/drivers/base/firmware_loader/fallback.h
new file mode 100644
index 000000000000..f8255670a663
--- /dev/null
+++ b/drivers/base/firmware_loader/fallback.h
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __FIRMWARE_FALLBACK_H
+#define __FIRMWARE_FALLBACK_H
+
+#include <linux/firmware.h>
+#include <linux/device.h>
+
+/**
+ * struct firmware_fallback_config - firmware fallback configuration settings
+ *
+ * Helps describe and fine tune the fallback mechanism.
+ *
+ * @force_sysfs_fallback: force the sysfs fallback mechanism to be used
+ * as if one had enabled CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y.
+ * Useful to help debug a CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y
+ * functionality on a kernel where that config entry has been disabled.
+ * @ignore_sysfs_fallback: force to disable the sysfs fallback mechanism.
+ * This emulates the behaviour as if we had set the kernel
+ * config CONFIG_FW_LOADER_USER_HELPER=n.
+ * @old_timeout: for internal use
+ * @loading_timeout: the timeout to wait for the fallback mechanism before
+ * giving up, in seconds.
+ */
+struct firmware_fallback_config {
+ unsigned int force_sysfs_fallback;
+ unsigned int ignore_sysfs_fallback;
+ int old_timeout;
+ int loading_timeout;
+};
+
+#ifdef CONFIG_FW_LOADER_USER_HELPER
+int fw_sysfs_fallback(struct firmware *fw, const char *name,
+ struct device *device,
+ unsigned int opt_flags,
+ int ret);
+void kill_pending_fw_fallback_reqs(bool only_kill_custom);
+
+void fw_fallback_set_cache_timeout(void);
+void fw_fallback_set_default_timeout(void);
+
+int register_sysfs_loader(void);
+void unregister_sysfs_loader(void);
+#else /* CONFIG_FW_LOADER_USER_HELPER */
+static inline int fw_sysfs_fallback(struct firmware *fw, const char *name,
+ struct device *device,
+ unsigned int opt_flags,
+ int ret)
+{
+ /* Keep carrying over the same error */
+ return ret;
+}
+
+static inline void kill_pending_fw_fallback_reqs(bool only_kill_custom) { }
+static inline void fw_fallback_set_cache_timeout(void) { }
+static inline void fw_fallback_set_default_timeout(void) { }
+
+static inline int register_sysfs_loader(void)
+{
+ return 0;
+}
+
+static inline void unregister_sysfs_loader(void)
+{
+}
+#endif /* CONFIG_FW_LOADER_USER_HELPER */
+
+#endif /* __FIRMWARE_FALLBACK_H */
diff --git a/drivers/base/firmware_loader/fallback_table.c b/drivers/base/firmware_loader/fallback_table.c
new file mode 100644
index 000000000000..7428659d8df9
--- /dev/null
+++ b/drivers/base/firmware_loader/fallback_table.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/types.h>
+#include <linux/kconfig.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/security.h>
+#include <linux/highmem.h>
+#include <linux/umh.h>
+#include <linux/sysctl.h>
+
+#include "fallback.h"
+#include "firmware.h"
+
+/*
+ * firmware fallback configuration table
+ */
+
+/* Module or buit-in */
+#ifdef CONFIG_FW_LOADER_USER_HELPER
+
+static unsigned int zero;
+static unsigned int one = 1;
+
+struct firmware_fallback_config fw_fallback_config = {
+ .force_sysfs_fallback = IS_ENABLED(CONFIG_FW_LOADER_USER_HELPER_FALLBACK),
+ .loading_timeout = 60,
+ .old_timeout = 60,
+};
+EXPORT_SYMBOL_GPL(fw_fallback_config);
+
+struct ctl_table firmware_config_table[] = {
+ {
+ .procname = "force_sysfs_fallback",
+ .data = &fw_fallback_config.force_sysfs_fallback,
+ .maxlen = sizeof(unsigned int),
+ .mode = 0644,
+ .proc_handler = proc_douintvec_minmax,
+ .extra1 = &zero,
+ .extra2 = &one,
+ },
+ {
+ .procname = "ignore_sysfs_fallback",
+ .data = &fw_fallback_config.ignore_sysfs_fallback,
+ .maxlen = sizeof(unsigned int),
+ .mode = 0644,
+ .proc_handler = proc_douintvec_minmax,
+ .extra1 = &zero,
+ .extra2 = &one,
+ },
+ { }
+};
+EXPORT_SYMBOL_GPL(firmware_config_table);
+
+#endif
diff --git a/drivers/base/firmware_loader/firmware.h b/drivers/base/firmware_loader/firmware.h
new file mode 100644
index 000000000000..64acbb1a392c
--- /dev/null
+++ b/drivers/base/firmware_loader/firmware.h
@@ -0,0 +1,115 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __FIRMWARE_LOADER_H
+#define __FIRMWARE_LOADER_H
+
+#include <linux/firmware.h>
+#include <linux/types.h>
+#include <linux/kref.h>
+#include <linux/list.h>
+#include <linux/completion.h>
+
+#include <generated/utsrelease.h>
+
+/* firmware behavior options */
+#define FW_OPT_UEVENT (1U << 0)
+#define FW_OPT_NOWAIT (1U << 1)
+#define FW_OPT_USERHELPER (1U << 2)
+#define FW_OPT_NO_WARN (1U << 3)
+#define FW_OPT_NOCACHE (1U << 4)
+#define FW_OPT_NOFALLBACK (1U << 5)
+
+enum fw_status {
+ FW_STATUS_UNKNOWN,
+ FW_STATUS_LOADING,
+ FW_STATUS_DONE,
+ FW_STATUS_ABORTED,
+};
+
+/*
+ * Concurrent request_firmware() for the same firmware need to be
+ * serialized. struct fw_state is simple state machine which hold the
+ * state of the firmware loading.
+ */
+struct fw_state {
+ struct completion completion;
+ enum fw_status status;
+};
+
+struct fw_priv {
+ struct kref ref;
+ struct list_head list;
+ struct firmware_cache *fwc;
+ struct fw_state fw_st;
+ void *data;
+ size_t size;
+ size_t allocated_size;
+#ifdef CONFIG_FW_LOADER_USER_HELPER
+ bool is_paged_buf;
+ bool need_uevent;
+ struct page **pages;
+ int nr_pages;
+ int page_array_size;
+ struct list_head pending_list;
+#endif
+ const char *fw_name;
+};
+
+extern struct mutex fw_lock;
+
+static inline bool __fw_state_check(struct fw_priv *fw_priv,
+ enum fw_status status)
+{
+ struct fw_state *fw_st = &fw_priv->fw_st;
+
+ return fw_st->status == status;
+}
+
+static inline int __fw_state_wait_common(struct fw_priv *fw_priv, long timeout)
+{
+ struct fw_state *fw_st = &fw_priv->fw_st;
+ long ret;
+
+ ret = wait_for_completion_killable_timeout(&fw_st->completion, timeout);
+ if (ret != 0 && fw_st->status == FW_STATUS_ABORTED)
+ return -ENOENT;
+ if (!ret)
+ return -ETIMEDOUT;
+
+ return ret < 0 ? ret : 0;
+}
+
+static inline void __fw_state_set(struct fw_priv *fw_priv,
+ enum fw_status status)
+{
+ struct fw_state *fw_st = &fw_priv->fw_st;
+
+ WRITE_ONCE(fw_st->status, status);
+
+ if (status == FW_STATUS_DONE || status == FW_STATUS_ABORTED)
+ complete_all(&fw_st->completion);
+}
+
+static inline void fw_state_aborted(struct fw_priv *fw_priv)
+{
+ __fw_state_set(fw_priv, FW_STATUS_ABORTED);
+}
+
+static inline bool fw_state_is_aborted(struct fw_priv *fw_priv)
+{
+ return __fw_state_check(fw_priv, FW_STATUS_ABORTED);
+}
+
+static inline void fw_state_start(struct fw_priv *fw_priv)
+{
+ __fw_state_set(fw_priv, FW_STATUS_LOADING);
+}
+
+static inline void fw_state_done(struct fw_priv *fw_priv)
+{
+ __fw_state_set(fw_priv, FW_STATUS_DONE);
+}
+
+int assign_fw(struct firmware *fw, struct device *device,
+ unsigned int opt_flags);
+
+#endif /* __FIRMWARE_LOADER_H */
diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c
new file mode 100644
index 000000000000..eb34089e4299
--- /dev/null
+++ b/drivers/base/firmware_loader/main.c
@@ -0,0 +1,1243 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * main.c - Multi purpose firmware loading support
+ *
+ * Copyright (c) 2003 Manuel Estrada Sainz
+ *
+ * Please see Documentation/firmware_class/ for more information.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/capability.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/highmem.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/file.h>
+#include <linux/list.h>
+#include <linux/fs.h>
+#include <linux/async.h>
+#include <linux/pm.h>
+#include <linux/suspend.h>
+#include <linux/syscore_ops.h>
+#include <linux/reboot.h>
+#include <linux/security.h>
+
+#include <generated/utsrelease.h>
+
+#include "../base.h"
+#include "firmware.h"
+#include "fallback.h"
+
+MODULE_AUTHOR("Manuel Estrada Sainz");
+MODULE_DESCRIPTION("Multi purpose firmware loading support");
+MODULE_LICENSE("GPL");
+
+struct firmware_cache {
+ /* firmware_buf instance will be added into the below list */
+ spinlock_t lock;
+ struct list_head head;
+ int state;
+
+#ifdef CONFIG_PM_SLEEP
+ /*
+ * Names of firmware images which have been cached successfully
+ * will be added into the below list so that device uncache
+ * helper can trace which firmware images have been cached
+ * before.
+ */
+ spinlock_t name_lock;
+ struct list_head fw_names;
+
+ struct delayed_work work;
+
+ struct notifier_block pm_notify;
+#endif
+};
+
+struct fw_cache_entry {
+ struct list_head list;
+ const char *name;
+};
+
+struct fw_name_devm {
+ unsigned long magic;
+ const char *name;
+};
+
+static inline struct fw_priv *to_fw_priv(struct kref *ref)
+{
+ return container_of(ref, struct fw_priv, ref);
+}
+
+#define FW_LOADER_NO_CACHE 0
+#define FW_LOADER_START_CACHE 1
+
+/* fw_lock could be moved to 'struct fw_sysfs' but since it is just
+ * guarding for corner cases a global lock should be OK */
+DEFINE_MUTEX(fw_lock);
+
+static struct firmware_cache fw_cache;
+
+/* Builtin firmware support */
+
+#ifdef CONFIG_FW_LOADER
+
+extern struct builtin_fw __start_builtin_fw[];
+extern struct builtin_fw __end_builtin_fw[];
+
+static void fw_copy_to_prealloc_buf(struct firmware *fw,
+ void *buf, size_t size)
+{
+ if (!buf || size < fw->size)
+ return;
+ memcpy(buf, fw->data, fw->size);
+}
+
+static bool fw_get_builtin_firmware(struct firmware *fw, const char *name,
+ void *buf, size_t size)
+{
+ struct builtin_fw *b_fw;
+
+ for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++) {
+ if (strcmp(name, b_fw->name) == 0) {
+ fw->size = b_fw->size;
+ fw->data = b_fw->data;
+ fw_copy_to_prealloc_buf(fw, buf, size);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool fw_is_builtin_firmware(const struct firmware *fw)
+{
+ struct builtin_fw *b_fw;
+
+ for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++)
+ if (fw->data == b_fw->data)
+ return true;
+
+ return false;
+}
+
+#else /* Module case - no builtin firmware support */
+
+static inline bool fw_get_builtin_firmware(struct firmware *fw,
+ const char *name, void *buf,
+ size_t size)
+{
+ return false;
+}
+
+static inline bool fw_is_builtin_firmware(const struct firmware *fw)
+{
+ return false;
+}
+#endif
+
+static void fw_state_init(struct fw_priv *fw_priv)
+{
+ struct fw_state *fw_st = &fw_priv->fw_st;
+
+ init_completion(&fw_st->completion);
+ fw_st->status = FW_STATUS_UNKNOWN;
+}
+
+static inline int fw_state_wait(struct fw_priv *fw_priv)
+{
+ return __fw_state_wait_common(fw_priv, MAX_SCHEDULE_TIMEOUT);
+}
+
+static int fw_cache_piggyback_on_request(const char *name);
+
+static struct fw_priv *__allocate_fw_priv(const char *fw_name,
+ struct firmware_cache *fwc,
+ void *dbuf, size_t size)
+{
+ struct fw_priv *fw_priv;
+
+ fw_priv = kzalloc(sizeof(*fw_priv), GFP_ATOMIC);
+ if (!fw_priv)
+ return NULL;
+
+ fw_priv->fw_name = kstrdup_const(fw_name, GFP_ATOMIC);
+ if (!fw_priv->fw_name) {
+ kfree(fw_priv);
+ return NULL;
+ }
+
+ kref_init(&fw_priv->ref);
+ fw_priv->fwc = fwc;
+ fw_priv->data = dbuf;
+ fw_priv->allocated_size = size;
+ fw_state_init(fw_priv);
+#ifdef CONFIG_FW_LOADER_USER_HELPER
+ INIT_LIST_HEAD(&fw_priv->pending_list);
+#endif
+
+ pr_debug("%s: fw-%s fw_priv=%p\n", __func__, fw_name, fw_priv);
+
+ return fw_priv;
+}
+
+static struct fw_priv *__lookup_fw_priv(const char *fw_name)
+{
+ struct fw_priv *tmp;
+ struct firmware_cache *fwc = &fw_cache;
+
+ list_for_each_entry(tmp, &fwc->head, list)
+ if (!strcmp(tmp->fw_name, fw_name))
+ return tmp;
+ return NULL;
+}
+
+/* Returns 1 for batching firmware requests with the same name */
+static int alloc_lookup_fw_priv(const char *fw_name,
+ struct firmware_cache *fwc,
+ struct fw_priv **fw_priv, void *dbuf,
+ size_t size)
+{
+ struct fw_priv *tmp;
+
+ spin_lock(&fwc->lock);
+ tmp = __lookup_fw_priv(fw_name);
+ if (tmp) {
+ kref_get(&tmp->ref);
+ spin_unlock(&fwc->lock);
+ *fw_priv = tmp;
+ pr_debug("batched request - sharing the same struct fw_priv and lookup for multiple requests\n");
+ return 1;
+ }
+ tmp = __allocate_fw_priv(fw_name, fwc, dbuf, size);
+ if (tmp)
+ list_add(&tmp->list, &fwc->head);
+ spin_unlock(&fwc->lock);
+
+ *fw_priv = tmp;
+
+ return tmp ? 0 : -ENOMEM;
+}
+
+static void __free_fw_priv(struct kref *ref)
+ __releases(&fwc->lock)
+{
+ struct fw_priv *fw_priv = to_fw_priv(ref);
+ struct firmware_cache *fwc = fw_priv->fwc;
+
+ pr_debug("%s: fw-%s fw_priv=%p data=%p size=%u\n",
+ __func__, fw_priv->fw_name, fw_priv, fw_priv->data,
+ (unsigned int)fw_priv->size);
+
+ list_del(&fw_priv->list);
+ spin_unlock(&fwc->lock);
+
+#ifdef CONFIG_FW_LOADER_USER_HELPER
+ if (fw_priv->is_paged_buf) {
+ int i;
+ vunmap(fw_priv->data);
+ for (i = 0; i < fw_priv->nr_pages; i++)
+ __free_page(fw_priv->pages[i]);
+ vfree(fw_priv->pages);
+ } else
+#endif
+ if (!fw_priv->allocated_size)
+ vfree(fw_priv->data);
+ kfree_const(fw_priv->fw_name);
+ kfree(fw_priv);
+}
+
+static void free_fw_priv(struct fw_priv *fw_priv)
+{
+ struct firmware_cache *fwc = fw_priv->fwc;
+ spin_lock(&fwc->lock);
+ if (!kref_put(&fw_priv->ref, __free_fw_priv))
+ spin_unlock(&fwc->lock);
+}
+
+/* direct firmware loading support */
+static char fw_path_para[256];
+static const char * const fw_path[] = {
+ fw_path_para,
+ "/lib/firmware/updates/" UTS_RELEASE,
+ "/lib/firmware/updates",
+ "/lib/firmware/" UTS_RELEASE,
+ "/lib/firmware"
+};
+
+/*
+ * Typical usage is that passing 'firmware_class.path=$CUSTOMIZED_PATH'
+ * from kernel command line because firmware_class is generally built in
+ * kernel instead of module.
+ */
+module_param_string(path, fw_path_para, sizeof(fw_path_para), 0644);
+MODULE_PARM_DESC(path, "customized firmware image search path with a higher priority than default path");
+
+static int
+fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv)
+{
+ loff_t size;
+ int i, len;
+ int rc = -ENOENT;
+ char *path;
+ enum kernel_read_file_id id = READING_FIRMWARE;
+ size_t msize = INT_MAX;
+
+ /* Already populated data member means we're loading into a buffer */
+ if (fw_priv->data) {
+ id = READING_FIRMWARE_PREALLOC_BUFFER;
+ msize = fw_priv->allocated_size;
+ }
+
+ path = __getname();
+ if (!path)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(fw_path); i++) {
+ /* skip the unset customized path */
+ if (!fw_path[i][0])
+ continue;
+
+ len = snprintf(path, PATH_MAX, "%s/%s",
+ fw_path[i], fw_priv->fw_name);
+ if (len >= PATH_MAX) {
+ rc = -ENAMETOOLONG;
+ break;
+ }
+
+ fw_priv->size = 0;
+ rc = kernel_read_file_from_path(path, &fw_priv->data, &size,
+ msize, id);
+ if (rc) {
+ if (rc == -ENOENT)
+ dev_dbg(device, "loading %s failed with error %d\n",
+ path, rc);
+ else
+ dev_warn(device, "loading %s failed with error %d\n",
+ path, rc);
+ continue;
+ }
+ dev_dbg(device, "direct-loading %s\n", fw_priv->fw_name);
+ fw_priv->size = size;
+ fw_state_done(fw_priv);
+ break;
+ }
+ __putname(path);
+
+ return rc;
+}
+
+/* firmware holds the ownership of pages */
+static void firmware_free_data(const struct firmware *fw)
+{
+ /* Loaded directly? */
+ if (!fw->priv) {
+ vfree(fw->data);
+ return;
+ }
+ free_fw_priv(fw->priv);
+}
+
+/* store the pages buffer info firmware from buf */
+static void fw_set_page_data(struct fw_priv *fw_priv, struct firmware *fw)
+{
+ fw->priv = fw_priv;
+#ifdef CONFIG_FW_LOADER_USER_HELPER
+ fw->pages = fw_priv->pages;
+#endif
+ fw->size = fw_priv->size;
+ fw->data = fw_priv->data;
+
+ pr_debug("%s: fw-%s fw_priv=%p data=%p size=%u\n",
+ __func__, fw_priv->fw_name, fw_priv, fw_priv->data,
+ (unsigned int)fw_priv->size);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static void fw_name_devm_release(struct device *dev, void *res)
+{
+ struct fw_name_devm *fwn = res;
+
+ if (fwn->magic == (unsigned long)&fw_cache)
+ pr_debug("%s: fw_name-%s devm-%p released\n",
+ __func__, fwn->name, res);
+ kfree_const(fwn->name);
+}
+
+static int fw_devm_match(struct device *dev, void *res,
+ void *match_data)
+{
+ struct fw_name_devm *fwn = res;
+
+ return (fwn->magic == (unsigned long)&fw_cache) &&
+ !strcmp(fwn->name, match_data);
+}
+
+static struct fw_name_devm *fw_find_devm_name(struct device *dev,
+ const char *name)
+{
+ struct fw_name_devm *fwn;
+
+ fwn = devres_find(dev, fw_name_devm_release,
+ fw_devm_match, (void *)name);
+ return fwn;
+}
+
+static bool fw_cache_is_setup(struct device *dev, const char *name)
+{
+ struct fw_name_devm *fwn;
+
+ fwn = fw_find_devm_name(dev, name);
+ if (fwn)
+ return true;
+
+ return false;
+}
+
+/* add firmware name into devres list */
+static int fw_add_devm_name(struct device *dev, const char *name)
+{
+ struct fw_name_devm *fwn;
+
+ if (fw_cache_is_setup(dev, name))
+ return 0;
+
+ fwn = devres_alloc(fw_name_devm_release, sizeof(struct fw_name_devm),
+ GFP_KERNEL);
+ if (!fwn)
+ return -ENOMEM;
+ fwn->name = kstrdup_const(name, GFP_KERNEL);
+ if (!fwn->name) {
+ devres_free(fwn);
+ return -ENOMEM;
+ }
+
+ fwn->magic = (unsigned long)&fw_cache;
+ devres_add(dev, fwn);
+
+ return 0;
+}
+#else
+static bool fw_cache_is_setup(struct device *dev, const char *name)
+{
+ return false;
+}
+
+static int fw_add_devm_name(struct device *dev, const char *name)
+{
+ return 0;
+}
+#endif
+
+int assign_fw(struct firmware *fw, struct device *device,
+ unsigned int opt_flags)
+{
+ struct fw_priv *fw_priv = fw->priv;
+ int ret;
+
+ mutex_lock(&fw_lock);
+ if (!fw_priv->size || fw_state_is_aborted(fw_priv)) {
+ mutex_unlock(&fw_lock);
+ return -ENOENT;
+ }
+
+ /*
+ * add firmware name into devres list so that we can auto cache
+ * and uncache firmware for device.
+ *
+ * device may has been deleted already, but the problem
+ * should be fixed in devres or driver core.
+ */
+ /* don't cache firmware handled without uevent */
+ if (device && (opt_flags & FW_OPT_UEVENT) &&
+ !(opt_flags & FW_OPT_NOCACHE)) {
+ ret = fw_add_devm_name(device, fw_priv->fw_name);
+ if (ret) {
+ mutex_unlock(&fw_lock);
+ return ret;
+ }
+ }
+
+ /*
+ * After caching firmware image is started, let it piggyback
+ * on request firmware.
+ */
+ if (!(opt_flags & FW_OPT_NOCACHE) &&
+ fw_priv->fwc->state == FW_LOADER_START_CACHE) {
+ if (fw_cache_piggyback_on_request(fw_priv->fw_name))
+ kref_get(&fw_priv->ref);
+ }
+
+ /* pass the pages buffer to driver at the last minute */
+ fw_set_page_data(fw_priv, fw);
+ mutex_unlock(&fw_lock);
+ return 0;
+}
+
+/* prepare firmware and firmware_buf structs;
+ * return 0 if a firmware is already assigned, 1 if need to load one,
+ * or a negative error code
+ */
+static int
+_request_firmware_prepare(struct firmware **firmware_p, const char *name,
+ struct device *device, void *dbuf, size_t size)
+{
+ struct firmware *firmware;
+ struct fw_priv *fw_priv;
+ int ret;
+
+ *firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL);
+ if (!firmware) {
+ dev_err(device, "%s: kmalloc(struct firmware) failed\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ if (fw_get_builtin_firmware(firmware, name, dbuf, size)) {
+ dev_dbg(device, "using built-in %s\n", name);
+ return 0; /* assigned */
+ }
+
+ ret = alloc_lookup_fw_priv(name, &fw_cache, &fw_priv, dbuf, size);
+
+ /*
+ * bind with 'priv' now to avoid warning in failure path
+ * of requesting firmware.
+ */
+ firmware->priv = fw_priv;
+
+ if (ret > 0) {
+ ret = fw_state_wait(fw_priv);
+ if (!ret) {
+ fw_set_page_data(fw_priv, firmware);
+ return 0; /* assigned */
+ }
+ }
+
+ if (ret < 0)
+ return ret;
+ return 1; /* need to load */
+}
+
+/*
+ * Batched requests need only one wake, we need to do this step last due to the
+ * fallback mechanism. The buf is protected with kref_get(), and it won't be
+ * released until the last user calls release_firmware().
+ *
+ * Failed batched requests are possible as well, in such cases we just share
+ * the struct fw_priv and won't release it until all requests are woken
+ * and have gone through this same path.
+ */
+static void fw_abort_batch_reqs(struct firmware *fw)
+{
+ struct fw_priv *fw_priv;
+
+ /* Loaded directly? */
+ if (!fw || !fw->priv)
+ return;
+
+ fw_priv = fw->priv;
+ if (!fw_state_is_aborted(fw_priv))
+ fw_state_aborted(fw_priv);
+}
+
+/* called from request_firmware() and request_firmware_work_func() */
+static int
+_request_firmware(const struct firmware **firmware_p, const char *name,
+ struct device *device, void *buf, size_t size,
+ unsigned int opt_flags)
+{
+ struct firmware *fw = NULL;
+ int ret;
+
+ if (!firmware_p)
+ return -EINVAL;
+
+ if (!name || name[0] == '\0') {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = _request_firmware_prepare(&fw, name, device, buf, size);
+ if (ret <= 0) /* error or already assigned */
+ goto out;
+
+ ret = fw_get_filesystem_firmware(device, fw->priv);
+ if (ret) {
+ if (!(opt_flags & FW_OPT_NO_WARN))
+ dev_warn(device,
+ "Direct firmware load for %s failed with error %d\n",
+ name, ret);
+ ret = fw_sysfs_fallback(fw, name, device, opt_flags, ret);
+ } else
+ ret = assign_fw(fw, device, opt_flags);
+
+ out:
+ if (ret < 0) {
+ fw_abort_batch_reqs(fw);
+ release_firmware(fw);
+ fw = NULL;
+ }
+
+ *firmware_p = fw;
+ return ret;
+}
+
+/**
+ * request_firmware: - send firmware request and wait for it
+ * @firmware_p: pointer to firmware image
+ * @name: name of firmware file
+ * @device: device for which firmware is being loaded
+ *
+ * @firmware_p will be used to return a firmware image by the name
+ * of @name for device @device.
+ *
+ * Should be called from user context where sleeping is allowed.
+ *
+ * @name will be used as $FIRMWARE in the uevent environment and
+ * should be distinctive enough not to be confused with any other
+ * firmware image for this or any other device.
+ *
+ * Caller must hold the reference count of @device.
+ *
+ * The function can be called safely inside device's suspend and
+ * resume callback.
+ **/
+int
+request_firmware(const struct firmware **firmware_p, const char *name,
+ struct device *device)
+{
+ int ret;
+
+ /* Need to pin this module until return */
+ __module_get(THIS_MODULE);
+ ret = _request_firmware(firmware_p, name, device, NULL, 0,
+ FW_OPT_UEVENT);
+ module_put(THIS_MODULE);
+ return ret;
+}
+EXPORT_SYMBOL(request_firmware);
+
+/**
+ * request_firmware_direct: - load firmware directly without usermode helper
+ * @firmware_p: pointer to firmware image
+ * @name: name of firmware file
+ * @device: device for which firmware is being loaded
+ *
+ * This function works pretty much like request_firmware(), but this doesn't
+ * fall back to usermode helper even if the firmware couldn't be loaded
+ * directly from fs. Hence it's useful for loading optional firmwares, which
+ * aren't always present, without extra long timeouts of udev.
+ **/
+int request_firmware_direct(const struct firmware **firmware_p,
+ const char *name, struct device *device)
+{
+ int ret;
+
+ __module_get(THIS_MODULE);
+ ret = _request_firmware(firmware_p, name, device, NULL, 0,
+ FW_OPT_UEVENT | FW_OPT_NO_WARN |
+ FW_OPT_NOFALLBACK);
+ module_put(THIS_MODULE);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(request_firmware_direct);
+
+/**
+ * firmware_request_cache: - cache firmware for suspend so resume can use it
+ * @name: name of firmware file
+ * @device: device for which firmware should be cached for
+ *
+ * There are some devices with an optimization that enables the device to not
+ * require loading firmware on system reboot. This optimization may still
+ * require the firmware present on resume from suspend. This routine can be
+ * used to ensure the firmware is present on resume from suspend in these
+ * situations. This helper is not compatible with drivers which use
+ * request_firmware_into_buf() or request_firmware_nowait() with no uevent set.
+ **/
+int firmware_request_cache(struct device *device, const char *name)
+{
+ int ret;
+
+ mutex_lock(&fw_lock);
+ ret = fw_add_devm_name(device, name);
+ mutex_unlock(&fw_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(firmware_request_cache);
+
+/**
+ * request_firmware_into_buf - load firmware into a previously allocated buffer
+ * @firmware_p: pointer to firmware image
+ * @name: name of firmware file
+ * @device: device for which firmware is being loaded and DMA region allocated
+ * @buf: address of buffer to load firmware into
+ * @size: size of buffer
+ *
+ * This function works pretty much like request_firmware(), but it doesn't
+ * allocate a buffer to hold the firmware data. Instead, the firmware
+ * is loaded directly into the buffer pointed to by @buf and the @firmware_p
+ * data member is pointed at @buf.
+ *
+ * This function doesn't cache firmware either.
+ */
+int
+request_firmware_into_buf(const struct firmware **firmware_p, const char *name,
+ struct device *device, void *buf, size_t size)
+{
+ int ret;
+
+ if (fw_cache_is_setup(device, name))
+ return -EOPNOTSUPP;
+
+ __module_get(THIS_MODULE);
+ ret = _request_firmware(firmware_p, name, device, buf, size,
+ FW_OPT_UEVENT | FW_OPT_NOCACHE);
+ module_put(THIS_MODULE);
+ return ret;
+}
+EXPORT_SYMBOL(request_firmware_into_buf);
+
+/**
+ * release_firmware: - release the resource associated with a firmware image
+ * @fw: firmware resource to release
+ **/
+void release_firmware(const struct firmware *fw)
+{
+ if (fw) {
+ if (!fw_is_builtin_firmware(fw))
+ firmware_free_data(fw);
+ kfree(fw);
+ }
+}
+EXPORT_SYMBOL(release_firmware);
+
+/* Async support */
+struct firmware_work {
+ struct work_struct work;
+ struct module *module;
+ const char *name;
+ struct device *device;
+ void *context;
+ void (*cont)(const struct firmware *fw, void *context);
+ unsigned int opt_flags;
+};
+
+static void request_firmware_work_func(struct work_struct *work)
+{
+ struct firmware_work *fw_work;
+ const struct firmware *fw;
+
+ fw_work = container_of(work, struct firmware_work, work);
+
+ _request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0,
+ fw_work->opt_flags);
+ fw_work->cont(fw, fw_work->context);
+ put_device(fw_work->device); /* taken in request_firmware_nowait() */
+
+ module_put(fw_work->module);
+ kfree_const(fw_work->name);
+ kfree(fw_work);
+}
+
+/**
+ * request_firmware_nowait - asynchronous version of request_firmware
+ * @module: module requesting the firmware
+ * @uevent: sends uevent to copy the firmware image if this flag
+ * is non-zero else the firmware copy must be done manually.
+ * @name: name of firmware file
+ * @device: device for which firmware is being loaded
+ * @gfp: allocation flags
+ * @context: will be passed over to @cont, and
+ * @fw may be %NULL if firmware request fails.
+ * @cont: function will be called asynchronously when the firmware
+ * request is over.
+ *
+ * Caller must hold the reference count of @device.
+ *
+ * Asynchronous variant of request_firmware() for user contexts:
+ * - sleep for as small periods as possible since it may
+ * increase kernel boot time of built-in device drivers
+ * requesting firmware in their ->probe() methods, if
+ * @gfp is GFP_KERNEL.
+ *
+ * - can't sleep at all if @gfp is GFP_ATOMIC.
+ **/
+int
+request_firmware_nowait(
+ struct module *module, bool uevent,
+ const char *name, struct device *device, gfp_t gfp, void *context,
+ void (*cont)(const struct firmware *fw, void *context))
+{
+ struct firmware_work *fw_work;
+
+ fw_work = kzalloc(sizeof(struct firmware_work), gfp);
+ if (!fw_work)
+ return -ENOMEM;
+
+ fw_work->module = module;
+ fw_work->name = kstrdup_const(name, gfp);
+ if (!fw_work->name) {
+ kfree(fw_work);
+ return -ENOMEM;
+ }
+ fw_work->device = device;
+ fw_work->context = context;
+ fw_work->cont = cont;
+ fw_work->opt_flags = FW_OPT_NOWAIT |
+ (uevent ? FW_OPT_UEVENT : FW_OPT_USERHELPER);
+
+ if (!uevent && fw_cache_is_setup(device, name)) {
+ kfree_const(fw_work->name);
+ kfree(fw_work);
+ return -EOPNOTSUPP;
+ }
+
+ if (!try_module_get(module)) {
+ kfree_const(fw_work->name);
+ kfree(fw_work);
+ return -EFAULT;
+ }
+
+ get_device(fw_work->device);
+ INIT_WORK(&fw_work->work, request_firmware_work_func);
+ schedule_work(&fw_work->work);
+ return 0;
+}
+EXPORT_SYMBOL(request_firmware_nowait);
+
+#ifdef CONFIG_PM_SLEEP
+static ASYNC_DOMAIN_EXCLUSIVE(fw_cache_domain);
+
+/**
+ * cache_firmware - cache one firmware image in kernel memory space
+ * @fw_name: the firmware image name
+ *
+ * Cache firmware in kernel memory so that drivers can use it when
+ * system isn't ready for them to request firmware image from userspace.
+ * Once it returns successfully, driver can use request_firmware or its
+ * nowait version to get the cached firmware without any interacting
+ * with userspace
+ *
+ * Return 0 if the firmware image has been cached successfully
+ * Return !0 otherwise
+ *
+ */
+static int cache_firmware(const char *fw_name)
+{
+ int ret;
+ const struct firmware *fw;
+
+ pr_debug("%s: %s\n", __func__, fw_name);
+
+ ret = request_firmware(&fw, fw_name, NULL);
+ if (!ret)
+ kfree(fw);
+
+ pr_debug("%s: %s ret=%d\n", __func__, fw_name, ret);
+
+ return ret;
+}
+
+static struct fw_priv *lookup_fw_priv(const char *fw_name)
+{
+ struct fw_priv *tmp;
+ struct firmware_cache *fwc = &fw_cache;
+
+ spin_lock(&fwc->lock);
+ tmp = __lookup_fw_priv(fw_name);
+ spin_unlock(&fwc->lock);
+
+ return tmp;
+}
+
+/**
+ * uncache_firmware - remove one cached firmware image
+ * @fw_name: the firmware image name
+ *
+ * Uncache one firmware image which has been cached successfully
+ * before.
+ *
+ * Return 0 if the firmware cache has been removed successfully
+ * Return !0 otherwise
+ *
+ */
+static int uncache_firmware(const char *fw_name)
+{
+ struct fw_priv *fw_priv;
+ struct firmware fw;
+
+ pr_debug("%s: %s\n", __func__, fw_name);
+
+ if (fw_get_builtin_firmware(&fw, fw_name, NULL, 0))
+ return 0;
+
+ fw_priv = lookup_fw_priv(fw_name);
+ if (fw_priv) {
+ free_fw_priv(fw_priv);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static struct fw_cache_entry *alloc_fw_cache_entry(const char *name)
+{
+ struct fw_cache_entry *fce;
+
+ fce = kzalloc(sizeof(*fce), GFP_ATOMIC);
+ if (!fce)
+ goto exit;
+
+ fce->name = kstrdup_const(name, GFP_ATOMIC);
+ if (!fce->name) {
+ kfree(fce);
+ fce = NULL;
+ goto exit;
+ }
+exit:
+ return fce;
+}
+
+static int __fw_entry_found(const char *name)
+{
+ struct firmware_cache *fwc = &fw_cache;
+ struct fw_cache_entry *fce;
+
+ list_for_each_entry(fce, &fwc->fw_names, list) {
+ if (!strcmp(fce->name, name))
+ return 1;
+ }
+ return 0;
+}
+
+static int fw_cache_piggyback_on_request(const char *name)
+{
+ struct firmware_cache *fwc = &fw_cache;
+ struct fw_cache_entry *fce;
+ int ret = 0;
+
+ spin_lock(&fwc->name_lock);
+ if (__fw_entry_found(name))
+ goto found;
+
+ fce = alloc_fw_cache_entry(name);
+ if (fce) {
+ ret = 1;
+ list_add(&fce->list, &fwc->fw_names);
+ pr_debug("%s: fw: %s\n", __func__, name);
+ }
+found:
+ spin_unlock(&fwc->name_lock);
+ return ret;
+}
+
+static void free_fw_cache_entry(struct fw_cache_entry *fce)
+{
+ kfree_const(fce->name);
+ kfree(fce);
+}
+
+static void __async_dev_cache_fw_image(void *fw_entry,
+ async_cookie_t cookie)
+{
+ struct fw_cache_entry *fce = fw_entry;
+ struct firmware_cache *fwc = &fw_cache;
+ int ret;
+
+ ret = cache_firmware(fce->name);
+ if (ret) {
+ spin_lock(&fwc->name_lock);
+ list_del(&fce->list);
+ spin_unlock(&fwc->name_lock);
+
+ free_fw_cache_entry(fce);
+ }
+}
+
+/* called with dev->devres_lock held */
+static void dev_create_fw_entry(struct device *dev, void *res,
+ void *data)
+{
+ struct fw_name_devm *fwn = res;
+ const char *fw_name = fwn->name;
+ struct list_head *head = data;
+ struct fw_cache_entry *fce;
+
+ fce = alloc_fw_cache_entry(fw_name);
+ if (fce)
+ list_add(&fce->list, head);
+}
+
+static int devm_name_match(struct device *dev, void *res,
+ void *match_data)
+{
+ struct fw_name_devm *fwn = res;
+ return (fwn->magic == (unsigned long)match_data);
+}
+
+static void dev_cache_fw_image(struct device *dev, void *data)
+{
+ LIST_HEAD(todo);
+ struct fw_cache_entry *fce;
+ struct fw_cache_entry *fce_next;
+ struct firmware_cache *fwc = &fw_cache;
+
+ devres_for_each_res(dev, fw_name_devm_release,
+ devm_name_match, &fw_cache,
+ dev_create_fw_entry, &todo);
+
+ list_for_each_entry_safe(fce, fce_next, &todo, list) {
+ list_del(&fce->list);
+
+ spin_lock(&fwc->name_lock);
+ /* only one cache entry for one firmware */
+ if (!__fw_entry_found(fce->name)) {
+ list_add(&fce->list, &fwc->fw_names);
+ } else {
+ free_fw_cache_entry(fce);
+ fce = NULL;
+ }
+ spin_unlock(&fwc->name_lock);
+
+ if (fce)
+ async_schedule_domain(__async_dev_cache_fw_image,
+ (void *)fce,
+ &fw_cache_domain);
+ }
+}
+
+static void __device_uncache_fw_images(void)
+{
+ struct firmware_cache *fwc = &fw_cache;
+ struct fw_cache_entry *fce;
+
+ spin_lock(&fwc->name_lock);
+ while (!list_empty(&fwc->fw_names)) {
+ fce = list_entry(fwc->fw_names.next,
+ struct fw_cache_entry, list);
+ list_del(&fce->list);
+ spin_unlock(&fwc->name_lock);
+
+ uncache_firmware(fce->name);
+ free_fw_cache_entry(fce);
+
+ spin_lock(&fwc->name_lock);
+ }
+ spin_unlock(&fwc->name_lock);
+}
+
+/**
+ * device_cache_fw_images - cache devices' firmware
+ *
+ * If one device called request_firmware or its nowait version
+ * successfully before, the firmware names are recored into the
+ * device's devres link list, so device_cache_fw_images can call
+ * cache_firmware() to cache these firmwares for the device,
+ * then the device driver can load its firmwares easily at
+ * time when system is not ready to complete loading firmware.
+ */
+static void device_cache_fw_images(void)
+{
+ struct firmware_cache *fwc = &fw_cache;
+ DEFINE_WAIT(wait);
+
+ pr_debug("%s\n", __func__);
+
+ /* cancel uncache work */
+ cancel_delayed_work_sync(&fwc->work);
+
+ fw_fallback_set_cache_timeout();
+
+ mutex_lock(&fw_lock);
+ fwc->state = FW_LOADER_START_CACHE;
+ dpm_for_each_dev(NULL, dev_cache_fw_image);
+ mutex_unlock(&fw_lock);
+
+ /* wait for completion of caching firmware for all devices */
+ async_synchronize_full_domain(&fw_cache_domain);
+
+ fw_fallback_set_default_timeout();
+}
+
+/**
+ * device_uncache_fw_images - uncache devices' firmware
+ *
+ * uncache all firmwares which have been cached successfully
+ * by device_uncache_fw_images earlier
+ */
+static void device_uncache_fw_images(void)
+{
+ pr_debug("%s\n", __func__);
+ __device_uncache_fw_images();
+}
+
+static void device_uncache_fw_images_work(struct work_struct *work)
+{
+ device_uncache_fw_images();
+}
+
+/**
+ * device_uncache_fw_images_delay - uncache devices firmwares
+ * @delay: number of milliseconds to delay uncache device firmwares
+ *
+ * uncache all devices's firmwares which has been cached successfully
+ * by device_cache_fw_images after @delay milliseconds.
+ */
+static void device_uncache_fw_images_delay(unsigned long delay)
+{
+ queue_delayed_work(system_power_efficient_wq, &fw_cache.work,
+ msecs_to_jiffies(delay));
+}
+
+static int fw_pm_notify(struct notifier_block *notify_block,
+ unsigned long mode, void *unused)
+{
+ switch (mode) {
+ case PM_HIBERNATION_PREPARE:
+ case PM_SUSPEND_PREPARE:
+ case PM_RESTORE_PREPARE:
+ /*
+ * kill pending fallback requests with a custom fallback
+ * to avoid stalling suspend.
+ */
+ kill_pending_fw_fallback_reqs(true);
+ device_cache_fw_images();
+ break;
+
+ case PM_POST_SUSPEND:
+ case PM_POST_HIBERNATION:
+ case PM_POST_RESTORE:
+ /*
+ * In case that system sleep failed and syscore_suspend is
+ * not called.
+ */
+ mutex_lock(&fw_lock);
+ fw_cache.state = FW_LOADER_NO_CACHE;
+ mutex_unlock(&fw_lock);
+
+ device_uncache_fw_images_delay(10 * MSEC_PER_SEC);
+ break;
+ }
+
+ return 0;
+}
+
+/* stop caching firmware once syscore_suspend is reached */
+static int fw_suspend(void)
+{
+ fw_cache.state = FW_LOADER_NO_CACHE;
+ return 0;
+}
+
+static struct syscore_ops fw_syscore_ops = {
+ .suspend = fw_suspend,
+};
+
+static int __init register_fw_pm_ops(void)
+{
+ int ret;
+
+ spin_lock_init(&fw_cache.name_lock);
+ INIT_LIST_HEAD(&fw_cache.fw_names);
+
+ INIT_DELAYED_WORK(&fw_cache.work,
+ device_uncache_fw_images_work);
+
+ fw_cache.pm_notify.notifier_call = fw_pm_notify;
+ ret = register_pm_notifier(&fw_cache.pm_notify);
+ if (ret)
+ return ret;
+
+ register_syscore_ops(&fw_syscore_ops);
+
+ return ret;
+}
+
+static inline void unregister_fw_pm_ops(void)
+{
+ unregister_syscore_ops(&fw_syscore_ops);
+ unregister_pm_notifier(&fw_cache.pm_notify);
+}
+#else
+static int fw_cache_piggyback_on_request(const char *name)
+{
+ return 0;
+}
+static inline int register_fw_pm_ops(void)
+{
+ return 0;
+}
+static inline void unregister_fw_pm_ops(void)
+{
+}
+#endif
+
+static void __init fw_cache_init(void)
+{
+ spin_lock_init(&fw_cache.lock);
+ INIT_LIST_HEAD(&fw_cache.head);
+ fw_cache.state = FW_LOADER_NO_CACHE;
+}
+
+static int fw_shutdown_notify(struct notifier_block *unused1,
+ unsigned long unused2, void *unused3)
+{
+ /*
+ * Kill all pending fallback requests to avoid both stalling shutdown,
+ * and avoid a deadlock with the usermode_lock.
+ */
+ kill_pending_fw_fallback_reqs(false);
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block fw_shutdown_nb = {
+ .notifier_call = fw_shutdown_notify,
+};
+
+static int __init firmware_class_init(void)
+{
+ int ret;
+
+ /* No need to unfold these on exit */
+ fw_cache_init();
+
+ ret = register_fw_pm_ops();
+ if (ret)
+ return ret;
+
+ ret = register_reboot_notifier(&fw_shutdown_nb);
+ if (ret)
+ goto out;
+
+ return register_sysfs_loader();
+
+out:
+ unregister_fw_pm_ops();
+ return ret;
+}
+
+static void __exit firmware_class_exit(void)
+{
+ unregister_fw_pm_ops();
+ unregister_reboot_notifier(&fw_shutdown_nb);
+ unregister_sysfs_loader();
+}
+
+fs_initcall(firmware_class_init);
+module_exit(firmware_class_exit);
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index fe4b24f05f6a..bffe8616bd55 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -187,13 +187,14 @@ int memory_isolate_notify(unsigned long val, void *v)
}
/*
- * The probe routines leave the pages reserved, just as the bootmem code does.
- * Make sure they're still that way.
+ * The probe routines leave the pages uninitialized, just as the bootmem code
+ * does. Make sure we do not access them, but instead use only information from
+ * within sections.
*/
-static bool pages_correctly_reserved(unsigned long start_pfn)
+static bool pages_correctly_probed(unsigned long start_pfn)
{
- int i, j;
- struct page *page;
+ unsigned long section_nr = pfn_to_section_nr(start_pfn);
+ unsigned long section_nr_end = section_nr + sections_per_block;
unsigned long pfn = start_pfn;
/*
@@ -201,21 +202,24 @@ static bool pages_correctly_reserved(unsigned long start_pfn)
* SPARSEMEM_VMEMMAP. We lookup the page once per section
* and assume memmap is contiguous within each section
*/
- for (i = 0; i < sections_per_block; i++, pfn += PAGES_PER_SECTION) {
+ for (; section_nr < section_nr_end; section_nr++) {
if (WARN_ON_ONCE(!pfn_valid(pfn)))
return false;
- page = pfn_to_page(pfn);
-
- for (j = 0; j < PAGES_PER_SECTION; j++) {
- if (PageReserved(page + j))
- continue;
-
- printk(KERN_WARNING "section number %ld page number %d "
- "not reserved, was it already online?\n",
- pfn_to_section_nr(pfn), j);
+ if (!present_section_nr(section_nr)) {
+ pr_warn("section %ld pfn[%lx, %lx) not present",
+ section_nr, pfn, pfn + PAGES_PER_SECTION);
+ return false;
+ } else if (!valid_section_nr(section_nr)) {
+ pr_warn("section %ld pfn[%lx, %lx) no valid memmap",
+ section_nr, pfn, pfn + PAGES_PER_SECTION);
+ return false;
+ } else if (online_section_nr(section_nr)) {
+ pr_warn("section %ld pfn[%lx, %lx) is already online",
+ section_nr, pfn, pfn + PAGES_PER_SECTION);
return false;
}
+ pfn += PAGES_PER_SECTION;
}
return true;
@@ -237,7 +241,7 @@ memory_block_action(unsigned long phys_index, unsigned long action, int online_t
switch (action) {
case MEM_ONLINE:
- if (!pages_correctly_reserved(start_pfn))
+ if (!pages_correctly_probed(start_pfn))
return -EBUSY;
ret = online_pages(start_pfn, nr_pages, online_type);
@@ -708,7 +712,7 @@ static int add_memory_block(int base_section_nr)
* need an interface for the VM to add new memory regions,
* but without onlining it.
*/
-int register_new_memory(int nid, struct mem_section *section)
+int hotplug_memory_register(int nid, struct mem_section *section)
{
int ret = 0;
struct memory_block *mem;
@@ -727,7 +731,7 @@ int register_new_memory(int nid, struct mem_section *section)
}
if (mem->section_count == sections_per_block)
- ret = register_mem_sect_under_node(mem, nid);
+ ret = register_mem_sect_under_node(mem, nid, false);
out:
mutex_unlock(&mem_sysfs_mutex);
return ret;
@@ -833,11 +837,8 @@ int __init memory_dev_init(void)
* during boot and have been initialized
*/
mutex_lock(&mem_sysfs_mutex);
- for (i = 0; i < NR_MEM_SECTIONS; i += sections_per_block) {
- /* Don't iterate over sections we know are !present: */
- if (i > __highest_present_section_nr)
- break;
-
+ for (i = 0; i <= __highest_present_section_nr;
+ i += sections_per_block) {
err = add_memory_block(i);
if (!ret)
ret = err;
diff --git a/drivers/base/node.c b/drivers/base/node.c
index ee090ab9171c..7a3a580821e0 100644
--- a/drivers/base/node.c
+++ b/drivers/base/node.c
@@ -315,7 +315,9 @@ static int register_node(struct node *node, int num)
node->dev.groups = node_dev_groups;
error = device_register(&node->dev);
- if (!error){
+ if (error)
+ put_device(&node->dev);
+ else {
hugetlb_register_node(node);
compaction_register_node(node);
@@ -397,13 +399,16 @@ static int __ref get_nid_for_pfn(unsigned long pfn)
}
/* register memory section under specified node if it spans that node */
-int register_mem_sect_under_node(struct memory_block *mem_blk, int nid)
+int register_mem_sect_under_node(struct memory_block *mem_blk, int nid,
+ bool check_nid)
{
int ret;
unsigned long pfn, sect_start_pfn, sect_end_pfn;
if (!mem_blk)
return -EFAULT;
+
+ mem_blk->nid = nid;
if (!node_online(nid))
return 0;
@@ -423,11 +428,18 @@ int register_mem_sect_under_node(struct memory_block *mem_blk, int nid)
continue;
}
- page_nid = get_nid_for_pfn(pfn);
- if (page_nid < 0)
- continue;
- if (page_nid != nid)
- continue;
+ /*
+ * We need to check if page belongs to nid only for the boot
+ * case, during hotplug we know that all pages in the memory
+ * block belong to the same node.
+ */
+ if (check_nid) {
+ page_nid = get_nid_for_pfn(pfn);
+ if (page_nid < 0)
+ continue;
+ if (page_nid != nid)
+ continue;
+ }
ret = sysfs_create_link_nowarn(&node_devices[nid]->dev.kobj,
&mem_blk->dev.kobj,
kobject_name(&mem_blk->dev.kobj));
@@ -502,7 +514,7 @@ int link_mem_sections(int nid, unsigned long start_pfn, unsigned long nr_pages)
mem_blk = find_memory_block_hinted(mem_sect, mem_blk);
- ret = register_mem_sect_under_node(mem_blk, nid);
+ ret = register_mem_sect_under_node(mem_blk, nid, true);
if (!err)
err = ret;
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index f1bf7b38d91c..8075ddc70a17 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -1153,8 +1153,10 @@ int __init platform_bus_init(void)
early_platform_cleanup();
error = device_register(&platform_bus);
- if (error)
+ if (error) {
+ put_device(&platform_bus);
return error;
+ }
error = bus_register(&platform_bus_type);
if (error)
device_unregister(&platform_bus);
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index 21244c53e377..86e67e70b509 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -31,6 +31,7 @@ struct wake_irq {
struct device *dev;
unsigned int status;
int irq;
+ const char *name;
};
extern void dev_pm_arm_wake_irq(struct wake_irq *wirq);
diff --git a/drivers/base/power/wakeirq.c b/drivers/base/power/wakeirq.c
index 6637fc319269..b8fa5c0f2d13 100644
--- a/drivers/base/power/wakeirq.c
+++ b/drivers/base/power/wakeirq.c
@@ -112,6 +112,7 @@ void dev_pm_clear_wake_irq(struct device *dev)
free_irq(wirq->irq, wirq);
wirq->status &= ~WAKE_IRQ_DEDICATED_MASK;
}
+ kfree(wirq->name);
kfree(wirq);
}
EXPORT_SYMBOL_GPL(dev_pm_clear_wake_irq);
@@ -184,6 +185,12 @@ int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq)
if (!wirq)
return -ENOMEM;
+ wirq->name = kasprintf(GFP_KERNEL, "%s:wakeup", dev_name(dev));
+ if (!wirq->name) {
+ err = -ENOMEM;
+ goto err_free;
+ }
+
wirq->dev = dev;
wirq->irq = irq;
irq_set_status_flags(irq, IRQ_NOAUTOEN);
@@ -196,9 +203,9 @@ int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq)
* so we use a threaded irq.
*/
err = request_threaded_irq(irq, NULL, handle_threaded_wake_irq,
- IRQF_ONESHOT, dev_name(dev), wirq);
+ IRQF_ONESHOT, wirq->name, wirq);
if (err)
- goto err_free;
+ goto err_free_name;
err = dev_pm_attach_wake_irq(dev, irq, wirq);
if (err)
@@ -210,6 +217,8 @@ int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq)
err_free_irq:
free_irq(irq, wirq);
+err_free_name:
+ kfree(wirq->name);
err_free:
kfree(wirq);
diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c
index f3266334063e..87b562e49a43 100644
--- a/drivers/base/regmap/regmap-debugfs.c
+++ b/drivers/base/regmap/regmap-debugfs.c
@@ -25,6 +25,7 @@ struct regmap_debugfs_node {
struct list_head link;
};
+static unsigned int dummy_index;
static struct dentry *regmap_debugfs_root;
static LIST_HEAD(regmap_debugfs_early_list);
static DEFINE_MUTEX(regmap_debugfs_early_lock);
@@ -40,6 +41,7 @@ static ssize_t regmap_name_read_file(struct file *file,
loff_t *ppos)
{
struct regmap *map = file->private_data;
+ const char *name = "nodev";
int ret;
char *buf;
@@ -47,7 +49,10 @@ static ssize_t regmap_name_read_file(struct file *file,
if (!buf)
return -ENOMEM;
- ret = snprintf(buf, PAGE_SIZE, "%s\n", map->dev->driver->name);
+ if (map->dev && map->dev->driver)
+ name = map->dev->driver->name;
+
+ ret = snprintf(buf, PAGE_SIZE, "%s\n", name);
if (ret < 0) {
kfree(buf);
return ret;
@@ -569,9 +574,20 @@ void regmap_debugfs_init(struct regmap *map, const char *name)
name = devname;
}
+ if (!strcmp(name, "dummy")) {
+ map->debugfs_name = kasprintf(GFP_KERNEL, "dummy%d",
+ dummy_index);
+ name = map->debugfs_name;
+ dummy_index++;
+ }
+
map->debugfs = debugfs_create_dir(name, regmap_debugfs_root);
if (!map->debugfs) {
- dev_warn(map->dev, "Failed to create debugfs directory\n");
+ dev_warn(map->dev,
+ "Failed to create %s debugfs directory\n", name);
+
+ kfree(map->debugfs_name);
+ map->debugfs_name = NULL;
return;
}
diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c
index 4735318f4268..056acde5e7d3 100644
--- a/drivers/base/regmap/regmap-i2c.c
+++ b/drivers/base/regmap/regmap-i2c.c
@@ -217,8 +217,6 @@ static int regmap_i2c_smbus_i2c_write(void *context, const void *data,
if (count < 1)
return -EINVAL;
- if (count >= I2C_SMBUS_BLOCK_MAX)
- return -E2BIG;
--count;
return i2c_smbus_write_i2c_block_data(i2c, ((u8 *)data)[0], count,
@@ -235,8 +233,6 @@ static int regmap_i2c_smbus_i2c_read(void *context, const void *reg,
if (reg_size != 1 || val_size < 1)
return -EINVAL;
- if (val_size >= I2C_SMBUS_BLOCK_MAX)
- return -E2BIG;
ret = i2c_smbus_read_i2c_block_data(i2c, ((u8 *)reg)[0], val_size, val);
if (ret == val_size)
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index ee302ccdfbc8..3bc84885eb91 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -99,7 +99,7 @@ bool regmap_cached(struct regmap *map, unsigned int reg)
int ret;
unsigned int val;
- if (map->cache == REGCACHE_NONE)
+ if (map->cache_type == REGCACHE_NONE)
return false;
if (!map->cache_ops)
@@ -174,7 +174,7 @@ static bool regmap_volatile_range(struct regmap *map, unsigned int reg,
unsigned int i;
for (i = 0; i < num; i++)
- if (!regmap_volatile(map, reg + i))
+ if (!regmap_volatile(map, reg + regmap_get_offset(map, i)))
return false;
return true;
@@ -1116,6 +1116,8 @@ skip_format_initialization:
ret = regmap_attach_dev(dev, map, config);
if (ret != 0)
goto err_regcache;
+ } else {
+ regmap_debugfs_init(map, config->name);
}
return map;
@@ -1438,8 +1440,8 @@ static void regmap_set_work_buf_flag_mask(struct regmap *map, int max_bytes,
buf[i] |= (mask >> (8 * i)) & 0xff;
}
-int _regmap_raw_write(struct regmap *map, unsigned int reg,
- const void *val, size_t val_len)
+static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg,
+ const void *val, size_t val_len)
{
struct regmap_range_node *range;
unsigned long flags;
@@ -1490,8 +1492,9 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg,
while (val_num > win_residue) {
dev_dbg(map->dev, "Writing window %d/%zu\n",
win_residue, val_len / map->format.val_bytes);
- ret = _regmap_raw_write(map, reg, val, win_residue *
- map->format.val_bytes);
+ ret = _regmap_raw_write_impl(map, reg, val,
+ win_residue *
+ map->format.val_bytes);
if (ret != 0)
return ret;
@@ -1707,11 +1710,11 @@ static int _regmap_bus_raw_write(void *context, unsigned int reg,
map->format.format_val(map->work_buf + map->format.reg_bytes
+ map->format.pad_bytes, val, 0);
- return _regmap_raw_write(map, reg,
- map->work_buf +
- map->format.reg_bytes +
- map->format.pad_bytes,
- map->format.val_bytes);
+ return _regmap_raw_write_impl(map, reg,
+ map->work_buf +
+ map->format.reg_bytes +
+ map->format.pad_bytes,
+ map->format.val_bytes);
}
static inline void *_regmap_map_get_context(struct regmap *map)
@@ -1806,6 +1809,44 @@ int regmap_write_async(struct regmap *map, unsigned int reg, unsigned int val)
}
EXPORT_SYMBOL_GPL(regmap_write_async);
+int _regmap_raw_write(struct regmap *map, unsigned int reg,
+ const void *val, size_t val_len)
+{
+ size_t val_bytes = map->format.val_bytes;
+ size_t val_count = val_len / val_bytes;
+ size_t chunk_count, chunk_bytes;
+ size_t chunk_regs = val_count;
+ int ret, i;
+
+ if (!val_count)
+ return -EINVAL;
+
+ if (map->use_single_write)
+ chunk_regs = 1;
+ else if (map->max_raw_write && val_len > map->max_raw_write)
+ chunk_regs = map->max_raw_write / val_bytes;
+
+ chunk_count = val_count / chunk_regs;
+ chunk_bytes = chunk_regs * val_bytes;
+
+ /* Write as many bytes as possible with chunk_size */
+ for (i = 0; i < chunk_count; i++) {
+ ret = _regmap_raw_write_impl(map, reg, val, chunk_bytes);
+ if (ret)
+ return ret;
+
+ reg += regmap_get_offset(map, chunk_regs);
+ val += chunk_bytes;
+ val_len -= chunk_bytes;
+ }
+
+ /* Write remaining bytes */
+ if (val_len)
+ ret = _regmap_raw_write_impl(map, reg, val, val_len);
+
+ return ret;
+}
+
/**
* regmap_raw_write() - Write raw values to one or more registers
*
@@ -1831,8 +1872,6 @@ int regmap_raw_write(struct regmap *map, unsigned int reg,
return -EINVAL;
if (val_len % map->format.val_bytes)
return -EINVAL;
- if (map->max_raw_write && map->max_raw_write > val_len)
- return -E2BIG;
map->lock(map->lock_arg);
@@ -1923,23 +1962,15 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
{
int ret = 0, i;
size_t val_bytes = map->format.val_bytes;
- size_t total_size = val_bytes * val_count;
if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
/*
- * Some devices don't support bulk write, for
- * them we have a series of single write operations in the first two if
- * blocks.
- *
- * The first if block is used for memory mapped io. It does not allow
- * val_bytes of 3 for example.
- * The second one is for busses that do not provide raw I/O.
- * The third one is used for busses which do not have these limitations
- * and can write arbitrary value lengths.
+ * Some devices don't support bulk write, for them we have a series of
+ * single write operations.
*/
- if (!map->bus) {
+ if (!map->bus || !map->format.parse_inplace) {
map->lock(map->lock_arg);
for (i = 0; i < val_count; i++) {
unsigned int ival;
@@ -1972,81 +2003,17 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
}
out:
map->unlock(map->lock_arg);
- } else if (map->bus && !map->format.parse_inplace) {
- const u8 *u8 = val;
- const u16 *u16 = val;
- const u32 *u32 = val;
- unsigned int ival;
-
- for (i = 0; i < val_count; i++) {
- switch (map->format.val_bytes) {
- case 4:
- ival = u32[i];
- break;
- case 2:
- ival = u16[i];
- break;
- case 1:
- ival = u8[i];
- break;
- default:
- return -EINVAL;
- }
-
- ret = regmap_write(map, reg + (i * map->reg_stride),
- ival);
- if (ret)
- return ret;
- }
- } else if (map->use_single_write ||
- (map->max_raw_write && map->max_raw_write < total_size)) {
- int chunk_stride = map->reg_stride;
- size_t chunk_size = val_bytes;
- size_t chunk_count = val_count;
-
- if (!map->use_single_write) {
- chunk_size = map->max_raw_write;
- if (chunk_size % val_bytes)
- chunk_size -= chunk_size % val_bytes;
- chunk_count = total_size / chunk_size;
- chunk_stride *= chunk_size / val_bytes;
- }
-
- map->lock(map->lock_arg);
- /* Write as many bytes as possible with chunk_size */
- for (i = 0; i < chunk_count; i++) {
- ret = _regmap_raw_write(map,
- reg + (i * chunk_stride),
- val + (i * chunk_size),
- chunk_size);
- if (ret)
- break;
- }
-
- /* Write remaining bytes */
- if (!ret && chunk_size * i < total_size) {
- ret = _regmap_raw_write(map, reg + (i * chunk_stride),
- val + (i * chunk_size),
- total_size - i * chunk_size);
- }
- map->unlock(map->lock_arg);
} else {
void *wval;
- if (!val_count)
- return -EINVAL;
-
wval = kmemdup(val, val_count * val_bytes, map->alloc_flags);
- if (!wval) {
- dev_err(map->dev, "Error in memory allocation\n");
+ if (!wval)
return -ENOMEM;
- }
+
for (i = 0; i < val_count * val_bytes; i += val_bytes)
map->format.parse_inplace(wval + i);
- map->lock(map->lock_arg);
- ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count);
- map->unlock(map->lock_arg);
+ ret = regmap_raw_write(map, reg, wval, val_bytes * val_count);
kfree(wval);
}
@@ -2542,18 +2509,39 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
if (regmap_volatile_range(map, reg, val_count) || map->cache_bypass ||
map->cache_type == REGCACHE_NONE) {
+ size_t chunk_count, chunk_bytes;
+ size_t chunk_regs = val_count;
+
if (!map->bus->read) {
ret = -ENOTSUPP;
goto out;
}
- if (map->max_raw_read && map->max_raw_read < val_len) {
- ret = -E2BIG;
- goto out;
- }
- /* Physical block read if there's no cache involved */
- ret = _regmap_raw_read(map, reg, val, val_len);
+ if (map->use_single_read)
+ chunk_regs = 1;
+ else if (map->max_raw_read && val_len > map->max_raw_read)
+ chunk_regs = map->max_raw_read / val_bytes;
+
+ chunk_count = val_count / chunk_regs;
+ chunk_bytes = chunk_regs * val_bytes;
+ /* Read bytes that fit into whole chunks */
+ for (i = 0; i < chunk_count; i++) {
+ ret = _regmap_raw_read(map, reg, val, chunk_bytes);
+ if (ret != 0)
+ goto out;
+
+ reg += regmap_get_offset(map, chunk_regs);
+ val += chunk_bytes;
+ val_len -= chunk_bytes;
+ }
+
+ /* Read remaining bytes */
+ if (val_len) {
+ ret = _regmap_raw_read(map, reg, val, val_len);
+ if (ret != 0)
+ goto out;
+ }
} else {
/* Otherwise go word by word for the cache; should be low
* cost as we expect to hit the cache.
@@ -2653,108 +2641,60 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
+ if (val_count == 0)
+ return -EINVAL;
if (map->bus && map->format.parse_inplace && (vol || map->cache_type == REGCACHE_NONE)) {
- /*
- * Some devices does not support bulk read, for
- * them we have a series of single read operations.
- */
- size_t total_size = val_bytes * val_count;
-
- if (!map->use_single_read &&
- (!map->max_raw_read || map->max_raw_read > total_size)) {
- ret = regmap_raw_read(map, reg, val,
- val_bytes * val_count);
- if (ret != 0)
- return ret;
- } else {
- /*
- * Some devices do not support bulk read or do not
- * support large bulk reads, for them we have a series
- * of read operations.
- */
- int chunk_stride = map->reg_stride;
- size_t chunk_size = val_bytes;
- size_t chunk_count = val_count;
-
- if (!map->use_single_read) {
- chunk_size = map->max_raw_read;
- if (chunk_size % val_bytes)
- chunk_size -= chunk_size % val_bytes;
- chunk_count = total_size / chunk_size;
- chunk_stride *= chunk_size / val_bytes;
- }
-
- /* Read bytes that fit into a multiple of chunk_size */
- for (i = 0; i < chunk_count; i++) {
- ret = regmap_raw_read(map,
- reg + (i * chunk_stride),
- val + (i * chunk_size),
- chunk_size);
- if (ret != 0)
- return ret;
- }
-
- /* Read remaining bytes */
- if (chunk_size * i < total_size) {
- ret = regmap_raw_read(map,
- reg + (i * chunk_stride),
- val + (i * chunk_size),
- total_size - i * chunk_size);
- if (ret != 0)
- return ret;
- }
- }
+ ret = regmap_raw_read(map, reg, val, val_bytes * val_count);
+ if (ret != 0)
+ return ret;
for (i = 0; i < val_count * val_bytes; i += val_bytes)
map->format.parse_inplace(val + i);
} else {
+#ifdef CONFIG_64BIT
+ u64 *u64 = val;
+#endif
+ u32 *u32 = val;
+ u16 *u16 = val;
+ u8 *u8 = val;
+
+ map->lock(map->lock_arg);
+
for (i = 0; i < val_count; i++) {
unsigned int ival;
- ret = regmap_read(map, reg + regmap_get_offset(map, i),
- &ival);
- if (ret != 0)
- return ret;
- if (map->format.format_val) {
- map->format.format_val(val + (i * val_bytes), ival, 0);
- } else {
- /* Devices providing read and write
- * operations can use the bulk I/O
- * functions if they define a val_bytes,
- * we assume that the values are native
- * endian.
- */
-#ifdef CONFIG_64BIT
- u64 *u64 = val;
-#endif
- u32 *u32 = val;
- u16 *u16 = val;
- u8 *u8 = val;
+ ret = _regmap_read(map, reg + regmap_get_offset(map, i),
+ &ival);
+ if (ret != 0)
+ goto out;
- switch (map->format.val_bytes) {
+ switch (map->format.val_bytes) {
#ifdef CONFIG_64BIT
- case 8:
- u64[i] = ival;
- break;
+ case 8:
+ u64[i] = ival;
+ break;
#endif
- case 4:
- u32[i] = ival;
- break;
- case 2:
- u16[i] = ival;
- break;
- case 1:
- u8[i] = ival;
- break;
- default:
- return -EINVAL;
- }
+ case 4:
+ u32[i] = ival;
+ break;
+ case 2:
+ u16[i] = ival;
+ break;
+ case 1:
+ u8[i] = ival;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
}
}
+
+out:
+ map->unlock(map->lock_arg);
}
- return 0;
+ return ret;
}
EXPORT_SYMBOL_GPL(regmap_bulk_read);
diff --git a/drivers/base/soc.c b/drivers/base/soc.c
index 4e80f48ad5d6..10b280f30217 100644
--- a/drivers/base/soc.c
+++ b/drivers/base/soc.c
@@ -150,6 +150,8 @@ struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr
out3:
ida_simple_remove(&soc_ida, soc_dev->soc_dev_num);
+ put_device(&soc_dev->dev);
+ soc_dev = NULL;
out2:
kfree(soc_dev);
out1:
diff --git a/drivers/bcma/Kconfig b/drivers/bcma/Kconfig
index ba8acca036df..cb0f1aad20b7 100644
--- a/drivers/bcma/Kconfig
+++ b/drivers/bcma/Kconfig
@@ -55,7 +55,7 @@ config BCMA_DRIVER_PCI
config BCMA_DRIVER_PCI_HOSTMODE
bool "Driver for PCI core working in hostmode"
- depends on MIPS && BCMA_DRIVER_PCI && PCI_DRIVERS_LEGACY
+ depends on MIPS && BCMA_DRIVER_PCI && PCI_DRIVERS_LEGACY && BCMA = y
help
PCI core hostmode operation (external PCI bus).
diff --git a/drivers/bcma/driver_chipcommon_pmu.c b/drivers/bcma/driver_chipcommon_pmu.c
index f1eb4d3e1d57..f4161064365c 100644
--- a/drivers/bcma/driver_chipcommon_pmu.c
+++ b/drivers/bcma/driver_chipcommon_pmu.c
@@ -203,7 +203,7 @@ static void bcma_pmu_resources_init(struct bcma_drv_cc *cc)
* Add some delay; allow resources to come up and settle.
* Delay is required for SoC (early init).
*/
- mdelay(2);
+ usleep_range(2000, 2500);
}
/* Disable to allow reading SPROM. Don't know the adventages of enabling it. */
diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c
index 925842996986..63410ecfe640 100644
--- a/drivers/bcma/host_pci.c
+++ b/drivers/bcma/host_pci.c
@@ -297,6 +297,7 @@ static const struct pci_device_id bcma_pci_bridge_tbl[] = {
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_BROADCOM, 0x4365, PCI_VENDOR_ID_DELL, 0x0016) },
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_BROADCOM, 0x4365, PCI_VENDOR_ID_DELL, 0x0018) },
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_BROADCOM, 0x4365, PCI_VENDOR_ID_FOXCONN, 0xe092) },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_BROADCOM, 0x4365, PCI_VENDOR_ID_HP, 0x804a) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43a0) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43a9) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43aa) },
diff --git a/drivers/block/brd.c b/drivers/block/brd.c
index deea78e485da..66cb0f857f64 100644
--- a/drivers/block/brd.c
+++ b/drivers/block/brd.c
@@ -24,7 +24,6 @@
#include <linux/uaccess.h>
-#define SECTOR_SHIFT 9
#define PAGE_SECTORS_SHIFT (PAGE_SHIFT - SECTOR_SHIFT)
#define PAGE_SECTORS (1 << PAGE_SECTORS_SHIFT)
diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c
index 0a0394aa1b9c..185f1ef00a7c 100644
--- a/drivers/block/drbd/drbd_main.c
+++ b/drivers/block/drbd/drbd_main.c
@@ -2816,7 +2816,7 @@ enum drbd_ret_code drbd_create_device(struct drbd_config_context *adm_ctx, unsig
drbd_init_set_defaults(device);
- q = blk_alloc_queue(GFP_KERNEL);
+ q = blk_alloc_queue_node(GFP_KERNEL, NUMA_NO_NODE, &resource->req_lock);
if (!q)
goto out_no_q;
device->rq_queue = q;
@@ -2848,7 +2848,6 @@ enum drbd_ret_code drbd_create_device(struct drbd_config_context *adm_ctx, unsig
/* Setting the max_hw_sectors to an odd value of 8kibyte here
This triggers a max_bio_size message upon first attach or connect */
blk_queue_max_hw_sectors(q, DRBD_MAX_BIO_SIZE_SAFE >> 8);
- q->queue_lock = &resource->req_lock;
device->md_io.page = alloc_page(GFP_KERNEL);
if (!device->md_io.page)
diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c
index a12f77e6891e..b4f02768ba47 100644
--- a/drivers/block/drbd/drbd_nl.c
+++ b/drivers/block/drbd/drbd_nl.c
@@ -1212,10 +1212,10 @@ static void decide_on_discard_support(struct drbd_device *device,
* topology on all peers. */
blk_queue_discard_granularity(q, 512);
q->limits.max_discard_sectors = drbd_max_discard_sectors(connection);
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q);
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, q);
q->limits.max_write_zeroes_sectors = drbd_max_discard_sectors(connection);
} else {
- queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, q);
+ blk_queue_flag_clear(QUEUE_FLAG_DISCARD, q);
blk_queue_discard_granularity(q, 0);
q->limits.max_discard_sectors = 0;
q->limits.max_write_zeroes_sectors = 0;
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index ee62d2d517bf..5d4e31655d96 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -214,10 +214,10 @@ static void __loop_update_dio(struct loop_device *lo, bool dio)
blk_mq_freeze_queue(lo->lo_queue);
lo->use_dio = use_dio;
if (use_dio) {
- queue_flag_clear_unlocked(QUEUE_FLAG_NOMERGES, lo->lo_queue);
+ blk_queue_flag_clear(QUEUE_FLAG_NOMERGES, lo->lo_queue);
lo->lo_flags |= LO_FLAGS_DIRECT_IO;
} else {
- queue_flag_set_unlocked(QUEUE_FLAG_NOMERGES, lo->lo_queue);
+ blk_queue_flag_set(QUEUE_FLAG_NOMERGES, lo->lo_queue);
lo->lo_flags &= ~LO_FLAGS_DIRECT_IO;
}
blk_mq_unfreeze_queue(lo->lo_queue);
@@ -451,25 +451,47 @@ static int lo_req_flush(struct loop_device *lo, struct request *rq)
static void lo_complete_rq(struct request *rq)
{
struct loop_cmd *cmd = blk_mq_rq_to_pdu(rq);
+ blk_status_t ret = BLK_STS_OK;
- if (unlikely(req_op(cmd->rq) == REQ_OP_READ && cmd->use_aio &&
- cmd->ret >= 0 && cmd->ret < blk_rq_bytes(cmd->rq))) {
- struct bio *bio = cmd->rq->bio;
-
- bio_advance(bio, cmd->ret);
- zero_fill_bio(bio);
+ if (!cmd->use_aio || cmd->ret < 0 || cmd->ret == blk_rq_bytes(rq) ||
+ req_op(rq) != REQ_OP_READ) {
+ if (cmd->ret < 0)
+ ret = BLK_STS_IOERR;
+ goto end_io;
}
- blk_mq_end_request(rq, cmd->ret < 0 ? BLK_STS_IOERR : BLK_STS_OK);
+ /*
+ * Short READ - if we got some data, advance our request and
+ * retry it. If we got no data, end the rest with EIO.
+ */
+ if (cmd->ret) {
+ blk_update_request(rq, BLK_STS_OK, cmd->ret);
+ cmd->ret = 0;
+ blk_mq_requeue_request(rq, true);
+ } else {
+ if (cmd->use_aio) {
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ zero_fill_bio(bio);
+ bio = bio->bi_next;
+ }
+ }
+ ret = BLK_STS_IOERR;
+end_io:
+ blk_mq_end_request(rq, ret);
+ }
}
static void lo_rw_aio_do_completion(struct loop_cmd *cmd)
{
+ struct request *rq = blk_mq_rq_from_pdu(cmd);
+
if (!atomic_dec_and_test(&cmd->ref))
return;
kfree(cmd->bvec);
cmd->bvec = NULL;
- blk_mq_complete_request(cmd->rq);
+ blk_mq_complete_request(rq);
}
static void lo_rw_aio_complete(struct kiocb *iocb, long ret, long ret2)
@@ -487,7 +509,7 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd,
{
struct iov_iter iter;
struct bio_vec *bvec;
- struct request *rq = cmd->rq;
+ struct request *rq = blk_mq_rq_from_pdu(cmd);
struct bio *bio = rq->bio;
struct file *file = lo->lo_backing_file;
unsigned int offset;
@@ -817,7 +839,7 @@ static void loop_config_discard(struct loop_device *lo)
q->limits.discard_alignment = 0;
blk_queue_max_discard_sectors(q, 0);
blk_queue_max_write_zeroes_sectors(q, 0);
- queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, q);
+ blk_queue_flag_clear(QUEUE_FLAG_DISCARD, q);
return;
}
@@ -826,7 +848,7 @@ static void loop_config_discard(struct loop_device *lo)
blk_queue_max_discard_sectors(q, UINT_MAX >> 9);
blk_queue_max_write_zeroes_sectors(q, UINT_MAX >> 9);
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q);
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, q);
}
static void loop_unprepare_queue(struct loop_device *lo)
@@ -1103,11 +1125,15 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
if (info->lo_encrypt_type) {
unsigned int type = info->lo_encrypt_type;
- if (type >= MAX_LO_CRYPT)
- return -EINVAL;
+ if (type >= MAX_LO_CRYPT) {
+ err = -EINVAL;
+ goto exit;
+ }
xfer = xfer_funcs[type];
- if (xfer == NULL)
- return -EINVAL;
+ if (xfer == NULL) {
+ err = -EINVAL;
+ goto exit;
+ }
} else
xfer = NULL;
@@ -1167,21 +1193,17 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
static int
loop_get_status(struct loop_device *lo, struct loop_info64 *info)
{
- struct file *file = lo->lo_backing_file;
+ struct file *file;
struct kstat stat;
- int error;
+ int ret;
- if (lo->lo_state != Lo_bound)
+ if (lo->lo_state != Lo_bound) {
+ mutex_unlock(&lo->lo_ctl_mutex);
return -ENXIO;
- error = vfs_getattr(&file->f_path, &stat,
- STATX_INO, AT_STATX_SYNC_AS_STAT);
- if (error)
- return error;
+ }
+
memset(info, 0, sizeof(*info));
info->lo_number = lo->lo_number;
- info->lo_device = huge_encode_dev(stat.dev);
- info->lo_inode = stat.ino;
- info->lo_rdevice = huge_encode_dev(lo->lo_device ? stat.rdev : stat.dev);
info->lo_offset = lo->lo_offset;
info->lo_sizelimit = lo->lo_sizelimit;
info->lo_flags = lo->lo_flags;
@@ -1194,7 +1216,19 @@ loop_get_status(struct loop_device *lo, struct loop_info64 *info)
memcpy(info->lo_encrypt_key, lo->lo_encrypt_key,
lo->lo_encrypt_key_size);
}
- return 0;
+
+ /* Drop lo_ctl_mutex while we call into the filesystem. */
+ file = get_file(lo->lo_backing_file);
+ mutex_unlock(&lo->lo_ctl_mutex);
+ ret = vfs_getattr(&file->f_path, &stat, STATX_INO,
+ AT_STATX_SYNC_AS_STAT);
+ if (!ret) {
+ info->lo_device = huge_encode_dev(stat.dev);
+ info->lo_inode = stat.ino;
+ info->lo_rdevice = huge_encode_dev(stat.rdev);
+ }
+ fput(file);
+ return ret;
}
static void
@@ -1275,12 +1309,13 @@ static int
loop_get_status_old(struct loop_device *lo, struct loop_info __user *arg) {
struct loop_info info;
struct loop_info64 info64;
- int err = 0;
+ int err;
- if (!arg)
- err = -EINVAL;
- if (!err)
- err = loop_get_status(lo, &info64);
+ if (!arg) {
+ mutex_unlock(&lo->lo_ctl_mutex);
+ return -EINVAL;
+ }
+ err = loop_get_status(lo, &info64);
if (!err)
err = loop_info64_to_old(&info64, &info);
if (!err && copy_to_user(arg, &info, sizeof(info)))
@@ -1292,12 +1327,13 @@ loop_get_status_old(struct loop_device *lo, struct loop_info __user *arg) {
static int
loop_get_status64(struct loop_device *lo, struct loop_info64 __user *arg) {
struct loop_info64 info64;
- int err = 0;
+ int err;
- if (!arg)
- err = -EINVAL;
- if (!err)
- err = loop_get_status(lo, &info64);
+ if (!arg) {
+ mutex_unlock(&lo->lo_ctl_mutex);
+ return -EINVAL;
+ }
+ err = loop_get_status(lo, &info64);
if (!err && copy_to_user(arg, &info64, sizeof(info64)))
err = -EFAULT;
@@ -1352,7 +1388,10 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode,
struct loop_device *lo = bdev->bd_disk->private_data;
int err;
- mutex_lock_nested(&lo->lo_ctl_mutex, 1);
+ err = mutex_lock_killable_nested(&lo->lo_ctl_mutex, 1);
+ if (err)
+ goto out_unlocked;
+
switch (cmd) {
case LOOP_SET_FD:
err = loop_set_fd(lo, mode, bdev, arg);
@@ -1374,7 +1413,8 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode,
break;
case LOOP_GET_STATUS:
err = loop_get_status_old(lo, (struct loop_info __user *) arg);
- break;
+ /* loop_get_status() unlocks lo_ctl_mutex */
+ goto out_unlocked;
case LOOP_SET_STATUS64:
err = -EPERM;
if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN))
@@ -1383,7 +1423,8 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode,
break;
case LOOP_GET_STATUS64:
err = loop_get_status64(lo, (struct loop_info64 __user *) arg);
- break;
+ /* loop_get_status() unlocks lo_ctl_mutex */
+ goto out_unlocked;
case LOOP_SET_CAPACITY:
err = -EPERM;
if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN))
@@ -1516,12 +1557,13 @@ loop_get_status_compat(struct loop_device *lo,
struct compat_loop_info __user *arg)
{
struct loop_info64 info64;
- int err = 0;
+ int err;
- if (!arg)
- err = -EINVAL;
- if (!err)
- err = loop_get_status(lo, &info64);
+ if (!arg) {
+ mutex_unlock(&lo->lo_ctl_mutex);
+ return -EINVAL;
+ }
+ err = loop_get_status(lo, &info64);
if (!err)
err = loop_info64_to_compat(&info64, arg);
return err;
@@ -1535,16 +1577,20 @@ static int lo_compat_ioctl(struct block_device *bdev, fmode_t mode,
switch(cmd) {
case LOOP_SET_STATUS:
- mutex_lock(&lo->lo_ctl_mutex);
- err = loop_set_status_compat(
- lo, (const struct compat_loop_info __user *) arg);
- mutex_unlock(&lo->lo_ctl_mutex);
+ err = mutex_lock_killable(&lo->lo_ctl_mutex);
+ if (!err) {
+ err = loop_set_status_compat(lo,
+ (const struct compat_loop_info __user *)arg);
+ mutex_unlock(&lo->lo_ctl_mutex);
+ }
break;
case LOOP_GET_STATUS:
- mutex_lock(&lo->lo_ctl_mutex);
- err = loop_get_status_compat(
- lo, (struct compat_loop_info __user *) arg);
- mutex_unlock(&lo->lo_ctl_mutex);
+ err = mutex_lock_killable(&lo->lo_ctl_mutex);
+ if (!err) {
+ err = loop_get_status_compat(lo,
+ (struct compat_loop_info __user *)arg);
+ /* loop_get_status() unlocks lo_ctl_mutex */
+ }
break;
case LOOP_SET_CAPACITY:
case LOOP_CLR_FD:
@@ -1678,15 +1724,16 @@ EXPORT_SYMBOL(loop_unregister_transfer);
static blk_status_t loop_queue_rq(struct blk_mq_hw_ctx *hctx,
const struct blk_mq_queue_data *bd)
{
- struct loop_cmd *cmd = blk_mq_rq_to_pdu(bd->rq);
- struct loop_device *lo = cmd->rq->q->queuedata;
+ struct request *rq = bd->rq;
+ struct loop_cmd *cmd = blk_mq_rq_to_pdu(rq);
+ struct loop_device *lo = rq->q->queuedata;
- blk_mq_start_request(bd->rq);
+ blk_mq_start_request(rq);
if (lo->lo_state != Lo_bound)
return BLK_STS_IOERR;
- switch (req_op(cmd->rq)) {
+ switch (req_op(rq)) {
case REQ_OP_FLUSH:
case REQ_OP_DISCARD:
case REQ_OP_WRITE_ZEROES:
@@ -1699,8 +1746,8 @@ static blk_status_t loop_queue_rq(struct blk_mq_hw_ctx *hctx,
/* always use the first bio's css */
#ifdef CONFIG_BLK_CGROUP
- if (cmd->use_aio && cmd->rq->bio && cmd->rq->bio->bi_css) {
- cmd->css = cmd->rq->bio->bi_css;
+ if (cmd->use_aio && rq->bio && rq->bio->bi_css) {
+ cmd->css = rq->bio->bi_css;
css_get(cmd->css);
} else
#endif
@@ -1712,8 +1759,9 @@ static blk_status_t loop_queue_rq(struct blk_mq_hw_ctx *hctx,
static void loop_handle_cmd(struct loop_cmd *cmd)
{
- const bool write = op_is_write(req_op(cmd->rq));
- struct loop_device *lo = cmd->rq->q->queuedata;
+ struct request *rq = blk_mq_rq_from_pdu(cmd);
+ const bool write = op_is_write(req_op(rq));
+ struct loop_device *lo = rq->q->queuedata;
int ret = 0;
if (write && (lo->lo_flags & LO_FLAGS_READ_ONLY)) {
@@ -1721,12 +1769,12 @@ static void loop_handle_cmd(struct loop_cmd *cmd)
goto failed;
}
- ret = do_req_filebacked(lo, cmd->rq);
+ ret = do_req_filebacked(lo, rq);
failed:
/* complete non-aio request */
if (!cmd->use_aio || ret) {
cmd->ret = ret ? -EIO : 0;
- blk_mq_complete_request(cmd->rq);
+ blk_mq_complete_request(rq);
}
}
@@ -1743,9 +1791,7 @@ static int loop_init_request(struct blk_mq_tag_set *set, struct request *rq,
{
struct loop_cmd *cmd = blk_mq_rq_to_pdu(rq);
- cmd->rq = rq;
kthread_init_work(&cmd->work, loop_queue_work);
-
return 0;
}
@@ -1808,7 +1854,7 @@ static int loop_add(struct loop_device **l, int i)
* page. For directio mode, merge does help to dispatch bigger request
* to underlayer disk. We will enable merge once directio is enabled.
*/
- queue_flag_set_unlocked(QUEUE_FLAG_NOMERGES, lo->lo_queue);
+ blk_queue_flag_set(QUEUE_FLAG_NOMERGES, lo->lo_queue);
err = -ENOMEM;
disk = lo->lo_disk = alloc_disk(1 << part_shift);
@@ -1864,8 +1910,8 @@ out:
static void loop_remove(struct loop_device *lo)
{
- blk_cleanup_queue(lo->lo_queue);
del_gendisk(lo->lo_disk);
+ blk_cleanup_queue(lo->lo_queue);
blk_mq_free_tag_set(&lo->tag_set);
put_disk(lo->lo_disk);
kfree(lo);
@@ -1949,7 +1995,9 @@ static long loop_control_ioctl(struct file *file, unsigned int cmd,
ret = loop_lookup(&lo, parm);
if (ret < 0)
break;
- mutex_lock(&lo->lo_ctl_mutex);
+ ret = mutex_lock_killable(&lo->lo_ctl_mutex);
+ if (ret)
+ break;
if (lo->lo_state != Lo_unbound) {
ret = -EBUSY;
mutex_unlock(&lo->lo_ctl_mutex);
diff --git a/drivers/block/loop.h b/drivers/block/loop.h
index 0f45416e4fcf..b78de9879f4f 100644
--- a/drivers/block/loop.h
+++ b/drivers/block/loop.h
@@ -66,7 +66,6 @@ struct loop_device {
struct loop_cmd {
struct kthread_work work;
- struct request *rq;
bool use_aio; /* use AIO interface to handle I/O */
atomic_t ref; /* only for aio */
long ret;
diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c
index b8af7352a18f..769c551e3d71 100644
--- a/drivers/block/mtip32xx/mtip32xx.c
+++ b/drivers/block/mtip32xx/mtip32xx.c
@@ -159,7 +159,7 @@ static bool mtip_check_surprise_removal(struct pci_dev *pdev)
if (vendor_id == 0xFFFF) {
dd->sr = true;
if (dd->queue)
- set_bit(QUEUE_FLAG_DEAD, &dd->queue->queue_flags);
+ blk_queue_flag_set(QUEUE_FLAG_DEAD, dd->queue);
else
dev_warn(&dd->pdev->dev,
"%s: dd->queue is NULL\n", __func__);
@@ -3855,8 +3855,8 @@ skip_create_disk:
goto start_service_thread;
/* Set device limits. */
- set_bit(QUEUE_FLAG_NONROT, &dd->queue->queue_flags);
- clear_bit(QUEUE_FLAG_ADD_RANDOM, &dd->queue->queue_flags);
+ blk_queue_flag_set(QUEUE_FLAG_NONROT, dd->queue);
+ blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, dd->queue);
blk_queue_max_segments(dd->queue, MTIP_MAX_SG);
blk_queue_physical_block_size(dd->queue, 4096);
blk_queue_max_hw_sectors(dd->queue, 0xffff);
@@ -3866,7 +3866,7 @@ skip_create_disk:
/* Signal trim support */
if (dd->trim_supp == true) {
- set_bit(QUEUE_FLAG_DISCARD, &dd->queue->queue_flags);
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, dd->queue);
dd->queue->limits.discard_granularity = 4096;
blk_queue_max_discard_sectors(dd->queue,
MTIP_MAX_TRIM_ENTRY_LEN * MTIP_MAX_TRIM_ENTRIES);
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index 86258b00a1d4..afbc202ca6fd 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -964,7 +964,7 @@ static void nbd_parse_flags(struct nbd_device *nbd)
else
set_disk_ro(nbd->disk, false);
if (config->flags & NBD_FLAG_SEND_TRIM)
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, nbd->disk->queue);
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, nbd->disk->queue);
if (config->flags & NBD_FLAG_SEND_FLUSH) {
if (config->flags & NBD_FLAG_SEND_FUA)
blk_queue_write_cache(nbd->disk->queue, true, true);
@@ -1040,7 +1040,7 @@ static void nbd_config_put(struct nbd_device *nbd)
nbd->config = NULL;
nbd->tag_set.timeout = 0;
- queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, nbd->disk->queue);
+ blk_queue_flag_clear(QUEUE_FLAG_DISCARD, nbd->disk->queue);
mutex_unlock(&nbd->config_lock);
nbd_put(nbd);
@@ -1488,8 +1488,8 @@ static int nbd_dev_add(int index)
/*
* Tell the block layer that we are not a rotational device
*/
- queue_flag_set_unlocked(QUEUE_FLAG_NONROT, disk->queue);
- queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, disk->queue);
+ blk_queue_flag_set(QUEUE_FLAG_NONROT, disk->queue);
+ blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, disk->queue);
disk->queue->limits.discard_granularity = 512;
blk_queue_max_discard_sectors(disk->queue, UINT_MAX);
blk_queue_max_segment_size(disk->queue, UINT_MAX);
diff --git a/drivers/block/null_blk.c b/drivers/block/null_blk.c
index 287a09611c0f..a76553293a31 100644
--- a/drivers/block/null_blk.c
+++ b/drivers/block/null_blk.c
@@ -16,10 +16,8 @@
#include <linux/badblocks.h>
#include <linux/fault-inject.h>
-#define SECTOR_SHIFT 9
#define PAGE_SECTORS_SHIFT (PAGE_SHIFT - SECTOR_SHIFT)
#define PAGE_SECTORS (1 << PAGE_SECTORS_SHIFT)
-#define SECTOR_SIZE (1 << SECTOR_SHIFT)
#define SECTOR_MASK (PAGE_SECTORS - 1)
#define FREE_BATCH 16
@@ -29,6 +27,7 @@
#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION
static DECLARE_FAULT_ATTR(null_timeout_attr);
+static DECLARE_FAULT_ATTR(null_requeue_attr);
#endif
static inline u64 mb_per_tick(int mbps)
@@ -53,6 +52,7 @@ struct nullb_queue {
wait_queue_head_t wait;
unsigned int queue_depth;
struct nullb_device *dev;
+ unsigned int requeue_selection;
struct nullb_cmd *cmds;
};
@@ -72,6 +72,7 @@ enum nullb_device_flags {
NULLB_DEV_FL_CACHE = 3,
};
+#define MAP_SZ ((PAGE_SIZE >> SECTOR_SHIFT) + 2)
/*
* nullb_page is a page in memory for nullb devices.
*
@@ -86,10 +87,10 @@ enum nullb_device_flags {
*/
struct nullb_page {
struct page *page;
- unsigned long bitmap;
+ DECLARE_BITMAP(bitmap, MAP_SZ);
};
-#define NULLB_PAGE_LOCK (sizeof(unsigned long) * 8 - 1)
-#define NULLB_PAGE_FREE (sizeof(unsigned long) * 8 - 2)
+#define NULLB_PAGE_LOCK (MAP_SZ - 1)
+#define NULLB_PAGE_FREE (MAP_SZ - 2)
struct nullb_device {
struct nullb *nullb;
@@ -170,6 +171,9 @@ MODULE_PARM_DESC(home_node, "Home node for the device");
#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION
static char g_timeout_str[80];
module_param_string(timeout, g_timeout_str, sizeof(g_timeout_str), S_IRUGO);
+
+static char g_requeue_str[80];
+module_param_string(requeue, g_requeue_str, sizeof(g_requeue_str), S_IRUGO);
#endif
static int g_queue_mode = NULL_Q_MQ;
@@ -728,7 +732,7 @@ static struct nullb_page *null_alloc_page(gfp_t gfp_flags)
if (!t_page->page)
goto out_freepage;
- t_page->bitmap = 0;
+ memset(t_page->bitmap, 0, sizeof(t_page->bitmap));
return t_page;
out_freepage:
kfree(t_page);
@@ -738,13 +742,20 @@ out:
static void null_free_page(struct nullb_page *t_page)
{
- __set_bit(NULLB_PAGE_FREE, &t_page->bitmap);
- if (test_bit(NULLB_PAGE_LOCK, &t_page->bitmap))
+ __set_bit(NULLB_PAGE_FREE, t_page->bitmap);
+ if (test_bit(NULLB_PAGE_LOCK, t_page->bitmap))
return;
__free_page(t_page->page);
kfree(t_page);
}
+static bool null_page_empty(struct nullb_page *page)
+{
+ int size = MAP_SZ - 2;
+
+ return find_first_bit(page->bitmap, size) == size;
+}
+
static void null_free_sector(struct nullb *nullb, sector_t sector,
bool is_cache)
{
@@ -759,9 +770,9 @@ static void null_free_sector(struct nullb *nullb, sector_t sector,
t_page = radix_tree_lookup(root, idx);
if (t_page) {
- __clear_bit(sector_bit, &t_page->bitmap);
+ __clear_bit(sector_bit, t_page->bitmap);
- if (!t_page->bitmap) {
+ if (null_page_empty(t_page)) {
ret = radix_tree_delete_item(root, idx, t_page);
WARN_ON(ret != t_page);
null_free_page(ret);
@@ -832,7 +843,7 @@ static struct nullb_page *__null_lookup_page(struct nullb *nullb,
t_page = radix_tree_lookup(root, idx);
WARN_ON(t_page && t_page->page->index != idx);
- if (t_page && (for_write || test_bit(sector_bit, &t_page->bitmap)))
+ if (t_page && (for_write || test_bit(sector_bit, t_page->bitmap)))
return t_page;
return NULL;
@@ -895,10 +906,10 @@ static int null_flush_cache_page(struct nullb *nullb, struct nullb_page *c_page)
t_page = null_insert_page(nullb, idx << PAGE_SECTORS_SHIFT, true);
- __clear_bit(NULLB_PAGE_LOCK, &c_page->bitmap);
- if (test_bit(NULLB_PAGE_FREE, &c_page->bitmap)) {
+ __clear_bit(NULLB_PAGE_LOCK, c_page->bitmap);
+ if (test_bit(NULLB_PAGE_FREE, c_page->bitmap)) {
null_free_page(c_page);
- if (t_page && t_page->bitmap == 0) {
+ if (t_page && null_page_empty(t_page)) {
ret = radix_tree_delete_item(&nullb->dev->data,
idx, t_page);
null_free_page(t_page);
@@ -914,11 +925,11 @@ static int null_flush_cache_page(struct nullb *nullb, struct nullb_page *c_page)
for (i = 0; i < PAGE_SECTORS;
i += (nullb->dev->blocksize >> SECTOR_SHIFT)) {
- if (test_bit(i, &c_page->bitmap)) {
+ if (test_bit(i, c_page->bitmap)) {
offset = (i << SECTOR_SHIFT);
memcpy(dst + offset, src + offset,
nullb->dev->blocksize);
- __set_bit(i, &t_page->bitmap);
+ __set_bit(i, t_page->bitmap);
}
}
@@ -955,10 +966,10 @@ again:
* We found the page which is being flushed to disk by other
* threads
*/
- if (test_bit(NULLB_PAGE_LOCK, &c_pages[i]->bitmap))
+ if (test_bit(NULLB_PAGE_LOCK, c_pages[i]->bitmap))
c_pages[i] = NULL;
else
- __set_bit(NULLB_PAGE_LOCK, &c_pages[i]->bitmap);
+ __set_bit(NULLB_PAGE_LOCK, c_pages[i]->bitmap);
}
one_round = 0;
@@ -1011,7 +1022,7 @@ static int copy_to_nullb(struct nullb *nullb, struct page *source,
kunmap_atomic(dst);
kunmap_atomic(src);
- __set_bit(sector & SECTOR_MASK, &t_page->bitmap);
+ __set_bit(sector & SECTOR_MASK, t_page->bitmap);
if (is_fua)
null_free_sector(nullb, sector, true);
@@ -1380,7 +1391,15 @@ static bool should_timeout_request(struct request *rq)
if (g_timeout_str[0])
return should_fail(&null_timeout_attr, 1);
#endif
+ return false;
+}
+static bool should_requeue_request(struct request *rq)
+{
+#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION
+ if (g_requeue_str[0])
+ return should_fail(&null_requeue_attr, 1);
+#endif
return false;
}
@@ -1391,11 +1410,17 @@ static void null_request_fn(struct request_queue *q)
while ((rq = blk_fetch_request(q)) != NULL) {
struct nullb_cmd *cmd = rq->special;
- if (!should_timeout_request(rq)) {
- spin_unlock_irq(q->queue_lock);
- null_handle_cmd(cmd);
- spin_lock_irq(q->queue_lock);
+ /* just ignore the request */
+ if (should_timeout_request(rq))
+ continue;
+ if (should_requeue_request(rq)) {
+ blk_requeue_request(q, rq);
+ continue;
}
+
+ spin_unlock_irq(q->queue_lock);
+ null_handle_cmd(cmd);
+ spin_lock_irq(q->queue_lock);
}
}
@@ -1422,10 +1447,23 @@ static blk_status_t null_queue_rq(struct blk_mq_hw_ctx *hctx,
blk_mq_start_request(bd->rq);
- if (!should_timeout_request(bd->rq))
- return null_handle_cmd(cmd);
+ if (should_requeue_request(bd->rq)) {
+ /*
+ * Alternate between hitting the core BUSY path, and the
+ * driver driven requeue path
+ */
+ nq->requeue_selection++;
+ if (nq->requeue_selection & 1)
+ return BLK_STS_RESOURCE;
+ else {
+ blk_mq_requeue_request(bd->rq, true);
+ return BLK_STS_OK;
+ }
+ }
+ if (should_timeout_request(bd->rq))
+ return BLK_STS_OK;
- return BLK_STS_OK;
+ return null_handle_cmd(cmd);
}
static const struct blk_mq_ops null_mq_ops = {
@@ -1485,7 +1523,7 @@ static void null_config_discard(struct nullb *nullb)
nullb->q->limits.discard_granularity = nullb->dev->blocksize;
nullb->q->limits.discard_alignment = nullb->dev->blocksize;
blk_queue_max_discard_sectors(nullb->q, UINT_MAX >> 9);
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, nullb->q);
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, nullb->q);
}
static int null_open(struct block_device *bdev, fmode_t mode)
@@ -1659,16 +1697,27 @@ static void null_validate_conf(struct nullb_device *dev)
dev->mbps = 0;
}
-static bool null_setup_fault(void)
-{
#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION
- if (!g_timeout_str[0])
+static bool __null_setup_fault(struct fault_attr *attr, char *str)
+{
+ if (!str[0])
return true;
- if (!setup_fault_attr(&null_timeout_attr, g_timeout_str))
+ if (!setup_fault_attr(attr, str))
return false;
- null_timeout_attr.verbose = 0;
+ attr->verbose = 0;
+ return true;
+}
+#endif
+
+static bool null_setup_fault(void)
+{
+#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION
+ if (!__null_setup_fault(&null_timeout_attr, g_timeout_str))
+ return false;
+ if (!__null_setup_fault(&null_requeue_attr, g_requeue_str))
+ return false;
#endif
return true;
}
@@ -1717,7 +1766,8 @@ static int null_add_dev(struct nullb_device *dev)
}
null_init_queues(nullb);
} else if (dev->queue_mode == NULL_Q_BIO) {
- nullb->q = blk_alloc_queue_node(GFP_KERNEL, dev->home_node);
+ nullb->q = blk_alloc_queue_node(GFP_KERNEL, dev->home_node,
+ NULL);
if (!nullb->q) {
rv = -ENOMEM;
goto out_cleanup_queues;
@@ -1758,8 +1808,8 @@ static int null_add_dev(struct nullb_device *dev)
}
nullb->q->queuedata = nullb;
- queue_flag_set_unlocked(QUEUE_FLAG_NONROT, nullb->q);
- queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, nullb->q);
+ blk_queue_flag_set(QUEUE_FLAG_NONROT, nullb->q);
+ blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, nullb->q);
mutex_lock(&lock);
nullb->index = ida_simple_get(&nullb_indexes, 0, 0, GFP_KERNEL);
@@ -1802,10 +1852,6 @@ static int __init null_init(void)
struct nullb *nullb;
struct nullb_device *dev;
- /* check for nullb_page.bitmap */
- if (sizeof(unsigned long) * 8 - 2 < (PAGE_SIZE >> SECTOR_SHIFT))
- return -EINVAL;
-
if (g_bs > PAGE_SIZE) {
pr_warn("null_blk: invalid block size\n");
pr_warn("null_blk: defaults block size to %lu\n", PAGE_SIZE);
diff --git a/drivers/block/paride/pcd.c b/drivers/block/paride/pcd.c
index 7b8c6368beb7..a026211afb51 100644
--- a/drivers/block/paride/pcd.c
+++ b/drivers/block/paride/pcd.c
@@ -230,6 +230,8 @@ static int pcd_block_open(struct block_device *bdev, fmode_t mode)
struct pcd_unit *cd = bdev->bd_disk->private_data;
int ret;
+ check_disk_change(bdev);
+
mutex_lock(&pcd_mutex);
ret = cdrom_open(&cd->info, bdev, mode);
mutex_unlock(&pcd_mutex);
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index 8e40da093766..8e8b04cc569a 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -32,6 +32,7 @@
#include <linux/ceph/osd_client.h>
#include <linux/ceph/mon_client.h>
#include <linux/ceph/cls_lock_client.h>
+#include <linux/ceph/striper.h>
#include <linux/ceph/decode.h>
#include <linux/parser.h>
#include <linux/bsearch.h>
@@ -51,15 +52,6 @@
#define RBD_DEBUG /* Activate rbd_assert() calls */
/*
- * The basic unit of block I/O is a sector. It is interpreted in a
- * number of contexts in Linux (blk, bio, genhd), but the default is
- * universally 512 bytes. These symbols are just slightly more
- * meaningful than the bare numbers they represent.
- */
-#define SECTOR_SHIFT 9
-#define SECTOR_SIZE (1ULL << SECTOR_SHIFT)
-
-/*
* Increment the given counter and return its updated value.
* If the counter is already 0 it will not be incremented.
* If the counter is already at its maximum value returns
@@ -209,95 +201,81 @@ struct rbd_client {
};
struct rbd_img_request;
-typedef void (*rbd_img_callback_t)(struct rbd_img_request *);
-
-#define BAD_WHICH U32_MAX /* Good which or bad which, which? */
-
-struct rbd_obj_request;
-typedef void (*rbd_obj_callback_t)(struct rbd_obj_request *);
enum obj_request_type {
- OBJ_REQUEST_NODATA, OBJ_REQUEST_BIO, OBJ_REQUEST_PAGES
+ OBJ_REQUEST_NODATA = 1,
+ OBJ_REQUEST_BIO, /* pointer into provided bio (list) */
+ OBJ_REQUEST_BVECS, /* pointer into provided bio_vec array */
+ OBJ_REQUEST_OWN_BVECS, /* private bio_vec array, doesn't own pages */
};
enum obj_operation_type {
+ OBJ_OP_READ = 1,
OBJ_OP_WRITE,
- OBJ_OP_READ,
OBJ_OP_DISCARD,
};
-enum obj_req_flags {
- OBJ_REQ_DONE, /* completion flag: not done = 0, done = 1 */
- OBJ_REQ_IMG_DATA, /* object usage: standalone = 0, image = 1 */
- OBJ_REQ_KNOWN, /* EXISTS flag valid: no = 0, yes = 1 */
- OBJ_REQ_EXISTS, /* target exists: no = 0, yes = 1 */
+/*
+ * Writes go through the following state machine to deal with
+ * layering:
+ *
+ * need copyup
+ * RBD_OBJ_WRITE_GUARD ---------------> RBD_OBJ_WRITE_COPYUP
+ * | ^ |
+ * v \------------------------------/
+ * done
+ * ^
+ * |
+ * RBD_OBJ_WRITE_FLAT
+ *
+ * Writes start in RBD_OBJ_WRITE_GUARD or _FLAT, depending on whether
+ * there is a parent or not.
+ */
+enum rbd_obj_write_state {
+ RBD_OBJ_WRITE_FLAT = 1,
+ RBD_OBJ_WRITE_GUARD,
+ RBD_OBJ_WRITE_COPYUP,
};
struct rbd_obj_request {
- u64 object_no;
- u64 offset; /* object start byte */
- u64 length; /* bytes from offset */
- unsigned long flags;
-
- /*
- * An object request associated with an image will have its
- * img_data flag set; a standalone object request will not.
- *
- * A standalone object request will have which == BAD_WHICH
- * and a null obj_request pointer.
- *
- * An object request initiated in support of a layered image
- * object (to check for its existence before a write) will
- * have which == BAD_WHICH and a non-null obj_request pointer.
- *
- * Finally, an object request for rbd image data will have
- * which != BAD_WHICH, and will have a non-null img_request
- * pointer. The value of which will be in the range
- * 0..(img_request->obj_request_count-1).
- */
+ struct ceph_object_extent ex;
union {
- struct rbd_obj_request *obj_request; /* STAT op */
- struct {
- struct rbd_img_request *img_request;
- u64 img_offset;
- /* links for img_request->obj_requests list */
- struct list_head links;
- };
+ bool tried_parent; /* for reads */
+ enum rbd_obj_write_state write_state; /* for writes */
};
- u32 which; /* posn image request list */
- enum obj_request_type type;
+ struct rbd_img_request *img_request;
+ struct ceph_file_extent *img_extents;
+ u32 num_img_extents;
+
union {
- struct bio *bio_list;
+ struct ceph_bio_iter bio_pos;
struct {
- struct page **pages;
- u32 page_count;
+ struct ceph_bvec_iter bvec_pos;
+ u32 bvec_count;
+ u32 bvec_idx;
};
};
- struct page **copyup_pages;
- u32 copyup_page_count;
+ struct bio_vec *copyup_bvecs;
+ u32 copyup_bvec_count;
struct ceph_osd_request *osd_req;
u64 xferred; /* bytes transferred */
int result;
- rbd_obj_callback_t callback;
-
struct kref kref;
};
enum img_req_flags {
- IMG_REQ_WRITE, /* I/O direction: read = 0, write = 1 */
IMG_REQ_CHILD, /* initiator: block = 0, child image = 1 */
IMG_REQ_LAYERED, /* ENOENT handling: normal = 0, layered = 1 */
- IMG_REQ_DISCARD, /* discard: normal = 0, discard request = 1 */
};
struct rbd_img_request {
struct rbd_device *rbd_dev;
- u64 offset; /* starting image byte offset */
- u64 length; /* byte count from offset */
+ enum obj_operation_type op_type;
+ enum obj_request_type data_type;
unsigned long flags;
union {
u64 snap_id; /* for reads */
@@ -307,26 +285,21 @@ struct rbd_img_request {
struct request *rq; /* block request */
struct rbd_obj_request *obj_request; /* obj req initiator */
};
- struct page **copyup_pages;
- u32 copyup_page_count;
- spinlock_t completion_lock;/* protects next_completion */
- u32 next_completion;
- rbd_img_callback_t callback;
+ spinlock_t completion_lock;
u64 xferred;/* aggregate bytes transferred */
int result; /* first nonzero obj_request result */
+ struct list_head object_extents; /* obj_req.ex structs */
u32 obj_request_count;
- struct list_head obj_requests; /* rbd_obj_request structs */
+ u32 pending_count;
struct kref kref;
};
#define for_each_obj_request(ireq, oreq) \
- list_for_each_entry(oreq, &(ireq)->obj_requests, links)
-#define for_each_obj_request_from(ireq, oreq) \
- list_for_each_entry_from(oreq, &(ireq)->obj_requests, links)
+ list_for_each_entry(oreq, &(ireq)->object_extents, ex.oe_item)
#define for_each_obj_request_safe(ireq, oreq, n) \
- list_for_each_entry_safe_reverse(oreq, n, &(ireq)->obj_requests, links)
+ list_for_each_entry_safe(oreq, n, &(ireq)->object_extents, ex.oe_item)
enum rbd_watch_state {
RBD_WATCH_STATE_UNREGISTERED,
@@ -442,8 +415,6 @@ static DEFINE_SPINLOCK(rbd_client_list_lock);
static struct kmem_cache *rbd_img_request_cache;
static struct kmem_cache *rbd_obj_request_cache;
-static struct bio_set *rbd_bio_clone;
-
static int rbd_major;
static DEFINE_IDA(rbd_dev_id_ida);
@@ -456,8 +427,6 @@ static bool single_major = true;
module_param(single_major, bool, S_IRUGO);
MODULE_PARM_DESC(single_major, "Use a single major number for all rbd devices (default: true)");
-static int rbd_img_request_submit(struct rbd_img_request *img_request);
-
static ssize_t rbd_add(struct bus_type *bus, const char *buf,
size_t count);
static ssize_t rbd_remove(struct bus_type *bus, const char *buf,
@@ -467,7 +436,6 @@ static ssize_t rbd_add_single_major(struct bus_type *bus, const char *buf,
static ssize_t rbd_remove_single_major(struct bus_type *bus, const char *buf,
size_t count);
static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth);
-static void rbd_spec_put(struct rbd_spec *spec);
static int rbd_dev_id_to_minor(int dev_id)
{
@@ -586,9 +554,6 @@ void rbd_warn(struct rbd_device *rbd_dev, const char *fmt, ...)
# define rbd_assert(expr) ((void) 0)
#endif /* !RBD_DEBUG */
-static void rbd_osd_copyup_callback(struct rbd_obj_request *obj_request);
-static int rbd_img_obj_request_submit(struct rbd_obj_request *obj_request);
-static void rbd_img_parent_read(struct rbd_obj_request *obj_request);
static void rbd_dev_remove_parent(struct rbd_device *rbd_dev);
static int rbd_dev_refresh(struct rbd_device *rbd_dev);
@@ -767,6 +732,7 @@ static struct rbd_client *rbd_client_find(struct ceph_options *ceph_opts)
*/
enum {
Opt_queue_depth,
+ Opt_lock_timeout,
Opt_last_int,
/* int args above */
Opt_last_string,
@@ -775,11 +741,13 @@ enum {
Opt_read_write,
Opt_lock_on_read,
Opt_exclusive,
+ Opt_notrim,
Opt_err
};
static match_table_t rbd_opts_tokens = {
{Opt_queue_depth, "queue_depth=%d"},
+ {Opt_lock_timeout, "lock_timeout=%d"},
/* int args above */
/* string args above */
{Opt_read_only, "read_only"},
@@ -788,20 +756,25 @@ static match_table_t rbd_opts_tokens = {
{Opt_read_write, "rw"}, /* Alternate spelling */
{Opt_lock_on_read, "lock_on_read"},
{Opt_exclusive, "exclusive"},
+ {Opt_notrim, "notrim"},
{Opt_err, NULL}
};
struct rbd_options {
int queue_depth;
+ unsigned long lock_timeout;
bool read_only;
bool lock_on_read;
bool exclusive;
+ bool trim;
};
#define RBD_QUEUE_DEPTH_DEFAULT BLKDEV_MAX_RQ
+#define RBD_LOCK_TIMEOUT_DEFAULT 0 /* no timeout */
#define RBD_READ_ONLY_DEFAULT false
#define RBD_LOCK_ON_READ_DEFAULT false
#define RBD_EXCLUSIVE_DEFAULT false
+#define RBD_TRIM_DEFAULT true
static int parse_rbd_opts_token(char *c, void *private)
{
@@ -831,6 +804,14 @@ static int parse_rbd_opts_token(char *c, void *private)
}
rbd_opts->queue_depth = intval;
break;
+ case Opt_lock_timeout:
+ /* 0 is "wait forever" (i.e. infinite timeout) */
+ if (intval < 0 || intval > INT_MAX / 1000) {
+ pr_err("lock_timeout out of range\n");
+ return -EINVAL;
+ }
+ rbd_opts->lock_timeout = msecs_to_jiffies(intval * 1000);
+ break;
case Opt_read_only:
rbd_opts->read_only = true;
break;
@@ -843,6 +824,9 @@ static int parse_rbd_opts_token(char *c, void *private)
case Opt_exclusive:
rbd_opts->exclusive = true;
break;
+ case Opt_notrim:
+ rbd_opts->trim = false;
+ break;
default:
/* libceph prints "bad option" msg */
return -EINVAL;
@@ -866,26 +850,6 @@ static char* obj_op_name(enum obj_operation_type op_type)
}
/*
- * Get a ceph client with specific addr and configuration, if one does
- * not exist create it. Either way, ceph_opts is consumed by this
- * function.
- */
-static struct rbd_client *rbd_get_client(struct ceph_options *ceph_opts)
-{
- struct rbd_client *rbdc;
-
- mutex_lock_nested(&client_mutex, SINGLE_DEPTH_NESTING);
- rbdc = rbd_client_find(ceph_opts);
- if (rbdc) /* using an existing client */
- ceph_destroy_options(ceph_opts);
- else
- rbdc = rbd_client_create(ceph_opts);
- mutex_unlock(&client_mutex);
-
- return rbdc;
-}
-
-/*
* Destroy ceph client
*
* Caller must hold rbd_client_list_lock.
@@ -913,6 +877,56 @@ static void rbd_put_client(struct rbd_client *rbdc)
kref_put(&rbdc->kref, rbd_client_release);
}
+static int wait_for_latest_osdmap(struct ceph_client *client)
+{
+ u64 newest_epoch;
+ int ret;
+
+ ret = ceph_monc_get_version(&client->monc, "osdmap", &newest_epoch);
+ if (ret)
+ return ret;
+
+ if (client->osdc.osdmap->epoch >= newest_epoch)
+ return 0;
+
+ ceph_osdc_maybe_request_map(&client->osdc);
+ return ceph_monc_wait_osdmap(&client->monc, newest_epoch,
+ client->options->mount_timeout);
+}
+
+/*
+ * Get a ceph client with specific addr and configuration, if one does
+ * not exist create it. Either way, ceph_opts is consumed by this
+ * function.
+ */
+static struct rbd_client *rbd_get_client(struct ceph_options *ceph_opts)
+{
+ struct rbd_client *rbdc;
+ int ret;
+
+ mutex_lock_nested(&client_mutex, SINGLE_DEPTH_NESTING);
+ rbdc = rbd_client_find(ceph_opts);
+ if (rbdc) {
+ ceph_destroy_options(ceph_opts);
+
+ /*
+ * Using an existing client. Make sure ->pg_pools is up to
+ * date before we look up the pool id in do_rbd_add().
+ */
+ ret = wait_for_latest_osdmap(rbdc->client);
+ if (ret) {
+ rbd_warn(NULL, "failed to get latest osdmap: %d", ret);
+ rbd_put_client(rbdc);
+ rbdc = ERR_PTR(ret);
+ }
+ } else {
+ rbdc = rbd_client_create(ceph_opts);
+ }
+ mutex_unlock(&client_mutex);
+
+ return rbdc;
+}
+
static bool rbd_image_format_valid(u32 image_format)
{
return image_format == 1 || image_format == 2;
@@ -1232,272 +1246,59 @@ static void rbd_dev_mapping_clear(struct rbd_device *rbd_dev)
rbd_dev->mapping.features = 0;
}
-static u64 rbd_segment_offset(struct rbd_device *rbd_dev, u64 offset)
-{
- u64 segment_size = rbd_obj_bytes(&rbd_dev->header);
-
- return offset & (segment_size - 1);
-}
-
-static u64 rbd_segment_length(struct rbd_device *rbd_dev,
- u64 offset, u64 length)
-{
- u64 segment_size = rbd_obj_bytes(&rbd_dev->header);
-
- offset &= segment_size - 1;
-
- rbd_assert(length <= U64_MAX - offset);
- if (offset + length > segment_size)
- length = segment_size - offset;
-
- return length;
-}
-
-/*
- * bio helpers
- */
-
-static void bio_chain_put(struct bio *chain)
-{
- struct bio *tmp;
-
- while (chain) {
- tmp = chain;
- chain = chain->bi_next;
- bio_put(tmp);
- }
-}
-
-/*
- * zeros a bio chain, starting at specific offset
- */
-static void zero_bio_chain(struct bio *chain, int start_ofs)
+static void zero_bvec(struct bio_vec *bv)
{
- struct bio_vec bv;
- struct bvec_iter iter;
- unsigned long flags;
void *buf;
- int pos = 0;
-
- while (chain) {
- bio_for_each_segment(bv, chain, iter) {
- if (pos + bv.bv_len > start_ofs) {
- int remainder = max(start_ofs - pos, 0);
- buf = bvec_kmap_irq(&bv, &flags);
- memset(buf + remainder, 0,
- bv.bv_len - remainder);
- flush_dcache_page(bv.bv_page);
- bvec_kunmap_irq(buf, &flags);
- }
- pos += bv.bv_len;
- }
+ unsigned long flags;
- chain = chain->bi_next;
- }
+ buf = bvec_kmap_irq(bv, &flags);
+ memset(buf, 0, bv->bv_len);
+ flush_dcache_page(bv->bv_page);
+ bvec_kunmap_irq(buf, &flags);
}
-/*
- * similar to zero_bio_chain(), zeros data defined by a page array,
- * starting at the given byte offset from the start of the array and
- * continuing up to the given end offset. The pages array is
- * assumed to be big enough to hold all bytes up to the end.
- */
-static void zero_pages(struct page **pages, u64 offset, u64 end)
+static void zero_bios(struct ceph_bio_iter *bio_pos, u32 off, u32 bytes)
{
- struct page **page = &pages[offset >> PAGE_SHIFT];
-
- rbd_assert(end > offset);
- rbd_assert(end - offset <= (u64)SIZE_MAX);
- while (offset < end) {
- size_t page_offset;
- size_t length;
- unsigned long flags;
- void *kaddr;
+ struct ceph_bio_iter it = *bio_pos;
- page_offset = offset & ~PAGE_MASK;
- length = min_t(size_t, PAGE_SIZE - page_offset, end - offset);
- local_irq_save(flags);
- kaddr = kmap_atomic(*page);
- memset(kaddr + page_offset, 0, length);
- flush_dcache_page(*page);
- kunmap_atomic(kaddr);
- local_irq_restore(flags);
-
- offset += length;
- page++;
- }
+ ceph_bio_iter_advance(&it, off);
+ ceph_bio_iter_advance_step(&it, bytes, ({
+ zero_bvec(&bv);
+ }));
}
-/*
- * Clone a portion of a bio, starting at the given byte offset
- * and continuing for the number of bytes indicated.
- */
-static struct bio *bio_clone_range(struct bio *bio_src,
- unsigned int offset,
- unsigned int len,
- gfp_t gfpmask)
+static void zero_bvecs(struct ceph_bvec_iter *bvec_pos, u32 off, u32 bytes)
{
- struct bio *bio;
-
- bio = bio_clone_fast(bio_src, gfpmask, rbd_bio_clone);
- if (!bio)
- return NULL; /* ENOMEM */
-
- bio_advance(bio, offset);
- bio->bi_iter.bi_size = len;
+ struct ceph_bvec_iter it = *bvec_pos;
- return bio;
+ ceph_bvec_iter_advance(&it, off);
+ ceph_bvec_iter_advance_step(&it, bytes, ({
+ zero_bvec(&bv);
+ }));
}
/*
- * Clone a portion of a bio chain, starting at the given byte offset
- * into the first bio in the source chain and continuing for the
- * number of bytes indicated. The result is another bio chain of
- * exactly the given length, or a null pointer on error.
+ * Zero a range in @obj_req data buffer defined by a bio (list) or
+ * (private) bio_vec array.
*
- * The bio_src and offset parameters are both in-out. On entry they
- * refer to the first source bio and the offset into that bio where
- * the start of data to be cloned is located.
- *
- * On return, bio_src is updated to refer to the bio in the source
- * chain that contains first un-cloned byte, and *offset will
- * contain the offset of that byte within that bio.
+ * @off is relative to the start of the data buffer.
*/
-static struct bio *bio_chain_clone_range(struct bio **bio_src,
- unsigned int *offset,
- unsigned int len,
- gfp_t gfpmask)
+static void rbd_obj_zero_range(struct rbd_obj_request *obj_req, u32 off,
+ u32 bytes)
{
- struct bio *bi = *bio_src;
- unsigned int off = *offset;
- struct bio *chain = NULL;
- struct bio **end;
-
- /* Build up a chain of clone bios up to the limit */
-
- if (!bi || off >= bi->bi_iter.bi_size || !len)
- return NULL; /* Nothing to clone */
-
- end = &chain;
- while (len) {
- unsigned int bi_size;
- struct bio *bio;
-
- if (!bi) {
- rbd_warn(NULL, "bio_chain exhausted with %u left", len);
- goto out_err; /* EINVAL; ran out of bio's */
- }
- bi_size = min_t(unsigned int, bi->bi_iter.bi_size - off, len);
- bio = bio_clone_range(bi, off, bi_size, gfpmask);
- if (!bio)
- goto out_err; /* ENOMEM */
-
- *end = bio;
- end = &bio->bi_next;
-
- off += bi_size;
- if (off == bi->bi_iter.bi_size) {
- bi = bi->bi_next;
- off = 0;
- }
- len -= bi_size;
- }
- *bio_src = bi;
- *offset = off;
-
- return chain;
-out_err:
- bio_chain_put(chain);
-
- return NULL;
-}
-
-/*
- * The default/initial value for all object request flags is 0. For
- * each flag, once its value is set to 1 it is never reset to 0
- * again.
- */
-static void obj_request_img_data_set(struct rbd_obj_request *obj_request)
-{
- if (test_and_set_bit(OBJ_REQ_IMG_DATA, &obj_request->flags)) {
- struct rbd_device *rbd_dev;
-
- rbd_dev = obj_request->img_request->rbd_dev;
- rbd_warn(rbd_dev, "obj_request %p already marked img_data",
- obj_request);
- }
-}
-
-static bool obj_request_img_data_test(struct rbd_obj_request *obj_request)
-{
- smp_mb();
- return test_bit(OBJ_REQ_IMG_DATA, &obj_request->flags) != 0;
-}
-
-static void obj_request_done_set(struct rbd_obj_request *obj_request)
-{
- if (test_and_set_bit(OBJ_REQ_DONE, &obj_request->flags)) {
- struct rbd_device *rbd_dev = NULL;
-
- if (obj_request_img_data_test(obj_request))
- rbd_dev = obj_request->img_request->rbd_dev;
- rbd_warn(rbd_dev, "obj_request %p already marked done",
- obj_request);
+ switch (obj_req->img_request->data_type) {
+ case OBJ_REQUEST_BIO:
+ zero_bios(&obj_req->bio_pos, off, bytes);
+ break;
+ case OBJ_REQUEST_BVECS:
+ case OBJ_REQUEST_OWN_BVECS:
+ zero_bvecs(&obj_req->bvec_pos, off, bytes);
+ break;
+ default:
+ rbd_assert(0);
}
}
-static bool obj_request_done_test(struct rbd_obj_request *obj_request)
-{
- smp_mb();
- return test_bit(OBJ_REQ_DONE, &obj_request->flags) != 0;
-}
-
-/*
- * This sets the KNOWN flag after (possibly) setting the EXISTS
- * flag. The latter is set based on the "exists" value provided.
- *
- * Note that for our purposes once an object exists it never goes
- * away again. It's possible that the response from two existence
- * checks are separated by the creation of the target object, and
- * the first ("doesn't exist") response arrives *after* the second
- * ("does exist"). In that case we ignore the second one.
- */
-static void obj_request_existence_set(struct rbd_obj_request *obj_request,
- bool exists)
-{
- if (exists)
- set_bit(OBJ_REQ_EXISTS, &obj_request->flags);
- set_bit(OBJ_REQ_KNOWN, &obj_request->flags);
- smp_mb();
-}
-
-static bool obj_request_known_test(struct rbd_obj_request *obj_request)
-{
- smp_mb();
- return test_bit(OBJ_REQ_KNOWN, &obj_request->flags) != 0;
-}
-
-static bool obj_request_exists_test(struct rbd_obj_request *obj_request)
-{
- smp_mb();
- return test_bit(OBJ_REQ_EXISTS, &obj_request->flags) != 0;
-}
-
-static bool obj_request_overlaps_parent(struct rbd_obj_request *obj_request)
-{
- struct rbd_device *rbd_dev = obj_request->img_request->rbd_dev;
-
- return obj_request->img_offset <
- round_up(rbd_dev->parent_overlap, rbd_obj_bytes(&rbd_dev->header));
-}
-
-static void rbd_obj_request_get(struct rbd_obj_request *obj_request)
-{
- dout("%s: obj %p (was %d)\n", __func__, obj_request,
- kref_read(&obj_request->kref));
- kref_get(&obj_request->kref);
-}
-
static void rbd_obj_request_destroy(struct kref *kref);
static void rbd_obj_request_put(struct rbd_obj_request *obj_request)
{
@@ -1514,18 +1315,13 @@ static void rbd_img_request_get(struct rbd_img_request *img_request)
kref_get(&img_request->kref);
}
-static bool img_request_child_test(struct rbd_img_request *img_request);
-static void rbd_parent_request_destroy(struct kref *kref);
static void rbd_img_request_destroy(struct kref *kref);
static void rbd_img_request_put(struct rbd_img_request *img_request)
{
rbd_assert(img_request != NULL);
dout("%s: img %p (was %d)\n", __func__, img_request,
kref_read(&img_request->kref));
- if (img_request_child_test(img_request))
- kref_put(&img_request->kref, rbd_parent_request_destroy);
- else
- kref_put(&img_request->kref, rbd_img_request_destroy);
+ kref_put(&img_request->kref, rbd_img_request_destroy);
}
static inline void rbd_img_obj_request_add(struct rbd_img_request *img_request,
@@ -1535,139 +1331,37 @@ static inline void rbd_img_obj_request_add(struct rbd_img_request *img_request,
/* Image request now owns object's original reference */
obj_request->img_request = img_request;
- obj_request->which = img_request->obj_request_count;
- rbd_assert(!obj_request_img_data_test(obj_request));
- obj_request_img_data_set(obj_request);
- rbd_assert(obj_request->which != BAD_WHICH);
img_request->obj_request_count++;
- list_add_tail(&obj_request->links, &img_request->obj_requests);
- dout("%s: img %p obj %p w=%u\n", __func__, img_request, obj_request,
- obj_request->which);
+ img_request->pending_count++;
+ dout("%s: img %p obj %p\n", __func__, img_request, obj_request);
}
static inline void rbd_img_obj_request_del(struct rbd_img_request *img_request,
struct rbd_obj_request *obj_request)
{
- rbd_assert(obj_request->which != BAD_WHICH);
-
- dout("%s: img %p obj %p w=%u\n", __func__, img_request, obj_request,
- obj_request->which);
- list_del(&obj_request->links);
+ dout("%s: img %p obj %p\n", __func__, img_request, obj_request);
+ list_del(&obj_request->ex.oe_item);
rbd_assert(img_request->obj_request_count > 0);
img_request->obj_request_count--;
- rbd_assert(obj_request->which == img_request->obj_request_count);
- obj_request->which = BAD_WHICH;
- rbd_assert(obj_request_img_data_test(obj_request));
rbd_assert(obj_request->img_request == img_request);
- obj_request->img_request = NULL;
- obj_request->callback = NULL;
rbd_obj_request_put(obj_request);
}
-static bool obj_request_type_valid(enum obj_request_type type)
-{
- switch (type) {
- case OBJ_REQUEST_NODATA:
- case OBJ_REQUEST_BIO:
- case OBJ_REQUEST_PAGES:
- return true;
- default:
- return false;
- }
-}
-
-static void rbd_img_obj_callback(struct rbd_obj_request *obj_request);
-
static void rbd_obj_request_submit(struct rbd_obj_request *obj_request)
{
struct ceph_osd_request *osd_req = obj_request->osd_req;
dout("%s %p object_no %016llx %llu~%llu osd_req %p\n", __func__,
- obj_request, obj_request->object_no, obj_request->offset,
- obj_request->length, osd_req);
- if (obj_request_img_data_test(obj_request)) {
- WARN_ON(obj_request->callback != rbd_img_obj_callback);
- rbd_img_request_get(obj_request->img_request);
- }
+ obj_request, obj_request->ex.oe_objno, obj_request->ex.oe_off,
+ obj_request->ex.oe_len, osd_req);
ceph_osdc_start_request(osd_req->r_osdc, osd_req, false);
}
-static void rbd_img_request_complete(struct rbd_img_request *img_request)
-{
-
- dout("%s: img %p\n", __func__, img_request);
-
- /*
- * If no error occurred, compute the aggregate transfer
- * count for the image request. We could instead use
- * atomic64_cmpxchg() to update it as each object request
- * completes; not clear which way is better off hand.
- */
- if (!img_request->result) {
- struct rbd_obj_request *obj_request;
- u64 xferred = 0;
-
- for_each_obj_request(img_request, obj_request)
- xferred += obj_request->xferred;
- img_request->xferred = xferred;
- }
-
- if (img_request->callback)
- img_request->callback(img_request);
- else
- rbd_img_request_put(img_request);
-}
-
/*
* The default/initial value for all image request flags is 0. Each
* is conditionally set to 1 at image request initialization time
* and currently never change thereafter.
*/
-static void img_request_write_set(struct rbd_img_request *img_request)
-{
- set_bit(IMG_REQ_WRITE, &img_request->flags);
- smp_mb();
-}
-
-static bool img_request_write_test(struct rbd_img_request *img_request)
-{
- smp_mb();
- return test_bit(IMG_REQ_WRITE, &img_request->flags) != 0;
-}
-
-/*
- * Set the discard flag when the img_request is an discard request
- */
-static void img_request_discard_set(struct rbd_img_request *img_request)
-{
- set_bit(IMG_REQ_DISCARD, &img_request->flags);
- smp_mb();
-}
-
-static bool img_request_discard_test(struct rbd_img_request *img_request)
-{
- smp_mb();
- return test_bit(IMG_REQ_DISCARD, &img_request->flags) != 0;
-}
-
-static void img_request_child_set(struct rbd_img_request *img_request)
-{
- set_bit(IMG_REQ_CHILD, &img_request->flags);
- smp_mb();
-}
-
-static void img_request_child_clear(struct rbd_img_request *img_request)
-{
- clear_bit(IMG_REQ_CHILD, &img_request->flags);
- smp_mb();
-}
-
-static bool img_request_child_test(struct rbd_img_request *img_request)
-{
- smp_mb();
- return test_bit(IMG_REQ_CHILD, &img_request->flags) != 0;
-}
-
static void img_request_layered_set(struct rbd_img_request *img_request)
{
set_bit(IMG_REQ_LAYERED, &img_request->flags);
@@ -1686,209 +1380,70 @@ static bool img_request_layered_test(struct rbd_img_request *img_request)
return test_bit(IMG_REQ_LAYERED, &img_request->flags) != 0;
}
-static enum obj_operation_type
-rbd_img_request_op_type(struct rbd_img_request *img_request)
-{
- if (img_request_write_test(img_request))
- return OBJ_OP_WRITE;
- else if (img_request_discard_test(img_request))
- return OBJ_OP_DISCARD;
- else
- return OBJ_OP_READ;
-}
-
-static void
-rbd_img_obj_request_read_callback(struct rbd_obj_request *obj_request)
-{
- u64 xferred = obj_request->xferred;
- u64 length = obj_request->length;
-
- dout("%s: obj %p img %p result %d %llu/%llu\n", __func__,
- obj_request, obj_request->img_request, obj_request->result,
- xferred, length);
- /*
- * ENOENT means a hole in the image. We zero-fill the entire
- * length of the request. A short read also implies zero-fill
- * to the end of the request. An error requires the whole
- * length of the request to be reported finished with an error
- * to the block layer. In each case we update the xferred
- * count to indicate the whole request was satisfied.
- */
- rbd_assert(obj_request->type != OBJ_REQUEST_NODATA);
- if (obj_request->result == -ENOENT) {
- if (obj_request->type == OBJ_REQUEST_BIO)
- zero_bio_chain(obj_request->bio_list, 0);
- else
- zero_pages(obj_request->pages, 0, length);
- obj_request->result = 0;
- } else if (xferred < length && !obj_request->result) {
- if (obj_request->type == OBJ_REQUEST_BIO)
- zero_bio_chain(obj_request->bio_list, xferred);
- else
- zero_pages(obj_request->pages, xferred, length);
- }
- obj_request->xferred = length;
- obj_request_done_set(obj_request);
-}
-
-static void rbd_obj_request_complete(struct rbd_obj_request *obj_request)
+static bool rbd_obj_is_entire(struct rbd_obj_request *obj_req)
{
- dout("%s: obj %p cb %p\n", __func__, obj_request,
- obj_request->callback);
- obj_request->callback(obj_request);
-}
+ struct rbd_device *rbd_dev = obj_req->img_request->rbd_dev;
-static void rbd_obj_request_error(struct rbd_obj_request *obj_request, int err)
-{
- obj_request->result = err;
- obj_request->xferred = 0;
- /*
- * kludge - mirror rbd_obj_request_submit() to match a put in
- * rbd_img_obj_callback()
- */
- if (obj_request_img_data_test(obj_request)) {
- WARN_ON(obj_request->callback != rbd_img_obj_callback);
- rbd_img_request_get(obj_request->img_request);
- }
- obj_request_done_set(obj_request);
- rbd_obj_request_complete(obj_request);
+ return !obj_req->ex.oe_off &&
+ obj_req->ex.oe_len == rbd_dev->layout.object_size;
}
-static void rbd_osd_read_callback(struct rbd_obj_request *obj_request)
+static bool rbd_obj_is_tail(struct rbd_obj_request *obj_req)
{
- struct rbd_img_request *img_request = NULL;
- struct rbd_device *rbd_dev = NULL;
- bool layered = false;
-
- if (obj_request_img_data_test(obj_request)) {
- img_request = obj_request->img_request;
- layered = img_request && img_request_layered_test(img_request);
- rbd_dev = img_request->rbd_dev;
- }
-
- dout("%s: obj %p img %p result %d %llu/%llu\n", __func__,
- obj_request, img_request, obj_request->result,
- obj_request->xferred, obj_request->length);
- if (layered && obj_request->result == -ENOENT &&
- obj_request->img_offset < rbd_dev->parent_overlap)
- rbd_img_parent_read(obj_request);
- else if (img_request)
- rbd_img_obj_request_read_callback(obj_request);
- else
- obj_request_done_set(obj_request);
-}
+ struct rbd_device *rbd_dev = obj_req->img_request->rbd_dev;
-static void rbd_osd_write_callback(struct rbd_obj_request *obj_request)
-{
- dout("%s: obj %p result %d %llu\n", __func__, obj_request,
- obj_request->result, obj_request->length);
- /*
- * There is no such thing as a successful short write. Set
- * it to our originally-requested length.
- */
- obj_request->xferred = obj_request->length;
- obj_request_done_set(obj_request);
+ return obj_req->ex.oe_off + obj_req->ex.oe_len ==
+ rbd_dev->layout.object_size;
}
-static void rbd_osd_discard_callback(struct rbd_obj_request *obj_request)
+static u64 rbd_obj_img_extents_bytes(struct rbd_obj_request *obj_req)
{
- dout("%s: obj %p result %d %llu\n", __func__, obj_request,
- obj_request->result, obj_request->length);
- /*
- * There is no such thing as a successful short discard. Set
- * it to our originally-requested length.
- */
- obj_request->xferred = obj_request->length;
- /* discarding a non-existent object is not a problem */
- if (obj_request->result == -ENOENT)
- obj_request->result = 0;
- obj_request_done_set(obj_request);
+ return ceph_file_extents_bytes(obj_req->img_extents,
+ obj_req->num_img_extents);
}
-/*
- * For a simple stat call there's nothing to do. We'll do more if
- * this is part of a write sequence for a layered image.
- */
-static void rbd_osd_stat_callback(struct rbd_obj_request *obj_request)
+static bool rbd_img_is_write(struct rbd_img_request *img_req)
{
- dout("%s: obj %p\n", __func__, obj_request);
- obj_request_done_set(obj_request);
+ switch (img_req->op_type) {
+ case OBJ_OP_READ:
+ return false;
+ case OBJ_OP_WRITE:
+ case OBJ_OP_DISCARD:
+ return true;
+ default:
+ BUG();
+ }
}
-static void rbd_osd_call_callback(struct rbd_obj_request *obj_request)
-{
- dout("%s: obj %p\n", __func__, obj_request);
-
- if (obj_request_img_data_test(obj_request))
- rbd_osd_copyup_callback(obj_request);
- else
- obj_request_done_set(obj_request);
-}
+static void rbd_obj_handle_request(struct rbd_obj_request *obj_req);
static void rbd_osd_req_callback(struct ceph_osd_request *osd_req)
{
- struct rbd_obj_request *obj_request = osd_req->r_priv;
- u16 opcode;
-
- dout("%s: osd_req %p\n", __func__, osd_req);
- rbd_assert(osd_req == obj_request->osd_req);
- if (obj_request_img_data_test(obj_request)) {
- rbd_assert(obj_request->img_request);
- rbd_assert(obj_request->which != BAD_WHICH);
- } else {
- rbd_assert(obj_request->which == BAD_WHICH);
- }
-
- if (osd_req->r_result < 0)
- obj_request->result = osd_req->r_result;
+ struct rbd_obj_request *obj_req = osd_req->r_priv;
- /*
- * We support a 64-bit length, but ultimately it has to be
- * passed to the block layer, which just supports a 32-bit
- * length field.
- */
- obj_request->xferred = osd_req->r_ops[0].outdata_len;
- rbd_assert(obj_request->xferred < (u64)UINT_MAX);
+ dout("%s osd_req %p result %d for obj_req %p\n", __func__, osd_req,
+ osd_req->r_result, obj_req);
+ rbd_assert(osd_req == obj_req->osd_req);
- opcode = osd_req->r_ops[0].op;
- switch (opcode) {
- case CEPH_OSD_OP_READ:
- rbd_osd_read_callback(obj_request);
- break;
- case CEPH_OSD_OP_SETALLOCHINT:
- rbd_assert(osd_req->r_ops[1].op == CEPH_OSD_OP_WRITE ||
- osd_req->r_ops[1].op == CEPH_OSD_OP_WRITEFULL);
- /* fall through */
- case CEPH_OSD_OP_WRITE:
- case CEPH_OSD_OP_WRITEFULL:
- rbd_osd_write_callback(obj_request);
- break;
- case CEPH_OSD_OP_STAT:
- rbd_osd_stat_callback(obj_request);
- break;
- case CEPH_OSD_OP_DELETE:
- case CEPH_OSD_OP_TRUNCATE:
- case CEPH_OSD_OP_ZERO:
- rbd_osd_discard_callback(obj_request);
- break;
- case CEPH_OSD_OP_CALL:
- rbd_osd_call_callback(obj_request);
- break;
- default:
- rbd_warn(NULL, "unexpected OSD op: object_no %016llx opcode %d",
- obj_request->object_no, opcode);
- break;
- }
+ obj_req->result = osd_req->r_result < 0 ? osd_req->r_result : 0;
+ if (!obj_req->result && !rbd_img_is_write(obj_req->img_request))
+ obj_req->xferred = osd_req->r_result;
+ else
+ /*
+ * Writes aren't allowed to return a data payload. In some
+ * guarded write cases (e.g. stat + zero on an empty object)
+ * a stat response makes it through, but we don't care.
+ */
+ obj_req->xferred = 0;
- if (obj_request_done_test(obj_request))
- rbd_obj_request_complete(obj_request);
+ rbd_obj_handle_request(obj_req);
}
static void rbd_osd_req_format_read(struct rbd_obj_request *obj_request)
{
struct ceph_osd_request *osd_req = obj_request->osd_req;
- rbd_assert(obj_request_img_data_test(obj_request));
+ osd_req->r_flags = CEPH_OSD_FLAG_READ;
osd_req->r_snapid = obj_request->img_request->snap_id;
}
@@ -1896,32 +1451,33 @@ static void rbd_osd_req_format_write(struct rbd_obj_request *obj_request)
{
struct ceph_osd_request *osd_req = obj_request->osd_req;
+ osd_req->r_flags = CEPH_OSD_FLAG_WRITE;
ktime_get_real_ts(&osd_req->r_mtime);
- osd_req->r_data_offset = obj_request->offset;
+ osd_req->r_data_offset = obj_request->ex.oe_off;
}
static struct ceph_osd_request *
-__rbd_osd_req_create(struct rbd_device *rbd_dev,
- struct ceph_snap_context *snapc,
- int num_ops, unsigned int flags,
- struct rbd_obj_request *obj_request)
+rbd_osd_req_create(struct rbd_obj_request *obj_req, unsigned int num_ops)
{
+ struct rbd_img_request *img_req = obj_req->img_request;
+ struct rbd_device *rbd_dev = img_req->rbd_dev;
struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc;
struct ceph_osd_request *req;
const char *name_format = rbd_dev->image_format == 1 ?
RBD_V1_DATA_FORMAT : RBD_V2_DATA_FORMAT;
- req = ceph_osdc_alloc_request(osdc, snapc, num_ops, false, GFP_NOIO);
+ req = ceph_osdc_alloc_request(osdc,
+ (rbd_img_is_write(img_req) ? img_req->snapc : NULL),
+ num_ops, false, GFP_NOIO);
if (!req)
return NULL;
- req->r_flags = flags;
req->r_callback = rbd_osd_req_callback;
- req->r_priv = obj_request;
+ req->r_priv = obj_req;
req->r_base_oloc.pool = rbd_dev->layout.pool_id;
if (ceph_oid_aprintf(&req->r_base_oid, GFP_NOIO, name_format,
- rbd_dev->header.object_prefix, obj_request->object_no))
+ rbd_dev->header.object_prefix, obj_req->ex.oe_objno))
goto err_req;
if (ceph_osdc_alloc_messages(req, GFP_NOIO))
@@ -1934,83 +1490,20 @@ err_req:
return NULL;
}
-/*
- * Create an osd request. A read request has one osd op (read).
- * A write request has either one (watch) or two (hint+write) osd ops.
- * (All rbd data writes are prefixed with an allocation hint op, but
- * technically osd watch is a write request, hence this distinction.)
- */
-static struct ceph_osd_request *rbd_osd_req_create(
- struct rbd_device *rbd_dev,
- enum obj_operation_type op_type,
- unsigned int num_ops,
- struct rbd_obj_request *obj_request)
-{
- struct ceph_snap_context *snapc = NULL;
-
- if (obj_request_img_data_test(obj_request) &&
- (op_type == OBJ_OP_DISCARD || op_type == OBJ_OP_WRITE)) {
- struct rbd_img_request *img_request = obj_request->img_request;
- if (op_type == OBJ_OP_WRITE) {
- rbd_assert(img_request_write_test(img_request));
- } else {
- rbd_assert(img_request_discard_test(img_request));
- }
- snapc = img_request->snapc;
- }
-
- rbd_assert(num_ops == 1 || ((op_type == OBJ_OP_WRITE) && num_ops == 2));
-
- return __rbd_osd_req_create(rbd_dev, snapc, num_ops,
- (op_type == OBJ_OP_WRITE || op_type == OBJ_OP_DISCARD) ?
- CEPH_OSD_FLAG_WRITE : CEPH_OSD_FLAG_READ, obj_request);
-}
-
-/*
- * Create a copyup osd request based on the information in the object
- * request supplied. A copyup request has two or three osd ops, a
- * copyup method call, potentially a hint op, and a write or truncate
- * or zero op.
- */
-static struct ceph_osd_request *
-rbd_osd_req_create_copyup(struct rbd_obj_request *obj_request)
-{
- struct rbd_img_request *img_request;
- int num_osd_ops = 3;
-
- rbd_assert(obj_request_img_data_test(obj_request));
- img_request = obj_request->img_request;
- rbd_assert(img_request);
- rbd_assert(img_request_write_test(img_request) ||
- img_request_discard_test(img_request));
-
- if (img_request_discard_test(img_request))
- num_osd_ops = 2;
-
- return __rbd_osd_req_create(img_request->rbd_dev,
- img_request->snapc, num_osd_ops,
- CEPH_OSD_FLAG_WRITE, obj_request);
-}
-
static void rbd_osd_req_destroy(struct ceph_osd_request *osd_req)
{
ceph_osdc_put_request(osd_req);
}
-static struct rbd_obj_request *
-rbd_obj_request_create(enum obj_request_type type)
+static struct rbd_obj_request *rbd_obj_request_create(void)
{
struct rbd_obj_request *obj_request;
- rbd_assert(obj_request_type_valid(type));
-
obj_request = kmem_cache_zalloc(rbd_obj_request_cache, GFP_NOIO);
if (!obj_request)
return NULL;
- obj_request->which = BAD_WHICH;
- obj_request->type = type;
- INIT_LIST_HEAD(&obj_request->links);
+ ceph_object_extent_init(&obj_request->ex);
kref_init(&obj_request->kref);
dout("%s %p\n", __func__, obj_request);
@@ -2020,32 +1513,34 @@ rbd_obj_request_create(enum obj_request_type type)
static void rbd_obj_request_destroy(struct kref *kref)
{
struct rbd_obj_request *obj_request;
+ u32 i;
obj_request = container_of(kref, struct rbd_obj_request, kref);
dout("%s: obj %p\n", __func__, obj_request);
- rbd_assert(obj_request->img_request == NULL);
- rbd_assert(obj_request->which == BAD_WHICH);
-
if (obj_request->osd_req)
rbd_osd_req_destroy(obj_request->osd_req);
- rbd_assert(obj_request_type_valid(obj_request->type));
- switch (obj_request->type) {
+ switch (obj_request->img_request->data_type) {
case OBJ_REQUEST_NODATA:
- break; /* Nothing to do */
case OBJ_REQUEST_BIO:
- if (obj_request->bio_list)
- bio_chain_put(obj_request->bio_list);
- break;
- case OBJ_REQUEST_PAGES:
- /* img_data requests don't own their page array */
- if (obj_request->pages &&
- !obj_request_img_data_test(obj_request))
- ceph_release_page_vector(obj_request->pages,
- obj_request->page_count);
+ case OBJ_REQUEST_BVECS:
+ break; /* Nothing to do */
+ case OBJ_REQUEST_OWN_BVECS:
+ kfree(obj_request->bvec_pos.bvecs);
break;
+ default:
+ rbd_assert(0);
+ }
+
+ kfree(obj_request->img_extents);
+ if (obj_request->copyup_bvecs) {
+ for (i = 0; i < obj_request->copyup_bvec_count; i++) {
+ if (obj_request->copyup_bvecs[i].bv_page)
+ __free_page(obj_request->copyup_bvecs[i].bv_page);
+ }
+ kfree(obj_request->copyup_bvecs);
}
kmem_cache_free(rbd_obj_request_cache, obj_request);
@@ -2120,7 +1615,6 @@ static bool rbd_dev_parent_get(struct rbd_device *rbd_dev)
*/
static struct rbd_img_request *rbd_img_request_create(
struct rbd_device *rbd_dev,
- u64 offset, u64 length,
enum obj_operation_type op_type,
struct ceph_snap_context *snapc)
{
@@ -2131,27 +1625,21 @@ static struct rbd_img_request *rbd_img_request_create(
return NULL;
img_request->rbd_dev = rbd_dev;
- img_request->offset = offset;
- img_request->length = length;
- if (op_type == OBJ_OP_DISCARD) {
- img_request_discard_set(img_request);
- img_request->snapc = snapc;
- } else if (op_type == OBJ_OP_WRITE) {
- img_request_write_set(img_request);
- img_request->snapc = snapc;
- } else {
+ img_request->op_type = op_type;
+ if (!rbd_img_is_write(img_request))
img_request->snap_id = rbd_dev->spec->snap_id;
- }
+ else
+ img_request->snapc = snapc;
+
if (rbd_dev_parent_get(rbd_dev))
img_request_layered_set(img_request);
spin_lock_init(&img_request->completion_lock);
- INIT_LIST_HEAD(&img_request->obj_requests);
+ INIT_LIST_HEAD(&img_request->object_extents);
kref_init(&img_request->kref);
- dout("%s: rbd_dev %p %s %llu/%llu -> img %p\n", __func__, rbd_dev,
- obj_op_name(op_type), offset, length, img_request);
-
+ dout("%s: rbd_dev %p %s -> img %p\n", __func__, rbd_dev,
+ obj_op_name(op_type), img_request);
return img_request;
}
@@ -2174,829 +1662,934 @@ static void rbd_img_request_destroy(struct kref *kref)
rbd_dev_parent_put(img_request->rbd_dev);
}
- if (img_request_write_test(img_request) ||
- img_request_discard_test(img_request))
+ if (rbd_img_is_write(img_request))
ceph_put_snap_context(img_request->snapc);
kmem_cache_free(rbd_img_request_cache, img_request);
}
-static struct rbd_img_request *rbd_parent_request_create(
- struct rbd_obj_request *obj_request,
- u64 img_offset, u64 length)
+static void prune_extents(struct ceph_file_extent *img_extents,
+ u32 *num_img_extents, u64 overlap)
{
- struct rbd_img_request *parent_request;
- struct rbd_device *rbd_dev;
+ u32 cnt = *num_img_extents;
- rbd_assert(obj_request->img_request);
- rbd_dev = obj_request->img_request->rbd_dev;
+ /* drop extents completely beyond the overlap */
+ while (cnt && img_extents[cnt - 1].fe_off >= overlap)
+ cnt--;
- parent_request = rbd_img_request_create(rbd_dev->parent, img_offset,
- length, OBJ_OP_READ, NULL);
- if (!parent_request)
- return NULL;
+ if (cnt) {
+ struct ceph_file_extent *ex = &img_extents[cnt - 1];
- img_request_child_set(parent_request);
- rbd_obj_request_get(obj_request);
- parent_request->obj_request = obj_request;
+ /* trim final overlapping extent */
+ if (ex->fe_off + ex->fe_len > overlap)
+ ex->fe_len = overlap - ex->fe_off;
+ }
- return parent_request;
+ *num_img_extents = cnt;
}
-static void rbd_parent_request_destroy(struct kref *kref)
+/*
+ * Determine the byte range(s) covered by either just the object extent
+ * or the entire object in the parent image.
+ */
+static int rbd_obj_calc_img_extents(struct rbd_obj_request *obj_req,
+ bool entire)
{
- struct rbd_img_request *parent_request;
- struct rbd_obj_request *orig_request;
+ struct rbd_device *rbd_dev = obj_req->img_request->rbd_dev;
+ int ret;
- parent_request = container_of(kref, struct rbd_img_request, kref);
- orig_request = parent_request->obj_request;
+ if (!rbd_dev->parent_overlap)
+ return 0;
- parent_request->obj_request = NULL;
- rbd_obj_request_put(orig_request);
- img_request_child_clear(parent_request);
+ ret = ceph_extent_to_file(&rbd_dev->layout, obj_req->ex.oe_objno,
+ entire ? 0 : obj_req->ex.oe_off,
+ entire ? rbd_dev->layout.object_size :
+ obj_req->ex.oe_len,
+ &obj_req->img_extents,
+ &obj_req->num_img_extents);
+ if (ret)
+ return ret;
- rbd_img_request_destroy(kref);
+ prune_extents(obj_req->img_extents, &obj_req->num_img_extents,
+ rbd_dev->parent_overlap);
+ return 0;
}
-static bool rbd_img_obj_end_request(struct rbd_obj_request *obj_request)
+static void rbd_osd_req_setup_data(struct rbd_obj_request *obj_req, u32 which)
{
- struct rbd_img_request *img_request;
- unsigned int xferred;
- int result;
- bool more;
-
- rbd_assert(obj_request_img_data_test(obj_request));
- img_request = obj_request->img_request;
-
- rbd_assert(obj_request->xferred <= (u64)UINT_MAX);
- xferred = (unsigned int)obj_request->xferred;
- result = obj_request->result;
- if (result) {
- struct rbd_device *rbd_dev = img_request->rbd_dev;
- enum obj_operation_type op_type;
-
- if (img_request_discard_test(img_request))
- op_type = OBJ_OP_DISCARD;
- else if (img_request_write_test(img_request))
- op_type = OBJ_OP_WRITE;
- else
- op_type = OBJ_OP_READ;
-
- rbd_warn(rbd_dev, "%s %llx at %llx (%llx)",
- obj_op_name(op_type), obj_request->length,
- obj_request->img_offset, obj_request->offset);
- rbd_warn(rbd_dev, " result %d xferred %x",
- result, xferred);
- if (!img_request->result)
- img_request->result = result;
- /*
- * Need to end I/O on the entire obj_request worth of
- * bytes in case of error.
- */
- xferred = obj_request->length;
+ switch (obj_req->img_request->data_type) {
+ case OBJ_REQUEST_BIO:
+ osd_req_op_extent_osd_data_bio(obj_req->osd_req, which,
+ &obj_req->bio_pos,
+ obj_req->ex.oe_len);
+ break;
+ case OBJ_REQUEST_BVECS:
+ case OBJ_REQUEST_OWN_BVECS:
+ rbd_assert(obj_req->bvec_pos.iter.bi_size ==
+ obj_req->ex.oe_len);
+ rbd_assert(obj_req->bvec_idx == obj_req->bvec_count);
+ osd_req_op_extent_osd_data_bvec_pos(obj_req->osd_req, which,
+ &obj_req->bvec_pos);
+ break;
+ default:
+ rbd_assert(0);
}
+}
- if (img_request_child_test(img_request)) {
- rbd_assert(img_request->obj_request != NULL);
- more = obj_request->which < img_request->obj_request_count - 1;
- } else {
- blk_status_t status = errno_to_blk_status(result);
+static int rbd_obj_setup_read(struct rbd_obj_request *obj_req)
+{
+ obj_req->osd_req = rbd_osd_req_create(obj_req, 1);
+ if (!obj_req->osd_req)
+ return -ENOMEM;
- rbd_assert(img_request->rq != NULL);
+ osd_req_op_extent_init(obj_req->osd_req, 0, CEPH_OSD_OP_READ,
+ obj_req->ex.oe_off, obj_req->ex.oe_len, 0, 0);
+ rbd_osd_req_setup_data(obj_req, 0);
- more = blk_update_request(img_request->rq, status, xferred);
- if (!more)
- __blk_mq_end_request(img_request->rq, status);
- }
+ rbd_osd_req_format_read(obj_req);
+ return 0;
+}
+
+static int __rbd_obj_setup_stat(struct rbd_obj_request *obj_req,
+ unsigned int which)
+{
+ struct page **pages;
- return more;
+ /*
+ * The response data for a STAT call consists of:
+ * le64 length;
+ * struct {
+ * le32 tv_sec;
+ * le32 tv_nsec;
+ * } mtime;
+ */
+ pages = ceph_alloc_page_vector(1, GFP_NOIO);
+ if (IS_ERR(pages))
+ return PTR_ERR(pages);
+
+ osd_req_op_init(obj_req->osd_req, which, CEPH_OSD_OP_STAT, 0);
+ osd_req_op_raw_data_in_pages(obj_req->osd_req, which, pages,
+ 8 + sizeof(struct ceph_timespec),
+ 0, false, true);
+ return 0;
}
-static void rbd_img_obj_callback(struct rbd_obj_request *obj_request)
+static void __rbd_obj_setup_write(struct rbd_obj_request *obj_req,
+ unsigned int which)
{
- struct rbd_img_request *img_request;
- u32 which = obj_request->which;
- bool more = true;
+ struct rbd_device *rbd_dev = obj_req->img_request->rbd_dev;
+ u16 opcode;
- rbd_assert(obj_request_img_data_test(obj_request));
- img_request = obj_request->img_request;
+ osd_req_op_alloc_hint_init(obj_req->osd_req, which++,
+ rbd_dev->layout.object_size,
+ rbd_dev->layout.object_size);
- dout("%s: img %p obj %p\n", __func__, img_request, obj_request);
- rbd_assert(img_request != NULL);
- rbd_assert(img_request->obj_request_count > 0);
- rbd_assert(which != BAD_WHICH);
- rbd_assert(which < img_request->obj_request_count);
+ if (rbd_obj_is_entire(obj_req))
+ opcode = CEPH_OSD_OP_WRITEFULL;
+ else
+ opcode = CEPH_OSD_OP_WRITE;
- spin_lock_irq(&img_request->completion_lock);
- if (which != img_request->next_completion)
- goto out;
+ osd_req_op_extent_init(obj_req->osd_req, which, opcode,
+ obj_req->ex.oe_off, obj_req->ex.oe_len, 0, 0);
+ rbd_osd_req_setup_data(obj_req, which++);
- for_each_obj_request_from(img_request, obj_request) {
- rbd_assert(more);
- rbd_assert(which < img_request->obj_request_count);
+ rbd_assert(which == obj_req->osd_req->r_num_ops);
+ rbd_osd_req_format_write(obj_req);
+}
- if (!obj_request_done_test(obj_request))
- break;
- more = rbd_img_obj_end_request(obj_request);
- which++;
+static int rbd_obj_setup_write(struct rbd_obj_request *obj_req)
+{
+ unsigned int num_osd_ops, which = 0;
+ int ret;
+
+ /* reverse map the entire object onto the parent */
+ ret = rbd_obj_calc_img_extents(obj_req, true);
+ if (ret)
+ return ret;
+
+ if (obj_req->num_img_extents) {
+ obj_req->write_state = RBD_OBJ_WRITE_GUARD;
+ num_osd_ops = 3; /* stat + setallochint + write/writefull */
+ } else {
+ obj_req->write_state = RBD_OBJ_WRITE_FLAT;
+ num_osd_ops = 2; /* setallochint + write/writefull */
}
- rbd_assert(more ^ (which == img_request->obj_request_count));
- img_request->next_completion = which;
-out:
- spin_unlock_irq(&img_request->completion_lock);
- rbd_img_request_put(img_request);
+ obj_req->osd_req = rbd_osd_req_create(obj_req, num_osd_ops);
+ if (!obj_req->osd_req)
+ return -ENOMEM;
- if (!more)
- rbd_img_request_complete(img_request);
+ if (obj_req->num_img_extents) {
+ ret = __rbd_obj_setup_stat(obj_req, which++);
+ if (ret)
+ return ret;
+ }
+
+ __rbd_obj_setup_write(obj_req, which);
+ return 0;
}
-/*
- * Add individual osd ops to the given ceph_osd_request and prepare
- * them for submission. num_ops is the current number of
- * osd operations already to the object request.
- */
-static void rbd_img_obj_request_fill(struct rbd_obj_request *obj_request,
- struct ceph_osd_request *osd_request,
- enum obj_operation_type op_type,
- unsigned int num_ops)
-{
- struct rbd_img_request *img_request = obj_request->img_request;
- struct rbd_device *rbd_dev = img_request->rbd_dev;
- u64 object_size = rbd_obj_bytes(&rbd_dev->header);
- u64 offset = obj_request->offset;
- u64 length = obj_request->length;
- u64 img_end;
+static void __rbd_obj_setup_discard(struct rbd_obj_request *obj_req,
+ unsigned int which)
+{
u16 opcode;
- if (op_type == OBJ_OP_DISCARD) {
- if (!offset && length == object_size &&
- (!img_request_layered_test(img_request) ||
- !obj_request_overlaps_parent(obj_request))) {
- opcode = CEPH_OSD_OP_DELETE;
- } else if ((offset + length == object_size)) {
+ if (rbd_obj_is_entire(obj_req)) {
+ if (obj_req->num_img_extents) {
+ osd_req_op_init(obj_req->osd_req, which++,
+ CEPH_OSD_OP_CREATE, 0);
opcode = CEPH_OSD_OP_TRUNCATE;
} else {
- down_read(&rbd_dev->header_rwsem);
- img_end = rbd_dev->header.image_size;
- up_read(&rbd_dev->header_rwsem);
-
- if (obj_request->img_offset + length == img_end)
- opcode = CEPH_OSD_OP_TRUNCATE;
- else
- opcode = CEPH_OSD_OP_ZERO;
+ osd_req_op_init(obj_req->osd_req, which++,
+ CEPH_OSD_OP_DELETE, 0);
+ opcode = 0;
}
- } else if (op_type == OBJ_OP_WRITE) {
- if (!offset && length == object_size)
- opcode = CEPH_OSD_OP_WRITEFULL;
- else
- opcode = CEPH_OSD_OP_WRITE;
- osd_req_op_alloc_hint_init(osd_request, num_ops,
- object_size, object_size);
- num_ops++;
+ } else if (rbd_obj_is_tail(obj_req)) {
+ opcode = CEPH_OSD_OP_TRUNCATE;
} else {
- opcode = CEPH_OSD_OP_READ;
+ opcode = CEPH_OSD_OP_ZERO;
}
- if (opcode == CEPH_OSD_OP_DELETE)
- osd_req_op_init(osd_request, num_ops, opcode, 0);
- else
- osd_req_op_extent_init(osd_request, num_ops, opcode,
- offset, length, 0, 0);
-
- if (obj_request->type == OBJ_REQUEST_BIO)
- osd_req_op_extent_osd_data_bio(osd_request, num_ops,
- obj_request->bio_list, length);
- else if (obj_request->type == OBJ_REQUEST_PAGES)
- osd_req_op_extent_osd_data_pages(osd_request, num_ops,
- obj_request->pages, length,
- offset & ~PAGE_MASK, false, false);
-
- /* Discards are also writes */
- if (op_type == OBJ_OP_WRITE || op_type == OBJ_OP_DISCARD)
- rbd_osd_req_format_write(obj_request);
- else
- rbd_osd_req_format_read(obj_request);
+ if (opcode)
+ osd_req_op_extent_init(obj_req->osd_req, which++, opcode,
+ obj_req->ex.oe_off, obj_req->ex.oe_len,
+ 0, 0);
+
+ rbd_assert(which == obj_req->osd_req->r_num_ops);
+ rbd_osd_req_format_write(obj_req);
}
-/*
- * Split up an image request into one or more object requests, each
- * to a different object. The "type" parameter indicates whether
- * "data_desc" is the pointer to the head of a list of bio
- * structures, or the base of a page array. In either case this
- * function assumes data_desc describes memory sufficient to hold
- * all data described by the image request.
- */
-static int rbd_img_request_fill(struct rbd_img_request *img_request,
- enum obj_request_type type,
- void *data_desc)
+static int rbd_obj_setup_discard(struct rbd_obj_request *obj_req)
{
- struct rbd_device *rbd_dev = img_request->rbd_dev;
- struct rbd_obj_request *obj_request = NULL;
- struct rbd_obj_request *next_obj_request;
- struct bio *bio_list = NULL;
- unsigned int bio_offset = 0;
- struct page **pages = NULL;
- enum obj_operation_type op_type;
- u64 img_offset;
- u64 resid;
-
- dout("%s: img %p type %d data_desc %p\n", __func__, img_request,
- (int)type, data_desc);
+ unsigned int num_osd_ops, which = 0;
+ int ret;
- img_offset = img_request->offset;
- resid = img_request->length;
- rbd_assert(resid > 0);
- op_type = rbd_img_request_op_type(img_request);
+ /* reverse map the entire object onto the parent */
+ ret = rbd_obj_calc_img_extents(obj_req, true);
+ if (ret)
+ return ret;
- if (type == OBJ_REQUEST_BIO) {
- bio_list = data_desc;
- rbd_assert(img_offset ==
- bio_list->bi_iter.bi_sector << SECTOR_SHIFT);
- } else if (type == OBJ_REQUEST_PAGES) {
- pages = data_desc;
+ if (rbd_obj_is_entire(obj_req)) {
+ obj_req->write_state = RBD_OBJ_WRITE_FLAT;
+ if (obj_req->num_img_extents)
+ num_osd_ops = 2; /* create + truncate */
+ else
+ num_osd_ops = 1; /* delete */
+ } else {
+ if (obj_req->num_img_extents) {
+ obj_req->write_state = RBD_OBJ_WRITE_GUARD;
+ num_osd_ops = 2; /* stat + truncate/zero */
+ } else {
+ obj_req->write_state = RBD_OBJ_WRITE_FLAT;
+ num_osd_ops = 1; /* truncate/zero */
+ }
}
- while (resid) {
- struct ceph_osd_request *osd_req;
- u64 object_no = img_offset >> rbd_dev->header.obj_order;
- u64 offset = rbd_segment_offset(rbd_dev, img_offset);
- u64 length = rbd_segment_length(rbd_dev, img_offset, resid);
-
- obj_request = rbd_obj_request_create(type);
- if (!obj_request)
- goto out_unwind;
-
- obj_request->object_no = object_no;
- obj_request->offset = offset;
- obj_request->length = length;
-
- /*
- * set obj_request->img_request before creating the
- * osd_request so that it gets the right snapc
- */
- rbd_img_obj_request_add(img_request, obj_request);
-
- if (type == OBJ_REQUEST_BIO) {
- unsigned int clone_size;
-
- rbd_assert(length <= (u64)UINT_MAX);
- clone_size = (unsigned int)length;
- obj_request->bio_list =
- bio_chain_clone_range(&bio_list,
- &bio_offset,
- clone_size,
- GFP_NOIO);
- if (!obj_request->bio_list)
- goto out_unwind;
- } else if (type == OBJ_REQUEST_PAGES) {
- unsigned int page_count;
-
- obj_request->pages = pages;
- page_count = (u32)calc_pages_for(offset, length);
- obj_request->page_count = page_count;
- if ((offset + length) & ~PAGE_MASK)
- page_count--; /* more on last page */
- pages += page_count;
- }
+ obj_req->osd_req = rbd_osd_req_create(obj_req, num_osd_ops);
+ if (!obj_req->osd_req)
+ return -ENOMEM;
- osd_req = rbd_osd_req_create(rbd_dev, op_type,
- (op_type == OBJ_OP_WRITE) ? 2 : 1,
- obj_request);
- if (!osd_req)
- goto out_unwind;
+ if (!rbd_obj_is_entire(obj_req) && obj_req->num_img_extents) {
+ ret = __rbd_obj_setup_stat(obj_req, which++);
+ if (ret)
+ return ret;
+ }
- obj_request->osd_req = osd_req;
- obj_request->callback = rbd_img_obj_callback;
- obj_request->img_offset = img_offset;
+ __rbd_obj_setup_discard(obj_req, which);
+ return 0;
+}
- rbd_img_obj_request_fill(obj_request, osd_req, op_type, 0);
+/*
+ * For each object request in @img_req, allocate an OSD request, add
+ * individual OSD ops and prepare them for submission. The number of
+ * OSD ops depends on op_type and the overlap point (if any).
+ */
+static int __rbd_img_fill_request(struct rbd_img_request *img_req)
+{
+ struct rbd_obj_request *obj_req;
+ int ret;
- img_offset += length;
- resid -= length;
+ for_each_obj_request(img_req, obj_req) {
+ switch (img_req->op_type) {
+ case OBJ_OP_READ:
+ ret = rbd_obj_setup_read(obj_req);
+ break;
+ case OBJ_OP_WRITE:
+ ret = rbd_obj_setup_write(obj_req);
+ break;
+ case OBJ_OP_DISCARD:
+ ret = rbd_obj_setup_discard(obj_req);
+ break;
+ default:
+ rbd_assert(0);
+ }
+ if (ret)
+ return ret;
}
return 0;
+}
-out_unwind:
- for_each_obj_request_safe(img_request, obj_request, next_obj_request)
- rbd_img_obj_request_del(img_request, obj_request);
+union rbd_img_fill_iter {
+ struct ceph_bio_iter bio_iter;
+ struct ceph_bvec_iter bvec_iter;
+};
- return -ENOMEM;
-}
+struct rbd_img_fill_ctx {
+ enum obj_request_type pos_type;
+ union rbd_img_fill_iter *pos;
+ union rbd_img_fill_iter iter;
+ ceph_object_extent_fn_t set_pos_fn;
+ ceph_object_extent_fn_t count_fn;
+ ceph_object_extent_fn_t copy_fn;
+};
-static void
-rbd_osd_copyup_callback(struct rbd_obj_request *obj_request)
+static struct ceph_object_extent *alloc_object_extent(void *arg)
{
- struct rbd_img_request *img_request;
- struct rbd_device *rbd_dev;
- struct page **pages;
- u32 page_count;
+ struct rbd_img_request *img_req = arg;
+ struct rbd_obj_request *obj_req;
- dout("%s: obj %p\n", __func__, obj_request);
+ obj_req = rbd_obj_request_create();
+ if (!obj_req)
+ return NULL;
- rbd_assert(obj_request->type == OBJ_REQUEST_BIO ||
- obj_request->type == OBJ_REQUEST_NODATA);
- rbd_assert(obj_request_img_data_test(obj_request));
- img_request = obj_request->img_request;
- rbd_assert(img_request);
+ rbd_img_obj_request_add(img_req, obj_req);
+ return &obj_req->ex;
+}
- rbd_dev = img_request->rbd_dev;
- rbd_assert(rbd_dev);
+/*
+ * While su != os && sc == 1 is technically not fancy (it's the same
+ * layout as su == os && sc == 1), we can't use the nocopy path for it
+ * because ->set_pos_fn() should be called only once per object.
+ * ceph_file_to_extents() invokes action_fn once per stripe unit, so
+ * treat su != os && sc == 1 as fancy.
+ */
+static bool rbd_layout_is_fancy(struct ceph_file_layout *l)
+{
+ return l->stripe_unit != l->object_size;
+}
+
+static int rbd_img_fill_request_nocopy(struct rbd_img_request *img_req,
+ struct ceph_file_extent *img_extents,
+ u32 num_img_extents,
+ struct rbd_img_fill_ctx *fctx)
+{
+ u32 i;
+ int ret;
- pages = obj_request->copyup_pages;
- rbd_assert(pages != NULL);
- obj_request->copyup_pages = NULL;
- page_count = obj_request->copyup_page_count;
- rbd_assert(page_count);
- obj_request->copyup_page_count = 0;
- ceph_release_page_vector(pages, page_count);
+ img_req->data_type = fctx->pos_type;
/*
- * We want the transfer count to reflect the size of the
- * original write request. There is no such thing as a
- * successful short write, so if the request was successful
- * we can just set it to the originally-requested length.
+ * Create object requests and set each object request's starting
+ * position in the provided bio (list) or bio_vec array.
*/
- if (!obj_request->result)
- obj_request->xferred = obj_request->length;
+ fctx->iter = *fctx->pos;
+ for (i = 0; i < num_img_extents; i++) {
+ ret = ceph_file_to_extents(&img_req->rbd_dev->layout,
+ img_extents[i].fe_off,
+ img_extents[i].fe_len,
+ &img_req->object_extents,
+ alloc_object_extent, img_req,
+ fctx->set_pos_fn, &fctx->iter);
+ if (ret)
+ return ret;
+ }
- obj_request_done_set(obj_request);
+ return __rbd_img_fill_request(img_req);
}
-static void
-rbd_img_obj_parent_read_full_callback(struct rbd_img_request *img_request)
+/*
+ * Map a list of image extents to a list of object extents, create the
+ * corresponding object requests (normally each to a different object,
+ * but not always) and add them to @img_req. For each object request,
+ * set up its data descriptor to point to the corresponding chunk(s) of
+ * @fctx->pos data buffer.
+ *
+ * Because ceph_file_to_extents() will merge adjacent object extents
+ * together, each object request's data descriptor may point to multiple
+ * different chunks of @fctx->pos data buffer.
+ *
+ * @fctx->pos data buffer is assumed to be large enough.
+ */
+static int rbd_img_fill_request(struct rbd_img_request *img_req,
+ struct ceph_file_extent *img_extents,
+ u32 num_img_extents,
+ struct rbd_img_fill_ctx *fctx)
{
- struct rbd_obj_request *orig_request;
- struct ceph_osd_request *osd_req;
- struct rbd_device *rbd_dev;
- struct page **pages;
- enum obj_operation_type op_type;
- u32 page_count;
- int img_result;
- u64 parent_length;
-
- rbd_assert(img_request_child_test(img_request));
-
- /* First get what we need from the image request */
-
- pages = img_request->copyup_pages;
- rbd_assert(pages != NULL);
- img_request->copyup_pages = NULL;
- page_count = img_request->copyup_page_count;
- rbd_assert(page_count);
- img_request->copyup_page_count = 0;
-
- orig_request = img_request->obj_request;
- rbd_assert(orig_request != NULL);
- rbd_assert(obj_request_type_valid(orig_request->type));
- img_result = img_request->result;
- parent_length = img_request->length;
- rbd_assert(img_result || parent_length == img_request->xferred);
- rbd_img_request_put(img_request);
+ struct rbd_device *rbd_dev = img_req->rbd_dev;
+ struct rbd_obj_request *obj_req;
+ u32 i;
+ int ret;
- rbd_assert(orig_request->img_request);
- rbd_dev = orig_request->img_request->rbd_dev;
- rbd_assert(rbd_dev);
+ if (fctx->pos_type == OBJ_REQUEST_NODATA ||
+ !rbd_layout_is_fancy(&rbd_dev->layout))
+ return rbd_img_fill_request_nocopy(img_req, img_extents,
+ num_img_extents, fctx);
+
+ img_req->data_type = OBJ_REQUEST_OWN_BVECS;
/*
- * If the overlap has become 0 (most likely because the
- * image has been flattened) we need to free the pages
- * and re-submit the original write request.
+ * Create object requests and determine ->bvec_count for each object
+ * request. Note that ->bvec_count sum over all object requests may
+ * be greater than the number of bio_vecs in the provided bio (list)
+ * or bio_vec array because when mapped, those bio_vecs can straddle
+ * stripe unit boundaries.
*/
- if (!rbd_dev->parent_overlap) {
- ceph_release_page_vector(pages, page_count);
- rbd_obj_request_submit(orig_request);
- return;
+ fctx->iter = *fctx->pos;
+ for (i = 0; i < num_img_extents; i++) {
+ ret = ceph_file_to_extents(&rbd_dev->layout,
+ img_extents[i].fe_off,
+ img_extents[i].fe_len,
+ &img_req->object_extents,
+ alloc_object_extent, img_req,
+ fctx->count_fn, &fctx->iter);
+ if (ret)
+ return ret;
}
- if (img_result)
- goto out_err;
+ for_each_obj_request(img_req, obj_req) {
+ obj_req->bvec_pos.bvecs = kmalloc_array(obj_req->bvec_count,
+ sizeof(*obj_req->bvec_pos.bvecs),
+ GFP_NOIO);
+ if (!obj_req->bvec_pos.bvecs)
+ return -ENOMEM;
+ }
/*
- * The original osd request is of no use to use any more.
- * We need a new one that can hold the three ops in a copyup
- * request. Allocate the new copyup osd request for the
- * original request, and release the old one.
+ * Fill in each object request's private bio_vec array, splitting and
+ * rearranging the provided bio_vecs in stripe unit chunks as needed.
*/
- img_result = -ENOMEM;
- osd_req = rbd_osd_req_create_copyup(orig_request);
- if (!osd_req)
- goto out_err;
- rbd_osd_req_destroy(orig_request->osd_req);
- orig_request->osd_req = osd_req;
- orig_request->copyup_pages = pages;
- orig_request->copyup_page_count = page_count;
+ fctx->iter = *fctx->pos;
+ for (i = 0; i < num_img_extents; i++) {
+ ret = ceph_iterate_extents(&rbd_dev->layout,
+ img_extents[i].fe_off,
+ img_extents[i].fe_len,
+ &img_req->object_extents,
+ fctx->copy_fn, &fctx->iter);
+ if (ret)
+ return ret;
+ }
- /* Initialize the copyup op */
+ return __rbd_img_fill_request(img_req);
+}
- osd_req_op_cls_init(osd_req, 0, CEPH_OSD_OP_CALL, "rbd", "copyup");
- osd_req_op_cls_request_data_pages(osd_req, 0, pages, parent_length, 0,
- false, false);
+static int rbd_img_fill_nodata(struct rbd_img_request *img_req,
+ u64 off, u64 len)
+{
+ struct ceph_file_extent ex = { off, len };
+ union rbd_img_fill_iter dummy;
+ struct rbd_img_fill_ctx fctx = {
+ .pos_type = OBJ_REQUEST_NODATA,
+ .pos = &dummy,
+ };
- /* Add the other op(s) */
+ return rbd_img_fill_request(img_req, &ex, 1, &fctx);
+}
- op_type = rbd_img_request_op_type(orig_request->img_request);
- rbd_img_obj_request_fill(orig_request, osd_req, op_type, 1);
+static void set_bio_pos(struct ceph_object_extent *ex, u32 bytes, void *arg)
+{
+ struct rbd_obj_request *obj_req =
+ container_of(ex, struct rbd_obj_request, ex);
+ struct ceph_bio_iter *it = arg;
- /* All set, send it off. */
+ dout("%s objno %llu bytes %u\n", __func__, ex->oe_objno, bytes);
+ obj_req->bio_pos = *it;
+ ceph_bio_iter_advance(it, bytes);
+}
- rbd_obj_request_submit(orig_request);
- return;
+static void count_bio_bvecs(struct ceph_object_extent *ex, u32 bytes, void *arg)
+{
+ struct rbd_obj_request *obj_req =
+ container_of(ex, struct rbd_obj_request, ex);
+ struct ceph_bio_iter *it = arg;
+
+ dout("%s objno %llu bytes %u\n", __func__, ex->oe_objno, bytes);
+ ceph_bio_iter_advance_step(it, bytes, ({
+ obj_req->bvec_count++;
+ }));
-out_err:
- ceph_release_page_vector(pages, page_count);
- rbd_obj_request_error(orig_request, img_result);
}
-/*
- * Read from the parent image the range of data that covers the
- * entire target of the given object request. This is used for
- * satisfying a layered image write request when the target of an
- * object request from the image request does not exist.
- *
- * A page array big enough to hold the returned data is allocated
- * and supplied to rbd_img_request_fill() as the "data descriptor."
- * When the read completes, this page array will be transferred to
- * the original object request for the copyup operation.
- *
- * If an error occurs, it is recorded as the result of the original
- * object request in rbd_img_obj_exists_callback().
- */
-static int rbd_img_obj_parent_read_full(struct rbd_obj_request *obj_request)
-{
- struct rbd_device *rbd_dev = obj_request->img_request->rbd_dev;
- struct rbd_img_request *parent_request = NULL;
- u64 img_offset;
- u64 length;
- struct page **pages = NULL;
- u32 page_count;
- int result;
+static void copy_bio_bvecs(struct ceph_object_extent *ex, u32 bytes, void *arg)
+{
+ struct rbd_obj_request *obj_req =
+ container_of(ex, struct rbd_obj_request, ex);
+ struct ceph_bio_iter *it = arg;
- rbd_assert(rbd_dev->parent != NULL);
+ dout("%s objno %llu bytes %u\n", __func__, ex->oe_objno, bytes);
+ ceph_bio_iter_advance_step(it, bytes, ({
+ obj_req->bvec_pos.bvecs[obj_req->bvec_idx++] = bv;
+ obj_req->bvec_pos.iter.bi_size += bv.bv_len;
+ }));
+}
- /*
- * Determine the byte range covered by the object in the
- * child image to which the original request was to be sent.
- */
- img_offset = obj_request->img_offset - obj_request->offset;
- length = rbd_obj_bytes(&rbd_dev->header);
+static int __rbd_img_fill_from_bio(struct rbd_img_request *img_req,
+ struct ceph_file_extent *img_extents,
+ u32 num_img_extents,
+ struct ceph_bio_iter *bio_pos)
+{
+ struct rbd_img_fill_ctx fctx = {
+ .pos_type = OBJ_REQUEST_BIO,
+ .pos = (union rbd_img_fill_iter *)bio_pos,
+ .set_pos_fn = set_bio_pos,
+ .count_fn = count_bio_bvecs,
+ .copy_fn = copy_bio_bvecs,
+ };
- /*
- * There is no defined parent data beyond the parent
- * overlap, so limit what we read at that boundary if
- * necessary.
- */
- if (img_offset + length > rbd_dev->parent_overlap) {
- rbd_assert(img_offset < rbd_dev->parent_overlap);
- length = rbd_dev->parent_overlap - img_offset;
- }
+ return rbd_img_fill_request(img_req, img_extents, num_img_extents,
+ &fctx);
+}
- /*
- * Allocate a page array big enough to receive the data read
- * from the parent.
- */
- page_count = (u32)calc_pages_for(0, length);
- pages = ceph_alloc_page_vector(page_count, GFP_NOIO);
- if (IS_ERR(pages)) {
- result = PTR_ERR(pages);
- pages = NULL;
- goto out_err;
- }
+static int rbd_img_fill_from_bio(struct rbd_img_request *img_req,
+ u64 off, u64 len, struct bio *bio)
+{
+ struct ceph_file_extent ex = { off, len };
+ struct ceph_bio_iter it = { .bio = bio, .iter = bio->bi_iter };
- result = -ENOMEM;
- parent_request = rbd_parent_request_create(obj_request,
- img_offset, length);
- if (!parent_request)
- goto out_err;
+ return __rbd_img_fill_from_bio(img_req, &ex, 1, &it);
+}
- result = rbd_img_request_fill(parent_request, OBJ_REQUEST_PAGES, pages);
- if (result)
- goto out_err;
+static void set_bvec_pos(struct ceph_object_extent *ex, u32 bytes, void *arg)
+{
+ struct rbd_obj_request *obj_req =
+ container_of(ex, struct rbd_obj_request, ex);
+ struct ceph_bvec_iter *it = arg;
- parent_request->copyup_pages = pages;
- parent_request->copyup_page_count = page_count;
- parent_request->callback = rbd_img_obj_parent_read_full_callback;
+ obj_req->bvec_pos = *it;
+ ceph_bvec_iter_shorten(&obj_req->bvec_pos, bytes);
+ ceph_bvec_iter_advance(it, bytes);
+}
- result = rbd_img_request_submit(parent_request);
- if (!result)
- return 0;
+static void count_bvecs(struct ceph_object_extent *ex, u32 bytes, void *arg)
+{
+ struct rbd_obj_request *obj_req =
+ container_of(ex, struct rbd_obj_request, ex);
+ struct ceph_bvec_iter *it = arg;
- parent_request->copyup_pages = NULL;
- parent_request->copyup_page_count = 0;
-out_err:
- if (pages)
- ceph_release_page_vector(pages, page_count);
- if (parent_request)
- rbd_img_request_put(parent_request);
- return result;
+ ceph_bvec_iter_advance_step(it, bytes, ({
+ obj_req->bvec_count++;
+ }));
}
-static void rbd_img_obj_exists_callback(struct rbd_obj_request *obj_request)
+static void copy_bvecs(struct ceph_object_extent *ex, u32 bytes, void *arg)
{
- struct rbd_obj_request *orig_request;
- struct rbd_device *rbd_dev;
- int result;
+ struct rbd_obj_request *obj_req =
+ container_of(ex, struct rbd_obj_request, ex);
+ struct ceph_bvec_iter *it = arg;
- rbd_assert(!obj_request_img_data_test(obj_request));
+ ceph_bvec_iter_advance_step(it, bytes, ({
+ obj_req->bvec_pos.bvecs[obj_req->bvec_idx++] = bv;
+ obj_req->bvec_pos.iter.bi_size += bv.bv_len;
+ }));
+}
- /*
- * All we need from the object request is the original
- * request and the result of the STAT op. Grab those, then
- * we're done with the request.
- */
- orig_request = obj_request->obj_request;
- obj_request->obj_request = NULL;
- rbd_obj_request_put(orig_request);
- rbd_assert(orig_request);
- rbd_assert(orig_request->img_request);
-
- result = obj_request->result;
- obj_request->result = 0;
-
- dout("%s: obj %p for obj %p result %d %llu/%llu\n", __func__,
- obj_request, orig_request, result,
- obj_request->xferred, obj_request->length);
- rbd_obj_request_put(obj_request);
+static int __rbd_img_fill_from_bvecs(struct rbd_img_request *img_req,
+ struct ceph_file_extent *img_extents,
+ u32 num_img_extents,
+ struct ceph_bvec_iter *bvec_pos)
+{
+ struct rbd_img_fill_ctx fctx = {
+ .pos_type = OBJ_REQUEST_BVECS,
+ .pos = (union rbd_img_fill_iter *)bvec_pos,
+ .set_pos_fn = set_bvec_pos,
+ .count_fn = count_bvecs,
+ .copy_fn = copy_bvecs,
+ };
- /*
- * If the overlap has become 0 (most likely because the
- * image has been flattened) we need to re-submit the
- * original request.
- */
- rbd_dev = orig_request->img_request->rbd_dev;
- if (!rbd_dev->parent_overlap) {
- rbd_obj_request_submit(orig_request);
- return;
- }
+ return rbd_img_fill_request(img_req, img_extents, num_img_extents,
+ &fctx);
+}
- /*
- * Our only purpose here is to determine whether the object
- * exists, and we don't want to treat the non-existence as
- * an error. If something else comes back, transfer the
- * error to the original request and complete it now.
- */
- if (!result) {
- obj_request_existence_set(orig_request, true);
- } else if (result == -ENOENT) {
- obj_request_existence_set(orig_request, false);
- } else {
- goto fail_orig_request;
- }
+static int rbd_img_fill_from_bvecs(struct rbd_img_request *img_req,
+ struct ceph_file_extent *img_extents,
+ u32 num_img_extents,
+ struct bio_vec *bvecs)
+{
+ struct ceph_bvec_iter it = {
+ .bvecs = bvecs,
+ .iter = { .bi_size = ceph_file_extents_bytes(img_extents,
+ num_img_extents) },
+ };
- /*
- * Resubmit the original request now that we have recorded
- * whether the target object exists.
- */
- result = rbd_img_obj_request_submit(orig_request);
- if (result)
- goto fail_orig_request;
+ return __rbd_img_fill_from_bvecs(img_req, img_extents, num_img_extents,
+ &it);
+}
- return;
+static void rbd_img_request_submit(struct rbd_img_request *img_request)
+{
+ struct rbd_obj_request *obj_request;
+
+ dout("%s: img %p\n", __func__, img_request);
+
+ rbd_img_request_get(img_request);
+ for_each_obj_request(img_request, obj_request)
+ rbd_obj_request_submit(obj_request);
-fail_orig_request:
- rbd_obj_request_error(orig_request, result);
+ rbd_img_request_put(img_request);
}
-static int rbd_img_obj_exists_submit(struct rbd_obj_request *obj_request)
+static int rbd_obj_read_from_parent(struct rbd_obj_request *obj_req)
{
- struct rbd_device *rbd_dev = obj_request->img_request->rbd_dev;
- struct rbd_obj_request *stat_request;
- struct page **pages;
- u32 page_count;
- size_t size;
+ struct rbd_img_request *img_req = obj_req->img_request;
+ struct rbd_img_request *child_img_req;
int ret;
- stat_request = rbd_obj_request_create(OBJ_REQUEST_PAGES);
- if (!stat_request)
+ child_img_req = rbd_img_request_create(img_req->rbd_dev->parent,
+ OBJ_OP_READ, NULL);
+ if (!child_img_req)
return -ENOMEM;
- stat_request->object_no = obj_request->object_no;
+ __set_bit(IMG_REQ_CHILD, &child_img_req->flags);
+ child_img_req->obj_request = obj_req;
- stat_request->osd_req = rbd_osd_req_create(rbd_dev, OBJ_OP_READ, 1,
- stat_request);
- if (!stat_request->osd_req) {
- ret = -ENOMEM;
- goto fail_stat_request;
+ if (!rbd_img_is_write(img_req)) {
+ switch (img_req->data_type) {
+ case OBJ_REQUEST_BIO:
+ ret = __rbd_img_fill_from_bio(child_img_req,
+ obj_req->img_extents,
+ obj_req->num_img_extents,
+ &obj_req->bio_pos);
+ break;
+ case OBJ_REQUEST_BVECS:
+ case OBJ_REQUEST_OWN_BVECS:
+ ret = __rbd_img_fill_from_bvecs(child_img_req,
+ obj_req->img_extents,
+ obj_req->num_img_extents,
+ &obj_req->bvec_pos);
+ break;
+ default:
+ rbd_assert(0);
+ }
+ } else {
+ ret = rbd_img_fill_from_bvecs(child_img_req,
+ obj_req->img_extents,
+ obj_req->num_img_extents,
+ obj_req->copyup_bvecs);
+ }
+ if (ret) {
+ rbd_img_request_put(child_img_req);
+ return ret;
+ }
+
+ rbd_img_request_submit(child_img_req);
+ return 0;
+}
+
+static bool rbd_obj_handle_read(struct rbd_obj_request *obj_req)
+{
+ struct rbd_device *rbd_dev = obj_req->img_request->rbd_dev;
+ int ret;
+
+ if (obj_req->result == -ENOENT &&
+ rbd_dev->parent_overlap && !obj_req->tried_parent) {
+ /* reverse map this object extent onto the parent */
+ ret = rbd_obj_calc_img_extents(obj_req, false);
+ if (ret) {
+ obj_req->result = ret;
+ return true;
+ }
+
+ if (obj_req->num_img_extents) {
+ obj_req->tried_parent = true;
+ ret = rbd_obj_read_from_parent(obj_req);
+ if (ret) {
+ obj_req->result = ret;
+ return true;
+ }
+ return false;
+ }
}
/*
- * The response data for a STAT call consists of:
- * le64 length;
- * struct {
- * le32 tv_sec;
- * le32 tv_nsec;
- * } mtime;
+ * -ENOENT means a hole in the image -- zero-fill the entire
+ * length of the request. A short read also implies zero-fill
+ * to the end of the request. In both cases we update xferred
+ * count to indicate the whole request was satisfied.
*/
- size = sizeof (__le64) + sizeof (__le32) + sizeof (__le32);
- page_count = (u32)calc_pages_for(0, size);
- pages = ceph_alloc_page_vector(page_count, GFP_NOIO);
- if (IS_ERR(pages)) {
- ret = PTR_ERR(pages);
- goto fail_stat_request;
+ if (obj_req->result == -ENOENT ||
+ (!obj_req->result && obj_req->xferred < obj_req->ex.oe_len)) {
+ rbd_assert(!obj_req->xferred || !obj_req->result);
+ rbd_obj_zero_range(obj_req, obj_req->xferred,
+ obj_req->ex.oe_len - obj_req->xferred);
+ obj_req->result = 0;
+ obj_req->xferred = obj_req->ex.oe_len;
}
- osd_req_op_init(stat_request->osd_req, 0, CEPH_OSD_OP_STAT, 0);
- osd_req_op_raw_data_in_pages(stat_request->osd_req, 0, pages, size, 0,
- false, false);
-
- rbd_obj_request_get(obj_request);
- stat_request->obj_request = obj_request;
- stat_request->pages = pages;
- stat_request->page_count = page_count;
- stat_request->callback = rbd_img_obj_exists_callback;
+ return true;
+}
- rbd_obj_request_submit(stat_request);
- return 0;
+/*
+ * copyup_bvecs pages are never highmem pages
+ */
+static bool is_zero_bvecs(struct bio_vec *bvecs, u32 bytes)
+{
+ struct ceph_bvec_iter it = {
+ .bvecs = bvecs,
+ .iter = { .bi_size = bytes },
+ };
-fail_stat_request:
- rbd_obj_request_put(stat_request);
- return ret;
+ ceph_bvec_iter_advance_step(&it, bytes, ({
+ if (memchr_inv(page_address(bv.bv_page) + bv.bv_offset, 0,
+ bv.bv_len))
+ return false;
+ }));
+ return true;
}
-static bool img_obj_request_simple(struct rbd_obj_request *obj_request)
+static int rbd_obj_issue_copyup(struct rbd_obj_request *obj_req, u32 bytes)
{
- struct rbd_img_request *img_request = obj_request->img_request;
- struct rbd_device *rbd_dev = img_request->rbd_dev;
+ unsigned int num_osd_ops = obj_req->osd_req->r_num_ops;
- /* Reads */
- if (!img_request_write_test(img_request) &&
- !img_request_discard_test(img_request))
- return true;
-
- /* Non-layered writes */
- if (!img_request_layered_test(img_request))
- return true;
+ dout("%s obj_req %p bytes %u\n", __func__, obj_req, bytes);
+ rbd_assert(obj_req->osd_req->r_ops[0].op == CEPH_OSD_OP_STAT);
+ rbd_osd_req_destroy(obj_req->osd_req);
/*
- * Layered writes outside of the parent overlap range don't
- * share any data with the parent.
+ * Create a copyup request with the same number of OSD ops as
+ * the original request. The original request was stat + op(s),
+ * the new copyup request will be copyup + the same op(s).
*/
- if (!obj_request_overlaps_parent(obj_request))
- return true;
+ obj_req->osd_req = rbd_osd_req_create(obj_req, num_osd_ops);
+ if (!obj_req->osd_req)
+ return -ENOMEM;
/*
- * Entire-object layered writes - we will overwrite whatever
- * parent data there is anyway.
+ * Only send non-zero copyup data to save some I/O and network
+ * bandwidth -- zero copyup data is equivalent to the object not
+ * existing.
*/
- if (!obj_request->offset &&
- obj_request->length == rbd_obj_bytes(&rbd_dev->header))
- return true;
+ if (is_zero_bvecs(obj_req->copyup_bvecs, bytes)) {
+ dout("%s obj_req %p detected zeroes\n", __func__, obj_req);
+ bytes = 0;
+ }
- /*
- * If the object is known to already exist, its parent data has
- * already been copied.
- */
- if (obj_request_known_test(obj_request) &&
- obj_request_exists_test(obj_request))
- return true;
+ osd_req_op_cls_init(obj_req->osd_req, 0, CEPH_OSD_OP_CALL, "rbd",
+ "copyup");
+ osd_req_op_cls_request_data_bvecs(obj_req->osd_req, 0,
+ obj_req->copyup_bvecs, bytes);
+
+ switch (obj_req->img_request->op_type) {
+ case OBJ_OP_WRITE:
+ __rbd_obj_setup_write(obj_req, 1);
+ break;
+ case OBJ_OP_DISCARD:
+ rbd_assert(!rbd_obj_is_entire(obj_req));
+ __rbd_obj_setup_discard(obj_req, 1);
+ break;
+ default:
+ rbd_assert(0);
+ }
- return false;
+ rbd_obj_request_submit(obj_req);
+ return 0;
}
-static int rbd_img_obj_request_submit(struct rbd_obj_request *obj_request)
+static int setup_copyup_bvecs(struct rbd_obj_request *obj_req, u64 obj_overlap)
{
- rbd_assert(obj_request_img_data_test(obj_request));
- rbd_assert(obj_request_type_valid(obj_request->type));
- rbd_assert(obj_request->img_request);
+ u32 i;
- if (img_obj_request_simple(obj_request)) {
- rbd_obj_request_submit(obj_request);
- return 0;
- }
+ rbd_assert(!obj_req->copyup_bvecs);
+ obj_req->copyup_bvec_count = calc_pages_for(0, obj_overlap);
+ obj_req->copyup_bvecs = kcalloc(obj_req->copyup_bvec_count,
+ sizeof(*obj_req->copyup_bvecs),
+ GFP_NOIO);
+ if (!obj_req->copyup_bvecs)
+ return -ENOMEM;
- /*
- * It's a layered write. The target object might exist but
- * we may not know that yet. If we know it doesn't exist,
- * start by reading the data for the full target object from
- * the parent so we can use it for a copyup to the target.
- */
- if (obj_request_known_test(obj_request))
- return rbd_img_obj_parent_read_full(obj_request);
+ for (i = 0; i < obj_req->copyup_bvec_count; i++) {
+ unsigned int len = min(obj_overlap, (u64)PAGE_SIZE);
+
+ obj_req->copyup_bvecs[i].bv_page = alloc_page(GFP_NOIO);
+ if (!obj_req->copyup_bvecs[i].bv_page)
+ return -ENOMEM;
- /* We don't know whether the target exists. Go find out. */
+ obj_req->copyup_bvecs[i].bv_offset = 0;
+ obj_req->copyup_bvecs[i].bv_len = len;
+ obj_overlap -= len;
+ }
- return rbd_img_obj_exists_submit(obj_request);
+ rbd_assert(!obj_overlap);
+ return 0;
}
-static int rbd_img_request_submit(struct rbd_img_request *img_request)
+static int rbd_obj_handle_write_guard(struct rbd_obj_request *obj_req)
{
- struct rbd_obj_request *obj_request;
- struct rbd_obj_request *next_obj_request;
- int ret = 0;
-
- dout("%s: img %p\n", __func__, img_request);
+ struct rbd_device *rbd_dev = obj_req->img_request->rbd_dev;
+ int ret;
- rbd_img_request_get(img_request);
- for_each_obj_request_safe(img_request, obj_request, next_obj_request) {
- ret = rbd_img_obj_request_submit(obj_request);
- if (ret)
- goto out_put_ireq;
+ rbd_assert(obj_req->num_img_extents);
+ prune_extents(obj_req->img_extents, &obj_req->num_img_extents,
+ rbd_dev->parent_overlap);
+ if (!obj_req->num_img_extents) {
+ /*
+ * The overlap has become 0 (most likely because the
+ * image has been flattened). Use rbd_obj_issue_copyup()
+ * to re-submit the original write request -- the copyup
+ * operation itself will be a no-op, since someone must
+ * have populated the child object while we weren't
+ * looking. Move to WRITE_FLAT state as we'll be done
+ * with the operation once the null copyup completes.
+ */
+ obj_req->write_state = RBD_OBJ_WRITE_FLAT;
+ return rbd_obj_issue_copyup(obj_req, 0);
}
-out_put_ireq:
- rbd_img_request_put(img_request);
- return ret;
+ ret = setup_copyup_bvecs(obj_req, rbd_obj_img_extents_bytes(obj_req));
+ if (ret)
+ return ret;
+
+ obj_req->write_state = RBD_OBJ_WRITE_COPYUP;
+ return rbd_obj_read_from_parent(obj_req);
}
-static void rbd_img_parent_read_callback(struct rbd_img_request *img_request)
+static bool rbd_obj_handle_write(struct rbd_obj_request *obj_req)
{
- struct rbd_obj_request *obj_request;
- struct rbd_device *rbd_dev;
- u64 obj_end;
- u64 img_xferred;
- int img_result;
+ int ret;
- rbd_assert(img_request_child_test(img_request));
+again:
+ switch (obj_req->write_state) {
+ case RBD_OBJ_WRITE_GUARD:
+ rbd_assert(!obj_req->xferred);
+ if (obj_req->result == -ENOENT) {
+ /*
+ * The target object doesn't exist. Read the data for
+ * the entire target object up to the overlap point (if
+ * any) from the parent, so we can use it for a copyup.
+ */
+ ret = rbd_obj_handle_write_guard(obj_req);
+ if (ret) {
+ obj_req->result = ret;
+ return true;
+ }
+ return false;
+ }
+ /* fall through */
+ case RBD_OBJ_WRITE_FLAT:
+ if (!obj_req->result)
+ /*
+ * There is no such thing as a successful short
+ * write -- indicate the whole request was satisfied.
+ */
+ obj_req->xferred = obj_req->ex.oe_len;
+ return true;
+ case RBD_OBJ_WRITE_COPYUP:
+ obj_req->write_state = RBD_OBJ_WRITE_GUARD;
+ if (obj_req->result)
+ goto again;
- /* First get what we need from the image request and release it */
+ rbd_assert(obj_req->xferred);
+ ret = rbd_obj_issue_copyup(obj_req, obj_req->xferred);
+ if (ret) {
+ obj_req->result = ret;
+ return true;
+ }
+ return false;
+ default:
+ BUG();
+ }
+}
- obj_request = img_request->obj_request;
- img_xferred = img_request->xferred;
- img_result = img_request->result;
- rbd_img_request_put(img_request);
+/*
+ * Returns true if @obj_req is completed, or false otherwise.
+ */
+static bool __rbd_obj_handle_request(struct rbd_obj_request *obj_req)
+{
+ switch (obj_req->img_request->op_type) {
+ case OBJ_OP_READ:
+ return rbd_obj_handle_read(obj_req);
+ case OBJ_OP_WRITE:
+ return rbd_obj_handle_write(obj_req);
+ case OBJ_OP_DISCARD:
+ if (rbd_obj_handle_write(obj_req)) {
+ /*
+ * Hide -ENOENT from delete/truncate/zero -- discarding
+ * a non-existent object is not a problem.
+ */
+ if (obj_req->result == -ENOENT) {
+ obj_req->result = 0;
+ obj_req->xferred = obj_req->ex.oe_len;
+ }
+ return true;
+ }
+ return false;
+ default:
+ BUG();
+ }
+}
- /*
- * If the overlap has become 0 (most likely because the
- * image has been flattened) we need to re-submit the
- * original request.
- */
- rbd_assert(obj_request);
- rbd_assert(obj_request->img_request);
- rbd_dev = obj_request->img_request->rbd_dev;
- if (!rbd_dev->parent_overlap) {
- rbd_obj_request_submit(obj_request);
+static void rbd_obj_end_request(struct rbd_obj_request *obj_req)
+{
+ struct rbd_img_request *img_req = obj_req->img_request;
+
+ rbd_assert((!obj_req->result &&
+ obj_req->xferred == obj_req->ex.oe_len) ||
+ (obj_req->result < 0 && !obj_req->xferred));
+ if (!obj_req->result) {
+ img_req->xferred += obj_req->xferred;
return;
}
- obj_request->result = img_result;
- if (obj_request->result)
- goto out;
+ rbd_warn(img_req->rbd_dev,
+ "%s at objno %llu %llu~%llu result %d xferred %llu",
+ obj_op_name(img_req->op_type), obj_req->ex.oe_objno,
+ obj_req->ex.oe_off, obj_req->ex.oe_len, obj_req->result,
+ obj_req->xferred);
+ if (!img_req->result) {
+ img_req->result = obj_req->result;
+ img_req->xferred = 0;
+ }
+}
- /*
- * We need to zero anything beyond the parent overlap
- * boundary. Since rbd_img_obj_request_read_callback()
- * will zero anything beyond the end of a short read, an
- * easy way to do this is to pretend the data from the
- * parent came up short--ending at the overlap boundary.
- */
- rbd_assert(obj_request->img_offset < U64_MAX - obj_request->length);
- obj_end = obj_request->img_offset + obj_request->length;
- if (obj_end > rbd_dev->parent_overlap) {
- u64 xferred = 0;
+static void rbd_img_end_child_request(struct rbd_img_request *img_req)
+{
+ struct rbd_obj_request *obj_req = img_req->obj_request;
- if (obj_request->img_offset < rbd_dev->parent_overlap)
- xferred = rbd_dev->parent_overlap -
- obj_request->img_offset;
+ rbd_assert(test_bit(IMG_REQ_CHILD, &img_req->flags));
+ rbd_assert((!img_req->result &&
+ img_req->xferred == rbd_obj_img_extents_bytes(obj_req)) ||
+ (img_req->result < 0 && !img_req->xferred));
- obj_request->xferred = min(img_xferred, xferred);
- } else {
- obj_request->xferred = img_xferred;
- }
-out:
- rbd_img_obj_request_read_callback(obj_request);
- rbd_obj_request_complete(obj_request);
+ obj_req->result = img_req->result;
+ obj_req->xferred = img_req->xferred;
+ rbd_img_request_put(img_req);
}
-static void rbd_img_parent_read(struct rbd_obj_request *obj_request)
+static void rbd_img_end_request(struct rbd_img_request *img_req)
{
- struct rbd_img_request *img_request;
- int result;
+ rbd_assert(!test_bit(IMG_REQ_CHILD, &img_req->flags));
+ rbd_assert((!img_req->result &&
+ img_req->xferred == blk_rq_bytes(img_req->rq)) ||
+ (img_req->result < 0 && !img_req->xferred));
- rbd_assert(obj_request_img_data_test(obj_request));
- rbd_assert(obj_request->img_request != NULL);
- rbd_assert(obj_request->result == (s32) -ENOENT);
- rbd_assert(obj_request_type_valid(obj_request->type));
+ blk_mq_end_request(img_req->rq,
+ errno_to_blk_status(img_req->result));
+ rbd_img_request_put(img_req);
+}
- /* rbd_read_finish(obj_request, obj_request->length); */
- img_request = rbd_parent_request_create(obj_request,
- obj_request->img_offset,
- obj_request->length);
- result = -ENOMEM;
- if (!img_request)
- goto out_err;
+static void rbd_obj_handle_request(struct rbd_obj_request *obj_req)
+{
+ struct rbd_img_request *img_req;
- if (obj_request->type == OBJ_REQUEST_BIO)
- result = rbd_img_request_fill(img_request, OBJ_REQUEST_BIO,
- obj_request->bio_list);
- else
- result = rbd_img_request_fill(img_request, OBJ_REQUEST_PAGES,
- obj_request->pages);
- if (result)
- goto out_err;
+again:
+ if (!__rbd_obj_handle_request(obj_req))
+ return;
- img_request->callback = rbd_img_parent_read_callback;
- result = rbd_img_request_submit(img_request);
- if (result)
- goto out_err;
+ img_req = obj_req->img_request;
+ spin_lock(&img_req->completion_lock);
+ rbd_obj_end_request(obj_req);
+ rbd_assert(img_req->pending_count);
+ if (--img_req->pending_count) {
+ spin_unlock(&img_req->completion_lock);
+ return;
+ }
- return;
-out_err:
- if (img_request)
- rbd_img_request_put(img_request);
- obj_request->result = result;
- obj_request->xferred = 0;
- obj_request_done_set(obj_request);
+ spin_unlock(&img_req->completion_lock);
+ if (test_bit(IMG_REQ_CHILD, &img_req->flags)) {
+ obj_req = img_req->obj_request;
+ rbd_img_end_child_request(img_req);
+ goto again;
+ }
+ rbd_img_end_request(img_req);
}
static const struct rbd_client_id rbd_empty_cid;
@@ -3100,8 +2693,8 @@ static int __rbd_notify_op_lock(struct rbd_device *rbd_dev,
{
struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc;
struct rbd_client_id cid = rbd_get_cid(rbd_dev);
- int buf_size = 4 + 8 + 8 + CEPH_ENCODING_START_BLK_LEN;
- char buf[buf_size];
+ char buf[4 + 8 + 8 + CEPH_ENCODING_START_BLK_LEN];
+ int buf_size = sizeof(buf);
void *p = buf;
dout("%s rbd_dev %p notify_op %d\n", __func__, rbd_dev, notify_op);
@@ -3619,8 +3212,8 @@ static void __rbd_acknowledge_notify(struct rbd_device *rbd_dev,
u64 notify_id, u64 cookie, s32 *result)
{
struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc;
- int buf_size = 4 + CEPH_ENCODING_START_BLK_LEN;
- char buf[buf_size];
+ char buf[4 + CEPH_ENCODING_START_BLK_LEN];
+ int buf_size = sizeof(buf);
int ret;
if (result) {
@@ -3896,7 +3489,7 @@ static void rbd_reregister_watch(struct work_struct *work)
ret = rbd_dev_refresh(rbd_dev);
if (ret)
- rbd_warn(rbd_dev, "reregisteration refresh failed: %d", ret);
+ rbd_warn(rbd_dev, "reregistration refresh failed: %d", ret);
}
/*
@@ -3959,9 +3552,22 @@ static int rbd_obj_method_sync(struct rbd_device *rbd_dev,
/*
* lock_rwsem must be held for read
*/
-static void rbd_wait_state_locked(struct rbd_device *rbd_dev)
+static int rbd_wait_state_locked(struct rbd_device *rbd_dev, bool may_acquire)
{
DEFINE_WAIT(wait);
+ unsigned long timeout;
+ int ret = 0;
+
+ if (test_bit(RBD_DEV_FLAG_BLACKLISTED, &rbd_dev->flags))
+ return -EBLACKLISTED;
+
+ if (rbd_dev->lock_state == RBD_LOCK_STATE_LOCKED)
+ return 0;
+
+ if (!may_acquire) {
+ rbd_warn(rbd_dev, "exclusive lock required");
+ return -EROFS;
+ }
do {
/*
@@ -3973,12 +3579,22 @@ static void rbd_wait_state_locked(struct rbd_device *rbd_dev)
prepare_to_wait_exclusive(&rbd_dev->lock_waitq, &wait,
TASK_UNINTERRUPTIBLE);
up_read(&rbd_dev->lock_rwsem);
- schedule();
+ timeout = schedule_timeout(ceph_timeout_jiffies(
+ rbd_dev->opts->lock_timeout));
down_read(&rbd_dev->lock_rwsem);
- } while (rbd_dev->lock_state != RBD_LOCK_STATE_LOCKED &&
- !test_bit(RBD_DEV_FLAG_BLACKLISTED, &rbd_dev->flags));
+ if (test_bit(RBD_DEV_FLAG_BLACKLISTED, &rbd_dev->flags)) {
+ ret = -EBLACKLISTED;
+ break;
+ }
+ if (!timeout) {
+ rbd_warn(rbd_dev, "timed out waiting for lock");
+ ret = -ETIMEDOUT;
+ break;
+ }
+ } while (rbd_dev->lock_state != RBD_LOCK_STATE_LOCKED);
finish_wait(&rbd_dev->lock_waitq, &wait);
+ return ret;
}
static void rbd_queue_workfn(struct work_struct *work)
@@ -4064,23 +3680,13 @@ static void rbd_queue_workfn(struct work_struct *work)
(op_type != OBJ_OP_READ || rbd_dev->opts->lock_on_read);
if (must_be_locked) {
down_read(&rbd_dev->lock_rwsem);
- if (rbd_dev->lock_state != RBD_LOCK_STATE_LOCKED &&
- !test_bit(RBD_DEV_FLAG_BLACKLISTED, &rbd_dev->flags)) {
- if (rbd_dev->opts->exclusive) {
- rbd_warn(rbd_dev, "exclusive lock required");
- result = -EROFS;
- goto err_unlock;
- }
- rbd_wait_state_locked(rbd_dev);
- }
- if (test_bit(RBD_DEV_FLAG_BLACKLISTED, &rbd_dev->flags)) {
- result = -EBLACKLISTED;
+ result = rbd_wait_state_locked(rbd_dev,
+ !rbd_dev->opts->exclusive);
+ if (result)
goto err_unlock;
- }
}
- img_request = rbd_img_request_create(rbd_dev, offset, length, op_type,
- snapc);
+ img_request = rbd_img_request_create(rbd_dev, op_type, snapc);
if (!img_request) {
result = -ENOMEM;
goto err_unlock;
@@ -4089,18 +3695,14 @@ static void rbd_queue_workfn(struct work_struct *work)
snapc = NULL; /* img_request consumes a ref */
if (op_type == OBJ_OP_DISCARD)
- result = rbd_img_request_fill(img_request, OBJ_REQUEST_NODATA,
- NULL);
+ result = rbd_img_fill_nodata(img_request, offset, length);
else
- result = rbd_img_request_fill(img_request, OBJ_REQUEST_BIO,
- rq->bio);
- if (result)
- goto err_img_request;
-
- result = rbd_img_request_submit(img_request);
+ result = rbd_img_fill_from_bio(img_request, offset, length,
+ rq->bio);
if (result)
goto err_img_request;
+ rbd_img_request_submit(img_request);
if (must_be_locked)
up_read(&rbd_dev->lock_rwsem);
return;
@@ -4333,7 +3935,8 @@ static int rbd_init_disk(struct rbd_device *rbd_dev)
{
struct gendisk *disk;
struct request_queue *q;
- u64 segment_size;
+ unsigned int objset_bytes =
+ rbd_dev->layout.object_size * rbd_dev->layout.stripe_count;
int err;
/* create gendisk info */
@@ -4370,23 +3973,22 @@ static int rbd_init_disk(struct rbd_device *rbd_dev)
goto out_tag_set;
}
- queue_flag_set_unlocked(QUEUE_FLAG_NONROT, q);
+ blk_queue_flag_set(QUEUE_FLAG_NONROT, q);
/* QUEUE_FLAG_ADD_RANDOM is off by default for blk-mq */
- /* set io sizes to object size */
- segment_size = rbd_obj_bytes(&rbd_dev->header);
- blk_queue_max_hw_sectors(q, segment_size / SECTOR_SIZE);
+ blk_queue_max_hw_sectors(q, objset_bytes >> SECTOR_SHIFT);
q->limits.max_sectors = queue_max_hw_sectors(q);
blk_queue_max_segments(q, USHRT_MAX);
- blk_queue_max_segment_size(q, segment_size);
- blk_queue_io_min(q, segment_size);
- blk_queue_io_opt(q, segment_size);
+ blk_queue_max_segment_size(q, UINT_MAX);
+ blk_queue_io_min(q, objset_bytes);
+ blk_queue_io_opt(q, objset_bytes);
- /* enable the discard support */
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q);
- q->limits.discard_granularity = segment_size;
- blk_queue_max_discard_sectors(q, segment_size / SECTOR_SIZE);
- blk_queue_max_write_zeroes_sectors(q, segment_size / SECTOR_SIZE);
+ if (rbd_dev->opts->trim) {
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, q);
+ q->limits.discard_granularity = objset_bytes;
+ blk_queue_max_discard_sectors(q, objset_bytes >> SECTOR_SHIFT);
+ blk_queue_max_write_zeroes_sectors(q, objset_bytes >> SECTOR_SHIFT);
+ }
if (!ceph_test_opt(rbd_dev->rbd_client->client, NOCRC))
q->backing_dev_info->capabilities |= BDI_CAP_STABLE_WRITES;
@@ -5066,9 +4668,6 @@ static int rbd_dev_v2_striping_info(struct rbd_device *rbd_dev)
} __attribute__ ((packed)) striping_info_buf = { 0 };
size_t size = sizeof (striping_info_buf);
void *p;
- u64 obj_size;
- u64 stripe_unit;
- u64 stripe_count;
int ret;
ret = rbd_obj_method_sync(rbd_dev, &rbd_dev->header_oid,
@@ -5080,31 +4679,9 @@ static int rbd_dev_v2_striping_info(struct rbd_device *rbd_dev)
if (ret < size)
return -ERANGE;
- /*
- * We don't actually support the "fancy striping" feature
- * (STRIPINGV2) yet, but if the striping sizes are the
- * defaults the behavior is the same as before. So find
- * out, and only fail if the image has non-default values.
- */
- ret = -EINVAL;
- obj_size = rbd_obj_bytes(&rbd_dev->header);
p = &striping_info_buf;
- stripe_unit = ceph_decode_64(&p);
- if (stripe_unit != obj_size) {
- rbd_warn(rbd_dev, "unsupported stripe unit "
- "(got %llu want %llu)",
- stripe_unit, obj_size);
- return -EINVAL;
- }
- stripe_count = ceph_decode_64(&p);
- if (stripe_count != 1) {
- rbd_warn(rbd_dev, "unsupported stripe count "
- "(got %llu want 1)", stripe_count);
- return -EINVAL;
- }
- rbd_dev->header.stripe_unit = stripe_unit;
- rbd_dev->header.stripe_count = stripe_count;
-
+ rbd_dev->header.stripe_unit = ceph_decode_64(&p);
+ rbd_dev->header.stripe_count = ceph_decode_64(&p);
return 0;
}
@@ -5635,8 +5212,10 @@ static int rbd_add_parse_args(const char *buf,
rbd_opts->read_only = RBD_READ_ONLY_DEFAULT;
rbd_opts->queue_depth = RBD_QUEUE_DEPTH_DEFAULT;
+ rbd_opts->lock_timeout = RBD_LOCK_TIMEOUT_DEFAULT;
rbd_opts->lock_on_read = RBD_LOCK_ON_READ_DEFAULT;
rbd_opts->exclusive = RBD_EXCLUSIVE_DEFAULT;
+ rbd_opts->trim = RBD_TRIM_DEFAULT;
copts = ceph_parse_options(options, mon_addrs,
mon_addrs + mon_addrs_size - 1,
@@ -5662,39 +5241,6 @@ out_err:
return ret;
}
-/*
- * Return pool id (>= 0) or a negative error code.
- */
-static int rbd_add_get_pool_id(struct rbd_client *rbdc, const char *pool_name)
-{
- struct ceph_options *opts = rbdc->client->options;
- u64 newest_epoch;
- int tries = 0;
- int ret;
-
-again:
- ret = ceph_pg_poolid_by_name(rbdc->client->osdc.osdmap, pool_name);
- if (ret == -ENOENT && tries++ < 1) {
- ret = ceph_monc_get_version(&rbdc->client->monc, "osdmap",
- &newest_epoch);
- if (ret < 0)
- return ret;
-
- if (rbdc->client->osdc.osdmap->epoch < newest_epoch) {
- ceph_osdc_maybe_request_map(&rbdc->client->osdc);
- (void) ceph_monc_wait_osdmap(&rbdc->client->monc,
- newest_epoch,
- opts->mount_timeout);
- goto again;
- } else {
- /* the osdmap we have is new enough */
- return -ENOENT;
- }
- }
-
- return ret;
-}
-
static void rbd_dev_image_unlock(struct rbd_device *rbd_dev)
{
down_write(&rbd_dev->lock_rwsem);
@@ -5705,6 +5251,8 @@ static void rbd_dev_image_unlock(struct rbd_device *rbd_dev)
static int rbd_add_acquire_lock(struct rbd_device *rbd_dev)
{
+ int ret;
+
if (!(rbd_dev->header.features & RBD_FEATURE_EXCLUSIVE_LOCK)) {
rbd_warn(rbd_dev, "exclusive-lock feature is not enabled");
return -EINVAL;
@@ -5712,9 +5260,9 @@ static int rbd_add_acquire_lock(struct rbd_device *rbd_dev)
/* FIXME: "rbd map --exclusive" should be in interruptible */
down_read(&rbd_dev->lock_rwsem);
- rbd_wait_state_locked(rbd_dev);
+ ret = rbd_wait_state_locked(rbd_dev, true);
up_read(&rbd_dev->lock_rwsem);
- if (test_bit(RBD_DEV_FLAG_BLACKLISTED, &rbd_dev->flags)) {
+ if (ret) {
rbd_warn(rbd_dev, "failed to acquire exclusive lock");
return -EROFS;
}
@@ -6123,7 +5671,7 @@ static ssize_t do_rbd_add(struct bus_type *bus,
}
/* pick the pool */
- rc = rbd_add_get_pool_id(rbdc, spec->pool_name);
+ rc = ceph_pg_poolid_by_name(rbdc->client->osdc.osdmap, spec->pool_name);
if (rc < 0) {
if (rc == -ENOENT)
pr_info("pool %s does not exist\n", spec->pool_name);
@@ -6375,16 +5923,8 @@ static int rbd_slab_init(void)
if (!rbd_obj_request_cache)
goto out_err;
- rbd_assert(!rbd_bio_clone);
- rbd_bio_clone = bioset_create(BIO_POOL_SIZE, 0, 0);
- if (!rbd_bio_clone)
- goto out_err_clone;
-
return 0;
-out_err_clone:
- kmem_cache_destroy(rbd_obj_request_cache);
- rbd_obj_request_cache = NULL;
out_err:
kmem_cache_destroy(rbd_img_request_cache);
rbd_img_request_cache = NULL;
@@ -6400,10 +5940,6 @@ static void rbd_slab_exit(void)
rbd_assert(rbd_img_request_cache);
kmem_cache_destroy(rbd_img_request_cache);
rbd_img_request_cache = NULL;
-
- rbd_assert(rbd_bio_clone);
- bioset_free(rbd_bio_clone);
- rbd_bio_clone = NULL;
}
static int __init rbd_init(void)
diff --git a/drivers/block/rsxx/dev.c b/drivers/block/rsxx/dev.c
index e397d3ee7308..dddb3f2490b6 100644
--- a/drivers/block/rsxx/dev.c
+++ b/drivers/block/rsxx/dev.c
@@ -287,10 +287,10 @@ int rsxx_setup_dev(struct rsxx_cardinfo *card)
blk_queue_max_hw_sectors(card->queue, blkdev_max_hw_sectors);
blk_queue_physical_block_size(card->queue, RSXX_HW_BLK_SIZE);
- queue_flag_set_unlocked(QUEUE_FLAG_NONROT, card->queue);
- queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, card->queue);
+ blk_queue_flag_set(QUEUE_FLAG_NONROT, card->queue);
+ blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, card->queue);
if (rsxx_discard_supported(card)) {
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, card->queue);
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, card->queue);
blk_queue_max_discard_sectors(card->queue,
RSXX_HW_BLK_SIZE >> 9);
card->queue->limits.discard_granularity = RSXX_HW_BLK_SIZE;
diff --git a/drivers/block/skd_main.c b/drivers/block/skd_main.c
index e41935ab41ef..bc7aea6d7b7c 100644
--- a/drivers/block/skd_main.c
+++ b/drivers/block/skd_main.c
@@ -2858,8 +2858,8 @@ static int skd_cons_disk(struct skd_device *skdev)
/* set optimal I/O size to 8KB */
blk_queue_io_opt(q, 8192);
- queue_flag_set_unlocked(QUEUE_FLAG_NONROT, q);
- queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, q);
+ blk_queue_flag_set(QUEUE_FLAG_NONROT, q);
+ blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, q);
blk_queue_rq_timeout(q, 8 * HZ);
diff --git a/drivers/block/swim.c b/drivers/block/swim.c
index 64e066eba72e..0e31884a9519 100644
--- a/drivers/block/swim.c
+++ b/drivers/block/swim.c
@@ -110,7 +110,7 @@ struct iwm {
/* Select values for swim_select and swim_readbit */
#define READ_DATA_0 0x074
-#define TWOMEG_DRIVE 0x075
+#define ONEMEG_DRIVE 0x075
#define SINGLE_SIDED 0x076
#define DRIVE_PRESENT 0x077
#define DISK_IN 0x170
@@ -118,9 +118,9 @@ struct iwm {
#define TRACK_ZERO 0x172
#define TACHO 0x173
#define READ_DATA_1 0x174
-#define MFM_MODE 0x175
+#define GCR_MODE 0x175
#define SEEK_COMPLETE 0x176
-#define ONEMEG_MEDIA 0x177
+#define TWOMEG_MEDIA 0x177
/* Bits in handshake register */
@@ -612,7 +612,6 @@ static void setup_medium(struct floppy_state *fs)
struct floppy_struct *g;
fs->disk_in = 1;
fs->write_protected = swim_readbit(base, WRITE_PROT);
- fs->type = swim_readbit(base, ONEMEG_MEDIA);
if (swim_track00(base))
printk(KERN_ERR
@@ -620,6 +619,9 @@ static void setup_medium(struct floppy_state *fs)
swim_track00(base);
+ fs->type = swim_readbit(base, TWOMEG_MEDIA) ?
+ HD_MEDIA : DD_MEDIA;
+ fs->head_number = swim_readbit(base, SINGLE_SIDED) ? 1 : 2;
get_floppy_geometry(fs, 0, &g);
fs->total_secs = g->size;
fs->secpercyl = g->head * g->sect;
@@ -646,7 +648,7 @@ static int floppy_open(struct block_device *bdev, fmode_t mode)
swim_write(base, setup, S_IBM_DRIVE | S_FCLK_DIV2);
udelay(10);
- swim_drive(base, INTERNAL_DRIVE);
+ swim_drive(base, fs->location);
swim_motor(base, ON);
swim_action(base, SETMFM);
if (fs->ejected)
@@ -656,6 +658,8 @@ static int floppy_open(struct block_device *bdev, fmode_t mode)
goto out;
}
+ set_capacity(fs->disk, fs->total_secs);
+
if (mode & FMODE_NDELAY)
return 0;
@@ -727,14 +731,9 @@ static int floppy_ioctl(struct block_device *bdev, fmode_t mode,
if (copy_to_user((void __user *) param, (void *) &floppy_type,
sizeof(struct floppy_struct)))
return -EFAULT;
- break;
-
- default:
- printk(KERN_DEBUG "SWIM floppy_ioctl: unknown cmd %d\n",
- cmd);
- return -ENOSYS;
+ return 0;
}
- return 0;
+ return -ENOTTY;
}
static int floppy_getgeo(struct block_device *bdev, struct hd_geometry *geo)
@@ -795,7 +794,7 @@ static struct kobject *floppy_find(dev_t dev, int *part, void *data)
struct swim_priv *swd = data;
int drive = (*part & 3);
- if (drive > swd->floppy_count)
+ if (drive >= swd->floppy_count)
return NULL;
*part = 0;
@@ -813,10 +812,9 @@ static int swim_add_floppy(struct swim_priv *swd, enum drive_location location)
swim_motor(base, OFF);
- if (swim_readbit(base, SINGLE_SIDED))
- fs->head_number = 1;
- else
- fs->head_number = 2;
+ fs->type = HD_MEDIA;
+ fs->head_number = 2;
+
fs->ref_count = 0;
fs->ejected = 1;
@@ -834,10 +832,12 @@ static int swim_floppy_init(struct swim_priv *swd)
/* scan floppy drives */
swim_drive(base, INTERNAL_DRIVE);
- if (swim_readbit(base, DRIVE_PRESENT))
+ if (swim_readbit(base, DRIVE_PRESENT) &&
+ !swim_readbit(base, ONEMEG_DRIVE))
swim_add_floppy(swd, INTERNAL_DRIVE);
swim_drive(base, EXTERNAL_DRIVE);
- if (swim_readbit(base, DRIVE_PRESENT))
+ if (swim_readbit(base, DRIVE_PRESENT) &&
+ !swim_readbit(base, ONEMEG_DRIVE))
swim_add_floppy(swd, EXTERNAL_DRIVE);
/* register floppy drives */
@@ -861,7 +861,6 @@ static int swim_floppy_init(struct swim_priv *swd)
&swd->lock);
if (!swd->unit[drive].disk->queue) {
err = -ENOMEM;
- put_disk(swd->unit[drive].disk);
goto exit_put_disks;
}
blk_queue_bounce_limit(swd->unit[drive].disk->queue,
@@ -911,7 +910,7 @@ static int swim_probe(struct platform_device *dev)
goto out;
}
- swim_base = ioremap(res->start, resource_size(res));
+ swim_base = (struct swim __iomem *)res->start;
if (!swim_base) {
ret = -ENOMEM;
goto out_release_io;
@@ -923,7 +922,7 @@ static int swim_probe(struct platform_device *dev)
if (!get_swim_mode(swim_base)) {
printk(KERN_INFO "SWIM device not found !\n");
ret = -ENODEV;
- goto out_iounmap;
+ goto out_release_io;
}
/* set platform driver data */
@@ -931,7 +930,7 @@ static int swim_probe(struct platform_device *dev)
swd = kzalloc(sizeof(struct swim_priv), GFP_KERNEL);
if (!swd) {
ret = -ENOMEM;
- goto out_iounmap;
+ goto out_release_io;
}
platform_set_drvdata(dev, swd);
@@ -945,8 +944,6 @@ static int swim_probe(struct platform_device *dev)
out_kfree:
kfree(swd);
-out_iounmap:
- iounmap(swim_base);
out_release_io:
release_mem_region(res->start, resource_size(res));
out:
@@ -974,8 +971,6 @@ static int swim_remove(struct platform_device *dev)
for (drive = 0; drive < swd->floppy_count; drive++)
floppy_eject(&swd->unit[drive]);
- iounmap(swd->base);
-
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (res)
release_mem_region(res->start, resource_size(res));
diff --git a/drivers/block/swim3.c b/drivers/block/swim3.c
index af51015d056e..469541c1e51e 100644
--- a/drivers/block/swim3.c
+++ b/drivers/block/swim3.c
@@ -148,7 +148,7 @@ struct swim3 {
#define MOTOR_ON 2
#define RELAX 3 /* also eject in progress */
#define READ_DATA_0 4
-#define TWOMEG_DRIVE 5
+#define ONEMEG_DRIVE 5
#define SINGLE_SIDED 6 /* drive or diskette is 4MB type? */
#define DRIVE_PRESENT 7
#define DISK_IN 8
@@ -156,9 +156,9 @@ struct swim3 {
#define TRACK_ZERO 10
#define TACHO 11
#define READ_DATA_1 12
-#define MFM_MODE 13
+#define GCR_MODE 13
#define SEEK_COMPLETE 14
-#define ONEMEG_MEDIA 15
+#define TWOMEG_MEDIA 15
/* Definitions of values used in writing and formatting */
#define DATA_ESCAPE 0x99
diff --git a/drivers/block/umem.c b/drivers/block/umem.c
index 8077123678ad..5c7fb8cc4149 100644
--- a/drivers/block/umem.c
+++ b/drivers/block/umem.c
@@ -888,13 +888,14 @@ static int mm_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
card->Active = -1; /* no page is active */
card->bio = NULL;
card->biotail = &card->bio;
+ spin_lock_init(&card->lock);
- card->queue = blk_alloc_queue(GFP_KERNEL);
+ card->queue = blk_alloc_queue_node(GFP_KERNEL, NUMA_NO_NODE,
+ &card->lock);
if (!card->queue)
goto failed_alloc;
blk_queue_make_request(card->queue, mm_make_request);
- card->queue->queue_lock = &card->lock;
card->queue->queuedata = card;
tasklet_init(&card->tasklet, process_page, (unsigned long)card);
@@ -968,8 +969,6 @@ static int mm_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
dev_printk(KERN_INFO, &card->dev->dev,
"Window size %d bytes, IRQ %d\n", data, dev->irq);
- spin_lock_init(&card->lock);
-
pci_set_drvdata(dev, card);
if (pci_write_cmd != 0x0F) /* If not Memory Write & Invalidate */
diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
index 92ec1bbece51..2a8e7813bd1a 100644
--- a/drivers/block/xen-blkfront.c
+++ b/drivers/block/xen-blkfront.c
@@ -932,15 +932,15 @@ static void blkif_set_queue_limits(struct blkfront_info *info)
unsigned int segments = info->max_indirect_segments ? :
BLKIF_MAX_SEGMENTS_PER_REQUEST;
- queue_flag_set_unlocked(QUEUE_FLAG_VIRT, rq);
+ blk_queue_flag_set(QUEUE_FLAG_VIRT, rq);
if (info->feature_discard) {
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, rq);
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, rq);
blk_queue_max_discard_sectors(rq, get_capacity(gd));
rq->limits.discard_granularity = info->discard_granularity;
rq->limits.discard_alignment = info->discard_alignment;
if (info->feature_secdiscard)
- queue_flag_set_unlocked(QUEUE_FLAG_SECERASE, rq);
+ blk_queue_flag_set(QUEUE_FLAG_SECERASE, rq);
}
/* Hard sector size and max sectors impersonate the equiv. hardware. */
@@ -1611,8 +1611,8 @@ static irqreturn_t blkif_interrupt(int irq, void *dev_id)
blkif_req(req)->error = BLK_STS_NOTSUPP;
info->feature_discard = 0;
info->feature_secdiscard = 0;
- queue_flag_clear(QUEUE_FLAG_DISCARD, rq);
- queue_flag_clear(QUEUE_FLAG_SECERASE, rq);
+ blk_queue_flag_clear(QUEUE_FLAG_DISCARD, rq);
+ blk_queue_flag_clear(QUEUE_FLAG_SECERASE, rq);
}
break;
case BLKIF_OP_FLUSH_DISKCACHE:
diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index 0afa6c8c3857..0f3fadd71230 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -44,6 +44,11 @@ static const char *default_compressor = "lzo";
/* Module params (documentation at end) */
static unsigned int num_devices = 1;
+/*
+ * Pages that compress to sizes equals or greater than this are stored
+ * uncompressed in memory.
+ */
+static size_t huge_class_size;
static void zram_free_page(struct zram *zram, size_t index);
@@ -786,6 +791,8 @@ static bool zram_meta_alloc(struct zram *zram, u64 disksize)
return false;
}
+ if (!huge_class_size)
+ huge_class_size = zs_huge_class_size(zram->mem_pool);
return true;
}
@@ -965,7 +972,7 @@ compress_again:
return ret;
}
- if (unlikely(comp_len > max_zpage_size)) {
+ if (unlikely(comp_len >= huge_class_size)) {
if (zram_wb_enabled(zram) && allow_wb) {
zcomp_stream_put(zram->comp);
ret = write_to_bdev(zram, bvec, index, bio, &element);
@@ -1530,8 +1537,8 @@ static int zram_add(void)
/* Actual capacity set using syfs (/sys/block/zram<id>/disksize */
set_capacity(zram->disk, 0);
/* zram devices sort of resembles non-rotational disks */
- queue_flag_set_unlocked(QUEUE_FLAG_NONROT, zram->disk->queue);
- queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, zram->disk->queue);
+ blk_queue_flag_set(QUEUE_FLAG_NONROT, zram->disk->queue);
+ blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, zram->disk->queue);
/*
* To ensure that we always get PAGE_SIZE aligned
@@ -1544,7 +1551,7 @@ static int zram_add(void)
blk_queue_io_opt(zram->disk->queue, PAGE_SIZE);
zram->disk->queue->limits.discard_granularity = PAGE_SIZE;
blk_queue_max_discard_sectors(zram->disk->queue, UINT_MAX);
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, zram->disk->queue);
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, zram->disk->queue);
/*
* zram_bio_discard() will clear all logical blocks if logical block
@@ -1620,8 +1627,8 @@ static int zram_remove(struct zram *zram)
pr_info("Removed device: %s\n", zram->disk->disk_name);
- blk_cleanup_queue(zram->disk->queue);
del_gendisk(zram->disk);
+ blk_cleanup_queue(zram->disk->queue);
put_disk(zram->disk);
kfree(zram);
return 0;
diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h
index 31762db861e3..008861220723 100644
--- a/drivers/block/zram/zram_drv.h
+++ b/drivers/block/zram/zram_drv.h
@@ -21,23 +21,6 @@
#include "zcomp.h"
-/*-- Configurable parameters */
-
-/*
- * Pages that compress to size greater than this are stored
- * uncompressed in memory.
- */
-static const size_t max_zpage_size = PAGE_SIZE / 4 * 3;
-
-/*
- * NOTE: max_zpage_size must be less than or equal to:
- * ZS_MAX_ALLOC_SIZE. Otherwise, zs_malloc() would
- * always return failure.
- */
-
-/*-- End of configurable params */
-
-#define SECTOR_SHIFT 9
#define SECTORS_PER_PAGE_SHIFT (PAGE_SHIFT - SECTOR_SHIFT)
#define SECTORS_PER_PAGE (1 << SECTORS_PER_PAGE_SHIFT)
#define ZRAM_LOGICAL_BLOCK_SHIFT 12
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 07e55cd8f8c8..010f5f579e68 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -147,6 +147,7 @@ config BT_HCIUART_ATH3K
config BT_HCIUART_LL
bool "HCILL protocol support"
depends on BT_HCIUART_SERDEV
+ select BT_HCIUART_H4
help
HCILL (HCI Low Level) is a serial protocol for communication
between Bluetooth device and host. This protocol is required for
@@ -242,8 +243,7 @@ config BT_HCIBCM203X
config BT_HCIBPA10X
tristate "HCI BPA10x USB driver"
- depends on USB && BT_HCIUART
- select BT_HCIUART_H4
+ depends on USB
help
Bluetooth HCI BPA10x USB driver.
This driver provides support for the Digianswer BPA 100/105 Bluetooth
@@ -305,22 +305,6 @@ config BT_HCIBLUECARD
Say Y here to compile support for HCI BlueCard devices into the
kernel or say M to compile it as module (bluecard_cs).
-config BT_HCIBTUART
- tristate "HCI UART (PC Card) device driver"
- depends on PCMCIA
- help
- Bluetooth HCI UART (PC Card) driver.
- This driver provides support for Bluetooth PCMCIA devices with
- an UART interface:
- Xircom CreditCard Bluetooth Adapter
- Xircom RealPort2 Bluetooth Adapter
- Sphinx PICO Card
- H-Soft blue+Card
- Cyber-blue Compact Flash Card
-
- Say Y here to compile support for HCI UART devices into the
- kernel or say M to compile it as module (btuart_cs).
-
config BT_HCIVHCI
tristate "HCI VHCI (Virtual HCI device) driver"
help
@@ -392,4 +376,14 @@ config BT_QCOMSMD
Say Y here to compile support for HCI over Qualcomm SMD into the
kernel or say M to compile as a module.
+config BT_HCIRSI
+ tristate
+ help
+ Redpine BT driver.
+ This driver handles BT traffic from upper layers and pass
+ to the RSI_91x coex module for further scheduling to device
+
+ Say Y here to compile support for HCI over Redpine into the
+ kernel or say M to compile as a module.
+
endmenu
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index 4e4e44d09796..ec16c55eb6e9 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -11,7 +11,6 @@ obj-$(CONFIG_BT_HCIBFUSB) += bfusb.o
obj-$(CONFIG_BT_HCIDTL1) += dtl1_cs.o
obj-$(CONFIG_BT_HCIBT3C) += bt3c_cs.o
obj-$(CONFIG_BT_HCIBLUECARD) += bluecard_cs.o
-obj-$(CONFIG_BT_HCIBTUART) += btuart_cs.o
obj-$(CONFIG_BT_HCIBTUSB) += btusb.o
obj-$(CONFIG_BT_HCIBTSDIO) += btsdio.o
@@ -28,6 +27,8 @@ obj-$(CONFIG_BT_QCA) += btqca.o
obj-$(CONFIG_BT_HCIUART_NOKIA) += hci_nokia.o
+obj-$(CONFIG_BT_HCIRSI) += btrsi.o
+
btmrvl-y := btmrvl_main.o
btmrvl-$(CONFIG_DEBUG_FS) += btmrvl_debugfs.o
diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c
index 204afe66de92..3d7a5c149af3 100644
--- a/drivers/bluetooth/ath3k.c
+++ b/drivers/bluetooth/ath3k.c
@@ -203,6 +203,12 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
{ } /* Terminating entry */
};
+static inline void ath3k_log_failed_loading(int err, int len, int size)
+{
+ BT_ERR("Error in firmware loading err = %d, len = %d, size = %d",
+ err, len, size);
+}
+
#define USB_REQ_DFU_DNLOAD 1
#define BULK_SIZE 4096
#define FW_HDR_SIZE 20
@@ -227,15 +233,16 @@ static int ath3k_load_firmware(struct usb_device *udev,
return -ENOMEM;
}
- memcpy(send_buf, firmware->data, 20);
+ memcpy(send_buf, firmware->data, FW_HDR_SIZE);
err = usb_control_msg(udev, pipe, USB_REQ_DFU_DNLOAD, USB_TYPE_VENDOR,
- 0, 0, send_buf, 20, USB_CTRL_SET_TIMEOUT);
+ 0, 0, send_buf, FW_HDR_SIZE,
+ USB_CTRL_SET_TIMEOUT);
if (err < 0) {
BT_ERR("Can't change to loading configuration err");
goto error;
}
- sent += 20;
- count -= 20;
+ sent += FW_HDR_SIZE;
+ count -= FW_HDR_SIZE;
pipe = usb_sndbulkpipe(udev, 0x02);
@@ -250,8 +257,7 @@ static int ath3k_load_firmware(struct usb_device *udev,
&len, 3000);
if (err || (len != size)) {
- BT_ERR("Error in firmware loading err = %d,"
- "len = %d, size = %d", err, len, size);
+ ath3k_log_failed_loading(err, len, size);
goto error;
}
@@ -350,8 +356,7 @@ static int ath3k_load_fwfile(struct usb_device *udev,
err = usb_bulk_msg(udev, pipe, send_buf, size,
&len, 3000);
if (err || (len != size)) {
- BT_ERR("Error in firmware loading err = %d,"
- "len = %d, size = %d", err, len, size);
+ ath3k_log_failed_loading(err, len, size);
kfree(send_buf);
return err;
}
@@ -398,7 +403,7 @@ static int ath3k_set_normal_mode(struct usb_device *udev)
static int ath3k_load_patch(struct usb_device *udev)
{
unsigned char fw_state;
- char filename[ATH3K_NAME_LEN] = {0};
+ char filename[ATH3K_NAME_LEN];
const struct firmware *firmware;
struct ath3k_version fw_version;
__u32 pt_rom_version, pt_build_version;
@@ -451,7 +456,7 @@ static int ath3k_load_patch(struct usb_device *udev)
static int ath3k_load_syscfg(struct usb_device *udev)
{
unsigned char fw_state;
- char filename[ATH3K_NAME_LEN] = {0};
+ char filename[ATH3K_NAME_LEN];
const struct firmware *firmware;
struct ath3k_version fw_version;
int clk_value, ret;
@@ -522,7 +527,6 @@ static int ath3k_probe(struct usb_interface *intf,
/* load patch and sysconfig files for AR3012 */
if (id->driver_info & BTUSB_ATH3012) {
-
/* New firmware with patch and sysconfig files already loaded */
if (le16_to_cpu(udev->descriptor.bcdDevice) > 0x0001)
return -ENODEV;
@@ -565,7 +569,7 @@ static int ath3k_probe(struct usb_interface *intf,
static void ath3k_disconnect(struct usb_interface *intf)
{
- BT_DBG("ath3k_disconnect intf %p", intf);
+ BT_DBG("%s intf %p", __func__, intf);
}
static struct usb_driver ath3k_driver = {
diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c
index 801ea4ca65e4..c6f7cc57db14 100644
--- a/drivers/bluetooth/bpa10x.c
+++ b/drivers/bluetooth/bpa10x.c
@@ -35,7 +35,7 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
-#include "hci_uart.h"
+#include "h4_recv.h"
#define VERSION "0.11"
diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c
index b280d466f05b..f6c694a1b9b0 100644
--- a/drivers/bluetooth/btmrvl_main.c
+++ b/drivers/bluetooth/btmrvl_main.c
@@ -183,7 +183,7 @@ static int btmrvl_send_sync_cmd(struct btmrvl_private *priv, u16 opcode,
return -EFAULT;
}
- skb = bt_skb_alloc(HCI_COMMAND_HDR_SIZE + len, GFP_ATOMIC);
+ skb = bt_skb_alloc(HCI_COMMAND_HDR_SIZE + len, GFP_KERNEL);
if (!skb) {
BT_ERR("No free skb");
return -ENOMEM;
diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c
index 7dbb4463b539..6f99b9f3d57f 100644
--- a/drivers/bluetooth/btmrvl_sdio.c
+++ b/drivers/bluetooth/btmrvl_sdio.c
@@ -689,7 +689,7 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
int ret, num_blocks, blksz;
struct sk_buff *skb = NULL;
u32 type;
- u8 *payload = NULL;
+ u8 *payload;
struct hci_dev *hdev = priv->btmrvl_dev.hcidev;
struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
@@ -920,7 +920,7 @@ static int btmrvl_sdio_register_dev(struct btmrvl_sdio_card *card)
{
struct sdio_func *func;
u8 reg;
- int ret = 0;
+ int ret;
if (!card || !card->func) {
BT_ERR("Error: card or function is NULL!");
diff --git a/drivers/bluetooth/btrsi.c b/drivers/bluetooth/btrsi.c
new file mode 100644
index 000000000000..60d1419590ba
--- /dev/null
+++ b/drivers/bluetooth/btrsi.c
@@ -0,0 +1,187 @@
+/**
+ * Copyright (c) 2017 Redpine Signals Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <asm/unaligned.h>
+#include <net/rsi_91x.h>
+#include <net/genetlink.h>
+
+#define RSI_HEADROOM_FOR_BT_HAL 16
+#define RSI_FRAME_DESC_SIZE 16
+
+struct rsi_hci_adapter {
+ void *priv;
+ struct rsi_proto_ops *proto_ops;
+ struct hci_dev *hdev;
+};
+
+static int rsi_hci_open(struct hci_dev *hdev)
+{
+ return 0;
+}
+
+static int rsi_hci_close(struct hci_dev *hdev)
+{
+ return 0;
+}
+
+static int rsi_hci_flush(struct hci_dev *hdev)
+{
+ return 0;
+}
+
+static int rsi_hci_send_pkt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct rsi_hci_adapter *h_adapter = hci_get_drvdata(hdev);
+ struct sk_buff *new_skb = NULL;
+
+ switch (hci_skb_pkt_type(skb)) {
+ case HCI_COMMAND_PKT:
+ hdev->stat.cmd_tx++;
+ break;
+ case HCI_ACLDATA_PKT:
+ hdev->stat.acl_tx++;
+ break;
+ case HCI_SCODATA_PKT:
+ hdev->stat.sco_tx++;
+ break;
+ }
+
+ if (skb_headroom(skb) < RSI_HEADROOM_FOR_BT_HAL) {
+ /* Insufficient skb headroom - allocate a new skb */
+ new_skb = skb_realloc_headroom(skb, RSI_HEADROOM_FOR_BT_HAL);
+ if (unlikely(!new_skb))
+ return -ENOMEM;
+ bt_cb(new_skb)->pkt_type = hci_skb_pkt_type(skb);
+ kfree_skb(skb);
+ skb = new_skb;
+ }
+
+ return h_adapter->proto_ops->coex_send_pkt(h_adapter->priv, skb,
+ RSI_BT_Q);
+}
+
+static int rsi_hci_recv_pkt(void *priv, const u8 *pkt)
+{
+ struct rsi_hci_adapter *h_adapter = priv;
+ struct hci_dev *hdev = h_adapter->hdev;
+ struct sk_buff *skb;
+ int pkt_len = get_unaligned_le16(pkt) & 0x0fff;
+
+ skb = dev_alloc_skb(pkt_len);
+ if (!skb)
+ return -ENOMEM;
+
+ memcpy(skb->data, pkt + RSI_FRAME_DESC_SIZE, pkt_len);
+ skb_put(skb, pkt_len);
+ h_adapter->hdev->stat.byte_rx += skb->len;
+
+ hci_skb_pkt_type(skb) = pkt[14];
+
+ return hci_recv_frame(hdev, skb);
+}
+
+static int rsi_hci_attach(void *priv, struct rsi_proto_ops *ops)
+{
+ struct rsi_hci_adapter *h_adapter = NULL;
+ struct hci_dev *hdev;
+ int err = 0;
+
+ h_adapter = kzalloc(sizeof(*h_adapter), GFP_KERNEL);
+ if (!h_adapter)
+ return -ENOMEM;
+
+ h_adapter->priv = priv;
+ ops->set_bt_context(priv, h_adapter);
+ h_adapter->proto_ops = ops;
+
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ BT_ERR("Failed to alloc HCI device");
+ goto err;
+ }
+
+ h_adapter->hdev = hdev;
+
+ if (ops->get_host_intf(priv) == RSI_HOST_INTF_SDIO)
+ hdev->bus = HCI_SDIO;
+ else
+ hdev->bus = HCI_USB;
+
+ hci_set_drvdata(hdev, h_adapter);
+ hdev->dev_type = HCI_PRIMARY;
+ hdev->open = rsi_hci_open;
+ hdev->close = rsi_hci_close;
+ hdev->flush = rsi_hci_flush;
+ hdev->send = rsi_hci_send_pkt;
+
+ err = hci_register_dev(hdev);
+ if (err < 0) {
+ BT_ERR("HCI registration failed with errcode %d", err);
+ hci_free_dev(hdev);
+ goto err;
+ }
+
+ return 0;
+err:
+ h_adapter->hdev = NULL;
+ kfree(h_adapter);
+ return -EINVAL;
+}
+
+static void rsi_hci_detach(void *priv)
+{
+ struct rsi_hci_adapter *h_adapter = priv;
+ struct hci_dev *hdev;
+
+ if (!h_adapter)
+ return;
+
+ hdev = h_adapter->hdev;
+ if (hdev) {
+ hci_unregister_dev(hdev);
+ hci_free_dev(hdev);
+ h_adapter->hdev = NULL;
+ }
+
+ kfree(h_adapter);
+}
+
+const struct rsi_mod_ops rsi_bt_ops = {
+ .attach = rsi_hci_attach,
+ .detach = rsi_hci_detach,
+ .recv_pkt = rsi_hci_recv_pkt,
+};
+EXPORT_SYMBOL(rsi_bt_ops);
+
+static int rsi_91x_bt_module_init(void)
+{
+ return 0;
+}
+
+static void rsi_91x_bt_module_exit(void)
+{
+ return;
+}
+
+module_init(rsi_91x_bt_module_init);
+module_exit(rsi_91x_bt_module_exit);
+MODULE_AUTHOR("Redpine Signals Inc");
+MODULE_DESCRIPTION("RSI BT driver");
+MODULE_SUPPORTED_DEVICE("RSI-BT");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c
index 6e2ad748abba..437f080deaab 100644
--- a/drivers/bluetooth/btrtl.c
+++ b/drivers/bluetooth/btrtl.c
@@ -35,6 +35,60 @@
#define RTL_ROM_LMP_8761A 0x8761
#define RTL_ROM_LMP_8822B 0x8822
+#define IC_MATCH_FL_LMPSUBV (1 << 0)
+#define IC_MATCH_FL_HCIREV (1 << 1)
+#define IC_INFO(lmps, hcir) \
+ .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV, \
+ .lmp_subver = (lmps), \
+ .hci_rev = (hcir)
+
+struct id_table {
+ __u16 match_flags;
+ __u16 lmp_subver;
+ __u16 hci_rev;
+ bool config_needed;
+ char *fw_name;
+ char *cfg_name;
+};
+
+static const struct id_table ic_id_table[] = {
+ /* 8723B */
+ { IC_INFO(RTL_ROM_LMP_8723B, 0xb),
+ .config_needed = false,
+ .fw_name = "rtl_bt/rtl8723b_fw.bin",
+ .cfg_name = "rtl_bt/rtl8723b_config.bin" },
+
+ /* 8723D */
+ { IC_INFO(RTL_ROM_LMP_8723B, 0xd),
+ .config_needed = true,
+ .fw_name = "rtl_bt/rtl8723d_fw.bin",
+ .cfg_name = "rtl_bt/rtl8723d_config.bin" },
+
+ /* 8821A */
+ { IC_INFO(RTL_ROM_LMP_8821A, 0xa),
+ .config_needed = false,
+ .fw_name = "rtl_bt/rtl8821a_fw.bin",
+ .cfg_name = "rtl_bt/rtl8821a_config.bin" },
+
+ /* 8821C */
+ { IC_INFO(RTL_ROM_LMP_8821A, 0xc),
+ .config_needed = false,
+ .fw_name = "rtl_bt/rtl8821c_fw.bin",
+ .cfg_name = "rtl_bt/rtl8821c_config.bin" },
+
+ /* 8761A */
+ { IC_MATCH_FL_LMPSUBV, RTL_ROM_LMP_8761A, 0x0,
+ .config_needed = false,
+ .fw_name = "rtl_bt/rtl8761a_fw.bin",
+ .cfg_name = "rtl_bt/rtl8761a_config.bin" },
+
+ /* 8822B */
+ { IC_INFO(RTL_ROM_LMP_8822B, 0xb),
+ .config_needed = true,
+ .fw_name = "rtl_bt/rtl8822b_fw.bin",
+ .cfg_name = "rtl_bt/rtl8822b_config.bin" },
+ };
+
static int rtl_read_rom_version(struct hci_dev *hdev, u8 *version)
{
struct rtl_rom_version_evt *rom_version;
@@ -64,9 +118,9 @@ static int rtl_read_rom_version(struct hci_dev *hdev, u8 *version)
return 0;
}
-static int rtl8723b_parse_firmware(struct hci_dev *hdev, u16 lmp_subver,
- const struct firmware *fw,
- unsigned char **_buf)
+static int rtlbt_parse_firmware(struct hci_dev *hdev, u16 lmp_subver,
+ const struct firmware *fw,
+ unsigned char **_buf)
{
const u8 extension_sig[] = { 0x51, 0x04, 0xfd, 0x77 };
struct rtl_epatch_header *epatch_info;
@@ -88,6 +142,8 @@ static int rtl8723b_parse_firmware(struct hci_dev *hdev, u16 lmp_subver,
{ RTL_ROM_LMP_8821A, 2 },
{ RTL_ROM_LMP_8761A, 3 },
{ RTL_ROM_LMP_8822B, 8 },
+ { RTL_ROM_LMP_8723B, 9 }, /* 8723D */
+ { RTL_ROM_LMP_8821A, 10 }, /* 8821C */
};
ret = rtl_read_rom_version(hdev, &rom_version);
@@ -320,8 +376,8 @@ out:
return ret;
}
-static int btrtl_setup_rtl8723b(struct hci_dev *hdev, u16 lmp_subver,
- const char *fw_name)
+static int btrtl_setup_rtl8723b(struct hci_dev *hdev, u16 hci_rev,
+ u16 lmp_subver)
{
unsigned char *fw_data = NULL;
const struct firmware *fw;
@@ -330,39 +386,40 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev, u16 lmp_subver,
u8 *cfg_buff = NULL;
u8 *tbuff;
char *cfg_name = NULL;
- bool config_needed = false;
+ char *fw_name = NULL;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ic_id_table); i++) {
+ if ((ic_id_table[i].match_flags & IC_MATCH_FL_LMPSUBV) &&
+ (ic_id_table[i].lmp_subver != lmp_subver))
+ continue;
+ if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIREV) &&
+ (ic_id_table[i].hci_rev != hci_rev))
+ continue;
- switch (lmp_subver) {
- case RTL_ROM_LMP_8723B:
- cfg_name = "rtl_bt/rtl8723b_config.bin";
- break;
- case RTL_ROM_LMP_8821A:
- cfg_name = "rtl_bt/rtl8821a_config.bin";
- break;
- case RTL_ROM_LMP_8761A:
- cfg_name = "rtl_bt/rtl8761a_config.bin";
- break;
- case RTL_ROM_LMP_8822B:
- cfg_name = "rtl_bt/rtl8822b_config.bin";
- config_needed = true;
- break;
- default:
- BT_ERR("%s: rtl: no config according to lmp_subver %04x",
- hdev->name, lmp_subver);
break;
}
+ if (i >= ARRAY_SIZE(ic_id_table)) {
+ BT_ERR("%s: unknown IC info, lmp subver %04x, hci rev %04x",
+ hdev->name, lmp_subver, hci_rev);
+ return -EINVAL;
+ }
+
+ cfg_name = ic_id_table[i].cfg_name;
+
if (cfg_name) {
cfg_sz = rtl_load_config(hdev, cfg_name, &cfg_buff);
if (cfg_sz < 0) {
cfg_sz = 0;
- if (config_needed)
+ if (ic_id_table[i].config_needed)
BT_ERR("Necessary config file %s not found\n",
cfg_name);
}
} else
cfg_sz = 0;
+ fw_name = ic_id_table[i].fw_name;
bt_dev_info(hdev, "rtl: loading %s", fw_name);
ret = request_firmware(&fw, fw_name, &hdev->dev);
if (ret < 0) {
@@ -370,7 +427,7 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev, u16 lmp_subver,
goto err_req_fw;
}
- ret = rtl8723b_parse_firmware(hdev, lmp_subver, fw, &fw_data);
+ ret = rtlbt_parse_firmware(hdev, lmp_subver, fw, &fw_data);
if (ret < 0)
goto out;
@@ -429,7 +486,7 @@ int btrtl_setup_realtek(struct hci_dev *hdev)
{
struct sk_buff *skb;
struct hci_rp_read_local_version *resp;
- u16 lmp_subver;
+ u16 hci_rev, lmp_subver;
skb = btrtl_read_local_version(hdev);
if (IS_ERR(skb))
@@ -441,6 +498,7 @@ int btrtl_setup_realtek(struct hci_dev *hdev)
resp->hci_ver, resp->hci_rev,
resp->lmp_ver, resp->lmp_subver);
+ hci_rev = le16_to_cpu(resp->hci_rev);
lmp_subver = le16_to_cpu(resp->lmp_subver);
kfree_skb(skb);
@@ -455,17 +513,10 @@ int btrtl_setup_realtek(struct hci_dev *hdev)
case RTL_ROM_LMP_3499:
return btrtl_setup_rtl8723a(hdev);
case RTL_ROM_LMP_8723B:
- return btrtl_setup_rtl8723b(hdev, lmp_subver,
- "rtl_bt/rtl8723b_fw.bin");
case RTL_ROM_LMP_8821A:
- return btrtl_setup_rtl8723b(hdev, lmp_subver,
- "rtl_bt/rtl8821a_fw.bin");
case RTL_ROM_LMP_8761A:
- return btrtl_setup_rtl8723b(hdev, lmp_subver,
- "rtl_bt/rtl8761a_fw.bin");
case RTL_ROM_LMP_8822B:
- return btrtl_setup_rtl8723b(hdev, lmp_subver,
- "rtl_bt/rtl8822b_fw.bin");
+ return btrtl_setup_rtl8723b(hdev, hci_rev, lmp_subver);
default:
bt_dev_info(hdev, "rtl: assuming no firmware upload needed");
return 0;
diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c
deleted file mode 100644
index 310e9c2e09b6..000000000000
--- a/drivers/bluetooth/btuart_cs.c
+++ /dev/null
@@ -1,675 +0,0 @@
-/*
- *
- * Driver for Bluetooth PCMCIA cards with HCI UART interface
- *
- * Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org>
- *
- *
- * 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;
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * The initial developer of the original code is David A. Hinds
- * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
- * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
- *
- */
-
-#include <linux/module.h>
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/types.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/spinlock.h>
-#include <linux/moduleparam.h>
-
-#include <linux/skbuff.h>
-#include <linux/string.h>
-#include <linux/serial.h>
-#include <linux/serial_reg.h>
-#include <linux/bitops.h>
-#include <linux/io.h>
-
-#include <pcmcia/cistpl.h>
-#include <pcmcia/ciscode.h>
-#include <pcmcia/ds.h>
-#include <pcmcia/cisreg.h>
-
-#include <net/bluetooth/bluetooth.h>
-#include <net/bluetooth/hci_core.h>
-
-
-
-/* ======================== Module parameters ======================== */
-
-
-MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
-MODULE_DESCRIPTION("Bluetooth driver for Bluetooth PCMCIA cards with HCI UART interface");
-MODULE_LICENSE("GPL");
-
-
-
-/* ======================== Local structures ======================== */
-
-
-struct btuart_info {
- struct pcmcia_device *p_dev;
-
- struct hci_dev *hdev;
-
- spinlock_t lock; /* For serializing operations */
-
- struct sk_buff_head txq;
- unsigned long tx_state;
-
- unsigned long rx_state;
- unsigned long rx_count;
- struct sk_buff *rx_skb;
-};
-
-
-static int btuart_config(struct pcmcia_device *link);
-static void btuart_release(struct pcmcia_device *link);
-
-static void btuart_detach(struct pcmcia_device *p_dev);
-
-
-/* Maximum baud rate */
-#define SPEED_MAX 115200
-
-/* Default baud rate: 57600, 115200, 230400 or 460800 */
-#define DEFAULT_BAUD_RATE 115200
-
-
-/* Transmit states */
-#define XMIT_SENDING 1
-#define XMIT_WAKEUP 2
-#define XMIT_WAITING 8
-
-/* Receiver states */
-#define RECV_WAIT_PACKET_TYPE 0
-#define RECV_WAIT_EVENT_HEADER 1
-#define RECV_WAIT_ACL_HEADER 2
-#define RECV_WAIT_SCO_HEADER 3
-#define RECV_WAIT_DATA 4
-
-
-
-/* ======================== Interrupt handling ======================== */
-
-
-static int btuart_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
-{
- int actual = 0;
-
- /* Tx FIFO should be empty */
- if (!(inb(iobase + UART_LSR) & UART_LSR_THRE))
- return 0;
-
- /* Fill FIFO with current frame */
- while ((fifo_size-- > 0) && (actual < len)) {
- /* Transmit next byte */
- outb(buf[actual], iobase + UART_TX);
- actual++;
- }
-
- return actual;
-}
-
-
-static void btuart_write_wakeup(struct btuart_info *info)
-{
- if (!info) {
- BT_ERR("Unknown device");
- return;
- }
-
- if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) {
- set_bit(XMIT_WAKEUP, &(info->tx_state));
- return;
- }
-
- do {
- unsigned int iobase = info->p_dev->resource[0]->start;
- register struct sk_buff *skb;
- int len;
-
- clear_bit(XMIT_WAKEUP, &(info->tx_state));
-
- if (!pcmcia_dev_present(info->p_dev))
- return;
-
- skb = skb_dequeue(&(info->txq));
- if (!skb)
- break;
-
- /* Send frame */
- len = btuart_write(iobase, 16, skb->data, skb->len);
- set_bit(XMIT_WAKEUP, &(info->tx_state));
-
- if (len == skb->len) {
- kfree_skb(skb);
- } else {
- skb_pull(skb, len);
- skb_queue_head(&(info->txq), skb);
- }
-
- info->hdev->stat.byte_tx += len;
-
- } while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
-
- clear_bit(XMIT_SENDING, &(info->tx_state));
-}
-
-
-static void btuart_receive(struct btuart_info *info)
-{
- unsigned int iobase;
- int boguscount = 0;
-
- if (!info) {
- BT_ERR("Unknown device");
- return;
- }
-
- iobase = info->p_dev->resource[0]->start;
-
- do {
- info->hdev->stat.byte_rx++;
-
- /* Allocate packet */
- if (!info->rx_skb) {
- info->rx_state = RECV_WAIT_PACKET_TYPE;
- info->rx_count = 0;
- info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
- if (!info->rx_skb) {
- BT_ERR("Can't allocate mem for new packet");
- return;
- }
- }
-
- if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
-
- hci_skb_pkt_type(info->rx_skb) = inb(iobase + UART_RX);
-
- switch (hci_skb_pkt_type(info->rx_skb)) {
-
- case HCI_EVENT_PKT:
- info->rx_state = RECV_WAIT_EVENT_HEADER;
- info->rx_count = HCI_EVENT_HDR_SIZE;
- break;
-
- case HCI_ACLDATA_PKT:
- info->rx_state = RECV_WAIT_ACL_HEADER;
- info->rx_count = HCI_ACL_HDR_SIZE;
- break;
-
- case HCI_SCODATA_PKT:
- info->rx_state = RECV_WAIT_SCO_HEADER;
- info->rx_count = HCI_SCO_HDR_SIZE;
- break;
-
- default:
- /* Unknown packet */
- BT_ERR("Unknown HCI packet with type 0x%02x received",
- hci_skb_pkt_type(info->rx_skb));
- info->hdev->stat.err_rx++;
-
- kfree_skb(info->rx_skb);
- info->rx_skb = NULL;
- break;
-
- }
-
- } else {
-
- skb_put_u8(info->rx_skb, inb(iobase + UART_RX));
- info->rx_count--;
-
- if (info->rx_count == 0) {
-
- int dlen;
- struct hci_event_hdr *eh;
- struct hci_acl_hdr *ah;
- struct hci_sco_hdr *sh;
-
-
- switch (info->rx_state) {
-
- case RECV_WAIT_EVENT_HEADER:
- eh = hci_event_hdr(info->rx_skb);
- info->rx_state = RECV_WAIT_DATA;
- info->rx_count = eh->plen;
- break;
-
- case RECV_WAIT_ACL_HEADER:
- ah = hci_acl_hdr(info->rx_skb);
- dlen = __le16_to_cpu(ah->dlen);
- info->rx_state = RECV_WAIT_DATA;
- info->rx_count = dlen;
- break;
-
- case RECV_WAIT_SCO_HEADER:
- sh = hci_sco_hdr(info->rx_skb);
- info->rx_state = RECV_WAIT_DATA;
- info->rx_count = sh->dlen;
- break;
-
- case RECV_WAIT_DATA:
- hci_recv_frame(info->hdev, info->rx_skb);
- info->rx_skb = NULL;
- break;
-
- }
-
- }
-
- }
-
- /* Make sure we don't stay here too long */
- if (boguscount++ > 16)
- break;
-
- } while (inb(iobase + UART_LSR) & UART_LSR_DR);
-}
-
-
-static irqreturn_t btuart_interrupt(int irq, void *dev_inst)
-{
- struct btuart_info *info = dev_inst;
- unsigned int iobase;
- int boguscount = 0;
- int iir, lsr;
- irqreturn_t r = IRQ_NONE;
-
- if (!info || !info->hdev)
- /* our irq handler is shared */
- return IRQ_NONE;
-
- iobase = info->p_dev->resource[0]->start;
-
- spin_lock(&(info->lock));
-
- iir = inb(iobase + UART_IIR) & UART_IIR_ID;
- while (iir) {
- r = IRQ_HANDLED;
-
- /* Clear interrupt */
- lsr = inb(iobase + UART_LSR);
-
- switch (iir) {
- case UART_IIR_RLSI:
- BT_ERR("RLSI");
- break;
- case UART_IIR_RDI:
- /* Receive interrupt */
- btuart_receive(info);
- break;
- case UART_IIR_THRI:
- if (lsr & UART_LSR_THRE) {
- /* Transmitter ready for data */
- btuart_write_wakeup(info);
- }
- break;
- default:
- BT_ERR("Unhandled IIR=%#x", iir);
- break;
- }
-
- /* Make sure we don't stay here too long */
- if (boguscount++ > 100)
- break;
-
- iir = inb(iobase + UART_IIR) & UART_IIR_ID;
-
- }
-
- spin_unlock(&(info->lock));
-
- return r;
-}
-
-
-static void btuart_change_speed(struct btuart_info *info,
- unsigned int speed)
-{
- unsigned long flags;
- unsigned int iobase;
- int fcr; /* FIFO control reg */
- int lcr; /* Line control reg */
- int divisor;
-
- if (!info) {
- BT_ERR("Unknown device");
- return;
- }
-
- iobase = info->p_dev->resource[0]->start;
-
- spin_lock_irqsave(&(info->lock), flags);
-
- /* Turn off interrupts */
- outb(0, iobase + UART_IER);
-
- divisor = SPEED_MAX / speed;
-
- fcr = UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT;
-
- /*
- * Use trigger level 1 to avoid 3 ms. timeout delay at 9600 bps, and
- * almost 1,7 ms at 19200 bps. At speeds above that we can just forget
- * about this timeout since it will always be fast enough.
- */
-
- if (speed < 38400)
- fcr |= UART_FCR_TRIGGER_1;
- else
- fcr |= UART_FCR_TRIGGER_14;
-
- /* Bluetooth cards use 8N1 */
- lcr = UART_LCR_WLEN8;
-
- outb(UART_LCR_DLAB | lcr, iobase + UART_LCR); /* Set DLAB */
- outb(divisor & 0xff, iobase + UART_DLL); /* Set speed */
- outb(divisor >> 8, iobase + UART_DLM);
- outb(lcr, iobase + UART_LCR); /* Set 8N1 */
- outb(fcr, iobase + UART_FCR); /* Enable FIFO's */
-
- /* Turn on interrupts */
- outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);
-
- spin_unlock_irqrestore(&(info->lock), flags);
-}
-
-
-
-/* ======================== HCI interface ======================== */
-
-
-static int btuart_hci_flush(struct hci_dev *hdev)
-{
- struct btuart_info *info = hci_get_drvdata(hdev);
-
- /* Drop TX queue */
- skb_queue_purge(&(info->txq));
-
- return 0;
-}
-
-
-static int btuart_hci_open(struct hci_dev *hdev)
-{
- return 0;
-}
-
-
-static int btuart_hci_close(struct hci_dev *hdev)
-{
- btuart_hci_flush(hdev);
-
- return 0;
-}
-
-
-static int btuart_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
-{
- struct btuart_info *info = hci_get_drvdata(hdev);
-
- switch (hci_skb_pkt_type(skb)) {
- case HCI_COMMAND_PKT:
- hdev->stat.cmd_tx++;
- break;
- case HCI_ACLDATA_PKT:
- hdev->stat.acl_tx++;
- break;
- case HCI_SCODATA_PKT:
- hdev->stat.sco_tx++;
- break;
- }
-
- /* Prepend skb with frame type */
- memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
- skb_queue_tail(&(info->txq), skb);
-
- btuart_write_wakeup(info);
-
- return 0;
-}
-
-
-
-/* ======================== Card services HCI interaction ======================== */
-
-
-static int btuart_open(struct btuart_info *info)
-{
- unsigned long flags;
- unsigned int iobase = info->p_dev->resource[0]->start;
- struct hci_dev *hdev;
-
- spin_lock_init(&(info->lock));
-
- skb_queue_head_init(&(info->txq));
-
- info->rx_state = RECV_WAIT_PACKET_TYPE;
- info->rx_count = 0;
- info->rx_skb = NULL;
-
- /* Initialize HCI device */
- hdev = hci_alloc_dev();
- if (!hdev) {
- BT_ERR("Can't allocate HCI device");
- return -ENOMEM;
- }
-
- info->hdev = hdev;
-
- hdev->bus = HCI_PCCARD;
- hci_set_drvdata(hdev, info);
- SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
-
- hdev->open = btuart_hci_open;
- hdev->close = btuart_hci_close;
- hdev->flush = btuart_hci_flush;
- hdev->send = btuart_hci_send_frame;
-
- spin_lock_irqsave(&(info->lock), flags);
-
- /* Reset UART */
- outb(0, iobase + UART_MCR);
-
- /* Turn off interrupts */
- outb(0, iobase + UART_IER);
-
- /* Initialize UART */
- outb(UART_LCR_WLEN8, iobase + UART_LCR); /* Reset DLAB */
- outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase + UART_MCR);
-
- /* Turn on interrupts */
- // outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);
-
- spin_unlock_irqrestore(&(info->lock), flags);
-
- btuart_change_speed(info, DEFAULT_BAUD_RATE);
-
- /* Timeout before it is safe to send the first HCI packet */
- msleep(1000);
-
- /* Register HCI device */
- if (hci_register_dev(hdev) < 0) {
- BT_ERR("Can't register HCI device");
- info->hdev = NULL;
- hci_free_dev(hdev);
- return -ENODEV;
- }
-
- return 0;
-}
-
-
-static int btuart_close(struct btuart_info *info)
-{
- unsigned long flags;
- unsigned int iobase = info->p_dev->resource[0]->start;
- struct hci_dev *hdev = info->hdev;
-
- if (!hdev)
- return -ENODEV;
-
- btuart_hci_close(hdev);
-
- spin_lock_irqsave(&(info->lock), flags);
-
- /* Reset UART */
- outb(0, iobase + UART_MCR);
-
- /* Turn off interrupts */
- outb(0, iobase + UART_IER);
-
- spin_unlock_irqrestore(&(info->lock), flags);
-
- hci_unregister_dev(hdev);
- hci_free_dev(hdev);
-
- return 0;
-}
-
-static int btuart_probe(struct pcmcia_device *link)
-{
- struct btuart_info *info;
-
- /* Create new info device */
- info = devm_kzalloc(&link->dev, sizeof(*info), GFP_KERNEL);
- if (!info)
- return -ENOMEM;
-
- info->p_dev = link;
- link->priv = info;
-
- link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP |
- CONF_AUTO_SET_IO;
-
- return btuart_config(link);
-}
-
-
-static void btuart_detach(struct pcmcia_device *link)
-{
- btuart_release(link);
-}
-
-static int btuart_check_config(struct pcmcia_device *p_dev, void *priv_data)
-{
- int *try = priv_data;
-
- if (!try)
- p_dev->io_lines = 16;
-
- if ((p_dev->resource[0]->end != 8) || (p_dev->resource[0]->start == 0))
- return -EINVAL;
-
- p_dev->resource[0]->end = 8;
- p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
- p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
-
- return pcmcia_request_io(p_dev);
-}
-
-static int btuart_check_config_notpicky(struct pcmcia_device *p_dev,
- void *priv_data)
-{
- static unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
- int j;
-
- if (p_dev->io_lines > 3)
- return -ENODEV;
-
- p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
- p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
- p_dev->resource[0]->end = 8;
-
- for (j = 0; j < 5; j++) {
- p_dev->resource[0]->start = base[j];
- p_dev->io_lines = base[j] ? 16 : 3;
- if (!pcmcia_request_io(p_dev))
- return 0;
- }
- return -ENODEV;
-}
-
-static int btuart_config(struct pcmcia_device *link)
-{
- struct btuart_info *info = link->priv;
- int i;
- int try;
-
- /* First pass: look for a config entry that looks normal.
- * Two tries: without IO aliases, then with aliases
- */
- for (try = 0; try < 2; try++)
- if (!pcmcia_loop_config(link, btuart_check_config, &try))
- goto found_port;
-
- /* Second pass: try to find an entry that isn't picky about
- * its base address, then try to grab any standard serial port
- * address, and finally try to get any free port.
- */
- if (!pcmcia_loop_config(link, btuart_check_config_notpicky, NULL))
- goto found_port;
-
- BT_ERR("No usable port range found");
- goto failed;
-
-found_port:
- i = pcmcia_request_irq(link, btuart_interrupt);
- if (i != 0)
- goto failed;
-
- i = pcmcia_enable_device(link);
- if (i != 0)
- goto failed;
-
- if (btuart_open(info) != 0)
- goto failed;
-
- return 0;
-
-failed:
- btuart_release(link);
- return -ENODEV;
-}
-
-
-static void btuart_release(struct pcmcia_device *link)
-{
- struct btuart_info *info = link->priv;
-
- btuart_close(info);
-
- pcmcia_disable_device(link);
-}
-
-static const struct pcmcia_device_id btuart_ids[] = {
- /* don't use this driver. Use serial_cs + hci_uart instead */
- PCMCIA_DEVICE_NULL
-};
-MODULE_DEVICE_TABLE(pcmcia, btuart_ids);
-
-static struct pcmcia_driver btuart_driver = {
- .owner = THIS_MODULE,
- .name = "btuart_cs",
- .probe = btuart_probe,
- .remove = btuart_detach,
- .id_table = btuart_ids,
-};
-module_pcmcia_driver(btuart_driver);
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 366a49c7c08f..c8c8b0b8d333 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -340,6 +340,7 @@ static const struct usb_device_id blacklist_table[] = {
/* Intel Bluetooth devices */
{ USB_DEVICE(0x8087, 0x0025), .driver_info = BTUSB_INTEL_NEW },
+ { USB_DEVICE(0x8087, 0x0026), .driver_info = BTUSB_INTEL_NEW },
{ USB_DEVICE(0x8087, 0x07da), .driver_info = BTUSB_CSR },
{ USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL },
{ USB_DEVICE(0x8087, 0x0a2a), .driver_info = BTUSB_INTEL },
@@ -367,6 +368,9 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x13d3, 0x3459), .driver_info = BTUSB_REALTEK },
{ USB_DEVICE(0x13d3, 0x3494), .driver_info = BTUSB_REALTEK },
+ /* Additional Realtek 8723BU Bluetooth devices */
+ { USB_DEVICE(0x7392, 0xa611), .driver_info = BTUSB_REALTEK },
+
/* Additional Realtek 8821AE Bluetooth devices */
{ USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK },
{ USB_DEVICE(0x13d3, 0x3414), .driver_info = BTUSB_REALTEK },
@@ -374,6 +378,9 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x13d3, 0x3461), .driver_info = BTUSB_REALTEK },
{ USB_DEVICE(0x13d3, 0x3462), .driver_info = BTUSB_REALTEK },
+ /* Additional Realtek 8822BE Bluetooth devices */
+ { USB_DEVICE(0x0b05, 0x185c), .driver_info = BTUSB_REALTEK },
+
/* Silicon Wave based devices */
{ USB_DEVICE(0x0c10, 0x0000), .driver_info = BTUSB_SWAVE },
@@ -2073,6 +2080,8 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
case 0x0c: /* WsP */
case 0x11: /* JfP */
case 0x12: /* ThP */
+ case 0x13: /* HrP */
+ case 0x14: /* QnJ, IcP */
break;
default:
BT_ERR("%s: Unsupported Intel hardware variant (%u)",
@@ -2165,6 +2174,8 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
break;
case 0x11: /* JfP */
case 0x12: /* ThP */
+ case 0x13: /* HrP */
+ case 0x14: /* QnJ, IcP */
snprintf(fwname, sizeof(fwname), "intel/ibt-%u-%u-%u.sfi",
le16_to_cpu(ver.hw_variant),
le16_to_cpu(ver.hw_revision),
@@ -2196,6 +2207,8 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
break;
case 0x11: /* JfP */
case 0x12: /* ThP */
+ case 0x13: /* HrP */
+ case 0x14: /* QnJ, IcP */
snprintf(fwname, sizeof(fwname), "intel/ibt-%u-%u-%u.ddc",
le16_to_cpu(ver.hw_variant),
le16_to_cpu(ver.hw_revision),
@@ -3050,6 +3063,7 @@ static int btusb_probe(struct usb_interface *intf,
if (id->driver_info & BTUSB_QCA_ROME) {
data->setup_on_usb = btusb_setup_qca;
hdev->set_bdaddr = btusb_set_bdaddr_ath3012;
+ set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
}
#ifdef CONFIG_BT_HCIBTUSB_RTL
diff --git a/drivers/bluetooth/h4_recv.h b/drivers/bluetooth/h4_recv.h
new file mode 100644
index 000000000000..b432651f8236
--- /dev/null
+++ b/drivers/bluetooth/h4_recv.h
@@ -0,0 +1,160 @@
+/*
+ *
+ * Generic Bluetooth HCI UART driver
+ *
+ * Copyright (C) 2015-2018 Intel Corporation
+ *
+ *
+ * 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 <asm/unaligned.h>
+
+struct h4_recv_pkt {
+ u8 type; /* Packet type */
+ u8 hlen; /* Header length */
+ u8 loff; /* Data length offset in header */
+ u8 lsize; /* Data length field size */
+ u16 maxlen; /* Max overall packet length */
+ int (*recv)(struct hci_dev *hdev, struct sk_buff *skb);
+};
+
+#define H4_RECV_ACL \
+ .type = HCI_ACLDATA_PKT, \
+ .hlen = HCI_ACL_HDR_SIZE, \
+ .loff = 2, \
+ .lsize = 2, \
+ .maxlen = HCI_MAX_FRAME_SIZE \
+
+#define H4_RECV_SCO \
+ .type = HCI_SCODATA_PKT, \
+ .hlen = HCI_SCO_HDR_SIZE, \
+ .loff = 2, \
+ .lsize = 1, \
+ .maxlen = HCI_MAX_SCO_SIZE
+
+#define H4_RECV_EVENT \
+ .type = HCI_EVENT_PKT, \
+ .hlen = HCI_EVENT_HDR_SIZE, \
+ .loff = 1, \
+ .lsize = 1, \
+ .maxlen = HCI_MAX_EVENT_SIZE
+
+static inline struct sk_buff *h4_recv_buf(struct hci_dev *hdev,
+ struct sk_buff *skb,
+ const unsigned char *buffer,
+ int count,
+ const struct h4_recv_pkt *pkts,
+ int pkts_count)
+{
+ while (count) {
+ int i, len;
+
+ if (!count)
+ break;
+
+ if (!skb) {
+ for (i = 0; i < pkts_count; i++) {
+ if (buffer[0] != (&pkts[i])->type)
+ continue;
+
+ skb = bt_skb_alloc((&pkts[i])->maxlen,
+ GFP_ATOMIC);
+ if (!skb)
+ return ERR_PTR(-ENOMEM);
+
+ hci_skb_pkt_type(skb) = (&pkts[i])->type;
+ hci_skb_expect(skb) = (&pkts[i])->hlen;
+ break;
+ }
+
+ /* Check for invalid packet type */
+ if (!skb)
+ return ERR_PTR(-EILSEQ);
+
+ count -= 1;
+ buffer += 1;
+ }
+
+ len = min_t(uint, hci_skb_expect(skb) - skb->len, count);
+ skb_put_data(skb, buffer, len);
+
+ count -= len;
+ buffer += len;
+
+ /* Check for partial packet */
+ if (skb->len < hci_skb_expect(skb))
+ continue;
+
+ for (i = 0; i < pkts_count; i++) {
+ if (hci_skb_pkt_type(skb) == (&pkts[i])->type)
+ break;
+ }
+
+ if (i >= pkts_count) {
+ kfree_skb(skb);
+ return ERR_PTR(-EILSEQ);
+ }
+
+ if (skb->len == (&pkts[i])->hlen) {
+ u16 dlen;
+
+ switch ((&pkts[i])->lsize) {
+ case 0:
+ /* No variable data length */
+ dlen = 0;
+ break;
+ case 1:
+ /* Single octet variable length */
+ dlen = skb->data[(&pkts[i])->loff];
+ hci_skb_expect(skb) += dlen;
+
+ if (skb_tailroom(skb) < dlen) {
+ kfree_skb(skb);
+ return ERR_PTR(-EMSGSIZE);
+ }
+ break;
+ case 2:
+ /* Double octet variable length */
+ dlen = get_unaligned_le16(skb->data +
+ (&pkts[i])->loff);
+ hci_skb_expect(skb) += dlen;
+
+ if (skb_tailroom(skb) < dlen) {
+ kfree_skb(skb);
+ return ERR_PTR(-EMSGSIZE);
+ }
+ break;
+ default:
+ /* Unsupported variable length */
+ kfree_skb(skb);
+ return ERR_PTR(-EILSEQ);
+ }
+
+ if (!dlen) {
+ /* No more data, complete frame */
+ (&pkts[i])->recv(hdev, skb);
+ skb = NULL;
+ }
+ } else {
+ /* Complete frame */
+ (&pkts[i])->recv(hdev, skb);
+ skb = NULL;
+ }
+ }
+
+ return skb;
+}
diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c
index 14ae7ee88acb..d568fbd94d6c 100644
--- a/drivers/bluetooth/hci_ath.c
+++ b/drivers/bluetooth/hci_ath.c
@@ -71,12 +71,12 @@ static int ath_wakeup_ar3k(struct tty_struct *tty)
/* Clear RTS first */
tty->driver->ops->tiocmget(tty);
tty->driver->ops->tiocmset(tty, 0x00, TIOCM_RTS);
- mdelay(20);
+ msleep(20);
/* Set RTS, wake up board */
tty->driver->ops->tiocmget(tty);
tty->driver->ops->tiocmset(tty, TIOCM_RTS, 0x00);
- mdelay(20);
+ msleep(20);
status = tty->driver->ops->tiocmget(tty);
return status;
diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c
index 40b9fb247010..441f5e1deb11 100644
--- a/drivers/bluetooth/hci_bcm.c
+++ b/drivers/bluetooth/hci_bcm.c
@@ -98,6 +98,8 @@ struct bcm_device {
int (*set_shutdown)(struct bcm_device *, bool);
#ifdef CONFIG_ACPI
acpi_handle btlp, btpu, btpd;
+ int gpio_count;
+ int gpio_int_idx;
#endif
struct clk *clk;
@@ -126,6 +128,10 @@ struct bcm_data {
static DEFINE_MUTEX(bcm_device_lock);
static LIST_HEAD(bcm_device_list);
+static int irq_polarity = -1;
+module_param(irq_polarity, int, 0444);
+MODULE_PARM_DESC(irq_polarity, "IRQ polarity 0: active-high 1: active-low");
+
static inline void host_set_baudrate(struct hci_uart *hu, unsigned int speed)
{
if (hu->serdev)
@@ -770,47 +776,27 @@ unlock:
}
#endif
-static const struct acpi_gpio_params int_last_device_wakeup_gpios = { 0, 0, false };
-static const struct acpi_gpio_params int_last_shutdown_gpios = { 1, 0, false };
-static const struct acpi_gpio_params int_last_host_wakeup_gpios = { 2, 0, false };
+static const struct acpi_gpio_params first_gpio = { 0, 0, false };
+static const struct acpi_gpio_params second_gpio = { 1, 0, false };
+static const struct acpi_gpio_params third_gpio = { 2, 0, false };
static const struct acpi_gpio_mapping acpi_bcm_int_last_gpios[] = {
- { "device-wakeup-gpios", &int_last_device_wakeup_gpios, 1 },
- { "shutdown-gpios", &int_last_shutdown_gpios, 1 },
- { "host-wakeup-gpios", &int_last_host_wakeup_gpios, 1 },
+ { "device-wakeup-gpios", &first_gpio, 1 },
+ { "shutdown-gpios", &second_gpio, 1 },
+ { "host-wakeup-gpios", &third_gpio, 1 },
{ },
};
-static const struct acpi_gpio_params int_first_host_wakeup_gpios = { 0, 0, false };
-static const struct acpi_gpio_params int_first_device_wakeup_gpios = { 1, 0, false };
-static const struct acpi_gpio_params int_first_shutdown_gpios = { 2, 0, false };
-
static const struct acpi_gpio_mapping acpi_bcm_int_first_gpios[] = {
- { "device-wakeup-gpios", &int_first_device_wakeup_gpios, 1 },
- { "shutdown-gpios", &int_first_shutdown_gpios, 1 },
- { "host-wakeup-gpios", &int_first_host_wakeup_gpios, 1 },
+ { "host-wakeup-gpios", &first_gpio, 1 },
+ { "device-wakeup-gpios", &second_gpio, 1 },
+ { "shutdown-gpios", &third_gpio, 1 },
{ },
};
#ifdef CONFIG_ACPI
/* IRQ polarity of some chipsets are not defined correctly in ACPI table. */
static const struct dmi_system_id bcm_active_low_irq_dmi_table[] = {
- {
- .ident = "Asus T100TA",
- .matches = {
- DMI_EXACT_MATCH(DMI_SYS_VENDOR,
- "ASUSTeK COMPUTER INC."),
- DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TA"),
- },
- },
- {
- .ident = "Asus T100CHI",
- .matches = {
- DMI_EXACT_MATCH(DMI_SYS_VENDOR,
- "ASUSTeK COMPUTER INC."),
- DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100CHI"),
- },
- },
{ /* Handle ThinkPad 8 tablets with BCM2E55 chipset ACPI ID */
.ident = "Lenovo ThinkPad 8",
.matches = {
@@ -818,13 +804,6 @@ static const struct dmi_system_id bcm_active_low_irq_dmi_table[] = {
DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 8"),
},
},
- {
- .ident = "MINIX Z83-4",
- .matches = {
- DMI_EXACT_MATCH(DMI_SYS_VENDOR, "MINIX"),
- DMI_MATCH(DMI_PRODUCT_NAME, "Z83-4"),
- },
- },
{ }
};
@@ -838,13 +817,18 @@ static int bcm_resource(struct acpi_resource *ares, void *data)
switch (ares->type) {
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
irq = &ares->data.extended_irq;
- dev->irq_active_low = irq->polarity == ACPI_ACTIVE_LOW;
+ if (irq->polarity != ACPI_ACTIVE_LOW)
+ dev_info(dev->dev, "ACPI Interrupt resource is active-high, this is usually wrong, treating the IRQ as active-low\n");
+ dev->irq_active_low = true;
break;
case ACPI_RESOURCE_TYPE_GPIO:
gpio = &ares->data.gpio;
- if (gpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT)
+ if (gpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT) {
+ dev->gpio_int_idx = dev->gpio_count;
dev->irq_active_low = gpio->polarity == ACPI_ACTIVE_LOW;
+ }
+ dev->gpio_count++;
break;
case ACPI_RESOURCE_TYPE_SERIAL_BUS:
@@ -908,13 +892,13 @@ static inline int bcm_apple_get_resources(struct bcm_device *dev)
static int bcm_gpio_set_device_wakeup(struct bcm_device *dev, bool awake)
{
- gpiod_set_value(dev->device_wakeup, awake);
+ gpiod_set_value_cansleep(dev->device_wakeup, awake);
return 0;
}
static int bcm_gpio_set_shutdown(struct bcm_device *dev, bool powered)
{
- gpiod_set_value(dev->shutdown, powered);
+ gpiod_set_value_cansleep(dev->shutdown, powered);
return 0;
}
@@ -962,20 +946,11 @@ static int bcm_acpi_probe(struct bcm_device *dev)
LIST_HEAD(resources);
const struct dmi_system_id *dmi_id;
const struct acpi_gpio_mapping *gpio_mapping = acpi_bcm_int_last_gpios;
- const struct acpi_device_id *id;
struct resource_entry *entry;
int ret;
- /* Retrieve GPIO data */
- id = acpi_match_device(dev->dev->driver->acpi_match_table, dev->dev);
- if (id)
- gpio_mapping = (const struct acpi_gpio_mapping *) id->driver_data;
-
- ret = devm_acpi_dev_add_driver_gpios(dev->dev, gpio_mapping);
- if (ret)
- return ret;
-
/* Retrieve UART ACPI info */
+ dev->gpio_int_idx = -1;
ret = acpi_dev_get_resources(ACPI_COMPANION(dev->dev),
&resources, bcm_resource, dev);
if (ret < 0)
@@ -989,11 +964,40 @@ static int bcm_acpi_probe(struct bcm_device *dev)
}
acpi_dev_free_resource_list(&resources);
- dmi_id = dmi_first_match(bcm_active_low_irq_dmi_table);
- if (dmi_id) {
- dev_warn(dev->dev, "%s: Overwriting IRQ polarity to active low",
- dmi_id->ident);
- dev->irq_active_low = true;
+ /* If the DSDT uses an Interrupt resource for the IRQ, then there are
+ * only 2 GPIO resources, we use the irq-last mapping for this, since
+ * we already have an irq the 3th / last mapping will not be used.
+ */
+ if (dev->irq)
+ gpio_mapping = acpi_bcm_int_last_gpios;
+ else if (dev->gpio_int_idx == 0)
+ gpio_mapping = acpi_bcm_int_first_gpios;
+ else if (dev->gpio_int_idx == 2)
+ gpio_mapping = acpi_bcm_int_last_gpios;
+ else
+ dev_warn(dev->dev, "Unexpected ACPI gpio_int_idx: %d\n",
+ dev->gpio_int_idx);
+
+ /* Warn if our expectations are not met. */
+ if (dev->gpio_count != (dev->irq ? 2 : 3))
+ dev_warn(dev->dev, "Unexpected number of ACPI GPIOs: %d\n",
+ dev->gpio_count);
+
+ ret = devm_acpi_dev_add_driver_gpios(dev->dev, gpio_mapping);
+ if (ret)
+ return ret;
+
+ if (irq_polarity != -1) {
+ dev->irq_active_low = irq_polarity;
+ dev_warn(dev->dev, "Overwriting IRQ polarity to active %s by module-param\n",
+ dev->irq_active_low ? "low" : "high");
+ } else {
+ dmi_id = dmi_first_match(bcm_active_low_irq_dmi_table);
+ if (dmi_id) {
+ dev_warn(dev->dev, "%s: Overwriting IRQ polarity to active low",
+ dmi_id->ident);
+ dev->irq_active_low = true;
+ }
}
return 0;
@@ -1079,25 +1083,172 @@ static const struct hci_uart_proto bcm_proto = {
#ifdef CONFIG_ACPI
static const struct acpi_device_id bcm_acpi_match[] = {
- { "BCM2E1A", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
- { "BCM2E39", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
- { "BCM2E3A", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
- { "BCM2E3D", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
- { "BCM2E3F", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
- { "BCM2E40", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
- { "BCM2E54", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
- { "BCM2E55", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
- { "BCM2E64", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
- { "BCM2E65", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
- { "BCM2E67", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
- { "BCM2E71", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
- { "BCM2E72", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
- { "BCM2E7B", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
- { "BCM2E7C", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
- { "BCM2E7E", (kernel_ulong_t)&acpi_bcm_int_first_gpios },
- { "BCM2E95", (kernel_ulong_t)&acpi_bcm_int_first_gpios },
- { "BCM2E96", (kernel_ulong_t)&acpi_bcm_int_first_gpios },
- { "BCM2EA4", (kernel_ulong_t)&acpi_bcm_int_first_gpios },
+ { "BCM2E00" },
+ { "BCM2E01" },
+ { "BCM2E02" },
+ { "BCM2E03" },
+ { "BCM2E04" },
+ { "BCM2E05" },
+ { "BCM2E06" },
+ { "BCM2E07" },
+ { "BCM2E08" },
+ { "BCM2E09" },
+ { "BCM2E0A" },
+ { "BCM2E0B" },
+ { "BCM2E0C" },
+ { "BCM2E0D" },
+ { "BCM2E0E" },
+ { "BCM2E0F" },
+ { "BCM2E10" },
+ { "BCM2E11" },
+ { "BCM2E12" },
+ { "BCM2E13" },
+ { "BCM2E14" },
+ { "BCM2E15" },
+ { "BCM2E16" },
+ { "BCM2E17" },
+ { "BCM2E18" },
+ { "BCM2E19" },
+ { "BCM2E1A" },
+ { "BCM2E1B" },
+ { "BCM2E1C" },
+ { "BCM2E1D" },
+ { "BCM2E1F" },
+ { "BCM2E20" },
+ { "BCM2E21" },
+ { "BCM2E22" },
+ { "BCM2E23" },
+ { "BCM2E24" },
+ { "BCM2E25" },
+ { "BCM2E26" },
+ { "BCM2E27" },
+ { "BCM2E28" },
+ { "BCM2E29" },
+ { "BCM2E2A" },
+ { "BCM2E2B" },
+ { "BCM2E2C" },
+ { "BCM2E2D" },
+ { "BCM2E2E" },
+ { "BCM2E2F" },
+ { "BCM2E30" },
+ { "BCM2E31" },
+ { "BCM2E32" },
+ { "BCM2E33" },
+ { "BCM2E34" },
+ { "BCM2E35" },
+ { "BCM2E36" },
+ { "BCM2E37" },
+ { "BCM2E38" },
+ { "BCM2E39" },
+ { "BCM2E3A" },
+ { "BCM2E3B" },
+ { "BCM2E3C" },
+ { "BCM2E3D" },
+ { "BCM2E3E" },
+ { "BCM2E3F" },
+ { "BCM2E40" },
+ { "BCM2E41" },
+ { "BCM2E42" },
+ { "BCM2E43" },
+ { "BCM2E44" },
+ { "BCM2E45" },
+ { "BCM2E46" },
+ { "BCM2E47" },
+ { "BCM2E48" },
+ { "BCM2E49" },
+ { "BCM2E4A" },
+ { "BCM2E4B" },
+ { "BCM2E4C" },
+ { "BCM2E4D" },
+ { "BCM2E4E" },
+ { "BCM2E4F" },
+ { "BCM2E50" },
+ { "BCM2E51" },
+ { "BCM2E52" },
+ { "BCM2E53" },
+ { "BCM2E54" },
+ { "BCM2E55" },
+ { "BCM2E56" },
+ { "BCM2E57" },
+ { "BCM2E58" },
+ { "BCM2E59" },
+ { "BCM2E5A" },
+ { "BCM2E5B" },
+ { "BCM2E5C" },
+ { "BCM2E5D" },
+ { "BCM2E5E" },
+ { "BCM2E5F" },
+ { "BCM2E60" },
+ { "BCM2E61" },
+ { "BCM2E62" },
+ { "BCM2E63" },
+ { "BCM2E64" },
+ { "BCM2E65" },
+ { "BCM2E66" },
+ { "BCM2E67" },
+ { "BCM2E68" },
+ { "BCM2E69" },
+ { "BCM2E6B" },
+ { "BCM2E6D" },
+ { "BCM2E6E" },
+ { "BCM2E6F" },
+ { "BCM2E70" },
+ { "BCM2E71" },
+ { "BCM2E72" },
+ { "BCM2E73" },
+ { "BCM2E74" },
+ { "BCM2E75" },
+ { "BCM2E76" },
+ { "BCM2E77" },
+ { "BCM2E78" },
+ { "BCM2E79" },
+ { "BCM2E7A" },
+ { "BCM2E7B" },
+ { "BCM2E7C" },
+ { "BCM2E7D" },
+ { "BCM2E7E" },
+ { "BCM2E7F" },
+ { "BCM2E80" },
+ { "BCM2E81" },
+ { "BCM2E82" },
+ { "BCM2E83" },
+ { "BCM2E84" },
+ { "BCM2E85" },
+ { "BCM2E86" },
+ { "BCM2E87" },
+ { "BCM2E88" },
+ { "BCM2E89" },
+ { "BCM2E8A" },
+ { "BCM2E8B" },
+ { "BCM2E8C" },
+ { "BCM2E8D" },
+ { "BCM2E8E" },
+ { "BCM2E90" },
+ { "BCM2E92" },
+ { "BCM2E93" },
+ { "BCM2E94" },
+ { "BCM2E95" },
+ { "BCM2E96" },
+ { "BCM2E97" },
+ { "BCM2E98" },
+ { "BCM2E99" },
+ { "BCM2E9A" },
+ { "BCM2E9B" },
+ { "BCM2E9C" },
+ { "BCM2E9D" },
+ { "BCM2EA0" },
+ { "BCM2EA1" },
+ { "BCM2EA2" },
+ { "BCM2EA3" },
+ { "BCM2EA4" },
+ { "BCM2EA5" },
+ { "BCM2EA6" },
+ { "BCM2EA7" },
+ { "BCM2EA8" },
+ { "BCM2EA9" },
+ { "BCM2EAA" },
+ { "BCM2EAB" },
+ { "BCM2EAC" },
{ },
};
MODULE_DEVICE_TABLE(acpi, bcm_acpi_match);
@@ -1146,6 +1297,12 @@ static int bcm_serdev_probe(struct serdev_device *serdev)
if (err)
return err;
+ if (!bcmdev->shutdown) {
+ dev_warn(&serdev->dev,
+ "No reset resource, using default baud rate\n");
+ bcmdev->oper_speed = bcmdev->init_speed;
+ }
+
err = bcm_gpio_set_power(bcmdev, false);
if (err)
dev_err(&serdev->dev, "Failed to power down\n");
diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c
index 1b4417a623a4..27e414b4e3a2 100644
--- a/drivers/bluetooth/hci_ll.c
+++ b/drivers/bluetooth/hci_ll.c
@@ -67,13 +67,6 @@
#define HCILL_WAKE_UP_IND 0x32
#define HCILL_WAKE_UP_ACK 0x33
-/* HCILL receiver States */
-#define HCILL_W4_PACKET_TYPE 0
-#define HCILL_W4_EVENT_HDR 1
-#define HCILL_W4_ACL_HDR 2
-#define HCILL_W4_SCO_HDR 3
-#define HCILL_W4_DATA 4
-
/* HCILL states */
enum hcill_states_e {
HCILL_ASLEEP,
@@ -82,10 +75,6 @@ enum hcill_states_e {
HCILL_AWAKE_TO_ASLEEP
};
-struct hcill_cmd {
- u8 cmd;
-} __packed;
-
struct ll_device {
struct hci_uart hu;
struct serdev_device *serdev;
@@ -95,8 +84,6 @@ struct ll_device {
};
struct ll_struct {
- unsigned long rx_state;
- unsigned long rx_count;
struct sk_buff *rx_skb;
struct sk_buff_head txq;
spinlock_t hcill_lock; /* HCILL state lock */
@@ -113,7 +100,6 @@ static int send_hcill_cmd(u8 cmd, struct hci_uart *hu)
int err = 0;
struct sk_buff *skb = NULL;
struct ll_struct *ll = hu->priv;
- struct hcill_cmd *hcill_packet;
BT_DBG("hu %p cmd 0x%x", hu, cmd);
@@ -126,8 +112,7 @@ static int send_hcill_cmd(u8 cmd, struct hci_uart *hu)
}
/* prepare packet */
- hcill_packet = skb_put(skb, 1);
- hcill_packet->cmd = cmd;
+ skb_put_u8(skb, cmd);
/* send packet */
skb_queue_tail(&ll->txq, skb);
@@ -379,155 +364,88 @@ static int ll_enqueue(struct hci_uart *hu, struct sk_buff *skb)
return 0;
}
-static inline int ll_check_data_len(struct hci_dev *hdev, struct ll_struct *ll, int len)
+static int ll_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
- int room = skb_tailroom(ll->rx_skb);
-
- BT_DBG("len %d room %d", len, room);
+ struct hci_uart *hu = hci_get_drvdata(hdev);
+ struct ll_struct *ll = hu->priv;
- if (!len) {
- hci_recv_frame(hdev, ll->rx_skb);
- } else if (len > room) {
- BT_ERR("Data length is too large");
- kfree_skb(ll->rx_skb);
- } else {
- ll->rx_state = HCILL_W4_DATA;
- ll->rx_count = len;
- return len;
+ switch (hci_skb_pkt_type(skb)) {
+ case HCILL_GO_TO_SLEEP_IND:
+ BT_DBG("HCILL_GO_TO_SLEEP_IND packet");
+ ll_device_want_to_sleep(hu);
+ break;
+ case HCILL_GO_TO_SLEEP_ACK:
+ /* shouldn't happen */
+ bt_dev_err(hdev, "received HCILL_GO_TO_SLEEP_ACK in state %ld",
+ ll->hcill_state);
+ break;
+ case HCILL_WAKE_UP_IND:
+ BT_DBG("HCILL_WAKE_UP_IND packet");
+ ll_device_want_to_wakeup(hu);
+ break;
+ case HCILL_WAKE_UP_ACK:
+ BT_DBG("HCILL_WAKE_UP_ACK packet");
+ ll_device_woke_up(hu);
+ break;
}
- ll->rx_state = HCILL_W4_PACKET_TYPE;
- ll->rx_skb = NULL;
- ll->rx_count = 0;
-
+ kfree_skb(skb);
return 0;
}
+#define LL_RECV_SLEEP_IND \
+ .type = HCILL_GO_TO_SLEEP_IND, \
+ .hlen = 0, \
+ .loff = 0, \
+ .lsize = 0, \
+ .maxlen = 0
+
+#define LL_RECV_SLEEP_ACK \
+ .type = HCILL_GO_TO_SLEEP_ACK, \
+ .hlen = 0, \
+ .loff = 0, \
+ .lsize = 0, \
+ .maxlen = 0
+
+#define LL_RECV_WAKE_IND \
+ .type = HCILL_WAKE_UP_IND, \
+ .hlen = 0, \
+ .loff = 0, \
+ .lsize = 0, \
+ .maxlen = 0
+
+#define LL_RECV_WAKE_ACK \
+ .type = HCILL_WAKE_UP_ACK, \
+ .hlen = 0, \
+ .loff = 0, \
+ .lsize = 0, \
+ .maxlen = 0
+
+static const struct h4_recv_pkt ll_recv_pkts[] = {
+ { H4_RECV_ACL, .recv = hci_recv_frame },
+ { H4_RECV_SCO, .recv = hci_recv_frame },
+ { H4_RECV_EVENT, .recv = hci_recv_frame },
+ { LL_RECV_SLEEP_IND, .recv = ll_recv_frame },
+ { LL_RECV_SLEEP_ACK, .recv = ll_recv_frame },
+ { LL_RECV_WAKE_IND, .recv = ll_recv_frame },
+ { LL_RECV_WAKE_ACK, .recv = ll_recv_frame },
+};
+
/* Recv data */
static int ll_recv(struct hci_uart *hu, const void *data, int count)
{
struct ll_struct *ll = hu->priv;
- const char *ptr;
- struct hci_event_hdr *eh;
- struct hci_acl_hdr *ah;
- struct hci_sco_hdr *sh;
- int len, type, dlen;
-
- BT_DBG("hu %p count %d rx_state %ld rx_count %ld", hu, count, ll->rx_state, ll->rx_count);
-
- ptr = data;
- while (count) {
- if (ll->rx_count) {
- len = min_t(unsigned int, ll->rx_count, count);
- skb_put_data(ll->rx_skb, ptr, len);
- ll->rx_count -= len; count -= len; ptr += len;
-
- if (ll->rx_count)
- continue;
-
- switch (ll->rx_state) {
- case HCILL_W4_DATA:
- BT_DBG("Complete data");
- hci_recv_frame(hu->hdev, ll->rx_skb);
-
- ll->rx_state = HCILL_W4_PACKET_TYPE;
- ll->rx_skb = NULL;
- continue;
-
- case HCILL_W4_EVENT_HDR:
- eh = hci_event_hdr(ll->rx_skb);
-
- BT_DBG("Event header: evt 0x%2.2x plen %d", eh->evt, eh->plen);
- ll_check_data_len(hu->hdev, ll, eh->plen);
- continue;
+ if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
+ return -EUNATCH;
- case HCILL_W4_ACL_HDR:
- ah = hci_acl_hdr(ll->rx_skb);
- dlen = __le16_to_cpu(ah->dlen);
-
- BT_DBG("ACL header: dlen %d", dlen);
-
- ll_check_data_len(hu->hdev, ll, dlen);
- continue;
-
- case HCILL_W4_SCO_HDR:
- sh = hci_sco_hdr(ll->rx_skb);
-
- BT_DBG("SCO header: dlen %d", sh->dlen);
-
- ll_check_data_len(hu->hdev, ll, sh->dlen);
- continue;
- }
- }
-
- /* HCILL_W4_PACKET_TYPE */
- switch (*ptr) {
- case HCI_EVENT_PKT:
- BT_DBG("Event packet");
- ll->rx_state = HCILL_W4_EVENT_HDR;
- ll->rx_count = HCI_EVENT_HDR_SIZE;
- type = HCI_EVENT_PKT;
- break;
-
- case HCI_ACLDATA_PKT:
- BT_DBG("ACL packet");
- ll->rx_state = HCILL_W4_ACL_HDR;
- ll->rx_count = HCI_ACL_HDR_SIZE;
- type = HCI_ACLDATA_PKT;
- break;
-
- case HCI_SCODATA_PKT:
- BT_DBG("SCO packet");
- ll->rx_state = HCILL_W4_SCO_HDR;
- ll->rx_count = HCI_SCO_HDR_SIZE;
- type = HCI_SCODATA_PKT;
- break;
-
- /* HCILL signals */
- case HCILL_GO_TO_SLEEP_IND:
- BT_DBG("HCILL_GO_TO_SLEEP_IND packet");
- ll_device_want_to_sleep(hu);
- ptr++; count--;
- continue;
-
- case HCILL_GO_TO_SLEEP_ACK:
- /* shouldn't happen */
- BT_ERR("received HCILL_GO_TO_SLEEP_ACK (in state %ld)", ll->hcill_state);
- ptr++; count--;
- continue;
-
- case HCILL_WAKE_UP_IND:
- BT_DBG("HCILL_WAKE_UP_IND packet");
- ll_device_want_to_wakeup(hu);
- ptr++; count--;
- continue;
-
- case HCILL_WAKE_UP_ACK:
- BT_DBG("HCILL_WAKE_UP_ACK packet");
- ll_device_woke_up(hu);
- ptr++; count--;
- continue;
-
- default:
- BT_ERR("Unknown HCI packet type %2.2x", (__u8)*ptr);
- hu->hdev->stat.err_rx++;
- ptr++; count--;
- continue;
- }
-
- ptr++; count--;
-
- /* Allocate packet */
- ll->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
- if (!ll->rx_skb) {
- BT_ERR("Can't allocate mem for new packet");
- ll->rx_state = HCILL_W4_PACKET_TYPE;
- ll->rx_count = 0;
- return -ENOMEM;
- }
-
- hci_skb_pkt_type(ll->rx_skb) = type;
+ ll->rx_skb = h4_recv_buf(hu->hdev, ll->rx_skb, data, count,
+ ll_recv_pkts, ARRAY_SIZE(ll_recv_pkts));
+ if (IS_ERR(ll->rx_skb)) {
+ int err = PTR_ERR(ll->rx_skb);
+ bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err);
+ ll->rx_skb = NULL;
+ return err;
}
return count;
@@ -650,7 +568,7 @@ static int download_firmware(struct ll_device *lldev)
break;
case ACTION_DELAY: /* sleep */
bt_dev_info(lldev->hu.hdev, "sleep command in scr");
- mdelay(((struct bts_action_delay *)action_ptr)->msec);
+ msleep(((struct bts_action_delay *)action_ptr)->msec);
break;
}
len -= (sizeof(struct bts_action) +
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index 57e011d36a79..6dc177bf4c42 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -8,25 +8,10 @@ menu "Bus devices"
config ARM_CCI
bool
-config ARM_CCI_PMU
- bool
- select ARM_CCI
-
config ARM_CCI400_COMMON
bool
select ARM_CCI
-config ARM_CCI400_PMU
- bool "ARM CCI400 PMU support"
- depends on (ARM && CPU_V7) || ARM64
- depends on PERF_EVENTS
- select ARM_CCI400_COMMON
- select ARM_CCI_PMU
- help
- Support for PMU events monitoring on the ARM CCI-400 (cache coherent
- interconnect). CCI-400 supports counting events related to the
- connected slave/master interfaces.
-
config ARM_CCI400_PORT_CTRL
bool
depends on ARM && OF && CPU_V7
@@ -35,27 +20,6 @@ config ARM_CCI400_PORT_CTRL
Low level power management driver for CCI400 cache coherent
interconnect for ARM platforms.
-config ARM_CCI5xx_PMU
- bool "ARM CCI-500/CCI-550 PMU support"
- depends on (ARM && CPU_V7) || ARM64
- depends on PERF_EVENTS
- select ARM_CCI_PMU
- help
- Support for PMU events monitoring on the ARM CCI-500/CCI-550 cache
- coherent interconnects. Both of them provide 8 independent event counters,
- which can count events pertaining to the slave/master interfaces as well
- as the internal events to the CCI.
-
- If unsure, say Y
-
-config ARM_CCN
- tristate "ARM CCN driver support"
- depends on ARM || ARM64
- depends on PERF_EVENTS
- help
- PMU (perf) driver supporting the ARM CCN (Cache Coherent Network)
- interconnect.
-
config BRCMSTB_GISB_ARB
bool "Broadcom STB GISB bus arbiter"
depends on ARM || ARM64 || MIPS
@@ -65,6 +29,15 @@ config BRCMSTB_GISB_ARB
arbiter. This driver provides timeout and target abort error handling
and internal bus master decoding.
+config HISILICON_LPC
+ bool "Support for ISA I/O space on HiSilicon Hip06/7"
+ depends on ARM64 && (ARCH_HISI || COMPILE_TEST)
+ select INDIRECT_PIO
+ select MFD_CORE if ACPI
+ help
+ Driver to enable I/O access to devices attached to the Low Pin
+ Count bus on the HiSilicon Hip06/7 SoC.
+
config IMX_WEIM
bool "Freescale EIM DRIVER"
depends on ARCH_MXC
@@ -199,4 +172,6 @@ config DA8XX_MSTPRI
configuration. Allows to adjust the priorities of all master
peripherals.
+source "drivers/bus/fsl-mc/Kconfig"
+
endmenu
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
index 9bcd0bf3954b..b8f036cca7ff 100644
--- a/drivers/bus/Makefile
+++ b/drivers/bus/Makefile
@@ -5,9 +5,13 @@
# Interconnect bus drivers for ARM platforms
obj-$(CONFIG_ARM_CCI) += arm-cci.o
-obj-$(CONFIG_ARM_CCN) += arm-ccn.o
+obj-$(CONFIG_HISILICON_LPC) += hisi_lpc.o
obj-$(CONFIG_BRCMSTB_GISB_ARB) += brcmstb_gisb.o
+
+# DPAA2 fsl-mc bus
+obj-$(CONFIG_FSL_MC_BUS) += fsl-mc/
+
obj-$(CONFIG_IMX_WEIM) += imx-weim.o
obj-$(CONFIG_MIPS_CDMM) += mips_cdmm.o
obj-$(CONFIG_MVEBU_MBUS) += mvebu-mbus.o
diff --git a/drivers/bus/arm-cci.c b/drivers/bus/arm-cci.c
index 5426c04fe24b..443e4c3fd357 100644
--- a/drivers/bus/arm-cci.c
+++ b/drivers/bus/arm-cci.c
@@ -16,21 +16,17 @@
#include <linux/arm-cci.h>
#include <linux/io.h>
-#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of_address.h>
-#include <linux/of_irq.h>
#include <linux/of_platform.h>
-#include <linux/perf_event.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
-#include <linux/spinlock.h>
#include <asm/cacheflush.h>
#include <asm/smp_plat.h>
-static void __iomem *cci_ctrl_base;
-static unsigned long cci_ctrl_phys;
+static void __iomem *cci_ctrl_base __ro_after_init;
+static unsigned long cci_ctrl_phys __ro_after_init;
#ifdef CONFIG_ARM_CCI400_PORT_CTRL
struct cci_nb_ports {
@@ -59,1733 +55,26 @@ static const struct of_device_id arm_cci_matches[] = {
{},
};
-#ifdef CONFIG_ARM_CCI_PMU
-
-#define DRIVER_NAME "ARM-CCI"
-#define DRIVER_NAME_PMU DRIVER_NAME " PMU"
-
-#define CCI_PMCR 0x0100
-#define CCI_PID2 0x0fe8
-
-#define CCI_PMCR_CEN 0x00000001
-#define CCI_PMCR_NCNT_MASK 0x0000f800
-#define CCI_PMCR_NCNT_SHIFT 11
-
-#define CCI_PID2_REV_MASK 0xf0
-#define CCI_PID2_REV_SHIFT 4
-
-#define CCI_PMU_EVT_SEL 0x000
-#define CCI_PMU_CNTR 0x004
-#define CCI_PMU_CNTR_CTRL 0x008
-#define CCI_PMU_OVRFLW 0x00c
-
-#define CCI_PMU_OVRFLW_FLAG 1
-
-#define CCI_PMU_CNTR_SIZE(model) ((model)->cntr_size)
-#define CCI_PMU_CNTR_BASE(model, idx) ((idx) * CCI_PMU_CNTR_SIZE(model))
-#define CCI_PMU_CNTR_MASK ((1ULL << 32) -1)
-#define CCI_PMU_CNTR_LAST(cci_pmu) (cci_pmu->num_cntrs - 1)
-
-#define CCI_PMU_MAX_HW_CNTRS(model) \
- ((model)->num_hw_cntrs + (model)->fixed_hw_cntrs)
-
-/* Types of interfaces that can generate events */
-enum {
- CCI_IF_SLAVE,
- CCI_IF_MASTER,
-#ifdef CONFIG_ARM_CCI5xx_PMU
- CCI_IF_GLOBAL,
-#endif
- CCI_IF_MAX,
-};
-
-struct event_range {
- u32 min;
- u32 max;
-};
-
-struct cci_pmu_hw_events {
- struct perf_event **events;
- unsigned long *used_mask;
- raw_spinlock_t pmu_lock;
-};
-
-struct cci_pmu;
-/*
- * struct cci_pmu_model:
- * @fixed_hw_cntrs - Number of fixed event counters
- * @num_hw_cntrs - Maximum number of programmable event counters
- * @cntr_size - Size of an event counter mapping
- */
-struct cci_pmu_model {
- char *name;
- u32 fixed_hw_cntrs;
- u32 num_hw_cntrs;
- u32 cntr_size;
- struct attribute **format_attrs;
- struct attribute **event_attrs;
- struct event_range event_ranges[CCI_IF_MAX];
- int (*validate_hw_event)(struct cci_pmu *, unsigned long);
- int (*get_event_idx)(struct cci_pmu *, struct cci_pmu_hw_events *, unsigned long);
- void (*write_counters)(struct cci_pmu *, unsigned long *);
-};
-
-static struct cci_pmu_model cci_pmu_models[];
-
-struct cci_pmu {
- void __iomem *base;
- struct pmu pmu;
- int nr_irqs;
- int *irqs;
- unsigned long active_irqs;
- const struct cci_pmu_model *model;
- struct cci_pmu_hw_events hw_events;
- struct platform_device *plat_device;
- int num_cntrs;
- atomic_t active_events;
- struct mutex reserve_mutex;
- struct hlist_node node;
- cpumask_t cpus;
-};
-
-#define to_cci_pmu(c) (container_of(c, struct cci_pmu, pmu))
-
-enum cci_models {
-#ifdef CONFIG_ARM_CCI400_PMU
- CCI400_R0,
- CCI400_R1,
-#endif
-#ifdef CONFIG_ARM_CCI5xx_PMU
- CCI500_R0,
- CCI550_R0,
-#endif
- CCI_MODEL_MAX
-};
-
-static void pmu_write_counters(struct cci_pmu *cci_pmu,
- unsigned long *mask);
-static ssize_t cci_pmu_format_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-static ssize_t cci_pmu_event_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-#define CCI_EXT_ATTR_ENTRY(_name, _func, _config) \
- &((struct dev_ext_attribute[]) { \
- { __ATTR(_name, S_IRUGO, _func, NULL), (void *)_config } \
- })[0].attr.attr
-
-#define CCI_FORMAT_EXT_ATTR_ENTRY(_name, _config) \
- CCI_EXT_ATTR_ENTRY(_name, cci_pmu_format_show, (char *)_config)
-#define CCI_EVENT_EXT_ATTR_ENTRY(_name, _config) \
- CCI_EXT_ATTR_ENTRY(_name, cci_pmu_event_show, (unsigned long)_config)
-
-/* CCI400 PMU Specific definitions */
-
-#ifdef CONFIG_ARM_CCI400_PMU
-
-/* Port ids */
-#define CCI400_PORT_S0 0
-#define CCI400_PORT_S1 1
-#define CCI400_PORT_S2 2
-#define CCI400_PORT_S3 3
-#define CCI400_PORT_S4 4
-#define CCI400_PORT_M0 5
-#define CCI400_PORT_M1 6
-#define CCI400_PORT_M2 7
-
-#define CCI400_R1_PX 5
-
-/*
- * Instead of an event id to monitor CCI cycles, a dedicated counter is
- * provided. Use 0xff to represent CCI cycles and hope that no future revisions
- * make use of this event in hardware.
- */
-enum cci400_perf_events {
- CCI400_PMU_CYCLES = 0xff
-};
-
-#define CCI400_PMU_CYCLE_CNTR_IDX 0
-#define CCI400_PMU_CNTR0_IDX 1
-
-/*
- * CCI PMU event id is an 8-bit value made of two parts - bits 7:5 for one of 8
- * ports and bits 4:0 are event codes. There are different event codes
- * associated with each port type.
- *
- * Additionally, the range of events associated with the port types changed
- * between Rev0 and Rev1.
- *
- * The constants below define the range of valid codes for each port type for
- * the different revisions and are used to validate the event to be monitored.
- */
-
-#define CCI400_PMU_EVENT_MASK 0xffUL
-#define CCI400_PMU_EVENT_SOURCE_SHIFT 5
-#define CCI400_PMU_EVENT_SOURCE_MASK 0x7
-#define CCI400_PMU_EVENT_CODE_SHIFT 0
-#define CCI400_PMU_EVENT_CODE_MASK 0x1f
-#define CCI400_PMU_EVENT_SOURCE(event) \
- ((event >> CCI400_PMU_EVENT_SOURCE_SHIFT) & \
- CCI400_PMU_EVENT_SOURCE_MASK)
-#define CCI400_PMU_EVENT_CODE(event) \
- ((event >> CCI400_PMU_EVENT_CODE_SHIFT) & CCI400_PMU_EVENT_CODE_MASK)
-
-#define CCI400_R0_SLAVE_PORT_MIN_EV 0x00
-#define CCI400_R0_SLAVE_PORT_MAX_EV 0x13
-#define CCI400_R0_MASTER_PORT_MIN_EV 0x14
-#define CCI400_R0_MASTER_PORT_MAX_EV 0x1a
-
-#define CCI400_R1_SLAVE_PORT_MIN_EV 0x00
-#define CCI400_R1_SLAVE_PORT_MAX_EV 0x14
-#define CCI400_R1_MASTER_PORT_MIN_EV 0x00
-#define CCI400_R1_MASTER_PORT_MAX_EV 0x11
-
-#define CCI400_CYCLE_EVENT_EXT_ATTR_ENTRY(_name, _config) \
- CCI_EXT_ATTR_ENTRY(_name, cci400_pmu_cycle_event_show, \
- (unsigned long)_config)
-
-static ssize_t cci400_pmu_cycle_event_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static struct attribute *cci400_pmu_format_attrs[] = {
- CCI_FORMAT_EXT_ATTR_ENTRY(event, "config:0-4"),
- CCI_FORMAT_EXT_ATTR_ENTRY(source, "config:5-7"),
- NULL
-};
-
-static struct attribute *cci400_r0_pmu_event_attrs[] = {
- /* Slave events */
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_any, 0x0),
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_device, 0x01),
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_normal_or_nonshareable, 0x2),
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_inner_or_outershareable, 0x3),
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_cache_maintenance, 0x4),
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_mem_barrier, 0x5),
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_sync_barrier, 0x6),
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_dvm_msg, 0x7),
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_dvm_msg_sync, 0x8),
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_stall_tt_full, 0x9),
- CCI_EVENT_EXT_ATTR_ENTRY(si_r_data_last_hs_snoop, 0xA),
- CCI_EVENT_EXT_ATTR_ENTRY(si_r_data_stall_rvalids_h_rready_l, 0xB),
- CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_any, 0xC),
- CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_device, 0xD),
- CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_normal_or_nonshareable, 0xE),
- CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_inner_or_outershare_wback_wclean, 0xF),
- CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_write_unique, 0x10),
- CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_write_line_unique, 0x11),
- CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_evict, 0x12),
- CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_stall_tt_full, 0x13),
- /* Master events */
- CCI_EVENT_EXT_ATTR_ENTRY(mi_retry_speculative_fetch, 0x14),
- CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_addr_hazard, 0x15),
- CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_id_hazard, 0x16),
- CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_tt_full, 0x17),
- CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_barrier_hazard, 0x18),
- CCI_EVENT_EXT_ATTR_ENTRY(mi_wrq_stall_barrier_hazard, 0x19),
- CCI_EVENT_EXT_ATTR_ENTRY(mi_wrq_stall_tt_full, 0x1A),
- /* Special event for cycles counter */
- CCI400_CYCLE_EVENT_EXT_ATTR_ENTRY(cycles, 0xff),
- NULL
-};
-
-static struct attribute *cci400_r1_pmu_event_attrs[] = {
- /* Slave events */
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_any, 0x0),
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_device, 0x01),
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_normal_or_nonshareable, 0x2),
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_inner_or_outershareable, 0x3),
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_cache_maintenance, 0x4),
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_mem_barrier, 0x5),
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_sync_barrier, 0x6),
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_dvm_msg, 0x7),
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_dvm_msg_sync, 0x8),
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_stall_tt_full, 0x9),
- CCI_EVENT_EXT_ATTR_ENTRY(si_r_data_last_hs_snoop, 0xA),
- CCI_EVENT_EXT_ATTR_ENTRY(si_r_data_stall_rvalids_h_rready_l, 0xB),
- CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_any, 0xC),
- CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_device, 0xD),
- CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_normal_or_nonshareable, 0xE),
- CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_inner_or_outershare_wback_wclean, 0xF),
- CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_write_unique, 0x10),
- CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_write_line_unique, 0x11),
- CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_evict, 0x12),
- CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_stall_tt_full, 0x13),
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_stall_slave_id_hazard, 0x14),
- /* Master events */
- CCI_EVENT_EXT_ATTR_ENTRY(mi_retry_speculative_fetch, 0x0),
- CCI_EVENT_EXT_ATTR_ENTRY(mi_stall_cycle_addr_hazard, 0x1),
- CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_master_id_hazard, 0x2),
- CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_hi_prio_rtq_full, 0x3),
- CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_barrier_hazard, 0x4),
- CCI_EVENT_EXT_ATTR_ENTRY(mi_wrq_stall_barrier_hazard, 0x5),
- CCI_EVENT_EXT_ATTR_ENTRY(mi_wrq_stall_wtq_full, 0x6),
- CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_low_prio_rtq_full, 0x7),
- CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_mid_prio_rtq_full, 0x8),
- CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_qvn_vn0, 0x9),
- CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_qvn_vn1, 0xA),
- CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_qvn_vn2, 0xB),
- CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_qvn_vn3, 0xC),
- CCI_EVENT_EXT_ATTR_ENTRY(mi_wrq_stall_qvn_vn0, 0xD),
- CCI_EVENT_EXT_ATTR_ENTRY(mi_wrq_stall_qvn_vn1, 0xE),
- CCI_EVENT_EXT_ATTR_ENTRY(mi_wrq_stall_qvn_vn2, 0xF),
- CCI_EVENT_EXT_ATTR_ENTRY(mi_wrq_stall_qvn_vn3, 0x10),
- CCI_EVENT_EXT_ATTR_ENTRY(mi_wrq_unique_or_line_unique_addr_hazard, 0x11),
- /* Special event for cycles counter */
- CCI400_CYCLE_EVENT_EXT_ATTR_ENTRY(cycles, 0xff),
- NULL
-};
-
-static ssize_t cci400_pmu_cycle_event_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct dev_ext_attribute *eattr = container_of(attr,
- struct dev_ext_attribute, attr);
- return snprintf(buf, PAGE_SIZE, "config=0x%lx\n", (unsigned long)eattr->var);
-}
-
-static int cci400_get_event_idx(struct cci_pmu *cci_pmu,
- struct cci_pmu_hw_events *hw,
- unsigned long cci_event)
-{
- int idx;
-
- /* cycles event idx is fixed */
- if (cci_event == CCI400_PMU_CYCLES) {
- if (test_and_set_bit(CCI400_PMU_CYCLE_CNTR_IDX, hw->used_mask))
- return -EAGAIN;
-
- return CCI400_PMU_CYCLE_CNTR_IDX;
- }
-
- for (idx = CCI400_PMU_CNTR0_IDX; idx <= CCI_PMU_CNTR_LAST(cci_pmu); ++idx)
- if (!test_and_set_bit(idx, hw->used_mask))
- return idx;
-
- /* No counters available */
- return -EAGAIN;
-}
-
-static int cci400_validate_hw_event(struct cci_pmu *cci_pmu, unsigned long hw_event)
-{
- u8 ev_source = CCI400_PMU_EVENT_SOURCE(hw_event);
- u8 ev_code = CCI400_PMU_EVENT_CODE(hw_event);
- int if_type;
-
- if (hw_event & ~CCI400_PMU_EVENT_MASK)
- return -ENOENT;
-
- if (hw_event == CCI400_PMU_CYCLES)
- return hw_event;
-
- switch (ev_source) {
- case CCI400_PORT_S0:
- case CCI400_PORT_S1:
- case CCI400_PORT_S2:
- case CCI400_PORT_S3:
- case CCI400_PORT_S4:
- /* Slave Interface */
- if_type = CCI_IF_SLAVE;
- break;
- case CCI400_PORT_M0:
- case CCI400_PORT_M1:
- case CCI400_PORT_M2:
- /* Master Interface */
- if_type = CCI_IF_MASTER;
- break;
- default:
- return -ENOENT;
- }
-
- if (ev_code >= cci_pmu->model->event_ranges[if_type].min &&
- ev_code <= cci_pmu->model->event_ranges[if_type].max)
- return hw_event;
-
- return -ENOENT;
-}
-
-static int probe_cci400_revision(void)
-{
- int rev;
- rev = readl_relaxed(cci_ctrl_base + CCI_PID2) & CCI_PID2_REV_MASK;
- rev >>= CCI_PID2_REV_SHIFT;
-
- if (rev < CCI400_R1_PX)
- return CCI400_R0;
- else
- return CCI400_R1;
-}
-
-static const struct cci_pmu_model *probe_cci_model(struct platform_device *pdev)
-{
- if (platform_has_secure_cci_access())
- return &cci_pmu_models[probe_cci400_revision()];
- return NULL;
-}
-#else /* !CONFIG_ARM_CCI400_PMU */
-static inline struct cci_pmu_model *probe_cci_model(struct platform_device *pdev)
-{
- return NULL;
-}
-#endif /* CONFIG_ARM_CCI400_PMU */
-
-#ifdef CONFIG_ARM_CCI5xx_PMU
-
-/*
- * CCI5xx PMU event id is an 9-bit value made of two parts.
- * bits [8:5] - Source for the event
- * bits [4:0] - Event code (specific to type of interface)
- *
- *
- */
-
-/* Port ids */
-#define CCI5xx_PORT_S0 0x0
-#define CCI5xx_PORT_S1 0x1
-#define CCI5xx_PORT_S2 0x2
-#define CCI5xx_PORT_S3 0x3
-#define CCI5xx_PORT_S4 0x4
-#define CCI5xx_PORT_S5 0x5
-#define CCI5xx_PORT_S6 0x6
-
-#define CCI5xx_PORT_M0 0x8
-#define CCI5xx_PORT_M1 0x9
-#define CCI5xx_PORT_M2 0xa
-#define CCI5xx_PORT_M3 0xb
-#define CCI5xx_PORT_M4 0xc
-#define CCI5xx_PORT_M5 0xd
-#define CCI5xx_PORT_M6 0xe
-
-#define CCI5xx_PORT_GLOBAL 0xf
-
-#define CCI5xx_PMU_EVENT_MASK 0x1ffUL
-#define CCI5xx_PMU_EVENT_SOURCE_SHIFT 0x5
-#define CCI5xx_PMU_EVENT_SOURCE_MASK 0xf
-#define CCI5xx_PMU_EVENT_CODE_SHIFT 0x0
-#define CCI5xx_PMU_EVENT_CODE_MASK 0x1f
-
-#define CCI5xx_PMU_EVENT_SOURCE(event) \
- ((event >> CCI5xx_PMU_EVENT_SOURCE_SHIFT) & CCI5xx_PMU_EVENT_SOURCE_MASK)
-#define CCI5xx_PMU_EVENT_CODE(event) \
- ((event >> CCI5xx_PMU_EVENT_CODE_SHIFT) & CCI5xx_PMU_EVENT_CODE_MASK)
-
-#define CCI5xx_SLAVE_PORT_MIN_EV 0x00
-#define CCI5xx_SLAVE_PORT_MAX_EV 0x1f
-#define CCI5xx_MASTER_PORT_MIN_EV 0x00
-#define CCI5xx_MASTER_PORT_MAX_EV 0x06
-#define CCI5xx_GLOBAL_PORT_MIN_EV 0x00
-#define CCI5xx_GLOBAL_PORT_MAX_EV 0x0f
-
-
-#define CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(_name, _config) \
- CCI_EXT_ATTR_ENTRY(_name, cci5xx_pmu_global_event_show, \
- (unsigned long) _config)
-
-static ssize_t cci5xx_pmu_global_event_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static struct attribute *cci5xx_pmu_format_attrs[] = {
- CCI_FORMAT_EXT_ATTR_ENTRY(event, "config:0-4"),
- CCI_FORMAT_EXT_ATTR_ENTRY(source, "config:5-8"),
- NULL,
-};
-
-static struct attribute *cci5xx_pmu_event_attrs[] = {
- /* Slave events */
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_arvalid, 0x0),
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_dev, 0x1),
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_nonshareable, 0x2),
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_shareable_non_alloc, 0x3),
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_shareable_alloc, 0x4),
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_invalidate, 0x5),
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_cache_maint, 0x6),
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_dvm_msg, 0x7),
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_rval, 0x8),
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_rlast_snoop, 0x9),
- CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_awalid, 0xA),
- CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_dev, 0xB),
- CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_non_shareable, 0xC),
- CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_share_wb, 0xD),
- CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_share_wlu, 0xE),
- CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_share_wunique, 0xF),
- CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_evict, 0x10),
- CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_wrevict, 0x11),
- CCI_EVENT_EXT_ATTR_ENTRY(si_w_data_beat, 0x12),
- CCI_EVENT_EXT_ATTR_ENTRY(si_srq_acvalid, 0x13),
- CCI_EVENT_EXT_ATTR_ENTRY(si_srq_read, 0x14),
- CCI_EVENT_EXT_ATTR_ENTRY(si_srq_clean, 0x15),
- CCI_EVENT_EXT_ATTR_ENTRY(si_srq_data_transfer_low, 0x16),
- CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_stall_arvalid, 0x17),
- CCI_EVENT_EXT_ATTR_ENTRY(si_r_data_stall, 0x18),
- CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_stall, 0x19),
- CCI_EVENT_EXT_ATTR_ENTRY(si_w_data_stall, 0x1A),
- CCI_EVENT_EXT_ATTR_ENTRY(si_w_resp_stall, 0x1B),
- CCI_EVENT_EXT_ATTR_ENTRY(si_srq_stall, 0x1C),
- CCI_EVENT_EXT_ATTR_ENTRY(si_s_data_stall, 0x1D),
- CCI_EVENT_EXT_ATTR_ENTRY(si_rq_stall_ot_limit, 0x1E),
- CCI_EVENT_EXT_ATTR_ENTRY(si_r_stall_arbit, 0x1F),
-
- /* Master events */
- CCI_EVENT_EXT_ATTR_ENTRY(mi_r_data_beat_any, 0x0),
- CCI_EVENT_EXT_ATTR_ENTRY(mi_w_data_beat_any, 0x1),
- CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall, 0x2),
- CCI_EVENT_EXT_ATTR_ENTRY(mi_r_data_stall, 0x3),
- CCI_EVENT_EXT_ATTR_ENTRY(mi_wrq_stall, 0x4),
- CCI_EVENT_EXT_ATTR_ENTRY(mi_w_data_stall, 0x5),
- CCI_EVENT_EXT_ATTR_ENTRY(mi_w_resp_stall, 0x6),
-
- /* Global events */
- CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_access_filter_bank_0_1, 0x0),
- CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_access_filter_bank_2_3, 0x1),
- CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_access_filter_bank_4_5, 0x2),
- CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_access_filter_bank_6_7, 0x3),
- CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_access_miss_filter_bank_0_1, 0x4),
- CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_access_miss_filter_bank_2_3, 0x5),
- CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_access_miss_filter_bank_4_5, 0x6),
- CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_access_miss_filter_bank_6_7, 0x7),
- CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_back_invalidation, 0x8),
- CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_stall_alloc_busy, 0x9),
- CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_stall_tt_full, 0xA),
- CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_wrq, 0xB),
- CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_cd_hs, 0xC),
- CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_rq_stall_addr_hazard, 0xD),
- CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_rq_stall_tt_full, 0xE),
- CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_rq_tzmp1_prot, 0xF),
- NULL
-};
-
-static ssize_t cci5xx_pmu_global_event_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct dev_ext_attribute *eattr = container_of(attr,
- struct dev_ext_attribute, attr);
- /* Global events have single fixed source code */
- return snprintf(buf, PAGE_SIZE, "event=0x%lx,source=0x%x\n",
- (unsigned long)eattr->var, CCI5xx_PORT_GLOBAL);
-}
-
-/*
- * CCI500 provides 8 independent event counters that can count
- * any of the events available.
- * CCI500 PMU event source ids
- * 0x0-0x6 - Slave interfaces
- * 0x8-0xD - Master interfaces
- * 0xf - Global Events
- * 0x7,0xe - Reserved
- */
-static int cci500_validate_hw_event(struct cci_pmu *cci_pmu,
- unsigned long hw_event)
-{
- u32 ev_source = CCI5xx_PMU_EVENT_SOURCE(hw_event);
- u32 ev_code = CCI5xx_PMU_EVENT_CODE(hw_event);
- int if_type;
-
- if (hw_event & ~CCI5xx_PMU_EVENT_MASK)
- return -ENOENT;
-
- switch (ev_source) {
- case CCI5xx_PORT_S0:
- case CCI5xx_PORT_S1:
- case CCI5xx_PORT_S2:
- case CCI5xx_PORT_S3:
- case CCI5xx_PORT_S4:
- case CCI5xx_PORT_S5:
- case CCI5xx_PORT_S6:
- if_type = CCI_IF_SLAVE;
- break;
- case CCI5xx_PORT_M0:
- case CCI5xx_PORT_M1:
- case CCI5xx_PORT_M2:
- case CCI5xx_PORT_M3:
- case CCI5xx_PORT_M4:
- case CCI5xx_PORT_M5:
- if_type = CCI_IF_MASTER;
- break;
- case CCI5xx_PORT_GLOBAL:
- if_type = CCI_IF_GLOBAL;
- break;
- default:
- return -ENOENT;
- }
-
- if (ev_code >= cci_pmu->model->event_ranges[if_type].min &&
- ev_code <= cci_pmu->model->event_ranges[if_type].max)
- return hw_event;
-
- return -ENOENT;
-}
-
-/*
- * CCI550 provides 8 independent event counters that can count
- * any of the events available.
- * CCI550 PMU event source ids
- * 0x0-0x6 - Slave interfaces
- * 0x8-0xe - Master interfaces
- * 0xf - Global Events
- * 0x7 - Reserved
- */
-static int cci550_validate_hw_event(struct cci_pmu *cci_pmu,
- unsigned long hw_event)
-{
- u32 ev_source = CCI5xx_PMU_EVENT_SOURCE(hw_event);
- u32 ev_code = CCI5xx_PMU_EVENT_CODE(hw_event);
- int if_type;
-
- if (hw_event & ~CCI5xx_PMU_EVENT_MASK)
- return -ENOENT;
-
- switch (ev_source) {
- case CCI5xx_PORT_S0:
- case CCI5xx_PORT_S1:
- case CCI5xx_PORT_S2:
- case CCI5xx_PORT_S3:
- case CCI5xx_PORT_S4:
- case CCI5xx_PORT_S5:
- case CCI5xx_PORT_S6:
- if_type = CCI_IF_SLAVE;
- break;
- case CCI5xx_PORT_M0:
- case CCI5xx_PORT_M1:
- case CCI5xx_PORT_M2:
- case CCI5xx_PORT_M3:
- case CCI5xx_PORT_M4:
- case CCI5xx_PORT_M5:
- case CCI5xx_PORT_M6:
- if_type = CCI_IF_MASTER;
- break;
- case CCI5xx_PORT_GLOBAL:
- if_type = CCI_IF_GLOBAL;
- break;
- default:
- return -ENOENT;
- }
-
- if (ev_code >= cci_pmu->model->event_ranges[if_type].min &&
- ev_code <= cci_pmu->model->event_ranges[if_type].max)
- return hw_event;
-
- return -ENOENT;
-}
-
-#endif /* CONFIG_ARM_CCI5xx_PMU */
-
-/*
- * Program the CCI PMU counters which have PERF_HES_ARCH set
- * with the event period and mark them ready before we enable
- * PMU.
- */
-static void cci_pmu_sync_counters(struct cci_pmu *cci_pmu)
-{
- int i;
- struct cci_pmu_hw_events *cci_hw = &cci_pmu->hw_events;
-
- DECLARE_BITMAP(mask, cci_pmu->num_cntrs);
-
- bitmap_zero(mask, cci_pmu->num_cntrs);
- for_each_set_bit(i, cci_pmu->hw_events.used_mask, cci_pmu->num_cntrs) {
- struct perf_event *event = cci_hw->events[i];
-
- if (WARN_ON(!event))
- continue;
-
- /* Leave the events which are not counting */
- if (event->hw.state & PERF_HES_STOPPED)
- continue;
- if (event->hw.state & PERF_HES_ARCH) {
- set_bit(i, mask);
- event->hw.state &= ~PERF_HES_ARCH;
- }
- }
-
- pmu_write_counters(cci_pmu, mask);
-}
-
-/* Should be called with cci_pmu->hw_events->pmu_lock held */
-static void __cci_pmu_enable_nosync(struct cci_pmu *cci_pmu)
-{
- u32 val;
-
- /* Enable all the PMU counters. */
- val = readl_relaxed(cci_ctrl_base + CCI_PMCR) | CCI_PMCR_CEN;
- writel(val, cci_ctrl_base + CCI_PMCR);
-}
-
-/* Should be called with cci_pmu->hw_events->pmu_lock held */
-static void __cci_pmu_enable_sync(struct cci_pmu *cci_pmu)
-{
- cci_pmu_sync_counters(cci_pmu);
- __cci_pmu_enable_nosync(cci_pmu);
-}
-
-/* Should be called with cci_pmu->hw_events->pmu_lock held */
-static void __cci_pmu_disable(void)
-{
- u32 val;
-
- /* Disable all the PMU counters. */
- val = readl_relaxed(cci_ctrl_base + CCI_PMCR) & ~CCI_PMCR_CEN;
- writel(val, cci_ctrl_base + CCI_PMCR);
-}
-
-static ssize_t cci_pmu_format_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct dev_ext_attribute *eattr = container_of(attr,
- struct dev_ext_attribute, attr);
- return snprintf(buf, PAGE_SIZE, "%s\n", (char *)eattr->var);
-}
-
-static ssize_t cci_pmu_event_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct dev_ext_attribute *eattr = container_of(attr,
- struct dev_ext_attribute, attr);
- /* source parameter is mandatory for normal PMU events */
- return snprintf(buf, PAGE_SIZE, "source=?,event=0x%lx\n",
- (unsigned long)eattr->var);
-}
-
-static int pmu_is_valid_counter(struct cci_pmu *cci_pmu, int idx)
-{
- return 0 <= idx && idx <= CCI_PMU_CNTR_LAST(cci_pmu);
-}
-
-static u32 pmu_read_register(struct cci_pmu *cci_pmu, int idx, unsigned int offset)
-{
- return readl_relaxed(cci_pmu->base +
- CCI_PMU_CNTR_BASE(cci_pmu->model, idx) + offset);
-}
-
-static void pmu_write_register(struct cci_pmu *cci_pmu, u32 value,
- int idx, unsigned int offset)
-{
- writel_relaxed(value, cci_pmu->base +
- CCI_PMU_CNTR_BASE(cci_pmu->model, idx) + offset);
-}
-
-static void pmu_disable_counter(struct cci_pmu *cci_pmu, int idx)
-{
- pmu_write_register(cci_pmu, 0, idx, CCI_PMU_CNTR_CTRL);
-}
-
-static void pmu_enable_counter(struct cci_pmu *cci_pmu, int idx)
-{
- pmu_write_register(cci_pmu, 1, idx, CCI_PMU_CNTR_CTRL);
-}
-
-static bool __maybe_unused
-pmu_counter_is_enabled(struct cci_pmu *cci_pmu, int idx)
-{
- return (pmu_read_register(cci_pmu, idx, CCI_PMU_CNTR_CTRL) & 0x1) != 0;
-}
-
-static void pmu_set_event(struct cci_pmu *cci_pmu, int idx, unsigned long event)
-{
- pmu_write_register(cci_pmu, event, idx, CCI_PMU_EVT_SEL);
-}
-
-/*
- * For all counters on the CCI-PMU, disable any 'enabled' counters,
- * saving the changed counters in the mask, so that we can restore
- * it later using pmu_restore_counters. The mask is private to the
- * caller. We cannot rely on the used_mask maintained by the CCI_PMU
- * as it only tells us if the counter is assigned to perf_event or not.
- * The state of the perf_event cannot be locked by the PMU layer, hence
- * we check the individual counter status (which can be locked by
- * cci_pm->hw_events->pmu_lock).
- *
- * @mask should be initialised to empty by the caller.
- */
-static void __maybe_unused
-pmu_save_counters(struct cci_pmu *cci_pmu, unsigned long *mask)
-{
- int i;
-
- for (i = 0; i < cci_pmu->num_cntrs; i++) {
- if (pmu_counter_is_enabled(cci_pmu, i)) {
- set_bit(i, mask);
- pmu_disable_counter(cci_pmu, i);
- }
- }
-}
-
-/*
- * Restore the status of the counters. Reversal of the pmu_save_counters().
- * For each counter set in the mask, enable the counter back.
- */
-static void __maybe_unused
-pmu_restore_counters(struct cci_pmu *cci_pmu, unsigned long *mask)
-{
- int i;
-
- for_each_set_bit(i, mask, cci_pmu->num_cntrs)
- pmu_enable_counter(cci_pmu, i);
-}
-
-/*
- * Returns the number of programmable counters actually implemented
- * by the cci
- */
-static u32 pmu_get_max_counters(void)
-{
- return (readl_relaxed(cci_ctrl_base + CCI_PMCR) &
- CCI_PMCR_NCNT_MASK) >> CCI_PMCR_NCNT_SHIFT;
-}
-
-static int pmu_get_event_idx(struct cci_pmu_hw_events *hw, struct perf_event *event)
-{
- struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu);
- unsigned long cci_event = event->hw.config_base;
- int idx;
-
- if (cci_pmu->model->get_event_idx)
- return cci_pmu->model->get_event_idx(cci_pmu, hw, cci_event);
-
- /* Generic code to find an unused idx from the mask */
- for(idx = 0; idx <= CCI_PMU_CNTR_LAST(cci_pmu); idx++)
- if (!test_and_set_bit(idx, hw->used_mask))
- return idx;
-
- /* No counters available */
- return -EAGAIN;
-}
-
-static int pmu_map_event(struct perf_event *event)
-{
- struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu);
-
- if (event->attr.type < PERF_TYPE_MAX ||
- !cci_pmu->model->validate_hw_event)
- return -ENOENT;
-
- return cci_pmu->model->validate_hw_event(cci_pmu, event->attr.config);
-}
-
-static int pmu_request_irq(struct cci_pmu *cci_pmu, irq_handler_t handler)
-{
- int i;
- struct platform_device *pmu_device = cci_pmu->plat_device;
-
- if (unlikely(!pmu_device))
- return -ENODEV;
-
- if (cci_pmu->nr_irqs < 1) {
- dev_err(&pmu_device->dev, "no irqs for CCI PMUs defined\n");
- return -ENODEV;
- }
-
- /*
- * Register all available CCI PMU interrupts. In the interrupt handler
- * we iterate over the counters checking for interrupt source (the
- * overflowing counter) and clear it.
- *
- * This should allow handling of non-unique interrupt for the counters.
- */
- for (i = 0; i < cci_pmu->nr_irqs; i++) {
- int err = request_irq(cci_pmu->irqs[i], handler, IRQF_SHARED,
- "arm-cci-pmu", cci_pmu);
- if (err) {
- dev_err(&pmu_device->dev, "unable to request IRQ%d for ARM CCI PMU counters\n",
- cci_pmu->irqs[i]);
- return err;
- }
-
- set_bit(i, &cci_pmu->active_irqs);
- }
-
- return 0;
-}
-
-static void pmu_free_irq(struct cci_pmu *cci_pmu)
-{
- int i;
-
- for (i = 0; i < cci_pmu->nr_irqs; i++) {
- if (!test_and_clear_bit(i, &cci_pmu->active_irqs))
- continue;
-
- free_irq(cci_pmu->irqs[i], cci_pmu);
- }
-}
-
-static u32 pmu_read_counter(struct perf_event *event)
-{
- struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu);
- struct hw_perf_event *hw_counter = &event->hw;
- int idx = hw_counter->idx;
- u32 value;
-
- if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) {
- dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx);
- return 0;
- }
- value = pmu_read_register(cci_pmu, idx, CCI_PMU_CNTR);
-
- return value;
-}
-
-static void pmu_write_counter(struct cci_pmu *cci_pmu, u32 value, int idx)
-{
- pmu_write_register(cci_pmu, value, idx, CCI_PMU_CNTR);
-}
-
-static void __pmu_write_counters(struct cci_pmu *cci_pmu, unsigned long *mask)
-{
- int i;
- struct cci_pmu_hw_events *cci_hw = &cci_pmu->hw_events;
-
- for_each_set_bit(i, mask, cci_pmu->num_cntrs) {
- struct perf_event *event = cci_hw->events[i];
-
- if (WARN_ON(!event))
- continue;
- pmu_write_counter(cci_pmu, local64_read(&event->hw.prev_count), i);
- }
-}
-
-static void pmu_write_counters(struct cci_pmu *cci_pmu, unsigned long *mask)
-{
- if (cci_pmu->model->write_counters)
- cci_pmu->model->write_counters(cci_pmu, mask);
- else
- __pmu_write_counters(cci_pmu, mask);
-}
-
-#ifdef CONFIG_ARM_CCI5xx_PMU
-
-/*
- * CCI-500/CCI-550 has advanced power saving policies, which could gate the
- * clocks to the PMU counters, which makes the writes to them ineffective.
- * The only way to write to those counters is when the global counters
- * are enabled and the particular counter is enabled.
- *
- * So we do the following :
- *
- * 1) Disable all the PMU counters, saving their current state
- * 2) Enable the global PMU profiling, now that all counters are
- * disabled.
- *
- * For each counter to be programmed, repeat steps 3-7:
- *
- * 3) Write an invalid event code to the event control register for the
- counter, so that the counters are not modified.
- * 4) Enable the counter control for the counter.
- * 5) Set the counter value
- * 6) Disable the counter
- * 7) Restore the event in the target counter
- *
- * 8) Disable the global PMU.
- * 9) Restore the status of the rest of the counters.
- *
- * We choose an event which for CCI-5xx is guaranteed not to count.
- * We use the highest possible event code (0x1f) for the master interface 0.
- */
-#define CCI5xx_INVALID_EVENT ((CCI5xx_PORT_M0 << CCI5xx_PMU_EVENT_SOURCE_SHIFT) | \
- (CCI5xx_PMU_EVENT_CODE_MASK << CCI5xx_PMU_EVENT_CODE_SHIFT))
-static void cci5xx_pmu_write_counters(struct cci_pmu *cci_pmu, unsigned long *mask)
-{
- int i;
- DECLARE_BITMAP(saved_mask, cci_pmu->num_cntrs);
-
- bitmap_zero(saved_mask, cci_pmu->num_cntrs);
- pmu_save_counters(cci_pmu, saved_mask);
-
- /*
- * Now that all the counters are disabled, we can safely turn the PMU on,
- * without syncing the status of the counters
- */
- __cci_pmu_enable_nosync(cci_pmu);
-
- for_each_set_bit(i, mask, cci_pmu->num_cntrs) {
- struct perf_event *event = cci_pmu->hw_events.events[i];
-
- if (WARN_ON(!event))
- continue;
-
- pmu_set_event(cci_pmu, i, CCI5xx_INVALID_EVENT);
- pmu_enable_counter(cci_pmu, i);
- pmu_write_counter(cci_pmu, local64_read(&event->hw.prev_count), i);
- pmu_disable_counter(cci_pmu, i);
- pmu_set_event(cci_pmu, i, event->hw.config_base);
- }
-
- __cci_pmu_disable();
-
- pmu_restore_counters(cci_pmu, saved_mask);
-}
-
-#endif /* CONFIG_ARM_CCI5xx_PMU */
-
-static u64 pmu_event_update(struct perf_event *event)
-{
- struct hw_perf_event *hwc = &event->hw;
- u64 delta, prev_raw_count, new_raw_count;
-
- do {
- prev_raw_count = local64_read(&hwc->prev_count);
- new_raw_count = pmu_read_counter(event);
- } while (local64_cmpxchg(&hwc->prev_count, prev_raw_count,
- new_raw_count) != prev_raw_count);
-
- delta = (new_raw_count - prev_raw_count) & CCI_PMU_CNTR_MASK;
-
- local64_add(delta, &event->count);
-
- return new_raw_count;
-}
-
-static void pmu_read(struct perf_event *event)
-{
- pmu_event_update(event);
-}
-
-static void pmu_event_set_period(struct perf_event *event)
-{
- struct hw_perf_event *hwc = &event->hw;
- /*
- * The CCI PMU counters have a period of 2^32. To account for the
- * possiblity of extreme interrupt latency we program for a period of
- * half that. Hopefully we can handle the interrupt before another 2^31
- * events occur and the counter overtakes its previous value.
- */
- u64 val = 1ULL << 31;
- local64_set(&hwc->prev_count, val);
-
- /*
- * CCI PMU uses PERF_HES_ARCH to keep track of the counters, whose
- * values needs to be sync-ed with the s/w state before the PMU is
- * enabled.
- * Mark this counter for sync.
- */
- hwc->state |= PERF_HES_ARCH;
-}
-
-static irqreturn_t pmu_handle_irq(int irq_num, void *dev)
-{
- unsigned long flags;
- struct cci_pmu *cci_pmu = dev;
- struct cci_pmu_hw_events *events = &cci_pmu->hw_events;
- int idx, handled = IRQ_NONE;
-
- raw_spin_lock_irqsave(&events->pmu_lock, flags);
-
- /* Disable the PMU while we walk through the counters */
- __cci_pmu_disable();
- /*
- * Iterate over counters and update the corresponding perf events.
- * This should work regardless of whether we have per-counter overflow
- * interrupt or a combined overflow interrupt.
- */
- for (idx = 0; idx <= CCI_PMU_CNTR_LAST(cci_pmu); idx++) {
- struct perf_event *event = events->events[idx];
-
- if (!event)
- continue;
-
- /* Did this counter overflow? */
- if (!(pmu_read_register(cci_pmu, idx, CCI_PMU_OVRFLW) &
- CCI_PMU_OVRFLW_FLAG))
- continue;
-
- pmu_write_register(cci_pmu, CCI_PMU_OVRFLW_FLAG, idx,
- CCI_PMU_OVRFLW);
-
- pmu_event_update(event);
- pmu_event_set_period(event);
- handled = IRQ_HANDLED;
- }
-
- /* Enable the PMU and sync possibly overflowed counters */
- __cci_pmu_enable_sync(cci_pmu);
- raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
-
- return IRQ_RETVAL(handled);
-}
-
-static int cci_pmu_get_hw(struct cci_pmu *cci_pmu)
-{
- int ret = pmu_request_irq(cci_pmu, pmu_handle_irq);
- if (ret) {
- pmu_free_irq(cci_pmu);
- return ret;
- }
- return 0;
-}
-
-static void cci_pmu_put_hw(struct cci_pmu *cci_pmu)
-{
- pmu_free_irq(cci_pmu);
-}
-
-static void hw_perf_event_destroy(struct perf_event *event)
-{
- struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu);
- atomic_t *active_events = &cci_pmu->active_events;
- struct mutex *reserve_mutex = &cci_pmu->reserve_mutex;
-
- if (atomic_dec_and_mutex_lock(active_events, reserve_mutex)) {
- cci_pmu_put_hw(cci_pmu);
- mutex_unlock(reserve_mutex);
- }
-}
-
-static void cci_pmu_enable(struct pmu *pmu)
-{
- struct cci_pmu *cci_pmu = to_cci_pmu(pmu);
- struct cci_pmu_hw_events *hw_events = &cci_pmu->hw_events;
- int enabled = bitmap_weight(hw_events->used_mask, cci_pmu->num_cntrs);
- unsigned long flags;
-
- if (!enabled)
- return;
-
- raw_spin_lock_irqsave(&hw_events->pmu_lock, flags);
- __cci_pmu_enable_sync(cci_pmu);
- raw_spin_unlock_irqrestore(&hw_events->pmu_lock, flags);
-
-}
-
-static void cci_pmu_disable(struct pmu *pmu)
-{
- struct cci_pmu *cci_pmu = to_cci_pmu(pmu);
- struct cci_pmu_hw_events *hw_events = &cci_pmu->hw_events;
- unsigned long flags;
-
- raw_spin_lock_irqsave(&hw_events->pmu_lock, flags);
- __cci_pmu_disable();
- raw_spin_unlock_irqrestore(&hw_events->pmu_lock, flags);
-}
-
-/*
- * Check if the idx represents a non-programmable counter.
- * All the fixed event counters are mapped before the programmable
- * counters.
- */
-static bool pmu_fixed_hw_idx(struct cci_pmu *cci_pmu, int idx)
-{
- return (idx >= 0) && (idx < cci_pmu->model->fixed_hw_cntrs);
-}
-
-static void cci_pmu_start(struct perf_event *event, int pmu_flags)
-{
- struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu);
- struct cci_pmu_hw_events *hw_events = &cci_pmu->hw_events;
- struct hw_perf_event *hwc = &event->hw;
- int idx = hwc->idx;
- unsigned long flags;
-
- /*
- * To handle interrupt latency, we always reprogram the period
- * regardlesss of PERF_EF_RELOAD.
- */
- if (pmu_flags & PERF_EF_RELOAD)
- WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
-
- hwc->state = 0;
-
- if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) {
- dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx);
- return;
- }
-
- raw_spin_lock_irqsave(&hw_events->pmu_lock, flags);
-
- /* Configure the counter unless you are counting a fixed event */
- if (!pmu_fixed_hw_idx(cci_pmu, idx))
- pmu_set_event(cci_pmu, idx, hwc->config_base);
-
- pmu_event_set_period(event);
- pmu_enable_counter(cci_pmu, idx);
-
- raw_spin_unlock_irqrestore(&hw_events->pmu_lock, flags);
-}
-
-static void cci_pmu_stop(struct perf_event *event, int pmu_flags)
-{
- struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu);
- struct hw_perf_event *hwc = &event->hw;
- int idx = hwc->idx;
-
- if (hwc->state & PERF_HES_STOPPED)
- return;
-
- if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) {
- dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx);
- return;
- }
-
- /*
- * We always reprogram the counter, so ignore PERF_EF_UPDATE. See
- * cci_pmu_start()
- */
- pmu_disable_counter(cci_pmu, idx);
- pmu_event_update(event);
- hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;
-}
-
-static int cci_pmu_add(struct perf_event *event, int flags)
-{
- struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu);
- struct cci_pmu_hw_events *hw_events = &cci_pmu->hw_events;
- struct hw_perf_event *hwc = &event->hw;
- int idx;
- int err = 0;
-
- perf_pmu_disable(event->pmu);
-
- /* If we don't have a space for the counter then finish early. */
- idx = pmu_get_event_idx(hw_events, event);
- if (idx < 0) {
- err = idx;
- goto out;
- }
-
- event->hw.idx = idx;
- hw_events->events[idx] = event;
-
- hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE;
- if (flags & PERF_EF_START)
- cci_pmu_start(event, PERF_EF_RELOAD);
-
- /* Propagate our changes to the userspace mapping. */
- perf_event_update_userpage(event);
-
-out:
- perf_pmu_enable(event->pmu);
- return err;
-}
-
-static void cci_pmu_del(struct perf_event *event, int flags)
-{
- struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu);
- struct cci_pmu_hw_events *hw_events = &cci_pmu->hw_events;
- struct hw_perf_event *hwc = &event->hw;
- int idx = hwc->idx;
-
- cci_pmu_stop(event, PERF_EF_UPDATE);
- hw_events->events[idx] = NULL;
- clear_bit(idx, hw_events->used_mask);
-
- perf_event_update_userpage(event);
-}
-
-static int
-validate_event(struct pmu *cci_pmu,
- struct cci_pmu_hw_events *hw_events,
- struct perf_event *event)
-{
- if (is_software_event(event))
- return 1;
-
- /*
- * Reject groups spanning multiple HW PMUs (e.g. CPU + CCI). The
- * core perf code won't check that the pmu->ctx == leader->ctx
- * until after pmu->event_init(event).
- */
- if (event->pmu != cci_pmu)
- return 0;
-
- if (event->state < PERF_EVENT_STATE_OFF)
- return 1;
-
- if (event->state == PERF_EVENT_STATE_OFF && !event->attr.enable_on_exec)
- return 1;
-
- return pmu_get_event_idx(hw_events, event) >= 0;
-}
-
-static int
-validate_group(struct perf_event *event)
-{
- struct perf_event *sibling, *leader = event->group_leader;
- struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu);
- unsigned long mask[BITS_TO_LONGS(cci_pmu->num_cntrs)];
- struct cci_pmu_hw_events fake_pmu = {
- /*
- * Initialise the fake PMU. We only need to populate the
- * used_mask for the purposes of validation.
- */
- .used_mask = mask,
- };
- memset(mask, 0, BITS_TO_LONGS(cci_pmu->num_cntrs) * sizeof(unsigned long));
-
- if (!validate_event(event->pmu, &fake_pmu, leader))
- return -EINVAL;
-
- list_for_each_entry(sibling, &leader->sibling_list, group_entry) {
- if (!validate_event(event->pmu, &fake_pmu, sibling))
- return -EINVAL;
- }
-
- if (!validate_event(event->pmu, &fake_pmu, event))
- return -EINVAL;
-
- return 0;
-}
-
-static int
-__hw_perf_event_init(struct perf_event *event)
-{
- struct hw_perf_event *hwc = &event->hw;
- int mapping;
-
- mapping = pmu_map_event(event);
-
- if (mapping < 0) {
- pr_debug("event %x:%llx not supported\n", event->attr.type,
- event->attr.config);
- return mapping;
- }
-
- /*
- * We don't assign an index until we actually place the event onto
- * hardware. Use -1 to signify that we haven't decided where to put it
- * yet.
- */
- hwc->idx = -1;
- hwc->config_base = 0;
- hwc->config = 0;
- hwc->event_base = 0;
-
- /*
- * Store the event encoding into the config_base field.
- */
- hwc->config_base |= (unsigned long)mapping;
-
- /*
- * Limit the sample_period to half of the counter width. That way, the
- * new counter value is far less likely to overtake the previous one
- * unless you have some serious IRQ latency issues.
- */
- hwc->sample_period = CCI_PMU_CNTR_MASK >> 1;
- hwc->last_period = hwc->sample_period;
- local64_set(&hwc->period_left, hwc->sample_period);
-
- if (event->group_leader != event) {
- if (validate_group(event) != 0)
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int cci_pmu_event_init(struct perf_event *event)
-{
- struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu);
- atomic_t *active_events = &cci_pmu->active_events;
- int err = 0;
- int cpu;
-
- if (event->attr.type != event->pmu->type)
- return -ENOENT;
-
- /* Shared by all CPUs, no meaningful state to sample */
- if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK)
- return -EOPNOTSUPP;
-
- /* We have no filtering of any kind */
- if (event->attr.exclude_user ||
- event->attr.exclude_kernel ||
- event->attr.exclude_hv ||
- event->attr.exclude_idle ||
- event->attr.exclude_host ||
- event->attr.exclude_guest)
- return -EINVAL;
-
- /*
- * Following the example set by other "uncore" PMUs, we accept any CPU
- * and rewrite its affinity dynamically rather than having perf core
- * handle cpu == -1 and pid == -1 for this case.
- *
- * The perf core will pin online CPUs for the duration of this call and
- * the event being installed into its context, so the PMU's CPU can't
- * change under our feet.
- */
- cpu = cpumask_first(&cci_pmu->cpus);
- if (event->cpu < 0 || cpu < 0)
- return -EINVAL;
- event->cpu = cpu;
-
- event->destroy = hw_perf_event_destroy;
- if (!atomic_inc_not_zero(active_events)) {
- mutex_lock(&cci_pmu->reserve_mutex);
- if (atomic_read(active_events) == 0)
- err = cci_pmu_get_hw(cci_pmu);
- if (!err)
- atomic_inc(active_events);
- mutex_unlock(&cci_pmu->reserve_mutex);
- }
- if (err)
- return err;
-
- err = __hw_perf_event_init(event);
- if (err)
- hw_perf_event_destroy(event);
-
- return err;
-}
-
-static ssize_t pmu_cpumask_attr_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct pmu *pmu = dev_get_drvdata(dev);
- struct cci_pmu *cci_pmu = to_cci_pmu(pmu);
-
- int n = scnprintf(buf, PAGE_SIZE - 1, "%*pbl",
- cpumask_pr_args(&cci_pmu->cpus));
- buf[n++] = '\n';
- buf[n] = '\0';
- return n;
-}
-
-static struct device_attribute pmu_cpumask_attr =
- __ATTR(cpumask, S_IRUGO, pmu_cpumask_attr_show, NULL);
-
-static struct attribute *pmu_attrs[] = {
- &pmu_cpumask_attr.attr,
- NULL,
-};
-
-static struct attribute_group pmu_attr_group = {
- .attrs = pmu_attrs,
-};
-
-static struct attribute_group pmu_format_attr_group = {
- .name = "format",
- .attrs = NULL, /* Filled in cci_pmu_init_attrs */
-};
-
-static struct attribute_group pmu_event_attr_group = {
- .name = "events",
- .attrs = NULL, /* Filled in cci_pmu_init_attrs */
-};
-
-static const struct attribute_group *pmu_attr_groups[] = {
- &pmu_attr_group,
- &pmu_format_attr_group,
- &pmu_event_attr_group,
- NULL
-};
-
-static int cci_pmu_init(struct cci_pmu *cci_pmu, struct platform_device *pdev)
-{
- const struct cci_pmu_model *model = cci_pmu->model;
- char *name = model->name;
- u32 num_cntrs;
-
- pmu_event_attr_group.attrs = model->event_attrs;
- pmu_format_attr_group.attrs = model->format_attrs;
-
- cci_pmu->pmu = (struct pmu) {
- .name = cci_pmu->model->name,
- .task_ctx_nr = perf_invalid_context,
- .pmu_enable = cci_pmu_enable,
- .pmu_disable = cci_pmu_disable,
- .event_init = cci_pmu_event_init,
- .add = cci_pmu_add,
- .del = cci_pmu_del,
- .start = cci_pmu_start,
- .stop = cci_pmu_stop,
- .read = pmu_read,
- .attr_groups = pmu_attr_groups,
- };
-
- cci_pmu->plat_device = pdev;
- num_cntrs = pmu_get_max_counters();
- if (num_cntrs > cci_pmu->model->num_hw_cntrs) {
- dev_warn(&pdev->dev,
- "PMU implements more counters(%d) than supported by"
- " the model(%d), truncated.",
- num_cntrs, cci_pmu->model->num_hw_cntrs);
- num_cntrs = cci_pmu->model->num_hw_cntrs;
- }
- cci_pmu->num_cntrs = num_cntrs + cci_pmu->model->fixed_hw_cntrs;
-
- return perf_pmu_register(&cci_pmu->pmu, name, -1);
-}
-
-static int cci_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node)
-{
- struct cci_pmu *cci_pmu = hlist_entry_safe(node, struct cci_pmu, node);
- unsigned int target;
-
- if (!cpumask_test_and_clear_cpu(cpu, &cci_pmu->cpus))
- return 0;
- target = cpumask_any_but(cpu_online_mask, cpu);
- if (target >= nr_cpu_ids)
- return 0;
- /*
- * TODO: migrate context once core races on event->ctx have
- * been fixed.
- */
- cpumask_set_cpu(target, &cci_pmu->cpus);
- return 0;
-}
-
-static struct cci_pmu_model cci_pmu_models[] = {
-#ifdef CONFIG_ARM_CCI400_PMU
- [CCI400_R0] = {
- .name = "CCI_400",
- .fixed_hw_cntrs = 1, /* Cycle counter */
- .num_hw_cntrs = 4,
- .cntr_size = SZ_4K,
- .format_attrs = cci400_pmu_format_attrs,
- .event_attrs = cci400_r0_pmu_event_attrs,
- .event_ranges = {
- [CCI_IF_SLAVE] = {
- CCI400_R0_SLAVE_PORT_MIN_EV,
- CCI400_R0_SLAVE_PORT_MAX_EV,
- },
- [CCI_IF_MASTER] = {
- CCI400_R0_MASTER_PORT_MIN_EV,
- CCI400_R0_MASTER_PORT_MAX_EV,
- },
- },
- .validate_hw_event = cci400_validate_hw_event,
- .get_event_idx = cci400_get_event_idx,
- },
- [CCI400_R1] = {
- .name = "CCI_400_r1",
- .fixed_hw_cntrs = 1, /* Cycle counter */
- .num_hw_cntrs = 4,
- .cntr_size = SZ_4K,
- .format_attrs = cci400_pmu_format_attrs,
- .event_attrs = cci400_r1_pmu_event_attrs,
- .event_ranges = {
- [CCI_IF_SLAVE] = {
- CCI400_R1_SLAVE_PORT_MIN_EV,
- CCI400_R1_SLAVE_PORT_MAX_EV,
- },
- [CCI_IF_MASTER] = {
- CCI400_R1_MASTER_PORT_MIN_EV,
- CCI400_R1_MASTER_PORT_MAX_EV,
- },
- },
- .validate_hw_event = cci400_validate_hw_event,
- .get_event_idx = cci400_get_event_idx,
- },
-#endif
-#ifdef CONFIG_ARM_CCI5xx_PMU
- [CCI500_R0] = {
- .name = "CCI_500",
- .fixed_hw_cntrs = 0,
- .num_hw_cntrs = 8,
- .cntr_size = SZ_64K,
- .format_attrs = cci5xx_pmu_format_attrs,
- .event_attrs = cci5xx_pmu_event_attrs,
- .event_ranges = {
- [CCI_IF_SLAVE] = {
- CCI5xx_SLAVE_PORT_MIN_EV,
- CCI5xx_SLAVE_PORT_MAX_EV,
- },
- [CCI_IF_MASTER] = {
- CCI5xx_MASTER_PORT_MIN_EV,
- CCI5xx_MASTER_PORT_MAX_EV,
- },
- [CCI_IF_GLOBAL] = {
- CCI5xx_GLOBAL_PORT_MIN_EV,
- CCI5xx_GLOBAL_PORT_MAX_EV,
- },
- },
- .validate_hw_event = cci500_validate_hw_event,
- .write_counters = cci5xx_pmu_write_counters,
- },
- [CCI550_R0] = {
- .name = "CCI_550",
- .fixed_hw_cntrs = 0,
- .num_hw_cntrs = 8,
- .cntr_size = SZ_64K,
- .format_attrs = cci5xx_pmu_format_attrs,
- .event_attrs = cci5xx_pmu_event_attrs,
- .event_ranges = {
- [CCI_IF_SLAVE] = {
- CCI5xx_SLAVE_PORT_MIN_EV,
- CCI5xx_SLAVE_PORT_MAX_EV,
- },
- [CCI_IF_MASTER] = {
- CCI5xx_MASTER_PORT_MIN_EV,
- CCI5xx_MASTER_PORT_MAX_EV,
- },
- [CCI_IF_GLOBAL] = {
- CCI5xx_GLOBAL_PORT_MIN_EV,
- CCI5xx_GLOBAL_PORT_MAX_EV,
- },
- },
- .validate_hw_event = cci550_validate_hw_event,
- .write_counters = cci5xx_pmu_write_counters,
- },
-#endif
-};
-
-static const struct of_device_id arm_cci_pmu_matches[] = {
-#ifdef CONFIG_ARM_CCI400_PMU
- {
- .compatible = "arm,cci-400-pmu",
- .data = NULL,
- },
- {
- .compatible = "arm,cci-400-pmu,r0",
- .data = &cci_pmu_models[CCI400_R0],
- },
- {
- .compatible = "arm,cci-400-pmu,r1",
- .data = &cci_pmu_models[CCI400_R1],
- },
-#endif
-#ifdef CONFIG_ARM_CCI5xx_PMU
- {
- .compatible = "arm,cci-500-pmu,r0",
- .data = &cci_pmu_models[CCI500_R0],
- },
- {
- .compatible = "arm,cci-550-pmu,r0",
- .data = &cci_pmu_models[CCI550_R0],
- },
-#endif
- {},
+static const struct of_dev_auxdata arm_cci_auxdata[] = {
+ OF_DEV_AUXDATA("arm,cci-400-pmu", 0, NULL, &cci_ctrl_base),
+ OF_DEV_AUXDATA("arm,cci-400-pmu,r0", 0, NULL, &cci_ctrl_base),
+ OF_DEV_AUXDATA("arm,cci-400-pmu,r1", 0, NULL, &cci_ctrl_base),
+ OF_DEV_AUXDATA("arm,cci-500-pmu,r0", 0, NULL, &cci_ctrl_base),
+ OF_DEV_AUXDATA("arm,cci-550-pmu,r0", 0, NULL, &cci_ctrl_base),
+ {}
};
-static inline const struct cci_pmu_model *get_cci_model(struct platform_device *pdev)
-{
- const struct of_device_id *match = of_match_node(arm_cci_pmu_matches,
- pdev->dev.of_node);
- if (!match)
- return NULL;
- if (match->data)
- return match->data;
-
- dev_warn(&pdev->dev, "DEPRECATED compatible property,"
- "requires secure access to CCI registers");
- return probe_cci_model(pdev);
-}
-
-static bool is_duplicate_irq(int irq, int *irqs, int nr_irqs)
-{
- int i;
-
- for (i = 0; i < nr_irqs; i++)
- if (irq == irqs[i])
- return true;
-
- return false;
-}
-
-static struct cci_pmu *cci_pmu_alloc(struct platform_device *pdev)
-{
- struct cci_pmu *cci_pmu;
- const struct cci_pmu_model *model;
-
- /*
- * All allocations are devm_* hence we don't have to free
- * them explicitly on an error, as it would end up in driver
- * detach.
- */
- model = get_cci_model(pdev);
- if (!model) {
- dev_warn(&pdev->dev, "CCI PMU version not supported\n");
- return ERR_PTR(-ENODEV);
- }
-
- cci_pmu = devm_kzalloc(&pdev->dev, sizeof(*cci_pmu), GFP_KERNEL);
- if (!cci_pmu)
- return ERR_PTR(-ENOMEM);
-
- cci_pmu->model = model;
- cci_pmu->irqs = devm_kcalloc(&pdev->dev, CCI_PMU_MAX_HW_CNTRS(model),
- sizeof(*cci_pmu->irqs), GFP_KERNEL);
- if (!cci_pmu->irqs)
- return ERR_PTR(-ENOMEM);
- cci_pmu->hw_events.events = devm_kcalloc(&pdev->dev,
- CCI_PMU_MAX_HW_CNTRS(model),
- sizeof(*cci_pmu->hw_events.events),
- GFP_KERNEL);
- if (!cci_pmu->hw_events.events)
- return ERR_PTR(-ENOMEM);
- cci_pmu->hw_events.used_mask = devm_kcalloc(&pdev->dev,
- BITS_TO_LONGS(CCI_PMU_MAX_HW_CNTRS(model)),
- sizeof(*cci_pmu->hw_events.used_mask),
- GFP_KERNEL);
- if (!cci_pmu->hw_events.used_mask)
- return ERR_PTR(-ENOMEM);
-
- return cci_pmu;
-}
-
-
-static int cci_pmu_probe(struct platform_device *pdev)
-{
- struct resource *res;
- struct cci_pmu *cci_pmu;
- int i, ret, irq;
-
- cci_pmu = cci_pmu_alloc(pdev);
- if (IS_ERR(cci_pmu))
- return PTR_ERR(cci_pmu);
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- cci_pmu->base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(cci_pmu->base))
- return -ENOMEM;
-
- /*
- * CCI PMU has one overflow interrupt per counter; but some may be tied
- * together to a common interrupt.
- */
- cci_pmu->nr_irqs = 0;
- for (i = 0; i < CCI_PMU_MAX_HW_CNTRS(cci_pmu->model); i++) {
- irq = platform_get_irq(pdev, i);
- if (irq < 0)
- break;
-
- if (is_duplicate_irq(irq, cci_pmu->irqs, cci_pmu->nr_irqs))
- continue;
-
- cci_pmu->irqs[cci_pmu->nr_irqs++] = irq;
- }
-
- /*
- * Ensure that the device tree has as many interrupts as the number
- * of counters.
- */
- if (i < CCI_PMU_MAX_HW_CNTRS(cci_pmu->model)) {
- dev_warn(&pdev->dev, "In-correct number of interrupts: %d, should be %d\n",
- i, CCI_PMU_MAX_HW_CNTRS(cci_pmu->model));
- return -EINVAL;
- }
-
- raw_spin_lock_init(&cci_pmu->hw_events.pmu_lock);
- mutex_init(&cci_pmu->reserve_mutex);
- atomic_set(&cci_pmu->active_events, 0);
- cpumask_set_cpu(get_cpu(), &cci_pmu->cpus);
-
- ret = cci_pmu_init(cci_pmu, pdev);
- if (ret) {
- put_cpu();
- return ret;
- }
-
- cpuhp_state_add_instance_nocalls(CPUHP_AP_PERF_ARM_CCI_ONLINE,
- &cci_pmu->node);
- put_cpu();
- pr_info("ARM %s PMU driver probed", cci_pmu->model->name);
- return 0;
-}
+#define DRIVER_NAME "ARM-CCI"
static int cci_platform_probe(struct platform_device *pdev)
{
if (!cci_probed())
return -ENODEV;
- return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+ return of_platform_populate(pdev->dev.of_node, NULL,
+ arm_cci_auxdata, &pdev->dev);
}
-static struct platform_driver cci_pmu_driver = {
- .driver = {
- .name = DRIVER_NAME_PMU,
- .of_match_table = arm_cci_pmu_matches,
- },
- .probe = cci_pmu_probe,
-};
-
static struct platform_driver cci_platform_driver = {
.driver = {
.name = DRIVER_NAME,
@@ -1796,30 +85,9 @@ static struct platform_driver cci_platform_driver = {
static int __init cci_platform_init(void)
{
- int ret;
-
- ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_CCI_ONLINE,
- "perf/arm/cci:online", NULL,
- cci_pmu_offline_cpu);
- if (ret)
- return ret;
-
- ret = platform_driver_register(&cci_pmu_driver);
- if (ret)
- return ret;
-
return platform_driver_register(&cci_platform_driver);
}
-#else /* !CONFIG_ARM_CCI_PMU */
-
-static int __init cci_platform_init(void)
-{
- return 0;
-}
-
-#endif /* CONFIG_ARM_CCI_PMU */
-
#ifdef CONFIG_ARM_CCI400_PORT_CTRL
#define CCI_PORT_CTRL 0x0
@@ -2189,13 +457,10 @@ static int cci_probe_ports(struct device_node *np)
if (!ports)
return -ENOMEM;
- for_each_child_of_node(np, cp) {
+ for_each_available_child_of_node(np, cp) {
if (!of_match_node(arm_cci_ctrl_if_matches, cp))
continue;
- if (!of_device_is_available(cp))
- continue;
-
i = nb_ace + nb_ace_lite;
if (i >= nb_cci_ports)
@@ -2275,7 +540,7 @@ static int cci_probe(void)
struct resource res;
np = of_find_matching_node(NULL, arm_cci_matches);
- if(!np || !of_device_is_available(np))
+ if (!of_device_is_available(np))
return -ENODEV;
ret = of_address_to_resource(np, 0, &res);
diff --git a/drivers/bus/arm-ccn.c b/drivers/bus/arm-ccn.c
deleted file mode 100644
index b52332e52ca5..000000000000
--- a/drivers/bus/arm-ccn.c
+++ /dev/null
@@ -1,1597 +0,0 @@
-/*
- * 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.
- *
- * 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.
- *
- * Copyright (C) 2014 ARM Limited
- */
-
-#include <linux/ctype.h>
-#include <linux/hrtimer.h>
-#include <linux/idr.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/perf_event.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-
-#define CCN_NUM_XP_PORTS 2
-#define CCN_NUM_VCS 4
-#define CCN_NUM_REGIONS 256
-#define CCN_REGION_SIZE 0x10000
-
-#define CCN_ALL_OLY_ID 0xff00
-#define CCN_ALL_OLY_ID__OLY_ID__SHIFT 0
-#define CCN_ALL_OLY_ID__OLY_ID__MASK 0x1f
-#define CCN_ALL_OLY_ID__NODE_ID__SHIFT 8
-#define CCN_ALL_OLY_ID__NODE_ID__MASK 0x3f
-
-#define CCN_MN_ERRINT_STATUS 0x0008
-#define CCN_MN_ERRINT_STATUS__INTREQ__DESSERT 0x11
-#define CCN_MN_ERRINT_STATUS__ALL_ERRORS__ENABLE 0x02
-#define CCN_MN_ERRINT_STATUS__ALL_ERRORS__DISABLED 0x20
-#define CCN_MN_ERRINT_STATUS__ALL_ERRORS__DISABLE 0x22
-#define CCN_MN_ERRINT_STATUS__CORRECTED_ERRORS_ENABLE 0x04
-#define CCN_MN_ERRINT_STATUS__CORRECTED_ERRORS_DISABLED 0x40
-#define CCN_MN_ERRINT_STATUS__CORRECTED_ERRORS_DISABLE 0x44
-#define CCN_MN_ERRINT_STATUS__PMU_EVENTS__ENABLE 0x08
-#define CCN_MN_ERRINT_STATUS__PMU_EVENTS__DISABLED 0x80
-#define CCN_MN_ERRINT_STATUS__PMU_EVENTS__DISABLE 0x88
-#define CCN_MN_OLY_COMP_LIST_63_0 0x01e0
-#define CCN_MN_ERR_SIG_VAL_63_0 0x0300
-#define CCN_MN_ERR_SIG_VAL_63_0__DT (1 << 1)
-
-#define CCN_DT_ACTIVE_DSM 0x0000
-#define CCN_DT_ACTIVE_DSM__DSM_ID__SHIFT(n) ((n) * 8)
-#define CCN_DT_ACTIVE_DSM__DSM_ID__MASK 0xff
-#define CCN_DT_CTL 0x0028
-#define CCN_DT_CTL__DT_EN (1 << 0)
-#define CCN_DT_PMEVCNT(n) (0x0100 + (n) * 0x8)
-#define CCN_DT_PMCCNTR 0x0140
-#define CCN_DT_PMCCNTRSR 0x0190
-#define CCN_DT_PMOVSR 0x0198
-#define CCN_DT_PMOVSR_CLR 0x01a0
-#define CCN_DT_PMOVSR_CLR__MASK 0x1f
-#define CCN_DT_PMCR 0x01a8
-#define CCN_DT_PMCR__OVFL_INTR_EN (1 << 6)
-#define CCN_DT_PMCR__PMU_EN (1 << 0)
-#define CCN_DT_PMSR 0x01b0
-#define CCN_DT_PMSR_REQ 0x01b8
-#define CCN_DT_PMSR_CLR 0x01c0
-
-#define CCN_HNF_PMU_EVENT_SEL 0x0600
-#define CCN_HNF_PMU_EVENT_SEL__ID__SHIFT(n) ((n) * 4)
-#define CCN_HNF_PMU_EVENT_SEL__ID__MASK 0xf
-
-#define CCN_XP_DT_CONFIG 0x0300
-#define CCN_XP_DT_CONFIG__DT_CFG__SHIFT(n) ((n) * 4)
-#define CCN_XP_DT_CONFIG__DT_CFG__MASK 0xf
-#define CCN_XP_DT_CONFIG__DT_CFG__PASS_THROUGH 0x0
-#define CCN_XP_DT_CONFIG__DT_CFG__WATCHPOINT_0_OR_1 0x1
-#define CCN_XP_DT_CONFIG__DT_CFG__WATCHPOINT(n) (0x2 + (n))
-#define CCN_XP_DT_CONFIG__DT_CFG__XP_PMU_EVENT(n) (0x4 + (n))
-#define CCN_XP_DT_CONFIG__DT_CFG__DEVICE_PMU_EVENT(d, n) (0x8 + (d) * 4 + (n))
-#define CCN_XP_DT_INTERFACE_SEL 0x0308
-#define CCN_XP_DT_INTERFACE_SEL__DT_IO_SEL__SHIFT(n) (0 + (n) * 8)
-#define CCN_XP_DT_INTERFACE_SEL__DT_IO_SEL__MASK 0x1
-#define CCN_XP_DT_INTERFACE_SEL__DT_DEV_SEL__SHIFT(n) (1 + (n) * 8)
-#define CCN_XP_DT_INTERFACE_SEL__DT_DEV_SEL__MASK 0x1
-#define CCN_XP_DT_INTERFACE_SEL__DT_VC_SEL__SHIFT(n) (2 + (n) * 8)
-#define CCN_XP_DT_INTERFACE_SEL__DT_VC_SEL__MASK 0x3
-#define CCN_XP_DT_CMP_VAL_L(n) (0x0310 + (n) * 0x40)
-#define CCN_XP_DT_CMP_VAL_H(n) (0x0318 + (n) * 0x40)
-#define CCN_XP_DT_CMP_MASK_L(n) (0x0320 + (n) * 0x40)
-#define CCN_XP_DT_CMP_MASK_H(n) (0x0328 + (n) * 0x40)
-#define CCN_XP_DT_CONTROL 0x0370
-#define CCN_XP_DT_CONTROL__DT_ENABLE (1 << 0)
-#define CCN_XP_DT_CONTROL__WP_ARM_SEL__SHIFT(n) (12 + (n) * 4)
-#define CCN_XP_DT_CONTROL__WP_ARM_SEL__MASK 0xf
-#define CCN_XP_DT_CONTROL__WP_ARM_SEL__ALWAYS 0xf
-#define CCN_XP_PMU_EVENT_SEL 0x0600
-#define CCN_XP_PMU_EVENT_SEL__ID__SHIFT(n) ((n) * 7)
-#define CCN_XP_PMU_EVENT_SEL__ID__MASK 0x3f
-
-#define CCN_SBAS_PMU_EVENT_SEL 0x0600
-#define CCN_SBAS_PMU_EVENT_SEL__ID__SHIFT(n) ((n) * 4)
-#define CCN_SBAS_PMU_EVENT_SEL__ID__MASK 0xf
-
-#define CCN_RNI_PMU_EVENT_SEL 0x0600
-#define CCN_RNI_PMU_EVENT_SEL__ID__SHIFT(n) ((n) * 4)
-#define CCN_RNI_PMU_EVENT_SEL__ID__MASK 0xf
-
-#define CCN_TYPE_MN 0x01
-#define CCN_TYPE_DT 0x02
-#define CCN_TYPE_HNF 0x04
-#define CCN_TYPE_HNI 0x05
-#define CCN_TYPE_XP 0x08
-#define CCN_TYPE_SBSX 0x0c
-#define CCN_TYPE_SBAS 0x10
-#define CCN_TYPE_RNI_1P 0x14
-#define CCN_TYPE_RNI_2P 0x15
-#define CCN_TYPE_RNI_3P 0x16
-#define CCN_TYPE_RND_1P 0x18 /* RN-D = RN-I + DVM */
-#define CCN_TYPE_RND_2P 0x19
-#define CCN_TYPE_RND_3P 0x1a
-#define CCN_TYPE_CYCLES 0xff /* Pseudotype */
-
-#define CCN_EVENT_WATCHPOINT 0xfe /* Pseudoevent */
-
-#define CCN_NUM_PMU_EVENTS 4
-#define CCN_NUM_XP_WATCHPOINTS 2 /* See DT.dbg_id.num_watchpoints */
-#define CCN_NUM_PMU_EVENT_COUNTERS 8 /* See DT.dbg_id.num_pmucntr */
-#define CCN_IDX_PMU_CYCLE_COUNTER CCN_NUM_PMU_EVENT_COUNTERS
-
-#define CCN_NUM_PREDEFINED_MASKS 4
-#define CCN_IDX_MASK_ANY (CCN_NUM_PMU_EVENT_COUNTERS + 0)
-#define CCN_IDX_MASK_EXACT (CCN_NUM_PMU_EVENT_COUNTERS + 1)
-#define CCN_IDX_MASK_ORDER (CCN_NUM_PMU_EVENT_COUNTERS + 2)
-#define CCN_IDX_MASK_OPCODE (CCN_NUM_PMU_EVENT_COUNTERS + 3)
-
-struct arm_ccn_component {
- void __iomem *base;
- u32 type;
-
- DECLARE_BITMAP(pmu_events_mask, CCN_NUM_PMU_EVENTS);
- union {
- struct {
- DECLARE_BITMAP(dt_cmp_mask, CCN_NUM_XP_WATCHPOINTS);
- } xp;
- };
-};
-
-#define pmu_to_arm_ccn(_pmu) container_of(container_of(_pmu, \
- struct arm_ccn_dt, pmu), struct arm_ccn, dt)
-
-struct arm_ccn_dt {
- int id;
- void __iomem *base;
-
- spinlock_t config_lock;
-
- DECLARE_BITMAP(pmu_counters_mask, CCN_NUM_PMU_EVENT_COUNTERS + 1);
- struct {
- struct arm_ccn_component *source;
- struct perf_event *event;
- } pmu_counters[CCN_NUM_PMU_EVENT_COUNTERS + 1];
-
- struct {
- u64 l, h;
- } cmp_mask[CCN_NUM_PMU_EVENT_COUNTERS + CCN_NUM_PREDEFINED_MASKS];
-
- struct hrtimer hrtimer;
-
- cpumask_t cpu;
- struct hlist_node node;
-
- struct pmu pmu;
-};
-
-struct arm_ccn {
- struct device *dev;
- void __iomem *base;
- unsigned int irq;
-
- unsigned sbas_present:1;
- unsigned sbsx_present:1;
-
- int num_nodes;
- struct arm_ccn_component *node;
-
- int num_xps;
- struct arm_ccn_component *xp;
-
- struct arm_ccn_dt dt;
- int mn_id;
-};
-
-static int arm_ccn_node_to_xp(int node)
-{
- return node / CCN_NUM_XP_PORTS;
-}
-
-static int arm_ccn_node_to_xp_port(int node)
-{
- return node % CCN_NUM_XP_PORTS;
-}
-
-
-/*
- * Bit shifts and masks in these defines must be kept in sync with
- * arm_ccn_pmu_config_set() and CCN_FORMAT_ATTRs below!
- */
-#define CCN_CONFIG_NODE(_config) (((_config) >> 0) & 0xff)
-#define CCN_CONFIG_XP(_config) (((_config) >> 0) & 0xff)
-#define CCN_CONFIG_TYPE(_config) (((_config) >> 8) & 0xff)
-#define CCN_CONFIG_EVENT(_config) (((_config) >> 16) & 0xff)
-#define CCN_CONFIG_PORT(_config) (((_config) >> 24) & 0x3)
-#define CCN_CONFIG_BUS(_config) (((_config) >> 24) & 0x3)
-#define CCN_CONFIG_VC(_config) (((_config) >> 26) & 0x7)
-#define CCN_CONFIG_DIR(_config) (((_config) >> 29) & 0x1)
-#define CCN_CONFIG_MASK(_config) (((_config) >> 30) & 0xf)
-
-static void arm_ccn_pmu_config_set(u64 *config, u32 node_xp, u32 type, u32 port)
-{
- *config &= ~((0xff << 0) | (0xff << 8) | (0x3 << 24));
- *config |= (node_xp << 0) | (type << 8) | (port << 24);
-}
-
-static ssize_t arm_ccn_pmu_format_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct dev_ext_attribute *ea = container_of(attr,
- struct dev_ext_attribute, attr);
-
- return snprintf(buf, PAGE_SIZE, "%s\n", (char *)ea->var);
-}
-
-#define CCN_FORMAT_ATTR(_name, _config) \
- struct dev_ext_attribute arm_ccn_pmu_format_attr_##_name = \
- { __ATTR(_name, S_IRUGO, arm_ccn_pmu_format_show, \
- NULL), _config }
-
-static CCN_FORMAT_ATTR(node, "config:0-7");
-static CCN_FORMAT_ATTR(xp, "config:0-7");
-static CCN_FORMAT_ATTR(type, "config:8-15");
-static CCN_FORMAT_ATTR(event, "config:16-23");
-static CCN_FORMAT_ATTR(port, "config:24-25");
-static CCN_FORMAT_ATTR(bus, "config:24-25");
-static CCN_FORMAT_ATTR(vc, "config:26-28");
-static CCN_FORMAT_ATTR(dir, "config:29-29");
-static CCN_FORMAT_ATTR(mask, "config:30-33");
-static CCN_FORMAT_ATTR(cmp_l, "config1:0-62");
-static CCN_FORMAT_ATTR(cmp_h, "config2:0-59");
-
-static struct attribute *arm_ccn_pmu_format_attrs[] = {
- &arm_ccn_pmu_format_attr_node.attr.attr,
- &arm_ccn_pmu_format_attr_xp.attr.attr,
- &arm_ccn_pmu_format_attr_type.attr.attr,
- &arm_ccn_pmu_format_attr_event.attr.attr,
- &arm_ccn_pmu_format_attr_port.attr.attr,
- &arm_ccn_pmu_format_attr_bus.attr.attr,
- &arm_ccn_pmu_format_attr_vc.attr.attr,
- &arm_ccn_pmu_format_attr_dir.attr.attr,
- &arm_ccn_pmu_format_attr_mask.attr.attr,
- &arm_ccn_pmu_format_attr_cmp_l.attr.attr,
- &arm_ccn_pmu_format_attr_cmp_h.attr.attr,
- NULL
-};
-
-static const struct attribute_group arm_ccn_pmu_format_attr_group = {
- .name = "format",
- .attrs = arm_ccn_pmu_format_attrs,
-};
-
-
-struct arm_ccn_pmu_event {
- struct device_attribute attr;
- u32 type;
- u32 event;
- int num_ports;
- int num_vcs;
- const char *def;
- int mask;
-};
-
-#define CCN_EVENT_ATTR(_name) \
- __ATTR(_name, S_IRUGO, arm_ccn_pmu_event_show, NULL)
-
-/*
- * Events defined in TRM for MN, HN-I and SBSX are actually watchpoints set on
- * their ports in XP they are connected to. For the sake of usability they are
- * explicitly defined here (and translated into a relevant watchpoint in
- * arm_ccn_pmu_event_init()) so the user can easily request them without deep
- * knowledge of the flit format.
- */
-
-#define CCN_EVENT_MN(_name, _def, _mask) { .attr = CCN_EVENT_ATTR(mn_##_name), \
- .type = CCN_TYPE_MN, .event = CCN_EVENT_WATCHPOINT, \
- .num_ports = CCN_NUM_XP_PORTS, .num_vcs = CCN_NUM_VCS, \
- .def = _def, .mask = _mask, }
-
-#define CCN_EVENT_HNI(_name, _def, _mask) { \
- .attr = CCN_EVENT_ATTR(hni_##_name), .type = CCN_TYPE_HNI, \
- .event = CCN_EVENT_WATCHPOINT, .num_ports = CCN_NUM_XP_PORTS, \
- .num_vcs = CCN_NUM_VCS, .def = _def, .mask = _mask, }
-
-#define CCN_EVENT_SBSX(_name, _def, _mask) { \
- .attr = CCN_EVENT_ATTR(sbsx_##_name), .type = CCN_TYPE_SBSX, \
- .event = CCN_EVENT_WATCHPOINT, .num_ports = CCN_NUM_XP_PORTS, \
- .num_vcs = CCN_NUM_VCS, .def = _def, .mask = _mask, }
-
-#define CCN_EVENT_HNF(_name, _event) { .attr = CCN_EVENT_ATTR(hnf_##_name), \
- .type = CCN_TYPE_HNF, .event = _event, }
-
-#define CCN_EVENT_XP(_name, _event) { .attr = CCN_EVENT_ATTR(xp_##_name), \
- .type = CCN_TYPE_XP, .event = _event, \
- .num_ports = CCN_NUM_XP_PORTS, .num_vcs = CCN_NUM_VCS, }
-
-/*
- * RN-I & RN-D (RN-D = RN-I + DVM) nodes have different type ID depending
- * on configuration. One of them is picked to represent the whole group,
- * as they all share the same event types.
- */
-#define CCN_EVENT_RNI(_name, _event) { .attr = CCN_EVENT_ATTR(rni_##_name), \
- .type = CCN_TYPE_RNI_3P, .event = _event, }
-
-#define CCN_EVENT_SBAS(_name, _event) { .attr = CCN_EVENT_ATTR(sbas_##_name), \
- .type = CCN_TYPE_SBAS, .event = _event, }
-
-#define CCN_EVENT_CYCLES(_name) { .attr = CCN_EVENT_ATTR(_name), \
- .type = CCN_TYPE_CYCLES }
-
-
-static ssize_t arm_ccn_pmu_event_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct arm_ccn *ccn = pmu_to_arm_ccn(dev_get_drvdata(dev));
- struct arm_ccn_pmu_event *event = container_of(attr,
- struct arm_ccn_pmu_event, attr);
- ssize_t res;
-
- res = snprintf(buf, PAGE_SIZE, "type=0x%x", event->type);
- if (event->event)
- res += snprintf(buf + res, PAGE_SIZE - res, ",event=0x%x",
- event->event);
- if (event->def)
- res += snprintf(buf + res, PAGE_SIZE - res, ",%s",
- event->def);
- if (event->mask)
- res += snprintf(buf + res, PAGE_SIZE - res, ",mask=0x%x",
- event->mask);
-
- /* Arguments required by an event */
- switch (event->type) {
- case CCN_TYPE_CYCLES:
- break;
- case CCN_TYPE_XP:
- res += snprintf(buf + res, PAGE_SIZE - res,
- ",xp=?,vc=?");
- if (event->event == CCN_EVENT_WATCHPOINT)
- res += snprintf(buf + res, PAGE_SIZE - res,
- ",port=?,dir=?,cmp_l=?,cmp_h=?,mask=?");
- else
- res += snprintf(buf + res, PAGE_SIZE - res,
- ",bus=?");
-
- break;
- case CCN_TYPE_MN:
- res += snprintf(buf + res, PAGE_SIZE - res, ",node=%d", ccn->mn_id);
- break;
- default:
- res += snprintf(buf + res, PAGE_SIZE - res, ",node=?");
- break;
- }
-
- res += snprintf(buf + res, PAGE_SIZE - res, "\n");
-
- return res;
-}
-
-static umode_t arm_ccn_pmu_events_is_visible(struct kobject *kobj,
- struct attribute *attr, int index)
-{
- struct device *dev = kobj_to_dev(kobj);
- struct arm_ccn *ccn = pmu_to_arm_ccn(dev_get_drvdata(dev));
- struct device_attribute *dev_attr = container_of(attr,
- struct device_attribute, attr);
- struct arm_ccn_pmu_event *event = container_of(dev_attr,
- struct arm_ccn_pmu_event, attr);
-
- if (event->type == CCN_TYPE_SBAS && !ccn->sbas_present)
- return 0;
- if (event->type == CCN_TYPE_SBSX && !ccn->sbsx_present)
- return 0;
-
- return attr->mode;
-}
-
-static struct arm_ccn_pmu_event arm_ccn_pmu_events[] = {
- CCN_EVENT_MN(eobarrier, "dir=1,vc=0,cmp_h=0x1c00", CCN_IDX_MASK_OPCODE),
- CCN_EVENT_MN(ecbarrier, "dir=1,vc=0,cmp_h=0x1e00", CCN_IDX_MASK_OPCODE),
- CCN_EVENT_MN(dvmop, "dir=1,vc=0,cmp_h=0x2800", CCN_IDX_MASK_OPCODE),
- CCN_EVENT_HNI(txdatflits, "dir=1,vc=3", CCN_IDX_MASK_ANY),
- CCN_EVENT_HNI(rxdatflits, "dir=0,vc=3", CCN_IDX_MASK_ANY),
- CCN_EVENT_HNI(txreqflits, "dir=1,vc=0", CCN_IDX_MASK_ANY),
- CCN_EVENT_HNI(rxreqflits, "dir=0,vc=0", CCN_IDX_MASK_ANY),
- CCN_EVENT_HNI(rxreqflits_order, "dir=0,vc=0,cmp_h=0x8000",
- CCN_IDX_MASK_ORDER),
- CCN_EVENT_SBSX(txdatflits, "dir=1,vc=3", CCN_IDX_MASK_ANY),
- CCN_EVENT_SBSX(rxdatflits, "dir=0,vc=3", CCN_IDX_MASK_ANY),
- CCN_EVENT_SBSX(txreqflits, "dir=1,vc=0", CCN_IDX_MASK_ANY),
- CCN_EVENT_SBSX(rxreqflits, "dir=0,vc=0", CCN_IDX_MASK_ANY),
- CCN_EVENT_SBSX(rxreqflits_order, "dir=0,vc=0,cmp_h=0x8000",
- CCN_IDX_MASK_ORDER),
- CCN_EVENT_HNF(cache_miss, 0x1),
- CCN_EVENT_HNF(l3_sf_cache_access, 0x02),
- CCN_EVENT_HNF(cache_fill, 0x3),
- CCN_EVENT_HNF(pocq_retry, 0x4),
- CCN_EVENT_HNF(pocq_reqs_recvd, 0x5),
- CCN_EVENT_HNF(sf_hit, 0x6),
- CCN_EVENT_HNF(sf_evictions, 0x7),
- CCN_EVENT_HNF(snoops_sent, 0x8),
- CCN_EVENT_HNF(snoops_broadcast, 0x9),
- CCN_EVENT_HNF(l3_eviction, 0xa),
- CCN_EVENT_HNF(l3_fill_invalid_way, 0xb),
- CCN_EVENT_HNF(mc_retries, 0xc),
- CCN_EVENT_HNF(mc_reqs, 0xd),
- CCN_EVENT_HNF(qos_hh_retry, 0xe),
- CCN_EVENT_RNI(rdata_beats_p0, 0x1),
- CCN_EVENT_RNI(rdata_beats_p1, 0x2),
- CCN_EVENT_RNI(rdata_beats_p2, 0x3),
- CCN_EVENT_RNI(rxdat_flits, 0x4),
- CCN_EVENT_RNI(txdat_flits, 0x5),
- CCN_EVENT_RNI(txreq_flits, 0x6),
- CCN_EVENT_RNI(txreq_flits_retried, 0x7),
- CCN_EVENT_RNI(rrt_full, 0x8),
- CCN_EVENT_RNI(wrt_full, 0x9),
- CCN_EVENT_RNI(txreq_flits_replayed, 0xa),
- CCN_EVENT_XP(upload_starvation, 0x1),
- CCN_EVENT_XP(download_starvation, 0x2),
- CCN_EVENT_XP(respin, 0x3),
- CCN_EVENT_XP(valid_flit, 0x4),
- CCN_EVENT_XP(watchpoint, CCN_EVENT_WATCHPOINT),
- CCN_EVENT_SBAS(rdata_beats_p0, 0x1),
- CCN_EVENT_SBAS(rxdat_flits, 0x4),
- CCN_EVENT_SBAS(txdat_flits, 0x5),
- CCN_EVENT_SBAS(txreq_flits, 0x6),
- CCN_EVENT_SBAS(txreq_flits_retried, 0x7),
- CCN_EVENT_SBAS(rrt_full, 0x8),
- CCN_EVENT_SBAS(wrt_full, 0x9),
- CCN_EVENT_SBAS(txreq_flits_replayed, 0xa),
- CCN_EVENT_CYCLES(cycles),
-};
-
-/* Populated in arm_ccn_init() */
-static struct attribute
- *arm_ccn_pmu_events_attrs[ARRAY_SIZE(arm_ccn_pmu_events) + 1];
-
-static const struct attribute_group arm_ccn_pmu_events_attr_group = {
- .name = "events",
- .is_visible = arm_ccn_pmu_events_is_visible,
- .attrs = arm_ccn_pmu_events_attrs,
-};
-
-
-static u64 *arm_ccn_pmu_get_cmp_mask(struct arm_ccn *ccn, const char *name)
-{
- unsigned long i;
-
- if (WARN_ON(!name || !name[0] || !isxdigit(name[0]) || !name[1]))
- return NULL;
- i = isdigit(name[0]) ? name[0] - '0' : 0xa + tolower(name[0]) - 'a';
-
- switch (name[1]) {
- case 'l':
- return &ccn->dt.cmp_mask[i].l;
- case 'h':
- return &ccn->dt.cmp_mask[i].h;
- default:
- return NULL;
- }
-}
-
-static ssize_t arm_ccn_pmu_cmp_mask_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct arm_ccn *ccn = pmu_to_arm_ccn(dev_get_drvdata(dev));
- u64 *mask = arm_ccn_pmu_get_cmp_mask(ccn, attr->attr.name);
-
- return mask ? snprintf(buf, PAGE_SIZE, "0x%016llx\n", *mask) : -EINVAL;
-}
-
-static ssize_t arm_ccn_pmu_cmp_mask_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- struct arm_ccn *ccn = pmu_to_arm_ccn(dev_get_drvdata(dev));
- u64 *mask = arm_ccn_pmu_get_cmp_mask(ccn, attr->attr.name);
- int err = -EINVAL;
-
- if (mask)
- err = kstrtoull(buf, 0, mask);
-
- return err ? err : count;
-}
-
-#define CCN_CMP_MASK_ATTR(_name) \
- struct device_attribute arm_ccn_pmu_cmp_mask_attr_##_name = \
- __ATTR(_name, S_IRUGO | S_IWUSR, \
- arm_ccn_pmu_cmp_mask_show, arm_ccn_pmu_cmp_mask_store)
-
-#define CCN_CMP_MASK_ATTR_RO(_name) \
- struct device_attribute arm_ccn_pmu_cmp_mask_attr_##_name = \
- __ATTR(_name, S_IRUGO, arm_ccn_pmu_cmp_mask_show, NULL)
-
-static CCN_CMP_MASK_ATTR(0l);
-static CCN_CMP_MASK_ATTR(0h);
-static CCN_CMP_MASK_ATTR(1l);
-static CCN_CMP_MASK_ATTR(1h);
-static CCN_CMP_MASK_ATTR(2l);
-static CCN_CMP_MASK_ATTR(2h);
-static CCN_CMP_MASK_ATTR(3l);
-static CCN_CMP_MASK_ATTR(3h);
-static CCN_CMP_MASK_ATTR(4l);
-static CCN_CMP_MASK_ATTR(4h);
-static CCN_CMP_MASK_ATTR(5l);
-static CCN_CMP_MASK_ATTR(5h);
-static CCN_CMP_MASK_ATTR(6l);
-static CCN_CMP_MASK_ATTR(6h);
-static CCN_CMP_MASK_ATTR(7l);
-static CCN_CMP_MASK_ATTR(7h);
-static CCN_CMP_MASK_ATTR_RO(8l);
-static CCN_CMP_MASK_ATTR_RO(8h);
-static CCN_CMP_MASK_ATTR_RO(9l);
-static CCN_CMP_MASK_ATTR_RO(9h);
-static CCN_CMP_MASK_ATTR_RO(al);
-static CCN_CMP_MASK_ATTR_RO(ah);
-static CCN_CMP_MASK_ATTR_RO(bl);
-static CCN_CMP_MASK_ATTR_RO(bh);
-
-static struct attribute *arm_ccn_pmu_cmp_mask_attrs[] = {
- &arm_ccn_pmu_cmp_mask_attr_0l.attr, &arm_ccn_pmu_cmp_mask_attr_0h.attr,
- &arm_ccn_pmu_cmp_mask_attr_1l.attr, &arm_ccn_pmu_cmp_mask_attr_1h.attr,
- &arm_ccn_pmu_cmp_mask_attr_2l.attr, &arm_ccn_pmu_cmp_mask_attr_2h.attr,
- &arm_ccn_pmu_cmp_mask_attr_3l.attr, &arm_ccn_pmu_cmp_mask_attr_3h.attr,
- &arm_ccn_pmu_cmp_mask_attr_4l.attr, &arm_ccn_pmu_cmp_mask_attr_4h.attr,
- &arm_ccn_pmu_cmp_mask_attr_5l.attr, &arm_ccn_pmu_cmp_mask_attr_5h.attr,
- &arm_ccn_pmu_cmp_mask_attr_6l.attr, &arm_ccn_pmu_cmp_mask_attr_6h.attr,
- &arm_ccn_pmu_cmp_mask_attr_7l.attr, &arm_ccn_pmu_cmp_mask_attr_7h.attr,
- &arm_ccn_pmu_cmp_mask_attr_8l.attr, &arm_ccn_pmu_cmp_mask_attr_8h.attr,
- &arm_ccn_pmu_cmp_mask_attr_9l.attr, &arm_ccn_pmu_cmp_mask_attr_9h.attr,
- &arm_ccn_pmu_cmp_mask_attr_al.attr, &arm_ccn_pmu_cmp_mask_attr_ah.attr,
- &arm_ccn_pmu_cmp_mask_attr_bl.attr, &arm_ccn_pmu_cmp_mask_attr_bh.attr,
- NULL
-};
-
-static const struct attribute_group arm_ccn_pmu_cmp_mask_attr_group = {
- .name = "cmp_mask",
- .attrs = arm_ccn_pmu_cmp_mask_attrs,
-};
-
-static ssize_t arm_ccn_pmu_cpumask_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct arm_ccn *ccn = pmu_to_arm_ccn(dev_get_drvdata(dev));
-
- return cpumap_print_to_pagebuf(true, buf, &ccn->dt.cpu);
-}
-
-static struct device_attribute arm_ccn_pmu_cpumask_attr =
- __ATTR(cpumask, S_IRUGO, arm_ccn_pmu_cpumask_show, NULL);
-
-static struct attribute *arm_ccn_pmu_cpumask_attrs[] = {
- &arm_ccn_pmu_cpumask_attr.attr,
- NULL,
-};
-
-static const struct attribute_group arm_ccn_pmu_cpumask_attr_group = {
- .attrs = arm_ccn_pmu_cpumask_attrs,
-};
-
-/*
- * Default poll period is 10ms, which is way over the top anyway,
- * as in the worst case scenario (an event every cycle), with 1GHz
- * clocked bus, the smallest, 32 bit counter will overflow in
- * more than 4s.
- */
-static unsigned int arm_ccn_pmu_poll_period_us = 10000;
-module_param_named(pmu_poll_period_us, arm_ccn_pmu_poll_period_us, uint,
- S_IRUGO | S_IWUSR);
-
-static ktime_t arm_ccn_pmu_timer_period(void)
-{
- return ns_to_ktime((u64)arm_ccn_pmu_poll_period_us * 1000);
-}
-
-
-static const struct attribute_group *arm_ccn_pmu_attr_groups[] = {
- &arm_ccn_pmu_events_attr_group,
- &arm_ccn_pmu_format_attr_group,
- &arm_ccn_pmu_cmp_mask_attr_group,
- &arm_ccn_pmu_cpumask_attr_group,
- NULL
-};
-
-
-static int arm_ccn_pmu_alloc_bit(unsigned long *bitmap, unsigned long size)
-{
- int bit;
-
- do {
- bit = find_first_zero_bit(bitmap, size);
- if (bit >= size)
- return -EAGAIN;
- } while (test_and_set_bit(bit, bitmap));
-
- return bit;
-}
-
-/* All RN-I and RN-D nodes have identical PMUs */
-static int arm_ccn_pmu_type_eq(u32 a, u32 b)
-{
- if (a == b)
- return 1;
-
- switch (a) {
- case CCN_TYPE_RNI_1P:
- case CCN_TYPE_RNI_2P:
- case CCN_TYPE_RNI_3P:
- case CCN_TYPE_RND_1P:
- case CCN_TYPE_RND_2P:
- case CCN_TYPE_RND_3P:
- switch (b) {
- case CCN_TYPE_RNI_1P:
- case CCN_TYPE_RNI_2P:
- case CCN_TYPE_RNI_3P:
- case CCN_TYPE_RND_1P:
- case CCN_TYPE_RND_2P:
- case CCN_TYPE_RND_3P:
- return 1;
- }
- break;
- }
-
- return 0;
-}
-
-static int arm_ccn_pmu_event_alloc(struct perf_event *event)
-{
- struct arm_ccn *ccn = pmu_to_arm_ccn(event->pmu);
- struct hw_perf_event *hw = &event->hw;
- u32 node_xp, type, event_id;
- struct arm_ccn_component *source;
- int bit;
-
- node_xp = CCN_CONFIG_NODE(event->attr.config);
- type = CCN_CONFIG_TYPE(event->attr.config);
- event_id = CCN_CONFIG_EVENT(event->attr.config);
-
- /* Allocate the cycle counter */
- if (type == CCN_TYPE_CYCLES) {
- if (test_and_set_bit(CCN_IDX_PMU_CYCLE_COUNTER,
- ccn->dt.pmu_counters_mask))
- return -EAGAIN;
-
- hw->idx = CCN_IDX_PMU_CYCLE_COUNTER;
- ccn->dt.pmu_counters[CCN_IDX_PMU_CYCLE_COUNTER].event = event;
-
- return 0;
- }
-
- /* Allocate an event counter */
- hw->idx = arm_ccn_pmu_alloc_bit(ccn->dt.pmu_counters_mask,
- CCN_NUM_PMU_EVENT_COUNTERS);
- if (hw->idx < 0) {
- dev_dbg(ccn->dev, "No more counters available!\n");
- return -EAGAIN;
- }
-
- if (type == CCN_TYPE_XP)
- source = &ccn->xp[node_xp];
- else
- source = &ccn->node[node_xp];
- ccn->dt.pmu_counters[hw->idx].source = source;
-
- /* Allocate an event source or a watchpoint */
- if (type == CCN_TYPE_XP && event_id == CCN_EVENT_WATCHPOINT)
- bit = arm_ccn_pmu_alloc_bit(source->xp.dt_cmp_mask,
- CCN_NUM_XP_WATCHPOINTS);
- else
- bit = arm_ccn_pmu_alloc_bit(source->pmu_events_mask,
- CCN_NUM_PMU_EVENTS);
- if (bit < 0) {
- dev_dbg(ccn->dev, "No more event sources/watchpoints on node/XP %d!\n",
- node_xp);
- clear_bit(hw->idx, ccn->dt.pmu_counters_mask);
- return -EAGAIN;
- }
- hw->config_base = bit;
-
- ccn->dt.pmu_counters[hw->idx].event = event;
-
- return 0;
-}
-
-static void arm_ccn_pmu_event_release(struct perf_event *event)
-{
- struct arm_ccn *ccn = pmu_to_arm_ccn(event->pmu);
- struct hw_perf_event *hw = &event->hw;
-
- if (hw->idx == CCN_IDX_PMU_CYCLE_COUNTER) {
- clear_bit(CCN_IDX_PMU_CYCLE_COUNTER, ccn->dt.pmu_counters_mask);
- } else {
- struct arm_ccn_component *source =
- ccn->dt.pmu_counters[hw->idx].source;
-
- if (CCN_CONFIG_TYPE(event->attr.config) == CCN_TYPE_XP &&
- CCN_CONFIG_EVENT(event->attr.config) ==
- CCN_EVENT_WATCHPOINT)
- clear_bit(hw->config_base, source->xp.dt_cmp_mask);
- else
- clear_bit(hw->config_base, source->pmu_events_mask);
- clear_bit(hw->idx, ccn->dt.pmu_counters_mask);
- }
-
- ccn->dt.pmu_counters[hw->idx].source = NULL;
- ccn->dt.pmu_counters[hw->idx].event = NULL;
-}
-
-static int arm_ccn_pmu_event_init(struct perf_event *event)
-{
- struct arm_ccn *ccn;
- struct hw_perf_event *hw = &event->hw;
- u32 node_xp, type, event_id;
- int valid;
- int i;
- struct perf_event *sibling;
-
- if (event->attr.type != event->pmu->type)
- return -ENOENT;
-
- ccn = pmu_to_arm_ccn(event->pmu);
-
- if (hw->sample_period) {
- dev_warn(ccn->dev, "Sampling not supported!\n");
- return -EOPNOTSUPP;
- }
-
- if (has_branch_stack(event) || event->attr.exclude_user ||
- event->attr.exclude_kernel || event->attr.exclude_hv ||
- event->attr.exclude_idle || event->attr.exclude_host ||
- event->attr.exclude_guest) {
- dev_warn(ccn->dev, "Can't exclude execution levels!\n");
- return -EINVAL;
- }
-
- if (event->cpu < 0) {
- dev_warn(ccn->dev, "Can't provide per-task data!\n");
- return -EOPNOTSUPP;
- }
- /*
- * Many perf core operations (eg. events rotation) operate on a
- * single CPU context. This is obvious for CPU PMUs, where one
- * expects the same sets of events being observed on all CPUs,
- * but can lead to issues for off-core PMUs, like CCN, where each
- * event could be theoretically assigned to a different CPU. To
- * mitigate this, we enforce CPU assignment to one, selected
- * processor (the one described in the "cpumask" attribute).
- */
- event->cpu = cpumask_first(&ccn->dt.cpu);
-
- node_xp = CCN_CONFIG_NODE(event->attr.config);
- type = CCN_CONFIG_TYPE(event->attr.config);
- event_id = CCN_CONFIG_EVENT(event->attr.config);
-
- /* Validate node/xp vs topology */
- switch (type) {
- case CCN_TYPE_MN:
- if (node_xp != ccn->mn_id) {
- dev_warn(ccn->dev, "Invalid MN ID %d!\n", node_xp);
- return -EINVAL;
- }
- break;
- case CCN_TYPE_XP:
- if (node_xp >= ccn->num_xps) {
- dev_warn(ccn->dev, "Invalid XP ID %d!\n", node_xp);
- return -EINVAL;
- }
- break;
- case CCN_TYPE_CYCLES:
- break;
- default:
- if (node_xp >= ccn->num_nodes) {
- dev_warn(ccn->dev, "Invalid node ID %d!\n", node_xp);
- return -EINVAL;
- }
- if (!arm_ccn_pmu_type_eq(type, ccn->node[node_xp].type)) {
- dev_warn(ccn->dev, "Invalid type 0x%x for node %d!\n",
- type, node_xp);
- return -EINVAL;
- }
- break;
- }
-
- /* Validate event ID vs available for the type */
- for (i = 0, valid = 0; i < ARRAY_SIZE(arm_ccn_pmu_events) && !valid;
- i++) {
- struct arm_ccn_pmu_event *e = &arm_ccn_pmu_events[i];
- u32 port = CCN_CONFIG_PORT(event->attr.config);
- u32 vc = CCN_CONFIG_VC(event->attr.config);
-
- if (!arm_ccn_pmu_type_eq(type, e->type))
- continue;
- if (event_id != e->event)
- continue;
- if (e->num_ports && port >= e->num_ports) {
- dev_warn(ccn->dev, "Invalid port %d for node/XP %d!\n",
- port, node_xp);
- return -EINVAL;
- }
- if (e->num_vcs && vc >= e->num_vcs) {
- dev_warn(ccn->dev, "Invalid vc %d for node/XP %d!\n",
- vc, node_xp);
- return -EINVAL;
- }
- valid = 1;
- }
- if (!valid) {
- dev_warn(ccn->dev, "Invalid event 0x%x for node/XP %d!\n",
- event_id, node_xp);
- return -EINVAL;
- }
-
- /* Watchpoint-based event for a node is actually set on XP */
- if (event_id == CCN_EVENT_WATCHPOINT && type != CCN_TYPE_XP) {
- u32 port;
-
- type = CCN_TYPE_XP;
- port = arm_ccn_node_to_xp_port(node_xp);
- node_xp = arm_ccn_node_to_xp(node_xp);
-
- arm_ccn_pmu_config_set(&event->attr.config,
- node_xp, type, port);
- }
-
- /*
- * We must NOT create groups containing mixed PMUs, although software
- * events are acceptable (for example to create a CCN group
- * periodically read when a hrtimer aka cpu-clock leader triggers).
- */
- if (event->group_leader->pmu != event->pmu &&
- !is_software_event(event->group_leader))
- return -EINVAL;
-
- list_for_each_entry(sibling, &event->group_leader->sibling_list,
- group_entry)
- if (sibling->pmu != event->pmu &&
- !is_software_event(sibling))
- return -EINVAL;
-
- return 0;
-}
-
-static u64 arm_ccn_pmu_read_counter(struct arm_ccn *ccn, int idx)
-{
- u64 res;
-
- if (idx == CCN_IDX_PMU_CYCLE_COUNTER) {
-#ifdef readq
- res = readq(ccn->dt.base + CCN_DT_PMCCNTR);
-#else
- /* 40 bit counter, can do snapshot and read in two parts */
- writel(0x1, ccn->dt.base + CCN_DT_PMSR_REQ);
- while (!(readl(ccn->dt.base + CCN_DT_PMSR) & 0x1))
- ;
- writel(0x1, ccn->dt.base + CCN_DT_PMSR_CLR);
- res = readl(ccn->dt.base + CCN_DT_PMCCNTRSR + 4) & 0xff;
- res <<= 32;
- res |= readl(ccn->dt.base + CCN_DT_PMCCNTRSR);
-#endif
- } else {
- res = readl(ccn->dt.base + CCN_DT_PMEVCNT(idx));
- }
-
- return res;
-}
-
-static void arm_ccn_pmu_event_update(struct perf_event *event)
-{
- struct arm_ccn *ccn = pmu_to_arm_ccn(event->pmu);
- struct hw_perf_event *hw = &event->hw;
- u64 prev_count, new_count, mask;
-
- do {
- prev_count = local64_read(&hw->prev_count);
- new_count = arm_ccn_pmu_read_counter(ccn, hw->idx);
- } while (local64_xchg(&hw->prev_count, new_count) != prev_count);
-
- mask = (1LLU << (hw->idx == CCN_IDX_PMU_CYCLE_COUNTER ? 40 : 32)) - 1;
-
- local64_add((new_count - prev_count) & mask, &event->count);
-}
-
-static void arm_ccn_pmu_xp_dt_config(struct perf_event *event, int enable)
-{
- struct arm_ccn *ccn = pmu_to_arm_ccn(event->pmu);
- struct hw_perf_event *hw = &event->hw;
- struct arm_ccn_component *xp;
- u32 val, dt_cfg;
-
- /* Nothing to do for cycle counter */
- if (hw->idx == CCN_IDX_PMU_CYCLE_COUNTER)
- return;
-
- if (CCN_CONFIG_TYPE(event->attr.config) == CCN_TYPE_XP)
- xp = &ccn->xp[CCN_CONFIG_XP(event->attr.config)];
- else
- xp = &ccn->xp[arm_ccn_node_to_xp(
- CCN_CONFIG_NODE(event->attr.config))];
-
- if (enable)
- dt_cfg = hw->event_base;
- else
- dt_cfg = CCN_XP_DT_CONFIG__DT_CFG__PASS_THROUGH;
-
- spin_lock(&ccn->dt.config_lock);
-
- val = readl(xp->base + CCN_XP_DT_CONFIG);
- val &= ~(CCN_XP_DT_CONFIG__DT_CFG__MASK <<
- CCN_XP_DT_CONFIG__DT_CFG__SHIFT(hw->idx));
- val |= dt_cfg << CCN_XP_DT_CONFIG__DT_CFG__SHIFT(hw->idx);
- writel(val, xp->base + CCN_XP_DT_CONFIG);
-
- spin_unlock(&ccn->dt.config_lock);
-}
-
-static void arm_ccn_pmu_event_start(struct perf_event *event, int flags)
-{
- struct arm_ccn *ccn = pmu_to_arm_ccn(event->pmu);
- struct hw_perf_event *hw = &event->hw;
-
- local64_set(&event->hw.prev_count,
- arm_ccn_pmu_read_counter(ccn, hw->idx));
- hw->state = 0;
-
- /* Set the DT bus input, engaging the counter */
- arm_ccn_pmu_xp_dt_config(event, 1);
-}
-
-static void arm_ccn_pmu_event_stop(struct perf_event *event, int flags)
-{
- struct hw_perf_event *hw = &event->hw;
-
- /* Disable counting, setting the DT bus to pass-through mode */
- arm_ccn_pmu_xp_dt_config(event, 0);
-
- if (flags & PERF_EF_UPDATE)
- arm_ccn_pmu_event_update(event);
-
- hw->state |= PERF_HES_STOPPED;
-}
-
-static void arm_ccn_pmu_xp_watchpoint_config(struct perf_event *event)
-{
- struct arm_ccn *ccn = pmu_to_arm_ccn(event->pmu);
- struct hw_perf_event *hw = &event->hw;
- struct arm_ccn_component *source =
- ccn->dt.pmu_counters[hw->idx].source;
- unsigned long wp = hw->config_base;
- u32 val;
- u64 cmp_l = event->attr.config1;
- u64 cmp_h = event->attr.config2;
- u64 mask_l = ccn->dt.cmp_mask[CCN_CONFIG_MASK(event->attr.config)].l;
- u64 mask_h = ccn->dt.cmp_mask[CCN_CONFIG_MASK(event->attr.config)].h;
-
- hw->event_base = CCN_XP_DT_CONFIG__DT_CFG__WATCHPOINT(wp);
-
- /* Direction (RX/TX), device (port) & virtual channel */
- val = readl(source->base + CCN_XP_DT_INTERFACE_SEL);
- val &= ~(CCN_XP_DT_INTERFACE_SEL__DT_IO_SEL__MASK <<
- CCN_XP_DT_INTERFACE_SEL__DT_IO_SEL__SHIFT(wp));
- val |= CCN_CONFIG_DIR(event->attr.config) <<
- CCN_XP_DT_INTERFACE_SEL__DT_IO_SEL__SHIFT(wp);
- val &= ~(CCN_XP_DT_INTERFACE_SEL__DT_DEV_SEL__MASK <<
- CCN_XP_DT_INTERFACE_SEL__DT_DEV_SEL__SHIFT(wp));
- val |= CCN_CONFIG_PORT(event->attr.config) <<
- CCN_XP_DT_INTERFACE_SEL__DT_DEV_SEL__SHIFT(wp);
- val &= ~(CCN_XP_DT_INTERFACE_SEL__DT_VC_SEL__MASK <<
- CCN_XP_DT_INTERFACE_SEL__DT_VC_SEL__SHIFT(wp));
- val |= CCN_CONFIG_VC(event->attr.config) <<
- CCN_XP_DT_INTERFACE_SEL__DT_VC_SEL__SHIFT(wp);
- writel(val, source->base + CCN_XP_DT_INTERFACE_SEL);
-
- /* Comparison values */
- writel(cmp_l & 0xffffffff, source->base + CCN_XP_DT_CMP_VAL_L(wp));
- writel((cmp_l >> 32) & 0x7fffffff,
- source->base + CCN_XP_DT_CMP_VAL_L(wp) + 4);
- writel(cmp_h & 0xffffffff, source->base + CCN_XP_DT_CMP_VAL_H(wp));
- writel((cmp_h >> 32) & 0x0fffffff,
- source->base + CCN_XP_DT_CMP_VAL_H(wp) + 4);
-
- /* Mask */
- writel(mask_l & 0xffffffff, source->base + CCN_XP_DT_CMP_MASK_L(wp));
- writel((mask_l >> 32) & 0x7fffffff,
- source->base + CCN_XP_DT_CMP_MASK_L(wp) + 4);
- writel(mask_h & 0xffffffff, source->base + CCN_XP_DT_CMP_MASK_H(wp));
- writel((mask_h >> 32) & 0x0fffffff,
- source->base + CCN_XP_DT_CMP_MASK_H(wp) + 4);
-}
-
-static void arm_ccn_pmu_xp_event_config(struct perf_event *event)
-{
- struct arm_ccn *ccn = pmu_to_arm_ccn(event->pmu);
- struct hw_perf_event *hw = &event->hw;
- struct arm_ccn_component *source =
- ccn->dt.pmu_counters[hw->idx].source;
- u32 val, id;
-
- hw->event_base = CCN_XP_DT_CONFIG__DT_CFG__XP_PMU_EVENT(hw->config_base);
-
- id = (CCN_CONFIG_VC(event->attr.config) << 4) |
- (CCN_CONFIG_BUS(event->attr.config) << 3) |
- (CCN_CONFIG_EVENT(event->attr.config) << 0);
-
- val = readl(source->base + CCN_XP_PMU_EVENT_SEL);
- val &= ~(CCN_XP_PMU_EVENT_SEL__ID__MASK <<
- CCN_XP_PMU_EVENT_SEL__ID__SHIFT(hw->config_base));
- val |= id << CCN_XP_PMU_EVENT_SEL__ID__SHIFT(hw->config_base);
- writel(val, source->base + CCN_XP_PMU_EVENT_SEL);
-}
-
-static void arm_ccn_pmu_node_event_config(struct perf_event *event)
-{
- struct arm_ccn *ccn = pmu_to_arm_ccn(event->pmu);
- struct hw_perf_event *hw = &event->hw;
- struct arm_ccn_component *source =
- ccn->dt.pmu_counters[hw->idx].source;
- u32 type = CCN_CONFIG_TYPE(event->attr.config);
- u32 val, port;
-
- port = arm_ccn_node_to_xp_port(CCN_CONFIG_NODE(event->attr.config));
- hw->event_base = CCN_XP_DT_CONFIG__DT_CFG__DEVICE_PMU_EVENT(port,
- hw->config_base);
-
- /* These *_event_sel regs should be identical, but let's make sure... */
- BUILD_BUG_ON(CCN_HNF_PMU_EVENT_SEL != CCN_SBAS_PMU_EVENT_SEL);
- BUILD_BUG_ON(CCN_SBAS_PMU_EVENT_SEL != CCN_RNI_PMU_EVENT_SEL);
- BUILD_BUG_ON(CCN_HNF_PMU_EVENT_SEL__ID__SHIFT(1) !=
- CCN_SBAS_PMU_EVENT_SEL__ID__SHIFT(1));
- BUILD_BUG_ON(CCN_SBAS_PMU_EVENT_SEL__ID__SHIFT(1) !=
- CCN_RNI_PMU_EVENT_SEL__ID__SHIFT(1));
- BUILD_BUG_ON(CCN_HNF_PMU_EVENT_SEL__ID__MASK !=
- CCN_SBAS_PMU_EVENT_SEL__ID__MASK);
- BUILD_BUG_ON(CCN_SBAS_PMU_EVENT_SEL__ID__MASK !=
- CCN_RNI_PMU_EVENT_SEL__ID__MASK);
- if (WARN_ON(type != CCN_TYPE_HNF && type != CCN_TYPE_SBAS &&
- !arm_ccn_pmu_type_eq(type, CCN_TYPE_RNI_3P)))
- return;
-
- /* Set the event id for the pre-allocated counter */
- val = readl(source->base + CCN_HNF_PMU_EVENT_SEL);
- val &= ~(CCN_HNF_PMU_EVENT_SEL__ID__MASK <<
- CCN_HNF_PMU_EVENT_SEL__ID__SHIFT(hw->config_base));
- val |= CCN_CONFIG_EVENT(event->attr.config) <<
- CCN_HNF_PMU_EVENT_SEL__ID__SHIFT(hw->config_base);
- writel(val, source->base + CCN_HNF_PMU_EVENT_SEL);
-}
-
-static void arm_ccn_pmu_event_config(struct perf_event *event)
-{
- struct arm_ccn *ccn = pmu_to_arm_ccn(event->pmu);
- struct hw_perf_event *hw = &event->hw;
- u32 xp, offset, val;
-
- /* Cycle counter requires no setup */
- if (hw->idx == CCN_IDX_PMU_CYCLE_COUNTER)
- return;
-
- if (CCN_CONFIG_TYPE(event->attr.config) == CCN_TYPE_XP)
- xp = CCN_CONFIG_XP(event->attr.config);
- else
- xp = arm_ccn_node_to_xp(CCN_CONFIG_NODE(event->attr.config));
-
- spin_lock(&ccn->dt.config_lock);
-
- /* Set the DT bus "distance" register */
- offset = (hw->idx / 4) * 4;
- val = readl(ccn->dt.base + CCN_DT_ACTIVE_DSM + offset);
- val &= ~(CCN_DT_ACTIVE_DSM__DSM_ID__MASK <<
- CCN_DT_ACTIVE_DSM__DSM_ID__SHIFT(hw->idx % 4));
- val |= xp << CCN_DT_ACTIVE_DSM__DSM_ID__SHIFT(hw->idx % 4);
- writel(val, ccn->dt.base + CCN_DT_ACTIVE_DSM + offset);
-
- if (CCN_CONFIG_TYPE(event->attr.config) == CCN_TYPE_XP) {
- if (CCN_CONFIG_EVENT(event->attr.config) ==
- CCN_EVENT_WATCHPOINT)
- arm_ccn_pmu_xp_watchpoint_config(event);
- else
- arm_ccn_pmu_xp_event_config(event);
- } else {
- arm_ccn_pmu_node_event_config(event);
- }
-
- spin_unlock(&ccn->dt.config_lock);
-}
-
-static int arm_ccn_pmu_active_counters(struct arm_ccn *ccn)
-{
- return bitmap_weight(ccn->dt.pmu_counters_mask,
- CCN_NUM_PMU_EVENT_COUNTERS + 1);
-}
-
-static int arm_ccn_pmu_event_add(struct perf_event *event, int flags)
-{
- int err;
- struct hw_perf_event *hw = &event->hw;
- struct arm_ccn *ccn = pmu_to_arm_ccn(event->pmu);
-
- err = arm_ccn_pmu_event_alloc(event);
- if (err)
- return err;
-
- /*
- * Pin the timer, so that the overflows are handled by the chosen
- * event->cpu (this is the same one as presented in "cpumask"
- * attribute).
- */
- if (!ccn->irq && arm_ccn_pmu_active_counters(ccn) == 1)
- hrtimer_start(&ccn->dt.hrtimer, arm_ccn_pmu_timer_period(),
- HRTIMER_MODE_REL_PINNED);
-
- arm_ccn_pmu_event_config(event);
-
- hw->state = PERF_HES_STOPPED;
-
- if (flags & PERF_EF_START)
- arm_ccn_pmu_event_start(event, PERF_EF_UPDATE);
-
- return 0;
-}
-
-static void arm_ccn_pmu_event_del(struct perf_event *event, int flags)
-{
- struct arm_ccn *ccn = pmu_to_arm_ccn(event->pmu);
-
- arm_ccn_pmu_event_stop(event, PERF_EF_UPDATE);
-
- arm_ccn_pmu_event_release(event);
-
- if (!ccn->irq && arm_ccn_pmu_active_counters(ccn) == 0)
- hrtimer_cancel(&ccn->dt.hrtimer);
-}
-
-static void arm_ccn_pmu_event_read(struct perf_event *event)
-{
- arm_ccn_pmu_event_update(event);
-}
-
-static void arm_ccn_pmu_enable(struct pmu *pmu)
-{
- struct arm_ccn *ccn = pmu_to_arm_ccn(pmu);
-
- u32 val = readl(ccn->dt.base + CCN_DT_PMCR);
- val |= CCN_DT_PMCR__PMU_EN;
- writel(val, ccn->dt.base + CCN_DT_PMCR);
-}
-
-static void arm_ccn_pmu_disable(struct pmu *pmu)
-{
- struct arm_ccn *ccn = pmu_to_arm_ccn(pmu);
-
- u32 val = readl(ccn->dt.base + CCN_DT_PMCR);
- val &= ~CCN_DT_PMCR__PMU_EN;
- writel(val, ccn->dt.base + CCN_DT_PMCR);
-}
-
-static irqreturn_t arm_ccn_pmu_overflow_handler(struct arm_ccn_dt *dt)
-{
- u32 pmovsr = readl(dt->base + CCN_DT_PMOVSR);
- int idx;
-
- if (!pmovsr)
- return IRQ_NONE;
-
- writel(pmovsr, dt->base + CCN_DT_PMOVSR_CLR);
-
- BUILD_BUG_ON(CCN_IDX_PMU_CYCLE_COUNTER != CCN_NUM_PMU_EVENT_COUNTERS);
-
- for (idx = 0; idx < CCN_NUM_PMU_EVENT_COUNTERS + 1; idx++) {
- struct perf_event *event = dt->pmu_counters[idx].event;
- int overflowed = pmovsr & BIT(idx);
-
- WARN_ON_ONCE(overflowed && !event &&
- idx != CCN_IDX_PMU_CYCLE_COUNTER);
-
- if (!event || !overflowed)
- continue;
-
- arm_ccn_pmu_event_update(event);
- }
-
- return IRQ_HANDLED;
-}
-
-static enum hrtimer_restart arm_ccn_pmu_timer_handler(struct hrtimer *hrtimer)
-{
- struct arm_ccn_dt *dt = container_of(hrtimer, struct arm_ccn_dt,
- hrtimer);
- unsigned long flags;
-
- local_irq_save(flags);
- arm_ccn_pmu_overflow_handler(dt);
- local_irq_restore(flags);
-
- hrtimer_forward_now(hrtimer, arm_ccn_pmu_timer_period());
- return HRTIMER_RESTART;
-}
-
-
-static int arm_ccn_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node)
-{
- struct arm_ccn_dt *dt = hlist_entry_safe(node, struct arm_ccn_dt, node);
- struct arm_ccn *ccn = container_of(dt, struct arm_ccn, dt);
- unsigned int target;
-
- if (!cpumask_test_and_clear_cpu(cpu, &dt->cpu))
- return 0;
- target = cpumask_any_but(cpu_online_mask, cpu);
- if (target >= nr_cpu_ids)
- return 0;
- perf_pmu_migrate_context(&dt->pmu, cpu, target);
- cpumask_set_cpu(target, &dt->cpu);
- if (ccn->irq)
- WARN_ON(irq_set_affinity_hint(ccn->irq, &dt->cpu) != 0);
- return 0;
-}
-
-static DEFINE_IDA(arm_ccn_pmu_ida);
-
-static int arm_ccn_pmu_init(struct arm_ccn *ccn)
-{
- int i;
- char *name;
- int err;
-
- /* Initialize DT subsystem */
- ccn->dt.base = ccn->base + CCN_REGION_SIZE;
- spin_lock_init(&ccn->dt.config_lock);
- writel(CCN_DT_PMOVSR_CLR__MASK, ccn->dt.base + CCN_DT_PMOVSR_CLR);
- writel(CCN_DT_CTL__DT_EN, ccn->dt.base + CCN_DT_CTL);
- writel(CCN_DT_PMCR__OVFL_INTR_EN | CCN_DT_PMCR__PMU_EN,
- ccn->dt.base + CCN_DT_PMCR);
- writel(0x1, ccn->dt.base + CCN_DT_PMSR_CLR);
- for (i = 0; i < ccn->num_xps; i++) {
- writel(0, ccn->xp[i].base + CCN_XP_DT_CONFIG);
- writel((CCN_XP_DT_CONTROL__WP_ARM_SEL__ALWAYS <<
- CCN_XP_DT_CONTROL__WP_ARM_SEL__SHIFT(0)) |
- (CCN_XP_DT_CONTROL__WP_ARM_SEL__ALWAYS <<
- CCN_XP_DT_CONTROL__WP_ARM_SEL__SHIFT(1)) |
- CCN_XP_DT_CONTROL__DT_ENABLE,
- ccn->xp[i].base + CCN_XP_DT_CONTROL);
- }
- ccn->dt.cmp_mask[CCN_IDX_MASK_ANY].l = ~0;
- ccn->dt.cmp_mask[CCN_IDX_MASK_ANY].h = ~0;
- ccn->dt.cmp_mask[CCN_IDX_MASK_EXACT].l = 0;
- ccn->dt.cmp_mask[CCN_IDX_MASK_EXACT].h = 0;
- ccn->dt.cmp_mask[CCN_IDX_MASK_ORDER].l = ~0;
- ccn->dt.cmp_mask[CCN_IDX_MASK_ORDER].h = ~(0x1 << 15);
- ccn->dt.cmp_mask[CCN_IDX_MASK_OPCODE].l = ~0;
- ccn->dt.cmp_mask[CCN_IDX_MASK_OPCODE].h = ~(0x1f << 9);
-
- /* Get a convenient /sys/event_source/devices/ name */
- ccn->dt.id = ida_simple_get(&arm_ccn_pmu_ida, 0, 0, GFP_KERNEL);
- if (ccn->dt.id == 0) {
- name = "ccn";
- } else {
- name = devm_kasprintf(ccn->dev, GFP_KERNEL, "ccn_%d",
- ccn->dt.id);
- if (!name) {
- err = -ENOMEM;
- goto error_choose_name;
- }
- }
-
- /* Perf driver registration */
- ccn->dt.pmu = (struct pmu) {
- .module = THIS_MODULE,
- .attr_groups = arm_ccn_pmu_attr_groups,
- .task_ctx_nr = perf_invalid_context,
- .event_init = arm_ccn_pmu_event_init,
- .add = arm_ccn_pmu_event_add,
- .del = arm_ccn_pmu_event_del,
- .start = arm_ccn_pmu_event_start,
- .stop = arm_ccn_pmu_event_stop,
- .read = arm_ccn_pmu_event_read,
- .pmu_enable = arm_ccn_pmu_enable,
- .pmu_disable = arm_ccn_pmu_disable,
- };
-
- /* No overflow interrupt? Have to use a timer instead. */
- if (!ccn->irq) {
- dev_info(ccn->dev, "No access to interrupts, using timer.\n");
- hrtimer_init(&ccn->dt.hrtimer, CLOCK_MONOTONIC,
- HRTIMER_MODE_REL);
- ccn->dt.hrtimer.function = arm_ccn_pmu_timer_handler;
- }
-
- /* Pick one CPU which we will use to collect data from CCN... */
- cpumask_set_cpu(get_cpu(), &ccn->dt.cpu);
-
- /* Also make sure that the overflow interrupt is handled by this CPU */
- if (ccn->irq) {
- err = irq_set_affinity_hint(ccn->irq, &ccn->dt.cpu);
- if (err) {
- dev_err(ccn->dev, "Failed to set interrupt affinity!\n");
- goto error_set_affinity;
- }
- }
-
- err = perf_pmu_register(&ccn->dt.pmu, name, -1);
- if (err)
- goto error_pmu_register;
-
- cpuhp_state_add_instance_nocalls(CPUHP_AP_PERF_ARM_CCN_ONLINE,
- &ccn->dt.node);
- put_cpu();
- return 0;
-
-error_pmu_register:
-error_set_affinity:
- put_cpu();
-error_choose_name:
- ida_simple_remove(&arm_ccn_pmu_ida, ccn->dt.id);
- for (i = 0; i < ccn->num_xps; i++)
- writel(0, ccn->xp[i].base + CCN_XP_DT_CONTROL);
- writel(0, ccn->dt.base + CCN_DT_PMCR);
- return err;
-}
-
-static void arm_ccn_pmu_cleanup(struct arm_ccn *ccn)
-{
- int i;
-
- cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_CCN_ONLINE,
- &ccn->dt.node);
- if (ccn->irq)
- irq_set_affinity_hint(ccn->irq, NULL);
- for (i = 0; i < ccn->num_xps; i++)
- writel(0, ccn->xp[i].base + CCN_XP_DT_CONTROL);
- writel(0, ccn->dt.base + CCN_DT_PMCR);
- perf_pmu_unregister(&ccn->dt.pmu);
- ida_simple_remove(&arm_ccn_pmu_ida, ccn->dt.id);
-}
-
-static int arm_ccn_for_each_valid_region(struct arm_ccn *ccn,
- int (*callback)(struct arm_ccn *ccn, int region,
- void __iomem *base, u32 type, u32 id))
-{
- int region;
-
- for (region = 0; region < CCN_NUM_REGIONS; region++) {
- u32 val, type, id;
- void __iomem *base;
- int err;
-
- val = readl(ccn->base + CCN_MN_OLY_COMP_LIST_63_0 +
- 4 * (region / 32));
- if (!(val & (1 << (region % 32))))
- continue;
-
- base = ccn->base + region * CCN_REGION_SIZE;
- val = readl(base + CCN_ALL_OLY_ID);
- type = (val >> CCN_ALL_OLY_ID__OLY_ID__SHIFT) &
- CCN_ALL_OLY_ID__OLY_ID__MASK;
- id = (val >> CCN_ALL_OLY_ID__NODE_ID__SHIFT) &
- CCN_ALL_OLY_ID__NODE_ID__MASK;
-
- err = callback(ccn, region, base, type, id);
- if (err)
- return err;
- }
-
- return 0;
-}
-
-static int arm_ccn_get_nodes_num(struct arm_ccn *ccn, int region,
- void __iomem *base, u32 type, u32 id)
-{
-
- if (type == CCN_TYPE_XP && id >= ccn->num_xps)
- ccn->num_xps = id + 1;
- else if (id >= ccn->num_nodes)
- ccn->num_nodes = id + 1;
-
- return 0;
-}
-
-static int arm_ccn_init_nodes(struct arm_ccn *ccn, int region,
- void __iomem *base, u32 type, u32 id)
-{
- struct arm_ccn_component *component;
-
- dev_dbg(ccn->dev, "Region %d: id=%u, type=0x%02x\n", region, id, type);
-
- switch (type) {
- case CCN_TYPE_MN:
- ccn->mn_id = id;
- return 0;
- case CCN_TYPE_DT:
- return 0;
- case CCN_TYPE_XP:
- component = &ccn->xp[id];
- break;
- case CCN_TYPE_SBSX:
- ccn->sbsx_present = 1;
- component = &ccn->node[id];
- break;
- case CCN_TYPE_SBAS:
- ccn->sbas_present = 1;
- /* Fall-through */
- default:
- component = &ccn->node[id];
- break;
- }
-
- component->base = base;
- component->type = type;
-
- return 0;
-}
-
-
-static irqreturn_t arm_ccn_error_handler(struct arm_ccn *ccn,
- const u32 *err_sig_val)
-{
- /* This should be really handled by firmware... */
- dev_err(ccn->dev, "Error reported in %08x%08x%08x%08x%08x%08x.\n",
- err_sig_val[5], err_sig_val[4], err_sig_val[3],
- err_sig_val[2], err_sig_val[1], err_sig_val[0]);
- dev_err(ccn->dev, "Disabling interrupt generation for all errors.\n");
- writel(CCN_MN_ERRINT_STATUS__ALL_ERRORS__DISABLE,
- ccn->base + CCN_MN_ERRINT_STATUS);
-
- return IRQ_HANDLED;
-}
-
-
-static irqreturn_t arm_ccn_irq_handler(int irq, void *dev_id)
-{
- irqreturn_t res = IRQ_NONE;
- struct arm_ccn *ccn = dev_id;
- u32 err_sig_val[6];
- u32 err_or;
- int i;
-
- /* PMU overflow is a special case */
- err_or = err_sig_val[0] = readl(ccn->base + CCN_MN_ERR_SIG_VAL_63_0);
- if (err_or & CCN_MN_ERR_SIG_VAL_63_0__DT) {
- err_or &= ~CCN_MN_ERR_SIG_VAL_63_0__DT;
- res = arm_ccn_pmu_overflow_handler(&ccn->dt);
- }
-
- /* Have to read all err_sig_vals to clear them */
- for (i = 1; i < ARRAY_SIZE(err_sig_val); i++) {
- err_sig_val[i] = readl(ccn->base +
- CCN_MN_ERR_SIG_VAL_63_0 + i * 4);
- err_or |= err_sig_val[i];
- }
- if (err_or)
- res |= arm_ccn_error_handler(ccn, err_sig_val);
-
- if (res != IRQ_NONE)
- writel(CCN_MN_ERRINT_STATUS__INTREQ__DESSERT,
- ccn->base + CCN_MN_ERRINT_STATUS);
-
- return res;
-}
-
-
-static int arm_ccn_probe(struct platform_device *pdev)
-{
- struct arm_ccn *ccn;
- struct resource *res;
- unsigned int irq;
- int err;
-
- ccn = devm_kzalloc(&pdev->dev, sizeof(*ccn), GFP_KERNEL);
- if (!ccn)
- return -ENOMEM;
- ccn->dev = &pdev->dev;
- platform_set_drvdata(pdev, ccn);
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -EINVAL;
-
- if (!devm_request_mem_region(ccn->dev, res->start,
- resource_size(res), pdev->name))
- return -EBUSY;
-
- ccn->base = devm_ioremap(ccn->dev, res->start,
- resource_size(res));
- if (!ccn->base)
- return -EFAULT;
-
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res)
- return -EINVAL;
- irq = res->start;
-
- /* Check if we can use the interrupt */
- writel(CCN_MN_ERRINT_STATUS__PMU_EVENTS__DISABLE,
- ccn->base + CCN_MN_ERRINT_STATUS);
- if (readl(ccn->base + CCN_MN_ERRINT_STATUS) &
- CCN_MN_ERRINT_STATUS__PMU_EVENTS__DISABLED) {
- /* Can set 'disable' bits, so can acknowledge interrupts */
- writel(CCN_MN_ERRINT_STATUS__PMU_EVENTS__ENABLE,
- ccn->base + CCN_MN_ERRINT_STATUS);
- err = devm_request_irq(ccn->dev, irq, arm_ccn_irq_handler,
- IRQF_NOBALANCING | IRQF_NO_THREAD,
- dev_name(ccn->dev), ccn);
- if (err)
- return err;
-
- ccn->irq = irq;
- }
-
-
- /* Build topology */
-
- err = arm_ccn_for_each_valid_region(ccn, arm_ccn_get_nodes_num);
- if (err)
- return err;
-
- ccn->node = devm_kcalloc(ccn->dev, ccn->num_nodes, sizeof(*ccn->node),
- GFP_KERNEL);
- ccn->xp = devm_kcalloc(ccn->dev, ccn->num_xps, sizeof(*ccn->node),
- GFP_KERNEL);
- if (!ccn->node || !ccn->xp)
- return -ENOMEM;
-
- err = arm_ccn_for_each_valid_region(ccn, arm_ccn_init_nodes);
- if (err)
- return err;
-
- return arm_ccn_pmu_init(ccn);
-}
-
-static int arm_ccn_remove(struct platform_device *pdev)
-{
- struct arm_ccn *ccn = platform_get_drvdata(pdev);
-
- arm_ccn_pmu_cleanup(ccn);
-
- return 0;
-}
-
-static const struct of_device_id arm_ccn_match[] = {
- { .compatible = "arm,ccn-502", },
- { .compatible = "arm,ccn-504", },
- {},
-};
-MODULE_DEVICE_TABLE(of, arm_ccn_match);
-
-static struct platform_driver arm_ccn_driver = {
- .driver = {
- .name = "arm-ccn",
- .of_match_table = arm_ccn_match,
- },
- .probe = arm_ccn_probe,
- .remove = arm_ccn_remove,
-};
-
-static int __init arm_ccn_init(void)
-{
- int i, ret;
-
- ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_CCN_ONLINE,
- "perf/arm/ccn:online", NULL,
- arm_ccn_pmu_offline_cpu);
- if (ret)
- return ret;
-
- for (i = 0; i < ARRAY_SIZE(arm_ccn_pmu_events); i++)
- arm_ccn_pmu_events_attrs[i] = &arm_ccn_pmu_events[i].attr.attr;
-
- ret = platform_driver_register(&arm_ccn_driver);
- if (ret)
- cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_CCN_ONLINE);
- return ret;
-}
-
-static void __exit arm_ccn_exit(void)
-{
- platform_driver_unregister(&arm_ccn_driver);
- cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_CCN_ONLINE);
-}
-
-module_init(arm_ccn_init);
-module_exit(arm_ccn_exit);
-
-MODULE_AUTHOR("Pawel Moll <pawel.moll@arm.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/bus/fsl-mc/Kconfig b/drivers/bus/fsl-mc/Kconfig
new file mode 100644
index 000000000000..c23c77c9b705
--- /dev/null
+++ b/drivers/bus/fsl-mc/Kconfig
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# DPAA2 fsl-mc bus
+#
+# Copyright (C) 2014-2016 Freescale Semiconductor, Inc.
+#
+
+config FSL_MC_BUS
+ bool "QorIQ DPAA2 fsl-mc bus driver"
+ depends on OF && (ARCH_LAYERSCAPE || (COMPILE_TEST && (ARM || ARM64 || X86_LOCAL_APIC || PPC)))
+ select GENERIC_MSI_IRQ_DOMAIN
+ help
+ Driver to enable the bus infrastructure for the QorIQ DPAA2
+ architecture. The fsl-mc bus driver handles discovery of
+ DPAA2 objects (which are represented as Linux devices) and
+ binding objects to drivers.
diff --git a/drivers/bus/fsl-mc/Makefile b/drivers/bus/fsl-mc/Makefile
new file mode 100644
index 000000000000..3c518c7e8374
--- /dev/null
+++ b/drivers/bus/fsl-mc/Makefile
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Freescale Management Complex (MC) bus drivers
+#
+# Copyright (C) 2014 Freescale Semiconductor, Inc.
+#
+obj-$(CONFIG_FSL_MC_BUS) += mc-bus-driver.o
+
+mc-bus-driver-objs := fsl-mc-bus.o \
+ mc-sys.o \
+ mc-io.o \
+ dpbp.o \
+ dpcon.o \
+ dprc.o \
+ dprc-driver.o \
+ fsl-mc-allocator.o \
+ fsl-mc-msi.o \
+ dpmcp.o
diff --git a/drivers/bus/fsl-mc/dpbp.c b/drivers/bus/fsl-mc/dpbp.c
new file mode 100644
index 000000000000..17e3c5d2f22e
--- /dev/null
+++ b/drivers/bus/fsl-mc/dpbp.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/*
+ * Copyright 2013-2016 Freescale Semiconductor Inc.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/fsl/mc.h>
+#include <linux/fsl/mc.h>
+
+#include "fsl-mc-private.h"
+
+/**
+ * dpbp_open() - Open a control session for the specified object.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @dpbp_id: DPBP unique ID
+ * @token: Returned token; use in subsequent API calls
+ *
+ * This function can be used to open a control session for an
+ * already created object; an object may have been declared in
+ * the DPL or by calling the dpbp_create function.
+ * This function returns a unique authentication token,
+ * associated with the specific object ID and the specific MC
+ * portal; this token must be used in all subsequent commands for
+ * this specific object
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpbp_open(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ int dpbp_id,
+ u16 *token)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpbp_cmd_open *cmd_params;
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_OPEN,
+ cmd_flags, 0);
+ cmd_params = (struct dpbp_cmd_open *)cmd.params;
+ cmd_params->dpbp_id = cpu_to_le32(dpbp_id);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *token = mc_cmd_hdr_read_token(&cmd);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(dpbp_open);
+
+/**
+ * dpbp_close() - Close the control session of the object
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPBP object
+ *
+ * After this function is called, no further operations are
+ * allowed on the object without opening a new control session.
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpbp_close(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token)
+{
+ struct fsl_mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_CLOSE, cmd_flags,
+ token);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+EXPORT_SYMBOL_GPL(dpbp_close);
+
+/**
+ * dpbp_enable() - Enable the DPBP.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPBP object
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpbp_enable(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token)
+{
+ struct fsl_mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_ENABLE, cmd_flags,
+ token);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+EXPORT_SYMBOL_GPL(dpbp_enable);
+
+/**
+ * dpbp_disable() - Disable the DPBP.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPBP object
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpbp_disable(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token)
+{
+ struct fsl_mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_DISABLE,
+ cmd_flags, token);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+EXPORT_SYMBOL_GPL(dpbp_disable);
+
+/**
+ * dpbp_reset() - Reset the DPBP, returns the object to initial state.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPBP object
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpbp_reset(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token)
+{
+ struct fsl_mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_RESET,
+ cmd_flags, token);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+EXPORT_SYMBOL_GPL(dpbp_reset);
+
+/**
+ * dpbp_get_attributes - Retrieve DPBP attributes.
+ *
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPBP object
+ * @attr: Returned object's attributes
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpbp_get_attributes(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ struct dpbp_attr *attr)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpbp_rsp_get_attributes *rsp_params;
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_GET_ATTR,
+ cmd_flags, token);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ rsp_params = (struct dpbp_rsp_get_attributes *)cmd.params;
+ attr->bpid = le16_to_cpu(rsp_params->bpid);
+ attr->id = le32_to_cpu(rsp_params->id);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dpbp_get_attributes);
diff --git a/drivers/bus/fsl-mc/dpcon.c b/drivers/bus/fsl-mc/dpcon.c
new file mode 100644
index 000000000000..760555d7946e
--- /dev/null
+++ b/drivers/bus/fsl-mc/dpcon.c
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/*
+ * Copyright 2013-2016 Freescale Semiconductor Inc.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/fsl/mc.h>
+#include <linux/fsl/mc.h>
+
+#include "fsl-mc-private.h"
+
+/**
+ * dpcon_open() - Open a control session for the specified object
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @dpcon_id: DPCON unique ID
+ * @token: Returned token; use in subsequent API calls
+ *
+ * This function can be used to open a control session for an
+ * already created object; an object may have been declared in
+ * the DPL or by calling the dpcon_create() function.
+ * This function returns a unique authentication token,
+ * associated with the specific object ID and the specific MC
+ * portal; this token must be used in all subsequent commands for
+ * this specific object.
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpcon_open(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ int dpcon_id,
+ u16 *token)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpcon_cmd_open *dpcon_cmd;
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_OPEN,
+ cmd_flags,
+ 0);
+ dpcon_cmd = (struct dpcon_cmd_open *)cmd.params;
+ dpcon_cmd->dpcon_id = cpu_to_le32(dpcon_id);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *token = mc_cmd_hdr_read_token(&cmd);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dpcon_open);
+
+/**
+ * dpcon_close() - Close the control session of the object
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPCON object
+ *
+ * After this function is called, no further operations are
+ * allowed on the object without opening a new control session.
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpcon_close(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token)
+{
+ struct fsl_mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_CLOSE,
+ cmd_flags,
+ token);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+EXPORT_SYMBOL_GPL(dpcon_close);
+
+/**
+ * dpcon_enable() - Enable the DPCON
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPCON object
+ *
+ * Return: '0' on Success; Error code otherwise
+ */
+int dpcon_enable(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token)
+{
+ struct fsl_mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_ENABLE,
+ cmd_flags,
+ token);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+EXPORT_SYMBOL_GPL(dpcon_enable);
+
+/**
+ * dpcon_disable() - Disable the DPCON
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPCON object
+ *
+ * Return: '0' on Success; Error code otherwise
+ */
+int dpcon_disable(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token)
+{
+ struct fsl_mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_DISABLE,
+ cmd_flags,
+ token);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+EXPORT_SYMBOL_GPL(dpcon_disable);
+
+/**
+ * dpcon_reset() - Reset the DPCON, returns the object to initial state.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPCON object
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpcon_reset(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token)
+{
+ struct fsl_mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_RESET,
+ cmd_flags, token);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+EXPORT_SYMBOL_GPL(dpcon_reset);
+
+/**
+ * dpcon_get_attributes() - Retrieve DPCON attributes.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPCON object
+ * @attr: Object's attributes
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpcon_get_attributes(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ struct dpcon_attr *attr)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpcon_rsp_get_attr *dpcon_rsp;
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_GET_ATTR,
+ cmd_flags,
+ token);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ dpcon_rsp = (struct dpcon_rsp_get_attr *)cmd.params;
+ attr->id = le32_to_cpu(dpcon_rsp->id);
+ attr->qbman_ch_id = le16_to_cpu(dpcon_rsp->qbman_ch_id);
+ attr->num_priorities = dpcon_rsp->num_priorities;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dpcon_get_attributes);
+
+/**
+ * dpcon_set_notification() - Set DPCON notification destination
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPCON object
+ * @cfg: Notification parameters
+ *
+ * Return: '0' on Success; Error code otherwise
+ */
+int dpcon_set_notification(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ struct dpcon_notification_cfg *cfg)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpcon_cmd_set_notification *dpcon_cmd;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPCON_CMDID_SET_NOTIFICATION,
+ cmd_flags,
+ token);
+ dpcon_cmd = (struct dpcon_cmd_set_notification *)cmd.params;
+ dpcon_cmd->dpio_id = cpu_to_le32(cfg->dpio_id);
+ dpcon_cmd->priority = cfg->priority;
+ dpcon_cmd->user_ctx = cpu_to_le64(cfg->user_ctx);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+EXPORT_SYMBOL_GPL(dpcon_set_notification);
diff --git a/drivers/bus/fsl-mc/dpmcp.c b/drivers/bus/fsl-mc/dpmcp.c
new file mode 100644
index 000000000000..5fbd0dbde24a
--- /dev/null
+++ b/drivers/bus/fsl-mc/dpmcp.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/*
+ * Copyright 2013-2016 Freescale Semiconductor Inc.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/fsl/mc.h>
+
+#include "fsl-mc-private.h"
+
+/**
+ * dpmcp_open() - Open a control session for the specified object.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @dpmcp_id: DPMCP unique ID
+ * @token: Returned token; use in subsequent API calls
+ *
+ * This function can be used to open a control session for an
+ * already created object; an object may have been declared in
+ * the DPL or by calling the dpmcp_create function.
+ * This function returns a unique authentication token,
+ * associated with the specific object ID and the specific MC
+ * portal; this token must be used in all subsequent commands for
+ * this specific object
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpmcp_open(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ int dpmcp_id,
+ u16 *token)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpmcp_cmd_open *cmd_params;
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_OPEN,
+ cmd_flags, 0);
+ cmd_params = (struct dpmcp_cmd_open *)cmd.params;
+ cmd_params->dpmcp_id = cpu_to_le32(dpmcp_id);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *token = mc_cmd_hdr_read_token(&cmd);
+
+ return err;
+}
+
+/**
+ * dpmcp_close() - Close the control session of the object
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPMCP object
+ *
+ * After this function is called, no further operations are
+ * allowed on the object without opening a new control session.
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpmcp_close(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token)
+{
+ struct fsl_mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_CLOSE,
+ cmd_flags, token);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpmcp_reset() - Reset the DPMCP, returns the object to initial state.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPMCP object
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpmcp_reset(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token)
+{
+ struct fsl_mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_RESET,
+ cmd_flags, token);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
diff --git a/drivers/bus/fsl-mc/dprc-driver.c b/drivers/bus/fsl-mc/dprc-driver.c
new file mode 100644
index 000000000000..52c7e15143d6
--- /dev/null
+++ b/drivers/bus/fsl-mc/dprc-driver.c
@@ -0,0 +1,809 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Freescale data path resource container (DPRC) driver
+ *
+ * Copyright (C) 2014-2016 Freescale Semiconductor, Inc.
+ * Author: German Rivera <German.Rivera@freescale.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/msi.h>
+#include <linux/fsl/mc.h>
+
+#include "fsl-mc-private.h"
+
+#define FSL_MC_DPRC_DRIVER_NAME "fsl_mc_dprc"
+
+struct fsl_mc_child_objs {
+ int child_count;
+ struct fsl_mc_obj_desc *child_array;
+};
+
+static bool fsl_mc_device_match(struct fsl_mc_device *mc_dev,
+ struct fsl_mc_obj_desc *obj_desc)
+{
+ return mc_dev->obj_desc.id == obj_desc->id &&
+ strcmp(mc_dev->obj_desc.type, obj_desc->type) == 0;
+
+}
+
+static int __fsl_mc_device_remove_if_not_in_mc(struct device *dev, void *data)
+{
+ int i;
+ struct fsl_mc_child_objs *objs;
+ struct fsl_mc_device *mc_dev;
+
+ mc_dev = to_fsl_mc_device(dev);
+ objs = data;
+
+ for (i = 0; i < objs->child_count; i++) {
+ struct fsl_mc_obj_desc *obj_desc = &objs->child_array[i];
+
+ if (strlen(obj_desc->type) != 0 &&
+ fsl_mc_device_match(mc_dev, obj_desc))
+ break;
+ }
+
+ if (i == objs->child_count)
+ fsl_mc_device_remove(mc_dev);
+
+ return 0;
+}
+
+static int __fsl_mc_device_remove(struct device *dev, void *data)
+{
+ fsl_mc_device_remove(to_fsl_mc_device(dev));
+ return 0;
+}
+
+/**
+ * dprc_remove_devices - Removes devices for objects removed from a DPRC
+ *
+ * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
+ * @obj_desc_array: array of object descriptors for child objects currently
+ * present in the DPRC in the MC.
+ * @num_child_objects_in_mc: number of entries in obj_desc_array
+ *
+ * Synchronizes the state of the Linux bus driver with the actual state of
+ * the MC by removing devices that represent MC objects that have
+ * been dynamically removed in the physical DPRC.
+ */
+static void dprc_remove_devices(struct fsl_mc_device *mc_bus_dev,
+ struct fsl_mc_obj_desc *obj_desc_array,
+ int num_child_objects_in_mc)
+{
+ if (num_child_objects_in_mc != 0) {
+ /*
+ * Remove child objects that are in the DPRC in Linux,
+ * but not in the MC:
+ */
+ struct fsl_mc_child_objs objs;
+
+ objs.child_count = num_child_objects_in_mc;
+ objs.child_array = obj_desc_array;
+ device_for_each_child(&mc_bus_dev->dev, &objs,
+ __fsl_mc_device_remove_if_not_in_mc);
+ } else {
+ /*
+ * There are no child objects for this DPRC in the MC.
+ * So, remove all the child devices from Linux:
+ */
+ device_for_each_child(&mc_bus_dev->dev, NULL,
+ __fsl_mc_device_remove);
+ }
+}
+
+static int __fsl_mc_device_match(struct device *dev, void *data)
+{
+ struct fsl_mc_obj_desc *obj_desc = data;
+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
+
+ return fsl_mc_device_match(mc_dev, obj_desc);
+}
+
+static struct fsl_mc_device *fsl_mc_device_lookup(struct fsl_mc_obj_desc
+ *obj_desc,
+ struct fsl_mc_device
+ *mc_bus_dev)
+{
+ struct device *dev;
+
+ dev = device_find_child(&mc_bus_dev->dev, obj_desc,
+ __fsl_mc_device_match);
+
+ return dev ? to_fsl_mc_device(dev) : NULL;
+}
+
+/**
+ * check_plugged_state_change - Check change in an MC object's plugged state
+ *
+ * @mc_dev: pointer to the fsl-mc device for a given MC object
+ * @obj_desc: pointer to the MC object's descriptor in the MC
+ *
+ * If the plugged state has changed from unplugged to plugged, the fsl-mc
+ * device is bound to the corresponding device driver.
+ * If the plugged state has changed from plugged to unplugged, the fsl-mc
+ * device is unbound from the corresponding device driver.
+ */
+static void check_plugged_state_change(struct fsl_mc_device *mc_dev,
+ struct fsl_mc_obj_desc *obj_desc)
+{
+ int error;
+ u32 plugged_flag_at_mc =
+ obj_desc->state & FSL_MC_OBJ_STATE_PLUGGED;
+
+ if (plugged_flag_at_mc !=
+ (mc_dev->obj_desc.state & FSL_MC_OBJ_STATE_PLUGGED)) {
+ if (plugged_flag_at_mc) {
+ mc_dev->obj_desc.state |= FSL_MC_OBJ_STATE_PLUGGED;
+ error = device_attach(&mc_dev->dev);
+ if (error < 0) {
+ dev_err(&mc_dev->dev,
+ "device_attach() failed: %d\n",
+ error);
+ }
+ } else {
+ mc_dev->obj_desc.state &= ~FSL_MC_OBJ_STATE_PLUGGED;
+ device_release_driver(&mc_dev->dev);
+ }
+ }
+}
+
+/**
+ * dprc_add_new_devices - Adds devices to the logical bus for a DPRC
+ *
+ * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
+ * @obj_desc_array: array of device descriptors for child devices currently
+ * present in the physical DPRC.
+ * @num_child_objects_in_mc: number of entries in obj_desc_array
+ *
+ * Synchronizes the state of the Linux bus driver with the actual
+ * state of the MC by adding objects that have been newly discovered
+ * in the physical DPRC.
+ */
+static void dprc_add_new_devices(struct fsl_mc_device *mc_bus_dev,
+ struct fsl_mc_obj_desc *obj_desc_array,
+ int num_child_objects_in_mc)
+{
+ int error;
+ int i;
+
+ for (i = 0; i < num_child_objects_in_mc; i++) {
+ struct fsl_mc_device *child_dev;
+ struct fsl_mc_obj_desc *obj_desc = &obj_desc_array[i];
+
+ if (strlen(obj_desc->type) == 0)
+ continue;
+
+ /*
+ * Check if device is already known to Linux:
+ */
+ child_dev = fsl_mc_device_lookup(obj_desc, mc_bus_dev);
+ if (child_dev) {
+ check_plugged_state_change(child_dev, obj_desc);
+ put_device(&child_dev->dev);
+ continue;
+ }
+
+ error = fsl_mc_device_add(obj_desc, NULL, &mc_bus_dev->dev,
+ &child_dev);
+ if (error < 0)
+ continue;
+ }
+}
+
+/**
+ * dprc_scan_objects - Discover objects in a DPRC
+ *
+ * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
+ * @total_irq_count: If argument is provided the function populates the
+ * total number of IRQs created by objects in the DPRC.
+ *
+ * Detects objects added and removed from a DPRC and synchronizes the
+ * state of the Linux bus driver, MC by adding and removing
+ * devices accordingly.
+ * Two types of devices can be found in a DPRC: allocatable objects (e.g.,
+ * dpbp, dpmcp) and non-allocatable devices (e.g., dprc, dpni).
+ * All allocatable devices needed to be probed before all non-allocatable
+ * devices, to ensure that device drivers for non-allocatable
+ * devices can allocate any type of allocatable devices.
+ * That is, we need to ensure that the corresponding resource pools are
+ * populated before they can get allocation requests from probe callbacks
+ * of the device drivers for the non-allocatable devices.
+ */
+static int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev,
+ unsigned int *total_irq_count)
+{
+ int num_child_objects;
+ int dprc_get_obj_failures;
+ int error;
+ unsigned int irq_count = mc_bus_dev->obj_desc.irq_count;
+ struct fsl_mc_obj_desc *child_obj_desc_array = NULL;
+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
+
+ error = dprc_get_obj_count(mc_bus_dev->mc_io,
+ 0,
+ mc_bus_dev->mc_handle,
+ &num_child_objects);
+ if (error < 0) {
+ dev_err(&mc_bus_dev->dev, "dprc_get_obj_count() failed: %d\n",
+ error);
+ return error;
+ }
+
+ if (num_child_objects != 0) {
+ int i;
+
+ child_obj_desc_array =
+ devm_kmalloc_array(&mc_bus_dev->dev, num_child_objects,
+ sizeof(*child_obj_desc_array),
+ GFP_KERNEL);
+ if (!child_obj_desc_array)
+ return -ENOMEM;
+
+ /*
+ * Discover objects currently present in the physical DPRC:
+ */
+ dprc_get_obj_failures = 0;
+ for (i = 0; i < num_child_objects; i++) {
+ struct fsl_mc_obj_desc *obj_desc =
+ &child_obj_desc_array[i];
+
+ error = dprc_get_obj(mc_bus_dev->mc_io,
+ 0,
+ mc_bus_dev->mc_handle,
+ i, obj_desc);
+ if (error < 0) {
+ dev_err(&mc_bus_dev->dev,
+ "dprc_get_obj(i=%d) failed: %d\n",
+ i, error);
+ /*
+ * Mark the obj entry as "invalid", by using the
+ * empty string as obj type:
+ */
+ obj_desc->type[0] = '\0';
+ obj_desc->id = error;
+ dprc_get_obj_failures++;
+ continue;
+ }
+
+ /*
+ * add a quirk for all versions of dpsec < 4.0...none
+ * are coherent regardless of what the MC reports.
+ */
+ if ((strcmp(obj_desc->type, "dpseci") == 0) &&
+ (obj_desc->ver_major < 4))
+ obj_desc->flags |=
+ FSL_MC_OBJ_FLAG_NO_MEM_SHAREABILITY;
+
+ irq_count += obj_desc->irq_count;
+ dev_dbg(&mc_bus_dev->dev,
+ "Discovered object: type %s, id %d\n",
+ obj_desc->type, obj_desc->id);
+ }
+
+ if (dprc_get_obj_failures != 0) {
+ dev_err(&mc_bus_dev->dev,
+ "%d out of %d devices could not be retrieved\n",
+ dprc_get_obj_failures, num_child_objects);
+ }
+ }
+
+ /*
+ * Allocate IRQ's before binding the scanned devices with their
+ * respective drivers.
+ */
+ if (dev_get_msi_domain(&mc_bus_dev->dev) && !mc_bus->irq_resources) {
+ if (irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS) {
+ dev_warn(&mc_bus_dev->dev,
+ "IRQs needed (%u) exceed IRQs preallocated (%u)\n",
+ irq_count, FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS);
+ }
+
+ error = fsl_mc_populate_irq_pool(mc_bus,
+ FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS);
+ if (error < 0)
+ return error;
+ }
+
+ if (total_irq_count)
+ *total_irq_count = irq_count;
+
+ dprc_remove_devices(mc_bus_dev, child_obj_desc_array,
+ num_child_objects);
+
+ dprc_add_new_devices(mc_bus_dev, child_obj_desc_array,
+ num_child_objects);
+
+ if (child_obj_desc_array)
+ devm_kfree(&mc_bus_dev->dev, child_obj_desc_array);
+
+ return 0;
+}
+
+/**
+ * dprc_scan_container - Scans a physical DPRC and synchronizes Linux bus state
+ *
+ * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
+ *
+ * Scans the physical DPRC and synchronizes the state of the Linux
+ * bus driver with the actual state of the MC by adding and removing
+ * devices as appropriate.
+ */
+static int dprc_scan_container(struct fsl_mc_device *mc_bus_dev)
+{
+ int error;
+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
+
+ fsl_mc_init_all_resource_pools(mc_bus_dev);
+
+ /*
+ * Discover objects in the DPRC:
+ */
+ mutex_lock(&mc_bus->scan_mutex);
+ error = dprc_scan_objects(mc_bus_dev, NULL);
+ mutex_unlock(&mc_bus->scan_mutex);
+ if (error < 0) {
+ fsl_mc_cleanup_all_resource_pools(mc_bus_dev);
+ return error;
+ }
+
+ return 0;
+}
+
+/**
+ * dprc_irq0_handler - Regular ISR for DPRC interrupt 0
+ *
+ * @irq: IRQ number of the interrupt being handled
+ * @arg: Pointer to device structure
+ */
+static irqreturn_t dprc_irq0_handler(int irq_num, void *arg)
+{
+ return IRQ_WAKE_THREAD;
+}
+
+/**
+ * dprc_irq0_handler_thread - Handler thread function for DPRC interrupt 0
+ *
+ * @irq: IRQ number of the interrupt being handled
+ * @arg: Pointer to device structure
+ */
+static irqreturn_t dprc_irq0_handler_thread(int irq_num, void *arg)
+{
+ int error;
+ u32 status;
+ struct device *dev = arg;
+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
+ struct fsl_mc_io *mc_io = mc_dev->mc_io;
+ struct msi_desc *msi_desc = mc_dev->irqs[0]->msi_desc;
+
+ dev_dbg(dev, "DPRC IRQ %d triggered on CPU %u\n",
+ irq_num, smp_processor_id());
+
+ if (!(mc_dev->flags & FSL_MC_IS_DPRC))
+ return IRQ_HANDLED;
+
+ mutex_lock(&mc_bus->scan_mutex);
+ if (!msi_desc || msi_desc->irq != (u32)irq_num)
+ goto out;
+
+ status = 0;
+ error = dprc_get_irq_status(mc_io, 0, mc_dev->mc_handle, 0,
+ &status);
+ if (error < 0) {
+ dev_err(dev,
+ "dprc_get_irq_status() failed: %d\n", error);
+ goto out;
+ }
+
+ error = dprc_clear_irq_status(mc_io, 0, mc_dev->mc_handle, 0,
+ status);
+ if (error < 0) {
+ dev_err(dev,
+ "dprc_clear_irq_status() failed: %d\n", error);
+ goto out;
+ }
+
+ if (status & (DPRC_IRQ_EVENT_OBJ_ADDED |
+ DPRC_IRQ_EVENT_OBJ_REMOVED |
+ DPRC_IRQ_EVENT_CONTAINER_DESTROYED |
+ DPRC_IRQ_EVENT_OBJ_DESTROYED |
+ DPRC_IRQ_EVENT_OBJ_CREATED)) {
+ unsigned int irq_count;
+
+ error = dprc_scan_objects(mc_dev, &irq_count);
+ if (error < 0) {
+ /*
+ * If the error is -ENXIO, we ignore it, as it indicates
+ * that the object scan was aborted, as we detected that
+ * an object was removed from the DPRC in the MC, while
+ * we were scanning the DPRC.
+ */
+ if (error != -ENXIO) {
+ dev_err(dev, "dprc_scan_objects() failed: %d\n",
+ error);
+ }
+
+ goto out;
+ }
+
+ if (irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS) {
+ dev_warn(dev,
+ "IRQs needed (%u) exceed IRQs preallocated (%u)\n",
+ irq_count, FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS);
+ }
+ }
+
+out:
+ mutex_unlock(&mc_bus->scan_mutex);
+ return IRQ_HANDLED;
+}
+
+/*
+ * Disable and clear interrupt for a given DPRC object
+ */
+static int disable_dprc_irq(struct fsl_mc_device *mc_dev)
+{
+ int error;
+ struct fsl_mc_io *mc_io = mc_dev->mc_io;
+
+ /*
+ * Disable generation of interrupt, while we configure it:
+ */
+ error = dprc_set_irq_enable(mc_io, 0, mc_dev->mc_handle, 0, 0);
+ if (error < 0) {
+ dev_err(&mc_dev->dev,
+ "Disabling DPRC IRQ failed: dprc_set_irq_enable() failed: %d\n",
+ error);
+ return error;
+ }
+
+ /*
+ * Disable all interrupt causes for the interrupt:
+ */
+ error = dprc_set_irq_mask(mc_io, 0, mc_dev->mc_handle, 0, 0x0);
+ if (error < 0) {
+ dev_err(&mc_dev->dev,
+ "Disabling DPRC IRQ failed: dprc_set_irq_mask() failed: %d\n",
+ error);
+ return error;
+ }
+
+ /*
+ * Clear any leftover interrupts:
+ */
+ error = dprc_clear_irq_status(mc_io, 0, mc_dev->mc_handle, 0, ~0x0U);
+ if (error < 0) {
+ dev_err(&mc_dev->dev,
+ "Disabling DPRC IRQ failed: dprc_clear_irq_status() failed: %d\n",
+ error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int register_dprc_irq_handler(struct fsl_mc_device *mc_dev)
+{
+ int error;
+ struct fsl_mc_device_irq *irq = mc_dev->irqs[0];
+
+ /*
+ * NOTE: devm_request_threaded_irq() invokes the device-specific
+ * function that programs the MSI physically in the device
+ */
+ error = devm_request_threaded_irq(&mc_dev->dev,
+ irq->msi_desc->irq,
+ dprc_irq0_handler,
+ dprc_irq0_handler_thread,
+ IRQF_NO_SUSPEND | IRQF_ONESHOT,
+ dev_name(&mc_dev->dev),
+ &mc_dev->dev);
+ if (error < 0) {
+ dev_err(&mc_dev->dev,
+ "devm_request_threaded_irq() failed: %d\n",
+ error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int enable_dprc_irq(struct fsl_mc_device *mc_dev)
+{
+ int error;
+
+ /*
+ * Enable all interrupt causes for the interrupt:
+ */
+ error = dprc_set_irq_mask(mc_dev->mc_io, 0, mc_dev->mc_handle, 0,
+ ~0x0u);
+ if (error < 0) {
+ dev_err(&mc_dev->dev,
+ "Enabling DPRC IRQ failed: dprc_set_irq_mask() failed: %d\n",
+ error);
+
+ return error;
+ }
+
+ /*
+ * Enable generation of the interrupt:
+ */
+ error = dprc_set_irq_enable(mc_dev->mc_io, 0, mc_dev->mc_handle, 0, 1);
+ if (error < 0) {
+ dev_err(&mc_dev->dev,
+ "Enabling DPRC IRQ failed: dprc_set_irq_enable() failed: %d\n",
+ error);
+
+ return error;
+ }
+
+ return 0;
+}
+
+/*
+ * Setup interrupt for a given DPRC device
+ */
+static int dprc_setup_irq(struct fsl_mc_device *mc_dev)
+{
+ int error;
+
+ error = fsl_mc_allocate_irqs(mc_dev);
+ if (error < 0)
+ return error;
+
+ error = disable_dprc_irq(mc_dev);
+ if (error < 0)
+ goto error_free_irqs;
+
+ error = register_dprc_irq_handler(mc_dev);
+ if (error < 0)
+ goto error_free_irqs;
+
+ error = enable_dprc_irq(mc_dev);
+ if (error < 0)
+ goto error_free_irqs;
+
+ return 0;
+
+error_free_irqs:
+ fsl_mc_free_irqs(mc_dev);
+ return error;
+}
+
+/**
+ * dprc_probe - callback invoked when a DPRC is being bound to this driver
+ *
+ * @mc_dev: Pointer to fsl-mc device representing a DPRC
+ *
+ * It opens the physical DPRC in the MC.
+ * It scans the DPRC to discover the MC objects contained in it.
+ * It creates the interrupt pool for the MC bus associated with the DPRC.
+ * It configures the interrupts for the DPRC device itself.
+ */
+static int dprc_probe(struct fsl_mc_device *mc_dev)
+{
+ int error;
+ size_t region_size;
+ struct device *parent_dev = mc_dev->dev.parent;
+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
+ bool mc_io_created = false;
+ bool msi_domain_set = false;
+ u16 major_ver, minor_ver;
+
+ if (!is_fsl_mc_bus_dprc(mc_dev))
+ return -EINVAL;
+
+ if (dev_get_msi_domain(&mc_dev->dev))
+ return -EINVAL;
+
+ if (!mc_dev->mc_io) {
+ /*
+ * This is a child DPRC:
+ */
+ if (!dev_is_fsl_mc(parent_dev))
+ return -EINVAL;
+
+ if (mc_dev->obj_desc.region_count == 0)
+ return -EINVAL;
+
+ region_size = resource_size(mc_dev->regions);
+
+ error = fsl_create_mc_io(&mc_dev->dev,
+ mc_dev->regions[0].start,
+ region_size,
+ NULL,
+ FSL_MC_IO_ATOMIC_CONTEXT_PORTAL,
+ &mc_dev->mc_io);
+ if (error < 0)
+ return error;
+
+ mc_io_created = true;
+
+ /*
+ * Inherit parent MSI domain:
+ */
+ dev_set_msi_domain(&mc_dev->dev,
+ dev_get_msi_domain(parent_dev));
+ msi_domain_set = true;
+ } else {
+ /*
+ * This is a root DPRC
+ */
+ struct irq_domain *mc_msi_domain;
+
+ if (dev_is_fsl_mc(parent_dev))
+ return -EINVAL;
+
+ error = fsl_mc_find_msi_domain(parent_dev,
+ &mc_msi_domain);
+ if (error < 0) {
+ dev_warn(&mc_dev->dev,
+ "WARNING: MC bus without interrupt support\n");
+ } else {
+ dev_set_msi_domain(&mc_dev->dev, mc_msi_domain);
+ msi_domain_set = true;
+ }
+ }
+
+ error = dprc_open(mc_dev->mc_io, 0, mc_dev->obj_desc.id,
+ &mc_dev->mc_handle);
+ if (error < 0) {
+ dev_err(&mc_dev->dev, "dprc_open() failed: %d\n", error);
+ goto error_cleanup_msi_domain;
+ }
+
+ error = dprc_get_attributes(mc_dev->mc_io, 0, mc_dev->mc_handle,
+ &mc_bus->dprc_attr);
+ if (error < 0) {
+ dev_err(&mc_dev->dev, "dprc_get_attributes() failed: %d\n",
+ error);
+ goto error_cleanup_open;
+ }
+
+ error = dprc_get_api_version(mc_dev->mc_io, 0,
+ &major_ver,
+ &minor_ver);
+ if (error < 0) {
+ dev_err(&mc_dev->dev, "dprc_get_api_version() failed: %d\n",
+ error);
+ goto error_cleanup_open;
+ }
+
+ if (major_ver < DPRC_MIN_VER_MAJOR ||
+ (major_ver == DPRC_MIN_VER_MAJOR &&
+ minor_ver < DPRC_MIN_VER_MINOR)) {
+ dev_err(&mc_dev->dev,
+ "ERROR: DPRC version %d.%d not supported\n",
+ major_ver, minor_ver);
+ error = -ENOTSUPP;
+ goto error_cleanup_open;
+ }
+
+ mutex_init(&mc_bus->scan_mutex);
+
+ /*
+ * Discover MC objects in DPRC object:
+ */
+ error = dprc_scan_container(mc_dev);
+ if (error < 0)
+ goto error_cleanup_open;
+
+ /*
+ * Configure interrupt for the DPRC object associated with this MC bus:
+ */
+ error = dprc_setup_irq(mc_dev);
+ if (error < 0)
+ goto error_cleanup_open;
+
+ dev_info(&mc_dev->dev, "DPRC device bound to driver");
+ return 0;
+
+error_cleanup_open:
+ (void)dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle);
+
+error_cleanup_msi_domain:
+ if (msi_domain_set)
+ dev_set_msi_domain(&mc_dev->dev, NULL);
+
+ if (mc_io_created) {
+ fsl_destroy_mc_io(mc_dev->mc_io);
+ mc_dev->mc_io = NULL;
+ }
+
+ return error;
+}
+
+/*
+ * Tear down interrupt for a given DPRC object
+ */
+static void dprc_teardown_irq(struct fsl_mc_device *mc_dev)
+{
+ struct fsl_mc_device_irq *irq = mc_dev->irqs[0];
+
+ (void)disable_dprc_irq(mc_dev);
+
+ devm_free_irq(&mc_dev->dev, irq->msi_desc->irq, &mc_dev->dev);
+
+ fsl_mc_free_irqs(mc_dev);
+}
+
+/**
+ * dprc_remove - callback invoked when a DPRC is being unbound from this driver
+ *
+ * @mc_dev: Pointer to fsl-mc device representing the DPRC
+ *
+ * It removes the DPRC's child objects from Linux (not from the MC) and
+ * closes the DPRC device in the MC.
+ * It tears down the interrupts that were configured for the DPRC device.
+ * It destroys the interrupt pool associated with this MC bus.
+ */
+static int dprc_remove(struct fsl_mc_device *mc_dev)
+{
+ int error;
+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
+
+ if (!is_fsl_mc_bus_dprc(mc_dev))
+ return -EINVAL;
+ if (!mc_dev->mc_io)
+ return -EINVAL;
+
+ if (!mc_bus->irq_resources)
+ return -EINVAL;
+
+ if (dev_get_msi_domain(&mc_dev->dev))
+ dprc_teardown_irq(mc_dev);
+
+ device_for_each_child(&mc_dev->dev, NULL, __fsl_mc_device_remove);
+
+ if (dev_get_msi_domain(&mc_dev->dev)) {
+ fsl_mc_cleanup_irq_pool(mc_bus);
+ dev_set_msi_domain(&mc_dev->dev, NULL);
+ }
+
+ fsl_mc_cleanup_all_resource_pools(mc_dev);
+
+ error = dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle);
+ if (error < 0)
+ dev_err(&mc_dev->dev, "dprc_close() failed: %d\n", error);
+
+ if (!fsl_mc_is_root_dprc(&mc_dev->dev)) {
+ fsl_destroy_mc_io(mc_dev->mc_io);
+ mc_dev->mc_io = NULL;
+ }
+
+ dev_info(&mc_dev->dev, "DPRC device unbound from driver");
+ return 0;
+}
+
+static const struct fsl_mc_device_id match_id_table[] = {
+ {
+ .vendor = FSL_MC_VENDOR_FREESCALE,
+ .obj_type = "dprc"},
+ {.vendor = 0x0},
+};
+
+static struct fsl_mc_driver dprc_driver = {
+ .driver = {
+ .name = FSL_MC_DPRC_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .pm = NULL,
+ },
+ .match_id_table = match_id_table,
+ .probe = dprc_probe,
+ .remove = dprc_remove,
+};
+
+int __init dprc_driver_init(void)
+{
+ return fsl_mc_driver_register(&dprc_driver);
+}
+
+void dprc_driver_exit(void)
+{
+ fsl_mc_driver_unregister(&dprc_driver);
+}
diff --git a/drivers/bus/fsl-mc/dprc.c b/drivers/bus/fsl-mc/dprc.c
new file mode 100644
index 000000000000..1c3f62182266
--- /dev/null
+++ b/drivers/bus/fsl-mc/dprc.c
@@ -0,0 +1,532 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/*
+ * Copyright 2013-2016 Freescale Semiconductor Inc.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/fsl/mc.h>
+
+#include "fsl-mc-private.h"
+
+/**
+ * dprc_open() - Open DPRC object for use
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @container_id: Container ID to open
+ * @token: Returned token of DPRC object
+ *
+ * Return: '0' on Success; Error code otherwise.
+ *
+ * @warning Required before any operation on the object.
+ */
+int dprc_open(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ int container_id,
+ u16 *token)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dprc_cmd_open *cmd_params;
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_OPEN, cmd_flags,
+ 0);
+ cmd_params = (struct dprc_cmd_open *)cmd.params;
+ cmd_params->container_id = cpu_to_le32(container_id);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *token = mc_cmd_hdr_read_token(&cmd);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dprc_open);
+
+/**
+ * dprc_close() - Close the control session of the object
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPRC object
+ *
+ * After this function is called, no further operations are
+ * allowed on the object without opening a new control session.
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_close(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token)
+{
+ struct fsl_mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_CLOSE, cmd_flags,
+ token);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+EXPORT_SYMBOL_GPL(dprc_close);
+
+/**
+ * dprc_set_irq() - Set IRQ information for the DPRC to trigger an interrupt.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPRC object
+ * @irq_index: Identifies the interrupt index to configure
+ * @irq_cfg: IRQ configuration
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_set_irq(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u8 irq_index,
+ struct dprc_irq_cfg *irq_cfg)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dprc_cmd_set_irq *cmd_params;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_IRQ,
+ cmd_flags,
+ token);
+ cmd_params = (struct dprc_cmd_set_irq *)cmd.params;
+ cmd_params->irq_val = cpu_to_le32(irq_cfg->val);
+ cmd_params->irq_index = irq_index;
+ cmd_params->irq_addr = cpu_to_le64(irq_cfg->paddr);
+ cmd_params->irq_num = cpu_to_le32(irq_cfg->irq_num);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dprc_set_irq_enable() - Set overall interrupt state.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPRC object
+ * @irq_index: The interrupt index to configure
+ * @en: Interrupt state - enable = 1, disable = 0
+ *
+ * Allows GPP software to control when interrupts are generated.
+ * Each interrupt can have up to 32 causes. The enable/disable control's the
+ * overall interrupt state. if the interrupt is disabled no causes will cause
+ * an interrupt.
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_set_irq_enable(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u8 irq_index,
+ u8 en)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dprc_cmd_set_irq_enable *cmd_params;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_IRQ_ENABLE,
+ cmd_flags, token);
+ cmd_params = (struct dprc_cmd_set_irq_enable *)cmd.params;
+ cmd_params->enable = en & DPRC_ENABLE;
+ cmd_params->irq_index = irq_index;
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dprc_set_irq_mask() - Set interrupt mask.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPRC object
+ * @irq_index: The interrupt index to configure
+ * @mask: event mask to trigger interrupt;
+ * each bit:
+ * 0 = ignore event
+ * 1 = consider event for asserting irq
+ *
+ * Every interrupt can have up to 32 causes and the interrupt model supports
+ * masking/unmasking each cause independently
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_set_irq_mask(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u8 irq_index,
+ u32 mask)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dprc_cmd_set_irq_mask *cmd_params;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_IRQ_MASK,
+ cmd_flags, token);
+ cmd_params = (struct dprc_cmd_set_irq_mask *)cmd.params;
+ cmd_params->mask = cpu_to_le32(mask);
+ cmd_params->irq_index = irq_index;
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dprc_get_irq_status() - Get the current status of any pending interrupts.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPRC object
+ * @irq_index: The interrupt index to configure
+ * @status: Returned interrupts status - one bit per cause:
+ * 0 = no interrupt pending
+ * 1 = interrupt pending
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_get_irq_status(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u8 irq_index,
+ u32 *status)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dprc_cmd_get_irq_status *cmd_params;
+ struct dprc_rsp_get_irq_status *rsp_params;
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_IRQ_STATUS,
+ cmd_flags, token);
+ cmd_params = (struct dprc_cmd_get_irq_status *)cmd.params;
+ cmd_params->status = cpu_to_le32(*status);
+ cmd_params->irq_index = irq_index;
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ rsp_params = (struct dprc_rsp_get_irq_status *)cmd.params;
+ *status = le32_to_cpu(rsp_params->status);
+
+ return 0;
+}
+
+/**
+ * dprc_clear_irq_status() - Clear a pending interrupt's status
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPRC object
+ * @irq_index: The interrupt index to configure
+ * @status: bits to clear (W1C) - one bit per cause:
+ * 0 = don't change
+ * 1 = clear status bit
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_clear_irq_status(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u8 irq_index,
+ u32 status)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dprc_cmd_clear_irq_status *cmd_params;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_CLEAR_IRQ_STATUS,
+ cmd_flags, token);
+ cmd_params = (struct dprc_cmd_clear_irq_status *)cmd.params;
+ cmd_params->status = cpu_to_le32(status);
+ cmd_params->irq_index = irq_index;
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dprc_get_attributes() - Obtains container attributes
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPRC object
+ * @attributes Returned container attributes
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_get_attributes(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ struct dprc_attributes *attr)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dprc_rsp_get_attributes *rsp_params;
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_ATTR,
+ cmd_flags,
+ token);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ rsp_params = (struct dprc_rsp_get_attributes *)cmd.params;
+ attr->container_id = le32_to_cpu(rsp_params->container_id);
+ attr->icid = le16_to_cpu(rsp_params->icid);
+ attr->options = le32_to_cpu(rsp_params->options);
+ attr->portal_id = le32_to_cpu(rsp_params->portal_id);
+
+ return 0;
+}
+
+/**
+ * dprc_get_obj_count() - Obtains the number of objects in the DPRC
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPRC object
+ * @obj_count: Number of objects assigned to the DPRC
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_get_obj_count(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ int *obj_count)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dprc_rsp_get_obj_count *rsp_params;
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ_COUNT,
+ cmd_flags, token);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ rsp_params = (struct dprc_rsp_get_obj_count *)cmd.params;
+ *obj_count = le32_to_cpu(rsp_params->obj_count);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dprc_get_obj_count);
+
+/**
+ * dprc_get_obj() - Get general information on an object
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPRC object
+ * @obj_index: Index of the object to be queried (< obj_count)
+ * @obj_desc: Returns the requested object descriptor
+ *
+ * The object descriptors are retrieved one by one by incrementing
+ * obj_index up to (not including) the value of obj_count returned
+ * from dprc_get_obj_count(). dprc_get_obj_count() must
+ * be called prior to dprc_get_obj().
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_get_obj(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ int obj_index,
+ struct fsl_mc_obj_desc *obj_desc)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dprc_cmd_get_obj *cmd_params;
+ struct dprc_rsp_get_obj *rsp_params;
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ,
+ cmd_flags,
+ token);
+ cmd_params = (struct dprc_cmd_get_obj *)cmd.params;
+ cmd_params->obj_index = cpu_to_le32(obj_index);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ rsp_params = (struct dprc_rsp_get_obj *)cmd.params;
+ obj_desc->id = le32_to_cpu(rsp_params->id);
+ obj_desc->vendor = le16_to_cpu(rsp_params->vendor);
+ obj_desc->irq_count = rsp_params->irq_count;
+ obj_desc->region_count = rsp_params->region_count;
+ obj_desc->state = le32_to_cpu(rsp_params->state);
+ obj_desc->ver_major = le16_to_cpu(rsp_params->version_major);
+ obj_desc->ver_minor = le16_to_cpu(rsp_params->version_minor);
+ obj_desc->flags = le16_to_cpu(rsp_params->flags);
+ strncpy(obj_desc->type, rsp_params->type, 16);
+ obj_desc->type[15] = '\0';
+ strncpy(obj_desc->label, rsp_params->label, 16);
+ obj_desc->label[15] = '\0';
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dprc_get_obj);
+
+/**
+ * dprc_set_obj_irq() - Set IRQ information for object to trigger an interrupt.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPRC object
+ * @obj_type: Type of the object to set its IRQ
+ * @obj_id: ID of the object to set its IRQ
+ * @irq_index: The interrupt index to configure
+ * @irq_cfg: IRQ configuration
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_set_obj_irq(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ char *obj_type,
+ int obj_id,
+ u8 irq_index,
+ struct dprc_irq_cfg *irq_cfg)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dprc_cmd_set_obj_irq *cmd_params;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_OBJ_IRQ,
+ cmd_flags,
+ token);
+ cmd_params = (struct dprc_cmd_set_obj_irq *)cmd.params;
+ cmd_params->irq_val = cpu_to_le32(irq_cfg->val);
+ cmd_params->irq_index = irq_index;
+ cmd_params->irq_addr = cpu_to_le64(irq_cfg->paddr);
+ cmd_params->irq_num = cpu_to_le32(irq_cfg->irq_num);
+ cmd_params->obj_id = cpu_to_le32(obj_id);
+ strncpy(cmd_params->obj_type, obj_type, 16);
+ cmd_params->obj_type[15] = '\0';
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+EXPORT_SYMBOL_GPL(dprc_set_obj_irq);
+
+/**
+ * dprc_get_obj_region() - Get region information for a specified object.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPRC object
+ * @obj_type; Object type as returned in dprc_get_obj()
+ * @obj_id: Unique object instance as returned in dprc_get_obj()
+ * @region_index: The specific region to query
+ * @region_desc: Returns the requested region descriptor
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_get_obj_region(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ char *obj_type,
+ int obj_id,
+ u8 region_index,
+ struct dprc_region_desc *region_desc)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dprc_cmd_get_obj_region *cmd_params;
+ struct dprc_rsp_get_obj_region *rsp_params;
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ_REG,
+ cmd_flags, token);
+ cmd_params = (struct dprc_cmd_get_obj_region *)cmd.params;
+ cmd_params->obj_id = cpu_to_le32(obj_id);
+ cmd_params->region_index = region_index;
+ strncpy(cmd_params->obj_type, obj_type, 16);
+ cmd_params->obj_type[15] = '\0';
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ rsp_params = (struct dprc_rsp_get_obj_region *)cmd.params;
+ region_desc->base_offset = le64_to_cpu(rsp_params->base_addr);
+ region_desc->size = le32_to_cpu(rsp_params->size);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dprc_get_obj_region);
+
+/**
+ * dprc_get_api_version - Get Data Path Resource Container API version
+ * @mc_io: Pointer to Mc portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @major_ver: Major version of Data Path Resource Container API
+ * @minor_ver: Minor version of Data Path Resource Container API
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_get_api_version(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 *major_ver,
+ u16 *minor_ver)
+{
+ struct fsl_mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_API_VERSION,
+ cmd_flags, 0);
+
+ /* send command to mc */
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ mc_cmd_read_api_version(&cmd, major_ver, minor_ver);
+
+ return 0;
+}
+
+/**
+ * dprc_get_container_id - Get container ID associated with a given portal.
+ * @mc_io: Pointer to Mc portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @container_id: Requested container id
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_get_container_id(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ int *container_id)
+{
+ struct fsl_mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_CONT_ID,
+ cmd_flags,
+ 0);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *container_id = (int)mc_cmd_read_object_id(&cmd);
+
+ return 0;
+}
diff --git a/drivers/bus/fsl-mc/fsl-mc-allocator.c b/drivers/bus/fsl-mc/fsl-mc-allocator.c
new file mode 100644
index 000000000000..fb1442b08962
--- /dev/null
+++ b/drivers/bus/fsl-mc/fsl-mc-allocator.c
@@ -0,0 +1,653 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * fsl-mc object allocator driver
+ *
+ * Copyright (C) 2013-2016 Freescale Semiconductor, Inc.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/fsl/mc.h>
+
+#include "fsl-mc-private.h"
+
+static bool __must_check fsl_mc_is_allocatable(struct fsl_mc_device *mc_dev)
+{
+ return is_fsl_mc_bus_dpbp(mc_dev) ||
+ is_fsl_mc_bus_dpmcp(mc_dev) ||
+ is_fsl_mc_bus_dpcon(mc_dev);
+}
+
+/**
+ * fsl_mc_resource_pool_add_device - add allocatable object to a resource
+ * pool of a given fsl-mc bus
+ *
+ * @mc_bus: pointer to the fsl-mc bus
+ * @pool_type: pool type
+ * @mc_dev: pointer to allocatable fsl-mc device
+ */
+static int __must_check fsl_mc_resource_pool_add_device(struct fsl_mc_bus
+ *mc_bus,
+ enum fsl_mc_pool_type
+ pool_type,
+ struct fsl_mc_device
+ *mc_dev)
+{
+ struct fsl_mc_resource_pool *res_pool;
+ struct fsl_mc_resource *resource;
+ struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev;
+ int error = -EINVAL;
+
+ if (pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES)
+ goto out;
+ if (!fsl_mc_is_allocatable(mc_dev))
+ goto out;
+ if (mc_dev->resource)
+ goto out;
+
+ res_pool = &mc_bus->resource_pools[pool_type];
+ if (res_pool->type != pool_type)
+ goto out;
+ if (res_pool->mc_bus != mc_bus)
+ goto out;
+
+ mutex_lock(&res_pool->mutex);
+
+ if (res_pool->max_count < 0)
+ goto out_unlock;
+ if (res_pool->free_count < 0 ||
+ res_pool->free_count > res_pool->max_count)
+ goto out_unlock;
+
+ resource = devm_kzalloc(&mc_bus_dev->dev, sizeof(*resource),
+ GFP_KERNEL);
+ if (!resource) {
+ error = -ENOMEM;
+ dev_err(&mc_bus_dev->dev,
+ "Failed to allocate memory for fsl_mc_resource\n");
+ goto out_unlock;
+ }
+
+ resource->type = pool_type;
+ resource->id = mc_dev->obj_desc.id;
+ resource->data = mc_dev;
+ resource->parent_pool = res_pool;
+ INIT_LIST_HEAD(&resource->node);
+ list_add_tail(&resource->node, &res_pool->free_list);
+ mc_dev->resource = resource;
+ res_pool->free_count++;
+ res_pool->max_count++;
+ error = 0;
+out_unlock:
+ mutex_unlock(&res_pool->mutex);
+out:
+ return error;
+}
+
+/**
+ * fsl_mc_resource_pool_remove_device - remove an allocatable device from a
+ * resource pool
+ *
+ * @mc_dev: pointer to allocatable fsl-mc device
+ *
+ * It permanently removes an allocatable fsl-mc device from the resource
+ * pool. It's an error if the device is in use.
+ */
+static int __must_check fsl_mc_resource_pool_remove_device(struct fsl_mc_device
+ *mc_dev)
+{
+ struct fsl_mc_device *mc_bus_dev;
+ struct fsl_mc_bus *mc_bus;
+ struct fsl_mc_resource_pool *res_pool;
+ struct fsl_mc_resource *resource;
+ int error = -EINVAL;
+
+ if (!fsl_mc_is_allocatable(mc_dev))
+ goto out;
+
+ resource = mc_dev->resource;
+ if (!resource || resource->data != mc_dev)
+ goto out;
+
+ mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
+ mc_bus = to_fsl_mc_bus(mc_bus_dev);
+ res_pool = resource->parent_pool;
+ if (res_pool != &mc_bus->resource_pools[resource->type])
+ goto out;
+
+ mutex_lock(&res_pool->mutex);
+
+ if (res_pool->max_count <= 0)
+ goto out_unlock;
+ if (res_pool->free_count <= 0 ||
+ res_pool->free_count > res_pool->max_count)
+ goto out_unlock;
+
+ /*
+ * If the device is currently allocated, its resource is not
+ * in the free list and thus, the device cannot be removed.
+ */
+ if (list_empty(&resource->node)) {
+ error = -EBUSY;
+ dev_err(&mc_bus_dev->dev,
+ "Device %s cannot be removed from resource pool\n",
+ dev_name(&mc_dev->dev));
+ goto out_unlock;
+ }
+
+ list_del_init(&resource->node);
+ res_pool->free_count--;
+ res_pool->max_count--;
+
+ devm_kfree(&mc_bus_dev->dev, resource);
+ mc_dev->resource = NULL;
+ error = 0;
+out_unlock:
+ mutex_unlock(&res_pool->mutex);
+out:
+ return error;
+}
+
+static const char *const fsl_mc_pool_type_strings[] = {
+ [FSL_MC_POOL_DPMCP] = "dpmcp",
+ [FSL_MC_POOL_DPBP] = "dpbp",
+ [FSL_MC_POOL_DPCON] = "dpcon",
+ [FSL_MC_POOL_IRQ] = "irq",
+};
+
+static int __must_check object_type_to_pool_type(const char *object_type,
+ enum fsl_mc_pool_type
+ *pool_type)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(fsl_mc_pool_type_strings); i++) {
+ if (strcmp(object_type, fsl_mc_pool_type_strings[i]) == 0) {
+ *pool_type = i;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+int __must_check fsl_mc_resource_allocate(struct fsl_mc_bus *mc_bus,
+ enum fsl_mc_pool_type pool_type,
+ struct fsl_mc_resource **new_resource)
+{
+ struct fsl_mc_resource_pool *res_pool;
+ struct fsl_mc_resource *resource;
+ struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev;
+ int error = -EINVAL;
+
+ BUILD_BUG_ON(ARRAY_SIZE(fsl_mc_pool_type_strings) !=
+ FSL_MC_NUM_POOL_TYPES);
+
+ *new_resource = NULL;
+ if (pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES)
+ goto out;
+
+ res_pool = &mc_bus->resource_pools[pool_type];
+ if (res_pool->mc_bus != mc_bus)
+ goto out;
+
+ mutex_lock(&res_pool->mutex);
+ resource = list_first_entry_or_null(&res_pool->free_list,
+ struct fsl_mc_resource, node);
+
+ if (!resource) {
+ error = -ENXIO;
+ dev_err(&mc_bus_dev->dev,
+ "No more resources of type %s left\n",
+ fsl_mc_pool_type_strings[pool_type]);
+ goto out_unlock;
+ }
+
+ if (resource->type != pool_type)
+ goto out_unlock;
+ if (resource->parent_pool != res_pool)
+ goto out_unlock;
+ if (res_pool->free_count <= 0 ||
+ res_pool->free_count > res_pool->max_count)
+ goto out_unlock;
+
+ list_del_init(&resource->node);
+
+ res_pool->free_count--;
+ error = 0;
+out_unlock:
+ mutex_unlock(&res_pool->mutex);
+ *new_resource = resource;
+out:
+ return error;
+}
+EXPORT_SYMBOL_GPL(fsl_mc_resource_allocate);
+
+void fsl_mc_resource_free(struct fsl_mc_resource *resource)
+{
+ struct fsl_mc_resource_pool *res_pool;
+
+ res_pool = resource->parent_pool;
+ if (resource->type != res_pool->type)
+ return;
+
+ mutex_lock(&res_pool->mutex);
+ if (res_pool->free_count < 0 ||
+ res_pool->free_count >= res_pool->max_count)
+ goto out_unlock;
+
+ if (!list_empty(&resource->node))
+ goto out_unlock;
+
+ list_add_tail(&resource->node, &res_pool->free_list);
+ res_pool->free_count++;
+out_unlock:
+ mutex_unlock(&res_pool->mutex);
+}
+EXPORT_SYMBOL_GPL(fsl_mc_resource_free);
+
+/**
+ * fsl_mc_object_allocate - Allocates an fsl-mc object of the given
+ * pool type from a given fsl-mc bus instance
+ *
+ * @mc_dev: fsl-mc device which is used in conjunction with the
+ * allocated object
+ * @pool_type: pool type
+ * @new_mc_dev: pointer to area where the pointer to the allocated device
+ * is to be returned
+ *
+ * Allocatable objects are always used in conjunction with some functional
+ * device. This function allocates an object of the specified type from
+ * the DPRC containing the functional device.
+ *
+ * NOTE: pool_type must be different from FSL_MC_POOL_MCP, since MC
+ * portals are allocated using fsl_mc_portal_allocate(), instead of
+ * this function.
+ */
+int __must_check fsl_mc_object_allocate(struct fsl_mc_device *mc_dev,
+ enum fsl_mc_pool_type pool_type,
+ struct fsl_mc_device **new_mc_adev)
+{
+ struct fsl_mc_device *mc_bus_dev;
+ struct fsl_mc_bus *mc_bus;
+ struct fsl_mc_device *mc_adev;
+ int error = -EINVAL;
+ struct fsl_mc_resource *resource = NULL;
+
+ *new_mc_adev = NULL;
+ if (mc_dev->flags & FSL_MC_IS_DPRC)
+ goto error;
+
+ if (!dev_is_fsl_mc(mc_dev->dev.parent))
+ goto error;
+
+ if (pool_type == FSL_MC_POOL_DPMCP)
+ goto error;
+
+ mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
+ mc_bus = to_fsl_mc_bus(mc_bus_dev);
+ error = fsl_mc_resource_allocate(mc_bus, pool_type, &resource);
+ if (error < 0)
+ goto error;
+
+ mc_adev = resource->data;
+ if (!mc_adev)
+ goto error;
+
+ *new_mc_adev = mc_adev;
+ return 0;
+error:
+ if (resource)
+ fsl_mc_resource_free(resource);
+
+ return error;
+}
+EXPORT_SYMBOL_GPL(fsl_mc_object_allocate);
+
+/**
+ * fsl_mc_object_free - Returns an fsl-mc object to the resource
+ * pool where it came from.
+ * @mc_adev: Pointer to the fsl-mc device
+ */
+void fsl_mc_object_free(struct fsl_mc_device *mc_adev)
+{
+ struct fsl_mc_resource *resource;
+
+ resource = mc_adev->resource;
+ if (resource->type == FSL_MC_POOL_DPMCP)
+ return;
+ if (resource->data != mc_adev)
+ return;
+
+ fsl_mc_resource_free(resource);
+}
+EXPORT_SYMBOL_GPL(fsl_mc_object_free);
+
+/*
+ * A DPRC and the devices in the DPRC all share the same GIC-ITS device
+ * ID. A block of IRQs is pre-allocated and maintained in a pool
+ * from which devices can allocate them when needed.
+ */
+
+/*
+ * Initialize the interrupt pool associated with an fsl-mc bus.
+ * It allocates a block of IRQs from the GIC-ITS.
+ */
+int fsl_mc_populate_irq_pool(struct fsl_mc_bus *mc_bus,
+ unsigned int irq_count)
+{
+ unsigned int i;
+ struct msi_desc *msi_desc;
+ struct fsl_mc_device_irq *irq_resources;
+ struct fsl_mc_device_irq *mc_dev_irq;
+ int error;
+ struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev;
+ struct fsl_mc_resource_pool *res_pool =
+ &mc_bus->resource_pools[FSL_MC_POOL_IRQ];
+
+ if (irq_count == 0 ||
+ irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS)
+ return -EINVAL;
+
+ error = fsl_mc_msi_domain_alloc_irqs(&mc_bus_dev->dev, irq_count);
+ if (error < 0)
+ return error;
+
+ irq_resources = devm_kzalloc(&mc_bus_dev->dev,
+ sizeof(*irq_resources) * irq_count,
+ GFP_KERNEL);
+ if (!irq_resources) {
+ error = -ENOMEM;
+ goto cleanup_msi_irqs;
+ }
+
+ for (i = 0; i < irq_count; i++) {
+ mc_dev_irq = &irq_resources[i];
+
+ /*
+ * NOTE: This mc_dev_irq's MSI addr/value pair will be set
+ * by the fsl_mc_msi_write_msg() callback
+ */
+ mc_dev_irq->resource.type = res_pool->type;
+ mc_dev_irq->resource.data = mc_dev_irq;
+ mc_dev_irq->resource.parent_pool = res_pool;
+ INIT_LIST_HEAD(&mc_dev_irq->resource.node);
+ list_add_tail(&mc_dev_irq->resource.node, &res_pool->free_list);
+ }
+
+ for_each_msi_entry(msi_desc, &mc_bus_dev->dev) {
+ mc_dev_irq = &irq_resources[msi_desc->fsl_mc.msi_index];
+ mc_dev_irq->msi_desc = msi_desc;
+ mc_dev_irq->resource.id = msi_desc->irq;
+ }
+
+ res_pool->max_count = irq_count;
+ res_pool->free_count = irq_count;
+ mc_bus->irq_resources = irq_resources;
+ return 0;
+
+cleanup_msi_irqs:
+ fsl_mc_msi_domain_free_irqs(&mc_bus_dev->dev);
+ return error;
+}
+EXPORT_SYMBOL_GPL(fsl_mc_populate_irq_pool);
+
+/**
+ * Teardown the interrupt pool associated with an fsl-mc bus.
+ * It frees the IRQs that were allocated to the pool, back to the GIC-ITS.
+ */
+void fsl_mc_cleanup_irq_pool(struct fsl_mc_bus *mc_bus)
+{
+ struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev;
+ struct fsl_mc_resource_pool *res_pool =
+ &mc_bus->resource_pools[FSL_MC_POOL_IRQ];
+
+ if (!mc_bus->irq_resources)
+ return;
+
+ if (res_pool->max_count == 0)
+ return;
+
+ if (res_pool->free_count != res_pool->max_count)
+ return;
+
+ INIT_LIST_HEAD(&res_pool->free_list);
+ res_pool->max_count = 0;
+ res_pool->free_count = 0;
+ mc_bus->irq_resources = NULL;
+ fsl_mc_msi_domain_free_irqs(&mc_bus_dev->dev);
+}
+EXPORT_SYMBOL_GPL(fsl_mc_cleanup_irq_pool);
+
+/**
+ * Allocate the IRQs required by a given fsl-mc device.
+ */
+int __must_check fsl_mc_allocate_irqs(struct fsl_mc_device *mc_dev)
+{
+ int i;
+ int irq_count;
+ int res_allocated_count = 0;
+ int error = -EINVAL;
+ struct fsl_mc_device_irq **irqs = NULL;
+ struct fsl_mc_bus *mc_bus;
+ struct fsl_mc_resource_pool *res_pool;
+
+ if (mc_dev->irqs)
+ return -EINVAL;
+
+ irq_count = mc_dev->obj_desc.irq_count;
+ if (irq_count == 0)
+ return -EINVAL;
+
+ if (is_fsl_mc_bus_dprc(mc_dev))
+ mc_bus = to_fsl_mc_bus(mc_dev);
+ else
+ mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent));
+
+ if (!mc_bus->irq_resources)
+ return -EINVAL;
+
+ res_pool = &mc_bus->resource_pools[FSL_MC_POOL_IRQ];
+ if (res_pool->free_count < irq_count) {
+ dev_err(&mc_dev->dev,
+ "Not able to allocate %u irqs for device\n", irq_count);
+ return -ENOSPC;
+ }
+
+ irqs = devm_kzalloc(&mc_dev->dev, irq_count * sizeof(irqs[0]),
+ GFP_KERNEL);
+ if (!irqs)
+ return -ENOMEM;
+
+ for (i = 0; i < irq_count; i++) {
+ struct fsl_mc_resource *resource;
+
+ error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_IRQ,
+ &resource);
+ if (error < 0)
+ goto error_resource_alloc;
+
+ irqs[i] = to_fsl_mc_irq(resource);
+ res_allocated_count++;
+
+ irqs[i]->mc_dev = mc_dev;
+ irqs[i]->dev_irq_index = i;
+ }
+
+ mc_dev->irqs = irqs;
+ return 0;
+
+error_resource_alloc:
+ for (i = 0; i < res_allocated_count; i++) {
+ irqs[i]->mc_dev = NULL;
+ fsl_mc_resource_free(&irqs[i]->resource);
+ }
+
+ return error;
+}
+EXPORT_SYMBOL_GPL(fsl_mc_allocate_irqs);
+
+/*
+ * Frees the IRQs that were allocated for an fsl-mc device.
+ */
+void fsl_mc_free_irqs(struct fsl_mc_device *mc_dev)
+{
+ int i;
+ int irq_count;
+ struct fsl_mc_bus *mc_bus;
+ struct fsl_mc_device_irq **irqs = mc_dev->irqs;
+
+ if (!irqs)
+ return;
+
+ irq_count = mc_dev->obj_desc.irq_count;
+
+ if (is_fsl_mc_bus_dprc(mc_dev))
+ mc_bus = to_fsl_mc_bus(mc_dev);
+ else
+ mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent));
+
+ if (!mc_bus->irq_resources)
+ return;
+
+ for (i = 0; i < irq_count; i++) {
+ irqs[i]->mc_dev = NULL;
+ fsl_mc_resource_free(&irqs[i]->resource);
+ }
+
+ mc_dev->irqs = NULL;
+}
+EXPORT_SYMBOL_GPL(fsl_mc_free_irqs);
+
+void fsl_mc_init_all_resource_pools(struct fsl_mc_device *mc_bus_dev)
+{
+ int pool_type;
+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
+
+ for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++) {
+ struct fsl_mc_resource_pool *res_pool =
+ &mc_bus->resource_pools[pool_type];
+
+ res_pool->type = pool_type;
+ res_pool->max_count = 0;
+ res_pool->free_count = 0;
+ res_pool->mc_bus = mc_bus;
+ INIT_LIST_HEAD(&res_pool->free_list);
+ mutex_init(&res_pool->mutex);
+ }
+}
+
+static void fsl_mc_cleanup_resource_pool(struct fsl_mc_device *mc_bus_dev,
+ enum fsl_mc_pool_type pool_type)
+{
+ struct fsl_mc_resource *resource;
+ struct fsl_mc_resource *next;
+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
+ struct fsl_mc_resource_pool *res_pool =
+ &mc_bus->resource_pools[pool_type];
+ int free_count = 0;
+
+ list_for_each_entry_safe(resource, next, &res_pool->free_list, node) {
+ free_count++;
+ devm_kfree(&mc_bus_dev->dev, resource);
+ }
+}
+
+void fsl_mc_cleanup_all_resource_pools(struct fsl_mc_device *mc_bus_dev)
+{
+ int pool_type;
+
+ for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++)
+ fsl_mc_cleanup_resource_pool(mc_bus_dev, pool_type);
+}
+
+/**
+ * fsl_mc_allocator_probe - callback invoked when an allocatable device is
+ * being added to the system
+ */
+static int fsl_mc_allocator_probe(struct fsl_mc_device *mc_dev)
+{
+ enum fsl_mc_pool_type pool_type;
+ struct fsl_mc_device *mc_bus_dev;
+ struct fsl_mc_bus *mc_bus;
+ int error;
+
+ if (!fsl_mc_is_allocatable(mc_dev))
+ return -EINVAL;
+
+ mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
+ if (!dev_is_fsl_mc(&mc_bus_dev->dev))
+ return -EINVAL;
+
+ mc_bus = to_fsl_mc_bus(mc_bus_dev);
+ error = object_type_to_pool_type(mc_dev->obj_desc.type, &pool_type);
+ if (error < 0)
+ return error;
+
+ error = fsl_mc_resource_pool_add_device(mc_bus, pool_type, mc_dev);
+ if (error < 0)
+ return error;
+
+ dev_dbg(&mc_dev->dev,
+ "Allocatable fsl-mc device bound to fsl_mc_allocator driver");
+ return 0;
+}
+
+/**
+ * fsl_mc_allocator_remove - callback invoked when an allocatable device is
+ * being removed from the system
+ */
+static int fsl_mc_allocator_remove(struct fsl_mc_device *mc_dev)
+{
+ int error;
+
+ if (!fsl_mc_is_allocatable(mc_dev))
+ return -EINVAL;
+
+ if (mc_dev->resource) {
+ error = fsl_mc_resource_pool_remove_device(mc_dev);
+ if (error < 0)
+ return error;
+ }
+
+ dev_dbg(&mc_dev->dev,
+ "Allocatable fsl-mc device unbound from fsl_mc_allocator driver");
+ return 0;
+}
+
+static const struct fsl_mc_device_id match_id_table[] = {
+ {
+ .vendor = FSL_MC_VENDOR_FREESCALE,
+ .obj_type = "dpbp",
+ },
+ {
+ .vendor = FSL_MC_VENDOR_FREESCALE,
+ .obj_type = "dpmcp",
+ },
+ {
+ .vendor = FSL_MC_VENDOR_FREESCALE,
+ .obj_type = "dpcon",
+ },
+ {.vendor = 0x0},
+};
+
+static struct fsl_mc_driver fsl_mc_allocator_driver = {
+ .driver = {
+ .name = "fsl_mc_allocator",
+ .pm = NULL,
+ },
+ .match_id_table = match_id_table,
+ .probe = fsl_mc_allocator_probe,
+ .remove = fsl_mc_allocator_remove,
+};
+
+int __init fsl_mc_allocator_driver_init(void)
+{
+ return fsl_mc_driver_register(&fsl_mc_allocator_driver);
+}
+
+void fsl_mc_allocator_driver_exit(void)
+{
+ fsl_mc_driver_unregister(&fsl_mc_allocator_driver);
+}
diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c
new file mode 100644
index 000000000000..5d8266c6571f
--- /dev/null
+++ b/drivers/bus/fsl-mc/fsl-mc-bus.c
@@ -0,0 +1,948 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Freescale Management Complex (MC) bus driver
+ *
+ * Copyright (C) 2014-2016 Freescale Semiconductor, Inc.
+ * Author: German Rivera <German.Rivera@freescale.com>
+ *
+ */
+
+#define pr_fmt(fmt) "fsl-mc: " fmt
+
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/limits.h>
+#include <linux/bitops.h>
+#include <linux/msi.h>
+#include <linux/dma-mapping.h>
+
+#include "fsl-mc-private.h"
+
+/**
+ * Default DMA mask for devices on a fsl-mc bus
+ */
+#define FSL_MC_DEFAULT_DMA_MASK (~0ULL)
+
+/**
+ * struct fsl_mc - Private data of a "fsl,qoriq-mc" platform device
+ * @root_mc_bus_dev: fsl-mc device representing the root DPRC
+ * @num_translation_ranges: number of entries in addr_translation_ranges
+ * @translation_ranges: array of bus to system address translation ranges
+ */
+struct fsl_mc {
+ struct fsl_mc_device *root_mc_bus_dev;
+ u8 num_translation_ranges;
+ struct fsl_mc_addr_translation_range *translation_ranges;
+};
+
+/**
+ * struct fsl_mc_addr_translation_range - bus to system address translation
+ * range
+ * @mc_region_type: Type of MC region for the range being translated
+ * @start_mc_offset: Start MC offset of the range being translated
+ * @end_mc_offset: MC offset of the first byte after the range (last MC
+ * offset of the range is end_mc_offset - 1)
+ * @start_phys_addr: system physical address corresponding to start_mc_addr
+ */
+struct fsl_mc_addr_translation_range {
+ enum dprc_region_type mc_region_type;
+ u64 start_mc_offset;
+ u64 end_mc_offset;
+ phys_addr_t start_phys_addr;
+};
+
+/**
+ * struct mc_version
+ * @major: Major version number: incremented on API compatibility changes
+ * @minor: Minor version number: incremented on API additions (that are
+ * backward compatible); reset when major version is incremented
+ * @revision: Internal revision number: incremented on implementation changes
+ * and/or bug fixes that have no impact on API
+ */
+struct mc_version {
+ u32 major;
+ u32 minor;
+ u32 revision;
+};
+
+/**
+ * fsl_mc_bus_match - device to driver matching callback
+ * @dev: the fsl-mc device to match against
+ * @drv: the device driver to search for matching fsl-mc object type
+ * structures
+ *
+ * Returns 1 on success, 0 otherwise.
+ */
+static int fsl_mc_bus_match(struct device *dev, struct device_driver *drv)
+{
+ const struct fsl_mc_device_id *id;
+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
+ struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(drv);
+ bool found = false;
+
+ if (!mc_drv->match_id_table)
+ goto out;
+
+ /*
+ * If the object is not 'plugged' don't match.
+ * Only exception is the root DPRC, which is a special case.
+ */
+ if ((mc_dev->obj_desc.state & FSL_MC_OBJ_STATE_PLUGGED) == 0 &&
+ !fsl_mc_is_root_dprc(&mc_dev->dev))
+ goto out;
+
+ /*
+ * Traverse the match_id table of the given driver, trying to find
+ * a matching for the given device.
+ */
+ for (id = mc_drv->match_id_table; id->vendor != 0x0; id++) {
+ if (id->vendor == mc_dev->obj_desc.vendor &&
+ strcmp(id->obj_type, mc_dev->obj_desc.type) == 0) {
+ found = true;
+
+ break;
+ }
+ }
+
+out:
+ dev_dbg(dev, "%smatched\n", found ? "" : "not ");
+ return found;
+}
+
+/**
+ * fsl_mc_bus_uevent - callback invoked when a device is added
+ */
+static int fsl_mc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
+
+ if (add_uevent_var(env, "MODALIAS=fsl-mc:v%08Xd%s",
+ mc_dev->obj_desc.vendor,
+ mc_dev->obj_desc.type))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
+
+ return sprintf(buf, "fsl-mc:v%08Xd%s\n", mc_dev->obj_desc.vendor,
+ mc_dev->obj_desc.type);
+}
+static DEVICE_ATTR_RO(modalias);
+
+static struct attribute *fsl_mc_dev_attrs[] = {
+ &dev_attr_modalias.attr,
+ NULL,
+};
+
+ATTRIBUTE_GROUPS(fsl_mc_dev);
+
+struct bus_type fsl_mc_bus_type = {
+ .name = "fsl-mc",
+ .match = fsl_mc_bus_match,
+ .uevent = fsl_mc_bus_uevent,
+ .dev_groups = fsl_mc_dev_groups,
+};
+EXPORT_SYMBOL_GPL(fsl_mc_bus_type);
+
+struct device_type fsl_mc_bus_dprc_type = {
+ .name = "fsl_mc_bus_dprc"
+};
+
+struct device_type fsl_mc_bus_dpni_type = {
+ .name = "fsl_mc_bus_dpni"
+};
+
+struct device_type fsl_mc_bus_dpio_type = {
+ .name = "fsl_mc_bus_dpio"
+};
+
+struct device_type fsl_mc_bus_dpsw_type = {
+ .name = "fsl_mc_bus_dpsw"
+};
+
+struct device_type fsl_mc_bus_dpbp_type = {
+ .name = "fsl_mc_bus_dpbp"
+};
+
+struct device_type fsl_mc_bus_dpcon_type = {
+ .name = "fsl_mc_bus_dpcon"
+};
+
+struct device_type fsl_mc_bus_dpmcp_type = {
+ .name = "fsl_mc_bus_dpmcp"
+};
+
+struct device_type fsl_mc_bus_dpmac_type = {
+ .name = "fsl_mc_bus_dpmac"
+};
+
+struct device_type fsl_mc_bus_dprtc_type = {
+ .name = "fsl_mc_bus_dprtc"
+};
+
+static struct device_type *fsl_mc_get_device_type(const char *type)
+{
+ static const struct {
+ struct device_type *dev_type;
+ const char *type;
+ } dev_types[] = {
+ { &fsl_mc_bus_dprc_type, "dprc" },
+ { &fsl_mc_bus_dpni_type, "dpni" },
+ { &fsl_mc_bus_dpio_type, "dpio" },
+ { &fsl_mc_bus_dpsw_type, "dpsw" },
+ { &fsl_mc_bus_dpbp_type, "dpbp" },
+ { &fsl_mc_bus_dpcon_type, "dpcon" },
+ { &fsl_mc_bus_dpmcp_type, "dpmcp" },
+ { &fsl_mc_bus_dpmac_type, "dpmac" },
+ { &fsl_mc_bus_dprtc_type, "dprtc" },
+ { NULL, NULL }
+ };
+ int i;
+
+ for (i = 0; dev_types[i].dev_type; i++)
+ if (!strcmp(dev_types[i].type, type))
+ return dev_types[i].dev_type;
+
+ return NULL;
+}
+
+static int fsl_mc_driver_probe(struct device *dev)
+{
+ struct fsl_mc_driver *mc_drv;
+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
+ int error;
+
+ mc_drv = to_fsl_mc_driver(dev->driver);
+
+ error = mc_drv->probe(mc_dev);
+ if (error < 0) {
+ if (error != -EPROBE_DEFER)
+ dev_err(dev, "%s failed: %d\n", __func__, error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int fsl_mc_driver_remove(struct device *dev)
+{
+ struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
+ int error;
+
+ error = mc_drv->remove(mc_dev);
+ if (error < 0) {
+ dev_err(dev, "%s failed: %d\n", __func__, error);
+ return error;
+ }
+
+ return 0;
+}
+
+static void fsl_mc_driver_shutdown(struct device *dev)
+{
+ struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
+
+ mc_drv->shutdown(mc_dev);
+}
+
+/**
+ * __fsl_mc_driver_register - registers a child device driver with the
+ * MC bus
+ *
+ * This function is implicitly invoked from the registration function of
+ * fsl_mc device drivers, which is generated by the
+ * module_fsl_mc_driver() macro.
+ */
+int __fsl_mc_driver_register(struct fsl_mc_driver *mc_driver,
+ struct module *owner)
+{
+ int error;
+
+ mc_driver->driver.owner = owner;
+ mc_driver->driver.bus = &fsl_mc_bus_type;
+
+ if (mc_driver->probe)
+ mc_driver->driver.probe = fsl_mc_driver_probe;
+
+ if (mc_driver->remove)
+ mc_driver->driver.remove = fsl_mc_driver_remove;
+
+ if (mc_driver->shutdown)
+ mc_driver->driver.shutdown = fsl_mc_driver_shutdown;
+
+ error = driver_register(&mc_driver->driver);
+ if (error < 0) {
+ pr_err("driver_register() failed for %s: %d\n",
+ mc_driver->driver.name, error);
+ return error;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__fsl_mc_driver_register);
+
+/**
+ * fsl_mc_driver_unregister - unregisters a device driver from the
+ * MC bus
+ */
+void fsl_mc_driver_unregister(struct fsl_mc_driver *mc_driver)
+{
+ driver_unregister(&mc_driver->driver);
+}
+EXPORT_SYMBOL_GPL(fsl_mc_driver_unregister);
+
+/**
+ * mc_get_version() - Retrieves the Management Complex firmware
+ * version information
+ * @mc_io: Pointer to opaque I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @mc_ver_info: Returned version information structure
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+static int mc_get_version(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ struct mc_version *mc_ver_info)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpmng_rsp_get_version *rsp_params;
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPMNG_CMDID_GET_VERSION,
+ cmd_flags,
+ 0);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ rsp_params = (struct dpmng_rsp_get_version *)cmd.params;
+ mc_ver_info->revision = le32_to_cpu(rsp_params->revision);
+ mc_ver_info->major = le32_to_cpu(rsp_params->version_major);
+ mc_ver_info->minor = le32_to_cpu(rsp_params->version_minor);
+
+ return 0;
+}
+
+/**
+ * fsl_mc_get_root_dprc - function to traverse to the root dprc
+ */
+static void fsl_mc_get_root_dprc(struct device *dev,
+ struct device **root_dprc_dev)
+{
+ if (!dev) {
+ *root_dprc_dev = NULL;
+ } else if (!dev_is_fsl_mc(dev)) {
+ *root_dprc_dev = NULL;
+ } else {
+ *root_dprc_dev = dev;
+ while (dev_is_fsl_mc((*root_dprc_dev)->parent))
+ *root_dprc_dev = (*root_dprc_dev)->parent;
+ }
+}
+
+static int get_dprc_attr(struct fsl_mc_io *mc_io,
+ int container_id, struct dprc_attributes *attr)
+{
+ u16 dprc_handle;
+ int error;
+
+ error = dprc_open(mc_io, 0, container_id, &dprc_handle);
+ if (error < 0) {
+ dev_err(mc_io->dev, "dprc_open() failed: %d\n", error);
+ return error;
+ }
+
+ memset(attr, 0, sizeof(struct dprc_attributes));
+ error = dprc_get_attributes(mc_io, 0, dprc_handle, attr);
+ if (error < 0) {
+ dev_err(mc_io->dev, "dprc_get_attributes() failed: %d\n",
+ error);
+ goto common_cleanup;
+ }
+
+ error = 0;
+
+common_cleanup:
+ (void)dprc_close(mc_io, 0, dprc_handle);
+ return error;
+}
+
+static int get_dprc_icid(struct fsl_mc_io *mc_io,
+ int container_id, u16 *icid)
+{
+ struct dprc_attributes attr;
+ int error;
+
+ error = get_dprc_attr(mc_io, container_id, &attr);
+ if (error == 0)
+ *icid = attr.icid;
+
+ return error;
+}
+
+static int translate_mc_addr(struct fsl_mc_device *mc_dev,
+ enum dprc_region_type mc_region_type,
+ u64 mc_offset, phys_addr_t *phys_addr)
+{
+ int i;
+ struct device *root_dprc_dev;
+ struct fsl_mc *mc;
+
+ fsl_mc_get_root_dprc(&mc_dev->dev, &root_dprc_dev);
+ mc = dev_get_drvdata(root_dprc_dev->parent);
+
+ if (mc->num_translation_ranges == 0) {
+ /*
+ * Do identity mapping:
+ */
+ *phys_addr = mc_offset;
+ return 0;
+ }
+
+ for (i = 0; i < mc->num_translation_ranges; i++) {
+ struct fsl_mc_addr_translation_range *range =
+ &mc->translation_ranges[i];
+
+ if (mc_region_type == range->mc_region_type &&
+ mc_offset >= range->start_mc_offset &&
+ mc_offset < range->end_mc_offset) {
+ *phys_addr = range->start_phys_addr +
+ (mc_offset - range->start_mc_offset);
+ return 0;
+ }
+ }
+
+ return -EFAULT;
+}
+
+static int fsl_mc_device_get_mmio_regions(struct fsl_mc_device *mc_dev,
+ struct fsl_mc_device *mc_bus_dev)
+{
+ int i;
+ int error;
+ struct resource *regions;
+ struct fsl_mc_obj_desc *obj_desc = &mc_dev->obj_desc;
+ struct device *parent_dev = mc_dev->dev.parent;
+ enum dprc_region_type mc_region_type;
+
+ if (is_fsl_mc_bus_dprc(mc_dev) ||
+ is_fsl_mc_bus_dpmcp(mc_dev)) {
+ mc_region_type = DPRC_REGION_TYPE_MC_PORTAL;
+ } else if (is_fsl_mc_bus_dpio(mc_dev)) {
+ mc_region_type = DPRC_REGION_TYPE_QBMAN_PORTAL;
+ } else {
+ /*
+ * This function should not have been called for this MC object
+ * type, as this object type is not supposed to have MMIO
+ * regions
+ */
+ return -EINVAL;
+ }
+
+ regions = kmalloc_array(obj_desc->region_count,
+ sizeof(regions[0]), GFP_KERNEL);
+ if (!regions)
+ return -ENOMEM;
+
+ for (i = 0; i < obj_desc->region_count; i++) {
+ struct dprc_region_desc region_desc;
+
+ error = dprc_get_obj_region(mc_bus_dev->mc_io,
+ 0,
+ mc_bus_dev->mc_handle,
+ obj_desc->type,
+ obj_desc->id, i, &region_desc);
+ if (error < 0) {
+ dev_err(parent_dev,
+ "dprc_get_obj_region() failed: %d\n", error);
+ goto error_cleanup_regions;
+ }
+
+ error = translate_mc_addr(mc_dev, mc_region_type,
+ region_desc.base_offset,
+ &regions[i].start);
+ if (error < 0) {
+ dev_err(parent_dev,
+ "Invalid MC offset: %#x (for %s.%d\'s region %d)\n",
+ region_desc.base_offset,
+ obj_desc->type, obj_desc->id, i);
+ goto error_cleanup_regions;
+ }
+
+ regions[i].end = regions[i].start + region_desc.size - 1;
+ regions[i].name = "fsl-mc object MMIO region";
+ regions[i].flags = IORESOURCE_IO;
+ if (region_desc.flags & DPRC_REGION_CACHEABLE)
+ regions[i].flags |= IORESOURCE_CACHEABLE;
+ }
+
+ mc_dev->regions = regions;
+ return 0;
+
+error_cleanup_regions:
+ kfree(regions);
+ return error;
+}
+
+/**
+ * fsl_mc_is_root_dprc - function to check if a given device is a root dprc
+ */
+bool fsl_mc_is_root_dprc(struct device *dev)
+{
+ struct device *root_dprc_dev;
+
+ fsl_mc_get_root_dprc(dev, &root_dprc_dev);
+ if (!root_dprc_dev)
+ return false;
+ return dev == root_dprc_dev;
+}
+
+static void fsl_mc_device_release(struct device *dev)
+{
+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
+
+ kfree(mc_dev->regions);
+
+ if (is_fsl_mc_bus_dprc(mc_dev))
+ kfree(to_fsl_mc_bus(mc_dev));
+ else
+ kfree(mc_dev);
+}
+
+/**
+ * Add a newly discovered fsl-mc device to be visible in Linux
+ */
+int fsl_mc_device_add(struct fsl_mc_obj_desc *obj_desc,
+ struct fsl_mc_io *mc_io,
+ struct device *parent_dev,
+ struct fsl_mc_device **new_mc_dev)
+{
+ int error;
+ struct fsl_mc_device *mc_dev = NULL;
+ struct fsl_mc_bus *mc_bus = NULL;
+ struct fsl_mc_device *parent_mc_dev;
+
+ if (dev_is_fsl_mc(parent_dev))
+ parent_mc_dev = to_fsl_mc_device(parent_dev);
+ else
+ parent_mc_dev = NULL;
+
+ if (strcmp(obj_desc->type, "dprc") == 0) {
+ /*
+ * Allocate an MC bus device object:
+ */
+ mc_bus = kzalloc(sizeof(*mc_bus), GFP_KERNEL);
+ if (!mc_bus)
+ return -ENOMEM;
+
+ mc_dev = &mc_bus->mc_dev;
+ } else {
+ /*
+ * Allocate a regular fsl_mc_device object:
+ */
+ mc_dev = kzalloc(sizeof(*mc_dev), GFP_KERNEL);
+ if (!mc_dev)
+ return -ENOMEM;
+ }
+
+ mc_dev->obj_desc = *obj_desc;
+ mc_dev->mc_io = mc_io;
+ device_initialize(&mc_dev->dev);
+ mc_dev->dev.parent = parent_dev;
+ mc_dev->dev.bus = &fsl_mc_bus_type;
+ mc_dev->dev.release = fsl_mc_device_release;
+ mc_dev->dev.type = fsl_mc_get_device_type(obj_desc->type);
+ if (!mc_dev->dev.type) {
+ error = -ENODEV;
+ dev_err(parent_dev, "unknown device type %s\n", obj_desc->type);
+ goto error_cleanup_dev;
+ }
+ dev_set_name(&mc_dev->dev, "%s.%d", obj_desc->type, obj_desc->id);
+
+ if (strcmp(obj_desc->type, "dprc") == 0) {
+ struct fsl_mc_io *mc_io2;
+
+ mc_dev->flags |= FSL_MC_IS_DPRC;
+
+ /*
+ * To get the DPRC's ICID, we need to open the DPRC
+ * in get_dprc_icid(). For child DPRCs, we do so using the
+ * parent DPRC's MC portal instead of the child DPRC's MC
+ * portal, in case the child DPRC is already opened with
+ * its own portal (e.g., the DPRC used by AIOP).
+ *
+ * NOTE: There cannot be more than one active open for a
+ * given MC object, using the same MC portal.
+ */
+ if (parent_mc_dev) {
+ /*
+ * device being added is a child DPRC device
+ */
+ mc_io2 = parent_mc_dev->mc_io;
+ } else {
+ /*
+ * device being added is the root DPRC device
+ */
+ if (!mc_io) {
+ error = -EINVAL;
+ goto error_cleanup_dev;
+ }
+
+ mc_io2 = mc_io;
+ }
+
+ error = get_dprc_icid(mc_io2, obj_desc->id, &mc_dev->icid);
+ if (error < 0)
+ goto error_cleanup_dev;
+ } else {
+ /*
+ * A non-DPRC object has to be a child of a DPRC, use the
+ * parent's ICID and interrupt domain.
+ */
+ mc_dev->icid = parent_mc_dev->icid;
+ mc_dev->dma_mask = FSL_MC_DEFAULT_DMA_MASK;
+ mc_dev->dev.dma_mask = &mc_dev->dma_mask;
+ dev_set_msi_domain(&mc_dev->dev,
+ dev_get_msi_domain(&parent_mc_dev->dev));
+ }
+
+ /*
+ * Get MMIO regions for the device from the MC:
+ *
+ * NOTE: the root DPRC is a special case as its MMIO region is
+ * obtained from the device tree
+ */
+ if (parent_mc_dev && obj_desc->region_count != 0) {
+ error = fsl_mc_device_get_mmio_regions(mc_dev,
+ parent_mc_dev);
+ if (error < 0)
+ goto error_cleanup_dev;
+ }
+
+ /* Objects are coherent, unless 'no shareability' flag set. */
+ if (!(obj_desc->flags & FSL_MC_OBJ_FLAG_NO_MEM_SHAREABILITY))
+ arch_setup_dma_ops(&mc_dev->dev, 0, 0, NULL, true);
+
+ /*
+ * The device-specific probe callback will get invoked by device_add()
+ */
+ error = device_add(&mc_dev->dev);
+ if (error < 0) {
+ dev_err(parent_dev,
+ "device_add() failed for device %s: %d\n",
+ dev_name(&mc_dev->dev), error);
+ goto error_cleanup_dev;
+ }
+
+ dev_dbg(parent_dev, "added %s\n", dev_name(&mc_dev->dev));
+
+ *new_mc_dev = mc_dev;
+ return 0;
+
+error_cleanup_dev:
+ kfree(mc_dev->regions);
+ kfree(mc_bus);
+ kfree(mc_dev);
+
+ return error;
+}
+EXPORT_SYMBOL_GPL(fsl_mc_device_add);
+
+/**
+ * fsl_mc_device_remove - Remove an fsl-mc device from being visible to
+ * Linux
+ *
+ * @mc_dev: Pointer to an fsl-mc device
+ */
+void fsl_mc_device_remove(struct fsl_mc_device *mc_dev)
+{
+ /*
+ * The device-specific remove callback will get invoked by device_del()
+ */
+ device_del(&mc_dev->dev);
+ put_device(&mc_dev->dev);
+}
+EXPORT_SYMBOL_GPL(fsl_mc_device_remove);
+
+static int parse_mc_ranges(struct device *dev,
+ int *paddr_cells,
+ int *mc_addr_cells,
+ int *mc_size_cells,
+ const __be32 **ranges_start)
+{
+ const __be32 *prop;
+ int range_tuple_cell_count;
+ int ranges_len;
+ int tuple_len;
+ struct device_node *mc_node = dev->of_node;
+
+ *ranges_start = of_get_property(mc_node, "ranges", &ranges_len);
+ if (!(*ranges_start) || !ranges_len) {
+ dev_warn(dev,
+ "missing or empty ranges property for device tree node '%s'\n",
+ mc_node->name);
+ return 0;
+ }
+
+ *paddr_cells = of_n_addr_cells(mc_node);
+
+ prop = of_get_property(mc_node, "#address-cells", NULL);
+ if (prop)
+ *mc_addr_cells = be32_to_cpup(prop);
+ else
+ *mc_addr_cells = *paddr_cells;
+
+ prop = of_get_property(mc_node, "#size-cells", NULL);
+ if (prop)
+ *mc_size_cells = be32_to_cpup(prop);
+ else
+ *mc_size_cells = of_n_size_cells(mc_node);
+
+ range_tuple_cell_count = *paddr_cells + *mc_addr_cells +
+ *mc_size_cells;
+
+ tuple_len = range_tuple_cell_count * sizeof(__be32);
+ if (ranges_len % tuple_len != 0) {
+ dev_err(dev, "malformed ranges property '%s'\n", mc_node->name);
+ return -EINVAL;
+ }
+
+ return ranges_len / tuple_len;
+}
+
+static int get_mc_addr_translation_ranges(struct device *dev,
+ struct fsl_mc_addr_translation_range
+ **ranges,
+ u8 *num_ranges)
+{
+ int ret;
+ int paddr_cells;
+ int mc_addr_cells;
+ int mc_size_cells;
+ int i;
+ const __be32 *ranges_start;
+ const __be32 *cell;
+
+ ret = parse_mc_ranges(dev,
+ &paddr_cells,
+ &mc_addr_cells,
+ &mc_size_cells,
+ &ranges_start);
+ if (ret < 0)
+ return ret;
+
+ *num_ranges = ret;
+ if (!ret) {
+ /*
+ * Missing or empty ranges property ("ranges;") for the
+ * 'fsl,qoriq-mc' node. In this case, identity mapping
+ * will be used.
+ */
+ *ranges = NULL;
+ return 0;
+ }
+
+ *ranges = devm_kcalloc(dev, *num_ranges,
+ sizeof(struct fsl_mc_addr_translation_range),
+ GFP_KERNEL);
+ if (!(*ranges))
+ return -ENOMEM;
+
+ cell = ranges_start;
+ for (i = 0; i < *num_ranges; ++i) {
+ struct fsl_mc_addr_translation_range *range = &(*ranges)[i];
+
+ range->mc_region_type = of_read_number(cell, 1);
+ range->start_mc_offset = of_read_number(cell + 1,
+ mc_addr_cells - 1);
+ cell += mc_addr_cells;
+ range->start_phys_addr = of_read_number(cell, paddr_cells);
+ cell += paddr_cells;
+ range->end_mc_offset = range->start_mc_offset +
+ of_read_number(cell, mc_size_cells);
+
+ cell += mc_size_cells;
+ }
+
+ return 0;
+}
+
+/**
+ * fsl_mc_bus_probe - callback invoked when the root MC bus is being
+ * added
+ */
+static int fsl_mc_bus_probe(struct platform_device *pdev)
+{
+ struct fsl_mc_obj_desc obj_desc;
+ int error;
+ struct fsl_mc *mc;
+ struct fsl_mc_device *mc_bus_dev = NULL;
+ struct fsl_mc_io *mc_io = NULL;
+ int container_id;
+ phys_addr_t mc_portal_phys_addr;
+ u32 mc_portal_size;
+ struct mc_version mc_version;
+ struct resource res;
+
+ mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL);
+ if (!mc)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, mc);
+
+ /*
+ * Get physical address of MC portal for the root DPRC:
+ */
+ error = of_address_to_resource(pdev->dev.of_node, 0, &res);
+ if (error < 0) {
+ dev_err(&pdev->dev,
+ "of_address_to_resource() failed for %pOF\n",
+ pdev->dev.of_node);
+ return error;
+ }
+
+ mc_portal_phys_addr = res.start;
+ mc_portal_size = resource_size(&res);
+ error = fsl_create_mc_io(&pdev->dev, mc_portal_phys_addr,
+ mc_portal_size, NULL,
+ FSL_MC_IO_ATOMIC_CONTEXT_PORTAL, &mc_io);
+ if (error < 0)
+ return error;
+
+ error = mc_get_version(mc_io, 0, &mc_version);
+ if (error != 0) {
+ dev_err(&pdev->dev,
+ "mc_get_version() failed with error %d\n", error);
+ goto error_cleanup_mc_io;
+ }
+
+ dev_info(&pdev->dev, "MC firmware version: %u.%u.%u\n",
+ mc_version.major, mc_version.minor, mc_version.revision);
+
+ error = get_mc_addr_translation_ranges(&pdev->dev,
+ &mc->translation_ranges,
+ &mc->num_translation_ranges);
+ if (error < 0)
+ goto error_cleanup_mc_io;
+
+ error = dprc_get_container_id(mc_io, 0, &container_id);
+ if (error < 0) {
+ dev_err(&pdev->dev,
+ "dprc_get_container_id() failed: %d\n", error);
+ goto error_cleanup_mc_io;
+ }
+
+ memset(&obj_desc, 0, sizeof(struct fsl_mc_obj_desc));
+ error = dprc_get_api_version(mc_io, 0,
+ &obj_desc.ver_major,
+ &obj_desc.ver_minor);
+ if (error < 0)
+ goto error_cleanup_mc_io;
+
+ obj_desc.vendor = FSL_MC_VENDOR_FREESCALE;
+ strcpy(obj_desc.type, "dprc");
+ obj_desc.id = container_id;
+ obj_desc.irq_count = 1;
+ obj_desc.region_count = 0;
+
+ error = fsl_mc_device_add(&obj_desc, mc_io, &pdev->dev, &mc_bus_dev);
+ if (error < 0)
+ goto error_cleanup_mc_io;
+
+ mc->root_mc_bus_dev = mc_bus_dev;
+ return 0;
+
+error_cleanup_mc_io:
+ fsl_destroy_mc_io(mc_io);
+ return error;
+}
+
+/**
+ * fsl_mc_bus_remove - callback invoked when the root MC bus is being
+ * removed
+ */
+static int fsl_mc_bus_remove(struct platform_device *pdev)
+{
+ struct fsl_mc *mc = platform_get_drvdata(pdev);
+
+ if (!fsl_mc_is_root_dprc(&mc->root_mc_bus_dev->dev))
+ return -EINVAL;
+
+ fsl_mc_device_remove(mc->root_mc_bus_dev);
+
+ fsl_destroy_mc_io(mc->root_mc_bus_dev->mc_io);
+ mc->root_mc_bus_dev->mc_io = NULL;
+
+ return 0;
+}
+
+static const struct of_device_id fsl_mc_bus_match_table[] = {
+ {.compatible = "fsl,qoriq-mc",},
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, fsl_mc_bus_match_table);
+
+static struct platform_driver fsl_mc_bus_driver = {
+ .driver = {
+ .name = "fsl_mc_bus",
+ .pm = NULL,
+ .of_match_table = fsl_mc_bus_match_table,
+ },
+ .probe = fsl_mc_bus_probe,
+ .remove = fsl_mc_bus_remove,
+};
+
+static int __init fsl_mc_bus_driver_init(void)
+{
+ int error;
+
+ error = bus_register(&fsl_mc_bus_type);
+ if (error < 0) {
+ pr_err("bus type registration failed: %d\n", error);
+ goto error_cleanup_cache;
+ }
+
+ error = platform_driver_register(&fsl_mc_bus_driver);
+ if (error < 0) {
+ pr_err("platform_driver_register() failed: %d\n", error);
+ goto error_cleanup_bus;
+ }
+
+ error = dprc_driver_init();
+ if (error < 0)
+ goto error_cleanup_driver;
+
+ error = fsl_mc_allocator_driver_init();
+ if (error < 0)
+ goto error_cleanup_dprc_driver;
+
+ return 0;
+
+error_cleanup_dprc_driver:
+ dprc_driver_exit();
+
+error_cleanup_driver:
+ platform_driver_unregister(&fsl_mc_bus_driver);
+
+error_cleanup_bus:
+ bus_unregister(&fsl_mc_bus_type);
+
+error_cleanup_cache:
+ return error;
+}
+postcore_initcall(fsl_mc_bus_driver_init);
diff --git a/drivers/bus/fsl-mc/fsl-mc-msi.c b/drivers/bus/fsl-mc/fsl-mc-msi.c
new file mode 100644
index 000000000000..ec35e255b496
--- /dev/null
+++ b/drivers/bus/fsl-mc/fsl-mc-msi.c
@@ -0,0 +1,285 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Freescale Management Complex (MC) bus driver MSI support
+ *
+ * Copyright (C) 2015-2016 Freescale Semiconductor, Inc.
+ * Author: German Rivera <German.Rivera@freescale.com>
+ *
+ */
+
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/msi.h>
+
+#include "fsl-mc-private.h"
+
+#ifdef GENERIC_MSI_DOMAIN_OPS
+/*
+ * Generate a unique ID identifying the interrupt (only used within the MSI
+ * irqdomain. Combine the icid with the interrupt index.
+ */
+static irq_hw_number_t fsl_mc_domain_calc_hwirq(struct fsl_mc_device *dev,
+ struct msi_desc *desc)
+{
+ /*
+ * Make the base hwirq value for ICID*10000 so it is readable
+ * as a decimal value in /proc/interrupts.
+ */
+ return (irq_hw_number_t)(desc->fsl_mc.msi_index + (dev->icid * 10000));
+}
+
+static void fsl_mc_msi_set_desc(msi_alloc_info_t *arg,
+ struct msi_desc *desc)
+{
+ arg->desc = desc;
+ arg->hwirq = fsl_mc_domain_calc_hwirq(to_fsl_mc_device(desc->dev),
+ desc);
+}
+#else
+#define fsl_mc_msi_set_desc NULL
+#endif
+
+static void fsl_mc_msi_update_dom_ops(struct msi_domain_info *info)
+{
+ struct msi_domain_ops *ops = info->ops;
+
+ if (!ops)
+ return;
+
+ /*
+ * set_desc should not be set by the caller
+ */
+ if (!ops->set_desc)
+ ops->set_desc = fsl_mc_msi_set_desc;
+}
+
+static void __fsl_mc_msi_write_msg(struct fsl_mc_device *mc_bus_dev,
+ struct fsl_mc_device_irq *mc_dev_irq)
+{
+ int error;
+ struct fsl_mc_device *owner_mc_dev = mc_dev_irq->mc_dev;
+ struct msi_desc *msi_desc = mc_dev_irq->msi_desc;
+ struct dprc_irq_cfg irq_cfg;
+
+ /*
+ * msi_desc->msg.address is 0x0 when this function is invoked in
+ * the free_irq() code path. In this case, for the MC, we don't
+ * really need to "unprogram" the MSI, so we just return.
+ */
+ if (msi_desc->msg.address_lo == 0x0 && msi_desc->msg.address_hi == 0x0)
+ return;
+
+ if (!owner_mc_dev)
+ return;
+
+ irq_cfg.paddr = ((u64)msi_desc->msg.address_hi << 32) |
+ msi_desc->msg.address_lo;
+ irq_cfg.val = msi_desc->msg.data;
+ irq_cfg.irq_num = msi_desc->irq;
+
+ if (owner_mc_dev == mc_bus_dev) {
+ /*
+ * IRQ is for the mc_bus_dev's DPRC itself
+ */
+ error = dprc_set_irq(mc_bus_dev->mc_io,
+ MC_CMD_FLAG_INTR_DIS | MC_CMD_FLAG_PRI,
+ mc_bus_dev->mc_handle,
+ mc_dev_irq->dev_irq_index,
+ &irq_cfg);
+ if (error < 0) {
+ dev_err(&owner_mc_dev->dev,
+ "dprc_set_irq() failed: %d\n", error);
+ }
+ } else {
+ /*
+ * IRQ is for for a child device of mc_bus_dev
+ */
+ error = dprc_set_obj_irq(mc_bus_dev->mc_io,
+ MC_CMD_FLAG_INTR_DIS | MC_CMD_FLAG_PRI,
+ mc_bus_dev->mc_handle,
+ owner_mc_dev->obj_desc.type,
+ owner_mc_dev->obj_desc.id,
+ mc_dev_irq->dev_irq_index,
+ &irq_cfg);
+ if (error < 0) {
+ dev_err(&owner_mc_dev->dev,
+ "dprc_obj_set_irq() failed: %d\n", error);
+ }
+ }
+}
+
+/*
+ * NOTE: This function is invoked with interrupts disabled
+ */
+static void fsl_mc_msi_write_msg(struct irq_data *irq_data,
+ struct msi_msg *msg)
+{
+ struct msi_desc *msi_desc = irq_data_get_msi_desc(irq_data);
+ struct fsl_mc_device *mc_bus_dev = to_fsl_mc_device(msi_desc->dev);
+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
+ struct fsl_mc_device_irq *mc_dev_irq =
+ &mc_bus->irq_resources[msi_desc->fsl_mc.msi_index];
+
+ msi_desc->msg = *msg;
+
+ /*
+ * Program the MSI (paddr, value) pair in the device:
+ */
+ __fsl_mc_msi_write_msg(mc_bus_dev, mc_dev_irq);
+}
+
+static void fsl_mc_msi_update_chip_ops(struct msi_domain_info *info)
+{
+ struct irq_chip *chip = info->chip;
+
+ if (!chip)
+ return;
+
+ /*
+ * irq_write_msi_msg should not be set by the caller
+ */
+ if (!chip->irq_write_msi_msg)
+ chip->irq_write_msi_msg = fsl_mc_msi_write_msg;
+}
+
+/**
+ * fsl_mc_msi_create_irq_domain - Create a fsl-mc MSI interrupt domain
+ * @np: Optional device-tree node of the interrupt controller
+ * @info: MSI domain info
+ * @parent: Parent irq domain
+ *
+ * Updates the domain and chip ops and creates a fsl-mc MSI
+ * interrupt domain.
+ *
+ * Returns:
+ * A domain pointer or NULL in case of failure.
+ */
+struct irq_domain *fsl_mc_msi_create_irq_domain(struct fwnode_handle *fwnode,
+ struct msi_domain_info *info,
+ struct irq_domain *parent)
+{
+ struct irq_domain *domain;
+
+ if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS)
+ fsl_mc_msi_update_dom_ops(info);
+ if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
+ fsl_mc_msi_update_chip_ops(info);
+
+ domain = msi_create_irq_domain(fwnode, info, parent);
+ if (domain)
+ irq_domain_update_bus_token(domain, DOMAIN_BUS_FSL_MC_MSI);
+
+ return domain;
+}
+
+int fsl_mc_find_msi_domain(struct device *mc_platform_dev,
+ struct irq_domain **mc_msi_domain)
+{
+ struct irq_domain *msi_domain;
+ struct device_node *mc_of_node = mc_platform_dev->of_node;
+
+ msi_domain = of_msi_get_domain(mc_platform_dev, mc_of_node,
+ DOMAIN_BUS_FSL_MC_MSI);
+ if (!msi_domain) {
+ pr_err("Unable to find fsl-mc MSI domain for %pOF\n",
+ mc_of_node);
+
+ return -ENOENT;
+ }
+
+ *mc_msi_domain = msi_domain;
+ return 0;
+}
+
+static void fsl_mc_msi_free_descs(struct device *dev)
+{
+ struct msi_desc *desc, *tmp;
+
+ list_for_each_entry_safe(desc, tmp, dev_to_msi_list(dev), list) {
+ list_del(&desc->list);
+ free_msi_entry(desc);
+ }
+}
+
+static int fsl_mc_msi_alloc_descs(struct device *dev, unsigned int irq_count)
+
+{
+ unsigned int i;
+ int error;
+ struct msi_desc *msi_desc;
+
+ for (i = 0; i < irq_count; i++) {
+ msi_desc = alloc_msi_entry(dev, 1, NULL);
+ if (!msi_desc) {
+ dev_err(dev, "Failed to allocate msi entry\n");
+ error = -ENOMEM;
+ goto cleanup_msi_descs;
+ }
+
+ msi_desc->fsl_mc.msi_index = i;
+ INIT_LIST_HEAD(&msi_desc->list);
+ list_add_tail(&msi_desc->list, dev_to_msi_list(dev));
+ }
+
+ return 0;
+
+cleanup_msi_descs:
+ fsl_mc_msi_free_descs(dev);
+ return error;
+}
+
+int fsl_mc_msi_domain_alloc_irqs(struct device *dev,
+ unsigned int irq_count)
+{
+ struct irq_domain *msi_domain;
+ int error;
+
+ if (!list_empty(dev_to_msi_list(dev)))
+ return -EINVAL;
+
+ error = fsl_mc_msi_alloc_descs(dev, irq_count);
+ if (error < 0)
+ return error;
+
+ msi_domain = dev_get_msi_domain(dev);
+ if (!msi_domain) {
+ error = -EINVAL;
+ goto cleanup_msi_descs;
+ }
+
+ /*
+ * NOTE: Calling this function will trigger the invocation of the
+ * its_fsl_mc_msi_prepare() callback
+ */
+ error = msi_domain_alloc_irqs(msi_domain, dev, irq_count);
+
+ if (error) {
+ dev_err(dev, "Failed to allocate IRQs\n");
+ goto cleanup_msi_descs;
+ }
+
+ return 0;
+
+cleanup_msi_descs:
+ fsl_mc_msi_free_descs(dev);
+ return error;
+}
+
+void fsl_mc_msi_domain_free_irqs(struct device *dev)
+{
+ struct irq_domain *msi_domain;
+
+ msi_domain = dev_get_msi_domain(dev);
+ if (!msi_domain)
+ return;
+
+ msi_domain_free_irqs(msi_domain, dev);
+
+ if (list_empty(dev_to_msi_list(dev)))
+ return;
+
+ fsl_mc_msi_free_descs(dev);
+}
diff --git a/drivers/bus/fsl-mc/fsl-mc-private.h b/drivers/bus/fsl-mc/fsl-mc-private.h
new file mode 100644
index 000000000000..ea11b4fe59f7
--- /dev/null
+++ b/drivers/bus/fsl-mc/fsl-mc-private.h
@@ -0,0 +1,564 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Freescale Management Complex (MC) bus private declarations
+ *
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
+ *
+ */
+#ifndef _FSL_MC_PRIVATE_H_
+#define _FSL_MC_PRIVATE_H_
+
+#include <linux/fsl/mc.h>
+#include <linux/mutex.h>
+
+/*
+ * Data Path Management Complex (DPMNG) General API
+ */
+
+/* DPMNG command versioning */
+#define DPMNG_CMD_BASE_VERSION 1
+#define DPMNG_CMD_ID_OFFSET 4
+
+#define DPMNG_CMD(id) (((id) << DPMNG_CMD_ID_OFFSET) | DPMNG_CMD_BASE_VERSION)
+
+/* DPMNG command IDs */
+#define DPMNG_CMDID_GET_VERSION DPMNG_CMD(0x831)
+
+struct dpmng_rsp_get_version {
+ __le32 revision;
+ __le32 version_major;
+ __le32 version_minor;
+};
+
+/*
+ * Data Path Management Command Portal (DPMCP) API
+ */
+
+/* Minimal supported DPMCP Version */
+#define DPMCP_MIN_VER_MAJOR 3
+#define DPMCP_MIN_VER_MINOR 0
+
+/* DPMCP command versioning */
+#define DPMCP_CMD_BASE_VERSION 1
+#define DPMCP_CMD_ID_OFFSET 4
+
+#define DPMCP_CMD(id) (((id) << DPMCP_CMD_ID_OFFSET) | DPMCP_CMD_BASE_VERSION)
+
+/* DPMCP command IDs */
+#define DPMCP_CMDID_CLOSE DPMCP_CMD(0x800)
+#define DPMCP_CMDID_OPEN DPMCP_CMD(0x80b)
+#define DPMCP_CMDID_RESET DPMCP_CMD(0x005)
+
+struct dpmcp_cmd_open {
+ __le32 dpmcp_id;
+};
+
+/*
+ * Initialization and runtime control APIs for DPMCP
+ */
+int dpmcp_open(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ int dpmcp_id,
+ u16 *token);
+
+int dpmcp_close(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token);
+
+int dpmcp_reset(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token);
+
+/*
+ * Data Path Resource Container (DPRC) API
+ */
+
+/* Minimal supported DPRC Version */
+#define DPRC_MIN_VER_MAJOR 6
+#define DPRC_MIN_VER_MINOR 0
+
+/* DPRC command versioning */
+#define DPRC_CMD_BASE_VERSION 1
+#define DPRC_CMD_ID_OFFSET 4
+
+#define DPRC_CMD(id) (((id) << DPRC_CMD_ID_OFFSET) | DPRC_CMD_BASE_VERSION)
+
+/* DPRC command IDs */
+#define DPRC_CMDID_CLOSE DPRC_CMD(0x800)
+#define DPRC_CMDID_OPEN DPRC_CMD(0x805)
+#define DPRC_CMDID_GET_API_VERSION DPRC_CMD(0xa05)
+
+#define DPRC_CMDID_GET_ATTR DPRC_CMD(0x004)
+
+#define DPRC_CMDID_SET_IRQ DPRC_CMD(0x010)
+#define DPRC_CMDID_SET_IRQ_ENABLE DPRC_CMD(0x012)
+#define DPRC_CMDID_SET_IRQ_MASK DPRC_CMD(0x014)
+#define DPRC_CMDID_GET_IRQ_STATUS DPRC_CMD(0x016)
+#define DPRC_CMDID_CLEAR_IRQ_STATUS DPRC_CMD(0x017)
+
+#define DPRC_CMDID_GET_CONT_ID DPRC_CMD(0x830)
+#define DPRC_CMDID_GET_OBJ_COUNT DPRC_CMD(0x159)
+#define DPRC_CMDID_GET_OBJ DPRC_CMD(0x15A)
+#define DPRC_CMDID_GET_OBJ_REG DPRC_CMD(0x15E)
+#define DPRC_CMDID_SET_OBJ_IRQ DPRC_CMD(0x15F)
+
+struct dprc_cmd_open {
+ __le32 container_id;
+};
+
+struct dprc_cmd_set_irq {
+ /* cmd word 0 */
+ __le32 irq_val;
+ u8 irq_index;
+ u8 pad[3];
+ /* cmd word 1 */
+ __le64 irq_addr;
+ /* cmd word 2 */
+ __le32 irq_num;
+};
+
+#define DPRC_ENABLE 0x1
+
+struct dprc_cmd_set_irq_enable {
+ u8 enable;
+ u8 pad[3];
+ u8 irq_index;
+};
+
+struct dprc_cmd_set_irq_mask {
+ __le32 mask;
+ u8 irq_index;
+};
+
+struct dprc_cmd_get_irq_status {
+ __le32 status;
+ u8 irq_index;
+};
+
+struct dprc_rsp_get_irq_status {
+ __le32 status;
+};
+
+struct dprc_cmd_clear_irq_status {
+ __le32 status;
+ u8 irq_index;
+};
+
+struct dprc_rsp_get_attributes {
+ /* response word 0 */
+ __le32 container_id;
+ __le16 icid;
+ __le16 pad;
+ /* response word 1 */
+ __le32 options;
+ __le32 portal_id;
+};
+
+struct dprc_rsp_get_obj_count {
+ __le32 pad;
+ __le32 obj_count;
+};
+
+struct dprc_cmd_get_obj {
+ __le32 obj_index;
+};
+
+struct dprc_rsp_get_obj {
+ /* response word 0 */
+ __le32 pad0;
+ __le32 id;
+ /* response word 1 */
+ __le16 vendor;
+ u8 irq_count;
+ u8 region_count;
+ __le32 state;
+ /* response word 2 */
+ __le16 version_major;
+ __le16 version_minor;
+ __le16 flags;
+ __le16 pad1;
+ /* response word 3-4 */
+ u8 type[16];
+ /* response word 5-6 */
+ u8 label[16];
+};
+
+struct dprc_cmd_get_obj_region {
+ /* cmd word 0 */
+ __le32 obj_id;
+ __le16 pad0;
+ u8 region_index;
+ u8 pad1;
+ /* cmd word 1-2 */
+ __le64 pad2[2];
+ /* cmd word 3-4 */
+ u8 obj_type[16];
+};
+
+struct dprc_rsp_get_obj_region {
+ /* response word 0 */
+ __le64 pad;
+ /* response word 1 */
+ __le64 base_addr;
+ /* response word 2 */
+ __le32 size;
+};
+
+struct dprc_cmd_set_obj_irq {
+ /* cmd word 0 */
+ __le32 irq_val;
+ u8 irq_index;
+ u8 pad[3];
+ /* cmd word 1 */
+ __le64 irq_addr;
+ /* cmd word 2 */
+ __le32 irq_num;
+ __le32 obj_id;
+ /* cmd word 3-4 */
+ u8 obj_type[16];
+};
+
+/*
+ * DPRC API for managing and querying DPAA resources
+ */
+int dprc_open(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ int container_id,
+ u16 *token);
+
+int dprc_close(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token);
+
+/* DPRC IRQ events */
+
+/* IRQ event - Indicates that a new object added to the container */
+#define DPRC_IRQ_EVENT_OBJ_ADDED 0x00000001
+/* IRQ event - Indicates that an object was removed from the container */
+#define DPRC_IRQ_EVENT_OBJ_REMOVED 0x00000002
+/*
+ * IRQ event - Indicates that one of the descendant containers that opened by
+ * this container is destroyed
+ */
+#define DPRC_IRQ_EVENT_CONTAINER_DESTROYED 0x00000010
+
+/*
+ * IRQ event - Indicates that on one of the container's opened object is
+ * destroyed
+ */
+#define DPRC_IRQ_EVENT_OBJ_DESTROYED 0x00000020
+
+/* Irq event - Indicates that object is created at the container */
+#define DPRC_IRQ_EVENT_OBJ_CREATED 0x00000040
+
+/**
+ * struct dprc_irq_cfg - IRQ configuration
+ * @paddr: Address that must be written to signal a message-based interrupt
+ * @val: Value to write into irq_addr address
+ * @irq_num: A user defined number associated with this IRQ
+ */
+struct dprc_irq_cfg {
+ phys_addr_t paddr;
+ u32 val;
+ int irq_num;
+};
+
+int dprc_set_irq(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u8 irq_index,
+ struct dprc_irq_cfg *irq_cfg);
+
+int dprc_set_irq_enable(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u8 irq_index,
+ u8 en);
+
+int dprc_set_irq_mask(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u8 irq_index,
+ u32 mask);
+
+int dprc_get_irq_status(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u8 irq_index,
+ u32 *status);
+
+int dprc_clear_irq_status(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u8 irq_index,
+ u32 status);
+
+/**
+ * struct dprc_attributes - Container attributes
+ * @container_id: Container's ID
+ * @icid: Container's ICID
+ * @portal_id: Container's portal ID
+ * @options: Container's options as set at container's creation
+ */
+struct dprc_attributes {
+ int container_id;
+ u16 icid;
+ int portal_id;
+ u64 options;
+};
+
+int dprc_get_attributes(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ struct dprc_attributes *attributes);
+
+int dprc_get_obj_count(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ int *obj_count);
+
+int dprc_get_obj(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ int obj_index,
+ struct fsl_mc_obj_desc *obj_desc);
+
+int dprc_set_obj_irq(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ char *obj_type,
+ int obj_id,
+ u8 irq_index,
+ struct dprc_irq_cfg *irq_cfg);
+
+/* Region flags */
+/* Cacheable - Indicates that region should be mapped as cacheable */
+#define DPRC_REGION_CACHEABLE 0x00000001
+
+/**
+ * enum dprc_region_type - Region type
+ * @DPRC_REGION_TYPE_MC_PORTAL: MC portal region
+ * @DPRC_REGION_TYPE_QBMAN_PORTAL: Qbman portal region
+ */
+enum dprc_region_type {
+ DPRC_REGION_TYPE_MC_PORTAL,
+ DPRC_REGION_TYPE_QBMAN_PORTAL
+};
+
+/**
+ * struct dprc_region_desc - Mappable region descriptor
+ * @base_offset: Region offset from region's base address.
+ * For DPMCP and DPRC objects, region base is offset from SoC MC portals
+ * base address; For DPIO, region base is offset from SoC QMan portals
+ * base address
+ * @size: Region size (in bytes)
+ * @flags: Region attributes
+ * @type: Portal region type
+ */
+struct dprc_region_desc {
+ u32 base_offset;
+ u32 size;
+ u32 flags;
+ enum dprc_region_type type;
+};
+
+int dprc_get_obj_region(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ char *obj_type,
+ int obj_id,
+ u8 region_index,
+ struct dprc_region_desc *region_desc);
+
+int dprc_get_api_version(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 *major_ver,
+ u16 *minor_ver);
+
+int dprc_get_container_id(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ int *container_id);
+
+/*
+ * Data Path Buffer Pool (DPBP) API
+ */
+
+/* DPBP Version */
+#define DPBP_VER_MAJOR 3
+#define DPBP_VER_MINOR 2
+
+/* Command versioning */
+#define DPBP_CMD_BASE_VERSION 1
+#define DPBP_CMD_ID_OFFSET 4
+
+#define DPBP_CMD(id) (((id) << DPBP_CMD_ID_OFFSET) | DPBP_CMD_BASE_VERSION)
+
+/* Command IDs */
+#define DPBP_CMDID_CLOSE DPBP_CMD(0x800)
+#define DPBP_CMDID_OPEN DPBP_CMD(0x804)
+
+#define DPBP_CMDID_ENABLE DPBP_CMD(0x002)
+#define DPBP_CMDID_DISABLE DPBP_CMD(0x003)
+#define DPBP_CMDID_GET_ATTR DPBP_CMD(0x004)
+#define DPBP_CMDID_RESET DPBP_CMD(0x005)
+
+struct dpbp_cmd_open {
+ __le32 dpbp_id;
+};
+
+#define DPBP_ENABLE 0x1
+
+struct dpbp_rsp_get_attributes {
+ /* response word 0 */
+ __le16 pad;
+ __le16 bpid;
+ __le32 id;
+ /* response word 1 */
+ __le16 version_major;
+ __le16 version_minor;
+};
+
+/*
+ * Data Path Concentrator (DPCON) API
+ */
+
+/* DPCON Version */
+#define DPCON_VER_MAJOR 3
+#define DPCON_VER_MINOR 2
+
+/* Command versioning */
+#define DPCON_CMD_BASE_VERSION 1
+#define DPCON_CMD_ID_OFFSET 4
+
+#define DPCON_CMD(id) (((id) << DPCON_CMD_ID_OFFSET) | DPCON_CMD_BASE_VERSION)
+
+/* Command IDs */
+#define DPCON_CMDID_CLOSE DPCON_CMD(0x800)
+#define DPCON_CMDID_OPEN DPCON_CMD(0x808)
+
+#define DPCON_CMDID_ENABLE DPCON_CMD(0x002)
+#define DPCON_CMDID_DISABLE DPCON_CMD(0x003)
+#define DPCON_CMDID_GET_ATTR DPCON_CMD(0x004)
+#define DPCON_CMDID_RESET DPCON_CMD(0x005)
+
+#define DPCON_CMDID_SET_NOTIFICATION DPCON_CMD(0x100)
+
+struct dpcon_cmd_open {
+ __le32 dpcon_id;
+};
+
+#define DPCON_ENABLE 1
+
+struct dpcon_rsp_get_attr {
+ /* response word 0 */
+ __le32 id;
+ __le16 qbman_ch_id;
+ u8 num_priorities;
+ u8 pad;
+};
+
+struct dpcon_cmd_set_notification {
+ /* cmd word 0 */
+ __le32 dpio_id;
+ u8 priority;
+ u8 pad[3];
+ /* cmd word 1 */
+ __le64 user_ctx;
+};
+
+/**
+ * Maximum number of total IRQs that can be pre-allocated for an MC bus'
+ * IRQ pool
+ */
+#define FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS 256
+
+/**
+ * struct fsl_mc_resource_pool - Pool of MC resources of a given
+ * type
+ * @type: type of resources in the pool
+ * @max_count: maximum number of resources in the pool
+ * @free_count: number of free resources in the pool
+ * @mutex: mutex to serialize access to the pool's free list
+ * @free_list: anchor node of list of free resources in the pool
+ * @mc_bus: pointer to the MC bus that owns this resource pool
+ */
+struct fsl_mc_resource_pool {
+ enum fsl_mc_pool_type type;
+ int max_count;
+ int free_count;
+ struct mutex mutex; /* serializes access to free_list */
+ struct list_head free_list;
+ struct fsl_mc_bus *mc_bus;
+};
+
+/**
+ * struct fsl_mc_bus - logical bus that corresponds to a physical DPRC
+ * @mc_dev: fsl-mc device for the bus device itself.
+ * @resource_pools: array of resource pools (one pool per resource type)
+ * for this MC bus. These resources represent allocatable entities
+ * from the physical DPRC.
+ * @irq_resources: Pointer to array of IRQ objects for the IRQ pool
+ * @scan_mutex: Serializes bus scanning
+ * @dprc_attr: DPRC attributes
+ */
+struct fsl_mc_bus {
+ struct fsl_mc_device mc_dev;
+ struct fsl_mc_resource_pool resource_pools[FSL_MC_NUM_POOL_TYPES];
+ struct fsl_mc_device_irq *irq_resources;
+ struct mutex scan_mutex; /* serializes bus scanning */
+ struct dprc_attributes dprc_attr;
+};
+
+#define to_fsl_mc_bus(_mc_dev) \
+ container_of(_mc_dev, struct fsl_mc_bus, mc_dev)
+
+int __must_check fsl_mc_device_add(struct fsl_mc_obj_desc *obj_desc,
+ struct fsl_mc_io *mc_io,
+ struct device *parent_dev,
+ struct fsl_mc_device **new_mc_dev);
+
+void fsl_mc_device_remove(struct fsl_mc_device *mc_dev);
+
+int __init dprc_driver_init(void);
+
+void dprc_driver_exit(void);
+
+int __init fsl_mc_allocator_driver_init(void);
+
+void fsl_mc_allocator_driver_exit(void);
+
+void fsl_mc_init_all_resource_pools(struct fsl_mc_device *mc_bus_dev);
+
+void fsl_mc_cleanup_all_resource_pools(struct fsl_mc_device *mc_bus_dev);
+
+int __must_check fsl_mc_resource_allocate(struct fsl_mc_bus *mc_bus,
+ enum fsl_mc_pool_type pool_type,
+ struct fsl_mc_resource
+ **new_resource);
+
+void fsl_mc_resource_free(struct fsl_mc_resource *resource);
+
+int fsl_mc_msi_domain_alloc_irqs(struct device *dev,
+ unsigned int irq_count);
+
+void fsl_mc_msi_domain_free_irqs(struct device *dev);
+
+int fsl_mc_find_msi_domain(struct device *mc_platform_dev,
+ struct irq_domain **mc_msi_domain);
+
+int fsl_mc_populate_irq_pool(struct fsl_mc_bus *mc_bus,
+ unsigned int irq_count);
+
+void fsl_mc_cleanup_irq_pool(struct fsl_mc_bus *mc_bus);
+
+int __must_check fsl_create_mc_io(struct device *dev,
+ phys_addr_t mc_portal_phys_addr,
+ u32 mc_portal_size,
+ struct fsl_mc_device *dpmcp_dev,
+ u32 flags, struct fsl_mc_io **new_mc_io);
+
+void fsl_destroy_mc_io(struct fsl_mc_io *mc_io);
+
+bool fsl_mc_is_root_dprc(struct device *dev);
+
+#endif /* _FSL_MC_PRIVATE_H_ */
diff --git a/drivers/bus/fsl-mc/mc-io.c b/drivers/bus/fsl-mc/mc-io.c
new file mode 100644
index 000000000000..7226cfc49b6f
--- /dev/null
+++ b/drivers/bus/fsl-mc/mc-io.c
@@ -0,0 +1,268 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/*
+ * Copyright 2013-2016 Freescale Semiconductor Inc.
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/fsl/mc.h>
+
+#include "fsl-mc-private.h"
+
+static int fsl_mc_io_set_dpmcp(struct fsl_mc_io *mc_io,
+ struct fsl_mc_device *dpmcp_dev)
+{
+ int error;
+
+ if (mc_io->dpmcp_dev)
+ return -EINVAL;
+
+ if (dpmcp_dev->mc_io)
+ return -EINVAL;
+
+ error = dpmcp_open(mc_io,
+ 0,
+ dpmcp_dev->obj_desc.id,
+ &dpmcp_dev->mc_handle);
+ if (error < 0)
+ return error;
+
+ mc_io->dpmcp_dev = dpmcp_dev;
+ dpmcp_dev->mc_io = mc_io;
+ return 0;
+}
+
+static void fsl_mc_io_unset_dpmcp(struct fsl_mc_io *mc_io)
+{
+ int error;
+ struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev;
+
+ error = dpmcp_close(mc_io,
+ 0,
+ dpmcp_dev->mc_handle);
+ if (error < 0) {
+ dev_err(&dpmcp_dev->dev, "dpmcp_close() failed: %d\n",
+ error);
+ }
+
+ mc_io->dpmcp_dev = NULL;
+ dpmcp_dev->mc_io = NULL;
+}
+
+/**
+ * Creates an MC I/O object
+ *
+ * @dev: device to be associated with the MC I/O object
+ * @mc_portal_phys_addr: physical address of the MC portal to use
+ * @mc_portal_size: size in bytes of the MC portal
+ * @dpmcp-dev: Pointer to the DPMCP object associated with this MC I/O
+ * object or NULL if none.
+ * @flags: flags for the new MC I/O object
+ * @new_mc_io: Area to return pointer to newly created MC I/O object
+ *
+ * Returns '0' on Success; Error code otherwise.
+ */
+int __must_check fsl_create_mc_io(struct device *dev,
+ phys_addr_t mc_portal_phys_addr,
+ u32 mc_portal_size,
+ struct fsl_mc_device *dpmcp_dev,
+ u32 flags, struct fsl_mc_io **new_mc_io)
+{
+ int error;
+ struct fsl_mc_io *mc_io;
+ void __iomem *mc_portal_virt_addr;
+ struct resource *res;
+
+ mc_io = devm_kzalloc(dev, sizeof(*mc_io), GFP_KERNEL);
+ if (!mc_io)
+ return -ENOMEM;
+
+ mc_io->dev = dev;
+ mc_io->flags = flags;
+ mc_io->portal_phys_addr = mc_portal_phys_addr;
+ mc_io->portal_size = mc_portal_size;
+ if (flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)
+ spin_lock_init(&mc_io->spinlock);
+ else
+ mutex_init(&mc_io->mutex);
+
+ res = devm_request_mem_region(dev,
+ mc_portal_phys_addr,
+ mc_portal_size,
+ "mc_portal");
+ if (!res) {
+ dev_err(dev,
+ "devm_request_mem_region failed for MC portal %pa\n",
+ &mc_portal_phys_addr);
+ return -EBUSY;
+ }
+
+ mc_portal_virt_addr = devm_ioremap_nocache(dev,
+ mc_portal_phys_addr,
+ mc_portal_size);
+ if (!mc_portal_virt_addr) {
+ dev_err(dev,
+ "devm_ioremap_nocache failed for MC portal %pa\n",
+ &mc_portal_phys_addr);
+ return -ENXIO;
+ }
+
+ mc_io->portal_virt_addr = mc_portal_virt_addr;
+ if (dpmcp_dev) {
+ error = fsl_mc_io_set_dpmcp(mc_io, dpmcp_dev);
+ if (error < 0)
+ goto error_destroy_mc_io;
+ }
+
+ *new_mc_io = mc_io;
+ return 0;
+
+error_destroy_mc_io:
+ fsl_destroy_mc_io(mc_io);
+ return error;
+}
+
+/**
+ * Destroys an MC I/O object
+ *
+ * @mc_io: MC I/O object to destroy
+ */
+void fsl_destroy_mc_io(struct fsl_mc_io *mc_io)
+{
+ struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev;
+
+ if (dpmcp_dev)
+ fsl_mc_io_unset_dpmcp(mc_io);
+
+ devm_iounmap(mc_io->dev, mc_io->portal_virt_addr);
+ devm_release_mem_region(mc_io->dev,
+ mc_io->portal_phys_addr,
+ mc_io->portal_size);
+
+ mc_io->portal_virt_addr = NULL;
+ devm_kfree(mc_io->dev, mc_io);
+}
+
+/**
+ * fsl_mc_portal_allocate - Allocates an MC portal
+ *
+ * @mc_dev: MC device for which the MC portal is to be allocated
+ * @mc_io_flags: Flags for the fsl_mc_io object that wraps the allocated
+ * MC portal.
+ * @new_mc_io: Pointer to area where the pointer to the fsl_mc_io object
+ * that wraps the allocated MC portal is to be returned
+ *
+ * This function allocates an MC portal from the device's parent DPRC,
+ * from the corresponding MC bus' pool of MC portals and wraps
+ * it in a new fsl_mc_io object. If 'mc_dev' is a DPRC itself, the
+ * portal is allocated from its own MC bus.
+ */
+int __must_check fsl_mc_portal_allocate(struct fsl_mc_device *mc_dev,
+ u16 mc_io_flags,
+ struct fsl_mc_io **new_mc_io)
+{
+ struct fsl_mc_device *mc_bus_dev;
+ struct fsl_mc_bus *mc_bus;
+ phys_addr_t mc_portal_phys_addr;
+ size_t mc_portal_size;
+ struct fsl_mc_device *dpmcp_dev;
+ int error = -EINVAL;
+ struct fsl_mc_resource *resource = NULL;
+ struct fsl_mc_io *mc_io = NULL;
+
+ if (mc_dev->flags & FSL_MC_IS_DPRC) {
+ mc_bus_dev = mc_dev;
+ } else {
+ if (!dev_is_fsl_mc(mc_dev->dev.parent))
+ return error;
+
+ mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
+ }
+
+ mc_bus = to_fsl_mc_bus(mc_bus_dev);
+ *new_mc_io = NULL;
+ error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_DPMCP, &resource);
+ if (error < 0)
+ return error;
+
+ error = -EINVAL;
+ dpmcp_dev = resource->data;
+
+ if (dpmcp_dev->obj_desc.ver_major < DPMCP_MIN_VER_MAJOR ||
+ (dpmcp_dev->obj_desc.ver_major == DPMCP_MIN_VER_MAJOR &&
+ dpmcp_dev->obj_desc.ver_minor < DPMCP_MIN_VER_MINOR)) {
+ dev_err(&dpmcp_dev->dev,
+ "ERROR: Version %d.%d of DPMCP not supported.\n",
+ dpmcp_dev->obj_desc.ver_major,
+ dpmcp_dev->obj_desc.ver_minor);
+ error = -ENOTSUPP;
+ goto error_cleanup_resource;
+ }
+
+ mc_portal_phys_addr = dpmcp_dev->regions[0].start;
+ mc_portal_size = resource_size(dpmcp_dev->regions);
+
+ error = fsl_create_mc_io(&mc_bus_dev->dev,
+ mc_portal_phys_addr,
+ mc_portal_size, dpmcp_dev,
+ mc_io_flags, &mc_io);
+ if (error < 0)
+ goto error_cleanup_resource;
+
+ *new_mc_io = mc_io;
+ return 0;
+
+error_cleanup_resource:
+ fsl_mc_resource_free(resource);
+ return error;
+}
+EXPORT_SYMBOL_GPL(fsl_mc_portal_allocate);
+
+/**
+ * fsl_mc_portal_free - Returns an MC portal to the pool of free MC portals
+ * of a given MC bus
+ *
+ * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free
+ */
+void fsl_mc_portal_free(struct fsl_mc_io *mc_io)
+{
+ struct fsl_mc_device *dpmcp_dev;
+ struct fsl_mc_resource *resource;
+
+ /*
+ * Every mc_io obtained by calling fsl_mc_portal_allocate() is supposed
+ * to have a DPMCP object associated with.
+ */
+ dpmcp_dev = mc_io->dpmcp_dev;
+
+ resource = dpmcp_dev->resource;
+ if (!resource || resource->type != FSL_MC_POOL_DPMCP)
+ return;
+
+ if (resource->data != dpmcp_dev)
+ return;
+
+ fsl_destroy_mc_io(mc_io);
+ fsl_mc_resource_free(resource);
+}
+EXPORT_SYMBOL_GPL(fsl_mc_portal_free);
+
+/**
+ * fsl_mc_portal_reset - Resets the dpmcp object for a given fsl_mc_io object
+ *
+ * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free
+ */
+int fsl_mc_portal_reset(struct fsl_mc_io *mc_io)
+{
+ int error;
+ struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev;
+
+ error = dpmcp_reset(mc_io, 0, dpmcp_dev->mc_handle);
+ if (error < 0) {
+ dev_err(&dpmcp_dev->dev, "dpmcp_reset() failed: %d\n", error);
+ return error;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fsl_mc_portal_reset);
diff --git a/drivers/bus/fsl-mc/mc-sys.c b/drivers/bus/fsl-mc/mc-sys.c
new file mode 100644
index 000000000000..3221a7fbaf0a
--- /dev/null
+++ b/drivers/bus/fsl-mc/mc-sys.c
@@ -0,0 +1,296 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/*
+ * Copyright 2013-2016 Freescale Semiconductor Inc.
+ *
+ * I/O services to send MC commands to the MC hardware
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/io-64-nonatomic-hi-lo.h>
+#include <linux/fsl/mc.h>
+
+#include "fsl-mc-private.h"
+
+/**
+ * Timeout in milliseconds to wait for the completion of an MC command
+ */
+#define MC_CMD_COMPLETION_TIMEOUT_MS 500
+
+/*
+ * usleep_range() min and max values used to throttle down polling
+ * iterations while waiting for MC command completion
+ */
+#define MC_CMD_COMPLETION_POLLING_MIN_SLEEP_USECS 10
+#define MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS 500
+
+static enum mc_cmd_status mc_cmd_hdr_read_status(struct fsl_mc_command *cmd)
+{
+ struct mc_cmd_header *hdr = (struct mc_cmd_header *)&cmd->header;
+
+ return (enum mc_cmd_status)hdr->status;
+}
+
+static u16 mc_cmd_hdr_read_cmdid(struct fsl_mc_command *cmd)
+{
+ struct mc_cmd_header *hdr = (struct mc_cmd_header *)&cmd->header;
+ u16 cmd_id = le16_to_cpu(hdr->cmd_id);
+
+ return cmd_id;
+}
+
+static int mc_status_to_error(enum mc_cmd_status status)
+{
+ static const int mc_status_to_error_map[] = {
+ [MC_CMD_STATUS_OK] = 0,
+ [MC_CMD_STATUS_AUTH_ERR] = -EACCES,
+ [MC_CMD_STATUS_NO_PRIVILEGE] = -EPERM,
+ [MC_CMD_STATUS_DMA_ERR] = -EIO,
+ [MC_CMD_STATUS_CONFIG_ERR] = -ENXIO,
+ [MC_CMD_STATUS_TIMEOUT] = -ETIMEDOUT,
+ [MC_CMD_STATUS_NO_RESOURCE] = -ENAVAIL,
+ [MC_CMD_STATUS_NO_MEMORY] = -ENOMEM,
+ [MC_CMD_STATUS_BUSY] = -EBUSY,
+ [MC_CMD_STATUS_UNSUPPORTED_OP] = -ENOTSUPP,
+ [MC_CMD_STATUS_INVALID_STATE] = -ENODEV,
+ };
+
+ if ((u32)status >= ARRAY_SIZE(mc_status_to_error_map))
+ return -EINVAL;
+
+ return mc_status_to_error_map[status];
+}
+
+static const char *mc_status_to_string(enum mc_cmd_status status)
+{
+ static const char *const status_strings[] = {
+ [MC_CMD_STATUS_OK] = "Command completed successfully",
+ [MC_CMD_STATUS_READY] = "Command ready to be processed",
+ [MC_CMD_STATUS_AUTH_ERR] = "Authentication error",
+ [MC_CMD_STATUS_NO_PRIVILEGE] = "No privilege",
+ [MC_CMD_STATUS_DMA_ERR] = "DMA or I/O error",
+ [MC_CMD_STATUS_CONFIG_ERR] = "Configuration error",
+ [MC_CMD_STATUS_TIMEOUT] = "Operation timed out",
+ [MC_CMD_STATUS_NO_RESOURCE] = "No resources",
+ [MC_CMD_STATUS_NO_MEMORY] = "No memory available",
+ [MC_CMD_STATUS_BUSY] = "Device is busy",
+ [MC_CMD_STATUS_UNSUPPORTED_OP] = "Unsupported operation",
+ [MC_CMD_STATUS_INVALID_STATE] = "Invalid state"
+ };
+
+ if ((unsigned int)status >= ARRAY_SIZE(status_strings))
+ return "Unknown MC error";
+
+ return status_strings[status];
+}
+
+/**
+ * mc_write_command - writes a command to a Management Complex (MC) portal
+ *
+ * @portal: pointer to an MC portal
+ * @cmd: pointer to a filled command
+ */
+static inline void mc_write_command(struct fsl_mc_command __iomem *portal,
+ struct fsl_mc_command *cmd)
+{
+ int i;
+
+ /* copy command parameters into the portal */
+ for (i = 0; i < MC_CMD_NUM_OF_PARAMS; i++)
+ /*
+ * Data is already in the expected LE byte-order. Do an
+ * extra LE -> CPU conversion so that the CPU -> LE done in
+ * the device io write api puts it back in the right order.
+ */
+ writeq_relaxed(le64_to_cpu(cmd->params[i]), &portal->params[i]);
+
+ /* submit the command by writing the header */
+ writeq(le64_to_cpu(cmd->header), &portal->header);
+}
+
+/**
+ * mc_read_response - reads the response for the last MC command from a
+ * Management Complex (MC) portal
+ *
+ * @portal: pointer to an MC portal
+ * @resp: pointer to command response buffer
+ *
+ * Returns MC_CMD_STATUS_OK on Success; Error code otherwise.
+ */
+static inline enum mc_cmd_status mc_read_response(struct fsl_mc_command __iomem
+ *portal,
+ struct fsl_mc_command *resp)
+{
+ int i;
+ enum mc_cmd_status status;
+
+ /* Copy command response header from MC portal: */
+ resp->header = cpu_to_le64(readq_relaxed(&portal->header));
+ status = mc_cmd_hdr_read_status(resp);
+ if (status != MC_CMD_STATUS_OK)
+ return status;
+
+ /* Copy command response data from MC portal: */
+ for (i = 0; i < MC_CMD_NUM_OF_PARAMS; i++)
+ /*
+ * Data is expected to be in LE byte-order. Do an
+ * extra CPU -> LE to revert the LE -> CPU done in
+ * the device io read api.
+ */
+ resp->params[i] =
+ cpu_to_le64(readq_relaxed(&portal->params[i]));
+
+ return status;
+}
+
+/**
+ * Waits for the completion of an MC command doing preemptible polling.
+ * uslepp_range() is called between polling iterations.
+ *
+ * @mc_io: MC I/O object to be used
+ * @cmd: command buffer to receive MC response
+ * @mc_status: MC command completion status
+ */
+static int mc_polling_wait_preemptible(struct fsl_mc_io *mc_io,
+ struct fsl_mc_command *cmd,
+ enum mc_cmd_status *mc_status)
+{
+ enum mc_cmd_status status;
+ unsigned long jiffies_until_timeout =
+ jiffies + msecs_to_jiffies(MC_CMD_COMPLETION_TIMEOUT_MS);
+
+ /*
+ * Wait for response from the MC hardware:
+ */
+ for (;;) {
+ status = mc_read_response(mc_io->portal_virt_addr, cmd);
+ if (status != MC_CMD_STATUS_READY)
+ break;
+
+ /*
+ * TODO: When MC command completion interrupts are supported
+ * call wait function here instead of usleep_range()
+ */
+ usleep_range(MC_CMD_COMPLETION_POLLING_MIN_SLEEP_USECS,
+ MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS);
+
+ if (time_after_eq(jiffies, jiffies_until_timeout)) {
+ dev_dbg(mc_io->dev,
+ "MC command timed out (portal: %pa, dprc handle: %#x, command: %#x)\n",
+ &mc_io->portal_phys_addr,
+ (unsigned int)mc_cmd_hdr_read_token(cmd),
+ (unsigned int)mc_cmd_hdr_read_cmdid(cmd));
+
+ return -ETIMEDOUT;
+ }
+ }
+
+ *mc_status = status;
+ return 0;
+}
+
+/**
+ * Waits for the completion of an MC command doing atomic polling.
+ * udelay() is called between polling iterations.
+ *
+ * @mc_io: MC I/O object to be used
+ * @cmd: command buffer to receive MC response
+ * @mc_status: MC command completion status
+ */
+static int mc_polling_wait_atomic(struct fsl_mc_io *mc_io,
+ struct fsl_mc_command *cmd,
+ enum mc_cmd_status *mc_status)
+{
+ enum mc_cmd_status status;
+ unsigned long timeout_usecs = MC_CMD_COMPLETION_TIMEOUT_MS * 1000;
+
+ BUILD_BUG_ON((MC_CMD_COMPLETION_TIMEOUT_MS * 1000) %
+ MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS != 0);
+
+ for (;;) {
+ status = mc_read_response(mc_io->portal_virt_addr, cmd);
+ if (status != MC_CMD_STATUS_READY)
+ break;
+
+ udelay(MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS);
+ timeout_usecs -= MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS;
+ if (timeout_usecs == 0) {
+ dev_dbg(mc_io->dev,
+ "MC command timed out (portal: %pa, dprc handle: %#x, command: %#x)\n",
+ &mc_io->portal_phys_addr,
+ (unsigned int)mc_cmd_hdr_read_token(cmd),
+ (unsigned int)mc_cmd_hdr_read_cmdid(cmd));
+
+ return -ETIMEDOUT;
+ }
+ }
+
+ *mc_status = status;
+ return 0;
+}
+
+/**
+ * Sends a command to the MC device using the given MC I/O object
+ *
+ * @mc_io: MC I/O object to be used
+ * @cmd: command to be sent
+ *
+ * Returns '0' on Success; Error code otherwise.
+ */
+int mc_send_command(struct fsl_mc_io *mc_io, struct fsl_mc_command *cmd)
+{
+ int error;
+ enum mc_cmd_status status;
+ unsigned long irq_flags = 0;
+
+ if (in_irq() && !(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL))
+ return -EINVAL;
+
+ if (mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)
+ spin_lock_irqsave(&mc_io->spinlock, irq_flags);
+ else
+ mutex_lock(&mc_io->mutex);
+
+ /*
+ * Send command to the MC hardware:
+ */
+ mc_write_command(mc_io->portal_virt_addr, cmd);
+
+ /*
+ * Wait for response from the MC hardware:
+ */
+ if (!(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL))
+ error = mc_polling_wait_preemptible(mc_io, cmd, &status);
+ else
+ error = mc_polling_wait_atomic(mc_io, cmd, &status);
+
+ if (error < 0)
+ goto common_exit;
+
+ if (status != MC_CMD_STATUS_OK) {
+ dev_dbg(mc_io->dev,
+ "MC command failed: portal: %pa, dprc handle: %#x, command: %#x, status: %s (%#x)\n",
+ &mc_io->portal_phys_addr,
+ (unsigned int)mc_cmd_hdr_read_token(cmd),
+ (unsigned int)mc_cmd_hdr_read_cmdid(cmd),
+ mc_status_to_string(status),
+ (unsigned int)status);
+
+ error = mc_status_to_error(status);
+ goto common_exit;
+ }
+
+ error = 0;
+common_exit:
+ if (mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)
+ spin_unlock_irqrestore(&mc_io->spinlock, irq_flags);
+ else
+ mutex_unlock(&mc_io->mutex);
+
+ return error;
+}
+EXPORT_SYMBOL_GPL(mc_send_command);
diff --git a/drivers/bus/hisi_lpc.c b/drivers/bus/hisi_lpc.c
new file mode 100644
index 000000000000..2d4611e4c339
--- /dev/null
+++ b/drivers/bus/hisi_lpc.c
@@ -0,0 +1,615 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Hisilicon Limited, All Rights Reserved.
+ * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
+ * Author: Zou Rongrong <zourongrong@huawei.com>
+ * Author: John Garry <john.garry@huawei.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/logic_pio.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#define DRV_NAME "hisi-lpc"
+
+/*
+ * Setting this bit means each IO operation will target a different port
+ * address; 0 means repeated IO operations will use the same port,
+ * such as BT.
+ */
+#define FG_INCRADDR_LPC 0x02
+
+struct lpc_cycle_para {
+ unsigned int opflags;
+ unsigned int csize; /* data length of each operation */
+};
+
+struct hisi_lpc_dev {
+ spinlock_t cycle_lock;
+ void __iomem *membase;
+ struct logic_pio_hwaddr *io_host;
+};
+
+/* The max IO cycle counts supported is four per operation at maximum */
+#define LPC_MAX_DWIDTH 4
+
+#define LPC_REG_STARTUP_SIGNAL 0x00
+#define LPC_REG_STARTUP_SIGNAL_START BIT(0)
+#define LPC_REG_OP_STATUS 0x04
+#define LPC_REG_OP_STATUS_IDLE BIT(0)
+#define LPC_REG_OP_STATUS_FINISHED BIT(1)
+#define LPC_REG_OP_LEN 0x10 /* LPC cycles count per start */
+#define LPC_REG_CMD 0x14
+#define LPC_REG_CMD_OP BIT(0) /* 0: read, 1: write */
+#define LPC_REG_CMD_SAMEADDR BIT(3)
+#define LPC_REG_ADDR 0x20 /* target address */
+#define LPC_REG_WDATA 0x24 /* write FIFO */
+#define LPC_REG_RDATA 0x28 /* read FIFO */
+
+/* The minimal nanosecond interval for each query on LPC cycle status */
+#define LPC_NSEC_PERWAIT 100
+
+/*
+ * The maximum waiting time is about 128us. It is specific for stream I/O,
+ * such as ins.
+ *
+ * The fastest IO cycle time is about 390ns, but the worst case will wait
+ * for extra 256 lpc clocks, so (256 + 13) * 30ns = 8 us. The maximum burst
+ * cycles is 16. So, the maximum waiting time is about 128us under worst
+ * case.
+ *
+ * Choose 1300 as the maximum.
+ */
+#define LPC_MAX_WAITCNT 1300
+
+/* About 10us. This is specific for single IO operations, such as inb */
+#define LPC_PEROP_WAITCNT 100
+
+static int wait_lpc_idle(unsigned char *mbase, unsigned int waitcnt)
+{
+ u32 status;
+
+ do {
+ status = readl(mbase + LPC_REG_OP_STATUS);
+ if (status & LPC_REG_OP_STATUS_IDLE)
+ return (status & LPC_REG_OP_STATUS_FINISHED) ? 0 : -EIO;
+ ndelay(LPC_NSEC_PERWAIT);
+ } while (--waitcnt);
+
+ return -ETIME;
+}
+
+/*
+ * hisi_lpc_target_in - trigger a series of LPC cycles for read operation
+ * @lpcdev: pointer to hisi lpc device
+ * @para: some parameters used to control the lpc I/O operations
+ * @addr: the lpc I/O target port address
+ * @buf: where the read back data is stored
+ * @opcnt: how many I/O operations required, i.e. data width
+ *
+ * Returns 0 on success, non-zero on fail.
+ */
+static int hisi_lpc_target_in(struct hisi_lpc_dev *lpcdev,
+ struct lpc_cycle_para *para, unsigned long addr,
+ unsigned char *buf, unsigned long opcnt)
+{
+ unsigned int cmd_word;
+ unsigned int waitcnt;
+ unsigned long flags;
+ int ret;
+
+ if (!buf || !opcnt || !para || !para->csize || !lpcdev)
+ return -EINVAL;
+
+ cmd_word = 0; /* IO mode, Read */
+ waitcnt = LPC_PEROP_WAITCNT;
+ if (!(para->opflags & FG_INCRADDR_LPC)) {
+ cmd_word |= LPC_REG_CMD_SAMEADDR;
+ waitcnt = LPC_MAX_WAITCNT;
+ }
+
+ /* whole operation must be atomic */
+ spin_lock_irqsave(&lpcdev->cycle_lock, flags);
+
+ writel_relaxed(opcnt, lpcdev->membase + LPC_REG_OP_LEN);
+ writel_relaxed(cmd_word, lpcdev->membase + LPC_REG_CMD);
+ writel_relaxed(addr, lpcdev->membase + LPC_REG_ADDR);
+
+ writel(LPC_REG_STARTUP_SIGNAL_START,
+ lpcdev->membase + LPC_REG_STARTUP_SIGNAL);
+
+ /* whether the operation is finished */
+ ret = wait_lpc_idle(lpcdev->membase, waitcnt);
+ if (ret) {
+ spin_unlock_irqrestore(&lpcdev->cycle_lock, flags);
+ return ret;
+ }
+
+ readsb(lpcdev->membase + LPC_REG_RDATA, buf, opcnt);
+
+ spin_unlock_irqrestore(&lpcdev->cycle_lock, flags);
+
+ return 0;
+}
+
+/*
+ * hisi_lpc_target_out - trigger a series of LPC cycles for write operation
+ * @lpcdev: pointer to hisi lpc device
+ * @para: some parameters used to control the lpc I/O operations
+ * @addr: the lpc I/O target port address
+ * @buf: where the data to be written is stored
+ * @opcnt: how many I/O operations required, i.e. data width
+ *
+ * Returns 0 on success, non-zero on fail.
+ */
+static int hisi_lpc_target_out(struct hisi_lpc_dev *lpcdev,
+ struct lpc_cycle_para *para, unsigned long addr,
+ const unsigned char *buf, unsigned long opcnt)
+{
+ unsigned int waitcnt;
+ unsigned long flags;
+ u32 cmd_word;
+ int ret;
+
+ if (!buf || !opcnt || !para || !lpcdev)
+ return -EINVAL;
+
+ /* default is increasing address */
+ cmd_word = LPC_REG_CMD_OP; /* IO mode, write */
+ waitcnt = LPC_PEROP_WAITCNT;
+ if (!(para->opflags & FG_INCRADDR_LPC)) {
+ cmd_word |= LPC_REG_CMD_SAMEADDR;
+ waitcnt = LPC_MAX_WAITCNT;
+ }
+
+ spin_lock_irqsave(&lpcdev->cycle_lock, flags);
+
+ writel_relaxed(opcnt, lpcdev->membase + LPC_REG_OP_LEN);
+ writel_relaxed(cmd_word, lpcdev->membase + LPC_REG_CMD);
+ writel_relaxed(addr, lpcdev->membase + LPC_REG_ADDR);
+
+ writesb(lpcdev->membase + LPC_REG_WDATA, buf, opcnt);
+
+ writel(LPC_REG_STARTUP_SIGNAL_START,
+ lpcdev->membase + LPC_REG_STARTUP_SIGNAL);
+
+ /* whether the operation is finished */
+ ret = wait_lpc_idle(lpcdev->membase, waitcnt);
+
+ spin_unlock_irqrestore(&lpcdev->cycle_lock, flags);
+
+ return ret;
+}
+
+static unsigned long hisi_lpc_pio_to_addr(struct hisi_lpc_dev *lpcdev,
+ unsigned long pio)
+{
+ return pio - lpcdev->io_host->io_start + lpcdev->io_host->hw_start;
+}
+
+/*
+ * hisi_lpc_comm_in - input the data in a single operation
+ * @hostdata: pointer to the device information relevant to LPC controller
+ * @pio: the target I/O port address
+ * @dwidth: the data length required to read from the target I/O port
+ *
+ * When success, data is returned. Otherwise, ~0 is returned.
+ */
+static u32 hisi_lpc_comm_in(void *hostdata, unsigned long pio, size_t dwidth)
+{
+ struct hisi_lpc_dev *lpcdev = hostdata;
+ struct lpc_cycle_para iopara;
+ unsigned long addr;
+ u32 rd_data = 0;
+ int ret;
+
+ if (!lpcdev || !dwidth || dwidth > LPC_MAX_DWIDTH)
+ return ~0;
+
+ addr = hisi_lpc_pio_to_addr(lpcdev, pio);
+
+ iopara.opflags = FG_INCRADDR_LPC;
+ iopara.csize = dwidth;
+
+ ret = hisi_lpc_target_in(lpcdev, &iopara, addr,
+ (unsigned char *)&rd_data, dwidth);
+ if (ret)
+ return ~0;
+
+ return le32_to_cpu(rd_data);
+}
+
+/*
+ * hisi_lpc_comm_out - output the data in a single operation
+ * @hostdata: pointer to the device information relevant to LPC controller
+ * @pio: the target I/O port address
+ * @val: a value to be output from caller, maximum is four bytes
+ * @dwidth: the data width required writing to the target I/O port
+ *
+ * This function corresponds to out(b,w,l) only.
+ */
+static void hisi_lpc_comm_out(void *hostdata, unsigned long pio,
+ u32 val, size_t dwidth)
+{
+ struct hisi_lpc_dev *lpcdev = hostdata;
+ struct lpc_cycle_para iopara;
+ const unsigned char *buf;
+ unsigned long addr;
+
+ if (!lpcdev || !dwidth || dwidth > LPC_MAX_DWIDTH)
+ return;
+
+ val = cpu_to_le32(val);
+
+ buf = (const unsigned char *)&val;
+ addr = hisi_lpc_pio_to_addr(lpcdev, pio);
+
+ iopara.opflags = FG_INCRADDR_LPC;
+ iopara.csize = dwidth;
+
+ hisi_lpc_target_out(lpcdev, &iopara, addr, buf, dwidth);
+}
+
+/*
+ * hisi_lpc_comm_ins - input the data in the buffer in multiple operations
+ * @hostdata: pointer to the device information relevant to LPC controller
+ * @pio: the target I/O port address
+ * @buffer: a buffer where read/input data bytes are stored
+ * @dwidth: the data width required writing to the target I/O port
+ * @count: how many data units whose length is dwidth will be read
+ *
+ * When success, the data read back is stored in buffer pointed by buffer.
+ * Returns 0 on success, -errno otherwise.
+ */
+static u32 hisi_lpc_comm_ins(void *hostdata, unsigned long pio, void *buffer,
+ size_t dwidth, unsigned int count)
+{
+ struct hisi_lpc_dev *lpcdev = hostdata;
+ unsigned char *buf = buffer;
+ struct lpc_cycle_para iopara;
+ unsigned long addr;
+
+ if (!lpcdev || !buf || !count || !dwidth || dwidth > LPC_MAX_DWIDTH)
+ return -EINVAL;
+
+ iopara.opflags = 0;
+ if (dwidth > 1)
+ iopara.opflags |= FG_INCRADDR_LPC;
+ iopara.csize = dwidth;
+
+ addr = hisi_lpc_pio_to_addr(lpcdev, pio);
+
+ do {
+ int ret;
+
+ ret = hisi_lpc_target_in(lpcdev, &iopara, addr, buf, dwidth);
+ if (ret)
+ return ret;
+ buf += dwidth;
+ } while (--count);
+
+ return 0;
+}
+
+/*
+ * hisi_lpc_comm_outs - output the data in the buffer in multiple operations
+ * @hostdata: pointer to the device information relevant to LPC controller
+ * @pio: the target I/O port address
+ * @buffer: a buffer where write/output data bytes are stored
+ * @dwidth: the data width required writing to the target I/O port
+ * @count: how many data units whose length is dwidth will be written
+ */
+static void hisi_lpc_comm_outs(void *hostdata, unsigned long pio,
+ const void *buffer, size_t dwidth,
+ unsigned int count)
+{
+ struct hisi_lpc_dev *lpcdev = hostdata;
+ struct lpc_cycle_para iopara;
+ const unsigned char *buf = buffer;
+ unsigned long addr;
+
+ if (!lpcdev || !buf || !count || !dwidth || dwidth > LPC_MAX_DWIDTH)
+ return;
+
+ iopara.opflags = 0;
+ if (dwidth > 1)
+ iopara.opflags |= FG_INCRADDR_LPC;
+ iopara.csize = dwidth;
+
+ addr = hisi_lpc_pio_to_addr(lpcdev, pio);
+ do {
+ if (hisi_lpc_target_out(lpcdev, &iopara, addr, buf, dwidth))
+ break;
+ buf += dwidth;
+ } while (--count);
+}
+
+static const struct logic_pio_host_ops hisi_lpc_ops = {
+ .in = hisi_lpc_comm_in,
+ .out = hisi_lpc_comm_out,
+ .ins = hisi_lpc_comm_ins,
+ .outs = hisi_lpc_comm_outs,
+};
+
+#ifdef CONFIG_ACPI
+#define MFD_CHILD_NAME_PREFIX DRV_NAME"-"
+#define MFD_CHILD_NAME_LEN (ACPI_ID_LEN + sizeof(MFD_CHILD_NAME_PREFIX) - 1)
+
+struct hisi_lpc_mfd_cell {
+ struct mfd_cell_acpi_match acpi_match;
+ char name[MFD_CHILD_NAME_LEN];
+ char pnpid[ACPI_ID_LEN];
+};
+
+static int hisi_lpc_acpi_xlat_io_res(struct acpi_device *adev,
+ struct acpi_device *host,
+ struct resource *res)
+{
+ unsigned long sys_port;
+ resource_size_t len = resource_size(res);
+
+ sys_port = logic_pio_trans_hwaddr(&host->fwnode, res->start, len);
+ if (sys_port == ~0UL)
+ return -EFAULT;
+
+ res->start = sys_port;
+ res->end = sys_port + len;
+
+ return 0;
+}
+
+/*
+ * hisi_lpc_acpi_set_io_res - set the resources for a child's MFD
+ * @child: the device node to be updated the I/O resource
+ * @hostdev: the device node associated with host controller
+ * @res: double pointer to be set to the address of translated resources
+ * @num_res: pointer to variable to hold the number of translated resources
+ *
+ * Returns 0 when successful, and a negative value for failure.
+ *
+ * For a given host controller, each child device will have an associated
+ * host-relative address resource. This function will return the translated
+ * logical PIO addresses for each child devices resources.
+ */
+static int hisi_lpc_acpi_set_io_res(struct device *child,
+ struct device *hostdev,
+ const struct resource **res, int *num_res)
+{
+ struct acpi_device *adev;
+ struct acpi_device *host;
+ struct resource_entry *rentry;
+ LIST_HEAD(resource_list);
+ struct resource *resources;
+ int count;
+ int i;
+
+ if (!child || !hostdev)
+ return -EINVAL;
+
+ host = to_acpi_device(hostdev);
+ adev = to_acpi_device(child);
+
+ if (!adev->status.present) {
+ dev_dbg(child, "device is not present\n");
+ return -EIO;
+ }
+
+ if (acpi_device_enumerated(adev)) {
+ dev_dbg(child, "has been enumerated\n");
+ return -EIO;
+ }
+
+ /*
+ * The following code segment to retrieve the resources is common to
+ * acpi_create_platform_device(), so consider a common helper function
+ * in future.
+ */
+ count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
+ if (count <= 0) {
+ dev_dbg(child, "failed to get resources\n");
+ return count ? count : -EIO;
+ }
+
+ resources = devm_kcalloc(hostdev, count, sizeof(*resources),
+ GFP_KERNEL);
+ if (!resources) {
+ dev_warn(hostdev, "could not allocate memory for %d resources\n",
+ count);
+ acpi_dev_free_resource_list(&resource_list);
+ return -ENOMEM;
+ }
+ count = 0;
+ list_for_each_entry(rentry, &resource_list, node)
+ resources[count++] = *rentry->res;
+
+ acpi_dev_free_resource_list(&resource_list);
+
+ /* translate the I/O resources */
+ for (i = 0; i < count; i++) {
+ int ret;
+
+ if (!(resources[i].flags & IORESOURCE_IO))
+ continue;
+ ret = hisi_lpc_acpi_xlat_io_res(adev, host, &resources[i]);
+ if (ret) {
+ dev_err(child, "translate IO range %pR failed (%d)\n",
+ &resources[i], ret);
+ return ret;
+ }
+ }
+ *res = resources;
+ *num_res = count;
+
+ return 0;
+}
+
+/*
+ * hisi_lpc_acpi_probe - probe children for ACPI FW
+ * @hostdev: LPC host device pointer
+ *
+ * Returns 0 when successful, and a negative value for failure.
+ *
+ * Scan all child devices and create a per-device MFD with
+ * logical PIO translated IO resources.
+ */
+static int hisi_lpc_acpi_probe(struct device *hostdev)
+{
+ struct acpi_device *adev = ACPI_COMPANION(hostdev);
+ struct hisi_lpc_mfd_cell *hisi_lpc_mfd_cells;
+ struct mfd_cell *mfd_cells;
+ struct acpi_device *child;
+ int size, ret, count = 0, cell_num = 0;
+
+ list_for_each_entry(child, &adev->children, node)
+ cell_num++;
+
+ /* allocate the mfd cell and companion ACPI info, one per child */
+ size = sizeof(*mfd_cells) + sizeof(*hisi_lpc_mfd_cells);
+ mfd_cells = devm_kcalloc(hostdev, cell_num, size, GFP_KERNEL);
+ if (!mfd_cells)
+ return -ENOMEM;
+
+ hisi_lpc_mfd_cells = (struct hisi_lpc_mfd_cell *)&mfd_cells[cell_num];
+ /* Only consider the children of the host */
+ list_for_each_entry(child, &adev->children, node) {
+ struct mfd_cell *mfd_cell = &mfd_cells[count];
+ struct hisi_lpc_mfd_cell *hisi_lpc_mfd_cell =
+ &hisi_lpc_mfd_cells[count];
+ struct mfd_cell_acpi_match *acpi_match =
+ &hisi_lpc_mfd_cell->acpi_match;
+ char *name = hisi_lpc_mfd_cell[count].name;
+ char *pnpid = hisi_lpc_mfd_cell[count].pnpid;
+ struct mfd_cell_acpi_match match = {
+ .pnpid = pnpid,
+ };
+
+ /*
+ * For any instances of this host controller (Hip06 and Hip07
+ * are the only chipsets), we would not have multiple slaves
+ * with the same HID. And in any system we would have just one
+ * controller active. So don't worrry about MFD name clashes.
+ */
+ snprintf(name, MFD_CHILD_NAME_LEN, MFD_CHILD_NAME_PREFIX"%s",
+ acpi_device_hid(child));
+ snprintf(pnpid, ACPI_ID_LEN, "%s", acpi_device_hid(child));
+
+ memcpy(acpi_match, &match, sizeof(*acpi_match));
+ mfd_cell->name = name;
+ mfd_cell->acpi_match = acpi_match;
+
+ ret = hisi_lpc_acpi_set_io_res(&child->dev, &adev->dev,
+ &mfd_cell->resources,
+ &mfd_cell->num_resources);
+ if (ret) {
+ dev_warn(&child->dev, "set resource fail (%d)\n", ret);
+ return ret;
+ }
+ count++;
+ }
+
+ ret = mfd_add_devices(hostdev, PLATFORM_DEVID_NONE,
+ mfd_cells, cell_num, NULL, 0, NULL);
+ if (ret) {
+ dev_err(hostdev, "failed to add mfd cells (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct acpi_device_id hisi_lpc_acpi_match[] = {
+ {"HISI0191"},
+ {}
+};
+#else
+static int hisi_lpc_acpi_probe(struct device *dev)
+{
+ return -ENODEV;
+}
+#endif // CONFIG_ACPI
+
+/*
+ * hisi_lpc_probe - the probe callback function for hisi lpc host,
+ * will finish all the initialization.
+ * @pdev: the platform device corresponding to hisi lpc host
+ *
+ * Returns 0 on success, non-zero on fail.
+ */
+static int hisi_lpc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acpi_device *acpi_device = ACPI_COMPANION(dev);
+ struct logic_pio_hwaddr *range;
+ struct hisi_lpc_dev *lpcdev;
+ resource_size_t io_end;
+ struct resource *res;
+ int ret;
+
+ lpcdev = devm_kzalloc(dev, sizeof(*lpcdev), GFP_KERNEL);
+ if (!lpcdev)
+ return -ENOMEM;
+
+ spin_lock_init(&lpcdev->cycle_lock);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ lpcdev->membase = devm_ioremap_resource(dev, res);
+ if (IS_ERR(lpcdev->membase))
+ return PTR_ERR(lpcdev->membase);
+
+ range = devm_kzalloc(dev, sizeof(*range), GFP_KERNEL);
+ if (!range)
+ return -ENOMEM;
+
+ range->fwnode = dev->fwnode;
+ range->flags = LOGIC_PIO_INDIRECT;
+ range->size = PIO_INDIRECT_SIZE;
+
+ ret = logic_pio_register_range(range);
+ if (ret) {
+ dev_err(dev, "register IO range failed (%d)!\n", ret);
+ return ret;
+ }
+ lpcdev->io_host = range;
+
+ /* register the LPC host PIO resources */
+ if (acpi_device)
+ ret = hisi_lpc_acpi_probe(dev);
+ else
+ ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
+ if (ret)
+ return ret;
+
+ lpcdev->io_host->hostdata = lpcdev;
+ lpcdev->io_host->ops = &hisi_lpc_ops;
+
+ io_end = lpcdev->io_host->io_start + lpcdev->io_host->size;
+ dev_info(dev, "registered range [%pa - %pa]\n",
+ &lpcdev->io_host->io_start, &io_end);
+
+ return ret;
+}
+
+static const struct of_device_id hisi_lpc_of_match[] = {
+ { .compatible = "hisilicon,hip06-lpc", },
+ { .compatible = "hisilicon,hip07-lpc", },
+ {}
+};
+
+static struct platform_driver hisi_lpc_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = hisi_lpc_of_match,
+ .acpi_match_table = ACPI_PTR(hisi_lpc_acpi_match),
+ },
+ .probe = hisi_lpc_probe,
+};
+builtin_platform_driver(hisi_lpc_driver);
diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c
index cdaeeea7999c..7cd2fd04b212 100644
--- a/drivers/bus/ti-sysc.c
+++ b/drivers/bus/ti-sysc.c
@@ -13,22 +13,20 @@
#include <linux/io.h>
#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/delay.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
+#include <linux/slab.h>
+
#include <linux/platform_data/ti-sysc.h>
#include <dt-bindings/bus/ti-sysc.h>
-enum sysc_registers {
- SYSC_REVISION,
- SYSC_SYSCONFIG,
- SYSC_SYSSTATUS,
- SYSC_MAX_REGS,
-};
-
static const char * const reg_names[] = { "rev", "sysc", "syss", };
enum sysc_clocks {
@@ -55,6 +53,7 @@ static const char * const clock_names[] = { "fck", "ick", };
* @cfg: interconnect target module configuration
* @name: name if available
* @revision: interconnect target module revision
+ * @needs_resume: runtime resume needed on resume from suspend
*/
struct sysc {
struct device *dev;
@@ -66,8 +65,13 @@ struct sysc {
const char *legacy_mode;
const struct sysc_capabilities *cap;
struct sysc_config cfg;
+ struct ti_sysc_cookie cookie;
const char *name;
u32 revision;
+ bool enabled;
+ bool needs_resume;
+ bool child_needs_resume;
+ struct delayed_work idle_work;
};
static u32 sysc_read(struct sysc *ddata, int offset)
@@ -136,9 +140,6 @@ static int sysc_get_clocks(struct sysc *ddata)
{
int i, error;
- if (ddata->legacy_mode)
- return 0;
-
for (i = 0; i < SYSC_MAX_CLOCKS; i++) {
error = sysc_get_one_clock(ddata, i);
if (error && error != -ENOENT)
@@ -197,12 +198,53 @@ static int sysc_parse_and_check_child_range(struct sysc *ddata)
ddata->module_pa = of_translate_address(np, ranges++);
ddata->module_size = be32_to_cpup(ranges);
- dev_dbg(ddata->dev, "interconnect target 0x%llx size 0x%x for %pOF\n",
- ddata->module_pa, ddata->module_size, np);
-
return 0;
}
+static struct device_node *stdout_path;
+
+static void sysc_init_stdout_path(struct sysc *ddata)
+{
+ struct device_node *np = NULL;
+ const char *uart;
+
+ if (IS_ERR(stdout_path))
+ return;
+
+ if (stdout_path)
+ return;
+
+ np = of_find_node_by_path("/chosen");
+ if (!np)
+ goto err;
+
+ uart = of_get_property(np, "stdout-path", NULL);
+ if (!uart)
+ goto err;
+
+ np = of_find_node_by_path(uart);
+ if (!np)
+ goto err;
+
+ stdout_path = np;
+
+ return;
+
+err:
+ stdout_path = ERR_PTR(-ENODEV);
+}
+
+static void sysc_check_quirk_stdout(struct sysc *ddata,
+ struct device_node *np)
+{
+ sysc_init_stdout_path(ddata);
+ if (np != stdout_path)
+ return;
+
+ ddata->cfg.quirks |= SYSC_QUIRK_NO_IDLE_ON_INIT |
+ SYSC_QUIRK_NO_RESET_ON_INIT;
+}
+
/**
* sysc_check_one_child - check child configuration
* @ddata: device driver data
@@ -221,6 +263,8 @@ static int sysc_check_one_child(struct sysc *ddata,
if (name)
dev_warn(ddata->dev, "really a child ti,hwmods property?");
+ sysc_check_quirk_stdout(ddata, np);
+
return 0;
}
@@ -246,11 +290,8 @@ static int sysc_check_children(struct sysc *ddata)
*/
static void sysc_check_quirk_16bit(struct sysc *ddata, struct resource *res)
{
- if (resource_size(res) == 8) {
- dev_dbg(ddata->dev,
- "enabling 16-bit and clockactivity quirks\n");
+ if (resource_size(res) == 8)
ddata->cfg.quirks |= SYSC_QUIRK_16BIT | SYSC_QUIRK_USE_CLOCKACT;
- }
}
/**
@@ -276,7 +317,6 @@ static int sysc_parse_one(struct sysc *ddata, enum sysc_registers reg)
res = platform_get_resource_byname(to_platform_device(ddata->dev),
IORESOURCE_MEM, name);
if (!res) {
- dev_dbg(ddata->dev, "has no %s register\n", name);
ddata->offsets[reg] = -ENODEV;
return 0;
@@ -437,6 +477,14 @@ static int sysc_show_reg(struct sysc *ddata,
return sprintf(bufp, ":%x", ddata->offsets[reg]);
}
+static int sysc_show_name(char *bufp, struct sysc *ddata)
+{
+ if (!ddata->name)
+ return 0;
+
+ return sprintf(bufp, ":%s", ddata->name);
+}
+
/**
* sysc_show_registers - show information about interconnect target module
* @ddata: device driver data
@@ -451,6 +499,7 @@ static void sysc_show_registers(struct sysc *ddata)
bufp += sysc_show_reg(ddata, bufp, i);
bufp += sysc_show_rev(bufp, ddata);
+ bufp += sysc_show_name(bufp, ddata);
dev_dbg(ddata->dev, "%llx:%x%s\n",
ddata->module_pa, ddata->module_size,
@@ -459,33 +508,70 @@ static void sysc_show_registers(struct sysc *ddata)
static int __maybe_unused sysc_runtime_suspend(struct device *dev)
{
+ struct ti_sysc_platform_data *pdata;
struct sysc *ddata;
- int i;
+ int error = 0, i;
ddata = dev_get_drvdata(dev);
- if (ddata->legacy_mode)
+ if (!ddata->enabled)
return 0;
+ if (ddata->legacy_mode) {
+ pdata = dev_get_platdata(ddata->dev);
+ if (!pdata)
+ return 0;
+
+ if (!pdata->idle_module)
+ return -ENODEV;
+
+ error = pdata->idle_module(dev, &ddata->cookie);
+ if (error)
+ dev_err(dev, "%s: could not idle: %i\n",
+ __func__, error);
+
+ goto idled;
+ }
+
for (i = 0; i < SYSC_MAX_CLOCKS; i++) {
if (IS_ERR_OR_NULL(ddata->clocks[i]))
continue;
clk_disable(ddata->clocks[i]);
}
- return 0;
+idled:
+ ddata->enabled = false;
+
+ return error;
}
static int __maybe_unused sysc_runtime_resume(struct device *dev)
{
+ struct ti_sysc_platform_data *pdata;
struct sysc *ddata;
- int i, error;
+ int error = 0, i;
ddata = dev_get_drvdata(dev);
- if (ddata->legacy_mode)
+ if (ddata->enabled)
return 0;
+ if (ddata->legacy_mode) {
+ pdata = dev_get_platdata(ddata->dev);
+ if (!pdata)
+ return 0;
+
+ if (!pdata->enable_module)
+ return -ENODEV;
+
+ error = pdata->enable_module(dev, &ddata->cookie);
+ if (error)
+ dev_err(dev, "%s: could not enable: %i\n",
+ __func__, error);
+
+ goto awake;
+ }
+
for (i = 0; i < SYSC_MAX_CLOCKS; i++) {
if (IS_ERR_OR_NULL(ddata->clocks[i]))
continue;
@@ -494,20 +580,136 @@ static int __maybe_unused sysc_runtime_resume(struct device *dev)
return error;
}
+awake:
+ ddata->enabled = true;
+
+ return error;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int sysc_suspend(struct device *dev)
+{
+ struct sysc *ddata;
+
+ ddata = dev_get_drvdata(dev);
+
+ if (!ddata->enabled)
+ return 0;
+
+ ddata->needs_resume = true;
+
+ return sysc_runtime_suspend(dev);
+}
+
+static int sysc_resume(struct device *dev)
+{
+ struct sysc *ddata;
+
+ ddata = dev_get_drvdata(dev);
+ if (ddata->needs_resume) {
+ ddata->needs_resume = false;
+
+ return sysc_runtime_resume(dev);
+ }
+
return 0;
}
+#endif
static const struct dev_pm_ops sysc_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(sysc_suspend, sysc_resume)
SET_RUNTIME_PM_OPS(sysc_runtime_suspend,
sysc_runtime_resume,
NULL)
};
+/* Module revision register based quirks */
+struct sysc_revision_quirk {
+ const char *name;
+ u32 base;
+ int rev_offset;
+ int sysc_offset;
+ int syss_offset;
+ u32 revision;
+ u32 revision_mask;
+ u32 quirks;
+};
+
+#define SYSC_QUIRK(optname, optbase, optrev, optsysc, optsyss, \
+ optrev_val, optrevmask, optquirkmask) \
+ { \
+ .name = (optname), \
+ .base = (optbase), \
+ .rev_offset = (optrev), \
+ .sysc_offset = (optsysc), \
+ .syss_offset = (optsyss), \
+ .revision = (optrev_val), \
+ .revision_mask = (optrevmask), \
+ .quirks = (optquirkmask), \
+ }
+
+static const struct sysc_revision_quirk sysc_revision_quirks[] = {
+ /* These drivers need to be fixed to not use pm_runtime_irq_safe() */
+ SYSC_QUIRK("gpio", 0, 0, 0x10, 0x114, 0x50600801, 0xffffffff,
+ SYSC_QUIRK_LEGACY_IDLE),
+ SYSC_QUIRK("mmu", 0, 0, 0x10, 0x14, 0x00000020, 0xffffffff,
+ SYSC_QUIRK_LEGACY_IDLE),
+ SYSC_QUIRK("mmu", 0, 0, 0x10, 0x14, 0x00000030, 0xffffffff,
+ SYSC_QUIRK_LEGACY_IDLE),
+ SYSC_QUIRK("sham", 0, 0x100, 0x110, 0x114, 0x40000c03, 0xffffffff,
+ SYSC_QUIRK_LEGACY_IDLE),
+ SYSC_QUIRK("smartreflex", 0, -1, 0x24, -1, 0x00000000, 0xffffffff,
+ SYSC_QUIRK_LEGACY_IDLE),
+ SYSC_QUIRK("smartreflex", 0, -1, 0x38, -1, 0x00000000, 0xffffffff,
+ SYSC_QUIRK_LEGACY_IDLE),
+ SYSC_QUIRK("timer", 0, 0, 0x10, 0x14, 0x00000015, 0xffffffff,
+ SYSC_QUIRK_LEGACY_IDLE),
+ SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000052, 0xffffffff,
+ SYSC_QUIRK_LEGACY_IDLE),
+};
+
+static void sysc_init_revision_quirks(struct sysc *ddata)
+{
+ const struct sysc_revision_quirk *q;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(sysc_revision_quirks); i++) {
+ q = &sysc_revision_quirks[i];
+
+ if (q->base && q->base != ddata->module_pa)
+ continue;
+
+ if (q->rev_offset >= 0 &&
+ q->rev_offset != ddata->offsets[SYSC_REVISION])
+ continue;
+
+ if (q->sysc_offset >= 0 &&
+ q->sysc_offset != ddata->offsets[SYSC_SYSCONFIG])
+ continue;
+
+ if (q->syss_offset >= 0 &&
+ q->syss_offset != ddata->offsets[SYSC_SYSSTATUS])
+ continue;
+
+ if (q->revision == ddata->revision ||
+ (q->revision & q->revision_mask) ==
+ (ddata->revision & q->revision_mask)) {
+ ddata->name = q->name;
+ ddata->cfg.quirks |= q->quirks;
+ }
+ }
+}
+
/* At this point the module is configured enough to read the revision */
static int sysc_init_module(struct sysc *ddata)
{
int error;
+ if (ddata->cfg.quirks & SYSC_QUIRK_NO_IDLE_ON_INIT) {
+ ddata->revision = sysc_read_revision(ddata);
+ goto rev_quirks;
+ }
+
error = pm_runtime_get_sync(ddata->dev);
if (error < 0) {
pm_runtime_put_noidle(ddata->dev);
@@ -517,6 +719,9 @@ static int sysc_init_module(struct sysc *ddata)
ddata->revision = sysc_read_revision(ddata);
pm_runtime_put_sync(ddata->dev);
+rev_quirks:
+ sysc_init_revision_quirks(ddata);
+
return 0;
}
@@ -605,6 +810,196 @@ static int sysc_init_syss_mask(struct sysc *ddata)
return 0;
}
+/*
+ * Many child device drivers need to have fck available to get the clock
+ * rate for device internal configuration.
+ */
+static int sysc_child_add_fck(struct sysc *ddata,
+ struct device *child)
+{
+ struct clk *fck;
+ struct clk_lookup *l;
+ const char *name = clock_names[SYSC_FCK];
+
+ if (IS_ERR_OR_NULL(ddata->clocks[SYSC_FCK]))
+ return 0;
+
+ fck = clk_get(child, name);
+ if (!IS_ERR(fck)) {
+ clk_put(fck);
+
+ return -EEXIST;
+ }
+
+ l = clkdev_create(ddata->clocks[SYSC_FCK], name, dev_name(child));
+
+ return l ? 0 : -ENODEV;
+}
+
+static struct device_type sysc_device_type = {
+};
+
+static struct sysc *sysc_child_to_parent(struct device *dev)
+{
+ struct device *parent = dev->parent;
+
+ if (!parent || parent->type != &sysc_device_type)
+ return NULL;
+
+ return dev_get_drvdata(parent);
+}
+
+static int __maybe_unused sysc_child_runtime_suspend(struct device *dev)
+{
+ struct sysc *ddata;
+ int error;
+
+ ddata = sysc_child_to_parent(dev);
+
+ error = pm_generic_runtime_suspend(dev);
+ if (error)
+ return error;
+
+ if (!ddata->enabled)
+ return 0;
+
+ return sysc_runtime_suspend(ddata->dev);
+}
+
+static int __maybe_unused sysc_child_runtime_resume(struct device *dev)
+{
+ struct sysc *ddata;
+ int error;
+
+ ddata = sysc_child_to_parent(dev);
+
+ if (!ddata->enabled) {
+ error = sysc_runtime_resume(ddata->dev);
+ if (error < 0)
+ dev_err(ddata->dev,
+ "%s error: %i\n", __func__, error);
+ }
+
+ return pm_generic_runtime_resume(dev);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int sysc_child_suspend_noirq(struct device *dev)
+{
+ struct sysc *ddata;
+ int error;
+
+ ddata = sysc_child_to_parent(dev);
+
+ error = pm_generic_suspend_noirq(dev);
+ if (error)
+ return error;
+
+ if (!pm_runtime_status_suspended(dev)) {
+ error = pm_generic_runtime_suspend(dev);
+ if (error)
+ return error;
+
+ error = sysc_runtime_suspend(ddata->dev);
+ if (error)
+ return error;
+
+ ddata->child_needs_resume = true;
+ }
+
+ return 0;
+}
+
+static int sysc_child_resume_noirq(struct device *dev)
+{
+ struct sysc *ddata;
+ int error;
+
+ ddata = sysc_child_to_parent(dev);
+
+ if (ddata->child_needs_resume) {
+ ddata->child_needs_resume = false;
+
+ error = sysc_runtime_resume(ddata->dev);
+ if (error)
+ dev_err(ddata->dev,
+ "%s runtime resume error: %i\n",
+ __func__, error);
+
+ error = pm_generic_runtime_resume(dev);
+ if (error)
+ dev_err(ddata->dev,
+ "%s generic runtime resume: %i\n",
+ __func__, error);
+ }
+
+ return pm_generic_resume_noirq(dev);
+}
+#endif
+
+struct dev_pm_domain sysc_child_pm_domain = {
+ .ops = {
+ SET_RUNTIME_PM_OPS(sysc_child_runtime_suspend,
+ sysc_child_runtime_resume,
+ NULL)
+ USE_PLATFORM_PM_SLEEP_OPS
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(sysc_child_suspend_noirq,
+ sysc_child_resume_noirq)
+ }
+};
+
+/**
+ * sysc_legacy_idle_quirk - handle children in omap_device compatible way
+ * @ddata: device driver data
+ * @child: child device driver
+ *
+ * Allow idle for child devices as done with _od_runtime_suspend().
+ * Otherwise many child devices will not idle because of the permanent
+ * parent usecount set in pm_runtime_irq_safe().
+ *
+ * Note that the long term solution is to just modify the child device
+ * drivers to not set pm_runtime_irq_safe() and then this can be just
+ * dropped.
+ */
+static void sysc_legacy_idle_quirk(struct sysc *ddata, struct device *child)
+{
+ if (!ddata->legacy_mode)
+ return;
+
+ if (ddata->cfg.quirks & SYSC_QUIRK_LEGACY_IDLE)
+ dev_pm_domain_set(child, &sysc_child_pm_domain);
+}
+
+static int sysc_notifier_call(struct notifier_block *nb,
+ unsigned long event, void *device)
+{
+ struct device *dev = device;
+ struct sysc *ddata;
+ int error;
+
+ ddata = sysc_child_to_parent(dev);
+ if (!ddata)
+ return NOTIFY_DONE;
+
+ switch (event) {
+ case BUS_NOTIFY_ADD_DEVICE:
+ error = sysc_child_add_fck(ddata, dev);
+ if (error && error != -EEXIST)
+ dev_warn(ddata->dev, "could not add %s fck: %i\n",
+ dev_name(dev), error);
+ sysc_legacy_idle_quirk(ddata, dev);
+ break;
+ default:
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block sysc_nb = {
+ .notifier_call = sysc_notifier_call,
+};
+
/* Device tree configured quirks */
struct sysc_dts_quirk {
const char *name;
@@ -797,7 +1192,8 @@ static const struct sysc_capabilities sysc_34xx_sr = {
.type = TI_SYSC_OMAP34XX_SR,
.sysc_mask = SYSC_OMAP2_CLOCKACTIVITY,
.regbits = &sysc_regbits_omap34xx_sr,
- .mod_quirks = SYSC_QUIRK_USE_CLOCKACT | SYSC_QUIRK_UNCACHED,
+ .mod_quirks = SYSC_QUIRK_USE_CLOCKACT | SYSC_QUIRK_UNCACHED |
+ SYSC_QUIRK_LEGACY_IDLE,
};
/*
@@ -818,12 +1214,13 @@ static const struct sysc_capabilities sysc_36xx_sr = {
.type = TI_SYSC_OMAP36XX_SR,
.sysc_mask = SYSC_OMAP3_SR_ENAWAKEUP,
.regbits = &sysc_regbits_omap36xx_sr,
- .mod_quirks = SYSC_QUIRK_UNCACHED,
+ .mod_quirks = SYSC_QUIRK_UNCACHED | SYSC_QUIRK_LEGACY_IDLE,
};
static const struct sysc_capabilities sysc_omap4_sr = {
.type = TI_SYSC_OMAP4_SR,
.regbits = &sysc_regbits_omap36xx_sr,
+ .mod_quirks = SYSC_QUIRK_LEGACY_IDLE,
};
/*
@@ -865,6 +1262,33 @@ static const struct sysc_capabilities sysc_omap4_usb_host_fs = {
.regbits = &sysc_regbits_omap4_usb_host_fs,
};
+static int sysc_init_pdata(struct sysc *ddata)
+{
+ struct ti_sysc_platform_data *pdata = dev_get_platdata(ddata->dev);
+ struct ti_sysc_module_data mdata;
+ int error = 0;
+
+ if (!pdata || !ddata->legacy_mode)
+ return 0;
+
+ mdata.name = ddata->legacy_mode;
+ mdata.module_pa = ddata->module_pa;
+ mdata.module_size = ddata->module_size;
+ mdata.offsets = ddata->offsets;
+ mdata.nr_offsets = SYSC_MAX_REGS;
+ mdata.cap = ddata->cap;
+ mdata.cfg = &ddata->cfg;
+
+ if (!pdata->init_module)
+ return -ENODEV;
+
+ error = pdata->init_module(ddata->dev, &mdata, &ddata->cookie);
+ if (error == -EEXIST)
+ error = 0;
+
+ return error;
+}
+
static int sysc_init_match(struct sysc *ddata)
{
const struct sysc_capabilities *cap;
@@ -880,8 +1304,19 @@ static int sysc_init_match(struct sysc *ddata)
return 0;
}
+static void ti_sysc_idle(struct work_struct *work)
+{
+ struct sysc *ddata;
+
+ ddata = container_of(work, struct sysc, idle_work.work);
+
+ if (pm_runtime_active(ddata->dev))
+ pm_runtime_put_sync(ddata->dev);
+}
+
static int sysc_probe(struct platform_device *pdev)
{
+ struct ti_sysc_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct sysc *ddata;
int error;
@@ -920,6 +1355,10 @@ static int sysc_probe(struct platform_device *pdev)
if (error)
goto unprepare;
+ error = sysc_init_pdata(ddata);
+ if (error)
+ goto unprepare;
+
pm_runtime_enable(ddata->dev);
error = sysc_init_module(ddata);
@@ -933,22 +1372,28 @@ static int sysc_probe(struct platform_device *pdev)
goto unprepare;
}
- pm_runtime_use_autosuspend(ddata->dev);
-
sysc_show_registers(ddata);
+ ddata->dev->type = &sysc_device_type;
error = of_platform_populate(ddata->dev->of_node,
- NULL, NULL, ddata->dev);
+ NULL, pdata ? pdata->auxdata : NULL,
+ ddata->dev);
if (error)
goto err;
- pm_runtime_mark_last_busy(ddata->dev);
- pm_runtime_put_autosuspend(ddata->dev);
+ INIT_DELAYED_WORK(&ddata->idle_work, ti_sysc_idle);
+
+ /* At least earlycon won't survive without deferred idle */
+ if (ddata->cfg.quirks & (SYSC_QUIRK_NO_IDLE_ON_INIT |
+ SYSC_QUIRK_NO_RESET_ON_INIT)) {
+ schedule_delayed_work(&ddata->idle_work, 3000);
+ } else {
+ pm_runtime_put(&pdev->dev);
+ }
return 0;
err:
- pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
unprepare:
@@ -962,6 +1407,8 @@ static int sysc_remove(struct platform_device *pdev)
struct sysc *ddata = platform_get_drvdata(pdev);
int error;
+ cancel_delayed_work_sync(&ddata->idle_work);
+
error = pm_runtime_get_sync(ddata->dev);
if (error < 0) {
pm_runtime_put_noidle(ddata->dev);
@@ -971,7 +1418,6 @@ static int sysc_remove(struct platform_device *pdev)
of_platform_depopulate(&pdev->dev);
- pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
@@ -1008,7 +1454,21 @@ static struct platform_driver sysc_driver = {
.pm = &sysc_pm_ops,
},
};
-module_platform_driver(sysc_driver);
+
+static int __init sysc_init(void)
+{
+ bus_register_notifier(&platform_bus_type, &sysc_nb);
+
+ return platform_driver_register(&sysc_driver);
+}
+module_init(sysc_init);
+
+static void __exit sysc_exit(void)
+{
+ bus_unregister_notifier(&platform_bus_type, &sysc_nb);
+ platform_driver_unregister(&sysc_driver);
+}
+module_exit(sysc_exit);
MODULE_DESCRIPTION("TI sysc interconnect target driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c
index e36d160c458f..bfc566d3f31a 100644
--- a/drivers/cdrom/cdrom.c
+++ b/drivers/cdrom/cdrom.c
@@ -1152,9 +1152,6 @@ int cdrom_open(struct cdrom_device_info *cdi, struct block_device *bdev,
cd_dbg(CD_OPEN, "entering cdrom_open\n");
- /* open is event synchronization point, check events first */
- check_disk_change(bdev);
-
/* if this was a O_NONBLOCK open and we should honor the flags,
* do a quick open without drive/disc integrity checks. */
cdi->use_count++;
@@ -2374,7 +2371,7 @@ static int cdrom_ioctl_media_changed(struct cdrom_device_info *cdi,
if (!CDROM_CAN(CDC_SELECT_DISC) || arg == CDSL_CURRENT)
return media_changed(cdi, 1);
- if ((unsigned int)arg >= cdi->capacity)
+ if (arg >= cdi->capacity)
return -EINVAL;
info = kmalloc(sizeof(*info), GFP_KERNEL);
diff --git a/drivers/cdrom/gdrom.c b/drivers/cdrom/gdrom.c
index 6495b03f576c..ae3a7537cf0f 100644
--- a/drivers/cdrom/gdrom.c
+++ b/drivers/cdrom/gdrom.c
@@ -497,6 +497,9 @@ static const struct cdrom_device_ops gdrom_ops = {
static int gdrom_bdops_open(struct block_device *bdev, fmode_t mode)
{
int ret;
+
+ check_disk_change(bdev);
+
mutex_lock(&gdrom_mutex);
ret = cdrom_open(gd.cd_info, bdev, mode);
mutex_unlock(&gdrom_mutex);
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index c28dca0c613d..e538061eadcb 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -66,34 +66,6 @@ config TTY_PRINTK
If unsure, say N.
-config BFIN_OTP
- tristate "Blackfin On-Chip OTP Memory Support"
- depends on BLACKFIN && (BF51x || BF52x || BF54x)
- default y
- help
- If you say Y here, you will get support for a character device
- interface into the One Time Programmable memory pages that are
- stored on the Blackfin processor. This will not get you access
- to the secure memory pages however. You will need to write your
- own secure code and reader for that.
-
- To compile this driver as a module, choose M here: the module
- will be called bfin-otp.
-
- If unsure, it is safe to say Y.
-
-config BFIN_OTP_WRITE_ENABLE
- bool "Enable writing support of OTP pages"
- depends on BFIN_OTP
- default n
- help
- If you say Y here, you will enable support for writing of the
- OTP pages. This is dangerous by nature as you can only program
- the pages once, so only enable this option when you actually
- need it so as to not inadvertently clobber data.
-
- If unsure, say N.
-
config PRINTER
tristate "Parallel printer support"
depends on PARPORT
@@ -264,7 +236,7 @@ source "drivers/char/hw_random/Kconfig"
config NVRAM
tristate "/dev/nvram support"
- depends on ATARI || X86 || (ARM && RTC_DRV_CMOS) || GENERIC_NVRAM
+ depends on ATARI || X86 || GENERIC_NVRAM
---help---
If you say Y here and create a character special file /dev/nvram
with major number 10 and minor number 144 using mknod ("man mknod"),
@@ -346,15 +318,6 @@ config EFI_RTC
bool "EFI Real Time Clock Services"
depends on IA64
-config DS1302
- tristate "DS1302 RTC support"
- depends on M32R && (PLAT_M32700UT || PLAT_OPSPUT)
- help
- If you say Y here and create a character special file /dev/rtc with
- major number 121 and minor number 0 using mknod ("man mknod"), you
- will get access to the real time clock (or hardware clock) built
- into your computer.
-
endif # RTC_LIB
config DTLK
@@ -575,17 +538,6 @@ config DEVPORT
source "drivers/s390/char/Kconfig"
-config TILE_SROM
- tristate "Character-device access via hypervisor to the Tilera SPI ROM"
- depends on TILE
- default y
- ---help---
- This device provides character-level read-write access
- to the SROM, typically via the "0", "1", and "2" devices
- in /dev/srom/. The Tilera hypervisor makes the flash
- device appear much like a simple EEPROM, and knows
- how to partition a single ROM for multiple purposes.
-
source "drivers/char/xillybus/Kconfig"
endmenu
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 7dc3abe66464..c97c768cd1dd 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -14,7 +14,6 @@ obj-$(CONFIG_MSPEC) += mspec.o
obj-$(CONFIG_UV_MMTIMER) += uv_mmtimer.o
obj-$(CONFIG_IBM_BSR) += bsr.o
obj-$(CONFIG_SGI_MBCS) += mbcs.o
-obj-$(CONFIG_BFIN_OTP) += bfin-otp.o
obj-$(CONFIG_PRINTER) += lp.o
@@ -26,7 +25,6 @@ obj-$(CONFIG_SONYPI) += sonypi.o
obj-$(CONFIG_RTC) += rtc.o
obj-$(CONFIG_HPET) += hpet.o
obj-$(CONFIG_EFI_RTC) += efirtc.o
-obj-$(CONFIG_DS1302) += ds1302.o
obj-$(CONFIG_XILINX_HWICAP) += xilinx_hwicap/
ifeq ($(CONFIG_GENERIC_NVRAM),y)
obj-$(CONFIG_NVRAM) += generic_nvram.o
@@ -57,6 +55,5 @@ obj-$(CONFIG_PS3_FLASH) += ps3flash.o
obj-$(CONFIG_JS_RTC) += js-rtc.o
js-rtc-y = rtc.o
-obj-$(CONFIG_TILE_SROM) += tile-srom.o
obj-$(CONFIG_XILLYBUS) += xillybus/
obj-$(CONFIG_POWERNV_OP_PANEL) += powernv-op-panel.o
diff --git a/drivers/char/bfin-otp.c b/drivers/char/bfin-otp.c
deleted file mode 100644
index 0584025bb0c2..000000000000
--- a/drivers/char/bfin-otp.c
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * Blackfin On-Chip OTP Memory Interface
- *
- * Copyright 2007-2009 Analog Devices Inc.
- *
- * Enter bugs at http://blackfin.uclinux.org/
- *
- * Licensed under the GPL-2 or later.
- */
-
-#include <linux/device.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/init.h>
-#include <linux/miscdevice.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/types.h>
-#include <mtd/mtd-abi.h>
-
-#include <asm/blackfin.h>
-#include <asm/bfrom.h>
-#include <linux/uaccess.h>
-
-#define stamp(fmt, args...) pr_debug("%s:%i: " fmt "\n", __func__, __LINE__, ## args)
-#define stampit() stamp("here i am")
-#define pr_init(fmt, args...) ({ static const __initconst char __fmt[] = fmt; printk(__fmt, ## args); })
-
-#define DRIVER_NAME "bfin-otp"
-#define PFX DRIVER_NAME ": "
-
-static DEFINE_MUTEX(bfin_otp_lock);
-
-/**
- * bfin_otp_read - Read OTP pages
- *
- * All reads must be in half page chunks (half page == 64 bits).
- */
-static ssize_t bfin_otp_read(struct file *file, char __user *buff, size_t count, loff_t *pos)
-{
- ssize_t bytes_done;
- u32 page, flags, ret;
- u64 content;
-
- stampit();
-
- if (count % sizeof(u64))
- return -EMSGSIZE;
-
- if (mutex_lock_interruptible(&bfin_otp_lock))
- return -ERESTARTSYS;
-
- bytes_done = 0;
- page = *pos / (sizeof(u64) * 2);
- while (bytes_done < count) {
- flags = (*pos % (sizeof(u64) * 2) ? OTP_UPPER_HALF : OTP_LOWER_HALF);
- stamp("processing page %i (0x%x:%s)", page, flags,
- (flags & OTP_UPPER_HALF ? "upper" : "lower"));
- ret = bfrom_OtpRead(page, flags, &content);
- if (ret & OTP_MASTER_ERROR) {
- stamp("error from otp: 0x%x", ret);
- bytes_done = -EIO;
- break;
- }
- if (copy_to_user(buff + bytes_done, &content, sizeof(content))) {
- bytes_done = -EFAULT;
- break;
- }
- if (flags & OTP_UPPER_HALF)
- ++page;
- bytes_done += sizeof(content);
- *pos += sizeof(content);
- }
-
- mutex_unlock(&bfin_otp_lock);
-
- return bytes_done;
-}
-
-#ifdef CONFIG_BFIN_OTP_WRITE_ENABLE
-static bool allow_writes;
-
-/**
- * bfin_otp_init_timing - setup OTP timing parameters
- *
- * Required before doing any write operation. Algorithms from HRM.
- */
-static u32 bfin_otp_init_timing(void)
-{
- u32 tp1, tp2, tp3, timing;
-
- tp1 = get_sclk() / 1000000;
- tp2 = (2 * get_sclk() / 10000000) << 8;
- tp3 = (0x1401) << 15;
- timing = tp1 | tp2 | tp3;
- if (bfrom_OtpCommand(OTP_INIT, timing))
- return 0;
-
- return timing;
-}
-
-/**
- * bfin_otp_deinit_timing - set timings to only allow reads
- *
- * Should be called after all writes are done.
- */
-static void bfin_otp_deinit_timing(u32 timing)
-{
- /* mask bits [31:15] so that any attempts to write fail */
- bfrom_OtpCommand(OTP_CLOSE, 0);
- bfrom_OtpCommand(OTP_INIT, timing & ~(-1 << 15));
- bfrom_OtpCommand(OTP_CLOSE, 0);
-}
-
-/**
- * bfin_otp_write - write OTP pages
- *
- * All writes must be in half page chunks (half page == 64 bits).
- */
-static ssize_t bfin_otp_write(struct file *filp, const char __user *buff, size_t count, loff_t *pos)
-{
- ssize_t bytes_done;
- u32 timing, page, base_flags, flags, ret;
- u64 content;
-
- if (!allow_writes)
- return -EACCES;
-
- if (count % sizeof(u64))
- return -EMSGSIZE;
-
- if (mutex_lock_interruptible(&bfin_otp_lock))
- return -ERESTARTSYS;
-
- stampit();
-
- timing = bfin_otp_init_timing();
- if (timing == 0) {
- mutex_unlock(&bfin_otp_lock);
- return -EIO;
- }
-
- base_flags = OTP_CHECK_FOR_PREV_WRITE;
-
- bytes_done = 0;
- page = *pos / (sizeof(u64) * 2);
- while (bytes_done < count) {
- flags = base_flags | (*pos % (sizeof(u64) * 2) ? OTP_UPPER_HALF : OTP_LOWER_HALF);
- stamp("processing page %i (0x%x:%s) from %p", page, flags,
- (flags & OTP_UPPER_HALF ? "upper" : "lower"), buff + bytes_done);
- if (copy_from_user(&content, buff + bytes_done, sizeof(content))) {
- bytes_done = -EFAULT;
- break;
- }
- ret = bfrom_OtpWrite(page, flags, &content);
- if (ret & OTP_MASTER_ERROR) {
- stamp("error from otp: 0x%x", ret);
- bytes_done = -EIO;
- break;
- }
- if (flags & OTP_UPPER_HALF)
- ++page;
- bytes_done += sizeof(content);
- *pos += sizeof(content);
- }
-
- bfin_otp_deinit_timing(timing);
-
- mutex_unlock(&bfin_otp_lock);
-
- return bytes_done;
-}
-
-static long bfin_otp_ioctl(struct file *filp, unsigned cmd, unsigned long arg)
-{
- stampit();
-
- switch (cmd) {
- case OTPLOCK: {
- u32 timing;
- int ret = -EIO;
-
- if (!allow_writes)
- return -EACCES;
-
- if (mutex_lock_interruptible(&bfin_otp_lock))
- return -ERESTARTSYS;
-
- timing = bfin_otp_init_timing();
- if (timing) {
- u32 otp_result = bfrom_OtpWrite(arg, OTP_LOCK, NULL);
- stamp("locking page %lu resulted in 0x%x", arg, otp_result);
- if (!(otp_result & OTP_MASTER_ERROR))
- ret = 0;
-
- bfin_otp_deinit_timing(timing);
- }
-
- mutex_unlock(&bfin_otp_lock);
-
- return ret;
- }
-
- case MEMLOCK:
- allow_writes = false;
- return 0;
-
- case MEMUNLOCK:
- allow_writes = true;
- return 0;
- }
-
- return -EINVAL;
-}
-#else
-# define bfin_otp_write NULL
-# define bfin_otp_ioctl NULL
-#endif
-
-static const struct file_operations bfin_otp_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = bfin_otp_ioctl,
- .read = bfin_otp_read,
- .write = bfin_otp_write,
- .llseek = default_llseek,
-};
-
-static struct miscdevice bfin_otp_misc_device = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = DRIVER_NAME,
- .fops = &bfin_otp_fops,
-};
-module_misc_device(bfin_otp_misc_device);
-
-MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>");
-MODULE_DESCRIPTION("Blackfin OTP Memory Interface");
-MODULE_LICENSE("GPL");
diff --git a/drivers/char/ds1302.c b/drivers/char/ds1302.c
deleted file mode 100644
index 8e16ad5d6d89..000000000000
--- a/drivers/char/ds1302.c
+++ /dev/null
@@ -1,357 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*!***************************************************************************
-*!
-*! FILE NAME : ds1302.c
-*!
-*! DESCRIPTION: Implements an interface for the DS1302 RTC
-*!
-*! Functions exported: ds1302_readreg, ds1302_writereg, ds1302_init, get_rtc_status
-*!
-*! ---------------------------------------------------------------------------
-*!
-*! (C) Copyright 1999, 2000, 2001 Axis Communications AB, LUND, SWEDEN
-*!
-*!***************************************************************************/
-
-
-#include <linux/fs.h>
-#include <linux/init.h>
-#include <linux/mm.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/bcd.h>
-#include <linux/mutex.h>
-#include <linux/uaccess.h>
-#include <linux/io.h>
-
-#include <asm/rtc.h>
-#if defined(CONFIG_M32R)
-#include <asm/m32r.h>
-#endif
-
-#define RTC_MAJOR_NR 121 /* local major, change later */
-
-static DEFINE_MUTEX(rtc_mutex);
-static const char ds1302_name[] = "ds1302";
-
-/* Send 8 bits. */
-static void
-out_byte_rtc(unsigned int reg_addr, unsigned char x)
-{
- //RST H
- outw(0x0001,(unsigned long)PLD_RTCRSTODT);
- //write data
- outw(((x<<8)|(reg_addr&0xff)),(unsigned long)PLD_RTCWRDATA);
- //WE
- outw(0x0002,(unsigned long)PLD_RTCCR);
- //wait
- while(inw((unsigned long)PLD_RTCCR));
-
- //RST L
- outw(0x0000,(unsigned long)PLD_RTCRSTODT);
-
-}
-
-static unsigned char
-in_byte_rtc(unsigned int reg_addr)
-{
- unsigned char retval;
-
- //RST H
- outw(0x0001,(unsigned long)PLD_RTCRSTODT);
- //write data
- outw((reg_addr&0xff),(unsigned long)PLD_RTCRDDATA);
- //RE
- outw(0x0001,(unsigned long)PLD_RTCCR);
- //wait
- while(inw((unsigned long)PLD_RTCCR));
-
- //read data
- retval=(inw((unsigned long)PLD_RTCRDDATA) & 0xff00)>>8;
-
- //RST L
- outw(0x0000,(unsigned long)PLD_RTCRSTODT);
-
- return retval;
-}
-
-/* Enable writing. */
-
-static void
-ds1302_wenable(void)
-{
- out_byte_rtc(0x8e,0x00);
-}
-
-/* Disable writing. */
-
-static void
-ds1302_wdisable(void)
-{
- out_byte_rtc(0x8e,0x80);
-}
-
-
-
-/* Read a byte from the selected register in the DS1302. */
-
-unsigned char
-ds1302_readreg(int reg)
-{
- unsigned char x;
-
- x=in_byte_rtc((0x81 | (reg << 1))); /* read register */
-
- return x;
-}
-
-/* Write a byte to the selected register. */
-
-void
-ds1302_writereg(int reg, unsigned char val)
-{
- ds1302_wenable();
- out_byte_rtc((0x80 | (reg << 1)),val);
- ds1302_wdisable();
-}
-
-void
-get_rtc_time(struct rtc_time *rtc_tm)
-{
- unsigned long flags;
-
- local_irq_save(flags);
-
- rtc_tm->tm_sec = CMOS_READ(RTC_SECONDS);
- rtc_tm->tm_min = CMOS_READ(RTC_MINUTES);
- rtc_tm->tm_hour = CMOS_READ(RTC_HOURS);
- rtc_tm->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH);
- rtc_tm->tm_mon = CMOS_READ(RTC_MONTH);
- rtc_tm->tm_year = CMOS_READ(RTC_YEAR);
-
- local_irq_restore(flags);
-
- rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec);
- rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min);
- rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour);
- rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday);
- rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon);
- rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year);
-
- /*
- * Account for differences between how the RTC uses the values
- * and how they are defined in a struct rtc_time;
- */
-
- if (rtc_tm->tm_year <= 69)
- rtc_tm->tm_year += 100;
-
- rtc_tm->tm_mon--;
-}
-
-static unsigned char days_in_mo[] =
- {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
-
-/* ioctl that supports RTC_RD_TIME and RTC_SET_TIME (read and set time/date). */
-
-static long rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- unsigned long flags;
-
- switch(cmd) {
- case RTC_RD_TIME: /* read the time/date from RTC */
- {
- struct rtc_time rtc_tm;
-
- memset(&rtc_tm, 0, sizeof (struct rtc_time));
- mutex_lock(&rtc_mutex);
- get_rtc_time(&rtc_tm);
- mutex_unlock(&rtc_mutex);
- if (copy_to_user((struct rtc_time*)arg, &rtc_tm, sizeof(struct rtc_time)))
- return -EFAULT;
- return 0;
- }
-
- case RTC_SET_TIME: /* set the RTC */
- {
- struct rtc_time rtc_tm;
- unsigned char mon, day, hrs, min, sec, leap_yr;
- unsigned int yrs;
-
- if (!capable(CAP_SYS_TIME))
- return -EPERM;
-
- if (copy_from_user(&rtc_tm, (struct rtc_time*)arg, sizeof(struct rtc_time)))
- return -EFAULT;
-
- yrs = rtc_tm.tm_year + 1900;
- mon = rtc_tm.tm_mon + 1; /* tm_mon starts at zero */
- day = rtc_tm.tm_mday;
- hrs = rtc_tm.tm_hour;
- min = rtc_tm.tm_min;
- sec = rtc_tm.tm_sec;
-
-
- if ((yrs < 1970) || (yrs > 2069))
- return -EINVAL;
-
- leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400));
-
- if ((mon > 12) || (day == 0))
- return -EINVAL;
-
- if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr)))
- return -EINVAL;
-
- if ((hrs >= 24) || (min >= 60) || (sec >= 60))
- return -EINVAL;
-
- if (yrs >= 2000)
- yrs -= 2000; /* RTC (0, 1, ... 69) */
- else
- yrs -= 1900; /* RTC (70, 71, ... 99) */
-
- sec = bin2bcd(sec);
- min = bin2bcd(min);
- hrs = bin2bcd(hrs);
- day = bin2bcd(day);
- mon = bin2bcd(mon);
- yrs = bin2bcd(yrs);
-
- mutex_lock(&rtc_mutex);
- local_irq_save(flags);
- CMOS_WRITE(yrs, RTC_YEAR);
- CMOS_WRITE(mon, RTC_MONTH);
- CMOS_WRITE(day, RTC_DAY_OF_MONTH);
- CMOS_WRITE(hrs, RTC_HOURS);
- CMOS_WRITE(min, RTC_MINUTES);
- CMOS_WRITE(sec, RTC_SECONDS);
- local_irq_restore(flags);
- mutex_unlock(&rtc_mutex);
-
- /* Notice that at this point, the RTC is updated but
- * the kernel is still running with the old time.
- * You need to set that separately with settimeofday
- * or adjtimex.
- */
- return 0;
- }
-
- case RTC_SET_CHARGE: /* set the RTC TRICKLE CHARGE register */
- {
- int tcs_val;
-
- if (!capable(CAP_SYS_TIME))
- return -EPERM;
-
- if(copy_from_user(&tcs_val, (int*)arg, sizeof(int)))
- return -EFAULT;
-
- mutex_lock(&rtc_mutex);
- tcs_val = RTC_TCR_PATTERN | (tcs_val & 0x0F);
- ds1302_writereg(RTC_TRICKLECHARGER, tcs_val);
- mutex_unlock(&rtc_mutex);
- return 0;
- }
- default:
- return -EINVAL;
- }
-}
-
-int
-get_rtc_status(char *buf)
-{
- char *p;
- struct rtc_time tm;
-
- p = buf;
-
- get_rtc_time(&tm);
-
- /*
- * There is no way to tell if the luser has the RTC set for local
- * time or for Universal Standard Time (GMT). Probably local though.
- */
-
- p += sprintf(p,
- "rtc_time\t: %02d:%02d:%02d\n"
- "rtc_date\t: %04d-%02d-%02d\n",
- tm.tm_hour, tm.tm_min, tm.tm_sec,
- tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
-
- return p - buf;
-}
-
-
-/* The various file operations we support. */
-
-static const struct file_operations rtc_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = rtc_ioctl,
- .llseek = noop_llseek,
-};
-
-/* Probe for the chip by writing something to its RAM and try reading it back. */
-
-#define MAGIC_PATTERN 0x42
-
-static int __init
-ds1302_probe(void)
-{
- int retval, res, baur;
-
- baur=(boot_cpu_data.bus_clock/(2*1000*1000));
-
- printk("%s: Set PLD_RTCBAUR = %d\n", ds1302_name,baur);
-
- outw(0x0000,(unsigned long)PLD_RTCCR);
- outw(0x0000,(unsigned long)PLD_RTCRSTODT);
- outw(baur,(unsigned long)PLD_RTCBAUR);
-
- /* Try to talk to timekeeper. */
-
- ds1302_wenable();
- /* write RAM byte 0 */
- /* write something magic */
- out_byte_rtc(0xc0,MAGIC_PATTERN);
-
- /* read RAM byte 0 */
- if((res = in_byte_rtc(0xc1)) == MAGIC_PATTERN) {
- char buf[100];
- ds1302_wdisable();
- printk("%s: RTC found.\n", ds1302_name);
- get_rtc_status(buf);
- printk(buf);
- retval = 1;
- } else {
- printk("%s: RTC not found.\n", ds1302_name);
- retval = 0;
- }
-
- return retval;
-}
-
-
-/* Just probe for the RTC and register the device to handle the ioctl needed. */
-
-int __init
-ds1302_init(void)
-{
- if (!ds1302_probe()) {
- return -1;
- }
- return 0;
-}
-
-static int __init ds1302_register(void)
-{
- ds1302_init();
- if (register_chrdev(RTC_MAJOR_NR, ds1302_name, &rtc_fops)) {
- printk(KERN_INFO "%s: unable to get major %d for rtc\n",
- ds1302_name, RTC_MAJOR_NR);
- return -1;
- }
- return 0;
-}
-
-module_init(ds1302_register);
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
index 4d0f571c15f9..d53541e96bee 100644
--- a/drivers/char/hw_random/Kconfig
+++ b/drivers/char/hw_random/Kconfig
@@ -452,3 +452,10 @@ config UML_RANDOM
(check your distro, or download from
http://sourceforge.net/projects/gkernel/). rngd periodically reads
/dev/hwrng and injects the entropy into /dev/random.
+
+config HW_RANDOM_KEYSTONE
+ depends on ARCH_KEYSTONE
+ default HW_RANDOM
+ tristate "TI Keystone NETCP SA Hardware random number generator"
+ help
+ This option enables Keystone's hardware random generator.
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile
index b780370bd4eb..533e913c93d1 100644
--- a/drivers/char/hw_random/Makefile
+++ b/drivers/char/hw_random/Makefile
@@ -38,3 +38,4 @@ obj-$(CONFIG_HW_RANDOM_MESON) += meson-rng.o
obj-$(CONFIG_HW_RANDOM_CAVIUM) += cavium-rng.o cavium-rng-vf.o
obj-$(CONFIG_HW_RANDOM_MTK) += mtk-rng.o
obj-$(CONFIG_HW_RANDOM_S390) += s390-trng.o
+obj-$(CONFIG_HW_RANDOM_KEYSTONE) += ks-sa-rng.o
diff --git a/drivers/char/hw_random/bcm2835-rng.c b/drivers/char/hw_random/bcm2835-rng.c
index 7a84cec30c3a..6767d965c36c 100644
--- a/drivers/char/hw_random/bcm2835-rng.c
+++ b/drivers/char/hw_random/bcm2835-rng.c
@@ -163,6 +163,8 @@ static int bcm2835_rng_probe(struct platform_device *pdev)
/* Clock is optional on most platforms */
priv->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(priv->clk) && PTR_ERR(priv->clk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
priv->rng.name = pdev->name;
priv->rng.init = bcm2835_rng_init;
diff --git a/drivers/char/hw_random/cavium-rng-vf.c b/drivers/char/hw_random/cavium-rng-vf.c
index dd1007aecb10..2d1352b67168 100644
--- a/drivers/char/hw_random/cavium-rng-vf.c
+++ b/drivers/char/hw_random/cavium-rng-vf.c
@@ -77,7 +77,7 @@ static int cavium_rng_probe_vf(struct pci_dev *pdev,
}
/* Remove the VF */
-void cavium_rng_remove_vf(struct pci_dev *pdev)
+static void cavium_rng_remove_vf(struct pci_dev *pdev)
{
struct cavium_rng *rng;
diff --git a/drivers/char/hw_random/cavium-rng.c b/drivers/char/hw_random/cavium-rng.c
index a944e0a47f42..63d6e68c24d2 100644
--- a/drivers/char/hw_random/cavium-rng.c
+++ b/drivers/char/hw_random/cavium-rng.c
@@ -62,7 +62,7 @@ static int cavium_rng_probe(struct pci_dev *pdev,
}
/* Disable VF and RNG Hardware */
-void cavium_rng_remove(struct pci_dev *pdev)
+static void cavium_rng_remove(struct pci_dev *pdev)
{
struct cavium_rng_pf *rng;
diff --git a/drivers/char/hw_random/imx-rngc.c b/drivers/char/hw_random/imx-rngc.c
index eca87249bcff..250123bc4905 100644
--- a/drivers/char/hw_random/imx-rngc.c
+++ b/drivers/char/hw_random/imx-rngc.c
@@ -300,7 +300,7 @@ static int __maybe_unused imx_rngc_resume(struct device *dev)
return 0;
}
-SIMPLE_DEV_PM_OPS(imx_rngc_pm_ops, imx_rngc_suspend, imx_rngc_resume);
+static SIMPLE_DEV_PM_OPS(imx_rngc_pm_ops, imx_rngc_suspend, imx_rngc_resume);
static const struct of_device_id imx_rngc_dt_ids[] = {
{ .compatible = "fsl,imx25-rngb", .data = NULL, },
diff --git a/drivers/char/hw_random/ks-sa-rng.c b/drivers/char/hw_random/ks-sa-rng.c
new file mode 100644
index 000000000000..62c6696c1dbd
--- /dev/null
+++ b/drivers/char/hw_random/ks-sa-rng.c
@@ -0,0 +1,257 @@
+/*
+ * Random Number Generator driver for the Keystone SOC
+ *
+ * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Authors: Sandeep Nair
+ * Vitaly Andrianov
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+#include <linux/hw_random.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/err.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/delay.h>
+
+#define SA_CMD_STATUS_OFS 0x8
+
+/* TRNG enable control in SA System module*/
+#define SA_CMD_STATUS_REG_TRNG_ENABLE BIT(3)
+
+/* TRNG start control in TRNG module */
+#define TRNG_CNTL_REG_TRNG_ENABLE BIT(10)
+
+/* Data ready indicator in STATUS register */
+#define TRNG_STATUS_REG_READY BIT(0)
+
+/* Data ready clear control in INTACK register */
+#define TRNG_INTACK_REG_READY BIT(0)
+
+/*
+ * Number of samples taken to gather entropy during startup.
+ * If value is 0, the number of samples is 2^24 else
+ * equals value times 2^8.
+ */
+#define TRNG_DEF_STARTUP_CYCLES 0
+#define TRNG_CNTL_REG_STARTUP_CYCLES_SHIFT 16
+
+/*
+ * Minimum number of samples taken to regenerate entropy
+ * If value is 0, the number of samples is 2^24 else
+ * equals value times 2^6.
+ */
+#define TRNG_DEF_MIN_REFILL_CYCLES 1
+#define TRNG_CFG_REG_MIN_REFILL_CYCLES_SHIFT 0
+
+/*
+ * Maximum number of samples taken to regenerate entropy
+ * If value is 0, the number of samples is 2^24 else
+ * equals value times 2^8.
+ */
+#define TRNG_DEF_MAX_REFILL_CYCLES 0
+#define TRNG_CFG_REG_MAX_REFILL_CYCLES_SHIFT 16
+
+/* Number of CLK input cycles between samples */
+#define TRNG_DEF_CLK_DIV_CYCLES 0
+#define TRNG_CFG_REG_SAMPLE_DIV_SHIFT 8
+
+/* Maximum retries to get rng data */
+#define SA_MAX_RNG_DATA_RETRIES 5
+/* Delay between retries (in usecs) */
+#define SA_RNG_DATA_RETRY_DELAY 5
+
+struct trng_regs {
+ u32 output_l;
+ u32 output_h;
+ u32 status;
+ u32 intmask;
+ u32 intack;
+ u32 control;
+ u32 config;
+};
+
+struct ks_sa_rng {
+ struct device *dev;
+ struct hwrng rng;
+ struct clk *clk;
+ struct regmap *regmap_cfg;
+ struct trng_regs *reg_rng;
+};
+
+static int ks_sa_rng_init(struct hwrng *rng)
+{
+ u32 value;
+ struct device *dev = (struct device *)rng->priv;
+ struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev);
+
+ /* Enable RNG module */
+ regmap_write_bits(ks_sa_rng->regmap_cfg, SA_CMD_STATUS_OFS,
+ SA_CMD_STATUS_REG_TRNG_ENABLE,
+ SA_CMD_STATUS_REG_TRNG_ENABLE);
+
+ /* Configure RNG module */
+ writel(0, &ks_sa_rng->reg_rng->control);
+ value = TRNG_DEF_STARTUP_CYCLES << TRNG_CNTL_REG_STARTUP_CYCLES_SHIFT;
+ writel(value, &ks_sa_rng->reg_rng->control);
+
+ value = (TRNG_DEF_MIN_REFILL_CYCLES <<
+ TRNG_CFG_REG_MIN_REFILL_CYCLES_SHIFT) |
+ (TRNG_DEF_MAX_REFILL_CYCLES <<
+ TRNG_CFG_REG_MAX_REFILL_CYCLES_SHIFT) |
+ (TRNG_DEF_CLK_DIV_CYCLES <<
+ TRNG_CFG_REG_SAMPLE_DIV_SHIFT);
+
+ writel(value, &ks_sa_rng->reg_rng->config);
+
+ /* Disable all interrupts from TRNG */
+ writel(0, &ks_sa_rng->reg_rng->intmask);
+
+ /* Enable RNG */
+ value = readl(&ks_sa_rng->reg_rng->control);
+ value |= TRNG_CNTL_REG_TRNG_ENABLE;
+ writel(value, &ks_sa_rng->reg_rng->control);
+
+ return 0;
+}
+
+static void ks_sa_rng_cleanup(struct hwrng *rng)
+{
+ struct device *dev = (struct device *)rng->priv;
+ struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev);
+
+ /* Disable RNG */
+ writel(0, &ks_sa_rng->reg_rng->control);
+ regmap_write_bits(ks_sa_rng->regmap_cfg, SA_CMD_STATUS_OFS,
+ SA_CMD_STATUS_REG_TRNG_ENABLE, 0);
+}
+
+static int ks_sa_rng_data_read(struct hwrng *rng, u32 *data)
+{
+ struct device *dev = (struct device *)rng->priv;
+ struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev);
+
+ /* Read random data */
+ data[0] = readl(&ks_sa_rng->reg_rng->output_l);
+ data[1] = readl(&ks_sa_rng->reg_rng->output_h);
+
+ writel(TRNG_INTACK_REG_READY, &ks_sa_rng->reg_rng->intack);
+
+ return sizeof(u32) * 2;
+}
+
+static int ks_sa_rng_data_present(struct hwrng *rng, int wait)
+{
+ struct device *dev = (struct device *)rng->priv;
+ struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev);
+
+ u32 ready;
+ int j;
+
+ for (j = 0; j < SA_MAX_RNG_DATA_RETRIES; j++) {
+ ready = readl(&ks_sa_rng->reg_rng->status);
+ ready &= TRNG_STATUS_REG_READY;
+
+ if (ready || !wait)
+ break;
+
+ udelay(SA_RNG_DATA_RETRY_DELAY);
+ }
+
+ return ready;
+}
+
+static int ks_sa_rng_probe(struct platform_device *pdev)
+{
+ struct ks_sa_rng *ks_sa_rng;
+ struct device *dev = &pdev->dev;
+ int ret;
+ struct resource *mem;
+
+ ks_sa_rng = devm_kzalloc(dev, sizeof(*ks_sa_rng), GFP_KERNEL);
+ if (!ks_sa_rng)
+ return -ENOMEM;
+
+ ks_sa_rng->dev = dev;
+ ks_sa_rng->rng = (struct hwrng) {
+ .name = "ks_sa_hwrng",
+ .init = ks_sa_rng_init,
+ .data_read = ks_sa_rng_data_read,
+ .data_present = ks_sa_rng_data_present,
+ .cleanup = ks_sa_rng_cleanup,
+ };
+ ks_sa_rng->rng.priv = (unsigned long)dev;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ks_sa_rng->reg_rng = devm_ioremap_resource(dev, mem);
+ if (IS_ERR(ks_sa_rng->reg_rng))
+ return PTR_ERR(ks_sa_rng->reg_rng);
+
+ ks_sa_rng->regmap_cfg =
+ syscon_regmap_lookup_by_phandle(dev->of_node,
+ "ti,syscon-sa-cfg");
+
+ if (IS_ERR(ks_sa_rng->regmap_cfg)) {
+ dev_err(dev, "syscon_node_to_regmap failed\n");
+ return -EINVAL;
+ }
+
+ pm_runtime_enable(dev);
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable SA power-domain\n");
+ pm_runtime_disable(dev);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, ks_sa_rng);
+
+ return devm_hwrng_register(&pdev->dev, &ks_sa_rng->rng);
+}
+
+static int ks_sa_rng_remove(struct platform_device *pdev)
+{
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+static const struct of_device_id ks_sa_rng_dt_match[] = {
+ {
+ .compatible = "ti,keystone-rng",
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ks_sa_rng_dt_match);
+
+static struct platform_driver ks_sa_rng_driver = {
+ .driver = {
+ .name = "ks-sa-rng",
+ .of_match_table = ks_sa_rng_dt_match,
+ },
+ .probe = ks_sa_rng_probe,
+ .remove = ks_sa_rng_remove,
+};
+
+module_platform_driver(ks_sa_rng_driver);
+
+MODULE_DESCRIPTION("Keystone NETCP SA H/W Random Number Generator driver");
+MODULE_AUTHOR("Vitaly Andrianov <vitalya@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/hw_random/mxc-rnga.c b/drivers/char/hw_random/mxc-rnga.c
index 467362262651..f83bee513d91 100644
--- a/drivers/char/hw_random/mxc-rnga.c
+++ b/drivers/char/hw_random/mxc-rnga.c
@@ -16,16 +16,13 @@
* This driver is based on other RNG drivers.
*/
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
#include <linux/clk.h>
-#include <linux/err.h>
-#include <linux/ioport.h>
-#include <linux/platform_device.h>
-#include <linux/hw_random.h>
#include <linux/delay.h>
+#include <linux/hw_random.h>
#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
/* RNGA Registers */
#define RNGA_CONTROL 0x00
@@ -197,10 +194,18 @@ static int __exit mxc_rnga_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id mxc_rnga_of_match[] = {
+ { .compatible = "fsl,imx21-rnga", },
+ { .compatible = "fsl,imx31-rnga", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, mxc_rnga_of_match);
+
static struct platform_driver mxc_rnga_driver = {
.driver = {
- .name = "mxc_rnga",
- },
+ .name = "mxc_rnga",
+ .of_match_table = mxc_rnga_of_match,
+ },
.remove = __exit_p(mxc_rnga_remove),
};
diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c
index 74d11ae6abe9..b65ff6962899 100644
--- a/drivers/char/hw_random/omap-rng.c
+++ b/drivers/char/hw_random/omap-rng.c
@@ -150,6 +150,7 @@ struct omap_rng_dev {
const struct omap_rng_pdata *pdata;
struct hwrng rng;
struct clk *clk;
+ struct clk *clk_reg;
};
static inline u32 omap_rng_read(struct omap_rng_dev *priv, u16 reg)
@@ -480,6 +481,19 @@ static int omap_rng_probe(struct platform_device *pdev)
}
}
+ priv->clk_reg = devm_clk_get(&pdev->dev, "reg");
+ if (IS_ERR(priv->clk_reg) && PTR_ERR(priv->clk_reg) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ if (!IS_ERR(priv->clk_reg)) {
+ ret = clk_prepare_enable(priv->clk_reg);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Unable to enable the register clk: %d\n",
+ ret);
+ goto err_register;
+ }
+ }
+
ret = (dev->of_node) ? of_get_omap_rng_device_details(priv, pdev) :
get_omap_rng_device_details(priv);
if (ret)
@@ -499,8 +513,8 @@ err_register:
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
- if (!IS_ERR(priv->clk))
- clk_disable_unprepare(priv->clk);
+ clk_disable_unprepare(priv->clk_reg);
+ clk_disable_unprepare(priv->clk);
err_ioremap:
dev_err(dev, "initialization failed.\n");
return ret;
@@ -517,8 +531,8 @@ static int omap_rng_remove(struct platform_device *pdev)
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
- if (!IS_ERR(priv->clk))
- clk_disable_unprepare(priv->clk);
+ clk_disable_unprepare(priv->clk);
+ clk_disable_unprepare(priv->clk_reg);
return 0;
}
diff --git a/drivers/char/hw_random/stm32-rng.c b/drivers/char/hw_random/stm32-rng.c
index 63d84e6f1891..0d2328da3b76 100644
--- a/drivers/char/hw_random/stm32-rng.c
+++ b/drivers/char/hw_random/stm32-rng.c
@@ -16,15 +16,18 @@
#include <linux/delay.h>
#include <linux/hw_random.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/pm_runtime.h>
+#include <linux/reset.h>
#include <linux/slab.h>
#define RNG_CR 0x00
#define RNG_CR_RNGEN BIT(2)
+#define RNG_CR_CED BIT(5)
#define RNG_SR 0x04
#define RNG_SR_SEIS BIT(6)
@@ -33,19 +36,12 @@
#define RNG_DR 0x08
-/*
- * It takes 40 cycles @ 48MHz to generate each random number (e.g. <1us).
- * At the time of writing STM32 parts max out at ~200MHz meaning a timeout
- * of 500 leaves us a very comfortable margin for error. The loop to which
- * the timeout applies takes at least 4 instructions per iteration so the
- * timeout is enough to take us up to multi-GHz parts!
- */
-#define RNG_TIMEOUT 500
-
struct stm32_rng_private {
struct hwrng rng;
void __iomem *base;
struct clk *clk;
+ struct reset_control *rst;
+ bool ced;
};
static int stm32_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
@@ -59,13 +55,16 @@ static int stm32_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
while (max > sizeof(u32)) {
sr = readl_relaxed(priv->base + RNG_SR);
+ /* Manage timeout which is based on timer and take */
+ /* care of initial delay time when enabling rng */
if (!sr && wait) {
- unsigned int timeout = RNG_TIMEOUT;
-
- do {
- cpu_relax();
- sr = readl_relaxed(priv->base + RNG_SR);
- } while (!sr && --timeout);
+ retval = readl_relaxed_poll_timeout_atomic(priv->base
+ + RNG_SR,
+ sr, sr,
+ 10, 50000);
+ if (retval)
+ dev_err((struct device *)priv->rng.priv,
+ "%s: timeout %x!\n", __func__, sr);
}
/* If error detected or data not ready... */
@@ -99,7 +98,11 @@ static int stm32_rng_init(struct hwrng *rng)
if (err)
return err;
- writel_relaxed(RNG_CR_RNGEN, priv->base + RNG_CR);
+ if (priv->ced)
+ writel_relaxed(RNG_CR_RNGEN, priv->base + RNG_CR);
+ else
+ writel_relaxed(RNG_CR_RNGEN | RNG_CR_CED,
+ priv->base + RNG_CR);
/* clear error indicators */
writel_relaxed(0, priv->base + RNG_SR);
@@ -140,6 +143,15 @@ static int stm32_rng_probe(struct platform_device *ofdev)
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
+ priv->rst = devm_reset_control_get(&ofdev->dev, NULL);
+ if (!IS_ERR(priv->rst)) {
+ reset_control_assert(priv->rst);
+ udelay(2);
+ reset_control_deassert(priv->rst);
+ }
+
+ priv->ced = of_property_read_bool(np, "clock-error-detect");
+
dev_set_drvdata(dev, priv);
priv->rng.name = dev_driver_string(dev),
diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig
index 3544abc0f9f9..3bda116c8aa0 100644
--- a/drivers/char/ipmi/Kconfig
+++ b/drivers/char/ipmi/Kconfig
@@ -96,6 +96,21 @@ config IPMI_POWEROFF
endif # IPMI_HANDLER
+config IPMI_KCS_BMC
+ tristate
+
+config ASPEED_KCS_IPMI_BMC
+ depends on ARCH_ASPEED || COMPILE_TEST
+ select IPMI_KCS_BMC
+ select REGMAP_MMIO
+ tristate "Aspeed KCS IPMI BMC driver"
+ help
+ Provides a driver for the KCS (Keyboard Controller Style) IPMI
+ interface found on Aspeed SOCs (AST2400 and AST2500).
+
+ The driver implements the BMC side of the KCS contorller, it
+ provides the access of KCS IO space for BMC side.
+
config ASPEED_BT_IPMI_BMC
depends on ARCH_ASPEED || COMPILE_TEST
depends on REGMAP && REGMAP_MMIO && MFD_SYSCON
diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile
index 33b899fcf14a..21e9e872d973 100644
--- a/drivers/char/ipmi/Makefile
+++ b/drivers/char/ipmi/Makefile
@@ -21,4 +21,6 @@ obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o
obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o
obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o
+obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o
obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o
+obj-$(CONFIG_ASPEED_KCS_IPMI_BMC) += kcs_bmc_aspeed.o
diff --git a/drivers/char/ipmi/bt-bmc.c b/drivers/char/ipmi/bt-bmc.c
index c95b93b7598b..40b9927c072c 100644
--- a/drivers/char/ipmi/bt-bmc.c
+++ b/drivers/char/ipmi/bt-bmc.c
@@ -1,10 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2015-2016, IBM Corporation.
- *
- * 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/atomic.h>
diff --git a/drivers/char/ipmi/ipmi_bt_sm.c b/drivers/char/ipmi/ipmi_bt_sm.c
index feafdab734ae..fd4ea8d87d4b 100644
--- a/drivers/char/ipmi/ipmi_bt_sm.c
+++ b/drivers/char/ipmi/ipmi_bt_sm.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* ipmi_bt_sm.c
*
@@ -5,26 +6,7 @@
* of the driver architecture at http://sourceforge.net/projects/openipmi
*
* Author: Rocky Craig <first.last@hp.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 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. */
+ */
#include <linux/kernel.h> /* For printk. */
#include <linux/string.h>
diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c
index 5f1bc9174735..8ecfd47806fa 100644
--- a/drivers/char/ipmi/ipmi_devintf.c
+++ b/drivers/char/ipmi/ipmi_devintf.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* ipmi_devintf.c
*
@@ -8,27 +9,6 @@
* source@mvista.com
*
* Copyright 2002 MontaVista Software Inc.
- *
- * 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.
*/
#include <linux/module.h>
diff --git a/drivers/char/ipmi/ipmi_dmi.c b/drivers/char/ipmi/ipmi_dmi.c
index c5112b17d7ea..e2c143861b1e 100644
--- a/drivers/char/ipmi/ipmi_dmi.c
+++ b/drivers/char/ipmi/ipmi_dmi.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0+
/*
* A hack to create a platform device from a DMI entry. This will
* allow autoloading of the IPMI drive based on SMBIOS entries.
@@ -29,15 +29,6 @@ static struct ipmi_dmi_info *ipmi_dmi_infos;
static int ipmi_dmi_nr __initdata;
-#define set_prop_entry(_p_, _name_, type, val) \
-do { \
- struct property_entry *_p = &_p_; \
- _p->name = _name_; \
- _p->length = sizeof(type); \
- _p->is_string = false; \
- _p->value.type##_data = val; \
-} while(0)
-
static void __init dmi_add_platform_ipmi(unsigned long base_addr,
u32 flags,
u8 slave_addr,
@@ -85,9 +76,10 @@ static void __init dmi_add_platform_ipmi(unsigned long base_addr,
}
if (si_type != SI_TYPE_INVALID)
- set_prop_entry(p[pidx++], "ipmi-type", u8, si_type);
- set_prop_entry(p[pidx++], "slave-addr", u8, slave_addr);
- set_prop_entry(p[pidx++], "addr-source", u8, SI_SMBIOS);
+ p[pidx++] = PROPERTY_ENTRY_U8("ipmi-type", si_type);
+
+ p[pidx++] = PROPERTY_ENTRY_U8("slave-addr", slave_addr);
+ p[pidx++] = PROPERTY_ENTRY_U8("addr-source", SI_SMBIOS);
info = kmalloc(sizeof(*info), GFP_KERNEL);
if (!info) {
@@ -112,7 +104,7 @@ static void __init dmi_add_platform_ipmi(unsigned long base_addr,
goto err;
if (type == IPMI_DMI_TYPE_SSIF) {
- set_prop_entry(p[pidx++], "i2c-addr", u16, base_addr);
+ p[pidx++] = PROPERTY_ENTRY_U16("i2c-addr", base_addr);
goto add_properties;
}
diff --git a/drivers/char/ipmi/ipmi_dmi.h b/drivers/char/ipmi/ipmi_dmi.h
index 6c21018e3668..8d2b094db8e6 100644
--- a/drivers/char/ipmi/ipmi_dmi.h
+++ b/drivers/char/ipmi/ipmi_dmi.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* DMI defines for use by IPMI
*/
diff --git a/drivers/char/ipmi/ipmi_kcs_sm.c b/drivers/char/ipmi/ipmi_kcs_sm.c
index 1da61af7f576..f4ea9f47230a 100644
--- a/drivers/char/ipmi/ipmi_kcs_sm.c
+++ b/drivers/char/ipmi/ipmi_kcs_sm.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* ipmi_kcs_sm.c
*
@@ -8,27 +9,6 @@
* source@mvista.com
*
* Copyright 2002 MontaVista Software Inc.
- *
- * 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.
*/
/*
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
index e0b0d7e2d976..361148938801 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* ipmi_msghandler.c
*
@@ -8,27 +9,6 @@
* source@mvista.com
*
* Copyright 2002 MontaVista Software Inc.
- *
- * 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.
*/
#include <linux/module.h>
diff --git a/drivers/char/ipmi/ipmi_powernv.c b/drivers/char/ipmi/ipmi_powernv.c
index bcf493d8e238..e96500372ce2 100644
--- a/drivers/char/ipmi/ipmi_powernv.c
+++ b/drivers/char/ipmi/ipmi_powernv.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* PowerNV OPAL IPMI driver
*
* Copyright 2014 IBM Corp.
- *
- * 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.
*/
#define pr_fmt(fmt) "ipmi-powernv: " fmt
diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c
index 38e6af1c8e38..7996337852f2 100644
--- a/drivers/char/ipmi/ipmi_poweroff.c
+++ b/drivers/char/ipmi/ipmi_poweroff.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* ipmi_poweroff.c
*
@@ -9,27 +10,6 @@
* source@mvista.com
*
* Copyright 2002,2004 MontaVista Software Inc.
- *
- * 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.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -457,6 +437,24 @@ static int ipmi_dell_chassis_detect(ipmi_user_t user)
}
/*
+ * ipmi_hp_chassis_detect()
+ * HP PA-RISC servers rp3410/rp3440, the C8000 workstation and the rx2600 and
+ * zx6000 machines support IPMI vers 1 and don't set the chassis capability bit
+ * but they can handle a chassis poweroff or powercycle command.
+ */
+
+#define HP_IANA_MFR_ID 0x0b
+#define HP_BMC_PROD_ID 0x8201
+static int ipmi_hp_chassis_detect(ipmi_user_t user)
+{
+ if (mfg_id == HP_IANA_MFR_ID
+ && prod_id == HP_BMC_PROD_ID
+ && ipmi_version == 1)
+ return 1;
+ return 0;
+}
+
+/*
* Standard chassis support
*/
@@ -533,14 +531,16 @@ static struct poweroff_function poweroff_functions[] = {
{ .platform_type = "chassis",
.detect = ipmi_dell_chassis_detect,
.poweroff_func = ipmi_poweroff_chassis },
+ { .platform_type = "chassis",
+ .detect = ipmi_hp_chassis_detect,
+ .poweroff_func = ipmi_poweroff_chassis },
/* Chassis should generally be last, other things should override
it. */
{ .platform_type = "chassis",
.detect = ipmi_chassis_detect,
.poweroff_func = ipmi_poweroff_chassis },
};
-#define NUM_PO_FUNCS (sizeof(poweroff_functions) \
- / sizeof(struct poweroff_function))
+#define NUM_PO_FUNCS ARRAY_SIZE(poweroff_functions)
/* Called on a powerdown request. */
diff --git a/drivers/char/ipmi/ipmi_si.h b/drivers/char/ipmi/ipmi_si.h
index 17ce5f7b89ab..52f6152d1fcb 100644
--- a/drivers/char/ipmi/ipmi_si.h
+++ b/drivers/char/ipmi/ipmi_si.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* ipmi_si.h
*
diff --git a/drivers/char/ipmi/ipmi_si_hardcode.c b/drivers/char/ipmi/ipmi_si_hardcode.c
index fa9a4780de36..10219f24546b 100644
--- a/drivers/char/ipmi/ipmi_si_hardcode.c
+++ b/drivers/char/ipmi/ipmi_si_hardcode.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
#include <linux/moduleparam.h>
#include "ipmi_si.h"
diff --git a/drivers/char/ipmi/ipmi_si_hotmod.c b/drivers/char/ipmi/ipmi_si_hotmod.c
index fc03b9be2f3d..a98ca42a50b1 100644
--- a/drivers/char/ipmi/ipmi_si_hotmod.c
+++ b/drivers/char/ipmi/ipmi_si_hotmod.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* ipmi_si_hotmod.c
*
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index 6768cb2dd740..ff870aa91cfe 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* ipmi_si.c
*
@@ -10,27 +11,6 @@
*
* Copyright 2002 MontaVista Software Inc.
* Copyright 2006 IBM Corp., Christian Krafft <krafft@de.ibm.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 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.
*/
/*
@@ -252,6 +232,12 @@ struct smi_info {
/* Default driver model device. */
struct platform_device *pdev;
+ /* Have we added the device group to the device? */
+ bool dev_group_added;
+
+ /* Have we added the platform device? */
+ bool pdev_registered;
+
/* Counters and things for the proc filesystem. */
atomic_t stats[SI_NUM_STATS];
@@ -275,7 +261,8 @@ static int num_max_busy_us;
static bool unload_when_empty = true;
static int try_smi_init(struct smi_info *smi);
-static void cleanup_one_si(struct smi_info *to_clean);
+static void shutdown_one_si(struct smi_info *smi_info);
+static void cleanup_one_si(struct smi_info *smi_info);
static void cleanup_ipmi_si(void);
#ifdef DEBUG_TIMING
@@ -2017,18 +2004,13 @@ int ipmi_si_add_smi(struct si_sm_io *io)
ipmi_addr_src_to_str(new_smi->io.addr_source),
si_to_str[new_smi->io.si_type]);
- /* So we know not to free it unless we have allocated one. */
- new_smi->intf = NULL;
- new_smi->si_sm = NULL;
- new_smi->handlers = NULL;
-
list_add_tail(&new_smi->link, &smi_infos);
if (initialized) {
rv = try_smi_init(new_smi);
if (rv) {
- mutex_unlock(&smi_infos_lock);
cleanup_one_si(new_smi);
+ mutex_unlock(&smi_infos_lock);
return rv;
}
}
@@ -2047,7 +2029,6 @@ static int try_smi_init(struct smi_info *new_smi)
int rv = 0;
int i;
char *init_name = NULL;
- bool platform_device_registered = false;
pr_info(PFX "Trying %s-specified %s state machine at %s address 0x%lx, slave address 0x%x, irq %d\n",
ipmi_addr_src_to_str(new_smi->io.addr_source),
@@ -2090,6 +2071,7 @@ static int try_smi_init(struct smi_info *new_smi)
new_smi->intf_num);
if (!new_smi->pdev) {
pr_err(PFX "Unable to allocate platform device\n");
+ rv = -ENOMEM;
goto out_err;
}
new_smi->io.dev = &new_smi->pdev->dev;
@@ -2168,7 +2150,7 @@ static int try_smi_init(struct smi_info *new_smi)
atomic_set(&new_smi->req_events, 1);
}
- if (new_smi->pdev) {
+ if (new_smi->pdev && !new_smi->pdev_registered) {
rv = platform_device_add(new_smi->pdev);
if (rv) {
dev_err(new_smi->io.dev,
@@ -2176,7 +2158,7 @@ static int try_smi_init(struct smi_info *new_smi)
rv);
goto out_err;
}
- platform_device_registered = true;
+ new_smi->pdev_registered = true;
}
dev_set_drvdata(new_smi->io.dev, new_smi);
@@ -2185,8 +2167,9 @@ static int try_smi_init(struct smi_info *new_smi)
dev_err(new_smi->io.dev,
"Unable to add device attributes: error %d\n",
rv);
- goto out_err_stop_timer;
+ goto out_err;
}
+ new_smi->dev_group_added = true;
rv = ipmi_register_smi(&handlers,
new_smi,
@@ -2196,7 +2179,7 @@ static int try_smi_init(struct smi_info *new_smi)
dev_err(new_smi->io.dev,
"Unable to register device: error %d\n",
rv);
- goto out_err_remove_attrs;
+ goto out_err;
}
#ifdef CONFIG_IPMI_PROC_INTERFACE
@@ -2206,7 +2189,7 @@ static int try_smi_init(struct smi_info *new_smi)
if (rv) {
dev_err(new_smi->io.dev,
"Unable to create proc entry: %d\n", rv);
- goto out_err_stop_timer;
+ goto out_err;
}
rv = ipmi_smi_add_proc_entry(new_smi->intf, "si_stats",
@@ -2215,7 +2198,7 @@ static int try_smi_init(struct smi_info *new_smi)
if (rv) {
dev_err(new_smi->io.dev,
"Unable to create proc entry: %d\n", rv);
- goto out_err_stop_timer;
+ goto out_err;
}
rv = ipmi_smi_add_proc_entry(new_smi->intf, "params",
@@ -2224,7 +2207,7 @@ static int try_smi_init(struct smi_info *new_smi)
if (rv) {
dev_err(new_smi->io.dev,
"Unable to create proc entry: %d\n", rv);
- goto out_err_stop_timer;
+ goto out_err;
}
#endif
@@ -2239,56 +2222,8 @@ static int try_smi_init(struct smi_info *new_smi)
return 0;
-out_err_remove_attrs:
- device_remove_group(new_smi->io.dev, &ipmi_si_dev_attr_group);
- dev_set_drvdata(new_smi->io.dev, NULL);
-
-out_err_stop_timer:
- stop_timer_and_thread(new_smi);
-
out_err:
- new_smi->interrupt_disabled = true;
-
- if (new_smi->intf) {
- ipmi_smi_t intf = new_smi->intf;
- new_smi->intf = NULL;
- ipmi_unregister_smi(intf);
- }
-
- if (new_smi->io.irq_cleanup) {
- new_smi->io.irq_cleanup(&new_smi->io);
- new_smi->io.irq_cleanup = NULL;
- }
-
- /*
- * Wait until we know that we are out of any interrupt
- * handlers might have been running before we freed the
- * interrupt.
- */
- synchronize_sched();
-
- if (new_smi->si_sm) {
- if (new_smi->handlers)
- new_smi->handlers->cleanup(new_smi->si_sm);
- kfree(new_smi->si_sm);
- new_smi->si_sm = NULL;
- }
- if (new_smi->io.addr_source_cleanup) {
- new_smi->io.addr_source_cleanup(&new_smi->io);
- new_smi->io.addr_source_cleanup = NULL;
- }
- if (new_smi->io.io_cleanup) {
- new_smi->io.io_cleanup(&new_smi->io);
- new_smi->io.io_cleanup = NULL;
- }
-
- if (new_smi->pdev) {
- if (platform_device_registered)
- platform_device_unregister(new_smi->pdev);
- else
- platform_device_put(new_smi->pdev);
- new_smi->pdev = NULL;
- }
+ shutdown_one_si(new_smi);
kfree(init_name);
@@ -2366,17 +2301,14 @@ skip_fallback_noirq:
}
module_init(init_ipmi_si);
-static void cleanup_one_si(struct smi_info *to_clean)
+static void shutdown_one_si(struct smi_info *smi_info)
{
int rv = 0;
- if (!to_clean)
- return;
-
- if (to_clean->intf) {
- ipmi_smi_t intf = to_clean->intf;
+ if (smi_info->intf) {
+ ipmi_smi_t intf = smi_info->intf;
- to_clean->intf = NULL;
+ smi_info->intf = NULL;
rv = ipmi_unregister_smi(intf);
if (rv) {
pr_err(PFX "Unable to unregister device: errno=%d\n",
@@ -2384,49 +2316,79 @@ static void cleanup_one_si(struct smi_info *to_clean)
}
}
- device_remove_group(to_clean->io.dev, &ipmi_si_dev_attr_group);
- dev_set_drvdata(to_clean->io.dev, NULL);
-
- list_del(&to_clean->link);
+ if (smi_info->dev_group_added) {
+ device_remove_group(smi_info->io.dev, &ipmi_si_dev_attr_group);
+ smi_info->dev_group_added = false;
+ }
+ if (smi_info->io.dev)
+ dev_set_drvdata(smi_info->io.dev, NULL);
/*
* Make sure that interrupts, the timer and the thread are
* stopped and will not run again.
*/
- if (to_clean->io.irq_cleanup)
- to_clean->io.irq_cleanup(&to_clean->io);
- stop_timer_and_thread(to_clean);
+ smi_info->interrupt_disabled = true;
+ if (smi_info->io.irq_cleanup) {
+ smi_info->io.irq_cleanup(&smi_info->io);
+ smi_info->io.irq_cleanup = NULL;
+ }
+ stop_timer_and_thread(smi_info);
+
+ /*
+ * Wait until we know that we are out of any interrupt
+ * handlers might have been running before we freed the
+ * interrupt.
+ */
+ synchronize_sched();
/*
* Timeouts are stopped, now make sure the interrupts are off
* in the BMC. Note that timers and CPU interrupts are off,
* so no need for locks.
*/
- while (to_clean->curr_msg || (to_clean->si_state != SI_NORMAL)) {
- poll(to_clean);
+ while (smi_info->curr_msg || (smi_info->si_state != SI_NORMAL)) {
+ poll(smi_info);
schedule_timeout_uninterruptible(1);
}
- if (to_clean->handlers)
- disable_si_irq(to_clean);
- while (to_clean->curr_msg || (to_clean->si_state != SI_NORMAL)) {
- poll(to_clean);
+ if (smi_info->handlers)
+ disable_si_irq(smi_info);
+ while (smi_info->curr_msg || (smi_info->si_state != SI_NORMAL)) {
+ poll(smi_info);
schedule_timeout_uninterruptible(1);
}
+ if (smi_info->handlers)
+ smi_info->handlers->cleanup(smi_info->si_sm);
+
+ if (smi_info->io.addr_source_cleanup) {
+ smi_info->io.addr_source_cleanup(&smi_info->io);
+ smi_info->io.addr_source_cleanup = NULL;
+ }
+ if (smi_info->io.io_cleanup) {
+ smi_info->io.io_cleanup(&smi_info->io);
+ smi_info->io.io_cleanup = NULL;
+ }
- if (to_clean->handlers)
- to_clean->handlers->cleanup(to_clean->si_sm);
+ kfree(smi_info->si_sm);
+ smi_info->si_sm = NULL;
+}
- kfree(to_clean->si_sm);
+static void cleanup_one_si(struct smi_info *smi_info)
+{
+ if (!smi_info)
+ return;
- if (to_clean->io.addr_source_cleanup)
- to_clean->io.addr_source_cleanup(&to_clean->io);
- if (to_clean->io.io_cleanup)
- to_clean->io.io_cleanup(&to_clean->io);
+ list_del(&smi_info->link);
- if (to_clean->pdev)
- platform_device_unregister(to_clean->pdev);
+ shutdown_one_si(smi_info);
+
+ if (smi_info->pdev) {
+ if (smi_info->pdev_registered)
+ platform_device_unregister(smi_info->pdev);
+ else
+ platform_device_put(smi_info->pdev);
+ }
- kfree(to_clean);
+ kfree(smi_info);
}
int ipmi_si_remove_by_dev(struct device *dev)
diff --git a/drivers/char/ipmi/ipmi_si_mem_io.c b/drivers/char/ipmi/ipmi_si_mem_io.c
index 8796396ecd0f..1b869d530884 100644
--- a/drivers/char/ipmi/ipmi_si_mem_io.c
+++ b/drivers/char/ipmi/ipmi_si_mem_io.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
#include <linux/io.h>
#include "ipmi_si.h"
diff --git a/drivers/char/ipmi/ipmi_si_parisc.c b/drivers/char/ipmi/ipmi_si_parisc.c
index 6b10f0e18a95..f3c99820f564 100644
--- a/drivers/char/ipmi/ipmi_si_parisc.c
+++ b/drivers/char/ipmi/ipmi_si_parisc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
#include <linux/module.h>
#include <asm/hardware.h> /* for register_parisc_driver() stuff */
diff --git a/drivers/char/ipmi/ipmi_si_pci.c b/drivers/char/ipmi/ipmi_si_pci.c
index 27dd11c49d21..f54ca6869ed2 100644
--- a/drivers/char/ipmi/ipmi_si_pci.c
+++ b/drivers/char/ipmi/ipmi_si_pci.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* ipmi_si_pci.c
*
@@ -17,16 +18,12 @@ module_param_named(trypci, si_trypci, bool, 0);
MODULE_PARM_DESC(trypci, "Setting this to zero will disable the"
" default scan of the interfaces identified via pci");
-#define PCI_ERMC_CLASSCODE 0x0C0700
-#define PCI_ERMC_CLASSCODE_MASK 0xffffff00
-#define PCI_ERMC_CLASSCODE_TYPE_MASK 0xff
-#define PCI_ERMC_CLASSCODE_TYPE_SMIC 0x00
-#define PCI_ERMC_CLASSCODE_TYPE_KCS 0x01
-#define PCI_ERMC_CLASSCODE_TYPE_BT 0x02
+#define PCI_CLASS_SERIAL_IPMI 0x0c07
+#define PCI_CLASS_SERIAL_IPMI_SMIC 0x0c0700
+#define PCI_CLASS_SERIAL_IPMI_KCS 0x0c0701
+#define PCI_CLASS_SERIAL_IPMI_BT 0x0c0702
-#define PCI_HP_VENDOR_ID 0x103C
-#define PCI_MMC_DEVICE_ID 0x121A
-#define PCI_MMC_ADDR_CW 0x10
+#define PCI_DEVICE_ID_HP_MMC 0x121A
static void ipmi_pci_cleanup(struct si_sm_io *io)
{
@@ -65,32 +62,43 @@ static int ipmi_pci_probe_regspacing(struct si_sm_io *io)
return DEFAULT_REGSPACING;
}
+static struct pci_device_id ipmi_pci_blacklist[] = {
+ /*
+ * This is a "Virtual IPMI device", whatever that is. It appears
+ * as a KCS device by the class, but it is not one.
+ */
+ { PCI_VDEVICE(REALTEK, 0x816c) },
+ { 0, }
+};
+
static int ipmi_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
int rv;
- int class_type = pdev->class & PCI_ERMC_CLASSCODE_TYPE_MASK;
struct si_sm_io io;
+ if (pci_match_id(ipmi_pci_blacklist, pdev))
+ return -ENODEV;
+
memset(&io, 0, sizeof(io));
io.addr_source = SI_PCI;
dev_info(&pdev->dev, "probing via PCI");
- switch (class_type) {
- case PCI_ERMC_CLASSCODE_TYPE_SMIC:
+ switch (pdev->class) {
+ case PCI_CLASS_SERIAL_IPMI_SMIC:
io.si_type = SI_SMIC;
break;
- case PCI_ERMC_CLASSCODE_TYPE_KCS:
+ case PCI_CLASS_SERIAL_IPMI_KCS:
io.si_type = SI_KCS;
break;
- case PCI_ERMC_CLASSCODE_TYPE_BT:
+ case PCI_CLASS_SERIAL_IPMI_BT:
io.si_type = SI_BT;
break;
default:
- dev_info(&pdev->dev, "Unknown IPMI type: %d\n", class_type);
+ dev_info(&pdev->dev, "Unknown IPMI class: %x\n", pdev->class);
return -ENOMEM;
}
@@ -138,8 +146,10 @@ static void ipmi_pci_remove(struct pci_dev *pdev)
}
static const struct pci_device_id ipmi_pci_devices[] = {
- { PCI_DEVICE(PCI_HP_VENDOR_ID, PCI_MMC_DEVICE_ID) },
- { PCI_DEVICE_CLASS(PCI_ERMC_CLASSCODE, PCI_ERMC_CLASSCODE_MASK) },
+ { PCI_VDEVICE(HP, PCI_DEVICE_ID_HP_MMC) },
+ { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_IPMI_SMIC, ~0) },
+ { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_IPMI_KCS, ~0) },
+ { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_IPMI_BT, ~0) },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, ipmi_pci_devices);
diff --git a/drivers/char/ipmi/ipmi_si_platform.c b/drivers/char/ipmi/ipmi_si_platform.c
index f4214870d726..bf69927502bd 100644
--- a/drivers/char/ipmi/ipmi_si_platform.c
+++ b/drivers/char/ipmi/ipmi_si_platform.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* ipmi_si_platform.c
*
@@ -50,14 +51,6 @@ MODULE_PARM_DESC(trydmi, "Setting this to zero will disable the"
#endif
#ifdef CONFIG_ACPI
-
-/*
- * Once we get an ACPI failure, we don't try any more, because we go
- * through the tables sequentially. Once we don't find a table, there
- * are no more.
- */
-static int acpi_failure;
-
/* For GPE-type interrupts. */
static u32 ipmi_acpi_gpe(acpi_handle gpe_device,
u32 gpe_number, void *context)
@@ -102,146 +95,6 @@ static int acpi_gpe_irq_setup(struct si_sm_io *io)
return 0;
}
}
-
-/*
- * Defined at
- * http://h21007.www2.hp.com/portal/download/files/unprot/hpspmi.pdf
- */
-struct SPMITable {
- s8 Signature[4];
- u32 Length;
- u8 Revision;
- u8 Checksum;
- s8 OEMID[6];
- s8 OEMTableID[8];
- s8 OEMRevision[4];
- s8 CreatorID[4];
- s8 CreatorRevision[4];
- u8 InterfaceType;
- u8 IPMIlegacy;
- s16 SpecificationRevision;
-
- /*
- * Bit 0 - SCI interrupt supported
- * Bit 1 - I/O APIC/SAPIC
- */
- u8 InterruptType;
-
- /*
- * If bit 0 of InterruptType is set, then this is the SCI
- * interrupt in the GPEx_STS register.
- */
- u8 GPE;
-
- s16 Reserved;
-
- /*
- * If bit 1 of InterruptType is set, then this is the I/O
- * APIC/SAPIC interrupt.
- */
- u32 GlobalSystemInterrupt;
-
- /* The actual register address. */
- struct acpi_generic_address addr;
-
- u8 UID[4];
-
- s8 spmi_id[1]; /* A '\0' terminated array starts here. */
-};
-
-static int try_init_spmi(struct SPMITable *spmi)
-{
- struct si_sm_io io;
-
- if (spmi->IPMIlegacy != 1) {
- pr_info(PFX "Bad SPMI legacy %d\n", spmi->IPMIlegacy);
- return -ENODEV;
- }
-
- memset(&io, 0, sizeof(io));
- io.addr_source = SI_SPMI;
- pr_info(PFX "probing via SPMI\n");
-
- /* Figure out the interface type. */
- switch (spmi->InterfaceType) {
- case 1: /* KCS */
- io.si_type = SI_KCS;
- break;
- case 2: /* SMIC */
- io.si_type = SI_SMIC;
- break;
- case 3: /* BT */
- io.si_type = SI_BT;
- break;
- case 4: /* SSIF, just ignore */
- return -EIO;
- default:
- pr_info(PFX "Unknown ACPI/SPMI SI type %d\n",
- spmi->InterfaceType);
- return -EIO;
- }
-
- if (spmi->InterruptType & 1) {
- /* We've got a GPE interrupt. */
- io.irq = spmi->GPE;
- io.irq_setup = acpi_gpe_irq_setup;
- } else if (spmi->InterruptType & 2) {
- /* We've got an APIC/SAPIC interrupt. */
- io.irq = spmi->GlobalSystemInterrupt;
- io.irq_setup = ipmi_std_irq_setup;
- } else {
- /* Use the default interrupt setting. */
- io.irq = 0;
- io.irq_setup = NULL;
- }
-
- if (spmi->addr.bit_width) {
- /* A (hopefully) properly formed register bit width. */
- io.regspacing = spmi->addr.bit_width / 8;
- } else {
- io.regspacing = DEFAULT_REGSPACING;
- }
- io.regsize = io.regspacing;
- io.regshift = spmi->addr.bit_offset;
-
- if (spmi->addr.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
- io.addr_type = IPMI_MEM_ADDR_SPACE;
- } else if (spmi->addr.space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
- io.addr_type = IPMI_IO_ADDR_SPACE;
- } else {
- pr_warn(PFX "Unknown ACPI I/O Address type\n");
- return -EIO;
- }
- io.addr_data = spmi->addr.address;
-
- pr_info("ipmi_si: SPMI: %s %#lx regsize %d spacing %d irq %d\n",
- (io.addr_type == IPMI_IO_ADDR_SPACE) ? "io" : "mem",
- io.addr_data, io.regsize, io.regspacing, io.irq);
-
- return ipmi_si_add_smi(&io);
-}
-
-static void spmi_find_bmc(void)
-{
- acpi_status status;
- struct SPMITable *spmi;
- int i;
-
- if (acpi_disabled)
- return;
-
- if (acpi_failure)
- return;
-
- for (i = 0; ; i++) {
- status = acpi_get_table(ACPI_SIG_SPMI, i+1,
- (struct acpi_table_header **)&spmi);
- if (status != AE_OK)
- return;
-
- try_init_spmi(spmi);
- }
-}
#endif
static struct resource *
@@ -579,12 +432,6 @@ void ipmi_si_platform_init(void)
int rv = platform_driver_register(&ipmi_platform_driver);
if (rv)
pr_err(PFX "Unable to register driver: %d\n", rv);
-
-#ifdef CONFIG_ACPI
- if (si_tryacpi)
- spmi_find_bmc();
-#endif
-
}
void ipmi_si_platform_shutdown(void)
diff --git a/drivers/char/ipmi/ipmi_si_port_io.c b/drivers/char/ipmi/ipmi_si_port_io.c
index e5ce174fbeeb..ef6dffcea9fa 100644
--- a/drivers/char/ipmi/ipmi_si_port_io.c
+++ b/drivers/char/ipmi/ipmi_si_port_io.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
#include <linux/io.h>
#include "ipmi_si.h"
diff --git a/drivers/char/ipmi/ipmi_si_sm.h b/drivers/char/ipmi/ipmi_si_sm.h
index aa8d88ab4433..aaddf047d923 100644
--- a/drivers/char/ipmi/ipmi_si_sm.h
+++ b/drivers/char/ipmi/ipmi_si_sm.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* ipmi_si_sm.h
*
@@ -11,27 +12,6 @@
* source@mvista.com
*
* Copyright 2002 MontaVista Software Inc.
- *
- * 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.
*/
#include <linux/ipmi.h>
diff --git a/drivers/char/ipmi/ipmi_smic_sm.c b/drivers/char/ipmi/ipmi_smic_sm.c
index 8f7c73ff58f2..466a5aac5298 100644
--- a/drivers/char/ipmi/ipmi_smic_sm.c
+++ b/drivers/char/ipmi/ipmi_smic_sm.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* ipmi_smic_sm.c
*
@@ -18,28 +19,7 @@
* copyright notice:
* (c) Copyright 2001 Grant Grundler (c) Copyright
* 2001 Hewlett-Packard Company
- *
- *
- * 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. */
+ */
#include <linux/kernel.h> /* For printk. */
#include <linux/string.h>
diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c
index f929e72bdac8..35a82f4bfd78 100644
--- a/drivers/char/ipmi/ipmi_ssif.c
+++ b/drivers/char/ipmi/ipmi_ssif.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* ipmi_ssif.c
*
@@ -13,11 +14,6 @@
*
* Copyright 2003 Intel Corporation
* Copyright 2005 MontaVista Software
- *
- * 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.
*/
/*
@@ -761,7 +757,7 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
ssif_info->ssif_state = SSIF_NORMAL;
ipmi_ssif_unlock_cond(ssif_info, flags);
pr_warn(PFX "Error getting flags: %d %d, %x\n",
- result, len, data[2]);
+ result, len, (len >= 3) ? data[2] : 0);
} else if (data[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2
|| data[1] != IPMI_GET_MSG_FLAGS_CMD) {
/*
@@ -783,7 +779,7 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
if ((result < 0) || (len < 3) || (data[2] != 0)) {
/* Error clearing flags */
pr_warn(PFX "Error clearing flags: %d %d, %x\n",
- result, len, data[2]);
+ result, len, (len >= 3) ? data[2] : 0);
} else if (data[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2
|| data[1] != IPMI_CLEAR_MSG_FLAGS_CMD) {
pr_warn(PFX "Invalid response clearing flags: %x %x\n",
@@ -1906,108 +1902,6 @@ static const struct acpi_device_id ssif_acpi_match[] = {
{ },
};
MODULE_DEVICE_TABLE(acpi, ssif_acpi_match);
-
-/*
- * Once we get an ACPI failure, we don't try any more, because we go
- * through the tables sequentially. Once we don't find a table, there
- * are no more.
- */
-static int acpi_failure;
-
-/*
- * Defined in the IPMI 2.0 spec.
- */
-struct SPMITable {
- s8 Signature[4];
- u32 Length;
- u8 Revision;
- u8 Checksum;
- s8 OEMID[6];
- s8 OEMTableID[8];
- s8 OEMRevision[4];
- s8 CreatorID[4];
- s8 CreatorRevision[4];
- u8 InterfaceType;
- u8 IPMIlegacy;
- s16 SpecificationRevision;
-
- /*
- * Bit 0 - SCI interrupt supported
- * Bit 1 - I/O APIC/SAPIC
- */
- u8 InterruptType;
-
- /*
- * If bit 0 of InterruptType is set, then this is the SCI
- * interrupt in the GPEx_STS register.
- */
- u8 GPE;
-
- s16 Reserved;
-
- /*
- * If bit 1 of InterruptType is set, then this is the I/O
- * APIC/SAPIC interrupt.
- */
- u32 GlobalSystemInterrupt;
-
- /* The actual register address. */
- struct acpi_generic_address addr;
-
- u8 UID[4];
-
- s8 spmi_id[1]; /* A '\0' terminated array starts here. */
-};
-
-static int try_init_spmi(struct SPMITable *spmi)
-{
- unsigned short myaddr;
-
- if (num_addrs >= MAX_SSIF_BMCS)
- return -1;
-
- if (spmi->IPMIlegacy != 1) {
- pr_warn("IPMI: Bad SPMI legacy: %d\n", spmi->IPMIlegacy);
- return -ENODEV;
- }
-
- if (spmi->InterfaceType != 4)
- return -ENODEV;
-
- if (spmi->addr.space_id != ACPI_ADR_SPACE_SMBUS) {
- pr_warn(PFX "Invalid ACPI SSIF I/O Address type: %d\n",
- spmi->addr.space_id);
- return -EIO;
- }
-
- myaddr = spmi->addr.address & 0x7f;
-
- return new_ssif_client(myaddr, NULL, 0, 0, SI_SPMI, NULL);
-}
-
-static void spmi_find_bmc(void)
-{
- acpi_status status;
- struct SPMITable *spmi;
- int i;
-
- if (acpi_disabled)
- return;
-
- if (acpi_failure)
- return;
-
- for (i = 0; ; i++) {
- status = acpi_get_table(ACPI_SIG_SPMI, i+1,
- (struct acpi_table_header **)&spmi);
- if (status != AE_OK)
- return;
-
- try_init_spmi(spmi);
- }
-}
-#else
-static void spmi_find_bmc(void) { }
#endif
#ifdef CONFIG_DMI
@@ -2112,9 +2006,6 @@ static int init_ipmi_ssif(void)
ssif_i2c_driver.driver.acpi_match_table =
ACPI_PTR(ssif_acpi_match);
- if (ssif_tryacpi)
- spmi_find_bmc();
-
if (ssif_trydmi) {
rv = platform_driver_register(&ipmi_driver);
if (rv)
diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c
index a58acdcf7414..22bc287eac2d 100644
--- a/drivers/char/ipmi/ipmi_watchdog.c
+++ b/drivers/char/ipmi/ipmi_watchdog.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* ipmi_watchdog.c
*
@@ -8,27 +9,6 @@
* source@mvista.com
*
* Copyright 2002 MontaVista Software Inc.
- *
- * 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.
*/
#include <linux/module.h>
diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
new file mode 100644
index 000000000000..fbfc05e3f3d1
--- /dev/null
+++ b/drivers/char/ipmi/kcs_bmc.c
@@ -0,0 +1,467 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2015-2018, Intel Corporation.
+ */
+
+#define pr_fmt(fmt) "kcs-bmc: " fmt
+
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/ipmi_bmc.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include "kcs_bmc.h"
+
+#define KCS_MSG_BUFSIZ 1000
+
+#define KCS_ZERO_DATA 0
+
+
+/* IPMI 2.0 - Table 9-1, KCS Interface Status Register Bits */
+#define KCS_STATUS_STATE(state) (state << 6)
+#define KCS_STATUS_STATE_MASK GENMASK(7, 6)
+#define KCS_STATUS_CMD_DAT BIT(3)
+#define KCS_STATUS_SMS_ATN BIT(2)
+#define KCS_STATUS_IBF BIT(1)
+#define KCS_STATUS_OBF BIT(0)
+
+/* IPMI 2.0 - Table 9-2, KCS Interface State Bits */
+enum kcs_states {
+ IDLE_STATE = 0,
+ READ_STATE = 1,
+ WRITE_STATE = 2,
+ ERROR_STATE = 3,
+};
+
+/* IPMI 2.0 - Table 9-3, KCS Interface Control Codes */
+#define KCS_CMD_GET_STATUS_ABORT 0x60
+#define KCS_CMD_WRITE_START 0x61
+#define KCS_CMD_WRITE_END 0x62
+#define KCS_CMD_READ_BYTE 0x68
+
+static inline u8 read_data(struct kcs_bmc *kcs_bmc)
+{
+ return kcs_bmc->io_inputb(kcs_bmc, kcs_bmc->ioreg.idr);
+}
+
+static inline void write_data(struct kcs_bmc *kcs_bmc, u8 data)
+{
+ kcs_bmc->io_outputb(kcs_bmc, kcs_bmc->ioreg.odr, data);
+}
+
+static inline u8 read_status(struct kcs_bmc *kcs_bmc)
+{
+ return kcs_bmc->io_inputb(kcs_bmc, kcs_bmc->ioreg.str);
+}
+
+static inline void write_status(struct kcs_bmc *kcs_bmc, u8 data)
+{
+ kcs_bmc->io_outputb(kcs_bmc, kcs_bmc->ioreg.str, data);
+}
+
+static void update_status_bits(struct kcs_bmc *kcs_bmc, u8 mask, u8 val)
+{
+ u8 tmp = read_status(kcs_bmc);
+
+ tmp &= ~mask;
+ tmp |= val & mask;
+
+ write_status(kcs_bmc, tmp);
+}
+
+static inline void set_state(struct kcs_bmc *kcs_bmc, u8 state)
+{
+ update_status_bits(kcs_bmc, KCS_STATUS_STATE_MASK,
+ KCS_STATUS_STATE(state));
+}
+
+static void kcs_force_abort(struct kcs_bmc *kcs_bmc)
+{
+ set_state(kcs_bmc, ERROR_STATE);
+ read_data(kcs_bmc);
+ write_data(kcs_bmc, KCS_ZERO_DATA);
+
+ kcs_bmc->phase = KCS_PHASE_ERROR;
+ kcs_bmc->data_in_avail = false;
+ kcs_bmc->data_in_idx = 0;
+}
+
+static void kcs_bmc_handle_data(struct kcs_bmc *kcs_bmc)
+{
+ u8 data;
+
+ switch (kcs_bmc->phase) {
+ case KCS_PHASE_WRITE_START:
+ kcs_bmc->phase = KCS_PHASE_WRITE_DATA;
+ /* fall through */
+
+ case KCS_PHASE_WRITE_DATA:
+ if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) {
+ set_state(kcs_bmc, WRITE_STATE);
+ write_data(kcs_bmc, KCS_ZERO_DATA);
+ kcs_bmc->data_in[kcs_bmc->data_in_idx++] =
+ read_data(kcs_bmc);
+ } else {
+ kcs_force_abort(kcs_bmc);
+ kcs_bmc->error = KCS_LENGTH_ERROR;
+ }
+ break;
+
+ case KCS_PHASE_WRITE_END_CMD:
+ if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) {
+ set_state(kcs_bmc, READ_STATE);
+ kcs_bmc->data_in[kcs_bmc->data_in_idx++] =
+ read_data(kcs_bmc);
+ kcs_bmc->phase = KCS_PHASE_WRITE_DONE;
+ kcs_bmc->data_in_avail = true;
+ wake_up_interruptible(&kcs_bmc->queue);
+ } else {
+ kcs_force_abort(kcs_bmc);
+ kcs_bmc->error = KCS_LENGTH_ERROR;
+ }
+ break;
+
+ case KCS_PHASE_READ:
+ if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len)
+ set_state(kcs_bmc, IDLE_STATE);
+
+ data = read_data(kcs_bmc);
+ if (data != KCS_CMD_READ_BYTE) {
+ set_state(kcs_bmc, ERROR_STATE);
+ write_data(kcs_bmc, KCS_ZERO_DATA);
+ break;
+ }
+
+ if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len) {
+ write_data(kcs_bmc, KCS_ZERO_DATA);
+ kcs_bmc->phase = KCS_PHASE_IDLE;
+ break;
+ }
+
+ write_data(kcs_bmc,
+ kcs_bmc->data_out[kcs_bmc->data_out_idx++]);
+ break;
+
+ case KCS_PHASE_ABORT_ERROR1:
+ set_state(kcs_bmc, READ_STATE);
+ read_data(kcs_bmc);
+ write_data(kcs_bmc, kcs_bmc->error);
+ kcs_bmc->phase = KCS_PHASE_ABORT_ERROR2;
+ break;
+
+ case KCS_PHASE_ABORT_ERROR2:
+ set_state(kcs_bmc, IDLE_STATE);
+ read_data(kcs_bmc);
+ write_data(kcs_bmc, KCS_ZERO_DATA);
+ kcs_bmc->phase = KCS_PHASE_IDLE;
+ break;
+
+ default:
+ kcs_force_abort(kcs_bmc);
+ break;
+ }
+}
+
+static void kcs_bmc_handle_cmd(struct kcs_bmc *kcs_bmc)
+{
+ u8 cmd;
+
+ set_state(kcs_bmc, WRITE_STATE);
+ write_data(kcs_bmc, KCS_ZERO_DATA);
+
+ cmd = read_data(kcs_bmc);
+ switch (cmd) {
+ case KCS_CMD_WRITE_START:
+ kcs_bmc->phase = KCS_PHASE_WRITE_START;
+ kcs_bmc->error = KCS_NO_ERROR;
+ kcs_bmc->data_in_avail = false;
+ kcs_bmc->data_in_idx = 0;
+ break;
+
+ case KCS_CMD_WRITE_END:
+ if (kcs_bmc->phase != KCS_PHASE_WRITE_DATA) {
+ kcs_force_abort(kcs_bmc);
+ break;
+ }
+
+ kcs_bmc->phase = KCS_PHASE_WRITE_END_CMD;
+ break;
+
+ case KCS_CMD_GET_STATUS_ABORT:
+ if (kcs_bmc->error == KCS_NO_ERROR)
+ kcs_bmc->error = KCS_ABORTED_BY_COMMAND;
+
+ kcs_bmc->phase = KCS_PHASE_ABORT_ERROR1;
+ kcs_bmc->data_in_avail = false;
+ kcs_bmc->data_in_idx = 0;
+ break;
+
+ default:
+ kcs_force_abort(kcs_bmc);
+ kcs_bmc->error = KCS_ILLEGAL_CONTROL_CODE;
+ break;
+ }
+}
+
+int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc)
+{
+ unsigned long flags;
+ int ret = 0;
+ u8 status;
+
+ spin_lock_irqsave(&kcs_bmc->lock, flags);
+
+ if (!kcs_bmc->running) {
+ kcs_force_abort(kcs_bmc);
+ ret = -ENODEV;
+ goto out_unlock;
+ }
+
+ status = read_status(kcs_bmc) & (KCS_STATUS_IBF | KCS_STATUS_CMD_DAT);
+
+ switch (status) {
+ case KCS_STATUS_IBF | KCS_STATUS_CMD_DAT:
+ kcs_bmc_handle_cmd(kcs_bmc);
+ break;
+
+ case KCS_STATUS_IBF:
+ kcs_bmc_handle_data(kcs_bmc);
+ break;
+
+ default:
+ ret = -ENODATA;
+ break;
+ }
+
+out_unlock:
+ spin_unlock_irqrestore(&kcs_bmc->lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL(kcs_bmc_handle_event);
+
+static inline struct kcs_bmc *to_kcs_bmc(struct file *filp)
+{
+ return container_of(filp->private_data, struct kcs_bmc, miscdev);
+}
+
+static int kcs_bmc_open(struct inode *inode, struct file *filp)
+{
+ struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
+ int ret = 0;
+
+ spin_lock_irq(&kcs_bmc->lock);
+ if (!kcs_bmc->running)
+ kcs_bmc->running = 1;
+ else
+ ret = -EBUSY;
+ spin_unlock_irq(&kcs_bmc->lock);
+
+ return ret;
+}
+
+static __poll_t kcs_bmc_poll(struct file *filp, poll_table *wait)
+{
+ struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
+ __poll_t mask = 0;
+
+ poll_wait(filp, &kcs_bmc->queue, wait);
+
+ spin_lock_irq(&kcs_bmc->lock);
+ if (kcs_bmc->data_in_avail)
+ mask |= EPOLLIN;
+ spin_unlock_irq(&kcs_bmc->lock);
+
+ return mask;
+}
+
+static ssize_t kcs_bmc_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
+ bool data_avail;
+ size_t data_len;
+ ssize_t ret;
+
+ if (!(filp->f_flags & O_NONBLOCK))
+ wait_event_interruptible(kcs_bmc->queue,
+ kcs_bmc->data_in_avail);
+
+ mutex_lock(&kcs_bmc->mutex);
+
+ spin_lock_irq(&kcs_bmc->lock);
+ data_avail = kcs_bmc->data_in_avail;
+ if (data_avail) {
+ data_len = kcs_bmc->data_in_idx;
+ memcpy(kcs_bmc->kbuffer, kcs_bmc->data_in, data_len);
+ }
+ spin_unlock_irq(&kcs_bmc->lock);
+
+ if (!data_avail) {
+ ret = -EAGAIN;
+ goto out_unlock;
+ }
+
+ if (count < data_len) {
+ pr_err("channel=%u with too large data : %zu\n",
+ kcs_bmc->channel, data_len);
+
+ spin_lock_irq(&kcs_bmc->lock);
+ kcs_force_abort(kcs_bmc);
+ spin_unlock_irq(&kcs_bmc->lock);
+
+ ret = -EOVERFLOW;
+ goto out_unlock;
+ }
+
+ if (copy_to_user(buf, kcs_bmc->kbuffer, data_len)) {
+ ret = -EFAULT;
+ goto out_unlock;
+ }
+
+ ret = data_len;
+
+ spin_lock_irq(&kcs_bmc->lock);
+ if (kcs_bmc->phase == KCS_PHASE_WRITE_DONE) {
+ kcs_bmc->phase = KCS_PHASE_WAIT_READ;
+ kcs_bmc->data_in_avail = false;
+ kcs_bmc->data_in_idx = 0;
+ } else {
+ ret = -EAGAIN;
+ }
+ spin_unlock_irq(&kcs_bmc->lock);
+
+out_unlock:
+ mutex_unlock(&kcs_bmc->mutex);
+
+ return ret;
+}
+
+static ssize_t kcs_bmc_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
+ ssize_t ret;
+
+ /* a minimum response size '3' : netfn + cmd + ccode */
+ if (count < 3 || count > KCS_MSG_BUFSIZ)
+ return -EINVAL;
+
+ mutex_lock(&kcs_bmc->mutex);
+
+ if (copy_from_user(kcs_bmc->kbuffer, buf, count)) {
+ ret = -EFAULT;
+ goto out_unlock;
+ }
+
+ spin_lock_irq(&kcs_bmc->lock);
+ if (kcs_bmc->phase == KCS_PHASE_WAIT_READ) {
+ kcs_bmc->phase = KCS_PHASE_READ;
+ kcs_bmc->data_out_idx = 1;
+ kcs_bmc->data_out_len = count;
+ memcpy(kcs_bmc->data_out, kcs_bmc->kbuffer, count);
+ write_data(kcs_bmc, kcs_bmc->data_out[0]);
+ ret = count;
+ } else {
+ ret = -EINVAL;
+ }
+ spin_unlock_irq(&kcs_bmc->lock);
+
+out_unlock:
+ mutex_unlock(&kcs_bmc->mutex);
+
+ return ret;
+}
+
+static long kcs_bmc_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
+ long ret = 0;
+
+ spin_lock_irq(&kcs_bmc->lock);
+
+ switch (cmd) {
+ case IPMI_BMC_IOCTL_SET_SMS_ATN:
+ update_status_bits(kcs_bmc, KCS_STATUS_SMS_ATN,
+ KCS_STATUS_SMS_ATN);
+ break;
+
+ case IPMI_BMC_IOCTL_CLEAR_SMS_ATN:
+ update_status_bits(kcs_bmc, KCS_STATUS_SMS_ATN,
+ 0);
+ break;
+
+ case IPMI_BMC_IOCTL_FORCE_ABORT:
+ kcs_force_abort(kcs_bmc);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ spin_unlock_irq(&kcs_bmc->lock);
+
+ return ret;
+}
+
+static int kcs_bmc_release(struct inode *inode, struct file *filp)
+{
+ struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
+
+ spin_lock_irq(&kcs_bmc->lock);
+ kcs_bmc->running = 0;
+ kcs_force_abort(kcs_bmc);
+ spin_unlock_irq(&kcs_bmc->lock);
+
+ return 0;
+}
+
+static const struct file_operations kcs_bmc_fops = {
+ .owner = THIS_MODULE,
+ .open = kcs_bmc_open,
+ .read = kcs_bmc_read,
+ .write = kcs_bmc_write,
+ .release = kcs_bmc_release,
+ .poll = kcs_bmc_poll,
+ .unlocked_ioctl = kcs_bmc_ioctl,
+};
+
+struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, u32 channel)
+{
+ struct kcs_bmc *kcs_bmc;
+
+ kcs_bmc = devm_kzalloc(dev, sizeof(*kcs_bmc) + sizeof_priv, GFP_KERNEL);
+ if (!kcs_bmc)
+ return NULL;
+
+ dev_set_name(dev, "ipmi-kcs%u", channel);
+
+ spin_lock_init(&kcs_bmc->lock);
+ kcs_bmc->channel = channel;
+
+ mutex_init(&kcs_bmc->mutex);
+ init_waitqueue_head(&kcs_bmc->queue);
+
+ kcs_bmc->data_in = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
+ kcs_bmc->data_out = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
+ kcs_bmc->kbuffer = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
+ if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer)
+ return NULL;
+
+ kcs_bmc->miscdev.minor = MISC_DYNAMIC_MINOR;
+ kcs_bmc->miscdev.name = dev_name(dev);
+ kcs_bmc->miscdev.fops = &kcs_bmc_fops;
+
+ return kcs_bmc;
+}
+EXPORT_SYMBOL(kcs_bmc_alloc);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
+MODULE_DESCRIPTION("KCS BMC to handle the IPMI request from system software");
diff --git a/drivers/char/ipmi/kcs_bmc.h b/drivers/char/ipmi/kcs_bmc.h
new file mode 100644
index 000000000000..eb9ea4ce78b8
--- /dev/null
+++ b/drivers/char/ipmi/kcs_bmc.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2015-2018, Intel Corporation.
+ */
+
+#ifndef __KCS_BMC_H__
+#define __KCS_BMC_H__
+
+#include <linux/miscdevice.h>
+
+/* Different phases of the KCS BMC module.
+ * KCS_PHASE_IDLE:
+ * BMC should not be expecting nor sending any data.
+ * KCS_PHASE_WRITE_START:
+ * BMC is receiving a WRITE_START command from system software.
+ * KCS_PHASE_WRITE_DATA:
+ * BMC is receiving a data byte from system software.
+ * KCS_PHASE_WRITE_END_CMD:
+ * BMC is waiting a last data byte from system software.
+ * KCS_PHASE_WRITE_DONE:
+ * BMC has received the whole request from system software.
+ * KCS_PHASE_WAIT_READ:
+ * BMC is waiting the response from the upper IPMI service.
+ * KCS_PHASE_READ:
+ * BMC is transferring the response to system software.
+ * KCS_PHASE_ABORT_ERROR1:
+ * BMC is waiting error status request from system software.
+ * KCS_PHASE_ABORT_ERROR2:
+ * BMC is waiting for idle status afer error from system software.
+ * KCS_PHASE_ERROR:
+ * BMC has detected a protocol violation at the interface level.
+ */
+enum kcs_phases {
+ KCS_PHASE_IDLE,
+
+ KCS_PHASE_WRITE_START,
+ KCS_PHASE_WRITE_DATA,
+ KCS_PHASE_WRITE_END_CMD,
+ KCS_PHASE_WRITE_DONE,
+
+ KCS_PHASE_WAIT_READ,
+ KCS_PHASE_READ,
+
+ KCS_PHASE_ABORT_ERROR1,
+ KCS_PHASE_ABORT_ERROR2,
+ KCS_PHASE_ERROR
+};
+
+/* IPMI 2.0 - Table 9-4, KCS Interface Status Codes */
+enum kcs_errors {
+ KCS_NO_ERROR = 0x00,
+ KCS_ABORTED_BY_COMMAND = 0x01,
+ KCS_ILLEGAL_CONTROL_CODE = 0x02,
+ KCS_LENGTH_ERROR = 0x06,
+ KCS_UNSPECIFIED_ERROR = 0xFF
+};
+
+/* IPMI 2.0 - 9.5, KCS Interface Registers
+ * @idr: Input Data Register
+ * @odr: Output Data Register
+ * @str: Status Register
+ */
+struct kcs_ioreg {
+ u32 idr;
+ u32 odr;
+ u32 str;
+};
+
+struct kcs_bmc {
+ spinlock_t lock;
+
+ u32 channel;
+ int running;
+
+ /* Setup by BMC KCS controller driver */
+ struct kcs_ioreg ioreg;
+ u8 (*io_inputb)(struct kcs_bmc *kcs_bmc, u32 reg);
+ void (*io_outputb)(struct kcs_bmc *kcs_bmc, u32 reg, u8 b);
+
+ enum kcs_phases phase;
+ enum kcs_errors error;
+
+ wait_queue_head_t queue;
+ bool data_in_avail;
+ int data_in_idx;
+ u8 *data_in;
+
+ int data_out_idx;
+ int data_out_len;
+ u8 *data_out;
+
+ struct mutex mutex;
+ u8 *kbuffer;
+
+ struct miscdevice miscdev;
+
+ unsigned long priv[];
+};
+
+static inline void *kcs_bmc_priv(struct kcs_bmc *kcs_bmc)
+{
+ return kcs_bmc->priv;
+}
+
+int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc);
+struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv,
+ u32 channel);
+#endif /* __KCS_BMC_H__ */
diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
new file mode 100644
index 000000000000..3c955946e647
--- /dev/null
+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2015-2018, Intel Corporation.
+ */
+
+#define pr_fmt(fmt) "aspeed-kcs-bmc: " fmt
+
+#include <linux/atomic.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/regmap.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+
+#include "kcs_bmc.h"
+
+
+#define DEVICE_NAME "ast-kcs-bmc"
+
+#define KCS_CHANNEL_MAX 4
+
+/* mapped to lpc-bmc@0 IO space */
+#define LPC_HICR0 0x000
+#define LPC_HICR0_LPC3E BIT(7)
+#define LPC_HICR0_LPC2E BIT(6)
+#define LPC_HICR0_LPC1E BIT(5)
+#define LPC_HICR2 0x008
+#define LPC_HICR2_IBFIF3 BIT(3)
+#define LPC_HICR2_IBFIF2 BIT(2)
+#define LPC_HICR2_IBFIF1 BIT(1)
+#define LPC_HICR4 0x010
+#define LPC_HICR4_LADR12AS BIT(7)
+#define LPC_HICR4_KCSENBL BIT(2)
+#define LPC_LADR3H 0x014
+#define LPC_LADR3L 0x018
+#define LPC_LADR12H 0x01C
+#define LPC_LADR12L 0x020
+#define LPC_IDR1 0x024
+#define LPC_IDR2 0x028
+#define LPC_IDR3 0x02C
+#define LPC_ODR1 0x030
+#define LPC_ODR2 0x034
+#define LPC_ODR3 0x038
+#define LPC_STR1 0x03C
+#define LPC_STR2 0x040
+#define LPC_STR3 0x044
+
+/* mapped to lpc-host@80 IO space */
+#define LPC_HICRB 0x080
+#define LPC_HICRB_IBFIF4 BIT(1)
+#define LPC_HICRB_LPC4E BIT(0)
+#define LPC_LADR4 0x090
+#define LPC_IDR4 0x094
+#define LPC_ODR4 0x098
+#define LPC_STR4 0x09C
+
+struct aspeed_kcs_bmc {
+ struct regmap *map;
+};
+
+
+static u8 aspeed_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
+{
+ struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
+ u32 val = 0;
+ int rc;
+
+ rc = regmap_read(priv->map, reg, &val);
+ WARN(rc != 0, "regmap_read() failed: %d\n", rc);
+
+ return rc == 0 ? (u8) val : 0;
+}
+
+static void aspeed_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
+{
+ struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
+ int rc;
+
+ rc = regmap_write(priv->map, reg, data);
+ WARN(rc != 0, "regmap_write() failed: %d\n", rc);
+}
+
+
+/*
+ * AST_usrGuide_KCS.pdf
+ * 2. Background:
+ * we note D for Data, and C for Cmd/Status, default rules are
+ * A. KCS1 / KCS2 ( D / C:X / X+4 )
+ * D / C : CA0h / CA4h
+ * D / C : CA8h / CACh
+ * B. KCS3 ( D / C:XX2h / XX3h )
+ * D / C : CA2h / CA3h
+ * D / C : CB2h / CB3h
+ * C. KCS4
+ * D / C : CA4h / CA5h
+ */
+static void aspeed_kcs_set_address(struct kcs_bmc *kcs_bmc, u16 addr)
+{
+ struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
+
+ switch (kcs_bmc->channel) {
+ case 1:
+ regmap_update_bits(priv->map, LPC_HICR4,
+ LPC_HICR4_LADR12AS, 0);
+ regmap_write(priv->map, LPC_LADR12H, addr >> 8);
+ regmap_write(priv->map, LPC_LADR12L, addr & 0xFF);
+ break;
+
+ case 2:
+ regmap_update_bits(priv->map, LPC_HICR4,
+ LPC_HICR4_LADR12AS, LPC_HICR4_LADR12AS);
+ regmap_write(priv->map, LPC_LADR12H, addr >> 8);
+ regmap_write(priv->map, LPC_LADR12L, addr & 0xFF);
+ break;
+
+ case 3:
+ regmap_write(priv->map, LPC_LADR3H, addr >> 8);
+ regmap_write(priv->map, LPC_LADR3L, addr & 0xFF);
+ break;
+
+ case 4:
+ regmap_write(priv->map, LPC_LADR4, ((addr + 1) << 16) |
+ addr);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void aspeed_kcs_enable_channel(struct kcs_bmc *kcs_bmc, bool enable)
+{
+ struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
+
+ switch (kcs_bmc->channel) {
+ case 1:
+ if (enable) {
+ regmap_update_bits(priv->map, LPC_HICR2,
+ LPC_HICR2_IBFIF1, LPC_HICR2_IBFIF1);
+ regmap_update_bits(priv->map, LPC_HICR0,
+ LPC_HICR0_LPC1E, LPC_HICR0_LPC1E);
+ } else {
+ regmap_update_bits(priv->map, LPC_HICR0,
+ LPC_HICR0_LPC1E, 0);
+ regmap_update_bits(priv->map, LPC_HICR2,
+ LPC_HICR2_IBFIF1, 0);
+ }
+ break;
+
+ case 2:
+ if (enable) {
+ regmap_update_bits(priv->map, LPC_HICR2,
+ LPC_HICR2_IBFIF2, LPC_HICR2_IBFIF2);
+ regmap_update_bits(priv->map, LPC_HICR0,
+ LPC_HICR0_LPC2E, LPC_HICR0_LPC2E);
+ } else {
+ regmap_update_bits(priv->map, LPC_HICR0,
+ LPC_HICR0_LPC2E, 0);
+ regmap_update_bits(priv->map, LPC_HICR2,
+ LPC_HICR2_IBFIF2, 0);
+ }
+ break;
+
+ case 3:
+ if (enable) {
+ regmap_update_bits(priv->map, LPC_HICR2,
+ LPC_HICR2_IBFIF3, LPC_HICR2_IBFIF3);
+ regmap_update_bits(priv->map, LPC_HICR0,
+ LPC_HICR0_LPC3E, LPC_HICR0_LPC3E);
+ regmap_update_bits(priv->map, LPC_HICR4,
+ LPC_HICR4_KCSENBL, LPC_HICR4_KCSENBL);
+ } else {
+ regmap_update_bits(priv->map, LPC_HICR0,
+ LPC_HICR0_LPC3E, 0);
+ regmap_update_bits(priv->map, LPC_HICR4,
+ LPC_HICR4_KCSENBL, 0);
+ regmap_update_bits(priv->map, LPC_HICR2,
+ LPC_HICR2_IBFIF3, 0);
+ }
+ break;
+
+ case 4:
+ if (enable)
+ regmap_update_bits(priv->map, LPC_HICRB,
+ LPC_HICRB_IBFIF4 | LPC_HICRB_LPC4E,
+ LPC_HICRB_IBFIF4 | LPC_HICRB_LPC4E);
+ else
+ regmap_update_bits(priv->map, LPC_HICRB,
+ LPC_HICRB_IBFIF4 | LPC_HICRB_LPC4E,
+ 0);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static irqreturn_t aspeed_kcs_irq(int irq, void *arg)
+{
+ struct kcs_bmc *kcs_bmc = arg;
+
+ if (!kcs_bmc_handle_event(kcs_bmc))
+ return IRQ_HANDLED;
+
+ return IRQ_NONE;
+}
+
+static int aspeed_kcs_config_irq(struct kcs_bmc *kcs_bmc,
+ struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ int irq;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ return devm_request_irq(dev, irq, aspeed_kcs_irq, IRQF_SHARED,
+ dev_name(dev), kcs_bmc);
+}
+
+static const struct kcs_ioreg ast_kcs_bmc_ioregs[KCS_CHANNEL_MAX] = {
+ { .idr = LPC_IDR1, .odr = LPC_ODR1, .str = LPC_STR1 },
+ { .idr = LPC_IDR2, .odr = LPC_ODR2, .str = LPC_STR2 },
+ { .idr = LPC_IDR3, .odr = LPC_ODR3, .str = LPC_STR3 },
+ { .idr = LPC_IDR4, .odr = LPC_ODR4, .str = LPC_STR4 },
+};
+
+static int aspeed_kcs_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct aspeed_kcs_bmc *priv;
+ struct kcs_bmc *kcs_bmc;
+ u32 chan, addr;
+ int rc;
+
+ rc = of_property_read_u32(dev->of_node, "kcs_chan", &chan);
+ if ((rc != 0) || (chan == 0 || chan > KCS_CHANNEL_MAX)) {
+ dev_err(dev, "no valid 'kcs_chan' configured\n");
+ return -ENODEV;
+ }
+
+ rc = of_property_read_u32(dev->of_node, "kcs_addr", &addr);
+ if (rc) {
+ dev_err(dev, "no valid 'kcs_addr' configured\n");
+ return -ENODEV;
+ }
+
+ kcs_bmc = kcs_bmc_alloc(dev, sizeof(*priv), chan);
+ if (!kcs_bmc)
+ return -ENOMEM;
+
+ priv = kcs_bmc_priv(kcs_bmc);
+ priv->map = syscon_node_to_regmap(dev->parent->of_node);
+ if (IS_ERR(priv->map)) {
+ dev_err(dev, "Couldn't get regmap\n");
+ return -ENODEV;
+ }
+
+ kcs_bmc->ioreg = ast_kcs_bmc_ioregs[chan - 1];
+ kcs_bmc->io_inputb = aspeed_kcs_inb;
+ kcs_bmc->io_outputb = aspeed_kcs_outb;
+
+ dev_set_drvdata(dev, kcs_bmc);
+
+ aspeed_kcs_set_address(kcs_bmc, addr);
+ aspeed_kcs_enable_channel(kcs_bmc, true);
+ rc = aspeed_kcs_config_irq(kcs_bmc, pdev);
+ if (rc)
+ return rc;
+
+ rc = misc_register(&kcs_bmc->miscdev);
+ if (rc) {
+ dev_err(dev, "Unable to register device\n");
+ return rc;
+ }
+
+ pr_info("channel=%u addr=0x%x idr=0x%x odr=0x%x str=0x%x\n",
+ chan, addr,
+ kcs_bmc->ioreg.idr, kcs_bmc->ioreg.odr, kcs_bmc->ioreg.str);
+
+ return 0;
+}
+
+static int aspeed_kcs_remove(struct platform_device *pdev)
+{
+ struct kcs_bmc *kcs_bmc = dev_get_drvdata(&pdev->dev);
+
+ misc_deregister(&kcs_bmc->miscdev);
+
+ return 0;
+}
+
+static const struct of_device_id ast_kcs_bmc_match[] = {
+ { .compatible = "aspeed,ast2400-kcs-bmc" },
+ { .compatible = "aspeed,ast2500-kcs-bmc" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ast_kcs_bmc_match);
+
+static struct platform_driver ast_kcs_bmc_driver = {
+ .driver = {
+ .name = DEVICE_NAME,
+ .of_match_table = ast_kcs_bmc_match,
+ },
+ .probe = aspeed_kcs_probe,
+ .remove = aspeed_kcs_remove,
+};
+module_platform_driver(ast_kcs_bmc_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
+MODULE_DESCRIPTION("Aspeed device interface to the KCS BMC device");
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 052011bcf100..ffeb60d3434c 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -137,7 +137,7 @@ static ssize_t read_mem(struct file *file, char __user *buf,
while (count > 0) {
unsigned long remaining;
- int allowed;
+ int allowed, probe;
sz = size_inside_page(p, count);
@@ -160,9 +160,9 @@ static ssize_t read_mem(struct file *file, char __user *buf,
if (!ptr)
goto failed;
- err = probe_kernel_read(bounce, ptr, sz);
+ probe = probe_kernel_read(bounce, ptr, sz);
unxlate_dev_mem_ptr(p, ptr);
- if (err)
+ if (probe)
goto failed;
remaining = copy_to_user(buf, bounce, sz);
diff --git a/drivers/char/random.c b/drivers/char/random.c
index e5b3d3ba4660..cd888d4ee605 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -261,6 +261,7 @@
#include <linux/ptrace.h>
#include <linux/workqueue.h>
#include <linux/irq.h>
+#include <linux/ratelimit.h>
#include <linux/syscalls.h>
#include <linux/completion.h>
#include <linux/uuid.h>
@@ -427,8 +428,9 @@ struct crng_state primary_crng = {
* its value (from 0->1->2).
*/
static int crng_init = 0;
-#define crng_ready() (likely(crng_init > 0))
+#define crng_ready() (likely(crng_init > 1))
static int crng_init_cnt = 0;
+static unsigned long crng_global_init_time = 0;
#define CRNG_INIT_CNT_THRESH (2*CHACHA20_KEY_SIZE)
static void _extract_crng(struct crng_state *crng,
__u32 out[CHACHA20_BLOCK_WORDS]);
@@ -437,6 +439,16 @@ static void _crng_backtrack_protect(struct crng_state *crng,
static void process_random_ready_list(void);
static void _get_random_bytes(void *buf, int nbytes);
+static struct ratelimit_state unseeded_warning =
+ RATELIMIT_STATE_INIT("warn_unseeded_randomness", HZ, 3);
+static struct ratelimit_state urandom_warning =
+ RATELIMIT_STATE_INIT("warn_urandom_randomness", HZ, 3);
+
+static int ratelimit_disable __read_mostly;
+
+module_param_named(ratelimit_disable, ratelimit_disable, int, 0644);
+MODULE_PARM_DESC(ratelimit_disable, "Disable random ratelimit suppression");
+
/**********************************************************************
*
* OS independent entropy store. Here are the functions which handle
@@ -709,7 +721,8 @@ retry:
}
/* should we wake readers? */
- if (entropy_bits >= random_read_wakeup_bits) {
+ if (entropy_bits >= random_read_wakeup_bits &&
+ wq_has_sleeper(&random_read_wait)) {
wake_up_interruptible(&random_read_wait);
kill_fasync(&fasync, SIGIO, POLL_IN);
}
@@ -732,7 +745,7 @@ retry:
static int credit_entropy_bits_safe(struct entropy_store *r, int nbits)
{
- const int nbits_max = (int)(~0U >> (ENTROPY_SHIFT + 1));
+ const int nbits_max = r->poolinfo->poolwords * 32;
if (nbits < 0)
return -EINVAL;
@@ -786,6 +799,43 @@ static void crng_initialize(struct crng_state *crng)
crng->init_time = jiffies - CRNG_RESEED_INTERVAL - 1;
}
+#ifdef CONFIG_NUMA
+static void do_numa_crng_init(struct work_struct *work)
+{
+ int i;
+ struct crng_state *crng;
+ struct crng_state **pool;
+
+ pool = kcalloc(nr_node_ids, sizeof(*pool), GFP_KERNEL|__GFP_NOFAIL);
+ for_each_online_node(i) {
+ crng = kmalloc_node(sizeof(struct crng_state),
+ GFP_KERNEL | __GFP_NOFAIL, i);
+ spin_lock_init(&crng->lock);
+ crng_initialize(crng);
+ pool[i] = crng;
+ }
+ mb();
+ if (cmpxchg(&crng_node_pool, NULL, pool)) {
+ for_each_node(i)
+ kfree(pool[i]);
+ kfree(pool);
+ }
+}
+
+static DECLARE_WORK(numa_crng_init_work, do_numa_crng_init);
+
+static void numa_crng_init(void)
+{
+ schedule_work(&numa_crng_init_work);
+}
+#else
+static void numa_crng_init(void) {}
+#endif
+
+/*
+ * crng_fast_load() can be called by code in the interrupt service
+ * path. So we can't afford to dilly-dally.
+ */
static int crng_fast_load(const char *cp, size_t len)
{
unsigned long flags;
@@ -793,7 +843,7 @@ static int crng_fast_load(const char *cp, size_t len)
if (!spin_trylock_irqsave(&primary_crng.lock, flags))
return 0;
- if (crng_ready()) {
+ if (crng_init != 0) {
spin_unlock_irqrestore(&primary_crng.lock, flags);
return 0;
}
@@ -812,6 +862,51 @@ static int crng_fast_load(const char *cp, size_t len)
return 1;
}
+/*
+ * crng_slow_load() is called by add_device_randomness, which has two
+ * attributes. (1) We can't trust the buffer passed to it is
+ * guaranteed to be unpredictable (so it might not have any entropy at
+ * all), and (2) it doesn't have the performance constraints of
+ * crng_fast_load().
+ *
+ * So we do something more comprehensive which is guaranteed to touch
+ * all of the primary_crng's state, and which uses a LFSR with a
+ * period of 255 as part of the mixing algorithm. Finally, we do
+ * *not* advance crng_init_cnt since buffer we may get may be something
+ * like a fixed DMI table (for example), which might very well be
+ * unique to the machine, but is otherwise unvarying.
+ */
+static int crng_slow_load(const char *cp, size_t len)
+{
+ unsigned long flags;
+ static unsigned char lfsr = 1;
+ unsigned char tmp;
+ unsigned i, max = CHACHA20_KEY_SIZE;
+ const char * src_buf = cp;
+ char * dest_buf = (char *) &primary_crng.state[4];
+
+ if (!spin_trylock_irqsave(&primary_crng.lock, flags))
+ return 0;
+ if (crng_init != 0) {
+ spin_unlock_irqrestore(&primary_crng.lock, flags);
+ return 0;
+ }
+ if (len > max)
+ max = len;
+
+ for (i = 0; i < max ; i++) {
+ tmp = lfsr;
+ lfsr >>= 1;
+ if (tmp & 1)
+ lfsr ^= 0xE1;
+ tmp = dest_buf[i % CHACHA20_KEY_SIZE];
+ dest_buf[i % CHACHA20_KEY_SIZE] ^= src_buf[i % len] ^ lfsr;
+ lfsr += (tmp << 3) | (tmp >> 5);
+ }
+ spin_unlock_irqrestore(&primary_crng.lock, flags);
+ return 1;
+}
+
static void crng_reseed(struct crng_state *crng, struct entropy_store *r)
{
unsigned long flags;
@@ -830,7 +925,7 @@ static void crng_reseed(struct crng_state *crng, struct entropy_store *r)
_crng_backtrack_protect(&primary_crng, buf.block,
CHACHA20_KEY_SIZE);
}
- spin_lock_irqsave(&primary_crng.lock, flags);
+ spin_lock_irqsave(&crng->lock, flags);
for (i = 0; i < 8; i++) {
unsigned long rv;
if (!arch_get_random_seed_long(&rv) &&
@@ -840,13 +935,26 @@ static void crng_reseed(struct crng_state *crng, struct entropy_store *r)
}
memzero_explicit(&buf, sizeof(buf));
crng->init_time = jiffies;
- spin_unlock_irqrestore(&primary_crng.lock, flags);
+ spin_unlock_irqrestore(&crng->lock, flags);
if (crng == &primary_crng && crng_init < 2) {
invalidate_batched_entropy();
+ numa_crng_init();
crng_init = 2;
process_random_ready_list();
wake_up_interruptible(&crng_init_wait);
pr_notice("random: crng init done\n");
+ if (unseeded_warning.missed) {
+ pr_notice("random: %d get_random_xx warning(s) missed "
+ "due to ratelimiting\n",
+ unseeded_warning.missed);
+ unseeded_warning.missed = 0;
+ }
+ if (urandom_warning.missed) {
+ pr_notice("random: %d urandom warning(s) missed "
+ "due to ratelimiting\n",
+ urandom_warning.missed);
+ urandom_warning.missed = 0;
+ }
}
}
@@ -855,8 +963,9 @@ static void _extract_crng(struct crng_state *crng,
{
unsigned long v, flags;
- if (crng_init > 1 &&
- time_after(jiffies, crng->init_time + CRNG_RESEED_INTERVAL))
+ if (crng_ready() &&
+ (time_after(crng_global_init_time, crng->init_time) ||
+ time_after(jiffies, crng->init_time + CRNG_RESEED_INTERVAL)))
crng_reseed(crng, crng == &primary_crng ? &input_pool : NULL);
spin_lock_irqsave(&crng->lock, flags);
if (arch_get_random_long(&v))
@@ -963,7 +1072,6 @@ static ssize_t extract_crng_user(void __user *buf, size_t nbytes)
struct timer_rand_state {
cycles_t last_time;
long last_delta, last_delta2;
- unsigned dont_count_entropy:1;
};
#define INIT_TIMER_RAND_STATE { INITIAL_JIFFIES, };
@@ -981,10 +1089,8 @@ void add_device_randomness(const void *buf, unsigned int size)
unsigned long time = random_get_entropy() ^ jiffies;
unsigned long flags;
- if (!crng_ready()) {
- crng_fast_load(buf, size);
- return;
- }
+ if (!crng_ready() && size)
+ crng_slow_load(buf, size);
trace_add_device_randomness(size, _RET_IP_);
spin_lock_irqsave(&input_pool.lock, flags);
@@ -1029,35 +1135,33 @@ static void add_timer_randomness(struct timer_rand_state *state, unsigned num)
* We take into account the first, second and third-order deltas
* in order to make our estimate.
*/
+ delta = sample.jiffies - state->last_time;
+ state->last_time = sample.jiffies;
+
+ delta2 = delta - state->last_delta;
+ state->last_delta = delta;
+
+ delta3 = delta2 - state->last_delta2;
+ state->last_delta2 = delta2;
+
+ if (delta < 0)
+ delta = -delta;
+ if (delta2 < 0)
+ delta2 = -delta2;
+ if (delta3 < 0)
+ delta3 = -delta3;
+ if (delta > delta2)
+ delta = delta2;
+ if (delta > delta3)
+ delta = delta3;
- if (!state->dont_count_entropy) {
- delta = sample.jiffies - state->last_time;
- state->last_time = sample.jiffies;
-
- delta2 = delta - state->last_delta;
- state->last_delta = delta;
-
- delta3 = delta2 - state->last_delta2;
- state->last_delta2 = delta2;
-
- if (delta < 0)
- delta = -delta;
- if (delta2 < 0)
- delta2 = -delta2;
- if (delta3 < 0)
- delta3 = -delta3;
- if (delta > delta2)
- delta = delta2;
- if (delta > delta3)
- delta = delta3;
+ /*
+ * delta is now minimum absolute delta.
+ * Round down by 1 bit on general principles,
+ * and limit entropy entimate to 12 bits.
+ */
+ credit_entropy_bits(r, min_t(int, fls(delta>>1), 11));
- /*
- * delta is now minimum absolute delta.
- * Round down by 1 bit on general principles,
- * and limit entropy entimate to 12 bits.
- */
- credit_entropy_bits(r, min_t(int, fls(delta>>1), 11));
- }
preempt_enable();
}
@@ -1141,7 +1245,7 @@ void add_interrupt_randomness(int irq, int irq_flags)
fast_mix(fast_pool);
add_interrupt_bench(cycles);
- if (!crng_ready()) {
+ if (unlikely(crng_init == 0)) {
if ((fast_pool->count >= 64) &&
crng_fast_load((char *) fast_pool->pool,
sizeof(fast_pool->pool))) {
@@ -1491,8 +1595,9 @@ static void _warn_unseeded_randomness(const char *func_name, void *caller,
#ifndef CONFIG_WARN_ALL_UNSEEDED_RANDOM
print_once = true;
#endif
- pr_notice("random: %s called from %pS with crng_init=%d\n",
- func_name, caller, crng_init);
+ if (__ratelimit(&unseeded_warning))
+ pr_notice("random: %s called from %pS with crng_init=%d\n",
+ func_name, caller, crng_init);
}
/*
@@ -1682,28 +1787,14 @@ static void init_std_data(struct entropy_store *r)
*/
static int rand_initialize(void)
{
-#ifdef CONFIG_NUMA
- int i;
- struct crng_state *crng;
- struct crng_state **pool;
-#endif
-
init_std_data(&input_pool);
init_std_data(&blocking_pool);
crng_initialize(&primary_crng);
-
-#ifdef CONFIG_NUMA
- pool = kcalloc(nr_node_ids, sizeof(*pool), GFP_KERNEL|__GFP_NOFAIL);
- for_each_online_node(i) {
- crng = kmalloc_node(sizeof(struct crng_state),
- GFP_KERNEL | __GFP_NOFAIL, i);
- spin_lock_init(&crng->lock);
- crng_initialize(crng);
- pool[i] = crng;
+ crng_global_init_time = jiffies;
+ if (ratelimit_disable) {
+ urandom_warning.interval = 0;
+ unseeded_warning.interval = 0;
}
- mb();
- crng_node_pool = pool;
-#endif
return 0;
}
early_initcall(rand_initialize);
@@ -1771,9 +1862,10 @@ urandom_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
if (!crng_ready() && maxwarn > 0) {
maxwarn--;
- printk(KERN_NOTICE "random: %s: uninitialized urandom read "
- "(%zd bytes read)\n",
- current->comm, nbytes);
+ if (__ratelimit(&urandom_warning))
+ printk(KERN_NOTICE "random: %s: uninitialized "
+ "urandom read (%zd bytes read)\n",
+ current->comm, nbytes);
spin_lock_irqsave(&primary_crng.lock, flags);
crng_init_cnt = 0;
spin_unlock_irqrestore(&primary_crng.lock, flags);
@@ -1877,6 +1969,14 @@ static long random_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
input_pool.entropy_count = 0;
blocking_pool.entropy_count = 0;
return 0;
+ case RNDRESEEDCRNG:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (crng_init < 2)
+ return -ENODATA;
+ crng_reseed(&primary_crng, NULL);
+ crng_global_init_time = jiffies - 1;
+ return 0;
default:
return -EINVAL;
}
@@ -2214,7 +2314,7 @@ void add_hwgenerator_randomness(const char *buffer, size_t count,
{
struct entropy_store *poolp = &input_pool;
- if (!crng_ready()) {
+ if (unlikely(crng_init == 0)) {
crng_fast_load(buffer, count);
return;
}
diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c
index 0c858d027bf3..57dc546628b5 100644
--- a/drivers/char/rtc.c
+++ b/drivers/char/rtc.c
@@ -809,89 +809,6 @@ static __poll_t rtc_poll(struct file *file, poll_table *wait)
}
#endif
-int rtc_register(rtc_task_t *task)
-{
-#ifndef RTC_IRQ
- return -EIO;
-#else
- if (task == NULL || task->func == NULL)
- return -EINVAL;
- spin_lock_irq(&rtc_lock);
- if (rtc_status & RTC_IS_OPEN) {
- spin_unlock_irq(&rtc_lock);
- return -EBUSY;
- }
- spin_lock(&rtc_task_lock);
- if (rtc_callback) {
- spin_unlock(&rtc_task_lock);
- spin_unlock_irq(&rtc_lock);
- return -EBUSY;
- }
- rtc_status |= RTC_IS_OPEN;
- rtc_callback = task;
- spin_unlock(&rtc_task_lock);
- spin_unlock_irq(&rtc_lock);
- return 0;
-#endif
-}
-EXPORT_SYMBOL(rtc_register);
-
-int rtc_unregister(rtc_task_t *task)
-{
-#ifndef RTC_IRQ
- return -EIO;
-#else
- unsigned char tmp;
-
- spin_lock_irq(&rtc_lock);
- spin_lock(&rtc_task_lock);
- if (rtc_callback != task) {
- spin_unlock(&rtc_task_lock);
- spin_unlock_irq(&rtc_lock);
- return -ENXIO;
- }
- rtc_callback = NULL;
-
- /* disable controls */
- if (!hpet_mask_rtc_irq_bit(RTC_PIE | RTC_AIE | RTC_UIE)) {
- tmp = CMOS_READ(RTC_CONTROL);
- tmp &= ~RTC_PIE;
- tmp &= ~RTC_AIE;
- tmp &= ~RTC_UIE;
- CMOS_WRITE(tmp, RTC_CONTROL);
- CMOS_READ(RTC_INTR_FLAGS);
- }
- if (rtc_status & RTC_TIMER_ON) {
- rtc_status &= ~RTC_TIMER_ON;
- del_timer(&rtc_irq_timer);
- }
- rtc_status &= ~RTC_IS_OPEN;
- spin_unlock(&rtc_task_lock);
- spin_unlock_irq(&rtc_lock);
- return 0;
-#endif
-}
-EXPORT_SYMBOL(rtc_unregister);
-
-int rtc_control(rtc_task_t *task, unsigned int cmd, unsigned long arg)
-{
-#ifndef RTC_IRQ
- return -EIO;
-#else
- unsigned long flags;
- if (cmd != RTC_PIE_ON && cmd != RTC_PIE_OFF && cmd != RTC_IRQP_SET)
- return -EINVAL;
- spin_lock_irqsave(&rtc_task_lock, flags);
- if (rtc_callback != task) {
- spin_unlock_irqrestore(&rtc_task_lock, flags);
- return -ENXIO;
- }
- spin_unlock_irqrestore(&rtc_task_lock, flags);
- return rtc_do_ioctl(cmd, arg, 1);
-#endif
-}
-EXPORT_SYMBOL(rtc_control);
-
/*
* The various file operations we support.
*/
diff --git a/drivers/char/tile-srom.c b/drivers/char/tile-srom.c
deleted file mode 100644
index 3d4cca64b2d4..000000000000
--- a/drivers/char/tile-srom.c
+++ /dev/null
@@ -1,475 +0,0 @@
-/*
- * Copyright 2011 Tilera Corporation. All Rights Reserved.
- *
- * 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 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, GOOD TITLE or
- * NON INFRINGEMENT. See the GNU General Public License for
- * more details.
- *
- * SPI Flash ROM driver
- *
- * This source code is derived from code provided in "Linux Device
- * Drivers, Third Edition", by Jonathan Corbet, Alessandro Rubini, and
- * Greg Kroah-Hartman, published by O'Reilly Media, Inc.
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h> /* printk() */
-#include <linux/slab.h> /* kmalloc() */
-#include <linux/fs.h> /* everything... */
-#include <linux/errno.h> /* error codes */
-#include <linux/types.h> /* size_t */
-#include <linux/proc_fs.h>
-#include <linux/fcntl.h> /* O_ACCMODE */
-#include <linux/pagemap.h>
-#include <linux/hugetlb.h>
-#include <linux/uaccess.h>
-#include <linux/platform_device.h>
-#include <hv/hypervisor.h>
-#include <linux/ioctl.h>
-#include <linux/cdev.h>
-#include <linux/delay.h>
-#include <hv/drv_srom_intf.h>
-
-/*
- * Size of our hypervisor I/O requests. We break up large transfers
- * so that we don't spend large uninterrupted spans of time in the
- * hypervisor. Erasing an SROM sector takes a significant fraction of
- * a second, so if we allowed the user to, say, do one I/O to write the
- * entire ROM, we'd get soft lockup timeouts, or worse.
- */
-#define SROM_CHUNK_SIZE ((size_t)4096)
-
-/*
- * When hypervisor is busy (e.g. erasing), poll the status periodically.
- */
-
-/*
- * Interval to poll the state in msec
- */
-#define SROM_WAIT_TRY_INTERVAL 20
-
-/*
- * Maximum times to poll the state
- */
-#define SROM_MAX_WAIT_TRY_TIMES 1000
-
-struct srom_dev {
- int hv_devhdl; /* Handle for hypervisor device */
- u32 total_size; /* Size of this device */
- u32 sector_size; /* Size of a sector */
- u32 page_size; /* Size of a page */
- struct mutex lock; /* Allow only one accessor at a time */
-};
-
-static int srom_major; /* Dynamic major by default */
-module_param(srom_major, int, 0);
-MODULE_AUTHOR("Tilera Corporation");
-MODULE_LICENSE("GPL");
-
-static int srom_devs; /* Number of SROM partitions */
-static struct cdev srom_cdev;
-static struct platform_device *srom_parent;
-static struct class *srom_class;
-static struct srom_dev *srom_devices;
-
-/*
- * Handle calling the hypervisor and managing EAGAIN/EBUSY.
- */
-
-static ssize_t _srom_read(int hv_devhdl, void *buf,
- loff_t off, size_t count)
-{
- int retval, retries = SROM_MAX_WAIT_TRY_TIMES;
- for (;;) {
- retval = hv_dev_pread(hv_devhdl, 0, (HV_VirtAddr)buf,
- count, off);
- if (retval >= 0)
- return retval;
- if (retval == HV_EAGAIN)
- continue;
- if (retval == HV_EBUSY && --retries > 0) {
- msleep(SROM_WAIT_TRY_INTERVAL);
- continue;
- }
- pr_err("_srom_read: error %d\n", retval);
- return -EIO;
- }
-}
-
-static ssize_t _srom_write(int hv_devhdl, const void *buf,
- loff_t off, size_t count)
-{
- int retval, retries = SROM_MAX_WAIT_TRY_TIMES;
- for (;;) {
- retval = hv_dev_pwrite(hv_devhdl, 0, (HV_VirtAddr)buf,
- count, off);
- if (retval >= 0)
- return retval;
- if (retval == HV_EAGAIN)
- continue;
- if (retval == HV_EBUSY && --retries > 0) {
- msleep(SROM_WAIT_TRY_INTERVAL);
- continue;
- }
- pr_err("_srom_write: error %d\n", retval);
- return -EIO;
- }
-}
-
-/**
- * srom_open() - Device open routine.
- * @inode: Inode for this device.
- * @filp: File for this specific open of the device.
- *
- * Returns zero, or an error code.
- */
-static int srom_open(struct inode *inode, struct file *filp)
-{
- filp->private_data = &srom_devices[iminor(inode)];
- return 0;
-}
-
-
-/**
- * srom_release() - Device release routine.
- * @inode: Inode for this device.
- * @filp: File for this specific open of the device.
- *
- * Returns zero, or an error code.
- */
-static int srom_release(struct inode *inode, struct file *filp)
-{
- struct srom_dev *srom = filp->private_data;
- char dummy;
-
- /* Make sure we've flushed anything written to the ROM. */
- mutex_lock(&srom->lock);
- if (srom->hv_devhdl >= 0)
- _srom_write(srom->hv_devhdl, &dummy, SROM_FLUSH_OFF, 1);
- mutex_unlock(&srom->lock);
-
- filp->private_data = NULL;
-
- return 0;
-}
-
-
-/**
- * srom_read() - Read data from the device.
- * @filp: File for this specific open of the device.
- * @buf: User's data buffer.
- * @count: Number of bytes requested.
- * @f_pos: File position.
- *
- * Returns number of bytes read, or an error code.
- */
-static ssize_t srom_read(struct file *filp, char __user *buf,
- size_t count, loff_t *f_pos)
-{
- int retval = 0;
- void *kernbuf;
- struct srom_dev *srom = filp->private_data;
-
- kernbuf = kmalloc(SROM_CHUNK_SIZE, GFP_KERNEL);
- if (!kernbuf)
- return -ENOMEM;
-
- if (mutex_lock_interruptible(&srom->lock)) {
- retval = -ERESTARTSYS;
- kfree(kernbuf);
- return retval;
- }
-
- while (count) {
- int hv_retval;
- int bytes_this_pass = min(count, SROM_CHUNK_SIZE);
-
- hv_retval = _srom_read(srom->hv_devhdl, kernbuf,
- *f_pos, bytes_this_pass);
- if (hv_retval <= 0) {
- if (retval == 0)
- retval = hv_retval;
- break;
- }
-
- if (copy_to_user(buf, kernbuf, hv_retval) != 0) {
- retval = -EFAULT;
- break;
- }
-
- retval += hv_retval;
- *f_pos += hv_retval;
- buf += hv_retval;
- count -= hv_retval;
- }
-
- mutex_unlock(&srom->lock);
- kfree(kernbuf);
-
- return retval;
-}
-
-/**
- * srom_write() - Write data to the device.
- * @filp: File for this specific open of the device.
- * @buf: User's data buffer.
- * @count: Number of bytes requested.
- * @f_pos: File position.
- *
- * Returns number of bytes written, or an error code.
- */
-static ssize_t srom_write(struct file *filp, const char __user *buf,
- size_t count, loff_t *f_pos)
-{
- int retval = 0;
- void *kernbuf;
- struct srom_dev *srom = filp->private_data;
-
- kernbuf = kmalloc(SROM_CHUNK_SIZE, GFP_KERNEL);
- if (!kernbuf)
- return -ENOMEM;
-
- if (mutex_lock_interruptible(&srom->lock)) {
- retval = -ERESTARTSYS;
- kfree(kernbuf);
- return retval;
- }
-
- while (count) {
- int hv_retval;
- int bytes_this_pass = min(count, SROM_CHUNK_SIZE);
-
- if (copy_from_user(kernbuf, buf, bytes_this_pass) != 0) {
- retval = -EFAULT;
- break;
- }
-
- hv_retval = _srom_write(srom->hv_devhdl, kernbuf,
- *f_pos, bytes_this_pass);
- if (hv_retval <= 0) {
- if (retval == 0)
- retval = hv_retval;
- break;
- }
-
- retval += hv_retval;
- *f_pos += hv_retval;
- buf += hv_retval;
- count -= hv_retval;
- }
-
- mutex_unlock(&srom->lock);
- kfree(kernbuf);
-
- return retval;
-}
-
-/* Provide our own implementation so we can use srom->total_size. */
-loff_t srom_llseek(struct file *file, loff_t offset, int origin)
-{
- struct srom_dev *srom = file->private_data;
- return fixed_size_llseek(file, offset, origin, srom->total_size);
-}
-
-static ssize_t total_size_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct srom_dev *srom = dev_get_drvdata(dev);
- return sprintf(buf, "%u\n", srom->total_size);
-}
-static DEVICE_ATTR_RO(total_size);
-
-static ssize_t sector_size_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct srom_dev *srom = dev_get_drvdata(dev);
- return sprintf(buf, "%u\n", srom->sector_size);
-}
-static DEVICE_ATTR_RO(sector_size);
-
-static ssize_t page_size_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct srom_dev *srom = dev_get_drvdata(dev);
- return sprintf(buf, "%u\n", srom->page_size);
-}
-static DEVICE_ATTR_RO(page_size);
-
-static struct attribute *srom_dev_attrs[] = {
- &dev_attr_total_size.attr,
- &dev_attr_sector_size.attr,
- &dev_attr_page_size.attr,
- NULL,
-};
-ATTRIBUTE_GROUPS(srom_dev);
-
-static char *srom_devnode(struct device *dev, umode_t *mode)
-{
- if (mode)
- *mode = 0644;
- return kasprintf(GFP_KERNEL, "srom/%s", dev_name(dev));
-}
-
-/*
- * The fops
- */
-static const struct file_operations srom_fops = {
- .owner = THIS_MODULE,
- .llseek = srom_llseek,
- .read = srom_read,
- .write = srom_write,
- .open = srom_open,
- .release = srom_release,
-};
-
-/**
- * srom_setup_minor() - Initialize per-minor information.
- * @srom: Per-device SROM state.
- * @devhdl: Partition device handle.
- */
-static int srom_setup_minor(struct srom_dev *srom, int devhdl)
-{
- srom->hv_devhdl = devhdl;
- mutex_init(&srom->lock);
-
- if (_srom_read(devhdl, &srom->total_size,
- SROM_TOTAL_SIZE_OFF, sizeof(srom->total_size)) < 0)
- return -EIO;
- if (_srom_read(devhdl, &srom->sector_size,
- SROM_SECTOR_SIZE_OFF, sizeof(srom->sector_size)) < 0)
- return -EIO;
- if (_srom_read(devhdl, &srom->page_size,
- SROM_PAGE_SIZE_OFF, sizeof(srom->page_size)) < 0)
- return -EIO;
-
- return 0;
-}
-
-/** srom_init() - Initialize the driver's module. */
-static int srom_init(void)
-{
- int result, i;
- dev_t dev = MKDEV(srom_major, 0);
-
- /*
- * Start with a plausible number of partitions; the krealloc() call
- * below will yield about log(srom_devs) additional allocations.
- */
- srom_devices = kmalloc(4 * sizeof(struct srom_dev), GFP_KERNEL);
-
- /* Discover the number of srom partitions. */
- for (i = 0; ; i++) {
- int devhdl;
- char buf[20];
- struct srom_dev *new_srom_devices =
- krealloc(srom_devices, (i+1) * sizeof(struct srom_dev),
- GFP_KERNEL);
- if (!new_srom_devices) {
- result = -ENOMEM;
- goto fail_mem;
- }
- srom_devices = new_srom_devices;
- sprintf(buf, "srom/0/%d", i);
- devhdl = hv_dev_open((HV_VirtAddr)buf, 0);
- if (devhdl < 0) {
- if (devhdl != HV_ENODEV)
- pr_notice("srom/%d: hv_dev_open failed: %d.\n",
- i, devhdl);
- break;
- }
- result = srom_setup_minor(&srom_devices[i], devhdl);
- if (result != 0)
- goto fail_mem;
- }
- srom_devs = i;
-
- /* Bail out early if we have no partitions at all. */
- if (srom_devs == 0) {
- result = -ENODEV;
- goto fail_mem;
- }
-
- /* Register our major, and accept a dynamic number. */
- if (srom_major)
- result = register_chrdev_region(dev, srom_devs, "srom");
- else {
- result = alloc_chrdev_region(&dev, 0, srom_devs, "srom");
- srom_major = MAJOR(dev);
- }
- if (result < 0)
- goto fail_mem;
-
- /* Register a character device. */
- cdev_init(&srom_cdev, &srom_fops);
- srom_cdev.owner = THIS_MODULE;
- srom_cdev.ops = &srom_fops;
- result = cdev_add(&srom_cdev, dev, srom_devs);
- if (result < 0)
- goto fail_chrdev;
-
- /* Create a parent device */
- srom_parent = platform_device_register_simple("srom", -1, NULL, 0);
- if (IS_ERR(srom_parent)) {
- result = PTR_ERR(srom_parent);
- goto fail_pdev;
- }
-
- /* Create a sysfs class. */
- srom_class = class_create(THIS_MODULE, "srom");
- if (IS_ERR(srom_class)) {
- result = PTR_ERR(srom_class);
- goto fail_cdev;
- }
- srom_class->dev_groups = srom_dev_groups;
- srom_class->devnode = srom_devnode;
-
- /* Create per-partition devices */
- for (i = 0; i < srom_devs; i++) {
- struct device *dev =
- device_create(srom_class, &srom_parent->dev,
- MKDEV(srom_major, i), srom_devices + i,
- "%d", i);
- result = PTR_ERR_OR_ZERO(dev);
- if (result < 0)
- goto fail_class;
- }
-
- return 0;
-
-fail_class:
- for (i = 0; i < srom_devs; i++)
- device_destroy(srom_class, MKDEV(srom_major, i));
- class_destroy(srom_class);
-fail_cdev:
- platform_device_unregister(srom_parent);
-fail_pdev:
- cdev_del(&srom_cdev);
-fail_chrdev:
- unregister_chrdev_region(dev, srom_devs);
-fail_mem:
- kfree(srom_devices);
- return result;
-}
-
-/** srom_cleanup() - Clean up the driver's module. */
-static void srom_cleanup(void)
-{
- int i;
- for (i = 0; i < srom_devs; i++)
- device_destroy(srom_class, MKDEV(srom_major, i));
- class_destroy(srom_class);
- cdev_del(&srom_cdev);
- platform_device_unregister(srom_parent);
- unregister_chrdev_region(MKDEV(srom_major, 0), srom_devs);
- kfree(srom_devices);
-}
-
-module_init(srom_init);
-module_exit(srom_cleanup);
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index 9e80a953d693..c43a9e28995e 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -369,20 +369,40 @@ err_len:
return -EINVAL;
}
-/**
- * tmp_transmit - Internal kernel interface to transmit TPM commands.
- *
- * @chip: TPM chip to use
- * @buf: TPM command buffer
- * @bufsiz: length of the TPM command buffer
- * @flags: tpm transmit flags - bitmap
- *
- * Return:
- * 0 when the operation is successful.
- * A negative number for system errors (errno).
- */
-ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
- u8 *buf, size_t bufsiz, unsigned int flags)
+static int tpm_request_locality(struct tpm_chip *chip)
+{
+ int rc;
+
+ if (!chip->ops->request_locality)
+ return 0;
+
+ rc = chip->ops->request_locality(chip, 0);
+ if (rc < 0)
+ return rc;
+
+ chip->locality = rc;
+
+ return 0;
+}
+
+static void tpm_relinquish_locality(struct tpm_chip *chip)
+{
+ int rc;
+
+ if (!chip->ops->relinquish_locality)
+ return;
+
+ rc = chip->ops->relinquish_locality(chip, chip->locality);
+ if (rc)
+ dev_err(&chip->dev, "%s: : error %d\n", __func__, rc);
+
+ chip->locality = -1;
+}
+
+static ssize_t tpm_try_transmit(struct tpm_chip *chip,
+ struct tpm_space *space,
+ u8 *buf, size_t bufsiz,
+ unsigned int flags)
{
struct tpm_output_header *header = (void *)buf;
int rc;
@@ -422,8 +442,6 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
if (!(flags & TPM_TRANSMIT_UNLOCKED))
mutex_lock(&chip->tpm_mutex);
- if (chip->dev.parent)
- pm_runtime_get_sync(chip->dev.parent);
if (chip->ops->clk_enable != NULL)
chip->ops->clk_enable(chip, true);
@@ -431,19 +449,20 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
/* Store the decision as chip->locality will be changed. */
need_locality = chip->locality == -1;
- if (!(flags & TPM_TRANSMIT_RAW) &&
- need_locality && chip->ops->request_locality) {
- rc = chip->ops->request_locality(chip, 0);
+ if (!(flags & TPM_TRANSMIT_RAW) && need_locality) {
+ rc = tpm_request_locality(chip);
if (rc < 0)
goto out_no_locality;
- chip->locality = rc;
}
+ if (chip->dev.parent)
+ pm_runtime_get_sync(chip->dev.parent);
+
rc = tpm2_prepare_space(chip, space, ordinal, buf);
if (rc)
goto out;
- rc = chip->ops->send(chip, (u8 *) buf, count);
+ rc = chip->ops->send(chip, buf, count);
if (rc < 0) {
if (rc != -EPIPE)
dev_err(&chip->dev,
@@ -480,7 +499,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
goto out;
out_recv:
- len = chip->ops->recv(chip, (u8 *) buf, bufsiz);
+ len = chip->ops->recv(chip, buf, bufsiz);
if (len < 0) {
rc = len;
dev_err(&chip->dev,
@@ -499,27 +518,95 @@ out_recv:
rc = tpm2_commit_space(chip, space, ordinal, buf, &len);
out:
- if (need_locality && chip->ops->relinquish_locality) {
- chip->ops->relinquish_locality(chip, chip->locality);
- chip->locality = -1;
- }
+ if (chip->dev.parent)
+ pm_runtime_put_sync(chip->dev.parent);
+
+ if (need_locality)
+ tpm_relinquish_locality(chip);
+
out_no_locality:
if (chip->ops->clk_enable != NULL)
chip->ops->clk_enable(chip, false);
- if (chip->dev.parent)
- pm_runtime_put_sync(chip->dev.parent);
-
if (!(flags & TPM_TRANSMIT_UNLOCKED))
mutex_unlock(&chip->tpm_mutex);
return rc ? rc : len;
}
/**
- * tmp_transmit_cmd - send a tpm command to the device
+ * tpm_transmit - Internal kernel interface to transmit TPM commands.
+ *
+ * @chip: TPM chip to use
+ * @space: tpm space
+ * @buf: TPM command buffer
+ * @bufsiz: length of the TPM command buffer
+ * @flags: tpm transmit flags - bitmap
+ *
+ * A wrapper around tpm_try_transmit that handles TPM2_RC_RETRY
+ * returns from the TPM and retransmits the command after a delay up
+ * to a maximum wait of TPM2_DURATION_LONG.
+ *
+ * Note: TPM1 never returns TPM2_RC_RETRY so the retry logic is TPM2
+ * only
+ *
+ * Return:
+ * the length of the return when the operation is successful.
+ * A negative number for system errors (errno).
+ */
+ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
+ u8 *buf, size_t bufsiz, unsigned int flags)
+{
+ struct tpm_output_header *header = (struct tpm_output_header *)buf;
+ /* space for header and handles */
+ u8 save[TPM_HEADER_SIZE + 3*sizeof(u32)];
+ unsigned int delay_msec = TPM2_DURATION_SHORT;
+ u32 rc = 0;
+ ssize_t ret;
+ const size_t save_size = min(space ? sizeof(save) : TPM_HEADER_SIZE,
+ bufsiz);
+ /* the command code is where the return code will be */
+ u32 cc = be32_to_cpu(header->return_code);
+
+ /*
+ * Subtlety here: if we have a space, the handles will be
+ * transformed, so when we restore the header we also have to
+ * restore the handles.
+ */
+ memcpy(save, buf, save_size);
+
+ for (;;) {
+ ret = tpm_try_transmit(chip, space, buf, bufsiz, flags);
+ if (ret < 0)
+ break;
+ rc = be32_to_cpu(header->return_code);
+ if (rc != TPM2_RC_RETRY && rc != TPM2_RC_TESTING)
+ break;
+ /*
+ * return immediately if self test returns test
+ * still running to shorten boot time.
+ */
+ if (rc == TPM2_RC_TESTING && cc == TPM2_CC_SELF_TEST)
+ break;
+ delay_msec *= 2;
+ if (delay_msec > TPM2_DURATION_LONG) {
+ if (rc == TPM2_RC_RETRY)
+ dev_err(&chip->dev, "in retry loop\n");
+ else
+ dev_err(&chip->dev,
+ "self test is still running\n");
+ break;
+ }
+ tpm_msleep(delay_msec);
+ memcpy(buf, save, save_size);
+ }
+ return ret;
+}
+/**
+ * tpm_transmit_cmd - send a tpm command to the device
* The function extracts tpm out header return code
*
* @chip: TPM chip to use
+ * @space: tpm space
* @buf: TPM command buffer
* @bufsiz: length of the buffer
* @min_rsp_body_length: minimum expected length of response body
@@ -532,7 +619,7 @@ out_no_locality:
* A positive number for a TPM error.
*/
ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space,
- const void *buf, size_t bufsiz,
+ void *buf, size_t bufsiz,
size_t min_rsp_body_length, unsigned int flags,
const char *desc)
{
@@ -540,7 +627,7 @@ ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space,
int err;
ssize_t len;
- len = tpm_transmit(chip, space, (u8 *)buf, bufsiz, flags);
+ len = tpm_transmit(chip, space, buf, bufsiz, flags);
if (len < 0)
return len;
@@ -666,6 +753,8 @@ int tpm_get_timeouts(struct tpm_chip *chip)
msecs_to_jiffies(TPM2_DURATION_MEDIUM);
chip->duration[TPM_LONG] =
msecs_to_jiffies(TPM2_DURATION_LONG);
+ chip->duration[TPM_LONG_LONG] =
+ msecs_to_jiffies(TPM2_DURATION_LONG_LONG);
chip->flags |= TPM_CHIP_FLAG_HAVE_TIMEOUTS;
return 0;
@@ -754,6 +843,7 @@ int tpm_get_timeouts(struct tpm_chip *chip)
usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_medium));
chip->duration[TPM_LONG] =
usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_long));
+ chip->duration[TPM_LONG_LONG] = 0; /* not used under 1.2 */
/* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
* value wrong and apparently reports msecs rather than usecs. So we
@@ -969,6 +1059,10 @@ int tpm_do_selftest(struct tpm_chip *chip)
loops = jiffies_to_msecs(duration) / delay_msec;
rc = tpm_continue_selftest(chip);
+ if (rc == TPM_ERR_INVALID_POSTINIT) {
+ chip->flags |= TPM_CHIP_FLAG_ALWAYS_POWERED;
+ dev_info(&chip->dev, "TPM not ready (%d)\n", rc);
+ }
/* This may fail if there was no TPM driver during a suspend/resume
* cycle; some may return 10 (BAD_ORDINAL), others 28 (FAILEDSELFTEST)
*/
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index f895fba4e20d..7f2d0f489e9c 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -67,7 +67,9 @@ enum tpm_duration {
TPM_SHORT = 0,
TPM_MEDIUM = 1,
TPM_LONG = 2,
+ TPM_LONG_LONG = 3,
TPM_UNDEFINED,
+ TPM_NUM_DURATIONS = TPM_UNDEFINED,
};
#define TPM_WARN_RETRY 0x800
@@ -79,15 +81,20 @@ enum tpm_duration {
#define TPM_HEADER_SIZE 10
enum tpm2_const {
- TPM2_PLATFORM_PCR = 24,
- TPM2_PCR_SELECT_MIN = ((TPM2_PLATFORM_PCR + 7) / 8),
- TPM2_TIMEOUT_A = 750,
- TPM2_TIMEOUT_B = 2000,
- TPM2_TIMEOUT_C = 200,
- TPM2_TIMEOUT_D = 30,
- TPM2_DURATION_SHORT = 20,
- TPM2_DURATION_MEDIUM = 750,
- TPM2_DURATION_LONG = 2000,
+ TPM2_PLATFORM_PCR = 24,
+ TPM2_PCR_SELECT_MIN = ((TPM2_PLATFORM_PCR + 7) / 8),
+};
+
+enum tpm2_timeouts {
+ TPM2_TIMEOUT_A = 750,
+ TPM2_TIMEOUT_B = 2000,
+ TPM2_TIMEOUT_C = 200,
+ TPM2_TIMEOUT_D = 30,
+ TPM2_DURATION_SHORT = 20,
+ TPM2_DURATION_MEDIUM = 750,
+ TPM2_DURATION_LONG = 2000,
+ TPM2_DURATION_LONG_LONG = 300000,
+ TPM2_DURATION_DEFAULT = 120000,
};
enum tpm2_structures {
@@ -104,10 +111,12 @@ enum tpm2_return_codes {
TPM2_RC_HASH = 0x0083, /* RC_FMT1 */
TPM2_RC_HANDLE = 0x008B,
TPM2_RC_INITIALIZE = 0x0100, /* RC_VER1 */
+ TPM2_RC_FAILURE = 0x0101,
TPM2_RC_DISABLED = 0x0120,
TPM2_RC_COMMAND_CODE = 0x0143,
TPM2_RC_TESTING = 0x090A, /* RC_WARN */
TPM2_RC_REFERENCE_H0 = 0x0910,
+ TPM2_RC_RETRY = 0x0922,
};
enum tpm2_algorithms {
@@ -123,6 +132,7 @@ enum tpm2_algorithms {
enum tpm2_command_codes {
TPM2_CC_FIRST = 0x011F,
+ TPM2_CC_CREATE_PRIMARY = 0x0131,
TPM2_CC_SELF_TEST = 0x0143,
TPM2_CC_STARTUP = 0x0144,
TPM2_CC_SHUTDOWN = 0x0145,
@@ -227,7 +237,7 @@ struct tpm_chip {
unsigned long timeout_c; /* jiffies */
unsigned long timeout_d; /* jiffies */
bool timeout_adjusted;
- unsigned long duration[3]; /* jiffies */
+ unsigned long duration[TPM_NUM_DURATIONS]; /* jiffies */
bool duration_adjusted;
struct dentry *bios_dir[TPM_NUM_EVENT_LOG_FILES];
@@ -506,7 +516,7 @@ enum tpm_transmit_flags {
ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
u8 *buf, size_t bufsiz, unsigned int flags);
ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space,
- const void *buf, size_t bufsiz,
+ void *buf, size_t bufsiz,
size_t min_rsp_body_length, unsigned int flags,
const char *desc);
int tpm_startup(struct tpm_chip *chip);
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index a700f8f9ead7..96c77c8e7f40 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -31,10 +31,6 @@ struct tpm2_startup_in {
__be16 startup_type;
} __packed;
-struct tpm2_self_test_in {
- u8 full_test;
-} __packed;
-
struct tpm2_get_tpm_pt_in {
__be32 cap_id;
__be32 property_id;
@@ -60,7 +56,6 @@ struct tpm2_get_random_out {
union tpm2_cmd_params {
struct tpm2_startup_in startup_in;
- struct tpm2_self_test_in selftest_in;
struct tpm2_get_tpm_pt_in get_tpm_pt_in;
struct tpm2_get_tpm_pt_out get_tpm_pt_out;
struct tpm2_get_random_in getrandom_in;
@@ -90,6 +85,8 @@ static struct tpm2_hash tpm2_hash_map[] = {
* of time the chip could take to return the result. The values
* of the SHORT, MEDIUM, and LONG durations are taken from the
* PC Client Profile (PTP) specification.
+ * LONG_LONG is for commands that generates keys which empirically
+ * takes longer time on some systems.
*/
static const u8 tpm2_ordinal_duration[TPM2_CC_LAST - TPM2_CC_FIRST + 1] = {
TPM_UNDEFINED, /* 11F */
@@ -110,7 +107,7 @@ static const u8 tpm2_ordinal_duration[TPM2_CC_LAST - TPM2_CC_FIRST + 1] = {
TPM_UNDEFINED, /* 12e */
TPM_UNDEFINED, /* 12f */
TPM_UNDEFINED, /* 130 */
- TPM_UNDEFINED, /* 131 */
+ TPM_LONG_LONG, /* 131 */
TPM_UNDEFINED, /* 132 */
TPM_UNDEFINED, /* 133 */
TPM_UNDEFINED, /* 134 */
@@ -144,7 +141,7 @@ static const u8 tpm2_ordinal_duration[TPM2_CC_LAST - TPM2_CC_FIRST + 1] = {
TPM_UNDEFINED, /* 150 */
TPM_UNDEFINED, /* 151 */
TPM_UNDEFINED, /* 152 */
- TPM_UNDEFINED, /* 153 */
+ TPM_LONG_LONG, /* 153 */
TPM_UNDEFINED, /* 154 */
TPM_UNDEFINED, /* 155 */
TPM_UNDEFINED, /* 156 */
@@ -821,22 +818,12 @@ unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
duration = chip->duration[index];
if (duration <= 0)
- duration = 2 * 60 * HZ;
+ duration = msecs_to_jiffies(TPM2_DURATION_DEFAULT);
return duration;
}
EXPORT_SYMBOL_GPL(tpm2_calc_ordinal_duration);
-#define TPM2_SELF_TEST_IN_SIZE \
- (sizeof(struct tpm_input_header) + \
- sizeof(struct tpm2_self_test_in))
-
-static const struct tpm_input_header tpm2_selftest_header = {
- .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
- .length = cpu_to_be32(TPM2_SELF_TEST_IN_SIZE),
- .ordinal = cpu_to_be32(TPM2_CC_SELF_TEST)
-};
-
/**
* tpm2_do_selftest() - ensure that all self tests have passed
*
@@ -852,27 +839,24 @@ static const struct tpm_input_header tpm2_selftest_header = {
*/
static int tpm2_do_selftest(struct tpm_chip *chip)
{
+ struct tpm_buf buf;
+ int full;
int rc;
- unsigned int delay_msec = 10;
- long duration;
- struct tpm2_cmd cmd;
- duration = jiffies_to_msecs(
- tpm2_calc_ordinal_duration(chip, TPM2_CC_SELF_TEST));
-
- while (1) {
- cmd.header.in = tpm2_selftest_header;
- cmd.params.selftest_in.full_test = 0;
-
- rc = tpm_transmit_cmd(chip, NULL, &cmd, TPM2_SELF_TEST_IN_SIZE,
- 0, 0, "continue selftest");
+ for (full = 0; full < 2; full++) {
+ rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SELF_TEST);
+ if (rc)
+ return rc;
- if (rc != TPM2_RC_TESTING || delay_msec >= duration)
- break;
+ tpm_buf_append_u8(&buf, full);
+ rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0,
+ "attempting the self test");
+ tpm_buf_destroy(&buf);
- /* wait longer than before */
- delay_msec *= 2;
- tpm_msleep(delay_msec);
+ if (rc == TPM2_RC_TESTING)
+ rc = TPM2_RC_SUCCESS;
+ if (rc == TPM2_RC_INITIALIZE || rc == TPM2_RC_SUCCESS)
+ return rc;
}
return rc;
@@ -1058,10 +1042,8 @@ int tpm2_auto_startup(struct tpm_chip *chip)
goto out;
rc = tpm2_do_selftest(chip);
- if (rc != 0 && rc != TPM2_RC_INITIALIZE) {
- dev_err(&chip->dev, "TPM self test failed\n");
+ if (rc && rc != TPM2_RC_INITIALIZE)
goto out;
- }
if (rc == TPM2_RC_INITIALIZE) {
rc = tpm_startup(chip);
@@ -1069,10 +1051,8 @@ int tpm2_auto_startup(struct tpm_chip *chip)
goto out;
rc = tpm2_do_selftest(chip);
- if (rc) {
- dev_err(&chip->dev, "TPM self test failed\n");
+ if (rc)
goto out;
- }
}
rc = tpm2_get_pcr_allocation(chip);
diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c
index 7b3c2a8aa9de..7f78482cd157 100644
--- a/drivers/char/tpm/tpm_crb.c
+++ b/drivers/char/tpm/tpm_crb.c
@@ -112,6 +112,25 @@ struct tpm2_crb_smc {
u32 smc_func_id;
};
+static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value,
+ unsigned long timeout)
+{
+ ktime_t start;
+ ktime_t stop;
+
+ start = ktime_get();
+ stop = ktime_add(start, ms_to_ktime(timeout));
+
+ do {
+ if ((ioread32(reg) & mask) == value)
+ return true;
+
+ usleep_range(50, 100);
+ } while (ktime_before(ktime_get(), stop));
+
+ return ((ioread32(reg) & mask) == value);
+}
+
/**
* crb_go_idle - request tpm crb device to go the idle state
*
@@ -128,7 +147,7 @@ struct tpm2_crb_smc {
*
* Return: 0 always
*/
-static int __maybe_unused crb_go_idle(struct device *dev, struct crb_priv *priv)
+static int crb_go_idle(struct device *dev, struct crb_priv *priv)
{
if ((priv->sm == ACPI_TPM2_START_METHOD) ||
(priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) ||
@@ -136,30 +155,17 @@ static int __maybe_unused crb_go_idle(struct device *dev, struct crb_priv *priv)
return 0;
iowrite32(CRB_CTRL_REQ_GO_IDLE, &priv->regs_t->ctrl_req);
- /* we don't really care when this settles */
+ if (!crb_wait_for_reg_32(&priv->regs_t->ctrl_req,
+ CRB_CTRL_REQ_GO_IDLE/* mask */,
+ 0, /* value */
+ TPM2_TIMEOUT_C)) {
+ dev_warn(dev, "goIdle timed out\n");
+ return -ETIME;
+ }
return 0;
}
-static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value,
- unsigned long timeout)
-{
- ktime_t start;
- ktime_t stop;
-
- start = ktime_get();
- stop = ktime_add(start, ms_to_ktime(timeout));
-
- do {
- if ((ioread32(reg) & mask) == value)
- return true;
-
- usleep_range(50, 100);
- } while (ktime_before(ktime_get(), stop));
-
- return false;
-}
-
/**
* crb_cmd_ready - request tpm crb device to enter ready state
*
@@ -175,8 +181,7 @@ static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value,
*
* Return: 0 on success -ETIME on timeout;
*/
-static int __maybe_unused crb_cmd_ready(struct device *dev,
- struct crb_priv *priv)
+static int crb_cmd_ready(struct device *dev, struct crb_priv *priv)
{
if ((priv->sm == ACPI_TPM2_START_METHOD) ||
(priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) ||
@@ -195,11 +200,11 @@ static int __maybe_unused crb_cmd_ready(struct device *dev,
return 0;
}
-static int crb_request_locality(struct tpm_chip *chip, int loc)
+static int __crb_request_locality(struct device *dev,
+ struct crb_priv *priv, int loc)
{
- struct crb_priv *priv = dev_get_drvdata(&chip->dev);
u32 value = CRB_LOC_STATE_LOC_ASSIGNED |
- CRB_LOC_STATE_TPM_REG_VALID_STS;
+ CRB_LOC_STATE_TPM_REG_VALID_STS;
if (!priv->regs_h)
return 0;
@@ -207,21 +212,45 @@ static int crb_request_locality(struct tpm_chip *chip, int loc)
iowrite32(CRB_LOC_CTRL_REQUEST_ACCESS, &priv->regs_h->loc_ctrl);
if (!crb_wait_for_reg_32(&priv->regs_h->loc_state, value, value,
TPM2_TIMEOUT_C)) {
- dev_warn(&chip->dev, "TPM_LOC_STATE_x.requestAccess timed out\n");
+ dev_warn(dev, "TPM_LOC_STATE_x.requestAccess timed out\n");
return -ETIME;
}
return 0;
}
-static void crb_relinquish_locality(struct tpm_chip *chip, int loc)
+static int crb_request_locality(struct tpm_chip *chip, int loc)
{
struct crb_priv *priv = dev_get_drvdata(&chip->dev);
+ return __crb_request_locality(&chip->dev, priv, loc);
+}
+
+static int __crb_relinquish_locality(struct device *dev,
+ struct crb_priv *priv, int loc)
+{
+ u32 mask = CRB_LOC_STATE_LOC_ASSIGNED |
+ CRB_LOC_STATE_TPM_REG_VALID_STS;
+ u32 value = CRB_LOC_STATE_TPM_REG_VALID_STS;
+
if (!priv->regs_h)
- return;
+ return 0;
iowrite32(CRB_LOC_CTRL_RELINQUISH, &priv->regs_h->loc_ctrl);
+ if (!crb_wait_for_reg_32(&priv->regs_h->loc_state, mask, value,
+ TPM2_TIMEOUT_C)) {
+ dev_warn(dev, "TPM_LOC_STATE_x.requestAccess timed out\n");
+ return -ETIME;
+ }
+
+ return 0;
+}
+
+static int crb_relinquish_locality(struct tpm_chip *chip, int loc)
+{
+ struct crb_priv *priv = dev_get_drvdata(&chip->dev);
+
+ return __crb_relinquish_locality(&chip->dev, priv, loc);
}
static u8 crb_status(struct tpm_chip *chip)
@@ -442,6 +471,7 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
u32 pa_high, pa_low;
u64 cmd_pa;
u32 cmd_size;
+ __le64 __rsp_pa;
u64 rsp_pa;
u32 rsp_size;
int ret;
@@ -475,6 +505,10 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
dev_warn(dev, FW_BUG "Bad ACPI memory layout");
}
+ ret = __crb_request_locality(dev, priv, 0);
+ if (ret)
+ return ret;
+
priv->regs_t = crb_map_res(dev, priv, &io_res, buf->control_address,
sizeof(struct crb_regs_tail));
if (IS_ERR(priv->regs_t))
@@ -503,8 +537,8 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
goto out;
}
- memcpy_fromio(&rsp_pa, &priv->regs_t->ctrl_rsp_pa, 8);
- rsp_pa = le64_to_cpu(rsp_pa);
+ memcpy_fromio(&__rsp_pa, &priv->regs_t->ctrl_rsp_pa, 8);
+ rsp_pa = le64_to_cpu(__rsp_pa);
rsp_size = crb_fixup_cmd_size(dev, &io_res, rsp_pa,
ioread32(&priv->regs_t->ctrl_rsp_size));
@@ -531,6 +565,8 @@ out:
crb_go_idle(dev, priv);
+ __crb_relinquish_locality(dev, priv, 0);
+
return ret;
}
@@ -588,10 +624,14 @@ static int crb_acpi_add(struct acpi_device *device)
chip->acpi_dev_handle = device->handle;
chip->flags = TPM_CHIP_FLAG_TPM2;
- rc = crb_cmd_ready(dev, priv);
+ rc = __crb_request_locality(dev, priv, 0);
if (rc)
return rc;
+ rc = crb_cmd_ready(dev, priv);
+ if (rc)
+ goto out;
+
pm_runtime_get_noresume(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
@@ -601,12 +641,15 @@ static int crb_acpi_add(struct acpi_device *device)
crb_go_idle(dev, priv);
pm_runtime_put_noidle(dev);
pm_runtime_disable(dev);
- return rc;
+ goto out;
}
- pm_runtime_put(dev);
+ pm_runtime_put_sync(dev);
- return 0;
+out:
+ __crb_relinquish_locality(dev, priv, 0);
+
+ return rc;
}
static int crb_acpi_remove(struct acpi_device *device)
diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c
index da074e3db19b..5a1f47b43947 100644
--- a/drivers/char/tpm/tpm_tis_core.c
+++ b/drivers/char/tpm/tpm_tis_core.c
@@ -143,11 +143,13 @@ static bool check_locality(struct tpm_chip *chip, int l)
return false;
}
-static void release_locality(struct tpm_chip *chip, int l)
+static int release_locality(struct tpm_chip *chip, int l)
{
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_ACTIVE_LOCALITY);
+
+ return 0;
}
static int request_locality(struct tpm_chip *chip, int l)
diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h
index d5c6a2e952b3..f6e1dbe212a7 100644
--- a/drivers/char/tpm/tpm_tis_core.h
+++ b/drivers/char/tpm/tpm_tis_core.h
@@ -62,10 +62,10 @@ enum tis_defaults {
/* Some timeout values are needed before it is known whether the chip is
* TPM 1.0 or TPM 2.0.
*/
-#define TIS_TIMEOUT_A_MAX max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_A)
-#define TIS_TIMEOUT_B_MAX max(TIS_LONG_TIMEOUT, TPM2_TIMEOUT_B)
-#define TIS_TIMEOUT_C_MAX max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_C)
-#define TIS_TIMEOUT_D_MAX max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_D)
+#define TIS_TIMEOUT_A_MAX max_t(int, TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_A)
+#define TIS_TIMEOUT_B_MAX max_t(int, TIS_LONG_TIMEOUT, TPM2_TIMEOUT_B)
+#define TIS_TIMEOUT_C_MAX max_t(int, TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_C)
+#define TIS_TIMEOUT_D_MAX max_t(int, TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_D)
#define TPM_ACCESS(l) (0x0000 | ((l) << 12))
#define TPM_INT_ENABLE(l) (0x0008 | ((l) << 12))
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 468f06134012..21085515814f 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -422,7 +422,7 @@ static void reclaim_dma_bufs(void)
}
}
-static struct port_buffer *alloc_buf(struct virtqueue *vq, size_t buf_size,
+static struct port_buffer *alloc_buf(struct virtio_device *vdev, size_t buf_size,
int pages)
{
struct port_buffer *buf;
@@ -445,16 +445,16 @@ static struct port_buffer *alloc_buf(struct virtqueue *vq, size_t buf_size,
return buf;
}
- if (is_rproc_serial(vq->vdev)) {
+ if (is_rproc_serial(vdev)) {
/*
* Allocate DMA memory from ancestor. When a virtio
* device is created by remoteproc, the DMA memory is
* associated with the grandparent device:
* vdev => rproc => platform-dev.
*/
- if (!vq->vdev->dev.parent || !vq->vdev->dev.parent->parent)
+ if (!vdev->dev.parent || !vdev->dev.parent->parent)
goto free_buf;
- buf->dev = vq->vdev->dev.parent->parent;
+ buf->dev = vdev->dev.parent->parent;
/* Increase device refcnt to avoid freeing it */
get_device(buf->dev);
@@ -838,7 +838,7 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf,
count = min((size_t)(32 * 1024), count);
- buf = alloc_buf(port->out_vq, count, 0);
+ buf = alloc_buf(port->portdev->vdev, count, 0);
if (!buf)
return -ENOMEM;
@@ -957,7 +957,7 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe,
if (ret < 0)
goto error_out;
- buf = alloc_buf(port->out_vq, 0, pipe->nrbufs);
+ buf = alloc_buf(port->portdev->vdev, 0, pipe->nrbufs);
if (!buf) {
ret = -ENOMEM;
goto error_out;
@@ -1374,7 +1374,7 @@ static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock)
nr_added_bufs = 0;
do {
- buf = alloc_buf(vq, PAGE_SIZE, 0);
+ buf = alloc_buf(vq->vdev, PAGE_SIZE, 0);
if (!buf)
break;
@@ -1402,7 +1402,6 @@ static int add_port(struct ports_device *portdev, u32 id)
{
char debugfs_name[16];
struct port *port;
- struct port_buffer *buf;
dev_t devt;
unsigned int nr_added_bufs;
int err;
@@ -1513,8 +1512,6 @@ static int add_port(struct ports_device *portdev, u32 id)
return 0;
free_inbufs:
- while ((buf = virtqueue_detach_unused_buf(port->in_vq)))
- free_buf(buf, true);
free_device:
device_destroy(pdrvdata.class, port->dev->devt);
free_cdev:
@@ -1539,34 +1536,14 @@ static void remove_port(struct kref *kref)
static void remove_port_data(struct port *port)
{
- struct port_buffer *buf;
-
spin_lock_irq(&port->inbuf_lock);
/* Remove unused data this port might have received. */
discard_port_data(port);
spin_unlock_irq(&port->inbuf_lock);
- /* Remove buffers we queued up for the Host to send us data in. */
- do {
- spin_lock_irq(&port->inbuf_lock);
- buf = virtqueue_detach_unused_buf(port->in_vq);
- spin_unlock_irq(&port->inbuf_lock);
- if (buf)
- free_buf(buf, true);
- } while (buf);
-
spin_lock_irq(&port->outvq_lock);
reclaim_consumed_buffers(port);
spin_unlock_irq(&port->outvq_lock);
-
- /* Free pending buffers from the out-queue. */
- do {
- spin_lock_irq(&port->outvq_lock);
- buf = virtqueue_detach_unused_buf(port->out_vq);
- spin_unlock_irq(&port->outvq_lock);
- if (buf)
- free_buf(buf, true);
- } while (buf);
}
/*
@@ -1791,13 +1768,24 @@ static void control_work_handler(struct work_struct *work)
spin_unlock(&portdev->c_ivq_lock);
}
+static void flush_bufs(struct virtqueue *vq, bool can_sleep)
+{
+ struct port_buffer *buf;
+ unsigned int len;
+
+ while ((buf = virtqueue_get_buf(vq, &len)))
+ free_buf(buf, can_sleep);
+}
+
static void out_intr(struct virtqueue *vq)
{
struct port *port;
port = find_port_by_vq(vq->vdev->priv, vq);
- if (!port)
+ if (!port) {
+ flush_bufs(vq, false);
return;
+ }
wake_up_interruptible(&port->waitqueue);
}
@@ -1808,8 +1796,10 @@ static void in_intr(struct virtqueue *vq)
unsigned long flags;
port = find_port_by_vq(vq->vdev->priv, vq);
- if (!port)
+ if (!port) {
+ flush_bufs(vq, false);
return;
+ }
spin_lock_irqsave(&port->inbuf_lock, flags);
port->inbuf = get_inbuf(port);
@@ -1984,24 +1974,54 @@ static const struct file_operations portdev_fops = {
static void remove_vqs(struct ports_device *portdev)
{
+ struct virtqueue *vq;
+
+ virtio_device_for_each_vq(portdev->vdev, vq) {
+ struct port_buffer *buf;
+
+ flush_bufs(vq, true);
+ while ((buf = virtqueue_detach_unused_buf(vq)))
+ free_buf(buf, true);
+ }
portdev->vdev->config->del_vqs(portdev->vdev);
kfree(portdev->in_vqs);
kfree(portdev->out_vqs);
}
-static void remove_controlq_data(struct ports_device *portdev)
+static void virtcons_remove(struct virtio_device *vdev)
{
- struct port_buffer *buf;
- unsigned int len;
+ struct ports_device *portdev;
+ struct port *port, *port2;
- if (!use_multiport(portdev))
- return;
+ portdev = vdev->priv;
- while ((buf = virtqueue_get_buf(portdev->c_ivq, &len)))
- free_buf(buf, true);
+ spin_lock_irq(&pdrvdata_lock);
+ list_del(&portdev->list);
+ spin_unlock_irq(&pdrvdata_lock);
- while ((buf = virtqueue_detach_unused_buf(portdev->c_ivq)))
- free_buf(buf, true);
+ /* Disable interrupts for vqs */
+ vdev->config->reset(vdev);
+ /* Finish up work that's lined up */
+ if (use_multiport(portdev))
+ cancel_work_sync(&portdev->control_work);
+ else
+ cancel_work_sync(&portdev->config_work);
+
+ list_for_each_entry_safe(port, port2, &portdev->ports, list)
+ unplug_port(port);
+
+ unregister_chrdev(portdev->chr_major, "virtio-portsdev");
+
+ /*
+ * When yanking out a device, we immediately lose the
+ * (device-side) queues. So there's no point in keeping the
+ * guest side around till we drop our final reference. This
+ * also means that any ports which are in an open state will
+ * have to just stop using the port, as the vqs are going
+ * away.
+ */
+ remove_vqs(portdev);
+ kfree(portdev);
}
/*
@@ -2070,6 +2090,7 @@ static int virtcons_probe(struct virtio_device *vdev)
spin_lock_init(&portdev->ports_lock);
INIT_LIST_HEAD(&portdev->ports);
+ INIT_LIST_HEAD(&portdev->list);
virtio_device_ready(portdev->vdev);
@@ -2087,8 +2108,15 @@ static int virtcons_probe(struct virtio_device *vdev)
if (!nr_added_bufs) {
dev_err(&vdev->dev,
"Error allocating buffers for control queue\n");
- err = -ENOMEM;
- goto free_vqs;
+ /*
+ * The host might want to notify mgmt sw about device
+ * add failure.
+ */
+ __send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID,
+ VIRTIO_CONSOLE_DEVICE_READY, 0);
+ /* Device was functional: we need full cleanup. */
+ virtcons_remove(vdev);
+ return -ENOMEM;
}
} else {
/*
@@ -2119,11 +2147,6 @@ static int virtcons_probe(struct virtio_device *vdev)
return 0;
-free_vqs:
- /* The host might want to notify mgmt sw about device add failure */
- __send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID,
- VIRTIO_CONSOLE_DEVICE_READY, 0);
- remove_vqs(portdev);
free_chrdev:
unregister_chrdev(portdev->chr_major, "virtio-portsdev");
free:
@@ -2132,43 +2155,6 @@ fail:
return err;
}
-static void virtcons_remove(struct virtio_device *vdev)
-{
- struct ports_device *portdev;
- struct port *port, *port2;
-
- portdev = vdev->priv;
-
- spin_lock_irq(&pdrvdata_lock);
- list_del(&portdev->list);
- spin_unlock_irq(&pdrvdata_lock);
-
- /* Disable interrupts for vqs */
- vdev->config->reset(vdev);
- /* Finish up work that's lined up */
- if (use_multiport(portdev))
- cancel_work_sync(&portdev->control_work);
- else
- cancel_work_sync(&portdev->config_work);
-
- list_for_each_entry_safe(port, port2, &portdev->ports, list)
- unplug_port(port);
-
- unregister_chrdev(portdev->chr_major, "virtio-portsdev");
-
- /*
- * When yanking out a device, we immediately lose the
- * (device-side) queues. So there's no point in keeping the
- * guest side around till we drop our final reference. This
- * also means that any ports which are in an open state will
- * have to just stop using the port, as the vqs are going
- * away.
- */
- remove_controlq_data(portdev);
- remove_vqs(portdev);
- kfree(portdev);
-}
-
static struct virtio_device_id id_table[] = {
{ VIRTIO_ID_CONSOLE, VIRTIO_DEV_ANY_ID },
{ 0 },
@@ -2209,7 +2195,6 @@ static int virtcons_freeze(struct virtio_device *vdev)
*/
if (use_multiport(portdev))
virtqueue_disable_cb(portdev->c_ivq);
- remove_controlq_data(portdev);
list_for_each_entry(port, &portdev->ports, list) {
virtqueue_disable_cb(port->in_vq);
diff --git a/drivers/char/xillybus/xillybus_pcie.c b/drivers/char/xillybus/xillybus_pcie.c
index dff2d1538164..05e5324f60bd 100644
--- a/drivers/char/xillybus/xillybus_pcie.c
+++ b/drivers/char/xillybus/xillybus_pcie.c
@@ -24,7 +24,6 @@ MODULE_LICENSE("GPL v2");
#define PCI_DEVICE_ID_XILLYBUS 0xebeb
-#define PCI_VENDOR_ID_ALTERA 0x1172
#define PCI_VENDOR_ID_ACTEL 0x11aa
#define PCI_VENDOR_ID_LATTICE 0x1204
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 98ce9fc6e6c0..41492e980ef4 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -55,13 +55,25 @@ config COMMON_CLK_RK808
by control register.
config COMMON_CLK_HI655X
- tristate "Clock driver for Hi655x"
- depends on MFD_HI655X_PMIC || COMPILE_TEST
+ tristate "Clock driver for Hi655x" if EXPERT
+ depends on (MFD_HI655X_PMIC || COMPILE_TEST)
+ depends on REGMAP
+ default MFD_HI655X_PMIC
---help---
This driver supports the hi655x PMIC clock. This
multi-function device has one fixed-rate oscillator, clocked
at 32KHz.
+config COMMON_CLK_SCMI
+ tristate "Clock driver controlled via SCMI interface"
+ depends on ARM_SCMI_PROTOCOL || COMPILE_TEST
+ ---help---
+ This driver provides support for clocks that are controlled
+ by firmware that implements the SCMI interface.
+
+ This driver uses SCMI Message Protocol to interact with the
+ firmware providing all the clock controls.
+
config COMMON_CLK_SCPI
tristate "Clock driver controlled via SCPI interface"
depends on ARM_SCPI_PROTOCOL || COMPILE_TEST
@@ -91,6 +103,15 @@ config COMMON_CLK_SI514
This driver supports the Silicon Labs 514 programmable clock
generator.
+config COMMON_CLK_SI544
+ tristate "Clock driver for SiLabs 544 devices"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ ---help---
+ This driver supports the Silicon Labs 544 programmable clock
+ generator.
+
config COMMON_CLK_SI570
tristate "Clock driver for SiLabs 570 and compatible devices"
depends on I2C
@@ -238,6 +259,26 @@ config COMMON_CLK_VC5
This driver supports the IDT VersaClock 5 and VersaClock 6
programmable clock generators.
+config COMMON_CLK_STM32MP157
+ def_bool COMMON_CLK && MACH_STM32MP157
+ help
+ ---help---
+ Support for stm32mp157 SoC family clocks
+
+config COMMON_CLK_STM32F
+ bool "Clock driver for stm32f4 and stm32f7 SoC families"
+ depends on MACH_STM32F429 || MACH_STM32F469 || MACH_STM32F746
+ help
+ ---help---
+ Support for stm32f4 and stm32f7 SoC families clocks
+
+config COMMON_CLK_STM32H7
+ bool "Clock driver for stm32h7 SoC family"
+ depends on MACH_STM32H743
+ help
+ ---help---
+ Support for stm32h7 SoC family clocks
+
source "drivers/clk/bcm/Kconfig"
source "drivers/clk/hisilicon/Kconfig"
source "drivers/clk/imgtec/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 71ec41e6364f..de6d06ac790b 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -41,12 +41,15 @@ obj-$(CONFIG_CLK_QORIQ) += clk-qoriq.o
obj-$(CONFIG_COMMON_CLK_RK808) += clk-rk808.o
obj-$(CONFIG_COMMON_CLK_HI655X) += clk-hi655x.o
obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o
+obj-$(CONFIG_COMMON_CLK_SCMI) += clk-scmi.o
obj-$(CONFIG_COMMON_CLK_SCPI) += clk-scpi.o
obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o
obj-$(CONFIG_COMMON_CLK_SI514) += clk-si514.o
+obj-$(CONFIG_COMMON_CLK_SI544) += clk-si544.o
obj-$(CONFIG_COMMON_CLK_SI570) += clk-si570.o
-obj-$(CONFIG_ARCH_STM32) += clk-stm32f4.o
-obj-$(CONFIG_ARCH_STM32) += clk-stm32h7.o
+obj-$(CONFIG_COMMON_CLK_STM32F) += clk-stm32f4.o
+obj-$(CONFIG_COMMON_CLK_STM32H7) += clk-stm32h7.o
+obj-$(CONFIG_COMMON_CLK_STM32MP157) += clk-stm32mp1.o
obj-$(CONFIG_ARCH_TANGO) += clk-tango4.o
obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o
obj-$(CONFIG_ARCH_U300) += clk-u300.o
@@ -61,6 +64,7 @@ obj-$(CONFIG_ARCH_ARTPEC) += axis/
obj-$(CONFIG_ARC_PLAT_AXS10X) += axs10x/
obj-y += bcm/
obj-$(CONFIG_ARCH_BERLIN) += berlin/
+obj-$(CONFIG_ARCH_DAVINCI) += davinci/
obj-$(CONFIG_H8300) += h8300/
obj-$(CONFIG_ARCH_HISI) += hisilicon/
obj-y += imgtec/
@@ -88,6 +92,7 @@ obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/
obj-$(CONFIG_PLAT_SPEAR) += spear/
obj-$(CONFIG_ARCH_SPRD) += sprd/
obj-$(CONFIG_ARCH_STI) += st/
+obj-$(CONFIG_ARCH_STRATIX10) += socfpga/
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
obj-$(CONFIG_ARCH_SUNXI) += sunxi-ng/
obj-$(CONFIG_ARCH_TEGRA) += tegra/
diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c
index a07f6451694a..fa0d5c8611a0 100644
--- a/drivers/clk/bcm/clk-bcm2835.c
+++ b/drivers/clk/bcm/clk-bcm2835.c
@@ -602,9 +602,7 @@ static void bcm2835_pll_off(struct clk_hw *hw)
const struct bcm2835_pll_data *data = pll->data;
spin_lock(&cprman->regs_lock);
- cprman_write(cprman, data->cm_ctrl_reg,
- cprman_read(cprman, data->cm_ctrl_reg) |
- CM_PLL_ANARST);
+ cprman_write(cprman, data->cm_ctrl_reg, CM_PLL_ANARST);
cprman_write(cprman, data->a2w_ctrl_reg,
cprman_read(cprman, data->a2w_ctrl_reg) |
A2W_PLL_CTRL_PWRDN);
@@ -640,6 +638,10 @@ static int bcm2835_pll_on(struct clk_hw *hw)
cpu_relax();
}
+ cprman_write(cprman, data->a2w_ctrl_reg,
+ cprman_read(cprman, data->a2w_ctrl_reg) |
+ A2W_PLL_CTRL_PRST_DISABLE);
+
return 0;
}
diff --git a/drivers/clk/clk-cs2000-cp.c b/drivers/clk/clk-cs2000-cp.c
index e8ea81c30f0c..c58019750b7e 100644
--- a/drivers/clk/clk-cs2000-cp.c
+++ b/drivers/clk/clk-cs2000-cp.c
@@ -549,7 +549,7 @@ static int cs2000_resume(struct device *dev)
}
static const struct dev_pm_ops cs2000_pm_ops = {
- .resume_early = cs2000_resume,
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL, cs2000_resume)
};
static struct i2c_driver cs2000_driver = {
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index b49942b9fe50..b6234a5da12d 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -28,12 +28,10 @@
* parent - fixed parent. No clk_set_parent support
*/
-#define div_mask(width) ((1 << (width)) - 1)
-
static unsigned int _get_table_maxdiv(const struct clk_div_table *table,
u8 width)
{
- unsigned int maxdiv = 0, mask = div_mask(width);
+ unsigned int maxdiv = 0, mask = clk_div_mask(width);
const struct clk_div_table *clkt;
for (clkt = table; clkt->div; clkt++)
@@ -57,12 +55,12 @@ static unsigned int _get_maxdiv(const struct clk_div_table *table, u8 width,
unsigned long flags)
{
if (flags & CLK_DIVIDER_ONE_BASED)
- return div_mask(width);
+ return clk_div_mask(width);
if (flags & CLK_DIVIDER_POWER_OF_TWO)
- return 1 << div_mask(width);
+ return 1 << clk_div_mask(width);
if (table)
return _get_table_maxdiv(table, width);
- return div_mask(width) + 1;
+ return clk_div_mask(width) + 1;
}
static unsigned int _get_table_div(const struct clk_div_table *table,
@@ -84,7 +82,7 @@ static unsigned int _get_div(const struct clk_div_table *table,
if (flags & CLK_DIVIDER_POWER_OF_TWO)
return 1 << val;
if (flags & CLK_DIVIDER_MAX_AT_ZERO)
- return val ? val : div_mask(width) + 1;
+ return val ? val : clk_div_mask(width) + 1;
if (table)
return _get_table_div(table, val);
return val + 1;
@@ -109,7 +107,7 @@ static unsigned int _get_val(const struct clk_div_table *table,
if (flags & CLK_DIVIDER_POWER_OF_TWO)
return __ffs(div);
if (flags & CLK_DIVIDER_MAX_AT_ZERO)
- return (div == div_mask(width) + 1) ? 0 : div;
+ return (div == clk_div_mask(width) + 1) ? 0 : div;
if (table)
return _get_table_val(table, div);
return div - 1;
@@ -141,7 +139,7 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
unsigned int val;
val = clk_readl(divider->reg) >> divider->shift;
- val &= div_mask(divider->width);
+ val &= clk_div_mask(divider->width);
return divider_recalc_rate(hw, parent_rate, val, divider->table,
divider->flags, divider->width);
@@ -344,19 +342,43 @@ long divider_round_rate_parent(struct clk_hw *hw, struct clk_hw *parent,
}
EXPORT_SYMBOL_GPL(divider_round_rate_parent);
+long divider_ro_round_rate_parent(struct clk_hw *hw, struct clk_hw *parent,
+ unsigned long rate, unsigned long *prate,
+ const struct clk_div_table *table, u8 width,
+ unsigned long flags, unsigned int val)
+{
+ int div;
+
+ div = _get_div(table, val, flags, width);
+
+ /* Even a read-only clock can propagate a rate change */
+ if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
+ if (!parent)
+ return -EINVAL;
+
+ *prate = clk_hw_round_rate(parent, rate * div);
+ }
+
+ return DIV_ROUND_UP_ULL((u64)*prate, div);
+}
+EXPORT_SYMBOL_GPL(divider_ro_round_rate_parent);
+
+
static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
struct clk_divider *divider = to_clk_divider(hw);
- int bestdiv;
/* if read only, just return current value */
if (divider->flags & CLK_DIVIDER_READ_ONLY) {
- bestdiv = clk_readl(divider->reg) >> divider->shift;
- bestdiv &= div_mask(divider->width);
- bestdiv = _get_div(divider->table, bestdiv, divider->flags,
- divider->width);
- return DIV_ROUND_UP_ULL((u64)*prate, bestdiv);
+ u32 val;
+
+ val = clk_readl(divider->reg) >> divider->shift;
+ val &= clk_div_mask(divider->width);
+
+ return divider_ro_round_rate(hw, rate, prate, divider->table,
+ divider->width, divider->flags,
+ val);
}
return divider_round_rate(hw, rate, prate, divider->table,
@@ -376,7 +398,7 @@ int divider_get_val(unsigned long rate, unsigned long parent_rate,
value = _get_val(table, div, flags, width);
- return min_t(unsigned int, value, div_mask(width));
+ return min_t(unsigned int, value, clk_div_mask(width));
}
EXPORT_SYMBOL_GPL(divider_get_val);
@@ -399,10 +421,10 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
__acquire(divider->lock);
if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
- val = div_mask(divider->width) << (divider->shift + 16);
+ val = clk_div_mask(divider->width) << (divider->shift + 16);
} else {
val = clk_readl(divider->reg);
- val &= ~(div_mask(divider->width) << divider->shift);
+ val &= ~(clk_div_mask(divider->width) << divider->shift);
}
val |= (u32)value << divider->shift;
clk_writel(val, divider->reg);
diff --git a/drivers/clk/clk-gpio.c b/drivers/clk/clk-gpio.c
index 151513c655c3..40af4fbab4d2 100644
--- a/drivers/clk/clk-gpio.c
+++ b/drivers/clk/clk-gpio.c
@@ -73,14 +73,14 @@ static u8 clk_gpio_mux_get_parent(struct clk_hw *hw)
{
struct clk_gpio *clk = to_clk_gpio(hw);
- return gpiod_get_value(clk->gpiod);
+ return gpiod_get_value_cansleep(clk->gpiod);
}
static int clk_gpio_mux_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_gpio *clk = to_clk_gpio(hw);
- gpiod_set_value(clk->gpiod, index);
+ gpiod_set_value_cansleep(clk->gpiod, index);
return 0;
}
diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c
index 39cabe157163..ac4a042f8658 100644
--- a/drivers/clk/clk-mux.c
+++ b/drivers/clk/clk-mux.c
@@ -26,35 +26,24 @@
* parent - parent is adjustable through clk_set_parent
*/
-static u8 clk_mux_get_parent(struct clk_hw *hw)
+int clk_mux_val_to_index(struct clk_hw *hw, u32 *table, unsigned int flags,
+ unsigned int val)
{
- struct clk_mux *mux = to_clk_mux(hw);
int num_parents = clk_hw_get_num_parents(hw);
- u32 val;
- /*
- * FIXME need a mux-specific flag to determine if val is bitwise or numeric
- * e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges from 0x1
- * to 0x7 (index starts at one)
- * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so
- * val = 0x4 really means "bit 2, index starts at bit 0"
- */
- val = clk_readl(mux->reg) >> mux->shift;
- val &= mux->mask;
-
- if (mux->table) {
+ if (table) {
int i;
for (i = 0; i < num_parents; i++)
- if (mux->table[i] == val)
+ if (table[i] == val)
return i;
return -EINVAL;
}
- if (val && (mux->flags & CLK_MUX_INDEX_BIT))
+ if (val && (flags & CLK_MUX_INDEX_BIT))
val = ffs(val) - 1;
- if (val && (mux->flags & CLK_MUX_INDEX_ONE))
+ if (val && (flags & CLK_MUX_INDEX_ONE))
val--;
if (val >= num_parents)
@@ -62,36 +51,58 @@ static u8 clk_mux_get_parent(struct clk_hw *hw)
return val;
}
+EXPORT_SYMBOL_GPL(clk_mux_val_to_index);
-static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
+unsigned int clk_mux_index_to_val(u32 *table, unsigned int flags, u8 index)
{
- struct clk_mux *mux = to_clk_mux(hw);
- u32 val;
- unsigned long flags = 0;
+ unsigned int val = index;
- if (mux->table) {
- index = mux->table[index];
+ if (table) {
+ val = table[index];
} else {
- if (mux->flags & CLK_MUX_INDEX_BIT)
- index = 1 << index;
+ if (flags & CLK_MUX_INDEX_BIT)
+ val = 1 << index;
- if (mux->flags & CLK_MUX_INDEX_ONE)
- index++;
+ if (flags & CLK_MUX_INDEX_ONE)
+ val++;
}
+ return val;
+}
+EXPORT_SYMBOL_GPL(clk_mux_index_to_val);
+
+static u8 clk_mux_get_parent(struct clk_hw *hw)
+{
+ struct clk_mux *mux = to_clk_mux(hw);
+ u32 val;
+
+ val = clk_readl(mux->reg) >> mux->shift;
+ val &= mux->mask;
+
+ return clk_mux_val_to_index(hw, mux->table, mux->flags, val);
+}
+
+static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_mux *mux = to_clk_mux(hw);
+ u32 val = clk_mux_index_to_val(mux->table, mux->flags, index);
+ unsigned long flags = 0;
+ u32 reg;
+
if (mux->lock)
spin_lock_irqsave(mux->lock, flags);
else
__acquire(mux->lock);
if (mux->flags & CLK_MUX_HIWORD_MASK) {
- val = mux->mask << (mux->shift + 16);
+ reg = mux->mask << (mux->shift + 16);
} else {
- val = clk_readl(mux->reg);
- val &= ~(mux->mask << mux->shift);
+ reg = clk_readl(mux->reg);
+ reg &= ~(mux->mask << mux->shift);
}
- val |= index << mux->shift;
- clk_writel(val, mux->reg);
+ val = val << mux->shift;
+ reg |= val;
+ clk_writel(reg, mux->reg);
if (mux->lock)
spin_unlock_irqrestore(mux->lock, flags);
diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c
new file mode 100644
index 000000000000..488c21376b55
--- /dev/null
+++ b/drivers/clk/clk-scmi.c
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Power Interface (SCMI) Protocol based clock driver
+ *
+ * Copyright (C) 2018 ARM Ltd.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/scmi_protocol.h>
+#include <asm/div64.h>
+
+struct scmi_clk {
+ u32 id;
+ struct clk_hw hw;
+ const struct scmi_clock_info *info;
+ const struct scmi_handle *handle;
+};
+
+#define to_scmi_clk(clk) container_of(clk, struct scmi_clk, hw)
+
+static unsigned long scmi_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ int ret;
+ u64 rate;
+ struct scmi_clk *clk = to_scmi_clk(hw);
+
+ ret = clk->handle->clk_ops->rate_get(clk->handle, clk->id, &rate);
+ if (ret)
+ return 0;
+ return rate;
+}
+
+static long scmi_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ int step;
+ u64 fmin, fmax, ftmp;
+ struct scmi_clk *clk = to_scmi_clk(hw);
+
+ /*
+ * We can't figure out what rate it will be, so just return the
+ * rate back to the caller. scmi_clk_recalc_rate() will be called
+ * after the rate is set and we'll know what rate the clock is
+ * running at then.
+ */
+ if (clk->info->rate_discrete)
+ return rate;
+
+ fmin = clk->info->range.min_rate;
+ fmax = clk->info->range.max_rate;
+ if (rate <= fmin)
+ return fmin;
+ else if (rate >= fmax)
+ return fmax;
+
+ ftmp = rate - fmin;
+ ftmp += clk->info->range.step_size - 1; /* to round up */
+ step = do_div(ftmp, clk->info->range.step_size);
+
+ return step * clk->info->range.step_size + fmin;
+}
+
+static int scmi_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct scmi_clk *clk = to_scmi_clk(hw);
+
+ return clk->handle->clk_ops->rate_set(clk->handle, clk->id, 0, rate);
+}
+
+static int scmi_clk_enable(struct clk_hw *hw)
+{
+ struct scmi_clk *clk = to_scmi_clk(hw);
+
+ return clk->handle->clk_ops->enable(clk->handle, clk->id);
+}
+
+static void scmi_clk_disable(struct clk_hw *hw)
+{
+ struct scmi_clk *clk = to_scmi_clk(hw);
+
+ clk->handle->clk_ops->disable(clk->handle, clk->id);
+}
+
+static const struct clk_ops scmi_clk_ops = {
+ .recalc_rate = scmi_clk_recalc_rate,
+ .round_rate = scmi_clk_round_rate,
+ .set_rate = scmi_clk_set_rate,
+ /*
+ * We can't provide enable/disable callback as we can't perform the same
+ * in atomic context. Since the clock framework provides standard API
+ * clk_prepare_enable that helps cases using clk_enable in non-atomic
+ * context, it should be fine providing prepare/unprepare.
+ */
+ .prepare = scmi_clk_enable,
+ .unprepare = scmi_clk_disable,
+};
+
+static int scmi_clk_ops_init(struct device *dev, struct scmi_clk *sclk)
+{
+ int ret;
+ struct clk_init_data init = {
+ .flags = CLK_GET_RATE_NOCACHE,
+ .num_parents = 0,
+ .ops = &scmi_clk_ops,
+ .name = sclk->info->name,
+ };
+
+ sclk->hw.init = &init;
+ ret = devm_clk_hw_register(dev, &sclk->hw);
+ if (!ret)
+ clk_hw_set_rate_range(&sclk->hw, sclk->info->range.min_rate,
+ sclk->info->range.max_rate);
+ return ret;
+}
+
+static int scmi_clocks_probe(struct scmi_device *sdev)
+{
+ int idx, count, err;
+ struct clk_hw **hws;
+ struct clk_hw_onecell_data *clk_data;
+ struct device *dev = &sdev->dev;
+ struct device_node *np = dev->of_node;
+ const struct scmi_handle *handle = sdev->handle;
+
+ if (!handle || !handle->clk_ops)
+ return -ENODEV;
+
+ count = handle->clk_ops->count_get(handle);
+ if (count < 0) {
+ dev_err(dev, "%s: invalid clock output count\n", np->name);
+ return -EINVAL;
+ }
+
+ clk_data = devm_kzalloc(dev, sizeof(*clk_data) +
+ sizeof(*clk_data->hws) * count, GFP_KERNEL);
+ if (!clk_data)
+ return -ENOMEM;
+
+ clk_data->num = count;
+ hws = clk_data->hws;
+
+ for (idx = 0; idx < count; idx++) {
+ struct scmi_clk *sclk;
+
+ sclk = devm_kzalloc(dev, sizeof(*sclk), GFP_KERNEL);
+ if (!sclk)
+ return -ENOMEM;
+
+ sclk->info = handle->clk_ops->info_get(handle, idx);
+ if (!sclk->info) {
+ dev_dbg(dev, "invalid clock info for idx %d\n", idx);
+ continue;
+ }
+
+ sclk->id = idx;
+ sclk->handle = handle;
+
+ err = scmi_clk_ops_init(dev, sclk);
+ if (err) {
+ dev_err(dev, "failed to register clock %d\n", idx);
+ devm_kfree(dev, sclk);
+ hws[idx] = NULL;
+ } else {
+ dev_dbg(dev, "Registered clock:%s\n", sclk->info->name);
+ hws[idx] = &sclk->hw;
+ }
+ }
+
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
+ clk_data);
+}
+
+static const struct scmi_device_id scmi_id_table[] = {
+ { SCMI_PROTOCOL_CLOCK },
+ { },
+};
+MODULE_DEVICE_TABLE(scmi, scmi_id_table);
+
+static struct scmi_driver scmi_clocks_driver = {
+ .name = "scmi-clocks",
+ .probe = scmi_clocks_probe,
+ .id_table = scmi_id_table,
+};
+module_scmi_driver(scmi_clocks_driver);
+
+MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
+MODULE_DESCRIPTION("ARM SCMI clock driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/clk-si544.c b/drivers/clk/clk-si544.c
new file mode 100644
index 000000000000..1c96a9f6c022
--- /dev/null
+++ b/drivers/clk/clk-si544.c
@@ -0,0 +1,411 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for Silicon Labs Si544 Programmable Oscillator
+ * Copyright (C) 2018 Topic Embedded Products
+ * Author: Mike Looijmans <mike.looijmans@topic.nl>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+/* I2C registers (decimal as in datasheet) */
+#define SI544_REG_CONTROL 7
+#define SI544_REG_OE_STATE 17
+#define SI544_REG_HS_DIV 23
+#define SI544_REG_LS_HS_DIV 24
+#define SI544_REG_FBDIV0 26
+#define SI544_REG_FBDIV8 27
+#define SI544_REG_FBDIV16 28
+#define SI544_REG_FBDIV24 29
+#define SI544_REG_FBDIV32 30
+#define SI544_REG_FBDIV40 31
+#define SI544_REG_FCAL_OVR 69
+#define SI544_REG_ADPLL_DELTA_M0 231
+#define SI544_REG_ADPLL_DELTA_M8 232
+#define SI544_REG_ADPLL_DELTA_M16 233
+#define SI544_REG_PAGE_SELECT 255
+
+/* Register values */
+#define SI544_CONTROL_RESET BIT(7)
+#define SI544_CONTROL_MS_ICAL2 BIT(3)
+
+#define SI544_OE_STATE_ODC_OE BIT(0)
+
+/* Max freq depends on speed grade */
+#define SI544_MIN_FREQ 200000U
+
+/* Si544 Internal oscilator runs at 55.05 MHz */
+#define FXO 55050000U
+
+/* VCO range is 10.8 .. 12.1 GHz, max depends on speed grade */
+#define FVCO_MIN 10800000000ULL
+
+#define HS_DIV_MAX 2046
+#define HS_DIV_MAX_ODD 33
+
+/* Lowest frequency synthesizeable using only the HS divider */
+#define MIN_HSDIV_FREQ (FVCO_MIN / HS_DIV_MAX)
+
+enum si544_speed_grade {
+ si544a,
+ si544b,
+ si544c,
+};
+
+struct clk_si544 {
+ struct clk_hw hw;
+ struct regmap *regmap;
+ struct i2c_client *i2c_client;
+ enum si544_speed_grade speed_grade;
+};
+#define to_clk_si544(_hw) container_of(_hw, struct clk_si544, hw)
+
+/**
+ * struct clk_si544_muldiv - Multiplier/divider settings
+ * @fb_div_frac: integer part of feedback divider (32 bits)
+ * @fb_div_int: fractional part of feedback divider (11 bits)
+ * @hs_div: 1st divider, 5..2046, must be even when >33
+ * @ls_div_bits: 2nd divider, as 2^x, range 0..5
+ * If ls_div_bits is non-zero, hs_div must be even
+ */
+struct clk_si544_muldiv {
+ u32 fb_div_frac;
+ u16 fb_div_int;
+ u16 hs_div;
+ u8 ls_div_bits;
+};
+
+/* Enables or disables the output driver */
+static int si544_enable_output(struct clk_si544 *data, bool enable)
+{
+ return regmap_update_bits(data->regmap, SI544_REG_OE_STATE,
+ SI544_OE_STATE_ODC_OE, enable ? SI544_OE_STATE_ODC_OE : 0);
+}
+
+/* Retrieve clock multiplier and dividers from hardware */
+static int si544_get_muldiv(struct clk_si544 *data,
+ struct clk_si544_muldiv *settings)
+{
+ int err;
+ u8 reg[6];
+
+ err = regmap_bulk_read(data->regmap, SI544_REG_HS_DIV, reg, 2);
+ if (err)
+ return err;
+
+ settings->ls_div_bits = (reg[1] >> 4) & 0x07;
+ settings->hs_div = (reg[1] & 0x07) << 8 | reg[0];
+
+ err = regmap_bulk_read(data->regmap, SI544_REG_FBDIV0, reg, 6);
+ if (err)
+ return err;
+
+ settings->fb_div_int = reg[4] | (reg[5] & 0x07) << 8;
+ settings->fb_div_frac = reg[0] | reg[1] << 8 | reg[2] << 16 |
+ reg[3] << 24;
+ return 0;
+}
+
+static int si544_set_muldiv(struct clk_si544 *data,
+ struct clk_si544_muldiv *settings)
+{
+ int err;
+ u8 reg[6];
+
+ reg[0] = settings->hs_div;
+ reg[1] = settings->hs_div >> 8 | settings->ls_div_bits << 4;
+
+ err = regmap_bulk_write(data->regmap, SI544_REG_HS_DIV, reg, 2);
+ if (err < 0)
+ return err;
+
+ reg[0] = settings->fb_div_frac;
+ reg[1] = settings->fb_div_frac >> 8;
+ reg[2] = settings->fb_div_frac >> 16;
+ reg[3] = settings->fb_div_frac >> 24;
+ reg[4] = settings->fb_div_int;
+ reg[5] = settings->fb_div_int >> 8;
+
+ /*
+ * Writing to SI544_REG_FBDIV40 triggers the clock change, so that
+ * must be written last
+ */
+ return regmap_bulk_write(data->regmap, SI544_REG_FBDIV0, reg, 6);
+}
+
+static bool is_valid_frequency(const struct clk_si544 *data,
+ unsigned long frequency)
+{
+ unsigned long max_freq = 0;
+
+ if (frequency < SI544_MIN_FREQ)
+ return false;
+
+ switch (data->speed_grade) {
+ case si544a:
+ max_freq = 1500000000;
+ break;
+ case si544b:
+ max_freq = 800000000;
+ break;
+ case si544c:
+ max_freq = 350000000;
+ break;
+ }
+
+ return frequency <= max_freq;
+}
+
+/* Calculate divider settings for a given frequency */
+static int si544_calc_muldiv(struct clk_si544_muldiv *settings,
+ unsigned long frequency)
+{
+ u64 vco;
+ u32 ls_freq;
+ u32 tmp;
+ u8 res;
+
+ /* Determine the minimum value of LS_DIV and resulting target freq. */
+ ls_freq = frequency;
+ settings->ls_div_bits = 0;
+
+ if (frequency >= MIN_HSDIV_FREQ) {
+ settings->ls_div_bits = 0;
+ } else {
+ res = 1;
+ tmp = 2 * HS_DIV_MAX;
+ while (tmp <= (HS_DIV_MAX * 32)) {
+ if (((u64)frequency * tmp) >= FVCO_MIN)
+ break;
+ ++res;
+ tmp <<= 1;
+ }
+ settings->ls_div_bits = res;
+ ls_freq = frequency << res;
+ }
+
+ /* Determine minimum HS_DIV by rounding up */
+ vco = FVCO_MIN + ls_freq - 1;
+ do_div(vco, ls_freq);
+ settings->hs_div = vco;
+
+ /* round up to even number when required */
+ if ((settings->hs_div & 1) &&
+ (settings->hs_div > HS_DIV_MAX_ODD || settings->ls_div_bits))
+ ++settings->hs_div;
+
+ /* Calculate VCO frequency (in 10..12GHz range) */
+ vco = (u64)ls_freq * settings->hs_div;
+
+ /* Calculate the integer part of the feedback divider */
+ tmp = do_div(vco, FXO);
+ settings->fb_div_int = vco;
+
+ /* And the fractional bits using the remainder */
+ vco = (u64)tmp << 32;
+ do_div(vco, FXO);
+ settings->fb_div_frac = vco;
+
+ return 0;
+}
+
+/* Calculate resulting frequency given the register settings */
+static unsigned long si544_calc_rate(struct clk_si544_muldiv *settings)
+{
+ u32 d = settings->hs_div * BIT(settings->ls_div_bits);
+ u64 vco;
+
+ /* Calculate VCO from the fractional part */
+ vco = (u64)settings->fb_div_frac * FXO;
+ vco += (FXO / 2);
+ vco >>= 32;
+
+ /* Add the integer part of the VCO frequency */
+ vco += (u64)settings->fb_div_int * FXO;
+
+ /* Apply divider to obtain the generated frequency */
+ do_div(vco, d);
+
+ return vco;
+}
+
+static unsigned long si544_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_si544 *data = to_clk_si544(hw);
+ struct clk_si544_muldiv settings;
+ int err;
+
+ err = si544_get_muldiv(data, &settings);
+ if (err)
+ return 0;
+
+ return si544_calc_rate(&settings);
+}
+
+static long si544_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct clk_si544 *data = to_clk_si544(hw);
+ struct clk_si544_muldiv settings;
+ int err;
+
+ if (!is_valid_frequency(data, rate))
+ return -EINVAL;
+
+ err = si544_calc_muldiv(&settings, rate);
+ if (err)
+ return err;
+
+ return si544_calc_rate(&settings);
+}
+
+/*
+ * Update output frequency for "big" frequency changes
+ */
+static int si544_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_si544 *data = to_clk_si544(hw);
+ struct clk_si544_muldiv settings;
+ int err;
+
+ if (!is_valid_frequency(data, rate))
+ return -EINVAL;
+
+ err = si544_calc_muldiv(&settings, rate);
+ if (err)
+ return err;
+
+ si544_enable_output(data, false);
+
+ /* Allow FCAL for this frequency update */
+ err = regmap_write(data->regmap, SI544_REG_FCAL_OVR, 0);
+ if (err < 0)
+ return err;
+
+
+ err = si544_set_muldiv(data, &settings);
+ if (err < 0)
+ return err; /* Undefined state now, best to leave disabled */
+
+ /* Trigger calibration */
+ err = regmap_write(data->regmap, SI544_REG_CONTROL,
+ SI544_CONTROL_MS_ICAL2);
+ if (err < 0)
+ return err;
+
+ /* Applying a new frequency can take up to 10ms */
+ usleep_range(10000, 12000);
+
+ si544_enable_output(data, true);
+
+ return err;
+}
+
+static const struct clk_ops si544_clk_ops = {
+ .recalc_rate = si544_recalc_rate,
+ .round_rate = si544_round_rate,
+ .set_rate = si544_set_rate,
+};
+
+static bool si544_regmap_is_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SI544_REG_CONTROL:
+ case SI544_REG_FCAL_OVR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config si544_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_RBTREE,
+ .max_register = SI544_REG_PAGE_SELECT,
+ .volatile_reg = si544_regmap_is_volatile,
+};
+
+static int si544_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct clk_si544 *data;
+ struct clk_init_data init;
+ int err;
+
+ data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ init.ops = &si544_clk_ops;
+ init.flags = 0;
+ init.num_parents = 0;
+ data->hw.init = &init;
+ data->i2c_client = client;
+ data->speed_grade = id->driver_data;
+
+ if (of_property_read_string(client->dev.of_node, "clock-output-names",
+ &init.name))
+ init.name = client->dev.of_node->name;
+
+ data->regmap = devm_regmap_init_i2c(client, &si544_regmap_config);
+ if (IS_ERR(data->regmap))
+ return PTR_ERR(data->regmap);
+
+ i2c_set_clientdata(client, data);
+
+ /* Select page 0, just to be sure, there appear to be no more */
+ err = regmap_write(data->regmap, SI544_REG_PAGE_SELECT, 0);
+ if (err < 0)
+ return err;
+
+ err = devm_clk_hw_register(&client->dev, &data->hw);
+ if (err) {
+ dev_err(&client->dev, "clock registration failed\n");
+ return err;
+ }
+ err = devm_of_clk_add_hw_provider(&client->dev, of_clk_hw_simple_get,
+ &data->hw);
+ if (err) {
+ dev_err(&client->dev, "unable to add clk provider\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id si544_id[] = {
+ { "si544a", si544a },
+ { "si544b", si544b },
+ { "si544c", si544c },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, si544_id);
+
+static const struct of_device_id clk_si544_of_match[] = {
+ { .compatible = "silabs,si544a" },
+ { .compatible = "silabs,si544b" },
+ { .compatible = "silabs,si544c" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, clk_si544_of_match);
+
+static struct i2c_driver si544_driver = {
+ .driver = {
+ .name = "si544",
+ .of_match_table = clk_si544_of_match,
+ },
+ .probe = si544_probe,
+ .id_table = si544_id,
+};
+module_i2c_driver(si544_driver);
+
+MODULE_AUTHOR("Mike Looijmans <mike.looijmans@topic.nl>");
+MODULE_DESCRIPTION("Si544 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/clk-stm32f4.c b/drivers/clk/clk-stm32f4.c
index da44f8dc1d29..294850bdc195 100644
--- a/drivers/clk/clk-stm32f4.c
+++ b/drivers/clk/clk-stm32f4.c
@@ -282,6 +282,7 @@ static const struct stm32f4_gate_data stm32f746_gates[] __initconst = {
{ STM32F4_RCC_APB2ENR, 0, "tim1", "apb2_mul" },
{ STM32F4_RCC_APB2ENR, 1, "tim8", "apb2_mul" },
+ { STM32F4_RCC_APB2ENR, 7, "sdmmc2", "sdmux" },
{ STM32F4_RCC_APB2ENR, 8, "adc1", "apb2_div" },
{ STM32F4_RCC_APB2ENR, 9, "adc2", "apb2_div" },
{ STM32F4_RCC_APB2ENR, 10, "adc3", "apb2_div" },
@@ -315,7 +316,7 @@ static const u64 stm32f46xx_gate_map[MAX_GATE_MAP] = { 0x000000f17ef417ffull,
static const u64 stm32f746_gate_map[MAX_GATE_MAP] = { 0x000000f17ef417ffull,
0x0000000000000003ull,
- 0x04f77f033e01c9ffull };
+ 0x04f77f833e01c9ffull };
static const u64 *stm32f4_gate_map;
@@ -521,7 +522,7 @@ static const struct stm32f4_pll_data stm32f429_pll[MAX_PLL_DIV] = {
};
static const struct stm32f4_pll_data stm32f469_pll[MAX_PLL_DIV] = {
- { PLL, 50, { "pll", "pll-q", NULL } },
+ { PLL, 50, { "pll", "pll-q", "pll-r" } },
{ PLL_I2S, 50, { "plli2s-p", "plli2s-q", "plli2s-r" } },
{ PLL_SAI, 50, { "pllsai-p", "pllsai-q", "pllsai-r" } },
};
@@ -1047,6 +1048,8 @@ static const char *rtc_parents[4] = {
"no-clock", "lse", "lsi", "hse-rtc"
};
+static const char *dsi_parent[2] = { NULL, "pll-r" };
+
static const char *lcd_parent[1] = { "pllsai-r-div" };
static const char *i2s_parents[2] = { "plli2s-r", NULL };
@@ -1156,6 +1159,12 @@ static const struct stm32_aux_clk stm32f469_aux_clk[] = {
NO_GATE, 0,
0
},
+ {
+ CLK_F469_DSI, "dsi", dsi_parent, ARRAY_SIZE(dsi_parent),
+ STM32F4_RCC_DCKCFGR, 29, 1,
+ STM32F4_RCC_APB2ENR, 27,
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT
+ },
};
static const struct stm32_aux_clk stm32f746_aux_clk[] = {
@@ -1450,6 +1459,7 @@ static void __init stm32f4_rcc_init(struct device_node *np)
stm32f4_gate_map = data->gates_map;
hse_clk = of_clk_get_parent_name(np, 0);
+ dsi_parent[0] = hse_clk;
i2s_in_clk = of_clk_get_parent_name(np, 1);
diff --git a/drivers/clk/clk-stm32mp1.c b/drivers/clk/clk-stm32mp1.c
new file mode 100644
index 000000000000..f1d5967b4b39
--- /dev/null
+++ b/drivers/clk/clk-stm32mp1.c
@@ -0,0 +1,2117 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
+ * Author: Olivier Bideau <olivier.bideau@st.com> for STMicroelectronics.
+ * Author: Gabriel Fernandez <gabriel.fernandez@st.com> for STMicroelectronics.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <dt-bindings/clock/stm32mp1-clks.h>
+
+static DEFINE_SPINLOCK(rlock);
+
+#define RCC_OCENSETR 0x0C
+#define RCC_HSICFGR 0x18
+#define RCC_RDLSICR 0x144
+#define RCC_PLL1CR 0x80
+#define RCC_PLL1CFGR1 0x84
+#define RCC_PLL1CFGR2 0x88
+#define RCC_PLL2CR 0x94
+#define RCC_PLL2CFGR1 0x98
+#define RCC_PLL2CFGR2 0x9C
+#define RCC_PLL3CR 0x880
+#define RCC_PLL3CFGR1 0x884
+#define RCC_PLL3CFGR2 0x888
+#define RCC_PLL4CR 0x894
+#define RCC_PLL4CFGR1 0x898
+#define RCC_PLL4CFGR2 0x89C
+#define RCC_APB1ENSETR 0xA00
+#define RCC_APB2ENSETR 0xA08
+#define RCC_APB3ENSETR 0xA10
+#define RCC_APB4ENSETR 0x200
+#define RCC_APB5ENSETR 0x208
+#define RCC_AHB2ENSETR 0xA18
+#define RCC_AHB3ENSETR 0xA20
+#define RCC_AHB4ENSETR 0xA28
+#define RCC_AHB5ENSETR 0x210
+#define RCC_AHB6ENSETR 0x218
+#define RCC_AHB6LPENSETR 0x318
+#define RCC_RCK12SELR 0x28
+#define RCC_RCK3SELR 0x820
+#define RCC_RCK4SELR 0x824
+#define RCC_MPCKSELR 0x20
+#define RCC_ASSCKSELR 0x24
+#define RCC_MSSCKSELR 0x48
+#define RCC_SPI6CKSELR 0xC4
+#define RCC_SDMMC12CKSELR 0x8F4
+#define RCC_SDMMC3CKSELR 0x8F8
+#define RCC_FMCCKSELR 0x904
+#define RCC_I2C46CKSELR 0xC0
+#define RCC_I2C12CKSELR 0x8C0
+#define RCC_I2C35CKSELR 0x8C4
+#define RCC_UART1CKSELR 0xC8
+#define RCC_QSPICKSELR 0x900
+#define RCC_ETHCKSELR 0x8FC
+#define RCC_RNG1CKSELR 0xCC
+#define RCC_RNG2CKSELR 0x920
+#define RCC_GPUCKSELR 0x938
+#define RCC_USBCKSELR 0x91C
+#define RCC_STGENCKSELR 0xD4
+#define RCC_SPDIFCKSELR 0x914
+#define RCC_SPI2S1CKSELR 0x8D8
+#define RCC_SPI2S23CKSELR 0x8DC
+#define RCC_SPI2S45CKSELR 0x8E0
+#define RCC_CECCKSELR 0x918
+#define RCC_LPTIM1CKSELR 0x934
+#define RCC_LPTIM23CKSELR 0x930
+#define RCC_LPTIM45CKSELR 0x92C
+#define RCC_UART24CKSELR 0x8E8
+#define RCC_UART35CKSELR 0x8EC
+#define RCC_UART6CKSELR 0x8E4
+#define RCC_UART78CKSELR 0x8F0
+#define RCC_FDCANCKSELR 0x90C
+#define RCC_SAI1CKSELR 0x8C8
+#define RCC_SAI2CKSELR 0x8CC
+#define RCC_SAI3CKSELR 0x8D0
+#define RCC_SAI4CKSELR 0x8D4
+#define RCC_ADCCKSELR 0x928
+#define RCC_MPCKDIVR 0x2C
+#define RCC_DSICKSELR 0x924
+#define RCC_CPERCKSELR 0xD0
+#define RCC_MCO1CFGR 0x800
+#define RCC_MCO2CFGR 0x804
+#define RCC_BDCR 0x140
+#define RCC_AXIDIVR 0x30
+#define RCC_MCUDIVR 0x830
+#define RCC_APB1DIVR 0x834
+#define RCC_APB2DIVR 0x838
+#define RCC_APB3DIVR 0x83C
+#define RCC_APB4DIVR 0x3C
+#define RCC_APB5DIVR 0x40
+#define RCC_TIMG1PRER 0x828
+#define RCC_TIMG2PRER 0x82C
+#define RCC_RTCDIVR 0x44
+#define RCC_DBGCFGR 0x80C
+
+#define RCC_CLR 0x4
+
+static const char * const ref12_parents[] = {
+ "ck_hsi", "ck_hse"
+};
+
+static const char * const ref3_parents[] = {
+ "ck_hsi", "ck_hse", "ck_csi"
+};
+
+static const char * const ref4_parents[] = {
+ "ck_hsi", "ck_hse", "ck_csi"
+};
+
+static const char * const cpu_src[] = {
+ "ck_hsi", "ck_hse", "pll1_p"
+};
+
+static const char * const axi_src[] = {
+ "ck_hsi", "ck_hse", "pll2_p", "pll3_p"
+};
+
+static const char * const per_src[] = {
+ "ck_hsi", "ck_csi", "ck_hse"
+};
+
+static const char * const mcu_src[] = {
+ "ck_hsi", "ck_hse", "ck_csi", "pll3_p"
+};
+
+static const char * const sdmmc12_src[] = {
+ "ck_axi", "pll3_r", "pll4_p", "ck_hsi"
+};
+
+static const char * const sdmmc3_src[] = {
+ "ck_mcu", "pll3_r", "pll4_p", "ck_hsi"
+};
+
+static const char * const fmc_src[] = {
+ "ck_axi", "pll3_r", "pll4_p", "ck_per"
+};
+
+static const char * const qspi_src[] = {
+ "ck_axi", "pll3_r", "pll4_p", "ck_per"
+};
+
+static const char * const eth_src[] = {
+ "pll4_p", "pll3_q"
+};
+
+static const char * const rng_src[] = {
+ "ck_csi", "pll4_r", "ck_lse", "ck_lsi"
+};
+
+static const char * const usbphy_src[] = {
+ "ck_hse", "pll4_r", "clk-hse-div2"
+};
+
+static const char * const usbo_src[] = {
+ "pll4_r", "ck_usbo_48m"
+};
+
+static const char * const stgen_src[] = {
+ "ck_hsi", "ck_hse"
+};
+
+static const char * const spdif_src[] = {
+ "pll4_p", "pll3_q", "ck_hsi"
+};
+
+static const char * const spi123_src[] = {
+ "pll4_p", "pll3_q", "i2s_ckin", "ck_per", "pll3_r"
+};
+
+static const char * const spi45_src[] = {
+ "pclk2", "pll4_q", "ck_hsi", "ck_csi", "ck_hse"
+};
+
+static const char * const spi6_src[] = {
+ "pclk5", "pll4_q", "ck_hsi", "ck_csi", "ck_hse", "pll3_q"
+};
+
+static const char * const cec_src[] = {
+ "ck_lse", "ck_lsi", "ck_csi"
+};
+
+static const char * const i2c12_src[] = {
+ "pclk1", "pll4_r", "ck_hsi", "ck_csi"
+};
+
+static const char * const i2c35_src[] = {
+ "pclk1", "pll4_r", "ck_hsi", "ck_csi"
+};
+
+static const char * const i2c46_src[] = {
+ "pclk5", "pll3_q", "ck_hsi", "ck_csi"
+};
+
+static const char * const lptim1_src[] = {
+ "pclk1", "pll4_p", "pll3_q", "ck_lse", "ck_lsi", "ck_per"
+};
+
+static const char * const lptim23_src[] = {
+ "pclk3", "pll4_q", "ck_per", "ck_lse", "ck_lsi"
+};
+
+static const char * const lptim45_src[] = {
+ "pclk3", "pll4_p", "pll3_q", "ck_lse", "ck_lsi", "ck_per"
+};
+
+static const char * const usart1_src[] = {
+ "pclk5", "pll3_q", "ck_hsi", "ck_csi", "pll4_q", "ck_hse"
+};
+
+const char * const usart234578_src[] = {
+ "pclk1", "pll4_q", "ck_hsi", "ck_csi", "ck_hse"
+};
+
+static const char * const usart6_src[] = {
+ "pclk2", "pll4_q", "ck_hsi", "ck_csi", "ck_hse"
+};
+
+static const char * const dfsdm_src[] = {
+ "pclk2", "ck_mcu"
+};
+
+static const char * const fdcan_src[] = {
+ "ck_hse", "pll3_q", "pll4_q"
+};
+
+static const char * const sai_src[] = {
+ "pll4_q", "pll3_q", "i2s_ckin", "ck_per"
+};
+
+static const char * const sai2_src[] = {
+ "pll4_q", "pll3_q", "i2s_ckin", "ck_per", "spdif_ck_symb"
+};
+
+static const char * const adc12_src[] = {
+ "pll4_q", "ck_per"
+};
+
+static const char * const dsi_src[] = {
+ "ck_dsi_phy", "pll4_p"
+};
+
+static const char * const rtc_src[] = {
+ "off", "ck_lse", "ck_lsi", "ck_hse_rtc"
+};
+
+static const char * const mco1_src[] = {
+ "ck_hsi", "ck_hse", "ck_csi", "ck_lsi", "ck_lse"
+};
+
+static const char * const mco2_src[] = {
+ "ck_mpu", "ck_axi", "ck_mcu", "pll4_p", "ck_hse", "ck_hsi"
+};
+
+static const char * const ck_trace_src[] = {
+ "ck_axi"
+};
+
+static const struct clk_div_table axi_div_table[] = {
+ { 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 4 },
+ { 4, 4 }, { 5, 4 }, { 6, 4 }, { 7, 4 },
+ { 0 },
+};
+
+static const struct clk_div_table mcu_div_table[] = {
+ { 0, 1 }, { 1, 2 }, { 2, 4 }, { 3, 8 },
+ { 4, 16 }, { 5, 32 }, { 6, 64 }, { 7, 128 },
+ { 8, 512 }, { 9, 512 }, { 10, 512}, { 11, 512 },
+ { 12, 512 }, { 13, 512 }, { 14, 512}, { 15, 512 },
+ { 0 },
+};
+
+static const struct clk_div_table apb_div_table[] = {
+ { 0, 1 }, { 1, 2 }, { 2, 4 }, { 3, 8 },
+ { 4, 16 }, { 5, 16 }, { 6, 16 }, { 7, 16 },
+ { 0 },
+};
+
+static const struct clk_div_table ck_trace_div_table[] = {
+ { 0, 1 }, { 1, 2 }, { 2, 4 }, { 3, 8 },
+ { 4, 16 }, { 5, 16 }, { 6, 16 }, { 7, 16 },
+ { 0 },
+};
+
+#define MAX_MUX_CLK 2
+
+struct stm32_mmux {
+ u8 nbr_clk;
+ struct clk_hw *hws[MAX_MUX_CLK];
+};
+
+struct stm32_clk_mmux {
+ struct clk_mux mux;
+ struct stm32_mmux *mmux;
+};
+
+struct stm32_mgate {
+ u8 nbr_clk;
+ u32 flag;
+};
+
+struct stm32_clk_mgate {
+ struct clk_gate gate;
+ struct stm32_mgate *mgate;
+ u32 mask;
+};
+
+struct clock_config {
+ u32 id;
+ const char *name;
+ union {
+ const char *parent_name;
+ const char * const *parent_names;
+ };
+ int num_parents;
+ unsigned long flags;
+ void *cfg;
+ struct clk_hw * (*func)(struct device *dev,
+ struct clk_hw_onecell_data *clk_data,
+ void __iomem *base, spinlock_t *lock,
+ const struct clock_config *cfg);
+};
+
+#define NO_ID ~0
+
+struct gate_cfg {
+ u32 reg_off;
+ u8 bit_idx;
+ u8 gate_flags;
+};
+
+struct fixed_factor_cfg {
+ unsigned int mult;
+ unsigned int div;
+};
+
+struct div_cfg {
+ u32 reg_off;
+ u8 shift;
+ u8 width;
+ u8 div_flags;
+ const struct clk_div_table *table;
+};
+
+struct mux_cfg {
+ u32 reg_off;
+ u8 shift;
+ u8 width;
+ u8 mux_flags;
+ u32 *table;
+};
+
+struct stm32_gate_cfg {
+ struct gate_cfg *gate;
+ struct stm32_mgate *mgate;
+ const struct clk_ops *ops;
+};
+
+struct stm32_div_cfg {
+ struct div_cfg *div;
+ const struct clk_ops *ops;
+};
+
+struct stm32_mux_cfg {
+ struct mux_cfg *mux;
+ struct stm32_mmux *mmux;
+ const struct clk_ops *ops;
+};
+
+/* STM32 Composite clock */
+struct stm32_composite_cfg {
+ const struct stm32_gate_cfg *gate;
+ const struct stm32_div_cfg *div;
+ const struct stm32_mux_cfg *mux;
+};
+
+static struct clk_hw *
+_clk_hw_register_gate(struct device *dev,
+ struct clk_hw_onecell_data *clk_data,
+ void __iomem *base, spinlock_t *lock,
+ const struct clock_config *cfg)
+{
+ struct gate_cfg *gate_cfg = cfg->cfg;
+
+ return clk_hw_register_gate(dev,
+ cfg->name,
+ cfg->parent_name,
+ cfg->flags,
+ gate_cfg->reg_off + base,
+ gate_cfg->bit_idx,
+ gate_cfg->gate_flags,
+ lock);
+}
+
+static struct clk_hw *
+_clk_hw_register_fixed_factor(struct device *dev,
+ struct clk_hw_onecell_data *clk_data,
+ void __iomem *base, spinlock_t *lock,
+ const struct clock_config *cfg)
+{
+ struct fixed_factor_cfg *ff_cfg = cfg->cfg;
+
+ return clk_hw_register_fixed_factor(dev, cfg->name, cfg->parent_name,
+ cfg->flags, ff_cfg->mult,
+ ff_cfg->div);
+}
+
+static struct clk_hw *
+_clk_hw_register_divider_table(struct device *dev,
+ struct clk_hw_onecell_data *clk_data,
+ void __iomem *base, spinlock_t *lock,
+ const struct clock_config *cfg)
+{
+ struct div_cfg *div_cfg = cfg->cfg;
+
+ return clk_hw_register_divider_table(dev,
+ cfg->name,
+ cfg->parent_name,
+ cfg->flags,
+ div_cfg->reg_off + base,
+ div_cfg->shift,
+ div_cfg->width,
+ div_cfg->div_flags,
+ div_cfg->table,
+ lock);
+}
+
+static struct clk_hw *
+_clk_hw_register_mux(struct device *dev,
+ struct clk_hw_onecell_data *clk_data,
+ void __iomem *base, spinlock_t *lock,
+ const struct clock_config *cfg)
+{
+ struct mux_cfg *mux_cfg = cfg->cfg;
+
+ return clk_hw_register_mux(dev, cfg->name, cfg->parent_names,
+ cfg->num_parents, cfg->flags,
+ mux_cfg->reg_off + base, mux_cfg->shift,
+ mux_cfg->width, mux_cfg->mux_flags, lock);
+}
+
+/* MP1 Gate clock with set & clear registers */
+
+static int mp1_gate_clk_enable(struct clk_hw *hw)
+{
+ if (!clk_gate_ops.is_enabled(hw))
+ clk_gate_ops.enable(hw);
+
+ return 0;
+}
+
+static void mp1_gate_clk_disable(struct clk_hw *hw)
+{
+ struct clk_gate *gate = to_clk_gate(hw);
+ unsigned long flags = 0;
+
+ if (clk_gate_ops.is_enabled(hw)) {
+ spin_lock_irqsave(gate->lock, flags);
+ writel_relaxed(BIT(gate->bit_idx), gate->reg + RCC_CLR);
+ spin_unlock_irqrestore(gate->lock, flags);
+ }
+}
+
+const struct clk_ops mp1_gate_clk_ops = {
+ .enable = mp1_gate_clk_enable,
+ .disable = mp1_gate_clk_disable,
+ .is_enabled = clk_gate_is_enabled,
+};
+
+static struct clk_hw *_get_stm32_mux(void __iomem *base,
+ const struct stm32_mux_cfg *cfg,
+ spinlock_t *lock)
+{
+ struct stm32_clk_mmux *mmux;
+ struct clk_mux *mux;
+ struct clk_hw *mux_hw;
+
+ if (cfg->mmux) {
+ mmux = kzalloc(sizeof(*mmux), GFP_KERNEL);
+ if (!mmux)
+ return ERR_PTR(-ENOMEM);
+
+ mmux->mux.reg = cfg->mux->reg_off + base;
+ mmux->mux.shift = cfg->mux->shift;
+ mmux->mux.mask = (1 << cfg->mux->width) - 1;
+ mmux->mux.flags = cfg->mux->mux_flags;
+ mmux->mux.table = cfg->mux->table;
+ mmux->mux.lock = lock;
+ mmux->mmux = cfg->mmux;
+ mux_hw = &mmux->mux.hw;
+ cfg->mmux->hws[cfg->mmux->nbr_clk++] = mux_hw;
+
+ } else {
+ mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+ if (!mux)
+ return ERR_PTR(-ENOMEM);
+
+ mux->reg = cfg->mux->reg_off + base;
+ mux->shift = cfg->mux->shift;
+ mux->mask = (1 << cfg->mux->width) - 1;
+ mux->flags = cfg->mux->mux_flags;
+ mux->table = cfg->mux->table;
+ mux->lock = lock;
+ mux_hw = &mux->hw;
+ }
+
+ return mux_hw;
+}
+
+static struct clk_hw *_get_stm32_div(void __iomem *base,
+ const struct stm32_div_cfg *cfg,
+ spinlock_t *lock)
+{
+ struct clk_divider *div;
+
+ div = kzalloc(sizeof(*div), GFP_KERNEL);
+
+ if (!div)
+ return ERR_PTR(-ENOMEM);
+
+ div->reg = cfg->div->reg_off + base;
+ div->shift = cfg->div->shift;
+ div->width = cfg->div->width;
+ div->flags = cfg->div->div_flags;
+ div->table = cfg->div->table;
+ div->lock = lock;
+
+ return &div->hw;
+}
+
+static struct clk_hw *
+_get_stm32_gate(void __iomem *base,
+ const struct stm32_gate_cfg *cfg, spinlock_t *lock)
+{
+ struct stm32_clk_mgate *mgate;
+ struct clk_gate *gate;
+ struct clk_hw *gate_hw;
+
+ if (cfg->mgate) {
+ mgate = kzalloc(sizeof(*mgate), GFP_KERNEL);
+ if (!mgate)
+ return ERR_PTR(-ENOMEM);
+
+ mgate->gate.reg = cfg->gate->reg_off + base;
+ mgate->gate.bit_idx = cfg->gate->bit_idx;
+ mgate->gate.flags = cfg->gate->gate_flags;
+ mgate->gate.lock = lock;
+ mgate->mask = BIT(cfg->mgate->nbr_clk++);
+
+ mgate->mgate = cfg->mgate;
+
+ gate_hw = &mgate->gate.hw;
+
+ } else {
+ gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+ if (!gate)
+ return ERR_PTR(-ENOMEM);
+
+ gate->reg = cfg->gate->reg_off + base;
+ gate->bit_idx = cfg->gate->bit_idx;
+ gate->flags = cfg->gate->gate_flags;
+ gate->lock = lock;
+
+ gate_hw = &gate->hw;
+ }
+
+ return gate_hw;
+}
+
+static struct clk_hw *
+clk_stm32_register_gate_ops(struct device *dev,
+ const char *name,
+ const char *parent_name,
+ unsigned long flags,
+ void __iomem *base,
+ const struct stm32_gate_cfg *cfg,
+ spinlock_t *lock)
+{
+ struct clk_init_data init = { NULL };
+ struct clk_gate *gate;
+ struct clk_hw *hw;
+ int ret;
+
+ gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+ if (!gate)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = flags;
+
+ init.ops = &clk_gate_ops;
+
+ if (cfg->ops)
+ init.ops = cfg->ops;
+
+ hw = _get_stm32_gate(base, cfg, lock);
+ if (IS_ERR(hw))
+ return ERR_PTR(-ENOMEM);
+
+ hw->init = &init;
+
+ ret = clk_hw_register(dev, hw);
+ if (ret) {
+ kfree(gate);
+ hw = ERR_PTR(ret);
+ }
+
+ return hw;
+}
+
+static struct clk_hw *
+clk_stm32_register_composite(struct device *dev,
+ const char *name, const char * const *parent_names,
+ int num_parents, void __iomem *base,
+ const struct stm32_composite_cfg *cfg,
+ unsigned long flags, spinlock_t *lock)
+{
+ const struct clk_ops *mux_ops, *div_ops, *gate_ops;
+ struct clk_hw *mux_hw, *div_hw, *gate_hw;
+
+ mux_hw = NULL;
+ div_hw = NULL;
+ gate_hw = NULL;
+ mux_ops = NULL;
+ div_ops = NULL;
+ gate_ops = NULL;
+
+ if (cfg->mux) {
+ mux_hw = _get_stm32_mux(base, cfg->mux, lock);
+
+ if (!IS_ERR(mux_hw)) {
+ mux_ops = &clk_mux_ops;
+
+ if (cfg->mux->ops)
+ mux_ops = cfg->mux->ops;
+ }
+ }
+
+ if (cfg->div) {
+ div_hw = _get_stm32_div(base, cfg->div, lock);
+
+ if (!IS_ERR(div_hw)) {
+ div_ops = &clk_divider_ops;
+
+ if (cfg->div->ops)
+ div_ops = cfg->div->ops;
+ }
+ }
+
+ if (cfg->gate) {
+ gate_hw = _get_stm32_gate(base, cfg->gate, lock);
+
+ if (!IS_ERR(gate_hw)) {
+ gate_ops = &clk_gate_ops;
+
+ if (cfg->gate->ops)
+ gate_ops = cfg->gate->ops;
+ }
+ }
+
+ return clk_hw_register_composite(dev, name, parent_names, num_parents,
+ mux_hw, mux_ops, div_hw, div_ops,
+ gate_hw, gate_ops, flags);
+}
+
+#define to_clk_mgate(_gate) container_of(_gate, struct stm32_clk_mgate, gate)
+
+static int mp1_mgate_clk_enable(struct clk_hw *hw)
+{
+ struct clk_gate *gate = to_clk_gate(hw);
+ struct stm32_clk_mgate *clk_mgate = to_clk_mgate(gate);
+
+ clk_mgate->mgate->flag |= clk_mgate->mask;
+
+ mp1_gate_clk_enable(hw);
+
+ return 0;
+}
+
+static void mp1_mgate_clk_disable(struct clk_hw *hw)
+{
+ struct clk_gate *gate = to_clk_gate(hw);
+ struct stm32_clk_mgate *clk_mgate = to_clk_mgate(gate);
+
+ clk_mgate->mgate->flag &= ~clk_mgate->mask;
+
+ if (clk_mgate->mgate->flag == 0)
+ mp1_gate_clk_disable(hw);
+}
+
+const struct clk_ops mp1_mgate_clk_ops = {
+ .enable = mp1_mgate_clk_enable,
+ .disable = mp1_mgate_clk_disable,
+ .is_enabled = clk_gate_is_enabled,
+
+};
+
+#define to_clk_mmux(_mux) container_of(_mux, struct stm32_clk_mmux, mux)
+
+static u8 clk_mmux_get_parent(struct clk_hw *hw)
+{
+ return clk_mux_ops.get_parent(hw);
+}
+
+static int clk_mmux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_mux *mux = to_clk_mux(hw);
+ struct stm32_clk_mmux *clk_mmux = to_clk_mmux(mux);
+ struct clk_hw *hwp;
+ int ret, n;
+
+ ret = clk_mux_ops.set_parent(hw, index);
+ if (ret)
+ return ret;
+
+ hwp = clk_hw_get_parent(hw);
+
+ for (n = 0; n < clk_mmux->mmux->nbr_clk; n++)
+ if (clk_mmux->mmux->hws[n] != hw)
+ clk_hw_reparent(clk_mmux->mmux->hws[n], hwp);
+
+ return 0;
+}
+
+const struct clk_ops clk_mmux_ops = {
+ .get_parent = clk_mmux_get_parent,
+ .set_parent = clk_mmux_set_parent,
+ .determine_rate = __clk_mux_determine_rate,
+};
+
+/* STM32 PLL */
+struct stm32_pll_obj {
+ /* lock pll enable/disable registers */
+ spinlock_t *lock;
+ void __iomem *reg;
+ struct clk_hw hw;
+};
+
+#define to_pll(_hw) container_of(_hw, struct stm32_pll_obj, hw)
+
+#define PLL_ON BIT(0)
+#define PLL_RDY BIT(1)
+#define DIVN_MASK 0x1FF
+#define DIVM_MASK 0x3F
+#define DIVM_SHIFT 16
+#define DIVN_SHIFT 0
+#define FRAC_OFFSET 0xC
+#define FRAC_MASK 0x1FFF
+#define FRAC_SHIFT 3
+#define FRACLE BIT(16)
+
+static int __pll_is_enabled(struct clk_hw *hw)
+{
+ struct stm32_pll_obj *clk_elem = to_pll(hw);
+
+ return readl_relaxed(clk_elem->reg) & PLL_ON;
+}
+
+#define TIMEOUT 5
+
+static int pll_enable(struct clk_hw *hw)
+{
+ struct stm32_pll_obj *clk_elem = to_pll(hw);
+ u32 reg;
+ unsigned long flags = 0;
+ unsigned int timeout = TIMEOUT;
+ int bit_status = 0;
+
+ spin_lock_irqsave(clk_elem->lock, flags);
+
+ if (__pll_is_enabled(hw))
+ goto unlock;
+
+ reg = readl_relaxed(clk_elem->reg);
+ reg |= PLL_ON;
+ writel_relaxed(reg, clk_elem->reg);
+
+ /* We can't use readl_poll_timeout() because we can be blocked if
+ * someone enables this clock before clocksource changes.
+ * Only jiffies counter is available. Jiffies are incremented by
+ * interruptions and enable op does not allow to be interrupted.
+ */
+ do {
+ bit_status = !(readl_relaxed(clk_elem->reg) & PLL_RDY);
+
+ if (bit_status)
+ udelay(120);
+
+ } while (bit_status && --timeout);
+
+unlock:
+ spin_unlock_irqrestore(clk_elem->lock, flags);
+
+ return bit_status;
+}
+
+static void pll_disable(struct clk_hw *hw)
+{
+ struct stm32_pll_obj *clk_elem = to_pll(hw);
+ u32 reg;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(clk_elem->lock, flags);
+
+ reg = readl_relaxed(clk_elem->reg);
+ reg &= ~PLL_ON;
+ writel_relaxed(reg, clk_elem->reg);
+
+ spin_unlock_irqrestore(clk_elem->lock, flags);
+}
+
+static u32 pll_frac_val(struct clk_hw *hw)
+{
+ struct stm32_pll_obj *clk_elem = to_pll(hw);
+ u32 reg, frac = 0;
+
+ reg = readl_relaxed(clk_elem->reg + FRAC_OFFSET);
+ if (reg & FRACLE)
+ frac = (reg >> FRAC_SHIFT) & FRAC_MASK;
+
+ return frac;
+}
+
+static unsigned long pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct stm32_pll_obj *clk_elem = to_pll(hw);
+ u32 reg;
+ u32 frac, divm, divn;
+ u64 rate, rate_frac = 0;
+
+ reg = readl_relaxed(clk_elem->reg + 4);
+
+ divm = ((reg >> DIVM_SHIFT) & DIVM_MASK) + 1;
+ divn = ((reg >> DIVN_SHIFT) & DIVN_MASK) + 1;
+ rate = (u64)parent_rate * divn;
+
+ do_div(rate, divm);
+
+ frac = pll_frac_val(hw);
+ if (frac) {
+ rate_frac = (u64)parent_rate * (u64)frac;
+ do_div(rate_frac, (divm * 8192));
+ }
+
+ return rate + rate_frac;
+}
+
+static int pll_is_enabled(struct clk_hw *hw)
+{
+ struct stm32_pll_obj *clk_elem = to_pll(hw);
+ unsigned long flags = 0;
+ int ret;
+
+ spin_lock_irqsave(clk_elem->lock, flags);
+ ret = __pll_is_enabled(hw);
+ spin_unlock_irqrestore(clk_elem->lock, flags);
+
+ return ret;
+}
+
+static const struct clk_ops pll_ops = {
+ .enable = pll_enable,
+ .disable = pll_disable,
+ .recalc_rate = pll_recalc_rate,
+ .is_enabled = pll_is_enabled,
+};
+
+static struct clk_hw *clk_register_pll(struct device *dev, const char *name,
+ const char *parent_name,
+ void __iomem *reg,
+ unsigned long flags,
+ spinlock_t *lock)
+{
+ struct stm32_pll_obj *element;
+ struct clk_init_data init;
+ struct clk_hw *hw;
+ int err;
+
+ element = kzalloc(sizeof(*element), GFP_KERNEL);
+ if (!element)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &pll_ops;
+ init.flags = flags;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ element->hw.init = &init;
+ element->reg = reg;
+ element->lock = lock;
+
+ hw = &element->hw;
+ err = clk_hw_register(dev, hw);
+
+ if (err) {
+ kfree(element);
+ return ERR_PTR(err);
+ }
+
+ return hw;
+}
+
+/* Kernel Timer */
+struct timer_cker {
+ /* lock the kernel output divider register */
+ spinlock_t *lock;
+ void __iomem *apbdiv;
+ void __iomem *timpre;
+ struct clk_hw hw;
+};
+
+#define to_timer_cker(_hw) container_of(_hw, struct timer_cker, hw)
+
+#define APB_DIV_MASK 0x07
+#define TIM_PRE_MASK 0x01
+
+static unsigned long __bestmult(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct timer_cker *tim_ker = to_timer_cker(hw);
+ u32 prescaler;
+ unsigned int mult = 0;
+
+ prescaler = readl_relaxed(tim_ker->apbdiv) & APB_DIV_MASK;
+ if (prescaler < 2)
+ return 1;
+
+ mult = 2;
+
+ if (rate / parent_rate >= 4)
+ mult = 4;
+
+ return mult;
+}
+
+static long timer_ker_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ unsigned long factor = __bestmult(hw, rate, *parent_rate);
+
+ return *parent_rate * factor;
+}
+
+static int timer_ker_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct timer_cker *tim_ker = to_timer_cker(hw);
+ unsigned long flags = 0;
+ unsigned long factor = __bestmult(hw, rate, parent_rate);
+ int ret = 0;
+
+ spin_lock_irqsave(tim_ker->lock, flags);
+
+ switch (factor) {
+ case 1:
+ break;
+ case 2:
+ writel_relaxed(0, tim_ker->timpre);
+ break;
+ case 4:
+ writel_relaxed(1, tim_ker->timpre);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ spin_unlock_irqrestore(tim_ker->lock, flags);
+
+ return ret;
+}
+
+static unsigned long timer_ker_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct timer_cker *tim_ker = to_timer_cker(hw);
+ u32 prescaler, timpre;
+ u32 mul;
+
+ prescaler = readl_relaxed(tim_ker->apbdiv) & APB_DIV_MASK;
+
+ timpre = readl_relaxed(tim_ker->timpre) & TIM_PRE_MASK;
+
+ if (!prescaler)
+ return parent_rate;
+
+ mul = (timpre + 1) * 2;
+
+ return parent_rate * mul;
+}
+
+static const struct clk_ops timer_ker_ops = {
+ .recalc_rate = timer_ker_recalc_rate,
+ .round_rate = timer_ker_round_rate,
+ .set_rate = timer_ker_set_rate,
+
+};
+
+static struct clk_hw *clk_register_cktim(struct device *dev, const char *name,
+ const char *parent_name,
+ unsigned long flags,
+ void __iomem *apbdiv,
+ void __iomem *timpre,
+ spinlock_t *lock)
+{
+ struct timer_cker *tim_ker;
+ struct clk_init_data init;
+ struct clk_hw *hw;
+ int err;
+
+ tim_ker = kzalloc(sizeof(*tim_ker), GFP_KERNEL);
+ if (!tim_ker)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &timer_ker_ops;
+ init.flags = flags;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ tim_ker->hw.init = &init;
+ tim_ker->lock = lock;
+ tim_ker->apbdiv = apbdiv;
+ tim_ker->timpre = timpre;
+
+ hw = &tim_ker->hw;
+ err = clk_hw_register(dev, hw);
+
+ if (err) {
+ kfree(tim_ker);
+ return ERR_PTR(err);
+ }
+
+ return hw;
+}
+
+struct stm32_pll_cfg {
+ u32 offset;
+};
+
+struct clk_hw *_clk_register_pll(struct device *dev,
+ struct clk_hw_onecell_data *clk_data,
+ void __iomem *base, spinlock_t *lock,
+ const struct clock_config *cfg)
+{
+ struct stm32_pll_cfg *stm_pll_cfg = cfg->cfg;
+
+ return clk_register_pll(dev, cfg->name, cfg->parent_name,
+ base + stm_pll_cfg->offset, cfg->flags, lock);
+}
+
+struct stm32_cktim_cfg {
+ u32 offset_apbdiv;
+ u32 offset_timpre;
+};
+
+static struct clk_hw *_clk_register_cktim(struct device *dev,
+ struct clk_hw_onecell_data *clk_data,
+ void __iomem *base, spinlock_t *lock,
+ const struct clock_config *cfg)
+{
+ struct stm32_cktim_cfg *cktim_cfg = cfg->cfg;
+
+ return clk_register_cktim(dev, cfg->name, cfg->parent_name, cfg->flags,
+ cktim_cfg->offset_apbdiv + base,
+ cktim_cfg->offset_timpre + base, lock);
+}
+
+static struct clk_hw *
+_clk_stm32_register_gate(struct device *dev,
+ struct clk_hw_onecell_data *clk_data,
+ void __iomem *base, spinlock_t *lock,
+ const struct clock_config *cfg)
+{
+ return clk_stm32_register_gate_ops(dev,
+ cfg->name,
+ cfg->parent_name,
+ cfg->flags,
+ base,
+ cfg->cfg,
+ lock);
+}
+
+static struct clk_hw *
+_clk_stm32_register_composite(struct device *dev,
+ struct clk_hw_onecell_data *clk_data,
+ void __iomem *base, spinlock_t *lock,
+ const struct clock_config *cfg)
+{
+ return clk_stm32_register_composite(dev, cfg->name, cfg->parent_names,
+ cfg->num_parents, base, cfg->cfg,
+ cfg->flags, lock);
+}
+
+#define GATE(_id, _name, _parent, _flags, _offset, _bit_idx, _gate_flags)\
+{\
+ .id = _id,\
+ .name = _name,\
+ .parent_name = _parent,\
+ .flags = _flags,\
+ .cfg = &(struct gate_cfg) {\
+ .reg_off = _offset,\
+ .bit_idx = _bit_idx,\
+ .gate_flags = _gate_flags,\
+ },\
+ .func = _clk_hw_register_gate,\
+}
+
+#define FIXED_FACTOR(_id, _name, _parent, _flags, _mult, _div)\
+{\
+ .id = _id,\
+ .name = _name,\
+ .parent_name = _parent,\
+ .flags = _flags,\
+ .cfg = &(struct fixed_factor_cfg) {\
+ .mult = _mult,\
+ .div = _div,\
+ },\
+ .func = _clk_hw_register_fixed_factor,\
+}
+
+#define DIV_TABLE(_id, _name, _parent, _flags, _offset, _shift, _width,\
+ _div_flags, _div_table)\
+{\
+ .id = _id,\
+ .name = _name,\
+ .parent_name = _parent,\
+ .flags = _flags,\
+ .cfg = &(struct div_cfg) {\
+ .reg_off = _offset,\
+ .shift = _shift,\
+ .width = _width,\
+ .div_flags = _div_flags,\
+ .table = _div_table,\
+ },\
+ .func = _clk_hw_register_divider_table,\
+}
+
+#define DIV(_id, _name, _parent, _flags, _offset, _shift, _width, _div_flags)\
+ DIV_TABLE(_id, _name, _parent, _flags, _offset, _shift, _width,\
+ _div_flags, NULL)
+
+#define MUX(_id, _name, _parents, _flags, _offset, _shift, _width, _mux_flags)\
+{\
+ .id = _id,\
+ .name = _name,\
+ .parent_names = _parents,\
+ .num_parents = ARRAY_SIZE(_parents),\
+ .flags = _flags,\
+ .cfg = &(struct mux_cfg) {\
+ .reg_off = _offset,\
+ .shift = _shift,\
+ .width = _width,\
+ .mux_flags = _mux_flags,\
+ },\
+ .func = _clk_hw_register_mux,\
+}
+
+#define PLL(_id, _name, _parent, _flags, _offset)\
+{\
+ .id = _id,\
+ .name = _name,\
+ .parent_name = _parent,\
+ .flags = _flags,\
+ .cfg = &(struct stm32_pll_cfg) {\
+ .offset = _offset,\
+ },\
+ .func = _clk_register_pll,\
+}
+
+#define STM32_CKTIM(_name, _parent, _flags, _offset_apbdiv, _offset_timpre)\
+{\
+ .id = NO_ID,\
+ .name = _name,\
+ .parent_name = _parent,\
+ .flags = _flags,\
+ .cfg = &(struct stm32_cktim_cfg) {\
+ .offset_apbdiv = _offset_apbdiv,\
+ .offset_timpre = _offset_timpre,\
+ },\
+ .func = _clk_register_cktim,\
+}
+
+#define STM32_TIM(_id, _name, _parent, _offset_set, _bit_idx)\
+ GATE_MP1(_id, _name, _parent, CLK_SET_RATE_PARENT,\
+ _offset_set, _bit_idx, 0)
+
+/* STM32 GATE */
+#define STM32_GATE(_id, _name, _parent, _flags, _gate)\
+{\
+ .id = _id,\
+ .name = _name,\
+ .parent_name = _parent,\
+ .flags = _flags,\
+ .cfg = (struct stm32_gate_cfg *) {_gate},\
+ .func = _clk_stm32_register_gate,\
+}
+
+#define _STM32_GATE(_gate_offset, _gate_bit_idx, _gate_flags, _mgate, _ops)\
+ (&(struct stm32_gate_cfg) {\
+ &(struct gate_cfg) {\
+ .reg_off = _gate_offset,\
+ .bit_idx = _gate_bit_idx,\
+ .gate_flags = _gate_flags,\
+ },\
+ .mgate = _mgate,\
+ .ops = _ops,\
+ })
+
+#define _STM32_MGATE(_mgate)\
+ (&per_gate_cfg[_mgate])
+
+#define _GATE(_gate_offset, _gate_bit_idx, _gate_flags)\
+ _STM32_GATE(_gate_offset, _gate_bit_idx, _gate_flags,\
+ NULL, NULL)\
+
+#define _GATE_MP1(_gate_offset, _gate_bit_idx, _gate_flags)\
+ _STM32_GATE(_gate_offset, _gate_bit_idx, _gate_flags,\
+ NULL, &mp1_gate_clk_ops)\
+
+#define _MGATE_MP1(_mgate)\
+ .gate = &per_gate_cfg[_mgate]
+
+#define GATE_MP1(_id, _name, _parent, _flags, _offset, _bit_idx, _gate_flags)\
+ STM32_GATE(_id, _name, _parent, _flags,\
+ _GATE_MP1(_offset, _bit_idx, _gate_flags))
+
+#define MGATE_MP1(_id, _name, _parent, _flags, _mgate)\
+ STM32_GATE(_id, _name, _parent, _flags,\
+ _STM32_MGATE(_mgate))
+
+#define _STM32_DIV(_div_offset, _div_shift, _div_width,\
+ _div_flags, _div_table, _ops)\
+ .div = &(struct stm32_div_cfg) {\
+ &(struct div_cfg) {\
+ .reg_off = _div_offset,\
+ .shift = _div_shift,\
+ .width = _div_width,\
+ .div_flags = _div_flags,\
+ .table = _div_table,\
+ },\
+ .ops = _ops,\
+ }
+
+#define _DIV(_div_offset, _div_shift, _div_width, _div_flags, _div_table)\
+ _STM32_DIV(_div_offset, _div_shift, _div_width,\
+ _div_flags, _div_table, NULL)\
+
+#define _STM32_MUX(_offset, _shift, _width, _mux_flags, _mmux, _ops)\
+ .mux = &(struct stm32_mux_cfg) {\
+ &(struct mux_cfg) {\
+ .reg_off = _offset,\
+ .shift = _shift,\
+ .width = _width,\
+ .mux_flags = _mux_flags,\
+ .table = NULL,\
+ },\
+ .mmux = _mmux,\
+ .ops = _ops,\
+ }
+
+#define _MUX(_offset, _shift, _width, _mux_flags)\
+ _STM32_MUX(_offset, _shift, _width, _mux_flags, NULL, NULL)\
+
+#define _MMUX(_mmux) .mux = &ker_mux_cfg[_mmux]
+
+#define PARENT(_parent) ((const char *[]) { _parent})
+
+#define _NO_MUX .mux = NULL
+#define _NO_DIV .div = NULL
+#define _NO_GATE .gate = NULL
+
+#define COMPOSITE(_id, _name, _parents, _flags, _gate, _mux, _div)\
+{\
+ .id = _id,\
+ .name = _name,\
+ .parent_names = _parents,\
+ .num_parents = ARRAY_SIZE(_parents),\
+ .flags = _flags,\
+ .cfg = &(struct stm32_composite_cfg) {\
+ _gate,\
+ _mux,\
+ _div,\
+ },\
+ .func = _clk_stm32_register_composite,\
+}
+
+#define PCLK(_id, _name, _parent, _flags, _mgate)\
+ MGATE_MP1(_id, _name, _parent, _flags, _mgate)
+
+#define KCLK(_id, _name, _parents, _flags, _mgate, _mmux)\
+ COMPOSITE(_id, _name, _parents, CLK_OPS_PARENT_ENABLE | _flags,\
+ _MGATE_MP1(_mgate),\
+ _MMUX(_mmux),\
+ _NO_DIV)
+
+enum {
+ G_SAI1,
+ G_SAI2,
+ G_SAI3,
+ G_SAI4,
+ G_SPI1,
+ G_SPI2,
+ G_SPI3,
+ G_SPI4,
+ G_SPI5,
+ G_SPI6,
+ G_SPDIF,
+ G_I2C1,
+ G_I2C2,
+ G_I2C3,
+ G_I2C4,
+ G_I2C5,
+ G_I2C6,
+ G_USART2,
+ G_UART4,
+ G_USART3,
+ G_UART5,
+ G_USART1,
+ G_USART6,
+ G_UART7,
+ G_UART8,
+ G_LPTIM1,
+ G_LPTIM2,
+ G_LPTIM3,
+ G_LPTIM4,
+ G_LPTIM5,
+ G_LTDC,
+ G_DSI,
+ G_QSPI,
+ G_FMC,
+ G_SDMMC1,
+ G_SDMMC2,
+ G_SDMMC3,
+ G_USBO,
+ G_USBPHY,
+ G_RNG1,
+ G_RNG2,
+ G_FDCAN,
+ G_DAC12,
+ G_CEC,
+ G_ADC12,
+ G_GPU,
+ G_STGEN,
+ G_DFSDM,
+ G_ADFSDM,
+ G_TIM2,
+ G_TIM3,
+ G_TIM4,
+ G_TIM5,
+ G_TIM6,
+ G_TIM7,
+ G_TIM12,
+ G_TIM13,
+ G_TIM14,
+ G_MDIO,
+ G_TIM1,
+ G_TIM8,
+ G_TIM15,
+ G_TIM16,
+ G_TIM17,
+ G_SYSCFG,
+ G_VREF,
+ G_TMPSENS,
+ G_PMBCTRL,
+ G_HDP,
+ G_IWDG2,
+ G_STGENRO,
+ G_DMA1,
+ G_DMA2,
+ G_DMAMUX,
+ G_DCMI,
+ G_CRYP2,
+ G_HASH2,
+ G_CRC2,
+ G_HSEM,
+ G_IPCC,
+ G_GPIOA,
+ G_GPIOB,
+ G_GPIOC,
+ G_GPIOD,
+ G_GPIOE,
+ G_GPIOF,
+ G_GPIOG,
+ G_GPIOH,
+ G_GPIOI,
+ G_GPIOJ,
+ G_GPIOK,
+ G_MDMA,
+ G_ETHCK,
+ G_ETHTX,
+ G_ETHRX,
+ G_ETHMAC,
+ G_CRC1,
+ G_USBH,
+ G_ETHSTP,
+ G_RTCAPB,
+ G_TZC,
+ G_TZPC,
+ G_IWDG1,
+ G_BSEC,
+ G_GPIOZ,
+ G_CRYP1,
+ G_HASH1,
+ G_BKPSRAM,
+
+ G_LAST
+};
+
+struct stm32_mgate mp1_mgate[G_LAST];
+
+#define _K_GATE(_id, _gate_offset, _gate_bit_idx, _gate_flags,\
+ _mgate, _ops)\
+ [_id] = {\
+ &(struct gate_cfg) {\
+ .reg_off = _gate_offset,\
+ .bit_idx = _gate_bit_idx,\
+ .gate_flags = _gate_flags,\
+ },\
+ .mgate = _mgate,\
+ .ops = _ops,\
+ }
+
+#define K_GATE(_id, _gate_offset, _gate_bit_idx, _gate_flags)\
+ _K_GATE(_id, _gate_offset, _gate_bit_idx, _gate_flags,\
+ NULL, &mp1_gate_clk_ops)
+
+#define K_MGATE(_id, _gate_offset, _gate_bit_idx, _gate_flags)\
+ _K_GATE(_id, _gate_offset, _gate_bit_idx, _gate_flags,\
+ &mp1_mgate[_id], &mp1_mgate_clk_ops)
+
+/* Peripheral gates */
+struct stm32_gate_cfg per_gate_cfg[G_LAST] = {
+ /* Multi gates */
+ K_GATE(G_MDIO, RCC_APB1ENSETR, 31, 0),
+ K_MGATE(G_DAC12, RCC_APB1ENSETR, 29, 0),
+ K_MGATE(G_CEC, RCC_APB1ENSETR, 27, 0),
+ K_MGATE(G_SPDIF, RCC_APB1ENSETR, 26, 0),
+ K_MGATE(G_I2C5, RCC_APB1ENSETR, 24, 0),
+ K_MGATE(G_I2C3, RCC_APB1ENSETR, 23, 0),
+ K_MGATE(G_I2C2, RCC_APB1ENSETR, 22, 0),
+ K_MGATE(G_I2C1, RCC_APB1ENSETR, 21, 0),
+ K_MGATE(G_UART8, RCC_APB1ENSETR, 19, 0),
+ K_MGATE(G_UART7, RCC_APB1ENSETR, 18, 0),
+ K_MGATE(G_UART5, RCC_APB1ENSETR, 17, 0),
+ K_MGATE(G_UART4, RCC_APB1ENSETR, 16, 0),
+ K_MGATE(G_USART3, RCC_APB1ENSETR, 15, 0),
+ K_MGATE(G_USART2, RCC_APB1ENSETR, 14, 0),
+ K_MGATE(G_SPI3, RCC_APB1ENSETR, 12, 0),
+ K_MGATE(G_SPI2, RCC_APB1ENSETR, 11, 0),
+ K_MGATE(G_LPTIM1, RCC_APB1ENSETR, 9, 0),
+ K_GATE(G_TIM14, RCC_APB1ENSETR, 8, 0),
+ K_GATE(G_TIM13, RCC_APB1ENSETR, 7, 0),
+ K_GATE(G_TIM12, RCC_APB1ENSETR, 6, 0),
+ K_GATE(G_TIM7, RCC_APB1ENSETR, 5, 0),
+ K_GATE(G_TIM6, RCC_APB1ENSETR, 4, 0),
+ K_GATE(G_TIM5, RCC_APB1ENSETR, 3, 0),
+ K_GATE(G_TIM4, RCC_APB1ENSETR, 2, 0),
+ K_GATE(G_TIM3, RCC_APB1ENSETR, 1, 0),
+ K_GATE(G_TIM2, RCC_APB1ENSETR, 0, 0),
+
+ K_MGATE(G_FDCAN, RCC_APB2ENSETR, 24, 0),
+ K_GATE(G_ADFSDM, RCC_APB2ENSETR, 21, 0),
+ K_GATE(G_DFSDM, RCC_APB2ENSETR, 20, 0),
+ K_MGATE(G_SAI3, RCC_APB2ENSETR, 18, 0),
+ K_MGATE(G_SAI2, RCC_APB2ENSETR, 17, 0),
+ K_MGATE(G_SAI1, RCC_APB2ENSETR, 16, 0),
+ K_MGATE(G_USART6, RCC_APB2ENSETR, 13, 0),
+ K_MGATE(G_SPI5, RCC_APB2ENSETR, 10, 0),
+ K_MGATE(G_SPI4, RCC_APB2ENSETR, 9, 0),
+ K_MGATE(G_SPI1, RCC_APB2ENSETR, 8, 0),
+ K_GATE(G_TIM17, RCC_APB2ENSETR, 4, 0),
+ K_GATE(G_TIM16, RCC_APB2ENSETR, 3, 0),
+ K_GATE(G_TIM15, RCC_APB2ENSETR, 2, 0),
+ K_GATE(G_TIM8, RCC_APB2ENSETR, 1, 0),
+ K_GATE(G_TIM1, RCC_APB2ENSETR, 0, 0),
+
+ K_GATE(G_HDP, RCC_APB3ENSETR, 20, 0),
+ K_GATE(G_PMBCTRL, RCC_APB3ENSETR, 17, 0),
+ K_GATE(G_TMPSENS, RCC_APB3ENSETR, 16, 0),
+ K_GATE(G_VREF, RCC_APB3ENSETR, 13, 0),
+ K_GATE(G_SYSCFG, RCC_APB3ENSETR, 11, 0),
+ K_MGATE(G_SAI4, RCC_APB3ENSETR, 8, 0),
+ K_MGATE(G_LPTIM5, RCC_APB3ENSETR, 3, 0),
+ K_MGATE(G_LPTIM4, RCC_APB3ENSETR, 2, 0),
+ K_MGATE(G_LPTIM3, RCC_APB3ENSETR, 1, 0),
+ K_MGATE(G_LPTIM2, RCC_APB3ENSETR, 0, 0),
+
+ K_GATE(G_STGENRO, RCC_APB4ENSETR, 20, 0),
+ K_MGATE(G_USBPHY, RCC_APB4ENSETR, 16, 0),
+ K_GATE(G_IWDG2, RCC_APB4ENSETR, 15, 0),
+ K_MGATE(G_DSI, RCC_APB4ENSETR, 4, 0),
+ K_MGATE(G_LTDC, RCC_APB4ENSETR, 0, 0),
+
+ K_GATE(G_STGEN, RCC_APB5ENSETR, 20, 0),
+ K_GATE(G_BSEC, RCC_APB5ENSETR, 16, 0),
+ K_GATE(G_IWDG1, RCC_APB5ENSETR, 15, 0),
+ K_GATE(G_TZPC, RCC_APB5ENSETR, 13, 0),
+ K_GATE(G_TZC, RCC_APB5ENSETR, 12, 0),
+ K_GATE(G_RTCAPB, RCC_APB5ENSETR, 8, 0),
+ K_MGATE(G_USART1, RCC_APB5ENSETR, 4, 0),
+ K_MGATE(G_I2C6, RCC_APB5ENSETR, 3, 0),
+ K_MGATE(G_I2C4, RCC_APB5ENSETR, 2, 0),
+ K_MGATE(G_SPI6, RCC_APB5ENSETR, 0, 0),
+
+ K_MGATE(G_SDMMC3, RCC_AHB2ENSETR, 16, 0),
+ K_MGATE(G_USBO, RCC_AHB2ENSETR, 8, 0),
+ K_MGATE(G_ADC12, RCC_AHB2ENSETR, 5, 0),
+ K_GATE(G_DMAMUX, RCC_AHB2ENSETR, 2, 0),
+ K_GATE(G_DMA2, RCC_AHB2ENSETR, 1, 0),
+ K_GATE(G_DMA1, RCC_AHB2ENSETR, 0, 0),
+
+ K_GATE(G_IPCC, RCC_AHB3ENSETR, 12, 0),
+ K_GATE(G_HSEM, RCC_AHB3ENSETR, 11, 0),
+ K_GATE(G_CRC2, RCC_AHB3ENSETR, 7, 0),
+ K_MGATE(G_RNG2, RCC_AHB3ENSETR, 6, 0),
+ K_GATE(G_HASH2, RCC_AHB3ENSETR, 5, 0),
+ K_GATE(G_CRYP2, RCC_AHB3ENSETR, 4, 0),
+ K_GATE(G_DCMI, RCC_AHB3ENSETR, 0, 0),
+
+ K_GATE(G_GPIOK, RCC_AHB4ENSETR, 10, 0),
+ K_GATE(G_GPIOJ, RCC_AHB4ENSETR, 9, 0),
+ K_GATE(G_GPIOI, RCC_AHB4ENSETR, 8, 0),
+ K_GATE(G_GPIOH, RCC_AHB4ENSETR, 7, 0),
+ K_GATE(G_GPIOG, RCC_AHB4ENSETR, 6, 0),
+ K_GATE(G_GPIOF, RCC_AHB4ENSETR, 5, 0),
+ K_GATE(G_GPIOE, RCC_AHB4ENSETR, 4, 0),
+ K_GATE(G_GPIOD, RCC_AHB4ENSETR, 3, 0),
+ K_GATE(G_GPIOC, RCC_AHB4ENSETR, 2, 0),
+ K_GATE(G_GPIOB, RCC_AHB4ENSETR, 1, 0),
+ K_GATE(G_GPIOA, RCC_AHB4ENSETR, 0, 0),
+
+ K_GATE(G_BKPSRAM, RCC_AHB5ENSETR, 8, 0),
+ K_MGATE(G_RNG1, RCC_AHB5ENSETR, 6, 0),
+ K_GATE(G_HASH1, RCC_AHB5ENSETR, 5, 0),
+ K_GATE(G_CRYP1, RCC_AHB5ENSETR, 4, 0),
+ K_GATE(G_GPIOZ, RCC_AHB5ENSETR, 0, 0),
+
+ K_GATE(G_USBH, RCC_AHB6ENSETR, 24, 0),
+ K_GATE(G_CRC1, RCC_AHB6ENSETR, 20, 0),
+ K_MGATE(G_SDMMC2, RCC_AHB6ENSETR, 17, 0),
+ K_MGATE(G_SDMMC1, RCC_AHB6ENSETR, 16, 0),
+ K_MGATE(G_QSPI, RCC_AHB6ENSETR, 14, 0),
+ K_MGATE(G_FMC, RCC_AHB6ENSETR, 12, 0),
+ K_GATE(G_ETHMAC, RCC_AHB6ENSETR, 10, 0),
+ K_GATE(G_ETHRX, RCC_AHB6ENSETR, 9, 0),
+ K_GATE(G_ETHTX, RCC_AHB6ENSETR, 8, 0),
+ K_GATE(G_ETHCK, RCC_AHB6ENSETR, 7, 0),
+ K_MGATE(G_GPU, RCC_AHB6ENSETR, 5, 0),
+ K_GATE(G_MDMA, RCC_AHB6ENSETR, 0, 0),
+ K_GATE(G_ETHSTP, RCC_AHB6LPENSETR, 11, 0),
+};
+
+enum {
+ M_SDMMC12,
+ M_SDMMC3,
+ M_FMC,
+ M_QSPI,
+ M_RNG1,
+ M_RNG2,
+ M_USBPHY,
+ M_USBO,
+ M_STGEN,
+ M_SPDIF,
+ M_SPI1,
+ M_SPI23,
+ M_SPI45,
+ M_SPI6,
+ M_CEC,
+ M_I2C12,
+ M_I2C35,
+ M_I2C46,
+ M_LPTIM1,
+ M_LPTIM23,
+ M_LPTIM45,
+ M_USART1,
+ M_UART24,
+ M_UART35,
+ M_USART6,
+ M_UART78,
+ M_SAI1,
+ M_SAI2,
+ M_SAI3,
+ M_SAI4,
+ M_DSI,
+ M_FDCAN,
+ M_ADC12,
+ M_ETHCK,
+ M_CKPER,
+ M_LAST
+};
+
+struct stm32_mmux ker_mux[M_LAST];
+
+#define _K_MUX(_id, _offset, _shift, _width, _mux_flags, _mmux, _ops)\
+ [_id] = {\
+ &(struct mux_cfg) {\
+ .reg_off = _offset,\
+ .shift = _shift,\
+ .width = _width,\
+ .mux_flags = _mux_flags,\
+ .table = NULL,\
+ },\
+ .mmux = _mmux,\
+ .ops = _ops,\
+ }
+
+#define K_MUX(_id, _offset, _shift, _width, _mux_flags)\
+ _K_MUX(_id, _offset, _shift, _width, _mux_flags,\
+ NULL, NULL)
+
+#define K_MMUX(_id, _offset, _shift, _width, _mux_flags)\
+ _K_MUX(_id, _offset, _shift, _width, _mux_flags,\
+ &ker_mux[_id], &clk_mmux_ops)
+
+const struct stm32_mux_cfg ker_mux_cfg[M_LAST] = {
+ /* Kernel multi mux */
+ K_MMUX(M_SDMMC12, RCC_SDMMC12CKSELR, 0, 3, 0),
+ K_MMUX(M_SPI23, RCC_SPI2S23CKSELR, 0, 3, 0),
+ K_MMUX(M_SPI45, RCC_SPI2S45CKSELR, 0, 3, 0),
+ K_MMUX(M_I2C12, RCC_I2C12CKSELR, 0, 3, 0),
+ K_MMUX(M_I2C35, RCC_I2C35CKSELR, 0, 3, 0),
+ K_MMUX(M_LPTIM23, RCC_LPTIM23CKSELR, 0, 3, 0),
+ K_MMUX(M_LPTIM45, RCC_LPTIM45CKSELR, 0, 3, 0),
+ K_MMUX(M_UART24, RCC_UART24CKSELR, 0, 3, 0),
+ K_MMUX(M_UART35, RCC_UART35CKSELR, 0, 3, 0),
+ K_MMUX(M_UART78, RCC_UART78CKSELR, 0, 3, 0),
+ K_MMUX(M_SAI1, RCC_SAI1CKSELR, 0, 3, 0),
+ K_MMUX(M_ETHCK, RCC_ETHCKSELR, 0, 2, 0),
+ K_MMUX(M_I2C46, RCC_I2C46CKSELR, 0, 3, 0),
+
+ /* Kernel simple mux */
+ K_MUX(M_RNG2, RCC_RNG2CKSELR, 0, 2, 0),
+ K_MUX(M_SDMMC3, RCC_SDMMC3CKSELR, 0, 3, 0),
+ K_MUX(M_FMC, RCC_FMCCKSELR, 0, 2, 0),
+ K_MUX(M_QSPI, RCC_QSPICKSELR, 0, 2, 0),
+ K_MUX(M_USBPHY, RCC_USBCKSELR, 0, 2, 0),
+ K_MUX(M_USBO, RCC_USBCKSELR, 4, 1, 0),
+ K_MUX(M_SPDIF, RCC_SPDIFCKSELR, 0, 2, 0),
+ K_MUX(M_SPI1, RCC_SPI2S1CKSELR, 0, 3, 0),
+ K_MUX(M_CEC, RCC_CECCKSELR, 0, 2, 0),
+ K_MUX(M_LPTIM1, RCC_LPTIM1CKSELR, 0, 3, 0),
+ K_MUX(M_USART6, RCC_UART6CKSELR, 0, 3, 0),
+ K_MUX(M_FDCAN, RCC_FDCANCKSELR, 0, 2, 0),
+ K_MUX(M_SAI2, RCC_SAI2CKSELR, 0, 3, 0),
+ K_MUX(M_SAI3, RCC_SAI3CKSELR, 0, 3, 0),
+ K_MUX(M_SAI4, RCC_SAI4CKSELR, 0, 3, 0),
+ K_MUX(M_ADC12, RCC_ADCCKSELR, 0, 2, 0),
+ K_MUX(M_DSI, RCC_DSICKSELR, 0, 1, 0),
+ K_MUX(M_CKPER, RCC_CPERCKSELR, 0, 2, 0),
+ K_MUX(M_RNG1, RCC_RNG1CKSELR, 0, 2, 0),
+ K_MUX(M_STGEN, RCC_STGENCKSELR, 0, 2, 0),
+ K_MUX(M_USART1, RCC_UART1CKSELR, 0, 3, 0),
+ K_MUX(M_SPI6, RCC_SPI6CKSELR, 0, 3, 0),
+};
+
+static const struct clock_config stm32mp1_clock_cfg[] = {
+ /* Oscillator divider */
+ DIV(NO_ID, "clk-hsi-div", "clk-hsi", 0, RCC_HSICFGR, 0, 2,
+ CLK_DIVIDER_READ_ONLY),
+
+ /* External / Internal Oscillators */
+ GATE_MP1(CK_HSE, "ck_hse", "clk-hse", 0, RCC_OCENSETR, 8, 0),
+ GATE_MP1(CK_CSI, "ck_csi", "clk-csi", 0, RCC_OCENSETR, 4, 0),
+ GATE_MP1(CK_HSI, "ck_hsi", "clk-hsi-div", 0, RCC_OCENSETR, 0, 0),
+ GATE(CK_LSI, "ck_lsi", "clk-lsi", 0, RCC_RDLSICR, 0, 0),
+ GATE(CK_LSE, "ck_lse", "clk-lse", 0, RCC_BDCR, 0, 0),
+
+ FIXED_FACTOR(CK_HSE_DIV2, "clk-hse-div2", "ck_hse", 0, 1, 2),
+
+ /* ref clock pll */
+ MUX(NO_ID, "ref1", ref12_parents, CLK_OPS_PARENT_ENABLE, RCC_RCK12SELR,
+ 0, 2, CLK_MUX_READ_ONLY),
+
+ MUX(NO_ID, "ref3", ref3_parents, CLK_OPS_PARENT_ENABLE, RCC_RCK3SELR,
+ 0, 2, CLK_MUX_READ_ONLY),
+
+ MUX(NO_ID, "ref4", ref4_parents, CLK_OPS_PARENT_ENABLE, RCC_RCK4SELR,
+ 0, 2, CLK_MUX_READ_ONLY),
+
+ /* PLLs */
+ PLL(PLL1, "pll1", "ref1", CLK_IGNORE_UNUSED, RCC_PLL1CR),
+ PLL(PLL2, "pll2", "ref1", CLK_IGNORE_UNUSED, RCC_PLL2CR),
+ PLL(PLL3, "pll3", "ref3", CLK_IGNORE_UNUSED, RCC_PLL3CR),
+ PLL(PLL4, "pll4", "ref4", CLK_IGNORE_UNUSED, RCC_PLL4CR),
+
+ /* ODF */
+ COMPOSITE(PLL1_P, "pll1_p", PARENT("pll1"), 0,
+ _GATE(RCC_PLL1CR, 4, 0),
+ _NO_MUX,
+ _DIV(RCC_PLL1CFGR2, 0, 7, 0, NULL)),
+
+ COMPOSITE(PLL2_P, "pll2_p", PARENT("pll2"), 0,
+ _GATE(RCC_PLL2CR, 4, 0),
+ _NO_MUX,
+ _DIV(RCC_PLL2CFGR2, 0, 7, 0, NULL)),
+
+ COMPOSITE(PLL2_Q, "pll2_q", PARENT("pll2"), 0,
+ _GATE(RCC_PLL2CR, 5, 0),
+ _NO_MUX,
+ _DIV(RCC_PLL2CFGR2, 8, 7, 0, NULL)),
+
+ COMPOSITE(PLL2_R, "pll2_r", PARENT("pll2"), CLK_IS_CRITICAL,
+ _GATE(RCC_PLL2CR, 6, 0),
+ _NO_MUX,
+ _DIV(RCC_PLL2CFGR2, 16, 7, 0, NULL)),
+
+ COMPOSITE(PLL3_P, "pll3_p", PARENT("pll3"), 0,
+ _GATE(RCC_PLL3CR, 4, 0),
+ _NO_MUX,
+ _DIV(RCC_PLL3CFGR2, 0, 7, 0, NULL)),
+
+ COMPOSITE(PLL3_Q, "pll3_q", PARENT("pll3"), 0,
+ _GATE(RCC_PLL3CR, 5, 0),
+ _NO_MUX,
+ _DIV(RCC_PLL3CFGR2, 8, 7, 0, NULL)),
+
+ COMPOSITE(PLL3_R, "pll3_r", PARENT("pll3"), 0,
+ _GATE(RCC_PLL3CR, 6, 0),
+ _NO_MUX,
+ _DIV(RCC_PLL3CFGR2, 16, 7, 0, NULL)),
+
+ COMPOSITE(PLL4_P, "pll4_p", PARENT("pll4"), 0,
+ _GATE(RCC_PLL4CR, 4, 0),
+ _NO_MUX,
+ _DIV(RCC_PLL4CFGR2, 0, 7, 0, NULL)),
+
+ COMPOSITE(PLL4_Q, "pll4_q", PARENT("pll4"), 0,
+ _GATE(RCC_PLL4CR, 5, 0),
+ _NO_MUX,
+ _DIV(RCC_PLL4CFGR2, 8, 7, 0, NULL)),
+
+ COMPOSITE(PLL4_R, "pll4_r", PARENT("pll4"), 0,
+ _GATE(RCC_PLL4CR, 6, 0),
+ _NO_MUX,
+ _DIV(RCC_PLL4CFGR2, 16, 7, 0, NULL)),
+
+ /* MUX system clocks */
+ MUX(CK_PER, "ck_per", per_src, CLK_OPS_PARENT_ENABLE,
+ RCC_CPERCKSELR, 0, 2, 0),
+
+ MUX(CK_MPU, "ck_mpu", cpu_src, CLK_OPS_PARENT_ENABLE |
+ CLK_IS_CRITICAL, RCC_MPCKSELR, 0, 2, 0),
+
+ COMPOSITE(CK_AXI, "ck_axi", axi_src, CLK_IS_CRITICAL |
+ CLK_OPS_PARENT_ENABLE,
+ _NO_GATE,
+ _MUX(RCC_ASSCKSELR, 0, 2, 0),
+ _DIV(RCC_AXIDIVR, 0, 3, 0, axi_div_table)),
+
+ COMPOSITE(CK_MCU, "ck_mcu", mcu_src, CLK_IS_CRITICAL |
+ CLK_OPS_PARENT_ENABLE,
+ _NO_GATE,
+ _MUX(RCC_MSSCKSELR, 0, 2, 0),
+ _DIV(RCC_MCUDIVR, 0, 4, 0, mcu_div_table)),
+
+ DIV_TABLE(NO_ID, "pclk1", "ck_mcu", CLK_IGNORE_UNUSED, RCC_APB1DIVR, 0,
+ 3, CLK_DIVIDER_READ_ONLY, apb_div_table),
+
+ DIV_TABLE(NO_ID, "pclk2", "ck_mcu", CLK_IGNORE_UNUSED, RCC_APB2DIVR, 0,
+ 3, CLK_DIVIDER_READ_ONLY, apb_div_table),
+
+ DIV_TABLE(NO_ID, "pclk3", "ck_mcu", CLK_IGNORE_UNUSED, RCC_APB3DIVR, 0,
+ 3, CLK_DIVIDER_READ_ONLY, apb_div_table),
+
+ DIV_TABLE(NO_ID, "pclk4", "ck_axi", CLK_IGNORE_UNUSED, RCC_APB4DIVR, 0,
+ 3, CLK_DIVIDER_READ_ONLY, apb_div_table),
+
+ DIV_TABLE(NO_ID, "pclk5", "ck_axi", CLK_IGNORE_UNUSED, RCC_APB5DIVR, 0,
+ 3, CLK_DIVIDER_READ_ONLY, apb_div_table),
+
+ /* Kernel Timers */
+ STM32_CKTIM("ck1_tim", "pclk1", 0, RCC_APB1DIVR, RCC_TIMG1PRER),
+ STM32_CKTIM("ck2_tim", "pclk2", 0, RCC_APB2DIVR, RCC_TIMG2PRER),
+
+ STM32_TIM(TIM2_K, "tim2_k", "ck1_tim", RCC_APB1ENSETR, 0),
+ STM32_TIM(TIM3_K, "tim3_k", "ck1_tim", RCC_APB1ENSETR, 1),
+ STM32_TIM(TIM4_K, "tim4_k", "ck1_tim", RCC_APB1ENSETR, 2),
+ STM32_TIM(TIM5_K, "tim5_k", "ck1_tim", RCC_APB1ENSETR, 3),
+ STM32_TIM(TIM6_K, "tim6_k", "ck1_tim", RCC_APB1ENSETR, 4),
+ STM32_TIM(TIM7_K, "tim7_k", "ck1_tim", RCC_APB1ENSETR, 5),
+ STM32_TIM(TIM12_K, "tim12_k", "ck1_tim", RCC_APB1ENSETR, 6),
+ STM32_TIM(TIM13_K, "tim13_k", "ck1_tim", RCC_APB1ENSETR, 7),
+ STM32_TIM(TIM14_K, "tim14_k", "ck1_tim", RCC_APB1ENSETR, 8),
+ STM32_TIM(TIM1_K, "tim1_k", "ck2_tim", RCC_APB2ENSETR, 0),
+ STM32_TIM(TIM8_K, "tim8_k", "ck2_tim", RCC_APB2ENSETR, 1),
+ STM32_TIM(TIM15_K, "tim15_k", "ck2_tim", RCC_APB2ENSETR, 2),
+ STM32_TIM(TIM16_K, "tim16_k", "ck2_tim", RCC_APB2ENSETR, 3),
+ STM32_TIM(TIM17_K, "tim17_k", "ck2_tim", RCC_APB2ENSETR, 4),
+
+ /* Peripheral clocks */
+ PCLK(TIM2, "tim2", "pclk1", CLK_IGNORE_UNUSED, G_TIM2),
+ PCLK(TIM3, "tim3", "pclk1", CLK_IGNORE_UNUSED, G_TIM3),
+ PCLK(TIM4, "tim4", "pclk1", CLK_IGNORE_UNUSED, G_TIM4),
+ PCLK(TIM5, "tim5", "pclk1", CLK_IGNORE_UNUSED, G_TIM5),
+ PCLK(TIM6, "tim6", "pclk1", CLK_IGNORE_UNUSED, G_TIM6),
+ PCLK(TIM7, "tim7", "pclk1", CLK_IGNORE_UNUSED, G_TIM7),
+ PCLK(TIM12, "tim12", "pclk1", CLK_IGNORE_UNUSED, G_TIM12),
+ PCLK(TIM13, "tim13", "pclk1", CLK_IGNORE_UNUSED, G_TIM13),
+ PCLK(TIM14, "tim14", "pclk1", CLK_IGNORE_UNUSED, G_TIM14),
+ PCLK(LPTIM1, "lptim1", "pclk1", 0, G_LPTIM1),
+ PCLK(SPI2, "spi2", "pclk1", 0, G_SPI2),
+ PCLK(SPI3, "spi3", "pclk1", 0, G_SPI3),
+ PCLK(USART2, "usart2", "pclk1", 0, G_USART2),
+ PCLK(USART3, "usart3", "pclk1", 0, G_USART3),
+ PCLK(UART4, "uart4", "pclk1", 0, G_UART4),
+ PCLK(UART5, "uart5", "pclk1", 0, G_UART5),
+ PCLK(UART7, "uart7", "pclk1", 0, G_UART7),
+ PCLK(UART8, "uart8", "pclk1", 0, G_UART8),
+ PCLK(I2C1, "i2c1", "pclk1", 0, G_I2C1),
+ PCLK(I2C2, "i2c2", "pclk1", 0, G_I2C2),
+ PCLK(I2C3, "i2c3", "pclk1", 0, G_I2C3),
+ PCLK(I2C5, "i2c5", "pclk1", 0, G_I2C5),
+ PCLK(SPDIF, "spdif", "pclk1", 0, G_SPDIF),
+ PCLK(CEC, "cec", "pclk1", 0, G_CEC),
+ PCLK(DAC12, "dac12", "pclk1", 0, G_DAC12),
+ PCLK(MDIO, "mdio", "pclk1", 0, G_MDIO),
+ PCLK(TIM1, "tim1", "pclk2", CLK_IGNORE_UNUSED, G_TIM1),
+ PCLK(TIM8, "tim8", "pclk2", CLK_IGNORE_UNUSED, G_TIM8),
+ PCLK(TIM15, "tim15", "pclk2", CLK_IGNORE_UNUSED, G_TIM15),
+ PCLK(TIM16, "tim16", "pclk2", CLK_IGNORE_UNUSED, G_TIM16),
+ PCLK(TIM17, "tim17", "pclk2", CLK_IGNORE_UNUSED, G_TIM17),
+ PCLK(SPI1, "spi1", "pclk2", 0, G_SPI1),
+ PCLK(SPI4, "spi4", "pclk2", 0, G_SPI4),
+ PCLK(SPI5, "spi5", "pclk2", 0, G_SPI5),
+ PCLK(USART6, "usart6", "pclk2", 0, G_USART6),
+ PCLK(SAI1, "sai1", "pclk2", 0, G_SAI1),
+ PCLK(SAI2, "sai2", "pclk2", 0, G_SAI2),
+ PCLK(SAI3, "sai3", "pclk2", 0, G_SAI3),
+ PCLK(DFSDM, "dfsdm", "pclk2", 0, G_DFSDM),
+ PCLK(FDCAN, "fdcan", "pclk2", 0, G_FDCAN),
+ PCLK(LPTIM2, "lptim2", "pclk3", 0, G_LPTIM2),
+ PCLK(LPTIM3, "lptim3", "pclk3", 0, G_LPTIM3),
+ PCLK(LPTIM4, "lptim4", "pclk3", 0, G_LPTIM4),
+ PCLK(LPTIM5, "lptim5", "pclk3", 0, G_LPTIM5),
+ PCLK(SAI4, "sai4", "pclk3", 0, G_SAI4),
+ PCLK(SYSCFG, "syscfg", "pclk3", 0, G_SYSCFG),
+ PCLK(VREF, "vref", "pclk3", 13, G_VREF),
+ PCLK(TMPSENS, "tmpsens", "pclk3", 0, G_TMPSENS),
+ PCLK(PMBCTRL, "pmbctrl", "pclk3", 0, G_PMBCTRL),
+ PCLK(HDP, "hdp", "pclk3", 0, G_HDP),
+ PCLK(LTDC, "ltdc", "pclk4", 0, G_LTDC),
+ PCLK(DSI, "dsi", "pclk4", 0, G_DSI),
+ PCLK(IWDG2, "iwdg2", "pclk4", 0, G_IWDG2),
+ PCLK(USBPHY, "usbphy", "pclk4", 0, G_USBPHY),
+ PCLK(STGENRO, "stgenro", "pclk4", 0, G_STGENRO),
+ PCLK(SPI6, "spi6", "pclk5", 0, G_SPI6),
+ PCLK(I2C4, "i2c4", "pclk5", 0, G_I2C4),
+ PCLK(I2C6, "i2c6", "pclk5", 0, G_I2C6),
+ PCLK(USART1, "usart1", "pclk5", 0, G_USART1),
+ PCLK(RTCAPB, "rtcapb", "pclk5", CLK_IGNORE_UNUSED |
+ CLK_IS_CRITICAL, G_RTCAPB),
+ PCLK(TZC, "tzc", "pclk5", CLK_IGNORE_UNUSED, G_TZC),
+ PCLK(TZPC, "tzpc", "pclk5", CLK_IGNORE_UNUSED, G_TZPC),
+ PCLK(IWDG1, "iwdg1", "pclk5", 0, G_IWDG1),
+ PCLK(BSEC, "bsec", "pclk5", CLK_IGNORE_UNUSED, G_BSEC),
+ PCLK(STGEN, "stgen", "pclk5", CLK_IGNORE_UNUSED, G_STGEN),
+ PCLK(DMA1, "dma1", "ck_mcu", 0, G_DMA1),
+ PCLK(DMA2, "dma2", "ck_mcu", 0, G_DMA2),
+ PCLK(DMAMUX, "dmamux", "ck_mcu", 0, G_DMAMUX),
+ PCLK(ADC12, "adc12", "ck_mcu", 0, G_ADC12),
+ PCLK(USBO, "usbo", "ck_mcu", 0, G_USBO),
+ PCLK(SDMMC3, "sdmmc3", "ck_mcu", 0, G_SDMMC3),
+ PCLK(DCMI, "dcmi", "ck_mcu", 0, G_DCMI),
+ PCLK(CRYP2, "cryp2", "ck_mcu", 0, G_CRYP2),
+ PCLK(HASH2, "hash2", "ck_mcu", 0, G_HASH2),
+ PCLK(RNG2, "rng2", "ck_mcu", 0, G_RNG2),
+ PCLK(CRC2, "crc2", "ck_mcu", 0, G_CRC2),
+ PCLK(HSEM, "hsem", "ck_mcu", 0, G_HSEM),
+ PCLK(IPCC, "ipcc", "ck_mcu", 0, G_IPCC),
+ PCLK(GPIOA, "gpioa", "ck_mcu", 0, G_GPIOA),
+ PCLK(GPIOB, "gpiob", "ck_mcu", 0, G_GPIOB),
+ PCLK(GPIOC, "gpioc", "ck_mcu", 0, G_GPIOC),
+ PCLK(GPIOD, "gpiod", "ck_mcu", 0, G_GPIOD),
+ PCLK(GPIOE, "gpioe", "ck_mcu", 0, G_GPIOE),
+ PCLK(GPIOF, "gpiof", "ck_mcu", 0, G_GPIOF),
+ PCLK(GPIOG, "gpiog", "ck_mcu", 0, G_GPIOG),
+ PCLK(GPIOH, "gpioh", "ck_mcu", 0, G_GPIOH),
+ PCLK(GPIOI, "gpioi", "ck_mcu", 0, G_GPIOI),
+ PCLK(GPIOJ, "gpioj", "ck_mcu", 0, G_GPIOJ),
+ PCLK(GPIOK, "gpiok", "ck_mcu", 0, G_GPIOK),
+ PCLK(GPIOZ, "gpioz", "ck_axi", CLK_IGNORE_UNUSED, G_GPIOZ),
+ PCLK(CRYP1, "cryp1", "ck_axi", CLK_IGNORE_UNUSED, G_CRYP1),
+ PCLK(HASH1, "hash1", "ck_axi", CLK_IGNORE_UNUSED, G_HASH1),
+ PCLK(RNG1, "rng1", "ck_axi", 0, G_RNG1),
+ PCLK(BKPSRAM, "bkpsram", "ck_axi", CLK_IGNORE_UNUSED, G_BKPSRAM),
+ PCLK(MDMA, "mdma", "ck_axi", 0, G_MDMA),
+ PCLK(GPU, "gpu", "ck_axi", 0, G_GPU),
+ PCLK(ETHTX, "ethtx", "ck_axi", 0, G_ETHTX),
+ PCLK(ETHRX, "ethrx", "ck_axi", 0, G_ETHRX),
+ PCLK(ETHMAC, "ethmac", "ck_axi", 0, G_ETHMAC),
+ PCLK(FMC, "fmc", "ck_axi", CLK_IGNORE_UNUSED, G_FMC),
+ PCLK(QSPI, "qspi", "ck_axi", CLK_IGNORE_UNUSED, G_QSPI),
+ PCLK(SDMMC1, "sdmmc1", "ck_axi", 0, G_SDMMC1),
+ PCLK(SDMMC2, "sdmmc2", "ck_axi", 0, G_SDMMC2),
+ PCLK(CRC1, "crc1", "ck_axi", 0, G_CRC1),
+ PCLK(USBH, "usbh", "ck_axi", 0, G_USBH),
+ PCLK(ETHSTP, "ethstp", "ck_axi", 0, G_ETHSTP),
+
+ /* Kernel clocks */
+ KCLK(SDMMC1_K, "sdmmc1_k", sdmmc12_src, 0, G_SDMMC1, M_SDMMC12),
+ KCLK(SDMMC2_K, "sdmmc2_k", sdmmc12_src, 0, G_SDMMC2, M_SDMMC12),
+ KCLK(SDMMC3_K, "sdmmc3_k", sdmmc3_src, 0, G_SDMMC3, M_SDMMC3),
+ KCLK(FMC_K, "fmc_k", fmc_src, 0, G_FMC, M_FMC),
+ KCLK(QSPI_K, "qspi_k", qspi_src, 0, G_QSPI, M_QSPI),
+ KCLK(RNG1_K, "rng1_k", rng_src, 0, G_RNG1, M_RNG1),
+ KCLK(RNG2_K, "rng2_k", rng_src, 0, G_RNG2, M_RNG2),
+ KCLK(USBPHY_K, "usbphy_k", usbphy_src, 0, G_USBPHY, M_USBPHY),
+ KCLK(STGEN_K, "stgen_k", stgen_src, CLK_IGNORE_UNUSED,
+ G_STGEN, M_STGEN),
+ KCLK(SPDIF_K, "spdif_k", spdif_src, 0, G_SPDIF, M_SPDIF),
+ KCLK(SPI1_K, "spi1_k", spi123_src, 0, G_SPI1, M_SPI1),
+ KCLK(SPI2_K, "spi2_k", spi123_src, 0, G_SPI2, M_SPI23),
+ KCLK(SPI3_K, "spi3_k", spi123_src, 0, G_SPI3, M_SPI23),
+ KCLK(SPI4_K, "spi4_k", spi45_src, 0, G_SPI4, M_SPI45),
+ KCLK(SPI5_K, "spi5_k", spi45_src, 0, G_SPI5, M_SPI45),
+ KCLK(SPI6_K, "spi6_k", spi6_src, 0, G_SPI6, M_SPI6),
+ KCLK(CEC_K, "cec_k", cec_src, 0, G_CEC, M_CEC),
+ KCLK(I2C1_K, "i2c1_k", i2c12_src, 0, G_I2C1, M_I2C12),
+ KCLK(I2C2_K, "i2c2_k", i2c12_src, 0, G_I2C2, M_I2C12),
+ KCLK(I2C3_K, "i2c3_k", i2c35_src, 0, G_I2C3, M_I2C35),
+ KCLK(I2C5_K, "i2c5_k", i2c35_src, 0, G_I2C5, M_I2C35),
+ KCLK(I2C4_K, "i2c4_k", i2c46_src, 0, G_I2C4, M_I2C46),
+ KCLK(I2C6_K, "i2c6_k", i2c46_src, 0, G_I2C6, M_I2C46),
+ KCLK(LPTIM1_K, "lptim1_k", lptim1_src, 0, G_LPTIM1, M_LPTIM1),
+ KCLK(LPTIM2_K, "lptim2_k", lptim23_src, 0, G_LPTIM2, M_LPTIM23),
+ KCLK(LPTIM3_K, "lptim3_k", lptim23_src, 0, G_LPTIM3, M_LPTIM23),
+ KCLK(LPTIM4_K, "lptim4_k", lptim45_src, 0, G_LPTIM4, M_LPTIM45),
+ KCLK(LPTIM5_K, "lptim5_k", lptim45_src, 0, G_LPTIM5, M_LPTIM45),
+ KCLK(USART1_K, "usart1_k", usart1_src, 0, G_USART1, M_USART1),
+ KCLK(USART2_K, "usart2_k", usart234578_src, 0, G_USART2, M_UART24),
+ KCLK(USART3_K, "usart3_k", usart234578_src, 0, G_USART3, M_UART35),
+ KCLK(UART4_K, "uart4_k", usart234578_src, 0, G_UART4, M_UART24),
+ KCLK(UART5_K, "uart5_k", usart234578_src, 0, G_UART5, M_UART35),
+ KCLK(USART6_K, "uart6_k", usart6_src, 0, G_USART6, M_USART6),
+ KCLK(UART7_K, "uart7_k", usart234578_src, 0, G_UART7, M_UART78),
+ KCLK(UART8_K, "uart8_k", usart234578_src, 0, G_UART8, M_UART78),
+ KCLK(FDCAN_K, "fdcan_k", fdcan_src, 0, G_FDCAN, M_FDCAN),
+ KCLK(SAI1_K, "sai1_k", sai_src, 0, G_SAI1, M_SAI1),
+ KCLK(SAI2_K, "sai2_k", sai2_src, 0, G_SAI2, M_SAI2),
+ KCLK(SAI3_K, "sai3_k", sai_src, 0, G_SAI2, M_SAI3),
+ KCLK(SAI4_K, "sai4_k", sai_src, 0, G_SAI2, M_SAI4),
+ KCLK(ADC12_K, "adc12_k", adc12_src, 0, G_ADC12, M_ADC12),
+ KCLK(DSI_K, "dsi_k", dsi_src, 0, G_DSI, M_DSI),
+ KCLK(ADFSDM_K, "adfsdm_k", sai_src, 0, G_ADFSDM, M_SAI1),
+ KCLK(USBO_K, "usbo_k", usbo_src, 0, G_USBO, M_USBO),
+ KCLK(ETHCK_K, "ethck_k", eth_src, 0, G_ETHCK, M_ETHCK),
+
+ /* Particulary Kernel Clocks (no mux or no gate) */
+ MGATE_MP1(DFSDM_K, "dfsdm_k", "ck_mcu", 0, G_DFSDM),
+ MGATE_MP1(DSI_PX, "dsi_px", "pll4_q", CLK_SET_RATE_PARENT, G_DSI),
+ MGATE_MP1(LTDC_PX, "ltdc_px", "pll4_q", CLK_SET_RATE_PARENT, G_LTDC),
+ MGATE_MP1(GPU_K, "gpu_k", "pll2_q", 0, G_GPU),
+ MGATE_MP1(DAC12_K, "dac12_k", "ck_lsi", 0, G_DAC12),
+
+ COMPOSITE(ETHPTP_K, "ethptp_k", eth_src, CLK_OPS_PARENT_ENABLE,
+ _NO_GATE,
+ _MMUX(M_ETHCK),
+ _DIV(RCC_ETHCKSELR, 4, 4, CLK_DIVIDER_ALLOW_ZERO, NULL)),
+
+ /* RTC clock */
+ DIV(NO_ID, "ck_hse_rtc", "ck_hse", 0, RCC_RTCDIVR, 0, 7,
+ CLK_DIVIDER_ALLOW_ZERO),
+
+ COMPOSITE(RTC, "ck_rtc", rtc_src, CLK_OPS_PARENT_ENABLE |
+ CLK_SET_RATE_PARENT,
+ _GATE(RCC_BDCR, 20, 0),
+ _MUX(RCC_BDCR, 16, 2, 0),
+ _NO_DIV),
+
+ /* MCO clocks */
+ COMPOSITE(CK_MCO1, "ck_mco1", mco1_src, CLK_OPS_PARENT_ENABLE |
+ CLK_SET_RATE_NO_REPARENT,
+ _GATE(RCC_MCO1CFGR, 12, 0),
+ _MUX(RCC_MCO1CFGR, 0, 3, 0),
+ _DIV(RCC_MCO1CFGR, 4, 4, 0, NULL)),
+
+ COMPOSITE(CK_MCO2, "ck_mco2", mco2_src, CLK_OPS_PARENT_ENABLE |
+ CLK_SET_RATE_NO_REPARENT,
+ _GATE(RCC_MCO2CFGR, 12, 0),
+ _MUX(RCC_MCO2CFGR, 0, 3, 0),
+ _DIV(RCC_MCO2CFGR, 4, 4, 0, NULL)),
+
+ /* Debug clocks */
+ FIXED_FACTOR(NO_ID, "ck_axi_div2", "ck_axi", 0, 1, 2),
+
+ GATE(DBG, "ck_apb_dbg", "ck_axi_div2", 0, RCC_DBGCFGR, 8, 0),
+
+ GATE(CK_DBG, "ck_sys_dbg", "ck_axi", 0, RCC_DBGCFGR, 8, 0),
+
+ COMPOSITE(CK_TRACE, "ck_trace", ck_trace_src, CLK_OPS_PARENT_ENABLE,
+ _GATE(RCC_DBGCFGR, 9, 0),
+ _NO_MUX,
+ _DIV(RCC_DBGCFGR, 0, 3, 0, ck_trace_div_table)),
+};
+
+struct stm32_clock_match_data {
+ const struct clock_config *cfg;
+ unsigned int num;
+ unsigned int maxbinding;
+};
+
+static struct stm32_clock_match_data stm32mp1_data = {
+ .cfg = stm32mp1_clock_cfg,
+ .num = ARRAY_SIZE(stm32mp1_clock_cfg),
+ .maxbinding = STM32MP1_LAST_CLK,
+};
+
+static const struct of_device_id stm32mp1_match_data[] = {
+ {
+ .compatible = "st,stm32mp1-rcc",
+ .data = &stm32mp1_data,
+ },
+ { }
+};
+
+static int stm32_register_hw_clk(struct device *dev,
+ struct clk_hw_onecell_data *clk_data,
+ void __iomem *base, spinlock_t *lock,
+ const struct clock_config *cfg)
+{
+ static struct clk_hw **hws;
+ struct clk_hw *hw = ERR_PTR(-ENOENT);
+
+ hws = clk_data->hws;
+
+ if (cfg->func)
+ hw = (*cfg->func)(dev, clk_data, base, lock, cfg);
+
+ if (IS_ERR(hw)) {
+ pr_err("Unable to register %s\n", cfg->name);
+ return PTR_ERR(hw);
+ }
+
+ if (cfg->id != NO_ID)
+ hws[cfg->id] = hw;
+
+ return 0;
+}
+
+static int stm32_rcc_init(struct device_node *np,
+ void __iomem *base,
+ const struct of_device_id *match_data)
+{
+ struct clk_hw_onecell_data *clk_data;
+ struct clk_hw **hws;
+ const struct of_device_id *match;
+ const struct stm32_clock_match_data *data;
+ int err, n, max_binding;
+
+ match = of_match_node(match_data, np);
+ if (!match) {
+ pr_err("%s: match data not found\n", __func__);
+ return -ENODEV;
+ }
+
+ data = match->data;
+
+ max_binding = data->maxbinding;
+
+ clk_data = kzalloc(sizeof(*clk_data) +
+ sizeof(*clk_data->hws) * max_binding,
+ GFP_KERNEL);
+ if (!clk_data)
+ return -ENOMEM;
+
+ clk_data->num = max_binding;
+
+ hws = clk_data->hws;
+
+ for (n = 0; n < max_binding; n++)
+ hws[n] = ERR_PTR(-ENOENT);
+
+ for (n = 0; n < data->num; n++) {
+ err = stm32_register_hw_clk(NULL, clk_data, base, &rlock,
+ &data->cfg[n]);
+ if (err) {
+ pr_err("%s: can't register %s\n", __func__,
+ data->cfg[n].name);
+
+ kfree(clk_data);
+
+ return err;
+ }
+ }
+
+ return of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
+}
+
+static void stm32mp1_rcc_init(struct device_node *np)
+{
+ void __iomem *base;
+
+ base = of_iomap(np, 0);
+ if (!base) {
+ pr_err("%s: unable to map resource", np->name);
+ of_node_put(np);
+ return;
+ }
+
+ if (stm32_rcc_init(np, base, stm32mp1_match_data)) {
+ iounmap(base);
+ of_node_put(np);
+ }
+}
+
+CLK_OF_DECLARE_DRIVER(stm32mp1_rcc, "st,stm32mp1-rcc", stm32mp1_rcc_init);
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 076d4244d672..ea67ac81c6f9 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -2375,6 +2375,9 @@ static int clk_core_get_phase(struct clk_core *core)
int ret;
clk_prepare_lock();
+ /* Always try to update cached phase if possible */
+ if (core->ops->get_phase)
+ core->phase = core->ops->get_phase(core->hw);
ret = core->phase;
clk_prepare_unlock();
@@ -2491,19 +2494,7 @@ static int clk_summary_show(struct seq_file *s, void *data)
return 0;
}
-
-
-static int clk_summary_open(struct inode *inode, struct file *file)
-{
- return single_open(file, clk_summary_show, inode->i_private);
-}
-
-static const struct file_operations clk_summary_fops = {
- .open = clk_summary_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(clk_summary);
static void clk_dump_one(struct seq_file *s, struct clk_core *c, int level)
{
@@ -2537,7 +2528,7 @@ static void clk_dump_subtree(struct seq_file *s, struct clk_core *c, int level)
seq_putc(s, '}');
}
-static int clk_dump(struct seq_file *s, void *data)
+static int clk_dump_show(struct seq_file *s, void *data)
{
struct clk_core *c;
bool first_node = true;
@@ -2560,19 +2551,7 @@ static int clk_dump(struct seq_file *s, void *data)
seq_puts(s, "}\n");
return 0;
}
-
-
-static int clk_dump_open(struct inode *inode, struct file *file)
-{
- return single_open(file, clk_dump, inode->i_private);
-}
-
-static const struct file_operations clk_dump_fops = {
- .open = clk_dump_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(clk_dump);
static const struct {
unsigned long flag;
@@ -2594,7 +2573,7 @@ static const struct {
#undef ENTRY
};
-static int clk_flags_dump(struct seq_file *s, void *data)
+static int clk_flags_show(struct seq_file *s, void *data)
{
struct clk_core *core = s->private;
unsigned long flags = core->flags;
@@ -2613,20 +2592,9 @@ static int clk_flags_dump(struct seq_file *s, void *data)
return 0;
}
+DEFINE_SHOW_ATTRIBUTE(clk_flags);
-static int clk_flags_open(struct inode *inode, struct file *file)
-{
- return single_open(file, clk_flags_dump, inode->i_private);
-}
-
-static const struct file_operations clk_flags_fops = {
- .open = clk_flags_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static int possible_parents_dump(struct seq_file *s, void *data)
+static int possible_parents_show(struct seq_file *s, void *data)
{
struct clk_core *core = s->private;
int i;
@@ -2638,18 +2606,7 @@ static int possible_parents_dump(struct seq_file *s, void *data)
return 0;
}
-
-static int possible_parents_open(struct inode *inode, struct file *file)
-{
- return single_open(file, possible_parents_dump, inode->i_private);
-}
-
-static const struct file_operations possible_parents_fops = {
- .open = possible_parents_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(possible_parents);
static int clk_debug_create_one(struct clk_core *core, struct dentry *pdentry)
{
@@ -2933,6 +2890,17 @@ static int __clk_core_init(struct clk_core *core)
}
/*
+ * optional platform-specific magic
+ *
+ * The .init callback is not used by any of the basic clock types, but
+ * exists for weird hardware that must perform initialization magic.
+ * Please consider other ways of solving initialization problems before
+ * using this callback, as its use is discouraged.
+ */
+ if (core->ops->init)
+ core->ops->init(core->hw);
+
+ /*
* Set clk's accuracy. The preferred method is to use
* .recalc_accuracy. For simple clocks and lazy developers the default
* fallback is to use the parent's accuracy. If a clock doesn't have a
@@ -3009,17 +2977,6 @@ static int __clk_core_init(struct clk_core *core)
}
}
- /*
- * optional platform-specific magic
- *
- * The .init callback is not used by any of the basic clock types, but
- * exists for weird hardware that must perform initialization magic.
- * Please consider other ways of solving initialization problems before
- * using this callback, as its use is discouraged.
- */
- if (core->ops->init)
- core->ops->init(core->hw);
-
kref_init(&core->ref);
out:
clk_pm_runtime_put(core);
diff --git a/drivers/clk/davinci/Makefile b/drivers/clk/davinci/Makefile
new file mode 100644
index 000000000000..11178b79b483
--- /dev/null
+++ b/drivers/clk/davinci/Makefile
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0
+
+ifeq ($(CONFIG_COMMON_CLK), y)
+obj-$(CONFIG_ARCH_DAVINCI_DA8XX) += da8xx-cfgchip.o
+
+obj-y += pll.o
+obj-$(CONFIG_ARCH_DAVINCI_DA830) += pll-da830.o
+obj-$(CONFIG_ARCH_DAVINCI_DA850) += pll-da850.o
+obj-$(CONFIG_ARCH_DAVINCI_DM355) += pll-dm355.o
+obj-$(CONFIG_ARCH_DAVINCI_DM365) += pll-dm365.o
+obj-$(CONFIG_ARCH_DAVINCI_DM644x) += pll-dm644x.o
+obj-$(CONFIG_ARCH_DAVINCI_DM646x) += pll-dm646x.o
+
+obj-y += psc.o
+obj-$(CONFIG_ARCH_DAVINCI_DA830) += psc-da830.o
+obj-$(CONFIG_ARCH_DAVINCI_DA850) += psc-da850.o
+obj-$(CONFIG_ARCH_DAVINCI_DM355) += psc-dm355.o
+obj-$(CONFIG_ARCH_DAVINCI_DM365) += psc-dm365.o
+obj-$(CONFIG_ARCH_DAVINCI_DM644x) += psc-dm644x.o
+obj-$(CONFIG_ARCH_DAVINCI_DM646x) += psc-dm646x.o
+endif
diff --git a/drivers/clk/davinci/da8xx-cfgchip.c b/drivers/clk/davinci/da8xx-cfgchip.c
new file mode 100644
index 000000000000..c971111d2601
--- /dev/null
+++ b/drivers/clk/davinci/da8xx-cfgchip.c
@@ -0,0 +1,790 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Clock driver for DA8xx/AM17xx/AM18xx/OMAP-L13x CFGCHIP
+ *
+ * Copyright (C) 2018 David Lechner <david@lechnology.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/init.h>
+#include <linux/mfd/da8xx-cfgchip.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/platform_data/clk-da8xx-cfgchip.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+/* --- Gate clocks --- */
+
+#define DA8XX_GATE_CLOCK_IS_DIV4P5 BIT(1)
+
+struct da8xx_cfgchip_gate_clk_info {
+ const char *name;
+ u32 cfgchip;
+ u32 bit;
+ u32 flags;
+};
+
+struct da8xx_cfgchip_gate_clk {
+ struct clk_hw hw;
+ struct regmap *regmap;
+ u32 reg;
+ u32 mask;
+};
+
+#define to_da8xx_cfgchip_gate_clk(_hw) \
+ container_of((_hw), struct da8xx_cfgchip_gate_clk, hw)
+
+static int da8xx_cfgchip_gate_clk_enable(struct clk_hw *hw)
+{
+ struct da8xx_cfgchip_gate_clk *clk = to_da8xx_cfgchip_gate_clk(hw);
+
+ return regmap_write_bits(clk->regmap, clk->reg, clk->mask, clk->mask);
+}
+
+static void da8xx_cfgchip_gate_clk_disable(struct clk_hw *hw)
+{
+ struct da8xx_cfgchip_gate_clk *clk = to_da8xx_cfgchip_gate_clk(hw);
+
+ regmap_write_bits(clk->regmap, clk->reg, clk->mask, 0);
+}
+
+static int da8xx_cfgchip_gate_clk_is_enabled(struct clk_hw *hw)
+{
+ struct da8xx_cfgchip_gate_clk *clk = to_da8xx_cfgchip_gate_clk(hw);
+ unsigned int val;
+
+ regmap_read(clk->regmap, clk->reg, &val);
+
+ return !!(val & clk->mask);
+}
+
+static unsigned long da8xx_cfgchip_div4p5_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ /* this clock divides by 4.5 */
+ return parent_rate * 2 / 9;
+}
+
+static const struct clk_ops da8xx_cfgchip_gate_clk_ops = {
+ .enable = da8xx_cfgchip_gate_clk_enable,
+ .disable = da8xx_cfgchip_gate_clk_disable,
+ .is_enabled = da8xx_cfgchip_gate_clk_is_enabled,
+};
+
+static const struct clk_ops da8xx_cfgchip_div4p5_clk_ops = {
+ .enable = da8xx_cfgchip_gate_clk_enable,
+ .disable = da8xx_cfgchip_gate_clk_disable,
+ .is_enabled = da8xx_cfgchip_gate_clk_is_enabled,
+ .recalc_rate = da8xx_cfgchip_div4p5_recalc_rate,
+};
+
+static struct da8xx_cfgchip_gate_clk * __init
+da8xx_cfgchip_gate_clk_register(struct device *dev,
+ const struct da8xx_cfgchip_gate_clk_info *info,
+ struct regmap *regmap)
+{
+ struct clk *parent;
+ const char *parent_name;
+ struct da8xx_cfgchip_gate_clk *gate;
+ struct clk_init_data init;
+ int ret;
+
+ parent = devm_clk_get(dev, NULL);
+ if (IS_ERR(parent))
+ return ERR_CAST(parent);
+
+ parent_name = __clk_get_name(parent);
+
+ gate = devm_kzalloc(dev, sizeof(*gate), GFP_KERNEL);
+ if (!gate)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = info->name;
+ if (info->flags & DA8XX_GATE_CLOCK_IS_DIV4P5)
+ init.ops = &da8xx_cfgchip_div4p5_clk_ops;
+ else
+ init.ops = &da8xx_cfgchip_gate_clk_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = 0;
+
+ gate->hw.init = &init;
+ gate->regmap = regmap;
+ gate->reg = info->cfgchip;
+ gate->mask = info->bit;
+
+ ret = devm_clk_hw_register(dev, &gate->hw);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ return gate;
+}
+
+static const struct da8xx_cfgchip_gate_clk_info da8xx_tbclksync_info __initconst = {
+ .name = "ehrpwm_tbclk",
+ .cfgchip = CFGCHIP(1),
+ .bit = CFGCHIP1_TBCLKSYNC,
+};
+
+static int __init da8xx_cfgchip_register_tbclk(struct device *dev,
+ struct regmap *regmap)
+{
+ struct da8xx_cfgchip_gate_clk *gate;
+
+ gate = da8xx_cfgchip_gate_clk_register(dev, &da8xx_tbclksync_info,
+ regmap);
+ if (IS_ERR(gate))
+ return PTR_ERR(gate);
+
+ clk_hw_register_clkdev(&gate->hw, "tbclk", "ehrpwm.0");
+ clk_hw_register_clkdev(&gate->hw, "tbclk", "ehrpwm.1");
+
+ return 0;
+}
+
+static const struct da8xx_cfgchip_gate_clk_info da8xx_div4p5ena_info __initconst = {
+ .name = "div4.5",
+ .cfgchip = CFGCHIP(3),
+ .bit = CFGCHIP3_DIV45PENA,
+ .flags = DA8XX_GATE_CLOCK_IS_DIV4P5,
+};
+
+static int __init da8xx_cfgchip_register_div4p5(struct device *dev,
+ struct regmap *regmap)
+{
+ struct da8xx_cfgchip_gate_clk *gate;
+
+ gate = da8xx_cfgchip_gate_clk_register(dev, &da8xx_div4p5ena_info, regmap);
+ if (IS_ERR(gate))
+ return PTR_ERR(gate);
+
+ return 0;
+}
+
+static int __init
+of_da8xx_cfgchip_gate_clk_init(struct device *dev,
+ const struct da8xx_cfgchip_gate_clk_info *info,
+ struct regmap *regmap)
+{
+ struct da8xx_cfgchip_gate_clk *gate;
+
+ gate = da8xx_cfgchip_gate_clk_register(dev, info, regmap);
+ if (IS_ERR(gate))
+ return PTR_ERR(gate);
+
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, gate);
+}
+
+static int __init of_da8xx_tbclksync_init(struct device *dev,
+ struct regmap *regmap)
+{
+ return of_da8xx_cfgchip_gate_clk_init(dev, &da8xx_tbclksync_info, regmap);
+}
+
+static int __init of_da8xx_div4p5ena_init(struct device *dev,
+ struct regmap *regmap)
+{
+ return of_da8xx_cfgchip_gate_clk_init(dev, &da8xx_div4p5ena_info, regmap);
+}
+
+/* --- MUX clocks --- */
+
+struct da8xx_cfgchip_mux_clk_info {
+ const char *name;
+ const char *parent0;
+ const char *parent1;
+ u32 cfgchip;
+ u32 bit;
+};
+
+struct da8xx_cfgchip_mux_clk {
+ struct clk_hw hw;
+ struct regmap *regmap;
+ u32 reg;
+ u32 mask;
+};
+
+#define to_da8xx_cfgchip_mux_clk(_hw) \
+ container_of((_hw), struct da8xx_cfgchip_mux_clk, hw)
+
+static int da8xx_cfgchip_mux_clk_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct da8xx_cfgchip_mux_clk *clk = to_da8xx_cfgchip_mux_clk(hw);
+ unsigned int val = index ? clk->mask : 0;
+
+ return regmap_write_bits(clk->regmap, clk->reg, clk->mask, val);
+}
+
+static u8 da8xx_cfgchip_mux_clk_get_parent(struct clk_hw *hw)
+{
+ struct da8xx_cfgchip_mux_clk *clk = to_da8xx_cfgchip_mux_clk(hw);
+ unsigned int val;
+
+ regmap_read(clk->regmap, clk->reg, &val);
+
+ return (val & clk->mask) ? 1 : 0;
+}
+
+static const struct clk_ops da8xx_cfgchip_mux_clk_ops = {
+ .set_parent = da8xx_cfgchip_mux_clk_set_parent,
+ .get_parent = da8xx_cfgchip_mux_clk_get_parent,
+};
+
+static struct da8xx_cfgchip_mux_clk * __init
+da8xx_cfgchip_mux_clk_register(struct device *dev,
+ const struct da8xx_cfgchip_mux_clk_info *info,
+ struct regmap *regmap)
+{
+ const char * const parent_names[] = { info->parent0, info->parent1 };
+ struct da8xx_cfgchip_mux_clk *mux;
+ struct clk_init_data init;
+ int ret;
+
+ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
+ if (!mux)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = info->name;
+ init.ops = &da8xx_cfgchip_mux_clk_ops;
+ init.parent_names = parent_names;
+ init.num_parents = 2;
+ init.flags = 0;
+
+ mux->hw.init = &init;
+ mux->regmap = regmap;
+ mux->reg = info->cfgchip;
+ mux->mask = info->bit;
+
+ ret = devm_clk_hw_register(dev, &mux->hw);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ return mux;
+}
+
+static const struct da8xx_cfgchip_mux_clk_info da850_async1_info __initconst = {
+ .name = "async1",
+ .parent0 = "pll0_sysclk3",
+ .parent1 = "div4.5",
+ .cfgchip = CFGCHIP(3),
+ .bit = CFGCHIP3_EMA_CLKSRC,
+};
+
+static int __init da8xx_cfgchip_register_async1(struct device *dev,
+ struct regmap *regmap)
+{
+ struct da8xx_cfgchip_mux_clk *mux;
+
+ mux = da8xx_cfgchip_mux_clk_register(dev, &da850_async1_info, regmap);
+ if (IS_ERR(mux))
+ return PTR_ERR(mux);
+
+ clk_hw_register_clkdev(&mux->hw, "async1", "da850-psc0");
+
+ return 0;
+}
+
+static const struct da8xx_cfgchip_mux_clk_info da850_async3_info __initconst = {
+ .name = "async3",
+ .parent0 = "pll0_sysclk2",
+ .parent1 = "pll1_sysclk2",
+ .cfgchip = CFGCHIP(3),
+ .bit = CFGCHIP3_ASYNC3_CLKSRC,
+};
+
+static int __init da850_cfgchip_register_async3(struct device *dev,
+ struct regmap *regmap)
+{
+ struct da8xx_cfgchip_mux_clk *mux;
+ struct clk_hw *parent;
+
+ mux = da8xx_cfgchip_mux_clk_register(dev, &da850_async3_info, regmap);
+ if (IS_ERR(mux))
+ return PTR_ERR(mux);
+
+ clk_hw_register_clkdev(&mux->hw, "async3", "da850-psc1");
+
+ /* pll1_sysclk2 is not affected by CPU scaling, so use it for async3 */
+ parent = clk_hw_get_parent_by_index(&mux->hw, 1);
+ if (parent)
+ clk_set_parent(mux->hw.clk, parent->clk);
+ else
+ dev_warn(dev, "Failed to find async3 parent clock\n");
+
+ return 0;
+}
+
+static int __init
+of_da8xx_cfgchip_init_mux_clock(struct device *dev,
+ const struct da8xx_cfgchip_mux_clk_info *info,
+ struct regmap *regmap)
+{
+ struct da8xx_cfgchip_mux_clk *mux;
+
+ mux = da8xx_cfgchip_mux_clk_register(dev, info, regmap);
+ if (IS_ERR(mux))
+ return PTR_ERR(mux);
+
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &mux->hw);
+}
+
+static int __init of_da850_async1_init(struct device *dev, struct regmap *regmap)
+{
+ return of_da8xx_cfgchip_init_mux_clock(dev, &da850_async1_info, regmap);
+}
+
+static int __init of_da850_async3_init(struct device *dev, struct regmap *regmap)
+{
+ return of_da8xx_cfgchip_init_mux_clock(dev, &da850_async3_info, regmap);
+}
+
+/* --- USB 2.0 PHY clock --- */
+
+struct da8xx_usb0_clk48 {
+ struct clk_hw hw;
+ struct clk *fck;
+ struct regmap *regmap;
+};
+
+#define to_da8xx_usb0_clk48(_hw) \
+ container_of((_hw), struct da8xx_usb0_clk48, hw)
+
+static int da8xx_usb0_clk48_prepare(struct clk_hw *hw)
+{
+ struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw);
+
+ /* The USB 2.0 PSC clock is only needed temporarily during the USB 2.0
+ * PHY clock enable, but since clk_prepare() can't be called in an
+ * atomic context (i.e. in clk_enable()), we have to prepare it here.
+ */
+ return clk_prepare(usb0->fck);
+}
+
+static void da8xx_usb0_clk48_unprepare(struct clk_hw *hw)
+{
+ struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw);
+
+ clk_unprepare(usb0->fck);
+}
+
+static int da8xx_usb0_clk48_enable(struct clk_hw *hw)
+{
+ struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw);
+ unsigned int mask, val;
+ int ret;
+
+ /* Locking the USB 2.O PLL requires that the USB 2.O PSC is enabled
+ * temporaily. It can be turned back off once the PLL is locked.
+ */
+ clk_enable(usb0->fck);
+
+ /* Turn on the USB 2.0 PHY, but just the PLL, and not OTG. The USB 1.1
+ * PHY may use the USB 2.0 PLL clock without USB 2.0 OTG being used.
+ */
+ mask = CFGCHIP2_RESET | CFGCHIP2_PHYPWRDN | CFGCHIP2_PHY_PLLON;
+ val = CFGCHIP2_PHY_PLLON;
+
+ regmap_write_bits(usb0->regmap, CFGCHIP(2), mask, val);
+ ret = regmap_read_poll_timeout(usb0->regmap, CFGCHIP(2), val,
+ val & CFGCHIP2_PHYCLKGD, 0, 500000);
+
+ clk_disable(usb0->fck);
+
+ return ret;
+}
+
+static void da8xx_usb0_clk48_disable(struct clk_hw *hw)
+{
+ struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw);
+ unsigned int val;
+
+ val = CFGCHIP2_PHYPWRDN;
+ regmap_write_bits(usb0->regmap, CFGCHIP(2), val, val);
+}
+
+static int da8xx_usb0_clk48_is_enabled(struct clk_hw *hw)
+{
+ struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw);
+ unsigned int val;
+
+ regmap_read(usb0->regmap, CFGCHIP(2), &val);
+
+ return !!(val & CFGCHIP2_PHYCLKGD);
+}
+
+static unsigned long da8xx_usb0_clk48_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw);
+ unsigned int mask, val;
+
+ /* The parent clock rate must be one of the following */
+ mask = CFGCHIP2_REFFREQ_MASK;
+ switch (parent_rate) {
+ case 12000000:
+ val = CFGCHIP2_REFFREQ_12MHZ;
+ break;
+ case 13000000:
+ val = CFGCHIP2_REFFREQ_13MHZ;
+ break;
+ case 19200000:
+ val = CFGCHIP2_REFFREQ_19_2MHZ;
+ break;
+ case 20000000:
+ val = CFGCHIP2_REFFREQ_20MHZ;
+ break;
+ case 24000000:
+ val = CFGCHIP2_REFFREQ_24MHZ;
+ break;
+ case 26000000:
+ val = CFGCHIP2_REFFREQ_26MHZ;
+ break;
+ case 38400000:
+ val = CFGCHIP2_REFFREQ_38_4MHZ;
+ break;
+ case 40000000:
+ val = CFGCHIP2_REFFREQ_40MHZ;
+ break;
+ case 48000000:
+ val = CFGCHIP2_REFFREQ_48MHZ;
+ break;
+ default:
+ return 0;
+ }
+
+ regmap_write_bits(usb0->regmap, CFGCHIP(2), mask, val);
+
+ /* USB 2.0 PLL always supplies 48MHz */
+ return 48000000;
+}
+
+static long da8xx_usb0_clk48_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ return 48000000;
+}
+
+static int da8xx_usb0_clk48_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw);
+
+ return regmap_write_bits(usb0->regmap, CFGCHIP(2),
+ CFGCHIP2_USB2PHYCLKMUX,
+ index ? CFGCHIP2_USB2PHYCLKMUX : 0);
+}
+
+static u8 da8xx_usb0_clk48_get_parent(struct clk_hw *hw)
+{
+ struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw);
+ unsigned int val;
+
+ regmap_read(usb0->regmap, CFGCHIP(2), &val);
+
+ return (val & CFGCHIP2_USB2PHYCLKMUX) ? 1 : 0;
+}
+
+static const struct clk_ops da8xx_usb0_clk48_ops = {
+ .prepare = da8xx_usb0_clk48_prepare,
+ .unprepare = da8xx_usb0_clk48_unprepare,
+ .enable = da8xx_usb0_clk48_enable,
+ .disable = da8xx_usb0_clk48_disable,
+ .is_enabled = da8xx_usb0_clk48_is_enabled,
+ .recalc_rate = da8xx_usb0_clk48_recalc_rate,
+ .round_rate = da8xx_usb0_clk48_round_rate,
+ .set_parent = da8xx_usb0_clk48_set_parent,
+ .get_parent = da8xx_usb0_clk48_get_parent,
+};
+
+static struct da8xx_usb0_clk48 *
+da8xx_cfgchip_register_usb0_clk48(struct device *dev,
+ struct regmap *regmap)
+{
+ const char * const parent_names[] = { "usb_refclkin", "pll0_auxclk" };
+ struct clk *fck_clk;
+ struct da8xx_usb0_clk48 *usb0;
+ struct clk_init_data init;
+ int ret;
+
+ fck_clk = devm_clk_get(dev, "fck");
+ if (IS_ERR(fck_clk)) {
+ if (PTR_ERR(fck_clk) != -EPROBE_DEFER)
+ dev_err(dev, "Missing fck clock\n");
+ return ERR_CAST(fck_clk);
+ }
+
+ usb0 = devm_kzalloc(dev, sizeof(*usb0), GFP_KERNEL);
+ if (!usb0)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = "usb0_clk48";
+ init.ops = &da8xx_usb0_clk48_ops;
+ init.parent_names = parent_names;
+ init.num_parents = 2;
+
+ usb0->hw.init = &init;
+ usb0->fck = fck_clk;
+ usb0->regmap = regmap;
+
+ ret = devm_clk_hw_register(dev, &usb0->hw);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ return usb0;
+}
+
+/* --- USB 1.1 PHY clock --- */
+
+struct da8xx_usb1_clk48 {
+ struct clk_hw hw;
+ struct regmap *regmap;
+};
+
+#define to_da8xx_usb1_clk48(_hw) \
+ container_of((_hw), struct da8xx_usb1_clk48, hw)
+
+static int da8xx_usb1_clk48_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct da8xx_usb1_clk48 *usb1 = to_da8xx_usb1_clk48(hw);
+
+ return regmap_write_bits(usb1->regmap, CFGCHIP(2),
+ CFGCHIP2_USB1PHYCLKMUX,
+ index ? CFGCHIP2_USB1PHYCLKMUX : 0);
+}
+
+static u8 da8xx_usb1_clk48_get_parent(struct clk_hw *hw)
+{
+ struct da8xx_usb1_clk48 *usb1 = to_da8xx_usb1_clk48(hw);
+ unsigned int val;
+
+ regmap_read(usb1->regmap, CFGCHIP(2), &val);
+
+ return (val & CFGCHIP2_USB1PHYCLKMUX) ? 1 : 0;
+}
+
+static const struct clk_ops da8xx_usb1_clk48_ops = {
+ .set_parent = da8xx_usb1_clk48_set_parent,
+ .get_parent = da8xx_usb1_clk48_get_parent,
+};
+
+/**
+ * da8xx_cfgchip_register_usb1_clk48 - Register a new USB 1.1 PHY clock
+ * @regmap: The CFGCHIP regmap
+ */
+static struct da8xx_usb1_clk48 *
+da8xx_cfgchip_register_usb1_clk48(struct device *dev,
+ struct regmap *regmap)
+{
+ const char * const parent_names[] = { "usb0_clk48", "usb_refclkin" };
+ struct da8xx_usb1_clk48 *usb1;
+ struct clk_init_data init;
+ int ret;
+
+ usb1 = devm_kzalloc(dev, sizeof(*usb1), GFP_KERNEL);
+ if (!usb1)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = "usb1_clk48";
+ init.ops = &da8xx_usb1_clk48_ops;
+ init.parent_names = parent_names;
+ init.num_parents = 2;
+
+ usb1->hw.init = &init;
+ usb1->regmap = regmap;
+
+ ret = devm_clk_hw_register(dev, &usb1->hw);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ return usb1;
+}
+
+static int da8xx_cfgchip_register_usb_phy_clk(struct device *dev,
+ struct regmap *regmap)
+{
+ struct da8xx_usb0_clk48 *usb0;
+ struct da8xx_usb1_clk48 *usb1;
+ struct clk_hw *parent;
+
+ usb0 = da8xx_cfgchip_register_usb0_clk48(dev, regmap);
+ if (IS_ERR(usb0))
+ return PTR_ERR(usb0);
+
+ /*
+ * All existing boards use pll0_auxclk as the parent and new boards
+ * should use device tree, so hard-coding the value (1) here.
+ */
+ parent = clk_hw_get_parent_by_index(&usb0->hw, 1);
+ if (parent)
+ clk_set_parent(usb0->hw.clk, parent->clk);
+ else
+ dev_warn(dev, "Failed to find usb0 parent clock\n");
+
+ usb1 = da8xx_cfgchip_register_usb1_clk48(dev, regmap);
+ if (IS_ERR(usb1))
+ return PTR_ERR(usb1);
+
+ /*
+ * All existing boards use usb0_clk48 as the parent and new boards
+ * should use device tree, so hard-coding the value (0) here.
+ */
+ parent = clk_hw_get_parent_by_index(&usb1->hw, 0);
+ if (parent)
+ clk_set_parent(usb1->hw.clk, parent->clk);
+ else
+ dev_warn(dev, "Failed to find usb1 parent clock\n");
+
+ clk_hw_register_clkdev(&usb0->hw, "usb0_clk48", "da8xx-usb-phy");
+ clk_hw_register_clkdev(&usb1->hw, "usb1_clk48", "da8xx-usb-phy");
+
+ return 0;
+}
+
+static int of_da8xx_usb_phy_clk_init(struct device *dev, struct regmap *regmap)
+{
+ struct clk_hw_onecell_data *clk_data;
+ struct da8xx_usb0_clk48 *usb0;
+ struct da8xx_usb1_clk48 *usb1;
+
+ clk_data = devm_kzalloc(dev, sizeof(*clk_data) + 2 *
+ sizeof(*clk_data->hws), GFP_KERNEL);
+ if (!clk_data)
+ return -ENOMEM;
+
+ clk_data->num = 2;
+
+ usb0 = da8xx_cfgchip_register_usb0_clk48(dev, regmap);
+ if (IS_ERR(usb0)) {
+ if (PTR_ERR(usb0) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ dev_warn(dev, "Failed to register usb0_clk48 (%ld)\n",
+ PTR_ERR(usb0));
+
+ clk_data->hws[0] = ERR_PTR(-ENOENT);
+ } else {
+ clk_data->hws[0] = &usb0->hw;
+ }
+
+ usb1 = da8xx_cfgchip_register_usb1_clk48(dev, regmap);
+ if (IS_ERR(usb1)) {
+ if (PTR_ERR(usb0) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ dev_warn(dev, "Failed to register usb1_clk48 (%ld)\n",
+ PTR_ERR(usb1));
+
+ clk_data->hws[1] = ERR_PTR(-ENOENT);
+ } else {
+ clk_data->hws[1] = &usb1->hw;
+ }
+
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data);
+}
+
+/* --- platform device --- */
+
+static const struct of_device_id da8xx_cfgchip_of_match[] = {
+ {
+ .compatible = "ti,da830-tbclksync",
+ .data = of_da8xx_tbclksync_init,
+ },
+ {
+ .compatible = "ti,da830-div4p5ena",
+ .data = of_da8xx_div4p5ena_init,
+ },
+ {
+ .compatible = "ti,da850-async1-clksrc",
+ .data = of_da850_async1_init,
+ },
+ {
+ .compatible = "ti,da850-async3-clksrc",
+ .data = of_da850_async3_init,
+ },
+ {
+ .compatible = "ti,da830-usb-phy-clocks",
+ .data = of_da8xx_usb_phy_clk_init,
+ },
+ { }
+};
+
+static const struct platform_device_id da8xx_cfgchip_id_table[] = {
+ {
+ .name = "da830-tbclksync",
+ .driver_data = (kernel_ulong_t)da8xx_cfgchip_register_tbclk,
+ },
+ {
+ .name = "da830-div4p5ena",
+ .driver_data = (kernel_ulong_t)da8xx_cfgchip_register_div4p5,
+ },
+ {
+ .name = "da850-async1-clksrc",
+ .driver_data = (kernel_ulong_t)da8xx_cfgchip_register_async1,
+ },
+ {
+ .name = "da850-async3-clksrc",
+ .driver_data = (kernel_ulong_t)da850_cfgchip_register_async3,
+ },
+ {
+ .name = "da830-usb-phy-clks",
+ .driver_data = (kernel_ulong_t)da8xx_cfgchip_register_usb_phy_clk,
+ },
+ { }
+};
+
+typedef int (*da8xx_cfgchip_init)(struct device *dev, struct regmap *regmap);
+
+static int da8xx_cfgchip_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct da8xx_cfgchip_clk_platform_data *pdata = dev->platform_data;
+ const struct of_device_id *of_id;
+ da8xx_cfgchip_init clk_init = NULL;
+ struct regmap *regmap = NULL;
+
+ of_id = of_match_device(da8xx_cfgchip_of_match, dev);
+ if (of_id) {
+ struct device_node *parent;
+
+ clk_init = of_id->data;
+ parent = of_get_parent(dev->of_node);
+ regmap = syscon_node_to_regmap(parent);
+ of_node_put(parent);
+ } else if (pdev->id_entry && pdata) {
+ clk_init = (void *)pdev->id_entry->driver_data;
+ regmap = pdata->cfgchip;
+ }
+
+ if (!clk_init) {
+ dev_err(dev, "unable to find driver data\n");
+ return -EINVAL;
+ }
+
+ if (IS_ERR_OR_NULL(regmap)) {
+ dev_err(dev, "no regmap for CFGCHIP syscon\n");
+ return regmap ? PTR_ERR(regmap) : -ENOENT;
+ }
+
+ return clk_init(dev, regmap);
+}
+
+static struct platform_driver da8xx_cfgchip_driver = {
+ .probe = da8xx_cfgchip_probe,
+ .driver = {
+ .name = "da8xx-cfgchip-clk",
+ .of_match_table = da8xx_cfgchip_of_match,
+ },
+ .id_table = da8xx_cfgchip_id_table,
+};
+
+static int __init da8xx_cfgchip_driver_init(void)
+{
+ return platform_driver_register(&da8xx_cfgchip_driver);
+}
+
+/* has to be postcore_initcall because PSC devices depend on the async3 clock */
+postcore_initcall(da8xx_cfgchip_driver_init);
diff --git a/drivers/clk/davinci/pll-da830.c b/drivers/clk/davinci/pll-da830.c
new file mode 100644
index 000000000000..929a3d3a9adb
--- /dev/null
+++ b/drivers/clk/davinci/pll-da830.c
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PLL clock descriptions for TI DA830/OMAP-L137/AM17XX
+ *
+ * Copyright (C) 2018 David Lechner <david@lechnology.com>
+ */
+
+#include <linux/clkdev.h>
+#include <linux/bitops.h>
+#include <linux/init.h>
+#include <linux/types.h>
+
+#include "pll.h"
+
+static const struct davinci_pll_clk_info da830_pll_info = {
+ .name = "pll0",
+ .pllm_mask = GENMASK(4, 0),
+ .pllm_min = 4,
+ .pllm_max = 32,
+ .pllout_min_rate = 300000000,
+ .pllout_max_rate = 600000000,
+ .flags = PLL_HAS_CLKMODE | PLL_HAS_PREDIV | PLL_HAS_POSTDIV,
+};
+
+/*
+ * NB: Technically, the clocks flagged as SYSCLK_FIXED_DIV are "fixed ratio",
+ * meaning that we could change the divider as long as we keep the correct
+ * ratio between all of the clocks, but we don't support that because there is
+ * currently not a need for it.
+ */
+
+SYSCLK(2, pll0_sysclk2, pll0_pllen, 5, SYSCLK_FIXED_DIV);
+SYSCLK(3, pll0_sysclk3, pll0_pllen, 5, 0);
+SYSCLK(4, pll0_sysclk4, pll0_pllen, 5, SYSCLK_FIXED_DIV);
+SYSCLK(5, pll0_sysclk5, pll0_pllen, 5, 0);
+SYSCLK(6, pll0_sysclk6, pll0_pllen, 5, SYSCLK_FIXED_DIV);
+SYSCLK(7, pll0_sysclk7, pll0_pllen, 5, 0);
+
+int da830_pll_init(struct device *dev, void __iomem *base)
+{
+ struct clk *clk;
+
+ davinci_pll_clk_register(dev, &da830_pll_info, "ref_clk", base);
+
+ clk = davinci_pll_sysclk_register(dev, &pll0_sysclk2, base);
+ clk_register_clkdev(clk, "pll0_sysclk2", "da830-psc0");
+ clk_register_clkdev(clk, "pll0_sysclk2", "da830-psc1");
+
+ clk = davinci_pll_sysclk_register(dev, &pll0_sysclk3, base);
+ clk_register_clkdev(clk, "pll0_sysclk3", "da830-psc0");
+
+ clk = davinci_pll_sysclk_register(dev, &pll0_sysclk4, base);
+ clk_register_clkdev(clk, "pll0_sysclk4", "da830-psc0");
+ clk_register_clkdev(clk, "pll0_sysclk4", "da830-psc1");
+
+ clk = davinci_pll_sysclk_register(dev, &pll0_sysclk5, base);
+ clk_register_clkdev(clk, "pll0_sysclk5", "da830-psc1");
+
+ clk = davinci_pll_sysclk_register(dev, &pll0_sysclk6, base);
+ clk_register_clkdev(clk, "pll0_sysclk6", "da830-psc0");
+
+ clk = davinci_pll_sysclk_register(dev, &pll0_sysclk7, base);
+
+ clk = davinci_pll_auxclk_register(dev, "pll0_auxclk", base);
+ clk_register_clkdev(clk, NULL, "i2c_davinci.1");
+ clk_register_clkdev(clk, "timer0", NULL);
+ clk_register_clkdev(clk, NULL, "davinci-wdt");
+
+ return 0;
+}
diff --git a/drivers/clk/davinci/pll-da850.c b/drivers/clk/davinci/pll-da850.c
new file mode 100644
index 000000000000..2a038b7908cc
--- /dev/null
+++ b/drivers/clk/davinci/pll-da850.c
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PLL clock descriptions for TI DA850/OMAP-L138/AM18XX
+ *
+ * Copyright (C) 2018 David Lechner <david@lechnology.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mfd/da8xx-cfgchip.h>
+#include <linux/of.h>
+#include <linux/types.h>
+
+#include "pll.h"
+
+#define OCSEL_OCSRC_OSCIN 0x14
+#define OCSEL_OCSRC_PLL0_SYSCLK(n) (0x16 + (n))
+#define OCSEL_OCSRC_PLL1_OBSCLK 0x1e
+#define OCSEL_OCSRC_PLL1_SYSCLK(n) (0x16 + (n))
+
+static const struct davinci_pll_clk_info da850_pll0_info = {
+ .name = "pll0",
+ .unlock_reg = CFGCHIP(0),
+ .unlock_mask = CFGCHIP0_PLL_MASTER_LOCK,
+ .pllm_mask = GENMASK(4, 0),
+ .pllm_min = 4,
+ .pllm_max = 32,
+ .pllout_min_rate = 300000000,
+ .pllout_max_rate = 600000000,
+ .flags = PLL_HAS_CLKMODE | PLL_HAS_PREDIV | PLL_HAS_POSTDIV |
+ PLL_HAS_EXTCLKSRC,
+};
+
+/*
+ * NB: Technically, the clocks flagged as SYSCLK_FIXED_DIV are "fixed ratio",
+ * meaning that we could change the divider as long as we keep the correct
+ * ratio between all of the clocks, but we don't support that because there is
+ * currently not a need for it.
+ */
+
+SYSCLK(1, pll0_sysclk1, pll0_pllen, 5, SYSCLK_FIXED_DIV);
+SYSCLK(2, pll0_sysclk2, pll0_pllen, 5, SYSCLK_FIXED_DIV);
+SYSCLK(3, pll0_sysclk3, pll0_pllen, 5, 0);
+SYSCLK(4, pll0_sysclk4, pll0_pllen, 5, SYSCLK_FIXED_DIV);
+SYSCLK(5, pll0_sysclk5, pll0_pllen, 5, 0);
+SYSCLK(6, pll0_sysclk6, pll0_pllen, 5, SYSCLK_ARM_RATE | SYSCLK_FIXED_DIV);
+SYSCLK(7, pll0_sysclk7, pll0_pllen, 5, 0);
+
+static const char * const da850_pll0_obsclk_parent_names[] = {
+ "oscin",
+ "pll0_sysclk1",
+ "pll0_sysclk2",
+ "pll0_sysclk3",
+ "pll0_sysclk4",
+ "pll0_sysclk5",
+ "pll0_sysclk6",
+ "pll0_sysclk7",
+ "pll1_obsclk",
+};
+
+static u32 da850_pll0_obsclk_table[] = {
+ OCSEL_OCSRC_OSCIN,
+ OCSEL_OCSRC_PLL0_SYSCLK(1),
+ OCSEL_OCSRC_PLL0_SYSCLK(2),
+ OCSEL_OCSRC_PLL0_SYSCLK(3),
+ OCSEL_OCSRC_PLL0_SYSCLK(4),
+ OCSEL_OCSRC_PLL0_SYSCLK(5),
+ OCSEL_OCSRC_PLL0_SYSCLK(6),
+ OCSEL_OCSRC_PLL0_SYSCLK(7),
+ OCSEL_OCSRC_PLL1_OBSCLK,
+};
+
+static const struct davinci_pll_obsclk_info da850_pll0_obsclk_info = {
+ .name = "pll0_obsclk",
+ .parent_names = da850_pll0_obsclk_parent_names,
+ .num_parents = ARRAY_SIZE(da850_pll0_obsclk_parent_names),
+ .table = da850_pll0_obsclk_table,
+ .ocsrc_mask = GENMASK(4, 0),
+};
+
+int da850_pll0_init(struct device *dev, void __iomem *base)
+{
+ struct clk *clk;
+
+ davinci_pll_clk_register(dev, &da850_pll0_info, "ref_clk", base);
+
+ clk = davinci_pll_sysclk_register(dev, &pll0_sysclk1, base);
+ clk_register_clkdev(clk, "pll0_sysclk1", "da850-psc0");
+
+ clk = davinci_pll_sysclk_register(dev, &pll0_sysclk2, base);
+ clk_register_clkdev(clk, "pll0_sysclk2", "da850-psc0");
+ clk_register_clkdev(clk, "pll0_sysclk2", "da850-psc1");
+ clk_register_clkdev(clk, "pll0_sysclk2", "da850-async3-clksrc");
+
+ clk = davinci_pll_sysclk_register(dev, &pll0_sysclk3, base);
+ clk_register_clkdev(clk, "pll0_sysclk3", "da850-async1-clksrc");
+
+ clk = davinci_pll_sysclk_register(dev, &pll0_sysclk4, base);
+ clk_register_clkdev(clk, "pll0_sysclk4", "da850-psc0");
+ clk_register_clkdev(clk, "pll0_sysclk4", "da850-psc1");
+
+ davinci_pll_sysclk_register(dev, &pll0_sysclk5, base);
+
+ clk = davinci_pll_sysclk_register(dev, &pll0_sysclk6, base);
+ clk_register_clkdev(clk, "pll0_sysclk6", "da850-psc0");
+
+ davinci_pll_sysclk_register(dev, &pll0_sysclk7, base);
+
+ davinci_pll_auxclk_register(dev, "pll0_auxclk", base);
+
+ clk = clk_register_fixed_factor(dev, "async2", "pll0_auxclk",
+ CLK_IS_CRITICAL, 1, 1);
+
+ clk_register_clkdev(clk, NULL, "i2c_davinci.1");
+ clk_register_clkdev(clk, "timer0", NULL);
+ clk_register_clkdev(clk, NULL, "davinci-wdt");
+
+ davinci_pll_obsclk_register(dev, &da850_pll0_obsclk_info, base);
+
+ return 0;
+}
+
+static const struct davinci_pll_sysclk_info *da850_pll0_sysclk_info[] = {
+ &pll0_sysclk1,
+ &pll0_sysclk2,
+ &pll0_sysclk3,
+ &pll0_sysclk4,
+ &pll0_sysclk5,
+ &pll0_sysclk6,
+ &pll0_sysclk7,
+ NULL
+};
+
+int of_da850_pll0_init(struct device *dev, void __iomem *base)
+{
+ return of_davinci_pll_init(dev, &da850_pll0_info,
+ &da850_pll0_obsclk_info,
+ da850_pll0_sysclk_info, 7, base);
+}
+
+static const struct davinci_pll_clk_info da850_pll1_info = {
+ .name = "pll1",
+ .unlock_reg = CFGCHIP(3),
+ .unlock_mask = CFGCHIP3_PLL1_MASTER_LOCK,
+ .pllm_mask = GENMASK(4, 0),
+ .pllm_min = 4,
+ .pllm_max = 32,
+ .pllout_min_rate = 300000000,
+ .pllout_max_rate = 600000000,
+ .flags = PLL_HAS_POSTDIV,
+};
+
+SYSCLK(1, pll1_sysclk1, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
+SYSCLK(2, pll1_sysclk2, pll1_pllen, 5, 0);
+SYSCLK(3, pll1_sysclk3, pll1_pllen, 5, 0);
+
+static const char * const da850_pll1_obsclk_parent_names[] = {
+ "oscin",
+ "pll1_sysclk1",
+ "pll1_sysclk2",
+ "pll1_sysclk3",
+};
+
+static u32 da850_pll1_obsclk_table[] = {
+ OCSEL_OCSRC_OSCIN,
+ OCSEL_OCSRC_PLL1_SYSCLK(1),
+ OCSEL_OCSRC_PLL1_SYSCLK(2),
+ OCSEL_OCSRC_PLL1_SYSCLK(3),
+};
+
+static const struct davinci_pll_obsclk_info da850_pll1_obsclk_info = {
+ .name = "pll1_obsclk",
+ .parent_names = da850_pll1_obsclk_parent_names,
+ .num_parents = ARRAY_SIZE(da850_pll1_obsclk_parent_names),
+ .table = da850_pll1_obsclk_table,
+ .ocsrc_mask = GENMASK(4, 0),
+};
+
+int da850_pll1_init(struct device *dev, void __iomem *base)
+{
+ struct clk *clk;
+
+ davinci_pll_clk_register(dev, &da850_pll1_info, "oscin", base);
+
+ davinci_pll_sysclk_register(dev, &pll1_sysclk1, base);
+
+ clk = davinci_pll_sysclk_register(dev, &pll1_sysclk2, base);
+ clk_register_clkdev(clk, "pll1_sysclk2", "da850-async3-clksrc");
+
+ davinci_pll_sysclk_register(dev, &pll1_sysclk3, base);
+
+ davinci_pll_obsclk_register(dev, &da850_pll1_obsclk_info, base);
+
+ return 0;
+}
+
+static const struct davinci_pll_sysclk_info *da850_pll1_sysclk_info[] = {
+ &pll1_sysclk1,
+ &pll1_sysclk2,
+ &pll1_sysclk3,
+ NULL
+};
+
+int of_da850_pll1_init(struct device *dev, void __iomem *base)
+{
+ return of_davinci_pll_init(dev, &da850_pll1_info,
+ &da850_pll1_obsclk_info,
+ da850_pll1_sysclk_info, 3, base);
+}
diff --git a/drivers/clk/davinci/pll-dm355.c b/drivers/clk/davinci/pll-dm355.c
new file mode 100644
index 000000000000..5345f8286c50
--- /dev/null
+++ b/drivers/clk/davinci/pll-dm355.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PLL clock descriptions for TI DM355
+ *
+ * Copyright (C) 2018 David Lechner <david@lechnology.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/clkdev.h>
+#include <linux/init.h>
+#include <linux/types.h>
+
+#include "pll.h"
+
+static const struct davinci_pll_clk_info dm355_pll1_info = {
+ .name = "pll1",
+ .pllm_mask = GENMASK(7, 0),
+ .pllm_min = 92,
+ .pllm_max = 184,
+ .flags = PLL_HAS_CLKMODE | PLL_HAS_PREDIV | PLL_PREDIV_ALWAYS_ENABLED |
+ PLL_PREDIV_FIXED8 | PLL_HAS_POSTDIV |
+ PLL_POSTDIV_ALWAYS_ENABLED | PLL_POSTDIV_FIXED_DIV,
+};
+
+SYSCLK(1, pll1_sysclk1, pll1, 5, SYSCLK_FIXED_DIV | SYSCLK_ALWAYS_ENABLED);
+SYSCLK(2, pll1_sysclk2, pll1, 5, SYSCLK_FIXED_DIV | SYSCLK_ALWAYS_ENABLED);
+SYSCLK(3, pll1_sysclk3, pll1, 5, SYSCLK_ALWAYS_ENABLED);
+SYSCLK(4, pll1_sysclk4, pll1, 5, SYSCLK_ALWAYS_ENABLED);
+
+int dm355_pll1_init(struct device *dev, void __iomem *base)
+{
+ struct clk *clk;
+
+ davinci_pll_clk_register(dev, &dm355_pll1_info, "ref_clk", base);
+
+ clk = davinci_pll_sysclk_register(dev, &pll1_sysclk1, base);
+ clk_register_clkdev(clk, "pll1_sysclk1", "dm355-psc");
+
+ clk = davinci_pll_sysclk_register(dev, &pll1_sysclk2, base);
+ clk_register_clkdev(clk, "pll1_sysclk2", "dm355-psc");
+
+ clk = davinci_pll_sysclk_register(dev, &pll1_sysclk3, base);
+ clk_register_clkdev(clk, "pll1_sysclk3", "dm355-psc");
+
+ clk = davinci_pll_sysclk_register(dev, &pll1_sysclk4, base);
+ clk_register_clkdev(clk, "pll1_sysclk4", "dm355-psc");
+
+ clk = davinci_pll_auxclk_register(dev, "pll1_auxclk", base);
+ clk_register_clkdev(clk, "pll1_auxclk", "dm355-psc");
+
+ davinci_pll_sysclkbp_clk_register(dev, "pll1_sysclkbp", base);
+
+ return 0;
+}
+
+static const struct davinci_pll_clk_info dm355_pll2_info = {
+ .name = "pll2",
+ .pllm_mask = GENMASK(7, 0),
+ .pllm_min = 92,
+ .pllm_max = 184,
+ .flags = PLL_HAS_PREDIV | PLL_PREDIV_ALWAYS_ENABLED | PLL_HAS_POSTDIV |
+ PLL_POSTDIV_ALWAYS_ENABLED | PLL_POSTDIV_FIXED_DIV,
+};
+
+SYSCLK(1, pll2_sysclk1, pll2, 5, SYSCLK_FIXED_DIV);
+SYSCLK(2, pll2_sysclk2, pll2, 5, SYSCLK_FIXED_DIV | SYSCLK_ALWAYS_ENABLED);
+
+int dm355_pll2_init(struct device *dev, void __iomem *base)
+{
+ davinci_pll_clk_register(dev, &dm355_pll2_info, "oscin", base);
+
+ davinci_pll_sysclk_register(dev, &pll2_sysclk1, base);
+
+ davinci_pll_sysclk_register(dev, &pll2_sysclk2, base);
+
+ davinci_pll_sysclkbp_clk_register(dev, "pll2_sysclkbp", base);
+
+ return 0;
+}
diff --git a/drivers/clk/davinci/pll-dm365.c b/drivers/clk/davinci/pll-dm365.c
new file mode 100644
index 000000000000..5f8d9f42d0f3
--- /dev/null
+++ b/drivers/clk/davinci/pll-dm365.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PLL clock descriptions for TI DM365
+ *
+ * Copyright (C) 2018 David Lechner <david@lechnology.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/clkdev.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+#include "pll.h"
+
+#define OCSEL_OCSRC_ENABLE 0
+
+static const struct davinci_pll_clk_info dm365_pll1_info = {
+ .name = "pll1",
+ .pllm_mask = GENMASK(9, 0),
+ .pllm_min = 1,
+ .pllm_max = 1023,
+ .flags = PLL_HAS_CLKMODE | PLL_HAS_PREDIV | PLL_HAS_POSTDIV |
+ PLL_POSTDIV_ALWAYS_ENABLED | PLL_PLLM_2X,
+};
+
+SYSCLK(1, pll1_sysclk1, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
+SYSCLK(2, pll1_sysclk2, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
+SYSCLK(3, pll1_sysclk3, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
+SYSCLK(4, pll1_sysclk4, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
+SYSCLK(5, pll1_sysclk5, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
+SYSCLK(6, pll1_sysclk6, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
+SYSCLK(7, pll1_sysclk7, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
+SYSCLK(8, pll1_sysclk8, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
+SYSCLK(9, pll1_sysclk9, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
+
+/*
+ * This is a bit of a hack to make OCSEL[OCSRC] on DM365 look like OCSEL[OCSRC]
+ * on DA850. On DM365, OCSEL[OCSRC] is just an enable/disable bit instead of a
+ * multiplexer. By modeling it as a single parent mux clock, the clock code will
+ * still do the right thing in this case.
+ */
+static const char * const dm365_pll_obsclk_parent_names[] = {
+ "oscin",
+};
+
+static u32 dm365_pll_obsclk_table[] = {
+ OCSEL_OCSRC_ENABLE,
+};
+
+static const struct davinci_pll_obsclk_info dm365_pll1_obsclk_info = {
+ .name = "pll1_obsclk",
+ .parent_names = dm365_pll_obsclk_parent_names,
+ .num_parents = ARRAY_SIZE(dm365_pll_obsclk_parent_names),
+ .table = dm365_pll_obsclk_table,
+ .ocsrc_mask = BIT(4),
+};
+
+int dm365_pll1_init(struct device *dev, void __iomem *base)
+{
+ struct clk *clk;
+
+ davinci_pll_clk_register(dev, &dm365_pll1_info, "ref_clk", base);
+
+ clk = davinci_pll_sysclk_register(dev, &pll1_sysclk1, base);
+ clk_register_clkdev(clk, "pll1_sysclk1", "dm365-psc");
+
+ clk = davinci_pll_sysclk_register(dev, &pll1_sysclk2, base);
+ clk_register_clkdev(clk, "pll1_sysclk2", "dm365-psc");
+
+ clk = davinci_pll_sysclk_register(dev, &pll1_sysclk3, base);
+ clk_register_clkdev(clk, "pll1_sysclk3", "dm365-psc");
+
+ clk = davinci_pll_sysclk_register(dev, &pll1_sysclk4, base);
+ clk_register_clkdev(clk, "pll1_sysclk4", "dm365-psc");
+
+ clk = davinci_pll_sysclk_register(dev, &pll1_sysclk5, base);
+ clk_register_clkdev(clk, "pll1_sysclk5", "dm365-psc");
+
+ davinci_pll_sysclk_register(dev, &pll1_sysclk6, base);
+
+ davinci_pll_sysclk_register(dev, &pll1_sysclk7, base);
+
+ clk = davinci_pll_sysclk_register(dev, &pll1_sysclk8, base);
+ clk_register_clkdev(clk, "pll1_sysclk8", "dm365-psc");
+
+ davinci_pll_sysclk_register(dev, &pll1_sysclk9, base);
+
+ clk = davinci_pll_auxclk_register(dev, "pll1_auxclk", base);
+ clk_register_clkdev(clk, "pll1_auxclk", "dm355-psc");
+
+ davinci_pll_sysclkbp_clk_register(dev, "pll1_sysclkbp", base);
+
+ davinci_pll_obsclk_register(dev, &dm365_pll1_obsclk_info, base);
+
+ return 0;
+}
+
+static const struct davinci_pll_clk_info dm365_pll2_info = {
+ .name = "pll2",
+ .pllm_mask = GENMASK(9, 0),
+ .pllm_min = 1,
+ .pllm_max = 1023,
+ .flags = PLL_HAS_PREDIV | PLL_HAS_POSTDIV | PLL_POSTDIV_ALWAYS_ENABLED |
+ PLL_PLLM_2X,
+};
+
+SYSCLK(1, pll2_sysclk1, pll2_pllen, 5, SYSCLK_ALWAYS_ENABLED);
+SYSCLK(2, pll2_sysclk2, pll2_pllen, 5, SYSCLK_ALWAYS_ENABLED);
+SYSCLK(3, pll2_sysclk3, pll2_pllen, 5, SYSCLK_ALWAYS_ENABLED);
+SYSCLK(4, pll2_sysclk4, pll2_pllen, 5, SYSCLK_ALWAYS_ENABLED);
+SYSCLK(5, pll2_sysclk5, pll2_pllen, 5, SYSCLK_ALWAYS_ENABLED);
+
+static const struct davinci_pll_obsclk_info dm365_pll2_obsclk_info = {
+ .name = "pll2_obsclk",
+ .parent_names = dm365_pll_obsclk_parent_names,
+ .num_parents = ARRAY_SIZE(dm365_pll_obsclk_parent_names),
+ .table = dm365_pll_obsclk_table,
+ .ocsrc_mask = BIT(4),
+};
+
+int dm365_pll2_init(struct device *dev, void __iomem *base)
+{
+ struct clk *clk;
+
+ davinci_pll_clk_register(dev, &dm365_pll2_info, "oscin", base);
+
+ davinci_pll_sysclk_register(dev, &pll2_sysclk1, base);
+
+ clk = davinci_pll_sysclk_register(dev, &pll2_sysclk2, base);
+ clk_register_clkdev(clk, "pll1_sysclk2", "dm365-psc");
+
+ davinci_pll_sysclk_register(dev, &pll2_sysclk3, base);
+
+ clk = davinci_pll_sysclk_register(dev, &pll2_sysclk4, base);
+ clk_register_clkdev(clk, "pll1_sysclk4", "dm365-psc");
+
+ davinci_pll_sysclk_register(dev, &pll2_sysclk5, base);
+
+ davinci_pll_auxclk_register(dev, "pll2_auxclk", base);
+
+ davinci_pll_obsclk_register(dev, &dm365_pll2_obsclk_info, base);
+
+ return 0;
+}
diff --git a/drivers/clk/davinci/pll-dm644x.c b/drivers/clk/davinci/pll-dm644x.c
new file mode 100644
index 000000000000..69bf785377cf
--- /dev/null
+++ b/drivers/clk/davinci/pll-dm644x.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PLL clock descriptions for TI DM644X
+ *
+ * Copyright (C) 2018 David Lechner <david@lechnology.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/clkdev.h>
+#include <linux/init.h>
+#include <linux/types.h>
+
+#include "pll.h"
+
+static const struct davinci_pll_clk_info dm644x_pll1_info = {
+ .name = "pll1",
+ .pllm_mask = GENMASK(4, 0),
+ .pllm_min = 1,
+ .pllm_max = 32,
+ .pllout_min_rate = 400000000,
+ .pllout_max_rate = 600000000, /* 810MHz @ 1.3V, -810 only */
+ .flags = PLL_HAS_CLKMODE | PLL_HAS_POSTDIV,
+};
+
+SYSCLK(1, pll1_sysclk1, pll1_pllen, 4, SYSCLK_FIXED_DIV);
+SYSCLK(2, pll1_sysclk2, pll1_pllen, 4, SYSCLK_FIXED_DIV);
+SYSCLK(3, pll1_sysclk3, pll1_pllen, 4, SYSCLK_FIXED_DIV);
+SYSCLK(5, pll1_sysclk5, pll1_pllen, 4, SYSCLK_FIXED_DIV);
+
+int dm644x_pll1_init(struct device *dev, void __iomem *base)
+{
+ struct clk *clk;
+
+ davinci_pll_clk_register(dev, &dm644x_pll1_info, "ref_clk", base);
+
+ clk = davinci_pll_sysclk_register(dev, &pll1_sysclk1, base);
+ clk_register_clkdev(clk, "pll1_sysclk1", "dm644x-psc");
+
+ clk = davinci_pll_sysclk_register(dev, &pll1_sysclk2, base);
+ clk_register_clkdev(clk, "pll1_sysclk2", "dm644x-psc");
+
+ clk = davinci_pll_sysclk_register(dev, &pll1_sysclk3, base);
+ clk_register_clkdev(clk, "pll1_sysclk3", "dm644x-psc");
+
+ clk = davinci_pll_sysclk_register(dev, &pll1_sysclk5, base);
+ clk_register_clkdev(clk, "pll1_sysclk5", "dm644x-psc");
+
+ clk = davinci_pll_auxclk_register(dev, "pll1_auxclk", base);
+ clk_register_clkdev(clk, "pll1_auxclk", "dm644x-psc");
+
+ davinci_pll_sysclkbp_clk_register(dev, "pll1_sysclkbp", base);
+
+ return 0;
+}
+
+static const struct davinci_pll_clk_info dm644x_pll2_info = {
+ .name = "pll2",
+ .pllm_mask = GENMASK(4, 0),
+ .pllm_min = 1,
+ .pllm_max = 32,
+ .pllout_min_rate = 400000000,
+ .pllout_max_rate = 900000000,
+ .flags = PLL_HAS_POSTDIV | PLL_POSTDIV_FIXED_DIV,
+};
+
+SYSCLK(1, pll2_sysclk1, pll2_pllen, 4, 0);
+SYSCLK(2, pll2_sysclk2, pll2_pllen, 4, 0);
+
+int dm644x_pll2_init(struct device *dev, void __iomem *base)
+{
+ davinci_pll_clk_register(dev, &dm644x_pll2_info, "oscin", base);
+
+ davinci_pll_sysclk_register(dev, &pll2_sysclk1, base);
+
+ davinci_pll_sysclk_register(dev, &pll2_sysclk2, base);
+
+ davinci_pll_sysclkbp_clk_register(dev, "pll2_sysclkbp", base);
+
+ return 0;
+}
diff --git a/drivers/clk/davinci/pll-dm646x.c b/drivers/clk/davinci/pll-dm646x.c
new file mode 100644
index 000000000000..a61cc3256418
--- /dev/null
+++ b/drivers/clk/davinci/pll-dm646x.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PLL clock descriptions for TI DM646X
+ *
+ * Copyright (C) 2018 David Lechner <david@lechnology.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/init.h>
+#include <linux/types.h>
+
+#include "pll.h"
+
+static const struct davinci_pll_clk_info dm646x_pll1_info = {
+ .name = "pll1",
+ .pllm_mask = GENMASK(4, 0),
+ .pllm_min = 14,
+ .pllm_max = 32,
+ .flags = PLL_HAS_CLKMODE,
+};
+
+SYSCLK(1, pll1_sysclk1, pll1_pllen, 4, SYSCLK_FIXED_DIV);
+SYSCLK(2, pll1_sysclk2, pll1_pllen, 4, SYSCLK_FIXED_DIV);
+SYSCLK(3, pll1_sysclk3, pll1_pllen, 4, SYSCLK_FIXED_DIV);
+SYSCLK(4, pll1_sysclk4, pll1_pllen, 4, 0);
+SYSCLK(5, pll1_sysclk5, pll1_pllen, 4, 0);
+SYSCLK(6, pll1_sysclk6, pll1_pllen, 4, 0);
+SYSCLK(8, pll1_sysclk8, pll1_pllen, 4, 0);
+SYSCLK(9, pll1_sysclk9, pll1_pllen, 4, 0);
+
+int dm646x_pll1_init(struct device *dev, void __iomem *base)
+{
+ struct clk *clk;
+
+ davinci_pll_clk_register(dev, &dm646x_pll1_info, "ref_clk", base);
+
+ clk = davinci_pll_sysclk_register(dev, &pll1_sysclk1, base);
+ clk_register_clkdev(clk, "pll1_sysclk1", "dm646x-psc");
+
+ clk = davinci_pll_sysclk_register(dev, &pll1_sysclk2, base);
+ clk_register_clkdev(clk, "pll1_sysclk2", "dm646x-psc");
+
+ clk = davinci_pll_sysclk_register(dev, &pll1_sysclk3, base);
+ clk_register_clkdev(clk, "pll1_sysclk3", "dm646x-psc");
+ clk_register_clkdev(clk, NULL, "davinci-wdt");
+
+ clk = davinci_pll_sysclk_register(dev, &pll1_sysclk4, base);
+ clk_register_clkdev(clk, "pll1_sysclk4", "dm646x-psc");
+
+ clk = davinci_pll_sysclk_register(dev, &pll1_sysclk5, base);
+ clk_register_clkdev(clk, "pll1_sysclk5", "dm646x-psc");
+
+ davinci_pll_sysclk_register(dev, &pll1_sysclk6, base);
+
+ davinci_pll_sysclk_register(dev, &pll1_sysclk8, base);
+
+ davinci_pll_sysclk_register(dev, &pll1_sysclk9, base);
+
+ davinci_pll_sysclkbp_clk_register(dev, "pll1_sysclkbp", base);
+
+ davinci_pll_auxclk_register(dev, "pll1_auxclk", base);
+
+ return 0;
+}
+
+static const struct davinci_pll_clk_info dm646x_pll2_info = {
+ .name = "pll2",
+ .pllm_mask = GENMASK(4, 0),
+ .pllm_min = 14,
+ .pllm_max = 32,
+ .flags = 0,
+};
+
+SYSCLK(1, pll2_sysclk1, pll2_pllen, 4, 0);
+
+int dm646x_pll2_init(struct device *dev, void __iomem *base)
+{
+ davinci_pll_clk_register(dev, &dm646x_pll2_info, "oscin", base);
+
+ davinci_pll_sysclk_register(dev, &pll2_sysclk1, base);
+
+ return 0;
+}
diff --git a/drivers/clk/davinci/pll.c b/drivers/clk/davinci/pll.c
new file mode 100644
index 000000000000..23a24c944f1d
--- /dev/null
+++ b/drivers/clk/davinci/pll.c
@@ -0,0 +1,899 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PLL clock driver for TI Davinci SoCs
+ *
+ * Copyright (C) 2018 David Lechner <david@lechnology.com>
+ *
+ * Based on arch/arm/mach-davinci/clock.c
+ * Copyright (C) 2006-2007 Texas Instruments.
+ * Copyright (C) 2008-2009 Deep Root Systems, LLC
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/notifier.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/platform_data/clk-davinci-pll.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "pll.h"
+
+#define MAX_NAME_SIZE 20
+#define OSCIN_CLK_NAME "oscin"
+
+#define REVID 0x000
+#define PLLCTL 0x100
+#define OCSEL 0x104
+#define PLLSECCTL 0x108
+#define PLLM 0x110
+#define PREDIV 0x114
+#define PLLDIV1 0x118
+#define PLLDIV2 0x11c
+#define PLLDIV3 0x120
+#define OSCDIV 0x124
+#define POSTDIV 0x128
+#define BPDIV 0x12c
+#define PLLCMD 0x138
+#define PLLSTAT 0x13c
+#define ALNCTL 0x140
+#define DCHANGE 0x144
+#define CKEN 0x148
+#define CKSTAT 0x14c
+#define SYSTAT 0x150
+#define PLLDIV4 0x160
+#define PLLDIV5 0x164
+#define PLLDIV6 0x168
+#define PLLDIV7 0x16c
+#define PLLDIV8 0x170
+#define PLLDIV9 0x174
+
+#define PLLCTL_PLLEN BIT(0)
+#define PLLCTL_PLLPWRDN BIT(1)
+#define PLLCTL_PLLRST BIT(3)
+#define PLLCTL_PLLDIS BIT(4)
+#define PLLCTL_PLLENSRC BIT(5)
+#define PLLCTL_CLKMODE BIT(8)
+
+/* shared by most *DIV registers */
+#define DIV_RATIO_SHIFT 0
+#define DIV_RATIO_WIDTH 5
+#define DIV_ENABLE_SHIFT 15
+
+#define PLLCMD_GOSET BIT(0)
+#define PLLSTAT_GOSTAT BIT(0)
+
+#define CKEN_OBSCLK_SHIFT 1
+#define CKEN_AUXEN_SHIFT 0
+
+/*
+ * OMAP-L138 system reference guide recommends a wait for 4 OSCIN/CLKIN
+ * cycles to ensure that the PLLC has switched to bypass mode. Delay of 1us
+ * ensures we are good for all > 4MHz OSCIN/CLKIN inputs. Typically the input
+ * is ~25MHz. Units are micro seconds.
+ */
+#define PLL_BYPASS_TIME 1
+
+/* From OMAP-L138 datasheet table 6-4. Units are micro seconds */
+#define PLL_RESET_TIME 1
+
+/*
+ * From OMAP-L138 datasheet table 6-4; assuming prediv = 1, sqrt(pllm) = 4
+ * Units are micro seconds.
+ */
+#define PLL_LOCK_TIME 20
+
+/**
+ * struct davinci_pll_clk - Main PLL clock (aka PLLOUT)
+ * @hw: clk_hw for the pll
+ * @base: Base memory address
+ * @pllm_min: The minimum allowable PLLM[PLLM] value
+ * @pllm_max: The maxiumum allowable PLLM[PLLM] value
+ * @pllm_mask: Bitmask for PLLM[PLLM] value
+ */
+struct davinci_pll_clk {
+ struct clk_hw hw;
+ void __iomem *base;
+ u32 pllm_min;
+ u32 pllm_max;
+ u32 pllm_mask;
+};
+
+#define to_davinci_pll_clk(_hw) \
+ container_of((_hw), struct davinci_pll_clk, hw)
+
+static unsigned long davinci_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct davinci_pll_clk *pll = to_davinci_pll_clk(hw);
+ unsigned long rate = parent_rate;
+ u32 mult;
+
+ mult = readl(pll->base + PLLM) & pll->pllm_mask;
+ rate *= mult + 1;
+
+ return rate;
+}
+
+static int davinci_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct davinci_pll_clk *pll = to_davinci_pll_clk(hw);
+ struct clk_hw *parent = req->best_parent_hw;
+ unsigned long parent_rate = req->best_parent_rate;
+ unsigned long rate = req->rate;
+ unsigned long best_rate, r;
+ u32 mult;
+
+ /* there is a limited range of valid outputs (see datasheet) */
+ if (rate < req->min_rate)
+ return -EINVAL;
+
+ rate = min(rate, req->max_rate);
+ mult = rate / parent_rate;
+ best_rate = parent_rate * mult;
+
+ /* easy case when there is no PREDIV */
+ if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) {
+ if (best_rate < req->min_rate)
+ return -EINVAL;
+
+ if (mult < pll->pllm_min || mult > pll->pllm_max)
+ return -EINVAL;
+
+ req->rate = best_rate;
+
+ return 0;
+ }
+
+ /* see if the PREDIV clock can help us */
+ best_rate = 0;
+
+ for (mult = pll->pllm_min; mult <= pll->pllm_max; mult++) {
+ parent_rate = clk_hw_round_rate(parent, rate / mult);
+ r = parent_rate * mult;
+ if (r < req->min_rate)
+ continue;
+ if (r > rate || r > req->max_rate)
+ break;
+ if (r > best_rate) {
+ best_rate = r;
+ req->rate = best_rate;
+ req->best_parent_rate = parent_rate;
+ if (best_rate == rate)
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int davinci_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct davinci_pll_clk *pll = to_davinci_pll_clk(hw);
+ u32 mult;
+
+ mult = rate / parent_rate;
+ writel(mult - 1, pll->base + PLLM);
+
+ return 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int davinci_pll_debug_init(struct clk_hw *hw, struct dentry *dentry);
+#else
+#define davinci_pll_debug_init NULL
+#endif
+
+static const struct clk_ops davinci_pll_ops = {
+ .recalc_rate = davinci_pll_recalc_rate,
+ .determine_rate = davinci_pll_determine_rate,
+ .set_rate = davinci_pll_set_rate,
+ .debug_init = davinci_pll_debug_init,
+};
+
+/* PLLM works differently on DM365 */
+static unsigned long dm365_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct davinci_pll_clk *pll = to_davinci_pll_clk(hw);
+ unsigned long rate = parent_rate;
+ u32 mult;
+
+ mult = readl(pll->base + PLLM) & pll->pllm_mask;
+ rate *= mult * 2;
+
+ return rate;
+}
+
+static const struct clk_ops dm365_pll_ops = {
+ .recalc_rate = dm365_pll_recalc_rate,
+ .debug_init = davinci_pll_debug_init,
+};
+
+/**
+ * davinci_pll_div_register - common *DIV clock implementation
+ * @name: the clock name
+ * @parent_name: the parent clock name
+ * @reg: the *DIV register
+ * @fixed: if true, the divider is a fixed value
+ * @flags: bitmap of CLK_* flags from clock-provider.h
+ */
+static struct clk *davinci_pll_div_register(struct device *dev,
+ const char *name,
+ const char *parent_name,
+ void __iomem *reg,
+ bool fixed, u32 flags)
+{
+ const char * const *parent_names = parent_name ? &parent_name : NULL;
+ int num_parents = parent_name ? 1 : 0;
+ const struct clk_ops *divider_ops = &clk_divider_ops;
+ struct clk_gate *gate;
+ struct clk_divider *divider;
+
+ gate = devm_kzalloc(dev, sizeof(*gate), GFP_KERNEL);
+ if (!gate)
+ return ERR_PTR(-ENOMEM);
+
+ gate->reg = reg;
+ gate->bit_idx = DIV_ENABLE_SHIFT;
+
+ divider = devm_kzalloc(dev, sizeof(*divider), GFP_KERNEL);
+ if (!divider)
+ return ERR_PTR(-ENOMEM);
+
+ divider->reg = reg;
+ divider->shift = DIV_RATIO_SHIFT;
+ divider->width = DIV_RATIO_WIDTH;
+
+ if (fixed) {
+ divider->flags |= CLK_DIVIDER_READ_ONLY;
+ divider_ops = &clk_divider_ro_ops;
+ }
+
+ return clk_register_composite(dev, name, parent_names, num_parents,
+ NULL, NULL, &divider->hw, divider_ops,
+ &gate->hw, &clk_gate_ops, flags);
+}
+
+struct davinci_pllen_clk {
+ struct clk_hw hw;
+ void __iomem *base;
+};
+
+#define to_davinci_pllen_clk(_hw) \
+ container_of((_hw), struct davinci_pllen_clk, hw)
+
+static const struct clk_ops davinci_pllen_ops = {
+ /* this clocks just uses the clock notification feature */
+};
+
+/*
+ * The PLL has to be switched into bypass mode while we are chaning the rate,
+ * so we do that on the PLLEN clock since it is the end of the line. This will
+ * switch to bypass before any of the parent clocks (PREDIV, PLL, POSTDIV) are
+ * changed and will switch back to the PLL after the changes have been made.
+ */
+static int davinci_pllen_rate_change(struct notifier_block *nb,
+ unsigned long flags, void *data)
+{
+ struct clk_notifier_data *cnd = data;
+ struct clk_hw *hw = __clk_get_hw(cnd->clk);
+ struct davinci_pllen_clk *pll = to_davinci_pllen_clk(hw);
+ u32 ctrl;
+
+ ctrl = readl(pll->base + PLLCTL);
+
+ if (flags == PRE_RATE_CHANGE) {
+ /* Switch the PLL to bypass mode */
+ ctrl &= ~(PLLCTL_PLLENSRC | PLLCTL_PLLEN);
+ writel(ctrl, pll->base + PLLCTL);
+
+ udelay(PLL_BYPASS_TIME);
+
+ /* Reset and enable PLL */
+ ctrl &= ~(PLLCTL_PLLRST | PLLCTL_PLLDIS);
+ writel(ctrl, pll->base + PLLCTL);
+ } else {
+ udelay(PLL_RESET_TIME);
+
+ /* Bring PLL out of reset */
+ ctrl |= PLLCTL_PLLRST;
+ writel(ctrl, pll->base + PLLCTL);
+
+ udelay(PLL_LOCK_TIME);
+
+ /* Remove PLL from bypass mode */
+ ctrl |= PLLCTL_PLLEN;
+ writel(ctrl, pll->base + PLLCTL);
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct davinci_pll_platform_data *davinci_pll_get_pdata(struct device *dev)
+{
+ struct davinci_pll_platform_data *pdata = dev_get_platdata(dev);
+
+ /*
+ * Platform data is optional, so allocate a new struct if one was not
+ * provided. For device tree, this will always be the case.
+ */
+ if (!pdata)
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+
+ /* for device tree, we need to fill in the struct */
+ if (dev->of_node)
+ pdata->cfgchip =
+ syscon_regmap_lookup_by_compatible("ti,da830-cfgchip");
+
+ return pdata;
+}
+
+static struct notifier_block davinci_pllen_notifier = {
+ .notifier_call = davinci_pllen_rate_change,
+};
+
+/**
+ * davinci_pll_clk_register - Register a PLL clock
+ * @info: The device-specific clock info
+ * @parent_name: The parent clock name
+ * @base: The PLL's memory region
+ *
+ * This creates a series of clocks that represent the PLL.
+ *
+ * OSCIN > [PREDIV >] PLL > [POSTDIV >] PLLEN
+ *
+ * - OSCIN is the parent clock (on secondary PLL, may come from primary PLL)
+ * - PREDIV and POSTDIV are optional (depends on the PLL controller)
+ * - PLL is the PLL output (aka PLLOUT)
+ * - PLLEN is the bypass multiplexer
+ *
+ * Returns: The PLLOUT clock or a negative error code.
+ */
+struct clk *davinci_pll_clk_register(struct device *dev,
+ const struct davinci_pll_clk_info *info,
+ const char *parent_name,
+ void __iomem *base)
+{
+ struct davinci_pll_platform_data *pdata;
+ char prediv_name[MAX_NAME_SIZE];
+ char pllout_name[MAX_NAME_SIZE];
+ char postdiv_name[MAX_NAME_SIZE];
+ char pllen_name[MAX_NAME_SIZE];
+ struct clk_init_data init;
+ struct davinci_pll_clk *pllout;
+ struct davinci_pllen_clk *pllen;
+ struct clk *pllout_clk, *clk;
+
+ pdata = davinci_pll_get_pdata(dev);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ if (info->flags & PLL_HAS_CLKMODE) {
+ /*
+ * If a PLL has PLLCTL[CLKMODE], then it is the primary PLL.
+ * We register a clock named "oscin" that serves as the internal
+ * "input clock" domain shared by both PLLs (if there are 2)
+ * and will be the parent clock to the AUXCLK, SYSCLKBP and
+ * OBSCLK domains. NB: The various TRMs use "OSCIN" to mean
+ * a number of different things. In this driver we use it to
+ * mean the signal after the PLLCTL[CLKMODE] switch.
+ */
+ clk = clk_register_fixed_factor(dev, OSCIN_CLK_NAME,
+ parent_name, 0, 1, 1);
+ if (IS_ERR(clk))
+ return clk;
+
+ parent_name = OSCIN_CLK_NAME;
+ }
+
+ if (info->flags & PLL_HAS_PREDIV) {
+ bool fixed = info->flags & PLL_PREDIV_FIXED_DIV;
+ u32 flags = 0;
+
+ snprintf(prediv_name, MAX_NAME_SIZE, "%s_prediv", info->name);
+
+ if (info->flags & PLL_PREDIV_ALWAYS_ENABLED)
+ flags |= CLK_IS_CRITICAL;
+
+ /* Some? DM355 chips don't correctly report the PREDIV value */
+ if (info->flags & PLL_PREDIV_FIXED8)
+ clk = clk_register_fixed_factor(dev, prediv_name,
+ parent_name, flags, 1, 8);
+ else
+ clk = davinci_pll_div_register(dev, prediv_name,
+ parent_name, base + PREDIV, fixed, flags);
+ if (IS_ERR(clk))
+ return clk;
+
+ parent_name = prediv_name;
+ }
+
+ /* Unlock writing to PLL registers */
+ if (info->unlock_reg) {
+ if (IS_ERR_OR_NULL(pdata->cfgchip))
+ dev_warn(dev, "Failed to get CFGCHIP (%ld)\n",
+ PTR_ERR(pdata->cfgchip));
+ else
+ regmap_write_bits(pdata->cfgchip, info->unlock_reg,
+ info->unlock_mask, 0);
+ }
+
+ pllout = devm_kzalloc(dev, sizeof(*pllout), GFP_KERNEL);
+ if (!pllout)
+ return ERR_PTR(-ENOMEM);
+
+ snprintf(pllout_name, MAX_NAME_SIZE, "%s_pllout", info->name);
+
+ init.name = pllout_name;
+ if (info->flags & PLL_PLLM_2X)
+ init.ops = &dm365_pll_ops;
+ else
+ init.ops = &davinci_pll_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = 0;
+
+ if (info->flags & PLL_HAS_PREDIV)
+ init.flags |= CLK_SET_RATE_PARENT;
+
+ pllout->hw.init = &init;
+ pllout->base = base;
+ pllout->pllm_mask = info->pllm_mask;
+ pllout->pllm_min = info->pllm_min;
+ pllout->pllm_max = info->pllm_max;
+
+ pllout_clk = devm_clk_register(dev, &pllout->hw);
+ if (IS_ERR(pllout_clk))
+ return pllout_clk;
+
+ clk_hw_set_rate_range(&pllout->hw, info->pllout_min_rate,
+ info->pllout_max_rate);
+
+ parent_name = pllout_name;
+
+ if (info->flags & PLL_HAS_POSTDIV) {
+ bool fixed = info->flags & PLL_POSTDIV_FIXED_DIV;
+ u32 flags = CLK_SET_RATE_PARENT;
+
+ snprintf(postdiv_name, MAX_NAME_SIZE, "%s_postdiv", info->name);
+
+ if (info->flags & PLL_POSTDIV_ALWAYS_ENABLED)
+ flags |= CLK_IS_CRITICAL;
+
+ clk = davinci_pll_div_register(dev, postdiv_name, parent_name,
+ base + POSTDIV, fixed, flags);
+ if (IS_ERR(clk))
+ return clk;
+
+ parent_name = postdiv_name;
+ }
+
+ pllen = devm_kzalloc(dev, sizeof(*pllout), GFP_KERNEL);
+ if (!pllen)
+ return ERR_PTR(-ENOMEM);
+
+ snprintf(pllen_name, MAX_NAME_SIZE, "%s_pllen", info->name);
+
+ init.name = pllen_name;
+ init.ops = &davinci_pllen_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = CLK_SET_RATE_PARENT;
+
+ pllen->hw.init = &init;
+ pllen->base = base;
+
+ clk = devm_clk_register(dev, &pllen->hw);
+ if (IS_ERR(clk))
+ return clk;
+
+ clk_notifier_register(clk, &davinci_pllen_notifier);
+
+ return pllout_clk;
+}
+
+/**
+ * davinci_pll_auxclk_register - Register bypass clock (AUXCLK)
+ * @name: The clock name
+ * @base: The PLL memory region
+ */
+struct clk *davinci_pll_auxclk_register(struct device *dev,
+ const char *name,
+ void __iomem *base)
+{
+ return clk_register_gate(dev, name, OSCIN_CLK_NAME, 0, base + CKEN,
+ CKEN_AUXEN_SHIFT, 0, NULL);
+}
+
+/**
+ * davinci_pll_sysclkbp_clk_register - Register bypass divider clock (SYSCLKBP)
+ * @name: The clock name
+ * @base: The PLL memory region
+ */
+struct clk *davinci_pll_sysclkbp_clk_register(struct device *dev,
+ const char *name,
+ void __iomem *base)
+{
+ return clk_register_divider(dev, name, OSCIN_CLK_NAME, 0, base + BPDIV,
+ DIV_RATIO_SHIFT, DIV_RATIO_WIDTH,
+ CLK_DIVIDER_READ_ONLY, NULL);
+}
+
+/**
+ * davinci_pll_obsclk_register - Register oscillator divider clock (OBSCLK)
+ * @info: The clock info
+ * @base: The PLL memory region
+ */
+struct clk *
+davinci_pll_obsclk_register(struct device *dev,
+ const struct davinci_pll_obsclk_info *info,
+ void __iomem *base)
+{
+ struct clk_mux *mux;
+ struct clk_gate *gate;
+ struct clk_divider *divider;
+ u32 oscdiv;
+
+ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
+ if (!mux)
+ return ERR_PTR(-ENOMEM);
+
+ mux->reg = base + OCSEL;
+ mux->table = info->table;
+ mux->mask = info->ocsrc_mask;
+
+ gate = devm_kzalloc(dev, sizeof(*gate), GFP_KERNEL);
+ if (!gate)
+ return ERR_PTR(-ENOMEM);
+
+ gate->reg = base + CKEN;
+ gate->bit_idx = CKEN_OBSCLK_SHIFT;
+
+ divider = devm_kzalloc(dev, sizeof(*divider), GFP_KERNEL);
+ if (!divider)
+ return ERR_PTR(-ENOMEM);
+
+ divider->reg = base + OSCDIV;
+ divider->shift = DIV_RATIO_SHIFT;
+ divider->width = DIV_RATIO_WIDTH;
+
+ /* make sure divider is enabled just in case bootloader disabled it */
+ oscdiv = readl(base + OSCDIV);
+ oscdiv |= BIT(DIV_ENABLE_SHIFT);
+ writel(oscdiv, base + OSCDIV);
+
+ return clk_register_composite(dev, info->name, info->parent_names,
+ info->num_parents,
+ &mux->hw, &clk_mux_ops,
+ &divider->hw, &clk_divider_ops,
+ &gate->hw, &clk_gate_ops, 0);
+}
+
+/* The PLL SYSCLKn clocks have a mechanism for synchronizing rate changes. */
+static int davinci_pll_sysclk_rate_change(struct notifier_block *nb,
+ unsigned long flags, void *data)
+{
+ struct clk_notifier_data *cnd = data;
+ struct clk_hw *hw = __clk_get_hw(clk_get_parent(cnd->clk));
+ struct davinci_pllen_clk *pll = to_davinci_pllen_clk(hw);
+ u32 pllcmd, pllstat;
+
+ switch (flags) {
+ case POST_RATE_CHANGE:
+ /* apply the changes */
+ pllcmd = readl(pll->base + PLLCMD);
+ pllcmd |= PLLCMD_GOSET;
+ writel(pllcmd, pll->base + PLLCMD);
+ /* fallthrough */
+ case PRE_RATE_CHANGE:
+ /* Wait until for outstanding changes to take effect */
+ do {
+ pllstat = readl(pll->base + PLLSTAT);
+ } while (pllstat & PLLSTAT_GOSTAT);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block davinci_pll_sysclk_notifier = {
+ .notifier_call = davinci_pll_sysclk_rate_change,
+};
+
+/**
+ * davinci_pll_sysclk_register - Register divider clocks (SYSCLKn)
+ * @info: The clock info
+ * @base: The PLL memory region
+ */
+struct clk *
+davinci_pll_sysclk_register(struct device *dev,
+ const struct davinci_pll_sysclk_info *info,
+ void __iomem *base)
+{
+ const struct clk_ops *divider_ops = &clk_divider_ops;
+ struct clk_gate *gate;
+ struct clk_divider *divider;
+ struct clk *clk;
+ u32 reg;
+ u32 flags = 0;
+
+ /* PLLDIVn registers are not entirely consecutive */
+ if (info->id < 4)
+ reg = PLLDIV1 + 4 * (info->id - 1);
+ else
+ reg = PLLDIV4 + 4 * (info->id - 4);
+
+ gate = devm_kzalloc(dev, sizeof(*gate), GFP_KERNEL);
+ if (!gate)
+ return ERR_PTR(-ENOMEM);
+
+ gate->reg = base + reg;
+ gate->bit_idx = DIV_ENABLE_SHIFT;
+
+ divider = devm_kzalloc(dev, sizeof(*divider), GFP_KERNEL);
+ if (!divider)
+ return ERR_PTR(-ENOMEM);
+
+ divider->reg = base + reg;
+ divider->shift = DIV_RATIO_SHIFT;
+ divider->width = info->ratio_width;
+ divider->flags = 0;
+
+ if (info->flags & SYSCLK_FIXED_DIV) {
+ divider->flags |= CLK_DIVIDER_READ_ONLY;
+ divider_ops = &clk_divider_ro_ops;
+ }
+
+ /* Only the ARM clock can change the parent PLL rate */
+ if (info->flags & SYSCLK_ARM_RATE)
+ flags |= CLK_SET_RATE_PARENT;
+
+ if (info->flags & SYSCLK_ALWAYS_ENABLED)
+ flags |= CLK_IS_CRITICAL;
+
+ clk = clk_register_composite(dev, info->name, &info->parent_name, 1,
+ NULL, NULL, &divider->hw, divider_ops,
+ &gate->hw, &clk_gate_ops, flags);
+ if (IS_ERR(clk))
+ return clk;
+
+ clk_notifier_register(clk, &davinci_pll_sysclk_notifier);
+
+ return clk;
+}
+
+int of_davinci_pll_init(struct device *dev,
+ const struct davinci_pll_clk_info *info,
+ const struct davinci_pll_obsclk_info *obsclk_info,
+ const struct davinci_pll_sysclk_info **div_info,
+ u8 max_sysclk_id,
+ void __iomem *base)
+{
+ struct device_node *node = dev->of_node;
+ struct device_node *child;
+ const char *parent_name;
+ struct clk *clk;
+
+ if (info->flags & PLL_HAS_CLKMODE)
+ parent_name = of_clk_get_parent_name(node, 0);
+ else
+ parent_name = OSCIN_CLK_NAME;
+
+ clk = davinci_pll_clk_register(dev, info, parent_name, base);
+ if (IS_ERR(clk)) {
+ dev_err(dev, "failed to register %s\n", info->name);
+ return PTR_ERR(clk);
+ }
+
+ child = of_get_child_by_name(node, "pllout");
+ if (of_device_is_available(child))
+ of_clk_add_provider(child, of_clk_src_simple_get, clk);
+ of_node_put(child);
+
+ child = of_get_child_by_name(node, "sysclk");
+ if (of_device_is_available(child)) {
+ struct clk_onecell_data *clk_data;
+ struct clk **clks;
+ int n_clks = max_sysclk_id + 1;
+ int i;
+
+ clk_data = devm_kzalloc(dev, sizeof(*clk_data), GFP_KERNEL);
+ if (!clk_data)
+ return -ENOMEM;
+
+ clks = devm_kmalloc_array(dev, n_clks, sizeof(*clks), GFP_KERNEL);
+ if (!clks)
+ return -ENOMEM;
+
+ clk_data->clks = clks;
+ clk_data->clk_num = n_clks;
+
+ for (i = 0; i < n_clks; i++)
+ clks[i] = ERR_PTR(-ENOENT);
+
+ for (; *div_info; div_info++) {
+ clk = davinci_pll_sysclk_register(dev, *div_info, base);
+ if (IS_ERR(clk))
+ dev_warn(dev, "failed to register %s (%ld)\n",
+ (*div_info)->name, PTR_ERR(clk));
+ else
+ clks[(*div_info)->id] = clk;
+ }
+ of_clk_add_provider(child, of_clk_src_onecell_get, clk_data);
+ }
+ of_node_put(child);
+
+ child = of_get_child_by_name(node, "auxclk");
+ if (of_device_is_available(child)) {
+ char child_name[MAX_NAME_SIZE];
+
+ snprintf(child_name, MAX_NAME_SIZE, "%s_auxclk", info->name);
+
+ clk = davinci_pll_auxclk_register(dev, child_name, base);
+ if (IS_ERR(clk))
+ dev_warn(dev, "failed to register %s (%ld)\n",
+ child_name, PTR_ERR(clk));
+ else
+ of_clk_add_provider(child, of_clk_src_simple_get, clk);
+ }
+ of_node_put(child);
+
+ child = of_get_child_by_name(node, "obsclk");
+ if (of_device_is_available(child)) {
+ if (obsclk_info)
+ clk = davinci_pll_obsclk_register(dev, obsclk_info, base);
+ else
+ clk = ERR_PTR(-EINVAL);
+
+ if (IS_ERR(clk))
+ dev_warn(dev, "failed to register obsclk (%ld)\n",
+ PTR_ERR(clk));
+ else
+ of_clk_add_provider(child, of_clk_src_simple_get, clk);
+ }
+ of_node_put(child);
+
+ return 0;
+}
+
+static const struct of_device_id davinci_pll_of_match[] = {
+ { .compatible = "ti,da850-pll0", .data = of_da850_pll0_init },
+ { .compatible = "ti,da850-pll1", .data = of_da850_pll1_init },
+ { }
+};
+
+static const struct platform_device_id davinci_pll_id_table[] = {
+ { .name = "da830-pll", .driver_data = (kernel_ulong_t)da830_pll_init },
+ { .name = "da850-pll0", .driver_data = (kernel_ulong_t)da850_pll0_init },
+ { .name = "da850-pll1", .driver_data = (kernel_ulong_t)da850_pll1_init },
+ { .name = "dm355-pll1", .driver_data = (kernel_ulong_t)dm355_pll1_init },
+ { .name = "dm355-pll2", .driver_data = (kernel_ulong_t)dm355_pll2_init },
+ { .name = "dm365-pll1", .driver_data = (kernel_ulong_t)dm365_pll1_init },
+ { .name = "dm365-pll2", .driver_data = (kernel_ulong_t)dm365_pll2_init },
+ { .name = "dm644x-pll1", .driver_data = (kernel_ulong_t)dm644x_pll1_init },
+ { .name = "dm644x-pll2", .driver_data = (kernel_ulong_t)dm644x_pll2_init },
+ { .name = "dm646x-pll1", .driver_data = (kernel_ulong_t)dm646x_pll1_init },
+ { .name = "dm646x-pll2", .driver_data = (kernel_ulong_t)dm646x_pll2_init },
+ { }
+};
+
+typedef int (*davinci_pll_init)(struct device *dev, void __iomem *base);
+
+static int davinci_pll_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct of_device_id *of_id;
+ davinci_pll_init pll_init = NULL;
+ struct resource *res;
+ void __iomem *base;
+
+ of_id = of_match_device(davinci_pll_of_match, dev);
+ if (of_id)
+ pll_init = of_id->data;
+ else if (pdev->id_entry)
+ pll_init = (void *)pdev->id_entry->driver_data;
+
+ if (!pll_init) {
+ dev_err(dev, "unable to find driver data\n");
+ return -EINVAL;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ return pll_init(dev, base);
+}
+
+static struct platform_driver davinci_pll_driver = {
+ .probe = davinci_pll_probe,
+ .driver = {
+ .name = "davinci-pll-clk",
+ .of_match_table = davinci_pll_of_match,
+ },
+ .id_table = davinci_pll_id_table,
+};
+
+static int __init davinci_pll_driver_init(void)
+{
+ return platform_driver_register(&davinci_pll_driver);
+}
+
+/* has to be postcore_initcall because PSC devices depend on PLL parent clocks */
+postcore_initcall(davinci_pll_driver_init);
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+
+#define DEBUG_REG(n) \
+{ \
+ .name = #n, \
+ .offset = n, \
+}
+
+static const struct debugfs_reg32 davinci_pll_regs[] = {
+ DEBUG_REG(REVID),
+ DEBUG_REG(PLLCTL),
+ DEBUG_REG(OCSEL),
+ DEBUG_REG(PLLSECCTL),
+ DEBUG_REG(PLLM),
+ DEBUG_REG(PREDIV),
+ DEBUG_REG(PLLDIV1),
+ DEBUG_REG(PLLDIV2),
+ DEBUG_REG(PLLDIV3),
+ DEBUG_REG(OSCDIV),
+ DEBUG_REG(POSTDIV),
+ DEBUG_REG(BPDIV),
+ DEBUG_REG(PLLCMD),
+ DEBUG_REG(PLLSTAT),
+ DEBUG_REG(ALNCTL),
+ DEBUG_REG(DCHANGE),
+ DEBUG_REG(CKEN),
+ DEBUG_REG(CKSTAT),
+ DEBUG_REG(SYSTAT),
+ DEBUG_REG(PLLDIV4),
+ DEBUG_REG(PLLDIV5),
+ DEBUG_REG(PLLDIV6),
+ DEBUG_REG(PLLDIV7),
+ DEBUG_REG(PLLDIV8),
+ DEBUG_REG(PLLDIV9),
+};
+
+static int davinci_pll_debug_init(struct clk_hw *hw, struct dentry *dentry)
+{
+ struct davinci_pll_clk *pll = to_davinci_pll_clk(hw);
+ struct debugfs_regset32 *regset;
+ struct dentry *d;
+
+ regset = kzalloc(sizeof(*regset), GFP_KERNEL);
+ if (!regset)
+ return -ENOMEM;
+
+ regset->regs = davinci_pll_regs;
+ regset->nregs = ARRAY_SIZE(davinci_pll_regs);
+ regset->base = pll->base;
+
+ d = debugfs_create_regset32("registers", 0400, dentry, regset);
+ if (IS_ERR(d)) {
+ kfree(regset);
+ return PTR_ERR(d);
+ }
+
+ return 0;
+}
+#endif
diff --git a/drivers/clk/davinci/pll.h b/drivers/clk/davinci/pll.h
new file mode 100644
index 000000000000..b1b6fb23f972
--- /dev/null
+++ b/drivers/clk/davinci/pll.h
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Clock driver for TI Davinci PSC controllers
+ *
+ * Copyright (C) 2018 David Lechner <david@lechnology.com>
+ */
+
+#ifndef __CLK_DAVINCI_PLL_H___
+#define __CLK_DAVINCI_PLL_H___
+
+#include <linux/bitops.h>
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/types.h>
+
+#define PLL_HAS_CLKMODE BIT(0) /* PLL has PLLCTL[CLKMODE] */
+#define PLL_HAS_PREDIV BIT(1) /* has prediv before PLL */
+#define PLL_PREDIV_ALWAYS_ENABLED BIT(2) /* don't clear DEN bit */
+#define PLL_PREDIV_FIXED_DIV BIT(3) /* fixed divider value */
+#define PLL_HAS_POSTDIV BIT(4) /* has postdiv after PLL */
+#define PLL_POSTDIV_ALWAYS_ENABLED BIT(5) /* don't clear DEN bit */
+#define PLL_POSTDIV_FIXED_DIV BIT(6) /* fixed divider value */
+#define PLL_HAS_EXTCLKSRC BIT(7) /* has selectable bypass */
+#define PLL_PLLM_2X BIT(8) /* PLLM value is 2x (DM365) */
+#define PLL_PREDIV_FIXED8 BIT(9) /* DM355 quirk */
+
+/** davinci_pll_clk_info - controller-specific PLL info
+ * @name: The name of the PLL
+ * @unlock_reg: Option CFGCHIP register for unlocking PLL
+ * @unlock_mask: Bitmask used with @unlock_reg
+ * @pllm_mask: Bitmask for PLLM[PLLM] value
+ * @pllm_min: Minimum allowable value for PLLM[PLLM]
+ * @pllm_max: Maximum allowable value for PLLM[PLLM]
+ * @pllout_min_rate: Minimum allowable rate for PLLOUT
+ * @pllout_max_rate: Maximum allowable rate for PLLOUT
+ * @flags: Bitmap of PLL_* flags.
+ */
+struct davinci_pll_clk_info {
+ const char *name;
+ u32 unlock_reg;
+ u32 unlock_mask;
+ u32 pllm_mask;
+ u32 pllm_min;
+ u32 pllm_max;
+ unsigned long pllout_min_rate;
+ unsigned long pllout_max_rate;
+ u32 flags;
+};
+
+#define SYSCLK_ARM_RATE BIT(0) /* Controls ARM rate */
+#define SYSCLK_ALWAYS_ENABLED BIT(1) /* Or bad things happen */
+#define SYSCLK_FIXED_DIV BIT(2) /* Fixed divider */
+
+/** davinci_pll_sysclk_info - SYSCLKn-specific info
+ * @name: The name of the clock
+ * @parent_name: The name of the parent clock
+ * @id: "n" in "SYSCLKn"
+ * @ratio_width: Width (in bits) of RATIO in PLLDIVn register
+ * @flags: Bitmap of SYSCLK_* flags.
+ */
+struct davinci_pll_sysclk_info {
+ const char *name;
+ const char *parent_name;
+ u32 id;
+ u32 ratio_width;
+ u32 flags;
+};
+
+#define SYSCLK(i, n, p, w, f) \
+static const struct davinci_pll_sysclk_info n = { \
+ .name = #n, \
+ .parent_name = #p, \
+ .id = (i), \
+ .ratio_width = (w), \
+ .flags = (f), \
+}
+
+/** davinci_pll_obsclk_info - OBSCLK-specific info
+ * @name: The name of the clock
+ * @parent_names: Array of names of the parent clocks
+ * @num_parents: Length of @parent_names
+ * @table: Array of values to write to OCSEL[OCSRC] cooresponding to
+ * @parent_names
+ * @ocsrc_mask: Bitmask for OCSEL[OCSRC]
+ */
+struct davinci_pll_obsclk_info {
+ const char *name;
+ const char * const *parent_names;
+ u8 num_parents;
+ u32 *table;
+ u32 ocsrc_mask;
+};
+
+struct clk *davinci_pll_clk_register(struct device *dev,
+ const struct davinci_pll_clk_info *info,
+ const char *parent_name,
+ void __iomem *base);
+struct clk *davinci_pll_auxclk_register(struct device *dev,
+ const char *name,
+ void __iomem *base);
+struct clk *davinci_pll_sysclkbp_clk_register(struct device *dev,
+ const char *name,
+ void __iomem *base);
+struct clk *
+davinci_pll_obsclk_register(struct device *dev,
+ const struct davinci_pll_obsclk_info *info,
+ void __iomem *base);
+struct clk *
+davinci_pll_sysclk_register(struct device *dev,
+ const struct davinci_pll_sysclk_info *info,
+ void __iomem *base);
+
+int of_davinci_pll_init(struct device *dev,
+ const struct davinci_pll_clk_info *info,
+ const struct davinci_pll_obsclk_info *obsclk_info,
+ const struct davinci_pll_sysclk_info **div_info,
+ u8 max_sysclk_id,
+ void __iomem *base);
+
+/* Platform-specific callbacks */
+
+int da830_pll_init(struct device *dev, void __iomem *base);
+
+int da850_pll0_init(struct device *dev, void __iomem *base);
+int da850_pll1_init(struct device *dev, void __iomem *base);
+int of_da850_pll0_init(struct device *dev, void __iomem *base);
+int of_da850_pll1_init(struct device *dev, void __iomem *base);
+
+int dm355_pll1_init(struct device *dev, void __iomem *base);
+int dm355_pll2_init(struct device *dev, void __iomem *base);
+
+int dm365_pll1_init(struct device *dev, void __iomem *base);
+int dm365_pll2_init(struct device *dev, void __iomem *base);
+
+int dm644x_pll1_init(struct device *dev, void __iomem *base);
+int dm644x_pll2_init(struct device *dev, void __iomem *base);
+
+int dm646x_pll1_init(struct device *dev, void __iomem *base);
+int dm646x_pll2_init(struct device *dev, void __iomem *base);
+
+#endif /* __CLK_DAVINCI_PLL_H___ */
diff --git a/drivers/clk/davinci/psc-da830.c b/drivers/clk/davinci/psc-da830.c
new file mode 100644
index 000000000000..f61abf5632ff
--- /dev/null
+++ b/drivers/clk/davinci/psc-da830.c
@@ -0,0 +1,116 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PSC clock descriptions for TI DA830/OMAP-L137/AM17XX
+ *
+ * Copyright (C) 2018 David Lechner <david@lechnology.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+#include "psc.h"
+
+LPSC_CLKDEV1(spi0_clkdev, NULL, "spi_davinci.0");
+LPSC_CLKDEV1(mmcsd_clkdev, NULL, "da830-mmc.0");
+LPSC_CLKDEV1(uart0_clkdev, NULL, "serial8250.0");
+
+static const struct davinci_lpsc_clk_info da830_psc0_info[] = {
+ LPSC(0, 0, tpcc, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
+ LPSC(1, 0, tptc0, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
+ LPSC(2, 0, tptc1, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
+ LPSC(3, 0, aemif, pll0_sysclk3, NULL, LPSC_ALWAYS_ENABLED),
+ LPSC(4, 0, spi0, pll0_sysclk2, spi0_clkdev, 0),
+ LPSC(5, 0, mmcsd, pll0_sysclk2, mmcsd_clkdev, 0),
+ LPSC(6, 0, aintc, pll0_sysclk4, NULL, LPSC_ALWAYS_ENABLED),
+ LPSC(7, 0, arm_rom, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
+ LPSC(8, 0, secu_mgr, pll0_sysclk4, NULL, LPSC_ALWAYS_ENABLED),
+ LPSC(9, 0, uart0, pll0_sysclk2, uart0_clkdev, 0),
+ LPSC(10, 0, scr0_ss, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
+ LPSC(11, 0, scr1_ss, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
+ LPSC(12, 0, scr2_ss, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
+ LPSC(13, 0, pruss, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
+ LPSC(14, 0, arm, pll0_sysclk6, NULL, LPSC_ALWAYS_ENABLED),
+ { }
+};
+
+static int da830_psc0_init(struct device *dev, void __iomem *base)
+{
+ return davinci_psc_register_clocks(dev, da830_psc0_info, 16, base);
+}
+
+static struct clk_bulk_data da830_psc0_parent_clks[] = {
+ { .id = "pll0_sysclk2" },
+ { .id = "pll0_sysclk3" },
+ { .id = "pll0_sysclk4" },
+ { .id = "pll0_sysclk6" },
+};
+
+const struct davinci_psc_init_data da830_psc0_init_data = {
+ .parent_clks = da830_psc0_parent_clks,
+ .num_parent_clks = ARRAY_SIZE(da830_psc0_parent_clks),
+ .psc_init = &da830_psc0_init,
+};
+
+LPSC_CLKDEV2(usb0_clkdev, NULL, "musb-da8xx",
+ NULL, "cppi41-dmaengine");
+LPSC_CLKDEV1(usb1_clkdev, NULL, "ohci-da8xx");
+/* REVISIT: gpio-davinci.c should be modified to drop con_id */
+LPSC_CLKDEV1(gpio_clkdev, "gpio", NULL);
+LPSC_CLKDEV2(emac_clkdev, NULL, "davinci_emac.1",
+ "fck", "davinci_mdio.0");
+LPSC_CLKDEV1(mcasp0_clkdev, NULL, "davinci-mcasp.0");
+LPSC_CLKDEV1(mcasp1_clkdev, NULL, "davinci-mcasp.1");
+LPSC_CLKDEV1(mcasp2_clkdev, NULL, "davinci-mcasp.2");
+LPSC_CLKDEV1(spi1_clkdev, NULL, "spi_davinci.1");
+LPSC_CLKDEV1(i2c1_clkdev, NULL, "i2c_davinci.2");
+LPSC_CLKDEV1(uart1_clkdev, NULL, "serial8250.1");
+LPSC_CLKDEV1(uart2_clkdev, NULL, "serial8250.2");
+LPSC_CLKDEV1(lcdc_clkdev, "fck", "da8xx_lcdc.0");
+LPSC_CLKDEV2(pwm_clkdev, "fck", "ehrpwm.0",
+ "fck", "ehrpwm.1");
+LPSC_CLKDEV3(ecap_clkdev, "fck", "ecap.0",
+ "fck", "ecap.1",
+ "fck", "ecap.2");
+LPSC_CLKDEV2(eqep_clkdev, NULL, "eqep.0",
+ NULL, "eqep.1");
+
+static const struct davinci_lpsc_clk_info da830_psc1_info[] = {
+ LPSC(1, 0, usb0, pll0_sysclk2, usb0_clkdev, 0),
+ LPSC(2, 0, usb1, pll0_sysclk4, usb1_clkdev, 0),
+ LPSC(3, 0, gpio, pll0_sysclk4, gpio_clkdev, 0),
+ LPSC(5, 0, emac, pll0_sysclk4, emac_clkdev, 0),
+ LPSC(6, 0, emif3, pll0_sysclk5, NULL, LPSC_ALWAYS_ENABLED),
+ LPSC(7, 0, mcasp0, pll0_sysclk2, mcasp0_clkdev, 0),
+ LPSC(8, 0, mcasp1, pll0_sysclk2, mcasp1_clkdev, 0),
+ LPSC(9, 0, mcasp2, pll0_sysclk2, mcasp2_clkdev, 0),
+ LPSC(10, 0, spi1, pll0_sysclk2, spi1_clkdev, 0),
+ LPSC(11, 0, i2c1, pll0_sysclk4, i2c1_clkdev, 0),
+ LPSC(12, 0, uart1, pll0_sysclk2, uart1_clkdev, 0),
+ LPSC(13, 0, uart2, pll0_sysclk2, uart2_clkdev, 0),
+ LPSC(16, 0, lcdc, pll0_sysclk2, lcdc_clkdev, 0),
+ LPSC(17, 0, pwm, pll0_sysclk2, pwm_clkdev, 0),
+ LPSC(20, 0, ecap, pll0_sysclk2, ecap_clkdev, 0),
+ LPSC(21, 0, eqep, pll0_sysclk2, eqep_clkdev, 0),
+ { }
+};
+
+static int da830_psc1_init(struct device *dev, void __iomem *base)
+{
+ return davinci_psc_register_clocks(dev, da830_psc1_info, 32, base);
+}
+
+static struct clk_bulk_data da830_psc1_parent_clks[] = {
+ { .id = "pll0_sysclk2" },
+ { .id = "pll0_sysclk4" },
+ { .id = "pll0_sysclk5" },
+};
+
+const struct davinci_psc_init_data da830_psc1_init_data = {
+ .parent_clks = da830_psc1_parent_clks,
+ .num_parent_clks = ARRAY_SIZE(da830_psc1_parent_clks),
+ .psc_init = &da830_psc1_init,
+};
diff --git a/drivers/clk/davinci/psc-da850.c b/drivers/clk/davinci/psc-da850.c
new file mode 100644
index 000000000000..d196dcbed560
--- /dev/null
+++ b/drivers/clk/davinci/psc-da850.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PSC clock descriptions for TI DA850/OMAP-L138/AM18XX
+ *
+ * Copyright (C) 2018 David Lechner <david@lechnology.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/reset-controller.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/types.h>
+
+#include "psc.h"
+
+LPSC_CLKDEV2(emifa_clkdev, NULL, "ti-aemif",
+ "aemif", "davinci_nand.0");
+LPSC_CLKDEV1(spi0_clkdev, NULL, "spi_davinci.0");
+LPSC_CLKDEV1(mmcsd0_clkdev, NULL, "da830-mmc.0");
+LPSC_CLKDEV1(uart0_clkdev, NULL, "serial8250.0");
+/* REVISIT: used dev_id instead of con_id */
+LPSC_CLKDEV1(arm_clkdev, "arm", NULL);
+LPSC_CLKDEV1(dsp_clkdev, NULL, "davinci-rproc.0");
+
+static const struct davinci_lpsc_clk_info da850_psc0_info[] = {
+ LPSC(0, 0, tpcc0, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
+ LPSC(1, 0, tptc0, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
+ LPSC(2, 0, tptc1, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
+ LPSC(3, 0, emifa, async1, emifa_clkdev, 0),
+ LPSC(4, 0, spi0, pll0_sysclk2, spi0_clkdev, 0),
+ LPSC(5, 0, mmcsd0, pll0_sysclk2, mmcsd0_clkdev, 0),
+ LPSC(6, 0, aintc, pll0_sysclk4, NULL, LPSC_ALWAYS_ENABLED),
+ LPSC(7, 0, arm_rom, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
+ LPSC(9, 0, uart0, pll0_sysclk2, uart0_clkdev, 0),
+ LPSC(13, 0, pruss, pll0_sysclk2, NULL, 0),
+ LPSC(14, 0, arm, pll0_sysclk6, arm_clkdev, LPSC_ALWAYS_ENABLED | LPSC_SET_RATE_PARENT),
+ LPSC(15, 1, dsp, pll0_sysclk1, dsp_clkdev, LPSC_FORCE | LPSC_LOCAL_RESET),
+ { }
+};
+
+LPSC_CLKDEV3(usb0_clkdev, "fck", "da830-usb-phy-clks",
+ NULL, "musb-da8xx",
+ NULL, "cppi41-dmaengine");
+LPSC_CLKDEV1(usb1_clkdev, NULL, "ohci-da8xx");
+/* REVISIT: gpio-davinci.c should be modified to drop con_id */
+LPSC_CLKDEV1(gpio_clkdev, "gpio", NULL);
+LPSC_CLKDEV2(emac_clkdev, NULL, "davinci_emac.1",
+ "fck", "davinci_mdio.0");
+LPSC_CLKDEV1(mcasp0_clkdev, NULL, "davinci-mcasp.0");
+LPSC_CLKDEV1(sata_clkdev, "fck", "ahci_da850");
+LPSC_CLKDEV1(vpif_clkdev, NULL, "vpif");
+LPSC_CLKDEV1(spi1_clkdev, NULL, "spi_davinci.1");
+LPSC_CLKDEV1(i2c1_clkdev, NULL, "i2c_davinci.2");
+LPSC_CLKDEV1(uart1_clkdev, NULL, "serial8250.1");
+LPSC_CLKDEV1(uart2_clkdev, NULL, "serial8250.2");
+LPSC_CLKDEV1(mcbsp0_clkdev, NULL, "davinci-mcbsp.0");
+LPSC_CLKDEV1(mcbsp1_clkdev, NULL, "davinci-mcbsp.1");
+LPSC_CLKDEV1(lcdc_clkdev, "fck", "da8xx_lcdc.0");
+LPSC_CLKDEV3(ehrpwm_clkdev, "fck", "ehrpwm.0",
+ "fck", "ehrpwm.1",
+ NULL, "da830-tbclksync");
+LPSC_CLKDEV1(mmcsd1_clkdev, NULL, "da830-mmc.1");
+LPSC_CLKDEV3(ecap_clkdev, "fck", "ecap.0",
+ "fck", "ecap.1",
+ "fck", "ecap.2");
+
+static struct reset_control_lookup da850_psc0_reset_lookup_table[] = {
+ RESET_LOOKUP("da850-psc0", 15, "davinci-rproc.0", NULL),
+};
+
+static int da850_psc0_init(struct device *dev, void __iomem *base)
+{
+ reset_controller_add_lookup(da850_psc0_reset_lookup_table,
+ ARRAY_SIZE(da850_psc0_reset_lookup_table));
+ return davinci_psc_register_clocks(dev, da850_psc0_info, 16, base);
+}
+
+static int of_da850_psc0_init(struct device *dev, void __iomem *base)
+{
+ return of_davinci_psc_clk_init(dev, da850_psc0_info, 16, base);
+}
+
+static struct clk_bulk_data da850_psc0_parent_clks[] = {
+ { .id = "pll0_sysclk1" },
+ { .id = "pll0_sysclk2" },
+ { .id = "pll0_sysclk4" },
+ { .id = "pll0_sysclk6" },
+ { .id = "async1" },
+};
+
+const struct davinci_psc_init_data da850_psc0_init_data = {
+ .parent_clks = da850_psc0_parent_clks,
+ .num_parent_clks = ARRAY_SIZE(da850_psc0_parent_clks),
+ .psc_init = &da850_psc0_init,
+};
+
+const struct davinci_psc_init_data of_da850_psc0_init_data = {
+ .parent_clks = da850_psc0_parent_clks,
+ .num_parent_clks = ARRAY_SIZE(da850_psc0_parent_clks),
+ .psc_init = &of_da850_psc0_init,
+};
+
+static const struct davinci_lpsc_clk_info da850_psc1_info[] = {
+ LPSC(0, 0, tpcc1, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
+ LPSC(1, 0, usb0, pll0_sysclk2, usb0_clkdev, 0),
+ LPSC(2, 0, usb1, pll0_sysclk4, usb1_clkdev, 0),
+ LPSC(3, 0, gpio, pll0_sysclk4, gpio_clkdev, 0),
+ LPSC(5, 0, emac, pll0_sysclk4, emac_clkdev, 0),
+ LPSC(6, 0, ddr, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
+ LPSC(7, 0, mcasp0, async3, mcasp0_clkdev, 0),
+ LPSC(8, 0, sata, pll0_sysclk2, sata_clkdev, LPSC_FORCE),
+ LPSC(9, 0, vpif, pll0_sysclk2, vpif_clkdev, 0),
+ LPSC(10, 0, spi1, async3, spi1_clkdev, 0),
+ LPSC(11, 0, i2c1, pll0_sysclk4, i2c1_clkdev, 0),
+ LPSC(12, 0, uart1, async3, uart1_clkdev, 0),
+ LPSC(13, 0, uart2, async3, uart2_clkdev, 0),
+ LPSC(14, 0, mcbsp0, async3, mcbsp0_clkdev, 0),
+ LPSC(15, 0, mcbsp1, async3, mcbsp1_clkdev, 0),
+ LPSC(16, 0, lcdc, pll0_sysclk2, lcdc_clkdev, 0),
+ LPSC(17, 0, ehrpwm, async3, ehrpwm_clkdev, 0),
+ LPSC(18, 0, mmcsd1, pll0_sysclk2, mmcsd1_clkdev, 0),
+ LPSC(20, 0, ecap, async3, ecap_clkdev, 0),
+ LPSC(21, 0, tptc2, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
+ { }
+};
+
+static int da850_psc1_init(struct device *dev, void __iomem *base)
+{
+ return davinci_psc_register_clocks(dev, da850_psc1_info, 32, base);
+}
+
+static int of_da850_psc1_init(struct device *dev, void __iomem *base)
+{
+ return of_davinci_psc_clk_init(dev, da850_psc1_info, 32, base);
+}
+
+static struct clk_bulk_data da850_psc1_parent_clks[] = {
+ { .id = "pll0_sysclk2" },
+ { .id = "pll0_sysclk4" },
+ { .id = "async3" },
+};
+
+const struct davinci_psc_init_data da850_psc1_init_data = {
+ .parent_clks = da850_psc1_parent_clks,
+ .num_parent_clks = ARRAY_SIZE(da850_psc1_parent_clks),
+ .psc_init = &da850_psc1_init,
+};
+
+const struct davinci_psc_init_data of_da850_psc1_init_data = {
+ .parent_clks = da850_psc1_parent_clks,
+ .num_parent_clks = ARRAY_SIZE(da850_psc1_parent_clks),
+ .psc_init = &of_da850_psc1_init,
+};
diff --git a/drivers/clk/davinci/psc-dm355.c b/drivers/clk/davinci/psc-dm355.c
new file mode 100644
index 000000000000..6995ecea2677
--- /dev/null
+++ b/drivers/clk/davinci/psc-dm355.c
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PSC clock descriptions for TI DaVinci DM355
+ *
+ * Copyright (C) 2018 David Lechner <david@lechnology.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+#include "psc.h"
+
+LPSC_CLKDEV1(vpss_master_clkdev, "master", "vpss");
+LPSC_CLKDEV1(vpss_slave_clkdev, "slave", "vpss");
+LPSC_CLKDEV1(spi1_clkdev, NULL, "spi_davinci.1");
+LPSC_CLKDEV1(mmcsd1_clkdev, NULL, "dm6441-mmc.1");
+LPSC_CLKDEV1(mcbsp1_clkdev, NULL, "davinci-mcbsp.1");
+LPSC_CLKDEV1(usb_clkdev, "usb", NULL);
+LPSC_CLKDEV1(spi2_clkdev, NULL, "spi_davinci.2");
+LPSC_CLKDEV1(aemif_clkdev, "aemif", NULL);
+LPSC_CLKDEV1(mmcsd0_clkdev, NULL, "dm6441-mmc.0");
+LPSC_CLKDEV1(mcbsp0_clkdev, NULL, "davinci-mcbsp.0");
+LPSC_CLKDEV1(i2c_clkdev, NULL, "i2c_davinci.1");
+LPSC_CLKDEV1(uart0_clkdev, NULL, "serial8250.0");
+LPSC_CLKDEV1(uart1_clkdev, NULL, "serial8250.1");
+LPSC_CLKDEV1(uart2_clkdev, NULL, "serial8250.2");
+LPSC_CLKDEV1(spi0_clkdev, NULL, "spi_davinci.0");
+/* REVISIT: gpio-davinci.c should be modified to drop con_id */
+LPSC_CLKDEV1(gpio_clkdev, "gpio", NULL);
+LPSC_CLKDEV1(timer0_clkdev, "timer0", NULL);
+LPSC_CLKDEV1(timer2_clkdev, NULL, "davinci-wdt");
+LPSC_CLKDEV1(vpss_dac_clkdev, "vpss_dac", NULL);
+
+static const struct davinci_lpsc_clk_info dm355_psc_info[] = {
+ LPSC(0, 0, vpss_master, pll1_sysclk4, vpss_master_clkdev, 0),
+ LPSC(1, 0, vpss_slave, pll1_sysclk4, vpss_slave_clkdev, 0),
+ LPSC(5, 0, timer3, pll1_auxclk, NULL, 0),
+ LPSC(6, 0, spi1, pll1_sysclk2, spi1_clkdev, 0),
+ LPSC(7, 0, mmcsd1, pll1_sysclk2, mmcsd1_clkdev, 0),
+ LPSC(8, 0, asp1, pll1_sysclk2, NULL, 0),
+ LPSC(9, 0, usb, pll1_sysclk2, usb_clkdev, 0),
+ LPSC(10, 0, pwm3, pll1_auxclk, NULL, 0),
+ LPSC(11, 0, spi2, pll1_sysclk2, spi2_clkdev, 0),
+ LPSC(12, 0, rto, pll1_auxclk, NULL, 0),
+ LPSC(14, 0, aemif, pll1_sysclk2, aemif_clkdev, 0),
+ LPSC(15, 0, mmcsd0, pll1_sysclk2, mmcsd0_clkdev, 0),
+ LPSC(17, 0, asp0, pll1_sysclk2, NULL, 0),
+ LPSC(18, 0, i2c, pll1_auxclk, i2c_clkdev, 0),
+ LPSC(19, 0, uart0, pll1_auxclk, uart0_clkdev, 0),
+ LPSC(20, 0, uart1, pll1_auxclk, uart1_clkdev, 0),
+ LPSC(21, 0, uart2, pll1_sysclk2, uart2_clkdev, 0),
+ LPSC(22, 0, spi0, pll1_sysclk2, spi0_clkdev, 0),
+ LPSC(23, 0, pwm0, pll1_auxclk, NULL, 0),
+ LPSC(24, 0, pwm1, pll1_auxclk, NULL, 0),
+ LPSC(25, 0, pwm2, pll1_auxclk, NULL, 0),
+ LPSC(26, 0, gpio, pll1_sysclk2, gpio_clkdev, 0),
+ LPSC(27, 0, timer0, pll1_auxclk, timer0_clkdev, LPSC_ALWAYS_ENABLED),
+ LPSC(28, 0, timer1, pll1_auxclk, NULL, 0),
+ /* REVISIT: why can't this be disabled? */
+ LPSC(29, 0, timer2, pll1_auxclk, timer2_clkdev, LPSC_ALWAYS_ENABLED),
+ LPSC(31, 0, arm, pll1_sysclk1, NULL, LPSC_ALWAYS_ENABLED),
+ LPSC(40, 0, mjcp, pll1_sysclk1, NULL, 0),
+ LPSC(41, 0, vpss_dac, pll1_sysclk3, vpss_dac_clkdev, 0),
+ { }
+};
+
+static int dm355_psc_init(struct device *dev, void __iomem *base)
+{
+ return davinci_psc_register_clocks(dev, dm355_psc_info, 42, base);
+}
+
+static struct clk_bulk_data dm355_psc_parent_clks[] = {
+ { .id = "pll1_sysclk1" },
+ { .id = "pll1_sysclk2" },
+ { .id = "pll1_sysclk3" },
+ { .id = "pll1_sysclk4" },
+ { .id = "pll1_auxclk" },
+};
+
+const struct davinci_psc_init_data dm355_psc_init_data = {
+ .parent_clks = dm355_psc_parent_clks,
+ .num_parent_clks = ARRAY_SIZE(dm355_psc_parent_clks),
+ .psc_init = &dm355_psc_init,
+};
diff --git a/drivers/clk/davinci/psc-dm365.c b/drivers/clk/davinci/psc-dm365.c
new file mode 100644
index 000000000000..3ad915f37376
--- /dev/null
+++ b/drivers/clk/davinci/psc-dm365.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PSC clock descriptions for TI DaVinci DM365
+ *
+ * Copyright (C) 2018 David Lechner <david@lechnology.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+#include "psc.h"
+
+LPSC_CLKDEV1(vpss_slave_clkdev, "slave", "vpss");
+LPSC_CLKDEV1(spi1_clkdev, NULL, "spi_davinci.1");
+LPSC_CLKDEV1(mmcsd1_clkdev, NULL, "da830-mmc.1");
+LPSC_CLKDEV1(asp0_clkdev, NULL, "davinci-mcbsp");
+LPSC_CLKDEV1(usb_clkdev, "usb", NULL);
+LPSC_CLKDEV1(spi2_clkdev, NULL, "spi_davinci.2");
+LPSC_CLKDEV1(aemif_clkdev, "aemif", NULL);
+LPSC_CLKDEV1(mmcsd0_clkdev, NULL, "da830-mmc.0");
+LPSC_CLKDEV1(i2c_clkdev, NULL, "i2c_davinci.1");
+LPSC_CLKDEV1(uart0_clkdev, NULL, "serial8250.0");
+LPSC_CLKDEV1(uart1_clkdev, NULL, "serial8250.1");
+LPSC_CLKDEV1(spi0_clkdev, NULL, "spi_davinci.0");
+/* REVISIT: gpio-davinci.c should be modified to drop con_id */
+LPSC_CLKDEV1(gpio_clkdev, "gpio", NULL);
+LPSC_CLKDEV1(timer0_clkdev, "timer0", NULL);
+LPSC_CLKDEV1(timer2_clkdev, NULL, "davinci-wdt");
+LPSC_CLKDEV1(spi3_clkdev, NULL, "spi_davinci.3");
+LPSC_CLKDEV1(spi4_clkdev, NULL, "spi_davinci.4");
+LPSC_CLKDEV2(emac_clkdev, NULL, "davinci_emac.1",
+ "fck", "davinci_mdio.0");
+LPSC_CLKDEV1(voice_codec_clkdev, NULL, "davinci_voicecodec");
+LPSC_CLKDEV1(vpss_dac_clkdev, "vpss_dac", NULL);
+LPSC_CLKDEV1(vpss_master_clkdev, "master", "vpss");
+
+static const struct davinci_lpsc_clk_info dm365_psc_info[] = {
+ LPSC(1, 0, vpss_slave, pll1_sysclk5, vpss_slave_clkdev, 0),
+ LPSC(5, 0, timer3, pll1_auxclk, NULL, 0),
+ LPSC(6, 0, spi1, pll1_sysclk4, spi1_clkdev, 0),
+ LPSC(7, 0, mmcsd1, pll1_sysclk4, mmcsd1_clkdev, 0),
+ LPSC(8, 0, asp0, pll1_sysclk4, asp0_clkdev, 0),
+ LPSC(9, 0, usb, pll1_auxclk, usb_clkdev, 0),
+ LPSC(10, 0, pwm3, pll1_auxclk, NULL, 0),
+ LPSC(11, 0, spi2, pll1_sysclk4, spi2_clkdev, 0),
+ LPSC(12, 0, rto, pll1_sysclk4, NULL, 0),
+ LPSC(14, 0, aemif, pll1_sysclk4, aemif_clkdev, 0),
+ LPSC(15, 0, mmcsd0, pll1_sysclk8, mmcsd0_clkdev, 0),
+ LPSC(18, 0, i2c, pll1_auxclk, i2c_clkdev, 0),
+ LPSC(19, 0, uart0, pll1_auxclk, uart0_clkdev, 0),
+ LPSC(20, 0, uart1, pll1_sysclk4, uart1_clkdev, 0),
+ LPSC(22, 0, spi0, pll1_sysclk4, spi0_clkdev, 0),
+ LPSC(23, 0, pwm0, pll1_auxclk, NULL, 0),
+ LPSC(24, 0, pwm1, pll1_auxclk, NULL, 0),
+ LPSC(25, 0, pwm2, pll1_auxclk, NULL, 0),
+ LPSC(26, 0, gpio, pll1_sysclk4, gpio_clkdev, 0),
+ LPSC(27, 0, timer0, pll1_auxclk, timer0_clkdev, LPSC_ALWAYS_ENABLED),
+ LPSC(28, 0, timer1, pll1_auxclk, NULL, 0),
+ /* REVISIT: why can't this be disabled? */
+ LPSC(29, 0, timer2, pll1_auxclk, timer2_clkdev, LPSC_ALWAYS_ENABLED),
+ LPSC(31, 0, arm, pll2_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
+ LPSC(38, 0, spi3, pll1_sysclk4, spi3_clkdev, 0),
+ LPSC(39, 0, spi4, pll1_auxclk, spi4_clkdev, 0),
+ LPSC(40, 0, emac, pll2_sysclk4, emac_clkdev, 0),
+ LPSC(44, 1, voice_codec, pll1_sysclk3, voice_codec_clkdev, 0),
+ LPSC(46, 1, vpss_dac, pll1_sysclk3, vpss_dac_clkdev, 0),
+ LPSC(47, 0, vpss_master, pll1_sysclk5, vpss_master_clkdev, 0),
+ LPSC(50, 0, mjcp, pll1_sysclk3, NULL, 0),
+ { }
+};
+
+static int dm365_psc_init(struct device *dev, void __iomem *base)
+{
+ return davinci_psc_register_clocks(dev, dm365_psc_info, 52, base);
+}
+
+static struct clk_bulk_data dm365_psc_parent_clks[] = {
+ { .id = "pll1_sysclk1" },
+ { .id = "pll1_sysclk3" },
+ { .id = "pll1_sysclk4" },
+ { .id = "pll1_sysclk5" },
+ { .id = "pll1_sysclk8" },
+ { .id = "pll2_sysclk2" },
+ { .id = "pll2_sysclk4" },
+ { .id = "pll1_auxclk" },
+};
+
+const struct davinci_psc_init_data dm365_psc_init_data = {
+ .parent_clks = dm365_psc_parent_clks,
+ .num_parent_clks = ARRAY_SIZE(dm365_psc_parent_clks),
+ .psc_init = &dm365_psc_init,
+};
diff --git a/drivers/clk/davinci/psc-dm644x.c b/drivers/clk/davinci/psc-dm644x.c
new file mode 100644
index 000000000000..c22367baa46f
--- /dev/null
+++ b/drivers/clk/davinci/psc-dm644x.c
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PSC clock descriptions for TI DaVinci DM644x
+ *
+ * Copyright (C) 2018 David Lechner <david@lechnology.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+#include "psc.h"
+
+LPSC_CLKDEV1(vpss_master_clkdev, "master", "vpss");
+LPSC_CLKDEV1(vpss_slave_clkdev, "slave", "vpss");
+LPSC_CLKDEV2(emac_clkdev, NULL, "davinci_emac.1",
+ "fck", "davinci_mdio.0");
+LPSC_CLKDEV1(usb_clkdev, "usb", NULL);
+LPSC_CLKDEV1(ide_clkdev, NULL, "palm_bk3710");
+LPSC_CLKDEV1(aemif_clkdev, "aemif", NULL);
+LPSC_CLKDEV1(mmcsd_clkdev, NULL, "dm6441-mmc.0");
+LPSC_CLKDEV1(asp0_clkdev, NULL, "davinci-mcbsp");
+LPSC_CLKDEV1(i2c_clkdev, NULL, "i2c_davinci.1");
+LPSC_CLKDEV1(uart0_clkdev, NULL, "serial8250.0");
+LPSC_CLKDEV1(uart1_clkdev, NULL, "serial8250.1");
+LPSC_CLKDEV1(uart2_clkdev, NULL, "serial8250.2");
+/* REVISIT: gpio-davinci.c should be modified to drop con_id */
+LPSC_CLKDEV1(gpio_clkdev, "gpio", NULL);
+LPSC_CLKDEV1(timer0_clkdev, "timer0", NULL);
+LPSC_CLKDEV1(timer2_clkdev, NULL, "davinci-wdt");
+
+static const struct davinci_lpsc_clk_info dm644x_psc_info[] = {
+ LPSC(0, 0, vpss_master, pll1_sysclk3, vpss_master_clkdev, 0),
+ LPSC(1, 0, vpss_slave, pll1_sysclk3, vpss_slave_clkdev, 0),
+ LPSC(6, 0, emac, pll1_sysclk5, emac_clkdev, 0),
+ LPSC(9, 0, usb, pll1_sysclk5, usb_clkdev, 0),
+ LPSC(10, 0, ide, pll1_sysclk5, ide_clkdev, 0),
+ LPSC(11, 0, vlynq, pll1_sysclk5, NULL, 0),
+ LPSC(14, 0, aemif, pll1_sysclk5, aemif_clkdev, 0),
+ LPSC(15, 0, mmcsd, pll1_sysclk5, mmcsd_clkdev, 0),
+ LPSC(17, 0, asp0, pll1_sysclk5, asp0_clkdev, 0),
+ LPSC(18, 0, i2c, pll1_auxclk, i2c_clkdev, 0),
+ LPSC(19, 0, uart0, pll1_auxclk, uart0_clkdev, 0),
+ LPSC(20, 0, uart1, pll1_auxclk, uart1_clkdev, 0),
+ LPSC(21, 0, uart2, pll1_auxclk, uart2_clkdev, 0),
+ LPSC(22, 0, spi, pll1_sysclk5, NULL, 0),
+ LPSC(23, 0, pwm0, pll1_auxclk, NULL, 0),
+ LPSC(24, 0, pwm1, pll1_auxclk, NULL, 0),
+ LPSC(25, 0, pwm2, pll1_auxclk, NULL, 0),
+ LPSC(26, 0, gpio, pll1_sysclk5, gpio_clkdev, 0),
+ LPSC(27, 0, timer0, pll1_auxclk, timer0_clkdev, LPSC_ALWAYS_ENABLED),
+ LPSC(28, 0, timer1, pll1_auxclk, NULL, 0),
+ /* REVISIT: why can't this be disabled? */
+ LPSC(29, 0, timer2, pll1_auxclk, timer2_clkdev, LPSC_ALWAYS_ENABLED),
+ LPSC(31, 0, arm, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
+ /* REVISIT how to disable? */
+ LPSC(39, 1, dsp, pll1_sysclk1, NULL, LPSC_ALWAYS_ENABLED),
+ /* REVISIT how to disable? */
+ LPSC(40, 1, vicp, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
+ { }
+};
+
+static int dm644x_psc_init(struct device *dev, void __iomem *base)
+{
+ return davinci_psc_register_clocks(dev, dm644x_psc_info, 41, base);
+}
+
+static struct clk_bulk_data dm644x_psc_parent_clks[] = {
+ { .id = "pll1_sysclk1" },
+ { .id = "pll1_sysclk2" },
+ { .id = "pll1_sysclk3" },
+ { .id = "pll1_sysclk5" },
+ { .id = "pll1_auxclk" },
+};
+
+const struct davinci_psc_init_data dm644x_psc_init_data = {
+ .parent_clks = dm644x_psc_parent_clks,
+ .num_parent_clks = ARRAY_SIZE(dm644x_psc_parent_clks),
+ .psc_init = &dm644x_psc_init,
+};
diff --git a/drivers/clk/davinci/psc-dm646x.c b/drivers/clk/davinci/psc-dm646x.c
new file mode 100644
index 000000000000..468ef86ea40b
--- /dev/null
+++ b/drivers/clk/davinci/psc-dm646x.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PSC clock descriptions for TI DaVinci DM646x
+ *
+ * Copyright (C) 2018 David Lechner <david@lechnology.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+#include "psc.h"
+
+LPSC_CLKDEV1(ide_clkdev, NULL, "palm_bk3710");
+LPSC_CLKDEV2(emac_clkdev, NULL, "davinci_emac.1",
+ "fck", "davinci_mdio.0");
+LPSC_CLKDEV1(aemif_clkdev, "aemif", NULL);
+LPSC_CLKDEV1(mcasp0_clkdev, NULL, "davinci-mcasp.0");
+LPSC_CLKDEV1(mcasp1_clkdev, NULL, "davinci-mcasp.1");
+LPSC_CLKDEV1(uart0_clkdev, NULL, "serial8250.0");
+LPSC_CLKDEV1(uart1_clkdev, NULL, "serial8250.1");
+LPSC_CLKDEV1(uart2_clkdev, NULL, "serial8250.2");
+LPSC_CLKDEV1(i2c_clkdev, NULL, "i2c_davinci.1");
+/* REVISIT: gpio-davinci.c should be modified to drop con_id */
+LPSC_CLKDEV1(gpio_clkdev, "gpio", NULL);
+LPSC_CLKDEV1(timer0_clkdev, "timer0", NULL);
+
+static const struct davinci_lpsc_clk_info dm646x_psc_info[] = {
+ LPSC(0, 0, arm, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
+ /* REVISIT how to disable? */
+ LPSC(1, 0, dsp, pll1_sysclk1, NULL, LPSC_ALWAYS_ENABLED),
+ LPSC(4, 0, edma_cc, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
+ LPSC(5, 0, edma_tc0, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
+ LPSC(6, 0, edma_tc1, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
+ LPSC(7, 0, edma_tc2, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
+ LPSC(8, 0, edma_tc3, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
+ LPSC(10, 0, ide, pll1_sysclk4, ide_clkdev, 0),
+ LPSC(14, 0, emac, pll1_sysclk3, emac_clkdev, 0),
+ LPSC(16, 0, vpif0, ref_clk, NULL, LPSC_ALWAYS_ENABLED),
+ LPSC(17, 0, vpif1, ref_clk, NULL, LPSC_ALWAYS_ENABLED),
+ LPSC(21, 0, aemif, pll1_sysclk3, aemif_clkdev, LPSC_ALWAYS_ENABLED),
+ LPSC(22, 0, mcasp0, pll1_sysclk3, mcasp0_clkdev, 0),
+ LPSC(23, 0, mcasp1, pll1_sysclk3, mcasp1_clkdev, 0),
+ LPSC(26, 0, uart0, aux_clkin, uart0_clkdev, 0),
+ LPSC(27, 0, uart1, aux_clkin, uart1_clkdev, 0),
+ LPSC(28, 0, uart2, aux_clkin, uart2_clkdev, 0),
+ /* REVIST: disabling hangs system */
+ LPSC(29, 0, pwm0, pll1_sysclk3, NULL, LPSC_ALWAYS_ENABLED),
+ /* REVIST: disabling hangs system */
+ LPSC(30, 0, pwm1, pll1_sysclk3, NULL, LPSC_ALWAYS_ENABLED),
+ LPSC(31, 0, i2c, pll1_sysclk3, i2c_clkdev, 0),
+ LPSC(33, 0, gpio, pll1_sysclk3, gpio_clkdev, 0),
+ LPSC(34, 0, timer0, pll1_sysclk3, timer0_clkdev, LPSC_ALWAYS_ENABLED),
+ LPSC(35, 0, timer1, pll1_sysclk3, NULL, 0),
+ { }
+};
+
+static int dm646x_psc_init(struct device *dev, void __iomem *base)
+{
+ return davinci_psc_register_clocks(dev, dm646x_psc_info, 46, base);
+}
+
+static struct clk_bulk_data dm646x_psc_parent_clks[] = {
+ { .id = "ref_clk" },
+ { .id = "aux_clkin" },
+ { .id = "pll1_sysclk1" },
+ { .id = "pll1_sysclk2" },
+ { .id = "pll1_sysclk3" },
+ { .id = "pll1_sysclk4" },
+ { .id = "pll1_sysclk5" },
+};
+
+const struct davinci_psc_init_data dm646x_psc_init_data = {
+ .parent_clks = dm646x_psc_parent_clks,
+ .num_parent_clks = ARRAY_SIZE(dm646x_psc_parent_clks),
+ .psc_init = &dm646x_psc_init,
+};
diff --git a/drivers/clk/davinci/psc.c b/drivers/clk/davinci/psc.c
new file mode 100644
index 000000000000..ce170e600f09
--- /dev/null
+++ b/drivers/clk/davinci/psc.c
@@ -0,0 +1,551 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Clock driver for TI Davinci PSC controllers
+ *
+ * Copyright (C) 2017 David Lechner <david@lechnology.com>
+ *
+ * Based on: drivers/clk/keystone/gate.c
+ * Copyright (C) 2013 Texas Instruments.
+ * Murali Karicheri <m-karicheri2@ti.com>
+ * Santosh Shilimkar <santosh.shilimkar@ti.com>
+ *
+ * And: arch/arm/mach-davinci/psc.c
+ * Copyright (C) 2006 Texas Instruments.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/err.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_clock.h>
+#include <linux/pm_domain.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "psc.h"
+
+/* PSC register offsets */
+#define EPCPR 0x070
+#define PTCMD 0x120
+#define PTSTAT 0x128
+#define PDSTAT(n) (0x200 + 4 * (n))
+#define PDCTL(n) (0x300 + 4 * (n))
+#define MDSTAT(n) (0x800 + 4 * (n))
+#define MDCTL(n) (0xa00 + 4 * (n))
+
+/* PSC module states */
+enum davinci_lpsc_state {
+ LPSC_STATE_SWRSTDISABLE = 0,
+ LPSC_STATE_SYNCRST = 1,
+ LPSC_STATE_DISABLE = 2,
+ LPSC_STATE_ENABLE = 3,
+};
+
+#define MDSTAT_STATE_MASK GENMASK(5, 0)
+#define MDSTAT_MCKOUT BIT(12)
+#define PDSTAT_STATE_MASK GENMASK(4, 0)
+#define MDCTL_FORCE BIT(31)
+#define MDCTL_LRESET BIT(8)
+#define PDCTL_EPCGOOD BIT(8)
+#define PDCTL_NEXT BIT(0)
+
+struct davinci_psc_data {
+ struct clk_onecell_data clk_data;
+ struct genpd_onecell_data pm_data;
+ struct reset_controller_dev rcdev;
+};
+
+/**
+ * struct davinci_lpsc_clk - LPSC clock structure
+ * @dev: the device that provides this LPSC
+ * @hw: clk_hw for the LPSC
+ * @pm_domain: power domain for the LPSC
+ * @genpd_clk: clock reference owned by @pm_domain
+ * @regmap: PSC MMIO region
+ * @md: Module domain (LPSC module id)
+ * @pd: Power domain
+ * @flags: LPSC_* quirk flags
+ */
+struct davinci_lpsc_clk {
+ struct device *dev;
+ struct clk_hw hw;
+ struct generic_pm_domain pm_domain;
+ struct clk *genpd_clk;
+ struct regmap *regmap;
+ u32 md;
+ u32 pd;
+ u32 flags;
+};
+
+#define to_davinci_psc_data(x) container_of(x, struct davinci_psc_data, x)
+#define to_davinci_lpsc_clk(x) container_of(x, struct davinci_lpsc_clk, x)
+
+/**
+ * best_dev_name - get the "best" device name.
+ * @dev: the device
+ *
+ * Returns the device tree compatible name if the device has a DT node,
+ * otherwise return the device name. This is mainly needed because clkdev
+ * lookups are limited to 20 chars for dev_id and when using device tree,
+ * dev_name(dev) is much longer than that.
+ */
+static inline const char *best_dev_name(struct device *dev)
+{
+ const char *compatible;
+
+ if (!of_property_read_string(dev->of_node, "compatible", &compatible))
+ return compatible;
+
+ return dev_name(dev);
+}
+
+static void davinci_lpsc_config(struct davinci_lpsc_clk *lpsc,
+ enum davinci_lpsc_state next_state)
+{
+ u32 epcpr, pdstat, mdstat, ptstat;
+
+ regmap_write_bits(lpsc->regmap, MDCTL(lpsc->md), MDSTAT_STATE_MASK,
+ next_state);
+
+ if (lpsc->flags & LPSC_FORCE)
+ regmap_write_bits(lpsc->regmap, MDCTL(lpsc->md), MDCTL_FORCE,
+ MDCTL_FORCE);
+
+ regmap_read(lpsc->regmap, PDSTAT(lpsc->pd), &pdstat);
+ if ((pdstat & PDSTAT_STATE_MASK) == 0) {
+ regmap_write_bits(lpsc->regmap, PDCTL(lpsc->pd), PDCTL_NEXT,
+ PDCTL_NEXT);
+
+ regmap_write(lpsc->regmap, PTCMD, BIT(lpsc->pd));
+
+ regmap_read_poll_timeout(lpsc->regmap, EPCPR, epcpr,
+ epcpr & BIT(lpsc->pd), 0, 0);
+
+ regmap_write_bits(lpsc->regmap, PDCTL(lpsc->pd), PDCTL_EPCGOOD,
+ PDCTL_EPCGOOD);
+ } else {
+ regmap_write(lpsc->regmap, PTCMD, BIT(lpsc->pd));
+ }
+
+ regmap_read_poll_timeout(lpsc->regmap, PTSTAT, ptstat,
+ !(ptstat & BIT(lpsc->pd)), 0, 0);
+
+ regmap_read_poll_timeout(lpsc->regmap, MDSTAT(lpsc->md), mdstat,
+ (mdstat & MDSTAT_STATE_MASK) == next_state,
+ 0, 0);
+}
+
+static int davinci_lpsc_clk_enable(struct clk_hw *hw)
+{
+ struct davinci_lpsc_clk *lpsc = to_davinci_lpsc_clk(hw);
+
+ davinci_lpsc_config(lpsc, LPSC_STATE_ENABLE);
+
+ return 0;
+}
+
+static void davinci_lpsc_clk_disable(struct clk_hw *hw)
+{
+ struct davinci_lpsc_clk *lpsc = to_davinci_lpsc_clk(hw);
+
+ davinci_lpsc_config(lpsc, LPSC_STATE_DISABLE);
+}
+
+static int davinci_lpsc_clk_is_enabled(struct clk_hw *hw)
+{
+ struct davinci_lpsc_clk *lpsc = to_davinci_lpsc_clk(hw);
+ u32 mdstat;
+
+ regmap_read(lpsc->regmap, MDSTAT(lpsc->md), &mdstat);
+
+ return (mdstat & MDSTAT_MCKOUT) ? 1 : 0;
+}
+
+static const struct clk_ops davinci_lpsc_clk_ops = {
+ .enable = davinci_lpsc_clk_enable,
+ .disable = davinci_lpsc_clk_disable,
+ .is_enabled = davinci_lpsc_clk_is_enabled,
+};
+
+static int davinci_psc_genpd_attach_dev(struct generic_pm_domain *pm_domain,
+ struct device *dev)
+{
+ struct davinci_lpsc_clk *lpsc = to_davinci_lpsc_clk(pm_domain);
+ struct clk *clk;
+ int ret;
+
+ /*
+ * pm_clk_remove_clk() will call clk_put(), so we have to use clk_get()
+ * to get the clock instead of using lpsc->hw.clk directly.
+ */
+ clk = clk_get_sys(best_dev_name(lpsc->dev), clk_hw_get_name(&lpsc->hw));
+ if (IS_ERR(clk))
+ return (PTR_ERR(clk));
+
+ ret = pm_clk_create(dev);
+ if (ret < 0)
+ goto fail_clk_put;
+
+ ret = pm_clk_add_clk(dev, clk);
+ if (ret < 0)
+ goto fail_pm_clk_destroy;
+
+ lpsc->genpd_clk = clk;
+
+ return 0;
+
+fail_pm_clk_destroy:
+ pm_clk_destroy(dev);
+fail_clk_put:
+ clk_put(clk);
+
+ return ret;
+}
+
+static void davinci_psc_genpd_detach_dev(struct generic_pm_domain *pm_domain,
+ struct device *dev)
+{
+ struct davinci_lpsc_clk *lpsc = to_davinci_lpsc_clk(pm_domain);
+
+ pm_clk_remove_clk(dev, lpsc->genpd_clk);
+ pm_clk_destroy(dev);
+
+ lpsc->genpd_clk = NULL;
+}
+
+/**
+ * davinci_lpsc_clk_register - register LPSC clock
+ * @name: name of this clock
+ * @parent_name: name of clock's parent
+ * @regmap: PSC MMIO region
+ * @md: local PSC number
+ * @pd: power domain
+ * @flags: LPSC_* flags
+ */
+static struct davinci_lpsc_clk *
+davinci_lpsc_clk_register(struct device *dev, const char *name,
+ const char *parent_name, struct regmap *regmap,
+ u32 md, u32 pd, u32 flags)
+{
+ struct clk_init_data init;
+ struct davinci_lpsc_clk *lpsc;
+ int ret;
+ bool is_on;
+
+ lpsc = devm_kzalloc(dev, sizeof(*lpsc), GFP_KERNEL);
+ if (!lpsc)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &davinci_lpsc_clk_ops;
+ init.parent_names = (parent_name ? &parent_name : NULL);
+ init.num_parents = (parent_name ? 1 : 0);
+ init.flags = 0;
+
+ if (flags & LPSC_ALWAYS_ENABLED)
+ init.flags |= CLK_IS_CRITICAL;
+
+ if (flags & LPSC_SET_RATE_PARENT)
+ init.flags |= CLK_SET_RATE_PARENT;
+
+ lpsc->dev = dev;
+ lpsc->regmap = regmap;
+ lpsc->hw.init = &init;
+ lpsc->md = md;
+ lpsc->pd = pd;
+ lpsc->flags = flags;
+
+ ret = devm_clk_hw_register(dev, &lpsc->hw);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ /* genpd attach needs a way to look up this clock */
+ ret = clk_hw_register_clkdev(&lpsc->hw, name, best_dev_name(dev));
+
+ lpsc->pm_domain.name = devm_kasprintf(dev, GFP_KERNEL, "%s: %s",
+ best_dev_name(dev), name);
+ lpsc->pm_domain.attach_dev = davinci_psc_genpd_attach_dev;
+ lpsc->pm_domain.detach_dev = davinci_psc_genpd_detach_dev;
+ lpsc->pm_domain.flags = GENPD_FLAG_PM_CLK;
+
+ is_on = davinci_lpsc_clk_is_enabled(&lpsc->hw);
+ pm_genpd_init(&lpsc->pm_domain, NULL, is_on);
+
+ return lpsc;
+}
+
+static int davinci_lpsc_clk_reset(struct clk *clk, bool reset)
+{
+ struct clk_hw *hw = __clk_get_hw(clk);
+ struct davinci_lpsc_clk *lpsc = to_davinci_lpsc_clk(hw);
+ u32 mdctl;
+
+ if (IS_ERR_OR_NULL(lpsc))
+ return -EINVAL;
+
+ mdctl = reset ? 0 : MDCTL_LRESET;
+ regmap_write_bits(lpsc->regmap, MDCTL(lpsc->md), MDCTL_LRESET, mdctl);
+
+ return 0;
+}
+
+/*
+ * REVISIT: These exported functions can be removed after a non-DT lookup is
+ * added to the reset controller framework and the davinci-rproc driver is
+ * updated to use the generic reset controller framework.
+ */
+
+int davinci_clk_reset_assert(struct clk *clk)
+{
+ return davinci_lpsc_clk_reset(clk, true);
+}
+EXPORT_SYMBOL(davinci_clk_reset_assert);
+
+int davinci_clk_reset_deassert(struct clk *clk)
+{
+ return davinci_lpsc_clk_reset(clk, false);
+}
+EXPORT_SYMBOL(davinci_clk_reset_deassert);
+
+static int davinci_psc_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct davinci_psc_data *psc = to_davinci_psc_data(rcdev);
+ struct clk *clk = psc->clk_data.clks[id];
+
+ return davinci_lpsc_clk_reset(clk, true);
+}
+
+static int davinci_psc_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct davinci_psc_data *psc = to_davinci_psc_data(rcdev);
+ struct clk *clk = psc->clk_data.clks[id];
+
+ return davinci_lpsc_clk_reset(clk, false);
+}
+
+static const struct reset_control_ops davinci_psc_reset_ops = {
+ .assert = davinci_psc_reset_assert,
+ .deassert = davinci_psc_reset_deassert,
+};
+
+static int davinci_psc_reset_of_xlate(struct reset_controller_dev *rcdev,
+ const struct of_phandle_args *reset_spec)
+{
+ struct of_phandle_args clkspec = *reset_spec; /* discard const qualifier */
+ struct clk *clk;
+ struct clk_hw *hw;
+ struct davinci_lpsc_clk *lpsc;
+
+ /* the clock node is the same as the reset node */
+ clk = of_clk_get_from_provider(&clkspec);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ hw = __clk_get_hw(clk);
+ lpsc = to_davinci_lpsc_clk(hw);
+ clk_put(clk);
+
+ /* not all modules support local reset */
+ if (!(lpsc->flags & LPSC_LOCAL_RESET))
+ return -EINVAL;
+
+ return lpsc->md;
+}
+
+static const struct regmap_config davinci_psc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+};
+
+static struct davinci_psc_data *
+__davinci_psc_register_clocks(struct device *dev,
+ const struct davinci_lpsc_clk_info *info,
+ int num_clks,
+ void __iomem *base)
+{
+ struct davinci_psc_data *psc;
+ struct clk **clks;
+ struct generic_pm_domain **pm_domains;
+ struct regmap *regmap;
+ int i, ret;
+
+ psc = devm_kzalloc(dev, sizeof(*psc), GFP_KERNEL);
+ if (!psc)
+ return ERR_PTR(-ENOMEM);
+
+ clks = devm_kmalloc_array(dev, num_clks, sizeof(*clks), GFP_KERNEL);
+ if (!clks)
+ return ERR_PTR(-ENOMEM);
+
+ psc->clk_data.clks = clks;
+ psc->clk_data.clk_num = num_clks;
+
+ /*
+ * init array with error so that of_clk_src_onecell_get() doesn't
+ * return NULL for gaps in the sparse array
+ */
+ for (i = 0; i < num_clks; i++)
+ clks[i] = ERR_PTR(-ENOENT);
+
+ pm_domains = devm_kcalloc(dev, num_clks, sizeof(*pm_domains), GFP_KERNEL);
+ if (!pm_domains)
+ return ERR_PTR(-ENOMEM);
+
+ psc->pm_data.domains = pm_domains;
+ psc->pm_data.num_domains = num_clks;
+
+ regmap = devm_regmap_init_mmio(dev, base, &davinci_psc_regmap_config);
+ if (IS_ERR(regmap))
+ return ERR_CAST(regmap);
+
+ for (; info->name; info++) {
+ struct davinci_lpsc_clk *lpsc;
+
+ lpsc = davinci_lpsc_clk_register(dev, info->name, info->parent,
+ regmap, info->md, info->pd,
+ info->flags);
+ if (IS_ERR(lpsc)) {
+ dev_warn(dev, "Failed to register %s (%ld)\n",
+ info->name, PTR_ERR(lpsc));
+ continue;
+ }
+
+ clks[info->md] = lpsc->hw.clk;
+ pm_domains[info->md] = &lpsc->pm_domain;
+ }
+
+ psc->rcdev.ops = &davinci_psc_reset_ops;
+ psc->rcdev.owner = THIS_MODULE;
+ psc->rcdev.dev = dev;
+ psc->rcdev.of_node = dev->of_node;
+ psc->rcdev.of_reset_n_cells = 1;
+ psc->rcdev.of_xlate = davinci_psc_reset_of_xlate;
+ psc->rcdev.nr_resets = num_clks;
+
+ ret = devm_reset_controller_register(dev, &psc->rcdev);
+ if (ret < 0)
+ dev_warn(dev, "Failed to register reset controller (%d)\n", ret);
+
+ return psc;
+}
+
+int davinci_psc_register_clocks(struct device *dev,
+ const struct davinci_lpsc_clk_info *info,
+ u8 num_clks,
+ void __iomem *base)
+{
+ struct davinci_psc_data *psc;
+
+ psc = __davinci_psc_register_clocks(dev, info, num_clks, base);
+ if (IS_ERR(psc))
+ return PTR_ERR(psc);
+
+ for (; info->name; info++) {
+ const struct davinci_lpsc_clkdev_info *cdevs = info->cdevs;
+ struct clk *clk = psc->clk_data.clks[info->md];
+
+ if (!cdevs || IS_ERR_OR_NULL(clk))
+ continue;
+
+ for (; cdevs->con_id || cdevs->dev_id; cdevs++)
+ clk_register_clkdev(clk, cdevs->con_id, cdevs->dev_id);
+ }
+
+ return 0;
+}
+
+int of_davinci_psc_clk_init(struct device *dev,
+ const struct davinci_lpsc_clk_info *info,
+ u8 num_clks,
+ void __iomem *base)
+{
+ struct device_node *node = dev->of_node;
+ struct davinci_psc_data *psc;
+
+ psc = __davinci_psc_register_clocks(dev, info, num_clks, base);
+ if (IS_ERR(psc))
+ return PTR_ERR(psc);
+
+ of_genpd_add_provider_onecell(node, &psc->pm_data);
+
+ of_clk_add_provider(node, of_clk_src_onecell_get, &psc->clk_data);
+
+ return 0;
+}
+
+static const struct of_device_id davinci_psc_of_match[] = {
+ { .compatible = "ti,da850-psc0", .data = &of_da850_psc0_init_data },
+ { .compatible = "ti,da850-psc1", .data = &of_da850_psc1_init_data },
+ { }
+};
+
+static const struct platform_device_id davinci_psc_id_table[] = {
+ { .name = "da830-psc0", .driver_data = (kernel_ulong_t)&da830_psc0_init_data },
+ { .name = "da830-psc1", .driver_data = (kernel_ulong_t)&da830_psc1_init_data },
+ { .name = "da850-psc0", .driver_data = (kernel_ulong_t)&da850_psc0_init_data },
+ { .name = "da850-psc1", .driver_data = (kernel_ulong_t)&da850_psc1_init_data },
+ { .name = "dm355-psc", .driver_data = (kernel_ulong_t)&dm355_psc_init_data },
+ { .name = "dm365-psc", .driver_data = (kernel_ulong_t)&dm365_psc_init_data },
+ { .name = "dm644x-psc", .driver_data = (kernel_ulong_t)&dm644x_psc_init_data },
+ { .name = "dm646x-psc", .driver_data = (kernel_ulong_t)&dm646x_psc_init_data },
+ { }
+};
+
+static int davinci_psc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct of_device_id *of_id;
+ const struct davinci_psc_init_data *init_data = NULL;
+ struct resource *res;
+ void __iomem *base;
+ int ret;
+
+ of_id = of_match_device(davinci_psc_of_match, dev);
+ if (of_id)
+ init_data = of_id->data;
+ else if (pdev->id_entry)
+ init_data = (void *)pdev->id_entry->driver_data;
+
+ if (!init_data) {
+ dev_err(dev, "unable to find driver init data\n");
+ return -EINVAL;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ ret = devm_clk_bulk_get(dev, init_data->num_parent_clks,
+ init_data->parent_clks);
+ if (ret < 0)
+ return ret;
+
+ return init_data->psc_init(dev, base);
+}
+
+static struct platform_driver davinci_psc_driver = {
+ .probe = davinci_psc_probe,
+ .driver = {
+ .name = "davinci-psc-clk",
+ .of_match_table = davinci_psc_of_match,
+ },
+ .id_table = davinci_psc_id_table,
+};
+
+static int __init davinci_psc_driver_init(void)
+{
+ return platform_driver_register(&davinci_psc_driver);
+}
+
+/* has to be postcore_initcall because davinci_gpio depend on PSC clocks */
+postcore_initcall(davinci_psc_driver_init);
diff --git a/drivers/clk/davinci/psc.h b/drivers/clk/davinci/psc.h
new file mode 100644
index 000000000000..c2a7df6413fe
--- /dev/null
+++ b/drivers/clk/davinci/psc.h
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Clock driver for TI Davinci PSC controllers
+ *
+ * Copyright (C) 2018 David Lechner <david@lechnology.com>
+ */
+
+#ifndef __CLK_DAVINCI_PSC_H__
+#define __CLK_DAVINCI_PSC_H__
+
+#include <linux/clk-provider.h>
+#include <linux/types.h>
+
+/* PSC quirk flags */
+#define LPSC_ALWAYS_ENABLED BIT(0) /* never disable this clock */
+#define LPSC_SET_RATE_PARENT BIT(1) /* propagate set_rate to parent clock */
+#define LPSC_FORCE BIT(2) /* requires MDCTL FORCE bit */
+#define LPSC_LOCAL_RESET BIT(3) /* acts as reset provider */
+
+struct davinci_lpsc_clkdev_info {
+ const char *con_id;
+ const char *dev_id;
+};
+
+#define LPSC_CLKDEV(c, d) { \
+ .con_id = (c), \
+ .dev_id = (d) \
+}
+
+#define LPSC_CLKDEV1(n, c, d) \
+static const struct davinci_lpsc_clkdev_info n[] __initconst = { \
+ LPSC_CLKDEV((c), (d)), \
+ { } \
+}
+
+#define LPSC_CLKDEV2(n, c1, d1, c2, d2) \
+static const struct davinci_lpsc_clkdev_info n[] __initconst = { \
+ LPSC_CLKDEV((c1), (d1)), \
+ LPSC_CLKDEV((c2), (d2)), \
+ { } \
+}
+
+#define LPSC_CLKDEV3(n, c1, d1, c2, d2, c3, d3) \
+static const struct davinci_lpsc_clkdev_info n[] __initconst = { \
+ LPSC_CLKDEV((c1), (d1)), \
+ LPSC_CLKDEV((c2), (d2)), \
+ LPSC_CLKDEV((c3), (d3)), \
+ { } \
+}
+
+/**
+ * davinci_lpsc_clk_info - LPSC module-specific clock information
+ * @name: the clock name
+ * @parent: the parent clock name
+ * @cdevs: optional array of clkdev lookup table info
+ * @md: the local module domain (LPSC id)
+ * @pd: the power domain id
+ * @flags: bitmask of LPSC_* flags
+ */
+struct davinci_lpsc_clk_info {
+ const char *name;
+ const char *parent;
+ const struct davinci_lpsc_clkdev_info *cdevs;
+ u32 md;
+ u32 pd;
+ unsigned long flags;
+};
+
+#define LPSC(m, d, n, p, c, f) \
+{ \
+ .name = #n, \
+ .parent = #p, \
+ .cdevs = (c), \
+ .md = (m), \
+ .pd = (d), \
+ .flags = (f), \
+}
+
+int davinci_psc_register_clocks(struct device *dev,
+ const struct davinci_lpsc_clk_info *info,
+ u8 num_clks,
+ void __iomem *base);
+
+int of_davinci_psc_clk_init(struct device *dev,
+ const struct davinci_lpsc_clk_info *info,
+ u8 num_clks,
+ void __iomem *base);
+
+/* Device-specific data */
+
+struct davinci_psc_init_data {
+ struct clk_bulk_data *parent_clks;
+ int num_parent_clks;
+ int (*psc_init)(struct device *dev, void __iomem *base);
+};
+
+extern const struct davinci_psc_init_data da830_psc0_init_data;
+extern const struct davinci_psc_init_data da830_psc1_init_data;
+extern const struct davinci_psc_init_data da850_psc0_init_data;
+extern const struct davinci_psc_init_data da850_psc1_init_data;
+extern const struct davinci_psc_init_data of_da850_psc0_init_data;
+extern const struct davinci_psc_init_data of_da850_psc1_init_data;
+extern const struct davinci_psc_init_data dm355_psc_init_data;
+extern const struct davinci_psc_init_data dm365_psc_init_data;
+extern const struct davinci_psc_init_data dm644x_psc_init_data;
+extern const struct davinci_psc_init_data dm646x_psc_init_data;
+
+#endif /* __CLK_DAVINCI_PSC_H__ */
diff --git a/drivers/clk/hisilicon/Makefile b/drivers/clk/hisilicon/Makefile
index 4806fc2cb4ac..2a714c0f9657 100644
--- a/drivers/clk/hisilicon/Makefile
+++ b/drivers/clk/hisilicon/Makefile
@@ -3,7 +3,7 @@
# Hisilicon Clock specific Makefile
#
-obj-y += clk.o clkgate-separated.o clkdivider-hi6220.o
+obj-y += clk.o clkgate-separated.o clkdivider-hi6220.o clk-hisi-phase.o
obj-$(CONFIG_ARCH_HI3xxx) += clk-hi3620.o
obj-$(CONFIG_ARCH_HIP04) += clk-hip04.o
diff --git a/drivers/clk/hisilicon/clk-hisi-phase.c b/drivers/clk/hisilicon/clk-hisi-phase.c
new file mode 100644
index 000000000000..5bce9297b78b
--- /dev/null
+++ b/drivers/clk/hisilicon/clk-hisi-phase.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2017 HiSilicon Technologies Co., Ltd.
+ *
+ * Simple HiSilicon phase clock implementation.
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "clk.h"
+
+struct clk_hisi_phase {
+ struct clk_hw hw;
+ void __iomem *reg;
+ u32 *phase_degrees;
+ u32 *phase_regvals;
+ u8 phase_num;
+ u32 mask;
+ u8 shift;
+ u8 flags;
+ spinlock_t *lock;
+};
+
+#define to_clk_hisi_phase(_hw) container_of(_hw, struct clk_hisi_phase, hw)
+
+static int hisi_phase_regval_to_degrees(struct clk_hisi_phase *phase,
+ u32 regval)
+{
+ int i;
+
+ for (i = 0; i < phase->phase_num; i++)
+ if (phase->phase_regvals[i] == regval)
+ return phase->phase_degrees[i];
+
+ return -EINVAL;
+}
+
+static int hisi_clk_get_phase(struct clk_hw *hw)
+{
+ struct clk_hisi_phase *phase = to_clk_hisi_phase(hw);
+ u32 regval;
+
+ regval = readl(phase->reg);
+ regval = (regval & phase->mask) >> phase->shift;
+
+ return hisi_phase_regval_to_degrees(phase, regval);
+}
+
+static int hisi_phase_degrees_to_regval(struct clk_hisi_phase *phase,
+ int degrees)
+{
+ int i;
+
+ for (i = 0; i < phase->phase_num; i++)
+ if (phase->phase_degrees[i] == degrees)
+ return phase->phase_regvals[i];
+
+ return -EINVAL;
+}
+
+static int hisi_clk_set_phase(struct clk_hw *hw, int degrees)
+{
+ struct clk_hisi_phase *phase = to_clk_hisi_phase(hw);
+ unsigned long flags = 0;
+ int regval;
+ u32 val;
+
+ regval = hisi_phase_degrees_to_regval(phase, degrees);
+ if (regval < 0)
+ return regval;
+
+ spin_lock_irqsave(phase->lock, flags);
+
+ val = clk_readl(phase->reg);
+ val &= ~phase->mask;
+ val |= regval << phase->shift;
+ clk_writel(val, phase->reg);
+
+ spin_unlock_irqrestore(phase->lock, flags);
+
+ return 0;
+}
+
+static const struct clk_ops clk_phase_ops = {
+ .get_phase = hisi_clk_get_phase,
+ .set_phase = hisi_clk_set_phase,
+};
+
+struct clk *clk_register_hisi_phase(struct device *dev,
+ const struct hisi_phase_clock *clks,
+ void __iomem *base, spinlock_t *lock)
+{
+ struct clk_hisi_phase *phase;
+ struct clk_init_data init;
+
+ phase = devm_kzalloc(dev, sizeof(struct clk_hisi_phase), GFP_KERNEL);
+ if (!phase)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = clks->name;
+ init.ops = &clk_phase_ops;
+ init.flags = clks->flags | CLK_IS_BASIC;
+ init.parent_names = clks->parent_names ? &clks->parent_names : NULL;
+ init.num_parents = clks->parent_names ? 1 : 0;
+
+ phase->reg = base + clks->offset;
+ phase->shift = clks->shift;
+ phase->mask = (BIT(clks->width) - 1) << clks->shift;
+ phase->lock = lock;
+ phase->phase_degrees = clks->phase_degrees;
+ phase->phase_regvals = clks->phase_regvals;
+ phase->phase_num = clks->phase_num;
+ phase->hw.init = &init;
+
+ return devm_clk_register(dev, &phase->hw);
+}
+EXPORT_SYMBOL_GPL(clk_register_hisi_phase);
diff --git a/drivers/clk/hisilicon/clk.c b/drivers/clk/hisilicon/clk.c
index b73c1dfae7f1..953c8dacef8b 100644
--- a/drivers/clk/hisilicon/clk.c
+++ b/drivers/clk/hisilicon/clk.c
@@ -49,6 +49,8 @@ struct hisi_clock_data *hisi_clk_alloc(struct platform_device *pdev,
return NULL;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return NULL;
clk_data->base = devm_ioremap(&pdev->dev,
res->start, resource_size(res));
if (!clk_data->base)
@@ -197,6 +199,30 @@ err:
}
EXPORT_SYMBOL_GPL(hisi_clk_register_mux);
+int hisi_clk_register_phase(struct device *dev,
+ const struct hisi_phase_clock *clks,
+ int nums, struct hisi_clock_data *data)
+{
+ void __iomem *base = data->base;
+ struct clk *clk;
+ int i;
+
+ for (i = 0; i < nums; i++) {
+ clk = clk_register_hisi_phase(dev, &clks[i], base,
+ &hisi_clk_lock);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to register clock %s\n", __func__,
+ clks[i].name);
+ return PTR_ERR(clk);
+ }
+
+ data->clk_data.clks[clks[i].id] = clk;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hisi_clk_register_phase);
+
int hisi_clk_register_divider(const struct hisi_divider_clock *clks,
int nums, struct hisi_clock_data *data)
{
diff --git a/drivers/clk/hisilicon/clk.h b/drivers/clk/hisilicon/clk.h
index 4e1d1affc6f5..8d7ee5c3231b 100644
--- a/drivers/clk/hisilicon/clk.h
+++ b/drivers/clk/hisilicon/clk.h
@@ -68,6 +68,19 @@ struct hisi_mux_clock {
const char *alias;
};
+struct hisi_phase_clock {
+ unsigned int id;
+ const char *name;
+ const char *parent_names;
+ unsigned long flags;
+ unsigned long offset;
+ u8 shift;
+ u8 width;
+ u32 *phase_degrees;
+ u32 *phase_regvals;
+ u8 phase_num;
+};
+
struct hisi_divider_clock {
unsigned int id;
const char *name;
@@ -120,6 +133,12 @@ int hisi_clk_register_fixed_factor(const struct hisi_fixed_factor_clock *,
int, struct hisi_clock_data *);
int hisi_clk_register_mux(const struct hisi_mux_clock *, int,
struct hisi_clock_data *);
+struct clk *clk_register_hisi_phase(struct device *dev,
+ const struct hisi_phase_clock *clks,
+ void __iomem *base, spinlock_t *lock);
+int hisi_clk_register_phase(struct device *dev,
+ const struct hisi_phase_clock *clks,
+ int nums, struct hisi_clock_data *data);
int hisi_clk_register_divider(const struct hisi_divider_clock *,
int, struct hisi_clock_data *);
int hisi_clk_register_gate(const struct hisi_gate_clock *,
diff --git a/drivers/clk/hisilicon/crg-hi3516cv300.c b/drivers/clk/hisilicon/crg-hi3516cv300.c
index 2007123832bb..53450b651e4c 100644
--- a/drivers/clk/hisilicon/crg-hi3516cv300.c
+++ b/drivers/clk/hisilicon/crg-hi3516cv300.c
@@ -204,7 +204,7 @@ static const struct hisi_crg_funcs hi3516cv300_crg_funcs = {
/* hi3516CV300 sysctrl CRG */
#define HI3516CV300_SYSCTRL_NR_CLKS 16
-static const char *wdt_mux_p[] __initconst = { "3m", "apb" };
+static const char *const wdt_mux_p[] __initconst = { "3m", "apb" };
static u32 wdt_mux_table[] = {0, 1};
static const struct hisi_mux_clock hi3516cv300_sysctrl_mux_clks[] = {
diff --git a/drivers/clk/hisilicon/crg-hi3798cv200.c b/drivers/clk/hisilicon/crg-hi3798cv200.c
index 8478948e858e..743eec131528 100644
--- a/drivers/clk/hisilicon/crg-hi3798cv200.c
+++ b/drivers/clk/hisilicon/crg-hi3798cv200.c
@@ -27,30 +27,31 @@
#include "reset.h"
/* hi3798CV200 core CRG */
-#define HI3798CV200_INNER_CLK_OFFSET 64
-#define HI3798CV200_FIXED_24M 65
-#define HI3798CV200_FIXED_25M 66
-#define HI3798CV200_FIXED_50M 67
-#define HI3798CV200_FIXED_75M 68
-#define HI3798CV200_FIXED_100M 69
-#define HI3798CV200_FIXED_150M 70
-#define HI3798CV200_FIXED_200M 71
-#define HI3798CV200_FIXED_250M 72
-#define HI3798CV200_FIXED_300M 73
-#define HI3798CV200_FIXED_400M 74
-#define HI3798CV200_MMC_MUX 75
-#define HI3798CV200_ETH_PUB_CLK 76
-#define HI3798CV200_ETH_BUS_CLK 77
-#define HI3798CV200_ETH_BUS0_CLK 78
-#define HI3798CV200_ETH_BUS1_CLK 79
-#define HI3798CV200_COMBPHY1_MUX 80
-#define HI3798CV200_FIXED_12M 81
-#define HI3798CV200_FIXED_48M 82
-#define HI3798CV200_FIXED_60M 83
-#define HI3798CV200_FIXED_166P5M 84
-#define HI3798CV200_SDIO0_MUX 85
-
-#define HI3798CV200_CRG_NR_CLKS 128
+#define HI3798CV200_INNER_CLK_OFFSET 64
+#define HI3798CV200_FIXED_24M 65
+#define HI3798CV200_FIXED_25M 66
+#define HI3798CV200_FIXED_50M 67
+#define HI3798CV200_FIXED_75M 68
+#define HI3798CV200_FIXED_100M 69
+#define HI3798CV200_FIXED_150M 70
+#define HI3798CV200_FIXED_200M 71
+#define HI3798CV200_FIXED_250M 72
+#define HI3798CV200_FIXED_300M 73
+#define HI3798CV200_FIXED_400M 74
+#define HI3798CV200_MMC_MUX 75
+#define HI3798CV200_ETH_PUB_CLK 76
+#define HI3798CV200_ETH_BUS_CLK 77
+#define HI3798CV200_ETH_BUS0_CLK 78
+#define HI3798CV200_ETH_BUS1_CLK 79
+#define HI3798CV200_COMBPHY1_MUX 80
+#define HI3798CV200_FIXED_12M 81
+#define HI3798CV200_FIXED_48M 82
+#define HI3798CV200_FIXED_60M 83
+#define HI3798CV200_FIXED_166P5M 84
+#define HI3798CV200_SDIO0_MUX 85
+#define HI3798CV200_COMBPHY0_MUX 86
+
+#define HI3798CV200_CRG_NR_CLKS 128
static const struct hisi_fixed_rate_clock hi3798cv200_fixed_rate_clks[] = {
{ HISTB_OSC_CLK, "clk_osc", NULL, 0, 24000000, },
@@ -74,9 +75,9 @@ static const char *const mmc_mux_p[] = {
"100m", "50m", "25m", "200m", "150m" };
static u32 mmc_mux_table[] = {0, 1, 2, 3, 6};
-static const char *const comphy1_mux_p[] = {
+static const char *const comphy_mux_p[] = {
"100m", "25m"};
-static u32 comphy1_mux_table[] = {2, 3};
+static u32 comphy_mux_table[] = {2, 3};
static const char *const sdio_mux_p[] = {
"100m", "50m", "150m", "166p5m" };
@@ -85,14 +86,29 @@ static u32 sdio_mux_table[] = {0, 1, 2, 3};
static struct hisi_mux_clock hi3798cv200_mux_clks[] = {
{ HI3798CV200_MMC_MUX, "mmc_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p),
CLK_SET_RATE_PARENT, 0xa0, 8, 3, 0, mmc_mux_table, },
+ { HI3798CV200_COMBPHY0_MUX, "combphy0_mux",
+ comphy_mux_p, ARRAY_SIZE(comphy_mux_p),
+ CLK_SET_RATE_PARENT, 0x188, 2, 2, 0, comphy_mux_table, },
{ HI3798CV200_COMBPHY1_MUX, "combphy1_mux",
- comphy1_mux_p, ARRAY_SIZE(comphy1_mux_p),
- CLK_SET_RATE_PARENT, 0x188, 10, 2, 0, comphy1_mux_table, },
+ comphy_mux_p, ARRAY_SIZE(comphy_mux_p),
+ CLK_SET_RATE_PARENT, 0x188, 10, 2, 0, comphy_mux_table, },
{ HI3798CV200_SDIO0_MUX, "sdio0_mux", sdio_mux_p,
ARRAY_SIZE(sdio_mux_p), CLK_SET_RATE_PARENT,
0x9c, 8, 2, 0, sdio_mux_table, },
};
+static u32 mmc_phase_regvals[] = {0, 1, 2, 3, 4, 5, 6, 7};
+static u32 mmc_phase_degrees[] = {0, 45, 90, 135, 180, 225, 270, 315};
+
+static struct hisi_phase_clock hi3798cv200_phase_clks[] = {
+ { HISTB_MMC_SAMPLE_CLK, "mmc_sample", "clk_mmc_ciu",
+ CLK_SET_RATE_PARENT, 0xa0, 12, 3, mmc_phase_degrees,
+ mmc_phase_regvals, ARRAY_SIZE(mmc_phase_regvals) },
+ { HISTB_MMC_DRV_CLK, "mmc_drive", "clk_mmc_ciu",
+ CLK_SET_RATE_PARENT, 0xa0, 16, 3, mmc_phase_degrees,
+ mmc_phase_regvals, ARRAY_SIZE(mmc_phase_regvals) },
+};
+
static const struct hisi_gate_clock hi3798cv200_gate_clks[] = {
/* UART */
{ HISTB_UART2_CLK, "clk_uart2", "75m",
@@ -147,6 +163,9 @@ static const struct hisi_gate_clock hi3798cv200_gate_clks[] = {
CLK_SET_RATE_PARENT, 0xcc, 4, 0, },
{ HISTB_ETH1_MACIF_CLK, "clk_macif1", "clk_bus_m1",
CLK_SET_RATE_PARENT, 0xcc, 25, 0, },
+ /* COMBPHY0 */
+ { HISTB_COMBPHY0_CLK, "clk_combphy0", "combphy0_mux",
+ CLK_SET_RATE_PARENT, 0x188, 0, 0, },
/* COMBPHY1 */
{ HISTB_COMBPHY1_CLK, "clk_combphy1", "combphy1_mux",
CLK_SET_RATE_PARENT, 0x188, 8, 0, },
@@ -161,6 +180,8 @@ static const struct hisi_gate_clock hi3798cv200_gate_clks[] = {
CLK_SET_RATE_PARENT, 0xb8, 1, 0 },
{ HISTB_USB2_UTMI_CLK, "clk_u2_utmi", "60m",
CLK_SET_RATE_PARENT, 0xb8, 5, 0 },
+ { HISTB_USB2_OTG_UTMI_CLK, "clk_u2_otg_utmi", "60m",
+ CLK_SET_RATE_PARENT, 0xb8, 3, 0 },
{ HISTB_USB2_PHY1_REF_CLK, "clk_u2_phy1_ref", "24m",
CLK_SET_RATE_PARENT, 0xbc, 0, 0 },
{ HISTB_USB2_PHY2_REF_CLK, "clk_u2_phy2_ref", "24m",
@@ -177,6 +198,14 @@ static struct hisi_clock_data *hi3798cv200_clk_register(
if (!clk_data)
return ERR_PTR(-ENOMEM);
+ /* hisi_phase_clock is resource managed */
+ ret = hisi_clk_register_phase(&pdev->dev,
+ hi3798cv200_phase_clks,
+ ARRAY_SIZE(hi3798cv200_phase_clks),
+ clk_data);
+ if (ret)
+ return ERR_PTR(ret);
+
ret = hisi_clk_register_fixed_rate(hi3798cv200_fixed_rate_clks,
ARRAY_SIZE(hi3798cv200_fixed_rate_clks),
clk_data);
@@ -202,18 +231,17 @@ static struct hisi_clock_data *hi3798cv200_clk_register(
return clk_data;
-unregister_fixed_rate:
- hisi_clk_unregister_fixed_rate(hi3798cv200_fixed_rate_clks,
- ARRAY_SIZE(hi3798cv200_fixed_rate_clks),
+unregister_gate:
+ hisi_clk_unregister_gate(hi3798cv200_gate_clks,
+ ARRAY_SIZE(hi3798cv200_gate_clks),
clk_data);
-
unregister_mux:
hisi_clk_unregister_mux(hi3798cv200_mux_clks,
ARRAY_SIZE(hi3798cv200_mux_clks),
clk_data);
-unregister_gate:
- hisi_clk_unregister_gate(hi3798cv200_gate_clks,
- ARRAY_SIZE(hi3798cv200_gate_clks),
+unregister_fixed_rate:
+ hisi_clk_unregister_fixed_rate(hi3798cv200_fixed_rate_clks,
+ ARRAY_SIZE(hi3798cv200_fixed_rate_clks),
clk_data);
return ERR_PTR(ret);
}
@@ -245,7 +273,7 @@ static const struct hisi_crg_funcs hi3798cv200_crg_funcs = {
#define HI3798CV200_SYSCTRL_NR_CLKS 16
static const struct hisi_gate_clock hi3798cv200_sysctrl_gate_clks[] = {
- { HISTB_IR_CLK, "clk_ir", "100m",
+ { HISTB_IR_CLK, "clk_ir", "24m",
CLK_SET_RATE_PARENT, 0x48, 4, 0, },
{ HISTB_TIMER01_CLK, "clk_timer01", "24m",
CLK_SET_RATE_PARENT, 0x48, 6, 0, },
diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile
index f91f2b2e11cd..8c3baa7e6496 100644
--- a/drivers/clk/imx/Makefile
+++ b/drivers/clk/imx/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_SOC_IMX35) += clk-imx35.o
obj-$(CONFIG_SOC_IMX5) += clk-imx51-imx53.o
obj-$(CONFIG_SOC_IMX6Q) += clk-imx6q.o
obj-$(CONFIG_SOC_IMX6SL) += clk-imx6sl.o
+obj-$(CONFIG_SOC_IMX6SLL) += clk-imx6sll.o
obj-$(CONFIG_SOC_IMX6SX) += clk-imx6sx.o
obj-$(CONFIG_SOC_IMX6UL) += clk-imx6ul.o
obj-$(CONFIG_SOC_IMX7D) += clk-imx7d.o
diff --git a/drivers/clk/imx/clk-busy.c b/drivers/clk/imx/clk-busy.c
index 6df3389687bc..99036527eb0d 100644
--- a/drivers/clk/imx/clk-busy.c
+++ b/drivers/clk/imx/clk-busy.c
@@ -101,7 +101,7 @@ struct clk *imx_clk_busy_divider(const char *name, const char *parent_name,
init.name = name;
init.ops = &clk_busy_divider_ops;
- init.flags = CLK_SET_RATE_PARENT;
+ init.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL;
init.parent_names = &parent_name;
init.num_parents = 1;
@@ -175,7 +175,7 @@ struct clk *imx_clk_busy_mux(const char *name, void __iomem *reg, u8 shift,
init.name = name;
init.ops = &clk_busy_mux_ops;
- init.flags = 0;
+ init.flags = CLK_IS_CRITICAL;
init.parent_names = parent_names;
init.num_parents = num_parents;
diff --git a/drivers/clk/imx/clk-imx6sll.c b/drivers/clk/imx/clk-imx6sll.c
new file mode 100644
index 000000000000..3651c77fbabe
--- /dev/null
+++ b/drivers/clk/imx/clk-imx6sll.c
@@ -0,0 +1,340 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
+ * Copyright 2017-2018 NXP.
+ */
+
+#include <dt-bindings/clock/imx6sll-clock.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include "clk.h"
+
+#define CCM_ANALOG_PLL_BYPASS (0x1 << 16)
+#define BM_CCM_CCDR_MMDC_CH0_MASK (0x2 << 16)
+#define xPLL_CLR(offset) (offset + 0x8)
+
+static const char *pll_bypass_src_sels[] = { "osc", "dummy", };
+static const char *pll1_bypass_sels[] = { "pll1", "pll1_bypass_src", };
+static const char *pll2_bypass_sels[] = { "pll2", "pll2_bypass_src", };
+static const char *pll3_bypass_sels[] = { "pll3", "pll3_bypass_src", };
+static const char *pll4_bypass_sels[] = { "pll4", "pll4_bypass_src", };
+static const char *pll5_bypass_sels[] = { "pll5", "pll5_bypass_src", };
+static const char *pll6_bypass_sels[] = { "pll6", "pll6_bypass_src", };
+static const char *pll7_bypass_sels[] = { "pll7", "pll7_bypass_src", };
+static const char *step_sels[] = { "osc", "pll2_pfd2_396m", };
+static const char *pll1_sw_sels[] = { "pll1_sys", "step", };
+static const char *axi_alt_sels[] = { "pll2_pfd2_396m", "pll3_pfd1_540m", };
+static const char *axi_sels[] = {"periph", "axi_alt_sel", };
+static const char *periph_pre_sels[] = { "pll2_bus", "pll2_pfd2_396m", "pll2_pfd0_352m", "pll2_198m", };
+static const char *periph2_pre_sels[] = { "pll2_bus", "pll2_pfd2_396m", "pll2_pfd0_352m", "pll4_audio_div", };
+static const char *periph_clk2_sels[] = { "pll3_usb_otg", "osc", "osc", };
+static const char *periph2_clk2_sels[] = { "pll3_usb_otg", "osc", };
+static const char *periph_sels[] = { "periph_pre", "periph_clk2", };
+static const char *periph2_sels[] = { "periph2_pre", "periph2_clk2", };
+static const char *usdhc_sels[] = { "pll2_pfd2_396m", "pll2_pfd0_352m", };
+static const char *ssi_sels[] = {"pll3_pfd2_508m", "pll3_pfd3_454m", "pll4_audio_div", "dummy",};
+static const char *spdif_sels[] = { "pll4_audio_div", "pll3_pfd2_508m", "pll5_video_div", "pll3_usb_otg", };
+static const char *ldb_di0_div_sels[] = { "ldb_di0_div_3_5", "ldb_di0_div_7", };
+static const char *ldb_di1_div_sels[] = { "ldb_di1_div_3_5", "ldb_di1_div_7", };
+static const char *ldb_di0_sels[] = { "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll2_pfd3_594m", "pll2_pfd1_594m", "pll3_pfd3_454m", };
+static const char *ldb_di1_sels[] = { "pll3_usb_otg", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll2_bus", "pll3_pfd3_454m", "pll3_pfd2_508m", };
+static const char *lcdif_pre_sels[] = { "pll2_bus", "pll3_pfd3_454m", "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd1_594m", "pll3_pfd1_540m", };
+static const char *ecspi_sels[] = { "pll3_60m", "osc", };
+static const char *uart_sels[] = { "pll3_80m", "osc", };
+static const char *perclk_sels[] = { "ipg", "osc", };
+static const char *lcdif_sels[] = { "lcdif_podf", "ipp_di0", "ipp_di1", "ldb_di0", "ldb_di1", };
+
+static const char *epdc_pre_sels[] = { "pll2_bus", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll3_pfd2_508m", };
+static const char *epdc_sels[] = { "epdc_podf", "ipp_di0", "ipp_di1", "ldb_di0", "ldb_di1", };
+
+static struct clk *clks[IMX6SLL_CLK_END];
+static struct clk_onecell_data clk_data;
+
+static const struct clk_div_table post_div_table[] = {
+ { .val = 2, .div = 1, },
+ { .val = 1, .div = 2, },
+ { .val = 0, .div = 4, },
+ { }
+};
+
+static const struct clk_div_table video_div_table[] = {
+ { .val = 0, .div = 1, },
+ { .val = 1, .div = 2, },
+ { .val = 2, .div = 1, },
+ { .val = 3, .div = 4, },
+ { }
+};
+
+static u32 share_count_audio;
+static u32 share_count_ssi1;
+static u32 share_count_ssi2;
+static u32 share_count_ssi3;
+
+static void __init imx6sll_clocks_init(struct device_node *ccm_node)
+{
+ struct device_node *np;
+ void __iomem *base;
+
+ clks[IMX6SLL_CLK_DUMMY] = imx_clk_fixed("dummy", 0);
+
+ clks[IMX6SLL_CLK_CKIL] = of_clk_get_by_name(ccm_node, "ckil");
+ clks[IMX6SLL_CLK_OSC] = of_clk_get_by_name(ccm_node, "osc");
+
+ /* ipp_di clock is external input */
+ clks[IMX6SLL_CLK_IPP_DI0] = of_clk_get_by_name(ccm_node, "ipp_di0");
+ clks[IMX6SLL_CLK_IPP_DI1] = of_clk_get_by_name(ccm_node, "ipp_di1");
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,imx6sll-anatop");
+ base = of_iomap(np, 0);
+ WARN_ON(!base);
+
+ /* Do not bypass PLLs initially */
+ writel_relaxed(CCM_ANALOG_PLL_BYPASS, base + xPLL_CLR(0x0));
+ writel_relaxed(CCM_ANALOG_PLL_BYPASS, base + xPLL_CLR(0x10));
+ writel_relaxed(CCM_ANALOG_PLL_BYPASS, base + xPLL_CLR(0x20));
+ writel_relaxed(CCM_ANALOG_PLL_BYPASS, base + xPLL_CLR(0x30));
+ writel_relaxed(CCM_ANALOG_PLL_BYPASS, base + xPLL_CLR(0x70));
+ writel_relaxed(CCM_ANALOG_PLL_BYPASS, base + xPLL_CLR(0xa0));
+ writel_relaxed(CCM_ANALOG_PLL_BYPASS, base + xPLL_CLR(0xe0));
+
+ clks[IMX6SLL_PLL1_BYPASS_SRC] = imx_clk_mux("pll1_bypass_src", base + 0x00, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels));
+ clks[IMX6SLL_PLL2_BYPASS_SRC] = imx_clk_mux("pll2_bypass_src", base + 0x30, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels));
+ clks[IMX6SLL_PLL3_BYPASS_SRC] = imx_clk_mux("pll3_bypass_src", base + 0x10, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels));
+ clks[IMX6SLL_PLL4_BYPASS_SRC] = imx_clk_mux("pll4_bypass_src", base + 0x70, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels));
+ clks[IMX6SLL_PLL5_BYPASS_SRC] = imx_clk_mux("pll5_bypass_src", base + 0xa0, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels));
+ clks[IMX6SLL_PLL6_BYPASS_SRC] = imx_clk_mux("pll6_bypass_src", base + 0xe0, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels));
+ clks[IMX6SLL_PLL7_BYPASS_SRC] = imx_clk_mux("pll7_bypass_src", base + 0x20, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels));
+
+ clks[IMX6SLL_CLK_PLL1] = imx_clk_pllv3(IMX_PLLV3_SYS, "pll1", "pll1_bypass_src", base + 0x00, 0x7f);
+ clks[IMX6SLL_CLK_PLL2] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2", "pll2_bypass_src", base + 0x30, 0x1);
+ clks[IMX6SLL_CLK_PLL3] = imx_clk_pllv3(IMX_PLLV3_USB, "pll3", "pll3_bypass_src", base + 0x10, 0x3);
+ clks[IMX6SLL_CLK_PLL4] = imx_clk_pllv3(IMX_PLLV3_AV, "pll4", "pll4_bypass_src", base + 0x70, 0x7f);
+ clks[IMX6SLL_CLK_PLL5] = imx_clk_pllv3(IMX_PLLV3_AV, "pll5", "pll5_bypass_src", base + 0xa0, 0x7f);
+ clks[IMX6SLL_CLK_PLL6] = imx_clk_pllv3(IMX_PLLV3_ENET, "pll6", "pll6_bypass_src", base + 0xe0, 0x3);
+ clks[IMX6SLL_CLK_PLL7] = imx_clk_pllv3(IMX_PLLV3_USB, "pll7", "pll7_bypass_src", base + 0x20, 0x3);
+
+ clks[IMX6SLL_PLL1_BYPASS] = imx_clk_mux_flags("pll1_bypass", base + 0x00, 16, 1, pll1_bypass_sels, ARRAY_SIZE(pll1_bypass_sels), CLK_SET_RATE_PARENT);
+ clks[IMX6SLL_PLL2_BYPASS] = imx_clk_mux_flags("pll2_bypass", base + 0x30, 16, 1, pll2_bypass_sels, ARRAY_SIZE(pll2_bypass_sels), CLK_SET_RATE_PARENT);
+ clks[IMX6SLL_PLL3_BYPASS] = imx_clk_mux_flags("pll3_bypass", base + 0x10, 16, 1, pll3_bypass_sels, ARRAY_SIZE(pll3_bypass_sels), CLK_SET_RATE_PARENT);
+ clks[IMX6SLL_PLL4_BYPASS] = imx_clk_mux_flags("pll4_bypass", base + 0x70, 16, 1, pll4_bypass_sels, ARRAY_SIZE(pll4_bypass_sels), CLK_SET_RATE_PARENT);
+ clks[IMX6SLL_PLL5_BYPASS] = imx_clk_mux_flags("pll5_bypass", base + 0xa0, 16, 1, pll5_bypass_sels, ARRAY_SIZE(pll5_bypass_sels), CLK_SET_RATE_PARENT);
+ clks[IMX6SLL_PLL6_BYPASS] = imx_clk_mux_flags("pll6_bypass", base + 0xe0, 16, 1, pll6_bypass_sels, ARRAY_SIZE(pll6_bypass_sels), CLK_SET_RATE_PARENT);
+ clks[IMX6SLL_PLL7_BYPASS] = imx_clk_mux_flags("pll7_bypass", base + 0x20, 16, 1, pll7_bypass_sels, ARRAY_SIZE(pll7_bypass_sels), CLK_SET_RATE_PARENT);
+
+ clks[IMX6SLL_CLK_PLL1_SYS] = imx_clk_fixed_factor("pll1_sys", "pll1_bypass", 1, 1);
+ clks[IMX6SLL_CLK_PLL2_BUS] = imx_clk_gate("pll2_bus", "pll2_bypass", base + 0x30, 13);
+ clks[IMX6SLL_CLK_PLL3_USB_OTG] = imx_clk_gate("pll3_usb_otg", "pll3_bypass", base + 0x10, 13);
+ clks[IMX6SLL_CLK_PLL4_AUDIO] = imx_clk_gate("pll4_audio", "pll4_bypass", base + 0x70, 13);
+ clks[IMX6SLL_CLK_PLL5_VIDEO] = imx_clk_gate("pll5_video", "pll5_bypass", base + 0xa0, 13);
+ clks[IMX6SLL_CLK_PLL6_ENET] = imx_clk_gate("pll6_enet", "pll6_bypass", base + 0xe0, 13);
+ clks[IMX6SLL_CLK_PLL7_USB_HOST] = imx_clk_gate("pll7_usb_host", "pll7_bypass", base + 0x20, 13);
+
+ /*
+ * Bit 20 is the reserved and read-only bit, we do this only for:
+ * - Do nothing for usbphy clk_enable/disable
+ * - Keep refcount when do usbphy clk_enable/disable, in that case,
+ * the clk framework many need to enable/disable usbphy's parent
+ */
+ clks[IMX6SLL_CLK_USBPHY1] = imx_clk_gate("usbphy1", "pll3_usb_otg", base + 0x10, 20);
+ clks[IMX6SLL_CLK_USBPHY2] = imx_clk_gate("usbphy2", "pll7_usb_host", base + 0x20, 20);
+
+ /*
+ * usbphy*_gate needs to be on after system boots up, and software
+ * never needs to control it anymore.
+ */
+ if (IS_ENABLED(CONFIG_USB_MXS_PHY)) {
+ clks[IMX6SLL_CLK_USBPHY1_GATE] = imx_clk_gate_flags("usbphy1_gate", "dummy", base + 0x10, 6, CLK_IS_CRITICAL);
+ clks[IMX6SLL_CLK_USBPHY2_GATE] = imx_clk_gate_flags("usbphy2_gate", "dummy", base + 0x20, 6, CLK_IS_CRITICAL);
+ }
+
+ /* name parent_name reg idx */
+ clks[IMX6SLL_CLK_PLL2_PFD0] = imx_clk_pfd("pll2_pfd0_352m", "pll2_bus", base + 0x100, 0);
+ clks[IMX6SLL_CLK_PLL2_PFD1] = imx_clk_pfd("pll2_pfd1_594m", "pll2_bus", base + 0x100, 1);
+ clks[IMX6SLL_CLK_PLL2_PFD2] = imx_clk_pfd("pll2_pfd2_396m", "pll2_bus", base + 0x100, 2);
+ clks[IMX6SLL_CLK_PLL2_PFD3] = imx_clk_pfd("pll2_pfd3_594m", "pll2_bus", base + 0x100, 3);
+ clks[IMX6SLL_CLK_PLL3_PFD0] = imx_clk_pfd("pll3_pfd0_720m", "pll3_usb_otg", base + 0xf0, 0);
+ clks[IMX6SLL_CLK_PLL3_PFD1] = imx_clk_pfd("pll3_pfd1_540m", "pll3_usb_otg", base + 0xf0, 1);
+ clks[IMX6SLL_CLK_PLL3_PFD2] = imx_clk_pfd("pll3_pfd2_508m", "pll3_usb_otg", base + 0xf0, 2);
+ clks[IMX6SLL_CLK_PLL3_PFD3] = imx_clk_pfd("pll3_pfd3_454m", "pll3_usb_otg", base + 0xf0, 3);
+
+ clks[IMX6SLL_CLK_PLL4_POST_DIV] = clk_register_divider_table(NULL, "pll4_post_div", "pll4_audio",
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0x70, 19, 2, 0, post_div_table, &imx_ccm_lock);
+ clks[IMX6SLL_CLK_PLL4_AUDIO_DIV] = clk_register_divider(NULL, "pll4_audio_div", "pll4_post_div",
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0x170, 15, 1, 0, &imx_ccm_lock);
+ clks[IMX6SLL_CLK_PLL5_POST_DIV] = clk_register_divider_table(NULL, "pll5_post_div", "pll5_video",
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0xa0, 19, 2, 0, post_div_table, &imx_ccm_lock);
+ clks[IMX6SLL_CLK_PLL5_VIDEO_DIV] = clk_register_divider_table(NULL, "pll5_video_div", "pll5_post_div",
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0x170, 30, 2, 0, video_div_table, &imx_ccm_lock);
+
+ /* name parent_name mult div */
+ clks[IMX6SLL_CLK_PLL2_198M] = imx_clk_fixed_factor("pll2_198m", "pll2_pfd2_396m", 1, 2);
+ clks[IMX6SLL_CLK_PLL3_120M] = imx_clk_fixed_factor("pll3_120m", "pll3_usb_otg", 1, 4);
+ clks[IMX6SLL_CLK_PLL3_80M] = imx_clk_fixed_factor("pll3_80m", "pll3_usb_otg", 1, 6);
+ clks[IMX6SLL_CLK_PLL3_60M] = imx_clk_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8);
+
+ np = ccm_node;
+ base = of_iomap(np, 0);
+ WARN_ON(!base);
+
+ clks[IMX6SLL_CLK_STEP] = imx_clk_mux("step", base + 0x0c, 8, 1, step_sels, ARRAY_SIZE(step_sels));
+ clks[IMX6SLL_CLK_PLL1_SW] = imx_clk_mux_flags("pll1_sw", base + 0x0c, 2, 1, pll1_sw_sels, ARRAY_SIZE(pll1_sw_sels), 0);
+ clks[IMX6SLL_CLK_AXI_ALT_SEL] = imx_clk_mux("axi_alt_sel", base + 0x14, 7, 1, axi_alt_sels, ARRAY_SIZE(axi_alt_sels));
+ clks[IMX6SLL_CLK_AXI_SEL] = imx_clk_mux_flags("axi_sel", base + 0x14, 6, 1, axi_sels, ARRAY_SIZE(axi_sels), 0);
+ clks[IMX6SLL_CLK_PERIPH_PRE] = imx_clk_mux("periph_pre", base + 0x18, 18, 2, periph_pre_sels, ARRAY_SIZE(periph_pre_sels));
+ clks[IMX6SLL_CLK_PERIPH2_PRE] = imx_clk_mux("periph2_pre", base + 0x18, 21, 2, periph2_pre_sels, ARRAY_SIZE(periph2_pre_sels));
+ clks[IMX6SLL_CLK_PERIPH_CLK2_SEL] = imx_clk_mux("periph_clk2_sel", base + 0x18, 12, 2, periph_clk2_sels, ARRAY_SIZE(periph_clk2_sels));
+ clks[IMX6SLL_CLK_PERIPH2_CLK2_SEL] = imx_clk_mux("periph2_clk2_sel", base + 0x18, 20, 1, periph2_clk2_sels, ARRAY_SIZE(periph2_clk2_sels));
+ clks[IMX6SLL_CLK_USDHC1_SEL] = imx_clk_mux("usdhc1_sel", base + 0x1c, 16, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels));
+ clks[IMX6SLL_CLK_USDHC2_SEL] = imx_clk_mux("usdhc2_sel", base + 0x1c, 17, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels));
+ clks[IMX6SLL_CLK_USDHC3_SEL] = imx_clk_mux("usdhc3_sel", base + 0x1c, 18, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels));
+ clks[IMX6SLL_CLK_SSI1_SEL] = imx_clk_mux("ssi1_sel", base + 0x1c, 10, 2, ssi_sels, ARRAY_SIZE(ssi_sels));
+ clks[IMX6SLL_CLK_SSI2_SEL] = imx_clk_mux("ssi2_sel", base + 0x1c, 12, 2, ssi_sels, ARRAY_SIZE(ssi_sels));
+ clks[IMX6SLL_CLK_SSI3_SEL] = imx_clk_mux("ssi3_sel", base + 0x1c, 14, 2, ssi_sels, ARRAY_SIZE(ssi_sels));
+ clks[IMX6SLL_CLK_PERCLK_SEL] = imx_clk_mux("perclk_sel", base + 0x1c, 6, 1, perclk_sels, ARRAY_SIZE(perclk_sels));
+ clks[IMX6SLL_CLK_UART_SEL] = imx_clk_mux("uart_sel", base + 0x24, 6, 1, uart_sels, ARRAY_SIZE(uart_sels));
+ clks[IMX6SLL_CLK_SPDIF_SEL] = imx_clk_mux("spdif_sel", base + 0x30, 20, 2, spdif_sels, ARRAY_SIZE(spdif_sels));
+ clks[IMX6SLL_CLK_EXTERN_AUDIO_SEL] = imx_clk_mux("extern_audio_sel", base + 0x30, 7, 2, spdif_sels, ARRAY_SIZE(spdif_sels));
+ clks[IMX6SLL_CLK_EPDC_PRE_SEL] = imx_clk_mux("epdc_pre_sel", base + 0x34, 15, 3, epdc_pre_sels, ARRAY_SIZE(epdc_pre_sels));
+ clks[IMX6SLL_CLK_EPDC_SEL] = imx_clk_mux("epdc_sel", base + 0x34, 9, 3, epdc_sels, ARRAY_SIZE(epdc_sels));
+ clks[IMX6SLL_CLK_ECSPI_SEL] = imx_clk_mux("ecspi_sel", base + 0x38, 18, 1, ecspi_sels, ARRAY_SIZE(ecspi_sels));
+ clks[IMX6SLL_CLK_LCDIF_PRE_SEL] = imx_clk_mux("lcdif_pre_sel", base + 0x38, 15, 3, lcdif_pre_sels, ARRAY_SIZE(lcdif_pre_sels));
+ clks[IMX6SLL_CLK_LCDIF_SEL] = imx_clk_mux("lcdif_sel", base + 0x38, 9, 3, lcdif_sels, ARRAY_SIZE(lcdif_sels));
+
+ clks[IMX6SLL_CLK_PERIPH] = imx_clk_busy_mux("periph", base + 0x14, 25, 1, base + 0x48, 5, periph_sels, ARRAY_SIZE(periph_sels));
+ clks[IMX6SLL_CLK_PERIPH2] = imx_clk_busy_mux("periph2", base + 0x14, 26, 1, base + 0x48, 3, periph2_sels, ARRAY_SIZE(periph2_sels));
+
+ clks[IMX6SLL_CLK_PERIPH_CLK2] = imx_clk_divider("periph_clk2", "periph_clk2_sel", base + 0x14, 27, 3);
+ clks[IMX6SLL_CLK_PERIPH2_CLK2] = imx_clk_divider("periph2_clk2", "periph2_clk2_sel", base + 0x14, 0, 3);
+ clks[IMX6SLL_CLK_IPG] = imx_clk_divider("ipg", "ahb", base + 0x14, 8, 2);
+ clks[IMX6SLL_CLK_LCDIF_PODF] = imx_clk_divider("lcdif_podf", "lcdif_pred", base + 0x18, 23, 3);
+ clks[IMX6SLL_CLK_PERCLK] = imx_clk_divider("perclk", "perclk_sel", base + 0x1c, 0, 6);
+ clks[IMX6SLL_CLK_USDHC3_PODF] = imx_clk_divider("usdhc3_podf", "usdhc3_sel", base + 0x24, 19, 3);
+ clks[IMX6SLL_CLK_USDHC2_PODF] = imx_clk_divider("usdhc2_podf", "usdhc2_sel", base + 0x24, 16, 3);
+ clks[IMX6SLL_CLK_USDHC1_PODF] = imx_clk_divider("usdhc1_podf", "usdhc1_sel", base + 0x24, 11, 3);
+ clks[IMX6SLL_CLK_UART_PODF] = imx_clk_divider("uart_podf", "uart_sel", base + 0x24, 0, 6);
+ clks[IMX6SLL_CLK_SSI3_PRED] = imx_clk_divider("ssi3_pred", "ssi3_sel", base + 0x28, 22, 3);
+ clks[IMX6SLL_CLK_SSI3_PODF] = imx_clk_divider("ssi3_podf", "ssi3_pred", base + 0x28, 16, 6);
+ clks[IMX6SLL_CLK_SSI1_PRED] = imx_clk_divider("ssi1_pred", "ssi1_sel", base + 0x28, 6, 3);
+ clks[IMX6SLL_CLK_SSI1_PODF] = imx_clk_divider("ssi1_podf", "ssi1_pred", base + 0x28, 0, 6);
+ clks[IMX6SLL_CLK_SSI2_PRED] = imx_clk_divider("ssi2_pred", "ssi2_sel", base + 0x2c, 6, 3);
+ clks[IMX6SLL_CLK_SSI2_PODF] = imx_clk_divider("ssi2_podf", "ssi2_pred", base + 0x2c, 0, 6);
+ clks[IMX6SLL_CLK_SPDIF_PRED] = imx_clk_divider("spdif_pred", "spdif_sel", base + 0x30, 25, 3);
+ clks[IMX6SLL_CLK_SPDIF_PODF] = imx_clk_divider("spdif_podf", "spdif_pred", base + 0x30, 22, 3);
+ clks[IMX6SLL_CLK_EXTERN_AUDIO_PRED] = imx_clk_divider("extern_audio_pred", "extern_audio_sel", base + 0x30, 12, 3);
+ clks[IMX6SLL_CLK_EXTERN_AUDIO_PODF] = imx_clk_divider("extern_audio_podf", "extern_audio_pred", base + 0x30, 9, 3);
+ clks[IMX6SLL_CLK_EPDC_PODF] = imx_clk_divider("epdc_podf", "epdc_pre_sel", base + 0x34, 12, 3);
+ clks[IMX6SLL_CLK_ECSPI_PODF] = imx_clk_divider("ecspi_podf", "ecspi_sel", base + 0x38, 19, 6);
+ clks[IMX6SLL_CLK_LCDIF_PRED] = imx_clk_divider("lcdif_pred", "lcdif_pre_sel", base + 0x38, 12, 3);
+
+ clks[IMX6SLL_CLK_ARM] = imx_clk_busy_divider("arm", "pll1_sw", base + 0x10, 0, 3, base + 0x48, 16);
+ clks[IMX6SLL_CLK_MMDC_PODF] = imx_clk_busy_divider("mmdc_podf", "periph2", base + 0x14, 3, 3, base + 0x48, 2);
+ clks[IMX6SLL_CLK_AXI_PODF] = imx_clk_busy_divider("axi", "axi_sel", base + 0x14, 16, 3, base + 0x48, 0);
+ clks[IMX6SLL_CLK_AHB] = imx_clk_busy_divider("ahb", "periph", base + 0x14, 10, 3, base + 0x48, 1);
+
+ clks[IMX6SLL_CLK_LDB_DI0_DIV_3_5] = imx_clk_fixed_factor("ldb_di0_div_3_5", "ldb_di0_sel", 2, 7);
+ clks[IMX6SLL_CLK_LDB_DI0_DIV_7] = imx_clk_fixed_factor("ldb_di0_div_7", "ldb_di0_sel", 1, 7);
+ clks[IMX6SLL_CLK_LDB_DI1_DIV_3_5] = imx_clk_fixed_factor("ldb_di1_div_3_5", "ldb_di1_sel", 2, 7);
+ clks[IMX6SLL_CLK_LDB_DI1_DIV_7] = imx_clk_fixed_factor("ldb_di1_div_7", "ldb_di1_sel", 1, 7);
+
+ clks[IMX6SLL_CLK_LDB_DI0_SEL] = imx_clk_mux("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di0_sels, ARRAY_SIZE(ldb_di0_sels));
+ clks[IMX6SLL_CLK_LDB_DI1_SEL] = imx_clk_mux("ldb_di1_sel", base + 0x1c, 7, 3, ldb_di1_sels, ARRAY_SIZE(ldb_di1_sels));
+ clks[IMX6SLL_CLK_LDB_DI0_DIV_SEL] = imx_clk_mux("ldb_di0_div_sel", base + 0x20, 10, 1, ldb_di0_div_sels, ARRAY_SIZE(ldb_di0_div_sels));
+ clks[IMX6SLL_CLK_LDB_DI1_DIV_SEL] = imx_clk_mux("ldb_di1_div_sel", base + 0x20, 10, 1, ldb_di1_div_sels, ARRAY_SIZE(ldb_di1_div_sels));
+
+ /* CCGR0 */
+ clks[IMX6SLL_CLK_AIPSTZ1] = imx_clk_gate2_flags("aips_tz1", "ahb", base + 0x68, 0, CLK_IS_CRITICAL);
+ clks[IMX6SLL_CLK_AIPSTZ2] = imx_clk_gate2_flags("aips_tz2", "ahb", base + 0x68, 2, CLK_IS_CRITICAL);
+ clks[IMX6SLL_CLK_DCP] = imx_clk_gate2("dcp", "ahb", base + 0x68, 10);
+ clks[IMX6SLL_CLK_UART2_IPG] = imx_clk_gate2("uart2_ipg", "ipg", base + 0x68, 28);
+ clks[IMX6SLL_CLK_UART2_SERIAL] = imx_clk_gate2("uart2_serial", "uart_podf", base + 0x68, 28);
+
+ /* CCGR1 */
+ clks[IMX6SLL_CLK_ECSPI1] = imx_clk_gate2("ecspi1", "ecspi_podf", base + 0x6c, 0);
+ clks[IMX6SLL_CLK_ECSPI2] = imx_clk_gate2("ecspi2", "ecspi_podf", base + 0x6c, 2);
+ clks[IMX6SLL_CLK_ECSPI3] = imx_clk_gate2("ecspi3", "ecspi_podf", base + 0x6c, 4);
+ clks[IMX6SLL_CLK_ECSPI4] = imx_clk_gate2("ecspi4", "ecspi_podf", base + 0x6c, 6);
+ clks[IMX6SLL_CLK_UART3_IPG] = imx_clk_gate2("uart3_ipg", "ipg", base + 0x6c, 10);
+ clks[IMX6SLL_CLK_UART3_SERIAL] = imx_clk_gate2("uart3_serial", "uart_podf", base + 0x6c, 10);
+ clks[IMX6SLL_CLK_EPIT1] = imx_clk_gate2("epit1", "perclk", base + 0x6c, 12);
+ clks[IMX6SLL_CLK_EPIT2] = imx_clk_gate2("epit2", "perclk", base + 0x6c, 14);
+ clks[IMX6SLL_CLK_GPT_BUS] = imx_clk_gate2("gpt1_bus", "perclk", base + 0x6c, 20);
+ clks[IMX6SLL_CLK_GPT_SERIAL] = imx_clk_gate2("gpt1_serial", "perclk", base + 0x6c, 22);
+ clks[IMX6SLL_CLK_UART4_IPG] = imx_clk_gate2("uart4_ipg", "ipg", base + 0x6c, 24);
+ clks[IMX6SLL_CLK_UART4_SERIAL] = imx_clk_gate2("uart4_serail", "uart_podf", base + 0x6c, 24);
+
+ /* CCGR2 */
+ clks[IMX6SLL_CLK_CSI] = imx_clk_gate2("csi", "axi", base + 0x70, 2);
+ clks[IMX6SLL_CLK_I2C1] = imx_clk_gate2("i2c1", "perclk", base + 0x70, 6);
+ clks[IMX6SLL_CLK_I2C2] = imx_clk_gate2("i2c2", "perclk", base + 0x70, 8);
+ clks[IMX6SLL_CLK_I2C3] = imx_clk_gate2("i2c3", "perclk", base + 0x70, 10);
+ clks[IMX6SLL_CLK_OCOTP] = imx_clk_gate2("ocotp", "ipg", base + 0x70, 12);
+ clks[IMX6SLL_CLK_LCDIF_APB] = imx_clk_gate2("lcdif_apb", "axi", base + 0x70, 28);
+ clks[IMX6SLL_CLK_PXP] = imx_clk_gate2("pxp", "axi", base + 0x70, 30);
+
+ /* CCGR3 */
+ clks[IMX6SLL_CLK_UART5_IPG] = imx_clk_gate2("uart5_ipg", "ipg", base + 0x74, 2);
+ clks[IMX6SLL_CLK_UART5_SERIAL] = imx_clk_gate2("uart5_serial", "uart_podf", base + 0x74, 2);
+ clks[IMX6SLL_CLK_EPDC_AXI] = imx_clk_gate2("epdc_aclk", "axi", base + 0x74, 4);
+ clks[IMX6SLL_CLK_EPDC_PIX] = imx_clk_gate2("epdc_pix", "epdc_podf", base + 0x74, 4);
+ clks[IMX6SLL_CLK_LCDIF_PIX] = imx_clk_gate2("lcdif_pix", "lcdif_podf", base + 0x74, 10);
+ clks[IMX6SLL_CLK_WDOG1] = imx_clk_gate2("wdog1", "ipg", base + 0x74, 16);
+ clks[IMX6SLL_CLK_MMDC_P0_FAST] = imx_clk_gate_flags("mmdc_p0_fast", "mmdc_podf", base + 0x74, 20, CLK_IS_CRITICAL);
+ clks[IMX6SLL_CLK_MMDC_P0_IPG] = imx_clk_gate2_flags("mmdc_p0_ipg", "ipg", base + 0x74, 24, CLK_IS_CRITICAL);
+ clks[IMX6SLL_CLK_OCRAM] = imx_clk_gate_flags("ocram","ahb", base + 0x74, 28, CLK_IS_CRITICAL);
+
+ /* CCGR4 */
+ clks[IMX6SLL_CLK_PWM1] = imx_clk_gate2("pwm1", "perclk", base + 0x78, 16);
+ clks[IMX6SLL_CLK_PWM2] = imx_clk_gate2("pwm2", "perclk", base + 0x78, 18);
+ clks[IMX6SLL_CLK_PWM3] = imx_clk_gate2("pwm3", "perclk", base + 0x78, 20);
+ clks[IMX6SLL_CLK_PWM4] = imx_clk_gate2("pwm4", "perclk", base + 0x78, 22);
+
+ /* CCGR5 */
+ clks[IMX6SLL_CLK_ROM] = imx_clk_gate2_flags("rom", "ahb", base + 0x7c, 0, CLK_IS_CRITICAL);
+ clks[IMX6SLL_CLK_SDMA] = imx_clk_gate2("sdma", "ahb", base + 0x7c, 6);
+ clks[IMX6SLL_CLK_WDOG2] = imx_clk_gate2("wdog2", "ipg", base + 0x7c, 10);
+ clks[IMX6SLL_CLK_SPBA] = imx_clk_gate2("spba", "ipg", base + 0x7c, 12);
+ clks[IMX6SLL_CLK_EXTERN_AUDIO] = imx_clk_gate2_shared("extern_audio", "extern_audio_podf", base + 0x7c, 14, &share_count_audio);
+ clks[IMX6SLL_CLK_SPDIF] = imx_clk_gate2_shared("spdif", "spdif_podf", base + 0x7c, 14, &share_count_audio);
+ clks[IMX6SLL_CLK_SPDIF_GCLK] = imx_clk_gate2_shared("spdif_gclk", "ipg", base + 0x7c, 14, &share_count_audio);
+ clks[IMX6SLL_CLK_SSI1] = imx_clk_gate2_shared("ssi1", "ssi1_podf", base + 0x7c, 18, &share_count_ssi1);
+ clks[IMX6SLL_CLK_SSI1_IPG] = imx_clk_gate2_shared("ssi1_ipg", "ipg", base + 0x7c, 18, &share_count_ssi1);
+ clks[IMX6SLL_CLK_SSI2] = imx_clk_gate2_shared("ssi2", "ssi2_podf", base + 0x7c, 20, &share_count_ssi2);
+ clks[IMX6SLL_CLK_SSI2_IPG] = imx_clk_gate2_shared("ssi2_ipg", "ipg", base + 0x7c, 20, &share_count_ssi2);
+ clks[IMX6SLL_CLK_SSI3] = imx_clk_gate2_shared("ssi3", "ssi3_podf", base + 0x7c, 22, &share_count_ssi3);
+ clks[IMX6SLL_CLK_SSI3_IPG] = imx_clk_gate2_shared("ssi3_ipg", "ipg", base + 0x7c, 22, &share_count_ssi3);
+ clks[IMX6SLL_CLK_UART1_IPG] = imx_clk_gate2("uart1_ipg", "ipg", base + 0x7c, 24);
+ clks[IMX6SLL_CLK_UART1_SERIAL] = imx_clk_gate2("uart1_serial", "uart_podf", base + 0x7c, 24);
+
+ /* CCGR6 */
+ clks[IMX6SLL_CLK_USBOH3] = imx_clk_gate2("usboh3", "ipg", base + 0x80, 0);
+ clks[IMX6SLL_CLK_USDHC1] = imx_clk_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2);
+ clks[IMX6SLL_CLK_USDHC2] = imx_clk_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4);
+ clks[IMX6SLL_CLK_USDHC3] = imx_clk_gate2("usdhc3", "usdhc3_podf", base + 0x80, 6);
+
+ /* mask handshake of mmdc */
+ writel_relaxed(BM_CCM_CCDR_MMDC_CH0_MASK, base + 0x4);
+
+ imx_check_clocks(clks, ARRAY_SIZE(clks));
+
+ clk_data.clks = clks;
+ clk_data.clk_num = ARRAY_SIZE(clks);
+ of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+
+ /* Lower the AHB clock rate before changing the clock source. */
+ clk_set_rate(clks[IMX6SLL_CLK_AHB], 99000000);
+
+ /* Change periph_pre clock to pll2_bus to adjust AXI rate to 264MHz */
+ clk_set_parent(clks[IMX6SLL_CLK_PERIPH_CLK2_SEL], clks[IMX6SLL_CLK_PLL3_USB_OTG]);
+ clk_set_parent(clks[IMX6SLL_CLK_PERIPH], clks[IMX6SLL_CLK_PERIPH_CLK2]);
+ clk_set_parent(clks[IMX6SLL_CLK_PERIPH_PRE], clks[IMX6SLL_CLK_PLL2_BUS]);
+ clk_set_parent(clks[IMX6SLL_CLK_PERIPH], clks[IMX6SLL_CLK_PERIPH_PRE]);
+
+ clk_set_rate(clks[IMX6SLL_CLK_AHB], 132000000);
+}
+CLK_OF_DECLARE_DRIVER(imx6sll, "fsl,imx6sll-ccm", imx6sll_clocks_init);
diff --git a/drivers/clk/imx/clk-imx6sx.c b/drivers/clk/imx/clk-imx6sx.c
index e6d389e333d7..bc3f9ebf2d9e 100644
--- a/drivers/clk/imx/clk-imx6sx.c
+++ b/drivers/clk/imx/clk-imx6sx.c
@@ -63,17 +63,17 @@ static const char *lcdif2_sels[] = { "lcdif2_podf", "ipp_di0", "ipp_di1", "ldb_d
static const char *display_sels[] = { "pll2_bus", "pll2_pfd2_396m", "pll3_usb_otg", "pll3_pfd1_540m", };
static const char *csi_sels[] = { "osc", "pll2_pfd2_396m", "pll3_120m", "pll3_pfd1_540m", };
static const char *cko1_sels[] = {
- "pll3_usb_otg", "pll2_bus", "pll1_sys", "pll5_video_div",
- "dummy", "ocram", "dummy", "pxp_axi", "epdc_axi", "lcdif_pix",
- "epdc_pix", "ahb", "ipg", "perclk", "ckil", "pll4_audio_div",
+ "dummy", "dummy", "dummy", "dummy",
+ "vadc", "ocram", "qspi2", "m4", "enet_ahb", "lcdif2_pix",
+ "lcdif1_pix", "ahb", "ipg", "perclk", "ckil", "pll4_audio_div",
};
static const char *cko2_sels[] = {
"dummy", "mmdc_p0_fast", "usdhc4", "usdhc1", "dummy", "wrck",
"ecspi_root", "dummy", "usdhc3", "pcie", "arm", "csi_core",
- "lcdif_axi", "dummy", "osc", "dummy", "gpu2d_ovg_core",
- "usdhc2", "ssi1", "ssi2", "ssi3", "gpu2d_core", "dummy",
- "dummy", "dummy", "dummy", "esai_extal", "eim_slow", "uart_serial",
- "spdif", "asrc", "dummy",
+ "display_axi", "dummy", "osc", "dummy", "dummy",
+ "usdhc2", "ssi1", "ssi2", "ssi3", "gpu_axi_podf", "dummy",
+ "can_podf", "lvds1_out", "qspi1", "esai_extal", "eim_slow",
+ "uart_serial", "spdif", "audio", "dummy",
};
static const char *cko_sels[] = { "cko1", "cko2", };
static const char *lvds_sels[] = {
diff --git a/drivers/clk/imx/clk-imx6ul.c b/drivers/clk/imx/clk-imx6ul.c
index 85c118164469..114ecbb94ec5 100644
--- a/drivers/clk/imx/clk-imx6ul.c
+++ b/drivers/clk/imx/clk-imx6ul.c
@@ -308,7 +308,10 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
clks[IMX6UL_CLK_SAI2_PODF] = imx_clk_divider("sai2_podf", "sai2_pred", base + 0x2c, 0, 6);
clks[IMX6UL_CLK_SPDIF_PRED] = imx_clk_divider("spdif_pred", "spdif_sel", base + 0x30, 25, 3);
clks[IMX6UL_CLK_SPDIF_PODF] = imx_clk_divider("spdif_podf", "spdif_pred", base + 0x30, 22, 3);
- clks[IMX6UL_CLK_SIM_PODF] = imx_clk_divider("sim_podf", "sim_pre_sel", base + 0x34, 12, 3);
+ if (clk_on_imx6ul())
+ clks[IMX6UL_CLK_SIM_PODF] = imx_clk_divider("sim_podf", "sim_pre_sel", base + 0x34, 12, 3);
+ else if (clk_on_imx6ull())
+ clks[IMX6ULL_CLK_EPDC_PODF] = imx_clk_divider("epdc_podf", "epdc_pre_sel", base + 0x34, 12, 3);
clks[IMX6UL_CLK_ECSPI_PODF] = imx_clk_divider("ecspi_podf", "ecspi_sel", base + 0x38, 19, 6);
clks[IMX6UL_CLK_LCDIF_PRED] = imx_clk_divider("lcdif_pred", "lcdif_pre_sel", base + 0x38, 12, 3);
clks[IMX6UL_CLK_CSI_PODF] = imx_clk_divider("csi_podf", "csi_sel", base + 0x3c, 11, 3);
diff --git a/drivers/clk/imx/clk-imx7d.c b/drivers/clk/imx/clk-imx7d.c
index 80dc211eb74b..975a20d3cc94 100644
--- a/drivers/clk/imx/clk-imx7d.c
+++ b/drivers/clk/imx/clk-imx7d.c
@@ -51,20 +51,20 @@ static const char *arm_a7_sel[] = { "osc", "pll_arm_main_clk",
static const char *arm_m4_sel[] = { "osc", "pll_sys_main_240m_clk",
"pll_enet_250m_clk", "pll_sys_pfd2_270m_clk",
- "pll_dram_533m_clk", "pll_audio_post_div", "pll_video_main_clk",
+ "pll_dram_533m_clk", "pll_audio_post_div", "pll_video_post_div",
"pll_usb_main_clk", };
static const char *axi_sel[] = { "osc", "pll_sys_pfd1_332m_clk",
"pll_dram_533m_clk", "pll_enet_250m_clk", "pll_sys_pfd5_clk",
- "pll_audio_post_div", "pll_video_main_clk", "pll_sys_pfd7_clk", };
+ "pll_audio_post_div", "pll_video_post_div", "pll_sys_pfd7_clk", };
static const char *disp_axi_sel[] = { "osc", "pll_sys_pfd1_332m_clk",
"pll_dram_533m_clk", "pll_enet_250m_clk", "pll_sys_pfd6_clk",
- "pll_sys_pfd7_clk", "pll_audio_post_div", "pll_video_main_clk", };
+ "pll_sys_pfd7_clk", "pll_audio_post_div", "pll_video_post_div", };
static const char *enet_axi_sel[] = { "osc", "pll_sys_pfd2_270m_clk",
"pll_dram_533m_clk", "pll_enet_250m_clk",
- "pll_sys_main_240m_clk", "pll_audio_post_div", "pll_video_main_clk",
+ "pll_sys_main_240m_clk", "pll_audio_post_div", "pll_video_post_div",
"pll_sys_pfd4_clk", };
static const char *nand_usdhc_bus_sel[] = { "osc", "pll_sys_pfd2_270m_clk",
@@ -74,8 +74,8 @@ static const char *nand_usdhc_bus_sel[] = { "osc", "pll_sys_pfd2_270m_clk",
static const char *ahb_channel_sel[] = { "osc", "pll_sys_pfd2_270m_clk",
"pll_dram_533m_clk", "pll_sys_pfd0_392m_clk",
- "pll_enet_125m_clk", "pll_usb_main_clk", "pll_audio_post_div",
- "pll_video_main_clk", };
+ "pll_enet_250m_clk", "pll_usb_main_clk", "pll_audio_post_div",
+ "pll_video_post_div", };
static const char *dram_phym_sel[] = { "pll_dram_main_clk",
"dram_phym_alt_clk", };
@@ -86,7 +86,7 @@ static const char *dram_sel[] = { "pll_dram_main_clk",
static const char *dram_phym_alt_sel[] = { "osc", "pll_dram_533m_clk",
"pll_sys_main_clk", "pll_enet_500m_clk",
"pll_usb_main_clk", "pll_sys_pfd7_clk", "pll_audio_post_div",
- "pll_video_main_clk", };
+ "pll_video_post_div", };
static const char *dram_alt_sel[] = { "osc", "pll_dram_533m_clk",
"pll_sys_main_clk", "pll_enet_500m_clk",
@@ -108,62 +108,62 @@ static const char *pcie_phy_sel[] = { "osc", "pll_enet_100m_clk",
static const char *epdc_pixel_sel[] = { "osc", "pll_sys_pfd1_332m_clk",
"pll_dram_533m_clk", "pll_sys_main_clk", "pll_sys_pfd5_clk",
- "pll_sys_pfd6_clk", "pll_sys_pfd7_clk", "pll_video_main_clk", };
+ "pll_sys_pfd6_clk", "pll_sys_pfd7_clk", "pll_video_post_div", };
static const char *lcdif_pixel_sel[] = { "osc", "pll_sys_pfd5_clk",
"pll_dram_533m_clk", "ext_clk_3", "pll_sys_pfd4_clk",
- "pll_sys_pfd2_270m_clk", "pll_video_main_clk",
+ "pll_sys_pfd2_270m_clk", "pll_video_post_div",
"pll_usb_main_clk", };
static const char *mipi_dsi_sel[] = { "osc", "pll_sys_pfd5_clk",
"pll_sys_pfd3_clk", "pll_sys_main_clk", "pll_sys_pfd0_196m_clk",
- "pll_dram_533m_clk", "pll_video_main_clk", "pll_audio_post_div", };
+ "pll_dram_533m_clk", "pll_video_post_div", "pll_audio_post_div", };
static const char *mipi_csi_sel[] = { "osc", "pll_sys_pfd4_clk",
"pll_sys_pfd3_clk", "pll_sys_main_clk", "pll_sys_pfd0_196m_clk",
- "pll_dram_533m_clk", "pll_video_main_clk", "pll_audio_post_div", };
+ "pll_dram_533m_clk", "pll_video_post_div", "pll_audio_post_div", };
static const char *mipi_dphy_sel[] = { "osc", "pll_sys_main_120m_clk",
"pll_dram_533m_clk", "pll_sys_pfd5_clk", "ref_1m_clk", "ext_clk_2",
- "pll_video_main_clk", "ext_clk_3", };
+ "pll_video_post_div", "ext_clk_3", };
static const char *sai1_sel[] = { "osc", "pll_sys_pfd2_135m_clk",
- "pll_audio_post_div", "pll_dram_533m_clk", "pll_video_main_clk",
+ "pll_audio_post_div", "pll_dram_533m_clk", "pll_video_post_div",
"pll_sys_pfd4_clk", "pll_enet_125m_clk", "ext_clk_2", };
static const char *sai2_sel[] = { "osc", "pll_sys_pfd2_135m_clk",
- "pll_audio_post_div", "pll_dram_533m_clk", "pll_video_main_clk",
+ "pll_audio_post_div", "pll_dram_533m_clk", "pll_video_post_div",
"pll_sys_pfd4_clk", "pll_enet_125m_clk", "ext_clk_2", };
static const char *sai3_sel[] = { "osc", "pll_sys_pfd2_135m_clk",
- "pll_audio_post_div", "pll_dram_533m_clk", "pll_video_main_clk",
+ "pll_audio_post_div", "pll_dram_533m_clk", "pll_video_post_div",
"pll_sys_pfd4_clk", "pll_enet_125m_clk", "ext_clk_3", };
static const char *spdif_sel[] = { "osc", "pll_sys_pfd2_135m_clk",
- "pll_audio_post_div", "pll_dram_533m_clk", "pll_video_main_clk",
+ "pll_audio_post_div", "pll_dram_533m_clk", "pll_video_post_div",
"pll_sys_pfd4_clk", "pll_enet_125m_clk", "ext_3_clk", };
static const char *enet1_ref_sel[] = { "osc", "pll_enet_125m_clk",
"pll_enet_50m_clk", "pll_enet_25m_clk",
- "pll_sys_main_120m_clk", "pll_audio_post_div", "pll_video_main_clk",
+ "pll_sys_main_120m_clk", "pll_audio_post_div", "pll_video_post_div",
"ext_clk_4", };
static const char *enet1_time_sel[] = { "osc", "pll_enet_100m_clk",
"pll_audio_post_div", "ext_clk_1", "ext_clk_2", "ext_clk_3",
- "ext_clk_4", "pll_video_main_clk", };
+ "ext_clk_4", "pll_video_post_div", };
static const char *enet2_ref_sel[] = { "osc", "pll_enet_125m_clk",
"pll_enet_50m_clk", "pll_enet_25m_clk",
- "pll_sys_main_120m_clk", "pll_audio_post_div", "pll_video_main_clk",
+ "pll_sys_main_120m_clk", "pll_audio_post_div", "pll_video_post_div",
"ext_clk_4", };
static const char *enet2_time_sel[] = { "osc", "pll_enet_100m_clk",
"pll_audio_post_div", "ext_clk_1", "ext_clk_2", "ext_clk_3",
- "ext_clk_4", "pll_video_main_clk", };
+ "ext_clk_4", "pll_video_post_div", };
static const char *enet_phy_ref_sel[] = { "osc", "pll_enet_25m_clk",
"pll_enet_50m_clk", "pll_enet_125m_clk",
- "pll_dram_533m_clk", "pll_audio_post_div", "pll_video_main_clk",
+ "pll_dram_533m_clk", "pll_audio_post_div", "pll_video_post_div",
"pll_sys_pfd3_clk", };
static const char *eim_sel[] = { "osc", "pll_sys_pfd2_135m_clk",
@@ -174,7 +174,7 @@ static const char *eim_sel[] = { "osc", "pll_sys_pfd2_135m_clk",
static const char *nand_sel[] = { "osc", "pll_sys_main_clk",
"pll_dram_533m_clk", "pll_sys_pfd0_392m_clk", "pll_sys_pfd3_clk",
"pll_enet_500m_clk", "pll_enet_250m_clk",
- "pll_video_main_clk", };
+ "pll_video_post_div", };
static const char *qspi_sel[] = { "osc", "pll_sys_pfd4_clk",
"pll_dram_533m_clk", "pll_enet_500m_clk", "pll_sys_pfd3_clk",
@@ -204,22 +204,22 @@ static const char *can2_sel[] = { "osc", "pll_sys_main_120m_clk",
static const char *i2c1_sel[] = { "osc", "pll_sys_main_120m_clk",
"pll_enet_50m_clk", "pll_dram_533m_clk",
- "pll_audio_post_div", "pll_video_main_clk", "pll_usb_main_clk",
+ "pll_audio_post_div", "pll_video_post_div", "pll_usb_main_clk",
"pll_sys_pfd2_135m_clk", };
static const char *i2c2_sel[] = { "osc", "pll_sys_main_120m_clk",
"pll_enet_50m_clk", "pll_dram_533m_clk",
- "pll_audio_post_div", "pll_video_main_clk", "pll_usb_main_clk",
+ "pll_audio_post_div", "pll_video_post_div", "pll_usb_main_clk",
"pll_sys_pfd2_135m_clk", };
static const char *i2c3_sel[] = { "osc", "pll_sys_main_120m_clk",
"pll_enet_50m_clk", "pll_dram_533m_clk",
- "pll_audio_post_div", "pll_video_main_clk", "pll_usb_main_clk",
+ "pll_audio_post_div", "pll_video_post_div", "pll_usb_main_clk",
"pll_sys_pfd2_135m_clk", };
static const char *i2c4_sel[] = { "osc", "pll_sys_main_120m_clk",
"pll_enet_50m_clk", "pll_dram_533m_clk",
- "pll_audio_post_div", "pll_video_main_clk", "pll_usb_main_clk",
+ "pll_audio_post_div", "pll_video_post_div", "pll_usb_main_clk",
"pll_sys_pfd2_135m_clk", };
static const char *uart1_sel[] = { "osc", "pll_sys_main_240m_clk",
@@ -279,27 +279,27 @@ static const char *ecspi4_sel[] = { "osc", "pll_sys_main_240m_clk",
static const char *pwm1_sel[] = { "osc", "pll_enet_100m_clk",
"pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_post_div",
- "ext_clk_1", "ref_1m_clk", "pll_video_main_clk", };
+ "ext_clk_1", "ref_1m_clk", "pll_video_post_div", };
static const char *pwm2_sel[] = { "osc", "pll_enet_100m_clk",
"pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_post_div",
- "ext_clk_1", "ref_1m_clk", "pll_video_main_clk", };
+ "ext_clk_1", "ref_1m_clk", "pll_video_post_div", };
static const char *pwm3_sel[] = { "osc", "pll_enet_100m_clk",
"pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_post_div",
- "ext_clk_2", "ref_1m_clk", "pll_video_main_clk", };
+ "ext_clk_2", "ref_1m_clk", "pll_video_post_div", };
static const char *pwm4_sel[] = { "osc", "pll_enet_100m_clk",
"pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_post_div",
- "ext_clk_2", "ref_1m_clk", "pll_video_main_clk", };
+ "ext_clk_2", "ref_1m_clk", "pll_video_post_div", };
static const char *flextimer1_sel[] = { "osc", "pll_enet_100m_clk",
"pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_post_div",
- "ext_clk_3", "ref_1m_clk", "pll_video_main_clk", };
+ "ext_clk_3", "ref_1m_clk", "pll_video_post_div", };
static const char *flextimer2_sel[] = { "osc", "pll_enet_100m_clk",
"pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_post_div",
- "ext_clk_3", "ref_1m_clk", "pll_video_main_clk", };
+ "ext_clk_3", "ref_1m_clk", "pll_video_post_div", };
static const char *sim1_sel[] = { "osc", "pll_sys_pfd2_135m_clk",
"pll_sys_main_120m_clk", "pll_dram_533m_clk",
@@ -308,23 +308,23 @@ static const char *sim1_sel[] = { "osc", "pll_sys_pfd2_135m_clk",
static const char *sim2_sel[] = { "osc", "pll_sys_pfd2_135m_clk",
"pll_sys_main_120m_clk", "pll_dram_533m_clk",
- "pll_usb_main_clk", "pll_video_main_clk", "pll_enet_125m_clk",
+ "pll_usb_main_clk", "pll_video_post_div", "pll_enet_125m_clk",
"pll_sys_pfd7_clk", };
static const char *gpt1_sel[] = { "osc", "pll_enet_100m_clk",
- "pll_sys_pfd0_392m_clk", "pll_enet_40m_clk", "pll_video_main_clk",
+ "pll_sys_pfd0_392m_clk", "pll_enet_40m_clk", "pll_video_post_div",
"ref_1m_clk", "pll_audio_post_div", "ext_clk_1", };
static const char *gpt2_sel[] = { "osc", "pll_enet_100m_clk",
- "pll_sys_pfd0_392m_clk", "pll_enet_40m_clk", "pll_video_main_clk",
+ "pll_sys_pfd0_392m_clk", "pll_enet_40m_clk", "pll_video_post_div",
"ref_1m_clk", "pll_audio_post_div", "ext_clk_2", };
static const char *gpt3_sel[] = { "osc", "pll_enet_100m_clk",
- "pll_sys_pfd0_392m_clk", "pll_enet_40m_clk", "pll_video_main_clk",
+ "pll_sys_pfd0_392m_clk", "pll_enet_40m_clk", "pll_video_post_div",
"ref_1m_clk", "pll_audio_post_div", "ext_clk_3", };
static const char *gpt4_sel[] = { "osc", "pll_enet_100m_clk",
- "pll_sys_pfd0_392m_clk", "pll_enet_40m_clk", "pll_video_main_clk",
+ "pll_sys_pfd0_392m_clk", "pll_enet_40m_clk", "pll_video_post_div",
"ref_1m_clk", "pll_audio_post_div", "ext_clk_4", };
static const char *trace_sel[] = { "osc", "pll_sys_pfd2_135m_clk",
@@ -339,12 +339,12 @@ static const char *wdog_sel[] = { "osc", "pll_sys_pfd2_135m_clk",
static const char *csi_mclk_sel[] = { "osc", "pll_sys_pfd2_135m_clk",
"pll_sys_main_120m_clk", "pll_dram_533m_clk",
- "pll_enet_125m_clk", "pll_audio_post_div", "pll_video_main_clk",
+ "pll_enet_125m_clk", "pll_audio_post_div", "pll_video_post_div",
"pll_usb_main_clk", };
static const char *audio_mclk_sel[] = { "osc", "pll_sys_pfd2_135m_clk",
"pll_sys_main_120m_clk", "pll_dram_533m_clk",
- "pll_enet_125m_clk", "pll_audio_post_div", "pll_video_main_clk",
+ "pll_enet_125m_clk", "pll_audio_post_div", "pll_video_post_div",
"pll_usb_main_clk", };
static const char *wrclk_sel[] = { "osc", "pll_enet_40m_clk",
@@ -358,13 +358,13 @@ static const char *clko1_sel[] = { "osc", "pll_sys_main_clk",
static const char *clko2_sel[] = { "osc", "pll_sys_main_240m_clk",
"pll_sys_pfd0_392m_clk", "pll_sys_pfd1_166m_clk", "pll_sys_pfd4_clk",
- "pll_audio_post_div", "pll_video_main_clk", "ckil", };
+ "pll_audio_post_div", "pll_video_post_div", "ckil", };
static const char *lvds1_sel[] = { "pll_arm_main_clk",
"pll_sys_main_clk", "pll_sys_pfd0_392m_clk", "pll_sys_pfd1_332m_clk",
"pll_sys_pfd2_270m_clk", "pll_sys_pfd3_clk", "pll_sys_pfd4_clk",
"pll_sys_pfd5_clk", "pll_sys_pfd6_clk", "pll_sys_pfd7_clk",
- "pll_audio_post_div", "pll_video_main_clk", "pll_enet_500m_clk",
+ "pll_audio_post_div", "pll_video_post_div", "pll_enet_500m_clk",
"pll_enet_250m_clk", "pll_enet_125m_clk", "pll_enet_100m_clk",
"pll_enet_50m_clk", "pll_enet_40m_clk", "pll_enet_25m_clk",
"pll_dram_main_clk", };
@@ -433,23 +433,22 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node)
clks[IMX7D_PLL_AUDIO_MAIN_BYPASS] = imx_clk_mux_flags("pll_audio_main_bypass", base + 0xf0, 16, 1, pll_audio_bypass_sel, ARRAY_SIZE(pll_audio_bypass_sel), CLK_SET_RATE_PARENT);
clks[IMX7D_PLL_VIDEO_MAIN_BYPASS] = imx_clk_mux_flags("pll_video_main_bypass", base + 0x130, 16, 1, pll_video_bypass_sel, ARRAY_SIZE(pll_video_bypass_sel), CLK_SET_RATE_PARENT);
- clk_set_parent(clks[IMX7D_PLL_ARM_MAIN_BYPASS], clks[IMX7D_PLL_ARM_MAIN]);
- clk_set_parent(clks[IMX7D_PLL_DRAM_MAIN_BYPASS], clks[IMX7D_PLL_DRAM_MAIN]);
- clk_set_parent(clks[IMX7D_PLL_SYS_MAIN_BYPASS], clks[IMX7D_PLL_SYS_MAIN]);
- clk_set_parent(clks[IMX7D_PLL_ENET_MAIN_BYPASS], clks[IMX7D_PLL_ENET_MAIN]);
- clk_set_parent(clks[IMX7D_PLL_AUDIO_MAIN_BYPASS], clks[IMX7D_PLL_AUDIO_MAIN]);
- clk_set_parent(clks[IMX7D_PLL_VIDEO_MAIN_BYPASS], clks[IMX7D_PLL_VIDEO_MAIN]);
-
clks[IMX7D_PLL_ARM_MAIN_CLK] = imx_clk_gate("pll_arm_main_clk", "pll_arm_main_bypass", base + 0x60, 13);
- clks[IMX7D_PLL_DRAM_MAIN_CLK] = imx_clk_gate("pll_dram_main_clk", "pll_dram_main_bypass", base + 0x70, 13);
+ clks[IMX7D_PLL_DRAM_MAIN_CLK] = imx_clk_gate("pll_dram_main_clk", "pll_dram_test_div", base + 0x70, 13);
clks[IMX7D_PLL_SYS_MAIN_CLK] = imx_clk_gate("pll_sys_main_clk", "pll_sys_main_bypass", base + 0xb0, 13);
clks[IMX7D_PLL_AUDIO_MAIN_CLK] = imx_clk_gate("pll_audio_main_clk", "pll_audio_main_bypass", base + 0xf0, 13);
clks[IMX7D_PLL_VIDEO_MAIN_CLK] = imx_clk_gate("pll_video_main_clk", "pll_video_main_bypass", base + 0x130, 13);
+ clks[IMX7D_PLL_DRAM_TEST_DIV] = clk_register_divider_table(NULL, "pll_dram_test_div", "pll_dram_main_bypass",
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0x70, 21, 2, 0, test_div_table, &imx_ccm_lock);
clks[IMX7D_PLL_AUDIO_TEST_DIV] = clk_register_divider_table(NULL, "pll_audio_test_div", "pll_audio_main_clk",
CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0xf0, 19, 2, 0, test_div_table, &imx_ccm_lock);
clks[IMX7D_PLL_AUDIO_POST_DIV] = clk_register_divider_table(NULL, "pll_audio_post_div", "pll_audio_test_div",
CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0xf0, 22, 2, 0, post_div_table, &imx_ccm_lock);
+ clks[IMX7D_PLL_VIDEO_TEST_DIV] = clk_register_divider_table(NULL, "pll_video_test_div", "pll_video_main_clk",
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0x130, 19, 2, 0, test_div_table, &imx_ccm_lock);
+ clks[IMX7D_PLL_VIDEO_POST_DIV] = clk_register_divider_table(NULL, "pll_video_post_div", "pll_video_test_div",
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0x130, 22, 2, 0, post_div_table, &imx_ccm_lock);
clks[IMX7D_PLL_SYS_PFD0_392M_CLK] = imx_clk_pfd("pll_sys_pfd0_392m_clk", "pll_sys_main_clk", base + 0xc0, 0);
clks[IMX7D_PLL_SYS_PFD1_332M_CLK] = imx_clk_pfd("pll_sys_pfd1_332m_clk", "pll_sys_main_clk", base + 0xc0, 1);
@@ -795,7 +794,9 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node)
clks[IMX7D_DRAM_PHYM_ALT_ROOT_CLK] = imx_clk_gate4("dram_phym_alt_root_clk", "dram_phym_alt_post_div", base + 0x4130, 0);
clks[IMX7D_DRAM_ALT_ROOT_CLK] = imx_clk_gate4("dram_alt_root_clk", "dram_alt_post_div", base + 0x4130, 0);
clks[IMX7D_OCOTP_CLK] = imx_clk_gate4("ocotp_clk", "ipg_root_clk", base + 0x4230, 0);
- clks[IMX7D_USB_HSIC_ROOT_CLK] = imx_clk_gate4("usb_hsic_root_clk", "usb_hsic_post_div", base + 0x4420, 0);
+ clks[IMX7D_SNVS_CLK] = imx_clk_gate4("snvs_clk", "ipg_root_clk", base + 0x4250, 0);
+ clks[IMX7D_CAAM_CLK] = imx_clk_gate4("caam_clk", "ipg_root_clk", base + 0x4240, 0);
+ clks[IMX7D_USB_HSIC_ROOT_CLK] = imx_clk_gate4("usb_hsic_root_clk", "usb_hsic_post_div", base + 0x4690, 0);
clks[IMX7D_SDMA_CORE_CLK] = imx_clk_gate4("sdma_root_clk", "ahb_root_clk", base + 0x4480, 0);
clks[IMX7D_PCIE_CTRL_ROOT_CLK] = imx_clk_gate4("pcie_ctrl_root_clk", "pcie_ctrl_post_div", base + 0x4600, 0);
clks[IMX7D_PCIE_PHY_ROOT_CLK] = imx_clk_gate4("pcie_phy_root_clk", "pcie_phy_post_div", base + 0x4600, 0);
@@ -857,9 +858,13 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node)
clks[IMX7D_WDOG2_ROOT_CLK] = imx_clk_gate4("wdog2_root_clk", "wdog_post_div", base + 0x49d0, 0);
clks[IMX7D_WDOG3_ROOT_CLK] = imx_clk_gate4("wdog3_root_clk", "wdog_post_div", base + 0x49e0, 0);
clks[IMX7D_WDOG4_ROOT_CLK] = imx_clk_gate4("wdog4_root_clk", "wdog_post_div", base + 0x49f0, 0);
+ clks[IMX7D_KPP_ROOT_CLK] = imx_clk_gate4("kpp_root_clk", "ipg_root_clk", base + 0x4aa0, 0);
clks[IMX7D_CSI_MCLK_ROOT_CLK] = imx_clk_gate4("csi_mclk_root_clk", "csi_mclk_post_div", base + 0x4490, 0);
clks[IMX7D_AUDIO_MCLK_ROOT_CLK] = imx_clk_gate4("audio_mclk_root_clk", "audio_mclk_post_div", base + 0x4790, 0);
clks[IMX7D_WRCLK_ROOT_CLK] = imx_clk_gate4("wrclk_root_clk", "wrclk_post_div", base + 0x47a0, 0);
+ clks[IMX7D_USB_CTRL_CLK] = imx_clk_gate4("usb_ctrl_clk", "ahb_root_clk", base + 0x4680, 0);
+ clks[IMX7D_USB_PHY1_CLK] = imx_clk_gate4("usb_phy1_clk", "pll_usb1_main_clk", base + 0x46a0, 0);
+ clks[IMX7D_USB_PHY2_CLK] = imx_clk_gate4("usb_phy2_clk", "pll_usb_main_clk", base + 0x46b0, 0);
clks[IMX7D_ADC_ROOT_CLK] = imx_clk_gate4("adc_root_clk", "ipg_root_clk", base + 0x4200, 0);
clks[IMX7D_GPT_3M_CLK] = imx_clk_fixed_factor("gpt_3m", "osc", 1, 8);
@@ -879,12 +884,23 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node)
for (i = 0; i < ARRAY_SIZE(clks_init_on); i++)
clk_prepare_enable(clks[clks_init_on[i]]);
+ clk_set_parent(clks[IMX7D_PLL_ARM_MAIN_BYPASS], clks[IMX7D_PLL_ARM_MAIN]);
+ clk_set_parent(clks[IMX7D_PLL_DRAM_MAIN_BYPASS], clks[IMX7D_PLL_DRAM_MAIN]);
+ clk_set_parent(clks[IMX7D_PLL_SYS_MAIN_BYPASS], clks[IMX7D_PLL_SYS_MAIN]);
+ clk_set_parent(clks[IMX7D_PLL_ENET_MAIN_BYPASS], clks[IMX7D_PLL_ENET_MAIN]);
+ clk_set_parent(clks[IMX7D_PLL_AUDIO_MAIN_BYPASS], clks[IMX7D_PLL_AUDIO_MAIN]);
+ clk_set_parent(clks[IMX7D_PLL_VIDEO_MAIN_BYPASS], clks[IMX7D_PLL_VIDEO_MAIN]);
+
/* use old gpt clk setting, gpt1 root clk must be twice as gpt counter freq */
clk_set_parent(clks[IMX7D_GPT1_ROOT_SRC], clks[IMX7D_OSC_24M_CLK]);
/* set uart module clock's parent clock source that must be great then 80MHz */
clk_set_parent(clks[IMX7D_UART1_ROOT_SRC], clks[IMX7D_OSC_24M_CLK]);
+ /* Set clock rate for USBPHY, the USB_PLL at CCM is from USBOTG2 */
+ clks[IMX7D_USB1_MAIN_480M_CLK] = imx_clk_fixed_factor("pll_usb1_main_clk", "osc", 20, 1);
+ clks[IMX7D_USB_MAIN_480M_CLK] = imx_clk_fixed_factor("pll_usb_main_clk", "osc", 20, 1);
+
imx_register_uart_clocks(uart_clks);
}
diff --git a/drivers/clk/imx/clk-pllv2.c b/drivers/clk/imx/clk-pllv2.c
index 85b5cbe9744c..eeba3cb14e2d 100644
--- a/drivers/clk/imx/clk-pllv2.c
+++ b/drivers/clk/imx/clk-pllv2.c
@@ -182,8 +182,12 @@ static long clk_pllv2_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
u32 dp_op, dp_mfd, dp_mfn;
+ int ret;
+
+ ret = __clk_pllv2_set_rate(rate, *prate, &dp_op, &dp_mfd, &dp_mfn);
+ if (ret)
+ return ret;
- __clk_pllv2_set_rate(rate, *prate, &dp_op, &dp_mfd, &dp_mfn);
return __clk_pllv2_recalc_rate(*prate, MXC_PLL_DP_CTL_DPDCK0_2_EN,
dp_op, dp_mfd, dp_mfn);
}
diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h
index d69c4bbf3597..8076ec040f37 100644
--- a/drivers/clk/imx/clk.h
+++ b/drivers/clk/imx/clk.h
@@ -123,6 +123,13 @@ static inline struct clk *imx_clk_gate(const char *name, const char *parent,
shift, 0, &imx_ccm_lock);
}
+static inline struct clk *imx_clk_gate_flags(const char *name, const char *parent,
+ void __iomem *reg, u8 shift, unsigned long flags)
+{
+ return clk_register_gate(NULL, name, parent, flags | CLK_SET_RATE_PARENT, reg,
+ shift, 0, &imx_ccm_lock);
+}
+
static inline struct clk *imx_clk_gate_dis(const char *name, const char *parent,
void __iomem *reg, u8 shift)
{
@@ -137,6 +144,13 @@ static inline struct clk *imx_clk_gate2(const char *name, const char *parent,
shift, 0x3, 0, &imx_ccm_lock, NULL);
}
+static inline struct clk *imx_clk_gate2_flags(const char *name, const char *parent,
+ void __iomem *reg, u8 shift, unsigned long flags)
+{
+ return clk_register_gate2(NULL, name, parent, flags | CLK_SET_RATE_PARENT, reg,
+ shift, 0x3, 0, &imx_ccm_lock, NULL);
+}
+
static inline struct clk *imx_clk_gate2_shared(const char *name,
const char *parent, void __iomem *reg, u8 shift,
unsigned int *share_count)
diff --git a/drivers/clk/keystone/sci-clk.c b/drivers/clk/keystone/sci-clk.c
index 9cdf9d5050ac..4cb70bed89a9 100644
--- a/drivers/clk/keystone/sci-clk.c
+++ b/drivers/clk/keystone/sci-clk.c
@@ -29,21 +29,10 @@
#define SCI_CLK_INPUT_TERMINATION BIT(2)
/**
- * struct sci_clk_data - TI SCI clock data
- * @dev: device index
- * @num_clks: number of clocks for this device
- */
-struct sci_clk_data {
- u16 dev;
- u16 num_clks;
-};
-
-/**
* struct sci_clk_provider - TI SCI clock provider representation
* @sci: Handle to the System Control Interface protocol handler
* @ops: Pointer to the SCI ops to be used by the clocks
* @dev: Device pointer for the clock provider
- * @clk_data: Clock data
* @clocks: Clocks array for this device
* @num_clocks: Total number of clocks for this provider
*/
@@ -51,8 +40,7 @@ struct sci_clk_provider {
const struct ti_sci_handle *sci;
const struct ti_sci_clk_ops *ops;
struct device *dev;
- const struct sci_clk_data *clk_data;
- struct clk_hw **clocks;
+ struct sci_clk **clocks;
int num_clocks;
};
@@ -61,6 +49,7 @@ struct sci_clk_provider {
* @hw: Hardware clock cookie for common clock framework
* @dev_id: Device index
* @clk_id: Clock index
+ * @num_parents: Number of parents for this clock
* @provider: Master clock provider
* @flags: Flags for the clock
*/
@@ -68,6 +57,7 @@ struct sci_clk {
struct clk_hw hw;
u16 dev_id;
u8 clk_id;
+ u8 num_parents;
struct sci_clk_provider *provider;
u8 flags;
};
@@ -273,38 +263,22 @@ static const struct clk_ops sci_clk_ops = {
/**
* _sci_clk_get - Gets a handle for an SCI clock
* @provider: Handle to SCI clock provider
- * @dev_id: device ID for the clock to register
- * @clk_id: clock ID for the clock to register
+ * @sci_clk: Handle to the SCI clock to populate
*
* Gets a handle to an existing TI SCI hw clock, or builds a new clock
* entry and registers it with the common clock framework. Called from
* the common clock framework, when a corresponding of_clk_get call is
* executed, or recursively from itself when parsing parent clocks.
- * Returns a pointer to the hw clock struct, or ERR_PTR value in failure.
+ * Returns 0 on success, negative error code on failure.
*/
-static struct clk_hw *_sci_clk_build(struct sci_clk_provider *provider,
- u16 dev_id, u8 clk_id)
+static int _sci_clk_build(struct sci_clk_provider *provider,
+ struct sci_clk *sci_clk)
{
struct clk_init_data init = { NULL };
- struct sci_clk *sci_clk = NULL;
char *name = NULL;
char **parent_names = NULL;
int i;
- int ret;
-
- sci_clk = devm_kzalloc(provider->dev, sizeof(*sci_clk), GFP_KERNEL);
- if (!sci_clk)
- return ERR_PTR(-ENOMEM);
-
- sci_clk->dev_id = dev_id;
- sci_clk->clk_id = clk_id;
- sci_clk->provider = provider;
-
- ret = provider->ops->get_num_parents(provider->sci, dev_id,
- clk_id,
- &init.num_parents);
- if (ret)
- goto err;
+ int ret = 0;
name = kasprintf(GFP_KERNEL, "%s:%d:%d", dev_name(provider->dev),
sci_clk->dev_id, sci_clk->clk_id);
@@ -317,11 +291,11 @@ static struct clk_hw *_sci_clk_build(struct sci_clk_provider *provider,
* to have mux functionality. Otherwise it is going to act as a root
* clock.
*/
- if (init.num_parents < 2)
- init.num_parents = 0;
+ if (sci_clk->num_parents < 2)
+ sci_clk->num_parents = 0;
- if (init.num_parents) {
- parent_names = kcalloc(init.num_parents, sizeof(char *),
+ if (sci_clk->num_parents) {
+ parent_names = kcalloc(sci_clk->num_parents, sizeof(char *),
GFP_KERNEL);
if (!parent_names) {
@@ -329,7 +303,7 @@ static struct clk_hw *_sci_clk_build(struct sci_clk_provider *provider,
goto err;
}
- for (i = 0; i < init.num_parents; i++) {
+ for (i = 0; i < sci_clk->num_parents; i++) {
char *parent_name;
parent_name = kasprintf(GFP_KERNEL, "%s:%d:%d",
@@ -346,6 +320,7 @@ static struct clk_hw *_sci_clk_build(struct sci_clk_provider *provider,
}
init.ops = &sci_clk_ops;
+ init.num_parents = sci_clk->num_parents;
sci_clk->hw.init = &init;
ret = devm_clk_hw_register(provider->dev, &sci_clk->hw);
@@ -354,7 +329,7 @@ static struct clk_hw *_sci_clk_build(struct sci_clk_provider *provider,
err:
if (parent_names) {
- for (i = 0; i < init.num_parents; i++)
+ for (i = 0; i < sci_clk->num_parents; i++)
kfree(parent_names[i]);
kfree(parent_names);
@@ -362,10 +337,7 @@ err:
kfree(name);
- if (ret)
- return ERR_PTR(ret);
-
- return &sci_clk->hw;
+ return ret;
}
static int _cmp_sci_clk(const void *a, const void *b)
@@ -414,253 +386,20 @@ static struct clk_hw *sci_clk_get(struct of_phandle_args *clkspec, void *data)
static int ti_sci_init_clocks(struct sci_clk_provider *p)
{
- const struct sci_clk_data *data = p->clk_data;
- struct clk_hw *hw;
int i;
- int num_clks = 0;
-
- while (data->num_clks) {
- num_clks += data->num_clks;
- data++;
- }
-
- p->num_clocks = num_clks;
-
- p->clocks = devm_kcalloc(p->dev, num_clks, sizeof(struct sci_clk),
- GFP_KERNEL);
- if (!p->clocks)
- return -ENOMEM;
-
- num_clks = 0;
-
- data = p->clk_data;
-
- while (data->num_clks) {
- for (i = 0; i < data->num_clks; i++) {
- hw = _sci_clk_build(p, data->dev, i);
- if (!IS_ERR(hw)) {
- p->clocks[num_clks++] = hw;
- continue;
- }
-
- /* Skip any holes in the clock lists */
- if (PTR_ERR(hw) == -ENODEV)
- continue;
+ int ret;
- return PTR_ERR(hw);
- }
- data++;
+ for (i = 0; i < p->num_clocks; i++) {
+ ret = _sci_clk_build(p, p->clocks[i]);
+ if (ret)
+ return ret;
}
return 0;
}
-static const struct sci_clk_data k2g_clk_data[] = {
- /* pmmc */
- { .dev = 0x0, .num_clks = 4 },
-
- /* mlb0 */
- { .dev = 0x1, .num_clks = 5 },
-
- /* dss0 */
- { .dev = 0x2, .num_clks = 2 },
-
- /* mcbsp0 */
- { .dev = 0x3, .num_clks = 8 },
-
- /* mcasp0 */
- { .dev = 0x4, .num_clks = 8 },
-
- /* mcasp1 */
- { .dev = 0x5, .num_clks = 8 },
-
- /* mcasp2 */
- { .dev = 0x6, .num_clks = 8 },
-
- /* dcan0 */
- { .dev = 0x8, .num_clks = 2 },
-
- /* dcan1 */
- { .dev = 0x9, .num_clks = 2 },
-
- /* emif0 */
- { .dev = 0xa, .num_clks = 6 },
-
- /* mmchs0 */
- { .dev = 0xb, .num_clks = 3 },
-
- /* mmchs1 */
- { .dev = 0xc, .num_clks = 3 },
-
- /* gpmc0 */
- { .dev = 0xd, .num_clks = 1 },
-
- /* elm0 */
- { .dev = 0xe, .num_clks = 1 },
-
- /* spi0 */
- { .dev = 0x10, .num_clks = 1 },
-
- /* spi1 */
- { .dev = 0x11, .num_clks = 1 },
-
- /* spi2 */
- { .dev = 0x12, .num_clks = 1 },
-
- /* spi3 */
- { .dev = 0x13, .num_clks = 1 },
-
- /* icss0 */
- { .dev = 0x14, .num_clks = 6 },
-
- /* icss1 */
- { .dev = 0x15, .num_clks = 6 },
-
- /* usb0 */
- { .dev = 0x16, .num_clks = 7 },
-
- /* usb1 */
- { .dev = 0x17, .num_clks = 7 },
-
- /* nss0 */
- { .dev = 0x18, .num_clks = 14 },
-
- /* pcie0 */
- { .dev = 0x19, .num_clks = 1 },
-
- /* gpio0 */
- { .dev = 0x1b, .num_clks = 1 },
-
- /* gpio1 */
- { .dev = 0x1c, .num_clks = 1 },
-
- /* timer64_0 */
- { .dev = 0x1d, .num_clks = 9 },
-
- /* timer64_1 */
- { .dev = 0x1e, .num_clks = 9 },
-
- /* timer64_2 */
- { .dev = 0x1f, .num_clks = 9 },
-
- /* timer64_3 */
- { .dev = 0x20, .num_clks = 9 },
-
- /* timer64_4 */
- { .dev = 0x21, .num_clks = 9 },
-
- /* timer64_5 */
- { .dev = 0x22, .num_clks = 9 },
-
- /* timer64_6 */
- { .dev = 0x23, .num_clks = 9 },
-
- /* msgmgr0 */
- { .dev = 0x25, .num_clks = 1 },
-
- /* bootcfg0 */
- { .dev = 0x26, .num_clks = 1 },
-
- /* arm_bootrom0 */
- { .dev = 0x27, .num_clks = 1 },
-
- /* dsp_bootrom0 */
- { .dev = 0x29, .num_clks = 1 },
-
- /* debugss0 */
- { .dev = 0x2b, .num_clks = 8 },
-
- /* uart0 */
- { .dev = 0x2c, .num_clks = 1 },
-
- /* uart1 */
- { .dev = 0x2d, .num_clks = 1 },
-
- /* uart2 */
- { .dev = 0x2e, .num_clks = 1 },
-
- /* ehrpwm0 */
- { .dev = 0x2f, .num_clks = 1 },
-
- /* ehrpwm1 */
- { .dev = 0x30, .num_clks = 1 },
-
- /* ehrpwm2 */
- { .dev = 0x31, .num_clks = 1 },
-
- /* ehrpwm3 */
- { .dev = 0x32, .num_clks = 1 },
-
- /* ehrpwm4 */
- { .dev = 0x33, .num_clks = 1 },
-
- /* ehrpwm5 */
- { .dev = 0x34, .num_clks = 1 },
-
- /* eqep0 */
- { .dev = 0x35, .num_clks = 1 },
-
- /* eqep1 */
- { .dev = 0x36, .num_clks = 1 },
-
- /* eqep2 */
- { .dev = 0x37, .num_clks = 1 },
-
- /* ecap0 */
- { .dev = 0x38, .num_clks = 1 },
-
- /* ecap1 */
- { .dev = 0x39, .num_clks = 1 },
-
- /* i2c0 */
- { .dev = 0x3a, .num_clks = 1 },
-
- /* i2c1 */
- { .dev = 0x3b, .num_clks = 1 },
-
- /* i2c2 */
- { .dev = 0x3c, .num_clks = 1 },
-
- /* edma0 */
- { .dev = 0x3f, .num_clks = 2 },
-
- /* semaphore0 */
- { .dev = 0x40, .num_clks = 1 },
-
- /* intc0 */
- { .dev = 0x41, .num_clks = 1 },
-
- /* gic0 */
- { .dev = 0x42, .num_clks = 1 },
-
- /* qspi0 */
- { .dev = 0x43, .num_clks = 5 },
-
- /* arm_64b_counter0 */
- { .dev = 0x44, .num_clks = 2 },
-
- /* tetris0 */
- { .dev = 0x45, .num_clks = 2 },
-
- /* cgem0 */
- { .dev = 0x46, .num_clks = 2 },
-
- /* msmc0 */
- { .dev = 0x47, .num_clks = 1 },
-
- /* cbass0 */
- { .dev = 0x49, .num_clks = 1 },
-
- /* board0 */
- { .dev = 0x4c, .num_clks = 36 },
-
- /* edma1 */
- { .dev = 0x4f, .num_clks = 2 },
- { .num_clks = 0 },
-};
-
static const struct of_device_id ti_sci_clk_of_match[] = {
- { .compatible = "ti,k2g-sci-clk", .data = &k2g_clk_data },
+ { .compatible = "ti,k2g-sci-clk" },
{ /* Sentinel */ },
};
MODULE_DEVICE_TABLE(of, ti_sci_clk_of_match);
@@ -681,12 +420,16 @@ static int ti_sci_clk_probe(struct platform_device *pdev)
struct device_node *np = dev->of_node;
struct sci_clk_provider *provider;
const struct ti_sci_handle *handle;
- const struct sci_clk_data *data;
int ret;
-
- data = of_device_get_match_data(dev);
- if (!data)
- return -EINVAL;
+ int num_clks = 0;
+ struct sci_clk **clks = NULL;
+ struct sci_clk **tmp_clks;
+ struct sci_clk *sci_clk;
+ int max_clks = 0;
+ int clk_id = 0;
+ int dev_id = 0;
+ u8 num_parents;
+ int gap_size = 0;
handle = devm_ti_sci_get_handle(dev);
if (IS_ERR(handle))
@@ -696,12 +439,69 @@ static int ti_sci_clk_probe(struct platform_device *pdev)
if (!provider)
return -ENOMEM;
- provider->clk_data = data;
-
provider->sci = handle;
provider->ops = &handle->ops.clk_ops;
provider->dev = dev;
+ while (1) {
+ ret = provider->ops->get_num_parents(provider->sci, dev_id,
+ clk_id, &num_parents);
+ if (ret) {
+ gap_size++;
+ if (!clk_id) {
+ if (gap_size >= 5)
+ break;
+ dev_id++;
+ } else {
+ if (gap_size >= 2) {
+ dev_id++;
+ clk_id = 0;
+ gap_size = 0;
+ } else {
+ clk_id++;
+ }
+ }
+ continue;
+ }
+
+ gap_size = 0;
+
+ if (num_clks == max_clks) {
+ tmp_clks = devm_kmalloc_array(dev, max_clks + 64,
+ sizeof(sci_clk),
+ GFP_KERNEL);
+ memcpy(tmp_clks, clks, max_clks * sizeof(sci_clk));
+ if (max_clks)
+ devm_kfree(dev, clks);
+ max_clks += 64;
+ clks = tmp_clks;
+ }
+
+ sci_clk = devm_kzalloc(dev, sizeof(*sci_clk), GFP_KERNEL);
+ if (!sci_clk)
+ return -ENOMEM;
+ sci_clk->dev_id = dev_id;
+ sci_clk->clk_id = clk_id;
+ sci_clk->provider = provider;
+ sci_clk->num_parents = num_parents;
+
+ clks[num_clks] = sci_clk;
+
+ clk_id++;
+ num_clks++;
+ }
+
+ provider->clocks = devm_kmalloc_array(dev, num_clks, sizeof(sci_clk),
+ GFP_KERNEL);
+ if (!provider->clocks)
+ return -ENOMEM;
+
+ memcpy(provider->clocks, clks, num_clks * sizeof(sci_clk));
+
+ provider->num_clocks = num_clks;
+
+ devm_kfree(dev, clks);
+
ret = ti_sci_init_clocks(provider);
if (ret) {
pr_err("ti-sci-init-clocks failed.\n");
diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig
index 1f9ea0f21df1..92afe5989e97 100644
--- a/drivers/clk/mediatek/Kconfig
+++ b/drivers/clk/mediatek/Kconfig
@@ -54,6 +54,12 @@ config COMMON_CLK_MT2701_BDPSYS
---help---
This driver supports MediaTek MT2701 bdpsys clocks.
+config COMMON_CLK_MT2701_AUDSYS
+ bool "Clock driver for Mediatek MT2701 audsys"
+ depends on COMMON_CLK_MT2701
+ ---help---
+ This driver supports Mediatek MT2701 audsys clocks.
+
config COMMON_CLK_MT2712
bool "Clock driver for MediaTek MT2712"
depends on (ARCH_MEDIATEK && ARM64) || COMPILE_TEST
diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
index 5160fdc4bbb8..b80eff2abb31 100644
--- a/drivers/clk/mediatek/Makefile
+++ b/drivers/clk/mediatek/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_COMMON_CLK_MT6797_MMSYS) += clk-mt6797-mm.o
obj-$(CONFIG_COMMON_CLK_MT6797_VDECSYS) += clk-mt6797-vdec.o
obj-$(CONFIG_COMMON_CLK_MT6797_VENCSYS) += clk-mt6797-venc.o
obj-$(CONFIG_COMMON_CLK_MT2701) += clk-mt2701.o
+obj-$(CONFIG_COMMON_CLK_MT2701_AUDSYS) += clk-mt2701-aud.o
obj-$(CONFIG_COMMON_CLK_MT2701_BDPSYS) += clk-mt2701-bdp.o
obj-$(CONFIG_COMMON_CLK_MT2701_ETHSYS) += clk-mt2701-eth.o
obj-$(CONFIG_COMMON_CLK_MT2701_HIFSYS) += clk-mt2701-hif.o
diff --git a/drivers/clk/mediatek/clk-mt2701-aud.c b/drivers/clk/mediatek/clk-mt2701-aud.c
new file mode 100644
index 000000000000..e66896a44fad
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt2701-aud.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Ryder Lee <ryder.lee@mediatek.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-gate.h"
+
+#include <dt-bindings/clock/mt2701-clk.h>
+
+#define GATE_AUDIO0(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &audio0_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_no_setclr, \
+ }
+
+#define GATE_AUDIO1(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &audio1_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_no_setclr, \
+ }
+
+#define GATE_AUDIO2(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &audio2_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_no_setclr, \
+ }
+
+#define GATE_AUDIO3(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &audio3_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_no_setclr, \
+ }
+
+static const struct mtk_gate_regs audio0_cg_regs = {
+ .set_ofs = 0x0,
+ .clr_ofs = 0x0,
+ .sta_ofs = 0x0,
+};
+
+static const struct mtk_gate_regs audio1_cg_regs = {
+ .set_ofs = 0x10,
+ .clr_ofs = 0x10,
+ .sta_ofs = 0x10,
+};
+
+static const struct mtk_gate_regs audio2_cg_regs = {
+ .set_ofs = 0x14,
+ .clr_ofs = 0x14,
+ .sta_ofs = 0x14,
+};
+
+static const struct mtk_gate_regs audio3_cg_regs = {
+ .set_ofs = 0x634,
+ .clr_ofs = 0x634,
+ .sta_ofs = 0x634,
+};
+
+static const struct mtk_gate audio_clks[] = {
+ /* AUDIO0 */
+ GATE_AUDIO0(CLK_AUD_AFE, "audio_afe", "aud_intbus_sel", 2),
+ GATE_AUDIO0(CLK_AUD_HDMI, "audio_hdmi", "audpll_sel", 20),
+ GATE_AUDIO0(CLK_AUD_SPDF, "audio_spdf", "audpll_sel", 21),
+ GATE_AUDIO0(CLK_AUD_SPDF2, "audio_spdf2", "audpll_sel", 22),
+ GATE_AUDIO0(CLK_AUD_APLL, "audio_apll", "audpll_sel", 23),
+ /* AUDIO1 */
+ GATE_AUDIO1(CLK_AUD_I2SIN1, "audio_i2sin1", "aud_mux1_sel", 0),
+ GATE_AUDIO1(CLK_AUD_I2SIN2, "audio_i2sin2", "aud_mux1_sel", 1),
+ GATE_AUDIO1(CLK_AUD_I2SIN3, "audio_i2sin3", "aud_mux1_sel", 2),
+ GATE_AUDIO1(CLK_AUD_I2SIN4, "audio_i2sin4", "aud_mux1_sel", 3),
+ GATE_AUDIO1(CLK_AUD_I2SIN5, "audio_i2sin5", "aud_mux1_sel", 4),
+ GATE_AUDIO1(CLK_AUD_I2SIN6, "audio_i2sin6", "aud_mux1_sel", 5),
+ GATE_AUDIO1(CLK_AUD_I2SO1, "audio_i2so1", "aud_mux1_sel", 6),
+ GATE_AUDIO1(CLK_AUD_I2SO2, "audio_i2so2", "aud_mux1_sel", 7),
+ GATE_AUDIO1(CLK_AUD_I2SO3, "audio_i2so3", "aud_mux1_sel", 8),
+ GATE_AUDIO1(CLK_AUD_I2SO4, "audio_i2so4", "aud_mux1_sel", 9),
+ GATE_AUDIO1(CLK_AUD_I2SO5, "audio_i2so5", "aud_mux1_sel", 10),
+ GATE_AUDIO1(CLK_AUD_I2SO6, "audio_i2so6", "aud_mux1_sel", 11),
+ GATE_AUDIO1(CLK_AUD_ASRCI1, "audio_asrci1", "asm_h_sel", 12),
+ GATE_AUDIO1(CLK_AUD_ASRCI2, "audio_asrci2", "asm_h_sel", 13),
+ GATE_AUDIO1(CLK_AUD_ASRCO1, "audio_asrco1", "asm_h_sel", 14),
+ GATE_AUDIO1(CLK_AUD_ASRCO2, "audio_asrco2", "asm_h_sel", 15),
+ GATE_AUDIO1(CLK_AUD_INTDIR, "audio_intdir", "intdir_sel", 20),
+ GATE_AUDIO1(CLK_AUD_A1SYS, "audio_a1sys", "aud_mux1_sel", 21),
+ GATE_AUDIO1(CLK_AUD_A2SYS, "audio_a2sys", "aud_mux2_sel", 22),
+ GATE_AUDIO1(CLK_AUD_AFE_CONN, "audio_afe_conn", "aud_mux1_sel", 23),
+ GATE_AUDIO1(CLK_AUD_AFE_MRGIF, "audio_afe_mrgif", "aud_mux1_sel", 25),
+ /* AUDIO2 */
+ GATE_AUDIO2(CLK_AUD_MMIF_UL1, "audio_ul1", "aud_mux1_sel", 0),
+ GATE_AUDIO2(CLK_AUD_MMIF_UL2, "audio_ul2", "aud_mux1_sel", 1),
+ GATE_AUDIO2(CLK_AUD_MMIF_UL3, "audio_ul3", "aud_mux1_sel", 2),
+ GATE_AUDIO2(CLK_AUD_MMIF_UL4, "audio_ul4", "aud_mux1_sel", 3),
+ GATE_AUDIO2(CLK_AUD_MMIF_UL5, "audio_ul5", "aud_mux1_sel", 4),
+ GATE_AUDIO2(CLK_AUD_MMIF_UL6, "audio_ul6", "aud_mux1_sel", 5),
+ GATE_AUDIO2(CLK_AUD_MMIF_DL1, "audio_dl1", "aud_mux1_sel", 6),
+ GATE_AUDIO2(CLK_AUD_MMIF_DL2, "audio_dl2", "aud_mux1_sel", 7),
+ GATE_AUDIO2(CLK_AUD_MMIF_DL3, "audio_dl3", "aud_mux1_sel", 8),
+ GATE_AUDIO2(CLK_AUD_MMIF_DL4, "audio_dl4", "aud_mux1_sel", 9),
+ GATE_AUDIO2(CLK_AUD_MMIF_DL5, "audio_dl5", "aud_mux1_sel", 10),
+ GATE_AUDIO2(CLK_AUD_MMIF_DL6, "audio_dl6", "aud_mux1_sel", 11),
+ GATE_AUDIO2(CLK_AUD_MMIF_DLMCH, "audio_dlmch", "aud_mux1_sel", 12),
+ GATE_AUDIO2(CLK_AUD_MMIF_ARB1, "audio_arb1", "aud_mux1_sel", 13),
+ GATE_AUDIO2(CLK_AUD_MMIF_AWB1, "audio_awb", "aud_mux1_sel", 14),
+ GATE_AUDIO2(CLK_AUD_MMIF_AWB2, "audio_awb2", "aud_mux1_sel", 15),
+ GATE_AUDIO2(CLK_AUD_MMIF_DAI, "audio_dai", "aud_mux1_sel", 16),
+ /* AUDIO3 */
+ GATE_AUDIO3(CLK_AUD_ASRCI3, "audio_asrci3", "asm_h_sel", 2),
+ GATE_AUDIO3(CLK_AUD_ASRCI4, "audio_asrci4", "asm_h_sel", 3),
+ GATE_AUDIO3(CLK_AUD_ASRCI5, "audio_asrci5", "asm_h_sel", 4),
+ GATE_AUDIO3(CLK_AUD_ASRCI6, "audio_asrci6", "asm_h_sel", 5),
+ GATE_AUDIO3(CLK_AUD_ASRCO3, "audio_asrco3", "asm_h_sel", 6),
+ GATE_AUDIO3(CLK_AUD_ASRCO4, "audio_asrco4", "asm_h_sel", 7),
+ GATE_AUDIO3(CLK_AUD_ASRCO5, "audio_asrco5", "asm_h_sel", 8),
+ GATE_AUDIO3(CLK_AUD_ASRCO6, "audio_asrco6", "asm_h_sel", 9),
+ GATE_AUDIO3(CLK_AUD_MEM_ASRC1, "audio_mem_asrc1", "asm_h_sel", 10),
+ GATE_AUDIO3(CLK_AUD_MEM_ASRC2, "audio_mem_asrc2", "asm_h_sel", 11),
+ GATE_AUDIO3(CLK_AUD_MEM_ASRC3, "audio_mem_asrc3", "asm_h_sel", 12),
+ GATE_AUDIO3(CLK_AUD_MEM_ASRC4, "audio_mem_asrc4", "asm_h_sel", 13),
+ GATE_AUDIO3(CLK_AUD_MEM_ASRC5, "audio_mem_asrc5", "asm_h_sel", 14),
+};
+
+static const struct of_device_id of_match_clk_mt2701_aud[] = {
+ { .compatible = "mediatek,mt2701-audsys", },
+ {}
+};
+
+static int clk_mt2701_aud_probe(struct platform_device *pdev)
+{
+ struct clk_onecell_data *clk_data;
+ struct device_node *node = pdev->dev.of_node;
+ int r;
+
+ clk_data = mtk_alloc_clk_data(CLK_AUD_NR);
+
+ mtk_clk_register_gates(node, audio_clks, ARRAY_SIZE(audio_clks),
+ clk_data);
+
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+ if (r) {
+ dev_err(&pdev->dev,
+ "could not register clock provider: %s: %d\n",
+ pdev->name, r);
+
+ goto err_clk_provider;
+ }
+
+ r = devm_of_platform_populate(&pdev->dev);
+ if (r)
+ goto err_plat_populate;
+
+ return 0;
+
+err_plat_populate:
+ of_clk_del_provider(node);
+err_clk_provider:
+ return r;
+}
+
+static struct platform_driver clk_mt2701_aud_drv = {
+ .probe = clk_mt2701_aud_probe,
+ .driver = {
+ .name = "clk-mt2701-aud",
+ .of_match_table = of_match_clk_mt2701_aud,
+ },
+};
+
+builtin_platform_driver(clk_mt2701_aud_drv);
diff --git a/drivers/clk/mediatek/clk-mt2701.c b/drivers/clk/mediatek/clk-mt2701.c
index 8e7f16fd87c9..deca7527f92f 100644
--- a/drivers/clk/mediatek/clk-mt2701.c
+++ b/drivers/clk/mediatek/clk-mt2701.c
@@ -148,6 +148,7 @@ static const struct mtk_fixed_factor top_fixed_divs[] = {
FACTOR(CLK_TOP_CLK26M_D8, "clk26m_d8", "clk26m", 1, 8),
FACTOR(CLK_TOP_32K_INTERNAL, "32k_internal", "clk26m", 1, 793),
FACTOR(CLK_TOP_32K_EXTERNAL, "32k_external", "rtc32k", 1, 1),
+ FACTOR(CLK_TOP_AXISEL_D4, "axisel_d4", "axi_sel", 1, 4),
};
static const char * const axi_parents[] = {
@@ -857,13 +858,13 @@ static const struct mtk_gate peri_clks[] = {
GATE_PERI0(CLK_PERI_USB1, "usb1_ck", "usb20_sel", 11),
GATE_PERI0(CLK_PERI_USB0, "usb0_ck", "usb20_sel", 10),
GATE_PERI0(CLK_PERI_PWM, "pwm_ck", "axi_sel", 9),
- GATE_PERI0(CLK_PERI_PWM7, "pwm7_ck", "axi_sel", 8),
- GATE_PERI0(CLK_PERI_PWM6, "pwm6_ck", "axi_sel", 7),
- GATE_PERI0(CLK_PERI_PWM5, "pwm5_ck", "axi_sel", 6),
- GATE_PERI0(CLK_PERI_PWM4, "pwm4_ck", "axi_sel", 5),
- GATE_PERI0(CLK_PERI_PWM3, "pwm3_ck", "axi_sel", 4),
- GATE_PERI0(CLK_PERI_PWM2, "pwm2_ck", "axi_sel", 3),
- GATE_PERI0(CLK_PERI_PWM1, "pwm1_ck", "axi_sel", 2),
+ GATE_PERI0(CLK_PERI_PWM7, "pwm7_ck", "axisel_d4", 8),
+ GATE_PERI0(CLK_PERI_PWM6, "pwm6_ck", "axisel_d4", 7),
+ GATE_PERI0(CLK_PERI_PWM5, "pwm5_ck", "axisel_d4", 6),
+ GATE_PERI0(CLK_PERI_PWM4, "pwm4_ck", "axisel_d4", 5),
+ GATE_PERI0(CLK_PERI_PWM3, "pwm3_ck", "axisel_d4", 4),
+ GATE_PERI0(CLK_PERI_PWM2, "pwm2_ck", "axisel_d4", 3),
+ GATE_PERI0(CLK_PERI_PWM1, "pwm1_ck", "axisel_d4", 2),
GATE_PERI0(CLK_PERI_THERM, "therm_ck", "axi_sel", 1),
GATE_PERI0(CLK_PERI_NFI, "nfi_ck", "nfi2x_sel", 0),
diff --git a/drivers/clk/mediatek/clk-mt2712.c b/drivers/clk/mediatek/clk-mt2712.c
index 498d13799388..991d4093726e 100644
--- a/drivers/clk/mediatek/clk-mt2712.c
+++ b/drivers/clk/mediatek/clk-mt2712.c
@@ -221,6 +221,8 @@ static const struct mtk_fixed_factor top_divs[] = {
4),
FACTOR(CLK_TOP_D2A_ULCLK_6P5M, "d2a_ulclk_6p5m", "clk26m", 1,
4),
+ FACTOR(CLK_TOP_APLL1_D3, "apll1_d3", "apll1_ck", 1,
+ 3),
};
static const char * const axi_parents[] = {
@@ -625,7 +627,7 @@ static const char * const ether_125m_parents[] = {
static const char * const ether_50m_parents[] = {
"clk26m",
"etherpll_50m",
- "univpll_d26",
+ "apll1_d3",
"univpll3_d4"
};
@@ -686,7 +688,7 @@ static const char * const i2c_parents[] = {
static const char * const msdc0p_aes_parents[] = {
"clk26m",
- "msdcpll_ck",
+ "syspll_d2",
"univpll_d3",
"vcodecpll_ck"
};
@@ -719,6 +721,17 @@ static const char * const aud_apll2_parents[] = {
"clkaud_ext_i_2"
};
+static const char * const apll1_ref_parents[] = {
+ "clkaud_ext_i_2",
+ "clkaud_ext_i_1",
+ "clki2si0_mck_i",
+ "clki2si1_mck_i",
+ "clki2si2_mck_i",
+ "clktdmin_mclk_i",
+ "clki2si2_mck_i",
+ "clktdmin_mclk_i"
+};
+
static const char * const audull_vtx_parents[] = {
"d2a_ulclk_6p5m",
"clkaud_ext_i_0"
@@ -886,6 +899,10 @@ static struct mtk_composite top_muxes[] = {
aud_apll2_parents, 0x134, 1, 1),
MUX(CLK_TOP_DA_AUDULL_VTX_6P5M_SEL, "audull_vtx_sel",
audull_vtx_parents, 0x134, 31, 1),
+ MUX(CLK_TOP_APLL1_REF_SEL, "apll1_ref_sel",
+ apll1_ref_parents, 0x134, 4, 3),
+ MUX(CLK_TOP_APLL2_REF_SEL, "apll2_ref_sel",
+ apll1_ref_parents, 0x134, 7, 3),
};
static const char * const mcu_mp0_parents[] = {
@@ -932,36 +949,56 @@ static const struct mtk_clk_divider top_adj_divs[] = {
DIV_ADJ(CLK_TOP_APLL_DIV7, "apll_div7", "i2si3_sel", 0x128, 24, 8),
};
-static const struct mtk_gate_regs top_cg_regs = {
+static const struct mtk_gate_regs top0_cg_regs = {
.set_ofs = 0x120,
.clr_ofs = 0x120,
.sta_ofs = 0x120,
};
-#define GATE_TOP(_id, _name, _parent, _shift) { \
+static const struct mtk_gate_regs top1_cg_regs = {
+ .set_ofs = 0x424,
+ .clr_ofs = 0x424,
+ .sta_ofs = 0x424,
+};
+
+#define GATE_TOP0(_id, _name, _parent, _shift) { \
.id = _id, \
.name = _name, \
.parent_name = _parent, \
- .regs = &top_cg_regs, \
+ .regs = &top0_cg_regs, \
.shift = _shift, \
.ops = &mtk_clk_gate_ops_no_setclr, \
}
+#define GATE_TOP1(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &top1_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_no_setclr_inv, \
+ }
+
static const struct mtk_gate top_clks[] = {
- GATE_TOP(CLK_TOP_APLL_DIV_PDN0, "apll_div_pdn0", "i2so1_sel", 0),
- GATE_TOP(CLK_TOP_APLL_DIV_PDN1, "apll_div_pdn1", "i2so2_sel", 1),
- GATE_TOP(CLK_TOP_APLL_DIV_PDN2, "apll_div_pdn2", "i2so3_sel", 2),
- GATE_TOP(CLK_TOP_APLL_DIV_PDN3, "apll_div_pdn3", "tdmo0_sel", 3),
- GATE_TOP(CLK_TOP_APLL_DIV_PDN4, "apll_div_pdn4", "tdmo1_sel", 4),
- GATE_TOP(CLK_TOP_APLL_DIV_PDN5, "apll_div_pdn5", "i2si1_sel", 5),
- GATE_TOP(CLK_TOP_APLL_DIV_PDN6, "apll_div_pdn6", "i2si2_sel", 6),
- GATE_TOP(CLK_TOP_APLL_DIV_PDN7, "apll_div_pdn7", "i2si3_sel", 7),
+ /* TOP0 */
+ GATE_TOP0(CLK_TOP_APLL_DIV_PDN0, "apll_div_pdn0", "i2so1_sel", 0),
+ GATE_TOP0(CLK_TOP_APLL_DIV_PDN1, "apll_div_pdn1", "i2so2_sel", 1),
+ GATE_TOP0(CLK_TOP_APLL_DIV_PDN2, "apll_div_pdn2", "i2so3_sel", 2),
+ GATE_TOP0(CLK_TOP_APLL_DIV_PDN3, "apll_div_pdn3", "tdmo0_sel", 3),
+ GATE_TOP0(CLK_TOP_APLL_DIV_PDN4, "apll_div_pdn4", "tdmo1_sel", 4),
+ GATE_TOP0(CLK_TOP_APLL_DIV_PDN5, "apll_div_pdn5", "i2si1_sel", 5),
+ GATE_TOP0(CLK_TOP_APLL_DIV_PDN6, "apll_div_pdn6", "i2si2_sel", 6),
+ GATE_TOP0(CLK_TOP_APLL_DIV_PDN7, "apll_div_pdn7", "i2si3_sel", 7),
+ /* TOP1 */
+ GATE_TOP1(CLK_TOP_NFI2X_EN, "nfi2x_en", "nfi2x_sel", 0),
+ GATE_TOP1(CLK_TOP_NFIECC_EN, "nfiecc_en", "nfiecc_sel", 1),
+ GATE_TOP1(CLK_TOP_NFI1X_CK_EN, "nfi1x_ck_en", "nfi2x_sel", 2),
};
static const struct mtk_gate_regs infra_cg_regs = {
.set_ofs = 0x40,
.clr_ofs = 0x44,
- .sta_ofs = 0x40,
+ .sta_ofs = 0x48,
};
#define GATE_INFRA(_id, _name, _parent, _shift) { \
@@ -1120,6 +1157,10 @@ static const struct mtk_gate peri_clks[] = {
"msdc50_0_h_sel", 4),
GATE_PERI2(CLK_PERI_MSDC50_3_HCLK_EN, "per_msdc50_3_h",
"msdc50_3_h_sel", 5),
+ GATE_PERI2(CLK_PERI_MSDC30_0_QTR_EN, "per_msdc30_0_q",
+ "axi_sel", 6),
+ GATE_PERI2(CLK_PERI_MSDC30_3_QTR_EN, "per_msdc30_3_q",
+ "mem_sel", 7),
};
#define MT2712_PLL_FMAX (3000UL * MHZ)
diff --git a/drivers/clk/mediatek/clk-mt7622-aud.c b/drivers/clk/mediatek/clk-mt7622-aud.c
index fad7d9fc53ba..4f3d47b41b3e 100644
--- a/drivers/clk/mediatek/clk-mt7622-aud.c
+++ b/drivers/clk/mediatek/clk-mt7622-aud.c
@@ -106,6 +106,7 @@ static const struct mtk_gate audio_clks[] = {
GATE_AUDIO1(CLK_AUDIO_INTDIR, "audio_intdir", "intdir_sel", 20),
GATE_AUDIO1(CLK_AUDIO_A1SYS, "audio_a1sys", "a1sys_hp_sel", 21),
GATE_AUDIO1(CLK_AUDIO_A2SYS, "audio_a2sys", "a2sys_hp_sel", 22),
+ GATE_AUDIO1(CLK_AUDIO_AFE_CONN, "audio_afe_conn", "a1sys_hp_sel", 23),
/* AUDIO2 */
GATE_AUDIO2(CLK_AUDIO_UL1, "audio_ul1", "a1sys_hp_sel", 0),
GATE_AUDIO2(CLK_AUDIO_UL2, "audio_ul2", "a1sys_hp_sel", 1),
@@ -149,11 +150,23 @@ static int clk_mt7622_audiosys_init(struct platform_device *pdev)
clk_data);
r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
- if (r)
+ if (r) {
dev_err(&pdev->dev,
"could not register clock provider: %s: %d\n",
pdev->name, r);
+ goto err_clk_provider;
+ }
+
+ r = devm_of_platform_populate(&pdev->dev);
+ if (r)
+ goto err_plat_populate;
+
+ return 0;
+
+err_plat_populate:
+ of_clk_del_provider(node);
+err_clk_provider:
return r;
}
diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
index 7694302c70a4..d5cbec522aec 100644
--- a/drivers/clk/meson/Kconfig
+++ b/drivers/clk/meson/Kconfig
@@ -3,10 +3,15 @@ config COMMON_CLK_AMLOGIC
depends on OF
depends on ARCH_MESON || COMPILE_TEST
+config COMMON_CLK_REGMAP_MESON
+ bool
+ select REGMAP
+
config COMMON_CLK_MESON8B
bool
depends on COMMON_CLK_AMLOGIC
select RESET_CONTROLLER
+ select COMMON_CLK_REGMAP_MESON
help
Support for the clock controller on AmLogic S802 (Meson8),
S805 (Meson8b) and S812 (Meson8m2) devices. Say Y if you
@@ -16,6 +21,8 @@ config COMMON_CLK_GXBB
bool
depends on COMMON_CLK_AMLOGIC
select RESET_CONTROLLER
+ select COMMON_CLK_REGMAP_MESON
+ select MFD_SYSCON
help
Support for the clock controller on AmLogic S905 devices, aka gxbb.
Say Y if you want peripherals and CPU frequency scaling to work.
@@ -24,6 +31,8 @@ config COMMON_CLK_AXG
bool
depends on COMMON_CLK_AMLOGIC
select RESET_CONTROLLER
+ select COMMON_CLK_REGMAP_MESON
+ select MFD_SYSCON
help
Support for the clock controller on AmLogic A113D devices, aka axg.
Say Y if you want peripherals and CPU frequency scaling to work.
diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
index 3c03ce583798..ffee82e60b7a 100644
--- a/drivers/clk/meson/Makefile
+++ b/drivers/clk/meson/Makefile
@@ -2,7 +2,8 @@
# Makefile for Meson specific clk
#
-obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-cpu.o clk-mpll.o clk-audio-divider.o
+obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o clk-audio-divider.o
obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o
-obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o gxbb-aoclk-regmap.o gxbb-aoclk-32k.o
+obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o gxbb-aoclk-32k.o
obj-$(CONFIG_COMMON_CLK_AXG) += axg.o
+obj-$(CONFIG_COMMON_CLK_REGMAP_MESON) += clk-regmap.o
diff --git a/drivers/clk/meson/axg.c b/drivers/clk/meson/axg.c
index 1294f3ad7cd5..5f5d468c1efe 100644
--- a/drivers/clk/meson/axg.c
+++ b/drivers/clk/meson/axg.c
@@ -11,125 +11,51 @@
#include <linux/clk.h>
#include <linux/clk-provider.h>
+#include <linux/init.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
+#include <linux/mfd/syscon.h>
#include <linux/platform_device.h>
-#include <linux/init.h>
+#include <linux/regmap.h>
#include "clkc.h"
#include "axg.h"
static DEFINE_SPINLOCK(meson_clk_lock);
-static const struct pll_rate_table sys_pll_rate_table[] = {
- PLL_RATE(24000000, 56, 1, 2),
- PLL_RATE(48000000, 64, 1, 2),
- PLL_RATE(72000000, 72, 1, 2),
- PLL_RATE(96000000, 64, 1, 2),
- PLL_RATE(120000000, 80, 1, 2),
- PLL_RATE(144000000, 96, 1, 2),
- PLL_RATE(168000000, 56, 1, 1),
- PLL_RATE(192000000, 64, 1, 1),
- PLL_RATE(216000000, 72, 1, 1),
- PLL_RATE(240000000, 80, 1, 1),
- PLL_RATE(264000000, 88, 1, 1),
- PLL_RATE(288000000, 96, 1, 1),
- PLL_RATE(312000000, 52, 1, 2),
- PLL_RATE(336000000, 56, 1, 2),
- PLL_RATE(360000000, 60, 1, 2),
- PLL_RATE(384000000, 64, 1, 2),
- PLL_RATE(408000000, 68, 1, 2),
- PLL_RATE(432000000, 72, 1, 2),
- PLL_RATE(456000000, 76, 1, 2),
- PLL_RATE(480000000, 80, 1, 2),
- PLL_RATE(504000000, 84, 1, 2),
- PLL_RATE(528000000, 88, 1, 2),
- PLL_RATE(552000000, 92, 1, 2),
- PLL_RATE(576000000, 96, 1, 2),
- PLL_RATE(600000000, 50, 1, 1),
- PLL_RATE(624000000, 52, 1, 1),
- PLL_RATE(648000000, 54, 1, 1),
- PLL_RATE(672000000, 56, 1, 1),
- PLL_RATE(696000000, 58, 1, 1),
- PLL_RATE(720000000, 60, 1, 1),
- PLL_RATE(744000000, 62, 1, 1),
- PLL_RATE(768000000, 64, 1, 1),
- PLL_RATE(792000000, 66, 1, 1),
- PLL_RATE(816000000, 68, 1, 1),
- PLL_RATE(840000000, 70, 1, 1),
- PLL_RATE(864000000, 72, 1, 1),
- PLL_RATE(888000000, 74, 1, 1),
- PLL_RATE(912000000, 76, 1, 1),
- PLL_RATE(936000000, 78, 1, 1),
- PLL_RATE(960000000, 80, 1, 1),
- PLL_RATE(984000000, 82, 1, 1),
- PLL_RATE(1008000000, 84, 1, 1),
- PLL_RATE(1032000000, 86, 1, 1),
- PLL_RATE(1056000000, 88, 1, 1),
- PLL_RATE(1080000000, 90, 1, 1),
- PLL_RATE(1104000000, 92, 1, 1),
- PLL_RATE(1128000000, 94, 1, 1),
- PLL_RATE(1152000000, 96, 1, 1),
- PLL_RATE(1176000000, 98, 1, 1),
- PLL_RATE(1200000000, 50, 1, 0),
- PLL_RATE(1224000000, 51, 1, 0),
- PLL_RATE(1248000000, 52, 1, 0),
- PLL_RATE(1272000000, 53, 1, 0),
- PLL_RATE(1296000000, 54, 1, 0),
- PLL_RATE(1320000000, 55, 1, 0),
- PLL_RATE(1344000000, 56, 1, 0),
- PLL_RATE(1368000000, 57, 1, 0),
- PLL_RATE(1392000000, 58, 1, 0),
- PLL_RATE(1416000000, 59, 1, 0),
- PLL_RATE(1440000000, 60, 1, 0),
- PLL_RATE(1464000000, 61, 1, 0),
- PLL_RATE(1488000000, 62, 1, 0),
- PLL_RATE(1512000000, 63, 1, 0),
- PLL_RATE(1536000000, 64, 1, 0),
- PLL_RATE(1560000000, 65, 1, 0),
- PLL_RATE(1584000000, 66, 1, 0),
- PLL_RATE(1608000000, 67, 1, 0),
- PLL_RATE(1632000000, 68, 1, 0),
- PLL_RATE(1656000000, 68, 1, 0),
- PLL_RATE(1680000000, 68, 1, 0),
- PLL_RATE(1704000000, 68, 1, 0),
- PLL_RATE(1728000000, 69, 1, 0),
- PLL_RATE(1752000000, 69, 1, 0),
- PLL_RATE(1776000000, 69, 1, 0),
- PLL_RATE(1800000000, 69, 1, 0),
- PLL_RATE(1824000000, 70, 1, 0),
- PLL_RATE(1848000000, 70, 1, 0),
- PLL_RATE(1872000000, 70, 1, 0),
- PLL_RATE(1896000000, 70, 1, 0),
- PLL_RATE(1920000000, 71, 1, 0),
- PLL_RATE(1944000000, 71, 1, 0),
- PLL_RATE(1968000000, 71, 1, 0),
- PLL_RATE(1992000000, 71, 1, 0),
- PLL_RATE(2016000000, 72, 1, 0),
- PLL_RATE(2040000000, 72, 1, 0),
- PLL_RATE(2064000000, 72, 1, 0),
- PLL_RATE(2088000000, 72, 1, 0),
- PLL_RATE(2112000000, 73, 1, 0),
- { /* sentinel */ },
-};
-
-static struct meson_clk_pll axg_fixed_pll = {
- .m = {
- .reg_off = HHI_MPLL_CNTL,
- .shift = 0,
- .width = 9,
- },
- .n = {
- .reg_off = HHI_MPLL_CNTL,
- .shift = 9,
- .width = 5,
- },
- .od = {
- .reg_off = HHI_MPLL_CNTL,
- .shift = 16,
- .width = 2,
+static struct clk_regmap axg_fixed_pll = {
+ .data = &(struct meson_clk_pll_data){
+ .m = {
+ .reg_off = HHI_MPLL_CNTL,
+ .shift = 0,
+ .width = 9,
+ },
+ .n = {
+ .reg_off = HHI_MPLL_CNTL,
+ .shift = 9,
+ .width = 5,
+ },
+ .od = {
+ .reg_off = HHI_MPLL_CNTL,
+ .shift = 16,
+ .width = 2,
+ },
+ .frac = {
+ .reg_off = HHI_MPLL_CNTL2,
+ .shift = 0,
+ .width = 12,
+ },
+ .l = {
+ .reg_off = HHI_MPLL_CNTL,
+ .shift = 31,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = HHI_MPLL_CNTL,
+ .shift = 29,
+ .width = 1,
+ },
},
- .lock = &meson_clk_lock,
.hw.init = &(struct clk_init_data){
.name = "fixed_pll",
.ops = &meson_clk_pll_ro_ops,
@@ -138,25 +64,34 @@ static struct meson_clk_pll axg_fixed_pll = {
},
};
-static struct meson_clk_pll axg_sys_pll = {
- .m = {
- .reg_off = HHI_SYS_PLL_CNTL,
- .shift = 0,
- .width = 9,
+static struct clk_regmap axg_sys_pll = {
+ .data = &(struct meson_clk_pll_data){
+ .m = {
+ .reg_off = HHI_SYS_PLL_CNTL,
+ .shift = 0,
+ .width = 9,
+ },
+ .n = {
+ .reg_off = HHI_SYS_PLL_CNTL,
+ .shift = 9,
+ .width = 5,
+ },
+ .od = {
+ .reg_off = HHI_SYS_PLL_CNTL,
+ .shift = 16,
+ .width = 2,
+ },
+ .l = {
+ .reg_off = HHI_SYS_PLL_CNTL,
+ .shift = 31,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = HHI_SYS_PLL_CNTL,
+ .shift = 29,
+ .width = 1,
+ },
},
- .n = {
- .reg_off = HHI_SYS_PLL_CNTL,
- .shift = 9,
- .width = 5,
- },
- .od = {
- .reg_off = HHI_SYS_PLL_CNTL,
- .shift = 10,
- .width = 2,
- },
- .rate_table = sys_pll_rate_table,
- .rate_count = ARRAY_SIZE(sys_pll_rate_table),
- .lock = &meson_clk_lock,
.hw.init = &(struct clk_init_data){
.name = "sys_pll",
.ops = &meson_clk_pll_ro_ops,
@@ -257,40 +192,51 @@ static const struct pll_rate_table axg_gp0_pll_rate_table[] = {
{ /* sentinel */ },
};
-static struct pll_params_table axg_gp0_params_table[] = {
- PLL_PARAM(HHI_GP0_PLL_CNTL, 0x40010250),
- PLL_PARAM(HHI_GP0_PLL_CNTL1, 0xc084a000),
- PLL_PARAM(HHI_GP0_PLL_CNTL2, 0xb75020be),
- PLL_PARAM(HHI_GP0_PLL_CNTL3, 0x0a59a288),
- PLL_PARAM(HHI_GP0_PLL_CNTL4, 0xc000004d),
- PLL_PARAM(HHI_GP0_PLL_CNTL5, 0x00078000),
-};
-
-static struct meson_clk_pll axg_gp0_pll = {
- .m = {
- .reg_off = HHI_GP0_PLL_CNTL,
- .shift = 0,
- .width = 9,
- },
- .n = {
- .reg_off = HHI_GP0_PLL_CNTL,
- .shift = 9,
- .width = 5,
- },
- .od = {
- .reg_off = HHI_GP0_PLL_CNTL,
- .shift = 16,
- .width = 2,
- },
- .params = {
- .params_table = axg_gp0_params_table,
- .params_count = ARRAY_SIZE(axg_gp0_params_table),
- .no_init_reset = true,
- .reset_lock_loop = true,
- },
- .rate_table = axg_gp0_pll_rate_table,
- .rate_count = ARRAY_SIZE(axg_gp0_pll_rate_table),
- .lock = &meson_clk_lock,
+static const struct reg_sequence axg_gp0_init_regs[] = {
+ { .reg = HHI_GP0_PLL_CNTL1, .def = 0xc084b000 },
+ { .reg = HHI_GP0_PLL_CNTL2, .def = 0xb75020be },
+ { .reg = HHI_GP0_PLL_CNTL3, .def = 0x0a59a288 },
+ { .reg = HHI_GP0_PLL_CNTL4, .def = 0xc000004d },
+ { .reg = HHI_GP0_PLL_CNTL5, .def = 0x00078000 },
+ { .reg = HHI_GP0_PLL_CNTL, .def = 0x40010250 },
+};
+
+static struct clk_regmap axg_gp0_pll = {
+ .data = &(struct meson_clk_pll_data){
+ .m = {
+ .reg_off = HHI_GP0_PLL_CNTL,
+ .shift = 0,
+ .width = 9,
+ },
+ .n = {
+ .reg_off = HHI_GP0_PLL_CNTL,
+ .shift = 9,
+ .width = 5,
+ },
+ .od = {
+ .reg_off = HHI_GP0_PLL_CNTL,
+ .shift = 16,
+ .width = 2,
+ },
+ .frac = {
+ .reg_off = HHI_GP0_PLL_CNTL1,
+ .shift = 0,
+ .width = 10,
+ },
+ .l = {
+ .reg_off = HHI_GP0_PLL_CNTL,
+ .shift = 31,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = HHI_GP0_PLL_CNTL,
+ .shift = 29,
+ .width = 1,
+ },
+ .table = axg_gp0_pll_rate_table,
+ .init_regs = axg_gp0_init_regs,
+ .init_count = ARRAY_SIZE(axg_gp0_init_regs),
+ },
.hw.init = &(struct clk_init_data){
.name = "gp0_pll",
.ops = &meson_clk_pll_ops,
@@ -299,234 +245,427 @@ static struct meson_clk_pll axg_gp0_pll = {
},
};
+static const struct reg_sequence axg_hifi_init_regs[] = {
+ { .reg = HHI_HIFI_PLL_CNTL1, .def = 0xc084b000 },
+ { .reg = HHI_HIFI_PLL_CNTL2, .def = 0xb75020be },
+ { .reg = HHI_HIFI_PLL_CNTL3, .def = 0x0a6a3a88 },
+ { .reg = HHI_HIFI_PLL_CNTL4, .def = 0xc000004d },
+ { .reg = HHI_HIFI_PLL_CNTL5, .def = 0x00058000 },
+ { .reg = HHI_HIFI_PLL_CNTL, .def = 0x40010250 },
+};
+
+static struct clk_regmap axg_hifi_pll = {
+ .data = &(struct meson_clk_pll_data){
+ .m = {
+ .reg_off = HHI_HIFI_PLL_CNTL,
+ .shift = 0,
+ .width = 9,
+ },
+ .n = {
+ .reg_off = HHI_HIFI_PLL_CNTL,
+ .shift = 9,
+ .width = 5,
+ },
+ .od = {
+ .reg_off = HHI_HIFI_PLL_CNTL,
+ .shift = 16,
+ .width = 2,
+ },
+ .frac = {
+ .reg_off = HHI_HIFI_PLL_CNTL5,
+ .shift = 0,
+ .width = 13,
+ },
+ .l = {
+ .reg_off = HHI_HIFI_PLL_CNTL,
+ .shift = 31,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = HHI_HIFI_PLL_CNTL,
+ .shift = 29,
+ .width = 1,
+ },
+ .table = axg_gp0_pll_rate_table,
+ .init_regs = axg_hifi_init_regs,
+ .init_count = ARRAY_SIZE(axg_hifi_init_regs),
+ .flags = CLK_MESON_PLL_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hifi_pll",
+ .ops = &meson_clk_pll_ops,
+ .parent_names = (const char *[]){ "xtal" },
+ .num_parents = 1,
+ },
+};
-static struct clk_fixed_factor axg_fclk_div2 = {
+static struct clk_fixed_factor axg_fclk_div2_div = {
.mult = 1,
.div = 2,
.hw.init = &(struct clk_init_data){
- .name = "fclk_div2",
+ .name = "fclk_div2_div",
.ops = &clk_fixed_factor_ops,
.parent_names = (const char *[]){ "fixed_pll" },
.num_parents = 1,
},
};
-static struct clk_fixed_factor axg_fclk_div3 = {
+static struct clk_regmap axg_fclk_div2 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPLL_CNTL6,
+ .bit_idx = 27,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div2",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "fclk_div2_div" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor axg_fclk_div3_div = {
.mult = 1,
.div = 3,
.hw.init = &(struct clk_init_data){
- .name = "fclk_div3",
+ .name = "fclk_div3_div",
.ops = &clk_fixed_factor_ops,
.parent_names = (const char *[]){ "fixed_pll" },
.num_parents = 1,
},
};
-static struct clk_fixed_factor axg_fclk_div4 = {
+static struct clk_regmap axg_fclk_div3 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPLL_CNTL6,
+ .bit_idx = 28,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div3",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "fclk_div3_div" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor axg_fclk_div4_div = {
.mult = 1,
.div = 4,
.hw.init = &(struct clk_init_data){
- .name = "fclk_div4",
+ .name = "fclk_div4_div",
.ops = &clk_fixed_factor_ops,
.parent_names = (const char *[]){ "fixed_pll" },
.num_parents = 1,
},
};
-static struct clk_fixed_factor axg_fclk_div5 = {
+static struct clk_regmap axg_fclk_div4 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPLL_CNTL6,
+ .bit_idx = 29,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div4",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "fclk_div4_div" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor axg_fclk_div5_div = {
.mult = 1,
.div = 5,
.hw.init = &(struct clk_init_data){
- .name = "fclk_div5",
+ .name = "fclk_div5_div",
.ops = &clk_fixed_factor_ops,
.parent_names = (const char *[]){ "fixed_pll" },
.num_parents = 1,
},
};
-static struct clk_fixed_factor axg_fclk_div7 = {
+static struct clk_regmap axg_fclk_div5 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPLL_CNTL6,
+ .bit_idx = 30,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div5",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "fclk_div5_div" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor axg_fclk_div7_div = {
.mult = 1,
.div = 7,
.hw.init = &(struct clk_init_data){
- .name = "fclk_div7",
+ .name = "fclk_div7_div",
.ops = &clk_fixed_factor_ops,
.parent_names = (const char *[]){ "fixed_pll" },
.num_parents = 1,
},
};
-static struct meson_clk_mpll axg_mpll0 = {
- .sdm = {
- .reg_off = HHI_MPLL_CNTL7,
- .shift = 0,
- .width = 14,
+static struct clk_regmap axg_fclk_div7 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPLL_CNTL6,
+ .bit_idx = 31,
},
- .sdm_en = {
- .reg_off = HHI_MPLL_CNTL7,
- .shift = 15,
- .width = 1,
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div7",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "fclk_div7_div" },
+ .num_parents = 1,
},
- .n2 = {
- .reg_off = HHI_MPLL_CNTL7,
- .shift = 16,
- .width = 9,
+};
+
+static struct clk_regmap axg_mpll_prediv = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_MPLL_CNTL5,
+ .shift = 12,
+ .width = 1,
},
- .en = {
- .reg_off = HHI_MPLL_CNTL7,
- .shift = 14,
- .width = 1,
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll_prediv",
+ .ops = &clk_regmap_divider_ro_ops,
+ .parent_names = (const char *[]){ "fixed_pll" },
+ .num_parents = 1,
},
- .ssen = {
- .reg_off = HHI_MPLL_CNTL,
- .shift = 25,
- .width = 1,
+};
+
+static struct clk_regmap axg_mpll0_div = {
+ .data = &(struct meson_clk_mpll_data){
+ .sdm = {
+ .reg_off = HHI_MPLL_CNTL7,
+ .shift = 0,
+ .width = 14,
+ },
+ .sdm_en = {
+ .reg_off = HHI_MPLL_CNTL7,
+ .shift = 15,
+ .width = 1,
+ },
+ .n2 = {
+ .reg_off = HHI_MPLL_CNTL7,
+ .shift = 16,
+ .width = 9,
+ },
+ .ssen = {
+ .reg_off = HHI_MPLL_CNTL,
+ .shift = 25,
+ .width = 1,
+ },
+ .misc = {
+ .reg_off = HHI_PLL_TOP_MISC,
+ .shift = 0,
+ .width = 1,
+ },
+ .lock = &meson_clk_lock,
},
- .lock = &meson_clk_lock,
.hw.init = &(struct clk_init_data){
- .name = "mpll0",
+ .name = "mpll0_div",
.ops = &meson_clk_mpll_ops,
- .parent_names = (const char *[]){ "fixed_pll" },
+ .parent_names = (const char *[]){ "mpll_prediv" },
.num_parents = 1,
},
};
-static struct meson_clk_mpll axg_mpll1 = {
- .sdm = {
- .reg_off = HHI_MPLL_CNTL8,
- .shift = 0,
- .width = 14,
+static struct clk_regmap axg_mpll0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPLL_CNTL7,
+ .bit_idx = 14,
},
- .sdm_en = {
- .reg_off = HHI_MPLL_CNTL8,
- .shift = 15,
- .width = 1,
- },
- .n2 = {
- .reg_off = HHI_MPLL_CNTL8,
- .shift = 16,
- .width = 9,
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll0",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "mpll0_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
},
- .en = {
- .reg_off = HHI_MPLL_CNTL8,
- .shift = 14,
- .width = 1,
+};
+
+static struct clk_regmap axg_mpll1_div = {
+ .data = &(struct meson_clk_mpll_data){
+ .sdm = {
+ .reg_off = HHI_MPLL_CNTL8,
+ .shift = 0,
+ .width = 14,
+ },
+ .sdm_en = {
+ .reg_off = HHI_MPLL_CNTL8,
+ .shift = 15,
+ .width = 1,
+ },
+ .n2 = {
+ .reg_off = HHI_MPLL_CNTL8,
+ .shift = 16,
+ .width = 9,
+ },
+ .misc = {
+ .reg_off = HHI_PLL_TOP_MISC,
+ .shift = 1,
+ .width = 1,
+ },
+ .lock = &meson_clk_lock,
},
- .lock = &meson_clk_lock,
.hw.init = &(struct clk_init_data){
- .name = "mpll1",
+ .name = "mpll1_div",
.ops = &meson_clk_mpll_ops,
- .parent_names = (const char *[]){ "fixed_pll" },
+ .parent_names = (const char *[]){ "mpll_prediv" },
.num_parents = 1,
},
};
-static struct meson_clk_mpll axg_mpll2 = {
- .sdm = {
- .reg_off = HHI_MPLL_CNTL9,
- .shift = 0,
- .width = 14,
+static struct clk_regmap axg_mpll1 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPLL_CNTL8,
+ .bit_idx = 14,
},
- .sdm_en = {
- .reg_off = HHI_MPLL_CNTL9,
- .shift = 15,
- .width = 1,
- },
- .n2 = {
- .reg_off = HHI_MPLL_CNTL9,
- .shift = 16,
- .width = 9,
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll1",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "mpll1_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
},
- .en = {
- .reg_off = HHI_MPLL_CNTL9,
- .shift = 14,
- .width = 1,
+};
+
+static struct clk_regmap axg_mpll2_div = {
+ .data = &(struct meson_clk_mpll_data){
+ .sdm = {
+ .reg_off = HHI_MPLL_CNTL9,
+ .shift = 0,
+ .width = 14,
+ },
+ .sdm_en = {
+ .reg_off = HHI_MPLL_CNTL9,
+ .shift = 15,
+ .width = 1,
+ },
+ .n2 = {
+ .reg_off = HHI_MPLL_CNTL9,
+ .shift = 16,
+ .width = 9,
+ },
+ .misc = {
+ .reg_off = HHI_PLL_TOP_MISC,
+ .shift = 2,
+ .width = 1,
+ },
+ .lock = &meson_clk_lock,
},
- .lock = &meson_clk_lock,
.hw.init = &(struct clk_init_data){
- .name = "mpll2",
+ .name = "mpll2_div",
.ops = &meson_clk_mpll_ops,
- .parent_names = (const char *[]){ "fixed_pll" },
+ .parent_names = (const char *[]){ "mpll_prediv" },
.num_parents = 1,
},
};
-static struct meson_clk_mpll axg_mpll3 = {
- .sdm = {
- .reg_off = HHI_MPLL3_CNTL0,
- .shift = 12,
- .width = 14,
+static struct clk_regmap axg_mpll2 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPLL_CNTL9,
+ .bit_idx = 14,
},
- .sdm_en = {
- .reg_off = HHI_MPLL3_CNTL0,
- .shift = 11,
- .width = 1,
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll2",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "mpll2_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap axg_mpll3_div = {
+ .data = &(struct meson_clk_mpll_data){
+ .sdm = {
+ .reg_off = HHI_MPLL3_CNTL0,
+ .shift = 12,
+ .width = 14,
+ },
+ .sdm_en = {
+ .reg_off = HHI_MPLL3_CNTL0,
+ .shift = 11,
+ .width = 1,
+ },
+ .n2 = {
+ .reg_off = HHI_MPLL3_CNTL0,
+ .shift = 2,
+ .width = 9,
+ },
+ .misc = {
+ .reg_off = HHI_PLL_TOP_MISC,
+ .shift = 3,
+ .width = 1,
+ },
+ .lock = &meson_clk_lock,
},
- .n2 = {
- .reg_off = HHI_MPLL3_CNTL0,
- .shift = 2,
- .width = 9,
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll3_div",
+ .ops = &meson_clk_mpll_ops,
+ .parent_names = (const char *[]){ "mpll_prediv" },
+ .num_parents = 1,
},
- .en = {
- .reg_off = HHI_MPLL3_CNTL0,
- .shift = 0,
- .width = 1,
+};
+
+static struct clk_regmap axg_mpll3 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPLL3_CNTL0,
+ .bit_idx = 0,
},
- .lock = &meson_clk_lock,
.hw.init = &(struct clk_init_data){
.name = "mpll3",
- .ops = &meson_clk_mpll_ops,
- .parent_names = (const char *[]){ "fixed_pll" },
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "mpll3_div" },
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
},
};
-/*
- * FIXME The legacy composite clocks (e.g. clk81) are both PLL post-dividers
- * and should be modeled with their respective PLLs via the forthcoming
- * coordinated clock rates feature
- */
static u32 mux_table_clk81[] = { 0, 2, 3, 4, 5, 6, 7 };
static const char * const clk81_parent_names[] = {
"xtal", "fclk_div7", "mpll1", "mpll2", "fclk_div4",
"fclk_div3", "fclk_div5"
};
-static struct clk_mux axg_mpeg_clk_sel = {
- .reg = (void *)HHI_MPEG_CLK_CNTL,
- .mask = 0x7,
- .shift = 12,
- .flags = CLK_MUX_READ_ONLY,
- .table = mux_table_clk81,
- .lock = &meson_clk_lock,
+static struct clk_regmap axg_mpeg_clk_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_MPEG_CLK_CNTL,
+ .mask = 0x7,
+ .shift = 12,
+ .table = mux_table_clk81,
+ },
.hw.init = &(struct clk_init_data){
.name = "mpeg_clk_sel",
- .ops = &clk_mux_ro_ops,
+ .ops = &clk_regmap_mux_ro_ops,
.parent_names = clk81_parent_names,
.num_parents = ARRAY_SIZE(clk81_parent_names),
},
};
-static struct clk_divider axg_mpeg_clk_div = {
- .reg = (void *)HHI_MPEG_CLK_CNTL,
- .shift = 0,
- .width = 7,
- .lock = &meson_clk_lock,
+static struct clk_regmap axg_mpeg_clk_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_MPEG_CLK_CNTL,
+ .shift = 0,
+ .width = 7,
+ },
.hw.init = &(struct clk_init_data){
.name = "mpeg_clk_div",
- .ops = &clk_divider_ops,
+ .ops = &clk_regmap_divider_ops,
.parent_names = (const char *[]){ "mpeg_clk_sel" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_gate axg_clk81 = {
- .reg = (void *)HHI_MPEG_CLK_CNTL,
- .bit_idx = 7,
- .lock = &meson_clk_lock,
+static struct clk_regmap axg_clk81 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPEG_CLK_CNTL,
+ .bit_idx = 7,
+ },
.hw.init = &(struct clk_init_data){
.name = "clk81",
- .ops = &clk_gate_ops,
+ .ops = &clk_regmap_gate_ops,
.parent_names = (const char *[]){ "mpeg_clk_div" },
.num_parents = 1,
.flags = (CLK_SET_RATE_PARENT | CLK_IS_CRITICAL),
@@ -545,42 +684,45 @@ static const char * const axg_sd_emmc_clk0_parent_names[] = {
};
/* SDcard clock */
-static struct clk_mux axg_sd_emmc_b_clk0_sel = {
- .reg = (void *)HHI_SD_EMMC_CLK_CNTL,
- .mask = 0x7,
- .shift = 25,
- .lock = &meson_clk_lock,
+static struct clk_regmap axg_sd_emmc_b_clk0_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_SD_EMMC_CLK_CNTL,
+ .mask = 0x7,
+ .shift = 25,
+ },
.hw.init = &(struct clk_init_data) {
.name = "sd_emmc_b_clk0_sel",
- .ops = &clk_mux_ops,
+ .ops = &clk_regmap_mux_ops,
.parent_names = axg_sd_emmc_clk0_parent_names,
.num_parents = ARRAY_SIZE(axg_sd_emmc_clk0_parent_names),
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_divider axg_sd_emmc_b_clk0_div = {
- .reg = (void *)HHI_SD_EMMC_CLK_CNTL,
- .shift = 16,
- .width = 7,
- .lock = &meson_clk_lock,
- .flags = CLK_DIVIDER_ROUND_CLOSEST,
+static struct clk_regmap axg_sd_emmc_b_clk0_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_SD_EMMC_CLK_CNTL,
+ .shift = 16,
+ .width = 7,
+ .flags = CLK_DIVIDER_ROUND_CLOSEST,
+ },
.hw.init = &(struct clk_init_data) {
.name = "sd_emmc_b_clk0_div",
- .ops = &clk_divider_ops,
+ .ops = &clk_regmap_divider_ops,
.parent_names = (const char *[]){ "sd_emmc_b_clk0_sel" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_gate axg_sd_emmc_b_clk0 = {
- .reg = (void *)HHI_SD_EMMC_CLK_CNTL,
- .bit_idx = 23,
- .lock = &meson_clk_lock,
+static struct clk_regmap axg_sd_emmc_b_clk0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_SD_EMMC_CLK_CNTL,
+ .bit_idx = 23,
+ },
.hw.init = &(struct clk_init_data){
.name = "sd_emmc_b_clk0",
- .ops = &clk_gate_ops,
+ .ops = &clk_regmap_gate_ops,
.parent_names = (const char *[]){ "sd_emmc_b_clk0_div" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
@@ -588,42 +730,45 @@ static struct clk_gate axg_sd_emmc_b_clk0 = {
};
/* EMMC/NAND clock */
-static struct clk_mux axg_sd_emmc_c_clk0_sel = {
- .reg = (void *)HHI_NAND_CLK_CNTL,
- .mask = 0x7,
- .shift = 9,
- .lock = &meson_clk_lock,
+static struct clk_regmap axg_sd_emmc_c_clk0_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_NAND_CLK_CNTL,
+ .mask = 0x7,
+ .shift = 9,
+ },
.hw.init = &(struct clk_init_data) {
.name = "sd_emmc_c_clk0_sel",
- .ops = &clk_mux_ops,
+ .ops = &clk_regmap_mux_ops,
.parent_names = axg_sd_emmc_clk0_parent_names,
.num_parents = ARRAY_SIZE(axg_sd_emmc_clk0_parent_names),
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_divider axg_sd_emmc_c_clk0_div = {
- .reg = (void *)HHI_NAND_CLK_CNTL,
- .shift = 0,
- .width = 7,
- .lock = &meson_clk_lock,
- .flags = CLK_DIVIDER_ROUND_CLOSEST,
+static struct clk_regmap axg_sd_emmc_c_clk0_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_NAND_CLK_CNTL,
+ .shift = 0,
+ .width = 7,
+ .flags = CLK_DIVIDER_ROUND_CLOSEST,
+ },
.hw.init = &(struct clk_init_data) {
.name = "sd_emmc_c_clk0_div",
- .ops = &clk_divider_ops,
+ .ops = &clk_regmap_divider_ops,
.parent_names = (const char *[]){ "sd_emmc_c_clk0_sel" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_gate axg_sd_emmc_c_clk0 = {
- .reg = (void *)HHI_NAND_CLK_CNTL,
- .bit_idx = 7,
- .lock = &meson_clk_lock,
+static struct clk_regmap axg_sd_emmc_c_clk0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_NAND_CLK_CNTL,
+ .bit_idx = 7,
+ },
.hw.init = &(struct clk_init_data){
.name = "sd_emmc_c_clk0",
- .ops = &clk_gate_ops,
+ .ops = &clk_regmap_gate_ops,
.parent_names = (const char *[]){ "sd_emmc_c_clk0_div" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
@@ -750,27 +895,24 @@ static struct clk_hw_onecell_data axg_hw_onecell_data = {
[CLKID_SD_EMMC_C_CLK0_SEL] = &axg_sd_emmc_c_clk0_sel.hw,
[CLKID_SD_EMMC_C_CLK0_DIV] = &axg_sd_emmc_c_clk0_div.hw,
[CLKID_SD_EMMC_C_CLK0] = &axg_sd_emmc_c_clk0.hw,
+ [CLKID_MPLL0_DIV] = &axg_mpll0_div.hw,
+ [CLKID_MPLL1_DIV] = &axg_mpll1_div.hw,
+ [CLKID_MPLL2_DIV] = &axg_mpll2_div.hw,
+ [CLKID_MPLL3_DIV] = &axg_mpll3_div.hw,
+ [CLKID_HIFI_PLL] = &axg_hifi_pll.hw,
+ [CLKID_MPLL_PREDIV] = &axg_mpll_prediv.hw,
+ [CLKID_FCLK_DIV2_DIV] = &axg_fclk_div2_div.hw,
+ [CLKID_FCLK_DIV3_DIV] = &axg_fclk_div3_div.hw,
+ [CLKID_FCLK_DIV4_DIV] = &axg_fclk_div4_div.hw,
+ [CLKID_FCLK_DIV5_DIV] = &axg_fclk_div5_div.hw,
+ [CLKID_FCLK_DIV7_DIV] = &axg_fclk_div7_div.hw,
[NR_CLKS] = NULL,
},
.num = NR_CLKS,
};
-/* Convenience tables to populate base addresses in .probe */
-
-static struct meson_clk_pll *const axg_clk_plls[] = {
- &axg_fixed_pll,
- &axg_sys_pll,
- &axg_gp0_pll,
-};
-
-static struct meson_clk_mpll *const axg_clk_mplls[] = {
- &axg_mpll0,
- &axg_mpll1,
- &axg_mpll2,
- &axg_mpll3,
-};
-
-static struct clk_gate *const axg_clk_gates[] = {
+/* Convenience table to populate regmap in .probe */
+static struct clk_regmap *const axg_clk_regmaps[] = {
&axg_clk81,
&axg_ddr,
&axg_audio_locker,
@@ -818,113 +960,100 @@ static struct clk_gate *const axg_clk_gates[] = {
&axg_ao_i2c,
&axg_sd_emmc_b_clk0,
&axg_sd_emmc_c_clk0,
-};
-
-static struct clk_mux *const axg_clk_muxes[] = {
- &axg_mpeg_clk_sel,
- &axg_sd_emmc_b_clk0_sel,
- &axg_sd_emmc_c_clk0_sel,
-};
-
-static struct clk_divider *const axg_clk_dividers[] = {
&axg_mpeg_clk_div,
&axg_sd_emmc_b_clk0_div,
&axg_sd_emmc_c_clk0_div,
-};
-
-struct clkc_data {
- struct clk_gate *const *clk_gates;
- unsigned int clk_gates_count;
- struct meson_clk_mpll *const *clk_mplls;
- unsigned int clk_mplls_count;
- struct meson_clk_pll *const *clk_plls;
- unsigned int clk_plls_count;
- struct clk_mux *const *clk_muxes;
- unsigned int clk_muxes_count;
- struct clk_divider *const *clk_dividers;
- unsigned int clk_dividers_count;
- struct clk_hw_onecell_data *hw_onecell_data;
-};
-
-static const struct clkc_data axg_clkc_data = {
- .clk_gates = axg_clk_gates,
- .clk_gates_count = ARRAY_SIZE(axg_clk_gates),
- .clk_mplls = axg_clk_mplls,
- .clk_mplls_count = ARRAY_SIZE(axg_clk_mplls),
- .clk_plls = axg_clk_plls,
- .clk_plls_count = ARRAY_SIZE(axg_clk_plls),
- .clk_muxes = axg_clk_muxes,
- .clk_muxes_count = ARRAY_SIZE(axg_clk_muxes),
- .clk_dividers = axg_clk_dividers,
- .clk_dividers_count = ARRAY_SIZE(axg_clk_dividers),
- .hw_onecell_data = &axg_hw_onecell_data,
+ &axg_mpeg_clk_sel,
+ &axg_sd_emmc_b_clk0_sel,
+ &axg_sd_emmc_c_clk0_sel,
+ &axg_mpll0,
+ &axg_mpll1,
+ &axg_mpll2,
+ &axg_mpll3,
+ &axg_mpll0_div,
+ &axg_mpll1_div,
+ &axg_mpll2_div,
+ &axg_mpll3_div,
+ &axg_fixed_pll,
+ &axg_sys_pll,
+ &axg_gp0_pll,
+ &axg_hifi_pll,
+ &axg_mpll_prediv,
+ &axg_fclk_div2,
+ &axg_fclk_div3,
+ &axg_fclk_div4,
+ &axg_fclk_div5,
+ &axg_fclk_div7,
};
static const struct of_device_id clkc_match_table[] = {
- { .compatible = "amlogic,axg-clkc", .data = &axg_clkc_data },
+ { .compatible = "amlogic,axg-clkc" },
{}
};
+static const struct regmap_config clkc_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};
+
static int axg_clkc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- const struct clkc_data *clkc_data;
struct resource *res;
- void __iomem *clk_base;
- int ret, clkid, i;
-
- clkc_data = of_device_get_match_data(&pdev->dev);
- if (!clkc_data)
- return -EINVAL;
-
- /* Generic clocks and PLLs */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -EINVAL;
- clk_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
- if (!clk_base) {
- dev_err(&pdev->dev, "Unable to map clk base\n");
- return -ENXIO;
- }
+ void __iomem *clk_base = NULL;
+ struct regmap *map;
+ int ret, i;
- /* Populate base address for PLLs */
- for (i = 0; i < clkc_data->clk_plls_count; i++)
- clkc_data->clk_plls[i]->base = clk_base;
+ /* Get the hhi system controller node if available */
+ map = syscon_node_to_regmap(of_get_parent(dev->of_node));
+ if (IS_ERR(map)) {
+ dev_err(dev,
+ "failed to get HHI regmap - Trying obsolete regs\n");
- /* Populate base address for MPLLs */
- for (i = 0; i < clkc_data->clk_mplls_count; i++)
- clkc_data->clk_mplls[i]->base = clk_base;
+ /*
+ * FIXME: HHI registers should be accessed through
+ * the appropriate system controller. This is required because
+ * there is more than just clocks in this register space
+ *
+ * This fallback method is only provided temporarily until
+ * all the platform DTs are properly using the syscon node
+ */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
- /* Populate base address for gates */
- for (i = 0; i < clkc_data->clk_gates_count; i++)
- clkc_data->clk_gates[i]->reg = clk_base +
- (u64)clkc_data->clk_gates[i]->reg;
- /* Populate base address for muxes */
- for (i = 0; i < clkc_data->clk_muxes_count; i++)
- clkc_data->clk_muxes[i]->reg = clk_base +
- (u64)clkc_data->clk_muxes[i]->reg;
+ clk_base = devm_ioremap(dev, res->start, resource_size(res));
+ if (!clk_base) {
+ dev_err(dev, "Unable to map clk base\n");
+ return -ENXIO;
+ }
+
+ map = devm_regmap_init_mmio(dev, clk_base,
+ &clkc_regmap_config);
+ if (IS_ERR(map))
+ return PTR_ERR(map);
+ }
- /* Populate base address for dividers */
- for (i = 0; i < clkc_data->clk_dividers_count; i++)
- clkc_data->clk_dividers[i]->reg = clk_base +
- (u64)clkc_data->clk_dividers[i]->reg;
+ /* Populate regmap for the regmap backed clocks */
+ for (i = 0; i < ARRAY_SIZE(axg_clk_regmaps); i++)
+ axg_clk_regmaps[i]->map = map;
- for (clkid = 0; clkid < clkc_data->hw_onecell_data->num; clkid++) {
+ for (i = 0; i < axg_hw_onecell_data.num; i++) {
/* array might be sparse */
- if (!clkc_data->hw_onecell_data->hws[clkid])
+ if (!axg_hw_onecell_data.hws[i])
continue;
- ret = devm_clk_hw_register(dev,
- clkc_data->hw_onecell_data->hws[clkid]);
+ ret = devm_clk_hw_register(dev, axg_hw_onecell_data.hws[i]);
if (ret) {
- dev_err(&pdev->dev, "Clock registration failed\n");
+ dev_err(dev, "Clock registration failed\n");
return ret;
}
}
- return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
- clkc_data->hw_onecell_data);
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
+ &axg_hw_onecell_data);
}
static struct platform_driver axg_driver = {
diff --git a/drivers/clk/meson/axg.h b/drivers/clk/meson/axg.h
index ce0bafdb6b28..b421df6a7ea0 100644
--- a/drivers/clk/meson/axg.h
+++ b/drivers/clk/meson/axg.h
@@ -117,8 +117,18 @@
#define CLKID_SD_EMMC_B_CLK0_DIV 62
#define CLKID_SD_EMMC_C_CLK0_SEL 63
#define CLKID_SD_EMMC_C_CLK0_DIV 64
+#define CLKID_MPLL0_DIV 65
+#define CLKID_MPLL1_DIV 66
+#define CLKID_MPLL2_DIV 67
+#define CLKID_MPLL3_DIV 68
+#define CLKID_MPLL_PREDIV 70
+#define CLKID_FCLK_DIV2_DIV 71
+#define CLKID_FCLK_DIV3_DIV 72
+#define CLKID_FCLK_DIV4_DIV 73
+#define CLKID_FCLK_DIV5_DIV 74
+#define CLKID_FCLK_DIV7_DIV 75
-#define NR_CLKS 65
+#define NR_CLKS 76
/* include the CLKIDs that have been made part of the DT binding */
#include <dt-bindings/clock/axg-clkc.h>
diff --git a/drivers/clk/meson/clk-audio-divider.c b/drivers/clk/meson/clk-audio-divider.c
index 6c07db06642d..f7ab5b1db342 100644
--- a/drivers/clk/meson/clk-audio-divider.c
+++ b/drivers/clk/meson/clk-audio-divider.c
@@ -28,8 +28,11 @@
#include <linux/clk-provider.h>
#include "clkc.h"
-#define to_meson_clk_audio_divider(_hw) container_of(_hw, \
- struct meson_clk_audio_divider, hw)
+static inline struct meson_clk_audio_div_data *
+meson_clk_audio_div_data(struct clk_regmap *clk)
+{
+ return (struct meson_clk_audio_div_data *)clk->data;
+}
static int _div_round(unsigned long parent_rate, unsigned long rate,
unsigned long flags)
@@ -45,15 +48,9 @@ static int _get_val(unsigned long parent_rate, unsigned long rate)
return DIV_ROUND_UP_ULL((u64)parent_rate, rate) - 1;
}
-static int _valid_divider(struct clk_hw *hw, int divider)
+static int _valid_divider(unsigned int width, int divider)
{
- struct meson_clk_audio_divider *adiv =
- to_meson_clk_audio_divider(hw);
- int max_divider;
- u8 width;
-
- width = adiv->div.width;
- max_divider = 1 << width;
+ int max_divider = 1 << width;
return clamp(divider, 1, max_divider);
}
@@ -61,14 +58,11 @@ static int _valid_divider(struct clk_hw *hw, int divider)
static unsigned long audio_divider_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
- struct meson_clk_audio_divider *adiv =
- to_meson_clk_audio_divider(hw);
- struct parm *p;
- unsigned long reg, divider;
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_clk_audio_div_data *adiv = meson_clk_audio_div_data(clk);
+ unsigned long divider;
- p = &adiv->div;
- reg = readl(adiv->base + p->reg_off);
- divider = PARM_GET(p->width, p->shift, reg) + 1;
+ divider = meson_parm_read(clk->map, &adiv->div);
return DIV_ROUND_UP_ULL((u64)parent_rate, divider);
}
@@ -77,14 +71,14 @@ static long audio_divider_round_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long *parent_rate)
{
- struct meson_clk_audio_divider *adiv =
- to_meson_clk_audio_divider(hw);
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_clk_audio_div_data *adiv = meson_clk_audio_div_data(clk);
unsigned long max_prate;
int divider;
if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) {
divider = _div_round(*parent_rate, rate, adiv->flags);
- divider = _valid_divider(hw, divider);
+ divider = _valid_divider(adiv->div.width, divider);
return DIV_ROUND_UP_ULL((u64)*parent_rate, divider);
}
@@ -93,7 +87,7 @@ static long audio_divider_round_rate(struct clk_hw *hw,
/* Get the corresponding rounded down divider */
divider = max_prate / rate;
- divider = _valid_divider(hw, divider);
+ divider = _valid_divider(adiv->div.width, divider);
/* Get actual rate of the parent */
*parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw),
@@ -106,28 +100,11 @@ static int audio_divider_set_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long parent_rate)
{
- struct meson_clk_audio_divider *adiv =
- to_meson_clk_audio_divider(hw);
- struct parm *p;
- unsigned long reg, flags = 0;
- int val;
-
- val = _get_val(parent_rate, rate);
-
- if (adiv->lock)
- spin_lock_irqsave(adiv->lock, flags);
- else
- __acquire(adiv->lock);
-
- p = &adiv->div;
- reg = readl(adiv->base + p->reg_off);
- reg = PARM_SET(p->width, p->shift, reg, val);
- writel(reg, adiv->base + p->reg_off);
-
- if (adiv->lock)
- spin_unlock_irqrestore(adiv->lock, flags);
- else
- __release(adiv->lock);
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_clk_audio_div_data *adiv = meson_clk_audio_div_data(clk);
+ int val = _get_val(parent_rate, rate);
+
+ meson_parm_write(clk->map, &adiv->div, val);
return 0;
}
diff --git a/drivers/clk/meson/clk-cpu.c b/drivers/clk/meson/clk-cpu.c
deleted file mode 100644
index f8b2b7efd016..000000000000
--- a/drivers/clk/meson/clk-cpu.c
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright (c) 2015 Endless Mobile, Inc.
- * Author: Carlo Caione <carlo@endlessm.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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, see <http://www.gnu.org/licenses/>.
- */
-
-/*
- * CPU clock path:
- *
- * +-[/N]-----|3|
- * MUX2 +--[/3]-+----------|2| MUX1
- * [sys_pll]---|1| |--[/2]------------|1|-|1|
- * | |---+------------------|0| | |----- [a5_clk]
- * +--|0| | |
- * [xtal]---+-------------------------------|0|
- *
- *
- *
- */
-
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/of_address.h>
-#include <linux/slab.h>
-#include <linux/clk.h>
-#include <linux/clk-provider.h>
-
-#define MESON_CPU_CLK_CNTL1 0x00
-#define MESON_CPU_CLK_CNTL 0x40
-
-#define MESON_CPU_CLK_MUX1 BIT(7)
-#define MESON_CPU_CLK_MUX2 BIT(0)
-
-#define MESON_N_WIDTH 9
-#define MESON_N_SHIFT 20
-#define MESON_SEL_WIDTH 2
-#define MESON_SEL_SHIFT 2
-
-#include "clkc.h"
-
-#define to_meson_clk_cpu_hw(_hw) container_of(_hw, struct meson_clk_cpu, hw)
-#define to_meson_clk_cpu_nb(_nb) container_of(_nb, struct meson_clk_cpu, clk_nb)
-
-static long meson_clk_cpu_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
-{
- struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_hw(hw);
-
- return divider_round_rate(hw, rate, prate, clk_cpu->div_table,
- MESON_N_WIDTH, CLK_DIVIDER_ROUND_CLOSEST);
-}
-
-static int meson_clk_cpu_set_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long parent_rate)
-{
- struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_hw(hw);
- unsigned int div, sel, N = 0;
- u32 reg;
-
- div = DIV_ROUND_UP(parent_rate, rate);
-
- if (div <= 3) {
- sel = div - 1;
- } else {
- sel = 3;
- N = div / 2;
- }
-
- reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL1);
- reg = PARM_SET(MESON_N_WIDTH, MESON_N_SHIFT, reg, N);
- writel(reg, clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL1);
-
- reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL);
- reg = PARM_SET(MESON_SEL_WIDTH, MESON_SEL_SHIFT, reg, sel);
- writel(reg, clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL);
-
- return 0;
-}
-
-static unsigned long meson_clk_cpu_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
-{
- struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_hw(hw);
- unsigned int N, sel;
- unsigned int div = 1;
- u32 reg;
-
- reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL1);
- N = PARM_GET(MESON_N_WIDTH, MESON_N_SHIFT, reg);
-
- reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL);
- sel = PARM_GET(MESON_SEL_WIDTH, MESON_SEL_SHIFT, reg);
-
- if (sel < 3)
- div = sel + 1;
- else
- div = 2 * N;
-
- return parent_rate / div;
-}
-
-/* FIXME MUX1 & MUX2 should be struct clk_hw objects */
-static int meson_clk_cpu_pre_rate_change(struct meson_clk_cpu *clk_cpu,
- struct clk_notifier_data *ndata)
-{
- u32 cpu_clk_cntl;
-
- /* switch MUX1 to xtal */
- cpu_clk_cntl = readl(clk_cpu->base + clk_cpu->reg_off
- + MESON_CPU_CLK_CNTL);
- cpu_clk_cntl &= ~MESON_CPU_CLK_MUX1;
- writel(cpu_clk_cntl, clk_cpu->base + clk_cpu->reg_off
- + MESON_CPU_CLK_CNTL);
- udelay(100);
-
- /* switch MUX2 to sys-pll */
- cpu_clk_cntl |= MESON_CPU_CLK_MUX2;
- writel(cpu_clk_cntl, clk_cpu->base + clk_cpu->reg_off
- + MESON_CPU_CLK_CNTL);
-
- return 0;
-}
-
-/* FIXME MUX1 & MUX2 should be struct clk_hw objects */
-static int meson_clk_cpu_post_rate_change(struct meson_clk_cpu *clk_cpu,
- struct clk_notifier_data *ndata)
-{
- u32 cpu_clk_cntl;
-
- /* switch MUX1 to divisors' output */
- cpu_clk_cntl = readl(clk_cpu->base + clk_cpu->reg_off
- + MESON_CPU_CLK_CNTL);
- cpu_clk_cntl |= MESON_CPU_CLK_MUX1;
- writel(cpu_clk_cntl, clk_cpu->base + clk_cpu->reg_off
- + MESON_CPU_CLK_CNTL);
- udelay(100);
-
- return 0;
-}
-
-/*
- * This clock notifier is called when the frequency of the of the parent
- * PLL clock is to be changed. We use the xtal input as temporary parent
- * while the PLL frequency is stabilized.
- */
-int meson_clk_cpu_notifier_cb(struct notifier_block *nb,
- unsigned long event, void *data)
-{
- struct clk_notifier_data *ndata = data;
- struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_nb(nb);
- int ret = 0;
-
- if (event == PRE_RATE_CHANGE)
- ret = meson_clk_cpu_pre_rate_change(clk_cpu, ndata);
- else if (event == POST_RATE_CHANGE)
- ret = meson_clk_cpu_post_rate_change(clk_cpu, ndata);
-
- return notifier_from_errno(ret);
-}
-
-const struct clk_ops meson_clk_cpu_ops = {
- .recalc_rate = meson_clk_cpu_recalc_rate,
- .round_rate = meson_clk_cpu_round_rate,
- .set_rate = meson_clk_cpu_set_rate,
-};
diff --git a/drivers/clk/meson/clk-mpll.c b/drivers/clk/meson/clk-mpll.c
index 5144360e2c80..0df1227b65b3 100644
--- a/drivers/clk/meson/clk-mpll.c
+++ b/drivers/clk/meson/clk-mpll.c
@@ -68,11 +68,15 @@
#define N2_MIN 4
#define N2_MAX 511
-#define to_meson_clk_mpll(_hw) container_of(_hw, struct meson_clk_mpll, hw)
+static inline struct meson_clk_mpll_data *
+meson_clk_mpll_data(struct clk_regmap *clk)
+{
+ return (struct meson_clk_mpll_data *)clk->data;
+}
static long rate_from_params(unsigned long parent_rate,
- unsigned long sdm,
- unsigned long n2)
+ unsigned int sdm,
+ unsigned int n2)
{
unsigned long divisor = (SDM_DEN * n2) + sdm;
@@ -84,8 +88,8 @@ static long rate_from_params(unsigned long parent_rate,
static void params_from_rate(unsigned long requested_rate,
unsigned long parent_rate,
- unsigned long *sdm,
- unsigned long *n2)
+ unsigned int *sdm,
+ unsigned int *n2)
{
uint64_t div = parent_rate;
unsigned long rem = do_div(div, requested_rate);
@@ -105,31 +109,23 @@ static void params_from_rate(unsigned long requested_rate,
static unsigned long mpll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
- struct meson_clk_mpll *mpll = to_meson_clk_mpll(hw);
- struct parm *p;
- unsigned long reg, sdm, n2;
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_clk_mpll_data *mpll = meson_clk_mpll_data(clk);
+ unsigned int sdm, n2;
long rate;
- p = &mpll->sdm;
- reg = readl(mpll->base + p->reg_off);
- sdm = PARM_GET(p->width, p->shift, reg);
-
- p = &mpll->n2;
- reg = readl(mpll->base + p->reg_off);
- n2 = PARM_GET(p->width, p->shift, reg);
+ sdm = meson_parm_read(clk->map, &mpll->sdm);
+ n2 = meson_parm_read(clk->map, &mpll->n2);
rate = rate_from_params(parent_rate, sdm, n2);
- if (rate < 0)
- return 0;
-
- return rate;
+ return rate < 0 ? 0 : rate;
}
static long mpll_round_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long *parent_rate)
{
- unsigned long sdm, n2;
+ unsigned int sdm, n2;
params_from_rate(rate, *parent_rate, &sdm, &n2);
return rate_from_params(*parent_rate, sdm, n2);
@@ -139,9 +135,9 @@ static int mpll_set_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long parent_rate)
{
- struct meson_clk_mpll *mpll = to_meson_clk_mpll(hw);
- struct parm *p;
- unsigned long reg, sdm, n2;
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_clk_mpll_data *mpll = meson_clk_mpll_data(clk);
+ unsigned int sdm, n2;
unsigned long flags = 0;
params_from_rate(rate, parent_rate, &sdm, &n2);
@@ -151,97 +147,36 @@ static int mpll_set_rate(struct clk_hw *hw,
else
__acquire(mpll->lock);
- p = &mpll->sdm;
- reg = readl(mpll->base + p->reg_off);
- reg = PARM_SET(p->width, p->shift, reg, sdm);
- writel(reg, mpll->base + p->reg_off);
-
- p = &mpll->sdm_en;
- reg = readl(mpll->base + p->reg_off);
- reg = PARM_SET(p->width, p->shift, reg, 1);
- writel(reg, mpll->base + p->reg_off);
-
- p = &mpll->ssen;
- if (p->width != 0) {
- reg = readl(mpll->base + p->reg_off);
- reg = PARM_SET(p->width, p->shift, reg, 1);
- writel(reg, mpll->base + p->reg_off);
- }
-
- p = &mpll->n2;
- reg = readl(mpll->base + p->reg_off);
- reg = PARM_SET(p->width, p->shift, reg, n2);
- writel(reg, mpll->base + p->reg_off);
-
- if (mpll->lock)
- spin_unlock_irqrestore(mpll->lock, flags);
- else
- __release(mpll->lock);
-
- return 0;
-}
+ /* Enable and set the fractional part */
+ meson_parm_write(clk->map, &mpll->sdm, sdm);
+ meson_parm_write(clk->map, &mpll->sdm_en, 1);
-static void mpll_enable_core(struct clk_hw *hw, int enable)
-{
- struct meson_clk_mpll *mpll = to_meson_clk_mpll(hw);
- struct parm *p;
- unsigned long reg;
- unsigned long flags = 0;
+ /* Set additional fractional part enable if required */
+ if (MESON_PARM_APPLICABLE(&mpll->ssen))
+ meson_parm_write(clk->map, &mpll->ssen, 1);
- if (mpll->lock)
- spin_lock_irqsave(mpll->lock, flags);
- else
- __acquire(mpll->lock);
+ /* Set the integer divider part */
+ meson_parm_write(clk->map, &mpll->n2, n2);
- p = &mpll->en;
- reg = readl(mpll->base + p->reg_off);
- reg = PARM_SET(p->width, p->shift, reg, enable ? 1 : 0);
- writel(reg, mpll->base + p->reg_off);
+ /* Set the magic misc bit if required */
+ if (MESON_PARM_APPLICABLE(&mpll->misc))
+ meson_parm_write(clk->map, &mpll->misc, 1);
if (mpll->lock)
spin_unlock_irqrestore(mpll->lock, flags);
else
__release(mpll->lock);
-}
-
-
-static int mpll_enable(struct clk_hw *hw)
-{
- mpll_enable_core(hw, 1);
return 0;
}
-static void mpll_disable(struct clk_hw *hw)
-{
- mpll_enable_core(hw, 0);
-}
-
-static int mpll_is_enabled(struct clk_hw *hw)
-{
- struct meson_clk_mpll *mpll = to_meson_clk_mpll(hw);
- struct parm *p;
- unsigned long reg;
- int en;
-
- p = &mpll->en;
- reg = readl(mpll->base + p->reg_off);
- en = PARM_GET(p->width, p->shift, reg);
-
- return en;
-}
-
const struct clk_ops meson_clk_mpll_ro_ops = {
.recalc_rate = mpll_recalc_rate,
.round_rate = mpll_round_rate,
- .is_enabled = mpll_is_enabled,
};
const struct clk_ops meson_clk_mpll_ops = {
.recalc_rate = mpll_recalc_rate,
.round_rate = mpll_round_rate,
.set_rate = mpll_set_rate,
- .enable = mpll_enable,
- .disable = mpll_disable,
- .is_enabled = mpll_is_enabled,
};
diff --git a/drivers/clk/meson/clk-pll.c b/drivers/clk/meson/clk-pll.c
index 01341553f50b..65a7bd903551 100644
--- a/drivers/clk/meson/clk-pll.c
+++ b/drivers/clk/meson/clk-pll.c
@@ -2,6 +2,9 @@
* Copyright (c) 2015 Endless Mobile, Inc.
* Author: Carlo Caione <carlo@endlessm.com>
*
+ * Copyright (c) 2018 Baylibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ *
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
@@ -27,13 +30,14 @@
* | |
* FREF VCO
*
- * out = (in * M / N) >> OD
+ * out = in * (m + frac / frac_max) / (n << sum(ods))
*/
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
+#include <linux/math64.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/slab.h>
@@ -41,209 +45,213 @@
#include "clkc.h"
-#define MESON_PLL_RESET BIT(29)
-#define MESON_PLL_LOCK BIT(31)
-
-#define to_meson_clk_pll(_hw) container_of(_hw, struct meson_clk_pll, hw)
-
-static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
+static inline struct meson_clk_pll_data *
+meson_clk_pll_data(struct clk_regmap *clk)
{
- struct meson_clk_pll *pll = to_meson_clk_pll(hw);
- struct parm *p;
- unsigned long parent_rate_mhz = parent_rate / 1000000;
- unsigned long rate_mhz;
- u16 n, m, frac = 0, od, od2 = 0;
- u32 reg;
-
- p = &pll->n;
- reg = readl(pll->base + p->reg_off);
- n = PARM_GET(p->width, p->shift, reg);
-
- p = &pll->m;
- reg = readl(pll->base + p->reg_off);
- m = PARM_GET(p->width, p->shift, reg);
-
- p = &pll->od;
- reg = readl(pll->base + p->reg_off);
- od = PARM_GET(p->width, p->shift, reg);
-
- p = &pll->od2;
- if (p->width) {
- reg = readl(pll->base + p->reg_off);
- od2 = PARM_GET(p->width, p->shift, reg);
- }
-
- p = &pll->frac;
- if (p->width) {
- reg = readl(pll->base + p->reg_off);
- frac = PARM_GET(p->width, p->shift, reg);
- rate_mhz = (parent_rate_mhz * m + \
- (parent_rate_mhz * frac >> 12)) * 2 / n;
- rate_mhz = rate_mhz >> od >> od2;
- } else
- rate_mhz = (parent_rate_mhz * m / n) >> od >> od2;
-
- return rate_mhz * 1000000;
+ return (struct meson_clk_pll_data *)clk->data;
}
-static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static unsigned long __pll_params_to_rate(unsigned long parent_rate,
+ const struct pll_rate_table *pllt,
+ u16 frac,
+ struct meson_clk_pll_data *pll)
{
- struct meson_clk_pll *pll = to_meson_clk_pll(hw);
- const struct pll_rate_table *rate_table = pll->rate_table;
- int i;
+ u64 rate = (u64)parent_rate * pllt->m;
+ unsigned int od = pllt->od + pllt->od2 + pllt->od3;
- for (i = 0; i < pll->rate_count; i++) {
- if (rate <= rate_table[i].rate)
- return rate_table[i].rate;
+ if (frac && MESON_PARM_APPLICABLE(&pll->frac)) {
+ u64 frac_rate = (u64)parent_rate * frac;
+
+ rate += DIV_ROUND_UP_ULL(frac_rate,
+ (1 << pll->frac.width));
}
- /* else return the smallest value */
- return rate_table[0].rate;
+ return DIV_ROUND_UP_ULL(rate, pllt->n << od);
}
-static const struct pll_rate_table *meson_clk_get_pll_settings(struct meson_clk_pll *pll,
- unsigned long rate)
+static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
{
- const struct pll_rate_table *rate_table = pll->rate_table;
- int i;
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
+ struct pll_rate_table pllt;
+ u16 frac;
- for (i = 0; i < pll->rate_count; i++) {
- if (rate == rate_table[i].rate)
- return &rate_table[i];
- }
- return NULL;
+ pllt.n = meson_parm_read(clk->map, &pll->n);
+ pllt.m = meson_parm_read(clk->map, &pll->m);
+ pllt.od = meson_parm_read(clk->map, &pll->od);
+
+ pllt.od2 = MESON_PARM_APPLICABLE(&pll->od2) ?
+ meson_parm_read(clk->map, &pll->od2) :
+ 0;
+
+ pllt.od3 = MESON_PARM_APPLICABLE(&pll->od3) ?
+ meson_parm_read(clk->map, &pll->od3) :
+ 0;
+
+ frac = MESON_PARM_APPLICABLE(&pll->frac) ?
+ meson_parm_read(clk->map, &pll->frac) :
+ 0;
+
+ return __pll_params_to_rate(parent_rate, &pllt, frac, pll);
}
-/* Specific wait loop for GXL/GXM GP0 PLL */
-static int meson_clk_pll_wait_lock_reset(struct meson_clk_pll *pll,
- struct parm *p_n)
+static u16 __pll_params_with_frac(unsigned long rate,
+ unsigned long parent_rate,
+ const struct pll_rate_table *pllt,
+ struct meson_clk_pll_data *pll)
{
- int delay = 100;
- u32 reg;
+ u16 frac_max = (1 << pll->frac.width);
+ u64 val = (u64)rate * pllt->n;
- while (delay > 0) {
- reg = readl(pll->base + p_n->reg_off);
- writel(reg | MESON_PLL_RESET, pll->base + p_n->reg_off);
- udelay(10);
- writel(reg & ~MESON_PLL_RESET, pll->base + p_n->reg_off);
+ val <<= pllt->od + pllt->od2 + pllt->od3;
- /* This delay comes from AMLogic tree clk-gp0-gxl driver */
- mdelay(1);
+ if (pll->flags & CLK_MESON_PLL_ROUND_CLOSEST)
+ val = DIV_ROUND_CLOSEST_ULL(val * frac_max, parent_rate);
+ else
+ val = div_u64(val * frac_max, parent_rate);
- reg = readl(pll->base + p_n->reg_off);
- if (reg & MESON_PLL_LOCK)
- return 0;
- delay--;
+ val -= pllt->m * frac_max;
+
+ return min((u16)val, (u16)(frac_max - 1));
+}
+
+static const struct pll_rate_table *
+meson_clk_get_pll_settings(unsigned long rate,
+ struct meson_clk_pll_data *pll)
+{
+ const struct pll_rate_table *table = pll->table;
+ unsigned int i = 0;
+
+ if (!table)
+ return NULL;
+
+ /* Find the first table element exceeding rate */
+ while (table[i].rate && table[i].rate <= rate)
+ i++;
+
+ if (i != 0) {
+ if (MESON_PARM_APPLICABLE(&pll->frac) ||
+ !(pll->flags & CLK_MESON_PLL_ROUND_CLOSEST) ||
+ (abs(rate - table[i - 1].rate) <
+ abs(rate - table[i].rate)))
+ i--;
}
- return -ETIMEDOUT;
+
+ return (struct pll_rate_table *)&table[i];
}
-static int meson_clk_pll_wait_lock(struct meson_clk_pll *pll,
- struct parm *p_n)
+static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
{
- int delay = 24000000;
- u32 reg;
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
+ const struct pll_rate_table *pllt =
+ meson_clk_get_pll_settings(rate, pll);
+ u16 frac;
+
+ if (!pllt)
+ return meson_clk_pll_recalc_rate(hw, *parent_rate);
+
+ if (!MESON_PARM_APPLICABLE(&pll->frac)
+ || rate == pllt->rate)
+ return pllt->rate;
+
+ /*
+ * The rate provided by the setting is not an exact match, let's
+ * try to improve the result using the fractional parameter
+ */
+ frac = __pll_params_with_frac(rate, *parent_rate, pllt, pll);
+
+ return __pll_params_to_rate(*parent_rate, pllt, frac, pll);
+}
- while (delay > 0) {
- reg = readl(pll->base + p_n->reg_off);
+static int meson_clk_pll_wait_lock(struct clk_hw *hw)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
+ int delay = 24000000;
- if (reg & MESON_PLL_LOCK)
+ do {
+ /* Is the clock locked now ? */
+ if (meson_parm_read(clk->map, &pll->l))
return 0;
+
delay--;
- }
+ } while (delay > 0);
+
return -ETIMEDOUT;
}
-static void meson_clk_pll_init_params(struct meson_clk_pll *pll)
+static void meson_clk_pll_init(struct clk_hw *hw)
{
- int i;
-
- for (i = 0 ; i < pll->params.params_count ; ++i)
- writel(pll->params.params_table[i].value,
- pll->base + pll->params.params_table[i].reg_off);
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
+
+ if (pll->init_count) {
+ meson_parm_write(clk->map, &pll->rst, 1);
+ regmap_multi_reg_write(clk->map, pll->init_regs,
+ pll->init_count);
+ meson_parm_write(clk->map, &pll->rst, 0);
+ }
}
static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
- struct meson_clk_pll *pll = to_meson_clk_pll(hw);
- struct parm *p;
- const struct pll_rate_table *rate_set;
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
+ const struct pll_rate_table *pllt;
unsigned long old_rate;
- int ret = 0;
- u32 reg;
+ u16 frac = 0;
if (parent_rate == 0 || rate == 0)
return -EINVAL;
old_rate = rate;
- rate_set = meson_clk_get_pll_settings(pll, rate);
- if (!rate_set)
+ pllt = meson_clk_get_pll_settings(rate, pll);
+ if (!pllt)
return -EINVAL;
- /* Initialize the PLL in a clean state if specified */
- if (pll->params.params_count)
- meson_clk_pll_init_params(pll);
-
- /* PLL reset */
- p = &pll->n;
- reg = readl(pll->base + p->reg_off);
- /* If no_init_reset is provided, avoid resetting at this point */
- if (!pll->params.no_init_reset)
- writel(reg | MESON_PLL_RESET, pll->base + p->reg_off);
-
- reg = PARM_SET(p->width, p->shift, reg, rate_set->n);
- writel(reg, pll->base + p->reg_off);
-
- p = &pll->m;
- reg = readl(pll->base + p->reg_off);
- reg = PARM_SET(p->width, p->shift, reg, rate_set->m);
- writel(reg, pll->base + p->reg_off);
-
- p = &pll->od;
- reg = readl(pll->base + p->reg_off);
- reg = PARM_SET(p->width, p->shift, reg, rate_set->od);
- writel(reg, pll->base + p->reg_off);
-
- p = &pll->od2;
- if (p->width) {
- reg = readl(pll->base + p->reg_off);
- reg = PARM_SET(p->width, p->shift, reg, rate_set->od2);
- writel(reg, pll->base + p->reg_off);
- }
+ /* Put the pll in reset to write the params */
+ meson_parm_write(clk->map, &pll->rst, 1);
- p = &pll->frac;
- if (p->width) {
- reg = readl(pll->base + p->reg_off);
- reg = PARM_SET(p->width, p->shift, reg, rate_set->frac);
- writel(reg, pll->base + p->reg_off);
- }
+ meson_parm_write(clk->map, &pll->n, pllt->n);
+ meson_parm_write(clk->map, &pll->m, pllt->m);
+ meson_parm_write(clk->map, &pll->od, pllt->od);
+
+ if (MESON_PARM_APPLICABLE(&pll->od2))
+ meson_parm_write(clk->map, &pll->od2, pllt->od2);
+
+ if (MESON_PARM_APPLICABLE(&pll->od3))
+ meson_parm_write(clk->map, &pll->od3, pllt->od3);
- p = &pll->n;
- /* If clear_reset_for_lock is provided, remove the reset bit here */
- if (pll->params.clear_reset_for_lock) {
- reg = readl(pll->base + p->reg_off);
- writel(reg & ~MESON_PLL_RESET, pll->base + p->reg_off);
+ if (MESON_PARM_APPLICABLE(&pll->frac)) {
+ frac = __pll_params_with_frac(rate, parent_rate, pllt, pll);
+ meson_parm_write(clk->map, &pll->frac, frac);
}
- /* If reset_lock_loop, use a special loop including resetting */
- if (pll->params.reset_lock_loop)
- ret = meson_clk_pll_wait_lock_reset(pll, p);
- else
- ret = meson_clk_pll_wait_lock(pll, p);
- if (ret) {
+ /* make sure the reset is cleared at this point */
+ meson_parm_write(clk->map, &pll->rst, 0);
+
+ if (meson_clk_pll_wait_lock(hw)) {
pr_warn("%s: pll did not lock, trying to restore old rate %lu\n",
__func__, old_rate);
+ /*
+ * FIXME: Do we really need/want this HACK ?
+ * It looks unsafe. what happens if the clock gets into a
+ * broken state and we can't lock back on the old_rate ? Looks
+ * like an infinite recursion is possible
+ */
meson_clk_pll_set_rate(hw, old_rate, parent_rate);
}
- return ret;
+ return 0;
}
const struct clk_ops meson_clk_pll_ops = {
+ .init = meson_clk_pll_init,
.recalc_rate = meson_clk_pll_recalc_rate,
.round_rate = meson_clk_pll_round_rate,
.set_rate = meson_clk_pll_set_rate,
diff --git a/drivers/clk/meson/clk-regmap.c b/drivers/clk/meson/clk-regmap.c
new file mode 100644
index 000000000000..3645fdb62343
--- /dev/null
+++ b/drivers/clk/meson/clk-regmap.c
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#include "clk-regmap.h"
+
+static int clk_regmap_gate_endisable(struct clk_hw *hw, int enable)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct clk_regmap_gate_data *gate = clk_get_regmap_gate_data(clk);
+ int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0;
+
+ set ^= enable;
+
+ return regmap_update_bits(clk->map, gate->offset, BIT(gate->bit_idx),
+ set ? BIT(gate->bit_idx) : 0);
+}
+
+static int clk_regmap_gate_enable(struct clk_hw *hw)
+{
+ return clk_regmap_gate_endisable(hw, 1);
+}
+
+static void clk_regmap_gate_disable(struct clk_hw *hw)
+{
+ clk_regmap_gate_endisable(hw, 0);
+}
+
+static int clk_regmap_gate_is_enabled(struct clk_hw *hw)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct clk_regmap_gate_data *gate = clk_get_regmap_gate_data(clk);
+ unsigned int val;
+
+ regmap_read(clk->map, gate->offset, &val);
+ if (gate->flags & CLK_GATE_SET_TO_DISABLE)
+ val ^= BIT(gate->bit_idx);
+
+ val &= BIT(gate->bit_idx);
+
+ return val ? 1 : 0;
+}
+
+const struct clk_ops clk_regmap_gate_ops = {
+ .enable = clk_regmap_gate_enable,
+ .disable = clk_regmap_gate_disable,
+ .is_enabled = clk_regmap_gate_is_enabled,
+};
+EXPORT_SYMBOL_GPL(clk_regmap_gate_ops);
+
+static unsigned long clk_regmap_div_recalc_rate(struct clk_hw *hw,
+ unsigned long prate)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk);
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(clk->map, div->offset, &val);
+ if (ret)
+ /* Gives a hint that something is wrong */
+ return 0;
+
+ val >>= div->shift;
+ val &= clk_div_mask(div->width);
+ return divider_recalc_rate(hw, prate, val, div->table, div->flags,
+ div->width);
+}
+
+static long clk_regmap_div_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk);
+ unsigned int val;
+ int ret;
+
+ /* if read only, just return current value */
+ if (div->flags & CLK_DIVIDER_READ_ONLY) {
+ ret = regmap_read(clk->map, div->offset, &val);
+ if (ret)
+ /* Gives a hint that something is wrong */
+ return 0;
+
+ val >>= div->shift;
+ val &= clk_div_mask(div->width);
+
+ return divider_ro_round_rate(hw, rate, prate, div->table,
+ div->width, div->flags, val);
+ }
+
+ return divider_round_rate(hw, rate, prate, div->table, div->width,
+ div->flags);
+}
+
+static int clk_regmap_div_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk);
+ unsigned int val;
+ int ret;
+
+ ret = divider_get_val(rate, parent_rate, div->table, div->width,
+ div->flags);
+ if (ret < 0)
+ return ret;
+
+ val = (unsigned int)ret << div->shift;
+ return regmap_update_bits(clk->map, div->offset,
+ clk_div_mask(div->width) << div->shift, val);
+};
+
+/* Would prefer clk_regmap_div_ro_ops but clashes with qcom */
+
+const struct clk_ops clk_regmap_divider_ops = {
+ .recalc_rate = clk_regmap_div_recalc_rate,
+ .round_rate = clk_regmap_div_round_rate,
+ .set_rate = clk_regmap_div_set_rate,
+};
+EXPORT_SYMBOL_GPL(clk_regmap_divider_ops);
+
+const struct clk_ops clk_regmap_divider_ro_ops = {
+ .recalc_rate = clk_regmap_div_recalc_rate,
+ .round_rate = clk_regmap_div_round_rate,
+};
+EXPORT_SYMBOL_GPL(clk_regmap_divider_ro_ops);
+
+static u8 clk_regmap_mux_get_parent(struct clk_hw *hw)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk);
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(clk->map, mux->offset, &val);
+ if (ret)
+ return ret;
+
+ val >>= mux->shift;
+ val &= mux->mask;
+ return clk_mux_val_to_index(hw, mux->table, mux->flags, val);
+}
+
+static int clk_regmap_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk);
+ unsigned int val = clk_mux_index_to_val(mux->table, mux->flags, index);
+
+ return regmap_update_bits(clk->map, mux->offset,
+ mux->mask << mux->shift,
+ val << mux->shift);
+}
+
+const struct clk_ops clk_regmap_mux_ops = {
+ .get_parent = clk_regmap_mux_get_parent,
+ .set_parent = clk_regmap_mux_set_parent,
+ .determine_rate = __clk_mux_determine_rate,
+};
+EXPORT_SYMBOL_GPL(clk_regmap_mux_ops);
+
+const struct clk_ops clk_regmap_mux_ro_ops = {
+ .get_parent = clk_regmap_mux_get_parent,
+};
+EXPORT_SYMBOL_GPL(clk_regmap_mux_ro_ops);
diff --git a/drivers/clk/meson/clk-regmap.h b/drivers/clk/meson/clk-regmap.h
new file mode 100644
index 000000000000..627c888026d7
--- /dev/null
+++ b/drivers/clk/meson/clk-regmap.h
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#ifndef __CLK_REGMAP_H
+#define __CLK_REGMAP_H
+
+#include <linux/clk-provider.h>
+#include <linux/regmap.h>
+
+/**
+ * struct clk_regmap - regmap backed clock
+ *
+ * @hw: handle between common and hardware-specific interfaces
+ * @map: pointer to the regmap structure controlling the clock
+ * @data: data specific to the clock type
+ *
+ * Clock which is controlled by regmap backed registers. The actual type of
+ * of the clock is controlled by the clock_ops and data.
+ */
+struct clk_regmap {
+ struct clk_hw hw;
+ struct regmap *map;
+ void *data;
+};
+
+#define to_clk_regmap(_hw) container_of(_hw, struct clk_regmap, hw)
+
+/**
+ * struct clk_regmap_gate_data - regmap backed gate specific data
+ *
+ * @offset: offset of the register controlling gate
+ * @bit_idx: single bit controlling gate
+ * @flags: hardware-specific flags
+ *
+ * Flags:
+ * Same as clk_gate except CLK_GATE_HIWORD_MASK which is ignored
+ */
+struct clk_regmap_gate_data {
+ unsigned int offset;
+ u8 bit_idx;
+ u8 flags;
+};
+
+static inline struct clk_regmap_gate_data *
+clk_get_regmap_gate_data(struct clk_regmap *clk)
+{
+ return (struct clk_regmap_gate_data *)clk->data;
+}
+
+extern const struct clk_ops clk_regmap_gate_ops;
+
+/**
+ * struct clk_regmap_div_data - regmap backed adjustable divider specific data
+ *
+ * @offset: offset of the register controlling the divider
+ * @shift: shift to the divider bit field
+ * @width: width of the divider bit field
+ * @table: array of value/divider pairs, last entry should have div = 0
+ *
+ * Flags:
+ * Same as clk_divider except CLK_DIVIDER_HIWORD_MASK which is ignored
+ */
+struct clk_regmap_div_data {
+ unsigned int offset;
+ u8 shift;
+ u8 width;
+ u8 flags;
+ const struct clk_div_table *table;
+};
+
+static inline struct clk_regmap_div_data *
+clk_get_regmap_div_data(struct clk_regmap *clk)
+{
+ return (struct clk_regmap_div_data *)clk->data;
+}
+
+extern const struct clk_ops clk_regmap_divider_ops;
+extern const struct clk_ops clk_regmap_divider_ro_ops;
+
+/**
+ * struct clk_regmap_mux_data - regmap backed multiplexer clock specific data
+ *
+ * @hw: handle between common and hardware-specific interfaces
+ * @offset: offset of theregister controlling multiplexer
+ * @table: array of parent indexed register values
+ * @shift: shift to multiplexer bit field
+ * @mask: mask of mutliplexer bit field
+ * @flags: hardware-specific flags
+ *
+ * Flags:
+ * Same as clk_divider except CLK_MUX_HIWORD_MASK which is ignored
+ */
+struct clk_regmap_mux_data {
+ unsigned int offset;
+ u32 *table;
+ u32 mask;
+ u8 shift;
+ u8 flags;
+};
+
+static inline struct clk_regmap_mux_data *
+clk_get_regmap_mux_data(struct clk_regmap *clk)
+{
+ return (struct clk_regmap_mux_data *)clk->data;
+}
+
+extern const struct clk_ops clk_regmap_mux_ops;
+extern const struct clk_ops clk_regmap_mux_ro_ops;
+
+#endif /* __CLK_REGMAP_H */
diff --git a/drivers/clk/meson/clkc.h b/drivers/clk/meson/clkc.h
index c2ff0520ce53..8fe73c4edca8 100644
--- a/drivers/clk/meson/clkc.h
+++ b/drivers/clk/meson/clkc.h
@@ -18,6 +18,9 @@
#ifndef __CLKC_H
#define __CLKC_H
+#include <linux/clk-provider.h>
+#include "clk-regmap.h"
+
#define PMASK(width) GENMASK(width - 1, 0)
#define SETPMASK(width, shift) GENMASK(shift + width - 1, shift)
#define CLRPMASK(width, shift) (~SETPMASK(width, shift))
@@ -35,13 +38,29 @@ struct parm {
u8 width;
};
+static inline unsigned int meson_parm_read(struct regmap *map, struct parm *p)
+{
+ unsigned int val;
+
+ regmap_read(map, p->reg_off, &val);
+ return PARM_GET(p->width, p->shift, val);
+}
+
+static inline void meson_parm_write(struct regmap *map, struct parm *p,
+ unsigned int val)
+{
+ regmap_update_bits(map, p->reg_off, SETPMASK(p->width, p->shift),
+ val << p->shift);
+}
+
+
struct pll_rate_table {
unsigned long rate;
u16 m;
u16 n;
u16 od;
u16 od2;
- u16 frac;
+ u16 od3;
};
#define PLL_RATE(_r, _m, _n, _od) \
@@ -50,97 +69,53 @@ struct pll_rate_table {
.m = (_m), \
.n = (_n), \
.od = (_od), \
- } \
-
-#define PLL_FRAC_RATE(_r, _m, _n, _od, _od2, _frac) \
- { \
- .rate = (_r), \
- .m = (_m), \
- .n = (_n), \
- .od = (_od), \
- .od2 = (_od2), \
- .frac = (_frac), \
- } \
-
-struct pll_params_table {
- unsigned int reg_off;
- unsigned int value;
-};
-
-#define PLL_PARAM(_reg, _val) \
- { \
- .reg_off = (_reg), \
- .value = (_val), \
}
-struct pll_setup_params {
- struct pll_params_table *params_table;
- unsigned int params_count;
- /* Workaround for GP0, do not reset before configuring */
- bool no_init_reset;
- /* Workaround for GP0, unreset right before checking for lock */
- bool clear_reset_for_lock;
- /* Workaround for GXL GP0, reset in the lock checking loop */
- bool reset_lock_loop;
-};
+#define CLK_MESON_PLL_ROUND_CLOSEST BIT(0)
-struct meson_clk_pll {
- struct clk_hw hw;
- void __iomem *base;
+struct meson_clk_pll_data {
struct parm m;
struct parm n;
struct parm frac;
struct parm od;
struct parm od2;
- const struct pll_setup_params params;
- const struct pll_rate_table *rate_table;
- unsigned int rate_count;
- spinlock_t *lock;
+ struct parm od3;
+ struct parm l;
+ struct parm rst;
+ const struct reg_sequence *init_regs;
+ unsigned int init_count;
+ const struct pll_rate_table *table;
+ u8 flags;
};
#define to_meson_clk_pll(_hw) container_of(_hw, struct meson_clk_pll, hw)
-struct meson_clk_cpu {
- struct clk_hw hw;
- void __iomem *base;
- u16 reg_off;
- struct notifier_block clk_nb;
- const struct clk_div_table *div_table;
-};
-
-int meson_clk_cpu_notifier_cb(struct notifier_block *nb, unsigned long event,
- void *data);
-
-struct meson_clk_mpll {
- struct clk_hw hw;
- void __iomem *base;
+struct meson_clk_mpll_data {
struct parm sdm;
struct parm sdm_en;
struct parm n2;
- struct parm en;
struct parm ssen;
+ struct parm misc;
spinlock_t *lock;
};
-struct meson_clk_audio_divider {
- struct clk_hw hw;
- void __iomem *base;
+struct meson_clk_audio_div_data {
struct parm div;
u8 flags;
- spinlock_t *lock;
};
#define MESON_GATE(_name, _reg, _bit) \
-struct clk_gate _name = { \
- .reg = (void __iomem *) _reg, \
- .bit_idx = (_bit), \
- .lock = &meson_clk_lock, \
- .hw.init = &(struct clk_init_data) { \
- .name = #_name, \
- .ops = &clk_gate_ops, \
+struct clk_regmap _name = { \
+ .data = &(struct clk_regmap_gate_data){ \
+ .offset = (_reg), \
+ .bit_idx = (_bit), \
+ }, \
+ .hw.init = &(struct clk_init_data) { \
+ .name = #_name, \
+ .ops = &clk_regmap_gate_ops, \
.parent_names = (const char *[]){ "clk81" }, \
.num_parents = 1, \
- .flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED), \
+ .flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED), \
}, \
};
diff --git a/drivers/clk/meson/gxbb-aoclk-regmap.c b/drivers/clk/meson/gxbb-aoclk-regmap.c
deleted file mode 100644
index 2515fbfa0467..000000000000
--- a/drivers/clk/meson/gxbb-aoclk-regmap.c
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (c) 2017 BayLibre, SAS.
- * Author: Neil Armstrong <narmstrong@baylibre.com>
- *
- * SPDX-License-Identifier: GPL-2.0+
- */
-
-#include <linux/clk-provider.h>
-#include <linux/bitfield.h>
-#include <linux/regmap.h>
-#include "gxbb-aoclk.h"
-
-static int aoclk_gate_regmap_enable(struct clk_hw *hw)
-{
- struct aoclk_gate_regmap *gate = to_aoclk_gate_regmap(hw);
-
- return regmap_update_bits(gate->regmap, AO_RTI_GEN_CNTL_REG0,
- BIT(gate->bit_idx), BIT(gate->bit_idx));
-}
-
-static void aoclk_gate_regmap_disable(struct clk_hw *hw)
-{
- struct aoclk_gate_regmap *gate = to_aoclk_gate_regmap(hw);
-
- regmap_update_bits(gate->regmap, AO_RTI_GEN_CNTL_REG0,
- BIT(gate->bit_idx), 0);
-}
-
-static int aoclk_gate_regmap_is_enabled(struct clk_hw *hw)
-{
- struct aoclk_gate_regmap *gate = to_aoclk_gate_regmap(hw);
- unsigned int val;
- int ret;
-
- ret = regmap_read(gate->regmap, AO_RTI_GEN_CNTL_REG0, &val);
- if (ret)
- return ret;
-
- return (val & BIT(gate->bit_idx)) != 0;
-}
-
-const struct clk_ops meson_aoclk_gate_regmap_ops = {
- .enable = aoclk_gate_regmap_enable,
- .disable = aoclk_gate_regmap_disable,
- .is_enabled = aoclk_gate_regmap_is_enabled,
-};
diff --git a/drivers/clk/meson/gxbb-aoclk.c b/drivers/clk/meson/gxbb-aoclk.c
index 6c161e0a8e59..9ec23ae9a219 100644
--- a/drivers/clk/meson/gxbb-aoclk.c
+++ b/drivers/clk/meson/gxbb-aoclk.c
@@ -62,10 +62,9 @@
#include <linux/delay.h>
#include <dt-bindings/clock/gxbb-aoclkc.h>
#include <dt-bindings/reset/gxbb-aoclkc.h>
+#include "clk-regmap.h"
#include "gxbb-aoclk.h"
-static DEFINE_SPINLOCK(gxbb_aoclk_lock);
-
struct gxbb_aoclk_reset_controller {
struct reset_controller_dev reset;
unsigned int *data;
@@ -87,12 +86,14 @@ static const struct reset_control_ops gxbb_aoclk_reset_ops = {
};
#define GXBB_AO_GATE(_name, _bit) \
-static struct aoclk_gate_regmap _name##_ao = { \
- .bit_idx = (_bit), \
- .lock = &gxbb_aoclk_lock, \
+static struct clk_regmap _name##_ao = { \
+ .data = &(struct clk_regmap_gate_data) { \
+ .offset = AO_RTI_GEN_CNTL_REG0, \
+ .bit_idx = (_bit), \
+ }, \
.hw.init = &(struct clk_init_data) { \
.name = #_name "_ao", \
- .ops = &meson_aoclk_gate_regmap_ops, \
+ .ops = &clk_regmap_gate_ops, \
.parent_names = (const char *[]){ "clk81" }, \
.num_parents = 1, \
.flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED), \
@@ -107,7 +108,6 @@ GXBB_AO_GATE(uart2, 5);
GXBB_AO_GATE(ir_blaster, 6);
static struct aoclk_cec_32k cec_32k_ao = {
- .lock = &gxbb_aoclk_lock,
.hw.init = &(struct clk_init_data) {
.name = "cec_32k_ao",
.ops = &meson_aoclk_cec_32k_ops,
@@ -126,7 +126,7 @@ static unsigned int gxbb_aoclk_reset[] = {
[RESET_AO_IR_BLASTER] = 23,
};
-static struct aoclk_gate_regmap *gxbb_aoclk_gate[] = {
+static struct clk_regmap *gxbb_aoclk_gate[] = {
[CLKID_AO_REMOTE] = &remote_ao,
[CLKID_AO_I2C_MASTER] = &i2c_master_ao,
[CLKID_AO_I2C_SLAVE] = &i2c_slave_ao,
@@ -177,10 +177,10 @@ static int gxbb_aoclkc_probe(struct platform_device *pdev)
* Populate regmap and register all clks
*/
for (clkid = 0; clkid < ARRAY_SIZE(gxbb_aoclk_gate); clkid++) {
- gxbb_aoclk_gate[clkid]->regmap = regmap;
+ gxbb_aoclk_gate[clkid]->map = regmap;
ret = devm_clk_hw_register(dev,
- gxbb_aoclk_onecell_data.hws[clkid]);
+ gxbb_aoclk_onecell_data.hws[clkid]);
if (ret)
return ret;
}
diff --git a/drivers/clk/meson/gxbb-aoclk.h b/drivers/clk/meson/gxbb-aoclk.h
index e8604c8f7eee..0be78383f257 100644
--- a/drivers/clk/meson/gxbb-aoclk.h
+++ b/drivers/clk/meson/gxbb-aoclk.h
@@ -17,22 +17,11 @@
#define AO_RTC_ALT_CLK_CNTL0 0x94
#define AO_RTC_ALT_CLK_CNTL1 0x98
-struct aoclk_gate_regmap {
- struct clk_hw hw;
- unsigned bit_idx;
- struct regmap *regmap;
- spinlock_t *lock;
-};
-
-#define to_aoclk_gate_regmap(_hw) \
- container_of(_hw, struct aoclk_gate_regmap, hw)
-
extern const struct clk_ops meson_aoclk_gate_regmap_ops;
struct aoclk_cec_32k {
struct clk_hw hw;
struct regmap *regmap;
- spinlock_t *lock;
};
#define to_aoclk_cec_32k(_hw) container_of(_hw, struct aoclk_cec_32k, hw)
diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c
index af24455af5b4..b1e4d9557610 100644
--- a/drivers/clk/meson/gxbb.c
+++ b/drivers/clk/meson/gxbb.c
@@ -19,108 +19,19 @@
#include <linux/clk.h>
#include <linux/clk-provider.h>
+#include <linux/init.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
+#include <linux/mfd/syscon.h>
#include <linux/platform_device.h>
-#include <linux/init.h>
+#include <linux/regmap.h>
#include "clkc.h"
#include "gxbb.h"
+#include "clk-regmap.h"
static DEFINE_SPINLOCK(meson_clk_lock);
-static const struct pll_rate_table sys_pll_rate_table[] = {
- PLL_RATE(24000000, 56, 1, 2),
- PLL_RATE(48000000, 64, 1, 2),
- PLL_RATE(72000000, 72, 1, 2),
- PLL_RATE(96000000, 64, 1, 2),
- PLL_RATE(120000000, 80, 1, 2),
- PLL_RATE(144000000, 96, 1, 2),
- PLL_RATE(168000000, 56, 1, 1),
- PLL_RATE(192000000, 64, 1, 1),
- PLL_RATE(216000000, 72, 1, 1),
- PLL_RATE(240000000, 80, 1, 1),
- PLL_RATE(264000000, 88, 1, 1),
- PLL_RATE(288000000, 96, 1, 1),
- PLL_RATE(312000000, 52, 1, 2),
- PLL_RATE(336000000, 56, 1, 2),
- PLL_RATE(360000000, 60, 1, 2),
- PLL_RATE(384000000, 64, 1, 2),
- PLL_RATE(408000000, 68, 1, 2),
- PLL_RATE(432000000, 72, 1, 2),
- PLL_RATE(456000000, 76, 1, 2),
- PLL_RATE(480000000, 80, 1, 2),
- PLL_RATE(504000000, 84, 1, 2),
- PLL_RATE(528000000, 88, 1, 2),
- PLL_RATE(552000000, 92, 1, 2),
- PLL_RATE(576000000, 96, 1, 2),
- PLL_RATE(600000000, 50, 1, 1),
- PLL_RATE(624000000, 52, 1, 1),
- PLL_RATE(648000000, 54, 1, 1),
- PLL_RATE(672000000, 56, 1, 1),
- PLL_RATE(696000000, 58, 1, 1),
- PLL_RATE(720000000, 60, 1, 1),
- PLL_RATE(744000000, 62, 1, 1),
- PLL_RATE(768000000, 64, 1, 1),
- PLL_RATE(792000000, 66, 1, 1),
- PLL_RATE(816000000, 68, 1, 1),
- PLL_RATE(840000000, 70, 1, 1),
- PLL_RATE(864000000, 72, 1, 1),
- PLL_RATE(888000000, 74, 1, 1),
- PLL_RATE(912000000, 76, 1, 1),
- PLL_RATE(936000000, 78, 1, 1),
- PLL_RATE(960000000, 80, 1, 1),
- PLL_RATE(984000000, 82, 1, 1),
- PLL_RATE(1008000000, 84, 1, 1),
- PLL_RATE(1032000000, 86, 1, 1),
- PLL_RATE(1056000000, 88, 1, 1),
- PLL_RATE(1080000000, 90, 1, 1),
- PLL_RATE(1104000000, 92, 1, 1),
- PLL_RATE(1128000000, 94, 1, 1),
- PLL_RATE(1152000000, 96, 1, 1),
- PLL_RATE(1176000000, 98, 1, 1),
- PLL_RATE(1200000000, 50, 1, 0),
- PLL_RATE(1224000000, 51, 1, 0),
- PLL_RATE(1248000000, 52, 1, 0),
- PLL_RATE(1272000000, 53, 1, 0),
- PLL_RATE(1296000000, 54, 1, 0),
- PLL_RATE(1320000000, 55, 1, 0),
- PLL_RATE(1344000000, 56, 1, 0),
- PLL_RATE(1368000000, 57, 1, 0),
- PLL_RATE(1392000000, 58, 1, 0),
- PLL_RATE(1416000000, 59, 1, 0),
- PLL_RATE(1440000000, 60, 1, 0),
- PLL_RATE(1464000000, 61, 1, 0),
- PLL_RATE(1488000000, 62, 1, 0),
- PLL_RATE(1512000000, 63, 1, 0),
- PLL_RATE(1536000000, 64, 1, 0),
- PLL_RATE(1560000000, 65, 1, 0),
- PLL_RATE(1584000000, 66, 1, 0),
- PLL_RATE(1608000000, 67, 1, 0),
- PLL_RATE(1632000000, 68, 1, 0),
- PLL_RATE(1656000000, 68, 1, 0),
- PLL_RATE(1680000000, 68, 1, 0),
- PLL_RATE(1704000000, 68, 1, 0),
- PLL_RATE(1728000000, 69, 1, 0),
- PLL_RATE(1752000000, 69, 1, 0),
- PLL_RATE(1776000000, 69, 1, 0),
- PLL_RATE(1800000000, 69, 1, 0),
- PLL_RATE(1824000000, 70, 1, 0),
- PLL_RATE(1848000000, 70, 1, 0),
- PLL_RATE(1872000000, 70, 1, 0),
- PLL_RATE(1896000000, 70, 1, 0),
- PLL_RATE(1920000000, 71, 1, 0),
- PLL_RATE(1944000000, 71, 1, 0),
- PLL_RATE(1968000000, 71, 1, 0),
- PLL_RATE(1992000000, 71, 1, 0),
- PLL_RATE(2016000000, 72, 1, 0),
- PLL_RATE(2040000000, 72, 1, 0),
- PLL_RATE(2064000000, 72, 1, 0),
- PLL_RATE(2088000000, 72, 1, 0),
- PLL_RATE(2112000000, 73, 1, 0),
- { /* sentinel */ },
-};
-
static const struct pll_rate_table gxbb_gp0_pll_rate_table[] = {
PLL_RATE(96000000, 32, 1, 3),
PLL_RATE(99000000, 33, 1, 3),
@@ -278,23 +189,39 @@ static const struct pll_rate_table gxl_gp0_pll_rate_table[] = {
{ /* sentinel */ },
};
-static struct meson_clk_pll gxbb_fixed_pll = {
- .m = {
- .reg_off = HHI_MPLL_CNTL,
- .shift = 0,
- .width = 9,
+static struct clk_regmap gxbb_fixed_pll = {
+ .data = &(struct meson_clk_pll_data){
+ .m = {
+ .reg_off = HHI_MPLL_CNTL,
+ .shift = 0,
+ .width = 9,
+ },
+ .n = {
+ .reg_off = HHI_MPLL_CNTL,
+ .shift = 9,
+ .width = 5,
+ },
+ .od = {
+ .reg_off = HHI_MPLL_CNTL,
+ .shift = 16,
+ .width = 2,
+ },
+ .frac = {
+ .reg_off = HHI_MPLL_CNTL2,
+ .shift = 0,
+ .width = 12,
+ },
+ .l = {
+ .reg_off = HHI_MPLL_CNTL,
+ .shift = 31,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = HHI_MPLL_CNTL,
+ .shift = 29,
+ .width = 1,
+ },
},
- .n = {
- .reg_off = HHI_MPLL_CNTL,
- .shift = 9,
- .width = 5,
- },
- .od = {
- .reg_off = HHI_MPLL_CNTL,
- .shift = 16,
- .width = 2,
- },
- .lock = &meson_clk_lock,
.hw.init = &(struct clk_init_data){
.name = "fixed_pll",
.ops = &meson_clk_pll_ro_ops,
@@ -304,33 +231,118 @@ static struct meson_clk_pll gxbb_fixed_pll = {
},
};
-static struct meson_clk_pll gxbb_hdmi_pll = {
- .m = {
- .reg_off = HHI_HDMI_PLL_CNTL,
- .shift = 0,
- .width = 9,
- },
- .n = {
- .reg_off = HHI_HDMI_PLL_CNTL,
- .shift = 9,
- .width = 5,
+static struct clk_fixed_factor gxbb_hdmi_pll_pre_mult = {
+ .mult = 2,
+ .div = 1,
+ .hw.init = &(struct clk_init_data){
+ .name = "hdmi_pll_pre_mult",
+ .ops = &clk_fixed_factor_ops,
+ .parent_names = (const char *[]){ "xtal" },
+ .num_parents = 1,
},
- .frac = {
- .reg_off = HHI_HDMI_PLL_CNTL2,
- .shift = 0,
- .width = 12,
+};
+
+static struct clk_regmap gxbb_hdmi_pll = {
+ .data = &(struct meson_clk_pll_data){
+ .m = {
+ .reg_off = HHI_HDMI_PLL_CNTL,
+ .shift = 0,
+ .width = 9,
+ },
+ .n = {
+ .reg_off = HHI_HDMI_PLL_CNTL,
+ .shift = 9,
+ .width = 5,
+ },
+ .frac = {
+ .reg_off = HHI_HDMI_PLL_CNTL2,
+ .shift = 0,
+ .width = 12,
+ },
+ .od = {
+ .reg_off = HHI_HDMI_PLL_CNTL2,
+ .shift = 16,
+ .width = 2,
+ },
+ .od2 = {
+ .reg_off = HHI_HDMI_PLL_CNTL2,
+ .shift = 22,
+ .width = 2,
+ },
+ .od3 = {
+ .reg_off = HHI_HDMI_PLL_CNTL2,
+ .shift = 18,
+ .width = 2,
+ },
+ .l = {
+ .reg_off = HHI_HDMI_PLL_CNTL,
+ .shift = 31,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = HHI_HDMI_PLL_CNTL,
+ .shift = 28,
+ .width = 1,
+ },
},
- .od = {
- .reg_off = HHI_HDMI_PLL_CNTL2,
- .shift = 16,
- .width = 2,
+ .hw.init = &(struct clk_init_data){
+ .name = "hdmi_pll",
+ .ops = &meson_clk_pll_ro_ops,
+ .parent_names = (const char *[]){ "hdmi_pll_pre_mult" },
+ .num_parents = 1,
+ .flags = CLK_GET_RATE_NOCACHE,
},
- .od2 = {
- .reg_off = HHI_HDMI_PLL_CNTL2,
- .shift = 22,
- .width = 2,
+};
+
+static struct clk_regmap gxl_hdmi_pll = {
+ .data = &(struct meson_clk_pll_data){
+ .m = {
+ .reg_off = HHI_HDMI_PLL_CNTL,
+ .shift = 0,
+ .width = 9,
+ },
+ .n = {
+ .reg_off = HHI_HDMI_PLL_CNTL,
+ .shift = 9,
+ .width = 5,
+ },
+ .frac = {
+ /*
+ * On gxl, there is a register shift due to
+ * HHI_HDMI_PLL_CNTL1 which does not exist on gxbb,
+ * so we compute the register offset based on the PLL
+ * base to get it right
+ */
+ .reg_off = HHI_HDMI_PLL_CNTL + 4,
+ .shift = 0,
+ .width = 12,
+ },
+ .od = {
+ .reg_off = HHI_HDMI_PLL_CNTL + 8,
+ .shift = 21,
+ .width = 2,
+ },
+ .od2 = {
+ .reg_off = HHI_HDMI_PLL_CNTL + 8,
+ .shift = 23,
+ .width = 2,
+ },
+ .od3 = {
+ .reg_off = HHI_HDMI_PLL_CNTL + 8,
+ .shift = 19,
+ .width = 2,
+ },
+ .l = {
+ .reg_off = HHI_HDMI_PLL_CNTL,
+ .shift = 31,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = HHI_HDMI_PLL_CNTL,
+ .shift = 29,
+ .width = 1,
+ },
},
- .lock = &meson_clk_lock,
.hw.init = &(struct clk_init_data){
.name = "hdmi_pll",
.ops = &meson_clk_pll_ro_ops,
@@ -340,25 +352,34 @@ static struct meson_clk_pll gxbb_hdmi_pll = {
},
};
-static struct meson_clk_pll gxbb_sys_pll = {
- .m = {
- .reg_off = HHI_SYS_PLL_CNTL,
- .shift = 0,
- .width = 9,
- },
- .n = {
- .reg_off = HHI_SYS_PLL_CNTL,
- .shift = 9,
- .width = 5,
+static struct clk_regmap gxbb_sys_pll = {
+ .data = &(struct meson_clk_pll_data){
+ .m = {
+ .reg_off = HHI_SYS_PLL_CNTL,
+ .shift = 0,
+ .width = 9,
+ },
+ .n = {
+ .reg_off = HHI_SYS_PLL_CNTL,
+ .shift = 9,
+ .width = 5,
+ },
+ .od = {
+ .reg_off = HHI_SYS_PLL_CNTL,
+ .shift = 10,
+ .width = 2,
+ },
+ .l = {
+ .reg_off = HHI_SYS_PLL_CNTL,
+ .shift = 31,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = HHI_SYS_PLL_CNTL,
+ .shift = 29,
+ .width = 1,
+ },
},
- .od = {
- .reg_off = HHI_SYS_PLL_CNTL,
- .shift = 10,
- .width = 2,
- },
- .rate_table = sys_pll_rate_table,
- .rate_count = ARRAY_SIZE(sys_pll_rate_table),
- .lock = &meson_clk_lock,
.hw.init = &(struct clk_init_data){
.name = "sys_pll",
.ops = &meson_clk_pll_ro_ops,
@@ -368,38 +389,44 @@ static struct meson_clk_pll gxbb_sys_pll = {
},
};
-struct pll_params_table gxbb_gp0_params_table[] = {
- PLL_PARAM(HHI_GP0_PLL_CNTL, 0x6a000228),
- PLL_PARAM(HHI_GP0_PLL_CNTL2, 0x69c80000),
- PLL_PARAM(HHI_GP0_PLL_CNTL3, 0x0a5590c4),
- PLL_PARAM(HHI_GP0_PLL_CNTL4, 0x0000500d),
-};
-
-static struct meson_clk_pll gxbb_gp0_pll = {
- .m = {
- .reg_off = HHI_GP0_PLL_CNTL,
- .shift = 0,
- .width = 9,
- },
- .n = {
- .reg_off = HHI_GP0_PLL_CNTL,
- .shift = 9,
- .width = 5,
- },
- .od = {
- .reg_off = HHI_GP0_PLL_CNTL,
- .shift = 16,
- .width = 2,
- },
- .params = {
- .params_table = gxbb_gp0_params_table,
- .params_count = ARRAY_SIZE(gxbb_gp0_params_table),
- .no_init_reset = true,
- .clear_reset_for_lock = true,
+static const struct reg_sequence gxbb_gp0_init_regs[] = {
+ { .reg = HHI_GP0_PLL_CNTL2, .def = 0x69c80000 },
+ { .reg = HHI_GP0_PLL_CNTL3, .def = 0x0a5590c4 },
+ { .reg = HHI_GP0_PLL_CNTL4, .def = 0x0000500d },
+ { .reg = HHI_GP0_PLL_CNTL, .def = 0x4a000228 },
+};
+
+static struct clk_regmap gxbb_gp0_pll = {
+ .data = &(struct meson_clk_pll_data){
+ .m = {
+ .reg_off = HHI_GP0_PLL_CNTL,
+ .shift = 0,
+ .width = 9,
+ },
+ .n = {
+ .reg_off = HHI_GP0_PLL_CNTL,
+ .shift = 9,
+ .width = 5,
+ },
+ .od = {
+ .reg_off = HHI_GP0_PLL_CNTL,
+ .shift = 16,
+ .width = 2,
+ },
+ .l = {
+ .reg_off = HHI_GP0_PLL_CNTL,
+ .shift = 31,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = HHI_GP0_PLL_CNTL,
+ .shift = 29,
+ .width = 1,
+ },
+ .table = gxbb_gp0_pll_rate_table,
+ .init_regs = gxbb_gp0_init_regs,
+ .init_count = ARRAY_SIZE(gxbb_gp0_init_regs),
},
- .rate_table = gxbb_gp0_pll_rate_table,
- .rate_count = ARRAY_SIZE(gxbb_gp0_pll_rate_table),
- .lock = &meson_clk_lock,
.hw.init = &(struct clk_init_data){
.name = "gp0_pll",
.ops = &meson_clk_pll_ops,
@@ -409,40 +436,51 @@ static struct meson_clk_pll gxbb_gp0_pll = {
},
};
-struct pll_params_table gxl_gp0_params_table[] = {
- PLL_PARAM(HHI_GP0_PLL_CNTL, 0x40010250),
- PLL_PARAM(HHI_GP0_PLL_CNTL1, 0xc084a000),
- PLL_PARAM(HHI_GP0_PLL_CNTL2, 0xb75020be),
- PLL_PARAM(HHI_GP0_PLL_CNTL3, 0x0a59a288),
- PLL_PARAM(HHI_GP0_PLL_CNTL4, 0xc000004d),
- PLL_PARAM(HHI_GP0_PLL_CNTL5, 0x00078000),
-};
-
-static struct meson_clk_pll gxl_gp0_pll = {
- .m = {
- .reg_off = HHI_GP0_PLL_CNTL,
- .shift = 0,
- .width = 9,
- },
- .n = {
- .reg_off = HHI_GP0_PLL_CNTL,
- .shift = 9,
- .width = 5,
- },
- .od = {
- .reg_off = HHI_GP0_PLL_CNTL,
- .shift = 16,
- .width = 2,
+static const struct reg_sequence gxl_gp0_init_regs[] = {
+ { .reg = HHI_GP0_PLL_CNTL1, .def = 0xc084b000 },
+ { .reg = HHI_GP0_PLL_CNTL2, .def = 0xb75020be },
+ { .reg = HHI_GP0_PLL_CNTL3, .def = 0x0a59a288 },
+ { .reg = HHI_GP0_PLL_CNTL4, .def = 0xc000004d },
+ { .reg = HHI_GP0_PLL_CNTL5, .def = 0x00078000 },
+ { .reg = HHI_GP0_PLL_CNTL, .def = 0x40010250 },
+};
+
+static struct clk_regmap gxl_gp0_pll = {
+ .data = &(struct meson_clk_pll_data){
+ .m = {
+ .reg_off = HHI_GP0_PLL_CNTL,
+ .shift = 0,
+ .width = 9,
+ },
+ .n = {
+ .reg_off = HHI_GP0_PLL_CNTL,
+ .shift = 9,
+ .width = 5,
+ },
+ .od = {
+ .reg_off = HHI_GP0_PLL_CNTL,
+ .shift = 16,
+ .width = 2,
+ },
+ .frac = {
+ .reg_off = HHI_GP0_PLL_CNTL1,
+ .shift = 0,
+ .width = 10,
+ },
+ .l = {
+ .reg_off = HHI_GP0_PLL_CNTL,
+ .shift = 31,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = HHI_GP0_PLL_CNTL,
+ .shift = 29,
+ .width = 1,
+ },
+ .table = gxl_gp0_pll_rate_table,
+ .init_regs = gxl_gp0_init_regs,
+ .init_count = ARRAY_SIZE(gxl_gp0_init_regs),
},
- .params = {
- .params_table = gxl_gp0_params_table,
- .params_count = ARRAY_SIZE(gxl_gp0_params_table),
- .no_init_reset = true,
- .reset_lock_loop = true,
- },
- .rate_table = gxl_gp0_pll_rate_table,
- .rate_count = ARRAY_SIZE(gxl_gp0_pll_rate_table),
- .lock = &meson_clk_lock,
.hw.init = &(struct clk_init_data){
.name = "gp0_pll",
.ops = &meson_clk_pll_ops,
@@ -452,161 +490,267 @@ static struct meson_clk_pll gxl_gp0_pll = {
},
};
-static struct clk_fixed_factor gxbb_fclk_div2 = {
+static struct clk_fixed_factor gxbb_fclk_div2_div = {
.mult = 1,
.div = 2,
.hw.init = &(struct clk_init_data){
- .name = "fclk_div2",
+ .name = "fclk_div2_div",
.ops = &clk_fixed_factor_ops,
.parent_names = (const char *[]){ "fixed_pll" },
.num_parents = 1,
},
};
-static struct clk_fixed_factor gxbb_fclk_div3 = {
+static struct clk_regmap gxbb_fclk_div2 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPLL_CNTL6,
+ .bit_idx = 27,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div2",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "fclk_div2_div" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor gxbb_fclk_div3_div = {
.mult = 1,
.div = 3,
.hw.init = &(struct clk_init_data){
- .name = "fclk_div3",
+ .name = "fclk_div3_div",
.ops = &clk_fixed_factor_ops,
.parent_names = (const char *[]){ "fixed_pll" },
.num_parents = 1,
},
};
-static struct clk_fixed_factor gxbb_fclk_div4 = {
+static struct clk_regmap gxbb_fclk_div3 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPLL_CNTL6,
+ .bit_idx = 28,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div3",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "fclk_div3_div" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor gxbb_fclk_div4_div = {
.mult = 1,
.div = 4,
.hw.init = &(struct clk_init_data){
- .name = "fclk_div4",
+ .name = "fclk_div4_div",
.ops = &clk_fixed_factor_ops,
.parent_names = (const char *[]){ "fixed_pll" },
.num_parents = 1,
},
};
-static struct clk_fixed_factor gxbb_fclk_div5 = {
+static struct clk_regmap gxbb_fclk_div4 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPLL_CNTL6,
+ .bit_idx = 29,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div4",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "fclk_div4_div" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor gxbb_fclk_div5_div = {
.mult = 1,
.div = 5,
.hw.init = &(struct clk_init_data){
- .name = "fclk_div5",
+ .name = "fclk_div5_div",
.ops = &clk_fixed_factor_ops,
.parent_names = (const char *[]){ "fixed_pll" },
.num_parents = 1,
},
};
-static struct clk_fixed_factor gxbb_fclk_div7 = {
+static struct clk_regmap gxbb_fclk_div5 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPLL_CNTL6,
+ .bit_idx = 30,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div5",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "fclk_div5_div" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor gxbb_fclk_div7_div = {
.mult = 1,
.div = 7,
.hw.init = &(struct clk_init_data){
- .name = "fclk_div7",
+ .name = "fclk_div7_div",
.ops = &clk_fixed_factor_ops,
.parent_names = (const char *[]){ "fixed_pll" },
.num_parents = 1,
},
};
-static struct meson_clk_mpll gxbb_mpll0 = {
- .sdm = {
- .reg_off = HHI_MPLL_CNTL7,
- .shift = 0,
- .width = 14,
+static struct clk_regmap gxbb_fclk_div7 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPLL_CNTL6,
+ .bit_idx = 31,
},
- .sdm_en = {
- .reg_off = HHI_MPLL_CNTL7,
- .shift = 15,
- .width = 1,
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div7",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "fclk_div7_div" },
+ .num_parents = 1,
},
- .n2 = {
- .reg_off = HHI_MPLL_CNTL7,
- .shift = 16,
- .width = 9,
+};
+
+static struct clk_regmap gxbb_mpll_prediv = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_MPLL_CNTL5,
+ .shift = 12,
+ .width = 1,
},
- .en = {
- .reg_off = HHI_MPLL_CNTL7,
- .shift = 14,
- .width = 1,
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll_prediv",
+ .ops = &clk_regmap_divider_ro_ops,
+ .parent_names = (const char *[]){ "fixed_pll" },
+ .num_parents = 1,
},
- .ssen = {
- .reg_off = HHI_MPLL_CNTL,
- .shift = 25,
- .width = 1,
+};
+
+static struct clk_regmap gxbb_mpll0_div = {
+ .data = &(struct meson_clk_mpll_data){
+ .sdm = {
+ .reg_off = HHI_MPLL_CNTL7,
+ .shift = 0,
+ .width = 14,
+ },
+ .sdm_en = {
+ .reg_off = HHI_MPLL_CNTL7,
+ .shift = 15,
+ .width = 1,
+ },
+ .n2 = {
+ .reg_off = HHI_MPLL_CNTL7,
+ .shift = 16,
+ .width = 9,
+ },
+ .ssen = {
+ .reg_off = HHI_MPLL_CNTL,
+ .shift = 25,
+ .width = 1,
+ },
+ .lock = &meson_clk_lock,
},
- .lock = &meson_clk_lock,
.hw.init = &(struct clk_init_data){
- .name = "mpll0",
+ .name = "mpll0_div",
.ops = &meson_clk_mpll_ops,
- .parent_names = (const char *[]){ "fixed_pll" },
+ .parent_names = (const char *[]){ "mpll_prediv" },
.num_parents = 1,
},
};
-static struct meson_clk_mpll gxbb_mpll1 = {
- .sdm = {
- .reg_off = HHI_MPLL_CNTL8,
- .shift = 0,
- .width = 14,
- },
- .sdm_en = {
- .reg_off = HHI_MPLL_CNTL8,
- .shift = 15,
- .width = 1,
+static struct clk_regmap gxbb_mpll0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPLL_CNTL7,
+ .bit_idx = 14,
},
- .n2 = {
- .reg_off = HHI_MPLL_CNTL8,
- .shift = 16,
- .width = 9,
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll0",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "mpll0_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
},
- .en = {
- .reg_off = HHI_MPLL_CNTL8,
- .shift = 14,
- .width = 1,
+};
+
+static struct clk_regmap gxbb_mpll1_div = {
+ .data = &(struct meson_clk_mpll_data){
+ .sdm = {
+ .reg_off = HHI_MPLL_CNTL8,
+ .shift = 0,
+ .width = 14,
+ },
+ .sdm_en = {
+ .reg_off = HHI_MPLL_CNTL8,
+ .shift = 15,
+ .width = 1,
+ },
+ .n2 = {
+ .reg_off = HHI_MPLL_CNTL8,
+ .shift = 16,
+ .width = 9,
+ },
+ .lock = &meson_clk_lock,
},
- .lock = &meson_clk_lock,
.hw.init = &(struct clk_init_data){
- .name = "mpll1",
+ .name = "mpll1_div",
.ops = &meson_clk_mpll_ops,
- .parent_names = (const char *[]){ "fixed_pll" },
+ .parent_names = (const char *[]){ "mpll_prediv" },
.num_parents = 1,
},
};
-static struct meson_clk_mpll gxbb_mpll2 = {
- .sdm = {
- .reg_off = HHI_MPLL_CNTL9,
- .shift = 0,
- .width = 14,
- },
- .sdm_en = {
- .reg_off = HHI_MPLL_CNTL9,
- .shift = 15,
- .width = 1,
+static struct clk_regmap gxbb_mpll1 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPLL_CNTL8,
+ .bit_idx = 14,
},
- .n2 = {
- .reg_off = HHI_MPLL_CNTL9,
- .shift = 16,
- .width = 9,
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll1",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "mpll1_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
},
- .en = {
- .reg_off = HHI_MPLL_CNTL9,
- .shift = 14,
- .width = 1,
+};
+
+static struct clk_regmap gxbb_mpll2_div = {
+ .data = &(struct meson_clk_mpll_data){
+ .sdm = {
+ .reg_off = HHI_MPLL_CNTL9,
+ .shift = 0,
+ .width = 14,
+ },
+ .sdm_en = {
+ .reg_off = HHI_MPLL_CNTL9,
+ .shift = 15,
+ .width = 1,
+ },
+ .n2 = {
+ .reg_off = HHI_MPLL_CNTL9,
+ .shift = 16,
+ .width = 9,
+ },
+ .lock = &meson_clk_lock,
},
- .lock = &meson_clk_lock,
.hw.init = &(struct clk_init_data){
- .name = "mpll2",
+ .name = "mpll2_div",
.ops = &meson_clk_mpll_ops,
- .parent_names = (const char *[]){ "fixed_pll" },
+ .parent_names = (const char *[]){ "mpll_prediv" },
.num_parents = 1,
},
};
-/*
- * FIXME The legacy composite clocks (e.g. clk81) are both PLL post-dividers
- * and should be modeled with their respective PLLs via the forthcoming
- * coordinated clock rates feature
- */
+static struct clk_regmap gxbb_mpll2 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPLL_CNTL9,
+ .bit_idx = 14,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll2",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "mpll2_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
static u32 mux_table_clk81[] = { 0, 2, 3, 4, 5, 6, 7 };
static const char * const clk81_parent_names[] = {
@@ -614,16 +758,16 @@ static const char * const clk81_parent_names[] = {
"fclk_div3", "fclk_div5"
};
-static struct clk_mux gxbb_mpeg_clk_sel = {
- .reg = (void *)HHI_MPEG_CLK_CNTL,
- .mask = 0x7,
- .shift = 12,
- .flags = CLK_MUX_READ_ONLY,
- .table = mux_table_clk81,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_mpeg_clk_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_MPEG_CLK_CNTL,
+ .mask = 0x7,
+ .shift = 12,
+ .table = mux_table_clk81,
+ },
.hw.init = &(struct clk_init_data){
.name = "mpeg_clk_sel",
- .ops = &clk_mux_ro_ops,
+ .ops = &clk_regmap_mux_ro_ops,
/*
* bits 14:12 selects from 8 possible parents:
* xtal, 1'b0 (wtf), fclk_div7, mpll_clkout1, mpll_clkout2,
@@ -631,72 +775,75 @@ static struct clk_mux gxbb_mpeg_clk_sel = {
*/
.parent_names = clk81_parent_names,
.num_parents = ARRAY_SIZE(clk81_parent_names),
- .flags = (CLK_SET_RATE_NO_REPARENT | CLK_IGNORE_UNUSED),
},
};
-static struct clk_divider gxbb_mpeg_clk_div = {
- .reg = (void *)HHI_MPEG_CLK_CNTL,
- .shift = 0,
- .width = 7,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_mpeg_clk_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_MPEG_CLK_CNTL,
+ .shift = 0,
+ .width = 7,
+ },
.hw.init = &(struct clk_init_data){
.name = "mpeg_clk_div",
- .ops = &clk_divider_ops,
+ .ops = &clk_regmap_divider_ro_ops,
.parent_names = (const char *[]){ "mpeg_clk_sel" },
.num_parents = 1,
- .flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED),
},
};
-/* the mother of dragons^W gates */
-static struct clk_gate gxbb_clk81 = {
- .reg = (void *)HHI_MPEG_CLK_CNTL,
- .bit_idx = 7,
- .lock = &meson_clk_lock,
+/* the mother of dragons gates */
+static struct clk_regmap gxbb_clk81 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPEG_CLK_CNTL,
+ .bit_idx = 7,
+ },
.hw.init = &(struct clk_init_data){
.name = "clk81",
- .ops = &clk_gate_ops,
+ .ops = &clk_regmap_gate_ops,
.parent_names = (const char *[]){ "mpeg_clk_div" },
.num_parents = 1,
- .flags = (CLK_SET_RATE_PARENT | CLK_IS_CRITICAL),
+ .flags = CLK_IS_CRITICAL,
},
};
-static struct clk_mux gxbb_sar_adc_clk_sel = {
- .reg = (void *)HHI_SAR_CLK_CNTL,
- .mask = 0x3,
- .shift = 9,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_sar_adc_clk_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_SAR_CLK_CNTL,
+ .mask = 0x3,
+ .shift = 9,
+ },
.hw.init = &(struct clk_init_data){
.name = "sar_adc_clk_sel",
- .ops = &clk_mux_ops,
+ .ops = &clk_regmap_mux_ops,
/* NOTE: The datasheet doesn't list the parents for bit 10 */
.parent_names = (const char *[]){ "xtal", "clk81", },
.num_parents = 2,
},
};
-static struct clk_divider gxbb_sar_adc_clk_div = {
- .reg = (void *)HHI_SAR_CLK_CNTL,
- .shift = 0,
- .width = 8,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_sar_adc_clk_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_SAR_CLK_CNTL,
+ .shift = 0,
+ .width = 8,
+ },
.hw.init = &(struct clk_init_data){
.name = "sar_adc_clk_div",
- .ops = &clk_divider_ops,
+ .ops = &clk_regmap_divider_ops,
.parent_names = (const char *[]){ "sar_adc_clk_sel" },
.num_parents = 1,
},
};
-static struct clk_gate gxbb_sar_adc_clk = {
- .reg = (void *)HHI_SAR_CLK_CNTL,
- .bit_idx = 8,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_sar_adc_clk = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_SAR_CLK_CNTL,
+ .bit_idx = 8,
+ },
.hw.init = &(struct clk_init_data){
.name = "sar_adc_clk",
- .ops = &clk_gate_ops,
+ .ops = &clk_regmap_gate_ops,
.parent_names = (const char *[]){ "sar_adc_clk_div" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
@@ -708,21 +855,20 @@ static struct clk_gate gxbb_sar_adc_clk = {
* muxed by a glitch-free switch.
*/
-static u32 mux_table_mali_0_1[] = {0, 1, 2, 3, 4, 5, 6, 7};
static const char * const gxbb_mali_0_1_parent_names[] = {
"xtal", "gp0_pll", "mpll2", "mpll1", "fclk_div7",
"fclk_div4", "fclk_div3", "fclk_div5"
};
-static struct clk_mux gxbb_mali_0_sel = {
- .reg = (void *)HHI_MALI_CLK_CNTL,
- .mask = 0x7,
- .shift = 9,
- .table = mux_table_mali_0_1,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_mali_0_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_MALI_CLK_CNTL,
+ .mask = 0x7,
+ .shift = 9,
+ },
.hw.init = &(struct clk_init_data){
.name = "mali_0_sel",
- .ops = &clk_mux_ops,
+ .ops = &clk_regmap_mux_ops,
/*
* bits 10:9 selects from 8 possible parents:
* xtal, gp0_pll, mpll2, mpll1, fclk_div7,
@@ -734,42 +880,44 @@ static struct clk_mux gxbb_mali_0_sel = {
},
};
-static struct clk_divider gxbb_mali_0_div = {
- .reg = (void *)HHI_MALI_CLK_CNTL,
- .shift = 0,
- .width = 7,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_mali_0_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_MALI_CLK_CNTL,
+ .shift = 0,
+ .width = 7,
+ },
.hw.init = &(struct clk_init_data){
.name = "mali_0_div",
- .ops = &clk_divider_ops,
+ .ops = &clk_regmap_divider_ops,
.parent_names = (const char *[]){ "mali_0_sel" },
.num_parents = 1,
.flags = CLK_SET_RATE_NO_REPARENT,
},
};
-static struct clk_gate gxbb_mali_0 = {
- .reg = (void *)HHI_MALI_CLK_CNTL,
- .bit_idx = 8,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_mali_0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MALI_CLK_CNTL,
+ .bit_idx = 8,
+ },
.hw.init = &(struct clk_init_data){
.name = "mali_0",
- .ops = &clk_gate_ops,
+ .ops = &clk_regmap_gate_ops,
.parent_names = (const char *[]){ "mali_0_div" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_mux gxbb_mali_1_sel = {
- .reg = (void *)HHI_MALI_CLK_CNTL,
- .mask = 0x7,
- .shift = 25,
- .table = mux_table_mali_0_1,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_mali_1_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_MALI_CLK_CNTL,
+ .mask = 0x7,
+ .shift = 25,
+ },
.hw.init = &(struct clk_init_data){
.name = "mali_1_sel",
- .ops = &clk_mux_ops,
+ .ops = &clk_regmap_mux_ops,
/*
* bits 10:9 selects from 8 possible parents:
* xtal, gp0_pll, mpll2, mpll1, fclk_div7,
@@ -781,77 +929,79 @@ static struct clk_mux gxbb_mali_1_sel = {
},
};
-static struct clk_divider gxbb_mali_1_div = {
- .reg = (void *)HHI_MALI_CLK_CNTL,
- .shift = 16,
- .width = 7,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_mali_1_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_MALI_CLK_CNTL,
+ .shift = 16,
+ .width = 7,
+ },
.hw.init = &(struct clk_init_data){
.name = "mali_1_div",
- .ops = &clk_divider_ops,
+ .ops = &clk_regmap_divider_ops,
.parent_names = (const char *[]){ "mali_1_sel" },
.num_parents = 1,
.flags = CLK_SET_RATE_NO_REPARENT,
},
};
-static struct clk_gate gxbb_mali_1 = {
- .reg = (void *)HHI_MALI_CLK_CNTL,
- .bit_idx = 24,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_mali_1 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MALI_CLK_CNTL,
+ .bit_idx = 24,
+ },
.hw.init = &(struct clk_init_data){
.name = "mali_1",
- .ops = &clk_gate_ops,
+ .ops = &clk_regmap_gate_ops,
.parent_names = (const char *[]){ "mali_1_div" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
-static u32 mux_table_mali[] = {0, 1};
static const char * const gxbb_mali_parent_names[] = {
"mali_0", "mali_1"
};
-static struct clk_mux gxbb_mali = {
- .reg = (void *)HHI_MALI_CLK_CNTL,
- .mask = 1,
- .shift = 31,
- .table = mux_table_mali,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_mali = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_MALI_CLK_CNTL,
+ .mask = 1,
+ .shift = 31,
+ },
.hw.init = &(struct clk_init_data){
.name = "mali",
- .ops = &clk_mux_ops,
+ .ops = &clk_regmap_mux_ops,
.parent_names = gxbb_mali_parent_names,
.num_parents = 2,
.flags = CLK_SET_RATE_NO_REPARENT,
},
};
-static struct clk_mux gxbb_cts_amclk_sel = {
- .reg = (void *) HHI_AUD_CLK_CNTL,
- .mask = 0x3,
- .shift = 9,
- /* Default parent unknown (register reset value: 0) */
- .table = (u32[]){ 1, 2, 3 },
- .lock = &meson_clk_lock,
- .hw.init = &(struct clk_init_data){
+static struct clk_regmap gxbb_cts_amclk_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_AUD_CLK_CNTL,
+ .mask = 0x3,
+ .shift = 9,
+ .table = (u32[]){ 1, 2, 3 },
+ },
+ .hw.init = &(struct clk_init_data){
.name = "cts_amclk_sel",
- .ops = &clk_mux_ops,
+ .ops = &clk_regmap_mux_ops,
.parent_names = (const char *[]){ "mpll0", "mpll1", "mpll2" },
.num_parents = 3,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct meson_clk_audio_divider gxbb_cts_amclk_div = {
- .div = {
- .reg_off = HHI_AUD_CLK_CNTL,
- .shift = 0,
- .width = 8,
+static struct clk_regmap gxbb_cts_amclk_div = {
+ .data = &(struct meson_clk_audio_div_data){
+ .div = {
+ .reg_off = HHI_AUD_CLK_CNTL,
+ .shift = 0,
+ .width = 8,
+ },
+ .flags = CLK_DIVIDER_ROUND_CLOSEST,
},
- .flags = CLK_DIVIDER_ROUND_CLOSEST,
- .lock = &meson_clk_lock,
.hw.init = &(struct clk_init_data){
.name = "cts_amclk_div",
.ops = &meson_clk_audio_divider_ops,
@@ -861,71 +1011,75 @@ static struct meson_clk_audio_divider gxbb_cts_amclk_div = {
},
};
-static struct clk_gate gxbb_cts_amclk = {
- .reg = (void *) HHI_AUD_CLK_CNTL,
- .bit_idx = 8,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_cts_amclk = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_AUD_CLK_CNTL,
+ .bit_idx = 8,
+ },
.hw.init = &(struct clk_init_data){
.name = "cts_amclk",
- .ops = &clk_gate_ops,
+ .ops = &clk_regmap_gate_ops,
.parent_names = (const char *[]){ "cts_amclk_div" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_mux gxbb_cts_mclk_i958_sel = {
- .reg = (void *)HHI_AUD_CLK_CNTL2,
- .mask = 0x3,
- .shift = 25,
- /* Default parent unknown (register reset value: 0) */
- .table = (u32[]){ 1, 2, 3 },
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_cts_mclk_i958_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_AUD_CLK_CNTL2,
+ .mask = 0x3,
+ .shift = 25,
+ .table = (u32[]){ 1, 2, 3 },
+ },
.hw.init = &(struct clk_init_data) {
.name = "cts_mclk_i958_sel",
- .ops = &clk_mux_ops,
+ .ops = &clk_regmap_mux_ops,
.parent_names = (const char *[]){ "mpll0", "mpll1", "mpll2" },
.num_parents = 3,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_divider gxbb_cts_mclk_i958_div = {
- .reg = (void *)HHI_AUD_CLK_CNTL2,
- .shift = 16,
- .width = 8,
- .lock = &meson_clk_lock,
- .flags = CLK_DIVIDER_ROUND_CLOSEST,
+static struct clk_regmap gxbb_cts_mclk_i958_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_AUD_CLK_CNTL2,
+ .shift = 16,
+ .width = 8,
+ .flags = CLK_DIVIDER_ROUND_CLOSEST,
+ },
.hw.init = &(struct clk_init_data) {
.name = "cts_mclk_i958_div",
- .ops = &clk_divider_ops,
+ .ops = &clk_regmap_divider_ops,
.parent_names = (const char *[]){ "cts_mclk_i958_sel" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_gate gxbb_cts_mclk_i958 = {
- .reg = (void *)HHI_AUD_CLK_CNTL2,
- .bit_idx = 24,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_cts_mclk_i958 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_AUD_CLK_CNTL2,
+ .bit_idx = 24,
+ },
.hw.init = &(struct clk_init_data){
.name = "cts_mclk_i958",
- .ops = &clk_gate_ops,
+ .ops = &clk_regmap_gate_ops,
.parent_names = (const char *[]){ "cts_mclk_i958_div" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_mux gxbb_cts_i958 = {
- .reg = (void *)HHI_AUD_CLK_CNTL2,
- .mask = 0x1,
- .shift = 27,
- .lock = &meson_clk_lock,
- .hw.init = &(struct clk_init_data){
+static struct clk_regmap gxbb_cts_i958 = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_AUD_CLK_CNTL2,
+ .mask = 0x1,
+ .shift = 27,
+ },
+ .hw.init = &(struct clk_init_data){
.name = "cts_i958",
- .ops = &clk_mux_ops,
+ .ops = &clk_regmap_mux_ops,
.parent_names = (const char *[]){ "cts_amclk", "cts_mclk_i958" },
.num_parents = 2,
/*
@@ -936,27 +1090,29 @@ static struct clk_mux gxbb_cts_i958 = {
},
};
-static struct clk_divider gxbb_32k_clk_div = {
- .reg = (void *)HHI_32K_CLK_CNTL,
- .shift = 0,
- .width = 14,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_32k_clk_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_32K_CLK_CNTL,
+ .shift = 0,
+ .width = 14,
+ },
.hw.init = &(struct clk_init_data){
.name = "32k_clk_div",
- .ops = &clk_divider_ops,
+ .ops = &clk_regmap_divider_ops,
.parent_names = (const char *[]){ "32k_clk_sel" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT | CLK_DIVIDER_ROUND_CLOSEST,
},
};
-static struct clk_gate gxbb_32k_clk = {
- .reg = (void *)HHI_32K_CLK_CNTL,
- .bit_idx = 15,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_32k_clk = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_32K_CLK_CNTL,
+ .bit_idx = 15,
+ },
.hw.init = &(struct clk_init_data){
.name = "32k_clk",
- .ops = &clk_gate_ops,
+ .ops = &clk_regmap_gate_ops,
.parent_names = (const char *[]){ "32k_clk_div" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
@@ -967,14 +1123,15 @@ static const char * const gxbb_32k_clk_parent_names[] = {
"xtal", "cts_slow_oscin", "fclk_div3", "fclk_div5"
};
-static struct clk_mux gxbb_32k_clk_sel = {
- .reg = (void *)HHI_32K_CLK_CNTL,
- .mask = 0x3,
- .shift = 16,
- .lock = &meson_clk_lock,
- .hw.init = &(struct clk_init_data){
+static struct clk_regmap gxbb_32k_clk_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_32K_CLK_CNTL,
+ .mask = 0x3,
+ .shift = 16,
+ },
+ .hw.init = &(struct clk_init_data){
.name = "32k_clk_sel",
- .ops = &clk_mux_ops,
+ .ops = &clk_regmap_mux_ops,
.parent_names = gxbb_32k_clk_parent_names,
.num_parents = 4,
.flags = CLK_SET_RATE_PARENT,
@@ -993,42 +1150,45 @@ static const char * const gxbb_sd_emmc_clk0_parent_names[] = {
};
/* SDIO clock */
-static struct clk_mux gxbb_sd_emmc_a_clk0_sel = {
- .reg = (void *)HHI_SD_EMMC_CLK_CNTL,
- .mask = 0x7,
- .shift = 9,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_sd_emmc_a_clk0_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_SD_EMMC_CLK_CNTL,
+ .mask = 0x7,
+ .shift = 9,
+ },
.hw.init = &(struct clk_init_data) {
.name = "sd_emmc_a_clk0_sel",
- .ops = &clk_mux_ops,
+ .ops = &clk_regmap_mux_ops,
.parent_names = gxbb_sd_emmc_clk0_parent_names,
.num_parents = ARRAY_SIZE(gxbb_sd_emmc_clk0_parent_names),
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_divider gxbb_sd_emmc_a_clk0_div = {
- .reg = (void *)HHI_SD_EMMC_CLK_CNTL,
- .shift = 0,
- .width = 7,
- .lock = &meson_clk_lock,
- .flags = CLK_DIVIDER_ROUND_CLOSEST,
+static struct clk_regmap gxbb_sd_emmc_a_clk0_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_SD_EMMC_CLK_CNTL,
+ .shift = 0,
+ .width = 7,
+ .flags = CLK_DIVIDER_ROUND_CLOSEST,
+ },
.hw.init = &(struct clk_init_data) {
.name = "sd_emmc_a_clk0_div",
- .ops = &clk_divider_ops,
+ .ops = &clk_regmap_divider_ops,
.parent_names = (const char *[]){ "sd_emmc_a_clk0_sel" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_gate gxbb_sd_emmc_a_clk0 = {
- .reg = (void *)HHI_SD_EMMC_CLK_CNTL,
- .bit_idx = 7,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_sd_emmc_a_clk0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_SD_EMMC_CLK_CNTL,
+ .bit_idx = 7,
+ },
.hw.init = &(struct clk_init_data){
.name = "sd_emmc_a_clk0",
- .ops = &clk_gate_ops,
+ .ops = &clk_regmap_gate_ops,
.parent_names = (const char *[]){ "sd_emmc_a_clk0_div" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
@@ -1036,42 +1196,45 @@ static struct clk_gate gxbb_sd_emmc_a_clk0 = {
};
/* SDcard clock */
-static struct clk_mux gxbb_sd_emmc_b_clk0_sel = {
- .reg = (void *)HHI_SD_EMMC_CLK_CNTL,
- .mask = 0x7,
- .shift = 25,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_sd_emmc_b_clk0_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_SD_EMMC_CLK_CNTL,
+ .mask = 0x7,
+ .shift = 25,
+ },
.hw.init = &(struct clk_init_data) {
.name = "sd_emmc_b_clk0_sel",
- .ops = &clk_mux_ops,
+ .ops = &clk_regmap_mux_ops,
.parent_names = gxbb_sd_emmc_clk0_parent_names,
.num_parents = ARRAY_SIZE(gxbb_sd_emmc_clk0_parent_names),
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_divider gxbb_sd_emmc_b_clk0_div = {
- .reg = (void *)HHI_SD_EMMC_CLK_CNTL,
- .shift = 16,
- .width = 7,
- .lock = &meson_clk_lock,
- .flags = CLK_DIVIDER_ROUND_CLOSEST,
+static struct clk_regmap gxbb_sd_emmc_b_clk0_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_SD_EMMC_CLK_CNTL,
+ .shift = 16,
+ .width = 7,
+ .flags = CLK_DIVIDER_ROUND_CLOSEST,
+ },
.hw.init = &(struct clk_init_data) {
.name = "sd_emmc_b_clk0_div",
- .ops = &clk_divider_ops,
+ .ops = &clk_regmap_divider_ops,
.parent_names = (const char *[]){ "sd_emmc_b_clk0_sel" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_gate gxbb_sd_emmc_b_clk0 = {
- .reg = (void *)HHI_SD_EMMC_CLK_CNTL,
- .bit_idx = 23,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_sd_emmc_b_clk0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_SD_EMMC_CLK_CNTL,
+ .bit_idx = 23,
+ },
.hw.init = &(struct clk_init_data){
.name = "sd_emmc_b_clk0",
- .ops = &clk_gate_ops,
+ .ops = &clk_regmap_gate_ops,
.parent_names = (const char *[]){ "sd_emmc_b_clk0_div" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
@@ -1079,42 +1242,45 @@ static struct clk_gate gxbb_sd_emmc_b_clk0 = {
};
/* EMMC/NAND clock */
-static struct clk_mux gxbb_sd_emmc_c_clk0_sel = {
- .reg = (void *)HHI_NAND_CLK_CNTL,
- .mask = 0x7,
- .shift = 9,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_sd_emmc_c_clk0_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_NAND_CLK_CNTL,
+ .mask = 0x7,
+ .shift = 9,
+ },
.hw.init = &(struct clk_init_data) {
.name = "sd_emmc_c_clk0_sel",
- .ops = &clk_mux_ops,
+ .ops = &clk_regmap_mux_ops,
.parent_names = gxbb_sd_emmc_clk0_parent_names,
.num_parents = ARRAY_SIZE(gxbb_sd_emmc_clk0_parent_names),
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_divider gxbb_sd_emmc_c_clk0_div = {
- .reg = (void *)HHI_NAND_CLK_CNTL,
- .shift = 0,
- .width = 7,
- .lock = &meson_clk_lock,
- .flags = CLK_DIVIDER_ROUND_CLOSEST,
+static struct clk_regmap gxbb_sd_emmc_c_clk0_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_NAND_CLK_CNTL,
+ .shift = 0,
+ .width = 7,
+ .flags = CLK_DIVIDER_ROUND_CLOSEST,
+ },
.hw.init = &(struct clk_init_data) {
.name = "sd_emmc_c_clk0_div",
- .ops = &clk_divider_ops,
+ .ops = &clk_regmap_divider_ops,
.parent_names = (const char *[]){ "sd_emmc_c_clk0_sel" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_gate gxbb_sd_emmc_c_clk0 = {
- .reg = (void *)HHI_NAND_CLK_CNTL,
- .bit_idx = 7,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_sd_emmc_c_clk0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_NAND_CLK_CNTL,
+ .bit_idx = 7,
+ },
.hw.init = &(struct clk_init_data){
.name = "sd_emmc_c_clk0",
- .ops = &clk_gate_ops,
+ .ops = &clk_regmap_gate_ops,
.parent_names = (const char *[]){ "sd_emmc_c_clk0_div" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
@@ -1123,20 +1289,19 @@ static struct clk_gate gxbb_sd_emmc_c_clk0 = {
/* VPU Clock */
-static u32 mux_table_vpu[] = {0, 1, 2, 3};
static const char * const gxbb_vpu_parent_names[] = {
"fclk_div4", "fclk_div3", "fclk_div5", "fclk_div7"
};
-static struct clk_mux gxbb_vpu_0_sel = {
- .reg = (void *)HHI_VPU_CLK_CNTL,
- .mask = 0x3,
- .shift = 9,
- .lock = &meson_clk_lock,
- .table = mux_table_vpu,
+static struct clk_regmap gxbb_vpu_0_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_VPU_CLK_CNTL,
+ .mask = 0x3,
+ .shift = 9,
+ },
.hw.init = &(struct clk_init_data){
.name = "vpu_0_sel",
- .ops = &clk_mux_ops,
+ .ops = &clk_regmap_mux_ops,
/*
* bits 9:10 selects from 4 possible parents:
* fclk_div4, fclk_div3, fclk_div5, fclk_div7,
@@ -1147,42 +1312,44 @@ static struct clk_mux gxbb_vpu_0_sel = {
},
};
-static struct clk_divider gxbb_vpu_0_div = {
- .reg = (void *)HHI_VPU_CLK_CNTL,
- .shift = 0,
- .width = 7,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_vpu_0_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_VPU_CLK_CNTL,
+ .shift = 0,
+ .width = 7,
+ },
.hw.init = &(struct clk_init_data){
.name = "vpu_0_div",
- .ops = &clk_divider_ops,
+ .ops = &clk_regmap_divider_ops,
.parent_names = (const char *[]){ "vpu_0_sel" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_gate gxbb_vpu_0 = {
- .reg = (void *)HHI_VPU_CLK_CNTL,
- .bit_idx = 8,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_vpu_0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VPU_CLK_CNTL,
+ .bit_idx = 8,
+ },
.hw.init = &(struct clk_init_data) {
.name = "vpu_0",
- .ops = &clk_gate_ops,
+ .ops = &clk_regmap_gate_ops,
.parent_names = (const char *[]){ "vpu_0_div" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
},
};
-static struct clk_mux gxbb_vpu_1_sel = {
- .reg = (void *)HHI_VPU_CLK_CNTL,
- .mask = 0x3,
- .shift = 25,
- .lock = &meson_clk_lock,
- .table = mux_table_vpu,
+static struct clk_regmap gxbb_vpu_1_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_VPU_CLK_CNTL,
+ .mask = 0x3,
+ .shift = 25,
+ },
.hw.init = &(struct clk_init_data){
.name = "vpu_1_sel",
- .ops = &clk_mux_ops,
+ .ops = &clk_regmap_mux_ops,
/*
* bits 25:26 selects from 4 possible parents:
* fclk_div4, fclk_div3, fclk_div5, fclk_div7,
@@ -1193,41 +1360,44 @@ static struct clk_mux gxbb_vpu_1_sel = {
},
};
-static struct clk_divider gxbb_vpu_1_div = {
- .reg = (void *)HHI_VPU_CLK_CNTL,
- .shift = 16,
- .width = 7,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_vpu_1_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_VPU_CLK_CNTL,
+ .shift = 16,
+ .width = 7,
+ },
.hw.init = &(struct clk_init_data){
.name = "vpu_1_div",
- .ops = &clk_divider_ops,
+ .ops = &clk_regmap_divider_ops,
.parent_names = (const char *[]){ "vpu_1_sel" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_gate gxbb_vpu_1 = {
- .reg = (void *)HHI_VPU_CLK_CNTL,
- .bit_idx = 24,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_vpu_1 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VPU_CLK_CNTL,
+ .bit_idx = 24,
+ },
.hw.init = &(struct clk_init_data) {
.name = "vpu_1",
- .ops = &clk_gate_ops,
+ .ops = &clk_regmap_gate_ops,
.parent_names = (const char *[]){ "vpu_1_div" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
},
};
-static struct clk_mux gxbb_vpu = {
- .reg = (void *)HHI_VPU_CLK_CNTL,
- .mask = 1,
- .shift = 31,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_vpu = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_VPU_CLK_CNTL,
+ .mask = 1,
+ .shift = 31,
+ },
.hw.init = &(struct clk_init_data){
.name = "vpu",
- .ops = &clk_mux_ops,
+ .ops = &clk_regmap_mux_ops,
/*
* bit 31 selects from 2 possible parents:
* vpu_0 or vpu_1
@@ -1240,20 +1410,19 @@ static struct clk_mux gxbb_vpu = {
/* VAPB Clock */
-static u32 mux_table_vapb[] = {0, 1, 2, 3};
static const char * const gxbb_vapb_parent_names[] = {
"fclk_div4", "fclk_div3", "fclk_div5", "fclk_div7"
};
-static struct clk_mux gxbb_vapb_0_sel = {
- .reg = (void *)HHI_VAPBCLK_CNTL,
- .mask = 0x3,
- .shift = 9,
- .lock = &meson_clk_lock,
- .table = mux_table_vapb,
+static struct clk_regmap gxbb_vapb_0_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_VAPBCLK_CNTL,
+ .mask = 0x3,
+ .shift = 9,
+ },
.hw.init = &(struct clk_init_data){
.name = "vapb_0_sel",
- .ops = &clk_mux_ops,
+ .ops = &clk_regmap_mux_ops,
/*
* bits 9:10 selects from 4 possible parents:
* fclk_div4, fclk_div3, fclk_div5, fclk_div7,
@@ -1264,42 +1433,44 @@ static struct clk_mux gxbb_vapb_0_sel = {
},
};
-static struct clk_divider gxbb_vapb_0_div = {
- .reg = (void *)HHI_VAPBCLK_CNTL,
- .shift = 0,
- .width = 7,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_vapb_0_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_VAPBCLK_CNTL,
+ .shift = 0,
+ .width = 7,
+ },
.hw.init = &(struct clk_init_data){
.name = "vapb_0_div",
- .ops = &clk_divider_ops,
+ .ops = &clk_regmap_divider_ops,
.parent_names = (const char *[]){ "vapb_0_sel" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_gate gxbb_vapb_0 = {
- .reg = (void *)HHI_VAPBCLK_CNTL,
- .bit_idx = 8,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_vapb_0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VAPBCLK_CNTL,
+ .bit_idx = 8,
+ },
.hw.init = &(struct clk_init_data) {
.name = "vapb_0",
- .ops = &clk_gate_ops,
+ .ops = &clk_regmap_gate_ops,
.parent_names = (const char *[]){ "vapb_0_div" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
},
};
-static struct clk_mux gxbb_vapb_1_sel = {
- .reg = (void *)HHI_VAPBCLK_CNTL,
- .mask = 0x3,
- .shift = 25,
- .lock = &meson_clk_lock,
- .table = mux_table_vapb,
+static struct clk_regmap gxbb_vapb_1_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_VAPBCLK_CNTL,
+ .mask = 0x3,
+ .shift = 25,
+ },
.hw.init = &(struct clk_init_data){
.name = "vapb_1_sel",
- .ops = &clk_mux_ops,
+ .ops = &clk_regmap_mux_ops,
/*
* bits 25:26 selects from 4 possible parents:
* fclk_div4, fclk_div3, fclk_div5, fclk_div7,
@@ -1310,41 +1481,44 @@ static struct clk_mux gxbb_vapb_1_sel = {
},
};
-static struct clk_divider gxbb_vapb_1_div = {
- .reg = (void *)HHI_VAPBCLK_CNTL,
- .shift = 16,
- .width = 7,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_vapb_1_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_VAPBCLK_CNTL,
+ .shift = 16,
+ .width = 7,
+ },
.hw.init = &(struct clk_init_data){
.name = "vapb_1_div",
- .ops = &clk_divider_ops,
+ .ops = &clk_regmap_divider_ops,
.parent_names = (const char *[]){ "vapb_1_sel" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
},
};
-static struct clk_gate gxbb_vapb_1 = {
- .reg = (void *)HHI_VAPBCLK_CNTL,
- .bit_idx = 24,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_vapb_1 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VAPBCLK_CNTL,
+ .bit_idx = 24,
+ },
.hw.init = &(struct clk_init_data) {
.name = "vapb_1",
- .ops = &clk_gate_ops,
+ .ops = &clk_regmap_gate_ops,
.parent_names = (const char *[]){ "vapb_1_div" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
},
};
-static struct clk_mux gxbb_vapb_sel = {
- .reg = (void *)HHI_VAPBCLK_CNTL,
- .mask = 1,
- .shift = 31,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_vapb_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_VAPBCLK_CNTL,
+ .mask = 1,
+ .shift = 31,
+ },
.hw.init = &(struct clk_init_data){
.name = "vapb_sel",
- .ops = &clk_mux_ops,
+ .ops = &clk_regmap_mux_ops,
/*
* bit 31 selects from 2 possible parents:
* vapb_0 or vapb_1
@@ -1355,13 +1529,14 @@ static struct clk_mux gxbb_vapb_sel = {
},
};
-static struct clk_gate gxbb_vapb = {
- .reg = (void *)HHI_VAPBCLK_CNTL,
- .bit_idx = 30,
- .lock = &meson_clk_lock,
+static struct clk_regmap gxbb_vapb = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VAPBCLK_CNTL,
+ .bit_idx = 30,
+ },
.hw.init = &(struct clk_init_data) {
.name = "vapb",
- .ops = &clk_gate_ops,
+ .ops = &clk_regmap_gate_ops,
.parent_names = (const char *[]){ "vapb_sel" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
@@ -1601,6 +1776,16 @@ static struct clk_hw_onecell_data gxbb_hw_onecell_data = {
[CLKID_VAPB_1] = &gxbb_vapb_1.hw,
[CLKID_VAPB_SEL] = &gxbb_vapb_sel.hw,
[CLKID_VAPB] = &gxbb_vapb.hw,
+ [CLKID_HDMI_PLL_PRE_MULT] = &gxbb_hdmi_pll_pre_mult.hw,
+ [CLKID_MPLL0_DIV] = &gxbb_mpll0_div.hw,
+ [CLKID_MPLL1_DIV] = &gxbb_mpll1_div.hw,
+ [CLKID_MPLL2_DIV] = &gxbb_mpll2_div.hw,
+ [CLKID_MPLL_PREDIV] = &gxbb_mpll_prediv.hw,
+ [CLKID_FCLK_DIV2_DIV] = &gxbb_fclk_div2_div.hw,
+ [CLKID_FCLK_DIV3_DIV] = &gxbb_fclk_div3_div.hw,
+ [CLKID_FCLK_DIV4_DIV] = &gxbb_fclk_div4_div.hw,
+ [CLKID_FCLK_DIV5_DIV] = &gxbb_fclk_div5_div.hw,
+ [CLKID_FCLK_DIV7_DIV] = &gxbb_fclk_div7_div.hw,
[NR_CLKS] = NULL,
},
.num = NR_CLKS,
@@ -1609,7 +1794,7 @@ static struct clk_hw_onecell_data gxbb_hw_onecell_data = {
static struct clk_hw_onecell_data gxl_hw_onecell_data = {
.hws = {
[CLKID_SYS_PLL] = &gxbb_sys_pll.hw,
- [CLKID_HDMI_PLL] = &gxbb_hdmi_pll.hw,
+ [CLKID_HDMI_PLL] = &gxl_hdmi_pll.hw,
[CLKID_FIXED_PLL] = &gxbb_fixed_pll.hw,
[CLKID_FCLK_DIV2] = &gxbb_fclk_div2.hw,
[CLKID_FCLK_DIV3] = &gxbb_fclk_div3.hw,
@@ -1748,34 +1933,31 @@ static struct clk_hw_onecell_data gxl_hw_onecell_data = {
[CLKID_VAPB_1] = &gxbb_vapb_1.hw,
[CLKID_VAPB_SEL] = &gxbb_vapb_sel.hw,
[CLKID_VAPB] = &gxbb_vapb.hw,
+ [CLKID_MPLL0_DIV] = &gxbb_mpll0_div.hw,
+ [CLKID_MPLL1_DIV] = &gxbb_mpll1_div.hw,
+ [CLKID_MPLL2_DIV] = &gxbb_mpll2_div.hw,
+ [CLKID_MPLL_PREDIV] = &gxbb_mpll_prediv.hw,
+ [CLKID_FCLK_DIV2_DIV] = &gxbb_fclk_div2_div.hw,
+ [CLKID_FCLK_DIV3_DIV] = &gxbb_fclk_div3_div.hw,
+ [CLKID_FCLK_DIV4_DIV] = &gxbb_fclk_div4_div.hw,
+ [CLKID_FCLK_DIV5_DIV] = &gxbb_fclk_div5_div.hw,
+ [CLKID_FCLK_DIV7_DIV] = &gxbb_fclk_div7_div.hw,
[NR_CLKS] = NULL,
},
.num = NR_CLKS,
};
-/* Convenience tables to populate base addresses in .probe */
-
-static struct meson_clk_pll *const gxbb_clk_plls[] = {
- &gxbb_fixed_pll,
- &gxbb_hdmi_pll,
- &gxbb_sys_pll,
+static struct clk_regmap *const gxbb_clk_regmaps[] = {
&gxbb_gp0_pll,
-};
-
-static struct meson_clk_pll *const gxl_clk_plls[] = {
- &gxbb_fixed_pll,
&gxbb_hdmi_pll,
- &gxbb_sys_pll,
- &gxl_gp0_pll,
};
-static struct meson_clk_mpll *const gxbb_clk_mplls[] = {
- &gxbb_mpll0,
- &gxbb_mpll1,
- &gxbb_mpll2,
+static struct clk_regmap *const gxl_clk_regmaps[] = {
+ &gxl_gp0_pll,
+ &gxl_hdmi_pll,
};
-static struct clk_gate *const gxbb_clk_gates[] = {
+static struct clk_regmap *const gx_clk_regmaps[] = {
&gxbb_clk81,
&gxbb_ddr,
&gxbb_dos,
@@ -1872,9 +2054,19 @@ static struct clk_gate *const gxbb_clk_gates[] = {
&gxbb_vapb_0,
&gxbb_vapb_1,
&gxbb_vapb,
-};
-
-static struct clk_mux *const gxbb_clk_muxes[] = {
+ &gxbb_mpeg_clk_div,
+ &gxbb_sar_adc_clk_div,
+ &gxbb_mali_0_div,
+ &gxbb_mali_1_div,
+ &gxbb_cts_mclk_i958_div,
+ &gxbb_32k_clk_div,
+ &gxbb_sd_emmc_a_clk0_div,
+ &gxbb_sd_emmc_b_clk0_div,
+ &gxbb_sd_emmc_c_clk0_div,
+ &gxbb_vpu_0_div,
+ &gxbb_vpu_1_div,
+ &gxbb_vapb_0_div,
+ &gxbb_vapb_1_div,
&gxbb_mpeg_clk_sel,
&gxbb_sar_adc_clk_sel,
&gxbb_mali_0_sel,
@@ -1893,73 +2085,38 @@ static struct clk_mux *const gxbb_clk_muxes[] = {
&gxbb_vapb_0_sel,
&gxbb_vapb_1_sel,
&gxbb_vapb_sel,
-};
-
-static struct clk_divider *const gxbb_clk_dividers[] = {
- &gxbb_mpeg_clk_div,
- &gxbb_sar_adc_clk_div,
- &gxbb_mali_0_div,
- &gxbb_mali_1_div,
- &gxbb_cts_mclk_i958_div,
- &gxbb_32k_clk_div,
- &gxbb_sd_emmc_a_clk0_div,
- &gxbb_sd_emmc_b_clk0_div,
- &gxbb_sd_emmc_c_clk0_div,
- &gxbb_vpu_0_div,
- &gxbb_vpu_1_div,
- &gxbb_vapb_0_div,
- &gxbb_vapb_1_div,
-};
-
-static struct meson_clk_audio_divider *const gxbb_audio_dividers[] = {
+ &gxbb_mpll0,
+ &gxbb_mpll1,
+ &gxbb_mpll2,
+ &gxbb_mpll0_div,
+ &gxbb_mpll1_div,
+ &gxbb_mpll2_div,
&gxbb_cts_amclk_div,
+ &gxbb_fixed_pll,
+ &gxbb_sys_pll,
+ &gxbb_mpll_prediv,
+ &gxbb_fclk_div2,
+ &gxbb_fclk_div3,
+ &gxbb_fclk_div4,
+ &gxbb_fclk_div5,
+ &gxbb_fclk_div7,
};
struct clkc_data {
- struct clk_gate *const *clk_gates;
- unsigned int clk_gates_count;
- struct meson_clk_mpll *const *clk_mplls;
- unsigned int clk_mplls_count;
- struct meson_clk_pll *const *clk_plls;
- unsigned int clk_plls_count;
- struct clk_mux *const *clk_muxes;
- unsigned int clk_muxes_count;
- struct clk_divider *const *clk_dividers;
- unsigned int clk_dividers_count;
- struct meson_clk_audio_divider *const *clk_audio_dividers;
- unsigned int clk_audio_dividers_count;
+ struct clk_regmap *const *regmap_clks;
+ unsigned int regmap_clks_count;
struct clk_hw_onecell_data *hw_onecell_data;
};
static const struct clkc_data gxbb_clkc_data = {
- .clk_gates = gxbb_clk_gates,
- .clk_gates_count = ARRAY_SIZE(gxbb_clk_gates),
- .clk_mplls = gxbb_clk_mplls,
- .clk_mplls_count = ARRAY_SIZE(gxbb_clk_mplls),
- .clk_plls = gxbb_clk_plls,
- .clk_plls_count = ARRAY_SIZE(gxbb_clk_plls),
- .clk_muxes = gxbb_clk_muxes,
- .clk_muxes_count = ARRAY_SIZE(gxbb_clk_muxes),
- .clk_dividers = gxbb_clk_dividers,
- .clk_dividers_count = ARRAY_SIZE(gxbb_clk_dividers),
- .clk_audio_dividers = gxbb_audio_dividers,
- .clk_audio_dividers_count = ARRAY_SIZE(gxbb_audio_dividers),
+ .regmap_clks = gxbb_clk_regmaps,
+ .regmap_clks_count = ARRAY_SIZE(gxbb_clk_regmaps),
.hw_onecell_data = &gxbb_hw_onecell_data,
};
static const struct clkc_data gxl_clkc_data = {
- .clk_gates = gxbb_clk_gates,
- .clk_gates_count = ARRAY_SIZE(gxbb_clk_gates),
- .clk_mplls = gxbb_clk_mplls,
- .clk_mplls_count = ARRAY_SIZE(gxbb_clk_mplls),
- .clk_plls = gxl_clk_plls,
- .clk_plls_count = ARRAY_SIZE(gxl_clk_plls),
- .clk_muxes = gxbb_clk_muxes,
- .clk_muxes_count = ARRAY_SIZE(gxbb_clk_muxes),
- .clk_dividers = gxbb_clk_dividers,
- .clk_dividers_count = ARRAY_SIZE(gxbb_clk_dividers),
- .clk_audio_dividers = gxbb_audio_dividers,
- .clk_audio_dividers_count = ARRAY_SIZE(gxbb_audio_dividers),
+ .regmap_clks = gxl_clk_regmaps,
+ .regmap_clks_count = ARRAY_SIZE(gxl_clk_regmaps),
.hw_onecell_data = &gxl_hw_onecell_data,
};
@@ -1969,71 +2126,79 @@ static const struct of_device_id clkc_match_table[] = {
{},
};
+static const struct regmap_config clkc_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};
+
static int gxbb_clkc_probe(struct platform_device *pdev)
{
const struct clkc_data *clkc_data;
+ struct resource *res;
void __iomem *clk_base;
- int ret, clkid, i;
+ struct regmap *map;
+ int ret, i;
struct device *dev = &pdev->dev;
- clkc_data = of_device_get_match_data(&pdev->dev);
+ clkc_data = of_device_get_match_data(dev);
if (!clkc_data)
return -EINVAL;
- /* Generic clocks and PLLs */
- clk_base = of_iomap(dev->of_node, 0);
- if (!clk_base) {
- pr_err("%s: Unable to map clk base\n", __func__);
- return -ENXIO;
- }
-
- /* Populate base address for PLLs */
- for (i = 0; i < clkc_data->clk_plls_count; i++)
- clkc_data->clk_plls[i]->base = clk_base;
-
- /* Populate base address for MPLLs */
- for (i = 0; i < clkc_data->clk_mplls_count; i++)
- clkc_data->clk_mplls[i]->base = clk_base;
+ /* Get the hhi system controller node if available */
+ map = syscon_node_to_regmap(of_get_parent(dev->of_node));
+ if (IS_ERR(map)) {
+ dev_err(dev,
+ "failed to get HHI regmap - Trying obsolete regs\n");
- /* Populate base address for gates */
- for (i = 0; i < clkc_data->clk_gates_count; i++)
- clkc_data->clk_gates[i]->reg = clk_base +
- (u64)clkc_data->clk_gates[i]->reg;
-
- /* Populate base address for muxes */
- for (i = 0; i < clkc_data->clk_muxes_count; i++)
- clkc_data->clk_muxes[i]->reg = clk_base +
- (u64)clkc_data->clk_muxes[i]->reg;
+ /*
+ * FIXME: HHI registers should be accessed through
+ * the appropriate system controller. This is required because
+ * there is more than just clocks in this register space
+ *
+ * This fallback method is only provided temporarily until
+ * all the platform DTs are properly using the syscon node
+ */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+
+ clk_base = devm_ioremap(dev, res->start, resource_size(res));
+ if (!clk_base) {
+ dev_err(dev, "Unable to map clk base\n");
+ return -ENXIO;
+ }
+
+ map = devm_regmap_init_mmio(dev, clk_base,
+ &clkc_regmap_config);
+ if (IS_ERR(map))
+ return PTR_ERR(map);
+ }
- /* Populate base address for dividers */
- for (i = 0; i < clkc_data->clk_dividers_count; i++)
- clkc_data->clk_dividers[i]->reg = clk_base +
- (u64)clkc_data->clk_dividers[i]->reg;
+ /* Populate regmap for the common regmap backed clocks */
+ for (i = 0; i < ARRAY_SIZE(gx_clk_regmaps); i++)
+ gx_clk_regmaps[i]->map = map;
- /* Populate base address for the audio dividers */
- for (i = 0; i < clkc_data->clk_audio_dividers_count; i++)
- clkc_data->clk_audio_dividers[i]->base = clk_base;
+ /* Populate regmap for soc specific clocks */
+ for (i = 0; i < clkc_data->regmap_clks_count; i++)
+ clkc_data->regmap_clks[i]->map = map;
- /*
- * register all clks
- */
- for (clkid = 0; clkid < clkc_data->hw_onecell_data->num; clkid++) {
+ /* Register all clks */
+ for (i = 0; i < clkc_data->hw_onecell_data->num; i++) {
/* array might be sparse */
- if (!clkc_data->hw_onecell_data->hws[clkid])
+ if (!clkc_data->hw_onecell_data->hws[i])
continue;
ret = devm_clk_hw_register(dev,
- clkc_data->hw_onecell_data->hws[clkid]);
- if (ret)
- goto iounmap;
+ clkc_data->hw_onecell_data->hws[i]);
+ if (ret) {
+ dev_err(dev, "Clock registration failed\n");
+ return ret;
+ }
}
- return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
- clkc_data->hw_onecell_data);
-
-iounmap:
- iounmap(clk_base);
- return ret;
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
+ clkc_data->hw_onecell_data);
}
static struct platform_driver gxbb_driver = {
diff --git a/drivers/clk/meson/gxbb.h b/drivers/clk/meson/gxbb.h
index aee6fbba2004..9febf3f03739 100644
--- a/drivers/clk/meson/gxbb.h
+++ b/drivers/clk/meson/gxbb.h
@@ -194,8 +194,18 @@
#define CLKID_VPU_1_DIV 130
#define CLKID_VAPB_0_DIV 134
#define CLKID_VAPB_1_DIV 137
-
-#define NR_CLKS 141
+#define CLKID_HDMI_PLL_PRE_MULT 141
+#define CLKID_MPLL0_DIV 142
+#define CLKID_MPLL1_DIV 143
+#define CLKID_MPLL2_DIV 144
+#define CLKID_MPLL_PREDIV 145
+#define CLKID_FCLK_DIV2_DIV 146
+#define CLKID_FCLK_DIV3_DIV 147
+#define CLKID_FCLK_DIV4_DIV 148
+#define CLKID_FCLK_DIV5_DIV 149
+#define CLKID_FCLK_DIV7_DIV 150
+
+#define NR_CLKS 151
/* include the CLKIDs that have been made part of the DT binding */
#include <dt-bindings/clock/gxbb-clkc.h>
diff --git a/drivers/clk/meson/meson8b.c b/drivers/clk/meson/meson8b.c
index 3ffea80c1308..cc2992493e0b 100644
--- a/drivers/clk/meson/meson8b.c
+++ b/drivers/clk/meson/meson8b.c
@@ -23,14 +23,16 @@
#include <linux/clk.h>
#include <linux/clk-provider.h>
+#include <linux/init.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/reset-controller.h>
#include <linux/slab.h>
-#include <linux/init.h>
+#include <linux/regmap.h>
#include "clkc.h"
#include "meson8b.h"
+#include "clk-regmap.h"
static DEFINE_SPINLOCK(meson_clk_lock);
@@ -97,20 +99,6 @@ static const struct pll_rate_table sys_pll_rate_table[] = {
{ /* sentinel */ },
};
-static const struct clk_div_table cpu_div_table[] = {
- { .val = 1, .div = 1 },
- { .val = 2, .div = 2 },
- { .val = 3, .div = 3 },
- { .val = 2, .div = 4 },
- { .val = 3, .div = 6 },
- { .val = 4, .div = 8 },
- { .val = 5, .div = 10 },
- { .val = 6, .div = 12 },
- { .val = 7, .div = 14 },
- { .val = 8, .div = 16 },
- { /* sentinel */ },
-};
-
static struct clk_fixed_rate meson8b_xtal = {
.fixed_rate = 24000000,
.hw.init = &(struct clk_init_data){
@@ -120,23 +108,39 @@ static struct clk_fixed_rate meson8b_xtal = {
},
};
-static struct meson_clk_pll meson8b_fixed_pll = {
- .m = {
- .reg_off = HHI_MPLL_CNTL,
- .shift = 0,
- .width = 9,
- },
- .n = {
- .reg_off = HHI_MPLL_CNTL,
- .shift = 9,
- .width = 5,
- },
- .od = {
- .reg_off = HHI_MPLL_CNTL,
- .shift = 16,
- .width = 2,
+static struct clk_regmap meson8b_fixed_pll = {
+ .data = &(struct meson_clk_pll_data){
+ .m = {
+ .reg_off = HHI_MPLL_CNTL,
+ .shift = 0,
+ .width = 9,
+ },
+ .n = {
+ .reg_off = HHI_MPLL_CNTL,
+ .shift = 9,
+ .width = 5,
+ },
+ .od = {
+ .reg_off = HHI_MPLL_CNTL,
+ .shift = 16,
+ .width = 2,
+ },
+ .frac = {
+ .reg_off = HHI_MPLL_CNTL2,
+ .shift = 0,
+ .width = 12,
+ },
+ .l = {
+ .reg_off = HHI_MPLL_CNTL,
+ .shift = 31,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = HHI_MPLL_CNTL,
+ .shift = 29,
+ .width = 1,
+ },
},
- .lock = &meson_clk_lock,
.hw.init = &(struct clk_init_data){
.name = "fixed_pll",
.ops = &meson_clk_pll_ro_ops,
@@ -146,23 +150,34 @@ static struct meson_clk_pll meson8b_fixed_pll = {
},
};
-static struct meson_clk_pll meson8b_vid_pll = {
- .m = {
- .reg_off = HHI_VID_PLL_CNTL,
- .shift = 0,
- .width = 9,
+static struct clk_regmap meson8b_vid_pll = {
+ .data = &(struct meson_clk_pll_data){
+ .m = {
+ .reg_off = HHI_VID_PLL_CNTL,
+ .shift = 0,
+ .width = 9,
+ },
+ .n = {
+ .reg_off = HHI_VID_PLL_CNTL,
+ .shift = 9,
+ .width = 5,
+ },
+ .od = {
+ .reg_off = HHI_VID_PLL_CNTL,
+ .shift = 16,
+ .width = 2,
+ },
+ .l = {
+ .reg_off = HHI_VID_PLL_CNTL,
+ .shift = 31,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = HHI_VID_PLL_CNTL,
+ .shift = 29,
+ .width = 1,
+ },
},
- .n = {
- .reg_off = HHI_VID_PLL_CNTL,
- .shift = 9,
- .width = 5,
- },
- .od = {
- .reg_off = HHI_VID_PLL_CNTL,
- .shift = 16,
- .width = 2,
- },
- .lock = &meson_clk_lock,
.hw.init = &(struct clk_init_data){
.name = "vid_pll",
.ops = &meson_clk_pll_ro_ops,
@@ -172,213 +187,317 @@ static struct meson_clk_pll meson8b_vid_pll = {
},
};
-static struct meson_clk_pll meson8b_sys_pll = {
- .m = {
- .reg_off = HHI_SYS_PLL_CNTL,
- .shift = 0,
- .width = 9,
- },
- .n = {
- .reg_off = HHI_SYS_PLL_CNTL,
- .shift = 9,
- .width = 5,
+static struct clk_regmap meson8b_sys_pll = {
+ .data = &(struct meson_clk_pll_data){
+ .m = {
+ .reg_off = HHI_SYS_PLL_CNTL,
+ .shift = 0,
+ .width = 9,
+ },
+ .n = {
+ .reg_off = HHI_SYS_PLL_CNTL,
+ .shift = 9,
+ .width = 5,
+ },
+ .od = {
+ .reg_off = HHI_SYS_PLL_CNTL,
+ .shift = 16,
+ .width = 2,
+ },
+ .l = {
+ .reg_off = HHI_SYS_PLL_CNTL,
+ .shift = 31,
+ .width = 1,
+ },
+ .rst = {
+ .reg_off = HHI_SYS_PLL_CNTL,
+ .shift = 29,
+ .width = 1,
+ },
+ .table = sys_pll_rate_table,
},
- .od = {
- .reg_off = HHI_SYS_PLL_CNTL,
- .shift = 16,
- .width = 2,
- },
- .rate_table = sys_pll_rate_table,
- .rate_count = ARRAY_SIZE(sys_pll_rate_table),
- .lock = &meson_clk_lock,
.hw.init = &(struct clk_init_data){
.name = "sys_pll",
- .ops = &meson_clk_pll_ops,
+ .ops = &meson_clk_pll_ro_ops,
.parent_names = (const char *[]){ "xtal" },
.num_parents = 1,
.flags = CLK_GET_RATE_NOCACHE,
},
};
-static struct clk_fixed_factor meson8b_fclk_div2 = {
+static struct clk_fixed_factor meson8b_fclk_div2_div = {
.mult = 1,
.div = 2,
.hw.init = &(struct clk_init_data){
- .name = "fclk_div2",
+ .name = "fclk_div2_div",
.ops = &clk_fixed_factor_ops,
.parent_names = (const char *[]){ "fixed_pll" },
.num_parents = 1,
},
};
-static struct clk_fixed_factor meson8b_fclk_div3 = {
+static struct clk_regmap meson8b_fclk_div2 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPLL_CNTL6,
+ .bit_idx = 27,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div2",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "fclk_div2_div" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor meson8b_fclk_div3_div = {
.mult = 1,
.div = 3,
.hw.init = &(struct clk_init_data){
- .name = "fclk_div3",
+ .name = "fclk_div_div3",
.ops = &clk_fixed_factor_ops,
.parent_names = (const char *[]){ "fixed_pll" },
.num_parents = 1,
},
};
-static struct clk_fixed_factor meson8b_fclk_div4 = {
+static struct clk_regmap meson8b_fclk_div3 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPLL_CNTL6,
+ .bit_idx = 28,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div3",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "fclk_div3_div" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor meson8b_fclk_div4_div = {
.mult = 1,
.div = 4,
.hw.init = &(struct clk_init_data){
- .name = "fclk_div4",
+ .name = "fclk_div4_div",
.ops = &clk_fixed_factor_ops,
.parent_names = (const char *[]){ "fixed_pll" },
.num_parents = 1,
},
};
-static struct clk_fixed_factor meson8b_fclk_div5 = {
+static struct clk_regmap meson8b_fclk_div4 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPLL_CNTL6,
+ .bit_idx = 29,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div4",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "fclk_div4_div" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor meson8b_fclk_div5_div = {
.mult = 1,
.div = 5,
.hw.init = &(struct clk_init_data){
- .name = "fclk_div5",
+ .name = "fclk_div5_div",
.ops = &clk_fixed_factor_ops,
.parent_names = (const char *[]){ "fixed_pll" },
.num_parents = 1,
},
};
-static struct clk_fixed_factor meson8b_fclk_div7 = {
+static struct clk_regmap meson8b_fclk_div5 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPLL_CNTL6,
+ .bit_idx = 30,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div5",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "fclk_div5_div" },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_fixed_factor meson8b_fclk_div7_div = {
.mult = 1,
.div = 7,
.hw.init = &(struct clk_init_data){
- .name = "fclk_div7",
+ .name = "fclk_div7_div",
.ops = &clk_fixed_factor_ops,
.parent_names = (const char *[]){ "fixed_pll" },
.num_parents = 1,
},
};
-static struct meson_clk_mpll meson8b_mpll0 = {
- .sdm = {
- .reg_off = HHI_MPLL_CNTL7,
- .shift = 0,
- .width = 14,
+static struct clk_regmap meson8b_fclk_div7 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPLL_CNTL6,
+ .bit_idx = 31,
},
- .sdm_en = {
- .reg_off = HHI_MPLL_CNTL7,
- .shift = 15,
- .width = 1,
+ .hw.init = &(struct clk_init_data){
+ .name = "fclk_div7",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "fclk_div7_div" },
+ .num_parents = 1,
},
- .n2 = {
- .reg_off = HHI_MPLL_CNTL7,
- .shift = 16,
- .width = 9,
+};
+
+static struct clk_regmap meson8b_mpll_prediv = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_MPLL_CNTL5,
+ .shift = 12,
+ .width = 1,
},
- .en = {
- .reg_off = HHI_MPLL_CNTL7,
- .shift = 14,
- .width = 1,
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll_prediv",
+ .ops = &clk_regmap_divider_ro_ops,
+ .parent_names = (const char *[]){ "fixed_pll" },
+ .num_parents = 1,
},
- .ssen = {
- .reg_off = HHI_MPLL_CNTL,
- .shift = 25,
- .width = 1,
+};
+
+static struct clk_regmap meson8b_mpll0_div = {
+ .data = &(struct meson_clk_mpll_data){
+ .sdm = {
+ .reg_off = HHI_MPLL_CNTL7,
+ .shift = 0,
+ .width = 14,
+ },
+ .sdm_en = {
+ .reg_off = HHI_MPLL_CNTL7,
+ .shift = 15,
+ .width = 1,
+ },
+ .n2 = {
+ .reg_off = HHI_MPLL_CNTL7,
+ .shift = 16,
+ .width = 9,
+ },
+ .ssen = {
+ .reg_off = HHI_MPLL_CNTL,
+ .shift = 25,
+ .width = 1,
+ },
+ .lock = &meson_clk_lock,
},
- .lock = &meson_clk_lock,
.hw.init = &(struct clk_init_data){
- .name = "mpll0",
+ .name = "mpll0_div",
.ops = &meson_clk_mpll_ops,
- .parent_names = (const char *[]){ "fixed_pll" },
+ .parent_names = (const char *[]){ "mpll_prediv" },
.num_parents = 1,
},
};
-static struct meson_clk_mpll meson8b_mpll1 = {
- .sdm = {
- .reg_off = HHI_MPLL_CNTL8,
- .shift = 0,
- .width = 14,
+static struct clk_regmap meson8b_mpll0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPLL_CNTL7,
+ .bit_idx = 14,
},
- .sdm_en = {
- .reg_off = HHI_MPLL_CNTL8,
- .shift = 15,
- .width = 1,
- },
- .n2 = {
- .reg_off = HHI_MPLL_CNTL8,
- .shift = 16,
- .width = 9,
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll0",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "mpll0_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
},
- .en = {
- .reg_off = HHI_MPLL_CNTL8,
- .shift = 14,
- .width = 1,
+};
+
+static struct clk_regmap meson8b_mpll1_div = {
+ .data = &(struct meson_clk_mpll_data){
+ .sdm = {
+ .reg_off = HHI_MPLL_CNTL8,
+ .shift = 0,
+ .width = 14,
+ },
+ .sdm_en = {
+ .reg_off = HHI_MPLL_CNTL8,
+ .shift = 15,
+ .width = 1,
+ },
+ .n2 = {
+ .reg_off = HHI_MPLL_CNTL8,
+ .shift = 16,
+ .width = 9,
+ },
+ .lock = &meson_clk_lock,
},
- .lock = &meson_clk_lock,
.hw.init = &(struct clk_init_data){
- .name = "mpll1",
+ .name = "mpll1_div",
.ops = &meson_clk_mpll_ops,
- .parent_names = (const char *[]){ "fixed_pll" },
+ .parent_names = (const char *[]){ "mpll_prediv" },
.num_parents = 1,
},
};
-static struct meson_clk_mpll meson8b_mpll2 = {
- .sdm = {
- .reg_off = HHI_MPLL_CNTL9,
- .shift = 0,
- .width = 14,
- },
- .sdm_en = {
- .reg_off = HHI_MPLL_CNTL9,
- .shift = 15,
- .width = 1,
+static struct clk_regmap meson8b_mpll1 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPLL_CNTL8,
+ .bit_idx = 14,
},
- .n2 = {
- .reg_off = HHI_MPLL_CNTL9,
- .shift = 16,
- .width = 9,
+ .hw.init = &(struct clk_init_data){
+ .name = "mpll1",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "mpll1_div" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
},
- .en = {
- .reg_off = HHI_MPLL_CNTL9,
- .shift = 14,
- .width = 1,
+};
+
+static struct clk_regmap meson8b_mpll2_div = {
+ .data = &(struct meson_clk_mpll_data){
+ .sdm = {
+ .reg_off = HHI_MPLL_CNTL9,
+ .shift = 0,
+ .width = 14,
+ },
+ .sdm_en = {
+ .reg_off = HHI_MPLL_CNTL9,
+ .shift = 15,
+ .width = 1,
+ },
+ .n2 = {
+ .reg_off = HHI_MPLL_CNTL9,
+ .shift = 16,
+ .width = 9,
+ },
+ .lock = &meson_clk_lock,
},
- .lock = &meson_clk_lock,
.hw.init = &(struct clk_init_data){
- .name = "mpll2",
+ .name = "mpll2_div",
.ops = &meson_clk_mpll_ops,
- .parent_names = (const char *[]){ "fixed_pll" },
+ .parent_names = (const char *[]){ "mpll_prediv" },
.num_parents = 1,
},
};
-/*
- * FIXME cpu clocks and the legacy composite clocks (e.g. clk81) are both PLL
- * post-dividers and should be modeled with their respective PLLs via the
- * forthcoming coordinated clock rates feature
- */
-static struct meson_clk_cpu meson8b_cpu_clk = {
- .reg_off = HHI_SYS_CPU_CLK_CNTL1,
- .div_table = cpu_div_table,
- .clk_nb.notifier_call = meson_clk_cpu_notifier_cb,
+static struct clk_regmap meson8b_mpll2 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPLL_CNTL9,
+ .bit_idx = 14,
+ },
.hw.init = &(struct clk_init_data){
- .name = "cpu_clk",
- .ops = &meson_clk_cpu_ops,
- .parent_names = (const char *[]){ "sys_pll" },
+ .name = "mpll2",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]){ "mpll2_div" },
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
},
};
static u32 mux_table_clk81[] = { 6, 5, 7 };
-
-struct clk_mux meson8b_mpeg_clk_sel = {
- .reg = (void *)HHI_MPEG_CLK_CNTL,
- .mask = 0x7,
- .shift = 12,
- .flags = CLK_MUX_READ_ONLY,
- .table = mux_table_clk81,
- .lock = &meson_clk_lock,
+static struct clk_regmap meson8b_mpeg_clk_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_MPEG_CLK_CNTL,
+ .mask = 0x7,
+ .shift = 12,
+ .table = mux_table_clk81,
+ },
.hw.init = &(struct clk_init_data){
.name = "mpeg_clk_sel",
- .ops = &clk_mux_ro_ops,
+ .ops = &clk_regmap_mux_ro_ops,
/*
* FIXME bits 14:12 selects from 8 possible parents:
* xtal, 1'b0 (wtf), fclk_div7, mpll_clkout1, mpll_clkout2,
@@ -387,34 +506,136 @@ struct clk_mux meson8b_mpeg_clk_sel = {
.parent_names = (const char *[]){ "fclk_div3", "fclk_div4",
"fclk_div5" },
.num_parents = 3,
- .flags = (CLK_SET_RATE_NO_REPARENT | CLK_IGNORE_UNUSED),
},
};
-struct clk_divider meson8b_mpeg_clk_div = {
- .reg = (void *)HHI_MPEG_CLK_CNTL,
- .shift = 0,
- .width = 7,
- .lock = &meson_clk_lock,
+static struct clk_regmap meson8b_mpeg_clk_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_MPEG_CLK_CNTL,
+ .shift = 0,
+ .width = 7,
+ },
.hw.init = &(struct clk_init_data){
.name = "mpeg_clk_div",
- .ops = &clk_divider_ops,
+ .ops = &clk_regmap_divider_ro_ops,
.parent_names = (const char *[]){ "mpeg_clk_sel" },
.num_parents = 1,
- .flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED),
},
};
-struct clk_gate meson8b_clk81 = {
- .reg = (void *)HHI_MPEG_CLK_CNTL,
- .bit_idx = 7,
- .lock = &meson_clk_lock,
+static struct clk_regmap meson8b_clk81 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_MPEG_CLK_CNTL,
+ .bit_idx = 7,
+ },
.hw.init = &(struct clk_init_data){
.name = "clk81",
- .ops = &clk_gate_ops,
+ .ops = &clk_regmap_gate_ops,
.parent_names = (const char *[]){ "mpeg_clk_div" },
.num_parents = 1,
- .flags = (CLK_SET_RATE_PARENT | CLK_IS_CRITICAL),
+ .flags = CLK_IS_CRITICAL,
+ },
+};
+
+static struct clk_regmap meson8b_cpu_in_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_SYS_CPU_CLK_CNTL0,
+ .mask = 0x1,
+ .shift = 0,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "cpu_in_sel",
+ .ops = &clk_regmap_mux_ro_ops,
+ .parent_names = (const char *[]){ "xtal", "sys_pll" },
+ .num_parents = 2,
+ .flags = (CLK_SET_RATE_PARENT |
+ CLK_SET_RATE_NO_REPARENT),
+ },
+};
+
+static struct clk_fixed_factor meson8b_cpu_div2 = {
+ .mult = 1,
+ .div = 2,
+ .hw.init = &(struct clk_init_data){
+ .name = "cpu_div2",
+ .ops = &clk_fixed_factor_ops,
+ .parent_names = (const char *[]){ "cpu_in_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_fixed_factor meson8b_cpu_div3 = {
+ .mult = 1,
+ .div = 3,
+ .hw.init = &(struct clk_init_data){
+ .name = "cpu_div3",
+ .ops = &clk_fixed_factor_ops,
+ .parent_names = (const char *[]){ "cpu_in_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_div_table cpu_scale_table[] = {
+ { .val = 2, .div = 4 },
+ { .val = 3, .div = 6 },
+ { .val = 4, .div = 8 },
+ { .val = 5, .div = 10 },
+ { .val = 6, .div = 12 },
+ { .val = 7, .div = 14 },
+ { .val = 8, .div = 16 },
+ { /* sentinel */ },
+};
+
+static struct clk_regmap meson8b_cpu_scale_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = HHI_SYS_CPU_CLK_CNTL1,
+ .shift = 20,
+ .width = 9,
+ .table = cpu_scale_table,
+ .flags = CLK_DIVIDER_ALLOW_ZERO,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "cpu_scale_div",
+ .ops = &clk_regmap_divider_ro_ops,
+ .parent_names = (const char *[]){ "cpu_in_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap meson8b_cpu_scale_out_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_SYS_CPU_CLK_CNTL0,
+ .mask = 0x3,
+ .shift = 2,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "cpu_scale_out_sel",
+ .ops = &clk_regmap_mux_ro_ops,
+ .parent_names = (const char *[]) { "cpu_in_sel",
+ "cpu_div2",
+ "cpu_div3",
+ "cpu_scale_div" },
+ .num_parents = 4,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap meson8b_cpu_clk = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_SYS_CPU_CLK_CNTL0,
+ .mask = 0x1,
+ .shift = 7,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "cpu_clk",
+ .ops = &clk_regmap_mux_ro_ops,
+ .parent_names = (const char *[]){ "xtal", "cpu_out_sel" },
+ .num_parents = 2,
+ .flags = (CLK_SET_RATE_PARENT |
+ CLK_SET_RATE_NO_REPARENT),
},
};
@@ -599,24 +820,26 @@ static struct clk_hw_onecell_data meson8b_hw_onecell_data = {
[CLKID_MPLL0] = &meson8b_mpll0.hw,
[CLKID_MPLL1] = &meson8b_mpll1.hw,
[CLKID_MPLL2] = &meson8b_mpll2.hw,
+ [CLKID_MPLL0_DIV] = &meson8b_mpll0_div.hw,
+ [CLKID_MPLL1_DIV] = &meson8b_mpll1_div.hw,
+ [CLKID_MPLL2_DIV] = &meson8b_mpll2_div.hw,
+ [CLKID_CPU_IN_SEL] = &meson8b_cpu_in_sel.hw,
+ [CLKID_CPU_DIV2] = &meson8b_cpu_div2.hw,
+ [CLKID_CPU_DIV3] = &meson8b_cpu_div3.hw,
+ [CLKID_CPU_SCALE_DIV] = &meson8b_cpu_scale_div.hw,
+ [CLKID_CPU_SCALE_OUT_SEL] = &meson8b_cpu_scale_out_sel.hw,
+ [CLKID_MPLL_PREDIV] = &meson8b_mpll_prediv.hw,
+ [CLKID_FCLK_DIV2_DIV] = &meson8b_fclk_div2_div.hw,
+ [CLKID_FCLK_DIV3_DIV] = &meson8b_fclk_div3_div.hw,
+ [CLKID_FCLK_DIV4_DIV] = &meson8b_fclk_div4_div.hw,
+ [CLKID_FCLK_DIV5_DIV] = &meson8b_fclk_div5_div.hw,
+ [CLKID_FCLK_DIV7_DIV] = &meson8b_fclk_div7_div.hw,
[CLK_NR_CLKS] = NULL,
},
.num = CLK_NR_CLKS,
};
-static struct meson_clk_pll *const meson8b_clk_plls[] = {
- &meson8b_fixed_pll,
- &meson8b_vid_pll,
- &meson8b_sys_pll,
-};
-
-static struct meson_clk_mpll *const meson8b_clk_mplls[] = {
- &meson8b_mpll0,
- &meson8b_mpll1,
- &meson8b_mpll2,
-};
-
-static struct clk_gate *const meson8b_clk_gates[] = {
+static struct clk_regmap *const meson8b_clk_regmaps[] = {
&meson8b_clk81,
&meson8b_ddr,
&meson8b_dos,
@@ -695,14 +918,27 @@ static struct clk_gate *const meson8b_clk_gates[] = {
&meson8b_ao_ahb_sram,
&meson8b_ao_ahb_bus,
&meson8b_ao_iface,
-};
-
-static struct clk_mux *const meson8b_clk_muxes[] = {
- &meson8b_mpeg_clk_sel,
-};
-
-static struct clk_divider *const meson8b_clk_dividers[] = {
&meson8b_mpeg_clk_div,
+ &meson8b_mpeg_clk_sel,
+ &meson8b_mpll0,
+ &meson8b_mpll1,
+ &meson8b_mpll2,
+ &meson8b_mpll0_div,
+ &meson8b_mpll1_div,
+ &meson8b_mpll2_div,
+ &meson8b_fixed_pll,
+ &meson8b_vid_pll,
+ &meson8b_sys_pll,
+ &meson8b_cpu_in_sel,
+ &meson8b_cpu_scale_div,
+ &meson8b_cpu_scale_out_sel,
+ &meson8b_cpu_clk,
+ &meson8b_mpll_prediv,
+ &meson8b_fclk_div2,
+ &meson8b_fclk_div3,
+ &meson8b_fclk_div4,
+ &meson8b_fclk_div5,
+ &meson8b_fclk_div7,
};
static const struct meson8b_clk_reset_line {
@@ -804,82 +1040,45 @@ static const struct reset_control_ops meson8b_clk_reset_ops = {
.deassert = meson8b_clk_reset_deassert,
};
+static const struct regmap_config clkc_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};
+
static int meson8b_clkc_probe(struct platform_device *pdev)
{
- int ret, clkid, i;
- struct clk_hw *parent_hw;
- struct clk *parent_clk;
+ int ret, i;
struct device *dev = &pdev->dev;
+ struct regmap *map;
if (!clk_base)
return -ENXIO;
- /* Populate base address for PLLs */
- for (i = 0; i < ARRAY_SIZE(meson8b_clk_plls); i++)
- meson8b_clk_plls[i]->base = clk_base;
-
- /* Populate base address for MPLLs */
- for (i = 0; i < ARRAY_SIZE(meson8b_clk_mplls); i++)
- meson8b_clk_mplls[i]->base = clk_base;
-
- /* Populate the base address for CPU clk */
- meson8b_cpu_clk.base = clk_base;
-
- /* Populate base address for gates */
- for (i = 0; i < ARRAY_SIZE(meson8b_clk_gates); i++)
- meson8b_clk_gates[i]->reg = clk_base +
- (u32)meson8b_clk_gates[i]->reg;
-
- /* Populate base address for muxes */
- for (i = 0; i < ARRAY_SIZE(meson8b_clk_muxes); i++)
- meson8b_clk_muxes[i]->reg = clk_base +
- (u32)meson8b_clk_muxes[i]->reg;
+ map = devm_regmap_init_mmio(dev, clk_base, &clkc_regmap_config);
+ if (IS_ERR(map))
+ return PTR_ERR(map);
- /* Populate base address for dividers */
- for (i = 0; i < ARRAY_SIZE(meson8b_clk_dividers); i++)
- meson8b_clk_dividers[i]->reg = clk_base +
- (u32)meson8b_clk_dividers[i]->reg;
+ /* Populate regmap for the regmap backed clocks */
+ for (i = 0; i < ARRAY_SIZE(meson8b_clk_regmaps); i++)
+ meson8b_clk_regmaps[i]->map = map;
/*
* register all clks
* CLKID_UNUSED = 0, so skip it and start with CLKID_XTAL = 1
*/
- for (clkid = CLKID_XTAL; clkid < CLK_NR_CLKS; clkid++) {
+ for (i = CLKID_XTAL; i < CLK_NR_CLKS; i++) {
/* array might be sparse */
- if (!meson8b_hw_onecell_data.hws[clkid])
+ if (!meson8b_hw_onecell_data.hws[i])
continue;
- /* FIXME convert to devm_clk_register */
- ret = devm_clk_hw_register(dev, meson8b_hw_onecell_data.hws[clkid]);
+ ret = devm_clk_hw_register(dev, meson8b_hw_onecell_data.hws[i]);
if (ret)
return ret;
}
- /*
- * Register CPU clk notifier
- *
- * FIXME this is wrong for a lot of reasons. First, the muxes should be
- * struct clk_hw objects. Second, we shouldn't program the muxes in
- * notifier handlers. The tricky programming sequence will be handled
- * by the forthcoming coordinated clock rates mechanism once that
- * feature is released.
- *
- * Furthermore, looking up the parent this way is terrible. At some
- * point we will stop allocating a default struct clk when registering
- * a new clk_hw, and this hack will no longer work. Releasing the ccr
- * feature before that time solves the problem :-)
- */
- parent_hw = clk_hw_get_parent(&meson8b_cpu_clk.hw);
- parent_clk = parent_hw->clk;
- ret = clk_notifier_register(parent_clk, &meson8b_cpu_clk.clk_nb);
- if (ret) {
- pr_err("%s: failed to register clock notifier for cpu_clk\n",
- __func__);
- return ret;
- }
-
- return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
- &meson8b_hw_onecell_data);
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
+ &meson8b_hw_onecell_data);
}
static const struct of_device_id meson8b_clkc_match_table[] = {
diff --git a/drivers/clk/meson/meson8b.h b/drivers/clk/meson/meson8b.h
index 2eaf8a52e7dd..6e414bd36981 100644
--- a/drivers/clk/meson/meson8b.h
+++ b/drivers/clk/meson/meson8b.h
@@ -69,7 +69,22 @@
* will remain defined here.
*/
-#define CLK_NR_CLKS 96
+#define CLKID_MPLL0_DIV 96
+#define CLKID_MPLL1_DIV 97
+#define CLKID_MPLL2_DIV 98
+#define CLKID_CPU_IN_SEL 99
+#define CLKID_CPU_DIV2 100
+#define CLKID_CPU_DIV3 101
+#define CLKID_CPU_SCALE_DIV 102
+#define CLKID_CPU_SCALE_OUT_SEL 103
+#define CLKID_MPLL_PREDIV 104
+#define CLKID_FCLK_DIV2_DIV 105
+#define CLKID_FCLK_DIV3_DIV 106
+#define CLKID_FCLK_DIV4_DIV 107
+#define CLKID_FCLK_DIV5_DIV 108
+#define CLKID_FCLK_DIV7_DIV 109
+
+#define CLK_NR_CLKS 110
/*
* include the CLKID and RESETID that have
diff --git a/drivers/clk/mvebu/armada-38x.c b/drivers/clk/mvebu/armada-38x.c
index 394aa6f03f01..9ff4ea63932d 100644
--- a/drivers/clk/mvebu/armada-38x.c
+++ b/drivers/clk/mvebu/armada-38x.c
@@ -46,11 +46,11 @@ static u32 __init armada_38x_get_tclk_freq(void __iomem *sar)
}
static const u32 armada_38x_cpu_frequencies[] __initconst = {
- 0, 0, 0, 0,
- 1066 * 1000 * 1000, 0, 0, 0,
+ 666 * 1000 * 1000, 0, 800 * 1000 * 1000, 0,
+ 1066 * 1000 * 1000, 0, 1200 * 1000 * 1000, 0,
1332 * 1000 * 1000, 0, 0, 0,
1600 * 1000 * 1000, 0, 0, 0,
- 1866 * 1000 * 1000,
+ 1866 * 1000 * 1000, 0, 0, 2000 * 1000 * 1000,
};
static u32 __init armada_38x_get_cpu_freq(void __iomem *sar)
@@ -76,11 +76,11 @@ static const struct coreclk_ratio armada_38x_coreclk_ratios[] __initconst = {
};
static const int armada_38x_cpu_l2_ratios[32][2] __initconst = {
- {0, 1}, {0, 1}, {0, 1}, {0, 1},
- {1, 2}, {0, 1}, {0, 1}, {0, 1},
- {1, 2}, {0, 1}, {0, 1}, {0, 1},
+ {1, 2}, {0, 1}, {1, 2}, {0, 1},
+ {1, 2}, {0, 1}, {1, 2}, {0, 1},
{1, 2}, {0, 1}, {0, 1}, {0, 1},
{1, 2}, {0, 1}, {0, 1}, {0, 1},
+ {1, 2}, {0, 1}, {0, 1}, {1, 2},
{0, 1}, {0, 1}, {0, 1}, {0, 1},
{0, 1}, {0, 1}, {0, 1}, {0, 1},
{0, 1}, {0, 1}, {0, 1}, {0, 1},
@@ -91,7 +91,7 @@ static const int armada_38x_cpu_ddr_ratios[32][2] __initconst = {
{1, 2}, {0, 1}, {0, 1}, {0, 1},
{1, 2}, {0, 1}, {0, 1}, {0, 1},
{1, 2}, {0, 1}, {0, 1}, {0, 1},
- {1, 2}, {0, 1}, {0, 1}, {0, 1},
+ {1, 2}, {0, 1}, {0, 1}, {7, 15},
{0, 1}, {0, 1}, {0, 1}, {0, 1},
{0, 1}, {0, 1}, {0, 1}, {0, 1},
{0, 1}, {0, 1}, {0, 1}, {0, 1},
diff --git a/drivers/clk/mvebu/cp110-system-controller.c b/drivers/clk/mvebu/cp110-system-controller.c
index ca9a0a536174..75bf7b8f282f 100644
--- a/drivers/clk/mvebu/cp110-system-controller.c
+++ b/drivers/clk/mvebu/cp110-system-controller.c
@@ -13,18 +13,17 @@
/*
* CP110 has 6 core clocks:
*
- * - APLL (1 Ghz)
- * - PPv2 core (1/3 APLL)
- * - EIP (1/2 APLL)
- * - Core (1/2 EIP)
- * - SDIO (2/5 APLL)
+ * - PLL0 (1 Ghz)
+ * - PPv2 core (1/3 PLL0)
+ * - x2 Core (1/2 PLL0)
+ * - Core (1/2 x2 Core)
+ * - SDIO (2/5 PLL0)
*
* - NAND clock, which is either:
* - Equal to SDIO clock
- * - 2/5 APLL
+ * - 2/5 PLL0
*
- * CP110 has 32 gatable clocks, for the various peripherals in the
- * IP. They have fairly complicated parent/child relationships.
+ * CP110 has 32 gatable clocks, for the various peripherals in the IP.
*/
#define pr_fmt(fmt) "cp110-system-controller: " fmt
@@ -53,9 +52,9 @@ enum {
#define CP110_CLK_NUM \
(CP110_MAX_CORE_CLOCKS + CP110_MAX_GATABLE_CLOCKS)
-#define CP110_CORE_APLL 0
+#define CP110_CORE_PLL0 0
#define CP110_CORE_PPV2 1
-#define CP110_CORE_EIP 2
+#define CP110_CORE_X2CORE 2
#define CP110_CORE_CORE 3
#define CP110_CORE_NAND 4
#define CP110_CORE_SDIO 5
@@ -237,7 +236,7 @@ static int cp110_syscon_common_probe(struct platform_device *pdev,
struct regmap *regmap;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
- const char *ppv2_name, *apll_name, *core_name, *eip_name, *nand_name,
+ const char *ppv2_name, *pll0_name, *core_name, *x2core_name, *nand_name,
*sdio_name;
struct clk_hw_onecell_data *cp110_clk_data;
struct clk_hw *hw, **cp110_clks;
@@ -263,20 +262,20 @@ static int cp110_syscon_common_probe(struct platform_device *pdev,
cp110_clks = cp110_clk_data->hws;
cp110_clk_data->num = CP110_CLK_NUM;
- /* Register the APLL which is the root of the hw tree */
- apll_name = cp110_unique_name(dev, syscon_node, "apll");
- hw = clk_hw_register_fixed_rate(NULL, apll_name, NULL, 0,
+ /* Register the PLL0 which is the root of the hw tree */
+ pll0_name = cp110_unique_name(dev, syscon_node, "pll0");
+ hw = clk_hw_register_fixed_rate(NULL, pll0_name, NULL, 0,
1000 * 1000 * 1000);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
- goto fail_apll;
+ goto fail_pll0;
}
- cp110_clks[CP110_CORE_APLL] = hw;
+ cp110_clks[CP110_CORE_PLL0] = hw;
- /* PPv2 is APLL/3 */
+ /* PPv2 is PLL0/3 */
ppv2_name = cp110_unique_name(dev, syscon_node, "ppv2-core");
- hw = clk_hw_register_fixed_factor(NULL, ppv2_name, apll_name, 0, 1, 3);
+ hw = clk_hw_register_fixed_factor(NULL, ppv2_name, pll0_name, 0, 1, 3);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto fail_ppv2;
@@ -284,30 +283,32 @@ static int cp110_syscon_common_probe(struct platform_device *pdev,
cp110_clks[CP110_CORE_PPV2] = hw;
- /* EIP clock is APLL/2 */
- eip_name = cp110_unique_name(dev, syscon_node, "eip");
- hw = clk_hw_register_fixed_factor(NULL, eip_name, apll_name, 0, 1, 2);
+ /* X2CORE clock is PLL0/2 */
+ x2core_name = cp110_unique_name(dev, syscon_node, "x2core");
+ hw = clk_hw_register_fixed_factor(NULL, x2core_name, pll0_name,
+ 0, 1, 2);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto fail_eip;
}
- cp110_clks[CP110_CORE_EIP] = hw;
+ cp110_clks[CP110_CORE_X2CORE] = hw;
- /* Core clock is EIP/2 */
+ /* Core clock is X2CORE/2 */
core_name = cp110_unique_name(dev, syscon_node, "core");
- hw = clk_hw_register_fixed_factor(NULL, core_name, eip_name, 0, 1, 2);
+ hw = clk_hw_register_fixed_factor(NULL, core_name, x2core_name,
+ 0, 1, 2);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto fail_core;
}
cp110_clks[CP110_CORE_CORE] = hw;
- /* NAND can be either APLL/2.5 or core clock */
+ /* NAND can be either PLL0/2.5 or core clock */
nand_name = cp110_unique_name(dev, syscon_node, "nand-core");
if (nand_clk_ctrl & NF_CLOCK_SEL_400_MASK)
hw = clk_hw_register_fixed_factor(NULL, nand_name,
- apll_name, 0, 2, 5);
+ pll0_name, 0, 2, 5);
else
hw = clk_hw_register_fixed_factor(NULL, nand_name,
core_name, 0, 1, 1);
@@ -318,10 +319,10 @@ static int cp110_syscon_common_probe(struct platform_device *pdev,
cp110_clks[CP110_CORE_NAND] = hw;
- /* SDIO clock is APLL/2.5 */
+ /* SDIO clock is PLL0/2.5 */
sdio_name = cp110_unique_name(dev, syscon_node, "sdio-core");
hw = clk_hw_register_fixed_factor(NULL, sdio_name,
- apll_name, 0, 2, 5);
+ pll0_name, 0, 2, 5);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto fail_sdio;
@@ -341,40 +342,23 @@ static int cp110_syscon_common_probe(struct platform_device *pdev,
continue;
switch (i) {
- case CP110_GATE_AUDIO:
- case CP110_GATE_COMM_UNIT:
- case CP110_GATE_EIP150:
- case CP110_GATE_EIP197:
- case CP110_GATE_SLOW_IO:
- parent = gate_name[CP110_GATE_MAIN];
- break;
- case CP110_GATE_MG:
- parent = gate_name[CP110_GATE_MG_CORE];
- break;
case CP110_GATE_NAND:
parent = nand_name;
break;
+ case CP110_GATE_MG:
+ case CP110_GATE_GOP_DP:
case CP110_GATE_PPV2:
parent = ppv2_name;
break;
case CP110_GATE_SDIO:
parent = sdio_name;
break;
- case CP110_GATE_GOP_DP:
- parent = gate_name[CP110_GATE_SDMMC_GOP];
- break;
- case CP110_GATE_XOR1:
- case CP110_GATE_XOR0:
- case CP110_GATE_PCIE_X1_0:
- case CP110_GATE_PCIE_X1_1:
+ case CP110_GATE_MAIN:
+ case CP110_GATE_PCIE_XOR:
case CP110_GATE_PCIE_X4:
- parent = gate_name[CP110_GATE_PCIE_XOR];
- break;
- case CP110_GATE_SATA:
- case CP110_GATE_USB3H0:
- case CP110_GATE_USB3H1:
- case CP110_GATE_USB3DEV:
- parent = gate_name[CP110_GATE_SATA_USB];
+ case CP110_GATE_EIP150:
+ case CP110_GATE_EIP197:
+ parent = x2core_name;
break;
default:
parent = core_name;
@@ -413,12 +397,12 @@ fail_sdio:
fail_nand:
clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_CORE]);
fail_core:
- clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_EIP]);
+ clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_X2CORE]);
fail_eip:
clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_PPV2]);
fail_ppv2:
- clk_hw_unregister_fixed_rate(cp110_clks[CP110_CORE_APLL]);
-fail_apll:
+ clk_hw_unregister_fixed_rate(cp110_clks[CP110_CORE_PLL0]);
+fail_pll0:
return ret;
}
diff --git a/drivers/clk/nxp/clk-lpc32xx.c b/drivers/clk/nxp/clk-lpc32xx.c
index f5d815f577e0..5eeecee17b69 100644
--- a/drivers/clk/nxp/clk-lpc32xx.c
+++ b/drivers/clk/nxp/clk-lpc32xx.c
@@ -67,6 +67,7 @@
#define LPC32XX_USB_CLK_STS 0xF8
static struct regmap_config lpc32xx_scb_regmap_config = {
+ .name = "scb",
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
diff --git a/drivers/clk/qcom/clk-regmap-divider.c b/drivers/clk/qcom/clk-regmap-divider.c
index 4e9b8c2c8980..1ee75a5e93f4 100644
--- a/drivers/clk/qcom/clk-regmap-divider.c
+++ b/drivers/clk/qcom/clk-regmap-divider.c
@@ -28,22 +28,14 @@ static long div_round_ro_rate(struct clk_hw *hw, unsigned long rate,
{
struct clk_regmap_div *divider = to_clk_regmap_div(hw);
struct clk_regmap *clkr = &divider->clkr;
- u32 div;
- struct clk_hw *hw_parent = clk_hw_get_parent(hw);
-
- regmap_read(clkr->regmap, divider->reg, &div);
- div >>= divider->shift;
- div &= BIT(divider->width) - 1;
- div += 1;
-
- if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
- if (!hw_parent)
- return -EINVAL;
+ u32 val;
- *prate = clk_hw_round_rate(hw_parent, rate * div);
- }
+ regmap_read(clkr->regmap, divider->reg, &val);
+ val >>= divider->shift;
+ val &= BIT(divider->width) - 1;
- return DIV_ROUND_UP_ULL((u64)*prate, div);
+ return divider_ro_round_rate(hw, rate, prate, NULL, divider->width,
+ CLK_DIVIDER_ROUND_CLOSEST, val);
}
static long div_round_rate(struct clk_hw *hw, unsigned long rate,
diff --git a/drivers/clk/qcom/clk-rpm.c b/drivers/clk/qcom/clk-rpm.c
index c60f61b10c7f..b94981447664 100644
--- a/drivers/clk/qcom/clk-rpm.c
+++ b/drivers/clk/qcom/clk-rpm.c
@@ -29,6 +29,7 @@
#define QCOM_RPM_MISC_CLK_TYPE 0x306b6c63
#define QCOM_RPM_SCALING_ENABLE_ID 0x2
+#define QCOM_RPM_XO_MODE_ON 0x2
#define DEFINE_CLK_RPM(_platform, _name, _active, r_id) \
static struct clk_rpm _platform##_##_active; \
@@ -56,6 +57,18 @@
}, \
}
+#define DEFINE_CLK_RPM_XO_BUFFER(_platform, _name, _active, offset) \
+ static struct clk_rpm _platform##_##_name = { \
+ .rpm_clk_id = QCOM_RPM_CXO_BUFFERS, \
+ .xo_offset = (offset), \
+ .hw.init = &(struct clk_init_data){ \
+ .ops = &clk_rpm_xo_ops, \
+ .name = #_name, \
+ .parent_names = (const char *[]){ "cxo_board" }, \
+ .num_parents = 1, \
+ }, \
+ }
+
#define DEFINE_CLK_RPM_FIXED(_platform, _name, _active, r_id, r) \
static struct clk_rpm _platform##_##_name = { \
.rpm_clk_id = (r_id), \
@@ -126,8 +139,11 @@
#define to_clk_rpm(_hw) container_of(_hw, struct clk_rpm, hw)
+struct rpm_cc;
+
struct clk_rpm {
const int rpm_clk_id;
+ const int xo_offset;
const bool active_only;
unsigned long rate;
bool enabled;
@@ -135,12 +151,15 @@ struct clk_rpm {
struct clk_rpm *peer;
struct clk_hw hw;
struct qcom_rpm *rpm;
+ struct rpm_cc *rpm_cc;
};
struct rpm_cc {
struct qcom_rpm *rpm;
struct clk_rpm **clks;
size_t num_clks;
+ u32 xo_buffer_value;
+ struct mutex xo_lock;
};
struct rpm_clk_desc {
@@ -159,7 +178,8 @@ static int clk_rpm_handoff(struct clk_rpm *r)
* The vendor tree simply reads the status for this
* RPM clock.
*/
- if (r->rpm_clk_id == QCOM_RPM_PLL_4)
+ if (r->rpm_clk_id == QCOM_RPM_PLL_4 ||
+ r->rpm_clk_id == QCOM_RPM_CXO_BUFFERS)
return 0;
ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE,
@@ -288,6 +308,46 @@ out:
mutex_unlock(&rpm_clk_lock);
}
+static int clk_rpm_xo_prepare(struct clk_hw *hw)
+{
+ struct clk_rpm *r = to_clk_rpm(hw);
+ struct rpm_cc *rcc = r->rpm_cc;
+ int ret, clk_id = r->rpm_clk_id;
+ u32 value;
+
+ mutex_lock(&rcc->xo_lock);
+
+ value = rcc->xo_buffer_value | (QCOM_RPM_XO_MODE_ON << r->xo_offset);
+ ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, clk_id, &value, 1);
+ if (!ret) {
+ r->enabled = true;
+ rcc->xo_buffer_value = value;
+ }
+
+ mutex_unlock(&rcc->xo_lock);
+
+ return ret;
+}
+
+static void clk_rpm_xo_unprepare(struct clk_hw *hw)
+{
+ struct clk_rpm *r = to_clk_rpm(hw);
+ struct rpm_cc *rcc = r->rpm_cc;
+ int ret, clk_id = r->rpm_clk_id;
+ u32 value;
+
+ mutex_lock(&rcc->xo_lock);
+
+ value = rcc->xo_buffer_value & ~(QCOM_RPM_XO_MODE_ON << r->xo_offset);
+ ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, clk_id, &value, 1);
+ if (!ret) {
+ r->enabled = false;
+ rcc->xo_buffer_value = value;
+ }
+
+ mutex_unlock(&rcc->xo_lock);
+}
+
static int clk_rpm_fixed_prepare(struct clk_hw *hw)
{
struct clk_rpm *r = to_clk_rpm(hw);
@@ -378,6 +438,11 @@ static unsigned long clk_rpm_recalc_rate(struct clk_hw *hw,
return r->rate;
}
+static const struct clk_ops clk_rpm_xo_ops = {
+ .prepare = clk_rpm_xo_prepare,
+ .unprepare = clk_rpm_xo_unprepare,
+};
+
static const struct clk_ops clk_rpm_fixed_ops = {
.prepare = clk_rpm_fixed_prepare,
.unprepare = clk_rpm_fixed_unprepare,
@@ -449,6 +514,11 @@ DEFINE_CLK_RPM(apq8064, mmfpb_clk, mmfpb_a_clk, QCOM_RPM_MMFPB_CLK);
DEFINE_CLK_RPM(apq8064, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK);
DEFINE_CLK_RPM(apq8064, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK);
DEFINE_CLK_RPM(apq8064, qdss_clk, qdss_a_clk, QCOM_RPM_QDSS_CLK);
+DEFINE_CLK_RPM_XO_BUFFER(apq8064, xo_d0_clk, xo_d0_a_clk, 0);
+DEFINE_CLK_RPM_XO_BUFFER(apq8064, xo_d1_clk, xo_d1_a_clk, 8);
+DEFINE_CLK_RPM_XO_BUFFER(apq8064, xo_a0_clk, xo_a0_a_clk, 16);
+DEFINE_CLK_RPM_XO_BUFFER(apq8064, xo_a1_clk, xo_a1_a_clk, 24);
+DEFINE_CLK_RPM_XO_BUFFER(apq8064, xo_a2_clk, xo_a2_a_clk, 28);
static struct clk_rpm *apq8064_clks[] = {
[RPM_APPS_FABRIC_CLK] = &apq8064_afab_clk,
@@ -469,6 +539,11 @@ static struct clk_rpm *apq8064_clks[] = {
[RPM_SFPB_A_CLK] = &apq8064_sfpb_a_clk,
[RPM_QDSS_CLK] = &apq8064_qdss_clk,
[RPM_QDSS_A_CLK] = &apq8064_qdss_a_clk,
+ [RPM_XO_D0] = &apq8064_xo_d0_clk,
+ [RPM_XO_D1] = &apq8064_xo_d1_clk,
+ [RPM_XO_A0] = &apq8064_xo_a0_clk,
+ [RPM_XO_A1] = &apq8064_xo_a1_clk,
+ [RPM_XO_A2] = &apq8064_xo_a2_clk,
};
static const struct rpm_clk_desc rpm_clk_apq8064 = {
@@ -526,12 +601,14 @@ static int rpm_clk_probe(struct platform_device *pdev)
rcc->clks = rpm_clks;
rcc->num_clks = num_clks;
+ mutex_init(&rcc->xo_lock);
for (i = 0; i < num_clks; i++) {
if (!rpm_clks[i])
continue;
rpm_clks[i]->rpm = rpm;
+ rpm_clks[i]->rpm_cc = rcc;
ret = clk_rpm_handoff(rpm_clks[i]);
if (ret)
diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c
index c26d9007bfc4..850c02a52248 100644
--- a/drivers/clk/qcom/clk-smd-rpm.c
+++ b/drivers/clk/qcom/clk-smd-rpm.c
@@ -686,7 +686,7 @@ static int rpm_smd_clk_probe(struct platform_device *pdev)
goto err;
}
- ret = of_clk_add_hw_provider(pdev->dev.of_node, qcom_smdrpm_clk_hw_get,
+ ret = devm_of_clk_add_hw_provider(&pdev->dev, qcom_smdrpm_clk_hw_get,
rcc);
if (ret)
goto err;
@@ -697,19 +697,12 @@ err:
return ret;
}
-static int rpm_smd_clk_remove(struct platform_device *pdev)
-{
- of_clk_del_provider(pdev->dev.of_node);
- return 0;
-}
-
static struct platform_driver rpm_smd_clk_driver = {
.driver = {
.name = "qcom-clk-smd-rpm",
.of_match_table = rpm_smd_clk_match_table,
},
.probe = rpm_smd_clk_probe,
- .remove = rpm_smd_clk_remove,
};
static int __init rpm_smd_clk_init(void)
diff --git a/drivers/clk/qcom/gcc-msm8996.c b/drivers/clk/qcom/gcc-msm8996.c
index 5d7451209206..3d6452932797 100644
--- a/drivers/clk/qcom/gcc-msm8996.c
+++ b/drivers/clk/qcom/gcc-msm8996.c
@@ -2895,7 +2895,7 @@ static struct clk_branch gcc_aggre0_snoc_axi_clk = {
.name = "gcc_aggre0_snoc_axi_clk",
.parent_names = (const char *[]){ "system_noc_clk_src" },
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
+ .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
.ops = &clk_branch2_ops,
},
},
@@ -2910,7 +2910,7 @@ static struct clk_branch gcc_aggre0_cnoc_ahb_clk = {
.name = "gcc_aggre0_cnoc_ahb_clk",
.parent_names = (const char *[]){ "config_noc_clk_src" },
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
+ .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
.ops = &clk_branch2_ops,
},
},
@@ -2925,7 +2925,7 @@ static struct clk_branch gcc_smmu_aggre0_axi_clk = {
.name = "gcc_smmu_aggre0_axi_clk",
.parent_names = (const char *[]){ "system_noc_clk_src" },
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
+ .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
.ops = &clk_branch2_ops,
},
},
@@ -2940,7 +2940,7 @@ static struct clk_branch gcc_smmu_aggre0_ahb_clk = {
.name = "gcc_smmu_aggre0_ahb_clk",
.parent_names = (const char *[]){ "config_noc_clk_src" },
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
+ .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
.ops = &clk_branch2_ops,
},
},
diff --git a/drivers/clk/renesas/Kconfig b/drivers/clk/renesas/Kconfig
index 43b5a89c4b28..ef76c861ec84 100644
--- a/drivers/clk/renesas/Kconfig
+++ b/drivers/clk/renesas/Kconfig
@@ -15,7 +15,9 @@ config CLK_RENESAS
select CLK_R8A7794 if ARCH_R8A7794
select CLK_R8A7795 if ARCH_R8A7795
select CLK_R8A7796 if ARCH_R8A7796
+ select CLK_R8A77965 if ARCH_R8A77965
select CLK_R8A77970 if ARCH_R8A77970
+ select CLK_R8A77980 if ARCH_R8A77980
select CLK_R8A77995 if ARCH_R8A77995
select CLK_SH73A0 if ARCH_SH73A0
@@ -24,12 +26,13 @@ if CLK_RENESAS
config CLK_RENESAS_LEGACY
bool "Legacy DT clock support"
depends on CLK_R8A7790 || CLK_R8A7791 || CLK_R8A7792 || CLK_R8A7794
- default y
help
Enable backward compatibility with old device trees describing a
hierarchical representation of the various CPG and MSTP clocks.
Say Y if you want your kernel to work with old DTBs.
+ It is safe to say N if you use the DTS that is supplied with the
+ current kernel source tree.
# SoC
config CLK_EMEV2
@@ -96,10 +99,18 @@ config CLK_R8A7796
bool "R-Car M3-W clock support" if COMPILE_TEST
select CLK_RCAR_GEN3_CPG
+config CLK_R8A77965
+ bool "R-Car M3-N clock support" if COMPILE_TEST
+ select CLK_RCAR_GEN3_CPG
+
config CLK_R8A77970
bool "R-Car V3M clock support" if COMPILE_TEST
select CLK_RCAR_GEN3_CPG
+config CLK_R8A77980
+ bool "R-Car V3H clock support" if COMPILE_TEST
+ select CLK_RCAR_GEN3_CPG
+
config CLK_R8A77995
bool "R-Car D3 clock support" if COMPILE_TEST
select CLK_RCAR_GEN3_CPG
diff --git a/drivers/clk/renesas/Makefile b/drivers/clk/renesas/Makefile
index 34c4e0b37afa..6c0f19636e3e 100644
--- a/drivers/clk/renesas/Makefile
+++ b/drivers/clk/renesas/Makefile
@@ -14,7 +14,9 @@ obj-$(CONFIG_CLK_R8A7792) += r8a7792-cpg-mssr.o
obj-$(CONFIG_CLK_R8A7794) += r8a7794-cpg-mssr.o
obj-$(CONFIG_CLK_R8A7795) += r8a7795-cpg-mssr.o
obj-$(CONFIG_CLK_R8A7796) += r8a7796-cpg-mssr.o
+obj-$(CONFIG_CLK_R8A77965) += r8a77965-cpg-mssr.o
obj-$(CONFIG_CLK_R8A77970) += r8a77970-cpg-mssr.o
+obj-$(CONFIG_CLK_R8A77980) += r8a77980-cpg-mssr.o
obj-$(CONFIG_CLK_R8A77995) += r8a77995-cpg-mssr.o
obj-$(CONFIG_CLK_SH73A0) += clk-sh73a0.o
diff --git a/drivers/clk/renesas/clk-div6.c b/drivers/clk/renesas/clk-div6.c
index 151336d2ba59..9febbf42c3df 100644
--- a/drivers/clk/renesas/clk-div6.c
+++ b/drivers/clk/renesas/clk-div6.c
@@ -53,9 +53,9 @@ static int cpg_div6_clock_enable(struct clk_hw *hw)
struct div6_clock *clock = to_div6_clock(hw);
u32 val;
- val = (clk_readl(clock->reg) & ~(CPG_DIV6_DIV_MASK | CPG_DIV6_CKSTP))
+ val = (readl(clock->reg) & ~(CPG_DIV6_DIV_MASK | CPG_DIV6_CKSTP))
| CPG_DIV6_DIV(clock->div - 1);
- clk_writel(val, clock->reg);
+ writel(val, clock->reg);
return 0;
}
@@ -65,7 +65,7 @@ static void cpg_div6_clock_disable(struct clk_hw *hw)
struct div6_clock *clock = to_div6_clock(hw);
u32 val;
- val = clk_readl(clock->reg);
+ val = readl(clock->reg);
val |= CPG_DIV6_CKSTP;
/*
* DIV6 clocks require the divisor field to be non-zero when stopping
@@ -75,14 +75,14 @@ static void cpg_div6_clock_disable(struct clk_hw *hw)
*/
if (!(val & CPG_DIV6_DIV_MASK))
val |= CPG_DIV6_DIV_MASK;
- clk_writel(val, clock->reg);
+ writel(val, clock->reg);
}
static int cpg_div6_clock_is_enabled(struct clk_hw *hw)
{
struct div6_clock *clock = to_div6_clock(hw);
- return !(clk_readl(clock->reg) & CPG_DIV6_CKSTP);
+ return !(readl(clock->reg) & CPG_DIV6_CKSTP);
}
static unsigned long cpg_div6_clock_recalc_rate(struct clk_hw *hw,
@@ -122,10 +122,10 @@ static int cpg_div6_clock_set_rate(struct clk_hw *hw, unsigned long rate,
clock->div = div;
- val = clk_readl(clock->reg) & ~CPG_DIV6_DIV_MASK;
+ val = readl(clock->reg) & ~CPG_DIV6_DIV_MASK;
/* Only program the new divisor if the clock isn't stopped. */
if (!(val & CPG_DIV6_CKSTP))
- clk_writel(val | CPG_DIV6_DIV(clock->div - 1), clock->reg);
+ writel(val | CPG_DIV6_DIV(clock->div - 1), clock->reg);
return 0;
}
@@ -139,7 +139,7 @@ static u8 cpg_div6_clock_get_parent(struct clk_hw *hw)
if (clock->src_width == 0)
return 0;
- hw_index = (clk_readl(clock->reg) >> clock->src_shift) &
+ hw_index = (readl(clock->reg) >> clock->src_shift) &
(BIT(clock->src_width) - 1);
for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
if (clock->parents[i] == hw_index)
@@ -163,8 +163,8 @@ static int cpg_div6_clock_set_parent(struct clk_hw *hw, u8 index)
mask = ~((BIT(clock->src_width) - 1) << clock->src_shift);
hw_index = clock->parents[index];
- clk_writel((clk_readl(clock->reg) & mask) |
- (hw_index << clock->src_shift), clock->reg);
+ writel((readl(clock->reg) & mask) | (hw_index << clock->src_shift),
+ clock->reg);
return 0;
}
@@ -241,7 +241,7 @@ struct clk * __init cpg_div6_register(const char *name,
* Read the divisor. Disabling the clock overwrites the divisor, so we
* need to cache its value for the enable operation.
*/
- clock->div = (clk_readl(clock->reg) & CPG_DIV6_DIV_MASK) + 1;
+ clock->div = (readl(clock->reg) & CPG_DIV6_DIV_MASK) + 1;
switch (num_parents) {
case 1:
diff --git a/drivers/clk/renesas/clk-mstp.c b/drivers/clk/renesas/clk-mstp.c
index 858c24d4da8f..e82adcb16a52 100644
--- a/drivers/clk/renesas/clk-mstp.c
+++ b/drivers/clk/renesas/clk-mstp.c
@@ -64,13 +64,13 @@ struct mstp_clock {
static inline u32 cpg_mstp_read(struct mstp_clock_group *group,
u32 __iomem *reg)
{
- return group->width_8bit ? readb(reg) : clk_readl(reg);
+ return group->width_8bit ? readb(reg) : readl(reg);
}
static inline void cpg_mstp_write(struct mstp_clock_group *group, u32 val,
u32 __iomem *reg)
{
- group->width_8bit ? writeb(val, reg) : clk_writel(val, reg);
+ group->width_8bit ? writeb(val, reg) : writel(val, reg);
}
static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable)
diff --git a/drivers/clk/renesas/clk-r8a73a4.c b/drivers/clk/renesas/clk-r8a73a4.c
index 28d204bb659e..7b903ce4c901 100644
--- a/drivers/clk/renesas/clk-r8a73a4.c
+++ b/drivers/clk/renesas/clk-r8a73a4.c
@@ -71,7 +71,7 @@ r8a73a4_cpg_register_clock(struct device_node *np, struct r8a73a4_cpg *cpg,
if (!strcmp(name, "main")) {
- u32 ckscr = clk_readl(cpg->reg + CPG_CKSCR);
+ u32 ckscr = readl(cpg->reg + CPG_CKSCR);
switch ((ckscr >> 28) & 3) {
case 0: /* extal1 */
@@ -95,14 +95,14 @@ r8a73a4_cpg_register_clock(struct device_node *np, struct r8a73a4_cpg *cpg,
* clock implementation and we currently have no need to change
* the multiplier value.
*/
- u32 value = clk_readl(cpg->reg + CPG_PLL0CR);
+ u32 value = readl(cpg->reg + CPG_PLL0CR);
parent_name = "main";
mult = ((value >> 24) & 0x7f) + 1;
if (value & BIT(20))
div = 2;
} else if (!strcmp(name, "pll1")) {
- u32 value = clk_readl(cpg->reg + CPG_PLL1CR);
+ u32 value = readl(cpg->reg + CPG_PLL1CR);
parent_name = "main";
/* XXX: enable bit? */
@@ -125,7 +125,7 @@ r8a73a4_cpg_register_clock(struct device_node *np, struct r8a73a4_cpg *cpg,
default:
return ERR_PTR(-EINVAL);
}
- value = clk_readl(cpg->reg + cr);
+ value = readl(cpg->reg + cr);
switch ((value >> 5) & 7) {
case 0:
parent_name = "main";
@@ -161,8 +161,7 @@ r8a73a4_cpg_register_clock(struct device_node *np, struct r8a73a4_cpg *cpg,
shift = 0;
}
div *= 32;
- mult = 0x20 - ((clk_readl(cpg->reg + CPG_FRQCRC) >> shift)
- & 0x1f);
+ mult = 0x20 - ((readl(cpg->reg + CPG_FRQCRC) >> shift) & 0x1f);
} else {
struct div4_clk *c;
diff --git a/drivers/clk/renesas/clk-r8a7740.c b/drivers/clk/renesas/clk-r8a7740.c
index 2f7ce6696b6c..d074f8e982d0 100644
--- a/drivers/clk/renesas/clk-r8a7740.c
+++ b/drivers/clk/renesas/clk-r8a7740.c
@@ -98,20 +98,20 @@ r8a7740_cpg_register_clock(struct device_node *np, struct r8a7740_cpg *cpg,
* clock implementation and we currently have no need to change
* the multiplier value.
*/
- u32 value = clk_readl(cpg->reg + CPG_FRQCRC);
+ u32 value = readl(cpg->reg + CPG_FRQCRC);
parent_name = "system";
mult = ((value >> 24) & 0x7f) + 1;
} else if (!strcmp(name, "pllc1")) {
- u32 value = clk_readl(cpg->reg + CPG_FRQCRA);
+ u32 value = readl(cpg->reg + CPG_FRQCRA);
parent_name = "system";
mult = ((value >> 24) & 0x7f) + 1;
div = 2;
} else if (!strcmp(name, "pllc2")) {
- u32 value = clk_readl(cpg->reg + CPG_PLLC2CR);
+ u32 value = readl(cpg->reg + CPG_PLLC2CR);
parent_name = "system";
mult = ((value >> 24) & 0x3f) + 1;
} else if (!strcmp(name, "usb24s")) {
- u32 value = clk_readl(cpg->reg + CPG_USBCKCR);
+ u32 value = readl(cpg->reg + CPG_USBCKCR);
if (value & BIT(7))
/* extal2 */
parent_name = of_clk_get_parent_name(np, 1);
diff --git a/drivers/clk/renesas/clk-rcar-gen2.c b/drivers/clk/renesas/clk-rcar-gen2.c
index d14cbe1ca29a..ee32a022e6da 100644
--- a/drivers/clk/renesas/clk-rcar-gen2.c
+++ b/drivers/clk/renesas/clk-rcar-gen2.c
@@ -62,8 +62,7 @@ static unsigned long cpg_z_clk_recalc_rate(struct clk_hw *hw,
unsigned int mult;
unsigned int val;
- val = (clk_readl(zclk->reg) & CPG_FRQCRC_ZFC_MASK)
- >> CPG_FRQCRC_ZFC_SHIFT;
+ val = (readl(zclk->reg) & CPG_FRQCRC_ZFC_MASK) >> CPG_FRQCRC_ZFC_SHIFT;
mult = 32 - val;
return div_u64((u64)parent_rate * mult, 32);
@@ -95,21 +94,21 @@ static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate,
mult = div_u64((u64)rate * 32, parent_rate);
mult = clamp(mult, 1U, 32U);
- if (clk_readl(zclk->kick_reg) & CPG_FRQCRB_KICK)
+ if (readl(zclk->kick_reg) & CPG_FRQCRB_KICK)
return -EBUSY;
- val = clk_readl(zclk->reg);
+ val = readl(zclk->reg);
val &= ~CPG_FRQCRC_ZFC_MASK;
val |= (32 - mult) << CPG_FRQCRC_ZFC_SHIFT;
- clk_writel(val, zclk->reg);
+ writel(val, zclk->reg);
/*
* Set KICK bit in FRQCRB to update hardware setting and wait for
* clock change completion.
*/
- kick = clk_readl(zclk->kick_reg);
+ kick = readl(zclk->kick_reg);
kick |= CPG_FRQCRB_KICK;
- clk_writel(kick, zclk->kick_reg);
+ writel(kick, zclk->kick_reg);
/*
* Note: There is no HW information about the worst case latency.
@@ -121,7 +120,7 @@ static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate,
* "super" safe value.
*/
for (i = 1000; i; i--) {
- if (!(clk_readl(zclk->kick_reg) & CPG_FRQCRB_KICK))
+ if (!(readl(zclk->kick_reg) & CPG_FRQCRB_KICK))
return 0;
cpu_relax();
@@ -332,7 +331,7 @@ rcar_gen2_cpg_register_clock(struct device_node *np, struct rcar_gen2_cpg *cpg,
mult = config->pll0_mult;
div = 3;
} else {
- u32 value = clk_readl(cpg->reg + CPG_PLL0CR);
+ u32 value = readl(cpg->reg + CPG_PLL0CR);
mult = ((value >> 24) & ((1 << 7) - 1)) + 1;
}
parent_name = "main";
diff --git a/drivers/clk/renesas/clk-rz.c b/drivers/clk/renesas/clk-rz.c
index 127c58135c8f..67dd712aa723 100644
--- a/drivers/clk/renesas/clk-rz.c
+++ b/drivers/clk/renesas/clk-rz.c
@@ -75,9 +75,9 @@ rz_cpg_register_clock(struct device_node *np, struct rz_cpg *cpg, const char *na
* let them run at fixed current speed and implement the details later.
*/
if (strcmp(name, "i") == 0)
- val = (clk_readl(cpg->reg + CPG_FRQCR) >> 8) & 3;
+ val = (readl(cpg->reg + CPG_FRQCR) >> 8) & 3;
else if (strcmp(name, "g") == 0)
- val = clk_readl(cpg->reg + CPG_FRQCR2) & 3;
+ val = readl(cpg->reg + CPG_FRQCR2) & 3;
else
return ERR_PTR(-EINVAL);
diff --git a/drivers/clk/renesas/clk-sh73a0.c b/drivers/clk/renesas/clk-sh73a0.c
index eea38f6ea77e..bab33610eb6c 100644
--- a/drivers/clk/renesas/clk-sh73a0.c
+++ b/drivers/clk/renesas/clk-sh73a0.c
@@ -46,7 +46,7 @@ struct div4_clk {
unsigned int shift;
};
-static struct div4_clk div4_clks[] = {
+static const struct div4_clk div4_clks[] = {
{ "zg", "pll0", CPG_FRQCRA, 16 },
{ "m3", "pll1", CPG_FRQCRA, 12 },
{ "b", "pll1", CPG_FRQCRA, 8 },
@@ -79,13 +79,13 @@ sh73a0_cpg_register_clock(struct device_node *np, struct sh73a0_cpg *cpg,
{
const struct clk_div_table *table = NULL;
unsigned int shift, reg, width;
- const char *parent_name;
+ const char *parent_name = NULL;
unsigned int mult = 1;
unsigned int div = 1;
if (!strcmp(name, "main")) {
/* extal1, extal1_div2, extal2, extal2_div2 */
- u32 parent_idx = (clk_readl(cpg->reg + CPG_CKSCR) >> 28) & 3;
+ u32 parent_idx = (readl(cpg->reg + CPG_CKSCR) >> 28) & 3;
parent_name = of_clk_get_parent_name(np, parent_idx >> 1);
div = (parent_idx & 1) + 1;
@@ -110,11 +110,11 @@ sh73a0_cpg_register_clock(struct device_node *np, struct sh73a0_cpg *cpg,
default:
return ERR_PTR(-EINVAL);
}
- if (clk_readl(cpg->reg + CPG_PLLECR) & BIT(enable_bit)) {
- mult = ((clk_readl(enable_reg) >> 24) & 0x3f) + 1;
+ if (readl(cpg->reg + CPG_PLLECR) & BIT(enable_bit)) {
+ mult = ((readl(enable_reg) >> 24) & 0x3f) + 1;
/* handle CFG bit for PLL1 and PLL2 */
if (enable_bit == 1 || enable_bit == 2)
- if (clk_readl(enable_reg) & BIT(20))
+ if (readl(enable_reg) & BIT(20))
mult *= 2;
}
} else if (!strcmp(name, "dsi0phy") || !strcmp(name, "dsi1phy")) {
@@ -135,7 +135,7 @@ sh73a0_cpg_register_clock(struct device_node *np, struct sh73a0_cpg *cpg,
shift = 24;
width = 5;
} else {
- struct div4_clk *c;
+ const struct div4_clk *c;
for (c = div4_clks; c->name; c++) {
if (!strcmp(name, c->name)) {
@@ -193,9 +193,9 @@ static void __init sh73a0_cpg_clocks_init(struct device_node *np)
return;
/* Set SDHI clocks to a known state */
- clk_writel(0x108, cpg->reg + CPG_SD0CKCR);
- clk_writel(0x108, cpg->reg + CPG_SD1CKCR);
- clk_writel(0x108, cpg->reg + CPG_SD2CKCR);
+ writel(0x108, cpg->reg + CPG_SD0CKCR);
+ writel(0x108, cpg->reg + CPG_SD1CKCR);
+ writel(0x108, cpg->reg + CPG_SD2CKCR);
for (i = 0; i < num_clks; ++i) {
const char *name;
diff --git a/drivers/clk/renesas/r8a7743-cpg-mssr.c b/drivers/clk/renesas/r8a7743-cpg-mssr.c
index 6dc0b3082aa6..d3c8b1e2969f 100644
--- a/drivers/clk/renesas/r8a7743-cpg-mssr.c
+++ b/drivers/clk/renesas/r8a7743-cpg-mssr.c
@@ -117,6 +117,7 @@ static const struct mssr_mod_clk r8a7743_mod_clks[] __initconst = {
DEF_MOD("cmt1", 329, R8A7743_CLK_R),
DEF_MOD("usbhs-dmac0", 330, R8A7743_CLK_HP),
DEF_MOD("usbhs-dmac1", 331, R8A7743_CLK_HP),
+ DEF_MOD("rwdt", 402, R8A7743_CLK_R),
DEF_MOD("irqc", 407, R8A7743_CLK_CP),
DEF_MOD("intc-sys", 408, R8A7743_CLK_ZS),
DEF_MOD("audio-dmac1", 501, R8A7743_CLK_HP),
@@ -195,6 +196,7 @@ static const struct mssr_mod_clk r8a7743_mod_clks[] __initconst = {
};
static const unsigned int r8a7743_crit_mod_clks[] __initconst = {
+ MOD_CLK_ID(402), /* RWDT */
MOD_CLK_ID(408), /* INTC-SYS (GIC) */
};
diff --git a/drivers/clk/renesas/r8a7745-cpg-mssr.c b/drivers/clk/renesas/r8a7745-cpg-mssr.c
index 2859504cc866..87f5a3619e4f 100644
--- a/drivers/clk/renesas/r8a7745-cpg-mssr.c
+++ b/drivers/clk/renesas/r8a7745-cpg-mssr.c
@@ -114,6 +114,7 @@ static const struct mssr_mod_clk r8a7745_mod_clks[] __initconst = {
DEF_MOD("cmt1", 329, R8A7745_CLK_R),
DEF_MOD("usbhs-dmac0", 330, R8A7745_CLK_HP),
DEF_MOD("usbhs-dmac1", 331, R8A7745_CLK_HP),
+ DEF_MOD("rwdt", 402, R8A7745_CLK_R),
DEF_MOD("irqc", 407, R8A7745_CLK_CP),
DEF_MOD("intc-sys", 408, R8A7745_CLK_ZS),
DEF_MOD("audio-dmac0", 502, R8A7745_CLK_HP),
@@ -180,6 +181,7 @@ static const struct mssr_mod_clk r8a7745_mod_clks[] __initconst = {
};
static const unsigned int r8a7745_crit_mod_clks[] __initconst = {
+ MOD_CLK_ID(402), /* RWDT */
MOD_CLK_ID(408), /* INTC-SYS (GIC) */
};
diff --git a/drivers/clk/renesas/r8a7790-cpg-mssr.c b/drivers/clk/renesas/r8a7790-cpg-mssr.c
index 46bb55bb223d..f936cb74b681 100644
--- a/drivers/clk/renesas/r8a7790-cpg-mssr.c
+++ b/drivers/clk/renesas/r8a7790-cpg-mssr.c
@@ -140,6 +140,7 @@ static const struct mssr_mod_clk r8a7790_mod_clks[] __initconst = {
DEF_MOD("cmt1", 329, R8A7790_CLK_R),
DEF_MOD("usbhs-dmac0", 330, R8A7790_CLK_HP),
DEF_MOD("usbhs-dmac1", 331, R8A7790_CLK_HP),
+ DEF_MOD("rwdt", 402, R8A7790_CLK_R),
DEF_MOD("irqc", 407, R8A7790_CLK_CP),
DEF_MOD("intc-sys", 408, R8A7790_CLK_ZS),
DEF_MOD("audio-dmac1", 501, R8A7790_CLK_HP),
@@ -211,6 +212,7 @@ static const struct mssr_mod_clk r8a7790_mod_clks[] __initconst = {
};
static const unsigned int r8a7790_crit_mod_clks[] __initconst = {
+ MOD_CLK_ID(402), /* RWDT */
MOD_CLK_ID(408), /* INTC-SYS (GIC) */
};
diff --git a/drivers/clk/renesas/r8a7791-cpg-mssr.c b/drivers/clk/renesas/r8a7791-cpg-mssr.c
index c0b51f9bb278..820b220b09cc 100644
--- a/drivers/clk/renesas/r8a7791-cpg-mssr.c
+++ b/drivers/clk/renesas/r8a7791-cpg-mssr.c
@@ -128,6 +128,7 @@ static const struct mssr_mod_clk r8a7791_mod_clks[] __initconst = {
DEF_MOD("cmt1", 329, R8A7791_CLK_R),
DEF_MOD("usbhs-dmac0", 330, R8A7791_CLK_HP),
DEF_MOD("usbhs-dmac1", 331, R8A7791_CLK_HP),
+ DEF_MOD("rwdt", 402, R8A7791_CLK_R),
DEF_MOD("irqc", 407, R8A7791_CLK_CP),
DEF_MOD("intc-sys", 408, R8A7791_CLK_ZS),
DEF_MOD("audio-dmac1", 501, R8A7791_CLK_HP),
@@ -209,6 +210,7 @@ static const struct mssr_mod_clk r8a7791_mod_clks[] __initconst = {
};
static const unsigned int r8a7791_crit_mod_clks[] __initconst = {
+ MOD_CLK_ID(402), /* RWDT */
MOD_CLK_ID(408), /* INTC-SYS (GIC) */
};
diff --git a/drivers/clk/renesas/r8a7792-cpg-mssr.c b/drivers/clk/renesas/r8a7792-cpg-mssr.c
index 7f85bbf20bf7..609a54080496 100644
--- a/drivers/clk/renesas/r8a7792-cpg-mssr.c
+++ b/drivers/clk/renesas/r8a7792-cpg-mssr.c
@@ -98,6 +98,7 @@ static const struct mssr_mod_clk r8a7792_mod_clks[] __initconst = {
DEF_MOD("tpu0", 304, R8A7792_CLK_CP),
DEF_MOD("sdhi0", 314, R8A7792_CLK_SD),
DEF_MOD("cmt1", 329, R8A7792_CLK_R),
+ DEF_MOD("rwdt", 402, R8A7792_CLK_R),
DEF_MOD("irqc", 407, R8A7792_CLK_CP),
DEF_MOD("intc-sys", 408, R8A7792_CLK_ZS),
DEF_MOD("audio-dmac0", 502, R8A7792_CLK_HP),
@@ -154,6 +155,7 @@ static const struct mssr_mod_clk r8a7792_mod_clks[] __initconst = {
};
static const unsigned int r8a7792_crit_mod_clks[] __initconst = {
+ MOD_CLK_ID(402), /* RWDT */
MOD_CLK_ID(408), /* INTC-SYS (GIC) */
};
diff --git a/drivers/clk/renesas/r8a7794-cpg-mssr.c b/drivers/clk/renesas/r8a7794-cpg-mssr.c
index ec091a42da54..2a40bbeaeeaf 100644
--- a/drivers/clk/renesas/r8a7794-cpg-mssr.c
+++ b/drivers/clk/renesas/r8a7794-cpg-mssr.c
@@ -121,6 +121,7 @@ static const struct mssr_mod_clk r8a7794_mod_clks[] __initconst = {
DEF_MOD("cmt1", 329, R8A7794_CLK_R),
DEF_MOD("usbhs-dmac0", 330, R8A7794_CLK_HP),
DEF_MOD("usbhs-dmac1", 331, R8A7794_CLK_HP),
+ DEF_MOD("rwdt", 402, R8A7794_CLK_R),
DEF_MOD("irqc", 407, R8A7794_CLK_CP),
DEF_MOD("intc-sys", 408, R8A7794_CLK_ZS),
DEF_MOD("audio-dmac0", 502, R8A7794_CLK_HP),
@@ -190,6 +191,7 @@ static const struct mssr_mod_clk r8a7794_mod_clks[] __initconst = {
};
static const unsigned int r8a7794_crit_mod_clks[] __initconst = {
+ MOD_CLK_ID(402), /* RWDT */
MOD_CLK_ID(408), /* INTC-SYS (GIC) */
};
diff --git a/drivers/clk/renesas/r8a7795-cpg-mssr.c b/drivers/clk/renesas/r8a7795-cpg-mssr.c
index b1d9f48eae9e..775b0ceaa337 100644
--- a/drivers/clk/renesas/r8a7795-cpg-mssr.c
+++ b/drivers/clk/renesas/r8a7795-cpg-mssr.c
@@ -74,6 +74,8 @@ static struct cpg_core_clk r8a7795_core_clks[] __initdata = {
DEF_FIXED(".sdsrc", CLK_SDSRC, CLK_PLL1_DIV2, 2, 1),
/* Core Clock Outputs */
+ DEF_BASE("z", R8A7795_CLK_Z, CLK_TYPE_GEN3_Z, CLK_PLL0),
+ DEF_BASE("z2", R8A7795_CLK_Z2, CLK_TYPE_GEN3_Z2, CLK_PLL2),
DEF_FIXED("ztr", R8A7795_CLK_ZTR, CLK_PLL1_DIV2, 6, 1),
DEF_FIXED("ztrd2", R8A7795_CLK_ZTRD2, CLK_PLL1_DIV2, 12, 1),
DEF_FIXED("zt", R8A7795_CLK_ZT, CLK_PLL1_DIV2, 4, 1),
diff --git a/drivers/clk/renesas/r8a7796-cpg-mssr.c b/drivers/clk/renesas/r8a7796-cpg-mssr.c
index 41e29734126b..dfb267a92f2a 100644
--- a/drivers/clk/renesas/r8a7796-cpg-mssr.c
+++ b/drivers/clk/renesas/r8a7796-cpg-mssr.c
@@ -74,6 +74,8 @@ static const struct cpg_core_clk r8a7796_core_clks[] __initconst = {
DEF_FIXED(".sdsrc", CLK_SDSRC, CLK_PLL1_DIV2, 2, 1),
/* Core Clock Outputs */
+ DEF_BASE("z", R8A7796_CLK_Z, CLK_TYPE_GEN3_Z, CLK_PLL0),
+ DEF_BASE("z2", R8A7796_CLK_Z2, CLK_TYPE_GEN3_Z2, CLK_PLL2),
DEF_FIXED("ztr", R8A7796_CLK_ZTR, CLK_PLL1_DIV2, 6, 1),
DEF_FIXED("ztrd2", R8A7796_CLK_ZTRD2, CLK_PLL1_DIV2, 12, 1),
DEF_FIXED("zt", R8A7796_CLK_ZT, CLK_PLL1_DIV2, 4, 1),
diff --git a/drivers/clk/renesas/r8a77965-cpg-mssr.c b/drivers/clk/renesas/r8a77965-cpg-mssr.c
new file mode 100644
index 000000000000..b1acfb60351c
--- /dev/null
+++ b/drivers/clk/renesas/r8a77965-cpg-mssr.c
@@ -0,0 +1,334 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * r8a77965 Clock Pulse Generator / Module Standby and Software Reset
+ *
+ * Copyright (C) 2018 Jacopo Mondi <jacopo+renesas@jmondi.org>
+ *
+ * Based on r8a7795-cpg-mssr.c
+ *
+ * Copyright (C) 2015 Glider bvba
+ * Copyright (C) 2015 Renesas Electronics Corp.
+ */
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/soc/renesas/rcar-rst.h>
+
+#include <dt-bindings/clock/r8a77965-cpg-mssr.h>
+
+#include "renesas-cpg-mssr.h"
+#include "rcar-gen3-cpg.h"
+
+enum clk_ids {
+ /* Core Clock Outputs exported to DT */
+ LAST_DT_CORE_CLK = R8A77965_CLK_OSC,
+
+ /* External Input Clocks */
+ CLK_EXTAL,
+ CLK_EXTALR,
+
+ /* Internal Core Clocks */
+ CLK_MAIN,
+ CLK_PLL0,
+ CLK_PLL1,
+ CLK_PLL3,
+ CLK_PLL4,
+ CLK_PLL1_DIV2,
+ CLK_PLL1_DIV4,
+ CLK_S0,
+ CLK_S1,
+ CLK_S2,
+ CLK_S3,
+ CLK_SDSRC,
+ CLK_SSPSRC,
+ CLK_RINT,
+
+ /* Module Clocks */
+ MOD_CLK_BASE
+};
+
+static const struct cpg_core_clk r8a77965_core_clks[] __initconst = {
+ /* External Clock Inputs */
+ DEF_INPUT("extal", CLK_EXTAL),
+ DEF_INPUT("extalr", CLK_EXTALR),
+
+ /* Internal Core Clocks */
+ DEF_BASE(".main", CLK_MAIN, CLK_TYPE_GEN3_MAIN, CLK_EXTAL),
+ DEF_BASE(".pll0", CLK_PLL0, CLK_TYPE_GEN3_PLL0, CLK_MAIN),
+ DEF_BASE(".pll1", CLK_PLL1, CLK_TYPE_GEN3_PLL1, CLK_MAIN),
+ DEF_BASE(".pll3", CLK_PLL3, CLK_TYPE_GEN3_PLL3, CLK_MAIN),
+ DEF_BASE(".pll4", CLK_PLL4, CLK_TYPE_GEN3_PLL4, CLK_MAIN),
+
+ DEF_FIXED(".pll1_div2", CLK_PLL1_DIV2, CLK_PLL1, 2, 1),
+ DEF_FIXED(".pll1_div4", CLK_PLL1_DIV4, CLK_PLL1_DIV2, 2, 1),
+ DEF_FIXED(".s0", CLK_S0, CLK_PLL1_DIV2, 2, 1),
+ DEF_FIXED(".s1", CLK_S1, CLK_PLL1_DIV2, 3, 1),
+ DEF_FIXED(".s2", CLK_S2, CLK_PLL1_DIV2, 4, 1),
+ DEF_FIXED(".s3", CLK_S3, CLK_PLL1_DIV2, 6, 1),
+ DEF_FIXED(".sdsrc", CLK_SDSRC, CLK_PLL1_DIV2, 2, 1),
+
+ /* Core Clock Outputs */
+ DEF_BASE("z", R8A77965_CLK_Z, CLK_TYPE_GEN3_Z, CLK_PLL0),
+ DEF_FIXED("ztr", R8A77965_CLK_ZTR, CLK_PLL1_DIV2, 6, 1),
+ DEF_FIXED("ztrd2", R8A77965_CLK_ZTRD2, CLK_PLL1_DIV2, 12, 1),
+ DEF_FIXED("zt", R8A77965_CLK_ZT, CLK_PLL1_DIV2, 4, 1),
+ DEF_FIXED("zx", R8A77965_CLK_ZX, CLK_PLL1_DIV2, 2, 1),
+ DEF_FIXED("s0d1", R8A77965_CLK_S0D1, CLK_S0, 1, 1),
+ DEF_FIXED("s0d2", R8A77965_CLK_S0D2, CLK_S0, 2, 1),
+ DEF_FIXED("s0d3", R8A77965_CLK_S0D3, CLK_S0, 3, 1),
+ DEF_FIXED("s0d4", R8A77965_CLK_S0D4, CLK_S0, 4, 1),
+ DEF_FIXED("s0d6", R8A77965_CLK_S0D6, CLK_S0, 6, 1),
+ DEF_FIXED("s0d8", R8A77965_CLK_S0D8, CLK_S0, 8, 1),
+ DEF_FIXED("s0d12", R8A77965_CLK_S0D12, CLK_S0, 12, 1),
+ DEF_FIXED("s1d1", R8A77965_CLK_S1D1, CLK_S1, 1, 1),
+ DEF_FIXED("s1d2", R8A77965_CLK_S1D2, CLK_S1, 2, 1),
+ DEF_FIXED("s1d4", R8A77965_CLK_S1D4, CLK_S1, 4, 1),
+ DEF_FIXED("s2d1", R8A77965_CLK_S2D1, CLK_S2, 1, 1),
+ DEF_FIXED("s2d2", R8A77965_CLK_S2D2, CLK_S2, 2, 1),
+ DEF_FIXED("s2d4", R8A77965_CLK_S2D4, CLK_S2, 4, 1),
+ DEF_FIXED("s3d1", R8A77965_CLK_S3D1, CLK_S3, 1, 1),
+ DEF_FIXED("s3d2", R8A77965_CLK_S3D2, CLK_S3, 2, 1),
+ DEF_FIXED("s3d4", R8A77965_CLK_S3D4, CLK_S3, 4, 1),
+
+ DEF_GEN3_SD("sd0", R8A77965_CLK_SD0, CLK_SDSRC, 0x074),
+ DEF_GEN3_SD("sd1", R8A77965_CLK_SD1, CLK_SDSRC, 0x078),
+ DEF_GEN3_SD("sd2", R8A77965_CLK_SD2, CLK_SDSRC, 0x268),
+ DEF_GEN3_SD("sd3", R8A77965_CLK_SD3, CLK_SDSRC, 0x26c),
+
+ DEF_FIXED("cl", R8A77965_CLK_CL, CLK_PLL1_DIV2, 48, 1),
+ DEF_FIXED("cp", R8A77965_CLK_CP, CLK_EXTAL, 2, 1),
+
+ DEF_DIV6P1("canfd", R8A77965_CLK_CANFD, CLK_PLL1_DIV4, 0x244),
+ DEF_DIV6P1("csi0", R8A77965_CLK_CSI0, CLK_PLL1_DIV4, 0x00c),
+ DEF_DIV6P1("mso", R8A77965_CLK_MSO, CLK_PLL1_DIV4, 0x014),
+ DEF_DIV6P1("hdmi", R8A77965_CLK_HDMI, CLK_PLL1_DIV4, 0x250),
+
+ DEF_DIV6_RO("osc", R8A77965_CLK_OSC, CLK_EXTAL, CPG_RCKCR, 8),
+ DEF_DIV6_RO("r_int", CLK_RINT, CLK_EXTAL, CPG_RCKCR, 32),
+
+ DEF_BASE("r", R8A77965_CLK_R, CLK_TYPE_GEN3_R, CLK_RINT),
+};
+
+static const struct mssr_mod_clk r8a77965_mod_clks[] __initconst = {
+ DEF_MOD("scif5", 202, R8A77965_CLK_S3D4),
+ DEF_MOD("scif4", 203, R8A77965_CLK_S3D4),
+ DEF_MOD("scif3", 204, R8A77965_CLK_S3D4),
+ DEF_MOD("scif1", 206, R8A77965_CLK_S3D4),
+ DEF_MOD("scif0", 207, R8A77965_CLK_S3D4),
+ DEF_MOD("sys-dmac2", 217, R8A77965_CLK_S0D3),
+ DEF_MOD("sys-dmac1", 218, R8A77965_CLK_S0D3),
+ DEF_MOD("sys-dmac0", 219, R8A77965_CLK_S0D3),
+
+ DEF_MOD("cmt3", 300, R8A77965_CLK_R),
+ DEF_MOD("cmt2", 301, R8A77965_CLK_R),
+ DEF_MOD("cmt1", 302, R8A77965_CLK_R),
+ DEF_MOD("cmt0", 303, R8A77965_CLK_R),
+ DEF_MOD("scif2", 310, R8A77965_CLK_S3D4),
+ DEF_MOD("sdif3", 311, R8A77965_CLK_SD3),
+ DEF_MOD("sdif2", 312, R8A77965_CLK_SD2),
+ DEF_MOD("sdif1", 313, R8A77965_CLK_SD1),
+ DEF_MOD("sdif0", 314, R8A77965_CLK_SD0),
+ DEF_MOD("pcie1", 318, R8A77965_CLK_S3D1),
+ DEF_MOD("pcie0", 319, R8A77965_CLK_S3D1),
+ DEF_MOD("usb3-if0", 328, R8A77965_CLK_S3D1),
+ DEF_MOD("usb-dmac0", 330, R8A77965_CLK_S3D1),
+ DEF_MOD("usb-dmac1", 331, R8A77965_CLK_S3D1),
+
+ DEF_MOD("rwdt", 402, R8A77965_CLK_R),
+ DEF_MOD("intc-ex", 407, R8A77965_CLK_CP),
+ DEF_MOD("intc-ap", 408, R8A77965_CLK_S0D3),
+
+ DEF_MOD("audmac1", 501, R8A77965_CLK_S0D3),
+ DEF_MOD("audmac0", 502, R8A77965_CLK_S0D3),
+ DEF_MOD("drif7", 508, R8A77965_CLK_S3D2),
+ DEF_MOD("drif6", 509, R8A77965_CLK_S3D2),
+ DEF_MOD("drif5", 510, R8A77965_CLK_S3D2),
+ DEF_MOD("drif4", 511, R8A77965_CLK_S3D2),
+ DEF_MOD("drif3", 512, R8A77965_CLK_S3D2),
+ DEF_MOD("drif2", 513, R8A77965_CLK_S3D2),
+ DEF_MOD("drif1", 514, R8A77965_CLK_S3D2),
+ DEF_MOD("drif0", 515, R8A77965_CLK_S3D2),
+ DEF_MOD("hscif4", 516, R8A77965_CLK_S3D1),
+ DEF_MOD("hscif3", 517, R8A77965_CLK_S3D1),
+ DEF_MOD("hscif2", 518, R8A77965_CLK_S3D1),
+ DEF_MOD("hscif1", 519, R8A77965_CLK_S3D1),
+ DEF_MOD("hscif0", 520, R8A77965_CLK_S3D1),
+ DEF_MOD("thermal", 522, R8A77965_CLK_CP),
+ DEF_MOD("pwm", 523, R8A77965_CLK_S0D12),
+
+ DEF_MOD("fcpvd1", 602, R8A77965_CLK_S0D2),
+ DEF_MOD("fcpvd0", 603, R8A77965_CLK_S0D2),
+ DEF_MOD("fcpvb0", 607, R8A77965_CLK_S0D1),
+ DEF_MOD("fcpvi0", 611, R8A77965_CLK_S0D1),
+ DEF_MOD("fcpf0", 615, R8A77965_CLK_S0D1),
+ DEF_MOD("fcpcs", 619, R8A77965_CLK_S0D2),
+ DEF_MOD("vspd1", 622, R8A77965_CLK_S0D2),
+ DEF_MOD("vspd0", 623, R8A77965_CLK_S0D2),
+ DEF_MOD("vspb", 626, R8A77965_CLK_S0D1),
+ DEF_MOD("vspi0", 631, R8A77965_CLK_S0D1),
+
+ DEF_MOD("ehci1", 702, R8A77965_CLK_S3D4),
+ DEF_MOD("ehci0", 703, R8A77965_CLK_S3D4),
+ DEF_MOD("hsusb", 704, R8A77965_CLK_S3D4),
+ DEF_MOD("csi20", 714, R8A77965_CLK_CSI0),
+ DEF_MOD("csi40", 716, R8A77965_CLK_CSI0),
+ DEF_MOD("du3", 721, R8A77965_CLK_S2D1),
+ DEF_MOD("du1", 723, R8A77965_CLK_S2D1),
+ DEF_MOD("du0", 724, R8A77965_CLK_S2D1),
+ DEF_MOD("lvds", 727, R8A77965_CLK_S2D1),
+ DEF_MOD("hdmi0", 729, R8A77965_CLK_HDMI),
+
+ DEF_MOD("vin7", 804, R8A77965_CLK_S0D2),
+ DEF_MOD("vin6", 805, R8A77965_CLK_S0D2),
+ DEF_MOD("vin5", 806, R8A77965_CLK_S0D2),
+ DEF_MOD("vin4", 807, R8A77965_CLK_S0D2),
+ DEF_MOD("vin3", 808, R8A77965_CLK_S0D2),
+ DEF_MOD("vin2", 809, R8A77965_CLK_S0D2),
+ DEF_MOD("vin1", 810, R8A77965_CLK_S0D2),
+ DEF_MOD("vin0", 811, R8A77965_CLK_S0D2),
+ DEF_MOD("etheravb", 812, R8A77965_CLK_S0D6),
+ DEF_MOD("imr1", 822, R8A77965_CLK_S0D2),
+ DEF_MOD("imr0", 823, R8A77965_CLK_S0D2),
+
+ DEF_MOD("gpio7", 905, R8A77965_CLK_S3D4),
+ DEF_MOD("gpio6", 906, R8A77965_CLK_S3D4),
+ DEF_MOD("gpio5", 907, R8A77965_CLK_S3D4),
+ DEF_MOD("gpio4", 908, R8A77965_CLK_S3D4),
+ DEF_MOD("gpio3", 909, R8A77965_CLK_S3D4),
+ DEF_MOD("gpio2", 910, R8A77965_CLK_S3D4),
+ DEF_MOD("gpio1", 911, R8A77965_CLK_S3D4),
+ DEF_MOD("gpio0", 912, R8A77965_CLK_S3D4),
+ DEF_MOD("can-fd", 914, R8A77965_CLK_S3D2),
+ DEF_MOD("can-if1", 915, R8A77965_CLK_S3D4),
+ DEF_MOD("can-if0", 916, R8A77965_CLK_S3D4),
+ DEF_MOD("i2c6", 918, R8A77965_CLK_S0D6),
+ DEF_MOD("i2c5", 919, R8A77965_CLK_S0D6),
+ DEF_MOD("i2c-dvfs", 926, R8A77965_CLK_CP),
+ DEF_MOD("i2c4", 927, R8A77965_CLK_S0D6),
+ DEF_MOD("i2c3", 928, R8A77965_CLK_S0D6),
+ DEF_MOD("i2c2", 929, R8A77965_CLK_S3D2),
+ DEF_MOD("i2c1", 930, R8A77965_CLK_S3D2),
+ DEF_MOD("i2c0", 931, R8A77965_CLK_S3D2),
+
+ DEF_MOD("ssi-all", 1005, R8A77965_CLK_S3D4),
+ DEF_MOD("ssi9", 1006, MOD_CLK_ID(1005)),
+ DEF_MOD("ssi8", 1007, MOD_CLK_ID(1005)),
+ DEF_MOD("ssi7", 1008, MOD_CLK_ID(1005)),
+ DEF_MOD("ssi6", 1009, MOD_CLK_ID(1005)),
+ DEF_MOD("ssi5", 1010, MOD_CLK_ID(1005)),
+ DEF_MOD("ssi4", 1011, MOD_CLK_ID(1005)),
+ DEF_MOD("ssi3", 1012, MOD_CLK_ID(1005)),
+ DEF_MOD("ssi2", 1013, MOD_CLK_ID(1005)),
+ DEF_MOD("ssi1", 1014, MOD_CLK_ID(1005)),
+ DEF_MOD("ssi0", 1015, MOD_CLK_ID(1005)),
+ DEF_MOD("scu-all", 1017, R8A77965_CLK_S3D4),
+ DEF_MOD("scu-dvc1", 1018, MOD_CLK_ID(1017)),
+ DEF_MOD("scu-dvc0", 1019, MOD_CLK_ID(1017)),
+ DEF_MOD("scu-ctu1-mix1", 1020, MOD_CLK_ID(1017)),
+ DEF_MOD("scu-ctu0-mix0", 1021, MOD_CLK_ID(1017)),
+ DEF_MOD("scu-src9", 1022, MOD_CLK_ID(1017)),
+ DEF_MOD("scu-src8", 1023, MOD_CLK_ID(1017)),
+ DEF_MOD("scu-src7", 1024, MOD_CLK_ID(1017)),
+ DEF_MOD("scu-src6", 1025, MOD_CLK_ID(1017)),
+ DEF_MOD("scu-src5", 1026, MOD_CLK_ID(1017)),
+ DEF_MOD("scu-src4", 1027, MOD_CLK_ID(1017)),
+ DEF_MOD("scu-src3", 1028, MOD_CLK_ID(1017)),
+ DEF_MOD("scu-src2", 1029, MOD_CLK_ID(1017)),
+ DEF_MOD("scu-src1", 1030, MOD_CLK_ID(1017)),
+ DEF_MOD("scu-src0", 1031, MOD_CLK_ID(1017)),
+};
+
+static const unsigned int r8a77965_crit_mod_clks[] __initconst = {
+ MOD_CLK_ID(408), /* INTC-AP (GIC) */
+};
+
+/*
+ * CPG Clock Data
+ */
+
+/*
+ * MD EXTAL PLL0 PLL1 PLL3 PLL4
+ * 14 13 19 17 (MHz)
+ *-----------------------------------------------------------
+ * 0 0 0 0 16.66 x 1 x180 x192 x192 x144
+ * 0 0 0 1 16.66 x 1 x180 x192 x128 x144
+ * 0 0 1 0 Prohibited setting
+ * 0 0 1 1 16.66 x 1 x180 x192 x192 x144
+ * 0 1 0 0 20 x 1 x150 x160 x160 x120
+ * 0 1 0 1 20 x 1 x150 x160 x106 x120
+ * 0 1 1 0 Prohibited setting
+ * 0 1 1 1 20 x 1 x150 x160 x160 x120
+ * 1 0 0 0 25 x 1 x120 x128 x128 x96
+ * 1 0 0 1 25 x 1 x120 x128 x84 x96
+ * 1 0 1 0 Prohibited setting
+ * 1 0 1 1 25 x 1 x120 x128 x128 x96
+ * 1 1 0 0 33.33 / 2 x180 x192 x192 x144
+ * 1 1 0 1 33.33 / 2 x180 x192 x128 x144
+ * 1 1 1 0 Prohibited setting
+ * 1 1 1 1 33.33 / 2 x180 x192 x192 x144
+ */
+#define CPG_PLL_CONFIG_INDEX(md) ((((md) & BIT(14)) >> 11) | \
+ (((md) & BIT(13)) >> 11) | \
+ (((md) & BIT(19)) >> 18) | \
+ (((md) & BIT(17)) >> 17))
+
+static const struct rcar_gen3_cpg_pll_config cpg_pll_configs[16] __initconst = {
+ /* EXTAL div PLL1 mult/div PLL3 mult/div */
+ { 1, 192, 1, 192, 1, },
+ { 1, 192, 1, 128, 1, },
+ { 0, /* Prohibited setting */ },
+ { 1, 192, 1, 192, 1, },
+ { 1, 160, 1, 160, 1, },
+ { 1, 160, 1, 106, 1, },
+ { 0, /* Prohibited setting */ },
+ { 1, 160, 1, 160, 1, },
+ { 1, 128, 1, 128, 1, },
+ { 1, 128, 1, 84, 1, },
+ { 0, /* Prohibited setting */ },
+ { 1, 128, 1, 128, 1, },
+ { 2, 192, 1, 192, 1, },
+ { 2, 192, 1, 128, 1, },
+ { 0, /* Prohibited setting */ },
+ { 2, 192, 1, 192, 1, },
+};
+
+static int __init r8a77965_cpg_mssr_init(struct device *dev)
+{
+ const struct rcar_gen3_cpg_pll_config *cpg_pll_config;
+ u32 cpg_mode;
+ int error;
+
+ error = rcar_rst_read_mode_pins(&cpg_mode);
+ if (error)
+ return error;
+
+ cpg_pll_config = &cpg_pll_configs[CPG_PLL_CONFIG_INDEX(cpg_mode)];
+ if (!cpg_pll_config->extal_div) {
+ dev_err(dev, "Prohibited setting (cpg_mode=0x%x)\n", cpg_mode);
+ return -EINVAL;
+ }
+
+ return rcar_gen3_cpg_init(cpg_pll_config, CLK_EXTALR, cpg_mode);
+};
+
+const struct cpg_mssr_info r8a77965_cpg_mssr_info __initconst = {
+ /* Core Clocks */
+ .core_clks = r8a77965_core_clks,
+ .num_core_clks = ARRAY_SIZE(r8a77965_core_clks),
+ .last_dt_core_clk = LAST_DT_CORE_CLK,
+ .num_total_core_clks = MOD_CLK_BASE,
+
+ /* Module Clocks */
+ .mod_clks = r8a77965_mod_clks,
+ .num_mod_clks = ARRAY_SIZE(r8a77965_mod_clks),
+ .num_hw_mod_clks = 12 * 32,
+
+ /* Critical Module Clocks */
+ .crit_mod_clks = r8a77965_crit_mod_clks,
+ .num_crit_mod_clks = ARRAY_SIZE(r8a77965_crit_mod_clks),
+
+ /* Callbacks */
+ .init = r8a77965_cpg_mssr_init,
+ .cpg_clk_register = rcar_gen3_cpg_clk_register,
+};
diff --git a/drivers/clk/renesas/r8a77980-cpg-mssr.c b/drivers/clk/renesas/r8a77980-cpg-mssr.c
new file mode 100644
index 000000000000..7aaae73a321a
--- /dev/null
+++ b/drivers/clk/renesas/r8a77980-cpg-mssr.c
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * r8a77980 Clock Pulse Generator / Module Standby and Software Reset
+ *
+ * Copyright (C) 2018 Renesas Electronics Corp.
+ * Copyright (C) 2018 Cogent Embedded, Inc.
+ *
+ * Based on r8a7795-cpg-mssr.c
+ *
+ * Copyright (C) 2015 Glider bvba
+ */
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/soc/renesas/rcar-rst.h>
+#include <linux/sys_soc.h>
+
+#include <dt-bindings/clock/r8a77980-cpg-mssr.h>
+
+#include "renesas-cpg-mssr.h"
+#include "rcar-gen3-cpg.h"
+
+enum clk_ids {
+ /* Core Clock Outputs exported to DT */
+ LAST_DT_CORE_CLK = R8A77980_CLK_OSC,
+
+ /* External Input Clocks */
+ CLK_EXTAL,
+ CLK_EXTALR,
+
+ /* Internal Core Clocks */
+ CLK_MAIN,
+ CLK_PLL1,
+ CLK_PLL2,
+ CLK_PLL3,
+ CLK_PLL1_DIV2,
+ CLK_PLL1_DIV4,
+ CLK_S0,
+ CLK_S1,
+ CLK_S2,
+ CLK_S3,
+ CLK_SDSRC,
+
+ /* Module Clocks */
+ MOD_CLK_BASE
+};
+
+static const struct cpg_core_clk r8a77980_core_clks[] __initconst = {
+ /* External Clock Inputs */
+ DEF_INPUT("extal", CLK_EXTAL),
+ DEF_INPUT("extalr", CLK_EXTALR),
+
+ /* Internal Core Clocks */
+ DEF_BASE(".main", CLK_MAIN, CLK_TYPE_GEN3_MAIN, CLK_EXTAL),
+ DEF_BASE(".pll1", CLK_PLL1, CLK_TYPE_GEN3_PLL1, CLK_MAIN),
+ DEF_BASE(".pll2", CLK_PLL2, CLK_TYPE_GEN3_PLL2, CLK_MAIN),
+ DEF_BASE(".pll3", CLK_PLL3, CLK_TYPE_GEN3_PLL3, CLK_MAIN),
+
+ DEF_FIXED(".pll1_div2", CLK_PLL1_DIV2, CLK_PLL1, 2, 1),
+ DEF_FIXED(".pll1_div4", CLK_PLL1_DIV4, CLK_PLL1_DIV2, 2, 1),
+ DEF_FIXED(".s0", CLK_S0, CLK_PLL1_DIV2, 2, 1),
+ DEF_FIXED(".s1", CLK_S1, CLK_PLL1_DIV2, 3, 1),
+ DEF_FIXED(".s2", CLK_S2, CLK_PLL1_DIV2, 4, 1),
+ DEF_FIXED(".s3", CLK_S3, CLK_PLL1_DIV2, 6, 1),
+ DEF_FIXED(".sdsrc", CLK_SDSRC, CLK_PLL1_DIV2, 2, 1),
+
+ /* Core Clock Outputs */
+ DEF_FIXED("ztr", R8A77980_CLK_ZTR, CLK_PLL1_DIV2, 6, 1),
+ DEF_FIXED("ztrd2", R8A77980_CLK_ZTRD2, CLK_PLL1_DIV2, 12, 1),
+ DEF_FIXED("zt", R8A77980_CLK_ZT, CLK_PLL1_DIV2, 4, 1),
+ DEF_FIXED("zx", R8A77980_CLK_ZX, CLK_PLL1_DIV2, 2, 1),
+ DEF_FIXED("s0d1", R8A77980_CLK_S0D1, CLK_S0, 1, 1),
+ DEF_FIXED("s0d2", R8A77980_CLK_S0D2, CLK_S0, 2, 1),
+ DEF_FIXED("s0d3", R8A77980_CLK_S0D3, CLK_S0, 3, 1),
+ DEF_FIXED("s0d4", R8A77980_CLK_S0D4, CLK_S0, 4, 1),
+ DEF_FIXED("s0d6", R8A77980_CLK_S0D6, CLK_S0, 6, 1),
+ DEF_FIXED("s0d12", R8A77980_CLK_S0D12, CLK_S0, 12, 1),
+ DEF_FIXED("s0d24", R8A77980_CLK_S0D24, CLK_S0, 24, 1),
+ DEF_FIXED("s1d1", R8A77980_CLK_S1D1, CLK_S1, 1, 1),
+ DEF_FIXED("s1d2", R8A77980_CLK_S1D2, CLK_S1, 2, 1),
+ DEF_FIXED("s1d4", R8A77980_CLK_S1D4, CLK_S1, 4, 1),
+ DEF_FIXED("s2d1", R8A77980_CLK_S2D1, CLK_S2, 1, 1),
+ DEF_FIXED("s2d2", R8A77980_CLK_S2D2, CLK_S2, 2, 1),
+ DEF_FIXED("s2d4", R8A77980_CLK_S2D4, CLK_S2, 4, 1),
+ DEF_FIXED("s3d1", R8A77980_CLK_S3D1, CLK_S3, 1, 1),
+ DEF_FIXED("s3d2", R8A77980_CLK_S3D2, CLK_S3, 2, 1),
+ DEF_FIXED("s3d4", R8A77980_CLK_S3D4, CLK_S3, 4, 1),
+
+ DEF_GEN3_SD("sd0", R8A77980_CLK_SD0, CLK_SDSRC, 0x0074),
+
+ DEF_FIXED("cl", R8A77980_CLK_CL, CLK_PLL1_DIV2, 48, 1),
+ DEF_FIXED("cp", R8A77980_CLK_CP, CLK_EXTAL, 2, 1),
+ DEF_FIXED("cpex", R8A77980_CLK_CPEX, CLK_EXTAL, 2, 1),
+
+ DEF_DIV6P1("canfd", R8A77980_CLK_CANFD, CLK_PLL1_DIV4, 0x244),
+ DEF_DIV6P1("csi0", R8A77980_CLK_CSI0, CLK_PLL1_DIV4, 0x00c),
+ DEF_DIV6P1("mso", R8A77980_CLK_MSO, CLK_PLL1_DIV4, 0x014),
+};
+
+static const struct mssr_mod_clk r8a77980_mod_clks[] __initconst = {
+ DEF_MOD("tmu4", 121, R8A77980_CLK_S0D6),
+ DEF_MOD("tmu3", 122, R8A77980_CLK_S0D6),
+ DEF_MOD("tmu2", 123, R8A77980_CLK_S0D6),
+ DEF_MOD("tmu1", 124, R8A77980_CLK_S0D6),
+ DEF_MOD("tmu0", 125, R8A77980_CLK_CP),
+ DEF_MOD("scif4", 203, R8A77980_CLK_S3D4),
+ DEF_MOD("scif3", 204, R8A77980_CLK_S3D4),
+ DEF_MOD("scif1", 206, R8A77980_CLK_S3D4),
+ DEF_MOD("scif0", 207, R8A77980_CLK_S3D4),
+ DEF_MOD("msiof3", 208, R8A77980_CLK_MSO),
+ DEF_MOD("msiof2", 209, R8A77980_CLK_MSO),
+ DEF_MOD("msiof1", 210, R8A77980_CLK_MSO),
+ DEF_MOD("msiof0", 211, R8A77980_CLK_MSO),
+ DEF_MOD("sys-dmac2", 217, R8A77980_CLK_S0D3),
+ DEF_MOD("sys-dmac1", 218, R8A77980_CLK_S0D3),
+ DEF_MOD("tpu0", 304, R8A77980_CLK_S3D4),
+ DEF_MOD("sdif", 314, R8A77980_CLK_SD0),
+ DEF_MOD("pciec0", 319, R8A77980_CLK_S3D1),
+ DEF_MOD("intc-ex", 407, R8A77980_CLK_CP),
+ DEF_MOD("intc-ap", 408, R8A77980_CLK_S0D3),
+ DEF_MOD("hscif3", 517, R8A77980_CLK_S3D1),
+ DEF_MOD("hscif2", 518, R8A77980_CLK_S3D1),
+ DEF_MOD("hscif1", 519, R8A77980_CLK_S3D1),
+ DEF_MOD("hscif0", 520, R8A77980_CLK_S3D1),
+ DEF_MOD("imp4", 521, R8A77980_CLK_S1D1),
+ DEF_MOD("thermal", 522, R8A77980_CLK_CP),
+ DEF_MOD("pwm", 523, R8A77980_CLK_S0D12),
+ DEF_MOD("impdma1", 526, R8A77980_CLK_S1D1),
+ DEF_MOD("impdma0", 527, R8A77980_CLK_S1D1),
+ DEF_MOD("imp-ocv4", 528, R8A77980_CLK_S1D1),
+ DEF_MOD("imp-ocv3", 529, R8A77980_CLK_S1D1),
+ DEF_MOD("imp-ocv2", 531, R8A77980_CLK_S1D1),
+ DEF_MOD("fcpvd0", 603, R8A77980_CLK_S3D1),
+ DEF_MOD("vspd0", 623, R8A77980_CLK_S3D1),
+ DEF_MOD("csi41", 715, R8A77980_CLK_CSI0),
+ DEF_MOD("csi40", 716, R8A77980_CLK_CSI0),
+ DEF_MOD("du0", 724, R8A77980_CLK_S2D1),
+ DEF_MOD("lvds", 727, R8A77980_CLK_S2D1),
+ DEF_MOD("etheravb", 812, R8A77980_CLK_S3D2),
+ DEF_MOD("gether", 813, R8A77980_CLK_S3D2),
+ DEF_MOD("imp3", 824, R8A77980_CLK_S1D1),
+ DEF_MOD("imp2", 825, R8A77980_CLK_S1D1),
+ DEF_MOD("imp1", 826, R8A77980_CLK_S1D1),
+ DEF_MOD("imp0", 827, R8A77980_CLK_S1D1),
+ DEF_MOD("imp-ocv1", 828, R8A77980_CLK_S1D1),
+ DEF_MOD("imp-ocv0", 829, R8A77980_CLK_S1D1),
+ DEF_MOD("impram", 830, R8A77980_CLK_S1D1),
+ DEF_MOD("impcnn", 831, R8A77980_CLK_S1D1),
+ DEF_MOD("gpio5", 907, R8A77980_CLK_CP),
+ DEF_MOD("gpio4", 908, R8A77980_CLK_CP),
+ DEF_MOD("gpio3", 909, R8A77980_CLK_CP),
+ DEF_MOD("gpio2", 910, R8A77980_CLK_CP),
+ DEF_MOD("gpio1", 911, R8A77980_CLK_CP),
+ DEF_MOD("gpio0", 912, R8A77980_CLK_CP),
+ DEF_MOD("can-fd", 914, R8A77980_CLK_S3D2),
+ DEF_MOD("i2c4", 927, R8A77980_CLK_S0D6),
+ DEF_MOD("i2c3", 928, R8A77980_CLK_S0D6),
+ DEF_MOD("i2c2", 929, R8A77980_CLK_S3D2),
+ DEF_MOD("i2c1", 930, R8A77980_CLK_S3D2),
+ DEF_MOD("i2c0", 931, R8A77980_CLK_S3D2),
+};
+
+static const unsigned int r8a77980_crit_mod_clks[] __initconst = {
+ MOD_CLK_ID(408), /* INTC-AP (GIC) */
+};
+
+
+/*
+ * CPG Clock Data
+ */
+
+/*
+ * MD EXTAL PLL2 PLL1 PLL3
+ * 14 13 (MHz)
+ * --------------------------------------------------
+ * 0 0 16.66 x 1 x240 x192 x192
+ * 0 1 20 x 1 x200 x160 x160
+ * 1 0 27 x 1 x148 x118 x118
+ * 1 1 33.33 / 2 x240 x192 x192
+ */
+#define CPG_PLL_CONFIG_INDEX(md) ((((md) & BIT(14)) >> 13) | \
+ (((md) & BIT(13)) >> 13))
+
+static const struct rcar_gen3_cpg_pll_config cpg_pll_configs[4] __initconst = {
+ /* EXTAL div PLL1 mult/div PLL3 mult/div */
+ { 1, 192, 1, 192, 1, },
+ { 1, 160, 1, 160, 1, },
+ { 1, 118, 1, 118, 1, },
+ { 2, 192, 1, 192, 1, },
+};
+
+static int __init r8a77980_cpg_mssr_init(struct device *dev)
+{
+ const struct rcar_gen3_cpg_pll_config *cpg_pll_config;
+ u32 cpg_mode;
+ int error;
+
+ error = rcar_rst_read_mode_pins(&cpg_mode);
+ if (error)
+ return error;
+
+ cpg_pll_config = &cpg_pll_configs[CPG_PLL_CONFIG_INDEX(cpg_mode)];
+
+ return rcar_gen3_cpg_init(cpg_pll_config, CLK_EXTALR, cpg_mode);
+}
+
+const struct cpg_mssr_info r8a77980_cpg_mssr_info __initconst = {
+ /* Core Clocks */
+ .core_clks = r8a77980_core_clks,
+ .num_core_clks = ARRAY_SIZE(r8a77980_core_clks),
+ .last_dt_core_clk = LAST_DT_CORE_CLK,
+ .num_total_core_clks = MOD_CLK_BASE,
+
+ /* Module Clocks */
+ .mod_clks = r8a77980_mod_clks,
+ .num_mod_clks = ARRAY_SIZE(r8a77980_mod_clks),
+ .num_hw_mod_clks = 12 * 32,
+
+ /* Critical Module Clocks */
+ .crit_mod_clks = r8a77980_crit_mod_clks,
+ .num_crit_mod_clks = ARRAY_SIZE(r8a77980_crit_mod_clks),
+
+ /* Callbacks */
+ .init = r8a77980_cpg_mssr_init,
+ .cpg_clk_register = rcar_gen3_cpg_clk_register,
+};
diff --git a/drivers/clk/renesas/rcar-gen3-cpg.c b/drivers/clk/renesas/rcar-gen3-cpg.c
index 0904886f5501..628b63b85d3f 100644
--- a/drivers/clk/renesas/rcar-gen3-cpg.c
+++ b/drivers/clk/renesas/rcar-gen3-cpg.c
@@ -13,6 +13,7 @@
*/
#include <linux/bug.h>
+#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/device.h>
@@ -62,6 +63,140 @@ static void cpg_simple_notifier_register(struct raw_notifier_head *notifiers,
}
/*
+ * Z Clock & Z2 Clock
+ *
+ * Traits of this clock:
+ * prepare - clk_prepare only ensures that parents are prepared
+ * enable - clk_enable only ensures that parents are enabled
+ * rate - rate is adjustable. clk->rate = (parent->rate * mult / 32 ) / 2
+ * parent - fixed parent. No clk_set_parent support
+ */
+#define CPG_FRQCRB 0x00000004
+#define CPG_FRQCRB_KICK BIT(31)
+#define CPG_FRQCRC 0x000000e0
+#define CPG_FRQCRC_ZFC_MASK GENMASK(12, 8)
+#define CPG_FRQCRC_Z2FC_MASK GENMASK(4, 0)
+
+struct cpg_z_clk {
+ struct clk_hw hw;
+ void __iomem *reg;
+ void __iomem *kick_reg;
+ unsigned long mask;
+};
+
+#define to_z_clk(_hw) container_of(_hw, struct cpg_z_clk, hw)
+
+static unsigned long cpg_z_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct cpg_z_clk *zclk = to_z_clk(hw);
+ unsigned int mult;
+ u32 val;
+
+ val = readl(zclk->reg) & zclk->mask;
+ mult = 32 - (val >> __ffs(zclk->mask));
+
+ /* Factor of 2 is for fixed divider */
+ return DIV_ROUND_CLOSEST_ULL((u64)parent_rate * mult, 32 * 2);
+}
+
+static long cpg_z_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ /* Factor of 2 is for fixed divider */
+ unsigned long prate = *parent_rate / 2;
+ unsigned int mult;
+
+ mult = div_u64(rate * 32ULL, prate);
+ mult = clamp(mult, 1U, 32U);
+
+ return (u64)prate * mult / 32;
+}
+
+static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct cpg_z_clk *zclk = to_z_clk(hw);
+ unsigned int mult;
+ unsigned int i;
+ u32 val, kick;
+
+ /* Factor of 2 is for fixed divider */
+ mult = DIV_ROUND_CLOSEST_ULL(rate * 32ULL * 2, parent_rate);
+ mult = clamp(mult, 1U, 32U);
+
+ if (readl(zclk->kick_reg) & CPG_FRQCRB_KICK)
+ return -EBUSY;
+
+ val = readl(zclk->reg) & ~zclk->mask;
+ val |= ((32 - mult) << __ffs(zclk->mask)) & zclk->mask;
+ writel(val, zclk->reg);
+
+ /*
+ * Set KICK bit in FRQCRB to update hardware setting and wait for
+ * clock change completion.
+ */
+ kick = readl(zclk->kick_reg);
+ kick |= CPG_FRQCRB_KICK;
+ writel(kick, zclk->kick_reg);
+
+ /*
+ * Note: There is no HW information about the worst case latency.
+ *
+ * Using experimental measurements, it seems that no more than
+ * ~10 iterations are needed, independently of the CPU rate.
+ * Since this value might be dependent of external xtal rate, pll1
+ * rate or even the other emulation clocks rate, use 1000 as a
+ * "super" safe value.
+ */
+ for (i = 1000; i; i--) {
+ if (!(readl(zclk->kick_reg) & CPG_FRQCRB_KICK))
+ return 0;
+
+ cpu_relax();
+ }
+
+ return -ETIMEDOUT;
+}
+
+static const struct clk_ops cpg_z_clk_ops = {
+ .recalc_rate = cpg_z_clk_recalc_rate,
+ .round_rate = cpg_z_clk_round_rate,
+ .set_rate = cpg_z_clk_set_rate,
+};
+
+static struct clk * __init cpg_z_clk_register(const char *name,
+ const char *parent_name,
+ void __iomem *reg,
+ unsigned long mask)
+{
+ struct clk_init_data init;
+ struct cpg_z_clk *zclk;
+ struct clk *clk;
+
+ zclk = kzalloc(sizeof(*zclk), GFP_KERNEL);
+ if (!zclk)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &cpg_z_clk_ops;
+ init.flags = 0;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ zclk->reg = reg + CPG_FRQCRC;
+ zclk->kick_reg = reg + CPG_FRQCRB;
+ zclk->hw.init = &init;
+ zclk->mask = mask;
+
+ clk = clk_register(NULL, &zclk->hw);
+ if (IS_ERR(clk))
+ kfree(zclk);
+
+ return clk;
+}
+
+/*
* SDn Clock
*/
#define CPG_SD_STP_HCK BIT(9)
@@ -420,6 +555,14 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev,
mult = 1;
break;
+ case CLK_TYPE_GEN3_Z:
+ return cpg_z_clk_register(core->name, __clk_get_name(parent),
+ base, CPG_FRQCRC_ZFC_MASK);
+
+ case CLK_TYPE_GEN3_Z2:
+ return cpg_z_clk_register(core->name, __clk_get_name(parent),
+ base, CPG_FRQCRC_Z2FC_MASK);
+
default:
return ERR_PTR(-EINVAL);
}
diff --git a/drivers/clk/renesas/rcar-gen3-cpg.h b/drivers/clk/renesas/rcar-gen3-cpg.h
index 2e4284399f53..ea4f8fc3c4c9 100644
--- a/drivers/clk/renesas/rcar-gen3-cpg.h
+++ b/drivers/clk/renesas/rcar-gen3-cpg.h
@@ -21,6 +21,8 @@ enum rcar_gen3_clk_types {
CLK_TYPE_GEN3_SD,
CLK_TYPE_GEN3_R,
CLK_TYPE_GEN3_PE,
+ CLK_TYPE_GEN3_Z,
+ CLK_TYPE_GEN3_Z2,
};
#define DEF_GEN3_SD(_name, _id, _parent, _offset) \
diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c
index e3cc72c81311..4e88e980fb76 100644
--- a/drivers/clk/renesas/renesas-cpg-mssr.c
+++ b/drivers/clk/renesas/renesas-cpg-mssr.c
@@ -693,12 +693,24 @@ static const struct of_device_id cpg_mssr_match[] = {
.data = &r8a7796_cpg_mssr_info,
},
#endif
+#ifdef CONFIG_CLK_R8A77965
+ {
+ .compatible = "renesas,r8a77965-cpg-mssr",
+ .data = &r8a77965_cpg_mssr_info,
+ },
+#endif
#ifdef CONFIG_CLK_R8A77970
{
.compatible = "renesas,r8a77970-cpg-mssr",
.data = &r8a77970_cpg_mssr_info,
},
#endif
+#ifdef CONFIG_CLK_R8A77980
+ {
+ .compatible = "renesas,r8a77980-cpg-mssr",
+ .data = &r8a77980_cpg_mssr_info,
+ },
+#endif
#ifdef CONFIG_CLK_R8A77995
{
.compatible = "renesas,r8a77995-cpg-mssr",
diff --git a/drivers/clk/renesas/renesas-cpg-mssr.h b/drivers/clk/renesas/renesas-cpg-mssr.h
index 0745b0930308..97ccb093c10f 100644
--- a/drivers/clk/renesas/renesas-cpg-mssr.h
+++ b/drivers/clk/renesas/renesas-cpg-mssr.h
@@ -139,7 +139,9 @@ extern const struct cpg_mssr_info r8a7792_cpg_mssr_info;
extern const struct cpg_mssr_info r8a7794_cpg_mssr_info;
extern const struct cpg_mssr_info r8a7795_cpg_mssr_info;
extern const struct cpg_mssr_info r8a7796_cpg_mssr_info;
+extern const struct cpg_mssr_info r8a77965_cpg_mssr_info;
extern const struct cpg_mssr_info r8a77970_cpg_mssr_info;
+extern const struct cpg_mssr_info r8a77980_cpg_mssr_info;
extern const struct cpg_mssr_info r8a77995_cpg_mssr_info;
diff --git a/drivers/clk/rockchip/clk-mmc-phase.c b/drivers/clk/rockchip/clk-mmc-phase.c
index 077fcdc7908b..026a26bb702d 100644
--- a/drivers/clk/rockchip/clk-mmc-phase.c
+++ b/drivers/clk/rockchip/clk-mmc-phase.c
@@ -25,6 +25,8 @@ struct rockchip_mmc_clock {
void __iomem *reg;
int id;
int shift;
+ int cached_phase;
+ struct notifier_block clk_rate_change_nb;
};
#define to_mmc_clock(_hw) container_of(_hw, struct rockchip_mmc_clock, hw)
@@ -58,6 +60,12 @@ static int rockchip_mmc_get_phase(struct clk_hw *hw)
u16 degrees;
u32 delay_num = 0;
+ /* See the comment for rockchip_mmc_set_phase below */
+ if (!rate) {
+ pr_err("%s: invalid clk rate\n", __func__);
+ return -EINVAL;
+ }
+
raw_value = readl(mmc_clock->reg) >> (mmc_clock->shift);
degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90;
@@ -84,6 +92,23 @@ static int rockchip_mmc_set_phase(struct clk_hw *hw, int degrees)
u32 raw_value;
u32 delay;
+ /*
+ * The below calculation is based on the output clock from
+ * MMC host to the card, which expects the phase clock inherits
+ * the clock rate from its parent, namely the output clock
+ * provider of MMC host. However, things may go wrong if
+ * (1) It is orphan.
+ * (2) It is assigned to the wrong parent.
+ *
+ * This check help debug the case (1), which seems to be the
+ * most likely problem we often face and which makes it difficult
+ * for people to debug unstable mmc tuning results.
+ */
+ if (!rate) {
+ pr_err("%s: invalid clk rate\n", __func__);
+ return -EINVAL;
+ }
+
nineties = degrees / 90;
remainder = (degrees % 90);
@@ -139,6 +164,41 @@ static const struct clk_ops rockchip_mmc_clk_ops = {
.set_phase = rockchip_mmc_set_phase,
};
+#define to_rockchip_mmc_clock(x) \
+ container_of(x, struct rockchip_mmc_clock, clk_rate_change_nb)
+static int rockchip_mmc_clk_rate_notify(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct rockchip_mmc_clock *mmc_clock = to_rockchip_mmc_clock(nb);
+ struct clk_notifier_data *ndata = data;
+
+ /*
+ * rockchip_mmc_clk is mostly used by mmc controllers to sample
+ * the intput data, which expects the fixed phase after the tuning
+ * process. However if the clock rate is changed, the phase is stale
+ * and may break the data sampling. So here we try to restore the phase
+ * for that case, except that
+ * (1) cached_phase is invaild since we inevitably cached it when the
+ * clock provider be reparented from orphan to its real parent in the
+ * first place. Otherwise we may mess up the initialization of MMC cards
+ * since we only set the default sample phase and drive phase later on.
+ * (2) the new coming rate is higher than the older one since mmc driver
+ * set the max-frequency to match the boards' ability but we can't go
+ * over the heads of that, otherwise the tests smoke out the issue.
+ */
+ if (ndata->old_rate <= ndata->new_rate)
+ return NOTIFY_DONE;
+
+ if (event == PRE_RATE_CHANGE)
+ mmc_clock->cached_phase =
+ rockchip_mmc_get_phase(&mmc_clock->hw);
+ else if (mmc_clock->cached_phase != -EINVAL &&
+ event == POST_RATE_CHANGE)
+ rockchip_mmc_set_phase(&mmc_clock->hw, mmc_clock->cached_phase);
+
+ return NOTIFY_DONE;
+}
+
struct clk *rockchip_clk_register_mmc(const char *name,
const char *const *parent_names, u8 num_parents,
void __iomem *reg, int shift)
@@ -146,6 +206,7 @@ struct clk *rockchip_clk_register_mmc(const char *name,
struct clk_init_data init;
struct rockchip_mmc_clock *mmc_clock;
struct clk *clk;
+ int ret;
mmc_clock = kmalloc(sizeof(*mmc_clock), GFP_KERNEL);
if (!mmc_clock)
@@ -162,8 +223,21 @@ struct clk *rockchip_clk_register_mmc(const char *name,
mmc_clock->shift = shift;
clk = clk_register(NULL, &mmc_clock->hw);
- if (IS_ERR(clk))
- kfree(mmc_clock);
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ goto err_register;
+ }
+
+ mmc_clock->clk_rate_change_nb.notifier_call =
+ &rockchip_mmc_clk_rate_notify;
+ ret = clk_notifier_register(clk, &mmc_clock->clk_rate_change_nb);
+ if (ret)
+ goto err_notifier;
return clk;
+err_notifier:
+ clk_unregister(clk);
+err_register:
+ kfree(mmc_clock);
+ return ERR_PTR(ret);
}
diff --git a/drivers/clk/rockchip/clk-rk3228.c b/drivers/clk/rockchip/clk-rk3228.c
index 11e7f2d1c054..7af48184b022 100644
--- a/drivers/clk/rockchip/clk-rk3228.c
+++ b/drivers/clk/rockchip/clk-rk3228.c
@@ -387,7 +387,7 @@ static struct rockchip_clk_branch rk3228_clk_branches[] __initdata = {
RK2928_CLKSEL_CON(23), 5, 2, MFLAGS, 0, 6, DFLAGS,
RK2928_CLKGATE_CON(2), 15, GFLAGS),
- COMPOSITE(SCLK_SDMMC, "sclk_sdmmc0", mux_mmc_src_p, 0,
+ COMPOSITE(SCLK_SDMMC, "sclk_sdmmc", mux_mmc_src_p, 0,
RK2928_CLKSEL_CON(11), 8, 2, MFLAGS, 0, 8, DFLAGS,
RK2928_CLKGATE_CON(2), 11, GFLAGS),
diff --git a/drivers/clk/rockchip/clk-rk3328.c b/drivers/clk/rockchip/clk-rk3328.c
index b04f29774ee7..252366a5231f 100644
--- a/drivers/clk/rockchip/clk-rk3328.c
+++ b/drivers/clk/rockchip/clk-rk3328.c
@@ -304,7 +304,7 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = {
COMPOSITE_NOMUX(0, "aclk_core", "armclk", CLK_IGNORE_UNUSED,
RK3328_CLKSEL_CON(1), 4, 3, DFLAGS | CLK_DIVIDER_READ_ONLY,
RK3328_CLKGATE_CON(7), 1, GFLAGS),
- GATE(0, "aclk_core_niu", "aclk_core", CLK_IGNORE_UNUSED,
+ GATE(0, "aclk_core_niu", "aclk_core", 0,
RK3328_CLKGATE_CON(13), 0, GFLAGS),
GATE(0, "aclk_gic400", "aclk_core", CLK_IGNORE_UNUSED,
RK3328_CLKGATE_CON(13), 1, GFLAGS),
@@ -318,7 +318,7 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = {
RK3328_CLKGATE_CON(6), 6, GFLAGS),
GATE(ACLK_GPU, "aclk_gpu", "aclk_gpu_pre", CLK_SET_RATE_PARENT,
RK3328_CLKGATE_CON(14), 0, GFLAGS),
- GATE(0, "aclk_gpu_niu", "aclk_gpu_pre", CLK_IGNORE_UNUSED,
+ GATE(0, "aclk_gpu_niu", "aclk_gpu_pre", 0,
RK3328_CLKGATE_CON(14), 1, GFLAGS),
/* PD_DDR */
@@ -513,9 +513,9 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = {
RK3328_CLKGATE_CON(24), 0, GFLAGS),
GATE(HCLK_RKVDEC, "hclk_rkvdec", "hclk_rkvdec_pre", CLK_SET_RATE_PARENT,
RK3328_CLKGATE_CON(24), 1, GFLAGS),
- GATE(0, "aclk_rkvdec_niu", "aclk_rkvdec_pre", CLK_IGNORE_UNUSED,
+ GATE(0, "aclk_rkvdec_niu", "aclk_rkvdec_pre", 0,
RK3328_CLKGATE_CON(24), 2, GFLAGS),
- GATE(0, "hclk_rkvdec_niu", "hclk_rkvdec_pre", CLK_IGNORE_UNUSED,
+ GATE(0, "hclk_rkvdec_niu", "hclk_rkvdec_pre", 0,
RK3328_CLKGATE_CON(24), 3, GFLAGS),
COMPOSITE(SCLK_VDEC_CABAC, "sclk_vdec_cabac", mux_4plls_p, 0,
@@ -535,9 +535,9 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = {
RK3328_CLKGATE_CON(23), 0, GFLAGS),
GATE(HCLK_VPU, "hclk_vpu", "hclk_vpu_pre", CLK_SET_RATE_PARENT,
RK3328_CLKGATE_CON(23), 1, GFLAGS),
- GATE(0, "aclk_vpu_niu", "aclk_vpu_pre", CLK_IGNORE_UNUSED,
+ GATE(0, "aclk_vpu_niu", "aclk_vpu_pre", 0,
RK3328_CLKGATE_CON(23), 2, GFLAGS),
- GATE(0, "hclk_vpu_niu", "hclk_vpu_pre", CLK_IGNORE_UNUSED,
+ GATE(0, "hclk_vpu_niu", "hclk_vpu_pre", 0,
RK3328_CLKGATE_CON(23), 3, GFLAGS),
COMPOSITE(ACLK_RKVENC, "aclk_rkvenc", mux_4plls_p, 0,
@@ -545,9 +545,9 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = {
RK3328_CLKGATE_CON(6), 3, GFLAGS),
FACTOR_GATE(HCLK_RKVENC, "hclk_rkvenc", "aclk_rkvenc", 0, 1, 4,
RK3328_CLKGATE_CON(11), 4, GFLAGS),
- GATE(0, "aclk_rkvenc_niu", "aclk_rkvenc", CLK_IGNORE_UNUSED,
+ GATE(0, "aclk_rkvenc_niu", "aclk_rkvenc", 0,
RK3328_CLKGATE_CON(25), 0, GFLAGS),
- GATE(0, "hclk_rkvenc_niu", "hclk_rkvenc", CLK_IGNORE_UNUSED,
+ GATE(0, "hclk_rkvenc_niu", "hclk_rkvenc", 0,
RK3328_CLKGATE_CON(25), 1, GFLAGS),
GATE(ACLK_H265, "aclk_h265", "aclk_rkvenc", 0,
RK3328_CLKGATE_CON(25), 0, GFLAGS),
@@ -588,7 +588,7 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = {
COMPOSITE(ACLK_VOP_PRE, "aclk_vop_pre", mux_4plls_p, 0,
RK3328_CLKSEL_CON(39), 6, 2, MFLAGS, 0, 5, DFLAGS,
RK3328_CLKGATE_CON(5), 5, GFLAGS),
- GATE(0, "clk_hdmi_sfc", "xin24m", 0,
+ GATE(SCLK_HDMI_SFC, "sclk_hdmi_sfc", "xin24m", 0,
RK3328_CLKGATE_CON(5), 4, GFLAGS),
COMPOSITE_NODIV(0, "clk_cif_src", mux_2plls_p, 0,
@@ -602,7 +602,7 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = {
RK3328_CLKGATE_CON(5), 6, GFLAGS),
DIV(DCLK_HDMIPHY, "dclk_hdmiphy", "dclk_lcdc_src", 0,
RK3328_CLKSEL_CON(40), 3, 3, DFLAGS),
- MUX(DCLK_LCDC, "dclk_lcdc", mux_dclk_lcdc_p, 0,
+ MUX(DCLK_LCDC, "dclk_lcdc", mux_dclk_lcdc_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
RK3328_CLKSEL_CON(40), 1, 1, MFLAGS),
/*
@@ -709,14 +709,14 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = {
/* PD_VOP */
GATE(ACLK_RGA, "aclk_rga", "aclk_rga_pre", 0, RK3328_CLKGATE_CON(21), 10, GFLAGS),
- GATE(0, "aclk_rga_niu", "aclk_rga_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(22), 3, GFLAGS),
+ GATE(0, "aclk_rga_niu", "aclk_rga_pre", 0, RK3328_CLKGATE_CON(22), 3, GFLAGS),
GATE(ACLK_VOP, "aclk_vop", "aclk_vop_pre", 0, RK3328_CLKGATE_CON(21), 2, GFLAGS),
- GATE(0, "aclk_vop_niu", "aclk_vop_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(21), 4, GFLAGS),
+ GATE(0, "aclk_vop_niu", "aclk_vop_pre", 0, RK3328_CLKGATE_CON(21), 4, GFLAGS),
GATE(ACLK_IEP, "aclk_iep", "aclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 6, GFLAGS),
GATE(ACLK_CIF, "aclk_cif", "aclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 8, GFLAGS),
GATE(ACLK_HDCP, "aclk_hdcp", "aclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 15, GFLAGS),
- GATE(0, "aclk_vio_niu", "aclk_vio_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(22), 2, GFLAGS),
+ GATE(0, "aclk_vio_niu", "aclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 2, GFLAGS),
GATE(HCLK_VOP, "hclk_vop", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 3, GFLAGS),
GATE(0, "hclk_vop_niu", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 5, GFLAGS),
@@ -724,10 +724,10 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = {
GATE(HCLK_CIF, "hclk_cif", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 9, GFLAGS),
GATE(HCLK_RGA, "hclk_rga", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 11, GFLAGS),
GATE(0, "hclk_ahb1tom", "hclk_vio_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(21), 12, GFLAGS),
- GATE(0, "pclk_vio_h2p", "hclk_vio_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(21), 13, GFLAGS),
- GATE(0, "hclk_vio_h2p", "hclk_vio_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(21), 14, GFLAGS),
+ GATE(0, "pclk_vio_h2p", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 13, GFLAGS),
+ GATE(0, "hclk_vio_h2p", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 14, GFLAGS),
GATE(HCLK_HDCP, "hclk_hdcp", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 0, GFLAGS),
- GATE(HCLK_VIO, "hclk_vio", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 1, GFLAGS),
+ GATE(0, "hclk_vio_niu", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 1, GFLAGS),
GATE(PCLK_HDMI, "pclk_hdmi", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 4, GFLAGS),
GATE(PCLK_HDCP, "pclk_hdcp", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 5, GFLAGS),
@@ -743,19 +743,19 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = {
GATE(HCLK_HOST0_ARB, "hclk_host0_arb", "hclk_peri", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(19), 7, GFLAGS),
GATE(HCLK_OTG, "hclk_otg", "hclk_peri", 0, RK3328_CLKGATE_CON(19), 8, GFLAGS),
GATE(HCLK_OTG_PMU, "hclk_otg_pmu", "hclk_peri", 0, RK3328_CLKGATE_CON(19), 9, GFLAGS),
- GATE(0, "hclk_peri_niu", "hclk_peri", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(19), 12, GFLAGS),
- GATE(0, "pclk_peri_niu", "hclk_peri", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(19), 13, GFLAGS),
+ GATE(0, "hclk_peri_niu", "hclk_peri", 0, RK3328_CLKGATE_CON(19), 12, GFLAGS),
+ GATE(0, "pclk_peri_niu", "hclk_peri", 0, RK3328_CLKGATE_CON(19), 13, GFLAGS),
/* PD_GMAC */
GATE(ACLK_MAC2PHY, "aclk_mac2phy", "aclk_gmac", 0, RK3328_CLKGATE_CON(26), 0, GFLAGS),
GATE(ACLK_MAC2IO, "aclk_mac2io", "aclk_gmac", 0, RK3328_CLKGATE_CON(26), 2, GFLAGS),
- GATE(0, "aclk_gmac_niu", "aclk_gmac", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(26), 4, GFLAGS),
+ GATE(0, "aclk_gmac_niu", "aclk_gmac", 0, RK3328_CLKGATE_CON(26), 4, GFLAGS),
GATE(PCLK_MAC2PHY, "pclk_mac2phy", "pclk_gmac", 0, RK3328_CLKGATE_CON(26), 1, GFLAGS),
GATE(PCLK_MAC2IO, "pclk_mac2io", "pclk_gmac", 0, RK3328_CLKGATE_CON(26), 3, GFLAGS),
- GATE(0, "pclk_gmac_niu", "pclk_gmac", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(26), 5, GFLAGS),
+ GATE(0, "pclk_gmac_niu", "pclk_gmac", 0, RK3328_CLKGATE_CON(26), 5, GFLAGS),
/* PD_BUS */
- GATE(0, "aclk_bus_niu", "aclk_bus_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 12, GFLAGS),
+ GATE(0, "aclk_bus_niu", "aclk_bus_pre", 0, RK3328_CLKGATE_CON(15), 12, GFLAGS),
GATE(ACLK_DCF, "aclk_dcf", "aclk_bus_pre", 0, RK3328_CLKGATE_CON(15), 11, GFLAGS),
GATE(ACLK_TSP, "aclk_tsp", "aclk_bus_pre", 0, RK3328_CLKGATE_CON(17), 12, GFLAGS),
GATE(0, "aclk_intmem", "aclk_bus_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 0, GFLAGS),
@@ -769,10 +769,10 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = {
GATE(HCLK_TSP, "hclk_tsp", "hclk_bus_pre", 0, RK3328_CLKGATE_CON(17), 11, GFLAGS),
GATE(HCLK_CRYPTO_MST, "hclk_crypto_mst", "hclk_bus_pre", 0, RK3328_CLKGATE_CON(15), 7, GFLAGS),
GATE(HCLK_CRYPTO_SLV, "hclk_crypto_slv", "hclk_bus_pre", 0, RK3328_CLKGATE_CON(15), 8, GFLAGS),
- GATE(0, "hclk_bus_niu", "hclk_bus_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 13, GFLAGS),
+ GATE(0, "hclk_bus_niu", "hclk_bus_pre", 0, RK3328_CLKGATE_CON(15), 13, GFLAGS),
GATE(HCLK_PDM, "hclk_pdm", "hclk_bus_pre", 0, RK3328_CLKGATE_CON(28), 0, GFLAGS),
- GATE(0, "pclk_bus_niu", "pclk_bus", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 14, GFLAGS),
+ GATE(0, "pclk_bus_niu", "pclk_bus", 0, RK3328_CLKGATE_CON(15), 14, GFLAGS),
GATE(0, "pclk_efuse", "pclk_bus", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 9, GFLAGS),
GATE(0, "pclk_otp", "pclk_bus", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(28), 4, GFLAGS),
GATE(PCLK_I2C0, "pclk_i2c0", "pclk_bus", 0, RK3328_CLKGATE_CON(15), 10, GFLAGS),
@@ -807,37 +807,42 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = {
GATE(0, "pclk_acodecphy", "pclk_phy_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(17), 5, GFLAGS),
GATE(PCLK_HDMIPHY, "pclk_hdmiphy", "pclk_phy_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(17), 7, GFLAGS),
GATE(0, "pclk_vdacphy", "pclk_phy_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(17), 8, GFLAGS),
- GATE(0, "pclk_phy_niu", "pclk_phy_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 15, GFLAGS),
+ GATE(0, "pclk_phy_niu", "pclk_phy_pre", 0, RK3328_CLKGATE_CON(15), 15, GFLAGS),
/* PD_MMC */
- MMC(SCLK_SDMMC_DRV, "sdmmc_drv", "sclk_sdmmc",
+ MMC(SCLK_SDMMC_DRV, "sdmmc_drv", "clk_sdmmc",
RK3328_SDMMC_CON0, 1),
- MMC(SCLK_SDMMC_SAMPLE, "sdmmc_sample", "sclk_sdmmc",
+ MMC(SCLK_SDMMC_SAMPLE, "sdmmc_sample", "clk_sdmmc",
RK3328_SDMMC_CON1, 1),
- MMC(SCLK_SDIO_DRV, "sdio_drv", "sclk_sdio",
+ MMC(SCLK_SDIO_DRV, "sdio_drv", "clk_sdio",
RK3328_SDIO_CON0, 1),
- MMC(SCLK_SDIO_SAMPLE, "sdio_sample", "sclk_sdio",
+ MMC(SCLK_SDIO_SAMPLE, "sdio_sample", "clk_sdio",
RK3328_SDIO_CON1, 1),
- MMC(SCLK_EMMC_DRV, "emmc_drv", "sclk_emmc",
+ MMC(SCLK_EMMC_DRV, "emmc_drv", "clk_emmc",
RK3328_EMMC_CON0, 1),
- MMC(SCLK_EMMC_SAMPLE, "emmc_sample", "sclk_emmc",
+ MMC(SCLK_EMMC_SAMPLE, "emmc_sample", "clk_emmc",
RK3328_EMMC_CON1, 1),
- MMC(SCLK_SDMMC_EXT_DRV, "sdmmc_ext_drv", "sclk_sdmmc_ext",
+ MMC(SCLK_SDMMC_EXT_DRV, "sdmmc_ext_drv", "clk_sdmmc_ext",
RK3328_SDMMC_EXT_CON0, 1),
- MMC(SCLK_SDMMC_EXT_SAMPLE, "sdmmc_ext_sample", "sclk_sdmmc_ext",
+ MMC(SCLK_SDMMC_EXT_SAMPLE, "sdmmc_ext_sample", "clk_sdmmc_ext",
RK3328_SDMMC_EXT_CON1, 1),
};
static const char *const rk3328_critical_clocks[] __initconst = {
"aclk_bus",
+ "aclk_bus_niu",
"pclk_bus",
+ "pclk_bus_niu",
"hclk_bus",
+ "hclk_bus_niu",
"aclk_peri",
"hclk_peri",
+ "hclk_peri_niu",
"pclk_peri",
+ "pclk_peri_niu",
"pclk_dbg",
"aclk_core_niu",
"aclk_gic400",
@@ -861,6 +866,20 @@ static const char *const rk3328_critical_clocks[] __initconst = {
"aclk_rga_niu",
"pclk_vio_h2p",
"hclk_vio_h2p",
+ "aclk_vio_niu",
+ "hclk_vio_niu",
+ "aclk_vop_niu",
+ "hclk_vop_niu",
+ "aclk_gpu_niu",
+ "aclk_rkvdec_niu",
+ "hclk_rkvdec_niu",
+ "aclk_vpu_niu",
+ "hclk_vpu_niu",
+ "aclk_rkvenc_niu",
+ "hclk_rkvenc_niu",
+ "aclk_gmac_niu",
+ "pclk_gmac_niu",
+ "pclk_phy_niu",
};
static void __init rk3328_clk_init(struct device_node *np)
diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c
index 6847120b61cd..bca10d618f0a 100644
--- a/drivers/clk/rockchip/clk-rk3399.c
+++ b/drivers/clk/rockchip/clk-rk3399.c
@@ -57,6 +57,7 @@ static struct rockchip_pll_rate_table rk3399_pll_rates[] = {
RK3036_PLL_RATE(1656000000, 1, 69, 1, 1, 1, 0),
RK3036_PLL_RATE(1632000000, 1, 68, 1, 1, 1, 0),
RK3036_PLL_RATE(1608000000, 1, 67, 1, 1, 1, 0),
+ RK3036_PLL_RATE(1600000000, 3, 200, 1, 1, 1, 0),
RK3036_PLL_RATE(1584000000, 1, 66, 1, 1, 1, 0),
RK3036_PLL_RATE(1560000000, 1, 65, 1, 1, 1, 0),
RK3036_PLL_RATE(1536000000, 1, 64, 1, 1, 1, 0),
@@ -670,7 +671,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
RK3399_CLKGATE_CON(9), 7, GFLAGS,
&rk3399_uart3_fracmux),
- COMPOSITE(0, "pclk_ddr", mux_pll_src_cpll_gpll_p, CLK_IGNORE_UNUSED,
+ COMPOSITE(PCLK_DDR, "pclk_ddr", mux_pll_src_cpll_gpll_p, CLK_IGNORE_UNUSED,
RK3399_CLKSEL_CON(6), 15, 1, MFLAGS, 8, 5, DFLAGS,
RK3399_CLKGATE_CON(3), 4, GFLAGS),
@@ -886,7 +887,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
RK3399_CLKGATE_CON(31), 8, GFLAGS),
/* sdio & sdmmc */
- COMPOSITE(0, "hclk_sd", mux_pll_src_cpll_gpll_p, 0,
+ COMPOSITE(HCLK_SD, "hclk_sd", mux_pll_src_cpll_gpll_p, 0,
RK3399_CLKSEL_CON(13), 15, 1, MFLAGS, 8, 5, DFLAGS,
RK3399_CLKGATE_CON(12), 13, GFLAGS),
GATE(HCLK_SDMMC, "hclk_sdmmc", "hclk_sd", 0,
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
index 35dbd63c2f49..3cd8ad59e0b7 100644
--- a/drivers/clk/rockchip/clk.c
+++ b/drivers/clk/rockchip/clk.c
@@ -57,6 +57,7 @@ static struct clk *rockchip_clk_register_branch(const char *name,
struct clk_divider *div = NULL;
const struct clk_ops *mux_ops = NULL, *div_ops = NULL,
*gate_ops = NULL;
+ int ret;
if (num_parents > 1) {
mux = kzalloc(sizeof(*mux), GFP_KERNEL);
@@ -74,8 +75,10 @@ static struct clk *rockchip_clk_register_branch(const char *name,
if (gate_offset >= 0) {
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
- if (!gate)
+ if (!gate) {
+ ret = -ENOMEM;
goto err_gate;
+ }
gate->flags = gate_flags;
gate->reg = base + gate_offset;
@@ -86,8 +89,10 @@ static struct clk *rockchip_clk_register_branch(const char *name,
if (div_width > 0) {
div = kzalloc(sizeof(*div), GFP_KERNEL);
- if (!div)
+ if (!div) {
+ ret = -ENOMEM;
goto err_div;
+ }
div->flags = div_flags;
div->reg = base + muxdiv_offset;
@@ -106,12 +111,19 @@ static struct clk *rockchip_clk_register_branch(const char *name,
gate ? &gate->hw : NULL, gate_ops,
flags);
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ goto err_composite;
+ }
+
return clk;
+err_composite:
+ kfree(div);
err_div:
kfree(gate);
err_gate:
kfree(mux);
- return ERR_PTR(-ENOMEM);
+ return ERR_PTR(ret);
}
struct rockchip_clk_frac {
@@ -291,8 +303,10 @@ static struct clk *rockchip_clk_register_frac_branch(
init.num_parents = child->num_parents;
mux_clk = clk_register(NULL, &frac_mux->hw);
- if (IS_ERR(mux_clk))
+ if (IS_ERR(mux_clk)) {
+ kfree(frac);
return clk;
+ }
rockchip_clk_add_lookup(ctx, mux_clk, child->id);
diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile
index ef8900bc077f..513826393158 100644
--- a/drivers/clk/samsung/Makefile
+++ b/drivers/clk/samsung/Makefile
@@ -8,9 +8,11 @@ obj-$(CONFIG_SOC_EXYNOS3250) += clk-exynos3250.o
obj-$(CONFIG_ARCH_EXYNOS4) += clk-exynos4.o
obj-$(CONFIG_ARCH_EXYNOS4) += clk-exynos4412-isp.o
obj-$(CONFIG_SOC_EXYNOS5250) += clk-exynos5250.o
+obj-$(CONFIG_SOC_EXYNOS5250) += clk-exynos5-subcmu.o
obj-$(CONFIG_SOC_EXYNOS5260) += clk-exynos5260.o
obj-$(CONFIG_SOC_EXYNOS5410) += clk-exynos5410.o
obj-$(CONFIG_SOC_EXYNOS5420) += clk-exynos5420.o
+obj-$(CONFIG_SOC_EXYNOS5420) += clk-exynos5-subcmu.o
obj-$(CONFIG_EXYNOS_ARM64_COMMON_CLK) += clk-exynos5433.o
obj-$(CONFIG_SOC_EXYNOS5440) += clk-exynos5440.o
obj-$(CONFIG_EXYNOS_AUDSS_CLK_CON) += clk-exynos-audss.o
diff --git a/drivers/clk/samsung/clk-exynos-audss.c b/drivers/clk/samsung/clk-exynos-audss.c
index 5bfc92ee3129..b4b057c7301c 100644
--- a/drivers/clk/samsung/clk-exynos-audss.c
+++ b/drivers/clk/samsung/clk-exynos-audss.c
@@ -143,10 +143,8 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
reg_base = devm_ioremap_resource(dev, res);
- if (IS_ERR(reg_base)) {
- dev_err(dev, "failed to map audss registers\n");
+ if (IS_ERR(reg_base))
return PTR_ERR(reg_base);
- }
epll = ERR_PTR(-ENODEV);
diff --git a/drivers/clk/samsung/clk-exynos3250.c b/drivers/clk/samsung/clk-exynos3250.c
index 1b81e283f605..27c9d23657b3 100644
--- a/drivers/clk/samsung/clk-exynos3250.c
+++ b/drivers/clk/samsung/clk-exynos3250.c
@@ -670,73 +670,73 @@ static const struct samsung_gate_clock gate_clks[] __initconst = {
/* APLL & MPLL & BPLL & UPLL */
static const struct samsung_pll_rate_table exynos3250_pll_rates[] __initconst = {
- PLL_35XX_RATE(1200000000, 400, 4, 1),
- PLL_35XX_RATE(1100000000, 275, 3, 1),
- PLL_35XX_RATE(1066000000, 533, 6, 1),
- PLL_35XX_RATE(1000000000, 250, 3, 1),
- PLL_35XX_RATE( 960000000, 320, 4, 1),
- PLL_35XX_RATE( 900000000, 300, 4, 1),
- PLL_35XX_RATE( 850000000, 425, 6, 1),
- PLL_35XX_RATE( 800000000, 200, 3, 1),
- PLL_35XX_RATE( 700000000, 175, 3, 1),
- PLL_35XX_RATE( 667000000, 667, 12, 1),
- PLL_35XX_RATE( 600000000, 400, 4, 2),
- PLL_35XX_RATE( 533000000, 533, 6, 2),
- PLL_35XX_RATE( 520000000, 260, 3, 2),
- PLL_35XX_RATE( 500000000, 250, 3, 2),
- PLL_35XX_RATE( 400000000, 200, 3, 2),
- PLL_35XX_RATE( 200000000, 200, 3, 3),
- PLL_35XX_RATE( 100000000, 200, 3, 4),
+ PLL_35XX_RATE(24 * MHZ, 1200000000, 400, 4, 1),
+ PLL_35XX_RATE(24 * MHZ, 1100000000, 275, 3, 1),
+ PLL_35XX_RATE(24 * MHZ, 1066000000, 533, 6, 1),
+ PLL_35XX_RATE(24 * MHZ, 1000000000, 250, 3, 1),
+ PLL_35XX_RATE(24 * MHZ, 960000000, 320, 4, 1),
+ PLL_35XX_RATE(24 * MHZ, 900000000, 300, 4, 1),
+ PLL_35XX_RATE(24 * MHZ, 850000000, 425, 6, 1),
+ PLL_35XX_RATE(24 * MHZ, 800000000, 200, 3, 1),
+ PLL_35XX_RATE(24 * MHZ, 700000000, 175, 3, 1),
+ PLL_35XX_RATE(24 * MHZ, 667000000, 667, 12, 1),
+ PLL_35XX_RATE(24 * MHZ, 600000000, 400, 4, 2),
+ PLL_35XX_RATE(24 * MHZ, 533000000, 533, 6, 2),
+ PLL_35XX_RATE(24 * MHZ, 520000000, 260, 3, 2),
+ PLL_35XX_RATE(24 * MHZ, 500000000, 250, 3, 2),
+ PLL_35XX_RATE(24 * MHZ, 400000000, 200, 3, 2),
+ PLL_35XX_RATE(24 * MHZ, 200000000, 200, 3, 3),
+ PLL_35XX_RATE(24 * MHZ, 100000000, 200, 3, 4),
{ /* sentinel */ }
};
/* EPLL */
static const struct samsung_pll_rate_table exynos3250_epll_rates[] __initconst = {
- PLL_36XX_RATE(800000000, 200, 3, 1, 0),
- PLL_36XX_RATE(288000000, 96, 2, 2, 0),
- PLL_36XX_RATE(192000000, 128, 2, 3, 0),
- PLL_36XX_RATE(144000000, 96, 2, 3, 0),
- PLL_36XX_RATE( 96000000, 128, 2, 4, 0),
- PLL_36XX_RATE( 84000000, 112, 2, 4, 0),
- PLL_36XX_RATE( 80000004, 106, 2, 4, 43691),
- PLL_36XX_RATE( 73728000, 98, 2, 4, 19923),
- PLL_36XX_RATE( 67737598, 270, 3, 5, 62285),
- PLL_36XX_RATE( 65535999, 174, 2, 5, 49982),
- PLL_36XX_RATE( 50000000, 200, 3, 5, 0),
- PLL_36XX_RATE( 49152002, 131, 2, 5, 4719),
- PLL_36XX_RATE( 48000000, 128, 2, 5, 0),
- PLL_36XX_RATE( 45158401, 180, 3, 5, 41524),
+ PLL_36XX_RATE(24 * MHZ, 800000000, 200, 3, 1, 0),
+ PLL_36XX_RATE(24 * MHZ, 288000000, 96, 2, 2, 0),
+ PLL_36XX_RATE(24 * MHZ, 192000000, 128, 2, 3, 0),
+ PLL_36XX_RATE(24 * MHZ, 144000000, 96, 2, 3, 0),
+ PLL_36XX_RATE(24 * MHZ, 96000000, 128, 2, 4, 0),
+ PLL_36XX_RATE(24 * MHZ, 84000000, 112, 2, 4, 0),
+ PLL_36XX_RATE(24 * MHZ, 80000003, 106, 2, 4, 43691),
+ PLL_36XX_RATE(24 * MHZ, 73728000, 98, 2, 4, 19923),
+ PLL_36XX_RATE(24 * MHZ, 67737598, 270, 3, 5, 62285),
+ PLL_36XX_RATE(24 * MHZ, 65535999, 174, 2, 5, 49982),
+ PLL_36XX_RATE(24 * MHZ, 50000000, 200, 3, 5, 0),
+ PLL_36XX_RATE(24 * MHZ, 49152002, 131, 2, 5, 4719),
+ PLL_36XX_RATE(24 * MHZ, 48000000, 128, 2, 5, 0),
+ PLL_36XX_RATE(24 * MHZ, 45158401, 180, 3, 5, 41524),
{ /* sentinel */ }
};
/* VPLL */
static const struct samsung_pll_rate_table exynos3250_vpll_rates[] __initconst = {
- PLL_36XX_RATE(600000000, 100, 2, 1, 0),
- PLL_36XX_RATE(533000000, 266, 3, 2, 32768),
- PLL_36XX_RATE(519230987, 173, 2, 2, 5046),
- PLL_36XX_RATE(500000000, 250, 3, 2, 0),
- PLL_36XX_RATE(445500000, 148, 2, 2, 32768),
- PLL_36XX_RATE(445055007, 148, 2, 2, 23047),
- PLL_36XX_RATE(400000000, 200, 3, 2, 0),
- PLL_36XX_RATE(371250000, 123, 2, 2, 49152),
- PLL_36XX_RATE(370878997, 185, 3, 2, 28803),
- PLL_36XX_RATE(340000000, 170, 3, 2, 0),
- PLL_36XX_RATE(335000015, 111, 2, 2, 43691),
- PLL_36XX_RATE(333000000, 111, 2, 2, 0),
- PLL_36XX_RATE(330000000, 110, 2, 2, 0),
- PLL_36XX_RATE(320000015, 106, 2, 2, 43691),
- PLL_36XX_RATE(300000000, 100, 2, 2, 0),
- PLL_36XX_RATE(275000000, 275, 3, 3, 0),
- PLL_36XX_RATE(222750000, 148, 2, 3, 32768),
- PLL_36XX_RATE(222528007, 148, 2, 3, 23069),
- PLL_36XX_RATE(160000000, 160, 3, 3, 0),
- PLL_36XX_RATE(148500000, 99, 2, 3, 0),
- PLL_36XX_RATE(148352005, 98, 2, 3, 59070),
- PLL_36XX_RATE(108000000, 144, 2, 4, 0),
- PLL_36XX_RATE( 74250000, 99, 2, 4, 0),
- PLL_36XX_RATE( 74176002, 98, 3, 4, 59070),
- PLL_36XX_RATE( 54054000, 216, 3, 5, 14156),
- PLL_36XX_RATE( 54000000, 144, 2, 5, 0),
+ PLL_36XX_RATE(24 * MHZ, 600000000, 100, 2, 1, 0),
+ PLL_36XX_RATE(24 * MHZ, 533000000, 266, 3, 2, 32768),
+ PLL_36XX_RATE(24 * MHZ, 519230987, 173, 2, 2, 5046),
+ PLL_36XX_RATE(24 * MHZ, 500000000, 250, 3, 2, 0),
+ PLL_36XX_RATE(24 * MHZ, 445500000, 148, 2, 2, 32768),
+ PLL_36XX_RATE(24 * MHZ, 445055007, 148, 2, 2, 23047),
+ PLL_36XX_RATE(24 * MHZ, 400000000, 200, 3, 2, 0),
+ PLL_36XX_RATE(24 * MHZ, 371250000, 123, 2, 2, 49152),
+ PLL_36XX_RATE(24 * MHZ, 370878997, 185, 3, 2, 28803),
+ PLL_36XX_RATE(24 * MHZ, 340000000, 170, 3, 2, 0),
+ PLL_36XX_RATE(24 * MHZ, 335000015, 111, 2, 2, 43691),
+ PLL_36XX_RATE(24 * MHZ, 333000000, 111, 2, 2, 0),
+ PLL_36XX_RATE(24 * MHZ, 330000000, 110, 2, 2, 0),
+ PLL_36XX_RATE(24 * MHZ, 320000015, 106, 2, 2, 43691),
+ PLL_36XX_RATE(24 * MHZ, 300000000, 100, 2, 2, 0),
+ PLL_36XX_RATE(24 * MHZ, 275000000, 275, 3, 3, 0),
+ PLL_36XX_RATE(24 * MHZ, 222750000, 148, 2, 3, 32768),
+ PLL_36XX_RATE(24 * MHZ, 222528007, 148, 2, 3, 23069),
+ PLL_36XX_RATE(24 * MHZ, 160000000, 160, 3, 3, 0),
+ PLL_36XX_RATE(24 * MHZ, 148500000, 99, 2, 3, 0),
+ PLL_36XX_RATE(24 * MHZ, 148352005, 98, 2, 3, 59070),
+ PLL_36XX_RATE(24 * MHZ, 108000000, 144, 2, 4, 0),
+ PLL_36XX_RATE(24 * MHZ, 74250000, 99, 2, 4, 0),
+ PLL_36XX_RATE(24 * MHZ, 74176002, 98, 2, 4, 59070),
+ PLL_36XX_RATE(24 * MHZ, 54054000, 216, 3, 5, 14156),
+ PLL_36XX_RATE(24 * MHZ, 54000000, 144, 2, 5, 0),
{ /* sentinel */ }
};
diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c
index 134f25f2a913..0421960eb963 100644
--- a/drivers/clk/samsung/clk-exynos4.c
+++ b/drivers/clk/samsung/clk-exynos4.c
@@ -1266,77 +1266,78 @@ static const struct of_device_id ext_clk_match[] __initconst = {
/* PLLs PMS values */
static const struct samsung_pll_rate_table exynos4210_apll_rates[] __initconst = {
- PLL_45XX_RATE(1200000000, 150, 3, 1, 28),
- PLL_45XX_RATE(1000000000, 250, 6, 1, 28),
- PLL_45XX_RATE( 800000000, 200, 6, 1, 28),
- PLL_45XX_RATE( 666857142, 389, 14, 1, 13),
- PLL_45XX_RATE( 600000000, 100, 4, 1, 13),
- PLL_45XX_RATE( 533000000, 533, 24, 1, 5),
- PLL_45XX_RATE( 500000000, 250, 6, 2, 28),
- PLL_45XX_RATE( 400000000, 200, 6, 2, 28),
- PLL_45XX_RATE( 200000000, 200, 6, 3, 28),
+ PLL_4508_RATE(24 * MHZ, 1200000000, 150, 3, 1, 28),
+ PLL_4508_RATE(24 * MHZ, 1000000000, 250, 6, 1, 28),
+ PLL_4508_RATE(24 * MHZ, 800000000, 200, 6, 1, 28),
+ PLL_4508_RATE(24 * MHZ, 666857142, 389, 14, 1, 13),
+ PLL_4508_RATE(24 * MHZ, 600000000, 100, 4, 1, 13),
+ PLL_4508_RATE(24 * MHZ, 533000000, 533, 24, 1, 5),
+ PLL_4508_RATE(24 * MHZ, 500000000, 250, 6, 2, 28),
+ PLL_4508_RATE(24 * MHZ, 400000000, 200, 6, 2, 28),
+ PLL_4508_RATE(24 * MHZ, 200000000, 200, 6, 3, 28),
{ /* sentinel */ }
};
static const struct samsung_pll_rate_table exynos4210_epll_rates[] __initconst = {
- PLL_4600_RATE(192000000, 48, 3, 1, 0, 0),
- PLL_4600_RATE(180633605, 45, 3, 1, 10381, 0),
- PLL_4600_RATE(180000000, 45, 3, 1, 0, 0),
- PLL_4600_RATE( 73727996, 73, 3, 3, 47710, 1),
- PLL_4600_RATE( 67737602, 90, 4, 3, 20762, 1),
- PLL_4600_RATE( 49151992, 49, 3, 3, 9961, 0),
- PLL_4600_RATE( 45158401, 45, 3, 3, 10381, 0),
+ PLL_4600_RATE(24 * MHZ, 192000000, 48, 3, 1, 0, 0),
+ PLL_4600_RATE(24 * MHZ, 180633605, 45, 3, 1, 10381, 0),
+ PLL_4600_RATE(24 * MHZ, 180000000, 45, 3, 1, 0, 0),
+ PLL_4600_RATE(24 * MHZ, 73727996, 73, 3, 3, 47710, 1),
+ PLL_4600_RATE(24 * MHZ, 67737602, 90, 4, 3, 20762, 1),
+ PLL_4600_RATE(24 * MHZ, 49151992, 49, 3, 3, 9961, 0),
+ PLL_4600_RATE(24 * MHZ, 45158401, 45, 3, 3, 10381, 0),
{ /* sentinel */ }
};
static const struct samsung_pll_rate_table exynos4210_vpll_rates[] __initconst = {
- PLL_4650_RATE(360000000, 44, 3, 0, 1024, 0, 14, 0),
- PLL_4650_RATE(324000000, 53, 2, 1, 1024, 1, 1, 1),
- PLL_4650_RATE(259617187, 63, 3, 1, 1950, 0, 20, 1),
- PLL_4650_RATE(110000000, 53, 3, 2, 2048, 0, 17, 0),
- PLL_4650_RATE( 55360351, 53, 3, 3, 2417, 0, 17, 0),
+ PLL_4650_RATE(24 * MHZ, 360000000, 44, 3, 0, 1024, 0, 14, 0),
+ PLL_4650_RATE(24 * MHZ, 324000000, 53, 2, 1, 1024, 1, 1, 1),
+ PLL_4650_RATE(24 * MHZ, 259617187, 63, 3, 1, 1950, 0, 20, 1),
+ PLL_4650_RATE(24 * MHZ, 110000000, 53, 3, 2, 2048, 0, 17, 0),
+ PLL_4650_RATE(24 * MHZ, 55360351, 53, 3, 3, 2417, 0, 17, 0),
{ /* sentinel */ }
};
static const struct samsung_pll_rate_table exynos4x12_apll_rates[] __initconst = {
- PLL_35XX_RATE(1704000000, 213, 3, 0),
- PLL_35XX_RATE(1600000000, 200, 3, 0),
- PLL_35XX_RATE(1500000000, 250, 4, 0),
- PLL_35XX_RATE(1400000000, 175, 3, 0),
- PLL_35XX_RATE(1300000000, 325, 6, 0),
- PLL_35XX_RATE(1200000000, 200, 4, 0),
- PLL_35XX_RATE(1100000000, 275, 6, 0),
- PLL_35XX_RATE(1000000000, 125, 3, 0),
- PLL_35XX_RATE( 900000000, 150, 4, 0),
- PLL_35XX_RATE( 800000000, 100, 3, 0),
- PLL_35XX_RATE( 700000000, 175, 3, 1),
- PLL_35XX_RATE( 600000000, 200, 4, 1),
- PLL_35XX_RATE( 500000000, 125, 3, 1),
- PLL_35XX_RATE( 400000000, 100, 3, 1),
- PLL_35XX_RATE( 300000000, 200, 4, 2),
- PLL_35XX_RATE( 200000000, 100, 3, 2),
+ PLL_35XX_RATE(24 * MHZ, 1704000000, 213, 3, 0),
+ PLL_35XX_RATE(24 * MHZ, 1600000000, 200, 3, 0),
+ PLL_35XX_RATE(24 * MHZ, 1500000000, 250, 4, 0),
+ PLL_35XX_RATE(24 * MHZ, 1400000000, 175, 3, 0),
+ PLL_35XX_RATE(24 * MHZ, 1300000000, 325, 6, 0),
+ PLL_35XX_RATE(24 * MHZ, 1200000000, 200, 4, 0),
+ PLL_35XX_RATE(24 * MHZ, 1100000000, 275, 6, 0),
+ PLL_35XX_RATE(24 * MHZ, 1000000000, 125, 3, 0),
+ PLL_35XX_RATE(24 * MHZ, 900000000, 150, 4, 0),
+ PLL_35XX_RATE(24 * MHZ, 800000000, 100, 3, 0),
+ PLL_35XX_RATE(24 * MHZ, 700000000, 175, 3, 1),
+ PLL_35XX_RATE(24 * MHZ, 600000000, 200, 4, 1),
+ PLL_35XX_RATE(24 * MHZ, 500000000, 125, 3, 1),
+ PLL_35XX_RATE(24 * MHZ, 400000000, 100, 3, 1),
+ PLL_35XX_RATE(24 * MHZ, 300000000, 200, 4, 2),
+ PLL_35XX_RATE(24 * MHZ, 200000000, 100, 3, 2),
{ /* sentinel */ }
};
static const struct samsung_pll_rate_table exynos4x12_epll_rates[] __initconst = {
- PLL_36XX_RATE(192000000, 48, 3, 1, 0),
- PLL_36XX_RATE(180633605, 45, 3, 1, 10381),
- PLL_36XX_RATE(180000000, 45, 3, 1, 0),
- PLL_36XX_RATE( 73727996, 73, 3, 3, 47710),
- PLL_36XX_RATE( 67737602, 90, 4, 3, 20762),
- PLL_36XX_RATE( 49151992, 49, 3, 3, 9961),
- PLL_36XX_RATE( 45158401, 45, 3, 3, 10381),
+ PLL_36XX_RATE(24 * MHZ, 196608001, 197, 3, 3, -25690),
+ PLL_36XX_RATE(24 * MHZ, 192000000, 48, 3, 1, 0),
+ PLL_36XX_RATE(24 * MHZ, 180633605, 45, 3, 1, 10381),
+ PLL_36XX_RATE(24 * MHZ, 180000000, 45, 3, 1, 0),
+ PLL_36XX_RATE(24 * MHZ, 73727996, 73, 3, 3, 47710),
+ PLL_36XX_RATE(24 * MHZ, 67737602, 90, 4, 3, 20762),
+ PLL_36XX_RATE(24 * MHZ, 49151992, 49, 3, 3, 9961),
+ PLL_36XX_RATE(24 * MHZ, 45158401, 45, 3, 3, 10381),
{ /* sentinel */ }
};
static const struct samsung_pll_rate_table exynos4x12_vpll_rates[] __initconst = {
- PLL_36XX_RATE(533000000, 133, 3, 1, 16384),
- PLL_36XX_RATE(440000000, 110, 3, 1, 0),
- PLL_36XX_RATE(350000000, 175, 3, 2, 0),
- PLL_36XX_RATE(266000000, 133, 3, 2, 0),
- PLL_36XX_RATE(160000000, 160, 3, 3, 0),
- PLL_36XX_RATE(106031250, 53, 3, 2, 1024),
- PLL_36XX_RATE( 53015625, 53, 3, 3, 1024),
+ PLL_36XX_RATE(24 * MHZ, 533000000, 133, 3, 1, 16384),
+ PLL_36XX_RATE(24 * MHZ, 440000000, 110, 3, 1, 0),
+ PLL_36XX_RATE(24 * MHZ, 350000000, 175, 3, 2, 0),
+ PLL_36XX_RATE(24 * MHZ, 266000000, 133, 3, 2, 0),
+ PLL_36XX_RATE(24 * MHZ, 160000000, 160, 3, 3, 0),
+ PLL_36XX_RATE(24 * MHZ, 106031250, 53, 3, 2, 1024),
+ PLL_36XX_RATE(24 * MHZ, 53015625, 53, 3, 3, 1024),
{ /* sentinel */ }
};
diff --git a/drivers/clk/samsung/clk-exynos5-subcmu.c b/drivers/clk/samsung/clk-exynos5-subcmu.c
new file mode 100644
index 000000000000..93306283d764
--- /dev/null
+++ b/drivers/clk/samsung/clk-exynos5-subcmu.c
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2018 Samsung Electronics Co., Ltd.
+// Author: Marek Szyprowski <m.szyprowski@samsung.com>
+// Common Clock Framework support for Exynos5 power-domain dependent clocks
+
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+
+#include "clk.h"
+#include "clk-exynos5-subcmu.h"
+
+static struct samsung_clk_provider *ctx;
+static const struct exynos5_subcmu_info *cmu;
+static int nr_cmus;
+
+static void exynos5_subcmu_clk_save(void __iomem *base,
+ struct exynos5_subcmu_reg_dump *rd,
+ unsigned int num_regs)
+{
+ for (; num_regs > 0; --num_regs, ++rd) {
+ rd->save = readl(base + rd->offset);
+ writel((rd->save & ~rd->mask) | rd->value, base + rd->offset);
+ rd->save &= rd->mask;
+ }
+};
+
+static void exynos5_subcmu_clk_restore(void __iomem *base,
+ struct exynos5_subcmu_reg_dump *rd,
+ unsigned int num_regs)
+{
+ for (; num_regs > 0; --num_regs, ++rd)
+ writel((readl(base + rd->offset) & ~rd->mask) | rd->save,
+ base + rd->offset);
+}
+
+static void exynos5_subcmu_defer_gate(struct samsung_clk_provider *ctx,
+ const struct samsung_gate_clock *list, int nr_clk)
+{
+ while (nr_clk--)
+ samsung_clk_add_lookup(ctx, ERR_PTR(-EPROBE_DEFER), list++->id);
+}
+
+/*
+ * Pass the needed clock provider context and register sub-CMU clocks
+ *
+ * NOTE: This function has to be called from the main, OF_CLK_DECLARE-
+ * initialized clock provider driver. This happens very early during boot
+ * process. Then this driver, during core_initcall registers two platform
+ * drivers: one which binds to the same device-tree node as OF_CLK_DECLARE
+ * driver and second, for handling its per-domain child-devices. Those
+ * platform drivers are bound to their devices a bit later in arch_initcall,
+ * when OF-core populates all device-tree nodes.
+ */
+void exynos5_subcmus_init(struct samsung_clk_provider *_ctx, int _nr_cmus,
+ const struct exynos5_subcmu_info *_cmu)
+{
+ ctx = _ctx;
+ cmu = _cmu;
+ nr_cmus = _nr_cmus;
+
+ for (; _nr_cmus--; _cmu++) {
+ exynos5_subcmu_defer_gate(ctx, _cmu->gate_clks,
+ _cmu->nr_gate_clks);
+ exynos5_subcmu_clk_save(ctx->reg_base, _cmu->suspend_regs,
+ _cmu->nr_suspend_regs);
+ }
+}
+
+static int __maybe_unused exynos5_subcmu_suspend(struct device *dev)
+{
+ struct exynos5_subcmu_info *info = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctx->lock, flags);
+ exynos5_subcmu_clk_save(ctx->reg_base, info->suspend_regs,
+ info->nr_suspend_regs);
+ spin_unlock_irqrestore(&ctx->lock, flags);
+
+ return 0;
+}
+
+static int __maybe_unused exynos5_subcmu_resume(struct device *dev)
+{
+ struct exynos5_subcmu_info *info = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctx->lock, flags);
+ exynos5_subcmu_clk_restore(ctx->reg_base, info->suspend_regs,
+ info->nr_suspend_regs);
+ spin_unlock_irqrestore(&ctx->lock, flags);
+
+ return 0;
+}
+
+static int __init exynos5_subcmu_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct exynos5_subcmu_info *info = dev_get_drvdata(dev);
+
+ pm_runtime_set_suspended(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_get(dev);
+
+ ctx->dev = dev;
+ samsung_clk_register_div(ctx, info->div_clks, info->nr_div_clks);
+ samsung_clk_register_gate(ctx, info->gate_clks, info->nr_gate_clks);
+ ctx->dev = NULL;
+
+ pm_runtime_put_sync(dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops exynos5_subcmu_pm_ops = {
+ SET_RUNTIME_PM_OPS(exynos5_subcmu_suspend,
+ exynos5_subcmu_resume, NULL)
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+};
+
+static struct platform_driver exynos5_subcmu_driver __refdata = {
+ .driver = {
+ .name = "exynos5-subcmu",
+ .suppress_bind_attrs = true,
+ .pm = &exynos5_subcmu_pm_ops,
+ },
+ .probe = exynos5_subcmu_probe,
+};
+
+static int __init exynos5_clk_register_subcmu(struct device *parent,
+ const struct exynos5_subcmu_info *info,
+ struct device_node *pd_node)
+{
+ struct of_phandle_args genpdspec = { .np = pd_node };
+ struct platform_device *pdev;
+
+ pdev = platform_device_alloc(info->pd_name, -1);
+ pdev->dev.parent = parent;
+ pdev->driver_override = "exynos5-subcmu";
+ platform_set_drvdata(pdev, (void *)info);
+ of_genpd_add_device(&genpdspec, &pdev->dev);
+ platform_device_add(pdev);
+
+ return 0;
+}
+
+static int __init exynos5_clk_probe(struct platform_device *pdev)
+{
+ struct device_node *np;
+ const char *name;
+ int i;
+
+ for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") {
+ if (of_property_read_string(np, "label", &name) < 0)
+ continue;
+ for (i = 0; i < nr_cmus; i++)
+ if (strcmp(cmu[i].pd_name, name) == 0)
+ exynos5_clk_register_subcmu(&pdev->dev,
+ &cmu[i], np);
+ }
+ return 0;
+}
+
+static const struct of_device_id exynos5_clk_of_match[] = {
+ { .compatible = "samsung,exynos5250-clock", },
+ { .compatible = "samsung,exynos5420-clock", },
+ { .compatible = "samsung,exynos5800-clock", },
+ { },
+};
+
+static struct platform_driver exynos5_clk_driver __refdata = {
+ .driver = {
+ .name = "exynos5-clock",
+ .of_match_table = exynos5_clk_of_match,
+ .suppress_bind_attrs = true,
+ },
+ .probe = exynos5_clk_probe,
+};
+
+static int __init exynos5_clk_drv_init(void)
+{
+ platform_driver_register(&exynos5_clk_driver);
+ platform_driver_register(&exynos5_subcmu_driver);
+ return 0;
+}
+core_initcall(exynos5_clk_drv_init);
diff --git a/drivers/clk/samsung/clk-exynos5-subcmu.h b/drivers/clk/samsung/clk-exynos5-subcmu.h
new file mode 100644
index 000000000000..755ee8aaa3de
--- /dev/null
+++ b/drivers/clk/samsung/clk-exynos5-subcmu.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __CLK_EXYNOS5_SUBCMU_H
+#define __CLK_EXYNOS5_SUBCMU_H
+
+struct exynos5_subcmu_reg_dump {
+ u32 offset;
+ u32 value;
+ u32 mask;
+ u32 save;
+};
+
+struct exynos5_subcmu_info {
+ const struct samsung_div_clock *div_clks;
+ unsigned int nr_div_clks;
+ const struct samsung_gate_clock *gate_clks;
+ unsigned int nr_gate_clks;
+ struct exynos5_subcmu_reg_dump *suspend_regs;
+ unsigned int nr_suspend_regs;
+ const char *pd_name;
+};
+
+void exynos5_subcmus_init(struct samsung_clk_provider *ctx, int nr_cmus,
+ const struct exynos5_subcmu_info *cmu);
+
+#endif
diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c
index 9b073c98a891..347fd80c351b 100644
--- a/drivers/clk/samsung/clk-exynos5250.c
+++ b/drivers/clk/samsung/clk-exynos5250.c
@@ -18,6 +18,7 @@
#include "clk.h"
#include "clk-cpu.h"
+#include "clk-exynos5-subcmu.h"
#define APLL_LOCK 0x0
#define APLL_CON0 0x100
@@ -560,6 +561,8 @@ static const struct samsung_gate_clock exynos5250_gate_clks[] __initconst = {
0),
GATE(CLK_GSCL3, "gscl3", "mout_aclk266_gscl_sub", GATE_IP_GSCL, 3, 0,
0),
+ GATE(CLK_CAMIF_TOP, "camif_top", "mout_aclk266_gscl_sub",
+ GATE_IP_GSCL, 4, 0, 0),
GATE(CLK_GSCL_WA, "gscl_wa", "div_gscl_wa", GATE_IP_GSCL, 5, 0, 0),
GATE(CLK_GSCL_WB, "gscl_wb", "div_gscl_wb", GATE_IP_GSCL, 6, 0, 0),
GATE(CLK_SMMU_GSCL0, "smmu_gscl0", "mout_aclk266_gscl_sub",
@@ -570,18 +573,11 @@ static const struct samsung_gate_clock exynos5250_gate_clks[] __initconst = {
GATE_IP_GSCL, 9, 0, 0),
GATE(CLK_SMMU_GSCL3, "smmu_gscl3", "mout_aclk266_gscl_sub",
GATE_IP_GSCL, 10, 0, 0),
+ GATE(CLK_SMMU_FIMC_LITE0, "smmu_fimc_lite0", "mout_aclk266_gscl_sub",
+ GATE_IP_GSCL, 11, 0, 0),
+ GATE(CLK_SMMU_FIMC_LITE1, "smmu_fimc_lite1", "mout_aclk266_gscl_sub",
+ GATE_IP_GSCL, 12, 0, 0),
- GATE(CLK_FIMD1, "fimd1", "mout_aclk200_disp1_sub", GATE_IP_DISP1, 0, 0,
- 0),
- GATE(CLK_MIE1, "mie1", "mout_aclk200_disp1_sub", GATE_IP_DISP1, 1, 0,
- 0),
- GATE(CLK_DSIM0, "dsim0", "mout_aclk200_disp1_sub", GATE_IP_DISP1, 3, 0,
- 0),
- GATE(CLK_DP, "dp", "mout_aclk200_disp1_sub", GATE_IP_DISP1, 4, 0, 0),
- GATE(CLK_MIXER, "mixer", "mout_aclk200_disp1_sub", GATE_IP_DISP1, 5, 0,
- 0),
- GATE(CLK_HDMI, "hdmi", "mout_aclk200_disp1_sub", GATE_IP_DISP1, 6, 0,
- 0),
GATE(CLK_MFC, "mfc", "mout_aclk333_sub", GATE_IP_MFC, 0, 0, 0),
GATE(CLK_SMMU_MFCR, "smmu_mfcr", "mout_aclk333_sub", GATE_IP_MFC, 1, 0,
@@ -671,10 +667,6 @@ static const struct samsung_gate_clock exynos5250_gate_clks[] __initconst = {
GATE(CLK_WDT, "wdt", "div_aclk66", GATE_IP_PERIS, 19, 0, 0),
GATE(CLK_RTC, "rtc", "div_aclk66", GATE_IP_PERIS, 20, 0, 0),
GATE(CLK_TMU, "tmu", "div_aclk66", GATE_IP_PERIS, 21, 0, 0),
- GATE(CLK_SMMU_TV, "smmu_tv", "mout_aclk200_disp1_sub",
- GATE_IP_DISP1, 9, 0, 0),
- GATE(CLK_SMMU_FIMD1, "smmu_fimd1", "mout_aclk200_disp1_sub",
- GATE_IP_DISP1, 8, 0, 0),
GATE(CLK_SMMU_2D, "smmu_2d", "div_aclk200", GATE_IP_ACP, 7, 0, 0),
GATE(CLK_SMMU_FIMC_ISP, "smmu_fimc_isp", "mout_aclk_266_isp_sub",
GATE_IP_ISP0, 8, 0, 0),
@@ -698,48 +690,80 @@ static const struct samsung_gate_clock exynos5250_gate_clks[] __initconst = {
GATE_IP_ISP1, 7, 0, 0),
};
+static const struct samsung_gate_clock exynos5250_disp_gate_clks[] __initconst = {
+ GATE(CLK_FIMD1, "fimd1", "mout_aclk200_disp1_sub", GATE_IP_DISP1, 0, 0,
+ 0),
+ GATE(CLK_MIE1, "mie1", "mout_aclk200_disp1_sub", GATE_IP_DISP1, 1, 0,
+ 0),
+ GATE(CLK_DSIM0, "dsim0", "mout_aclk200_disp1_sub", GATE_IP_DISP1, 3, 0,
+ 0),
+ GATE(CLK_DP, "dp", "mout_aclk200_disp1_sub", GATE_IP_DISP1, 4, 0, 0),
+ GATE(CLK_MIXER, "mixer", "mout_aclk200_disp1_sub", GATE_IP_DISP1, 5, 0,
+ 0),
+ GATE(CLK_HDMI, "hdmi", "mout_aclk200_disp1_sub", GATE_IP_DISP1, 6, 0,
+ 0),
+ GATE(CLK_SMMU_TV, "smmu_tv", "mout_aclk200_disp1_sub",
+ GATE_IP_DISP1, 9, 0, 0),
+ GATE(CLK_SMMU_FIMD1, "smmu_fimd1", "mout_aclk200_disp1_sub",
+ GATE_IP_DISP1, 8, 0, 0),
+};
+
+static struct exynos5_subcmu_reg_dump exynos5250_disp_suspend_regs[] = {
+ { GATE_IP_DISP1, 0xffffffff, 0xffffffff }, /* DISP1 gates */
+ { SRC_TOP3, 0, BIT(4) }, /* MUX mout_aclk200_disp1_sub */
+ { SRC_TOP3, 0, BIT(6) }, /* MUX mout_aclk300_disp1_sub */
+};
+
+static const struct exynos5_subcmu_info exynos5250_disp_subcmu = {
+ .gate_clks = exynos5250_disp_gate_clks,
+ .nr_gate_clks = ARRAY_SIZE(exynos5250_disp_gate_clks),
+ .suspend_regs = exynos5250_disp_suspend_regs,
+ .nr_suspend_regs = ARRAY_SIZE(exynos5250_disp_suspend_regs),
+ .pd_name = "DISP1",
+};
+
static const struct samsung_pll_rate_table vpll_24mhz_tbl[] __initconst = {
/* sorted in descending order */
/* PLL_36XX_RATE(rate, m, p, s, k) */
- PLL_36XX_RATE(266000000, 266, 3, 3, 0),
+ PLL_36XX_RATE(24 * MHZ, 266000000, 266, 3, 3, 0),
/* Not in UM, but need for eDP on snow */
- PLL_36XX_RATE(70500000, 94, 2, 4, 0),
+ PLL_36XX_RATE(24 * MHZ, 70500000, 94, 2, 4, 0),
{ },
};
static const struct samsung_pll_rate_table epll_24mhz_tbl[] __initconst = {
/* sorted in descending order */
/* PLL_36XX_RATE(rate, m, p, s, k) */
- PLL_36XX_RATE(192000000, 64, 2, 2, 0),
- PLL_36XX_RATE(180633600, 90, 3, 2, 20762),
- PLL_36XX_RATE(180000000, 90, 3, 2, 0),
- PLL_36XX_RATE(73728000, 98, 2, 4, 19923),
- PLL_36XX_RATE(67737600, 90, 2, 4, 20762),
- PLL_36XX_RATE(49152000, 98, 3, 4, 19923),
- PLL_36XX_RATE(45158400, 90, 3, 4, 20762),
- PLL_36XX_RATE(32768000, 131, 3, 5, 4719),
+ PLL_36XX_RATE(24 * MHZ, 192000000, 64, 2, 2, 0),
+ PLL_36XX_RATE(24 * MHZ, 180633605, 90, 3, 2, 20762),
+ PLL_36XX_RATE(24 * MHZ, 180000000, 90, 3, 2, 0),
+ PLL_36XX_RATE(24 * MHZ, 73728000, 98, 2, 4, 19923),
+ PLL_36XX_RATE(24 * MHZ, 67737602, 90, 2, 4, 20762),
+ PLL_36XX_RATE(24 * MHZ, 49152000, 98, 3, 4, 19923),
+ PLL_36XX_RATE(24 * MHZ, 45158401, 90, 3, 4, 20762),
+ PLL_36XX_RATE(24 * MHZ, 32768001, 131, 3, 5, 4719),
{ },
};
static const struct samsung_pll_rate_table apll_24mhz_tbl[] __initconst = {
/* sorted in descending order */
- /* PLL_35XX_RATE(rate, m, p, s) */
- PLL_35XX_RATE(1700000000, 425, 6, 0),
- PLL_35XX_RATE(1600000000, 200, 3, 0),
- PLL_35XX_RATE(1500000000, 250, 4, 0),
- PLL_35XX_RATE(1400000000, 175, 3, 0),
- PLL_35XX_RATE(1300000000, 325, 6, 0),
- PLL_35XX_RATE(1200000000, 200, 4, 0),
- PLL_35XX_RATE(1100000000, 275, 6, 0),
- PLL_35XX_RATE(1000000000, 125, 3, 0),
- PLL_35XX_RATE(900000000, 150, 4, 0),
- PLL_35XX_RATE(800000000, 100, 3, 0),
- PLL_35XX_RATE(700000000, 175, 3, 1),
- PLL_35XX_RATE(600000000, 200, 4, 1),
- PLL_35XX_RATE(500000000, 125, 3, 1),
- PLL_35XX_RATE(400000000, 100, 3, 1),
- PLL_35XX_RATE(300000000, 200, 4, 2),
- PLL_35XX_RATE(200000000, 100, 3, 2),
+ /* PLL_35XX_RATE(fin, rate, m, p, s) */
+ PLL_35XX_RATE(24 * MHZ, 1700000000, 425, 6, 0),
+ PLL_35XX_RATE(24 * MHZ, 1600000000, 200, 3, 0),
+ PLL_35XX_RATE(24 * MHZ, 1500000000, 250, 4, 0),
+ PLL_35XX_RATE(24 * MHZ, 1400000000, 175, 3, 0),
+ PLL_35XX_RATE(24 * MHZ, 1300000000, 325, 6, 0),
+ PLL_35XX_RATE(24 * MHZ, 1200000000, 200, 4, 0),
+ PLL_35XX_RATE(24 * MHZ, 1100000000, 275, 6, 0),
+ PLL_35XX_RATE(24 * MHZ, 1000000000, 125, 3, 0),
+ PLL_35XX_RATE(24 * MHZ, 900000000, 150, 4, 0),
+ PLL_35XX_RATE(24 * MHZ, 800000000, 100, 3, 0),
+ PLL_35XX_RATE(24 * MHZ, 700000000, 175, 3, 1),
+ PLL_35XX_RATE(24 * MHZ, 600000000, 200, 4, 1),
+ PLL_35XX_RATE(24 * MHZ, 500000000, 125, 3, 1),
+ PLL_35XX_RATE(24 * MHZ, 400000000, 100, 3, 1),
+ PLL_35XX_RATE(24 * MHZ, 300000000, 200, 4, 2),
+ PLL_35XX_RATE(24 * MHZ, 200000000, 100, 3, 2),
};
static struct samsung_pll_clock exynos5250_plls[nr_plls] __initdata = {
@@ -859,10 +883,11 @@ static void __init exynos5250_clk_init(struct device_node *np)
__raw_writel(tmp, reg_base + PWR_CTRL2);
exynos5250_clk_sleep_init();
+ exynos5_subcmus_init(ctx, 1, &exynos5250_disp_subcmu);
samsung_clk_of_add_provider(np, ctx);
pr_info("Exynos5250: clock setup completed, armclk=%ld\n",
_get_rate("div_arm2"));
}
-CLK_OF_DECLARE(exynos5250_clk, "samsung,exynos5250-clock", exynos5250_clk_init);
+CLK_OF_DECLARE_DRIVER(exynos5250_clk, "samsung,exynos5250-clock", exynos5250_clk_init);
diff --git a/drivers/clk/samsung/clk-exynos5260.c b/drivers/clk/samsung/clk-exynos5260.c
index fd1d9bfc151b..2cc2583abd87 100644
--- a/drivers/clk/samsung/clk-exynos5260.c
+++ b/drivers/clk/samsung/clk-exynos5260.c
@@ -23,57 +23,57 @@
* DISP_PLL, EGL_PLL, KFC_PLL, MEM_PLL, BUS_PLL, MEDIA_PLL, G3D_PLL.
*/
static const struct samsung_pll_rate_table pll2550_24mhz_tbl[] __initconst = {
- PLL_35XX_RATE(1700000000, 425, 6, 0),
- PLL_35XX_RATE(1600000000, 200, 3, 0),
- PLL_35XX_RATE(1500000000, 250, 4, 0),
- PLL_35XX_RATE(1400000000, 175, 3, 0),
- PLL_35XX_RATE(1300000000, 325, 6, 0),
- PLL_35XX_RATE(1200000000, 400, 4, 1),
- PLL_35XX_RATE(1100000000, 275, 3, 1),
- PLL_35XX_RATE(1000000000, 250, 3, 1),
- PLL_35XX_RATE(933000000, 311, 4, 1),
- PLL_35XX_RATE(900000000, 300, 4, 1),
- PLL_35XX_RATE(800000000, 200, 3, 1),
- PLL_35XX_RATE(733000000, 733, 12, 1),
- PLL_35XX_RATE(700000000, 175, 3, 1),
- PLL_35XX_RATE(667000000, 667, 12, 1),
- PLL_35XX_RATE(633000000, 211, 4, 1),
- PLL_35XX_RATE(620000000, 310, 3, 2),
- PLL_35XX_RATE(600000000, 400, 4, 2),
- PLL_35XX_RATE(543000000, 362, 4, 2),
- PLL_35XX_RATE(533000000, 533, 6, 2),
- PLL_35XX_RATE(500000000, 250, 3, 2),
- PLL_35XX_RATE(450000000, 300, 4, 2),
- PLL_35XX_RATE(400000000, 200, 3, 2),
- PLL_35XX_RATE(350000000, 175, 3, 2),
- PLL_35XX_RATE(300000000, 400, 4, 3),
- PLL_35XX_RATE(266000000, 266, 3, 3),
- PLL_35XX_RATE(200000000, 200, 3, 3),
- PLL_35XX_RATE(160000000, 160, 3, 3),
+ PLL_35XX_RATE(24 * MHZ, 1700000000, 425, 6, 0),
+ PLL_35XX_RATE(24 * MHZ, 1600000000, 200, 3, 0),
+ PLL_35XX_RATE(24 * MHZ, 1500000000, 250, 4, 0),
+ PLL_35XX_RATE(24 * MHZ, 1400000000, 175, 3, 0),
+ PLL_35XX_RATE(24 * MHZ, 1300000000, 325, 6, 0),
+ PLL_35XX_RATE(24 * MHZ, 1200000000, 400, 4, 1),
+ PLL_35XX_RATE(24 * MHZ, 1100000000, 275, 3, 1),
+ PLL_35XX_RATE(24 * MHZ, 1000000000, 250, 3, 1),
+ PLL_35XX_RATE(24 * MHZ, 933000000, 311, 4, 1),
+ PLL_35XX_RATE(24 * MHZ, 900000000, 300, 4, 1),
+ PLL_35XX_RATE(24 * MHZ, 800000000, 200, 3, 1),
+ PLL_35XX_RATE(24 * MHZ, 733000000, 733, 12, 1),
+ PLL_35XX_RATE(24 * MHZ, 700000000, 175, 3, 1),
+ PLL_35XX_RATE(24 * MHZ, 667000000, 667, 12, 1),
+ PLL_35XX_RATE(24 * MHZ, 633000000, 211, 4, 1),
+ PLL_35XX_RATE(24 * MHZ, 620000000, 310, 3, 2),
+ PLL_35XX_RATE(24 * MHZ, 600000000, 400, 4, 2),
+ PLL_35XX_RATE(24 * MHZ, 543000000, 362, 4, 2),
+ PLL_35XX_RATE(24 * MHZ, 533000000, 533, 6, 2),
+ PLL_35XX_RATE(24 * MHZ, 500000000, 250, 3, 2),
+ PLL_35XX_RATE(24 * MHZ, 450000000, 300, 4, 2),
+ PLL_35XX_RATE(24 * MHZ, 400000000, 200, 3, 2),
+ PLL_35XX_RATE(24 * MHZ, 350000000, 175, 3, 2),
+ PLL_35XX_RATE(24 * MHZ, 300000000, 400, 4, 3),
+ PLL_35XX_RATE(24 * MHZ, 266000000, 266, 3, 3),
+ PLL_35XX_RATE(24 * MHZ, 200000000, 200, 3, 3),
+ PLL_35XX_RATE(24 * MHZ, 160000000, 160, 3, 3),
};
/*
* Applicable for 2650 Type PLL for AUD_PLL.
*/
static const struct samsung_pll_rate_table pll2650_24mhz_tbl[] __initconst = {
- PLL_36XX_RATE(1600000000, 200, 3, 0, 0),
- PLL_36XX_RATE(1200000000, 100, 2, 0, 0),
- PLL_36XX_RATE(1000000000, 250, 3, 1, 0),
- PLL_36XX_RATE(800000000, 200, 3, 1, 0),
- PLL_36XX_RATE(600000000, 100, 2, 1, 0),
- PLL_36XX_RATE(532000000, 266, 3, 2, 0),
- PLL_36XX_RATE(480000000, 160, 2, 2, 0),
- PLL_36XX_RATE(432000000, 144, 2, 2, 0),
- PLL_36XX_RATE(400000000, 200, 3, 2, 0),
- PLL_36XX_RATE(394073130, 459, 7, 2, 49282),
- PLL_36XX_RATE(333000000, 111, 2, 2, 0),
- PLL_36XX_RATE(300000000, 100, 2, 2, 0),
- PLL_36XX_RATE(266000000, 266, 3, 3, 0),
- PLL_36XX_RATE(200000000, 200, 3, 3, 0),
- PLL_36XX_RATE(166000000, 166, 3, 3, 0),
- PLL_36XX_RATE(133000000, 266, 3, 4, 0),
- PLL_36XX_RATE(100000000, 200, 3, 4, 0),
- PLL_36XX_RATE(66000000, 176, 2, 5, 0),
+ PLL_36XX_RATE(24 * MHZ, 1600000000, 200, 3, 0, 0),
+ PLL_36XX_RATE(24 * MHZ, 1200000000, 100, 2, 0, 0),
+ PLL_36XX_RATE(24 * MHZ, 1000000000, 250, 3, 1, 0),
+ PLL_36XX_RATE(24 * MHZ, 800000000, 200, 3, 1, 0),
+ PLL_36XX_RATE(24 * MHZ, 600000000, 100, 2, 1, 0),
+ PLL_36XX_RATE(24 * MHZ, 532000000, 266, 3, 2, 0),
+ PLL_36XX_RATE(24 * MHZ, 480000000, 160, 2, 2, 0),
+ PLL_36XX_RATE(24 * MHZ, 432000000, 144, 2, 2, 0),
+ PLL_36XX_RATE(24 * MHZ, 400000000, 200, 3, 2, 0),
+ PLL_36XX_RATE(24 * MHZ, 394073128, 459, 7, 2, 49282),
+ PLL_36XX_RATE(24 * MHZ, 333000000, 111, 2, 2, 0),
+ PLL_36XX_RATE(24 * MHZ, 300000000, 100, 2, 2, 0),
+ PLL_36XX_RATE(24 * MHZ, 266000000, 266, 3, 3, 0),
+ PLL_36XX_RATE(24 * MHZ, 200000000, 200, 3, 3, 0),
+ PLL_36XX_RATE(24 * MHZ, 166000000, 166, 3, 3, 0),
+ PLL_36XX_RATE(24 * MHZ, 133000000, 266, 3, 4, 0),
+ PLL_36XX_RATE(24 * MHZ, 100000000, 200, 3, 4, 0),
+ PLL_36XX_RATE(24 * MHZ, 66000000, 176, 2, 5, 0),
};
/* CMU_AUD */
diff --git a/drivers/clk/samsung/clk-exynos5410.c b/drivers/clk/samsung/clk-exynos5410.c
index fc471a49e8f4..0a0b09591e6f 100644
--- a/drivers/clk/samsung/clk-exynos5410.c
+++ b/drivers/clk/samsung/clk-exynos5410.c
@@ -226,16 +226,16 @@ static const struct samsung_gate_clock exynos5410_gate_clks[] __initconst = {
};
static const struct samsung_pll_rate_table exynos5410_pll2550x_24mhz_tbl[] __initconst = {
- PLL_36XX_RATE(400000000U, 200, 3, 2, 0),
- PLL_36XX_RATE(333000000U, 111, 2, 2, 0),
- PLL_36XX_RATE(300000000U, 100, 2, 2, 0),
- PLL_36XX_RATE(266000000U, 266, 3, 3, 0),
- PLL_36XX_RATE(200000000U, 200, 3, 3, 0),
- PLL_36XX_RATE(192000000U, 192, 3, 3, 0),
- PLL_36XX_RATE(166000000U, 166, 3, 3, 0),
- PLL_36XX_RATE(133000000U, 266, 3, 4, 0),
- PLL_36XX_RATE(100000000U, 200, 3, 4, 0),
- PLL_36XX_RATE(66000000U, 176, 2, 5, 0),
+ PLL_36XX_RATE(24 * MHZ, 400000000U, 200, 3, 2, 0),
+ PLL_36XX_RATE(24 * MHZ, 333000000U, 111, 2, 2, 0),
+ PLL_36XX_RATE(24 * MHZ, 300000000U, 100, 2, 2, 0),
+ PLL_36XX_RATE(24 * MHZ, 266000000U, 266, 3, 3, 0),
+ PLL_36XX_RATE(24 * MHZ, 200000000U, 200, 3, 3, 0),
+ PLL_36XX_RATE(24 * MHZ, 192000000U, 192, 3, 3, 0),
+ PLL_36XX_RATE(24 * MHZ, 166000000U, 166, 3, 3, 0),
+ PLL_36XX_RATE(24 * MHZ, 133000000U, 266, 3, 4, 0),
+ PLL_36XX_RATE(24 * MHZ, 100000000U, 200, 3, 4, 0),
+ PLL_36XX_RATE(24 * MHZ, 66000000U, 176, 2, 5, 0),
};
static struct samsung_pll_clock exynos5410_plls[nr_plls] __initdata = {
diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c
index 45d34f601e9e..95e1bf69449b 100644
--- a/drivers/clk/samsung/clk-exynos5420.c
+++ b/drivers/clk/samsung/clk-exynos5420.c
@@ -19,6 +19,7 @@
#include "clk.h"
#include "clk-cpu.h"
+#include "clk-exynos5-subcmu.h"
#define APLL_LOCK 0x0
#define APLL_CON0 0x100
@@ -620,7 +621,8 @@ static const struct samsung_mux_clock exynos5420_mux_clks[] __initconst = {
MUX(CLK_MOUT_MX_MSPLL_CCORE, "mout_mx_mspll_ccore",
mout_group5_5800_p, SRC_TOP7, 16, 2),
- MUX(0, "mout_mau_epll_clk", mout_mau_epll_clk_p, SRC_TOP7, 20, 2),
+ MUX_F(0, "mout_mau_epll_clk", mout_mau_epll_clk_p, SRC_TOP7, 20, 2,
+ CLK_SET_RATE_PARENT, 0),
MUX(0, "mout_fimd1", mout_group3_p, SRC_DISP10, 4, 1),
};
@@ -863,7 +865,6 @@ static const struct samsung_div_clock exynos5x_div_clks[] __initconst = {
DIV(0, "dout_mipi1", "mout_mipi1", DIV_DISP10, 16, 8),
DIV(0, "dout_dp1", "mout_dp1", DIV_DISP10, 24, 4),
DIV(CLK_DOUT_PIXEL, "dout_hdmi_pixel", "mout_pixel", DIV_DISP10, 28, 4),
- DIV(0, "dout_disp1_blk", "aclk200_disp1", DIV2_RATIO0, 16, 2),
DIV(CLK_DOUT_ACLK400_DISP1, "dout_aclk400_disp1",
"mout_aclk400_disp1", DIV_TOP2, 4, 3),
@@ -912,8 +913,6 @@ static const struct samsung_div_clock exynos5x_div_clks[] __initconst = {
DIV(0, "dout_spi1", "mout_spi1", DIV_PERIC1, 24, 4),
DIV(0, "dout_spi2", "mout_spi2", DIV_PERIC1, 28, 4),
- /* Mfc Block */
- DIV(0, "dout_mfc_blk", "mout_user_aclk333", DIV4_RATIO, 0, 2),
/* PCM */
DIV(0, "dout_pcm1", "dout_audio1", DIV_PERIC2, 16, 8),
@@ -932,8 +931,6 @@ static const struct samsung_div_clock exynos5x_div_clks[] __initconst = {
DIV(0, "dout_spi2_pre", "dout_spi2", DIV_PERIC4, 24, 8),
/* GSCL Block */
- DIV(0, "dout_gscl_blk_300", "mout_user_aclk300_gscl",
- DIV2_RATIO0, 4, 2),
DIV(0, "dout_gscl_blk_333", "aclk333_432_gscl", DIV2_RATIO0, 6, 2),
/* MSCL Block */
@@ -1190,8 +1187,6 @@ static const struct samsung_gate_clock exynos5x_gate_clks[] __initconst = {
GATE(CLK_SCLK_GSCL_WB, "sclk_gscl_wb", "mout_user_aclk333_432_gscl",
GATE_TOP_SCLK_GSCL, 7, 0, 0),
- GATE(CLK_GSCL0, "gscl0", "aclk300_gscl", GATE_IP_GSCL0, 0, 0, 0),
- GATE(CLK_GSCL1, "gscl1", "aclk300_gscl", GATE_IP_GSCL0, 1, 0, 0),
GATE(CLK_FIMC_3AA, "fimc_3aa", "aclk333_432_gscl",
GATE_IP_GSCL0, 4, 0, 0),
GATE(CLK_FIMC_LITE0, "fimc_lite0", "aclk333_432_gscl",
@@ -1205,10 +1200,6 @@ static const struct samsung_gate_clock exynos5x_gate_clks[] __initconst = {
GATE_IP_GSCL1, 3, 0, 0),
GATE(CLK_SMMU_FIMCL1, "smmu_fimcl1", "dout_gscl_blk_333",
GATE_IP_GSCL1, 4, 0, 0),
- GATE(CLK_SMMU_GSCL0, "smmu_gscl0", "dout_gscl_blk_300",
- GATE_IP_GSCL1, 6, 0, 0),
- GATE(CLK_SMMU_GSCL1, "smmu_gscl1", "dout_gscl_blk_300",
- GATE_IP_GSCL1, 7, 0, 0),
GATE(CLK_GSCL_WA, "gscl_wa", "sclk_gscl_wa", GATE_IP_GSCL1, 12, 0, 0),
GATE(CLK_GSCL_WB, "gscl_wb", "sclk_gscl_wb", GATE_IP_GSCL1, 13, 0, 0),
GATE(CLK_SMMU_FIMCL3, "smmu_fimcl3,", "dout_gscl_blk_333",
@@ -1227,18 +1218,6 @@ static const struct samsung_gate_clock exynos5x_gate_clks[] __initconst = {
GATE(CLK_SMMU_MSCL2, "smmu_mscl2", "dout_mscl_blk",
GATE_IP_MSCL, 10, 0, 0),
- GATE(CLK_FIMD1, "fimd1", "aclk300_disp1", GATE_IP_DISP1, 0, 0, 0),
- GATE(CLK_DSIM1, "dsim1", "aclk200_disp1", GATE_IP_DISP1, 3, 0, 0),
- GATE(CLK_DP1, "dp1", "aclk200_disp1", GATE_IP_DISP1, 4, 0, 0),
- GATE(CLK_MIXER, "mixer", "aclk200_disp1", GATE_IP_DISP1, 5, 0, 0),
- GATE(CLK_HDMI, "hdmi", "aclk200_disp1", GATE_IP_DISP1, 6, 0, 0),
- GATE(CLK_SMMU_FIMD1M0, "smmu_fimd1m0", "dout_disp1_blk",
- GATE_IP_DISP1, 7, 0, 0),
- GATE(CLK_SMMU_FIMD1M1, "smmu_fimd1m1", "dout_disp1_blk",
- GATE_IP_DISP1, 8, 0, 0),
- GATE(CLK_SMMU_MIXER, "smmu_mixer", "aclk200_disp1",
- GATE_IP_DISP1, 9, 0, 0),
-
/* ISP */
GATE(CLK_SCLK_UART_ISP, "sclk_uart_isp", "dout_uart_isp",
GATE_TOP_SCLK_ISP, 0, CLK_SET_RATE_PARENT, 0),
@@ -1255,48 +1234,138 @@ static const struct samsung_gate_clock exynos5x_gate_clks[] __initconst = {
GATE(CLK_SCLK_ISP_SENSOR2, "sclk_isp_sensor2", "dout_isp_sensor2",
GATE_TOP_SCLK_ISP, 12, CLK_SET_RATE_PARENT, 0),
+ GATE(CLK_G3D, "g3d", "mout_user_aclk_g3d", GATE_IP_G3D, 9, 0, 0),
+};
+
+static const struct samsung_div_clock exynos5x_disp_div_clks[] __initconst = {
+ DIV(0, "dout_disp1_blk", "aclk200_disp1", DIV2_RATIO0, 16, 2),
+};
+
+static const struct samsung_gate_clock exynos5x_disp_gate_clks[] __initconst = {
+ GATE(CLK_FIMD1, "fimd1", "aclk300_disp1", GATE_IP_DISP1, 0, 0, 0),
+ GATE(CLK_DSIM1, "dsim1", "aclk200_disp1", GATE_IP_DISP1, 3, 0, 0),
+ GATE(CLK_DP1, "dp1", "aclk200_disp1", GATE_IP_DISP1, 4, 0, 0),
+ GATE(CLK_MIXER, "mixer", "aclk200_disp1", GATE_IP_DISP1, 5, 0, 0),
+ GATE(CLK_HDMI, "hdmi", "aclk200_disp1", GATE_IP_DISP1, 6, 0, 0),
+ GATE(CLK_SMMU_FIMD1M0, "smmu_fimd1m0", "dout_disp1_blk",
+ GATE_IP_DISP1, 7, 0, 0),
+ GATE(CLK_SMMU_FIMD1M1, "smmu_fimd1m1", "dout_disp1_blk",
+ GATE_IP_DISP1, 8, 0, 0),
+ GATE(CLK_SMMU_MIXER, "smmu_mixer", "aclk200_disp1",
+ GATE_IP_DISP1, 9, 0, 0),
+};
+
+static struct exynos5_subcmu_reg_dump exynos5x_disp_suspend_regs[] = {
+ { GATE_IP_DISP1, 0xffffffff, 0xffffffff }, /* DISP1 gates */
+ { SRC_TOP5, 0, BIT(0) }, /* MUX mout_user_aclk400_disp1 */
+ { SRC_TOP5, 0, BIT(24) }, /* MUX mout_user_aclk300_disp1 */
+ { SRC_TOP3, 0, BIT(8) }, /* MUX mout_user_aclk200_disp1 */
+ { DIV2_RATIO0, 0, 0x30000 }, /* DIV dout_disp1_blk */
+};
+
+static const struct samsung_div_clock exynos5x_gsc_div_clks[] __initconst = {
+ DIV(0, "dout_gscl_blk_300", "mout_user_aclk300_gscl",
+ DIV2_RATIO0, 4, 2),
+};
+
+static const struct samsung_gate_clock exynos5x_gsc_gate_clks[] __initconst = {
+ GATE(CLK_GSCL0, "gscl0", "aclk300_gscl", GATE_IP_GSCL0, 0, 0, 0),
+ GATE(CLK_GSCL1, "gscl1", "aclk300_gscl", GATE_IP_GSCL0, 1, 0, 0),
+ GATE(CLK_SMMU_GSCL0, "smmu_gscl0", "dout_gscl_blk_300",
+ GATE_IP_GSCL1, 6, 0, 0),
+ GATE(CLK_SMMU_GSCL1, "smmu_gscl1", "dout_gscl_blk_300",
+ GATE_IP_GSCL1, 7, 0, 0),
+};
+
+static struct exynos5_subcmu_reg_dump exynos5x_gsc_suspend_regs[] = {
+ { GATE_IP_GSCL0, 0x3, 0x3 }, /* GSC gates */
+ { GATE_IP_GSCL1, 0xc0, 0xc0 }, /* GSC gates */
+ { SRC_TOP5, 0, BIT(28) }, /* MUX mout_user_aclk300_gscl */
+ { DIV2_RATIO0, 0, 0x30 }, /* DIV dout_gscl_blk_300 */
+};
+
+static const struct samsung_div_clock exynos5x_mfc_div_clks[] __initconst = {
+ DIV(0, "dout_mfc_blk", "mout_user_aclk333", DIV4_RATIO, 0, 2),
+};
+
+static const struct samsung_gate_clock exynos5x_mfc_gate_clks[] __initconst = {
GATE(CLK_MFC, "mfc", "aclk333", GATE_IP_MFC, 0, 0, 0),
GATE(CLK_SMMU_MFCL, "smmu_mfcl", "dout_mfc_blk", GATE_IP_MFC, 1, 0, 0),
GATE(CLK_SMMU_MFCR, "smmu_mfcr", "dout_mfc_blk", GATE_IP_MFC, 2, 0, 0),
+};
- GATE(CLK_G3D, "g3d", "mout_user_aclk_g3d", GATE_IP_G3D, 9, 0, 0),
+static struct exynos5_subcmu_reg_dump exynos5x_mfc_suspend_regs[] = {
+ { GATE_IP_MFC, 0xffffffff, 0xffffffff }, /* MFC gates */
+ { SRC_TOP4, 0, BIT(28) }, /* MUX mout_user_aclk333 */
+ { DIV4_RATIO, 0, 0x3 }, /* DIV dout_mfc_blk */
+};
+
+static const struct exynos5_subcmu_info exynos5x_subcmus[] = {
+ {
+ .div_clks = exynos5x_disp_div_clks,
+ .nr_div_clks = ARRAY_SIZE(exynos5x_disp_div_clks),
+ .gate_clks = exynos5x_disp_gate_clks,
+ .nr_gate_clks = ARRAY_SIZE(exynos5x_disp_gate_clks),
+ .suspend_regs = exynos5x_disp_suspend_regs,
+ .nr_suspend_regs = ARRAY_SIZE(exynos5x_disp_suspend_regs),
+ .pd_name = "DISP",
+ }, {
+ .div_clks = exynos5x_gsc_div_clks,
+ .nr_div_clks = ARRAY_SIZE(exynos5x_gsc_div_clks),
+ .gate_clks = exynos5x_gsc_gate_clks,
+ .nr_gate_clks = ARRAY_SIZE(exynos5x_gsc_gate_clks),
+ .suspend_regs = exynos5x_gsc_suspend_regs,
+ .nr_suspend_regs = ARRAY_SIZE(exynos5x_gsc_suspend_regs),
+ .pd_name = "GSC",
+ }, {
+ .div_clks = exynos5x_mfc_div_clks,
+ .nr_div_clks = ARRAY_SIZE(exynos5x_mfc_div_clks),
+ .gate_clks = exynos5x_mfc_gate_clks,
+ .nr_gate_clks = ARRAY_SIZE(exynos5x_mfc_gate_clks),
+ .suspend_regs = exynos5x_mfc_suspend_regs,
+ .nr_suspend_regs = ARRAY_SIZE(exynos5x_mfc_suspend_regs),
+ .pd_name = "MFC",
+ },
};
static const struct samsung_pll_rate_table exynos5420_pll2550x_24mhz_tbl[] __initconst = {
- PLL_35XX_RATE(2000000000, 250, 3, 0),
- PLL_35XX_RATE(1900000000, 475, 6, 0),
- PLL_35XX_RATE(1800000000, 225, 3, 0),
- PLL_35XX_RATE(1700000000, 425, 6, 0),
- PLL_35XX_RATE(1600000000, 200, 3, 0),
- PLL_35XX_RATE(1500000000, 250, 4, 0),
- PLL_35XX_RATE(1400000000, 175, 3, 0),
- PLL_35XX_RATE(1300000000, 325, 6, 0),
- PLL_35XX_RATE(1200000000, 200, 2, 1),
- PLL_35XX_RATE(1100000000, 275, 3, 1),
- PLL_35XX_RATE(1000000000, 250, 3, 1),
- PLL_35XX_RATE(900000000, 150, 2, 1),
- PLL_35XX_RATE(800000000, 200, 3, 1),
- PLL_35XX_RATE(700000000, 175, 3, 1),
- PLL_35XX_RATE(600000000, 200, 2, 2),
- PLL_35XX_RATE(500000000, 250, 3, 2),
- PLL_35XX_RATE(400000000, 200, 3, 2),
- PLL_35XX_RATE(300000000, 200, 2, 3),
- PLL_35XX_RATE(200000000, 200, 3, 3),
+ PLL_35XX_RATE(24 * MHZ, 2000000000, 250, 3, 0),
+ PLL_35XX_RATE(24 * MHZ, 1900000000, 475, 6, 0),
+ PLL_35XX_RATE(24 * MHZ, 1800000000, 225, 3, 0),
+ PLL_35XX_RATE(24 * MHZ, 1700000000, 425, 6, 0),
+ PLL_35XX_RATE(24 * MHZ, 1600000000, 200, 3, 0),
+ PLL_35XX_RATE(24 * MHZ, 1500000000, 250, 4, 0),
+ PLL_35XX_RATE(24 * MHZ, 1400000000, 175, 3, 0),
+ PLL_35XX_RATE(24 * MHZ, 1300000000, 325, 6, 0),
+ PLL_35XX_RATE(24 * MHZ, 1200000000, 200, 2, 1),
+ PLL_35XX_RATE(24 * MHZ, 1100000000, 275, 3, 1),
+ PLL_35XX_RATE(24 * MHZ, 1000000000, 250, 3, 1),
+ PLL_35XX_RATE(24 * MHZ, 900000000, 150, 2, 1),
+ PLL_35XX_RATE(24 * MHZ, 800000000, 200, 3, 1),
+ PLL_35XX_RATE(24 * MHZ, 700000000, 175, 3, 1),
+ PLL_35XX_RATE(24 * MHZ, 600000000, 200, 2, 2),
+ PLL_35XX_RATE(24 * MHZ, 500000000, 250, 3, 2),
+ PLL_35XX_RATE(24 * MHZ, 400000000, 200, 3, 2),
+ PLL_35XX_RATE(24 * MHZ, 300000000, 200, 2, 3),
+ PLL_35XX_RATE(24 * MHZ, 200000000, 200, 3, 3),
};
static const struct samsung_pll_rate_table exynos5420_epll_24mhz_tbl[] = {
- PLL_36XX_RATE(600000000U, 100, 2, 1, 0),
- PLL_36XX_RATE(400000000U, 200, 3, 2, 0),
- PLL_36XX_RATE(393216003U, 197, 3, 2, -25690),
- PLL_36XX_RATE(361267218U, 301, 5, 2, 3671),
- PLL_36XX_RATE(200000000U, 200, 3, 3, 0),
- PLL_36XX_RATE(196608001U, 197, 3, 3, -25690),
- PLL_36XX_RATE(180633609U, 301, 5, 3, 3671),
- PLL_36XX_RATE(131072006U, 131, 3, 3, 4719),
- PLL_36XX_RATE(100000000U, 200, 3, 4, 0),
- PLL_36XX_RATE( 65536003U, 131, 3, 4, 4719),
- PLL_36XX_RATE( 49152000U, 197, 3, 5, -25690),
- PLL_36XX_RATE( 32768001U, 131, 3, 5, 4719),
+ PLL_36XX_RATE(24 * MHZ, 600000000U, 100, 2, 1, 0),
+ PLL_36XX_RATE(24 * MHZ, 400000000U, 200, 3, 2, 0),
+ PLL_36XX_RATE(24 * MHZ, 393216003U, 197, 3, 2, -25690),
+ PLL_36XX_RATE(24 * MHZ, 361267218U, 301, 5, 2, 3671),
+ PLL_36XX_RATE(24 * MHZ, 200000000U, 200, 3, 3, 0),
+ PLL_36XX_RATE(24 * MHZ, 196608001U, 197, 3, 3, -25690),
+ PLL_36XX_RATE(24 * MHZ, 180633609U, 301, 5, 3, 3671),
+ PLL_36XX_RATE(24 * MHZ, 131072006U, 131, 3, 3, 4719),
+ PLL_36XX_RATE(24 * MHZ, 100000000U, 200, 3, 4, 0),
+ PLL_36XX_RATE(24 * MHZ, 73728000U, 98, 2, 4, 19923),
+ PLL_36XX_RATE(24 * MHZ, 67737602U, 90, 2, 4, 20762),
+ PLL_36XX_RATE(24 * MHZ, 65536003U, 131, 3, 4, 4719),
+ PLL_36XX_RATE(24 * MHZ, 49152000U, 197, 3, 5, -25690),
+ PLL_36XX_RATE(24 * MHZ, 45158401U, 90, 3, 4, 20762),
+ PLL_36XX_RATE(24 * MHZ, 32768001U, 131, 3, 5, 4719),
};
static struct samsung_pll_clock exynos5x_plls[nr_plls] __initdata = {
@@ -1472,6 +1541,8 @@ static void __init exynos5x_clk_init(struct device_node *np,
exynos5420_kfcclk_d, ARRAY_SIZE(exynos5420_kfcclk_d), 0);
exynos5420_clk_sleep_init();
+ exynos5_subcmus_init(ctx, ARRAY_SIZE(exynos5x_subcmus),
+ exynos5x_subcmus);
samsung_clk_of_add_provider(np, ctx);
}
@@ -1480,10 +1551,12 @@ static void __init exynos5420_clk_init(struct device_node *np)
{
exynos5x_clk_init(np, EXYNOS5420);
}
-CLK_OF_DECLARE(exynos5420_clk, "samsung,exynos5420-clock", exynos5420_clk_init);
+CLK_OF_DECLARE_DRIVER(exynos5420_clk, "samsung,exynos5420-clock",
+ exynos5420_clk_init);
static void __init exynos5800_clk_init(struct device_node *np)
{
exynos5x_clk_init(np, EXYNOS5800);
}
-CLK_OF_DECLARE(exynos5800_clk, "samsung,exynos5800-clock", exynos5800_clk_init);
+CLK_OF_DECLARE_DRIVER(exynos5800_clk, "samsung,exynos5800-clock",
+ exynos5800_clk_init);
diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c
index db270908037a..5305ace514b2 100644
--- a/drivers/clk/samsung/clk-exynos5433.c
+++ b/drivers/clk/samsung/clk-exynos5433.c
@@ -703,68 +703,69 @@ static const struct samsung_gate_clock top_gate_clks[] __initconst = {
* & MPHY_PLL & G3D_PLL & DISP_PLL & ISP_PLL
*/
static const struct samsung_pll_rate_table exynos5433_pll_rates[] __initconst = {
- PLL_35XX_RATE(2500000000U, 625, 6, 0),
- PLL_35XX_RATE(2400000000U, 500, 5, 0),
- PLL_35XX_RATE(2300000000U, 575, 6, 0),
- PLL_35XX_RATE(2200000000U, 550, 6, 0),
- PLL_35XX_RATE(2100000000U, 350, 4, 0),
- PLL_35XX_RATE(2000000000U, 500, 6, 0),
- PLL_35XX_RATE(1900000000U, 475, 6, 0),
- PLL_35XX_RATE(1800000000U, 375, 5, 0),
- PLL_35XX_RATE(1700000000U, 425, 6, 0),
- PLL_35XX_RATE(1600000000U, 400, 6, 0),
- PLL_35XX_RATE(1500000000U, 250, 4, 0),
- PLL_35XX_RATE(1400000000U, 350, 6, 0),
- PLL_35XX_RATE(1332000000U, 222, 4, 0),
- PLL_35XX_RATE(1300000000U, 325, 6, 0),
- PLL_35XX_RATE(1200000000U, 500, 5, 1),
- PLL_35XX_RATE(1100000000U, 550, 6, 1),
- PLL_35XX_RATE(1086000000U, 362, 4, 1),
- PLL_35XX_RATE(1066000000U, 533, 6, 1),
- PLL_35XX_RATE(1000000000U, 500, 6, 1),
- PLL_35XX_RATE(933000000U, 311, 4, 1),
- PLL_35XX_RATE(921000000U, 307, 4, 1),
- PLL_35XX_RATE(900000000U, 375, 5, 1),
- PLL_35XX_RATE(825000000U, 275, 4, 1),
- PLL_35XX_RATE(800000000U, 400, 6, 1),
- PLL_35XX_RATE(733000000U, 733, 12, 1),
- PLL_35XX_RATE(700000000U, 175, 3, 1),
- PLL_35XX_RATE(667000000U, 222, 4, 1),
- PLL_35XX_RATE(633000000U, 211, 4, 1),
- PLL_35XX_RATE(600000000U, 500, 5, 2),
- PLL_35XX_RATE(552000000U, 460, 5, 2),
- PLL_35XX_RATE(550000000U, 550, 6, 2),
- PLL_35XX_RATE(543000000U, 362, 4, 2),
- PLL_35XX_RATE(533000000U, 533, 6, 2),
- PLL_35XX_RATE(500000000U, 500, 6, 2),
- PLL_35XX_RATE(444000000U, 370, 5, 2),
- PLL_35XX_RATE(420000000U, 350, 5, 2),
- PLL_35XX_RATE(400000000U, 400, 6, 2),
- PLL_35XX_RATE(350000000U, 350, 6, 2),
- PLL_35XX_RATE(333000000U, 222, 4, 2),
- PLL_35XX_RATE(300000000U, 500, 5, 3),
- PLL_35XX_RATE(278000000U, 556, 6, 3),
- PLL_35XX_RATE(266000000U, 532, 6, 3),
- PLL_35XX_RATE(250000000U, 500, 6, 3),
- PLL_35XX_RATE(200000000U, 400, 6, 3),
- PLL_35XX_RATE(166000000U, 332, 6, 3),
- PLL_35XX_RATE(160000000U, 320, 6, 3),
- PLL_35XX_RATE(133000000U, 532, 6, 4),
- PLL_35XX_RATE(100000000U, 400, 6, 4),
+ PLL_35XX_RATE(24 * MHZ, 2500000000U, 625, 6, 0),
+ PLL_35XX_RATE(24 * MHZ, 2400000000U, 500, 5, 0),
+ PLL_35XX_RATE(24 * MHZ, 2300000000U, 575, 6, 0),
+ PLL_35XX_RATE(24 * MHZ, 2200000000U, 550, 6, 0),
+ PLL_35XX_RATE(24 * MHZ, 2100000000U, 350, 4, 0),
+ PLL_35XX_RATE(24 * MHZ, 2000000000U, 500, 6, 0),
+ PLL_35XX_RATE(24 * MHZ, 1900000000U, 475, 6, 0),
+ PLL_35XX_RATE(24 * MHZ, 1800000000U, 375, 5, 0),
+ PLL_35XX_RATE(24 * MHZ, 1700000000U, 425, 6, 0),
+ PLL_35XX_RATE(24 * MHZ, 1600000000U, 400, 6, 0),
+ PLL_35XX_RATE(24 * MHZ, 1500000000U, 250, 4, 0),
+ PLL_35XX_RATE(24 * MHZ, 1400000000U, 350, 6, 0),
+ PLL_35XX_RATE(24 * MHZ, 1332000000U, 222, 4, 0),
+ PLL_35XX_RATE(24 * MHZ, 1300000000U, 325, 6, 0),
+ PLL_35XX_RATE(24 * MHZ, 1200000000U, 500, 5, 1),
+ PLL_35XX_RATE(24 * MHZ, 1100000000U, 550, 6, 1),
+ PLL_35XX_RATE(24 * MHZ, 1086000000U, 362, 4, 1),
+ PLL_35XX_RATE(24 * MHZ, 1066000000U, 533, 6, 1),
+ PLL_35XX_RATE(24 * MHZ, 1000000000U, 500, 6, 1),
+ PLL_35XX_RATE(24 * MHZ, 933000000U, 311, 4, 1),
+ PLL_35XX_RATE(24 * MHZ, 921000000U, 307, 4, 1),
+ PLL_35XX_RATE(24 * MHZ, 900000000U, 375, 5, 1),
+ PLL_35XX_RATE(24 * MHZ, 825000000U, 275, 4, 1),
+ PLL_35XX_RATE(24 * MHZ, 800000000U, 400, 6, 1),
+ PLL_35XX_RATE(24 * MHZ, 733000000U, 733, 12, 1),
+ PLL_35XX_RATE(24 * MHZ, 700000000U, 175, 3, 1),
+ PLL_35XX_RATE(24 * MHZ, 666000000U, 222, 4, 1),
+ PLL_35XX_RATE(24 * MHZ, 633000000U, 211, 4, 1),
+ PLL_35XX_RATE(24 * MHZ, 600000000U, 500, 5, 2),
+ PLL_35XX_RATE(24 * MHZ, 552000000U, 460, 5, 2),
+ PLL_35XX_RATE(24 * MHZ, 550000000U, 550, 6, 2),
+ PLL_35XX_RATE(24 * MHZ, 543000000U, 362, 4, 2),
+ PLL_35XX_RATE(24 * MHZ, 533000000U, 533, 6, 2),
+ PLL_35XX_RATE(24 * MHZ, 500000000U, 500, 6, 2),
+ PLL_35XX_RATE(24 * MHZ, 444000000U, 370, 5, 2),
+ PLL_35XX_RATE(24 * MHZ, 420000000U, 350, 5, 2),
+ PLL_35XX_RATE(24 * MHZ, 400000000U, 400, 6, 2),
+ PLL_35XX_RATE(24 * MHZ, 350000000U, 350, 6, 2),
+ PLL_35XX_RATE(24 * MHZ, 333000000U, 222, 4, 2),
+ PLL_35XX_RATE(24 * MHZ, 300000000U, 500, 5, 3),
+ PLL_35XX_RATE(24 * MHZ, 278000000U, 556, 6, 3),
+ PLL_35XX_RATE(24 * MHZ, 266000000U, 532, 6, 3),
+ PLL_35XX_RATE(24 * MHZ, 250000000U, 500, 6, 3),
+ PLL_35XX_RATE(24 * MHZ, 200000000U, 400, 6, 3),
+ PLL_35XX_RATE(24 * MHZ, 166000000U, 332, 6, 3),
+ PLL_35XX_RATE(24 * MHZ, 160000000U, 320, 6, 3),
+ PLL_35XX_RATE(24 * MHZ, 133000000U, 532, 6, 4),
+ PLL_35XX_RATE(24 * MHZ, 100000000U, 400, 6, 4),
{ /* sentinel */ }
};
/* AUD_PLL */
static const struct samsung_pll_rate_table exynos5433_aud_pll_rates[] __initconst = {
- PLL_36XX_RATE(400000000U, 200, 3, 2, 0),
- PLL_36XX_RATE(393216000U, 197, 3, 2, -25690),
- PLL_36XX_RATE(384000000U, 128, 2, 2, 0),
- PLL_36XX_RATE(368640000U, 246, 4, 2, -15729),
- PLL_36XX_RATE(361507200U, 181, 3, 2, -16148),
- PLL_36XX_RATE(338688000U, 113, 2, 2, -6816),
- PLL_36XX_RATE(294912000U, 98, 1, 3, 19923),
- PLL_36XX_RATE(288000000U, 96, 1, 3, 0),
- PLL_36XX_RATE(252000000U, 84, 1, 3, 0),
+ PLL_36XX_RATE(24 * MHZ, 400000000U, 200, 3, 2, 0),
+ PLL_36XX_RATE(24 * MHZ, 393216003U, 197, 3, 2, -25690),
+ PLL_36XX_RATE(24 * MHZ, 384000000U, 128, 2, 2, 0),
+ PLL_36XX_RATE(24 * MHZ, 368639991U, 246, 4, 2, -15729),
+ PLL_36XX_RATE(24 * MHZ, 361507202U, 181, 3, 2, -16148),
+ PLL_36XX_RATE(24 * MHZ, 338687988U, 113, 2, 2, -6816),
+ PLL_36XX_RATE(24 * MHZ, 294912002U, 98, 1, 3, 19923),
+ PLL_36XX_RATE(24 * MHZ, 288000000U, 96, 1, 3, 0),
+ PLL_36XX_RATE(24 * MHZ, 252000000U, 84, 1, 3, 0),
+ PLL_36XX_RATE(24 * MHZ, 196608001U, 197, 3, 3, -25690),
{ /* sentinel */ }
};
@@ -1672,7 +1673,7 @@ static const struct samsung_gate_clock peric_gate_clks[] __initconst = {
ENABLE_SCLK_PERIC, 11, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_IOCLK_I2S1_BCLK, "sclk_ioclk_i2s1_bclk",
"ioclk_i2s1_bclk_in", ENABLE_SCLK_PERIC, 10,
- CLK_SET_RATE_PARENT, 0),
+ CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0),
GATE(CLK_SCLK_SPDIF, "sclk_spdif", "sclk_spdif_peric",
ENABLE_SCLK_PERIC, 8, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_PCM1, "sclk_pcm1", "sclk_pcm1_peric",
@@ -5513,10 +5514,8 @@ static int __init exynos5433_cmu_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
reg_base = devm_ioremap_resource(dev, res);
- if (IS_ERR(reg_base)) {
- dev_err(dev, "failed to map registers\n");
+ if (IS_ERR(reg_base))
return PTR_ERR(reg_base);
- }
for (i = 0; i < info->nr_clk_ids; ++i)
ctx->clk_data.hws[i] = ERR_PTR(-ENOENT);
diff --git a/drivers/clk/samsung/clk-exynos7.c b/drivers/clk/samsung/clk-exynos7.c
index 5931a4140c3d..492d51691080 100644
--- a/drivers/clk/samsung/clk-exynos7.c
+++ b/drivers/clk/samsung/clk-exynos7.c
@@ -140,7 +140,7 @@ static const struct samsung_div_clock topc_div_clks[] __initconst = {
};
static const struct samsung_pll_rate_table pll1460x_24mhz_tbl[] __initconst = {
- PLL_36XX_RATE(491520000, 20, 1, 0, 31457),
+ PLL_36XX_RATE(24 * MHZ, 491519897, 20, 1, 0, 31457),
{},
};
diff --git a/drivers/clk/samsung/clk-pll.h b/drivers/clk/samsung/clk-pll.h
index 61eb8abbfd9c..ca57b3dfa814 100644
--- a/drivers/clk/samsung/clk-pll.h
+++ b/drivers/clk/samsung/clk-pll.h
@@ -41,35 +41,62 @@ enum samsung_pll_type {
pll_1460x,
};
-#define PLL_35XX_RATE(_rate, _m, _p, _s) \
+#define PLL_RATE(_fin, _m, _p, _s, _k, _ks) \
+ ((u64)(_fin) * (BIT(_ks) * (_m) + (_k)) / BIT(_ks) / ((_p) << (_s)))
+#define PLL_VALID_RATE(_fin, _fout, _m, _p, _s, _k, _ks) ((_fout) + \
+ BUILD_BUG_ON_ZERO(PLL_RATE(_fin, _m, _p, _s, _k, _ks) != (_fout)))
+
+#define PLL_35XX_RATE(_fin, _rate, _m, _p, _s) \
+ { \
+ .rate = PLL_VALID_RATE(_fin, _rate, \
+ _m, _p, _s, 0, 16), \
+ .mdiv = (_m), \
+ .pdiv = (_p), \
+ .sdiv = (_s), \
+ }
+
+#define PLL_S3C2410_MPLL_RATE(_fin, _rate, _m, _p, _s) \
+ { \
+ .rate = PLL_VALID_RATE(_fin, _rate, \
+ _m + 8, _p + 2, _s, 0, 16), \
+ .mdiv = (_m), \
+ .pdiv = (_p), \
+ .sdiv = (_s), \
+ }
+
+#define PLL_S3C2440_MPLL_RATE(_fin, _rate, _m, _p, _s) \
{ \
- .rate = (_rate), \
+ .rate = PLL_VALID_RATE(_fin, _rate, \
+ 2 * (_m + 8), _p + 2, _s, 0, 16), \
.mdiv = (_m), \
.pdiv = (_p), \
.sdiv = (_s), \
}
-#define PLL_36XX_RATE(_rate, _m, _p, _s, _k) \
+#define PLL_36XX_RATE(_fin, _rate, _m, _p, _s, _k) \
{ \
- .rate = (_rate), \
+ .rate = PLL_VALID_RATE(_fin, _rate, \
+ _m, _p, _s, _k, 16), \
.mdiv = (_m), \
.pdiv = (_p), \
.sdiv = (_s), \
.kdiv = (_k), \
}
-#define PLL_45XX_RATE(_rate, _m, _p, _s, _afc) \
+#define PLL_4508_RATE(_fin, _rate, _m, _p, _s, _afc) \
{ \
- .rate = (_rate), \
+ .rate = PLL_VALID_RATE(_fin, _rate, \
+ _m, _p, _s - 1, 0, 16), \
.mdiv = (_m), \
.pdiv = (_p), \
.sdiv = (_s), \
.afc = (_afc), \
}
-#define PLL_4600_RATE(_rate, _m, _p, _s, _k, _vsel) \
+#define PLL_4600_RATE(_fin, _rate, _m, _p, _s, _k, _vsel) \
{ \
- .rate = (_rate), \
+ .rate = PLL_VALID_RATE(_fin, _rate, \
+ _m, _p, _s, _k, 16), \
.mdiv = (_m), \
.pdiv = (_p), \
.sdiv = (_s), \
@@ -77,9 +104,10 @@ enum samsung_pll_type {
.vsel = (_vsel), \
}
-#define PLL_4650_RATE(_rate, _m, _p, _s, _k, _mfr, _mrr, _vsel) \
+#define PLL_4650_RATE(_fin, _rate, _m, _p, _s, _k, _mfr, _mrr, _vsel) \
{ \
- .rate = (_rate), \
+ .rate = PLL_VALID_RATE(_fin, _rate, \
+ _m, _p, _s, _k, 10), \
.mdiv = (_m), \
.pdiv = (_p), \
.sdiv = (_s), \
diff --git a/drivers/clk/samsung/clk-s3c2410.c b/drivers/clk/samsung/clk-s3c2410.c
index e0650c33863b..a9c887475054 100644
--- a/drivers/clk/samsung/clk-s3c2410.c
+++ b/drivers/clk/samsung/clk-s3c2410.c
@@ -95,7 +95,7 @@ static void __init s3c2410_clk_sleep_init(void) {}
PNAME(fclk_p) = { "mpll", "div_slow" };
-struct samsung_mux_clock s3c2410_common_muxes[] __initdata = {
+static struct samsung_mux_clock s3c2410_common_muxes[] __initdata = {
MUX(FCLK, "fclk", fclk_p, CLKSLOW, 4, 1),
};
@@ -111,12 +111,12 @@ static struct clk_div_table divslow_d[] = {
{ /* sentinel */ },
};
-struct samsung_div_clock s3c2410_common_dividers[] __initdata = {
+static struct samsung_div_clock s3c2410_common_dividers[] __initdata = {
DIV_T(0, "div_slow", "xti", CLKSLOW, 0, 3, divslow_d),
DIV(PCLK, "pclk", "hclk", CLKDIVN, 0, 1),
};
-struct samsung_gate_clock s3c2410_common_gates[] __initdata = {
+static struct samsung_gate_clock s3c2410_common_gates[] __initdata = {
GATE(PCLK_SPI, "spi", "pclk", CLKCON, 18, 0, 0),
GATE(PCLK_I2S, "i2s", "pclk", CLKCON, 17, 0, 0),
GATE(PCLK_I2C, "i2c", "pclk", CLKCON, 16, 0, 0),
@@ -135,7 +135,7 @@ struct samsung_gate_clock s3c2410_common_gates[] __initdata = {
};
/* should be added _after_ the soc-specific clocks are created */
-struct samsung_clock_alias s3c2410_common_aliases[] __initdata = {
+static struct samsung_clock_alias s3c2410_common_aliases[] __initdata = {
ALIAS(PCLK_I2C, "s3c2410-i2c.0", "i2c"),
ALIAS(PCLK_ADC, NULL, "adc"),
ALIAS(PCLK_RTC, NULL, "rtc"),
@@ -162,34 +162,34 @@ struct samsung_clock_alias s3c2410_common_aliases[] __initdata = {
static struct samsung_pll_rate_table pll_s3c2410_12mhz_tbl[] __initdata = {
/* sorted in descending order */
/* 2410A extras */
- PLL_35XX_RATE(270000000, 127, 1, 1),
- PLL_35XX_RATE(268000000, 126, 1, 1),
- PLL_35XX_RATE(266000000, 125, 1, 1),
- PLL_35XX_RATE(226000000, 105, 1, 1),
- PLL_35XX_RATE(210000000, 132, 2, 1),
+ PLL_S3C2410_MPLL_RATE(12 * MHZ, 270000000, 127, 1, 1),
+ PLL_S3C2410_MPLL_RATE(12 * MHZ, 268000000, 126, 1, 1),
+ PLL_S3C2410_MPLL_RATE(12 * MHZ, 266000000, 125, 1, 1),
+ PLL_S3C2410_MPLL_RATE(12 * MHZ, 226000000, 105, 1, 1),
+ PLL_S3C2410_MPLL_RATE(12 * MHZ, 210000000, 132, 2, 1),
/* 2410 common */
- PLL_35XX_RATE(203000000, 161, 3, 1),
- PLL_35XX_RATE(192000000, 88, 1, 1),
- PLL_35XX_RATE(186000000, 85, 1, 1),
- PLL_35XX_RATE(180000000, 82, 1, 1),
- PLL_35XX_RATE(170000000, 77, 1, 1),
- PLL_35XX_RATE(158000000, 71, 1, 1),
- PLL_35XX_RATE(152000000, 68, 1, 1),
- PLL_35XX_RATE(147000000, 90, 2, 1),
- PLL_35XX_RATE(135000000, 82, 2, 1),
- PLL_35XX_RATE(124000000, 116, 1, 2),
- PLL_35XX_RATE(118000000, 150, 2, 2),
- PLL_35XX_RATE(113000000, 105, 1, 2),
- PLL_35XX_RATE(101000000, 127, 2, 2),
- PLL_35XX_RATE(90000000, 112, 2, 2),
- PLL_35XX_RATE(85000000, 105, 2, 2),
- PLL_35XX_RATE(79000000, 71, 1, 2),
- PLL_35XX_RATE(68000000, 82, 2, 2),
- PLL_35XX_RATE(56000000, 142, 2, 3),
- PLL_35XX_RATE(48000000, 120, 2, 3),
- PLL_35XX_RATE(51000000, 161, 3, 3),
- PLL_35XX_RATE(45000000, 82, 1, 3),
- PLL_35XX_RATE(34000000, 82, 2, 3),
+ PLL_S3C2410_MPLL_RATE(12 * MHZ, 202800000, 161, 3, 1),
+ PLL_S3C2410_MPLL_RATE(12 * MHZ, 192000000, 88, 1, 1),
+ PLL_S3C2410_MPLL_RATE(12 * MHZ, 186000000, 85, 1, 1),
+ PLL_S3C2410_MPLL_RATE(12 * MHZ, 180000000, 82, 1, 1),
+ PLL_S3C2410_MPLL_RATE(12 * MHZ, 170000000, 77, 1, 1),
+ PLL_S3C2410_MPLL_RATE(12 * MHZ, 158000000, 71, 1, 1),
+ PLL_S3C2410_MPLL_RATE(12 * MHZ, 152000000, 68, 1, 1),
+ PLL_S3C2410_MPLL_RATE(12 * MHZ, 147000000, 90, 2, 1),
+ PLL_S3C2410_MPLL_RATE(12 * MHZ, 135000000, 82, 2, 1),
+ PLL_S3C2410_MPLL_RATE(12 * MHZ, 124000000, 116, 1, 2),
+ PLL_S3C2410_MPLL_RATE(12 * MHZ, 118500000, 150, 2, 2),
+ PLL_S3C2410_MPLL_RATE(12 * MHZ, 113000000, 105, 1, 2),
+ PLL_S3C2410_MPLL_RATE(12 * MHZ, 101250000, 127, 2, 2),
+ PLL_S3C2410_MPLL_RATE(12 * MHZ, 90000000, 112, 2, 2),
+ PLL_S3C2410_MPLL_RATE(12 * MHZ, 84750000, 105, 2, 2),
+ PLL_S3C2410_MPLL_RATE(12 * MHZ, 79000000, 71, 1, 2),
+ PLL_S3C2410_MPLL_RATE(12 * MHZ, 67500000, 82, 2, 2),
+ PLL_S3C2410_MPLL_RATE(12 * MHZ, 56250000, 142, 2, 3),
+ PLL_S3C2410_MPLL_RATE(12 * MHZ, 48000000, 120, 2, 3),
+ PLL_S3C2410_MPLL_RATE(12 * MHZ, 50700000, 161, 3, 3),
+ PLL_S3C2410_MPLL_RATE(12 * MHZ, 45000000, 82, 1, 3),
+ PLL_S3C2410_MPLL_RATE(12 * MHZ, 33750000, 82, 2, 3),
{ /* sentinel */ },
};
@@ -200,11 +200,11 @@ static struct samsung_pll_clock s3c2410_plls[] __initdata = {
LOCKTIME, UPLLCON, NULL),
};
-struct samsung_div_clock s3c2410_dividers[] __initdata = {
+static struct samsung_div_clock s3c2410_dividers[] __initdata = {
DIV(HCLK, "hclk", "mpll", CLKDIVN, 1, 1),
};
-struct samsung_fixed_factor_clock s3c2410_ffactor[] __initdata = {
+static struct samsung_fixed_factor_clock s3c2410_ffactor[] __initdata = {
/*
* armclk is directly supplied by the fclk, without
* switching possibility like on the s3c244x below.
@@ -215,7 +215,7 @@ struct samsung_fixed_factor_clock s3c2410_ffactor[] __initdata = {
FFACTOR(UCLK, "uclk", "upll", 1, 1, 0),
};
-struct samsung_clock_alias s3c2410_aliases[] __initdata = {
+static struct samsung_clock_alias s3c2410_aliases[] __initdata = {
ALIAS(PCLK_UART0, "s3c2410-uart.0", "uart"),
ALIAS(PCLK_UART1, "s3c2410-uart.1", "uart"),
ALIAS(PCLK_UART2, "s3c2410-uart.2", "uart"),
@@ -229,33 +229,33 @@ struct samsung_clock_alias s3c2410_aliases[] __initdata = {
static struct samsung_pll_rate_table pll_s3c244x_12mhz_tbl[] __initdata = {
/* sorted in descending order */
- PLL_35XX_RATE(400000000, 0x5c, 1, 1),
- PLL_35XX_RATE(390000000, 0x7a, 2, 1),
- PLL_35XX_RATE(380000000, 0x57, 1, 1),
- PLL_35XX_RATE(370000000, 0xb1, 4, 1),
- PLL_35XX_RATE(360000000, 0x70, 2, 1),
- PLL_35XX_RATE(350000000, 0xa7, 4, 1),
- PLL_35XX_RATE(340000000, 0x4d, 1, 1),
- PLL_35XX_RATE(330000000, 0x66, 2, 1),
- PLL_35XX_RATE(320000000, 0x98, 4, 1),
- PLL_35XX_RATE(310000000, 0x93, 4, 1),
- PLL_35XX_RATE(300000000, 0x75, 3, 1),
- PLL_35XX_RATE(240000000, 0x70, 1, 2),
- PLL_35XX_RATE(230000000, 0x6b, 1, 2),
- PLL_35XX_RATE(220000000, 0x66, 1, 2),
- PLL_35XX_RATE(210000000, 0x84, 2, 2),
- PLL_35XX_RATE(200000000, 0x5c, 1, 2),
- PLL_35XX_RATE(190000000, 0x57, 1, 2),
- PLL_35XX_RATE(180000000, 0x70, 2, 2),
- PLL_35XX_RATE(170000000, 0x4d, 1, 2),
- PLL_35XX_RATE(160000000, 0x98, 4, 2),
- PLL_35XX_RATE(150000000, 0x75, 3, 2),
- PLL_35XX_RATE(120000000, 0x70, 1, 3),
- PLL_35XX_RATE(110000000, 0x66, 1, 3),
- PLL_35XX_RATE(100000000, 0x5c, 1, 3),
- PLL_35XX_RATE(90000000, 0x70, 2, 3),
- PLL_35XX_RATE(80000000, 0x98, 4, 3),
- PLL_35XX_RATE(75000000, 0x75, 3, 3),
+ PLL_S3C2440_MPLL_RATE(12 * MHZ, 400000000, 0x5c, 1, 1),
+ PLL_S3C2440_MPLL_RATE(12 * MHZ, 390000000, 0x7a, 2, 1),
+ PLL_S3C2440_MPLL_RATE(12 * MHZ, 380000000, 0x57, 1, 1),
+ PLL_S3C2440_MPLL_RATE(12 * MHZ, 370000000, 0xb1, 4, 1),
+ PLL_S3C2440_MPLL_RATE(12 * MHZ, 360000000, 0x70, 2, 1),
+ PLL_S3C2440_MPLL_RATE(12 * MHZ, 350000000, 0xa7, 4, 1),
+ PLL_S3C2440_MPLL_RATE(12 * MHZ, 340000000, 0x4d, 1, 1),
+ PLL_S3C2440_MPLL_RATE(12 * MHZ, 330000000, 0x66, 2, 1),
+ PLL_S3C2440_MPLL_RATE(12 * MHZ, 320000000, 0x98, 4, 1),
+ PLL_S3C2440_MPLL_RATE(12 * MHZ, 310000000, 0x93, 4, 1),
+ PLL_S3C2440_MPLL_RATE(12 * MHZ, 300000000, 0x75, 3, 1),
+ PLL_S3C2440_MPLL_RATE(12 * MHZ, 240000000, 0x70, 1, 2),
+ PLL_S3C2440_MPLL_RATE(12 * MHZ, 230000000, 0x6b, 1, 2),
+ PLL_S3C2440_MPLL_RATE(12 * MHZ, 220000000, 0x66, 1, 2),
+ PLL_S3C2440_MPLL_RATE(12 * MHZ, 210000000, 0x84, 2, 2),
+ PLL_S3C2440_MPLL_RATE(12 * MHZ, 200000000, 0x5c, 1, 2),
+ PLL_S3C2440_MPLL_RATE(12 * MHZ, 190000000, 0x57, 1, 2),
+ PLL_S3C2440_MPLL_RATE(12 * MHZ, 180000000, 0x70, 2, 2),
+ PLL_S3C2440_MPLL_RATE(12 * MHZ, 170000000, 0x4d, 1, 2),
+ PLL_S3C2440_MPLL_RATE(12 * MHZ, 160000000, 0x98, 4, 2),
+ PLL_S3C2440_MPLL_RATE(12 * MHZ, 150000000, 0x75, 3, 2),
+ PLL_S3C2440_MPLL_RATE(12 * MHZ, 120000000, 0x70, 1, 3),
+ PLL_S3C2440_MPLL_RATE(12 * MHZ, 110000000, 0x66, 1, 3),
+ PLL_S3C2440_MPLL_RATE(12 * MHZ, 100000000, 0x5c, 1, 3),
+ PLL_S3C2440_MPLL_RATE(12 * MHZ, 90000000, 0x70, 2, 3),
+ PLL_S3C2440_MPLL_RATE(12 * MHZ, 80000000, 0x98, 4, 3),
+ PLL_S3C2440_MPLL_RATE(12 * MHZ, 75000000, 0x75, 3, 3),
{ /* sentinel */ },
};
@@ -269,12 +269,12 @@ static struct samsung_pll_clock s3c244x_common_plls[] __initdata = {
PNAME(hclk_p) = { "fclk", "div_hclk_2", "div_hclk_4", "div_hclk_3" };
PNAME(armclk_p) = { "fclk", "hclk" };
-struct samsung_mux_clock s3c244x_common_muxes[] __initdata = {
+static struct samsung_mux_clock s3c244x_common_muxes[] __initdata = {
MUX(HCLK, "hclk", hclk_p, CLKDIVN, 1, 2),
MUX(ARMCLK, "armclk", armclk_p, CAMDIVN, 12, 1),
};
-struct samsung_fixed_factor_clock s3c244x_common_ffactor[] __initdata = {
+static struct samsung_fixed_factor_clock s3c244x_common_ffactor[] __initdata = {
FFACTOR(0, "div_hclk_2", "fclk", 1, 2, 0),
FFACTOR(0, "ff_cam", "div_cam", 2, 1, CLK_SET_RATE_PARENT),
};
@@ -291,7 +291,7 @@ static struct clk_div_table div_hclk_3_d[] = {
{ /* sentinel */ },
};
-struct samsung_div_clock s3c244x_common_dividers[] __initdata = {
+static struct samsung_div_clock s3c244x_common_dividers[] __initdata = {
DIV(UCLK, "uclk", "upll", CLKDIVN, 3, 1),
DIV(0, "div_hclk", "fclk", CLKDIVN, 1, 1),
DIV_T(0, "div_hclk_4", "fclk", CAMDIVN, 9, 1, div_hclk_4_d),
@@ -299,11 +299,11 @@ struct samsung_div_clock s3c244x_common_dividers[] __initdata = {
DIV(0, "div_cam", "upll", CAMDIVN, 0, 3),
};
-struct samsung_gate_clock s3c244x_common_gates[] __initdata = {
+static struct samsung_gate_clock s3c244x_common_gates[] __initdata = {
GATE(HCLK_CAM, "cam", "hclk", CLKCON, 19, 0, 0),
};
-struct samsung_clock_alias s3c244x_common_aliases[] __initdata = {
+static struct samsung_clock_alias s3c244x_common_aliases[] __initdata = {
ALIAS(PCLK_UART0, "s3c2440-uart.0", "uart"),
ALIAS(PCLK_UART1, "s3c2440-uart.1", "uart"),
ALIAS(PCLK_UART2, "s3c2440-uart.2", "uart"),
@@ -318,23 +318,23 @@ struct samsung_clock_alias s3c244x_common_aliases[] __initdata = {
PNAME(s3c2440_camif_p) = { "upll", "ff_cam" };
-struct samsung_mux_clock s3c2440_muxes[] __initdata = {
+static struct samsung_mux_clock s3c2440_muxes[] __initdata = {
MUX(CAMIF, "camif", s3c2440_camif_p, CAMDIVN, 4, 1),
};
-struct samsung_gate_clock s3c2440_gates[] __initdata = {
+static struct samsung_gate_clock s3c2440_gates[] __initdata = {
GATE(PCLK_AC97, "ac97", "pclk", CLKCON, 20, 0, 0),
};
/* S3C2442 specific clocks */
-struct samsung_fixed_factor_clock s3c2442_ffactor[] __initdata = {
+static struct samsung_fixed_factor_clock s3c2442_ffactor[] __initdata = {
FFACTOR(0, "upll_3", "upll", 1, 3, 0),
};
PNAME(s3c2442_camif_p) = { "upll", "ff_cam", "upll", "upll_3" };
-struct samsung_mux_clock s3c2442_muxes[] __initdata = {
+static struct samsung_mux_clock s3c2442_muxes[] __initdata = {
MUX(CAMIF, "camif", s3c2442_camif_p, CAMDIVN, 4, 2),
};
@@ -343,7 +343,7 @@ struct samsung_mux_clock s3c2442_muxes[] __initdata = {
* Only necessary until the devicetree-move is complete
*/
#define XTI 1
-struct samsung_fixed_rate_clock s3c2410_common_frate_clks[] __initdata = {
+static struct samsung_fixed_rate_clock s3c2410_common_frate_clks[] __initdata = {
FRATE(XTI, "xti", NULL, 0, 0),
};
@@ -468,18 +468,18 @@ void __init s3c2410_common_clk_init(struct device_node *np, unsigned long xti_f,
static void __init s3c2410_clk_init(struct device_node *np)
{
- s3c2410_common_clk_init(np, 0, S3C2410, 0);
+ s3c2410_common_clk_init(np, 0, S3C2410, NULL);
}
CLK_OF_DECLARE(s3c2410_clk, "samsung,s3c2410-clock", s3c2410_clk_init);
static void __init s3c2440_clk_init(struct device_node *np)
{
- s3c2410_common_clk_init(np, 0, S3C2440, 0);
+ s3c2410_common_clk_init(np, 0, S3C2440, NULL);
}
CLK_OF_DECLARE(s3c2440_clk, "samsung,s3c2440-clock", s3c2440_clk_init);
static void __init s3c2442_clk_init(struct device_node *np)
{
- s3c2410_common_clk_init(np, 0, S3C2442, 0);
+ s3c2410_common_clk_init(np, 0, S3C2442, NULL);
}
CLK_OF_DECLARE(s3c2442_clk, "samsung,s3c2442-clock", s3c2442_clk_init);
diff --git a/drivers/clk/samsung/clk-s3c2412.c b/drivers/clk/samsung/clk-s3c2412.c
index b8340a49921b..6bc94d3aff78 100644
--- a/drivers/clk/samsung/clk-s3c2412.c
+++ b/drivers/clk/samsung/clk-s3c2412.c
@@ -27,11 +27,6 @@
#define CLKSRC 0x1c
#define SWRST 0x30
-/* list of PLLs to be registered */
-enum s3c2412_plls {
- mpll, upll,
-};
-
static void __iomem *reg_base;
#ifdef CONFIG_PM_SLEEP
@@ -98,7 +93,7 @@ static struct clk_div_table divxti_d[] = {
{ /* sentinel */ },
};
-struct samsung_div_clock s3c2412_dividers[] __initdata = {
+static struct samsung_div_clock s3c2412_dividers[] __initdata = {
DIV_T(0, "div_xti", "xti", CLKSRC, 0, 3, divxti_d),
DIV(0, "div_cam", "mux_cam", CLKDIVN, 16, 4),
DIV(0, "div_i2s", "mux_i2s", CLKDIVN, 12, 4),
@@ -110,7 +105,7 @@ struct samsung_div_clock s3c2412_dividers[] __initdata = {
DIV(HCLK, "hclk", "armdiv", CLKDIVN, 0, 2),
};
-struct samsung_fixed_factor_clock s3c2412_ffactor[] __initdata = {
+static struct samsung_fixed_factor_clock s3c2412_ffactor[] __initdata = {
FFACTOR(0, "ff_hclk", "hclk", 2, 1, CLK_SET_RATE_PARENT),
};
@@ -130,7 +125,7 @@ PNAME(msysclk_p) = { "mdivclk", "mpll" };
PNAME(mdivclk_p) = { "xti", "div_xti" };
PNAME(armclk_p) = { "armdiv", "hclk" };
-struct samsung_mux_clock s3c2412_muxes[] __initdata = {
+static struct samsung_mux_clock s3c2412_muxes[] __initdata = {
MUX(0, "erefclk", erefclk_p, CLKSRC, 14, 2),
MUX(0, "urefclk", urefclk_p, CLKSRC, 12, 2),
MUX(0, "mux_cam", camclk_p, CLKSRC, 11, 1),
@@ -144,13 +139,11 @@ struct samsung_mux_clock s3c2412_muxes[] __initdata = {
};
static struct samsung_pll_clock s3c2412_plls[] __initdata = {
- [mpll] = PLL(pll_s3c2440_mpll, MPLL, "mpll", "xti",
- LOCKTIME, MPLLCON, NULL),
- [upll] = PLL(pll_s3c2410_upll, UPLL, "upll", "urefclk",
- LOCKTIME, UPLLCON, NULL),
+ PLL(pll_s3c2440_mpll, MPLL, "mpll", "xti", LOCKTIME, MPLLCON, NULL),
+ PLL(pll_s3c2410_upll, UPLL, "upll", "urefclk", LOCKTIME, UPLLCON, NULL),
};
-struct samsung_gate_clock s3c2412_gates[] __initdata = {
+static struct samsung_gate_clock s3c2412_gates[] __initdata = {
GATE(PCLK_WDT, "wdt", "pclk", CLKCON, 28, 0, 0),
GATE(PCLK_SPI, "spi", "pclk", CLKCON, 27, 0, 0),
GATE(PCLK_I2S, "i2s", "pclk", CLKCON, 26, 0, 0),
@@ -181,7 +174,7 @@ struct samsung_gate_clock s3c2412_gates[] __initdata = {
GATE(HCLK_DMA0, "dma0", "hclk", CLKCON, 0, CLK_IGNORE_UNUSED, 0),
};
-struct samsung_clock_alias s3c2412_aliases[] __initdata = {
+static struct samsung_clock_alias s3c2412_aliases[] __initdata = {
ALIAS(PCLK_UART0, "s3c2412-uart.0", "uart"),
ALIAS(PCLK_UART1, "s3c2412-uart.1", "uart"),
ALIAS(PCLK_UART2, "s3c2412-uart.2", "uart"),
@@ -231,7 +224,7 @@ static struct notifier_block s3c2412_restart_handler = {
* Only necessary until the devicetree-move is complete
*/
#define XTI 1
-struct samsung_fixed_rate_clock s3c2412_common_frate_clks[] __initdata = {
+static struct samsung_fixed_rate_clock s3c2412_common_frate_clks[] __initdata = {
FRATE(XTI, "xti", NULL, 0, 0),
FRATE(0, "ext", NULL, 0, 0),
};
@@ -296,6 +289,6 @@ void __init s3c2412_common_clk_init(struct device_node *np, unsigned long xti_f,
static void __init s3c2412_clk_init(struct device_node *np)
{
- s3c2412_common_clk_init(np, 0, 0, 0);
+ s3c2412_common_clk_init(np, 0, 0, NULL);
}
CLK_OF_DECLARE(s3c2412_clk, "samsung,s3c2412-clock", s3c2412_clk_init);
diff --git a/drivers/clk/samsung/clk-s3c2443.c b/drivers/clk/samsung/clk-s3c2443.c
index d94b85a42356..c46e6d5bc9bc 100644
--- a/drivers/clk/samsung/clk-s3c2443.c
+++ b/drivers/clk/samsung/clk-s3c2443.c
@@ -41,11 +41,6 @@ enum supported_socs {
S3C2450,
};
-/* list of PLLs to be registered */
-enum s3c2443_plls {
- mpll, epll,
-};
-
static void __iomem *reg_base;
#ifdef CONFIG_PM_SLEEP
@@ -113,7 +108,7 @@ PNAME(msysclk_p) = { "mpllref", "mpll" };
PNAME(armclk_p) = { "armdiv" , "hclk" };
PNAME(i2s0_p) = { "div_i2s0", "ext_i2s", "epllref", "epllref" };
-struct samsung_mux_clock s3c2443_common_muxes[] __initdata = {
+static struct samsung_mux_clock s3c2443_common_muxes[] __initdata = {
MUX(0, "epllref", epllref_p, CLKSRC, 7, 2),
MUX(ESYSCLK, "esysclk", esysclk_p, CLKSRC, 6, 1),
MUX(0, "mpllref", mpllref_p, CLKSRC, 3, 1),
@@ -141,7 +136,7 @@ static struct clk_div_table mdivclk_d[] = {
{ /* sentinel */ },
};
-struct samsung_div_clock s3c2443_common_dividers[] __initdata = {
+static struct samsung_div_clock s3c2443_common_dividers[] __initdata = {
DIV_T(0, "mdivclk", "xti", CLKDIV0, 6, 3, mdivclk_d),
DIV(0, "prediv", "msysclk", CLKDIV0, 4, 2),
DIV_T(HCLK, "hclk", "prediv", CLKDIV0, 0, 2, hclk_d),
@@ -154,7 +149,7 @@ struct samsung_div_clock s3c2443_common_dividers[] __initdata = {
DIV(0, "div_usbhost", "esysclk", CLKDIV1, 4, 2),
};
-struct samsung_gate_clock s3c2443_common_gates[] __initdata = {
+static struct samsung_gate_clock s3c2443_common_gates[] __initdata = {
GATE(SCLK_HSMMC_EXT, "sclk_hsmmcext", "ext", SCLKCON, 13, 0, 0),
GATE(SCLK_HSMMC1, "sclk_hsmmc1", "div_hsmmc1", SCLKCON, 12, 0, 0),
GATE(SCLK_FIMD, "sclk_fimd", "div_fimd", SCLKCON, 10, 0, 0),
@@ -188,7 +183,7 @@ struct samsung_gate_clock s3c2443_common_gates[] __initdata = {
GATE(PCLK_UART0, "uart0", "pclk", PCLKCON, 0, 0, 0),
};
-struct samsung_clock_alias s3c2443_common_aliases[] __initdata = {
+static struct samsung_clock_alias s3c2443_common_aliases[] __initdata = {
ALIAS(MSYSCLK, NULL, "msysclk"),
ALIAS(ARMCLK, NULL, "armclk"),
ALIAS(MPLL, NULL, "mpll"),
@@ -225,10 +220,8 @@ struct samsung_clock_alias s3c2443_common_aliases[] __initdata = {
/* S3C2416 specific clocks */
static struct samsung_pll_clock s3c2416_pll_clks[] __initdata = {
- [mpll] = PLL(pll_6552_s3c2416, MPLL, "mpll", "mpllref",
- LOCKCON0, MPLLCON, NULL),
- [epll] = PLL(pll_6553, EPLL, "epll", "epllref",
- LOCKCON1, EPLLCON, NULL),
+ PLL(pll_6552_s3c2416, MPLL, "mpll", "mpllref", LOCKCON0, MPLLCON, NULL),
+ PLL(pll_6553, EPLL, "epll", "epllref", LOCKCON1, EPLLCON, NULL),
};
PNAME(s3c2416_hsmmc0_p) = { "sclk_hsmmc0", "sclk_hsmmcext" };
@@ -245,19 +238,19 @@ static struct clk_div_table armdiv_s3c2416_d[] = {
{ /* sentinel */ },
};
-struct samsung_div_clock s3c2416_dividers[] __initdata = {
+static struct samsung_div_clock s3c2416_dividers[] __initdata = {
DIV_T(ARMDIV, "armdiv", "msysclk", CLKDIV0, 9, 3, armdiv_s3c2416_d),
DIV(0, "div_hsspi0_mpll", "msysclk", CLKDIV2, 0, 4),
DIV(0, "div_hsmmc0", "esysclk", CLKDIV2, 6, 2),
};
-struct samsung_mux_clock s3c2416_muxes[] __initdata = {
+static struct samsung_mux_clock s3c2416_muxes[] __initdata = {
MUX(MUX_HSMMC0, "mux_hsmmc0", s3c2416_hsmmc0_p, CLKSRC, 16, 1),
MUX(MUX_HSMMC1, "mux_hsmmc1", s3c2416_hsmmc1_p, CLKSRC, 17, 1),
MUX(MUX_HSSPI0, "mux_hsspi0", s3c2416_hsspi0_p, CLKSRC, 18, 1),
};
-struct samsung_gate_clock s3c2416_gates[] __initdata = {
+static struct samsung_gate_clock s3c2416_gates[] __initdata = {
GATE(0, "hsspi0_mpll", "div_hsspi0_mpll", SCLKCON, 19, 0, 0),
GATE(0, "hsspi0_epll", "div_hsspi0_epll", SCLKCON, 14, 0, 0),
GATE(0, "sclk_hsmmc0", "div_hsmmc0", SCLKCON, 6, 0, 0),
@@ -267,7 +260,7 @@ struct samsung_gate_clock s3c2416_gates[] __initdata = {
GATE(PCLK_PCM, "pcm", "pclk", PCLKCON, 19, 0, 0),
};
-struct samsung_clock_alias s3c2416_aliases[] __initdata = {
+static struct samsung_clock_alias s3c2416_aliases[] __initdata = {
ALIAS(HCLK_HSMMC0, "s3c-sdhci.0", "hsmmc"),
ALIAS(HCLK_HSMMC0, "s3c-sdhci.0", "mmc_busclk.0"),
ALIAS(MUX_HSMMC0, "s3c-sdhci.0", "mmc_busclk.2"),
@@ -279,10 +272,8 @@ struct samsung_clock_alias s3c2416_aliases[] __initdata = {
/* S3C2443 specific clocks */
static struct samsung_pll_clock s3c2443_pll_clks[] __initdata = {
- [mpll] = PLL(pll_3000, MPLL, "mpll", "mpllref",
- LOCKCON0, MPLLCON, NULL),
- [epll] = PLL(pll_2126, EPLL, "epll", "epllref",
- LOCKCON1, EPLLCON, NULL),
+ PLL(pll_3000, MPLL, "mpll", "mpllref", LOCKCON0, MPLLCON, NULL),
+ PLL(pll_2126, EPLL, "epll", "epllref", LOCKCON1, EPLLCON, NULL),
};
static struct clk_div_table armdiv_s3c2443_d[] = {
@@ -297,12 +288,12 @@ static struct clk_div_table armdiv_s3c2443_d[] = {
{ /* sentinel */ },
};
-struct samsung_div_clock s3c2443_dividers[] __initdata = {
+static struct samsung_div_clock s3c2443_dividers[] __initdata = {
DIV_T(ARMDIV, "armdiv", "msysclk", CLKDIV0, 9, 4, armdiv_s3c2443_d),
DIV(0, "div_cam", "esysclk", CLKDIV1, 26, 4),
};
-struct samsung_gate_clock s3c2443_gates[] __initdata = {
+static struct samsung_gate_clock s3c2443_gates[] __initdata = {
GATE(SCLK_HSSPI0, "sclk_hsspi0", "div_hsspi0_epll", SCLKCON, 14, 0, 0),
GATE(SCLK_CAM, "sclk_cam", "div_cam", SCLKCON, 11, 0, 0),
GATE(HCLK_CFC, "cfc", "hclk", HCLKCON, 17, CLK_IGNORE_UNUSED, 0),
@@ -311,7 +302,7 @@ struct samsung_gate_clock s3c2443_gates[] __initdata = {
GATE(PCLK_SDI, "sdi", "pclk", PCLKCON, 5, 0, 0),
};
-struct samsung_clock_alias s3c2443_aliases[] __initdata = {
+static struct samsung_clock_alias s3c2443_aliases[] __initdata = {
ALIAS(SCLK_HSSPI0, "s3c2443-spi.0", "spi_busclk2"),
ALIAS(SCLK_HSMMC1, "s3c-sdhci.1", "mmc_busclk.2"),
ALIAS(SCLK_CAM, NULL, "camif-upll"),
@@ -327,20 +318,20 @@ PNAME(s3c2450_cam_p) = { "div_cam", "hclk" };
PNAME(s3c2450_hsspi1_p) = { "hsspi1_epll", "hsspi1_mpll" };
PNAME(i2s1_p) = { "div_i2s1", "ext_i2s", "epllref", "epllref" };
-struct samsung_div_clock s3c2450_dividers[] __initdata = {
+static struct samsung_div_clock s3c2450_dividers[] __initdata = {
DIV(0, "div_cam", "esysclk", CLKDIV1, 26, 4),
DIV(0, "div_hsspi1_epll", "esysclk", CLKDIV2, 24, 2),
DIV(0, "div_hsspi1_mpll", "msysclk", CLKDIV2, 16, 4),
DIV(0, "div_i2s1", "esysclk", CLKDIV2, 12, 4),
};
-struct samsung_mux_clock s3c2450_muxes[] __initdata = {
+static struct samsung_mux_clock s3c2450_muxes[] __initdata = {
MUX(0, "mux_cam", s3c2450_cam_p, CLKSRC, 20, 1),
MUX(MUX_HSSPI1, "mux_hsspi1", s3c2450_hsspi1_p, CLKSRC, 19, 1),
MUX(0, "mux_i2s1", i2s1_p, CLKSRC, 12, 2),
};
-struct samsung_gate_clock s3c2450_gates[] __initdata = {
+static struct samsung_gate_clock s3c2450_gates[] __initdata = {
GATE(SCLK_I2S1, "sclk_i2s1", "div_i2s1", SCLKCON, 5, 0, 0),
GATE(HCLK_CFC, "cfc", "hclk", HCLKCON, 17, 0, 0),
GATE(HCLK_CAM, "cam", "hclk", HCLKCON, 8, 0, 0),
@@ -351,7 +342,7 @@ struct samsung_gate_clock s3c2450_gates[] __initdata = {
GATE(PCLK_SPI1, "spi1", "pclk", PCLKCON, 14, 0, 0),
};
-struct samsung_clock_alias s3c2450_aliases[] __initdata = {
+static struct samsung_clock_alias s3c2450_aliases[] __initdata = {
ALIAS(PCLK_SPI1, "s3c2443-spi.1", "spi"),
ALIAS(PCLK_SPI1, "s3c2443-spi.1", "spi_busclk0"),
ALIAS(MUX_HSSPI1, "s3c2443-spi.1", "spi_busclk2"),
@@ -374,7 +365,7 @@ static struct notifier_block s3c2443_restart_handler = {
* fixed rate clocks generated outside the soc
* Only necessary until the devicetree-move is complete
*/
-struct samsung_fixed_rate_clock s3c2443_common_frate_clks[] __initdata = {
+static struct samsung_fixed_rate_clock s3c2443_common_frate_clks[] __initdata = {
FRATE(0, "xti", NULL, 0, 0),
FRATE(0, "ext", NULL, 0, 0),
FRATE(0, "ext_i2s", NULL, 0, 0),
@@ -470,18 +461,18 @@ void __init s3c2443_common_clk_init(struct device_node *np, unsigned long xti_f,
static void __init s3c2416_clk_init(struct device_node *np)
{
- s3c2443_common_clk_init(np, 0, S3C2416, 0);
+ s3c2443_common_clk_init(np, 0, S3C2416, NULL);
}
CLK_OF_DECLARE(s3c2416_clk, "samsung,s3c2416-clock", s3c2416_clk_init);
static void __init s3c2443_clk_init(struct device_node *np)
{
- s3c2443_common_clk_init(np, 0, S3C2443, 0);
+ s3c2443_common_clk_init(np, 0, S3C2443, NULL);
}
CLK_OF_DECLARE(s3c2443_clk, "samsung,s3c2443-clock", s3c2443_clk_init);
static void __init s3c2450_clk_init(struct device_node *np)
{
- s3c2443_common_clk_init(np, 0, S3C2450, 0);
+ s3c2443_common_clk_init(np, 0, S3C2450, NULL);
}
CLK_OF_DECLARE(s3c2450_clk, "samsung,s3c2450-clock", s3c2450_clk_init);
diff --git a/drivers/clk/samsung/clk-s3c64xx.c b/drivers/clk/samsung/clk-s3c64xx.c
index 7306867a0ab8..6db01cf5ab83 100644
--- a/drivers/clk/samsung/clk-s3c64xx.c
+++ b/drivers/clk/samsung/clk-s3c64xx.c
@@ -56,11 +56,6 @@
#define GATE_ON(_id, cname, pname, o, b) \
GATE(_id, cname, pname, o, b, CLK_IGNORE_UNUSED, 0)
-/* list of PLLs to be registered */
-enum s3c64xx_plls {
- apll, mpll, epll,
-};
-
static void __iomem *reg_base;
static bool is_s3c6400;
@@ -364,12 +359,12 @@ GATE_CLOCKS(s3c6410_gate_clks) __initdata = {
/* List of PLL clocks. */
static struct samsung_pll_clock s3c64xx_pll_clks[] __initdata = {
- [apll] = PLL(pll_6552, FOUT_APLL, "fout_apll", "fin_pll",
- APLL_LOCK, APLL_CON, NULL),
- [mpll] = PLL(pll_6552, FOUT_MPLL, "fout_mpll", "fin_pll",
- MPLL_LOCK, MPLL_CON, NULL),
- [epll] = PLL(pll_6553, FOUT_EPLL, "fout_epll", "fin_pll",
- EPLL_LOCK, EPLL_CON0, NULL),
+ PLL(pll_6552, FOUT_APLL, "fout_apll", "fin_pll",
+ APLL_LOCK, APLL_CON, NULL),
+ PLL(pll_6552, FOUT_MPLL, "fout_mpll", "fin_pll",
+ MPLL_LOCK, MPLL_CON, NULL),
+ PLL(pll_6553, FOUT_EPLL, "fout_epll", "fin_pll",
+ EPLL_LOCK, EPLL_CON0, NULL),
};
/* Aliases for common s3c64xx clocks. */
diff --git a/drivers/clk/socfpga/Makefile b/drivers/clk/socfpga/Makefile
index 9146c20fe21f..ce5aa7802eb8 100644
--- a/drivers/clk/socfpga/Makefile
+++ b/drivers/clk/socfpga/Makefile
@@ -1,6 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
-obj-y += clk.o
-obj-y += clk-gate.o
-obj-y += clk-pll.o
-obj-y += clk-periph.o
-obj-y += clk-pll-a10.o clk-periph-a10.o clk-gate-a10.o
+obj-$(CONFIG_ARCH_SOCFPGA) += clk.o clk-gate.o clk-pll.o clk-periph.o
+obj-$(CONFIG_ARCH_SOCFPGA) += clk-pll-a10.o clk-periph-a10.o clk-gate-a10.o
+obj-$(CONFIG_ARCH_STRATIX10) += clk-s10.o
+obj-$(CONFIG_ARCH_STRATIX10) += clk-pll-s10.o clk-periph-s10.o clk-gate-s10.o
diff --git a/drivers/clk/socfpga/clk-gate-s10.c b/drivers/clk/socfpga/clk-gate-s10.c
new file mode 100644
index 000000000000..eee2d48ab656
--- /dev/null
+++ b/drivers/clk/socfpga/clk-gate-s10.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2017, Intel Corporation
+ */
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include "stratix10-clk.h"
+#include "clk.h"
+
+#define SOCFPGA_CS_PDBG_CLK "cs_pdbg_clk"
+#define to_socfpga_gate_clk(p) container_of(p, struct socfpga_gate_clk, hw.hw)
+
+static unsigned long socfpga_gate_clk_recalc_rate(struct clk_hw *hwclk,
+ unsigned long parent_rate)
+{
+ struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk);
+ u32 div = 1, val;
+
+ if (socfpgaclk->fixed_div) {
+ div = socfpgaclk->fixed_div;
+ } else if (socfpgaclk->div_reg) {
+ val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift;
+ val &= GENMASK(socfpgaclk->width - 1, 0);
+ div = (1 << val);
+ }
+ return parent_rate / div;
+}
+
+static unsigned long socfpga_dbg_clk_recalc_rate(struct clk_hw *hwclk,
+ unsigned long parent_rate)
+{
+ struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk);
+ u32 div = 1, val;
+
+ val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift;
+ val &= GENMASK(socfpgaclk->width - 1, 0);
+ div = (1 << val);
+ div = div ? 4 : 1;
+
+ return parent_rate / div;
+}
+
+static u8 socfpga_gate_get_parent(struct clk_hw *hwclk)
+{
+ struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk);
+ u32 mask;
+ u8 parent = 0;
+
+ if (socfpgaclk->bypass_reg) {
+ mask = (0x1 << socfpgaclk->bypass_shift);
+ parent = ((readl(socfpgaclk->bypass_reg) & mask) >>
+ socfpgaclk->bypass_shift);
+ }
+ return parent;
+}
+
+static struct clk_ops gateclk_ops = {
+ .recalc_rate = socfpga_gate_clk_recalc_rate,
+ .get_parent = socfpga_gate_get_parent,
+};
+
+static const struct clk_ops dbgclk_ops = {
+ .recalc_rate = socfpga_dbg_clk_recalc_rate,
+ .get_parent = socfpga_gate_get_parent,
+};
+
+struct clk *s10_register_gate(const char *name, const char *parent_name,
+ const char * const *parent_names,
+ u8 num_parents, unsigned long flags,
+ void __iomem *regbase, unsigned long gate_reg,
+ unsigned long gate_idx, unsigned long div_reg,
+ unsigned long div_offset, u8 div_width,
+ unsigned long bypass_reg, u8 bypass_shift,
+ u8 fixed_div)
+{
+ struct clk *clk;
+ struct socfpga_gate_clk *socfpga_clk;
+ struct clk_init_data init;
+
+ socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL);
+ if (!socfpga_clk)
+ return NULL;
+
+ socfpga_clk->hw.reg = regbase + gate_reg;
+ socfpga_clk->hw.bit_idx = gate_idx;
+
+ gateclk_ops.enable = clk_gate_ops.enable;
+ gateclk_ops.disable = clk_gate_ops.disable;
+
+ socfpga_clk->fixed_div = fixed_div;
+
+ if (div_reg)
+ socfpga_clk->div_reg = regbase + div_reg;
+ else
+ socfpga_clk->div_reg = NULL;
+
+ socfpga_clk->width = div_width;
+ socfpga_clk->shift = div_offset;
+
+ if (bypass_reg)
+ socfpga_clk->bypass_reg = regbase + bypass_reg;
+ else
+ socfpga_clk->bypass_reg = NULL;
+ socfpga_clk->bypass_shift = bypass_shift;
+
+ if (streq(name, "cs_pdbg_clk"))
+ init.ops = &dbgclk_ops;
+ else
+ init.ops = &gateclk_ops;
+
+ init.name = name;
+ init.flags = flags;
+
+ init.num_parents = num_parents;
+ init.parent_names = parent_names ? parent_names : &parent_name;
+ socfpga_clk->hw.hw.init = &init;
+
+ clk = clk_register(NULL, &socfpga_clk->hw.hw);
+ if (WARN_ON(IS_ERR(clk))) {
+ kfree(socfpga_clk);
+ return NULL;
+ }
+
+ return clk;
+}
diff --git a/drivers/clk/socfpga/clk-periph-s10.c b/drivers/clk/socfpga/clk-periph-s10.c
new file mode 100644
index 000000000000..568f59b58ddf
--- /dev/null
+++ b/drivers/clk/socfpga/clk-periph-s10.c
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2017, Intel Corporation
+ */
+#include <linux/slab.h>
+#include <linux/clk-provider.h>
+
+#include "stratix10-clk.h"
+#include "clk.h"
+
+#define CLK_MGR_FREE_SHIFT 16
+#define CLK_MGR_FREE_MASK 0x7
+#define SWCTRLBTCLKSEN_SHIFT 8
+
+#define to_periph_clk(p) container_of(p, struct socfpga_periph_clk, hw.hw)
+
+static unsigned long clk_peri_c_clk_recalc_rate(struct clk_hw *hwclk,
+ unsigned long parent_rate)
+{
+ struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk);
+ unsigned long div = 1;
+ u32 val;
+
+ val = readl(socfpgaclk->hw.reg);
+ val &= GENMASK(SWCTRLBTCLKSEN_SHIFT - 1, 0);
+ parent_rate /= val;
+
+ return parent_rate / div;
+}
+
+static unsigned long clk_peri_cnt_clk_recalc_rate(struct clk_hw *hwclk,
+ unsigned long parent_rate)
+{
+ struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk);
+ unsigned long div = 1;
+
+ if (socfpgaclk->fixed_div) {
+ div = socfpgaclk->fixed_div;
+ } else {
+ if (!socfpgaclk->bypass_reg)
+ div = ((readl(socfpgaclk->hw.reg) & 0x7ff) + 1);
+ }
+
+ return parent_rate / div;
+}
+
+static u8 clk_periclk_get_parent(struct clk_hw *hwclk)
+{
+ struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk);
+ u32 clk_src, mask;
+ u8 parent;
+
+ if (socfpgaclk->bypass_reg) {
+ mask = (0x1 << socfpgaclk->bypass_shift);
+ parent = ((readl(socfpgaclk->bypass_reg) & mask) >>
+ socfpgaclk->bypass_shift);
+ } else {
+ clk_src = readl(socfpgaclk->hw.reg);
+ parent = (clk_src >> CLK_MGR_FREE_SHIFT) &
+ CLK_MGR_FREE_MASK;
+ }
+ return parent;
+}
+
+static const struct clk_ops peri_c_clk_ops = {
+ .recalc_rate = clk_peri_c_clk_recalc_rate,
+ .get_parent = clk_periclk_get_parent,
+};
+
+static const struct clk_ops peri_cnt_clk_ops = {
+ .recalc_rate = clk_peri_cnt_clk_recalc_rate,
+ .get_parent = clk_periclk_get_parent,
+};
+
+struct clk *s10_register_periph(const char *name, const char *parent_name,
+ const char * const *parent_names,
+ u8 num_parents, unsigned long flags,
+ void __iomem *reg, unsigned long offset)
+{
+ struct clk *clk;
+ struct socfpga_periph_clk *periph_clk;
+ struct clk_init_data init;
+
+ periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL);
+ if (WARN_ON(!periph_clk))
+ return NULL;
+
+ periph_clk->hw.reg = reg + offset;
+
+ init.name = name;
+ init.ops = &peri_c_clk_ops;
+ init.flags = flags;
+
+ init.num_parents = num_parents;
+ init.parent_names = parent_names ? parent_names : &parent_name;
+
+ periph_clk->hw.hw.init = &init;
+
+ clk = clk_register(NULL, &periph_clk->hw.hw);
+ if (WARN_ON(IS_ERR(clk))) {
+ kfree(periph_clk);
+ return NULL;
+ }
+ return clk;
+}
+
+struct clk *s10_register_cnt_periph(const char *name, const char *parent_name,
+ const char * const *parent_names,
+ u8 num_parents, unsigned long flags,
+ void __iomem *regbase, unsigned long offset,
+ u8 fixed_divider, unsigned long bypass_reg,
+ unsigned long bypass_shift)
+{
+ struct clk *clk;
+ struct socfpga_periph_clk *periph_clk;
+ struct clk_init_data init;
+
+ periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL);
+ if (WARN_ON(!periph_clk))
+ return NULL;
+
+ if (offset)
+ periph_clk->hw.reg = regbase + offset;
+ else
+ periph_clk->hw.reg = NULL;
+
+ if (bypass_reg)
+ periph_clk->bypass_reg = regbase + bypass_reg;
+ else
+ periph_clk->bypass_reg = NULL;
+ periph_clk->bypass_shift = bypass_shift;
+ periph_clk->fixed_div = fixed_divider;
+
+ init.name = name;
+ init.ops = &peri_cnt_clk_ops;
+ init.flags = flags;
+
+ init.num_parents = num_parents;
+ init.parent_names = parent_names ? parent_names : &parent_name;
+
+ periph_clk->hw.hw.init = &init;
+
+ clk = clk_register(NULL, &periph_clk->hw.hw);
+ if (WARN_ON(IS_ERR(clk))) {
+ kfree(periph_clk);
+ return NULL;
+ }
+ return clk;
+}
diff --git a/drivers/clk/socfpga/clk-pll-s10.c b/drivers/clk/socfpga/clk-pll-s10.c
new file mode 100644
index 000000000000..2d5d8b43727e
--- /dev/null
+++ b/drivers/clk/socfpga/clk-pll-s10.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2017, Intel Corporation
+ */
+#include <linux/slab.h>
+#include <linux/clk-provider.h>
+
+#include "stratix10-clk.h"
+#include "clk.h"
+
+/* Clock Manager offsets */
+#define CLK_MGR_PLL_CLK_SRC_SHIFT 16
+#define CLK_MGR_PLL_CLK_SRC_MASK 0x3
+
+/* PLL Clock enable bits */
+#define SOCFPGA_PLL_POWER 0
+#define SOCFPGA_PLL_RESET_MASK 0x2
+#define SOCFPGA_PLL_REFDIV_MASK 0x00003F00
+#define SOCFPGA_PLL_REFDIV_SHIFT 8
+#define SOCFPGA_PLL_MDIV_MASK 0xFF000000
+#define SOCFPGA_PLL_MDIV_SHIFT 24
+#define SWCTRLBTCLKSEL_MASK 0x200
+#define SWCTRLBTCLKSEL_SHIFT 9
+
+#define SOCFPGA_BOOT_CLK "boot_clk"
+
+#define to_socfpga_clk(p) container_of(p, struct socfpga_pll, hw.hw)
+
+static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk,
+ unsigned long parent_rate)
+{
+ struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
+ unsigned long mdiv;
+ unsigned long refdiv;
+ unsigned long reg;
+ unsigned long long vco_freq;
+
+ /* read VCO1 reg for numerator and denominator */
+ reg = readl(socfpgaclk->hw.reg);
+ refdiv = (reg & SOCFPGA_PLL_REFDIV_MASK) >> SOCFPGA_PLL_REFDIV_SHIFT;
+ vco_freq = (unsigned long long)parent_rate / refdiv;
+
+ /* Read mdiv and fdiv from the fdbck register */
+ reg = readl(socfpgaclk->hw.reg + 0x4);
+ mdiv = (reg & SOCFPGA_PLL_MDIV_MASK) >> SOCFPGA_PLL_MDIV_SHIFT;
+ vco_freq = (unsigned long long)parent_rate * (mdiv + 6);
+
+ return (unsigned long)vco_freq;
+}
+
+static unsigned long clk_boot_clk_recalc_rate(struct clk_hw *hwclk,
+ unsigned long parent_rate)
+{
+ struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
+ u32 div = 1;
+
+ div = ((readl(socfpgaclk->hw.reg) &
+ SWCTRLBTCLKSEL_MASK) >>
+ SWCTRLBTCLKSEL_SHIFT);
+ div += 1;
+ return parent_rate /= div;
+}
+
+
+static u8 clk_pll_get_parent(struct clk_hw *hwclk)
+{
+ struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
+ u32 pll_src;
+
+ pll_src = readl(socfpgaclk->hw.reg);
+ return (pll_src >> CLK_MGR_PLL_CLK_SRC_SHIFT) &
+ CLK_MGR_PLL_CLK_SRC_MASK;
+}
+
+static u8 clk_boot_get_parent(struct clk_hw *hwclk)
+{
+ struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
+ u32 pll_src;
+
+ pll_src = readl(socfpgaclk->hw.reg);
+ return (pll_src >> SWCTRLBTCLKSEL_SHIFT) &
+ SWCTRLBTCLKSEL_MASK;
+}
+
+static int clk_pll_prepare(struct clk_hw *hwclk)
+{
+ struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
+ u32 reg;
+
+ /* Bring PLL out of reset */
+ reg = readl(socfpgaclk->hw.reg);
+ reg |= SOCFPGA_PLL_RESET_MASK;
+ writel(reg, socfpgaclk->hw.reg);
+
+ return 0;
+}
+
+static struct clk_ops clk_pll_ops = {
+ .recalc_rate = clk_pll_recalc_rate,
+ .get_parent = clk_pll_get_parent,
+ .prepare = clk_pll_prepare,
+};
+
+static struct clk_ops clk_boot_ops = {
+ .recalc_rate = clk_boot_clk_recalc_rate,
+ .get_parent = clk_boot_get_parent,
+ .prepare = clk_pll_prepare,
+};
+
+struct clk *s10_register_pll(const char *name, const char * const *parent_names,
+ u8 num_parents, unsigned long flags,
+ void __iomem *reg, unsigned long offset)
+{
+ struct clk *clk;
+ struct socfpga_pll *pll_clk;
+ struct clk_init_data init;
+
+ pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL);
+ if (WARN_ON(!pll_clk))
+ return NULL;
+
+ pll_clk->hw.reg = reg + offset;
+
+ if (streq(name, SOCFPGA_BOOT_CLK))
+ init.ops = &clk_boot_ops;
+ else
+ init.ops = &clk_pll_ops;
+
+ init.name = name;
+ init.flags = flags;
+
+ init.num_parents = num_parents;
+ init.parent_names = parent_names;
+ pll_clk->hw.hw.init = &init;
+
+ pll_clk->hw.bit_idx = SOCFPGA_PLL_POWER;
+ clk_pll_ops.enable = clk_gate_ops.enable;
+ clk_pll_ops.disable = clk_gate_ops.disable;
+
+ clk = clk_register(NULL, &pll_clk->hw.hw);
+ if (WARN_ON(IS_ERR(clk))) {
+ kfree(pll_clk);
+ return NULL;
+ }
+ return clk;
+}
diff --git a/drivers/clk/socfpga/clk-s10.c b/drivers/clk/socfpga/clk-s10.c
new file mode 100644
index 000000000000..3a11c382a663
--- /dev/null
+++ b/drivers/clk/socfpga/clk-s10.c
@@ -0,0 +1,345 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2017, Intel Corporation
+ */
+#include <linux/slab.h>
+#include <linux/clk-provider.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+
+#include <dt-bindings/clock/stratix10-clock.h>
+
+#include "stratix10-clk.h"
+
+static const char * const pll_mux[] = { "osc1", "cb_intosc_hs_div2_clk",
+ "f2s_free_clk",};
+static const char * const cntr_mux[] = { "main_pll", "periph_pll",
+ "osc1", "cb_intosc_hs_div2_clk",
+ "f2s_free_clk"};
+static const char * const boot_mux[] = { "osc1", "cb_intosc_hs_div2_clk",};
+
+static const char * const noc_free_mux[] = {"main_noc_base_clk",
+ "peri_noc_base_clk",
+ "osc1", "cb_intosc_hs_div2_clk",
+ "f2s_free_clk"};
+
+static const char * const emaca_free_mux[] = {"peri_emaca_clk", "boot_clk"};
+static const char * const emacb_free_mux[] = {"peri_emacb_clk", "boot_clk"};
+static const char * const emac_ptp_free_mux[] = {"peri_emac_ptp_clk", "boot_clk"};
+static const char * const gpio_db_free_mux[] = {"peri_gpio_db_clk", "boot_clk"};
+static const char * const sdmmc_free_mux[] = {"peri_sdmmc_clk", "boot_clk"};
+static const char * const s2f_usr1_free_mux[] = {"peri_s2f_usr1_clk", "boot_clk"};
+static const char * const psi_ref_free_mux[] = {"peri_psi_ref_clk", "boot_clk"};
+static const char * const mpu_mux[] = { "mpu_free_clk", "boot_clk",};
+
+static const char * const s2f_usr0_mux[] = {"f2s_free_clk", "boot_clk"};
+static const char * const emac_mux[] = {"emaca_free_clk", "emacb_free_clk"};
+static const char * const noc_mux[] = {"noc_free_clk", "boot_clk"};
+
+/* clocks in AO (always on) controller */
+static const struct stratix10_pll_clock s10_pll_clks[] = {
+ { STRATIX10_BOOT_CLK, "boot_clk", boot_mux, ARRAY_SIZE(boot_mux), 0,
+ 0x0},
+ { STRATIX10_MAIN_PLL_CLK, "main_pll", pll_mux, ARRAY_SIZE(pll_mux),
+ 0, 0x74},
+ { STRATIX10_PERIPH_PLL_CLK, "periph_pll", pll_mux, ARRAY_SIZE(pll_mux),
+ 0, 0xe4},
+};
+
+static const struct stratix10_perip_c_clock s10_main_perip_c_clks[] = {
+ { STRATIX10_MAIN_MPU_BASE_CLK, "main_mpu_base_clk", "main_pll", NULL, 1, 0, 0x84},
+ { STRATIX10_MAIN_NOC_BASE_CLK, "main_noc_base_clk", "main_pll", NULL, 1, 0, 0x88},
+ { STRATIX10_PERI_MPU_BASE_CLK, "peri_mpu_base_clk", "periph_pll", NULL, 1, 0,
+ 0xF4},
+ { STRATIX10_PERI_NOC_BASE_CLK, "peri_noc_base_clk", "periph_pll", NULL, 1, 0,
+ 0xF8},
+};
+
+static const struct stratix10_perip_cnt_clock s10_main_perip_cnt_clks[] = {
+ { STRATIX10_MPU_FREE_CLK, "mpu_free_clk", NULL, cntr_mux, ARRAY_SIZE(cntr_mux),
+ 0, 0x48, 0, 0, 0},
+ { STRATIX10_NOC_FREE_CLK, "noc_free_clk", NULL, noc_free_mux, ARRAY_SIZE(noc_free_mux),
+ 0, 0x4C, 0, 0, 0},
+ { STRATIX10_MAIN_EMACA_CLK, "main_emaca_clk", "main_noc_base_clk", NULL, 1, 0,
+ 0x50, 0, 0, 0},
+ { STRATIX10_MAIN_EMACB_CLK, "main_emacb_clk", "main_noc_base_clk", NULL, 1, 0,
+ 0x54, 0, 0, 0},
+ { STRATIX10_MAIN_EMAC_PTP_CLK, "main_emac_ptp_clk", "main_noc_base_clk", NULL, 1, 0,
+ 0x58, 0, 0, 0},
+ { STRATIX10_MAIN_GPIO_DB_CLK, "main_gpio_db_clk", "main_noc_base_clk", NULL, 1, 0,
+ 0x5C, 0, 0, 0},
+ { STRATIX10_MAIN_SDMMC_CLK, "main_sdmmc_clk", "main_noc_base_clk", NULL, 1, 0,
+ 0x60, 0, 0, 0},
+ { STRATIX10_MAIN_S2F_USR0_CLK, "main_s2f_usr0_clk", NULL, cntr_mux, ARRAY_SIZE(cntr_mux),
+ 0, 0x64, 0, 0, 0},
+ { STRATIX10_MAIN_S2F_USR1_CLK, "main_s2f_usr1_clk", "main_noc_base_clk", NULL, 1, 0,
+ 0x68, 0, 0, 0},
+ { STRATIX10_MAIN_PSI_REF_CLK, "main_psi_ref_clk", "main_noc_base_clk", NULL, 1, 0,
+ 0x6C, 0, 0, 0},
+ { STRATIX10_PERI_EMACA_CLK, "peri_emaca_clk", NULL, cntr_mux, ARRAY_SIZE(cntr_mux),
+ 0, 0xBC, 0, 0, 0},
+ { STRATIX10_PERI_EMACB_CLK, "peri_emacb_clk", NULL, cntr_mux, ARRAY_SIZE(cntr_mux),
+ 0, 0xC0, 0, 0, 0},
+ { STRATIX10_PERI_EMAC_PTP_CLK, "peri_emac_ptp_clk", NULL, cntr_mux, ARRAY_SIZE(cntr_mux),
+ 0, 0xC4, 0, 0, 0},
+ { STRATIX10_PERI_GPIO_DB_CLK, "peri_gpio_db_clk", NULL, cntr_mux, ARRAY_SIZE(cntr_mux),
+ 0, 0xC8, 0, 0, 0},
+ { STRATIX10_PERI_SDMMC_CLK, "peri_sdmmc_clk", NULL, cntr_mux, ARRAY_SIZE(cntr_mux),
+ 0, 0xCC, 0, 0, 0},
+ { STRATIX10_PERI_S2F_USR0_CLK, "peri_s2f_usr0_clk", "peri_noc_base_clk", NULL, 1, 0,
+ 0xD0, 0, 0, 0},
+ { STRATIX10_PERI_S2F_USR1_CLK, "peri_s2f_usr1_clk", NULL, cntr_mux, ARRAY_SIZE(cntr_mux),
+ 0, 0xD4, 0, 0, 0},
+ { STRATIX10_PERI_PSI_REF_CLK, "peri_psi_ref_clk", "peri_noc_base_clk", NULL, 1, 0,
+ 0xD8, 0, 0, 0},
+ { STRATIX10_L4_SYS_FREE_CLK, "l4_sys_free_clk", "noc_free_clk", NULL, 1, 0,
+ 0, 4, 0, 0},
+ { STRATIX10_NOC_CLK, "noc_clk", NULL, noc_mux, ARRAY_SIZE(noc_mux),
+ 0, 0, 0, 0x3C, 1},
+ { STRATIX10_EMAC_A_FREE_CLK, "emaca_free_clk", NULL, emaca_free_mux, ARRAY_SIZE(emaca_free_mux),
+ 0, 0, 4, 0xB0, 0},
+ { STRATIX10_EMAC_B_FREE_CLK, "emacb_free_clk", NULL, emacb_free_mux, ARRAY_SIZE(emacb_free_mux),
+ 0, 0, 4, 0xB0, 1},
+ { STRATIX10_EMAC_PTP_FREE_CLK, "emac_ptp_free_clk", NULL, emac_ptp_free_mux,
+ ARRAY_SIZE(emac_ptp_free_mux), 0, 0, 4, 0xB0, 2},
+ { STRATIX10_GPIO_DB_FREE_CLK, "gpio_db_free_clk", NULL, gpio_db_free_mux,
+ ARRAY_SIZE(gpio_db_free_mux), 0, 0, 0, 0xB0, 3},
+ { STRATIX10_SDMMC_FREE_CLK, "sdmmc_free_clk", NULL, sdmmc_free_mux,
+ ARRAY_SIZE(sdmmc_free_mux), 0, 0, 0, 0xB0, 4},
+ { STRATIX10_S2F_USER1_FREE_CLK, "s2f_user1_free_clk", NULL, s2f_usr1_free_mux,
+ ARRAY_SIZE(s2f_usr1_free_mux), 0, 0, 0, 0xB0, 5},
+ { STRATIX10_PSI_REF_FREE_CLK, "psi_ref_free_clk", NULL, psi_ref_free_mux,
+ ARRAY_SIZE(psi_ref_free_mux), 0, 0, 0, 0xB0, 6},
+};
+
+static const struct stratix10_gate_clock s10_gate_clks[] = {
+ { STRATIX10_MPU_CLK, "mpu_clk", NULL, mpu_mux, ARRAY_SIZE(mpu_mux), 0, 0x30,
+ 0, 0, 0, 0, 0x3C, 0, 0},
+ { STRATIX10_MPU_PERIPH_CLK, "mpu_periph_clk", "mpu_clk", NULL, 1, 0, 0x30,
+ 0, 0, 0, 0, 0, 0, 4},
+ { STRATIX10_MPU_L2RAM_CLK, "mpu_l2ram_clk", "mpu_clk", NULL, 1, 0, 0x30,
+ 0, 0, 0, 0, 0, 0, 2},
+ { STRATIX10_L4_MAIN_CLK, "l4_main_clk", "noc_clk", NULL, 1, 0, 0x30,
+ 1, 0x70, 0, 2, 0, 0, 0},
+ { STRATIX10_L4_MP_CLK, "l4_mp_clk", "noc_clk", NULL, 1, 0, 0x30,
+ 2, 0x70, 8, 2, 0, 0, 0},
+ { STRATIX10_L4_SP_CLK, "l4_sp_clk", "noc_clk", NULL, 1, CLK_IS_CRITICAL, 0x30,
+ 3, 0x70, 16, 2, 0, 0, 0},
+ { STRATIX10_CS_AT_CLK, "cs_at_clk", "noc_clk", NULL, 1, 0, 0x30,
+ 4, 0x70, 24, 2, 0, 0, 0},
+ { STRATIX10_CS_TRACE_CLK, "cs_trace_clk", "noc_clk", NULL, 1, 0, 0x30,
+ 4, 0x70, 26, 2, 0, 0, 0},
+ { STRATIX10_CS_PDBG_CLK, "cs_pdbg_clk", "cs_at_clk", NULL, 1, 0, 0x30,
+ 4, 0x70, 28, 1, 0, 0, 0},
+ { STRATIX10_CS_TIMER_CLK, "cs_timer_clk", "noc_clk", NULL, 1, 0, 0x30,
+ 5, 0, 0, 0, 0, 0, 0},
+ { STRATIX10_S2F_USER0_CLK, "s2f_user0_clk", NULL, s2f_usr0_mux, ARRAY_SIZE(s2f_usr0_mux), 0, 0x30,
+ 6, 0, 0, 0, 0, 0, 0},
+ { STRATIX10_EMAC0_CLK, "emac0_clk", NULL, emac_mux, ARRAY_SIZE(emac_mux), 0, 0xA4,
+ 0, 0, 0, 0, 0xDC, 26, 0},
+ { STRATIX10_EMAC1_CLK, "emac1_clk", NULL, emac_mux, ARRAY_SIZE(emac_mux), 0, 0xA4,
+ 1, 0, 0, 0, 0xDC, 27, 0},
+ { STRATIX10_EMAC2_CLK, "emac2_clk", NULL, emac_mux, ARRAY_SIZE(emac_mux), 0, 0xA4,
+ 2, 0, 0, 0, 0xDC, 28, 0},
+ { STRATIX10_EMAC_PTP_CLK, "emac_ptp_clk", "emac_ptp_free_clk", NULL, 1, 0, 0xA4,
+ 3, 0, 0, 0, 0, 0, 0},
+ { STRATIX10_GPIO_DB_CLK, "gpio_db_clk", "gpio_db_free_clk", NULL, 1, 0, 0xA4,
+ 4, 0xE0, 0, 16, 0, 0, 0},
+ { STRATIX10_SDMMC_CLK, "sdmmc_clk", "sdmmc_free_clk", NULL, 1, 0, 0xA4,
+ 5, 0, 0, 0, 0, 0, 4},
+ { STRATIX10_S2F_USER1_CLK, "s2f_user1_clk", "s2f_user1_free_clk", NULL, 1, 0, 0xA4,
+ 6, 0, 0, 0, 0, 0, 0},
+ { STRATIX10_PSI_REF_CLK, "psi_ref_clk", "psi_ref_free_clk", NULL, 1, 0, 0xA4,
+ 7, 0, 0, 0, 0, 0, 0},
+ { STRATIX10_USB_CLK, "usb_clk", "l4_mp_clk", NULL, 1, 0, 0xA4,
+ 8, 0, 0, 0, 0, 0, 0},
+ { STRATIX10_SPI_M_CLK, "spi_m_clk", "l4_mp_clk", NULL, 1, 0, 0xA4,
+ 9, 0, 0, 0, 0, 0, 0},
+ { STRATIX10_NAND_CLK, "nand_clk", "l4_main_clk", NULL, 1, 0, 0xA4,
+ 10, 0, 0, 0, 0, 0, 0},
+};
+
+static int s10_clk_register_c_perip(const struct stratix10_perip_c_clock *clks,
+ int nums, struct stratix10_clock_data *data)
+{
+ struct clk *clk;
+ void __iomem *base = data->base;
+ int i;
+
+ for (i = 0; i < nums; i++) {
+ clk = s10_register_periph(clks[i].name, clks[i].parent_name,
+ clks[i].parent_names, clks[i].num_parents,
+ clks[i].flags, base, clks[i].offset);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to register clock %s\n",
+ __func__, clks[i].name);
+ continue;
+ }
+ data->clk_data.clks[clks[i].id] = clk;
+ }
+ return 0;
+}
+
+static int s10_clk_register_cnt_perip(const struct stratix10_perip_cnt_clock *clks,
+ int nums, struct stratix10_clock_data *data)
+{
+ struct clk *clk;
+ void __iomem *base = data->base;
+ int i;
+
+ for (i = 0; i < nums; i++) {
+ clk = s10_register_cnt_periph(clks[i].name, clks[i].parent_name,
+ clks[i].parent_names,
+ clks[i].num_parents,
+ clks[i].flags, base,
+ clks[i].offset,
+ clks[i].fixed_divider,
+ clks[i].bypass_reg,
+ clks[i].bypass_shift);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to register clock %s\n",
+ __func__, clks[i].name);
+ continue;
+ }
+ data->clk_data.clks[clks[i].id] = clk;
+ }
+
+ return 0;
+}
+
+static int s10_clk_register_gate(const struct stratix10_gate_clock *clks,
+ int nums, struct stratix10_clock_data *data)
+{
+ struct clk *clk;
+ void __iomem *base = data->base;
+ int i;
+
+ for (i = 0; i < nums; i++) {
+ clk = s10_register_gate(clks[i].name, clks[i].parent_name,
+ clks[i].parent_names,
+ clks[i].num_parents,
+ clks[i].flags, base,
+ clks[i].gate_reg,
+ clks[i].gate_idx, clks[i].div_reg,
+ clks[i].div_offset, clks[i].div_width,
+ clks[i].bypass_reg,
+ clks[i].bypass_shift,
+ clks[i].fixed_div);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to register clock %s\n",
+ __func__, clks[i].name);
+ continue;
+ }
+ data->clk_data.clks[clks[i].id] = clk;
+ }
+
+ return 0;
+}
+
+static int s10_clk_register_pll(const struct stratix10_pll_clock *clks,
+ int nums, struct stratix10_clock_data *data)
+{
+ struct clk *clk;
+ void __iomem *base = data->base;
+ int i;
+
+ for (i = 0; i < nums; i++) {
+ clk = s10_register_pll(clks[i].name, clks[i].parent_names,
+ clks[i].num_parents,
+ clks[i].flags, base,
+ clks[i].offset);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to register clock %s\n",
+ __func__, clks[i].name);
+ continue;
+ }
+ data->clk_data.clks[clks[i].id] = clk;
+ }
+
+ return 0;
+}
+
+static struct stratix10_clock_data *__socfpga_s10_clk_init(struct device_node *np,
+ int nr_clks)
+{
+ struct stratix10_clock_data *clk_data;
+ struct clk **clk_table;
+ void __iomem *base;
+
+ base = of_iomap(np, 0);
+ if (!base) {
+ pr_err("%s: failed to map clock registers\n", __func__);
+ goto err;
+ }
+
+ clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
+ if (!clk_data)
+ goto err;
+
+ clk_data->base = base;
+ clk_table = kcalloc(nr_clks, sizeof(*clk_table), GFP_KERNEL);
+ if (!clk_table)
+ goto err_data;
+
+ clk_data->clk_data.clks = clk_table;
+ clk_data->clk_data.clk_num = nr_clks;
+ of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data->clk_data);
+ return clk_data;
+
+err_data:
+ kfree(clk_data);
+err:
+ return NULL;
+}
+
+static int s10_clkmgr_init(struct device_node *np)
+{
+ struct stratix10_clock_data *clk_data;
+
+ clk_data = __socfpga_s10_clk_init(np, STRATIX10_NUM_CLKS);
+ if (!clk_data)
+ return -ENOMEM;
+
+ s10_clk_register_pll(s10_pll_clks, ARRAY_SIZE(s10_pll_clks), clk_data);
+
+ s10_clk_register_c_perip(s10_main_perip_c_clks,
+ ARRAY_SIZE(s10_main_perip_c_clks), clk_data);
+
+ s10_clk_register_cnt_perip(s10_main_perip_cnt_clks,
+ ARRAY_SIZE(s10_main_perip_cnt_clks),
+ clk_data);
+
+ s10_clk_register_gate(s10_gate_clks, ARRAY_SIZE(s10_gate_clks),
+ clk_data);
+ return 0;
+}
+
+static int s10_clkmgr_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+
+ s10_clkmgr_init(np);
+
+ return 0;
+}
+
+static const struct of_device_id stratix10_clkmgr_match_table[] = {
+ { .compatible = "intel,stratix10-clkmgr",
+ .data = s10_clkmgr_init },
+ { }
+};
+
+static struct platform_driver stratix10_clkmgr_driver = {
+ .probe = s10_clkmgr_probe,
+ .driver = {
+ .name = "stratix10-clkmgr",
+ .of_match_table = stratix10_clkmgr_match_table,
+ },
+};
+
+static int __init s10_clk_init(void)
+{
+ return platform_driver_register(&stratix10_clkmgr_driver);
+}
+core_initcall(s10_clk_init);
diff --git a/drivers/clk/socfpga/clk.h b/drivers/clk/socfpga/clk.h
index 9cf1230115b1..26c3a265cf78 100644
--- a/drivers/clk/socfpga/clk.h
+++ b/drivers/clk/socfpga/clk.h
@@ -54,9 +54,11 @@ struct socfpga_gate_clk {
char *parent_name;
u32 fixed_div;
void __iomem *div_reg;
+ void __iomem *bypass_reg;
struct regmap *sys_mgr_base_addr;
u32 width; /* only valid if div_reg != 0 */
u32 shift; /* only valid if div_reg != 0 */
+ u32 bypass_shift; /* only valid if bypass_reg != 0 */
u32 clk_phase[2];
};
@@ -65,8 +67,10 @@ struct socfpga_periph_clk {
char *parent_name;
u32 fixed_div;
void __iomem *div_reg;
+ void __iomem *bypass_reg;
u32 width; /* only valid if div_reg != 0 */
u32 shift; /* only valid if div_reg != 0 */
+ u32 bypass_shift; /* only valid if bypass_reg != 0 */
};
#endif /* SOCFPGA_CLK_H */
diff --git a/drivers/clk/socfpga/stratix10-clk.h b/drivers/clk/socfpga/stratix10-clk.h
new file mode 100644
index 000000000000..e8e121907952
--- /dev/null
+++ b/drivers/clk/socfpga/stratix10-clk.h
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2017, Intel Corporation
+ */
+
+#ifndef __STRATIX10_CLK_H
+#define __STRATIX10_CLK_H
+
+struct stratix10_clock_data {
+ struct clk_onecell_data clk_data;
+ void __iomem *base;
+};
+
+struct stratix10_pll_clock {
+ unsigned int id;
+ const char *name;
+ const char *const *parent_names;
+ u8 num_parents;
+ unsigned long flags;
+ unsigned long offset;
+};
+
+struct stratix10_perip_c_clock {
+ unsigned int id;
+ const char *name;
+ const char *parent_name;
+ const char *const *parent_names;
+ u8 num_parents;
+ unsigned long flags;
+ unsigned long offset;
+};
+
+struct stratix10_perip_cnt_clock {
+ unsigned int id;
+ const char *name;
+ const char *parent_name;
+ const char *const *parent_names;
+ u8 num_parents;
+ unsigned long flags;
+ unsigned long offset;
+ u8 fixed_divider;
+ unsigned long bypass_reg;
+ unsigned long bypass_shift;
+};
+
+struct stratix10_gate_clock {
+ unsigned int id;
+ const char *name;
+ const char *parent_name;
+ const char *const *parent_names;
+ u8 num_parents;
+ unsigned long flags;
+ unsigned long gate_reg;
+ u8 gate_idx;
+ unsigned long div_reg;
+ u8 div_offset;
+ u8 div_width;
+ unsigned long bypass_reg;
+ u8 bypass_shift;
+ u8 fixed_div;
+};
+
+struct clk *s10_register_pll(const char *, const char *const *, u8,
+ unsigned long, void __iomem *, unsigned long);
+
+struct clk *s10_register_periph(const char *, const char *,
+ const char * const *, u8, unsigned long,
+ void __iomem *, unsigned long);
+struct clk *s10_register_cnt_periph(const char *, const char *,
+ const char * const *, u8,
+ unsigned long, void __iomem *,
+ unsigned long, u8, unsigned long,
+ unsigned long);
+struct clk *s10_register_gate(const char *, const char *,
+ const char * const *, u8,
+ unsigned long, void __iomem *,
+ unsigned long, unsigned long,
+ unsigned long, unsigned long, u8,
+ unsigned long, u8, u8);
+#endif /* __STRATIX10_CLK_H */
diff --git a/drivers/clk/sprd/sc9860-clk.c b/drivers/clk/sprd/sc9860-clk.c
index ed5c027df0f4..9980ab55271b 100644
--- a/drivers/clk/sprd/sc9860-clk.c
+++ b/drivers/clk/sprd/sc9860-clk.c
@@ -959,6 +959,44 @@ static SPRD_SC_GATE_CLK(sdio2_2x_en, "sdio2-2x-en", "aon-apb", 0x13c,
0x1000, BIT(6), 0, 0);
static SPRD_SC_GATE_CLK(emmc_2x_en, "emmc-2x-en", "aon-apb", 0x13c,
0x1000, BIT(9), 0, 0);
+static SPRD_SC_GATE_CLK(arch_rtc_eb, "arch-rtc-eb", "aon-apb", 0x10,
+ 0x1000, BIT(0), CLK_IGNORE_UNUSED, 0);
+static SPRD_SC_GATE_CLK(kpb_rtc_eb, "kpb-rtc-eb", "aon-apb", 0x10,
+ 0x1000, BIT(1), CLK_IGNORE_UNUSED, 0);
+static SPRD_SC_GATE_CLK(aon_syst_rtc_eb, "aon-syst-rtc-eb", "aon-apb", 0x10,
+ 0x1000, BIT(2), CLK_IGNORE_UNUSED, 0);
+static SPRD_SC_GATE_CLK(ap_syst_rtc_eb, "ap-syst-rtc-eb", "aon-apb", 0x10,
+ 0x1000, BIT(3), CLK_IGNORE_UNUSED, 0);
+static SPRD_SC_GATE_CLK(aon_tmr_rtc_eb, "aon-tmr-rtc-eb", "aon-apb", 0x10,
+ 0x1000, BIT(4), CLK_IGNORE_UNUSED, 0);
+static SPRD_SC_GATE_CLK(ap_tmr0_rtc_eb, "ap-tmr0-rtc-eb", "aon-apb", 0x10,
+ 0x1000, BIT(5), CLK_IGNORE_UNUSED, 0);
+static SPRD_SC_GATE_CLK(eic_rtc_eb, "eic-rtc-eb", "aon-apb", 0x10,
+ 0x1000, BIT(6), CLK_IGNORE_UNUSED, 0);
+static SPRD_SC_GATE_CLK(eic_rtcdv5_eb, "eic-rtcdv5-eb", "aon-apb", 0x10,
+ 0x1000, BIT(7), CLK_IGNORE_UNUSED, 0);
+static SPRD_SC_GATE_CLK(ap_wdg_rtc_eb, "ap-wdg-rtc-eb", "aon-apb", 0x10,
+ 0x1000, BIT(9), CLK_IGNORE_UNUSED, 0);
+static SPRD_SC_GATE_CLK(ap_tmr1_rtc_eb, "ap-tmr1-rtc-eb", "aon-apb", 0x10,
+ 0x1000, BIT(15), CLK_IGNORE_UNUSED, 0);
+static SPRD_SC_GATE_CLK(ap_tmr2_rtc_eb, "ap-tmr2-rtc-eb", "aon-apb", 0x10,
+ 0x1000, BIT(16), CLK_IGNORE_UNUSED, 0);
+static SPRD_SC_GATE_CLK(dcxo_tmr_rtc_eb, "dcxo-tmr-rtc-eb", "aon-apb", 0x10,
+ 0x1000, BIT(17), CLK_IGNORE_UNUSED, 0);
+static SPRD_SC_GATE_CLK(bb_cal_rtc_eb, "bb-cal-rtc-eb", "aon-apb", 0x10,
+ 0x1000, BIT(18), CLK_IGNORE_UNUSED, 0);
+static SPRD_SC_GATE_CLK(avs_big_rtc_eb, "avs-big-rtc-eb", "aon-apb", 0x10,
+ 0x1000, BIT(20), CLK_IGNORE_UNUSED, 0);
+static SPRD_SC_GATE_CLK(avs_lit_rtc_eb, "avs-lit-rtc-eb", "aon-apb", 0x10,
+ 0x1000, BIT(21), CLK_IGNORE_UNUSED, 0);
+static SPRD_SC_GATE_CLK(avs_gpu0_rtc_eb, "avs-gpu0-rtc-eb", "aon-apb", 0x10,
+ 0x1000, BIT(22), CLK_IGNORE_UNUSED, 0);
+static SPRD_SC_GATE_CLK(avs_gpu1_rtc_eb, "avs-gpu1-rtc-eb", "aon-apb", 0x10,
+ 0x1000, BIT(23), CLK_IGNORE_UNUSED, 0);
+static SPRD_SC_GATE_CLK(gpu_ts_eb, "gpu-ts-eb", "aon-apb", 0x10,
+ 0x1000, BIT(24), CLK_IGNORE_UNUSED, 0);
+static SPRD_SC_GATE_CLK(rtcdv10_eb, "rtcdv10-eb", "aon-apb", 0x10,
+ 0x1000, BIT(27), CLK_IGNORE_UNUSED, 0);
static struct sprd_clk_common *sc9860_aon_gate[] = {
/* address base is 0x402e0000 */
@@ -1030,6 +1068,25 @@ static struct sprd_clk_common *sc9860_aon_gate[] = {
&sdio1_2x_en.common,
&sdio2_2x_en.common,
&emmc_2x_en.common,
+ &arch_rtc_eb.common,
+ &kpb_rtc_eb.common,
+ &aon_syst_rtc_eb.common,
+ &ap_syst_rtc_eb.common,
+ &aon_tmr_rtc_eb.common,
+ &ap_tmr0_rtc_eb.common,
+ &eic_rtc_eb.common,
+ &eic_rtcdv5_eb.common,
+ &ap_wdg_rtc_eb.common,
+ &ap_tmr1_rtc_eb.common,
+ &ap_tmr2_rtc_eb.common,
+ &dcxo_tmr_rtc_eb.common,
+ &bb_cal_rtc_eb.common,
+ &avs_big_rtc_eb.common,
+ &avs_lit_rtc_eb.common,
+ &avs_gpu0_rtc_eb.common,
+ &avs_gpu1_rtc_eb.common,
+ &gpu_ts_eb.common,
+ &rtcdv10_eb.common,
};
static struct clk_hw_onecell_data sc9860_aon_gate_hws = {
@@ -1102,6 +1159,25 @@ static struct clk_hw_onecell_data sc9860_aon_gate_hws = {
[CLK_SDIO1_2X_EN] = &sdio1_2x_en.common.hw,
[CLK_SDIO2_2X_EN] = &sdio2_2x_en.common.hw,
[CLK_EMMC_2X_EN] = &emmc_2x_en.common.hw,
+ [CLK_ARCH_RTC_EB] = &arch_rtc_eb.common.hw,
+ [CLK_KPB_RTC_EB] = &kpb_rtc_eb.common.hw,
+ [CLK_AON_SYST_RTC_EB] = &aon_syst_rtc_eb.common.hw,
+ [CLK_AP_SYST_RTC_EB] = &ap_syst_rtc_eb.common.hw,
+ [CLK_AON_TMR_RTC_EB] = &aon_tmr_rtc_eb.common.hw,
+ [CLK_AP_TMR0_RTC_EB] = &ap_tmr0_rtc_eb.common.hw,
+ [CLK_EIC_RTC_EB] = &eic_rtc_eb.common.hw,
+ [CLK_EIC_RTCDV5_EB] = &eic_rtcdv5_eb.common.hw,
+ [CLK_AP_WDG_RTC_EB] = &ap_wdg_rtc_eb.common.hw,
+ [CLK_AP_TMR1_RTC_EB] = &ap_tmr1_rtc_eb.common.hw,
+ [CLK_AP_TMR2_RTC_EB] = &ap_tmr2_rtc_eb.common.hw,
+ [CLK_DCXO_TMR_RTC_EB] = &dcxo_tmr_rtc_eb.common.hw,
+ [CLK_BB_CAL_RTC_EB] = &bb_cal_rtc_eb.common.hw,
+ [CLK_AVS_BIG_RTC_EB] = &avs_big_rtc_eb.common.hw,
+ [CLK_AVS_LIT_RTC_EB] = &avs_lit_rtc_eb.common.hw,
+ [CLK_AVS_GPU0_RTC_EB] = &avs_gpu0_rtc_eb.common.hw,
+ [CLK_AVS_GPU1_RTC_EB] = &avs_gpu1_rtc_eb.common.hw,
+ [CLK_GPU_TS_EB] = &gpu_ts_eb.common.hw,
+ [CLK_RTCDV10_EB] = &rtcdv10_eb.common.hw,
},
.num = CLK_AON_GATE_NUM,
};
diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig
index 6427d0ebe2de..79dfd296c3d1 100644
--- a/drivers/clk/sunxi-ng/Kconfig
+++ b/drivers/clk/sunxi-ng/Kconfig
@@ -11,15 +11,13 @@ config SUN50I_A64_CCU
default ARM64 && ARCH_SUNXI
depends on (ARM64 && ARCH_SUNXI) || COMPILE_TEST
+config SUN50I_H6_CCU
+ bool "Support for the Allwinner H6 CCU"
+ default ARM64 && ARCH_SUNXI
+ depends on (ARM64 && ARCH_SUNXI) || COMPILE_TEST
+
config SUN4I_A10_CCU
bool "Support for the Allwinner A10/A20 CCU"
- select SUNXI_CCU_DIV
- select SUNXI_CCU_MULT
- select SUNXI_CCU_NK
- select SUNXI_CCU_NKM
- select SUNXI_CCU_NM
- select SUNXI_CCU_MP
- select SUNXI_CCU_PHASE
default MACH_SUN4I
default MACH_SUN7I
depends on MACH_SUN4I || MACH_SUN7I || COMPILE_TEST
diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile
index 4141c3fe08ae..128a40ee5c5e 100644
--- a/drivers/clk/sunxi-ng/Makefile
+++ b/drivers/clk/sunxi-ng/Makefile
@@ -22,6 +22,7 @@ lib-$(CONFIG_SUNXI_CCU) += ccu_mp.o
# SoC support
obj-$(CONFIG_SUN50I_A64_CCU) += ccu-sun50i-a64.o
+obj-$(CONFIG_SUN50I_H6_CCU) += ccu-sun50i-h6.o
obj-$(CONFIG_SUN4I_A10_CCU) += ccu-sun4i-a10.o
obj-$(CONFIG_SUN5I_CCU) += ccu-sun5i.o
obj-$(CONFIG_SUN6I_A31_CCU) += ccu-sun6i-a31.o
diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-h6.c b/drivers/clk/sunxi-ng/ccu-sun50i-h6.c
new file mode 100644
index 000000000000..bdbfe78fe133
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun50i-h6.c
@@ -0,0 +1,1211 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2017 Icenowy Zheng <icenowy@aosc.io>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+
+#include "ccu_common.h"
+#include "ccu_reset.h"
+
+#include "ccu_div.h"
+#include "ccu_gate.h"
+#include "ccu_mp.h"
+#include "ccu_mult.h"
+#include "ccu_nk.h"
+#include "ccu_nkm.h"
+#include "ccu_nkmp.h"
+#include "ccu_nm.h"
+
+#include "ccu-sun50i-h6.h"
+
+/*
+ * The CPU PLL is actually NP clock, with P being /1, /2 or /4. However
+ * P should only be used for output frequencies lower than 288 MHz.
+ *
+ * For now we can just model it as a multiplier clock, and force P to /1.
+ *
+ * The M factor is present in the register's description, but not in the
+ * frequency formula, and it's documented as "M is only used for backdoor
+ * testing", so it's not modelled and then force to 0.
+ */
+#define SUN50I_H6_PLL_CPUX_REG 0x000
+static struct ccu_mult pll_cpux_clk = {
+ .enable = BIT(31),
+ .lock = BIT(28),
+ .mult = _SUNXI_CCU_MULT_MIN(8, 8, 12),
+ .common = {
+ .reg = 0x000,
+ .hw.init = CLK_HW_INIT("pll-cpux", "osc24M",
+ &ccu_mult_ops,
+ CLK_SET_RATE_UNGATE),
+ },
+};
+
+/* Some PLLs are input * N / div1 / P. Model them as NKMP with no K */
+#define SUN50I_H6_PLL_DDR0_REG 0x010
+static struct ccu_nkmp pll_ddr0_clk = {
+ .enable = BIT(31),
+ .lock = BIT(28),
+ .n = _SUNXI_CCU_MULT_MIN(8, 8, 12),
+ .m = _SUNXI_CCU_DIV(1, 1), /* input divider */
+ .p = _SUNXI_CCU_DIV(0, 1), /* output divider */
+ .common = {
+ .reg = 0x010,
+ .hw.init = CLK_HW_INIT("pll-ddr0", "osc24M",
+ &ccu_nkmp_ops,
+ CLK_SET_RATE_UNGATE),
+ },
+};
+
+#define SUN50I_H6_PLL_PERIPH0_REG 0x020
+static struct ccu_nkmp pll_periph0_clk = {
+ .enable = BIT(31),
+ .lock = BIT(28),
+ .n = _SUNXI_CCU_MULT_MIN(8, 8, 12),
+ .m = _SUNXI_CCU_DIV(1, 1), /* input divider */
+ .p = _SUNXI_CCU_DIV(0, 1), /* output divider */
+ .fixed_post_div = 4,
+ .common = {
+ .reg = 0x020,
+ .features = CCU_FEATURE_FIXED_POSTDIV,
+ .hw.init = CLK_HW_INIT("pll-periph0", "osc24M",
+ &ccu_nkmp_ops,
+ CLK_SET_RATE_UNGATE),
+ },
+};
+
+#define SUN50I_H6_PLL_PERIPH1_REG 0x028
+static struct ccu_nkmp pll_periph1_clk = {
+ .enable = BIT(31),
+ .lock = BIT(28),
+ .n = _SUNXI_CCU_MULT_MIN(8, 8, 12),
+ .m = _SUNXI_CCU_DIV(1, 1), /* input divider */
+ .p = _SUNXI_CCU_DIV(0, 1), /* output divider */
+ .fixed_post_div = 4,
+ .common = {
+ .reg = 0x028,
+ .features = CCU_FEATURE_FIXED_POSTDIV,
+ .hw.init = CLK_HW_INIT("pll-periph1", "osc24M",
+ &ccu_nkmp_ops,
+ CLK_SET_RATE_UNGATE),
+ },
+};
+
+#define SUN50I_H6_PLL_GPU_REG 0x030
+static struct ccu_nkmp pll_gpu_clk = {
+ .enable = BIT(31),
+ .lock = BIT(28),
+ .n = _SUNXI_CCU_MULT_MIN(8, 8, 12),
+ .m = _SUNXI_CCU_DIV(1, 1), /* input divider */
+ .p = _SUNXI_CCU_DIV(0, 1), /* output divider */
+ .common = {
+ .reg = 0x030,
+ .hw.init = CLK_HW_INIT("pll-gpu", "osc24M",
+ &ccu_nkmp_ops,
+ CLK_SET_RATE_UNGATE),
+ },
+};
+
+/*
+ * For Video PLLs, the output divider is described as "used for testing"
+ * in the user manual. So it's not modelled and forced to 0.
+ */
+#define SUN50I_H6_PLL_VIDEO0_REG 0x040
+static struct ccu_nm pll_video0_clk = {
+ .enable = BIT(31),
+ .lock = BIT(28),
+ .n = _SUNXI_CCU_MULT_MIN(8, 8, 12),
+ .m = _SUNXI_CCU_DIV(1, 1), /* input divider */
+ .fixed_post_div = 4,
+ .common = {
+ .reg = 0x040,
+ .features = CCU_FEATURE_FIXED_POSTDIV,
+ .hw.init = CLK_HW_INIT("pll-video0", "osc24M",
+ &ccu_nm_ops,
+ CLK_SET_RATE_UNGATE),
+ },
+};
+
+#define SUN50I_H6_PLL_VIDEO1_REG 0x048
+static struct ccu_nm pll_video1_clk = {
+ .enable = BIT(31),
+ .lock = BIT(28),
+ .n = _SUNXI_CCU_MULT_MIN(8, 8, 12),
+ .m = _SUNXI_CCU_DIV(1, 1), /* input divider */
+ .fixed_post_div = 4,
+ .common = {
+ .reg = 0x048,
+ .features = CCU_FEATURE_FIXED_POSTDIV,
+ .hw.init = CLK_HW_INIT("pll-video1", "osc24M",
+ &ccu_nm_ops,
+ CLK_SET_RATE_UNGATE),
+ },
+};
+
+#define SUN50I_H6_PLL_VE_REG 0x058
+static struct ccu_nkmp pll_ve_clk = {
+ .enable = BIT(31),
+ .lock = BIT(28),
+ .n = _SUNXI_CCU_MULT_MIN(8, 8, 12),
+ .m = _SUNXI_CCU_DIV(1, 1), /* input divider */
+ .p = _SUNXI_CCU_DIV(0, 1), /* output divider */
+ .common = {
+ .reg = 0x058,
+ .hw.init = CLK_HW_INIT("pll-ve", "osc24M",
+ &ccu_nkmp_ops,
+ CLK_SET_RATE_UNGATE),
+ },
+};
+
+#define SUN50I_H6_PLL_DE_REG 0x060
+static struct ccu_nkmp pll_de_clk = {
+ .enable = BIT(31),
+ .lock = BIT(28),
+ .n = _SUNXI_CCU_MULT_MIN(8, 8, 12),
+ .m = _SUNXI_CCU_DIV(1, 1), /* input divider */
+ .p = _SUNXI_CCU_DIV(0, 1), /* output divider */
+ .common = {
+ .reg = 0x060,
+ .hw.init = CLK_HW_INIT("pll-de", "osc24M",
+ &ccu_nkmp_ops,
+ CLK_SET_RATE_UNGATE),
+ },
+};
+
+#define SUN50I_H6_PLL_HSIC_REG 0x070
+static struct ccu_nkmp pll_hsic_clk = {
+ .enable = BIT(31),
+ .lock = BIT(28),
+ .n = _SUNXI_CCU_MULT_MIN(8, 8, 12),
+ .m = _SUNXI_CCU_DIV(1, 1), /* input divider */
+ .p = _SUNXI_CCU_DIV(0, 1), /* output divider */
+ .common = {
+ .reg = 0x070,
+ .hw.init = CLK_HW_INIT("pll-hsic", "osc24M",
+ &ccu_nkmp_ops,
+ CLK_SET_RATE_UNGATE),
+ },
+};
+
+/*
+ * The Audio PLL is supposed to have 3 outputs: 2 fixed factors from
+ * the base (2x and 4x), and one variable divider (the one true pll audio).
+ *
+ * We don't have any need for the variable divider for now, so we just
+ * hardcode it to match with the clock names.
+ */
+#define SUN50I_H6_PLL_AUDIO_REG 0x078
+static struct ccu_nm pll_audio_base_clk = {
+ .enable = BIT(31),
+ .lock = BIT(28),
+ .n = _SUNXI_CCU_MULT_MIN(8, 8, 12),
+ .m = _SUNXI_CCU_DIV(1, 1), /* input divider */
+ .common = {
+ .reg = 0x078,
+ .hw.init = CLK_HW_INIT("pll-audio-base", "osc24M",
+ &ccu_nm_ops,
+ CLK_SET_RATE_UNGATE),
+ },
+};
+
+static const char * const cpux_parents[] = { "osc24M", "osc32k",
+ "iosc", "pll-cpux" };
+static SUNXI_CCU_MUX(cpux_clk, "cpux", cpux_parents,
+ 0x500, 24, 2, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL);
+static SUNXI_CCU_M(axi_clk, "axi", "cpux", 0x500, 0, 2, 0);
+static SUNXI_CCU_M(cpux_apb_clk, "cpux-apb", "cpux", 0x500, 8, 2, 0);
+
+static const char * const psi_ahb1_ahb2_parents[] = { "osc24M", "osc32k",
+ "iosc", "pll-periph0" };
+static SUNXI_CCU_MP_WITH_MUX(psi_ahb1_ahb2_clk, "psi-ahb1-ahb2",
+ psi_ahb1_ahb2_parents,
+ 0x510,
+ 0, 5, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ 0);
+
+static const char * const ahb3_apb1_apb2_parents[] = { "osc24M", "osc32k",
+ "psi-ahb1-ahb2",
+ "pll-periph0" };
+static SUNXI_CCU_MP_WITH_MUX(ahb3_clk, "ahb3", ahb3_apb1_apb2_parents, 0x51c,
+ 0, 5, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX(apb1_clk, "apb1", ahb3_apb1_apb2_parents, 0x520,
+ 0, 5, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX(apb2_clk, "apb2", ahb3_apb1_apb2_parents, 0x524,
+ 0, 5, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ 0);
+
+static const char * const mbus_parents[] = { "osc24M", "pll-periph0-2x",
+ "pll-ddr0", "pll-periph0-4x" };
+static SUNXI_CCU_M_WITH_MUX_GATE(mbus_clk, "mbus", mbus_parents, 0x540,
+ 0, 3, /* M */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ CLK_IS_CRITICAL);
+
+static const char * const de_parents[] = { "pll-de", "pll-periph0-2x" };
+static SUNXI_CCU_M_WITH_MUX_GATE(de_clk, "de", de_parents, 0x600,
+ 0, 4, /* M */
+ 24, 1, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_GATE(bus_de_clk, "bus-de", "psi-ahb1-ahb2",
+ 0x60c, BIT(0), 0);
+
+static const char * const deinterlace_parents[] = { "pll-periph0",
+ "pll-periph1" };
+static SUNXI_CCU_M_WITH_MUX_GATE(deinterlace_clk, "deinterlace",
+ deinterlace_parents,
+ 0x620,
+ 0, 4, /* M */
+ 24, 1, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_GATE(bus_deinterlace_clk, "bus-deinterlace", "psi-ahb1-ahb2",
+ 0x62c, BIT(0), 0);
+
+static const char * const gpu_parents[] = { "pll-gpu" };
+static SUNXI_CCU_M_WITH_MUX_GATE(gpu_clk, "gpu", gpu_parents, 0x670,
+ 0, 3, /* M */
+ 24, 1, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_GATE(bus_gpu_clk, "bus-gpu", "psi-ahb1-ahb2",
+ 0x67c, BIT(0), 0);
+
+/* Also applies to EMCE */
+static const char * const ce_parents[] = { "osc24M", "pll-periph0-2x" };
+static SUNXI_CCU_MP_WITH_MUX_GATE(ce_clk, "ce", ce_parents, 0x680,
+ 0, 4, /* M */
+ 8, 2, /* N */
+ 24, 1, /* mux */
+ BIT(31),/* gate */
+ 0);
+
+static SUNXI_CCU_GATE(bus_ce_clk, "bus-ce", "psi-ahb1-ahb2",
+ 0x68c, BIT(0), 0);
+
+static const char * const ve_parents[] = { "pll-ve" };
+static SUNXI_CCU_M_WITH_MUX_GATE(ve_clk, "ve", ve_parents, 0x690,
+ 0, 3, /* M */
+ 24, 1, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_GATE(bus_ve_clk, "bus-ve", "psi-ahb1-ahb2",
+ 0x69c, BIT(0), 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(emce_clk, "emce", ce_parents, 0x6b0,
+ 0, 4, /* M */
+ 8, 2, /* N */
+ 24, 1, /* mux */
+ BIT(31),/* gate */
+ 0);
+
+static SUNXI_CCU_GATE(bus_emce_clk, "bus-emce", "psi-ahb1-ahb2",
+ 0x6bc, BIT(0), 0);
+
+static const char * const vp9_parents[] = { "pll-ve", "pll-periph0-2x" };
+static SUNXI_CCU_M_WITH_MUX_GATE(vp9_clk, "vp9", vp9_parents, 0x6c0,
+ 0, 3, /* M */
+ 24, 1, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_GATE(bus_vp9_clk, "bus-vp9", "psi-ahb1-ahb2",
+ 0x6cc, BIT(0), 0);
+
+static SUNXI_CCU_GATE(bus_dma_clk, "bus-dma", "psi-ahb1-ahb2",
+ 0x70c, BIT(0), 0);
+
+static SUNXI_CCU_GATE(bus_msgbox_clk, "bus-msgbox", "psi-ahb1-ahb2",
+ 0x71c, BIT(0), 0);
+
+static SUNXI_CCU_GATE(bus_spinlock_clk, "bus-spinlock", "psi-ahb1-ahb2",
+ 0x72c, BIT(0), 0);
+
+static SUNXI_CCU_GATE(bus_hstimer_clk, "bus-hstimer", "psi-ahb1-ahb2",
+ 0x73c, BIT(0), 0);
+
+static SUNXI_CCU_GATE(avs_clk, "avs", "osc24M", 0x740, BIT(31), 0);
+
+static SUNXI_CCU_GATE(bus_dbg_clk, "bus-dbg", "psi-ahb1-ahb2",
+ 0x78c, BIT(0), 0);
+
+static SUNXI_CCU_GATE(bus_psi_clk, "bus-psi", "psi-ahb1-ahb2",
+ 0x79c, BIT(0), 0);
+
+static SUNXI_CCU_GATE(bus_pwm_clk, "bus-pwm", "apb1", 0x79c, BIT(0), 0);
+
+static SUNXI_CCU_GATE(bus_iommu_clk, "bus-iommu", "apb1", 0x7bc, BIT(0), 0);
+
+static const char * const dram_parents[] = { "pll-ddr0" };
+static struct ccu_div dram_clk = {
+ .div = _SUNXI_CCU_DIV(0, 2),
+ .mux = _SUNXI_CCU_MUX(24, 2),
+ .common = {
+ .reg = 0x800,
+ .hw.init = CLK_HW_INIT_PARENTS("dram",
+ dram_parents,
+ &ccu_div_ops,
+ CLK_IS_CRITICAL),
+ },
+};
+
+static SUNXI_CCU_GATE(mbus_dma_clk, "mbus-dma", "mbus",
+ 0x804, BIT(0), 0);
+static SUNXI_CCU_GATE(mbus_ve_clk, "mbus-ve", "mbus",
+ 0x804, BIT(1), 0);
+static SUNXI_CCU_GATE(mbus_ce_clk, "mbus-ce", "mbus",
+ 0x804, BIT(2), 0);
+static SUNXI_CCU_GATE(mbus_ts_clk, "mbus-ts", "mbus",
+ 0x804, BIT(3), 0);
+static SUNXI_CCU_GATE(mbus_nand_clk, "mbus-nand", "mbus",
+ 0x804, BIT(5), 0);
+static SUNXI_CCU_GATE(mbus_csi_clk, "mbus-csi", "mbus",
+ 0x804, BIT(8), 0);
+static SUNXI_CCU_GATE(mbus_deinterlace_clk, "mbus-deinterlace", "mbus",
+ 0x804, BIT(11), 0);
+
+static SUNXI_CCU_GATE(bus_dram_clk, "bus-dram", "psi-ahb1-ahb2",
+ 0x80c, BIT(0), CLK_IS_CRITICAL);
+
+static const char * const nand_spi_parents[] = { "osc24M", "pll-periph0",
+ "pll-periph1", "pll-periph0-2x",
+ "pll-periph1-2x" };
+static SUNXI_CCU_MP_WITH_MUX_GATE(nand0_clk, "nand0", nand_spi_parents, 0x810,
+ 0, 4, /* M */
+ 8, 2, /* N */
+ 24, 3, /* mux */
+ BIT(31),/* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(nand1_clk, "nand1", nand_spi_parents, 0x814,
+ 0, 4, /* M */
+ 8, 2, /* N */
+ 24, 3, /* mux */
+ BIT(31),/* gate */
+ 0);
+
+static SUNXI_CCU_GATE(bus_nand_clk, "bus-nand", "ahb3", 0x82c, BIT(0), 0);
+
+static const char * const mmc_parents[] = { "osc24M", "pll-periph0-2x",
+ "pll-periph1-2x" };
+static SUNXI_CCU_MP_WITH_MUX_GATE(mmc0_clk, "mmc0", mmc_parents, 0x830,
+ 0, 4, /* M */
+ 8, 2, /* N */
+ 24, 3, /* mux */
+ BIT(31),/* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(mmc1_clk, "mmc1", mmc_parents, 0x834,
+ 0, 4, /* M */
+ 8, 2, /* N */
+ 24, 3, /* mux */
+ BIT(31),/* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(mmc2_clk, "mmc2", mmc_parents, 0x838,
+ 0, 4, /* M */
+ 8, 2, /* N */
+ 24, 3, /* mux */
+ BIT(31),/* gate */
+ 0);
+
+static SUNXI_CCU_GATE(bus_mmc0_clk, "bus-mmc0", "ahb3", 0x84c, BIT(0), 0);
+static SUNXI_CCU_GATE(bus_mmc1_clk, "bus-mmc1", "ahb3", 0x84c, BIT(1), 0);
+static SUNXI_CCU_GATE(bus_mmc2_clk, "bus-mmc2", "ahb3", 0x84c, BIT(2), 0);
+
+static SUNXI_CCU_GATE(bus_uart0_clk, "bus-uart0", "apb2", 0x90c, BIT(0), 0);
+static SUNXI_CCU_GATE(bus_uart1_clk, "bus-uart1", "apb2", 0x90c, BIT(1), 0);
+static SUNXI_CCU_GATE(bus_uart2_clk, "bus-uart2", "apb2", 0x90c, BIT(2), 0);
+static SUNXI_CCU_GATE(bus_uart3_clk, "bus-uart3", "apb2", 0x90c, BIT(3), 0);
+
+static SUNXI_CCU_GATE(bus_i2c0_clk, "bus-i2c0", "apb2", 0x91c, BIT(0), 0);
+static SUNXI_CCU_GATE(bus_i2c1_clk, "bus-i2c1", "apb2", 0x91c, BIT(1), 0);
+static SUNXI_CCU_GATE(bus_i2c2_clk, "bus-i2c2", "apb2", 0x91c, BIT(2), 0);
+static SUNXI_CCU_GATE(bus_i2c3_clk, "bus-i2c3", "apb2", 0x91c, BIT(3), 0);
+
+static SUNXI_CCU_GATE(bus_scr0_clk, "bus-scr0", "apb2", 0x93c, BIT(0), 0);
+static SUNXI_CCU_GATE(bus_scr1_clk, "bus-scr1", "apb2", 0x93c, BIT(1), 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(spi0_clk, "spi0", nand_spi_parents, 0x940,
+ 0, 4, /* M */
+ 8, 2, /* N */
+ 24, 3, /* mux */
+ BIT(31),/* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(spi1_clk, "spi1", nand_spi_parents, 0x944,
+ 0, 4, /* M */
+ 8, 2, /* N */
+ 24, 3, /* mux */
+ BIT(31),/* gate */
+ 0);
+
+static SUNXI_CCU_GATE(bus_spi0_clk, "bus-spi0", "ahb3", 0x96c, BIT(0), 0);
+static SUNXI_CCU_GATE(bus_spi1_clk, "bus-spi1", "ahb3", 0x96c, BIT(1), 0);
+
+static SUNXI_CCU_GATE(bus_emac_clk, "bus-emac", "ahb3", 0x97c, BIT(0), 0);
+
+static const char * const ts_parents[] = { "osc24M", "pll-periph0" };
+static SUNXI_CCU_MP_WITH_MUX_GATE(ts_clk, "ts", ts_parents, 0x9b0,
+ 0, 4, /* M */
+ 8, 2, /* N */
+ 24, 1, /* mux */
+ BIT(31),/* gate */
+ 0);
+
+static SUNXI_CCU_GATE(bus_ts_clk, "bus-ts", "ahb3", 0x9bc, BIT(0), 0);
+
+static const char * const ir_tx_parents[] = { "osc32k", "osc24M" };
+static SUNXI_CCU_MP_WITH_MUX_GATE(ir_tx_clk, "ir-tx", ir_tx_parents, 0x9c0,
+ 0, 4, /* M */
+ 8, 2, /* N */
+ 24, 1, /* mux */
+ BIT(31),/* gate */
+ 0);
+
+static SUNXI_CCU_GATE(bus_ir_tx_clk, "bus-ir-tx", "apb1", 0x9cc, BIT(0), 0);
+
+static SUNXI_CCU_GATE(bus_ths_clk, "bus-ths", "apb1", 0x9fc, BIT(0), 0);
+
+static const char * const audio_parents[] = { "pll-audio", "pll-audio-2x", "pll-audio-4x" };
+static struct ccu_div i2s3_clk = {
+ .enable = BIT(31),
+ .div = _SUNXI_CCU_DIV_FLAGS(8, 2, CLK_DIVIDER_POWER_OF_TWO),
+ .mux = _SUNXI_CCU_MUX(24, 2),
+ .common = {
+ .reg = 0xa0c,
+ .hw.init = CLK_HW_INIT_PARENTS("i2s3",
+ audio_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_div i2s0_clk = {
+ .enable = BIT(31),
+ .div = _SUNXI_CCU_DIV_FLAGS(8, 2, CLK_DIVIDER_POWER_OF_TWO),
+ .mux = _SUNXI_CCU_MUX(24, 2),
+ .common = {
+ .reg = 0xa10,
+ .hw.init = CLK_HW_INIT_PARENTS("i2s0",
+ audio_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_div i2s1_clk = {
+ .enable = BIT(31),
+ .div = _SUNXI_CCU_DIV_FLAGS(8, 2, CLK_DIVIDER_POWER_OF_TWO),
+ .mux = _SUNXI_CCU_MUX(24, 2),
+ .common = {
+ .reg = 0xa14,
+ .hw.init = CLK_HW_INIT_PARENTS("i2s1",
+ audio_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_div i2s2_clk = {
+ .enable = BIT(31),
+ .div = _SUNXI_CCU_DIV_FLAGS(8, 2, CLK_DIVIDER_POWER_OF_TWO),
+ .mux = _SUNXI_CCU_MUX(24, 2),
+ .common = {
+ .reg = 0xa18,
+ .hw.init = CLK_HW_INIT_PARENTS("i2s2",
+ audio_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static SUNXI_CCU_GATE(bus_i2s0_clk, "bus-i2s0", "apb1", 0xa1c, BIT(0), 0);
+static SUNXI_CCU_GATE(bus_i2s1_clk, "bus-i2s1", "apb1", 0xa1c, BIT(1), 0);
+static SUNXI_CCU_GATE(bus_i2s2_clk, "bus-i2s2", "apb1", 0xa1c, BIT(2), 0);
+static SUNXI_CCU_GATE(bus_i2s3_clk, "bus-i2s3", "apb1", 0xa1c, BIT(3), 0);
+
+static struct ccu_div spdif_clk = {
+ .enable = BIT(31),
+ .div = _SUNXI_CCU_DIV_FLAGS(8, 2, CLK_DIVIDER_POWER_OF_TWO),
+ .mux = _SUNXI_CCU_MUX(24, 2),
+ .common = {
+ .reg = 0xa20,
+ .hw.init = CLK_HW_INIT_PARENTS("spdif",
+ audio_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static SUNXI_CCU_GATE(bus_spdif_clk, "bus-spdif", "apb1", 0xa2c, BIT(0), 0);
+
+static struct ccu_div dmic_clk = {
+ .enable = BIT(31),
+ .div = _SUNXI_CCU_DIV_FLAGS(8, 2, CLK_DIVIDER_POWER_OF_TWO),
+ .mux = _SUNXI_CCU_MUX(24, 2),
+ .common = {
+ .reg = 0xa40,
+ .hw.init = CLK_HW_INIT_PARENTS("dmic",
+ audio_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static SUNXI_CCU_GATE(bus_dmic_clk, "bus-dmic", "apb1", 0xa4c, BIT(0), 0);
+
+static struct ccu_div audio_hub_clk = {
+ .enable = BIT(31),
+ .div = _SUNXI_CCU_DIV_FLAGS(8, 2, CLK_DIVIDER_POWER_OF_TWO),
+ .mux = _SUNXI_CCU_MUX(24, 2),
+ .common = {
+ .reg = 0xa60,
+ .hw.init = CLK_HW_INIT_PARENTS("audio-hub",
+ audio_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static SUNXI_CCU_GATE(bus_audio_hub_clk, "bus-audio-hub", "apb1", 0xa6c, BIT(0), 0);
+
+/*
+ * There are OHCI 12M clock source selection bits for 2 USB 2.0 ports.
+ * We will force them to 0 (12M divided from 48M).
+ */
+#define SUN50I_H6_USB0_CLK_REG 0xa70
+#define SUN50I_H6_USB3_CLK_REG 0xa7c
+
+static SUNXI_CCU_GATE(usb_ohci0_clk, "usb-ohci0", "osc12M", 0xa70, BIT(31), 0);
+static SUNXI_CCU_GATE(usb_phy0_clk, "usb-phy0", "osc24M", 0xa70, BIT(29), 0);
+
+static SUNXI_CCU_GATE(usb_phy1_clk, "usb-phy1", "osc24M", 0xa74, BIT(29), 0);
+
+static SUNXI_CCU_GATE(usb_ohci3_clk, "usb-ohci3", "osc12M", 0xa7c, BIT(31), 0);
+static SUNXI_CCU_GATE(usb_phy3_clk, "usb-phy3", "osc12M", 0xa7c, BIT(29), 0);
+static SUNXI_CCU_GATE(usb_hsic_12m_clk, "usb-hsic-12M", "osc12M", 0xa7c, BIT(27), 0);
+static SUNXI_CCU_GATE(usb_hsic_clk, "usb-hsic", "pll-hsic", 0xa7c, BIT(26), 0);
+
+static SUNXI_CCU_GATE(bus_ohci0_clk, "bus-ohci0", "ahb3", 0xa8c, BIT(0), 0);
+static SUNXI_CCU_GATE(bus_ohci3_clk, "bus-ohci3", "ahb3", 0xa8c, BIT(3), 0);
+static SUNXI_CCU_GATE(bus_ehci0_clk, "bus-ehci0", "ahb3", 0xa8c, BIT(4), 0);
+static SUNXI_CCU_GATE(bus_xhci_clk, "bus-xhci", "ahb3", 0xa8c, BIT(5), 0);
+static SUNXI_CCU_GATE(bus_ehci3_clk, "bus-ehci3", "ahb3", 0xa8c, BIT(7), 0);
+static SUNXI_CCU_GATE(bus_otg_clk, "bus-otg", "ahb3", 0xa8c, BIT(8), 0);
+
+static CLK_FIXED_FACTOR(pcie_ref_100m_clk, "pcie-ref-100M",
+ "pll-periph0-4x", 24, 1, 0);
+static SUNXI_CCU_GATE(pcie_ref_clk, "pcie-ref", "pcie-ref-100M",
+ 0xab0, BIT(31), 0);
+static SUNXI_CCU_GATE(pcie_ref_out_clk, "pcie-ref-out", "pcie-ref",
+ 0xab0, BIT(30), 0);
+
+static SUNXI_CCU_M_WITH_GATE(pcie_maxi_clk, "pcie-maxi",
+ "pll-periph0", 0xab4,
+ 0, 4, /* M */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_M_WITH_GATE(pcie_aux_clk, "pcie-aux", "osc24M", 0xab8,
+ 0, 5, /* M */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_GATE(bus_pcie_clk, "bus-pcie", "psi-ahb1-ahb2",
+ 0xabc, BIT(0), 0);
+
+static const char * const hdmi_parents[] = { "pll-video0", "pll-video1",
+ "pll-video1-4x" };
+static SUNXI_CCU_M_WITH_MUX_GATE(hdmi_clk, "hdmi", hdmi_parents, 0xb00,
+ 0, 4, /* M */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_GATE(hdmi_slow_clk, "hdmi-slow", "osc24M", 0xb04, BIT(31), 0);
+
+static const char * const hdmi_cec_parents[] = { "osc32k", "pll-periph0-2x" };
+static const struct ccu_mux_fixed_prediv hdmi_cec_predivs[] = {
+ { .index = 1, .div = 36621 },
+};
+static struct ccu_mux hdmi_cec_clk = {
+ .enable = BIT(31),
+
+ .mux = {
+ .shift = 24,
+ .width = 2,
+
+ .fixed_predivs = hdmi_cec_predivs,
+ .n_predivs = ARRAY_SIZE(hdmi_cec_predivs),
+ },
+
+ .common = {
+ .reg = 0xb10,
+ .features = CCU_FEATURE_VARIABLE_PREDIV,
+ .hw.init = CLK_HW_INIT_PARENTS("hdmi-cec",
+ hdmi_cec_parents,
+ &ccu_mux_ops,
+ 0),
+ },
+};
+
+static SUNXI_CCU_GATE(bus_hdmi_clk, "bus-hdmi", "ahb3", 0xb1c, BIT(0), 0);
+
+static SUNXI_CCU_GATE(bus_tcon_top_clk, "bus-tcon-top", "ahb3",
+ 0xb5c, BIT(0), 0);
+
+static const char * const tcon_lcd0_parents[] = { "pll-video0",
+ "pll-video0-4x",
+ "pll-video1" };
+static SUNXI_CCU_MUX_WITH_GATE(tcon_lcd0_clk, "tcon-lcd0",
+ tcon_lcd0_parents, 0xb60,
+ 24, 3, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_GATE(bus_tcon_lcd0_clk, "bus-tcon-lcd0", "ahb3",
+ 0xb7c, BIT(0), 0);
+
+static const char * const tcon_tv0_parents[] = { "pll-video0",
+ "pll-video0-4x",
+ "pll-video1",
+ "pll-video1-4x" };
+static SUNXI_CCU_MP_WITH_MUX_GATE(tcon_tv0_clk, "tcon-tv0",
+ tcon_tv0_parents, 0xb80,
+ 0, 4, /* M */
+ 8, 2, /* P */
+ 24, 3, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_GATE(bus_tcon_tv0_clk, "bus-tcon-tv0", "ahb3",
+ 0xb9c, BIT(0), 0);
+
+static SUNXI_CCU_GATE(csi_cci_clk, "csi-cci", "osc24M", 0xc00, BIT(0), 0);
+
+static const char * const csi_top_parents[] = { "pll-video0", "pll-ve",
+ "pll-periph0" };
+static const u8 csi_top_table[] = { 0, 2, 3 };
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(csi_top_clk, "csi-top",
+ csi_top_parents, csi_top_table, 0xc04,
+ 0, 4, /* M */
+ 24, 3, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static const char * const csi_mclk_parents[] = { "osc24M", "pll-video0",
+ "pll-periph0", "pll-periph1" };
+static SUNXI_CCU_M_WITH_MUX_GATE(csi_mclk_clk, "csi-mclk",
+ csi_mclk_parents, 0xc08,
+ 0, 5, /* M */
+ 24, 3, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_GATE(bus_csi_clk, "bus-csi", "ahb3", 0xc2c, BIT(0), 0);
+
+static const char * const hdcp_parents[] = { "pll-periph0", "pll-periph1" };
+static SUNXI_CCU_M_WITH_MUX_GATE(hdcp_clk, "hdcp", hdcp_parents, 0xc40,
+ 0, 4, /* M */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_GATE(bus_hdcp_clk, "bus-hdcp", "ahb3", 0xc4c, BIT(0), 0);
+
+/* Fixed factor clocks */
+static CLK_FIXED_FACTOR(osc12M_clk, "osc12M", "osc24M", 2, 1, 0);
+
+/*
+ * The divider of pll-audio is fixed to 8 now, as pll-audio-4x has a
+ * fixed post-divider 2.
+ */
+static CLK_FIXED_FACTOR(pll_audio_clk, "pll-audio",
+ "pll-audio-base", 8, 1, CLK_SET_RATE_PARENT);
+static CLK_FIXED_FACTOR(pll_audio_2x_clk, "pll-audio-2x",
+ "pll-audio-base", 4, 1, CLK_SET_RATE_PARENT);
+static CLK_FIXED_FACTOR(pll_audio_4x_clk, "pll-audio-4x",
+ "pll-audio-base", 2, 1, CLK_SET_RATE_PARENT);
+
+static CLK_FIXED_FACTOR(pll_periph0_4x_clk, "pll-periph0-4x",
+ "pll-periph0", 1, 4, 0);
+static CLK_FIXED_FACTOR(pll_periph0_2x_clk, "pll-periph0-2x",
+ "pll-periph0", 1, 2, 0);
+
+static CLK_FIXED_FACTOR(pll_periph1_4x_clk, "pll-periph1-4x",
+ "pll-periph1", 1, 4, 0);
+static CLK_FIXED_FACTOR(pll_periph1_2x_clk, "pll-periph1-2x",
+ "pll-periph1", 1, 2, 0);
+
+static CLK_FIXED_FACTOR(pll_video0_4x_clk, "pll-video0-4x",
+ "pll-video0", 1, 4, CLK_SET_RATE_PARENT);
+
+static CLK_FIXED_FACTOR(pll_video1_4x_clk, "pll-video1-4x",
+ "pll-video1", 1, 4, CLK_SET_RATE_PARENT);
+
+static struct ccu_common *sun50i_h6_ccu_clks[] = {
+ &pll_cpux_clk.common,
+ &pll_ddr0_clk.common,
+ &pll_periph0_clk.common,
+ &pll_periph1_clk.common,
+ &pll_gpu_clk.common,
+ &pll_video0_clk.common,
+ &pll_video1_clk.common,
+ &pll_ve_clk.common,
+ &pll_de_clk.common,
+ &pll_hsic_clk.common,
+ &pll_audio_base_clk.common,
+ &cpux_clk.common,
+ &axi_clk.common,
+ &cpux_apb_clk.common,
+ &psi_ahb1_ahb2_clk.common,
+ &ahb3_clk.common,
+ &apb1_clk.common,
+ &apb2_clk.common,
+ &mbus_clk.common,
+ &de_clk.common,
+ &bus_de_clk.common,
+ &deinterlace_clk.common,
+ &bus_deinterlace_clk.common,
+ &gpu_clk.common,
+ &bus_gpu_clk.common,
+ &ce_clk.common,
+ &bus_ce_clk.common,
+ &ve_clk.common,
+ &bus_ve_clk.common,
+ &emce_clk.common,
+ &bus_emce_clk.common,
+ &vp9_clk.common,
+ &bus_vp9_clk.common,
+ &bus_dma_clk.common,
+ &bus_msgbox_clk.common,
+ &bus_spinlock_clk.common,
+ &bus_hstimer_clk.common,
+ &avs_clk.common,
+ &bus_dbg_clk.common,
+ &bus_psi_clk.common,
+ &bus_pwm_clk.common,
+ &bus_iommu_clk.common,
+ &dram_clk.common,
+ &mbus_dma_clk.common,
+ &mbus_ve_clk.common,
+ &mbus_ce_clk.common,
+ &mbus_ts_clk.common,
+ &mbus_nand_clk.common,
+ &mbus_csi_clk.common,
+ &mbus_deinterlace_clk.common,
+ &bus_dram_clk.common,
+ &nand0_clk.common,
+ &nand1_clk.common,
+ &bus_nand_clk.common,
+ &mmc0_clk.common,
+ &mmc1_clk.common,
+ &mmc2_clk.common,
+ &bus_mmc0_clk.common,
+ &bus_mmc1_clk.common,
+ &bus_mmc2_clk.common,
+ &bus_uart0_clk.common,
+ &bus_uart1_clk.common,
+ &bus_uart2_clk.common,
+ &bus_uart3_clk.common,
+ &bus_i2c0_clk.common,
+ &bus_i2c1_clk.common,
+ &bus_i2c2_clk.common,
+ &bus_i2c3_clk.common,
+ &bus_scr0_clk.common,
+ &bus_scr1_clk.common,
+ &spi0_clk.common,
+ &spi1_clk.common,
+ &bus_spi0_clk.common,
+ &bus_spi1_clk.common,
+ &bus_emac_clk.common,
+ &ts_clk.common,
+ &bus_ts_clk.common,
+ &ir_tx_clk.common,
+ &bus_ir_tx_clk.common,
+ &bus_ths_clk.common,
+ &i2s3_clk.common,
+ &i2s0_clk.common,
+ &i2s1_clk.common,
+ &i2s2_clk.common,
+ &bus_i2s0_clk.common,
+ &bus_i2s1_clk.common,
+ &bus_i2s2_clk.common,
+ &bus_i2s3_clk.common,
+ &spdif_clk.common,
+ &bus_spdif_clk.common,
+ &dmic_clk.common,
+ &bus_dmic_clk.common,
+ &audio_hub_clk.common,
+ &bus_audio_hub_clk.common,
+ &usb_ohci0_clk.common,
+ &usb_phy0_clk.common,
+ &usb_phy1_clk.common,
+ &usb_ohci3_clk.common,
+ &usb_phy3_clk.common,
+ &usb_hsic_12m_clk.common,
+ &usb_hsic_clk.common,
+ &bus_ohci0_clk.common,
+ &bus_ohci3_clk.common,
+ &bus_ehci0_clk.common,
+ &bus_xhci_clk.common,
+ &bus_ehci3_clk.common,
+ &bus_otg_clk.common,
+ &pcie_ref_clk.common,
+ &pcie_ref_out_clk.common,
+ &pcie_maxi_clk.common,
+ &pcie_aux_clk.common,
+ &bus_pcie_clk.common,
+ &hdmi_clk.common,
+ &hdmi_slow_clk.common,
+ &hdmi_cec_clk.common,
+ &bus_hdmi_clk.common,
+ &bus_tcon_top_clk.common,
+ &tcon_lcd0_clk.common,
+ &bus_tcon_lcd0_clk.common,
+ &tcon_tv0_clk.common,
+ &bus_tcon_tv0_clk.common,
+ &csi_cci_clk.common,
+ &csi_top_clk.common,
+ &csi_mclk_clk.common,
+ &bus_csi_clk.common,
+ &hdcp_clk.common,
+ &bus_hdcp_clk.common,
+};
+
+static struct clk_hw_onecell_data sun50i_h6_hw_clks = {
+ .hws = {
+ [CLK_OSC12M] = &osc12M_clk.hw,
+ [CLK_PLL_CPUX] = &pll_cpux_clk.common.hw,
+ [CLK_PLL_DDR0] = &pll_ddr0_clk.common.hw,
+ [CLK_PLL_PERIPH0] = &pll_periph0_clk.common.hw,
+ [CLK_PLL_PERIPH0_2X] = &pll_periph0_2x_clk.hw,
+ [CLK_PLL_PERIPH0_4X] = &pll_periph0_4x_clk.hw,
+ [CLK_PLL_PERIPH1] = &pll_periph1_clk.common.hw,
+ [CLK_PLL_PERIPH1_2X] = &pll_periph1_2x_clk.hw,
+ [CLK_PLL_PERIPH1_4X] = &pll_periph1_4x_clk.hw,
+ [CLK_PLL_GPU] = &pll_gpu_clk.common.hw,
+ [CLK_PLL_VIDEO0] = &pll_video0_clk.common.hw,
+ [CLK_PLL_VIDEO0_4X] = &pll_video0_4x_clk.hw,
+ [CLK_PLL_VIDEO1] = &pll_video1_clk.common.hw,
+ [CLK_PLL_VIDEO1_4X] = &pll_video1_4x_clk.hw,
+ [CLK_PLL_VE] = &pll_ve_clk.common.hw,
+ [CLK_PLL_DE] = &pll_de_clk.common.hw,
+ [CLK_PLL_HSIC] = &pll_hsic_clk.common.hw,
+ [CLK_PLL_AUDIO_BASE] = &pll_audio_base_clk.common.hw,
+ [CLK_PLL_AUDIO] = &pll_audio_clk.hw,
+ [CLK_PLL_AUDIO_2X] = &pll_audio_2x_clk.hw,
+ [CLK_PLL_AUDIO_4X] = &pll_audio_4x_clk.hw,
+ [CLK_CPUX] = &cpux_clk.common.hw,
+ [CLK_AXI] = &axi_clk.common.hw,
+ [CLK_CPUX_APB] = &cpux_apb_clk.common.hw,
+ [CLK_PSI_AHB1_AHB2] = &psi_ahb1_ahb2_clk.common.hw,
+ [CLK_AHB3] = &ahb3_clk.common.hw,
+ [CLK_APB1] = &apb1_clk.common.hw,
+ [CLK_APB2] = &apb2_clk.common.hw,
+ [CLK_MBUS] = &mbus_clk.common.hw,
+ [CLK_DE] = &de_clk.common.hw,
+ [CLK_BUS_DE] = &bus_de_clk.common.hw,
+ [CLK_DEINTERLACE] = &deinterlace_clk.common.hw,
+ [CLK_BUS_DEINTERLACE] = &bus_deinterlace_clk.common.hw,
+ [CLK_GPU] = &gpu_clk.common.hw,
+ [CLK_BUS_GPU] = &bus_gpu_clk.common.hw,
+ [CLK_CE] = &ce_clk.common.hw,
+ [CLK_BUS_CE] = &bus_ce_clk.common.hw,
+ [CLK_VE] = &ve_clk.common.hw,
+ [CLK_BUS_VE] = &bus_ve_clk.common.hw,
+ [CLK_EMCE] = &emce_clk.common.hw,
+ [CLK_BUS_EMCE] = &bus_emce_clk.common.hw,
+ [CLK_VP9] = &vp9_clk.common.hw,
+ [CLK_BUS_VP9] = &bus_vp9_clk.common.hw,
+ [CLK_BUS_DMA] = &bus_dma_clk.common.hw,
+ [CLK_BUS_MSGBOX] = &bus_msgbox_clk.common.hw,
+ [CLK_BUS_SPINLOCK] = &bus_spinlock_clk.common.hw,
+ [CLK_BUS_HSTIMER] = &bus_hstimer_clk.common.hw,
+ [CLK_AVS] = &avs_clk.common.hw,
+ [CLK_BUS_DBG] = &bus_dbg_clk.common.hw,
+ [CLK_BUS_PSI] = &bus_psi_clk.common.hw,
+ [CLK_BUS_PWM] = &bus_pwm_clk.common.hw,
+ [CLK_BUS_IOMMU] = &bus_iommu_clk.common.hw,
+ [CLK_DRAM] = &dram_clk.common.hw,
+ [CLK_MBUS_DMA] = &mbus_dma_clk.common.hw,
+ [CLK_MBUS_VE] = &mbus_ve_clk.common.hw,
+ [CLK_MBUS_CE] = &mbus_ce_clk.common.hw,
+ [CLK_MBUS_TS] = &mbus_ts_clk.common.hw,
+ [CLK_MBUS_NAND] = &mbus_nand_clk.common.hw,
+ [CLK_MBUS_CSI] = &mbus_csi_clk.common.hw,
+ [CLK_MBUS_DEINTERLACE] = &mbus_deinterlace_clk.common.hw,
+ [CLK_BUS_DRAM] = &bus_dram_clk.common.hw,
+ [CLK_NAND0] = &nand0_clk.common.hw,
+ [CLK_NAND1] = &nand1_clk.common.hw,
+ [CLK_BUS_NAND] = &bus_nand_clk.common.hw,
+ [CLK_MMC0] = &mmc0_clk.common.hw,
+ [CLK_MMC1] = &mmc1_clk.common.hw,
+ [CLK_MMC2] = &mmc2_clk.common.hw,
+ [CLK_BUS_MMC0] = &bus_mmc0_clk.common.hw,
+ [CLK_BUS_MMC1] = &bus_mmc1_clk.common.hw,
+ [CLK_BUS_MMC2] = &bus_mmc2_clk.common.hw,
+ [CLK_BUS_UART0] = &bus_uart0_clk.common.hw,
+ [CLK_BUS_UART1] = &bus_uart1_clk.common.hw,
+ [CLK_BUS_UART2] = &bus_uart2_clk.common.hw,
+ [CLK_BUS_UART3] = &bus_uart3_clk.common.hw,
+ [CLK_BUS_I2C0] = &bus_i2c0_clk.common.hw,
+ [CLK_BUS_I2C1] = &bus_i2c1_clk.common.hw,
+ [CLK_BUS_I2C2] = &bus_i2c2_clk.common.hw,
+ [CLK_BUS_I2C3] = &bus_i2c3_clk.common.hw,
+ [CLK_BUS_SCR0] = &bus_scr0_clk.common.hw,
+ [CLK_BUS_SCR1] = &bus_scr1_clk.common.hw,
+ [CLK_SPI0] = &spi0_clk.common.hw,
+ [CLK_SPI1] = &spi1_clk.common.hw,
+ [CLK_BUS_SPI0] = &bus_spi0_clk.common.hw,
+ [CLK_BUS_SPI1] = &bus_spi1_clk.common.hw,
+ [CLK_BUS_EMAC] = &bus_emac_clk.common.hw,
+ [CLK_TS] = &ts_clk.common.hw,
+ [CLK_BUS_TS] = &bus_ts_clk.common.hw,
+ [CLK_IR_TX] = &ir_tx_clk.common.hw,
+ [CLK_BUS_IR_TX] = &bus_ir_tx_clk.common.hw,
+ [CLK_BUS_THS] = &bus_ths_clk.common.hw,
+ [CLK_I2S3] = &i2s3_clk.common.hw,
+ [CLK_I2S0] = &i2s0_clk.common.hw,
+ [CLK_I2S1] = &i2s1_clk.common.hw,
+ [CLK_I2S2] = &i2s2_clk.common.hw,
+ [CLK_BUS_I2S0] = &bus_i2s0_clk.common.hw,
+ [CLK_BUS_I2S1] = &bus_i2s1_clk.common.hw,
+ [CLK_BUS_I2S2] = &bus_i2s2_clk.common.hw,
+ [CLK_BUS_I2S3] = &bus_i2s3_clk.common.hw,
+ [CLK_SPDIF] = &spdif_clk.common.hw,
+ [CLK_BUS_SPDIF] = &bus_spdif_clk.common.hw,
+ [CLK_DMIC] = &dmic_clk.common.hw,
+ [CLK_BUS_DMIC] = &bus_dmic_clk.common.hw,
+ [CLK_AUDIO_HUB] = &audio_hub_clk.common.hw,
+ [CLK_BUS_AUDIO_HUB] = &bus_audio_hub_clk.common.hw,
+ [CLK_USB_OHCI0] = &usb_ohci0_clk.common.hw,
+ [CLK_USB_PHY0] = &usb_phy0_clk.common.hw,
+ [CLK_USB_PHY1] = &usb_phy1_clk.common.hw,
+ [CLK_USB_OHCI3] = &usb_ohci3_clk.common.hw,
+ [CLK_USB_PHY3] = &usb_phy3_clk.common.hw,
+ [CLK_USB_HSIC_12M] = &usb_hsic_12m_clk.common.hw,
+ [CLK_USB_HSIC] = &usb_hsic_clk.common.hw,
+ [CLK_BUS_OHCI0] = &bus_ohci0_clk.common.hw,
+ [CLK_BUS_OHCI3] = &bus_ohci3_clk.common.hw,
+ [CLK_BUS_EHCI0] = &bus_ehci0_clk.common.hw,
+ [CLK_BUS_XHCI] = &bus_xhci_clk.common.hw,
+ [CLK_BUS_EHCI3] = &bus_ehci3_clk.common.hw,
+ [CLK_BUS_OTG] = &bus_otg_clk.common.hw,
+ [CLK_PCIE_REF_100M] = &pcie_ref_100m_clk.hw,
+ [CLK_PCIE_REF] = &pcie_ref_clk.common.hw,
+ [CLK_PCIE_REF_OUT] = &pcie_ref_out_clk.common.hw,
+ [CLK_PCIE_MAXI] = &pcie_maxi_clk.common.hw,
+ [CLK_PCIE_AUX] = &pcie_aux_clk.common.hw,
+ [CLK_BUS_PCIE] = &bus_pcie_clk.common.hw,
+ [CLK_HDMI] = &hdmi_clk.common.hw,
+ [CLK_HDMI_SLOW] = &hdmi_slow_clk.common.hw,
+ [CLK_HDMI_CEC] = &hdmi_cec_clk.common.hw,
+ [CLK_BUS_HDMI] = &bus_hdmi_clk.common.hw,
+ [CLK_BUS_TCON_TOP] = &bus_tcon_top_clk.common.hw,
+ [CLK_TCON_LCD0] = &tcon_lcd0_clk.common.hw,
+ [CLK_BUS_TCON_LCD0] = &bus_tcon_lcd0_clk.common.hw,
+ [CLK_TCON_TV0] = &tcon_tv0_clk.common.hw,
+ [CLK_BUS_TCON_TV0] = &bus_tcon_tv0_clk.common.hw,
+ [CLK_CSI_CCI] = &csi_cci_clk.common.hw,
+ [CLK_CSI_TOP] = &csi_top_clk.common.hw,
+ [CLK_CSI_MCLK] = &csi_mclk_clk.common.hw,
+ [CLK_BUS_CSI] = &bus_csi_clk.common.hw,
+ [CLK_HDCP] = &hdcp_clk.common.hw,
+ [CLK_BUS_HDCP] = &bus_hdcp_clk.common.hw,
+ },
+ .num = CLK_NUMBER,
+};
+
+static struct ccu_reset_map sun50i_h6_ccu_resets[] = {
+ [RST_MBUS] = { 0x540, BIT(30) },
+
+ [RST_BUS_DE] = { 0x60c, BIT(16) },
+ [RST_BUS_DEINTERLACE] = { 0x62c, BIT(16) },
+ [RST_BUS_GPU] = { 0x67c, BIT(16) },
+ [RST_BUS_CE] = { 0x68c, BIT(16) },
+ [RST_BUS_VE] = { 0x69c, BIT(16) },
+ [RST_BUS_EMCE] = { 0x6bc, BIT(16) },
+ [RST_BUS_VP9] = { 0x6cc, BIT(16) },
+ [RST_BUS_DMA] = { 0x70c, BIT(16) },
+ [RST_BUS_MSGBOX] = { 0x71c, BIT(16) },
+ [RST_BUS_SPINLOCK] = { 0x72c, BIT(16) },
+ [RST_BUS_HSTIMER] = { 0x73c, BIT(16) },
+ [RST_BUS_DBG] = { 0x78c, BIT(16) },
+ [RST_BUS_PSI] = { 0x79c, BIT(16) },
+ [RST_BUS_PWM] = { 0x7ac, BIT(16) },
+ [RST_BUS_IOMMU] = { 0x7bc, BIT(16) },
+ [RST_BUS_DRAM] = { 0x80c, BIT(16) },
+ [RST_BUS_NAND] = { 0x82c, BIT(16) },
+ [RST_BUS_MMC0] = { 0x84c, BIT(16) },
+ [RST_BUS_MMC1] = { 0x84c, BIT(17) },
+ [RST_BUS_MMC2] = { 0x84c, BIT(18) },
+ [RST_BUS_UART0] = { 0x90c, BIT(16) },
+ [RST_BUS_UART1] = { 0x90c, BIT(17) },
+ [RST_BUS_UART2] = { 0x90c, BIT(18) },
+ [RST_BUS_UART3] = { 0x90c, BIT(19) },
+ [RST_BUS_I2C0] = { 0x91c, BIT(16) },
+ [RST_BUS_I2C1] = { 0x91c, BIT(17) },
+ [RST_BUS_I2C2] = { 0x91c, BIT(18) },
+ [RST_BUS_I2C3] = { 0x91c, BIT(19) },
+ [RST_BUS_SCR0] = { 0x93c, BIT(16) },
+ [RST_BUS_SCR1] = { 0x93c, BIT(17) },
+ [RST_BUS_SPI0] = { 0x96c, BIT(16) },
+ [RST_BUS_SPI1] = { 0x96c, BIT(17) },
+ [RST_BUS_EMAC] = { 0x97c, BIT(16) },
+ [RST_BUS_TS] = { 0x9bc, BIT(16) },
+ [RST_BUS_IR_TX] = { 0x9cc, BIT(16) },
+ [RST_BUS_THS] = { 0x9fc, BIT(16) },
+ [RST_BUS_I2S0] = { 0xa1c, BIT(16) },
+ [RST_BUS_I2S1] = { 0xa1c, BIT(17) },
+ [RST_BUS_I2S2] = { 0xa1c, BIT(18) },
+ [RST_BUS_I2S3] = { 0xa1c, BIT(19) },
+ [RST_BUS_SPDIF] = { 0xa2c, BIT(16) },
+ [RST_BUS_DMIC] = { 0xa4c, BIT(16) },
+ [RST_BUS_AUDIO_HUB] = { 0xa6c, BIT(16) },
+
+ [RST_USB_PHY0] = { 0xa70, BIT(30) },
+ [RST_USB_PHY1] = { 0xa74, BIT(30) },
+ [RST_USB_PHY3] = { 0xa7c, BIT(30) },
+ [RST_USB_HSIC] = { 0xa7c, BIT(28) },
+
+ [RST_BUS_OHCI0] = { 0xa8c, BIT(16) },
+ [RST_BUS_OHCI3] = { 0xa8c, BIT(19) },
+ [RST_BUS_EHCI0] = { 0xa8c, BIT(20) },
+ [RST_BUS_XHCI] = { 0xa8c, BIT(21) },
+ [RST_BUS_EHCI3] = { 0xa8c, BIT(23) },
+ [RST_BUS_OTG] = { 0xa8c, BIT(24) },
+ [RST_BUS_PCIE] = { 0xabc, BIT(16) },
+
+ [RST_PCIE_POWERUP] = { 0xabc, BIT(17) },
+
+ [RST_BUS_HDMI] = { 0xb1c, BIT(16) },
+ [RST_BUS_HDMI_SUB] = { 0xb1c, BIT(17) },
+ [RST_BUS_TCON_TOP] = { 0xb5c, BIT(16) },
+ [RST_BUS_TCON_LCD0] = { 0xb7c, BIT(16) },
+ [RST_BUS_TCON_TV0] = { 0xb9c, BIT(16) },
+ [RST_BUS_CSI] = { 0xc2c, BIT(16) },
+ [RST_BUS_HDCP] = { 0xc4c, BIT(16) },
+};
+
+static const struct sunxi_ccu_desc sun50i_h6_ccu_desc = {
+ .ccu_clks = sun50i_h6_ccu_clks,
+ .num_ccu_clks = ARRAY_SIZE(sun50i_h6_ccu_clks),
+
+ .hw_clks = &sun50i_h6_hw_clks,
+
+ .resets = sun50i_h6_ccu_resets,
+ .num_resets = ARRAY_SIZE(sun50i_h6_ccu_resets),
+};
+
+static const u32 pll_regs[] = {
+ SUN50I_H6_PLL_CPUX_REG,
+ SUN50I_H6_PLL_DDR0_REG,
+ SUN50I_H6_PLL_PERIPH0_REG,
+ SUN50I_H6_PLL_PERIPH1_REG,
+ SUN50I_H6_PLL_GPU_REG,
+ SUN50I_H6_PLL_VIDEO0_REG,
+ SUN50I_H6_PLL_VIDEO1_REG,
+ SUN50I_H6_PLL_VE_REG,
+ SUN50I_H6_PLL_DE_REG,
+ SUN50I_H6_PLL_HSIC_REG,
+ SUN50I_H6_PLL_AUDIO_REG,
+};
+
+static const u32 pll_video_regs[] = {
+ SUN50I_H6_PLL_VIDEO0_REG,
+ SUN50I_H6_PLL_VIDEO1_REG,
+};
+
+static const u32 usb2_clk_regs[] = {
+ SUN50I_H6_USB0_CLK_REG,
+ SUN50I_H6_USB3_CLK_REG,
+};
+
+static int sun50i_h6_ccu_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ void __iomem *reg;
+ u32 val;
+ int i;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ reg = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
+
+ /* Enable the lock bits on all PLLs */
+ for (i = 0; i < ARRAY_SIZE(pll_regs); i++) {
+ val = readl(reg + pll_regs[i]);
+ val |= BIT(29);
+ writel(val, reg + pll_regs[i]);
+ }
+
+ /*
+ * Force the output divider of video PLLs to 0.
+ *
+ * See the comment before pll-video0 definition for the reason.
+ */
+ for (i = 0; i < ARRAY_SIZE(pll_video_regs); i++) {
+ val = readl(reg + pll_video_regs[i]);
+ val &= ~BIT(0);
+ writel(val, reg + pll_video_regs[i]);
+ }
+
+ /*
+ * Force OHCI 12M clock sources to 00 (12MHz divided from 48MHz)
+ *
+ * This clock mux is still mysterious, and the code just enforces
+ * it to have a valid clock parent.
+ */
+ for (i = 0; i < ARRAY_SIZE(usb2_clk_regs); i++) {
+ val = readl(reg + usb2_clk_regs[i]);
+ val &= ~GENMASK(25, 24);
+ writel (val, reg + usb2_clk_regs[i]);
+ }
+
+ /*
+ * Force the post-divider of pll-audio to 8 and the output divider
+ * of it to 1, to make the clock name represents the real frequency.
+ */
+ val = readl(reg + SUN50I_H6_PLL_AUDIO_REG);
+ val &= ~(GENMASK(21, 16) | BIT(0));
+ writel(val | (7 << 16), reg + SUN50I_H6_PLL_AUDIO_REG);
+
+ return sunxi_ccu_probe(pdev->dev.of_node, reg, &sun50i_h6_ccu_desc);
+}
+
+static const struct of_device_id sun50i_h6_ccu_ids[] = {
+ { .compatible = "allwinner,sun50i-h6-ccu" },
+ { }
+};
+
+static struct platform_driver sun50i_h6_ccu_driver = {
+ .probe = sun50i_h6_ccu_probe,
+ .driver = {
+ .name = "sun50i-h6-ccu",
+ .of_match_table = sun50i_h6_ccu_ids,
+ },
+};
+builtin_platform_driver(sun50i_h6_ccu_driver);
diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-h6.h b/drivers/clk/sunxi-ng/ccu-sun50i-h6.h
new file mode 100644
index 000000000000..2ccfe4428260
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun50i-h6.h
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2016 Icenowy Zheng <icenowy@aosc.io>
+ */
+
+#ifndef _CCU_SUN50I_H6_H_
+#define _CCU_SUN50I_H6_H_
+
+#include <dt-bindings/clock/sun50i-h6-ccu.h>
+#include <dt-bindings/reset/sun50i-h6-ccu.h>
+
+#define CLK_OSC12M 0
+#define CLK_PLL_CPUX 1
+#define CLK_PLL_DDR0 2
+
+/* PLL_PERIPH0 exported for PRCM */
+
+#define CLK_PLL_PERIPH0_2X 4
+#define CLK_PLL_PERIPH0_4X 5
+#define CLK_PLL_PERIPH1 6
+#define CLK_PLL_PERIPH1_2X 7
+#define CLK_PLL_PERIPH1_4X 8
+#define CLK_PLL_GPU 9
+#define CLK_PLL_VIDEO0 10
+#define CLK_PLL_VIDEO0_4X 11
+#define CLK_PLL_VIDEO1 12
+#define CLK_PLL_VIDEO1_4X 13
+#define CLK_PLL_VE 14
+#define CLK_PLL_DE 15
+#define CLK_PLL_HSIC 16
+#define CLK_PLL_AUDIO_BASE 17
+#define CLK_PLL_AUDIO 18
+#define CLK_PLL_AUDIO_2X 19
+#define CLK_PLL_AUDIO_4X 20
+
+/* CPUX clock exported for DVFS */
+
+#define CLK_AXI 22
+#define CLK_CPUX_APB 23
+#define CLK_PSI_AHB1_AHB2 24
+#define CLK_AHB3 25
+
+/* APB1 clock exported for PIO */
+
+#define CLK_APB2 27
+#define CLK_MBUS 28
+
+/* All module clocks and bus gates are exported except DRAM */
+
+#define CLK_DRAM 52
+
+#define CLK_BUS_DRAM 60
+
+#define CLK_NUMBER (CLK_BUS_HDCP + 1)
+
+#endif /* _CCU_SUN50I_H6_H_ */
diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
index 29bc0566b776..77ed0b0ba681 100644
--- a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
@@ -69,17 +69,18 @@ static SUNXI_CCU_NM_WITH_SDM_GATE_LOCK(pll_audio_base_clk, "pll-audio-base",
BIT(28), /* lock */
CLK_SET_RATE_UNGATE);
-static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_video_clk, "pll-video",
- "osc24M", 0x0010,
- 8, 7, /* N */
- 0, 4, /* M */
- BIT(24), /* frac enable */
- BIT(25), /* frac select */
- 270000000, /* frac rate 0 */
- 297000000, /* frac rate 1 */
- BIT(31), /* gate */
- BIT(28), /* lock */
- CLK_SET_RATE_UNGATE);
+static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK_MIN(pll_video_clk, "pll-video",
+ "osc24M", 0x0010,
+ 192000000, /* Minimum rate */
+ 8, 7, /* N */
+ 0, 4, /* M */
+ BIT(24), /* frac enable */
+ BIT(25), /* frac select */
+ 270000000, /* frac rate 0 */
+ 297000000, /* frac rate 1 */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ CLK_SET_RATE_UNGATE);
static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_ve_clk, "pll-ve",
"osc24M", 0x0018,
@@ -451,11 +452,13 @@ static SUNXI_CCU_GATE(dram_ts_clk, "dram-ts", "dram",
static const char * const de_parents[] = { "pll-periph0-2x", "pll-de" };
static SUNXI_CCU_M_WITH_MUX_GATE(de_clk, "de", de_parents,
- 0x104, 0, 4, 24, 3, BIT(31), 0);
+ 0x104, 0, 4, 24, 3, BIT(31),
+ CLK_SET_RATE_PARENT);
static const char * const tcon_parents[] = { "pll-video" };
static SUNXI_CCU_M_WITH_MUX_GATE(tcon_clk, "tcon", tcon_parents,
- 0x118, 0, 4, 24, 3, BIT(31), 0);
+ 0x118, 0, 4, 24, 3, BIT(31),
+ CLK_SET_RATE_PARENT);
static const char * const tve_parents[] = { "pll-de", "pll-periph1" };
static SUNXI_CCU_M_WITH_MUX_GATE(tve_clk, "tve", tve_parents,
@@ -486,7 +489,8 @@ static SUNXI_CCU_GATE(avs_clk, "avs", "osc24M",
static const char * const hdmi_parents[] = { "pll-video" };
static SUNXI_CCU_M_WITH_MUX_GATE(hdmi_clk, "hdmi", hdmi_parents,
- 0x150, 0, 4, 24, 2, BIT(31), 0);
+ 0x150, 0, 4, 24, 2, BIT(31),
+ CLK_SET_RATE_PARENT);
static SUNXI_CCU_GATE(hdmi_ddc_clk, "hdmi-ddc", "osc24M",
0x154, BIT(31), 0);
diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-h3.h b/drivers/clk/sunxi-ng/ccu-sun8i-h3.h
index 1b4baea37d81..73d7392c968c 100644
--- a/drivers/clk/sunxi-ng/ccu-sun8i-h3.h
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-h3.h
@@ -26,7 +26,9 @@
#define CLK_PLL_AUDIO_2X 3
#define CLK_PLL_AUDIO_4X 4
#define CLK_PLL_AUDIO_8X 5
-#define CLK_PLL_VIDEO 6
+
+/* PLL_VIDEO is exported */
+
#define CLK_PLL_VE 7
#define CLK_PLL_DDR 8
diff --git a/drivers/clk/sunxi-ng/ccu_nkmp.c b/drivers/clk/sunxi-ng/ccu_nkmp.c
index e58c95787f94..ebd9436d2c7c 100644
--- a/drivers/clk/sunxi-ng/ccu_nkmp.c
+++ b/drivers/clk/sunxi-ng/ccu_nkmp.c
@@ -20,6 +20,18 @@ struct _ccu_nkmp {
unsigned long p, min_p, max_p;
};
+static unsigned long ccu_nkmp_calc_rate(unsigned long parent,
+ unsigned long n, unsigned long k,
+ unsigned long m, unsigned long p)
+{
+ u64 rate = parent;
+
+ rate *= n * k;
+ do_div(rate, m * p);
+
+ return rate;
+}
+
static void ccu_nkmp_find_best(unsigned long parent, unsigned long rate,
struct _ccu_nkmp *nkmp)
{
@@ -33,7 +45,9 @@ static void ccu_nkmp_find_best(unsigned long parent, unsigned long rate,
for (_p = nkmp->min_p; _p <= nkmp->max_p; _p <<= 1) {
unsigned long tmp_rate;
- tmp_rate = parent * _n * _k / (_m * _p);
+ tmp_rate = ccu_nkmp_calc_rate(parent,
+ _n, _k,
+ _m, _p);
if (tmp_rate > rate)
continue;
@@ -81,7 +95,7 @@ static unsigned long ccu_nkmp_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct ccu_nkmp *nkmp = hw_to_ccu_nkmp(hw);
- unsigned long n, m, k, p;
+ unsigned long n, m, k, p, rate;
u32 reg;
reg = readl(nkmp->common.base + nkmp->common.reg);
@@ -107,7 +121,11 @@ static unsigned long ccu_nkmp_recalc_rate(struct clk_hw *hw,
p = reg >> nkmp->p.shift;
p &= (1 << nkmp->p.width) - 1;
- return (parent_rate * n * k >> p) / m;
+ rate = ccu_nkmp_calc_rate(parent_rate, n, k, m, 1 << p);
+ if (nkmp->common.features & CCU_FEATURE_FIXED_POSTDIV)
+ rate /= nkmp->fixed_post_div;
+
+ return rate;
}
static long ccu_nkmp_round_rate(struct clk_hw *hw, unsigned long rate,
@@ -116,6 +134,9 @@ static long ccu_nkmp_round_rate(struct clk_hw *hw, unsigned long rate,
struct ccu_nkmp *nkmp = hw_to_ccu_nkmp(hw);
struct _ccu_nkmp _nkmp;
+ if (nkmp->common.features & CCU_FEATURE_FIXED_POSTDIV)
+ rate *= nkmp->fixed_post_div;
+
_nkmp.min_n = nkmp->n.min ?: 1;
_nkmp.max_n = nkmp->n.max ?: 1 << nkmp->n.width;
_nkmp.min_k = nkmp->k.min ?: 1;
@@ -127,17 +148,26 @@ static long ccu_nkmp_round_rate(struct clk_hw *hw, unsigned long rate,
ccu_nkmp_find_best(*parent_rate, rate, &_nkmp);
- return *parent_rate * _nkmp.n * _nkmp.k / (_nkmp.m * _nkmp.p);
+ rate = ccu_nkmp_calc_rate(*parent_rate, _nkmp.n, _nkmp.k,
+ _nkmp.m, _nkmp.p);
+ if (nkmp->common.features & CCU_FEATURE_FIXED_POSTDIV)
+ rate = rate / nkmp->fixed_post_div;
+
+ return rate;
}
static int ccu_nkmp_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct ccu_nkmp *nkmp = hw_to_ccu_nkmp(hw);
+ u32 n_mask, k_mask, m_mask, p_mask;
struct _ccu_nkmp _nkmp;
unsigned long flags;
u32 reg;
+ if (nkmp->common.features & CCU_FEATURE_FIXED_POSTDIV)
+ rate = rate * nkmp->fixed_post_div;
+
_nkmp.min_n = nkmp->n.min ?: 1;
_nkmp.max_n = nkmp->n.max ?: 1 << nkmp->n.width;
_nkmp.min_k = nkmp->k.min ?: 1;
@@ -149,18 +179,20 @@ static int ccu_nkmp_set_rate(struct clk_hw *hw, unsigned long rate,
ccu_nkmp_find_best(parent_rate, rate, &_nkmp);
+ n_mask = GENMASK(nkmp->n.width + nkmp->n.shift - 1, nkmp->n.shift);
+ k_mask = GENMASK(nkmp->k.width + nkmp->k.shift - 1, nkmp->k.shift);
+ m_mask = GENMASK(nkmp->m.width + nkmp->m.shift - 1, nkmp->m.shift);
+ p_mask = GENMASK(nkmp->p.width + nkmp->p.shift - 1, nkmp->p.shift);
+
spin_lock_irqsave(nkmp->common.lock, flags);
reg = readl(nkmp->common.base + nkmp->common.reg);
- reg &= ~GENMASK(nkmp->n.width + nkmp->n.shift - 1, nkmp->n.shift);
- reg &= ~GENMASK(nkmp->k.width + nkmp->k.shift - 1, nkmp->k.shift);
- reg &= ~GENMASK(nkmp->m.width + nkmp->m.shift - 1, nkmp->m.shift);
- reg &= ~GENMASK(nkmp->p.width + nkmp->p.shift - 1, nkmp->p.shift);
-
- reg |= (_nkmp.n - nkmp->n.offset) << nkmp->n.shift;
- reg |= (_nkmp.k - nkmp->k.offset) << nkmp->k.shift;
- reg |= (_nkmp.m - nkmp->m.offset) << nkmp->m.shift;
- reg |= ilog2(_nkmp.p) << nkmp->p.shift;
+ reg &= ~(n_mask | k_mask | m_mask | p_mask);
+
+ reg |= ((_nkmp.n - nkmp->n.offset) << nkmp->n.shift) & n_mask;
+ reg |= ((_nkmp.k - nkmp->k.offset) << nkmp->k.shift) & k_mask;
+ reg |= ((_nkmp.m - nkmp->m.offset) << nkmp->m.shift) & m_mask;
+ reg |= (ilog2(_nkmp.p) << nkmp->p.shift) & p_mask;
writel(reg, nkmp->common.base + nkmp->common.reg);
diff --git a/drivers/clk/sunxi-ng/ccu_nkmp.h b/drivers/clk/sunxi-ng/ccu_nkmp.h
index a82facbc6144..6940503e7fc4 100644
--- a/drivers/clk/sunxi-ng/ccu_nkmp.h
+++ b/drivers/clk/sunxi-ng/ccu_nkmp.h
@@ -34,6 +34,8 @@ struct ccu_nkmp {
struct ccu_div_internal m;
struct ccu_div_internal p;
+ unsigned int fixed_post_div;
+
struct ccu_common common;
};
diff --git a/drivers/clk/sunxi-ng/ccu_nm.c b/drivers/clk/sunxi-ng/ccu_nm.c
index a16de092bf94..4e2073307f34 100644
--- a/drivers/clk/sunxi-ng/ccu_nm.c
+++ b/drivers/clk/sunxi-ng/ccu_nm.c
@@ -117,6 +117,13 @@ static long ccu_nm_round_rate(struct clk_hw *hw, unsigned long rate,
if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV)
rate *= nm->fixed_post_div;
+ if (rate < nm->min_rate) {
+ rate = nm->min_rate;
+ if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV)
+ rate /= nm->fixed_post_div;
+ return rate;
+ }
+
if (ccu_frac_helper_has_rate(&nm->common, &nm->frac, rate)) {
if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV)
rate /= nm->fixed_post_div;
diff --git a/drivers/clk/sunxi-ng/ccu_nm.h b/drivers/clk/sunxi-ng/ccu_nm.h
index eba586b4c7d0..1d8b459c50b7 100644
--- a/drivers/clk/sunxi-ng/ccu_nm.h
+++ b/drivers/clk/sunxi-ng/ccu_nm.h
@@ -37,6 +37,7 @@ struct ccu_nm {
struct ccu_sdm_internal sdm;
unsigned int fixed_post_div;
+ unsigned int min_rate;
struct ccu_common common;
};
@@ -88,6 +89,32 @@ struct ccu_nm {
}, \
}
+#define SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK_MIN(_struct, _name, _parent, \
+ _reg, _min_rate, \
+ _nshift, _nwidth, \
+ _mshift, _mwidth, \
+ _frac_en, _frac_sel, \
+ _frac_rate_0, _frac_rate_1,\
+ _gate, _lock, _flags) \
+ struct ccu_nm _struct = { \
+ .enable = _gate, \
+ .lock = _lock, \
+ .n = _SUNXI_CCU_MULT(_nshift, _nwidth), \
+ .m = _SUNXI_CCU_DIV(_mshift, _mwidth), \
+ .frac = _SUNXI_CCU_FRAC(_frac_en, _frac_sel, \
+ _frac_rate_0, \
+ _frac_rate_1), \
+ .min_rate = _min_rate, \
+ .common = { \
+ .reg = _reg, \
+ .features = CCU_FEATURE_FRACTIONAL, \
+ .hw.init = CLK_HW_INIT(_name, \
+ _parent, \
+ &ccu_nm_ops, \
+ _flags), \
+ }, \
+ }
+
#define SUNXI_CCU_NM_WITH_GATE_LOCK(_struct, _name, _parent, _reg, \
_nshift, _nwidth, \
_mshift, _mwidth, \
diff --git a/drivers/clk/tegra/clk-emc.c b/drivers/clk/tegra/clk-emc.c
index 11a5066e5c27..5234acd30e89 100644
--- a/drivers/clk/tegra/clk-emc.c
+++ b/drivers/clk/tegra/clk-emc.c
@@ -515,7 +515,7 @@ struct clk *tegra_clk_register_emc(void __iomem *base, struct device_node *np,
init.name = "emc";
init.ops = &tegra_clk_emc_ops;
- init.flags = 0;
+ init.flags = CLK_IS_CRITICAL;
init.parent_names = emc_parent_clk_names;
init.num_parents = ARRAY_SIZE(emc_parent_clk_names);
diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c
index 7c369e21c91c..830d1c87fa7c 100644
--- a/drivers/clk/tegra/clk-pll.c
+++ b/drivers/clk/tegra/clk-pll.c
@@ -1151,6 +1151,8 @@ static const struct clk_ops tegra_clk_pllu_ops = {
.enable = clk_pllu_enable,
.disable = clk_pll_disable,
.recalc_rate = clk_pll_recalc_rate,
+ .round_rate = clk_pll_round_rate,
+ .set_rate = clk_pll_set_rate,
};
static int _pll_fixed_mdiv(struct tegra_clk_pll_params *pll_params,
diff --git a/drivers/clk/tegra/clk-tegra-periph.c b/drivers/clk/tegra/clk-tegra-periph.c
index c02711927d79..2acba2986bc6 100644
--- a/drivers/clk/tegra/clk-tegra-periph.c
+++ b/drivers/clk/tegra/clk-tegra-periph.c
@@ -830,7 +830,7 @@ static struct tegra_periph_init_data gate_clks[] = {
GATE("xusb_host", "xusb_host_src", 89, 0, tegra_clk_xusb_host, 0),
GATE("xusb_ss", "xusb_ss_src", 156, 0, tegra_clk_xusb_ss, 0),
GATE("xusb_dev", "xusb_dev_src", 95, 0, tegra_clk_xusb_dev, 0),
- GATE("emc", "emc_mux", 57, 0, tegra_clk_emc, CLK_IGNORE_UNUSED),
+ GATE("emc", "emc_mux", 57, 0, tegra_clk_emc, CLK_IS_CRITICAL),
GATE("sata_cold", "clk_m", 129, TEGRA_PERIPH_ON_APB, tegra_clk_sata_cold, 0),
GATE("ispa", "isp", 23, 0, tegra_clk_ispa, 0),
GATE("ispb", "isp", 3, 0, tegra_clk_ispb, 0),
diff --git a/drivers/clk/tegra/clk-tegra-super-gen4.c b/drivers/clk/tegra/clk-tegra-super-gen4.c
index 10047107c1dc..89d6b47a27a8 100644
--- a/drivers/clk/tegra/clk-tegra-super-gen4.c
+++ b/drivers/clk/tegra/clk-tegra-super-gen4.c
@@ -125,7 +125,8 @@ static void __init tegra_sclk_init(void __iomem *clk_base,
/* SCLK */
dt_clk = tegra_lookup_dt_id(tegra_clk_sclk, tegra_clks);
if (dt_clk) {
- clk = clk_register_divider(NULL, "sclk", "sclk_mux", 0,
+ clk = clk_register_divider(NULL, "sclk", "sclk_mux",
+ CLK_IS_CRITICAL,
clk_base + SCLK_DIVIDER, 0, 8,
0, &sysrate_lock);
*dt_clk = clk;
@@ -137,7 +138,8 @@ static void __init tegra_sclk_init(void __iomem *clk_base,
clk = tegra_clk_register_super_mux("sclk",
gen_info->sclk_parents,
gen_info->num_sclk_parents,
- CLK_SET_RATE_PARENT,
+ CLK_SET_RATE_PARENT |
+ CLK_IS_CRITICAL,
clk_base + SCLK_BURST_POLICY,
0, 4, 0, 0, NULL);
*dt_clk = clk;
@@ -151,7 +153,7 @@ static void __init tegra_sclk_init(void __iomem *clk_base,
clk_base + SYSTEM_CLK_RATE, 4, 2, 0,
&sysrate_lock);
clk = clk_register_gate(NULL, "hclk", "hclk_div",
- CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
clk_base + SYSTEM_CLK_RATE,
7, CLK_GATE_SET_TO_DISABLE, &sysrate_lock);
*dt_clk = clk;
diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c
index 63087d17c3e2..5d5a22d529f5 100644
--- a/drivers/clk/tegra/clk-tegra114.c
+++ b/drivers/clk/tegra/clk-tegra114.c
@@ -955,8 +955,7 @@ static void __init tegra114_pll_init(void __iomem *clk_base,
/* PLLM */
clk = tegra_clk_register_pllm("pll_m", "pll_ref", clk_base, pmc,
- CLK_IGNORE_UNUSED | CLK_SET_RATE_GATE,
- &pll_m_params, NULL);
+ CLK_SET_RATE_GATE, &pll_m_params, NULL);
clks[TEGRA114_CLK_PLL_M] = clk;
/* PLLM_OUT1 */
@@ -1190,6 +1189,7 @@ static struct tegra_clk_init_table init_table[] __initdata = {
{ TEGRA114_CLK_XUSB_HS_SRC, TEGRA114_CLK_XUSB_SS_DIV2, 61200000, 0 },
{ TEGRA114_CLK_XUSB_FALCON_SRC, TEGRA114_CLK_PLL_P, 204000000, 0 },
{ TEGRA114_CLK_XUSB_HOST_SRC, TEGRA114_CLK_PLL_P, 102000000, 0 },
+ { TEGRA114_CLK_VDE, TEGRA114_CLK_CLK_MAX, 600000000, 0 },
/* must be the last entry */
{ TEGRA114_CLK_CLK_MAX, TEGRA114_CLK_CLK_MAX, 0, 0 },
};
diff --git a/drivers/clk/tegra/clk-tegra124.c b/drivers/clk/tegra/clk-tegra124.c
index e81ea5b11577..50088e976611 100644
--- a/drivers/clk/tegra/clk-tegra124.c
+++ b/drivers/clk/tegra/clk-tegra124.c
@@ -1089,8 +1089,7 @@ static void __init tegra124_pll_init(void __iomem *clk_base,
/* PLLM */
clk = tegra_clk_register_pllm("pll_m", "pll_ref", clk_base, pmc,
- CLK_IGNORE_UNUSED | CLK_SET_RATE_GATE,
- &pll_m_params, NULL);
+ CLK_SET_RATE_GATE, &pll_m_params, NULL);
clk_register_clkdev(clk, "pll_m", NULL);
clks[TEGRA124_CLK_PLL_M] = clk;
@@ -1099,7 +1098,7 @@ static void __init tegra124_pll_init(void __iomem *clk_base,
clk_base + PLLM_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
8, 8, 1, NULL);
clk = tegra_clk_register_pll_out("pll_m_out1", "pll_m_out1_div",
- clk_base + PLLM_OUT, 1, 0, CLK_IGNORE_UNUSED |
+ clk_base + PLLM_OUT, 1, 0,
CLK_SET_RATE_PARENT, 0, NULL);
clk_register_clkdev(clk, "pll_m_out1", NULL);
clks[TEGRA124_CLK_PLL_M_OUT1] = clk;
@@ -1268,11 +1267,11 @@ static struct tegra_clk_init_table common_init_table[] __initdata = {
{ TEGRA124_CLK_I2S2, TEGRA124_CLK_PLL_A_OUT0, 11289600, 0 },
{ TEGRA124_CLK_I2S3, TEGRA124_CLK_PLL_A_OUT0, 11289600, 0 },
{ TEGRA124_CLK_I2S4, TEGRA124_CLK_PLL_A_OUT0, 11289600, 0 },
- { TEGRA124_CLK_VDE, TEGRA124_CLK_PLL_P, 0, 0 },
+ { TEGRA124_CLK_VDE, TEGRA124_CLK_CLK_MAX, 600000000, 0 },
{ TEGRA124_CLK_HOST1X, TEGRA124_CLK_PLL_P, 136000000, 1 },
{ TEGRA124_CLK_DSIALP, TEGRA124_CLK_PLL_P, 68000000, 0 },
{ TEGRA124_CLK_DSIBLP, TEGRA124_CLK_PLL_P, 68000000, 0 },
- { TEGRA124_CLK_SCLK, TEGRA124_CLK_PLL_P_OUT2, 102000000, 1 },
+ { TEGRA124_CLK_SCLK, TEGRA124_CLK_PLL_P_OUT2, 102000000, 0 },
{ TEGRA124_CLK_DFLL_SOC, TEGRA124_CLK_PLL_P, 51000000, 1 },
{ TEGRA124_CLK_DFLL_REF, TEGRA124_CLK_PLL_P, 51000000, 1 },
{ TEGRA124_CLK_PLL_C, TEGRA124_CLK_CLK_MAX, 768000000, 0 },
diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c
index cbd5a2e5c569..0ee56dd04cec 100644
--- a/drivers/clk/tegra/clk-tegra20.c
+++ b/drivers/clk/tegra/clk-tegra20.c
@@ -576,6 +576,7 @@ static struct tegra_clk tegra20_clks[tegra_clk_max] __initdata = {
[tegra_clk_afi] = { .dt_id = TEGRA20_CLK_AFI, .present = true },
[tegra_clk_fuse] = { .dt_id = TEGRA20_CLK_FUSE, .present = true },
[tegra_clk_kfuse] = { .dt_id = TEGRA20_CLK_KFUSE, .present = true },
+ [tegra_clk_emc] = { .dt_id = TEGRA20_CLK_EMC, .present = true },
};
static unsigned long tegra20_clk_measure_input_freq(void)
@@ -651,8 +652,7 @@ static void tegra20_pll_init(void)
/* PLLM */
clk = tegra_clk_register_pll("pll_m", "pll_ref", clk_base, NULL,
- CLK_IGNORE_UNUSED | CLK_SET_RATE_GATE,
- &pll_m_params, NULL);
+ CLK_SET_RATE_GATE, &pll_m_params, NULL);
clks[TEGRA20_CLK_PLL_M] = clk;
/* PLLM_OUT1 */
@@ -660,7 +660,7 @@ static void tegra20_pll_init(void)
clk_base + PLLM_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
8, 8, 1, NULL);
clk = tegra_clk_register_pll_out("pll_m_out1", "pll_m_out1_div",
- clk_base + PLLM_OUT, 1, 0, CLK_IGNORE_UNUSED |
+ clk_base + PLLM_OUT, 1, 0,
CLK_SET_RATE_PARENT, 0, NULL);
clks[TEGRA20_CLK_PLL_M_OUT1] = clk;
@@ -723,7 +723,8 @@ static void tegra20_super_clk_init(void)
/* SCLK */
clk = tegra_clk_register_super_mux("sclk", sclk_parents,
- ARRAY_SIZE(sclk_parents), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(sclk_parents),
+ CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
clk_base + SCLK_BURST_POLICY, 0, 4, 0, 0, NULL);
clks[TEGRA20_CLK_SCLK] = clk;
@@ -814,9 +815,6 @@ static void __init tegra20_periph_clk_init(void)
CLK_SET_RATE_NO_REPARENT,
clk_base + CLK_SOURCE_EMC,
30, 2, 0, &emc_lock);
- clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base, 0,
- 57, periph_clk_enb_refcnt);
- clks[TEGRA20_CLK_EMC] = clk;
clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC,
&emc_lock);
@@ -1019,13 +1017,12 @@ static struct tegra_clk_init_table init_table[] __initdata = {
{ TEGRA20_CLK_PLL_P_OUT2, TEGRA20_CLK_CLK_MAX, 48000000, 1 },
{ TEGRA20_CLK_PLL_P_OUT3, TEGRA20_CLK_CLK_MAX, 72000000, 1 },
{ TEGRA20_CLK_PLL_P_OUT4, TEGRA20_CLK_CLK_MAX, 24000000, 1 },
- { TEGRA20_CLK_PLL_C, TEGRA20_CLK_CLK_MAX, 600000000, 1 },
- { TEGRA20_CLK_PLL_C_OUT1, TEGRA20_CLK_CLK_MAX, 216000000, 1 },
- { TEGRA20_CLK_SCLK, TEGRA20_CLK_PLL_C_OUT1, 0, 1 },
- { TEGRA20_CLK_HCLK, TEGRA20_CLK_CLK_MAX, 0, 1 },
- { TEGRA20_CLK_PCLK, TEGRA20_CLK_CLK_MAX, 60000000, 1 },
+ { TEGRA20_CLK_PLL_C, TEGRA20_CLK_CLK_MAX, 600000000, 0 },
+ { TEGRA20_CLK_PLL_C_OUT1, TEGRA20_CLK_CLK_MAX, 240000000, 0 },
+ { TEGRA20_CLK_SCLK, TEGRA20_CLK_PLL_C_OUT1, 240000000, 0 },
+ { TEGRA20_CLK_HCLK, TEGRA20_CLK_CLK_MAX, 240000000, 0 },
+ { TEGRA20_CLK_PCLK, TEGRA20_CLK_CLK_MAX, 60000000, 0 },
{ TEGRA20_CLK_CSITE, TEGRA20_CLK_CLK_MAX, 0, 1 },
- { TEGRA20_CLK_EMC, TEGRA20_CLK_CLK_MAX, 0, 1 },
{ TEGRA20_CLK_CCLK, TEGRA20_CLK_CLK_MAX, 0, 1 },
{ TEGRA20_CLK_UARTA, TEGRA20_CLK_PLL_P, 0, 0 },
{ TEGRA20_CLK_UARTB, TEGRA20_CLK_PLL_P, 0, 0 },
@@ -1051,6 +1048,7 @@ static struct tegra_clk_init_table init_table[] __initdata = {
{ TEGRA20_CLK_DISP2, TEGRA20_CLK_PLL_P, 600000000, 0 },
{ TEGRA20_CLK_GR2D, TEGRA20_CLK_PLL_C, 300000000, 0 },
{ TEGRA20_CLK_GR3D, TEGRA20_CLK_PLL_C, 300000000, 0 },
+ { TEGRA20_CLK_VDE, TEGRA20_CLK_CLK_MAX, 300000000, 0 },
/* must be the last entry */
{ TEGRA20_CLK_CLK_MAX, TEGRA20_CLK_CLK_MAX, 0, 0 },
};
diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c
index 9e6260869eb9..9fb5d51ccce4 100644
--- a/drivers/clk/tegra/clk-tegra210.c
+++ b/drivers/clk/tegra/clk-tegra210.c
@@ -22,10 +22,12 @@
#include <linux/of_address.h>
#include <linux/delay.h>
#include <linux/export.h>
+#include <linux/mutex.h>
#include <linux/clk/tegra.h>
#include <dt-bindings/clock/tegra210-car.h>
#include <dt-bindings/reset/tegra210-car.h>
#include <linux/iopoll.h>
+#include <soc/tegra/pmc.h>
#include "clk.h"
#include "clk-id.h"
@@ -41,6 +43,7 @@
#define CLK_SOURCE_CSITE 0x1d4
#define CLK_SOURCE_EMC 0x19c
#define CLK_SOURCE_SOR1 0x410
+#define CLK_SOURCE_LA 0x1f8
#define PLLC_BASE 0x80
#define PLLC_OUT 0x84
@@ -231,6 +234,30 @@
#define CLK_RST_CONTROLLER_RST_DEV_Y_SET 0x2a8
#define CLK_RST_CONTROLLER_RST_DEV_Y_CLR 0x2ac
+#define LVL2_CLK_GATE_OVRA 0xf8
+#define LVL2_CLK_GATE_OVRC 0x3a0
+#define LVL2_CLK_GATE_OVRD 0x3a4
+#define LVL2_CLK_GATE_OVRE 0x554
+
+/* I2S registers to handle during APE MBIST WAR */
+#define TEGRA210_I2S_BASE 0x1000
+#define TEGRA210_I2S_SIZE 0x100
+#define TEGRA210_I2S_CTRLS 5
+#define TEGRA210_I2S_CG 0x88
+#define TEGRA210_I2S_CTRL 0xa0
+
+/* DISPA registers to handle during MBIST WAR */
+#define DC_CMD_DISPLAY_COMMAND 0xc8
+#define DC_COM_DSC_TOP_CTL 0xcf8
+
+/* VIC register to handle during MBIST WAR */
+#define NV_PVIC_THI_SLCG_OVERRIDE_LOW 0x8c
+
+/* APE, DISPA and VIC base addesses needed for MBIST WAR */
+#define TEGRA210_AHUB_BASE 0x702d0000
+#define TEGRA210_DISPA_BASE 0x54200000
+#define TEGRA210_VIC_BASE 0x54340000
+
/*
* SDM fractional divisor is 16-bit 2's complement signed number within
* (-2^12 ... 2^12-1) range. Represented in PLL data structure as unsigned
@@ -255,8 +282,22 @@ static struct cpu_clk_suspend_context {
} tegra210_cpu_clk_sctx;
#endif
+struct tegra210_domain_mbist_war {
+ void (*handle_lvl2_ovr)(struct tegra210_domain_mbist_war *mbist);
+ const u32 lvl2_offset;
+ const u32 lvl2_mask;
+ const unsigned int num_clks;
+ const unsigned int *clk_init_data;
+ struct clk_bulk_data *clks;
+};
+
+static struct clk **clks;
+
static void __iomem *clk_base;
static void __iomem *pmc_base;
+static void __iomem *ahub_base;
+static void __iomem *dispa_base;
+static void __iomem *vic_base;
static unsigned long osc_freq;
static unsigned long pll_ref_freq;
@@ -267,6 +308,7 @@ static DEFINE_SPINLOCK(pll_re_lock);
static DEFINE_SPINLOCK(pll_u_lock);
static DEFINE_SPINLOCK(sor1_lock);
static DEFINE_SPINLOCK(emc_lock);
+static DEFINE_MUTEX(lvl2_ovr_lock);
/* possible OSC frequencies in Hz */
static unsigned long tegra210_input_freq[] = {
@@ -310,6 +352,8 @@ static const char *mux_pllmcp_clkm[] = {
#define PLLA_MISC2_WRITE_MASK 0x06ffffff
/* PLLD */
+#define PLLD_BASE_CSI_CLKSOURCE (1 << 23)
+
#define PLLD_MISC0_EN_SDM (1 << 16)
#define PLLD_MISC0_LOCK_OVERRIDE (1 << 17)
#define PLLD_MISC0_LOCK_ENABLE (1 << 18)
@@ -513,6 +557,115 @@ void tegra210_set_sata_pll_seq_sw(bool state)
}
EXPORT_SYMBOL_GPL(tegra210_set_sata_pll_seq_sw);
+static void tegra210_generic_mbist_war(struct tegra210_domain_mbist_war *mbist)
+{
+ u32 val;
+
+ val = readl_relaxed(clk_base + mbist->lvl2_offset);
+ writel_relaxed(val | mbist->lvl2_mask, clk_base + mbist->lvl2_offset);
+ fence_udelay(1, clk_base);
+ writel_relaxed(val, clk_base + mbist->lvl2_offset);
+ fence_udelay(1, clk_base);
+}
+
+static void tegra210_venc_mbist_war(struct tegra210_domain_mbist_war *mbist)
+{
+ u32 csi_src, ovra, ovre;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&pll_d_lock, flags);
+
+ csi_src = readl_relaxed(clk_base + PLLD_BASE);
+ writel_relaxed(csi_src | PLLD_BASE_CSI_CLKSOURCE, clk_base + PLLD_BASE);
+ fence_udelay(1, clk_base);
+
+ ovra = readl_relaxed(clk_base + LVL2_CLK_GATE_OVRA);
+ writel_relaxed(ovra | BIT(15), clk_base + LVL2_CLK_GATE_OVRA);
+ ovre = readl_relaxed(clk_base + LVL2_CLK_GATE_OVRE);
+ writel_relaxed(ovre | BIT(3), clk_base + LVL2_CLK_GATE_OVRE);
+ fence_udelay(1, clk_base);
+
+ writel_relaxed(ovra, clk_base + LVL2_CLK_GATE_OVRA);
+ writel_relaxed(ovre, clk_base + LVL2_CLK_GATE_OVRE);
+ writel_relaxed(csi_src, clk_base + PLLD_BASE);
+ fence_udelay(1, clk_base);
+
+ spin_unlock_irqrestore(&pll_d_lock, flags);
+}
+
+static void tegra210_disp_mbist_war(struct tegra210_domain_mbist_war *mbist)
+{
+ u32 ovra, dsc_top_ctrl;
+
+ ovra = readl_relaxed(clk_base + LVL2_CLK_GATE_OVRA);
+ writel_relaxed(ovra | BIT(1), clk_base + LVL2_CLK_GATE_OVRA);
+ fence_udelay(1, clk_base);
+
+ dsc_top_ctrl = readl_relaxed(dispa_base + DC_COM_DSC_TOP_CTL);
+ writel_relaxed(dsc_top_ctrl | BIT(2), dispa_base + DC_COM_DSC_TOP_CTL);
+ readl_relaxed(dispa_base + DC_CMD_DISPLAY_COMMAND);
+ writel_relaxed(dsc_top_ctrl, dispa_base + DC_COM_DSC_TOP_CTL);
+ readl_relaxed(dispa_base + DC_CMD_DISPLAY_COMMAND);
+
+ writel_relaxed(ovra, clk_base + LVL2_CLK_GATE_OVRA);
+ fence_udelay(1, clk_base);
+}
+
+static void tegra210_vic_mbist_war(struct tegra210_domain_mbist_war *mbist)
+{
+ u32 ovre, val;
+
+ ovre = readl_relaxed(clk_base + LVL2_CLK_GATE_OVRE);
+ writel_relaxed(ovre | BIT(5), clk_base + LVL2_CLK_GATE_OVRE);
+ fence_udelay(1, clk_base);
+
+ val = readl_relaxed(vic_base + NV_PVIC_THI_SLCG_OVERRIDE_LOW);
+ writel_relaxed(val | BIT(0) | GENMASK(7, 2) | BIT(24),
+ vic_base + NV_PVIC_THI_SLCG_OVERRIDE_LOW);
+ fence_udelay(1, vic_base + NV_PVIC_THI_SLCG_OVERRIDE_LOW);
+
+ writel_relaxed(val, vic_base + NV_PVIC_THI_SLCG_OVERRIDE_LOW);
+ readl(vic_base + NV_PVIC_THI_SLCG_OVERRIDE_LOW);
+
+ writel_relaxed(ovre, clk_base + LVL2_CLK_GATE_OVRE);
+ fence_udelay(1, clk_base);
+}
+
+static void tegra210_ape_mbist_war(struct tegra210_domain_mbist_war *mbist)
+{
+ void __iomem *i2s_base;
+ unsigned int i;
+ u32 ovrc, ovre;
+
+ ovrc = readl_relaxed(clk_base + LVL2_CLK_GATE_OVRC);
+ ovre = readl_relaxed(clk_base + LVL2_CLK_GATE_OVRE);
+ writel_relaxed(ovrc | BIT(1), clk_base + LVL2_CLK_GATE_OVRC);
+ writel_relaxed(ovre | BIT(10) | BIT(11),
+ clk_base + LVL2_CLK_GATE_OVRE);
+ fence_udelay(1, clk_base);
+
+ i2s_base = ahub_base + TEGRA210_I2S_BASE;
+
+ for (i = 0; i < TEGRA210_I2S_CTRLS; i++) {
+ u32 i2s_ctrl;
+
+ i2s_ctrl = readl_relaxed(i2s_base + TEGRA210_I2S_CTRL);
+ writel_relaxed(i2s_ctrl | BIT(10),
+ i2s_base + TEGRA210_I2S_CTRL);
+ writel_relaxed(0, i2s_base + TEGRA210_I2S_CG);
+ readl(i2s_base + TEGRA210_I2S_CG);
+ writel_relaxed(1, i2s_base + TEGRA210_I2S_CG);
+ writel_relaxed(i2s_ctrl, i2s_base + TEGRA210_I2S_CTRL);
+ readl(i2s_base + TEGRA210_I2S_CTRL);
+
+ i2s_base += TEGRA210_I2S_SIZE;
+ }
+
+ writel_relaxed(ovrc, clk_base + LVL2_CLK_GATE_OVRC);
+ writel_relaxed(ovre, clk_base + LVL2_CLK_GATE_OVRE);
+ fence_udelay(1, clk_base);
+}
+
static inline void _pll_misc_chk_default(void __iomem *base,
struct tegra_clk_pll_params *params,
u8 misc_num, u32 default_val, u32 mask)
@@ -2411,13 +2564,150 @@ static struct tegra_audio_clk_info tegra210_audio_plls[] = {
{ "pll_a1", &pll_a1_params, tegra_clk_pll_a1, "pll_ref" },
};
-static struct clk **clks;
-
static const char * const aclk_parents[] = {
"pll_a1", "pll_c", "pll_p", "pll_a_out0", "pll_c2", "pll_c3",
"clk_m"
};
+static const unsigned int nvjpg_slcg_clkids[] = { TEGRA210_CLK_NVDEC };
+static const unsigned int nvdec_slcg_clkids[] = { TEGRA210_CLK_NVJPG };
+static const unsigned int sor_slcg_clkids[] = { TEGRA210_CLK_HDA2CODEC_2X,
+ TEGRA210_CLK_HDA2HDMI, TEGRA210_CLK_DISP1, TEGRA210_CLK_DISP2 };
+static const unsigned int disp_slcg_clkids[] = { TEGRA210_CLK_LA,
+ TEGRA210_CLK_HOST1X};
+static const unsigned int xusba_slcg_clkids[] = { TEGRA210_CLK_XUSB_HOST,
+ TEGRA210_CLK_XUSB_DEV };
+static const unsigned int xusbb_slcg_clkids[] = { TEGRA210_CLK_XUSB_HOST,
+ TEGRA210_CLK_XUSB_SS };
+static const unsigned int xusbc_slcg_clkids[] = { TEGRA210_CLK_XUSB_DEV,
+ TEGRA210_CLK_XUSB_SS };
+static const unsigned int venc_slcg_clkids[] = { TEGRA210_CLK_HOST1X,
+ TEGRA210_CLK_PLL_D };
+static const unsigned int ape_slcg_clkids[] = { TEGRA210_CLK_ACLK,
+ TEGRA210_CLK_I2S0, TEGRA210_CLK_I2S1, TEGRA210_CLK_I2S2,
+ TEGRA210_CLK_I2S3, TEGRA210_CLK_I2S4, TEGRA210_CLK_SPDIF_OUT,
+ TEGRA210_CLK_D_AUDIO };
+static const unsigned int vic_slcg_clkids[] = { TEGRA210_CLK_HOST1X };
+
+static struct tegra210_domain_mbist_war tegra210_pg_mbist_war[] = {
+ [TEGRA_POWERGATE_VENC] = {
+ .handle_lvl2_ovr = tegra210_venc_mbist_war,
+ .num_clks = ARRAY_SIZE(venc_slcg_clkids),
+ .clk_init_data = venc_slcg_clkids,
+ },
+ [TEGRA_POWERGATE_SATA] = {
+ .handle_lvl2_ovr = tegra210_generic_mbist_war,
+ .lvl2_offset = LVL2_CLK_GATE_OVRC,
+ .lvl2_mask = BIT(0) | BIT(17) | BIT(19),
+ },
+ [TEGRA_POWERGATE_MPE] = {
+ .handle_lvl2_ovr = tegra210_generic_mbist_war,
+ .lvl2_offset = LVL2_CLK_GATE_OVRE,
+ .lvl2_mask = BIT(2),
+ },
+ [TEGRA_POWERGATE_SOR] = {
+ .handle_lvl2_ovr = tegra210_generic_mbist_war,
+ .num_clks = ARRAY_SIZE(sor_slcg_clkids),
+ .clk_init_data = sor_slcg_clkids,
+ .lvl2_offset = LVL2_CLK_GATE_OVRA,
+ .lvl2_mask = BIT(1) | BIT(2),
+ },
+ [TEGRA_POWERGATE_DIS] = {
+ .handle_lvl2_ovr = tegra210_disp_mbist_war,
+ .num_clks = ARRAY_SIZE(disp_slcg_clkids),
+ .clk_init_data = disp_slcg_clkids,
+ },
+ [TEGRA_POWERGATE_DISB] = {
+ .num_clks = ARRAY_SIZE(disp_slcg_clkids),
+ .clk_init_data = disp_slcg_clkids,
+ .handle_lvl2_ovr = tegra210_generic_mbist_war,
+ .lvl2_offset = LVL2_CLK_GATE_OVRA,
+ .lvl2_mask = BIT(2),
+ },
+ [TEGRA_POWERGATE_XUSBA] = {
+ .num_clks = ARRAY_SIZE(xusba_slcg_clkids),
+ .clk_init_data = xusba_slcg_clkids,
+ .handle_lvl2_ovr = tegra210_generic_mbist_war,
+ .lvl2_offset = LVL2_CLK_GATE_OVRC,
+ .lvl2_mask = BIT(30) | BIT(31),
+ },
+ [TEGRA_POWERGATE_XUSBB] = {
+ .num_clks = ARRAY_SIZE(xusbb_slcg_clkids),
+ .clk_init_data = xusbb_slcg_clkids,
+ .handle_lvl2_ovr = tegra210_generic_mbist_war,
+ .lvl2_offset = LVL2_CLK_GATE_OVRC,
+ .lvl2_mask = BIT(30) | BIT(31),
+ },
+ [TEGRA_POWERGATE_XUSBC] = {
+ .num_clks = ARRAY_SIZE(xusbc_slcg_clkids),
+ .clk_init_data = xusbc_slcg_clkids,
+ .handle_lvl2_ovr = tegra210_generic_mbist_war,
+ .lvl2_offset = LVL2_CLK_GATE_OVRC,
+ .lvl2_mask = BIT(30) | BIT(31),
+ },
+ [TEGRA_POWERGATE_VIC] = {
+ .num_clks = ARRAY_SIZE(vic_slcg_clkids),
+ .clk_init_data = vic_slcg_clkids,
+ .handle_lvl2_ovr = tegra210_vic_mbist_war,
+ },
+ [TEGRA_POWERGATE_NVDEC] = {
+ .num_clks = ARRAY_SIZE(nvdec_slcg_clkids),
+ .clk_init_data = nvdec_slcg_clkids,
+ .handle_lvl2_ovr = tegra210_generic_mbist_war,
+ .lvl2_offset = LVL2_CLK_GATE_OVRC,
+ .lvl2_mask = BIT(9) | BIT(31),
+ },
+ [TEGRA_POWERGATE_NVJPG] = {
+ .num_clks = ARRAY_SIZE(nvjpg_slcg_clkids),
+ .clk_init_data = nvjpg_slcg_clkids,
+ .handle_lvl2_ovr = tegra210_generic_mbist_war,
+ .lvl2_offset = LVL2_CLK_GATE_OVRC,
+ .lvl2_mask = BIT(9) | BIT(31),
+ },
+ [TEGRA_POWERGATE_AUD] = {
+ .num_clks = ARRAY_SIZE(ape_slcg_clkids),
+ .clk_init_data = ape_slcg_clkids,
+ .handle_lvl2_ovr = tegra210_ape_mbist_war,
+ },
+ [TEGRA_POWERGATE_VE2] = {
+ .handle_lvl2_ovr = tegra210_generic_mbist_war,
+ .lvl2_offset = LVL2_CLK_GATE_OVRD,
+ .lvl2_mask = BIT(22),
+ },
+};
+
+int tegra210_clk_handle_mbist_war(unsigned int id)
+{
+ int err;
+ struct tegra210_domain_mbist_war *mbist_war;
+
+ if (id >= ARRAY_SIZE(tegra210_pg_mbist_war)) {
+ WARN(1, "unknown domain id in MBIST WAR handler\n");
+ return -EINVAL;
+ }
+
+ mbist_war = &tegra210_pg_mbist_war[id];
+ if (!mbist_war->handle_lvl2_ovr)
+ return 0;
+
+ if (mbist_war->num_clks && !mbist_war->clks)
+ return -ENODEV;
+
+ err = clk_bulk_prepare_enable(mbist_war->num_clks, mbist_war->clks);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&lvl2_ovr_lock);
+
+ mbist_war->handle_lvl2_ovr(mbist_war);
+
+ mutex_unlock(&lvl2_ovr_lock);
+
+ clk_bulk_disable_unprepare(mbist_war->num_clks, mbist_war->clks);
+
+ return 0;
+}
+
void tegra210_put_utmipll_in_iddq(void)
{
u32 reg;
@@ -2654,6 +2944,13 @@ static struct tegra_periph_init_data tegra210_periph[] = {
sor1_parents_idx, 0, &sor1_lock),
};
+static const char * const la_parents[] = {
+ "pll_p", "pll_c2", "pll_c", "pll_c3", "pll_re_out1", "pll_a1", "clk_m", "pll_c4_out0"
+};
+
+static struct tegra_clk_periph tegra210_la =
+ TEGRA_CLK_PERIPH(29, 7, 9, 0, 8, 1, TEGRA_DIVIDER_ROUND_UP, 76, 0, NULL, 0);
+
static __init void tegra210_periph_clk_init(void __iomem *clk_base,
void __iomem *pmc_base)
{
@@ -2700,6 +2997,12 @@ static __init void tegra210_periph_clk_init(void __iomem *clk_base,
periph_clk_enb_refcnt);
clks[TEGRA210_CLK_DSIB] = clk;
+ /* la */
+ clk = tegra_clk_register_periph("la", la_parents,
+ ARRAY_SIZE(la_parents), &tegra210_la, clk_base,
+ CLK_SOURCE_LA, 0);
+ clks[TEGRA210_CLK_LA] = clk;
+
/* emc mux */
clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm,
ARRAY_SIZE(mux_pllmcp_clkm), 0,
@@ -3025,7 +3328,7 @@ static struct tegra_clk_init_table init_table[] __initdata = {
{ TEGRA210_CLK_I2S4, TEGRA210_CLK_PLL_A_OUT0, 11289600, 0 },
{ TEGRA210_CLK_HOST1X, TEGRA210_CLK_PLL_P, 136000000, 1 },
{ TEGRA210_CLK_SCLK_MUX, TEGRA210_CLK_PLL_P, 0, 1 },
- { TEGRA210_CLK_SCLK, TEGRA210_CLK_CLK_MAX, 102000000, 1 },
+ { TEGRA210_CLK_SCLK, TEGRA210_CLK_CLK_MAX, 102000000, 0 },
{ TEGRA210_CLK_DFLL_SOC, TEGRA210_CLK_PLL_P, 51000000, 1 },
{ TEGRA210_CLK_DFLL_REF, TEGRA210_CLK_PLL_P, 51000000, 1 },
{ TEGRA210_CLK_SBC4, TEGRA210_CLK_PLL_P, 12000000, 1 },
@@ -3040,7 +3343,6 @@ static struct tegra_clk_init_table init_table[] __initdata = {
{ TEGRA210_CLK_XUSB_DEV_SRC, TEGRA210_CLK_PLL_P_OUT_XUSB, 102000000, 0 },
{ TEGRA210_CLK_SATA, TEGRA210_CLK_PLL_P, 104000000, 0 },
{ TEGRA210_CLK_SATA_OOB, TEGRA210_CLK_PLL_P, 204000000, 0 },
- { TEGRA210_CLK_EMC, TEGRA210_CLK_CLK_MAX, 0, 1 },
{ TEGRA210_CLK_MSELECT, TEGRA210_CLK_CLK_MAX, 0, 1 },
{ TEGRA210_CLK_CSITE, TEGRA210_CLK_CLK_MAX, 0, 1 },
/* TODO find a way to enable this on-demand */
@@ -3149,6 +3451,37 @@ static int tegra210_reset_deassert(unsigned long id)
return 0;
}
+static void tegra210_mbist_clk_init(void)
+{
+ unsigned int i, j;
+
+ for (i = 0; i < ARRAY_SIZE(tegra210_pg_mbist_war); i++) {
+ unsigned int num_clks = tegra210_pg_mbist_war[i].num_clks;
+ struct clk_bulk_data *clk_data;
+
+ if (!num_clks)
+ continue;
+
+ clk_data = kmalloc_array(num_clks, sizeof(*clk_data),
+ GFP_KERNEL);
+ if (WARN_ON(!clk_data))
+ return;
+
+ tegra210_pg_mbist_war[i].clks = clk_data;
+ for (j = 0; j < num_clks; j++) {
+ int clk_id = tegra210_pg_mbist_war[i].clk_init_data[j];
+ struct clk *clk = clks[clk_id];
+
+ if (WARN(IS_ERR(clk), "clk_id: %d\n", clk_id)) {
+ kfree(clk_data);
+ tegra210_pg_mbist_war[i].clks = NULL;
+ break;
+ }
+ clk_data[j].clk = clk;
+ }
+ }
+}
+
/**
* tegra210_clock_init - Tegra210-specific clock initialization
* @np: struct device_node * of the DT node for the SoC CAR IP block
@@ -3183,6 +3516,24 @@ static void __init tegra210_clock_init(struct device_node *np)
return;
}
+ ahub_base = ioremap(TEGRA210_AHUB_BASE, SZ_64K);
+ if (!ahub_base) {
+ pr_err("ioremap tegra210 APE failed\n");
+ return;
+ }
+
+ dispa_base = ioremap(TEGRA210_DISPA_BASE, SZ_256K);
+ if (!dispa_base) {
+ pr_err("ioremap tegra210 DISPA failed\n");
+ return;
+ }
+
+ vic_base = ioremap(TEGRA210_VIC_BASE, SZ_256K);
+ if (!vic_base) {
+ pr_err("ioremap tegra210 VIC failed\n");
+ return;
+ }
+
clks = tegra_clk_init(clk_base, TEGRA210_CLK_CLK_MAX,
TEGRA210_CAR_BANK_COUNT);
if (!clks)
@@ -3219,6 +3570,8 @@ static void __init tegra210_clock_init(struct device_node *np)
tegra_add_of_provider(np);
tegra_register_devclks(devclks, ARRAY_SIZE(devclks));
+ tegra210_mbist_clk_init();
+
tegra_cpu_car_ops = &tegra210_cpu_car_ops;
}
CLK_OF_DECLARE(tegra210, "nvidia,tegra210-car", tegra210_clock_init);
diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c
index bee84c554932..b316dfb6f6c7 100644
--- a/drivers/clk/tegra/clk-tegra30.c
+++ b/drivers/clk/tegra/clk-tegra30.c
@@ -819,6 +819,7 @@ static struct tegra_clk tegra30_clks[tegra_clk_max] __initdata = {
[tegra_clk_pll_a] = { .dt_id = TEGRA30_CLK_PLL_A, .present = true },
[tegra_clk_pll_a_out0] = { .dt_id = TEGRA30_CLK_PLL_A_OUT0, .present = true },
[tegra_clk_cec] = { .dt_id = TEGRA30_CLK_CEC, .present = true },
+ [tegra_clk_emc] = { .dt_id = TEGRA30_CLK_EMC, .present = true },
};
static const char *pll_e_parents[] = { "pll_ref", "pll_p" };
@@ -843,8 +844,7 @@ static void __init tegra30_pll_init(void)
/* PLLM */
clk = tegra_clk_register_pll("pll_m", "pll_ref", clk_base, pmc_base,
- CLK_IGNORE_UNUSED | CLK_SET_RATE_GATE,
- &pll_m_params, NULL);
+ CLK_SET_RATE_GATE, &pll_m_params, NULL);
clks[TEGRA30_CLK_PLL_M] = clk;
/* PLLM_OUT1 */
@@ -852,7 +852,7 @@ static void __init tegra30_pll_init(void)
clk_base + PLLM_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
8, 8, 1, NULL);
clk = tegra_clk_register_pll_out("pll_m_out1", "pll_m_out1_div",
- clk_base + PLLM_OUT, 1, 0, CLK_IGNORE_UNUSED |
+ clk_base + PLLM_OUT, 1, 0,
CLK_SET_RATE_PARENT, 0, NULL);
clks[TEGRA30_CLK_PLL_M_OUT1] = clk;
@@ -990,7 +990,7 @@ static void __init tegra30_super_clk_init(void)
/* SCLK */
clk = tegra_clk_register_super_mux("sclk", sclk_parents,
ARRAY_SIZE(sclk_parents),
- CLK_SET_RATE_PARENT,
+ CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
clk_base + SCLK_BURST_POLICY,
0, 4, 0, 0, NULL);
clks[TEGRA30_CLK_SCLK] = clk;
@@ -1060,9 +1060,6 @@ static void __init tegra30_periph_clk_init(void)
CLK_SET_RATE_NO_REPARENT,
clk_base + CLK_SOURCE_EMC,
30, 2, 0, &emc_lock);
- clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base, 0,
- 57, periph_clk_enb_refcnt);
- clks[TEGRA30_CLK_EMC] = clk;
clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC,
&emc_lock);
@@ -1252,10 +1249,7 @@ static struct tegra_clk_init_table init_table[] __initdata = {
{ TEGRA30_CLK_SDMMC1, TEGRA30_CLK_PLL_P, 48000000, 0 },
{ TEGRA30_CLK_SDMMC2, TEGRA30_CLK_PLL_P, 48000000, 0 },
{ TEGRA30_CLK_SDMMC3, TEGRA30_CLK_PLL_P, 48000000, 0 },
- { TEGRA30_CLK_PLL_M, TEGRA30_CLK_CLK_MAX, 0, 1 },
- { TEGRA30_CLK_PCLK, TEGRA30_CLK_CLK_MAX, 0, 1 },
{ TEGRA30_CLK_CSITE, TEGRA30_CLK_CLK_MAX, 0, 1 },
- { TEGRA30_CLK_EMC, TEGRA30_CLK_CLK_MAX, 0, 1 },
{ TEGRA30_CLK_MSELECT, TEGRA30_CLK_CLK_MAX, 0, 1 },
{ TEGRA30_CLK_SBC1, TEGRA30_CLK_PLL_P, 100000000, 0 },
{ TEGRA30_CLK_SBC2, TEGRA30_CLK_PLL_P, 100000000, 0 },
@@ -1272,6 +1266,7 @@ static struct tegra_clk_init_table init_table[] __initdata = {
{ TEGRA30_CLK_GR3D, TEGRA30_CLK_PLL_C, 300000000, 0 },
{ TEGRA30_CLK_GR3D2, TEGRA30_CLK_PLL_C, 300000000, 0 },
{ TEGRA30_CLK_PLL_U, TEGRA30_CLK_CLK_MAX, 480000000, 0 },
+ { TEGRA30_CLK_VDE, TEGRA30_CLK_CLK_MAX, 600000000, 0 },
/* must be the last entry */
{ TEGRA30_CLK_CLK_MAX, TEGRA30_CLK_CLK_MAX, 0, 0 },
};
diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h
index 3b2763df51c2..ba7e20e6a82b 100644
--- a/drivers/clk/tegra/clk.h
+++ b/drivers/clk/tegra/clk.h
@@ -812,4 +812,11 @@ int tegra_pll_wait_for_lock(struct tegra_clk_pll *pll);
u16 tegra_pll_get_fixed_mdiv(struct clk_hw *hw, unsigned long input_rate);
int tegra_pll_p_div_to_hw(struct tegra_clk_pll *pll, u8 p_div);
+/* Combined read fence with delay */
+#define fence_udelay(delay, reg) \
+ do { \
+ readl(reg); \
+ udelay(delay); \
+ } while (0)
+
#endif /* TEGRA_CLK_H */
diff --git a/drivers/clk/ti/clk.c b/drivers/clk/ti/clk.c
index f4d6802a8544..7d22e1af2247 100644
--- a/drivers/clk/ti/clk.c
+++ b/drivers/clk/ti/clk.c
@@ -55,6 +55,29 @@ static void clk_memmap_writel(u32 val, const struct clk_omap_reg *reg)
writel_relaxed(val, io->mem + reg->offset);
}
+static void _clk_rmw(u32 val, u32 mask, void __iomem *ptr)
+{
+ u32 v;
+
+ v = readl_relaxed(ptr);
+ v &= ~mask;
+ v |= val;
+ writel_relaxed(v, ptr);
+}
+
+static void clk_memmap_rmw(u32 val, u32 mask, const struct clk_omap_reg *reg)
+{
+ struct clk_iomap *io = clk_memmaps[reg->index];
+
+ if (reg->ptr) {
+ _clk_rmw(val, mask, reg->ptr);
+ } else if (io->regmap) {
+ regmap_update_bits(io->regmap, reg->offset, mask, val);
+ } else {
+ _clk_rmw(val, mask, io->mem + reg->offset);
+ }
+}
+
static u32 clk_memmap_readl(const struct clk_omap_reg *reg)
{
u32 val;
@@ -89,6 +112,7 @@ int ti_clk_setup_ll_ops(struct ti_clk_ll_ops *ops)
ti_clk_ll_ops = ops;
ops->clk_readl = clk_memmap_readl;
ops->clk_writel = clk_memmap_writel;
+ ops->clk_rmw = clk_memmap_rmw;
return 0;
}
@@ -251,6 +275,20 @@ int ti_clk_get_reg_addr(struct device_node *node, int index,
return 0;
}
+void ti_clk_latch(struct clk_omap_reg *reg, s8 shift)
+{
+ u32 latch;
+
+ if (shift < 0)
+ return;
+
+ latch = 1 << shift;
+
+ ti_clk_ll_ops->clk_rmw(latch, latch, reg);
+ ti_clk_ll_ops->clk_rmw(0, latch, reg);
+ ti_clk_ll_ops->clk_readl(reg); /* OCP barrier */
+}
+
/**
* omap2_clk_provider_init - init master clock provider
* @parent: master node
diff --git a/drivers/clk/ti/clock.h b/drivers/clk/ti/clock.h
index d9b43bfc2532..b58278077226 100644
--- a/drivers/clk/ti/clock.h
+++ b/drivers/clk/ti/clock.h
@@ -22,6 +22,7 @@ struct clk_omap_divider {
u8 shift;
u8 width;
u8 flags;
+ s8 latch;
const struct clk_div_table *table;
};
@@ -33,6 +34,7 @@ struct clk_omap_mux {
u32 *table;
u32 mask;
u8 shift;
+ s8 latch;
u8 flags;
};
@@ -74,6 +76,11 @@ enum {
#define CLKF_CORE (1 << 9)
#define CLKF_J_TYPE (1 << 10)
+/* CLKCTRL flags */
+#define CLKF_SW_SUP BIT(5)
+#define CLKF_HW_SUP BIT(6)
+#define CLKF_NO_IDLEST BIT(7)
+
#define CLK(dev, con, ck) \
{ \
.lk = { \
@@ -183,10 +190,6 @@ extern const struct omap_clkctrl_data am438x_clkctrl_data[];
extern const struct omap_clkctrl_data dm814_clkctrl_data[];
extern const struct omap_clkctrl_data dm816_clkctrl_data[];
-#define CLKF_SW_SUP BIT(0)
-#define CLKF_HW_SUP BIT(1)
-#define CLKF_NO_IDLEST BIT(2)
-
typedef void (*ti_of_clk_init_cb_t)(void *, struct device_node *);
struct clk *ti_clk_register(struct device *dev, struct clk_hw *hw,
@@ -194,6 +197,8 @@ struct clk *ti_clk_register(struct device *dev, struct clk_hw *hw,
int ti_clk_add_alias(struct device *dev, struct clk *clk, const char *con);
void ti_clk_add_aliases(void);
+void ti_clk_latch(struct clk_omap_reg *reg, s8 shift);
+
struct clk_hw *ti_clk_build_component_mux(struct ti_clk_mux *setup);
int ti_clk_parse_divider_data(int *div_table, int num_dividers, int max_div,
diff --git a/drivers/clk/ti/divider.c b/drivers/clk/ti/divider.c
index 77f93f6d2806..aaa277dd6d99 100644
--- a/drivers/clk/ti/divider.c
+++ b/drivers/clk/ti/divider.c
@@ -263,6 +263,8 @@ static int ti_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
val |= value << divider->shift;
ti_clk_ll_ops->clk_writel(val, &divider->reg);
+ ti_clk_latch(&divider->reg, divider->latch);
+
return 0;
}
@@ -276,7 +278,8 @@ static struct clk *_register_divider(struct device *dev, const char *name,
const char *parent_name,
unsigned long flags,
struct clk_omap_reg *reg,
- u8 shift, u8 width, u8 clk_divider_flags,
+ u8 shift, u8 width, s8 latch,
+ u8 clk_divider_flags,
const struct clk_div_table *table)
{
struct clk_omap_divider *div;
@@ -305,6 +308,7 @@ static struct clk *_register_divider(struct device *dev, const char *name,
memcpy(&div->reg, reg, sizeof(*reg));
div->shift = shift;
div->width = width;
+ div->latch = latch;
div->flags = clk_divider_flags;
div->hw.init = &init;
div->table = table;
@@ -420,6 +424,7 @@ struct clk_hw *ti_clk_build_component_div(struct ti_clk_divider *setup)
div->table = _get_div_table_from_setup(setup, &div->width);
div->shift = setup->bit_shift;
+ div->latch = -EINVAL;
return &div->hw;
}
@@ -452,7 +457,7 @@ struct clk *ti_clk_register_divider(struct ti_clk *setup)
clk = _register_divider(NULL, setup->name, div->parent,
flags, &reg, div->bit_shift,
- width, div_flags, table);
+ width, -EINVAL, div_flags, table);
if (IS_ERR(clk))
kfree(table);
@@ -556,7 +561,7 @@ static int _get_divider_width(struct device_node *node,
static int __init ti_clk_divider_populate(struct device_node *node,
struct clk_omap_reg *reg, const struct clk_div_table **table,
- u32 *flags, u8 *div_flags, u8 *width, u8 *shift)
+ u32 *flags, u8 *div_flags, u8 *width, u8 *shift, s8 *latch)
{
u32 val;
int ret;
@@ -570,6 +575,13 @@ static int __init ti_clk_divider_populate(struct device_node *node,
else
*shift = 0;
+ if (latch) {
+ if (!of_property_read_u32(node, "ti,latch-bit", &val))
+ *latch = val;
+ else
+ *latch = -EINVAL;
+ }
+
*flags = 0;
*div_flags = 0;
@@ -606,17 +618,18 @@ static void __init of_ti_divider_clk_setup(struct device_node *node)
u8 clk_divider_flags = 0;
u8 width = 0;
u8 shift = 0;
+ s8 latch = -EINVAL;
const struct clk_div_table *table = NULL;
u32 flags = 0;
parent_name = of_clk_get_parent_name(node, 0);
if (ti_clk_divider_populate(node, &reg, &table, &flags,
- &clk_divider_flags, &width, &shift))
+ &clk_divider_flags, &width, &shift, &latch))
goto cleanup;
clk = _register_divider(NULL, node->name, parent_name, flags, &reg,
- shift, width, clk_divider_flags, table);
+ shift, width, latch, clk_divider_flags, table);
if (!IS_ERR(clk)) {
of_clk_add_provider(node, of_clk_src_simple_get, clk);
@@ -639,7 +652,8 @@ static void __init of_ti_composite_divider_clk_setup(struct device_node *node)
return;
if (ti_clk_divider_populate(node, &div->reg, &div->table, &val,
- &div->flags, &div->width, &div->shift) < 0)
+ &div->flags, &div->width, &div->shift,
+ NULL) < 0)
goto cleanup;
if (!ti_clk_add_component(node, &div->hw, CLK_COMPONENT_TYPE_DIVIDER))
diff --git a/drivers/clk/ti/mux.c b/drivers/clk/ti/mux.c
index d4705803f3d3..69a4308a5a98 100644
--- a/drivers/clk/ti/mux.c
+++ b/drivers/clk/ti/mux.c
@@ -86,6 +86,7 @@ static int ti_clk_mux_set_parent(struct clk_hw *hw, u8 index)
}
val |= index << mux->shift;
ti_clk_ll_ops->clk_writel(val, &mux->reg);
+ ti_clk_latch(&mux->reg, mux->latch);
return 0;
}
@@ -100,7 +101,7 @@ static struct clk *_register_mux(struct device *dev, const char *name,
const char * const *parent_names,
u8 num_parents, unsigned long flags,
struct clk_omap_reg *reg, u8 shift, u32 mask,
- u8 clk_mux_flags, u32 *table)
+ s8 latch, u8 clk_mux_flags, u32 *table)
{
struct clk_omap_mux *mux;
struct clk *clk;
@@ -121,6 +122,7 @@ static struct clk *_register_mux(struct device *dev, const char *name,
memcpy(&mux->reg, reg, sizeof(*reg));
mux->shift = shift;
mux->mask = mask;
+ mux->latch = latch;
mux->flags = clk_mux_flags;
mux->table = table;
mux->hw.init = &init;
@@ -160,7 +162,7 @@ struct clk *ti_clk_register_mux(struct ti_clk *setup)
flags |= CLK_SET_RATE_PARENT;
return _register_mux(NULL, setup->name, mux->parents, mux->num_parents,
- flags, &reg, mux->bit_shift, mask,
+ flags, &reg, mux->bit_shift, mask, -EINVAL,
mux_flags, NULL);
}
@@ -179,6 +181,7 @@ static void of_mux_clk_setup(struct device_node *node)
u8 clk_mux_flags = 0;
u32 mask = 0;
u32 shift = 0;
+ s32 latch = -EINVAL;
u32 flags = CLK_SET_RATE_NO_REPARENT;
num_parents = of_clk_get_parent_count(node);
@@ -197,6 +200,8 @@ static void of_mux_clk_setup(struct device_node *node)
of_property_read_u32(node, "ti,bit-shift", &shift);
+ of_property_read_u32(node, "ti,latch-bit", &latch);
+
if (of_property_read_bool(node, "ti,index-starts-at-one"))
clk_mux_flags |= CLK_MUX_INDEX_ONE;
@@ -211,7 +216,8 @@ static void of_mux_clk_setup(struct device_node *node)
mask = (1 << fls(mask)) - 1;
clk = _register_mux(NULL, node->name, parent_names, num_parents,
- flags, &reg, shift, mask, clk_mux_flags, NULL);
+ flags, &reg, shift, mask, latch, clk_mux_flags,
+ NULL);
if (!IS_ERR(clk))
of_clk_add_provider(node, of_clk_src_simple_get, clk);
@@ -234,6 +240,7 @@ struct clk_hw *ti_clk_build_component_mux(struct ti_clk_mux *setup)
return ERR_PTR(-ENOMEM);
mux->shift = setup->bit_shift;
+ mux->latch = -EINVAL;
mux->reg.index = setup->module;
mux->reg.offset = setup->reg;
diff --git a/drivers/clk/uniphier/clk-uniphier-sys.c b/drivers/clk/uniphier/clk-uniphier-sys.c
index d244e724e198..ebc78ab2df05 100644
--- a/drivers/clk/uniphier/clk-uniphier-sys.c
+++ b/drivers/clk/uniphier/clk-uniphier-sys.c
@@ -57,6 +57,14 @@
#define UNIPHIER_PRO4_SYS_CLK_USB3(idx, ch) \
UNIPHIER_CLK_GATE("usb3" #ch, (idx), NULL, 0x2104, 16 + (ch))
+#define UNIPHIER_PRO4_SYS_CLK_AIO(idx) \
+ UNIPHIER_CLK_FACTOR("aio-io200m", -1, "spll", 1, 8), \
+ UNIPHIER_CLK_GATE("aio", (idx), "aio-io200m", 0x2104, 13)
+
+#define UNIPHIER_PRO5_SYS_CLK_AIO(idx) \
+ UNIPHIER_CLK_FACTOR("aio-io200m", -1, "spll", 1, 12), \
+ UNIPHIER_CLK_GATE("aio", (idx), "aio-io200m", 0x2104, 13)
+
#define UNIPHIER_LD11_SYS_CLK_AIO(idx) \
UNIPHIER_CLK_FACTOR("aio-io200m", -1, "spll", 1, 10), \
UNIPHIER_CLK_GATE("aio", (idx), "aio-io200m", 0x2108, 0)
@@ -94,16 +102,22 @@ const struct uniphier_clk_data uniphier_pro4_sys_clk_data[] = {
UNIPHIER_CLK_FACTOR("upll", -1, "ref", 288, 25), /* 288 MHz */
UNIPHIER_CLK_FACTOR("a2pll", -1, "upll", 256, 125), /* 589.824 MHz */
UNIPHIER_CLK_FACTOR("vpll27a", -1, "ref", 270, 25), /* 270 MHz */
+ UNIPHIER_CLK_FACTOR("gpll", -1, "ref", 10, 1), /* 250 MHz */
UNIPHIER_CLK_FACTOR("uart", 0, "a2pll", 1, 8),
UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 32),
UNIPHIER_LD4_SYS_CLK_NAND(2),
UNIPHIER_LD4_SYS_CLK_SD,
UNIPHIER_CLK_FACTOR("usb2", -1, "upll", 1, 12),
UNIPHIER_PRO4_SYS_CLK_ETHER(6),
+ UNIPHIER_CLK_GATE("ether-gb", 7, "gpll", 0x2104, 5),
UNIPHIER_LD4_SYS_CLK_STDMAC(8), /* HSC, MIO, RLE */
+ UNIPHIER_CLK_GATE("ether-phy", 10, "ref", 0x2260, 0),
UNIPHIER_PRO4_SYS_CLK_GIO(12), /* Ether, SATA, USB3 */
UNIPHIER_PRO4_SYS_CLK_USB3(14, 0),
UNIPHIER_PRO4_SYS_CLK_USB3(15, 1),
+ UNIPHIER_CLK_GATE("sata0", 28, NULL, 0x2104, 18),
+ UNIPHIER_CLK_GATE("sata1", 29, NULL, 0x2104, 19),
+ UNIPHIER_PRO4_SYS_CLK_AIO(40),
{ /* sentinel */ }
};
@@ -132,6 +146,8 @@ const struct uniphier_clk_data uniphier_pro5_sys_clk_data[] = {
UNIPHIER_PRO4_SYS_CLK_GIO(12), /* PCIe, USB3 */
UNIPHIER_PRO4_SYS_CLK_USB3(14, 0),
UNIPHIER_PRO4_SYS_CLK_USB3(15, 1),
+ UNIPHIER_CLK_GATE("pcie", 24, NULL, 0x2108, 2),
+ UNIPHIER_PRO5_SYS_CLK_AIO(40),
{ /* sentinel */ }
};
@@ -149,6 +165,8 @@ const struct uniphier_clk_data uniphier_pxs2_sys_clk_data[] = {
/* The document mentions 0x2104 bit 18, but not functional */
UNIPHIER_CLK_GATE("usb30-phy", 16, NULL, 0x2104, 19),
UNIPHIER_CLK_GATE("usb31-phy", 20, NULL, 0x2104, 20),
+ UNIPHIER_CLK_GATE("sata0", 28, NULL, 0x2104, 22),
+ UNIPHIER_PRO5_SYS_CLK_AIO(40),
{ /* sentinel */ }
};
@@ -205,6 +223,7 @@ const struct uniphier_clk_data uniphier_ld20_sys_clk_data[] = {
UNIPHIER_CLK_GATE("usb30", 14, NULL, 0x210c, 14),
UNIPHIER_CLK_GATE("usb30-phy0", 16, NULL, 0x210c, 12),
UNIPHIER_CLK_GATE("usb30-phy1", 17, NULL, 0x210c, 13),
+ UNIPHIER_CLK_GATE("pcie", 24, NULL, 0x210c, 4),
UNIPHIER_LD11_SYS_CLK_AIO(40),
UNIPHIER_LD11_SYS_CLK_EVEA(41),
UNIPHIER_LD11_SYS_CLK_EXIV(42),
@@ -233,6 +252,8 @@ const struct uniphier_clk_data uniphier_pxs3_sys_clk_data[] = {
UNIPHIER_LD20_SYS_CLK_SD,
UNIPHIER_LD11_SYS_CLK_NAND(2),
UNIPHIER_LD11_SYS_CLK_EMMC(4),
+ UNIPHIER_CLK_GATE("ether0", 6, NULL, 0x210c, 9),
+ UNIPHIER_CLK_GATE("ether1", 7, NULL, 0x210c, 10),
UNIPHIER_CLK_GATE("usb30", 12, NULL, 0x210c, 4), /* =GIO0 */
UNIPHIER_CLK_GATE("usb31-0", 13, NULL, 0x210c, 5), /* =GIO1 */
UNIPHIER_CLK_GATE("usb31-1", 14, NULL, 0x210c, 6), /* =GIO1-1 */
@@ -241,6 +262,10 @@ const struct uniphier_clk_data uniphier_pxs3_sys_clk_data[] = {
UNIPHIER_CLK_GATE("usb30-phy2", 18, NULL, 0x210c, 20),
UNIPHIER_CLK_GATE("usb31-phy0", 20, NULL, 0x210c, 17),
UNIPHIER_CLK_GATE("usb31-phy1", 21, NULL, 0x210c, 19),
+ UNIPHIER_CLK_GATE("pcie", 24, NULL, 0x210c, 3),
+ UNIPHIER_CLK_GATE("sata0", 28, NULL, 0x210c, 7),
+ UNIPHIER_CLK_GATE("sata1", 29, NULL, 0x210c, 8),
+ UNIPHIER_CLK_GATE("sata-phy", 30, NULL, 0x210c, 21),
/* CPU gears */
UNIPHIER_CLK_DIV4("cpll", 2, 3, 4, 8),
UNIPHIER_CLK_DIV4("spll", 2, 3, 4, 8),
diff --git a/drivers/clk/ux500/Makefile b/drivers/clk/ux500/Makefile
index fedc083dc8be..53fd29002401 100644
--- a/drivers/clk/ux500/Makefile
+++ b/drivers/clk/ux500/Makefile
@@ -10,8 +10,6 @@ obj-y += clk-sysctrl.o
# Clock definitions
obj-y += u8500_of_clk.o
-obj-y += u9540_clk.o
-obj-y += u8540_clk.o
# ABX500 clock driver
obj-y += abx500-clk.o
diff --git a/drivers/clk/ux500/abx500-clk.c b/drivers/clk/ux500/abx500-clk.c
index 2257d12ba988..5a86cd8fe5de 100644
--- a/drivers/clk/ux500/abx500-clk.c
+++ b/drivers/clk/ux500/abx500-clk.c
@@ -88,18 +88,6 @@ static int ab8500_reg_clks(struct device *dev)
return 0;
}
-/* Clock definitions for ab8540 */
-static int ab8540_reg_clks(struct device *dev)
-{
- return 0;
-}
-
-/* Clock definitions for ab9540 */
-static int ab9540_reg_clks(struct device *dev)
-{
- return 0;
-}
-
static int abx500_clk_probe(struct platform_device *pdev)
{
struct ab8500 *parent = dev_get_drvdata(pdev->dev.parent);
@@ -107,10 +95,6 @@ static int abx500_clk_probe(struct platform_device *pdev)
if (is_ab8500(parent) || is_ab8505(parent)) {
ret = ab8500_reg_clks(&pdev->dev);
- } else if (is_ab8540(parent)) {
- ret = ab8540_reg_clks(&pdev->dev);
- } else if (is_ab9540(parent)) {
- ret = ab9540_reg_clks(&pdev->dev);
} else {
dev_err(&pdev->dev, "non supported plf id\n");
return -ENODEV;
diff --git a/drivers/clk/ux500/u8540_clk.c b/drivers/clk/ux500/u8540_clk.c
deleted file mode 100644
index 133859f0e2bf..000000000000
--- a/drivers/clk/ux500/u8540_clk.c
+++ /dev/null
@@ -1,597 +0,0 @@
-/*
- * Clock definitions for u8540 platform.
- *
- * Copyright (C) 2012 ST-Ericsson SA
- * Author: Ulf Hansson <ulf.hansson@linaro.org>
- *
- * License terms: GNU General Public License (GPL) version 2
- */
-
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/clkdev.h>
-#include <linux/clk-provider.h>
-#include <linux/mfd/dbx500-prcmu.h>
-#include "clk.h"
-
-/* CLKRST4 is missing making it hard to index things */
-enum clkrst_index {
- CLKRST1_INDEX = 0,
- CLKRST2_INDEX,
- CLKRST3_INDEX,
- CLKRST5_INDEX,
- CLKRST6_INDEX,
- CLKRST_MAX,
-};
-
-static void u8540_clk_init(struct device_node *np)
-{
- struct clk *clk;
- u32 bases[CLKRST_MAX];
- int i;
-
- for (i = 0; i < ARRAY_SIZE(bases); i++) {
- struct resource r;
-
- if (of_address_to_resource(np, i, &r))
- /* Not much choice but to continue */
- pr_err("failed to get CLKRST %d base address\n",
- i + 1);
- bases[i] = r.start;
- }
-
- /* Clock sources. */
- /* Fixed ClockGen */
- clk = clk_reg_prcmu_gate("soc0_pll", NULL, PRCMU_PLLSOC0,
- CLK_IGNORE_UNUSED);
- clk_register_clkdev(clk, "soc0_pll", NULL);
-
- clk = clk_reg_prcmu_gate("soc1_pll", NULL, PRCMU_PLLSOC1,
- CLK_IGNORE_UNUSED);
- clk_register_clkdev(clk, "soc1_pll", NULL);
-
- clk = clk_reg_prcmu_gate("ddr_pll", NULL, PRCMU_PLLDDR,
- CLK_IGNORE_UNUSED);
- clk_register_clkdev(clk, "ddr_pll", NULL);
-
- clk = clk_register_fixed_rate(NULL, "rtc32k", NULL,
- CLK_IGNORE_UNUSED,
- 32768);
- clk_register_clkdev(clk, "clk32k", NULL);
- clk_register_clkdev(clk, "apb_pclk", "rtc-pl031");
-
- clk = clk_register_fixed_rate(NULL, "ulp38m4", NULL,
- CLK_IGNORE_UNUSED,
- 38400000);
-
- clk = clk_reg_prcmu_gate("uartclk", NULL, PRCMU_UARTCLK, 0);
- clk_register_clkdev(clk, NULL, "UART");
-
- /* msp02clk needs a abx500 clk as parent. Handle by abx500 clk driver */
- clk = clk_reg_prcmu_gate("msp02clk", "ab9540_sysclk12_b1",
- PRCMU_MSP02CLK, 0);
- clk_register_clkdev(clk, NULL, "MSP02");
-
- clk = clk_reg_prcmu_gate("msp1clk", NULL, PRCMU_MSP1CLK, 0);
- clk_register_clkdev(clk, NULL, "MSP1");
-
- clk = clk_reg_prcmu_gate("i2cclk", NULL, PRCMU_I2CCLK, 0);
- clk_register_clkdev(clk, NULL, "I2C");
-
- clk = clk_reg_prcmu_gate("slimclk", NULL, PRCMU_SLIMCLK, 0);
- clk_register_clkdev(clk, NULL, "slim");
-
- clk = clk_reg_prcmu_gate("per1clk", NULL, PRCMU_PER1CLK, 0);
- clk_register_clkdev(clk, NULL, "PERIPH1");
-
- clk = clk_reg_prcmu_gate("per2clk", NULL, PRCMU_PER2CLK, 0);
- clk_register_clkdev(clk, NULL, "PERIPH2");
-
- clk = clk_reg_prcmu_gate("per3clk", NULL, PRCMU_PER3CLK, 0);
- clk_register_clkdev(clk, NULL, "PERIPH3");
-
- clk = clk_reg_prcmu_gate("per5clk", NULL, PRCMU_PER5CLK, 0);
- clk_register_clkdev(clk, NULL, "PERIPH5");
-
- clk = clk_reg_prcmu_gate("per6clk", NULL, PRCMU_PER6CLK, 0);
- clk_register_clkdev(clk, NULL, "PERIPH6");
-
- clk = clk_reg_prcmu_gate("per7clk", NULL, PRCMU_PER7CLK, 0);
- clk_register_clkdev(clk, NULL, "PERIPH7");
-
- clk = clk_reg_prcmu_scalable("lcdclk", NULL, PRCMU_LCDCLK, 0,
- CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "lcd");
- clk_register_clkdev(clk, "lcd", "mcde");
-
- clk = clk_reg_prcmu_opp_gate("bmlclk", NULL, PRCMU_BMLCLK, 0);
- clk_register_clkdev(clk, NULL, "bml");
-
- clk = clk_reg_prcmu_scalable("hsitxclk", NULL, PRCMU_HSITXCLK, 0,
- CLK_SET_RATE_GATE);
-
- clk = clk_reg_prcmu_scalable("hsirxclk", NULL, PRCMU_HSIRXCLK, 0,
- CLK_SET_RATE_GATE);
-
- clk = clk_reg_prcmu_scalable("hdmiclk", NULL, PRCMU_HDMICLK, 0,
- CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "hdmi");
- clk_register_clkdev(clk, "hdmi", "mcde");
-
- clk = clk_reg_prcmu_gate("apeatclk", NULL, PRCMU_APEATCLK, 0);
- clk_register_clkdev(clk, NULL, "apeat");
-
- clk = clk_reg_prcmu_gate("apetraceclk", NULL, PRCMU_APETRACECLK, 0);
- clk_register_clkdev(clk, NULL, "apetrace");
-
- clk = clk_reg_prcmu_gate("mcdeclk", NULL, PRCMU_MCDECLK, 0);
- clk_register_clkdev(clk, NULL, "mcde");
- clk_register_clkdev(clk, "mcde", "mcde");
- clk_register_clkdev(clk, NULL, "dsilink.0");
- clk_register_clkdev(clk, NULL, "dsilink.1");
- clk_register_clkdev(clk, NULL, "dsilink.2");
-
- clk = clk_reg_prcmu_opp_gate("ipi2cclk", NULL, PRCMU_IPI2CCLK, 0);
- clk_register_clkdev(clk, NULL, "ipi2");
-
- clk = clk_reg_prcmu_gate("dsialtclk", NULL, PRCMU_DSIALTCLK, 0);
- clk_register_clkdev(clk, NULL, "dsialt");
-
- clk = clk_reg_prcmu_gate("dmaclk", NULL, PRCMU_DMACLK, 0);
- clk_register_clkdev(clk, NULL, "dma40.0");
-
- clk = clk_reg_prcmu_gate("b2r2clk", NULL, PRCMU_B2R2CLK, 0);
- clk_register_clkdev(clk, NULL, "b2r2");
- clk_register_clkdev(clk, NULL, "b2r2_core");
- clk_register_clkdev(clk, NULL, "U8500-B2R2.0");
- clk_register_clkdev(clk, NULL, "b2r2_1_core");
-
- clk = clk_reg_prcmu_scalable("tvclk", NULL, PRCMU_TVCLK, 0,
- CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "tv");
- clk_register_clkdev(clk, "tv", "mcde");
-
- clk = clk_reg_prcmu_gate("sspclk", NULL, PRCMU_SSPCLK, 0);
- clk_register_clkdev(clk, NULL, "SSP");
-
- clk = clk_reg_prcmu_gate("rngclk", NULL, PRCMU_RNGCLK, 0);
- clk_register_clkdev(clk, NULL, "rngclk");
-
- clk = clk_reg_prcmu_gate("uiccclk", NULL, PRCMU_UICCCLK, 0);
- clk_register_clkdev(clk, NULL, "uicc");
-
- clk = clk_reg_prcmu_gate("timclk", NULL, PRCMU_TIMCLK, 0);
- clk_register_clkdev(clk, NULL, "mtu0");
- clk_register_clkdev(clk, NULL, "mtu1");
-
- clk = clk_reg_prcmu_opp_volt_scalable("sdmmcclk", NULL,
- PRCMU_SDMMCCLK, 100000000,
- CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "sdmmc");
-
- clk = clk_reg_prcmu_opp_volt_scalable("sdmmchclk", NULL,
- PRCMU_SDMMCHCLK, 400000000,
- CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "sdmmchclk");
-
- clk = clk_reg_prcmu_gate("hvaclk", NULL, PRCMU_HVACLK, 0);
- clk_register_clkdev(clk, NULL, "hva");
-
- clk = clk_reg_prcmu_gate("g1clk", NULL, PRCMU_G1CLK, 0);
- clk_register_clkdev(clk, NULL, "g1");
-
- clk = clk_reg_prcmu_scalable("spare1clk", NULL, PRCMU_SPARE1CLK, 0,
- CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, "dsilcd", "mcde");
-
- clk = clk_reg_prcmu_scalable("dsi_pll", "hdmiclk",
- PRCMU_PLLDSI, 0, CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, "dsihs2", "mcde");
- clk_register_clkdev(clk, "hs_clk", "dsilink.2");
-
- clk = clk_reg_prcmu_scalable("dsilcd_pll", "spare1clk",
- PRCMU_PLLDSI_LCD, 0, CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, "dsilcd_pll", "mcde");
-
- clk = clk_reg_prcmu_scalable("dsi0clk", "dsi_pll",
- PRCMU_DSI0CLK, 0, CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, "dsihs0", "mcde");
-
- clk = clk_reg_prcmu_scalable("dsi0lcdclk", "dsilcd_pll",
- PRCMU_DSI0CLK_LCD, 0, CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, "dsihs0", "mcde");
- clk_register_clkdev(clk, "hs_clk", "dsilink.0");
-
- clk = clk_reg_prcmu_scalable("dsi1clk", "dsi_pll",
- PRCMU_DSI1CLK, 0, CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, "dsihs1", "mcde");
-
- clk = clk_reg_prcmu_scalable("dsi1lcdclk", "dsilcd_pll",
- PRCMU_DSI1CLK_LCD, 0, CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, "dsihs1", "mcde");
- clk_register_clkdev(clk, "hs_clk", "dsilink.1");
-
- clk = clk_reg_prcmu_scalable("dsi0escclk", "tvclk",
- PRCMU_DSI0ESCCLK, 0, CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, "lp_clk", "dsilink.0");
- clk_register_clkdev(clk, "dsilp0", "mcde");
-
- clk = clk_reg_prcmu_scalable("dsi1escclk", "tvclk",
- PRCMU_DSI1ESCCLK, 0, CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, "lp_clk", "dsilink.1");
- clk_register_clkdev(clk, "dsilp1", "mcde");
-
- clk = clk_reg_prcmu_scalable("dsi2escclk", "tvclk",
- PRCMU_DSI2ESCCLK, 0, CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, "lp_clk", "dsilink.2");
- clk_register_clkdev(clk, "dsilp2", "mcde");
-
- clk = clk_reg_prcmu_scalable_rate("armss", NULL,
- PRCMU_ARMSS, 0, CLK_IGNORE_UNUSED);
- clk_register_clkdev(clk, "armss", NULL);
-
- clk = clk_register_fixed_factor(NULL, "smp_twd", "armss",
- CLK_IGNORE_UNUSED, 1, 2);
- clk_register_clkdev(clk, NULL, "smp_twd");
-
- /* PRCC P-clocks */
- /* Peripheral 1 : PRCC P-clocks */
- clk = clk_reg_prcc_pclk("p1_pclk0", "per1clk", bases[CLKRST1_INDEX],
- BIT(0), 0);
- clk_register_clkdev(clk, "apb_pclk", "uart0");
-
- clk = clk_reg_prcc_pclk("p1_pclk1", "per1clk", bases[CLKRST1_INDEX],
- BIT(1), 0);
- clk_register_clkdev(clk, "apb_pclk", "uart1");
-
- clk = clk_reg_prcc_pclk("p1_pclk2", "per1clk", bases[CLKRST1_INDEX],
- BIT(2), 0);
- clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.1");
-
- clk = clk_reg_prcc_pclk("p1_pclk3", "per1clk", bases[CLKRST1_INDEX],
- BIT(3), 0);
- clk_register_clkdev(clk, "apb_pclk", "msp0");
- clk_register_clkdev(clk, "apb_pclk", "dbx5x0-msp-i2s.0");
-
- clk = clk_reg_prcc_pclk("p1_pclk4", "per1clk", bases[CLKRST1_INDEX],
- BIT(4), 0);
- clk_register_clkdev(clk, "apb_pclk", "msp1");
- clk_register_clkdev(clk, "apb_pclk", "dbx5x0-msp-i2s.1");
-
- clk = clk_reg_prcc_pclk("p1_pclk5", "per1clk", bases[CLKRST1_INDEX],
- BIT(5), 0);
- clk_register_clkdev(clk, "apb_pclk", "sdi0");
-
- clk = clk_reg_prcc_pclk("p1_pclk6", "per1clk", bases[CLKRST1_INDEX],
- BIT(6), 0);
- clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.2");
-
- clk = clk_reg_prcc_pclk("p1_pclk7", "per1clk", bases[CLKRST1_INDEX],
- BIT(7), 0);
- clk_register_clkdev(clk, NULL, "spi3");
-
- clk = clk_reg_prcc_pclk("p1_pclk8", "per1clk", bases[CLKRST1_INDEX],
- BIT(8), 0);
- clk_register_clkdev(clk, "apb_pclk", "slimbus0");
-
- clk = clk_reg_prcc_pclk("p1_pclk9", "per1clk", bases[CLKRST1_INDEX],
- BIT(9), 0);
- clk_register_clkdev(clk, NULL, "gpio.0");
- clk_register_clkdev(clk, NULL, "gpio.1");
- clk_register_clkdev(clk, NULL, "gpioblock0");
- clk_register_clkdev(clk, "apb_pclk", "ab85xx-codec.0");
-
- clk = clk_reg_prcc_pclk("p1_pclk10", "per1clk", bases[CLKRST1_INDEX],
- BIT(10), 0);
- clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.4");
-
- clk = clk_reg_prcc_pclk("p1_pclk11", "per1clk", bases[CLKRST1_INDEX],
- BIT(11), 0);
- clk_register_clkdev(clk, "apb_pclk", "msp3");
- clk_register_clkdev(clk, "apb_pclk", "dbx5x0-msp-i2s.3");
-
- /* Peripheral 2 : PRCC P-clocks */
- clk = clk_reg_prcc_pclk("p2_pclk0", "per2clk", bases[CLKRST2_INDEX],
- BIT(0), 0);
- clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.3");
-
- clk = clk_reg_prcc_pclk("p2_pclk1", "per2clk", bases[CLKRST2_INDEX],
- BIT(1), 0);
- clk_register_clkdev(clk, NULL, "spi2");
-
- clk = clk_reg_prcc_pclk("p2_pclk2", "per2clk", bases[CLKRST2_INDEX],
- BIT(2), 0);
- clk_register_clkdev(clk, NULL, "spi1");
-
- clk = clk_reg_prcc_pclk("p2_pclk3", "per2clk", bases[CLKRST2_INDEX],
- BIT(3), 0);
- clk_register_clkdev(clk, NULL, "pwl");
-
- clk = clk_reg_prcc_pclk("p2_pclk4", "per2clk", bases[CLKRST2_INDEX],
- BIT(4), 0);
- clk_register_clkdev(clk, "apb_pclk", "sdi4");
-
- clk = clk_reg_prcc_pclk("p2_pclk5", "per2clk", bases[CLKRST2_INDEX],
- BIT(5), 0);
- clk_register_clkdev(clk, "apb_pclk", "msp2");
- clk_register_clkdev(clk, "apb_pclk", "dbx5x0-msp-i2s.2");
-
- clk = clk_reg_prcc_pclk("p2_pclk6", "per2clk", bases[CLKRST2_INDEX],
- BIT(6), 0);
- clk_register_clkdev(clk, "apb_pclk", "sdi1");
-
- clk = clk_reg_prcc_pclk("p2_pclk7", "per2clk", bases[CLKRST2_INDEX],
- BIT(7), 0);
- clk_register_clkdev(clk, "apb_pclk", "sdi3");
-
- clk = clk_reg_prcc_pclk("p2_pclk8", "per2clk", bases[CLKRST2_INDEX],
- BIT(8), 0);
- clk_register_clkdev(clk, NULL, "spi0");
-
- clk = clk_reg_prcc_pclk("p2_pclk9", "per2clk", bases[CLKRST2_INDEX],
- BIT(9), 0);
- clk_register_clkdev(clk, "hsir_hclk", "ste_hsi.0");
-
- clk = clk_reg_prcc_pclk("p2_pclk10", "per2clk", bases[CLKRST2_INDEX],
- BIT(10), 0);
- clk_register_clkdev(clk, "hsit_hclk", "ste_hsi.0");
-
- clk = clk_reg_prcc_pclk("p2_pclk11", "per2clk", bases[CLKRST2_INDEX],
- BIT(11), 0);
- clk_register_clkdev(clk, NULL, "gpio.6");
- clk_register_clkdev(clk, NULL, "gpio.7");
- clk_register_clkdev(clk, NULL, "gpioblock1");
-
- clk = clk_reg_prcc_pclk("p2_pclk12", "per2clk", bases[CLKRST2_INDEX],
- BIT(12), 0);
- clk_register_clkdev(clk, "msp4-pclk", "ab85xx-codec.0");
-
- /* Peripheral 3 : PRCC P-clocks */
- clk = clk_reg_prcc_pclk("p3_pclk0", "per3clk", bases[CLKRST3_INDEX],
- BIT(0), 0);
- clk_register_clkdev(clk, NULL, "fsmc");
-
- clk = clk_reg_prcc_pclk("p3_pclk1", "per3clk", bases[CLKRST3_INDEX],
- BIT(1), 0);
- clk_register_clkdev(clk, "apb_pclk", "ssp0");
-
- clk = clk_reg_prcc_pclk("p3_pclk2", "per3clk", bases[CLKRST3_INDEX],
- BIT(2), 0);
- clk_register_clkdev(clk, "apb_pclk", "ssp1");
-
- clk = clk_reg_prcc_pclk("p3_pclk3", "per3clk", bases[CLKRST3_INDEX],
- BIT(3), 0);
- clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.0");
-
- clk = clk_reg_prcc_pclk("p3_pclk4", "per3clk", bases[CLKRST3_INDEX],
- BIT(4), 0);
- clk_register_clkdev(clk, "apb_pclk", "sdi2");
-
- clk = clk_reg_prcc_pclk("p3_pclk5", "per3clk", bases[CLKRST3_INDEX],
- BIT(5), 0);
- clk_register_clkdev(clk, "apb_pclk", "ske");
- clk_register_clkdev(clk, "apb_pclk", "nmk-ske-keypad");
-
- clk = clk_reg_prcc_pclk("p3_pclk6", "per3clk", bases[CLKRST3_INDEX],
- BIT(6), 0);
- clk_register_clkdev(clk, "apb_pclk", "uart2");
-
- clk = clk_reg_prcc_pclk("p3_pclk7", "per3clk", bases[CLKRST3_INDEX],
- BIT(7), 0);
- clk_register_clkdev(clk, "apb_pclk", "sdi5");
-
- clk = clk_reg_prcc_pclk("p3_pclk8", "per3clk", bases[CLKRST3_INDEX],
- BIT(8), 0);
- clk_register_clkdev(clk, NULL, "gpio.2");
- clk_register_clkdev(clk, NULL, "gpio.3");
- clk_register_clkdev(clk, NULL, "gpio.4");
- clk_register_clkdev(clk, NULL, "gpio.5");
- clk_register_clkdev(clk, NULL, "gpioblock2");
-
- clk = clk_reg_prcc_pclk("p3_pclk9", "per3clk", bases[CLKRST3_INDEX],
- BIT(9), 0);
- clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.5");
-
- clk = clk_reg_prcc_pclk("p3_pclk10", "per3clk", bases[CLKRST3_INDEX],
- BIT(10), 0);
- clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.6");
-
- clk = clk_reg_prcc_pclk("p3_pclk11", "per3clk", bases[CLKRST3_INDEX],
- BIT(11), 0);
- clk_register_clkdev(clk, "apb_pclk", "uart3");
-
- clk = clk_reg_prcc_pclk("p3_pclk12", "per3clk", bases[CLKRST3_INDEX],
- BIT(12), 0);
- clk_register_clkdev(clk, "apb_pclk", "uart4");
-
- /* Peripheral 5 : PRCC P-clocks */
- clk = clk_reg_prcc_pclk("p5_pclk0", "per5clk", bases[CLKRST5_INDEX],
- BIT(0), 0);
- clk_register_clkdev(clk, "usb", "musb-ux500.0");
- clk_register_clkdev(clk, "usbclk", "ab-iddet.0");
-
- clk = clk_reg_prcc_pclk("p5_pclk1", "per5clk", bases[CLKRST5_INDEX],
- BIT(1), 0);
- clk_register_clkdev(clk, NULL, "gpio.8");
- clk_register_clkdev(clk, NULL, "gpioblock3");
-
- /* Peripheral 6 : PRCC P-clocks */
- clk = clk_reg_prcc_pclk("p6_pclk0", "per6clk", bases[CLKRST6_INDEX],
- BIT(0), 0);
- clk_register_clkdev(clk, "apb_pclk", "rng");
-
- clk = clk_reg_prcc_pclk("p6_pclk1", "per6clk", bases[CLKRST6_INDEX],
- BIT(1), 0);
- clk_register_clkdev(clk, NULL, "cryp0");
- clk_register_clkdev(clk, NULL, "cryp1");
-
- clk = clk_reg_prcc_pclk("p6_pclk2", "per6clk", bases[CLKRST6_INDEX],
- BIT(2), 0);
- clk_register_clkdev(clk, NULL, "hash0");
-
- clk = clk_reg_prcc_pclk("p6_pclk3", "per6clk", bases[CLKRST6_INDEX],
- BIT(3), 0);
- clk_register_clkdev(clk, NULL, "pka");
-
- clk = clk_reg_prcc_pclk("p6_pclk4", "per6clk", bases[CLKRST6_INDEX],
- BIT(4), 0);
- clk_register_clkdev(clk, NULL, "db8540-hash1");
-
- clk = clk_reg_prcc_pclk("p6_pclk5", "per6clk", bases[CLKRST6_INDEX],
- BIT(5), 0);
- clk_register_clkdev(clk, NULL, "cfgreg");
-
- clk = clk_reg_prcc_pclk("p6_pclk6", "per6clk", bases[CLKRST6_INDEX],
- BIT(6), 0);
- clk_register_clkdev(clk, "apb_pclk", "mtu0");
-
- clk = clk_reg_prcc_pclk("p6_pclk7", "per6clk", bases[CLKRST6_INDEX],
- BIT(7), 0);
- clk_register_clkdev(clk, "apb_pclk", "mtu1");
-
- /*
- * PRCC K-clocks ==> see table PRCC_PCKEN/PRCC_KCKEN
- * This differs from the internal implementation:
- * We don't use the PERPIH[n| clock as parent, since those _should_
- * only be used as parents for the P-clocks.
- * TODO: "parentjoin" with corresponding P-clocks for all K-clocks.
- */
-
- /* Peripheral 1 : PRCC K-clocks */
- clk = clk_reg_prcc_kclk("p1_uart0_kclk", "uartclk",
- bases[CLKRST1_INDEX], BIT(0), CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "uart0");
-
- clk = clk_reg_prcc_kclk("p1_uart1_kclk", "uartclk",
- bases[CLKRST1_INDEX], BIT(1), CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "uart1");
-
- clk = clk_reg_prcc_kclk("p1_i2c1_kclk", "i2cclk",
- bases[CLKRST1_INDEX], BIT(2), CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "nmk-i2c.1");
-
- clk = clk_reg_prcc_kclk("p1_msp0_kclk", "msp02clk",
- bases[CLKRST1_INDEX], BIT(3), CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "msp0");
- clk_register_clkdev(clk, NULL, "dbx5x0-msp-i2s.0");
-
- clk = clk_reg_prcc_kclk("p1_msp1_kclk", "msp1clk",
- bases[CLKRST1_INDEX], BIT(4), CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "msp1");
- clk_register_clkdev(clk, NULL, "dbx5x0-msp-i2s.1");
-
- clk = clk_reg_prcc_kclk("p1_sdi0_kclk", "sdmmchclk",
- bases[CLKRST1_INDEX], BIT(5), CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "sdi0");
-
- clk = clk_reg_prcc_kclk("p1_i2c2_kclk", "i2cclk",
- bases[CLKRST1_INDEX], BIT(6), CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "nmk-i2c.2");
-
- clk = clk_reg_prcc_kclk("p1_slimbus0_kclk", "slimclk",
- bases[CLKRST1_INDEX], BIT(8), CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "slimbus0");
-
- clk = clk_reg_prcc_kclk("p1_i2c4_kclk", "i2cclk",
- bases[CLKRST1_INDEX], BIT(9), CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "nmk-i2c.4");
-
- clk = clk_reg_prcc_kclk("p1_msp3_kclk", "msp1clk",
- bases[CLKRST1_INDEX], BIT(10), CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "msp3");
- clk_register_clkdev(clk, NULL, "dbx5x0-msp-i2s.3");
-
- /* Peripheral 2 : PRCC K-clocks */
- clk = clk_reg_prcc_kclk("p2_i2c3_kclk", "i2cclk",
- bases[CLKRST2_INDEX], BIT(0), CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "nmk-i2c.3");
-
- clk = clk_reg_prcc_kclk("p2_pwl_kclk", "rtc32k",
- bases[CLKRST2_INDEX], BIT(1), CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "pwl");
-
- clk = clk_reg_prcc_kclk("p2_sdi4_kclk", "sdmmchclk",
- bases[CLKRST2_INDEX], BIT(2), CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "sdi4");
-
- clk = clk_reg_prcc_kclk("p2_msp2_kclk", "msp02clk",
- bases[CLKRST2_INDEX], BIT(3), CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "msp2");
- clk_register_clkdev(clk, NULL, "dbx5x0-msp-i2s.2");
-
- clk = clk_reg_prcc_kclk("p2_sdi1_kclk", "sdmmchclk",
- bases[CLKRST2_INDEX], BIT(4), CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "sdi1");
-
- clk = clk_reg_prcc_kclk("p2_sdi3_kclk", "sdmmcclk",
- bases[CLKRST2_INDEX], BIT(5), CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "sdi3");
-
- clk = clk_reg_prcc_kclk("p2_ssirx_kclk", "hsirxclk",
- bases[CLKRST2_INDEX], BIT(6),
- CLK_SET_RATE_GATE|CLK_SET_RATE_PARENT);
- clk_register_clkdev(clk, "hsir_hsirxclk", "ste_hsi.0");
-
- clk = clk_reg_prcc_kclk("p2_ssitx_kclk", "hsitxclk",
- bases[CLKRST2_INDEX], BIT(7),
- CLK_SET_RATE_GATE|CLK_SET_RATE_PARENT);
- clk_register_clkdev(clk, "hsit_hsitxclk", "ste_hsi.0");
-
- /* Should only be 9540, but might be added for 85xx as well */
- clk = clk_reg_prcc_kclk("p2_msp4_kclk", "msp02clk",
- bases[CLKRST2_INDEX], BIT(9), CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "msp4");
- clk_register_clkdev(clk, "msp4", "ab85xx-codec.0");
-
- /* Peripheral 3 : PRCC K-clocks */
- clk = clk_reg_prcc_kclk("p3_ssp0_kclk", "sspclk",
- bases[CLKRST3_INDEX], BIT(1), CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "ssp0");
-
- clk = clk_reg_prcc_kclk("p3_ssp1_kclk", "sspclk",
- bases[CLKRST3_INDEX], BIT(2), CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "ssp1");
-
- clk = clk_reg_prcc_kclk("p3_i2c0_kclk", "i2cclk",
- bases[CLKRST3_INDEX], BIT(3), CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "nmk-i2c.0");
-
- clk = clk_reg_prcc_kclk("p3_sdi2_kclk", "sdmmchclk",
- bases[CLKRST3_INDEX], BIT(4), CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "sdi2");
-
- clk = clk_reg_prcc_kclk("p3_ske_kclk", "rtc32k",
- bases[CLKRST3_INDEX], BIT(5), CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "ske");
- clk_register_clkdev(clk, NULL, "nmk-ske-keypad");
-
- clk = clk_reg_prcc_kclk("p3_uart2_kclk", "uartclk",
- bases[CLKRST3_INDEX], BIT(6), CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "uart2");
-
- clk = clk_reg_prcc_kclk("p3_sdi5_kclk", "sdmmcclk",
- bases[CLKRST3_INDEX], BIT(7), CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "sdi5");
-
- clk = clk_reg_prcc_kclk("p3_i2c5_kclk", "i2cclk",
- bases[CLKRST3_INDEX], BIT(8), CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "nmk-i2c.5");
-
- clk = clk_reg_prcc_kclk("p3_i2c6_kclk", "i2cclk",
- bases[CLKRST3_INDEX], BIT(9), CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "nmk-i2c.6");
-
- clk = clk_reg_prcc_kclk("p3_uart3_kclk", "uartclk",
- bases[CLKRST3_INDEX], BIT(10), CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "uart3");
-
- clk = clk_reg_prcc_kclk("p3_uart4_kclk", "uartclk",
- bases[CLKRST3_INDEX], BIT(11), CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "uart4");
-
- /* Peripheral 6 : PRCC K-clocks */
- clk = clk_reg_prcc_kclk("p6_rng_kclk", "rngclk",
- bases[CLKRST6_INDEX], BIT(0), CLK_SET_RATE_GATE);
- clk_register_clkdev(clk, NULL, "rng");
-}
-CLK_OF_DECLARE(u8540_clks, "stericsson,u8540-clks", u8540_clk_init);
diff --git a/drivers/clk/ux500/u9540_clk.c b/drivers/clk/ux500/u9540_clk.c
deleted file mode 100644
index 7b6bca49ce42..000000000000
--- a/drivers/clk/ux500/u9540_clk.c
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Clock definitions for u9540 platform.
- *
- * Copyright (C) 2012 ST-Ericsson SA
- * Author: Ulf Hansson <ulf.hansson@linaro.org>
- *
- * License terms: GNU General Public License (GPL) version 2
- */
-
-#include <linux/clk-provider.h>
-#include <linux/mfd/dbx500-prcmu.h>
-#include "clk.h"
-
-static void u9540_clk_init(struct device_node *np)
-{
- /* register clocks here */
-}
-CLK_OF_DECLARE(u9540_clks, "stericsson,u9540-clks", u9540_clk_init);
diff --git a/drivers/clk/versatile/clk-vexpress-osc.c b/drivers/clk/versatile/clk-vexpress-osc.c
index e7a868b83fe5..dd08ecb498be 100644
--- a/drivers/clk/versatile/clk-vexpress-osc.c
+++ b/drivers/clk/versatile/clk-vexpress-osc.c
@@ -44,10 +44,10 @@ static long vexpress_osc_round_rate(struct clk_hw *hw, unsigned long rate,
{
struct vexpress_osc *osc = to_vexpress_osc(hw);
- if (WARN_ON(osc->rate_min && rate < osc->rate_min))
+ if (osc->rate_min && rate < osc->rate_min)
rate = osc->rate_min;
- if (WARN_ON(osc->rate_max && rate > osc->rate_max))
+ if (osc->rate_max && rate > osc->rate_max)
rate = osc->rate_max;
return rate;
@@ -104,6 +104,7 @@ static int vexpress_osc_probe(struct platform_device *pdev)
return PTR_ERR(clk);
of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get, clk);
+ clk_hw_set_rate_range(&osc->hw, osc->rate_min, osc->rate_max);
dev_dbg(&pdev->dev, "Registered clock '%s'\n", init.name);
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index d2e5382821a4..8e8a09755d10 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -21,6 +21,9 @@ config CLKEVT_I8253
config I8253_LOCK
bool
+config OMAP_DM_TIMER
+ bool
+
config CLKBLD_I8253
def_bool y if CLKSRC_I8253 || CLKEVT_I8253 || I8253_LOCK
@@ -130,6 +133,14 @@ config VT8500_TIMER
help
Enables support for the VT8500 driver.
+config NPCM7XX_TIMER
+ bool "NPCM7xx timer driver" if COMPILE_TEST
+ depends on HAS_IOMEM
+ select CLKSRC_MMIO
+ help
+ Enable 24-bit TIMER0 and TIMER1 counters in the NPCM7xx architecture,
+ While TIMER0 serves as clockevent and TIMER1 serves as clocksource.
+
config CADENCE_TTC_TIMER
bool "Cadence TTC timer driver" if COMPILE_TEST
depends on COMMON_CLK
@@ -392,11 +403,6 @@ config ATMEL_ST
help
Support for the Atmel ST timer.
-config CLKSRC_METAG_GENERIC
- def_bool y if METAG
- help
- This option enables support for the Meta per-thread timers.
-
config CLKSRC_EXYNOS_MCT
bool "Exynos multi core timer driver" if COMPILE_TEST
depends on ARM || ARM64
@@ -592,4 +598,13 @@ config CLKSRC_ST_LPC
Enable this option to use the Low Power controller timer
as clocksource.
+config ATCPIT100_TIMER
+ bool "ATCPIT100 timer driver"
+ depends on NDS32 || COMPILE_TEST
+ depends on HAS_IOMEM
+ select TIMER_OF
+ default NDS32
+ help
+ This option enables support for the Andestech ATCPIT100 timers.
+
endmenu
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index d6dec4489d66..00caf37e52f9 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_EM_TIMER_STI) += em_sti.o
obj-$(CONFIG_CLKBLD_I8253) += i8253.o
obj-$(CONFIG_CLKSRC_MMIO) += mmio.o
obj-$(CONFIG_DIGICOLOR_TIMER) += timer-digicolor.o
+obj-$(CONFIG_OMAP_DM_TIMER) += timer-ti-dm.o
obj-$(CONFIG_DW_APB_TIMER) += dw_apb_timer.o
obj-$(CONFIG_DW_APB_TIMER_OF) += dw_apb_timer_of.o
obj-$(CONFIG_FTTMR010_TIMER) += timer-fttmr010.o
@@ -55,13 +56,13 @@ obj-$(CONFIG_CLKSRC_NPS) += timer-nps.o
obj-$(CONFIG_OXNAS_RPS_TIMER) += timer-oxnas-rps.o
obj-$(CONFIG_OWL_TIMER) += owl-timer.o
obj-$(CONFIG_SPRD_TIMER) += timer-sprd.o
+obj-$(CONFIG_NPCM7XX_TIMER) += timer-npcm7xx.o
obj-$(CONFIG_ARC_TIMERS) += arc_timer.o
obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o
obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o
obj-$(CONFIG_ARMV7M_SYSTICK) += armv7m_systick.o
obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp804.o
-obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o
obj-$(CONFIG_ARCH_HAS_TICK_BROADCAST) += dummy_timer.o
obj-$(CONFIG_KEYSTONE_TIMER) += timer-keystone.o
obj-$(CONFIG_INTEGRATOR_AP_TIMER) += timer-integrator-ap.o
@@ -76,3 +77,4 @@ obj-$(CONFIG_H8300_TMR16) += h8300_timer16.o
obj-$(CONFIG_H8300_TPU) += h8300_tpu.o
obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o
obj-$(CONFIG_X86_NUMACHIP) += numachip.o
+obj-$(CONFIG_ATCPIT100_TIMER) += timer-atcpit100.o
diff --git a/drivers/clocksource/metag_generic.c b/drivers/clocksource/metag_generic.c
deleted file mode 100644
index 3e5fa2f62d5f..000000000000
--- a/drivers/clocksource/metag_generic.c
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2005-2013 Imagination Technologies Ltd.
- *
- * 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.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- *
- *
- * Support for Meta per-thread timers.
- *
- * Meta hardware threads have 2 timers. The background timer (TXTIMER) is used
- * as a free-running time base (hz clocksource), and the interrupt timer
- * (TXTIMERI) is used for the timer interrupt (clock event). Both counters
- * traditionally count at approximately 1MHz.
- */
-
-#include <clocksource/metag_generic.h>
-#include <linux/cpu.h>
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/param.h>
-#include <linux/time.h>
-#include <linux/init.h>
-#include <linux/proc_fs.h>
-#include <linux/clocksource.h>
-#include <linux/clockchips.h>
-#include <linux/interrupt.h>
-
-#include <asm/clock.h>
-#include <asm/hwthread.h>
-#include <asm/core_reg.h>
-#include <asm/metag_mem.h>
-#include <asm/tbx.h>
-
-#define HARDWARE_FREQ 1000000 /* 1MHz */
-#define HARDWARE_DIV 1 /* divide by 1 = 1MHz clock */
-#define HARDWARE_TO_NS_SHIFT 10 /* convert ticks to ns */
-
-static unsigned int hwtimer_freq = HARDWARE_FREQ;
-static DEFINE_PER_CPU(struct clock_event_device, local_clockevent);
-static DEFINE_PER_CPU(char [11], local_clockevent_name);
-
-static int metag_timer_set_next_event(unsigned long delta,
- struct clock_event_device *dev)
-{
- __core_reg_set(TXTIMERI, -delta);
- return 0;
-}
-
-static u64 metag_clocksource_read(struct clocksource *cs)
-{
- return __core_reg_get(TXTIMER);
-}
-
-static struct clocksource clocksource_metag = {
- .name = "META",
- .rating = 200,
- .mask = CLOCKSOURCE_MASK(32),
- .read = metag_clocksource_read,
- .flags = CLOCK_SOURCE_IS_CONTINUOUS,
-};
-
-static irqreturn_t metag_timer_interrupt(int irq, void *dummy)
-{
- struct clock_event_device *evt = this_cpu_ptr(&local_clockevent);
-
- evt->event_handler(evt);
-
- return IRQ_HANDLED;
-}
-
-static struct irqaction metag_timer_irq = {
- .name = "META core timer",
- .handler = metag_timer_interrupt,
- .flags = IRQF_TIMER | IRQF_IRQPOLL | IRQF_PERCPU,
-};
-
-unsigned long long sched_clock(void)
-{
- unsigned long long ticks = __core_reg_get(TXTIMER);
- return ticks << HARDWARE_TO_NS_SHIFT;
-}
-
-static int arch_timer_starting_cpu(unsigned int cpu)
-{
- unsigned int txdivtime;
- struct clock_event_device *clk = &per_cpu(local_clockevent, cpu);
- char *name = per_cpu(local_clockevent_name, cpu);
-
- txdivtime = __core_reg_get(TXDIVTIME);
-
- txdivtime &= ~TXDIVTIME_DIV_BITS;
- txdivtime |= (HARDWARE_DIV & TXDIVTIME_DIV_BITS);
-
- __core_reg_set(TXDIVTIME, txdivtime);
-
- sprintf(name, "META %d", cpu);
- clk->name = name;
- clk->features = CLOCK_EVT_FEAT_ONESHOT,
-
- clk->rating = 200,
- clk->shift = 12,
- clk->irq = tbisig_map(TBID_SIGNUM_TRT),
- clk->set_next_event = metag_timer_set_next_event,
-
- clk->mult = div_sc(hwtimer_freq, NSEC_PER_SEC, clk->shift);
- clk->max_delta_ns = clockevent_delta2ns(0x7fffffff, clk);
- clk->max_delta_ticks = 0x7fffffff;
- clk->min_delta_ns = clockevent_delta2ns(0xf, clk);
- clk->min_delta_ticks = 0xf;
- clk->cpumask = cpumask_of(cpu);
-
- clockevents_register_device(clk);
-
- /*
- * For all non-boot CPUs we need to synchronize our free
- * running clock (TXTIMER) with the boot CPU's clock.
- *
- * While this won't be accurate, it should be close enough.
- */
- if (cpu) {
- unsigned int thread0 = cpu_2_hwthread_id[0];
- unsigned long val;
-
- val = core_reg_read(TXUCT_ID, TXTIMER_REGNUM, thread0);
- __core_reg_set(TXTIMER, val);
- }
- return 0;
-}
-
-int __init metag_generic_timer_init(void)
-{
- /*
- * On Meta 2 SoCs, the actual frequency of the timer is based on the
- * Meta core clock speed divided by an integer, so it is only
- * approximately 1MHz. Calculating the real frequency here drastically
- * reduces clock skew on these SoCs.
- */
-#ifdef CONFIG_METAG_META21
- hwtimer_freq = get_coreclock() / (metag_in32(EXPAND_TIMER_DIV) + 1);
-#endif
- pr_info("Timer frequency: %u Hz\n", hwtimer_freq);
-
- clocksource_register_hz(&clocksource_metag, hwtimer_freq);
-
- setup_irq(tbisig_map(TBID_SIGNUM_TRT), &metag_timer_irq);
-
- /* Hook cpu boot to configure the CPU's timers */
- return cpuhp_setup_state(CPUHP_AP_METAG_TIMER_STARTING,
- "clockevents/metag:starting",
- arch_timer_starting_cpu, NULL);
-}
diff --git a/drivers/clocksource/timer-atcpit100.c b/drivers/clocksource/timer-atcpit100.c
new file mode 100644
index 000000000000..5e23d7b4a722
--- /dev/null
+++ b/drivers/clocksource/timer-atcpit100.c
@@ -0,0 +1,266 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2005-2017 Andes Technology Corporation
+/*
+ * Andestech ATCPIT100 Timer Device Driver Implementation
+ * Rick Chen, Andes Technology Corporation <rick@andestech.com>
+ *
+ */
+
+#include <linux/irq.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/cpufreq.h>
+#include <linux/sched.h>
+#include <linux/sched_clock.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include "timer-of.h"
+#ifdef CONFIG_NDS32
+#include <asm/vdso_timer_info.h>
+#endif
+
+/*
+ * Definition of register offsets
+ */
+
+/* ID and Revision Register */
+#define ID_REV 0x0
+
+/* Configuration Register */
+#define CFG 0x10
+
+/* Interrupt Enable Register */
+#define INT_EN 0x14
+#define CH_INT_EN(c, i) ((1<<i)<<(4*c))
+#define CH0INT0EN 0x01
+
+/* Interrupt Status Register */
+#define INT_STA 0x18
+#define CH0INT0 0x01
+
+/* Channel Enable Register */
+#define CH_EN 0x1C
+#define CH0TMR0EN 0x1
+#define CH1TMR0EN 0x10
+
+/* Channel 0 , 1 Control Register */
+#define CH0_CTL (0x20)
+#define CH1_CTL (0x20 + 0x10)
+
+/* Channel clock source , bit 3 , 0:External clock , 1:APB clock */
+#define APB_CLK BIT(3)
+
+/* Channel mode , bit 0~2 */
+#define TMR_32 0x1
+#define TMR_16 0x2
+#define TMR_8 0x3
+
+/* Channel 0 , 1 Reload Register */
+#define CH0_REL (0x24)
+#define CH1_REL (0x24 + 0x10)
+
+/* Channel 0 , 1 Counter Register */
+#define CH0_CNT (0x28)
+#define CH1_CNT (0x28 + 0x10)
+
+#define TIMER_SYNC_TICKS 3
+
+static void atcpit100_ch1_tmr0_en(void __iomem *base)
+{
+ writel(~0, base + CH1_REL);
+ writel(APB_CLK|TMR_32, base + CH1_CTL);
+}
+
+static void atcpit100_ch0_tmr0_en(void __iomem *base)
+{
+ writel(APB_CLK|TMR_32, base + CH0_CTL);
+}
+
+static void atcpit100_clkevt_time_setup(void __iomem *base, unsigned long delay)
+{
+ writel(delay, base + CH0_CNT);
+ writel(delay, base + CH0_REL);
+}
+
+static void atcpit100_timer_clear_interrupt(void __iomem *base)
+{
+ u32 val;
+
+ val = readl(base + INT_STA);
+ writel(val | CH0INT0, base + INT_STA);
+}
+
+static void atcpit100_clocksource_start(void __iomem *base)
+{
+ u32 val;
+
+ val = readl(base + CH_EN);
+ writel(val | CH1TMR0EN, base + CH_EN);
+}
+
+static void atcpit100_clkevt_time_start(void __iomem *base)
+{
+ u32 val;
+
+ val = readl(base + CH_EN);
+ writel(val | CH0TMR0EN, base + CH_EN);
+}
+
+static void atcpit100_clkevt_time_stop(void __iomem *base)
+{
+ u32 val;
+
+ atcpit100_timer_clear_interrupt(base);
+ val = readl(base + CH_EN);
+ writel(val & ~CH0TMR0EN, base + CH_EN);
+}
+
+static int atcpit100_clkevt_next_event(unsigned long evt,
+ struct clock_event_device *clkevt)
+{
+ u32 val;
+ struct timer_of *to = to_timer_of(clkevt);
+
+ val = readl(timer_of_base(to) + CH_EN);
+ writel(val & ~CH0TMR0EN, timer_of_base(to) + CH_EN);
+ writel(evt, timer_of_base(to) + CH0_REL);
+ writel(val | CH0TMR0EN, timer_of_base(to) + CH_EN);
+
+ return 0;
+}
+
+static int atcpit100_clkevt_set_periodic(struct clock_event_device *evt)
+{
+ struct timer_of *to = to_timer_of(evt);
+
+ atcpit100_clkevt_time_setup(timer_of_base(to), timer_of_period(to));
+ atcpit100_clkevt_time_start(timer_of_base(to));
+
+ return 0;
+}
+static int atcpit100_clkevt_shutdown(struct clock_event_device *evt)
+{
+ struct timer_of *to = to_timer_of(evt);
+
+ atcpit100_clkevt_time_stop(timer_of_base(to));
+
+ return 0;
+}
+static int atcpit100_clkevt_set_oneshot(struct clock_event_device *evt)
+{
+ struct timer_of *to = to_timer_of(evt);
+ u32 val;
+
+ writel(~0x0, timer_of_base(to) + CH0_REL);
+ val = readl(timer_of_base(to) + CH_EN);
+ writel(val | CH0TMR0EN, timer_of_base(to) + CH_EN);
+
+ return 0;
+}
+
+static irqreturn_t atcpit100_timer_interrupt(int irq, void *dev_id)
+{
+ struct clock_event_device *evt = (struct clock_event_device *)dev_id;
+ struct timer_of *to = to_timer_of(evt);
+
+ atcpit100_timer_clear_interrupt(timer_of_base(to));
+
+ evt->event_handler(evt);
+
+ return IRQ_HANDLED;
+}
+
+static struct timer_of to = {
+ .flags = TIMER_OF_IRQ | TIMER_OF_CLOCK | TIMER_OF_BASE,
+
+ .clkevt = {
+ .name = "atcpit100_tick",
+ .rating = 300,
+ .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+ .set_state_shutdown = atcpit100_clkevt_shutdown,
+ .set_state_periodic = atcpit100_clkevt_set_periodic,
+ .set_state_oneshot = atcpit100_clkevt_set_oneshot,
+ .tick_resume = atcpit100_clkevt_shutdown,
+ .set_next_event = atcpit100_clkevt_next_event,
+ .cpumask = cpu_all_mask,
+ },
+
+ .of_irq = {
+ .handler = atcpit100_timer_interrupt,
+ .flags = IRQF_TIMER | IRQF_IRQPOLL,
+ },
+
+ /*
+ * FIXME: we currently only support clocking using PCLK
+ * and using EXTCLK is not supported in the driver.
+ */
+ .of_clk = {
+ .name = "PCLK",
+ }
+};
+
+static u64 notrace atcpit100_timer_sched_read(void)
+{
+ return ~readl(timer_of_base(&to) + CH1_CNT);
+}
+
+#ifdef CONFIG_NDS32
+static void fill_vdso_need_info(struct device_node *node)
+{
+ struct resource timer_res;
+ of_address_to_resource(node, 0, &timer_res);
+ timer_info.mapping_base = (unsigned long)timer_res.start;
+ timer_info.cycle_count_down = true;
+ timer_info.cycle_count_reg_offset = CH1_CNT;
+}
+#endif
+
+static int __init atcpit100_timer_init(struct device_node *node)
+{
+ int ret;
+ u32 val;
+ void __iomem *base;
+
+ ret = timer_of_init(node, &to);
+ if (ret)
+ return ret;
+
+ base = timer_of_base(&to);
+
+ sched_clock_register(atcpit100_timer_sched_read, 32,
+ timer_of_rate(&to));
+
+ ret = clocksource_mmio_init(base + CH1_CNT,
+ node->name, timer_of_rate(&to), 300, 32,
+ clocksource_mmio_readl_down);
+
+ if (ret) {
+ pr_err("Failed to register clocksource\n");
+ return ret;
+ }
+
+ /* clear channel 0 timer0 interrupt */
+ atcpit100_timer_clear_interrupt(base);
+
+ clockevents_config_and_register(&to.clkevt, timer_of_rate(&to),
+ TIMER_SYNC_TICKS, 0xffffffff);
+ atcpit100_ch0_tmr0_en(base);
+ atcpit100_ch1_tmr0_en(base);
+ atcpit100_clocksource_start(base);
+ atcpit100_clkevt_time_start(base);
+
+ /* Enable channel 0 timer0 interrupt */
+ val = readl(base + INT_EN);
+ writel(val | CH0INT0EN, base + INT_EN);
+
+#ifdef CONFIG_NDS32
+ fill_vdso_need_info(node);
+#endif
+
+ return ret;
+}
+
+TIMER_OF_DECLARE(atcpit100, "andestech,atcpit100", atcpit100_timer_init);
diff --git a/drivers/clocksource/timer-imx-tpm.c b/drivers/clocksource/timer-imx-tpm.c
index 21bffdcb2f20..6c8318470b48 100644
--- a/drivers/clocksource/timer-imx-tpm.c
+++ b/drivers/clocksource/timer-imx-tpm.c
@@ -17,9 +17,14 @@
#include <linux/of_irq.h>
#include <linux/sched_clock.h>
+#define TPM_PARAM 0x4
+#define TPM_PARAM_WIDTH_SHIFT 16
+#define TPM_PARAM_WIDTH_MASK (0xff << 16)
#define TPM_SC 0x10
#define TPM_SC_CMOD_INC_PER_CNT (0x1 << 3)
#define TPM_SC_CMOD_DIV_DEFAULT 0x3
+#define TPM_SC_CMOD_DIV_MAX 0x7
+#define TPM_SC_TOF_MASK (0x1 << 7)
#define TPM_CNT 0x14
#define TPM_MOD 0x18
#define TPM_STATUS 0x1c
@@ -29,8 +34,11 @@
#define TPM_C0SC_MODE_SHIFT 2
#define TPM_C0SC_MODE_MASK 0x3c
#define TPM_C0SC_MODE_SW_COMPARE 0x4
+#define TPM_C0SC_CHF_MASK (0x1 << 7)
#define TPM_C0V 0x24
+static int counter_width;
+static int rating;
static void __iomem *timer_base;
static struct clock_event_device clockevent_tpm;
@@ -83,10 +91,11 @@ static int __init tpm_clocksource_init(unsigned long rate)
tpm_delay_timer.freq = rate;
register_current_timer_delay(&tpm_delay_timer);
- sched_clock_register(tpm_read_sched_clock, 32, rate);
+ sched_clock_register(tpm_read_sched_clock, counter_width, rate);
return clocksource_mmio_init(timer_base + TPM_CNT, "imx-tpm",
- rate, 200, 32, clocksource_mmio_readl_up);
+ rate, rating, counter_width,
+ clocksource_mmio_readl_up);
}
static int tpm_set_next_event(unsigned long delta,
@@ -105,7 +114,7 @@ static int tpm_set_next_event(unsigned long delta,
* of writing CNT registers which may cause the min_delta event got
* missed, so we need add a ETIME check here in case it happened.
*/
- return (int)((next - now) <= 0) ? -ETIME : 0;
+ return (int)(next - now) <= 0 ? -ETIME : 0;
}
static int tpm_set_state_oneshot(struct clock_event_device *evt)
@@ -139,7 +148,6 @@ static struct clock_event_device clockevent_tpm = {
.set_state_oneshot = tpm_set_state_oneshot,
.set_next_event = tpm_set_next_event,
.set_state_shutdown = tpm_set_state_shutdown,
- .rating = 200,
};
static int __init tpm_clockevent_init(unsigned long rate, int irq)
@@ -149,10 +157,11 @@ static int __init tpm_clockevent_init(unsigned long rate, int irq)
ret = request_irq(irq, tpm_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL,
"i.MX7ULP TPM Timer", &clockevent_tpm);
+ clockevent_tpm.rating = rating;
clockevent_tpm.cpumask = cpumask_of(0);
clockevent_tpm.irq = irq;
- clockevents_config_and_register(&clockevent_tpm,
- rate, 300, 0xfffffffe);
+ clockevents_config_and_register(&clockevent_tpm, rate, 300,
+ GENMASK(counter_width - 1, 1));
return ret;
}
@@ -179,7 +188,7 @@ static int __init tpm_timer_init(struct device_node *np)
ipg = of_clk_get_by_name(np, "ipg");
per = of_clk_get_by_name(np, "per");
if (IS_ERR(ipg) || IS_ERR(per)) {
- pr_err("tpm: failed to get igp or per clk\n");
+ pr_err("tpm: failed to get ipg or per clk\n");
ret = -ENODEV;
goto err_clk_get;
}
@@ -197,6 +206,11 @@ static int __init tpm_timer_init(struct device_node *np)
goto err_per_clk_enable;
}
+ counter_width = (readl(timer_base + TPM_PARAM) & TPM_PARAM_WIDTH_MASK)
+ >> TPM_PARAM_WIDTH_SHIFT;
+ /* use rating 200 for 32-bit counter and 150 for 16-bit counter */
+ rating = counter_width == 0x20 ? 200 : 150;
+
/*
* Initialize tpm module to a known state
* 1) Counter disabled
@@ -205,16 +219,25 @@ static int __init tpm_timer_init(struct device_node *np)
* 4) Channel0 disabled
* 5) DMA transfers disabled
*/
+ /* make sure counter is disabled */
writel(0, timer_base + TPM_SC);
+ /* TOF is W1C */
+ writel(TPM_SC_TOF_MASK, timer_base + TPM_SC);
writel(0, timer_base + TPM_CNT);
- writel(0, timer_base + TPM_C0SC);
+ /* CHF is W1C */
+ writel(TPM_C0SC_CHF_MASK, timer_base + TPM_C0SC);
- /* increase per cnt, div 8 by default */
- writel(TPM_SC_CMOD_INC_PER_CNT | TPM_SC_CMOD_DIV_DEFAULT,
+ /*
+ * increase per cnt,
+ * div 8 for 32-bit counter and div 128 for 16-bit counter
+ */
+ writel(TPM_SC_CMOD_INC_PER_CNT |
+ (counter_width == 0x20 ?
+ TPM_SC_CMOD_DIV_DEFAULT : TPM_SC_CMOD_DIV_MAX),
timer_base + TPM_SC);
/* set MOD register to maximum for free running mode */
- writel(0xffffffff, timer_base + TPM_MOD);
+ writel(GENMASK(counter_width - 1, 0), timer_base + TPM_MOD);
rate = clk_get_rate(per) >> 3;
ret = tpm_clocksource_init(rate);
diff --git a/drivers/clocksource/timer-npcm7xx.c b/drivers/clocksource/timer-npcm7xx.c
new file mode 100644
index 000000000000..7a9bb5532d99
--- /dev/null
+++ b/drivers/clocksource/timer-npcm7xx.c
@@ -0,0 +1,215 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2014-2018 Nuvoton Technologies tomer.maimon@nuvoton.com
+ * All rights reserved.
+ *
+ * Copyright 2017 Google, Inc.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/clockchips.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include "timer-of.h"
+
+/* Timers registers */
+#define NPCM7XX_REG_TCSR0 0x0 /* Timer 0 Control and Status Register */
+#define NPCM7XX_REG_TICR0 0x8 /* Timer 0 Initial Count Register */
+#define NPCM7XX_REG_TCSR1 0x4 /* Timer 1 Control and Status Register */
+#define NPCM7XX_REG_TICR1 0xc /* Timer 1 Initial Count Register */
+#define NPCM7XX_REG_TDR1 0x14 /* Timer 1 Data Register */
+#define NPCM7XX_REG_TISR 0x18 /* Timer Interrupt Status Register */
+
+/* Timers control */
+#define NPCM7XX_Tx_RESETINT 0x1f
+#define NPCM7XX_Tx_PERIOD BIT(27)
+#define NPCM7XX_Tx_INTEN BIT(29)
+#define NPCM7XX_Tx_COUNTEN BIT(30)
+#define NPCM7XX_Tx_ONESHOT 0x0
+#define NPCM7XX_Tx_OPER GENMASK(3, 27)
+#define NPCM7XX_Tx_MIN_PRESCALE 0x1
+#define NPCM7XX_Tx_TDR_MASK_BITS 24
+#define NPCM7XX_Tx_MAX_CNT 0xFFFFFF
+#define NPCM7XX_T0_CLR_INT 0x1
+#define NPCM7XX_Tx_CLR_CSR 0x0
+
+/* Timers operating mode */
+#define NPCM7XX_START_PERIODIC_Tx (NPCM7XX_Tx_PERIOD | NPCM7XX_Tx_COUNTEN | \
+ NPCM7XX_Tx_INTEN | \
+ NPCM7XX_Tx_MIN_PRESCALE)
+
+#define NPCM7XX_START_ONESHOT_Tx (NPCM7XX_Tx_ONESHOT | NPCM7XX_Tx_COUNTEN | \
+ NPCM7XX_Tx_INTEN | \
+ NPCM7XX_Tx_MIN_PRESCALE)
+
+#define NPCM7XX_START_Tx (NPCM7XX_Tx_COUNTEN | NPCM7XX_Tx_PERIOD | \
+ NPCM7XX_Tx_MIN_PRESCALE)
+
+#define NPCM7XX_DEFAULT_CSR (NPCM7XX_Tx_CLR_CSR | NPCM7XX_Tx_MIN_PRESCALE)
+
+static int npcm7xx_timer_resume(struct clock_event_device *evt)
+{
+ struct timer_of *to = to_timer_of(evt);
+ u32 val;
+
+ val = readl(timer_of_base(to) + NPCM7XX_REG_TCSR0);
+ val |= NPCM7XX_Tx_COUNTEN;
+ writel(val, timer_of_base(to) + NPCM7XX_REG_TCSR0);
+
+ return 0;
+}
+
+static int npcm7xx_timer_shutdown(struct clock_event_device *evt)
+{
+ struct timer_of *to = to_timer_of(evt);
+ u32 val;
+
+ val = readl(timer_of_base(to) + NPCM7XX_REG_TCSR0);
+ val &= ~NPCM7XX_Tx_COUNTEN;
+ writel(val, timer_of_base(to) + NPCM7XX_REG_TCSR0);
+
+ return 0;
+}
+
+static int npcm7xx_timer_oneshot(struct clock_event_device *evt)
+{
+ struct timer_of *to = to_timer_of(evt);
+ u32 val;
+
+ val = readl(timer_of_base(to) + NPCM7XX_REG_TCSR0);
+ val &= ~NPCM7XX_Tx_OPER;
+
+ val = readl(timer_of_base(to) + NPCM7XX_REG_TCSR0);
+ val |= NPCM7XX_START_ONESHOT_Tx;
+ writel(val, timer_of_base(to) + NPCM7XX_REG_TCSR0);
+
+ return 0;
+}
+
+static int npcm7xx_timer_periodic(struct clock_event_device *evt)
+{
+ struct timer_of *to = to_timer_of(evt);
+ u32 val;
+
+ val = readl(timer_of_base(to) + NPCM7XX_REG_TCSR0);
+ val &= ~NPCM7XX_Tx_OPER;
+
+ writel(timer_of_period(to), timer_of_base(to) + NPCM7XX_REG_TICR0);
+ val |= NPCM7XX_START_PERIODIC_Tx;
+
+ writel(val, timer_of_base(to) + NPCM7XX_REG_TCSR0);
+
+ return 0;
+}
+
+static int npcm7xx_clockevent_set_next_event(unsigned long evt,
+ struct clock_event_device *clk)
+{
+ struct timer_of *to = to_timer_of(clk);
+ u32 val;
+
+ writel(evt, timer_of_base(to) + NPCM7XX_REG_TICR0);
+ val = readl(timer_of_base(to) + NPCM7XX_REG_TCSR0);
+ val |= NPCM7XX_START_Tx;
+ writel(val, timer_of_base(to) + NPCM7XX_REG_TCSR0);
+
+ return 0;
+}
+
+static irqreturn_t npcm7xx_timer0_interrupt(int irq, void *dev_id)
+{
+ struct clock_event_device *evt = (struct clock_event_device *)dev_id;
+ struct timer_of *to = to_timer_of(evt);
+
+ writel(NPCM7XX_T0_CLR_INT, timer_of_base(to) + NPCM7XX_REG_TISR);
+
+ evt->event_handler(evt);
+
+ return IRQ_HANDLED;
+}
+
+static struct timer_of npcm7xx_to = {
+ .flags = TIMER_OF_IRQ | TIMER_OF_BASE | TIMER_OF_CLOCK,
+
+ .clkevt = {
+ .name = "npcm7xx-timer0",
+ .features = CLOCK_EVT_FEAT_PERIODIC |
+ CLOCK_EVT_FEAT_ONESHOT,
+ .set_next_event = npcm7xx_clockevent_set_next_event,
+ .set_state_shutdown = npcm7xx_timer_shutdown,
+ .set_state_periodic = npcm7xx_timer_periodic,
+ .set_state_oneshot = npcm7xx_timer_oneshot,
+ .tick_resume = npcm7xx_timer_resume,
+ .rating = 300,
+ },
+
+ .of_irq = {
+ .handler = npcm7xx_timer0_interrupt,
+ .flags = IRQF_TIMER | IRQF_IRQPOLL,
+ },
+};
+
+static void __init npcm7xx_clockevents_init(void)
+{
+ writel(NPCM7XX_DEFAULT_CSR,
+ timer_of_base(&npcm7xx_to) + NPCM7XX_REG_TCSR0);
+
+ writel(NPCM7XX_Tx_RESETINT,
+ timer_of_base(&npcm7xx_to) + NPCM7XX_REG_TISR);
+
+ npcm7xx_to.clkevt.cpumask = cpumask_of(0);
+ clockevents_config_and_register(&npcm7xx_to.clkevt,
+ timer_of_rate(&npcm7xx_to),
+ 0x1, NPCM7XX_Tx_MAX_CNT);
+}
+
+static void __init npcm7xx_clocksource_init(void)
+{
+ u32 val;
+
+ writel(NPCM7XX_DEFAULT_CSR,
+ timer_of_base(&npcm7xx_to) + NPCM7XX_REG_TCSR1);
+ writel(NPCM7XX_Tx_MAX_CNT,
+ timer_of_base(&npcm7xx_to) + NPCM7XX_REG_TICR1);
+
+ val = readl(timer_of_base(&npcm7xx_to) + NPCM7XX_REG_TCSR1);
+ val |= NPCM7XX_START_Tx;
+ writel(val, timer_of_base(&npcm7xx_to) + NPCM7XX_REG_TCSR1);
+
+ clocksource_mmio_init(timer_of_base(&npcm7xx_to) +
+ NPCM7XX_REG_TDR1,
+ "npcm7xx-timer1", timer_of_rate(&npcm7xx_to),
+ 200, (unsigned int)NPCM7XX_Tx_TDR_MASK_BITS,
+ clocksource_mmio_readl_down);
+}
+
+static int __init npcm7xx_timer_init(struct device_node *np)
+{
+ int ret;
+
+ ret = timer_of_init(np, &npcm7xx_to);
+ if (ret)
+ return ret;
+
+ /* Clock input is divided by PRESCALE + 1 before it is fed */
+ /* to the counter */
+ npcm7xx_to.of_clk.rate = npcm7xx_to.of_clk.rate /
+ (NPCM7XX_Tx_MIN_PRESCALE + 1);
+
+ npcm7xx_clocksource_init();
+ npcm7xx_clockevents_init();
+
+ pr_info("Enabling NPCM7xx clocksource timer base: %px, IRQ: %d ",
+ timer_of_base(&npcm7xx_to), timer_of_irq(&npcm7xx_to));
+
+ return 0;
+}
+
+TIMER_OF_DECLARE(npcm7xx, "nuvoton,npcm750-timer", npcm7xx_timer_init);
+
diff --git a/drivers/clocksource/timer-ti-dm.c b/drivers/clocksource/timer-ti-dm.c
new file mode 100644
index 000000000000..4cce6b224b87
--- /dev/null
+++ b/drivers/clocksource/timer-ti-dm.c
@@ -0,0 +1,1000 @@
+/*
+ * linux/arch/arm/plat-omap/dmtimer.c
+ *
+ * OMAP Dual-Mode Timers
+ *
+ * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/
+ * Tarun Kanti DebBarma <tarun.kanti@ti.com>
+ * Thara Gopinath <thara@ti.com>
+ *
+ * dmtimer adaptation to platform_driver.
+ *
+ * Copyright (C) 2005 Nokia Corporation
+ * OMAP2 support by Juha Yrjola
+ * API improvements and OMAP2 clock framework support by Timo Teras
+ *
+ * Copyright (C) 2009 Texas Instruments
+ * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.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 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
+ * 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/dmtimer-omap.h>
+
+#include <clocksource/timer-ti-dm.h>
+
+static u32 omap_reserved_systimers;
+static LIST_HEAD(omap_timer_list);
+static DEFINE_SPINLOCK(dm_timer_lock);
+
+enum {
+ REQUEST_ANY = 0,
+ REQUEST_BY_ID,
+ REQUEST_BY_CAP,
+ REQUEST_BY_NODE,
+};
+
+/**
+ * omap_dm_timer_read_reg - read timer registers in posted and non-posted mode
+ * @timer: timer pointer over which read operation to perform
+ * @reg: lowest byte holds the register offset
+ *
+ * The posted mode bit is encoded in reg. Note that in posted mode write
+ * pending bit must be checked. Otherwise a read of a non completed write
+ * will produce an error.
+ */
+static inline u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, u32 reg)
+{
+ WARN_ON((reg & 0xff) < _OMAP_TIMER_WAKEUP_EN_OFFSET);
+ return __omap_dm_timer_read(timer, reg, timer->posted);
+}
+
+/**
+ * omap_dm_timer_write_reg - write timer registers in posted and non-posted mode
+ * @timer: timer pointer over which write operation is to perform
+ * @reg: lowest byte holds the register offset
+ * @value: data to write into the register
+ *
+ * The posted mode bit is encoded in reg. Note that in posted mode the write
+ * pending bit must be checked. Otherwise a write on a register which has a
+ * pending write will be lost.
+ */
+static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, u32 reg,
+ u32 value)
+{
+ WARN_ON((reg & 0xff) < _OMAP_TIMER_WAKEUP_EN_OFFSET);
+ __omap_dm_timer_write(timer, reg, value, timer->posted);
+}
+
+static void omap_timer_restore_context(struct omap_dm_timer *timer)
+{
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_WAKEUP_EN_REG,
+ timer->context.twer);
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG,
+ timer->context.tcrr);
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG,
+ timer->context.tldr);
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG,
+ timer->context.tmar);
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG,
+ timer->context.tsicr);
+ writel_relaxed(timer->context.tier, timer->irq_ena);
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG,
+ timer->context.tclr);
+}
+
+static int omap_dm_timer_reset(struct omap_dm_timer *timer)
+{
+ u32 l, timeout = 100000;
+
+ if (timer->revision != 1)
+ return -EINVAL;
+
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0x06);
+
+ do {
+ l = __omap_dm_timer_read(timer,
+ OMAP_TIMER_V1_SYS_STAT_OFFSET, 0);
+ } while (!l && timeout--);
+
+ if (!timeout) {
+ dev_err(&timer->pdev->dev, "Timer failed to reset\n");
+ return -ETIMEDOUT;
+ }
+
+ /* Configure timer for smart-idle mode */
+ l = __omap_dm_timer_read(timer, OMAP_TIMER_OCP_CFG_OFFSET, 0);
+ l |= 0x2 << 0x3;
+ __omap_dm_timer_write(timer, OMAP_TIMER_OCP_CFG_OFFSET, l, 0);
+
+ timer->posted = 0;
+
+ return 0;
+}
+
+static int omap_dm_timer_of_set_source(struct omap_dm_timer *timer)
+{
+ int ret;
+ struct clk *parent;
+
+ /*
+ * FIXME: OMAP1 devices do not use the clock framework for dmtimers so
+ * do not call clk_get() for these devices.
+ */
+ if (!timer->fclk)
+ return -ENODEV;
+
+ parent = clk_get(&timer->pdev->dev, NULL);
+ if (IS_ERR(parent))
+ return -ENODEV;
+
+ ret = clk_set_parent(timer->fclk, parent);
+ if (ret < 0)
+ pr_err("%s: failed to set parent\n", __func__);
+
+ clk_put(parent);
+
+ return ret;
+}
+
+static int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)
+{
+ int ret;
+ const char *parent_name;
+ struct clk *parent;
+ struct dmtimer_platform_data *pdata;
+
+ if (unlikely(!timer) || IS_ERR(timer->fclk))
+ return -EINVAL;
+
+ switch (source) {
+ case OMAP_TIMER_SRC_SYS_CLK:
+ parent_name = "timer_sys_ck";
+ break;
+ case OMAP_TIMER_SRC_32_KHZ:
+ parent_name = "timer_32k_ck";
+ break;
+ case OMAP_TIMER_SRC_EXT_CLK:
+ parent_name = "timer_ext_ck";
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ pdata = timer->pdev->dev.platform_data;
+
+ /*
+ * FIXME: Used for OMAP1 devices only because they do not currently
+ * use the clock framework to set the parent clock. To be removed
+ * once OMAP1 migrated to using clock framework for dmtimers
+ */
+ if (pdata && pdata->set_timer_src)
+ return pdata->set_timer_src(timer->pdev, source);
+
+#if defined(CONFIG_COMMON_CLK)
+ /* Check if the clock has configurable parents */
+ if (clk_hw_get_num_parents(__clk_get_hw(timer->fclk)) < 2)
+ return 0;
+#endif
+
+ parent = clk_get(&timer->pdev->dev, parent_name);
+ if (IS_ERR(parent)) {
+ pr_err("%s: %s not found\n", __func__, parent_name);
+ return -EINVAL;
+ }
+
+ ret = clk_set_parent(timer->fclk, parent);
+ if (ret < 0)
+ pr_err("%s: failed to set %s as parent\n", __func__,
+ parent_name);
+
+ clk_put(parent);
+
+ return ret;
+}
+
+static void omap_dm_timer_enable(struct omap_dm_timer *timer)
+{
+ int c;
+
+ pm_runtime_get_sync(&timer->pdev->dev);
+
+ if (!(timer->capability & OMAP_TIMER_ALWON)) {
+ if (timer->get_context_loss_count) {
+ c = timer->get_context_loss_count(&timer->pdev->dev);
+ if (c != timer->ctx_loss_count) {
+ omap_timer_restore_context(timer);
+ timer->ctx_loss_count = c;
+ }
+ } else {
+ omap_timer_restore_context(timer);
+ }
+ }
+}
+
+static void omap_dm_timer_disable(struct omap_dm_timer *timer)
+{
+ pm_runtime_put_sync(&timer->pdev->dev);
+}
+
+static int omap_dm_timer_prepare(struct omap_dm_timer *timer)
+{
+ int rc;
+
+ /*
+ * FIXME: OMAP1 devices do not use the clock framework for dmtimers so
+ * do not call clk_get() for these devices.
+ */
+ if (!(timer->capability & OMAP_TIMER_NEEDS_RESET)) {
+ timer->fclk = clk_get(&timer->pdev->dev, "fck");
+ if (WARN_ON_ONCE(IS_ERR(timer->fclk))) {
+ dev_err(&timer->pdev->dev, ": No fclk handle.\n");
+ return -EINVAL;
+ }
+ }
+
+ omap_dm_timer_enable(timer);
+
+ if (timer->capability & OMAP_TIMER_NEEDS_RESET) {
+ rc = omap_dm_timer_reset(timer);
+ if (rc) {
+ omap_dm_timer_disable(timer);
+ return rc;
+ }
+ }
+
+ __omap_dm_timer_enable_posted(timer);
+ omap_dm_timer_disable(timer);
+
+ rc = omap_dm_timer_of_set_source(timer);
+ if (rc == -ENODEV)
+ return omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ);
+
+ return rc;
+}
+
+static inline u32 omap_dm_timer_reserved_systimer(int id)
+{
+ return (omap_reserved_systimers & (1 << (id - 1))) ? 1 : 0;
+}
+
+int omap_dm_timer_reserve_systimer(int id)
+{
+ if (omap_dm_timer_reserved_systimer(id))
+ return -ENODEV;
+
+ omap_reserved_systimers |= (1 << (id - 1));
+
+ return 0;
+}
+
+static struct omap_dm_timer *_omap_dm_timer_request(int req_type, void *data)
+{
+ struct omap_dm_timer *timer = NULL, *t;
+ struct device_node *np = NULL;
+ unsigned long flags;
+ u32 cap = 0;
+ int id = 0;
+
+ switch (req_type) {
+ case REQUEST_BY_ID:
+ id = *(int *)data;
+ break;
+ case REQUEST_BY_CAP:
+ cap = *(u32 *)data;
+ break;
+ case REQUEST_BY_NODE:
+ np = (struct device_node *)data;
+ break;
+ default:
+ /* REQUEST_ANY */
+ break;
+ }
+
+ spin_lock_irqsave(&dm_timer_lock, flags);
+ list_for_each_entry(t, &omap_timer_list, node) {
+ if (t->reserved)
+ continue;
+
+ switch (req_type) {
+ case REQUEST_BY_ID:
+ if (id == t->pdev->id) {
+ timer = t;
+ timer->reserved = 1;
+ goto found;
+ }
+ break;
+ case REQUEST_BY_CAP:
+ if (cap == (t->capability & cap)) {
+ /*
+ * If timer is not NULL, we have already found
+ * one timer. But it was not an exact match
+ * because it had more capabilities than what
+ * was required. Therefore, unreserve the last
+ * timer found and see if this one is a better
+ * match.
+ */
+ if (timer)
+ timer->reserved = 0;
+ timer = t;
+ timer->reserved = 1;
+
+ /* Exit loop early if we find an exact match */
+ if (t->capability == cap)
+ goto found;
+ }
+ break;
+ case REQUEST_BY_NODE:
+ if (np == t->pdev->dev.of_node) {
+ timer = t;
+ timer->reserved = 1;
+ goto found;
+ }
+ break;
+ default:
+ /* REQUEST_ANY */
+ timer = t;
+ timer->reserved = 1;
+ goto found;
+ }
+ }
+found:
+ spin_unlock_irqrestore(&dm_timer_lock, flags);
+
+ if (timer && omap_dm_timer_prepare(timer)) {
+ timer->reserved = 0;
+ timer = NULL;
+ }
+
+ if (!timer)
+ pr_debug("%s: timer request failed!\n", __func__);
+
+ return timer;
+}
+
+static struct omap_dm_timer *omap_dm_timer_request(void)
+{
+ return _omap_dm_timer_request(REQUEST_ANY, NULL);
+}
+
+static struct omap_dm_timer *omap_dm_timer_request_specific(int id)
+{
+ /* Requesting timer by ID is not supported when device tree is used */
+ if (of_have_populated_dt()) {
+ pr_warn("%s: Please use omap_dm_timer_request_by_node()\n",
+ __func__);
+ return NULL;
+ }
+
+ return _omap_dm_timer_request(REQUEST_BY_ID, &id);
+}
+
+/**
+ * omap_dm_timer_request_by_cap - Request a timer by capability
+ * @cap: Bit mask of capabilities to match
+ *
+ * Find a timer based upon capabilities bit mask. Callers of this function
+ * should use the definitions found in the plat/dmtimer.h file under the
+ * comment "timer capabilities used in hwmod database". Returns pointer to
+ * timer handle on success and a NULL pointer on failure.
+ */
+struct omap_dm_timer *omap_dm_timer_request_by_cap(u32 cap)
+{
+ return _omap_dm_timer_request(REQUEST_BY_CAP, &cap);
+}
+
+/**
+ * omap_dm_timer_request_by_node - Request a timer by device-tree node
+ * @np: Pointer to device-tree timer node
+ *
+ * Request a timer based upon a device node pointer. Returns pointer to
+ * timer handle on success and a NULL pointer on failure.
+ */
+static struct omap_dm_timer *omap_dm_timer_request_by_node(struct device_node *np)
+{
+ if (!np)
+ return NULL;
+
+ return _omap_dm_timer_request(REQUEST_BY_NODE, np);
+}
+
+static int omap_dm_timer_free(struct omap_dm_timer *timer)
+{
+ if (unlikely(!timer))
+ return -EINVAL;
+
+ clk_put(timer->fclk);
+
+ WARN_ON(!timer->reserved);
+ timer->reserved = 0;
+ return 0;
+}
+
+int omap_dm_timer_get_irq(struct omap_dm_timer *timer)
+{
+ if (timer)
+ return timer->irq;
+ return -EINVAL;
+}
+
+#if defined(CONFIG_ARCH_OMAP1)
+#include <mach/hardware.h>
+
+static struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer)
+{
+ return NULL;
+}
+
+/**
+ * omap_dm_timer_modify_idlect_mask - Check if any running timers use ARMXOR
+ * @inputmask: current value of idlect mask
+ */
+__u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask)
+{
+ int i = 0;
+ struct omap_dm_timer *timer = NULL;
+ unsigned long flags;
+
+ /* If ARMXOR cannot be idled this function call is unnecessary */
+ if (!(inputmask & (1 << 1)))
+ return inputmask;
+
+ /* If any active timer is using ARMXOR return modified mask */
+ spin_lock_irqsave(&dm_timer_lock, flags);
+ list_for_each_entry(timer, &omap_timer_list, node) {
+ u32 l;
+
+ l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
+ if (l & OMAP_TIMER_CTRL_ST) {
+ if (((omap_readl(MOD_CONF_CTRL_1) >> (i * 2)) & 0x03) == 0)
+ inputmask &= ~(1 << 1);
+ else
+ inputmask &= ~(1 << 2);
+ }
+ i++;
+ }
+ spin_unlock_irqrestore(&dm_timer_lock, flags);
+
+ return inputmask;
+}
+
+#else
+
+static struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer)
+{
+ if (timer && !IS_ERR(timer->fclk))
+ return timer->fclk;
+ return NULL;
+}
+
+__u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask)
+{
+ BUG();
+
+ return 0;
+}
+
+#endif
+
+int omap_dm_timer_trigger(struct omap_dm_timer *timer)
+{
+ if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) {
+ pr_err("%s: timer not available or enabled.\n", __func__);
+ return -EINVAL;
+ }
+
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0);
+ return 0;
+}
+
+static int omap_dm_timer_start(struct omap_dm_timer *timer)
+{
+ u32 l;
+
+ if (unlikely(!timer))
+ return -EINVAL;
+
+ omap_dm_timer_enable(timer);
+
+ l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
+ if (!(l & OMAP_TIMER_CTRL_ST)) {
+ l |= OMAP_TIMER_CTRL_ST;
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
+ }
+
+ /* Save the context */
+ timer->context.tclr = l;
+ return 0;
+}
+
+static int omap_dm_timer_stop(struct omap_dm_timer *timer)
+{
+ unsigned long rate = 0;
+
+ if (unlikely(!timer))
+ return -EINVAL;
+
+ if (!(timer->capability & OMAP_TIMER_NEEDS_RESET))
+ rate = clk_get_rate(timer->fclk);
+
+ __omap_dm_timer_stop(timer, timer->posted, rate);
+
+ /*
+ * Since the register values are computed and written within
+ * __omap_dm_timer_stop, we need to use read to retrieve the
+ * context.
+ */
+ timer->context.tclr =
+ omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
+ omap_dm_timer_disable(timer);
+ return 0;
+}
+
+static int omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload,
+ unsigned int load)
+{
+ u32 l;
+
+ if (unlikely(!timer))
+ return -EINVAL;
+
+ omap_dm_timer_enable(timer);
+ l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
+ if (autoreload)
+ l |= OMAP_TIMER_CTRL_AR;
+ else
+ l &= ~OMAP_TIMER_CTRL_AR;
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load);
+
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0);
+ /* Save the context */
+ timer->context.tclr = l;
+ timer->context.tldr = load;
+ omap_dm_timer_disable(timer);
+ return 0;
+}
+
+/* Optimized set_load which removes costly spin wait in timer_start */
+int omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload,
+ unsigned int load)
+{
+ u32 l;
+
+ if (unlikely(!timer))
+ return -EINVAL;
+
+ omap_dm_timer_enable(timer);
+
+ l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
+ if (autoreload) {
+ l |= OMAP_TIMER_CTRL_AR;
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load);
+ } else {
+ l &= ~OMAP_TIMER_CTRL_AR;
+ }
+ l |= OMAP_TIMER_CTRL_ST;
+
+ __omap_dm_timer_load_start(timer, l, load, timer->posted);
+
+ /* Save the context */
+ timer->context.tclr = l;
+ timer->context.tldr = load;
+ timer->context.tcrr = load;
+ return 0;
+}
+static int omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable,
+ unsigned int match)
+{
+ u32 l;
+
+ if (unlikely(!timer))
+ return -EINVAL;
+
+ omap_dm_timer_enable(timer);
+ l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
+ if (enable)
+ l |= OMAP_TIMER_CTRL_CE;
+ else
+ l &= ~OMAP_TIMER_CTRL_CE;
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, match);
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
+
+ /* Save the context */
+ timer->context.tclr = l;
+ timer->context.tmar = match;
+ omap_dm_timer_disable(timer);
+ return 0;
+}
+
+static int omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on,
+ int toggle, int trigger)
+{
+ u32 l;
+
+ if (unlikely(!timer))
+ return -EINVAL;
+
+ omap_dm_timer_enable(timer);
+ l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
+ l &= ~(OMAP_TIMER_CTRL_GPOCFG | OMAP_TIMER_CTRL_SCPWM |
+ OMAP_TIMER_CTRL_PT | (0x03 << 10));
+ if (def_on)
+ l |= OMAP_TIMER_CTRL_SCPWM;
+ if (toggle)
+ l |= OMAP_TIMER_CTRL_PT;
+ l |= trigger << 10;
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
+
+ /* Save the context */
+ timer->context.tclr = l;
+ omap_dm_timer_disable(timer);
+ return 0;
+}
+
+static int omap_dm_timer_set_prescaler(struct omap_dm_timer *timer,
+ int prescaler)
+{
+ u32 l;
+
+ if (unlikely(!timer) || prescaler < -1 || prescaler > 7)
+ return -EINVAL;
+
+ omap_dm_timer_enable(timer);
+ l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
+ l &= ~(OMAP_TIMER_CTRL_PRE | (0x07 << 2));
+ if (prescaler >= 0) {
+ l |= OMAP_TIMER_CTRL_PRE;
+ l |= prescaler << 2;
+ }
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
+
+ /* Save the context */
+ timer->context.tclr = l;
+ omap_dm_timer_disable(timer);
+ return 0;
+}
+
+static int omap_dm_timer_set_int_enable(struct omap_dm_timer *timer,
+ unsigned int value)
+{
+ if (unlikely(!timer))
+ return -EINVAL;
+
+ omap_dm_timer_enable(timer);
+ __omap_dm_timer_int_enable(timer, value);
+
+ /* Save the context */
+ timer->context.tier = value;
+ timer->context.twer = value;
+ omap_dm_timer_disable(timer);
+ return 0;
+}
+
+/**
+ * omap_dm_timer_set_int_disable - disable timer interrupts
+ * @timer: pointer to timer handle
+ * @mask: bit mask of interrupts to be disabled
+ *
+ * Disables the specified timer interrupts for a timer.
+ */
+static int omap_dm_timer_set_int_disable(struct omap_dm_timer *timer, u32 mask)
+{
+ u32 l = mask;
+
+ if (unlikely(!timer))
+ return -EINVAL;
+
+ omap_dm_timer_enable(timer);
+
+ if (timer->revision == 1)
+ l = readl_relaxed(timer->irq_ena) & ~mask;
+
+ writel_relaxed(l, timer->irq_dis);
+ l = omap_dm_timer_read_reg(timer, OMAP_TIMER_WAKEUP_EN_REG) & ~mask;
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_WAKEUP_EN_REG, l);
+
+ /* Save the context */
+ timer->context.tier &= ~mask;
+ timer->context.twer &= ~mask;
+ omap_dm_timer_disable(timer);
+ return 0;
+}
+
+static unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer)
+{
+ unsigned int l;
+
+ if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) {
+ pr_err("%s: timer not available or enabled.\n", __func__);
+ return 0;
+ }
+
+ l = readl_relaxed(timer->irq_stat);
+
+ return l;
+}
+
+static int omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value)
+{
+ if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev)))
+ return -EINVAL;
+
+ __omap_dm_timer_write_status(timer, value);
+
+ return 0;
+}
+
+static unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer)
+{
+ if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) {
+ pr_err("%s: timer not iavailable or enabled.\n", __func__);
+ return 0;
+ }
+
+ return __omap_dm_timer_read_counter(timer, timer->posted);
+}
+
+static int omap_dm_timer_write_counter(struct omap_dm_timer *timer, unsigned int value)
+{
+ if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) {
+ pr_err("%s: timer not available or enabled.\n", __func__);
+ return -EINVAL;
+ }
+
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, value);
+
+ /* Save the context */
+ timer->context.tcrr = value;
+ return 0;
+}
+
+int omap_dm_timers_active(void)
+{
+ struct omap_dm_timer *timer;
+
+ list_for_each_entry(timer, &omap_timer_list, node) {
+ if (!timer->reserved)
+ continue;
+
+ if (omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG) &
+ OMAP_TIMER_CTRL_ST) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static const struct of_device_id omap_timer_match[];
+
+/**
+ * omap_dm_timer_probe - probe function called for every registered device
+ * @pdev: pointer to current timer platform device
+ *
+ * Called by driver framework at the end of device registration for all
+ * timer devices.
+ */
+static int omap_dm_timer_probe(struct platform_device *pdev)
+{
+ unsigned long flags;
+ struct omap_dm_timer *timer;
+ struct resource *mem, *irq;
+ struct device *dev = &pdev->dev;
+ const struct dmtimer_platform_data *pdata;
+ int ret;
+
+ pdata = of_device_get_match_data(dev);
+ if (!pdata)
+ pdata = dev_get_platdata(dev);
+ else
+ dev->platform_data = (void *)pdata;
+
+ if (!pdata) {
+ dev_err(dev, "%s: no platform data.\n", __func__);
+ return -ENODEV;
+ }
+
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (unlikely(!irq)) {
+ dev_err(dev, "%s: no IRQ resource.\n", __func__);
+ return -ENODEV;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (unlikely(!mem)) {
+ dev_err(dev, "%s: no memory resource.\n", __func__);
+ return -ENODEV;
+ }
+
+ timer = devm_kzalloc(dev, sizeof(*timer), GFP_KERNEL);
+ if (!timer)
+ return -ENOMEM;
+
+ timer->fclk = ERR_PTR(-ENODEV);
+ timer->io_base = devm_ioremap_resource(dev, mem);
+ if (IS_ERR(timer->io_base))
+ return PTR_ERR(timer->io_base);
+
+ if (dev->of_node) {
+ if (of_find_property(dev->of_node, "ti,timer-alwon", NULL))
+ timer->capability |= OMAP_TIMER_ALWON;
+ if (of_find_property(dev->of_node, "ti,timer-dsp", NULL))
+ timer->capability |= OMAP_TIMER_HAS_DSP_IRQ;
+ if (of_find_property(dev->of_node, "ti,timer-pwm", NULL))
+ timer->capability |= OMAP_TIMER_HAS_PWM;
+ if (of_find_property(dev->of_node, "ti,timer-secure", NULL))
+ timer->capability |= OMAP_TIMER_SECURE;
+ } else {
+ timer->id = pdev->id;
+ timer->capability = pdata->timer_capability;
+ timer->reserved = omap_dm_timer_reserved_systimer(timer->id);
+ timer->get_context_loss_count = pdata->get_context_loss_count;
+ }
+
+ if (pdata)
+ timer->errata = pdata->timer_errata;
+
+ timer->irq = irq->start;
+ timer->pdev = pdev;
+
+ pm_runtime_enable(dev);
+ pm_runtime_irq_safe(dev);
+
+ if (!timer->reserved) {
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ dev_err(dev, "%s: pm_runtime_get_sync failed!\n",
+ __func__);
+ goto err_get_sync;
+ }
+ __omap_dm_timer_init_regs(timer);
+ pm_runtime_put(dev);
+ }
+
+ /* add the timer element to the list */
+ spin_lock_irqsave(&dm_timer_lock, flags);
+ list_add_tail(&timer->node, &omap_timer_list);
+ spin_unlock_irqrestore(&dm_timer_lock, flags);
+
+ dev_dbg(dev, "Device Probed.\n");
+
+ return 0;
+
+err_get_sync:
+ pm_runtime_put_noidle(dev);
+ pm_runtime_disable(dev);
+ return ret;
+}
+
+/**
+ * omap_dm_timer_remove - cleanup a registered timer device
+ * @pdev: pointer to current timer platform device
+ *
+ * Called by driver framework whenever a timer device is unregistered.
+ * In addition to freeing platform resources it also deletes the timer
+ * entry from the local list.
+ */
+static int omap_dm_timer_remove(struct platform_device *pdev)
+{
+ struct omap_dm_timer *timer;
+ unsigned long flags;
+ int ret = -EINVAL;
+
+ spin_lock_irqsave(&dm_timer_lock, flags);
+ list_for_each_entry(timer, &omap_timer_list, node)
+ if (!strcmp(dev_name(&timer->pdev->dev),
+ dev_name(&pdev->dev))) {
+ list_del(&timer->node);
+ ret = 0;
+ break;
+ }
+ spin_unlock_irqrestore(&dm_timer_lock, flags);
+
+ pm_runtime_disable(&pdev->dev);
+
+ return ret;
+}
+
+const static struct omap_dm_timer_ops dmtimer_ops = {
+ .request_by_node = omap_dm_timer_request_by_node,
+ .request_specific = omap_dm_timer_request_specific,
+ .request = omap_dm_timer_request,
+ .set_source = omap_dm_timer_set_source,
+ .get_irq = omap_dm_timer_get_irq,
+ .set_int_enable = omap_dm_timer_set_int_enable,
+ .set_int_disable = omap_dm_timer_set_int_disable,
+ .free = omap_dm_timer_free,
+ .enable = omap_dm_timer_enable,
+ .disable = omap_dm_timer_disable,
+ .get_fclk = omap_dm_timer_get_fclk,
+ .start = omap_dm_timer_start,
+ .stop = omap_dm_timer_stop,
+ .set_load = omap_dm_timer_set_load,
+ .set_match = omap_dm_timer_set_match,
+ .set_pwm = omap_dm_timer_set_pwm,
+ .set_prescaler = omap_dm_timer_set_prescaler,
+ .read_counter = omap_dm_timer_read_counter,
+ .write_counter = omap_dm_timer_write_counter,
+ .read_status = omap_dm_timer_read_status,
+ .write_status = omap_dm_timer_write_status,
+};
+
+static const struct dmtimer_platform_data omap3plus_pdata = {
+ .timer_errata = OMAP_TIMER_ERRATA_I103_I767,
+ .timer_ops = &dmtimer_ops,
+};
+
+static const struct of_device_id omap_timer_match[] = {
+ {
+ .compatible = "ti,omap2420-timer",
+ },
+ {
+ .compatible = "ti,omap3430-timer",
+ .data = &omap3plus_pdata,
+ },
+ {
+ .compatible = "ti,omap4430-timer",
+ .data = &omap3plus_pdata,
+ },
+ {
+ .compatible = "ti,omap5430-timer",
+ .data = &omap3plus_pdata,
+ },
+ {
+ .compatible = "ti,am335x-timer",
+ .data = &omap3plus_pdata,
+ },
+ {
+ .compatible = "ti,am335x-timer-1ms",
+ .data = &omap3plus_pdata,
+ },
+ {
+ .compatible = "ti,dm816-timer",
+ .data = &omap3plus_pdata,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, omap_timer_match);
+
+static struct platform_driver omap_dm_timer_driver = {
+ .probe = omap_dm_timer_probe,
+ .remove = omap_dm_timer_remove,
+ .driver = {
+ .name = "omap_timer",
+ .of_match_table = of_match_ptr(omap_timer_match),
+ },
+};
+
+early_platform_init("earlytimer", &omap_dm_timer_driver);
+module_platform_driver(omap_dm_timer_driver);
+
+MODULE_DESCRIPTION("OMAP Dual-Mode Timer Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_AUTHOR("Texas Instruments Inc");
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index fb586e09682d..de55c7d57438 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -45,6 +45,7 @@ config ARM_DT_BL_CPUFREQ
config ARM_SCPI_CPUFREQ
tristate "SCPI based CPUfreq driver"
depends on ARM_SCPI_PROTOCOL && COMMON_CLK_SCPI
+ depends on !CPU_THERMAL || THERMAL
help
This adds the CPUfreq driver support for ARM platforms using SCPI
protocol for CPU power management.
@@ -70,16 +71,6 @@ config ARM_BRCMSTB_AVS_CPUFREQ
Say Y, if you have a Broadcom SoC with AVS support for DFS or DVFS.
-config ARM_BRCMSTB_AVS_CPUFREQ_DEBUG
- bool "Broadcom STB AVS CPUfreq driver sysfs debug capability"
- depends on ARM_BRCMSTB_AVS_CPUFREQ
- help
- Enabling this option turns on debug support via sysfs under
- /sys/kernel/debug/brcmstb-avs-cpufreq. It is possible to read all and
- write some AVS mailbox registers through sysfs entries.
-
- If in doubt, say N.
-
config ARM_EXYNOS5440_CPUFREQ
tristate "SAMSUNG EXYNOS5440"
depends on SOC_EXYNOS5440
@@ -238,6 +229,18 @@ config ARM_SA1100_CPUFREQ
config ARM_SA1110_CPUFREQ
bool
+config ARM_SCMI_CPUFREQ
+ tristate "SCMI based CPUfreq driver"
+ depends on ARM_SCMI_PROTOCOL || COMPILE_TEST
+ depends on !CPU_THERMAL || THERMAL
+ select PM_OPP
+ help
+ This adds the CPUfreq driver support for ARM platforms using SCMI
+ protocol for CPU power management.
+
+ This driver uses SCMI Message Protocol driver to interact with the
+ firmware providing the CPU DVFS functionality.
+
config ARM_SPEAR_CPUFREQ
bool "SPEAr CPUFreq support"
depends on PLAT_SPEAR
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index c60c1e141d9d..8d24ade3bd02 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -75,6 +75,7 @@ obj-$(CONFIG_ARM_S3C24XX_CPUFREQ_DEBUGFS) += s3c24xx-cpufreq-debugfs.o
obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o
obj-$(CONFIG_ARM_SA1100_CPUFREQ) += sa1100-cpufreq.o
obj-$(CONFIG_ARM_SA1110_CPUFREQ) += sa1110-cpufreq.o
+obj-$(CONFIG_ARM_SCMI_CPUFREQ) += scmi-cpufreq.o
obj-$(CONFIG_ARM_SCPI_CPUFREQ) += scpi-cpufreq.o
obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o
obj-$(CONFIG_ARM_STI_CPUFREQ) += sti-cpufreq.o
@@ -100,10 +101,7 @@ obj-$(CONFIG_POWERNV_CPUFREQ) += powernv-cpufreq.o
##################################################################################
# Other platform drivers
-obj-$(CONFIG_BFIN_CPU_FREQ) += blackfin-cpufreq.o
obj-$(CONFIG_BMIPS_CPUFREQ) += bmips-cpufreq.o
-obj-$(CONFIG_CRIS_MACH_ARTPEC3) += cris-artpec3-cpufreq.o
-obj-$(CONFIG_ETRAXFS) += cris-etraxfs-cpufreq.o
obj-$(CONFIG_IA64_ACPI_CPUFREQ) += ia64-acpi-cpufreq.o
obj-$(CONFIG_LOONGSON2_CPUFREQ) += loongson2_cpufreq.o
obj-$(CONFIG_LOONGSON1_CPUFREQ) += loongson1-cpufreq.o
diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c
index d0c34df0529c..9449657d72f0 100644
--- a/drivers/cpufreq/acpi-cpufreq.c
+++ b/drivers/cpufreq/acpi-cpufreq.c
@@ -794,15 +794,9 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
valid_states++;
}
freq_table[valid_states].frequency = CPUFREQ_TABLE_END;
+ policy->freq_table = freq_table;
perf->state = 0;
- result = cpufreq_table_validate_and_show(policy, freq_table);
- if (result)
- goto err_freqfree;
-
- if (perf->states[0].core_frequency * 1000 != policy->cpuinfo.max_freq)
- pr_warn(FW_WARN "P-state 0 is not max freq\n");
-
switch (perf->control_register.space_id) {
case ACPI_ADR_SPACE_SYSTEM_IO:
/*
@@ -842,8 +836,6 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
return result;
-err_freqfree:
- kfree(freq_table);
err_unreg:
acpi_processor_unregister_performance(cpu);
err_free_mask:
@@ -871,6 +863,15 @@ static int acpi_cpufreq_cpu_exit(struct cpufreq_policy *policy)
return 0;
}
+static void acpi_cpufreq_cpu_ready(struct cpufreq_policy *policy)
+{
+ struct acpi_processor_performance *perf = per_cpu_ptr(acpi_perf_data,
+ policy->cpu);
+
+ if (perf->states[0].core_frequency * 1000 != policy->cpuinfo.max_freq)
+ pr_warn(FW_WARN "P-state 0 is not max freq\n");
+}
+
static int acpi_cpufreq_resume(struct cpufreq_policy *policy)
{
struct acpi_cpufreq_data *data = policy->driver_data;
@@ -898,6 +899,7 @@ static struct cpufreq_driver acpi_cpufreq_driver = {
.bios_limit = acpi_processor_get_bios_limit,
.init = acpi_cpufreq_cpu_init,
.exit = acpi_cpufreq_cpu_exit,
+ .ready = acpi_cpufreq_cpu_ready,
.resume = acpi_cpufreq_resume,
.name = "acpi-cpufreq",
.attr = acpi_cpufreq_attr,
diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c
index c56b57dcfda5..1d7ef5fc1977 100644
--- a/drivers/cpufreq/arm_big_little.c
+++ b/drivers/cpufreq/arm_big_little.c
@@ -483,14 +483,7 @@ static int bL_cpufreq_init(struct cpufreq_policy *policy)
if (ret)
return ret;
- ret = cpufreq_table_validate_and_show(policy, freq_table[cur_cluster]);
- if (ret) {
- dev_err(cpu_dev, "CPU %d, cluster: %d invalid freq table\n",
- policy->cpu, cur_cluster);
- put_cluster_clk_and_freq_table(cpu_dev, policy->cpus);
- return ret;
- }
-
+ policy->freq_table = freq_table[cur_cluster];
policy->cpuinfo.transition_latency =
arm_bL_ops->get_transition_latency(cpu_dev);
diff --git a/drivers/cpufreq/armada-37xx-cpufreq.c b/drivers/cpufreq/armada-37xx-cpufreq.c
index c6ebc88a7d8d..72a2975499db 100644
--- a/drivers/cpufreq/armada-37xx-cpufreq.c
+++ b/drivers/cpufreq/armada-37xx-cpufreq.c
@@ -202,6 +202,7 @@ static int __init armada37xx_cpufreq_driver_init(void)
cur_frequency = clk_get_rate(clk);
if (!cur_frequency) {
dev_err(cpu_dev, "Failed to get clock rate for CPU\n");
+ clk_put(clk);
return -EINVAL;
}
@@ -210,6 +211,7 @@ static int __init armada37xx_cpufreq_driver_init(void)
return -EINVAL;
armada37xx_cpufreq_dvfs_setup(nb_pm_base, clk, dvfs->divider);
+ clk_put(clk);
for (load_lvl = ARMADA_37XX_DVFS_LOAD_0; load_lvl < LOAD_LEVEL_NR;
load_lvl++) {
diff --git a/drivers/cpufreq/blackfin-cpufreq.c b/drivers/cpufreq/blackfin-cpufreq.c
deleted file mode 100644
index 12e97d8a9db0..000000000000
--- a/drivers/cpufreq/blackfin-cpufreq.c
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Blackfin core clock scaling
- *
- * Copyright 2008-2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/init.h>
-#include <linux/clk.h>
-#include <linux/cpufreq.h>
-#include <linux/fs.h>
-#include <linux/delay.h>
-#include <asm/blackfin.h>
-#include <asm/time.h>
-#include <asm/dpmc.h>
-
-
-/* this is the table of CCLK frequencies, in Hz */
-/* .driver_data is the entry in the auxiliary dpm_state_table[] */
-static struct cpufreq_frequency_table bfin_freq_table[] = {
- {
- .frequency = CPUFREQ_TABLE_END,
- .driver_data = 0,
- },
- {
- .frequency = CPUFREQ_TABLE_END,
- .driver_data = 1,
- },
- {
- .frequency = CPUFREQ_TABLE_END,
- .driver_data = 2,
- },
- {
- .frequency = CPUFREQ_TABLE_END,
- .driver_data = 0,
- },
-};
-
-static struct bfin_dpm_state {
- unsigned int csel; /* system clock divider */
- unsigned int tscale; /* change the divider on the core timer interrupt */
-} dpm_state_table[3];
-
-#if defined(CONFIG_CYCLES_CLOCKSOURCE)
-/*
- * normalized to maximum frequency offset for CYCLES,
- * used in time-ts cycles clock source, but could be used
- * somewhere also.
- */
-unsigned long long __bfin_cycles_off;
-unsigned int __bfin_cycles_mod;
-#endif
-
-/**************************************************************************/
-static void __init bfin_init_tables(unsigned long cclk, unsigned long sclk)
-{
-
- unsigned long csel, min_cclk;
- int index;
-
- /* Anomaly 273 seems to still exist on non-BF54x w/dcache turned on */
-#if ANOMALY_05000273 || ANOMALY_05000274 || \
- (!(defined(CONFIG_BF54x) || defined(CONFIG_BF60x)) \
- && defined(CONFIG_BFIN_EXTMEM_DCACHEABLE))
- min_cclk = sclk * 2;
-#else
- min_cclk = sclk;
-#endif
-
-#ifndef CONFIG_BF60x
- csel = ((bfin_read_PLL_DIV() & CSEL) >> 4);
-#else
- csel = bfin_read32(CGU0_DIV) & 0x1F;
-#endif
-
- for (index = 0; (cclk >> index) >= min_cclk && csel <= 3 && index < 3; index++, csel++) {
- bfin_freq_table[index].frequency = cclk >> index;
-#ifndef CONFIG_BF60x
- dpm_state_table[index].csel = csel << 4; /* Shift now into PLL_DIV bitpos */
-#else
- dpm_state_table[index].csel = csel;
-#endif
- dpm_state_table[index].tscale = (TIME_SCALE >> index) - 1;
-
- pr_debug("cpufreq: freq:%d csel:0x%x tscale:%d\n",
- bfin_freq_table[index].frequency,
- dpm_state_table[index].csel,
- dpm_state_table[index].tscale);
- }
- return;
-}
-
-static void bfin_adjust_core_timer(void *info)
-{
- unsigned int tscale;
- unsigned int index = *(unsigned int *)info;
-
- /* we have to adjust the core timer, because it is using cclk */
- tscale = dpm_state_table[index].tscale;
- bfin_write_TSCALE(tscale);
- return;
-}
-
-static unsigned int bfin_getfreq_khz(unsigned int cpu)
-{
- /* Both CoreA/B have the same core clock */
- return get_cclk() / 1000;
-}
-
-#ifdef CONFIG_BF60x
-static int cpu_set_cclk(int cpu, unsigned long new)
-{
- struct clk *clk;
- int ret;
-
- clk = clk_get(NULL, "CCLK");
- if (IS_ERR(clk))
- return -ENODEV;
-
- ret = clk_set_rate(clk, new);
- clk_put(clk);
- return ret;
-}
-#endif
-
-static int bfin_target(struct cpufreq_policy *policy, unsigned int index)
-{
-#ifndef CONFIG_BF60x
- unsigned int plldiv;
-#endif
- static unsigned long lpj_ref;
- static unsigned int lpj_ref_freq;
- unsigned int old_freq, new_freq;
- int ret = 0;
-
-#if defined(CONFIG_CYCLES_CLOCKSOURCE)
- cycles_t cycles;
-#endif
-
- old_freq = bfin_getfreq_khz(0);
- new_freq = bfin_freq_table[index].frequency;
-
-#ifndef CONFIG_BF60x
- plldiv = (bfin_read_PLL_DIV() & SSEL) | dpm_state_table[index].csel;
- bfin_write_PLL_DIV(plldiv);
-#else
- ret = cpu_set_cclk(policy->cpu, new_freq * 1000);
- if (ret != 0) {
- WARN_ONCE(ret, "cpufreq set freq failed %d\n", ret);
- return ret;
- }
-#endif
- on_each_cpu(bfin_adjust_core_timer, &index, 1);
-#if defined(CONFIG_CYCLES_CLOCKSOURCE)
- cycles = get_cycles();
- SSYNC();
- cycles += 10; /* ~10 cycles we lose after get_cycles() */
- __bfin_cycles_off += (cycles << __bfin_cycles_mod) - (cycles << index);
- __bfin_cycles_mod = index;
-#endif
- if (!lpj_ref_freq) {
- lpj_ref = loops_per_jiffy;
- lpj_ref_freq = old_freq;
- }
- if (new_freq != old_freq) {
- loops_per_jiffy = cpufreq_scale(lpj_ref,
- lpj_ref_freq, new_freq);
- }
-
- return ret;
-}
-
-static int __bfin_cpu_init(struct cpufreq_policy *policy)
-{
-
- unsigned long cclk, sclk;
-
- cclk = get_cclk() / 1000;
- sclk = get_sclk() / 1000;
-
- if (policy->cpu == CPUFREQ_CPU)
- bfin_init_tables(cclk, sclk);
-
- policy->cpuinfo.transition_latency = 50000; /* 50us assumed */
-
- return cpufreq_table_validate_and_show(policy, bfin_freq_table);
-}
-
-static struct cpufreq_driver bfin_driver = {
- .verify = cpufreq_generic_frequency_table_verify,
- .target_index = bfin_target,
- .get = bfin_getfreq_khz,
- .init = __bfin_cpu_init,
- .name = "bfin cpufreq",
- .attr = cpufreq_generic_attr,
-};
-
-static int __init bfin_cpu_init(void)
-{
- return cpufreq_register_driver(&bfin_driver);
-}
-
-static void __exit bfin_cpu_exit(void)
-{
- cpufreq_unregister_driver(&bfin_driver);
-}
-
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
-MODULE_DESCRIPTION("cpufreq driver for Blackfin");
-MODULE_LICENSE("GPL");
-
-module_init(bfin_cpu_init);
-module_exit(bfin_cpu_exit);
diff --git a/drivers/cpufreq/brcmstb-avs-cpufreq.c b/drivers/cpufreq/brcmstb-avs-cpufreq.c
index 7281a2c19c36..b07559b9ed99 100644
--- a/drivers/cpufreq/brcmstb-avs-cpufreq.c
+++ b/drivers/cpufreq/brcmstb-avs-cpufreq.c
@@ -49,13 +49,6 @@
#include <linux/platform_device.h>
#include <linux/semaphore.h>
-#ifdef CONFIG_ARM_BRCMSTB_AVS_CPUFREQ_DEBUG
-#include <linux/ctype.h>
-#include <linux/debugfs.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-#endif
-
/* Max number of arguments AVS calls take */
#define AVS_MAX_CMD_ARGS 4
/*
@@ -182,88 +175,11 @@ struct private_data {
void __iomem *base;
void __iomem *avs_intr_base;
struct device *dev;
-#ifdef CONFIG_ARM_BRCMSTB_AVS_CPUFREQ_DEBUG
- struct dentry *debugfs;
-#endif
struct completion done;
struct semaphore sem;
struct pmap pmap;
};
-#ifdef CONFIG_ARM_BRCMSTB_AVS_CPUFREQ_DEBUG
-
-enum debugfs_format {
- DEBUGFS_NORMAL,
- DEBUGFS_FLOAT,
- DEBUGFS_REV,
-};
-
-struct debugfs_data {
- struct debugfs_entry *entry;
- struct private_data *priv;
-};
-
-struct debugfs_entry {
- char *name;
- u32 offset;
- fmode_t mode;
- enum debugfs_format format;
-};
-
-#define DEBUGFS_ENTRY(name, mode, format) { \
- #name, AVS_MBOX_##name, mode, format \
-}
-
-/*
- * These are used for debugfs only. Otherwise we use AVS_MBOX_PARAM() directly.
- */
-#define AVS_MBOX_PARAM1 AVS_MBOX_PARAM(0)
-#define AVS_MBOX_PARAM2 AVS_MBOX_PARAM(1)
-#define AVS_MBOX_PARAM3 AVS_MBOX_PARAM(2)
-#define AVS_MBOX_PARAM4 AVS_MBOX_PARAM(3)
-
-/*
- * This table stores the name, access permissions and offset for each hardware
- * register and is used to generate debugfs entries.
- */
-static struct debugfs_entry debugfs_entries[] = {
- DEBUGFS_ENTRY(COMMAND, S_IWUSR, DEBUGFS_NORMAL),
- DEBUGFS_ENTRY(STATUS, S_IWUSR, DEBUGFS_NORMAL),
- DEBUGFS_ENTRY(VOLTAGE0, 0, DEBUGFS_FLOAT),
- DEBUGFS_ENTRY(TEMP0, 0, DEBUGFS_FLOAT),
- DEBUGFS_ENTRY(PV0, 0, DEBUGFS_FLOAT),
- DEBUGFS_ENTRY(MV0, 0, DEBUGFS_FLOAT),
- DEBUGFS_ENTRY(PARAM1, S_IWUSR, DEBUGFS_NORMAL),
- DEBUGFS_ENTRY(PARAM2, S_IWUSR, DEBUGFS_NORMAL),
- DEBUGFS_ENTRY(PARAM3, S_IWUSR, DEBUGFS_NORMAL),
- DEBUGFS_ENTRY(PARAM4, S_IWUSR, DEBUGFS_NORMAL),
- DEBUGFS_ENTRY(REVISION, 0, DEBUGFS_REV),
- DEBUGFS_ENTRY(PSTATE, 0, DEBUGFS_NORMAL),
- DEBUGFS_ENTRY(HEARTBEAT, 0, DEBUGFS_NORMAL),
- DEBUGFS_ENTRY(MAGIC, S_IWUSR, DEBUGFS_NORMAL),
- DEBUGFS_ENTRY(SIGMA_HVT, 0, DEBUGFS_NORMAL),
- DEBUGFS_ENTRY(SIGMA_SVT, 0, DEBUGFS_NORMAL),
- DEBUGFS_ENTRY(VOLTAGE1, 0, DEBUGFS_FLOAT),
- DEBUGFS_ENTRY(TEMP1, 0, DEBUGFS_FLOAT),
- DEBUGFS_ENTRY(PV1, 0, DEBUGFS_FLOAT),
- DEBUGFS_ENTRY(MV1, 0, DEBUGFS_FLOAT),
- DEBUGFS_ENTRY(FREQUENCY, 0, DEBUGFS_NORMAL),
-};
-
-static int brcm_avs_target_index(struct cpufreq_policy *, unsigned int);
-
-static char *__strtolower(char *s)
-{
- char *p;
-
- for (p = s; *p; p++)
- *p = tolower(*p);
-
- return s;
-}
-
-#endif /* CONFIG_ARM_BRCMSTB_AVS_CPUFREQ_DEBUG */
-
static void __iomem *__map_region(const char *name)
{
struct device_node *np;
@@ -516,238 +432,6 @@ brcm_avs_get_freq_table(struct device *dev, struct private_data *priv)
return table;
}
-#ifdef CONFIG_ARM_BRCMSTB_AVS_CPUFREQ_DEBUG
-
-#define MANT(x) (unsigned int)(abs((x)) / 1000)
-#define FRAC(x) (unsigned int)(abs((x)) - abs((x)) / 1000 * 1000)
-
-static int brcm_avs_debug_show(struct seq_file *s, void *data)
-{
- struct debugfs_data *dbgfs = s->private;
- void __iomem *base;
- u32 val, offset;
-
- if (!dbgfs) {
- seq_puts(s, "No device pointer\n");
- return 0;
- }
-
- base = dbgfs->priv->base;
- offset = dbgfs->entry->offset;
- val = readl(base + offset);
- switch (dbgfs->entry->format) {
- case DEBUGFS_NORMAL:
- seq_printf(s, "%u\n", val);
- break;
- case DEBUGFS_FLOAT:
- seq_printf(s, "%d.%03d\n", MANT(val), FRAC(val));
- break;
- case DEBUGFS_REV:
- seq_printf(s, "%c.%c.%c.%c\n", (val >> 24 & 0xff),
- (val >> 16 & 0xff), (val >> 8 & 0xff),
- val & 0xff);
- break;
- }
- seq_printf(s, "0x%08x\n", val);
-
- return 0;
-}
-
-#undef MANT
-#undef FRAC
-
-static ssize_t brcm_avs_seq_write(struct file *file, const char __user *buf,
- size_t size, loff_t *ppos)
-{
- struct seq_file *s = file->private_data;
- struct debugfs_data *dbgfs = s->private;
- struct private_data *priv = dbgfs->priv;
- void __iomem *base, *avs_intr_base;
- bool use_issue_command = false;
- unsigned long val, offset;
- char str[128];
- int ret;
- char *str_ptr = str;
-
- if (size >= sizeof(str))
- return -E2BIG;
-
- memset(str, 0, sizeof(str));
- ret = copy_from_user(str, buf, size);
- if (ret)
- return ret;
-
- base = priv->base;
- avs_intr_base = priv->avs_intr_base;
- offset = dbgfs->entry->offset;
- /*
- * Special case writing to "command" entry only: if the string starts
- * with a 'c', we use the driver's __issue_avs_command() function.
- * Otherwise, we perform a raw write. This should allow testing of raw
- * access as well as using the higher level function. (Raw access
- * doesn't clear the firmware return status after issuing the command.)
- */
- if (str_ptr[0] == 'c' && offset == AVS_MBOX_COMMAND) {
- use_issue_command = true;
- str_ptr++;
- }
- if (kstrtoul(str_ptr, 0, &val) != 0)
- return -EINVAL;
-
- /*
- * Setting the P-state is a special case. We need to update the CPU
- * frequency we report.
- */
- if (val == AVS_CMD_SET_PSTATE) {
- struct cpufreq_policy *policy;
- unsigned int pstate;
-
- policy = cpufreq_cpu_get(smp_processor_id());
- /* Read back the P-state we are about to set */
- pstate = readl(base + AVS_MBOX_PARAM(0));
- if (use_issue_command) {
- ret = brcm_avs_target_index(policy, pstate);
- return ret ? ret : size;
- }
- policy->cur = policy->freq_table[pstate].frequency;
- }
-
- if (use_issue_command) {
- ret = __issue_avs_command(priv, val, false, NULL);
- } else {
- /* Locking here is not perfect, but is only for debug. */
- ret = down_interruptible(&priv->sem);
- if (ret)
- return ret;
-
- writel(val, base + offset);
- /* We have to wake up the firmware to process a command. */
- if (offset == AVS_MBOX_COMMAND)
- writel(AVS_CPU_L2_INT_MASK,
- avs_intr_base + AVS_CPU_L2_SET0);
- up(&priv->sem);
- }
-
- return ret ? ret : size;
-}
-
-static struct debugfs_entry *__find_debugfs_entry(const char *name)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(debugfs_entries); i++)
- if (strcasecmp(debugfs_entries[i].name, name) == 0)
- return &debugfs_entries[i];
-
- return NULL;
-}
-
-static int brcm_avs_debug_open(struct inode *inode, struct file *file)
-{
- struct debugfs_data *data;
- fmode_t fmode;
- int ret;
-
- /*
- * seq_open(), which is called by single_open(), clears "write" access.
- * We need write access to some files, so we preserve our access mode
- * and restore it.
- */
- fmode = file->f_mode;
- /*
- * Check access permissions even for root. We don't want to be writing
- * to read-only registers. Access for regular users has already been
- * checked by the VFS layer.
- */
- if ((fmode & FMODE_WRITER) && !(inode->i_mode & S_IWUSR))
- return -EACCES;
-
- data = kmalloc(sizeof(*data), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
- /*
- * We use the same file system operations for all our debug files. To
- * produce specific output, we look up the file name upon opening a
- * debugfs entry and map it to a memory offset. This offset is then used
- * in the generic "show" function to read a specific register.
- */
- data->entry = __find_debugfs_entry(file->f_path.dentry->d_iname);
- data->priv = inode->i_private;
-
- ret = single_open(file, brcm_avs_debug_show, data);
- if (ret)
- kfree(data);
- file->f_mode = fmode;
-
- return ret;
-}
-
-static int brcm_avs_debug_release(struct inode *inode, struct file *file)
-{
- struct seq_file *seq_priv = file->private_data;
- struct debugfs_data *data = seq_priv->private;
-
- kfree(data);
- return single_release(inode, file);
-}
-
-static const struct file_operations brcm_avs_debug_ops = {
- .open = brcm_avs_debug_open,
- .read = seq_read,
- .write = brcm_avs_seq_write,
- .llseek = seq_lseek,
- .release = brcm_avs_debug_release,
-};
-
-static void brcm_avs_cpufreq_debug_init(struct platform_device *pdev)
-{
- struct private_data *priv = platform_get_drvdata(pdev);
- struct dentry *dir;
- int i;
-
- if (!priv)
- return;
-
- dir = debugfs_create_dir(BRCM_AVS_CPUFREQ_NAME, NULL);
- if (IS_ERR_OR_NULL(dir))
- return;
- priv->debugfs = dir;
-
- for (i = 0; i < ARRAY_SIZE(debugfs_entries); i++) {
- /*
- * The DEBUGFS_ENTRY macro generates uppercase strings. We
- * convert them to lowercase before creating the debugfs
- * entries.
- */
- char *entry = __strtolower(debugfs_entries[i].name);
- fmode_t mode = debugfs_entries[i].mode;
-
- if (!debugfs_create_file(entry, S_IFREG | S_IRUGO | mode,
- dir, priv, &brcm_avs_debug_ops)) {
- priv->debugfs = NULL;
- debugfs_remove_recursive(dir);
- break;
- }
- }
-}
-
-static void brcm_avs_cpufreq_debug_exit(struct platform_device *pdev)
-{
- struct private_data *priv = platform_get_drvdata(pdev);
-
- if (priv && priv->debugfs) {
- debugfs_remove_recursive(priv->debugfs);
- priv->debugfs = NULL;
- }
-}
-
-#else
-
-static void brcm_avs_cpufreq_debug_init(struct platform_device *pdev) {}
-static void brcm_avs_cpufreq_debug_exit(struct platform_device *pdev) {}
-
-#endif /* CONFIG_ARM_BRCMSTB_AVS_CPUFREQ_DEBUG */
-
/*
* To ensure the right firmware is running we need to
* - check the MAGIC matches what we expect
@@ -902,11 +586,7 @@ static int brcm_avs_cpufreq_init(struct cpufreq_policy *policy)
return ret;
}
- ret = cpufreq_table_validate_and_show(policy, freq_table);
- if (ret) {
- dev_err(dev, "invalid frequency table: %d\n", ret);
- return ret;
- }
+ policy->freq_table = freq_table;
/* All cores share the same clock and thus the same policy. */
cpumask_setall(policy->cpus);
@@ -1020,11 +700,8 @@ static int brcm_avs_cpufreq_probe(struct platform_device *pdev)
return ret;
brcm_avs_driver.driver_data = pdev;
- ret = cpufreq_register_driver(&brcm_avs_driver);
- if (!ret)
- brcm_avs_cpufreq_debug_init(pdev);
- return ret;
+ return cpufreq_register_driver(&brcm_avs_driver);
}
static int brcm_avs_cpufreq_remove(struct platform_device *pdev)
@@ -1036,8 +713,6 @@ static int brcm_avs_cpufreq_remove(struct platform_device *pdev)
if (ret)
return ret;
- brcm_avs_cpufreq_debug_exit(pdev);
-
priv = platform_get_drvdata(pdev);
iounmap(priv->base);
iounmap(priv->avs_intr_base);
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index a1c3025f9df7..bc5fc1630876 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -20,6 +20,7 @@
#include <linux/cpu.h>
#include <linux/cpufreq.h>
#include <linux/dmi.h>
+#include <linux/time.h>
#include <linux/vmalloc.h>
#include <asm/unaligned.h>
@@ -161,12 +162,23 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
cpu->perf_caps.highest_perf;
policy->cpuinfo.max_freq = cppc_dmi_max_khz;
- policy->cpuinfo.transition_latency = cppc_get_transition_latency(cpu_num);
+ policy->transition_delay_us = cppc_get_transition_latency(cpu_num) /
+ NSEC_PER_USEC;
policy->shared_type = cpu->shared_type;
- if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY)
+ if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) {
+ int i;
+
cpumask_copy(policy->cpus, cpu->shared_cpu_map);
- else if (policy->shared_type == CPUFREQ_SHARED_TYPE_ALL) {
+
+ for_each_cpu(i, policy->cpus) {
+ if (unlikely(i == policy->cpu))
+ continue;
+
+ memcpy(&all_cpu_data[i]->perf_caps, &cpu->perf_caps,
+ sizeof(cpu->perf_caps));
+ }
+ } else if (policy->shared_type == CPUFREQ_SHARED_TYPE_ALL) {
/* Support only SW_ANY for now. */
pr_debug("Unsupported CPU co-ord type\n");
return -EFAULT;
@@ -230,8 +242,13 @@ static int __init cppc_cpufreq_init(void)
return ret;
out:
- for_each_possible_cpu(i)
- kfree(all_cpu_data[i]);
+ for_each_possible_cpu(i) {
+ cpu = all_cpu_data[i];
+ if (!cpu)
+ break;
+ free_cpumask_var(cpu->shared_cpu_map);
+ kfree(cpu);
+ }
kfree(all_cpu_data);
return -ENODEV;
diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c
index de3d104c25d7..190ea0dccb79 100644
--- a/drivers/cpufreq/cpufreq-dt.c
+++ b/drivers/cpufreq/cpufreq-dt.c
@@ -258,16 +258,10 @@ static int cpufreq_init(struct cpufreq_policy *policy)
priv->cpu_dev = cpu_dev;
policy->driver_data = priv;
policy->clk = cpu_clk;
+ policy->freq_table = freq_table;
policy->suspend_freq = dev_pm_opp_get_suspend_opp_freq(cpu_dev) / 1000;
- ret = cpufreq_table_validate_and_show(policy, freq_table);
- if (ret) {
- dev_err(cpu_dev, "%s: invalid frequency table: %d\n", __func__,
- ret);
- goto out_free_cpufreq_table;
- }
-
/* Support turbo/boost mode */
if (policy_has_boost_freq(policy)) {
/* This gets disabled by core on driver unregister */
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index de33ebf008ad..075d18f6ba7a 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -178,14 +178,7 @@ int cpufreq_generic_init(struct cpufreq_policy *policy,
struct cpufreq_frequency_table *table,
unsigned int transition_latency)
{
- int ret;
-
- ret = cpufreq_table_validate_and_show(policy, table);
- if (ret) {
- pr_err("%s: invalid frequency table: %d\n", __func__, ret);
- return ret;
- }
-
+ policy->freq_table = table;
policy->cpuinfo.transition_latency = transition_latency;
/*
@@ -1219,6 +1212,10 @@ static int cpufreq_online(unsigned int cpu)
goto out_free_policy;
}
+ ret = cpufreq_table_validate_and_sort(policy);
+ if (ret)
+ goto out_exit_policy;
+
down_write(&policy->rwsem);
if (new_policy) {
@@ -1249,7 +1246,7 @@ static int cpufreq_online(unsigned int cpu)
policy->cur = cpufreq_driver->get(policy->cpu);
if (!policy->cur) {
pr_err("%s: ->get() failed\n", __func__);
- goto out_exit_policy;
+ goto out_destroy_policy;
}
}
@@ -1296,7 +1293,7 @@ static int cpufreq_online(unsigned int cpu)
if (new_policy) {
ret = cpufreq_add_dev_interface(policy);
if (ret)
- goto out_exit_policy;
+ goto out_destroy_policy;
cpufreq_stats_create_table(policy);
@@ -1311,7 +1308,7 @@ static int cpufreq_online(unsigned int cpu)
__func__, cpu, ret);
/* cpufreq_policy_free() will notify based on this */
new_policy = false;
- goto out_exit_policy;
+ goto out_destroy_policy;
}
up_write(&policy->rwsem);
@@ -1326,15 +1323,16 @@ static int cpufreq_online(unsigned int cpu)
return 0;
-out_exit_policy:
+out_destroy_policy:
+ for_each_cpu(j, policy->real_cpus)
+ remove_cpu_dev_symlink(policy, get_cpu_device(j));
+
up_write(&policy->rwsem);
+out_exit_policy:
if (cpufreq_driver->exit)
cpufreq_driver->exit(policy);
- for_each_cpu(j, policy->real_cpus)
- remove_cpu_dev_symlink(policy, get_cpu_device(j));
-
out_free_policy:
cpufreq_policy_free(policy);
return ret;
diff --git a/drivers/cpufreq/cris-artpec3-cpufreq.c b/drivers/cpufreq/cris-artpec3-cpufreq.c
deleted file mode 100644
index 455b4fb78cba..000000000000
--- a/drivers/cpufreq/cris-artpec3-cpufreq.c
+++ /dev/null
@@ -1,93 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/cpufreq.h>
-#include <hwregs/reg_map.h>
-#include <hwregs/reg_rdwr.h>
-#include <hwregs/clkgen_defs.h>
-#include <hwregs/ddr2_defs.h>
-
-static int
-cris_sdram_freq_notifier(struct notifier_block *nb, unsigned long val,
- void *data);
-
-static struct notifier_block cris_sdram_freq_notifier_block = {
- .notifier_call = cris_sdram_freq_notifier
-};
-
-static struct cpufreq_frequency_table cris_freq_table[] = {
- {0, 0x01, 6000},
- {0, 0x02, 200000},
- {0, 0, CPUFREQ_TABLE_END},
-};
-
-static unsigned int cris_freq_get_cpu_frequency(unsigned int cpu)
-{
- reg_clkgen_rw_clk_ctrl clk_ctrl;
- clk_ctrl = REG_RD(clkgen, regi_clkgen, rw_clk_ctrl);
- return clk_ctrl.pll ? 200000 : 6000;
-}
-
-static int cris_freq_target(struct cpufreq_policy *policy, unsigned int state)
-{
- reg_clkgen_rw_clk_ctrl clk_ctrl;
- clk_ctrl = REG_RD(clkgen, regi_clkgen, rw_clk_ctrl);
-
- local_irq_disable();
-
- /* Even though we may be SMP they will share the same clock
- * so all settings are made on CPU0. */
- if (cris_freq_table[state].frequency == 200000)
- clk_ctrl.pll = 1;
- else
- clk_ctrl.pll = 0;
- REG_WR(clkgen, regi_clkgen, rw_clk_ctrl, clk_ctrl);
-
- local_irq_enable();
-
- return 0;
-}
-
-static int cris_freq_cpu_init(struct cpufreq_policy *policy)
-{
- return cpufreq_generic_init(policy, cris_freq_table, 1000000);
-}
-
-static struct cpufreq_driver cris_freq_driver = {
- .get = cris_freq_get_cpu_frequency,
- .verify = cpufreq_generic_frequency_table_verify,
- .target_index = cris_freq_target,
- .init = cris_freq_cpu_init,
- .name = "cris_freq",
- .attr = cpufreq_generic_attr,
-};
-
-static int __init cris_freq_init(void)
-{
- int ret;
- ret = cpufreq_register_driver(&cris_freq_driver);
- cpufreq_register_notifier(&cris_sdram_freq_notifier_block,
- CPUFREQ_TRANSITION_NOTIFIER);
- return ret;
-}
-
-static int
-cris_sdram_freq_notifier(struct notifier_block *nb, unsigned long val,
- void *data)
-{
- int i;
- struct cpufreq_freqs *freqs = data;
- if (val == CPUFREQ_PRECHANGE) {
- reg_ddr2_rw_cfg cfg =
- REG_RD(ddr2, regi_ddr2_ctrl, rw_cfg);
- cfg.ref_interval = (freqs->new == 200000 ? 1560 : 46);
-
- if (freqs->new == 200000)
- for (i = 0; i < 50000; i++);
- REG_WR(bif_core, regi_bif_core, rw_sdram_timing, timing);
- }
- return 0;
-}
-
-
-module_init(cris_freq_init);
diff --git a/drivers/cpufreq/cris-etraxfs-cpufreq.c b/drivers/cpufreq/cris-etraxfs-cpufreq.c
deleted file mode 100644
index 4c4b5dd685e3..000000000000
--- a/drivers/cpufreq/cris-etraxfs-cpufreq.c
+++ /dev/null
@@ -1,92 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/cpufreq.h>
-#include <hwregs/reg_map.h>
-#include <arch/hwregs/reg_rdwr.h>
-#include <arch/hwregs/config_defs.h>
-#include <arch/hwregs/bif_core_defs.h>
-
-static int
-cris_sdram_freq_notifier(struct notifier_block *nb, unsigned long val,
- void *data);
-
-static struct notifier_block cris_sdram_freq_notifier_block = {
- .notifier_call = cris_sdram_freq_notifier
-};
-
-static struct cpufreq_frequency_table cris_freq_table[] = {
- {0, 0x01, 6000},
- {0, 0x02, 200000},
- {0, 0, CPUFREQ_TABLE_END},
-};
-
-static unsigned int cris_freq_get_cpu_frequency(unsigned int cpu)
-{
- reg_config_rw_clk_ctrl clk_ctrl;
- clk_ctrl = REG_RD(config, regi_config, rw_clk_ctrl);
- return clk_ctrl.pll ? 200000 : 6000;
-}
-
-static int cris_freq_target(struct cpufreq_policy *policy, unsigned int state)
-{
- reg_config_rw_clk_ctrl clk_ctrl;
- clk_ctrl = REG_RD(config, regi_config, rw_clk_ctrl);
-
- local_irq_disable();
-
- /* Even though we may be SMP they will share the same clock
- * so all settings are made on CPU0. */
- if (cris_freq_table[state].frequency == 200000)
- clk_ctrl.pll = 1;
- else
- clk_ctrl.pll = 0;
- REG_WR(config, regi_config, rw_clk_ctrl, clk_ctrl);
-
- local_irq_enable();
-
- return 0;
-}
-
-static int cris_freq_cpu_init(struct cpufreq_policy *policy)
-{
- return cpufreq_generic_init(policy, cris_freq_table, 1000000);
-}
-
-static struct cpufreq_driver cris_freq_driver = {
- .get = cris_freq_get_cpu_frequency,
- .verify = cpufreq_generic_frequency_table_verify,
- .target_index = cris_freq_target,
- .init = cris_freq_cpu_init,
- .name = "cris_freq",
- .attr = cpufreq_generic_attr,
-};
-
-static int __init cris_freq_init(void)
-{
- int ret;
- ret = cpufreq_register_driver(&cris_freq_driver);
- cpufreq_register_notifier(&cris_sdram_freq_notifier_block,
- CPUFREQ_TRANSITION_NOTIFIER);
- return ret;
-}
-
-static int
-cris_sdram_freq_notifier(struct notifier_block *nb, unsigned long val,
- void *data)
-{
- int i;
- struct cpufreq_freqs *freqs = data;
- if (val == CPUFREQ_PRECHANGE) {
- reg_bif_core_rw_sdram_timing timing =
- REG_RD(bif_core, regi_bif_core, rw_sdram_timing);
- timing.cpd = (freqs->new == 200000 ? 0 : 1);
-
- if (freqs->new == 200000)
- for (i = 0; i < 50000; i++) ;
- REG_WR(bif_core, regi_bif_core, rw_sdram_timing, timing);
- }
- return 0;
-}
-
-module_init(cris_freq_init);
diff --git a/drivers/cpufreq/e_powersaver.c b/drivers/cpufreq/e_powersaver.c
index cdf097b29862..60bea302abbe 100644
--- a/drivers/cpufreq/e_powersaver.c
+++ b/drivers/cpufreq/e_powersaver.c
@@ -184,7 +184,6 @@ static int eps_cpu_init(struct cpufreq_policy *policy)
struct cpuinfo_x86 *c = &cpu_data(0);
struct cpufreq_frequency_table *f_table;
int k, step, voltage;
- int ret;
int states;
#if IS_ENABLED(CONFIG_ACPI_PROCESSOR)
unsigned int limit;
@@ -359,12 +358,7 @@ static int eps_cpu_init(struct cpufreq_policy *policy)
}
policy->cpuinfo.transition_latency = 140000; /* 844mV -> 700mV in ns */
-
- ret = cpufreq_table_validate_and_show(policy, &centaur->freq_table[0]);
- if (ret) {
- kfree(centaur);
- return ret;
- }
+ policy->freq_table = &centaur->freq_table[0];
return 0;
}
diff --git a/drivers/cpufreq/elanfreq.c b/drivers/cpufreq/elanfreq.c
index 45e2ca62515e..03419f064752 100644
--- a/drivers/cpufreq/elanfreq.c
+++ b/drivers/cpufreq/elanfreq.c
@@ -165,7 +165,8 @@ static int elanfreq_cpu_init(struct cpufreq_policy *policy)
if (pos->frequency > max_freq)
pos->frequency = CPUFREQ_ENTRY_INVALID;
- return cpufreq_table_validate_and_show(policy, elanfreq_table);
+ policy->freq_table = elanfreq_table;
+ return 0;
}
diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c
index 6d007f824ca7..3a8cc99e6815 100644
--- a/drivers/cpufreq/freq_table.c
+++ b/drivers/cpufreq/freq_table.c
@@ -352,19 +352,19 @@ static int set_freq_table_sorted(struct cpufreq_policy *policy)
return 0;
}
-int cpufreq_table_validate_and_show(struct cpufreq_policy *policy,
- struct cpufreq_frequency_table *table)
+int cpufreq_table_validate_and_sort(struct cpufreq_policy *policy)
{
int ret;
- ret = cpufreq_frequency_table_cpuinfo(policy, table);
+ if (!policy->freq_table)
+ return 0;
+
+ ret = cpufreq_frequency_table_cpuinfo(policy, policy->freq_table);
if (ret)
return ret;
- policy->freq_table = table;
return set_freq_table_sorted(policy);
}
-EXPORT_SYMBOL_GPL(cpufreq_table_validate_and_show);
MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
MODULE_DESCRIPTION("CPUfreq frequency table helpers");
diff --git a/drivers/cpufreq/ia64-acpi-cpufreq.c b/drivers/cpufreq/ia64-acpi-cpufreq.c
index a757c0a1e7b5..7974a2fdb760 100644
--- a/drivers/cpufreq/ia64-acpi-cpufreq.c
+++ b/drivers/cpufreq/ia64-acpi-cpufreq.c
@@ -270,10 +270,7 @@ acpi_cpufreq_cpu_init (
}
}
- result = cpufreq_table_validate_and_show(policy, freq_table);
- if (result) {
- goto err_freqfree;
- }
+ policy->freq_table = freq_table;
/* notify BIOS that we exist */
acpi_processor_notify_smm(THIS_MODULE);
@@ -296,8 +293,6 @@ acpi_cpufreq_cpu_init (
return (result);
- err_freqfree:
- kfree(freq_table);
err_unreg:
acpi_processor_unregister_performance(cpu);
err_free:
diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c
index ff67859948b3..83cf631fc9bc 100644
--- a/drivers/cpufreq/imx6q-cpufreq.c
+++ b/drivers/cpufreq/imx6q-cpufreq.c
@@ -52,6 +52,7 @@ static struct clk_bulk_data clks[] = {
static struct device *cpu_dev;
static bool free_opp;
static struct cpufreq_frequency_table *freq_table;
+static unsigned int max_freq;
static unsigned int transition_latency;
static u32 *imx6_soc_volt;
@@ -196,7 +197,7 @@ static int imx6q_cpufreq_init(struct cpufreq_policy *policy)
policy->clk = clks[ARM].clk;
ret = cpufreq_generic_init(policy, freq_table, transition_latency);
- policy->suspend_freq = policy->max;
+ policy->suspend_freq = max_freq;
return ret;
}
@@ -437,12 +438,12 @@ soc_opp_out:
* freq_table initialised from OPP is therefore sorted in the
* same order.
*/
+ max_freq = freq_table[--num].frequency;
opp = dev_pm_opp_find_freq_exact(cpu_dev,
freq_table[0].frequency * 1000, true);
min_volt = dev_pm_opp_get_voltage(opp);
dev_pm_opp_put(opp);
- opp = dev_pm_opp_find_freq_exact(cpu_dev,
- freq_table[--num].frequency * 1000, true);
+ opp = dev_pm_opp_find_freq_exact(cpu_dev, max_freq * 1000, true);
max_volt = dev_pm_opp_get_voltage(opp);
dev_pm_opp_put(opp);
diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
index 6d084c61ee25..17e566afbb41 100644
--- a/drivers/cpufreq/intel_pstate.c
+++ b/drivers/cpufreq/intel_pstate.c
@@ -26,7 +26,6 @@
#include <linux/sysfs.h>
#include <linux/types.h>
#include <linux/fs.h>
-#include <linux/debugfs.h>
#include <linux/acpi.h>
#include <linux/vmalloc.h>
#include <trace/events/power.h>
diff --git a/drivers/cpufreq/longhaul.c b/drivers/cpufreq/longhaul.c
index f730b6528c18..61a4c5b08219 100644
--- a/drivers/cpufreq/longhaul.c
+++ b/drivers/cpufreq/longhaul.c
@@ -895,8 +895,9 @@ static int longhaul_cpu_init(struct cpufreq_policy *policy)
longhaul_setup_voltagescaling();
policy->transition_delay_us = 200000; /* usec */
+ policy->freq_table = longhaul_table;
- return cpufreq_table_validate_and_show(policy, longhaul_table);
+ return 0;
}
static struct cpufreq_driver longhaul_driver = {
diff --git a/drivers/cpufreq/mediatek-cpufreq.c b/drivers/cpufreq/mediatek-cpufreq.c
index 8c04dddd3c28..eb8920d39818 100644
--- a/drivers/cpufreq/mediatek-cpufreq.c
+++ b/drivers/cpufreq/mediatek-cpufreq.c
@@ -460,21 +460,12 @@ static int mtk_cpufreq_init(struct cpufreq_policy *policy)
return ret;
}
- ret = cpufreq_table_validate_and_show(policy, freq_table);
- if (ret) {
- pr_err("%s: invalid frequency table: %d\n", __func__, ret);
- goto out_free_cpufreq_table;
- }
-
cpumask_copy(policy->cpus, &info->cpus);
+ policy->freq_table = freq_table;
policy->driver_data = info;
policy->clk = info->cpu_clk;
return 0;
-
-out_free_cpufreq_table:
- dev_pm_opp_free_cpufreq_table(info->cpu_dev, &freq_table);
- return ret;
}
static int mtk_cpufreq_exit(struct cpufreq_policy *policy)
@@ -578,7 +569,7 @@ static int __init mtk_cpufreq_driver_init(void)
match = of_match_node(mtk_cpufreq_machines, np);
of_node_put(np);
if (!match) {
- pr_warn("Machine is not compatible with mtk-cpufreq\n");
+ pr_debug("Machine is not compatible with mtk-cpufreq\n");
return -ENODEV;
}
diff --git a/drivers/cpufreq/p4-clockmod.c b/drivers/cpufreq/p4-clockmod.c
index a25741b1281b..911206243050 100644
--- a/drivers/cpufreq/p4-clockmod.c
+++ b/drivers/cpufreq/p4-clockmod.c
@@ -202,8 +202,9 @@ static int cpufreq_p4_cpu_init(struct cpufreq_policy *policy)
/* the transition latency is set to be 1 higher than the maximum
* transition latency of the ondemand governor */
policy->cpuinfo.transition_latency = 10000001;
+ policy->freq_table = &p4clockmod_table[0];
- return cpufreq_table_validate_and_show(policy, &p4clockmod_table[0]);
+ return 0;
}
diff --git a/drivers/cpufreq/powernow-k6.c b/drivers/cpufreq/powernow-k6.c
index dedd2568e852..8a8ea68611a8 100644
--- a/drivers/cpufreq/powernow-k6.c
+++ b/drivers/cpufreq/powernow-k6.c
@@ -214,8 +214,9 @@ have_busfreq:
/* cpuinfo and default policy values */
policy->cpuinfo.transition_latency = 500000;
+ policy->freq_table = clock_ratio;
- return cpufreq_table_validate_and_show(policy, clock_ratio);
+ return 0;
}
diff --git a/drivers/cpufreq/powernow-k7.c b/drivers/cpufreq/powernow-k7.c
index 302e9ce793a0..d6cb052b0a75 100644
--- a/drivers/cpufreq/powernow-k7.c
+++ b/drivers/cpufreq/powernow-k7.c
@@ -639,8 +639,9 @@ static int powernow_cpu_init(struct cpufreq_policy *policy)
policy->cpuinfo.transition_latency =
cpufreq_scale(2000000UL, fsb, latency);
+ policy->freq_table = powernow_table;
- return cpufreq_table_validate_and_show(policy, powernow_table);
+ return 0;
}
static int powernow_cpu_exit(struct cpufreq_policy *policy)
diff --git a/drivers/cpufreq/powernow-k8.c b/drivers/cpufreq/powernow-k8.c
index b01e31db5f83..fb77b39a4ce3 100644
--- a/drivers/cpufreq/powernow-k8.c
+++ b/drivers/cpufreq/powernow-k8.c
@@ -122,14 +122,12 @@ static int query_current_values_with_pending_wait(struct powernow_k8_data *data)
static void count_off_irt(struct powernow_k8_data *data)
{
udelay((1 << data->irt) * 10);
- return;
}
/* the voltage stabilization time */
static void count_off_vst(struct powernow_k8_data *data)
{
udelay(data->vstable * VST_UNITS_20US);
- return;
}
/* need to init the control msr to a safe value (for each cpu) */
@@ -591,10 +589,8 @@ static int fill_powernow_table(struct powernow_k8_data *data,
powernow_table = kzalloc((sizeof(*powernow_table)
* (data->numps + 1)), GFP_KERNEL);
- if (!powernow_table) {
- pr_err("powernow_table memory alloc failure\n");
+ if (!powernow_table)
return -ENOMEM;
- }
for (j = 0; j < data->numps; j++) {
int freq;
@@ -760,10 +756,8 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
/* fill in data->powernow_table */
powernow_table = kzalloc((sizeof(*powernow_table)
* (data->acpi_data.state_count + 1)), GFP_KERNEL);
- if (!powernow_table) {
- pr_debug("powernow_table memory alloc failure\n");
+ if (!powernow_table)
goto err_out;
- }
/* fill in data */
data->numps = data->acpi_data.state_count;
@@ -1042,10 +1036,8 @@ static int powernowk8_cpu_init(struct cpufreq_policy *pol)
return -ENODEV;
data = kzalloc(sizeof(*data), GFP_KERNEL);
- if (!data) {
- pr_err("unable to alloc powernow_k8_data\n");
+ if (!data)
return -ENOMEM;
- }
data->cpu = pol->cpu;
@@ -1084,15 +1076,7 @@ static int powernowk8_cpu_init(struct cpufreq_policy *pol)
cpumask_copy(pol->cpus, topology_core_cpumask(pol->cpu));
data->available_cores = pol->cpus;
-
- /* min/max the cpu is capable of */
- if (cpufreq_table_validate_and_show(pol, data->powernow_table)) {
- pr_err(FW_BUG "invalid powernow_table\n");
- powernow_k8_cpu_exit_acpi(data);
- kfree(data->powernow_table);
- kfree(data);
- return -EINVAL;
- }
+ pol->freq_table = data->powernow_table;
pr_debug("cpu_init done, current fid 0x%x, vid 0x%x\n",
data->currfid, data->currvid);
diff --git a/drivers/cpufreq/powernv-cpufreq.c b/drivers/cpufreq/powernv-cpufreq.c
index 29cdec198657..54edaec1e608 100644
--- a/drivers/cpufreq/powernv-cpufreq.c
+++ b/drivers/cpufreq/powernv-cpufreq.c
@@ -679,6 +679,16 @@ void gpstate_timer_handler(struct timer_list *t)
if (!spin_trylock(&gpstates->gpstate_lock))
return;
+ /*
+ * If the timer has migrated to the different cpu then bring
+ * it back to one of the policy->cpus
+ */
+ if (!cpumask_test_cpu(raw_smp_processor_id(), policy->cpus)) {
+ gpstates->timer.expires = jiffies + msecs_to_jiffies(1);
+ add_timer_on(&gpstates->timer, cpumask_first(policy->cpus));
+ spin_unlock(&gpstates->gpstate_lock);
+ return;
+ }
/*
* If PMCR was last updated was using fast_swtich then
@@ -718,10 +728,8 @@ void gpstate_timer_handler(struct timer_list *t)
if (gpstate_idx != gpstates->last_lpstate_idx)
queue_gpstate_timer(gpstates);
+ set_pstate(&freq_data);
spin_unlock(&gpstates->gpstate_lock);
-
- /* Timer may get migrated to a different cpu on cpu hot unplug */
- smp_call_function_any(policy->cpus, set_pstate, &freq_data, 1);
}
/*
@@ -812,7 +820,7 @@ gpstates_done:
static int powernv_cpufreq_cpu_init(struct cpufreq_policy *policy)
{
- int base, i, ret;
+ int base, i;
struct kernfs_node *kn;
struct global_pstate_info *gpstates;
@@ -848,15 +856,10 @@ static int powernv_cpufreq_cpu_init(struct cpufreq_policy *policy)
gpstates->timer.expires = jiffies +
msecs_to_jiffies(GPSTATE_TIMER_INTERVAL);
spin_lock_init(&gpstates->gpstate_lock);
- ret = cpufreq_table_validate_and_show(policy, powernv_freqs);
-
- if (ret < 0) {
- kfree(policy->driver_data);
- return ret;
- }
+ policy->freq_table = powernv_freqs;
policy->fast_switch_possible = true;
- return ret;
+ return 0;
}
static int powernv_cpufreq_cpu_exit(struct cpufreq_policy *policy)
diff --git a/drivers/cpufreq/ppc_cbe_cpufreq.c b/drivers/cpufreq/ppc_cbe_cpufreq.c
index 5a4c5a639f61..41a0f0be3f9f 100644
--- a/drivers/cpufreq/ppc_cbe_cpufreq.c
+++ b/drivers/cpufreq/ppc_cbe_cpufreq.c
@@ -121,9 +121,8 @@ static int cbe_cpufreq_cpu_init(struct cpufreq_policy *policy)
cpumask_copy(policy->cpus, cpu_sibling_mask(policy->cpu));
#endif
- /* this ensures that policy->cpuinfo_min
- * and policy->cpuinfo_max are set correctly */
- return cpufreq_table_validate_and_show(policy, cbe_freqs);
+ policy->freq_table = cbe_freqs;
+ return 0;
}
static int cbe_cpufreq_target(struct cpufreq_policy *policy,
diff --git a/drivers/cpufreq/pxa2xx-cpufreq.c b/drivers/cpufreq/pxa2xx-cpufreq.c
index 06b024a3e474..46254e583982 100644
--- a/drivers/cpufreq/pxa2xx-cpufreq.c
+++ b/drivers/cpufreq/pxa2xx-cpufreq.c
@@ -292,10 +292,10 @@ static int pxa_cpufreq_init(struct cpufreq_policy *policy)
pr_info("using %s frequency table\n",
pxa255_turbo_table ? "turbo" : "run");
- cpufreq_table_validate_and_show(policy, pxa255_freq_table);
+ policy->freq_table = pxa255_freq_table;
}
else if (cpu_is_pxa27x()) {
- cpufreq_table_validate_and_show(policy, pxa27x_freq_table);
+ policy->freq_table = pxa27x_freq_table;
}
pr_info("frequency change support initialized\n");
diff --git a/drivers/cpufreq/pxa3xx-cpufreq.c b/drivers/cpufreq/pxa3xx-cpufreq.c
index a01275900389..7acc7fa4536d 100644
--- a/drivers/cpufreq/pxa3xx-cpufreq.c
+++ b/drivers/cpufreq/pxa3xx-cpufreq.c
@@ -108,7 +108,9 @@ static int setup_freqs_table(struct cpufreq_policy *policy,
pxa3xx_freqs_num = num;
pxa3xx_freqs_table = table;
- return cpufreq_table_validate_and_show(policy, table);
+ policy->freq_table = table;
+
+ return 0;
}
static void __update_core_freq(struct pxa3xx_freq_info *info)
diff --git a/drivers/cpufreq/qoriq-cpufreq.c b/drivers/cpufreq/qoriq-cpufreq.c
index 0562761a3dec..3d773f64b4df 100644
--- a/drivers/cpufreq/qoriq-cpufreq.c
+++ b/drivers/cpufreq/qoriq-cpufreq.c
@@ -165,7 +165,7 @@ static void freq_table_sort(struct cpufreq_frequency_table *freq_table,
static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy)
{
struct device_node *np;
- int i, count, ret;
+ int i, count;
u32 freq;
struct clk *clk;
const struct clk_hw *hwclk;
@@ -192,16 +192,12 @@ static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy)
count = clk_hw_get_num_parents(hwclk);
data->pclk = kcalloc(count, sizeof(struct clk *), GFP_KERNEL);
- if (!data->pclk) {
- pr_err("%s: no memory\n", __func__);
+ if (!data->pclk)
goto err_nomem2;
- }
table = kcalloc(count + 1, sizeof(*table), GFP_KERNEL);
- if (!table) {
- pr_err("%s: no memory\n", __func__);
+ if (!table)
goto err_pclk;
- }
for (i = 0; i < count; i++) {
clk = clk_hw_get_parent_by_index(hwclk, i)->clk;
@@ -213,14 +209,7 @@ static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy)
freq_table_redup(table, count);
freq_table_sort(table, count);
table[i].frequency = CPUFREQ_TABLE_END;
-
- /* set the min and max frequency properly */
- ret = cpufreq_table_validate_and_show(policy, table);
- if (ret) {
- pr_err("invalid frequency table: %d\n", ret);
- goto err_nomem1;
- }
-
+ policy->freq_table = table;
data->table = table;
/* update ->cpus if we have cluster, no harm if not */
@@ -236,8 +225,6 @@ static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy)
return 0;
-err_nomem1:
- kfree(table);
err_pclk:
kfree(data->pclk);
err_nomem2:
diff --git a/drivers/cpufreq/s3c24xx-cpufreq.c b/drivers/cpufreq/s3c24xx-cpufreq.c
index 6bebc1f9f55a..909bd6e27639 100644
--- a/drivers/cpufreq/s3c24xx-cpufreq.c
+++ b/drivers/cpufreq/s3c24xx-cpufreq.c
@@ -351,11 +351,8 @@ struct clk *s3c_cpufreq_clk_get(struct device *dev, const char *name)
static int s3c_cpufreq_init(struct cpufreq_policy *policy)
{
policy->clk = clk_arm;
-
policy->cpuinfo.transition_latency = cpu_cur.info->latency;
-
- if (ftab)
- return cpufreq_table_validate_and_show(policy, ftab);
+ policy->freq_table = ftab;
return 0;
}
@@ -479,10 +476,8 @@ int __init s3c_cpufreq_setboard(struct s3c_cpufreq_board *board)
* initdata. */
ours = kzalloc(sizeof(*ours), GFP_KERNEL);
- if (ours == NULL) {
- pr_err("%s: no memory\n", __func__);
+ if (!ours)
return -ENOMEM;
- }
*ours = *board;
cpu_cur.board = ours;
@@ -568,10 +563,8 @@ static int s3c_cpufreq_build_freq(void)
size++;
ftab = kzalloc(sizeof(*ftab) * size, GFP_KERNEL);
- if (!ftab) {
- pr_err("%s: no memory for tables\n", __func__);
+ if (!ftab)
return -ENOMEM;
- }
ftab_size = size;
diff --git a/drivers/cpufreq/sc520_freq.c b/drivers/cpufreq/sc520_freq.c
index 4225501a4b78..abaa75e86148 100644
--- a/drivers/cpufreq/sc520_freq.c
+++ b/drivers/cpufreq/sc520_freq.c
@@ -83,8 +83,9 @@ static int sc520_freq_cpu_init(struct cpufreq_policy *policy)
/* cpuinfo and default policy values */
policy->cpuinfo.transition_latency = 1000000; /* 1ms */
+ policy->freq_table = sc520_freq_table;
- return cpufreq_table_validate_and_show(policy, sc520_freq_table);
+ return 0;
}
diff --git a/drivers/cpufreq/scmi-cpufreq.c b/drivers/cpufreq/scmi-cpufreq.c
new file mode 100644
index 000000000000..b4dbc77459b6
--- /dev/null
+++ b/drivers/cpufreq/scmi-cpufreq.c
@@ -0,0 +1,256 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Power Interface (SCMI) based CPUFreq Interface driver
+ *
+ * Copyright (C) 2018 ARM Ltd.
+ * Sudeep Holla <sudeep.holla@arm.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/cpu.h>
+#include <linux/cpufreq.h>
+#include <linux/cpumask.h>
+#include <linux/cpu_cooling.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/pm_opp.h>
+#include <linux/slab.h>
+#include <linux/scmi_protocol.h>
+#include <linux/types.h>
+
+struct scmi_data {
+ int domain_id;
+ struct device *cpu_dev;
+ struct thermal_cooling_device *cdev;
+};
+
+static const struct scmi_handle *handle;
+
+static unsigned int scmi_cpufreq_get_rate(unsigned int cpu)
+{
+ struct cpufreq_policy *policy = cpufreq_cpu_get_raw(cpu);
+ struct scmi_perf_ops *perf_ops = handle->perf_ops;
+ struct scmi_data *priv = policy->driver_data;
+ unsigned long rate;
+ int ret;
+
+ ret = perf_ops->freq_get(handle, priv->domain_id, &rate, false);
+ if (ret)
+ return 0;
+ return rate / 1000;
+}
+
+/*
+ * perf_ops->freq_set is not a synchronous, the actual OPP change will
+ * happen asynchronously and can get notified if the events are
+ * subscribed for by the SCMI firmware
+ */
+static int
+scmi_cpufreq_set_target(struct cpufreq_policy *policy, unsigned int index)
+{
+ int ret;
+ struct scmi_data *priv = policy->driver_data;
+ struct scmi_perf_ops *perf_ops = handle->perf_ops;
+ u64 freq = policy->freq_table[index].frequency * 1000;
+
+ ret = perf_ops->freq_set(handle, priv->domain_id, freq, false);
+ if (!ret)
+ arch_set_freq_scale(policy->related_cpus, freq,
+ policy->cpuinfo.max_freq);
+ return ret;
+}
+
+static unsigned int scmi_cpufreq_fast_switch(struct cpufreq_policy *policy,
+ unsigned int target_freq)
+{
+ struct scmi_data *priv = policy->driver_data;
+ struct scmi_perf_ops *perf_ops = handle->perf_ops;
+
+ if (!perf_ops->freq_set(handle, priv->domain_id,
+ target_freq * 1000, true)) {
+ arch_set_freq_scale(policy->related_cpus, target_freq,
+ policy->cpuinfo.max_freq);
+ return target_freq;
+ }
+
+ return 0;
+}
+
+static int
+scmi_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask)
+{
+ int cpu, domain, tdomain;
+ struct device *tcpu_dev;
+
+ domain = handle->perf_ops->device_domain_id(cpu_dev);
+ if (domain < 0)
+ return domain;
+
+ for_each_possible_cpu(cpu) {
+ if (cpu == cpu_dev->id)
+ continue;
+
+ tcpu_dev = get_cpu_device(cpu);
+ if (!tcpu_dev)
+ continue;
+
+ tdomain = handle->perf_ops->device_domain_id(tcpu_dev);
+ if (tdomain == domain)
+ cpumask_set_cpu(cpu, cpumask);
+ }
+
+ return 0;
+}
+
+static int scmi_cpufreq_init(struct cpufreq_policy *policy)
+{
+ int ret;
+ unsigned int latency;
+ struct device *cpu_dev;
+ struct scmi_data *priv;
+ struct cpufreq_frequency_table *freq_table;
+
+ cpu_dev = get_cpu_device(policy->cpu);
+ if (!cpu_dev) {
+ pr_err("failed to get cpu%d device\n", policy->cpu);
+ return -ENODEV;
+ }
+
+ ret = handle->perf_ops->add_opps_to_device(handle, cpu_dev);
+ if (ret) {
+ dev_warn(cpu_dev, "failed to add opps to the device\n");
+ return ret;
+ }
+
+ ret = scmi_get_sharing_cpus(cpu_dev, policy->cpus);
+ if (ret) {
+ dev_warn(cpu_dev, "failed to get sharing cpumask\n");
+ return ret;
+ }
+
+ ret = dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus);
+ if (ret) {
+ dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = dev_pm_opp_get_opp_count(cpu_dev);
+ if (ret <= 0) {
+ dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n");
+ ret = -EPROBE_DEFER;
+ goto out_free_opp;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ ret = -ENOMEM;
+ goto out_free_opp;
+ }
+
+ ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
+ if (ret) {
+ dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
+ goto out_free_priv;
+ }
+
+ priv->cpu_dev = cpu_dev;
+ priv->domain_id = handle->perf_ops->device_domain_id(cpu_dev);
+
+ policy->driver_data = priv;
+ policy->freq_table = freq_table;
+
+ /* SCMI allows DVFS request for any domain from any CPU */
+ policy->dvfs_possible_from_any_cpu = true;
+
+ latency = handle->perf_ops->get_transition_latency(handle, cpu_dev);
+ if (!latency)
+ latency = CPUFREQ_ETERNAL;
+
+ policy->cpuinfo.transition_latency = latency;
+
+ policy->fast_switch_possible = true;
+ return 0;
+
+out_free_priv:
+ kfree(priv);
+out_free_opp:
+ dev_pm_opp_cpumask_remove_table(policy->cpus);
+
+ return ret;
+}
+
+static int scmi_cpufreq_exit(struct cpufreq_policy *policy)
+{
+ struct scmi_data *priv = policy->driver_data;
+
+ cpufreq_cooling_unregister(priv->cdev);
+ dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
+ kfree(priv);
+ dev_pm_opp_cpumask_remove_table(policy->related_cpus);
+
+ return 0;
+}
+
+static void scmi_cpufreq_ready(struct cpufreq_policy *policy)
+{
+ struct scmi_data *priv = policy->driver_data;
+
+ priv->cdev = of_cpufreq_cooling_register(policy);
+}
+
+static struct cpufreq_driver scmi_cpufreq_driver = {
+ .name = "scmi",
+ .flags = CPUFREQ_STICKY | CPUFREQ_HAVE_GOVERNOR_PER_POLICY |
+ CPUFREQ_NEED_INITIAL_FREQ_CHECK,
+ .verify = cpufreq_generic_frequency_table_verify,
+ .attr = cpufreq_generic_attr,
+ .target_index = scmi_cpufreq_set_target,
+ .fast_switch = scmi_cpufreq_fast_switch,
+ .get = scmi_cpufreq_get_rate,
+ .init = scmi_cpufreq_init,
+ .exit = scmi_cpufreq_exit,
+ .ready = scmi_cpufreq_ready,
+};
+
+static int scmi_cpufreq_probe(struct scmi_device *sdev)
+{
+ int ret;
+
+ handle = sdev->handle;
+
+ if (!handle || !handle->perf_ops)
+ return -ENODEV;
+
+ ret = cpufreq_register_driver(&scmi_cpufreq_driver);
+ if (ret) {
+ dev_err(&sdev->dev, "%s: registering cpufreq failed, err: %d\n",
+ __func__, ret);
+ }
+
+ return ret;
+}
+
+static void scmi_cpufreq_remove(struct scmi_device *sdev)
+{
+ cpufreq_unregister_driver(&scmi_cpufreq_driver);
+}
+
+static const struct scmi_device_id scmi_id_table[] = {
+ { SCMI_PROTOCOL_PERF },
+ { },
+};
+MODULE_DEVICE_TABLE(scmi, scmi_id_table);
+
+static struct scmi_driver scmi_cpufreq_drv = {
+ .name = "scmi-cpufreq",
+ .probe = scmi_cpufreq_probe,
+ .remove = scmi_cpufreq_remove,
+ .id_table = scmi_id_table,
+};
+module_scmi_driver(scmi_cpufreq_drv);
+
+MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
+MODULE_DESCRIPTION("ARM SCMI CPUFreq interface driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/cpufreq/scpi-cpufreq.c b/drivers/cpufreq/scpi-cpufreq.c
index d300a163945f..87a98ec77773 100644
--- a/drivers/cpufreq/scpi-cpufreq.c
+++ b/drivers/cpufreq/scpi-cpufreq.c
@@ -158,13 +158,7 @@ static int scpi_cpufreq_init(struct cpufreq_policy *policy)
}
policy->driver_data = priv;
-
- ret = cpufreq_table_validate_and_show(policy, freq_table);
- if (ret) {
- dev_err(cpu_dev, "%s: invalid frequency table: %d\n", __func__,
- ret);
- goto out_put_clk;
- }
+ policy->freq_table = freq_table;
/* scpi allows DVFS request for any domain from any CPU */
policy->dvfs_possible_from_any_cpu = true;
@@ -178,8 +172,6 @@ static int scpi_cpufreq_init(struct cpufreq_policy *policy)
policy->fast_switch_possible = false;
return 0;
-out_put_clk:
- clk_put(priv->clk);
out_free_cpufreq_table:
dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
out_free_priv:
diff --git a/drivers/cpufreq/sfi-cpufreq.c b/drivers/cpufreq/sfi-cpufreq.c
index 3779742f86e3..9767afe05da2 100644
--- a/drivers/cpufreq/sfi-cpufreq.c
+++ b/drivers/cpufreq/sfi-cpufreq.c
@@ -72,8 +72,9 @@ static int sfi_cpufreq_cpu_init(struct cpufreq_policy *policy)
{
policy->shared_type = CPUFREQ_SHARED_TYPE_HW;
policy->cpuinfo.transition_latency = 100000; /* 100us */
+ policy->freq_table = freq_table;
- return cpufreq_table_validate_and_show(policy, freq_table);
+ return 0;
}
static struct cpufreq_driver sfi_cpufreq_driver = {
diff --git a/drivers/cpufreq/sh-cpufreq.c b/drivers/cpufreq/sh-cpufreq.c
index 28893d435cf5..5096c0ab781b 100644
--- a/drivers/cpufreq/sh-cpufreq.c
+++ b/drivers/cpufreq/sh-cpufreq.c
@@ -122,11 +122,7 @@ static int sh_cpufreq_cpu_init(struct cpufreq_policy *policy)
freq_table = cpuclk->nr_freqs ? cpuclk->freq_table : NULL;
if (freq_table) {
- int result;
-
- result = cpufreq_table_validate_and_show(policy, freq_table);
- if (result)
- return result;
+ policy->freq_table = freq_table;
} else {
dev_notice(dev, "no frequency table found, falling back "
"to rate rounding.\n");
@@ -137,11 +133,6 @@ static int sh_cpufreq_cpu_init(struct cpufreq_policy *policy)
(clk_round_rate(cpuclk, ~0UL) + 500) / 1000;
}
- dev_info(dev, "CPU Frequencies - Minimum %u.%03u MHz, "
- "Maximum %u.%03u MHz.\n",
- policy->min / 1000, policy->min % 1000,
- policy->max / 1000, policy->max % 1000);
-
return 0;
}
@@ -155,6 +146,16 @@ static int sh_cpufreq_cpu_exit(struct cpufreq_policy *policy)
return 0;
}
+static void sh_cpufreq_cpu_ready(struct cpufreq_policy *policy)
+{
+ struct device *dev = get_cpu_device(policy->cpu);
+
+ dev_info(dev, "CPU Frequencies - Minimum %u.%03u MHz, "
+ "Maximum %u.%03u MHz.\n",
+ policy->min / 1000, policy->min % 1000,
+ policy->max / 1000, policy->max % 1000);
+}
+
static struct cpufreq_driver sh_cpufreq_driver = {
.name = "sh",
.flags = CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING,
@@ -163,6 +164,7 @@ static struct cpufreq_driver sh_cpufreq_driver = {
.verify = sh_cpufreq_verify,
.init = sh_cpufreq_cpu_init,
.exit = sh_cpufreq_cpu_exit,
+ .ready = sh_cpufreq_cpu_ready,
.attr = cpufreq_generic_attr,
};
diff --git a/drivers/cpufreq/sparc-us2e-cpufreq.c b/drivers/cpufreq/sparc-us2e-cpufreq.c
index 90f33efee5fc..bbd1d1e166b8 100644
--- a/drivers/cpufreq/sparc-us2e-cpufreq.c
+++ b/drivers/cpufreq/sparc-us2e-cpufreq.c
@@ -292,8 +292,9 @@ static int __init us2e_freq_cpu_init(struct cpufreq_policy *policy)
policy->cpuinfo.transition_latency = 0;
policy->cur = clock_tick;
+ policy->freq_table = table;
- return cpufreq_table_validate_and_show(policy, table);
+ return 0;
}
static int us2e_freq_cpu_exit(struct cpufreq_policy *policy)
diff --git a/drivers/cpufreq/sparc-us3-cpufreq.c b/drivers/cpufreq/sparc-us3-cpufreq.c
index 30645b0118f9..51e3c6018e74 100644
--- a/drivers/cpufreq/sparc-us3-cpufreq.c
+++ b/drivers/cpufreq/sparc-us3-cpufreq.c
@@ -136,8 +136,9 @@ static int __init us3_freq_cpu_init(struct cpufreq_policy *policy)
policy->cpuinfo.transition_latency = 0;
policy->cur = clock_tick;
+ policy->freq_table = table;
- return cpufreq_table_validate_and_show(policy, table);
+ return 0;
}
static int us3_freq_cpu_exit(struct cpufreq_policy *policy)
diff --git a/drivers/cpufreq/speedstep-centrino.c b/drivers/cpufreq/speedstep-centrino.c
index 4fa5adf16c70..a1fb735685db 100644
--- a/drivers/cpufreq/speedstep-centrino.c
+++ b/drivers/cpufreq/speedstep-centrino.c
@@ -394,9 +394,9 @@ static int centrino_cpu_init(struct cpufreq_policy *policy)
policy->cpuinfo.transition_latency = 10000;
/* 10uS transition latency */
+ policy->freq_table = per_cpu(centrino_model, policy->cpu)->op_points;
- return cpufreq_table_validate_and_show(policy,
- per_cpu(centrino_model, policy->cpu)->op_points);
+ return 0;
}
static int centrino_cpu_exit(struct cpufreq_policy *policy)
diff --git a/drivers/cpufreq/speedstep-ich.c b/drivers/cpufreq/speedstep-ich.c
index 0412a246a785..fbbcb88db061 100644
--- a/drivers/cpufreq/speedstep-ich.c
+++ b/drivers/cpufreq/speedstep-ich.c
@@ -304,7 +304,9 @@ static int speedstep_cpu_init(struct cpufreq_policy *policy)
if (gf.ret)
return gf.ret;
- return cpufreq_table_validate_and_show(policy, speedstep_freqs);
+ policy->freq_table = speedstep_freqs;
+
+ return 0;
}
diff --git a/drivers/cpufreq/speedstep-smi.c b/drivers/cpufreq/speedstep-smi.c
index d23f24ccff38..01fe70dae20b 100644
--- a/drivers/cpufreq/speedstep-smi.c
+++ b/drivers/cpufreq/speedstep-smi.c
@@ -266,7 +266,9 @@ static int speedstep_cpu_init(struct cpufreq_policy *policy)
pr_debug("workaround worked.\n");
}
- return cpufreq_table_validate_and_show(policy, speedstep_freqs);
+ policy->freq_table = speedstep_freqs;
+
+ return 0;
}
static unsigned int speedstep_get(unsigned int cpu)
diff --git a/drivers/cpufreq/tegra186-cpufreq.c b/drivers/cpufreq/tegra186-cpufreq.c
index fe7875311d62..1f59966573aa 100644
--- a/drivers/cpufreq/tegra186-cpufreq.c
+++ b/drivers/cpufreq/tegra186-cpufreq.c
@@ -78,7 +78,8 @@ static int tegra186_cpufreq_init(struct cpufreq_policy *policy)
policy->driver_data =
data->regs + info->offset + EDVD_CORE_VOLT_FREQ(core);
- cpufreq_table_validate_and_show(policy, cluster->table);
+ policy->freq_table = cluster->table;
+ break;
}
policy->cpuinfo.transition_latency = 300 * 1000;
diff --git a/drivers/cpufreq/ti-cpufreq.c b/drivers/cpufreq/ti-cpufreq.c
index a099b7bf74cd..6ba709b6f095 100644
--- a/drivers/cpufreq/ti-cpufreq.c
+++ b/drivers/cpufreq/ti-cpufreq.c
@@ -304,7 +304,7 @@ static struct platform_driver ti_cpufreq_driver = {
.name = "ti-cpufreq",
},
};
-module_platform_driver(ti_cpufreq_driver);
+builtin_platform_driver(ti_cpufreq_driver);
MODULE_DESCRIPTION("TI CPUFreq/OPP hw-supported driver");
MODULE_AUTHOR("Dave Gerlach <d-gerlach@ti.com>");
diff --git a/drivers/cpuidle/cpuidle-arm.c b/drivers/cpuidle/cpuidle-arm.c
index ddee1b601b89..e07bc7ace774 100644
--- a/drivers/cpuidle/cpuidle-arm.c
+++ b/drivers/cpuidle/cpuidle-arm.c
@@ -129,7 +129,6 @@ static int __init arm_idle_init_cpu(int cpu)
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
- pr_err("Failed to allocate cpuidle device\n");
ret = -ENOMEM;
goto out_unregister_drv;
}
diff --git a/drivers/cpuidle/cpuidle-exynos.c b/drivers/cpuidle/cpuidle-exynos.c
index 00cd129b10a4..0171a6e190d7 100644
--- a/drivers/cpuidle/cpuidle-exynos.c
+++ b/drivers/cpuidle/cpuidle-exynos.c
@@ -117,7 +117,8 @@ static int exynos_cpuidle_probe(struct platform_device *pdev)
int ret;
if (IS_ENABLED(CONFIG_SMP) &&
- of_machine_is_compatible("samsung,exynos4210")) {
+ (of_machine_is_compatible("samsung,exynos4210") ||
+ of_machine_is_compatible("samsung,exynos3250"))) {
exynos_cpuidle_pdata = pdev->dev.platform_data;
ret = cpuidle_register(&exynos_coupled_idle_driver,
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index 68a16827f45f..6df894d65d9e 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -131,6 +131,10 @@ int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
static void enter_s2idle_proper(struct cpuidle_driver *drv,
struct cpuidle_device *dev, int index)
{
+ ktime_t time_start, time_end;
+
+ time_start = ns_to_ktime(local_clock());
+
/*
* trace_suspend_resume() called by tick_freeze() for the last CPU
* executing it contains RCU usage regarded as invalid in the idle
@@ -152,6 +156,11 @@ static void enter_s2idle_proper(struct cpuidle_driver *drv,
*/
RCU_NONIDLE(tick_unfreeze());
start_critical_timings();
+
+ time_end = ns_to_ktime(local_clock());
+
+ dev->states_usage[index].s2idle_time += ktime_us_delta(time_end, time_start);
+ dev->states_usage[index].s2idle_usage++;
}
/**
@@ -263,12 +272,18 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
*
* @drv: the cpuidle driver
* @dev: the cpuidle device
+ * @stop_tick: indication on whether or not to stop the tick
*
* Returns the index of the idle state. The return value must not be negative.
+ *
+ * The memory location pointed to by @stop_tick is expected to be written the
+ * 'false' boolean value if the scheduler tick should not be stopped before
+ * entering the returned state.
*/
-int cpuidle_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
+int cpuidle_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
+ bool *stop_tick)
{
- return cpuidle_curr_governor->select(drv, dev);
+ return cpuidle_curr_governor->select(drv, dev, stop_tick);
}
/**
diff --git a/drivers/cpuidle/governors/ladder.c b/drivers/cpuidle/governors/ladder.c
index 1ad8745fd6d6..b24883f85c99 100644
--- a/drivers/cpuidle/governors/ladder.c
+++ b/drivers/cpuidle/governors/ladder.c
@@ -63,9 +63,10 @@ static inline void ladder_do_selection(struct ladder_device *ldev,
* ladder_select_state - selects the next state to enter
* @drv: cpuidle driver
* @dev: the CPU
+ * @dummy: not used
*/
static int ladder_select_state(struct cpuidle_driver *drv,
- struct cpuidle_device *dev)
+ struct cpuidle_device *dev, bool *dummy)
{
struct ladder_device *ldev = this_cpu_ptr(&ladder_devices);
struct device *device = get_cpu_device(dev->cpu);
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c
index aa390404e85f..1bfe03ceb236 100644
--- a/drivers/cpuidle/governors/menu.c
+++ b/drivers/cpuidle/governors/menu.c
@@ -123,6 +123,7 @@
struct menu_device {
int last_state_idx;
int needs_update;
+ int tick_wakeup;
unsigned int next_timer_us;
unsigned int predicted_us;
@@ -279,8 +280,10 @@ again:
* menu_select - selects the next idle state to enter
* @drv: cpuidle driver containing state data
* @dev: the CPU
+ * @stop_tick: indication on whether or not to stop the tick
*/
-static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
+static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
+ bool *stop_tick)
{
struct menu_device *data = this_cpu_ptr(&menu_devices);
struct device *device = get_cpu_device(dev->cpu);
@@ -292,6 +295,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
unsigned int expected_interval;
unsigned long nr_iowaiters, cpu_load;
int resume_latency = dev_pm_qos_raw_read_value(device);
+ ktime_t delta_next;
if (data->needs_update) {
menu_update(drv, dev);
@@ -303,11 +307,13 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
latency_req = resume_latency;
/* Special case when user has set very strict latency requirement */
- if (unlikely(latency_req == 0))
+ if (unlikely(latency_req == 0)) {
+ *stop_tick = false;
return 0;
+ }
/* determine the expected residency time, round up */
- data->next_timer_us = ktime_to_us(tick_nohz_get_sleep_length());
+ data->next_timer_us = ktime_to_us(tick_nohz_get_sleep_length(&delta_next));
get_iowait_load(&nr_iowaiters, &cpu_load);
data->bucket = which_bucket(data->next_timer_us, nr_iowaiters);
@@ -346,14 +352,30 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
*/
data->predicted_us = min(data->predicted_us, expected_interval);
- /*
- * Use the performance multiplier and the user-configurable
- * latency_req to determine the maximum exit latency.
- */
- interactivity_req = data->predicted_us / performance_multiplier(nr_iowaiters, cpu_load);
- if (latency_req > interactivity_req)
- latency_req = interactivity_req;
+ if (tick_nohz_tick_stopped()) {
+ /*
+ * If the tick is already stopped, the cost of possible short
+ * idle duration misprediction is much higher, because the CPU
+ * may be stuck in a shallow idle state for a long time as a
+ * result of it. In that case say we might mispredict and try
+ * to force the CPU into a state for which we would have stopped
+ * the tick, unless a timer is going to expire really soon
+ * anyway.
+ */
+ if (data->predicted_us < TICK_USEC)
+ data->predicted_us = min_t(unsigned int, TICK_USEC,
+ ktime_to_us(delta_next));
+ } else {
+ /*
+ * Use the performance multiplier and the user-configurable
+ * latency_req to determine the maximum exit latency.
+ */
+ interactivity_req = data->predicted_us / performance_multiplier(nr_iowaiters, cpu_load);
+ if (latency_req > interactivity_req)
+ latency_req = interactivity_req;
+ }
+ expected_interval = data->predicted_us;
/*
* Find the idle state with the lowest power while satisfying
* our constraints.
@@ -369,15 +391,52 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
idx = i; /* first enabled state */
if (s->target_residency > data->predicted_us)
break;
- if (s->exit_latency > latency_req)
+ if (s->exit_latency > latency_req) {
+ /*
+ * If we break out of the loop for latency reasons, use
+ * the target residency of the selected state as the
+ * expected idle duration so that the tick is retained
+ * as long as that target residency is low enough.
+ */
+ expected_interval = drv->states[idx].target_residency;
break;
-
+ }
idx = i;
}
if (idx == -1)
idx = 0; /* No states enabled. Must use 0. */
+ /*
+ * Don't stop the tick if the selected state is a polling one or if the
+ * expected idle duration is shorter than the tick period length.
+ */
+ if ((drv->states[idx].flags & CPUIDLE_FLAG_POLLING) ||
+ expected_interval < TICK_USEC) {
+ unsigned int delta_next_us = ktime_to_us(delta_next);
+
+ *stop_tick = false;
+
+ if (!tick_nohz_tick_stopped() && idx > 0 &&
+ drv->states[idx].target_residency > delta_next_us) {
+ /*
+ * The tick is not going to be stopped and the target
+ * residency of the state to be returned is not within
+ * the time until the next timer event including the
+ * tick, so try to correct that.
+ */
+ for (i = idx - 1; i >= 0; i--) {
+ if (drv->states[i].disabled ||
+ dev->states_usage[i].disable)
+ continue;
+
+ idx = i;
+ if (drv->states[i].target_residency <= delta_next_us)
+ break;
+ }
+ }
+ }
+
data->last_state_idx = idx;
return data->last_state_idx;
@@ -397,6 +456,7 @@ static void menu_reflect(struct cpuidle_device *dev, int index)
data->last_state_idx = index;
data->needs_update = 1;
+ data->tick_wakeup = tick_nohz_idle_got_tick();
}
/**
@@ -427,14 +487,27 @@ static void menu_update(struct cpuidle_driver *drv, struct cpuidle_device *dev)
* assume the state was never reached and the exit latency is 0.
*/
- /* measured value */
- measured_us = cpuidle_get_last_residency(dev);
-
- /* Deduct exit latency */
- if (measured_us > 2 * target->exit_latency)
- measured_us -= target->exit_latency;
- else
- measured_us /= 2;
+ if (data->tick_wakeup && data->next_timer_us > TICK_USEC) {
+ /*
+ * The nohz code said that there wouldn't be any events within
+ * the tick boundary (if the tick was stopped), but the idle
+ * duration predictor had a differing opinion. Since the CPU
+ * was woken up by a tick (that wasn't stopped after all), the
+ * predictor was not quite right, so assume that the CPU could
+ * have been idle long (but not forever) to help the idle
+ * duration predictor do a better job next time.
+ */
+ measured_us = 9 * MAX_INTERESTING / 10;
+ } else {
+ /* measured value */
+ measured_us = cpuidle_get_last_residency(dev);
+
+ /* Deduct exit latency */
+ if (measured_us > 2 * target->exit_latency)
+ measured_us -= target->exit_latency;
+ else
+ measured_us /= 2;
+ }
/* Make sure our coefficients do not exceed unity */
if (measured_us > data->next_timer_us)
diff --git a/drivers/cpuidle/poll_state.c b/drivers/cpuidle/poll_state.c
index 7416b16287de..3f86d23c592e 100644
--- a/drivers/cpuidle/poll_state.c
+++ b/drivers/cpuidle/poll_state.c
@@ -6,15 +6,30 @@
#include <linux/cpuidle.h>
#include <linux/sched.h>
+#include <linux/sched/clock.h>
#include <linux/sched/idle.h>
+#define POLL_IDLE_TIME_LIMIT (TICK_NSEC / 16)
+#define POLL_IDLE_RELAX_COUNT 200
+
static int __cpuidle poll_idle(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int index)
{
+ u64 time_start = local_clock();
+
local_irq_enable();
if (!current_set_polling_and_test()) {
- while (!need_resched())
+ unsigned int loop_count = 0;
+
+ while (!need_resched()) {
cpu_relax();
+ if (loop_count++ < POLL_IDLE_RELAX_COUNT)
+ continue;
+
+ loop_count = 0;
+ if (local_clock() - time_start > POLL_IDLE_TIME_LIMIT)
+ break;
+ }
}
current_clr_polling();
diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c
index ae948b1da93a..e754c7aae7f7 100644
--- a/drivers/cpuidle/sysfs.c
+++ b/drivers/cpuidle/sysfs.c
@@ -330,6 +330,58 @@ struct cpuidle_state_kobj {
struct kobject kobj;
};
+#ifdef CONFIG_SUSPEND
+#define define_show_state_s2idle_ull_function(_name) \
+static ssize_t show_state_s2idle_##_name(struct cpuidle_state *state, \
+ struct cpuidle_state_usage *state_usage, \
+ char *buf) \
+{ \
+ return sprintf(buf, "%llu\n", state_usage->s2idle_##_name);\
+}
+
+define_show_state_s2idle_ull_function(usage);
+define_show_state_s2idle_ull_function(time);
+
+#define define_one_state_s2idle_ro(_name, show) \
+static struct cpuidle_state_attr attr_s2idle_##_name = \
+ __ATTR(_name, 0444, show, NULL)
+
+define_one_state_s2idle_ro(usage, show_state_s2idle_usage);
+define_one_state_s2idle_ro(time, show_state_s2idle_time);
+
+static struct attribute *cpuidle_state_s2idle_attrs[] = {
+ &attr_s2idle_usage.attr,
+ &attr_s2idle_time.attr,
+ NULL
+};
+
+static const struct attribute_group cpuidle_state_s2idle_group = {
+ .name = "s2idle",
+ .attrs = cpuidle_state_s2idle_attrs,
+};
+
+static void cpuidle_add_s2idle_attr_group(struct cpuidle_state_kobj *kobj)
+{
+ int ret;
+
+ if (!kobj->state->enter_s2idle)
+ return;
+
+ ret = sysfs_create_group(&kobj->kobj, &cpuidle_state_s2idle_group);
+ if (ret)
+ pr_debug("%s: sysfs attribute group not created\n", __func__);
+}
+
+static void cpuidle_remove_s2idle_attr_group(struct cpuidle_state_kobj *kobj)
+{
+ if (kobj->state->enter_s2idle)
+ sysfs_remove_group(&kobj->kobj, &cpuidle_state_s2idle_group);
+}
+#else
+static inline void cpuidle_add_s2idle_attr_group(struct cpuidle_state_kobj *kobj) { }
+static inline void cpuidle_remove_s2idle_attr_group(struct cpuidle_state_kobj *kobj) { }
+#endif /* CONFIG_SUSPEND */
+
#define kobj_to_state_obj(k) container_of(k, struct cpuidle_state_kobj, kobj)
#define kobj_to_state(k) (kobj_to_state_obj(k)->state)
#define kobj_to_state_usage(k) (kobj_to_state_obj(k)->state_usage)
@@ -383,6 +435,7 @@ static struct kobj_type ktype_state_cpuidle = {
static inline void cpuidle_free_state_kobj(struct cpuidle_device *device, int i)
{
+ cpuidle_remove_s2idle_attr_group(device->kobjs[i]);
kobject_put(&device->kobjs[i]->kobj);
wait_for_completion(&device->kobjs[i]->kobj_unregister);
kfree(device->kobjs[i]);
@@ -417,6 +470,7 @@ static int cpuidle_add_state_sysfs(struct cpuidle_device *device)
kfree(kobj);
goto error_state;
}
+ cpuidle_add_s2idle_attr_group(kobj);
kobject_uevent(&kobj->kobj, KOBJ_ADD);
device->kobjs[i] = kobj;
}
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index 4b741b83e23f..d1ea1a07cecb 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -464,13 +464,6 @@ if CRYPTO_DEV_UX500
source "drivers/crypto/ux500/Kconfig"
endif # if CRYPTO_DEV_UX500
-config CRYPTO_DEV_BFIN_CRC
- tristate "Support for Blackfin CRC hardware"
- depends on BF60x
- help
- Newer Blackfin processors have CRC hardware. Select this if you
- want to use the Blackfin CRC module.
-
config CRYPTO_DEV_ATMEL_AUTHENC
tristate "Support for Atmel IPSEC/SSL hw accelerator"
depends on HAS_DMA
@@ -730,4 +723,31 @@ config CRYPTO_DEV_ARTPEC6
To compile this driver as a module, choose M here.
+config CRYPTO_DEV_CCREE
+ tristate "Support for ARM TrustZone CryptoCell family of security processors"
+ depends on CRYPTO && CRYPTO_HW && OF && HAS_DMA
+ default n
+ select CRYPTO_HASH
+ select CRYPTO_BLKCIPHER
+ select CRYPTO_DES
+ select CRYPTO_AEAD
+ select CRYPTO_AUTHENC
+ select CRYPTO_SHA1
+ select CRYPTO_MD5
+ select CRYPTO_SHA256
+ select CRYPTO_SHA512
+ select CRYPTO_HMAC
+ select CRYPTO_AES
+ select CRYPTO_CBC
+ select CRYPTO_ECB
+ select CRYPTO_CTR
+ select CRYPTO_XTS
+ help
+ Say 'Y' to enable a driver for the REE interface of the Arm
+ TrustZone CryptoCell family of processors. Currently the
+ CryptoCell 712, 710 and 630 are supported.
+ Choose this if you wish to use hardware acceleration of
+ cryptographic operations on the system REE.
+ If unsure say Y.
+
endif # CRYPTO_HW
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index 2513d13ea2c4..7ae87b4f6c8d 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -3,9 +3,9 @@ obj-$(CONFIG_CRYPTO_DEV_ATMEL_AES) += atmel-aes.o
obj-$(CONFIG_CRYPTO_DEV_ATMEL_SHA) += atmel-sha.o
obj-$(CONFIG_CRYPTO_DEV_ATMEL_TDES) += atmel-tdes.o
obj-$(CONFIG_CRYPTO_DEV_ATMEL_ECC) += atmel-ecc.o
-obj-$(CONFIG_CRYPTO_DEV_BFIN_CRC) += bfin_crc.o
obj-$(CONFIG_CRYPTO_DEV_CAVIUM_ZIP) += cavium/
obj-$(CONFIG_CRYPTO_DEV_CCP) += ccp/
+obj-$(CONFIG_CRYPTO_DEV_CCREE) += ccree/
obj-$(CONFIG_CRYPTO_DEV_CHELSIO) += chelsio/
obj-$(CONFIG_CRYPTO_DEV_CPT) += cavium/cpt/
obj-$(CONFIG_CRYPTO_DEV_NITROX) += cavium/nitrox/
diff --git a/drivers/crypto/atmel-aes.c b/drivers/crypto/atmel-aes.c
index 691c6465b71e..801aeab5ab1e 100644
--- a/drivers/crypto/atmel-aes.c
+++ b/drivers/crypto/atmel-aes.c
@@ -2155,7 +2155,7 @@ static int atmel_aes_authenc_setkey(struct crypto_aead *tfm, const u8 *key,
badkey:
crypto_aead_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
- memzero_explicit(&key, sizeof(keys));
+ memzero_explicit(&keys, sizeof(keys));
return -EINVAL;
}
@@ -2602,16 +2602,13 @@ static struct crypto_platform_data *atmel_aes_of_init(struct platform_device *pd
}
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata) {
- dev_err(&pdev->dev, "could not allocate memory for pdata\n");
+ if (!pdata)
return ERR_PTR(-ENOMEM);
- }
pdata->dma_slave = devm_kzalloc(&pdev->dev,
sizeof(*(pdata->dma_slave)),
GFP_KERNEL);
if (!pdata->dma_slave) {
- dev_err(&pdev->dev, "could not allocate memory for dma_slave\n");
devm_kfree(&pdev->dev, pdata);
return ERR_PTR(-ENOMEM);
}
@@ -2649,7 +2646,6 @@ static int atmel_aes_probe(struct platform_device *pdev)
aes_dd = devm_kzalloc(&pdev->dev, sizeof(*aes_dd), GFP_KERNEL);
if (aes_dd == NULL) {
- dev_err(dev, "unable to alloc data struct.\n");
err = -ENOMEM;
goto aes_dd_err;
}
diff --git a/drivers/crypto/atmel-sha.c b/drivers/crypto/atmel-sha.c
index 8874aa5ca0f7..4d43081120db 100644
--- a/drivers/crypto/atmel-sha.c
+++ b/drivers/crypto/atmel-sha.c
@@ -2726,18 +2726,14 @@ static struct crypto_platform_data *atmel_sha_of_init(struct platform_device *pd
}
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata) {
- dev_err(&pdev->dev, "could not allocate memory for pdata\n");
+ if (!pdata)
return ERR_PTR(-ENOMEM);
- }
pdata->dma_slave = devm_kzalloc(&pdev->dev,
sizeof(*(pdata->dma_slave)),
GFP_KERNEL);
- if (!pdata->dma_slave) {
- dev_err(&pdev->dev, "could not allocate memory for dma_slave\n");
+ if (!pdata->dma_slave)
return ERR_PTR(-ENOMEM);
- }
return pdata;
}
@@ -2758,7 +2754,6 @@ static int atmel_sha_probe(struct platform_device *pdev)
sha_dd = devm_kzalloc(&pdev->dev, sizeof(*sha_dd), GFP_KERNEL);
if (sha_dd == NULL) {
- dev_err(dev, "unable to alloc data struct.\n");
err = -ENOMEM;
goto sha_dd_err;
}
diff --git a/drivers/crypto/atmel-tdes.c b/drivers/crypto/atmel-tdes.c
index 592124f8382b..97b0423efa7f 100644
--- a/drivers/crypto/atmel-tdes.c
+++ b/drivers/crypto/atmel-tdes.c
@@ -1312,18 +1312,14 @@ static struct crypto_platform_data *atmel_tdes_of_init(struct platform_device *p
}
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata) {
- dev_err(&pdev->dev, "could not allocate memory for pdata\n");
+ if (!pdata)
return ERR_PTR(-ENOMEM);
- }
pdata->dma_slave = devm_kzalloc(&pdev->dev,
sizeof(*(pdata->dma_slave)),
GFP_KERNEL);
- if (!pdata->dma_slave) {
- dev_err(&pdev->dev, "could not allocate memory for dma_slave\n");
+ if (!pdata->dma_slave)
return ERR_PTR(-ENOMEM);
- }
return pdata;
}
@@ -1344,7 +1340,6 @@ static int atmel_tdes_probe(struct platform_device *pdev)
tdes_dd = devm_kmalloc(&pdev->dev, sizeof(*tdes_dd), GFP_KERNEL);
if (tdes_dd == NULL) {
- dev_err(dev, "unable to alloc data struct.\n");
err = -ENOMEM;
goto tdes_dd_err;
}
diff --git a/drivers/crypto/bcm/cipher.c b/drivers/crypto/bcm/cipher.c
index 2b75f95bbe1b..309c67c7012f 100644
--- a/drivers/crypto/bcm/cipher.c
+++ b/drivers/crypto/bcm/cipher.c
@@ -818,7 +818,7 @@ static int handle_ahash_req(struct iproc_reqctx_s *rctx)
/* AES hashing keeps key size in type field, so need to copy it here */
if (hash_parms.alg == HASH_ALG_AES)
- hash_parms.type = cipher_parms.type;
+ hash_parms.type = (enum hash_type)cipher_parms.type;
else
hash_parms.type = spu->spu_hash_type(rctx->total_sent);
@@ -1409,7 +1409,7 @@ static int handle_aead_req(struct iproc_reqctx_s *rctx)
rctx->iv_ctr_len);
if (ctx->auth.alg == HASH_ALG_AES)
- hash_parms.type = ctx->cipher_type;
+ hash_parms.type = (enum hash_type)ctx->cipher_type;
/* General case AAD padding (CCM and RFC4543 special cases below) */
aead_parms.aad_pad_len = spu->spu_gcm_ccm_pad_len(ctx->cipher.mode,
diff --git a/drivers/crypto/bcm/util.c b/drivers/crypto/bcm/util.c
index d543c010ccd9..a912c6ad3e85 100644
--- a/drivers/crypto/bcm/util.c
+++ b/drivers/crypto/bcm/util.c
@@ -279,7 +279,6 @@ int do_shash(unsigned char *name, unsigned char *result,
sdesc = kmalloc(size, GFP_KERNEL);
if (!sdesc) {
rc = -ENOMEM;
- pr_err("%s: Memory allocation failure\n", __func__);
goto do_shash_err;
}
sdesc->shash.tfm = hash;
diff --git a/drivers/crypto/bfin_crc.c b/drivers/crypto/bfin_crc.c
deleted file mode 100644
index bfbf8bf77f03..000000000000
--- a/drivers/crypto/bfin_crc.c
+++ /dev/null
@@ -1,743 +0,0 @@
-/*
- * Cryptographic API.
- *
- * Support Blackfin CRC HW acceleration.
- *
- * Copyright 2012 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
- */
-
-#include <linux/err.h>
-#include <linux/device.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/irq.h>
-#include <linux/io.h>
-#include <linux/platform_device.h>
-#include <linux/scatterlist.h>
-#include <linux/dma-mapping.h>
-#include <linux/delay.h>
-#include <linux/crypto.h>
-#include <linux/cryptohash.h>
-#include <crypto/scatterwalk.h>
-#include <crypto/algapi.h>
-#include <crypto/hash.h>
-#include <crypto/internal/hash.h>
-#include <asm/unaligned.h>
-
-#include <asm/dma.h>
-#include <asm/portmux.h>
-#include <asm/io.h>
-
-#include "bfin_crc.h"
-
-#define CRC_CCRYPTO_QUEUE_LENGTH 5
-
-#define DRIVER_NAME "bfin-hmac-crc"
-#define CHKSUM_DIGEST_SIZE 4
-#define CHKSUM_BLOCK_SIZE 1
-
-#define CRC_MAX_DMA_DESC 100
-
-#define CRC_CRYPTO_STATE_UPDATE 1
-#define CRC_CRYPTO_STATE_FINALUPDATE 2
-#define CRC_CRYPTO_STATE_FINISH 3
-
-struct bfin_crypto_crc {
- struct list_head list;
- struct device *dev;
- spinlock_t lock;
-
- int irq;
- int dma_ch;
- u32 poly;
- struct crc_register *regs;
-
- struct ahash_request *req; /* current request in operation */
- struct dma_desc_array *sg_cpu; /* virt addr of sg dma descriptors */
- dma_addr_t sg_dma; /* phy addr of sg dma descriptors */
- u8 *sg_mid_buf;
- dma_addr_t sg_mid_dma; /* phy addr of sg mid buffer */
-
- struct tasklet_struct done_task;
- struct crypto_queue queue; /* waiting requests */
-
- u8 busy:1; /* crc device in operation flag */
-};
-
-static struct bfin_crypto_crc_list {
- struct list_head dev_list;
- spinlock_t lock;
-} crc_list;
-
-struct bfin_crypto_crc_reqctx {
- struct bfin_crypto_crc *crc;
-
- unsigned int total; /* total request bytes */
- size_t sg_buflen; /* bytes for this update */
- unsigned int sg_nents;
- struct scatterlist *sg; /* sg list head for this update*/
- struct scatterlist bufsl[2]; /* chained sg list */
-
- size_t bufnext_len;
- size_t buflast_len;
- u8 bufnext[CHKSUM_DIGEST_SIZE]; /* extra bytes for next udpate */
- u8 buflast[CHKSUM_DIGEST_SIZE]; /* extra bytes from last udpate */
-
- u8 flag;
-};
-
-struct bfin_crypto_crc_ctx {
- struct bfin_crypto_crc *crc;
- u32 key;
-};
-
-/*
- * get element in scatter list by given index
- */
-static struct scatterlist *sg_get(struct scatterlist *sg_list, unsigned int nents,
- unsigned int index)
-{
- struct scatterlist *sg = NULL;
- int i;
-
- for_each_sg(sg_list, sg, nents, i)
- if (i == index)
- break;
-
- return sg;
-}
-
-static int bfin_crypto_crc_init_hw(struct bfin_crypto_crc *crc, u32 key)
-{
- writel(0, &crc->regs->datacntrld);
- writel(MODE_CALC_CRC << OPMODE_OFFSET, &crc->regs->control);
- writel(key, &crc->regs->curresult);
-
- /* setup CRC interrupts */
- writel(CMPERRI | DCNTEXPI, &crc->regs->status);
- writel(CMPERRI | DCNTEXPI, &crc->regs->intrenset);
-
- return 0;
-}
-
-static int bfin_crypto_crc_init(struct ahash_request *req)
-{
- struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
- struct bfin_crypto_crc_ctx *crc_ctx = crypto_ahash_ctx(tfm);
- struct bfin_crypto_crc_reqctx *ctx = ahash_request_ctx(req);
- struct bfin_crypto_crc *crc;
-
- dev_dbg(ctx->crc->dev, "crc_init\n");
- spin_lock_bh(&crc_list.lock);
- list_for_each_entry(crc, &crc_list.dev_list, list) {
- crc_ctx->crc = crc;
- break;
- }
- spin_unlock_bh(&crc_list.lock);
-
- if (sg_nents(req->src) > CRC_MAX_DMA_DESC) {
- dev_dbg(ctx->crc->dev, "init: requested sg list is too big > %d\n",
- CRC_MAX_DMA_DESC);
- return -EINVAL;
- }
-
- ctx->crc = crc;
- ctx->bufnext_len = 0;
- ctx->buflast_len = 0;
- ctx->sg_buflen = 0;
- ctx->total = 0;
- ctx->flag = 0;
-
- /* init crc results */
- put_unaligned_le32(crc_ctx->key, req->result);
-
- dev_dbg(ctx->crc->dev, "init: digest size: %d\n",
- crypto_ahash_digestsize(tfm));
-
- return bfin_crypto_crc_init_hw(crc, crc_ctx->key);
-}
-
-static void bfin_crypto_crc_config_dma(struct bfin_crypto_crc *crc)
-{
- struct scatterlist *sg;
- struct bfin_crypto_crc_reqctx *ctx = ahash_request_ctx(crc->req);
- int i = 0, j = 0;
- unsigned long dma_config;
- unsigned int dma_count;
- unsigned int dma_addr;
- unsigned int mid_dma_count = 0;
- int dma_mod;
-
- dma_map_sg(crc->dev, ctx->sg, ctx->sg_nents, DMA_TO_DEVICE);
-
- for_each_sg(ctx->sg, sg, ctx->sg_nents, j) {
- dma_addr = sg_dma_address(sg);
- /* deduce extra bytes in last sg */
- if (sg_is_last(sg))
- dma_count = sg_dma_len(sg) - ctx->bufnext_len;
- else
- dma_count = sg_dma_len(sg);
-
- if (mid_dma_count) {
- /* Append last middle dma buffer to 4 bytes with first
- bytes in current sg buffer. Move addr of current
- sg and deduce the length of current sg.
- */
- memcpy(crc->sg_mid_buf +(i << 2) + mid_dma_count,
- sg_virt(sg),
- CHKSUM_DIGEST_SIZE - mid_dma_count);
- dma_addr += CHKSUM_DIGEST_SIZE - mid_dma_count;
- dma_count -= CHKSUM_DIGEST_SIZE - mid_dma_count;
-
- dma_config = DMAFLOW_ARRAY | RESTART | NDSIZE_3 |
- DMAEN | PSIZE_32 | WDSIZE_32;
-
- /* setup new dma descriptor for next middle dma */
- crc->sg_cpu[i].start_addr = crc->sg_mid_dma + (i << 2);
- crc->sg_cpu[i].cfg = dma_config;
- crc->sg_cpu[i].x_count = 1;
- crc->sg_cpu[i].x_modify = CHKSUM_DIGEST_SIZE;
- dev_dbg(crc->dev, "%d: crc_dma: start_addr:0x%lx, "
- "cfg:0x%x, x_count:0x%x, x_modify:0x%x\n",
- i, crc->sg_cpu[i].start_addr,
- crc->sg_cpu[i].cfg, crc->sg_cpu[i].x_count,
- crc->sg_cpu[i].x_modify);
- i++;
- }
-
- dma_config = DMAFLOW_ARRAY | RESTART | NDSIZE_3 | DMAEN | PSIZE_32;
- /* chop current sg dma len to multiple of 32 bits */
- mid_dma_count = dma_count % 4;
- dma_count &= ~0x3;
-
- if (dma_addr % 4 == 0) {
- dma_config |= WDSIZE_32;
- dma_count >>= 2;
- dma_mod = 4;
- } else if (dma_addr % 2 == 0) {
- dma_config |= WDSIZE_16;
- dma_count >>= 1;
- dma_mod = 2;
- } else {
- dma_config |= WDSIZE_8;
- dma_mod = 1;
- }
-
- crc->sg_cpu[i].start_addr = dma_addr;
- crc->sg_cpu[i].cfg = dma_config;
- crc->sg_cpu[i].x_count = dma_count;
- crc->sg_cpu[i].x_modify = dma_mod;
- dev_dbg(crc->dev, "%d: crc_dma: start_addr:0x%lx, "
- "cfg:0x%x, x_count:0x%x, x_modify:0x%x\n",
- i, crc->sg_cpu[i].start_addr,
- crc->sg_cpu[i].cfg, crc->sg_cpu[i].x_count,
- crc->sg_cpu[i].x_modify);
- i++;
-
- if (mid_dma_count) {
- /* copy extra bytes to next middle dma buffer */
- memcpy(crc->sg_mid_buf + (i << 2),
- (u8*)sg_virt(sg) + (dma_count << 2),
- mid_dma_count);
- }
- }
-
- dma_config = DMAFLOW_ARRAY | RESTART | NDSIZE_3 | DMAEN | PSIZE_32 | WDSIZE_32;
- /* For final update req, append the buffer for next update as well*/
- if (ctx->bufnext_len && (ctx->flag == CRC_CRYPTO_STATE_FINALUPDATE ||
- ctx->flag == CRC_CRYPTO_STATE_FINISH)) {
- crc->sg_cpu[i].start_addr = dma_map_single(crc->dev, ctx->bufnext,
- CHKSUM_DIGEST_SIZE, DMA_TO_DEVICE);
- crc->sg_cpu[i].cfg = dma_config;
- crc->sg_cpu[i].x_count = 1;
- crc->sg_cpu[i].x_modify = CHKSUM_DIGEST_SIZE;
- dev_dbg(crc->dev, "%d: crc_dma: start_addr:0x%lx, "
- "cfg:0x%x, x_count:0x%x, x_modify:0x%x\n",
- i, crc->sg_cpu[i].start_addr,
- crc->sg_cpu[i].cfg, crc->sg_cpu[i].x_count,
- crc->sg_cpu[i].x_modify);
- i++;
- }
-
- if (i == 0)
- return;
-
- /* Set the last descriptor to stop mode */
- crc->sg_cpu[i - 1].cfg &= ~(DMAFLOW | NDSIZE);
- crc->sg_cpu[i - 1].cfg |= DI_EN;
- set_dma_curr_desc_addr(crc->dma_ch, (unsigned long *)crc->sg_dma);
- set_dma_x_count(crc->dma_ch, 0);
- set_dma_x_modify(crc->dma_ch, 0);
- set_dma_config(crc->dma_ch, dma_config);
-}
-
-static int bfin_crypto_crc_handle_queue(struct bfin_crypto_crc *crc,
- struct ahash_request *req)
-{
- struct crypto_async_request *async_req, *backlog;
- struct bfin_crypto_crc_reqctx *ctx;
- struct scatterlist *sg;
- int ret = 0;
- int nsg, i, j;
- unsigned int nextlen;
- unsigned long flags;
- u32 reg;
-
- spin_lock_irqsave(&crc->lock, flags);
- if (req)
- ret = ahash_enqueue_request(&crc->queue, req);
- if (crc->busy) {
- spin_unlock_irqrestore(&crc->lock, flags);
- return ret;
- }
- backlog = crypto_get_backlog(&crc->queue);
- async_req = crypto_dequeue_request(&crc->queue);
- if (async_req)
- crc->busy = 1;
- spin_unlock_irqrestore(&crc->lock, flags);
-
- if (!async_req)
- return ret;
-
- if (backlog)
- backlog->complete(backlog, -EINPROGRESS);
-
- req = ahash_request_cast(async_req);
- crc->req = req;
- ctx = ahash_request_ctx(req);
- ctx->sg = NULL;
- ctx->sg_buflen = 0;
- ctx->sg_nents = 0;
-
- dev_dbg(crc->dev, "handling new req, flag=%u, nbytes: %d\n",
- ctx->flag, req->nbytes);
-
- if (ctx->flag == CRC_CRYPTO_STATE_FINISH) {
- if (ctx->bufnext_len == 0) {
- crc->busy = 0;
- return 0;
- }
-
- /* Pack last crc update buffer to 32bit */
- memset(ctx->bufnext + ctx->bufnext_len, 0,
- CHKSUM_DIGEST_SIZE - ctx->bufnext_len);
- } else {
- /* Pack small data which is less than 32bit to buffer for next update. */
- if (ctx->bufnext_len + req->nbytes < CHKSUM_DIGEST_SIZE) {
- memcpy(ctx->bufnext + ctx->bufnext_len,
- sg_virt(req->src), req->nbytes);
- ctx->bufnext_len += req->nbytes;
- if (ctx->flag == CRC_CRYPTO_STATE_FINALUPDATE &&
- ctx->bufnext_len) {
- goto finish_update;
- } else {
- crc->busy = 0;
- return 0;
- }
- }
-
- if (ctx->bufnext_len) {
- /* Chain in extra bytes of last update */
- ctx->buflast_len = ctx->bufnext_len;
- memcpy(ctx->buflast, ctx->bufnext, ctx->buflast_len);
-
- nsg = ctx->sg_buflen ? 2 : 1;
- sg_init_table(ctx->bufsl, nsg);
- sg_set_buf(ctx->bufsl, ctx->buflast, ctx->buflast_len);
- if (nsg > 1)
- sg_chain(ctx->bufsl, nsg, req->src);
- ctx->sg = ctx->bufsl;
- } else
- ctx->sg = req->src;
-
- /* Chop crc buffer size to multiple of 32 bit */
- nsg = sg_nents(ctx->sg);
- ctx->sg_nents = nsg;
- ctx->sg_buflen = ctx->buflast_len + req->nbytes;
- ctx->bufnext_len = ctx->sg_buflen % 4;
- ctx->sg_buflen &= ~0x3;
-
- if (ctx->bufnext_len) {
- /* copy extra bytes to buffer for next update */
- memset(ctx->bufnext, 0, CHKSUM_DIGEST_SIZE);
- nextlen = ctx->bufnext_len;
- for (i = nsg - 1; i >= 0; i--) {
- sg = sg_get(ctx->sg, nsg, i);
- j = min(nextlen, sg_dma_len(sg));
- memcpy(ctx->bufnext + nextlen - j,
- sg_virt(sg) + sg_dma_len(sg) - j, j);
- if (j == sg_dma_len(sg))
- ctx->sg_nents--;
- nextlen -= j;
- if (nextlen == 0)
- break;
- }
- }
- }
-
-finish_update:
- if (ctx->bufnext_len && (ctx->flag == CRC_CRYPTO_STATE_FINALUPDATE ||
- ctx->flag == CRC_CRYPTO_STATE_FINISH))
- ctx->sg_buflen += CHKSUM_DIGEST_SIZE;
-
- /* set CRC data count before start DMA */
- writel(ctx->sg_buflen >> 2, &crc->regs->datacnt);
-
- /* setup and enable CRC DMA */
- bfin_crypto_crc_config_dma(crc);
-
- /* finally kick off CRC operation */
- reg = readl(&crc->regs->control);
- writel(reg | BLKEN, &crc->regs->control);
-
- return -EINPROGRESS;
-}
-
-static int bfin_crypto_crc_update(struct ahash_request *req)
-{
- struct bfin_crypto_crc_reqctx *ctx = ahash_request_ctx(req);
-
- if (!req->nbytes)
- return 0;
-
- dev_dbg(ctx->crc->dev, "crc_update\n");
- ctx->total += req->nbytes;
- ctx->flag = CRC_CRYPTO_STATE_UPDATE;
-
- return bfin_crypto_crc_handle_queue(ctx->crc, req);
-}
-
-static int bfin_crypto_crc_final(struct ahash_request *req)
-{
- struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
- struct bfin_crypto_crc_ctx *crc_ctx = crypto_ahash_ctx(tfm);
- struct bfin_crypto_crc_reqctx *ctx = ahash_request_ctx(req);
-
- dev_dbg(ctx->crc->dev, "crc_final\n");
- ctx->flag = CRC_CRYPTO_STATE_FINISH;
- crc_ctx->key = 0;
-
- return bfin_crypto_crc_handle_queue(ctx->crc, req);
-}
-
-static int bfin_crypto_crc_finup(struct ahash_request *req)
-{
- struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
- struct bfin_crypto_crc_ctx *crc_ctx = crypto_ahash_ctx(tfm);
- struct bfin_crypto_crc_reqctx *ctx = ahash_request_ctx(req);
-
- dev_dbg(ctx->crc->dev, "crc_finishupdate\n");
- ctx->total += req->nbytes;
- ctx->flag = CRC_CRYPTO_STATE_FINALUPDATE;
- crc_ctx->key = 0;
-
- return bfin_crypto_crc_handle_queue(ctx->crc, req);
-}
-
-static int bfin_crypto_crc_digest(struct ahash_request *req)
-{
- int ret;
-
- ret = bfin_crypto_crc_init(req);
- if (ret)
- return ret;
-
- return bfin_crypto_crc_finup(req);
-}
-
-static int bfin_crypto_crc_setkey(struct crypto_ahash *tfm, const u8 *key,
- unsigned int keylen)
-{
- struct bfin_crypto_crc_ctx *crc_ctx = crypto_ahash_ctx(tfm);
-
- dev_dbg(crc_ctx->crc->dev, "crc_setkey\n");
- if (keylen != CHKSUM_DIGEST_SIZE) {
- crypto_ahash_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
- return -EINVAL;
- }
-
- crc_ctx->key = get_unaligned_le32(key);
-
- return 0;
-}
-
-static int bfin_crypto_crc_cra_init(struct crypto_tfm *tfm)
-{
- struct bfin_crypto_crc_ctx *crc_ctx = crypto_tfm_ctx(tfm);
-
- crc_ctx->key = 0;
- crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm),
- sizeof(struct bfin_crypto_crc_reqctx));
-
- return 0;
-}
-
-static void bfin_crypto_crc_cra_exit(struct crypto_tfm *tfm)
-{
-}
-
-static struct ahash_alg algs = {
- .init = bfin_crypto_crc_init,
- .update = bfin_crypto_crc_update,
- .final = bfin_crypto_crc_final,
- .finup = bfin_crypto_crc_finup,
- .digest = bfin_crypto_crc_digest,
- .setkey = bfin_crypto_crc_setkey,
- .halg.digestsize = CHKSUM_DIGEST_SIZE,
- .halg.base = {
- .cra_name = "hmac(crc32)",
- .cra_driver_name = DRIVER_NAME,
- .cra_priority = 100,
- .cra_flags = CRYPTO_ALG_TYPE_AHASH |
- CRYPTO_ALG_ASYNC |
- CRYPTO_ALG_OPTIONAL_KEY,
- .cra_blocksize = CHKSUM_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct bfin_crypto_crc_ctx),
- .cra_alignmask = 3,
- .cra_module = THIS_MODULE,
- .cra_init = bfin_crypto_crc_cra_init,
- .cra_exit = bfin_crypto_crc_cra_exit,
- }
-};
-
-static void bfin_crypto_crc_done_task(unsigned long data)
-{
- struct bfin_crypto_crc *crc = (struct bfin_crypto_crc *)data;
-
- bfin_crypto_crc_handle_queue(crc, NULL);
-}
-
-static irqreturn_t bfin_crypto_crc_handler(int irq, void *dev_id)
-{
- struct bfin_crypto_crc *crc = dev_id;
- u32 reg;
-
- if (readl(&crc->regs->status) & DCNTEXP) {
- writel(DCNTEXP, &crc->regs->status);
-
- /* prepare results */
- put_unaligned_le32(readl(&crc->regs->result),
- crc->req->result);
-
- reg = readl(&crc->regs->control);
- writel(reg & ~BLKEN, &crc->regs->control);
- crc->busy = 0;
-
- if (crc->req->base.complete)
- crc->req->base.complete(&crc->req->base, 0);
-
- tasklet_schedule(&crc->done_task);
-
- return IRQ_HANDLED;
- } else
- return IRQ_NONE;
-}
-
-#ifdef CONFIG_PM
-/**
- * bfin_crypto_crc_suspend - suspend crc device
- * @pdev: device being suspended
- * @state: requested suspend state
- */
-static int bfin_crypto_crc_suspend(struct platform_device *pdev, pm_message_t state)
-{
- struct bfin_crypto_crc *crc = platform_get_drvdata(pdev);
- int i = 100000;
-
- while ((readl(&crc->regs->control) & BLKEN) && --i)
- cpu_relax();
-
- if (i == 0)
- return -EBUSY;
-
- return 0;
-}
-#else
-# define bfin_crypto_crc_suspend NULL
-#endif
-
-#define bfin_crypto_crc_resume NULL
-
-/**
- * bfin_crypto_crc_probe - Initialize module
- *
- */
-static int bfin_crypto_crc_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct resource *res;
- struct bfin_crypto_crc *crc;
- unsigned int timeout = 100000;
- int ret;
-
- crc = devm_kzalloc(dev, sizeof(*crc), GFP_KERNEL);
- if (!crc) {
- dev_err(&pdev->dev, "fail to malloc bfin_crypto_crc\n");
- return -ENOMEM;
- }
-
- crc->dev = dev;
-
- INIT_LIST_HEAD(&crc->list);
- spin_lock_init(&crc->lock);
- tasklet_init(&crc->done_task, bfin_crypto_crc_done_task, (unsigned long)crc);
- crypto_init_queue(&crc->queue, CRC_CCRYPTO_QUEUE_LENGTH);
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- crc->regs = devm_ioremap_resource(dev, res);
- if (IS_ERR((void *)crc->regs)) {
- dev_err(&pdev->dev, "Cannot map CRC IO\n");
- return PTR_ERR((void *)crc->regs);
- }
-
- crc->irq = platform_get_irq(pdev, 0);
- if (crc->irq < 0) {
- dev_err(&pdev->dev, "No CRC DCNTEXP IRQ specified\n");
- return -ENOENT;
- }
-
- ret = devm_request_irq(dev, crc->irq, bfin_crypto_crc_handler,
- IRQF_SHARED, dev_name(dev), crc);
- if (ret) {
- dev_err(&pdev->dev, "Unable to request blackfin crc irq\n");
- return ret;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (res == NULL) {
- dev_err(&pdev->dev, "No CRC DMA channel specified\n");
- return -ENOENT;
- }
- crc->dma_ch = res->start;
-
- ret = request_dma(crc->dma_ch, dev_name(dev));
- if (ret) {
- dev_err(&pdev->dev, "Unable to attach Blackfin CRC DMA channel\n");
- return ret;
- }
-
- crc->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &crc->sg_dma, GFP_KERNEL);
- if (crc->sg_cpu == NULL) {
- ret = -ENOMEM;
- goto out_error_dma;
- }
- /*
- * need at most CRC_MAX_DMA_DESC sg + CRC_MAX_DMA_DESC middle +
- * 1 last + 1 next dma descriptors
- */
- crc->sg_mid_buf = (u8 *)(crc->sg_cpu + ((CRC_MAX_DMA_DESC + 1) << 1));
- crc->sg_mid_dma = crc->sg_dma + sizeof(struct dma_desc_array)
- * ((CRC_MAX_DMA_DESC + 1) << 1);
-
- writel(0, &crc->regs->control);
- crc->poly = (u32)pdev->dev.platform_data;
- writel(crc->poly, &crc->regs->poly);
-
- while (!(readl(&crc->regs->status) & LUTDONE) && (--timeout) > 0)
- cpu_relax();
-
- if (timeout == 0)
- dev_info(&pdev->dev, "init crc poly timeout\n");
-
- platform_set_drvdata(pdev, crc);
-
- spin_lock(&crc_list.lock);
- list_add(&crc->list, &crc_list.dev_list);
- spin_unlock(&crc_list.lock);
-
- if (list_is_singular(&crc_list.dev_list)) {
- ret = crypto_register_ahash(&algs);
- if (ret) {
- dev_err(&pdev->dev,
- "Can't register crypto ahash device\n");
- goto out_error_dma;
- }
- }
-
- dev_info(&pdev->dev, "initialized\n");
-
- return 0;
-
-out_error_dma:
- if (crc->sg_cpu)
- dma_free_coherent(&pdev->dev, PAGE_SIZE, crc->sg_cpu, crc->sg_dma);
- free_dma(crc->dma_ch);
-
- return ret;
-}
-
-/**
- * bfin_crypto_crc_remove - Initialize module
- *
- */
-static int bfin_crypto_crc_remove(struct platform_device *pdev)
-{
- struct bfin_crypto_crc *crc = platform_get_drvdata(pdev);
-
- if (!crc)
- return -ENODEV;
-
- spin_lock(&crc_list.lock);
- list_del(&crc->list);
- spin_unlock(&crc_list.lock);
-
- crypto_unregister_ahash(&algs);
- tasklet_kill(&crc->done_task);
- free_dma(crc->dma_ch);
-
- return 0;
-}
-
-static struct platform_driver bfin_crypto_crc_driver = {
- .probe = bfin_crypto_crc_probe,
- .remove = bfin_crypto_crc_remove,
- .suspend = bfin_crypto_crc_suspend,
- .resume = bfin_crypto_crc_resume,
- .driver = {
- .name = DRIVER_NAME,
- },
-};
-
-/**
- * bfin_crypto_crc_mod_init - Initialize module
- *
- * Checks the module params and registers the platform driver.
- * Real work is in the platform probe function.
- */
-static int __init bfin_crypto_crc_mod_init(void)
-{
- int ret;
-
- pr_info("Blackfin hardware CRC crypto driver\n");
-
- INIT_LIST_HEAD(&crc_list.dev_list);
- spin_lock_init(&crc_list.lock);
-
- ret = platform_driver_register(&bfin_crypto_crc_driver);
- if (ret) {
- pr_err("unable to register driver\n");
- return ret;
- }
-
- return 0;
-}
-
-/**
- * bfin_crypto_crc_mod_exit - Deinitialize module
- */
-static void __exit bfin_crypto_crc_mod_exit(void)
-{
- platform_driver_unregister(&bfin_crypto_crc_driver);
-}
-
-module_init(bfin_crypto_crc_mod_init);
-module_exit(bfin_crypto_crc_mod_exit);
-
-MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
-MODULE_DESCRIPTION("Blackfin CRC hardware crypto driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/crypto/bfin_crc.h b/drivers/crypto/bfin_crc.h
deleted file mode 100644
index 786ef746d109..000000000000
--- a/drivers/crypto/bfin_crc.h
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * bfin_crc.h - interface to Blackfin CRC controllers
- *
- * Copyright 2012 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
- */
-
-#ifndef __BFIN_CRC_H__
-#define __BFIN_CRC_H__
-
-/* Function driver which use hardware crc must initialize the structure */
-struct crc_info {
- /* Input data address */
- unsigned char *in_addr;
- /* Output data address */
- unsigned char *out_addr;
- /* Input or output bytes */
- unsigned long datasize;
- union {
- /* CRC to compare with that of input buffer */
- unsigned long crc_compare;
- /* Value to compare with input data */
- unsigned long val_verify;
- /* Value to fill */
- unsigned long val_fill;
- };
- /* Value to program the 32b CRC Polynomial */
- unsigned long crc_poly;
- union {
- /* CRC calculated from the input data */
- unsigned long crc_result;
- /* First failed position to verify input data */
- unsigned long pos_verify;
- };
- /* CRC mirror flags */
- unsigned int bitmirr:1;
- unsigned int bytmirr:1;
- unsigned int w16swp:1;
- unsigned int fdsel:1;
- unsigned int rsltmirr:1;
- unsigned int polymirr:1;
- unsigned int cmpmirr:1;
-};
-
-/* Userspace interface */
-#define CRC_IOC_MAGIC 'C'
-#define CRC_IOC_CALC_CRC _IOWR('C', 0x01, unsigned int)
-#define CRC_IOC_MEMCPY_CRC _IOWR('C', 0x02, unsigned int)
-#define CRC_IOC_VERIFY_VAL _IOWR('C', 0x03, unsigned int)
-#define CRC_IOC_FILL_VAL _IOWR('C', 0x04, unsigned int)
-
-
-#ifdef __KERNEL__
-
-#include <linux/types.h>
-#include <linux/spinlock.h>
-
-struct crc_register {
- u32 control;
- u32 datacnt;
- u32 datacntrld;
- u32 __pad_1[2];
- u32 compare;
- u32 fillval;
- u32 datafifo;
- u32 intren;
- u32 intrenset;
- u32 intrenclr;
- u32 poly;
- u32 __pad_2[4];
- u32 status;
- u32 datacntcap;
- u32 __pad_3;
- u32 result;
- u32 curresult;
- u32 __pad_4[3];
- u32 revid;
-};
-
-/* CRC_STATUS Masks */
-#define CMPERR 0x00000002 /* Compare error */
-#define DCNTEXP 0x00000010 /* datacnt register expired */
-#define IBR 0x00010000 /* Input buffer ready */
-#define OBR 0x00020000 /* Output buffer ready */
-#define IRR 0x00040000 /* Immediate result readt */
-#define LUTDONE 0x00080000 /* Look-up table generation done */
-#define FSTAT 0x00700000 /* FIFO status */
-#define MAX_FIFO 4 /* Max fifo size */
-
-/* CRC_CONTROL Masks */
-#define BLKEN 0x00000001 /* Block enable */
-#define OPMODE 0x000000F0 /* Operation mode */
-#define OPMODE_OFFSET 4 /* Operation mode mask offset*/
-#define MODE_DMACPY_CRC 1 /* MTM CRC compute and compare */
-#define MODE_DATA_FILL 2 /* MTM data fill */
-#define MODE_CALC_CRC 3 /* MSM CRC compute and compare */
-#define MODE_DATA_VERIFY 4 /* MSM data verify */
-#define AUTOCLRZ 0x00000100 /* Auto clear to zero */
-#define AUTOCLRF 0x00000200 /* Auto clear to one */
-#define OBRSTALL 0x00001000 /* Stall on output buffer ready */
-#define IRRSTALL 0x00002000 /* Stall on immediate result ready */
-#define BITMIRR 0x00010000 /* Mirror bits within each byte of 32-bit input data */
-#define BITMIRR_OFFSET 16 /* Mirror bits offset */
-#define BYTMIRR 0x00020000 /* Mirror bytes of 32-bit input data */
-#define BYTMIRR_OFFSET 17 /* Mirror bytes offset */
-#define W16SWP 0x00040000 /* Mirror uppper and lower 16-bit word of 32-bit input data */
-#define W16SWP_OFFSET 18 /* Mirror 16-bit word offset */
-#define FDSEL 0x00080000 /* FIFO is written after input data is mirrored */
-#define FDSEL_OFFSET 19 /* Mirror FIFO offset */
-#define RSLTMIRR 0x00100000 /* CRC result registers are mirrored. */
-#define RSLTMIRR_OFFSET 20 /* Mirror CRC result offset. */
-#define POLYMIRR 0x00200000 /* CRC poly register is mirrored. */
-#define POLYMIRR_OFFSET 21 /* Mirror CRC poly offset. */
-#define CMPMIRR 0x00400000 /* CRC compare register is mirrored. */
-#define CMPMIRR_OFFSET 22 /* Mirror CRC compare offset. */
-
-/* CRC_INTREN Masks */
-#define CMPERRI 0x02 /* CRC_ERROR_INTR */
-#define DCNTEXPI 0x10 /* CRC_STATUS_INTR */
-
-#endif
-
-#endif
diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c
index 2188235be02d..7207a535942d 100644
--- a/drivers/crypto/caam/caamalg.c
+++ b/drivers/crypto/caam/caamalg.c
@@ -328,6 +328,7 @@ static int gcm_set_sh_desc(struct crypto_aead *aead)
{
struct caam_ctx *ctx = crypto_aead_ctx(aead);
struct device *jrdev = ctx->jrdev;
+ unsigned int ivsize = crypto_aead_ivsize(aead);
u32 *desc;
int rem_bytes = CAAM_DESC_BYTES_MAX - GCM_DESC_JOB_IO_LEN -
ctx->cdata.keylen;
@@ -349,7 +350,7 @@ static int gcm_set_sh_desc(struct crypto_aead *aead)
}
desc = ctx->sh_desc_enc;
- cnstr_shdsc_gcm_encap(desc, &ctx->cdata, ctx->authsize);
+ cnstr_shdsc_gcm_encap(desc, &ctx->cdata, ivsize, ctx->authsize, false);
dma_sync_single_for_device(jrdev, ctx->sh_desc_enc_dma,
desc_bytes(desc), ctx->dir);
@@ -366,7 +367,7 @@ static int gcm_set_sh_desc(struct crypto_aead *aead)
}
desc = ctx->sh_desc_dec;
- cnstr_shdsc_gcm_decap(desc, &ctx->cdata, ctx->authsize);
+ cnstr_shdsc_gcm_decap(desc, &ctx->cdata, ivsize, ctx->authsize, false);
dma_sync_single_for_device(jrdev, ctx->sh_desc_dec_dma,
desc_bytes(desc), ctx->dir);
@@ -387,6 +388,7 @@ static int rfc4106_set_sh_desc(struct crypto_aead *aead)
{
struct caam_ctx *ctx = crypto_aead_ctx(aead);
struct device *jrdev = ctx->jrdev;
+ unsigned int ivsize = crypto_aead_ivsize(aead);
u32 *desc;
int rem_bytes = CAAM_DESC_BYTES_MAX - GCM_DESC_JOB_IO_LEN -
ctx->cdata.keylen;
@@ -408,7 +410,8 @@ static int rfc4106_set_sh_desc(struct crypto_aead *aead)
}
desc = ctx->sh_desc_enc;
- cnstr_shdsc_rfc4106_encap(desc, &ctx->cdata, ctx->authsize);
+ cnstr_shdsc_rfc4106_encap(desc, &ctx->cdata, ivsize, ctx->authsize,
+ false);
dma_sync_single_for_device(jrdev, ctx->sh_desc_enc_dma,
desc_bytes(desc), ctx->dir);
@@ -425,7 +428,8 @@ static int rfc4106_set_sh_desc(struct crypto_aead *aead)
}
desc = ctx->sh_desc_dec;
- cnstr_shdsc_rfc4106_decap(desc, &ctx->cdata, ctx->authsize);
+ cnstr_shdsc_rfc4106_decap(desc, &ctx->cdata, ivsize, ctx->authsize,
+ false);
dma_sync_single_for_device(jrdev, ctx->sh_desc_dec_dma,
desc_bytes(desc), ctx->dir);
@@ -447,6 +451,7 @@ static int rfc4543_set_sh_desc(struct crypto_aead *aead)
{
struct caam_ctx *ctx = crypto_aead_ctx(aead);
struct device *jrdev = ctx->jrdev;
+ unsigned int ivsize = crypto_aead_ivsize(aead);
u32 *desc;
int rem_bytes = CAAM_DESC_BYTES_MAX - GCM_DESC_JOB_IO_LEN -
ctx->cdata.keylen;
@@ -468,7 +473,8 @@ static int rfc4543_set_sh_desc(struct crypto_aead *aead)
}
desc = ctx->sh_desc_enc;
- cnstr_shdsc_rfc4543_encap(desc, &ctx->cdata, ctx->authsize);
+ cnstr_shdsc_rfc4543_encap(desc, &ctx->cdata, ivsize, ctx->authsize,
+ false);
dma_sync_single_for_device(jrdev, ctx->sh_desc_enc_dma,
desc_bytes(desc), ctx->dir);
@@ -485,7 +491,8 @@ static int rfc4543_set_sh_desc(struct crypto_aead *aead)
}
desc = ctx->sh_desc_dec;
- cnstr_shdsc_rfc4543_decap(desc, &ctx->cdata, ctx->authsize);
+ cnstr_shdsc_rfc4543_decap(desc, &ctx->cdata, ivsize, ctx->authsize,
+ false);
dma_sync_single_for_device(jrdev, ctx->sh_desc_dec_dma,
desc_bytes(desc), ctx->dir);
@@ -563,9 +570,11 @@ static int aead_setkey(struct crypto_aead *aead,
skip_split_key:
ctx->cdata.keylen = keys.enckeylen;
+ memzero_explicit(&keys, sizeof(keys));
return aead_set_sh_desc(aead);
badkey:
crypto_aead_set_flags(aead, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ memzero_explicit(&keys, sizeof(keys));
return -EINVAL;
}
diff --git a/drivers/crypto/caam/caamalg_desc.c b/drivers/crypto/caam/caamalg_desc.c
index ceb93fbb76e6..8ae7a1be7dfd 100644
--- a/drivers/crypto/caam/caamalg_desc.c
+++ b/drivers/crypto/caam/caamalg_desc.c
@@ -625,10 +625,13 @@ EXPORT_SYMBOL(cnstr_shdsc_aead_givencap);
* @desc: pointer to buffer used for descriptor construction
* @cdata: pointer to block cipher transform definitions
* Valid algorithm values - OP_ALG_ALGSEL_AES ANDed with OP_ALG_AAI_GCM.
+ * @ivsize: initialization vector size
* @icvsize: integrity check value (ICV) size (truncated or full)
+ * @is_qi: true when called from caam/qi
*/
void cnstr_shdsc_gcm_encap(u32 * const desc, struct alginfo *cdata,
- unsigned int icvsize)
+ unsigned int ivsize, unsigned int icvsize,
+ const bool is_qi)
{
u32 *key_jump_cmd, *zero_payload_jump_cmd, *zero_assoc_jump_cmd1,
*zero_assoc_jump_cmd2;
@@ -650,11 +653,35 @@ void cnstr_shdsc_gcm_encap(u32 * const desc, struct alginfo *cdata,
append_operation(desc, cdata->algtype | OP_ALG_AS_INITFINAL |
OP_ALG_ENCRYPT);
+ if (is_qi) {
+ u32 *wait_load_cmd;
+
+ /* REG3 = assoclen */
+ append_seq_load(desc, 4, LDST_CLASS_DECO |
+ LDST_SRCDST_WORD_DECO_MATH3 |
+ (4 << LDST_OFFSET_SHIFT));
+
+ wait_load_cmd = append_jump(desc, JUMP_JSL | JUMP_TEST_ALL |
+ JUMP_COND_CALM | JUMP_COND_NCP |
+ JUMP_COND_NOP | JUMP_COND_NIP |
+ JUMP_COND_NIFP);
+ set_jump_tgt_here(desc, wait_load_cmd);
+
+ append_math_sub_imm_u32(desc, VARSEQOUTLEN, SEQINLEN, IMM,
+ ivsize);
+ } else {
+ append_math_sub(desc, VARSEQOUTLEN, SEQINLEN, REG0,
+ CAAM_CMD_SZ);
+ }
+
/* if assoclen + cryptlen is ZERO, skip to ICV write */
- append_math_sub(desc, VARSEQOUTLEN, SEQINLEN, REG0, CAAM_CMD_SZ);
zero_assoc_jump_cmd2 = append_jump(desc, JUMP_TEST_ALL |
JUMP_COND_MATH_Z);
+ if (is_qi)
+ append_seq_fifo_load(desc, ivsize, FIFOLD_CLASS_CLASS1 |
+ FIFOLD_TYPE_IV | FIFOLD_TYPE_FLUSH1);
+
/* if assoclen is ZERO, skip reading the assoc data */
append_math_add(desc, VARSEQINLEN, ZERO, REG3, CAAM_CMD_SZ);
zero_assoc_jump_cmd1 = append_jump(desc, JUMP_TEST_ALL |
@@ -686,8 +713,11 @@ void cnstr_shdsc_gcm_encap(u32 * const desc, struct alginfo *cdata,
append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | FIFOLDST_VLF |
FIFOLD_TYPE_MSG | FIFOLD_TYPE_LAST1);
- /* jump the zero-payload commands */
- append_jump(desc, JUMP_TEST_ALL | 2);
+ /* jump to ICV writing */
+ if (is_qi)
+ append_jump(desc, JUMP_TEST_ALL | 4);
+ else
+ append_jump(desc, JUMP_TEST_ALL | 2);
/* zero-payload commands */
set_jump_tgt_here(desc, zero_payload_jump_cmd);
@@ -695,10 +725,18 @@ void cnstr_shdsc_gcm_encap(u32 * const desc, struct alginfo *cdata,
/* read assoc data */
append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | FIFOLDST_VLF |
FIFOLD_TYPE_AAD | FIFOLD_TYPE_LAST1);
+ if (is_qi)
+ /* jump to ICV writing */
+ append_jump(desc, JUMP_TEST_ALL | 2);
/* There is no input data */
set_jump_tgt_here(desc, zero_assoc_jump_cmd2);
+ if (is_qi)
+ append_seq_fifo_load(desc, ivsize, FIFOLD_CLASS_CLASS1 |
+ FIFOLD_TYPE_IV | FIFOLD_TYPE_FLUSH1 |
+ FIFOLD_TYPE_LAST1);
+
/* write ICV */
append_seq_store(desc, icvsize, LDST_CLASS_1_CCB |
LDST_SRCDST_BYTE_CONTEXT);
@@ -715,10 +753,13 @@ EXPORT_SYMBOL(cnstr_shdsc_gcm_encap);
* @desc: pointer to buffer used for descriptor construction
* @cdata: pointer to block cipher transform definitions
* Valid algorithm values - OP_ALG_ALGSEL_AES ANDed with OP_ALG_AAI_GCM.
+ * @ivsize: initialization vector size
* @icvsize: integrity check value (ICV) size (truncated or full)
+ * @is_qi: true when called from caam/qi
*/
void cnstr_shdsc_gcm_decap(u32 * const desc, struct alginfo *cdata,
- unsigned int icvsize)
+ unsigned int ivsize, unsigned int icvsize,
+ const bool is_qi)
{
u32 *key_jump_cmd, *zero_payload_jump_cmd, *zero_assoc_jump_cmd1;
@@ -739,6 +780,24 @@ void cnstr_shdsc_gcm_decap(u32 * const desc, struct alginfo *cdata,
append_operation(desc, cdata->algtype | OP_ALG_AS_INITFINAL |
OP_ALG_DECRYPT | OP_ALG_ICV_ON);
+ if (is_qi) {
+ u32 *wait_load_cmd;
+
+ /* REG3 = assoclen */
+ append_seq_load(desc, 4, LDST_CLASS_DECO |
+ LDST_SRCDST_WORD_DECO_MATH3 |
+ (4 << LDST_OFFSET_SHIFT));
+
+ wait_load_cmd = append_jump(desc, JUMP_JSL | JUMP_TEST_ALL |
+ JUMP_COND_CALM | JUMP_COND_NCP |
+ JUMP_COND_NOP | JUMP_COND_NIP |
+ JUMP_COND_NIFP);
+ set_jump_tgt_here(desc, wait_load_cmd);
+
+ append_seq_fifo_load(desc, ivsize, FIFOLD_CLASS_CLASS1 |
+ FIFOLD_TYPE_IV | FIFOLD_TYPE_FLUSH1);
+ }
+
/* if assoclen is ZERO, skip reading the assoc data */
append_math_add(desc, VARSEQINLEN, ZERO, REG3, CAAM_CMD_SZ);
zero_assoc_jump_cmd1 = append_jump(desc, JUMP_TEST_ALL |
@@ -791,10 +850,13 @@ EXPORT_SYMBOL(cnstr_shdsc_gcm_decap);
* @desc: pointer to buffer used for descriptor construction
* @cdata: pointer to block cipher transform definitions
* Valid algorithm values - OP_ALG_ALGSEL_AES ANDed with OP_ALG_AAI_GCM.
+ * @ivsize: initialization vector size
* @icvsize: integrity check value (ICV) size (truncated or full)
+ * @is_qi: true when called from caam/qi
*/
void cnstr_shdsc_rfc4106_encap(u32 * const desc, struct alginfo *cdata,
- unsigned int icvsize)
+ unsigned int ivsize, unsigned int icvsize,
+ const bool is_qi)
{
u32 *key_jump_cmd;
@@ -815,7 +877,29 @@ void cnstr_shdsc_rfc4106_encap(u32 * const desc, struct alginfo *cdata,
append_operation(desc, cdata->algtype | OP_ALG_AS_INITFINAL |
OP_ALG_ENCRYPT);
- append_math_sub_imm_u32(desc, VARSEQINLEN, REG3, IMM, 8);
+ if (is_qi) {
+ u32 *wait_load_cmd;
+
+ /* REG3 = assoclen */
+ append_seq_load(desc, 4, LDST_CLASS_DECO |
+ LDST_SRCDST_WORD_DECO_MATH3 |
+ (4 << LDST_OFFSET_SHIFT));
+
+ wait_load_cmd = append_jump(desc, JUMP_JSL | JUMP_TEST_ALL |
+ JUMP_COND_CALM | JUMP_COND_NCP |
+ JUMP_COND_NOP | JUMP_COND_NIP |
+ JUMP_COND_NIFP);
+ set_jump_tgt_here(desc, wait_load_cmd);
+
+ /* Read salt and IV */
+ append_fifo_load_as_imm(desc, (void *)(cdata->key_virt +
+ cdata->keylen), 4, FIFOLD_CLASS_CLASS1 |
+ FIFOLD_TYPE_IV);
+ append_seq_fifo_load(desc, ivsize, FIFOLD_CLASS_CLASS1 |
+ FIFOLD_TYPE_IV | FIFOLD_TYPE_FLUSH1);
+ }
+
+ append_math_sub_imm_u32(desc, VARSEQINLEN, REG3, IMM, ivsize);
append_math_add(desc, VARSEQOUTLEN, ZERO, REG3, CAAM_CMD_SZ);
/* Read assoc data */
@@ -823,7 +907,7 @@ void cnstr_shdsc_rfc4106_encap(u32 * const desc, struct alginfo *cdata,
FIFOLD_TYPE_AAD | FIFOLD_TYPE_FLUSH1);
/* Skip IV */
- append_seq_fifo_load(desc, 8, FIFOLD_CLASS_SKIP);
+ append_seq_fifo_load(desc, ivsize, FIFOLD_CLASS_SKIP);
/* Will read cryptlen bytes */
append_math_sub(desc, VARSEQINLEN, SEQINLEN, REG0, CAAM_CMD_SZ);
@@ -862,10 +946,13 @@ EXPORT_SYMBOL(cnstr_shdsc_rfc4106_encap);
* @desc: pointer to buffer used for descriptor construction
* @cdata: pointer to block cipher transform definitions
* Valid algorithm values - OP_ALG_ALGSEL_AES ANDed with OP_ALG_AAI_GCM.
+ * @ivsize: initialization vector size
* @icvsize: integrity check value (ICV) size (truncated or full)
+ * @is_qi: true when called from caam/qi
*/
void cnstr_shdsc_rfc4106_decap(u32 * const desc, struct alginfo *cdata,
- unsigned int icvsize)
+ unsigned int ivsize, unsigned int icvsize,
+ const bool is_qi)
{
u32 *key_jump_cmd;
@@ -887,7 +974,29 @@ void cnstr_shdsc_rfc4106_decap(u32 * const desc, struct alginfo *cdata,
append_operation(desc, cdata->algtype | OP_ALG_AS_INITFINAL |
OP_ALG_DECRYPT | OP_ALG_ICV_ON);
- append_math_sub_imm_u32(desc, VARSEQINLEN, REG3, IMM, 8);
+ if (is_qi) {
+ u32 *wait_load_cmd;
+
+ /* REG3 = assoclen */
+ append_seq_load(desc, 4, LDST_CLASS_DECO |
+ LDST_SRCDST_WORD_DECO_MATH3 |
+ (4 << LDST_OFFSET_SHIFT));
+
+ wait_load_cmd = append_jump(desc, JUMP_JSL | JUMP_TEST_ALL |
+ JUMP_COND_CALM | JUMP_COND_NCP |
+ JUMP_COND_NOP | JUMP_COND_NIP |
+ JUMP_COND_NIFP);
+ set_jump_tgt_here(desc, wait_load_cmd);
+
+ /* Read salt and IV */
+ append_fifo_load_as_imm(desc, (void *)(cdata->key_virt +
+ cdata->keylen), 4, FIFOLD_CLASS_CLASS1 |
+ FIFOLD_TYPE_IV);
+ append_seq_fifo_load(desc, ivsize, FIFOLD_CLASS_CLASS1 |
+ FIFOLD_TYPE_IV | FIFOLD_TYPE_FLUSH1);
+ }
+
+ append_math_sub_imm_u32(desc, VARSEQINLEN, REG3, IMM, ivsize);
append_math_add(desc, VARSEQOUTLEN, ZERO, REG3, CAAM_CMD_SZ);
/* Read assoc data */
@@ -895,7 +1004,7 @@ void cnstr_shdsc_rfc4106_decap(u32 * const desc, struct alginfo *cdata,
FIFOLD_TYPE_AAD | FIFOLD_TYPE_FLUSH1);
/* Skip IV */
- append_seq_fifo_load(desc, 8, FIFOLD_CLASS_SKIP);
+ append_seq_fifo_load(desc, ivsize, FIFOLD_CLASS_SKIP);
/* Will read cryptlen bytes */
append_math_sub(desc, VARSEQINLEN, SEQOUTLEN, REG3, CAAM_CMD_SZ);
@@ -934,10 +1043,13 @@ EXPORT_SYMBOL(cnstr_shdsc_rfc4106_decap);
* @desc: pointer to buffer used for descriptor construction
* @cdata: pointer to block cipher transform definitions
* Valid algorithm values - OP_ALG_ALGSEL_AES ANDed with OP_ALG_AAI_GCM.
+ * @ivsize: initialization vector size
* @icvsize: integrity check value (ICV) size (truncated or full)
+ * @is_qi: true when called from caam/qi
*/
void cnstr_shdsc_rfc4543_encap(u32 * const desc, struct alginfo *cdata,
- unsigned int icvsize)
+ unsigned int ivsize, unsigned int icvsize,
+ const bool is_qi)
{
u32 *key_jump_cmd, *read_move_cmd, *write_move_cmd;
@@ -958,6 +1070,18 @@ void cnstr_shdsc_rfc4543_encap(u32 * const desc, struct alginfo *cdata,
append_operation(desc, cdata->algtype | OP_ALG_AS_INITFINAL |
OP_ALG_ENCRYPT);
+ if (is_qi) {
+ /* assoclen is not needed, skip it */
+ append_seq_fifo_load(desc, 4, FIFOLD_CLASS_SKIP);
+
+ /* Read salt and IV */
+ append_fifo_load_as_imm(desc, (void *)(cdata->key_virt +
+ cdata->keylen), 4, FIFOLD_CLASS_CLASS1 |
+ FIFOLD_TYPE_IV);
+ append_seq_fifo_load(desc, ivsize, FIFOLD_CLASS_CLASS1 |
+ FIFOLD_TYPE_IV | FIFOLD_TYPE_FLUSH1);
+ }
+
/* assoclen + cryptlen = seqinlen */
append_math_sub(desc, REG3, SEQINLEN, REG0, CAAM_CMD_SZ);
@@ -1004,10 +1128,13 @@ EXPORT_SYMBOL(cnstr_shdsc_rfc4543_encap);
* @desc: pointer to buffer used for descriptor construction
* @cdata: pointer to block cipher transform definitions
* Valid algorithm values - OP_ALG_ALGSEL_AES ANDed with OP_ALG_AAI_GCM.
+ * @ivsize: initialization vector size
* @icvsize: integrity check value (ICV) size (truncated or full)
+ * @is_qi: true when called from caam/qi
*/
void cnstr_shdsc_rfc4543_decap(u32 * const desc, struct alginfo *cdata,
- unsigned int icvsize)
+ unsigned int ivsize, unsigned int icvsize,
+ const bool is_qi)
{
u32 *key_jump_cmd, *read_move_cmd, *write_move_cmd;
@@ -1028,6 +1155,18 @@ void cnstr_shdsc_rfc4543_decap(u32 * const desc, struct alginfo *cdata,
append_operation(desc, cdata->algtype | OP_ALG_AS_INITFINAL |
OP_ALG_DECRYPT | OP_ALG_ICV_ON);
+ if (is_qi) {
+ /* assoclen is not needed, skip it */
+ append_seq_fifo_load(desc, 4, FIFOLD_CLASS_SKIP);
+
+ /* Read salt and IV */
+ append_fifo_load_as_imm(desc, (void *)(cdata->key_virt +
+ cdata->keylen), 4, FIFOLD_CLASS_CLASS1 |
+ FIFOLD_TYPE_IV);
+ append_seq_fifo_load(desc, ivsize, FIFOLD_CLASS_CLASS1 |
+ FIFOLD_TYPE_IV | FIFOLD_TYPE_FLUSH1);
+ }
+
/* assoclen + cryptlen = seqoutlen */
append_math_sub(desc, REG3, SEQOUTLEN, REG0, CAAM_CMD_SZ);
diff --git a/drivers/crypto/caam/caamalg_desc.h b/drivers/crypto/caam/caamalg_desc.h
index 5f9445ae2114..a917af5776ce 100644
--- a/drivers/crypto/caam/caamalg_desc.h
+++ b/drivers/crypto/caam/caamalg_desc.h
@@ -27,14 +27,20 @@
#define DESC_GCM_BASE (3 * CAAM_CMD_SZ)
#define DESC_GCM_ENC_LEN (DESC_GCM_BASE + 16 * CAAM_CMD_SZ)
#define DESC_GCM_DEC_LEN (DESC_GCM_BASE + 12 * CAAM_CMD_SZ)
+#define DESC_QI_GCM_ENC_LEN (DESC_GCM_ENC_LEN + 6 * CAAM_CMD_SZ)
+#define DESC_QI_GCM_DEC_LEN (DESC_GCM_DEC_LEN + 3 * CAAM_CMD_SZ)
#define DESC_RFC4106_BASE (3 * CAAM_CMD_SZ)
#define DESC_RFC4106_ENC_LEN (DESC_RFC4106_BASE + 13 * CAAM_CMD_SZ)
#define DESC_RFC4106_DEC_LEN (DESC_RFC4106_BASE + 13 * CAAM_CMD_SZ)
+#define DESC_QI_RFC4106_ENC_LEN (DESC_RFC4106_ENC_LEN + 5 * CAAM_CMD_SZ)
+#define DESC_QI_RFC4106_DEC_LEN (DESC_RFC4106_DEC_LEN + 5 * CAAM_CMD_SZ)
#define DESC_RFC4543_BASE (3 * CAAM_CMD_SZ)
#define DESC_RFC4543_ENC_LEN (DESC_RFC4543_BASE + 11 * CAAM_CMD_SZ)
#define DESC_RFC4543_DEC_LEN (DESC_RFC4543_BASE + 12 * CAAM_CMD_SZ)
+#define DESC_QI_RFC4543_ENC_LEN (DESC_RFC4543_ENC_LEN + 4 * CAAM_CMD_SZ)
+#define DESC_QI_RFC4543_DEC_LEN (DESC_RFC4543_DEC_LEN + 4 * CAAM_CMD_SZ)
#define DESC_ABLKCIPHER_BASE (3 * CAAM_CMD_SZ)
#define DESC_ABLKCIPHER_ENC_LEN (DESC_ABLKCIPHER_BASE + \
@@ -67,22 +73,28 @@ void cnstr_shdsc_aead_givencap(u32 * const desc, struct alginfo *cdata,
const bool is_qi, int era);
void cnstr_shdsc_gcm_encap(u32 * const desc, struct alginfo *cdata,
- unsigned int icvsize);
+ unsigned int ivsize, unsigned int icvsize,
+ const bool is_qi);
void cnstr_shdsc_gcm_decap(u32 * const desc, struct alginfo *cdata,
- unsigned int icvsize);
+ unsigned int ivsize, unsigned int icvsize,
+ const bool is_qi);
void cnstr_shdsc_rfc4106_encap(u32 * const desc, struct alginfo *cdata,
- unsigned int icvsize);
+ unsigned int ivsize, unsigned int icvsize,
+ const bool is_qi);
void cnstr_shdsc_rfc4106_decap(u32 * const desc, struct alginfo *cdata,
- unsigned int icvsize);
+ unsigned int ivsize, unsigned int icvsize,
+ const bool is_qi);
void cnstr_shdsc_rfc4543_encap(u32 * const desc, struct alginfo *cdata,
- unsigned int icvsize);
+ unsigned int ivsize, unsigned int icvsize,
+ const bool is_qi);
void cnstr_shdsc_rfc4543_decap(u32 * const desc, struct alginfo *cdata,
- unsigned int icvsize);
+ unsigned int ivsize, unsigned int icvsize,
+ const bool is_qi);
void cnstr_shdsc_ablkcipher_encap(u32 * const desc, struct alginfo *cdata,
unsigned int ivsize, const bool is_rfc3686,
diff --git a/drivers/crypto/caam/caamalg_qi.c b/drivers/crypto/caam/caamalg_qi.c
index 4aecc9435f69..cacda0831390 100644
--- a/drivers/crypto/caam/caamalg_qi.c
+++ b/drivers/crypto/caam/caamalg_qi.c
@@ -278,12 +278,317 @@ skip_split_key:
}
}
+ memzero_explicit(&keys, sizeof(keys));
return ret;
badkey:
crypto_aead_set_flags(aead, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ memzero_explicit(&keys, sizeof(keys));
return -EINVAL;
}
+static int gcm_set_sh_desc(struct crypto_aead *aead)
+{
+ struct caam_ctx *ctx = crypto_aead_ctx(aead);
+ unsigned int ivsize = crypto_aead_ivsize(aead);
+ int rem_bytes = CAAM_DESC_BYTES_MAX - DESC_JOB_IO_LEN -
+ ctx->cdata.keylen;
+
+ if (!ctx->cdata.keylen || !ctx->authsize)
+ return 0;
+
+ /*
+ * Job Descriptor and Shared Descriptor
+ * must fit into the 64-word Descriptor h/w Buffer
+ */
+ if (rem_bytes >= DESC_QI_GCM_ENC_LEN) {
+ ctx->cdata.key_inline = true;
+ ctx->cdata.key_virt = ctx->key;
+ } else {
+ ctx->cdata.key_inline = false;
+ ctx->cdata.key_dma = ctx->key_dma;
+ }
+
+ cnstr_shdsc_gcm_encap(ctx->sh_desc_enc, &ctx->cdata, ivsize,
+ ctx->authsize, true);
+
+ /*
+ * Job Descriptor and Shared Descriptor
+ * must fit into the 64-word Descriptor h/w Buffer
+ */
+ if (rem_bytes >= DESC_QI_GCM_DEC_LEN) {
+ ctx->cdata.key_inline = true;
+ ctx->cdata.key_virt = ctx->key;
+ } else {
+ ctx->cdata.key_inline = false;
+ ctx->cdata.key_dma = ctx->key_dma;
+ }
+
+ cnstr_shdsc_gcm_decap(ctx->sh_desc_dec, &ctx->cdata, ivsize,
+ ctx->authsize, true);
+
+ return 0;
+}
+
+static int gcm_setauthsize(struct crypto_aead *authenc, unsigned int authsize)
+{
+ struct caam_ctx *ctx = crypto_aead_ctx(authenc);
+
+ ctx->authsize = authsize;
+ gcm_set_sh_desc(authenc);
+
+ return 0;
+}
+
+static int gcm_setkey(struct crypto_aead *aead,
+ const u8 *key, unsigned int keylen)
+{
+ struct caam_ctx *ctx = crypto_aead_ctx(aead);
+ struct device *jrdev = ctx->jrdev;
+ int ret;
+
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "key in @" __stringify(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1);
+#endif
+
+ memcpy(ctx->key, key, keylen);
+ dma_sync_single_for_device(jrdev, ctx->key_dma, keylen, ctx->dir);
+ ctx->cdata.keylen = keylen;
+
+ ret = gcm_set_sh_desc(aead);
+ if (ret)
+ return ret;
+
+ /* Now update the driver contexts with the new shared descriptor */
+ if (ctx->drv_ctx[ENCRYPT]) {
+ ret = caam_drv_ctx_update(ctx->drv_ctx[ENCRYPT],
+ ctx->sh_desc_enc);
+ if (ret) {
+ dev_err(jrdev, "driver enc context update failed\n");
+ return ret;
+ }
+ }
+
+ if (ctx->drv_ctx[DECRYPT]) {
+ ret = caam_drv_ctx_update(ctx->drv_ctx[DECRYPT],
+ ctx->sh_desc_dec);
+ if (ret) {
+ dev_err(jrdev, "driver dec context update failed\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int rfc4106_set_sh_desc(struct crypto_aead *aead)
+{
+ struct caam_ctx *ctx = crypto_aead_ctx(aead);
+ unsigned int ivsize = crypto_aead_ivsize(aead);
+ int rem_bytes = CAAM_DESC_BYTES_MAX - DESC_JOB_IO_LEN -
+ ctx->cdata.keylen;
+
+ if (!ctx->cdata.keylen || !ctx->authsize)
+ return 0;
+
+ ctx->cdata.key_virt = ctx->key;
+
+ /*
+ * Job Descriptor and Shared Descriptor
+ * must fit into the 64-word Descriptor h/w Buffer
+ */
+ if (rem_bytes >= DESC_QI_RFC4106_ENC_LEN) {
+ ctx->cdata.key_inline = true;
+ } else {
+ ctx->cdata.key_inline = false;
+ ctx->cdata.key_dma = ctx->key_dma;
+ }
+
+ cnstr_shdsc_rfc4106_encap(ctx->sh_desc_enc, &ctx->cdata, ivsize,
+ ctx->authsize, true);
+
+ /*
+ * Job Descriptor and Shared Descriptor
+ * must fit into the 64-word Descriptor h/w Buffer
+ */
+ if (rem_bytes >= DESC_QI_RFC4106_DEC_LEN) {
+ ctx->cdata.key_inline = true;
+ } else {
+ ctx->cdata.key_inline = false;
+ ctx->cdata.key_dma = ctx->key_dma;
+ }
+
+ cnstr_shdsc_rfc4106_decap(ctx->sh_desc_dec, &ctx->cdata, ivsize,
+ ctx->authsize, true);
+
+ return 0;
+}
+
+static int rfc4106_setauthsize(struct crypto_aead *authenc,
+ unsigned int authsize)
+{
+ struct caam_ctx *ctx = crypto_aead_ctx(authenc);
+
+ ctx->authsize = authsize;
+ rfc4106_set_sh_desc(authenc);
+
+ return 0;
+}
+
+static int rfc4106_setkey(struct crypto_aead *aead,
+ const u8 *key, unsigned int keylen)
+{
+ struct caam_ctx *ctx = crypto_aead_ctx(aead);
+ struct device *jrdev = ctx->jrdev;
+ int ret;
+
+ if (keylen < 4)
+ return -EINVAL;
+
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "key in @" __stringify(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1);
+#endif
+
+ memcpy(ctx->key, key, keylen);
+ /*
+ * The last four bytes of the key material are used as the salt value
+ * in the nonce. Update the AES key length.
+ */
+ ctx->cdata.keylen = keylen - 4;
+ dma_sync_single_for_device(jrdev, ctx->key_dma, ctx->cdata.keylen,
+ ctx->dir);
+
+ ret = rfc4106_set_sh_desc(aead);
+ if (ret)
+ return ret;
+
+ /* Now update the driver contexts with the new shared descriptor */
+ if (ctx->drv_ctx[ENCRYPT]) {
+ ret = caam_drv_ctx_update(ctx->drv_ctx[ENCRYPT],
+ ctx->sh_desc_enc);
+ if (ret) {
+ dev_err(jrdev, "driver enc context update failed\n");
+ return ret;
+ }
+ }
+
+ if (ctx->drv_ctx[DECRYPT]) {
+ ret = caam_drv_ctx_update(ctx->drv_ctx[DECRYPT],
+ ctx->sh_desc_dec);
+ if (ret) {
+ dev_err(jrdev, "driver dec context update failed\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int rfc4543_set_sh_desc(struct crypto_aead *aead)
+{
+ struct caam_ctx *ctx = crypto_aead_ctx(aead);
+ unsigned int ivsize = crypto_aead_ivsize(aead);
+ int rem_bytes = CAAM_DESC_BYTES_MAX - DESC_JOB_IO_LEN -
+ ctx->cdata.keylen;
+
+ if (!ctx->cdata.keylen || !ctx->authsize)
+ return 0;
+
+ ctx->cdata.key_virt = ctx->key;
+
+ /*
+ * Job Descriptor and Shared Descriptor
+ * must fit into the 64-word Descriptor h/w Buffer
+ */
+ if (rem_bytes >= DESC_QI_RFC4543_ENC_LEN) {
+ ctx->cdata.key_inline = true;
+ } else {
+ ctx->cdata.key_inline = false;
+ ctx->cdata.key_dma = ctx->key_dma;
+ }
+
+ cnstr_shdsc_rfc4543_encap(ctx->sh_desc_enc, &ctx->cdata, ivsize,
+ ctx->authsize, true);
+
+ /*
+ * Job Descriptor and Shared Descriptor
+ * must fit into the 64-word Descriptor h/w Buffer
+ */
+ if (rem_bytes >= DESC_QI_RFC4543_DEC_LEN) {
+ ctx->cdata.key_inline = true;
+ } else {
+ ctx->cdata.key_inline = false;
+ ctx->cdata.key_dma = ctx->key_dma;
+ }
+
+ cnstr_shdsc_rfc4543_decap(ctx->sh_desc_dec, &ctx->cdata, ivsize,
+ ctx->authsize, true);
+
+ return 0;
+}
+
+static int rfc4543_setauthsize(struct crypto_aead *authenc,
+ unsigned int authsize)
+{
+ struct caam_ctx *ctx = crypto_aead_ctx(authenc);
+
+ ctx->authsize = authsize;
+ rfc4543_set_sh_desc(authenc);
+
+ return 0;
+}
+
+static int rfc4543_setkey(struct crypto_aead *aead,
+ const u8 *key, unsigned int keylen)
+{
+ struct caam_ctx *ctx = crypto_aead_ctx(aead);
+ struct device *jrdev = ctx->jrdev;
+ int ret;
+
+ if (keylen < 4)
+ return -EINVAL;
+
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "key in @" __stringify(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1);
+#endif
+
+ memcpy(ctx->key, key, keylen);
+ /*
+ * The last four bytes of the key material are used as the salt value
+ * in the nonce. Update the AES key length.
+ */
+ ctx->cdata.keylen = keylen - 4;
+ dma_sync_single_for_device(jrdev, ctx->key_dma, ctx->cdata.keylen,
+ ctx->dir);
+
+ ret = rfc4543_set_sh_desc(aead);
+ if (ret)
+ return ret;
+
+ /* Now update the driver contexts with the new shared descriptor */
+ if (ctx->drv_ctx[ENCRYPT]) {
+ ret = caam_drv_ctx_update(ctx->drv_ctx[ENCRYPT],
+ ctx->sh_desc_enc);
+ if (ret) {
+ dev_err(jrdev, "driver enc context update failed\n");
+ return ret;
+ }
+ }
+
+ if (ctx->drv_ctx[DECRYPT]) {
+ ret = caam_drv_ctx_update(ctx->drv_ctx[DECRYPT],
+ ctx->sh_desc_dec);
+ if (ret) {
+ dev_err(jrdev, "driver dec context update failed\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
static int ablkcipher_setkey(struct crypto_ablkcipher *ablkcipher,
const u8 *key, unsigned int keylen)
{
@@ -562,8 +867,18 @@ static void aead_done(struct caam_drv_req *drv_req, u32 status)
qidev = caam_ctx->qidev;
if (unlikely(status)) {
+ u32 ssrc = status & JRSTA_SSRC_MASK;
+ u8 err_id = status & JRSTA_CCBERR_ERRID_MASK;
+
caam_jr_strstatus(qidev, status);
- ecode = -EIO;
+ /*
+ * verify hw auth check passed else return -EBADMSG
+ */
+ if (ssrc == JRSTA_SSRC_CCB_ERROR &&
+ err_id == JRSTA_CCBERR_ERRID_ICVCHK)
+ ecode = -EBADMSG;
+ else
+ ecode = -EIO;
}
edesc = container_of(drv_req, typeof(*edesc), drv_req);
@@ -807,6 +1122,22 @@ static int aead_decrypt(struct aead_request *req)
return aead_crypt(req, false);
}
+static int ipsec_gcm_encrypt(struct aead_request *req)
+{
+ if (req->assoclen < 8)
+ return -EINVAL;
+
+ return aead_crypt(req, true);
+}
+
+static int ipsec_gcm_decrypt(struct aead_request *req)
+{
+ if (req->assoclen < 8)
+ return -EINVAL;
+
+ return aead_crypt(req, false);
+}
+
static void ablkcipher_done(struct caam_drv_req *drv_req, u32 status)
{
struct ablkcipher_edesc *edesc;
@@ -1327,6 +1658,61 @@ static struct caam_alg_template driver_algs[] = {
};
static struct caam_aead_alg driver_aeads[] = {
+ {
+ .aead = {
+ .base = {
+ .cra_name = "rfc4106(gcm(aes))",
+ .cra_driver_name = "rfc4106-gcm-aes-caam-qi",
+ .cra_blocksize = 1,
+ },
+ .setkey = rfc4106_setkey,
+ .setauthsize = rfc4106_setauthsize,
+ .encrypt = ipsec_gcm_encrypt,
+ .decrypt = ipsec_gcm_decrypt,
+ .ivsize = 8,
+ .maxauthsize = AES_BLOCK_SIZE,
+ },
+ .caam = {
+ .class1_alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_GCM,
+ },
+ },
+ {
+ .aead = {
+ .base = {
+ .cra_name = "rfc4543(gcm(aes))",
+ .cra_driver_name = "rfc4543-gcm-aes-caam-qi",
+ .cra_blocksize = 1,
+ },
+ .setkey = rfc4543_setkey,
+ .setauthsize = rfc4543_setauthsize,
+ .encrypt = ipsec_gcm_encrypt,
+ .decrypt = ipsec_gcm_decrypt,
+ .ivsize = 8,
+ .maxauthsize = AES_BLOCK_SIZE,
+ },
+ .caam = {
+ .class1_alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_GCM,
+ },
+ },
+ /* Galois Counter Mode */
+ {
+ .aead = {
+ .base = {
+ .cra_name = "gcm(aes)",
+ .cra_driver_name = "gcm-aes-caam-qi",
+ .cra_blocksize = 1,
+ },
+ .setkey = gcm_setkey,
+ .setauthsize = gcm_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .ivsize = 12,
+ .maxauthsize = AES_BLOCK_SIZE,
+ },
+ .caam = {
+ .class1_alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_GCM,
+ }
+ },
/* single-pass ipsec_esp descriptor */
{
.aead = {
diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c
index e843cf410373..e4cc636e1104 100644
--- a/drivers/crypto/caam/ctrl.c
+++ b/drivers/crypto/caam/ctrl.c
@@ -337,7 +337,8 @@ static int caam_remove(struct platform_device *pdev)
/* shut clocks off before finalizing shutdown */
clk_disable_unprepare(ctrlpriv->caam_ipg);
- clk_disable_unprepare(ctrlpriv->caam_mem);
+ if (ctrlpriv->caam_mem)
+ clk_disable_unprepare(ctrlpriv->caam_mem);
clk_disable_unprepare(ctrlpriv->caam_aclk);
if (ctrlpriv->caam_emi_slow)
clk_disable_unprepare(ctrlpriv->caam_emi_slow);
@@ -466,14 +467,17 @@ static int caam_probe(struct platform_device *pdev)
}
ctrlpriv->caam_ipg = clk;
- clk = caam_drv_identify_clk(&pdev->dev, "mem");
- if (IS_ERR(clk)) {
- ret = PTR_ERR(clk);
- dev_err(&pdev->dev,
- "can't identify CAAM mem clk: %d\n", ret);
- return ret;
+ if (!of_machine_is_compatible("fsl,imx7d") &&
+ !of_machine_is_compatible("fsl,imx7s")) {
+ clk = caam_drv_identify_clk(&pdev->dev, "mem");
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ dev_err(&pdev->dev,
+ "can't identify CAAM mem clk: %d\n", ret);
+ return ret;
+ }
+ ctrlpriv->caam_mem = clk;
}
- ctrlpriv->caam_mem = clk;
clk = caam_drv_identify_clk(&pdev->dev, "aclk");
if (IS_ERR(clk)) {
@@ -484,7 +488,9 @@ static int caam_probe(struct platform_device *pdev)
}
ctrlpriv->caam_aclk = clk;
- if (!of_machine_is_compatible("fsl,imx6ul")) {
+ if (!of_machine_is_compatible("fsl,imx6ul") &&
+ !of_machine_is_compatible("fsl,imx7d") &&
+ !of_machine_is_compatible("fsl,imx7s")) {
clk = caam_drv_identify_clk(&pdev->dev, "emi_slow");
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
@@ -501,11 +507,13 @@ static int caam_probe(struct platform_device *pdev)
return ret;
}
- ret = clk_prepare_enable(ctrlpriv->caam_mem);
- if (ret < 0) {
- dev_err(&pdev->dev, "can't enable CAAM secure mem clock: %d\n",
- ret);
- goto disable_caam_ipg;
+ if (ctrlpriv->caam_mem) {
+ ret = clk_prepare_enable(ctrlpriv->caam_mem);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "can't enable CAAM secure mem clock: %d\n",
+ ret);
+ goto disable_caam_ipg;
+ }
}
ret = clk_prepare_enable(ctrlpriv->caam_aclk);
@@ -815,9 +823,6 @@ static int caam_probe(struct platform_device *pdev)
return 0;
caam_remove:
-#ifdef CONFIG_DEBUG_FS
- debugfs_remove_recursive(ctrlpriv->dfs_root);
-#endif
caam_remove(pdev);
return ret;
@@ -829,7 +834,8 @@ disable_caam_emi_slow:
disable_caam_aclk:
clk_disable_unprepare(ctrlpriv->caam_aclk);
disable_caam_mem:
- clk_disable_unprepare(ctrlpriv->caam_mem);
+ if (ctrlpriv->caam_mem)
+ clk_disable_unprepare(ctrlpriv->caam_mem);
disable_caam_ipg:
clk_disable_unprepare(ctrlpriv->caam_ipg);
return ret;
diff --git a/drivers/crypto/caam/qi.c b/drivers/crypto/caam/qi.c
index f9a44f485aac..b9480828da38 100644
--- a/drivers/crypto/caam/qi.c
+++ b/drivers/crypto/caam/qi.c
@@ -579,8 +579,15 @@ static enum qman_cb_dqrr_result caam_rsp_fq_dqrr_cb(struct qman_portal *p,
fd = &dqrr->fd;
status = be32_to_cpu(fd->status);
- if (unlikely(status))
- dev_err(qidev, "Error: %#x in CAAM response FD\n", status);
+ if (unlikely(status)) {
+ u32 ssrc = status & JRSTA_SSRC_MASK;
+ u8 err_id = status & JRSTA_CCBERR_ERRID_MASK;
+
+ if (ssrc != JRSTA_SSRC_CCB_ERROR ||
+ err_id != JRSTA_CCBERR_ERRID_ICVCHK)
+ dev_err(qidev, "Error: %#x in CAAM response FD\n",
+ status);
+ }
if (unlikely(qm_fd_get_format(fd) != qm_fd_compound)) {
dev_err(qidev, "Non-compound FD from CAAM\n");
diff --git a/drivers/crypto/cavium/cpt/cptpf_main.c b/drivers/crypto/cavium/cpt/cptpf_main.c
index 34a6d8bf229e..06ad85ab5e86 100644
--- a/drivers/crypto/cavium/cpt/cptpf_main.c
+++ b/drivers/crypto/cavium/cpt/cptpf_main.c
@@ -436,7 +436,7 @@ static int cpt_device_init(struct cpt_device *cpt)
/* Reset the PF when probed first */
cpt_reset(cpt);
- mdelay(100);
+ msleep(100);
/*Check BIST status*/
bist = (u64)cpt_check_bist_status(cpt);
diff --git a/drivers/crypto/ccp/ccp-crypto-aes-cmac.c b/drivers/crypto/ccp/ccp-crypto-aes-cmac.c
index 60fc0fa26fd3..26687f318de6 100644
--- a/drivers/crypto/ccp/ccp-crypto-aes-cmac.c
+++ b/drivers/crypto/ccp/ccp-crypto-aes-cmac.c
@@ -46,7 +46,7 @@ static int ccp_aes_cmac_complete(struct crypto_async_request *async_req,
}
/* Update result area if supplied */
- if (req->result)
+ if (req->result && rctx->final)
memcpy(req->result, rctx->iv, digest_size);
e_free:
diff --git a/drivers/crypto/ccp/ccp-crypto-rsa.c b/drivers/crypto/ccp/ccp-crypto-rsa.c
index e6db8672d89c..05850dfd7940 100644
--- a/drivers/crypto/ccp/ccp-crypto-rsa.c
+++ b/drivers/crypto/ccp/ccp-crypto-rsa.c
@@ -60,10 +60,9 @@ static int ccp_rsa_complete(struct crypto_async_request *async_req, int ret)
static unsigned int ccp_rsa_maxsize(struct crypto_akcipher *tfm)
{
- if (ccp_version() > CCP_VERSION(3, 0))
- return CCP5_RSA_MAXMOD;
- else
- return CCP_RSA_MAXMOD;
+ struct ccp_ctx *ctx = akcipher_tfm_ctx(tfm);
+
+ return ctx->u.rsa.n_len;
}
static int ccp_rsa_crypt(struct akcipher_request *req, bool encrypt)
diff --git a/drivers/crypto/ccp/ccp-crypto-sha.c b/drivers/crypto/ccp/ccp-crypto-sha.c
index 8b9b16d433f7..871c9628a2ee 100644
--- a/drivers/crypto/ccp/ccp-crypto-sha.c
+++ b/drivers/crypto/ccp/ccp-crypto-sha.c
@@ -47,7 +47,7 @@ static int ccp_sha_complete(struct crypto_async_request *async_req, int ret)
}
/* Update result area if supplied */
- if (req->result)
+ if (req->result && rctx->final)
memcpy(req->result, rctx->ctx, digest_size);
e_free:
diff --git a/drivers/crypto/ccp/ccp-debugfs.c b/drivers/crypto/ccp/ccp-debugfs.c
index 59d4ca4e72d8..1a734bd2070a 100644
--- a/drivers/crypto/ccp/ccp-debugfs.c
+++ b/drivers/crypto/ccp/ccp-debugfs.c
@@ -278,7 +278,7 @@ static const struct file_operations ccp_debugfs_stats_ops = {
};
static struct dentry *ccp_debugfs_dir;
-static DEFINE_RWLOCK(ccp_debugfs_lock);
+static DEFINE_MUTEX(ccp_debugfs_lock);
#define MAX_NAME_LEN 20
@@ -290,16 +290,15 @@ void ccp5_debugfs_setup(struct ccp_device *ccp)
struct dentry *debugfs_stats;
struct dentry *debugfs_q_instance;
struct dentry *debugfs_q_stats;
- unsigned long flags;
int i;
if (!debugfs_initialized())
return;
- write_lock_irqsave(&ccp_debugfs_lock, flags);
+ mutex_lock(&ccp_debugfs_lock);
if (!ccp_debugfs_dir)
ccp_debugfs_dir = debugfs_create_dir(KBUILD_MODNAME, NULL);
- write_unlock_irqrestore(&ccp_debugfs_lock, flags);
+ mutex_unlock(&ccp_debugfs_lock);
if (!ccp_debugfs_dir)
return;
diff --git a/drivers/crypto/ccp/ccp-dmaengine.c b/drivers/crypto/ccp/ccp-dmaengine.c
index 8b9da58459df..67155cb21636 100644
--- a/drivers/crypto/ccp/ccp-dmaengine.c
+++ b/drivers/crypto/ccp/ccp-dmaengine.c
@@ -38,7 +38,7 @@ static unsigned int dma_chan_attr = CCP_DMA_DFLT;
module_param(dma_chan_attr, uint, 0444);
MODULE_PARM_DESC(dma_chan_attr, "Set DMA channel visibility: 0 (default) = device defaults, 1 = make private, 2 = make public");
-unsigned int ccp_get_dma_chan_attr(struct ccp_device *ccp)
+static unsigned int ccp_get_dma_chan_attr(struct ccp_device *ccp)
{
switch (dma_chan_attr) {
case CCP_DMA_DFLT:
diff --git a/drivers/crypto/ccp/ccp-ops.c b/drivers/crypto/ccp/ccp-ops.c
index 406b95329b3d..0ea43cdeb05f 100644
--- a/drivers/crypto/ccp/ccp-ops.c
+++ b/drivers/crypto/ccp/ccp-ops.c
@@ -178,14 +178,18 @@ static int ccp_init_dm_workarea(struct ccp_dm_workarea *wa,
return 0;
}
-static void ccp_set_dm_area(struct ccp_dm_workarea *wa, unsigned int wa_offset,
- struct scatterlist *sg, unsigned int sg_offset,
- unsigned int len)
+static int ccp_set_dm_area(struct ccp_dm_workarea *wa, unsigned int wa_offset,
+ struct scatterlist *sg, unsigned int sg_offset,
+ unsigned int len)
{
WARN_ON(!wa->address);
+ if (len > (wa->length - wa_offset))
+ return -EINVAL;
+
scatterwalk_map_and_copy(wa->address + wa_offset, sg, sg_offset, len,
0);
+ return 0;
}
static void ccp_get_dm_area(struct ccp_dm_workarea *wa, unsigned int wa_offset,
@@ -205,8 +209,11 @@ static int ccp_reverse_set_dm_area(struct ccp_dm_workarea *wa,
unsigned int len)
{
u8 *p, *q;
+ int rc;
- ccp_set_dm_area(wa, wa_offset, sg, sg_offset, len);
+ rc = ccp_set_dm_area(wa, wa_offset, sg, sg_offset, len);
+ if (rc)
+ return rc;
p = wa->address + wa_offset;
q = p + len - 1;
@@ -509,7 +516,9 @@ static int ccp_run_aes_cmac_cmd(struct ccp_cmd_queue *cmd_q,
return ret;
dm_offset = CCP_SB_BYTES - aes->key_len;
- ccp_set_dm_area(&key, dm_offset, aes->key, 0, aes->key_len);
+ ret = ccp_set_dm_area(&key, dm_offset, aes->key, 0, aes->key_len);
+ if (ret)
+ goto e_key;
ret = ccp_copy_to_sb(cmd_q, &key, op.jobid, op.sb_key,
CCP_PASSTHRU_BYTESWAP_256BIT);
if (ret) {
@@ -528,7 +537,9 @@ static int ccp_run_aes_cmac_cmd(struct ccp_cmd_queue *cmd_q,
goto e_key;
dm_offset = CCP_SB_BYTES - AES_BLOCK_SIZE;
- ccp_set_dm_area(&ctx, dm_offset, aes->iv, 0, aes->iv_len);
+ ret = ccp_set_dm_area(&ctx, dm_offset, aes->iv, 0, aes->iv_len);
+ if (ret)
+ goto e_ctx;
ret = ccp_copy_to_sb(cmd_q, &ctx, op.jobid, op.sb_ctx,
CCP_PASSTHRU_BYTESWAP_256BIT);
if (ret) {
@@ -556,8 +567,10 @@ static int ccp_run_aes_cmac_cmd(struct ccp_cmd_queue *cmd_q,
goto e_src;
}
- ccp_set_dm_area(&ctx, 0, aes->cmac_key, 0,
- aes->cmac_key_len);
+ ret = ccp_set_dm_area(&ctx, 0, aes->cmac_key, 0,
+ aes->cmac_key_len);
+ if (ret)
+ goto e_src;
ret = ccp_copy_to_sb(cmd_q, &ctx, op.jobid, op.sb_ctx,
CCP_PASSTHRU_BYTESWAP_256BIT);
if (ret) {
@@ -666,7 +679,9 @@ static int ccp_run_aes_gcm_cmd(struct ccp_cmd_queue *cmd_q,
return ret;
dm_offset = CCP_SB_BYTES - aes->key_len;
- ccp_set_dm_area(&key, dm_offset, aes->key, 0, aes->key_len);
+ ret = ccp_set_dm_area(&key, dm_offset, aes->key, 0, aes->key_len);
+ if (ret)
+ goto e_key;
ret = ccp_copy_to_sb(cmd_q, &key, op.jobid, op.sb_key,
CCP_PASSTHRU_BYTESWAP_256BIT);
if (ret) {
@@ -685,7 +700,9 @@ static int ccp_run_aes_gcm_cmd(struct ccp_cmd_queue *cmd_q,
goto e_key;
dm_offset = CCP_AES_CTX_SB_COUNT * CCP_SB_BYTES - aes->iv_len;
- ccp_set_dm_area(&ctx, dm_offset, aes->iv, 0, aes->iv_len);
+ ret = ccp_set_dm_area(&ctx, dm_offset, aes->iv, 0, aes->iv_len);
+ if (ret)
+ goto e_ctx;
ret = ccp_copy_to_sb(cmd_q, &ctx, op.jobid, op.sb_ctx,
CCP_PASSTHRU_BYTESWAP_256BIT);
@@ -777,7 +794,9 @@ static int ccp_run_aes_gcm_cmd(struct ccp_cmd_queue *cmd_q,
goto e_dst;
}
- ccp_set_dm_area(&ctx, dm_offset, aes->iv, 0, aes->iv_len);
+ ret = ccp_set_dm_area(&ctx, dm_offset, aes->iv, 0, aes->iv_len);
+ if (ret)
+ goto e_dst;
ret = ccp_copy_to_sb(cmd_q, &ctx, op.jobid, op.sb_ctx,
CCP_PASSTHRU_BYTESWAP_256BIT);
@@ -820,7 +839,9 @@ static int ccp_run_aes_gcm_cmd(struct ccp_cmd_queue *cmd_q,
DMA_BIDIRECTIONAL);
if (ret)
goto e_tag;
- ccp_set_dm_area(&tag, 0, p_tag, 0, AES_BLOCK_SIZE);
+ ret = ccp_set_dm_area(&tag, 0, p_tag, 0, AES_BLOCK_SIZE);
+ if (ret)
+ goto e_tag;
ret = memcmp(tag.address, final_wa.address, AES_BLOCK_SIZE);
ccp_dm_free(&tag);
@@ -914,7 +935,9 @@ static int ccp_run_aes_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd)
return ret;
dm_offset = CCP_SB_BYTES - aes->key_len;
- ccp_set_dm_area(&key, dm_offset, aes->key, 0, aes->key_len);
+ ret = ccp_set_dm_area(&key, dm_offset, aes->key, 0, aes->key_len);
+ if (ret)
+ goto e_key;
ret = ccp_copy_to_sb(cmd_q, &key, op.jobid, op.sb_key,
CCP_PASSTHRU_BYTESWAP_256BIT);
if (ret) {
@@ -935,7 +958,9 @@ static int ccp_run_aes_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd)
if (aes->mode != CCP_AES_MODE_ECB) {
/* Load the AES context - convert to LE */
dm_offset = CCP_SB_BYTES - AES_BLOCK_SIZE;
- ccp_set_dm_area(&ctx, dm_offset, aes->iv, 0, aes->iv_len);
+ ret = ccp_set_dm_area(&ctx, dm_offset, aes->iv, 0, aes->iv_len);
+ if (ret)
+ goto e_ctx;
ret = ccp_copy_to_sb(cmd_q, &ctx, op.jobid, op.sb_ctx,
CCP_PASSTHRU_BYTESWAP_256BIT);
if (ret) {
@@ -1113,8 +1138,12 @@ static int ccp_run_xts_aes_cmd(struct ccp_cmd_queue *cmd_q,
* big endian to little endian.
*/
dm_offset = CCP_SB_BYTES - AES_KEYSIZE_128;
- ccp_set_dm_area(&key, dm_offset, xts->key, 0, xts->key_len);
- ccp_set_dm_area(&key, 0, xts->key, xts->key_len, xts->key_len);
+ ret = ccp_set_dm_area(&key, dm_offset, xts->key, 0, xts->key_len);
+ if (ret)
+ goto e_key;
+ ret = ccp_set_dm_area(&key, 0, xts->key, xts->key_len, xts->key_len);
+ if (ret)
+ goto e_key;
} else {
/* Version 5 CCPs use a 512-bit space for the key: each portion
* occupies 256 bits, or one entire slot, and is zero-padded.
@@ -1123,9 +1152,13 @@ static int ccp_run_xts_aes_cmd(struct ccp_cmd_queue *cmd_q,
dm_offset = CCP_SB_BYTES;
pad = dm_offset - xts->key_len;
- ccp_set_dm_area(&key, pad, xts->key, 0, xts->key_len);
- ccp_set_dm_area(&key, dm_offset + pad, xts->key, xts->key_len,
- xts->key_len);
+ ret = ccp_set_dm_area(&key, pad, xts->key, 0, xts->key_len);
+ if (ret)
+ goto e_key;
+ ret = ccp_set_dm_area(&key, dm_offset + pad, xts->key,
+ xts->key_len, xts->key_len);
+ if (ret)
+ goto e_key;
}
ret = ccp_copy_to_sb(cmd_q, &key, op.jobid, op.sb_key,
CCP_PASSTHRU_BYTESWAP_256BIT);
@@ -1144,7 +1177,9 @@ static int ccp_run_xts_aes_cmd(struct ccp_cmd_queue *cmd_q,
if (ret)
goto e_key;
- ccp_set_dm_area(&ctx, 0, xts->iv, 0, xts->iv_len);
+ ret = ccp_set_dm_area(&ctx, 0, xts->iv, 0, xts->iv_len);
+ if (ret)
+ goto e_ctx;
ret = ccp_copy_to_sb(cmd_q, &ctx, op.jobid, op.sb_ctx,
CCP_PASSTHRU_BYTESWAP_NOOP);
if (ret) {
@@ -1287,12 +1322,18 @@ static int ccp_run_des3_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd)
dm_offset = CCP_SB_BYTES - des3->key_len; /* Basic offset */
len_singlekey = des3->key_len / 3;
- ccp_set_dm_area(&key, dm_offset + 2 * len_singlekey,
- des3->key, 0, len_singlekey);
- ccp_set_dm_area(&key, dm_offset + len_singlekey,
- des3->key, len_singlekey, len_singlekey);
- ccp_set_dm_area(&key, dm_offset,
- des3->key, 2 * len_singlekey, len_singlekey);
+ ret = ccp_set_dm_area(&key, dm_offset + 2 * len_singlekey,
+ des3->key, 0, len_singlekey);
+ if (ret)
+ goto e_key;
+ ret = ccp_set_dm_area(&key, dm_offset + len_singlekey,
+ des3->key, len_singlekey, len_singlekey);
+ if (ret)
+ goto e_key;
+ ret = ccp_set_dm_area(&key, dm_offset,
+ des3->key, 2 * len_singlekey, len_singlekey);
+ if (ret)
+ goto e_key;
/* Copy the key to the SB */
ret = ccp_copy_to_sb(cmd_q, &key, op.jobid, op.sb_key,
@@ -1320,7 +1361,10 @@ static int ccp_run_des3_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd)
/* Load the context into the LSB */
dm_offset = CCP_SB_BYTES - des3->iv_len;
- ccp_set_dm_area(&ctx, dm_offset, des3->iv, 0, des3->iv_len);
+ ret = ccp_set_dm_area(&ctx, dm_offset, des3->iv, 0,
+ des3->iv_len);
+ if (ret)
+ goto e_ctx;
if (cmd_q->ccp->vdata->version == CCP_VERSION(3, 0))
load_mode = CCP_PASSTHRU_BYTESWAP_NOOP;
@@ -1604,8 +1648,10 @@ static int ccp_run_sha_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd)
}
} else {
/* Restore the context */
- ccp_set_dm_area(&ctx, 0, sha->ctx, 0,
- sb_count * CCP_SB_BYTES);
+ ret = ccp_set_dm_area(&ctx, 0, sha->ctx, 0,
+ sb_count * CCP_SB_BYTES);
+ if (ret)
+ goto e_ctx;
}
ret = ccp_copy_to_sb(cmd_q, &ctx, op.jobid, op.sb_ctx,
@@ -1927,7 +1973,9 @@ static int ccp_run_passthru_cmd(struct ccp_cmd_queue *cmd_q,
if (ret)
return ret;
- ccp_set_dm_area(&mask, 0, pt->mask, 0, pt->mask_len);
+ ret = ccp_set_dm_area(&mask, 0, pt->mask, 0, pt->mask_len);
+ if (ret)
+ goto e_mask;
ret = ccp_copy_to_sb(cmd_q, &mask, op.jobid, op.sb_key,
CCP_PASSTHRU_BYTESWAP_NOOP);
if (ret) {
diff --git a/drivers/crypto/ccp/psp-dev.c b/drivers/crypto/ccp/psp-dev.c
index b3afb6cc9d72..d95ec526587a 100644
--- a/drivers/crypto/ccp/psp-dev.c
+++ b/drivers/crypto/ccp/psp-dev.c
@@ -367,8 +367,6 @@ e_free:
void *psp_copy_user_blob(u64 __user uaddr, u32 len)
{
- void *data;
-
if (!uaddr || !len)
return ERR_PTR(-EINVAL);
@@ -376,18 +374,7 @@ void *psp_copy_user_blob(u64 __user uaddr, u32 len)
if (len > SEV_FW_BLOB_MAX_SIZE)
return ERR_PTR(-EINVAL);
- data = kmalloc(len, GFP_KERNEL);
- if (!data)
- return ERR_PTR(-ENOMEM);
-
- if (copy_from_user(data, (void __user *)(uintptr_t)uaddr, len))
- goto e_free;
-
- return data;
-
-e_free:
- kfree(data);
- return ERR_PTR(-EFAULT);
+ return memdup_user((void __user *)(uintptr_t)uaddr, len);
}
EXPORT_SYMBOL_GPL(psp_copy_user_blob);
diff --git a/drivers/crypto/ccp/sp-dev.c b/drivers/crypto/ccp/sp-dev.c
index eb0da6572720..e0459002eb71 100644
--- a/drivers/crypto/ccp/sp-dev.c
+++ b/drivers/crypto/ccp/sp-dev.c
@@ -252,12 +252,12 @@ struct sp_device *sp_get_psp_master_device(void)
goto unlock;
list_for_each_entry(i, &sp_units, entry) {
- if (i->psp_data)
+ if (i->psp_data && i->get_psp_master_device) {
+ ret = i->get_psp_master_device();
break;
+ }
}
- if (i->get_psp_master_device)
- ret = i->get_psp_master_device();
unlock:
write_unlock_irqrestore(&sp_unit_lock, flags);
return ret;
diff --git a/drivers/staging/ccree/Makefile b/drivers/crypto/ccree/Makefile
index bdc27970f95f..bdc27970f95f 100644
--- a/drivers/staging/ccree/Makefile
+++ b/drivers/crypto/ccree/Makefile
diff --git a/drivers/crypto/ccree/cc_aead.c b/drivers/crypto/ccree/cc_aead.c
new file mode 100644
index 000000000000..03f4b9fce556
--- /dev/null
+++ b/drivers/crypto/ccree/cc_aead.c
@@ -0,0 +1,2718 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <crypto/algapi.h>
+#include <crypto/internal/aead.h>
+#include <crypto/authenc.h>
+#include <crypto/des.h>
+#include <linux/rtnetlink.h>
+#include "cc_driver.h"
+#include "cc_buffer_mgr.h"
+#include "cc_aead.h"
+#include "cc_request_mgr.h"
+#include "cc_hash.h"
+#include "cc_sram_mgr.h"
+
+#define template_aead template_u.aead
+
+#define MAX_AEAD_SETKEY_SEQ 12
+#define MAX_AEAD_PROCESS_SEQ 23
+
+#define MAX_HMAC_DIGEST_SIZE (SHA256_DIGEST_SIZE)
+#define MAX_HMAC_BLOCK_SIZE (SHA256_BLOCK_SIZE)
+
+#define AES_CCM_RFC4309_NONCE_SIZE 3
+#define MAX_NONCE_SIZE CTR_RFC3686_NONCE_SIZE
+
+/* Value of each ICV_CMP byte (of 8) in case of success */
+#define ICV_VERIF_OK 0x01
+
+struct cc_aead_handle {
+ cc_sram_addr_t sram_workspace_addr;
+ struct list_head aead_list;
+};
+
+struct cc_hmac_s {
+ u8 *padded_authkey;
+ u8 *ipad_opad; /* IPAD, OPAD*/
+ dma_addr_t padded_authkey_dma_addr;
+ dma_addr_t ipad_opad_dma_addr;
+};
+
+struct cc_xcbc_s {
+ u8 *xcbc_keys; /* K1,K2,K3 */
+ dma_addr_t xcbc_keys_dma_addr;
+};
+
+struct cc_aead_ctx {
+ struct cc_drvdata *drvdata;
+ u8 ctr_nonce[MAX_NONCE_SIZE]; /* used for ctr3686 iv and aes ccm */
+ u8 *enckey;
+ dma_addr_t enckey_dma_addr;
+ union {
+ struct cc_hmac_s hmac;
+ struct cc_xcbc_s xcbc;
+ } auth_state;
+ unsigned int enc_keylen;
+ unsigned int auth_keylen;
+ unsigned int authsize; /* Actual (reduced?) size of the MAC/ICv */
+ enum drv_cipher_mode cipher_mode;
+ enum cc_flow_mode flow_mode;
+ enum drv_hash_mode auth_mode;
+};
+
+static inline bool valid_assoclen(struct aead_request *req)
+{
+ return ((req->assoclen == 16) || (req->assoclen == 20));
+}
+
+static void cc_aead_exit(struct crypto_aead *tfm)
+{
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+
+ dev_dbg(dev, "Clearing context @%p for %s\n", crypto_aead_ctx(tfm),
+ crypto_tfm_alg_name(&tfm->base));
+
+ /* Unmap enckey buffer */
+ if (ctx->enckey) {
+ dma_free_coherent(dev, AES_MAX_KEY_SIZE, ctx->enckey,
+ ctx->enckey_dma_addr);
+ dev_dbg(dev, "Freed enckey DMA buffer enckey_dma_addr=%pad\n",
+ &ctx->enckey_dma_addr);
+ ctx->enckey_dma_addr = 0;
+ ctx->enckey = NULL;
+ }
+
+ if (ctx->auth_mode == DRV_HASH_XCBC_MAC) { /* XCBC authetication */
+ struct cc_xcbc_s *xcbc = &ctx->auth_state.xcbc;
+
+ if (xcbc->xcbc_keys) {
+ dma_free_coherent(dev, CC_AES_128_BIT_KEY_SIZE * 3,
+ xcbc->xcbc_keys,
+ xcbc->xcbc_keys_dma_addr);
+ }
+ dev_dbg(dev, "Freed xcbc_keys DMA buffer xcbc_keys_dma_addr=%pad\n",
+ &xcbc->xcbc_keys_dma_addr);
+ xcbc->xcbc_keys_dma_addr = 0;
+ xcbc->xcbc_keys = NULL;
+ } else if (ctx->auth_mode != DRV_HASH_NULL) { /* HMAC auth. */
+ struct cc_hmac_s *hmac = &ctx->auth_state.hmac;
+
+ if (hmac->ipad_opad) {
+ dma_free_coherent(dev, 2 * MAX_HMAC_DIGEST_SIZE,
+ hmac->ipad_opad,
+ hmac->ipad_opad_dma_addr);
+ dev_dbg(dev, "Freed ipad_opad DMA buffer ipad_opad_dma_addr=%pad\n",
+ &hmac->ipad_opad_dma_addr);
+ hmac->ipad_opad_dma_addr = 0;
+ hmac->ipad_opad = NULL;
+ }
+ if (hmac->padded_authkey) {
+ dma_free_coherent(dev, MAX_HMAC_BLOCK_SIZE,
+ hmac->padded_authkey,
+ hmac->padded_authkey_dma_addr);
+ dev_dbg(dev, "Freed padded_authkey DMA buffer padded_authkey_dma_addr=%pad\n",
+ &hmac->padded_authkey_dma_addr);
+ hmac->padded_authkey_dma_addr = 0;
+ hmac->padded_authkey = NULL;
+ }
+ }
+}
+
+static int cc_aead_init(struct crypto_aead *tfm)
+{
+ struct aead_alg *alg = crypto_aead_alg(tfm);
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+ struct cc_crypto_alg *cc_alg =
+ container_of(alg, struct cc_crypto_alg, aead_alg);
+ struct device *dev = drvdata_to_dev(cc_alg->drvdata);
+
+ dev_dbg(dev, "Initializing context @%p for %s\n", ctx,
+ crypto_tfm_alg_name(&tfm->base));
+
+ /* Initialize modes in instance */
+ ctx->cipher_mode = cc_alg->cipher_mode;
+ ctx->flow_mode = cc_alg->flow_mode;
+ ctx->auth_mode = cc_alg->auth_mode;
+ ctx->drvdata = cc_alg->drvdata;
+ crypto_aead_set_reqsize(tfm, sizeof(struct aead_req_ctx));
+
+ /* Allocate key buffer, cache line aligned */
+ ctx->enckey = dma_alloc_coherent(dev, AES_MAX_KEY_SIZE,
+ &ctx->enckey_dma_addr, GFP_KERNEL);
+ if (!ctx->enckey) {
+ dev_err(dev, "Failed allocating key buffer\n");
+ goto init_failed;
+ }
+ dev_dbg(dev, "Allocated enckey buffer in context ctx->enckey=@%p\n",
+ ctx->enckey);
+
+ /* Set default authlen value */
+
+ if (ctx->auth_mode == DRV_HASH_XCBC_MAC) { /* XCBC authetication */
+ struct cc_xcbc_s *xcbc = &ctx->auth_state.xcbc;
+ const unsigned int key_size = CC_AES_128_BIT_KEY_SIZE * 3;
+
+ /* Allocate dma-coherent buffer for XCBC's K1+K2+K3 */
+ /* (and temporary for user key - up to 256b) */
+ xcbc->xcbc_keys = dma_alloc_coherent(dev, key_size,
+ &xcbc->xcbc_keys_dma_addr,
+ GFP_KERNEL);
+ if (!xcbc->xcbc_keys) {
+ dev_err(dev, "Failed allocating buffer for XCBC keys\n");
+ goto init_failed;
+ }
+ } else if (ctx->auth_mode != DRV_HASH_NULL) { /* HMAC authentication */
+ struct cc_hmac_s *hmac = &ctx->auth_state.hmac;
+ const unsigned int digest_size = 2 * MAX_HMAC_DIGEST_SIZE;
+ dma_addr_t *pkey_dma = &hmac->padded_authkey_dma_addr;
+
+ /* Allocate dma-coherent buffer for IPAD + OPAD */
+ hmac->ipad_opad = dma_alloc_coherent(dev, digest_size,
+ &hmac->ipad_opad_dma_addr,
+ GFP_KERNEL);
+
+ if (!hmac->ipad_opad) {
+ dev_err(dev, "Failed allocating IPAD/OPAD buffer\n");
+ goto init_failed;
+ }
+
+ dev_dbg(dev, "Allocated authkey buffer in context ctx->authkey=@%p\n",
+ hmac->ipad_opad);
+
+ hmac->padded_authkey = dma_alloc_coherent(dev,
+ MAX_HMAC_BLOCK_SIZE,
+ pkey_dma,
+ GFP_KERNEL);
+
+ if (!hmac->padded_authkey) {
+ dev_err(dev, "failed to allocate padded_authkey\n");
+ goto init_failed;
+ }
+ } else {
+ ctx->auth_state.hmac.ipad_opad = NULL;
+ ctx->auth_state.hmac.padded_authkey = NULL;
+ }
+
+ return 0;
+
+init_failed:
+ cc_aead_exit(tfm);
+ return -ENOMEM;
+}
+
+static void cc_aead_complete(struct device *dev, void *cc_req, int err)
+{
+ struct aead_request *areq = (struct aead_request *)cc_req;
+ struct aead_req_ctx *areq_ctx = aead_request_ctx(areq);
+ struct crypto_aead *tfm = crypto_aead_reqtfm(cc_req);
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+
+ cc_unmap_aead_request(dev, areq);
+
+ /* Restore ordinary iv pointer */
+ areq->iv = areq_ctx->backup_iv;
+
+ if (err)
+ goto done;
+
+ if (areq_ctx->gen_ctx.op_type == DRV_CRYPTO_DIRECTION_DECRYPT) {
+ if (memcmp(areq_ctx->mac_buf, areq_ctx->icv_virt_addr,
+ ctx->authsize) != 0) {
+ dev_dbg(dev, "Payload authentication failure, (auth-size=%d, cipher=%d)\n",
+ ctx->authsize, ctx->cipher_mode);
+ /* In case of payload authentication failure, MUST NOT
+ * revealed the decrypted message --> zero its memory.
+ */
+ cc_zero_sgl(areq->dst, areq_ctx->cryptlen);
+ err = -EBADMSG;
+ }
+ } else { /*ENCRYPT*/
+ if (areq_ctx->is_icv_fragmented) {
+ u32 skip = areq->cryptlen + areq_ctx->dst_offset;
+
+ cc_copy_sg_portion(dev, areq_ctx->mac_buf,
+ areq_ctx->dst_sgl, skip,
+ (skip + ctx->authsize),
+ CC_SG_FROM_BUF);
+ }
+
+ /* If an IV was generated, copy it back to the user provided
+ * buffer.
+ */
+ if (areq_ctx->backup_giv) {
+ if (ctx->cipher_mode == DRV_CIPHER_CTR)
+ memcpy(areq_ctx->backup_giv, areq_ctx->ctr_iv +
+ CTR_RFC3686_NONCE_SIZE,
+ CTR_RFC3686_IV_SIZE);
+ else if (ctx->cipher_mode == DRV_CIPHER_CCM)
+ memcpy(areq_ctx->backup_giv, areq_ctx->ctr_iv +
+ CCM_BLOCK_IV_OFFSET, CCM_BLOCK_IV_SIZE);
+ }
+ }
+done:
+ aead_request_complete(areq, err);
+}
+
+static unsigned int xcbc_setkey(struct cc_hw_desc *desc,
+ struct cc_aead_ctx *ctx)
+{
+ /* Load the AES key */
+ hw_desc_init(&desc[0]);
+ /* We are using for the source/user key the same buffer
+ * as for the output keys, * because after this key loading it
+ * is not needed anymore
+ */
+ set_din_type(&desc[0], DMA_DLLI,
+ ctx->auth_state.xcbc.xcbc_keys_dma_addr, ctx->auth_keylen,
+ NS_BIT);
+ set_cipher_mode(&desc[0], DRV_CIPHER_ECB);
+ set_cipher_config0(&desc[0], DRV_CRYPTO_DIRECTION_ENCRYPT);
+ set_key_size_aes(&desc[0], ctx->auth_keylen);
+ set_flow_mode(&desc[0], S_DIN_to_AES);
+ set_setup_mode(&desc[0], SETUP_LOAD_KEY0);
+
+ hw_desc_init(&desc[1]);
+ set_din_const(&desc[1], 0x01010101, CC_AES_128_BIT_KEY_SIZE);
+ set_flow_mode(&desc[1], DIN_AES_DOUT);
+ set_dout_dlli(&desc[1], ctx->auth_state.xcbc.xcbc_keys_dma_addr,
+ AES_KEYSIZE_128, NS_BIT, 0);
+
+ hw_desc_init(&desc[2]);
+ set_din_const(&desc[2], 0x02020202, CC_AES_128_BIT_KEY_SIZE);
+ set_flow_mode(&desc[2], DIN_AES_DOUT);
+ set_dout_dlli(&desc[2], (ctx->auth_state.xcbc.xcbc_keys_dma_addr
+ + AES_KEYSIZE_128),
+ AES_KEYSIZE_128, NS_BIT, 0);
+
+ hw_desc_init(&desc[3]);
+ set_din_const(&desc[3], 0x03030303, CC_AES_128_BIT_KEY_SIZE);
+ set_flow_mode(&desc[3], DIN_AES_DOUT);
+ set_dout_dlli(&desc[3], (ctx->auth_state.xcbc.xcbc_keys_dma_addr
+ + 2 * AES_KEYSIZE_128),
+ AES_KEYSIZE_128, NS_BIT, 0);
+
+ return 4;
+}
+
+static int hmac_setkey(struct cc_hw_desc *desc, struct cc_aead_ctx *ctx)
+{
+ unsigned int hmac_pad_const[2] = { HMAC_IPAD_CONST, HMAC_OPAD_CONST };
+ unsigned int digest_ofs = 0;
+ unsigned int hash_mode = (ctx->auth_mode == DRV_HASH_SHA1) ?
+ DRV_HASH_HW_SHA1 : DRV_HASH_HW_SHA256;
+ unsigned int digest_size = (ctx->auth_mode == DRV_HASH_SHA1) ?
+ CC_SHA1_DIGEST_SIZE : CC_SHA256_DIGEST_SIZE;
+ struct cc_hmac_s *hmac = &ctx->auth_state.hmac;
+
+ unsigned int idx = 0;
+ int i;
+
+ /* calc derived HMAC key */
+ for (i = 0; i < 2; i++) {
+ /* Load hash initial state */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], hash_mode);
+ set_din_sram(&desc[idx],
+ cc_larval_digest_addr(ctx->drvdata,
+ ctx->auth_mode),
+ digest_size);
+ set_flow_mode(&desc[idx], S_DIN_to_HASH);
+ set_setup_mode(&desc[idx], SETUP_LOAD_STATE0);
+ idx++;
+
+ /* Load the hash current length*/
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], hash_mode);
+ set_din_const(&desc[idx], 0, ctx->drvdata->hash_len_sz);
+ set_flow_mode(&desc[idx], S_DIN_to_HASH);
+ set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
+ idx++;
+
+ /* Prepare ipad key */
+ hw_desc_init(&desc[idx]);
+ set_xor_val(&desc[idx], hmac_pad_const[i]);
+ set_cipher_mode(&desc[idx], hash_mode);
+ set_flow_mode(&desc[idx], S_DIN_to_HASH);
+ set_setup_mode(&desc[idx], SETUP_LOAD_STATE1);
+ idx++;
+
+ /* Perform HASH update */
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_DLLI,
+ hmac->padded_authkey_dma_addr,
+ SHA256_BLOCK_SIZE, NS_BIT);
+ set_cipher_mode(&desc[idx], hash_mode);
+ set_xor_active(&desc[idx]);
+ set_flow_mode(&desc[idx], DIN_HASH);
+ idx++;
+
+ /* Get the digset */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], hash_mode);
+ set_dout_dlli(&desc[idx],
+ (hmac->ipad_opad_dma_addr + digest_ofs),
+ digest_size, NS_BIT, 0);
+ set_flow_mode(&desc[idx], S_HASH_to_DOUT);
+ set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
+ set_cipher_config1(&desc[idx], HASH_PADDING_DISABLED);
+ idx++;
+
+ digest_ofs += digest_size;
+ }
+
+ return idx;
+}
+
+static int validate_keys_sizes(struct cc_aead_ctx *ctx)
+{
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+
+ dev_dbg(dev, "enc_keylen=%u authkeylen=%u\n",
+ ctx->enc_keylen, ctx->auth_keylen);
+
+ switch (ctx->auth_mode) {
+ case DRV_HASH_SHA1:
+ case DRV_HASH_SHA256:
+ break;
+ case DRV_HASH_XCBC_MAC:
+ if (ctx->auth_keylen != AES_KEYSIZE_128 &&
+ ctx->auth_keylen != AES_KEYSIZE_192 &&
+ ctx->auth_keylen != AES_KEYSIZE_256)
+ return -ENOTSUPP;
+ break;
+ case DRV_HASH_NULL: /* Not authenc (e.g., CCM) - no auth_key) */
+ if (ctx->auth_keylen > 0)
+ return -EINVAL;
+ break;
+ default:
+ dev_err(dev, "Invalid auth_mode=%d\n", ctx->auth_mode);
+ return -EINVAL;
+ }
+ /* Check cipher key size */
+ if (ctx->flow_mode == S_DIN_to_DES) {
+ if (ctx->enc_keylen != DES3_EDE_KEY_SIZE) {
+ dev_err(dev, "Invalid cipher(3DES) key size: %u\n",
+ ctx->enc_keylen);
+ return -EINVAL;
+ }
+ } else { /* Default assumed to be AES ciphers */
+ if (ctx->enc_keylen != AES_KEYSIZE_128 &&
+ ctx->enc_keylen != AES_KEYSIZE_192 &&
+ ctx->enc_keylen != AES_KEYSIZE_256) {
+ dev_err(dev, "Invalid cipher(AES) key size: %u\n",
+ ctx->enc_keylen);
+ return -EINVAL;
+ }
+ }
+
+ return 0; /* All tests of keys sizes passed */
+}
+
+/* This function prepers the user key so it can pass to the hmac processing
+ * (copy to intenral buffer or hash in case of key longer than block
+ */
+static int cc_get_plain_hmac_key(struct crypto_aead *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ dma_addr_t key_dma_addr = 0;
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+ u32 larval_addr = cc_larval_digest_addr(ctx->drvdata, ctx->auth_mode);
+ struct cc_crypto_req cc_req = {};
+ unsigned int blocksize;
+ unsigned int digestsize;
+ unsigned int hashmode;
+ unsigned int idx = 0;
+ int rc = 0;
+ struct cc_hw_desc desc[MAX_AEAD_SETKEY_SEQ];
+ dma_addr_t padded_authkey_dma_addr =
+ ctx->auth_state.hmac.padded_authkey_dma_addr;
+
+ switch (ctx->auth_mode) { /* auth_key required and >0 */
+ case DRV_HASH_SHA1:
+ blocksize = SHA1_BLOCK_SIZE;
+ digestsize = SHA1_DIGEST_SIZE;
+ hashmode = DRV_HASH_HW_SHA1;
+ break;
+ case DRV_HASH_SHA256:
+ default:
+ blocksize = SHA256_BLOCK_SIZE;
+ digestsize = SHA256_DIGEST_SIZE;
+ hashmode = DRV_HASH_HW_SHA256;
+ }
+
+ if (keylen != 0) {
+ key_dma_addr = dma_map_single(dev, (void *)key, keylen,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, key_dma_addr)) {
+ dev_err(dev, "Mapping key va=0x%p len=%u for DMA failed\n",
+ key, keylen);
+ return -ENOMEM;
+ }
+ if (keylen > blocksize) {
+ /* Load hash initial state */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], hashmode);
+ set_din_sram(&desc[idx], larval_addr, digestsize);
+ set_flow_mode(&desc[idx], S_DIN_to_HASH);
+ set_setup_mode(&desc[idx], SETUP_LOAD_STATE0);
+ idx++;
+
+ /* Load the hash current length*/
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], hashmode);
+ set_din_const(&desc[idx], 0, ctx->drvdata->hash_len_sz);
+ set_cipher_config1(&desc[idx], HASH_PADDING_ENABLED);
+ set_flow_mode(&desc[idx], S_DIN_to_HASH);
+ set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
+ idx++;
+
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_DLLI,
+ key_dma_addr, keylen, NS_BIT);
+ set_flow_mode(&desc[idx], DIN_HASH);
+ idx++;
+
+ /* Get hashed key */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], hashmode);
+ set_dout_dlli(&desc[idx], padded_authkey_dma_addr,
+ digestsize, NS_BIT, 0);
+ set_flow_mode(&desc[idx], S_HASH_to_DOUT);
+ set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
+ set_cipher_config1(&desc[idx], HASH_PADDING_DISABLED);
+ set_cipher_config0(&desc[idx],
+ HASH_DIGEST_RESULT_LITTLE_ENDIAN);
+ idx++;
+
+ hw_desc_init(&desc[idx]);
+ set_din_const(&desc[idx], 0, (blocksize - digestsize));
+ set_flow_mode(&desc[idx], BYPASS);
+ set_dout_dlli(&desc[idx], (padded_authkey_dma_addr +
+ digestsize), (blocksize - digestsize),
+ NS_BIT, 0);
+ idx++;
+ } else {
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_DLLI, key_dma_addr,
+ keylen, NS_BIT);
+ set_flow_mode(&desc[idx], BYPASS);
+ set_dout_dlli(&desc[idx], padded_authkey_dma_addr,
+ keylen, NS_BIT, 0);
+ idx++;
+
+ if ((blocksize - keylen) != 0) {
+ hw_desc_init(&desc[idx]);
+ set_din_const(&desc[idx], 0,
+ (blocksize - keylen));
+ set_flow_mode(&desc[idx], BYPASS);
+ set_dout_dlli(&desc[idx],
+ (padded_authkey_dma_addr +
+ keylen),
+ (blocksize - keylen), NS_BIT, 0);
+ idx++;
+ }
+ }
+ } else {
+ hw_desc_init(&desc[idx]);
+ set_din_const(&desc[idx], 0, (blocksize - keylen));
+ set_flow_mode(&desc[idx], BYPASS);
+ set_dout_dlli(&desc[idx], padded_authkey_dma_addr,
+ blocksize, NS_BIT, 0);
+ idx++;
+ }
+
+ rc = cc_send_sync_request(ctx->drvdata, &cc_req, desc, idx);
+ if (rc)
+ dev_err(dev, "send_request() failed (rc=%d)\n", rc);
+
+ if (key_dma_addr)
+ dma_unmap_single(dev, key_dma_addr, keylen, DMA_TO_DEVICE);
+
+ return rc;
+}
+
+static int cc_aead_setkey(struct crypto_aead *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+ struct rtattr *rta = (struct rtattr *)key;
+ struct cc_crypto_req cc_req = {};
+ struct crypto_authenc_key_param *param;
+ struct cc_hw_desc desc[MAX_AEAD_SETKEY_SEQ];
+ int rc = -EINVAL;
+ unsigned int seq_len = 0;
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+
+ dev_dbg(dev, "Setting key in context @%p for %s. key=%p keylen=%u\n",
+ ctx, crypto_tfm_alg_name(crypto_aead_tfm(tfm)), key, keylen);
+
+ /* STAT_PHASE_0: Init and sanity checks */
+
+ if (ctx->auth_mode != DRV_HASH_NULL) { /* authenc() alg. */
+ if (!RTA_OK(rta, keylen))
+ goto badkey;
+ if (rta->rta_type != CRYPTO_AUTHENC_KEYA_PARAM)
+ goto badkey;
+ if (RTA_PAYLOAD(rta) < sizeof(*param))
+ goto badkey;
+ param = RTA_DATA(rta);
+ ctx->enc_keylen = be32_to_cpu(param->enckeylen);
+ key += RTA_ALIGN(rta->rta_len);
+ keylen -= RTA_ALIGN(rta->rta_len);
+ if (keylen < ctx->enc_keylen)
+ goto badkey;
+ ctx->auth_keylen = keylen - ctx->enc_keylen;
+
+ if (ctx->cipher_mode == DRV_CIPHER_CTR) {
+ /* the nonce is stored in bytes at end of key */
+ if (ctx->enc_keylen <
+ (AES_MIN_KEY_SIZE + CTR_RFC3686_NONCE_SIZE))
+ goto badkey;
+ /* Copy nonce from last 4 bytes in CTR key to
+ * first 4 bytes in CTR IV
+ */
+ memcpy(ctx->ctr_nonce, key + ctx->auth_keylen +
+ ctx->enc_keylen - CTR_RFC3686_NONCE_SIZE,
+ CTR_RFC3686_NONCE_SIZE);
+ /* Set CTR key size */
+ ctx->enc_keylen -= CTR_RFC3686_NONCE_SIZE;
+ }
+ } else { /* non-authenc - has just one key */
+ ctx->enc_keylen = keylen;
+ ctx->auth_keylen = 0;
+ }
+
+ rc = validate_keys_sizes(ctx);
+ if (rc)
+ goto badkey;
+
+ /* STAT_PHASE_1: Copy key to ctx */
+
+ /* Get key material */
+ memcpy(ctx->enckey, key + ctx->auth_keylen, ctx->enc_keylen);
+ if (ctx->enc_keylen == 24)
+ memset(ctx->enckey + 24, 0, CC_AES_KEY_SIZE_MAX - 24);
+ if (ctx->auth_mode == DRV_HASH_XCBC_MAC) {
+ memcpy(ctx->auth_state.xcbc.xcbc_keys, key, ctx->auth_keylen);
+ } else if (ctx->auth_mode != DRV_HASH_NULL) { /* HMAC */
+ rc = cc_get_plain_hmac_key(tfm, key, ctx->auth_keylen);
+ if (rc)
+ goto badkey;
+ }
+
+ /* STAT_PHASE_2: Create sequence */
+
+ switch (ctx->auth_mode) {
+ case DRV_HASH_SHA1:
+ case DRV_HASH_SHA256:
+ seq_len = hmac_setkey(desc, ctx);
+ break;
+ case DRV_HASH_XCBC_MAC:
+ seq_len = xcbc_setkey(desc, ctx);
+ break;
+ case DRV_HASH_NULL: /* non-authenc modes, e.g., CCM */
+ break; /* No auth. key setup */
+ default:
+ dev_err(dev, "Unsupported authenc (%d)\n", ctx->auth_mode);
+ rc = -ENOTSUPP;
+ goto badkey;
+ }
+
+ /* STAT_PHASE_3: Submit sequence to HW */
+
+ if (seq_len > 0) { /* For CCM there is no sequence to setup the key */
+ rc = cc_send_sync_request(ctx->drvdata, &cc_req, desc, seq_len);
+ if (rc) {
+ dev_err(dev, "send_request() failed (rc=%d)\n", rc);
+ goto setkey_error;
+ }
+ }
+
+ /* Update STAT_PHASE_3 */
+ return rc;
+
+badkey:
+ crypto_aead_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
+
+setkey_error:
+ return rc;
+}
+
+static int cc_rfc4309_ccm_setkey(struct crypto_aead *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+
+ if (keylen < 3)
+ return -EINVAL;
+
+ keylen -= 3;
+ memcpy(ctx->ctr_nonce, key + keylen, 3);
+
+ return cc_aead_setkey(tfm, key, keylen);
+}
+
+static int cc_aead_setauthsize(struct crypto_aead *authenc,
+ unsigned int authsize)
+{
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(authenc);
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+
+ /* Unsupported auth. sizes */
+ if (authsize == 0 ||
+ authsize > crypto_aead_maxauthsize(authenc)) {
+ return -ENOTSUPP;
+ }
+
+ ctx->authsize = authsize;
+ dev_dbg(dev, "authlen=%d\n", ctx->authsize);
+
+ return 0;
+}
+
+static int cc_rfc4309_ccm_setauthsize(struct crypto_aead *authenc,
+ unsigned int authsize)
+{
+ switch (authsize) {
+ case 8:
+ case 12:
+ case 16:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return cc_aead_setauthsize(authenc, authsize);
+}
+
+static int cc_ccm_setauthsize(struct crypto_aead *authenc,
+ unsigned int authsize)
+{
+ switch (authsize) {
+ case 4:
+ case 6:
+ case 8:
+ case 10:
+ case 12:
+ case 14:
+ case 16:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return cc_aead_setauthsize(authenc, authsize);
+}
+
+static void cc_set_assoc_desc(struct aead_request *areq, unsigned int flow_mode,
+ struct cc_hw_desc desc[], unsigned int *seq_size)
+{
+ struct crypto_aead *tfm = crypto_aead_reqtfm(areq);
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+ struct aead_req_ctx *areq_ctx = aead_request_ctx(areq);
+ enum cc_req_dma_buf_type assoc_dma_type = areq_ctx->assoc_buff_type;
+ unsigned int idx = *seq_size;
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+
+ switch (assoc_dma_type) {
+ case CC_DMA_BUF_DLLI:
+ dev_dbg(dev, "ASSOC buffer type DLLI\n");
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_DLLI, sg_dma_address(areq->src),
+ areq->assoclen, NS_BIT);
+ set_flow_mode(&desc[idx], flow_mode);
+ if (ctx->auth_mode == DRV_HASH_XCBC_MAC &&
+ areq_ctx->cryptlen > 0)
+ set_din_not_last_indication(&desc[idx]);
+ break;
+ case CC_DMA_BUF_MLLI:
+ dev_dbg(dev, "ASSOC buffer type MLLI\n");
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_MLLI, areq_ctx->assoc.sram_addr,
+ areq_ctx->assoc.mlli_nents, NS_BIT);
+ set_flow_mode(&desc[idx], flow_mode);
+ if (ctx->auth_mode == DRV_HASH_XCBC_MAC &&
+ areq_ctx->cryptlen > 0)
+ set_din_not_last_indication(&desc[idx]);
+ break;
+ case CC_DMA_BUF_NULL:
+ default:
+ dev_err(dev, "Invalid ASSOC buffer type\n");
+ }
+
+ *seq_size = (++idx);
+}
+
+static void cc_proc_authen_desc(struct aead_request *areq,
+ unsigned int flow_mode,
+ struct cc_hw_desc desc[],
+ unsigned int *seq_size, int direct)
+{
+ struct aead_req_ctx *areq_ctx = aead_request_ctx(areq);
+ enum cc_req_dma_buf_type data_dma_type = areq_ctx->data_buff_type;
+ unsigned int idx = *seq_size;
+ struct crypto_aead *tfm = crypto_aead_reqtfm(areq);
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+
+ switch (data_dma_type) {
+ case CC_DMA_BUF_DLLI:
+ {
+ struct scatterlist *cipher =
+ (direct == DRV_CRYPTO_DIRECTION_ENCRYPT) ?
+ areq_ctx->dst_sgl : areq_ctx->src_sgl;
+
+ unsigned int offset =
+ (direct == DRV_CRYPTO_DIRECTION_ENCRYPT) ?
+ areq_ctx->dst_offset : areq_ctx->src_offset;
+ dev_dbg(dev, "AUTHENC: SRC/DST buffer type DLLI\n");
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_DLLI,
+ (sg_dma_address(cipher) + offset),
+ areq_ctx->cryptlen, NS_BIT);
+ set_flow_mode(&desc[idx], flow_mode);
+ break;
+ }
+ case CC_DMA_BUF_MLLI:
+ {
+ /* DOUBLE-PASS flow (as default)
+ * assoc. + iv + data -compact in one table
+ * if assoclen is ZERO only IV perform
+ */
+ cc_sram_addr_t mlli_addr = areq_ctx->assoc.sram_addr;
+ u32 mlli_nents = areq_ctx->assoc.mlli_nents;
+
+ if (areq_ctx->is_single_pass) {
+ if (direct == DRV_CRYPTO_DIRECTION_ENCRYPT) {
+ mlli_addr = areq_ctx->dst.sram_addr;
+ mlli_nents = areq_ctx->dst.mlli_nents;
+ } else {
+ mlli_addr = areq_ctx->src.sram_addr;
+ mlli_nents = areq_ctx->src.mlli_nents;
+ }
+ }
+
+ dev_dbg(dev, "AUTHENC: SRC/DST buffer type MLLI\n");
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_MLLI, mlli_addr, mlli_nents,
+ NS_BIT);
+ set_flow_mode(&desc[idx], flow_mode);
+ break;
+ }
+ case CC_DMA_BUF_NULL:
+ default:
+ dev_err(dev, "AUTHENC: Invalid SRC/DST buffer type\n");
+ }
+
+ *seq_size = (++idx);
+}
+
+static void cc_proc_cipher_desc(struct aead_request *areq,
+ unsigned int flow_mode,
+ struct cc_hw_desc desc[],
+ unsigned int *seq_size)
+{
+ unsigned int idx = *seq_size;
+ struct aead_req_ctx *areq_ctx = aead_request_ctx(areq);
+ enum cc_req_dma_buf_type data_dma_type = areq_ctx->data_buff_type;
+ struct crypto_aead *tfm = crypto_aead_reqtfm(areq);
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+
+ if (areq_ctx->cryptlen == 0)
+ return; /*null processing*/
+
+ switch (data_dma_type) {
+ case CC_DMA_BUF_DLLI:
+ dev_dbg(dev, "CIPHER: SRC/DST buffer type DLLI\n");
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_DLLI,
+ (sg_dma_address(areq_ctx->src_sgl) +
+ areq_ctx->src_offset), areq_ctx->cryptlen,
+ NS_BIT);
+ set_dout_dlli(&desc[idx],
+ (sg_dma_address(areq_ctx->dst_sgl) +
+ areq_ctx->dst_offset),
+ areq_ctx->cryptlen, NS_BIT, 0);
+ set_flow_mode(&desc[idx], flow_mode);
+ break;
+ case CC_DMA_BUF_MLLI:
+ dev_dbg(dev, "CIPHER: SRC/DST buffer type MLLI\n");
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_MLLI, areq_ctx->src.sram_addr,
+ areq_ctx->src.mlli_nents, NS_BIT);
+ set_dout_mlli(&desc[idx], areq_ctx->dst.sram_addr,
+ areq_ctx->dst.mlli_nents, NS_BIT, 0);
+ set_flow_mode(&desc[idx], flow_mode);
+ break;
+ case CC_DMA_BUF_NULL:
+ default:
+ dev_err(dev, "CIPHER: Invalid SRC/DST buffer type\n");
+ }
+
+ *seq_size = (++idx);
+}
+
+static void cc_proc_digest_desc(struct aead_request *req,
+ struct cc_hw_desc desc[],
+ unsigned int *seq_size)
+{
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+ struct aead_req_ctx *req_ctx = aead_request_ctx(req);
+ unsigned int idx = *seq_size;
+ unsigned int hash_mode = (ctx->auth_mode == DRV_HASH_SHA1) ?
+ DRV_HASH_HW_SHA1 : DRV_HASH_HW_SHA256;
+ int direct = req_ctx->gen_ctx.op_type;
+
+ /* Get final ICV result */
+ if (direct == DRV_CRYPTO_DIRECTION_ENCRYPT) {
+ hw_desc_init(&desc[idx]);
+ set_flow_mode(&desc[idx], S_HASH_to_DOUT);
+ set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
+ set_dout_dlli(&desc[idx], req_ctx->icv_dma_addr, ctx->authsize,
+ NS_BIT, 1);
+ set_queue_last_ind(ctx->drvdata, &desc[idx]);
+ if (ctx->auth_mode == DRV_HASH_XCBC_MAC) {
+ set_aes_not_hash_mode(&desc[idx]);
+ set_cipher_mode(&desc[idx], DRV_CIPHER_XCBC_MAC);
+ } else {
+ set_cipher_config0(&desc[idx],
+ HASH_DIGEST_RESULT_LITTLE_ENDIAN);
+ set_cipher_mode(&desc[idx], hash_mode);
+ }
+ } else { /*Decrypt*/
+ /* Get ICV out from hardware */
+ hw_desc_init(&desc[idx]);
+ set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
+ set_flow_mode(&desc[idx], S_HASH_to_DOUT);
+ set_dout_dlli(&desc[idx], req_ctx->mac_buf_dma_addr,
+ ctx->authsize, NS_BIT, 1);
+ set_queue_last_ind(ctx->drvdata, &desc[idx]);
+ set_cipher_config0(&desc[idx],
+ HASH_DIGEST_RESULT_LITTLE_ENDIAN);
+ set_cipher_config1(&desc[idx], HASH_PADDING_DISABLED);
+ if (ctx->auth_mode == DRV_HASH_XCBC_MAC) {
+ set_cipher_mode(&desc[idx], DRV_CIPHER_XCBC_MAC);
+ set_aes_not_hash_mode(&desc[idx]);
+ } else {
+ set_cipher_mode(&desc[idx], hash_mode);
+ }
+ }
+
+ *seq_size = (++idx);
+}
+
+static void cc_set_cipher_desc(struct aead_request *req,
+ struct cc_hw_desc desc[],
+ unsigned int *seq_size)
+{
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+ struct aead_req_ctx *req_ctx = aead_request_ctx(req);
+ unsigned int hw_iv_size = req_ctx->hw_iv_size;
+ unsigned int idx = *seq_size;
+ int direct = req_ctx->gen_ctx.op_type;
+
+ /* Setup cipher state */
+ hw_desc_init(&desc[idx]);
+ set_cipher_config0(&desc[idx], direct);
+ set_flow_mode(&desc[idx], ctx->flow_mode);
+ set_din_type(&desc[idx], DMA_DLLI, req_ctx->gen_ctx.iv_dma_addr,
+ hw_iv_size, NS_BIT);
+ if (ctx->cipher_mode == DRV_CIPHER_CTR)
+ set_setup_mode(&desc[idx], SETUP_LOAD_STATE1);
+ else
+ set_setup_mode(&desc[idx], SETUP_LOAD_STATE0);
+ set_cipher_mode(&desc[idx], ctx->cipher_mode);
+ idx++;
+
+ /* Setup enc. key */
+ hw_desc_init(&desc[idx]);
+ set_cipher_config0(&desc[idx], direct);
+ set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
+ set_flow_mode(&desc[idx], ctx->flow_mode);
+ if (ctx->flow_mode == S_DIN_to_AES) {
+ set_din_type(&desc[idx], DMA_DLLI, ctx->enckey_dma_addr,
+ ((ctx->enc_keylen == 24) ? CC_AES_KEY_SIZE_MAX :
+ ctx->enc_keylen), NS_BIT);
+ set_key_size_aes(&desc[idx], ctx->enc_keylen);
+ } else {
+ set_din_type(&desc[idx], DMA_DLLI, ctx->enckey_dma_addr,
+ ctx->enc_keylen, NS_BIT);
+ set_key_size_des(&desc[idx], ctx->enc_keylen);
+ }
+ set_cipher_mode(&desc[idx], ctx->cipher_mode);
+ idx++;
+
+ *seq_size = idx;
+}
+
+static void cc_proc_cipher(struct aead_request *req, struct cc_hw_desc desc[],
+ unsigned int *seq_size, unsigned int data_flow_mode)
+{
+ struct aead_req_ctx *req_ctx = aead_request_ctx(req);
+ int direct = req_ctx->gen_ctx.op_type;
+ unsigned int idx = *seq_size;
+
+ if (req_ctx->cryptlen == 0)
+ return; /*null processing*/
+
+ cc_set_cipher_desc(req, desc, &idx);
+ cc_proc_cipher_desc(req, data_flow_mode, desc, &idx);
+ if (direct == DRV_CRYPTO_DIRECTION_ENCRYPT) {
+ /* We must wait for DMA to write all cipher */
+ hw_desc_init(&desc[idx]);
+ set_din_no_dma(&desc[idx], 0, 0xfffff0);
+ set_dout_no_dma(&desc[idx], 0, 0, 1);
+ idx++;
+ }
+
+ *seq_size = idx;
+}
+
+static void cc_set_hmac_desc(struct aead_request *req, struct cc_hw_desc desc[],
+ unsigned int *seq_size)
+{
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+ unsigned int hash_mode = (ctx->auth_mode == DRV_HASH_SHA1) ?
+ DRV_HASH_HW_SHA1 : DRV_HASH_HW_SHA256;
+ unsigned int digest_size = (ctx->auth_mode == DRV_HASH_SHA1) ?
+ CC_SHA1_DIGEST_SIZE : CC_SHA256_DIGEST_SIZE;
+ unsigned int idx = *seq_size;
+
+ /* Loading hash ipad xor key state */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], hash_mode);
+ set_din_type(&desc[idx], DMA_DLLI,
+ ctx->auth_state.hmac.ipad_opad_dma_addr, digest_size,
+ NS_BIT);
+ set_flow_mode(&desc[idx], S_DIN_to_HASH);
+ set_setup_mode(&desc[idx], SETUP_LOAD_STATE0);
+ idx++;
+
+ /* Load init. digest len (64 bytes) */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], hash_mode);
+ set_din_sram(&desc[idx], cc_digest_len_addr(ctx->drvdata, hash_mode),
+ ctx->drvdata->hash_len_sz);
+ set_flow_mode(&desc[idx], S_DIN_to_HASH);
+ set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
+ idx++;
+
+ *seq_size = idx;
+}
+
+static void cc_set_xcbc_desc(struct aead_request *req, struct cc_hw_desc desc[],
+ unsigned int *seq_size)
+{
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+ unsigned int idx = *seq_size;
+
+ /* Loading MAC state */
+ hw_desc_init(&desc[idx]);
+ set_din_const(&desc[idx], 0, CC_AES_BLOCK_SIZE);
+ set_setup_mode(&desc[idx], SETUP_LOAD_STATE0);
+ set_cipher_mode(&desc[idx], DRV_CIPHER_XCBC_MAC);
+ set_cipher_config0(&desc[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
+ set_key_size_aes(&desc[idx], CC_AES_128_BIT_KEY_SIZE);
+ set_flow_mode(&desc[idx], S_DIN_to_HASH);
+ set_aes_not_hash_mode(&desc[idx]);
+ idx++;
+
+ /* Setup XCBC MAC K1 */
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_DLLI,
+ ctx->auth_state.xcbc.xcbc_keys_dma_addr,
+ AES_KEYSIZE_128, NS_BIT);
+ set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
+ set_cipher_mode(&desc[idx], DRV_CIPHER_XCBC_MAC);
+ set_cipher_config0(&desc[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
+ set_key_size_aes(&desc[idx], CC_AES_128_BIT_KEY_SIZE);
+ set_flow_mode(&desc[idx], S_DIN_to_HASH);
+ set_aes_not_hash_mode(&desc[idx]);
+ idx++;
+
+ /* Setup XCBC MAC K2 */
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_DLLI,
+ (ctx->auth_state.xcbc.xcbc_keys_dma_addr +
+ AES_KEYSIZE_128), AES_KEYSIZE_128, NS_BIT);
+ set_setup_mode(&desc[idx], SETUP_LOAD_STATE1);
+ set_cipher_mode(&desc[idx], DRV_CIPHER_XCBC_MAC);
+ set_cipher_config0(&desc[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
+ set_key_size_aes(&desc[idx], CC_AES_128_BIT_KEY_SIZE);
+ set_flow_mode(&desc[idx], S_DIN_to_HASH);
+ set_aes_not_hash_mode(&desc[idx]);
+ idx++;
+
+ /* Setup XCBC MAC K3 */
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_DLLI,
+ (ctx->auth_state.xcbc.xcbc_keys_dma_addr +
+ 2 * AES_KEYSIZE_128), AES_KEYSIZE_128, NS_BIT);
+ set_setup_mode(&desc[idx], SETUP_LOAD_STATE2);
+ set_cipher_mode(&desc[idx], DRV_CIPHER_XCBC_MAC);
+ set_cipher_config0(&desc[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
+ set_key_size_aes(&desc[idx], CC_AES_128_BIT_KEY_SIZE);
+ set_flow_mode(&desc[idx], S_DIN_to_HASH);
+ set_aes_not_hash_mode(&desc[idx]);
+ idx++;
+
+ *seq_size = idx;
+}
+
+static void cc_proc_header_desc(struct aead_request *req,
+ struct cc_hw_desc desc[],
+ unsigned int *seq_size)
+{
+ unsigned int idx = *seq_size;
+ /* Hash associated data */
+ if (req->assoclen > 0)
+ cc_set_assoc_desc(req, DIN_HASH, desc, &idx);
+
+ /* Hash IV */
+ *seq_size = idx;
+}
+
+static void cc_proc_scheme_desc(struct aead_request *req,
+ struct cc_hw_desc desc[],
+ unsigned int *seq_size)
+{
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+ struct cc_aead_handle *aead_handle = ctx->drvdata->aead_handle;
+ unsigned int hash_mode = (ctx->auth_mode == DRV_HASH_SHA1) ?
+ DRV_HASH_HW_SHA1 : DRV_HASH_HW_SHA256;
+ unsigned int digest_size = (ctx->auth_mode == DRV_HASH_SHA1) ?
+ CC_SHA1_DIGEST_SIZE : CC_SHA256_DIGEST_SIZE;
+ unsigned int idx = *seq_size;
+
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], hash_mode);
+ set_dout_sram(&desc[idx], aead_handle->sram_workspace_addr,
+ ctx->drvdata->hash_len_sz);
+ set_flow_mode(&desc[idx], S_HASH_to_DOUT);
+ set_setup_mode(&desc[idx], SETUP_WRITE_STATE1);
+ set_cipher_do(&desc[idx], DO_PAD);
+ idx++;
+
+ /* Get final ICV result */
+ hw_desc_init(&desc[idx]);
+ set_dout_sram(&desc[idx], aead_handle->sram_workspace_addr,
+ digest_size);
+ set_flow_mode(&desc[idx], S_HASH_to_DOUT);
+ set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
+ set_cipher_config0(&desc[idx], HASH_DIGEST_RESULT_LITTLE_ENDIAN);
+ set_cipher_mode(&desc[idx], hash_mode);
+ idx++;
+
+ /* Loading hash opad xor key state */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], hash_mode);
+ set_din_type(&desc[idx], DMA_DLLI,
+ (ctx->auth_state.hmac.ipad_opad_dma_addr + digest_size),
+ digest_size, NS_BIT);
+ set_flow_mode(&desc[idx], S_DIN_to_HASH);
+ set_setup_mode(&desc[idx], SETUP_LOAD_STATE0);
+ idx++;
+
+ /* Load init. digest len (64 bytes) */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], hash_mode);
+ set_din_sram(&desc[idx], cc_digest_len_addr(ctx->drvdata, hash_mode),
+ ctx->drvdata->hash_len_sz);
+ set_cipher_config1(&desc[idx], HASH_PADDING_ENABLED);
+ set_flow_mode(&desc[idx], S_DIN_to_HASH);
+ set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
+ idx++;
+
+ /* Perform HASH update */
+ hw_desc_init(&desc[idx]);
+ set_din_sram(&desc[idx], aead_handle->sram_workspace_addr,
+ digest_size);
+ set_flow_mode(&desc[idx], DIN_HASH);
+ idx++;
+
+ *seq_size = idx;
+}
+
+static void cc_mlli_to_sram(struct aead_request *req,
+ struct cc_hw_desc desc[], unsigned int *seq_size)
+{
+ struct aead_req_ctx *req_ctx = aead_request_ctx(req);
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+
+ if (req_ctx->assoc_buff_type == CC_DMA_BUF_MLLI ||
+ req_ctx->data_buff_type == CC_DMA_BUF_MLLI ||
+ !req_ctx->is_single_pass) {
+ dev_dbg(dev, "Copy-to-sram: mlli_dma=%08x, mlli_size=%u\n",
+ (unsigned int)ctx->drvdata->mlli_sram_addr,
+ req_ctx->mlli_params.mlli_len);
+ /* Copy MLLI table host-to-sram */
+ hw_desc_init(&desc[*seq_size]);
+ set_din_type(&desc[*seq_size], DMA_DLLI,
+ req_ctx->mlli_params.mlli_dma_addr,
+ req_ctx->mlli_params.mlli_len, NS_BIT);
+ set_dout_sram(&desc[*seq_size],
+ ctx->drvdata->mlli_sram_addr,
+ req_ctx->mlli_params.mlli_len);
+ set_flow_mode(&desc[*seq_size], BYPASS);
+ (*seq_size)++;
+ }
+}
+
+static enum cc_flow_mode cc_get_data_flow(enum drv_crypto_direction direct,
+ enum cc_flow_mode setup_flow_mode,
+ bool is_single_pass)
+{
+ enum cc_flow_mode data_flow_mode;
+
+ if (direct == DRV_CRYPTO_DIRECTION_ENCRYPT) {
+ if (setup_flow_mode == S_DIN_to_AES)
+ data_flow_mode = is_single_pass ?
+ AES_to_HASH_and_DOUT : DIN_AES_DOUT;
+ else
+ data_flow_mode = is_single_pass ?
+ DES_to_HASH_and_DOUT : DIN_DES_DOUT;
+ } else { /* Decrypt */
+ if (setup_flow_mode == S_DIN_to_AES)
+ data_flow_mode = is_single_pass ?
+ AES_and_HASH : DIN_AES_DOUT;
+ else
+ data_flow_mode = is_single_pass ?
+ DES_and_HASH : DIN_DES_DOUT;
+ }
+
+ return data_flow_mode;
+}
+
+static void cc_hmac_authenc(struct aead_request *req, struct cc_hw_desc desc[],
+ unsigned int *seq_size)
+{
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+ struct aead_req_ctx *req_ctx = aead_request_ctx(req);
+ int direct = req_ctx->gen_ctx.op_type;
+ unsigned int data_flow_mode =
+ cc_get_data_flow(direct, ctx->flow_mode,
+ req_ctx->is_single_pass);
+
+ if (req_ctx->is_single_pass) {
+ /**
+ * Single-pass flow
+ */
+ cc_set_hmac_desc(req, desc, seq_size);
+ cc_set_cipher_desc(req, desc, seq_size);
+ cc_proc_header_desc(req, desc, seq_size);
+ cc_proc_cipher_desc(req, data_flow_mode, desc, seq_size);
+ cc_proc_scheme_desc(req, desc, seq_size);
+ cc_proc_digest_desc(req, desc, seq_size);
+ return;
+ }
+
+ /**
+ * Double-pass flow
+ * Fallback for unsupported single-pass modes,
+ * i.e. using assoc. data of non-word-multiple
+ */
+ if (direct == DRV_CRYPTO_DIRECTION_ENCRYPT) {
+ /* encrypt first.. */
+ cc_proc_cipher(req, desc, seq_size, data_flow_mode);
+ /* authenc after..*/
+ cc_set_hmac_desc(req, desc, seq_size);
+ cc_proc_authen_desc(req, DIN_HASH, desc, seq_size, direct);
+ cc_proc_scheme_desc(req, desc, seq_size);
+ cc_proc_digest_desc(req, desc, seq_size);
+
+ } else { /*DECRYPT*/
+ /* authenc first..*/
+ cc_set_hmac_desc(req, desc, seq_size);
+ cc_proc_authen_desc(req, DIN_HASH, desc, seq_size, direct);
+ cc_proc_scheme_desc(req, desc, seq_size);
+ /* decrypt after.. */
+ cc_proc_cipher(req, desc, seq_size, data_flow_mode);
+ /* read the digest result with setting the completion bit
+ * must be after the cipher operation
+ */
+ cc_proc_digest_desc(req, desc, seq_size);
+ }
+}
+
+static void
+cc_xcbc_authenc(struct aead_request *req, struct cc_hw_desc desc[],
+ unsigned int *seq_size)
+{
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+ struct aead_req_ctx *req_ctx = aead_request_ctx(req);
+ int direct = req_ctx->gen_ctx.op_type;
+ unsigned int data_flow_mode =
+ cc_get_data_flow(direct, ctx->flow_mode,
+ req_ctx->is_single_pass);
+
+ if (req_ctx->is_single_pass) {
+ /**
+ * Single-pass flow
+ */
+ cc_set_xcbc_desc(req, desc, seq_size);
+ cc_set_cipher_desc(req, desc, seq_size);
+ cc_proc_header_desc(req, desc, seq_size);
+ cc_proc_cipher_desc(req, data_flow_mode, desc, seq_size);
+ cc_proc_digest_desc(req, desc, seq_size);
+ return;
+ }
+
+ /**
+ * Double-pass flow
+ * Fallback for unsupported single-pass modes,
+ * i.e. using assoc. data of non-word-multiple
+ */
+ if (direct == DRV_CRYPTO_DIRECTION_ENCRYPT) {
+ /* encrypt first.. */
+ cc_proc_cipher(req, desc, seq_size, data_flow_mode);
+ /* authenc after.. */
+ cc_set_xcbc_desc(req, desc, seq_size);
+ cc_proc_authen_desc(req, DIN_HASH, desc, seq_size, direct);
+ cc_proc_digest_desc(req, desc, seq_size);
+ } else { /*DECRYPT*/
+ /* authenc first.. */
+ cc_set_xcbc_desc(req, desc, seq_size);
+ cc_proc_authen_desc(req, DIN_HASH, desc, seq_size, direct);
+ /* decrypt after..*/
+ cc_proc_cipher(req, desc, seq_size, data_flow_mode);
+ /* read the digest result with setting the completion bit
+ * must be after the cipher operation
+ */
+ cc_proc_digest_desc(req, desc, seq_size);
+ }
+}
+
+static int validate_data_size(struct cc_aead_ctx *ctx,
+ enum drv_crypto_direction direct,
+ struct aead_request *req)
+{
+ struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+ unsigned int assoclen = req->assoclen;
+ unsigned int cipherlen = (direct == DRV_CRYPTO_DIRECTION_DECRYPT) ?
+ (req->cryptlen - ctx->authsize) : req->cryptlen;
+
+ if (direct == DRV_CRYPTO_DIRECTION_DECRYPT &&
+ req->cryptlen < ctx->authsize)
+ goto data_size_err;
+
+ areq_ctx->is_single_pass = true; /*defaulted to fast flow*/
+
+ switch (ctx->flow_mode) {
+ case S_DIN_to_AES:
+ if (ctx->cipher_mode == DRV_CIPHER_CBC &&
+ !IS_ALIGNED(cipherlen, AES_BLOCK_SIZE))
+ goto data_size_err;
+ if (ctx->cipher_mode == DRV_CIPHER_CCM)
+ break;
+ if (ctx->cipher_mode == DRV_CIPHER_GCTR) {
+ if (areq_ctx->plaintext_authenticate_only)
+ areq_ctx->is_single_pass = false;
+ break;
+ }
+
+ if (!IS_ALIGNED(assoclen, sizeof(u32)))
+ areq_ctx->is_single_pass = false;
+
+ if (ctx->cipher_mode == DRV_CIPHER_CTR &&
+ !IS_ALIGNED(cipherlen, sizeof(u32)))
+ areq_ctx->is_single_pass = false;
+
+ break;
+ case S_DIN_to_DES:
+ if (!IS_ALIGNED(cipherlen, DES_BLOCK_SIZE))
+ goto data_size_err;
+ if (!IS_ALIGNED(assoclen, DES_BLOCK_SIZE))
+ areq_ctx->is_single_pass = false;
+ break;
+ default:
+ dev_err(dev, "Unexpected flow mode (%d)\n", ctx->flow_mode);
+ goto data_size_err;
+ }
+
+ return 0;
+
+data_size_err:
+ return -EINVAL;
+}
+
+static unsigned int format_ccm_a0(u8 *pa0_buff, u32 header_size)
+{
+ unsigned int len = 0;
+
+ if (header_size == 0)
+ return 0;
+
+ if (header_size < ((1UL << 16) - (1UL << 8))) {
+ len = 2;
+
+ pa0_buff[0] = (header_size >> 8) & 0xFF;
+ pa0_buff[1] = header_size & 0xFF;
+ } else {
+ len = 6;
+
+ pa0_buff[0] = 0xFF;
+ pa0_buff[1] = 0xFE;
+ pa0_buff[2] = (header_size >> 24) & 0xFF;
+ pa0_buff[3] = (header_size >> 16) & 0xFF;
+ pa0_buff[4] = (header_size >> 8) & 0xFF;
+ pa0_buff[5] = header_size & 0xFF;
+ }
+
+ return len;
+}
+
+static int set_msg_len(u8 *block, unsigned int msglen, unsigned int csize)
+{
+ __be32 data;
+
+ memset(block, 0, csize);
+ block += csize;
+
+ if (csize >= 4)
+ csize = 4;
+ else if (msglen > (1 << (8 * csize)))
+ return -EOVERFLOW;
+
+ data = cpu_to_be32(msglen);
+ memcpy(block - csize, (u8 *)&data + 4 - csize, csize);
+
+ return 0;
+}
+
+static int cc_ccm(struct aead_request *req, struct cc_hw_desc desc[],
+ unsigned int *seq_size)
+{
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+ struct aead_req_ctx *req_ctx = aead_request_ctx(req);
+ unsigned int idx = *seq_size;
+ unsigned int cipher_flow_mode;
+ dma_addr_t mac_result;
+
+ if (req_ctx->gen_ctx.op_type == DRV_CRYPTO_DIRECTION_DECRYPT) {
+ cipher_flow_mode = AES_to_HASH_and_DOUT;
+ mac_result = req_ctx->mac_buf_dma_addr;
+ } else { /* Encrypt */
+ cipher_flow_mode = AES_and_HASH;
+ mac_result = req_ctx->icv_dma_addr;
+ }
+
+ /* load key */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], DRV_CIPHER_CTR);
+ set_din_type(&desc[idx], DMA_DLLI, ctx->enckey_dma_addr,
+ ((ctx->enc_keylen == 24) ? CC_AES_KEY_SIZE_MAX :
+ ctx->enc_keylen), NS_BIT);
+ set_key_size_aes(&desc[idx], ctx->enc_keylen);
+ set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
+ set_cipher_config0(&desc[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
+ set_flow_mode(&desc[idx], S_DIN_to_AES);
+ idx++;
+
+ /* load ctr state */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], DRV_CIPHER_CTR);
+ set_key_size_aes(&desc[idx], ctx->enc_keylen);
+ set_din_type(&desc[idx], DMA_DLLI,
+ req_ctx->gen_ctx.iv_dma_addr, AES_BLOCK_SIZE, NS_BIT);
+ set_cipher_config0(&desc[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
+ set_setup_mode(&desc[idx], SETUP_LOAD_STATE1);
+ set_flow_mode(&desc[idx], S_DIN_to_AES);
+ idx++;
+
+ /* load MAC key */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], DRV_CIPHER_CBC_MAC);
+ set_din_type(&desc[idx], DMA_DLLI, ctx->enckey_dma_addr,
+ ((ctx->enc_keylen == 24) ? CC_AES_KEY_SIZE_MAX :
+ ctx->enc_keylen), NS_BIT);
+ set_key_size_aes(&desc[idx], ctx->enc_keylen);
+ set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
+ set_cipher_config0(&desc[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
+ set_flow_mode(&desc[idx], S_DIN_to_HASH);
+ set_aes_not_hash_mode(&desc[idx]);
+ idx++;
+
+ /* load MAC state */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], DRV_CIPHER_CBC_MAC);
+ set_key_size_aes(&desc[idx], ctx->enc_keylen);
+ set_din_type(&desc[idx], DMA_DLLI, req_ctx->mac_buf_dma_addr,
+ AES_BLOCK_SIZE, NS_BIT);
+ set_cipher_config0(&desc[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
+ set_setup_mode(&desc[idx], SETUP_LOAD_STATE0);
+ set_flow_mode(&desc[idx], S_DIN_to_HASH);
+ set_aes_not_hash_mode(&desc[idx]);
+ idx++;
+
+ /* process assoc data */
+ if (req->assoclen > 0) {
+ cc_set_assoc_desc(req, DIN_HASH, desc, &idx);
+ } else {
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_DLLI,
+ sg_dma_address(&req_ctx->ccm_adata_sg),
+ AES_BLOCK_SIZE + req_ctx->ccm_hdr_size, NS_BIT);
+ set_flow_mode(&desc[idx], DIN_HASH);
+ idx++;
+ }
+
+ /* process the cipher */
+ if (req_ctx->cryptlen)
+ cc_proc_cipher_desc(req, cipher_flow_mode, desc, &idx);
+
+ /* Read temporal MAC */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], DRV_CIPHER_CBC_MAC);
+ set_dout_dlli(&desc[idx], req_ctx->mac_buf_dma_addr, ctx->authsize,
+ NS_BIT, 0);
+ set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
+ set_cipher_config0(&desc[idx], HASH_DIGEST_RESULT_LITTLE_ENDIAN);
+ set_flow_mode(&desc[idx], S_HASH_to_DOUT);
+ set_aes_not_hash_mode(&desc[idx]);
+ idx++;
+
+ /* load AES-CTR state (for last MAC calculation)*/
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], DRV_CIPHER_CTR);
+ set_cipher_config0(&desc[idx], DRV_CRYPTO_DIRECTION_ENCRYPT);
+ set_din_type(&desc[idx], DMA_DLLI, req_ctx->ccm_iv0_dma_addr,
+ AES_BLOCK_SIZE, NS_BIT);
+ set_key_size_aes(&desc[idx], ctx->enc_keylen);
+ set_setup_mode(&desc[idx], SETUP_LOAD_STATE1);
+ set_flow_mode(&desc[idx], S_DIN_to_AES);
+ idx++;
+
+ hw_desc_init(&desc[idx]);
+ set_din_no_dma(&desc[idx], 0, 0xfffff0);
+ set_dout_no_dma(&desc[idx], 0, 0, 1);
+ idx++;
+
+ /* encrypt the "T" value and store MAC in mac_state */
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_DLLI, req_ctx->mac_buf_dma_addr,
+ ctx->authsize, NS_BIT);
+ set_dout_dlli(&desc[idx], mac_result, ctx->authsize, NS_BIT, 1);
+ set_queue_last_ind(ctx->drvdata, &desc[idx]);
+ set_flow_mode(&desc[idx], DIN_AES_DOUT);
+ idx++;
+
+ *seq_size = idx;
+ return 0;
+}
+
+static int config_ccm_adata(struct aead_request *req)
+{
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+ struct aead_req_ctx *req_ctx = aead_request_ctx(req);
+ //unsigned int size_of_a = 0, rem_a_size = 0;
+ unsigned int lp = req->iv[0];
+ /* Note: The code assume that req->iv[0] already contains the value
+ * of L' of RFC3610
+ */
+ unsigned int l = lp + 1; /* This is L' of RFC 3610. */
+ unsigned int m = ctx->authsize; /* This is M' of RFC 3610. */
+ u8 *b0 = req_ctx->ccm_config + CCM_B0_OFFSET;
+ u8 *a0 = req_ctx->ccm_config + CCM_A0_OFFSET;
+ u8 *ctr_count_0 = req_ctx->ccm_config + CCM_CTR_COUNT_0_OFFSET;
+ unsigned int cryptlen = (req_ctx->gen_ctx.op_type ==
+ DRV_CRYPTO_DIRECTION_ENCRYPT) ?
+ req->cryptlen :
+ (req->cryptlen - ctx->authsize);
+ int rc;
+
+ memset(req_ctx->mac_buf, 0, AES_BLOCK_SIZE);
+ memset(req_ctx->ccm_config, 0, AES_BLOCK_SIZE * 3);
+
+ /* taken from crypto/ccm.c */
+ /* 2 <= L <= 8, so 1 <= L' <= 7. */
+ if (l < 2 || l > 8) {
+ dev_err(dev, "illegal iv value %X\n", req->iv[0]);
+ return -EINVAL;
+ }
+ memcpy(b0, req->iv, AES_BLOCK_SIZE);
+
+ /* format control info per RFC 3610 and
+ * NIST Special Publication 800-38C
+ */
+ *b0 |= (8 * ((m - 2) / 2));
+ if (req->assoclen > 0)
+ *b0 |= 64; /* Enable bit 6 if Adata exists. */
+
+ rc = set_msg_len(b0 + 16 - l, cryptlen, l); /* Write L'. */
+ if (rc) {
+ dev_err(dev, "message len overflow detected");
+ return rc;
+ }
+ /* END of "taken from crypto/ccm.c" */
+
+ /* l(a) - size of associated data. */
+ req_ctx->ccm_hdr_size = format_ccm_a0(a0, req->assoclen);
+
+ memset(req->iv + 15 - req->iv[0], 0, req->iv[0] + 1);
+ req->iv[15] = 1;
+
+ memcpy(ctr_count_0, req->iv, AES_BLOCK_SIZE);
+ ctr_count_0[15] = 0;
+
+ return 0;
+}
+
+static void cc_proc_rfc4309_ccm(struct aead_request *req)
+{
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+ struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
+
+ /* L' */
+ memset(areq_ctx->ctr_iv, 0, AES_BLOCK_SIZE);
+ /* For RFC 4309, always use 4 bytes for message length
+ * (at most 2^32-1 bytes).
+ */
+ areq_ctx->ctr_iv[0] = 3;
+
+ /* In RFC 4309 there is an 11-bytes nonce+IV part,
+ * that we build here.
+ */
+ memcpy(areq_ctx->ctr_iv + CCM_BLOCK_NONCE_OFFSET, ctx->ctr_nonce,
+ CCM_BLOCK_NONCE_SIZE);
+ memcpy(areq_ctx->ctr_iv + CCM_BLOCK_IV_OFFSET, req->iv,
+ CCM_BLOCK_IV_SIZE);
+ req->iv = areq_ctx->ctr_iv;
+ req->assoclen -= CCM_BLOCK_IV_SIZE;
+}
+
+static void cc_set_ghash_desc(struct aead_request *req,
+ struct cc_hw_desc desc[], unsigned int *seq_size)
+{
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+ struct aead_req_ctx *req_ctx = aead_request_ctx(req);
+ unsigned int idx = *seq_size;
+
+ /* load key to AES*/
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], DRV_CIPHER_ECB);
+ set_cipher_config0(&desc[idx], DRV_CRYPTO_DIRECTION_ENCRYPT);
+ set_din_type(&desc[idx], DMA_DLLI, ctx->enckey_dma_addr,
+ ctx->enc_keylen, NS_BIT);
+ set_key_size_aes(&desc[idx], ctx->enc_keylen);
+ set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
+ set_flow_mode(&desc[idx], S_DIN_to_AES);
+ idx++;
+
+ /* process one zero block to generate hkey */
+ hw_desc_init(&desc[idx]);
+ set_din_const(&desc[idx], 0x0, AES_BLOCK_SIZE);
+ set_dout_dlli(&desc[idx], req_ctx->hkey_dma_addr, AES_BLOCK_SIZE,
+ NS_BIT, 0);
+ set_flow_mode(&desc[idx], DIN_AES_DOUT);
+ idx++;
+
+ /* Memory Barrier */
+ hw_desc_init(&desc[idx]);
+ set_din_no_dma(&desc[idx], 0, 0xfffff0);
+ set_dout_no_dma(&desc[idx], 0, 0, 1);
+ idx++;
+
+ /* Load GHASH subkey */
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_DLLI, req_ctx->hkey_dma_addr,
+ AES_BLOCK_SIZE, NS_BIT);
+ set_dout_no_dma(&desc[idx], 0, 0, 1);
+ set_flow_mode(&desc[idx], S_DIN_to_HASH);
+ set_aes_not_hash_mode(&desc[idx]);
+ set_cipher_mode(&desc[idx], DRV_HASH_HW_GHASH);
+ set_cipher_config1(&desc[idx], HASH_PADDING_ENABLED);
+ set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
+ idx++;
+
+ /* Configure Hash Engine to work with GHASH.
+ * Since it was not possible to extend HASH submodes to add GHASH,
+ * The following command is necessary in order to
+ * select GHASH (according to HW designers)
+ */
+ hw_desc_init(&desc[idx]);
+ set_din_no_dma(&desc[idx], 0, 0xfffff0);
+ set_dout_no_dma(&desc[idx], 0, 0, 1);
+ set_flow_mode(&desc[idx], S_DIN_to_HASH);
+ set_aes_not_hash_mode(&desc[idx]);
+ set_cipher_mode(&desc[idx], DRV_HASH_HW_GHASH);
+ set_cipher_do(&desc[idx], 1); //1=AES_SK RKEK
+ set_cipher_config0(&desc[idx], DRV_CRYPTO_DIRECTION_ENCRYPT);
+ set_cipher_config1(&desc[idx], HASH_PADDING_ENABLED);
+ set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
+ idx++;
+
+ /* Load GHASH initial STATE (which is 0). (for any hash there is an
+ * initial state)
+ */
+ hw_desc_init(&desc[idx]);
+ set_din_const(&desc[idx], 0x0, AES_BLOCK_SIZE);
+ set_dout_no_dma(&desc[idx], 0, 0, 1);
+ set_flow_mode(&desc[idx], S_DIN_to_HASH);
+ set_aes_not_hash_mode(&desc[idx]);
+ set_cipher_mode(&desc[idx], DRV_HASH_HW_GHASH);
+ set_cipher_config1(&desc[idx], HASH_PADDING_ENABLED);
+ set_setup_mode(&desc[idx], SETUP_LOAD_STATE0);
+ idx++;
+
+ *seq_size = idx;
+}
+
+static void cc_set_gctr_desc(struct aead_request *req, struct cc_hw_desc desc[],
+ unsigned int *seq_size)
+{
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+ struct aead_req_ctx *req_ctx = aead_request_ctx(req);
+ unsigned int idx = *seq_size;
+
+ /* load key to AES*/
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], DRV_CIPHER_GCTR);
+ set_cipher_config0(&desc[idx], DRV_CRYPTO_DIRECTION_ENCRYPT);
+ set_din_type(&desc[idx], DMA_DLLI, ctx->enckey_dma_addr,
+ ctx->enc_keylen, NS_BIT);
+ set_key_size_aes(&desc[idx], ctx->enc_keylen);
+ set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
+ set_flow_mode(&desc[idx], S_DIN_to_AES);
+ idx++;
+
+ if (req_ctx->cryptlen && !req_ctx->plaintext_authenticate_only) {
+ /* load AES/CTR initial CTR value inc by 2*/
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], DRV_CIPHER_GCTR);
+ set_key_size_aes(&desc[idx], ctx->enc_keylen);
+ set_din_type(&desc[idx], DMA_DLLI,
+ req_ctx->gcm_iv_inc2_dma_addr, AES_BLOCK_SIZE,
+ NS_BIT);
+ set_cipher_config0(&desc[idx], DRV_CRYPTO_DIRECTION_ENCRYPT);
+ set_setup_mode(&desc[idx], SETUP_LOAD_STATE1);
+ set_flow_mode(&desc[idx], S_DIN_to_AES);
+ idx++;
+ }
+
+ *seq_size = idx;
+}
+
+static void cc_proc_gcm_result(struct aead_request *req,
+ struct cc_hw_desc desc[],
+ unsigned int *seq_size)
+{
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+ struct aead_req_ctx *req_ctx = aead_request_ctx(req);
+ dma_addr_t mac_result;
+ unsigned int idx = *seq_size;
+
+ if (req_ctx->gen_ctx.op_type == DRV_CRYPTO_DIRECTION_DECRYPT) {
+ mac_result = req_ctx->mac_buf_dma_addr;
+ } else { /* Encrypt */
+ mac_result = req_ctx->icv_dma_addr;
+ }
+
+ /* process(ghash) gcm_block_len */
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_DLLI, req_ctx->gcm_block_len_dma_addr,
+ AES_BLOCK_SIZE, NS_BIT);
+ set_flow_mode(&desc[idx], DIN_HASH);
+ idx++;
+
+ /* Store GHASH state after GHASH(Associated Data + Cipher +LenBlock) */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], DRV_HASH_HW_GHASH);
+ set_din_no_dma(&desc[idx], 0, 0xfffff0);
+ set_dout_dlli(&desc[idx], req_ctx->mac_buf_dma_addr, AES_BLOCK_SIZE,
+ NS_BIT, 0);
+ set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
+ set_flow_mode(&desc[idx], S_HASH_to_DOUT);
+ set_aes_not_hash_mode(&desc[idx]);
+
+ idx++;
+
+ /* load AES/CTR initial CTR value inc by 1*/
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], DRV_CIPHER_GCTR);
+ set_key_size_aes(&desc[idx], ctx->enc_keylen);
+ set_din_type(&desc[idx], DMA_DLLI, req_ctx->gcm_iv_inc1_dma_addr,
+ AES_BLOCK_SIZE, NS_BIT);
+ set_cipher_config0(&desc[idx], DRV_CRYPTO_DIRECTION_ENCRYPT);
+ set_setup_mode(&desc[idx], SETUP_LOAD_STATE1);
+ set_flow_mode(&desc[idx], S_DIN_to_AES);
+ idx++;
+
+ /* Memory Barrier */
+ hw_desc_init(&desc[idx]);
+ set_din_no_dma(&desc[idx], 0, 0xfffff0);
+ set_dout_no_dma(&desc[idx], 0, 0, 1);
+ idx++;
+
+ /* process GCTR on stored GHASH and store MAC in mac_state*/
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], DRV_CIPHER_GCTR);
+ set_din_type(&desc[idx], DMA_DLLI, req_ctx->mac_buf_dma_addr,
+ AES_BLOCK_SIZE, NS_BIT);
+ set_dout_dlli(&desc[idx], mac_result, ctx->authsize, NS_BIT, 1);
+ set_queue_last_ind(ctx->drvdata, &desc[idx]);
+ set_flow_mode(&desc[idx], DIN_AES_DOUT);
+ idx++;
+
+ *seq_size = idx;
+}
+
+static int cc_gcm(struct aead_request *req, struct cc_hw_desc desc[],
+ unsigned int *seq_size)
+{
+ struct aead_req_ctx *req_ctx = aead_request_ctx(req);
+ unsigned int cipher_flow_mode;
+
+ if (req_ctx->gen_ctx.op_type == DRV_CRYPTO_DIRECTION_DECRYPT) {
+ cipher_flow_mode = AES_and_HASH;
+ } else { /* Encrypt */
+ cipher_flow_mode = AES_to_HASH_and_DOUT;
+ }
+
+ //in RFC4543 no data to encrypt. just copy data from src to dest.
+ if (req_ctx->plaintext_authenticate_only) {
+ cc_proc_cipher_desc(req, BYPASS, desc, seq_size);
+ cc_set_ghash_desc(req, desc, seq_size);
+ /* process(ghash) assoc data */
+ cc_set_assoc_desc(req, DIN_HASH, desc, seq_size);
+ cc_set_gctr_desc(req, desc, seq_size);
+ cc_proc_gcm_result(req, desc, seq_size);
+ return 0;
+ }
+
+ // for gcm and rfc4106.
+ cc_set_ghash_desc(req, desc, seq_size);
+ /* process(ghash) assoc data */
+ if (req->assoclen > 0)
+ cc_set_assoc_desc(req, DIN_HASH, desc, seq_size);
+ cc_set_gctr_desc(req, desc, seq_size);
+ /* process(gctr+ghash) */
+ if (req_ctx->cryptlen)
+ cc_proc_cipher_desc(req, cipher_flow_mode, desc, seq_size);
+ cc_proc_gcm_result(req, desc, seq_size);
+
+ return 0;
+}
+
+static int config_gcm_context(struct aead_request *req)
+{
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+ struct aead_req_ctx *req_ctx = aead_request_ctx(req);
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+
+ unsigned int cryptlen = (req_ctx->gen_ctx.op_type ==
+ DRV_CRYPTO_DIRECTION_ENCRYPT) ?
+ req->cryptlen :
+ (req->cryptlen - ctx->authsize);
+ __be32 counter = cpu_to_be32(2);
+
+ dev_dbg(dev, "%s() cryptlen = %d, req->assoclen = %d ctx->authsize = %d\n",
+ __func__, cryptlen, req->assoclen, ctx->authsize);
+
+ memset(req_ctx->hkey, 0, AES_BLOCK_SIZE);
+
+ memset(req_ctx->mac_buf, 0, AES_BLOCK_SIZE);
+
+ memcpy(req->iv + 12, &counter, 4);
+ memcpy(req_ctx->gcm_iv_inc2, req->iv, 16);
+
+ counter = cpu_to_be32(1);
+ memcpy(req->iv + 12, &counter, 4);
+ memcpy(req_ctx->gcm_iv_inc1, req->iv, 16);
+
+ if (!req_ctx->plaintext_authenticate_only) {
+ __be64 temp64;
+
+ temp64 = cpu_to_be64(req->assoclen * 8);
+ memcpy(&req_ctx->gcm_len_block.len_a, &temp64, sizeof(temp64));
+ temp64 = cpu_to_be64(cryptlen * 8);
+ memcpy(&req_ctx->gcm_len_block.len_c, &temp64, 8);
+ } else {
+ /* rfc4543=> all data(AAD,IV,Plain) are considered additional
+ * data that is nothing is encrypted.
+ */
+ __be64 temp64;
+
+ temp64 = cpu_to_be64((req->assoclen + GCM_BLOCK_RFC4_IV_SIZE +
+ cryptlen) * 8);
+ memcpy(&req_ctx->gcm_len_block.len_a, &temp64, sizeof(temp64));
+ temp64 = 0;
+ memcpy(&req_ctx->gcm_len_block.len_c, &temp64, 8);
+ }
+
+ return 0;
+}
+
+static void cc_proc_rfc4_gcm(struct aead_request *req)
+{
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+ struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
+
+ memcpy(areq_ctx->ctr_iv + GCM_BLOCK_RFC4_NONCE_OFFSET,
+ ctx->ctr_nonce, GCM_BLOCK_RFC4_NONCE_SIZE);
+ memcpy(areq_ctx->ctr_iv + GCM_BLOCK_RFC4_IV_OFFSET, req->iv,
+ GCM_BLOCK_RFC4_IV_SIZE);
+ req->iv = areq_ctx->ctr_iv;
+ req->assoclen -= GCM_BLOCK_RFC4_IV_SIZE;
+}
+
+static int cc_proc_aead(struct aead_request *req,
+ enum drv_crypto_direction direct)
+{
+ int rc = 0;
+ int seq_len = 0;
+ struct cc_hw_desc desc[MAX_AEAD_PROCESS_SEQ];
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+ struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+ struct cc_crypto_req cc_req = {};
+
+ dev_dbg(dev, "%s context=%p req=%p iv=%p src=%p src_ofs=%d dst=%p dst_ofs=%d cryptolen=%d\n",
+ ((direct == DRV_CRYPTO_DIRECTION_ENCRYPT) ? "Enc" : "Dec"),
+ ctx, req, req->iv, sg_virt(req->src), req->src->offset,
+ sg_virt(req->dst), req->dst->offset, req->cryptlen);
+
+ /* STAT_PHASE_0: Init and sanity checks */
+
+ /* Check data length according to mode */
+ if (validate_data_size(ctx, direct, req)) {
+ dev_err(dev, "Unsupported crypt/assoc len %d/%d.\n",
+ req->cryptlen, req->assoclen);
+ crypto_aead_set_flags(tfm, CRYPTO_TFM_RES_BAD_BLOCK_LEN);
+ return -EINVAL;
+ }
+
+ /* Setup request structure */
+ cc_req.user_cb = (void *)cc_aead_complete;
+ cc_req.user_arg = (void *)req;
+
+ /* Setup request context */
+ areq_ctx->gen_ctx.op_type = direct;
+ areq_ctx->req_authsize = ctx->authsize;
+ areq_ctx->cipher_mode = ctx->cipher_mode;
+
+ /* STAT_PHASE_1: Map buffers */
+
+ if (ctx->cipher_mode == DRV_CIPHER_CTR) {
+ /* Build CTR IV - Copy nonce from last 4 bytes in
+ * CTR key to first 4 bytes in CTR IV
+ */
+ memcpy(areq_ctx->ctr_iv, ctx->ctr_nonce,
+ CTR_RFC3686_NONCE_SIZE);
+ if (!areq_ctx->backup_giv) /*User none-generated IV*/
+ memcpy(areq_ctx->ctr_iv + CTR_RFC3686_NONCE_SIZE,
+ req->iv, CTR_RFC3686_IV_SIZE);
+ /* Initialize counter portion of counter block */
+ *(__be32 *)(areq_ctx->ctr_iv + CTR_RFC3686_NONCE_SIZE +
+ CTR_RFC3686_IV_SIZE) = cpu_to_be32(1);
+
+ /* Replace with counter iv */
+ req->iv = areq_ctx->ctr_iv;
+ areq_ctx->hw_iv_size = CTR_RFC3686_BLOCK_SIZE;
+ } else if ((ctx->cipher_mode == DRV_CIPHER_CCM) ||
+ (ctx->cipher_mode == DRV_CIPHER_GCTR)) {
+ areq_ctx->hw_iv_size = AES_BLOCK_SIZE;
+ if (areq_ctx->ctr_iv != req->iv) {
+ memcpy(areq_ctx->ctr_iv, req->iv,
+ crypto_aead_ivsize(tfm));
+ req->iv = areq_ctx->ctr_iv;
+ }
+ } else {
+ areq_ctx->hw_iv_size = crypto_aead_ivsize(tfm);
+ }
+
+ if (ctx->cipher_mode == DRV_CIPHER_CCM) {
+ rc = config_ccm_adata(req);
+ if (rc) {
+ dev_dbg(dev, "config_ccm_adata() returned with a failure %d!",
+ rc);
+ goto exit;
+ }
+ } else {
+ areq_ctx->ccm_hdr_size = ccm_header_size_null;
+ }
+
+ if (ctx->cipher_mode == DRV_CIPHER_GCTR) {
+ rc = config_gcm_context(req);
+ if (rc) {
+ dev_dbg(dev, "config_gcm_context() returned with a failure %d!",
+ rc);
+ goto exit;
+ }
+ }
+
+ rc = cc_map_aead_request(ctx->drvdata, req);
+ if (rc) {
+ dev_err(dev, "map_request() failed\n");
+ goto exit;
+ }
+
+ /* do we need to generate IV? */
+ if (areq_ctx->backup_giv) {
+ /* set the DMA mapped IV address*/
+ if (ctx->cipher_mode == DRV_CIPHER_CTR) {
+ cc_req.ivgen_dma_addr[0] =
+ areq_ctx->gen_ctx.iv_dma_addr +
+ CTR_RFC3686_NONCE_SIZE;
+ cc_req.ivgen_dma_addr_len = 1;
+ } else if (ctx->cipher_mode == DRV_CIPHER_CCM) {
+ /* In ccm, the IV needs to exist both inside B0 and
+ * inside the counter.It is also copied to iv_dma_addr
+ * for other reasons (like returning it to the user).
+ * So, using 3 (identical) IV outputs.
+ */
+ cc_req.ivgen_dma_addr[0] =
+ areq_ctx->gen_ctx.iv_dma_addr +
+ CCM_BLOCK_IV_OFFSET;
+ cc_req.ivgen_dma_addr[1] =
+ sg_dma_address(&areq_ctx->ccm_adata_sg) +
+ CCM_B0_OFFSET + CCM_BLOCK_IV_OFFSET;
+ cc_req.ivgen_dma_addr[2] =
+ sg_dma_address(&areq_ctx->ccm_adata_sg) +
+ CCM_CTR_COUNT_0_OFFSET + CCM_BLOCK_IV_OFFSET;
+ cc_req.ivgen_dma_addr_len = 3;
+ } else {
+ cc_req.ivgen_dma_addr[0] =
+ areq_ctx->gen_ctx.iv_dma_addr;
+ cc_req.ivgen_dma_addr_len = 1;
+ }
+
+ /* set the IV size (8/16 B long)*/
+ cc_req.ivgen_size = crypto_aead_ivsize(tfm);
+ }
+
+ /* STAT_PHASE_2: Create sequence */
+
+ /* Load MLLI tables to SRAM if necessary */
+ cc_mlli_to_sram(req, desc, &seq_len);
+
+ /*TODO: move seq len by reference */
+ switch (ctx->auth_mode) {
+ case DRV_HASH_SHA1:
+ case DRV_HASH_SHA256:
+ cc_hmac_authenc(req, desc, &seq_len);
+ break;
+ case DRV_HASH_XCBC_MAC:
+ cc_xcbc_authenc(req, desc, &seq_len);
+ break;
+ case DRV_HASH_NULL:
+ if (ctx->cipher_mode == DRV_CIPHER_CCM)
+ cc_ccm(req, desc, &seq_len);
+ if (ctx->cipher_mode == DRV_CIPHER_GCTR)
+ cc_gcm(req, desc, &seq_len);
+ break;
+ default:
+ dev_err(dev, "Unsupported authenc (%d)\n", ctx->auth_mode);
+ cc_unmap_aead_request(dev, req);
+ rc = -ENOTSUPP;
+ goto exit;
+ }
+
+ /* STAT_PHASE_3: Lock HW and push sequence */
+
+ rc = cc_send_request(ctx->drvdata, &cc_req, desc, seq_len, &req->base);
+
+ if (rc != -EINPROGRESS && rc != -EBUSY) {
+ dev_err(dev, "send_request() failed (rc=%d)\n", rc);
+ cc_unmap_aead_request(dev, req);
+ }
+
+exit:
+ return rc;
+}
+
+static int cc_aead_encrypt(struct aead_request *req)
+{
+ struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
+ int rc;
+
+ /* No generated IV required */
+ areq_ctx->backup_iv = req->iv;
+ areq_ctx->backup_giv = NULL;
+ areq_ctx->is_gcm4543 = false;
+
+ areq_ctx->plaintext_authenticate_only = false;
+
+ rc = cc_proc_aead(req, DRV_CRYPTO_DIRECTION_ENCRYPT);
+ if (rc != -EINPROGRESS && rc != -EBUSY)
+ req->iv = areq_ctx->backup_iv;
+
+ return rc;
+}
+
+static int cc_rfc4309_ccm_encrypt(struct aead_request *req)
+{
+ /* Very similar to cc_aead_encrypt() above. */
+
+ struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+ int rc = -EINVAL;
+
+ if (!valid_assoclen(req)) {
+ dev_err(dev, "invalid Assoclen:%u\n", req->assoclen);
+ goto out;
+ }
+
+ /* No generated IV required */
+ areq_ctx->backup_iv = req->iv;
+ areq_ctx->backup_giv = NULL;
+ areq_ctx->is_gcm4543 = true;
+
+ cc_proc_rfc4309_ccm(req);
+
+ rc = cc_proc_aead(req, DRV_CRYPTO_DIRECTION_ENCRYPT);
+ if (rc != -EINPROGRESS && rc != -EBUSY)
+ req->iv = areq_ctx->backup_iv;
+out:
+ return rc;
+}
+
+static int cc_aead_decrypt(struct aead_request *req)
+{
+ struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
+ int rc;
+
+ /* No generated IV required */
+ areq_ctx->backup_iv = req->iv;
+ areq_ctx->backup_giv = NULL;
+ areq_ctx->is_gcm4543 = false;
+
+ areq_ctx->plaintext_authenticate_only = false;
+
+ rc = cc_proc_aead(req, DRV_CRYPTO_DIRECTION_DECRYPT);
+ if (rc != -EINPROGRESS && rc != -EBUSY)
+ req->iv = areq_ctx->backup_iv;
+
+ return rc;
+}
+
+static int cc_rfc4309_ccm_decrypt(struct aead_request *req)
+{
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+ struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
+ int rc = -EINVAL;
+
+ if (!valid_assoclen(req)) {
+ dev_err(dev, "invalid Assoclen:%u\n", req->assoclen);
+ goto out;
+ }
+
+ /* No generated IV required */
+ areq_ctx->backup_iv = req->iv;
+ areq_ctx->backup_giv = NULL;
+
+ areq_ctx->is_gcm4543 = true;
+ cc_proc_rfc4309_ccm(req);
+
+ rc = cc_proc_aead(req, DRV_CRYPTO_DIRECTION_DECRYPT);
+ if (rc != -EINPROGRESS && rc != -EBUSY)
+ req->iv = areq_ctx->backup_iv;
+
+out:
+ return rc;
+}
+
+static int cc_rfc4106_gcm_setkey(struct crypto_aead *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+
+ dev_dbg(dev, "%s() keylen %d, key %p\n", __func__, keylen, key);
+
+ if (keylen < 4)
+ return -EINVAL;
+
+ keylen -= 4;
+ memcpy(ctx->ctr_nonce, key + keylen, 4);
+
+ return cc_aead_setkey(tfm, key, keylen);
+}
+
+static int cc_rfc4543_gcm_setkey(struct crypto_aead *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+
+ dev_dbg(dev, "%s() keylen %d, key %p\n", __func__, keylen, key);
+
+ if (keylen < 4)
+ return -EINVAL;
+
+ keylen -= 4;
+ memcpy(ctx->ctr_nonce, key + keylen, 4);
+
+ return cc_aead_setkey(tfm, key, keylen);
+}
+
+static int cc_gcm_setauthsize(struct crypto_aead *authenc,
+ unsigned int authsize)
+{
+ switch (authsize) {
+ case 4:
+ case 8:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return cc_aead_setauthsize(authenc, authsize);
+}
+
+static int cc_rfc4106_gcm_setauthsize(struct crypto_aead *authenc,
+ unsigned int authsize)
+{
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(authenc);
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+
+ dev_dbg(dev, "authsize %d\n", authsize);
+
+ switch (authsize) {
+ case 8:
+ case 12:
+ case 16:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return cc_aead_setauthsize(authenc, authsize);
+}
+
+static int cc_rfc4543_gcm_setauthsize(struct crypto_aead *authenc,
+ unsigned int authsize)
+{
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(authenc);
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+
+ dev_dbg(dev, "authsize %d\n", authsize);
+
+ if (authsize != 16)
+ return -EINVAL;
+
+ return cc_aead_setauthsize(authenc, authsize);
+}
+
+static int cc_rfc4106_gcm_encrypt(struct aead_request *req)
+{
+ /* Very similar to cc_aead_encrypt() above. */
+
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+ struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
+ int rc = -EINVAL;
+
+ if (!valid_assoclen(req)) {
+ dev_err(dev, "invalid Assoclen:%u\n", req->assoclen);
+ goto out;
+ }
+
+ /* No generated IV required */
+ areq_ctx->backup_iv = req->iv;
+ areq_ctx->backup_giv = NULL;
+
+ areq_ctx->plaintext_authenticate_only = false;
+
+ cc_proc_rfc4_gcm(req);
+ areq_ctx->is_gcm4543 = true;
+
+ rc = cc_proc_aead(req, DRV_CRYPTO_DIRECTION_ENCRYPT);
+ if (rc != -EINPROGRESS && rc != -EBUSY)
+ req->iv = areq_ctx->backup_iv;
+out:
+ return rc;
+}
+
+static int cc_rfc4543_gcm_encrypt(struct aead_request *req)
+{
+ /* Very similar to cc_aead_encrypt() above. */
+
+ struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
+ int rc;
+
+ //plaintext is not encryped with rfc4543
+ areq_ctx->plaintext_authenticate_only = true;
+
+ /* No generated IV required */
+ areq_ctx->backup_iv = req->iv;
+ areq_ctx->backup_giv = NULL;
+
+ cc_proc_rfc4_gcm(req);
+ areq_ctx->is_gcm4543 = true;
+
+ rc = cc_proc_aead(req, DRV_CRYPTO_DIRECTION_ENCRYPT);
+ if (rc != -EINPROGRESS && rc != -EBUSY)
+ req->iv = areq_ctx->backup_iv;
+
+ return rc;
+}
+
+static int cc_rfc4106_gcm_decrypt(struct aead_request *req)
+{
+ /* Very similar to cc_aead_decrypt() above. */
+
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+ struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
+ int rc = -EINVAL;
+
+ if (!valid_assoclen(req)) {
+ dev_err(dev, "invalid Assoclen:%u\n", req->assoclen);
+ goto out;
+ }
+
+ /* No generated IV required */
+ areq_ctx->backup_iv = req->iv;
+ areq_ctx->backup_giv = NULL;
+
+ areq_ctx->plaintext_authenticate_only = false;
+
+ cc_proc_rfc4_gcm(req);
+ areq_ctx->is_gcm4543 = true;
+
+ rc = cc_proc_aead(req, DRV_CRYPTO_DIRECTION_DECRYPT);
+ if (rc != -EINPROGRESS && rc != -EBUSY)
+ req->iv = areq_ctx->backup_iv;
+out:
+ return rc;
+}
+
+static int cc_rfc4543_gcm_decrypt(struct aead_request *req)
+{
+ /* Very similar to cc_aead_decrypt() above. */
+
+ struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
+ int rc;
+
+ //plaintext is not decryped with rfc4543
+ areq_ctx->plaintext_authenticate_only = true;
+
+ /* No generated IV required */
+ areq_ctx->backup_iv = req->iv;
+ areq_ctx->backup_giv = NULL;
+
+ cc_proc_rfc4_gcm(req);
+ areq_ctx->is_gcm4543 = true;
+
+ rc = cc_proc_aead(req, DRV_CRYPTO_DIRECTION_DECRYPT);
+ if (rc != -EINPROGRESS && rc != -EBUSY)
+ req->iv = areq_ctx->backup_iv;
+
+ return rc;
+}
+
+/* aead alg */
+static struct cc_alg_template aead_algs[] = {
+ {
+ .name = "authenc(hmac(sha1),cbc(aes))",
+ .driver_name = "authenc-hmac-sha1-cbc-aes-ccree",
+ .blocksize = AES_BLOCK_SIZE,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = cc_aead_setkey,
+ .setauthsize = cc_aead_setauthsize,
+ .encrypt = cc_aead_encrypt,
+ .decrypt = cc_aead_decrypt,
+ .init = cc_aead_init,
+ .exit = cc_aead_exit,
+ .ivsize = AES_BLOCK_SIZE,
+ .maxauthsize = SHA1_DIGEST_SIZE,
+ },
+ .cipher_mode = DRV_CIPHER_CBC,
+ .flow_mode = S_DIN_to_AES,
+ .auth_mode = DRV_HASH_SHA1,
+ .min_hw_rev = CC_HW_REV_630,
+ },
+ {
+ .name = "authenc(hmac(sha1),cbc(des3_ede))",
+ .driver_name = "authenc-hmac-sha1-cbc-des3-ccree",
+ .blocksize = DES3_EDE_BLOCK_SIZE,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = cc_aead_setkey,
+ .setauthsize = cc_aead_setauthsize,
+ .encrypt = cc_aead_encrypt,
+ .decrypt = cc_aead_decrypt,
+ .init = cc_aead_init,
+ .exit = cc_aead_exit,
+ .ivsize = DES3_EDE_BLOCK_SIZE,
+ .maxauthsize = SHA1_DIGEST_SIZE,
+ },
+ .cipher_mode = DRV_CIPHER_CBC,
+ .flow_mode = S_DIN_to_DES,
+ .auth_mode = DRV_HASH_SHA1,
+ .min_hw_rev = CC_HW_REV_630,
+ },
+ {
+ .name = "authenc(hmac(sha256),cbc(aes))",
+ .driver_name = "authenc-hmac-sha256-cbc-aes-ccree",
+ .blocksize = AES_BLOCK_SIZE,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = cc_aead_setkey,
+ .setauthsize = cc_aead_setauthsize,
+ .encrypt = cc_aead_encrypt,
+ .decrypt = cc_aead_decrypt,
+ .init = cc_aead_init,
+ .exit = cc_aead_exit,
+ .ivsize = AES_BLOCK_SIZE,
+ .maxauthsize = SHA256_DIGEST_SIZE,
+ },
+ .cipher_mode = DRV_CIPHER_CBC,
+ .flow_mode = S_DIN_to_AES,
+ .auth_mode = DRV_HASH_SHA256,
+ .min_hw_rev = CC_HW_REV_630,
+ },
+ {
+ .name = "authenc(hmac(sha256),cbc(des3_ede))",
+ .driver_name = "authenc-hmac-sha256-cbc-des3-ccree",
+ .blocksize = DES3_EDE_BLOCK_SIZE,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = cc_aead_setkey,
+ .setauthsize = cc_aead_setauthsize,
+ .encrypt = cc_aead_encrypt,
+ .decrypt = cc_aead_decrypt,
+ .init = cc_aead_init,
+ .exit = cc_aead_exit,
+ .ivsize = DES3_EDE_BLOCK_SIZE,
+ .maxauthsize = SHA256_DIGEST_SIZE,
+ },
+ .cipher_mode = DRV_CIPHER_CBC,
+ .flow_mode = S_DIN_to_DES,
+ .auth_mode = DRV_HASH_SHA256,
+ .min_hw_rev = CC_HW_REV_630,
+ },
+ {
+ .name = "authenc(xcbc(aes),cbc(aes))",
+ .driver_name = "authenc-xcbc-aes-cbc-aes-ccree",
+ .blocksize = AES_BLOCK_SIZE,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = cc_aead_setkey,
+ .setauthsize = cc_aead_setauthsize,
+ .encrypt = cc_aead_encrypt,
+ .decrypt = cc_aead_decrypt,
+ .init = cc_aead_init,
+ .exit = cc_aead_exit,
+ .ivsize = AES_BLOCK_SIZE,
+ .maxauthsize = AES_BLOCK_SIZE,
+ },
+ .cipher_mode = DRV_CIPHER_CBC,
+ .flow_mode = S_DIN_to_AES,
+ .auth_mode = DRV_HASH_XCBC_MAC,
+ .min_hw_rev = CC_HW_REV_630,
+ },
+ {
+ .name = "authenc(hmac(sha1),rfc3686(ctr(aes)))",
+ .driver_name = "authenc-hmac-sha1-rfc3686-ctr-aes-ccree",
+ .blocksize = 1,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = cc_aead_setkey,
+ .setauthsize = cc_aead_setauthsize,
+ .encrypt = cc_aead_encrypt,
+ .decrypt = cc_aead_decrypt,
+ .init = cc_aead_init,
+ .exit = cc_aead_exit,
+ .ivsize = CTR_RFC3686_IV_SIZE,
+ .maxauthsize = SHA1_DIGEST_SIZE,
+ },
+ .cipher_mode = DRV_CIPHER_CTR,
+ .flow_mode = S_DIN_to_AES,
+ .auth_mode = DRV_HASH_SHA1,
+ .min_hw_rev = CC_HW_REV_630,
+ },
+ {
+ .name = "authenc(hmac(sha256),rfc3686(ctr(aes)))",
+ .driver_name = "authenc-hmac-sha256-rfc3686-ctr-aes-ccree",
+ .blocksize = 1,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = cc_aead_setkey,
+ .setauthsize = cc_aead_setauthsize,
+ .encrypt = cc_aead_encrypt,
+ .decrypt = cc_aead_decrypt,
+ .init = cc_aead_init,
+ .exit = cc_aead_exit,
+ .ivsize = CTR_RFC3686_IV_SIZE,
+ .maxauthsize = SHA256_DIGEST_SIZE,
+ },
+ .cipher_mode = DRV_CIPHER_CTR,
+ .flow_mode = S_DIN_to_AES,
+ .auth_mode = DRV_HASH_SHA256,
+ .min_hw_rev = CC_HW_REV_630,
+ },
+ {
+ .name = "authenc(xcbc(aes),rfc3686(ctr(aes)))",
+ .driver_name = "authenc-xcbc-aes-rfc3686-ctr-aes-ccree",
+ .blocksize = 1,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = cc_aead_setkey,
+ .setauthsize = cc_aead_setauthsize,
+ .encrypt = cc_aead_encrypt,
+ .decrypt = cc_aead_decrypt,
+ .init = cc_aead_init,
+ .exit = cc_aead_exit,
+ .ivsize = CTR_RFC3686_IV_SIZE,
+ .maxauthsize = AES_BLOCK_SIZE,
+ },
+ .cipher_mode = DRV_CIPHER_CTR,
+ .flow_mode = S_DIN_to_AES,
+ .auth_mode = DRV_HASH_XCBC_MAC,
+ .min_hw_rev = CC_HW_REV_630,
+ },
+ {
+ .name = "ccm(aes)",
+ .driver_name = "ccm-aes-ccree",
+ .blocksize = 1,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = cc_aead_setkey,
+ .setauthsize = cc_ccm_setauthsize,
+ .encrypt = cc_aead_encrypt,
+ .decrypt = cc_aead_decrypt,
+ .init = cc_aead_init,
+ .exit = cc_aead_exit,
+ .ivsize = AES_BLOCK_SIZE,
+ .maxauthsize = AES_BLOCK_SIZE,
+ },
+ .cipher_mode = DRV_CIPHER_CCM,
+ .flow_mode = S_DIN_to_AES,
+ .auth_mode = DRV_HASH_NULL,
+ .min_hw_rev = CC_HW_REV_630,
+ },
+ {
+ .name = "rfc4309(ccm(aes))",
+ .driver_name = "rfc4309-ccm-aes-ccree",
+ .blocksize = 1,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = cc_rfc4309_ccm_setkey,
+ .setauthsize = cc_rfc4309_ccm_setauthsize,
+ .encrypt = cc_rfc4309_ccm_encrypt,
+ .decrypt = cc_rfc4309_ccm_decrypt,
+ .init = cc_aead_init,
+ .exit = cc_aead_exit,
+ .ivsize = CCM_BLOCK_IV_SIZE,
+ .maxauthsize = AES_BLOCK_SIZE,
+ },
+ .cipher_mode = DRV_CIPHER_CCM,
+ .flow_mode = S_DIN_to_AES,
+ .auth_mode = DRV_HASH_NULL,
+ .min_hw_rev = CC_HW_REV_630,
+ },
+ {
+ .name = "gcm(aes)",
+ .driver_name = "gcm-aes-ccree",
+ .blocksize = 1,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = cc_aead_setkey,
+ .setauthsize = cc_gcm_setauthsize,
+ .encrypt = cc_aead_encrypt,
+ .decrypt = cc_aead_decrypt,
+ .init = cc_aead_init,
+ .exit = cc_aead_exit,
+ .ivsize = 12,
+ .maxauthsize = AES_BLOCK_SIZE,
+ },
+ .cipher_mode = DRV_CIPHER_GCTR,
+ .flow_mode = S_DIN_to_AES,
+ .auth_mode = DRV_HASH_NULL,
+ .min_hw_rev = CC_HW_REV_630,
+ },
+ {
+ .name = "rfc4106(gcm(aes))",
+ .driver_name = "rfc4106-gcm-aes-ccree",
+ .blocksize = 1,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = cc_rfc4106_gcm_setkey,
+ .setauthsize = cc_rfc4106_gcm_setauthsize,
+ .encrypt = cc_rfc4106_gcm_encrypt,
+ .decrypt = cc_rfc4106_gcm_decrypt,
+ .init = cc_aead_init,
+ .exit = cc_aead_exit,
+ .ivsize = GCM_BLOCK_RFC4_IV_SIZE,
+ .maxauthsize = AES_BLOCK_SIZE,
+ },
+ .cipher_mode = DRV_CIPHER_GCTR,
+ .flow_mode = S_DIN_to_AES,
+ .auth_mode = DRV_HASH_NULL,
+ .min_hw_rev = CC_HW_REV_630,
+ },
+ {
+ .name = "rfc4543(gcm(aes))",
+ .driver_name = "rfc4543-gcm-aes-ccree",
+ .blocksize = 1,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = cc_rfc4543_gcm_setkey,
+ .setauthsize = cc_rfc4543_gcm_setauthsize,
+ .encrypt = cc_rfc4543_gcm_encrypt,
+ .decrypt = cc_rfc4543_gcm_decrypt,
+ .init = cc_aead_init,
+ .exit = cc_aead_exit,
+ .ivsize = GCM_BLOCK_RFC4_IV_SIZE,
+ .maxauthsize = AES_BLOCK_SIZE,
+ },
+ .cipher_mode = DRV_CIPHER_GCTR,
+ .flow_mode = S_DIN_to_AES,
+ .auth_mode = DRV_HASH_NULL,
+ .min_hw_rev = CC_HW_REV_630,
+ },
+};
+
+static struct cc_crypto_alg *cc_create_aead_alg(struct cc_alg_template *tmpl,
+ struct device *dev)
+{
+ struct cc_crypto_alg *t_alg;
+ struct aead_alg *alg;
+
+ t_alg = kzalloc(sizeof(*t_alg), GFP_KERNEL);
+ if (!t_alg)
+ return ERR_PTR(-ENOMEM);
+
+ alg = &tmpl->template_aead;
+
+ snprintf(alg->base.cra_name, CRYPTO_MAX_ALG_NAME, "%s", tmpl->name);
+ snprintf(alg->base.cra_driver_name, CRYPTO_MAX_ALG_NAME, "%s",
+ tmpl->driver_name);
+ alg->base.cra_module = THIS_MODULE;
+ alg->base.cra_priority = CC_CRA_PRIO;
+
+ alg->base.cra_ctxsize = sizeof(struct cc_aead_ctx);
+ alg->base.cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_KERN_DRIVER_ONLY |
+ tmpl->type;
+ alg->init = cc_aead_init;
+ alg->exit = cc_aead_exit;
+
+ t_alg->aead_alg = *alg;
+
+ t_alg->cipher_mode = tmpl->cipher_mode;
+ t_alg->flow_mode = tmpl->flow_mode;
+ t_alg->auth_mode = tmpl->auth_mode;
+
+ return t_alg;
+}
+
+int cc_aead_free(struct cc_drvdata *drvdata)
+{
+ struct cc_crypto_alg *t_alg, *n;
+ struct cc_aead_handle *aead_handle =
+ (struct cc_aead_handle *)drvdata->aead_handle;
+
+ if (aead_handle) {
+ /* Remove registered algs */
+ list_for_each_entry_safe(t_alg, n, &aead_handle->aead_list,
+ entry) {
+ crypto_unregister_aead(&t_alg->aead_alg);
+ list_del(&t_alg->entry);
+ kfree(t_alg);
+ }
+ kfree(aead_handle);
+ drvdata->aead_handle = NULL;
+ }
+
+ return 0;
+}
+
+int cc_aead_alloc(struct cc_drvdata *drvdata)
+{
+ struct cc_aead_handle *aead_handle;
+ struct cc_crypto_alg *t_alg;
+ int rc = -ENOMEM;
+ int alg;
+ struct device *dev = drvdata_to_dev(drvdata);
+
+ aead_handle = kmalloc(sizeof(*aead_handle), GFP_KERNEL);
+ if (!aead_handle) {
+ rc = -ENOMEM;
+ goto fail0;
+ }
+
+ INIT_LIST_HEAD(&aead_handle->aead_list);
+ drvdata->aead_handle = aead_handle;
+
+ aead_handle->sram_workspace_addr = cc_sram_alloc(drvdata,
+ MAX_HMAC_DIGEST_SIZE);
+
+ if (aead_handle->sram_workspace_addr == NULL_SRAM_ADDR) {
+ dev_err(dev, "SRAM pool exhausted\n");
+ rc = -ENOMEM;
+ goto fail1;
+ }
+
+ /* Linux crypto */
+ for (alg = 0; alg < ARRAY_SIZE(aead_algs); alg++) {
+ if (aead_algs[alg].min_hw_rev > drvdata->hw_rev)
+ continue;
+
+ t_alg = cc_create_aead_alg(&aead_algs[alg], dev);
+ if (IS_ERR(t_alg)) {
+ rc = PTR_ERR(t_alg);
+ dev_err(dev, "%s alg allocation failed\n",
+ aead_algs[alg].driver_name);
+ goto fail1;
+ }
+ t_alg->drvdata = drvdata;
+ rc = crypto_register_aead(&t_alg->aead_alg);
+ if (rc) {
+ dev_err(dev, "%s alg registration failed\n",
+ t_alg->aead_alg.base.cra_driver_name);
+ goto fail2;
+ } else {
+ list_add_tail(&t_alg->entry, &aead_handle->aead_list);
+ dev_dbg(dev, "Registered %s\n",
+ t_alg->aead_alg.base.cra_driver_name);
+ }
+ }
+
+ return 0;
+
+fail2:
+ kfree(t_alg);
+fail1:
+ cc_aead_free(drvdata);
+fail0:
+ return rc;
+}
diff --git a/drivers/staging/ccree/cc_aead.h b/drivers/crypto/ccree/cc_aead.h
index 5edf3b351fa4..5edf3b351fa4 100644
--- a/drivers/staging/ccree/cc_aead.h
+++ b/drivers/crypto/ccree/cc_aead.h
diff --git a/drivers/crypto/ccree/cc_buffer_mgr.c b/drivers/crypto/ccree/cc_buffer_mgr.c
new file mode 100644
index 000000000000..b32577477b4c
--- /dev/null
+++ b/drivers/crypto/ccree/cc_buffer_mgr.c
@@ -0,0 +1,1651 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
+
+#include <crypto/internal/aead.h>
+#include <crypto/authenc.h>
+#include <crypto/scatterwalk.h>
+#include <linux/dmapool.h>
+#include <linux/dma-mapping.h>
+
+#include "cc_buffer_mgr.h"
+#include "cc_lli_defs.h"
+#include "cc_cipher.h"
+#include "cc_hash.h"
+#include "cc_aead.h"
+
+enum dma_buffer_type {
+ DMA_NULL_TYPE = -1,
+ DMA_SGL_TYPE = 1,
+ DMA_BUFF_TYPE = 2,
+};
+
+struct buff_mgr_handle {
+ struct dma_pool *mlli_buffs_pool;
+};
+
+union buffer_array_entry {
+ struct scatterlist *sgl;
+ dma_addr_t buffer_dma;
+};
+
+struct buffer_array {
+ unsigned int num_of_buffers;
+ union buffer_array_entry entry[MAX_NUM_OF_BUFFERS_IN_MLLI];
+ unsigned int offset[MAX_NUM_OF_BUFFERS_IN_MLLI];
+ int nents[MAX_NUM_OF_BUFFERS_IN_MLLI];
+ int total_data_len[MAX_NUM_OF_BUFFERS_IN_MLLI];
+ enum dma_buffer_type type[MAX_NUM_OF_BUFFERS_IN_MLLI];
+ bool is_last[MAX_NUM_OF_BUFFERS_IN_MLLI];
+ u32 *mlli_nents[MAX_NUM_OF_BUFFERS_IN_MLLI];
+};
+
+static inline char *cc_dma_buf_type(enum cc_req_dma_buf_type type)
+{
+ switch (type) {
+ case CC_DMA_BUF_NULL:
+ return "BUF_NULL";
+ case CC_DMA_BUF_DLLI:
+ return "BUF_DLLI";
+ case CC_DMA_BUF_MLLI:
+ return "BUF_MLLI";
+ default:
+ return "BUF_INVALID";
+ }
+}
+
+/**
+ * cc_copy_mac() - Copy MAC to temporary location
+ *
+ * @dev: device object
+ * @req: aead request object
+ * @dir: [IN] copy from/to sgl
+ */
+static void cc_copy_mac(struct device *dev, struct aead_request *req,
+ enum cc_sg_cpy_direct dir)
+{
+ struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ u32 skip = req->assoclen + req->cryptlen;
+
+ if (areq_ctx->is_gcm4543)
+ skip += crypto_aead_ivsize(tfm);
+
+ cc_copy_sg_portion(dev, areq_ctx->backup_mac, req->src,
+ (skip - areq_ctx->req_authsize), skip, dir);
+}
+
+/**
+ * cc_get_sgl_nents() - Get scatterlist number of entries.
+ *
+ * @sg_list: SG list
+ * @nbytes: [IN] Total SGL data bytes.
+ * @lbytes: [OUT] Returns the amount of bytes at the last entry
+ */
+static unsigned int cc_get_sgl_nents(struct device *dev,
+ struct scatterlist *sg_list,
+ unsigned int nbytes, u32 *lbytes,
+ bool *is_chained)
+{
+ unsigned int nents = 0;
+
+ while (nbytes && sg_list) {
+ if (sg_list->length) {
+ nents++;
+ /* get the number of bytes in the last entry */
+ *lbytes = nbytes;
+ nbytes -= (sg_list->length > nbytes) ?
+ nbytes : sg_list->length;
+ sg_list = sg_next(sg_list);
+ } else {
+ sg_list = (struct scatterlist *)sg_page(sg_list);
+ if (is_chained)
+ *is_chained = true;
+ }
+ }
+ dev_dbg(dev, "nents %d last bytes %d\n", nents, *lbytes);
+ return nents;
+}
+
+/**
+ * cc_zero_sgl() - Zero scatter scatter list data.
+ *
+ * @sgl:
+ */
+void cc_zero_sgl(struct scatterlist *sgl, u32 data_len)
+{
+ struct scatterlist *current_sg = sgl;
+ int sg_index = 0;
+
+ while (sg_index <= data_len) {
+ if (!current_sg) {
+ /* reached the end of the sgl --> just return back */
+ return;
+ }
+ memset(sg_virt(current_sg), 0, current_sg->length);
+ sg_index += current_sg->length;
+ current_sg = sg_next(current_sg);
+ }
+}
+
+/**
+ * cc_copy_sg_portion() - Copy scatter list data,
+ * from to_skip to end, to dest and vice versa
+ *
+ * @dest:
+ * @sg:
+ * @to_skip:
+ * @end:
+ * @direct:
+ */
+void cc_copy_sg_portion(struct device *dev, u8 *dest, struct scatterlist *sg,
+ u32 to_skip, u32 end, enum cc_sg_cpy_direct direct)
+{
+ u32 nents, lbytes;
+
+ nents = cc_get_sgl_nents(dev, sg, end, &lbytes, NULL);
+ sg_copy_buffer(sg, nents, (void *)dest, (end - to_skip + 1), to_skip,
+ (direct == CC_SG_TO_BUF));
+}
+
+static int cc_render_buff_to_mlli(struct device *dev, dma_addr_t buff_dma,
+ u32 buff_size, u32 *curr_nents,
+ u32 **mlli_entry_pp)
+{
+ u32 *mlli_entry_p = *mlli_entry_pp;
+ u32 new_nents;
+
+ /* Verify there is no memory overflow*/
+ new_nents = (*curr_nents + buff_size / CC_MAX_MLLI_ENTRY_SIZE + 1);
+ if (new_nents > MAX_NUM_OF_TOTAL_MLLI_ENTRIES)
+ return -ENOMEM;
+
+ /*handle buffer longer than 64 kbytes */
+ while (buff_size > CC_MAX_MLLI_ENTRY_SIZE) {
+ cc_lli_set_addr(mlli_entry_p, buff_dma);
+ cc_lli_set_size(mlli_entry_p, CC_MAX_MLLI_ENTRY_SIZE);
+ dev_dbg(dev, "entry[%d]: single_buff=0x%08X size=%08X\n",
+ *curr_nents, mlli_entry_p[LLI_WORD0_OFFSET],
+ mlli_entry_p[LLI_WORD1_OFFSET]);
+ buff_dma += CC_MAX_MLLI_ENTRY_SIZE;
+ buff_size -= CC_MAX_MLLI_ENTRY_SIZE;
+ mlli_entry_p = mlli_entry_p + 2;
+ (*curr_nents)++;
+ }
+ /*Last entry */
+ cc_lli_set_addr(mlli_entry_p, buff_dma);
+ cc_lli_set_size(mlli_entry_p, buff_size);
+ dev_dbg(dev, "entry[%d]: single_buff=0x%08X size=%08X\n",
+ *curr_nents, mlli_entry_p[LLI_WORD0_OFFSET],
+ mlli_entry_p[LLI_WORD1_OFFSET]);
+ mlli_entry_p = mlli_entry_p + 2;
+ *mlli_entry_pp = mlli_entry_p;
+ (*curr_nents)++;
+ return 0;
+}
+
+static int cc_render_sg_to_mlli(struct device *dev, struct scatterlist *sgl,
+ u32 sgl_data_len, u32 sgl_offset,
+ u32 *curr_nents, u32 **mlli_entry_pp)
+{
+ struct scatterlist *curr_sgl = sgl;
+ u32 *mlli_entry_p = *mlli_entry_pp;
+ s32 rc = 0;
+
+ for ( ; (curr_sgl && sgl_data_len);
+ curr_sgl = sg_next(curr_sgl)) {
+ u32 entry_data_len =
+ (sgl_data_len > sg_dma_len(curr_sgl) - sgl_offset) ?
+ sg_dma_len(curr_sgl) - sgl_offset :
+ sgl_data_len;
+ sgl_data_len -= entry_data_len;
+ rc = cc_render_buff_to_mlli(dev, sg_dma_address(curr_sgl) +
+ sgl_offset, entry_data_len,
+ curr_nents, &mlli_entry_p);
+ if (rc)
+ return rc;
+
+ sgl_offset = 0;
+ }
+ *mlli_entry_pp = mlli_entry_p;
+ return 0;
+}
+
+static int cc_generate_mlli(struct device *dev, struct buffer_array *sg_data,
+ struct mlli_params *mlli_params, gfp_t flags)
+{
+ u32 *mlli_p;
+ u32 total_nents = 0, prev_total_nents = 0;
+ int rc = 0, i;
+
+ dev_dbg(dev, "NUM of SG's = %d\n", sg_data->num_of_buffers);
+
+ /* Allocate memory from the pointed pool */
+ mlli_params->mlli_virt_addr =
+ dma_pool_alloc(mlli_params->curr_pool, flags,
+ &mlli_params->mlli_dma_addr);
+ if (!mlli_params->mlli_virt_addr) {
+ dev_err(dev, "dma_pool_alloc() failed\n");
+ rc = -ENOMEM;
+ goto build_mlli_exit;
+ }
+ /* Point to start of MLLI */
+ mlli_p = (u32 *)mlli_params->mlli_virt_addr;
+ /* go over all SG's and link it to one MLLI table */
+ for (i = 0; i < sg_data->num_of_buffers; i++) {
+ union buffer_array_entry *entry = &sg_data->entry[i];
+ u32 tot_len = sg_data->total_data_len[i];
+ u32 offset = sg_data->offset[i];
+
+ if (sg_data->type[i] == DMA_SGL_TYPE)
+ rc = cc_render_sg_to_mlli(dev, entry->sgl, tot_len,
+ offset, &total_nents,
+ &mlli_p);
+ else /*DMA_BUFF_TYPE*/
+ rc = cc_render_buff_to_mlli(dev, entry->buffer_dma,
+ tot_len, &total_nents,
+ &mlli_p);
+ if (rc)
+ return rc;
+
+ /* set last bit in the current table */
+ if (sg_data->mlli_nents[i]) {
+ /*Calculate the current MLLI table length for the
+ *length field in the descriptor
+ */
+ *sg_data->mlli_nents[i] +=
+ (total_nents - prev_total_nents);
+ prev_total_nents = total_nents;
+ }
+ }
+
+ /* Set MLLI size for the bypass operation */
+ mlli_params->mlli_len = (total_nents * LLI_ENTRY_BYTE_SIZE);
+
+ dev_dbg(dev, "MLLI params: virt_addr=%pK dma_addr=%pad mlli_len=0x%X\n",
+ mlli_params->mlli_virt_addr, &mlli_params->mlli_dma_addr,
+ mlli_params->mlli_len);
+
+build_mlli_exit:
+ return rc;
+}
+
+static void cc_add_buffer_entry(struct device *dev,
+ struct buffer_array *sgl_data,
+ dma_addr_t buffer_dma, unsigned int buffer_len,
+ bool is_last_entry, u32 *mlli_nents)
+{
+ unsigned int index = sgl_data->num_of_buffers;
+
+ dev_dbg(dev, "index=%u single_buff=%pad buffer_len=0x%08X is_last=%d\n",
+ index, &buffer_dma, buffer_len, is_last_entry);
+ sgl_data->nents[index] = 1;
+ sgl_data->entry[index].buffer_dma = buffer_dma;
+ sgl_data->offset[index] = 0;
+ sgl_data->total_data_len[index] = buffer_len;
+ sgl_data->type[index] = DMA_BUFF_TYPE;
+ sgl_data->is_last[index] = is_last_entry;
+ sgl_data->mlli_nents[index] = mlli_nents;
+ if (sgl_data->mlli_nents[index])
+ *sgl_data->mlli_nents[index] = 0;
+ sgl_data->num_of_buffers++;
+}
+
+static void cc_add_sg_entry(struct device *dev, struct buffer_array *sgl_data,
+ unsigned int nents, struct scatterlist *sgl,
+ unsigned int data_len, unsigned int data_offset,
+ bool is_last_table, u32 *mlli_nents)
+{
+ unsigned int index = sgl_data->num_of_buffers;
+
+ dev_dbg(dev, "index=%u nents=%u sgl=%pK data_len=0x%08X is_last=%d\n",
+ index, nents, sgl, data_len, is_last_table);
+ sgl_data->nents[index] = nents;
+ sgl_data->entry[index].sgl = sgl;
+ sgl_data->offset[index] = data_offset;
+ sgl_data->total_data_len[index] = data_len;
+ sgl_data->type[index] = DMA_SGL_TYPE;
+ sgl_data->is_last[index] = is_last_table;
+ sgl_data->mlli_nents[index] = mlli_nents;
+ if (sgl_data->mlli_nents[index])
+ *sgl_data->mlli_nents[index] = 0;
+ sgl_data->num_of_buffers++;
+}
+
+static int cc_dma_map_sg(struct device *dev, struct scatterlist *sg, u32 nents,
+ enum dma_data_direction direction)
+{
+ u32 i, j;
+ struct scatterlist *l_sg = sg;
+
+ for (i = 0; i < nents; i++) {
+ if (!l_sg)
+ break;
+ if (dma_map_sg(dev, l_sg, 1, direction) != 1) {
+ dev_err(dev, "dma_map_page() sg buffer failed\n");
+ goto err;
+ }
+ l_sg = sg_next(l_sg);
+ }
+ return nents;
+
+err:
+ /* Restore mapped parts */
+ for (j = 0; j < i; j++) {
+ if (!sg)
+ break;
+ dma_unmap_sg(dev, sg, 1, direction);
+ sg = sg_next(sg);
+ }
+ return 0;
+}
+
+static int cc_map_sg(struct device *dev, struct scatterlist *sg,
+ unsigned int nbytes, int direction, u32 *nents,
+ u32 max_sg_nents, u32 *lbytes, u32 *mapped_nents)
+{
+ bool is_chained = false;
+
+ if (sg_is_last(sg)) {
+ /* One entry only case -set to DLLI */
+ if (dma_map_sg(dev, sg, 1, direction) != 1) {
+ dev_err(dev, "dma_map_sg() single buffer failed\n");
+ return -ENOMEM;
+ }
+ dev_dbg(dev, "Mapped sg: dma_address=%pad page=%p addr=%pK offset=%u length=%u\n",
+ &sg_dma_address(sg), sg_page(sg), sg_virt(sg),
+ sg->offset, sg->length);
+ *lbytes = nbytes;
+ *nents = 1;
+ *mapped_nents = 1;
+ } else { /*sg_is_last*/
+ *nents = cc_get_sgl_nents(dev, sg, nbytes, lbytes,
+ &is_chained);
+ if (*nents > max_sg_nents) {
+ *nents = 0;
+ dev_err(dev, "Too many fragments. current %d max %d\n",
+ *nents, max_sg_nents);
+ return -ENOMEM;
+ }
+ if (!is_chained) {
+ /* In case of mmu the number of mapped nents might
+ * be changed from the original sgl nents
+ */
+ *mapped_nents = dma_map_sg(dev, sg, *nents, direction);
+ if (*mapped_nents == 0) {
+ *nents = 0;
+ dev_err(dev, "dma_map_sg() sg buffer failed\n");
+ return -ENOMEM;
+ }
+ } else {
+ /*In this case the driver maps entry by entry so it
+ * must have the same nents before and after map
+ */
+ *mapped_nents = cc_dma_map_sg(dev, sg, *nents,
+ direction);
+ if (*mapped_nents != *nents) {
+ *nents = *mapped_nents;
+ dev_err(dev, "dma_map_sg() sg buffer failed\n");
+ return -ENOMEM;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int
+cc_set_aead_conf_buf(struct device *dev, struct aead_req_ctx *areq_ctx,
+ u8 *config_data, struct buffer_array *sg_data,
+ unsigned int assoclen)
+{
+ dev_dbg(dev, " handle additional data config set to DLLI\n");
+ /* create sg for the current buffer */
+ sg_init_one(&areq_ctx->ccm_adata_sg, config_data,
+ AES_BLOCK_SIZE + areq_ctx->ccm_hdr_size);
+ if (dma_map_sg(dev, &areq_ctx->ccm_adata_sg, 1, DMA_TO_DEVICE) != 1) {
+ dev_err(dev, "dma_map_sg() config buffer failed\n");
+ return -ENOMEM;
+ }
+ dev_dbg(dev, "Mapped curr_buff: dma_address=%pad page=%p addr=%pK offset=%u length=%u\n",
+ &sg_dma_address(&areq_ctx->ccm_adata_sg),
+ sg_page(&areq_ctx->ccm_adata_sg),
+ sg_virt(&areq_ctx->ccm_adata_sg),
+ areq_ctx->ccm_adata_sg.offset, areq_ctx->ccm_adata_sg.length);
+ /* prepare for case of MLLI */
+ if (assoclen > 0) {
+ cc_add_sg_entry(dev, sg_data, 1, &areq_ctx->ccm_adata_sg,
+ (AES_BLOCK_SIZE + areq_ctx->ccm_hdr_size),
+ 0, false, NULL);
+ }
+ return 0;
+}
+
+static int cc_set_hash_buf(struct device *dev, struct ahash_req_ctx *areq_ctx,
+ u8 *curr_buff, u32 curr_buff_cnt,
+ struct buffer_array *sg_data)
+{
+ dev_dbg(dev, " handle curr buff %x set to DLLI\n", curr_buff_cnt);
+ /* create sg for the current buffer */
+ sg_init_one(areq_ctx->buff_sg, curr_buff, curr_buff_cnt);
+ if (dma_map_sg(dev, areq_ctx->buff_sg, 1, DMA_TO_DEVICE) != 1) {
+ dev_err(dev, "dma_map_sg() src buffer failed\n");
+ return -ENOMEM;
+ }
+ dev_dbg(dev, "Mapped curr_buff: dma_address=%pad page=%p addr=%pK offset=%u length=%u\n",
+ &sg_dma_address(areq_ctx->buff_sg), sg_page(areq_ctx->buff_sg),
+ sg_virt(areq_ctx->buff_sg), areq_ctx->buff_sg->offset,
+ areq_ctx->buff_sg->length);
+ areq_ctx->data_dma_buf_type = CC_DMA_BUF_DLLI;
+ areq_ctx->curr_sg = areq_ctx->buff_sg;
+ areq_ctx->in_nents = 0;
+ /* prepare for case of MLLI */
+ cc_add_sg_entry(dev, sg_data, 1, areq_ctx->buff_sg, curr_buff_cnt, 0,
+ false, NULL);
+ return 0;
+}
+
+void cc_unmap_cipher_request(struct device *dev, void *ctx,
+ unsigned int ivsize, struct scatterlist *src,
+ struct scatterlist *dst)
+{
+ struct cipher_req_ctx *req_ctx = (struct cipher_req_ctx *)ctx;
+
+ if (req_ctx->gen_ctx.iv_dma_addr) {
+ dev_dbg(dev, "Unmapped iv: iv_dma_addr=%pad iv_size=%u\n",
+ &req_ctx->gen_ctx.iv_dma_addr, ivsize);
+ dma_unmap_single(dev, req_ctx->gen_ctx.iv_dma_addr,
+ ivsize,
+ req_ctx->is_giv ? DMA_BIDIRECTIONAL :
+ DMA_TO_DEVICE);
+ }
+ /* Release pool */
+ if (req_ctx->dma_buf_type == CC_DMA_BUF_MLLI &&
+ req_ctx->mlli_params.mlli_virt_addr) {
+ dma_pool_free(req_ctx->mlli_params.curr_pool,
+ req_ctx->mlli_params.mlli_virt_addr,
+ req_ctx->mlli_params.mlli_dma_addr);
+ }
+
+ dma_unmap_sg(dev, src, req_ctx->in_nents, DMA_BIDIRECTIONAL);
+ dev_dbg(dev, "Unmapped req->src=%pK\n", sg_virt(src));
+
+ if (src != dst) {
+ dma_unmap_sg(dev, dst, req_ctx->out_nents, DMA_BIDIRECTIONAL);
+ dev_dbg(dev, "Unmapped req->dst=%pK\n", sg_virt(dst));
+ }
+}
+
+int cc_map_cipher_request(struct cc_drvdata *drvdata, void *ctx,
+ unsigned int ivsize, unsigned int nbytes,
+ void *info, struct scatterlist *src,
+ struct scatterlist *dst, gfp_t flags)
+{
+ struct cipher_req_ctx *req_ctx = (struct cipher_req_ctx *)ctx;
+ struct mlli_params *mlli_params = &req_ctx->mlli_params;
+ struct buff_mgr_handle *buff_mgr = drvdata->buff_mgr_handle;
+ struct device *dev = drvdata_to_dev(drvdata);
+ struct buffer_array sg_data;
+ u32 dummy = 0;
+ int rc = 0;
+ u32 mapped_nents = 0;
+
+ req_ctx->dma_buf_type = CC_DMA_BUF_DLLI;
+ mlli_params->curr_pool = NULL;
+ sg_data.num_of_buffers = 0;
+
+ /* Map IV buffer */
+ if (ivsize) {
+ dump_byte_array("iv", (u8 *)info, ivsize);
+ req_ctx->gen_ctx.iv_dma_addr =
+ dma_map_single(dev, (void *)info,
+ ivsize,
+ req_ctx->is_giv ? DMA_BIDIRECTIONAL :
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, req_ctx->gen_ctx.iv_dma_addr)) {
+ dev_err(dev, "Mapping iv %u B at va=%pK for DMA failed\n",
+ ivsize, info);
+ return -ENOMEM;
+ }
+ dev_dbg(dev, "Mapped iv %u B at va=%pK to dma=%pad\n",
+ ivsize, info, &req_ctx->gen_ctx.iv_dma_addr);
+ } else {
+ req_ctx->gen_ctx.iv_dma_addr = 0;
+ }
+
+ /* Map the src SGL */
+ rc = cc_map_sg(dev, src, nbytes, DMA_BIDIRECTIONAL, &req_ctx->in_nents,
+ LLI_MAX_NUM_OF_DATA_ENTRIES, &dummy, &mapped_nents);
+ if (rc) {
+ rc = -ENOMEM;
+ goto cipher_exit;
+ }
+ if (mapped_nents > 1)
+ req_ctx->dma_buf_type = CC_DMA_BUF_MLLI;
+
+ if (src == dst) {
+ /* Handle inplace operation */
+ if (req_ctx->dma_buf_type == CC_DMA_BUF_MLLI) {
+ req_ctx->out_nents = 0;
+ cc_add_sg_entry(dev, &sg_data, req_ctx->in_nents, src,
+ nbytes, 0, true,
+ &req_ctx->in_mlli_nents);
+ }
+ } else {
+ /* Map the dst sg */
+ if (cc_map_sg(dev, dst, nbytes, DMA_BIDIRECTIONAL,
+ &req_ctx->out_nents, LLI_MAX_NUM_OF_DATA_ENTRIES,
+ &dummy, &mapped_nents)) {
+ rc = -ENOMEM;
+ goto cipher_exit;
+ }
+ if (mapped_nents > 1)
+ req_ctx->dma_buf_type = CC_DMA_BUF_MLLI;
+
+ if (req_ctx->dma_buf_type == CC_DMA_BUF_MLLI) {
+ cc_add_sg_entry(dev, &sg_data, req_ctx->in_nents, src,
+ nbytes, 0, true,
+ &req_ctx->in_mlli_nents);
+ cc_add_sg_entry(dev, &sg_data, req_ctx->out_nents, dst,
+ nbytes, 0, true,
+ &req_ctx->out_mlli_nents);
+ }
+ }
+
+ if (req_ctx->dma_buf_type == CC_DMA_BUF_MLLI) {
+ mlli_params->curr_pool = buff_mgr->mlli_buffs_pool;
+ rc = cc_generate_mlli(dev, &sg_data, mlli_params, flags);
+ if (rc)
+ goto cipher_exit;
+ }
+
+ dev_dbg(dev, "areq_ctx->dma_buf_type = %s\n",
+ cc_dma_buf_type(req_ctx->dma_buf_type));
+
+ return 0;
+
+cipher_exit:
+ cc_unmap_cipher_request(dev, req_ctx, ivsize, src, dst);
+ return rc;
+}
+
+void cc_unmap_aead_request(struct device *dev, struct aead_request *req)
+{
+ struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
+ unsigned int hw_iv_size = areq_ctx->hw_iv_size;
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ struct cc_drvdata *drvdata = dev_get_drvdata(dev);
+ u32 dummy;
+ bool chained;
+ u32 size_to_unmap = 0;
+
+ if (areq_ctx->mac_buf_dma_addr) {
+ dma_unmap_single(dev, areq_ctx->mac_buf_dma_addr,
+ MAX_MAC_SIZE, DMA_BIDIRECTIONAL);
+ }
+
+ if (areq_ctx->cipher_mode == DRV_CIPHER_GCTR) {
+ if (areq_ctx->hkey_dma_addr) {
+ dma_unmap_single(dev, areq_ctx->hkey_dma_addr,
+ AES_BLOCK_SIZE, DMA_BIDIRECTIONAL);
+ }
+
+ if (areq_ctx->gcm_block_len_dma_addr) {
+ dma_unmap_single(dev, areq_ctx->gcm_block_len_dma_addr,
+ AES_BLOCK_SIZE, DMA_TO_DEVICE);
+ }
+
+ if (areq_ctx->gcm_iv_inc1_dma_addr) {
+ dma_unmap_single(dev, areq_ctx->gcm_iv_inc1_dma_addr,
+ AES_BLOCK_SIZE, DMA_TO_DEVICE);
+ }
+
+ if (areq_ctx->gcm_iv_inc2_dma_addr) {
+ dma_unmap_single(dev, areq_ctx->gcm_iv_inc2_dma_addr,
+ AES_BLOCK_SIZE, DMA_TO_DEVICE);
+ }
+ }
+
+ if (areq_ctx->ccm_hdr_size != ccm_header_size_null) {
+ if (areq_ctx->ccm_iv0_dma_addr) {
+ dma_unmap_single(dev, areq_ctx->ccm_iv0_dma_addr,
+ AES_BLOCK_SIZE, DMA_TO_DEVICE);
+ }
+
+ dma_unmap_sg(dev, &areq_ctx->ccm_adata_sg, 1, DMA_TO_DEVICE);
+ }
+ if (areq_ctx->gen_ctx.iv_dma_addr) {
+ dma_unmap_single(dev, areq_ctx->gen_ctx.iv_dma_addr,
+ hw_iv_size, DMA_BIDIRECTIONAL);
+ }
+
+ /*In case a pool was set, a table was
+ *allocated and should be released
+ */
+ if (areq_ctx->mlli_params.curr_pool) {
+ dev_dbg(dev, "free MLLI buffer: dma=%pad virt=%pK\n",
+ &areq_ctx->mlli_params.mlli_dma_addr,
+ areq_ctx->mlli_params.mlli_virt_addr);
+ dma_pool_free(areq_ctx->mlli_params.curr_pool,
+ areq_ctx->mlli_params.mlli_virt_addr,
+ areq_ctx->mlli_params.mlli_dma_addr);
+ }
+
+ dev_dbg(dev, "Unmapping src sgl: req->src=%pK areq_ctx->src.nents=%u areq_ctx->assoc.nents=%u assoclen:%u cryptlen=%u\n",
+ sg_virt(req->src), areq_ctx->src.nents, areq_ctx->assoc.nents,
+ req->assoclen, req->cryptlen);
+ size_to_unmap = req->assoclen + req->cryptlen;
+ if (areq_ctx->gen_ctx.op_type == DRV_CRYPTO_DIRECTION_ENCRYPT)
+ size_to_unmap += areq_ctx->req_authsize;
+ if (areq_ctx->is_gcm4543)
+ size_to_unmap += crypto_aead_ivsize(tfm);
+
+ dma_unmap_sg(dev, req->src,
+ cc_get_sgl_nents(dev, req->src, size_to_unmap,
+ &dummy, &chained),
+ DMA_BIDIRECTIONAL);
+ if (req->src != req->dst) {
+ dev_dbg(dev, "Unmapping dst sgl: req->dst=%pK\n",
+ sg_virt(req->dst));
+ dma_unmap_sg(dev, req->dst,
+ cc_get_sgl_nents(dev, req->dst, size_to_unmap,
+ &dummy, &chained),
+ DMA_BIDIRECTIONAL);
+ }
+ if (drvdata->coherent &&
+ areq_ctx->gen_ctx.op_type == DRV_CRYPTO_DIRECTION_DECRYPT &&
+ req->src == req->dst) {
+ /* copy back mac from temporary location to deal with possible
+ * data memory overriding that caused by cache coherence
+ * problem.
+ */
+ cc_copy_mac(dev, req, CC_SG_FROM_BUF);
+ }
+}
+
+static int cc_get_aead_icv_nents(struct device *dev, struct scatterlist *sgl,
+ unsigned int sgl_nents, unsigned int authsize,
+ u32 last_entry_data_size,
+ bool *is_icv_fragmented)
+{
+ unsigned int icv_max_size = 0;
+ unsigned int icv_required_size = authsize > last_entry_data_size ?
+ (authsize - last_entry_data_size) :
+ authsize;
+ unsigned int nents;
+ unsigned int i;
+
+ if (sgl_nents < MAX_ICV_NENTS_SUPPORTED) {
+ *is_icv_fragmented = false;
+ return 0;
+ }
+
+ for (i = 0 ; i < (sgl_nents - MAX_ICV_NENTS_SUPPORTED) ; i++) {
+ if (!sgl)
+ break;
+ sgl = sg_next(sgl);
+ }
+
+ if (sgl)
+ icv_max_size = sgl->length;
+
+ if (last_entry_data_size > authsize) {
+ /* ICV attached to data in last entry (not fragmented!) */
+ nents = 0;
+ *is_icv_fragmented = false;
+ } else if (last_entry_data_size == authsize) {
+ /* ICV placed in whole last entry (not fragmented!) */
+ nents = 1;
+ *is_icv_fragmented = false;
+ } else if (icv_max_size > icv_required_size) {
+ nents = 1;
+ *is_icv_fragmented = true;
+ } else if (icv_max_size == icv_required_size) {
+ nents = 2;
+ *is_icv_fragmented = true;
+ } else {
+ dev_err(dev, "Unsupported num. of ICV fragments (> %d)\n",
+ MAX_ICV_NENTS_SUPPORTED);
+ nents = -1; /*unsupported*/
+ }
+ dev_dbg(dev, "is_frag=%s icv_nents=%u\n",
+ (*is_icv_fragmented ? "true" : "false"), nents);
+
+ return nents;
+}
+
+static int cc_aead_chain_iv(struct cc_drvdata *drvdata,
+ struct aead_request *req,
+ struct buffer_array *sg_data,
+ bool is_last, bool do_chain)
+{
+ struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
+ unsigned int hw_iv_size = areq_ctx->hw_iv_size;
+ struct device *dev = drvdata_to_dev(drvdata);
+ int rc = 0;
+
+ if (!req->iv) {
+ areq_ctx->gen_ctx.iv_dma_addr = 0;
+ goto chain_iv_exit;
+ }
+
+ areq_ctx->gen_ctx.iv_dma_addr = dma_map_single(dev, req->iv,
+ hw_iv_size,
+ DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(dev, areq_ctx->gen_ctx.iv_dma_addr)) {
+ dev_err(dev, "Mapping iv %u B at va=%pK for DMA failed\n",
+ hw_iv_size, req->iv);
+ rc = -ENOMEM;
+ goto chain_iv_exit;
+ }
+
+ dev_dbg(dev, "Mapped iv %u B at va=%pK to dma=%pad\n",
+ hw_iv_size, req->iv, &areq_ctx->gen_ctx.iv_dma_addr);
+ // TODO: what about CTR?? ask Ron
+ if (do_chain && areq_ctx->plaintext_authenticate_only) {
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ unsigned int iv_size_to_authenc = crypto_aead_ivsize(tfm);
+ unsigned int iv_ofs = GCM_BLOCK_RFC4_IV_OFFSET;
+ /* Chain to given list */
+ cc_add_buffer_entry(dev, sg_data,
+ (areq_ctx->gen_ctx.iv_dma_addr + iv_ofs),
+ iv_size_to_authenc, is_last,
+ &areq_ctx->assoc.mlli_nents);
+ areq_ctx->assoc_buff_type = CC_DMA_BUF_MLLI;
+ }
+
+chain_iv_exit:
+ return rc;
+}
+
+static int cc_aead_chain_assoc(struct cc_drvdata *drvdata,
+ struct aead_request *req,
+ struct buffer_array *sg_data,
+ bool is_last, bool do_chain)
+{
+ struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
+ int rc = 0;
+ u32 mapped_nents = 0;
+ struct scatterlist *current_sg = req->src;
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ unsigned int sg_index = 0;
+ u32 size_of_assoc = req->assoclen;
+ struct device *dev = drvdata_to_dev(drvdata);
+
+ if (areq_ctx->is_gcm4543)
+ size_of_assoc += crypto_aead_ivsize(tfm);
+
+ if (!sg_data) {
+ rc = -EINVAL;
+ goto chain_assoc_exit;
+ }
+
+ if (req->assoclen == 0) {
+ areq_ctx->assoc_buff_type = CC_DMA_BUF_NULL;
+ areq_ctx->assoc.nents = 0;
+ areq_ctx->assoc.mlli_nents = 0;
+ dev_dbg(dev, "Chain assoc of length 0: buff_type=%s nents=%u\n",
+ cc_dma_buf_type(areq_ctx->assoc_buff_type),
+ areq_ctx->assoc.nents);
+ goto chain_assoc_exit;
+ }
+
+ //iterate over the sgl to see how many entries are for associated data
+ //it is assumed that if we reach here , the sgl is already mapped
+ sg_index = current_sg->length;
+ //the first entry in the scatter list contains all the associated data
+ if (sg_index > size_of_assoc) {
+ mapped_nents++;
+ } else {
+ while (sg_index <= size_of_assoc) {
+ current_sg = sg_next(current_sg);
+ /* if have reached the end of the sgl, then this is
+ * unexpected
+ */
+ if (!current_sg) {
+ dev_err(dev, "reached end of sg list. unexpected\n");
+ return -EINVAL;
+ }
+ sg_index += current_sg->length;
+ mapped_nents++;
+ }
+ }
+ if (mapped_nents > LLI_MAX_NUM_OF_ASSOC_DATA_ENTRIES) {
+ dev_err(dev, "Too many fragments. current %d max %d\n",
+ mapped_nents, LLI_MAX_NUM_OF_ASSOC_DATA_ENTRIES);
+ return -ENOMEM;
+ }
+ areq_ctx->assoc.nents = mapped_nents;
+
+ /* in CCM case we have additional entry for
+ * ccm header configurations
+ */
+ if (areq_ctx->ccm_hdr_size != ccm_header_size_null) {
+ if ((mapped_nents + 1) > LLI_MAX_NUM_OF_ASSOC_DATA_ENTRIES) {
+ dev_err(dev, "CCM case.Too many fragments. Current %d max %d\n",
+ (areq_ctx->assoc.nents + 1),
+ LLI_MAX_NUM_OF_ASSOC_DATA_ENTRIES);
+ rc = -ENOMEM;
+ goto chain_assoc_exit;
+ }
+ }
+
+ if (mapped_nents == 1 && areq_ctx->ccm_hdr_size == ccm_header_size_null)
+ areq_ctx->assoc_buff_type = CC_DMA_BUF_DLLI;
+ else
+ areq_ctx->assoc_buff_type = CC_DMA_BUF_MLLI;
+
+ if (do_chain || areq_ctx->assoc_buff_type == CC_DMA_BUF_MLLI) {
+ dev_dbg(dev, "Chain assoc: buff_type=%s nents=%u\n",
+ cc_dma_buf_type(areq_ctx->assoc_buff_type),
+ areq_ctx->assoc.nents);
+ cc_add_sg_entry(dev, sg_data, areq_ctx->assoc.nents, req->src,
+ req->assoclen, 0, is_last,
+ &areq_ctx->assoc.mlli_nents);
+ areq_ctx->assoc_buff_type = CC_DMA_BUF_MLLI;
+ }
+
+chain_assoc_exit:
+ return rc;
+}
+
+static void cc_prepare_aead_data_dlli(struct aead_request *req,
+ u32 *src_last_bytes, u32 *dst_last_bytes)
+{
+ struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
+ enum drv_crypto_direction direct = areq_ctx->gen_ctx.op_type;
+ unsigned int authsize = areq_ctx->req_authsize;
+
+ areq_ctx->is_icv_fragmented = false;
+ if (req->src == req->dst) {
+ /*INPLACE*/
+ areq_ctx->icv_dma_addr = sg_dma_address(areq_ctx->src_sgl) +
+ (*src_last_bytes - authsize);
+ areq_ctx->icv_virt_addr = sg_virt(areq_ctx->src_sgl) +
+ (*src_last_bytes - authsize);
+ } else if (direct == DRV_CRYPTO_DIRECTION_DECRYPT) {
+ /*NON-INPLACE and DECRYPT*/
+ areq_ctx->icv_dma_addr = sg_dma_address(areq_ctx->src_sgl) +
+ (*src_last_bytes - authsize);
+ areq_ctx->icv_virt_addr = sg_virt(areq_ctx->src_sgl) +
+ (*src_last_bytes - authsize);
+ } else {
+ /*NON-INPLACE and ENCRYPT*/
+ areq_ctx->icv_dma_addr = sg_dma_address(areq_ctx->dst_sgl) +
+ (*dst_last_bytes - authsize);
+ areq_ctx->icv_virt_addr = sg_virt(areq_ctx->dst_sgl) +
+ (*dst_last_bytes - authsize);
+ }
+}
+
+static int cc_prepare_aead_data_mlli(struct cc_drvdata *drvdata,
+ struct aead_request *req,
+ struct buffer_array *sg_data,
+ u32 *src_last_bytes, u32 *dst_last_bytes,
+ bool is_last_table)
+{
+ struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
+ enum drv_crypto_direction direct = areq_ctx->gen_ctx.op_type;
+ unsigned int authsize = areq_ctx->req_authsize;
+ int rc = 0, icv_nents;
+ struct device *dev = drvdata_to_dev(drvdata);
+ struct scatterlist *sg;
+
+ if (req->src == req->dst) {
+ /*INPLACE*/
+ cc_add_sg_entry(dev, sg_data, areq_ctx->src.nents,
+ areq_ctx->src_sgl, areq_ctx->cryptlen,
+ areq_ctx->src_offset, is_last_table,
+ &areq_ctx->src.mlli_nents);
+
+ icv_nents = cc_get_aead_icv_nents(dev, areq_ctx->src_sgl,
+ areq_ctx->src.nents,
+ authsize, *src_last_bytes,
+ &areq_ctx->is_icv_fragmented);
+ if (icv_nents < 0) {
+ rc = -ENOTSUPP;
+ goto prepare_data_mlli_exit;
+ }
+
+ if (areq_ctx->is_icv_fragmented) {
+ /* Backup happens only when ICV is fragmented, ICV
+ * verification is made by CPU compare in order to
+ * simplify MAC verification upon request completion
+ */
+ if (direct == DRV_CRYPTO_DIRECTION_DECRYPT) {
+ /* In coherent platforms (e.g. ACP)
+ * already copying ICV for any
+ * INPLACE-DECRYPT operation, hence
+ * we must neglect this code.
+ */
+ if (!drvdata->coherent)
+ cc_copy_mac(dev, req, CC_SG_TO_BUF);
+
+ areq_ctx->icv_virt_addr = areq_ctx->backup_mac;
+ } else {
+ areq_ctx->icv_virt_addr = areq_ctx->mac_buf;
+ areq_ctx->icv_dma_addr =
+ areq_ctx->mac_buf_dma_addr;
+ }
+ } else { /* Contig. ICV */
+ sg = &areq_ctx->src_sgl[areq_ctx->src.nents - 1];
+ /*Should hanlde if the sg is not contig.*/
+ areq_ctx->icv_dma_addr = sg_dma_address(sg) +
+ (*src_last_bytes - authsize);
+ areq_ctx->icv_virt_addr = sg_virt(sg) +
+ (*src_last_bytes - authsize);
+ }
+
+ } else if (direct == DRV_CRYPTO_DIRECTION_DECRYPT) {
+ /*NON-INPLACE and DECRYPT*/
+ cc_add_sg_entry(dev, sg_data, areq_ctx->src.nents,
+ areq_ctx->src_sgl, areq_ctx->cryptlen,
+ areq_ctx->src_offset, is_last_table,
+ &areq_ctx->src.mlli_nents);
+ cc_add_sg_entry(dev, sg_data, areq_ctx->dst.nents,
+ areq_ctx->dst_sgl, areq_ctx->cryptlen,
+ areq_ctx->dst_offset, is_last_table,
+ &areq_ctx->dst.mlli_nents);
+
+ icv_nents = cc_get_aead_icv_nents(dev, areq_ctx->src_sgl,
+ areq_ctx->src.nents,
+ authsize, *src_last_bytes,
+ &areq_ctx->is_icv_fragmented);
+ if (icv_nents < 0) {
+ rc = -ENOTSUPP;
+ goto prepare_data_mlli_exit;
+ }
+
+ /* Backup happens only when ICV is fragmented, ICV
+ * verification is made by CPU compare in order to simplify
+ * MAC verification upon request completion
+ */
+ if (areq_ctx->is_icv_fragmented) {
+ cc_copy_mac(dev, req, CC_SG_TO_BUF);
+ areq_ctx->icv_virt_addr = areq_ctx->backup_mac;
+
+ } else { /* Contig. ICV */
+ sg = &areq_ctx->src_sgl[areq_ctx->src.nents - 1];
+ /*Should hanlde if the sg is not contig.*/
+ areq_ctx->icv_dma_addr = sg_dma_address(sg) +
+ (*src_last_bytes - authsize);
+ areq_ctx->icv_virt_addr = sg_virt(sg) +
+ (*src_last_bytes - authsize);
+ }
+
+ } else {
+ /*NON-INPLACE and ENCRYPT*/
+ cc_add_sg_entry(dev, sg_data, areq_ctx->dst.nents,
+ areq_ctx->dst_sgl, areq_ctx->cryptlen,
+ areq_ctx->dst_offset, is_last_table,
+ &areq_ctx->dst.mlli_nents);
+ cc_add_sg_entry(dev, sg_data, areq_ctx->src.nents,
+ areq_ctx->src_sgl, areq_ctx->cryptlen,
+ areq_ctx->src_offset, is_last_table,
+ &areq_ctx->src.mlli_nents);
+
+ icv_nents = cc_get_aead_icv_nents(dev, areq_ctx->dst_sgl,
+ areq_ctx->dst.nents,
+ authsize, *dst_last_bytes,
+ &areq_ctx->is_icv_fragmented);
+ if (icv_nents < 0) {
+ rc = -ENOTSUPP;
+ goto prepare_data_mlli_exit;
+ }
+
+ if (!areq_ctx->is_icv_fragmented) {
+ sg = &areq_ctx->dst_sgl[areq_ctx->dst.nents - 1];
+ /* Contig. ICV */
+ areq_ctx->icv_dma_addr = sg_dma_address(sg) +
+ (*dst_last_bytes - authsize);
+ areq_ctx->icv_virt_addr = sg_virt(sg) +
+ (*dst_last_bytes - authsize);
+ } else {
+ areq_ctx->icv_dma_addr = areq_ctx->mac_buf_dma_addr;
+ areq_ctx->icv_virt_addr = areq_ctx->mac_buf;
+ }
+ }
+
+prepare_data_mlli_exit:
+ return rc;
+}
+
+static int cc_aead_chain_data(struct cc_drvdata *drvdata,
+ struct aead_request *req,
+ struct buffer_array *sg_data,
+ bool is_last_table, bool do_chain)
+{
+ struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
+ struct device *dev = drvdata_to_dev(drvdata);
+ enum drv_crypto_direction direct = areq_ctx->gen_ctx.op_type;
+ unsigned int authsize = areq_ctx->req_authsize;
+ unsigned int src_last_bytes = 0, dst_last_bytes = 0;
+ int rc = 0;
+ u32 src_mapped_nents = 0, dst_mapped_nents = 0;
+ u32 offset = 0;
+ /* non-inplace mode */
+ unsigned int size_for_map = req->assoclen + req->cryptlen;
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ u32 sg_index = 0;
+ bool chained = false;
+ bool is_gcm4543 = areq_ctx->is_gcm4543;
+ u32 size_to_skip = req->assoclen;
+
+ if (is_gcm4543)
+ size_to_skip += crypto_aead_ivsize(tfm);
+
+ offset = size_to_skip;
+
+ if (!sg_data)
+ return -EINVAL;
+
+ areq_ctx->src_sgl = req->src;
+ areq_ctx->dst_sgl = req->dst;
+
+ if (is_gcm4543)
+ size_for_map += crypto_aead_ivsize(tfm);
+
+ size_for_map += (direct == DRV_CRYPTO_DIRECTION_ENCRYPT) ?
+ authsize : 0;
+ src_mapped_nents = cc_get_sgl_nents(dev, req->src, size_for_map,
+ &src_last_bytes, &chained);
+ sg_index = areq_ctx->src_sgl->length;
+ //check where the data starts
+ while (sg_index <= size_to_skip) {
+ offset -= areq_ctx->src_sgl->length;
+ areq_ctx->src_sgl = sg_next(areq_ctx->src_sgl);
+ //if have reached the end of the sgl, then this is unexpected
+ if (!areq_ctx->src_sgl) {
+ dev_err(dev, "reached end of sg list. unexpected\n");
+ return -EINVAL;
+ }
+ sg_index += areq_ctx->src_sgl->length;
+ src_mapped_nents--;
+ }
+ if (src_mapped_nents > LLI_MAX_NUM_OF_DATA_ENTRIES) {
+ dev_err(dev, "Too many fragments. current %d max %d\n",
+ src_mapped_nents, LLI_MAX_NUM_OF_DATA_ENTRIES);
+ return -ENOMEM;
+ }
+
+ areq_ctx->src.nents = src_mapped_nents;
+
+ areq_ctx->src_offset = offset;
+
+ if (req->src != req->dst) {
+ size_for_map = req->assoclen + req->cryptlen;
+ size_for_map += (direct == DRV_CRYPTO_DIRECTION_ENCRYPT) ?
+ authsize : 0;
+ if (is_gcm4543)
+ size_for_map += crypto_aead_ivsize(tfm);
+
+ rc = cc_map_sg(dev, req->dst, size_for_map, DMA_BIDIRECTIONAL,
+ &areq_ctx->dst.nents,
+ LLI_MAX_NUM_OF_DATA_ENTRIES, &dst_last_bytes,
+ &dst_mapped_nents);
+ if (rc) {
+ rc = -ENOMEM;
+ goto chain_data_exit;
+ }
+ }
+
+ dst_mapped_nents = cc_get_sgl_nents(dev, req->dst, size_for_map,
+ &dst_last_bytes, &chained);
+ sg_index = areq_ctx->dst_sgl->length;
+ offset = size_to_skip;
+
+ //check where the data starts
+ while (sg_index <= size_to_skip) {
+ offset -= areq_ctx->dst_sgl->length;
+ areq_ctx->dst_sgl = sg_next(areq_ctx->dst_sgl);
+ //if have reached the end of the sgl, then this is unexpected
+ if (!areq_ctx->dst_sgl) {
+ dev_err(dev, "reached end of sg list. unexpected\n");
+ return -EINVAL;
+ }
+ sg_index += areq_ctx->dst_sgl->length;
+ dst_mapped_nents--;
+ }
+ if (dst_mapped_nents > LLI_MAX_NUM_OF_DATA_ENTRIES) {
+ dev_err(dev, "Too many fragments. current %d max %d\n",
+ dst_mapped_nents, LLI_MAX_NUM_OF_DATA_ENTRIES);
+ return -ENOMEM;
+ }
+ areq_ctx->dst.nents = dst_mapped_nents;
+ areq_ctx->dst_offset = offset;
+ if (src_mapped_nents > 1 ||
+ dst_mapped_nents > 1 ||
+ do_chain) {
+ areq_ctx->data_buff_type = CC_DMA_BUF_MLLI;
+ rc = cc_prepare_aead_data_mlli(drvdata, req, sg_data,
+ &src_last_bytes,
+ &dst_last_bytes, is_last_table);
+ } else {
+ areq_ctx->data_buff_type = CC_DMA_BUF_DLLI;
+ cc_prepare_aead_data_dlli(req, &src_last_bytes,
+ &dst_last_bytes);
+ }
+
+chain_data_exit:
+ return rc;
+}
+
+static void cc_update_aead_mlli_nents(struct cc_drvdata *drvdata,
+ struct aead_request *req)
+{
+ struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
+ u32 curr_mlli_size = 0;
+
+ if (areq_ctx->assoc_buff_type == CC_DMA_BUF_MLLI) {
+ areq_ctx->assoc.sram_addr = drvdata->mlli_sram_addr;
+ curr_mlli_size = areq_ctx->assoc.mlli_nents *
+ LLI_ENTRY_BYTE_SIZE;
+ }
+
+ if (areq_ctx->data_buff_type == CC_DMA_BUF_MLLI) {
+ /*Inplace case dst nents equal to src nents*/
+ if (req->src == req->dst) {
+ areq_ctx->dst.mlli_nents = areq_ctx->src.mlli_nents;
+ areq_ctx->src.sram_addr = drvdata->mlli_sram_addr +
+ curr_mlli_size;
+ areq_ctx->dst.sram_addr = areq_ctx->src.sram_addr;
+ if (!areq_ctx->is_single_pass)
+ areq_ctx->assoc.mlli_nents +=
+ areq_ctx->src.mlli_nents;
+ } else {
+ if (areq_ctx->gen_ctx.op_type ==
+ DRV_CRYPTO_DIRECTION_DECRYPT) {
+ areq_ctx->src.sram_addr =
+ drvdata->mlli_sram_addr +
+ curr_mlli_size;
+ areq_ctx->dst.sram_addr =
+ areq_ctx->src.sram_addr +
+ areq_ctx->src.mlli_nents *
+ LLI_ENTRY_BYTE_SIZE;
+ if (!areq_ctx->is_single_pass)
+ areq_ctx->assoc.mlli_nents +=
+ areq_ctx->src.mlli_nents;
+ } else {
+ areq_ctx->dst.sram_addr =
+ drvdata->mlli_sram_addr +
+ curr_mlli_size;
+ areq_ctx->src.sram_addr =
+ areq_ctx->dst.sram_addr +
+ areq_ctx->dst.mlli_nents *
+ LLI_ENTRY_BYTE_SIZE;
+ if (!areq_ctx->is_single_pass)
+ areq_ctx->assoc.mlli_nents +=
+ areq_ctx->dst.mlli_nents;
+ }
+ }
+ }
+}
+
+int cc_map_aead_request(struct cc_drvdata *drvdata, struct aead_request *req)
+{
+ struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
+ struct mlli_params *mlli_params = &areq_ctx->mlli_params;
+ struct device *dev = drvdata_to_dev(drvdata);
+ struct buffer_array sg_data;
+ unsigned int authsize = areq_ctx->req_authsize;
+ struct buff_mgr_handle *buff_mgr = drvdata->buff_mgr_handle;
+ int rc = 0;
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ bool is_gcm4543 = areq_ctx->is_gcm4543;
+ dma_addr_t dma_addr;
+ u32 mapped_nents = 0;
+ u32 dummy = 0; /*used for the assoc data fragments */
+ u32 size_to_map = 0;
+ gfp_t flags = cc_gfp_flags(&req->base);
+
+ mlli_params->curr_pool = NULL;
+ sg_data.num_of_buffers = 0;
+
+ /* copy mac to a temporary location to deal with possible
+ * data memory overriding that caused by cache coherence problem.
+ */
+ if (drvdata->coherent &&
+ areq_ctx->gen_ctx.op_type == DRV_CRYPTO_DIRECTION_DECRYPT &&
+ req->src == req->dst)
+ cc_copy_mac(dev, req, CC_SG_TO_BUF);
+
+ /* cacluate the size for cipher remove ICV in decrypt*/
+ areq_ctx->cryptlen = (areq_ctx->gen_ctx.op_type ==
+ DRV_CRYPTO_DIRECTION_ENCRYPT) ?
+ req->cryptlen :
+ (req->cryptlen - authsize);
+
+ dma_addr = dma_map_single(dev, areq_ctx->mac_buf, MAX_MAC_SIZE,
+ DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(dev, dma_addr)) {
+ dev_err(dev, "Mapping mac_buf %u B at va=%pK for DMA failed\n",
+ MAX_MAC_SIZE, areq_ctx->mac_buf);
+ rc = -ENOMEM;
+ goto aead_map_failure;
+ }
+ areq_ctx->mac_buf_dma_addr = dma_addr;
+
+ if (areq_ctx->ccm_hdr_size != ccm_header_size_null) {
+ void *addr = areq_ctx->ccm_config + CCM_CTR_COUNT_0_OFFSET;
+
+ dma_addr = dma_map_single(dev, addr, AES_BLOCK_SIZE,
+ DMA_TO_DEVICE);
+
+ if (dma_mapping_error(dev, dma_addr)) {
+ dev_err(dev, "Mapping mac_buf %u B at va=%pK for DMA failed\n",
+ AES_BLOCK_SIZE, addr);
+ areq_ctx->ccm_iv0_dma_addr = 0;
+ rc = -ENOMEM;
+ goto aead_map_failure;
+ }
+ areq_ctx->ccm_iv0_dma_addr = dma_addr;
+
+ if (cc_set_aead_conf_buf(dev, areq_ctx, areq_ctx->ccm_config,
+ &sg_data, req->assoclen)) {
+ rc = -ENOMEM;
+ goto aead_map_failure;
+ }
+ }
+
+ if (areq_ctx->cipher_mode == DRV_CIPHER_GCTR) {
+ dma_addr = dma_map_single(dev, areq_ctx->hkey, AES_BLOCK_SIZE,
+ DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(dev, dma_addr)) {
+ dev_err(dev, "Mapping hkey %u B at va=%pK for DMA failed\n",
+ AES_BLOCK_SIZE, areq_ctx->hkey);
+ rc = -ENOMEM;
+ goto aead_map_failure;
+ }
+ areq_ctx->hkey_dma_addr = dma_addr;
+
+ dma_addr = dma_map_single(dev, &areq_ctx->gcm_len_block,
+ AES_BLOCK_SIZE, DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, dma_addr)) {
+ dev_err(dev, "Mapping gcm_len_block %u B at va=%pK for DMA failed\n",
+ AES_BLOCK_SIZE, &areq_ctx->gcm_len_block);
+ rc = -ENOMEM;
+ goto aead_map_failure;
+ }
+ areq_ctx->gcm_block_len_dma_addr = dma_addr;
+
+ dma_addr = dma_map_single(dev, areq_ctx->gcm_iv_inc1,
+ AES_BLOCK_SIZE, DMA_TO_DEVICE);
+
+ if (dma_mapping_error(dev, dma_addr)) {
+ dev_err(dev, "Mapping gcm_iv_inc1 %u B at va=%pK for DMA failed\n",
+ AES_BLOCK_SIZE, (areq_ctx->gcm_iv_inc1));
+ areq_ctx->gcm_iv_inc1_dma_addr = 0;
+ rc = -ENOMEM;
+ goto aead_map_failure;
+ }
+ areq_ctx->gcm_iv_inc1_dma_addr = dma_addr;
+
+ dma_addr = dma_map_single(dev, areq_ctx->gcm_iv_inc2,
+ AES_BLOCK_SIZE, DMA_TO_DEVICE);
+
+ if (dma_mapping_error(dev, dma_addr)) {
+ dev_err(dev, "Mapping gcm_iv_inc2 %u B at va=%pK for DMA failed\n",
+ AES_BLOCK_SIZE, (areq_ctx->gcm_iv_inc2));
+ areq_ctx->gcm_iv_inc2_dma_addr = 0;
+ rc = -ENOMEM;
+ goto aead_map_failure;
+ }
+ areq_ctx->gcm_iv_inc2_dma_addr = dma_addr;
+ }
+
+ size_to_map = req->cryptlen + req->assoclen;
+ if (areq_ctx->gen_ctx.op_type == DRV_CRYPTO_DIRECTION_ENCRYPT)
+ size_to_map += authsize;
+
+ if (is_gcm4543)
+ size_to_map += crypto_aead_ivsize(tfm);
+ rc = cc_map_sg(dev, req->src, size_to_map, DMA_BIDIRECTIONAL,
+ &areq_ctx->src.nents,
+ (LLI_MAX_NUM_OF_ASSOC_DATA_ENTRIES +
+ LLI_MAX_NUM_OF_DATA_ENTRIES),
+ &dummy, &mapped_nents);
+ if (rc) {
+ rc = -ENOMEM;
+ goto aead_map_failure;
+ }
+
+ if (areq_ctx->is_single_pass) {
+ /*
+ * Create MLLI table for:
+ * (1) Assoc. data
+ * (2) Src/Dst SGLs
+ * Note: IV is contg. buffer (not an SGL)
+ */
+ rc = cc_aead_chain_assoc(drvdata, req, &sg_data, true, false);
+ if (rc)
+ goto aead_map_failure;
+ rc = cc_aead_chain_iv(drvdata, req, &sg_data, true, false);
+ if (rc)
+ goto aead_map_failure;
+ rc = cc_aead_chain_data(drvdata, req, &sg_data, true, false);
+ if (rc)
+ goto aead_map_failure;
+ } else { /* DOUBLE-PASS flow */
+ /*
+ * Prepare MLLI table(s) in this order:
+ *
+ * If ENCRYPT/DECRYPT (inplace):
+ * (1) MLLI table for assoc
+ * (2) IV entry (chained right after end of assoc)
+ * (3) MLLI for src/dst (inplace operation)
+ *
+ * If ENCRYPT (non-inplace)
+ * (1) MLLI table for assoc
+ * (2) IV entry (chained right after end of assoc)
+ * (3) MLLI for dst
+ * (4) MLLI for src
+ *
+ * If DECRYPT (non-inplace)
+ * (1) MLLI table for assoc
+ * (2) IV entry (chained right after end of assoc)
+ * (3) MLLI for src
+ * (4) MLLI for dst
+ */
+ rc = cc_aead_chain_assoc(drvdata, req, &sg_data, false, true);
+ if (rc)
+ goto aead_map_failure;
+ rc = cc_aead_chain_iv(drvdata, req, &sg_data, false, true);
+ if (rc)
+ goto aead_map_failure;
+ rc = cc_aead_chain_data(drvdata, req, &sg_data, true, true);
+ if (rc)
+ goto aead_map_failure;
+ }
+
+ /* Mlli support -start building the MLLI according to the above
+ * results
+ */
+ if (areq_ctx->assoc_buff_type == CC_DMA_BUF_MLLI ||
+ areq_ctx->data_buff_type == CC_DMA_BUF_MLLI) {
+ mlli_params->curr_pool = buff_mgr->mlli_buffs_pool;
+ rc = cc_generate_mlli(dev, &sg_data, mlli_params, flags);
+ if (rc)
+ goto aead_map_failure;
+
+ cc_update_aead_mlli_nents(drvdata, req);
+ dev_dbg(dev, "assoc params mn %d\n",
+ areq_ctx->assoc.mlli_nents);
+ dev_dbg(dev, "src params mn %d\n", areq_ctx->src.mlli_nents);
+ dev_dbg(dev, "dst params mn %d\n", areq_ctx->dst.mlli_nents);
+ }
+ return 0;
+
+aead_map_failure:
+ cc_unmap_aead_request(dev, req);
+ return rc;
+}
+
+int cc_map_hash_request_final(struct cc_drvdata *drvdata, void *ctx,
+ struct scatterlist *src, unsigned int nbytes,
+ bool do_update, gfp_t flags)
+{
+ struct ahash_req_ctx *areq_ctx = (struct ahash_req_ctx *)ctx;
+ struct device *dev = drvdata_to_dev(drvdata);
+ u8 *curr_buff = cc_hash_buf(areq_ctx);
+ u32 *curr_buff_cnt = cc_hash_buf_cnt(areq_ctx);
+ struct mlli_params *mlli_params = &areq_ctx->mlli_params;
+ struct buffer_array sg_data;
+ struct buff_mgr_handle *buff_mgr = drvdata->buff_mgr_handle;
+ u32 dummy = 0;
+ u32 mapped_nents = 0;
+
+ dev_dbg(dev, "final params : curr_buff=%pK curr_buff_cnt=0x%X nbytes = 0x%X src=%pK curr_index=%u\n",
+ curr_buff, *curr_buff_cnt, nbytes, src, areq_ctx->buff_index);
+ /* Init the type of the dma buffer */
+ areq_ctx->data_dma_buf_type = CC_DMA_BUF_NULL;
+ mlli_params->curr_pool = NULL;
+ sg_data.num_of_buffers = 0;
+ areq_ctx->in_nents = 0;
+
+ if (nbytes == 0 && *curr_buff_cnt == 0) {
+ /* nothing to do */
+ return 0;
+ }
+
+ /*TODO: copy data in case that buffer is enough for operation */
+ /* map the previous buffer */
+ if (*curr_buff_cnt) {
+ if (cc_set_hash_buf(dev, areq_ctx, curr_buff, *curr_buff_cnt,
+ &sg_data)) {
+ return -ENOMEM;
+ }
+ }
+
+ if (src && nbytes > 0 && do_update) {
+ if (cc_map_sg(dev, src, nbytes, DMA_TO_DEVICE,
+ &areq_ctx->in_nents, LLI_MAX_NUM_OF_DATA_ENTRIES,
+ &dummy, &mapped_nents)) {
+ goto unmap_curr_buff;
+ }
+ if (src && mapped_nents == 1 &&
+ areq_ctx->data_dma_buf_type == CC_DMA_BUF_NULL) {
+ memcpy(areq_ctx->buff_sg, src,
+ sizeof(struct scatterlist));
+ areq_ctx->buff_sg->length = nbytes;
+ areq_ctx->curr_sg = areq_ctx->buff_sg;
+ areq_ctx->data_dma_buf_type = CC_DMA_BUF_DLLI;
+ } else {
+ areq_ctx->data_dma_buf_type = CC_DMA_BUF_MLLI;
+ }
+ }
+
+ /*build mlli */
+ if (areq_ctx->data_dma_buf_type == CC_DMA_BUF_MLLI) {
+ mlli_params->curr_pool = buff_mgr->mlli_buffs_pool;
+ /* add the src data to the sg_data */
+ cc_add_sg_entry(dev, &sg_data, areq_ctx->in_nents, src, nbytes,
+ 0, true, &areq_ctx->mlli_nents);
+ if (cc_generate_mlli(dev, &sg_data, mlli_params, flags))
+ goto fail_unmap_din;
+ }
+ /* change the buffer index for the unmap function */
+ areq_ctx->buff_index = (areq_ctx->buff_index ^ 1);
+ dev_dbg(dev, "areq_ctx->data_dma_buf_type = %s\n",
+ cc_dma_buf_type(areq_ctx->data_dma_buf_type));
+ return 0;
+
+fail_unmap_din:
+ dma_unmap_sg(dev, src, areq_ctx->in_nents, DMA_TO_DEVICE);
+
+unmap_curr_buff:
+ if (*curr_buff_cnt)
+ dma_unmap_sg(dev, areq_ctx->buff_sg, 1, DMA_TO_DEVICE);
+
+ return -ENOMEM;
+}
+
+int cc_map_hash_request_update(struct cc_drvdata *drvdata, void *ctx,
+ struct scatterlist *src, unsigned int nbytes,
+ unsigned int block_size, gfp_t flags)
+{
+ struct ahash_req_ctx *areq_ctx = (struct ahash_req_ctx *)ctx;
+ struct device *dev = drvdata_to_dev(drvdata);
+ u8 *curr_buff = cc_hash_buf(areq_ctx);
+ u32 *curr_buff_cnt = cc_hash_buf_cnt(areq_ctx);
+ u8 *next_buff = cc_next_buf(areq_ctx);
+ u32 *next_buff_cnt = cc_next_buf_cnt(areq_ctx);
+ struct mlli_params *mlli_params = &areq_ctx->mlli_params;
+ unsigned int update_data_len;
+ u32 total_in_len = nbytes + *curr_buff_cnt;
+ struct buffer_array sg_data;
+ struct buff_mgr_handle *buff_mgr = drvdata->buff_mgr_handle;
+ unsigned int swap_index = 0;
+ u32 dummy = 0;
+ u32 mapped_nents = 0;
+
+ dev_dbg(dev, " update params : curr_buff=%pK curr_buff_cnt=0x%X nbytes=0x%X src=%pK curr_index=%u\n",
+ curr_buff, *curr_buff_cnt, nbytes, src, areq_ctx->buff_index);
+ /* Init the type of the dma buffer */
+ areq_ctx->data_dma_buf_type = CC_DMA_BUF_NULL;
+ mlli_params->curr_pool = NULL;
+ areq_ctx->curr_sg = NULL;
+ sg_data.num_of_buffers = 0;
+ areq_ctx->in_nents = 0;
+
+ if (total_in_len < block_size) {
+ dev_dbg(dev, " less than one block: curr_buff=%pK *curr_buff_cnt=0x%X copy_to=%pK\n",
+ curr_buff, *curr_buff_cnt, &curr_buff[*curr_buff_cnt]);
+ areq_ctx->in_nents =
+ cc_get_sgl_nents(dev, src, nbytes, &dummy, NULL);
+ sg_copy_to_buffer(src, areq_ctx->in_nents,
+ &curr_buff[*curr_buff_cnt], nbytes);
+ *curr_buff_cnt += nbytes;
+ return 1;
+ }
+
+ /* Calculate the residue size*/
+ *next_buff_cnt = total_in_len & (block_size - 1);
+ /* update data len */
+ update_data_len = total_in_len - *next_buff_cnt;
+
+ dev_dbg(dev, " temp length : *next_buff_cnt=0x%X update_data_len=0x%X\n",
+ *next_buff_cnt, update_data_len);
+
+ /* Copy the new residue to next buffer */
+ if (*next_buff_cnt) {
+ dev_dbg(dev, " handle residue: next buff %pK skip data %u residue %u\n",
+ next_buff, (update_data_len - *curr_buff_cnt),
+ *next_buff_cnt);
+ cc_copy_sg_portion(dev, next_buff, src,
+ (update_data_len - *curr_buff_cnt),
+ nbytes, CC_SG_TO_BUF);
+ /* change the buffer index for next operation */
+ swap_index = 1;
+ }
+
+ if (*curr_buff_cnt) {
+ if (cc_set_hash_buf(dev, areq_ctx, curr_buff, *curr_buff_cnt,
+ &sg_data)) {
+ return -ENOMEM;
+ }
+ /* change the buffer index for next operation */
+ swap_index = 1;
+ }
+
+ if (update_data_len > *curr_buff_cnt) {
+ if (cc_map_sg(dev, src, (update_data_len - *curr_buff_cnt),
+ DMA_TO_DEVICE, &areq_ctx->in_nents,
+ LLI_MAX_NUM_OF_DATA_ENTRIES, &dummy,
+ &mapped_nents)) {
+ goto unmap_curr_buff;
+ }
+ if (mapped_nents == 1 &&
+ areq_ctx->data_dma_buf_type == CC_DMA_BUF_NULL) {
+ /* only one entry in the SG and no previous data */
+ memcpy(areq_ctx->buff_sg, src,
+ sizeof(struct scatterlist));
+ areq_ctx->buff_sg->length = update_data_len;
+ areq_ctx->data_dma_buf_type = CC_DMA_BUF_DLLI;
+ areq_ctx->curr_sg = areq_ctx->buff_sg;
+ } else {
+ areq_ctx->data_dma_buf_type = CC_DMA_BUF_MLLI;
+ }
+ }
+
+ if (areq_ctx->data_dma_buf_type == CC_DMA_BUF_MLLI) {
+ mlli_params->curr_pool = buff_mgr->mlli_buffs_pool;
+ /* add the src data to the sg_data */
+ cc_add_sg_entry(dev, &sg_data, areq_ctx->in_nents, src,
+ (update_data_len - *curr_buff_cnt), 0, true,
+ &areq_ctx->mlli_nents);
+ if (cc_generate_mlli(dev, &sg_data, mlli_params, flags))
+ goto fail_unmap_din;
+ }
+ areq_ctx->buff_index = (areq_ctx->buff_index ^ swap_index);
+
+ return 0;
+
+fail_unmap_din:
+ dma_unmap_sg(dev, src, areq_ctx->in_nents, DMA_TO_DEVICE);
+
+unmap_curr_buff:
+ if (*curr_buff_cnt)
+ dma_unmap_sg(dev, areq_ctx->buff_sg, 1, DMA_TO_DEVICE);
+
+ return -ENOMEM;
+}
+
+void cc_unmap_hash_request(struct device *dev, void *ctx,
+ struct scatterlist *src, bool do_revert)
+{
+ struct ahash_req_ctx *areq_ctx = (struct ahash_req_ctx *)ctx;
+ u32 *prev_len = cc_next_buf_cnt(areq_ctx);
+
+ /*In case a pool was set, a table was
+ *allocated and should be released
+ */
+ if (areq_ctx->mlli_params.curr_pool) {
+ dev_dbg(dev, "free MLLI buffer: dma=%pad virt=%pK\n",
+ &areq_ctx->mlli_params.mlli_dma_addr,
+ areq_ctx->mlli_params.mlli_virt_addr);
+ dma_pool_free(areq_ctx->mlli_params.curr_pool,
+ areq_ctx->mlli_params.mlli_virt_addr,
+ areq_ctx->mlli_params.mlli_dma_addr);
+ }
+
+ if (src && areq_ctx->in_nents) {
+ dev_dbg(dev, "Unmapped sg src: virt=%pK dma=%pad len=0x%X\n",
+ sg_virt(src), &sg_dma_address(src), sg_dma_len(src));
+ dma_unmap_sg(dev, src,
+ areq_ctx->in_nents, DMA_TO_DEVICE);
+ }
+
+ if (*prev_len) {
+ dev_dbg(dev, "Unmapped buffer: areq_ctx->buff_sg=%pK dma=%pad len 0x%X\n",
+ sg_virt(areq_ctx->buff_sg),
+ &sg_dma_address(areq_ctx->buff_sg),
+ sg_dma_len(areq_ctx->buff_sg));
+ dma_unmap_sg(dev, areq_ctx->buff_sg, 1, DMA_TO_DEVICE);
+ if (!do_revert) {
+ /* clean the previous data length for update
+ * operation
+ */
+ *prev_len = 0;
+ } else {
+ areq_ctx->buff_index ^= 1;
+ }
+ }
+}
+
+int cc_buffer_mgr_init(struct cc_drvdata *drvdata)
+{
+ struct buff_mgr_handle *buff_mgr_handle;
+ struct device *dev = drvdata_to_dev(drvdata);
+
+ buff_mgr_handle = kmalloc(sizeof(*buff_mgr_handle), GFP_KERNEL);
+ if (!buff_mgr_handle)
+ return -ENOMEM;
+
+ drvdata->buff_mgr_handle = buff_mgr_handle;
+
+ buff_mgr_handle->mlli_buffs_pool =
+ dma_pool_create("dx_single_mlli_tables", dev,
+ MAX_NUM_OF_TOTAL_MLLI_ENTRIES *
+ LLI_ENTRY_BYTE_SIZE,
+ MLLI_TABLE_MIN_ALIGNMENT, 0);
+
+ if (!buff_mgr_handle->mlli_buffs_pool)
+ goto error;
+
+ return 0;
+
+error:
+ cc_buffer_mgr_fini(drvdata);
+ return -ENOMEM;
+}
+
+int cc_buffer_mgr_fini(struct cc_drvdata *drvdata)
+{
+ struct buff_mgr_handle *buff_mgr_handle = drvdata->buff_mgr_handle;
+
+ if (buff_mgr_handle) {
+ dma_pool_destroy(buff_mgr_handle->mlli_buffs_pool);
+ kfree(drvdata->buff_mgr_handle);
+ drvdata->buff_mgr_handle = NULL;
+ }
+ return 0;
+}
diff --git a/drivers/crypto/ccree/cc_buffer_mgr.h b/drivers/crypto/ccree/cc_buffer_mgr.h
new file mode 100644
index 000000000000..3ec4b4db5247
--- /dev/null
+++ b/drivers/crypto/ccree/cc_buffer_mgr.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
+
+/* \file cc_buffer_mgr.h
+ * Buffer Manager
+ */
+
+#ifndef __CC_BUFFER_MGR_H__
+#define __CC_BUFFER_MGR_H__
+
+#include <crypto/algapi.h>
+
+#include "cc_driver.h"
+
+enum cc_req_dma_buf_type {
+ CC_DMA_BUF_NULL = 0,
+ CC_DMA_BUF_DLLI,
+ CC_DMA_BUF_MLLI
+};
+
+enum cc_sg_cpy_direct {
+ CC_SG_TO_BUF = 0,
+ CC_SG_FROM_BUF = 1
+};
+
+struct cc_mlli {
+ cc_sram_addr_t sram_addr;
+ unsigned int nents; //sg nents
+ unsigned int mlli_nents; //mlli nents might be different than the above
+};
+
+struct mlli_params {
+ struct dma_pool *curr_pool;
+ u8 *mlli_virt_addr;
+ dma_addr_t mlli_dma_addr;
+ u32 mlli_len;
+};
+
+int cc_buffer_mgr_init(struct cc_drvdata *drvdata);
+
+int cc_buffer_mgr_fini(struct cc_drvdata *drvdata);
+
+int cc_map_cipher_request(struct cc_drvdata *drvdata, void *ctx,
+ unsigned int ivsize, unsigned int nbytes,
+ void *info, struct scatterlist *src,
+ struct scatterlist *dst, gfp_t flags);
+
+void cc_unmap_cipher_request(struct device *dev, void *ctx, unsigned int ivsize,
+ struct scatterlist *src, struct scatterlist *dst);
+
+int cc_map_aead_request(struct cc_drvdata *drvdata, struct aead_request *req);
+
+void cc_unmap_aead_request(struct device *dev, struct aead_request *req);
+
+int cc_map_hash_request_final(struct cc_drvdata *drvdata, void *ctx,
+ struct scatterlist *src, unsigned int nbytes,
+ bool do_update, gfp_t flags);
+
+int cc_map_hash_request_update(struct cc_drvdata *drvdata, void *ctx,
+ struct scatterlist *src, unsigned int nbytes,
+ unsigned int block_size, gfp_t flags);
+
+void cc_unmap_hash_request(struct device *dev, void *ctx,
+ struct scatterlist *src, bool do_revert);
+
+void cc_copy_sg_portion(struct device *dev, u8 *dest, struct scatterlist *sg,
+ u32 to_skip, u32 end, enum cc_sg_cpy_direct direct);
+
+void cc_zero_sgl(struct scatterlist *sgl, u32 data_len);
+
+#endif /*__BUFFER_MGR_H__*/
diff --git a/drivers/crypto/ccree/cc_cipher.c b/drivers/crypto/ccree/cc_cipher.c
new file mode 100644
index 000000000000..df98f7afe645
--- /dev/null
+++ b/drivers/crypto/ccree/cc_cipher.c
@@ -0,0 +1,1150 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <crypto/algapi.h>
+#include <crypto/internal/skcipher.h>
+#include <crypto/des.h>
+#include <crypto/xts.h>
+#include <crypto/scatterwalk.h>
+
+#include "cc_driver.h"
+#include "cc_lli_defs.h"
+#include "cc_buffer_mgr.h"
+#include "cc_cipher.h"
+#include "cc_request_mgr.h"
+
+#define MAX_ABLKCIPHER_SEQ_LEN 6
+
+#define template_skcipher template_u.skcipher
+
+#define CC_MIN_AES_XTS_SIZE 0x10
+#define CC_MAX_AES_XTS_SIZE 0x2000
+struct cc_cipher_handle {
+ struct list_head alg_list;
+};
+
+struct cc_user_key_info {
+ u8 *key;
+ dma_addr_t key_dma_addr;
+};
+
+struct cc_hw_key_info {
+ enum cc_hw_crypto_key key1_slot;
+ enum cc_hw_crypto_key key2_slot;
+};
+
+struct cc_cipher_ctx {
+ struct cc_drvdata *drvdata;
+ int keylen;
+ int key_round_number;
+ int cipher_mode;
+ int flow_mode;
+ unsigned int flags;
+ struct cc_user_key_info user;
+ struct cc_hw_key_info hw;
+ struct crypto_shash *shash_tfm;
+};
+
+static void cc_cipher_complete(struct device *dev, void *cc_req, int err);
+
+static int validate_keys_sizes(struct cc_cipher_ctx *ctx_p, u32 size)
+{
+ switch (ctx_p->flow_mode) {
+ case S_DIN_to_AES:
+ switch (size) {
+ case CC_AES_128_BIT_KEY_SIZE:
+ case CC_AES_192_BIT_KEY_SIZE:
+ if (ctx_p->cipher_mode != DRV_CIPHER_XTS &&
+ ctx_p->cipher_mode != DRV_CIPHER_ESSIV &&
+ ctx_p->cipher_mode != DRV_CIPHER_BITLOCKER)
+ return 0;
+ break;
+ case CC_AES_256_BIT_KEY_SIZE:
+ return 0;
+ case (CC_AES_192_BIT_KEY_SIZE * 2):
+ case (CC_AES_256_BIT_KEY_SIZE * 2):
+ if (ctx_p->cipher_mode == DRV_CIPHER_XTS ||
+ ctx_p->cipher_mode == DRV_CIPHER_ESSIV ||
+ ctx_p->cipher_mode == DRV_CIPHER_BITLOCKER)
+ return 0;
+ break;
+ default:
+ break;
+ }
+ case S_DIN_to_DES:
+ if (size == DES3_EDE_KEY_SIZE || size == DES_KEY_SIZE)
+ return 0;
+ break;
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static int validate_data_size(struct cc_cipher_ctx *ctx_p,
+ unsigned int size)
+{
+ switch (ctx_p->flow_mode) {
+ case S_DIN_to_AES:
+ switch (ctx_p->cipher_mode) {
+ case DRV_CIPHER_XTS:
+ if (size >= CC_MIN_AES_XTS_SIZE &&
+ size <= CC_MAX_AES_XTS_SIZE &&
+ IS_ALIGNED(size, AES_BLOCK_SIZE))
+ return 0;
+ break;
+ case DRV_CIPHER_CBC_CTS:
+ if (size >= AES_BLOCK_SIZE)
+ return 0;
+ break;
+ case DRV_CIPHER_OFB:
+ case DRV_CIPHER_CTR:
+ return 0;
+ case DRV_CIPHER_ECB:
+ case DRV_CIPHER_CBC:
+ case DRV_CIPHER_ESSIV:
+ case DRV_CIPHER_BITLOCKER:
+ if (IS_ALIGNED(size, AES_BLOCK_SIZE))
+ return 0;
+ break;
+ default:
+ break;
+ }
+ break;
+ case S_DIN_to_DES:
+ if (IS_ALIGNED(size, DES_BLOCK_SIZE))
+ return 0;
+ break;
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static int cc_cipher_init(struct crypto_tfm *tfm)
+{
+ struct cc_cipher_ctx *ctx_p = crypto_tfm_ctx(tfm);
+ struct cc_crypto_alg *cc_alg =
+ container_of(tfm->__crt_alg, struct cc_crypto_alg,
+ skcipher_alg.base);
+ struct device *dev = drvdata_to_dev(cc_alg->drvdata);
+ unsigned int max_key_buf_size = cc_alg->skcipher_alg.max_keysize;
+ int rc = 0;
+
+ dev_dbg(dev, "Initializing context @%p for %s\n", ctx_p,
+ crypto_tfm_alg_name(tfm));
+
+ crypto_skcipher_set_reqsize(__crypto_skcipher_cast(tfm),
+ sizeof(struct cipher_req_ctx));
+
+ ctx_p->cipher_mode = cc_alg->cipher_mode;
+ ctx_p->flow_mode = cc_alg->flow_mode;
+ ctx_p->drvdata = cc_alg->drvdata;
+
+ /* Allocate key buffer, cache line aligned */
+ ctx_p->user.key = kmalloc(max_key_buf_size, GFP_KERNEL);
+ if (!ctx_p->user.key)
+ return -ENOMEM;
+
+ dev_dbg(dev, "Allocated key buffer in context. key=@%p\n",
+ ctx_p->user.key);
+
+ /* Map key buffer */
+ ctx_p->user.key_dma_addr = dma_map_single(dev, (void *)ctx_p->user.key,
+ max_key_buf_size,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, ctx_p->user.key_dma_addr)) {
+ dev_err(dev, "Mapping Key %u B at va=%pK for DMA failed\n",
+ max_key_buf_size, ctx_p->user.key);
+ return -ENOMEM;
+ }
+ dev_dbg(dev, "Mapped key %u B at va=%pK to dma=%pad\n",
+ max_key_buf_size, ctx_p->user.key, &ctx_p->user.key_dma_addr);
+
+ if (ctx_p->cipher_mode == DRV_CIPHER_ESSIV) {
+ /* Alloc hash tfm for essiv */
+ ctx_p->shash_tfm = crypto_alloc_shash("sha256-generic", 0, 0);
+ if (IS_ERR(ctx_p->shash_tfm)) {
+ dev_err(dev, "Error allocating hash tfm for ESSIV.\n");
+ return PTR_ERR(ctx_p->shash_tfm);
+ }
+ }
+
+ return rc;
+}
+
+static void cc_cipher_exit(struct crypto_tfm *tfm)
+{
+ struct crypto_alg *alg = tfm->__crt_alg;
+ struct cc_crypto_alg *cc_alg =
+ container_of(alg, struct cc_crypto_alg,
+ skcipher_alg.base);
+ unsigned int max_key_buf_size = cc_alg->skcipher_alg.max_keysize;
+ struct cc_cipher_ctx *ctx_p = crypto_tfm_ctx(tfm);
+ struct device *dev = drvdata_to_dev(ctx_p->drvdata);
+
+ dev_dbg(dev, "Clearing context @%p for %s\n",
+ crypto_tfm_ctx(tfm), crypto_tfm_alg_name(tfm));
+
+ if (ctx_p->cipher_mode == DRV_CIPHER_ESSIV) {
+ /* Free hash tfm for essiv */
+ crypto_free_shash(ctx_p->shash_tfm);
+ ctx_p->shash_tfm = NULL;
+ }
+
+ /* Unmap key buffer */
+ dma_unmap_single(dev, ctx_p->user.key_dma_addr, max_key_buf_size,
+ DMA_TO_DEVICE);
+ dev_dbg(dev, "Unmapped key buffer key_dma_addr=%pad\n",
+ &ctx_p->user.key_dma_addr);
+
+ /* Free key buffer in context */
+ kzfree(ctx_p->user.key);
+ dev_dbg(dev, "Free key buffer in context. key=@%p\n", ctx_p->user.key);
+}
+
+struct tdes_keys {
+ u8 key1[DES_KEY_SIZE];
+ u8 key2[DES_KEY_SIZE];
+ u8 key3[DES_KEY_SIZE];
+};
+
+static enum cc_hw_crypto_key hw_key_to_cc_hw_key(int slot_num)
+{
+ switch (slot_num) {
+ case 0:
+ return KFDE0_KEY;
+ case 1:
+ return KFDE1_KEY;
+ case 2:
+ return KFDE2_KEY;
+ case 3:
+ return KFDE3_KEY;
+ }
+ return END_OF_KEYS;
+}
+
+static int cc_cipher_setkey(struct crypto_skcipher *sktfm, const u8 *key,
+ unsigned int keylen)
+{
+ struct crypto_tfm *tfm = crypto_skcipher_tfm(sktfm);
+ struct cc_cipher_ctx *ctx_p = crypto_tfm_ctx(tfm);
+ struct device *dev = drvdata_to_dev(ctx_p->drvdata);
+ u32 tmp[DES3_EDE_EXPKEY_WORDS];
+ struct cc_crypto_alg *cc_alg =
+ container_of(tfm->__crt_alg, struct cc_crypto_alg,
+ skcipher_alg.base);
+ unsigned int max_key_buf_size = cc_alg->skcipher_alg.max_keysize;
+
+ dev_dbg(dev, "Setting key in context @%p for %s. keylen=%u\n",
+ ctx_p, crypto_tfm_alg_name(tfm), keylen);
+ dump_byte_array("key", (u8 *)key, keylen);
+
+ /* STAT_PHASE_0: Init and sanity checks */
+
+ if (validate_keys_sizes(ctx_p, keylen)) {
+ dev_err(dev, "Unsupported key size %d.\n", keylen);
+ crypto_tfm_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ return -EINVAL;
+ }
+
+ if (cc_is_hw_key(tfm)) {
+ /* setting HW key slots */
+ struct arm_hw_key_info *hki = (struct arm_hw_key_info *)key;
+
+ if (ctx_p->flow_mode != S_DIN_to_AES) {
+ dev_err(dev, "HW key not supported for non-AES flows\n");
+ return -EINVAL;
+ }
+
+ ctx_p->hw.key1_slot = hw_key_to_cc_hw_key(hki->hw_key1);
+ if (ctx_p->hw.key1_slot == END_OF_KEYS) {
+ dev_err(dev, "Unsupported hw key1 number (%d)\n",
+ hki->hw_key1);
+ return -EINVAL;
+ }
+
+ if (ctx_p->cipher_mode == DRV_CIPHER_XTS ||
+ ctx_p->cipher_mode == DRV_CIPHER_ESSIV ||
+ ctx_p->cipher_mode == DRV_CIPHER_BITLOCKER) {
+ if (hki->hw_key1 == hki->hw_key2) {
+ dev_err(dev, "Illegal hw key numbers (%d,%d)\n",
+ hki->hw_key1, hki->hw_key2);
+ return -EINVAL;
+ }
+ ctx_p->hw.key2_slot =
+ hw_key_to_cc_hw_key(hki->hw_key2);
+ if (ctx_p->hw.key2_slot == END_OF_KEYS) {
+ dev_err(dev, "Unsupported hw key2 number (%d)\n",
+ hki->hw_key2);
+ return -EINVAL;
+ }
+ }
+
+ ctx_p->keylen = keylen;
+ dev_dbg(dev, "cc_is_hw_key ret 0");
+
+ return 0;
+ }
+
+ /*
+ * Verify DES weak keys
+ * Note that we're dropping the expanded key since the
+ * HW does the expansion on its own.
+ */
+ if (ctx_p->flow_mode == S_DIN_to_DES) {
+ if (keylen == DES3_EDE_KEY_SIZE &&
+ __des3_ede_setkey(tmp, &tfm->crt_flags, key,
+ DES3_EDE_KEY_SIZE)) {
+ dev_dbg(dev, "weak 3DES key");
+ return -EINVAL;
+ } else if (!des_ekey(tmp, key) &&
+ (crypto_tfm_get_flags(tfm) & CRYPTO_TFM_REQ_WEAK_KEY)) {
+ tfm->crt_flags |= CRYPTO_TFM_RES_WEAK_KEY;
+ dev_dbg(dev, "weak DES key");
+ return -EINVAL;
+ }
+ }
+
+ if (ctx_p->cipher_mode == DRV_CIPHER_XTS &&
+ xts_check_key(tfm, key, keylen)) {
+ dev_dbg(dev, "weak XTS key");
+ return -EINVAL;
+ }
+
+ /* STAT_PHASE_1: Copy key to ctx */
+ dma_sync_single_for_cpu(dev, ctx_p->user.key_dma_addr,
+ max_key_buf_size, DMA_TO_DEVICE);
+
+ memcpy(ctx_p->user.key, key, keylen);
+ if (keylen == 24)
+ memset(ctx_p->user.key + 24, 0, CC_AES_KEY_SIZE_MAX - 24);
+
+ if (ctx_p->cipher_mode == DRV_CIPHER_ESSIV) {
+ /* sha256 for key2 - use sw implementation */
+ int key_len = keylen >> 1;
+ int err;
+
+ SHASH_DESC_ON_STACK(desc, ctx_p->shash_tfm);
+
+ desc->tfm = ctx_p->shash_tfm;
+
+ err = crypto_shash_digest(desc, ctx_p->user.key, key_len,
+ ctx_p->user.key + key_len);
+ if (err) {
+ dev_err(dev, "Failed to hash ESSIV key.\n");
+ return err;
+ }
+ }
+ dma_sync_single_for_device(dev, ctx_p->user.key_dma_addr,
+ max_key_buf_size, DMA_TO_DEVICE);
+ ctx_p->keylen = keylen;
+
+ dev_dbg(dev, "return safely");
+ return 0;
+}
+
+static void cc_setup_cipher_desc(struct crypto_tfm *tfm,
+ struct cipher_req_ctx *req_ctx,
+ unsigned int ivsize, unsigned int nbytes,
+ struct cc_hw_desc desc[],
+ unsigned int *seq_size)
+{
+ struct cc_cipher_ctx *ctx_p = crypto_tfm_ctx(tfm);
+ struct device *dev = drvdata_to_dev(ctx_p->drvdata);
+ int cipher_mode = ctx_p->cipher_mode;
+ int flow_mode = ctx_p->flow_mode;
+ int direction = req_ctx->gen_ctx.op_type;
+ dma_addr_t key_dma_addr = ctx_p->user.key_dma_addr;
+ unsigned int key_len = ctx_p->keylen;
+ dma_addr_t iv_dma_addr = req_ctx->gen_ctx.iv_dma_addr;
+ unsigned int du_size = nbytes;
+
+ struct cc_crypto_alg *cc_alg =
+ container_of(tfm->__crt_alg, struct cc_crypto_alg,
+ skcipher_alg.base);
+
+ if (cc_alg->data_unit)
+ du_size = cc_alg->data_unit;
+
+ switch (cipher_mode) {
+ case DRV_CIPHER_CBC:
+ case DRV_CIPHER_CBC_CTS:
+ case DRV_CIPHER_CTR:
+ case DRV_CIPHER_OFB:
+ /* Load cipher state */
+ hw_desc_init(&desc[*seq_size]);
+ set_din_type(&desc[*seq_size], DMA_DLLI, iv_dma_addr, ivsize,
+ NS_BIT);
+ set_cipher_config0(&desc[*seq_size], direction);
+ set_flow_mode(&desc[*seq_size], flow_mode);
+ set_cipher_mode(&desc[*seq_size], cipher_mode);
+ if (cipher_mode == DRV_CIPHER_CTR ||
+ cipher_mode == DRV_CIPHER_OFB) {
+ set_setup_mode(&desc[*seq_size], SETUP_LOAD_STATE1);
+ } else {
+ set_setup_mode(&desc[*seq_size], SETUP_LOAD_STATE0);
+ }
+ (*seq_size)++;
+ /*FALLTHROUGH*/
+ case DRV_CIPHER_ECB:
+ /* Load key */
+ hw_desc_init(&desc[*seq_size]);
+ set_cipher_mode(&desc[*seq_size], cipher_mode);
+ set_cipher_config0(&desc[*seq_size], direction);
+ if (flow_mode == S_DIN_to_AES) {
+ if (cc_is_hw_key(tfm)) {
+ set_hw_crypto_key(&desc[*seq_size],
+ ctx_p->hw.key1_slot);
+ } else {
+ set_din_type(&desc[*seq_size], DMA_DLLI,
+ key_dma_addr, ((key_len == 24) ?
+ AES_MAX_KEY_SIZE :
+ key_len), NS_BIT);
+ }
+ set_key_size_aes(&desc[*seq_size], key_len);
+ } else {
+ /*des*/
+ set_din_type(&desc[*seq_size], DMA_DLLI, key_dma_addr,
+ key_len, NS_BIT);
+ set_key_size_des(&desc[*seq_size], key_len);
+ }
+ set_flow_mode(&desc[*seq_size], flow_mode);
+ set_setup_mode(&desc[*seq_size], SETUP_LOAD_KEY0);
+ (*seq_size)++;
+ break;
+ case DRV_CIPHER_XTS:
+ case DRV_CIPHER_ESSIV:
+ case DRV_CIPHER_BITLOCKER:
+ /* Load AES key */
+ hw_desc_init(&desc[*seq_size]);
+ set_cipher_mode(&desc[*seq_size], cipher_mode);
+ set_cipher_config0(&desc[*seq_size], direction);
+ if (cc_is_hw_key(tfm)) {
+ set_hw_crypto_key(&desc[*seq_size],
+ ctx_p->hw.key1_slot);
+ } else {
+ set_din_type(&desc[*seq_size], DMA_DLLI, key_dma_addr,
+ (key_len / 2), NS_BIT);
+ }
+ set_key_size_aes(&desc[*seq_size], (key_len / 2));
+ set_flow_mode(&desc[*seq_size], flow_mode);
+ set_setup_mode(&desc[*seq_size], SETUP_LOAD_KEY0);
+ (*seq_size)++;
+
+ /* load XEX key */
+ hw_desc_init(&desc[*seq_size]);
+ set_cipher_mode(&desc[*seq_size], cipher_mode);
+ set_cipher_config0(&desc[*seq_size], direction);
+ if (cc_is_hw_key(tfm)) {
+ set_hw_crypto_key(&desc[*seq_size],
+ ctx_p->hw.key2_slot);
+ } else {
+ set_din_type(&desc[*seq_size], DMA_DLLI,
+ (key_dma_addr + (key_len / 2)),
+ (key_len / 2), NS_BIT);
+ }
+ set_xex_data_unit_size(&desc[*seq_size], du_size);
+ set_flow_mode(&desc[*seq_size], S_DIN_to_AES2);
+ set_key_size_aes(&desc[*seq_size], (key_len / 2));
+ set_setup_mode(&desc[*seq_size], SETUP_LOAD_XEX_KEY);
+ (*seq_size)++;
+
+ /* Set state */
+ hw_desc_init(&desc[*seq_size]);
+ set_setup_mode(&desc[*seq_size], SETUP_LOAD_STATE1);
+ set_cipher_mode(&desc[*seq_size], cipher_mode);
+ set_cipher_config0(&desc[*seq_size], direction);
+ set_key_size_aes(&desc[*seq_size], (key_len / 2));
+ set_flow_mode(&desc[*seq_size], flow_mode);
+ set_din_type(&desc[*seq_size], DMA_DLLI, iv_dma_addr,
+ CC_AES_BLOCK_SIZE, NS_BIT);
+ (*seq_size)++;
+ break;
+ default:
+ dev_err(dev, "Unsupported cipher mode (%d)\n", cipher_mode);
+ }
+}
+
+static void cc_setup_cipher_data(struct crypto_tfm *tfm,
+ struct cipher_req_ctx *req_ctx,
+ struct scatterlist *dst,
+ struct scatterlist *src, unsigned int nbytes,
+ void *areq, struct cc_hw_desc desc[],
+ unsigned int *seq_size)
+{
+ struct cc_cipher_ctx *ctx_p = crypto_tfm_ctx(tfm);
+ struct device *dev = drvdata_to_dev(ctx_p->drvdata);
+ unsigned int flow_mode = ctx_p->flow_mode;
+
+ switch (ctx_p->flow_mode) {
+ case S_DIN_to_AES:
+ flow_mode = DIN_AES_DOUT;
+ break;
+ case S_DIN_to_DES:
+ flow_mode = DIN_DES_DOUT;
+ break;
+ default:
+ dev_err(dev, "invalid flow mode, flow_mode = %d\n", flow_mode);
+ return;
+ }
+ /* Process */
+ if (req_ctx->dma_buf_type == CC_DMA_BUF_DLLI) {
+ dev_dbg(dev, " data params addr %pad length 0x%X\n",
+ &sg_dma_address(src), nbytes);
+ dev_dbg(dev, " data params addr %pad length 0x%X\n",
+ &sg_dma_address(dst), nbytes);
+ hw_desc_init(&desc[*seq_size]);
+ set_din_type(&desc[*seq_size], DMA_DLLI, sg_dma_address(src),
+ nbytes, NS_BIT);
+ set_dout_dlli(&desc[*seq_size], sg_dma_address(dst),
+ nbytes, NS_BIT, (!areq ? 0 : 1));
+ if (areq)
+ set_queue_last_ind(ctx_p->drvdata, &desc[*seq_size]);
+
+ set_flow_mode(&desc[*seq_size], flow_mode);
+ (*seq_size)++;
+ } else {
+ /* bypass */
+ dev_dbg(dev, " bypass params addr %pad length 0x%X addr 0x%08X\n",
+ &req_ctx->mlli_params.mlli_dma_addr,
+ req_ctx->mlli_params.mlli_len,
+ (unsigned int)ctx_p->drvdata->mlli_sram_addr);
+ hw_desc_init(&desc[*seq_size]);
+ set_din_type(&desc[*seq_size], DMA_DLLI,
+ req_ctx->mlli_params.mlli_dma_addr,
+ req_ctx->mlli_params.mlli_len, NS_BIT);
+ set_dout_sram(&desc[*seq_size],
+ ctx_p->drvdata->mlli_sram_addr,
+ req_ctx->mlli_params.mlli_len);
+ set_flow_mode(&desc[*seq_size], BYPASS);
+ (*seq_size)++;
+
+ hw_desc_init(&desc[*seq_size]);
+ set_din_type(&desc[*seq_size], DMA_MLLI,
+ ctx_p->drvdata->mlli_sram_addr,
+ req_ctx->in_mlli_nents, NS_BIT);
+ if (req_ctx->out_nents == 0) {
+ dev_dbg(dev, " din/dout params addr 0x%08X addr 0x%08X\n",
+ (unsigned int)ctx_p->drvdata->mlli_sram_addr,
+ (unsigned int)ctx_p->drvdata->mlli_sram_addr);
+ set_dout_mlli(&desc[*seq_size],
+ ctx_p->drvdata->mlli_sram_addr,
+ req_ctx->in_mlli_nents, NS_BIT,
+ (!areq ? 0 : 1));
+ } else {
+ dev_dbg(dev, " din/dout params addr 0x%08X addr 0x%08X\n",
+ (unsigned int)ctx_p->drvdata->mlli_sram_addr,
+ (unsigned int)ctx_p->drvdata->mlli_sram_addr +
+ (u32)LLI_ENTRY_BYTE_SIZE * req_ctx->in_nents);
+ set_dout_mlli(&desc[*seq_size],
+ (ctx_p->drvdata->mlli_sram_addr +
+ (LLI_ENTRY_BYTE_SIZE *
+ req_ctx->in_mlli_nents)),
+ req_ctx->out_mlli_nents, NS_BIT,
+ (!areq ? 0 : 1));
+ }
+ if (areq)
+ set_queue_last_ind(ctx_p->drvdata, &desc[*seq_size]);
+
+ set_flow_mode(&desc[*seq_size], flow_mode);
+ (*seq_size)++;
+ }
+}
+
+static void cc_cipher_complete(struct device *dev, void *cc_req, int err)
+{
+ struct skcipher_request *req = (struct skcipher_request *)cc_req;
+ struct scatterlist *dst = req->dst;
+ struct scatterlist *src = req->src;
+ struct cipher_req_ctx *req_ctx = skcipher_request_ctx(req);
+ struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
+ unsigned int ivsize = crypto_skcipher_ivsize(tfm);
+
+ cc_unmap_cipher_request(dev, req_ctx, ivsize, src, dst);
+ kzfree(req_ctx->iv);
+
+ /*
+ * The crypto API expects us to set the req->iv to the last
+ * ciphertext block. For encrypt, simply copy from the result.
+ * For decrypt, we must copy from a saved buffer since this
+ * could be an in-place decryption operation and the src is
+ * lost by this point.
+ */
+ if (req_ctx->gen_ctx.op_type == DRV_CRYPTO_DIRECTION_DECRYPT) {
+ memcpy(req->iv, req_ctx->backup_info, ivsize);
+ kzfree(req_ctx->backup_info);
+ } else if (!err) {
+ scatterwalk_map_and_copy(req->iv, req->dst,
+ (req->cryptlen - ivsize),
+ ivsize, 0);
+ }
+
+ skcipher_request_complete(req, err);
+}
+
+static int cc_cipher_process(struct skcipher_request *req,
+ enum drv_crypto_direction direction)
+{
+ struct crypto_skcipher *sk_tfm = crypto_skcipher_reqtfm(req);
+ struct crypto_tfm *tfm = crypto_skcipher_tfm(sk_tfm);
+ struct cipher_req_ctx *req_ctx = skcipher_request_ctx(req);
+ unsigned int ivsize = crypto_skcipher_ivsize(sk_tfm);
+ struct scatterlist *dst = req->dst;
+ struct scatterlist *src = req->src;
+ unsigned int nbytes = req->cryptlen;
+ void *iv = req->iv;
+ struct cc_cipher_ctx *ctx_p = crypto_tfm_ctx(tfm);
+ struct device *dev = drvdata_to_dev(ctx_p->drvdata);
+ struct cc_hw_desc desc[MAX_ABLKCIPHER_SEQ_LEN];
+ struct cc_crypto_req cc_req = {};
+ int rc, cts_restore_flag = 0;
+ unsigned int seq_len = 0;
+ gfp_t flags = cc_gfp_flags(&req->base);
+
+ dev_dbg(dev, "%s req=%p iv=%p nbytes=%d\n",
+ ((direction == DRV_CRYPTO_DIRECTION_ENCRYPT) ?
+ "Encrypt" : "Decrypt"), req, iv, nbytes);
+
+ /* STAT_PHASE_0: Init and sanity checks */
+
+ /* TODO: check data length according to mode */
+ if (validate_data_size(ctx_p, nbytes)) {
+ dev_err(dev, "Unsupported data size %d.\n", nbytes);
+ crypto_tfm_set_flags(tfm, CRYPTO_TFM_RES_BAD_BLOCK_LEN);
+ rc = -EINVAL;
+ goto exit_process;
+ }
+ if (nbytes == 0) {
+ /* No data to process is valid */
+ rc = 0;
+ goto exit_process;
+ }
+
+ /* The IV we are handed may be allocted from the stack so
+ * we must copy it to a DMAable buffer before use.
+ */
+ req_ctx->iv = kmemdup(iv, ivsize, flags);
+ if (!req_ctx->iv) {
+ rc = -ENOMEM;
+ goto exit_process;
+ }
+
+ /*For CTS in case of data size aligned to 16 use CBC mode*/
+ if (((nbytes % AES_BLOCK_SIZE) == 0) &&
+ ctx_p->cipher_mode == DRV_CIPHER_CBC_CTS) {
+ ctx_p->cipher_mode = DRV_CIPHER_CBC;
+ cts_restore_flag = 1;
+ }
+
+ /* Setup request structure */
+ cc_req.user_cb = (void *)cc_cipher_complete;
+ cc_req.user_arg = (void *)req;
+
+#ifdef ENABLE_CYCLE_COUNT
+ cc_req.op_type = (direction == DRV_CRYPTO_DIRECTION_DECRYPT) ?
+ STAT_OP_TYPE_DECODE : STAT_OP_TYPE_ENCODE;
+
+#endif
+
+ /* Setup request context */
+ req_ctx->gen_ctx.op_type = direction;
+
+ /* STAT_PHASE_1: Map buffers */
+
+ rc = cc_map_cipher_request(ctx_p->drvdata, req_ctx, ivsize, nbytes,
+ req_ctx->iv, src, dst, flags);
+ if (rc) {
+ dev_err(dev, "map_request() failed\n");
+ goto exit_process;
+ }
+
+ /* STAT_PHASE_2: Create sequence */
+
+ /* Setup processing */
+ cc_setup_cipher_desc(tfm, req_ctx, ivsize, nbytes, desc, &seq_len);
+ /* Data processing */
+ cc_setup_cipher_data(tfm, req_ctx, dst, src, nbytes, req, desc,
+ &seq_len);
+
+ /* do we need to generate IV? */
+ if (req_ctx->is_giv) {
+ cc_req.ivgen_dma_addr[0] = req_ctx->gen_ctx.iv_dma_addr;
+ cc_req.ivgen_dma_addr_len = 1;
+ /* set the IV size (8/16 B long)*/
+ cc_req.ivgen_size = ivsize;
+ }
+
+ /* STAT_PHASE_3: Lock HW and push sequence */
+
+ rc = cc_send_request(ctx_p->drvdata, &cc_req, desc, seq_len,
+ &req->base);
+ if (rc != -EINPROGRESS && rc != -EBUSY) {
+ /* Failed to send the request or request completed
+ * synchronously
+ */
+ cc_unmap_cipher_request(dev, req_ctx, ivsize, src, dst);
+ }
+
+exit_process:
+ if (cts_restore_flag)
+ ctx_p->cipher_mode = DRV_CIPHER_CBC_CTS;
+
+ if (rc != -EINPROGRESS && rc != -EBUSY) {
+ kzfree(req_ctx->backup_info);
+ kzfree(req_ctx->iv);
+ }
+
+ return rc;
+}
+
+static int cc_cipher_encrypt(struct skcipher_request *req)
+{
+ struct cipher_req_ctx *req_ctx = skcipher_request_ctx(req);
+
+ req_ctx->is_giv = false;
+ req_ctx->backup_info = NULL;
+
+ return cc_cipher_process(req, DRV_CRYPTO_DIRECTION_ENCRYPT);
+}
+
+static int cc_cipher_decrypt(struct skcipher_request *req)
+{
+ struct crypto_skcipher *sk_tfm = crypto_skcipher_reqtfm(req);
+ struct cipher_req_ctx *req_ctx = skcipher_request_ctx(req);
+ unsigned int ivsize = crypto_skcipher_ivsize(sk_tfm);
+ gfp_t flags = cc_gfp_flags(&req->base);
+
+ /*
+ * Allocate and save the last IV sized bytes of the source, which will
+ * be lost in case of in-place decryption and might be needed for CTS.
+ */
+ req_ctx->backup_info = kmalloc(ivsize, flags);
+ if (!req_ctx->backup_info)
+ return -ENOMEM;
+
+ scatterwalk_map_and_copy(req_ctx->backup_info, req->src,
+ (req->cryptlen - ivsize), ivsize, 0);
+ req_ctx->is_giv = false;
+
+ return cc_cipher_process(req, DRV_CRYPTO_DIRECTION_DECRYPT);
+}
+
+/* Block cipher alg */
+static const struct cc_alg_template skcipher_algs[] = {
+ {
+ .name = "xts(aes)",
+ .driver_name = "xts-aes-ccree",
+ .blocksize = AES_BLOCK_SIZE,
+ .template_skcipher = {
+ .setkey = cc_cipher_setkey,
+ .encrypt = cc_cipher_encrypt,
+ .decrypt = cc_cipher_decrypt,
+ .min_keysize = AES_MIN_KEY_SIZE * 2,
+ .max_keysize = AES_MAX_KEY_SIZE * 2,
+ .ivsize = AES_BLOCK_SIZE,
+ },
+ .cipher_mode = DRV_CIPHER_XTS,
+ .flow_mode = S_DIN_to_AES,
+ .min_hw_rev = CC_HW_REV_630,
+ },
+ {
+ .name = "xts512(aes)",
+ .driver_name = "xts-aes-du512-ccree",
+ .blocksize = AES_BLOCK_SIZE,
+ .template_skcipher = {
+ .setkey = cc_cipher_setkey,
+ .encrypt = cc_cipher_encrypt,
+ .decrypt = cc_cipher_decrypt,
+ .min_keysize = AES_MIN_KEY_SIZE * 2,
+ .max_keysize = AES_MAX_KEY_SIZE * 2,
+ .ivsize = AES_BLOCK_SIZE,
+ },
+ .cipher_mode = DRV_CIPHER_XTS,
+ .flow_mode = S_DIN_to_AES,
+ .data_unit = 512,
+ .min_hw_rev = CC_HW_REV_712,
+ },
+ {
+ .name = "xts4096(aes)",
+ .driver_name = "xts-aes-du4096-ccree",
+ .blocksize = AES_BLOCK_SIZE,
+ .template_skcipher = {
+ .setkey = cc_cipher_setkey,
+ .encrypt = cc_cipher_encrypt,
+ .decrypt = cc_cipher_decrypt,
+ .min_keysize = AES_MIN_KEY_SIZE * 2,
+ .max_keysize = AES_MAX_KEY_SIZE * 2,
+ .ivsize = AES_BLOCK_SIZE,
+ },
+ .cipher_mode = DRV_CIPHER_XTS,
+ .flow_mode = S_DIN_to_AES,
+ .data_unit = 4096,
+ .min_hw_rev = CC_HW_REV_712,
+ },
+ {
+ .name = "essiv(aes)",
+ .driver_name = "essiv-aes-ccree",
+ .blocksize = AES_BLOCK_SIZE,
+ .template_skcipher = {
+ .setkey = cc_cipher_setkey,
+ .encrypt = cc_cipher_encrypt,
+ .decrypt = cc_cipher_decrypt,
+ .min_keysize = AES_MIN_KEY_SIZE * 2,
+ .max_keysize = AES_MAX_KEY_SIZE * 2,
+ .ivsize = AES_BLOCK_SIZE,
+ },
+ .cipher_mode = DRV_CIPHER_ESSIV,
+ .flow_mode = S_DIN_to_AES,
+ .min_hw_rev = CC_HW_REV_712,
+ },
+ {
+ .name = "essiv512(aes)",
+ .driver_name = "essiv-aes-du512-ccree",
+ .blocksize = AES_BLOCK_SIZE,
+ .template_skcipher = {
+ .setkey = cc_cipher_setkey,
+ .encrypt = cc_cipher_encrypt,
+ .decrypt = cc_cipher_decrypt,
+ .min_keysize = AES_MIN_KEY_SIZE * 2,
+ .max_keysize = AES_MAX_KEY_SIZE * 2,
+ .ivsize = AES_BLOCK_SIZE,
+ },
+ .cipher_mode = DRV_CIPHER_ESSIV,
+ .flow_mode = S_DIN_to_AES,
+ .data_unit = 512,
+ .min_hw_rev = CC_HW_REV_712,
+ },
+ {
+ .name = "essiv4096(aes)",
+ .driver_name = "essiv-aes-du4096-ccree",
+ .blocksize = AES_BLOCK_SIZE,
+ .template_skcipher = {
+ .setkey = cc_cipher_setkey,
+ .encrypt = cc_cipher_encrypt,
+ .decrypt = cc_cipher_decrypt,
+ .min_keysize = AES_MIN_KEY_SIZE * 2,
+ .max_keysize = AES_MAX_KEY_SIZE * 2,
+ .ivsize = AES_BLOCK_SIZE,
+ },
+ .cipher_mode = DRV_CIPHER_ESSIV,
+ .flow_mode = S_DIN_to_AES,
+ .data_unit = 4096,
+ .min_hw_rev = CC_HW_REV_712,
+ },
+ {
+ .name = "bitlocker(aes)",
+ .driver_name = "bitlocker-aes-ccree",
+ .blocksize = AES_BLOCK_SIZE,
+ .template_skcipher = {
+ .setkey = cc_cipher_setkey,
+ .encrypt = cc_cipher_encrypt,
+ .decrypt = cc_cipher_decrypt,
+ .min_keysize = AES_MIN_KEY_SIZE * 2,
+ .max_keysize = AES_MAX_KEY_SIZE * 2,
+ .ivsize = AES_BLOCK_SIZE,
+ },
+ .cipher_mode = DRV_CIPHER_BITLOCKER,
+ .flow_mode = S_DIN_to_AES,
+ .min_hw_rev = CC_HW_REV_712,
+ },
+ {
+ .name = "bitlocker512(aes)",
+ .driver_name = "bitlocker-aes-du512-ccree",
+ .blocksize = AES_BLOCK_SIZE,
+ .template_skcipher = {
+ .setkey = cc_cipher_setkey,
+ .encrypt = cc_cipher_encrypt,
+ .decrypt = cc_cipher_decrypt,
+ .min_keysize = AES_MIN_KEY_SIZE * 2,
+ .max_keysize = AES_MAX_KEY_SIZE * 2,
+ .ivsize = AES_BLOCK_SIZE,
+ },
+ .cipher_mode = DRV_CIPHER_BITLOCKER,
+ .flow_mode = S_DIN_to_AES,
+ .data_unit = 512,
+ .min_hw_rev = CC_HW_REV_712,
+ },
+ {
+ .name = "bitlocker4096(aes)",
+ .driver_name = "bitlocker-aes-du4096-ccree",
+ .blocksize = AES_BLOCK_SIZE,
+ .template_skcipher = {
+ .setkey = cc_cipher_setkey,
+ .encrypt = cc_cipher_encrypt,
+ .decrypt = cc_cipher_decrypt,
+ .min_keysize = AES_MIN_KEY_SIZE * 2,
+ .max_keysize = AES_MAX_KEY_SIZE * 2,
+ .ivsize = AES_BLOCK_SIZE,
+ },
+ .cipher_mode = DRV_CIPHER_BITLOCKER,
+ .flow_mode = S_DIN_to_AES,
+ .data_unit = 4096,
+ .min_hw_rev = CC_HW_REV_712,
+ },
+ {
+ .name = "ecb(aes)",
+ .driver_name = "ecb-aes-ccree",
+ .blocksize = AES_BLOCK_SIZE,
+ .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .template_skcipher = {
+ .setkey = cc_cipher_setkey,
+ .encrypt = cc_cipher_encrypt,
+ .decrypt = cc_cipher_decrypt,
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .ivsize = 0,
+ },
+ .cipher_mode = DRV_CIPHER_ECB,
+ .flow_mode = S_DIN_to_AES,
+ .min_hw_rev = CC_HW_REV_630,
+ },
+ {
+ .name = "cbc(aes)",
+ .driver_name = "cbc-aes-ccree",
+ .blocksize = AES_BLOCK_SIZE,
+ .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .template_skcipher = {
+ .setkey = cc_cipher_setkey,
+ .encrypt = cc_cipher_encrypt,
+ .decrypt = cc_cipher_decrypt,
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .ivsize = AES_BLOCK_SIZE,
+ },
+ .cipher_mode = DRV_CIPHER_CBC,
+ .flow_mode = S_DIN_to_AES,
+ .min_hw_rev = CC_HW_REV_630,
+ },
+ {
+ .name = "ofb(aes)",
+ .driver_name = "ofb-aes-ccree",
+ .blocksize = AES_BLOCK_SIZE,
+ .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .template_skcipher = {
+ .setkey = cc_cipher_setkey,
+ .encrypt = cc_cipher_encrypt,
+ .decrypt = cc_cipher_decrypt,
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .ivsize = AES_BLOCK_SIZE,
+ },
+ .cipher_mode = DRV_CIPHER_OFB,
+ .flow_mode = S_DIN_to_AES,
+ .min_hw_rev = CC_HW_REV_630,
+ },
+ {
+ .name = "cts1(cbc(aes))",
+ .driver_name = "cts1-cbc-aes-ccree",
+ .blocksize = AES_BLOCK_SIZE,
+ .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .template_skcipher = {
+ .setkey = cc_cipher_setkey,
+ .encrypt = cc_cipher_encrypt,
+ .decrypt = cc_cipher_decrypt,
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .ivsize = AES_BLOCK_SIZE,
+ },
+ .cipher_mode = DRV_CIPHER_CBC_CTS,
+ .flow_mode = S_DIN_to_AES,
+ .min_hw_rev = CC_HW_REV_630,
+ },
+ {
+ .name = "ctr(aes)",
+ .driver_name = "ctr-aes-ccree",
+ .blocksize = 1,
+ .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .template_skcipher = {
+ .setkey = cc_cipher_setkey,
+ .encrypt = cc_cipher_encrypt,
+ .decrypt = cc_cipher_decrypt,
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .ivsize = AES_BLOCK_SIZE,
+ },
+ .cipher_mode = DRV_CIPHER_CTR,
+ .flow_mode = S_DIN_to_AES,
+ .min_hw_rev = CC_HW_REV_630,
+ },
+ {
+ .name = "cbc(des3_ede)",
+ .driver_name = "cbc-3des-ccree",
+ .blocksize = DES3_EDE_BLOCK_SIZE,
+ .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .template_skcipher = {
+ .setkey = cc_cipher_setkey,
+ .encrypt = cc_cipher_encrypt,
+ .decrypt = cc_cipher_decrypt,
+ .min_keysize = DES3_EDE_KEY_SIZE,
+ .max_keysize = DES3_EDE_KEY_SIZE,
+ .ivsize = DES3_EDE_BLOCK_SIZE,
+ },
+ .cipher_mode = DRV_CIPHER_CBC,
+ .flow_mode = S_DIN_to_DES,
+ .min_hw_rev = CC_HW_REV_630,
+ },
+ {
+ .name = "ecb(des3_ede)",
+ .driver_name = "ecb-3des-ccree",
+ .blocksize = DES3_EDE_BLOCK_SIZE,
+ .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .template_skcipher = {
+ .setkey = cc_cipher_setkey,
+ .encrypt = cc_cipher_encrypt,
+ .decrypt = cc_cipher_decrypt,
+ .min_keysize = DES3_EDE_KEY_SIZE,
+ .max_keysize = DES3_EDE_KEY_SIZE,
+ .ivsize = 0,
+ },
+ .cipher_mode = DRV_CIPHER_ECB,
+ .flow_mode = S_DIN_to_DES,
+ .min_hw_rev = CC_HW_REV_630,
+ },
+ {
+ .name = "cbc(des)",
+ .driver_name = "cbc-des-ccree",
+ .blocksize = DES_BLOCK_SIZE,
+ .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .template_skcipher = {
+ .setkey = cc_cipher_setkey,
+ .encrypt = cc_cipher_encrypt,
+ .decrypt = cc_cipher_decrypt,
+ .min_keysize = DES_KEY_SIZE,
+ .max_keysize = DES_KEY_SIZE,
+ .ivsize = DES_BLOCK_SIZE,
+ },
+ .cipher_mode = DRV_CIPHER_CBC,
+ .flow_mode = S_DIN_to_DES,
+ .min_hw_rev = CC_HW_REV_630,
+ },
+ {
+ .name = "ecb(des)",
+ .driver_name = "ecb-des-ccree",
+ .blocksize = DES_BLOCK_SIZE,
+ .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .template_skcipher = {
+ .setkey = cc_cipher_setkey,
+ .encrypt = cc_cipher_encrypt,
+ .decrypt = cc_cipher_decrypt,
+ .min_keysize = DES_KEY_SIZE,
+ .max_keysize = DES_KEY_SIZE,
+ .ivsize = 0,
+ },
+ .cipher_mode = DRV_CIPHER_ECB,
+ .flow_mode = S_DIN_to_DES,
+ .min_hw_rev = CC_HW_REV_630,
+ },
+};
+
+static struct cc_crypto_alg *cc_create_alg(const struct cc_alg_template *tmpl,
+ struct device *dev)
+{
+ struct cc_crypto_alg *t_alg;
+ struct skcipher_alg *alg;
+
+ t_alg = kzalloc(sizeof(*t_alg), GFP_KERNEL);
+ if (!t_alg)
+ return ERR_PTR(-ENOMEM);
+
+ alg = &t_alg->skcipher_alg;
+
+ memcpy(alg, &tmpl->template_skcipher, sizeof(*alg));
+
+ snprintf(alg->base.cra_name, CRYPTO_MAX_ALG_NAME, "%s", tmpl->name);
+ snprintf(alg->base.cra_driver_name, CRYPTO_MAX_ALG_NAME, "%s",
+ tmpl->driver_name);
+ alg->base.cra_module = THIS_MODULE;
+ alg->base.cra_priority = CC_CRA_PRIO;
+ alg->base.cra_blocksize = tmpl->blocksize;
+ alg->base.cra_alignmask = 0;
+ alg->base.cra_ctxsize = sizeof(struct cc_cipher_ctx);
+
+ alg->base.cra_init = cc_cipher_init;
+ alg->base.cra_exit = cc_cipher_exit;
+ alg->base.cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_KERN_DRIVER_ONLY |
+ CRYPTO_ALG_TYPE_SKCIPHER;
+
+ t_alg->cipher_mode = tmpl->cipher_mode;
+ t_alg->flow_mode = tmpl->flow_mode;
+ t_alg->data_unit = tmpl->data_unit;
+
+ return t_alg;
+}
+
+int cc_cipher_free(struct cc_drvdata *drvdata)
+{
+ struct cc_crypto_alg *t_alg, *n;
+ struct cc_cipher_handle *cipher_handle = drvdata->cipher_handle;
+
+ if (cipher_handle) {
+ /* Remove registered algs */
+ list_for_each_entry_safe(t_alg, n, &cipher_handle->alg_list,
+ entry) {
+ crypto_unregister_skcipher(&t_alg->skcipher_alg);
+ list_del(&t_alg->entry);
+ kfree(t_alg);
+ }
+ kfree(cipher_handle);
+ drvdata->cipher_handle = NULL;
+ }
+ return 0;
+}
+
+int cc_cipher_alloc(struct cc_drvdata *drvdata)
+{
+ struct cc_cipher_handle *cipher_handle;
+ struct cc_crypto_alg *t_alg;
+ struct device *dev = drvdata_to_dev(drvdata);
+ int rc = -ENOMEM;
+ int alg;
+
+ cipher_handle = kmalloc(sizeof(*cipher_handle), GFP_KERNEL);
+ if (!cipher_handle)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&cipher_handle->alg_list);
+ drvdata->cipher_handle = cipher_handle;
+
+ /* Linux crypto */
+ dev_dbg(dev, "Number of algorithms = %zu\n",
+ ARRAY_SIZE(skcipher_algs));
+ for (alg = 0; alg < ARRAY_SIZE(skcipher_algs); alg++) {
+ if (skcipher_algs[alg].min_hw_rev > drvdata->hw_rev)
+ continue;
+
+ dev_dbg(dev, "creating %s\n", skcipher_algs[alg].driver_name);
+ t_alg = cc_create_alg(&skcipher_algs[alg], dev);
+ if (IS_ERR(t_alg)) {
+ rc = PTR_ERR(t_alg);
+ dev_err(dev, "%s alg allocation failed\n",
+ skcipher_algs[alg].driver_name);
+ goto fail0;
+ }
+ t_alg->drvdata = drvdata;
+
+ dev_dbg(dev, "registering %s\n",
+ skcipher_algs[alg].driver_name);
+ rc = crypto_register_skcipher(&t_alg->skcipher_alg);
+ dev_dbg(dev, "%s alg registration rc = %x\n",
+ t_alg->skcipher_alg.base.cra_driver_name, rc);
+ if (rc) {
+ dev_err(dev, "%s alg registration failed\n",
+ t_alg->skcipher_alg.base.cra_driver_name);
+ kfree(t_alg);
+ goto fail0;
+ } else {
+ list_add_tail(&t_alg->entry,
+ &cipher_handle->alg_list);
+ dev_dbg(dev, "Registered %s\n",
+ t_alg->skcipher_alg.base.cra_driver_name);
+ }
+ }
+ return 0;
+
+fail0:
+ cc_cipher_free(drvdata);
+ return rc;
+}
diff --git a/drivers/crypto/ccree/cc_cipher.h b/drivers/crypto/ccree/cc_cipher.h
new file mode 100644
index 000000000000..2a2a6f46c515
--- /dev/null
+++ b/drivers/crypto/ccree/cc_cipher.h
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
+
+/* \file cc_cipher.h
+ * ARM CryptoCell Cipher Crypto API
+ */
+
+#ifndef __CC_CIPHER_H__
+#define __CC_CIPHER_H__
+
+#include <linux/kernel.h>
+#include <crypto/algapi.h>
+#include "cc_driver.h"
+#include "cc_buffer_mgr.h"
+
+/* Crypto cipher flags */
+#define CC_CRYPTO_CIPHER_KEY_KFDE0 BIT(0)
+#define CC_CRYPTO_CIPHER_KEY_KFDE1 BIT(1)
+#define CC_CRYPTO_CIPHER_KEY_KFDE2 BIT(2)
+#define CC_CRYPTO_CIPHER_KEY_KFDE3 BIT(3)
+#define CC_CRYPTO_CIPHER_DU_SIZE_512B BIT(4)
+
+#define CC_CRYPTO_CIPHER_KEY_KFDE_MASK (CC_CRYPTO_CIPHER_KEY_KFDE0 | \
+ CC_CRYPTO_CIPHER_KEY_KFDE1 | \
+ CC_CRYPTO_CIPHER_KEY_KFDE2 | \
+ CC_CRYPTO_CIPHER_KEY_KFDE3)
+
+struct cipher_req_ctx {
+ struct async_gen_req_ctx gen_ctx;
+ enum cc_req_dma_buf_type dma_buf_type;
+ u32 in_nents;
+ u32 in_mlli_nents;
+ u32 out_nents;
+ u32 out_mlli_nents;
+ u8 *backup_info; /*store iv for generated IV flow*/
+ u8 *iv;
+ bool is_giv;
+ struct mlli_params mlli_params;
+};
+
+int cc_cipher_alloc(struct cc_drvdata *drvdata);
+
+int cc_cipher_free(struct cc_drvdata *drvdata);
+
+struct arm_hw_key_info {
+ int hw_key1;
+ int hw_key2;
+};
+
+/*
+ * This is a stub function that will replaced when we
+ * implement secure keys
+ */
+static inline bool cc_is_hw_key(struct crypto_tfm *tfm)
+{
+ return false;
+}
+
+#endif /*__CC_CIPHER_H__*/
diff --git a/drivers/crypto/ccree/cc_crypto_ctx.h b/drivers/crypto/ccree/cc_crypto_ctx.h
new file mode 100644
index 000000000000..e032544f4e31
--- /dev/null
+++ b/drivers/crypto/ccree/cc_crypto_ctx.h
@@ -0,0 +1,133 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
+
+#ifndef _CC_CRYPTO_CTX_H_
+#define _CC_CRYPTO_CTX_H_
+
+#include <linux/types.h>
+
+#define CC_DRV_DES_IV_SIZE 8
+#define CC_DRV_DES_BLOCK_SIZE 8
+
+#define CC_DRV_DES_ONE_KEY_SIZE 8
+#define CC_DRV_DES_DOUBLE_KEY_SIZE 16
+#define CC_DRV_DES_TRIPLE_KEY_SIZE 24
+#define CC_DRV_DES_KEY_SIZE_MAX CC_DRV_DES_TRIPLE_KEY_SIZE
+
+#define CC_AES_IV_SIZE 16
+#define CC_AES_IV_SIZE_WORDS (CC_AES_IV_SIZE >> 2)
+
+#define CC_AES_BLOCK_SIZE 16
+#define CC_AES_BLOCK_SIZE_WORDS 4
+
+#define CC_AES_128_BIT_KEY_SIZE 16
+#define CC_AES_128_BIT_KEY_SIZE_WORDS (CC_AES_128_BIT_KEY_SIZE >> 2)
+#define CC_AES_192_BIT_KEY_SIZE 24
+#define CC_AES_192_BIT_KEY_SIZE_WORDS (CC_AES_192_BIT_KEY_SIZE >> 2)
+#define CC_AES_256_BIT_KEY_SIZE 32
+#define CC_AES_256_BIT_KEY_SIZE_WORDS (CC_AES_256_BIT_KEY_SIZE >> 2)
+#define CC_AES_KEY_SIZE_MAX CC_AES_256_BIT_KEY_SIZE
+#define CC_AES_KEY_SIZE_WORDS_MAX (CC_AES_KEY_SIZE_MAX >> 2)
+
+#define CC_MD5_DIGEST_SIZE 16
+#define CC_SHA1_DIGEST_SIZE 20
+#define CC_SHA224_DIGEST_SIZE 28
+#define CC_SHA256_DIGEST_SIZE 32
+#define CC_SHA256_DIGEST_SIZE_IN_WORDS 8
+#define CC_SHA384_DIGEST_SIZE 48
+#define CC_SHA512_DIGEST_SIZE 64
+
+#define CC_SHA1_BLOCK_SIZE 64
+#define CC_SHA1_BLOCK_SIZE_IN_WORDS 16
+#define CC_MD5_BLOCK_SIZE 64
+#define CC_MD5_BLOCK_SIZE_IN_WORDS 16
+#define CC_SHA224_BLOCK_SIZE 64
+#define CC_SHA256_BLOCK_SIZE 64
+#define CC_SHA256_BLOCK_SIZE_IN_WORDS 16
+#define CC_SHA1_224_256_BLOCK_SIZE 64
+#define CC_SHA384_BLOCK_SIZE 128
+#define CC_SHA512_BLOCK_SIZE 128
+
+#define CC_DIGEST_SIZE_MAX CC_SHA512_DIGEST_SIZE
+#define CC_HASH_BLOCK_SIZE_MAX CC_SHA512_BLOCK_SIZE /*1024b*/
+
+#define CC_HMAC_BLOCK_SIZE_MAX CC_HASH_BLOCK_SIZE_MAX
+
+#define CC_DRV_ALG_MAX_BLOCK_SIZE CC_HASH_BLOCK_SIZE_MAX
+
+enum drv_engine_type {
+ DRV_ENGINE_NULL = 0,
+ DRV_ENGINE_AES = 1,
+ DRV_ENGINE_DES = 2,
+ DRV_ENGINE_HASH = 3,
+ DRV_ENGINE_RC4 = 4,
+ DRV_ENGINE_DOUT = 5,
+ DRV_ENGINE_RESERVE32B = S32_MAX,
+};
+
+enum drv_crypto_alg {
+ DRV_CRYPTO_ALG_NULL = -1,
+ DRV_CRYPTO_ALG_AES = 0,
+ DRV_CRYPTO_ALG_DES = 1,
+ DRV_CRYPTO_ALG_HASH = 2,
+ DRV_CRYPTO_ALG_C2 = 3,
+ DRV_CRYPTO_ALG_HMAC = 4,
+ DRV_CRYPTO_ALG_AEAD = 5,
+ DRV_CRYPTO_ALG_BYPASS = 6,
+ DRV_CRYPTO_ALG_NUM = 7,
+ DRV_CRYPTO_ALG_RESERVE32B = S32_MAX
+};
+
+enum drv_crypto_direction {
+ DRV_CRYPTO_DIRECTION_NULL = -1,
+ DRV_CRYPTO_DIRECTION_ENCRYPT = 0,
+ DRV_CRYPTO_DIRECTION_DECRYPT = 1,
+ DRV_CRYPTO_DIRECTION_DECRYPT_ENCRYPT = 3,
+ DRV_CRYPTO_DIRECTION_RESERVE32B = S32_MAX
+};
+
+enum drv_cipher_mode {
+ DRV_CIPHER_NULL_MODE = -1,
+ DRV_CIPHER_ECB = 0,
+ DRV_CIPHER_CBC = 1,
+ DRV_CIPHER_CTR = 2,
+ DRV_CIPHER_CBC_MAC = 3,
+ DRV_CIPHER_XTS = 4,
+ DRV_CIPHER_XCBC_MAC = 5,
+ DRV_CIPHER_OFB = 6,
+ DRV_CIPHER_CMAC = 7,
+ DRV_CIPHER_CCM = 8,
+ DRV_CIPHER_CBC_CTS = 11,
+ DRV_CIPHER_GCTR = 12,
+ DRV_CIPHER_ESSIV = 13,
+ DRV_CIPHER_BITLOCKER = 14,
+ DRV_CIPHER_RESERVE32B = S32_MAX
+};
+
+enum drv_hash_mode {
+ DRV_HASH_NULL = -1,
+ DRV_HASH_SHA1 = 0,
+ DRV_HASH_SHA256 = 1,
+ DRV_HASH_SHA224 = 2,
+ DRV_HASH_SHA512 = 3,
+ DRV_HASH_SHA384 = 4,
+ DRV_HASH_MD5 = 5,
+ DRV_HASH_CBC_MAC = 6,
+ DRV_HASH_XCBC_MAC = 7,
+ DRV_HASH_CMAC = 8,
+ DRV_HASH_MODE_NUM = 9,
+ DRV_HASH_RESERVE32B = S32_MAX
+};
+
+enum drv_hash_hw_mode {
+ DRV_HASH_HW_MD5 = 0,
+ DRV_HASH_HW_SHA1 = 1,
+ DRV_HASH_HW_SHA256 = 2,
+ DRV_HASH_HW_SHA224 = 10,
+ DRV_HASH_HW_SHA512 = 4,
+ DRV_HASH_HW_SHA384 = 12,
+ DRV_HASH_HW_GHASH = 6,
+ DRV_HASH_HW_RESERVE32B = S32_MAX
+};
+
+#endif /* _CC_CRYPTO_CTX_H_ */
diff --git a/drivers/staging/ccree/cc_debugfs.c b/drivers/crypto/ccree/cc_debugfs.c
index 08f8db489cf0..08f8db489cf0 100644
--- a/drivers/staging/ccree/cc_debugfs.c
+++ b/drivers/crypto/ccree/cc_debugfs.c
diff --git a/drivers/staging/ccree/cc_debugfs.h b/drivers/crypto/ccree/cc_debugfs.h
index 5b5320eca7d2..5b5320eca7d2 100644
--- a/drivers/staging/ccree/cc_debugfs.h
+++ b/drivers/crypto/ccree/cc_debugfs.h
diff --git a/drivers/crypto/ccree/cc_driver.c b/drivers/crypto/ccree/cc_driver.c
new file mode 100644
index 000000000000..89ce013ae093
--- /dev/null
+++ b/drivers/crypto/ccree/cc_driver.c
@@ -0,0 +1,518 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include <linux/crypto.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/of_address.h>
+
+#include "cc_driver.h"
+#include "cc_request_mgr.h"
+#include "cc_buffer_mgr.h"
+#include "cc_debugfs.h"
+#include "cc_cipher.h"
+#include "cc_aead.h"
+#include "cc_hash.h"
+#include "cc_ivgen.h"
+#include "cc_sram_mgr.h"
+#include "cc_pm.h"
+#include "cc_fips.h"
+
+bool cc_dump_desc;
+module_param_named(dump_desc, cc_dump_desc, bool, 0600);
+MODULE_PARM_DESC(cc_dump_desc, "Dump descriptors to kernel log as debugging aid");
+
+bool cc_dump_bytes;
+module_param_named(dump_bytes, cc_dump_bytes, bool, 0600);
+MODULE_PARM_DESC(cc_dump_bytes, "Dump buffers to kernel log as debugging aid");
+
+struct cc_hw_data {
+ char *name;
+ enum cc_hw_rev rev;
+ u32 sig;
+};
+
+/* Hardware revisions defs. */
+
+static const struct cc_hw_data cc712_hw = {
+ .name = "712", .rev = CC_HW_REV_712, .sig = 0xDCC71200U
+};
+
+static const struct cc_hw_data cc710_hw = {
+ .name = "710", .rev = CC_HW_REV_710, .sig = 0xDCC63200U
+};
+
+static const struct cc_hw_data cc630p_hw = {
+ .name = "630P", .rev = CC_HW_REV_630, .sig = 0xDCC63000U
+};
+
+static const struct of_device_id arm_ccree_dev_of_match[] = {
+ { .compatible = "arm,cryptocell-712-ree", .data = &cc712_hw },
+ { .compatible = "arm,cryptocell-710-ree", .data = &cc710_hw },
+ { .compatible = "arm,cryptocell-630p-ree", .data = &cc630p_hw },
+ {}
+};
+MODULE_DEVICE_TABLE(of, arm_ccree_dev_of_match);
+
+void __dump_byte_array(const char *name, const u8 *buf, size_t len)
+{
+ char prefix[64];
+
+ if (!buf)
+ return;
+
+ snprintf(prefix, sizeof(prefix), "%s[%zu]: ", name, len);
+
+ print_hex_dump(KERN_DEBUG, prefix, DUMP_PREFIX_ADDRESS, 16, 1, buf,
+ len, false);
+}
+
+static irqreturn_t cc_isr(int irq, void *dev_id)
+{
+ struct cc_drvdata *drvdata = (struct cc_drvdata *)dev_id;
+ struct device *dev = drvdata_to_dev(drvdata);
+ u32 irr;
+ u32 imr;
+
+ /* STAT_OP_TYPE_GENERIC STAT_PHASE_0: Interrupt */
+
+ /* read the interrupt status */
+ irr = cc_ioread(drvdata, CC_REG(HOST_IRR));
+ dev_dbg(dev, "Got IRR=0x%08X\n", irr);
+ if (irr == 0) { /* Probably shared interrupt line */
+ dev_err(dev, "Got interrupt with empty IRR\n");
+ return IRQ_NONE;
+ }
+ imr = cc_ioread(drvdata, CC_REG(HOST_IMR));
+
+ /* clear interrupt - must be before processing events */
+ cc_iowrite(drvdata, CC_REG(HOST_ICR), irr);
+
+ drvdata->irq = irr;
+ /* Completion interrupt - most probable */
+ if (irr & CC_COMP_IRQ_MASK) {
+ /* Mask AXI completion interrupt - will be unmasked in
+ * Deferred service handler
+ */
+ cc_iowrite(drvdata, CC_REG(HOST_IMR), imr | CC_COMP_IRQ_MASK);
+ irr &= ~CC_COMP_IRQ_MASK;
+ complete_request(drvdata);
+ }
+#ifdef CONFIG_CRYPTO_FIPS
+ /* TEE FIPS interrupt */
+ if (irr & CC_GPR0_IRQ_MASK) {
+ /* Mask interrupt - will be unmasked in Deferred service
+ * handler
+ */
+ cc_iowrite(drvdata, CC_REG(HOST_IMR), imr | CC_GPR0_IRQ_MASK);
+ irr &= ~CC_GPR0_IRQ_MASK;
+ fips_handler(drvdata);
+ }
+#endif
+ /* AXI error interrupt */
+ if (irr & CC_AXI_ERR_IRQ_MASK) {
+ u32 axi_err;
+
+ /* Read the AXI error ID */
+ axi_err = cc_ioread(drvdata, CC_REG(AXIM_MON_ERR));
+ dev_dbg(dev, "AXI completion error: axim_mon_err=0x%08X\n",
+ axi_err);
+
+ irr &= ~CC_AXI_ERR_IRQ_MASK;
+ }
+
+ if (irr) {
+ dev_dbg(dev, "IRR includes unknown cause bits (0x%08X)\n",
+ irr);
+ /* Just warning */
+ }
+
+ return IRQ_HANDLED;
+}
+
+int init_cc_regs(struct cc_drvdata *drvdata, bool is_probe)
+{
+ unsigned int val, cache_params;
+ struct device *dev = drvdata_to_dev(drvdata);
+
+ /* Unmask all AXI interrupt sources AXI_CFG1 register */
+ val = cc_ioread(drvdata, CC_REG(AXIM_CFG));
+ cc_iowrite(drvdata, CC_REG(AXIM_CFG), val & ~CC_AXI_IRQ_MASK);
+ dev_dbg(dev, "AXIM_CFG=0x%08X\n",
+ cc_ioread(drvdata, CC_REG(AXIM_CFG)));
+
+ /* Clear all pending interrupts */
+ val = cc_ioread(drvdata, CC_REG(HOST_IRR));
+ dev_dbg(dev, "IRR=0x%08X\n", val);
+ cc_iowrite(drvdata, CC_REG(HOST_ICR), val);
+
+ /* Unmask relevant interrupt cause */
+ val = CC_COMP_IRQ_MASK | CC_AXI_ERR_IRQ_MASK;
+
+ if (drvdata->hw_rev >= CC_HW_REV_712)
+ val |= CC_GPR0_IRQ_MASK;
+
+ cc_iowrite(drvdata, CC_REG(HOST_IMR), ~val);
+
+ cache_params = (drvdata->coherent ? CC_COHERENT_CACHE_PARAMS : 0x0);
+
+ val = cc_ioread(drvdata, CC_REG(AXIM_CACHE_PARAMS));
+
+ if (is_probe)
+ dev_info(dev, "Cache params previous: 0x%08X\n", val);
+
+ cc_iowrite(drvdata, CC_REG(AXIM_CACHE_PARAMS), cache_params);
+ val = cc_ioread(drvdata, CC_REG(AXIM_CACHE_PARAMS));
+
+ if (is_probe)
+ dev_info(dev, "Cache params current: 0x%08X (expect: 0x%08X)\n",
+ val, cache_params);
+
+ return 0;
+}
+
+static int init_cc_resources(struct platform_device *plat_dev)
+{
+ struct resource *req_mem_cc_regs = NULL;
+ struct cc_drvdata *new_drvdata;
+ struct device *dev = &plat_dev->dev;
+ struct device_node *np = dev->of_node;
+ u32 signature_val;
+ u64 dma_mask;
+ const struct cc_hw_data *hw_rev;
+ const struct of_device_id *dev_id;
+ int rc = 0;
+
+ new_drvdata = devm_kzalloc(dev, sizeof(*new_drvdata), GFP_KERNEL);
+ if (!new_drvdata)
+ return -ENOMEM;
+
+ dev_id = of_match_node(arm_ccree_dev_of_match, np);
+ if (!dev_id)
+ return -ENODEV;
+
+ hw_rev = (struct cc_hw_data *)dev_id->data;
+ new_drvdata->hw_rev_name = hw_rev->name;
+ new_drvdata->hw_rev = hw_rev->rev;
+
+ if (hw_rev->rev >= CC_HW_REV_712) {
+ new_drvdata->hash_len_sz = HASH_LEN_SIZE_712;
+ new_drvdata->axim_mon_offset = CC_REG(AXIM_MON_COMP);
+ } else {
+ new_drvdata->hash_len_sz = HASH_LEN_SIZE_630;
+ new_drvdata->axim_mon_offset = CC_REG(AXIM_MON_COMP8);
+ }
+
+ platform_set_drvdata(plat_dev, new_drvdata);
+ new_drvdata->plat_dev = plat_dev;
+
+ new_drvdata->clk = of_clk_get(np, 0);
+ new_drvdata->coherent = of_dma_is_coherent(np);
+
+ /* Get device resources */
+ /* First CC registers space */
+ req_mem_cc_regs = platform_get_resource(plat_dev, IORESOURCE_MEM, 0);
+ /* Map registers space */
+ new_drvdata->cc_base = devm_ioremap_resource(dev, req_mem_cc_regs);
+ if (IS_ERR(new_drvdata->cc_base)) {
+ dev_err(dev, "Failed to ioremap registers");
+ return PTR_ERR(new_drvdata->cc_base);
+ }
+
+ dev_dbg(dev, "Got MEM resource (%s): %pR\n", req_mem_cc_regs->name,
+ req_mem_cc_regs);
+ dev_dbg(dev, "CC registers mapped from %pa to 0x%p\n",
+ &req_mem_cc_regs->start, new_drvdata->cc_base);
+
+ /* Then IRQ */
+ new_drvdata->irq = platform_get_irq(plat_dev, 0);
+ if (new_drvdata->irq < 0) {
+ dev_err(dev, "Failed getting IRQ resource\n");
+ return new_drvdata->irq;
+ }
+
+ rc = devm_request_irq(dev, new_drvdata->irq, cc_isr,
+ IRQF_SHARED, "ccree", new_drvdata);
+ if (rc) {
+ dev_err(dev, "Could not register to interrupt %d\n",
+ new_drvdata->irq);
+ return rc;
+ }
+ dev_dbg(dev, "Registered to IRQ: %d\n", new_drvdata->irq);
+
+ init_completion(&new_drvdata->hw_queue_avail);
+
+ if (!plat_dev->dev.dma_mask)
+ plat_dev->dev.dma_mask = &plat_dev->dev.coherent_dma_mask;
+
+ dma_mask = DMA_BIT_MASK(DMA_BIT_MASK_LEN);
+ while (dma_mask > 0x7fffffffUL) {
+ if (dma_supported(&plat_dev->dev, dma_mask)) {
+ rc = dma_set_coherent_mask(&plat_dev->dev, dma_mask);
+ if (!rc)
+ break;
+ }
+ dma_mask >>= 1;
+ }
+
+ if (rc) {
+ dev_err(dev, "Failed in dma_set_mask, mask=%pad\n", &dma_mask);
+ return rc;
+ }
+
+ rc = cc_clk_on(new_drvdata);
+ if (rc) {
+ dev_err(dev, "Failed to enable clock");
+ return rc;
+ }
+
+ /* Verify correct mapping */
+ signature_val = cc_ioread(new_drvdata, CC_REG(HOST_SIGNATURE));
+ if (signature_val != hw_rev->sig) {
+ dev_err(dev, "Invalid CC signature: SIGNATURE=0x%08X != expected=0x%08X\n",
+ signature_val, hw_rev->sig);
+ rc = -EINVAL;
+ goto post_clk_err;
+ }
+ dev_dbg(dev, "CC SIGNATURE=0x%08X\n", signature_val);
+
+ /* Display HW versions */
+ dev_info(dev, "ARM CryptoCell %s Driver: HW version 0x%08X, Driver version %s\n",
+ hw_rev->name, cc_ioread(new_drvdata, CC_REG(HOST_VERSION)),
+ DRV_MODULE_VERSION);
+
+ rc = init_cc_regs(new_drvdata, true);
+ if (rc) {
+ dev_err(dev, "init_cc_regs failed\n");
+ goto post_clk_err;
+ }
+
+ rc = cc_debugfs_init(new_drvdata);
+ if (rc) {
+ dev_err(dev, "Failed registering debugfs interface\n");
+ goto post_regs_err;
+ }
+
+ rc = cc_fips_init(new_drvdata);
+ if (rc) {
+ dev_err(dev, "CC_FIPS_INIT failed 0x%x\n", rc);
+ goto post_debugfs_err;
+ }
+ rc = cc_sram_mgr_init(new_drvdata);
+ if (rc) {
+ dev_err(dev, "cc_sram_mgr_init failed\n");
+ goto post_fips_init_err;
+ }
+
+ new_drvdata->mlli_sram_addr =
+ cc_sram_alloc(new_drvdata, MAX_MLLI_BUFF_SIZE);
+ if (new_drvdata->mlli_sram_addr == NULL_SRAM_ADDR) {
+ dev_err(dev, "Failed to alloc MLLI Sram buffer\n");
+ rc = -ENOMEM;
+ goto post_sram_mgr_err;
+ }
+
+ rc = cc_req_mgr_init(new_drvdata);
+ if (rc) {
+ dev_err(dev, "cc_req_mgr_init failed\n");
+ goto post_sram_mgr_err;
+ }
+
+ rc = cc_buffer_mgr_init(new_drvdata);
+ if (rc) {
+ dev_err(dev, "buffer_mgr_init failed\n");
+ goto post_req_mgr_err;
+ }
+
+ rc = cc_pm_init(new_drvdata);
+ if (rc) {
+ dev_err(dev, "ssi_power_mgr_init failed\n");
+ goto post_buf_mgr_err;
+ }
+
+ rc = cc_ivgen_init(new_drvdata);
+ if (rc) {
+ dev_err(dev, "cc_ivgen_init failed\n");
+ goto post_power_mgr_err;
+ }
+
+ /* Allocate crypto algs */
+ rc = cc_cipher_alloc(new_drvdata);
+ if (rc) {
+ dev_err(dev, "cc_cipher_alloc failed\n");
+ goto post_ivgen_err;
+ }
+
+ /* hash must be allocated before aead since hash exports APIs */
+ rc = cc_hash_alloc(new_drvdata);
+ if (rc) {
+ dev_err(dev, "cc_hash_alloc failed\n");
+ goto post_cipher_err;
+ }
+
+ rc = cc_aead_alloc(new_drvdata);
+ if (rc) {
+ dev_err(dev, "cc_aead_alloc failed\n");
+ goto post_hash_err;
+ }
+
+ /* If we got here and FIPS mode is enabled
+ * it means all FIPS test passed, so let TEE
+ * know we're good.
+ */
+ cc_set_ree_fips_status(new_drvdata, true);
+
+ return 0;
+
+post_hash_err:
+ cc_hash_free(new_drvdata);
+post_cipher_err:
+ cc_cipher_free(new_drvdata);
+post_ivgen_err:
+ cc_ivgen_fini(new_drvdata);
+post_power_mgr_err:
+ cc_pm_fini(new_drvdata);
+post_buf_mgr_err:
+ cc_buffer_mgr_fini(new_drvdata);
+post_req_mgr_err:
+ cc_req_mgr_fini(new_drvdata);
+post_sram_mgr_err:
+ cc_sram_mgr_fini(new_drvdata);
+post_fips_init_err:
+ cc_fips_fini(new_drvdata);
+post_debugfs_err:
+ cc_debugfs_fini(new_drvdata);
+post_regs_err:
+ fini_cc_regs(new_drvdata);
+post_clk_err:
+ cc_clk_off(new_drvdata);
+ return rc;
+}
+
+void fini_cc_regs(struct cc_drvdata *drvdata)
+{
+ /* Mask all interrupts */
+ cc_iowrite(drvdata, CC_REG(HOST_IMR), 0xFFFFFFFF);
+}
+
+static void cleanup_cc_resources(struct platform_device *plat_dev)
+{
+ struct cc_drvdata *drvdata =
+ (struct cc_drvdata *)platform_get_drvdata(plat_dev);
+
+ cc_aead_free(drvdata);
+ cc_hash_free(drvdata);
+ cc_cipher_free(drvdata);
+ cc_ivgen_fini(drvdata);
+ cc_pm_fini(drvdata);
+ cc_buffer_mgr_fini(drvdata);
+ cc_req_mgr_fini(drvdata);
+ cc_sram_mgr_fini(drvdata);
+ cc_fips_fini(drvdata);
+ cc_debugfs_fini(drvdata);
+ fini_cc_regs(drvdata);
+ cc_clk_off(drvdata);
+}
+
+int cc_clk_on(struct cc_drvdata *drvdata)
+{
+ struct clk *clk = drvdata->clk;
+ int rc;
+
+ if (IS_ERR(clk))
+ /* Not all devices have a clock associated with CCREE */
+ return 0;
+
+ rc = clk_prepare_enable(clk);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+void cc_clk_off(struct cc_drvdata *drvdata)
+{
+ struct clk *clk = drvdata->clk;
+
+ if (IS_ERR(clk))
+ /* Not all devices have a clock associated with CCREE */
+ return;
+
+ clk_disable_unprepare(clk);
+}
+
+static int ccree_probe(struct platform_device *plat_dev)
+{
+ int rc;
+ struct device *dev = &plat_dev->dev;
+
+ /* Map registers space */
+ rc = init_cc_resources(plat_dev);
+ if (rc)
+ return rc;
+
+ dev_info(dev, "ARM ccree device initialized\n");
+
+ return 0;
+}
+
+static int ccree_remove(struct platform_device *plat_dev)
+{
+ struct device *dev = &plat_dev->dev;
+
+ dev_dbg(dev, "Releasing ccree resources...\n");
+
+ cleanup_cc_resources(plat_dev);
+
+ dev_info(dev, "ARM ccree device terminated\n");
+
+ return 0;
+}
+
+static struct platform_driver ccree_driver = {
+ .driver = {
+ .name = "ccree",
+ .of_match_table = arm_ccree_dev_of_match,
+#ifdef CONFIG_PM
+ .pm = &ccree_pm,
+#endif
+ },
+ .probe = ccree_probe,
+ .remove = ccree_remove,
+};
+
+static int __init ccree_init(void)
+{
+ int ret;
+
+ cc_hash_global_init();
+
+ ret = cc_debugfs_global_init();
+ if (ret)
+ return ret;
+
+ return platform_driver_register(&ccree_driver);
+}
+module_init(ccree_init);
+
+static void __exit ccree_exit(void)
+{
+ platform_driver_unregister(&ccree_driver);
+ cc_debugfs_global_fini();
+}
+module_exit(ccree_exit);
+
+/* Module description */
+MODULE_DESCRIPTION("ARM TrustZone CryptoCell REE Driver");
+MODULE_VERSION(DRV_MODULE_VERSION);
+MODULE_AUTHOR("ARM");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/crypto/ccree/cc_driver.h b/drivers/crypto/ccree/cc_driver.h
new file mode 100644
index 000000000000..2048fdeb9579
--- /dev/null
+++ b/drivers/crypto/ccree/cc_driver.h
@@ -0,0 +1,208 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
+
+/* \file cc_driver.h
+ * ARM CryptoCell Linux Crypto Driver
+ */
+
+#ifndef __CC_DRIVER_H__
+#define __CC_DRIVER_H__
+
+#ifdef COMP_IN_WQ
+#include <linux/workqueue.h>
+#else
+#include <linux/interrupt.h>
+#endif
+#include <linux/dma-mapping.h>
+#include <crypto/algapi.h>
+#include <crypto/internal/skcipher.h>
+#include <crypto/aes.h>
+#include <crypto/sha.h>
+#include <crypto/aead.h>
+#include <crypto/authenc.h>
+#include <crypto/hash.h>
+#include <crypto/skcipher.h>
+#include <linux/version.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+/* Registers definitions from shared/hw/ree_include */
+#include "cc_host_regs.h"
+#define CC_DEV_SHA_MAX 512
+#include "cc_crypto_ctx.h"
+#include "cc_hw_queue_defs.h"
+#include "cc_sram_mgr.h"
+
+extern bool cc_dump_desc;
+extern bool cc_dump_bytes;
+
+#define DRV_MODULE_VERSION "4.0"
+
+enum cc_hw_rev {
+ CC_HW_REV_630 = 630,
+ CC_HW_REV_710 = 710,
+ CC_HW_REV_712 = 712
+};
+
+#define CC_COHERENT_CACHE_PARAMS 0xEEE
+
+/* Maximum DMA mask supported by IP */
+#define DMA_BIT_MASK_LEN 48
+
+#define CC_AXI_IRQ_MASK ((1 << CC_AXIM_CFG_BRESPMASK_BIT_SHIFT) | \
+ (1 << CC_AXIM_CFG_RRESPMASK_BIT_SHIFT) | \
+ (1 << CC_AXIM_CFG_INFLTMASK_BIT_SHIFT) | \
+ (1 << CC_AXIM_CFG_COMPMASK_BIT_SHIFT))
+
+#define CC_AXI_ERR_IRQ_MASK BIT(CC_HOST_IRR_AXI_ERR_INT_BIT_SHIFT)
+
+#define CC_COMP_IRQ_MASK BIT(CC_HOST_IRR_AXIM_COMP_INT_BIT_SHIFT)
+
+#define AXIM_MON_COMP_VALUE GENMASK(CC_AXIM_MON_COMP_VALUE_BIT_SIZE + \
+ CC_AXIM_MON_COMP_VALUE_BIT_SHIFT, \
+ CC_AXIM_MON_COMP_VALUE_BIT_SHIFT)
+
+/* Register name mangling macro */
+#define CC_REG(reg_name) CC_ ## reg_name ## _REG_OFFSET
+
+/* TEE FIPS status interrupt */
+#define CC_GPR0_IRQ_MASK BIT(CC_HOST_IRR_GPR0_BIT_SHIFT)
+
+#define CC_CRA_PRIO 400
+
+#define MIN_HW_QUEUE_SIZE 50 /* Minimum size required for proper function */
+
+#define MAX_REQUEST_QUEUE_SIZE 4096
+#define MAX_MLLI_BUFF_SIZE 2080
+#define MAX_ICV_NENTS_SUPPORTED 2
+
+/* Definitions for HW descriptors DIN/DOUT fields */
+#define NS_BIT 1
+#define AXI_ID 0
+/* AXI_ID is not actually the AXI ID of the transaction but the value of AXI_ID
+ * field in the HW descriptor. The DMA engine +8 that value.
+ */
+
+#define CC_MAX_IVGEN_DMA_ADDRESSES 3
+struct cc_crypto_req {
+ void (*user_cb)(struct device *dev, void *req, int err);
+ void *user_arg;
+ dma_addr_t ivgen_dma_addr[CC_MAX_IVGEN_DMA_ADDRESSES];
+ /* For the first 'ivgen_dma_addr_len' addresses of this array,
+ * generated IV would be placed in it by send_request().
+ * Same generated IV for all addresses!
+ */
+ /* Amount of 'ivgen_dma_addr' elements to be filled. */
+ unsigned int ivgen_dma_addr_len;
+ /* The generated IV size required, 8/16 B allowed. */
+ unsigned int ivgen_size;
+ struct completion seq_compl; /* request completion */
+};
+
+/**
+ * struct cc_drvdata - driver private data context
+ * @cc_base: virt address of the CC registers
+ * @irq: device IRQ number
+ * @irq_mask: Interrupt mask shadow (1 for masked interrupts)
+ * @fw_ver: SeP loaded firmware version
+ */
+struct cc_drvdata {
+ void __iomem *cc_base;
+ int irq;
+ u32 irq_mask;
+ u32 fw_ver;
+ struct completion hw_queue_avail; /* wait for HW queue availability */
+ struct platform_device *plat_dev;
+ cc_sram_addr_t mlli_sram_addr;
+ void *buff_mgr_handle;
+ void *cipher_handle;
+ void *hash_handle;
+ void *aead_handle;
+ void *request_mgr_handle;
+ void *fips_handle;
+ void *ivgen_handle;
+ void *sram_mgr_handle;
+ void *debugfs;
+ struct clk *clk;
+ bool coherent;
+ char *hw_rev_name;
+ enum cc_hw_rev hw_rev;
+ u32 hash_len_sz;
+ u32 axim_mon_offset;
+};
+
+struct cc_crypto_alg {
+ struct list_head entry;
+ int cipher_mode;
+ int flow_mode; /* Note: currently, refers to the cipher mode only. */
+ int auth_mode;
+ unsigned int data_unit;
+ struct cc_drvdata *drvdata;
+ struct skcipher_alg skcipher_alg;
+ struct aead_alg aead_alg;
+};
+
+struct cc_alg_template {
+ char name[CRYPTO_MAX_ALG_NAME];
+ char driver_name[CRYPTO_MAX_ALG_NAME];
+ unsigned int blocksize;
+ u32 type;
+ union {
+ struct skcipher_alg skcipher;
+ struct aead_alg aead;
+ } template_u;
+ int cipher_mode;
+ int flow_mode; /* Note: currently, refers to the cipher mode only. */
+ int auth_mode;
+ u32 min_hw_rev;
+ unsigned int data_unit;
+ struct cc_drvdata *drvdata;
+};
+
+struct async_gen_req_ctx {
+ dma_addr_t iv_dma_addr;
+ enum drv_crypto_direction op_type;
+};
+
+static inline struct device *drvdata_to_dev(struct cc_drvdata *drvdata)
+{
+ return &drvdata->plat_dev->dev;
+}
+
+void __dump_byte_array(const char *name, const u8 *buf, size_t len);
+static inline void dump_byte_array(const char *name, const u8 *the_array,
+ size_t size)
+{
+ if (cc_dump_bytes)
+ __dump_byte_array(name, the_array, size);
+}
+
+int init_cc_regs(struct cc_drvdata *drvdata, bool is_probe);
+void fini_cc_regs(struct cc_drvdata *drvdata);
+int cc_clk_on(struct cc_drvdata *drvdata);
+void cc_clk_off(struct cc_drvdata *drvdata);
+
+static inline void cc_iowrite(struct cc_drvdata *drvdata, u32 reg, u32 val)
+{
+ iowrite32(val, (drvdata->cc_base + reg));
+}
+
+static inline u32 cc_ioread(struct cc_drvdata *drvdata, u32 reg)
+{
+ return ioread32(drvdata->cc_base + reg);
+}
+
+static inline gfp_t cc_gfp_flags(struct crypto_async_request *req)
+{
+ return (req->flags & CRYPTO_TFM_REQ_MAY_SLEEP) ?
+ GFP_KERNEL : GFP_ATOMIC;
+}
+
+static inline void set_queue_last_ind(struct cc_drvdata *drvdata,
+ struct cc_hw_desc *pdesc)
+{
+ if (drvdata->hw_rev >= CC_HW_REV_712)
+ set_queue_last_ind_bit(pdesc);
+}
+
+#endif /*__CC_DRIVER_H__*/
diff --git a/drivers/crypto/ccree/cc_fips.c b/drivers/crypto/ccree/cc_fips.c
new file mode 100644
index 000000000000..b4d0a6d983e0
--- /dev/null
+++ b/drivers/crypto/ccree/cc_fips.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
+
+#include <linux/kernel.h>
+#include <linux/fips.h>
+
+#include "cc_driver.h"
+#include "cc_fips.h"
+
+static void fips_dsr(unsigned long devarg);
+
+struct cc_fips_handle {
+ struct tasklet_struct tasklet;
+};
+
+/* The function called once at driver entry point to check
+ * whether TEE FIPS error occurred.
+ */
+static bool cc_get_tee_fips_status(struct cc_drvdata *drvdata)
+{
+ u32 reg;
+
+ reg = cc_ioread(drvdata, CC_REG(GPR_HOST));
+ return (reg == (CC_FIPS_SYNC_TEE_STATUS | CC_FIPS_SYNC_MODULE_OK));
+}
+
+/*
+ * This function should push the FIPS REE library status towards the TEE library
+ * by writing the error state to HOST_GPR0 register.
+ */
+void cc_set_ree_fips_status(struct cc_drvdata *drvdata, bool status)
+{
+ int val = CC_FIPS_SYNC_REE_STATUS;
+
+ if (drvdata->hw_rev < CC_HW_REV_712)
+ return;
+
+ val |= (status ? CC_FIPS_SYNC_MODULE_OK : CC_FIPS_SYNC_MODULE_ERROR);
+
+ cc_iowrite(drvdata, CC_REG(HOST_GPR0), val);
+}
+
+void cc_fips_fini(struct cc_drvdata *drvdata)
+{
+ struct cc_fips_handle *fips_h = drvdata->fips_handle;
+
+ if (drvdata->hw_rev < CC_HW_REV_712 || !fips_h)
+ return;
+
+ /* Kill tasklet */
+ tasklet_kill(&fips_h->tasklet);
+
+ kfree(fips_h);
+ drvdata->fips_handle = NULL;
+}
+
+void fips_handler(struct cc_drvdata *drvdata)
+{
+ struct cc_fips_handle *fips_handle_ptr = drvdata->fips_handle;
+
+ if (drvdata->hw_rev < CC_HW_REV_712)
+ return;
+
+ tasklet_schedule(&fips_handle_ptr->tasklet);
+}
+
+static inline void tee_fips_error(struct device *dev)
+{
+ if (fips_enabled)
+ panic("ccree: TEE reported cryptographic error in fips mode!\n");
+ else
+ dev_err(dev, "TEE reported error!\n");
+}
+
+/* Deferred service handler, run as interrupt-fired tasklet */
+static void fips_dsr(unsigned long devarg)
+{
+ struct cc_drvdata *drvdata = (struct cc_drvdata *)devarg;
+ struct device *dev = drvdata_to_dev(drvdata);
+ u32 irq, state, val;
+
+ irq = (drvdata->irq & (CC_GPR0_IRQ_MASK));
+
+ if (irq) {
+ state = cc_ioread(drvdata, CC_REG(GPR_HOST));
+
+ if (state != (CC_FIPS_SYNC_TEE_STATUS | CC_FIPS_SYNC_MODULE_OK))
+ tee_fips_error(dev);
+ }
+
+ /* after verifing that there is nothing to do,
+ * unmask AXI completion interrupt.
+ */
+ val = (CC_REG(HOST_IMR) & ~irq);
+ cc_iowrite(drvdata, CC_REG(HOST_IMR), val);
+}
+
+/* The function called once at driver entry point .*/
+int cc_fips_init(struct cc_drvdata *p_drvdata)
+{
+ struct cc_fips_handle *fips_h;
+ struct device *dev = drvdata_to_dev(p_drvdata);
+
+ if (p_drvdata->hw_rev < CC_HW_REV_712)
+ return 0;
+
+ fips_h = kzalloc(sizeof(*fips_h), GFP_KERNEL);
+ if (!fips_h)
+ return -ENOMEM;
+
+ p_drvdata->fips_handle = fips_h;
+
+ dev_dbg(dev, "Initializing fips tasklet\n");
+ tasklet_init(&fips_h->tasklet, fips_dsr, (unsigned long)p_drvdata);
+
+ if (!cc_get_tee_fips_status(p_drvdata))
+ tee_fips_error(dev);
+
+ return 0;
+}
diff --git a/drivers/crypto/ccree/cc_fips.h b/drivers/crypto/ccree/cc_fips.h
new file mode 100644
index 000000000000..645e096a7a82
--- /dev/null
+++ b/drivers/crypto/ccree/cc_fips.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
+
+#ifndef __CC_FIPS_H__
+#define __CC_FIPS_H__
+
+#ifdef CONFIG_CRYPTO_FIPS
+
+enum cc_fips_status {
+ CC_FIPS_SYNC_MODULE_OK = 0x0,
+ CC_FIPS_SYNC_MODULE_ERROR = 0x1,
+ CC_FIPS_SYNC_REE_STATUS = 0x4,
+ CC_FIPS_SYNC_TEE_STATUS = 0x8,
+ CC_FIPS_SYNC_STATUS_RESERVE32B = S32_MAX
+};
+
+int cc_fips_init(struct cc_drvdata *p_drvdata);
+void cc_fips_fini(struct cc_drvdata *drvdata);
+void fips_handler(struct cc_drvdata *drvdata);
+void cc_set_ree_fips_status(struct cc_drvdata *drvdata, bool ok);
+
+#else /* CONFIG_CRYPTO_FIPS */
+
+static inline int cc_fips_init(struct cc_drvdata *p_drvdata)
+{
+ return 0;
+}
+
+static inline void cc_fips_fini(struct cc_drvdata *drvdata) {}
+static inline void cc_set_ree_fips_status(struct cc_drvdata *drvdata,
+ bool ok) {}
+static inline void fips_handler(struct cc_drvdata *drvdata) {}
+
+#endif /* CONFIG_CRYPTO_FIPS */
+
+#endif /*__CC_FIPS_H__*/
diff --git a/drivers/crypto/ccree/cc_hash.c b/drivers/crypto/ccree/cc_hash.c
new file mode 100644
index 000000000000..96ff777474d7
--- /dev/null
+++ b/drivers/crypto/ccree/cc_hash.c
@@ -0,0 +1,2296 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <crypto/algapi.h>
+#include <crypto/hash.h>
+#include <crypto/md5.h>
+#include <crypto/internal/hash.h>
+
+#include "cc_driver.h"
+#include "cc_request_mgr.h"
+#include "cc_buffer_mgr.h"
+#include "cc_hash.h"
+#include "cc_sram_mgr.h"
+
+#define CC_MAX_HASH_SEQ_LEN 12
+#define CC_MAX_OPAD_KEYS_SIZE CC_MAX_HASH_BLCK_SIZE
+
+struct cc_hash_handle {
+ cc_sram_addr_t digest_len_sram_addr; /* const value in SRAM*/
+ cc_sram_addr_t larval_digest_sram_addr; /* const value in SRAM */
+ struct list_head hash_list;
+};
+
+static const u32 digest_len_init[] = {
+ 0x00000040, 0x00000000, 0x00000000, 0x00000000 };
+static const u32 md5_init[] = {
+ SHA1_H3, SHA1_H2, SHA1_H1, SHA1_H0 };
+static const u32 sha1_init[] = {
+ SHA1_H4, SHA1_H3, SHA1_H2, SHA1_H1, SHA1_H0 };
+static const u32 sha224_init[] = {
+ SHA224_H7, SHA224_H6, SHA224_H5, SHA224_H4,
+ SHA224_H3, SHA224_H2, SHA224_H1, SHA224_H0 };
+static const u32 sha256_init[] = {
+ SHA256_H7, SHA256_H6, SHA256_H5, SHA256_H4,
+ SHA256_H3, SHA256_H2, SHA256_H1, SHA256_H0 };
+static const u32 digest_len_sha512_init[] = {
+ 0x00000080, 0x00000000, 0x00000000, 0x00000000 };
+static u64 sha384_init[] = {
+ SHA384_H7, SHA384_H6, SHA384_H5, SHA384_H4,
+ SHA384_H3, SHA384_H2, SHA384_H1, SHA384_H0 };
+static u64 sha512_init[] = {
+ SHA512_H7, SHA512_H6, SHA512_H5, SHA512_H4,
+ SHA512_H3, SHA512_H2, SHA512_H1, SHA512_H0 };
+
+static void cc_setup_xcbc(struct ahash_request *areq, struct cc_hw_desc desc[],
+ unsigned int *seq_size);
+
+static void cc_setup_cmac(struct ahash_request *areq, struct cc_hw_desc desc[],
+ unsigned int *seq_size);
+
+static const void *cc_larval_digest(struct device *dev, u32 mode);
+
+struct cc_hash_alg {
+ struct list_head entry;
+ int hash_mode;
+ int hw_mode;
+ int inter_digestsize;
+ struct cc_drvdata *drvdata;
+ struct ahash_alg ahash_alg;
+};
+
+struct hash_key_req_ctx {
+ u32 keylen;
+ dma_addr_t key_dma_addr;
+};
+
+/* hash per-session context */
+struct cc_hash_ctx {
+ struct cc_drvdata *drvdata;
+ /* holds the origin digest; the digest after "setkey" if HMAC,*
+ * the initial digest if HASH.
+ */
+ u8 digest_buff[CC_MAX_HASH_DIGEST_SIZE] ____cacheline_aligned;
+ u8 opad_tmp_keys_buff[CC_MAX_OPAD_KEYS_SIZE] ____cacheline_aligned;
+
+ dma_addr_t opad_tmp_keys_dma_addr ____cacheline_aligned;
+ dma_addr_t digest_buff_dma_addr;
+ /* use for hmac with key large then mode block size */
+ struct hash_key_req_ctx key_params;
+ int hash_mode;
+ int hw_mode;
+ int inter_digestsize;
+ struct completion setkey_comp;
+ bool is_hmac;
+};
+
+static void cc_set_desc(struct ahash_req_ctx *areq_ctx, struct cc_hash_ctx *ctx,
+ unsigned int flow_mode, struct cc_hw_desc desc[],
+ bool is_not_last_data, unsigned int *seq_size);
+
+static void cc_set_endianity(u32 mode, struct cc_hw_desc *desc)
+{
+ if (mode == DRV_HASH_MD5 || mode == DRV_HASH_SHA384 ||
+ mode == DRV_HASH_SHA512) {
+ set_bytes_swap(desc, 1);
+ } else {
+ set_cipher_config0(desc, HASH_DIGEST_RESULT_LITTLE_ENDIAN);
+ }
+}
+
+static int cc_map_result(struct device *dev, struct ahash_req_ctx *state,
+ unsigned int digestsize)
+{
+ state->digest_result_dma_addr =
+ dma_map_single(dev, state->digest_result_buff,
+ digestsize, DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(dev, state->digest_result_dma_addr)) {
+ dev_err(dev, "Mapping digest result buffer %u B for DMA failed\n",
+ digestsize);
+ return -ENOMEM;
+ }
+ dev_dbg(dev, "Mapped digest result buffer %u B at va=%pK to dma=%pad\n",
+ digestsize, state->digest_result_buff,
+ &state->digest_result_dma_addr);
+
+ return 0;
+}
+
+static void cc_init_req(struct device *dev, struct ahash_req_ctx *state,
+ struct cc_hash_ctx *ctx)
+{
+ bool is_hmac = ctx->is_hmac;
+
+ memset(state, 0, sizeof(*state));
+
+ if (is_hmac) {
+ if (ctx->hw_mode != DRV_CIPHER_XCBC_MAC &&
+ ctx->hw_mode != DRV_CIPHER_CMAC) {
+ dma_sync_single_for_cpu(dev, ctx->digest_buff_dma_addr,
+ ctx->inter_digestsize,
+ DMA_BIDIRECTIONAL);
+
+ memcpy(state->digest_buff, ctx->digest_buff,
+ ctx->inter_digestsize);
+ if (ctx->hash_mode == DRV_HASH_SHA512 ||
+ ctx->hash_mode == DRV_HASH_SHA384)
+ memcpy(state->digest_bytes_len,
+ digest_len_sha512_init,
+ ctx->drvdata->hash_len_sz);
+ else
+ memcpy(state->digest_bytes_len, digest_len_init,
+ ctx->drvdata->hash_len_sz);
+ }
+
+ if (ctx->hash_mode != DRV_HASH_NULL) {
+ dma_sync_single_for_cpu(dev,
+ ctx->opad_tmp_keys_dma_addr,
+ ctx->inter_digestsize,
+ DMA_BIDIRECTIONAL);
+ memcpy(state->opad_digest_buff,
+ ctx->opad_tmp_keys_buff, ctx->inter_digestsize);
+ }
+ } else { /*hash*/
+ /* Copy the initial digests if hash flow. */
+ const void *larval = cc_larval_digest(dev, ctx->hash_mode);
+
+ memcpy(state->digest_buff, larval, ctx->inter_digestsize);
+ }
+}
+
+static int cc_map_req(struct device *dev, struct ahash_req_ctx *state,
+ struct cc_hash_ctx *ctx)
+{
+ bool is_hmac = ctx->is_hmac;
+
+ state->digest_buff_dma_addr =
+ dma_map_single(dev, state->digest_buff,
+ ctx->inter_digestsize, DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(dev, state->digest_buff_dma_addr)) {
+ dev_err(dev, "Mapping digest len %d B at va=%pK for DMA failed\n",
+ ctx->inter_digestsize, state->digest_buff);
+ return -EINVAL;
+ }
+ dev_dbg(dev, "Mapped digest %d B at va=%pK to dma=%pad\n",
+ ctx->inter_digestsize, state->digest_buff,
+ &state->digest_buff_dma_addr);
+
+ if (ctx->hw_mode != DRV_CIPHER_XCBC_MAC) {
+ state->digest_bytes_len_dma_addr =
+ dma_map_single(dev, state->digest_bytes_len,
+ HASH_MAX_LEN_SIZE, DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(dev, state->digest_bytes_len_dma_addr)) {
+ dev_err(dev, "Mapping digest len %u B at va=%pK for DMA failed\n",
+ HASH_MAX_LEN_SIZE, state->digest_bytes_len);
+ goto unmap_digest_buf;
+ }
+ dev_dbg(dev, "Mapped digest len %u B at va=%pK to dma=%pad\n",
+ HASH_MAX_LEN_SIZE, state->digest_bytes_len,
+ &state->digest_bytes_len_dma_addr);
+ }
+
+ if (is_hmac && ctx->hash_mode != DRV_HASH_NULL) {
+ state->opad_digest_dma_addr =
+ dma_map_single(dev, state->opad_digest_buff,
+ ctx->inter_digestsize,
+ DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(dev, state->opad_digest_dma_addr)) {
+ dev_err(dev, "Mapping opad digest %d B at va=%pK for DMA failed\n",
+ ctx->inter_digestsize,
+ state->opad_digest_buff);
+ goto unmap_digest_len;
+ }
+ dev_dbg(dev, "Mapped opad digest %d B at va=%pK to dma=%pad\n",
+ ctx->inter_digestsize, state->opad_digest_buff,
+ &state->opad_digest_dma_addr);
+ }
+
+ return 0;
+
+unmap_digest_len:
+ if (state->digest_bytes_len_dma_addr) {
+ dma_unmap_single(dev, state->digest_bytes_len_dma_addr,
+ HASH_MAX_LEN_SIZE, DMA_BIDIRECTIONAL);
+ state->digest_bytes_len_dma_addr = 0;
+ }
+unmap_digest_buf:
+ if (state->digest_buff_dma_addr) {
+ dma_unmap_single(dev, state->digest_buff_dma_addr,
+ ctx->inter_digestsize, DMA_BIDIRECTIONAL);
+ state->digest_buff_dma_addr = 0;
+ }
+
+ return -EINVAL;
+}
+
+static void cc_unmap_req(struct device *dev, struct ahash_req_ctx *state,
+ struct cc_hash_ctx *ctx)
+{
+ if (state->digest_buff_dma_addr) {
+ dma_unmap_single(dev, state->digest_buff_dma_addr,
+ ctx->inter_digestsize, DMA_BIDIRECTIONAL);
+ dev_dbg(dev, "Unmapped digest-buffer: digest_buff_dma_addr=%pad\n",
+ &state->digest_buff_dma_addr);
+ state->digest_buff_dma_addr = 0;
+ }
+ if (state->digest_bytes_len_dma_addr) {
+ dma_unmap_single(dev, state->digest_bytes_len_dma_addr,
+ HASH_MAX_LEN_SIZE, DMA_BIDIRECTIONAL);
+ dev_dbg(dev, "Unmapped digest-bytes-len buffer: digest_bytes_len_dma_addr=%pad\n",
+ &state->digest_bytes_len_dma_addr);
+ state->digest_bytes_len_dma_addr = 0;
+ }
+ if (state->opad_digest_dma_addr) {
+ dma_unmap_single(dev, state->opad_digest_dma_addr,
+ ctx->inter_digestsize, DMA_BIDIRECTIONAL);
+ dev_dbg(dev, "Unmapped opad-digest: opad_digest_dma_addr=%pad\n",
+ &state->opad_digest_dma_addr);
+ state->opad_digest_dma_addr = 0;
+ }
+}
+
+static void cc_unmap_result(struct device *dev, struct ahash_req_ctx *state,
+ unsigned int digestsize, u8 *result)
+{
+ if (state->digest_result_dma_addr) {
+ dma_unmap_single(dev, state->digest_result_dma_addr, digestsize,
+ DMA_BIDIRECTIONAL);
+ dev_dbg(dev, "unmpa digest result buffer va (%pK) pa (%pad) len %u\n",
+ state->digest_result_buff,
+ &state->digest_result_dma_addr, digestsize);
+ memcpy(result, state->digest_result_buff, digestsize);
+ }
+ state->digest_result_dma_addr = 0;
+}
+
+static void cc_update_complete(struct device *dev, void *cc_req, int err)
+{
+ struct ahash_request *req = (struct ahash_request *)cc_req;
+ struct ahash_req_ctx *state = ahash_request_ctx(req);
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
+
+ dev_dbg(dev, "req=%pK\n", req);
+
+ cc_unmap_hash_request(dev, state, req->src, false);
+ cc_unmap_req(dev, state, ctx);
+ req->base.complete(&req->base, err);
+}
+
+static void cc_digest_complete(struct device *dev, void *cc_req, int err)
+{
+ struct ahash_request *req = (struct ahash_request *)cc_req;
+ struct ahash_req_ctx *state = ahash_request_ctx(req);
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
+ u32 digestsize = crypto_ahash_digestsize(tfm);
+
+ dev_dbg(dev, "req=%pK\n", req);
+
+ cc_unmap_hash_request(dev, state, req->src, false);
+ cc_unmap_result(dev, state, digestsize, req->result);
+ cc_unmap_req(dev, state, ctx);
+ req->base.complete(&req->base, err);
+}
+
+static void cc_hash_complete(struct device *dev, void *cc_req, int err)
+{
+ struct ahash_request *req = (struct ahash_request *)cc_req;
+ struct ahash_req_ctx *state = ahash_request_ctx(req);
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
+ u32 digestsize = crypto_ahash_digestsize(tfm);
+
+ dev_dbg(dev, "req=%pK\n", req);
+
+ cc_unmap_hash_request(dev, state, req->src, false);
+ cc_unmap_result(dev, state, digestsize, req->result);
+ cc_unmap_req(dev, state, ctx);
+ req->base.complete(&req->base, err);
+}
+
+static int cc_fin_result(struct cc_hw_desc *desc, struct ahash_request *req,
+ int idx)
+{
+ struct ahash_req_ctx *state = ahash_request_ctx(req);
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
+ u32 digestsize = crypto_ahash_digestsize(tfm);
+
+ /* Get final MAC result */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], ctx->hw_mode);
+ /* TODO */
+ set_dout_dlli(&desc[idx], state->digest_result_dma_addr, digestsize,
+ NS_BIT, 1);
+ set_queue_last_ind(ctx->drvdata, &desc[idx]);
+ set_flow_mode(&desc[idx], S_HASH_to_DOUT);
+ set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
+ set_cipher_config1(&desc[idx], HASH_PADDING_DISABLED);
+ cc_set_endianity(ctx->hash_mode, &desc[idx]);
+ idx++;
+
+ return idx;
+}
+
+static int cc_fin_hmac(struct cc_hw_desc *desc, struct ahash_request *req,
+ int idx)
+{
+ struct ahash_req_ctx *state = ahash_request_ctx(req);
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
+ u32 digestsize = crypto_ahash_digestsize(tfm);
+
+ /* store the hash digest result in the context */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], ctx->hw_mode);
+ set_dout_dlli(&desc[idx], state->digest_buff_dma_addr, digestsize,
+ NS_BIT, 0);
+ set_flow_mode(&desc[idx], S_HASH_to_DOUT);
+ cc_set_endianity(ctx->hash_mode, &desc[idx]);
+ set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
+ idx++;
+
+ /* Loading hash opad xor key state */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], ctx->hw_mode);
+ set_din_type(&desc[idx], DMA_DLLI, state->opad_digest_dma_addr,
+ ctx->inter_digestsize, NS_BIT);
+ set_flow_mode(&desc[idx], S_DIN_to_HASH);
+ set_setup_mode(&desc[idx], SETUP_LOAD_STATE0);
+ idx++;
+
+ /* Load the hash current length */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], ctx->hw_mode);
+ set_din_sram(&desc[idx],
+ cc_digest_len_addr(ctx->drvdata, ctx->hash_mode),
+ ctx->drvdata->hash_len_sz);
+ set_cipher_config1(&desc[idx], HASH_PADDING_ENABLED);
+ set_flow_mode(&desc[idx], S_DIN_to_HASH);
+ set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
+ idx++;
+
+ /* Memory Barrier: wait for IPAD/OPAD axi write to complete */
+ hw_desc_init(&desc[idx]);
+ set_din_no_dma(&desc[idx], 0, 0xfffff0);
+ set_dout_no_dma(&desc[idx], 0, 0, 1);
+ idx++;
+
+ /* Perform HASH update */
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_DLLI, state->digest_buff_dma_addr,
+ digestsize, NS_BIT);
+ set_flow_mode(&desc[idx], DIN_HASH);
+ idx++;
+
+ return idx;
+}
+
+static int cc_hash_digest(struct ahash_request *req)
+{
+ struct ahash_req_ctx *state = ahash_request_ctx(req);
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
+ u32 digestsize = crypto_ahash_digestsize(tfm);
+ struct scatterlist *src = req->src;
+ unsigned int nbytes = req->nbytes;
+ u8 *result = req->result;
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+ bool is_hmac = ctx->is_hmac;
+ struct cc_crypto_req cc_req = {};
+ struct cc_hw_desc desc[CC_MAX_HASH_SEQ_LEN];
+ cc_sram_addr_t larval_digest_addr =
+ cc_larval_digest_addr(ctx->drvdata, ctx->hash_mode);
+ int idx = 0;
+ int rc = 0;
+ gfp_t flags = cc_gfp_flags(&req->base);
+
+ dev_dbg(dev, "===== %s-digest (%d) ====\n", is_hmac ? "hmac" : "hash",
+ nbytes);
+
+ cc_init_req(dev, state, ctx);
+
+ if (cc_map_req(dev, state, ctx)) {
+ dev_err(dev, "map_ahash_source() failed\n");
+ return -ENOMEM;
+ }
+
+ if (cc_map_result(dev, state, digestsize)) {
+ dev_err(dev, "map_ahash_digest() failed\n");
+ cc_unmap_req(dev, state, ctx);
+ return -ENOMEM;
+ }
+
+ if (cc_map_hash_request_final(ctx->drvdata, state, src, nbytes, 1,
+ flags)) {
+ dev_err(dev, "map_ahash_request_final() failed\n");
+ cc_unmap_result(dev, state, digestsize, result);
+ cc_unmap_req(dev, state, ctx);
+ return -ENOMEM;
+ }
+
+ /* Setup request structure */
+ cc_req.user_cb = cc_digest_complete;
+ cc_req.user_arg = req;
+
+ /* If HMAC then load hash IPAD xor key, if HASH then load initial
+ * digest
+ */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], ctx->hw_mode);
+ if (is_hmac) {
+ set_din_type(&desc[idx], DMA_DLLI, state->digest_buff_dma_addr,
+ ctx->inter_digestsize, NS_BIT);
+ } else {
+ set_din_sram(&desc[idx], larval_digest_addr,
+ ctx->inter_digestsize);
+ }
+ set_flow_mode(&desc[idx], S_DIN_to_HASH);
+ set_setup_mode(&desc[idx], SETUP_LOAD_STATE0);
+ idx++;
+
+ /* Load the hash current length */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], ctx->hw_mode);
+
+ if (is_hmac) {
+ set_din_type(&desc[idx], DMA_DLLI,
+ state->digest_bytes_len_dma_addr,
+ ctx->drvdata->hash_len_sz, NS_BIT);
+ } else {
+ set_din_const(&desc[idx], 0, ctx->drvdata->hash_len_sz);
+ if (nbytes)
+ set_cipher_config1(&desc[idx], HASH_PADDING_ENABLED);
+ else
+ set_cipher_do(&desc[idx], DO_PAD);
+ }
+ set_flow_mode(&desc[idx], S_DIN_to_HASH);
+ set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
+ idx++;
+
+ cc_set_desc(state, ctx, DIN_HASH, desc, false, &idx);
+
+ if (is_hmac) {
+ /* HW last hash block padding (aka. "DO_PAD") */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], ctx->hw_mode);
+ set_dout_dlli(&desc[idx], state->digest_buff_dma_addr,
+ ctx->drvdata->hash_len_sz, NS_BIT, 0);
+ set_flow_mode(&desc[idx], S_HASH_to_DOUT);
+ set_setup_mode(&desc[idx], SETUP_WRITE_STATE1);
+ set_cipher_do(&desc[idx], DO_PAD);
+ idx++;
+
+ idx = cc_fin_hmac(desc, req, idx);
+ }
+
+ idx = cc_fin_result(desc, req, idx);
+
+ rc = cc_send_request(ctx->drvdata, &cc_req, desc, idx, &req->base);
+ if (rc != -EINPROGRESS && rc != -EBUSY) {
+ dev_err(dev, "send_request() failed (rc=%d)\n", rc);
+ cc_unmap_hash_request(dev, state, src, true);
+ cc_unmap_result(dev, state, digestsize, result);
+ cc_unmap_req(dev, state, ctx);
+ }
+ return rc;
+}
+
+static int cc_restore_hash(struct cc_hw_desc *desc, struct cc_hash_ctx *ctx,
+ struct ahash_req_ctx *state, unsigned int idx)
+{
+ /* Restore hash digest */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], ctx->hw_mode);
+ set_din_type(&desc[idx], DMA_DLLI, state->digest_buff_dma_addr,
+ ctx->inter_digestsize, NS_BIT);
+ set_flow_mode(&desc[idx], S_DIN_to_HASH);
+ set_setup_mode(&desc[idx], SETUP_LOAD_STATE0);
+ idx++;
+
+ /* Restore hash current length */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], ctx->hw_mode);
+ set_cipher_config1(&desc[idx], HASH_PADDING_DISABLED);
+ set_din_type(&desc[idx], DMA_DLLI, state->digest_bytes_len_dma_addr,
+ ctx->drvdata->hash_len_sz, NS_BIT);
+ set_flow_mode(&desc[idx], S_DIN_to_HASH);
+ set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
+ idx++;
+
+ cc_set_desc(state, ctx, DIN_HASH, desc, false, &idx);
+
+ return idx;
+}
+
+static int cc_hash_update(struct ahash_request *req)
+{
+ struct ahash_req_ctx *state = ahash_request_ctx(req);
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
+ unsigned int block_size = crypto_tfm_alg_blocksize(&tfm->base);
+ struct scatterlist *src = req->src;
+ unsigned int nbytes = req->nbytes;
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+ struct cc_crypto_req cc_req = {};
+ struct cc_hw_desc desc[CC_MAX_HASH_SEQ_LEN];
+ u32 idx = 0;
+ int rc;
+ gfp_t flags = cc_gfp_flags(&req->base);
+
+ dev_dbg(dev, "===== %s-update (%d) ====\n", ctx->is_hmac ?
+ "hmac" : "hash", nbytes);
+
+ if (nbytes == 0) {
+ /* no real updates required */
+ return 0;
+ }
+
+ rc = cc_map_hash_request_update(ctx->drvdata, state, src, nbytes,
+ block_size, flags);
+ if (rc) {
+ if (rc == 1) {
+ dev_dbg(dev, " data size not require HW update %x\n",
+ nbytes);
+ /* No hardware updates are required */
+ return 0;
+ }
+ dev_err(dev, "map_ahash_request_update() failed\n");
+ return -ENOMEM;
+ }
+
+ if (cc_map_req(dev, state, ctx)) {
+ dev_err(dev, "map_ahash_source() failed\n");
+ cc_unmap_hash_request(dev, state, src, true);
+ return -EINVAL;
+ }
+
+ /* Setup request structure */
+ cc_req.user_cb = cc_update_complete;
+ cc_req.user_arg = req;
+
+ idx = cc_restore_hash(desc, ctx, state, idx);
+
+ /* store the hash digest result in context */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], ctx->hw_mode);
+ set_dout_dlli(&desc[idx], state->digest_buff_dma_addr,
+ ctx->inter_digestsize, NS_BIT, 0);
+ set_flow_mode(&desc[idx], S_HASH_to_DOUT);
+ set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
+ idx++;
+
+ /* store current hash length in context */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], ctx->hw_mode);
+ set_dout_dlli(&desc[idx], state->digest_bytes_len_dma_addr,
+ ctx->drvdata->hash_len_sz, NS_BIT, 1);
+ set_queue_last_ind(ctx->drvdata, &desc[idx]);
+ set_flow_mode(&desc[idx], S_HASH_to_DOUT);
+ set_setup_mode(&desc[idx], SETUP_WRITE_STATE1);
+ idx++;
+
+ rc = cc_send_request(ctx->drvdata, &cc_req, desc, idx, &req->base);
+ if (rc != -EINPROGRESS && rc != -EBUSY) {
+ dev_err(dev, "send_request() failed (rc=%d)\n", rc);
+ cc_unmap_hash_request(dev, state, src, true);
+ cc_unmap_req(dev, state, ctx);
+ }
+ return rc;
+}
+
+static int cc_hash_finup(struct ahash_request *req)
+{
+ struct ahash_req_ctx *state = ahash_request_ctx(req);
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
+ u32 digestsize = crypto_ahash_digestsize(tfm);
+ struct scatterlist *src = req->src;
+ unsigned int nbytes = req->nbytes;
+ u8 *result = req->result;
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+ bool is_hmac = ctx->is_hmac;
+ struct cc_crypto_req cc_req = {};
+ struct cc_hw_desc desc[CC_MAX_HASH_SEQ_LEN];
+ unsigned int idx = 0;
+ int rc;
+ gfp_t flags = cc_gfp_flags(&req->base);
+
+ dev_dbg(dev, "===== %s-finup (%d) ====\n", is_hmac ? "hmac" : "hash",
+ nbytes);
+
+ if (cc_map_req(dev, state, ctx)) {
+ dev_err(dev, "map_ahash_source() failed\n");
+ return -EINVAL;
+ }
+
+ if (cc_map_hash_request_final(ctx->drvdata, state, src, nbytes, 1,
+ flags)) {
+ dev_err(dev, "map_ahash_request_final() failed\n");
+ cc_unmap_req(dev, state, ctx);
+ return -ENOMEM;
+ }
+ if (cc_map_result(dev, state, digestsize)) {
+ dev_err(dev, "map_ahash_digest() failed\n");
+ cc_unmap_hash_request(dev, state, src, true);
+ cc_unmap_req(dev, state, ctx);
+ return -ENOMEM;
+ }
+
+ /* Setup request structure */
+ cc_req.user_cb = cc_hash_complete;
+ cc_req.user_arg = req;
+
+ idx = cc_restore_hash(desc, ctx, state, idx);
+
+ if (is_hmac)
+ idx = cc_fin_hmac(desc, req, idx);
+
+ idx = cc_fin_result(desc, req, idx);
+
+ rc = cc_send_request(ctx->drvdata, &cc_req, desc, idx, &req->base);
+ if (rc != -EINPROGRESS && rc != -EBUSY) {
+ dev_err(dev, "send_request() failed (rc=%d)\n", rc);
+ cc_unmap_hash_request(dev, state, src, true);
+ cc_unmap_result(dev, state, digestsize, result);
+ cc_unmap_req(dev, state, ctx);
+ }
+ return rc;
+}
+
+static int cc_hash_final(struct ahash_request *req)
+{
+ struct ahash_req_ctx *state = ahash_request_ctx(req);
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
+ u32 digestsize = crypto_ahash_digestsize(tfm);
+ struct scatterlist *src = req->src;
+ unsigned int nbytes = req->nbytes;
+ u8 *result = req->result;
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+ bool is_hmac = ctx->is_hmac;
+ struct cc_crypto_req cc_req = {};
+ struct cc_hw_desc desc[CC_MAX_HASH_SEQ_LEN];
+ unsigned int idx = 0;
+ int rc;
+ gfp_t flags = cc_gfp_flags(&req->base);
+
+ dev_dbg(dev, "===== %s-final (%d) ====\n", is_hmac ? "hmac" : "hash",
+ nbytes);
+
+ if (cc_map_req(dev, state, ctx)) {
+ dev_err(dev, "map_ahash_source() failed\n");
+ return -EINVAL;
+ }
+
+ if (cc_map_hash_request_final(ctx->drvdata, state, src, nbytes, 0,
+ flags)) {
+ dev_err(dev, "map_ahash_request_final() failed\n");
+ cc_unmap_req(dev, state, ctx);
+ return -ENOMEM;
+ }
+
+ if (cc_map_result(dev, state, digestsize)) {
+ dev_err(dev, "map_ahash_digest() failed\n");
+ cc_unmap_hash_request(dev, state, src, true);
+ cc_unmap_req(dev, state, ctx);
+ return -ENOMEM;
+ }
+
+ /* Setup request structure */
+ cc_req.user_cb = cc_hash_complete;
+ cc_req.user_arg = req;
+
+ idx = cc_restore_hash(desc, ctx, state, idx);
+
+ /* "DO-PAD" must be enabled only when writing current length to HW */
+ hw_desc_init(&desc[idx]);
+ set_cipher_do(&desc[idx], DO_PAD);
+ set_cipher_mode(&desc[idx], ctx->hw_mode);
+ set_dout_dlli(&desc[idx], state->digest_bytes_len_dma_addr,
+ ctx->drvdata->hash_len_sz, NS_BIT, 0);
+ set_setup_mode(&desc[idx], SETUP_WRITE_STATE1);
+ set_flow_mode(&desc[idx], S_HASH_to_DOUT);
+ idx++;
+
+ if (is_hmac)
+ idx = cc_fin_hmac(desc, req, idx);
+
+ idx = cc_fin_result(desc, req, idx);
+
+ rc = cc_send_request(ctx->drvdata, &cc_req, desc, idx, &req->base);
+ if (rc != -EINPROGRESS && rc != -EBUSY) {
+ dev_err(dev, "send_request() failed (rc=%d)\n", rc);
+ cc_unmap_hash_request(dev, state, src, true);
+ cc_unmap_result(dev, state, digestsize, result);
+ cc_unmap_req(dev, state, ctx);
+ }
+ return rc;
+}
+
+static int cc_hash_init(struct ahash_request *req)
+{
+ struct ahash_req_ctx *state = ahash_request_ctx(req);
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+
+ dev_dbg(dev, "===== init (%d) ====\n", req->nbytes);
+
+ cc_init_req(dev, state, ctx);
+
+ return 0;
+}
+
+static int cc_hash_setkey(struct crypto_ahash *ahash, const u8 *key,
+ unsigned int keylen)
+{
+ unsigned int hmac_pad_const[2] = { HMAC_IPAD_CONST, HMAC_OPAD_CONST };
+ struct cc_crypto_req cc_req = {};
+ struct cc_hash_ctx *ctx = NULL;
+ int blocksize = 0;
+ int digestsize = 0;
+ int i, idx = 0, rc = 0;
+ struct cc_hw_desc desc[CC_MAX_HASH_SEQ_LEN];
+ cc_sram_addr_t larval_addr;
+ struct device *dev;
+
+ ctx = crypto_ahash_ctx(ahash);
+ dev = drvdata_to_dev(ctx->drvdata);
+ dev_dbg(dev, "start keylen: %d", keylen);
+
+ blocksize = crypto_tfm_alg_blocksize(&ahash->base);
+ digestsize = crypto_ahash_digestsize(ahash);
+
+ larval_addr = cc_larval_digest_addr(ctx->drvdata, ctx->hash_mode);
+
+ /* The keylen value distinguishes HASH in case keylen is ZERO bytes,
+ * any NON-ZERO value utilizes HMAC flow
+ */
+ ctx->key_params.keylen = keylen;
+ ctx->key_params.key_dma_addr = 0;
+ ctx->is_hmac = true;
+
+ if (keylen) {
+ ctx->key_params.key_dma_addr =
+ dma_map_single(dev, (void *)key, keylen, DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, ctx->key_params.key_dma_addr)) {
+ dev_err(dev, "Mapping key va=0x%p len=%u for DMA failed\n",
+ key, keylen);
+ return -ENOMEM;
+ }
+ dev_dbg(dev, "mapping key-buffer: key_dma_addr=%pad keylen=%u\n",
+ &ctx->key_params.key_dma_addr, ctx->key_params.keylen);
+
+ if (keylen > blocksize) {
+ /* Load hash initial state */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], ctx->hw_mode);
+ set_din_sram(&desc[idx], larval_addr,
+ ctx->inter_digestsize);
+ set_flow_mode(&desc[idx], S_DIN_to_HASH);
+ set_setup_mode(&desc[idx], SETUP_LOAD_STATE0);
+ idx++;
+
+ /* Load the hash current length*/
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], ctx->hw_mode);
+ set_din_const(&desc[idx], 0, ctx->drvdata->hash_len_sz);
+ set_cipher_config1(&desc[idx], HASH_PADDING_ENABLED);
+ set_flow_mode(&desc[idx], S_DIN_to_HASH);
+ set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
+ idx++;
+
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_DLLI,
+ ctx->key_params.key_dma_addr, keylen,
+ NS_BIT);
+ set_flow_mode(&desc[idx], DIN_HASH);
+ idx++;
+
+ /* Get hashed key */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], ctx->hw_mode);
+ set_dout_dlli(&desc[idx], ctx->opad_tmp_keys_dma_addr,
+ digestsize, NS_BIT, 0);
+ set_flow_mode(&desc[idx], S_HASH_to_DOUT);
+ set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
+ set_cipher_config1(&desc[idx], HASH_PADDING_DISABLED);
+ cc_set_endianity(ctx->hash_mode, &desc[idx]);
+ idx++;
+
+ hw_desc_init(&desc[idx]);
+ set_din_const(&desc[idx], 0, (blocksize - digestsize));
+ set_flow_mode(&desc[idx], BYPASS);
+ set_dout_dlli(&desc[idx],
+ (ctx->opad_tmp_keys_dma_addr +
+ digestsize),
+ (blocksize - digestsize), NS_BIT, 0);
+ idx++;
+ } else {
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_DLLI,
+ ctx->key_params.key_dma_addr, keylen,
+ NS_BIT);
+ set_flow_mode(&desc[idx], BYPASS);
+ set_dout_dlli(&desc[idx], ctx->opad_tmp_keys_dma_addr,
+ keylen, NS_BIT, 0);
+ idx++;
+
+ if ((blocksize - keylen)) {
+ hw_desc_init(&desc[idx]);
+ set_din_const(&desc[idx], 0,
+ (blocksize - keylen));
+ set_flow_mode(&desc[idx], BYPASS);
+ set_dout_dlli(&desc[idx],
+ (ctx->opad_tmp_keys_dma_addr +
+ keylen), (blocksize - keylen),
+ NS_BIT, 0);
+ idx++;
+ }
+ }
+ } else {
+ hw_desc_init(&desc[idx]);
+ set_din_const(&desc[idx], 0, blocksize);
+ set_flow_mode(&desc[idx], BYPASS);
+ set_dout_dlli(&desc[idx], (ctx->opad_tmp_keys_dma_addr),
+ blocksize, NS_BIT, 0);
+ idx++;
+ }
+
+ rc = cc_send_sync_request(ctx->drvdata, &cc_req, desc, idx);
+ if (rc) {
+ dev_err(dev, "send_request() failed (rc=%d)\n", rc);
+ goto out;
+ }
+
+ /* calc derived HMAC key */
+ for (idx = 0, i = 0; i < 2; i++) {
+ /* Load hash initial state */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], ctx->hw_mode);
+ set_din_sram(&desc[idx], larval_addr, ctx->inter_digestsize);
+ set_flow_mode(&desc[idx], S_DIN_to_HASH);
+ set_setup_mode(&desc[idx], SETUP_LOAD_STATE0);
+ idx++;
+
+ /* Load the hash current length*/
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], ctx->hw_mode);
+ set_din_const(&desc[idx], 0, ctx->drvdata->hash_len_sz);
+ set_flow_mode(&desc[idx], S_DIN_to_HASH);
+ set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
+ idx++;
+
+ /* Prepare ipad key */
+ hw_desc_init(&desc[idx]);
+ set_xor_val(&desc[idx], hmac_pad_const[i]);
+ set_cipher_mode(&desc[idx], ctx->hw_mode);
+ set_flow_mode(&desc[idx], S_DIN_to_HASH);
+ set_setup_mode(&desc[idx], SETUP_LOAD_STATE1);
+ idx++;
+
+ /* Perform HASH update */
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_DLLI, ctx->opad_tmp_keys_dma_addr,
+ blocksize, NS_BIT);
+ set_cipher_mode(&desc[idx], ctx->hw_mode);
+ set_xor_active(&desc[idx]);
+ set_flow_mode(&desc[idx], DIN_HASH);
+ idx++;
+
+ /* Get the IPAD/OPAD xor key (Note, IPAD is the initial digest
+ * of the first HASH "update" state)
+ */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], ctx->hw_mode);
+ if (i > 0) /* Not first iteration */
+ set_dout_dlli(&desc[idx], ctx->opad_tmp_keys_dma_addr,
+ ctx->inter_digestsize, NS_BIT, 0);
+ else /* First iteration */
+ set_dout_dlli(&desc[idx], ctx->digest_buff_dma_addr,
+ ctx->inter_digestsize, NS_BIT, 0);
+ set_flow_mode(&desc[idx], S_HASH_to_DOUT);
+ set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
+ idx++;
+ }
+
+ rc = cc_send_sync_request(ctx->drvdata, &cc_req, desc, idx);
+
+out:
+ if (rc)
+ crypto_ahash_set_flags(ahash, CRYPTO_TFM_RES_BAD_KEY_LEN);
+
+ if (ctx->key_params.key_dma_addr) {
+ dma_unmap_single(dev, ctx->key_params.key_dma_addr,
+ ctx->key_params.keylen, DMA_TO_DEVICE);
+ dev_dbg(dev, "Unmapped key-buffer: key_dma_addr=%pad keylen=%u\n",
+ &ctx->key_params.key_dma_addr, ctx->key_params.keylen);
+ }
+ return rc;
+}
+
+static int cc_xcbc_setkey(struct crypto_ahash *ahash,
+ const u8 *key, unsigned int keylen)
+{
+ struct cc_crypto_req cc_req = {};
+ struct cc_hash_ctx *ctx = crypto_ahash_ctx(ahash);
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+ int rc = 0;
+ unsigned int idx = 0;
+ struct cc_hw_desc desc[CC_MAX_HASH_SEQ_LEN];
+
+ dev_dbg(dev, "===== setkey (%d) ====\n", keylen);
+
+ switch (keylen) {
+ case AES_KEYSIZE_128:
+ case AES_KEYSIZE_192:
+ case AES_KEYSIZE_256:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ctx->key_params.keylen = keylen;
+
+ ctx->key_params.key_dma_addr =
+ dma_map_single(dev, (void *)key, keylen, DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, ctx->key_params.key_dma_addr)) {
+ dev_err(dev, "Mapping key va=0x%p len=%u for DMA failed\n",
+ key, keylen);
+ return -ENOMEM;
+ }
+ dev_dbg(dev, "mapping key-buffer: key_dma_addr=%pad keylen=%u\n",
+ &ctx->key_params.key_dma_addr, ctx->key_params.keylen);
+
+ ctx->is_hmac = true;
+ /* 1. Load the AES key */
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_DLLI, ctx->key_params.key_dma_addr,
+ keylen, NS_BIT);
+ set_cipher_mode(&desc[idx], DRV_CIPHER_ECB);
+ set_cipher_config0(&desc[idx], DRV_CRYPTO_DIRECTION_ENCRYPT);
+ set_key_size_aes(&desc[idx], keylen);
+ set_flow_mode(&desc[idx], S_DIN_to_AES);
+ set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
+ idx++;
+
+ hw_desc_init(&desc[idx]);
+ set_din_const(&desc[idx], 0x01010101, CC_AES_128_BIT_KEY_SIZE);
+ set_flow_mode(&desc[idx], DIN_AES_DOUT);
+ set_dout_dlli(&desc[idx],
+ (ctx->opad_tmp_keys_dma_addr + XCBC_MAC_K1_OFFSET),
+ CC_AES_128_BIT_KEY_SIZE, NS_BIT, 0);
+ idx++;
+
+ hw_desc_init(&desc[idx]);
+ set_din_const(&desc[idx], 0x02020202, CC_AES_128_BIT_KEY_SIZE);
+ set_flow_mode(&desc[idx], DIN_AES_DOUT);
+ set_dout_dlli(&desc[idx],
+ (ctx->opad_tmp_keys_dma_addr + XCBC_MAC_K2_OFFSET),
+ CC_AES_128_BIT_KEY_SIZE, NS_BIT, 0);
+ idx++;
+
+ hw_desc_init(&desc[idx]);
+ set_din_const(&desc[idx], 0x03030303, CC_AES_128_BIT_KEY_SIZE);
+ set_flow_mode(&desc[idx], DIN_AES_DOUT);
+ set_dout_dlli(&desc[idx],
+ (ctx->opad_tmp_keys_dma_addr + XCBC_MAC_K3_OFFSET),
+ CC_AES_128_BIT_KEY_SIZE, NS_BIT, 0);
+ idx++;
+
+ rc = cc_send_sync_request(ctx->drvdata, &cc_req, desc, idx);
+
+ if (rc)
+ crypto_ahash_set_flags(ahash, CRYPTO_TFM_RES_BAD_KEY_LEN);
+
+ dma_unmap_single(dev, ctx->key_params.key_dma_addr,
+ ctx->key_params.keylen, DMA_TO_DEVICE);
+ dev_dbg(dev, "Unmapped key-buffer: key_dma_addr=%pad keylen=%u\n",
+ &ctx->key_params.key_dma_addr, ctx->key_params.keylen);
+
+ return rc;
+}
+
+static int cc_cmac_setkey(struct crypto_ahash *ahash,
+ const u8 *key, unsigned int keylen)
+{
+ struct cc_hash_ctx *ctx = crypto_ahash_ctx(ahash);
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+
+ dev_dbg(dev, "===== setkey (%d) ====\n", keylen);
+
+ ctx->is_hmac = true;
+
+ switch (keylen) {
+ case AES_KEYSIZE_128:
+ case AES_KEYSIZE_192:
+ case AES_KEYSIZE_256:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ctx->key_params.keylen = keylen;
+
+ /* STAT_PHASE_1: Copy key to ctx */
+
+ dma_sync_single_for_cpu(dev, ctx->opad_tmp_keys_dma_addr,
+ keylen, DMA_TO_DEVICE);
+
+ memcpy(ctx->opad_tmp_keys_buff, key, keylen);
+ if (keylen == 24) {
+ memset(ctx->opad_tmp_keys_buff + 24, 0,
+ CC_AES_KEY_SIZE_MAX - 24);
+ }
+
+ dma_sync_single_for_device(dev, ctx->opad_tmp_keys_dma_addr,
+ keylen, DMA_TO_DEVICE);
+
+ ctx->key_params.keylen = keylen;
+
+ return 0;
+}
+
+static void cc_free_ctx(struct cc_hash_ctx *ctx)
+{
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+
+ if (ctx->digest_buff_dma_addr) {
+ dma_unmap_single(dev, ctx->digest_buff_dma_addr,
+ sizeof(ctx->digest_buff), DMA_BIDIRECTIONAL);
+ dev_dbg(dev, "Unmapped digest-buffer: digest_buff_dma_addr=%pad\n",
+ &ctx->digest_buff_dma_addr);
+ ctx->digest_buff_dma_addr = 0;
+ }
+ if (ctx->opad_tmp_keys_dma_addr) {
+ dma_unmap_single(dev, ctx->opad_tmp_keys_dma_addr,
+ sizeof(ctx->opad_tmp_keys_buff),
+ DMA_BIDIRECTIONAL);
+ dev_dbg(dev, "Unmapped opad-digest: opad_tmp_keys_dma_addr=%pad\n",
+ &ctx->opad_tmp_keys_dma_addr);
+ ctx->opad_tmp_keys_dma_addr = 0;
+ }
+
+ ctx->key_params.keylen = 0;
+}
+
+static int cc_alloc_ctx(struct cc_hash_ctx *ctx)
+{
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+
+ ctx->key_params.keylen = 0;
+
+ ctx->digest_buff_dma_addr =
+ dma_map_single(dev, (void *)ctx->digest_buff,
+ sizeof(ctx->digest_buff), DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(dev, ctx->digest_buff_dma_addr)) {
+ dev_err(dev, "Mapping digest len %zu B at va=%pK for DMA failed\n",
+ sizeof(ctx->digest_buff), ctx->digest_buff);
+ goto fail;
+ }
+ dev_dbg(dev, "Mapped digest %zu B at va=%pK to dma=%pad\n",
+ sizeof(ctx->digest_buff), ctx->digest_buff,
+ &ctx->digest_buff_dma_addr);
+
+ ctx->opad_tmp_keys_dma_addr =
+ dma_map_single(dev, (void *)ctx->opad_tmp_keys_buff,
+ sizeof(ctx->opad_tmp_keys_buff),
+ DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(dev, ctx->opad_tmp_keys_dma_addr)) {
+ dev_err(dev, "Mapping opad digest %zu B at va=%pK for DMA failed\n",
+ sizeof(ctx->opad_tmp_keys_buff),
+ ctx->opad_tmp_keys_buff);
+ goto fail;
+ }
+ dev_dbg(dev, "Mapped opad_tmp_keys %zu B at va=%pK to dma=%pad\n",
+ sizeof(ctx->opad_tmp_keys_buff), ctx->opad_tmp_keys_buff,
+ &ctx->opad_tmp_keys_dma_addr);
+
+ ctx->is_hmac = false;
+ return 0;
+
+fail:
+ cc_free_ctx(ctx);
+ return -ENOMEM;
+}
+
+static int cc_cra_init(struct crypto_tfm *tfm)
+{
+ struct cc_hash_ctx *ctx = crypto_tfm_ctx(tfm);
+ struct hash_alg_common *hash_alg_common =
+ container_of(tfm->__crt_alg, struct hash_alg_common, base);
+ struct ahash_alg *ahash_alg =
+ container_of(hash_alg_common, struct ahash_alg, halg);
+ struct cc_hash_alg *cc_alg =
+ container_of(ahash_alg, struct cc_hash_alg, ahash_alg);
+
+ crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm),
+ sizeof(struct ahash_req_ctx));
+
+ ctx->hash_mode = cc_alg->hash_mode;
+ ctx->hw_mode = cc_alg->hw_mode;
+ ctx->inter_digestsize = cc_alg->inter_digestsize;
+ ctx->drvdata = cc_alg->drvdata;
+
+ return cc_alloc_ctx(ctx);
+}
+
+static void cc_cra_exit(struct crypto_tfm *tfm)
+{
+ struct cc_hash_ctx *ctx = crypto_tfm_ctx(tfm);
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+
+ dev_dbg(dev, "cc_cra_exit");
+ cc_free_ctx(ctx);
+}
+
+static int cc_mac_update(struct ahash_request *req)
+{
+ struct ahash_req_ctx *state = ahash_request_ctx(req);
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+ unsigned int block_size = crypto_tfm_alg_blocksize(&tfm->base);
+ struct cc_crypto_req cc_req = {};
+ struct cc_hw_desc desc[CC_MAX_HASH_SEQ_LEN];
+ int rc;
+ u32 idx = 0;
+ gfp_t flags = cc_gfp_flags(&req->base);
+
+ if (req->nbytes == 0) {
+ /* no real updates required */
+ return 0;
+ }
+
+ state->xcbc_count++;
+
+ rc = cc_map_hash_request_update(ctx->drvdata, state, req->src,
+ req->nbytes, block_size, flags);
+ if (rc) {
+ if (rc == 1) {
+ dev_dbg(dev, " data size not require HW update %x\n",
+ req->nbytes);
+ /* No hardware updates are required */
+ return 0;
+ }
+ dev_err(dev, "map_ahash_request_update() failed\n");
+ return -ENOMEM;
+ }
+
+ if (cc_map_req(dev, state, ctx)) {
+ dev_err(dev, "map_ahash_source() failed\n");
+ return -EINVAL;
+ }
+
+ if (ctx->hw_mode == DRV_CIPHER_XCBC_MAC)
+ cc_setup_xcbc(req, desc, &idx);
+ else
+ cc_setup_cmac(req, desc, &idx);
+
+ cc_set_desc(state, ctx, DIN_AES_DOUT, desc, true, &idx);
+
+ /* store the hash digest result in context */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], ctx->hw_mode);
+ set_dout_dlli(&desc[idx], state->digest_buff_dma_addr,
+ ctx->inter_digestsize, NS_BIT, 1);
+ set_queue_last_ind(ctx->drvdata, &desc[idx]);
+ set_flow_mode(&desc[idx], S_AES_to_DOUT);
+ set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
+ idx++;
+
+ /* Setup request structure */
+ cc_req.user_cb = (void *)cc_update_complete;
+ cc_req.user_arg = (void *)req;
+
+ rc = cc_send_request(ctx->drvdata, &cc_req, desc, idx, &req->base);
+ if (rc != -EINPROGRESS && rc != -EBUSY) {
+ dev_err(dev, "send_request() failed (rc=%d)\n", rc);
+ cc_unmap_hash_request(dev, state, req->src, true);
+ cc_unmap_req(dev, state, ctx);
+ }
+ return rc;
+}
+
+static int cc_mac_final(struct ahash_request *req)
+{
+ struct ahash_req_ctx *state = ahash_request_ctx(req);
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+ struct cc_crypto_req cc_req = {};
+ struct cc_hw_desc desc[CC_MAX_HASH_SEQ_LEN];
+ int idx = 0;
+ int rc = 0;
+ u32 key_size, key_len;
+ u32 digestsize = crypto_ahash_digestsize(tfm);
+ gfp_t flags = cc_gfp_flags(&req->base);
+ u32 rem_cnt = *cc_hash_buf_cnt(state);
+
+ if (ctx->hw_mode == DRV_CIPHER_XCBC_MAC) {
+ key_size = CC_AES_128_BIT_KEY_SIZE;
+ key_len = CC_AES_128_BIT_KEY_SIZE;
+ } else {
+ key_size = (ctx->key_params.keylen == 24) ? AES_MAX_KEY_SIZE :
+ ctx->key_params.keylen;
+ key_len = ctx->key_params.keylen;
+ }
+
+ dev_dbg(dev, "===== final xcbc reminder (%d) ====\n", rem_cnt);
+
+ if (cc_map_req(dev, state, ctx)) {
+ dev_err(dev, "map_ahash_source() failed\n");
+ return -EINVAL;
+ }
+
+ if (cc_map_hash_request_final(ctx->drvdata, state, req->src,
+ req->nbytes, 0, flags)) {
+ dev_err(dev, "map_ahash_request_final() failed\n");
+ cc_unmap_req(dev, state, ctx);
+ return -ENOMEM;
+ }
+
+ if (cc_map_result(dev, state, digestsize)) {
+ dev_err(dev, "map_ahash_digest() failed\n");
+ cc_unmap_hash_request(dev, state, req->src, true);
+ cc_unmap_req(dev, state, ctx);
+ return -ENOMEM;
+ }
+
+ /* Setup request structure */
+ cc_req.user_cb = (void *)cc_hash_complete;
+ cc_req.user_arg = (void *)req;
+
+ if (state->xcbc_count && rem_cnt == 0) {
+ /* Load key for ECB decryption */
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], DRV_CIPHER_ECB);
+ set_cipher_config0(&desc[idx], DRV_CRYPTO_DIRECTION_DECRYPT);
+ set_din_type(&desc[idx], DMA_DLLI,
+ (ctx->opad_tmp_keys_dma_addr + XCBC_MAC_K1_OFFSET),
+ key_size, NS_BIT);
+ set_key_size_aes(&desc[idx], key_len);
+ set_flow_mode(&desc[idx], S_DIN_to_AES);
+ set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
+ idx++;
+
+ /* Initiate decryption of block state to previous
+ * block_state-XOR-M[n]
+ */
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_DLLI, state->digest_buff_dma_addr,
+ CC_AES_BLOCK_SIZE, NS_BIT);
+ set_dout_dlli(&desc[idx], state->digest_buff_dma_addr,
+ CC_AES_BLOCK_SIZE, NS_BIT, 0);
+ set_flow_mode(&desc[idx], DIN_AES_DOUT);
+ idx++;
+
+ /* Memory Barrier: wait for axi write to complete */
+ hw_desc_init(&desc[idx]);
+ set_din_no_dma(&desc[idx], 0, 0xfffff0);
+ set_dout_no_dma(&desc[idx], 0, 0, 1);
+ idx++;
+ }
+
+ if (ctx->hw_mode == DRV_CIPHER_XCBC_MAC)
+ cc_setup_xcbc(req, desc, &idx);
+ else
+ cc_setup_cmac(req, desc, &idx);
+
+ if (state->xcbc_count == 0) {
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], ctx->hw_mode);
+ set_key_size_aes(&desc[idx], key_len);
+ set_cmac_size0_mode(&desc[idx]);
+ set_flow_mode(&desc[idx], S_DIN_to_AES);
+ idx++;
+ } else if (rem_cnt > 0) {
+ cc_set_desc(state, ctx, DIN_AES_DOUT, desc, false, &idx);
+ } else {
+ hw_desc_init(&desc[idx]);
+ set_din_const(&desc[idx], 0x00, CC_AES_BLOCK_SIZE);
+ set_flow_mode(&desc[idx], DIN_AES_DOUT);
+ idx++;
+ }
+
+ /* Get final MAC result */
+ hw_desc_init(&desc[idx]);
+ /* TODO */
+ set_dout_dlli(&desc[idx], state->digest_result_dma_addr,
+ digestsize, NS_BIT, 1);
+ set_queue_last_ind(ctx->drvdata, &desc[idx]);
+ set_flow_mode(&desc[idx], S_AES_to_DOUT);
+ set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
+ set_cipher_mode(&desc[idx], ctx->hw_mode);
+ idx++;
+
+ rc = cc_send_request(ctx->drvdata, &cc_req, desc, idx, &req->base);
+ if (rc != -EINPROGRESS && rc != -EBUSY) {
+ dev_err(dev, "send_request() failed (rc=%d)\n", rc);
+ cc_unmap_hash_request(dev, state, req->src, true);
+ cc_unmap_result(dev, state, digestsize, req->result);
+ cc_unmap_req(dev, state, ctx);
+ }
+ return rc;
+}
+
+static int cc_mac_finup(struct ahash_request *req)
+{
+ struct ahash_req_ctx *state = ahash_request_ctx(req);
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+ struct cc_crypto_req cc_req = {};
+ struct cc_hw_desc desc[CC_MAX_HASH_SEQ_LEN];
+ int idx = 0;
+ int rc = 0;
+ u32 key_len = 0;
+ u32 digestsize = crypto_ahash_digestsize(tfm);
+ gfp_t flags = cc_gfp_flags(&req->base);
+
+ dev_dbg(dev, "===== finup xcbc(%d) ====\n", req->nbytes);
+ if (state->xcbc_count > 0 && req->nbytes == 0) {
+ dev_dbg(dev, "No data to update. Call to fdx_mac_final\n");
+ return cc_mac_final(req);
+ }
+
+ if (cc_map_req(dev, state, ctx)) {
+ dev_err(dev, "map_ahash_source() failed\n");
+ return -EINVAL;
+ }
+
+ if (cc_map_hash_request_final(ctx->drvdata, state, req->src,
+ req->nbytes, 1, flags)) {
+ dev_err(dev, "map_ahash_request_final() failed\n");
+ cc_unmap_req(dev, state, ctx);
+ return -ENOMEM;
+ }
+ if (cc_map_result(dev, state, digestsize)) {
+ dev_err(dev, "map_ahash_digest() failed\n");
+ cc_unmap_hash_request(dev, state, req->src, true);
+ cc_unmap_req(dev, state, ctx);
+ return -ENOMEM;
+ }
+
+ /* Setup request structure */
+ cc_req.user_cb = (void *)cc_hash_complete;
+ cc_req.user_arg = (void *)req;
+
+ if (ctx->hw_mode == DRV_CIPHER_XCBC_MAC) {
+ key_len = CC_AES_128_BIT_KEY_SIZE;
+ cc_setup_xcbc(req, desc, &idx);
+ } else {
+ key_len = ctx->key_params.keylen;
+ cc_setup_cmac(req, desc, &idx);
+ }
+
+ if (req->nbytes == 0) {
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], ctx->hw_mode);
+ set_key_size_aes(&desc[idx], key_len);
+ set_cmac_size0_mode(&desc[idx]);
+ set_flow_mode(&desc[idx], S_DIN_to_AES);
+ idx++;
+ } else {
+ cc_set_desc(state, ctx, DIN_AES_DOUT, desc, false, &idx);
+ }
+
+ /* Get final MAC result */
+ hw_desc_init(&desc[idx]);
+ /* TODO */
+ set_dout_dlli(&desc[idx], state->digest_result_dma_addr,
+ digestsize, NS_BIT, 1);
+ set_queue_last_ind(ctx->drvdata, &desc[idx]);
+ set_flow_mode(&desc[idx], S_AES_to_DOUT);
+ set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
+ set_cipher_mode(&desc[idx], ctx->hw_mode);
+ idx++;
+
+ rc = cc_send_request(ctx->drvdata, &cc_req, desc, idx, &req->base);
+ if (rc != -EINPROGRESS && rc != -EBUSY) {
+ dev_err(dev, "send_request() failed (rc=%d)\n", rc);
+ cc_unmap_hash_request(dev, state, req->src, true);
+ cc_unmap_result(dev, state, digestsize, req->result);
+ cc_unmap_req(dev, state, ctx);
+ }
+ return rc;
+}
+
+static int cc_mac_digest(struct ahash_request *req)
+{
+ struct ahash_req_ctx *state = ahash_request_ctx(req);
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+ u32 digestsize = crypto_ahash_digestsize(tfm);
+ struct cc_crypto_req cc_req = {};
+ struct cc_hw_desc desc[CC_MAX_HASH_SEQ_LEN];
+ u32 key_len;
+ unsigned int idx = 0;
+ int rc;
+ gfp_t flags = cc_gfp_flags(&req->base);
+
+ dev_dbg(dev, "===== -digest mac (%d) ====\n", req->nbytes);
+
+ cc_init_req(dev, state, ctx);
+
+ if (cc_map_req(dev, state, ctx)) {
+ dev_err(dev, "map_ahash_source() failed\n");
+ return -ENOMEM;
+ }
+ if (cc_map_result(dev, state, digestsize)) {
+ dev_err(dev, "map_ahash_digest() failed\n");
+ cc_unmap_req(dev, state, ctx);
+ return -ENOMEM;
+ }
+
+ if (cc_map_hash_request_final(ctx->drvdata, state, req->src,
+ req->nbytes, 1, flags)) {
+ dev_err(dev, "map_ahash_request_final() failed\n");
+ cc_unmap_req(dev, state, ctx);
+ return -ENOMEM;
+ }
+
+ /* Setup request structure */
+ cc_req.user_cb = (void *)cc_digest_complete;
+ cc_req.user_arg = (void *)req;
+
+ if (ctx->hw_mode == DRV_CIPHER_XCBC_MAC) {
+ key_len = CC_AES_128_BIT_KEY_SIZE;
+ cc_setup_xcbc(req, desc, &idx);
+ } else {
+ key_len = ctx->key_params.keylen;
+ cc_setup_cmac(req, desc, &idx);
+ }
+
+ if (req->nbytes == 0) {
+ hw_desc_init(&desc[idx]);
+ set_cipher_mode(&desc[idx], ctx->hw_mode);
+ set_key_size_aes(&desc[idx], key_len);
+ set_cmac_size0_mode(&desc[idx]);
+ set_flow_mode(&desc[idx], S_DIN_to_AES);
+ idx++;
+ } else {
+ cc_set_desc(state, ctx, DIN_AES_DOUT, desc, false, &idx);
+ }
+
+ /* Get final MAC result */
+ hw_desc_init(&desc[idx]);
+ set_dout_dlli(&desc[idx], state->digest_result_dma_addr,
+ CC_AES_BLOCK_SIZE, NS_BIT, 1);
+ set_queue_last_ind(ctx->drvdata, &desc[idx]);
+ set_flow_mode(&desc[idx], S_AES_to_DOUT);
+ set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
+ set_cipher_config0(&desc[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
+ set_cipher_mode(&desc[idx], ctx->hw_mode);
+ idx++;
+
+ rc = cc_send_request(ctx->drvdata, &cc_req, desc, idx, &req->base);
+ if (rc != -EINPROGRESS && rc != -EBUSY) {
+ dev_err(dev, "send_request() failed (rc=%d)\n", rc);
+ cc_unmap_hash_request(dev, state, req->src, true);
+ cc_unmap_result(dev, state, digestsize, req->result);
+ cc_unmap_req(dev, state, ctx);
+ }
+ return rc;
+}
+
+static int cc_hash_export(struct ahash_request *req, void *out)
+{
+ struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
+ struct cc_hash_ctx *ctx = crypto_ahash_ctx(ahash);
+ struct ahash_req_ctx *state = ahash_request_ctx(req);
+ u8 *curr_buff = cc_hash_buf(state);
+ u32 curr_buff_cnt = *cc_hash_buf_cnt(state);
+ const u32 tmp = CC_EXPORT_MAGIC;
+
+ memcpy(out, &tmp, sizeof(u32));
+ out += sizeof(u32);
+
+ memcpy(out, state->digest_buff, ctx->inter_digestsize);
+ out += ctx->inter_digestsize;
+
+ memcpy(out, state->digest_bytes_len, ctx->drvdata->hash_len_sz);
+ out += ctx->drvdata->hash_len_sz;
+
+ memcpy(out, &curr_buff_cnt, sizeof(u32));
+ out += sizeof(u32);
+
+ memcpy(out, curr_buff, curr_buff_cnt);
+
+ return 0;
+}
+
+static int cc_hash_import(struct ahash_request *req, const void *in)
+{
+ struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
+ struct cc_hash_ctx *ctx = crypto_ahash_ctx(ahash);
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+ struct ahash_req_ctx *state = ahash_request_ctx(req);
+ u32 tmp;
+
+ memcpy(&tmp, in, sizeof(u32));
+ if (tmp != CC_EXPORT_MAGIC)
+ return -EINVAL;
+ in += sizeof(u32);
+
+ cc_init_req(dev, state, ctx);
+
+ memcpy(state->digest_buff, in, ctx->inter_digestsize);
+ in += ctx->inter_digestsize;
+
+ memcpy(state->digest_bytes_len, in, ctx->drvdata->hash_len_sz);
+ in += ctx->drvdata->hash_len_sz;
+
+ /* Sanity check the data as much as possible */
+ memcpy(&tmp, in, sizeof(u32));
+ if (tmp > CC_MAX_HASH_BLCK_SIZE)
+ return -EINVAL;
+ in += sizeof(u32);
+
+ state->buf_cnt[0] = tmp;
+ memcpy(state->buffers[0], in, tmp);
+
+ return 0;
+}
+
+struct cc_hash_template {
+ char name[CRYPTO_MAX_ALG_NAME];
+ char driver_name[CRYPTO_MAX_ALG_NAME];
+ char mac_name[CRYPTO_MAX_ALG_NAME];
+ char mac_driver_name[CRYPTO_MAX_ALG_NAME];
+ unsigned int blocksize;
+ bool synchronize;
+ struct ahash_alg template_ahash;
+ int hash_mode;
+ int hw_mode;
+ int inter_digestsize;
+ struct cc_drvdata *drvdata;
+ u32 min_hw_rev;
+};
+
+#define CC_STATE_SIZE(_x) \
+ ((_x) + HASH_MAX_LEN_SIZE + CC_MAX_HASH_BLCK_SIZE + (2 * sizeof(u32)))
+
+/* hash descriptors */
+static struct cc_hash_template driver_hash[] = {
+ //Asynchronize hash template
+ {
+ .name = "sha1",
+ .driver_name = "sha1-ccree",
+ .mac_name = "hmac(sha1)",
+ .mac_driver_name = "hmac-sha1-ccree",
+ .blocksize = SHA1_BLOCK_SIZE,
+ .synchronize = false,
+ .template_ahash = {
+ .init = cc_hash_init,
+ .update = cc_hash_update,
+ .final = cc_hash_final,
+ .finup = cc_hash_finup,
+ .digest = cc_hash_digest,
+ .export = cc_hash_export,
+ .import = cc_hash_import,
+ .setkey = cc_hash_setkey,
+ .halg = {
+ .digestsize = SHA1_DIGEST_SIZE,
+ .statesize = CC_STATE_SIZE(SHA1_DIGEST_SIZE),
+ },
+ },
+ .hash_mode = DRV_HASH_SHA1,
+ .hw_mode = DRV_HASH_HW_SHA1,
+ .inter_digestsize = SHA1_DIGEST_SIZE,
+ .min_hw_rev = CC_HW_REV_630,
+ },
+ {
+ .name = "sha256",
+ .driver_name = "sha256-ccree",
+ .mac_name = "hmac(sha256)",
+ .mac_driver_name = "hmac-sha256-ccree",
+ .blocksize = SHA256_BLOCK_SIZE,
+ .template_ahash = {
+ .init = cc_hash_init,
+ .update = cc_hash_update,
+ .final = cc_hash_final,
+ .finup = cc_hash_finup,
+ .digest = cc_hash_digest,
+ .export = cc_hash_export,
+ .import = cc_hash_import,
+ .setkey = cc_hash_setkey,
+ .halg = {
+ .digestsize = SHA256_DIGEST_SIZE,
+ .statesize = CC_STATE_SIZE(SHA256_DIGEST_SIZE)
+ },
+ },
+ .hash_mode = DRV_HASH_SHA256,
+ .hw_mode = DRV_HASH_HW_SHA256,
+ .inter_digestsize = SHA256_DIGEST_SIZE,
+ .min_hw_rev = CC_HW_REV_630,
+ },
+ {
+ .name = "sha224",
+ .driver_name = "sha224-ccree",
+ .mac_name = "hmac(sha224)",
+ .mac_driver_name = "hmac-sha224-ccree",
+ .blocksize = SHA224_BLOCK_SIZE,
+ .template_ahash = {
+ .init = cc_hash_init,
+ .update = cc_hash_update,
+ .final = cc_hash_final,
+ .finup = cc_hash_finup,
+ .digest = cc_hash_digest,
+ .export = cc_hash_export,
+ .import = cc_hash_import,
+ .setkey = cc_hash_setkey,
+ .halg = {
+ .digestsize = SHA224_DIGEST_SIZE,
+ .statesize = CC_STATE_SIZE(SHA224_DIGEST_SIZE),
+ },
+ },
+ .hash_mode = DRV_HASH_SHA224,
+ .hw_mode = DRV_HASH_HW_SHA256,
+ .inter_digestsize = SHA256_DIGEST_SIZE,
+ .min_hw_rev = CC_HW_REV_630,
+ },
+ {
+ .name = "sha384",
+ .driver_name = "sha384-ccree",
+ .mac_name = "hmac(sha384)",
+ .mac_driver_name = "hmac-sha384-ccree",
+ .blocksize = SHA384_BLOCK_SIZE,
+ .template_ahash = {
+ .init = cc_hash_init,
+ .update = cc_hash_update,
+ .final = cc_hash_final,
+ .finup = cc_hash_finup,
+ .digest = cc_hash_digest,
+ .export = cc_hash_export,
+ .import = cc_hash_import,
+ .setkey = cc_hash_setkey,
+ .halg = {
+ .digestsize = SHA384_DIGEST_SIZE,
+ .statesize = CC_STATE_SIZE(SHA384_DIGEST_SIZE),
+ },
+ },
+ .hash_mode = DRV_HASH_SHA384,
+ .hw_mode = DRV_HASH_HW_SHA512,
+ .inter_digestsize = SHA512_DIGEST_SIZE,
+ .min_hw_rev = CC_HW_REV_712,
+ },
+ {
+ .name = "sha512",
+ .driver_name = "sha512-ccree",
+ .mac_name = "hmac(sha512)",
+ .mac_driver_name = "hmac-sha512-ccree",
+ .blocksize = SHA512_BLOCK_SIZE,
+ .template_ahash = {
+ .init = cc_hash_init,
+ .update = cc_hash_update,
+ .final = cc_hash_final,
+ .finup = cc_hash_finup,
+ .digest = cc_hash_digest,
+ .export = cc_hash_export,
+ .import = cc_hash_import,
+ .setkey = cc_hash_setkey,
+ .halg = {
+ .digestsize = SHA512_DIGEST_SIZE,
+ .statesize = CC_STATE_SIZE(SHA512_DIGEST_SIZE),
+ },
+ },
+ .hash_mode = DRV_HASH_SHA512,
+ .hw_mode = DRV_HASH_HW_SHA512,
+ .inter_digestsize = SHA512_DIGEST_SIZE,
+ .min_hw_rev = CC_HW_REV_712,
+ },
+ {
+ .name = "md5",
+ .driver_name = "md5-ccree",
+ .mac_name = "hmac(md5)",
+ .mac_driver_name = "hmac-md5-ccree",
+ .blocksize = MD5_HMAC_BLOCK_SIZE,
+ .template_ahash = {
+ .init = cc_hash_init,
+ .update = cc_hash_update,
+ .final = cc_hash_final,
+ .finup = cc_hash_finup,
+ .digest = cc_hash_digest,
+ .export = cc_hash_export,
+ .import = cc_hash_import,
+ .setkey = cc_hash_setkey,
+ .halg = {
+ .digestsize = MD5_DIGEST_SIZE,
+ .statesize = CC_STATE_SIZE(MD5_DIGEST_SIZE),
+ },
+ },
+ .hash_mode = DRV_HASH_MD5,
+ .hw_mode = DRV_HASH_HW_MD5,
+ .inter_digestsize = MD5_DIGEST_SIZE,
+ .min_hw_rev = CC_HW_REV_630,
+ },
+ {
+ .mac_name = "xcbc(aes)",
+ .mac_driver_name = "xcbc-aes-ccree",
+ .blocksize = AES_BLOCK_SIZE,
+ .template_ahash = {
+ .init = cc_hash_init,
+ .update = cc_mac_update,
+ .final = cc_mac_final,
+ .finup = cc_mac_finup,
+ .digest = cc_mac_digest,
+ .setkey = cc_xcbc_setkey,
+ .export = cc_hash_export,
+ .import = cc_hash_import,
+ .halg = {
+ .digestsize = AES_BLOCK_SIZE,
+ .statesize = CC_STATE_SIZE(AES_BLOCK_SIZE),
+ },
+ },
+ .hash_mode = DRV_HASH_NULL,
+ .hw_mode = DRV_CIPHER_XCBC_MAC,
+ .inter_digestsize = AES_BLOCK_SIZE,
+ .min_hw_rev = CC_HW_REV_630,
+ },
+ {
+ .mac_name = "cmac(aes)",
+ .mac_driver_name = "cmac-aes-ccree",
+ .blocksize = AES_BLOCK_SIZE,
+ .template_ahash = {
+ .init = cc_hash_init,
+ .update = cc_mac_update,
+ .final = cc_mac_final,
+ .finup = cc_mac_finup,
+ .digest = cc_mac_digest,
+ .setkey = cc_cmac_setkey,
+ .export = cc_hash_export,
+ .import = cc_hash_import,
+ .halg = {
+ .digestsize = AES_BLOCK_SIZE,
+ .statesize = CC_STATE_SIZE(AES_BLOCK_SIZE),
+ },
+ },
+ .hash_mode = DRV_HASH_NULL,
+ .hw_mode = DRV_CIPHER_CMAC,
+ .inter_digestsize = AES_BLOCK_SIZE,
+ .min_hw_rev = CC_HW_REV_630,
+ },
+};
+
+static struct cc_hash_alg *cc_alloc_hash_alg(struct cc_hash_template *template,
+ struct device *dev, bool keyed)
+{
+ struct cc_hash_alg *t_crypto_alg;
+ struct crypto_alg *alg;
+ struct ahash_alg *halg;
+
+ t_crypto_alg = kzalloc(sizeof(*t_crypto_alg), GFP_KERNEL);
+ if (!t_crypto_alg)
+ return ERR_PTR(-ENOMEM);
+
+ t_crypto_alg->ahash_alg = template->template_ahash;
+ halg = &t_crypto_alg->ahash_alg;
+ alg = &halg->halg.base;
+
+ if (keyed) {
+ snprintf(alg->cra_name, CRYPTO_MAX_ALG_NAME, "%s",
+ template->mac_name);
+ snprintf(alg->cra_driver_name, CRYPTO_MAX_ALG_NAME, "%s",
+ template->mac_driver_name);
+ } else {
+ halg->setkey = NULL;
+ snprintf(alg->cra_name, CRYPTO_MAX_ALG_NAME, "%s",
+ template->name);
+ snprintf(alg->cra_driver_name, CRYPTO_MAX_ALG_NAME, "%s",
+ template->driver_name);
+ }
+ alg->cra_module = THIS_MODULE;
+ alg->cra_ctxsize = sizeof(struct cc_hash_ctx);
+ alg->cra_priority = CC_CRA_PRIO;
+ alg->cra_blocksize = template->blocksize;
+ alg->cra_alignmask = 0;
+ alg->cra_exit = cc_cra_exit;
+
+ alg->cra_init = cc_cra_init;
+ alg->cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_TYPE_AHASH |
+ CRYPTO_ALG_KERN_DRIVER_ONLY;
+ alg->cra_type = &crypto_ahash_type;
+
+ t_crypto_alg->hash_mode = template->hash_mode;
+ t_crypto_alg->hw_mode = template->hw_mode;
+ t_crypto_alg->inter_digestsize = template->inter_digestsize;
+
+ return t_crypto_alg;
+}
+
+int cc_init_hash_sram(struct cc_drvdata *drvdata)
+{
+ struct cc_hash_handle *hash_handle = drvdata->hash_handle;
+ cc_sram_addr_t sram_buff_ofs = hash_handle->digest_len_sram_addr;
+ unsigned int larval_seq_len = 0;
+ struct cc_hw_desc larval_seq[CC_DIGEST_SIZE_MAX / sizeof(u32)];
+ bool large_sha_supported = (drvdata->hw_rev >= CC_HW_REV_712);
+ int rc = 0;
+
+ /* Copy-to-sram digest-len */
+ cc_set_sram_desc(digest_len_init, sram_buff_ofs,
+ ARRAY_SIZE(digest_len_init), larval_seq,
+ &larval_seq_len);
+ rc = send_request_init(drvdata, larval_seq, larval_seq_len);
+ if (rc)
+ goto init_digest_const_err;
+
+ sram_buff_ofs += sizeof(digest_len_init);
+ larval_seq_len = 0;
+
+ if (large_sha_supported) {
+ /* Copy-to-sram digest-len for sha384/512 */
+ cc_set_sram_desc(digest_len_sha512_init, sram_buff_ofs,
+ ARRAY_SIZE(digest_len_sha512_init),
+ larval_seq, &larval_seq_len);
+ rc = send_request_init(drvdata, larval_seq, larval_seq_len);
+ if (rc)
+ goto init_digest_const_err;
+
+ sram_buff_ofs += sizeof(digest_len_sha512_init);
+ larval_seq_len = 0;
+ }
+
+ /* The initial digests offset */
+ hash_handle->larval_digest_sram_addr = sram_buff_ofs;
+
+ /* Copy-to-sram initial SHA* digests */
+ cc_set_sram_desc(md5_init, sram_buff_ofs, ARRAY_SIZE(md5_init),
+ larval_seq, &larval_seq_len);
+ rc = send_request_init(drvdata, larval_seq, larval_seq_len);
+ if (rc)
+ goto init_digest_const_err;
+ sram_buff_ofs += sizeof(md5_init);
+ larval_seq_len = 0;
+
+ cc_set_sram_desc(sha1_init, sram_buff_ofs,
+ ARRAY_SIZE(sha1_init), larval_seq,
+ &larval_seq_len);
+ rc = send_request_init(drvdata, larval_seq, larval_seq_len);
+ if (rc)
+ goto init_digest_const_err;
+ sram_buff_ofs += sizeof(sha1_init);
+ larval_seq_len = 0;
+
+ cc_set_sram_desc(sha224_init, sram_buff_ofs,
+ ARRAY_SIZE(sha224_init), larval_seq,
+ &larval_seq_len);
+ rc = send_request_init(drvdata, larval_seq, larval_seq_len);
+ if (rc)
+ goto init_digest_const_err;
+ sram_buff_ofs += sizeof(sha224_init);
+ larval_seq_len = 0;
+
+ cc_set_sram_desc(sha256_init, sram_buff_ofs,
+ ARRAY_SIZE(sha256_init), larval_seq,
+ &larval_seq_len);
+ rc = send_request_init(drvdata, larval_seq, larval_seq_len);
+ if (rc)
+ goto init_digest_const_err;
+ sram_buff_ofs += sizeof(sha256_init);
+ larval_seq_len = 0;
+
+ if (large_sha_supported) {
+ cc_set_sram_desc((u32 *)sha384_init, sram_buff_ofs,
+ (ARRAY_SIZE(sha384_init) * 2), larval_seq,
+ &larval_seq_len);
+ rc = send_request_init(drvdata, larval_seq, larval_seq_len);
+ if (rc)
+ goto init_digest_const_err;
+ sram_buff_ofs += sizeof(sha384_init);
+ larval_seq_len = 0;
+
+ cc_set_sram_desc((u32 *)sha512_init, sram_buff_ofs,
+ (ARRAY_SIZE(sha512_init) * 2), larval_seq,
+ &larval_seq_len);
+ rc = send_request_init(drvdata, larval_seq, larval_seq_len);
+ if (rc)
+ goto init_digest_const_err;
+ }
+
+init_digest_const_err:
+ return rc;
+}
+
+static void __init cc_swap_dwords(u32 *buf, unsigned long size)
+{
+ int i;
+ u32 tmp;
+
+ for (i = 0; i < size; i += 2) {
+ tmp = buf[i];
+ buf[i] = buf[i + 1];
+ buf[i + 1] = tmp;
+ }
+}
+
+/*
+ * Due to the way the HW works we need to swap every
+ * double word in the SHA384 and SHA512 larval hashes
+ */
+void __init cc_hash_global_init(void)
+{
+ cc_swap_dwords((u32 *)&sha384_init, (ARRAY_SIZE(sha384_init) * 2));
+ cc_swap_dwords((u32 *)&sha512_init, (ARRAY_SIZE(sha512_init) * 2));
+}
+
+int cc_hash_alloc(struct cc_drvdata *drvdata)
+{
+ struct cc_hash_handle *hash_handle;
+ cc_sram_addr_t sram_buff;
+ u32 sram_size_to_alloc;
+ struct device *dev = drvdata_to_dev(drvdata);
+ int rc = 0;
+ int alg;
+
+ hash_handle = kzalloc(sizeof(*hash_handle), GFP_KERNEL);
+ if (!hash_handle)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&hash_handle->hash_list);
+ drvdata->hash_handle = hash_handle;
+
+ sram_size_to_alloc = sizeof(digest_len_init) +
+ sizeof(md5_init) +
+ sizeof(sha1_init) +
+ sizeof(sha224_init) +
+ sizeof(sha256_init);
+
+ if (drvdata->hw_rev >= CC_HW_REV_712)
+ sram_size_to_alloc += sizeof(digest_len_sha512_init) +
+ sizeof(sha384_init) + sizeof(sha512_init);
+
+ sram_buff = cc_sram_alloc(drvdata, sram_size_to_alloc);
+ if (sram_buff == NULL_SRAM_ADDR) {
+ dev_err(dev, "SRAM pool exhausted\n");
+ rc = -ENOMEM;
+ goto fail;
+ }
+
+ /* The initial digest-len offset */
+ hash_handle->digest_len_sram_addr = sram_buff;
+
+ /*must be set before the alg registration as it is being used there*/
+ rc = cc_init_hash_sram(drvdata);
+ if (rc) {
+ dev_err(dev, "Init digest CONST failed (rc=%d)\n", rc);
+ goto fail;
+ }
+
+ /* ahash registration */
+ for (alg = 0; alg < ARRAY_SIZE(driver_hash); alg++) {
+ struct cc_hash_alg *t_alg;
+ int hw_mode = driver_hash[alg].hw_mode;
+
+ /* We either support both HASH and MAC or none */
+ if (driver_hash[alg].min_hw_rev > drvdata->hw_rev)
+ continue;
+
+ /* register hmac version */
+ t_alg = cc_alloc_hash_alg(&driver_hash[alg], dev, true);
+ if (IS_ERR(t_alg)) {
+ rc = PTR_ERR(t_alg);
+ dev_err(dev, "%s alg allocation failed\n",
+ driver_hash[alg].driver_name);
+ goto fail;
+ }
+ t_alg->drvdata = drvdata;
+
+ rc = crypto_register_ahash(&t_alg->ahash_alg);
+ if (rc) {
+ dev_err(dev, "%s alg registration failed\n",
+ driver_hash[alg].driver_name);
+ kfree(t_alg);
+ goto fail;
+ } else {
+ list_add_tail(&t_alg->entry, &hash_handle->hash_list);
+ }
+
+ if (hw_mode == DRV_CIPHER_XCBC_MAC ||
+ hw_mode == DRV_CIPHER_CMAC)
+ continue;
+
+ /* register hash version */
+ t_alg = cc_alloc_hash_alg(&driver_hash[alg], dev, false);
+ if (IS_ERR(t_alg)) {
+ rc = PTR_ERR(t_alg);
+ dev_err(dev, "%s alg allocation failed\n",
+ driver_hash[alg].driver_name);
+ goto fail;
+ }
+ t_alg->drvdata = drvdata;
+
+ rc = crypto_register_ahash(&t_alg->ahash_alg);
+ if (rc) {
+ dev_err(dev, "%s alg registration failed\n",
+ driver_hash[alg].driver_name);
+ kfree(t_alg);
+ goto fail;
+ } else {
+ list_add_tail(&t_alg->entry, &hash_handle->hash_list);
+ }
+ }
+
+ return 0;
+
+fail:
+ kfree(drvdata->hash_handle);
+ drvdata->hash_handle = NULL;
+ return rc;
+}
+
+int cc_hash_free(struct cc_drvdata *drvdata)
+{
+ struct cc_hash_alg *t_hash_alg, *hash_n;
+ struct cc_hash_handle *hash_handle = drvdata->hash_handle;
+
+ if (hash_handle) {
+ list_for_each_entry_safe(t_hash_alg, hash_n,
+ &hash_handle->hash_list, entry) {
+ crypto_unregister_ahash(&t_hash_alg->ahash_alg);
+ list_del(&t_hash_alg->entry);
+ kfree(t_hash_alg);
+ }
+
+ kfree(hash_handle);
+ drvdata->hash_handle = NULL;
+ }
+ return 0;
+}
+
+static void cc_setup_xcbc(struct ahash_request *areq, struct cc_hw_desc desc[],
+ unsigned int *seq_size)
+{
+ unsigned int idx = *seq_size;
+ struct ahash_req_ctx *state = ahash_request_ctx(areq);
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq);
+ struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
+
+ /* Setup XCBC MAC K1 */
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_DLLI, (ctx->opad_tmp_keys_dma_addr +
+ XCBC_MAC_K1_OFFSET),
+ CC_AES_128_BIT_KEY_SIZE, NS_BIT);
+ set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
+ set_cipher_mode(&desc[idx], DRV_CIPHER_XCBC_MAC);
+ set_cipher_config0(&desc[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
+ set_key_size_aes(&desc[idx], CC_AES_128_BIT_KEY_SIZE);
+ set_flow_mode(&desc[idx], S_DIN_to_AES);
+ idx++;
+
+ /* Setup XCBC MAC K2 */
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_DLLI,
+ (ctx->opad_tmp_keys_dma_addr + XCBC_MAC_K2_OFFSET),
+ CC_AES_128_BIT_KEY_SIZE, NS_BIT);
+ set_setup_mode(&desc[idx], SETUP_LOAD_STATE1);
+ set_cipher_mode(&desc[idx], DRV_CIPHER_XCBC_MAC);
+ set_cipher_config0(&desc[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
+ set_key_size_aes(&desc[idx], CC_AES_128_BIT_KEY_SIZE);
+ set_flow_mode(&desc[idx], S_DIN_to_AES);
+ idx++;
+
+ /* Setup XCBC MAC K3 */
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_DLLI,
+ (ctx->opad_tmp_keys_dma_addr + XCBC_MAC_K3_OFFSET),
+ CC_AES_128_BIT_KEY_SIZE, NS_BIT);
+ set_setup_mode(&desc[idx], SETUP_LOAD_STATE2);
+ set_cipher_mode(&desc[idx], DRV_CIPHER_XCBC_MAC);
+ set_cipher_config0(&desc[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
+ set_key_size_aes(&desc[idx], CC_AES_128_BIT_KEY_SIZE);
+ set_flow_mode(&desc[idx], S_DIN_to_AES);
+ idx++;
+
+ /* Loading MAC state */
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_DLLI, state->digest_buff_dma_addr,
+ CC_AES_BLOCK_SIZE, NS_BIT);
+ set_setup_mode(&desc[idx], SETUP_LOAD_STATE0);
+ set_cipher_mode(&desc[idx], DRV_CIPHER_XCBC_MAC);
+ set_cipher_config0(&desc[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
+ set_key_size_aes(&desc[idx], CC_AES_128_BIT_KEY_SIZE);
+ set_flow_mode(&desc[idx], S_DIN_to_AES);
+ idx++;
+ *seq_size = idx;
+}
+
+static void cc_setup_cmac(struct ahash_request *areq, struct cc_hw_desc desc[],
+ unsigned int *seq_size)
+{
+ unsigned int idx = *seq_size;
+ struct ahash_req_ctx *state = ahash_request_ctx(areq);
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq);
+ struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
+
+ /* Setup CMAC Key */
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_DLLI, ctx->opad_tmp_keys_dma_addr,
+ ((ctx->key_params.keylen == 24) ? AES_MAX_KEY_SIZE :
+ ctx->key_params.keylen), NS_BIT);
+ set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
+ set_cipher_mode(&desc[idx], DRV_CIPHER_CMAC);
+ set_cipher_config0(&desc[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
+ set_key_size_aes(&desc[idx], ctx->key_params.keylen);
+ set_flow_mode(&desc[idx], S_DIN_to_AES);
+ idx++;
+
+ /* Load MAC state */
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_DLLI, state->digest_buff_dma_addr,
+ CC_AES_BLOCK_SIZE, NS_BIT);
+ set_setup_mode(&desc[idx], SETUP_LOAD_STATE0);
+ set_cipher_mode(&desc[idx], DRV_CIPHER_CMAC);
+ set_cipher_config0(&desc[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
+ set_key_size_aes(&desc[idx], ctx->key_params.keylen);
+ set_flow_mode(&desc[idx], S_DIN_to_AES);
+ idx++;
+ *seq_size = idx;
+}
+
+static void cc_set_desc(struct ahash_req_ctx *areq_ctx,
+ struct cc_hash_ctx *ctx, unsigned int flow_mode,
+ struct cc_hw_desc desc[], bool is_not_last_data,
+ unsigned int *seq_size)
+{
+ unsigned int idx = *seq_size;
+ struct device *dev = drvdata_to_dev(ctx->drvdata);
+
+ if (areq_ctx->data_dma_buf_type == CC_DMA_BUF_DLLI) {
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_DLLI,
+ sg_dma_address(areq_ctx->curr_sg),
+ areq_ctx->curr_sg->length, NS_BIT);
+ set_flow_mode(&desc[idx], flow_mode);
+ idx++;
+ } else {
+ if (areq_ctx->data_dma_buf_type == CC_DMA_BUF_NULL) {
+ dev_dbg(dev, " NULL mode\n");
+ /* nothing to build */
+ return;
+ }
+ /* bypass */
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_DLLI,
+ areq_ctx->mlli_params.mlli_dma_addr,
+ areq_ctx->mlli_params.mlli_len, NS_BIT);
+ set_dout_sram(&desc[idx], ctx->drvdata->mlli_sram_addr,
+ areq_ctx->mlli_params.mlli_len);
+ set_flow_mode(&desc[idx], BYPASS);
+ idx++;
+ /* process */
+ hw_desc_init(&desc[idx]);
+ set_din_type(&desc[idx], DMA_MLLI,
+ ctx->drvdata->mlli_sram_addr,
+ areq_ctx->mlli_nents, NS_BIT);
+ set_flow_mode(&desc[idx], flow_mode);
+ idx++;
+ }
+ if (is_not_last_data)
+ set_din_not_last_indication(&desc[(idx - 1)]);
+ /* return updated desc sequence size */
+ *seq_size = idx;
+}
+
+static const void *cc_larval_digest(struct device *dev, u32 mode)
+{
+ switch (mode) {
+ case DRV_HASH_MD5:
+ return md5_init;
+ case DRV_HASH_SHA1:
+ return sha1_init;
+ case DRV_HASH_SHA224:
+ return sha224_init;
+ case DRV_HASH_SHA256:
+ return sha256_init;
+ case DRV_HASH_SHA384:
+ return sha384_init;
+ case DRV_HASH_SHA512:
+ return sha512_init;
+ default:
+ dev_err(dev, "Invalid hash mode (%d)\n", mode);
+ return md5_init;
+ }
+}
+
+/*!
+ * Gets the address of the initial digest in SRAM
+ * according to the given hash mode
+ *
+ * \param drvdata
+ * \param mode The Hash mode. Supported modes: MD5/SHA1/SHA224/SHA256
+ *
+ * \return u32 The address of the initial digest in SRAM
+ */
+cc_sram_addr_t cc_larval_digest_addr(void *drvdata, u32 mode)
+{
+ struct cc_drvdata *_drvdata = (struct cc_drvdata *)drvdata;
+ struct cc_hash_handle *hash_handle = _drvdata->hash_handle;
+ struct device *dev = drvdata_to_dev(_drvdata);
+
+ switch (mode) {
+ case DRV_HASH_NULL:
+ break; /*Ignore*/
+ case DRV_HASH_MD5:
+ return (hash_handle->larval_digest_sram_addr);
+ case DRV_HASH_SHA1:
+ return (hash_handle->larval_digest_sram_addr +
+ sizeof(md5_init));
+ case DRV_HASH_SHA224:
+ return (hash_handle->larval_digest_sram_addr +
+ sizeof(md5_init) +
+ sizeof(sha1_init));
+ case DRV_HASH_SHA256:
+ return (hash_handle->larval_digest_sram_addr +
+ sizeof(md5_init) +
+ sizeof(sha1_init) +
+ sizeof(sha224_init));
+ case DRV_HASH_SHA384:
+ return (hash_handle->larval_digest_sram_addr +
+ sizeof(md5_init) +
+ sizeof(sha1_init) +
+ sizeof(sha224_init) +
+ sizeof(sha256_init));
+ case DRV_HASH_SHA512:
+ return (hash_handle->larval_digest_sram_addr +
+ sizeof(md5_init) +
+ sizeof(sha1_init) +
+ sizeof(sha224_init) +
+ sizeof(sha256_init) +
+ sizeof(sha384_init));
+ default:
+ dev_err(dev, "Invalid hash mode (%d)\n", mode);
+ }
+
+ /*This is valid wrong value to avoid kernel crash*/
+ return hash_handle->larval_digest_sram_addr;
+}
+
+cc_sram_addr_t
+cc_digest_len_addr(void *drvdata, u32 mode)
+{
+ struct cc_drvdata *_drvdata = (struct cc_drvdata *)drvdata;
+ struct cc_hash_handle *hash_handle = _drvdata->hash_handle;
+ cc_sram_addr_t digest_len_addr = hash_handle->digest_len_sram_addr;
+
+ switch (mode) {
+ case DRV_HASH_SHA1:
+ case DRV_HASH_SHA224:
+ case DRV_HASH_SHA256:
+ case DRV_HASH_MD5:
+ return digest_len_addr;
+#if (CC_DEV_SHA_MAX > 256)
+ case DRV_HASH_SHA384:
+ case DRV_HASH_SHA512:
+ return digest_len_addr + sizeof(digest_len_init);
+#endif
+ default:
+ return digest_len_addr; /*to avoid kernel crash*/
+ }
+}
diff --git a/drivers/crypto/ccree/cc_hash.h b/drivers/crypto/ccree/cc_hash.h
new file mode 100644
index 000000000000..2e5bf8b0bbb6
--- /dev/null
+++ b/drivers/crypto/ccree/cc_hash.h
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
+
+/* \file cc_hash.h
+ * ARM CryptoCell Hash Crypto API
+ */
+
+#ifndef __CC_HASH_H__
+#define __CC_HASH_H__
+
+#include "cc_buffer_mgr.h"
+
+#define HMAC_IPAD_CONST 0x36363636
+#define HMAC_OPAD_CONST 0x5C5C5C5C
+#define HASH_LEN_SIZE_712 16
+#define HASH_LEN_SIZE_630 8
+#define HASH_MAX_LEN_SIZE HASH_LEN_SIZE_712
+#define CC_MAX_HASH_DIGEST_SIZE SHA512_DIGEST_SIZE
+#define CC_MAX_HASH_BLCK_SIZE SHA512_BLOCK_SIZE
+
+#define XCBC_MAC_K1_OFFSET 0
+#define XCBC_MAC_K2_OFFSET 16
+#define XCBC_MAC_K3_OFFSET 32
+
+#define CC_EXPORT_MAGIC 0xC2EE1070U
+
+/* this struct was taken from drivers/crypto/nx/nx-aes-xcbc.c and it is used
+ * for xcbc/cmac statesize
+ */
+struct aeshash_state {
+ u8 state[AES_BLOCK_SIZE];
+ unsigned int count;
+ u8 buffer[AES_BLOCK_SIZE];
+};
+
+/* ahash state */
+struct ahash_req_ctx {
+ u8 buffers[2][CC_MAX_HASH_BLCK_SIZE] ____cacheline_aligned;
+ u8 digest_result_buff[CC_MAX_HASH_DIGEST_SIZE] ____cacheline_aligned;
+ u8 digest_buff[CC_MAX_HASH_DIGEST_SIZE] ____cacheline_aligned;
+ u8 opad_digest_buff[CC_MAX_HASH_DIGEST_SIZE] ____cacheline_aligned;
+ u8 digest_bytes_len[HASH_MAX_LEN_SIZE] ____cacheline_aligned;
+ struct async_gen_req_ctx gen_ctx ____cacheline_aligned;
+ enum cc_req_dma_buf_type data_dma_buf_type;
+ dma_addr_t opad_digest_dma_addr;
+ dma_addr_t digest_buff_dma_addr;
+ dma_addr_t digest_bytes_len_dma_addr;
+ dma_addr_t digest_result_dma_addr;
+ u32 buf_cnt[2];
+ u32 buff_index;
+ u32 xcbc_count; /* count xcbc update operatations */
+ struct scatterlist buff_sg[2];
+ struct scatterlist *curr_sg;
+ u32 in_nents;
+ u32 mlli_nents;
+ struct mlli_params mlli_params;
+};
+
+static inline u32 *cc_hash_buf_cnt(struct ahash_req_ctx *state)
+{
+ return &state->buf_cnt[state->buff_index];
+}
+
+static inline u8 *cc_hash_buf(struct ahash_req_ctx *state)
+{
+ return state->buffers[state->buff_index];
+}
+
+static inline u32 *cc_next_buf_cnt(struct ahash_req_ctx *state)
+{
+ return &state->buf_cnt[state->buff_index ^ 1];
+}
+
+static inline u8 *cc_next_buf(struct ahash_req_ctx *state)
+{
+ return state->buffers[state->buff_index ^ 1];
+}
+
+int cc_hash_alloc(struct cc_drvdata *drvdata);
+int cc_init_hash_sram(struct cc_drvdata *drvdata);
+int cc_hash_free(struct cc_drvdata *drvdata);
+
+/*!
+ * Gets the initial digest length
+ *
+ * \param drvdata
+ * \param mode The Hash mode. Supported modes:
+ * MD5/SHA1/SHA224/SHA256/SHA384/SHA512
+ *
+ * \return u32 returns the address of the initial digest length in SRAM
+ */
+cc_sram_addr_t
+cc_digest_len_addr(void *drvdata, u32 mode);
+
+/*!
+ * Gets the address of the initial digest in SRAM
+ * according to the given hash mode
+ *
+ * \param drvdata
+ * \param mode The Hash mode. Supported modes:
+ * MD5/SHA1/SHA224/SHA256/SHA384/SHA512
+ *
+ * \return u32 The address of the initial digest in SRAM
+ */
+cc_sram_addr_t cc_larval_digest_addr(void *drvdata, u32 mode);
+
+void cc_hash_global_init(void);
+
+#endif /*__CC_HASH_H__*/
diff --git a/drivers/crypto/ccree/cc_host_regs.h b/drivers/crypto/ccree/cc_host_regs.h
new file mode 100644
index 000000000000..f51001898ca1
--- /dev/null
+++ b/drivers/crypto/ccree/cc_host_regs.h
@@ -0,0 +1,145 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
+
+#ifndef __CC_HOST_H__
+#define __CC_HOST_H__
+
+// --------------------------------------
+// BLOCK: HOST_P
+// --------------------------------------
+#define CC_HOST_IRR_REG_OFFSET 0xA00UL
+#define CC_HOST_IRR_DSCRPTR_COMPLETION_LOW_INT_BIT_SHIFT 0x2UL
+#define CC_HOST_IRR_DSCRPTR_COMPLETION_LOW_INT_BIT_SIZE 0x1UL
+#define CC_HOST_IRR_AXI_ERR_INT_BIT_SHIFT 0x8UL
+#define CC_HOST_IRR_AXI_ERR_INT_BIT_SIZE 0x1UL
+#define CC_HOST_IRR_GPR0_BIT_SHIFT 0xBUL
+#define CC_HOST_IRR_GPR0_BIT_SIZE 0x1UL
+#define CC_HOST_IRR_DSCRPTR_WATERMARK_INT_BIT_SHIFT 0x13UL
+#define CC_HOST_IRR_DSCRPTR_WATERMARK_INT_BIT_SIZE 0x1UL
+#define CC_HOST_IRR_AXIM_COMP_INT_BIT_SHIFT 0x17UL
+#define CC_HOST_IRR_AXIM_COMP_INT_BIT_SIZE 0x1UL
+#define CC_HOST_SEP_SRAM_THRESHOLD_REG_OFFSET 0xA10UL
+#define CC_HOST_SEP_SRAM_THRESHOLD_VALUE_BIT_SHIFT 0x0UL
+#define CC_HOST_SEP_SRAM_THRESHOLD_VALUE_BIT_SIZE 0xCUL
+#define CC_HOST_IMR_REG_OFFSET 0xA04UL
+#define CC_HOST_IMR_NOT_USED_MASK_BIT_SHIFT 0x1UL
+#define CC_HOST_IMR_NOT_USED_MASK_BIT_SIZE 0x1UL
+#define CC_HOST_IMR_DSCRPTR_COMPLETION_MASK_BIT_SHIFT 0x2UL
+#define CC_HOST_IMR_DSCRPTR_COMPLETION_MASK_BIT_SIZE 0x1UL
+#define CC_HOST_IMR_AXI_ERR_MASK_BIT_SHIFT 0x8UL
+#define CC_HOST_IMR_AXI_ERR_MASK_BIT_SIZE 0x1UL
+#define CC_HOST_IMR_GPR0_BIT_SHIFT 0xBUL
+#define CC_HOST_IMR_GPR0_BIT_SIZE 0x1UL
+#define CC_HOST_IMR_DSCRPTR_WATERMARK_MASK0_BIT_SHIFT 0x13UL
+#define CC_HOST_IMR_DSCRPTR_WATERMARK_MASK0_BIT_SIZE 0x1UL
+#define CC_HOST_IMR_AXIM_COMP_INT_MASK_BIT_SHIFT 0x17UL
+#define CC_HOST_IMR_AXIM_COMP_INT_MASK_BIT_SIZE 0x1UL
+#define CC_HOST_ICR_REG_OFFSET 0xA08UL
+#define CC_HOST_ICR_DSCRPTR_COMPLETION_BIT_SHIFT 0x2UL
+#define CC_HOST_ICR_DSCRPTR_COMPLETION_BIT_SIZE 0x1UL
+#define CC_HOST_ICR_AXI_ERR_CLEAR_BIT_SHIFT 0x8UL
+#define CC_HOST_ICR_AXI_ERR_CLEAR_BIT_SIZE 0x1UL
+#define CC_HOST_ICR_GPR_INT_CLEAR_BIT_SHIFT 0xBUL
+#define CC_HOST_ICR_GPR_INT_CLEAR_BIT_SIZE 0x1UL
+#define CC_HOST_ICR_DSCRPTR_WATERMARK_QUEUE0_CLEAR_BIT_SHIFT 0x13UL
+#define CC_HOST_ICR_DSCRPTR_WATERMARK_QUEUE0_CLEAR_BIT_SIZE 0x1UL
+#define CC_HOST_ICR_AXIM_COMP_INT_CLEAR_BIT_SHIFT 0x17UL
+#define CC_HOST_ICR_AXIM_COMP_INT_CLEAR_BIT_SIZE 0x1UL
+#define CC_HOST_SIGNATURE_REG_OFFSET 0xA24UL
+#define CC_HOST_SIGNATURE_VALUE_BIT_SHIFT 0x0UL
+#define CC_HOST_SIGNATURE_VALUE_BIT_SIZE 0x20UL
+#define CC_HOST_BOOT_REG_OFFSET 0xA28UL
+#define CC_HOST_BOOT_SYNTHESIS_CONFIG_BIT_SHIFT 0x0UL
+#define CC_HOST_BOOT_SYNTHESIS_CONFIG_BIT_SIZE 0x1UL
+#define CC_HOST_BOOT_LARGE_RKEK_LOCAL_BIT_SHIFT 0x1UL
+#define CC_HOST_BOOT_LARGE_RKEK_LOCAL_BIT_SIZE 0x1UL
+#define CC_HOST_BOOT_HASH_IN_FUSES_LOCAL_BIT_SHIFT 0x2UL
+#define CC_HOST_BOOT_HASH_IN_FUSES_LOCAL_BIT_SIZE 0x1UL
+#define CC_HOST_BOOT_EXT_MEM_SECURED_LOCAL_BIT_SHIFT 0x3UL
+#define CC_HOST_BOOT_EXT_MEM_SECURED_LOCAL_BIT_SIZE 0x1UL
+#define CC_HOST_BOOT_RKEK_ECC_EXISTS_LOCAL_N_BIT_SHIFT 0x5UL
+#define CC_HOST_BOOT_RKEK_ECC_EXISTS_LOCAL_N_BIT_SIZE 0x1UL
+#define CC_HOST_BOOT_SRAM_SIZE_LOCAL_BIT_SHIFT 0x6UL
+#define CC_HOST_BOOT_SRAM_SIZE_LOCAL_BIT_SIZE 0x3UL
+#define CC_HOST_BOOT_DSCRPTR_EXISTS_LOCAL_BIT_SHIFT 0x9UL
+#define CC_HOST_BOOT_DSCRPTR_EXISTS_LOCAL_BIT_SIZE 0x1UL
+#define CC_HOST_BOOT_PAU_EXISTS_LOCAL_BIT_SHIFT 0xAUL
+#define CC_HOST_BOOT_PAU_EXISTS_LOCAL_BIT_SIZE 0x1UL
+#define CC_HOST_BOOT_RNG_EXISTS_LOCAL_BIT_SHIFT 0xBUL
+#define CC_HOST_BOOT_RNG_EXISTS_LOCAL_BIT_SIZE 0x1UL
+#define CC_HOST_BOOT_PKA_EXISTS_LOCAL_BIT_SHIFT 0xCUL
+#define CC_HOST_BOOT_PKA_EXISTS_LOCAL_BIT_SIZE 0x1UL
+#define CC_HOST_BOOT_RC4_EXISTS_LOCAL_BIT_SHIFT 0xDUL
+#define CC_HOST_BOOT_RC4_EXISTS_LOCAL_BIT_SIZE 0x1UL
+#define CC_HOST_BOOT_SHA_512_PRSNT_LOCAL_BIT_SHIFT 0xEUL
+#define CC_HOST_BOOT_SHA_512_PRSNT_LOCAL_BIT_SIZE 0x1UL
+#define CC_HOST_BOOT_SHA_256_PRSNT_LOCAL_BIT_SHIFT 0xFUL
+#define CC_HOST_BOOT_SHA_256_PRSNT_LOCAL_BIT_SIZE 0x1UL
+#define CC_HOST_BOOT_MD5_PRSNT_LOCAL_BIT_SHIFT 0x10UL
+#define CC_HOST_BOOT_MD5_PRSNT_LOCAL_BIT_SIZE 0x1UL
+#define CC_HOST_BOOT_HASH_EXISTS_LOCAL_BIT_SHIFT 0x11UL
+#define CC_HOST_BOOT_HASH_EXISTS_LOCAL_BIT_SIZE 0x1UL
+#define CC_HOST_BOOT_C2_EXISTS_LOCAL_BIT_SHIFT 0x12UL
+#define CC_HOST_BOOT_C2_EXISTS_LOCAL_BIT_SIZE 0x1UL
+#define CC_HOST_BOOT_DES_EXISTS_LOCAL_BIT_SHIFT 0x13UL
+#define CC_HOST_BOOT_DES_EXISTS_LOCAL_BIT_SIZE 0x1UL
+#define CC_HOST_BOOT_AES_XCBC_MAC_EXISTS_LOCAL_BIT_SHIFT 0x14UL
+#define CC_HOST_BOOT_AES_XCBC_MAC_EXISTS_LOCAL_BIT_SIZE 0x1UL
+#define CC_HOST_BOOT_AES_CMAC_EXISTS_LOCAL_BIT_SHIFT 0x15UL
+#define CC_HOST_BOOT_AES_CMAC_EXISTS_LOCAL_BIT_SIZE 0x1UL
+#define CC_HOST_BOOT_AES_CCM_EXISTS_LOCAL_BIT_SHIFT 0x16UL
+#define CC_HOST_BOOT_AES_CCM_EXISTS_LOCAL_BIT_SIZE 0x1UL
+#define CC_HOST_BOOT_AES_XEX_HW_T_CALC_LOCAL_BIT_SHIFT 0x17UL
+#define CC_HOST_BOOT_AES_XEX_HW_T_CALC_LOCAL_BIT_SIZE 0x1UL
+#define CC_HOST_BOOT_AES_XEX_EXISTS_LOCAL_BIT_SHIFT 0x18UL
+#define CC_HOST_BOOT_AES_XEX_EXISTS_LOCAL_BIT_SIZE 0x1UL
+#define CC_HOST_BOOT_CTR_EXISTS_LOCAL_BIT_SHIFT 0x19UL
+#define CC_HOST_BOOT_CTR_EXISTS_LOCAL_BIT_SIZE 0x1UL
+#define CC_HOST_BOOT_AES_DIN_BYTE_RESOLUTION_LOCAL_BIT_SHIFT 0x1AUL
+#define CC_HOST_BOOT_AES_DIN_BYTE_RESOLUTION_LOCAL_BIT_SIZE 0x1UL
+#define CC_HOST_BOOT_TUNNELING_ENB_LOCAL_BIT_SHIFT 0x1BUL
+#define CC_HOST_BOOT_TUNNELING_ENB_LOCAL_BIT_SIZE 0x1UL
+#define CC_HOST_BOOT_SUPPORT_256_192_KEY_LOCAL_BIT_SHIFT 0x1CUL
+#define CC_HOST_BOOT_SUPPORT_256_192_KEY_LOCAL_BIT_SIZE 0x1UL
+#define CC_HOST_BOOT_ONLY_ENCRYPT_LOCAL_BIT_SHIFT 0x1DUL
+#define CC_HOST_BOOT_ONLY_ENCRYPT_LOCAL_BIT_SIZE 0x1UL
+#define CC_HOST_BOOT_AES_EXISTS_LOCAL_BIT_SHIFT 0x1EUL
+#define CC_HOST_BOOT_AES_EXISTS_LOCAL_BIT_SIZE 0x1UL
+#define CC_HOST_VERSION_REG_OFFSET 0xA40UL
+#define CC_HOST_VERSION_VALUE_BIT_SHIFT 0x0UL
+#define CC_HOST_VERSION_VALUE_BIT_SIZE 0x20UL
+#define CC_HOST_KFDE0_VALID_REG_OFFSET 0xA60UL
+#define CC_HOST_KFDE0_VALID_VALUE_BIT_SHIFT 0x0UL
+#define CC_HOST_KFDE0_VALID_VALUE_BIT_SIZE 0x1UL
+#define CC_HOST_KFDE1_VALID_REG_OFFSET 0xA64UL
+#define CC_HOST_KFDE1_VALID_VALUE_BIT_SHIFT 0x0UL
+#define CC_HOST_KFDE1_VALID_VALUE_BIT_SIZE 0x1UL
+#define CC_HOST_KFDE2_VALID_REG_OFFSET 0xA68UL
+#define CC_HOST_KFDE2_VALID_VALUE_BIT_SHIFT 0x0UL
+#define CC_HOST_KFDE2_VALID_VALUE_BIT_SIZE 0x1UL
+#define CC_HOST_KFDE3_VALID_REG_OFFSET 0xA6CUL
+#define CC_HOST_KFDE3_VALID_VALUE_BIT_SHIFT 0x0UL
+#define CC_HOST_KFDE3_VALID_VALUE_BIT_SIZE 0x1UL
+#define CC_HOST_GPR0_REG_OFFSET 0xA70UL
+#define CC_HOST_GPR0_VALUE_BIT_SHIFT 0x0UL
+#define CC_HOST_GPR0_VALUE_BIT_SIZE 0x20UL
+#define CC_GPR_HOST_REG_OFFSET 0xA74UL
+#define CC_GPR_HOST_VALUE_BIT_SHIFT 0x0UL
+#define CC_GPR_HOST_VALUE_BIT_SIZE 0x20UL
+#define CC_HOST_POWER_DOWN_EN_REG_OFFSET 0xA78UL
+#define CC_HOST_POWER_DOWN_EN_VALUE_BIT_SHIFT 0x0UL
+#define CC_HOST_POWER_DOWN_EN_VALUE_BIT_SIZE 0x1UL
+// --------------------------------------
+// BLOCK: HOST_SRAM
+// --------------------------------------
+#define CC_SRAM_DATA_REG_OFFSET 0xF00UL
+#define CC_SRAM_DATA_VALUE_BIT_SHIFT 0x0UL
+#define CC_SRAM_DATA_VALUE_BIT_SIZE 0x20UL
+#define CC_SRAM_ADDR_REG_OFFSET 0xF04UL
+#define CC_SRAM_ADDR_VALUE_BIT_SHIFT 0x0UL
+#define CC_SRAM_ADDR_VALUE_BIT_SIZE 0xFUL
+#define CC_SRAM_DATA_READY_REG_OFFSET 0xF08UL
+#define CC_SRAM_DATA_READY_VALUE_BIT_SHIFT 0x0UL
+#define CC_SRAM_DATA_READY_VALUE_BIT_SIZE 0x1UL
+
+#endif //__CC_HOST_H__
diff --git a/drivers/crypto/ccree/cc_hw_queue_defs.h b/drivers/crypto/ccree/cc_hw_queue_defs.h
new file mode 100644
index 000000000000..a091ae57f902
--- /dev/null
+++ b/drivers/crypto/ccree/cc_hw_queue_defs.h
@@ -0,0 +1,576 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
+
+#ifndef __CC_HW_QUEUE_DEFS_H__
+#define __CC_HW_QUEUE_DEFS_H__
+
+#include <linux/types.h>
+
+#include "cc_kernel_regs.h"
+#include <linux/bitfield.h>
+
+/******************************************************************************
+ * DEFINITIONS
+ ******************************************************************************/
+
+#define HW_DESC_SIZE_WORDS 6
+/* Define max. available slots in HW queue */
+#define HW_QUEUE_SLOTS_MAX 15
+
+#define CC_REG_LOW(word, name) \
+ (CC_DSCRPTR_QUEUE_WORD ## word ## _ ## name ## _BIT_SHIFT)
+
+#define CC_REG_HIGH(word, name) \
+ (CC_REG_LOW(word, name) + \
+ CC_DSCRPTR_QUEUE_WORD ## word ## _ ## name ## _BIT_SIZE - 1)
+
+#define CC_GENMASK(word, name) \
+ GENMASK(CC_REG_HIGH(word, name), CC_REG_LOW(word, name))
+
+#define WORD0_VALUE CC_GENMASK(0, VALUE)
+#define WORD1_DIN_CONST_VALUE CC_GENMASK(1, DIN_CONST_VALUE)
+#define WORD1_DIN_DMA_MODE CC_GENMASK(1, DIN_DMA_MODE)
+#define WORD1_DIN_SIZE CC_GENMASK(1, DIN_SIZE)
+#define WORD1_NOT_LAST CC_GENMASK(1, NOT_LAST)
+#define WORD1_NS_BIT CC_GENMASK(1, NS_BIT)
+#define WORD2_VALUE CC_GENMASK(2, VALUE)
+#define WORD3_DOUT_DMA_MODE CC_GENMASK(3, DOUT_DMA_MODE)
+#define WORD3_DOUT_LAST_IND CC_GENMASK(3, DOUT_LAST_IND)
+#define WORD3_DOUT_SIZE CC_GENMASK(3, DOUT_SIZE)
+#define WORD3_HASH_XOR_BIT CC_GENMASK(3, HASH_XOR_BIT)
+#define WORD3_NS_BIT CC_GENMASK(3, NS_BIT)
+#define WORD3_QUEUE_LAST_IND CC_GENMASK(3, QUEUE_LAST_IND)
+#define WORD4_ACK_NEEDED CC_GENMASK(4, ACK_NEEDED)
+#define WORD4_AES_SEL_N_HASH CC_GENMASK(4, AES_SEL_N_HASH)
+#define WORD4_BYTES_SWAP CC_GENMASK(4, BYTES_SWAP)
+#define WORD4_CIPHER_CONF0 CC_GENMASK(4, CIPHER_CONF0)
+#define WORD4_CIPHER_CONF1 CC_GENMASK(4, CIPHER_CONF1)
+#define WORD4_CIPHER_CONF2 CC_GENMASK(4, CIPHER_CONF2)
+#define WORD4_CIPHER_DO CC_GENMASK(4, CIPHER_DO)
+#define WORD4_CIPHER_MODE CC_GENMASK(4, CIPHER_MODE)
+#define WORD4_CMAC_SIZE0 CC_GENMASK(4, CMAC_SIZE0)
+#define WORD4_DATA_FLOW_MODE CC_GENMASK(4, DATA_FLOW_MODE)
+#define WORD4_KEY_SIZE CC_GENMASK(4, KEY_SIZE)
+#define WORD4_SETUP_OPERATION CC_GENMASK(4, SETUP_OPERATION)
+#define WORD5_DIN_ADDR_HIGH CC_GENMASK(5, DIN_ADDR_HIGH)
+#define WORD5_DOUT_ADDR_HIGH CC_GENMASK(5, DOUT_ADDR_HIGH)
+
+/******************************************************************************
+ * TYPE DEFINITIONS
+ ******************************************************************************/
+
+struct cc_hw_desc {
+ union {
+ u32 word[HW_DESC_SIZE_WORDS];
+ u16 hword[HW_DESC_SIZE_WORDS * 2];
+ };
+};
+
+enum cc_axi_sec {
+ AXI_SECURE = 0,
+ AXI_NOT_SECURE = 1
+};
+
+enum cc_desc_direction {
+ DESC_DIRECTION_ILLEGAL = -1,
+ DESC_DIRECTION_ENCRYPT_ENCRYPT = 0,
+ DESC_DIRECTION_DECRYPT_DECRYPT = 1,
+ DESC_DIRECTION_DECRYPT_ENCRYPT = 3,
+ DESC_DIRECTION_END = S32_MAX,
+};
+
+enum cc_dma_mode {
+ DMA_MODE_NULL = -1,
+ NO_DMA = 0,
+ DMA_SRAM = 1,
+ DMA_DLLI = 2,
+ DMA_MLLI = 3,
+ DMA_MODE_END = S32_MAX,
+};
+
+enum cc_flow_mode {
+ FLOW_MODE_NULL = -1,
+ /* data flows */
+ BYPASS = 0,
+ DIN_AES_DOUT = 1,
+ AES_to_HASH = 2,
+ AES_and_HASH = 3,
+ DIN_DES_DOUT = 4,
+ DES_to_HASH = 5,
+ DES_and_HASH = 6,
+ DIN_HASH = 7,
+ DIN_HASH_and_BYPASS = 8,
+ AESMAC_and_BYPASS = 9,
+ AES_to_HASH_and_DOUT = 10,
+ DIN_RC4_DOUT = 11,
+ DES_to_HASH_and_DOUT = 12,
+ AES_to_AES_to_HASH_and_DOUT = 13,
+ AES_to_AES_to_HASH = 14,
+ AES_to_HASH_and_AES = 15,
+ DIN_AES_AESMAC = 17,
+ HASH_to_DOUT = 18,
+ /* setup flows */
+ S_DIN_to_AES = 32,
+ S_DIN_to_AES2 = 33,
+ S_DIN_to_DES = 34,
+ S_DIN_to_RC4 = 35,
+ S_DIN_to_HASH = 37,
+ S_AES_to_DOUT = 38,
+ S_AES2_to_DOUT = 39,
+ S_RC4_to_DOUT = 41,
+ S_DES_to_DOUT = 42,
+ S_HASH_to_DOUT = 43,
+ SET_FLOW_ID = 44,
+ FLOW_MODE_END = S32_MAX,
+};
+
+enum cc_setup_op {
+ SETUP_LOAD_NOP = 0,
+ SETUP_LOAD_STATE0 = 1,
+ SETUP_LOAD_STATE1 = 2,
+ SETUP_LOAD_STATE2 = 3,
+ SETUP_LOAD_KEY0 = 4,
+ SETUP_LOAD_XEX_KEY = 5,
+ SETUP_WRITE_STATE0 = 8,
+ SETUP_WRITE_STATE1 = 9,
+ SETUP_WRITE_STATE2 = 10,
+ SETUP_WRITE_STATE3 = 11,
+ SETUP_OP_END = S32_MAX,
+};
+
+enum cc_hash_conf_pad {
+ HASH_PADDING_DISABLED = 0,
+ HASH_PADDING_ENABLED = 1,
+ HASH_DIGEST_RESULT_LITTLE_ENDIAN = 2,
+ HASH_CONFIG1_PADDING_RESERVE32 = S32_MAX,
+};
+
+enum cc_aes_mac_selector {
+ AES_SK = 1,
+ AES_CMAC_INIT = 2,
+ AES_CMAC_SIZE0 = 3,
+ AES_MAC_END = S32_MAX,
+};
+
+#define HW_KEY_MASK_CIPHER_DO 0x3
+#define HW_KEY_SHIFT_CIPHER_CFG2 2
+
+/* HwCryptoKey[1:0] is mapped to cipher_do[1:0] */
+/* HwCryptoKey[2:3] is mapped to cipher_config2[1:0] */
+enum cc_hw_crypto_key {
+ USER_KEY = 0, /* 0x0000 */
+ ROOT_KEY = 1, /* 0x0001 */
+ PROVISIONING_KEY = 2, /* 0x0010 */ /* ==KCP */
+ SESSION_KEY = 3, /* 0x0011 */
+ RESERVED_KEY = 4, /* NA */
+ PLATFORM_KEY = 5, /* 0x0101 */
+ CUSTOMER_KEY = 6, /* 0x0110 */
+ KFDE0_KEY = 7, /* 0x0111 */
+ KFDE1_KEY = 9, /* 0x1001 */
+ KFDE2_KEY = 10, /* 0x1010 */
+ KFDE3_KEY = 11, /* 0x1011 */
+ END_OF_KEYS = S32_MAX,
+};
+
+enum cc_hw_aes_key_size {
+ AES_128_KEY = 0,
+ AES_192_KEY = 1,
+ AES_256_KEY = 2,
+ END_OF_AES_KEYS = S32_MAX,
+};
+
+enum cc_hash_cipher_pad {
+ DO_NOT_PAD = 0,
+ DO_PAD = 1,
+ HASH_CIPHER_DO_PADDING_RESERVE32 = S32_MAX,
+};
+
+/*****************************/
+/* Descriptor packing macros */
+/*****************************/
+
+/*
+ * Init a HW descriptor struct
+ * @pdesc: pointer HW descriptor struct
+ */
+static inline void hw_desc_init(struct cc_hw_desc *pdesc)
+{
+ memset(pdesc, 0, sizeof(struct cc_hw_desc));
+}
+
+/*
+ * Indicates the end of current HW descriptors flow and release the HW engines.
+ *
+ * @pdesc: pointer HW descriptor struct
+ */
+static inline void set_queue_last_ind_bit(struct cc_hw_desc *pdesc)
+{
+ pdesc->word[3] |= FIELD_PREP(WORD3_QUEUE_LAST_IND, 1);
+}
+
+/*
+ * Set the DIN field of a HW descriptors
+ *
+ * @pdesc: pointer HW descriptor struct
+ * @dma_mode: dmaMode The DMA mode: NO_DMA, SRAM, DLLI, MLLI, CONSTANT
+ * @addr: dinAdr DIN address
+ * @size: Data size in bytes
+ * @axi_sec: AXI secure bit
+ */
+static inline void set_din_type(struct cc_hw_desc *pdesc,
+ enum cc_dma_mode dma_mode, dma_addr_t addr,
+ u32 size, enum cc_axi_sec axi_sec)
+{
+ pdesc->word[0] = (u32)addr;
+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+ pdesc->word[5] |= FIELD_PREP(WORD5_DIN_ADDR_HIGH, ((u16)(addr >> 32)));
+#endif
+ pdesc->word[1] |= FIELD_PREP(WORD1_DIN_DMA_MODE, dma_mode) |
+ FIELD_PREP(WORD1_DIN_SIZE, size) |
+ FIELD_PREP(WORD1_NS_BIT, axi_sec);
+}
+
+/*
+ * Set the DIN field of a HW descriptors to NO DMA mode.
+ * Used for NOP descriptor, register patches and other special modes.
+ *
+ * @pdesc: pointer HW descriptor struct
+ * @addr: DIN address
+ * @size: Data size in bytes
+ */
+static inline void set_din_no_dma(struct cc_hw_desc *pdesc, u32 addr, u32 size)
+{
+ pdesc->word[0] = addr;
+ pdesc->word[1] |= FIELD_PREP(WORD1_DIN_SIZE, size);
+}
+
+/*
+ * Set the DIN field of a HW descriptors to SRAM mode.
+ * Note: No need to check SRAM alignment since host requests do not use SRAM and
+ * adaptor will enforce alignment check.
+ *
+ * @pdesc: pointer HW descriptor struct
+ * @addr: DIN address
+ * @size Data size in bytes
+ */
+static inline void set_din_sram(struct cc_hw_desc *pdesc, dma_addr_t addr,
+ u32 size)
+{
+ pdesc->word[0] = (u32)addr;
+ pdesc->word[1] |= FIELD_PREP(WORD1_DIN_SIZE, size) |
+ FIELD_PREP(WORD1_DIN_DMA_MODE, DMA_SRAM);
+}
+
+/*
+ * Set the DIN field of a HW descriptors to CONST mode
+ *
+ * @pdesc: pointer HW descriptor struct
+ * @val: DIN const value
+ * @size: Data size in bytes
+ */
+static inline void set_din_const(struct cc_hw_desc *pdesc, u32 val, u32 size)
+{
+ pdesc->word[0] = val;
+ pdesc->word[1] |= FIELD_PREP(WORD1_DIN_CONST_VALUE, 1) |
+ FIELD_PREP(WORD1_DIN_DMA_MODE, DMA_SRAM) |
+ FIELD_PREP(WORD1_DIN_SIZE, size);
+}
+
+/*
+ * Set the DIN not last input data indicator
+ *
+ * @pdesc: pointer HW descriptor struct
+ */
+static inline void set_din_not_last_indication(struct cc_hw_desc *pdesc)
+{
+ pdesc->word[1] |= FIELD_PREP(WORD1_NOT_LAST, 1);
+}
+
+/*
+ * Set the DOUT field of a HW descriptors
+ *
+ * @pdesc: pointer HW descriptor struct
+ * @dma_mode: The DMA mode: NO_DMA, SRAM, DLLI, MLLI, CONSTANT
+ * @addr: DOUT address
+ * @size: Data size in bytes
+ * @axi_sec: AXI secure bit
+ */
+static inline void set_dout_type(struct cc_hw_desc *pdesc,
+ enum cc_dma_mode dma_mode, dma_addr_t addr,
+ u32 size, enum cc_axi_sec axi_sec)
+{
+ pdesc->word[2] = (u32)addr;
+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+ pdesc->word[5] |= FIELD_PREP(WORD5_DOUT_ADDR_HIGH, ((u16)(addr >> 32)));
+#endif
+ pdesc->word[3] |= FIELD_PREP(WORD3_DOUT_DMA_MODE, dma_mode) |
+ FIELD_PREP(WORD3_DOUT_SIZE, size) |
+ FIELD_PREP(WORD3_NS_BIT, axi_sec);
+}
+
+/*
+ * Set the DOUT field of a HW descriptors to DLLI type
+ * The LAST INDICATION is provided by the user
+ *
+ * @pdesc pointer HW descriptor struct
+ * @addr: DOUT address
+ * @size: Data size in bytes
+ * @last_ind: The last indication bit
+ * @axi_sec: AXI secure bit
+ */
+static inline void set_dout_dlli(struct cc_hw_desc *pdesc, dma_addr_t addr,
+ u32 size, enum cc_axi_sec axi_sec,
+ u32 last_ind)
+{
+ set_dout_type(pdesc, DMA_DLLI, addr, size, axi_sec);
+ pdesc->word[3] |= FIELD_PREP(WORD3_DOUT_LAST_IND, last_ind);
+}
+
+/*
+ * Set the DOUT field of a HW descriptors to DLLI type
+ * The LAST INDICATION is provided by the user
+ *
+ * @pdesc: pointer HW descriptor struct
+ * @addr: DOUT address
+ * @size: Data size in bytes
+ * @last_ind: The last indication bit
+ * @axi_sec: AXI secure bit
+ */
+static inline void set_dout_mlli(struct cc_hw_desc *pdesc, dma_addr_t addr,
+ u32 size, enum cc_axi_sec axi_sec,
+ bool last_ind)
+{
+ set_dout_type(pdesc, DMA_MLLI, addr, size, axi_sec);
+ pdesc->word[3] |= FIELD_PREP(WORD3_DOUT_LAST_IND, last_ind);
+}
+
+/*
+ * Set the DOUT field of a HW descriptors to NO DMA mode.
+ * Used for NOP descriptor, register patches and other special modes.
+ *
+ * @pdesc: pointer HW descriptor struct
+ * @addr: DOUT address
+ * @size: Data size in bytes
+ * @write_enable: Enables a write operation to a register
+ */
+static inline void set_dout_no_dma(struct cc_hw_desc *pdesc, u32 addr,
+ u32 size, bool write_enable)
+{
+ pdesc->word[2] = addr;
+ pdesc->word[3] |= FIELD_PREP(WORD3_DOUT_SIZE, size) |
+ FIELD_PREP(WORD3_DOUT_LAST_IND, write_enable);
+}
+
+/*
+ * Set the word for the XOR operation.
+ *
+ * @pdesc: pointer HW descriptor struct
+ * @val: xor data value
+ */
+static inline void set_xor_val(struct cc_hw_desc *pdesc, u32 val)
+{
+ pdesc->word[2] = val;
+}
+
+/*
+ * Sets the XOR indicator bit in the descriptor
+ *
+ * @pdesc: pointer HW descriptor struct
+ */
+static inline void set_xor_active(struct cc_hw_desc *pdesc)
+{
+ pdesc->word[3] |= FIELD_PREP(WORD3_HASH_XOR_BIT, 1);
+}
+
+/*
+ * Select the AES engine instead of HASH engine when setting up combined mode
+ * with AES XCBC MAC
+ *
+ * @pdesc: pointer HW descriptor struct
+ */
+static inline void set_aes_not_hash_mode(struct cc_hw_desc *pdesc)
+{
+ pdesc->word[4] |= FIELD_PREP(WORD4_AES_SEL_N_HASH, 1);
+}
+
+/*
+ * Set the DOUT field of a HW descriptors to SRAM mode
+ * Note: No need to check SRAM alignment since host requests do not use SRAM and
+ * adaptor will enforce alignment check.
+ *
+ * @pdesc: pointer HW descriptor struct
+ * @addr: DOUT address
+ * @size: Data size in bytes
+ */
+static inline void set_dout_sram(struct cc_hw_desc *pdesc, u32 addr, u32 size)
+{
+ pdesc->word[2] = addr;
+ pdesc->word[3] |= FIELD_PREP(WORD3_DOUT_DMA_MODE, DMA_SRAM) |
+ FIELD_PREP(WORD3_DOUT_SIZE, size);
+}
+
+/*
+ * Sets the data unit size for XEX mode in data_out_addr[15:0]
+ *
+ * @pdesc: pDesc pointer HW descriptor struct
+ * @size: data unit size for XEX mode
+ */
+static inline void set_xex_data_unit_size(struct cc_hw_desc *pdesc, u32 size)
+{
+ pdesc->word[2] = size;
+}
+
+/*
+ * Set the number of rounds for Multi2 in data_out_addr[15:0]
+ *
+ * @pdesc: pointer HW descriptor struct
+ * @num: number of rounds for Multi2
+ */
+static inline void set_multi2_num_rounds(struct cc_hw_desc *pdesc, u32 num)
+{
+ pdesc->word[2] = num;
+}
+
+/*
+ * Set the flow mode.
+ *
+ * @pdesc: pointer HW descriptor struct
+ * @mode: Any one of the modes defined in [CC7x-DESC]
+ */
+static inline void set_flow_mode(struct cc_hw_desc *pdesc,
+ enum cc_flow_mode mode)
+{
+ pdesc->word[4] |= FIELD_PREP(WORD4_DATA_FLOW_MODE, mode);
+}
+
+/*
+ * Set the cipher mode.
+ *
+ * @pdesc: pointer HW descriptor struct
+ * @mode: Any one of the modes defined in [CC7x-DESC]
+ */
+static inline void set_cipher_mode(struct cc_hw_desc *pdesc,
+ enum drv_cipher_mode mode)
+{
+ pdesc->word[4] |= FIELD_PREP(WORD4_CIPHER_MODE, mode);
+}
+
+/*
+ * Set the cipher configuration fields.
+ *
+ * @pdesc: pointer HW descriptor struct
+ * @mode: Any one of the modes defined in [CC7x-DESC]
+ */
+static inline void set_cipher_config0(struct cc_hw_desc *pdesc,
+ enum drv_crypto_direction mode)
+{
+ pdesc->word[4] |= FIELD_PREP(WORD4_CIPHER_CONF0, mode);
+}
+
+/*
+ * Set the cipher configuration fields.
+ *
+ * @pdesc: pointer HW descriptor struct
+ * @config: Any one of the modes defined in [CC7x-DESC]
+ */
+static inline void set_cipher_config1(struct cc_hw_desc *pdesc,
+ enum cc_hash_conf_pad config)
+{
+ pdesc->word[4] |= FIELD_PREP(WORD4_CIPHER_CONF1, config);
+}
+
+/*
+ * Set HW key configuration fields.
+ *
+ * @pdesc: pointer HW descriptor struct
+ * @hw_key: The HW key slot asdefined in enum cc_hw_crypto_key
+ */
+static inline void set_hw_crypto_key(struct cc_hw_desc *pdesc,
+ enum cc_hw_crypto_key hw_key)
+{
+ pdesc->word[4] |= FIELD_PREP(WORD4_CIPHER_DO,
+ (hw_key & HW_KEY_MASK_CIPHER_DO)) |
+ FIELD_PREP(WORD4_CIPHER_CONF2,
+ (hw_key >> HW_KEY_SHIFT_CIPHER_CFG2));
+}
+
+/*
+ * Set byte order of all setup-finalize descriptors.
+ *
+ * @pdesc: pointer HW descriptor struct
+ * @config: Any one of the modes defined in [CC7x-DESC]
+ */
+static inline void set_bytes_swap(struct cc_hw_desc *pdesc, bool config)
+{
+ pdesc->word[4] |= FIELD_PREP(WORD4_BYTES_SWAP, config);
+}
+
+/*
+ * Set CMAC_SIZE0 mode.
+ *
+ * @pdesc: pointer HW descriptor struct
+ */
+static inline void set_cmac_size0_mode(struct cc_hw_desc *pdesc)
+{
+ pdesc->word[4] |= FIELD_PREP(WORD4_CMAC_SIZE0, 1);
+}
+
+/*
+ * Set key size descriptor field.
+ *
+ * @pdesc: pointer HW descriptor struct
+ * @size: key size in bytes (NOT size code)
+ */
+static inline void set_key_size(struct cc_hw_desc *pdesc, u32 size)
+{
+ pdesc->word[4] |= FIELD_PREP(WORD4_KEY_SIZE, size);
+}
+
+/*
+ * Set AES key size.
+ *
+ * @pdesc: pointer HW descriptor struct
+ * @size: key size in bytes (NOT size code)
+ */
+static inline void set_key_size_aes(struct cc_hw_desc *pdesc, u32 size)
+{
+ set_key_size(pdesc, ((size >> 3) - 2));
+}
+
+/*
+ * Set DES key size.
+ *
+ * @pdesc: pointer HW descriptor struct
+ * @size: key size in bytes (NOT size code)
+ */
+static inline void set_key_size_des(struct cc_hw_desc *pdesc, u32 size)
+{
+ set_key_size(pdesc, ((size >> 3) - 1));
+}
+
+/*
+ * Set the descriptor setup mode
+ *
+ * @pdesc: pointer HW descriptor struct
+ * @mode: Any one of the setup modes defined in [CC7x-DESC]
+ */
+static inline void set_setup_mode(struct cc_hw_desc *pdesc,
+ enum cc_setup_op mode)
+{
+ pdesc->word[4] |= FIELD_PREP(WORD4_SETUP_OPERATION, mode);
+}
+
+/*
+ * Set the descriptor cipher DO
+ *
+ * @pdesc: pointer HW descriptor struct
+ * @config: Any one of the cipher do defined in [CC7x-DESC]
+ */
+static inline void set_cipher_do(struct cc_hw_desc *pdesc,
+ enum cc_hash_cipher_pad config)
+{
+ pdesc->word[4] |= FIELD_PREP(WORD4_CIPHER_DO,
+ (config & HW_KEY_MASK_CIPHER_DO));
+}
+
+#endif /*__CC_HW_QUEUE_DEFS_H__*/
diff --git a/drivers/crypto/ccree/cc_ivgen.c b/drivers/crypto/ccree/cc_ivgen.c
new file mode 100644
index 000000000000..769458323394
--- /dev/null
+++ b/drivers/crypto/ccree/cc_ivgen.c
@@ -0,0 +1,279 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
+
+#include <crypto/ctr.h>
+#include "cc_driver.h"
+#include "cc_ivgen.h"
+#include "cc_request_mgr.h"
+#include "cc_sram_mgr.h"
+#include "cc_buffer_mgr.h"
+
+/* The max. size of pool *MUST* be <= SRAM total size */
+#define CC_IVPOOL_SIZE 1024
+/* The first 32B fraction of pool are dedicated to the
+ * next encryption "key" & "IV" for pool regeneration
+ */
+#define CC_IVPOOL_META_SIZE (CC_AES_IV_SIZE + AES_KEYSIZE_128)
+#define CC_IVPOOL_GEN_SEQ_LEN 4
+
+/**
+ * struct cc_ivgen_ctx -IV pool generation context
+ * @pool: the start address of the iv-pool resides in internal RAM
+ * @ctr_key_dma: address of pool's encryption key material in internal RAM
+ * @ctr_iv_dma: address of pool's counter iv in internal RAM
+ * @next_iv_ofs: the offset to the next available IV in pool
+ * @pool_meta: virt. address of the initial enc. key/IV
+ * @pool_meta_dma: phys. address of the initial enc. key/IV
+ */
+struct cc_ivgen_ctx {
+ cc_sram_addr_t pool;
+ cc_sram_addr_t ctr_key;
+ cc_sram_addr_t ctr_iv;
+ u32 next_iv_ofs;
+ u8 *pool_meta;
+ dma_addr_t pool_meta_dma;
+};
+
+/*!
+ * Generates CC_IVPOOL_SIZE of random bytes by
+ * encrypting 0's using AES128-CTR.
+ *
+ * \param ivgen iv-pool context
+ * \param iv_seq IN/OUT array to the descriptors sequence
+ * \param iv_seq_len IN/OUT pointer to the sequence length
+ */
+static int cc_gen_iv_pool(struct cc_ivgen_ctx *ivgen_ctx,
+ struct cc_hw_desc iv_seq[], unsigned int *iv_seq_len)
+{
+ unsigned int idx = *iv_seq_len;
+
+ if ((*iv_seq_len + CC_IVPOOL_GEN_SEQ_LEN) > CC_IVPOOL_SEQ_LEN) {
+ /* The sequence will be longer than allowed */
+ return -EINVAL;
+ }
+ /* Setup key */
+ hw_desc_init(&iv_seq[idx]);
+ set_din_sram(&iv_seq[idx], ivgen_ctx->ctr_key, AES_KEYSIZE_128);
+ set_setup_mode(&iv_seq[idx], SETUP_LOAD_KEY0);
+ set_cipher_config0(&iv_seq[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
+ set_flow_mode(&iv_seq[idx], S_DIN_to_AES);
+ set_key_size_aes(&iv_seq[idx], CC_AES_128_BIT_KEY_SIZE);
+ set_cipher_mode(&iv_seq[idx], DRV_CIPHER_CTR);
+ idx++;
+
+ /* Setup cipher state */
+ hw_desc_init(&iv_seq[idx]);
+ set_din_sram(&iv_seq[idx], ivgen_ctx->ctr_iv, CC_AES_IV_SIZE);
+ set_cipher_config0(&iv_seq[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
+ set_flow_mode(&iv_seq[idx], S_DIN_to_AES);
+ set_setup_mode(&iv_seq[idx], SETUP_LOAD_STATE1);
+ set_key_size_aes(&iv_seq[idx], CC_AES_128_BIT_KEY_SIZE);
+ set_cipher_mode(&iv_seq[idx], DRV_CIPHER_CTR);
+ idx++;
+
+ /* Perform dummy encrypt to skip first block */
+ hw_desc_init(&iv_seq[idx]);
+ set_din_const(&iv_seq[idx], 0, CC_AES_IV_SIZE);
+ set_dout_sram(&iv_seq[idx], ivgen_ctx->pool, CC_AES_IV_SIZE);
+ set_flow_mode(&iv_seq[idx], DIN_AES_DOUT);
+ idx++;
+
+ /* Generate IV pool */
+ hw_desc_init(&iv_seq[idx]);
+ set_din_const(&iv_seq[idx], 0, CC_IVPOOL_SIZE);
+ set_dout_sram(&iv_seq[idx], ivgen_ctx->pool, CC_IVPOOL_SIZE);
+ set_flow_mode(&iv_seq[idx], DIN_AES_DOUT);
+ idx++;
+
+ *iv_seq_len = idx; /* Update sequence length */
+
+ /* queue ordering assures pool readiness */
+ ivgen_ctx->next_iv_ofs = CC_IVPOOL_META_SIZE;
+
+ return 0;
+}
+
+/*!
+ * Generates the initial pool in SRAM.
+ * This function should be invoked when resuming driver.
+ *
+ * \param drvdata
+ *
+ * \return int Zero for success, negative value otherwise.
+ */
+int cc_init_iv_sram(struct cc_drvdata *drvdata)
+{
+ struct cc_ivgen_ctx *ivgen_ctx = drvdata->ivgen_handle;
+ struct cc_hw_desc iv_seq[CC_IVPOOL_SEQ_LEN];
+ unsigned int iv_seq_len = 0;
+ int rc;
+
+ /* Generate initial enc. key/iv */
+ get_random_bytes(ivgen_ctx->pool_meta, CC_IVPOOL_META_SIZE);
+
+ /* The first 32B reserved for the enc. Key/IV */
+ ivgen_ctx->ctr_key = ivgen_ctx->pool;
+ ivgen_ctx->ctr_iv = ivgen_ctx->pool + AES_KEYSIZE_128;
+
+ /* Copy initial enc. key and IV to SRAM at a single descriptor */
+ hw_desc_init(&iv_seq[iv_seq_len]);
+ set_din_type(&iv_seq[iv_seq_len], DMA_DLLI, ivgen_ctx->pool_meta_dma,
+ CC_IVPOOL_META_SIZE, NS_BIT);
+ set_dout_sram(&iv_seq[iv_seq_len], ivgen_ctx->pool,
+ CC_IVPOOL_META_SIZE);
+ set_flow_mode(&iv_seq[iv_seq_len], BYPASS);
+ iv_seq_len++;
+
+ /* Generate initial pool */
+ rc = cc_gen_iv_pool(ivgen_ctx, iv_seq, &iv_seq_len);
+ if (rc)
+ return rc;
+
+ /* Fire-and-forget */
+ return send_request_init(drvdata, iv_seq, iv_seq_len);
+}
+
+/*!
+ * Free iv-pool and ivgen context.
+ *
+ * \param drvdata
+ */
+void cc_ivgen_fini(struct cc_drvdata *drvdata)
+{
+ struct cc_ivgen_ctx *ivgen_ctx = drvdata->ivgen_handle;
+ struct device *device = &drvdata->plat_dev->dev;
+
+ if (!ivgen_ctx)
+ return;
+
+ if (ivgen_ctx->pool_meta) {
+ memset(ivgen_ctx->pool_meta, 0, CC_IVPOOL_META_SIZE);
+ dma_free_coherent(device, CC_IVPOOL_META_SIZE,
+ ivgen_ctx->pool_meta,
+ ivgen_ctx->pool_meta_dma);
+ }
+
+ ivgen_ctx->pool = NULL_SRAM_ADDR;
+
+ /* release "this" context */
+ kfree(ivgen_ctx);
+}
+
+/*!
+ * Allocates iv-pool and maps resources.
+ * This function generates the first IV pool.
+ *
+ * \param drvdata Driver's private context
+ *
+ * \return int Zero for success, negative value otherwise.
+ */
+int cc_ivgen_init(struct cc_drvdata *drvdata)
+{
+ struct cc_ivgen_ctx *ivgen_ctx;
+ struct device *device = &drvdata->plat_dev->dev;
+ int rc;
+
+ /* Allocate "this" context */
+ ivgen_ctx = kzalloc(sizeof(*ivgen_ctx), GFP_KERNEL);
+ if (!ivgen_ctx)
+ return -ENOMEM;
+
+ /* Allocate pool's header for initial enc. key/IV */
+ ivgen_ctx->pool_meta = dma_alloc_coherent(device, CC_IVPOOL_META_SIZE,
+ &ivgen_ctx->pool_meta_dma,
+ GFP_KERNEL);
+ if (!ivgen_ctx->pool_meta) {
+ dev_err(device, "Not enough memory to allocate DMA of pool_meta (%u B)\n",
+ CC_IVPOOL_META_SIZE);
+ rc = -ENOMEM;
+ goto out;
+ }
+ /* Allocate IV pool in SRAM */
+ ivgen_ctx->pool = cc_sram_alloc(drvdata, CC_IVPOOL_SIZE);
+ if (ivgen_ctx->pool == NULL_SRAM_ADDR) {
+ dev_err(device, "SRAM pool exhausted\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ drvdata->ivgen_handle = ivgen_ctx;
+
+ return cc_init_iv_sram(drvdata);
+
+out:
+ cc_ivgen_fini(drvdata);
+ return rc;
+}
+
+/*!
+ * Acquires 16 Bytes IV from the iv-pool
+ *
+ * \param drvdata Driver private context
+ * \param iv_out_dma Array of physical IV out addresses
+ * \param iv_out_dma_len Length of iv_out_dma array (additional elements
+ * of iv_out_dma array are ignore)
+ * \param iv_out_size May be 8 or 16 bytes long
+ * \param iv_seq IN/OUT array to the descriptors sequence
+ * \param iv_seq_len IN/OUT pointer to the sequence length
+ *
+ * \return int Zero for success, negative value otherwise.
+ */
+int cc_get_iv(struct cc_drvdata *drvdata, dma_addr_t iv_out_dma[],
+ unsigned int iv_out_dma_len, unsigned int iv_out_size,
+ struct cc_hw_desc iv_seq[], unsigned int *iv_seq_len)
+{
+ struct cc_ivgen_ctx *ivgen_ctx = drvdata->ivgen_handle;
+ unsigned int idx = *iv_seq_len;
+ struct device *dev = drvdata_to_dev(drvdata);
+ unsigned int t;
+
+ if (iv_out_size != CC_AES_IV_SIZE &&
+ iv_out_size != CTR_RFC3686_IV_SIZE) {
+ return -EINVAL;
+ }
+ if ((iv_out_dma_len + 1) > CC_IVPOOL_SEQ_LEN) {
+ /* The sequence will be longer than allowed */
+ return -EINVAL;
+ }
+
+ /* check that number of generated IV is limited to max dma address
+ * iv buffer size
+ */
+ if (iv_out_dma_len > CC_MAX_IVGEN_DMA_ADDRESSES) {
+ /* The sequence will be longer than allowed */
+ return -EINVAL;
+ }
+
+ for (t = 0; t < iv_out_dma_len; t++) {
+ /* Acquire IV from pool */
+ hw_desc_init(&iv_seq[idx]);
+ set_din_sram(&iv_seq[idx], (ivgen_ctx->pool +
+ ivgen_ctx->next_iv_ofs),
+ iv_out_size);
+ set_dout_dlli(&iv_seq[idx], iv_out_dma[t], iv_out_size,
+ NS_BIT, 0);
+ set_flow_mode(&iv_seq[idx], BYPASS);
+ idx++;
+ }
+
+ /* Bypass operation is proceeded by crypto sequence, hence must
+ * assure bypass-write-transaction by a memory barrier
+ */
+ hw_desc_init(&iv_seq[idx]);
+ set_din_no_dma(&iv_seq[idx], 0, 0xfffff0);
+ set_dout_no_dma(&iv_seq[idx], 0, 0, 1);
+ idx++;
+
+ *iv_seq_len = idx; /* update seq length */
+
+ /* Update iv index */
+ ivgen_ctx->next_iv_ofs += iv_out_size;
+
+ if ((CC_IVPOOL_SIZE - ivgen_ctx->next_iv_ofs) < CC_AES_IV_SIZE) {
+ dev_dbg(dev, "Pool exhausted, regenerating iv-pool\n");
+ /* pool is drained -regenerate it! */
+ return cc_gen_iv_pool(ivgen_ctx, iv_seq, iv_seq_len);
+ }
+
+ return 0;
+}
diff --git a/drivers/staging/ccree/cc_ivgen.h b/drivers/crypto/ccree/cc_ivgen.h
index b6ac16903dda..b6ac16903dda 100644
--- a/drivers/staging/ccree/cc_ivgen.h
+++ b/drivers/crypto/ccree/cc_ivgen.h
diff --git a/drivers/crypto/ccree/cc_kernel_regs.h b/drivers/crypto/ccree/cc_kernel_regs.h
new file mode 100644
index 000000000000..8d7262a35156
--- /dev/null
+++ b/drivers/crypto/ccree/cc_kernel_regs.h
@@ -0,0 +1,168 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
+
+#ifndef __CC_CRYS_KERNEL_H__
+#define __CC_CRYS_KERNEL_H__
+
+// --------------------------------------
+// BLOCK: DSCRPTR
+// --------------------------------------
+#define CC_DSCRPTR_COMPLETION_COUNTER_REG_OFFSET 0xE00UL
+#define CC_DSCRPTR_COMPLETION_COUNTER_COMPLETION_COUNTER_BIT_SHIFT 0x0UL
+#define CC_DSCRPTR_COMPLETION_COUNTER_COMPLETION_COUNTER_BIT_SIZE 0x6UL
+#define CC_DSCRPTR_COMPLETION_COUNTER_OVERFLOW_COUNTER_BIT_SHIFT 0x6UL
+#define CC_DSCRPTR_COMPLETION_COUNTER_OVERFLOW_COUNTER_BIT_SIZE 0x1UL
+#define CC_DSCRPTR_SW_RESET_REG_OFFSET 0xE40UL
+#define CC_DSCRPTR_SW_RESET_VALUE_BIT_SHIFT 0x0UL
+#define CC_DSCRPTR_SW_RESET_VALUE_BIT_SIZE 0x1UL
+#define CC_DSCRPTR_QUEUE_SRAM_SIZE_REG_OFFSET 0xE60UL
+#define CC_DSCRPTR_QUEUE_SRAM_SIZE_NUM_OF_DSCRPTR_BIT_SHIFT 0x0UL
+#define CC_DSCRPTR_QUEUE_SRAM_SIZE_NUM_OF_DSCRPTR_BIT_SIZE 0xAUL
+#define CC_DSCRPTR_QUEUE_SRAM_SIZE_DSCRPTR_SRAM_SIZE_BIT_SHIFT 0xAUL
+#define CC_DSCRPTR_QUEUE_SRAM_SIZE_DSCRPTR_SRAM_SIZE_BIT_SIZE 0xCUL
+#define CC_DSCRPTR_QUEUE_SRAM_SIZE_SRAM_SIZE_BIT_SHIFT 0x16UL
+#define CC_DSCRPTR_QUEUE_SRAM_SIZE_SRAM_SIZE_BIT_SIZE 0x3UL
+#define CC_DSCRPTR_SINGLE_ADDR_EN_REG_OFFSET 0xE64UL
+#define CC_DSCRPTR_SINGLE_ADDR_EN_VALUE_BIT_SHIFT 0x0UL
+#define CC_DSCRPTR_SINGLE_ADDR_EN_VALUE_BIT_SIZE 0x1UL
+#define CC_DSCRPTR_MEASURE_CNTR_REG_OFFSET 0xE68UL
+#define CC_DSCRPTR_MEASURE_CNTR_VALUE_BIT_SHIFT 0x0UL
+#define CC_DSCRPTR_MEASURE_CNTR_VALUE_BIT_SIZE 0x20UL
+#define CC_DSCRPTR_QUEUE_WORD0_REG_OFFSET 0xE80UL
+#define CC_DSCRPTR_QUEUE_WORD0_VALUE_BIT_SHIFT 0x0UL
+#define CC_DSCRPTR_QUEUE_WORD0_VALUE_BIT_SIZE 0x20UL
+#define CC_DSCRPTR_QUEUE_WORD1_REG_OFFSET 0xE84UL
+#define CC_DSCRPTR_QUEUE_WORD1_DIN_DMA_MODE_BIT_SHIFT 0x0UL
+#define CC_DSCRPTR_QUEUE_WORD1_DIN_DMA_MODE_BIT_SIZE 0x2UL
+#define CC_DSCRPTR_QUEUE_WORD1_DIN_SIZE_BIT_SHIFT 0x2UL
+#define CC_DSCRPTR_QUEUE_WORD1_DIN_SIZE_BIT_SIZE 0x18UL
+#define CC_DSCRPTR_QUEUE_WORD1_NS_BIT_BIT_SHIFT 0x1AUL
+#define CC_DSCRPTR_QUEUE_WORD1_NS_BIT_BIT_SIZE 0x1UL
+#define CC_DSCRPTR_QUEUE_WORD1_DIN_CONST_VALUE_BIT_SHIFT 0x1BUL
+#define CC_DSCRPTR_QUEUE_WORD1_DIN_CONST_VALUE_BIT_SIZE 0x1UL
+#define CC_DSCRPTR_QUEUE_WORD1_NOT_LAST_BIT_SHIFT 0x1CUL
+#define CC_DSCRPTR_QUEUE_WORD1_NOT_LAST_BIT_SIZE 0x1UL
+#define CC_DSCRPTR_QUEUE_WORD1_LOCK_QUEUE_BIT_SHIFT 0x1DUL
+#define CC_DSCRPTR_QUEUE_WORD1_LOCK_QUEUE_BIT_SIZE 0x1UL
+#define CC_DSCRPTR_QUEUE_WORD1_NOT_USED_BIT_SHIFT 0x1EUL
+#define CC_DSCRPTR_QUEUE_WORD1_NOT_USED_BIT_SIZE 0x2UL
+#define CC_DSCRPTR_QUEUE_WORD2_REG_OFFSET 0xE88UL
+#define CC_DSCRPTR_QUEUE_WORD2_VALUE_BIT_SHIFT 0x0UL
+#define CC_DSCRPTR_QUEUE_WORD2_VALUE_BIT_SIZE 0x20UL
+#define CC_DSCRPTR_QUEUE_WORD3_REG_OFFSET 0xE8CUL
+#define CC_DSCRPTR_QUEUE_WORD3_DOUT_DMA_MODE_BIT_SHIFT 0x0UL
+#define CC_DSCRPTR_QUEUE_WORD3_DOUT_DMA_MODE_BIT_SIZE 0x2UL
+#define CC_DSCRPTR_QUEUE_WORD3_DOUT_SIZE_BIT_SHIFT 0x2UL
+#define CC_DSCRPTR_QUEUE_WORD3_DOUT_SIZE_BIT_SIZE 0x18UL
+#define CC_DSCRPTR_QUEUE_WORD3_NS_BIT_BIT_SHIFT 0x1AUL
+#define CC_DSCRPTR_QUEUE_WORD3_NS_BIT_BIT_SIZE 0x1UL
+#define CC_DSCRPTR_QUEUE_WORD3_DOUT_LAST_IND_BIT_SHIFT 0x1BUL
+#define CC_DSCRPTR_QUEUE_WORD3_DOUT_LAST_IND_BIT_SIZE 0x1UL
+#define CC_DSCRPTR_QUEUE_WORD3_HASH_XOR_BIT_BIT_SHIFT 0x1DUL
+#define CC_DSCRPTR_QUEUE_WORD3_HASH_XOR_BIT_BIT_SIZE 0x1UL
+#define CC_DSCRPTR_QUEUE_WORD3_NOT_USED_BIT_SHIFT 0x1EUL
+#define CC_DSCRPTR_QUEUE_WORD3_NOT_USED_BIT_SIZE 0x1UL
+#define CC_DSCRPTR_QUEUE_WORD3_QUEUE_LAST_IND_BIT_SHIFT 0x1FUL
+#define CC_DSCRPTR_QUEUE_WORD3_QUEUE_LAST_IND_BIT_SIZE 0x1UL
+#define CC_DSCRPTR_QUEUE_WORD4_REG_OFFSET 0xE90UL
+#define CC_DSCRPTR_QUEUE_WORD4_DATA_FLOW_MODE_BIT_SHIFT 0x0UL
+#define CC_DSCRPTR_QUEUE_WORD4_DATA_FLOW_MODE_BIT_SIZE 0x6UL
+#define CC_DSCRPTR_QUEUE_WORD4_AES_SEL_N_HASH_BIT_SHIFT 0x6UL
+#define CC_DSCRPTR_QUEUE_WORD4_AES_SEL_N_HASH_BIT_SIZE 0x1UL
+#define CC_DSCRPTR_QUEUE_WORD4_AES_XOR_CRYPTO_KEY_BIT_SHIFT 0x7UL
+#define CC_DSCRPTR_QUEUE_WORD4_AES_XOR_CRYPTO_KEY_BIT_SIZE 0x1UL
+#define CC_DSCRPTR_QUEUE_WORD4_ACK_NEEDED_BIT_SHIFT 0x8UL
+#define CC_DSCRPTR_QUEUE_WORD4_ACK_NEEDED_BIT_SIZE 0x2UL
+#define CC_DSCRPTR_QUEUE_WORD4_CIPHER_MODE_BIT_SHIFT 0xAUL
+#define CC_DSCRPTR_QUEUE_WORD4_CIPHER_MODE_BIT_SIZE 0x4UL
+#define CC_DSCRPTR_QUEUE_WORD4_CMAC_SIZE0_BIT_SHIFT 0xEUL
+#define CC_DSCRPTR_QUEUE_WORD4_CMAC_SIZE0_BIT_SIZE 0x1UL
+#define CC_DSCRPTR_QUEUE_WORD4_CIPHER_DO_BIT_SHIFT 0xFUL
+#define CC_DSCRPTR_QUEUE_WORD4_CIPHER_DO_BIT_SIZE 0x2UL
+#define CC_DSCRPTR_QUEUE_WORD4_CIPHER_CONF0_BIT_SHIFT 0x11UL
+#define CC_DSCRPTR_QUEUE_WORD4_CIPHER_CONF0_BIT_SIZE 0x2UL
+#define CC_DSCRPTR_QUEUE_WORD4_CIPHER_CONF1_BIT_SHIFT 0x13UL
+#define CC_DSCRPTR_QUEUE_WORD4_CIPHER_CONF1_BIT_SIZE 0x1UL
+#define CC_DSCRPTR_QUEUE_WORD4_CIPHER_CONF2_BIT_SHIFT 0x14UL
+#define CC_DSCRPTR_QUEUE_WORD4_CIPHER_CONF2_BIT_SIZE 0x2UL
+#define CC_DSCRPTR_QUEUE_WORD4_KEY_SIZE_BIT_SHIFT 0x16UL
+#define CC_DSCRPTR_QUEUE_WORD4_KEY_SIZE_BIT_SIZE 0x2UL
+#define CC_DSCRPTR_QUEUE_WORD4_SETUP_OPERATION_BIT_SHIFT 0x18UL
+#define CC_DSCRPTR_QUEUE_WORD4_SETUP_OPERATION_BIT_SIZE 0x4UL
+#define CC_DSCRPTR_QUEUE_WORD4_DIN_SRAM_ENDIANNESS_BIT_SHIFT 0x1CUL
+#define CC_DSCRPTR_QUEUE_WORD4_DIN_SRAM_ENDIANNESS_BIT_SIZE 0x1UL
+#define CC_DSCRPTR_QUEUE_WORD4_DOUT_SRAM_ENDIANNESS_BIT_SHIFT 0x1DUL
+#define CC_DSCRPTR_QUEUE_WORD4_DOUT_SRAM_ENDIANNESS_BIT_SIZE 0x1UL
+#define CC_DSCRPTR_QUEUE_WORD4_WORD_SWAP_BIT_SHIFT 0x1EUL
+#define CC_DSCRPTR_QUEUE_WORD4_WORD_SWAP_BIT_SIZE 0x1UL
+#define CC_DSCRPTR_QUEUE_WORD4_BYTES_SWAP_BIT_SHIFT 0x1FUL
+#define CC_DSCRPTR_QUEUE_WORD4_BYTES_SWAP_BIT_SIZE 0x1UL
+#define CC_DSCRPTR_QUEUE_WORD5_REG_OFFSET 0xE94UL
+#define CC_DSCRPTR_QUEUE_WORD5_DIN_ADDR_HIGH_BIT_SHIFT 0x0UL
+#define CC_DSCRPTR_QUEUE_WORD5_DIN_ADDR_HIGH_BIT_SIZE 0x10UL
+#define CC_DSCRPTR_QUEUE_WORD5_DOUT_ADDR_HIGH_BIT_SHIFT 0x10UL
+#define CC_DSCRPTR_QUEUE_WORD5_DOUT_ADDR_HIGH_BIT_SIZE 0x10UL
+#define CC_DSCRPTR_QUEUE_WATERMARK_REG_OFFSET 0xE98UL
+#define CC_DSCRPTR_QUEUE_WATERMARK_VALUE_BIT_SHIFT 0x0UL
+#define CC_DSCRPTR_QUEUE_WATERMARK_VALUE_BIT_SIZE 0xAUL
+#define CC_DSCRPTR_QUEUE_CONTENT_REG_OFFSET 0xE9CUL
+#define CC_DSCRPTR_QUEUE_CONTENT_VALUE_BIT_SHIFT 0x0UL
+#define CC_DSCRPTR_QUEUE_CONTENT_VALUE_BIT_SIZE 0xAUL
+// --------------------------------------
+// BLOCK: AXI_P
+// --------------------------------------
+#define CC_AXIM_MON_INFLIGHT_REG_OFFSET 0xB00UL
+#define CC_AXIM_MON_INFLIGHT_VALUE_BIT_SHIFT 0x0UL
+#define CC_AXIM_MON_INFLIGHT_VALUE_BIT_SIZE 0x8UL
+#define CC_AXIM_MON_INFLIGHTLAST_REG_OFFSET 0xB40UL
+#define CC_AXIM_MON_INFLIGHTLAST_VALUE_BIT_SHIFT 0x0UL
+#define CC_AXIM_MON_INFLIGHTLAST_VALUE_BIT_SIZE 0x8UL
+#define CC_AXIM_MON_COMP_REG_OFFSET 0xB80UL
+#define CC_AXIM_MON_COMP8_REG_OFFSET 0xBA0UL
+#define CC_AXIM_MON_COMP_VALUE_BIT_SHIFT 0x0UL
+#define CC_AXIM_MON_COMP_VALUE_BIT_SIZE 0x10UL
+#define CC_AXIM_MON_ERR_REG_OFFSET 0xBC4UL
+#define CC_AXIM_MON_ERR_BRESP_BIT_SHIFT 0x0UL
+#define CC_AXIM_MON_ERR_BRESP_BIT_SIZE 0x2UL
+#define CC_AXIM_MON_ERR_BID_BIT_SHIFT 0x2UL
+#define CC_AXIM_MON_ERR_BID_BIT_SIZE 0x4UL
+#define CC_AXIM_MON_ERR_RRESP_BIT_SHIFT 0x10UL
+#define CC_AXIM_MON_ERR_RRESP_BIT_SIZE 0x2UL
+#define CC_AXIM_MON_ERR_RID_BIT_SHIFT 0x12UL
+#define CC_AXIM_MON_ERR_RID_BIT_SIZE 0x4UL
+#define CC_AXIM_CFG_REG_OFFSET 0xBE8UL
+#define CC_AXIM_CFG_BRESPMASK_BIT_SHIFT 0x4UL
+#define CC_AXIM_CFG_BRESPMASK_BIT_SIZE 0x1UL
+#define CC_AXIM_CFG_RRESPMASK_BIT_SHIFT 0x5UL
+#define CC_AXIM_CFG_RRESPMASK_BIT_SIZE 0x1UL
+#define CC_AXIM_CFG_INFLTMASK_BIT_SHIFT 0x6UL
+#define CC_AXIM_CFG_INFLTMASK_BIT_SIZE 0x1UL
+#define CC_AXIM_CFG_COMPMASK_BIT_SHIFT 0x7UL
+#define CC_AXIM_CFG_COMPMASK_BIT_SIZE 0x1UL
+#define CC_AXIM_ACE_CONST_REG_OFFSET 0xBECUL
+#define CC_AXIM_ACE_CONST_ARDOMAIN_BIT_SHIFT 0x0UL
+#define CC_AXIM_ACE_CONST_ARDOMAIN_BIT_SIZE 0x2UL
+#define CC_AXIM_ACE_CONST_AWDOMAIN_BIT_SHIFT 0x2UL
+#define CC_AXIM_ACE_CONST_AWDOMAIN_BIT_SIZE 0x2UL
+#define CC_AXIM_ACE_CONST_ARBAR_BIT_SHIFT 0x4UL
+#define CC_AXIM_ACE_CONST_ARBAR_BIT_SIZE 0x2UL
+#define CC_AXIM_ACE_CONST_AWBAR_BIT_SHIFT 0x6UL
+#define CC_AXIM_ACE_CONST_AWBAR_BIT_SIZE 0x2UL
+#define CC_AXIM_ACE_CONST_ARSNOOP_BIT_SHIFT 0x8UL
+#define CC_AXIM_ACE_CONST_ARSNOOP_BIT_SIZE 0x4UL
+#define CC_AXIM_ACE_CONST_AWSNOOP_NOT_ALIGNED_BIT_SHIFT 0xCUL
+#define CC_AXIM_ACE_CONST_AWSNOOP_NOT_ALIGNED_BIT_SIZE 0x3UL
+#define CC_AXIM_ACE_CONST_AWSNOOP_ALIGNED_BIT_SHIFT 0xFUL
+#define CC_AXIM_ACE_CONST_AWSNOOP_ALIGNED_BIT_SIZE 0x3UL
+#define CC_AXIM_ACE_CONST_AWADDR_NOT_MASKED_BIT_SHIFT 0x12UL
+#define CC_AXIM_ACE_CONST_AWADDR_NOT_MASKED_BIT_SIZE 0x7UL
+#define CC_AXIM_ACE_CONST_AWLEN_VAL_BIT_SHIFT 0x19UL
+#define CC_AXIM_ACE_CONST_AWLEN_VAL_BIT_SIZE 0x4UL
+#define CC_AXIM_CACHE_PARAMS_REG_OFFSET 0xBF0UL
+#define CC_AXIM_CACHE_PARAMS_AWCACHE_LAST_BIT_SHIFT 0x0UL
+#define CC_AXIM_CACHE_PARAMS_AWCACHE_LAST_BIT_SIZE 0x4UL
+#define CC_AXIM_CACHE_PARAMS_AWCACHE_BIT_SHIFT 0x4UL
+#define CC_AXIM_CACHE_PARAMS_AWCACHE_BIT_SIZE 0x4UL
+#define CC_AXIM_CACHE_PARAMS_ARCACHE_BIT_SHIFT 0x8UL
+#define CC_AXIM_CACHE_PARAMS_ARCACHE_BIT_SIZE 0x4UL
+#endif // __CC_CRYS_KERNEL_H__
diff --git a/drivers/staging/ccree/cc_lli_defs.h b/drivers/crypto/ccree/cc_lli_defs.h
index 64b15ac9f1d3..64b15ac9f1d3 100644
--- a/drivers/staging/ccree/cc_lli_defs.h
+++ b/drivers/crypto/ccree/cc_lli_defs.h
diff --git a/drivers/staging/ccree/cc_pm.c b/drivers/crypto/ccree/cc_pm.c
index d990f472e89f..d990f472e89f 100644
--- a/drivers/staging/ccree/cc_pm.c
+++ b/drivers/crypto/ccree/cc_pm.c
diff --git a/drivers/crypto/ccree/cc_pm.h b/drivers/crypto/ccree/cc_pm.h
new file mode 100644
index 000000000000..020a5403c58b
--- /dev/null
+++ b/drivers/crypto/ccree/cc_pm.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
+
+/* \file cc_pm.h
+ */
+
+#ifndef __CC_POWER_MGR_H__
+#define __CC_POWER_MGR_H__
+
+#include "cc_driver.h"
+
+#define CC_SUSPEND_TIMEOUT 3000
+
+#if defined(CONFIG_PM)
+
+extern const struct dev_pm_ops ccree_pm;
+
+int cc_pm_init(struct cc_drvdata *drvdata);
+void cc_pm_fini(struct cc_drvdata *drvdata);
+int cc_pm_suspend(struct device *dev);
+int cc_pm_resume(struct device *dev);
+int cc_pm_get(struct device *dev);
+int cc_pm_put_suspend(struct device *dev);
+
+#else
+
+static inline int cc_pm_init(struct cc_drvdata *drvdata)
+{
+ return 0;
+}
+
+static inline void cc_pm_fini(struct cc_drvdata *drvdata) {}
+
+static inline int cc_pm_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static inline int cc_pm_resume(struct device *dev)
+{
+ return 0;
+}
+
+static inline int cc_pm_get(struct device *dev)
+{
+ return 0;
+}
+
+static inline int cc_pm_put_suspend(struct device *dev)
+{
+ return 0;
+}
+
+#endif
+
+#endif /*__POWER_MGR_H__*/
diff --git a/drivers/crypto/ccree/cc_request_mgr.c b/drivers/crypto/ccree/cc_request_mgr.c
new file mode 100644
index 000000000000..83a8aaae61c7
--- /dev/null
+++ b/drivers/crypto/ccree/cc_request_mgr.c
@@ -0,0 +1,711 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
+
+#include <linux/kernel.h>
+#include "cc_driver.h"
+#include "cc_buffer_mgr.h"
+#include "cc_request_mgr.h"
+#include "cc_ivgen.h"
+#include "cc_pm.h"
+
+#define CC_MAX_POLL_ITER 10
+/* The highest descriptor count in used */
+#define CC_MAX_DESC_SEQ_LEN 23
+
+struct cc_req_mgr_handle {
+ /* Request manager resources */
+ unsigned int hw_queue_size; /* HW capability */
+ unsigned int min_free_hw_slots;
+ unsigned int max_used_sw_slots;
+ struct cc_crypto_req req_queue[MAX_REQUEST_QUEUE_SIZE];
+ u32 req_queue_head;
+ u32 req_queue_tail;
+ u32 axi_completed;
+ u32 q_free_slots;
+ /* This lock protects access to HW register
+ * that must be single request at a time
+ */
+ spinlock_t hw_lock;
+ struct cc_hw_desc compl_desc;
+ u8 *dummy_comp_buff;
+ dma_addr_t dummy_comp_buff_dma;
+
+ /* backlog queue */
+ struct list_head backlog;
+ unsigned int bl_len;
+ spinlock_t bl_lock; /* protect backlog queue */
+
+#ifdef COMP_IN_WQ
+ struct workqueue_struct *workq;
+ struct delayed_work compwork;
+#else
+ struct tasklet_struct comptask;
+#endif
+ bool is_runtime_suspended;
+};
+
+struct cc_bl_item {
+ struct cc_crypto_req creq;
+ struct cc_hw_desc desc[CC_MAX_DESC_SEQ_LEN];
+ unsigned int len;
+ struct list_head list;
+ bool notif;
+};
+
+static void comp_handler(unsigned long devarg);
+#ifdef COMP_IN_WQ
+static void comp_work_handler(struct work_struct *work);
+#endif
+
+void cc_req_mgr_fini(struct cc_drvdata *drvdata)
+{
+ struct cc_req_mgr_handle *req_mgr_h = drvdata->request_mgr_handle;
+ struct device *dev = drvdata_to_dev(drvdata);
+
+ if (!req_mgr_h)
+ return; /* Not allocated */
+
+ if (req_mgr_h->dummy_comp_buff_dma) {
+ dma_free_coherent(dev, sizeof(u32), req_mgr_h->dummy_comp_buff,
+ req_mgr_h->dummy_comp_buff_dma);
+ }
+
+ dev_dbg(dev, "max_used_hw_slots=%d\n", (req_mgr_h->hw_queue_size -
+ req_mgr_h->min_free_hw_slots));
+ dev_dbg(dev, "max_used_sw_slots=%d\n", req_mgr_h->max_used_sw_slots);
+
+#ifdef COMP_IN_WQ
+ flush_workqueue(req_mgr_h->workq);
+ destroy_workqueue(req_mgr_h->workq);
+#else
+ /* Kill tasklet */
+ tasklet_kill(&req_mgr_h->comptask);
+#endif
+ kzfree(req_mgr_h);
+ drvdata->request_mgr_handle = NULL;
+}
+
+int cc_req_mgr_init(struct cc_drvdata *drvdata)
+{
+ struct cc_req_mgr_handle *req_mgr_h;
+ struct device *dev = drvdata_to_dev(drvdata);
+ int rc = 0;
+
+ req_mgr_h = kzalloc(sizeof(*req_mgr_h), GFP_KERNEL);
+ if (!req_mgr_h) {
+ rc = -ENOMEM;
+ goto req_mgr_init_err;
+ }
+
+ drvdata->request_mgr_handle = req_mgr_h;
+
+ spin_lock_init(&req_mgr_h->hw_lock);
+ spin_lock_init(&req_mgr_h->bl_lock);
+ INIT_LIST_HEAD(&req_mgr_h->backlog);
+
+#ifdef COMP_IN_WQ
+ dev_dbg(dev, "Initializing completion workqueue\n");
+ req_mgr_h->workq = create_singlethread_workqueue("ccree");
+ if (!req_mgr_h->workq) {
+ dev_err(dev, "Failed creating work queue\n");
+ rc = -ENOMEM;
+ goto req_mgr_init_err;
+ }
+ INIT_DELAYED_WORK(&req_mgr_h->compwork, comp_work_handler);
+#else
+ dev_dbg(dev, "Initializing completion tasklet\n");
+ tasklet_init(&req_mgr_h->comptask, comp_handler,
+ (unsigned long)drvdata);
+#endif
+ req_mgr_h->hw_queue_size = cc_ioread(drvdata,
+ CC_REG(DSCRPTR_QUEUE_SRAM_SIZE));
+ dev_dbg(dev, "hw_queue_size=0x%08X\n", req_mgr_h->hw_queue_size);
+ if (req_mgr_h->hw_queue_size < MIN_HW_QUEUE_SIZE) {
+ dev_err(dev, "Invalid HW queue size = %u (Min. required is %u)\n",
+ req_mgr_h->hw_queue_size, MIN_HW_QUEUE_SIZE);
+ rc = -ENOMEM;
+ goto req_mgr_init_err;
+ }
+ req_mgr_h->min_free_hw_slots = req_mgr_h->hw_queue_size;
+ req_mgr_h->max_used_sw_slots = 0;
+
+ /* Allocate DMA word for "dummy" completion descriptor use */
+ req_mgr_h->dummy_comp_buff =
+ dma_alloc_coherent(dev, sizeof(u32),
+ &req_mgr_h->dummy_comp_buff_dma,
+ GFP_KERNEL);
+ if (!req_mgr_h->dummy_comp_buff) {
+ dev_err(dev, "Not enough memory to allocate DMA (%zu) dropped buffer\n",
+ sizeof(u32));
+ rc = -ENOMEM;
+ goto req_mgr_init_err;
+ }
+
+ /* Init. "dummy" completion descriptor */
+ hw_desc_init(&req_mgr_h->compl_desc);
+ set_din_const(&req_mgr_h->compl_desc, 0, sizeof(u32));
+ set_dout_dlli(&req_mgr_h->compl_desc, req_mgr_h->dummy_comp_buff_dma,
+ sizeof(u32), NS_BIT, 1);
+ set_flow_mode(&req_mgr_h->compl_desc, BYPASS);
+ set_queue_last_ind(drvdata, &req_mgr_h->compl_desc);
+
+ return 0;
+
+req_mgr_init_err:
+ cc_req_mgr_fini(drvdata);
+ return rc;
+}
+
+static void enqueue_seq(struct cc_drvdata *drvdata, struct cc_hw_desc seq[],
+ unsigned int seq_len)
+{
+ int i, w;
+ void __iomem *reg = drvdata->cc_base + CC_REG(DSCRPTR_QUEUE_WORD0);
+ struct device *dev = drvdata_to_dev(drvdata);
+
+ /*
+ * We do indeed write all 6 command words to the same
+ * register. The HW supports this.
+ */
+
+ for (i = 0; i < seq_len; i++) {
+ for (w = 0; w <= 5; w++)
+ writel_relaxed(seq[i].word[w], reg);
+
+ if (cc_dump_desc)
+ dev_dbg(dev, "desc[%02d]: 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X\n",
+ i, seq[i].word[0], seq[i].word[1],
+ seq[i].word[2], seq[i].word[3],
+ seq[i].word[4], seq[i].word[5]);
+ }
+}
+
+/*!
+ * Completion will take place if and only if user requested completion
+ * by cc_send_sync_request().
+ *
+ * \param dev
+ * \param dx_compl_h The completion event to signal
+ */
+static void request_mgr_complete(struct device *dev, void *dx_compl_h,
+ int dummy)
+{
+ struct completion *this_compl = dx_compl_h;
+
+ complete(this_compl);
+}
+
+static int cc_queues_status(struct cc_drvdata *drvdata,
+ struct cc_req_mgr_handle *req_mgr_h,
+ unsigned int total_seq_len)
+{
+ unsigned long poll_queue;
+ struct device *dev = drvdata_to_dev(drvdata);
+
+ /* SW queue is checked only once as it will not
+ * be chaned during the poll because the spinlock_bh
+ * is held by the thread
+ */
+ if (((req_mgr_h->req_queue_head + 1) & (MAX_REQUEST_QUEUE_SIZE - 1)) ==
+ req_mgr_h->req_queue_tail) {
+ dev_err(dev, "SW FIFO is full. req_queue_head=%d sw_fifo_len=%d\n",
+ req_mgr_h->req_queue_head, MAX_REQUEST_QUEUE_SIZE);
+ return -ENOSPC;
+ }
+
+ if (req_mgr_h->q_free_slots >= total_seq_len)
+ return 0;
+
+ /* Wait for space in HW queue. Poll constant num of iterations. */
+ for (poll_queue = 0; poll_queue < CC_MAX_POLL_ITER ; poll_queue++) {
+ req_mgr_h->q_free_slots =
+ cc_ioread(drvdata, CC_REG(DSCRPTR_QUEUE_CONTENT));
+ if (req_mgr_h->q_free_slots < req_mgr_h->min_free_hw_slots)
+ req_mgr_h->min_free_hw_slots = req_mgr_h->q_free_slots;
+
+ if (req_mgr_h->q_free_slots >= total_seq_len) {
+ /* If there is enough place return */
+ return 0;
+ }
+
+ dev_dbg(dev, "HW FIFO is full. q_free_slots=%d total_seq_len=%d\n",
+ req_mgr_h->q_free_slots, total_seq_len);
+ }
+ /* No room in the HW queue try again later */
+ dev_dbg(dev, "HW FIFO full, timeout. req_queue_head=%d sw_fifo_len=%d q_free_slots=%d total_seq_len=%d\n",
+ req_mgr_h->req_queue_head, MAX_REQUEST_QUEUE_SIZE,
+ req_mgr_h->q_free_slots, total_seq_len);
+ return -ENOSPC;
+}
+
+/*!
+ * Enqueue caller request to crypto hardware.
+ * Need to be called with HW lock held and PM running
+ *
+ * \param drvdata
+ * \param cc_req The request to enqueue
+ * \param desc The crypto sequence
+ * \param len The crypto sequence length
+ * \param add_comp If "true": add an artificial dout DMA to mark completion
+ *
+ * \return int Returns -EINPROGRESS or error code
+ */
+static int cc_do_send_request(struct cc_drvdata *drvdata,
+ struct cc_crypto_req *cc_req,
+ struct cc_hw_desc *desc, unsigned int len,
+ bool add_comp, bool ivgen)
+{
+ struct cc_req_mgr_handle *req_mgr_h = drvdata->request_mgr_handle;
+ unsigned int used_sw_slots;
+ unsigned int iv_seq_len = 0;
+ unsigned int total_seq_len = len; /*initial sequence length*/
+ struct cc_hw_desc iv_seq[CC_IVPOOL_SEQ_LEN];
+ struct device *dev = drvdata_to_dev(drvdata);
+ int rc;
+
+ if (ivgen) {
+ dev_dbg(dev, "Acquire IV from pool into %d DMA addresses %pad, %pad, %pad, IV-size=%u\n",
+ cc_req->ivgen_dma_addr_len,
+ &cc_req->ivgen_dma_addr[0],
+ &cc_req->ivgen_dma_addr[1],
+ &cc_req->ivgen_dma_addr[2],
+ cc_req->ivgen_size);
+
+ /* Acquire IV from pool */
+ rc = cc_get_iv(drvdata, cc_req->ivgen_dma_addr,
+ cc_req->ivgen_dma_addr_len,
+ cc_req->ivgen_size, iv_seq, &iv_seq_len);
+
+ if (rc) {
+ dev_err(dev, "Failed to generate IV (rc=%d)\n", rc);
+ return rc;
+ }
+
+ total_seq_len += iv_seq_len;
+ }
+
+ used_sw_slots = ((req_mgr_h->req_queue_head -
+ req_mgr_h->req_queue_tail) &
+ (MAX_REQUEST_QUEUE_SIZE - 1));
+ if (used_sw_slots > req_mgr_h->max_used_sw_slots)
+ req_mgr_h->max_used_sw_slots = used_sw_slots;
+
+ /* Enqueue request - must be locked with HW lock*/
+ req_mgr_h->req_queue[req_mgr_h->req_queue_head] = *cc_req;
+ req_mgr_h->req_queue_head = (req_mgr_h->req_queue_head + 1) &
+ (MAX_REQUEST_QUEUE_SIZE - 1);
+ /* TODO: Use circ_buf.h ? */
+
+ dev_dbg(dev, "Enqueue request head=%u\n", req_mgr_h->req_queue_head);
+
+ /*
+ * We are about to push command to the HW via the command registers
+ * that may refernece hsot memory. We need to issue a memory barrier
+ * to make sure there are no outstnading memory writes
+ */
+ wmb();
+
+ /* STAT_PHASE_4: Push sequence */
+ if (ivgen)
+ enqueue_seq(drvdata, iv_seq, iv_seq_len);
+
+ enqueue_seq(drvdata, desc, len);
+
+ if (add_comp) {
+ enqueue_seq(drvdata, &req_mgr_h->compl_desc, 1);
+ total_seq_len++;
+ }
+
+ if (req_mgr_h->q_free_slots < total_seq_len) {
+ /* This situation should never occur. Maybe indicating problem
+ * with resuming power. Set the free slot count to 0 and hope
+ * for the best.
+ */
+ dev_err(dev, "HW free slot count mismatch.");
+ req_mgr_h->q_free_slots = 0;
+ } else {
+ /* Update the free slots in HW queue */
+ req_mgr_h->q_free_slots -= total_seq_len;
+ }
+
+ /* Operation still in process */
+ return -EINPROGRESS;
+}
+
+static void cc_enqueue_backlog(struct cc_drvdata *drvdata,
+ struct cc_bl_item *bli)
+{
+ struct cc_req_mgr_handle *mgr = drvdata->request_mgr_handle;
+
+ spin_lock_bh(&mgr->bl_lock);
+ list_add_tail(&bli->list, &mgr->backlog);
+ ++mgr->bl_len;
+ spin_unlock_bh(&mgr->bl_lock);
+ tasklet_schedule(&mgr->comptask);
+}
+
+static void cc_proc_backlog(struct cc_drvdata *drvdata)
+{
+ struct cc_req_mgr_handle *mgr = drvdata->request_mgr_handle;
+ struct cc_bl_item *bli;
+ struct cc_crypto_req *creq;
+ struct crypto_async_request *req;
+ bool ivgen;
+ unsigned int total_len;
+ struct device *dev = drvdata_to_dev(drvdata);
+ int rc;
+
+ spin_lock(&mgr->bl_lock);
+
+ while (mgr->bl_len) {
+ bli = list_first_entry(&mgr->backlog, struct cc_bl_item, list);
+ spin_unlock(&mgr->bl_lock);
+
+ creq = &bli->creq;
+ req = (struct crypto_async_request *)creq->user_arg;
+
+ /*
+ * Notify the request we're moving out of the backlog
+ * but only if we haven't done so already.
+ */
+ if (!bli->notif) {
+ req->complete(req, -EINPROGRESS);
+ bli->notif = true;
+ }
+
+ ivgen = !!creq->ivgen_dma_addr_len;
+ total_len = bli->len + (ivgen ? CC_IVPOOL_SEQ_LEN : 0);
+
+ spin_lock(&mgr->hw_lock);
+
+ rc = cc_queues_status(drvdata, mgr, total_len);
+ if (rc) {
+ /*
+ * There is still not room in the FIFO for
+ * this request. Bail out. We'll return here
+ * on the next completion irq.
+ */
+ spin_unlock(&mgr->hw_lock);
+ return;
+ }
+
+ rc = cc_do_send_request(drvdata, &bli->creq, bli->desc,
+ bli->len, false, ivgen);
+
+ spin_unlock(&mgr->hw_lock);
+
+ if (rc != -EINPROGRESS) {
+ cc_pm_put_suspend(dev);
+ creq->user_cb(dev, req, rc);
+ }
+
+ /* Remove ourselves from the backlog list */
+ spin_lock(&mgr->bl_lock);
+ list_del(&bli->list);
+ --mgr->bl_len;
+ }
+
+ spin_unlock(&mgr->bl_lock);
+}
+
+int cc_send_request(struct cc_drvdata *drvdata, struct cc_crypto_req *cc_req,
+ struct cc_hw_desc *desc, unsigned int len,
+ struct crypto_async_request *req)
+{
+ int rc;
+ struct cc_req_mgr_handle *mgr = drvdata->request_mgr_handle;
+ bool ivgen = !!cc_req->ivgen_dma_addr_len;
+ unsigned int total_len = len + (ivgen ? CC_IVPOOL_SEQ_LEN : 0);
+ struct device *dev = drvdata_to_dev(drvdata);
+ bool backlog_ok = req->flags & CRYPTO_TFM_REQ_MAY_BACKLOG;
+ gfp_t flags = cc_gfp_flags(req);
+ struct cc_bl_item *bli;
+
+ rc = cc_pm_get(dev);
+ if (rc) {
+ dev_err(dev, "ssi_power_mgr_runtime_get returned %x\n", rc);
+ return rc;
+ }
+
+ spin_lock_bh(&mgr->hw_lock);
+ rc = cc_queues_status(drvdata, mgr, total_len);
+
+#ifdef CC_DEBUG_FORCE_BACKLOG
+ if (backlog_ok)
+ rc = -ENOSPC;
+#endif /* CC_DEBUG_FORCE_BACKLOG */
+
+ if (rc == -ENOSPC && backlog_ok) {
+ spin_unlock_bh(&mgr->hw_lock);
+
+ bli = kmalloc(sizeof(*bli), flags);
+ if (!bli) {
+ cc_pm_put_suspend(dev);
+ return -ENOMEM;
+ }
+
+ memcpy(&bli->creq, cc_req, sizeof(*cc_req));
+ memcpy(&bli->desc, desc, len * sizeof(*desc));
+ bli->len = len;
+ bli->notif = false;
+ cc_enqueue_backlog(drvdata, bli);
+ return -EBUSY;
+ }
+
+ if (!rc)
+ rc = cc_do_send_request(drvdata, cc_req, desc, len, false,
+ ivgen);
+
+ spin_unlock_bh(&mgr->hw_lock);
+ return rc;
+}
+
+int cc_send_sync_request(struct cc_drvdata *drvdata,
+ struct cc_crypto_req *cc_req, struct cc_hw_desc *desc,
+ unsigned int len)
+{
+ int rc;
+ struct device *dev = drvdata_to_dev(drvdata);
+ struct cc_req_mgr_handle *mgr = drvdata->request_mgr_handle;
+
+ init_completion(&cc_req->seq_compl);
+ cc_req->user_cb = request_mgr_complete;
+ cc_req->user_arg = &cc_req->seq_compl;
+
+ rc = cc_pm_get(dev);
+ if (rc) {
+ dev_err(dev, "ssi_power_mgr_runtime_get returned %x\n", rc);
+ return rc;
+ }
+
+ while (true) {
+ spin_lock_bh(&mgr->hw_lock);
+ rc = cc_queues_status(drvdata, mgr, len + 1);
+
+ if (!rc)
+ break;
+
+ spin_unlock_bh(&mgr->hw_lock);
+ if (rc != -EAGAIN) {
+ cc_pm_put_suspend(dev);
+ return rc;
+ }
+ wait_for_completion_interruptible(&drvdata->hw_queue_avail);
+ reinit_completion(&drvdata->hw_queue_avail);
+ }
+
+ rc = cc_do_send_request(drvdata, cc_req, desc, len, true, false);
+ spin_unlock_bh(&mgr->hw_lock);
+
+ if (rc != -EINPROGRESS) {
+ cc_pm_put_suspend(dev);
+ return rc;
+ }
+
+ wait_for_completion(&cc_req->seq_compl);
+ return 0;
+}
+
+/*!
+ * Enqueue caller request to crypto hardware during init process.
+ * assume this function is not called in middle of a flow,
+ * since we set QUEUE_LAST_IND flag in the last descriptor.
+ *
+ * \param drvdata
+ * \param desc The crypto sequence
+ * \param len The crypto sequence length
+ *
+ * \return int Returns "0" upon success
+ */
+int send_request_init(struct cc_drvdata *drvdata, struct cc_hw_desc *desc,
+ unsigned int len)
+{
+ struct cc_req_mgr_handle *req_mgr_h = drvdata->request_mgr_handle;
+ unsigned int total_seq_len = len; /*initial sequence length*/
+ int rc = 0;
+
+ /* Wait for space in HW and SW FIFO. Poll for as much as FIFO_TIMEOUT.
+ */
+ rc = cc_queues_status(drvdata, req_mgr_h, total_seq_len);
+ if (rc)
+ return rc;
+
+ set_queue_last_ind(drvdata, &desc[(len - 1)]);
+
+ /*
+ * We are about to push command to the HW via the command registers
+ * that may refernece hsot memory. We need to issue a memory barrier
+ * to make sure there are no outstnading memory writes
+ */
+ wmb();
+ enqueue_seq(drvdata, desc, len);
+
+ /* Update the free slots in HW queue */
+ req_mgr_h->q_free_slots =
+ cc_ioread(drvdata, CC_REG(DSCRPTR_QUEUE_CONTENT));
+
+ return 0;
+}
+
+void complete_request(struct cc_drvdata *drvdata)
+{
+ struct cc_req_mgr_handle *request_mgr_handle =
+ drvdata->request_mgr_handle;
+
+ complete(&drvdata->hw_queue_avail);
+#ifdef COMP_IN_WQ
+ queue_delayed_work(request_mgr_handle->workq,
+ &request_mgr_handle->compwork, 0);
+#else
+ tasklet_schedule(&request_mgr_handle->comptask);
+#endif
+}
+
+#ifdef COMP_IN_WQ
+static void comp_work_handler(struct work_struct *work)
+{
+ struct cc_drvdata *drvdata =
+ container_of(work, struct cc_drvdata, compwork.work);
+
+ comp_handler((unsigned long)drvdata);
+}
+#endif
+
+static void proc_completions(struct cc_drvdata *drvdata)
+{
+ struct cc_crypto_req *cc_req;
+ struct device *dev = drvdata_to_dev(drvdata);
+ struct cc_req_mgr_handle *request_mgr_handle =
+ drvdata->request_mgr_handle;
+ unsigned int *tail = &request_mgr_handle->req_queue_tail;
+ unsigned int *head = &request_mgr_handle->req_queue_head;
+
+ while (request_mgr_handle->axi_completed) {
+ request_mgr_handle->axi_completed--;
+
+ /* Dequeue request */
+ if (*head == *tail) {
+ /* We are supposed to handle a completion but our
+ * queue is empty. This is not normal. Return and
+ * hope for the best.
+ */
+ dev_err(dev, "Request queue is empty head == tail %u\n",
+ *head);
+ break;
+ }
+
+ cc_req = &request_mgr_handle->req_queue[*tail];
+
+ if (cc_req->user_cb)
+ cc_req->user_cb(dev, cc_req->user_arg, 0);
+ *tail = (*tail + 1) & (MAX_REQUEST_QUEUE_SIZE - 1);
+ dev_dbg(dev, "Dequeue request tail=%u\n", *tail);
+ dev_dbg(dev, "Request completed. axi_completed=%d\n",
+ request_mgr_handle->axi_completed);
+ cc_pm_put_suspend(dev);
+ }
+}
+
+static inline u32 cc_axi_comp_count(struct cc_drvdata *drvdata)
+{
+ return FIELD_GET(AXIM_MON_COMP_VALUE,
+ cc_ioread(drvdata, drvdata->axim_mon_offset));
+}
+
+/* Deferred service handler, run as interrupt-fired tasklet */
+static void comp_handler(unsigned long devarg)
+{
+ struct cc_drvdata *drvdata = (struct cc_drvdata *)devarg;
+ struct cc_req_mgr_handle *request_mgr_handle =
+ drvdata->request_mgr_handle;
+
+ u32 irq;
+
+ irq = (drvdata->irq & CC_COMP_IRQ_MASK);
+
+ if (irq & CC_COMP_IRQ_MASK) {
+ /* To avoid the interrupt from firing as we unmask it,
+ * we clear it now
+ */
+ cc_iowrite(drvdata, CC_REG(HOST_ICR), CC_COMP_IRQ_MASK);
+
+ /* Avoid race with above clear: Test completion counter
+ * once more
+ */
+ request_mgr_handle->axi_completed +=
+ cc_axi_comp_count(drvdata);
+
+ while (request_mgr_handle->axi_completed) {
+ do {
+ proc_completions(drvdata);
+ /* At this point (after proc_completions()),
+ * request_mgr_handle->axi_completed is 0.
+ */
+ request_mgr_handle->axi_completed =
+ cc_axi_comp_count(drvdata);
+ } while (request_mgr_handle->axi_completed > 0);
+
+ cc_iowrite(drvdata, CC_REG(HOST_ICR),
+ CC_COMP_IRQ_MASK);
+
+ request_mgr_handle->axi_completed +=
+ cc_axi_comp_count(drvdata);
+ }
+ }
+ /* after verifing that there is nothing to do,
+ * unmask AXI completion interrupt
+ */
+ cc_iowrite(drvdata, CC_REG(HOST_IMR),
+ cc_ioread(drvdata, CC_REG(HOST_IMR)) & ~irq);
+
+ cc_proc_backlog(drvdata);
+}
+
+/*
+ * resume the queue configuration - no need to take the lock as this happens
+ * inside the spin lock protection
+ */
+#if defined(CONFIG_PM)
+int cc_resume_req_queue(struct cc_drvdata *drvdata)
+{
+ struct cc_req_mgr_handle *request_mgr_handle =
+ drvdata->request_mgr_handle;
+
+ spin_lock_bh(&request_mgr_handle->hw_lock);
+ request_mgr_handle->is_runtime_suspended = false;
+ spin_unlock_bh(&request_mgr_handle->hw_lock);
+
+ return 0;
+}
+
+/*
+ * suspend the queue configuration. Since it is used for the runtime suspend
+ * only verify that the queue can be suspended.
+ */
+int cc_suspend_req_queue(struct cc_drvdata *drvdata)
+{
+ struct cc_req_mgr_handle *request_mgr_handle =
+ drvdata->request_mgr_handle;
+
+ /* lock the send_request */
+ spin_lock_bh(&request_mgr_handle->hw_lock);
+ if (request_mgr_handle->req_queue_head !=
+ request_mgr_handle->req_queue_tail) {
+ spin_unlock_bh(&request_mgr_handle->hw_lock);
+ return -EBUSY;
+ }
+ request_mgr_handle->is_runtime_suspended = true;
+ spin_unlock_bh(&request_mgr_handle->hw_lock);
+
+ return 0;
+}
+
+bool cc_req_queue_suspended(struct cc_drvdata *drvdata)
+{
+ struct cc_req_mgr_handle *request_mgr_handle =
+ drvdata->request_mgr_handle;
+
+ return request_mgr_handle->is_runtime_suspended;
+}
+
+#endif
diff --git a/drivers/staging/ccree/cc_request_mgr.h b/drivers/crypto/ccree/cc_request_mgr.h
index 573cb97af085..573cb97af085 100644
--- a/drivers/staging/ccree/cc_request_mgr.h
+++ b/drivers/crypto/ccree/cc_request_mgr.h
diff --git a/drivers/crypto/ccree/cc_sram_mgr.c b/drivers/crypto/ccree/cc_sram_mgr.c
new file mode 100644
index 000000000000..c8c276f6dee9
--- /dev/null
+++ b/drivers/crypto/ccree/cc_sram_mgr.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
+
+#include "cc_driver.h"
+#include "cc_sram_mgr.h"
+
+/**
+ * struct cc_sram_ctx -Internal RAM context manager
+ * @sram_free_offset: the offset to the non-allocated area
+ */
+struct cc_sram_ctx {
+ cc_sram_addr_t sram_free_offset;
+};
+
+/**
+ * cc_sram_mgr_fini() - Cleanup SRAM pool.
+ *
+ * @drvdata: Associated device driver context
+ */
+void cc_sram_mgr_fini(struct cc_drvdata *drvdata)
+{
+ /* Free "this" context */
+ kfree(drvdata->sram_mgr_handle);
+}
+
+/**
+ * cc_sram_mgr_init() - Initializes SRAM pool.
+ * The pool starts right at the beginning of SRAM.
+ * Returns zero for success, negative value otherwise.
+ *
+ * @drvdata: Associated device driver context
+ */
+int cc_sram_mgr_init(struct cc_drvdata *drvdata)
+{
+ struct cc_sram_ctx *ctx;
+ dma_addr_t start = 0;
+ struct device *dev = drvdata_to_dev(drvdata);
+
+ if (drvdata->hw_rev < CC_HW_REV_712) {
+ /* Pool starts after ROM bytes */
+ start = (dma_addr_t)cc_ioread(drvdata,
+ CC_REG(HOST_SEP_SRAM_THRESHOLD));
+
+ if ((start & 0x3) != 0) {
+ dev_err(dev, "Invalid SRAM offset %pad\n", &start);
+ return -EINVAL;
+ }
+ }
+
+ /* Allocate "this" context */
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->sram_free_offset = start;
+ drvdata->sram_mgr_handle = ctx;
+
+ return 0;
+}
+
+/*!
+ * Allocated buffer from SRAM pool.
+ * Note: Caller is responsible to free the LAST allocated buffer.
+ * This function does not taking care of any fragmentation may occur
+ * by the order of calls to alloc/free.
+ *
+ * \param drvdata
+ * \param size The requested bytes to allocate
+ */
+cc_sram_addr_t cc_sram_alloc(struct cc_drvdata *drvdata, u32 size)
+{
+ struct cc_sram_ctx *smgr_ctx = drvdata->sram_mgr_handle;
+ struct device *dev = drvdata_to_dev(drvdata);
+ cc_sram_addr_t p;
+
+ if ((size & 0x3)) {
+ dev_err(dev, "Requested buffer size (%u) is not multiple of 4",
+ size);
+ return NULL_SRAM_ADDR;
+ }
+ if (size > (CC_CC_SRAM_SIZE - smgr_ctx->sram_free_offset)) {
+ dev_err(dev, "Not enough space to allocate %u B (at offset %llu)\n",
+ size, smgr_ctx->sram_free_offset);
+ return NULL_SRAM_ADDR;
+ }
+
+ p = smgr_ctx->sram_free_offset;
+ smgr_ctx->sram_free_offset += size;
+ dev_dbg(dev, "Allocated %u B @ %u\n", size, (unsigned int)p);
+ return p;
+}
+
+/**
+ * cc_set_sram_desc() - Create const descriptors sequence to
+ * set values in given array into SRAM.
+ * Note: each const value can't exceed word size.
+ *
+ * @src: A pointer to array of words to set as consts.
+ * @dst: The target SRAM buffer to set into
+ * @nelements: The number of words in "src" array
+ * @seq: A pointer to the given IN/OUT descriptor sequence
+ * @seq_len: A pointer to the given IN/OUT sequence length
+ */
+void cc_set_sram_desc(const u32 *src, cc_sram_addr_t dst,
+ unsigned int nelement, struct cc_hw_desc *seq,
+ unsigned int *seq_len)
+{
+ u32 i;
+ unsigned int idx = *seq_len;
+
+ for (i = 0; i < nelement; i++, idx++) {
+ hw_desc_init(&seq[idx]);
+ set_din_const(&seq[idx], src[i], sizeof(u32));
+ set_dout_sram(&seq[idx], dst + (i * sizeof(u32)), sizeof(u32));
+ set_flow_mode(&seq[idx], BYPASS);
+ }
+
+ *seq_len = idx;
+}
diff --git a/drivers/staging/ccree/cc_sram_mgr.h b/drivers/crypto/ccree/cc_sram_mgr.h
index d48649fb3323..d48649fb3323 100644
--- a/drivers/staging/ccree/cc_sram_mgr.h
+++ b/drivers/crypto/ccree/cc_sram_mgr.h
diff --git a/drivers/crypto/chelsio/Kconfig b/drivers/crypto/chelsio/Kconfig
index 5ae9f8706f17..930d82d991f2 100644
--- a/drivers/crypto/chelsio/Kconfig
+++ b/drivers/crypto/chelsio/Kconfig
@@ -29,3 +29,14 @@ config CHELSIO_IPSEC_INLINE
default n
---help---
Enable support for IPSec Tx Inline.
+
+config CRYPTO_DEV_CHELSIO_TLS
+ tristate "Chelsio Crypto Inline TLS Driver"
+ depends on CHELSIO_T4
+ depends on TLS
+ select CRYPTO_DEV_CHELSIO
+ ---help---
+ Support Chelsio Inline TLS with Chelsio crypto accelerator.
+
+ To compile this driver as a module, choose M here: the module
+ will be called chtls.
diff --git a/drivers/crypto/chelsio/Makefile b/drivers/crypto/chelsio/Makefile
index eaecaf1ebcf3..639e5718dff4 100644
--- a/drivers/crypto/chelsio/Makefile
+++ b/drivers/crypto/chelsio/Makefile
@@ -3,3 +3,4 @@ ccflags-y := -Idrivers/net/ethernet/chelsio/cxgb4
obj-$(CONFIG_CRYPTO_DEV_CHELSIO) += chcr.o
chcr-objs := chcr_core.o chcr_algo.o
chcr-$(CONFIG_CHELSIO_IPSEC_INLINE) += chcr_ipsec.o
+obj-$(CONFIG_CRYPTO_DEV_CHELSIO_TLS) += chtls/
diff --git a/drivers/crypto/chelsio/chcr_algo.c b/drivers/crypto/chelsio/chcr_algo.c
index 34a02d690548..59fe6631e73e 100644
--- a/drivers/crypto/chelsio/chcr_algo.c
+++ b/drivers/crypto/chelsio/chcr_algo.c
@@ -131,6 +131,11 @@ static inline int is_ofld_imm(const struct sk_buff *skb)
return (skb->len <= SGE_MAX_WR_LEN);
}
+static inline void chcr_init_hctx_per_wr(struct chcr_ahash_req_ctx *reqctx)
+{
+ memset(&reqctx->hctx_wr, 0, sizeof(struct chcr_hctx_per_wr));
+}
+
static int sg_nents_xlen(struct scatterlist *sg, unsigned int reqlen,
unsigned int entlen,
unsigned int skip)
@@ -160,41 +165,6 @@ static int sg_nents_xlen(struct scatterlist *sg, unsigned int reqlen,
return nents;
}
-static inline void chcr_handle_ahash_resp(struct ahash_request *req,
- unsigned char *input,
- int err)
-{
- struct chcr_ahash_req_ctx *reqctx = ahash_request_ctx(req);
- int digestsize, updated_digestsize;
- struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
- struct uld_ctx *u_ctx = ULD_CTX(h_ctx(tfm));
-
- if (input == NULL)
- goto out;
- digestsize = crypto_ahash_digestsize(crypto_ahash_reqtfm(req));
- if (reqctx->is_sg_map)
- chcr_hash_dma_unmap(&u_ctx->lldi.pdev->dev, req);
- if (reqctx->dma_addr)
- dma_unmap_single(&u_ctx->lldi.pdev->dev, reqctx->dma_addr,
- reqctx->dma_len, DMA_TO_DEVICE);
- reqctx->dma_addr = 0;
- updated_digestsize = digestsize;
- if (digestsize == SHA224_DIGEST_SIZE)
- updated_digestsize = SHA256_DIGEST_SIZE;
- else if (digestsize == SHA384_DIGEST_SIZE)
- updated_digestsize = SHA512_DIGEST_SIZE;
- if (reqctx->result == 1) {
- reqctx->result = 0;
- memcpy(req->result, input + sizeof(struct cpl_fw6_pld),
- digestsize);
- } else {
- memcpy(reqctx->partial_hash, input + sizeof(struct cpl_fw6_pld),
- updated_digestsize);
- }
-out:
- req->base.complete(&req->base, err);
-}
-
static inline int get_aead_subtype(struct crypto_aead *aead)
{
struct aead_alg *alg = crypto_aead_alg(aead);
@@ -247,34 +217,6 @@ static inline void chcr_handle_aead_resp(struct aead_request *req,
req->base.complete(&req->base, err);
}
-/*
- * chcr_handle_resp - Unmap the DMA buffers associated with the request
- * @req: crypto request
- */
-int chcr_handle_resp(struct crypto_async_request *req, unsigned char *input,
- int err)
-{
- struct crypto_tfm *tfm = req->tfm;
- struct chcr_context *ctx = crypto_tfm_ctx(tfm);
- struct adapter *adap = padap(ctx->dev);
-
- switch (tfm->__crt_alg->cra_flags & CRYPTO_ALG_TYPE_MASK) {
- case CRYPTO_ALG_TYPE_AEAD:
- chcr_handle_aead_resp(aead_request_cast(req), input, err);
- break;
-
- case CRYPTO_ALG_TYPE_ABLKCIPHER:
- err = chcr_handle_cipher_resp(ablkcipher_request_cast(req),
- input, err);
- break;
-
- case CRYPTO_ALG_TYPE_AHASH:
- chcr_handle_ahash_resp(ahash_request_cast(req), input, err);
- }
- atomic_inc(&adap->chcr_stats.complete);
- return err;
-}
-
static void get_aes_decrypt_key(unsigned char *dec_key,
const unsigned char *key,
unsigned int keylength)
@@ -563,7 +505,6 @@ static void ulptx_walk_add_sg(struct ulptx_walk *walk,
if (!len)
return;
-
while (sg && skip) {
if (sg_dma_len(sg) <= skip) {
skip -= sg_dma_len(sg);
@@ -653,6 +594,35 @@ static int generate_copy_rrkey(struct ablk_ctx *ablkctx,
}
return 0;
}
+
+static int chcr_hash_ent_in_wr(struct scatterlist *src,
+ unsigned int minsg,
+ unsigned int space,
+ unsigned int srcskip)
+{
+ int srclen = 0;
+ int srcsg = minsg;
+ int soffset = 0, sless;
+
+ if (sg_dma_len(src) == srcskip) {
+ src = sg_next(src);
+ srcskip = 0;
+ }
+ while (src && space > (sgl_ent_len[srcsg + 1])) {
+ sless = min_t(unsigned int, sg_dma_len(src) - soffset - srcskip,
+ CHCR_SRC_SG_SIZE);
+ srclen += sless;
+ soffset += sless;
+ srcsg++;
+ if (sg_dma_len(src) == (soffset + srcskip)) {
+ src = sg_next(src);
+ soffset = 0;
+ srcskip = 0;
+ }
+ }
+ return srclen;
+}
+
static int chcr_sg_ent_in_wr(struct scatterlist *src,
struct scatterlist *dst,
unsigned int minsg,
@@ -662,7 +632,7 @@ static int chcr_sg_ent_in_wr(struct scatterlist *src,
{
int srclen = 0, dstlen = 0;
int srcsg = minsg, dstsg = minsg;
- int offset = 0, less;
+ int offset = 0, soffset = 0, less, sless = 0;
if (sg_dma_len(src) == srcskip) {
src = sg_next(src);
@@ -676,7 +646,9 @@ static int chcr_sg_ent_in_wr(struct scatterlist *src,
while (src && dst &&
space > (sgl_ent_len[srcsg + 1] + dsgl_ent_len[dstsg])) {
- srclen += (sg_dma_len(src) - srcskip);
+ sless = min_t(unsigned int, sg_dma_len(src) - srcskip - soffset,
+ CHCR_SRC_SG_SIZE);
+ srclen += sless;
srcsg++;
offset = 0;
while (dst && ((dstsg + 1) <= MAX_DSGL_ENT) &&
@@ -687,15 +659,20 @@ static int chcr_sg_ent_in_wr(struct scatterlist *src,
dstskip, CHCR_DST_SG_SIZE);
dstlen += less;
offset += less;
- if (offset == sg_dma_len(dst)) {
+ if ((offset + dstskip) == sg_dma_len(dst)) {
dst = sg_next(dst);
offset = 0;
}
dstsg++;
dstskip = 0;
}
- src = sg_next(src);
- srcskip = 0;
+ soffset += sless;
+ if ((soffset + srcskip) == sg_dma_len(src)) {
+ src = sg_next(src);
+ srcskip = 0;
+ soffset = 0;
+ }
+
}
return min(srclen, dstlen);
}
@@ -784,14 +761,14 @@ static struct sk_buff *create_cipher_wr(struct cipher_wr_param *wrparam)
nents = sg_nents_xlen(reqctx->dstsg, wrparam->bytes, CHCR_DST_SG_SIZE,
reqctx->dst_ofst);
dst_size = get_space_for_phys_dsgl(nents + 1);
- kctx_len = (DIV_ROUND_UP(ablkctx->enckey_len, 16) * 16);
+ kctx_len = roundup(ablkctx->enckey_len, 16);
transhdr_len = CIPHER_TRANSHDR_SIZE(kctx_len, dst_size);
nents = sg_nents_xlen(reqctx->srcsg, wrparam->bytes,
CHCR_SRC_SG_SIZE, reqctx->src_ofst);
- temp = reqctx->imm ? (DIV_ROUND_UP((IV + wrparam->req->nbytes), 16)
- * 16) : (sgl_len(nents + MIN_CIPHER_SG) * 8);
+ temp = reqctx->imm ? roundup(IV + wrparam->req->nbytes, 16) :
+ (sgl_len(nents + MIN_CIPHER_SG) * 8);
transhdr_len += temp;
- transhdr_len = DIV_ROUND_UP(transhdr_len, 16) * 16;
+ transhdr_len = roundup(transhdr_len, 16);
skb = alloc_skb(SGE_MAX_WR_LEN, flags);
if (!skb) {
error = -ENOMEM;
@@ -847,6 +824,13 @@ static struct sk_buff *create_cipher_wr(struct cipher_wr_param *wrparam)
transhdr_len, temp,
ablkctx->ciph_mode == CHCR_SCMD_CIPHER_MODE_AES_CBC);
reqctx->skb = skb;
+
+ if (reqctx->op && (ablkctx->ciph_mode ==
+ CHCR_SCMD_CIPHER_MODE_AES_CBC))
+ sg_pcopy_to_buffer(wrparam->req->src,
+ sg_nents(wrparam->req->src), wrparam->req->info, 16,
+ reqctx->processed + wrparam->bytes - AES_BLOCK_SIZE);
+
return skb;
err:
return ERR_PTR(error);
@@ -1070,9 +1054,8 @@ static int chcr_update_cipher_iv(struct ablkcipher_request *req,
ret = chcr_update_tweak(req, iv, 0);
else if (subtype == CRYPTO_ALG_SUB_TYPE_CBC) {
if (reqctx->op)
- sg_pcopy_to_buffer(req->src, sg_nents(req->src), iv,
- 16,
- reqctx->processed - AES_BLOCK_SIZE);
+ /*Updated before sending last WR*/
+ memcpy(iv, req->info, AES_BLOCK_SIZE);
else
memcpy(iv, &fw6_pld->data[2], AES_BLOCK_SIZE);
}
@@ -1100,11 +1083,8 @@ static int chcr_final_cipher_iv(struct ablkcipher_request *req,
else if (subtype == CRYPTO_ALG_SUB_TYPE_XTS)
ret = chcr_update_tweak(req, iv, 1);
else if (subtype == CRYPTO_ALG_SUB_TYPE_CBC) {
- if (reqctx->op)
- sg_pcopy_to_buffer(req->src, sg_nents(req->src), iv,
- 16,
- reqctx->processed - AES_BLOCK_SIZE);
- else
+ /*Already updated for Decrypt*/
+ if (!reqctx->op)
memcpy(iv, &fw6_pld->data[2], AES_BLOCK_SIZE);
}
@@ -1143,12 +1123,12 @@ static int chcr_handle_cipher_resp(struct ablkcipher_request *req,
}
if (!reqctx->imm) {
bytes = chcr_sg_ent_in_wr(reqctx->srcsg, reqctx->dstsg, 1,
- SPACE_LEFT(ablkctx->enckey_len),
+ CIP_SPACE_LEFT(ablkctx->enckey_len),
reqctx->src_ofst, reqctx->dst_ofst);
if ((bytes + reqctx->processed) >= req->nbytes)
bytes = req->nbytes - reqctx->processed;
else
- bytes = ROUND_16(bytes);
+ bytes = rounddown(bytes, 16);
} else {
/*CTR mode counter overfloa*/
bytes = req->nbytes - reqctx->processed;
@@ -1234,7 +1214,7 @@ static int process_cipher(struct ablkcipher_request *req,
CHCR_DST_SG_SIZE, 0);
dnents += 1; // IV
phys_dsgl = get_space_for_phys_dsgl(dnents);
- kctx_len = (DIV_ROUND_UP(ablkctx->enckey_len, 16) * 16);
+ kctx_len = roundup(ablkctx->enckey_len, 16);
transhdr_len = CIPHER_TRANSHDR_SIZE(kctx_len, phys_dsgl);
reqctx->imm = (transhdr_len + IV + req->nbytes) <=
SGE_MAX_WR_LEN;
@@ -1247,12 +1227,12 @@ static int process_cipher(struct ablkcipher_request *req,
if (!reqctx->imm) {
bytes = chcr_sg_ent_in_wr(req->src, req->dst,
MIN_CIPHER_SG,
- SPACE_LEFT(ablkctx->enckey_len),
+ CIP_SPACE_LEFT(ablkctx->enckey_len),
0, 0);
if ((bytes + reqctx->processed) >= req->nbytes)
bytes = req->nbytes - reqctx->processed;
else
- bytes = ROUND_16(bytes);
+ bytes = rounddown(bytes, 16);
} else {
bytes = req->nbytes;
}
@@ -1282,7 +1262,7 @@ static int process_cipher(struct ablkcipher_request *req,
req->src,
req->dst,
req->nbytes,
- req->info,
+ reqctx->iv,
op_type);
goto error;
}
@@ -1503,35 +1483,24 @@ static struct sk_buff *create_hash_wr(struct ahash_request *req,
struct uld_ctx *u_ctx = ULD_CTX(h_ctx(tfm));
struct chcr_wr *chcr_req;
struct ulptx_sgl *ulptx;
- unsigned int nents = 0, transhdr_len, iopad_alignment = 0;
- unsigned int digestsize = crypto_ahash_digestsize(tfm);
- unsigned int kctx_len = 0, temp = 0;
- u8 hash_size_in_response = 0;
+ unsigned int nents = 0, transhdr_len;
+ unsigned int temp = 0;
gfp_t flags = req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP ? GFP_KERNEL :
GFP_ATOMIC;
struct adapter *adap = padap(h_ctx(tfm)->dev);
int error = 0;
- iopad_alignment = KEYCTX_ALIGN_PAD(digestsize);
- kctx_len = param->alg_prm.result_size + iopad_alignment;
- if (param->opad_needed)
- kctx_len += param->alg_prm.result_size + iopad_alignment;
-
- if (req_ctx->result)
- hash_size_in_response = digestsize;
- else
- hash_size_in_response = param->alg_prm.result_size;
- transhdr_len = HASH_TRANSHDR_SIZE(kctx_len);
- req_ctx->imm = (transhdr_len + param->bfr_len + param->sg_len) <=
- SGE_MAX_WR_LEN;
- nents = sg_nents_xlen(req->src, param->sg_len, CHCR_SRC_SG_SIZE, 0);
+ transhdr_len = HASH_TRANSHDR_SIZE(param->kctx_len);
+ req_ctx->hctx_wr.imm = (transhdr_len + param->bfr_len +
+ param->sg_len) <= SGE_MAX_WR_LEN;
+ nents = sg_nents_xlen(req_ctx->hctx_wr.srcsg, param->sg_len,
+ CHCR_SRC_SG_SIZE, req_ctx->hctx_wr.src_ofst);
nents += param->bfr_len ? 1 : 0;
- transhdr_len += req_ctx->imm ? (DIV_ROUND_UP((param->bfr_len +
- param->sg_len), 16) * 16) :
- (sgl_len(nents) * 8);
- transhdr_len = DIV_ROUND_UP(transhdr_len, 16) * 16;
+ transhdr_len += req_ctx->hctx_wr.imm ? roundup(param->bfr_len +
+ param->sg_len, 16) : (sgl_len(nents) * 8);
+ transhdr_len = roundup(transhdr_len, 16);
- skb = alloc_skb(SGE_MAX_WR_LEN, flags);
+ skb = alloc_skb(transhdr_len, flags);
if (!skb)
return ERR_PTR(-ENOMEM);
chcr_req = __skb_put_zero(skb, transhdr_len);
@@ -1563,33 +1532,33 @@ static struct sk_buff *create_hash_wr(struct ahash_request *req,
chcr_req->key_ctx.ctx_hdr = FILL_KEY_CTX_HDR(CHCR_KEYCTX_NO_KEY,
param->alg_prm.mk_size, 0,
param->opad_needed,
- ((kctx_len +
+ ((param->kctx_len +
sizeof(chcr_req->key_ctx)) >> 4));
chcr_req->sec_cpl.scmd1 = cpu_to_be64((u64)param->scmd1);
- ulptx = (struct ulptx_sgl *)((u8 *)(chcr_req + 1) + kctx_len +
+ ulptx = (struct ulptx_sgl *)((u8 *)(chcr_req + 1) + param->kctx_len +
DUMMY_BYTES);
if (param->bfr_len != 0) {
- req_ctx->dma_addr = dma_map_single(&u_ctx->lldi.pdev->dev,
- req_ctx->reqbfr, param->bfr_len,
- DMA_TO_DEVICE);
+ req_ctx->hctx_wr.dma_addr =
+ dma_map_single(&u_ctx->lldi.pdev->dev, req_ctx->reqbfr,
+ param->bfr_len, DMA_TO_DEVICE);
if (dma_mapping_error(&u_ctx->lldi.pdev->dev,
- req_ctx->dma_addr)) {
+ req_ctx->hctx_wr. dma_addr)) {
error = -ENOMEM;
goto err;
}
- req_ctx->dma_len = param->bfr_len;
+ req_ctx->hctx_wr.dma_len = param->bfr_len;
} else {
- req_ctx->dma_addr = 0;
+ req_ctx->hctx_wr.dma_addr = 0;
}
chcr_add_hash_src_ent(req, ulptx, param);
/* Request upto max wr size */
- temp = kctx_len + DUMMY_BYTES + (req_ctx->imm ? (param->sg_len
- + param->bfr_len) : 0);
+ temp = param->kctx_len + DUMMY_BYTES + (req_ctx->hctx_wr.imm ?
+ (param->sg_len + param->bfr_len) : 0);
atomic_inc(&adap->chcr_stats.digest_rqst);
- create_wreq(h_ctx(tfm), chcr_req, &req->base, req_ctx->imm,
- hash_size_in_response, transhdr_len,
+ create_wreq(h_ctx(tfm), chcr_req, &req->base, req_ctx->hctx_wr.imm,
+ param->hash_size, transhdr_len,
temp, 0);
- req_ctx->skb = skb;
+ req_ctx->hctx_wr.skb = skb;
return skb;
err:
kfree_skb(skb);
@@ -1608,7 +1577,6 @@ static int chcr_ahash_update(struct ahash_request *req)
int error;
bs = crypto_tfm_alg_blocksize(crypto_ahash_tfm(rtfm));
-
u_ctx = ULD_CTX(h_ctx(rtfm));
if (unlikely(cxgb4_is_crypto_q_full(u_ctx->lldi.ports[0],
h_ctx(rtfm)->tx_qidx))) {
@@ -1625,17 +1593,26 @@ static int chcr_ahash_update(struct ahash_request *req)
req_ctx->reqlen += nbytes;
return 0;
}
+ chcr_init_hctx_per_wr(req_ctx);
error = chcr_hash_dma_map(&u_ctx->lldi.pdev->dev, req);
if (error)
return -ENOMEM;
+ get_alg_config(&params.alg_prm, crypto_ahash_digestsize(rtfm));
+ params.kctx_len = roundup(params.alg_prm.result_size, 16);
+ params.sg_len = chcr_hash_ent_in_wr(req->src, !!req_ctx->reqlen,
+ HASH_SPACE_LEFT(params.kctx_len), 0);
+ if (params.sg_len > req->nbytes)
+ params.sg_len = req->nbytes;
+ params.sg_len = rounddown(params.sg_len + req_ctx->reqlen, bs) -
+ req_ctx->reqlen;
params.opad_needed = 0;
params.more = 1;
params.last = 0;
- params.sg_len = nbytes - req_ctx->reqlen;
params.bfr_len = req_ctx->reqlen;
params.scmd1 = 0;
- get_alg_config(&params.alg_prm, crypto_ahash_digestsize(rtfm));
- req_ctx->result = 0;
+ req_ctx->hctx_wr.srcsg = req->src;
+
+ params.hash_size = params.alg_prm.result_size;
req_ctx->data_len += params.sg_len + params.bfr_len;
skb = create_hash_wr(req, &params);
if (IS_ERR(skb)) {
@@ -1643,6 +1620,7 @@ static int chcr_ahash_update(struct ahash_request *req)
goto unmap;
}
+ req_ctx->hctx_wr.processed += params.sg_len;
if (remainder) {
/* Swap buffers */
swap(req_ctx->reqbfr, req_ctx->skbfr);
@@ -1680,16 +1658,27 @@ static int chcr_ahash_final(struct ahash_request *req)
struct uld_ctx *u_ctx = NULL;
u8 bs = crypto_tfm_alg_blocksize(crypto_ahash_tfm(rtfm));
+ chcr_init_hctx_per_wr(req_ctx);
u_ctx = ULD_CTX(h_ctx(rtfm));
if (is_hmac(crypto_ahash_tfm(rtfm)))
params.opad_needed = 1;
else
params.opad_needed = 0;
params.sg_len = 0;
+ req_ctx->hctx_wr.isfinal = 1;
get_alg_config(&params.alg_prm, crypto_ahash_digestsize(rtfm));
- req_ctx->result = 1;
+ params.kctx_len = roundup(params.alg_prm.result_size, 16);
+ if (is_hmac(crypto_ahash_tfm(rtfm))) {
+ params.opad_needed = 1;
+ params.kctx_len *= 2;
+ } else {
+ params.opad_needed = 0;
+ }
+
+ req_ctx->hctx_wr.result = 1;
params.bfr_len = req_ctx->reqlen;
req_ctx->data_len += params.bfr_len + params.sg_len;
+ req_ctx->hctx_wr.srcsg = req->src;
if (req_ctx->reqlen == 0) {
create_last_hash_block(req_ctx->reqbfr, bs, req_ctx->data_len);
params.last = 0;
@@ -1702,10 +1691,11 @@ static int chcr_ahash_final(struct ahash_request *req)
params.last = 1;
params.more = 0;
}
+ params.hash_size = crypto_ahash_digestsize(rtfm);
skb = create_hash_wr(req, &params);
if (IS_ERR(skb))
return PTR_ERR(skb);
-
+ req_ctx->reqlen = 0;
skb->dev = u_ctx->lldi.ports[0];
set_wr_txq(skb, CPL_PRIORITY_DATA, h_ctx(rtfm)->tx_qidx);
chcr_send_wr(skb);
@@ -1730,37 +1720,59 @@ static int chcr_ahash_finup(struct ahash_request *req)
if (!(req->base.flags & CRYPTO_TFM_REQ_MAY_BACKLOG))
return -EBUSY;
}
+ chcr_init_hctx_per_wr(req_ctx);
+ error = chcr_hash_dma_map(&u_ctx->lldi.pdev->dev, req);
+ if (error)
+ return -ENOMEM;
- if (is_hmac(crypto_ahash_tfm(rtfm)))
+ get_alg_config(&params.alg_prm, crypto_ahash_digestsize(rtfm));
+ params.kctx_len = roundup(params.alg_prm.result_size, 16);
+ if (is_hmac(crypto_ahash_tfm(rtfm))) {
+ params.kctx_len *= 2;
params.opad_needed = 1;
- else
+ } else {
params.opad_needed = 0;
+ }
- params.sg_len = req->nbytes;
+ params.sg_len = chcr_hash_ent_in_wr(req->src, !!req_ctx->reqlen,
+ HASH_SPACE_LEFT(params.kctx_len), 0);
+ if (params.sg_len < req->nbytes) {
+ if (is_hmac(crypto_ahash_tfm(rtfm))) {
+ params.kctx_len /= 2;
+ params.opad_needed = 0;
+ }
+ params.last = 0;
+ params.more = 1;
+ params.sg_len = rounddown(params.sg_len + req_ctx->reqlen, bs)
+ - req_ctx->reqlen;
+ params.hash_size = params.alg_prm.result_size;
+ params.scmd1 = 0;
+ } else {
+ params.last = 1;
+ params.more = 0;
+ params.sg_len = req->nbytes;
+ params.hash_size = crypto_ahash_digestsize(rtfm);
+ params.scmd1 = req_ctx->data_len + req_ctx->reqlen +
+ params.sg_len;
+ }
params.bfr_len = req_ctx->reqlen;
- get_alg_config(&params.alg_prm, crypto_ahash_digestsize(rtfm));
req_ctx->data_len += params.bfr_len + params.sg_len;
- req_ctx->result = 1;
+ req_ctx->hctx_wr.result = 1;
+ req_ctx->hctx_wr.srcsg = req->src;
if ((req_ctx->reqlen + req->nbytes) == 0) {
create_last_hash_block(req_ctx->reqbfr, bs, req_ctx->data_len);
params.last = 0;
params.more = 1;
params.scmd1 = 0;
params.bfr_len = bs;
- } else {
- params.scmd1 = req_ctx->data_len;
- params.last = 1;
- params.more = 0;
}
- error = chcr_hash_dma_map(&u_ctx->lldi.pdev->dev, req);
- if (error)
- return -ENOMEM;
-
skb = create_hash_wr(req, &params);
if (IS_ERR(skb)) {
error = PTR_ERR(skb);
goto unmap;
}
+ req_ctx->reqlen = 0;
+ req_ctx->hctx_wr.processed += params.sg_len;
skb->dev = u_ctx->lldi.ports[0];
set_wr_txq(skb, CPL_PRIORITY_DATA, h_ctx(rtfm)->tx_qidx);
chcr_send_wr(skb);
@@ -1791,21 +1803,42 @@ static int chcr_ahash_digest(struct ahash_request *req)
return -EBUSY;
}
- if (is_hmac(crypto_ahash_tfm(rtfm)))
- params.opad_needed = 1;
- else
- params.opad_needed = 0;
+ chcr_init_hctx_per_wr(req_ctx);
error = chcr_hash_dma_map(&u_ctx->lldi.pdev->dev, req);
if (error)
return -ENOMEM;
- params.last = 0;
- params.more = 0;
- params.sg_len = req->nbytes;
- params.bfr_len = 0;
- params.scmd1 = 0;
get_alg_config(&params.alg_prm, crypto_ahash_digestsize(rtfm));
- req_ctx->result = 1;
+ params.kctx_len = roundup(params.alg_prm.result_size, 16);
+ if (is_hmac(crypto_ahash_tfm(rtfm))) {
+ params.kctx_len *= 2;
+ params.opad_needed = 1;
+ } else {
+ params.opad_needed = 0;
+ }
+ params.sg_len = chcr_hash_ent_in_wr(req->src, !!req_ctx->reqlen,
+ HASH_SPACE_LEFT(params.kctx_len), 0);
+ if (params.sg_len < req->nbytes) {
+ if (is_hmac(crypto_ahash_tfm(rtfm))) {
+ params.kctx_len /= 2;
+ params.opad_needed = 0;
+ }
+ params.last = 0;
+ params.more = 1;
+ params.scmd1 = 0;
+ params.sg_len = rounddown(params.sg_len, bs);
+ params.hash_size = params.alg_prm.result_size;
+ } else {
+ params.sg_len = req->nbytes;
+ params.hash_size = crypto_ahash_digestsize(rtfm);
+ params.last = 1;
+ params.more = 0;
+ params.scmd1 = req->nbytes + req_ctx->data_len;
+
+ }
+ params.bfr_len = 0;
+ req_ctx->hctx_wr.result = 1;
+ req_ctx->hctx_wr.srcsg = req->src;
req_ctx->data_len += params.bfr_len + params.sg_len;
if (req->nbytes == 0) {
@@ -1819,6 +1852,7 @@ static int chcr_ahash_digest(struct ahash_request *req)
error = PTR_ERR(skb);
goto unmap;
}
+ req_ctx->hctx_wr.processed += params.sg_len;
skb->dev = u_ctx->lldi.ports[0];
set_wr_txq(skb, CPL_PRIORITY_DATA, h_ctx(rtfm)->tx_qidx);
chcr_send_wr(skb);
@@ -1828,6 +1862,151 @@ unmap:
return error;
}
+static int chcr_ahash_continue(struct ahash_request *req)
+{
+ struct chcr_ahash_req_ctx *reqctx = ahash_request_ctx(req);
+ struct chcr_hctx_per_wr *hctx_wr = &reqctx->hctx_wr;
+ struct crypto_ahash *rtfm = crypto_ahash_reqtfm(req);
+ struct uld_ctx *u_ctx = NULL;
+ struct sk_buff *skb;
+ struct hash_wr_param params;
+ u8 bs;
+ int error;
+
+ bs = crypto_tfm_alg_blocksize(crypto_ahash_tfm(rtfm));
+ u_ctx = ULD_CTX(h_ctx(rtfm));
+ if (unlikely(cxgb4_is_crypto_q_full(u_ctx->lldi.ports[0],
+ h_ctx(rtfm)->tx_qidx))) {
+ if (!(req->base.flags & CRYPTO_TFM_REQ_MAY_BACKLOG))
+ return -EBUSY;
+ }
+ get_alg_config(&params.alg_prm, crypto_ahash_digestsize(rtfm));
+ params.kctx_len = roundup(params.alg_prm.result_size, 16);
+ if (is_hmac(crypto_ahash_tfm(rtfm))) {
+ params.kctx_len *= 2;
+ params.opad_needed = 1;
+ } else {
+ params.opad_needed = 0;
+ }
+ params.sg_len = chcr_hash_ent_in_wr(hctx_wr->srcsg, 0,
+ HASH_SPACE_LEFT(params.kctx_len),
+ hctx_wr->src_ofst);
+ if ((params.sg_len + hctx_wr->processed) > req->nbytes)
+ params.sg_len = req->nbytes - hctx_wr->processed;
+ if (!hctx_wr->result ||
+ ((params.sg_len + hctx_wr->processed) < req->nbytes)) {
+ if (is_hmac(crypto_ahash_tfm(rtfm))) {
+ params.kctx_len /= 2;
+ params.opad_needed = 0;
+ }
+ params.last = 0;
+ params.more = 1;
+ params.sg_len = rounddown(params.sg_len, bs);
+ params.hash_size = params.alg_prm.result_size;
+ params.scmd1 = 0;
+ } else {
+ params.last = 1;
+ params.more = 0;
+ params.hash_size = crypto_ahash_digestsize(rtfm);
+ params.scmd1 = reqctx->data_len + params.sg_len;
+ }
+ params.bfr_len = 0;
+ reqctx->data_len += params.sg_len;
+ skb = create_hash_wr(req, &params);
+ if (IS_ERR(skb)) {
+ error = PTR_ERR(skb);
+ goto err;
+ }
+ hctx_wr->processed += params.sg_len;
+ skb->dev = u_ctx->lldi.ports[0];
+ set_wr_txq(skb, CPL_PRIORITY_DATA, h_ctx(rtfm)->tx_qidx);
+ chcr_send_wr(skb);
+ return 0;
+err:
+ return error;
+}
+
+static inline void chcr_handle_ahash_resp(struct ahash_request *req,
+ unsigned char *input,
+ int err)
+{
+ struct chcr_ahash_req_ctx *reqctx = ahash_request_ctx(req);
+ struct chcr_hctx_per_wr *hctx_wr = &reqctx->hctx_wr;
+ int digestsize, updated_digestsize;
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct uld_ctx *u_ctx = ULD_CTX(h_ctx(tfm));
+
+ if (input == NULL)
+ goto out;
+ digestsize = crypto_ahash_digestsize(crypto_ahash_reqtfm(req));
+ updated_digestsize = digestsize;
+ if (digestsize == SHA224_DIGEST_SIZE)
+ updated_digestsize = SHA256_DIGEST_SIZE;
+ else if (digestsize == SHA384_DIGEST_SIZE)
+ updated_digestsize = SHA512_DIGEST_SIZE;
+
+ if (hctx_wr->dma_addr) {
+ dma_unmap_single(&u_ctx->lldi.pdev->dev, hctx_wr->dma_addr,
+ hctx_wr->dma_len, DMA_TO_DEVICE);
+ hctx_wr->dma_addr = 0;
+ }
+ if (hctx_wr->isfinal || ((hctx_wr->processed + reqctx->reqlen) ==
+ req->nbytes)) {
+ if (hctx_wr->result == 1) {
+ hctx_wr->result = 0;
+ memcpy(req->result, input + sizeof(struct cpl_fw6_pld),
+ digestsize);
+ } else {
+ memcpy(reqctx->partial_hash,
+ input + sizeof(struct cpl_fw6_pld),
+ updated_digestsize);
+
+ }
+ goto unmap;
+ }
+ memcpy(reqctx->partial_hash, input + sizeof(struct cpl_fw6_pld),
+ updated_digestsize);
+
+ err = chcr_ahash_continue(req);
+ if (err)
+ goto unmap;
+ return;
+unmap:
+ if (hctx_wr->is_sg_map)
+ chcr_hash_dma_unmap(&u_ctx->lldi.pdev->dev, req);
+
+
+out:
+ req->base.complete(&req->base, err);
+}
+
+/*
+ * chcr_handle_resp - Unmap the DMA buffers associated with the request
+ * @req: crypto request
+ */
+int chcr_handle_resp(struct crypto_async_request *req, unsigned char *input,
+ int err)
+{
+ struct crypto_tfm *tfm = req->tfm;
+ struct chcr_context *ctx = crypto_tfm_ctx(tfm);
+ struct adapter *adap = padap(ctx->dev);
+
+ switch (tfm->__crt_alg->cra_flags & CRYPTO_ALG_TYPE_MASK) {
+ case CRYPTO_ALG_TYPE_AEAD:
+ chcr_handle_aead_resp(aead_request_cast(req), input, err);
+ break;
+
+ case CRYPTO_ALG_TYPE_ABLKCIPHER:
+ err = chcr_handle_cipher_resp(ablkcipher_request_cast(req),
+ input, err);
+ break;
+
+ case CRYPTO_ALG_TYPE_AHASH:
+ chcr_handle_ahash_resp(ahash_request_cast(req), input, err);
+ }
+ atomic_inc(&adap->chcr_stats.complete);
+ return err;
+}
static int chcr_ahash_export(struct ahash_request *areq, void *out)
{
struct chcr_ahash_req_ctx *req_ctx = ahash_request_ctx(areq);
@@ -1835,11 +2014,10 @@ static int chcr_ahash_export(struct ahash_request *areq, void *out)
state->reqlen = req_ctx->reqlen;
state->data_len = req_ctx->data_len;
- state->is_sg_map = 0;
- state->result = 0;
memcpy(state->bfr1, req_ctx->reqbfr, req_ctx->reqlen);
memcpy(state->partial_hash, req_ctx->partial_hash,
CHCR_HASH_MAX_DIGEST_SIZE);
+ chcr_init_hctx_per_wr(state);
return 0;
}
@@ -1852,11 +2030,10 @@ static int chcr_ahash_import(struct ahash_request *areq, const void *in)
req_ctx->data_len = state->data_len;
req_ctx->reqbfr = req_ctx->bfr1;
req_ctx->skbfr = req_ctx->bfr2;
- req_ctx->is_sg_map = 0;
- req_ctx->result = 0;
memcpy(req_ctx->bfr1, state->bfr1, CHCR_HASH_MAX_BLOCK_SIZE_128);
memcpy(req_ctx->partial_hash, state->partial_hash,
CHCR_HASH_MAX_DIGEST_SIZE);
+ chcr_init_hctx_per_wr(req_ctx);
return 0;
}
@@ -1953,10 +2130,8 @@ static int chcr_sha_init(struct ahash_request *areq)
req_ctx->reqlen = 0;
req_ctx->reqbfr = req_ctx->bfr1;
req_ctx->skbfr = req_ctx->bfr2;
- req_ctx->skb = NULL;
- req_ctx->result = 0;
- req_ctx->is_sg_map = 0;
copy_hash_init_values(req_ctx->partial_hash, digestsize);
+
return 0;
}
@@ -2124,11 +2299,11 @@ static struct sk_buff *create_authenc_wr(struct aead_request *req,
transhdr_len = CIPHER_TRANSHDR_SIZE(kctx_len, dst_size);
reqctx->imm = (transhdr_len + assoclen + IV + req->cryptlen) <
SGE_MAX_WR_LEN;
- temp = reqctx->imm ? (DIV_ROUND_UP((assoclen + IV + req->cryptlen), 16)
- * 16) : (sgl_len(reqctx->src_nents + reqctx->aad_nents
+ temp = reqctx->imm ? roundup(assoclen + IV + req->cryptlen, 16)
+ : (sgl_len(reqctx->src_nents + reqctx->aad_nents
+ MIN_GCM_SG) * 8);
transhdr_len += temp;
- transhdr_len = DIV_ROUND_UP(transhdr_len, 16) * 16;
+ transhdr_len = roundup(transhdr_len, 16);
if (chcr_aead_need_fallback(req, dnents, T6_MAX_AAD_SIZE,
transhdr_len, op_type)) {
@@ -2187,9 +2362,8 @@ static struct sk_buff *create_authenc_wr(struct aead_request *req,
memcpy(chcr_req->key_ctx.key, actx->dec_rrkey,
aeadctx->enckey_len);
- memcpy(chcr_req->key_ctx.key + (DIV_ROUND_UP(aeadctx->enckey_len, 16) <<
- 4), actx->h_iopad, kctx_len -
- (DIV_ROUND_UP(aeadctx->enckey_len, 16) << 4));
+ memcpy(chcr_req->key_ctx.key + roundup(aeadctx->enckey_len, 16),
+ actx->h_iopad, kctx_len - roundup(aeadctx->enckey_len, 16));
if (subtype == CRYPTO_ALG_SUB_TYPE_CTR_SHA ||
subtype == CRYPTO_ALG_SUB_TYPE_CTR_NULL) {
memcpy(reqctx->iv, aeadctx->nonce, CTR_RFC3686_NONCE_SIZE);
@@ -2398,22 +2572,26 @@ void chcr_add_hash_src_ent(struct ahash_request *req,
struct ulptx_walk ulp_walk;
struct chcr_ahash_req_ctx *reqctx = ahash_request_ctx(req);
- if (reqctx->imm) {
+ if (reqctx->hctx_wr.imm) {
u8 *buf = (u8 *)ulptx;
if (param->bfr_len) {
memcpy(buf, reqctx->reqbfr, param->bfr_len);
buf += param->bfr_len;
}
- sg_pcopy_to_buffer(req->src, sg_nents(req->src),
- buf, param->sg_len, 0);
+
+ sg_pcopy_to_buffer(reqctx->hctx_wr.srcsg,
+ sg_nents(reqctx->hctx_wr.srcsg), buf,
+ param->sg_len, 0);
} else {
ulptx_walk_init(&ulp_walk, ulptx);
if (param->bfr_len)
ulptx_walk_add_page(&ulp_walk, param->bfr_len,
- &reqctx->dma_addr);
- ulptx_walk_add_sg(&ulp_walk, req->src, param->sg_len,
- 0);
+ &reqctx->hctx_wr.dma_addr);
+ ulptx_walk_add_sg(&ulp_walk, reqctx->hctx_wr.srcsg,
+ param->sg_len, reqctx->hctx_wr.src_ofst);
+ reqctx->hctx_wr.srcsg = ulp_walk.last_sg;
+ reqctx->hctx_wr.src_ofst = ulp_walk.last_sg_len;
ulptx_walk_end(&ulp_walk);
}
}
@@ -2430,7 +2608,7 @@ int chcr_hash_dma_map(struct device *dev,
DMA_TO_DEVICE);
if (!error)
return -ENOMEM;
- req_ctx->is_sg_map = 1;
+ req_ctx->hctx_wr.is_sg_map = 1;
return 0;
}
@@ -2444,7 +2622,7 @@ void chcr_hash_dma_unmap(struct device *dev,
dma_unmap_sg(dev, req->src, sg_nents(req->src),
DMA_TO_DEVICE);
- req_ctx->is_sg_map = 0;
+ req_ctx->hctx_wr.is_sg_map = 0;
}
@@ -2636,10 +2814,10 @@ static void fill_sec_cpl_for_aead(struct cpl_tx_sec_pdu *sec_cpl,
0, dst_size);
}
-int aead_ccm_validate_input(unsigned short op_type,
- struct aead_request *req,
- struct chcr_aead_ctx *aeadctx,
- unsigned int sub_type)
+static int aead_ccm_validate_input(unsigned short op_type,
+ struct aead_request *req,
+ struct chcr_aead_ctx *aeadctx,
+ unsigned int sub_type)
{
if (sub_type != CRYPTO_ALG_SUB_TYPE_AEAD_RFC4309) {
if (crypto_ccm_check_iv(req->iv)) {
@@ -2696,16 +2874,16 @@ static struct sk_buff *create_aead_ccm_wr(struct aead_request *req,
CHCR_DST_SG_SIZE, req->assoclen);
dnents += MIN_CCM_SG; // For IV and B0
dst_size = get_space_for_phys_dsgl(dnents);
- kctx_len = ((DIV_ROUND_UP(aeadctx->enckey_len, 16)) << 4) * 2;
+ kctx_len = roundup(aeadctx->enckey_len, 16) * 2;
transhdr_len = CIPHER_TRANSHDR_SIZE(kctx_len, dst_size);
reqctx->imm = (transhdr_len + assoclen + IV + req->cryptlen +
reqctx->b0_len) <= SGE_MAX_WR_LEN;
- temp = reqctx->imm ? (DIV_ROUND_UP((assoclen + IV + req->cryptlen +
- reqctx->b0_len), 16) * 16) :
+ temp = reqctx->imm ? roundup(assoclen + IV + req->cryptlen +
+ reqctx->b0_len, 16) :
(sgl_len(reqctx->src_nents + reqctx->aad_nents +
MIN_CCM_SG) * 8);
transhdr_len += temp;
- transhdr_len = DIV_ROUND_UP(transhdr_len, 16) * 16;
+ transhdr_len = roundup(transhdr_len, 16);
if (chcr_aead_need_fallback(req, dnents, T6_MAX_AAD_SIZE -
reqctx->b0_len, transhdr_len, op_type)) {
@@ -2727,8 +2905,8 @@ static struct sk_buff *create_aead_ccm_wr(struct aead_request *req,
chcr_req->key_ctx.ctx_hdr = aeadctx->key_ctx_hdr;
memcpy(chcr_req->key_ctx.key, aeadctx->key, aeadctx->enckey_len);
- memcpy(chcr_req->key_ctx.key + (DIV_ROUND_UP(aeadctx->enckey_len, 16) *
- 16), aeadctx->key, aeadctx->enckey_len);
+ memcpy(chcr_req->key_ctx.key + roundup(aeadctx->enckey_len, 16),
+ aeadctx->key, aeadctx->enckey_len);
phys_cpl = (struct cpl_rx_phys_dsgl *)((u8 *)(chcr_req + 1) + kctx_len);
ulptx = (struct ulptx_sgl *)((u8 *)(phys_cpl + 1) + dst_size);
@@ -2798,16 +2976,15 @@ static struct sk_buff *create_gcm_wr(struct aead_request *req,
CHCR_DST_SG_SIZE, req->assoclen);
dnents += MIN_GCM_SG; // For IV
dst_size = get_space_for_phys_dsgl(dnents);
- kctx_len = ((DIV_ROUND_UP(aeadctx->enckey_len, 16)) << 4) +
- AEAD_H_SIZE;
+ kctx_len = roundup(aeadctx->enckey_len, 16) + AEAD_H_SIZE;
transhdr_len = CIPHER_TRANSHDR_SIZE(kctx_len, dst_size);
reqctx->imm = (transhdr_len + assoclen + IV + req->cryptlen) <=
SGE_MAX_WR_LEN;
- temp = reqctx->imm ? (DIV_ROUND_UP((assoclen + IV +
- req->cryptlen), 16) * 16) : (sgl_len(reqctx->src_nents +
- reqctx->aad_nents + MIN_GCM_SG) * 8);
+ temp = reqctx->imm ? roundup(assoclen + IV + req->cryptlen, 16) :
+ (sgl_len(reqctx->src_nents +
+ reqctx->aad_nents + MIN_GCM_SG) * 8);
transhdr_len += temp;
- transhdr_len = DIV_ROUND_UP(transhdr_len, 16) * 16;
+ transhdr_len = roundup(transhdr_len, 16);
if (chcr_aead_need_fallback(req, dnents, T6_MAX_AAD_SIZE,
transhdr_len, op_type)) {
atomic_inc(&adap->chcr_stats.fallback);
@@ -2846,8 +3023,8 @@ static struct sk_buff *create_gcm_wr(struct aead_request *req,
0, 0, dst_size);
chcr_req->key_ctx.ctx_hdr = aeadctx->key_ctx_hdr;
memcpy(chcr_req->key_ctx.key, aeadctx->key, aeadctx->enckey_len);
- memcpy(chcr_req->key_ctx.key + (DIV_ROUND_UP(aeadctx->enckey_len, 16) *
- 16), GCM_CTX(aeadctx)->ghash_h, AEAD_H_SIZE);
+ memcpy(chcr_req->key_ctx.key + roundup(aeadctx->enckey_len, 16),
+ GCM_CTX(aeadctx)->ghash_h, AEAD_H_SIZE);
/* prepare a 16 byte iv */
/* S A L T | IV | 0x00000001 */
@@ -3067,11 +3244,10 @@ static int chcr_ccm_common_setkey(struct crypto_aead *aead,
unsigned char ck_size, mk_size;
int key_ctx_size = 0;
- key_ctx_size = sizeof(struct _key_ctx) +
- ((DIV_ROUND_UP(keylen, 16)) << 4) * 2;
+ key_ctx_size = sizeof(struct _key_ctx) + roundup(keylen, 16) * 2;
if (keylen == AES_KEYSIZE_128) {
- mk_size = CHCR_KEYCTX_CIPHER_KEY_SIZE_128;
ck_size = CHCR_KEYCTX_CIPHER_KEY_SIZE_128;
+ mk_size = CHCR_KEYCTX_MAC_KEY_SIZE_128;
} else if (keylen == AES_KEYSIZE_192) {
ck_size = CHCR_KEYCTX_CIPHER_KEY_SIZE_192;
mk_size = CHCR_KEYCTX_MAC_KEY_SIZE_192;
@@ -3178,10 +3354,9 @@ static int chcr_gcm_setkey(struct crypto_aead *aead, const u8 *key,
memcpy(aeadctx->key, key, keylen);
aeadctx->enckey_len = keylen;
- key_ctx_size = sizeof(struct _key_ctx) +
- ((DIV_ROUND_UP(keylen, 16)) << 4) +
+ key_ctx_size = sizeof(struct _key_ctx) + roundup(keylen, 16) +
AEAD_H_SIZE;
- aeadctx->key_ctx_hdr = FILL_KEY_CTX_HDR(ck_size,
+ aeadctx->key_ctx_hdr = FILL_KEY_CTX_HDR(ck_size,
CHCR_KEYCTX_MAC_KEY_SIZE_128,
0, 0,
key_ctx_size >> 4);
@@ -3281,6 +3456,7 @@ static int chcr_authenc_setkey(struct crypto_aead *authenc, const u8 *key,
if (IS_ERR(base_hash)) {
pr_err("chcr : Base driver cannot be loaded\n");
aeadctx->enckey_len = 0;
+ memzero_explicit(&keys, sizeof(keys));
return -EINVAL;
}
{
@@ -3325,17 +3501,19 @@ static int chcr_authenc_setkey(struct crypto_aead *authenc, const u8 *key,
chcr_change_order(actx->h_iopad, param.result_size);
chcr_change_order(o_ptr, param.result_size);
key_ctx_len = sizeof(struct _key_ctx) +
- ((DIV_ROUND_UP(keys.enckeylen, 16)) << 4) +
+ roundup(keys.enckeylen, 16) +
(param.result_size + align) * 2;
aeadctx->key_ctx_hdr = FILL_KEY_CTX_HDR(ck_size, param.mk_size,
0, 1, key_ctx_len >> 4);
actx->auth_mode = param.auth_mode;
chcr_free_shash(base_hash);
+ memzero_explicit(&keys, sizeof(keys));
return 0;
}
out:
aeadctx->enckey_len = 0;
+ memzero_explicit(&keys, sizeof(keys));
if (!IS_ERR(base_hash))
chcr_free_shash(base_hash);
return -EINVAL;
@@ -3393,15 +3571,16 @@ static int chcr_aead_digest_null_setkey(struct crypto_aead *authenc,
get_aes_decrypt_key(actx->dec_rrkey, aeadctx->key,
aeadctx->enckey_len << 3);
}
- key_ctx_len = sizeof(struct _key_ctx)
- + ((DIV_ROUND_UP(keys.enckeylen, 16)) << 4);
+ key_ctx_len = sizeof(struct _key_ctx) + roundup(keys.enckeylen, 16);
aeadctx->key_ctx_hdr = FILL_KEY_CTX_HDR(ck_size, CHCR_KEYCTX_NO_KEY, 0,
0, key_ctx_len >> 4);
actx->auth_mode = CHCR_SCMD_AUTH_MODE_NOP;
+ memzero_explicit(&keys, sizeof(keys));
return 0;
out:
aeadctx->enckey_len = 0;
+ memzero_explicit(&keys, sizeof(keys));
return -EINVAL;
}
diff --git a/drivers/crypto/chelsio/chcr_algo.h b/drivers/crypto/chelsio/chcr_algo.h
index d1673a5d4bf5..dba3dff1e209 100644
--- a/drivers/crypto/chelsio/chcr_algo.h
+++ b/drivers/crypto/chelsio/chcr_algo.h
@@ -86,6 +86,39 @@
KEY_CONTEXT_OPAD_PRESENT_M)
#define KEY_CONTEXT_OPAD_PRESENT_F KEY_CONTEXT_OPAD_PRESENT_V(1U)
+#define TLS_KEYCTX_RXFLIT_CNT_S 24
+#define TLS_KEYCTX_RXFLIT_CNT_V(x) ((x) << TLS_KEYCTX_RXFLIT_CNT_S)
+
+#define TLS_KEYCTX_RXPROT_VER_S 20
+#define TLS_KEYCTX_RXPROT_VER_M 0xf
+#define TLS_KEYCTX_RXPROT_VER_V(x) ((x) << TLS_KEYCTX_RXPROT_VER_S)
+
+#define TLS_KEYCTX_RXCIPH_MODE_S 16
+#define TLS_KEYCTX_RXCIPH_MODE_M 0xf
+#define TLS_KEYCTX_RXCIPH_MODE_V(x) ((x) << TLS_KEYCTX_RXCIPH_MODE_S)
+
+#define TLS_KEYCTX_RXAUTH_MODE_S 12
+#define TLS_KEYCTX_RXAUTH_MODE_M 0xf
+#define TLS_KEYCTX_RXAUTH_MODE_V(x) ((x) << TLS_KEYCTX_RXAUTH_MODE_S)
+
+#define TLS_KEYCTX_RXCIAU_CTRL_S 11
+#define TLS_KEYCTX_RXCIAU_CTRL_V(x) ((x) << TLS_KEYCTX_RXCIAU_CTRL_S)
+
+#define TLS_KEYCTX_RX_SEQCTR_S 9
+#define TLS_KEYCTX_RX_SEQCTR_M 0x3
+#define TLS_KEYCTX_RX_SEQCTR_V(x) ((x) << TLS_KEYCTX_RX_SEQCTR_S)
+
+#define TLS_KEYCTX_RX_VALID_S 8
+#define TLS_KEYCTX_RX_VALID_V(x) ((x) << TLS_KEYCTX_RX_VALID_S)
+
+#define TLS_KEYCTX_RXCK_SIZE_S 3
+#define TLS_KEYCTX_RXCK_SIZE_M 0x7
+#define TLS_KEYCTX_RXCK_SIZE_V(x) ((x) << TLS_KEYCTX_RXCK_SIZE_S)
+
+#define TLS_KEYCTX_RXMK_SIZE_S 0
+#define TLS_KEYCTX_RXMK_SIZE_M 0x7
+#define TLS_KEYCTX_RXMK_SIZE_V(x) ((x) << TLS_KEYCTX_RXMK_SIZE_S)
+
#define CHCR_HASH_MAX_DIGEST_SIZE 64
#define CHCR_MAX_SHA_DIGEST_SIZE 64
@@ -176,6 +209,15 @@
KEY_CONTEXT_SALT_PRESENT_V(1) | \
KEY_CONTEXT_CTX_LEN_V((ctx_len)))
+#define FILL_KEY_CRX_HDR(ck_size, mk_size, d_ck, opad, ctx_len) \
+ htonl(TLS_KEYCTX_RXMK_SIZE_V(mk_size) | \
+ TLS_KEYCTX_RXCK_SIZE_V(ck_size) | \
+ TLS_KEYCTX_RX_VALID_V(1) | \
+ TLS_KEYCTX_RX_SEQCTR_V(3) | \
+ TLS_KEYCTX_RXAUTH_MODE_V(4) | \
+ TLS_KEYCTX_RXCIPH_MODE_V(2) | \
+ TLS_KEYCTX_RXFLIT_CNT_V((ctx_len)))
+
#define FILL_WR_OP_CCTX_SIZE \
htonl( \
FW_CRYPTO_LOOKASIDE_WR_OPCODE_V( \
@@ -216,15 +258,16 @@
#define FILL_CMD_MORE(immdatalen) htonl(ULPTX_CMD_V(ULP_TX_SC_IMM) |\
ULP_TX_SC_MORE_V((immdatalen)))
#define MAX_NK 8
-#define ROUND_16(bytes) ((bytes) & 0xFFFFFFF0)
#define MAX_DSGL_ENT 32
#define MIN_CIPHER_SG 1 /* IV */
#define MIN_AUTH_SG 1 /* IV */
#define MIN_GCM_SG 1 /* IV */
#define MIN_DIGEST_SG 1 /*Partial Buffer*/
#define MIN_CCM_SG 2 /*IV+B0*/
-#define SPACE_LEFT(len) \
- ((SGE_MAX_WR_LEN - WR_MIN_LEN - (len)))
+#define CIP_SPACE_LEFT(len) \
+ ((SGE_MAX_WR_LEN - CIP_WR_MIN_LEN - (len)))
+#define HASH_SPACE_LEFT(len) \
+ ((SGE_MAX_WR_LEN - HASH_WR_MIN_LEN - (len)))
struct algo_param {
unsigned int auth_mode;
@@ -233,12 +276,14 @@ struct algo_param {
};
struct hash_wr_param {
+ struct algo_param alg_prm;
unsigned int opad_needed;
unsigned int more;
unsigned int last;
- struct algo_param alg_prm;
+ unsigned int kctx_len;
unsigned int sg_len;
unsigned int bfr_len;
+ unsigned int hash_size;
u64 scmd1;
};
diff --git a/drivers/crypto/chelsio/chcr_core.h b/drivers/crypto/chelsio/chcr_core.h
index 3c29ee09b8b5..1a20424e18c6 100644
--- a/drivers/crypto/chelsio/chcr_core.h
+++ b/drivers/crypto/chelsio/chcr_core.h
@@ -54,10 +54,14 @@
#define MAC_ERROR_BIT 0
#define CHK_MAC_ERR_BIT(x) (((x) >> MAC_ERROR_BIT) & 1)
#define MAX_SALT 4
-#define WR_MIN_LEN (sizeof(struct chcr_wr) + \
+#define CIP_WR_MIN_LEN (sizeof(struct chcr_wr) + \
sizeof(struct cpl_rx_phys_dsgl) + \
sizeof(struct ulptx_sgl))
+#define HASH_WR_MIN_LEN (sizeof(struct chcr_wr) + \
+ DUMMY_BYTES + \
+ sizeof(struct ulptx_sgl))
+
#define padap(dev) pci_get_drvdata(dev->u_ctx->lldi.pdev)
struct uld_ctx;
@@ -65,10 +69,58 @@ struct uld_ctx;
struct _key_ctx {
__be32 ctx_hdr;
u8 salt[MAX_SALT];
- __be64 reserverd;
+ __be64 iv_to_auth;
unsigned char key[0];
};
+#define KEYCTX_TX_WR_IV_S 55
+#define KEYCTX_TX_WR_IV_M 0x1ffULL
+#define KEYCTX_TX_WR_IV_V(x) ((x) << KEYCTX_TX_WR_IV_S)
+#define KEYCTX_TX_WR_IV_G(x) \
+ (((x) >> KEYCTX_TX_WR_IV_S) & KEYCTX_TX_WR_IV_M)
+
+#define KEYCTX_TX_WR_AAD_S 47
+#define KEYCTX_TX_WR_AAD_M 0xffULL
+#define KEYCTX_TX_WR_AAD_V(x) ((x) << KEYCTX_TX_WR_AAD_S)
+#define KEYCTX_TX_WR_AAD_G(x) (((x) >> KEYCTX_TX_WR_AAD_S) & \
+ KEYCTX_TX_WR_AAD_M)
+
+#define KEYCTX_TX_WR_AADST_S 39
+#define KEYCTX_TX_WR_AADST_M 0xffULL
+#define KEYCTX_TX_WR_AADST_V(x) ((x) << KEYCTX_TX_WR_AADST_S)
+#define KEYCTX_TX_WR_AADST_G(x) \
+ (((x) >> KEYCTX_TX_WR_AADST_S) & KEYCTX_TX_WR_AADST_M)
+
+#define KEYCTX_TX_WR_CIPHER_S 30
+#define KEYCTX_TX_WR_CIPHER_M 0x1ffULL
+#define KEYCTX_TX_WR_CIPHER_V(x) ((x) << KEYCTX_TX_WR_CIPHER_S)
+#define KEYCTX_TX_WR_CIPHER_G(x) \
+ (((x) >> KEYCTX_TX_WR_CIPHER_S) & KEYCTX_TX_WR_CIPHER_M)
+
+#define KEYCTX_TX_WR_CIPHERST_S 23
+#define KEYCTX_TX_WR_CIPHERST_M 0x7f
+#define KEYCTX_TX_WR_CIPHERST_V(x) ((x) << KEYCTX_TX_WR_CIPHERST_S)
+#define KEYCTX_TX_WR_CIPHERST_G(x) \
+ (((x) >> KEYCTX_TX_WR_CIPHERST_S) & KEYCTX_TX_WR_CIPHERST_M)
+
+#define KEYCTX_TX_WR_AUTH_S 14
+#define KEYCTX_TX_WR_AUTH_M 0x1ff
+#define KEYCTX_TX_WR_AUTH_V(x) ((x) << KEYCTX_TX_WR_AUTH_S)
+#define KEYCTX_TX_WR_AUTH_G(x) \
+ (((x) >> KEYCTX_TX_WR_AUTH_S) & KEYCTX_TX_WR_AUTH_M)
+
+#define KEYCTX_TX_WR_AUTHST_S 7
+#define KEYCTX_TX_WR_AUTHST_M 0x7f
+#define KEYCTX_TX_WR_AUTHST_V(x) ((x) << KEYCTX_TX_WR_AUTHST_S)
+#define KEYCTX_TX_WR_AUTHST_G(x) \
+ (((x) >> KEYCTX_TX_WR_AUTHST_S) & KEYCTX_TX_WR_AUTHST_M)
+
+#define KEYCTX_TX_WR_AUTHIN_S 0
+#define KEYCTX_TX_WR_AUTHIN_M 0x7f
+#define KEYCTX_TX_WR_AUTHIN_V(x) ((x) << KEYCTX_TX_WR_AUTHIN_S)
+#define KEYCTX_TX_WR_AUTHIN_G(x) \
+ (((x) >> KEYCTX_TX_WR_AUTHIN_S) & KEYCTX_TX_WR_AUTHIN_M)
+
struct chcr_wr {
struct fw_crypto_lookaside_wr wreq;
struct ulp_txpkt ulptx;
@@ -90,6 +142,11 @@ struct uld_ctx {
struct chcr_dev *dev;
};
+struct sge_opaque_hdr {
+ void *dev;
+ dma_addr_t addr[MAX_SKB_FRAGS + 1];
+};
+
struct chcr_ipsec_req {
struct ulp_txpkt ulptx;
struct ulptx_idata sc_imm;
diff --git a/drivers/crypto/chelsio/chcr_crypto.h b/drivers/crypto/chelsio/chcr_crypto.h
index 7daf0a17a7d2..c8e8972af283 100644
--- a/drivers/crypto/chelsio/chcr_crypto.h
+++ b/drivers/crypto/chelsio/chcr_crypto.h
@@ -258,21 +258,32 @@ struct chcr_context {
struct __crypto_ctx crypto_ctx[0];
};
-struct chcr_ahash_req_ctx {
+struct chcr_hctx_per_wr {
+ struct scatterlist *srcsg;
+ struct sk_buff *skb;
+ dma_addr_t dma_addr;
+ u32 dma_len;
+ unsigned int src_ofst;
+ unsigned int processed;
u32 result;
- u8 bfr1[CHCR_HASH_MAX_BLOCK_SIZE_128];
- u8 bfr2[CHCR_HASH_MAX_BLOCK_SIZE_128];
+ u8 is_sg_map;
+ u8 imm;
+ /*Final callback called. Driver cannot rely on nbytes to decide
+ * final call
+ */
+ u8 isfinal;
+};
+
+struct chcr_ahash_req_ctx {
+ struct chcr_hctx_per_wr hctx_wr;
u8 *reqbfr;
u8 *skbfr;
- dma_addr_t dma_addr;
- u32 dma_len;
+ /* SKB which is being sent to the hardware for processing */
+ u64 data_len; /* Data len till time */
u8 reqlen;
- u8 imm;
- u8 is_sg_map;
u8 partial_hash[CHCR_HASH_MAX_DIGEST_SIZE];
- u64 data_len; /* Data len till time */
- /* SKB which is being sent to the hardware for processing */
- struct sk_buff *skb;
+ u8 bfr1[CHCR_HASH_MAX_BLOCK_SIZE_128];
+ u8 bfr2[CHCR_HASH_MAX_BLOCK_SIZE_128];
};
struct chcr_blkcipher_req_ctx {
diff --git a/drivers/crypto/chelsio/chcr_ipsec.c b/drivers/crypto/chelsio/chcr_ipsec.c
index db1e241104ed..8e0aa3f175c9 100644
--- a/drivers/crypto/chelsio/chcr_ipsec.c
+++ b/drivers/crypto/chelsio/chcr_ipsec.c
@@ -360,8 +360,7 @@ inline void *copy_cpltx_pktxt(struct sk_buff *skb,
cpl = (struct cpl_tx_pkt_core *)pos;
- if (skb->ip_summed == CHECKSUM_PARTIAL)
- cntrl = TXPKT_L4CSUM_DIS_F | TXPKT_IPCSUM_DIS_F;
+ cntrl = TXPKT_L4CSUM_DIS_F | TXPKT_IPCSUM_DIS_F;
ctrl0 = TXPKT_OPCODE_V(CPL_TX_PKT_XT) | TXPKT_INTF_V(pi->tx_chan) |
TXPKT_PF_V(adap->pf);
if (skb_vlan_tag_present(skb)) {
@@ -475,7 +474,7 @@ inline void *chcr_crypto_wreq(struct sk_buff *skb,
wr->req.ulptx.len = htonl(DIV_ROUND_UP(flits, 2) - 1);
/* Sub-command */
- wr->req.sc_imm.cmd_more = FILL_CMD_MORE(immdatalen);
+ wr->req.sc_imm.cmd_more = FILL_CMD_MORE(!immdatalen);
wr->req.sc_imm.len = cpu_to_be32(sizeof(struct cpl_tx_sec_pdu) +
sizeof(wr->req.key_ctx) +
kctx_len +
diff --git a/drivers/crypto/chelsio/chtls/Makefile b/drivers/crypto/chelsio/chtls/Makefile
new file mode 100644
index 000000000000..df1379570a8e
--- /dev/null
+++ b/drivers/crypto/chelsio/chtls/Makefile
@@ -0,0 +1,4 @@
+ccflags-y := -Idrivers/net/ethernet/chelsio/cxgb4 -Idrivers/crypto/chelsio/
+
+obj-$(CONFIG_CRYPTO_DEV_CHELSIO_TLS) += chtls.o
+chtls-objs := chtls_main.o chtls_cm.o chtls_io.o chtls_hw.o
diff --git a/drivers/crypto/chelsio/chtls/chtls.h b/drivers/crypto/chelsio/chtls/chtls.h
new file mode 100644
index 000000000000..f4b8f1ec0061
--- /dev/null
+++ b/drivers/crypto/chelsio/chtls/chtls.h
@@ -0,0 +1,482 @@
+/*
+ * Copyright (c) 2018 Chelsio Communications, Inc.
+ *
+ * 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.
+ */
+
+#ifndef __CHTLS_H__
+#define __CHTLS_H__
+
+#include <crypto/aes.h>
+#include <crypto/algapi.h>
+#include <crypto/hash.h>
+#include <crypto/sha.h>
+#include <crypto/authenc.h>
+#include <crypto/ctr.h>
+#include <crypto/gf128mul.h>
+#include <crypto/internal/aead.h>
+#include <crypto/null.h>
+#include <crypto/internal/skcipher.h>
+#include <crypto/aead.h>
+#include <crypto/scatterwalk.h>
+#include <crypto/internal/hash.h>
+#include <linux/tls.h>
+#include <net/tls.h>
+
+#include "t4fw_api.h"
+#include "t4_msg.h"
+#include "cxgb4.h"
+#include "cxgb4_uld.h"
+#include "l2t.h"
+#include "chcr_algo.h"
+#include "chcr_core.h"
+#include "chcr_crypto.h"
+
+#define MAX_IVS_PAGE 256
+#define TLS_KEY_CONTEXT_SZ 64
+#define CIPHER_BLOCK_SIZE 16
+#define GCM_TAG_SIZE 16
+#define KEY_ON_MEM_SZ 16
+#define AEAD_EXPLICIT_DATA_SIZE 8
+#define TLS_HEADER_LENGTH 5
+#define SCMD_CIPH_MODE_AES_GCM 2
+/* Any MFS size should work and come from openssl */
+#define TLS_MFS 16384
+
+#define RSS_HDR sizeof(struct rss_header)
+#define TLS_WR_CPL_LEN \
+ (sizeof(struct fw_tlstx_data_wr) + sizeof(struct cpl_tx_tls_sfo))
+
+enum {
+ CHTLS_KEY_CONTEXT_DSGL,
+ CHTLS_KEY_CONTEXT_IMM,
+ CHTLS_KEY_CONTEXT_DDR,
+};
+
+enum {
+ CHTLS_LISTEN_START,
+ CHTLS_LISTEN_STOP,
+};
+
+/* Flags for return value of CPL message handlers */
+enum {
+ CPL_RET_BUF_DONE = 1, /* buffer processing done */
+ CPL_RET_BAD_MSG = 2, /* bad CPL message */
+ CPL_RET_UNKNOWN_TID = 4 /* unexpected unknown TID */
+};
+
+#define TLS_RCV_ST_READ_HEADER 0xF0
+#define TLS_RCV_ST_READ_BODY 0xF1
+#define TLS_RCV_ST_READ_DONE 0xF2
+#define TLS_RCV_ST_READ_NB 0xF3
+
+#define LISTEN_INFO_HASH_SIZE 32
+#define RSPQ_HASH_BITS 5
+struct listen_info {
+ struct listen_info *next; /* Link to next entry */
+ struct sock *sk; /* The listening socket */
+ unsigned int stid; /* The server TID */
+};
+
+enum {
+ T4_LISTEN_START_PENDING,
+ T4_LISTEN_STARTED
+};
+
+enum csk_flags {
+ CSK_CALLBACKS_CHKD, /* socket callbacks have been sanitized */
+ CSK_ABORT_REQ_RCVD, /* received one ABORT_REQ_RSS message */
+ CSK_TX_MORE_DATA, /* sending ULP data; don't set SHOVE bit */
+ CSK_TX_WAIT_IDLE, /* suspend Tx until in-flight data is ACKed */
+ CSK_ABORT_SHUTDOWN, /* shouldn't send more abort requests */
+ CSK_ABORT_RPL_PENDING, /* expecting an abort reply */
+ CSK_CLOSE_CON_REQUESTED,/* we've sent a close_conn_req */
+ CSK_TX_DATA_SENT, /* sent a TX_DATA WR on this connection */
+ CSK_TX_FAILOVER, /* Tx traffic failing over */
+ CSK_UPDATE_RCV_WND, /* Need to update rcv window */
+ CSK_RST_ABORTED, /* outgoing RST was aborted */
+ CSK_TLS_HANDSHK, /* TLS Handshake */
+ CSK_CONN_INLINE, /* Connection on HW */
+};
+
+struct listen_ctx {
+ struct sock *lsk;
+ struct chtls_dev *cdev;
+ struct sk_buff_head synq;
+ u32 state;
+};
+
+struct key_map {
+ unsigned long *addr;
+ unsigned int start;
+ unsigned int available;
+ unsigned int size;
+ spinlock_t lock; /* lock for key id request from map */
+} __packed;
+
+struct tls_scmd {
+ u32 seqno_numivs;
+ u32 ivgen_hdrlen;
+};
+
+struct chtls_dev {
+ struct tls_device tlsdev;
+ struct list_head list;
+ struct cxgb4_lld_info *lldi;
+ struct pci_dev *pdev;
+ struct listen_info *listen_hash_tab[LISTEN_INFO_HASH_SIZE];
+ spinlock_t listen_lock; /* lock for listen list */
+ struct net_device **ports;
+ struct tid_info *tids;
+ unsigned int pfvf;
+ const unsigned short *mtus;
+
+ struct idr hwtid_idr;
+ struct idr stid_idr;
+
+ spinlock_t idr_lock ____cacheline_aligned_in_smp;
+
+ struct net_device *egr_dev[NCHAN * 2];
+ struct sk_buff *rspq_skb_cache[1 << RSPQ_HASH_BITS];
+ struct sk_buff *askb;
+
+ struct sk_buff_head deferq;
+ struct work_struct deferq_task;
+
+ struct list_head list_node;
+ struct list_head rcu_node;
+ struct list_head na_node;
+ unsigned int send_page_order;
+ struct key_map kmap;
+};
+
+struct chtls_hws {
+ struct sk_buff_head sk_recv_queue;
+ u8 txqid;
+ u8 ofld;
+ u16 type;
+ u16 rstate;
+ u16 keyrpl;
+ u16 pldlen;
+ u16 rcvpld;
+ u16 compute;
+ u16 expansion;
+ u16 keylen;
+ u16 pdus;
+ u16 adjustlen;
+ u16 ivsize;
+ u16 txleft;
+ u32 mfs;
+ s32 txkey;
+ s32 rxkey;
+ u32 fcplenmax;
+ u32 copied_seq;
+ u64 tx_seq_no;
+ struct tls_scmd scmd;
+ struct tls12_crypto_info_aes_gcm_128 crypto_info;
+};
+
+struct chtls_sock {
+ struct sock *sk;
+ struct chtls_dev *cdev;
+ struct l2t_entry *l2t_entry; /* pointer to the L2T entry */
+ struct net_device *egress_dev; /* TX_CHAN for act open retry */
+
+ struct sk_buff_head txq;
+ struct sk_buff *wr_skb_head;
+ struct sk_buff *wr_skb_tail;
+ struct sk_buff *ctrl_skb_cache;
+ struct sk_buff *txdata_skb_cache; /* abort path messages */
+ struct kref kref;
+ unsigned long flags;
+ u32 opt2;
+ u32 wr_credits;
+ u32 wr_unacked;
+ u32 wr_max_credits;
+ u32 wr_nondata;
+ u32 hwtid; /* TCP Control Block ID */
+ u32 txq_idx;
+ u32 rss_qid;
+ u32 tid;
+ u32 idr;
+ u32 mss;
+ u32 ulp_mode;
+ u32 tx_chan;
+ u32 rx_chan;
+ u32 sndbuf;
+ u32 txplen_max;
+ u32 mtu_idx; /* MTU table index */
+ u32 smac_idx;
+ u8 port_id;
+ u8 tos;
+ u16 resv2;
+ u32 delack_mode;
+ u32 delack_seq;
+
+ void *passive_reap_next; /* placeholder for passive */
+ struct chtls_hws tlshws;
+ struct synq {
+ struct sk_buff *next;
+ struct sk_buff *prev;
+ } synq;
+ struct listen_ctx *listen_ctx;
+};
+
+struct tls_hdr {
+ u8 type;
+ u16 version;
+ u16 length;
+} __packed;
+
+struct tlsrx_cmp_hdr {
+ u8 type;
+ u16 version;
+ u16 length;
+
+ u64 tls_seq;
+ u16 reserved1;
+ u8 res_to_mac_error;
+} __packed;
+
+/* res_to_mac_error fields */
+#define TLSRX_HDR_PKT_INT_ERROR_S 4
+#define TLSRX_HDR_PKT_INT_ERROR_M 0x1
+#define TLSRX_HDR_PKT_INT_ERROR_V(x) \
+ ((x) << TLSRX_HDR_PKT_INT_ERROR_S)
+#define TLSRX_HDR_PKT_INT_ERROR_G(x) \
+ (((x) >> TLSRX_HDR_PKT_INT_ERROR_S) & TLSRX_HDR_PKT_INT_ERROR_M)
+#define TLSRX_HDR_PKT_INT_ERROR_F TLSRX_HDR_PKT_INT_ERROR_V(1U)
+
+#define TLSRX_HDR_PKT_SPP_ERROR_S 3
+#define TLSRX_HDR_PKT_SPP_ERROR_M 0x1
+#define TLSRX_HDR_PKT_SPP_ERROR_V(x) ((x) << TLSRX_HDR_PKT_SPP_ERROR)
+#define TLSRX_HDR_PKT_SPP_ERROR_G(x) \
+ (((x) >> TLSRX_HDR_PKT_SPP_ERROR_S) & TLSRX_HDR_PKT_SPP_ERROR_M)
+#define TLSRX_HDR_PKT_SPP_ERROR_F TLSRX_HDR_PKT_SPP_ERROR_V(1U)
+
+#define TLSRX_HDR_PKT_CCDX_ERROR_S 2
+#define TLSRX_HDR_PKT_CCDX_ERROR_M 0x1
+#define TLSRX_HDR_PKT_CCDX_ERROR_V(x) ((x) << TLSRX_HDR_PKT_CCDX_ERROR_S)
+#define TLSRX_HDR_PKT_CCDX_ERROR_G(x) \
+ (((x) >> TLSRX_HDR_PKT_CCDX_ERROR_S) & TLSRX_HDR_PKT_CCDX_ERROR_M)
+#define TLSRX_HDR_PKT_CCDX_ERROR_F TLSRX_HDR_PKT_CCDX_ERROR_V(1U)
+
+#define TLSRX_HDR_PKT_PAD_ERROR_S 1
+#define TLSRX_HDR_PKT_PAD_ERROR_M 0x1
+#define TLSRX_HDR_PKT_PAD_ERROR_V(x) ((x) << TLSRX_HDR_PKT_PAD_ERROR_S)
+#define TLSRX_HDR_PKT_PAD_ERROR_G(x) \
+ (((x) >> TLSRX_HDR_PKT_PAD_ERROR_S) & TLSRX_HDR_PKT_PAD_ERROR_M)
+#define TLSRX_HDR_PKT_PAD_ERROR_F TLSRX_HDR_PKT_PAD_ERROR_V(1U)
+
+#define TLSRX_HDR_PKT_MAC_ERROR_S 0
+#define TLSRX_HDR_PKT_MAC_ERROR_M 0x1
+#define TLSRX_HDR_PKT_MAC_ERROR_V(x) ((x) << TLSRX_HDR_PKT_MAC_ERROR)
+#define TLSRX_HDR_PKT_MAC_ERROR_G(x) \
+ (((x) >> S_TLSRX_HDR_PKT_MAC_ERROR_S) & TLSRX_HDR_PKT_MAC_ERROR_M)
+#define TLSRX_HDR_PKT_MAC_ERROR_F TLSRX_HDR_PKT_MAC_ERROR_V(1U)
+
+#define TLSRX_HDR_PKT_ERROR_M 0x1F
+
+struct ulp_mem_rw {
+ __be32 cmd;
+ __be32 len16; /* command length */
+ __be32 dlen; /* data length in 32-byte units */
+ __be32 lock_addr;
+};
+
+struct tls_key_wr {
+ __be32 op_to_compl;
+ __be32 flowid_len16;
+ __be32 ftid;
+ u8 reneg_to_write_rx;
+ u8 protocol;
+ __be16 mfs;
+};
+
+struct tls_key_req {
+ struct tls_key_wr wr;
+ struct ulp_mem_rw req;
+ struct ulptx_idata sc_imm;
+};
+
+/*
+ * This lives in skb->cb and is used to chain WRs in a linked list.
+ */
+struct wr_skb_cb {
+ struct l2t_skb_cb l2t; /* reserve space for l2t CB */
+ struct sk_buff *next_wr; /* next write request */
+};
+
+/* Per-skb backlog handler. Run when a socket's backlog is processed. */
+struct blog_skb_cb {
+ void (*backlog_rcv)(struct sock *sk, struct sk_buff *skb);
+ struct chtls_dev *cdev;
+};
+
+/*
+ * Similar to tcp_skb_cb but with ULP elements added to support TLS,
+ * etc.
+ */
+struct ulp_skb_cb {
+ struct wr_skb_cb wr; /* reserve space for write request */
+ u16 flags; /* TCP-like flags */
+ u8 psh;
+ u8 ulp_mode; /* ULP mode/submode of sk_buff */
+ u32 seq; /* TCP sequence number */
+ union { /* ULP-specific fields */
+ struct {
+ u8 type;
+ u8 ofld;
+ u8 iv;
+ } tls;
+ } ulp;
+};
+
+#define ULP_SKB_CB(skb) ((struct ulp_skb_cb *)&((skb)->cb[0]))
+#define BLOG_SKB_CB(skb) ((struct blog_skb_cb *)(skb)->cb)
+
+/*
+ * Flags for ulp_skb_cb.flags.
+ */
+enum {
+ ULPCB_FLAG_NEED_HDR = 1 << 0, /* packet needs a TX_DATA_WR header */
+ ULPCB_FLAG_NO_APPEND = 1 << 1, /* don't grow this skb */
+ ULPCB_FLAG_BARRIER = 1 << 2, /* set TX_WAIT_IDLE after sending */
+ ULPCB_FLAG_HOLD = 1 << 3, /* skb not ready for Tx yet */
+ ULPCB_FLAG_COMPL = 1 << 4, /* request WR completion */
+ ULPCB_FLAG_URG = 1 << 5, /* urgent data */
+ ULPCB_FLAG_TLS_ND = 1 << 6, /* payload of zero length */
+ ULPCB_FLAG_NO_HDR = 1 << 7, /* not a ofld wr */
+};
+
+/* The ULP mode/submode of an skbuff */
+#define skb_ulp_mode(skb) (ULP_SKB_CB(skb)->ulp_mode)
+#define TCP_PAGE(sk) (sk->sk_frag.page)
+#define TCP_OFF(sk) (sk->sk_frag.offset)
+
+static inline struct chtls_dev *to_chtls_dev(struct tls_device *tlsdev)
+{
+ return container_of(tlsdev, struct chtls_dev, tlsdev);
+}
+
+static inline void csk_set_flag(struct chtls_sock *csk,
+ enum csk_flags flag)
+{
+ __set_bit(flag, &csk->flags);
+}
+
+static inline void csk_reset_flag(struct chtls_sock *csk,
+ enum csk_flags flag)
+{
+ __clear_bit(flag, &csk->flags);
+}
+
+static inline bool csk_conn_inline(const struct chtls_sock *csk)
+{
+ return test_bit(CSK_CONN_INLINE, &csk->flags);
+}
+
+static inline int csk_flag(const struct sock *sk, enum csk_flags flag)
+{
+ struct chtls_sock *csk = rcu_dereference_sk_user_data(sk);
+
+ if (!csk_conn_inline(csk))
+ return 0;
+ return test_bit(flag, &csk->flags);
+}
+
+static inline int csk_flag_nochk(const struct chtls_sock *csk,
+ enum csk_flags flag)
+{
+ return test_bit(flag, &csk->flags);
+}
+
+static inline void *cplhdr(struct sk_buff *skb)
+{
+ return skb->data;
+}
+
+static inline int is_neg_adv(unsigned int status)
+{
+ return status == CPL_ERR_RTX_NEG_ADVICE ||
+ status == CPL_ERR_KEEPALV_NEG_ADVICE ||
+ status == CPL_ERR_PERSIST_NEG_ADVICE;
+}
+
+static inline void process_cpl_msg(void (*fn)(struct sock *, struct sk_buff *),
+ struct sock *sk,
+ struct sk_buff *skb)
+{
+ skb_reset_mac_header(skb);
+ skb_reset_network_header(skb);
+ skb_reset_transport_header(skb);
+
+ bh_lock_sock(sk);
+ if (unlikely(sock_owned_by_user(sk))) {
+ BLOG_SKB_CB(skb)->backlog_rcv = fn;
+ __sk_add_backlog(sk, skb);
+ } else {
+ fn(sk, skb);
+ }
+ bh_unlock_sock(sk);
+}
+
+static inline void chtls_sock_free(struct kref *ref)
+{
+ struct chtls_sock *csk = container_of(ref, struct chtls_sock,
+ kref);
+ kfree(csk);
+}
+
+static inline void __chtls_sock_put(const char *fn, struct chtls_sock *csk)
+{
+ kref_put(&csk->kref, chtls_sock_free);
+}
+
+static inline void __chtls_sock_get(const char *fn,
+ struct chtls_sock *csk)
+{
+ kref_get(&csk->kref);
+}
+
+static inline void send_or_defer(struct sock *sk, struct tcp_sock *tp,
+ struct sk_buff *skb, int through_l2t)
+{
+ struct chtls_sock *csk = rcu_dereference_sk_user_data(sk);
+
+ if (through_l2t) {
+ /* send through L2T */
+ cxgb4_l2t_send(csk->egress_dev, skb, csk->l2t_entry);
+ } else {
+ /* send directly */
+ cxgb4_ofld_send(csk->egress_dev, skb);
+ }
+}
+
+typedef int (*chtls_handler_func)(struct chtls_dev *, struct sk_buff *);
+extern chtls_handler_func chtls_handlers[NUM_CPL_CMDS];
+void chtls_install_cpl_ops(struct sock *sk);
+int chtls_init_kmap(struct chtls_dev *cdev, struct cxgb4_lld_info *lldi);
+void chtls_listen_stop(struct chtls_dev *cdev, struct sock *sk);
+int chtls_listen_start(struct chtls_dev *cdev, struct sock *sk);
+void chtls_close(struct sock *sk, long timeout);
+int chtls_disconnect(struct sock *sk, int flags);
+void chtls_shutdown(struct sock *sk, int how);
+void chtls_destroy_sock(struct sock *sk);
+int chtls_sendmsg(struct sock *sk, struct msghdr *msg, size_t size);
+int chtls_recvmsg(struct sock *sk, struct msghdr *msg,
+ size_t len, int nonblock, int flags, int *addr_len);
+int chtls_sendpage(struct sock *sk, struct page *page,
+ int offset, size_t size, int flags);
+int send_tx_flowc_wr(struct sock *sk, int compl,
+ u32 snd_nxt, u32 rcv_nxt);
+void chtls_tcp_push(struct sock *sk, int flags);
+int chtls_push_frames(struct chtls_sock *csk, int comp);
+int chtls_set_tcb_tflag(struct sock *sk, unsigned int bit_pos, int val);
+int chtls_setkey(struct chtls_sock *csk, u32 keylen, u32 mode);
+void skb_entail(struct sock *sk, struct sk_buff *skb, int flags);
+unsigned int keyid_to_addr(int start_addr, int keyid);
+void free_tls_keyid(struct sock *sk);
+#endif
diff --git a/drivers/crypto/chelsio/chtls/chtls_cm.c b/drivers/crypto/chelsio/chtls/chtls_cm.c
new file mode 100644
index 000000000000..82a473a0cefa
--- /dev/null
+++ b/drivers/crypto/chelsio/chtls/chtls_cm.c
@@ -0,0 +1,2126 @@
+/*
+ * Copyright (c) 2018 Chelsio Communications, Inc.
+ *
+ * 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.
+ *
+ * Written by: Atul Gupta (atul.gupta@chelsio.com)
+ */
+
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/workqueue.h>
+#include <linux/skbuff.h>
+#include <linux/timer.h>
+#include <linux/notifier.h>
+#include <linux/inetdevice.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/sched/signal.h>
+#include <linux/kallsyms.h>
+#include <linux/kprobes.h>
+#include <linux/if_vlan.h>
+#include <net/tcp.h>
+#include <net/dst.h>
+
+#include "chtls.h"
+#include "chtls_cm.h"
+
+/*
+ * State transitions and actions for close. Note that if we are in SYN_SENT
+ * we remain in that state as we cannot control a connection while it's in
+ * SYN_SENT; such connections are allowed to establish and are then aborted.
+ */
+static unsigned char new_state[16] = {
+ /* current state: new state: action: */
+ /* (Invalid) */ TCP_CLOSE,
+ /* TCP_ESTABLISHED */ TCP_FIN_WAIT1 | TCP_ACTION_FIN,
+ /* TCP_SYN_SENT */ TCP_SYN_SENT,
+ /* TCP_SYN_RECV */ TCP_FIN_WAIT1 | TCP_ACTION_FIN,
+ /* TCP_FIN_WAIT1 */ TCP_FIN_WAIT1,
+ /* TCP_FIN_WAIT2 */ TCP_FIN_WAIT2,
+ /* TCP_TIME_WAIT */ TCP_CLOSE,
+ /* TCP_CLOSE */ TCP_CLOSE,
+ /* TCP_CLOSE_WAIT */ TCP_LAST_ACK | TCP_ACTION_FIN,
+ /* TCP_LAST_ACK */ TCP_LAST_ACK,
+ /* TCP_LISTEN */ TCP_CLOSE,
+ /* TCP_CLOSING */ TCP_CLOSING,
+};
+
+static struct chtls_sock *chtls_sock_create(struct chtls_dev *cdev)
+{
+ struct chtls_sock *csk = kzalloc(sizeof(*csk), GFP_ATOMIC);
+
+ if (!csk)
+ return NULL;
+
+ csk->txdata_skb_cache = alloc_skb(TXDATA_SKB_LEN, GFP_ATOMIC);
+ if (!csk->txdata_skb_cache) {
+ kfree(csk);
+ return NULL;
+ }
+
+ kref_init(&csk->kref);
+ csk->cdev = cdev;
+ skb_queue_head_init(&csk->txq);
+ csk->wr_skb_head = NULL;
+ csk->wr_skb_tail = NULL;
+ csk->mss = MAX_MSS;
+ csk->tlshws.ofld = 1;
+ csk->tlshws.txkey = -1;
+ csk->tlshws.rxkey = -1;
+ csk->tlshws.mfs = TLS_MFS;
+ skb_queue_head_init(&csk->tlshws.sk_recv_queue);
+ return csk;
+}
+
+static void chtls_sock_release(struct kref *ref)
+{
+ struct chtls_sock *csk =
+ container_of(ref, struct chtls_sock, kref);
+
+ kfree(csk);
+}
+
+static struct net_device *chtls_ipv4_netdev(struct chtls_dev *cdev,
+ struct sock *sk)
+{
+ struct net_device *ndev = cdev->ports[0];
+
+ if (likely(!inet_sk(sk)->inet_rcv_saddr))
+ return ndev;
+
+ ndev = ip_dev_find(&init_net, inet_sk(sk)->inet_rcv_saddr);
+ if (!ndev)
+ return NULL;
+
+ if (is_vlan_dev(ndev))
+ return vlan_dev_real_dev(ndev);
+ return ndev;
+}
+
+static void assign_rxopt(struct sock *sk, unsigned int opt)
+{
+ const struct chtls_dev *cdev;
+ struct chtls_sock *csk;
+ struct tcp_sock *tp;
+
+ csk = rcu_dereference_sk_user_data(sk);
+ tp = tcp_sk(sk);
+
+ cdev = csk->cdev;
+ tp->tcp_header_len = sizeof(struct tcphdr);
+ tp->rx_opt.mss_clamp = cdev->mtus[TCPOPT_MSS_G(opt)] - 40;
+ tp->mss_cache = tp->rx_opt.mss_clamp;
+ tp->rx_opt.tstamp_ok = TCPOPT_TSTAMP_G(opt);
+ tp->rx_opt.snd_wscale = TCPOPT_SACK_G(opt);
+ tp->rx_opt.wscale_ok = TCPOPT_WSCALE_OK_G(opt);
+ SND_WSCALE(tp) = TCPOPT_SND_WSCALE_G(opt);
+ if (!tp->rx_opt.wscale_ok)
+ tp->rx_opt.rcv_wscale = 0;
+ if (tp->rx_opt.tstamp_ok) {
+ tp->tcp_header_len += TCPOLEN_TSTAMP_ALIGNED;
+ tp->rx_opt.mss_clamp -= TCPOLEN_TSTAMP_ALIGNED;
+ } else if (csk->opt2 & TSTAMPS_EN_F) {
+ csk->opt2 &= ~TSTAMPS_EN_F;
+ csk->mtu_idx = TCPOPT_MSS_G(opt);
+ }
+}
+
+static void chtls_purge_receive_queue(struct sock *sk)
+{
+ struct sk_buff *skb;
+
+ while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) {
+ skb_dst_set(skb, (void *)NULL);
+ kfree_skb(skb);
+ }
+}
+
+static void chtls_purge_write_queue(struct sock *sk)
+{
+ struct chtls_sock *csk = rcu_dereference_sk_user_data(sk);
+ struct sk_buff *skb;
+
+ while ((skb = __skb_dequeue(&csk->txq))) {
+ sk->sk_wmem_queued -= skb->truesize;
+ __kfree_skb(skb);
+ }
+}
+
+static void chtls_purge_recv_queue(struct sock *sk)
+{
+ struct chtls_sock *csk = rcu_dereference_sk_user_data(sk);
+ struct chtls_hws *tlsk = &csk->tlshws;
+ struct sk_buff *skb;
+
+ while ((skb = __skb_dequeue(&tlsk->sk_recv_queue)) != NULL) {
+ skb_dst_set(skb, NULL);
+ kfree_skb(skb);
+ }
+}
+
+static void abort_arp_failure(void *handle, struct sk_buff *skb)
+{
+ struct cpl_abort_req *req = cplhdr(skb);
+ struct chtls_dev *cdev;
+
+ cdev = (struct chtls_dev *)handle;
+ req->cmd = CPL_ABORT_NO_RST;
+ cxgb4_ofld_send(cdev->lldi->ports[0], skb);
+}
+
+static struct sk_buff *alloc_ctrl_skb(struct sk_buff *skb, int len)
+{
+ if (likely(skb && !skb_shared(skb) && !skb_cloned(skb))) {
+ __skb_trim(skb, 0);
+ refcount_add(2, &skb->users);
+ } else {
+ skb = alloc_skb(len, GFP_KERNEL | __GFP_NOFAIL);
+ }
+ return skb;
+}
+
+static void chtls_send_abort(struct sock *sk, int mode, struct sk_buff *skb)
+{
+ struct cpl_abort_req *req;
+ struct chtls_sock *csk;
+ struct tcp_sock *tp;
+
+ csk = rcu_dereference_sk_user_data(sk);
+ tp = tcp_sk(sk);
+
+ if (!skb)
+ skb = alloc_ctrl_skb(csk->txdata_skb_cache, sizeof(*req));
+
+ req = (struct cpl_abort_req *)skb_put(skb, sizeof(*req));
+ INIT_TP_WR_CPL(req, CPL_ABORT_REQ, csk->tid);
+ skb_set_queue_mapping(skb, (csk->txq_idx << 1) | CPL_PRIORITY_DATA);
+ req->rsvd0 = htonl(tp->snd_nxt);
+ req->rsvd1 = !csk_flag_nochk(csk, CSK_TX_DATA_SENT);
+ req->cmd = mode;
+ t4_set_arp_err_handler(skb, csk->cdev, abort_arp_failure);
+ send_or_defer(sk, tp, skb, mode == CPL_ABORT_SEND_RST);
+}
+
+static void chtls_send_reset(struct sock *sk, int mode, struct sk_buff *skb)
+{
+ struct chtls_sock *csk = rcu_dereference_sk_user_data(sk);
+
+ if (unlikely(csk_flag_nochk(csk, CSK_ABORT_SHUTDOWN) ||
+ !csk->cdev)) {
+ if (sk->sk_state == TCP_SYN_RECV)
+ csk_set_flag(csk, CSK_RST_ABORTED);
+ goto out;
+ }
+
+ if (!csk_flag_nochk(csk, CSK_TX_DATA_SENT)) {
+ struct tcp_sock *tp = tcp_sk(sk);
+
+ if (send_tx_flowc_wr(sk, 0, tp->snd_nxt, tp->rcv_nxt) < 0)
+ WARN_ONCE(1, "send tx flowc error");
+ csk_set_flag(csk, CSK_TX_DATA_SENT);
+ }
+
+ csk_set_flag(csk, CSK_ABORT_RPL_PENDING);
+ chtls_purge_write_queue(sk);
+
+ csk_set_flag(csk, CSK_ABORT_SHUTDOWN);
+ if (sk->sk_state != TCP_SYN_RECV)
+ chtls_send_abort(sk, mode, skb);
+ else
+ goto out;
+
+ return;
+out:
+ if (skb)
+ kfree_skb(skb);
+}
+
+static void release_tcp_port(struct sock *sk)
+{
+ if (inet_csk(sk)->icsk_bind_hash)
+ inet_put_port(sk);
+}
+
+static void tcp_uncork(struct sock *sk)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+
+ if (tp->nonagle & TCP_NAGLE_CORK) {
+ tp->nonagle &= ~TCP_NAGLE_CORK;
+ chtls_tcp_push(sk, 0);
+ }
+}
+
+static void chtls_close_conn(struct sock *sk)
+{
+ struct cpl_close_con_req *req;
+ struct chtls_sock *csk;
+ struct sk_buff *skb;
+ unsigned int tid;
+ unsigned int len;
+
+ len = roundup(sizeof(struct cpl_close_con_req), 16);
+ csk = rcu_dereference_sk_user_data(sk);
+ tid = csk->tid;
+
+ skb = alloc_skb(len, GFP_KERNEL | __GFP_NOFAIL);
+ req = (struct cpl_close_con_req *)__skb_put(skb, len);
+ memset(req, 0, len);
+ req->wr.wr_hi = htonl(FW_WR_OP_V(FW_TP_WR) |
+ FW_WR_IMMDLEN_V(sizeof(*req) -
+ sizeof(req->wr)));
+ req->wr.wr_mid = htonl(FW_WR_LEN16_V(DIV_ROUND_UP(sizeof(*req), 16)) |
+ FW_WR_FLOWID_V(tid));
+
+ OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_CLOSE_CON_REQ, tid));
+
+ tcp_uncork(sk);
+ skb_entail(sk, skb, ULPCB_FLAG_NO_HDR | ULPCB_FLAG_NO_APPEND);
+ if (sk->sk_state != TCP_SYN_SENT)
+ chtls_push_frames(csk, 1);
+}
+
+/*
+ * Perform a state transition during close and return the actions indicated
+ * for the transition. Do not make this function inline, the main reason
+ * it exists at all is to avoid multiple inlining of tcp_set_state.
+ */
+static int make_close_transition(struct sock *sk)
+{
+ int next = (int)new_state[sk->sk_state];
+
+ tcp_set_state(sk, next & TCP_STATE_MASK);
+ return next & TCP_ACTION_FIN;
+}
+
+void chtls_close(struct sock *sk, long timeout)
+{
+ int data_lost, prev_state;
+ struct chtls_sock *csk;
+
+ csk = rcu_dereference_sk_user_data(sk);
+
+ lock_sock(sk);
+ sk->sk_shutdown |= SHUTDOWN_MASK;
+
+ data_lost = skb_queue_len(&sk->sk_receive_queue);
+ data_lost |= skb_queue_len(&csk->tlshws.sk_recv_queue);
+ chtls_purge_recv_queue(sk);
+ chtls_purge_receive_queue(sk);
+
+ if (sk->sk_state == TCP_CLOSE) {
+ goto wait;
+ } else if (data_lost || sk->sk_state == TCP_SYN_SENT) {
+ chtls_send_reset(sk, CPL_ABORT_SEND_RST, NULL);
+ release_tcp_port(sk);
+ goto unlock;
+ } else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) {
+ sk->sk_prot->disconnect(sk, 0);
+ } else if (make_close_transition(sk)) {
+ chtls_close_conn(sk);
+ }
+wait:
+ if (timeout)
+ sk_stream_wait_close(sk, timeout);
+
+unlock:
+ prev_state = sk->sk_state;
+ sock_hold(sk);
+ sock_orphan(sk);
+
+ release_sock(sk);
+
+ local_bh_disable();
+ bh_lock_sock(sk);
+
+ if (prev_state != TCP_CLOSE && sk->sk_state == TCP_CLOSE)
+ goto out;
+
+ if (sk->sk_state == TCP_FIN_WAIT2 && tcp_sk(sk)->linger2 < 0 &&
+ !csk_flag(sk, CSK_ABORT_SHUTDOWN)) {
+ struct sk_buff *skb;
+
+ skb = alloc_skb(sizeof(struct cpl_abort_req), GFP_ATOMIC);
+ if (skb)
+ chtls_send_reset(sk, CPL_ABORT_SEND_RST, skb);
+ }
+
+ if (sk->sk_state == TCP_CLOSE)
+ inet_csk_destroy_sock(sk);
+
+out:
+ bh_unlock_sock(sk);
+ local_bh_enable();
+ sock_put(sk);
+}
+
+/*
+ * Wait until a socket enters on of the given states.
+ */
+static int wait_for_states(struct sock *sk, unsigned int states)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ struct socket_wq _sk_wq;
+ long current_timeo;
+ int err = 0;
+
+ current_timeo = 200;
+
+ /*
+ * We want this to work even when there's no associated struct socket.
+ * In that case we provide a temporary wait_queue_head_t.
+ */
+ if (!sk->sk_wq) {
+ init_waitqueue_head(&_sk_wq.wait);
+ _sk_wq.fasync_list = NULL;
+ init_rcu_head_on_stack(&_sk_wq.rcu);
+ RCU_INIT_POINTER(sk->sk_wq, &_sk_wq);
+ }
+
+ add_wait_queue(sk_sleep(sk), &wait);
+ while (!sk_in_state(sk, states)) {
+ if (!current_timeo) {
+ err = -EBUSY;
+ break;
+ }
+ if (signal_pending(current)) {
+ err = sock_intr_errno(current_timeo);
+ break;
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ release_sock(sk);
+ if (!sk_in_state(sk, states))
+ current_timeo = schedule_timeout(current_timeo);
+ __set_current_state(TASK_RUNNING);
+ lock_sock(sk);
+ }
+ remove_wait_queue(sk_sleep(sk), &wait);
+
+ if (rcu_dereference(sk->sk_wq) == &_sk_wq)
+ sk->sk_wq = NULL;
+ return err;
+}
+
+int chtls_disconnect(struct sock *sk, int flags)
+{
+ struct chtls_sock *csk;
+ struct tcp_sock *tp;
+ int err;
+
+ tp = tcp_sk(sk);
+ csk = rcu_dereference_sk_user_data(sk);
+ chtls_purge_recv_queue(sk);
+ chtls_purge_receive_queue(sk);
+ chtls_purge_write_queue(sk);
+
+ if (sk->sk_state != TCP_CLOSE) {
+ sk->sk_err = ECONNRESET;
+ chtls_send_reset(sk, CPL_ABORT_SEND_RST, NULL);
+ err = wait_for_states(sk, TCPF_CLOSE);
+ if (err)
+ return err;
+ }
+ chtls_purge_recv_queue(sk);
+ chtls_purge_receive_queue(sk);
+ tp->max_window = 0xFFFF << (tp->rx_opt.snd_wscale);
+ return tcp_disconnect(sk, flags);
+}
+
+#define SHUTDOWN_ELIGIBLE_STATE (TCPF_ESTABLISHED | \
+ TCPF_SYN_RECV | TCPF_CLOSE_WAIT)
+void chtls_shutdown(struct sock *sk, int how)
+{
+ if ((how & SEND_SHUTDOWN) &&
+ sk_in_state(sk, SHUTDOWN_ELIGIBLE_STATE) &&
+ make_close_transition(sk))
+ chtls_close_conn(sk);
+}
+
+void chtls_destroy_sock(struct sock *sk)
+{
+ struct chtls_sock *csk;
+
+ csk = rcu_dereference_sk_user_data(sk);
+ chtls_purge_recv_queue(sk);
+ csk->ulp_mode = ULP_MODE_NONE;
+ chtls_purge_write_queue(sk);
+ free_tls_keyid(sk);
+ kref_put(&csk->kref, chtls_sock_release);
+ sk->sk_prot = &tcp_prot;
+ sk->sk_prot->destroy(sk);
+}
+
+static void reset_listen_child(struct sock *child)
+{
+ struct chtls_sock *csk = rcu_dereference_sk_user_data(child);
+ struct sk_buff *skb;
+
+ skb = alloc_ctrl_skb(csk->txdata_skb_cache,
+ sizeof(struct cpl_abort_req));
+
+ chtls_send_reset(child, CPL_ABORT_SEND_RST, skb);
+ sock_orphan(child);
+ INC_ORPHAN_COUNT(child);
+ if (child->sk_state == TCP_CLOSE)
+ inet_csk_destroy_sock(child);
+}
+
+static void chtls_disconnect_acceptq(struct sock *listen_sk)
+{
+ struct request_sock **pprev;
+
+ pprev = ACCEPT_QUEUE(listen_sk);
+ while (*pprev) {
+ struct request_sock *req = *pprev;
+
+ if (req->rsk_ops == &chtls_rsk_ops) {
+ struct sock *child = req->sk;
+
+ *pprev = req->dl_next;
+ sk_acceptq_removed(listen_sk);
+ reqsk_put(req);
+ sock_hold(child);
+ local_bh_disable();
+ bh_lock_sock(child);
+ release_tcp_port(child);
+ reset_listen_child(child);
+ bh_unlock_sock(child);
+ local_bh_enable();
+ sock_put(child);
+ } else {
+ pprev = &req->dl_next;
+ }
+ }
+}
+
+static int listen_hashfn(const struct sock *sk)
+{
+ return ((unsigned long)sk >> 10) & (LISTEN_INFO_HASH_SIZE - 1);
+}
+
+static struct listen_info *listen_hash_add(struct chtls_dev *cdev,
+ struct sock *sk,
+ unsigned int stid)
+{
+ struct listen_info *p = kmalloc(sizeof(*p), GFP_KERNEL);
+
+ if (p) {
+ int key = listen_hashfn(sk);
+
+ p->sk = sk;
+ p->stid = stid;
+ spin_lock(&cdev->listen_lock);
+ p->next = cdev->listen_hash_tab[key];
+ cdev->listen_hash_tab[key] = p;
+ spin_unlock(&cdev->listen_lock);
+ }
+ return p;
+}
+
+static int listen_hash_find(struct chtls_dev *cdev,
+ struct sock *sk)
+{
+ struct listen_info *p;
+ int stid = -1;
+ int key;
+
+ key = listen_hashfn(sk);
+
+ spin_lock(&cdev->listen_lock);
+ for (p = cdev->listen_hash_tab[key]; p; p = p->next)
+ if (p->sk == sk) {
+ stid = p->stid;
+ break;
+ }
+ spin_unlock(&cdev->listen_lock);
+ return stid;
+}
+
+static int listen_hash_del(struct chtls_dev *cdev,
+ struct sock *sk)
+{
+ struct listen_info *p, **prev;
+ int stid = -1;
+ int key;
+
+ key = listen_hashfn(sk);
+ prev = &cdev->listen_hash_tab[key];
+
+ spin_lock(&cdev->listen_lock);
+ for (p = *prev; p; prev = &p->next, p = p->next)
+ if (p->sk == sk) {
+ stid = p->stid;
+ *prev = p->next;
+ kfree(p);
+ break;
+ }
+ spin_unlock(&cdev->listen_lock);
+ return stid;
+}
+
+static void cleanup_syn_rcv_conn(struct sock *child, struct sock *parent)
+{
+ struct request_sock *req;
+ struct chtls_sock *csk;
+
+ csk = rcu_dereference_sk_user_data(child);
+ req = csk->passive_reap_next;
+
+ reqsk_queue_removed(&inet_csk(parent)->icsk_accept_queue, req);
+ __skb_unlink((struct sk_buff *)&csk->synq, &csk->listen_ctx->synq);
+ chtls_reqsk_free(req);
+ csk->passive_reap_next = NULL;
+}
+
+static void chtls_reset_synq(struct listen_ctx *listen_ctx)
+{
+ struct sock *listen_sk = listen_ctx->lsk;
+
+ while (!skb_queue_empty(&listen_ctx->synq)) {
+ struct chtls_sock *csk =
+ container_of((struct synq *)__skb_dequeue
+ (&listen_ctx->synq), struct chtls_sock, synq);
+ struct sock *child = csk->sk;
+
+ cleanup_syn_rcv_conn(child, listen_sk);
+ sock_hold(child);
+ local_bh_disable();
+ bh_lock_sock(child);
+ release_tcp_port(child);
+ reset_listen_child(child);
+ bh_unlock_sock(child);
+ local_bh_enable();
+ sock_put(child);
+ }
+}
+
+int chtls_listen_start(struct chtls_dev *cdev, struct sock *sk)
+{
+ struct net_device *ndev;
+ struct listen_ctx *ctx;
+ struct adapter *adap;
+ struct port_info *pi;
+ int stid;
+ int ret;
+
+ if (sk->sk_family != PF_INET)
+ return -EAGAIN;
+
+ rcu_read_lock();
+ ndev = chtls_ipv4_netdev(cdev, sk);
+ rcu_read_unlock();
+ if (!ndev)
+ return -EBADF;
+
+ pi = netdev_priv(ndev);
+ adap = pi->adapter;
+ if (!(adap->flags & FULL_INIT_DONE))
+ return -EBADF;
+
+ if (listen_hash_find(cdev, sk) >= 0) /* already have it */
+ return -EADDRINUSE;
+
+ ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ __module_get(THIS_MODULE);
+ ctx->lsk = sk;
+ ctx->cdev = cdev;
+ ctx->state = T4_LISTEN_START_PENDING;
+ skb_queue_head_init(&ctx->synq);
+
+ stid = cxgb4_alloc_stid(cdev->tids, sk->sk_family, ctx);
+ if (stid < 0)
+ goto free_ctx;
+
+ sock_hold(sk);
+ if (!listen_hash_add(cdev, sk, stid))
+ goto free_stid;
+
+ ret = cxgb4_create_server(ndev, stid,
+ inet_sk(sk)->inet_rcv_saddr,
+ inet_sk(sk)->inet_sport, 0,
+ cdev->lldi->rxq_ids[0]);
+ if (ret > 0)
+ ret = net_xmit_errno(ret);
+ if (ret)
+ goto del_hash;
+ return 0;
+del_hash:
+ listen_hash_del(cdev, sk);
+free_stid:
+ cxgb4_free_stid(cdev->tids, stid, sk->sk_family);
+ sock_put(sk);
+free_ctx:
+ kfree(ctx);
+ module_put(THIS_MODULE);
+ return -EBADF;
+}
+
+void chtls_listen_stop(struct chtls_dev *cdev, struct sock *sk)
+{
+ struct listen_ctx *listen_ctx;
+ int stid;
+
+ stid = listen_hash_del(cdev, sk);
+ if (stid < 0)
+ return;
+
+ listen_ctx = (struct listen_ctx *)lookup_stid(cdev->tids, stid);
+ chtls_reset_synq(listen_ctx);
+
+ cxgb4_remove_server(cdev->lldi->ports[0], stid,
+ cdev->lldi->rxq_ids[0], 0);
+ chtls_disconnect_acceptq(sk);
+}
+
+static int chtls_pass_open_rpl(struct chtls_dev *cdev, struct sk_buff *skb)
+{
+ struct cpl_pass_open_rpl *rpl = cplhdr(skb) + RSS_HDR;
+ unsigned int stid = GET_TID(rpl);
+ struct listen_ctx *listen_ctx;
+
+ listen_ctx = (struct listen_ctx *)lookup_stid(cdev->tids, stid);
+ if (!listen_ctx)
+ return CPL_RET_BUF_DONE;
+
+ if (listen_ctx->state == T4_LISTEN_START_PENDING) {
+ listen_ctx->state = T4_LISTEN_STARTED;
+ return CPL_RET_BUF_DONE;
+ }
+
+ if (rpl->status != CPL_ERR_NONE) {
+ pr_info("Unexpected PASS_OPEN_RPL status %u for STID %u\n",
+ rpl->status, stid);
+ return CPL_RET_BUF_DONE;
+ }
+ cxgb4_free_stid(cdev->tids, stid, listen_ctx->lsk->sk_family);
+ sock_put(listen_ctx->lsk);
+ kfree(listen_ctx);
+ module_put(THIS_MODULE);
+
+ return 0;
+}
+
+static int chtls_close_listsrv_rpl(struct chtls_dev *cdev, struct sk_buff *skb)
+{
+ struct cpl_close_listsvr_rpl *rpl = cplhdr(skb) + RSS_HDR;
+ struct listen_ctx *listen_ctx;
+ unsigned int stid;
+ void *data;
+
+ stid = GET_TID(rpl);
+ data = lookup_stid(cdev->tids, stid);
+ listen_ctx = (struct listen_ctx *)data;
+
+ if (rpl->status != CPL_ERR_NONE) {
+ pr_info("Unexpected CLOSE_LISTSRV_RPL status %u for STID %u\n",
+ rpl->status, stid);
+ return CPL_RET_BUF_DONE;
+ }
+
+ cxgb4_free_stid(cdev->tids, stid, listen_ctx->lsk->sk_family);
+ sock_put(listen_ctx->lsk);
+ kfree(listen_ctx);
+ module_put(THIS_MODULE);
+
+ return 0;
+}
+
+static void chtls_release_resources(struct sock *sk)
+{
+ struct chtls_sock *csk = rcu_dereference_sk_user_data(sk);
+ struct chtls_dev *cdev = csk->cdev;
+ unsigned int tid = csk->tid;
+ struct tid_info *tids;
+
+ if (!cdev)
+ return;
+
+ tids = cdev->tids;
+ kfree_skb(csk->txdata_skb_cache);
+ csk->txdata_skb_cache = NULL;
+
+ if (csk->l2t_entry) {
+ cxgb4_l2t_release(csk->l2t_entry);
+ csk->l2t_entry = NULL;
+ }
+
+ cxgb4_remove_tid(tids, csk->port_id, tid, sk->sk_family);
+ sock_put(sk);
+}
+
+static void chtls_conn_done(struct sock *sk)
+{
+ if (sock_flag(sk, SOCK_DEAD))
+ chtls_purge_receive_queue(sk);
+ sk_wakeup_sleepers(sk, 0);
+ tcp_done(sk);
+}
+
+static void do_abort_syn_rcv(struct sock *child, struct sock *parent)
+{
+ /*
+ * If the server is still open we clean up the child connection,
+ * otherwise the server already did the clean up as it was purging
+ * its SYN queue and the skb was just sitting in its backlog.
+ */
+ if (likely(parent->sk_state == TCP_LISTEN)) {
+ cleanup_syn_rcv_conn(child, parent);
+ /* Without the below call to sock_orphan,
+ * we leak the socket resource with syn_flood test
+ * as inet_csk_destroy_sock will not be called
+ * in tcp_done since SOCK_DEAD flag is not set.
+ * Kernel handles this differently where new socket is
+ * created only after 3 way handshake is done.
+ */
+ sock_orphan(child);
+ percpu_counter_inc((child)->sk_prot->orphan_count);
+ chtls_release_resources(child);
+ chtls_conn_done(child);
+ } else {
+ if (csk_flag(child, CSK_RST_ABORTED)) {
+ chtls_release_resources(child);
+ chtls_conn_done(child);
+ }
+ }
+}
+
+static void pass_open_abort(struct sock *child, struct sock *parent,
+ struct sk_buff *skb)
+{
+ do_abort_syn_rcv(child, parent);
+ kfree_skb(skb);
+}
+
+static void bl_pass_open_abort(struct sock *lsk, struct sk_buff *skb)
+{
+ pass_open_abort(skb->sk, lsk, skb);
+}
+
+static void chtls_pass_open_arp_failure(struct sock *sk,
+ struct sk_buff *skb)
+{
+ const struct request_sock *oreq;
+ struct chtls_sock *csk;
+ struct chtls_dev *cdev;
+ struct sock *parent;
+ void *data;
+
+ csk = rcu_dereference_sk_user_data(sk);
+ cdev = csk->cdev;
+
+ /*
+ * If the connection is being aborted due to the parent listening
+ * socket going away there's nothing to do, the ABORT_REQ will close
+ * the connection.
+ */
+ if (csk_flag(sk, CSK_ABORT_RPL_PENDING)) {
+ kfree_skb(skb);
+ return;
+ }
+
+ oreq = csk->passive_reap_next;
+ data = lookup_stid(cdev->tids, oreq->ts_recent);
+ parent = ((struct listen_ctx *)data)->lsk;
+
+ bh_lock_sock(parent);
+ if (!sock_owned_by_user(parent)) {
+ pass_open_abort(sk, parent, skb);
+ } else {
+ BLOG_SKB_CB(skb)->backlog_rcv = bl_pass_open_abort;
+ __sk_add_backlog(parent, skb);
+ }
+ bh_unlock_sock(parent);
+}
+
+static void chtls_accept_rpl_arp_failure(void *handle,
+ struct sk_buff *skb)
+{
+ struct sock *sk = (struct sock *)handle;
+
+ sock_hold(sk);
+ process_cpl_msg(chtls_pass_open_arp_failure, sk, skb);
+ sock_put(sk);
+}
+
+static unsigned int chtls_select_mss(const struct chtls_sock *csk,
+ unsigned int pmtu,
+ struct cpl_pass_accept_req *req)
+{
+ struct chtls_dev *cdev;
+ struct dst_entry *dst;
+ unsigned int tcpoptsz;
+ unsigned int iphdrsz;
+ unsigned int mtu_idx;
+ struct tcp_sock *tp;
+ unsigned int mss;
+ struct sock *sk;
+
+ mss = ntohs(req->tcpopt.mss);
+ sk = csk->sk;
+ dst = __sk_dst_get(sk);
+ cdev = csk->cdev;
+ tp = tcp_sk(sk);
+ tcpoptsz = 0;
+
+ iphdrsz = sizeof(struct iphdr) + sizeof(struct tcphdr);
+ if (req->tcpopt.tstamp)
+ tcpoptsz += round_up(TCPOLEN_TIMESTAMP, 4);
+
+ tp->advmss = dst_metric_advmss(dst);
+ if (USER_MSS(tp) && tp->advmss > USER_MSS(tp))
+ tp->advmss = USER_MSS(tp);
+ if (tp->advmss > pmtu - iphdrsz)
+ tp->advmss = pmtu - iphdrsz;
+ if (mss && tp->advmss > mss)
+ tp->advmss = mss;
+
+ tp->advmss = cxgb4_best_aligned_mtu(cdev->lldi->mtus,
+ iphdrsz + tcpoptsz,
+ tp->advmss - tcpoptsz,
+ 8, &mtu_idx);
+ tp->advmss -= iphdrsz;
+
+ inet_csk(sk)->icsk_pmtu_cookie = pmtu;
+ return mtu_idx;
+}
+
+static unsigned int select_rcv_wnd(struct chtls_sock *csk)
+{
+ unsigned int rcvwnd;
+ unsigned int wnd;
+ struct sock *sk;
+
+ sk = csk->sk;
+ wnd = tcp_full_space(sk);
+
+ if (wnd < MIN_RCV_WND)
+ wnd = MIN_RCV_WND;
+
+ rcvwnd = MAX_RCV_WND;
+
+ csk_set_flag(csk, CSK_UPDATE_RCV_WND);
+ return min(wnd, rcvwnd);
+}
+
+static unsigned int select_rcv_wscale(int space, int wscale_ok, int win_clamp)
+{
+ int wscale = 0;
+
+ if (space > MAX_RCV_WND)
+ space = MAX_RCV_WND;
+ if (win_clamp && win_clamp < space)
+ space = win_clamp;
+
+ if (wscale_ok) {
+ while (wscale < 14 && (65535 << wscale) < space)
+ wscale++;
+ }
+ return wscale;
+}
+
+static void chtls_pass_accept_rpl(struct sk_buff *skb,
+ struct cpl_pass_accept_req *req,
+ unsigned int tid)
+
+{
+ struct cpl_t5_pass_accept_rpl *rpl5;
+ struct cxgb4_lld_info *lldi;
+ const struct tcphdr *tcph;
+ const struct tcp_sock *tp;
+ struct chtls_sock *csk;
+ unsigned int len;
+ struct sock *sk;
+ u32 opt2, hlen;
+ u64 opt0;
+
+ sk = skb->sk;
+ tp = tcp_sk(sk);
+ csk = sk->sk_user_data;
+ csk->tid = tid;
+ lldi = csk->cdev->lldi;
+ len = roundup(sizeof(*rpl5), 16);
+
+ rpl5 = __skb_put_zero(skb, len);
+ INIT_TP_WR(rpl5, tid);
+
+ OPCODE_TID(rpl5) = cpu_to_be32(MK_OPCODE_TID(CPL_PASS_ACCEPT_RPL,
+ csk->tid));
+ csk->mtu_idx = chtls_select_mss(csk, dst_mtu(__sk_dst_get(sk)),
+ req);
+ opt0 = TCAM_BYPASS_F |
+ WND_SCALE_V((tp)->rx_opt.rcv_wscale) |
+ MSS_IDX_V(csk->mtu_idx) |
+ L2T_IDX_V(csk->l2t_entry->idx) |
+ NAGLE_V(!(tp->nonagle & TCP_NAGLE_OFF)) |
+ TX_CHAN_V(csk->tx_chan) |
+ SMAC_SEL_V(csk->smac_idx) |
+ DSCP_V(csk->tos >> 2) |
+ ULP_MODE_V(ULP_MODE_TLS) |
+ RCV_BUFSIZ_V(min(tp->rcv_wnd >> 10, RCV_BUFSIZ_M));
+
+ opt2 = RX_CHANNEL_V(0) |
+ RSS_QUEUE_VALID_F | RSS_QUEUE_V(csk->rss_qid);
+
+ if (!is_t5(lldi->adapter_type))
+ opt2 |= RX_FC_DISABLE_F;
+ if (req->tcpopt.tstamp)
+ opt2 |= TSTAMPS_EN_F;
+ if (req->tcpopt.sack)
+ opt2 |= SACK_EN_F;
+ hlen = ntohl(req->hdr_len);
+
+ tcph = (struct tcphdr *)((u8 *)(req + 1) +
+ T6_ETH_HDR_LEN_G(hlen) + T6_IP_HDR_LEN_G(hlen));
+ if (tcph->ece && tcph->cwr)
+ opt2 |= CCTRL_ECN_V(1);
+ opt2 |= CONG_CNTRL_V(CONG_ALG_NEWRENO);
+ opt2 |= T5_ISS_F;
+ opt2 |= T5_OPT_2_VALID_F;
+ rpl5->opt0 = cpu_to_be64(opt0);
+ rpl5->opt2 = cpu_to_be32(opt2);
+ rpl5->iss = cpu_to_be32((prandom_u32() & ~7UL) - 1);
+ set_wr_txq(skb, CPL_PRIORITY_SETUP, csk->port_id);
+ t4_set_arp_err_handler(skb, sk, chtls_accept_rpl_arp_failure);
+ cxgb4_l2t_send(csk->egress_dev, skb, csk->l2t_entry);
+}
+
+static void inet_inherit_port(struct inet_hashinfo *hash_info,
+ struct sock *lsk, struct sock *newsk)
+{
+ local_bh_disable();
+ __inet_inherit_port(lsk, newsk);
+ local_bh_enable();
+}
+
+static int chtls_backlog_rcv(struct sock *sk, struct sk_buff *skb)
+{
+ if (skb->protocol) {
+ kfree_skb(skb);
+ return 0;
+ }
+ BLOG_SKB_CB(skb)->backlog_rcv(sk, skb);
+ return 0;
+}
+
+static struct sock *chtls_recv_sock(struct sock *lsk,
+ struct request_sock *oreq,
+ void *network_hdr,
+ const struct cpl_pass_accept_req *req,
+ struct chtls_dev *cdev)
+{
+ const struct tcphdr *tcph;
+ struct inet_sock *newinet;
+ const struct iphdr *iph;
+ struct net_device *ndev;
+ struct chtls_sock *csk;
+ struct dst_entry *dst;
+ struct neighbour *n;
+ struct tcp_sock *tp;
+ struct sock *newsk;
+ u16 port_id;
+ int rxq_idx;
+ int step;
+
+ iph = (const struct iphdr *)network_hdr;
+ newsk = tcp_create_openreq_child(lsk, oreq, cdev->askb);
+ if (!newsk)
+ goto free_oreq;
+
+ dst = inet_csk_route_child_sock(lsk, newsk, oreq);
+ if (!dst)
+ goto free_sk;
+
+ tcph = (struct tcphdr *)(iph + 1);
+ n = dst_neigh_lookup(dst, &iph->saddr);
+ if (!n)
+ goto free_sk;
+
+ ndev = n->dev;
+ if (!ndev)
+ goto free_dst;
+ port_id = cxgb4_port_idx(ndev);
+
+ csk = chtls_sock_create(cdev);
+ if (!csk)
+ goto free_dst;
+
+ csk->l2t_entry = cxgb4_l2t_get(cdev->lldi->l2t, n, ndev, 0);
+ if (!csk->l2t_entry)
+ goto free_csk;
+
+ newsk->sk_user_data = csk;
+ newsk->sk_backlog_rcv = chtls_backlog_rcv;
+
+ tp = tcp_sk(newsk);
+ newinet = inet_sk(newsk);
+
+ newinet->inet_daddr = iph->saddr;
+ newinet->inet_rcv_saddr = iph->daddr;
+ newinet->inet_saddr = iph->daddr;
+
+ oreq->ts_recent = PASS_OPEN_TID_G(ntohl(req->tos_stid));
+ sk_setup_caps(newsk, dst);
+ csk->sk = newsk;
+ csk->passive_reap_next = oreq;
+ csk->tx_chan = cxgb4_port_chan(ndev);
+ csk->port_id = port_id;
+ csk->egress_dev = ndev;
+ csk->tos = PASS_OPEN_TOS_G(ntohl(req->tos_stid));
+ csk->ulp_mode = ULP_MODE_TLS;
+ step = cdev->lldi->nrxq / cdev->lldi->nchan;
+ csk->rss_qid = cdev->lldi->rxq_ids[port_id * step];
+ rxq_idx = port_id * step;
+ csk->txq_idx = (rxq_idx < cdev->lldi->ntxq) ? rxq_idx :
+ port_id * step;
+ csk->sndbuf = newsk->sk_sndbuf;
+ csk->smac_idx = cxgb4_tp_smt_idx(cdev->lldi->adapter_type,
+ cxgb4_port_viid(ndev));
+ tp->rcv_wnd = select_rcv_wnd(csk);
+ RCV_WSCALE(tp) = select_rcv_wscale(tcp_full_space(newsk),
+ WSCALE_OK(tp),
+ tp->window_clamp);
+ neigh_release(n);
+ inet_inherit_port(&tcp_hashinfo, lsk, newsk);
+ csk_set_flag(csk, CSK_CONN_INLINE);
+ bh_unlock_sock(newsk); /* tcp_create_openreq_child ->sk_clone_lock */
+
+ return newsk;
+free_csk:
+ chtls_sock_release(&csk->kref);
+free_dst:
+ dst_release(dst);
+free_sk:
+ inet_csk_prepare_forced_close(newsk);
+ tcp_done(newsk);
+free_oreq:
+ chtls_reqsk_free(oreq);
+ return NULL;
+}
+
+/*
+ * Populate a TID_RELEASE WR. The skb must be already propely sized.
+ */
+static void mk_tid_release(struct sk_buff *skb,
+ unsigned int chan, unsigned int tid)
+{
+ struct cpl_tid_release *req;
+ unsigned int len;
+
+ len = roundup(sizeof(struct cpl_tid_release), 16);
+ req = (struct cpl_tid_release *)__skb_put(skb, len);
+ memset(req, 0, len);
+ set_wr_txq(skb, CPL_PRIORITY_SETUP, chan);
+ INIT_TP_WR_CPL(req, CPL_TID_RELEASE, tid);
+}
+
+static int chtls_get_module(struct sock *sk)
+{
+ struct inet_connection_sock *icsk = inet_csk(sk);
+
+ if (!try_module_get(icsk->icsk_ulp_ops->owner))
+ return -1;
+
+ return 0;
+}
+
+static void chtls_pass_accept_request(struct sock *sk,
+ struct sk_buff *skb)
+{
+ struct cpl_t5_pass_accept_rpl *rpl;
+ struct cpl_pass_accept_req *req;
+ struct listen_ctx *listen_ctx;
+ struct request_sock *oreq;
+ struct sk_buff *reply_skb;
+ struct chtls_sock *csk;
+ struct chtls_dev *cdev;
+ struct tcphdr *tcph;
+ struct sock *newsk;
+ struct ethhdr *eh;
+ struct iphdr *iph;
+ void *network_hdr;
+ unsigned int stid;
+ unsigned int len;
+ unsigned int tid;
+
+ req = cplhdr(skb) + RSS_HDR;
+ tid = GET_TID(req);
+ cdev = BLOG_SKB_CB(skb)->cdev;
+ newsk = lookup_tid(cdev->tids, tid);
+ stid = PASS_OPEN_TID_G(ntohl(req->tos_stid));
+ if (newsk) {
+ pr_info("tid (%d) already in use\n", tid);
+ return;
+ }
+
+ len = roundup(sizeof(*rpl), 16);
+ reply_skb = alloc_skb(len, GFP_ATOMIC);
+ if (!reply_skb) {
+ cxgb4_remove_tid(cdev->tids, 0, tid, sk->sk_family);
+ kfree_skb(skb);
+ return;
+ }
+
+ if (sk->sk_state != TCP_LISTEN)
+ goto reject;
+
+ if (inet_csk_reqsk_queue_is_full(sk))
+ goto reject;
+
+ if (sk_acceptq_is_full(sk))
+ goto reject;
+
+ oreq = inet_reqsk_alloc(&chtls_rsk_ops, sk, true);
+ if (!oreq)
+ goto reject;
+
+ oreq->rsk_rcv_wnd = 0;
+ oreq->rsk_window_clamp = 0;
+ oreq->cookie_ts = 0;
+ oreq->mss = 0;
+ oreq->ts_recent = 0;
+
+ eh = (struct ethhdr *)(req + 1);
+ iph = (struct iphdr *)(eh + 1);
+ if (iph->version != 0x4)
+ goto free_oreq;
+
+ network_hdr = (void *)(eh + 1);
+ tcph = (struct tcphdr *)(iph + 1);
+
+ tcp_rsk(oreq)->tfo_listener = false;
+ tcp_rsk(oreq)->rcv_isn = ntohl(tcph->seq);
+ chtls_set_req_port(oreq, tcph->source, tcph->dest);
+ inet_rsk(oreq)->ecn_ok = 0;
+ chtls_set_req_addr(oreq, iph->daddr, iph->saddr);
+ if (req->tcpopt.wsf <= 14) {
+ inet_rsk(oreq)->wscale_ok = 1;
+ inet_rsk(oreq)->snd_wscale = req->tcpopt.wsf;
+ }
+ inet_rsk(oreq)->ir_iif = sk->sk_bound_dev_if;
+
+ newsk = chtls_recv_sock(sk, oreq, network_hdr, req, cdev);
+ if (!newsk)
+ goto reject;
+
+ if (chtls_get_module(newsk))
+ goto reject;
+ inet_csk_reqsk_queue_added(sk);
+ reply_skb->sk = newsk;
+ chtls_install_cpl_ops(newsk);
+ cxgb4_insert_tid(cdev->tids, newsk, tid, newsk->sk_family);
+ csk = rcu_dereference_sk_user_data(newsk);
+ listen_ctx = (struct listen_ctx *)lookup_stid(cdev->tids, stid);
+ csk->listen_ctx = listen_ctx;
+ __skb_queue_tail(&listen_ctx->synq, (struct sk_buff *)&csk->synq);
+ chtls_pass_accept_rpl(reply_skb, req, tid);
+ kfree_skb(skb);
+ return;
+
+free_oreq:
+ chtls_reqsk_free(oreq);
+reject:
+ mk_tid_release(reply_skb, 0, tid);
+ cxgb4_ofld_send(cdev->lldi->ports[0], reply_skb);
+ kfree_skb(skb);
+}
+
+/*
+ * Handle a CPL_PASS_ACCEPT_REQ message.
+ */
+static int chtls_pass_accept_req(struct chtls_dev *cdev, struct sk_buff *skb)
+{
+ struct cpl_pass_accept_req *req = cplhdr(skb) + RSS_HDR;
+ struct listen_ctx *ctx;
+ unsigned int stid;
+ unsigned int tid;
+ struct sock *lsk;
+ void *data;
+
+ stid = PASS_OPEN_TID_G(ntohl(req->tos_stid));
+ tid = GET_TID(req);
+
+ data = lookup_stid(cdev->tids, stid);
+ if (!data)
+ return 1;
+
+ ctx = (struct listen_ctx *)data;
+ lsk = ctx->lsk;
+
+ if (unlikely(tid >= cdev->tids->ntids)) {
+ pr_info("passive open TID %u too large\n", tid);
+ return 1;
+ }
+
+ BLOG_SKB_CB(skb)->cdev = cdev;
+ process_cpl_msg(chtls_pass_accept_request, lsk, skb);
+ return 0;
+}
+
+/*
+ * Completes some final bits of initialization for just established connections
+ * and changes their state to TCP_ESTABLISHED.
+ *
+ * snd_isn here is the ISN after the SYN, i.e., the true ISN + 1.
+ */
+static void make_established(struct sock *sk, u32 snd_isn, unsigned int opt)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+
+ tp->pushed_seq = snd_isn;
+ tp->write_seq = snd_isn;
+ tp->snd_nxt = snd_isn;
+ tp->snd_una = snd_isn;
+ inet_sk(sk)->inet_id = tp->write_seq ^ jiffies;
+ assign_rxopt(sk, opt);
+
+ if (tp->rcv_wnd > (RCV_BUFSIZ_M << 10))
+ tp->rcv_wup -= tp->rcv_wnd - (RCV_BUFSIZ_M << 10);
+
+ smp_mb();
+ tcp_set_state(sk, TCP_ESTABLISHED);
+}
+
+static void chtls_abort_conn(struct sock *sk, struct sk_buff *skb)
+{
+ struct sk_buff *abort_skb;
+
+ abort_skb = alloc_skb(sizeof(struct cpl_abort_req), GFP_ATOMIC);
+ if (abort_skb)
+ chtls_send_reset(sk, CPL_ABORT_SEND_RST, abort_skb);
+}
+
+static struct sock *reap_list;
+static DEFINE_SPINLOCK(reap_list_lock);
+
+/*
+ * Process the reap list.
+ */
+DECLARE_TASK_FUNC(process_reap_list, task_param)
+{
+ spin_lock_bh(&reap_list_lock);
+ while (reap_list) {
+ struct sock *sk = reap_list;
+ struct chtls_sock *csk = rcu_dereference_sk_user_data(sk);
+
+ reap_list = csk->passive_reap_next;
+ csk->passive_reap_next = NULL;
+ spin_unlock(&reap_list_lock);
+ sock_hold(sk);
+
+ bh_lock_sock(sk);
+ chtls_abort_conn(sk, NULL);
+ sock_orphan(sk);
+ if (sk->sk_state == TCP_CLOSE)
+ inet_csk_destroy_sock(sk);
+ bh_unlock_sock(sk);
+ sock_put(sk);
+ spin_lock(&reap_list_lock);
+ }
+ spin_unlock_bh(&reap_list_lock);
+}
+
+static DECLARE_WORK(reap_task, process_reap_list);
+
+static void add_to_reap_list(struct sock *sk)
+{
+ struct chtls_sock *csk = sk->sk_user_data;
+
+ local_bh_disable();
+ bh_lock_sock(sk);
+ release_tcp_port(sk); /* release the port immediately */
+
+ spin_lock(&reap_list_lock);
+ csk->passive_reap_next = reap_list;
+ reap_list = sk;
+ if (!csk->passive_reap_next)
+ schedule_work(&reap_task);
+ spin_unlock(&reap_list_lock);
+ bh_unlock_sock(sk);
+ local_bh_enable();
+}
+
+static void add_pass_open_to_parent(struct sock *child, struct sock *lsk,
+ struct chtls_dev *cdev)
+{
+ struct request_sock *oreq;
+ struct chtls_sock *csk;
+
+ if (lsk->sk_state != TCP_LISTEN)
+ return;
+
+ csk = child->sk_user_data;
+ oreq = csk->passive_reap_next;
+ csk->passive_reap_next = NULL;
+
+ reqsk_queue_removed(&inet_csk(lsk)->icsk_accept_queue, oreq);
+ __skb_unlink((struct sk_buff *)&csk->synq, &csk->listen_ctx->synq);
+
+ if (sk_acceptq_is_full(lsk)) {
+ chtls_reqsk_free(oreq);
+ add_to_reap_list(child);
+ } else {
+ refcount_set(&oreq->rsk_refcnt, 1);
+ inet_csk_reqsk_queue_add(lsk, oreq, child);
+ lsk->sk_data_ready(lsk);
+ }
+}
+
+static void bl_add_pass_open_to_parent(struct sock *lsk, struct sk_buff *skb)
+{
+ struct sock *child = skb->sk;
+
+ skb->sk = NULL;
+ add_pass_open_to_parent(child, lsk, BLOG_SKB_CB(skb)->cdev);
+ kfree_skb(skb);
+}
+
+static int chtls_pass_establish(struct chtls_dev *cdev, struct sk_buff *skb)
+{
+ struct cpl_pass_establish *req = cplhdr(skb) + RSS_HDR;
+ struct chtls_sock *csk;
+ struct sock *lsk, *sk;
+ unsigned int hwtid;
+
+ hwtid = GET_TID(req);
+ sk = lookup_tid(cdev->tids, hwtid);
+ if (!sk)
+ return (CPL_RET_UNKNOWN_TID | CPL_RET_BUF_DONE);
+
+ bh_lock_sock(sk);
+ if (unlikely(sock_owned_by_user(sk))) {
+ kfree_skb(skb);
+ } else {
+ unsigned int stid;
+ void *data;
+
+ csk = sk->sk_user_data;
+ csk->wr_max_credits = 64;
+ csk->wr_credits = 64;
+ csk->wr_unacked = 0;
+ make_established(sk, ntohl(req->snd_isn), ntohs(req->tcp_opt));
+ stid = PASS_OPEN_TID_G(ntohl(req->tos_stid));
+ sk->sk_state_change(sk);
+ if (unlikely(sk->sk_socket))
+ sk_wake_async(sk, 0, POLL_OUT);
+
+ data = lookup_stid(cdev->tids, stid);
+ lsk = ((struct listen_ctx *)data)->lsk;
+
+ bh_lock_sock(lsk);
+ if (unlikely(skb_queue_empty(&csk->listen_ctx->synq))) {
+ /* removed from synq */
+ bh_unlock_sock(lsk);
+ kfree_skb(skb);
+ goto unlock;
+ }
+
+ if (likely(!sock_owned_by_user(lsk))) {
+ kfree_skb(skb);
+ add_pass_open_to_parent(sk, lsk, cdev);
+ } else {
+ skb->sk = sk;
+ BLOG_SKB_CB(skb)->cdev = cdev;
+ BLOG_SKB_CB(skb)->backlog_rcv =
+ bl_add_pass_open_to_parent;
+ __sk_add_backlog(lsk, skb);
+ }
+ bh_unlock_sock(lsk);
+ }
+unlock:
+ bh_unlock_sock(sk);
+ return 0;
+}
+
+/*
+ * Handle receipt of an urgent pointer.
+ */
+static void handle_urg_ptr(struct sock *sk, u32 urg_seq)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+
+ urg_seq--;
+ if (tp->urg_data && !after(urg_seq, tp->urg_seq))
+ return; /* duplicate pointer */
+
+ sk_send_sigurg(sk);
+ if (tp->urg_seq == tp->copied_seq && tp->urg_data &&
+ !sock_flag(sk, SOCK_URGINLINE) &&
+ tp->copied_seq != tp->rcv_nxt) {
+ struct sk_buff *skb = skb_peek(&sk->sk_receive_queue);
+
+ tp->copied_seq++;
+ if (skb && tp->copied_seq - ULP_SKB_CB(skb)->seq >= skb->len)
+ chtls_free_skb(sk, skb);
+ }
+
+ tp->urg_data = TCP_URG_NOTYET;
+ tp->urg_seq = urg_seq;
+}
+
+static void check_sk_callbacks(struct chtls_sock *csk)
+{
+ struct sock *sk = csk->sk;
+
+ if (unlikely(sk->sk_user_data &&
+ !csk_flag_nochk(csk, CSK_CALLBACKS_CHKD)))
+ csk_set_flag(csk, CSK_CALLBACKS_CHKD);
+}
+
+/*
+ * Handles Rx data that arrives in a state where the socket isn't accepting
+ * new data.
+ */
+static void handle_excess_rx(struct sock *sk, struct sk_buff *skb)
+{
+ if (!csk_flag(sk, CSK_ABORT_SHUTDOWN))
+ chtls_abort_conn(sk, skb);
+
+ kfree_skb(skb);
+}
+
+static void chtls_recv_data(struct sock *sk, struct sk_buff *skb)
+{
+ struct cpl_rx_data *hdr = cplhdr(skb) + RSS_HDR;
+ struct chtls_sock *csk;
+ struct tcp_sock *tp;
+
+ csk = rcu_dereference_sk_user_data(sk);
+ tp = tcp_sk(sk);
+
+ if (unlikely(sk->sk_shutdown & RCV_SHUTDOWN)) {
+ handle_excess_rx(sk, skb);
+ return;
+ }
+
+ ULP_SKB_CB(skb)->seq = ntohl(hdr->seq);
+ ULP_SKB_CB(skb)->psh = hdr->psh;
+ skb_ulp_mode(skb) = ULP_MODE_NONE;
+
+ skb_reset_transport_header(skb);
+ __skb_pull(skb, sizeof(*hdr) + RSS_HDR);
+ if (!skb->data_len)
+ __skb_trim(skb, ntohs(hdr->len));
+
+ if (unlikely(hdr->urg))
+ handle_urg_ptr(sk, tp->rcv_nxt + ntohs(hdr->urg));
+ if (unlikely(tp->urg_data == TCP_URG_NOTYET &&
+ tp->urg_seq - tp->rcv_nxt < skb->len))
+ tp->urg_data = TCP_URG_VALID |
+ skb->data[tp->urg_seq - tp->rcv_nxt];
+
+ if (unlikely(hdr->dack_mode != csk->delack_mode)) {
+ csk->delack_mode = hdr->dack_mode;
+ csk->delack_seq = tp->rcv_nxt;
+ }
+
+ tcp_hdr(skb)->fin = 0;
+ tp->rcv_nxt += skb->len;
+
+ __skb_queue_tail(&sk->sk_receive_queue, skb);
+
+ if (!sock_flag(sk, SOCK_DEAD)) {
+ check_sk_callbacks(csk);
+ sk->sk_data_ready(sk);
+ }
+}
+
+static int chtls_rx_data(struct chtls_dev *cdev, struct sk_buff *skb)
+{
+ struct cpl_rx_data *req = cplhdr(skb) + RSS_HDR;
+ unsigned int hwtid = GET_TID(req);
+ struct sock *sk;
+
+ sk = lookup_tid(cdev->tids, hwtid);
+ skb_dst_set(skb, NULL);
+ process_cpl_msg(chtls_recv_data, sk, skb);
+ return 0;
+}
+
+static void chtls_recv_pdu(struct sock *sk, struct sk_buff *skb)
+{
+ struct cpl_tls_data *hdr = cplhdr(skb);
+ struct chtls_sock *csk;
+ struct chtls_hws *tlsk;
+ struct tcp_sock *tp;
+
+ csk = rcu_dereference_sk_user_data(sk);
+ tlsk = &csk->tlshws;
+ tp = tcp_sk(sk);
+
+ if (unlikely(sk->sk_shutdown & RCV_SHUTDOWN)) {
+ handle_excess_rx(sk, skb);
+ return;
+ }
+
+ ULP_SKB_CB(skb)->seq = ntohl(hdr->seq);
+ ULP_SKB_CB(skb)->flags = 0;
+ skb_ulp_mode(skb) = ULP_MODE_TLS;
+
+ skb_reset_transport_header(skb);
+ __skb_pull(skb, sizeof(*hdr));
+ if (!skb->data_len)
+ __skb_trim(skb,
+ CPL_TLS_DATA_LENGTH_G(ntohl(hdr->length_pkd)));
+
+ if (unlikely(tp->urg_data == TCP_URG_NOTYET && tp->urg_seq -
+ tp->rcv_nxt < skb->len))
+ tp->urg_data = TCP_URG_VALID |
+ skb->data[tp->urg_seq - tp->rcv_nxt];
+
+ tcp_hdr(skb)->fin = 0;
+ tlsk->pldlen = CPL_TLS_DATA_LENGTH_G(ntohl(hdr->length_pkd));
+ __skb_queue_tail(&tlsk->sk_recv_queue, skb);
+}
+
+static int chtls_rx_pdu(struct chtls_dev *cdev, struct sk_buff *skb)
+{
+ struct cpl_tls_data *req = cplhdr(skb);
+ unsigned int hwtid = GET_TID(req);
+ struct sock *sk;
+
+ sk = lookup_tid(cdev->tids, hwtid);
+ skb_dst_set(skb, NULL);
+ process_cpl_msg(chtls_recv_pdu, sk, skb);
+ return 0;
+}
+
+static void chtls_set_hdrlen(struct sk_buff *skb, unsigned int nlen)
+{
+ struct tlsrx_cmp_hdr *tls_cmp_hdr = cplhdr(skb);
+
+ skb->hdr_len = ntohs((__force __be16)tls_cmp_hdr->length);
+ tls_cmp_hdr->length = ntohs((__force __be16)nlen);
+}
+
+static void chtls_rx_hdr(struct sock *sk, struct sk_buff *skb)
+{
+ struct cpl_rx_tls_cmp *cmp_cpl = cplhdr(skb);
+ struct sk_buff *skb_rec;
+ struct chtls_sock *csk;
+ struct chtls_hws *tlsk;
+ struct tcp_sock *tp;
+
+ csk = rcu_dereference_sk_user_data(sk);
+ tlsk = &csk->tlshws;
+ tp = tcp_sk(sk);
+
+ ULP_SKB_CB(skb)->seq = ntohl(cmp_cpl->seq);
+ ULP_SKB_CB(skb)->flags = 0;
+
+ skb_reset_transport_header(skb);
+ __skb_pull(skb, sizeof(*cmp_cpl));
+ if (!skb->data_len)
+ __skb_trim(skb, CPL_RX_TLS_CMP_LENGTH_G
+ (ntohl(cmp_cpl->pdulength_length)));
+
+ tp->rcv_nxt +=
+ CPL_RX_TLS_CMP_PDULENGTH_G(ntohl(cmp_cpl->pdulength_length));
+
+ skb_rec = __skb_dequeue(&tlsk->sk_recv_queue);
+ if (!skb_rec) {
+ ULP_SKB_CB(skb)->flags |= ULPCB_FLAG_TLS_ND;
+ __skb_queue_tail(&sk->sk_receive_queue, skb);
+ } else {
+ chtls_set_hdrlen(skb, tlsk->pldlen);
+ tlsk->pldlen = 0;
+ __skb_queue_tail(&sk->sk_receive_queue, skb);
+ __skb_queue_tail(&sk->sk_receive_queue, skb_rec);
+ }
+
+ if (!sock_flag(sk, SOCK_DEAD)) {
+ check_sk_callbacks(csk);
+ sk->sk_data_ready(sk);
+ }
+}
+
+static int chtls_rx_cmp(struct chtls_dev *cdev, struct sk_buff *skb)
+{
+ struct cpl_rx_tls_cmp *req = cplhdr(skb);
+ unsigned int hwtid = GET_TID(req);
+ struct sock *sk;
+
+ sk = lookup_tid(cdev->tids, hwtid);
+ skb_dst_set(skb, NULL);
+ process_cpl_msg(chtls_rx_hdr, sk, skb);
+
+ return 0;
+}
+
+static void chtls_timewait(struct sock *sk)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+
+ tp->rcv_nxt++;
+ tp->rx_opt.ts_recent_stamp = get_seconds();
+ tp->srtt_us = 0;
+ tcp_time_wait(sk, TCP_TIME_WAIT, 0);
+}
+
+static void chtls_peer_close(struct sock *sk, struct sk_buff *skb)
+{
+ struct chtls_sock *csk = rcu_dereference_sk_user_data(sk);
+
+ sk->sk_shutdown |= RCV_SHUTDOWN;
+ sock_set_flag(sk, SOCK_DONE);
+
+ switch (sk->sk_state) {
+ case TCP_SYN_RECV:
+ case TCP_ESTABLISHED:
+ tcp_set_state(sk, TCP_CLOSE_WAIT);
+ break;
+ case TCP_FIN_WAIT1:
+ tcp_set_state(sk, TCP_CLOSING);
+ break;
+ case TCP_FIN_WAIT2:
+ chtls_release_resources(sk);
+ if (csk_flag_nochk(csk, CSK_ABORT_RPL_PENDING))
+ chtls_conn_done(sk);
+ else
+ chtls_timewait(sk);
+ break;
+ default:
+ pr_info("cpl_peer_close in bad state %d\n", sk->sk_state);
+ }
+
+ if (!sock_flag(sk, SOCK_DEAD)) {
+ sk->sk_state_change(sk);
+ /* Do not send POLL_HUP for half duplex close. */
+
+ if ((sk->sk_shutdown & SEND_SHUTDOWN) ||
+ sk->sk_state == TCP_CLOSE)
+ sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_HUP);
+ else
+ sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN);
+ }
+}
+
+static void chtls_close_con_rpl(struct sock *sk, struct sk_buff *skb)
+{
+ struct cpl_close_con_rpl *rpl = cplhdr(skb) + RSS_HDR;
+ struct chtls_sock *csk;
+ struct tcp_sock *tp;
+
+ csk = rcu_dereference_sk_user_data(sk);
+ tp = tcp_sk(sk);
+
+ tp->snd_una = ntohl(rpl->snd_nxt) - 1; /* exclude FIN */
+
+ switch (sk->sk_state) {
+ case TCP_CLOSING:
+ chtls_release_resources(sk);
+ if (csk_flag_nochk(csk, CSK_ABORT_RPL_PENDING))
+ chtls_conn_done(sk);
+ else
+ chtls_timewait(sk);
+ break;
+ case TCP_LAST_ACK:
+ chtls_release_resources(sk);
+ chtls_conn_done(sk);
+ break;
+ case TCP_FIN_WAIT1:
+ tcp_set_state(sk, TCP_FIN_WAIT2);
+ sk->sk_shutdown |= SEND_SHUTDOWN;
+
+ if (!sock_flag(sk, SOCK_DEAD))
+ sk->sk_state_change(sk);
+ else if (tcp_sk(sk)->linger2 < 0 &&
+ !csk_flag_nochk(csk, CSK_ABORT_SHUTDOWN))
+ chtls_abort_conn(sk, skb);
+ break;
+ default:
+ pr_info("close_con_rpl in bad state %d\n", sk->sk_state);
+ }
+ kfree_skb(skb);
+}
+
+static struct sk_buff *get_cpl_skb(struct sk_buff *skb,
+ size_t len, gfp_t gfp)
+{
+ if (likely(!skb_is_nonlinear(skb) && !skb_cloned(skb))) {
+ WARN_ONCE(skb->len < len, "skb alloc error");
+ __skb_trim(skb, len);
+ skb_get(skb);
+ } else {
+ skb = alloc_skb(len, gfp);
+ if (skb)
+ __skb_put(skb, len);
+ }
+ return skb;
+}
+
+static void set_abort_rpl_wr(struct sk_buff *skb, unsigned int tid,
+ int cmd)
+{
+ struct cpl_abort_rpl *rpl = cplhdr(skb);
+
+ INIT_TP_WR_CPL(rpl, CPL_ABORT_RPL, tid);
+ rpl->cmd = cmd;
+}
+
+static void send_defer_abort_rpl(struct chtls_dev *cdev, struct sk_buff *skb)
+{
+ struct cpl_abort_req_rss *req = cplhdr(skb);
+ struct sk_buff *reply_skb;
+
+ reply_skb = alloc_skb(sizeof(struct cpl_abort_rpl),
+ GFP_KERNEL | __GFP_NOFAIL);
+ __skb_put(reply_skb, sizeof(struct cpl_abort_rpl));
+ set_abort_rpl_wr(reply_skb, GET_TID(req),
+ (req->status & CPL_ABORT_NO_RST));
+ set_wr_txq(reply_skb, CPL_PRIORITY_DATA, req->status >> 1);
+ cxgb4_ofld_send(cdev->lldi->ports[0], reply_skb);
+ kfree_skb(skb);
+}
+
+static void send_abort_rpl(struct sock *sk, struct sk_buff *skb,
+ struct chtls_dev *cdev, int status, int queue)
+{
+ struct cpl_abort_req_rss *req = cplhdr(skb);
+ struct sk_buff *reply_skb;
+ struct chtls_sock *csk;
+
+ csk = rcu_dereference_sk_user_data(sk);
+
+ reply_skb = alloc_skb(sizeof(struct cpl_abort_rpl),
+ GFP_KERNEL);
+
+ if (!reply_skb) {
+ req->status = (queue << 1);
+ send_defer_abort_rpl(cdev, skb);
+ return;
+ }
+
+ set_abort_rpl_wr(reply_skb, GET_TID(req), status);
+ kfree_skb(skb);
+
+ set_wr_txq(reply_skb, CPL_PRIORITY_DATA, queue);
+ if (csk_conn_inline(csk)) {
+ struct l2t_entry *e = csk->l2t_entry;
+
+ if (e && sk->sk_state != TCP_SYN_RECV) {
+ cxgb4_l2t_send(csk->egress_dev, reply_skb, e);
+ return;
+ }
+ }
+ cxgb4_ofld_send(cdev->lldi->ports[0], reply_skb);
+}
+
+/*
+ * Add an skb to the deferred skb queue for processing from process context.
+ */
+static void t4_defer_reply(struct sk_buff *skb, struct chtls_dev *cdev,
+ defer_handler_t handler)
+{
+ DEFERRED_SKB_CB(skb)->handler = handler;
+ spin_lock_bh(&cdev->deferq.lock);
+ __skb_queue_tail(&cdev->deferq, skb);
+ if (skb_queue_len(&cdev->deferq) == 1)
+ schedule_work(&cdev->deferq_task);
+ spin_unlock_bh(&cdev->deferq.lock);
+}
+
+static void chtls_send_abort_rpl(struct sock *sk, struct sk_buff *skb,
+ struct chtls_dev *cdev,
+ int status, int queue)
+{
+ struct cpl_abort_req_rss *req = cplhdr(skb) + RSS_HDR;
+ struct sk_buff *reply_skb;
+ struct chtls_sock *csk;
+ unsigned int tid;
+
+ csk = rcu_dereference_sk_user_data(sk);
+ tid = GET_TID(req);
+
+ reply_skb = get_cpl_skb(skb, sizeof(struct cpl_abort_rpl), gfp_any());
+ if (!reply_skb) {
+ req->status = (queue << 1) | status;
+ t4_defer_reply(skb, cdev, send_defer_abort_rpl);
+ return;
+ }
+
+ set_abort_rpl_wr(reply_skb, tid, status);
+ set_wr_txq(reply_skb, CPL_PRIORITY_DATA, queue);
+ if (csk_conn_inline(csk)) {
+ struct l2t_entry *e = csk->l2t_entry;
+
+ if (e && sk->sk_state != TCP_SYN_RECV) {
+ cxgb4_l2t_send(csk->egress_dev, reply_skb, e);
+ return;
+ }
+ }
+ cxgb4_ofld_send(cdev->lldi->ports[0], reply_skb);
+ kfree_skb(skb);
+}
+
+/*
+ * This is run from a listener's backlog to abort a child connection in
+ * SYN_RCV state (i.e., one on the listener's SYN queue).
+ */
+static void bl_abort_syn_rcv(struct sock *lsk, struct sk_buff *skb)
+{
+ struct chtls_sock *csk;
+ struct sock *child;
+ int queue;
+
+ child = skb->sk;
+ csk = rcu_dereference_sk_user_data(child);
+ queue = csk->txq_idx;
+
+ skb->sk = NULL;
+ do_abort_syn_rcv(child, lsk);
+ send_abort_rpl(child, skb, BLOG_SKB_CB(skb)->cdev,
+ CPL_ABORT_NO_RST, queue);
+}
+
+static int abort_syn_rcv(struct sock *sk, struct sk_buff *skb)
+{
+ const struct request_sock *oreq;
+ struct listen_ctx *listen_ctx;
+ struct chtls_sock *csk;
+ struct chtls_dev *cdev;
+ struct sock *psk;
+ void *ctx;
+
+ csk = sk->sk_user_data;
+ oreq = csk->passive_reap_next;
+ cdev = csk->cdev;
+
+ if (!oreq)
+ return -1;
+
+ ctx = lookup_stid(cdev->tids, oreq->ts_recent);
+ if (!ctx)
+ return -1;
+
+ listen_ctx = (struct listen_ctx *)ctx;
+ psk = listen_ctx->lsk;
+
+ bh_lock_sock(psk);
+ if (!sock_owned_by_user(psk)) {
+ int queue = csk->txq_idx;
+
+ do_abort_syn_rcv(sk, psk);
+ send_abort_rpl(sk, skb, cdev, CPL_ABORT_NO_RST, queue);
+ } else {
+ skb->sk = sk;
+ BLOG_SKB_CB(skb)->backlog_rcv = bl_abort_syn_rcv;
+ __sk_add_backlog(psk, skb);
+ }
+ bh_unlock_sock(psk);
+ return 0;
+}
+
+static void chtls_abort_req_rss(struct sock *sk, struct sk_buff *skb)
+{
+ const struct cpl_abort_req_rss *req = cplhdr(skb) + RSS_HDR;
+ struct chtls_sock *csk = sk->sk_user_data;
+ int rst_status = CPL_ABORT_NO_RST;
+ int queue = csk->txq_idx;
+
+ if (is_neg_adv(req->status)) {
+ if (sk->sk_state == TCP_SYN_RECV)
+ chtls_set_tcb_tflag(sk, 0, 0);
+
+ kfree_skb(skb);
+ return;
+ }
+
+ csk_reset_flag(csk, CSK_ABORT_REQ_RCVD);
+
+ if (!csk_flag_nochk(csk, CSK_ABORT_SHUTDOWN) &&
+ !csk_flag_nochk(csk, CSK_TX_DATA_SENT)) {
+ struct tcp_sock *tp = tcp_sk(sk);
+
+ if (send_tx_flowc_wr(sk, 0, tp->snd_nxt, tp->rcv_nxt) < 0)
+ WARN_ONCE(1, "send_tx_flowc error");
+ csk_set_flag(csk, CSK_TX_DATA_SENT);
+ }
+
+ csk_set_flag(csk, CSK_ABORT_SHUTDOWN);
+
+ if (!csk_flag_nochk(csk, CSK_ABORT_RPL_PENDING)) {
+ sk->sk_err = ETIMEDOUT;
+
+ if (!sock_flag(sk, SOCK_DEAD))
+ sk->sk_error_report(sk);
+
+ if (sk->sk_state == TCP_SYN_RECV && !abort_syn_rcv(sk, skb))
+ return;
+
+ chtls_release_resources(sk);
+ chtls_conn_done(sk);
+ }
+
+ chtls_send_abort_rpl(sk, skb, csk->cdev, rst_status, queue);
+}
+
+static void chtls_abort_rpl_rss(struct sock *sk, struct sk_buff *skb)
+{
+ struct cpl_abort_rpl_rss *rpl = cplhdr(skb) + RSS_HDR;
+ struct chtls_sock *csk;
+ struct chtls_dev *cdev;
+
+ csk = rcu_dereference_sk_user_data(sk);
+ cdev = csk->cdev;
+
+ if (csk_flag_nochk(csk, CSK_ABORT_RPL_PENDING)) {
+ csk_reset_flag(csk, CSK_ABORT_RPL_PENDING);
+ if (!csk_flag_nochk(csk, CSK_ABORT_REQ_RCVD)) {
+ if (sk->sk_state == TCP_SYN_SENT) {
+ cxgb4_remove_tid(cdev->tids,
+ csk->port_id,
+ GET_TID(rpl),
+ sk->sk_family);
+ sock_put(sk);
+ }
+ chtls_release_resources(sk);
+ chtls_conn_done(sk);
+ }
+ }
+ kfree_skb(skb);
+}
+
+static int chtls_conn_cpl(struct chtls_dev *cdev, struct sk_buff *skb)
+{
+ struct cpl_peer_close *req = cplhdr(skb) + RSS_HDR;
+ void (*fn)(struct sock *sk, struct sk_buff *skb);
+ unsigned int hwtid = GET_TID(req);
+ struct sock *sk;
+ u8 opcode;
+
+ opcode = ((const struct rss_header *)cplhdr(skb))->opcode;
+
+ sk = lookup_tid(cdev->tids, hwtid);
+ if (!sk)
+ goto rel_skb;
+
+ switch (opcode) {
+ case CPL_PEER_CLOSE:
+ fn = chtls_peer_close;
+ break;
+ case CPL_CLOSE_CON_RPL:
+ fn = chtls_close_con_rpl;
+ break;
+ case CPL_ABORT_REQ_RSS:
+ fn = chtls_abort_req_rss;
+ break;
+ case CPL_ABORT_RPL_RSS:
+ fn = chtls_abort_rpl_rss;
+ break;
+ default:
+ goto rel_skb;
+ }
+
+ process_cpl_msg(fn, sk, skb);
+ return 0;
+
+rel_skb:
+ kfree_skb(skb);
+ return 0;
+}
+
+static struct sk_buff *dequeue_wr(struct sock *sk)
+{
+ struct chtls_sock *csk = rcu_dereference_sk_user_data(sk);
+ struct sk_buff *skb = csk->wr_skb_head;
+
+ if (likely(skb)) {
+ /* Don't bother clearing the tail */
+ csk->wr_skb_head = WR_SKB_CB(skb)->next_wr;
+ WR_SKB_CB(skb)->next_wr = NULL;
+ }
+ return skb;
+}
+
+static void chtls_rx_ack(struct sock *sk, struct sk_buff *skb)
+{
+ struct cpl_fw4_ack *hdr = cplhdr(skb) + RSS_HDR;
+ struct chtls_sock *csk = sk->sk_user_data;
+ struct tcp_sock *tp = tcp_sk(sk);
+ u32 credits = hdr->credits;
+ u32 snd_una;
+
+ snd_una = ntohl(hdr->snd_una);
+ csk->wr_credits += credits;
+
+ if (csk->wr_unacked > csk->wr_max_credits - csk->wr_credits)
+ csk->wr_unacked = csk->wr_max_credits - csk->wr_credits;
+
+ while (credits) {
+ struct sk_buff *pskb = csk->wr_skb_head;
+ u32 csum;
+
+ if (unlikely(!pskb)) {
+ if (csk->wr_nondata)
+ csk->wr_nondata -= credits;
+ break;
+ }
+ csum = (__force u32)pskb->csum;
+ if (unlikely(credits < csum)) {
+ pskb->csum = (__force __wsum)(csum - credits);
+ break;
+ }
+ dequeue_wr(sk);
+ credits -= csum;
+ kfree_skb(pskb);
+ }
+ if (hdr->seq_vld & CPL_FW4_ACK_FLAGS_SEQVAL) {
+ if (unlikely(before(snd_una, tp->snd_una))) {
+ kfree_skb(skb);
+ return;
+ }
+
+ if (tp->snd_una != snd_una) {
+ tp->snd_una = snd_una;
+ tp->rcv_tstamp = tcp_time_stamp(tp);
+ if (tp->snd_una == tp->snd_nxt &&
+ !csk_flag_nochk(csk, CSK_TX_FAILOVER))
+ csk_reset_flag(csk, CSK_TX_WAIT_IDLE);
+ }
+ }
+
+ if (hdr->seq_vld & CPL_FW4_ACK_FLAGS_CH) {
+ unsigned int fclen16 = roundup(failover_flowc_wr_len, 16);
+
+ csk->wr_credits -= fclen16;
+ csk_reset_flag(csk, CSK_TX_WAIT_IDLE);
+ csk_reset_flag(csk, CSK_TX_FAILOVER);
+ }
+ if (skb_queue_len(&csk->txq) && chtls_push_frames(csk, 0))
+ sk->sk_write_space(sk);
+
+ kfree_skb(skb);
+}
+
+static int chtls_wr_ack(struct chtls_dev *cdev, struct sk_buff *skb)
+{
+ struct cpl_fw4_ack *rpl = cplhdr(skb) + RSS_HDR;
+ unsigned int hwtid = GET_TID(rpl);
+ struct sock *sk;
+
+ sk = lookup_tid(cdev->tids, hwtid);
+ process_cpl_msg(chtls_rx_ack, sk, skb);
+
+ return 0;
+}
+
+chtls_handler_func chtls_handlers[NUM_CPL_CMDS] = {
+ [CPL_PASS_OPEN_RPL] = chtls_pass_open_rpl,
+ [CPL_CLOSE_LISTSRV_RPL] = chtls_close_listsrv_rpl,
+ [CPL_PASS_ACCEPT_REQ] = chtls_pass_accept_req,
+ [CPL_PASS_ESTABLISH] = chtls_pass_establish,
+ [CPL_RX_DATA] = chtls_rx_data,
+ [CPL_TLS_DATA] = chtls_rx_pdu,
+ [CPL_RX_TLS_CMP] = chtls_rx_cmp,
+ [CPL_PEER_CLOSE] = chtls_conn_cpl,
+ [CPL_CLOSE_CON_RPL] = chtls_conn_cpl,
+ [CPL_ABORT_REQ_RSS] = chtls_conn_cpl,
+ [CPL_ABORT_RPL_RSS] = chtls_conn_cpl,
+ [CPL_FW4_ACK] = chtls_wr_ack,
+};
diff --git a/drivers/crypto/chelsio/chtls/chtls_cm.h b/drivers/crypto/chelsio/chtls/chtls_cm.h
new file mode 100644
index 000000000000..78eb3afa3a80
--- /dev/null
+++ b/drivers/crypto/chelsio/chtls/chtls_cm.h
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2018 Chelsio Communications, Inc.
+ *
+ * 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.
+ */
+
+#ifndef __CHTLS_CM_H__
+#define __CHTLS_CM_H__
+
+/*
+ * TCB settings
+ */
+/* 3:0 */
+#define TCB_ULP_TYPE_W 0
+#define TCB_ULP_TYPE_S 0
+#define TCB_ULP_TYPE_M 0xfULL
+#define TCB_ULP_TYPE_V(x) ((x) << TCB_ULP_TYPE_S)
+
+/* 11:4 */
+#define TCB_ULP_RAW_W 0
+#define TCB_ULP_RAW_S 4
+#define TCB_ULP_RAW_M 0xffULL
+#define TCB_ULP_RAW_V(x) ((x) << TCB_ULP_RAW_S)
+
+#define TF_TLS_KEY_SIZE_S 7
+#define TF_TLS_KEY_SIZE_V(x) ((x) << TF_TLS_KEY_SIZE_S)
+
+#define TF_TLS_CONTROL_S 2
+#define TF_TLS_CONTROL_V(x) ((x) << TF_TLS_CONTROL_S)
+
+#define TF_TLS_ACTIVE_S 1
+#define TF_TLS_ACTIVE_V(x) ((x) << TF_TLS_ACTIVE_S)
+
+#define TF_TLS_ENABLE_S 0
+#define TF_TLS_ENABLE_V(x) ((x) << TF_TLS_ENABLE_S)
+
+#define TF_RX_QUIESCE_S 15
+#define TF_RX_QUIESCE_V(x) ((x) << TF_RX_QUIESCE_S)
+
+/*
+ * Max receive window supported by HW in bytes. Only a small part of it can
+ * be set through option0, the rest needs to be set through RX_DATA_ACK.
+ */
+#define MAX_RCV_WND ((1U << 27) - 1)
+#define MAX_MSS 65536
+
+/*
+ * Min receive window. We want it to be large enough to accommodate receive
+ * coalescing, handle jumbo frames, and not trigger sender SWS avoidance.
+ */
+#define MIN_RCV_WND (24 * 1024U)
+#define LOOPBACK(x) (((x) & htonl(0xff000000)) == htonl(0x7f000000))
+
+/* ulp_mem_io + ulptx_idata + payload + padding */
+#define MAX_IMM_ULPTX_WR_LEN (32 + 8 + 256 + 8)
+
+/* for TX: a skb must have a headroom of at least TX_HEADER_LEN bytes */
+#define TX_HEADER_LEN \
+ (sizeof(struct fw_ofld_tx_data_wr) + sizeof(struct sge_opaque_hdr))
+#define TX_TLSHDR_LEN \
+ (sizeof(struct fw_tlstx_data_wr) + sizeof(struct cpl_tx_tls_sfo) + \
+ sizeof(struct sge_opaque_hdr))
+#define TXDATA_SKB_LEN 128
+
+enum {
+ CPL_TX_TLS_SFO_TYPE_CCS,
+ CPL_TX_TLS_SFO_TYPE_ALERT,
+ CPL_TX_TLS_SFO_TYPE_HANDSHAKE,
+ CPL_TX_TLS_SFO_TYPE_DATA,
+ CPL_TX_TLS_SFO_TYPE_HEARTBEAT,
+};
+
+enum {
+ TLS_HDR_TYPE_CCS = 20,
+ TLS_HDR_TYPE_ALERT,
+ TLS_HDR_TYPE_HANDSHAKE,
+ TLS_HDR_TYPE_RECORD,
+ TLS_HDR_TYPE_HEARTBEAT,
+};
+
+typedef void (*defer_handler_t)(struct chtls_dev *dev, struct sk_buff *skb);
+extern struct request_sock_ops chtls_rsk_ops;
+
+struct deferred_skb_cb {
+ defer_handler_t handler;
+ struct chtls_dev *dev;
+};
+
+#define DEFERRED_SKB_CB(skb) ((struct deferred_skb_cb *)(skb)->cb)
+#define failover_flowc_wr_len offsetof(struct fw_flowc_wr, mnemval[3])
+#define WR_SKB_CB(skb) ((struct wr_skb_cb *)(skb)->cb)
+#define ACCEPT_QUEUE(sk) (&inet_csk(sk)->icsk_accept_queue.rskq_accept_head)
+
+#define SND_WSCALE(tp) ((tp)->rx_opt.snd_wscale)
+#define RCV_WSCALE(tp) ((tp)->rx_opt.rcv_wscale)
+#define USER_MSS(tp) ((tp)->rx_opt.user_mss)
+#define TS_RECENT_STAMP(tp) ((tp)->rx_opt.ts_recent_stamp)
+#define WSCALE_OK(tp) ((tp)->rx_opt.wscale_ok)
+#define TSTAMP_OK(tp) ((tp)->rx_opt.tstamp_ok)
+#define SACK_OK(tp) ((tp)->rx_opt.sack_ok)
+#define INC_ORPHAN_COUNT(sk) percpu_counter_inc((sk)->sk_prot->orphan_count)
+
+/* TLS SKB */
+#define skb_ulp_tls_inline(skb) (ULP_SKB_CB(skb)->ulp.tls.ofld)
+#define skb_ulp_tls_iv_imm(skb) (ULP_SKB_CB(skb)->ulp.tls.iv)
+
+void chtls_defer_reply(struct sk_buff *skb, struct chtls_dev *dev,
+ defer_handler_t handler);
+
+/*
+ * Returns true if the socket is in one of the supplied states.
+ */
+static inline unsigned int sk_in_state(const struct sock *sk,
+ unsigned int states)
+{
+ return states & (1 << sk->sk_state);
+}
+
+static void chtls_rsk_destructor(struct request_sock *req)
+{
+ /* do nothing */
+}
+
+static inline void chtls_init_rsk_ops(struct proto *chtls_tcp_prot,
+ struct request_sock_ops *chtls_tcp_ops,
+ struct proto *tcp_prot, int family)
+{
+ memset(chtls_tcp_ops, 0, sizeof(*chtls_tcp_ops));
+ chtls_tcp_ops->family = family;
+ chtls_tcp_ops->obj_size = sizeof(struct tcp_request_sock);
+ chtls_tcp_ops->destructor = chtls_rsk_destructor;
+ chtls_tcp_ops->slab = tcp_prot->rsk_prot->slab;
+ chtls_tcp_prot->rsk_prot = chtls_tcp_ops;
+}
+
+static inline void chtls_reqsk_free(struct request_sock *req)
+{
+ if (req->rsk_listener)
+ sock_put(req->rsk_listener);
+ kmem_cache_free(req->rsk_ops->slab, req);
+}
+
+#define DECLARE_TASK_FUNC(task, task_param) \
+ static void task(struct work_struct *task_param)
+
+static inline void sk_wakeup_sleepers(struct sock *sk, bool interruptable)
+{
+ struct socket_wq *wq;
+
+ rcu_read_lock();
+ wq = rcu_dereference(sk->sk_wq);
+ if (skwq_has_sleeper(wq)) {
+ if (interruptable)
+ wake_up_interruptible(sk_sleep(sk));
+ else
+ wake_up_all(sk_sleep(sk));
+ }
+ rcu_read_unlock();
+}
+
+static inline void chtls_set_req_port(struct request_sock *oreq,
+ __be16 source, __be16 dest)
+{
+ inet_rsk(oreq)->ir_rmt_port = source;
+ inet_rsk(oreq)->ir_num = ntohs(dest);
+}
+
+static inline void chtls_set_req_addr(struct request_sock *oreq,
+ __be32 local_ip, __be32 peer_ip)
+{
+ inet_rsk(oreq)->ir_loc_addr = local_ip;
+ inet_rsk(oreq)->ir_rmt_addr = peer_ip;
+}
+
+static inline void chtls_free_skb(struct sock *sk, struct sk_buff *skb)
+{
+ skb_dst_set(skb, NULL);
+ __skb_unlink(skb, &sk->sk_receive_queue);
+ __kfree_skb(skb);
+}
+
+static inline void chtls_kfree_skb(struct sock *sk, struct sk_buff *skb)
+{
+ skb_dst_set(skb, NULL);
+ __skb_unlink(skb, &sk->sk_receive_queue);
+ kfree_skb(skb);
+}
+
+static inline void enqueue_wr(struct chtls_sock *csk, struct sk_buff *skb)
+{
+ WR_SKB_CB(skb)->next_wr = NULL;
+
+ skb_get(skb);
+
+ if (!csk->wr_skb_head)
+ csk->wr_skb_head = skb;
+ else
+ WR_SKB_CB(csk->wr_skb_tail)->next_wr = skb;
+ csk->wr_skb_tail = skb;
+}
+#endif
diff --git a/drivers/crypto/chelsio/chtls/chtls_hw.c b/drivers/crypto/chelsio/chtls/chtls_hw.c
new file mode 100644
index 000000000000..54a13aa99121
--- /dev/null
+++ b/drivers/crypto/chelsio/chtls/chtls_hw.c
@@ -0,0 +1,412 @@
+/*
+ * Copyright (c) 2018 Chelsio Communications, Inc.
+ *
+ * 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.
+ *
+ * Written by: Atul Gupta (atul.gupta@chelsio.com)
+ */
+
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/workqueue.h>
+#include <linux/skbuff.h>
+#include <linux/timer.h>
+#include <linux/notifier.h>
+#include <linux/inetdevice.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/tls.h>
+#include <net/tls.h>
+
+#include "chtls.h"
+#include "chtls_cm.h"
+
+static void __set_tcb_field_direct(struct chtls_sock *csk,
+ struct cpl_set_tcb_field *req, u16 word,
+ u64 mask, u64 val, u8 cookie, int no_reply)
+{
+ struct ulptx_idata *sc;
+
+ INIT_TP_WR_CPL(req, CPL_SET_TCB_FIELD, csk->tid);
+ req->wr.wr_mid |= htonl(FW_WR_FLOWID_V(csk->tid));
+ req->reply_ctrl = htons(NO_REPLY_V(no_reply) |
+ QUEUENO_V(csk->rss_qid));
+ req->word_cookie = htons(TCB_WORD_V(word) | TCB_COOKIE_V(cookie));
+ req->mask = cpu_to_be64(mask);
+ req->val = cpu_to_be64(val);
+ sc = (struct ulptx_idata *)(req + 1);
+ sc->cmd_more = htonl(ULPTX_CMD_V(ULP_TX_SC_NOOP));
+ sc->len = htonl(0);
+}
+
+static void __set_tcb_field(struct sock *sk, struct sk_buff *skb, u16 word,
+ u64 mask, u64 val, u8 cookie, int no_reply)
+{
+ struct cpl_set_tcb_field *req;
+ struct chtls_sock *csk;
+ struct ulptx_idata *sc;
+ unsigned int wrlen;
+
+ wrlen = roundup(sizeof(*req) + sizeof(*sc), 16);
+ csk = rcu_dereference_sk_user_data(sk);
+
+ req = (struct cpl_set_tcb_field *)__skb_put(skb, wrlen);
+ __set_tcb_field_direct(csk, req, word, mask, val, cookie, no_reply);
+ set_wr_txq(skb, CPL_PRIORITY_CONTROL, csk->port_id);
+}
+
+/*
+ * Send control message to HW, message go as immediate data and packet
+ * is freed immediately.
+ */
+static int chtls_set_tcb_field(struct sock *sk, u16 word, u64 mask, u64 val)
+{
+ struct cpl_set_tcb_field *req;
+ unsigned int credits_needed;
+ struct chtls_sock *csk;
+ struct ulptx_idata *sc;
+ struct sk_buff *skb;
+ unsigned int wrlen;
+ int ret;
+
+ wrlen = roundup(sizeof(*req) + sizeof(*sc), 16);
+
+ skb = alloc_skb(wrlen, GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+
+ credits_needed = DIV_ROUND_UP(wrlen, 16);
+ csk = rcu_dereference_sk_user_data(sk);
+
+ __set_tcb_field(sk, skb, word, mask, val, 0, 1);
+ skb_set_queue_mapping(skb, (csk->txq_idx << 1) | CPL_PRIORITY_DATA);
+ csk->wr_credits -= credits_needed;
+ csk->wr_unacked += credits_needed;
+ enqueue_wr(csk, skb);
+ ret = cxgb4_ofld_send(csk->egress_dev, skb);
+ if (ret < 0)
+ kfree_skb(skb);
+ return ret < 0 ? ret : 0;
+}
+
+/*
+ * Set one of the t_flags bits in the TCB.
+ */
+int chtls_set_tcb_tflag(struct sock *sk, unsigned int bit_pos, int val)
+{
+ return chtls_set_tcb_field(sk, 1, 1ULL << bit_pos,
+ val << bit_pos);
+}
+
+static int chtls_set_tcb_keyid(struct sock *sk, int keyid)
+{
+ return chtls_set_tcb_field(sk, 31, 0xFFFFFFFFULL, keyid);
+}
+
+static int chtls_set_tcb_seqno(struct sock *sk)
+{
+ return chtls_set_tcb_field(sk, 28, ~0ULL, 0);
+}
+
+static int chtls_set_tcb_quiesce(struct sock *sk, int val)
+{
+ return chtls_set_tcb_field(sk, 1, (1ULL << TF_RX_QUIESCE_S),
+ TF_RX_QUIESCE_V(val));
+}
+
+/* TLS Key bitmap processing */
+int chtls_init_kmap(struct chtls_dev *cdev, struct cxgb4_lld_info *lldi)
+{
+ unsigned int num_key_ctx, bsize;
+ int ksize;
+
+ num_key_ctx = (lldi->vr->key.size / TLS_KEY_CONTEXT_SZ);
+ bsize = BITS_TO_LONGS(num_key_ctx);
+
+ cdev->kmap.size = num_key_ctx;
+ cdev->kmap.available = bsize;
+ ksize = sizeof(*cdev->kmap.addr) * bsize;
+ cdev->kmap.addr = kvzalloc(ksize, GFP_KERNEL);
+ if (!cdev->kmap.addr)
+ return -ENOMEM;
+
+ cdev->kmap.start = lldi->vr->key.start;
+ spin_lock_init(&cdev->kmap.lock);
+ return 0;
+}
+
+static int get_new_keyid(struct chtls_sock *csk, u32 optname)
+{
+ struct net_device *dev = csk->egress_dev;
+ struct chtls_dev *cdev = csk->cdev;
+ struct chtls_hws *hws;
+ struct adapter *adap;
+ int keyid;
+
+ adap = netdev2adap(dev);
+ hws = &csk->tlshws;
+
+ spin_lock_bh(&cdev->kmap.lock);
+ keyid = find_first_zero_bit(cdev->kmap.addr, cdev->kmap.size);
+ if (keyid < cdev->kmap.size) {
+ __set_bit(keyid, cdev->kmap.addr);
+ if (optname == TLS_RX)
+ hws->rxkey = keyid;
+ else
+ hws->txkey = keyid;
+ atomic_inc(&adap->chcr_stats.tls_key);
+ } else {
+ keyid = -1;
+ }
+ spin_unlock_bh(&cdev->kmap.lock);
+ return keyid;
+}
+
+void free_tls_keyid(struct sock *sk)
+{
+ struct chtls_sock *csk = rcu_dereference_sk_user_data(sk);
+ struct net_device *dev = csk->egress_dev;
+ struct chtls_dev *cdev = csk->cdev;
+ struct chtls_hws *hws;
+ struct adapter *adap;
+
+ if (!cdev->kmap.addr)
+ return;
+
+ adap = netdev2adap(dev);
+ hws = &csk->tlshws;
+
+ spin_lock_bh(&cdev->kmap.lock);
+ if (hws->rxkey >= 0) {
+ __clear_bit(hws->rxkey, cdev->kmap.addr);
+ atomic_dec(&adap->chcr_stats.tls_key);
+ hws->rxkey = -1;
+ }
+ if (hws->txkey >= 0) {
+ __clear_bit(hws->txkey, cdev->kmap.addr);
+ atomic_dec(&adap->chcr_stats.tls_key);
+ hws->txkey = -1;
+ }
+ spin_unlock_bh(&cdev->kmap.lock);
+}
+
+unsigned int keyid_to_addr(int start_addr, int keyid)
+{
+ return (start_addr + (keyid * TLS_KEY_CONTEXT_SZ)) >> 5;
+}
+
+static void chtls_rxkey_ivauth(struct _key_ctx *kctx)
+{
+ kctx->iv_to_auth = cpu_to_be64(KEYCTX_TX_WR_IV_V(6ULL) |
+ KEYCTX_TX_WR_AAD_V(1ULL) |
+ KEYCTX_TX_WR_AADST_V(5ULL) |
+ KEYCTX_TX_WR_CIPHER_V(14ULL) |
+ KEYCTX_TX_WR_CIPHERST_V(0ULL) |
+ KEYCTX_TX_WR_AUTH_V(14ULL) |
+ KEYCTX_TX_WR_AUTHST_V(16ULL) |
+ KEYCTX_TX_WR_AUTHIN_V(16ULL));
+}
+
+static int chtls_key_info(struct chtls_sock *csk,
+ struct _key_ctx *kctx,
+ u32 keylen, u32 optname)
+{
+ unsigned char key[CHCR_KEYCTX_CIPHER_KEY_SIZE_256];
+ struct tls12_crypto_info_aes_gcm_128 *gcm_ctx;
+ unsigned char ghash_h[AEAD_H_SIZE];
+ struct crypto_cipher *cipher;
+ int ck_size, key_ctx_size;
+ int ret;
+
+ gcm_ctx = (struct tls12_crypto_info_aes_gcm_128 *)
+ &csk->tlshws.crypto_info;
+
+ key_ctx_size = sizeof(struct _key_ctx) +
+ roundup(keylen, 16) + AEAD_H_SIZE;
+
+ if (keylen == AES_KEYSIZE_128) {
+ ck_size = CHCR_KEYCTX_CIPHER_KEY_SIZE_128;
+ } else if (keylen == AES_KEYSIZE_192) {
+ ck_size = CHCR_KEYCTX_CIPHER_KEY_SIZE_192;
+ } else if (keylen == AES_KEYSIZE_256) {
+ ck_size = CHCR_KEYCTX_CIPHER_KEY_SIZE_256;
+ } else {
+ pr_err("GCM: Invalid key length %d\n", keylen);
+ return -EINVAL;
+ }
+ memcpy(key, gcm_ctx->key, keylen);
+
+ /* Calculate the H = CIPH(K, 0 repeated 16 times).
+ * It will go in key context
+ */
+ cipher = crypto_alloc_cipher("aes", 0, 0);
+ if (IS_ERR(cipher)) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = crypto_cipher_setkey(cipher, key, keylen);
+ if (ret)
+ goto out1;
+
+ memset(ghash_h, 0, AEAD_H_SIZE);
+ crypto_cipher_encrypt_one(cipher, ghash_h, ghash_h);
+ csk->tlshws.keylen = key_ctx_size;
+
+ /* Copy the Key context */
+ if (optname == TLS_RX) {
+ int key_ctx;
+
+ key_ctx = ((key_ctx_size >> 4) << 3);
+ kctx->ctx_hdr = FILL_KEY_CRX_HDR(ck_size,
+ CHCR_KEYCTX_MAC_KEY_SIZE_128,
+ 0, 0, key_ctx);
+ chtls_rxkey_ivauth(kctx);
+ } else {
+ kctx->ctx_hdr = FILL_KEY_CTX_HDR(ck_size,
+ CHCR_KEYCTX_MAC_KEY_SIZE_128,
+ 0, 0, key_ctx_size >> 4);
+ }
+
+ memcpy(kctx->salt, gcm_ctx->salt, TLS_CIPHER_AES_GCM_128_SALT_SIZE);
+ memcpy(kctx->key, gcm_ctx->key, keylen);
+ memcpy(kctx->key + keylen, ghash_h, AEAD_H_SIZE);
+ /* erase key info from driver */
+ memset(gcm_ctx->key, 0, keylen);
+
+out1:
+ crypto_free_cipher(cipher);
+out:
+ return ret;
+}
+
+static void chtls_set_scmd(struct chtls_sock *csk)
+{
+ struct chtls_hws *hws = &csk->tlshws;
+
+ hws->scmd.seqno_numivs =
+ SCMD_SEQ_NO_CTRL_V(3) |
+ SCMD_PROTO_VERSION_V(0) |
+ SCMD_ENC_DEC_CTRL_V(0) |
+ SCMD_CIPH_AUTH_SEQ_CTRL_V(1) |
+ SCMD_CIPH_MODE_V(2) |
+ SCMD_AUTH_MODE_V(4) |
+ SCMD_HMAC_CTRL_V(0) |
+ SCMD_IV_SIZE_V(4) |
+ SCMD_NUM_IVS_V(1);
+
+ hws->scmd.ivgen_hdrlen =
+ SCMD_IV_GEN_CTRL_V(1) |
+ SCMD_KEY_CTX_INLINE_V(0) |
+ SCMD_TLS_FRAG_ENABLE_V(1);
+}
+
+int chtls_setkey(struct chtls_sock *csk, u32 keylen, u32 optname)
+{
+ struct tls_key_req *kwr;
+ struct chtls_dev *cdev;
+ struct _key_ctx *kctx;
+ int wrlen, klen, len;
+ struct sk_buff *skb;
+ struct sock *sk;
+ int keyid;
+ int kaddr;
+ int ret;
+
+ cdev = csk->cdev;
+ sk = csk->sk;
+
+ klen = roundup((keylen + AEAD_H_SIZE) + sizeof(*kctx), 32);
+ wrlen = roundup(sizeof(*kwr), 16);
+ len = klen + wrlen;
+
+ /* Flush out-standing data before new key takes effect */
+ if (optname == TLS_TX) {
+ lock_sock(sk);
+ if (skb_queue_len(&csk->txq))
+ chtls_push_frames(csk, 0);
+ release_sock(sk);
+ }
+
+ skb = alloc_skb(len, GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
+ keyid = get_new_keyid(csk, optname);
+ if (keyid < 0) {
+ ret = -ENOSPC;
+ goto out_nokey;
+ }
+
+ kaddr = keyid_to_addr(cdev->kmap.start, keyid);
+ kwr = (struct tls_key_req *)__skb_put_zero(skb, len);
+ kwr->wr.op_to_compl =
+ cpu_to_be32(FW_WR_OP_V(FW_ULPTX_WR) | FW_WR_COMPL_F |
+ FW_WR_ATOMIC_V(1U));
+ kwr->wr.flowid_len16 =
+ cpu_to_be32(FW_WR_LEN16_V(DIV_ROUND_UP(len, 16) |
+ FW_WR_FLOWID_V(csk->tid)));
+ kwr->wr.protocol = 0;
+ kwr->wr.mfs = htons(TLS_MFS);
+ kwr->wr.reneg_to_write_rx = optname;
+
+ /* ulptx command */
+ kwr->req.cmd = cpu_to_be32(ULPTX_CMD_V(ULP_TX_MEM_WRITE) |
+ T5_ULP_MEMIO_ORDER_V(1) |
+ T5_ULP_MEMIO_IMM_V(1));
+ kwr->req.len16 = cpu_to_be32((csk->tid << 8) |
+ DIV_ROUND_UP(len - sizeof(kwr->wr), 16));
+ kwr->req.dlen = cpu_to_be32(ULP_MEMIO_DATA_LEN_V(klen >> 5));
+ kwr->req.lock_addr = cpu_to_be32(ULP_MEMIO_ADDR_V(kaddr));
+
+ /* sub command */
+ kwr->sc_imm.cmd_more = cpu_to_be32(ULPTX_CMD_V(ULP_TX_SC_IMM));
+ kwr->sc_imm.len = cpu_to_be32(klen);
+
+ /* key info */
+ kctx = (struct _key_ctx *)(kwr + 1);
+ ret = chtls_key_info(csk, kctx, keylen, optname);
+ if (ret)
+ goto out_notcb;
+
+ set_wr_txq(skb, CPL_PRIORITY_DATA, csk->tlshws.txqid);
+ csk->wr_credits -= DIV_ROUND_UP(len, 16);
+ csk->wr_unacked += DIV_ROUND_UP(len, 16);
+ enqueue_wr(csk, skb);
+ cxgb4_ofld_send(csk->egress_dev, skb);
+
+ chtls_set_scmd(csk);
+ /* Clear quiesce for Rx key */
+ if (optname == TLS_RX) {
+ ret = chtls_set_tcb_keyid(sk, keyid);
+ if (ret)
+ goto out_notcb;
+ ret = chtls_set_tcb_field(sk, 0,
+ TCB_ULP_RAW_V(TCB_ULP_RAW_M),
+ TCB_ULP_RAW_V((TF_TLS_KEY_SIZE_V(1) |
+ TF_TLS_CONTROL_V(1) |
+ TF_TLS_ACTIVE_V(1) |
+ TF_TLS_ENABLE_V(1))));
+ if (ret)
+ goto out_notcb;
+ ret = chtls_set_tcb_seqno(sk);
+ if (ret)
+ goto out_notcb;
+ ret = chtls_set_tcb_quiesce(sk, 0);
+ if (ret)
+ goto out_notcb;
+ csk->tlshws.rxkey = keyid;
+ } else {
+ csk->tlshws.tx_seq_no = 0;
+ csk->tlshws.txkey = keyid;
+ }
+
+ return ret;
+out_notcb:
+ free_tls_keyid(sk);
+out_nokey:
+ kfree_skb(skb);
+ return ret;
+}
diff --git a/drivers/crypto/chelsio/chtls/chtls_io.c b/drivers/crypto/chelsio/chtls/chtls_io.c
new file mode 100644
index 000000000000..5a75be43950f
--- /dev/null
+++ b/drivers/crypto/chelsio/chtls/chtls_io.c
@@ -0,0 +1,1822 @@
+/*
+ * Copyright (c) 2018 Chelsio Communications, Inc.
+ *
+ * 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.
+ *
+ * Written by: Atul Gupta (atul.gupta@chelsio.com)
+ */
+
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/workqueue.h>
+#include <linux/skbuff.h>
+#include <linux/timer.h>
+#include <linux/notifier.h>
+#include <linux/inetdevice.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/sched/signal.h>
+#include <net/tcp.h>
+#include <net/busy_poll.h>
+#include <crypto/aes.h>
+
+#include "chtls.h"
+#include "chtls_cm.h"
+
+static bool is_tls_tx(struct chtls_sock *csk)
+{
+ return csk->tlshws.txkey >= 0;
+}
+
+static bool is_tls_rx(struct chtls_sock *csk)
+{
+ return csk->tlshws.rxkey >= 0;
+}
+
+static int data_sgl_len(const struct sk_buff *skb)
+{
+ unsigned int cnt;
+
+ cnt = skb_shinfo(skb)->nr_frags;
+ return sgl_len(cnt) * 8;
+}
+
+static int nos_ivs(struct sock *sk, unsigned int size)
+{
+ struct chtls_sock *csk = rcu_dereference_sk_user_data(sk);
+
+ return DIV_ROUND_UP(size, csk->tlshws.mfs);
+}
+
+static int set_ivs_imm(struct sock *sk, const struct sk_buff *skb)
+{
+ int ivs_size = nos_ivs(sk, skb->len) * CIPHER_BLOCK_SIZE;
+ int hlen = TLS_WR_CPL_LEN + data_sgl_len(skb);
+
+ if ((hlen + KEY_ON_MEM_SZ + ivs_size) <
+ MAX_IMM_OFLD_TX_DATA_WR_LEN) {
+ ULP_SKB_CB(skb)->ulp.tls.iv = 1;
+ return 1;
+ }
+ ULP_SKB_CB(skb)->ulp.tls.iv = 0;
+ return 0;
+}
+
+static int max_ivs_size(struct sock *sk, int size)
+{
+ return nos_ivs(sk, size) * CIPHER_BLOCK_SIZE;
+}
+
+static int ivs_size(struct sock *sk, const struct sk_buff *skb)
+{
+ return set_ivs_imm(sk, skb) ? (nos_ivs(sk, skb->len) *
+ CIPHER_BLOCK_SIZE) : 0;
+}
+
+static int flowc_wr_credits(int nparams, int *flowclenp)
+{
+ int flowclen16, flowclen;
+
+ flowclen = offsetof(struct fw_flowc_wr, mnemval[nparams]);
+ flowclen16 = DIV_ROUND_UP(flowclen, 16);
+ flowclen = flowclen16 * 16;
+
+ if (flowclenp)
+ *flowclenp = flowclen;
+
+ return flowclen16;
+}
+
+static struct sk_buff *create_flowc_wr_skb(struct sock *sk,
+ struct fw_flowc_wr *flowc,
+ int flowclen)
+{
+ struct chtls_sock *csk = rcu_dereference_sk_user_data(sk);
+ struct sk_buff *skb;
+
+ skb = alloc_skb(flowclen, GFP_ATOMIC);
+ if (!skb)
+ return NULL;
+
+ memcpy(__skb_put(skb, flowclen), flowc, flowclen);
+ skb_set_queue_mapping(skb, (csk->txq_idx << 1) | CPL_PRIORITY_DATA);
+
+ return skb;
+}
+
+static int send_flowc_wr(struct sock *sk, struct fw_flowc_wr *flowc,
+ int flowclen)
+{
+ struct chtls_sock *csk = rcu_dereference_sk_user_data(sk);
+ struct tcp_sock *tp = tcp_sk(sk);
+ struct sk_buff *skb;
+ int flowclen16;
+ int ret;
+
+ flowclen16 = flowclen / 16;
+
+ if (csk_flag(sk, CSK_TX_DATA_SENT)) {
+ skb = create_flowc_wr_skb(sk, flowc, flowclen);
+ if (!skb)
+ return -ENOMEM;
+
+ skb_entail(sk, skb,
+ ULPCB_FLAG_NO_HDR | ULPCB_FLAG_NO_APPEND);
+ return 0;
+ }
+
+ ret = cxgb4_immdata_send(csk->egress_dev,
+ csk->txq_idx,
+ flowc, flowclen);
+ if (!ret)
+ return flowclen16;
+ skb = create_flowc_wr_skb(sk, flowc, flowclen);
+ if (!skb)
+ return -ENOMEM;
+ send_or_defer(sk, tp, skb, 0);
+ return flowclen16;
+}
+
+static u8 tcp_state_to_flowc_state(u8 state)
+{
+ switch (state) {
+ case TCP_ESTABLISHED:
+ return FW_FLOWC_MNEM_TCPSTATE_ESTABLISHED;
+ case TCP_CLOSE_WAIT:
+ return FW_FLOWC_MNEM_TCPSTATE_CLOSEWAIT;
+ case TCP_FIN_WAIT1:
+ return FW_FLOWC_MNEM_TCPSTATE_FINWAIT1;
+ case TCP_CLOSING:
+ return FW_FLOWC_MNEM_TCPSTATE_CLOSING;
+ case TCP_LAST_ACK:
+ return FW_FLOWC_MNEM_TCPSTATE_LASTACK;
+ case TCP_FIN_WAIT2:
+ return FW_FLOWC_MNEM_TCPSTATE_FINWAIT2;
+ }
+
+ return FW_FLOWC_MNEM_TCPSTATE_ESTABLISHED;
+}
+
+int send_tx_flowc_wr(struct sock *sk, int compl,
+ u32 snd_nxt, u32 rcv_nxt)
+{
+ struct flowc_packed {
+ struct fw_flowc_wr fc;
+ struct fw_flowc_mnemval mnemval[FW_FLOWC_MNEM_MAX];
+ } __packed sflowc;
+ int nparams, paramidx, flowclen16, flowclen;
+ struct fw_flowc_wr *flowc;
+ struct chtls_sock *csk;
+ struct tcp_sock *tp;
+
+ csk = rcu_dereference_sk_user_data(sk);
+ tp = tcp_sk(sk);
+ memset(&sflowc, 0, sizeof(sflowc));
+ flowc = &sflowc.fc;
+
+#define FLOWC_PARAM(__m, __v) \
+ do { \
+ flowc->mnemval[paramidx].mnemonic = FW_FLOWC_MNEM_##__m; \
+ flowc->mnemval[paramidx].val = cpu_to_be32(__v); \
+ paramidx++; \
+ } while (0)
+
+ paramidx = 0;
+
+ FLOWC_PARAM(PFNVFN, FW_PFVF_CMD_PFN_V(csk->cdev->lldi->pf));
+ FLOWC_PARAM(CH, csk->tx_chan);
+ FLOWC_PARAM(PORT, csk->tx_chan);
+ FLOWC_PARAM(IQID, csk->rss_qid);
+ FLOWC_PARAM(SNDNXT, tp->snd_nxt);
+ FLOWC_PARAM(RCVNXT, tp->rcv_nxt);
+ FLOWC_PARAM(SNDBUF, csk->sndbuf);
+ FLOWC_PARAM(MSS, tp->mss_cache);
+ FLOWC_PARAM(TCPSTATE, tcp_state_to_flowc_state(sk->sk_state));
+
+ if (SND_WSCALE(tp))
+ FLOWC_PARAM(RCV_SCALE, SND_WSCALE(tp));
+
+ if (csk->ulp_mode == ULP_MODE_TLS)
+ FLOWC_PARAM(ULD_MODE, ULP_MODE_TLS);
+
+ if (csk->tlshws.fcplenmax)
+ FLOWC_PARAM(TXDATAPLEN_MAX, csk->tlshws.fcplenmax);
+
+ nparams = paramidx;
+#undef FLOWC_PARAM
+
+ flowclen16 = flowc_wr_credits(nparams, &flowclen);
+ flowc->op_to_nparams =
+ cpu_to_be32(FW_WR_OP_V(FW_FLOWC_WR) |
+ FW_WR_COMPL_V(compl) |
+ FW_FLOWC_WR_NPARAMS_V(nparams));
+ flowc->flowid_len16 = cpu_to_be32(FW_WR_LEN16_V(flowclen16) |
+ FW_WR_FLOWID_V(csk->tid));
+
+ return send_flowc_wr(sk, flowc, flowclen);
+}
+
+/* Copy IVs to WR */
+static int tls_copy_ivs(struct sock *sk, struct sk_buff *skb)
+
+{
+ struct chtls_sock *csk;
+ unsigned char *iv_loc;
+ struct chtls_hws *hws;
+ unsigned char *ivs;
+ u16 number_of_ivs;
+ struct page *page;
+ int err = 0;
+
+ csk = rcu_dereference_sk_user_data(sk);
+ hws = &csk->tlshws;
+ number_of_ivs = nos_ivs(sk, skb->len);
+
+ if (number_of_ivs > MAX_IVS_PAGE) {
+ pr_warn("MAX IVs in PAGE exceeded %d\n", number_of_ivs);
+ return -ENOMEM;
+ }
+
+ /* generate the IVs */
+ ivs = kmalloc(number_of_ivs * CIPHER_BLOCK_SIZE, GFP_ATOMIC);
+ if (!ivs)
+ return -ENOMEM;
+ get_random_bytes(ivs, number_of_ivs * CIPHER_BLOCK_SIZE);
+
+ if (skb_ulp_tls_iv_imm(skb)) {
+ /* send the IVs as immediate data in the WR */
+ iv_loc = (unsigned char *)__skb_push(skb, number_of_ivs *
+ CIPHER_BLOCK_SIZE);
+ if (iv_loc)
+ memcpy(iv_loc, ivs, number_of_ivs * CIPHER_BLOCK_SIZE);
+
+ hws->ivsize = number_of_ivs * CIPHER_BLOCK_SIZE;
+ } else {
+ /* Send the IVs as sgls */
+ /* Already accounted IV DSGL for credits */
+ skb_shinfo(skb)->nr_frags--;
+ page = alloc_pages(sk->sk_allocation | __GFP_COMP, 0);
+ if (!page) {
+ pr_info("%s : Page allocation for IVs failed\n",
+ __func__);
+ err = -ENOMEM;
+ goto out;
+ }
+ memcpy(page_address(page), ivs, number_of_ivs *
+ CIPHER_BLOCK_SIZE);
+ skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page, 0,
+ number_of_ivs * CIPHER_BLOCK_SIZE);
+ hws->ivsize = 0;
+ }
+out:
+ kfree(ivs);
+ return err;
+}
+
+/* Copy Key to WR */
+static void tls_copy_tx_key(struct sock *sk, struct sk_buff *skb)
+{
+ struct ulptx_sc_memrd *sc_memrd;
+ struct chtls_sock *csk;
+ struct chtls_dev *cdev;
+ struct ulptx_idata *sc;
+ struct chtls_hws *hws;
+ u32 immdlen;
+ int kaddr;
+
+ csk = rcu_dereference_sk_user_data(sk);
+ hws = &csk->tlshws;
+ cdev = csk->cdev;
+
+ immdlen = sizeof(*sc) + sizeof(*sc_memrd);
+ kaddr = keyid_to_addr(cdev->kmap.start, hws->txkey);
+ sc = (struct ulptx_idata *)__skb_push(skb, immdlen);
+ if (sc) {
+ sc->cmd_more = htonl(ULPTX_CMD_V(ULP_TX_SC_NOOP));
+ sc->len = htonl(0);
+ sc_memrd = (struct ulptx_sc_memrd *)(sc + 1);
+ sc_memrd->cmd_to_len =
+ htonl(ULPTX_CMD_V(ULP_TX_SC_MEMRD) |
+ ULP_TX_SC_MORE_V(1) |
+ ULPTX_LEN16_V(hws->keylen >> 4));
+ sc_memrd->addr = htonl(kaddr);
+ }
+}
+
+static u64 tlstx_incr_seqnum(struct chtls_hws *hws)
+{
+ return hws->tx_seq_no++;
+}
+
+static bool is_sg_request(const struct sk_buff *skb)
+{
+ return skb->peeked ||
+ (skb->len > MAX_IMM_ULPTX_WR_LEN);
+}
+
+/*
+ * Returns true if an sk_buff carries urgent data.
+ */
+static bool skb_urgent(struct sk_buff *skb)
+{
+ return ULP_SKB_CB(skb)->flags & ULPCB_FLAG_URG;
+}
+
+/* TLS content type for CPL SFO */
+static unsigned char tls_content_type(unsigned char content_type)
+{
+ switch (content_type) {
+ case TLS_HDR_TYPE_CCS:
+ return CPL_TX_TLS_SFO_TYPE_CCS;
+ case TLS_HDR_TYPE_ALERT:
+ return CPL_TX_TLS_SFO_TYPE_ALERT;
+ case TLS_HDR_TYPE_HANDSHAKE:
+ return CPL_TX_TLS_SFO_TYPE_HANDSHAKE;
+ case TLS_HDR_TYPE_HEARTBEAT:
+ return CPL_TX_TLS_SFO_TYPE_HEARTBEAT;
+ }
+ return CPL_TX_TLS_SFO_TYPE_DATA;
+}
+
+static void tls_tx_data_wr(struct sock *sk, struct sk_buff *skb,
+ int dlen, int tls_immd, u32 credits,
+ int expn, int pdus)
+{
+ struct fw_tlstx_data_wr *req_wr;
+ struct cpl_tx_tls_sfo *req_cpl;
+ unsigned int wr_ulp_mode_force;
+ struct tls_scmd *updated_scmd;
+ unsigned char data_type;
+ struct chtls_sock *csk;
+ struct net_device *dev;
+ struct chtls_hws *hws;
+ struct tls_scmd *scmd;
+ struct adapter *adap;
+ unsigned char *req;
+ int immd_len;
+ int iv_imm;
+ int len;
+
+ csk = rcu_dereference_sk_user_data(sk);
+ iv_imm = skb_ulp_tls_iv_imm(skb);
+ dev = csk->egress_dev;
+ adap = netdev2adap(dev);
+ hws = &csk->tlshws;
+ scmd = &hws->scmd;
+ len = dlen + expn;
+
+ dlen = (dlen < hws->mfs) ? dlen : hws->mfs;
+ atomic_inc(&adap->chcr_stats.tls_pdu_tx);
+
+ updated_scmd = scmd;
+ updated_scmd->seqno_numivs &= 0xffffff80;
+ updated_scmd->seqno_numivs |= SCMD_NUM_IVS_V(pdus);
+ hws->scmd = *updated_scmd;
+
+ req = (unsigned char *)__skb_push(skb, sizeof(struct cpl_tx_tls_sfo));
+ req_cpl = (struct cpl_tx_tls_sfo *)req;
+ req = (unsigned char *)__skb_push(skb, (sizeof(struct
+ fw_tlstx_data_wr)));
+
+ req_wr = (struct fw_tlstx_data_wr *)req;
+ immd_len = (tls_immd ? dlen : 0);
+ req_wr->op_to_immdlen =
+ htonl(FW_WR_OP_V(FW_TLSTX_DATA_WR) |
+ FW_TLSTX_DATA_WR_COMPL_V(1) |
+ FW_TLSTX_DATA_WR_IMMDLEN_V(immd_len));
+ req_wr->flowid_len16 = htonl(FW_TLSTX_DATA_WR_FLOWID_V(csk->tid) |
+ FW_TLSTX_DATA_WR_LEN16_V(credits));
+ wr_ulp_mode_force = TX_ULP_MODE_V(ULP_MODE_TLS);
+
+ if (is_sg_request(skb))
+ wr_ulp_mode_force |= FW_OFLD_TX_DATA_WR_ALIGNPLD_F |
+ ((tcp_sk(sk)->nonagle & TCP_NAGLE_OFF) ? 0 :
+ FW_OFLD_TX_DATA_WR_SHOVE_F);
+
+ req_wr->lsodisable_to_flags =
+ htonl(TX_ULP_MODE_V(ULP_MODE_TLS) |
+ FW_OFLD_TX_DATA_WR_URGENT_V(skb_urgent(skb)) |
+ T6_TX_FORCE_F | wr_ulp_mode_force |
+ TX_SHOVE_V((!csk_flag(sk, CSK_TX_MORE_DATA)) &&
+ skb_queue_empty(&csk->txq)));
+
+ req_wr->ctxloc_to_exp =
+ htonl(FW_TLSTX_DATA_WR_NUMIVS_V(pdus) |
+ FW_TLSTX_DATA_WR_EXP_V(expn) |
+ FW_TLSTX_DATA_WR_CTXLOC_V(CHTLS_KEY_CONTEXT_DDR) |
+ FW_TLSTX_DATA_WR_IVDSGL_V(!iv_imm) |
+ FW_TLSTX_DATA_WR_KEYSIZE_V(hws->keylen >> 4));
+
+ /* Fill in the length */
+ req_wr->plen = htonl(len);
+ req_wr->mfs = htons(hws->mfs);
+ req_wr->adjustedplen_pkd =
+ htons(FW_TLSTX_DATA_WR_ADJUSTEDPLEN_V(hws->adjustlen));
+ req_wr->expinplenmax_pkd =
+ htons(FW_TLSTX_DATA_WR_EXPINPLENMAX_V(hws->expansion));
+ req_wr->pdusinplenmax_pkd =
+ FW_TLSTX_DATA_WR_PDUSINPLENMAX_V(hws->pdus);
+ req_wr->r10 = 0;
+
+ data_type = tls_content_type(ULP_SKB_CB(skb)->ulp.tls.type);
+ req_cpl->op_to_seg_len = htonl(CPL_TX_TLS_SFO_OPCODE_V(CPL_TX_TLS_SFO) |
+ CPL_TX_TLS_SFO_DATA_TYPE_V(data_type) |
+ CPL_TX_TLS_SFO_CPL_LEN_V(2) |
+ CPL_TX_TLS_SFO_SEG_LEN_V(dlen));
+ req_cpl->pld_len = htonl(len - expn);
+
+ req_cpl->type_protover = htonl(CPL_TX_TLS_SFO_TYPE_V
+ ((data_type == CPL_TX_TLS_SFO_TYPE_HEARTBEAT) ?
+ TLS_HDR_TYPE_HEARTBEAT : 0) |
+ CPL_TX_TLS_SFO_PROTOVER_V(0));
+
+ /* create the s-command */
+ req_cpl->r1_lo = 0;
+ req_cpl->seqno_numivs = cpu_to_be32(hws->scmd.seqno_numivs);
+ req_cpl->ivgen_hdrlen = cpu_to_be32(hws->scmd.ivgen_hdrlen);
+ req_cpl->scmd1 = cpu_to_be64(tlstx_incr_seqnum(hws));
+}
+
+/*
+ * Calculate the TLS data expansion size
+ */
+static int chtls_expansion_size(struct sock *sk, int data_len,
+ int fullpdu,
+ unsigned short *pducnt)
+{
+ struct chtls_sock *csk = rcu_dereference_sk_user_data(sk);
+ struct chtls_hws *hws = &csk->tlshws;
+ struct tls_scmd *scmd = &hws->scmd;
+ int fragsize = hws->mfs;
+ int expnsize = 0;
+ int fragleft;
+ int fragcnt;
+ int expppdu;
+
+ if (SCMD_CIPH_MODE_G(scmd->seqno_numivs) ==
+ SCMD_CIPH_MODE_AES_GCM) {
+ expppdu = GCM_TAG_SIZE + AEAD_EXPLICIT_DATA_SIZE +
+ TLS_HEADER_LENGTH;
+
+ if (fullpdu) {
+ *pducnt = data_len / (expppdu + fragsize);
+ if (*pducnt > 32)
+ *pducnt = 32;
+ else if (!*pducnt)
+ *pducnt = 1;
+ expnsize = (*pducnt) * expppdu;
+ return expnsize;
+ }
+ fragcnt = (data_len / fragsize);
+ expnsize = fragcnt * expppdu;
+ fragleft = data_len % fragsize;
+ if (fragleft > 0)
+ expnsize += expppdu;
+ }
+ return expnsize;
+}
+
+/* WR with IV, KEY and CPL SFO added */
+static void make_tlstx_data_wr(struct sock *sk, struct sk_buff *skb,
+ int tls_tx_imm, int tls_len, u32 credits)
+{
+ unsigned short pdus_per_ulp = 0;
+ struct chtls_sock *csk;
+ struct chtls_hws *hws;
+ int expn_sz;
+ int pdus;
+
+ csk = rcu_dereference_sk_user_data(sk);
+ hws = &csk->tlshws;
+ pdus = DIV_ROUND_UP(tls_len, hws->mfs);
+ expn_sz = chtls_expansion_size(sk, tls_len, 0, NULL);
+ if (!hws->compute) {
+ hws->expansion = chtls_expansion_size(sk,
+ hws->fcplenmax,
+ 1, &pdus_per_ulp);
+ hws->pdus = pdus_per_ulp;
+ hws->adjustlen = hws->pdus *
+ ((hws->expansion / hws->pdus) + hws->mfs);
+ hws->compute = 1;
+ }
+ if (tls_copy_ivs(sk, skb))
+ return;
+ tls_copy_tx_key(sk, skb);
+ tls_tx_data_wr(sk, skb, tls_len, tls_tx_imm, credits, expn_sz, pdus);
+ hws->tx_seq_no += (pdus - 1);
+}
+
+static void make_tx_data_wr(struct sock *sk, struct sk_buff *skb,
+ unsigned int immdlen, int len,
+ u32 credits, u32 compl)
+{
+ struct fw_ofld_tx_data_wr *req;
+ unsigned int wr_ulp_mode_force;
+ struct chtls_sock *csk;
+ unsigned int opcode;
+
+ csk = rcu_dereference_sk_user_data(sk);
+ opcode = FW_OFLD_TX_DATA_WR;
+
+ req = (struct fw_ofld_tx_data_wr *)__skb_push(skb, sizeof(*req));
+ req->op_to_immdlen = htonl(WR_OP_V(opcode) |
+ FW_WR_COMPL_V(compl) |
+ FW_WR_IMMDLEN_V(immdlen));
+ req->flowid_len16 = htonl(FW_WR_FLOWID_V(csk->tid) |
+ FW_WR_LEN16_V(credits));
+
+ wr_ulp_mode_force = TX_ULP_MODE_V(csk->ulp_mode);
+ if (is_sg_request(skb))
+ wr_ulp_mode_force |= FW_OFLD_TX_DATA_WR_ALIGNPLD_F |
+ ((tcp_sk(sk)->nonagle & TCP_NAGLE_OFF) ? 0 :
+ FW_OFLD_TX_DATA_WR_SHOVE_F);
+
+ req->tunnel_to_proxy = htonl(wr_ulp_mode_force |
+ FW_OFLD_TX_DATA_WR_URGENT_V(skb_urgent(skb)) |
+ FW_OFLD_TX_DATA_WR_SHOVE_V((!csk_flag
+ (sk, CSK_TX_MORE_DATA)) &&
+ skb_queue_empty(&csk->txq)));
+ req->plen = htonl(len);
+}
+
+static int chtls_wr_size(struct chtls_sock *csk, const struct sk_buff *skb,
+ bool size)
+{
+ int wr_size;
+
+ wr_size = TLS_WR_CPL_LEN;
+ wr_size += KEY_ON_MEM_SZ;
+ wr_size += ivs_size(csk->sk, skb);
+
+ if (size)
+ return wr_size;
+
+ /* frags counted for IV dsgl */
+ if (!skb_ulp_tls_iv_imm(skb))
+ skb_shinfo(skb)->nr_frags++;
+
+ return wr_size;
+}
+
+static bool is_ofld_imm(struct chtls_sock *csk, const struct sk_buff *skb)
+{
+ int length = skb->len;
+
+ if (skb->peeked || skb->len > MAX_IMM_ULPTX_WR_LEN)
+ return false;
+
+ if (likely(ULP_SKB_CB(skb)->flags & ULPCB_FLAG_NEED_HDR)) {
+ /* Check TLS header len for Immediate */
+ if (csk->ulp_mode == ULP_MODE_TLS &&
+ skb_ulp_tls_inline(skb))
+ length += chtls_wr_size(csk, skb, true);
+ else
+ length += sizeof(struct fw_ofld_tx_data_wr);
+
+ return length <= MAX_IMM_OFLD_TX_DATA_WR_LEN;
+ }
+ return true;
+}
+
+static unsigned int calc_tx_flits(const struct sk_buff *skb,
+ unsigned int immdlen)
+{
+ unsigned int flits, cnt;
+
+ flits = immdlen / 8; /* headers */
+ cnt = skb_shinfo(skb)->nr_frags;
+ if (skb_tail_pointer(skb) != skb_transport_header(skb))
+ cnt++;
+ return flits + sgl_len(cnt);
+}
+
+static void arp_failure_discard(void *handle, struct sk_buff *skb)
+{
+ kfree_skb(skb);
+}
+
+int chtls_push_frames(struct chtls_sock *csk, int comp)
+{
+ struct chtls_hws *hws = &csk->tlshws;
+ struct tcp_sock *tp;
+ struct sk_buff *skb;
+ int total_size = 0;
+ struct sock *sk;
+ int wr_size;
+
+ wr_size = sizeof(struct fw_ofld_tx_data_wr);
+ sk = csk->sk;
+ tp = tcp_sk(sk);
+
+ if (unlikely(sk_in_state(sk, TCPF_SYN_SENT | TCPF_CLOSE)))
+ return 0;
+
+ if (unlikely(csk_flag(sk, CSK_ABORT_SHUTDOWN)))
+ return 0;
+
+ while (csk->wr_credits && (skb = skb_peek(&csk->txq)) &&
+ (!(ULP_SKB_CB(skb)->flags & ULPCB_FLAG_HOLD) ||
+ skb_queue_len(&csk->txq) > 1)) {
+ unsigned int credit_len = skb->len;
+ unsigned int credits_needed;
+ unsigned int completion = 0;
+ int tls_len = skb->len;/* TLS data len before IV/key */
+ unsigned int immdlen;
+ int len = skb->len; /* length [ulp bytes] inserted by hw */
+ int flowclen16 = 0;
+ int tls_tx_imm = 0;
+
+ immdlen = skb->len;
+ if (!is_ofld_imm(csk, skb)) {
+ immdlen = skb_transport_offset(skb);
+ if (skb_ulp_tls_inline(skb))
+ wr_size = chtls_wr_size(csk, skb, false);
+ credit_len = 8 * calc_tx_flits(skb, immdlen);
+ } else {
+ if (skb_ulp_tls_inline(skb)) {
+ wr_size = chtls_wr_size(csk, skb, false);
+ tls_tx_imm = 1;
+ }
+ }
+ if (likely(ULP_SKB_CB(skb)->flags & ULPCB_FLAG_NEED_HDR))
+ credit_len += wr_size;
+ credits_needed = DIV_ROUND_UP(credit_len, 16);
+ if (!csk_flag_nochk(csk, CSK_TX_DATA_SENT)) {
+ flowclen16 = send_tx_flowc_wr(sk, 1, tp->snd_nxt,
+ tp->rcv_nxt);
+ if (flowclen16 <= 0)
+ break;
+ csk->wr_credits -= flowclen16;
+ csk->wr_unacked += flowclen16;
+ csk->wr_nondata += flowclen16;
+ csk_set_flag(csk, CSK_TX_DATA_SENT);
+ }
+
+ if (csk->wr_credits < credits_needed) {
+ if (skb_ulp_tls_inline(skb) &&
+ !skb_ulp_tls_iv_imm(skb))
+ skb_shinfo(skb)->nr_frags--;
+ break;
+ }
+
+ __skb_unlink(skb, &csk->txq);
+ skb_set_queue_mapping(skb, (csk->txq_idx << 1) |
+ CPL_PRIORITY_DATA);
+ if (hws->ofld)
+ hws->txqid = (skb->queue_mapping >> 1);
+ skb->csum = (__force __wsum)(credits_needed + csk->wr_nondata);
+ csk->wr_credits -= credits_needed;
+ csk->wr_unacked += credits_needed;
+ csk->wr_nondata = 0;
+ enqueue_wr(csk, skb);
+
+ if (likely(ULP_SKB_CB(skb)->flags & ULPCB_FLAG_NEED_HDR)) {
+ if ((comp && csk->wr_unacked == credits_needed) ||
+ (ULP_SKB_CB(skb)->flags & ULPCB_FLAG_COMPL) ||
+ csk->wr_unacked >= csk->wr_max_credits / 2) {
+ completion = 1;
+ csk->wr_unacked = 0;
+ }
+ if (skb_ulp_tls_inline(skb))
+ make_tlstx_data_wr(sk, skb, tls_tx_imm,
+ tls_len, credits_needed);
+ else
+ make_tx_data_wr(sk, skb, immdlen, len,
+ credits_needed, completion);
+ tp->snd_nxt += len;
+ tp->lsndtime = tcp_time_stamp(tp);
+ if (completion)
+ ULP_SKB_CB(skb)->flags &= ~ULPCB_FLAG_NEED_HDR;
+ } else {
+ struct cpl_close_con_req *req = cplhdr(skb);
+ unsigned int cmd = CPL_OPCODE_G(ntohl
+ (OPCODE_TID(req)));
+
+ if (cmd == CPL_CLOSE_CON_REQ)
+ csk_set_flag(csk,
+ CSK_CLOSE_CON_REQUESTED);
+
+ if ((ULP_SKB_CB(skb)->flags & ULPCB_FLAG_COMPL) &&
+ (csk->wr_unacked >= csk->wr_max_credits / 2)) {
+ req->wr.wr_hi |= htonl(FW_WR_COMPL_F);
+ csk->wr_unacked = 0;
+ }
+ }
+ total_size += skb->truesize;
+ if (ULP_SKB_CB(skb)->flags & ULPCB_FLAG_BARRIER)
+ csk_set_flag(csk, CSK_TX_WAIT_IDLE);
+ t4_set_arp_err_handler(skb, NULL, arp_failure_discard);
+ cxgb4_l2t_send(csk->egress_dev, skb, csk->l2t_entry);
+ }
+ sk->sk_wmem_queued -= total_size;
+ return total_size;
+}
+
+static void mark_urg(struct tcp_sock *tp, int flags,
+ struct sk_buff *skb)
+{
+ if (unlikely(flags & MSG_OOB)) {
+ tp->snd_up = tp->write_seq;
+ ULP_SKB_CB(skb)->flags = ULPCB_FLAG_URG |
+ ULPCB_FLAG_BARRIER |
+ ULPCB_FLAG_NO_APPEND |
+ ULPCB_FLAG_NEED_HDR;
+ }
+}
+
+/*
+ * Returns true if a connection should send more data to TCP engine
+ */
+static bool should_push(struct sock *sk)
+{
+ struct chtls_sock *csk = rcu_dereference_sk_user_data(sk);
+ struct chtls_dev *cdev = csk->cdev;
+ struct tcp_sock *tp = tcp_sk(sk);
+
+ /*
+ * If we've released our offload resources there's nothing to do ...
+ */
+ if (!cdev)
+ return false;
+
+ /*
+ * If there aren't any work requests in flight, or there isn't enough
+ * data in flight, or Nagle is off then send the current TX_DATA
+ * otherwise hold it and wait to accumulate more data.
+ */
+ return csk->wr_credits == csk->wr_max_credits ||
+ (tp->nonagle & TCP_NAGLE_OFF);
+}
+
+/*
+ * Returns true if a TCP socket is corked.
+ */
+static bool corked(const struct tcp_sock *tp, int flags)
+{
+ return (flags & MSG_MORE) || (tp->nonagle & TCP_NAGLE_CORK);
+}
+
+/*
+ * Returns true if a send should try to push new data.
+ */
+static bool send_should_push(struct sock *sk, int flags)
+{
+ return should_push(sk) && !corked(tcp_sk(sk), flags);
+}
+
+void chtls_tcp_push(struct sock *sk, int flags)
+{
+ struct chtls_sock *csk = rcu_dereference_sk_user_data(sk);
+ int qlen = skb_queue_len(&csk->txq);
+
+ if (likely(qlen)) {
+ struct sk_buff *skb = skb_peek_tail(&csk->txq);
+ struct tcp_sock *tp = tcp_sk(sk);
+
+ mark_urg(tp, flags, skb);
+
+ if (!(ULP_SKB_CB(skb)->flags & ULPCB_FLAG_NO_APPEND) &&
+ corked(tp, flags)) {
+ ULP_SKB_CB(skb)->flags |= ULPCB_FLAG_HOLD;
+ return;
+ }
+
+ ULP_SKB_CB(skb)->flags &= ~ULPCB_FLAG_HOLD;
+ if (qlen == 1 &&
+ ((ULP_SKB_CB(skb)->flags & ULPCB_FLAG_NO_APPEND) ||
+ should_push(sk)))
+ chtls_push_frames(csk, 1);
+ }
+}
+
+/*
+ * Calculate the size for a new send sk_buff. It's maximum size so we can
+ * pack lots of data into it, unless we plan to send it immediately, in which
+ * case we size it more tightly.
+ *
+ * Note: we don't bother compensating for MSS < PAGE_SIZE because it doesn't
+ * arise in normal cases and when it does we are just wasting memory.
+ */
+static int select_size(struct sock *sk, int io_len, int flags, int len)
+{
+ const int pgbreak = SKB_MAX_HEAD(len);
+
+ /*
+ * If the data wouldn't fit in the main body anyway, put only the
+ * header in the main body so it can use immediate data and place all
+ * the payload in page fragments.
+ */
+ if (io_len > pgbreak)
+ return 0;
+
+ /*
+ * If we will be accumulating payload get a large main body.
+ */
+ if (!send_should_push(sk, flags))
+ return pgbreak;
+
+ return io_len;
+}
+
+void skb_entail(struct sock *sk, struct sk_buff *skb, int flags)
+{
+ struct chtls_sock *csk = rcu_dereference_sk_user_data(sk);
+ struct tcp_sock *tp = tcp_sk(sk);
+
+ ULP_SKB_CB(skb)->seq = tp->write_seq;
+ ULP_SKB_CB(skb)->flags = flags;
+ __skb_queue_tail(&csk->txq, skb);
+ sk->sk_wmem_queued += skb->truesize;
+
+ if (TCP_PAGE(sk) && TCP_OFF(sk)) {
+ put_page(TCP_PAGE(sk));
+ TCP_PAGE(sk) = NULL;
+ TCP_OFF(sk) = 0;
+ }
+}
+
+static struct sk_buff *get_tx_skb(struct sock *sk, int size)
+{
+ struct sk_buff *skb;
+
+ skb = alloc_skb(size + TX_HEADER_LEN, sk->sk_allocation);
+ if (likely(skb)) {
+ skb_reserve(skb, TX_HEADER_LEN);
+ skb_entail(sk, skb, ULPCB_FLAG_NEED_HDR);
+ skb_reset_transport_header(skb);
+ }
+ return skb;
+}
+
+static struct sk_buff *get_record_skb(struct sock *sk, int size, bool zcopy)
+{
+ struct chtls_sock *csk = rcu_dereference_sk_user_data(sk);
+ struct sk_buff *skb;
+
+ skb = alloc_skb(((zcopy ? 0 : size) + TX_TLSHDR_LEN +
+ KEY_ON_MEM_SZ + max_ivs_size(sk, size)),
+ sk->sk_allocation);
+ if (likely(skb)) {
+ skb_reserve(skb, (TX_TLSHDR_LEN +
+ KEY_ON_MEM_SZ + max_ivs_size(sk, size)));
+ skb_entail(sk, skb, ULPCB_FLAG_NEED_HDR);
+ skb_reset_transport_header(skb);
+ ULP_SKB_CB(skb)->ulp.tls.ofld = 1;
+ ULP_SKB_CB(skb)->ulp.tls.type = csk->tlshws.type;
+ }
+ return skb;
+}
+
+static void tx_skb_finalize(struct sk_buff *skb)
+{
+ struct ulp_skb_cb *cb = ULP_SKB_CB(skb);
+
+ if (!(cb->flags & ULPCB_FLAG_NO_HDR))
+ cb->flags = ULPCB_FLAG_NEED_HDR;
+ cb->flags |= ULPCB_FLAG_NO_APPEND;
+}
+
+static void push_frames_if_head(struct sock *sk)
+{
+ struct chtls_sock *csk = rcu_dereference_sk_user_data(sk);
+
+ if (skb_queue_len(&csk->txq) == 1)
+ chtls_push_frames(csk, 1);
+}
+
+static int chtls_skb_copy_to_page_nocache(struct sock *sk,
+ struct iov_iter *from,
+ struct sk_buff *skb,
+ struct page *page,
+ int off, int copy)
+{
+ int err;
+
+ err = skb_do_copy_data_nocache(sk, skb, from, page_address(page) +
+ off, copy, skb->len);
+ if (err)
+ return err;
+
+ skb->len += copy;
+ skb->data_len += copy;
+ skb->truesize += copy;
+ sk->sk_wmem_queued += copy;
+ return 0;
+}
+
+/* Read TLS header to find content type and data length */
+static u16 tls_header_read(struct tls_hdr *thdr, struct iov_iter *from)
+{
+ if (copy_from_iter(thdr, sizeof(*thdr), from) != sizeof(*thdr))
+ return -EFAULT;
+ return (__force u16)cpu_to_be16(thdr->length);
+}
+
+int chtls_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
+{
+ struct chtls_sock *csk = rcu_dereference_sk_user_data(sk);
+ struct chtls_dev *cdev = csk->cdev;
+ struct tcp_sock *tp = tcp_sk(sk);
+ struct sk_buff *skb;
+ int mss, flags, err;
+ int recordsz = 0;
+ int copied = 0;
+ int hdrlen = 0;
+ long timeo;
+
+ lock_sock(sk);
+ flags = msg->msg_flags;
+ timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT);
+
+ if (!sk_in_state(sk, TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) {
+ err = sk_stream_wait_connect(sk, &timeo);
+ if (err)
+ goto out_err;
+ }
+
+ sk_clear_bit(SOCKWQ_ASYNC_NOSPACE, sk);
+ err = -EPIPE;
+ if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN))
+ goto out_err;
+
+ mss = csk->mss;
+ csk_set_flag(csk, CSK_TX_MORE_DATA);
+
+ while (msg_data_left(msg)) {
+ int copy = 0;
+
+ skb = skb_peek_tail(&csk->txq);
+ if (skb) {
+ copy = mss - skb->len;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ }
+
+ if (is_tls_tx(csk) && !csk->tlshws.txleft) {
+ struct tls_hdr hdr;
+
+ recordsz = tls_header_read(&hdr, &msg->msg_iter);
+ size -= TLS_HEADER_LENGTH;
+ hdrlen += TLS_HEADER_LENGTH;
+ csk->tlshws.txleft = recordsz;
+ csk->tlshws.type = hdr.type;
+ if (skb)
+ ULP_SKB_CB(skb)->ulp.tls.type = hdr.type;
+ }
+
+ if (!skb || (ULP_SKB_CB(skb)->flags & ULPCB_FLAG_NO_APPEND) ||
+ copy <= 0) {
+new_buf:
+ if (skb) {
+ tx_skb_finalize(skb);
+ push_frames_if_head(sk);
+ }
+
+ if (is_tls_tx(csk)) {
+ skb = get_record_skb(sk,
+ select_size(sk,
+ recordsz,
+ flags,
+ TX_TLSHDR_LEN),
+ false);
+ } else {
+ skb = get_tx_skb(sk,
+ select_size(sk, size, flags,
+ TX_HEADER_LEN));
+ }
+ if (unlikely(!skb))
+ goto wait_for_memory;
+
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ copy = mss;
+ }
+ if (copy > size)
+ copy = size;
+
+ if (skb_tailroom(skb) > 0) {
+ copy = min(copy, skb_tailroom(skb));
+ if (is_tls_tx(csk))
+ copy = min_t(int, copy, csk->tlshws.txleft);
+ err = skb_add_data_nocache(sk, skb,
+ &msg->msg_iter, copy);
+ if (err)
+ goto do_fault;
+ } else {
+ int i = skb_shinfo(skb)->nr_frags;
+ struct page *page = TCP_PAGE(sk);
+ int pg_size = PAGE_SIZE;
+ int off = TCP_OFF(sk);
+ bool merge;
+
+ if (page)
+ pg_size <<= compound_order(page);
+
+ if (off < pg_size &&
+ skb_can_coalesce(skb, i, page, off)) {
+ merge = 1;
+ goto copy;
+ }
+ merge = 0;
+ if (i == (is_tls_tx(csk) ? (MAX_SKB_FRAGS - 1) :
+ MAX_SKB_FRAGS))
+ goto new_buf;
+
+ if (page && off == pg_size) {
+ put_page(page);
+ TCP_PAGE(sk) = page = NULL;
+ pg_size = PAGE_SIZE;
+ }
+
+ if (!page) {
+ gfp_t gfp = sk->sk_allocation;
+ int order = cdev->send_page_order;
+
+ if (order) {
+ page = alloc_pages(gfp | __GFP_COMP |
+ __GFP_NOWARN |
+ __GFP_NORETRY,
+ order);
+ if (page)
+ pg_size <<=
+ compound_order(page);
+ }
+ if (!page) {
+ page = alloc_page(gfp);
+ pg_size = PAGE_SIZE;
+ }
+ if (!page)
+ goto wait_for_memory;
+ off = 0;
+ }
+copy:
+ if (copy > pg_size - off)
+ copy = pg_size - off;
+ if (is_tls_tx(csk))
+ copy = min_t(int, copy, csk->tlshws.txleft);
+
+ err = chtls_skb_copy_to_page_nocache(sk, &msg->msg_iter,
+ skb, page,
+ off, copy);
+ if (unlikely(err)) {
+ if (!TCP_PAGE(sk)) {
+ TCP_PAGE(sk) = page;
+ TCP_OFF(sk) = 0;
+ }
+ goto do_fault;
+ }
+ /* Update the skb. */
+ if (merge) {
+ skb_shinfo(skb)->frags[i - 1].size += copy;
+ } else {
+ skb_fill_page_desc(skb, i, page, off, copy);
+ if (off + copy < pg_size) {
+ /* space left keep page */
+ get_page(page);
+ TCP_PAGE(sk) = page;
+ } else {
+ TCP_PAGE(sk) = NULL;
+ }
+ }
+ TCP_OFF(sk) = off + copy;
+ }
+ if (unlikely(skb->len == mss))
+ tx_skb_finalize(skb);
+ tp->write_seq += copy;
+ copied += copy;
+ size -= copy;
+
+ if (is_tls_tx(csk))
+ csk->tlshws.txleft -= copy;
+
+ if (corked(tp, flags) &&
+ (sk_stream_wspace(sk) < sk_stream_min_wspace(sk)))
+ ULP_SKB_CB(skb)->flags |= ULPCB_FLAG_NO_APPEND;
+
+ if (size == 0)
+ goto out;
+
+ if (ULP_SKB_CB(skb)->flags & ULPCB_FLAG_NO_APPEND)
+ push_frames_if_head(sk);
+ continue;
+wait_for_memory:
+ err = sk_stream_wait_memory(sk, &timeo);
+ if (err)
+ goto do_error;
+ }
+out:
+ csk_reset_flag(csk, CSK_TX_MORE_DATA);
+ if (copied)
+ chtls_tcp_push(sk, flags);
+done:
+ release_sock(sk);
+ return copied + hdrlen;
+do_fault:
+ if (!skb->len) {
+ __skb_unlink(skb, &csk->txq);
+ sk->sk_wmem_queued -= skb->truesize;
+ __kfree_skb(skb);
+ }
+do_error:
+ if (copied)
+ goto out;
+out_err:
+ if (csk_conn_inline(csk))
+ csk_reset_flag(csk, CSK_TX_MORE_DATA);
+ copied = sk_stream_error(sk, flags, err);
+ goto done;
+}
+
+int chtls_sendpage(struct sock *sk, struct page *page,
+ int offset, size_t size, int flags)
+{
+ struct chtls_sock *csk;
+ int mss, err, copied;
+ struct tcp_sock *tp;
+ long timeo;
+
+ tp = tcp_sk(sk);
+ copied = 0;
+ csk = rcu_dereference_sk_user_data(sk);
+ timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT);
+
+ err = sk_stream_wait_connect(sk, &timeo);
+ if (!sk_in_state(sk, TCPF_ESTABLISHED | TCPF_CLOSE_WAIT) &&
+ err != 0)
+ goto out_err;
+
+ mss = csk->mss;
+ csk_set_flag(csk, CSK_TX_MORE_DATA);
+
+ while (size > 0) {
+ struct sk_buff *skb = skb_peek_tail(&csk->txq);
+ int copy, i;
+
+ copy = mss - skb->len;
+ if (!skb || (ULP_SKB_CB(skb)->flags & ULPCB_FLAG_NO_APPEND) ||
+ copy <= 0) {
+new_buf:
+
+ if (is_tls_tx(csk)) {
+ skb = get_record_skb(sk,
+ select_size(sk, size,
+ flags,
+ TX_TLSHDR_LEN),
+ true);
+ } else {
+ skb = get_tx_skb(sk, 0);
+ }
+ if (!skb)
+ goto do_error;
+ copy = mss;
+ }
+ if (copy > size)
+ copy = size;
+
+ i = skb_shinfo(skb)->nr_frags;
+ if (skb_can_coalesce(skb, i, page, offset)) {
+ skb_shinfo(skb)->frags[i - 1].size += copy;
+ } else if (i < MAX_SKB_FRAGS) {
+ get_page(page);
+ skb_fill_page_desc(skb, i, page, offset, copy);
+ } else {
+ tx_skb_finalize(skb);
+ push_frames_if_head(sk);
+ goto new_buf;
+ }
+
+ skb->len += copy;
+ if (skb->len == mss)
+ tx_skb_finalize(skb);
+ skb->data_len += copy;
+ skb->truesize += copy;
+ sk->sk_wmem_queued += copy;
+ tp->write_seq += copy;
+ copied += copy;
+ offset += copy;
+ size -= copy;
+
+ if (corked(tp, flags) &&
+ (sk_stream_wspace(sk) < sk_stream_min_wspace(sk)))
+ ULP_SKB_CB(skb)->flags |= ULPCB_FLAG_NO_APPEND;
+
+ if (!size)
+ break;
+
+ if (unlikely(ULP_SKB_CB(skb)->flags & ULPCB_FLAG_NO_APPEND))
+ push_frames_if_head(sk);
+ continue;
+
+ set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
+ }
+out:
+ csk_reset_flag(csk, CSK_TX_MORE_DATA);
+ if (copied)
+ chtls_tcp_push(sk, flags);
+done:
+ release_sock(sk);
+ return copied;
+
+do_error:
+ if (copied)
+ goto out;
+
+out_err:
+ if (csk_conn_inline(csk))
+ csk_reset_flag(csk, CSK_TX_MORE_DATA);
+ copied = sk_stream_error(sk, flags, err);
+ goto done;
+}
+
+static void chtls_select_window(struct sock *sk)
+{
+ struct chtls_sock *csk = rcu_dereference_sk_user_data(sk);
+ struct tcp_sock *tp = tcp_sk(sk);
+ unsigned int wnd = tp->rcv_wnd;
+
+ wnd = max_t(unsigned int, wnd, tcp_full_space(sk));
+ wnd = max_t(unsigned int, MIN_RCV_WND, wnd);
+
+ if (wnd > MAX_RCV_WND)
+ wnd = MAX_RCV_WND;
+
+/*
+ * Check if we need to grow the receive window in response to an increase in
+ * the socket's receive buffer size. Some applications increase the buffer
+ * size dynamically and rely on the window to grow accordingly.
+ */
+
+ if (wnd > tp->rcv_wnd) {
+ tp->rcv_wup -= wnd - tp->rcv_wnd;
+ tp->rcv_wnd = wnd;
+ /* Mark the receive window as updated */
+ csk_reset_flag(csk, CSK_UPDATE_RCV_WND);
+ }
+}
+
+/*
+ * Send RX credits through an RX_DATA_ACK CPL message. We are permitted
+ * to return without sending the message in case we cannot allocate
+ * an sk_buff. Returns the number of credits sent.
+ */
+static u32 send_rx_credits(struct chtls_sock *csk, u32 credits)
+{
+ struct cpl_rx_data_ack *req;
+ struct sk_buff *skb;
+
+ skb = alloc_skb(sizeof(*req), GFP_ATOMIC);
+ if (!skb)
+ return 0;
+ __skb_put(skb, sizeof(*req));
+ req = (struct cpl_rx_data_ack *)skb->head;
+
+ set_wr_txq(skb, CPL_PRIORITY_ACK, csk->port_id);
+ INIT_TP_WR(req, csk->tid);
+ OPCODE_TID(req) = cpu_to_be32(MK_OPCODE_TID(CPL_RX_DATA_ACK,
+ csk->tid));
+ req->credit_dack = cpu_to_be32(RX_CREDITS_V(credits) |
+ RX_FORCE_ACK_F);
+ cxgb4_ofld_send(csk->cdev->ports[csk->port_id], skb);
+ return credits;
+}
+
+#define CREDIT_RETURN_STATE (TCPF_ESTABLISHED | \
+ TCPF_FIN_WAIT1 | \
+ TCPF_FIN_WAIT2)
+
+/*
+ * Called after some received data has been read. It returns RX credits
+ * to the HW for the amount of data processed.
+ */
+static void chtls_cleanup_rbuf(struct sock *sk, int copied)
+{
+ struct chtls_sock *csk = rcu_dereference_sk_user_data(sk);
+ struct tcp_sock *tp;
+ int must_send;
+ u32 credits;
+ u32 thres;
+
+ thres = 15 * 1024;
+
+ if (!sk_in_state(sk, CREDIT_RETURN_STATE))
+ return;
+
+ chtls_select_window(sk);
+ tp = tcp_sk(sk);
+ credits = tp->copied_seq - tp->rcv_wup;
+ if (unlikely(!credits))
+ return;
+
+/*
+ * For coalescing to work effectively ensure the receive window has
+ * at least 16KB left.
+ */
+ must_send = credits + 16384 >= tp->rcv_wnd;
+
+ if (must_send || credits >= thres)
+ tp->rcv_wup += send_rx_credits(csk, credits);
+}
+
+static int chtls_pt_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
+ int nonblock, int flags, int *addr_len)
+{
+ struct chtls_sock *csk = rcu_dereference_sk_user_data(sk);
+ struct net_device *dev = csk->egress_dev;
+ struct chtls_hws *hws = &csk->tlshws;
+ struct tcp_sock *tp = tcp_sk(sk);
+ struct adapter *adap;
+ unsigned long avail;
+ int buffers_freed;
+ int copied = 0;
+ int request;
+ int target;
+ long timeo;
+
+ adap = netdev2adap(dev);
+ buffers_freed = 0;
+
+ timeo = sock_rcvtimeo(sk, nonblock);
+ target = sock_rcvlowat(sk, flags & MSG_WAITALL, len);
+ request = len;
+
+ if (unlikely(csk_flag(sk, CSK_UPDATE_RCV_WND)))
+ chtls_cleanup_rbuf(sk, copied);
+
+ do {
+ struct sk_buff *skb;
+ u32 offset = 0;
+
+ if (unlikely(tp->urg_data &&
+ tp->urg_seq == tp->copied_seq)) {
+ if (copied)
+ break;
+ if (signal_pending(current)) {
+ copied = timeo ? sock_intr_errno(timeo) :
+ -EAGAIN;
+ break;
+ }
+ }
+ skb = skb_peek(&sk->sk_receive_queue);
+ if (skb)
+ goto found_ok_skb;
+ if (csk->wr_credits &&
+ skb_queue_len(&csk->txq) &&
+ chtls_push_frames(csk, csk->wr_credits ==
+ csk->wr_max_credits))
+ sk->sk_write_space(sk);
+
+ if (copied >= target && !sk->sk_backlog.tail)
+ break;
+
+ if (copied) {
+ if (sk->sk_err || sk->sk_state == TCP_CLOSE ||
+ (sk->sk_shutdown & RCV_SHUTDOWN) ||
+ signal_pending(current))
+ break;
+
+ if (!timeo)
+ break;
+ } else {
+ if (sock_flag(sk, SOCK_DONE))
+ break;
+ if (sk->sk_err) {
+ copied = sock_error(sk);
+ break;
+ }
+ if (sk->sk_shutdown & RCV_SHUTDOWN)
+ break;
+ if (sk->sk_state == TCP_CLOSE) {
+ copied = -ENOTCONN;
+ break;
+ }
+ if (!timeo) {
+ copied = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ copied = sock_intr_errno(timeo);
+ break;
+ }
+ }
+ if (sk->sk_backlog.tail) {
+ release_sock(sk);
+ lock_sock(sk);
+ chtls_cleanup_rbuf(sk, copied);
+ continue;
+ }
+
+ if (copied >= target)
+ break;
+ chtls_cleanup_rbuf(sk, copied);
+ sk_wait_data(sk, &timeo, NULL);
+ continue;
+found_ok_skb:
+ if (!skb->len) {
+ skb_dst_set(skb, NULL);
+ __skb_unlink(skb, &sk->sk_receive_queue);
+ kfree_skb(skb);
+
+ if (!copied && !timeo) {
+ copied = -EAGAIN;
+ break;
+ }
+
+ if (copied < target) {
+ release_sock(sk);
+ lock_sock(sk);
+ continue;
+ }
+ break;
+ }
+ offset = hws->copied_seq;
+ avail = skb->len - offset;
+ if (len < avail)
+ avail = len;
+
+ if (unlikely(tp->urg_data)) {
+ u32 urg_offset = tp->urg_seq - tp->copied_seq;
+
+ if (urg_offset < avail) {
+ if (urg_offset) {
+ avail = urg_offset;
+ } else if (!sock_flag(sk, SOCK_URGINLINE)) {
+ /* First byte is urgent, skip */
+ tp->copied_seq++;
+ offset++;
+ avail--;
+ if (!avail)
+ goto skip_copy;
+ }
+ }
+ }
+ if (hws->rstate == TLS_RCV_ST_READ_BODY) {
+ if (skb_copy_datagram_msg(skb, offset,
+ msg, avail)) {
+ if (!copied) {
+ copied = -EFAULT;
+ break;
+ }
+ }
+ } else {
+ struct tlsrx_cmp_hdr *tls_hdr_pkt =
+ (struct tlsrx_cmp_hdr *)skb->data;
+
+ if ((tls_hdr_pkt->res_to_mac_error &
+ TLSRX_HDR_PKT_ERROR_M))
+ tls_hdr_pkt->type = 0x7F;
+
+ /* CMP pld len is for recv seq */
+ hws->rcvpld = skb->hdr_len;
+ if (skb_copy_datagram_msg(skb, offset, msg, avail)) {
+ if (!copied) {
+ copied = -EFAULT;
+ break;
+ }
+ }
+ }
+ copied += avail;
+ len -= avail;
+ hws->copied_seq += avail;
+skip_copy:
+ if (tp->urg_data && after(tp->copied_seq, tp->urg_seq))
+ tp->urg_data = 0;
+
+ if (hws->rstate == TLS_RCV_ST_READ_BODY &&
+ (avail + offset) >= skb->len) {
+ if (likely(skb))
+ chtls_free_skb(sk, skb);
+ buffers_freed++;
+ hws->rstate = TLS_RCV_ST_READ_HEADER;
+ atomic_inc(&adap->chcr_stats.tls_pdu_rx);
+ tp->copied_seq += hws->rcvpld;
+ hws->copied_seq = 0;
+ if (copied >= target &&
+ !skb_peek(&sk->sk_receive_queue))
+ break;
+ } else {
+ if (likely(skb)) {
+ if (ULP_SKB_CB(skb)->flags &
+ ULPCB_FLAG_TLS_ND)
+ hws->rstate =
+ TLS_RCV_ST_READ_HEADER;
+ else
+ hws->rstate =
+ TLS_RCV_ST_READ_BODY;
+ chtls_free_skb(sk, skb);
+ }
+ buffers_freed++;
+ tp->copied_seq += avail;
+ hws->copied_seq = 0;
+ }
+ } while (len > 0);
+
+ if (buffers_freed)
+ chtls_cleanup_rbuf(sk, copied);
+ release_sock(sk);
+ return copied;
+}
+
+/*
+ * Peek at data in a socket's receive buffer.
+ */
+static int peekmsg(struct sock *sk, struct msghdr *msg,
+ size_t len, int nonblock, int flags)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+ u32 peek_seq, offset;
+ struct sk_buff *skb;
+ int copied = 0;
+ size_t avail; /* amount of available data in current skb */
+ long timeo;
+
+ lock_sock(sk);
+ timeo = sock_rcvtimeo(sk, nonblock);
+ peek_seq = tp->copied_seq;
+
+ do {
+ if (unlikely(tp->urg_data && tp->urg_seq == peek_seq)) {
+ if (copied)
+ break;
+ if (signal_pending(current)) {
+ copied = timeo ? sock_intr_errno(timeo) :
+ -EAGAIN;
+ break;
+ }
+ }
+
+ skb_queue_walk(&sk->sk_receive_queue, skb) {
+ offset = peek_seq - ULP_SKB_CB(skb)->seq;
+ if (offset < skb->len)
+ goto found_ok_skb;
+ }
+
+ /* empty receive queue */
+ if (copied)
+ break;
+ if (sock_flag(sk, SOCK_DONE))
+ break;
+ if (sk->sk_err) {
+ copied = sock_error(sk);
+ break;
+ }
+ if (sk->sk_shutdown & RCV_SHUTDOWN)
+ break;
+ if (sk->sk_state == TCP_CLOSE) {
+ copied = -ENOTCONN;
+ break;
+ }
+ if (!timeo) {
+ copied = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ copied = sock_intr_errno(timeo);
+ break;
+ }
+
+ if (sk->sk_backlog.tail) {
+ /* Do not sleep, just process backlog. */
+ release_sock(sk);
+ lock_sock(sk);
+ } else {
+ sk_wait_data(sk, &timeo, NULL);
+ }
+
+ if (unlikely(peek_seq != tp->copied_seq)) {
+ if (net_ratelimit())
+ pr_info("TCP(%s:%d), race in MSG_PEEK.\n",
+ current->comm, current->pid);
+ peek_seq = tp->copied_seq;
+ }
+ continue;
+
+found_ok_skb:
+ avail = skb->len - offset;
+ if (len < avail)
+ avail = len;
+ /*
+ * Do we have urgent data here? We need to skip over the
+ * urgent byte.
+ */
+ if (unlikely(tp->urg_data)) {
+ u32 urg_offset = tp->urg_seq - peek_seq;
+
+ if (urg_offset < avail) {
+ /*
+ * The amount of data we are preparing to copy
+ * contains urgent data.
+ */
+ if (!urg_offset) { /* First byte is urgent */
+ if (!sock_flag(sk, SOCK_URGINLINE)) {
+ peek_seq++;
+ offset++;
+ avail--;
+ }
+ if (!avail)
+ continue;
+ } else {
+ /* stop short of the urgent data */
+ avail = urg_offset;
+ }
+ }
+ }
+
+ /*
+ * If MSG_TRUNC is specified the data is discarded.
+ */
+ if (likely(!(flags & MSG_TRUNC)))
+ if (skb_copy_datagram_msg(skb, offset, msg, len)) {
+ if (!copied) {
+ copied = -EFAULT;
+ break;
+ }
+ }
+ peek_seq += avail;
+ copied += avail;
+ len -= avail;
+ } while (len > 0);
+
+ release_sock(sk);
+ return copied;
+}
+
+int chtls_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
+ int nonblock, int flags, int *addr_len)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+ struct chtls_sock *csk;
+ struct chtls_hws *hws;
+ unsigned long avail; /* amount of available data in current skb */
+ int buffers_freed;
+ int copied = 0;
+ int request;
+ long timeo;
+ int target; /* Read at least this many bytes */
+
+ buffers_freed = 0;
+
+ if (unlikely(flags & MSG_OOB))
+ return tcp_prot.recvmsg(sk, msg, len, nonblock, flags,
+ addr_len);
+
+ if (unlikely(flags & MSG_PEEK))
+ return peekmsg(sk, msg, len, nonblock, flags);
+
+ if (sk_can_busy_loop(sk) &&
+ skb_queue_empty(&sk->sk_receive_queue) &&
+ sk->sk_state == TCP_ESTABLISHED)
+ sk_busy_loop(sk, nonblock);
+
+ lock_sock(sk);
+ csk = rcu_dereference_sk_user_data(sk);
+ hws = &csk->tlshws;
+
+ if (is_tls_rx(csk))
+ return chtls_pt_recvmsg(sk, msg, len, nonblock,
+ flags, addr_len);
+
+ timeo = sock_rcvtimeo(sk, nonblock);
+ target = sock_rcvlowat(sk, flags & MSG_WAITALL, len);
+ request = len;
+
+ if (unlikely(csk_flag(sk, CSK_UPDATE_RCV_WND)))
+ chtls_cleanup_rbuf(sk, copied);
+
+ do {
+ struct sk_buff *skb;
+ u32 offset;
+
+ if (unlikely(tp->urg_data && tp->urg_seq == tp->copied_seq)) {
+ if (copied)
+ break;
+ if (signal_pending(current)) {
+ copied = timeo ? sock_intr_errno(timeo) :
+ -EAGAIN;
+ break;
+ }
+ }
+
+ skb = skb_peek(&sk->sk_receive_queue);
+ if (skb)
+ goto found_ok_skb;
+
+ if (csk->wr_credits &&
+ skb_queue_len(&csk->txq) &&
+ chtls_push_frames(csk, csk->wr_credits ==
+ csk->wr_max_credits))
+ sk->sk_write_space(sk);
+
+ if (copied >= target && !sk->sk_backlog.tail)
+ break;
+
+ if (copied) {
+ if (sk->sk_err || sk->sk_state == TCP_CLOSE ||
+ (sk->sk_shutdown & RCV_SHUTDOWN) ||
+ signal_pending(current))
+ break;
+ } else {
+ if (sock_flag(sk, SOCK_DONE))
+ break;
+ if (sk->sk_err) {
+ copied = sock_error(sk);
+ break;
+ }
+ if (sk->sk_shutdown & RCV_SHUTDOWN)
+ break;
+ if (sk->sk_state == TCP_CLOSE) {
+ copied = -ENOTCONN;
+ break;
+ }
+ if (!timeo) {
+ copied = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ copied = sock_intr_errno(timeo);
+ break;
+ }
+ }
+
+ if (sk->sk_backlog.tail) {
+ release_sock(sk);
+ lock_sock(sk);
+ chtls_cleanup_rbuf(sk, copied);
+ continue;
+ }
+
+ if (copied >= target)
+ break;
+ chtls_cleanup_rbuf(sk, copied);
+ sk_wait_data(sk, &timeo, NULL);
+ continue;
+
+found_ok_skb:
+ if (!skb->len) {
+ chtls_kfree_skb(sk, skb);
+ if (!copied && !timeo) {
+ copied = -EAGAIN;
+ break;
+ }
+
+ if (copied < target)
+ continue;
+
+ break;
+ }
+
+ offset = tp->copied_seq - ULP_SKB_CB(skb)->seq;
+ avail = skb->len - offset;
+ if (len < avail)
+ avail = len;
+
+ if (unlikely(tp->urg_data)) {
+ u32 urg_offset = tp->urg_seq - tp->copied_seq;
+
+ if (urg_offset < avail) {
+ if (urg_offset) {
+ avail = urg_offset;
+ } else if (!sock_flag(sk, SOCK_URGINLINE)) {
+ tp->copied_seq++;
+ offset++;
+ avail--;
+ if (!avail)
+ goto skip_copy;
+ }
+ }
+ }
+
+ if (likely(!(flags & MSG_TRUNC))) {
+ if (skb_copy_datagram_msg(skb, offset,
+ msg, avail)) {
+ if (!copied) {
+ copied = -EFAULT;
+ break;
+ }
+ }
+ }
+
+ tp->copied_seq += avail;
+ copied += avail;
+ len -= avail;
+
+skip_copy:
+ if (tp->urg_data && after(tp->copied_seq, tp->urg_seq))
+ tp->urg_data = 0;
+
+ if (avail + offset >= skb->len) {
+ if (likely(skb))
+ chtls_free_skb(sk, skb);
+ buffers_freed++;
+
+ if (copied >= target &&
+ !skb_peek(&sk->sk_receive_queue))
+ break;
+ }
+ } while (len > 0);
+
+ if (buffers_freed)
+ chtls_cleanup_rbuf(sk, copied);
+
+ release_sock(sk);
+ return copied;
+}
diff --git a/drivers/crypto/chelsio/chtls/chtls_main.c b/drivers/crypto/chelsio/chtls/chtls_main.c
new file mode 100644
index 000000000000..007c45c38fc7
--- /dev/null
+++ b/drivers/crypto/chelsio/chtls/chtls_main.c
@@ -0,0 +1,578 @@
+/*
+ * Copyright (c) 2018 Chelsio Communications, Inc.
+ *
+ * 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.
+ *
+ * Written by: Atul Gupta (atul.gupta@chelsio.com)
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/hash.h>
+#include <linux/in.h>
+#include <linux/net.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <net/tcp.h>
+#include <net/tls.h>
+
+#include "chtls.h"
+#include "chtls_cm.h"
+
+#define DRV_NAME "chtls"
+
+/*
+ * chtls device management
+ * maintains a list of the chtls devices
+ */
+static LIST_HEAD(cdev_list);
+static DEFINE_MUTEX(cdev_mutex);
+static DEFINE_MUTEX(cdev_list_lock);
+
+static DEFINE_MUTEX(notify_mutex);
+static RAW_NOTIFIER_HEAD(listen_notify_list);
+static struct proto chtls_cpl_prot;
+struct request_sock_ops chtls_rsk_ops;
+static uint send_page_order = (14 - PAGE_SHIFT < 0) ? 0 : 14 - PAGE_SHIFT;
+
+static void register_listen_notifier(struct notifier_block *nb)
+{
+ mutex_lock(&notify_mutex);
+ raw_notifier_chain_register(&listen_notify_list, nb);
+ mutex_unlock(&notify_mutex);
+}
+
+static void unregister_listen_notifier(struct notifier_block *nb)
+{
+ mutex_lock(&notify_mutex);
+ raw_notifier_chain_unregister(&listen_notify_list, nb);
+ mutex_unlock(&notify_mutex);
+}
+
+static int listen_notify_handler(struct notifier_block *this,
+ unsigned long event, void *data)
+{
+ struct chtls_dev *cdev;
+ struct sock *sk;
+ int ret;
+
+ sk = data;
+ ret = NOTIFY_DONE;
+
+ switch (event) {
+ case CHTLS_LISTEN_START:
+ case CHTLS_LISTEN_STOP:
+ mutex_lock(&cdev_list_lock);
+ list_for_each_entry(cdev, &cdev_list, list) {
+ if (event == CHTLS_LISTEN_START)
+ ret = chtls_listen_start(cdev, sk);
+ else
+ chtls_listen_stop(cdev, sk);
+ }
+ mutex_unlock(&cdev_list_lock);
+ break;
+ }
+ return ret;
+}
+
+static struct notifier_block listen_notifier = {
+ .notifier_call = listen_notify_handler
+};
+
+static int listen_backlog_rcv(struct sock *sk, struct sk_buff *skb)
+{
+ if (likely(skb_transport_header(skb) != skb_network_header(skb)))
+ return tcp_v4_do_rcv(sk, skb);
+ BLOG_SKB_CB(skb)->backlog_rcv(sk, skb);
+ return 0;
+}
+
+static int chtls_start_listen(struct sock *sk)
+{
+ int err;
+
+ if (sk->sk_protocol != IPPROTO_TCP)
+ return -EPROTONOSUPPORT;
+
+ if (sk->sk_family == PF_INET &&
+ LOOPBACK(inet_sk(sk)->inet_rcv_saddr))
+ return -EADDRNOTAVAIL;
+
+ sk->sk_backlog_rcv = listen_backlog_rcv;
+ mutex_lock(&notify_mutex);
+ err = raw_notifier_call_chain(&listen_notify_list,
+ CHTLS_LISTEN_START, sk);
+ mutex_unlock(&notify_mutex);
+ return err;
+}
+
+static void chtls_stop_listen(struct sock *sk)
+{
+ if (sk->sk_protocol != IPPROTO_TCP)
+ return;
+
+ mutex_lock(&notify_mutex);
+ raw_notifier_call_chain(&listen_notify_list,
+ CHTLS_LISTEN_STOP, sk);
+ mutex_unlock(&notify_mutex);
+}
+
+static int chtls_inline_feature(struct tls_device *dev)
+{
+ struct net_device *netdev;
+ struct chtls_dev *cdev;
+ int i;
+
+ cdev = to_chtls_dev(dev);
+
+ for (i = 0; i < cdev->lldi->nports; i++) {
+ netdev = cdev->ports[i];
+ if (netdev->features & NETIF_F_HW_TLS_RECORD)
+ return 1;
+ }
+ return 0;
+}
+
+static int chtls_create_hash(struct tls_device *dev, struct sock *sk)
+{
+ if (sk->sk_state == TCP_LISTEN)
+ return chtls_start_listen(sk);
+ return 0;
+}
+
+static void chtls_destroy_hash(struct tls_device *dev, struct sock *sk)
+{
+ if (sk->sk_state == TCP_LISTEN)
+ chtls_stop_listen(sk);
+}
+
+static void chtls_register_dev(struct chtls_dev *cdev)
+{
+ struct tls_device *tlsdev = &cdev->tlsdev;
+
+ strlcpy(tlsdev->name, "chtls", TLS_DEVICE_NAME_MAX);
+ strlcat(tlsdev->name, cdev->lldi->ports[0]->name,
+ TLS_DEVICE_NAME_MAX);
+ tlsdev->feature = chtls_inline_feature;
+ tlsdev->hash = chtls_create_hash;
+ tlsdev->unhash = chtls_destroy_hash;
+ tls_register_device(&cdev->tlsdev);
+}
+
+static void chtls_unregister_dev(struct chtls_dev *cdev)
+{
+ tls_unregister_device(&cdev->tlsdev);
+}
+
+static void process_deferq(struct work_struct *task_param)
+{
+ struct chtls_dev *cdev = container_of(task_param,
+ struct chtls_dev, deferq_task);
+ struct sk_buff *skb;
+
+ spin_lock_bh(&cdev->deferq.lock);
+ while ((skb = __skb_dequeue(&cdev->deferq)) != NULL) {
+ spin_unlock_bh(&cdev->deferq.lock);
+ DEFERRED_SKB_CB(skb)->handler(cdev, skb);
+ spin_lock_bh(&cdev->deferq.lock);
+ }
+ spin_unlock_bh(&cdev->deferq.lock);
+}
+
+static int chtls_get_skb(struct chtls_dev *cdev)
+{
+ cdev->askb = alloc_skb(sizeof(struct tcphdr), GFP_KERNEL);
+ if (!cdev->askb)
+ return -ENOMEM;
+
+ skb_put(cdev->askb, sizeof(struct tcphdr));
+ skb_reset_transport_header(cdev->askb);
+ memset(cdev->askb->data, 0, cdev->askb->len);
+ return 0;
+}
+
+static void *chtls_uld_add(const struct cxgb4_lld_info *info)
+{
+ struct cxgb4_lld_info *lldi;
+ struct chtls_dev *cdev;
+ int i, j;
+
+ cdev = kzalloc(sizeof(*cdev) + info->nports *
+ (sizeof(struct net_device *)), GFP_KERNEL);
+ if (!cdev)
+ goto out;
+
+ lldi = kzalloc(sizeof(*lldi), GFP_KERNEL);
+ if (!lldi)
+ goto out_lldi;
+
+ if (chtls_get_skb(cdev))
+ goto out_skb;
+
+ *lldi = *info;
+ cdev->lldi = lldi;
+ cdev->pdev = lldi->pdev;
+ cdev->tids = lldi->tids;
+ cdev->ports = (struct net_device **)(cdev + 1);
+ cdev->ports = lldi->ports;
+ cdev->mtus = lldi->mtus;
+ cdev->tids = lldi->tids;
+ cdev->pfvf = FW_VIID_PFN_G(cxgb4_port_viid(lldi->ports[0]))
+ << FW_VIID_PFN_S;
+
+ for (i = 0; i < (1 << RSPQ_HASH_BITS); i++) {
+ unsigned int size = 64 - sizeof(struct rsp_ctrl) - 8;
+
+ cdev->rspq_skb_cache[i] = __alloc_skb(size,
+ gfp_any(), 0,
+ lldi->nodeid);
+ if (unlikely(!cdev->rspq_skb_cache[i]))
+ goto out_rspq_skb;
+ }
+
+ idr_init(&cdev->hwtid_idr);
+ INIT_WORK(&cdev->deferq_task, process_deferq);
+ spin_lock_init(&cdev->listen_lock);
+ spin_lock_init(&cdev->idr_lock);
+ cdev->send_page_order = min_t(uint, get_order(32768),
+ send_page_order);
+
+ if (lldi->vr->key.size)
+ if (chtls_init_kmap(cdev, lldi))
+ goto out_rspq_skb;
+
+ mutex_lock(&cdev_mutex);
+ list_add_tail(&cdev->list, &cdev_list);
+ mutex_unlock(&cdev_mutex);
+
+ return cdev;
+out_rspq_skb:
+ for (j = 0; j <= i; j++)
+ kfree_skb(cdev->rspq_skb_cache[j]);
+ kfree_skb(cdev->askb);
+out_skb:
+ kfree(lldi);
+out_lldi:
+ kfree(cdev);
+out:
+ return NULL;
+}
+
+static void chtls_free_uld(struct chtls_dev *cdev)
+{
+ int i;
+
+ chtls_unregister_dev(cdev);
+ kvfree(cdev->kmap.addr);
+ idr_destroy(&cdev->hwtid_idr);
+ for (i = 0; i < (1 << RSPQ_HASH_BITS); i++)
+ kfree_skb(cdev->rspq_skb_cache[i]);
+ kfree(cdev->lldi);
+ if (cdev->askb)
+ kfree_skb(cdev->askb);
+ kfree(cdev);
+}
+
+static void chtls_free_all_uld(void)
+{
+ struct chtls_dev *cdev, *tmp;
+
+ mutex_lock(&cdev_mutex);
+ list_for_each_entry_safe(cdev, tmp, &cdev_list, list)
+ chtls_free_uld(cdev);
+ mutex_unlock(&cdev_mutex);
+}
+
+static int chtls_uld_state_change(void *handle, enum cxgb4_state new_state)
+{
+ struct chtls_dev *cdev = handle;
+
+ switch (new_state) {
+ case CXGB4_STATE_UP:
+ chtls_register_dev(cdev);
+ break;
+ case CXGB4_STATE_DOWN:
+ break;
+ case CXGB4_STATE_START_RECOVERY:
+ break;
+ case CXGB4_STATE_DETACH:
+ mutex_lock(&cdev_mutex);
+ list_del(&cdev->list);
+ mutex_unlock(&cdev_mutex);
+ chtls_free_uld(cdev);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static struct sk_buff *copy_gl_to_skb_pkt(const struct pkt_gl *gl,
+ const __be64 *rsp,
+ u32 pktshift)
+{
+ struct sk_buff *skb;
+
+ /* Allocate space for cpl_pass_accpet_req which will be synthesized by
+ * driver. Once driver synthesizes cpl_pass_accpet_req the skb will go
+ * through the regular cpl_pass_accept_req processing in TOM.
+ */
+ skb = alloc_skb(gl->tot_len + sizeof(struct cpl_pass_accept_req)
+ - pktshift, GFP_ATOMIC);
+ if (unlikely(!skb))
+ return NULL;
+ __skb_put(skb, gl->tot_len + sizeof(struct cpl_pass_accept_req)
+ - pktshift);
+ /* For now we will copy cpl_rx_pkt in the skb */
+ skb_copy_to_linear_data(skb, rsp, sizeof(struct cpl_rx_pkt));
+ skb_copy_to_linear_data_offset(skb, sizeof(struct cpl_pass_accept_req)
+ , gl->va + pktshift,
+ gl->tot_len - pktshift);
+
+ return skb;
+}
+
+static int chtls_recv_packet(struct chtls_dev *cdev,
+ const struct pkt_gl *gl, const __be64 *rsp)
+{
+ unsigned int opcode = *(u8 *)rsp;
+ struct sk_buff *skb;
+ int ret;
+
+ skb = copy_gl_to_skb_pkt(gl, rsp, cdev->lldi->sge_pktshift);
+ if (!skb)
+ return -ENOMEM;
+
+ ret = chtls_handlers[opcode](cdev, skb);
+ if (ret & CPL_RET_BUF_DONE)
+ kfree_skb(skb);
+
+ return 0;
+}
+
+static int chtls_recv_rsp(struct chtls_dev *cdev, const __be64 *rsp)
+{
+ unsigned long rspq_bin;
+ unsigned int opcode;
+ struct sk_buff *skb;
+ unsigned int len;
+ int ret;
+
+ len = 64 - sizeof(struct rsp_ctrl) - 8;
+ opcode = *(u8 *)rsp;
+
+ rspq_bin = hash_ptr((void *)rsp, RSPQ_HASH_BITS);
+ skb = cdev->rspq_skb_cache[rspq_bin];
+ if (skb && !skb_is_nonlinear(skb) &&
+ !skb_shared(skb) && !skb_cloned(skb)) {
+ refcount_inc(&skb->users);
+ if (refcount_read(&skb->users) == 2) {
+ __skb_trim(skb, 0);
+ if (skb_tailroom(skb) >= len)
+ goto copy_out;
+ }
+ refcount_dec(&skb->users);
+ }
+ skb = alloc_skb(len, GFP_ATOMIC);
+ if (unlikely(!skb))
+ return -ENOMEM;
+
+copy_out:
+ __skb_put(skb, len);
+ skb_copy_to_linear_data(skb, rsp, len);
+ skb_reset_network_header(skb);
+ skb_reset_transport_header(skb);
+ ret = chtls_handlers[opcode](cdev, skb);
+
+ if (ret & CPL_RET_BUF_DONE)
+ kfree_skb(skb);
+ return 0;
+}
+
+static void chtls_recv(struct chtls_dev *cdev,
+ struct sk_buff **skbs, const __be64 *rsp)
+{
+ struct sk_buff *skb = *skbs;
+ unsigned int opcode;
+ int ret;
+
+ opcode = *(u8 *)rsp;
+
+ __skb_push(skb, sizeof(struct rss_header));
+ skb_copy_to_linear_data(skb, rsp, sizeof(struct rss_header));
+
+ ret = chtls_handlers[opcode](cdev, skb);
+ if (ret & CPL_RET_BUF_DONE)
+ kfree_skb(skb);
+}
+
+static int chtls_uld_rx_handler(void *handle, const __be64 *rsp,
+ const struct pkt_gl *gl)
+{
+ struct chtls_dev *cdev = handle;
+ unsigned int opcode;
+ struct sk_buff *skb;
+
+ opcode = *(u8 *)rsp;
+
+ if (unlikely(opcode == CPL_RX_PKT)) {
+ if (chtls_recv_packet(cdev, gl, rsp) < 0)
+ goto nomem;
+ return 0;
+ }
+
+ if (!gl)
+ return chtls_recv_rsp(cdev, rsp);
+
+#define RX_PULL_LEN 128
+ skb = cxgb4_pktgl_to_skb(gl, RX_PULL_LEN, RX_PULL_LEN);
+ if (unlikely(!skb))
+ goto nomem;
+ chtls_recv(cdev, &skb, rsp);
+ return 0;
+
+nomem:
+ return -ENOMEM;
+}
+
+static int do_chtls_getsockopt(struct sock *sk, char __user *optval,
+ int __user *optlen)
+{
+ struct tls_crypto_info crypto_info;
+
+ crypto_info.version = TLS_1_2_VERSION;
+ if (copy_to_user(optval, &crypto_info, sizeof(struct tls_crypto_info)))
+ return -EFAULT;
+ return 0;
+}
+
+static int chtls_getsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ struct tls_context *ctx = tls_get_ctx(sk);
+
+ if (level != SOL_TLS)
+ return ctx->getsockopt(sk, level, optname, optval, optlen);
+
+ return do_chtls_getsockopt(sk, optval, optlen);
+}
+
+static int do_chtls_setsockopt(struct sock *sk, int optname,
+ char __user *optval, unsigned int optlen)
+{
+ struct tls_crypto_info *crypto_info, tmp_crypto_info;
+ struct chtls_sock *csk;
+ int keylen;
+ int rc = 0;
+
+ csk = rcu_dereference_sk_user_data(sk);
+
+ if (!optval || optlen < sizeof(*crypto_info)) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ rc = copy_from_user(&tmp_crypto_info, optval, sizeof(*crypto_info));
+ if (rc) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ /* check version */
+ if (tmp_crypto_info.version != TLS_1_2_VERSION) {
+ rc = -ENOTSUPP;
+ goto out;
+ }
+
+ crypto_info = (struct tls_crypto_info *)&csk->tlshws.crypto_info;
+
+ switch (tmp_crypto_info.cipher_type) {
+ case TLS_CIPHER_AES_GCM_128: {
+ rc = copy_from_user(crypto_info, optval,
+ sizeof(struct
+ tls12_crypto_info_aes_gcm_128));
+
+ if (rc) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ keylen = TLS_CIPHER_AES_GCM_128_KEY_SIZE;
+ rc = chtls_setkey(csk, keylen, optname);
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ goto out;
+ }
+out:
+ return rc;
+}
+
+static int chtls_setsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, unsigned int optlen)
+{
+ struct tls_context *ctx = tls_get_ctx(sk);
+
+ if (level != SOL_TLS)
+ return ctx->setsockopt(sk, level, optname, optval, optlen);
+
+ return do_chtls_setsockopt(sk, optname, optval, optlen);
+}
+
+static struct cxgb4_uld_info chtls_uld_info = {
+ .name = DRV_NAME,
+ .nrxq = MAX_ULD_QSETS,
+ .ntxq = MAX_ULD_QSETS,
+ .rxq_size = 1024,
+ .add = chtls_uld_add,
+ .state_change = chtls_uld_state_change,
+ .rx_handler = chtls_uld_rx_handler,
+};
+
+void chtls_install_cpl_ops(struct sock *sk)
+{
+ sk->sk_prot = &chtls_cpl_prot;
+}
+
+static void __init chtls_init_ulp_ops(void)
+{
+ chtls_cpl_prot = tcp_prot;
+ chtls_init_rsk_ops(&chtls_cpl_prot, &chtls_rsk_ops,
+ &tcp_prot, PF_INET);
+ chtls_cpl_prot.close = chtls_close;
+ chtls_cpl_prot.disconnect = chtls_disconnect;
+ chtls_cpl_prot.destroy = chtls_destroy_sock;
+ chtls_cpl_prot.shutdown = chtls_shutdown;
+ chtls_cpl_prot.sendmsg = chtls_sendmsg;
+ chtls_cpl_prot.sendpage = chtls_sendpage;
+ chtls_cpl_prot.recvmsg = chtls_recvmsg;
+ chtls_cpl_prot.setsockopt = chtls_setsockopt;
+ chtls_cpl_prot.getsockopt = chtls_getsockopt;
+}
+
+static int __init chtls_register(void)
+{
+ chtls_init_ulp_ops();
+ register_listen_notifier(&listen_notifier);
+ cxgb4_register_uld(CXGB4_ULD_TLS, &chtls_uld_info);
+ return 0;
+}
+
+static void __exit chtls_unregister(void)
+{
+ unregister_listen_notifier(&listen_notifier);
+ chtls_free_all_uld();
+ cxgb4_unregister_uld(CXGB4_ULD_TLS);
+}
+
+module_init(chtls_register);
+module_exit(chtls_unregister);
+
+MODULE_DESCRIPTION("Chelsio TLS Inline driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Chelsio Communications");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/crypto/inside-secure/safexcel.c b/drivers/crypto/inside-secure/safexcel.c
index 225e74a7f724..d4a81be0d7d2 100644
--- a/drivers/crypto/inside-secure/safexcel.c
+++ b/drivers/crypto/inside-secure/safexcel.c
@@ -235,7 +235,7 @@ static int safexcel_hw_setup_rdesc_rings(struct safexcel_crypto_priv *priv)
/* Configure DMA tx control */
val = EIP197_HIA_xDR_CFG_WR_CACHE(WR_CACHE_3BITS);
val |= EIP197_HIA_xDR_CFG_RD_CACHE(RD_CACHE_3BITS);
- val |= EIP197_HIA_xDR_WR_RES_BUF | EIP197_HIA_xDR_WR_CTRL_BUG;
+ val |= EIP197_HIA_xDR_WR_RES_BUF | EIP197_HIA_xDR_WR_CTRL_BUF;
writel(val,
EIP197_HIA_RDR(priv, i) + EIP197_HIA_xDR_DMA_CFG);
@@ -332,7 +332,7 @@ static int safexcel_hw_init(struct safexcel_crypto_priv *priv)
val = EIP197_HIA_DSE_CFG_DIS_DEBUG;
val |= EIP197_HIA_DxE_CFG_MIN_DATA_SIZE(7) | EIP197_HIA_DxE_CFG_MAX_DATA_SIZE(8);
val |= EIP197_HIA_DxE_CFG_DATA_CACHE_CTRL(WR_CACHE_3BITS);
- val |= EIP197_HIA_DSE_CFG_ALLWAYS_BUFFERABLE;
+ val |= EIP197_HIA_DSE_CFG_ALWAYS_BUFFERABLE;
/* FIXME: instability issues can occur for EIP97 but disabling it impact
* performances.
*/
@@ -354,7 +354,7 @@ static int safexcel_hw_init(struct safexcel_crypto_priv *priv)
val |= EIP197_PROTOCOL_ENCRYPT_ONLY | EIP197_PROTOCOL_HASH_ONLY;
val |= EIP197_ALG_AES_ECB | EIP197_ALG_AES_CBC;
val |= EIP197_ALG_SHA1 | EIP197_ALG_HMAC_SHA1;
- val |= EIP197_ALG_SHA2;
+ val |= EIP197_ALG_SHA2 | EIP197_ALG_HMAC_SHA2;
writel(val, EIP197_PE(priv) + EIP197_PE_EIP96_FUNCTION_EN);
/* Command Descriptor Rings prepare */
@@ -432,20 +432,18 @@ static int safexcel_hw_init(struct safexcel_crypto_priv *priv)
}
/* Called with ring's lock taken */
-static int safexcel_try_push_requests(struct safexcel_crypto_priv *priv,
- int ring, int reqs)
+static void safexcel_try_push_requests(struct safexcel_crypto_priv *priv,
+ int ring)
{
- int coal = min_t(int, reqs, EIP197_MAX_BATCH_SZ);
+ int coal = min_t(int, priv->ring[ring].requests, EIP197_MAX_BATCH_SZ);
if (!coal)
- return 0;
+ return;
/* Configure when we want an interrupt */
writel(EIP197_HIA_RDR_THRESH_PKT_MODE |
EIP197_HIA_RDR_THRESH_PROC_PKT(coal),
EIP197_HIA_RDR(priv, ring) + EIP197_HIA_xDR_THRESH);
-
- return coal;
}
void safexcel_dequeue(struct safexcel_crypto_priv *priv, int ring)
@@ -490,6 +488,15 @@ handle_req:
if (backlog)
backlog->complete(backlog, -EINPROGRESS);
+ /* In case the send() helper did not issue any command to push
+ * to the engine because the input data was cached, continue to
+ * dequeue other requests as this is valid and not an error.
+ */
+ if (!commands && !results) {
+ kfree(request);
+ continue;
+ }
+
spin_lock_bh(&priv->ring[ring].egress_lock);
list_add_tail(&request->list, &priv->ring[ring].list);
spin_unlock_bh(&priv->ring[ring].egress_lock);
@@ -512,14 +519,13 @@ finalize:
spin_lock_bh(&priv->ring[ring].egress_lock);
+ priv->ring[ring].requests += nreq;
+
if (!priv->ring[ring].busy) {
- nreq -= safexcel_try_push_requests(priv, ring, nreq);
- if (nreq)
- priv->ring[ring].busy = true;
+ safexcel_try_push_requests(priv, ring);
+ priv->ring[ring].busy = true;
}
- priv->ring[ring].requests_left += nreq;
-
spin_unlock_bh(&priv->ring[ring].egress_lock);
/* let the RDR know we have pending descriptors */
@@ -531,25 +537,6 @@ finalize:
EIP197_HIA_CDR(priv, ring) + EIP197_HIA_xDR_PREP_COUNT);
}
-void safexcel_free_context(struct safexcel_crypto_priv *priv,
- struct crypto_async_request *req,
- int result_sz)
-{
- struct safexcel_context *ctx = crypto_tfm_ctx(req->tfm);
-
- if (ctx->result_dma)
- dma_unmap_single(priv->dev, ctx->result_dma, result_sz,
- DMA_FROM_DEVICE);
-
- if (ctx->cache) {
- dma_unmap_single(priv->dev, ctx->cache_dma, ctx->cache_sz,
- DMA_TO_DEVICE);
- kfree(ctx->cache);
- ctx->cache = NULL;
- ctx->cache_sz = 0;
- }
-}
-
void safexcel_complete(struct safexcel_crypto_priv *priv, int ring)
{
struct safexcel_command_desc *cdesc;
@@ -623,7 +610,7 @@ static inline void safexcel_handle_result_descriptor(struct safexcel_crypto_priv
{
struct safexcel_request *sreq;
struct safexcel_context *ctx;
- int ret, i, nreq, ndesc, tot_descs, done;
+ int ret, i, nreq, ndesc, tot_descs, handled = 0;
bool should_complete;
handle_results:
@@ -659,6 +646,7 @@ handle_results:
kfree(sreq);
tot_descs += ndesc;
+ handled++;
}
acknowledge:
@@ -677,11 +665,10 @@ acknowledge:
requests_left:
spin_lock_bh(&priv->ring[ring].egress_lock);
- done = safexcel_try_push_requests(priv, ring,
- priv->ring[ring].requests_left);
+ priv->ring[ring].requests -= handled;
+ safexcel_try_push_requests(priv, ring);
- priv->ring[ring].requests_left -= done;
- if (!done && !priv->ring[ring].requests_left)
+ if (!priv->ring[ring].requests)
priv->ring[ring].busy = false;
spin_unlock_bh(&priv->ring[ring].egress_lock);
@@ -781,6 +768,8 @@ static struct safexcel_alg_template *safexcel_algs[] = {
&safexcel_alg_sha224,
&safexcel_alg_sha256,
&safexcel_alg_hmac_sha1,
+ &safexcel_alg_hmac_sha224,
+ &safexcel_alg_hmac_sha256,
};
static int safexcel_register_algorithms(struct safexcel_crypto_priv *priv)
@@ -894,29 +883,44 @@ static int safexcel_probe(struct platform_device *pdev)
return PTR_ERR(priv->base);
}
- priv->clk = of_clk_get(dev->of_node, 0);
- if (!IS_ERR(priv->clk)) {
+ priv->clk = devm_clk_get(&pdev->dev, NULL);
+ ret = PTR_ERR_OR_ZERO(priv->clk);
+ /* The clock isn't mandatory */
+ if (ret != -ENOENT) {
+ if (ret)
+ return ret;
+
ret = clk_prepare_enable(priv->clk);
if (ret) {
dev_err(dev, "unable to enable clk (%d)\n", ret);
return ret;
}
- } else {
- /* The clock isn't mandatory */
- if (PTR_ERR(priv->clk) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
+ }
+
+ priv->reg_clk = devm_clk_get(&pdev->dev, "reg");
+ ret = PTR_ERR_OR_ZERO(priv->reg_clk);
+ /* The clock isn't mandatory */
+ if (ret != -ENOENT) {
+ if (ret)
+ goto err_core_clk;
+
+ ret = clk_prepare_enable(priv->reg_clk);
+ if (ret) {
+ dev_err(dev, "unable to enable reg clk (%d)\n", ret);
+ goto err_core_clk;
+ }
}
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
if (ret)
- goto err_clk;
+ goto err_reg_clk;
priv->context_pool = dmam_pool_create("safexcel-context", dev,
sizeof(struct safexcel_context_record),
1, 0);
if (!priv->context_pool) {
ret = -ENOMEM;
- goto err_clk;
+ goto err_reg_clk;
}
safexcel_configure(priv);
@@ -931,12 +935,12 @@ static int safexcel_probe(struct platform_device *pdev)
&priv->ring[i].cdr,
&priv->ring[i].rdr);
if (ret)
- goto err_clk;
+ goto err_reg_clk;
ring_irq = devm_kzalloc(dev, sizeof(*ring_irq), GFP_KERNEL);
if (!ring_irq) {
ret = -ENOMEM;
- goto err_clk;
+ goto err_reg_clk;
}
ring_irq->priv = priv;
@@ -948,7 +952,7 @@ static int safexcel_probe(struct platform_device *pdev)
ring_irq);
if (irq < 0) {
ret = irq;
- goto err_clk;
+ goto err_reg_clk;
}
priv->ring[i].work_data.priv = priv;
@@ -959,10 +963,10 @@ static int safexcel_probe(struct platform_device *pdev)
priv->ring[i].workqueue = create_singlethread_workqueue(wq_name);
if (!priv->ring[i].workqueue) {
ret = -ENOMEM;
- goto err_clk;
+ goto err_reg_clk;
}
- priv->ring[i].requests_left = 0;
+ priv->ring[i].requests = 0;
priv->ring[i].busy = false;
crypto_init_queue(&priv->ring[i].queue,
@@ -980,18 +984,20 @@ static int safexcel_probe(struct platform_device *pdev)
ret = safexcel_hw_init(priv);
if (ret) {
dev_err(dev, "EIP h/w init failed (%d)\n", ret);
- goto err_clk;
+ goto err_reg_clk;
}
ret = safexcel_register_algorithms(priv);
if (ret) {
dev_err(dev, "Failed to register algorithms (%d)\n", ret);
- goto err_clk;
+ goto err_reg_clk;
}
return 0;
-err_clk:
+err_reg_clk:
+ clk_disable_unprepare(priv->reg_clk);
+err_core_clk:
clk_disable_unprepare(priv->clk);
return ret;
}
diff --git a/drivers/crypto/inside-secure/safexcel.h b/drivers/crypto/inside-secure/safexcel.h
index 4e219c21608b..b470a849721f 100644
--- a/drivers/crypto/inside-secure/safexcel.h
+++ b/drivers/crypto/inside-secure/safexcel.h
@@ -135,7 +135,7 @@
/* EIP197_HIA_xDR_DMA_CFG */
#define EIP197_HIA_xDR_WR_RES_BUF BIT(22)
-#define EIP197_HIA_xDR_WR_CTRL_BUG BIT(23)
+#define EIP197_HIA_xDR_WR_CTRL_BUF BIT(23)
#define EIP197_HIA_xDR_WR_OWN_BUF BIT(24)
#define EIP197_HIA_xDR_CFG_WR_CACHE(n) (((n) & 0x7) << 25)
#define EIP197_HIA_xDR_CFG_RD_CACHE(n) (((n) & 0x7) << 29)
@@ -179,7 +179,7 @@
#define EIP197_HIA_DxE_CFG_MIN_DATA_SIZE(n) ((n) << 0)
#define EIP197_HIA_DxE_CFG_DATA_CACHE_CTRL(n) (((n) & 0x7) << 4)
#define EIP197_HIA_DxE_CFG_MAX_DATA_SIZE(n) ((n) << 8)
-#define EIP197_HIA_DSE_CFG_ALLWAYS_BUFFERABLE GENMASK(15, 14)
+#define EIP197_HIA_DSE_CFG_ALWAYS_BUFFERABLE GENMASK(15, 14)
#define EIP197_HIA_DxE_CFG_MIN_CTRL_SIZE(n) ((n) << 16)
#define EIP197_HIA_DxE_CFG_CTRL_CACHE_CTRL(n) (((n) & 0x7) << 20)
#define EIP197_HIA_DxE_CFG_MAX_CTRL_SIZE(n) ((n) << 24)
@@ -525,6 +525,7 @@ struct safexcel_crypto_priv {
void __iomem *base;
struct device *dev;
struct clk *clk;
+ struct clk *reg_clk;
struct safexcel_config config;
enum safexcel_eip_version version;
@@ -551,10 +552,8 @@ struct safexcel_crypto_priv {
struct crypto_queue queue;
spinlock_t queue_lock;
- /* Number of requests in the engine that needs the threshold
- * interrupt to be set up.
- */
- int requests_left;
+ /* Number of requests in the engine. */
+ int requests;
/* The ring is currently handling at least one request */
bool busy;
@@ -580,12 +579,6 @@ struct safexcel_context {
int ring;
bool needs_inv;
bool exit_inv;
-
- /* Used for ahash requests */
- dma_addr_t result_dma;
- void *cache;
- dma_addr_t cache_dma;
- unsigned int cache_sz;
};
/*
@@ -609,9 +602,6 @@ struct safexcel_inv_result {
void safexcel_dequeue(struct safexcel_crypto_priv *priv, int ring);
void safexcel_complete(struct safexcel_crypto_priv *priv, int ring);
-void safexcel_free_context(struct safexcel_crypto_priv *priv,
- struct crypto_async_request *req,
- int result_sz);
int safexcel_invalidate_cache(struct crypto_async_request *async,
struct safexcel_crypto_priv *priv,
dma_addr_t ctxr_dma, int ring,
@@ -643,5 +633,7 @@ extern struct safexcel_alg_template safexcel_alg_sha1;
extern struct safexcel_alg_template safexcel_alg_sha224;
extern struct safexcel_alg_template safexcel_alg_sha256;
extern struct safexcel_alg_template safexcel_alg_hmac_sha1;
+extern struct safexcel_alg_template safexcel_alg_hmac_sha224;
+extern struct safexcel_alg_template safexcel_alg_hmac_sha256;
#endif
diff --git a/drivers/crypto/inside-secure/safexcel_cipher.c b/drivers/crypto/inside-secure/safexcel_cipher.c
index 63a8768ed2ae..bafb60505fab 100644
--- a/drivers/crypto/inside-secure/safexcel_cipher.c
+++ b/drivers/crypto/inside-secure/safexcel_cipher.c
@@ -58,7 +58,8 @@ static void safexcel_cipher_token(struct safexcel_cipher_ctx *ctx,
token[0].opcode = EIP197_TOKEN_OPCODE_DIRECTION;
token[0].packet_length = length;
- token[0].stat = EIP197_TOKEN_STAT_LAST_PACKET;
+ token[0].stat = EIP197_TOKEN_STAT_LAST_PACKET |
+ EIP197_TOKEN_STAT_LAST_HASH;
token[0].instructions = EIP197_TOKEN_INS_LAST |
EIP197_TOKEN_INS_TYPE_CRYTO |
EIP197_TOKEN_INS_TYPE_OUTPUT;
@@ -456,7 +457,7 @@ static int safexcel_cipher_exit_inv(struct crypto_tfm *tfm)
queue_work(priv->ring[ring].workqueue,
&priv->ring[ring].work_data.work);
- wait_for_completion_interruptible(&result.completion);
+ wait_for_completion(&result.completion);
if (result.error) {
dev_warn(priv->dev,
diff --git a/drivers/crypto/inside-secure/safexcel_hash.c b/drivers/crypto/inside-secure/safexcel_hash.c
index 122a2a58e98f..317b9e480312 100644
--- a/drivers/crypto/inside-secure/safexcel_hash.c
+++ b/drivers/crypto/inside-secure/safexcel_hash.c
@@ -21,10 +21,9 @@ struct safexcel_ahash_ctx {
struct safexcel_crypto_priv *priv;
u32 alg;
- u32 digest;
- u32 ipad[SHA1_DIGEST_SIZE / sizeof(u32)];
- u32 opad[SHA1_DIGEST_SIZE / sizeof(u32)];
+ u32 ipad[SHA256_DIGEST_SIZE / sizeof(u32)];
+ u32 opad[SHA256_DIGEST_SIZE / sizeof(u32)];
};
struct safexcel_ahash_req {
@@ -34,6 +33,9 @@ struct safexcel_ahash_req {
bool needs_inv;
int nents;
+ dma_addr_t result_dma;
+
+ u32 digest;
u8 state_sz; /* expected sate size, only set once */
u32 state[SHA256_DIGEST_SIZE / sizeof(u32)] __aligned(sizeof(u32));
@@ -42,6 +44,9 @@ struct safexcel_ahash_req {
u64 processed;
u8 cache[SHA256_BLOCK_SIZE] __aligned(sizeof(u32));
+ dma_addr_t cache_dma;
+ unsigned int cache_sz;
+
u8 cache_next[SHA256_BLOCK_SIZE] __aligned(sizeof(u32));
};
@@ -49,6 +54,8 @@ struct safexcel_ahash_export_state {
u64 len;
u64 processed;
+ u32 digest;
+
u32 state[SHA256_DIGEST_SIZE / sizeof(u32)];
u8 cache[SHA256_BLOCK_SIZE];
};
@@ -82,9 +89,9 @@ static void safexcel_context_control(struct safexcel_ahash_ctx *ctx,
cdesc->control_data.control0 |= CONTEXT_CONTROL_TYPE_HASH_OUT;
cdesc->control_data.control0 |= ctx->alg;
- cdesc->control_data.control0 |= ctx->digest;
+ cdesc->control_data.control0 |= req->digest;
- if (ctx->digest == CONTEXT_CONTROL_DIGEST_PRECOMPUTED) {
+ if (req->digest == CONTEXT_CONTROL_DIGEST_PRECOMPUTED) {
if (req->processed) {
if (ctx->alg == CONTEXT_CONTROL_CRYPTO_ALG_SHA1)
cdesc->control_data.control0 |= CONTEXT_CONTROL_SIZE(6);
@@ -112,12 +119,12 @@ static void safexcel_context_control(struct safexcel_ahash_ctx *ctx,
if (req->finish)
ctx->base.ctxr->data[i] = cpu_to_le32(req->processed / blocksize);
}
- } else if (ctx->digest == CONTEXT_CONTROL_DIGEST_HMAC) {
- cdesc->control_data.control0 |= CONTEXT_CONTROL_SIZE(10);
+ } else if (req->digest == CONTEXT_CONTROL_DIGEST_HMAC) {
+ cdesc->control_data.control0 |= CONTEXT_CONTROL_SIZE(2 * req->state_sz / sizeof(u32));
- memcpy(ctx->base.ctxr->data, ctx->ipad, digestsize);
- memcpy(ctx->base.ctxr->data + digestsize / sizeof(u32),
- ctx->opad, digestsize);
+ memcpy(ctx->base.ctxr->data, ctx->ipad, req->state_sz);
+ memcpy(ctx->base.ctxr->data + req->state_sz / sizeof(u32),
+ ctx->opad, req->state_sz);
}
}
@@ -149,16 +156,26 @@ static int safexcel_handle_req_result(struct safexcel_crypto_priv *priv, int rin
safexcel_complete(priv, ring);
spin_unlock_bh(&priv->ring[ring].egress_lock);
- if (sreq->finish)
- memcpy(areq->result, sreq->state,
- crypto_ahash_digestsize(ahash));
-
if (sreq->nents) {
dma_unmap_sg(priv->dev, areq->src, sreq->nents, DMA_TO_DEVICE);
sreq->nents = 0;
}
- safexcel_free_context(priv, async, sreq->state_sz);
+ if (sreq->result_dma) {
+ dma_unmap_single(priv->dev, sreq->result_dma, sreq->state_sz,
+ DMA_FROM_DEVICE);
+ sreq->result_dma = 0;
+ }
+
+ if (sreq->cache_dma) {
+ dma_unmap_single(priv->dev, sreq->cache_dma, sreq->cache_sz,
+ DMA_TO_DEVICE);
+ sreq->cache_dma = 0;
+ }
+
+ if (sreq->finish)
+ memcpy(areq->result, sreq->state,
+ crypto_ahash_digestsize(ahash));
cache_len = sreq->len - sreq->processed;
if (cache_len)
@@ -184,7 +201,7 @@ static int safexcel_ahash_send_req(struct crypto_async_request *async, int ring,
int i, queued, len, cache_len, extra, n_cdesc = 0, ret = 0;
queued = len = req->len - req->processed;
- if (queued < crypto_ahash_blocksize(ahash))
+ if (queued <= crypto_ahash_blocksize(ahash))
cache_len = queued;
else
cache_len = queued - areq->nbytes;
@@ -198,7 +215,7 @@ static int safexcel_ahash_send_req(struct crypto_async_request *async, int ring,
/* If this is not the last request and the queued data
* is a multiple of a block, cache the last one for now.
*/
- extra = queued - crypto_ahash_blocksize(ahash);
+ extra = crypto_ahash_blocksize(ahash);
if (extra) {
sg_pcopy_to_buffer(areq->src, sg_nents(areq->src),
@@ -220,24 +237,17 @@ static int safexcel_ahash_send_req(struct crypto_async_request *async, int ring,
/* Add a command descriptor for the cached data, if any */
if (cache_len) {
- ctx->base.cache = kzalloc(cache_len, EIP197_GFP_FLAGS(*async));
- if (!ctx->base.cache) {
- ret = -ENOMEM;
- goto unlock;
- }
- memcpy(ctx->base.cache, req->cache, cache_len);
- ctx->base.cache_dma = dma_map_single(priv->dev, ctx->base.cache,
- cache_len, DMA_TO_DEVICE);
- if (dma_mapping_error(priv->dev, ctx->base.cache_dma)) {
- ret = -EINVAL;
- goto free_cache;
+ req->cache_dma = dma_map_single(priv->dev, req->cache,
+ cache_len, DMA_TO_DEVICE);
+ if (dma_mapping_error(priv->dev, req->cache_dma)) {
+ spin_unlock_bh(&priv->ring[ring].egress_lock);
+ return -EINVAL;
}
- ctx->base.cache_sz = cache_len;
+ req->cache_sz = cache_len;
first_cdesc = safexcel_add_cdesc(priv, ring, 1,
(cache_len == len),
- ctx->base.cache_dma,
- cache_len, len,
+ req->cache_dma, cache_len, len,
ctx->base.ctxr_dma);
if (IS_ERR(first_cdesc)) {
ret = PTR_ERR(first_cdesc);
@@ -271,7 +281,7 @@ static int safexcel_ahash_send_req(struct crypto_async_request *async, int ring,
sglen, len, ctx->base.ctxr_dma);
if (IS_ERR(cdesc)) {
ret = PTR_ERR(cdesc);
- goto cdesc_rollback;
+ goto unmap_sg;
}
n_cdesc++;
@@ -291,19 +301,19 @@ send_command:
/* Add the token */
safexcel_hash_token(first_cdesc, len, req->state_sz);
- ctx->base.result_dma = dma_map_single(priv->dev, req->state,
- req->state_sz, DMA_FROM_DEVICE);
- if (dma_mapping_error(priv->dev, ctx->base.result_dma)) {
+ req->result_dma = dma_map_single(priv->dev, req->state, req->state_sz,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(priv->dev, req->result_dma)) {
ret = -EINVAL;
- goto cdesc_rollback;
+ goto unmap_sg;
}
/* Add a result descriptor */
- rdesc = safexcel_add_rdesc(priv, ring, 1, 1, ctx->base.result_dma,
+ rdesc = safexcel_add_rdesc(priv, ring, 1, 1, req->result_dma,
req->state_sz);
if (IS_ERR(rdesc)) {
ret = PTR_ERR(rdesc);
- goto cdesc_rollback;
+ goto unmap_result;
}
spin_unlock_bh(&priv->ring[ring].egress_lock);
@@ -315,20 +325,21 @@ send_command:
*results = 1;
return 0;
+unmap_result:
+ dma_unmap_single(priv->dev, req->result_dma, req->state_sz,
+ DMA_FROM_DEVICE);
+unmap_sg:
+ dma_unmap_sg(priv->dev, areq->src, req->nents, DMA_TO_DEVICE);
cdesc_rollback:
for (i = 0; i < n_cdesc; i++)
safexcel_ring_rollback_wptr(priv, &priv->ring[ring].cdr);
unmap_cache:
- if (ctx->base.cache_dma) {
- dma_unmap_single(priv->dev, ctx->base.cache_dma,
- ctx->base.cache_sz, DMA_TO_DEVICE);
- ctx->base.cache_sz = 0;
+ if (req->cache_dma) {
+ dma_unmap_single(priv->dev, req->cache_dma, req->cache_sz,
+ DMA_TO_DEVICE);
+ req->cache_sz = 0;
}
-free_cache:
- kfree(ctx->base.cache);
- ctx->base.cache = NULL;
-unlock:
spin_unlock_bh(&priv->ring[ring].egress_lock);
return ret;
}
@@ -493,7 +504,7 @@ static int safexcel_ahash_exit_inv(struct crypto_tfm *tfm)
queue_work(priv->ring[ring].workqueue,
&priv->ring[ring].work_data.work);
- wait_for_completion_interruptible(&result.completion);
+ wait_for_completion(&result.completion);
if (result.error) {
dev_warn(priv->dev, "hash: completion error (%d)\n",
@@ -550,7 +561,7 @@ static int safexcel_ahash_enqueue(struct ahash_request *areq)
if (ctx->base.ctxr) {
if (priv->version == EIP197 &&
!ctx->base.needs_inv && req->processed &&
- ctx->digest == CONTEXT_CONTROL_DIGEST_PRECOMPUTED)
+ req->digest == CONTEXT_CONTROL_DIGEST_PRECOMPUTED)
/* We're still setting needs_inv here, even though it is
* cleared right away, because the needs_inv flag can be
* set in other functions and we want to keep the same
@@ -585,7 +596,6 @@ static int safexcel_ahash_enqueue(struct ahash_request *areq)
static int safexcel_ahash_update(struct ahash_request *areq)
{
- struct safexcel_ahash_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(areq));
struct safexcel_ahash_req *req = ahash_request_ctx(areq);
struct crypto_ahash *ahash = crypto_ahash_reqtfm(areq);
@@ -601,7 +611,7 @@ static int safexcel_ahash_update(struct ahash_request *areq)
* We're not doing partial updates when performing an hmac request.
* Everything will be handled by the final() call.
*/
- if (ctx->digest == CONTEXT_CONTROL_DIGEST_HMAC)
+ if (req->digest == CONTEXT_CONTROL_DIGEST_HMAC)
return 0;
if (req->hmac)
@@ -660,6 +670,8 @@ static int safexcel_ahash_export(struct ahash_request *areq, void *out)
export->len = req->len;
export->processed = req->processed;
+ export->digest = req->digest;
+
memcpy(export->state, req->state, req->state_sz);
memcpy(export->cache, req->cache, crypto_ahash_blocksize(ahash));
@@ -680,6 +692,8 @@ static int safexcel_ahash_import(struct ahash_request *areq, const void *in)
req->len = export->len;
req->processed = export->processed;
+ req->digest = export->digest;
+
memcpy(req->cache, export->cache, crypto_ahash_blocksize(ahash));
memcpy(req->state, export->state, req->state_sz);
@@ -716,7 +730,7 @@ static int safexcel_sha1_init(struct ahash_request *areq)
req->state[4] = SHA1_H4;
ctx->alg = CONTEXT_CONTROL_CRYPTO_ALG_SHA1;
- ctx->digest = CONTEXT_CONTROL_DIGEST_PRECOMPUTED;
+ req->digest = CONTEXT_CONTROL_DIGEST_PRECOMPUTED;
req->state_sz = SHA1_DIGEST_SIZE;
return 0;
@@ -783,10 +797,10 @@ struct safexcel_alg_template safexcel_alg_sha1 = {
static int safexcel_hmac_sha1_init(struct ahash_request *areq)
{
- struct safexcel_ahash_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(areq));
+ struct safexcel_ahash_req *req = ahash_request_ctx(areq);
safexcel_sha1_init(areq);
- ctx->digest = CONTEXT_CONTROL_DIGEST_HMAC;
+ req->digest = CONTEXT_CONTROL_DIGEST_HMAC;
return 0;
}
@@ -839,7 +853,7 @@ static int safexcel_hmac_init_pad(struct ahash_request *areq,
init_completion(&result.completion);
ret = crypto_ahash_digest(areq);
- if (ret == -EINPROGRESS) {
+ if (ret == -EINPROGRESS || ret == -EBUSY) {
wait_for_completion_interruptible(&result.completion);
ret = result.error;
}
@@ -949,20 +963,21 @@ free_ahash:
return ret;
}
-static int safexcel_hmac_sha1_setkey(struct crypto_ahash *tfm, const u8 *key,
- unsigned int keylen)
+static int safexcel_hmac_alg_setkey(struct crypto_ahash *tfm, const u8 *key,
+ unsigned int keylen, const char *alg,
+ unsigned int state_sz)
{
struct safexcel_ahash_ctx *ctx = crypto_tfm_ctx(crypto_ahash_tfm(tfm));
struct safexcel_crypto_priv *priv = ctx->priv;
struct safexcel_ahash_export_state istate, ostate;
int ret, i;
- ret = safexcel_hmac_setkey("safexcel-sha1", key, keylen, &istate, &ostate);
+ ret = safexcel_hmac_setkey(alg, key, keylen, &istate, &ostate);
if (ret)
return ret;
if (priv->version == EIP197 && ctx->base.ctxr) {
- for (i = 0; i < SHA1_DIGEST_SIZE / sizeof(u32); i++) {
+ for (i = 0; i < state_sz / sizeof(u32); i++) {
if (ctx->ipad[i] != le32_to_cpu(istate.state[i]) ||
ctx->opad[i] != le32_to_cpu(ostate.state[i])) {
ctx->base.needs_inv = true;
@@ -971,12 +986,19 @@ static int safexcel_hmac_sha1_setkey(struct crypto_ahash *tfm, const u8 *key,
}
}
- memcpy(ctx->ipad, &istate.state, SHA1_DIGEST_SIZE);
- memcpy(ctx->opad, &ostate.state, SHA1_DIGEST_SIZE);
+ memcpy(ctx->ipad, &istate.state, state_sz);
+ memcpy(ctx->opad, &ostate.state, state_sz);
return 0;
}
+static int safexcel_hmac_sha1_setkey(struct crypto_ahash *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ return safexcel_hmac_alg_setkey(tfm, key, keylen, "safexcel-sha1",
+ SHA1_DIGEST_SIZE);
+}
+
struct safexcel_alg_template safexcel_alg_hmac_sha1 = {
.type = SAFEXCEL_ALG_TYPE_AHASH,
.alg.ahash = {
@@ -1024,7 +1046,7 @@ static int safexcel_sha256_init(struct ahash_request *areq)
req->state[7] = SHA256_H7;
ctx->alg = CONTEXT_CONTROL_CRYPTO_ALG_SHA256;
- ctx->digest = CONTEXT_CONTROL_DIGEST_PRECOMPUTED;
+ req->digest = CONTEXT_CONTROL_DIGEST_PRECOMPUTED;
req->state_sz = SHA256_DIGEST_SIZE;
return 0;
@@ -1086,7 +1108,7 @@ static int safexcel_sha224_init(struct ahash_request *areq)
req->state[7] = SHA224_H7;
ctx->alg = CONTEXT_CONTROL_CRYPTO_ALG_SHA224;
- ctx->digest = CONTEXT_CONTROL_DIGEST_PRECOMPUTED;
+ req->digest = CONTEXT_CONTROL_DIGEST_PRECOMPUTED;
req->state_sz = SHA256_DIGEST_SIZE;
return 0;
@@ -1130,3 +1152,115 @@ struct safexcel_alg_template safexcel_alg_sha224 = {
},
},
};
+
+static int safexcel_hmac_sha224_setkey(struct crypto_ahash *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ return safexcel_hmac_alg_setkey(tfm, key, keylen, "safexcel-sha224",
+ SHA256_DIGEST_SIZE);
+}
+
+static int safexcel_hmac_sha224_init(struct ahash_request *areq)
+{
+ struct safexcel_ahash_req *req = ahash_request_ctx(areq);
+
+ safexcel_sha224_init(areq);
+ req->digest = CONTEXT_CONTROL_DIGEST_HMAC;
+ return 0;
+}
+
+static int safexcel_hmac_sha224_digest(struct ahash_request *areq)
+{
+ int ret = safexcel_hmac_sha224_init(areq);
+
+ if (ret)
+ return ret;
+
+ return safexcel_ahash_finup(areq);
+}
+
+struct safexcel_alg_template safexcel_alg_hmac_sha224 = {
+ .type = SAFEXCEL_ALG_TYPE_AHASH,
+ .alg.ahash = {
+ .init = safexcel_hmac_sha224_init,
+ .update = safexcel_ahash_update,
+ .final = safexcel_ahash_final,
+ .finup = safexcel_ahash_finup,
+ .digest = safexcel_hmac_sha224_digest,
+ .setkey = safexcel_hmac_sha224_setkey,
+ .export = safexcel_ahash_export,
+ .import = safexcel_ahash_import,
+ .halg = {
+ .digestsize = SHA224_DIGEST_SIZE,
+ .statesize = sizeof(struct safexcel_ahash_export_state),
+ .base = {
+ .cra_name = "hmac(sha224)",
+ .cra_driver_name = "safexcel-hmac-sha224",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_ASYNC |
+ CRYPTO_ALG_KERN_DRIVER_ONLY,
+ .cra_blocksize = SHA224_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct safexcel_ahash_ctx),
+ .cra_init = safexcel_ahash_cra_init,
+ .cra_exit = safexcel_ahash_cra_exit,
+ .cra_module = THIS_MODULE,
+ },
+ },
+ },
+};
+
+static int safexcel_hmac_sha256_setkey(struct crypto_ahash *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ return safexcel_hmac_alg_setkey(tfm, key, keylen, "safexcel-sha256",
+ SHA256_DIGEST_SIZE);
+}
+
+static int safexcel_hmac_sha256_init(struct ahash_request *areq)
+{
+ struct safexcel_ahash_req *req = ahash_request_ctx(areq);
+
+ safexcel_sha256_init(areq);
+ req->digest = CONTEXT_CONTROL_DIGEST_HMAC;
+ return 0;
+}
+
+static int safexcel_hmac_sha256_digest(struct ahash_request *areq)
+{
+ int ret = safexcel_hmac_sha256_init(areq);
+
+ if (ret)
+ return ret;
+
+ return safexcel_ahash_finup(areq);
+}
+
+struct safexcel_alg_template safexcel_alg_hmac_sha256 = {
+ .type = SAFEXCEL_ALG_TYPE_AHASH,
+ .alg.ahash = {
+ .init = safexcel_hmac_sha256_init,
+ .update = safexcel_ahash_update,
+ .final = safexcel_ahash_final,
+ .finup = safexcel_ahash_finup,
+ .digest = safexcel_hmac_sha256_digest,
+ .setkey = safexcel_hmac_sha256_setkey,
+ .export = safexcel_ahash_export,
+ .import = safexcel_ahash_import,
+ .halg = {
+ .digestsize = SHA256_DIGEST_SIZE,
+ .statesize = sizeof(struct safexcel_ahash_export_state),
+ .base = {
+ .cra_name = "hmac(sha256)",
+ .cra_driver_name = "safexcel-hmac-sha256",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_ASYNC |
+ CRYPTO_ALG_KERN_DRIVER_ONLY,
+ .cra_blocksize = SHA256_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct safexcel_ahash_ctx),
+ .cra_init = safexcel_ahash_cra_init,
+ .cra_exit = safexcel_ahash_cra_exit,
+ .cra_module = THIS_MODULE,
+ },
+ },
+ },
+};
diff --git a/drivers/crypto/ixp4xx_crypto.c b/drivers/crypto/ixp4xx_crypto.c
index 717a26607bdb..27f7dad2d45d 100644
--- a/drivers/crypto/ixp4xx_crypto.c
+++ b/drivers/crypto/ixp4xx_crypto.c
@@ -1167,9 +1167,11 @@ static int aead_setkey(struct crypto_aead *tfm, const u8 *key,
ctx->authkey_len = keys.authkeylen;
ctx->enckey_len = keys.enckeylen;
+ memzero_explicit(&keys, sizeof(keys));
return aead_setup(tfm, crypto_aead_authsize(tfm));
badkey:
crypto_aead_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ memzero_explicit(&keys, sizeof(keys));
return -EINVAL;
}
diff --git a/drivers/crypto/marvell/cesa.c b/drivers/crypto/marvell/cesa.c
index aca2373fa1de..f81fa4a3e66b 100644
--- a/drivers/crypto/marvell/cesa.c
+++ b/drivers/crypto/marvell/cesa.c
@@ -25,7 +25,6 @@
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/module.h>
-#include <linux/dma-direct.h> /* XXX: drivers shall never use this directly! */
#include <linux/clk.h>
#include <linux/of.h>
#include <linux/of_platform.h>
diff --git a/drivers/crypto/mxs-dcp.c b/drivers/crypto/mxs-dcp.c
index 764be3e6933c..a10c418d4e5c 100644
--- a/drivers/crypto/mxs-dcp.c
+++ b/drivers/crypto/mxs-dcp.c
@@ -759,6 +759,16 @@ static int dcp_sha_digest(struct ahash_request *req)
return dcp_sha_finup(req);
}
+static int dcp_sha_noimport(struct ahash_request *req, const void *in)
+{
+ return -ENOSYS;
+}
+
+static int dcp_sha_noexport(struct ahash_request *req, void *out)
+{
+ return -ENOSYS;
+}
+
static int dcp_sha_cra_init(struct crypto_tfm *tfm)
{
crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm),
@@ -829,6 +839,8 @@ static struct ahash_alg dcp_sha1_alg = {
.final = dcp_sha_final,
.finup = dcp_sha_finup,
.digest = dcp_sha_digest,
+ .import = dcp_sha_noimport,
+ .export = dcp_sha_noexport,
.halg = {
.digestsize = SHA1_DIGEST_SIZE,
.base = {
@@ -853,6 +865,8 @@ static struct ahash_alg dcp_sha256_alg = {
.final = dcp_sha_final,
.finup = dcp_sha_finup,
.digest = dcp_sha_digest,
+ .import = dcp_sha_noimport,
+ .export = dcp_sha_noexport,
.halg = {
.digestsize = SHA256_DIGEST_SIZE,
.base = {
diff --git a/drivers/crypto/n2_core.c b/drivers/crypto/n2_core.c
index 662e709812cc..80e9c842aad4 100644
--- a/drivers/crypto/n2_core.c
+++ b/drivers/crypto/n2_core.c
@@ -359,6 +359,16 @@ static int n2_hash_async_finup(struct ahash_request *req)
return crypto_ahash_finup(&rctx->fallback_req);
}
+static int n2_hash_async_noimport(struct ahash_request *req, const void *in)
+{
+ return -ENOSYS;
+}
+
+static int n2_hash_async_noexport(struct ahash_request *req, void *out)
+{
+ return -ENOSYS;
+}
+
static int n2_hash_cra_init(struct crypto_tfm *tfm)
{
const char *fallback_driver_name = crypto_tfm_alg_name(tfm);
@@ -1467,6 +1477,8 @@ static int __n2_register_one_ahash(const struct n2_hash_tmpl *tmpl)
ahash->final = n2_hash_async_final;
ahash->finup = n2_hash_async_finup;
ahash->digest = n2_hash_async_digest;
+ ahash->export = n2_hash_async_noexport;
+ ahash->import = n2_hash_async_noimport;
halg = &ahash->halg;
halg->digestsize = tmpl->digest_size;
diff --git a/drivers/crypto/nx/nx-842-pseries.c b/drivers/crypto/nx/nx-842-pseries.c
index bf52cd1d7fca..66869976cfa2 100644
--- a/drivers/crypto/nx/nx-842-pseries.c
+++ b/drivers/crypto/nx/nx-842-pseries.c
@@ -1105,10 +1105,9 @@ static int __init nx842_pseries_init(void)
RCU_INIT_POINTER(devdata, NULL);
new_devdata = kzalloc(sizeof(*new_devdata), GFP_KERNEL);
- if (!new_devdata) {
- pr_err("Could not allocate memory for device data\n");
+ if (!new_devdata)
return -ENOMEM;
- }
+
RCU_INIT_POINTER(devdata, new_devdata);
ret = vio_register_driver(&nx842_vio_driver);
diff --git a/drivers/crypto/omap-aes.c b/drivers/crypto/omap-aes.c
index fbec0a2e76dd..9019f6b67986 100644
--- a/drivers/crypto/omap-aes.c
+++ b/drivers/crypto/omap-aes.c
@@ -47,6 +47,8 @@
static LIST_HEAD(dev_list);
static DEFINE_SPINLOCK(list_lock);
+static int aes_fallback_sz = 200;
+
#ifdef DEBUG
#define omap_aes_read(dd, offset) \
({ \
@@ -388,7 +390,7 @@ static void omap_aes_finish_req(struct omap_aes_dev *dd, int err)
pr_debug("err: %d\n", err);
- crypto_finalize_cipher_request(dd->engine, req, err);
+ crypto_finalize_ablkcipher_request(dd->engine, req, err);
pm_runtime_mark_last_busy(dd->dev);
pm_runtime_put_autosuspend(dd->dev);
@@ -408,14 +410,15 @@ static int omap_aes_handle_queue(struct omap_aes_dev *dd,
struct ablkcipher_request *req)
{
if (req)
- return crypto_transfer_cipher_request_to_engine(dd->engine, req);
+ return crypto_transfer_ablkcipher_request_to_engine(dd->engine, req);
return 0;
}
static int omap_aes_prepare_req(struct crypto_engine *engine,
- struct ablkcipher_request *req)
+ void *areq)
{
+ struct ablkcipher_request *req = container_of(areq, struct ablkcipher_request, base);
struct omap_aes_ctx *ctx = crypto_ablkcipher_ctx(
crypto_ablkcipher_reqtfm(req));
struct omap_aes_reqctx *rctx = ablkcipher_request_ctx(req);
@@ -468,8 +471,9 @@ static int omap_aes_prepare_req(struct crypto_engine *engine,
}
static int omap_aes_crypt_req(struct crypto_engine *engine,
- struct ablkcipher_request *req)
+ void *areq)
{
+ struct ablkcipher_request *req = container_of(areq, struct ablkcipher_request, base);
struct omap_aes_reqctx *rctx = ablkcipher_request_ctx(req);
struct omap_aes_dev *dd = rctx->dd;
@@ -517,7 +521,7 @@ static int omap_aes_crypt(struct ablkcipher_request *req, unsigned long mode)
!!(mode & FLAGS_ENCRYPT),
!!(mode & FLAGS_CBC));
- if (req->nbytes < 200) {
+ if (req->nbytes < aes_fallback_sz) {
SKCIPHER_REQUEST_ON_STACK(subreq, ctx->fallback);
skcipher_request_set_tfm(subreq, ctx->fallback);
@@ -601,6 +605,11 @@ static int omap_aes_ctr_decrypt(struct ablkcipher_request *req)
return omap_aes_crypt(req, FLAGS_CTR);
}
+static int omap_aes_prepare_req(struct crypto_engine *engine,
+ void *req);
+static int omap_aes_crypt_req(struct crypto_engine *engine,
+ void *req);
+
static int omap_aes_cra_init(struct crypto_tfm *tfm)
{
const char *name = crypto_tfm_alg_name(tfm);
@@ -616,6 +625,10 @@ static int omap_aes_cra_init(struct crypto_tfm *tfm)
tfm->crt_ablkcipher.reqsize = sizeof(struct omap_aes_reqctx);
+ ctx->enginectx.op.prepare_request = omap_aes_prepare_req;
+ ctx->enginectx.op.unprepare_request = NULL;
+ ctx->enginectx.op.do_one_request = omap_aes_crypt_req;
+
return 0;
}
@@ -1029,6 +1042,87 @@ err:
return err;
}
+static ssize_t fallback_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", aes_fallback_sz);
+}
+
+static ssize_t fallback_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ ssize_t status;
+ long value;
+
+ status = kstrtol(buf, 0, &value);
+ if (status)
+ return status;
+
+ /* HW accelerator only works with buffers > 9 */
+ if (value < 9) {
+ dev_err(dev, "minimum fallback size 9\n");
+ return -EINVAL;
+ }
+
+ aes_fallback_sz = value;
+
+ return size;
+}
+
+static ssize_t queue_len_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct omap_aes_dev *dd = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", dd->engine->queue.max_qlen);
+}
+
+static ssize_t queue_len_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t size)
+{
+ struct omap_aes_dev *dd;
+ ssize_t status;
+ long value;
+ unsigned long flags;
+
+ status = kstrtol(buf, 0, &value);
+ if (status)
+ return status;
+
+ if (value < 1)
+ return -EINVAL;
+
+ /*
+ * Changing the queue size in fly is safe, if size becomes smaller
+ * than current size, it will just not accept new entries until
+ * it has shrank enough.
+ */
+ spin_lock_bh(&list_lock);
+ list_for_each_entry(dd, &dev_list, list) {
+ spin_lock_irqsave(&dd->lock, flags);
+ dd->engine->queue.max_qlen = value;
+ dd->aead_queue.base.max_qlen = value;
+ spin_unlock_irqrestore(&dd->lock, flags);
+ }
+ spin_unlock_bh(&list_lock);
+
+ return size;
+}
+
+static DEVICE_ATTR_RW(queue_len);
+static DEVICE_ATTR_RW(fallback);
+
+static struct attribute *omap_aes_attrs[] = {
+ &dev_attr_queue_len.attr,
+ &dev_attr_fallback.attr,
+ NULL,
+};
+
+static struct attribute_group omap_aes_attr_group = {
+ .attrs = omap_aes_attrs,
+};
+
static int omap_aes_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -1119,8 +1213,6 @@ static int omap_aes_probe(struct platform_device *pdev)
goto err_engine;
}
- dd->engine->prepare_cipher_request = omap_aes_prepare_req;
- dd->engine->cipher_one_request = omap_aes_crypt_req;
err = crypto_engine_start(dd->engine);
if (err)
goto err_engine;
@@ -1159,6 +1251,12 @@ static int omap_aes_probe(struct platform_device *pdev)
}
}
+ err = sysfs_create_group(&dev->kobj, &omap_aes_attr_group);
+ if (err) {
+ dev_err(dev, "could not create sysfs device attrs\n");
+ goto err_aead_algs;
+ }
+
return 0;
err_aead_algs:
for (i = dd->pdata->aead_algs_info->registered - 1; i >= 0; i--) {
diff --git a/drivers/crypto/omap-aes.h b/drivers/crypto/omap-aes.h
index 8906342e2b9a..fc3b46a85809 100644
--- a/drivers/crypto/omap-aes.h
+++ b/drivers/crypto/omap-aes.h
@@ -13,6 +13,8 @@
#ifndef __OMAP_AES_H__
#define __OMAP_AES_H__
+#include <crypto/engine.h>
+
#define DST_MAXBURST 4
#define DMA_MIN (DST_MAXBURST * sizeof(u32))
@@ -95,6 +97,7 @@ struct omap_aes_gcm_result {
};
struct omap_aes_ctx {
+ struct crypto_engine_ctx enginectx;
int keylen;
u32 key[AES_KEYSIZE_256 / sizeof(u32)];
u8 nonce[4];
diff --git a/drivers/crypto/omap-crypto.c b/drivers/crypto/omap-crypto.c
index 23e37779317e..2c42e4b4a6e9 100644
--- a/drivers/crypto/omap-crypto.c
+++ b/drivers/crypto/omap-crypto.c
@@ -104,6 +104,10 @@ static int omap_crypto_check_sg(struct scatterlist *sg, int total, int bs,
return OMAP_CRYPTO_NOT_ALIGNED;
if (!IS_ALIGNED(sg->length, bs))
return OMAP_CRYPTO_NOT_ALIGNED;
+#ifdef CONFIG_ZONE_DMA
+ if (page_zonenum(sg_page(sg)) != ZONE_DMA)
+ return OMAP_CRYPTO_NOT_ALIGNED;
+#endif
len += sg->length;
sg = sg_next(sg);
diff --git a/drivers/crypto/omap-des.c b/drivers/crypto/omap-des.c
index ebc5c0f11f03..eb95b0d7f184 100644
--- a/drivers/crypto/omap-des.c
+++ b/drivers/crypto/omap-des.c
@@ -86,6 +86,7 @@
#define FLAGS_OUT_DATA_ST_SHIFT 10
struct omap_des_ctx {
+ struct crypto_engine_ctx enginectx;
struct omap_des_dev *dd;
int keylen;
@@ -498,7 +499,7 @@ static void omap_des_finish_req(struct omap_des_dev *dd, int err)
pr_debug("err: %d\n", err);
- crypto_finalize_cipher_request(dd->engine, req, err);
+ crypto_finalize_ablkcipher_request(dd->engine, req, err);
pm_runtime_mark_last_busy(dd->dev);
pm_runtime_put_autosuspend(dd->dev);
@@ -520,14 +521,15 @@ static int omap_des_handle_queue(struct omap_des_dev *dd,
struct ablkcipher_request *req)
{
if (req)
- return crypto_transfer_cipher_request_to_engine(dd->engine, req);
+ return crypto_transfer_ablkcipher_request_to_engine(dd->engine, req);
return 0;
}
static int omap_des_prepare_req(struct crypto_engine *engine,
- struct ablkcipher_request *req)
+ void *areq)
{
+ struct ablkcipher_request *req = container_of(areq, struct ablkcipher_request, base);
struct omap_des_ctx *ctx = crypto_ablkcipher_ctx(
crypto_ablkcipher_reqtfm(req));
struct omap_des_dev *dd = omap_des_find_dev(ctx);
@@ -582,8 +584,9 @@ static int omap_des_prepare_req(struct crypto_engine *engine,
}
static int omap_des_crypt_req(struct crypto_engine *engine,
- struct ablkcipher_request *req)
+ void *areq)
{
+ struct ablkcipher_request *req = container_of(areq, struct ablkcipher_request, base);
struct omap_des_ctx *ctx = crypto_ablkcipher_ctx(
crypto_ablkcipher_reqtfm(req));
struct omap_des_dev *dd = omap_des_find_dev(ctx);
@@ -695,12 +698,23 @@ static int omap_des_cbc_decrypt(struct ablkcipher_request *req)
return omap_des_crypt(req, FLAGS_CBC);
}
+static int omap_des_prepare_req(struct crypto_engine *engine,
+ void *areq);
+static int omap_des_crypt_req(struct crypto_engine *engine,
+ void *areq);
+
static int omap_des_cra_init(struct crypto_tfm *tfm)
{
+ struct omap_des_ctx *ctx = crypto_tfm_ctx(tfm);
+
pr_debug("enter\n");
tfm->crt_ablkcipher.reqsize = sizeof(struct omap_des_reqctx);
+ ctx->enginectx.op.prepare_request = omap_des_prepare_req;
+ ctx->enginectx.op.unprepare_request = NULL;
+ ctx->enginectx.op.do_one_request = omap_des_crypt_req;
+
return 0;
}
@@ -1046,8 +1060,6 @@ static int omap_des_probe(struct platform_device *pdev)
goto err_engine;
}
- dd->engine->prepare_cipher_request = omap_des_prepare_req;
- dd->engine->cipher_one_request = omap_des_crypt_req;
err = crypto_engine_start(dd->engine);
if (err)
goto err_engine;
diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c
index 86b89ace836f..ad02aa63b519 100644
--- a/drivers/crypto/omap-sham.c
+++ b/drivers/crypto/omap-sham.c
@@ -229,6 +229,7 @@ struct omap_sham_dev {
u8 xmit_buf[BUFLEN] OMAP_ALIGNED;
unsigned long flags;
+ int fallback_sz;
struct crypto_queue queue;
struct ahash_request *req;
@@ -759,6 +760,13 @@ static int omap_sham_align_sgs(struct scatterlist *sg,
while (nbytes > 0 && sg_tmp) {
n++;
+#ifdef CONFIG_ZONE_DMA
+ if (page_zonenum(sg_page(sg_tmp)) != ZONE_DMA) {
+ aligned = false;
+ break;
+ }
+#endif
+
if (offset < sg_tmp->length) {
if (!IS_ALIGNED(offset + sg_tmp->offset, 4)) {
aligned = false;
@@ -809,9 +817,6 @@ static int omap_sham_prepare_request(struct ahash_request *req, bool update)
bool final = rctx->flags & BIT(FLAGS_FINUP);
int xmit_len, hash_later;
- if (!req)
- return 0;
-
bs = get_block_size(rctx);
if (update)
@@ -1002,7 +1007,7 @@ static int omap_sham_update_req(struct omap_sham_dev *dd)
ctx->total, ctx->digcnt, (ctx->flags & BIT(FLAGS_FINUP)) != 0);
if (ctx->total < get_block_size(ctx) ||
- ctx->total < OMAP_SHA_DMA_THRESHOLD)
+ ctx->total < dd->fallback_sz)
ctx->flags |= BIT(FLAGS_CPU);
if (ctx->flags & BIT(FLAGS_CPU))
@@ -1258,11 +1263,11 @@ static int omap_sham_final(struct ahash_request *req)
/*
* OMAP HW accel works only with buffers >= 9.
* HMAC is always >= 9 because ipad == block size.
- * If buffersize is less than DMA_THRESHOLD, we use fallback
+ * If buffersize is less than fallback_sz, we use fallback
* SW encoding, as using DMA + HW in this case doesn't provide
* any benefit.
*/
- if (!ctx->digcnt && ctx->bufcnt < OMAP_SHA_DMA_THRESHOLD)
+ if (!ctx->digcnt && ctx->bufcnt < ctx->dd->fallback_sz)
return omap_sham_final_shash(req);
else if (ctx->bufcnt)
return omap_sham_enqueue(req, OP_FINAL);
@@ -1761,7 +1766,7 @@ static void omap_sham_done_task(unsigned long data)
if (test_and_clear_bit(FLAGS_OUTPUT_READY, &dd->flags)) {
/* hash or semi-hash ready */
clear_bit(FLAGS_DMA_READY, &dd->flags);
- goto finish;
+ goto finish;
}
}
@@ -2013,6 +2018,85 @@ err:
return err;
}
+static ssize_t fallback_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct omap_sham_dev *dd = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", dd->fallback_sz);
+}
+
+static ssize_t fallback_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct omap_sham_dev *dd = dev_get_drvdata(dev);
+ ssize_t status;
+ long value;
+
+ status = kstrtol(buf, 0, &value);
+ if (status)
+ return status;
+
+ /* HW accelerator only works with buffers > 9 */
+ if (value < 9) {
+ dev_err(dev, "minimum fallback size 9\n");
+ return -EINVAL;
+ }
+
+ dd->fallback_sz = value;
+
+ return size;
+}
+
+static ssize_t queue_len_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct omap_sham_dev *dd = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", dd->queue.max_qlen);
+}
+
+static ssize_t queue_len_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t size)
+{
+ struct omap_sham_dev *dd = dev_get_drvdata(dev);
+ ssize_t status;
+ long value;
+ unsigned long flags;
+
+ status = kstrtol(buf, 0, &value);
+ if (status)
+ return status;
+
+ if (value < 1)
+ return -EINVAL;
+
+ /*
+ * Changing the queue size in fly is safe, if size becomes smaller
+ * than current size, it will just not accept new entries until
+ * it has shrank enough.
+ */
+ spin_lock_irqsave(&dd->lock, flags);
+ dd->queue.max_qlen = value;
+ spin_unlock_irqrestore(&dd->lock, flags);
+
+ return size;
+}
+
+static DEVICE_ATTR_RW(queue_len);
+static DEVICE_ATTR_RW(fallback);
+
+static struct attribute *omap_sham_attrs[] = {
+ &dev_attr_queue_len.attr,
+ &dev_attr_fallback.attr,
+ NULL,
+};
+
+static struct attribute_group omap_sham_attr_group = {
+ .attrs = omap_sham_attrs,
+};
+
static int omap_sham_probe(struct platform_device *pdev)
{
struct omap_sham_dev *dd;
@@ -2074,6 +2158,8 @@ static int omap_sham_probe(struct platform_device *pdev)
pm_runtime_use_autosuspend(dev);
pm_runtime_set_autosuspend_delay(dev, DEFAULT_AUTOSUSPEND_DELAY);
+ dd->fallback_sz = OMAP_SHA_DMA_THRESHOLD;
+
pm_runtime_enable(dev);
pm_runtime_irq_safe(dev);
@@ -2111,6 +2197,12 @@ static int omap_sham_probe(struct platform_device *pdev)
}
}
+ err = sysfs_create_group(&dev->kobj, &omap_sham_attr_group);
+ if (err) {
+ dev_err(dev, "could not create sysfs device attrs\n");
+ goto err_algs;
+ }
+
return 0;
err_algs:
diff --git a/drivers/crypto/picoxcell_crypto.c b/drivers/crypto/picoxcell_crypto.c
index 4ef52c9d72fc..a4df966adbf6 100644
--- a/drivers/crypto/picoxcell_crypto.c
+++ b/drivers/crypto/picoxcell_crypto.c
@@ -499,10 +499,12 @@ static int spacc_aead_setkey(struct crypto_aead *tfm, const u8 *key,
memcpy(ctx->hash_ctx, keys.authkey, keys.authkeylen);
ctx->hash_key_len = keys.authkeylen;
+ memzero_explicit(&keys, sizeof(keys));
return 0;
badkey:
crypto_aead_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ memzero_explicit(&keys, sizeof(keys));
return -EINVAL;
}
diff --git a/drivers/crypto/qat/qat_common/.gitignore b/drivers/crypto/qat/qat_common/.gitignore
deleted file mode 100644
index ee328374dba8..000000000000
--- a/drivers/crypto/qat/qat_common/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-*-asn1.[ch]
diff --git a/drivers/crypto/qat/qat_common/qat_algs.c b/drivers/crypto/qat/qat_common/qat_algs.c
index baffae817259..1138e41d6805 100644
--- a/drivers/crypto/qat/qat_common/qat_algs.c
+++ b/drivers/crypto/qat/qat_common/qat_algs.c
@@ -546,11 +546,14 @@ static int qat_alg_aead_init_sessions(struct crypto_aead *tfm, const u8 *key,
if (qat_alg_aead_init_dec_session(tfm, alg, &keys, mode))
goto error;
+ memzero_explicit(&keys, sizeof(keys));
return 0;
bad_key:
crypto_aead_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ memzero_explicit(&keys, sizeof(keys));
return -EINVAL;
error:
+ memzero_explicit(&keys, sizeof(keys));
return -EFAULT;
}
diff --git a/drivers/crypto/qat/qat_common/qat_asym_algs.c b/drivers/crypto/qat/qat_common/qat_asym_algs.c
index 13c52d6bf630..320e7854b4ee 100644
--- a/drivers/crypto/qat/qat_common/qat_asym_algs.c
+++ b/drivers/crypto/qat/qat_common/qat_asym_algs.c
@@ -969,7 +969,8 @@ unmap_src:
return ret;
}
-int qat_rsa_set_n(struct qat_rsa_ctx *ctx, const char *value, size_t vlen)
+static int qat_rsa_set_n(struct qat_rsa_ctx *ctx, const char *value,
+ size_t vlen)
{
struct qat_crypto_instance *inst = ctx->inst;
struct device *dev = &GET_DEV(inst->accel_dev);
@@ -1000,7 +1001,8 @@ err:
return ret;
}
-int qat_rsa_set_e(struct qat_rsa_ctx *ctx, const char *value, size_t vlen)
+static int qat_rsa_set_e(struct qat_rsa_ctx *ctx, const char *value,
+ size_t vlen)
{
struct qat_crypto_instance *inst = ctx->inst;
struct device *dev = &GET_DEV(inst->accel_dev);
@@ -1024,7 +1026,8 @@ int qat_rsa_set_e(struct qat_rsa_ctx *ctx, const char *value, size_t vlen)
return 0;
}
-int qat_rsa_set_d(struct qat_rsa_ctx *ctx, const char *value, size_t vlen)
+static int qat_rsa_set_d(struct qat_rsa_ctx *ctx, const char *value,
+ size_t vlen)
{
struct qat_crypto_instance *inst = ctx->inst;
struct device *dev = &GET_DEV(inst->accel_dev);
diff --git a/drivers/crypto/s5p-sss.c b/drivers/crypto/s5p-sss.c
index 5d64c08b7f47..bf7163042569 100644
--- a/drivers/crypto/s5p-sss.c
+++ b/drivers/crypto/s5p-sss.c
@@ -404,29 +404,31 @@ static const struct of_device_id s5p_sss_dt_match[] = {
};
MODULE_DEVICE_TABLE(of, s5p_sss_dt_match);
-static inline struct samsung_aes_variant *find_s5p_sss_version
- (struct platform_device *pdev)
+static inline const struct samsung_aes_variant *find_s5p_sss_version
+ (const struct platform_device *pdev)
{
if (IS_ENABLED(CONFIG_OF) && (pdev->dev.of_node)) {
const struct of_device_id *match;
match = of_match_node(s5p_sss_dt_match,
pdev->dev.of_node);
- return (struct samsung_aes_variant *)match->data;
+ return (const struct samsung_aes_variant *)match->data;
}
- return (struct samsung_aes_variant *)
+ return (const struct samsung_aes_variant *)
platform_get_device_id(pdev)->driver_data;
}
static struct s5p_aes_dev *s5p_dev;
-static void s5p_set_dma_indata(struct s5p_aes_dev *dev, struct scatterlist *sg)
+static void s5p_set_dma_indata(struct s5p_aes_dev *dev,
+ const struct scatterlist *sg)
{
SSS_WRITE(dev, FCBRDMAS, sg_dma_address(sg));
SSS_WRITE(dev, FCBRDMAL, sg_dma_len(sg));
}
-static void s5p_set_dma_outdata(struct s5p_aes_dev *dev, struct scatterlist *sg)
+static void s5p_set_dma_outdata(struct s5p_aes_dev *dev,
+ const struct scatterlist *sg)
{
SSS_WRITE(dev, FCBTDMAS, sg_dma_address(sg));
SSS_WRITE(dev, FCBTDMAL, sg_dma_len(sg));
@@ -619,7 +621,7 @@ static inline void s5p_hash_write(struct s5p_aes_dev *dd,
* @sg: scatterlist ready to DMA transmit
*/
static void s5p_set_dma_hashdata(struct s5p_aes_dev *dev,
- struct scatterlist *sg)
+ const struct scatterlist *sg)
{
dev->hash_sg_cnt--;
SSS_WRITE(dev, FCHRDMAS, sg_dma_address(sg));
@@ -792,9 +794,9 @@ static void s5p_hash_read_msg(struct ahash_request *req)
* @ctx: request context
*/
static void s5p_hash_write_ctx_iv(struct s5p_aes_dev *dd,
- struct s5p_hash_reqctx *ctx)
+ const struct s5p_hash_reqctx *ctx)
{
- u32 *hash = (u32 *)ctx->digest;
+ const u32 *hash = (const u32 *)ctx->digest;
unsigned int i;
for (i = 0; i < ctx->nregs; i++)
@@ -818,7 +820,7 @@ static void s5p_hash_write_iv(struct ahash_request *req)
*/
static void s5p_hash_copy_result(struct ahash_request *req)
{
- struct s5p_hash_reqctx *ctx = ahash_request_ctx(req);
+ const struct s5p_hash_reqctx *ctx = ahash_request_ctx(req);
if (!req->result)
return;
@@ -1210,9 +1212,6 @@ static int s5p_hash_prepare_request(struct ahash_request *req, bool update)
int xmit_len, hash_later, nbytes;
int ret;
- if (!req)
- return 0;
-
if (update)
nbytes = req->nbytes;
else
@@ -1293,7 +1292,7 @@ static int s5p_hash_prepare_request(struct ahash_request *req, bool update)
*/
static void s5p_hash_update_dma_stop(struct s5p_aes_dev *dd)
{
- struct s5p_hash_reqctx *ctx = ahash_request_ctx(dd->hash_req);
+ const struct s5p_hash_reqctx *ctx = ahash_request_ctx(dd->hash_req);
dma_unmap_sg(dd->dev, ctx->sg, ctx->sg_len, DMA_TO_DEVICE);
clear_bit(HASH_FLAGS_DMA_ACTIVE, &dd->hash_flags);
@@ -1720,7 +1719,7 @@ static void s5p_hash_cra_exit(struct crypto_tfm *tfm)
*/
static int s5p_hash_export(struct ahash_request *req, void *out)
{
- struct s5p_hash_reqctx *ctx = ahash_request_ctx(req);
+ const struct s5p_hash_reqctx *ctx = ahash_request_ctx(req);
memcpy(out, ctx, sizeof(*ctx) + ctx->bufcnt);
@@ -1834,7 +1833,8 @@ static struct ahash_alg algs_sha1_md5_sha256[] = {
};
static void s5p_set_aes(struct s5p_aes_dev *dev,
- uint8_t *key, uint8_t *iv, unsigned int keylen)
+ const uint8_t *key, const uint8_t *iv,
+ unsigned int keylen)
{
void __iomem *keystart;
@@ -2153,7 +2153,7 @@ static int s5p_aes_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
int i, j, err = -ENODEV;
- struct samsung_aes_variant *variant;
+ const struct samsung_aes_variant *variant;
struct s5p_aes_dev *pdata;
struct resource *res;
unsigned int hash_i;
diff --git a/drivers/crypto/sahara.c b/drivers/crypto/sahara.c
index 08e7bdcaa6e3..0f2245e1af2b 100644
--- a/drivers/crypto/sahara.c
+++ b/drivers/crypto/sahara.c
@@ -1397,11 +1397,9 @@ static int sahara_probe(struct platform_device *pdev)
int err;
int i;
- dev = devm_kzalloc(&pdev->dev, sizeof(struct sahara_dev), GFP_KERNEL);
- if (dev == NULL) {
- dev_err(&pdev->dev, "unable to alloc data struct.\n");
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
return -ENOMEM;
- }
dev->device = &pdev->dev;
platform_set_drvdata(pdev, dev);
diff --git a/drivers/crypto/stm32/stm32-cryp.c b/drivers/crypto/stm32/stm32-cryp.c
index 4a06a7a665ee..c5d3efc54a4f 100644
--- a/drivers/crypto/stm32/stm32-cryp.c
+++ b/drivers/crypto/stm32/stm32-cryp.c
@@ -17,6 +17,7 @@
#include <crypto/des.h>
#include <crypto/engine.h>
#include <crypto/scatterwalk.h>
+#include <crypto/internal/aead.h>
#define DRIVER_NAME "stm32-cryp"
@@ -29,8 +30,12 @@
#define FLG_ECB BIT(4)
#define FLG_CBC BIT(5)
#define FLG_CTR BIT(6)
+#define FLG_GCM BIT(7)
+#define FLG_CCM BIT(8)
/* Mode mask = bits [15..0] */
#define FLG_MODE_MASK GENMASK(15, 0)
+/* Bit [31..16] status */
+#define FLG_CCM_PADDED_WA BIT(16)
/* Registers */
#define CRYP_CR 0x00000000
@@ -53,6 +58,8 @@
#define CRYP_IV0RR 0x00000044
#define CRYP_IV1LR 0x00000048
#define CRYP_IV1RR 0x0000004C
+#define CRYP_CSGCMCCM0R 0x00000050
+#define CRYP_CSGCM0R 0x00000070
/* Registers values */
#define CR_DEC_NOT_ENC 0x00000004
@@ -64,6 +71,8 @@
#define CR_AES_CBC 0x00000028
#define CR_AES_CTR 0x00000030
#define CR_AES_KP 0x00000038
+#define CR_AES_GCM 0x00080000
+#define CR_AES_CCM 0x00080008
#define CR_AES_UNKNOWN 0xFFFFFFFF
#define CR_ALGO_MASK 0x00080038
#define CR_DATA32 0x00000000
@@ -75,6 +84,12 @@
#define CR_KEY256 0x00000200
#define CR_FFLUSH 0x00004000
#define CR_CRYPEN 0x00008000
+#define CR_PH_INIT 0x00000000
+#define CR_PH_HEADER 0x00010000
+#define CR_PH_PAYLOAD 0x00020000
+#define CR_PH_FINAL 0x00030000
+#define CR_PH_MASK 0x00030000
+#define CR_NBPBL_SHIFT 20
#define SR_BUSY 0x00000010
#define SR_OFNE 0x00000004
@@ -87,10 +102,17 @@
/* Misc */
#define AES_BLOCK_32 (AES_BLOCK_SIZE / sizeof(u32))
+#define GCM_CTR_INIT 2
#define _walked_in (cryp->in_walk.offset - cryp->in_sg->offset)
#define _walked_out (cryp->out_walk.offset - cryp->out_sg->offset)
+struct stm32_cryp_caps {
+ bool swap_final;
+ bool padding_wa;
+};
+
struct stm32_cryp_ctx {
+ struct crypto_engine_ctx enginectx;
struct stm32_cryp *cryp;
int keylen;
u32 key[AES_KEYSIZE_256 / sizeof(u32)];
@@ -108,13 +130,16 @@ struct stm32_cryp {
struct clk *clk;
unsigned long flags;
u32 irq_status;
+ const struct stm32_cryp_caps *caps;
struct stm32_cryp_ctx *ctx;
struct crypto_engine *engine;
- struct mutex lock; /* protects req */
+ struct mutex lock; /* protects req / areq */
struct ablkcipher_request *req;
+ struct aead_request *areq;
+ size_t authsize;
size_t hw_blocksize;
size_t total_in;
@@ -137,6 +162,7 @@ struct stm32_cryp {
struct scatter_walk out_walk;
u32 last_ctr[4];
+ u32 gcm_ctr;
};
struct stm32_cryp_list {
@@ -179,6 +205,16 @@ static inline bool is_ctr(struct stm32_cryp *cryp)
return cryp->flags & FLG_CTR;
}
+static inline bool is_gcm(struct stm32_cryp *cryp)
+{
+ return cryp->flags & FLG_GCM;
+}
+
+static inline bool is_ccm(struct stm32_cryp *cryp)
+{
+ return cryp->flags & FLG_CCM;
+}
+
static inline bool is_encrypt(struct stm32_cryp *cryp)
{
return cryp->flags & FLG_ENCRYPT;
@@ -207,6 +243,24 @@ static inline int stm32_cryp_wait_busy(struct stm32_cryp *cryp)
!(status & SR_BUSY), 10, 100000);
}
+static inline int stm32_cryp_wait_enable(struct stm32_cryp *cryp)
+{
+ u32 status;
+
+ return readl_relaxed_poll_timeout(cryp->regs + CRYP_CR, status,
+ !(status & CR_CRYPEN), 10, 100000);
+}
+
+static inline int stm32_cryp_wait_output(struct stm32_cryp *cryp)
+{
+ u32 status;
+
+ return readl_relaxed_poll_timeout(cryp->regs + CRYP_SR, status,
+ status & SR_OFNE, 10, 100000);
+}
+
+static int stm32_cryp_read_auth_tag(struct stm32_cryp *cryp);
+
static struct stm32_cryp *stm32_cryp_find_dev(struct stm32_cryp_ctx *ctx)
{
struct stm32_cryp *tmp, *cryp = NULL;
@@ -365,6 +419,12 @@ static u32 stm32_cryp_get_hw_mode(struct stm32_cryp *cryp)
if (is_aes(cryp) && is_ctr(cryp))
return CR_AES_CTR;
+ if (is_aes(cryp) && is_gcm(cryp))
+ return CR_AES_GCM;
+
+ if (is_aes(cryp) && is_ccm(cryp))
+ return CR_AES_CCM;
+
if (is_des(cryp) && is_ecb(cryp))
return CR_DES_ECB;
@@ -381,6 +441,79 @@ static u32 stm32_cryp_get_hw_mode(struct stm32_cryp *cryp)
return CR_AES_UNKNOWN;
}
+static unsigned int stm32_cryp_get_input_text_len(struct stm32_cryp *cryp)
+{
+ return is_encrypt(cryp) ? cryp->areq->cryptlen :
+ cryp->areq->cryptlen - cryp->authsize;
+}
+
+static int stm32_cryp_gcm_init(struct stm32_cryp *cryp, u32 cfg)
+{
+ int ret;
+ u32 iv[4];
+
+ /* Phase 1 : init */
+ memcpy(iv, cryp->areq->iv, 12);
+ iv[3] = cpu_to_be32(GCM_CTR_INIT);
+ cryp->gcm_ctr = GCM_CTR_INIT;
+ stm32_cryp_hw_write_iv(cryp, iv);
+
+ stm32_cryp_write(cryp, CRYP_CR, cfg | CR_PH_INIT | CR_CRYPEN);
+
+ /* Wait for end of processing */
+ ret = stm32_cryp_wait_enable(cryp);
+ if (ret)
+ dev_err(cryp->dev, "Timeout (gcm init)\n");
+
+ return ret;
+}
+
+static int stm32_cryp_ccm_init(struct stm32_cryp *cryp, u32 cfg)
+{
+ int ret;
+ u8 iv[AES_BLOCK_SIZE], b0[AES_BLOCK_SIZE];
+ u32 *d;
+ unsigned int i, textlen;
+
+ /* Phase 1 : init. Firstly set the CTR value to 1 (not 0) */
+ memcpy(iv, cryp->areq->iv, AES_BLOCK_SIZE);
+ memset(iv + AES_BLOCK_SIZE - 1 - iv[0], 0, iv[0] + 1);
+ iv[AES_BLOCK_SIZE - 1] = 1;
+ stm32_cryp_hw_write_iv(cryp, (u32 *)iv);
+
+ /* Build B0 */
+ memcpy(b0, iv, AES_BLOCK_SIZE);
+
+ b0[0] |= (8 * ((cryp->authsize - 2) / 2));
+
+ if (cryp->areq->assoclen)
+ b0[0] |= 0x40;
+
+ textlen = stm32_cryp_get_input_text_len(cryp);
+
+ b0[AES_BLOCK_SIZE - 2] = textlen >> 8;
+ b0[AES_BLOCK_SIZE - 1] = textlen & 0xFF;
+
+ /* Enable HW */
+ stm32_cryp_write(cryp, CRYP_CR, cfg | CR_PH_INIT | CR_CRYPEN);
+
+ /* Write B0 */
+ d = (u32 *)b0;
+
+ for (i = 0; i < AES_BLOCK_32; i++) {
+ if (!cryp->caps->padding_wa)
+ *d = cpu_to_be32(*d);
+ stm32_cryp_write(cryp, CRYP_DIN, *d++);
+ }
+
+ /* Wait for end of processing */
+ ret = stm32_cryp_wait_enable(cryp);
+ if (ret)
+ dev_err(cryp->dev, "Timeout (ccm init)\n");
+
+ return ret;
+}
+
static int stm32_cryp_hw_init(struct stm32_cryp *cryp)
{
int ret;
@@ -436,6 +569,29 @@ static int stm32_cryp_hw_init(struct stm32_cryp *cryp)
stm32_cryp_write(cryp, CRYP_CR, cfg);
switch (hw_mode) {
+ case CR_AES_GCM:
+ case CR_AES_CCM:
+ /* Phase 1 : init */
+ if (hw_mode == CR_AES_CCM)
+ ret = stm32_cryp_ccm_init(cryp, cfg);
+ else
+ ret = stm32_cryp_gcm_init(cryp, cfg);
+
+ if (ret)
+ return ret;
+
+ /* Phase 2 : header (authenticated data) */
+ if (cryp->areq->assoclen) {
+ cfg |= CR_PH_HEADER;
+ } else if (stm32_cryp_get_input_text_len(cryp)) {
+ cfg |= CR_PH_PAYLOAD;
+ stm32_cryp_write(cryp, CRYP_CR, cfg);
+ } else {
+ cfg |= CR_PH_INIT;
+ }
+
+ break;
+
case CR_DES_CBC:
case CR_TDES_CBC:
case CR_AES_CBC:
@@ -452,12 +608,16 @@ static int stm32_cryp_hw_init(struct stm32_cryp *cryp)
stm32_cryp_write(cryp, CRYP_CR, cfg);
+ cryp->flags &= ~FLG_CCM_PADDED_WA;
+
return 0;
}
-static void stm32_cryp_finish_req(struct stm32_cryp *cryp)
+static void stm32_cryp_finish_req(struct stm32_cryp *cryp, int err)
{
- int err = 0;
+ if (!err && (is_gcm(cryp) || is_ccm(cryp)))
+ /* Phase 4 : output tag */
+ err = stm32_cryp_read_auth_tag(cryp);
if (cryp->sgs_copied) {
void *buf_in, *buf_out;
@@ -478,8 +638,14 @@ static void stm32_cryp_finish_req(struct stm32_cryp *cryp)
free_pages((unsigned long)buf_out, pages);
}
- crypto_finalize_cipher_request(cryp->engine, cryp->req, err);
- cryp->req = NULL;
+ if (is_gcm(cryp) || is_ccm(cryp)) {
+ crypto_finalize_aead_request(cryp->engine, cryp->areq, err);
+ cryp->areq = NULL;
+ } else {
+ crypto_finalize_ablkcipher_request(cryp->engine, cryp->req,
+ err);
+ cryp->req = NULL;
+ }
memset(cryp->ctx->key, 0, cryp->ctx->keylen);
@@ -494,10 +660,36 @@ static int stm32_cryp_cpu_start(struct stm32_cryp *cryp)
return 0;
}
+static int stm32_cryp_cipher_one_req(struct crypto_engine *engine, void *areq);
+static int stm32_cryp_prepare_cipher_req(struct crypto_engine *engine,
+ void *areq);
+
static int stm32_cryp_cra_init(struct crypto_tfm *tfm)
{
+ struct stm32_cryp_ctx *ctx = crypto_tfm_ctx(tfm);
+
tfm->crt_ablkcipher.reqsize = sizeof(struct stm32_cryp_reqctx);
+ ctx->enginectx.op.do_one_request = stm32_cryp_cipher_one_req;
+ ctx->enginectx.op.prepare_request = stm32_cryp_prepare_cipher_req;
+ ctx->enginectx.op.unprepare_request = NULL;
+ return 0;
+}
+
+static int stm32_cryp_aead_one_req(struct crypto_engine *engine, void *areq);
+static int stm32_cryp_prepare_aead_req(struct crypto_engine *engine,
+ void *areq);
+
+static int stm32_cryp_aes_aead_init(struct crypto_aead *tfm)
+{
+ struct stm32_cryp_ctx *ctx = crypto_aead_ctx(tfm);
+
+ tfm->reqsize = sizeof(struct stm32_cryp_reqctx);
+
+ ctx->enginectx.op.do_one_request = stm32_cryp_aead_one_req;
+ ctx->enginectx.op.prepare_request = stm32_cryp_prepare_aead_req;
+ ctx->enginectx.op.unprepare_request = NULL;
+
return 0;
}
@@ -513,7 +705,21 @@ static int stm32_cryp_crypt(struct ablkcipher_request *req, unsigned long mode)
rctx->mode = mode;
- return crypto_transfer_cipher_request_to_engine(cryp->engine, req);
+ return crypto_transfer_ablkcipher_request_to_engine(cryp->engine, req);
+}
+
+static int stm32_cryp_aead_crypt(struct aead_request *req, unsigned long mode)
+{
+ struct stm32_cryp_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
+ struct stm32_cryp_reqctx *rctx = aead_request_ctx(req);
+ struct stm32_cryp *cryp = stm32_cryp_find_dev(ctx);
+
+ if (!cryp)
+ return -ENODEV;
+
+ rctx->mode = mode;
+
+ return crypto_transfer_aead_request_to_engine(cryp->engine, req);
}
static int stm32_cryp_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
@@ -555,6 +761,46 @@ static int stm32_cryp_tdes_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
return stm32_cryp_setkey(tfm, key, keylen);
}
+static int stm32_cryp_aes_aead_setkey(struct crypto_aead *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ struct stm32_cryp_ctx *ctx = crypto_aead_ctx(tfm);
+
+ if (keylen != AES_KEYSIZE_128 && keylen != AES_KEYSIZE_192 &&
+ keylen != AES_KEYSIZE_256)
+ return -EINVAL;
+
+ memcpy(ctx->key, key, keylen);
+ ctx->keylen = keylen;
+
+ return 0;
+}
+
+static int stm32_cryp_aes_gcm_setauthsize(struct crypto_aead *tfm,
+ unsigned int authsize)
+{
+ return authsize == AES_BLOCK_SIZE ? 0 : -EINVAL;
+}
+
+static int stm32_cryp_aes_ccm_setauthsize(struct crypto_aead *tfm,
+ unsigned int authsize)
+{
+ switch (authsize) {
+ case 4:
+ case 6:
+ case 8:
+ case 10:
+ case 12:
+ case 14:
+ case 16:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int stm32_cryp_aes_ecb_encrypt(struct ablkcipher_request *req)
{
return stm32_cryp_crypt(req, FLG_AES | FLG_ECB | FLG_ENCRYPT);
@@ -585,6 +831,26 @@ static int stm32_cryp_aes_ctr_decrypt(struct ablkcipher_request *req)
return stm32_cryp_crypt(req, FLG_AES | FLG_CTR);
}
+static int stm32_cryp_aes_gcm_encrypt(struct aead_request *req)
+{
+ return stm32_cryp_aead_crypt(req, FLG_AES | FLG_GCM | FLG_ENCRYPT);
+}
+
+static int stm32_cryp_aes_gcm_decrypt(struct aead_request *req)
+{
+ return stm32_cryp_aead_crypt(req, FLG_AES | FLG_GCM);
+}
+
+static int stm32_cryp_aes_ccm_encrypt(struct aead_request *req)
+{
+ return stm32_cryp_aead_crypt(req, FLG_AES | FLG_CCM | FLG_ENCRYPT);
+}
+
+static int stm32_cryp_aes_ccm_decrypt(struct aead_request *req)
+{
+ return stm32_cryp_aead_crypt(req, FLG_AES | FLG_CCM);
+}
+
static int stm32_cryp_des_ecb_encrypt(struct ablkcipher_request *req)
{
return stm32_cryp_crypt(req, FLG_DES | FLG_ECB | FLG_ENCRYPT);
@@ -625,18 +891,19 @@ static int stm32_cryp_tdes_cbc_decrypt(struct ablkcipher_request *req)
return stm32_cryp_crypt(req, FLG_TDES | FLG_CBC);
}
-static int stm32_cryp_prepare_req(struct crypto_engine *engine,
- struct ablkcipher_request *req)
+static int stm32_cryp_prepare_req(struct ablkcipher_request *req,
+ struct aead_request *areq)
{
struct stm32_cryp_ctx *ctx;
struct stm32_cryp *cryp;
struct stm32_cryp_reqctx *rctx;
int ret;
- if (!req)
+ if (!req && !areq)
return -EINVAL;
- ctx = crypto_ablkcipher_ctx(crypto_ablkcipher_reqtfm(req));
+ ctx = req ? crypto_ablkcipher_ctx(crypto_ablkcipher_reqtfm(req)) :
+ crypto_aead_ctx(crypto_aead_reqtfm(areq));
cryp = ctx->cryp;
@@ -645,7 +912,7 @@ static int stm32_cryp_prepare_req(struct crypto_engine *engine,
mutex_lock(&cryp->lock);
- rctx = ablkcipher_request_ctx(req);
+ rctx = req ? ablkcipher_request_ctx(req) : aead_request_ctx(areq);
rctx->mode &= FLG_MODE_MASK;
ctx->cryp = cryp;
@@ -654,15 +921,48 @@ static int stm32_cryp_prepare_req(struct crypto_engine *engine,
cryp->hw_blocksize = is_aes(cryp) ? AES_BLOCK_SIZE : DES_BLOCK_SIZE;
cryp->ctx = ctx;
- cryp->req = req;
- cryp->total_in = req->nbytes;
- cryp->total_out = cryp->total_in;
+ if (req) {
+ cryp->req = req;
+ cryp->total_in = req->nbytes;
+ cryp->total_out = cryp->total_in;
+ } else {
+ /*
+ * Length of input and output data:
+ * Encryption case:
+ * INPUT = AssocData || PlainText
+ * <- assoclen -> <- cryptlen ->
+ * <------- total_in ----------->
+ *
+ * OUTPUT = AssocData || CipherText || AuthTag
+ * <- assoclen -> <- cryptlen -> <- authsize ->
+ * <---------------- total_out ----------------->
+ *
+ * Decryption case:
+ * INPUT = AssocData || CipherText || AuthTag
+ * <- assoclen -> <--------- cryptlen --------->
+ * <- authsize ->
+ * <---------------- total_in ------------------>
+ *
+ * OUTPUT = AssocData || PlainText
+ * <- assoclen -> <- crypten - authsize ->
+ * <---------- total_out ----------------->
+ */
+ cryp->areq = areq;
+ cryp->authsize = crypto_aead_authsize(crypto_aead_reqtfm(areq));
+ cryp->total_in = areq->assoclen + areq->cryptlen;
+ if (is_encrypt(cryp))
+ /* Append auth tag to output */
+ cryp->total_out = cryp->total_in + cryp->authsize;
+ else
+ /* No auth tag in output */
+ cryp->total_out = cryp->total_in - cryp->authsize;
+ }
cryp->total_in_save = cryp->total_in;
cryp->total_out_save = cryp->total_out;
- cryp->in_sg = req->src;
- cryp->out_sg = req->dst;
+ cryp->in_sg = req ? req->src : areq->src;
+ cryp->out_sg = req ? req->dst : areq->dst;
cryp->out_sg_save = cryp->out_sg;
cryp->in_sg_len = sg_nents_for_len(cryp->in_sg, cryp->total_in);
@@ -686,6 +986,12 @@ static int stm32_cryp_prepare_req(struct crypto_engine *engine,
scatterwalk_start(&cryp->in_walk, cryp->in_sg);
scatterwalk_start(&cryp->out_walk, cryp->out_sg);
+ if (is_gcm(cryp) || is_ccm(cryp)) {
+ /* In output, jump after assoc data */
+ scatterwalk_advance(&cryp->out_walk, cryp->areq->assoclen);
+ cryp->total_out -= cryp->areq->assoclen;
+ }
+
ret = stm32_cryp_hw_init(cryp);
out:
if (ret)
@@ -695,14 +1001,20 @@ out:
}
static int stm32_cryp_prepare_cipher_req(struct crypto_engine *engine,
- struct ablkcipher_request *req)
+ void *areq)
{
- return stm32_cryp_prepare_req(engine, req);
+ struct ablkcipher_request *req = container_of(areq,
+ struct ablkcipher_request,
+ base);
+
+ return stm32_cryp_prepare_req(req, NULL);
}
-static int stm32_cryp_cipher_one_req(struct crypto_engine *engine,
- struct ablkcipher_request *req)
+static int stm32_cryp_cipher_one_req(struct crypto_engine *engine, void *areq)
{
+ struct ablkcipher_request *req = container_of(areq,
+ struct ablkcipher_request,
+ base);
struct stm32_cryp_ctx *ctx = crypto_ablkcipher_ctx(
crypto_ablkcipher_reqtfm(req));
struct stm32_cryp *cryp = ctx->cryp;
@@ -713,6 +1025,34 @@ static int stm32_cryp_cipher_one_req(struct crypto_engine *engine,
return stm32_cryp_cpu_start(cryp);
}
+static int stm32_cryp_prepare_aead_req(struct crypto_engine *engine, void *areq)
+{
+ struct aead_request *req = container_of(areq, struct aead_request,
+ base);
+
+ return stm32_cryp_prepare_req(NULL, req);
+}
+
+static int stm32_cryp_aead_one_req(struct crypto_engine *engine, void *areq)
+{
+ struct aead_request *req = container_of(areq, struct aead_request,
+ base);
+ struct stm32_cryp_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
+ struct stm32_cryp *cryp = ctx->cryp;
+
+ if (!cryp)
+ return -ENODEV;
+
+ if (unlikely(!cryp->areq->assoclen &&
+ !stm32_cryp_get_input_text_len(cryp))) {
+ /* No input data to process: get tag and finish */
+ stm32_cryp_finish_req(cryp, 0);
+ return 0;
+ }
+
+ return stm32_cryp_cpu_start(cryp);
+}
+
static u32 *stm32_cryp_next_out(struct stm32_cryp *cryp, u32 *dst,
unsigned int n)
{
@@ -745,6 +1085,111 @@ static u32 *stm32_cryp_next_in(struct stm32_cryp *cryp, u32 *src,
return (u32 *)((u8 *)src + n);
}
+static int stm32_cryp_read_auth_tag(struct stm32_cryp *cryp)
+{
+ u32 cfg, size_bit, *dst, d32;
+ u8 *d8;
+ unsigned int i, j;
+ int ret = 0;
+
+ /* Update Config */
+ cfg = stm32_cryp_read(cryp, CRYP_CR);
+
+ cfg &= ~CR_PH_MASK;
+ cfg |= CR_PH_FINAL;
+ cfg &= ~CR_DEC_NOT_ENC;
+ cfg |= CR_CRYPEN;
+
+ stm32_cryp_write(cryp, CRYP_CR, cfg);
+
+ if (is_gcm(cryp)) {
+ /* GCM: write aad and payload size (in bits) */
+ size_bit = cryp->areq->assoclen * 8;
+ if (cryp->caps->swap_final)
+ size_bit = cpu_to_be32(size_bit);
+
+ stm32_cryp_write(cryp, CRYP_DIN, 0);
+ stm32_cryp_write(cryp, CRYP_DIN, size_bit);
+
+ size_bit = is_encrypt(cryp) ? cryp->areq->cryptlen :
+ cryp->areq->cryptlen - AES_BLOCK_SIZE;
+ size_bit *= 8;
+ if (cryp->caps->swap_final)
+ size_bit = cpu_to_be32(size_bit);
+
+ stm32_cryp_write(cryp, CRYP_DIN, 0);
+ stm32_cryp_write(cryp, CRYP_DIN, size_bit);
+ } else {
+ /* CCM: write CTR0 */
+ u8 iv[AES_BLOCK_SIZE];
+ u32 *iv32 = (u32 *)iv;
+
+ memcpy(iv, cryp->areq->iv, AES_BLOCK_SIZE);
+ memset(iv + AES_BLOCK_SIZE - 1 - iv[0], 0, iv[0] + 1);
+
+ for (i = 0; i < AES_BLOCK_32; i++) {
+ if (!cryp->caps->padding_wa)
+ *iv32 = cpu_to_be32(*iv32);
+ stm32_cryp_write(cryp, CRYP_DIN, *iv32++);
+ }
+ }
+
+ /* Wait for output data */
+ ret = stm32_cryp_wait_output(cryp);
+ if (ret) {
+ dev_err(cryp->dev, "Timeout (read tag)\n");
+ return ret;
+ }
+
+ if (is_encrypt(cryp)) {
+ /* Get and write tag */
+ dst = sg_virt(cryp->out_sg) + _walked_out;
+
+ for (i = 0; i < AES_BLOCK_32; i++) {
+ if (cryp->total_out >= sizeof(u32)) {
+ /* Read a full u32 */
+ *dst = stm32_cryp_read(cryp, CRYP_DOUT);
+
+ dst = stm32_cryp_next_out(cryp, dst,
+ sizeof(u32));
+ cryp->total_out -= sizeof(u32);
+ } else if (!cryp->total_out) {
+ /* Empty fifo out (data from input padding) */
+ stm32_cryp_read(cryp, CRYP_DOUT);
+ } else {
+ /* Read less than an u32 */
+ d32 = stm32_cryp_read(cryp, CRYP_DOUT);
+ d8 = (u8 *)&d32;
+
+ for (j = 0; j < cryp->total_out; j++) {
+ *((u8 *)dst) = *(d8++);
+ dst = stm32_cryp_next_out(cryp, dst, 1);
+ }
+ cryp->total_out = 0;
+ }
+ }
+ } else {
+ /* Get and check tag */
+ u32 in_tag[AES_BLOCK_32], out_tag[AES_BLOCK_32];
+
+ scatterwalk_map_and_copy(in_tag, cryp->in_sg,
+ cryp->total_in_save - cryp->authsize,
+ cryp->authsize, 0);
+
+ for (i = 0; i < AES_BLOCK_32; i++)
+ out_tag[i] = stm32_cryp_read(cryp, CRYP_DOUT);
+
+ if (crypto_memneq(in_tag, out_tag, cryp->authsize))
+ ret = -EBADMSG;
+ }
+
+ /* Disable cryp */
+ cfg &= ~CR_CRYPEN;
+ stm32_cryp_write(cryp, CRYP_CR, cfg);
+
+ return ret;
+}
+
static void stm32_cryp_check_ctr_counter(struct stm32_cryp *cryp)
{
u32 cr;
@@ -777,17 +1222,24 @@ static bool stm32_cryp_irq_read_data(struct stm32_cryp *cryp)
unsigned int i, j;
u32 d32, *dst;
u8 *d8;
+ size_t tag_size;
+
+ /* Do no read tag now (if any) */
+ if (is_encrypt(cryp) && (is_gcm(cryp) || is_ccm(cryp)))
+ tag_size = cryp->authsize;
+ else
+ tag_size = 0;
dst = sg_virt(cryp->out_sg) + _walked_out;
for (i = 0; i < cryp->hw_blocksize / sizeof(u32); i++) {
- if (likely(cryp->total_out >= sizeof(u32))) {
+ if (likely(cryp->total_out - tag_size >= sizeof(u32))) {
/* Read a full u32 */
*dst = stm32_cryp_read(cryp, CRYP_DOUT);
dst = stm32_cryp_next_out(cryp, dst, sizeof(u32));
cryp->total_out -= sizeof(u32);
- } else if (!cryp->total_out) {
+ } else if (cryp->total_out == tag_size) {
/* Empty fifo out (data from input padding) */
d32 = stm32_cryp_read(cryp, CRYP_DOUT);
} else {
@@ -795,15 +1247,15 @@ static bool stm32_cryp_irq_read_data(struct stm32_cryp *cryp)
d32 = stm32_cryp_read(cryp, CRYP_DOUT);
d8 = (u8 *)&d32;
- for (j = 0; j < cryp->total_out; j++) {
+ for (j = 0; j < cryp->total_out - tag_size; j++) {
*((u8 *)dst) = *(d8++);
dst = stm32_cryp_next_out(cryp, dst, 1);
}
- cryp->total_out = 0;
+ cryp->total_out = tag_size;
}
}
- return !cryp->total_out || !cryp->total_in;
+ return !(cryp->total_out - tag_size) || !cryp->total_in;
}
static void stm32_cryp_irq_write_block(struct stm32_cryp *cryp)
@@ -811,33 +1263,219 @@ static void stm32_cryp_irq_write_block(struct stm32_cryp *cryp)
unsigned int i, j;
u32 *src;
u8 d8[4];
+ size_t tag_size;
+
+ /* Do no write tag (if any) */
+ if (is_decrypt(cryp) && (is_gcm(cryp) || is_ccm(cryp)))
+ tag_size = cryp->authsize;
+ else
+ tag_size = 0;
src = sg_virt(cryp->in_sg) + _walked_in;
for (i = 0; i < cryp->hw_blocksize / sizeof(u32); i++) {
- if (likely(cryp->total_in >= sizeof(u32))) {
+ if (likely(cryp->total_in - tag_size >= sizeof(u32))) {
/* Write a full u32 */
stm32_cryp_write(cryp, CRYP_DIN, *src);
src = stm32_cryp_next_in(cryp, src, sizeof(u32));
cryp->total_in -= sizeof(u32);
- } else if (!cryp->total_in) {
+ } else if (cryp->total_in == tag_size) {
/* Write padding data */
stm32_cryp_write(cryp, CRYP_DIN, 0);
} else {
/* Write less than an u32 */
memset(d8, 0, sizeof(u32));
- for (j = 0; j < cryp->total_in; j++) {
+ for (j = 0; j < cryp->total_in - tag_size; j++) {
d8[j] = *((u8 *)src);
src = stm32_cryp_next_in(cryp, src, 1);
}
stm32_cryp_write(cryp, CRYP_DIN, *(u32 *)d8);
- cryp->total_in = 0;
+ cryp->total_in = tag_size;
}
}
}
+static void stm32_cryp_irq_write_gcm_padded_data(struct stm32_cryp *cryp)
+{
+ int err;
+ u32 cfg, tmp[AES_BLOCK_32];
+ size_t total_in_ori = cryp->total_in;
+ struct scatterlist *out_sg_ori = cryp->out_sg;
+ unsigned int i;
+
+ /* 'Special workaround' procedure described in the datasheet */
+
+ /* a) disable ip */
+ stm32_cryp_write(cryp, CRYP_IMSCR, 0);
+ cfg = stm32_cryp_read(cryp, CRYP_CR);
+ cfg &= ~CR_CRYPEN;
+ stm32_cryp_write(cryp, CRYP_CR, cfg);
+
+ /* b) Update IV1R */
+ stm32_cryp_write(cryp, CRYP_IV1RR, cryp->gcm_ctr - 2);
+
+ /* c) change mode to CTR */
+ cfg &= ~CR_ALGO_MASK;
+ cfg |= CR_AES_CTR;
+ stm32_cryp_write(cryp, CRYP_CR, cfg);
+
+ /* a) enable IP */
+ cfg |= CR_CRYPEN;
+ stm32_cryp_write(cryp, CRYP_CR, cfg);
+
+ /* b) pad and write the last block */
+ stm32_cryp_irq_write_block(cryp);
+ cryp->total_in = total_in_ori;
+ err = stm32_cryp_wait_output(cryp);
+ if (err) {
+ dev_err(cryp->dev, "Timeout (write gcm header)\n");
+ return stm32_cryp_finish_req(cryp, err);
+ }
+
+ /* c) get and store encrypted data */
+ stm32_cryp_irq_read_data(cryp);
+ scatterwalk_map_and_copy(tmp, out_sg_ori,
+ cryp->total_in_save - total_in_ori,
+ total_in_ori, 0);
+
+ /* d) change mode back to AES GCM */
+ cfg &= ~CR_ALGO_MASK;
+ cfg |= CR_AES_GCM;
+ stm32_cryp_write(cryp, CRYP_CR, cfg);
+
+ /* e) change phase to Final */
+ cfg &= ~CR_PH_MASK;
+ cfg |= CR_PH_FINAL;
+ stm32_cryp_write(cryp, CRYP_CR, cfg);
+
+ /* f) write padded data */
+ for (i = 0; i < AES_BLOCK_32; i++) {
+ if (cryp->total_in)
+ stm32_cryp_write(cryp, CRYP_DIN, tmp[i]);
+ else
+ stm32_cryp_write(cryp, CRYP_DIN, 0);
+
+ cryp->total_in -= min_t(size_t, sizeof(u32), cryp->total_in);
+ }
+
+ /* g) Empty fifo out */
+ err = stm32_cryp_wait_output(cryp);
+ if (err) {
+ dev_err(cryp->dev, "Timeout (write gcm header)\n");
+ return stm32_cryp_finish_req(cryp, err);
+ }
+
+ for (i = 0; i < AES_BLOCK_32; i++)
+ stm32_cryp_read(cryp, CRYP_DOUT);
+
+ /* h) run the he normal Final phase */
+ stm32_cryp_finish_req(cryp, 0);
+}
+
+static void stm32_cryp_irq_set_npblb(struct stm32_cryp *cryp)
+{
+ u32 cfg, payload_bytes;
+
+ /* disable ip, set NPBLB and reneable ip */
+ cfg = stm32_cryp_read(cryp, CRYP_CR);
+ cfg &= ~CR_CRYPEN;
+ stm32_cryp_write(cryp, CRYP_CR, cfg);
+
+ payload_bytes = is_decrypt(cryp) ? cryp->total_in - cryp->authsize :
+ cryp->total_in;
+ cfg |= (cryp->hw_blocksize - payload_bytes) << CR_NBPBL_SHIFT;
+ cfg |= CR_CRYPEN;
+ stm32_cryp_write(cryp, CRYP_CR, cfg);
+}
+
+static void stm32_cryp_irq_write_ccm_padded_data(struct stm32_cryp *cryp)
+{
+ int err = 0;
+ u32 cfg, iv1tmp;
+ u32 cstmp1[AES_BLOCK_32], cstmp2[AES_BLOCK_32], tmp[AES_BLOCK_32];
+ size_t last_total_out, total_in_ori = cryp->total_in;
+ struct scatterlist *out_sg_ori = cryp->out_sg;
+ unsigned int i;
+
+ /* 'Special workaround' procedure described in the datasheet */
+ cryp->flags |= FLG_CCM_PADDED_WA;
+
+ /* a) disable ip */
+ stm32_cryp_write(cryp, CRYP_IMSCR, 0);
+
+ cfg = stm32_cryp_read(cryp, CRYP_CR);
+ cfg &= ~CR_CRYPEN;
+ stm32_cryp_write(cryp, CRYP_CR, cfg);
+
+ /* b) get IV1 from CRYP_CSGCMCCM7 */
+ iv1tmp = stm32_cryp_read(cryp, CRYP_CSGCMCCM0R + 7 * 4);
+
+ /* c) Load CRYP_CSGCMCCMxR */
+ for (i = 0; i < ARRAY_SIZE(cstmp1); i++)
+ cstmp1[i] = stm32_cryp_read(cryp, CRYP_CSGCMCCM0R + i * 4);
+
+ /* d) Write IV1R */
+ stm32_cryp_write(cryp, CRYP_IV1RR, iv1tmp);
+
+ /* e) change mode to CTR */
+ cfg &= ~CR_ALGO_MASK;
+ cfg |= CR_AES_CTR;
+ stm32_cryp_write(cryp, CRYP_CR, cfg);
+
+ /* a) enable IP */
+ cfg |= CR_CRYPEN;
+ stm32_cryp_write(cryp, CRYP_CR, cfg);
+
+ /* b) pad and write the last block */
+ stm32_cryp_irq_write_block(cryp);
+ cryp->total_in = total_in_ori;
+ err = stm32_cryp_wait_output(cryp);
+ if (err) {
+ dev_err(cryp->dev, "Timeout (wite ccm padded data)\n");
+ return stm32_cryp_finish_req(cryp, err);
+ }
+
+ /* c) get and store decrypted data */
+ last_total_out = cryp->total_out;
+ stm32_cryp_irq_read_data(cryp);
+
+ memset(tmp, 0, sizeof(tmp));
+ scatterwalk_map_and_copy(tmp, out_sg_ori,
+ cryp->total_out_save - last_total_out,
+ last_total_out, 0);
+
+ /* d) Load again CRYP_CSGCMCCMxR */
+ for (i = 0; i < ARRAY_SIZE(cstmp2); i++)
+ cstmp2[i] = stm32_cryp_read(cryp, CRYP_CSGCMCCM0R + i * 4);
+
+ /* e) change mode back to AES CCM */
+ cfg &= ~CR_ALGO_MASK;
+ cfg |= CR_AES_CCM;
+ stm32_cryp_write(cryp, CRYP_CR, cfg);
+
+ /* f) change phase to header */
+ cfg &= ~CR_PH_MASK;
+ cfg |= CR_PH_HEADER;
+ stm32_cryp_write(cryp, CRYP_CR, cfg);
+
+ /* g) XOR and write padded data */
+ for (i = 0; i < ARRAY_SIZE(tmp); i++) {
+ tmp[i] ^= cstmp1[i];
+ tmp[i] ^= cstmp2[i];
+ stm32_cryp_write(cryp, CRYP_DIN, tmp[i]);
+ }
+
+ /* h) wait for completion */
+ err = stm32_cryp_wait_busy(cryp);
+ if (err)
+ dev_err(cryp->dev, "Timeout (wite ccm padded data)\n");
+
+ /* i) run the he normal Final phase */
+ stm32_cryp_finish_req(cryp, err);
+}
+
static void stm32_cryp_irq_write_data(struct stm32_cryp *cryp)
{
if (unlikely(!cryp->total_in)) {
@@ -845,28 +1483,220 @@ static void stm32_cryp_irq_write_data(struct stm32_cryp *cryp)
return;
}
+ if (unlikely(cryp->total_in < AES_BLOCK_SIZE &&
+ (stm32_cryp_get_hw_mode(cryp) == CR_AES_GCM) &&
+ is_encrypt(cryp))) {
+ /* Padding for AES GCM encryption */
+ if (cryp->caps->padding_wa)
+ /* Special case 1 */
+ return stm32_cryp_irq_write_gcm_padded_data(cryp);
+
+ /* Setting padding bytes (NBBLB) */
+ stm32_cryp_irq_set_npblb(cryp);
+ }
+
+ if (unlikely((cryp->total_in - cryp->authsize < AES_BLOCK_SIZE) &&
+ (stm32_cryp_get_hw_mode(cryp) == CR_AES_CCM) &&
+ is_decrypt(cryp))) {
+ /* Padding for AES CCM decryption */
+ if (cryp->caps->padding_wa)
+ /* Special case 2 */
+ return stm32_cryp_irq_write_ccm_padded_data(cryp);
+
+ /* Setting padding bytes (NBBLB) */
+ stm32_cryp_irq_set_npblb(cryp);
+ }
+
if (is_aes(cryp) && is_ctr(cryp))
stm32_cryp_check_ctr_counter(cryp);
stm32_cryp_irq_write_block(cryp);
}
+static void stm32_cryp_irq_write_gcm_header(struct stm32_cryp *cryp)
+{
+ int err;
+ unsigned int i, j;
+ u32 cfg, *src;
+
+ src = sg_virt(cryp->in_sg) + _walked_in;
+
+ for (i = 0; i < AES_BLOCK_32; i++) {
+ stm32_cryp_write(cryp, CRYP_DIN, *src);
+
+ src = stm32_cryp_next_in(cryp, src, sizeof(u32));
+ cryp->total_in -= min_t(size_t, sizeof(u32), cryp->total_in);
+
+ /* Check if whole header written */
+ if ((cryp->total_in_save - cryp->total_in) ==
+ cryp->areq->assoclen) {
+ /* Write padding if needed */
+ for (j = i + 1; j < AES_BLOCK_32; j++)
+ stm32_cryp_write(cryp, CRYP_DIN, 0);
+
+ /* Wait for completion */
+ err = stm32_cryp_wait_busy(cryp);
+ if (err) {
+ dev_err(cryp->dev, "Timeout (gcm header)\n");
+ return stm32_cryp_finish_req(cryp, err);
+ }
+
+ if (stm32_cryp_get_input_text_len(cryp)) {
+ /* Phase 3 : payload */
+ cfg = stm32_cryp_read(cryp, CRYP_CR);
+ cfg &= ~CR_CRYPEN;
+ stm32_cryp_write(cryp, CRYP_CR, cfg);
+
+ cfg &= ~CR_PH_MASK;
+ cfg |= CR_PH_PAYLOAD;
+ cfg |= CR_CRYPEN;
+ stm32_cryp_write(cryp, CRYP_CR, cfg);
+ } else {
+ /* Phase 4 : tag */
+ stm32_cryp_write(cryp, CRYP_IMSCR, 0);
+ stm32_cryp_finish_req(cryp, 0);
+ }
+
+ break;
+ }
+
+ if (!cryp->total_in)
+ break;
+ }
+}
+
+static void stm32_cryp_irq_write_ccm_header(struct stm32_cryp *cryp)
+{
+ int err;
+ unsigned int i = 0, j, k;
+ u32 alen, cfg, *src;
+ u8 d8[4];
+
+ src = sg_virt(cryp->in_sg) + _walked_in;
+ alen = cryp->areq->assoclen;
+
+ if (!_walked_in) {
+ if (cryp->areq->assoclen <= 65280) {
+ /* Write first u32 of B1 */
+ d8[0] = (alen >> 8) & 0xFF;
+ d8[1] = alen & 0xFF;
+ d8[2] = *((u8 *)src);
+ src = stm32_cryp_next_in(cryp, src, 1);
+ d8[3] = *((u8 *)src);
+ src = stm32_cryp_next_in(cryp, src, 1);
+
+ stm32_cryp_write(cryp, CRYP_DIN, *(u32 *)d8);
+ i++;
+
+ cryp->total_in -= min_t(size_t, 2, cryp->total_in);
+ } else {
+ /* Build the two first u32 of B1 */
+ d8[0] = 0xFF;
+ d8[1] = 0xFE;
+ d8[2] = alen & 0xFF000000;
+ d8[3] = alen & 0x00FF0000;
+
+ stm32_cryp_write(cryp, CRYP_DIN, *(u32 *)d8);
+ i++;
+
+ d8[0] = alen & 0x0000FF00;
+ d8[1] = alen & 0x000000FF;
+ d8[2] = *((u8 *)src);
+ src = stm32_cryp_next_in(cryp, src, 1);
+ d8[3] = *((u8 *)src);
+ src = stm32_cryp_next_in(cryp, src, 1);
+
+ stm32_cryp_write(cryp, CRYP_DIN, *(u32 *)d8);
+ i++;
+
+ cryp->total_in -= min_t(size_t, 2, cryp->total_in);
+ }
+ }
+
+ /* Write next u32 */
+ for (; i < AES_BLOCK_32; i++) {
+ /* Build an u32 */
+ memset(d8, 0, sizeof(u32));
+ for (k = 0; k < sizeof(u32); k++) {
+ d8[k] = *((u8 *)src);
+ src = stm32_cryp_next_in(cryp, src, 1);
+
+ cryp->total_in -= min_t(size_t, 1, cryp->total_in);
+ if ((cryp->total_in_save - cryp->total_in) == alen)
+ break;
+ }
+
+ stm32_cryp_write(cryp, CRYP_DIN, *(u32 *)d8);
+
+ if ((cryp->total_in_save - cryp->total_in) == alen) {
+ /* Write padding if needed */
+ for (j = i + 1; j < AES_BLOCK_32; j++)
+ stm32_cryp_write(cryp, CRYP_DIN, 0);
+
+ /* Wait for completion */
+ err = stm32_cryp_wait_busy(cryp);
+ if (err) {
+ dev_err(cryp->dev, "Timeout (ccm header)\n");
+ return stm32_cryp_finish_req(cryp, err);
+ }
+
+ if (stm32_cryp_get_input_text_len(cryp)) {
+ /* Phase 3 : payload */
+ cfg = stm32_cryp_read(cryp, CRYP_CR);
+ cfg &= ~CR_CRYPEN;
+ stm32_cryp_write(cryp, CRYP_CR, cfg);
+
+ cfg &= ~CR_PH_MASK;
+ cfg |= CR_PH_PAYLOAD;
+ cfg |= CR_CRYPEN;
+ stm32_cryp_write(cryp, CRYP_CR, cfg);
+ } else {
+ /* Phase 4 : tag */
+ stm32_cryp_write(cryp, CRYP_IMSCR, 0);
+ stm32_cryp_finish_req(cryp, 0);
+ }
+
+ break;
+ }
+ }
+}
+
static irqreturn_t stm32_cryp_irq_thread(int irq, void *arg)
{
struct stm32_cryp *cryp = arg;
+ u32 ph;
if (cryp->irq_status & MISR_OUT)
/* Output FIFO IRQ: read data */
if (unlikely(stm32_cryp_irq_read_data(cryp))) {
/* All bytes processed, finish */
stm32_cryp_write(cryp, CRYP_IMSCR, 0);
- stm32_cryp_finish_req(cryp);
+ stm32_cryp_finish_req(cryp, 0);
return IRQ_HANDLED;
}
if (cryp->irq_status & MISR_IN) {
- /* Input FIFO IRQ: write data */
- stm32_cryp_irq_write_data(cryp);
+ if (is_gcm(cryp)) {
+ ph = stm32_cryp_read(cryp, CRYP_CR) & CR_PH_MASK;
+ if (unlikely(ph == CR_PH_HEADER))
+ /* Write Header */
+ stm32_cryp_irq_write_gcm_header(cryp);
+ else
+ /* Input FIFO IRQ: write data */
+ stm32_cryp_irq_write_data(cryp);
+ cryp->gcm_ctr++;
+ } else if (is_ccm(cryp)) {
+ ph = stm32_cryp_read(cryp, CRYP_CR) & CR_PH_MASK;
+ if (unlikely(ph == CR_PH_HEADER))
+ /* Write Header */
+ stm32_cryp_irq_write_ccm_header(cryp);
+ else
+ /* Input FIFO IRQ: write data */
+ stm32_cryp_irq_write_data(cryp);
+ } else {
+ /* Input FIFO IRQ: write data */
+ stm32_cryp_irq_write_data(cryp);
+ }
}
return IRQ_HANDLED;
@@ -1028,8 +1858,62 @@ static struct crypto_alg crypto_algs[] = {
},
};
+static struct aead_alg aead_algs[] = {
+{
+ .setkey = stm32_cryp_aes_aead_setkey,
+ .setauthsize = stm32_cryp_aes_gcm_setauthsize,
+ .encrypt = stm32_cryp_aes_gcm_encrypt,
+ .decrypt = stm32_cryp_aes_gcm_decrypt,
+ .init = stm32_cryp_aes_aead_init,
+ .ivsize = 12,
+ .maxauthsize = AES_BLOCK_SIZE,
+
+ .base = {
+ .cra_name = "gcm(aes)",
+ .cra_driver_name = "stm32-gcm-aes",
+ .cra_priority = 200,
+ .cra_flags = CRYPTO_ALG_ASYNC,
+ .cra_blocksize = 1,
+ .cra_ctxsize = sizeof(struct stm32_cryp_ctx),
+ .cra_alignmask = 0xf,
+ .cra_module = THIS_MODULE,
+ },
+},
+{
+ .setkey = stm32_cryp_aes_aead_setkey,
+ .setauthsize = stm32_cryp_aes_ccm_setauthsize,
+ .encrypt = stm32_cryp_aes_ccm_encrypt,
+ .decrypt = stm32_cryp_aes_ccm_decrypt,
+ .init = stm32_cryp_aes_aead_init,
+ .ivsize = AES_BLOCK_SIZE,
+ .maxauthsize = AES_BLOCK_SIZE,
+
+ .base = {
+ .cra_name = "ccm(aes)",
+ .cra_driver_name = "stm32-ccm-aes",
+ .cra_priority = 200,
+ .cra_flags = CRYPTO_ALG_ASYNC,
+ .cra_blocksize = 1,
+ .cra_ctxsize = sizeof(struct stm32_cryp_ctx),
+ .cra_alignmask = 0xf,
+ .cra_module = THIS_MODULE,
+ },
+},
+};
+
+static const struct stm32_cryp_caps f7_data = {
+ .swap_final = true,
+ .padding_wa = true,
+};
+
+static const struct stm32_cryp_caps mp1_data = {
+ .swap_final = false,
+ .padding_wa = false,
+};
+
static const struct of_device_id stm32_dt_ids[] = {
- { .compatible = "st,stm32f756-cryp", },
+ { .compatible = "st,stm32f756-cryp", .data = &f7_data},
+ { .compatible = "st,stm32mp1-cryp", .data = &mp1_data},
{},
};
MODULE_DEVICE_TABLE(of, stm32_dt_ids);
@@ -1046,6 +1930,10 @@ static int stm32_cryp_probe(struct platform_device *pdev)
if (!cryp)
return -ENOMEM;
+ cryp->caps = of_device_get_match_data(dev);
+ if (!cryp->caps)
+ return -ENODEV;
+
cryp->dev = dev;
mutex_init(&cryp->lock);
@@ -1102,9 +1990,6 @@ static int stm32_cryp_probe(struct platform_device *pdev)
goto err_engine1;
}
- cryp->engine->prepare_cipher_request = stm32_cryp_prepare_cipher_req;
- cryp->engine->cipher_one_request = stm32_cryp_cipher_one_req;
-
ret = crypto_engine_start(cryp->engine);
if (ret) {
dev_err(dev, "Could not start crypto engine\n");
@@ -1117,10 +2002,16 @@ static int stm32_cryp_probe(struct platform_device *pdev)
goto err_algs;
}
+ ret = crypto_register_aeads(aead_algs, ARRAY_SIZE(aead_algs));
+ if (ret)
+ goto err_aead_algs;
+
dev_info(dev, "Initialized\n");
return 0;
+err_aead_algs:
+ crypto_unregister_algs(crypto_algs, ARRAY_SIZE(crypto_algs));
err_algs:
err_engine2:
crypto_engine_exit(cryp->engine);
@@ -1141,6 +2032,7 @@ static int stm32_cryp_remove(struct platform_device *pdev)
if (!cryp)
return -ENODEV;
+ crypto_unregister_aeads(aead_algs, ARRAY_SIZE(aead_algs));
crypto_unregister_algs(crypto_algs, ARRAY_SIZE(crypto_algs));
crypto_engine_exit(cryp->engine);
diff --git a/drivers/crypto/stm32/stm32-hash.c b/drivers/crypto/stm32/stm32-hash.c
index 4ca4a264a833..981e45692695 100644
--- a/drivers/crypto/stm32/stm32-hash.c
+++ b/drivers/crypto/stm32/stm32-hash.c
@@ -122,6 +122,7 @@ enum stm32_hash_data_format {
#define HASH_DMA_THRESHOLD 50
struct stm32_hash_ctx {
+ struct crypto_engine_ctx enginectx;
struct stm32_hash_dev *hdev;
unsigned long flags;
@@ -626,7 +627,7 @@ static int stm32_hash_dma_send(struct stm32_hash_dev *hdev)
writesl(hdev->io_base + HASH_DIN, buffer,
DIV_ROUND_UP(ncp, sizeof(u32)));
}
- stm32_hash_set_nblw(hdev, DIV_ROUND_UP(ncp, sizeof(u32)));
+ stm32_hash_set_nblw(hdev, ncp);
reg = stm32_hash_read(hdev, HASH_STR);
reg |= HASH_STR_DCAL;
stm32_hash_write(hdev, HASH_STR, reg);
@@ -743,13 +744,15 @@ static int stm32_hash_final_req(struct stm32_hash_dev *hdev)
struct ahash_request *req = hdev->req;
struct stm32_hash_request_ctx *rctx = ahash_request_ctx(req);
int err;
+ int buflen = rctx->bufcnt;
+
+ rctx->bufcnt = 0;
if (!(rctx->flags & HASH_FLAGS_CPU))
err = stm32_hash_dma_send(hdev);
else
- err = stm32_hash_xmit_cpu(hdev, rctx->buffer, rctx->bufcnt, 1);
+ err = stm32_hash_xmit_cpu(hdev, rctx->buffer, buflen, 1);
- rctx->bufcnt = 0;
return err;
}
@@ -828,15 +831,19 @@ static int stm32_hash_hw_init(struct stm32_hash_dev *hdev,
return 0;
}
+static int stm32_hash_one_request(struct crypto_engine *engine, void *areq);
+static int stm32_hash_prepare_req(struct crypto_engine *engine, void *areq);
+
static int stm32_hash_handle_queue(struct stm32_hash_dev *hdev,
struct ahash_request *req)
{
return crypto_transfer_hash_request_to_engine(hdev->engine, req);
}
-static int stm32_hash_prepare_req(struct crypto_engine *engine,
- struct ahash_request *req)
+static int stm32_hash_prepare_req(struct crypto_engine *engine, void *areq)
{
+ struct ahash_request *req = container_of(areq, struct ahash_request,
+ base);
struct stm32_hash_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
struct stm32_hash_dev *hdev = stm32_hash_find_dev(ctx);
struct stm32_hash_request_ctx *rctx;
@@ -854,9 +861,10 @@ static int stm32_hash_prepare_req(struct crypto_engine *engine,
return stm32_hash_hw_init(hdev, rctx);
}
-static int stm32_hash_one_request(struct crypto_engine *engine,
- struct ahash_request *req)
+static int stm32_hash_one_request(struct crypto_engine *engine, void *areq)
{
+ struct ahash_request *req = container_of(areq, struct ahash_request,
+ base);
struct stm32_hash_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
struct stm32_hash_dev *hdev = stm32_hash_find_dev(ctx);
struct stm32_hash_request_ctx *rctx;
@@ -1033,6 +1041,9 @@ static int stm32_hash_cra_init_algs(struct crypto_tfm *tfm,
if (algs_hmac_name)
ctx->flags |= HASH_FLAGS_HMAC;
+ ctx->enginectx.op.do_one_request = stm32_hash_one_request;
+ ctx->enginectx.op.prepare_request = stm32_hash_prepare_req;
+ ctx->enginectx.op.unprepare_request = NULL;
return 0;
}
@@ -1096,6 +1107,8 @@ static irqreturn_t stm32_hash_irq_handler(int irq, void *dev_id)
reg &= ~HASH_SR_OUTPUT_READY;
stm32_hash_write(hdev, HASH_SR, reg);
hdev->flags |= HASH_FLAGS_OUTPUT_READY;
+ /* Disable IT*/
+ stm32_hash_write(hdev, HASH_IMR, 0);
return IRQ_WAKE_THREAD;
}
@@ -1404,18 +1417,19 @@ MODULE_DEVICE_TABLE(of, stm32_hash_of_match);
static int stm32_hash_get_of_match(struct stm32_hash_dev *hdev,
struct device *dev)
{
- int err;
-
hdev->pdata = of_device_get_match_data(dev);
if (!hdev->pdata) {
dev_err(dev, "no compatible OF match\n");
return -EINVAL;
}
- err = of_property_read_u32(dev->of_node, "dma-maxburst",
- &hdev->dma_maxburst);
+ if (of_property_read_u32(dev->of_node, "dma-maxburst",
+ &hdev->dma_maxburst)) {
+ dev_info(dev, "dma-maxburst not specified, using 0\n");
+ hdev->dma_maxburst = 0;
+ }
- return err;
+ return 0;
}
static int stm32_hash_probe(struct platform_device *pdev)
@@ -1493,9 +1507,6 @@ static int stm32_hash_probe(struct platform_device *pdev)
goto err_engine;
}
- hdev->engine->prepare_hash_request = stm32_hash_prepare_req;
- hdev->engine->hash_one_request = stm32_hash_one_request;
-
ret = crypto_engine_start(hdev->engine);
if (ret)
goto err_engine_start;
diff --git a/drivers/crypto/sunxi-ss/sun4i-ss-core.c b/drivers/crypto/sunxi-ss/sun4i-ss-core.c
index 1547cbe13dc2..a81d89b3b7d8 100644
--- a/drivers/crypto/sunxi-ss/sun4i-ss-core.c
+++ b/drivers/crypto/sunxi-ss/sun4i-ss-core.c
@@ -451,6 +451,7 @@ static struct platform_driver sun4i_ss_driver = {
module_platform_driver(sun4i_ss_driver);
+MODULE_ALIAS("platform:sun4i-ss");
MODULE_DESCRIPTION("Allwinner Security System cryptographic accelerator");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Corentin LABBE <clabbe.montjoie@gmail.com>");
diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c
index 6882fa2f8bad..7cebf0a6ffbc 100644
--- a/drivers/crypto/talitos.c
+++ b/drivers/crypto/talitos.c
@@ -104,16 +104,34 @@ static void to_talitos_ptr_ext_or(struct talitos_ptr *ptr, u8 val, bool is_sec1)
/*
* map virtual single (contiguous) pointer to h/w descriptor pointer
*/
+static void __map_single_talitos_ptr(struct device *dev,
+ struct talitos_ptr *ptr,
+ unsigned int len, void *data,
+ enum dma_data_direction dir,
+ unsigned long attrs)
+{
+ dma_addr_t dma_addr = dma_map_single_attrs(dev, data, len, dir, attrs);
+ struct talitos_private *priv = dev_get_drvdata(dev);
+ bool is_sec1 = has_ftr_sec1(priv);
+
+ to_talitos_ptr(ptr, dma_addr, len, is_sec1);
+}
+
static void map_single_talitos_ptr(struct device *dev,
struct talitos_ptr *ptr,
unsigned int len, void *data,
enum dma_data_direction dir)
{
- dma_addr_t dma_addr = dma_map_single(dev, data, len, dir);
- struct talitos_private *priv = dev_get_drvdata(dev);
- bool is_sec1 = has_ftr_sec1(priv);
+ __map_single_talitos_ptr(dev, ptr, len, data, dir, 0);
+}
- to_talitos_ptr(ptr, dma_addr, len, is_sec1);
+static void map_single_talitos_ptr_nosync(struct device *dev,
+ struct talitos_ptr *ptr,
+ unsigned int len, void *data,
+ enum dma_data_direction dir)
+{
+ __map_single_talitos_ptr(dev, ptr, len, data, dir,
+ DMA_ATTR_SKIP_CPU_SYNC);
}
/*
@@ -832,8 +850,6 @@ struct talitos_ctx {
unsigned int keylen;
unsigned int enckeylen;
unsigned int authkeylen;
- dma_addr_t dma_buf;
- dma_addr_t dma_hw_context;
};
#define HASH_MAX_BLOCK_SIZE SHA512_BLOCK_SIZE
@@ -888,10 +904,12 @@ static int aead_setkey(struct crypto_aead *authenc,
ctx->dma_key = dma_map_single(dev, ctx->key, ctx->keylen,
DMA_TO_DEVICE);
+ memzero_explicit(&keys, sizeof(keys));
return 0;
badkey:
crypto_aead_set_flags(authenc, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ memzero_explicit(&keys, sizeof(keys));
return -EINVAL;
}
@@ -1130,10 +1148,10 @@ next:
return count;
}
-static int talitos_sg_map(struct device *dev, struct scatterlist *src,
- unsigned int len, struct talitos_edesc *edesc,
- struct talitos_ptr *ptr,
- int sg_count, unsigned int offset, int tbl_off)
+static int talitos_sg_map_ext(struct device *dev, struct scatterlist *src,
+ unsigned int len, struct talitos_edesc *edesc,
+ struct talitos_ptr *ptr, int sg_count,
+ unsigned int offset, int tbl_off, int elen)
{
struct talitos_private *priv = dev_get_drvdata(dev);
bool is_sec1 = has_ftr_sec1(priv);
@@ -1142,6 +1160,7 @@ static int talitos_sg_map(struct device *dev, struct scatterlist *src,
to_talitos_ptr(ptr, 0, 0, is_sec1);
return 1;
}
+ to_talitos_ptr_ext_set(ptr, elen, is_sec1);
if (sg_count == 1) {
to_talitos_ptr(ptr, sg_dma_address(src) + offset, len, is_sec1);
return sg_count;
@@ -1150,7 +1169,7 @@ static int talitos_sg_map(struct device *dev, struct scatterlist *src,
to_talitos_ptr(ptr, edesc->dma_link_tbl + offset, len, is_sec1);
return sg_count;
}
- sg_count = sg_to_link_tbl_offset(src, sg_count, offset, len,
+ sg_count = sg_to_link_tbl_offset(src, sg_count, offset, len + elen,
&edesc->link_tbl[tbl_off]);
if (sg_count == 1) {
/* Only one segment now, so no link tbl needed*/
@@ -1164,6 +1183,15 @@ static int talitos_sg_map(struct device *dev, struct scatterlist *src,
return sg_count;
}
+static int talitos_sg_map(struct device *dev, struct scatterlist *src,
+ unsigned int len, struct talitos_edesc *edesc,
+ struct talitos_ptr *ptr, int sg_count,
+ unsigned int offset, int tbl_off)
+{
+ return talitos_sg_map_ext(dev, src, len, edesc, ptr, sg_count, offset,
+ tbl_off, 0);
+}
+
/*
* fill in and submit ipsec_esp descriptor
*/
@@ -1181,7 +1209,7 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq,
unsigned int ivsize = crypto_aead_ivsize(aead);
int tbl_off = 0;
int sg_count, ret;
- int sg_link_tbl_len;
+ int elen = 0;
bool sync_needed = false;
struct talitos_private *priv = dev_get_drvdata(dev);
bool is_sec1 = has_ftr_sec1(priv);
@@ -1223,17 +1251,11 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq,
* extent is bytes of HMAC postpended to ciphertext,
* typically 12 for ipsec
*/
- sg_link_tbl_len = cryptlen;
-
- if (is_ipsec_esp) {
- to_talitos_ptr_ext_set(&desc->ptr[4], authsize, is_sec1);
+ if (is_ipsec_esp && (desc->hdr & DESC_HDR_MODE1_MDEU_CICV))
+ elen = authsize;
- if (desc->hdr & DESC_HDR_MODE1_MDEU_CICV)
- sg_link_tbl_len += authsize;
- }
-
- ret = talitos_sg_map(dev, areq->src, sg_link_tbl_len, edesc,
- &desc->ptr[4], sg_count, areq->assoclen, tbl_off);
+ ret = talitos_sg_map_ext(dev, areq->src, cryptlen, edesc, &desc->ptr[4],
+ sg_count, areq->assoclen, tbl_off, elen);
if (ret > 1) {
tbl_off += ret;
@@ -1404,7 +1426,6 @@ static struct talitos_edesc *talitos_edesc_alloc(struct device *dev,
edesc = kmalloc(alloc_len, GFP_DMA | flags);
if (!edesc) {
- dev_err(dev, "could not allocate edescriptor\n");
err = ERR_PTR(-ENOMEM);
goto error_sg;
}
@@ -1690,9 +1711,30 @@ static void common_nonsnoop_hash_unmap(struct device *dev,
struct ahash_request *areq)
{
struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq);
+ struct talitos_private *priv = dev_get_drvdata(dev);
+ bool is_sec1 = has_ftr_sec1(priv);
+ struct talitos_desc *desc = &edesc->desc;
+ struct talitos_desc *desc2 = desc + 1;
+
+ unmap_single_talitos_ptr(dev, &edesc->desc.ptr[5], DMA_FROM_DEVICE);
+ if (desc->next_desc &&
+ desc->ptr[5].ptr != desc2->ptr[5].ptr)
+ unmap_single_talitos_ptr(dev, &desc2->ptr[5], DMA_FROM_DEVICE);
talitos_sg_unmap(dev, edesc, req_ctx->psrc, NULL, 0, 0);
+ /* When using hashctx-in, must unmap it. */
+ if (from_talitos_ptr_len(&edesc->desc.ptr[1], is_sec1))
+ unmap_single_talitos_ptr(dev, &edesc->desc.ptr[1],
+ DMA_TO_DEVICE);
+ else if (desc->next_desc)
+ unmap_single_talitos_ptr(dev, &desc2->ptr[1],
+ DMA_TO_DEVICE);
+
+ if (is_sec1 && req_ctx->nbuf)
+ unmap_single_talitos_ptr(dev, &desc->ptr[3],
+ DMA_TO_DEVICE);
+
if (edesc->dma_len)
dma_unmap_single(dev, edesc->dma_link_tbl, edesc->dma_len,
DMA_BIDIRECTIONAL);
@@ -1766,8 +1808,10 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc,
/* hash context in */
if (!req_ctx->first || req_ctx->swinit) {
- to_talitos_ptr(&desc->ptr[1], ctx->dma_hw_context,
- req_ctx->hw_context_size, is_sec1);
+ map_single_talitos_ptr_nosync(dev, &desc->ptr[1],
+ req_ctx->hw_context_size,
+ req_ctx->hw_context,
+ DMA_TO_DEVICE);
req_ctx->swinit = 0;
}
/* Indicate next op is not the first. */
@@ -1793,10 +1837,9 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc,
* data in
*/
if (is_sec1 && req_ctx->nbuf) {
- dma_addr_t dma_buf = ctx->dma_buf + req_ctx->buf_idx *
- HASH_MAX_BLOCK_SIZE;
-
- to_talitos_ptr(&desc->ptr[3], dma_buf, req_ctx->nbuf, is_sec1);
+ map_single_talitos_ptr(dev, &desc->ptr[3], req_ctx->nbuf,
+ req_ctx->buf[req_ctx->buf_idx],
+ DMA_TO_DEVICE);
} else {
sg_count = talitos_sg_map(dev, req_ctx->psrc, length, edesc,
&desc->ptr[3], sg_count, offset, 0);
@@ -1812,8 +1855,10 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc,
crypto_ahash_digestsize(tfm),
areq->result, DMA_FROM_DEVICE);
else
- to_talitos_ptr(&desc->ptr[5], ctx->dma_hw_context,
- req_ctx->hw_context_size, is_sec1);
+ map_single_talitos_ptr_nosync(dev, &desc->ptr[5],
+ req_ctx->hw_context_size,
+ req_ctx->hw_context,
+ DMA_FROM_DEVICE);
/* last DWORD empty */
@@ -1832,9 +1877,14 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc,
desc->hdr |= DESC_HDR_MODE0_MDEU_CONT;
desc->hdr &= ~DESC_HDR_DONE_NOTIFY;
- to_talitos_ptr(&desc2->ptr[1], ctx->dma_hw_context,
- req_ctx->hw_context_size, is_sec1);
-
+ if (desc->ptr[1].ptr)
+ copy_talitos_ptr(&desc2->ptr[1], &desc->ptr[1],
+ is_sec1);
+ else
+ map_single_talitos_ptr_nosync(dev, &desc2->ptr[1],
+ req_ctx->hw_context_size,
+ req_ctx->hw_context,
+ DMA_TO_DEVICE);
copy_talitos_ptr(&desc2->ptr[2], &desc->ptr[2], is_sec1);
sg_count = talitos_sg_map(dev, req_ctx->psrc, length, edesc,
&desc2->ptr[3], sg_count, offset, 0);
@@ -1842,8 +1892,10 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc,
sync_needed = true;
copy_talitos_ptr(&desc2->ptr[5], &desc->ptr[5], is_sec1);
if (req_ctx->last)
- to_talitos_ptr(&desc->ptr[5], ctx->dma_hw_context,
- req_ctx->hw_context_size, is_sec1);
+ map_single_talitos_ptr_nosync(dev, &desc->ptr[5],
+ req_ctx->hw_context_size,
+ req_ctx->hw_context,
+ DMA_FROM_DEVICE);
next_desc = dma_map_single(dev, &desc2->hdr1, TALITOS_DESC_SIZE,
DMA_BIDIRECTIONAL);
@@ -1885,8 +1937,7 @@ static int ahash_init(struct ahash_request *areq)
struct device *dev = ctx->dev;
struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq);
unsigned int size;
- struct talitos_private *priv = dev_get_drvdata(dev);
- bool is_sec1 = has_ftr_sec1(priv);
+ dma_addr_t dma;
/* Initialize the context */
req_ctx->buf_idx = 0;
@@ -1898,18 +1949,10 @@ static int ahash_init(struct ahash_request *areq)
: TALITOS_MDEU_CONTEXT_SIZE_SHA384_SHA512;
req_ctx->hw_context_size = size;
- if (ctx->dma_hw_context)
- dma_unmap_single(dev, ctx->dma_hw_context, size,
- DMA_BIDIRECTIONAL);
- ctx->dma_hw_context = dma_map_single(dev, req_ctx->hw_context, size,
- DMA_BIDIRECTIONAL);
- if (ctx->dma_buf)
- dma_unmap_single(dev, ctx->dma_buf, sizeof(req_ctx->buf),
- DMA_TO_DEVICE);
- if (is_sec1)
- ctx->dma_buf = dma_map_single(dev, req_ctx->buf,
- sizeof(req_ctx->buf),
- DMA_TO_DEVICE);
+ dma = dma_map_single(dev, req_ctx->hw_context, req_ctx->hw_context_size,
+ DMA_TO_DEVICE);
+ dma_unmap_single(dev, dma, req_ctx->hw_context_size, DMA_TO_DEVICE);
+
return 0;
}
@@ -1920,12 +1963,6 @@ static int ahash_init(struct ahash_request *areq)
static int ahash_init_sha224_swinit(struct ahash_request *areq)
{
struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq);
- struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq);
- struct talitos_ctx *ctx = crypto_ahash_ctx(tfm);
- struct device *dev = ctx->dev;
-
- ahash_init(areq);
- req_ctx->swinit = 1;/* prevent h/w initting context with sha256 values*/
req_ctx->hw_context[0] = SHA224_H0;
req_ctx->hw_context[1] = SHA224_H1;
@@ -1940,8 +1977,8 @@ static int ahash_init_sha224_swinit(struct ahash_request *areq)
req_ctx->hw_context[8] = 0;
req_ctx->hw_context[9] = 0;
- dma_sync_single_for_device(dev, ctx->dma_hw_context,
- req_ctx->hw_context_size, DMA_TO_DEVICE);
+ ahash_init(areq);
+ req_ctx->swinit = 1;/* prevent h/w initting context with sha256 values*/
return 0;
}
@@ -2046,13 +2083,6 @@ static int ahash_process_req(struct ahash_request *areq, unsigned int nbytes)
/* request SEC to INIT hash. */
if (req_ctx->first && !req_ctx->swinit)
edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_INIT;
- if (is_sec1) {
- dma_addr_t dma_buf = ctx->dma_buf + req_ctx->buf_idx *
- HASH_MAX_BLOCK_SIZE;
-
- dma_sync_single_for_device(dev, dma_buf,
- req_ctx->nbuf, DMA_TO_DEVICE);
- }
/* When the tfm context has a keylen, it's an HMAC.
* A first or last (ie. not middle) descriptor must request HMAC.
@@ -2106,12 +2136,15 @@ static int ahash_export(struct ahash_request *areq, void *out)
{
struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq);
struct talitos_export_state *export = out;
- struct crypto_ahash *ahash = crypto_ahash_reqtfm(areq);
- struct talitos_ctx *ctx = crypto_ahash_ctx(ahash);
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq);
+ struct talitos_ctx *ctx = crypto_ahash_ctx(tfm);
struct device *dev = ctx->dev;
+ dma_addr_t dma;
+
+ dma = dma_map_single(dev, req_ctx->hw_context, req_ctx->hw_context_size,
+ DMA_FROM_DEVICE);
+ dma_unmap_single(dev, dma, req_ctx->hw_context_size, DMA_FROM_DEVICE);
- dma_sync_single_for_cpu(dev, ctx->dma_hw_context,
- req_ctx->hw_context_size, DMA_FROM_DEVICE);
memcpy(export->hw_context, req_ctx->hw_context,
req_ctx->hw_context_size);
memcpy(export->buf, req_ctx->buf[req_ctx->buf_idx], req_ctx->nbuf);
@@ -2128,39 +2161,29 @@ static int ahash_import(struct ahash_request *areq, const void *in)
{
struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq);
struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq);
- const struct talitos_export_state *export = in;
- unsigned int size;
struct talitos_ctx *ctx = crypto_ahash_ctx(tfm);
struct device *dev = ctx->dev;
- struct talitos_private *priv = dev_get_drvdata(dev);
- bool is_sec1 = has_ftr_sec1(priv);
+ const struct talitos_export_state *export = in;
+ unsigned int size;
+ dma_addr_t dma;
memset(req_ctx, 0, sizeof(*req_ctx));
size = (crypto_ahash_digestsize(tfm) <= SHA256_DIGEST_SIZE)
? TALITOS_MDEU_CONTEXT_SIZE_MD5_SHA1_SHA256
: TALITOS_MDEU_CONTEXT_SIZE_SHA384_SHA512;
req_ctx->hw_context_size = size;
- if (ctx->dma_hw_context)
- dma_unmap_single(dev, ctx->dma_hw_context, size,
- DMA_BIDIRECTIONAL);
-
memcpy(req_ctx->hw_context, export->hw_context, size);
- ctx->dma_hw_context = dma_map_single(dev, req_ctx->hw_context, size,
- DMA_BIDIRECTIONAL);
- if (ctx->dma_buf)
- dma_unmap_single(dev, ctx->dma_buf, sizeof(req_ctx->buf),
- DMA_TO_DEVICE);
memcpy(req_ctx->buf[0], export->buf, export->nbuf);
- if (is_sec1)
- ctx->dma_buf = dma_map_single(dev, req_ctx->buf,
- sizeof(req_ctx->buf),
- DMA_TO_DEVICE);
req_ctx->swinit = export->swinit;
req_ctx->first = export->first;
req_ctx->last = export->last;
req_ctx->to_hash_later = export->to_hash_later;
req_ctx->nbuf = export->nbuf;
+ dma = dma_map_single(dev, req_ctx->hw_context, req_ctx->hw_context_size,
+ DMA_TO_DEVICE);
+ dma_unmap_single(dev, dma, req_ctx->hw_context_size, DMA_TO_DEVICE);
+
return 0;
}
@@ -3064,27 +3087,6 @@ static void talitos_cra_exit(struct crypto_tfm *tfm)
dma_unmap_single(dev, ctx->dma_key, ctx->keylen, DMA_TO_DEVICE);
}
-static void talitos_cra_exit_ahash(struct crypto_tfm *tfm)
-{
- struct talitos_ctx *ctx = crypto_tfm_ctx(tfm);
- struct device *dev = ctx->dev;
- unsigned int size;
-
- talitos_cra_exit(tfm);
-
- size = (crypto_ahash_digestsize(__crypto_ahash_cast(tfm)) <=
- SHA256_DIGEST_SIZE)
- ? TALITOS_MDEU_CONTEXT_SIZE_MD5_SHA1_SHA256
- : TALITOS_MDEU_CONTEXT_SIZE_SHA384_SHA512;
-
- if (ctx->dma_hw_context)
- dma_unmap_single(dev, ctx->dma_hw_context, size,
- DMA_BIDIRECTIONAL);
- if (ctx->dma_buf)
- dma_unmap_single(dev, ctx->dma_buf, HASH_MAX_BLOCK_SIZE * 2,
- DMA_TO_DEVICE);
-}
-
/*
* given the alg's descriptor header template, determine whether descriptor
* type and primary/secondary execution units required match the hw
@@ -3183,7 +3185,7 @@ static struct talitos_crypto_alg *talitos_alg_alloc(struct device *dev,
case CRYPTO_ALG_TYPE_AHASH:
alg = &t_alg->algt.alg.hash.halg.base;
alg->cra_init = talitos_cra_init_ahash;
- alg->cra_exit = talitos_cra_exit_ahash;
+ alg->cra_exit = talitos_cra_exit;
alg->cra_type = &crypto_ahash_type;
t_alg->algt.alg.hash.init = ahash_init;
t_alg->algt.alg.hash.update = ahash_update;
diff --git a/drivers/crypto/ux500/cryp/cryp_core.c b/drivers/crypto/ux500/cryp/cryp_core.c
index 765f53e548ab..cb31b59c9d53 100644
--- a/drivers/crypto/ux500/cryp/cryp_core.c
+++ b/drivers/crypto/ux500/cryp/cryp_core.c
@@ -1404,9 +1404,8 @@ static void cryp_algs_unregister_all(void)
static int ux500_cryp_probe(struct platform_device *pdev)
{
int ret;
- int cryp_error = 0;
- struct resource *res = NULL;
- struct resource *res_irq = NULL;
+ struct resource *res;
+ struct resource *res_irq;
struct cryp_device_data *device_data;
struct cryp_protection_config prot = {
.privilege_access = CRYP_STATE_ENABLE
@@ -1416,7 +1415,6 @@ static int ux500_cryp_probe(struct platform_device *pdev)
dev_dbg(dev, "[%s]", __func__);
device_data = devm_kzalloc(dev, sizeof(*device_data), GFP_ATOMIC);
if (!device_data) {
- dev_err(dev, "[%s]: kzalloc() failed!", __func__);
ret = -ENOMEM;
goto out;
}
@@ -1479,15 +1477,13 @@ static int ux500_cryp_probe(struct platform_device *pdev)
goto out_clk_unprepare;
}
- cryp_error = cryp_check(device_data);
- if (cryp_error != 0) {
- dev_err(dev, "[%s]: cryp_init() failed!", __func__);
+ if (cryp_check(device_data)) {
+ dev_err(dev, "[%s]: cryp_check() failed!", __func__);
ret = -EINVAL;
goto out_power;
}
- cryp_error = cryp_configure_protection(device_data, &prot);
- if (cryp_error != 0) {
+ if (cryp_configure_protection(device_data, &prot)) {
dev_err(dev, "[%s]: cryp_configure_protection() failed!",
__func__);
ret = -EINVAL;
diff --git a/drivers/crypto/ux500/hash/hash_core.c b/drivers/crypto/ux500/hash/hash_core.c
index 9acccad26928..2d0a677bcc76 100644
--- a/drivers/crypto/ux500/hash/hash_core.c
+++ b/drivers/crypto/ux500/hash/hash_core.c
@@ -1403,6 +1403,16 @@ out:
return ret1 ? ret1 : ret2;
}
+static int ahash_noimport(struct ahash_request *req, const void *in)
+{
+ return -ENOSYS;
+}
+
+static int ahash_noexport(struct ahash_request *req, void *out)
+{
+ return -ENOSYS;
+}
+
static int hmac_sha1_init(struct ahash_request *req)
{
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
@@ -1507,6 +1517,8 @@ static struct hash_algo_template hash_algs[] = {
.update = ahash_update,
.final = ahash_final,
.digest = ahash_sha1_digest,
+ .export = ahash_noexport,
+ .import = ahash_noimport,
.halg.digestsize = SHA1_DIGEST_SIZE,
.halg.statesize = sizeof(struct hash_ctx),
.halg.base = {
@@ -1529,6 +1541,8 @@ static struct hash_algo_template hash_algs[] = {
.update = ahash_update,
.final = ahash_final,
.digest = ahash_sha256_digest,
+ .export = ahash_noexport,
+ .import = ahash_noimport,
.halg.digestsize = SHA256_DIGEST_SIZE,
.halg.statesize = sizeof(struct hash_ctx),
.halg.base = {
@@ -1553,6 +1567,8 @@ static struct hash_algo_template hash_algs[] = {
.final = ahash_final,
.digest = hmac_sha1_digest,
.setkey = hmac_sha1_setkey,
+ .export = ahash_noexport,
+ .import = ahash_noimport,
.halg.digestsize = SHA1_DIGEST_SIZE,
.halg.statesize = sizeof(struct hash_ctx),
.halg.base = {
@@ -1577,6 +1593,8 @@ static struct hash_algo_template hash_algs[] = {
.final = ahash_final,
.digest = hmac_sha256_digest,
.setkey = hmac_sha256_setkey,
+ .export = ahash_noexport,
+ .import = ahash_noimport,
.halg.digestsize = SHA256_DIGEST_SIZE,
.halg.statesize = sizeof(struct hash_ctx),
.halg.base = {
diff --git a/drivers/crypto/virtio/Kconfig b/drivers/crypto/virtio/Kconfig
index 5db07495ddc5..a4324b1383a4 100644
--- a/drivers/crypto/virtio/Kconfig
+++ b/drivers/crypto/virtio/Kconfig
@@ -2,7 +2,6 @@ config CRYPTO_DEV_VIRTIO
tristate "VirtIO crypto driver"
depends on VIRTIO
select CRYPTO_AEAD
- select CRYPTO_AUTHENC
select CRYPTO_BLKCIPHER
select CRYPTO_ENGINE
default m
diff --git a/drivers/crypto/virtio/virtio_crypto_algs.c b/drivers/crypto/virtio/virtio_crypto_algs.c
index abe8c15450df..ba190cfa7aa1 100644
--- a/drivers/crypto/virtio/virtio_crypto_algs.c
+++ b/drivers/crypto/virtio/virtio_crypto_algs.c
@@ -29,6 +29,7 @@
struct virtio_crypto_ablkcipher_ctx {
+ struct crypto_engine_ctx enginectx;
struct virtio_crypto *vcrypto;
struct crypto_tfm *tfm;
@@ -491,7 +492,7 @@ static int virtio_crypto_ablkcipher_encrypt(struct ablkcipher_request *req)
vc_sym_req->ablkcipher_req = req;
vc_sym_req->encrypt = true;
- return crypto_transfer_cipher_request_to_engine(data_vq->engine, req);
+ return crypto_transfer_ablkcipher_request_to_engine(data_vq->engine, req);
}
static int virtio_crypto_ablkcipher_decrypt(struct ablkcipher_request *req)
@@ -511,7 +512,7 @@ static int virtio_crypto_ablkcipher_decrypt(struct ablkcipher_request *req)
vc_sym_req->ablkcipher_req = req;
vc_sym_req->encrypt = false;
- return crypto_transfer_cipher_request_to_engine(data_vq->engine, req);
+ return crypto_transfer_ablkcipher_request_to_engine(data_vq->engine, req);
}
static int virtio_crypto_ablkcipher_init(struct crypto_tfm *tfm)
@@ -521,6 +522,9 @@ static int virtio_crypto_ablkcipher_init(struct crypto_tfm *tfm)
tfm->crt_ablkcipher.reqsize = sizeof(struct virtio_crypto_sym_request);
ctx->tfm = tfm;
+ ctx->enginectx.op.do_one_request = virtio_crypto_ablkcipher_crypt_req;
+ ctx->enginectx.op.prepare_request = NULL;
+ ctx->enginectx.op.unprepare_request = NULL;
return 0;
}
@@ -538,9 +542,9 @@ static void virtio_crypto_ablkcipher_exit(struct crypto_tfm *tfm)
}
int virtio_crypto_ablkcipher_crypt_req(
- struct crypto_engine *engine,
- struct ablkcipher_request *req)
+ struct crypto_engine *engine, void *vreq)
{
+ struct ablkcipher_request *req = container_of(vreq, struct ablkcipher_request, base);
struct virtio_crypto_sym_request *vc_sym_req =
ablkcipher_request_ctx(req);
struct virtio_crypto_request *vc_req = &vc_sym_req->base;
@@ -561,8 +565,8 @@ static void virtio_crypto_ablkcipher_finalize_req(
struct ablkcipher_request *req,
int err)
{
- crypto_finalize_cipher_request(vc_sym_req->base.dataq->engine,
- req, err);
+ crypto_finalize_ablkcipher_request(vc_sym_req->base.dataq->engine,
+ req, err);
kzfree(vc_sym_req->iv);
virtcrypto_clear_request(&vc_sym_req->base);
}
diff --git a/drivers/crypto/virtio/virtio_crypto_common.h b/drivers/crypto/virtio/virtio_crypto_common.h
index e976539a05d9..66501a5a2b7b 100644
--- a/drivers/crypto/virtio/virtio_crypto_common.h
+++ b/drivers/crypto/virtio/virtio_crypto_common.h
@@ -24,7 +24,6 @@
#include <linux/spinlock.h>
#include <crypto/aead.h>
#include <crypto/aes.h>
-#include <crypto/authenc.h>
#include <crypto/engine.h>
@@ -107,8 +106,7 @@ struct virtio_crypto *virtcrypto_get_dev_node(int node);
int virtcrypto_dev_start(struct virtio_crypto *vcrypto);
void virtcrypto_dev_stop(struct virtio_crypto *vcrypto);
int virtio_crypto_ablkcipher_crypt_req(
- struct crypto_engine *engine,
- struct ablkcipher_request *req);
+ struct crypto_engine *engine, void *vreq);
void
virtcrypto_clear_request(struct virtio_crypto_request *vc_req);
diff --git a/drivers/crypto/virtio/virtio_crypto_core.c b/drivers/crypto/virtio/virtio_crypto_core.c
index ff1410a32c2b..83326986c113 100644
--- a/drivers/crypto/virtio/virtio_crypto_core.c
+++ b/drivers/crypto/virtio/virtio_crypto_core.c
@@ -111,9 +111,6 @@ static int virtcrypto_find_vqs(struct virtio_crypto *vi)
ret = -ENOMEM;
goto err_engine;
}
-
- vi->data_vq[i].engine->cipher_one_request =
- virtio_crypto_ablkcipher_crypt_req;
}
kfree(names);
diff --git a/drivers/dax/Kconfig b/drivers/dax/Kconfig
index b79aa8f7a497..e0700bf4893a 100644
--- a/drivers/dax/Kconfig
+++ b/drivers/dax/Kconfig
@@ -1,3 +1,7 @@
+config DAX_DRIVER
+ select DAX
+ bool
+
menuconfig DAX
tristate "DAX: direct access to differentiated memory"
select SRCU
@@ -16,7 +20,6 @@ config DEV_DAX
baseline memory pool. Mappings of a /dev/daxX.Y device impose
restrictions that make the mapping behavior deterministic.
-
config DEV_DAX_PMEM
tristate "PMEM DAX: direct access to persistent memory"
depends on LIBNVDIMM && NVDIMM_DAX && DEV_DAX
diff --git a/drivers/dax/device.c b/drivers/dax/device.c
index 2137dbc29877..aff2c1594220 100644
--- a/drivers/dax/device.c
+++ b/drivers/dax/device.c
@@ -19,6 +19,7 @@
#include <linux/dax.h>
#include <linux/fs.h>
#include <linux/mm.h>
+#include <linux/mman.h>
#include "dax-private.h"
#include "dax.h"
@@ -257,8 +258,8 @@ static int __dev_dax_pte_fault(struct dev_dax *dev_dax, struct vm_fault *vmf)
dax_region = dev_dax->region;
if (dax_region->align > PAGE_SIZE) {
- dev_dbg(dev, "%s: alignment (%#x) > fault size (%#x)\n",
- __func__, dax_region->align, fault_size);
+ dev_dbg(dev, "alignment (%#x) > fault size (%#x)\n",
+ dax_region->align, fault_size);
return VM_FAULT_SIGBUS;
}
@@ -267,8 +268,7 @@ static int __dev_dax_pte_fault(struct dev_dax *dev_dax, struct vm_fault *vmf)
phys = dax_pgoff_to_phys(dev_dax, vmf->pgoff, PAGE_SIZE);
if (phys == -1) {
- dev_dbg(dev, "%s: pgoff_to_phys(%#lx) failed\n", __func__,
- vmf->pgoff);
+ dev_dbg(dev, "pgoff_to_phys(%#lx) failed\n", vmf->pgoff);
return VM_FAULT_SIGBUS;
}
@@ -299,14 +299,14 @@ static int __dev_dax_pmd_fault(struct dev_dax *dev_dax, struct vm_fault *vmf)
dax_region = dev_dax->region;
if (dax_region->align > PMD_SIZE) {
- dev_dbg(dev, "%s: alignment (%#x) > fault size (%#x)\n",
- __func__, dax_region->align, fault_size);
+ dev_dbg(dev, "alignment (%#x) > fault size (%#x)\n",
+ dax_region->align, fault_size);
return VM_FAULT_SIGBUS;
}
/* dax pmd mappings require pfn_t_devmap() */
if ((dax_region->pfn_flags & (PFN_DEV|PFN_MAP)) != (PFN_DEV|PFN_MAP)) {
- dev_dbg(dev, "%s: region lacks devmap flags\n", __func__);
+ dev_dbg(dev, "region lacks devmap flags\n");
return VM_FAULT_SIGBUS;
}
@@ -323,8 +323,7 @@ static int __dev_dax_pmd_fault(struct dev_dax *dev_dax, struct vm_fault *vmf)
pgoff = linear_page_index(vmf->vma, pmd_addr);
phys = dax_pgoff_to_phys(dev_dax, pgoff, PMD_SIZE);
if (phys == -1) {
- dev_dbg(dev, "%s: pgoff_to_phys(%#lx) failed\n", __func__,
- pgoff);
+ dev_dbg(dev, "pgoff_to_phys(%#lx) failed\n", pgoff);
return VM_FAULT_SIGBUS;
}
@@ -351,14 +350,14 @@ static int __dev_dax_pud_fault(struct dev_dax *dev_dax, struct vm_fault *vmf)
dax_region = dev_dax->region;
if (dax_region->align > PUD_SIZE) {
- dev_dbg(dev, "%s: alignment (%#x) > fault size (%#x)\n",
- __func__, dax_region->align, fault_size);
+ dev_dbg(dev, "alignment (%#x) > fault size (%#x)\n",
+ dax_region->align, fault_size);
return VM_FAULT_SIGBUS;
}
/* dax pud mappings require pfn_t_devmap() */
if ((dax_region->pfn_flags & (PFN_DEV|PFN_MAP)) != (PFN_DEV|PFN_MAP)) {
- dev_dbg(dev, "%s: region lacks devmap flags\n", __func__);
+ dev_dbg(dev, "region lacks devmap flags\n");
return VM_FAULT_SIGBUS;
}
@@ -375,8 +374,7 @@ static int __dev_dax_pud_fault(struct dev_dax *dev_dax, struct vm_fault *vmf)
pgoff = linear_page_index(vmf->vma, pud_addr);
phys = dax_pgoff_to_phys(dev_dax, pgoff, PUD_SIZE);
if (phys == -1) {
- dev_dbg(dev, "%s: pgoff_to_phys(%#lx) failed\n", __func__,
- pgoff);
+ dev_dbg(dev, "pgoff_to_phys(%#lx) failed\n", pgoff);
return VM_FAULT_SIGBUS;
}
@@ -399,9 +397,8 @@ static int dev_dax_huge_fault(struct vm_fault *vmf,
struct file *filp = vmf->vma->vm_file;
struct dev_dax *dev_dax = filp->private_data;
- dev_dbg(&dev_dax->dev, "%s: %s: %s (%#lx - %#lx) size = %d\n", __func__,
- current->comm, (vmf->flags & FAULT_FLAG_WRITE)
- ? "write" : "read",
+ dev_dbg(&dev_dax->dev, "%s: %s (%#lx - %#lx) size = %d\n", current->comm,
+ (vmf->flags & FAULT_FLAG_WRITE) ? "write" : "read",
vmf->vma->vm_start, vmf->vma->vm_end, pe_size);
id = dax_read_lock();
@@ -439,10 +436,20 @@ static int dev_dax_split(struct vm_area_struct *vma, unsigned long addr)
return 0;
}
+static unsigned long dev_dax_pagesize(struct vm_area_struct *vma)
+{
+ struct file *filp = vma->vm_file;
+ struct dev_dax *dev_dax = filp->private_data;
+ struct dax_region *dax_region = dev_dax->region;
+
+ return dax_region->align;
+}
+
static const struct vm_operations_struct dax_vm_ops = {
.fault = dev_dax_fault,
.huge_fault = dev_dax_huge_fault,
.split = dev_dax_split,
+ .pagesize = dev_dax_pagesize,
};
static int dax_mmap(struct file *filp, struct vm_area_struct *vma)
@@ -450,7 +457,7 @@ static int dax_mmap(struct file *filp, struct vm_area_struct *vma)
struct dev_dax *dev_dax = filp->private_data;
int rc, id;
- dev_dbg(&dev_dax->dev, "%s\n", __func__);
+ dev_dbg(&dev_dax->dev, "trace\n");
/*
* We lock to check dax_dev liveness and will re-check at
@@ -508,7 +515,7 @@ static int dax_open(struct inode *inode, struct file *filp)
struct inode *__dax_inode = dax_inode(dax_dev);
struct dev_dax *dev_dax = dax_get_private(dax_dev);
- dev_dbg(&dev_dax->dev, "%s\n", __func__);
+ dev_dbg(&dev_dax->dev, "trace\n");
inode->i_mapping = __dax_inode->i_mapping;
inode->i_mapping->host = __dax_inode;
filp->f_mapping = inode->i_mapping;
@@ -523,7 +530,7 @@ static int dax_release(struct inode *inode, struct file *filp)
{
struct dev_dax *dev_dax = filp->private_data;
- dev_dbg(&dev_dax->dev, "%s\n", __func__);
+ dev_dbg(&dev_dax->dev, "trace\n");
return 0;
}
@@ -534,6 +541,7 @@ static const struct file_operations dax_fops = {
.release = dax_release,
.get_unmapped_area = dax_get_unmapped_area,
.mmap = dax_mmap,
+ .mmap_supported_flags = MAP_SYNC,
};
static void dev_dax_release(struct device *dev)
@@ -565,7 +573,7 @@ static void unregister_dev_dax(void *dev)
struct inode *inode = dax_inode(dax_dev);
struct cdev *cdev = inode->i_cdev;
- dev_dbg(dev, "%s\n", __func__);
+ dev_dbg(dev, "trace\n");
kill_dev_dax(dev_dax);
cdev_device_del(cdev, dev);
diff --git a/drivers/dax/pmem.c b/drivers/dax/pmem.c
index 31b6ecce4c64..fd49b24fd6af 100644
--- a/drivers/dax/pmem.c
+++ b/drivers/dax/pmem.c
@@ -34,7 +34,7 @@ static void dax_pmem_percpu_release(struct percpu_ref *ref)
{
struct dax_pmem *dax_pmem = to_dax_pmem(ref);
- dev_dbg(dax_pmem->dev, "%s\n", __func__);
+ dev_dbg(dax_pmem->dev, "trace\n");
complete(&dax_pmem->cmp);
}
@@ -43,7 +43,7 @@ static void dax_pmem_percpu_exit(void *data)
struct percpu_ref *ref = data;
struct dax_pmem *dax_pmem = to_dax_pmem(ref);
- dev_dbg(dax_pmem->dev, "%s\n", __func__);
+ dev_dbg(dax_pmem->dev, "trace\n");
wait_for_completion(&dax_pmem->cmp);
percpu_ref_exit(ref);
}
@@ -53,7 +53,7 @@ static void dax_pmem_percpu_kill(void *data)
struct percpu_ref *ref = data;
struct dax_pmem *dax_pmem = to_dax_pmem(ref);
- dev_dbg(dax_pmem->dev, "%s\n", __func__);
+ dev_dbg(dax_pmem->dev, "trace\n");
percpu_ref_kill(ref);
}
@@ -150,17 +150,7 @@ static struct nd_device_driver dax_pmem_driver = {
.type = ND_DRIVER_DAX_PMEM,
};
-static int __init dax_pmem_init(void)
-{
- return nd_driver_register(&dax_pmem_driver);
-}
-module_init(dax_pmem_init);
-
-static void __exit dax_pmem_exit(void)
-{
- driver_unregister(&dax_pmem_driver.drv);
-}
-module_exit(dax_pmem_exit);
+module_nd_driver(dax_pmem_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Intel Corporation");
diff --git a/drivers/dax/super.c b/drivers/dax/super.c
index ecdc292aa4e4..2b2332b605e4 100644
--- a/drivers/dax/super.c
+++ b/drivers/dax/super.c
@@ -124,10 +124,19 @@ int __bdev_dax_supported(struct super_block *sb, int blocksize)
return len < 0 ? len : -EIO;
}
- if ((IS_ENABLED(CONFIG_FS_DAX_LIMITED) && pfn_t_special(pfn))
- || pfn_t_devmap(pfn))
+ if (IS_ENABLED(CONFIG_FS_DAX_LIMITED) && pfn_t_special(pfn)) {
+ /*
+ * An arch that has enabled the pmem api should also
+ * have its drivers support pfn_t_devmap()
+ *
+ * This is a developer warning and should not trigger in
+ * production. dax_flush() will crash since it depends
+ * on being able to do (page_address(pfn_to_page())).
+ */
+ WARN_ON(IS_ENABLED(CONFIG_ARCH_HAS_PMEM_API));
+ } else if (pfn_t_devmap(pfn)) {
/* pass */;
- else {
+ } else {
pr_debug("VFS (%s): error: dax support not enabled\n",
sb->s_id);
return -EOPNOTSUPP;
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 27df3e2837fd..6d61cd023633 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -187,6 +187,16 @@ config DMA_SUN6I
help
Support for the DMA engine first found in Allwinner A31 SoCs.
+config DW_AXI_DMAC
+ tristate "Synopsys DesignWare AXI DMA support"
+ depends on OF || COMPILE_TEST
+ select DMA_ENGINE
+ select DMA_VIRTUAL_CHANNELS
+ help
+ Enable support for Synopsys DesignWare AXI DMA controller.
+ NOTE: This driver wasn't tested on 64 bit platform because
+ of lack 64 bit platform with Synopsys DW AXI DMAC.
+
config EP93XX_DMA
bool "Cirrus Logic EP93xx DMA support"
depends on ARCH_EP93XX || COMPILE_TEST
@@ -633,6 +643,8 @@ config ZX_DMA
# driver files
source "drivers/dma/bestcomm/Kconfig"
+source "drivers/dma/mediatek/Kconfig"
+
source "drivers/dma/qcom/Kconfig"
source "drivers/dma/dw/Kconfig"
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index b9dca8a0e142..0f62a4d49aab 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_DMA_OMAP) += omap-dma.o
obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o
obj-$(CONFIG_DMA_SUN4I) += sun4i-dma.o
obj-$(CONFIG_DMA_SUN6I) += sun6i-dma.o
+obj-$(CONFIG_DW_AXI_DMAC) += dw-axi-dmac/
obj-$(CONFIG_DW_DMAC_CORE) += dw/
obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o
obj-$(CONFIG_FSL_DMA) += fsldma.o
@@ -75,5 +76,6 @@ obj-$(CONFIG_XGENE_DMA) += xgene-dma.o
obj-$(CONFIG_ZX_DMA) += zx_dma.o
obj-$(CONFIG_ST_FDMA) += st_fdma.o
+obj-y += mediatek/
obj-y += qcom/
obj-y += xilinx/
diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c
index c00e3923d7d8..94236ec9d410 100644
--- a/drivers/dma/at_xdmac.c
+++ b/drivers/dma/at_xdmac.c
@@ -1471,10 +1471,10 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
for (retry = 0; retry < AT_XDMAC_RESIDUE_MAX_RETRIES; retry++) {
check_nda = at_xdmac_chan_read(atchan, AT_XDMAC_CNDA) & 0xfffffffc;
rmb();
- initd = !!(at_xdmac_chan_read(atchan, AT_XDMAC_CC) & AT_XDMAC_CC_INITD);
- rmb();
cur_ubc = at_xdmac_chan_read(atchan, AT_XDMAC_CUBC);
rmb();
+ initd = !!(at_xdmac_chan_read(atchan, AT_XDMAC_CC) & AT_XDMAC_CC_INITD);
+ rmb();
cur_nda = at_xdmac_chan_read(atchan, AT_XDMAC_CNDA) & 0xfffffffc;
rmb();
diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c
index 80cc2be6483c..b9339524d5bd 100644
--- a/drivers/dma/dmatest.c
+++ b/drivers/dma/dmatest.c
@@ -74,7 +74,11 @@ MODULE_PARM_DESC(timeout, "Transfer Timeout in msec (default: 3000), "
static bool noverify;
module_param(noverify, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(noverify, "Disable random data setup and verification");
+MODULE_PARM_DESC(noverify, "Disable data verification (default: verify)");
+
+static bool norandom;
+module_param(norandom, bool, 0644);
+MODULE_PARM_DESC(norandom, "Disable random offset setup (default: random)");
static bool verbose;
module_param(verbose, bool, S_IRUGO | S_IWUSR);
@@ -103,6 +107,7 @@ struct dmatest_params {
unsigned int pq_sources;
int timeout;
bool noverify;
+ bool norandom;
};
/**
@@ -575,7 +580,7 @@ static int dmatest_func(void *data)
break;
}
- if (params->noverify)
+ if (params->norandom)
len = params->buf_size;
else
len = dmatest_random() % params->buf_size + 1;
@@ -586,17 +591,19 @@ static int dmatest_func(void *data)
total_len += len;
- if (params->noverify) {
+ if (params->norandom) {
src_off = 0;
dst_off = 0;
} else {
- start = ktime_get();
src_off = dmatest_random() % (params->buf_size - len + 1);
dst_off = dmatest_random() % (params->buf_size - len + 1);
src_off = (src_off >> align) << align;
dst_off = (dst_off >> align) << align;
+ }
+ if (!params->noverify) {
+ start = ktime_get();
dmatest_init_srcs(thread->srcs, src_off, len,
params->buf_size, is_memset);
dmatest_init_dsts(thread->dsts, dst_off, len,
@@ -975,6 +982,7 @@ static void run_threaded_test(struct dmatest_info *info)
params->pq_sources = pq_sources;
params->timeout = timeout;
params->noverify = noverify;
+ params->norandom = norandom;
request_channels(info, DMA_MEMCPY);
request_channels(info, DMA_MEMSET);
diff --git a/drivers/dma/dw-axi-dmac/Makefile b/drivers/dma/dw-axi-dmac/Makefile
new file mode 100644
index 000000000000..4bfa462005be
--- /dev/null
+++ b/drivers/dma/dw-axi-dmac/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_DW_AXI_DMAC) += dw-axi-dmac-platform.o
diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
new file mode 100644
index 000000000000..c4eb55e3011c
--- /dev/null
+++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
@@ -0,0 +1,1008 @@
+// SPDX-License-Identifier: GPL-2.0
+// (C) 2017-2018 Synopsys, Inc. (www.synopsys.com)
+
+/*
+ * Synopsys DesignWare AXI DMA Controller driver.
+ *
+ * Author: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dmaengine.h>
+#include <linux/dmapool.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/types.h>
+
+#include "dw-axi-dmac.h"
+#include "../dmaengine.h"
+#include "../virt-dma.h"
+
+/*
+ * The set of bus widths supported by the DMA controller. DW AXI DMAC supports
+ * master data bus width up to 512 bits (for both AXI master interfaces), but
+ * it depends on IP block configurarion.
+ */
+#define AXI_DMA_BUSWIDTHS \
+ (DMA_SLAVE_BUSWIDTH_1_BYTE | \
+ DMA_SLAVE_BUSWIDTH_2_BYTES | \
+ DMA_SLAVE_BUSWIDTH_4_BYTES | \
+ DMA_SLAVE_BUSWIDTH_8_BYTES | \
+ DMA_SLAVE_BUSWIDTH_16_BYTES | \
+ DMA_SLAVE_BUSWIDTH_32_BYTES | \
+ DMA_SLAVE_BUSWIDTH_64_BYTES)
+
+static inline void
+axi_dma_iowrite32(struct axi_dma_chip *chip, u32 reg, u32 val)
+{
+ iowrite32(val, chip->regs + reg);
+}
+
+static inline u32 axi_dma_ioread32(struct axi_dma_chip *chip, u32 reg)
+{
+ return ioread32(chip->regs + reg);
+}
+
+static inline void
+axi_chan_iowrite32(struct axi_dma_chan *chan, u32 reg, u32 val)
+{
+ iowrite32(val, chan->chan_regs + reg);
+}
+
+static inline u32 axi_chan_ioread32(struct axi_dma_chan *chan, u32 reg)
+{
+ return ioread32(chan->chan_regs + reg);
+}
+
+static inline void
+axi_chan_iowrite64(struct axi_dma_chan *chan, u32 reg, u64 val)
+{
+ /*
+ * We split one 64 bit write for two 32 bit write as some HW doesn't
+ * support 64 bit access.
+ */
+ iowrite32(lower_32_bits(val), chan->chan_regs + reg);
+ iowrite32(upper_32_bits(val), chan->chan_regs + reg + 4);
+}
+
+static inline void axi_dma_disable(struct axi_dma_chip *chip)
+{
+ u32 val;
+
+ val = axi_dma_ioread32(chip, DMAC_CFG);
+ val &= ~DMAC_EN_MASK;
+ axi_dma_iowrite32(chip, DMAC_CFG, val);
+}
+
+static inline void axi_dma_enable(struct axi_dma_chip *chip)
+{
+ u32 val;
+
+ val = axi_dma_ioread32(chip, DMAC_CFG);
+ val |= DMAC_EN_MASK;
+ axi_dma_iowrite32(chip, DMAC_CFG, val);
+}
+
+static inline void axi_dma_irq_disable(struct axi_dma_chip *chip)
+{
+ u32 val;
+
+ val = axi_dma_ioread32(chip, DMAC_CFG);
+ val &= ~INT_EN_MASK;
+ axi_dma_iowrite32(chip, DMAC_CFG, val);
+}
+
+static inline void axi_dma_irq_enable(struct axi_dma_chip *chip)
+{
+ u32 val;
+
+ val = axi_dma_ioread32(chip, DMAC_CFG);
+ val |= INT_EN_MASK;
+ axi_dma_iowrite32(chip, DMAC_CFG, val);
+}
+
+static inline void axi_chan_irq_disable(struct axi_dma_chan *chan, u32 irq_mask)
+{
+ u32 val;
+
+ if (likely(irq_mask == DWAXIDMAC_IRQ_ALL)) {
+ axi_chan_iowrite32(chan, CH_INTSTATUS_ENA, DWAXIDMAC_IRQ_NONE);
+ } else {
+ val = axi_chan_ioread32(chan, CH_INTSTATUS_ENA);
+ val &= ~irq_mask;
+ axi_chan_iowrite32(chan, CH_INTSTATUS_ENA, val);
+ }
+}
+
+static inline void axi_chan_irq_set(struct axi_dma_chan *chan, u32 irq_mask)
+{
+ axi_chan_iowrite32(chan, CH_INTSTATUS_ENA, irq_mask);
+}
+
+static inline void axi_chan_irq_sig_set(struct axi_dma_chan *chan, u32 irq_mask)
+{
+ axi_chan_iowrite32(chan, CH_INTSIGNAL_ENA, irq_mask);
+}
+
+static inline void axi_chan_irq_clear(struct axi_dma_chan *chan, u32 irq_mask)
+{
+ axi_chan_iowrite32(chan, CH_INTCLEAR, irq_mask);
+}
+
+static inline u32 axi_chan_irq_read(struct axi_dma_chan *chan)
+{
+ return axi_chan_ioread32(chan, CH_INTSTATUS);
+}
+
+static inline void axi_chan_disable(struct axi_dma_chan *chan)
+{
+ u32 val;
+
+ val = axi_dma_ioread32(chan->chip, DMAC_CHEN);
+ val &= ~(BIT(chan->id) << DMAC_CHAN_EN_SHIFT);
+ val |= BIT(chan->id) << DMAC_CHAN_EN_WE_SHIFT;
+ axi_dma_iowrite32(chan->chip, DMAC_CHEN, val);
+}
+
+static inline void axi_chan_enable(struct axi_dma_chan *chan)
+{
+ u32 val;
+
+ val = axi_dma_ioread32(chan->chip, DMAC_CHEN);
+ val |= BIT(chan->id) << DMAC_CHAN_EN_SHIFT |
+ BIT(chan->id) << DMAC_CHAN_EN_WE_SHIFT;
+ axi_dma_iowrite32(chan->chip, DMAC_CHEN, val);
+}
+
+static inline bool axi_chan_is_hw_enable(struct axi_dma_chan *chan)
+{
+ u32 val;
+
+ val = axi_dma_ioread32(chan->chip, DMAC_CHEN);
+
+ return !!(val & (BIT(chan->id) << DMAC_CHAN_EN_SHIFT));
+}
+
+static void axi_dma_hw_init(struct axi_dma_chip *chip)
+{
+ u32 i;
+
+ for (i = 0; i < chip->dw->hdata->nr_channels; i++) {
+ axi_chan_irq_disable(&chip->dw->chan[i], DWAXIDMAC_IRQ_ALL);
+ axi_chan_disable(&chip->dw->chan[i]);
+ }
+}
+
+static u32 axi_chan_get_xfer_width(struct axi_dma_chan *chan, dma_addr_t src,
+ dma_addr_t dst, size_t len)
+{
+ u32 max_width = chan->chip->dw->hdata->m_data_width;
+
+ return __ffs(src | dst | len | BIT(max_width));
+}
+
+static inline const char *axi_chan_name(struct axi_dma_chan *chan)
+{
+ return dma_chan_name(&chan->vc.chan);
+}
+
+static struct axi_dma_desc *axi_desc_get(struct axi_dma_chan *chan)
+{
+ struct dw_axi_dma *dw = chan->chip->dw;
+ struct axi_dma_desc *desc;
+ dma_addr_t phys;
+
+ desc = dma_pool_zalloc(dw->desc_pool, GFP_NOWAIT, &phys);
+ if (unlikely(!desc)) {
+ dev_err(chan2dev(chan), "%s: not enough descriptors available\n",
+ axi_chan_name(chan));
+ return NULL;
+ }
+
+ atomic_inc(&chan->descs_allocated);
+ INIT_LIST_HEAD(&desc->xfer_list);
+ desc->vd.tx.phys = phys;
+ desc->chan = chan;
+
+ return desc;
+}
+
+static void axi_desc_put(struct axi_dma_desc *desc)
+{
+ struct axi_dma_chan *chan = desc->chan;
+ struct dw_axi_dma *dw = chan->chip->dw;
+ struct axi_dma_desc *child, *_next;
+ unsigned int descs_put = 0;
+
+ list_for_each_entry_safe(child, _next, &desc->xfer_list, xfer_list) {
+ list_del(&child->xfer_list);
+ dma_pool_free(dw->desc_pool, child, child->vd.tx.phys);
+ descs_put++;
+ }
+
+ dma_pool_free(dw->desc_pool, desc, desc->vd.tx.phys);
+ descs_put++;
+
+ atomic_sub(descs_put, &chan->descs_allocated);
+ dev_vdbg(chan2dev(chan), "%s: %d descs put, %d still allocated\n",
+ axi_chan_name(chan), descs_put,
+ atomic_read(&chan->descs_allocated));
+}
+
+static void vchan_desc_put(struct virt_dma_desc *vdesc)
+{
+ axi_desc_put(vd_to_axi_desc(vdesc));
+}
+
+static enum dma_status
+dma_chan_tx_status(struct dma_chan *dchan, dma_cookie_t cookie,
+ struct dma_tx_state *txstate)
+{
+ struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan);
+ enum dma_status ret;
+
+ ret = dma_cookie_status(dchan, cookie, txstate);
+
+ if (chan->is_paused && ret == DMA_IN_PROGRESS)
+ ret = DMA_PAUSED;
+
+ return ret;
+}
+
+static void write_desc_llp(struct axi_dma_desc *desc, dma_addr_t adr)
+{
+ desc->lli.llp = cpu_to_le64(adr);
+}
+
+static void write_chan_llp(struct axi_dma_chan *chan, dma_addr_t adr)
+{
+ axi_chan_iowrite64(chan, CH_LLP, adr);
+}
+
+/* Called in chan locked context */
+static void axi_chan_block_xfer_start(struct axi_dma_chan *chan,
+ struct axi_dma_desc *first)
+{
+ u32 priority = chan->chip->dw->hdata->priority[chan->id];
+ u32 reg, irq_mask;
+ u8 lms = 0; /* Select AXI0 master for LLI fetching */
+
+ if (unlikely(axi_chan_is_hw_enable(chan))) {
+ dev_err(chan2dev(chan), "%s is non-idle!\n",
+ axi_chan_name(chan));
+
+ return;
+ }
+
+ axi_dma_enable(chan->chip);
+
+ reg = (DWAXIDMAC_MBLK_TYPE_LL << CH_CFG_L_DST_MULTBLK_TYPE_POS |
+ DWAXIDMAC_MBLK_TYPE_LL << CH_CFG_L_SRC_MULTBLK_TYPE_POS);
+ axi_chan_iowrite32(chan, CH_CFG_L, reg);
+
+ reg = (DWAXIDMAC_TT_FC_MEM_TO_MEM_DMAC << CH_CFG_H_TT_FC_POS |
+ priority << CH_CFG_H_PRIORITY_POS |
+ DWAXIDMAC_HS_SEL_HW << CH_CFG_H_HS_SEL_DST_POS |
+ DWAXIDMAC_HS_SEL_HW << CH_CFG_H_HS_SEL_SRC_POS);
+ axi_chan_iowrite32(chan, CH_CFG_H, reg);
+
+ write_chan_llp(chan, first->vd.tx.phys | lms);
+
+ irq_mask = DWAXIDMAC_IRQ_DMA_TRF | DWAXIDMAC_IRQ_ALL_ERR;
+ axi_chan_irq_sig_set(chan, irq_mask);
+
+ /* Generate 'suspend' status but don't generate interrupt */
+ irq_mask |= DWAXIDMAC_IRQ_SUSPENDED;
+ axi_chan_irq_set(chan, irq_mask);
+
+ axi_chan_enable(chan);
+}
+
+static void axi_chan_start_first_queued(struct axi_dma_chan *chan)
+{
+ struct axi_dma_desc *desc;
+ struct virt_dma_desc *vd;
+
+ vd = vchan_next_desc(&chan->vc);
+ if (!vd)
+ return;
+
+ desc = vd_to_axi_desc(vd);
+ dev_vdbg(chan2dev(chan), "%s: started %u\n", axi_chan_name(chan),
+ vd->tx.cookie);
+ axi_chan_block_xfer_start(chan, desc);
+}
+
+static void dma_chan_issue_pending(struct dma_chan *dchan)
+{
+ struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan);
+ unsigned long flags;
+
+ spin_lock_irqsave(&chan->vc.lock, flags);
+ if (vchan_issue_pending(&chan->vc))
+ axi_chan_start_first_queued(chan);
+ spin_unlock_irqrestore(&chan->vc.lock, flags);
+}
+
+static int dma_chan_alloc_chan_resources(struct dma_chan *dchan)
+{
+ struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan);
+
+ /* ASSERT: channel is idle */
+ if (axi_chan_is_hw_enable(chan)) {
+ dev_err(chan2dev(chan), "%s is non-idle!\n",
+ axi_chan_name(chan));
+ return -EBUSY;
+ }
+
+ dev_vdbg(dchan2dev(dchan), "%s: allocating\n", axi_chan_name(chan));
+
+ pm_runtime_get(chan->chip->dev);
+
+ return 0;
+}
+
+static void dma_chan_free_chan_resources(struct dma_chan *dchan)
+{
+ struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan);
+
+ /* ASSERT: channel is idle */
+ if (axi_chan_is_hw_enable(chan))
+ dev_err(dchan2dev(dchan), "%s is non-idle!\n",
+ axi_chan_name(chan));
+
+ axi_chan_disable(chan);
+ axi_chan_irq_disable(chan, DWAXIDMAC_IRQ_ALL);
+
+ vchan_free_chan_resources(&chan->vc);
+
+ dev_vdbg(dchan2dev(dchan),
+ "%s: free resources, descriptor still allocated: %u\n",
+ axi_chan_name(chan), atomic_read(&chan->descs_allocated));
+
+ pm_runtime_put(chan->chip->dev);
+}
+
+/*
+ * If DW_axi_dmac sees CHx_CTL.ShadowReg_Or_LLI_Last bit of the fetched LLI
+ * as 1, it understands that the current block is the final block in the
+ * transfer and completes the DMA transfer operation at the end of current
+ * block transfer.
+ */
+static void set_desc_last(struct axi_dma_desc *desc)
+{
+ u32 val;
+
+ val = le32_to_cpu(desc->lli.ctl_hi);
+ val |= CH_CTL_H_LLI_LAST;
+ desc->lli.ctl_hi = cpu_to_le32(val);
+}
+
+static void write_desc_sar(struct axi_dma_desc *desc, dma_addr_t adr)
+{
+ desc->lli.sar = cpu_to_le64(adr);
+}
+
+static void write_desc_dar(struct axi_dma_desc *desc, dma_addr_t adr)
+{
+ desc->lli.dar = cpu_to_le64(adr);
+}
+
+static void set_desc_src_master(struct axi_dma_desc *desc)
+{
+ u32 val;
+
+ /* Select AXI0 for source master */
+ val = le32_to_cpu(desc->lli.ctl_lo);
+ val &= ~CH_CTL_L_SRC_MAST;
+ desc->lli.ctl_lo = cpu_to_le32(val);
+}
+
+static void set_desc_dest_master(struct axi_dma_desc *desc)
+{
+ u32 val;
+
+ /* Select AXI1 for source master if available */
+ val = le32_to_cpu(desc->lli.ctl_lo);
+ if (desc->chan->chip->dw->hdata->nr_masters > 1)
+ val |= CH_CTL_L_DST_MAST;
+ else
+ val &= ~CH_CTL_L_DST_MAST;
+
+ desc->lli.ctl_lo = cpu_to_le32(val);
+}
+
+static struct dma_async_tx_descriptor *
+dma_chan_prep_dma_memcpy(struct dma_chan *dchan, dma_addr_t dst_adr,
+ dma_addr_t src_adr, size_t len, unsigned long flags)
+{
+ struct axi_dma_desc *first = NULL, *desc = NULL, *prev = NULL;
+ struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan);
+ size_t block_ts, max_block_ts, xfer_len;
+ u32 xfer_width, reg;
+ u8 lms = 0; /* Select AXI0 master for LLI fetching */
+
+ dev_dbg(chan2dev(chan), "%s: memcpy: src: %pad dst: %pad length: %zd flags: %#lx",
+ axi_chan_name(chan), &src_adr, &dst_adr, len, flags);
+
+ max_block_ts = chan->chip->dw->hdata->block_size[chan->id];
+
+ while (len) {
+ xfer_len = len;
+
+ /*
+ * Take care for the alignment.
+ * Actually source and destination widths can be different, but
+ * make them same to be simpler.
+ */
+ xfer_width = axi_chan_get_xfer_width(chan, src_adr, dst_adr, xfer_len);
+
+ /*
+ * block_ts indicates the total number of data of width
+ * to be transferred in a DMA block transfer.
+ * BLOCK_TS register should be set to block_ts - 1
+ */
+ block_ts = xfer_len >> xfer_width;
+ if (block_ts > max_block_ts) {
+ block_ts = max_block_ts;
+ xfer_len = max_block_ts << xfer_width;
+ }
+
+ desc = axi_desc_get(chan);
+ if (unlikely(!desc))
+ goto err_desc_get;
+
+ write_desc_sar(desc, src_adr);
+ write_desc_dar(desc, dst_adr);
+ desc->lli.block_ts_lo = cpu_to_le32(block_ts - 1);
+
+ reg = CH_CTL_H_LLI_VALID;
+ if (chan->chip->dw->hdata->restrict_axi_burst_len) {
+ u32 burst_len = chan->chip->dw->hdata->axi_rw_burst_len;
+
+ reg |= (CH_CTL_H_ARLEN_EN |
+ burst_len << CH_CTL_H_ARLEN_POS |
+ CH_CTL_H_AWLEN_EN |
+ burst_len << CH_CTL_H_AWLEN_POS);
+ }
+ desc->lli.ctl_hi = cpu_to_le32(reg);
+
+ reg = (DWAXIDMAC_BURST_TRANS_LEN_4 << CH_CTL_L_DST_MSIZE_POS |
+ DWAXIDMAC_BURST_TRANS_LEN_4 << CH_CTL_L_SRC_MSIZE_POS |
+ xfer_width << CH_CTL_L_DST_WIDTH_POS |
+ xfer_width << CH_CTL_L_SRC_WIDTH_POS |
+ DWAXIDMAC_CH_CTL_L_INC << CH_CTL_L_DST_INC_POS |
+ DWAXIDMAC_CH_CTL_L_INC << CH_CTL_L_SRC_INC_POS);
+ desc->lli.ctl_lo = cpu_to_le32(reg);
+
+ set_desc_src_master(desc);
+ set_desc_dest_master(desc);
+
+ /* Manage transfer list (xfer_list) */
+ if (!first) {
+ first = desc;
+ } else {
+ list_add_tail(&desc->xfer_list, &first->xfer_list);
+ write_desc_llp(prev, desc->vd.tx.phys | lms);
+ }
+ prev = desc;
+
+ /* update the length and addresses for the next loop cycle */
+ len -= xfer_len;
+ dst_adr += xfer_len;
+ src_adr += xfer_len;
+ }
+
+ /* Total len of src/dest sg == 0, so no descriptor were allocated */
+ if (unlikely(!first))
+ return NULL;
+
+ /* Set end-of-link to the last link descriptor of list */
+ set_desc_last(desc);
+
+ return vchan_tx_prep(&chan->vc, &first->vd, flags);
+
+err_desc_get:
+ axi_desc_put(first);
+ return NULL;
+}
+
+static void axi_chan_dump_lli(struct axi_dma_chan *chan,
+ struct axi_dma_desc *desc)
+{
+ dev_err(dchan2dev(&chan->vc.chan),
+ "SAR: 0x%llx DAR: 0x%llx LLP: 0x%llx BTS 0x%x CTL: 0x%x:%08x",
+ le64_to_cpu(desc->lli.sar),
+ le64_to_cpu(desc->lli.dar),
+ le64_to_cpu(desc->lli.llp),
+ le32_to_cpu(desc->lli.block_ts_lo),
+ le32_to_cpu(desc->lli.ctl_hi),
+ le32_to_cpu(desc->lli.ctl_lo));
+}
+
+static void axi_chan_list_dump_lli(struct axi_dma_chan *chan,
+ struct axi_dma_desc *desc_head)
+{
+ struct axi_dma_desc *desc;
+
+ axi_chan_dump_lli(chan, desc_head);
+ list_for_each_entry(desc, &desc_head->xfer_list, xfer_list)
+ axi_chan_dump_lli(chan, desc);
+}
+
+static noinline void axi_chan_handle_err(struct axi_dma_chan *chan, u32 status)
+{
+ struct virt_dma_desc *vd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chan->vc.lock, flags);
+
+ axi_chan_disable(chan);
+
+ /* The bad descriptor currently is in the head of vc list */
+ vd = vchan_next_desc(&chan->vc);
+ /* Remove the completed descriptor from issued list */
+ list_del(&vd->node);
+
+ /* WARN about bad descriptor */
+ dev_err(chan2dev(chan),
+ "Bad descriptor submitted for %s, cookie: %d, irq: 0x%08x\n",
+ axi_chan_name(chan), vd->tx.cookie, status);
+ axi_chan_list_dump_lli(chan, vd_to_axi_desc(vd));
+
+ vchan_cookie_complete(vd);
+
+ /* Try to restart the controller */
+ axi_chan_start_first_queued(chan);
+
+ spin_unlock_irqrestore(&chan->vc.lock, flags);
+}
+
+static void axi_chan_block_xfer_complete(struct axi_dma_chan *chan)
+{
+ struct virt_dma_desc *vd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chan->vc.lock, flags);
+ if (unlikely(axi_chan_is_hw_enable(chan))) {
+ dev_err(chan2dev(chan), "BUG: %s caught DWAXIDMAC_IRQ_DMA_TRF, but channel not idle!\n",
+ axi_chan_name(chan));
+ axi_chan_disable(chan);
+ }
+
+ /* The completed descriptor currently is in the head of vc list */
+ vd = vchan_next_desc(&chan->vc);
+ /* Remove the completed descriptor from issued list before completing */
+ list_del(&vd->node);
+ vchan_cookie_complete(vd);
+
+ /* Submit queued descriptors after processing the completed ones */
+ axi_chan_start_first_queued(chan);
+
+ spin_unlock_irqrestore(&chan->vc.lock, flags);
+}
+
+static irqreturn_t dw_axi_dma_interrupt(int irq, void *dev_id)
+{
+ struct axi_dma_chip *chip = dev_id;
+ struct dw_axi_dma *dw = chip->dw;
+ struct axi_dma_chan *chan;
+
+ u32 status, i;
+
+ /* Disable DMAC inerrupts. We'll enable them after processing chanels */
+ axi_dma_irq_disable(chip);
+
+ /* Poll, clear and process every chanel interrupt status */
+ for (i = 0; i < dw->hdata->nr_channels; i++) {
+ chan = &dw->chan[i];
+ status = axi_chan_irq_read(chan);
+ axi_chan_irq_clear(chan, status);
+
+ dev_vdbg(chip->dev, "%s %u IRQ status: 0x%08x\n",
+ axi_chan_name(chan), i, status);
+
+ if (status & DWAXIDMAC_IRQ_ALL_ERR)
+ axi_chan_handle_err(chan, status);
+ else if (status & DWAXIDMAC_IRQ_DMA_TRF)
+ axi_chan_block_xfer_complete(chan);
+ }
+
+ /* Re-enable interrupts */
+ axi_dma_irq_enable(chip);
+
+ return IRQ_HANDLED;
+}
+
+static int dma_chan_terminate_all(struct dma_chan *dchan)
+{
+ struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan);
+ unsigned long flags;
+ LIST_HEAD(head);
+
+ spin_lock_irqsave(&chan->vc.lock, flags);
+
+ axi_chan_disable(chan);
+
+ vchan_get_all_descriptors(&chan->vc, &head);
+
+ /*
+ * As vchan_dma_desc_free_list can access to desc_allocated list
+ * we need to call it in vc.lock context.
+ */
+ vchan_dma_desc_free_list(&chan->vc, &head);
+
+ spin_unlock_irqrestore(&chan->vc.lock, flags);
+
+ dev_vdbg(dchan2dev(dchan), "terminated: %s\n", axi_chan_name(chan));
+
+ return 0;
+}
+
+static int dma_chan_pause(struct dma_chan *dchan)
+{
+ struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan);
+ unsigned long flags;
+ unsigned int timeout = 20; /* timeout iterations */
+ u32 val;
+
+ spin_lock_irqsave(&chan->vc.lock, flags);
+
+ val = axi_dma_ioread32(chan->chip, DMAC_CHEN);
+ val |= BIT(chan->id) << DMAC_CHAN_SUSP_SHIFT |
+ BIT(chan->id) << DMAC_CHAN_SUSP_WE_SHIFT;
+ axi_dma_iowrite32(chan->chip, DMAC_CHEN, val);
+
+ do {
+ if (axi_chan_irq_read(chan) & DWAXIDMAC_IRQ_SUSPENDED)
+ break;
+
+ udelay(2);
+ } while (--timeout);
+
+ axi_chan_irq_clear(chan, DWAXIDMAC_IRQ_SUSPENDED);
+
+ chan->is_paused = true;
+
+ spin_unlock_irqrestore(&chan->vc.lock, flags);
+
+ return timeout ? 0 : -EAGAIN;
+}
+
+/* Called in chan locked context */
+static inline void axi_chan_resume(struct axi_dma_chan *chan)
+{
+ u32 val;
+
+ val = axi_dma_ioread32(chan->chip, DMAC_CHEN);
+ val &= ~(BIT(chan->id) << DMAC_CHAN_SUSP_SHIFT);
+ val |= (BIT(chan->id) << DMAC_CHAN_SUSP_WE_SHIFT);
+ axi_dma_iowrite32(chan->chip, DMAC_CHEN, val);
+
+ chan->is_paused = false;
+}
+
+static int dma_chan_resume(struct dma_chan *dchan)
+{
+ struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan);
+ unsigned long flags;
+
+ spin_lock_irqsave(&chan->vc.lock, flags);
+
+ if (chan->is_paused)
+ axi_chan_resume(chan);
+
+ spin_unlock_irqrestore(&chan->vc.lock, flags);
+
+ return 0;
+}
+
+static int axi_dma_suspend(struct axi_dma_chip *chip)
+{
+ axi_dma_irq_disable(chip);
+ axi_dma_disable(chip);
+
+ clk_disable_unprepare(chip->core_clk);
+ clk_disable_unprepare(chip->cfgr_clk);
+
+ return 0;
+}
+
+static int axi_dma_resume(struct axi_dma_chip *chip)
+{
+ int ret;
+
+ ret = clk_prepare_enable(chip->cfgr_clk);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_prepare_enable(chip->core_clk);
+ if (ret < 0)
+ return ret;
+
+ axi_dma_enable(chip);
+ axi_dma_irq_enable(chip);
+
+ return 0;
+}
+
+static int __maybe_unused axi_dma_runtime_suspend(struct device *dev)
+{
+ struct axi_dma_chip *chip = dev_get_drvdata(dev);
+
+ return axi_dma_suspend(chip);
+}
+
+static int __maybe_unused axi_dma_runtime_resume(struct device *dev)
+{
+ struct axi_dma_chip *chip = dev_get_drvdata(dev);
+
+ return axi_dma_resume(chip);
+}
+
+static int parse_device_properties(struct axi_dma_chip *chip)
+{
+ struct device *dev = chip->dev;
+ u32 tmp, carr[DMAC_MAX_CHANNELS];
+ int ret;
+
+ ret = device_property_read_u32(dev, "dma-channels", &tmp);
+ if (ret)
+ return ret;
+ if (tmp == 0 || tmp > DMAC_MAX_CHANNELS)
+ return -EINVAL;
+
+ chip->dw->hdata->nr_channels = tmp;
+
+ ret = device_property_read_u32(dev, "snps,dma-masters", &tmp);
+ if (ret)
+ return ret;
+ if (tmp == 0 || tmp > DMAC_MAX_MASTERS)
+ return -EINVAL;
+
+ chip->dw->hdata->nr_masters = tmp;
+
+ ret = device_property_read_u32(dev, "snps,data-width", &tmp);
+ if (ret)
+ return ret;
+ if (tmp > DWAXIDMAC_TRANS_WIDTH_MAX)
+ return -EINVAL;
+
+ chip->dw->hdata->m_data_width = tmp;
+
+ ret = device_property_read_u32_array(dev, "snps,block-size", carr,
+ chip->dw->hdata->nr_channels);
+ if (ret)
+ return ret;
+ for (tmp = 0; tmp < chip->dw->hdata->nr_channels; tmp++) {
+ if (carr[tmp] == 0 || carr[tmp] > DMAC_MAX_BLK_SIZE)
+ return -EINVAL;
+
+ chip->dw->hdata->block_size[tmp] = carr[tmp];
+ }
+
+ ret = device_property_read_u32_array(dev, "snps,priority", carr,
+ chip->dw->hdata->nr_channels);
+ if (ret)
+ return ret;
+ /* Priority value must be programmed within [0:nr_channels-1] range */
+ for (tmp = 0; tmp < chip->dw->hdata->nr_channels; tmp++) {
+ if (carr[tmp] >= chip->dw->hdata->nr_channels)
+ return -EINVAL;
+
+ chip->dw->hdata->priority[tmp] = carr[tmp];
+ }
+
+ /* axi-max-burst-len is optional property */
+ ret = device_property_read_u32(dev, "snps,axi-max-burst-len", &tmp);
+ if (!ret) {
+ if (tmp > DWAXIDMAC_ARWLEN_MAX + 1)
+ return -EINVAL;
+ if (tmp < DWAXIDMAC_ARWLEN_MIN + 1)
+ return -EINVAL;
+
+ chip->dw->hdata->restrict_axi_burst_len = true;
+ chip->dw->hdata->axi_rw_burst_len = tmp - 1;
+ }
+
+ return 0;
+}
+
+static int dw_probe(struct platform_device *pdev)
+{
+ struct axi_dma_chip *chip;
+ struct resource *mem;
+ struct dw_axi_dma *dw;
+ struct dw_axi_dma_hcfg *hdata;
+ u32 i;
+ int ret;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ dw = devm_kzalloc(&pdev->dev, sizeof(*dw), GFP_KERNEL);
+ if (!dw)
+ return -ENOMEM;
+
+ hdata = devm_kzalloc(&pdev->dev, sizeof(*hdata), GFP_KERNEL);
+ if (!hdata)
+ return -ENOMEM;
+
+ chip->dw = dw;
+ chip->dev = &pdev->dev;
+ chip->dw->hdata = hdata;
+
+ chip->irq = platform_get_irq(pdev, 0);
+ if (chip->irq < 0)
+ return chip->irq;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ chip->regs = devm_ioremap_resource(chip->dev, mem);
+ if (IS_ERR(chip->regs))
+ return PTR_ERR(chip->regs);
+
+ chip->core_clk = devm_clk_get(chip->dev, "core-clk");
+ if (IS_ERR(chip->core_clk))
+ return PTR_ERR(chip->core_clk);
+
+ chip->cfgr_clk = devm_clk_get(chip->dev, "cfgr-clk");
+ if (IS_ERR(chip->cfgr_clk))
+ return PTR_ERR(chip->cfgr_clk);
+
+ ret = parse_device_properties(chip);
+ if (ret)
+ return ret;
+
+ dw->chan = devm_kcalloc(chip->dev, hdata->nr_channels,
+ sizeof(*dw->chan), GFP_KERNEL);
+ if (!dw->chan)
+ return -ENOMEM;
+
+ ret = devm_request_irq(chip->dev, chip->irq, dw_axi_dma_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip);
+ if (ret)
+ return ret;
+
+ /* Lli address must be aligned to a 64-byte boundary */
+ dw->desc_pool = dmam_pool_create(KBUILD_MODNAME, chip->dev,
+ sizeof(struct axi_dma_desc), 64, 0);
+ if (!dw->desc_pool) {
+ dev_err(chip->dev, "No memory for descriptors dma pool\n");
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&dw->dma.channels);
+ for (i = 0; i < hdata->nr_channels; i++) {
+ struct axi_dma_chan *chan = &dw->chan[i];
+
+ chan->chip = chip;
+ chan->id = i;
+ chan->chan_regs = chip->regs + COMMON_REG_LEN + i * CHAN_REG_LEN;
+ atomic_set(&chan->descs_allocated, 0);
+
+ chan->vc.desc_free = vchan_desc_put;
+ vchan_init(&chan->vc, &dw->dma);
+ }
+
+ /* Set capabilities */
+ dma_cap_set(DMA_MEMCPY, dw->dma.cap_mask);
+
+ /* DMA capabilities */
+ dw->dma.chancnt = hdata->nr_channels;
+ dw->dma.src_addr_widths = AXI_DMA_BUSWIDTHS;
+ dw->dma.dst_addr_widths = AXI_DMA_BUSWIDTHS;
+ dw->dma.directions = BIT(DMA_MEM_TO_MEM);
+ dw->dma.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
+
+ dw->dma.dev = chip->dev;
+ dw->dma.device_tx_status = dma_chan_tx_status;
+ dw->dma.device_issue_pending = dma_chan_issue_pending;
+ dw->dma.device_terminate_all = dma_chan_terminate_all;
+ dw->dma.device_pause = dma_chan_pause;
+ dw->dma.device_resume = dma_chan_resume;
+
+ dw->dma.device_alloc_chan_resources = dma_chan_alloc_chan_resources;
+ dw->dma.device_free_chan_resources = dma_chan_free_chan_resources;
+
+ dw->dma.device_prep_dma_memcpy = dma_chan_prep_dma_memcpy;
+
+ platform_set_drvdata(pdev, chip);
+
+ pm_runtime_enable(chip->dev);
+
+ /*
+ * We can't just call pm_runtime_get here instead of
+ * pm_runtime_get_noresume + axi_dma_resume because we need
+ * driver to work also without Runtime PM.
+ */
+ pm_runtime_get_noresume(chip->dev);
+ ret = axi_dma_resume(chip);
+ if (ret < 0)
+ goto err_pm_disable;
+
+ axi_dma_hw_init(chip);
+
+ pm_runtime_put(chip->dev);
+
+ ret = dma_async_device_register(&dw->dma);
+ if (ret)
+ goto err_pm_disable;
+
+ dev_info(chip->dev, "DesignWare AXI DMA Controller, %d channels\n",
+ dw->hdata->nr_channels);
+
+ return 0;
+
+err_pm_disable:
+ pm_runtime_disable(chip->dev);
+
+ return ret;
+}
+
+static int dw_remove(struct platform_device *pdev)
+{
+ struct axi_dma_chip *chip = platform_get_drvdata(pdev);
+ struct dw_axi_dma *dw = chip->dw;
+ struct axi_dma_chan *chan, *_chan;
+ u32 i;
+
+ /* Enable clk before accessing to registers */
+ clk_prepare_enable(chip->cfgr_clk);
+ clk_prepare_enable(chip->core_clk);
+ axi_dma_irq_disable(chip);
+ for (i = 0; i < dw->hdata->nr_channels; i++) {
+ axi_chan_disable(&chip->dw->chan[i]);
+ axi_chan_irq_disable(&chip->dw->chan[i], DWAXIDMAC_IRQ_ALL);
+ }
+ axi_dma_disable(chip);
+
+ pm_runtime_disable(chip->dev);
+ axi_dma_suspend(chip);
+
+ devm_free_irq(chip->dev, chip->irq, chip);
+
+ list_for_each_entry_safe(chan, _chan, &dw->dma.channels,
+ vc.chan.device_node) {
+ list_del(&chan->vc.chan.device_node);
+ tasklet_kill(&chan->vc.task);
+ }
+
+ dma_async_device_unregister(&dw->dma);
+
+ return 0;
+}
+
+static const struct dev_pm_ops dw_axi_dma_pm_ops = {
+ SET_RUNTIME_PM_OPS(axi_dma_runtime_suspend, axi_dma_runtime_resume, NULL)
+};
+
+static const struct of_device_id dw_dma_of_id_table[] = {
+ { .compatible = "snps,axi-dma-1.01a" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, dw_dma_of_id_table);
+
+static struct platform_driver dw_driver = {
+ .probe = dw_probe,
+ .remove = dw_remove,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = of_match_ptr(dw_dma_of_id_table),
+ .pm = &dw_axi_dma_pm_ops,
+ },
+};
+module_platform_driver(dw_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Synopsys DesignWare AXI DMA Controller platform driver");
+MODULE_AUTHOR("Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>");
diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h
new file mode 100644
index 000000000000..f8888dc0b8dc
--- /dev/null
+++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h
@@ -0,0 +1,334 @@
+// SPDX-License-Identifier: GPL-2.0
+// (C) 2017-2018 Synopsys, Inc. (www.synopsys.com)
+
+/*
+ * Synopsys DesignWare AXI DMA Controller driver.
+ *
+ * Author: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>
+ */
+
+#ifndef _AXI_DMA_PLATFORM_H
+#define _AXI_DMA_PLATFORM_H
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/dmaengine.h>
+#include <linux/types.h>
+
+#include "../virt-dma.h"
+
+#define DMAC_MAX_CHANNELS 8
+#define DMAC_MAX_MASTERS 2
+#define DMAC_MAX_BLK_SIZE 0x200000
+
+struct dw_axi_dma_hcfg {
+ u32 nr_channels;
+ u32 nr_masters;
+ u32 m_data_width;
+ u32 block_size[DMAC_MAX_CHANNELS];
+ u32 priority[DMAC_MAX_CHANNELS];
+ /* maximum supported axi burst length */
+ u32 axi_rw_burst_len;
+ bool restrict_axi_burst_len;
+};
+
+struct axi_dma_chan {
+ struct axi_dma_chip *chip;
+ void __iomem *chan_regs;
+ u8 id;
+ atomic_t descs_allocated;
+
+ struct virt_dma_chan vc;
+
+ /* these other elements are all protected by vc.lock */
+ bool is_paused;
+};
+
+struct dw_axi_dma {
+ struct dma_device dma;
+ struct dw_axi_dma_hcfg *hdata;
+ struct dma_pool *desc_pool;
+
+ /* channels */
+ struct axi_dma_chan *chan;
+};
+
+struct axi_dma_chip {
+ struct device *dev;
+ int irq;
+ void __iomem *regs;
+ struct clk *core_clk;
+ struct clk *cfgr_clk;
+ struct dw_axi_dma *dw;
+};
+
+/* LLI == Linked List Item */
+struct __packed axi_dma_lli {
+ __le64 sar;
+ __le64 dar;
+ __le32 block_ts_lo;
+ __le32 block_ts_hi;
+ __le64 llp;
+ __le32 ctl_lo;
+ __le32 ctl_hi;
+ __le32 sstat;
+ __le32 dstat;
+ __le32 status_lo;
+ __le32 ststus_hi;
+ __le32 reserved_lo;
+ __le32 reserved_hi;
+};
+
+struct axi_dma_desc {
+ struct axi_dma_lli lli;
+
+ struct virt_dma_desc vd;
+ struct axi_dma_chan *chan;
+ struct list_head xfer_list;
+};
+
+static inline struct device *dchan2dev(struct dma_chan *dchan)
+{
+ return &dchan->dev->device;
+}
+
+static inline struct device *chan2dev(struct axi_dma_chan *chan)
+{
+ return &chan->vc.chan.dev->device;
+}
+
+static inline struct axi_dma_desc *vd_to_axi_desc(struct virt_dma_desc *vd)
+{
+ return container_of(vd, struct axi_dma_desc, vd);
+}
+
+static inline struct axi_dma_chan *vc_to_axi_dma_chan(struct virt_dma_chan *vc)
+{
+ return container_of(vc, struct axi_dma_chan, vc);
+}
+
+static inline struct axi_dma_chan *dchan_to_axi_dma_chan(struct dma_chan *dchan)
+{
+ return vc_to_axi_dma_chan(to_virt_chan(dchan));
+}
+
+
+#define COMMON_REG_LEN 0x100
+#define CHAN_REG_LEN 0x100
+
+/* Common registers offset */
+#define DMAC_ID 0x000 /* R DMAC ID */
+#define DMAC_COMPVER 0x008 /* R DMAC Component Version */
+#define DMAC_CFG 0x010 /* R/W DMAC Configuration */
+#define DMAC_CHEN 0x018 /* R/W DMAC Channel Enable */
+#define DMAC_CHEN_L 0x018 /* R/W DMAC Channel Enable 00-31 */
+#define DMAC_CHEN_H 0x01C /* R/W DMAC Channel Enable 32-63 */
+#define DMAC_INTSTATUS 0x030 /* R DMAC Interrupt Status */
+#define DMAC_COMMON_INTCLEAR 0x038 /* W DMAC Interrupt Clear */
+#define DMAC_COMMON_INTSTATUS_ENA 0x040 /* R DMAC Interrupt Status Enable */
+#define DMAC_COMMON_INTSIGNAL_ENA 0x048 /* R/W DMAC Interrupt Signal Enable */
+#define DMAC_COMMON_INTSTATUS 0x050 /* R DMAC Interrupt Status */
+#define DMAC_RESET 0x058 /* R DMAC Reset Register1 */
+
+/* DMA channel registers offset */
+#define CH_SAR 0x000 /* R/W Chan Source Address */
+#define CH_DAR 0x008 /* R/W Chan Destination Address */
+#define CH_BLOCK_TS 0x010 /* R/W Chan Block Transfer Size */
+#define CH_CTL 0x018 /* R/W Chan Control */
+#define CH_CTL_L 0x018 /* R/W Chan Control 00-31 */
+#define CH_CTL_H 0x01C /* R/W Chan Control 32-63 */
+#define CH_CFG 0x020 /* R/W Chan Configuration */
+#define CH_CFG_L 0x020 /* R/W Chan Configuration 00-31 */
+#define CH_CFG_H 0x024 /* R/W Chan Configuration 32-63 */
+#define CH_LLP 0x028 /* R/W Chan Linked List Pointer */
+#define CH_STATUS 0x030 /* R Chan Status */
+#define CH_SWHSSRC 0x038 /* R/W Chan SW Handshake Source */
+#define CH_SWHSDST 0x040 /* R/W Chan SW Handshake Destination */
+#define CH_BLK_TFR_RESUMEREQ 0x048 /* W Chan Block Transfer Resume Req */
+#define CH_AXI_ID 0x050 /* R/W Chan AXI ID */
+#define CH_AXI_QOS 0x058 /* R/W Chan AXI QOS */
+#define CH_SSTAT 0x060 /* R Chan Source Status */
+#define CH_DSTAT 0x068 /* R Chan Destination Status */
+#define CH_SSTATAR 0x070 /* R/W Chan Source Status Fetch Addr */
+#define CH_DSTATAR 0x078 /* R/W Chan Destination Status Fetch Addr */
+#define CH_INTSTATUS_ENA 0x080 /* R/W Chan Interrupt Status Enable */
+#define CH_INTSTATUS 0x088 /* R/W Chan Interrupt Status */
+#define CH_INTSIGNAL_ENA 0x090 /* R/W Chan Interrupt Signal Enable */
+#define CH_INTCLEAR 0x098 /* W Chan Interrupt Clear */
+
+
+/* DMAC_CFG */
+#define DMAC_EN_POS 0
+#define DMAC_EN_MASK BIT(DMAC_EN_POS)
+
+#define INT_EN_POS 1
+#define INT_EN_MASK BIT(INT_EN_POS)
+
+#define DMAC_CHAN_EN_SHIFT 0
+#define DMAC_CHAN_EN_WE_SHIFT 8
+
+#define DMAC_CHAN_SUSP_SHIFT 16
+#define DMAC_CHAN_SUSP_WE_SHIFT 24
+
+/* CH_CTL_H */
+#define CH_CTL_H_ARLEN_EN BIT(6)
+#define CH_CTL_H_ARLEN_POS 7
+#define CH_CTL_H_AWLEN_EN BIT(15)
+#define CH_CTL_H_AWLEN_POS 16
+
+enum {
+ DWAXIDMAC_ARWLEN_1 = 0,
+ DWAXIDMAC_ARWLEN_2 = 1,
+ DWAXIDMAC_ARWLEN_4 = 3,
+ DWAXIDMAC_ARWLEN_8 = 7,
+ DWAXIDMAC_ARWLEN_16 = 15,
+ DWAXIDMAC_ARWLEN_32 = 31,
+ DWAXIDMAC_ARWLEN_64 = 63,
+ DWAXIDMAC_ARWLEN_128 = 127,
+ DWAXIDMAC_ARWLEN_256 = 255,
+ DWAXIDMAC_ARWLEN_MIN = DWAXIDMAC_ARWLEN_1,
+ DWAXIDMAC_ARWLEN_MAX = DWAXIDMAC_ARWLEN_256
+};
+
+#define CH_CTL_H_LLI_LAST BIT(30)
+#define CH_CTL_H_LLI_VALID BIT(31)
+
+/* CH_CTL_L */
+#define CH_CTL_L_LAST_WRITE_EN BIT(30)
+
+#define CH_CTL_L_DST_MSIZE_POS 18
+#define CH_CTL_L_SRC_MSIZE_POS 14
+
+enum {
+ DWAXIDMAC_BURST_TRANS_LEN_1 = 0,
+ DWAXIDMAC_BURST_TRANS_LEN_4,
+ DWAXIDMAC_BURST_TRANS_LEN_8,
+ DWAXIDMAC_BURST_TRANS_LEN_16,
+ DWAXIDMAC_BURST_TRANS_LEN_32,
+ DWAXIDMAC_BURST_TRANS_LEN_64,
+ DWAXIDMAC_BURST_TRANS_LEN_128,
+ DWAXIDMAC_BURST_TRANS_LEN_256,
+ DWAXIDMAC_BURST_TRANS_LEN_512,
+ DWAXIDMAC_BURST_TRANS_LEN_1024
+};
+
+#define CH_CTL_L_DST_WIDTH_POS 11
+#define CH_CTL_L_SRC_WIDTH_POS 8
+
+#define CH_CTL_L_DST_INC_POS 6
+#define CH_CTL_L_SRC_INC_POS 4
+enum {
+ DWAXIDMAC_CH_CTL_L_INC = 0,
+ DWAXIDMAC_CH_CTL_L_NOINC
+};
+
+#define CH_CTL_L_DST_MAST BIT(2)
+#define CH_CTL_L_SRC_MAST BIT(0)
+
+/* CH_CFG_H */
+#define CH_CFG_H_PRIORITY_POS 17
+#define CH_CFG_H_HS_SEL_DST_POS 4
+#define CH_CFG_H_HS_SEL_SRC_POS 3
+enum {
+ DWAXIDMAC_HS_SEL_HW = 0,
+ DWAXIDMAC_HS_SEL_SW
+};
+
+#define CH_CFG_H_TT_FC_POS 0
+enum {
+ DWAXIDMAC_TT_FC_MEM_TO_MEM_DMAC = 0,
+ DWAXIDMAC_TT_FC_MEM_TO_PER_DMAC,
+ DWAXIDMAC_TT_FC_PER_TO_MEM_DMAC,
+ DWAXIDMAC_TT_FC_PER_TO_PER_DMAC,
+ DWAXIDMAC_TT_FC_PER_TO_MEM_SRC,
+ DWAXIDMAC_TT_FC_PER_TO_PER_SRC,
+ DWAXIDMAC_TT_FC_MEM_TO_PER_DST,
+ DWAXIDMAC_TT_FC_PER_TO_PER_DST
+};
+
+/* CH_CFG_L */
+#define CH_CFG_L_DST_MULTBLK_TYPE_POS 2
+#define CH_CFG_L_SRC_MULTBLK_TYPE_POS 0
+enum {
+ DWAXIDMAC_MBLK_TYPE_CONTIGUOUS = 0,
+ DWAXIDMAC_MBLK_TYPE_RELOAD,
+ DWAXIDMAC_MBLK_TYPE_SHADOW_REG,
+ DWAXIDMAC_MBLK_TYPE_LL
+};
+
+/**
+ * DW AXI DMA channel interrupts
+ *
+ * @DWAXIDMAC_IRQ_NONE: Bitmask of no one interrupt
+ * @DWAXIDMAC_IRQ_BLOCK_TRF: Block transfer complete
+ * @DWAXIDMAC_IRQ_DMA_TRF: Dma transfer complete
+ * @DWAXIDMAC_IRQ_SRC_TRAN: Source transaction complete
+ * @DWAXIDMAC_IRQ_DST_TRAN: Destination transaction complete
+ * @DWAXIDMAC_IRQ_SRC_DEC_ERR: Source decode error
+ * @DWAXIDMAC_IRQ_DST_DEC_ERR: Destination decode error
+ * @DWAXIDMAC_IRQ_SRC_SLV_ERR: Source slave error
+ * @DWAXIDMAC_IRQ_DST_SLV_ERR: Destination slave error
+ * @DWAXIDMAC_IRQ_LLI_RD_DEC_ERR: LLI read decode error
+ * @DWAXIDMAC_IRQ_LLI_WR_DEC_ERR: LLI write decode error
+ * @DWAXIDMAC_IRQ_LLI_RD_SLV_ERR: LLI read slave error
+ * @DWAXIDMAC_IRQ_LLI_WR_SLV_ERR: LLI write slave error
+ * @DWAXIDMAC_IRQ_INVALID_ERR: LLI invalid error or Shadow register error
+ * @DWAXIDMAC_IRQ_MULTIBLKTYPE_ERR: Slave Interface Multiblock type error
+ * @DWAXIDMAC_IRQ_DEC_ERR: Slave Interface decode error
+ * @DWAXIDMAC_IRQ_WR2RO_ERR: Slave Interface write to read only error
+ * @DWAXIDMAC_IRQ_RD2RWO_ERR: Slave Interface read to write only error
+ * @DWAXIDMAC_IRQ_WRONCHEN_ERR: Slave Interface write to channel error
+ * @DWAXIDMAC_IRQ_SHADOWREG_ERR: Slave Interface shadow reg error
+ * @DWAXIDMAC_IRQ_WRONHOLD_ERR: Slave Interface hold error
+ * @DWAXIDMAC_IRQ_LOCK_CLEARED: Lock Cleared Status
+ * @DWAXIDMAC_IRQ_SRC_SUSPENDED: Source Suspended Status
+ * @DWAXIDMAC_IRQ_SUSPENDED: Channel Suspended Status
+ * @DWAXIDMAC_IRQ_DISABLED: Channel Disabled Status
+ * @DWAXIDMAC_IRQ_ABORTED: Channel Aborted Status
+ * @DWAXIDMAC_IRQ_ALL_ERR: Bitmask of all error interrupts
+ * @DWAXIDMAC_IRQ_ALL: Bitmask of all interrupts
+ */
+enum {
+ DWAXIDMAC_IRQ_NONE = 0,
+ DWAXIDMAC_IRQ_BLOCK_TRF = BIT(0),
+ DWAXIDMAC_IRQ_DMA_TRF = BIT(1),
+ DWAXIDMAC_IRQ_SRC_TRAN = BIT(3),
+ DWAXIDMAC_IRQ_DST_TRAN = BIT(4),
+ DWAXIDMAC_IRQ_SRC_DEC_ERR = BIT(5),
+ DWAXIDMAC_IRQ_DST_DEC_ERR = BIT(6),
+ DWAXIDMAC_IRQ_SRC_SLV_ERR = BIT(7),
+ DWAXIDMAC_IRQ_DST_SLV_ERR = BIT(8),
+ DWAXIDMAC_IRQ_LLI_RD_DEC_ERR = BIT(9),
+ DWAXIDMAC_IRQ_LLI_WR_DEC_ERR = BIT(10),
+ DWAXIDMAC_IRQ_LLI_RD_SLV_ERR = BIT(11),
+ DWAXIDMAC_IRQ_LLI_WR_SLV_ERR = BIT(12),
+ DWAXIDMAC_IRQ_INVALID_ERR = BIT(13),
+ DWAXIDMAC_IRQ_MULTIBLKTYPE_ERR = BIT(14),
+ DWAXIDMAC_IRQ_DEC_ERR = BIT(16),
+ DWAXIDMAC_IRQ_WR2RO_ERR = BIT(17),
+ DWAXIDMAC_IRQ_RD2RWO_ERR = BIT(18),
+ DWAXIDMAC_IRQ_WRONCHEN_ERR = BIT(19),
+ DWAXIDMAC_IRQ_SHADOWREG_ERR = BIT(20),
+ DWAXIDMAC_IRQ_WRONHOLD_ERR = BIT(21),
+ DWAXIDMAC_IRQ_LOCK_CLEARED = BIT(27),
+ DWAXIDMAC_IRQ_SRC_SUSPENDED = BIT(28),
+ DWAXIDMAC_IRQ_SUSPENDED = BIT(29),
+ DWAXIDMAC_IRQ_DISABLED = BIT(30),
+ DWAXIDMAC_IRQ_ABORTED = BIT(31),
+ DWAXIDMAC_IRQ_ALL_ERR = (GENMASK(21, 16) | GENMASK(14, 5)),
+ DWAXIDMAC_IRQ_ALL = GENMASK(31, 0)
+};
+
+enum {
+ DWAXIDMAC_TRANS_WIDTH_8 = 0,
+ DWAXIDMAC_TRANS_WIDTH_16,
+ DWAXIDMAC_TRANS_WIDTH_32,
+ DWAXIDMAC_TRANS_WIDTH_64,
+ DWAXIDMAC_TRANS_WIDTH_128,
+ DWAXIDMAC_TRANS_WIDTH_256,
+ DWAXIDMAC_TRANS_WIDTH_512,
+ DWAXIDMAC_TRANS_WIDTH_MAX = DWAXIDMAC_TRANS_WIDTH_512
+};
+
+#endif /* _AXI_DMA_PLATFORM_H */
diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c
index 948df1ab5f1a..85ea92fcea54 100644
--- a/drivers/dma/edma.c
+++ b/drivers/dma/edma.c
@@ -1876,6 +1876,11 @@ static void edma_dma_init(struct edma_cc *ecc, bool legacy_mode)
if (memcpy_channels) {
m_ddev = devm_kzalloc(ecc->dev, sizeof(*m_ddev), GFP_KERNEL);
+ if (!m_ddev) {
+ dev_warn(ecc->dev, "memcpy is disabled due to OoM\n");
+ memcpy_channels = NULL;
+ goto ch_setup;
+ }
ecc->dma_memcpy = m_ddev;
dma_cap_zero(m_ddev->cap_mask);
@@ -1903,6 +1908,7 @@ static void edma_dma_init(struct edma_cc *ecc, bool legacy_mode)
dev_info(ecc->dev, "memcpy is disabled\n");
}
+ch_setup:
for (i = 0; i < ecc->num_channels; i++) {
struct edma_chan *echan = &ecc->slave_chans[i];
echan->ch_num = EDMA_CTLR_CHAN(ecc->id, i);
diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
index e7db24c67030..ccd03c3cedfe 100644
--- a/drivers/dma/imx-sdma.c
+++ b/drivers/dma/imx-sdma.c
@@ -338,6 +338,7 @@ struct sdma_channel {
unsigned int chn_real_count;
struct tasklet_struct tasklet;
struct imx_dma_data data;
+ bool enabled;
};
#define IMX_DMA_SG_LOOP BIT(0)
@@ -596,7 +597,14 @@ static int sdma_config_ownership(struct sdma_channel *sdmac,
static void sdma_enable_channel(struct sdma_engine *sdma, int channel)
{
+ unsigned long flags;
+ struct sdma_channel *sdmac = &sdma->channel[channel];
+
writel(BIT(channel), sdma->regs + SDMA_H_START);
+
+ spin_lock_irqsave(&sdmac->lock, flags);
+ sdmac->enabled = true;
+ spin_unlock_irqrestore(&sdmac->lock, flags);
}
/*
@@ -685,6 +693,14 @@ static void sdma_update_channel_loop(struct sdma_channel *sdmac)
struct sdma_buffer_descriptor *bd;
int error = 0;
enum dma_status old_status = sdmac->status;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sdmac->lock, flags);
+ if (!sdmac->enabled) {
+ spin_unlock_irqrestore(&sdmac->lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&sdmac->lock, flags);
/*
* loop mode. Iterate over descriptors, re-setup them and
@@ -938,10 +954,15 @@ static int sdma_disable_channel(struct dma_chan *chan)
struct sdma_channel *sdmac = to_sdma_chan(chan);
struct sdma_engine *sdma = sdmac->sdma;
int channel = sdmac->channel;
+ unsigned long flags;
writel_relaxed(BIT(channel), sdma->regs + SDMA_H_STATSTOP);
sdmac->status = DMA_ERROR;
+ spin_lock_irqsave(&sdmac->lock, flags);
+ sdmac->enabled = false;
+ spin_unlock_irqrestore(&sdmac->lock, flags);
+
return 0;
}
diff --git a/drivers/dma/mediatek/Kconfig b/drivers/dma/mediatek/Kconfig
new file mode 100644
index 000000000000..27bac0bba09e
--- /dev/null
+++ b/drivers/dma/mediatek/Kconfig
@@ -0,0 +1,13 @@
+
+config MTK_HSDMA
+ tristate "MediaTek High-Speed DMA controller support"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ select DMA_ENGINE
+ select DMA_VIRTUAL_CHANNELS
+ ---help---
+ Enable support for High-Speed DMA controller on MediaTek
+ SoCs.
+
+ This controller provides the channels which is dedicated to
+ memory-to-memory transfer to offload from CPU through ring-
+ based descriptor management.
diff --git a/drivers/dma/mediatek/Makefile b/drivers/dma/mediatek/Makefile
new file mode 100644
index 000000000000..6e778f842f01
--- /dev/null
+++ b/drivers/dma/mediatek/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_MTK_HSDMA) += mtk-hsdma.o
diff --git a/drivers/dma/mediatek/mtk-hsdma.c b/drivers/dma/mediatek/mtk-hsdma.c
new file mode 100644
index 000000000000..b7ec56ae02a6
--- /dev/null
+++ b/drivers/dma/mediatek/mtk-hsdma.c
@@ -0,0 +1,1056 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2017-2018 MediaTek Inc.
+
+/*
+ * Driver for MediaTek High-Speed DMA Controller
+ *
+ * Author: Sean Wang <sean.wang@mediatek.com>
+ *
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/iopoll.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_dma.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/refcount.h>
+#include <linux/slab.h>
+
+#include "../virt-dma.h"
+
+#define MTK_HSDMA_USEC_POLL 20
+#define MTK_HSDMA_TIMEOUT_POLL 200000
+#define MTK_HSDMA_DMA_BUSWIDTHS BIT(DMA_SLAVE_BUSWIDTH_4_BYTES)
+
+/* The default number of virtual channel */
+#define MTK_HSDMA_NR_VCHANS 3
+
+/* Only one physical channel supported */
+#define MTK_HSDMA_NR_MAX_PCHANS 1
+
+/* Macro for physical descriptor (PD) manipulation */
+/* The number of PD which must be 2 of power */
+#define MTK_DMA_SIZE 64
+#define MTK_HSDMA_NEXT_DESP_IDX(x, y) (((x) + 1) & ((y) - 1))
+#define MTK_HSDMA_LAST_DESP_IDX(x, y) (((x) - 1) & ((y) - 1))
+#define MTK_HSDMA_MAX_LEN 0x3f80
+#define MTK_HSDMA_ALIGN_SIZE 4
+#define MTK_HSDMA_PLEN_MASK 0x3fff
+#define MTK_HSDMA_DESC_PLEN(x) (((x) & MTK_HSDMA_PLEN_MASK) << 16)
+#define MTK_HSDMA_DESC_PLEN_GET(x) (((x) >> 16) & MTK_HSDMA_PLEN_MASK)
+
+/* Registers for underlying ring manipulation */
+#define MTK_HSDMA_TX_BASE 0x0
+#define MTK_HSDMA_TX_CNT 0x4
+#define MTK_HSDMA_TX_CPU 0x8
+#define MTK_HSDMA_TX_DMA 0xc
+#define MTK_HSDMA_RX_BASE 0x100
+#define MTK_HSDMA_RX_CNT 0x104
+#define MTK_HSDMA_RX_CPU 0x108
+#define MTK_HSDMA_RX_DMA 0x10c
+
+/* Registers for global setup */
+#define MTK_HSDMA_GLO 0x204
+#define MTK_HSDMA_GLO_MULTI_DMA BIT(10)
+#define MTK_HSDMA_TX_WB_DDONE BIT(6)
+#define MTK_HSDMA_BURST_64BYTES (0x2 << 4)
+#define MTK_HSDMA_GLO_RX_BUSY BIT(3)
+#define MTK_HSDMA_GLO_RX_DMA BIT(2)
+#define MTK_HSDMA_GLO_TX_BUSY BIT(1)
+#define MTK_HSDMA_GLO_TX_DMA BIT(0)
+#define MTK_HSDMA_GLO_DMA (MTK_HSDMA_GLO_TX_DMA | \
+ MTK_HSDMA_GLO_RX_DMA)
+#define MTK_HSDMA_GLO_BUSY (MTK_HSDMA_GLO_RX_BUSY | \
+ MTK_HSDMA_GLO_TX_BUSY)
+#define MTK_HSDMA_GLO_DEFAULT (MTK_HSDMA_GLO_TX_DMA | \
+ MTK_HSDMA_GLO_RX_DMA | \
+ MTK_HSDMA_TX_WB_DDONE | \
+ MTK_HSDMA_BURST_64BYTES | \
+ MTK_HSDMA_GLO_MULTI_DMA)
+
+/* Registers for reset */
+#define MTK_HSDMA_RESET 0x208
+#define MTK_HSDMA_RST_TX BIT(0)
+#define MTK_HSDMA_RST_RX BIT(16)
+
+/* Registers for interrupt control */
+#define MTK_HSDMA_DLYINT 0x20c
+#define MTK_HSDMA_RXDLY_INT_EN BIT(15)
+
+/* Interrupt fires when the pending number's more than the specified */
+#define MTK_HSDMA_RXMAX_PINT(x) (((x) & 0x7f) << 8)
+
+/* Interrupt fires when the pending time's more than the specified in 20 us */
+#define MTK_HSDMA_RXMAX_PTIME(x) ((x) & 0x7f)
+#define MTK_HSDMA_DLYINT_DEFAULT (MTK_HSDMA_RXDLY_INT_EN | \
+ MTK_HSDMA_RXMAX_PINT(20) | \
+ MTK_HSDMA_RXMAX_PTIME(20))
+#define MTK_HSDMA_INT_STATUS 0x220
+#define MTK_HSDMA_INT_ENABLE 0x228
+#define MTK_HSDMA_INT_RXDONE BIT(16)
+
+enum mtk_hsdma_vdesc_flag {
+ MTK_HSDMA_VDESC_FINISHED = 0x01,
+};
+
+#define IS_MTK_HSDMA_VDESC_FINISHED(x) ((x) == MTK_HSDMA_VDESC_FINISHED)
+
+/**
+ * struct mtk_hsdma_pdesc - This is the struct holding info describing physical
+ * descriptor (PD) and its placement must be kept at
+ * 4-bytes alignment in little endian order.
+ * @desc[1-4]: The control pad used to indicate hardware how to
+ * deal with the descriptor such as source and
+ * destination address and data length. The maximum
+ * data length each pdesc can handle is 0x3f80 bytes
+ */
+struct mtk_hsdma_pdesc {
+ __le32 desc1;
+ __le32 desc2;
+ __le32 desc3;
+ __le32 desc4;
+} __packed __aligned(4);
+
+/**
+ * struct mtk_hsdma_vdesc - This is the struct holding info describing virtual
+ * descriptor (VD)
+ * @vd: An instance for struct virt_dma_desc
+ * @len: The total data size device wants to move
+ * @residue: The remaining data size device will move
+ * @dest: The destination address device wants to move to
+ * @src: The source address device wants to move from
+ */
+struct mtk_hsdma_vdesc {
+ struct virt_dma_desc vd;
+ size_t len;
+ size_t residue;
+ dma_addr_t dest;
+ dma_addr_t src;
+};
+
+/**
+ * struct mtk_hsdma_cb - This is the struct holding extra info required for RX
+ * ring to know what relevant VD the the PD is being
+ * mapped to.
+ * @vd: Pointer to the relevant VD.
+ * @flag: Flag indicating what action should be taken when VD
+ * is completed.
+ */
+struct mtk_hsdma_cb {
+ struct virt_dma_desc *vd;
+ enum mtk_hsdma_vdesc_flag flag;
+};
+
+/**
+ * struct mtk_hsdma_ring - This struct holds info describing underlying ring
+ * space
+ * @txd: The descriptor TX ring which describes DMA source
+ * information
+ * @rxd: The descriptor RX ring which describes DMA
+ * destination information
+ * @cb: The extra information pointed at by RX ring
+ * @tphys: The physical addr of TX ring
+ * @rphys: The physical addr of RX ring
+ * @cur_tptr: Pointer to the next free descriptor used by the host
+ * @cur_rptr: Pointer to the last done descriptor by the device
+ */
+struct mtk_hsdma_ring {
+ struct mtk_hsdma_pdesc *txd;
+ struct mtk_hsdma_pdesc *rxd;
+ struct mtk_hsdma_cb *cb;
+ dma_addr_t tphys;
+ dma_addr_t rphys;
+ u16 cur_tptr;
+ u16 cur_rptr;
+};
+
+/**
+ * struct mtk_hsdma_pchan - This is the struct holding info describing physical
+ * channel (PC)
+ * @ring: An instance for the underlying ring
+ * @sz_ring: Total size allocated for the ring
+ * @nr_free: Total number of free rooms in the ring. It would
+ * be accessed and updated frequently between IRQ
+ * context and user context to reflect whether ring
+ * can accept requests from VD.
+ */
+struct mtk_hsdma_pchan {
+ struct mtk_hsdma_ring ring;
+ size_t sz_ring;
+ atomic_t nr_free;
+};
+
+/**
+ * struct mtk_hsdma_vchan - This is the struct holding info describing virtual
+ * channel (VC)
+ * @vc: An instance for struct virt_dma_chan
+ * @issue_completion: The wait for all issued descriptors completited
+ * @issue_synchronize: Bool indicating channel synchronization starts
+ * @desc_hw_processing: List those descriptors the hardware is processing,
+ * which is protected by vc.lock
+ */
+struct mtk_hsdma_vchan {
+ struct virt_dma_chan vc;
+ struct completion issue_completion;
+ bool issue_synchronize;
+ struct list_head desc_hw_processing;
+};
+
+/**
+ * struct mtk_hsdma_soc - This is the struct holding differences among SoCs
+ * @ddone: Bit mask for DDONE
+ * @ls0: Bit mask for LS0
+ */
+struct mtk_hsdma_soc {
+ __le32 ddone;
+ __le32 ls0;
+};
+
+/**
+ * struct mtk_hsdma_device - This is the struct holding info describing HSDMA
+ * device
+ * @ddev: An instance for struct dma_device
+ * @base: The mapped register I/O base
+ * @clk: The clock that device internal is using
+ * @irq: The IRQ that device are using
+ * @dma_requests: The number of VCs the device supports to
+ * @vc: The pointer to all available VCs
+ * @pc: The pointer to the underlying PC
+ * @pc_refcnt: Track how many VCs are using the PC
+ * @lock: Lock protect agaisting multiple VCs access PC
+ * @soc: The pointer to area holding differences among
+ * vaious platform
+ */
+struct mtk_hsdma_device {
+ struct dma_device ddev;
+ void __iomem *base;
+ struct clk *clk;
+ u32 irq;
+
+ u32 dma_requests;
+ struct mtk_hsdma_vchan *vc;
+ struct mtk_hsdma_pchan *pc;
+ refcount_t pc_refcnt;
+
+ /* Lock used to protect against multiple VCs access PC */
+ spinlock_t lock;
+
+ const struct mtk_hsdma_soc *soc;
+};
+
+static struct mtk_hsdma_device *to_hsdma_dev(struct dma_chan *chan)
+{
+ return container_of(chan->device, struct mtk_hsdma_device, ddev);
+}
+
+static inline struct mtk_hsdma_vchan *to_hsdma_vchan(struct dma_chan *chan)
+{
+ return container_of(chan, struct mtk_hsdma_vchan, vc.chan);
+}
+
+static struct mtk_hsdma_vdesc *to_hsdma_vdesc(struct virt_dma_desc *vd)
+{
+ return container_of(vd, struct mtk_hsdma_vdesc, vd);
+}
+
+static struct device *hsdma2dev(struct mtk_hsdma_device *hsdma)
+{
+ return hsdma->ddev.dev;
+}
+
+static u32 mtk_dma_read(struct mtk_hsdma_device *hsdma, u32 reg)
+{
+ return readl(hsdma->base + reg);
+}
+
+static void mtk_dma_write(struct mtk_hsdma_device *hsdma, u32 reg, u32 val)
+{
+ writel(val, hsdma->base + reg);
+}
+
+static void mtk_dma_rmw(struct mtk_hsdma_device *hsdma, u32 reg,
+ u32 mask, u32 set)
+{
+ u32 val;
+
+ val = mtk_dma_read(hsdma, reg);
+ val &= ~mask;
+ val |= set;
+ mtk_dma_write(hsdma, reg, val);
+}
+
+static void mtk_dma_set(struct mtk_hsdma_device *hsdma, u32 reg, u32 val)
+{
+ mtk_dma_rmw(hsdma, reg, 0, val);
+}
+
+static void mtk_dma_clr(struct mtk_hsdma_device *hsdma, u32 reg, u32 val)
+{
+ mtk_dma_rmw(hsdma, reg, val, 0);
+}
+
+static void mtk_hsdma_vdesc_free(struct virt_dma_desc *vd)
+{
+ kfree(container_of(vd, struct mtk_hsdma_vdesc, vd));
+}
+
+static int mtk_hsdma_busy_wait(struct mtk_hsdma_device *hsdma)
+{
+ u32 status = 0;
+
+ return readl_poll_timeout(hsdma->base + MTK_HSDMA_GLO, status,
+ !(status & MTK_HSDMA_GLO_BUSY),
+ MTK_HSDMA_USEC_POLL,
+ MTK_HSDMA_TIMEOUT_POLL);
+}
+
+static int mtk_hsdma_alloc_pchan(struct mtk_hsdma_device *hsdma,
+ struct mtk_hsdma_pchan *pc)
+{
+ struct mtk_hsdma_ring *ring = &pc->ring;
+ int err;
+
+ memset(pc, 0, sizeof(*pc));
+
+ /*
+ * Allocate ring space where [0 ... MTK_DMA_SIZE - 1] is for TX ring
+ * and [MTK_DMA_SIZE ... 2 * MTK_DMA_SIZE - 1] is for RX ring.
+ */
+ pc->sz_ring = 2 * MTK_DMA_SIZE * sizeof(*ring->txd);
+ ring->txd = dma_zalloc_coherent(hsdma2dev(hsdma), pc->sz_ring,
+ &ring->tphys, GFP_NOWAIT);
+ if (!ring->txd)
+ return -ENOMEM;
+
+ ring->rxd = &ring->txd[MTK_DMA_SIZE];
+ ring->rphys = ring->tphys + MTK_DMA_SIZE * sizeof(*ring->txd);
+ ring->cur_tptr = 0;
+ ring->cur_rptr = MTK_DMA_SIZE - 1;
+
+ ring->cb = kcalloc(MTK_DMA_SIZE, sizeof(*ring->cb), GFP_NOWAIT);
+ if (!ring->cb) {
+ err = -ENOMEM;
+ goto err_free_dma;
+ }
+
+ atomic_set(&pc->nr_free, MTK_DMA_SIZE - 1);
+
+ /* Disable HSDMA and wait for the completion */
+ mtk_dma_clr(hsdma, MTK_HSDMA_GLO, MTK_HSDMA_GLO_DMA);
+ err = mtk_hsdma_busy_wait(hsdma);
+ if (err)
+ goto err_free_cb;
+
+ /* Reset */
+ mtk_dma_set(hsdma, MTK_HSDMA_RESET,
+ MTK_HSDMA_RST_TX | MTK_HSDMA_RST_RX);
+ mtk_dma_clr(hsdma, MTK_HSDMA_RESET,
+ MTK_HSDMA_RST_TX | MTK_HSDMA_RST_RX);
+
+ /* Setup HSDMA initial pointer in the ring */
+ mtk_dma_write(hsdma, MTK_HSDMA_TX_BASE, ring->tphys);
+ mtk_dma_write(hsdma, MTK_HSDMA_TX_CNT, MTK_DMA_SIZE);
+ mtk_dma_write(hsdma, MTK_HSDMA_TX_CPU, ring->cur_tptr);
+ mtk_dma_write(hsdma, MTK_HSDMA_TX_DMA, 0);
+ mtk_dma_write(hsdma, MTK_HSDMA_RX_BASE, ring->rphys);
+ mtk_dma_write(hsdma, MTK_HSDMA_RX_CNT, MTK_DMA_SIZE);
+ mtk_dma_write(hsdma, MTK_HSDMA_RX_CPU, ring->cur_rptr);
+ mtk_dma_write(hsdma, MTK_HSDMA_RX_DMA, 0);
+
+ /* Enable HSDMA */
+ mtk_dma_set(hsdma, MTK_HSDMA_GLO, MTK_HSDMA_GLO_DMA);
+
+ /* Setup delayed interrupt */
+ mtk_dma_write(hsdma, MTK_HSDMA_DLYINT, MTK_HSDMA_DLYINT_DEFAULT);
+
+ /* Enable interrupt */
+ mtk_dma_set(hsdma, MTK_HSDMA_INT_ENABLE, MTK_HSDMA_INT_RXDONE);
+
+ return 0;
+
+err_free_cb:
+ kfree(ring->cb);
+
+err_free_dma:
+ dma_free_coherent(hsdma2dev(hsdma),
+ pc->sz_ring, ring->txd, ring->tphys);
+ return err;
+}
+
+static void mtk_hsdma_free_pchan(struct mtk_hsdma_device *hsdma,
+ struct mtk_hsdma_pchan *pc)
+{
+ struct mtk_hsdma_ring *ring = &pc->ring;
+
+ /* Disable HSDMA and then wait for the completion */
+ mtk_dma_clr(hsdma, MTK_HSDMA_GLO, MTK_HSDMA_GLO_DMA);
+ mtk_hsdma_busy_wait(hsdma);
+
+ /* Reset pointer in the ring */
+ mtk_dma_clr(hsdma, MTK_HSDMA_INT_ENABLE, MTK_HSDMA_INT_RXDONE);
+ mtk_dma_write(hsdma, MTK_HSDMA_TX_BASE, 0);
+ mtk_dma_write(hsdma, MTK_HSDMA_TX_CNT, 0);
+ mtk_dma_write(hsdma, MTK_HSDMA_TX_CPU, 0);
+ mtk_dma_write(hsdma, MTK_HSDMA_RX_BASE, 0);
+ mtk_dma_write(hsdma, MTK_HSDMA_RX_CNT, 0);
+ mtk_dma_write(hsdma, MTK_HSDMA_RX_CPU, MTK_DMA_SIZE - 1);
+
+ kfree(ring->cb);
+
+ dma_free_coherent(hsdma2dev(hsdma),
+ pc->sz_ring, ring->txd, ring->tphys);
+}
+
+static int mtk_hsdma_issue_pending_vdesc(struct mtk_hsdma_device *hsdma,
+ struct mtk_hsdma_pchan *pc,
+ struct mtk_hsdma_vdesc *hvd)
+{
+ struct mtk_hsdma_ring *ring = &pc->ring;
+ struct mtk_hsdma_pdesc *txd, *rxd;
+ u16 reserved, prev, tlen, num_sgs;
+ unsigned long flags;
+
+ /* Protect against PC is accessed by multiple VCs simultaneously */
+ spin_lock_irqsave(&hsdma->lock, flags);
+
+ /*
+ * Reserve rooms, where pc->nr_free is used to track how many free
+ * rooms in the ring being updated in user and IRQ context.
+ */
+ num_sgs = DIV_ROUND_UP(hvd->len, MTK_HSDMA_MAX_LEN);
+ reserved = min_t(u16, num_sgs, atomic_read(&pc->nr_free));
+
+ if (!reserved) {
+ spin_unlock_irqrestore(&hsdma->lock, flags);
+ return -ENOSPC;
+ }
+
+ atomic_sub(reserved, &pc->nr_free);
+
+ while (reserved--) {
+ /* Limit size by PD capability for valid data moving */
+ tlen = (hvd->len > MTK_HSDMA_MAX_LEN) ?
+ MTK_HSDMA_MAX_LEN : hvd->len;
+
+ /*
+ * Setup PDs using the remaining VD info mapped on those
+ * reserved rooms. And since RXD is shared memory between the
+ * host and the device allocated by dma_alloc_coherent call,
+ * the helper macro WRITE_ONCE can ensure the data written to
+ * RAM would really happens.
+ */
+ txd = &ring->txd[ring->cur_tptr];
+ WRITE_ONCE(txd->desc1, hvd->src);
+ WRITE_ONCE(txd->desc2,
+ hsdma->soc->ls0 | MTK_HSDMA_DESC_PLEN(tlen));
+
+ rxd = &ring->rxd[ring->cur_tptr];
+ WRITE_ONCE(rxd->desc1, hvd->dest);
+ WRITE_ONCE(rxd->desc2, MTK_HSDMA_DESC_PLEN(tlen));
+
+ /* Associate VD, the PD belonged to */
+ ring->cb[ring->cur_tptr].vd = &hvd->vd;
+
+ /* Move forward the pointer of TX ring */
+ ring->cur_tptr = MTK_HSDMA_NEXT_DESP_IDX(ring->cur_tptr,
+ MTK_DMA_SIZE);
+
+ /* Update VD with remaining data */
+ hvd->src += tlen;
+ hvd->dest += tlen;
+ hvd->len -= tlen;
+ }
+
+ /*
+ * Tagging flag for the last PD for VD will be responsible for
+ * completing VD.
+ */
+ if (!hvd->len) {
+ prev = MTK_HSDMA_LAST_DESP_IDX(ring->cur_tptr, MTK_DMA_SIZE);
+ ring->cb[prev].flag = MTK_HSDMA_VDESC_FINISHED;
+ }
+
+ /* Ensure all changes indeed done before we're going on */
+ wmb();
+
+ /*
+ * Updating into hardware the pointer of TX ring lets HSDMA to take
+ * action for those pending PDs.
+ */
+ mtk_dma_write(hsdma, MTK_HSDMA_TX_CPU, ring->cur_tptr);
+
+ spin_unlock_irqrestore(&hsdma->lock, flags);
+
+ return 0;
+}
+
+static void mtk_hsdma_issue_vchan_pending(struct mtk_hsdma_device *hsdma,
+ struct mtk_hsdma_vchan *hvc)
+{
+ struct virt_dma_desc *vd, *vd2;
+ int err;
+
+ lockdep_assert_held(&hvc->vc.lock);
+
+ list_for_each_entry_safe(vd, vd2, &hvc->vc.desc_issued, node) {
+ struct mtk_hsdma_vdesc *hvd;
+
+ hvd = to_hsdma_vdesc(vd);
+
+ /* Map VD into PC and all VCs shares a single PC */
+ err = mtk_hsdma_issue_pending_vdesc(hsdma, hsdma->pc, hvd);
+
+ /*
+ * Move VD from desc_issued to desc_hw_processing when entire
+ * VD is fit into available PDs. Otherwise, the uncompleted
+ * VDs would stay in list desc_issued and then restart the
+ * processing as soon as possible once underlying ring space
+ * got freed.
+ */
+ if (err == -ENOSPC || hvd->len > 0)
+ break;
+
+ /*
+ * The extra list desc_hw_processing is used because
+ * hardware can't provide sufficient information allowing us
+ * to know what VDs are still working on the underlying ring.
+ * Through the additional list, it can help us to implement
+ * terminate_all, residue calculation and such thing needed
+ * to know detail descriptor status on the hardware.
+ */
+ list_move_tail(&vd->node, &hvc->desc_hw_processing);
+ }
+}
+
+static void mtk_hsdma_free_rooms_in_ring(struct mtk_hsdma_device *hsdma)
+{
+ struct mtk_hsdma_vchan *hvc;
+ struct mtk_hsdma_pdesc *rxd;
+ struct mtk_hsdma_vdesc *hvd;
+ struct mtk_hsdma_pchan *pc;
+ struct mtk_hsdma_cb *cb;
+ int i = MTK_DMA_SIZE;
+ __le32 desc2;
+ u32 status;
+ u16 next;
+
+ /* Read IRQ status */
+ status = mtk_dma_read(hsdma, MTK_HSDMA_INT_STATUS);
+ if (unlikely(!(status & MTK_HSDMA_INT_RXDONE)))
+ goto rx_done;
+
+ pc = hsdma->pc;
+
+ /*
+ * Using a fail-safe loop with iterations of up to MTK_DMA_SIZE to
+ * reclaim these finished descriptors: The most number of PDs the ISR
+ * can handle at one time shouldn't be more than MTK_DMA_SIZE so we
+ * take it as limited count instead of just using a dangerous infinite
+ * poll.
+ */
+ while (i--) {
+ next = MTK_HSDMA_NEXT_DESP_IDX(pc->ring.cur_rptr,
+ MTK_DMA_SIZE);
+ rxd = &pc->ring.rxd[next];
+
+ /*
+ * If MTK_HSDMA_DESC_DDONE is no specified, that means data
+ * moving for the PD is still under going.
+ */
+ desc2 = READ_ONCE(rxd->desc2);
+ if (!(desc2 & hsdma->soc->ddone))
+ break;
+
+ cb = &pc->ring.cb[next];
+ if (unlikely(!cb->vd)) {
+ dev_err(hsdma2dev(hsdma), "cb->vd cannot be null\n");
+ break;
+ }
+
+ /* Update residue of VD the associated PD belonged to */
+ hvd = to_hsdma_vdesc(cb->vd);
+ hvd->residue -= MTK_HSDMA_DESC_PLEN_GET(rxd->desc2);
+
+ /* Complete VD until the relevant last PD is finished */
+ if (IS_MTK_HSDMA_VDESC_FINISHED(cb->flag)) {
+ hvc = to_hsdma_vchan(cb->vd->tx.chan);
+
+ spin_lock(&hvc->vc.lock);
+
+ /* Remove VD from list desc_hw_processing */
+ list_del(&cb->vd->node);
+
+ /* Add VD into list desc_completed */
+ vchan_cookie_complete(cb->vd);
+
+ if (hvc->issue_synchronize &&
+ list_empty(&hvc->desc_hw_processing)) {
+ complete(&hvc->issue_completion);
+ hvc->issue_synchronize = false;
+ }
+ spin_unlock(&hvc->vc.lock);
+
+ cb->flag = 0;
+ }
+
+ cb->vd = 0;
+
+ /*
+ * Recycle the RXD with the helper WRITE_ONCE that can ensure
+ * data written into RAM would really happens.
+ */
+ WRITE_ONCE(rxd->desc1, 0);
+ WRITE_ONCE(rxd->desc2, 0);
+ pc->ring.cur_rptr = next;
+
+ /* Release rooms */
+ atomic_inc(&pc->nr_free);
+ }
+
+ /* Ensure all changes indeed done before we're going on */
+ wmb();
+
+ /* Update CPU pointer for those completed PDs */
+ mtk_dma_write(hsdma, MTK_HSDMA_RX_CPU, pc->ring.cur_rptr);
+
+ /*
+ * Acking the pending IRQ allows hardware no longer to keep the used
+ * IRQ line in certain trigger state when software has completed all
+ * the finished physical descriptors.
+ */
+ if (atomic_read(&pc->nr_free) >= MTK_DMA_SIZE - 1)
+ mtk_dma_write(hsdma, MTK_HSDMA_INT_STATUS, status);
+
+ /* ASAP handles pending VDs in all VCs after freeing some rooms */
+ for (i = 0; i < hsdma->dma_requests; i++) {
+ hvc = &hsdma->vc[i];
+ spin_lock(&hvc->vc.lock);
+ mtk_hsdma_issue_vchan_pending(hsdma, hvc);
+ spin_unlock(&hvc->vc.lock);
+ }
+
+rx_done:
+ /* All completed PDs are cleaned up, so enable interrupt again */
+ mtk_dma_set(hsdma, MTK_HSDMA_INT_ENABLE, MTK_HSDMA_INT_RXDONE);
+}
+
+static irqreturn_t mtk_hsdma_irq(int irq, void *devid)
+{
+ struct mtk_hsdma_device *hsdma = devid;
+
+ /*
+ * Disable interrupt until all completed PDs are cleaned up in
+ * mtk_hsdma_free_rooms call.
+ */
+ mtk_dma_clr(hsdma, MTK_HSDMA_INT_ENABLE, MTK_HSDMA_INT_RXDONE);
+
+ mtk_hsdma_free_rooms_in_ring(hsdma);
+
+ return IRQ_HANDLED;
+}
+
+static struct virt_dma_desc *mtk_hsdma_find_active_desc(struct dma_chan *c,
+ dma_cookie_t cookie)
+{
+ struct mtk_hsdma_vchan *hvc = to_hsdma_vchan(c);
+ struct virt_dma_desc *vd;
+
+ list_for_each_entry(vd, &hvc->desc_hw_processing, node)
+ if (vd->tx.cookie == cookie)
+ return vd;
+
+ list_for_each_entry(vd, &hvc->vc.desc_issued, node)
+ if (vd->tx.cookie == cookie)
+ return vd;
+
+ return NULL;
+}
+
+static enum dma_status mtk_hsdma_tx_status(struct dma_chan *c,
+ dma_cookie_t cookie,
+ struct dma_tx_state *txstate)
+{
+ struct mtk_hsdma_vchan *hvc = to_hsdma_vchan(c);
+ struct mtk_hsdma_vdesc *hvd;
+ struct virt_dma_desc *vd;
+ enum dma_status ret;
+ unsigned long flags;
+ size_t bytes = 0;
+
+ ret = dma_cookie_status(c, cookie, txstate);
+ if (ret == DMA_COMPLETE || !txstate)
+ return ret;
+
+ spin_lock_irqsave(&hvc->vc.lock, flags);
+ vd = mtk_hsdma_find_active_desc(c, cookie);
+ spin_unlock_irqrestore(&hvc->vc.lock, flags);
+
+ if (vd) {
+ hvd = to_hsdma_vdesc(vd);
+ bytes = hvd->residue;
+ }
+
+ dma_set_residue(txstate, bytes);
+
+ return ret;
+}
+
+static void mtk_hsdma_issue_pending(struct dma_chan *c)
+{
+ struct mtk_hsdma_device *hsdma = to_hsdma_dev(c);
+ struct mtk_hsdma_vchan *hvc = to_hsdma_vchan(c);
+ unsigned long flags;
+
+ spin_lock_irqsave(&hvc->vc.lock, flags);
+
+ if (vchan_issue_pending(&hvc->vc))
+ mtk_hsdma_issue_vchan_pending(hsdma, hvc);
+
+ spin_unlock_irqrestore(&hvc->vc.lock, flags);
+}
+
+static struct dma_async_tx_descriptor *
+mtk_hsdma_prep_dma_memcpy(struct dma_chan *c, dma_addr_t dest,
+ dma_addr_t src, size_t len, unsigned long flags)
+{
+ struct mtk_hsdma_vdesc *hvd;
+
+ hvd = kzalloc(sizeof(*hvd), GFP_NOWAIT);
+ if (!hvd)
+ return NULL;
+
+ hvd->len = len;
+ hvd->residue = len;
+ hvd->src = src;
+ hvd->dest = dest;
+
+ return vchan_tx_prep(to_virt_chan(c), &hvd->vd, flags);
+}
+
+static int mtk_hsdma_free_inactive_desc(struct dma_chan *c)
+{
+ struct virt_dma_chan *vc = to_virt_chan(c);
+ unsigned long flags;
+ LIST_HEAD(head);
+
+ spin_lock_irqsave(&vc->lock, flags);
+ list_splice_tail_init(&vc->desc_allocated, &head);
+ list_splice_tail_init(&vc->desc_submitted, &head);
+ list_splice_tail_init(&vc->desc_issued, &head);
+ spin_unlock_irqrestore(&vc->lock, flags);
+
+ /* At the point, we don't expect users put descriptor into VC again */
+ vchan_dma_desc_free_list(vc, &head);
+
+ return 0;
+}
+
+static void mtk_hsdma_free_active_desc(struct dma_chan *c)
+{
+ struct mtk_hsdma_vchan *hvc = to_hsdma_vchan(c);
+ bool sync_needed = false;
+
+ /*
+ * Once issue_synchronize is being set, which means once the hardware
+ * consumes all descriptors for the channel in the ring, the
+ * synchronization must be be notified immediately it is completed.
+ */
+ spin_lock(&hvc->vc.lock);
+ if (!list_empty(&hvc->desc_hw_processing)) {
+ hvc->issue_synchronize = true;
+ sync_needed = true;
+ }
+ spin_unlock(&hvc->vc.lock);
+
+ if (sync_needed)
+ wait_for_completion(&hvc->issue_completion);
+ /*
+ * At the point, we expect that all remaining descriptors in the ring
+ * for the channel should be all processing done.
+ */
+ WARN_ONCE(!list_empty(&hvc->desc_hw_processing),
+ "Desc pending still in list desc_hw_processing\n");
+
+ /* Free all descriptors in list desc_completed */
+ vchan_synchronize(&hvc->vc);
+
+ WARN_ONCE(!list_empty(&hvc->vc.desc_completed),
+ "Desc pending still in list desc_completed\n");
+}
+
+static int mtk_hsdma_terminate_all(struct dma_chan *c)
+{
+ /*
+ * Free pending descriptors not processed yet by hardware that have
+ * previously been submitted to the channel.
+ */
+ mtk_hsdma_free_inactive_desc(c);
+
+ /*
+ * However, the DMA engine doesn't provide any way to stop these
+ * descriptors being processed currently by hardware. The only way is
+ * to just waiting until these descriptors are all processed completely
+ * through mtk_hsdma_free_active_desc call.
+ */
+ mtk_hsdma_free_active_desc(c);
+
+ return 0;
+}
+
+static int mtk_hsdma_alloc_chan_resources(struct dma_chan *c)
+{
+ struct mtk_hsdma_device *hsdma = to_hsdma_dev(c);
+ int err;
+
+ /*
+ * Since HSDMA has only one PC, the resource for PC is being allocated
+ * when the first VC is being created and the other VCs would run on
+ * the same PC.
+ */
+ if (!refcount_read(&hsdma->pc_refcnt)) {
+ err = mtk_hsdma_alloc_pchan(hsdma, hsdma->pc);
+ if (err)
+ return err;
+ /*
+ * refcount_inc would complain increment on 0; use-after-free.
+ * Thus, we need to explicitly set it as 1 initially.
+ */
+ refcount_set(&hsdma->pc_refcnt, 1);
+ } else {
+ refcount_inc(&hsdma->pc_refcnt);
+ }
+
+ return 0;
+}
+
+static void mtk_hsdma_free_chan_resources(struct dma_chan *c)
+{
+ struct mtk_hsdma_device *hsdma = to_hsdma_dev(c);
+
+ /* Free all descriptors in all lists on the VC */
+ mtk_hsdma_terminate_all(c);
+
+ /* The resource for PC is not freed until all the VCs are destroyed */
+ if (!refcount_dec_and_test(&hsdma->pc_refcnt))
+ return;
+
+ mtk_hsdma_free_pchan(hsdma, hsdma->pc);
+}
+
+static int mtk_hsdma_hw_init(struct mtk_hsdma_device *hsdma)
+{
+ int err;
+
+ pm_runtime_enable(hsdma2dev(hsdma));
+ pm_runtime_get_sync(hsdma2dev(hsdma));
+
+ err = clk_prepare_enable(hsdma->clk);
+ if (err)
+ return err;
+
+ mtk_dma_write(hsdma, MTK_HSDMA_INT_ENABLE, 0);
+ mtk_dma_write(hsdma, MTK_HSDMA_GLO, MTK_HSDMA_GLO_DEFAULT);
+
+ return 0;
+}
+
+static int mtk_hsdma_hw_deinit(struct mtk_hsdma_device *hsdma)
+{
+ mtk_dma_write(hsdma, MTK_HSDMA_GLO, 0);
+
+ clk_disable_unprepare(hsdma->clk);
+
+ pm_runtime_put_sync(hsdma2dev(hsdma));
+ pm_runtime_disable(hsdma2dev(hsdma));
+
+ return 0;
+}
+
+static const struct mtk_hsdma_soc mt7623_soc = {
+ .ddone = BIT(31),
+ .ls0 = BIT(30),
+};
+
+static const struct mtk_hsdma_soc mt7622_soc = {
+ .ddone = BIT(15),
+ .ls0 = BIT(14),
+};
+
+static const struct of_device_id mtk_hsdma_match[] = {
+ { .compatible = "mediatek,mt7623-hsdma", .data = &mt7623_soc},
+ { .compatible = "mediatek,mt7622-hsdma", .data = &mt7622_soc},
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mtk_hsdma_match);
+
+static int mtk_hsdma_probe(struct platform_device *pdev)
+{
+ struct mtk_hsdma_device *hsdma;
+ struct mtk_hsdma_vchan *vc;
+ struct dma_device *dd;
+ struct resource *res;
+ int i, err;
+
+ hsdma = devm_kzalloc(&pdev->dev, sizeof(*hsdma), GFP_KERNEL);
+ if (!hsdma)
+ return -ENOMEM;
+
+ dd = &hsdma->ddev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hsdma->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(hsdma->base))
+ return PTR_ERR(hsdma->base);
+
+ hsdma->soc = of_device_get_match_data(&pdev->dev);
+ if (!hsdma->soc) {
+ dev_err(&pdev->dev, "No device match found\n");
+ return -ENODEV;
+ }
+
+ hsdma->clk = devm_clk_get(&pdev->dev, "hsdma");
+ if (IS_ERR(hsdma->clk)) {
+ dev_err(&pdev->dev, "No clock for %s\n",
+ dev_name(&pdev->dev));
+ return PTR_ERR(hsdma->clk);
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "No irq resource for %s\n",
+ dev_name(&pdev->dev));
+ return -EINVAL;
+ }
+ hsdma->irq = res->start;
+
+ refcount_set(&hsdma->pc_refcnt, 0);
+ spin_lock_init(&hsdma->lock);
+
+ dma_cap_set(DMA_MEMCPY, dd->cap_mask);
+
+ dd->copy_align = MTK_HSDMA_ALIGN_SIZE;
+ dd->device_alloc_chan_resources = mtk_hsdma_alloc_chan_resources;
+ dd->device_free_chan_resources = mtk_hsdma_free_chan_resources;
+ dd->device_tx_status = mtk_hsdma_tx_status;
+ dd->device_issue_pending = mtk_hsdma_issue_pending;
+ dd->device_prep_dma_memcpy = mtk_hsdma_prep_dma_memcpy;
+ dd->device_terminate_all = mtk_hsdma_terminate_all;
+ dd->src_addr_widths = MTK_HSDMA_DMA_BUSWIDTHS;
+ dd->dst_addr_widths = MTK_HSDMA_DMA_BUSWIDTHS;
+ dd->directions = BIT(DMA_MEM_TO_MEM);
+ dd->residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT;
+ dd->dev = &pdev->dev;
+ INIT_LIST_HEAD(&dd->channels);
+
+ hsdma->dma_requests = MTK_HSDMA_NR_VCHANS;
+ if (pdev->dev.of_node && of_property_read_u32(pdev->dev.of_node,
+ "dma-requests",
+ &hsdma->dma_requests)) {
+ dev_info(&pdev->dev,
+ "Using %u as missing dma-requests property\n",
+ MTK_HSDMA_NR_VCHANS);
+ }
+
+ hsdma->pc = devm_kcalloc(&pdev->dev, MTK_HSDMA_NR_MAX_PCHANS,
+ sizeof(*hsdma->pc), GFP_KERNEL);
+ if (!hsdma->pc)
+ return -ENOMEM;
+
+ hsdma->vc = devm_kcalloc(&pdev->dev, hsdma->dma_requests,
+ sizeof(*hsdma->vc), GFP_KERNEL);
+ if (!hsdma->vc)
+ return -ENOMEM;
+
+ for (i = 0; i < hsdma->dma_requests; i++) {
+ vc = &hsdma->vc[i];
+ vc->vc.desc_free = mtk_hsdma_vdesc_free;
+ vchan_init(&vc->vc, dd);
+ init_completion(&vc->issue_completion);
+ INIT_LIST_HEAD(&vc->desc_hw_processing);
+ }
+
+ err = dma_async_device_register(dd);
+ if (err)
+ return err;
+
+ err = of_dma_controller_register(pdev->dev.of_node,
+ of_dma_xlate_by_chan_id, hsdma);
+ if (err) {
+ dev_err(&pdev->dev,
+ "MediaTek HSDMA OF registration failed %d\n", err);
+ goto err_unregister;
+ }
+
+ mtk_hsdma_hw_init(hsdma);
+
+ err = devm_request_irq(&pdev->dev, hsdma->irq,
+ mtk_hsdma_irq, 0,
+ dev_name(&pdev->dev), hsdma);
+ if (err) {
+ dev_err(&pdev->dev,
+ "request_irq failed with err %d\n", err);
+ goto err_unregister;
+ }
+
+ platform_set_drvdata(pdev, hsdma);
+
+ dev_info(&pdev->dev, "MediaTek HSDMA driver registered\n");
+
+ return 0;
+
+err_unregister:
+ dma_async_device_unregister(dd);
+
+ return err;
+}
+
+static int mtk_hsdma_remove(struct platform_device *pdev)
+{
+ struct mtk_hsdma_device *hsdma = platform_get_drvdata(pdev);
+ struct mtk_hsdma_vchan *vc;
+ int i;
+
+ /* Kill VC task */
+ for (i = 0; i < hsdma->dma_requests; i++) {
+ vc = &hsdma->vc[i];
+
+ list_del(&vc->vc.chan.device_node);
+ tasklet_kill(&vc->vc.task);
+ }
+
+ /* Disable DMA interrupt */
+ mtk_dma_write(hsdma, MTK_HSDMA_INT_ENABLE, 0);
+
+ /* Waits for any pending IRQ handlers to complete */
+ synchronize_irq(hsdma->irq);
+
+ /* Disable hardware */
+ mtk_hsdma_hw_deinit(hsdma);
+
+ dma_async_device_unregister(&hsdma->ddev);
+ of_dma_controller_free(pdev->dev.of_node);
+
+ return 0;
+}
+
+static struct platform_driver mtk_hsdma_driver = {
+ .probe = mtk_hsdma_probe,
+ .remove = mtk_hsdma_remove,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = mtk_hsdma_match,
+ },
+};
+module_platform_driver(mtk_hsdma_driver);
+
+MODULE_DESCRIPTION("MediaTek High-Speed DMA Controller Driver");
+MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index d7327fd5f445..de1fd59fe136 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -1510,7 +1510,7 @@ static void pl330_dotask(unsigned long data)
/* Returns 1 if state was updated, 0 otherwise */
static int pl330_update(struct pl330_dmac *pl330)
{
- struct dma_pl330_desc *descdone, *tmp;
+ struct dma_pl330_desc *descdone;
unsigned long flags;
void __iomem *regs;
u32 val;
@@ -1588,7 +1588,9 @@ static int pl330_update(struct pl330_dmac *pl330)
}
/* Now that we are in no hurry, do the callbacks */
- list_for_each_entry_safe(descdone, tmp, &pl330->req_done, rqd) {
+ while (!list_empty(&pl330->req_done)) {
+ descdone = list_first_entry(&pl330->req_done,
+ struct dma_pl330_desc, rqd);
list_del(&descdone->rqd);
spin_unlock_irqrestore(&pl330->lock, flags);
dma_pl330_rqcb(descdone, PL330_ERR_NONE);
diff --git a/drivers/dma/qcom/bam_dma.c b/drivers/dma/qcom/bam_dma.c
index d076940e0c69..d29275b97e84 100644
--- a/drivers/dma/qcom/bam_dma.c
+++ b/drivers/dma/qcom/bam_dma.c
@@ -393,6 +393,7 @@ struct bam_device {
struct device_dma_parameters dma_parms;
struct bam_chan *channels;
u32 num_channels;
+ u32 num_ees;
/* execution environment ID, from DT */
u32 ee;
@@ -934,12 +935,15 @@ static void bam_apply_new_config(struct bam_chan *bchan,
struct bam_device *bdev = bchan->bdev;
u32 maxburst;
- if (dir == DMA_DEV_TO_MEM)
- maxburst = bchan->slave.src_maxburst;
- else
- maxburst = bchan->slave.dst_maxburst;
+ if (!bdev->controlled_remotely) {
+ if (dir == DMA_DEV_TO_MEM)
+ maxburst = bchan->slave.src_maxburst;
+ else
+ maxburst = bchan->slave.dst_maxburst;
- writel_relaxed(maxburst, bam_addr(bdev, 0, BAM_DESC_CNT_TRSHLD));
+ writel_relaxed(maxburst,
+ bam_addr(bdev, 0, BAM_DESC_CNT_TRSHLD));
+ }
bchan->reconfigure = 0;
}
@@ -1128,15 +1132,19 @@ static int bam_init(struct bam_device *bdev)
u32 val;
/* read revision and configuration information */
- val = readl_relaxed(bam_addr(bdev, 0, BAM_REVISION)) >> NUM_EES_SHIFT;
- val &= NUM_EES_MASK;
+ if (!bdev->num_ees) {
+ val = readl_relaxed(bam_addr(bdev, 0, BAM_REVISION));
+ bdev->num_ees = (val >> NUM_EES_SHIFT) & NUM_EES_MASK;
+ }
/* check that configured EE is within range */
- if (bdev->ee >= val)
+ if (bdev->ee >= bdev->num_ees)
return -EINVAL;
- val = readl_relaxed(bam_addr(bdev, 0, BAM_NUM_PIPES));
- bdev->num_channels = val & BAM_NUM_PIPES_MASK;
+ if (!bdev->num_channels) {
+ val = readl_relaxed(bam_addr(bdev, 0, BAM_NUM_PIPES));
+ bdev->num_channels = val & BAM_NUM_PIPES_MASK;
+ }
if (bdev->controlled_remotely)
return 0;
@@ -1232,9 +1240,25 @@ static int bam_dma_probe(struct platform_device *pdev)
bdev->controlled_remotely = of_property_read_bool(pdev->dev.of_node,
"qcom,controlled-remotely");
+ if (bdev->controlled_remotely) {
+ ret = of_property_read_u32(pdev->dev.of_node, "num-channels",
+ &bdev->num_channels);
+ if (ret)
+ dev_err(bdev->dev, "num-channels unspecified in dt\n");
+
+ ret = of_property_read_u32(pdev->dev.of_node, "qcom,num-ees",
+ &bdev->num_ees);
+ if (ret)
+ dev_err(bdev->dev, "num-ees unspecified in dt\n");
+ }
+
bdev->bamclk = devm_clk_get(bdev->dev, "bam_clk");
- if (IS_ERR(bdev->bamclk))
- return PTR_ERR(bdev->bamclk);
+ if (IS_ERR(bdev->bamclk)) {
+ if (!bdev->controlled_remotely)
+ return PTR_ERR(bdev->bamclk);
+
+ bdev->bamclk = NULL;
+ }
ret = clk_prepare_enable(bdev->bamclk);
if (ret) {
@@ -1309,6 +1333,11 @@ static int bam_dma_probe(struct platform_device *pdev)
if (ret)
goto err_unregister_dma;
+ if (bdev->controlled_remotely) {
+ pm_runtime_disable(&pdev->dev);
+ return 0;
+ }
+
pm_runtime_irq_safe(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev, BAM_DMA_AUTOSUSPEND_DELAY);
pm_runtime_use_autosuspend(&pdev->dev);
@@ -1392,7 +1421,8 @@ static int __maybe_unused bam_dma_suspend(struct device *dev)
{
struct bam_device *bdev = dev_get_drvdata(dev);
- pm_runtime_force_suspend(dev);
+ if (!bdev->controlled_remotely)
+ pm_runtime_force_suspend(dev);
clk_unprepare(bdev->bamclk);
@@ -1408,7 +1438,8 @@ static int __maybe_unused bam_dma_resume(struct device *dev)
if (ret)
return ret;
- pm_runtime_force_resume(dev);
+ if (!bdev->controlled_remotely)
+ pm_runtime_force_resume(dev);
return 0;
}
diff --git a/drivers/dma/sh/rcar-dmac.c b/drivers/dma/sh/rcar-dmac.c
index d0cacdb0713e..2a2ccd9c78e4 100644
--- a/drivers/dma/sh/rcar-dmac.c
+++ b/drivers/dma/sh/rcar-dmac.c
@@ -1301,8 +1301,17 @@ static unsigned int rcar_dmac_chan_get_residue(struct rcar_dmac_chan *chan,
* If the cookie doesn't correspond to the currently running transfer
* then the descriptor hasn't been processed yet, and the residue is
* equal to the full descriptor size.
+ * Also, a client driver is possible to call this function before
+ * rcar_dmac_isr_channel_thread() runs. In this case, the "desc.running"
+ * will be the next descriptor, and the done list will appear. So, if
+ * the argument cookie matches the done list's cookie, we can assume
+ * the residue is zero.
*/
if (cookie != desc->async_tx.cookie) {
+ list_for_each_entry(desc, &chan->desc.done, node) {
+ if (cookie == desc->async_tx.cookie)
+ return 0;
+ }
list_for_each_entry(desc, &chan->desc.pending, node) {
if (cookie == desc->async_tx.cookie)
return desc->size;
@@ -1677,8 +1686,8 @@ static const struct dev_pm_ops rcar_dmac_pm = {
* - Wait for the current transfer to complete and stop the device,
* - Resume transfers, if any.
*/
- SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(rcar_dmac_runtime_suspend, rcar_dmac_runtime_resume,
NULL)
};
diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c
index 786fc8fcc38e..8c5807362a25 100644
--- a/drivers/dma/stm32-dma.c
+++ b/drivers/dma/stm32-dma.c
@@ -5,6 +5,7 @@
*
* Copyright (C) M'boumba Cedric Madianga 2015
* Author: M'boumba Cedric Madianga <cedric.madianga@gmail.com>
+ * Pierre-Yves Mordret <pierre-yves.mordret@st.com>
*
* License terms: GNU General Public License (GPL), version 2
*/
@@ -33,9 +34,14 @@
#define STM32_DMA_LIFCR 0x0008 /* DMA Low Int Flag Clear Reg */
#define STM32_DMA_HIFCR 0x000c /* DMA High Int Flag Clear Reg */
#define STM32_DMA_TCI BIT(5) /* Transfer Complete Interrupt */
+#define STM32_DMA_HTI BIT(4) /* Half Transfer Interrupt */
#define STM32_DMA_TEI BIT(3) /* Transfer Error Interrupt */
#define STM32_DMA_DMEI BIT(2) /* Direct Mode Error Interrupt */
#define STM32_DMA_FEI BIT(0) /* FIFO Error Interrupt */
+#define STM32_DMA_MASKI (STM32_DMA_TCI \
+ | STM32_DMA_TEI \
+ | STM32_DMA_DMEI \
+ | STM32_DMA_FEI)
/* DMA Stream x Configuration Register */
#define STM32_DMA_SCR(x) (0x0010 + 0x18 * (x)) /* x = 0..7 */
@@ -60,7 +66,8 @@
#define STM32_DMA_SCR_PINC BIT(9) /* Peripheral increment mode */
#define STM32_DMA_SCR_CIRC BIT(8) /* Circular mode */
#define STM32_DMA_SCR_PFCTRL BIT(5) /* Peripheral Flow Controller */
-#define STM32_DMA_SCR_TCIE BIT(4) /* Transfer Cplete Int Enable*/
+#define STM32_DMA_SCR_TCIE BIT(4) /* Transfer Complete Int Enable
+ */
#define STM32_DMA_SCR_TEIE BIT(2) /* Transfer Error Int Enable */
#define STM32_DMA_SCR_DMEIE BIT(1) /* Direct Mode Err Int Enable */
#define STM32_DMA_SCR_EN BIT(0) /* Stream Enable */
@@ -111,11 +118,24 @@
#define STM32_DMA_FIFO_THRESHOLD_FULL 0x03
#define STM32_DMA_MAX_DATA_ITEMS 0xffff
+/*
+ * Valid transfer starts from @0 to @0xFFFE leading to unaligned scatter
+ * gather at boundary. Thus it's safer to round down this value on FIFO
+ * size (16 Bytes)
+ */
+#define STM32_DMA_ALIGNED_MAX_DATA_ITEMS \
+ ALIGN_DOWN(STM32_DMA_MAX_DATA_ITEMS, 16)
#define STM32_DMA_MAX_CHANNELS 0x08
#define STM32_DMA_MAX_REQUEST_ID 0x08
#define STM32_DMA_MAX_DATA_PARAM 0x03
+#define STM32_DMA_FIFO_SIZE 16 /* FIFO is 16 bytes */
+#define STM32_DMA_MIN_BURST 4
#define STM32_DMA_MAX_BURST 16
+/* DMA Features */
+#define STM32_DMA_THRESHOLD_FTR_MASK GENMASK(1, 0)
+#define STM32_DMA_THRESHOLD_FTR_GET(n) ((n) & STM32_DMA_THRESHOLD_FTR_MASK)
+
enum stm32_dma_width {
STM32_DMA_BYTE,
STM32_DMA_HALF_WORD,
@@ -129,11 +149,18 @@ enum stm32_dma_burst_size {
STM32_DMA_BURST_INCR16,
};
+/**
+ * struct stm32_dma_cfg - STM32 DMA custom configuration
+ * @channel_id: channel ID
+ * @request_line: DMA request
+ * @stream_config: 32bit mask specifying the DMA channel configuration
+ * @features: 32bit mask specifying the DMA Feature list
+ */
struct stm32_dma_cfg {
u32 channel_id;
u32 request_line;
u32 stream_config;
- u32 threshold;
+ u32 features;
};
struct stm32_dma_chan_reg {
@@ -171,6 +198,9 @@ struct stm32_dma_chan {
u32 next_sg;
struct dma_slave_config dma_sconfig;
struct stm32_dma_chan_reg chan_reg;
+ u32 threshold;
+ u32 mem_burst;
+ u32 mem_width;
};
struct stm32_dma_device {
@@ -235,6 +265,85 @@ static int stm32_dma_get_width(struct stm32_dma_chan *chan,
}
}
+static enum dma_slave_buswidth stm32_dma_get_max_width(u32 buf_len,
+ u32 threshold)
+{
+ enum dma_slave_buswidth max_width;
+
+ if (threshold == STM32_DMA_FIFO_THRESHOLD_FULL)
+ max_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ else
+ max_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+
+ while ((buf_len < max_width || buf_len % max_width) &&
+ max_width > DMA_SLAVE_BUSWIDTH_1_BYTE)
+ max_width = max_width >> 1;
+
+ return max_width;
+}
+
+static bool stm32_dma_fifo_threshold_is_allowed(u32 burst, u32 threshold,
+ enum dma_slave_buswidth width)
+{
+ u32 remaining;
+
+ if (width != DMA_SLAVE_BUSWIDTH_UNDEFINED) {
+ if (burst != 0) {
+ /*
+ * If number of beats fit in several whole bursts
+ * this configuration is allowed.
+ */
+ remaining = ((STM32_DMA_FIFO_SIZE / width) *
+ (threshold + 1) / 4) % burst;
+
+ if (remaining == 0)
+ return true;
+ } else {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool stm32_dma_is_burst_possible(u32 buf_len, u32 threshold)
+{
+ switch (threshold) {
+ case STM32_DMA_FIFO_THRESHOLD_FULL:
+ if (buf_len >= STM32_DMA_MAX_BURST)
+ return true;
+ else
+ return false;
+ case STM32_DMA_FIFO_THRESHOLD_HALFFULL:
+ if (buf_len >= STM32_DMA_MAX_BURST / 2)
+ return true;
+ else
+ return false;
+ default:
+ return false;
+ }
+}
+
+static u32 stm32_dma_get_best_burst(u32 buf_len, u32 max_burst, u32 threshold,
+ enum dma_slave_buswidth width)
+{
+ u32 best_burst = max_burst;
+
+ if (best_burst == 1 || !stm32_dma_is_burst_possible(buf_len, threshold))
+ return 0;
+
+ while ((buf_len < best_burst * width && best_burst > 1) ||
+ !stm32_dma_fifo_threshold_is_allowed(best_burst, threshold,
+ width)) {
+ if (best_burst > STM32_DMA_MIN_BURST)
+ best_burst = best_burst >> 1;
+ else
+ best_burst = 0;
+ }
+
+ return best_burst;
+}
+
static int stm32_dma_get_burst(struct stm32_dma_chan *chan, u32 maxburst)
{
switch (maxburst) {
@@ -254,12 +363,12 @@ static int stm32_dma_get_burst(struct stm32_dma_chan *chan, u32 maxburst)
}
static void stm32_dma_set_fifo_config(struct stm32_dma_chan *chan,
- u32 src_maxburst, u32 dst_maxburst)
+ u32 src_burst, u32 dst_burst)
{
chan->chan_reg.dma_sfcr &= ~STM32_DMA_SFCR_MASK;
chan->chan_reg.dma_scr &= ~STM32_DMA_SCR_DMEIE;
- if ((!src_maxburst) && (!dst_maxburst)) {
+ if (!src_burst && !dst_burst) {
/* Using direct mode */
chan->chan_reg.dma_scr |= STM32_DMA_SCR_DMEIE;
} else {
@@ -300,7 +409,7 @@ static u32 stm32_dma_irq_status(struct stm32_dma_chan *chan)
flags = dma_isr >> (((chan->id & 2) << 3) | ((chan->id & 1) * 6));
- return flags;
+ return flags & STM32_DMA_MASKI;
}
static void stm32_dma_irq_clear(struct stm32_dma_chan *chan, u32 flags)
@@ -315,6 +424,7 @@ static void stm32_dma_irq_clear(struct stm32_dma_chan *chan, u32 flags)
* If (ch % 4) is 2 or 3, left shift the mask by 16 bits.
* If (ch % 4) is 1 or 3, additionally left shift the mask by 6 bits.
*/
+ flags &= STM32_DMA_MASKI;
dma_ifcr = flags << (((chan->id & 2) << 3) | ((chan->id & 1) * 6));
if (chan->id & 4)
@@ -429,6 +539,8 @@ static void stm32_dma_dump_reg(struct stm32_dma_chan *chan)
dev_dbg(chan2dev(chan), "SFCR: 0x%08x\n", sfcr);
}
+static void stm32_dma_configure_next_sg(struct stm32_dma_chan *chan);
+
static void stm32_dma_start_transfer(struct stm32_dma_chan *chan)
{
struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan);
@@ -471,6 +583,9 @@ static void stm32_dma_start_transfer(struct stm32_dma_chan *chan)
if (status)
stm32_dma_irq_clear(chan, status);
+ if (chan->desc->cyclic)
+ stm32_dma_configure_next_sg(chan);
+
stm32_dma_dump_reg(chan);
/* Start DMA */
@@ -541,13 +656,29 @@ static irqreturn_t stm32_dma_chan_irq(int irq, void *devid)
status = stm32_dma_irq_status(chan);
scr = stm32_dma_read(dmadev, STM32_DMA_SCR(chan->id));
- if ((status & STM32_DMA_TCI) && (scr & STM32_DMA_SCR_TCIE)) {
+ if (status & STM32_DMA_TCI) {
stm32_dma_irq_clear(chan, STM32_DMA_TCI);
- stm32_dma_handle_chan_done(chan);
-
- } else {
+ if (scr & STM32_DMA_SCR_TCIE)
+ stm32_dma_handle_chan_done(chan);
+ status &= ~STM32_DMA_TCI;
+ }
+ if (status & STM32_DMA_HTI) {
+ stm32_dma_irq_clear(chan, STM32_DMA_HTI);
+ status &= ~STM32_DMA_HTI;
+ }
+ if (status & STM32_DMA_FEI) {
+ stm32_dma_irq_clear(chan, STM32_DMA_FEI);
+ status &= ~STM32_DMA_FEI;
+ if (!(scr & STM32_DMA_SCR_EN))
+ dev_err(chan2dev(chan), "FIFO Error\n");
+ else
+ dev_dbg(chan2dev(chan), "FIFO over/underrun\n");
+ }
+ if (status) {
stm32_dma_irq_clear(chan, status);
dev_err(chan2dev(chan), "DMA error: status=0x%08x\n", status);
+ if (!(scr & STM32_DMA_SCR_EN))
+ dev_err(chan2dev(chan), "chan disabled by HW\n");
}
spin_unlock(&chan->vchan.lock);
@@ -564,45 +695,59 @@ static void stm32_dma_issue_pending(struct dma_chan *c)
if (vchan_issue_pending(&chan->vchan) && !chan->desc && !chan->busy) {
dev_dbg(chan2dev(chan), "vchan %p: issued\n", &chan->vchan);
stm32_dma_start_transfer(chan);
- if (chan->desc->cyclic)
- stm32_dma_configure_next_sg(chan);
+
}
spin_unlock_irqrestore(&chan->vchan.lock, flags);
}
static int stm32_dma_set_xfer_param(struct stm32_dma_chan *chan,
enum dma_transfer_direction direction,
- enum dma_slave_buswidth *buswidth)
+ enum dma_slave_buswidth *buswidth,
+ u32 buf_len)
{
enum dma_slave_buswidth src_addr_width, dst_addr_width;
int src_bus_width, dst_bus_width;
int src_burst_size, dst_burst_size;
- u32 src_maxburst, dst_maxburst;
- u32 dma_scr = 0;
+ u32 src_maxburst, dst_maxburst, src_best_burst, dst_best_burst;
+ u32 dma_scr, threshold;
src_addr_width = chan->dma_sconfig.src_addr_width;
dst_addr_width = chan->dma_sconfig.dst_addr_width;
src_maxburst = chan->dma_sconfig.src_maxburst;
dst_maxburst = chan->dma_sconfig.dst_maxburst;
+ threshold = chan->threshold;
switch (direction) {
case DMA_MEM_TO_DEV:
+ /* Set device data size */
dst_bus_width = stm32_dma_get_width(chan, dst_addr_width);
if (dst_bus_width < 0)
return dst_bus_width;
- dst_burst_size = stm32_dma_get_burst(chan, dst_maxburst);
+ /* Set device burst size */
+ dst_best_burst = stm32_dma_get_best_burst(buf_len,
+ dst_maxburst,
+ threshold,
+ dst_addr_width);
+
+ dst_burst_size = stm32_dma_get_burst(chan, dst_best_burst);
if (dst_burst_size < 0)
return dst_burst_size;
- if (!src_addr_width)
- src_addr_width = dst_addr_width;
-
+ /* Set memory data size */
+ src_addr_width = stm32_dma_get_max_width(buf_len, threshold);
+ chan->mem_width = src_addr_width;
src_bus_width = stm32_dma_get_width(chan, src_addr_width);
if (src_bus_width < 0)
return src_bus_width;
- src_burst_size = stm32_dma_get_burst(chan, src_maxburst);
+ /* Set memory burst size */
+ src_maxburst = STM32_DMA_MAX_BURST;
+ src_best_burst = stm32_dma_get_best_burst(buf_len,
+ src_maxburst,
+ threshold,
+ src_addr_width);
+ src_burst_size = stm32_dma_get_burst(chan, src_best_burst);
if (src_burst_size < 0)
return src_burst_size;
@@ -612,27 +757,46 @@ static int stm32_dma_set_xfer_param(struct stm32_dma_chan *chan,
STM32_DMA_SCR_PBURST(dst_burst_size) |
STM32_DMA_SCR_MBURST(src_burst_size);
+ /* Set FIFO threshold */
+ chan->chan_reg.dma_sfcr &= ~STM32_DMA_SFCR_FTH_MASK;
+ chan->chan_reg.dma_sfcr |= STM32_DMA_SFCR_FTH(threshold);
+
+ /* Set peripheral address */
chan->chan_reg.dma_spar = chan->dma_sconfig.dst_addr;
*buswidth = dst_addr_width;
break;
case DMA_DEV_TO_MEM:
+ /* Set device data size */
src_bus_width = stm32_dma_get_width(chan, src_addr_width);
if (src_bus_width < 0)
return src_bus_width;
- src_burst_size = stm32_dma_get_burst(chan, src_maxburst);
+ /* Set device burst size */
+ src_best_burst = stm32_dma_get_best_burst(buf_len,
+ src_maxburst,
+ threshold,
+ src_addr_width);
+ chan->mem_burst = src_best_burst;
+ src_burst_size = stm32_dma_get_burst(chan, src_best_burst);
if (src_burst_size < 0)
return src_burst_size;
- if (!dst_addr_width)
- dst_addr_width = src_addr_width;
-
+ /* Set memory data size */
+ dst_addr_width = stm32_dma_get_max_width(buf_len, threshold);
+ chan->mem_width = dst_addr_width;
dst_bus_width = stm32_dma_get_width(chan, dst_addr_width);
if (dst_bus_width < 0)
return dst_bus_width;
- dst_burst_size = stm32_dma_get_burst(chan, dst_maxburst);
+ /* Set memory burst size */
+ dst_maxburst = STM32_DMA_MAX_BURST;
+ dst_best_burst = stm32_dma_get_best_burst(buf_len,
+ dst_maxburst,
+ threshold,
+ dst_addr_width);
+ chan->mem_burst = dst_best_burst;
+ dst_burst_size = stm32_dma_get_burst(chan, dst_best_burst);
if (dst_burst_size < 0)
return dst_burst_size;
@@ -642,6 +806,11 @@ static int stm32_dma_set_xfer_param(struct stm32_dma_chan *chan,
STM32_DMA_SCR_PBURST(src_burst_size) |
STM32_DMA_SCR_MBURST(dst_burst_size);
+ /* Set FIFO threshold */
+ chan->chan_reg.dma_sfcr &= ~STM32_DMA_SFCR_FTH_MASK;
+ chan->chan_reg.dma_sfcr |= STM32_DMA_SFCR_FTH(threshold);
+
+ /* Set peripheral address */
chan->chan_reg.dma_spar = chan->dma_sconfig.src_addr;
*buswidth = chan->dma_sconfig.src_addr_width;
break;
@@ -651,8 +820,9 @@ static int stm32_dma_set_xfer_param(struct stm32_dma_chan *chan,
return -EINVAL;
}
- stm32_dma_set_fifo_config(chan, src_maxburst, dst_maxburst);
+ stm32_dma_set_fifo_config(chan, src_best_burst, dst_best_burst);
+ /* Set DMA control register */
chan->chan_reg.dma_scr &= ~(STM32_DMA_SCR_DIR_MASK |
STM32_DMA_SCR_PSIZE_MASK | STM32_DMA_SCR_MSIZE_MASK |
STM32_DMA_SCR_PBURST_MASK | STM32_DMA_SCR_MBURST_MASK);
@@ -692,10 +862,6 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_slave_sg(
if (!desc)
return NULL;
- ret = stm32_dma_set_xfer_param(chan, direction, &buswidth);
- if (ret < 0)
- goto err;
-
/* Set peripheral flow controller */
if (chan->dma_sconfig.device_fc)
chan->chan_reg.dma_scr |= STM32_DMA_SCR_PFCTRL;
@@ -703,10 +869,15 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_slave_sg(
chan->chan_reg.dma_scr &= ~STM32_DMA_SCR_PFCTRL;
for_each_sg(sgl, sg, sg_len, i) {
+ ret = stm32_dma_set_xfer_param(chan, direction, &buswidth,
+ sg_dma_len(sg));
+ if (ret < 0)
+ goto err;
+
desc->sg_req[i].len = sg_dma_len(sg);
nb_data_items = desc->sg_req[i].len / buswidth;
- if (nb_data_items > STM32_DMA_MAX_DATA_ITEMS) {
+ if (nb_data_items > STM32_DMA_ALIGNED_MAX_DATA_ITEMS) {
dev_err(chan2dev(chan), "nb items not supported\n");
goto err;
}
@@ -767,12 +938,12 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_dma_cyclic(
return NULL;
}
- ret = stm32_dma_set_xfer_param(chan, direction, &buswidth);
+ ret = stm32_dma_set_xfer_param(chan, direction, &buswidth, period_len);
if (ret < 0)
return NULL;
nb_data_items = period_len / buswidth;
- if (nb_data_items > STM32_DMA_MAX_DATA_ITEMS) {
+ if (nb_data_items > STM32_DMA_ALIGNED_MAX_DATA_ITEMS) {
dev_err(chan2dev(chan), "number of items not supported\n");
return NULL;
}
@@ -816,35 +987,45 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_dma_memcpy(
dma_addr_t src, size_t len, unsigned long flags)
{
struct stm32_dma_chan *chan = to_stm32_dma_chan(c);
- u32 num_sgs;
+ enum dma_slave_buswidth max_width;
struct stm32_dma_desc *desc;
size_t xfer_count, offset;
+ u32 num_sgs, best_burst, dma_burst, threshold;
int i;
- num_sgs = DIV_ROUND_UP(len, STM32_DMA_MAX_DATA_ITEMS);
+ num_sgs = DIV_ROUND_UP(len, STM32_DMA_ALIGNED_MAX_DATA_ITEMS);
desc = stm32_dma_alloc_desc(num_sgs);
if (!desc)
return NULL;
+ threshold = chan->threshold;
+
for (offset = 0, i = 0; offset < len; offset += xfer_count, i++) {
xfer_count = min_t(size_t, len - offset,
- STM32_DMA_MAX_DATA_ITEMS);
+ STM32_DMA_ALIGNED_MAX_DATA_ITEMS);
- desc->sg_req[i].len = xfer_count;
+ /* Compute best burst size */
+ max_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ best_burst = stm32_dma_get_best_burst(len, STM32_DMA_MAX_BURST,
+ threshold, max_width);
+ dma_burst = stm32_dma_get_burst(chan, best_burst);
stm32_dma_clear_reg(&desc->sg_req[i].chan_reg);
desc->sg_req[i].chan_reg.dma_scr =
STM32_DMA_SCR_DIR(STM32_DMA_MEM_TO_MEM) |
+ STM32_DMA_SCR_PBURST(dma_burst) |
+ STM32_DMA_SCR_MBURST(dma_burst) |
STM32_DMA_SCR_MINC |
STM32_DMA_SCR_PINC |
STM32_DMA_SCR_TCIE |
STM32_DMA_SCR_TEIE;
- desc->sg_req[i].chan_reg.dma_sfcr = STM32_DMA_SFCR_DMDIS |
- STM32_DMA_SFCR_FTH(STM32_DMA_FIFO_THRESHOLD_FULL) |
- STM32_DMA_SFCR_FEIE;
+ desc->sg_req[i].chan_reg.dma_sfcr |= STM32_DMA_SFCR_MASK;
+ desc->sg_req[i].chan_reg.dma_sfcr |=
+ STM32_DMA_SFCR_FTH(threshold);
desc->sg_req[i].chan_reg.dma_spar = src + offset;
desc->sg_req[i].chan_reg.dma_sm0ar = dest + offset;
desc->sg_req[i].chan_reg.dma_sndtr = xfer_count;
+ desc->sg_req[i].len = xfer_count;
}
desc->num_sgs = num_sgs;
@@ -869,6 +1050,7 @@ static size_t stm32_dma_desc_residue(struct stm32_dma_chan *chan,
struct stm32_dma_desc *desc,
u32 next_sg)
{
+ u32 modulo, burst_size;
u32 residue = 0;
int i;
@@ -876,8 +1058,10 @@ static size_t stm32_dma_desc_residue(struct stm32_dma_chan *chan,
* In cyclic mode, for the last period, residue = remaining bytes from
* NDTR
*/
- if (chan->desc->cyclic && next_sg == 0)
- return stm32_dma_get_remaining_bytes(chan);
+ if (chan->desc->cyclic && next_sg == 0) {
+ residue = stm32_dma_get_remaining_bytes(chan);
+ goto end;
+ }
/*
* For all other periods in cyclic mode, and in sg mode,
@@ -888,6 +1072,15 @@ static size_t stm32_dma_desc_residue(struct stm32_dma_chan *chan,
residue += desc->sg_req[i].len;
residue += stm32_dma_get_remaining_bytes(chan);
+end:
+ if (!chan->mem_burst)
+ return residue;
+
+ burst_size = chan->mem_burst * chan->mem_width;
+ modulo = residue % burst_size;
+ if (modulo)
+ residue = residue - modulo + burst_size;
+
return residue;
}
@@ -902,7 +1095,7 @@ static enum dma_status stm32_dma_tx_status(struct dma_chan *c,
u32 residue = 0;
status = dma_cookie_status(c, cookie, state);
- if ((status == DMA_COMPLETE) || (!state))
+ if (status == DMA_COMPLETE || !state)
return status;
spin_lock_irqsave(&chan->vchan.lock, flags);
@@ -966,7 +1159,7 @@ static void stm32_dma_desc_free(struct virt_dma_desc *vdesc)
}
static void stm32_dma_set_config(struct stm32_dma_chan *chan,
- struct stm32_dma_cfg *cfg)
+ struct stm32_dma_cfg *cfg)
{
stm32_dma_clear_reg(&chan->chan_reg);
@@ -976,7 +1169,7 @@ static void stm32_dma_set_config(struct stm32_dma_chan *chan,
/* Enable Interrupts */
chan->chan_reg.dma_scr |= STM32_DMA_SCR_TEIE | STM32_DMA_SCR_TCIE;
- chan->chan_reg.dma_sfcr = cfg->threshold & STM32_DMA_SFCR_FTH_MASK;
+ chan->threshold = STM32_DMA_THRESHOLD_FTR_GET(cfg->features);
}
static struct dma_chan *stm32_dma_of_xlate(struct of_phandle_args *dma_spec,
@@ -996,10 +1189,10 @@ static struct dma_chan *stm32_dma_of_xlate(struct of_phandle_args *dma_spec,
cfg.channel_id = dma_spec->args[0];
cfg.request_line = dma_spec->args[1];
cfg.stream_config = dma_spec->args[2];
- cfg.threshold = dma_spec->args[3];
+ cfg.features = dma_spec->args[3];
- if ((cfg.channel_id >= STM32_DMA_MAX_CHANNELS) ||
- (cfg.request_line >= STM32_DMA_MAX_REQUEST_ID)) {
+ if (cfg.channel_id >= STM32_DMA_MAX_CHANNELS ||
+ cfg.request_line >= STM32_DMA_MAX_REQUEST_ID) {
dev_err(dev, "Bad channel and/or request id\n");
return NULL;
}
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index 3c4017007647..da2da53bca6d 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -232,9 +232,12 @@ config EDAC_SBRIDGE
config EDAC_SKX
tristate "Intel Skylake server Integrated MC"
depends on PCI && X86_64 && X86_MCE_INTEL && PCI_MMCONFIG
+ select DMI
help
Support for error detection and correction the Intel
- Skylake server Integrated Memory Controllers.
+ Skylake server Integrated Memory Controllers. If your
+ system has non-volatile DIMMs you should also manually
+ select CONFIG_ACPI_NFIT.
config EDAC_PND2
tristate "Intel Pondicherry2"
@@ -254,7 +257,7 @@ config EDAC_MPC85XX
config EDAC_LAYERSCAPE
tristate "Freescale Layerscape DDR"
- depends on ARCH_LAYERSCAPE
+ depends on ARCH_LAYERSCAPE || SOC_LS1021A
help
Support for error detection and correction on Freescale memory
controllers on Layerscape SoCs.
@@ -317,14 +320,6 @@ config EDAC_CPC925
a companion chip to the PowerPC 970 family of
processors.
-config EDAC_TILE
- tristate "Tilera Memory Controller"
- depends on TILE
- default y
- help
- Support for error detection and correction on the
- Tilera memory controller.
-
config EDAC_HIGHBANK_MC
tristate "Highbank Memory Controller"
depends on ARCH_HIGHBANK
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
index b54912eb39af..02b43a7d8c3e 100644
--- a/drivers/edac/Makefile
+++ b/drivers/edac/Makefile
@@ -64,8 +64,6 @@ obj-$(CONFIG_EDAC_PPC4XX) += ppc4xx_edac.o
obj-$(CONFIG_EDAC_AMD8111) += amd8111_edac.o
obj-$(CONFIG_EDAC_AMD8131) += amd8131_edac.o
-obj-$(CONFIG_EDAC_TILE) += tile_edac.o
-
obj-$(CONFIG_EDAC_HIGHBANK_MC) += highbank_mc_edac.o
obj-$(CONFIG_EDAC_HIGHBANK_L2) += highbank_l2_edac.o
diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c
index 48193f5f3b56..3bb82e511eca 100644
--- a/drivers/edac/edac_mc.c
+++ b/drivers/edac/edac_mc.c
@@ -195,26 +195,27 @@ static void edac_mc_dump_mci(struct mem_ctl_info *mci)
#endif /* CONFIG_EDAC_DEBUG */
const char * const edac_mem_types[] = {
- [MEM_EMPTY] = "Empty csrow",
- [MEM_RESERVED] = "Reserved csrow type",
- [MEM_UNKNOWN] = "Unknown csrow type",
- [MEM_FPM] = "Fast page mode RAM",
- [MEM_EDO] = "Extended data out RAM",
- [MEM_BEDO] = "Burst Extended data out RAM",
- [MEM_SDR] = "Single data rate SDRAM",
- [MEM_RDR] = "Registered single data rate SDRAM",
- [MEM_DDR] = "Double data rate SDRAM",
- [MEM_RDDR] = "Registered Double data rate SDRAM",
- [MEM_RMBS] = "Rambus DRAM",
- [MEM_DDR2] = "Unbuffered DDR2 RAM",
- [MEM_FB_DDR2] = "Fully buffered DDR2",
- [MEM_RDDR2] = "Registered DDR2 RAM",
- [MEM_XDR] = "Rambus XDR",
- [MEM_DDR3] = "Unbuffered DDR3 RAM",
- [MEM_RDDR3] = "Registered DDR3 RAM",
- [MEM_LRDDR3] = "Load-Reduced DDR3 RAM",
- [MEM_DDR4] = "Unbuffered DDR4 RAM",
- [MEM_RDDR4] = "Registered DDR4 RAM",
+ [MEM_EMPTY] = "Empty",
+ [MEM_RESERVED] = "Reserved",
+ [MEM_UNKNOWN] = "Unknown",
+ [MEM_FPM] = "FPM",
+ [MEM_EDO] = "EDO",
+ [MEM_BEDO] = "BEDO",
+ [MEM_SDR] = "Unbuffered-SDR",
+ [MEM_RDR] = "Registered-SDR",
+ [MEM_DDR] = "Unbuffered-DDR",
+ [MEM_RDDR] = "Registered-DDR",
+ [MEM_RMBS] = "RMBS",
+ [MEM_DDR2] = "Unbuffered-DDR2",
+ [MEM_FB_DDR2] = "FullyBuffered-DDR2",
+ [MEM_RDDR2] = "Registered-DDR2",
+ [MEM_XDR] = "XDR",
+ [MEM_DDR3] = "Unbuffered-DDR3",
+ [MEM_RDDR3] = "Registered-DDR3",
+ [MEM_LRDDR3] = "Load-Reduced-DDR3-RAM",
+ [MEM_DDR4] = "Unbuffered-DDR4",
+ [MEM_RDDR4] = "Registered-DDR4",
+ [MEM_NVDIMM] = "Non-volatile-RAM",
};
EXPORT_SYMBOL_GPL(edac_mem_types);
diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c
index c70ea82c815c..7481955160a4 100644
--- a/drivers/edac/edac_mc_sysfs.c
+++ b/drivers/edac/edac_mc_sysfs.c
@@ -91,28 +91,6 @@ static struct device *mci_pdev;
/*
* various constants for Memory Controllers
*/
-static const char * const mem_types[] = {
- [MEM_EMPTY] = "Empty",
- [MEM_RESERVED] = "Reserved",
- [MEM_UNKNOWN] = "Unknown",
- [MEM_FPM] = "FPM",
- [MEM_EDO] = "EDO",
- [MEM_BEDO] = "BEDO",
- [MEM_SDR] = "Unbuffered-SDR",
- [MEM_RDR] = "Registered-SDR",
- [MEM_DDR] = "Unbuffered-DDR",
- [MEM_RDDR] = "Registered-DDR",
- [MEM_RMBS] = "RMBS",
- [MEM_DDR2] = "Unbuffered-DDR2",
- [MEM_FB_DDR2] = "FullyBuffered-DDR2",
- [MEM_RDDR2] = "Registered-DDR2",
- [MEM_XDR] = "XDR",
- [MEM_DDR3] = "Unbuffered-DDR3",
- [MEM_RDDR3] = "Registered-DDR3",
- [MEM_DDR4] = "Unbuffered-DDR4",
- [MEM_RDDR4] = "Registered-DDR4"
-};
-
static const char * const dev_types[] = {
[DEV_UNKNOWN] = "Unknown",
[DEV_X1] = "x1",
@@ -196,7 +174,7 @@ static ssize_t csrow_mem_type_show(struct device *dev,
{
struct csrow_info *csrow = to_csrow(dev);
- return sprintf(data, "%s\n", mem_types[csrow->channels[0]->dimm->mtype]);
+ return sprintf(data, "%s\n", edac_mem_types[csrow->channels[0]->dimm->mtype]);
}
static ssize_t csrow_dev_type_show(struct device *dev,
@@ -549,7 +527,7 @@ static ssize_t dimmdev_mem_type_show(struct device *dev,
{
struct dimm_info *dimm = to_dimm(dev);
- return sprintf(data, "%s\n", mem_types[dimm->mtype]);
+ return sprintf(data, "%s\n", edac_mem_types[dimm->mtype]);
}
static ssize_t dimmdev_dev_type_show(struct device *dev,
diff --git a/drivers/edac/mce_amd.c b/drivers/edac/mce_amd.c
index a11a671c7a38..2ab4d61ee47e 100644
--- a/drivers/edac/mce_amd.c
+++ b/drivers/edac/mce_amd.c
@@ -854,21 +854,24 @@ static void decode_mc6_mce(struct mce *m)
static void decode_smca_error(struct mce *m)
{
struct smca_hwid *hwid;
- unsigned int bank_type;
+ enum smca_bank_types bank_type;
const char *ip_name;
u8 xec = XEC(m->status, xec_mask);
if (m->bank >= ARRAY_SIZE(smca_banks))
return;
- if (x86_family(m->cpuid) >= 0x17 && m->bank == 4)
- pr_emerg(HW_ERR "Bank 4 is reserved on Fam17h.\n");
-
hwid = smca_banks[m->bank].hwid;
if (!hwid)
return;
bank_type = hwid->bank_type;
+
+ if (bank_type == SMCA_RESERVED) {
+ pr_emerg(HW_ERR "Bank %d is reserved.\n", m->bank);
+ return;
+ }
+
ip_name = smca_get_long_name(bank_type);
pr_emerg(HW_ERR "%s Extended Error Code: %d\n", ip_name, xec);
diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c
index 872100215ca0..4a89c8093307 100644
--- a/drivers/edac/sb_edac.c
+++ b/drivers/edac/sb_edac.c
@@ -110,6 +110,10 @@ static const u32 knl_interleave_list[] = {
0xdc, 0xe4, 0xec, 0xf4, 0xfc, /* 15-19 */
0x104, 0x10c, 0x114, 0x11c, /* 20-23 */
};
+#define MAX_INTERLEAVE \
+ (max_t(unsigned int, ARRAY_SIZE(sbridge_interleave_list), \
+ max_t(unsigned int, ARRAY_SIZE(ibridge_interleave_list), \
+ ARRAY_SIZE(knl_interleave_list))))
struct interleave_pkg {
unsigned char start;
@@ -321,7 +325,6 @@ struct sbridge_info {
const u32 *interleave_list;
const struct interleave_pkg *interleave_pkg;
u8 max_sad;
- u8 max_interleave;
u8 (*get_node_id)(struct sbridge_pvt *pvt);
enum mem_type (*get_memory_type)(struct sbridge_pvt *pvt);
enum dev_type (*get_width)(struct sbridge_pvt *pvt, u32 mtr);
@@ -1899,7 +1902,7 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
int n_rir, n_sads, n_tads, sad_way, sck_xch;
int sad_interl, idx, base_ch;
int interleave_mode, shiftup = 0;
- unsigned sad_interleave[pvt->info.max_interleave];
+ unsigned int sad_interleave[MAX_INTERLEAVE];
u32 reg, dram_rule;
u8 ch_way, sck_way, pkg, sad_ha = 0;
u32 tad_offset;
@@ -3169,7 +3172,6 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
pvt->info.dram_attr = dram_attr;
pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule);
pvt->info.interleave_list = ibridge_interleave_list;
- pvt->info.max_interleave = ARRAY_SIZE(ibridge_interleave_list);
pvt->info.interleave_pkg = ibridge_interleave_pkg;
pvt->info.get_width = ibridge_get_width;
@@ -3194,7 +3196,6 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
pvt->info.dram_attr = dram_attr;
pvt->info.max_sad = ARRAY_SIZE(sbridge_dram_rule);
pvt->info.interleave_list = sbridge_interleave_list;
- pvt->info.max_interleave = ARRAY_SIZE(sbridge_interleave_list);
pvt->info.interleave_pkg = sbridge_interleave_pkg;
pvt->info.get_width = sbridge_get_width;
@@ -3219,7 +3220,6 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
pvt->info.dram_attr = dram_attr;
pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule);
pvt->info.interleave_list = ibridge_interleave_list;
- pvt->info.max_interleave = ARRAY_SIZE(ibridge_interleave_list);
pvt->info.interleave_pkg = ibridge_interleave_pkg;
pvt->info.get_width = ibridge_get_width;
@@ -3244,7 +3244,6 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
pvt->info.dram_attr = dram_attr;
pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule);
pvt->info.interleave_list = ibridge_interleave_list;
- pvt->info.max_interleave = ARRAY_SIZE(ibridge_interleave_list);
pvt->info.interleave_pkg = ibridge_interleave_pkg;
pvt->info.get_width = broadwell_get_width;
@@ -3269,7 +3268,6 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
pvt->info.dram_attr = dram_attr_knl;
pvt->info.max_sad = ARRAY_SIZE(knl_dram_rule);
pvt->info.interleave_list = knl_interleave_list;
- pvt->info.max_interleave = ARRAY_SIZE(knl_interleave_list);
pvt->info.interleave_pkg = ibridge_interleave_pkg;
pvt->info.get_width = knl_get_width;
diff --git a/drivers/edac/skx_edac.c b/drivers/edac/skx_edac.c
index 912c4930c9ef..fae095162c01 100644
--- a/drivers/edac/skx_edac.c
+++ b/drivers/edac/skx_edac.c
@@ -14,6 +14,8 @@
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/dmi.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/slab.h>
@@ -24,6 +26,7 @@
#include <linux/bitmap.h>
#include <linux/math64.h>
#include <linux/mod_devicetable.h>
+#include <acpi/nfit.h>
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
#include <asm/processor.h>
@@ -302,6 +305,7 @@ static int get_dimm_attr(u32 reg, int lobit, int hibit, int add, int minval,
}
#define IS_DIMM_PRESENT(mtr) GET_BITFIELD((mtr), 15, 15)
+#define IS_NVDIMM_PRESENT(mcddrtcfg, i) GET_BITFIELD((mcddrtcfg), (i), (i))
#define numrank(reg) get_dimm_attr((reg), 12, 13, 0, 0, 2, "ranks")
#define numrow(reg) get_dimm_attr((reg), 2, 4, 12, 1, 6, "rows")
@@ -350,8 +354,6 @@ static int get_dimm_info(u32 mtr, u32 amap, struct dimm_info *dimm,
int banks = 16, ranks, rows, cols, npages;
u64 size;
- if (!IS_DIMM_PRESENT(mtr))
- return 0;
ranks = numrank(mtr);
rows = numrow(mtr);
cols = numcol(mtr);
@@ -383,6 +385,54 @@ static int get_dimm_info(u32 mtr, u32 amap, struct dimm_info *dimm,
return 1;
}
+static int get_nvdimm_info(struct dimm_info *dimm, struct skx_imc *imc,
+ int chan, int dimmno)
+{
+ int smbios_handle;
+ u32 dev_handle;
+ u16 flags;
+ u64 size = 0;
+
+ dev_handle = ACPI_NFIT_BUILD_DEVICE_HANDLE(dimmno, chan, imc->lmc,
+ imc->src_id, 0);
+
+ smbios_handle = nfit_get_smbios_id(dev_handle, &flags);
+ if (smbios_handle == -EOPNOTSUPP) {
+ pr_warn_once(EDAC_MOD_STR ": Can't find size of NVDIMM. Try enabling CONFIG_ACPI_NFIT\n");
+ goto unknown_size;
+ }
+
+ if (smbios_handle < 0) {
+ skx_printk(KERN_ERR, "Can't find handle for NVDIMM ADR=%x\n", dev_handle);
+ goto unknown_size;
+ }
+
+ if (flags & ACPI_NFIT_MEM_MAP_FAILED) {
+ skx_printk(KERN_ERR, "NVDIMM ADR=%x is not mapped\n", dev_handle);
+ goto unknown_size;
+ }
+
+ size = dmi_memdev_size(smbios_handle);
+ if (size == ~0ull)
+ skx_printk(KERN_ERR, "Can't find size for NVDIMM ADR=%x/SMBIOS=%x\n",
+ dev_handle, smbios_handle);
+
+unknown_size:
+ dimm->nr_pages = size >> PAGE_SHIFT;
+ dimm->grain = 32;
+ dimm->dtype = DEV_UNKNOWN;
+ dimm->mtype = MEM_NVDIMM;
+ dimm->edac_mode = EDAC_SECDED; /* likely better than this */
+
+ edac_dbg(0, "mc#%d: channel %d, dimm %d, %llu Mb (%u pages)\n",
+ imc->mc, chan, dimmno, size >> 20, dimm->nr_pages);
+
+ snprintf(dimm->label, sizeof(dimm->label), "CPU_SrcID#%u_MC#%u_Chan#%u_DIMM#%u",
+ imc->src_id, imc->lmc, chan, dimmno);
+
+ return (size == 0 || size == ~0ull) ? 0 : 1;
+}
+
#define SKX_GET_MTMTR(dev, reg) \
pci_read_config_dword((dev), 0x87c, &reg)
@@ -399,20 +449,24 @@ static int skx_get_dimm_config(struct mem_ctl_info *mci)
{
struct skx_pvt *pvt = mci->pvt_info;
struct skx_imc *imc = pvt->imc;
+ u32 mtr, amap, mcddrtcfg;
struct dimm_info *dimm;
int i, j;
- u32 mtr, amap;
int ndimms;
for (i = 0; i < NUM_CHANNELS; i++) {
ndimms = 0;
pci_read_config_dword(imc->chan[i].cdev, 0x8C, &amap);
+ pci_read_config_dword(imc->chan[i].cdev, 0x400, &mcddrtcfg);
for (j = 0; j < NUM_DIMMS; j++) {
dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms,
mci->n_layers, i, j, 0);
pci_read_config_dword(imc->chan[i].cdev,
0x80 + 4*j, &mtr);
- ndimms += get_dimm_info(mtr, amap, dimm, imc, i, j);
+ if (IS_DIMM_PRESENT(mtr))
+ ndimms += get_dimm_info(mtr, amap, dimm, imc, i, j);
+ else if (IS_NVDIMM_PRESENT(mcddrtcfg, j))
+ ndimms += get_nvdimm_info(dimm, imc, i, j);
}
if (ndimms && !skx_check_ecc(imc->chan[0].cdev)) {
skx_printk(KERN_ERR, "ECC is disabled on imc %d\n", imc->mc);
@@ -468,13 +522,14 @@ static int skx_register_mci(struct skx_imc *imc)
pvt = mci->pvt_info;
pvt->imc = imc;
- mci->ctl_name = kasprintf(GFP_KERNEL, "Skylake Socket#%d IMC#%d", imc->node_id, imc->lmc);
+ mci->ctl_name = kasprintf(GFP_KERNEL, "Skylake Socket#%d IMC#%d",
+ imc->node_id, imc->lmc);
if (!mci->ctl_name) {
rc = -ENOMEM;
goto fail0;
}
- mci->mtype_cap = MEM_FLAG_DDR4;
+ mci->mtype_cap = MEM_FLAG_DDR4 | MEM_FLAG_NVDIMM;
mci->edac_ctl_cap = EDAC_FLAG_NONE;
mci->edac_cap = EDAC_FLAG_NONE;
mci->mod_name = EDAC_MOD_STR;
diff --git a/drivers/edac/tile_edac.c b/drivers/edac/tile_edac.c
deleted file mode 100644
index 8a33a87e67f1..000000000000
--- a/drivers/edac/tile_edac.c
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * Copyright 2011 Tilera Corporation. All Rights Reserved.
- *
- * 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 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, GOOD TITLE or
- * NON INFRINGEMENT. See the GNU General Public License for
- * more details.
- * Tilera-specific EDAC driver.
- *
- * This source code is derived from the following driver:
- *
- * Cell MIC driver for ECC counting
- *
- * Copyright 2007 Benjamin Herrenschmidt, IBM Corp.
- * <benh@kernel.crashing.org>
- *
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
-#include <linux/uaccess.h>
-#include <linux/edac.h>
-#include <hv/hypervisor.h>
-#include <hv/drv_mshim_intf.h>
-
-#include "edac_module.h"
-
-#define DRV_NAME "tile-edac"
-
-/* Number of cs_rows needed per memory controller on TILEPro. */
-#define TILE_EDAC_NR_CSROWS 1
-
-/* Number of channels per memory controller on TILEPro. */
-#define TILE_EDAC_NR_CHANS 1
-
-/* Granularity of reported error in bytes on TILEPro. */
-#define TILE_EDAC_ERROR_GRAIN 8
-
-/* TILE processor has multiple independent memory controllers. */
-struct platform_device *mshim_pdev[TILE_MAX_MSHIMS];
-
-struct tile_edac_priv {
- int hv_devhdl; /* Hypervisor device handle. */
- int node; /* Memory controller instance #. */
- unsigned int ce_count; /*
- * Correctable-error counter
- * kept by the driver.
- */
-};
-
-static void tile_edac_check(struct mem_ctl_info *mci)
-{
- struct tile_edac_priv *priv = mci->pvt_info;
- struct mshim_mem_error mem_error;
-
- if (hv_dev_pread(priv->hv_devhdl, 0, (HV_VirtAddr)&mem_error,
- sizeof(struct mshim_mem_error), MSHIM_MEM_ERROR_OFF) !=
- sizeof(struct mshim_mem_error)) {
- pr_err(DRV_NAME ": MSHIM_MEM_ERROR_OFF pread failure.\n");
- return;
- }
-
- /* Check if the current error count is different from the saved one. */
- if (mem_error.sbe_count != priv->ce_count) {
- dev_dbg(mci->pdev, "ECC CE err on node %d\n", priv->node);
- priv->ce_count = mem_error.sbe_count;
- edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
- 0, 0, 0,
- 0, 0, -1,
- mci->ctl_name, "");
- }
-}
-
-/*
- * Initialize the 'csrows' table within the mci control structure with the
- * addressing of memory.
- */
-static int tile_edac_init_csrows(struct mem_ctl_info *mci)
-{
- struct csrow_info *csrow = mci->csrows[0];
- struct tile_edac_priv *priv = mci->pvt_info;
- struct mshim_mem_info mem_info;
- struct dimm_info *dimm = csrow->channels[0]->dimm;
-
- if (hv_dev_pread(priv->hv_devhdl, 0, (HV_VirtAddr)&mem_info,
- sizeof(struct mshim_mem_info), MSHIM_MEM_INFO_OFF) !=
- sizeof(struct mshim_mem_info)) {
- pr_err(DRV_NAME ": MSHIM_MEM_INFO_OFF pread failure.\n");
- return -1;
- }
-
- if (mem_info.mem_ecc)
- dimm->edac_mode = EDAC_SECDED;
- else
- dimm->edac_mode = EDAC_NONE;
- switch (mem_info.mem_type) {
- case DDR2:
- dimm->mtype = MEM_DDR2;
- break;
-
- case DDR3:
- dimm->mtype = MEM_DDR3;
- break;
-
- default:
- return -1;
- }
-
- dimm->nr_pages = mem_info.mem_size >> PAGE_SHIFT;
- dimm->grain = TILE_EDAC_ERROR_GRAIN;
- dimm->dtype = DEV_UNKNOWN;
-
- return 0;
-}
-
-static int tile_edac_mc_probe(struct platform_device *pdev)
-{
- char hv_file[32];
- int hv_devhdl;
- struct mem_ctl_info *mci;
- struct edac_mc_layer layers[2];
- struct tile_edac_priv *priv;
- int rc;
-
- sprintf(hv_file, "mshim/%d", pdev->id);
- hv_devhdl = hv_dev_open((HV_VirtAddr)hv_file, 0);
- if (hv_devhdl < 0)
- return -EINVAL;
-
- /* A TILE MC has a single channel and one chip-select row. */
- layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
- layers[0].size = TILE_EDAC_NR_CSROWS;
- layers[0].is_virt_csrow = true;
- layers[1].type = EDAC_MC_LAYER_CHANNEL;
- layers[1].size = TILE_EDAC_NR_CHANS;
- layers[1].is_virt_csrow = false;
- mci = edac_mc_alloc(pdev->id, ARRAY_SIZE(layers), layers,
- sizeof(struct tile_edac_priv));
- if (mci == NULL)
- return -ENOMEM;
- priv = mci->pvt_info;
- priv->node = pdev->id;
- priv->hv_devhdl = hv_devhdl;
-
- mci->pdev = &pdev->dev;
- mci->mtype_cap = MEM_FLAG_DDR2;
- mci->edac_ctl_cap = EDAC_FLAG_SECDED;
-
- mci->mod_name = DRV_NAME;
-#ifdef __tilegx__
- mci->ctl_name = "TILEGx_Memory_Controller";
-#else
- mci->ctl_name = "TILEPro_Memory_Controller";
-#endif
- mci->dev_name = dev_name(&pdev->dev);
- mci->edac_check = tile_edac_check;
-
- /*
- * Initialize the MC control structure 'csrows' table
- * with the mapping and control information.
- */
- if (tile_edac_init_csrows(mci)) {
- /* No csrows found. */
- mci->edac_cap = EDAC_FLAG_NONE;
- } else {
- mci->edac_cap = EDAC_FLAG_SECDED;
- }
-
- platform_set_drvdata(pdev, mci);
-
- /* Register with EDAC core */
- rc = edac_mc_add_mc(mci);
- if (rc) {
- dev_err(&pdev->dev, "failed to register with EDAC core\n");
- edac_mc_free(mci);
- return rc;
- }
-
- return 0;
-}
-
-static int tile_edac_mc_remove(struct platform_device *pdev)
-{
- struct mem_ctl_info *mci = platform_get_drvdata(pdev);
-
- edac_mc_del_mc(&pdev->dev);
- if (mci)
- edac_mc_free(mci);
- return 0;
-}
-
-static struct platform_driver tile_edac_mc_driver = {
- .driver = {
- .name = DRV_NAME,
- },
- .probe = tile_edac_mc_probe,
- .remove = tile_edac_mc_remove,
-};
-
-/*
- * Driver init routine.
- */
-static int __init tile_edac_init(void)
-{
- char hv_file[32];
- struct platform_device *pdev;
- int i, err, num = 0;
-
- /* Only support POLL mode. */
- edac_op_state = EDAC_OPSTATE_POLL;
-
- err = platform_driver_register(&tile_edac_mc_driver);
- if (err)
- return err;
-
- for (i = 0; i < TILE_MAX_MSHIMS; i++) {
- /*
- * Not all memory controllers are configured such as in the
- * case of a simulator. So we register only those mshims
- * that are configured by the hypervisor.
- */
- sprintf(hv_file, "mshim/%d", i);
- if (hv_dev_open((HV_VirtAddr)hv_file, 0) < 0)
- continue;
-
- pdev = platform_device_register_simple(DRV_NAME, i, NULL, 0);
- if (IS_ERR(pdev))
- continue;
- mshim_pdev[i] = pdev;
- num++;
- }
-
- if (num == 0) {
- platform_driver_unregister(&tile_edac_mc_driver);
- return -ENODEV;
- }
- return 0;
-}
-
-/*
- * Driver cleanup routine.
- */
-static void __exit tile_edac_exit(void)
-{
- int i;
-
- for (i = 0; i < TILE_MAX_MSHIMS; i++) {
- struct platform_device *pdev = mshim_pdev[i];
- if (!pdev)
- continue;
-
- platform_device_unregister(pdev);
- }
- platform_driver_unregister(&tile_edac_mc_driver);
-}
-
-module_init(tile_edac_init);
-module_exit(tile_edac_exit);
diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig
index a7bca4207f44..de15bf55895b 100644
--- a/drivers/extcon/Kconfig
+++ b/drivers/extcon/Kconfig
@@ -30,7 +30,8 @@ config EXTCON_ARIZONA
config EXTCON_AXP288
tristate "X-Power AXP288 EXTCON support"
- depends on MFD_AXP20X && USB_PHY
+ depends on MFD_AXP20X && USB_SUPPORT && X86
+ select USB_ROLE_SWITCH
help
Say Y here to enable support for USB peripheral detection
and USB MUX switching by X-Power AXP288 PMIC.
diff --git a/drivers/extcon/extcon-axp288.c b/drivers/extcon/extcon-axp288.c
index 3ec4c715e240..a983708b77a6 100644
--- a/drivers/extcon/extcon-axp288.c
+++ b/drivers/extcon/extcon-axp288.c
@@ -1,6 +1,7 @@
/*
* extcon-axp288.c - X-Power AXP288 PMIC extcon cable detection driver
*
+ * Copyright (c) 2017-2018 Hans de Goede <hdegoede@redhat.com>
* Copyright (C) 2015 Intel Corporation
* Author: Ramakrishna Pallala <ramakrishna.pallala@intel.com>
*
@@ -14,6 +15,7 @@
* GNU General Public License for more details.
*/
+#include <linux/acpi.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/io.h>
@@ -25,6 +27,11 @@
#include <linux/extcon-provider.h>
#include <linux/regmap.h>
#include <linux/mfd/axp20x.h>
+#include <linux/usb/role.h>
+#include <linux/workqueue.h>
+
+#include <asm/cpu_device_id.h>
+#include <asm/intel-family.h>
/* Power source status register */
#define PS_STAT_VBUS_TRIGGER BIT(0)
@@ -97,9 +104,19 @@ struct axp288_extcon_info {
struct device *dev;
struct regmap *regmap;
struct regmap_irq_chip_data *regmap_irqc;
+ struct usb_role_switch *role_sw;
+ struct work_struct role_work;
int irq[EXTCON_IRQ_END];
struct extcon_dev *edev;
+ struct extcon_dev *id_extcon;
+ struct notifier_block id_nb;
unsigned int previous_cable;
+ bool vbus_attach;
+};
+
+static const struct x86_cpu_id cherry_trail_cpu_ids[] = {
+ { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_AIRMONT, X86_FEATURE_ANY },
+ {}
};
/* Power up/down reason string array */
@@ -137,20 +154,74 @@ static void axp288_extcon_log_rsi(struct axp288_extcon_info *info)
regmap_write(info->regmap, AXP288_PS_BOOT_REASON_REG, clear_mask);
}
-static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info)
+/*
+ * The below code to control the USB role-switch on devices with an AXP288
+ * may seem out of place, but there are 2 reasons why this is the best place
+ * to control the USB role-switch on such devices:
+ * 1) On many devices the USB role is controlled by AML code, but the AML code
+ * only switches between the host and none roles, because of Windows not
+ * really using device mode. To make device mode work we need to toggle
+ * between the none/device roles based on Vbus presence, and this driver
+ * gets interrupts on Vbus insertion / removal.
+ * 2) In order for our BC1.2 charger detection to work properly the role
+ * mux must be properly set to device mode before we do the detection.
+ */
+
+/* Returns the id-pin value, note pulled low / false == host-mode */
+static bool axp288_get_id_pin(struct axp288_extcon_info *info)
{
- int ret, stat, cfg, pwr_stat;
- u8 chrg_type;
- unsigned int cable = info->previous_cable;
- bool vbus_attach = false;
+ enum usb_role role;
+
+ if (info->id_extcon)
+ return extcon_get_state(info->id_extcon, EXTCON_USB_HOST) <= 0;
+
+ /* We cannot access the id-pin, see what mode the AML code has set */
+ role = usb_role_switch_get_role(info->role_sw);
+ return role != USB_ROLE_HOST;
+}
+
+static void axp288_usb_role_work(struct work_struct *work)
+{
+ struct axp288_extcon_info *info =
+ container_of(work, struct axp288_extcon_info, role_work);
+ enum usb_role role;
+ bool id_pin;
+ int ret;
+
+ id_pin = axp288_get_id_pin(info);
+ if (!id_pin)
+ role = USB_ROLE_HOST;
+ else if (info->vbus_attach)
+ role = USB_ROLE_DEVICE;
+ else
+ role = USB_ROLE_NONE;
+
+ ret = usb_role_switch_set_role(info->role_sw, role);
+ if (ret)
+ dev_err(info->dev, "failed to set role: %d\n", ret);
+}
+
+static bool axp288_get_vbus_attach(struct axp288_extcon_info *info)
+{
+ int ret, pwr_stat;
ret = regmap_read(info->regmap, AXP288_PS_STAT_REG, &pwr_stat);
if (ret < 0) {
dev_err(info->dev, "failed to read vbus status\n");
- return ret;
+ return false;
}
- vbus_attach = (pwr_stat & PS_STAT_VBUS_VALID);
+ return !!(pwr_stat & PS_STAT_VBUS_VALID);
+}
+
+static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info)
+{
+ int ret, stat, cfg;
+ u8 chrg_type;
+ unsigned int cable = info->previous_cable;
+ bool vbus_attach = false;
+
+ vbus_attach = axp288_get_vbus_attach(info);
if (!vbus_attach)
goto no_vbus;
@@ -201,6 +272,12 @@ no_vbus:
info->previous_cable = cable;
}
+ if (info->role_sw && info->vbus_attach != vbus_attach) {
+ info->vbus_attach = vbus_attach;
+ /* Setting the role can take a while */
+ queue_work(system_long_wq, &info->role_work);
+ }
+
return 0;
dev_det_ret:
@@ -210,6 +287,18 @@ dev_det_ret:
return ret;
}
+static int axp288_extcon_id_evt(struct notifier_block *nb,
+ unsigned long event, void *param)
+{
+ struct axp288_extcon_info *info =
+ container_of(nb, struct axp288_extcon_info, id_nb);
+
+ /* We may not sleep and setting the role can take a while */
+ queue_work(system_long_wq, &info->role_work);
+
+ return NOTIFY_OK;
+}
+
static irqreturn_t axp288_extcon_isr(int irq, void *data)
{
struct axp288_extcon_info *info = data;
@@ -231,10 +320,20 @@ static void axp288_extcon_enable(struct axp288_extcon_info *info)
BC_GLOBAL_RUN, BC_GLOBAL_RUN);
}
+static void axp288_put_role_sw(void *data)
+{
+ struct axp288_extcon_info *info = data;
+
+ cancel_work_sync(&info->role_work);
+ usb_role_switch_put(info->role_sw);
+}
+
static int axp288_extcon_probe(struct platform_device *pdev)
{
struct axp288_extcon_info *info;
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
+ struct device *dev = &pdev->dev;
+ const char *name;
int ret, i, pirq;
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
@@ -245,9 +344,33 @@ static int axp288_extcon_probe(struct platform_device *pdev)
info->regmap = axp20x->regmap;
info->regmap_irqc = axp20x->regmap_irqc;
info->previous_cable = EXTCON_NONE;
+ INIT_WORK(&info->role_work, axp288_usb_role_work);
+ info->id_nb.notifier_call = axp288_extcon_id_evt;
platform_set_drvdata(pdev, info);
+ info->role_sw = usb_role_switch_get(dev);
+ if (IS_ERR(info->role_sw))
+ return PTR_ERR(info->role_sw);
+ if (info->role_sw) {
+ ret = devm_add_action_or_reset(dev, axp288_put_role_sw, info);
+ if (ret)
+ return ret;
+
+ name = acpi_dev_get_first_match_name("INT3496", NULL, -1);
+ if (name) {
+ info->id_extcon = extcon_get_extcon_dev(name);
+ if (!info->id_extcon)
+ return -EPROBE_DEFER;
+
+ dev_info(dev, "controlling USB role\n");
+ } else {
+ dev_info(dev, "controlling USB role based on Vbus presence\n");
+ }
+ }
+
+ info->vbus_attach = axp288_get_vbus_attach(info);
+
axp288_extcon_log_rsi(info);
/* Initialize extcon device */
@@ -289,6 +412,19 @@ static int axp288_extcon_probe(struct platform_device *pdev)
}
}
+ if (info->id_extcon) {
+ ret = devm_extcon_register_notifier_all(dev, info->id_extcon,
+ &info->id_nb);
+ if (ret)
+ return ret;
+ }
+
+ /* Make sure the role-sw is set correctly before doing BC detection */
+ if (info->role_sw) {
+ queue_work(system_long_wq, &info->role_work);
+ flush_work(&info->role_work);
+ }
+
/* Start charger cable type detection */
axp288_extcon_enable(info);
@@ -308,8 +444,32 @@ static struct platform_driver axp288_extcon_driver = {
.name = "axp288_extcon",
},
};
-module_platform_driver(axp288_extcon_driver);
+
+static struct device_connection axp288_extcon_role_sw_conn = {
+ .endpoint[0] = "axp288_extcon",
+ .endpoint[1] = "intel_xhci_usb_sw-role-switch",
+ .id = "usb-role-switch",
+};
+
+static int __init axp288_extcon_init(void)
+{
+ if (x86_match_cpu(cherry_trail_cpu_ids))
+ device_connection_add(&axp288_extcon_role_sw_conn);
+
+ return platform_driver_register(&axp288_extcon_driver);
+}
+module_init(axp288_extcon_init);
+
+static void __exit axp288_extcon_exit(void)
+{
+ if (x86_match_cpu(cherry_trail_cpu_ids))
+ device_connection_remove(&axp288_extcon_role_sw_conn);
+
+ platform_driver_unregister(&axp288_extcon_driver);
+}
+module_exit(axp288_extcon_exit);
MODULE_AUTHOR("Ramakrishna Pallala <ramakrishna.pallala@intel.com>");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_DESCRIPTION("X-Powers AXP288 extcon driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/extcon/extcon-gpio.c b/drivers/extcon/extcon-gpio.c
index ab770adcca7e..13ba3a6e81d5 100644
--- a/drivers/extcon/extcon-gpio.c
+++ b/drivers/extcon/extcon-gpio.c
@@ -18,8 +18,6 @@
*/
#include <linux/extcon-provider.h>
-#include <linux/extcon/extcon-gpio.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/interrupt.h>
@@ -29,14 +27,30 @@
#include <linux/slab.h>
#include <linux/workqueue.h>
+/**
+ * struct gpio_extcon_data - A simple GPIO-controlled extcon device state container.
+ * @edev: Extcon device.
+ * @irq: Interrupt line for the external connector.
+ * @work: Work fired by the interrupt.
+ * @debounce_jiffies: Number of jiffies to wait for the GPIO to stabilize, from the debounce
+ * value.
+ * @gpiod: GPIO descriptor for this external connector.
+ * @extcon_id: The unique id of specific external connector.
+ * @debounce: Debounce time for GPIO IRQ in ms.
+ * @irq_flags: IRQ Flags (e.g., IRQF_TRIGGER_LOW).
+ * @check_on_resume: Boolean describing whether to check the state of gpio
+ * while resuming from sleep.
+ */
struct gpio_extcon_data {
struct extcon_dev *edev;
int irq;
struct delayed_work work;
unsigned long debounce_jiffies;
-
- struct gpio_desc *id_gpiod;
- struct gpio_extcon_pdata *pdata;
+ struct gpio_desc *gpiod;
+ unsigned int extcon_id;
+ unsigned long debounce;
+ unsigned long irq_flags;
+ bool check_on_resume;
};
static void gpio_extcon_work(struct work_struct *work)
@@ -46,11 +60,8 @@ static void gpio_extcon_work(struct work_struct *work)
container_of(to_delayed_work(work), struct gpio_extcon_data,
work);
- state = gpiod_get_value_cansleep(data->id_gpiod);
- if (data->pdata->gpio_active_low)
- state = !state;
-
- extcon_set_state_sync(data->edev, data->pdata->extcon_id, state);
+ state = gpiod_get_value_cansleep(data->gpiod);
+ extcon_set_state_sync(data->edev, data->extcon_id, state);
}
static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
@@ -62,65 +73,41 @@ static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int gpio_extcon_init(struct device *dev, struct gpio_extcon_data *data)
-{
- struct gpio_extcon_pdata *pdata = data->pdata;
- int ret;
-
- ret = devm_gpio_request_one(dev, pdata->gpio, GPIOF_DIR_IN,
- dev_name(dev));
- if (ret < 0)
- return ret;
-
- data->id_gpiod = gpio_to_desc(pdata->gpio);
- if (!data->id_gpiod)
- return -EINVAL;
-
- if (pdata->debounce) {
- ret = gpiod_set_debounce(data->id_gpiod,
- pdata->debounce * 1000);
- if (ret < 0)
- data->debounce_jiffies =
- msecs_to_jiffies(pdata->debounce);
- }
-
- data->irq = gpiod_to_irq(data->id_gpiod);
- if (data->irq < 0)
- return data->irq;
-
- return 0;
-}
-
static int gpio_extcon_probe(struct platform_device *pdev)
{
- struct gpio_extcon_pdata *pdata = dev_get_platdata(&pdev->dev);
struct gpio_extcon_data *data;
+ struct device *dev = &pdev->dev;
int ret;
- if (!pdata)
- return -EBUSY;
- if (!pdata->irq_flags || pdata->extcon_id > EXTCON_NONE)
- return -EINVAL;
-
- data = devm_kzalloc(&pdev->dev, sizeof(struct gpio_extcon_data),
- GFP_KERNEL);
+ data = devm_kzalloc(dev, sizeof(struct gpio_extcon_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
- data->pdata = pdata;
- /* Initialize the gpio */
- ret = gpio_extcon_init(&pdev->dev, data);
- if (ret < 0)
- return ret;
+ /*
+ * FIXME: extcon_id represents the unique identifier of external
+ * connectors such as EXTCON_USB, EXTCON_DISP_HDMI and so on. extcon_id
+ * is necessary to register the extcon device. But, it's not yet
+ * developed to get the extcon id from device-tree or others.
+ * On later, it have to be solved.
+ */
+ if (!data->irq_flags || data->extcon_id > EXTCON_NONE)
+ return -EINVAL;
+
+ data->gpiod = devm_gpiod_get(dev, "extcon", GPIOD_IN);
+ if (IS_ERR(data->gpiod))
+ return PTR_ERR(data->gpiod);
+ data->irq = gpiod_to_irq(data->gpiod);
+ if (data->irq <= 0)
+ return data->irq;
/* Allocate the memory of extcon devie and register extcon device */
- data->edev = devm_extcon_dev_allocate(&pdev->dev, &pdata->extcon_id);
+ data->edev = devm_extcon_dev_allocate(dev, &data->extcon_id);
if (IS_ERR(data->edev)) {
- dev_err(&pdev->dev, "failed to allocate extcon device\n");
+ dev_err(dev, "failed to allocate extcon device\n");
return -ENOMEM;
}
- ret = devm_extcon_dev_register(&pdev->dev, data->edev);
+ ret = devm_extcon_dev_register(dev, data->edev);
if (ret < 0)
return ret;
@@ -130,8 +117,8 @@ static int gpio_extcon_probe(struct platform_device *pdev)
* Request the interrupt of gpio to detect whether external connector
* is attached or detached.
*/
- ret = devm_request_any_context_irq(&pdev->dev, data->irq,
- gpio_irq_handler, pdata->irq_flags,
+ ret = devm_request_any_context_irq(dev, data->irq,
+ gpio_irq_handler, data->irq_flags,
pdev->name, data);
if (ret < 0)
return ret;
@@ -158,7 +145,7 @@ static int gpio_extcon_resume(struct device *dev)
struct gpio_extcon_data *data;
data = dev_get_drvdata(dev);
- if (data->pdata->check_on_resume)
+ if (data->check_on_resume)
queue_delayed_work(system_power_efficient_wq,
&data->work, data->debounce_jiffies);
diff --git a/drivers/extcon/extcon-intel-cht-wc.c b/drivers/extcon/extcon-intel-cht-wc.c
index 7c4bc8c44c3f..b7e9ea377d70 100644
--- a/drivers/extcon/extcon-intel-cht-wc.c
+++ b/drivers/extcon/extcon-intel-cht-wc.c
@@ -66,6 +66,8 @@
#define CHT_WC_VBUS_GPIO_CTLO 0x6e2d
#define CHT_WC_VBUS_GPIO_CTLO_OUTPUT BIT(0)
+#define CHT_WC_VBUS_GPIO_CTLO_DRV_OD BIT(4)
+#define CHT_WC_VBUS_GPIO_CTLO_DIR_OUT BIT(5)
enum cht_wc_usb_id {
USB_ID_OTG,
@@ -183,14 +185,15 @@ static void cht_wc_extcon_set_5v_boost(struct cht_wc_extcon_data *ext,
{
int ret, val;
- val = enable ? CHT_WC_VBUS_GPIO_CTLO_OUTPUT : 0;
-
/*
* The 5V boost converter is enabled through a gpio on the PMIC, since
* there currently is no gpio driver we access the gpio reg directly.
*/
- ret = regmap_update_bits(ext->regmap, CHT_WC_VBUS_GPIO_CTLO,
- CHT_WC_VBUS_GPIO_CTLO_OUTPUT, val);
+ val = CHT_WC_VBUS_GPIO_CTLO_DRV_OD | CHT_WC_VBUS_GPIO_CTLO_DIR_OUT;
+ if (enable)
+ val |= CHT_WC_VBUS_GPIO_CTLO_OUTPUT;
+
+ ret = regmap_write(ext->regmap, CHT_WC_VBUS_GPIO_CTLO, val);
if (ret)
dev_err(ext->dev, "Error writing Vbus GPIO CTLO: %d\n", ret);
}
diff --git a/drivers/extcon/extcon-intel-int3496.c b/drivers/extcon/extcon-intel-int3496.c
index 191e99f06a9a..acaccb128fc4 100644
--- a/drivers/extcon/extcon-intel-int3496.c
+++ b/drivers/extcon/extcon-intel-int3496.c
@@ -50,7 +50,11 @@ static const struct acpi_gpio_params vbus_gpios = { INT3496_GPIO_VBUS_EN, 0, fal
static const struct acpi_gpio_params mux_gpios = { INT3496_GPIO_USB_MUX, 0, false };
static const struct acpi_gpio_mapping acpi_int3496_default_gpios[] = {
- { "id-gpios", &id_gpios, 1 },
+ /*
+ * Some platforms have a bug in ACPI GPIO description making IRQ
+ * GPIO to be output only. Ask the GPIO core to ignore this limit.
+ */
+ { "id-gpios", &id_gpios, 1, ACPI_GPIO_QUIRK_NO_IO_RESTRICTION },
{ "vbus-gpios", &vbus_gpios, 1 },
{ "mux-gpios", &mux_gpios, 1 },
{ },
@@ -112,9 +116,6 @@ static int int3496_probe(struct platform_device *pdev)
ret = PTR_ERR(data->gpio_usb_id);
dev_err(dev, "can't request USB ID GPIO: %d\n", ret);
return ret;
- } else if (gpiod_get_direction(data->gpio_usb_id) != GPIOF_DIR_IN) {
- dev_warn(dev, FW_BUG "USB ID GPIO not in input mode, fixing\n");
- gpiod_direction_input(data->gpio_usb_id);
}
data->usb_id_irq = gpiod_to_irq(data->gpio_usb_id);
diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c
index cb38c2747684..8bff5fd18185 100644
--- a/drivers/extcon/extcon.c
+++ b/drivers/extcon/extcon.c
@@ -1336,6 +1336,28 @@ void extcon_dev_unregister(struct extcon_dev *edev)
EXPORT_SYMBOL_GPL(extcon_dev_unregister);
#ifdef CONFIG_OF
+
+/*
+ * extcon_find_edev_by_node - Find the extcon device from devicetree.
+ * @node : OF node identifying edev
+ *
+ * Return the pointer of extcon device if success or ERR_PTR(err) if fail.
+ */
+struct extcon_dev *extcon_find_edev_by_node(struct device_node *node)
+{
+ struct extcon_dev *edev;
+
+ mutex_lock(&extcon_dev_list_lock);
+ list_for_each_entry(edev, &extcon_dev_list, entry)
+ if (edev->dev.parent && edev->dev.parent->of_node == node)
+ goto out;
+ edev = ERR_PTR(-EPROBE_DEFER);
+out:
+ mutex_unlock(&extcon_dev_list_lock);
+
+ return edev;
+}
+
/*
* extcon_get_edev_by_phandle - Get the extcon device from devicetree.
* @dev : the instance to the given device
@@ -1363,25 +1385,27 @@ struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index)
return ERR_PTR(-ENODEV);
}
- mutex_lock(&extcon_dev_list_lock);
- list_for_each_entry(edev, &extcon_dev_list, entry) {
- if (edev->dev.parent && edev->dev.parent->of_node == node) {
- mutex_unlock(&extcon_dev_list_lock);
- of_node_put(node);
- return edev;
- }
- }
- mutex_unlock(&extcon_dev_list_lock);
+ edev = extcon_find_edev_by_node(node);
of_node_put(node);
- return ERR_PTR(-EPROBE_DEFER);
+ return edev;
}
+
#else
+
+struct extcon_dev *extcon_find_edev_by_node(struct device_node *node)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index)
{
return ERR_PTR(-ENOSYS);
}
+
#endif /* CONFIG_OF */
+
+EXPORT_SYMBOL_GPL(extcon_find_edev_by_node);
EXPORT_SYMBOL_GPL(extcon_get_edev_by_phandle);
/**
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index b7c748248e53..6e83880046d7 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -19,6 +19,40 @@ config ARM_PSCI_CHECKER
on and off through hotplug, so for now torture tests and PSCI checker
are mutually exclusive.
+config ARM_SCMI_PROTOCOL
+ bool "ARM System Control and Management Interface (SCMI) Message Protocol"
+ depends on ARM || ARM64 || COMPILE_TEST
+ depends on MAILBOX
+ help
+ ARM System Control and Management Interface (SCMI) protocol is a
+ set of operating system-independent software interfaces that are
+ used in system management. SCMI is extensible and currently provides
+ interfaces for: Discovery and self-description of the interfaces
+ it supports, Power domain management which is the ability to place
+ a given device or domain into the various power-saving states that
+ it supports, Performance management which is the ability to control
+ the performance of a domain that is composed of compute engines
+ such as application processors and other accelerators, Clock
+ management which is the ability to set and inquire rates on platform
+ managed clocks and Sensor management which is the ability to read
+ sensor data, and be notified of sensor value.
+
+ This protocol library provides interface for all the client drivers
+ making use of the features offered by the SCMI.
+
+config ARM_SCMI_POWER_DOMAIN
+ tristate "SCMI power domain driver"
+ depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
+ default y
+ select PM_GENERIC_DOMAINS if PM
+ help
+ This enables support for the SCMI power domains which can be
+ enabled or disabled via the SCP firmware
+
+ This driver can also be built as a module. If so, the module
+ will be called scmi_pm_domain. Note this may needed early in boot
+ before rootfs may be available.
+
config ARM_SCPI_PROTOCOL
tristate "ARM System Control and Power Interface (SCPI) Message Protocol"
depends on ARM || ARM64 || COMPILE_TEST
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index b248238ddc6a..e18a041cfc53 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_QCOM_SCM_32) += qcom_scm-32.o
CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch armv7-a\n.arch_extension sec,-DREQUIRES_SEC=1) -march=armv7-a
obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o
+obj-$(CONFIG_ARM_SCMI_PROTOCOL) += arm_scmi/
obj-y += broadcom/
obj-y += meson/
obj-$(CONFIG_GOOGLE_FIRMWARE) += google/
diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
new file mode 100644
index 000000000000..99e36c580fbc
--- /dev/null
+++ b/drivers/firmware/arm_scmi/Makefile
@@ -0,0 +1,5 @@
+obj-y = scmi-bus.o scmi-driver.o scmi-protocols.o
+scmi-bus-y = bus.o
+scmi-driver-y = driver.o
+scmi-protocols-y = base.o clock.o perf.o power.o sensors.o
+obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o
diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c
new file mode 100644
index 000000000000..0d3806c0d432
--- /dev/null
+++ b/drivers/firmware/arm_scmi/base.c
@@ -0,0 +1,253 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Management Interface (SCMI) Base Protocol
+ *
+ * Copyright (C) 2018 ARM Ltd.
+ */
+
+#include "common.h"
+
+enum scmi_base_protocol_cmd {
+ BASE_DISCOVER_VENDOR = 0x3,
+ BASE_DISCOVER_SUB_VENDOR = 0x4,
+ BASE_DISCOVER_IMPLEMENT_VERSION = 0x5,
+ BASE_DISCOVER_LIST_PROTOCOLS = 0x6,
+ BASE_DISCOVER_AGENT = 0x7,
+ BASE_NOTIFY_ERRORS = 0x8,
+};
+
+struct scmi_msg_resp_base_attributes {
+ u8 num_protocols;
+ u8 num_agents;
+ __le16 reserved;
+};
+
+/**
+ * scmi_base_attributes_get() - gets the implementation details
+ * that are associated with the base protocol.
+ *
+ * @handle - SCMI entity handle
+ *
+ * Return: 0 on success, else appropriate SCMI error.
+ */
+static int scmi_base_attributes_get(const struct scmi_handle *handle)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_msg_resp_base_attributes *attr_info;
+ struct scmi_revision_info *rev = handle->version;
+
+ ret = scmi_one_xfer_init(handle, PROTOCOL_ATTRIBUTES,
+ SCMI_PROTOCOL_BASE, 0, sizeof(*attr_info), &t);
+ if (ret)
+ return ret;
+
+ ret = scmi_do_xfer(handle, t);
+ if (!ret) {
+ attr_info = t->rx.buf;
+ rev->num_protocols = attr_info->num_protocols;
+ rev->num_agents = attr_info->num_agents;
+ }
+
+ scmi_one_xfer_put(handle, t);
+ return ret;
+}
+
+/**
+ * scmi_base_vendor_id_get() - gets vendor/subvendor identifier ASCII string.
+ *
+ * @handle - SCMI entity handle
+ * @sub_vendor - specify true if sub-vendor ID is needed
+ *
+ * Return: 0 on success, else appropriate SCMI error.
+ */
+static int
+scmi_base_vendor_id_get(const struct scmi_handle *handle, bool sub_vendor)
+{
+ u8 cmd;
+ int ret, size;
+ char *vendor_id;
+ struct scmi_xfer *t;
+ struct scmi_revision_info *rev = handle->version;
+
+ if (sub_vendor) {
+ cmd = BASE_DISCOVER_SUB_VENDOR;
+ vendor_id = rev->sub_vendor_id;
+ size = ARRAY_SIZE(rev->sub_vendor_id);
+ } else {
+ cmd = BASE_DISCOVER_VENDOR;
+ vendor_id = rev->vendor_id;
+ size = ARRAY_SIZE(rev->vendor_id);
+ }
+
+ ret = scmi_one_xfer_init(handle, cmd, SCMI_PROTOCOL_BASE, 0, size, &t);
+ if (ret)
+ return ret;
+
+ ret = scmi_do_xfer(handle, t);
+ if (!ret)
+ memcpy(vendor_id, t->rx.buf, size);
+
+ scmi_one_xfer_put(handle, t);
+ return ret;
+}
+
+/**
+ * scmi_base_implementation_version_get() - gets a vendor-specific
+ * implementation 32-bit version. The format of the version number is
+ * vendor-specific
+ *
+ * @handle - SCMI entity handle
+ *
+ * Return: 0 on success, else appropriate SCMI error.
+ */
+static int
+scmi_base_implementation_version_get(const struct scmi_handle *handle)
+{
+ int ret;
+ __le32 *impl_ver;
+ struct scmi_xfer *t;
+ struct scmi_revision_info *rev = handle->version;
+
+ ret = scmi_one_xfer_init(handle, BASE_DISCOVER_IMPLEMENT_VERSION,
+ SCMI_PROTOCOL_BASE, 0, sizeof(*impl_ver), &t);
+ if (ret)
+ return ret;
+
+ ret = scmi_do_xfer(handle, t);
+ if (!ret) {
+ impl_ver = t->rx.buf;
+ rev->impl_ver = le32_to_cpu(*impl_ver);
+ }
+
+ scmi_one_xfer_put(handle, t);
+ return ret;
+}
+
+/**
+ * scmi_base_implementation_list_get() - gets the list of protocols it is
+ * OSPM is allowed to access
+ *
+ * @handle - SCMI entity handle
+ * @protocols_imp - pointer to hold the list of protocol identifiers
+ *
+ * Return: 0 on success, else appropriate SCMI error.
+ */
+static int scmi_base_implementation_list_get(const struct scmi_handle *handle,
+ u8 *protocols_imp)
+{
+ u8 *list;
+ int ret, loop;
+ struct scmi_xfer *t;
+ __le32 *num_skip, *num_ret;
+ u32 tot_num_ret = 0, loop_num_ret;
+ struct device *dev = handle->dev;
+
+ ret = scmi_one_xfer_init(handle, BASE_DISCOVER_LIST_PROTOCOLS,
+ SCMI_PROTOCOL_BASE, sizeof(*num_skip), 0, &t);
+ if (ret)
+ return ret;
+
+ num_skip = t->tx.buf;
+ num_ret = t->rx.buf;
+ list = t->rx.buf + sizeof(*num_ret);
+
+ do {
+ /* Set the number of protocols to be skipped/already read */
+ *num_skip = cpu_to_le32(tot_num_ret);
+
+ ret = scmi_do_xfer(handle, t);
+ if (ret)
+ break;
+
+ loop_num_ret = le32_to_cpu(*num_ret);
+ if (tot_num_ret + loop_num_ret > MAX_PROTOCOLS_IMP) {
+ dev_err(dev, "No. of Protocol > MAX_PROTOCOLS_IMP");
+ break;
+ }
+
+ for (loop = 0; loop < loop_num_ret; loop++)
+ protocols_imp[tot_num_ret + loop] = *(list + loop);
+
+ tot_num_ret += loop_num_ret;
+ } while (loop_num_ret);
+
+ scmi_one_xfer_put(handle, t);
+ return ret;
+}
+
+/**
+ * scmi_base_discover_agent_get() - discover the name of an agent
+ *
+ * @handle - SCMI entity handle
+ * @id - Agent identifier
+ * @name - Agent identifier ASCII string
+ *
+ * An agent id of 0 is reserved to identify the platform itself.
+ * Generally operating system is represented as "OSPM"
+ *
+ * Return: 0 on success, else appropriate SCMI error.
+ */
+static int scmi_base_discover_agent_get(const struct scmi_handle *handle,
+ int id, char *name)
+{
+ int ret;
+ struct scmi_xfer *t;
+
+ ret = scmi_one_xfer_init(handle, BASE_DISCOVER_AGENT,
+ SCMI_PROTOCOL_BASE, sizeof(__le32),
+ SCMI_MAX_STR_SIZE, &t);
+ if (ret)
+ return ret;
+
+ *(__le32 *)t->tx.buf = cpu_to_le32(id);
+
+ ret = scmi_do_xfer(handle, t);
+ if (!ret)
+ memcpy(name, t->rx.buf, SCMI_MAX_STR_SIZE);
+
+ scmi_one_xfer_put(handle, t);
+ return ret;
+}
+
+int scmi_base_protocol_init(struct scmi_handle *h)
+{
+ int id, ret;
+ u8 *prot_imp;
+ u32 version;
+ char name[SCMI_MAX_STR_SIZE];
+ const struct scmi_handle *handle = h;
+ struct device *dev = handle->dev;
+ struct scmi_revision_info *rev = handle->version;
+
+ ret = scmi_version_get(handle, SCMI_PROTOCOL_BASE, &version);
+ if (ret)
+ return ret;
+
+ prot_imp = devm_kcalloc(dev, MAX_PROTOCOLS_IMP, sizeof(u8), GFP_KERNEL);
+ if (!prot_imp)
+ return -ENOMEM;
+
+ rev->major_ver = PROTOCOL_REV_MAJOR(version),
+ rev->minor_ver = PROTOCOL_REV_MINOR(version);
+
+ scmi_base_attributes_get(handle);
+ scmi_base_vendor_id_get(handle, false);
+ scmi_base_vendor_id_get(handle, true);
+ scmi_base_implementation_version_get(handle);
+ scmi_base_implementation_list_get(handle, prot_imp);
+ scmi_setup_protocol_implemented(handle, prot_imp);
+
+ dev_info(dev, "SCMI Protocol v%d.%d '%s:%s' Firmware version 0x%x\n",
+ rev->major_ver, rev->minor_ver, rev->vendor_id,
+ rev->sub_vendor_id, rev->impl_ver);
+ dev_dbg(dev, "Found %d protocol(s) %d agent(s)\n", rev->num_protocols,
+ rev->num_agents);
+
+ for (id = 0; id < rev->num_agents; id++) {
+ scmi_base_discover_agent_get(handle, id, name);
+ dev_dbg(dev, "Agent %d: %s\n", id, name);
+ }
+
+ return 0;
+}
diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c
new file mode 100644
index 000000000000..f2760a596c28
--- /dev/null
+++ b/drivers/firmware/arm_scmi/bus.c
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Management Interface (SCMI) Message Protocol bus layer
+ *
+ * Copyright (C) 2018 ARM Ltd.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+
+#include "common.h"
+
+static DEFINE_IDA(scmi_bus_id);
+static DEFINE_IDR(scmi_protocols);
+static DEFINE_SPINLOCK(protocol_lock);
+
+static const struct scmi_device_id *
+scmi_dev_match_id(struct scmi_device *scmi_dev, struct scmi_driver *scmi_drv)
+{
+ const struct scmi_device_id *id = scmi_drv->id_table;
+
+ if (!id)
+ return NULL;
+
+ for (; id->protocol_id; id++)
+ if (id->protocol_id == scmi_dev->protocol_id)
+ return id;
+
+ return NULL;
+}
+
+static int scmi_dev_match(struct device *dev, struct device_driver *drv)
+{
+ struct scmi_driver *scmi_drv = to_scmi_driver(drv);
+ struct scmi_device *scmi_dev = to_scmi_dev(dev);
+ const struct scmi_device_id *id;
+
+ id = scmi_dev_match_id(scmi_dev, scmi_drv);
+ if (id)
+ return 1;
+
+ return 0;
+}
+
+static int scmi_protocol_init(int protocol_id, struct scmi_handle *handle)
+{
+ scmi_prot_init_fn_t fn = idr_find(&scmi_protocols, protocol_id);
+
+ if (unlikely(!fn))
+ return -EINVAL;
+ return fn(handle);
+}
+
+static int scmi_dev_probe(struct device *dev)
+{
+ struct scmi_driver *scmi_drv = to_scmi_driver(dev->driver);
+ struct scmi_device *scmi_dev = to_scmi_dev(dev);
+ const struct scmi_device_id *id;
+ int ret;
+
+ id = scmi_dev_match_id(scmi_dev, scmi_drv);
+ if (!id)
+ return -ENODEV;
+
+ if (!scmi_dev->handle)
+ return -EPROBE_DEFER;
+
+ ret = scmi_protocol_init(scmi_dev->protocol_id, scmi_dev->handle);
+ if (ret)
+ return ret;
+
+ return scmi_drv->probe(scmi_dev);
+}
+
+static int scmi_dev_remove(struct device *dev)
+{
+ struct scmi_driver *scmi_drv = to_scmi_driver(dev->driver);
+ struct scmi_device *scmi_dev = to_scmi_dev(dev);
+
+ if (scmi_drv->remove)
+ scmi_drv->remove(scmi_dev);
+
+ return 0;
+}
+
+static struct bus_type scmi_bus_type = {
+ .name = "scmi_protocol",
+ .match = scmi_dev_match,
+ .probe = scmi_dev_probe,
+ .remove = scmi_dev_remove,
+};
+
+int scmi_driver_register(struct scmi_driver *driver, struct module *owner,
+ const char *mod_name)
+{
+ int retval;
+
+ driver->driver.bus = &scmi_bus_type;
+ driver->driver.name = driver->name;
+ driver->driver.owner = owner;
+ driver->driver.mod_name = mod_name;
+
+ retval = driver_register(&driver->driver);
+ if (!retval)
+ pr_debug("registered new scmi driver %s\n", driver->name);
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(scmi_driver_register);
+
+void scmi_driver_unregister(struct scmi_driver *driver)
+{
+ driver_unregister(&driver->driver);
+}
+EXPORT_SYMBOL_GPL(scmi_driver_unregister);
+
+struct scmi_device *
+scmi_device_create(struct device_node *np, struct device *parent, int protocol)
+{
+ int id, retval;
+ struct scmi_device *scmi_dev;
+
+ id = ida_simple_get(&scmi_bus_id, 1, 0, GFP_KERNEL);
+ if (id < 0)
+ return NULL;
+
+ scmi_dev = kzalloc(sizeof(*scmi_dev), GFP_KERNEL);
+ if (!scmi_dev)
+ goto no_mem;
+
+ scmi_dev->id = id;
+ scmi_dev->protocol_id = protocol;
+ scmi_dev->dev.parent = parent;
+ scmi_dev->dev.of_node = np;
+ scmi_dev->dev.bus = &scmi_bus_type;
+ dev_set_name(&scmi_dev->dev, "scmi_dev.%d", id);
+
+ retval = device_register(&scmi_dev->dev);
+ if (!retval)
+ return scmi_dev;
+
+ put_device(&scmi_dev->dev);
+ kfree(scmi_dev);
+no_mem:
+ ida_simple_remove(&scmi_bus_id, id);
+ return NULL;
+}
+
+void scmi_device_destroy(struct scmi_device *scmi_dev)
+{
+ scmi_handle_put(scmi_dev->handle);
+ device_unregister(&scmi_dev->dev);
+ ida_simple_remove(&scmi_bus_id, scmi_dev->id);
+ kfree(scmi_dev);
+}
+
+void scmi_set_handle(struct scmi_device *scmi_dev)
+{
+ scmi_dev->handle = scmi_handle_get(&scmi_dev->dev);
+}
+
+int scmi_protocol_register(int protocol_id, scmi_prot_init_fn_t fn)
+{
+ int ret;
+
+ spin_lock(&protocol_lock);
+ ret = idr_alloc(&scmi_protocols, fn, protocol_id, protocol_id + 1,
+ GFP_ATOMIC);
+ if (ret != protocol_id)
+ pr_err("unable to allocate SCMI idr slot, err %d\n", ret);
+ spin_unlock(&protocol_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(scmi_protocol_register);
+
+void scmi_protocol_unregister(int protocol_id)
+{
+ spin_lock(&protocol_lock);
+ idr_remove(&scmi_protocols, protocol_id);
+ spin_unlock(&protocol_lock);
+}
+EXPORT_SYMBOL_GPL(scmi_protocol_unregister);
+
+static int __scmi_devices_unregister(struct device *dev, void *data)
+{
+ struct scmi_device *scmi_dev = to_scmi_dev(dev);
+
+ scmi_device_destroy(scmi_dev);
+ return 0;
+}
+
+static void scmi_devices_unregister(void)
+{
+ bus_for_each_dev(&scmi_bus_type, NULL, NULL, __scmi_devices_unregister);
+}
+
+static int __init scmi_bus_init(void)
+{
+ int retval;
+
+ retval = bus_register(&scmi_bus_type);
+ if (retval)
+ pr_err("scmi protocol bus register failed (%d)\n", retval);
+
+ return retval;
+}
+subsys_initcall(scmi_bus_init);
+
+static void __exit scmi_bus_exit(void)
+{
+ scmi_devices_unregister();
+ bus_unregister(&scmi_bus_type);
+ ida_destroy(&scmi_bus_id);
+}
+module_exit(scmi_bus_exit);
diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c
new file mode 100644
index 000000000000..2b90606452a2
--- /dev/null
+++ b/drivers/firmware/arm_scmi/clock.c
@@ -0,0 +1,343 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Management Interface (SCMI) Clock Protocol
+ *
+ * Copyright (C) 2018 ARM Ltd.
+ */
+
+#include "common.h"
+
+enum scmi_clock_protocol_cmd {
+ CLOCK_ATTRIBUTES = 0x3,
+ CLOCK_DESCRIBE_RATES = 0x4,
+ CLOCK_RATE_SET = 0x5,
+ CLOCK_RATE_GET = 0x6,
+ CLOCK_CONFIG_SET = 0x7,
+};
+
+struct scmi_msg_resp_clock_protocol_attributes {
+ __le16 num_clocks;
+ u8 max_async_req;
+ u8 reserved;
+};
+
+struct scmi_msg_resp_clock_attributes {
+ __le32 attributes;
+#define CLOCK_ENABLE BIT(0)
+ u8 name[SCMI_MAX_STR_SIZE];
+};
+
+struct scmi_clock_set_config {
+ __le32 id;
+ __le32 attributes;
+};
+
+struct scmi_msg_clock_describe_rates {
+ __le32 id;
+ __le32 rate_index;
+};
+
+struct scmi_msg_resp_clock_describe_rates {
+ __le32 num_rates_flags;
+#define NUM_RETURNED(x) ((x) & 0xfff)
+#define RATE_DISCRETE(x) !((x) & BIT(12))
+#define NUM_REMAINING(x) ((x) >> 16)
+ struct {
+ __le32 value_low;
+ __le32 value_high;
+ } rate[0];
+#define RATE_TO_U64(X) \
+({ \
+ typeof(X) x = (X); \
+ le32_to_cpu((x).value_low) | (u64)le32_to_cpu((x).value_high) << 32; \
+})
+};
+
+struct scmi_clock_set_rate {
+ __le32 flags;
+#define CLOCK_SET_ASYNC BIT(0)
+#define CLOCK_SET_DELAYED BIT(1)
+#define CLOCK_SET_ROUND_UP BIT(2)
+#define CLOCK_SET_ROUND_AUTO BIT(3)
+ __le32 id;
+ __le32 value_low;
+ __le32 value_high;
+};
+
+struct clock_info {
+ int num_clocks;
+ int max_async_req;
+ struct scmi_clock_info *clk;
+};
+
+static int scmi_clock_protocol_attributes_get(const struct scmi_handle *handle,
+ struct clock_info *ci)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_msg_resp_clock_protocol_attributes *attr;
+
+ ret = scmi_one_xfer_init(handle, PROTOCOL_ATTRIBUTES,
+ SCMI_PROTOCOL_CLOCK, 0, sizeof(*attr), &t);
+ if (ret)
+ return ret;
+
+ attr = t->rx.buf;
+
+ ret = scmi_do_xfer(handle, t);
+ if (!ret) {
+ ci->num_clocks = le16_to_cpu(attr->num_clocks);
+ ci->max_async_req = attr->max_async_req;
+ }
+
+ scmi_one_xfer_put(handle, t);
+ return ret;
+}
+
+static int scmi_clock_attributes_get(const struct scmi_handle *handle,
+ u32 clk_id, struct scmi_clock_info *clk)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_msg_resp_clock_attributes *attr;
+
+ ret = scmi_one_xfer_init(handle, CLOCK_ATTRIBUTES, SCMI_PROTOCOL_CLOCK,
+ sizeof(clk_id), sizeof(*attr), &t);
+ if (ret)
+ return ret;
+
+ *(__le32 *)t->tx.buf = cpu_to_le32(clk_id);
+ attr = t->rx.buf;
+
+ ret = scmi_do_xfer(handle, t);
+ if (!ret)
+ memcpy(clk->name, attr->name, SCMI_MAX_STR_SIZE);
+ else
+ clk->name[0] = '\0';
+
+ scmi_one_xfer_put(handle, t);
+ return ret;
+}
+
+static int
+scmi_clock_describe_rates_get(const struct scmi_handle *handle, u32 clk_id,
+ struct scmi_clock_info *clk)
+{
+ u64 *rate;
+ int ret, cnt;
+ bool rate_discrete = false;
+ u32 tot_rate_cnt = 0, rates_flag;
+ u16 num_returned, num_remaining;
+ struct scmi_xfer *t;
+ struct scmi_msg_clock_describe_rates *clk_desc;
+ struct scmi_msg_resp_clock_describe_rates *rlist;
+
+ ret = scmi_one_xfer_init(handle, CLOCK_DESCRIBE_RATES,
+ SCMI_PROTOCOL_CLOCK, sizeof(*clk_desc), 0, &t);
+ if (ret)
+ return ret;
+
+ clk_desc = t->tx.buf;
+ rlist = t->rx.buf;
+
+ do {
+ clk_desc->id = cpu_to_le32(clk_id);
+ /* Set the number of rates to be skipped/already read */
+ clk_desc->rate_index = cpu_to_le32(tot_rate_cnt);
+
+ ret = scmi_do_xfer(handle, t);
+ if (ret)
+ goto err;
+
+ rates_flag = le32_to_cpu(rlist->num_rates_flags);
+ num_remaining = NUM_REMAINING(rates_flag);
+ rate_discrete = RATE_DISCRETE(rates_flag);
+ num_returned = NUM_RETURNED(rates_flag);
+
+ if (tot_rate_cnt + num_returned > SCMI_MAX_NUM_RATES) {
+ dev_err(handle->dev, "No. of rates > MAX_NUM_RATES");
+ break;
+ }
+
+ if (!rate_discrete) {
+ clk->range.min_rate = RATE_TO_U64(rlist->rate[0]);
+ clk->range.max_rate = RATE_TO_U64(rlist->rate[1]);
+ clk->range.step_size = RATE_TO_U64(rlist->rate[2]);
+ dev_dbg(handle->dev, "Min %llu Max %llu Step %llu Hz\n",
+ clk->range.min_rate, clk->range.max_rate,
+ clk->range.step_size);
+ break;
+ }
+
+ rate = &clk->list.rates[tot_rate_cnt];
+ for (cnt = 0; cnt < num_returned; cnt++, rate++) {
+ *rate = RATE_TO_U64(rlist->rate[cnt]);
+ dev_dbg(handle->dev, "Rate %llu Hz\n", *rate);
+ }
+
+ tot_rate_cnt += num_returned;
+ /*
+ * check for both returned and remaining to avoid infinite
+ * loop due to buggy firmware
+ */
+ } while (num_returned && num_remaining);
+
+ if (rate_discrete)
+ clk->list.num_rates = tot_rate_cnt;
+
+err:
+ scmi_one_xfer_put(handle, t);
+ return ret;
+}
+
+static int
+scmi_clock_rate_get(const struct scmi_handle *handle, u32 clk_id, u64 *value)
+{
+ int ret;
+ struct scmi_xfer *t;
+
+ ret = scmi_one_xfer_init(handle, CLOCK_RATE_GET, SCMI_PROTOCOL_CLOCK,
+ sizeof(__le32), sizeof(u64), &t);
+ if (ret)
+ return ret;
+
+ *(__le32 *)t->tx.buf = cpu_to_le32(clk_id);
+
+ ret = scmi_do_xfer(handle, t);
+ if (!ret) {
+ __le32 *pval = t->rx.buf;
+
+ *value = le32_to_cpu(*pval);
+ *value |= (u64)le32_to_cpu(*(pval + 1)) << 32;
+ }
+
+ scmi_one_xfer_put(handle, t);
+ return ret;
+}
+
+static int scmi_clock_rate_set(const struct scmi_handle *handle, u32 clk_id,
+ u32 config, u64 rate)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_clock_set_rate *cfg;
+
+ ret = scmi_one_xfer_init(handle, CLOCK_RATE_SET, SCMI_PROTOCOL_CLOCK,
+ sizeof(*cfg), 0, &t);
+ if (ret)
+ return ret;
+
+ cfg = t->tx.buf;
+ cfg->flags = cpu_to_le32(config);
+ cfg->id = cpu_to_le32(clk_id);
+ cfg->value_low = cpu_to_le32(rate & 0xffffffff);
+ cfg->value_high = cpu_to_le32(rate >> 32);
+
+ ret = scmi_do_xfer(handle, t);
+
+ scmi_one_xfer_put(handle, t);
+ return ret;
+}
+
+static int
+scmi_clock_config_set(const struct scmi_handle *handle, u32 clk_id, u32 config)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_clock_set_config *cfg;
+
+ ret = scmi_one_xfer_init(handle, CLOCK_CONFIG_SET, SCMI_PROTOCOL_CLOCK,
+ sizeof(*cfg), 0, &t);
+ if (ret)
+ return ret;
+
+ cfg = t->tx.buf;
+ cfg->id = cpu_to_le32(clk_id);
+ cfg->attributes = cpu_to_le32(config);
+
+ ret = scmi_do_xfer(handle, t);
+
+ scmi_one_xfer_put(handle, t);
+ return ret;
+}
+
+static int scmi_clock_enable(const struct scmi_handle *handle, u32 clk_id)
+{
+ return scmi_clock_config_set(handle, clk_id, CLOCK_ENABLE);
+}
+
+static int scmi_clock_disable(const struct scmi_handle *handle, u32 clk_id)
+{
+ return scmi_clock_config_set(handle, clk_id, 0);
+}
+
+static int scmi_clock_count_get(const struct scmi_handle *handle)
+{
+ struct clock_info *ci = handle->clk_priv;
+
+ return ci->num_clocks;
+}
+
+static const struct scmi_clock_info *
+scmi_clock_info_get(const struct scmi_handle *handle, u32 clk_id)
+{
+ struct clock_info *ci = handle->clk_priv;
+ struct scmi_clock_info *clk = ci->clk + clk_id;
+
+ if (!clk->name[0])
+ return NULL;
+
+ return clk;
+}
+
+static struct scmi_clk_ops clk_ops = {
+ .count_get = scmi_clock_count_get,
+ .info_get = scmi_clock_info_get,
+ .rate_get = scmi_clock_rate_get,
+ .rate_set = scmi_clock_rate_set,
+ .enable = scmi_clock_enable,
+ .disable = scmi_clock_disable,
+};
+
+static int scmi_clock_protocol_init(struct scmi_handle *handle)
+{
+ u32 version;
+ int clkid, ret;
+ struct clock_info *cinfo;
+
+ scmi_version_get(handle, SCMI_PROTOCOL_CLOCK, &version);
+
+ dev_dbg(handle->dev, "Clock Version %d.%d\n",
+ PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+
+ cinfo = devm_kzalloc(handle->dev, sizeof(*cinfo), GFP_KERNEL);
+ if (!cinfo)
+ return -ENOMEM;
+
+ scmi_clock_protocol_attributes_get(handle, cinfo);
+
+ cinfo->clk = devm_kcalloc(handle->dev, cinfo->num_clocks,
+ sizeof(*cinfo->clk), GFP_KERNEL);
+ if (!cinfo->clk)
+ return -ENOMEM;
+
+ for (clkid = 0; clkid < cinfo->num_clocks; clkid++) {
+ struct scmi_clock_info *clk = cinfo->clk + clkid;
+
+ ret = scmi_clock_attributes_get(handle, clkid, clk);
+ if (!ret)
+ scmi_clock_describe_rates_get(handle, clkid, clk);
+ }
+
+ handle->clk_ops = &clk_ops;
+ handle->clk_priv = cinfo;
+
+ return 0;
+}
+
+static int __init scmi_clock_init(void)
+{
+ return scmi_protocol_register(SCMI_PROTOCOL_CLOCK,
+ &scmi_clock_protocol_init);
+}
+subsys_initcall(scmi_clock_init);
diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
new file mode 100644
index 000000000000..0c30234f9098
--- /dev/null
+++ b/drivers/firmware/arm_scmi/common.h
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Management Interface (SCMI) Message Protocol
+ * driver common header file containing some definitions, structures
+ * and function prototypes used in all the different SCMI protocols.
+ *
+ * Copyright (C) 2018 ARM Ltd.
+ */
+
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/scmi_protocol.h>
+#include <linux/types.h>
+
+#define PROTOCOL_REV_MINOR_BITS 16
+#define PROTOCOL_REV_MINOR_MASK ((1U << PROTOCOL_REV_MINOR_BITS) - 1)
+#define PROTOCOL_REV_MAJOR(x) ((x) >> PROTOCOL_REV_MINOR_BITS)
+#define PROTOCOL_REV_MINOR(x) ((x) & PROTOCOL_REV_MINOR_MASK)
+#define MAX_PROTOCOLS_IMP 16
+#define MAX_OPPS 16
+
+enum scmi_common_cmd {
+ PROTOCOL_VERSION = 0x0,
+ PROTOCOL_ATTRIBUTES = 0x1,
+ PROTOCOL_MESSAGE_ATTRIBUTES = 0x2,
+};
+
+/**
+ * struct scmi_msg_resp_prot_version - Response for a message
+ *
+ * @major_version: Major version of the ABI that firmware supports
+ * @minor_version: Minor version of the ABI that firmware supports
+ *
+ * In general, ABI version changes follow the rule that minor version increments
+ * are backward compatible. Major revision changes in ABI may not be
+ * backward compatible.
+ *
+ * Response to a generic message with message type SCMI_MSG_VERSION
+ */
+struct scmi_msg_resp_prot_version {
+ __le16 minor_version;
+ __le16 major_version;
+};
+
+/**
+ * struct scmi_msg_hdr - Message(Tx/Rx) header
+ *
+ * @id: The identifier of the command being sent
+ * @protocol_id: The identifier of the protocol used to send @id command
+ * @seq: The token to identify the message. when a message/command returns,
+ * the platform returns the whole message header unmodified including
+ * the token.
+ */
+struct scmi_msg_hdr {
+ u8 id;
+ u8 protocol_id;
+ u16 seq;
+ u32 status;
+ bool poll_completion;
+};
+
+/**
+ * struct scmi_msg - Message(Tx/Rx) structure
+ *
+ * @buf: Buffer pointer
+ * @len: Length of data in the Buffer
+ */
+struct scmi_msg {
+ void *buf;
+ size_t len;
+};
+
+/**
+ * struct scmi_xfer - Structure representing a message flow
+ *
+ * @hdr: Transmit message header
+ * @tx: Transmit message
+ * @rx: Receive message, the buffer should be pre-allocated to store
+ * message. If request-ACK protocol is used, we can reuse the same
+ * buffer for the rx path as we use for the tx path.
+ * @done: completion event
+ */
+
+struct scmi_xfer {
+ void *con_priv;
+ struct scmi_msg_hdr hdr;
+ struct scmi_msg tx;
+ struct scmi_msg rx;
+ struct completion done;
+};
+
+void scmi_one_xfer_put(const struct scmi_handle *h, struct scmi_xfer *xfer);
+int scmi_do_xfer(const struct scmi_handle *h, struct scmi_xfer *xfer);
+int scmi_one_xfer_init(const struct scmi_handle *h, u8 msg_id, u8 prot_id,
+ size_t tx_size, size_t rx_size, struct scmi_xfer **p);
+int scmi_handle_put(const struct scmi_handle *handle);
+struct scmi_handle *scmi_handle_get(struct device *dev);
+void scmi_set_handle(struct scmi_device *scmi_dev);
+int scmi_version_get(const struct scmi_handle *h, u8 protocol, u32 *version);
+void scmi_setup_protocol_implemented(const struct scmi_handle *handle,
+ u8 *prot_imp);
+
+int scmi_base_protocol_init(struct scmi_handle *h);
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
new file mode 100644
index 000000000000..14b147135a0c
--- /dev/null
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -0,0 +1,871 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Management Interface (SCMI) Message Protocol driver
+ *
+ * SCMI Message Protocol is used between the System Control Processor(SCP)
+ * and the Application Processors(AP). The Message Handling Unit(MHU)
+ * provides a mechanism for inter-processor communication between SCP's
+ * Cortex M3 and AP.
+ *
+ * SCP offers control and management of the core/cluster power states,
+ * various power domain DVFS including the core/cluster, certain system
+ * clocks configuration, thermal sensors and many others.
+ *
+ * Copyright (C) 2018 ARM Ltd.
+ */
+
+#include <linux/bitmap.h>
+#include <linux/export.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/ktime.h>
+#include <linux/mailbox_client.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/processor.h>
+#include <linux/semaphore.h>
+#include <linux/slab.h>
+
+#include "common.h"
+
+#define MSG_ID_SHIFT 0
+#define MSG_ID_MASK 0xff
+#define MSG_TYPE_SHIFT 8
+#define MSG_TYPE_MASK 0x3
+#define MSG_PROTOCOL_ID_SHIFT 10
+#define MSG_PROTOCOL_ID_MASK 0xff
+#define MSG_TOKEN_ID_SHIFT 18
+#define MSG_TOKEN_ID_MASK 0x3ff
+#define MSG_XTRACT_TOKEN(header) \
+ (((header) >> MSG_TOKEN_ID_SHIFT) & MSG_TOKEN_ID_MASK)
+
+enum scmi_error_codes {
+ SCMI_SUCCESS = 0, /* Success */
+ SCMI_ERR_SUPPORT = -1, /* Not supported */
+ SCMI_ERR_PARAMS = -2, /* Invalid Parameters */
+ SCMI_ERR_ACCESS = -3, /* Invalid access/permission denied */
+ SCMI_ERR_ENTRY = -4, /* Not found */
+ SCMI_ERR_RANGE = -5, /* Value out of range */
+ SCMI_ERR_BUSY = -6, /* Device busy */
+ SCMI_ERR_COMMS = -7, /* Communication Error */
+ SCMI_ERR_GENERIC = -8, /* Generic Error */
+ SCMI_ERR_HARDWARE = -9, /* Hardware Error */
+ SCMI_ERR_PROTOCOL = -10,/* Protocol Error */
+ SCMI_ERR_MAX
+};
+
+/* List of all SCMI devices active in system */
+static LIST_HEAD(scmi_list);
+/* Protection for the entire list */
+static DEFINE_MUTEX(scmi_list_mutex);
+
+/**
+ * struct scmi_xfers_info - Structure to manage transfer information
+ *
+ * @xfer_block: Preallocated Message array
+ * @xfer_alloc_table: Bitmap table for allocated messages.
+ * Index of this bitmap table is also used for message
+ * sequence identifier.
+ * @xfer_lock: Protection for message allocation
+ */
+struct scmi_xfers_info {
+ struct scmi_xfer *xfer_block;
+ unsigned long *xfer_alloc_table;
+ /* protect transfer allocation */
+ spinlock_t xfer_lock;
+};
+
+/**
+ * struct scmi_desc - Description of SoC integration
+ *
+ * @max_rx_timeout_ms: Timeout for communication with SoC (in Milliseconds)
+ * @max_msg: Maximum number of messages that can be pending
+ * simultaneously in the system
+ * @max_msg_size: Maximum size of data per message that can be handled.
+ */
+struct scmi_desc {
+ int max_rx_timeout_ms;
+ int max_msg;
+ int max_msg_size;
+};
+
+/**
+ * struct scmi_chan_info - Structure representing a SCMI channel informfation
+ *
+ * @cl: Mailbox Client
+ * @chan: Transmit/Receive mailbox channel
+ * @payload: Transmit/Receive mailbox channel payload area
+ * @dev: Reference to device in the SCMI hierarchy corresponding to this
+ * channel
+ */
+struct scmi_chan_info {
+ struct mbox_client cl;
+ struct mbox_chan *chan;
+ void __iomem *payload;
+ struct device *dev;
+ struct scmi_handle *handle;
+};
+
+/**
+ * struct scmi_info - Structure representing a SCMI instance
+ *
+ * @dev: Device pointer
+ * @desc: SoC description for this instance
+ * @handle: Instance of SCMI handle to send to clients
+ * @version: SCMI revision information containing protocol version,
+ * implementation version and (sub-)vendor identification.
+ * @minfo: Message info
+ * @tx_idr: IDR object to map protocol id to channel info pointer
+ * @protocols_imp: list of protocols implemented, currently maximum of
+ * MAX_PROTOCOLS_IMP elements allocated by the base protocol
+ * @node: list head
+ * @users: Number of users of this instance
+ */
+struct scmi_info {
+ struct device *dev;
+ const struct scmi_desc *desc;
+ struct scmi_revision_info version;
+ struct scmi_handle handle;
+ struct scmi_xfers_info minfo;
+ struct idr tx_idr;
+ u8 *protocols_imp;
+ struct list_head node;
+ int users;
+};
+
+#define client_to_scmi_chan_info(c) container_of(c, struct scmi_chan_info, cl)
+#define handle_to_scmi_info(h) container_of(h, struct scmi_info, handle)
+
+/*
+ * SCMI specification requires all parameters, message headers, return
+ * arguments or any protocol data to be expressed in little endian
+ * format only.
+ */
+struct scmi_shared_mem {
+ __le32 reserved;
+ __le32 channel_status;
+#define SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR BIT(1)
+#define SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE BIT(0)
+ __le32 reserved1[2];
+ __le32 flags;
+#define SCMI_SHMEM_FLAG_INTR_ENABLED BIT(0)
+ __le32 length;
+ __le32 msg_header;
+ u8 msg_payload[0];
+};
+
+static const int scmi_linux_errmap[] = {
+ /* better than switch case as long as return value is continuous */
+ 0, /* SCMI_SUCCESS */
+ -EOPNOTSUPP, /* SCMI_ERR_SUPPORT */
+ -EINVAL, /* SCMI_ERR_PARAM */
+ -EACCES, /* SCMI_ERR_ACCESS */
+ -ENOENT, /* SCMI_ERR_ENTRY */
+ -ERANGE, /* SCMI_ERR_RANGE */
+ -EBUSY, /* SCMI_ERR_BUSY */
+ -ECOMM, /* SCMI_ERR_COMMS */
+ -EIO, /* SCMI_ERR_GENERIC */
+ -EREMOTEIO, /* SCMI_ERR_HARDWARE */
+ -EPROTO, /* SCMI_ERR_PROTOCOL */
+};
+
+static inline int scmi_to_linux_errno(int errno)
+{
+ if (errno < SCMI_SUCCESS && errno > SCMI_ERR_MAX)
+ return scmi_linux_errmap[-errno];
+ return -EIO;
+}
+
+/**
+ * scmi_dump_header_dbg() - Helper to dump a message header.
+ *
+ * @dev: Device pointer corresponding to the SCMI entity
+ * @hdr: pointer to header.
+ */
+static inline void scmi_dump_header_dbg(struct device *dev,
+ struct scmi_msg_hdr *hdr)
+{
+ dev_dbg(dev, "Command ID: %x Sequence ID: %x Protocol: %x\n",
+ hdr->id, hdr->seq, hdr->protocol_id);
+}
+
+static void scmi_fetch_response(struct scmi_xfer *xfer,
+ struct scmi_shared_mem __iomem *mem)
+{
+ xfer->hdr.status = ioread32(mem->msg_payload);
+ /* Skip the length of header and statues in payload area i.e 8 bytes*/
+ xfer->rx.len = min_t(size_t, xfer->rx.len, ioread32(&mem->length) - 8);
+
+ /* Take a copy to the rx buffer.. */
+ memcpy_fromio(xfer->rx.buf, mem->msg_payload + 4, xfer->rx.len);
+}
+
+/**
+ * scmi_rx_callback() - mailbox client callback for receive messages
+ *
+ * @cl: client pointer
+ * @m: mailbox message
+ *
+ * Processes one received message to appropriate transfer information and
+ * signals completion of the transfer.
+ *
+ * NOTE: This function will be invoked in IRQ context, hence should be
+ * as optimal as possible.
+ */
+static void scmi_rx_callback(struct mbox_client *cl, void *m)
+{
+ u16 xfer_id;
+ struct scmi_xfer *xfer;
+ struct scmi_chan_info *cinfo = client_to_scmi_chan_info(cl);
+ struct device *dev = cinfo->dev;
+ struct scmi_info *info = handle_to_scmi_info(cinfo->handle);
+ struct scmi_xfers_info *minfo = &info->minfo;
+ struct scmi_shared_mem __iomem *mem = cinfo->payload;
+
+ xfer_id = MSG_XTRACT_TOKEN(ioread32(&mem->msg_header));
+
+ /*
+ * Are we even expecting this?
+ */
+ if (!test_bit(xfer_id, minfo->xfer_alloc_table)) {
+ dev_err(dev, "message for %d is not expected!\n", xfer_id);
+ return;
+ }
+
+ xfer = &minfo->xfer_block[xfer_id];
+
+ scmi_dump_header_dbg(dev, &xfer->hdr);
+ /* Is the message of valid length? */
+ if (xfer->rx.len > info->desc->max_msg_size) {
+ dev_err(dev, "unable to handle %zu xfer(max %d)\n",
+ xfer->rx.len, info->desc->max_msg_size);
+ return;
+ }
+
+ scmi_fetch_response(xfer, mem);
+ complete(&xfer->done);
+}
+
+/**
+ * pack_scmi_header() - packs and returns 32-bit header
+ *
+ * @hdr: pointer to header containing all the information on message id,
+ * protocol id and sequence id.
+ */
+static inline u32 pack_scmi_header(struct scmi_msg_hdr *hdr)
+{
+ return ((hdr->id & MSG_ID_MASK) << MSG_ID_SHIFT) |
+ ((hdr->seq & MSG_TOKEN_ID_MASK) << MSG_TOKEN_ID_SHIFT) |
+ ((hdr->protocol_id & MSG_PROTOCOL_ID_MASK) << MSG_PROTOCOL_ID_SHIFT);
+}
+
+/**
+ * scmi_tx_prepare() - mailbox client callback to prepare for the transfer
+ *
+ * @cl: client pointer
+ * @m: mailbox message
+ *
+ * This function prepares the shared memory which contains the header and the
+ * payload.
+ */
+static void scmi_tx_prepare(struct mbox_client *cl, void *m)
+{
+ struct scmi_xfer *t = m;
+ struct scmi_chan_info *cinfo = client_to_scmi_chan_info(cl);
+ struct scmi_shared_mem __iomem *mem = cinfo->payload;
+
+ /* Mark channel busy + clear error */
+ iowrite32(0x0, &mem->channel_status);
+ iowrite32(t->hdr.poll_completion ? 0 : SCMI_SHMEM_FLAG_INTR_ENABLED,
+ &mem->flags);
+ iowrite32(sizeof(mem->msg_header) + t->tx.len, &mem->length);
+ iowrite32(pack_scmi_header(&t->hdr), &mem->msg_header);
+ if (t->tx.buf)
+ memcpy_toio(mem->msg_payload, t->tx.buf, t->tx.len);
+}
+
+/**
+ * scmi_one_xfer_get() - Allocate one message
+ *
+ * @handle: SCMI entity handle
+ *
+ * Helper function which is used by various command functions that are
+ * exposed to clients of this driver for allocating a message traffic event.
+ *
+ * This function can sleep depending on pending requests already in the system
+ * for the SCMI entity. Further, this also holds a spinlock to maintain
+ * integrity of internal data structures.
+ *
+ * Return: 0 if all went fine, else corresponding error.
+ */
+static struct scmi_xfer *scmi_one_xfer_get(const struct scmi_handle *handle)
+{
+ u16 xfer_id;
+ struct scmi_xfer *xfer;
+ unsigned long flags, bit_pos;
+ struct scmi_info *info = handle_to_scmi_info(handle);
+ struct scmi_xfers_info *minfo = &info->minfo;
+
+ /* Keep the locked section as small as possible */
+ spin_lock_irqsave(&minfo->xfer_lock, flags);
+ bit_pos = find_first_zero_bit(minfo->xfer_alloc_table,
+ info->desc->max_msg);
+ if (bit_pos == info->desc->max_msg) {
+ spin_unlock_irqrestore(&minfo->xfer_lock, flags);
+ return ERR_PTR(-ENOMEM);
+ }
+ set_bit(bit_pos, minfo->xfer_alloc_table);
+ spin_unlock_irqrestore(&minfo->xfer_lock, flags);
+
+ xfer_id = bit_pos;
+
+ xfer = &minfo->xfer_block[xfer_id];
+ xfer->hdr.seq = xfer_id;
+ reinit_completion(&xfer->done);
+
+ return xfer;
+}
+
+/**
+ * scmi_one_xfer_put() - Release a message
+ *
+ * @minfo: transfer info pointer
+ * @xfer: message that was reserved by scmi_one_xfer_get
+ *
+ * This holds a spinlock to maintain integrity of internal data structures.
+ */
+void scmi_one_xfer_put(const struct scmi_handle *handle, struct scmi_xfer *xfer)
+{
+ unsigned long flags;
+ struct scmi_info *info = handle_to_scmi_info(handle);
+ struct scmi_xfers_info *minfo = &info->minfo;
+
+ /*
+ * Keep the locked section as small as possible
+ * NOTE: we might escape with smp_mb and no lock here..
+ * but just be conservative and symmetric.
+ */
+ spin_lock_irqsave(&minfo->xfer_lock, flags);
+ clear_bit(xfer->hdr.seq, minfo->xfer_alloc_table);
+ spin_unlock_irqrestore(&minfo->xfer_lock, flags);
+}
+
+static bool
+scmi_xfer_poll_done(const struct scmi_chan_info *cinfo, struct scmi_xfer *xfer)
+{
+ struct scmi_shared_mem __iomem *mem = cinfo->payload;
+ u16 xfer_id = MSG_XTRACT_TOKEN(ioread32(&mem->msg_header));
+
+ if (xfer->hdr.seq != xfer_id)
+ return false;
+
+ return ioread32(&mem->channel_status) &
+ (SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR |
+ SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE);
+}
+
+#define SCMI_MAX_POLL_TO_NS (100 * NSEC_PER_USEC)
+
+static bool scmi_xfer_done_no_timeout(const struct scmi_chan_info *cinfo,
+ struct scmi_xfer *xfer, ktime_t stop)
+{
+ ktime_t __cur = ktime_get();
+
+ return scmi_xfer_poll_done(cinfo, xfer) || ktime_after(__cur, stop);
+}
+
+/**
+ * scmi_do_xfer() - Do one transfer
+ *
+ * @info: Pointer to SCMI entity information
+ * @xfer: Transfer to initiate and wait for response
+ *
+ * Return: -ETIMEDOUT in case of no response, if transmit error,
+ * return corresponding error, else if all goes well,
+ * return 0.
+ */
+int scmi_do_xfer(const struct scmi_handle *handle, struct scmi_xfer *xfer)
+{
+ int ret;
+ int timeout;
+ struct scmi_info *info = handle_to_scmi_info(handle);
+ struct device *dev = info->dev;
+ struct scmi_chan_info *cinfo;
+
+ cinfo = idr_find(&info->tx_idr, xfer->hdr.protocol_id);
+ if (unlikely(!cinfo))
+ return -EINVAL;
+
+ ret = mbox_send_message(cinfo->chan, xfer);
+ if (ret < 0) {
+ dev_dbg(dev, "mbox send fail %d\n", ret);
+ return ret;
+ }
+
+ /* mbox_send_message returns non-negative value on success, so reset */
+ ret = 0;
+
+ if (xfer->hdr.poll_completion) {
+ ktime_t stop = ktime_add_ns(ktime_get(), SCMI_MAX_POLL_TO_NS);
+
+ spin_until_cond(scmi_xfer_done_no_timeout(cinfo, xfer, stop));
+
+ if (ktime_before(ktime_get(), stop))
+ scmi_fetch_response(xfer, cinfo->payload);
+ else
+ ret = -ETIMEDOUT;
+ } else {
+ /* And we wait for the response. */
+ timeout = msecs_to_jiffies(info->desc->max_rx_timeout_ms);
+ if (!wait_for_completion_timeout(&xfer->done, timeout)) {
+ dev_err(dev, "mbox timed out in resp(caller: %pS)\n",
+ (void *)_RET_IP_);
+ ret = -ETIMEDOUT;
+ }
+ }
+
+ if (!ret && xfer->hdr.status)
+ ret = scmi_to_linux_errno(xfer->hdr.status);
+
+ /*
+ * NOTE: we might prefer not to need the mailbox ticker to manage the
+ * transfer queueing since the protocol layer queues things by itself.
+ * Unfortunately, we have to kick the mailbox framework after we have
+ * received our message.
+ */
+ mbox_client_txdone(cinfo->chan, ret);
+
+ return ret;
+}
+
+/**
+ * scmi_one_xfer_init() - Allocate and initialise one message
+ *
+ * @handle: SCMI entity handle
+ * @msg_id: Message identifier
+ * @msg_prot_id: Protocol identifier for the message
+ * @tx_size: transmit message size
+ * @rx_size: receive message size
+ * @p: pointer to the allocated and initialised message
+ *
+ * This function allocates the message using @scmi_one_xfer_get and
+ * initialise the header.
+ *
+ * Return: 0 if all went fine with @p pointing to message, else
+ * corresponding error.
+ */
+int scmi_one_xfer_init(const struct scmi_handle *handle, u8 msg_id, u8 prot_id,
+ size_t tx_size, size_t rx_size, struct scmi_xfer **p)
+{
+ int ret;
+ struct scmi_xfer *xfer;
+ struct scmi_info *info = handle_to_scmi_info(handle);
+ struct device *dev = info->dev;
+
+ /* Ensure we have sane transfer sizes */
+ if (rx_size > info->desc->max_msg_size ||
+ tx_size > info->desc->max_msg_size)
+ return -ERANGE;
+
+ xfer = scmi_one_xfer_get(handle);
+ if (IS_ERR(xfer)) {
+ ret = PTR_ERR(xfer);
+ dev_err(dev, "failed to get free message slot(%d)\n", ret);
+ return ret;
+ }
+
+ xfer->tx.len = tx_size;
+ xfer->rx.len = rx_size ? : info->desc->max_msg_size;
+ xfer->hdr.id = msg_id;
+ xfer->hdr.protocol_id = prot_id;
+ xfer->hdr.poll_completion = false;
+
+ *p = xfer;
+ return 0;
+}
+
+/**
+ * scmi_version_get() - command to get the revision of the SCMI entity
+ *
+ * @handle: Handle to SCMI entity information
+ *
+ * Updates the SCMI information in the internal data structure.
+ *
+ * Return: 0 if all went fine, else return appropriate error.
+ */
+int scmi_version_get(const struct scmi_handle *handle, u8 protocol,
+ u32 *version)
+{
+ int ret;
+ __le32 *rev_info;
+ struct scmi_xfer *t;
+
+ ret = scmi_one_xfer_init(handle, PROTOCOL_VERSION, protocol, 0,
+ sizeof(*version), &t);
+ if (ret)
+ return ret;
+
+ ret = scmi_do_xfer(handle, t);
+ if (!ret) {
+ rev_info = t->rx.buf;
+ *version = le32_to_cpu(*rev_info);
+ }
+
+ scmi_one_xfer_put(handle, t);
+ return ret;
+}
+
+void scmi_setup_protocol_implemented(const struct scmi_handle *handle,
+ u8 *prot_imp)
+{
+ struct scmi_info *info = handle_to_scmi_info(handle);
+
+ info->protocols_imp = prot_imp;
+}
+
+static bool
+scmi_is_protocol_implemented(const struct scmi_handle *handle, u8 prot_id)
+{
+ int i;
+ struct scmi_info *info = handle_to_scmi_info(handle);
+
+ if (!info->protocols_imp)
+ return false;
+
+ for (i = 0; i < MAX_PROTOCOLS_IMP; i++)
+ if (info->protocols_imp[i] == prot_id)
+ return true;
+ return false;
+}
+
+/**
+ * scmi_handle_get() - Get the SCMI handle for a device
+ *
+ * @dev: pointer to device for which we want SCMI handle
+ *
+ * NOTE: The function does not track individual clients of the framework
+ * and is expected to be maintained by caller of SCMI protocol library.
+ * scmi_handle_put must be balanced with successful scmi_handle_get
+ *
+ * Return: pointer to handle if successful, NULL on error
+ */
+struct scmi_handle *scmi_handle_get(struct device *dev)
+{
+ struct list_head *p;
+ struct scmi_info *info;
+ struct scmi_handle *handle = NULL;
+
+ mutex_lock(&scmi_list_mutex);
+ list_for_each(p, &scmi_list) {
+ info = list_entry(p, struct scmi_info, node);
+ if (dev->parent == info->dev) {
+ handle = &info->handle;
+ info->users++;
+ break;
+ }
+ }
+ mutex_unlock(&scmi_list_mutex);
+
+ return handle;
+}
+
+/**
+ * scmi_handle_put() - Release the handle acquired by scmi_handle_get
+ *
+ * @handle: handle acquired by scmi_handle_get
+ *
+ * NOTE: The function does not track individual clients of the framework
+ * and is expected to be maintained by caller of SCMI protocol library.
+ * scmi_handle_put must be balanced with successful scmi_handle_get
+ *
+ * Return: 0 is successfully released
+ * if null was passed, it returns -EINVAL;
+ */
+int scmi_handle_put(const struct scmi_handle *handle)
+{
+ struct scmi_info *info;
+
+ if (!handle)
+ return -EINVAL;
+
+ info = handle_to_scmi_info(handle);
+ mutex_lock(&scmi_list_mutex);
+ if (!WARN_ON(!info->users))
+ info->users--;
+ mutex_unlock(&scmi_list_mutex);
+
+ return 0;
+}
+
+static const struct scmi_desc scmi_generic_desc = {
+ .max_rx_timeout_ms = 30, /* we may increase this if required */
+ .max_msg = 20, /* Limited by MBOX_TX_QUEUE_LEN */
+ .max_msg_size = 128,
+};
+
+/* Each compatible listed below must have descriptor associated with it */
+static const struct of_device_id scmi_of_match[] = {
+ { .compatible = "arm,scmi", .data = &scmi_generic_desc },
+ { /* Sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, scmi_of_match);
+
+static int scmi_xfer_info_init(struct scmi_info *sinfo)
+{
+ int i;
+ struct scmi_xfer *xfer;
+ struct device *dev = sinfo->dev;
+ const struct scmi_desc *desc = sinfo->desc;
+ struct scmi_xfers_info *info = &sinfo->minfo;
+
+ /* Pre-allocated messages, no more than what hdr.seq can support */
+ if (WARN_ON(desc->max_msg >= (MSG_TOKEN_ID_MASK + 1))) {
+ dev_err(dev, "Maximum message of %d exceeds supported %d\n",
+ desc->max_msg, MSG_TOKEN_ID_MASK + 1);
+ return -EINVAL;
+ }
+
+ info->xfer_block = devm_kcalloc(dev, desc->max_msg,
+ sizeof(*info->xfer_block), GFP_KERNEL);
+ if (!info->xfer_block)
+ return -ENOMEM;
+
+ info->xfer_alloc_table = devm_kcalloc(dev, BITS_TO_LONGS(desc->max_msg),
+ sizeof(long), GFP_KERNEL);
+ if (!info->xfer_alloc_table)
+ return -ENOMEM;
+
+ bitmap_zero(info->xfer_alloc_table, desc->max_msg);
+
+ /* Pre-initialize the buffer pointer to pre-allocated buffers */
+ for (i = 0, xfer = info->xfer_block; i < desc->max_msg; i++, xfer++) {
+ xfer->rx.buf = devm_kcalloc(dev, sizeof(u8), desc->max_msg_size,
+ GFP_KERNEL);
+ if (!xfer->rx.buf)
+ return -ENOMEM;
+
+ xfer->tx.buf = xfer->rx.buf;
+ init_completion(&xfer->done);
+ }
+
+ spin_lock_init(&info->xfer_lock);
+
+ return 0;
+}
+
+static int scmi_mailbox_check(struct device_node *np)
+{
+ struct of_phandle_args arg;
+
+ return of_parse_phandle_with_args(np, "mboxes", "#mbox-cells", 0, &arg);
+}
+
+static int scmi_mbox_free_channel(int id, void *p, void *data)
+{
+ struct scmi_chan_info *cinfo = p;
+ struct idr *idr = data;
+
+ if (!IS_ERR_OR_NULL(cinfo->chan)) {
+ mbox_free_channel(cinfo->chan);
+ cinfo->chan = NULL;
+ }
+
+ idr_remove(idr, id);
+
+ return 0;
+}
+
+static int scmi_remove(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct scmi_info *info = platform_get_drvdata(pdev);
+ struct idr *idr = &info->tx_idr;
+
+ mutex_lock(&scmi_list_mutex);
+ if (info->users)
+ ret = -EBUSY;
+ else
+ list_del(&info->node);
+ mutex_unlock(&scmi_list_mutex);
+
+ if (!ret) {
+ /* Safe to free channels since no more users */
+ ret = idr_for_each(idr, scmi_mbox_free_channel, idr);
+ idr_destroy(&info->tx_idr);
+ }
+
+ return ret;
+}
+
+static inline int
+scmi_mbox_chan_setup(struct scmi_info *info, struct device *dev, int prot_id)
+{
+ int ret;
+ struct resource res;
+ resource_size_t size;
+ struct device_node *shmem, *np = dev->of_node;
+ struct scmi_chan_info *cinfo;
+ struct mbox_client *cl;
+
+ if (scmi_mailbox_check(np)) {
+ cinfo = idr_find(&info->tx_idr, SCMI_PROTOCOL_BASE);
+ goto idr_alloc;
+ }
+
+ cinfo = devm_kzalloc(info->dev, sizeof(*cinfo), GFP_KERNEL);
+ if (!cinfo)
+ return -ENOMEM;
+
+ cinfo->dev = dev;
+
+ cl = &cinfo->cl;
+ cl->dev = dev;
+ cl->rx_callback = scmi_rx_callback;
+ cl->tx_prepare = scmi_tx_prepare;
+ cl->tx_block = false;
+ cl->knows_txdone = true;
+
+ shmem = of_parse_phandle(np, "shmem", 0);
+ ret = of_address_to_resource(shmem, 0, &res);
+ of_node_put(shmem);
+ if (ret) {
+ dev_err(dev, "failed to get SCMI Tx payload mem resource\n");
+ return ret;
+ }
+
+ size = resource_size(&res);
+ cinfo->payload = devm_ioremap(info->dev, res.start, size);
+ if (!cinfo->payload) {
+ dev_err(dev, "failed to ioremap SCMI Tx payload\n");
+ return -EADDRNOTAVAIL;
+ }
+
+ /* Transmit channel is first entry i.e. index 0 */
+ cinfo->chan = mbox_request_channel(cl, 0);
+ if (IS_ERR(cinfo->chan)) {
+ ret = PTR_ERR(cinfo->chan);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "failed to request SCMI Tx mailbox\n");
+ return ret;
+ }
+
+idr_alloc:
+ ret = idr_alloc(&info->tx_idr, cinfo, prot_id, prot_id + 1, GFP_KERNEL);
+ if (ret != prot_id) {
+ dev_err(dev, "unable to allocate SCMI idr slot err %d\n", ret);
+ return ret;
+ }
+
+ cinfo->handle = &info->handle;
+ return 0;
+}
+
+static inline void
+scmi_create_protocol_device(struct device_node *np, struct scmi_info *info,
+ int prot_id)
+{
+ struct scmi_device *sdev;
+
+ sdev = scmi_device_create(np, info->dev, prot_id);
+ if (!sdev) {
+ dev_err(info->dev, "failed to create %d protocol device\n",
+ prot_id);
+ return;
+ }
+
+ if (scmi_mbox_chan_setup(info, &sdev->dev, prot_id)) {
+ dev_err(&sdev->dev, "failed to setup transport\n");
+ scmi_device_destroy(sdev);
+ }
+
+ /* setup handle now as the transport is ready */
+ scmi_set_handle(sdev);
+}
+
+static int scmi_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct scmi_handle *handle;
+ const struct scmi_desc *desc;
+ struct scmi_info *info;
+ struct device *dev = &pdev->dev;
+ struct device_node *child, *np = dev->of_node;
+
+ /* Only mailbox method supported, check for the presence of one */
+ if (scmi_mailbox_check(np)) {
+ dev_err(dev, "no mailbox found in %pOF\n", np);
+ return -EINVAL;
+ }
+
+ desc = of_match_device(scmi_of_match, dev)->data;
+
+ info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->dev = dev;
+ info->desc = desc;
+ INIT_LIST_HEAD(&info->node);
+
+ ret = scmi_xfer_info_init(info);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, info);
+ idr_init(&info->tx_idr);
+
+ handle = &info->handle;
+ handle->dev = info->dev;
+ handle->version = &info->version;
+
+ ret = scmi_mbox_chan_setup(info, dev, SCMI_PROTOCOL_BASE);
+ if (ret)
+ return ret;
+
+ ret = scmi_base_protocol_init(handle);
+ if (ret) {
+ dev_err(dev, "unable to communicate with SCMI(%d)\n", ret);
+ return ret;
+ }
+
+ mutex_lock(&scmi_list_mutex);
+ list_add_tail(&info->node, &scmi_list);
+ mutex_unlock(&scmi_list_mutex);
+
+ for_each_available_child_of_node(np, child) {
+ u32 prot_id;
+
+ if (of_property_read_u32(child, "reg", &prot_id))
+ continue;
+
+ prot_id &= MSG_PROTOCOL_ID_MASK;
+
+ if (!scmi_is_protocol_implemented(handle, prot_id)) {
+ dev_err(dev, "SCMI protocol %d not implemented\n",
+ prot_id);
+ continue;
+ }
+
+ scmi_create_protocol_device(child, info, prot_id);
+ }
+
+ return 0;
+}
+
+static struct platform_driver scmi_driver = {
+ .driver = {
+ .name = "arm-scmi",
+ .of_match_table = scmi_of_match,
+ },
+ .probe = scmi_probe,
+ .remove = scmi_remove,
+};
+
+module_platform_driver(scmi_driver);
+
+MODULE_ALIAS("platform: arm-scmi");
+MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
+MODULE_DESCRIPTION("ARM SCMI protocol driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c
new file mode 100644
index 000000000000..987c64d19801
--- /dev/null
+++ b/drivers/firmware/arm_scmi/perf.c
@@ -0,0 +1,481 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Management Interface (SCMI) Performance Protocol
+ *
+ * Copyright (C) 2018 ARM Ltd.
+ */
+
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
+#include <linux/sort.h>
+
+#include "common.h"
+
+enum scmi_performance_protocol_cmd {
+ PERF_DOMAIN_ATTRIBUTES = 0x3,
+ PERF_DESCRIBE_LEVELS = 0x4,
+ PERF_LIMITS_SET = 0x5,
+ PERF_LIMITS_GET = 0x6,
+ PERF_LEVEL_SET = 0x7,
+ PERF_LEVEL_GET = 0x8,
+ PERF_NOTIFY_LIMITS = 0x9,
+ PERF_NOTIFY_LEVEL = 0xa,
+};
+
+struct scmi_opp {
+ u32 perf;
+ u32 power;
+ u32 trans_latency_us;
+};
+
+struct scmi_msg_resp_perf_attributes {
+ __le16 num_domains;
+ __le16 flags;
+#define POWER_SCALE_IN_MILLIWATT(x) ((x) & BIT(0))
+ __le32 stats_addr_low;
+ __le32 stats_addr_high;
+ __le32 stats_size;
+};
+
+struct scmi_msg_resp_perf_domain_attributes {
+ __le32 flags;
+#define SUPPORTS_SET_LIMITS(x) ((x) & BIT(31))
+#define SUPPORTS_SET_PERF_LVL(x) ((x) & BIT(30))
+#define SUPPORTS_PERF_LIMIT_NOTIFY(x) ((x) & BIT(29))
+#define SUPPORTS_PERF_LEVEL_NOTIFY(x) ((x) & BIT(28))
+ __le32 rate_limit_us;
+ __le32 sustained_freq_khz;
+ __le32 sustained_perf_level;
+ u8 name[SCMI_MAX_STR_SIZE];
+};
+
+struct scmi_msg_perf_describe_levels {
+ __le32 domain;
+ __le32 level_index;
+};
+
+struct scmi_perf_set_limits {
+ __le32 domain;
+ __le32 max_level;
+ __le32 min_level;
+};
+
+struct scmi_perf_get_limits {
+ __le32 max_level;
+ __le32 min_level;
+};
+
+struct scmi_perf_set_level {
+ __le32 domain;
+ __le32 level;
+};
+
+struct scmi_perf_notify_level_or_limits {
+ __le32 domain;
+ __le32 notify_enable;
+};
+
+struct scmi_msg_resp_perf_describe_levels {
+ __le16 num_returned;
+ __le16 num_remaining;
+ struct {
+ __le32 perf_val;
+ __le32 power;
+ __le16 transition_latency_us;
+ __le16 reserved;
+ } opp[0];
+};
+
+struct perf_dom_info {
+ bool set_limits;
+ bool set_perf;
+ bool perf_limit_notify;
+ bool perf_level_notify;
+ u32 opp_count;
+ u32 sustained_freq_khz;
+ u32 sustained_perf_level;
+ u32 mult_factor;
+ char name[SCMI_MAX_STR_SIZE];
+ struct scmi_opp opp[MAX_OPPS];
+};
+
+struct scmi_perf_info {
+ int num_domains;
+ bool power_scale_mw;
+ u64 stats_addr;
+ u32 stats_size;
+ struct perf_dom_info *dom_info;
+};
+
+static int scmi_perf_attributes_get(const struct scmi_handle *handle,
+ struct scmi_perf_info *pi)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_msg_resp_perf_attributes *attr;
+
+ ret = scmi_one_xfer_init(handle, PROTOCOL_ATTRIBUTES,
+ SCMI_PROTOCOL_PERF, 0, sizeof(*attr), &t);
+ if (ret)
+ return ret;
+
+ attr = t->rx.buf;
+
+ ret = scmi_do_xfer(handle, t);
+ if (!ret) {
+ u16 flags = le16_to_cpu(attr->flags);
+
+ pi->num_domains = le16_to_cpu(attr->num_domains);
+ pi->power_scale_mw = POWER_SCALE_IN_MILLIWATT(flags);
+ pi->stats_addr = le32_to_cpu(attr->stats_addr_low) |
+ (u64)le32_to_cpu(attr->stats_addr_high) << 32;
+ pi->stats_size = le32_to_cpu(attr->stats_size);
+ }
+
+ scmi_one_xfer_put(handle, t);
+ return ret;
+}
+
+static int
+scmi_perf_domain_attributes_get(const struct scmi_handle *handle, u32 domain,
+ struct perf_dom_info *dom_info)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_msg_resp_perf_domain_attributes *attr;
+
+ ret = scmi_one_xfer_init(handle, PERF_DOMAIN_ATTRIBUTES,
+ SCMI_PROTOCOL_PERF, sizeof(domain),
+ sizeof(*attr), &t);
+ if (ret)
+ return ret;
+
+ *(__le32 *)t->tx.buf = cpu_to_le32(domain);
+ attr = t->rx.buf;
+
+ ret = scmi_do_xfer(handle, t);
+ if (!ret) {
+ u32 flags = le32_to_cpu(attr->flags);
+
+ dom_info->set_limits = SUPPORTS_SET_LIMITS(flags);
+ dom_info->set_perf = SUPPORTS_SET_PERF_LVL(flags);
+ dom_info->perf_limit_notify = SUPPORTS_PERF_LIMIT_NOTIFY(flags);
+ dom_info->perf_level_notify = SUPPORTS_PERF_LEVEL_NOTIFY(flags);
+ dom_info->sustained_freq_khz =
+ le32_to_cpu(attr->sustained_freq_khz);
+ dom_info->sustained_perf_level =
+ le32_to_cpu(attr->sustained_perf_level);
+ dom_info->mult_factor = (dom_info->sustained_freq_khz * 1000) /
+ dom_info->sustained_perf_level;
+ memcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE);
+ }
+
+ scmi_one_xfer_put(handle, t);
+ return ret;
+}
+
+static int opp_cmp_func(const void *opp1, const void *opp2)
+{
+ const struct scmi_opp *t1 = opp1, *t2 = opp2;
+
+ return t1->perf - t2->perf;
+}
+
+static int
+scmi_perf_describe_levels_get(const struct scmi_handle *handle, u32 domain,
+ struct perf_dom_info *perf_dom)
+{
+ int ret, cnt;
+ u32 tot_opp_cnt = 0;
+ u16 num_returned, num_remaining;
+ struct scmi_xfer *t;
+ struct scmi_opp *opp;
+ struct scmi_msg_perf_describe_levels *dom_info;
+ struct scmi_msg_resp_perf_describe_levels *level_info;
+
+ ret = scmi_one_xfer_init(handle, PERF_DESCRIBE_LEVELS,
+ SCMI_PROTOCOL_PERF, sizeof(*dom_info), 0, &t);
+ if (ret)
+ return ret;
+
+ dom_info = t->tx.buf;
+ level_info = t->rx.buf;
+
+ do {
+ dom_info->domain = cpu_to_le32(domain);
+ /* Set the number of OPPs to be skipped/already read */
+ dom_info->level_index = cpu_to_le32(tot_opp_cnt);
+
+ ret = scmi_do_xfer(handle, t);
+ if (ret)
+ break;
+
+ num_returned = le16_to_cpu(level_info->num_returned);
+ num_remaining = le16_to_cpu(level_info->num_remaining);
+ if (tot_opp_cnt + num_returned > MAX_OPPS) {
+ dev_err(handle->dev, "No. of OPPs exceeded MAX_OPPS");
+ break;
+ }
+
+ opp = &perf_dom->opp[tot_opp_cnt];
+ for (cnt = 0; cnt < num_returned; cnt++, opp++) {
+ opp->perf = le32_to_cpu(level_info->opp[cnt].perf_val);
+ opp->power = le32_to_cpu(level_info->opp[cnt].power);
+ opp->trans_latency_us = le16_to_cpu
+ (level_info->opp[cnt].transition_latency_us);
+
+ dev_dbg(handle->dev, "Level %d Power %d Latency %dus\n",
+ opp->perf, opp->power, opp->trans_latency_us);
+ }
+
+ tot_opp_cnt += num_returned;
+ /*
+ * check for both returned and remaining to avoid infinite
+ * loop due to buggy firmware
+ */
+ } while (num_returned && num_remaining);
+
+ perf_dom->opp_count = tot_opp_cnt;
+ scmi_one_xfer_put(handle, t);
+
+ sort(perf_dom->opp, tot_opp_cnt, sizeof(*opp), opp_cmp_func, NULL);
+ return ret;
+}
+
+static int scmi_perf_limits_set(const struct scmi_handle *handle, u32 domain,
+ u32 max_perf, u32 min_perf)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_perf_set_limits *limits;
+
+ ret = scmi_one_xfer_init(handle, PERF_LIMITS_SET, SCMI_PROTOCOL_PERF,
+ sizeof(*limits), 0, &t);
+ if (ret)
+ return ret;
+
+ limits = t->tx.buf;
+ limits->domain = cpu_to_le32(domain);
+ limits->max_level = cpu_to_le32(max_perf);
+ limits->min_level = cpu_to_le32(min_perf);
+
+ ret = scmi_do_xfer(handle, t);
+
+ scmi_one_xfer_put(handle, t);
+ return ret;
+}
+
+static int scmi_perf_limits_get(const struct scmi_handle *handle, u32 domain,
+ u32 *max_perf, u32 *min_perf)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_perf_get_limits *limits;
+
+ ret = scmi_one_xfer_init(handle, PERF_LIMITS_GET, SCMI_PROTOCOL_PERF,
+ sizeof(__le32), 0, &t);
+ if (ret)
+ return ret;
+
+ *(__le32 *)t->tx.buf = cpu_to_le32(domain);
+
+ ret = scmi_do_xfer(handle, t);
+ if (!ret) {
+ limits = t->rx.buf;
+
+ *max_perf = le32_to_cpu(limits->max_level);
+ *min_perf = le32_to_cpu(limits->min_level);
+ }
+
+ scmi_one_xfer_put(handle, t);
+ return ret;
+}
+
+static int scmi_perf_level_set(const struct scmi_handle *handle, u32 domain,
+ u32 level, bool poll)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_perf_set_level *lvl;
+
+ ret = scmi_one_xfer_init(handle, PERF_LEVEL_SET, SCMI_PROTOCOL_PERF,
+ sizeof(*lvl), 0, &t);
+ if (ret)
+ return ret;
+
+ t->hdr.poll_completion = poll;
+ lvl = t->tx.buf;
+ lvl->domain = cpu_to_le32(domain);
+ lvl->level = cpu_to_le32(level);
+
+ ret = scmi_do_xfer(handle, t);
+
+ scmi_one_xfer_put(handle, t);
+ return ret;
+}
+
+static int scmi_perf_level_get(const struct scmi_handle *handle, u32 domain,
+ u32 *level, bool poll)
+{
+ int ret;
+ struct scmi_xfer *t;
+
+ ret = scmi_one_xfer_init(handle, PERF_LEVEL_GET, SCMI_PROTOCOL_PERF,
+ sizeof(u32), sizeof(u32), &t);
+ if (ret)
+ return ret;
+
+ t->hdr.poll_completion = poll;
+ *(__le32 *)t->tx.buf = cpu_to_le32(domain);
+
+ ret = scmi_do_xfer(handle, t);
+ if (!ret)
+ *level = le32_to_cpu(*(__le32 *)t->rx.buf);
+
+ scmi_one_xfer_put(handle, t);
+ return ret;
+}
+
+/* Device specific ops */
+static int scmi_dev_domain_id(struct device *dev)
+{
+ struct of_phandle_args clkspec;
+
+ if (of_parse_phandle_with_args(dev->of_node, "clocks", "#clock-cells",
+ 0, &clkspec))
+ return -EINVAL;
+
+ return clkspec.args[0];
+}
+
+static int scmi_dvfs_add_opps_to_device(const struct scmi_handle *handle,
+ struct device *dev)
+{
+ int idx, ret, domain;
+ unsigned long freq;
+ struct scmi_opp *opp;
+ struct perf_dom_info *dom;
+ struct scmi_perf_info *pi = handle->perf_priv;
+
+ domain = scmi_dev_domain_id(dev);
+ if (domain < 0)
+ return domain;
+
+ dom = pi->dom_info + domain;
+ if (!dom)
+ return -EIO;
+
+ for (opp = dom->opp, idx = 0; idx < dom->opp_count; idx++, opp++) {
+ freq = opp->perf * dom->mult_factor;
+
+ ret = dev_pm_opp_add(dev, freq, 0);
+ if (ret) {
+ dev_warn(dev, "failed to add opp %luHz\n", freq);
+
+ while (idx-- > 0) {
+ freq = (--opp)->perf * dom->mult_factor;
+ dev_pm_opp_remove(dev, freq);
+ }
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int scmi_dvfs_get_transition_latency(const struct scmi_handle *handle,
+ struct device *dev)
+{
+ struct perf_dom_info *dom;
+ struct scmi_perf_info *pi = handle->perf_priv;
+ int domain = scmi_dev_domain_id(dev);
+
+ if (domain < 0)
+ return domain;
+
+ dom = pi->dom_info + domain;
+ if (!dom)
+ return -EIO;
+
+ /* uS to nS */
+ return dom->opp[dom->opp_count - 1].trans_latency_us * 1000;
+}
+
+static int scmi_dvfs_freq_set(const struct scmi_handle *handle, u32 domain,
+ unsigned long freq, bool poll)
+{
+ struct scmi_perf_info *pi = handle->perf_priv;
+ struct perf_dom_info *dom = pi->dom_info + domain;
+
+ return scmi_perf_level_set(handle, domain, freq / dom->mult_factor,
+ poll);
+}
+
+static int scmi_dvfs_freq_get(const struct scmi_handle *handle, u32 domain,
+ unsigned long *freq, bool poll)
+{
+ int ret;
+ u32 level;
+ struct scmi_perf_info *pi = handle->perf_priv;
+ struct perf_dom_info *dom = pi->dom_info + domain;
+
+ ret = scmi_perf_level_get(handle, domain, &level, poll);
+ if (!ret)
+ *freq = level * dom->mult_factor;
+
+ return ret;
+}
+
+static struct scmi_perf_ops perf_ops = {
+ .limits_set = scmi_perf_limits_set,
+ .limits_get = scmi_perf_limits_get,
+ .level_set = scmi_perf_level_set,
+ .level_get = scmi_perf_level_get,
+ .device_domain_id = scmi_dev_domain_id,
+ .get_transition_latency = scmi_dvfs_get_transition_latency,
+ .add_opps_to_device = scmi_dvfs_add_opps_to_device,
+ .freq_set = scmi_dvfs_freq_set,
+ .freq_get = scmi_dvfs_freq_get,
+};
+
+static int scmi_perf_protocol_init(struct scmi_handle *handle)
+{
+ int domain;
+ u32 version;
+ struct scmi_perf_info *pinfo;
+
+ scmi_version_get(handle, SCMI_PROTOCOL_PERF, &version);
+
+ dev_dbg(handle->dev, "Performance Version %d.%d\n",
+ PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+
+ pinfo = devm_kzalloc(handle->dev, sizeof(*pinfo), GFP_KERNEL);
+ if (!pinfo)
+ return -ENOMEM;
+
+ scmi_perf_attributes_get(handle, pinfo);
+
+ pinfo->dom_info = devm_kcalloc(handle->dev, pinfo->num_domains,
+ sizeof(*pinfo->dom_info), GFP_KERNEL);
+ if (!pinfo->dom_info)
+ return -ENOMEM;
+
+ for (domain = 0; domain < pinfo->num_domains; domain++) {
+ struct perf_dom_info *dom = pinfo->dom_info + domain;
+
+ scmi_perf_domain_attributes_get(handle, domain, dom);
+ scmi_perf_describe_levels_get(handle, domain, dom);
+ }
+
+ handle->perf_ops = &perf_ops;
+ handle->perf_priv = pinfo;
+
+ return 0;
+}
+
+static int __init scmi_perf_init(void)
+{
+ return scmi_protocol_register(SCMI_PROTOCOL_PERF,
+ &scmi_perf_protocol_init);
+}
+subsys_initcall(scmi_perf_init);
diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c
new file mode 100644
index 000000000000..087c2876cdf2
--- /dev/null
+++ b/drivers/firmware/arm_scmi/power.c
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Management Interface (SCMI) Power Protocol
+ *
+ * Copyright (C) 2018 ARM Ltd.
+ */
+
+#include "common.h"
+
+enum scmi_power_protocol_cmd {
+ POWER_DOMAIN_ATTRIBUTES = 0x3,
+ POWER_STATE_SET = 0x4,
+ POWER_STATE_GET = 0x5,
+ POWER_STATE_NOTIFY = 0x6,
+};
+
+struct scmi_msg_resp_power_attributes {
+ __le16 num_domains;
+ __le16 reserved;
+ __le32 stats_addr_low;
+ __le32 stats_addr_high;
+ __le32 stats_size;
+};
+
+struct scmi_msg_resp_power_domain_attributes {
+ __le32 flags;
+#define SUPPORTS_STATE_SET_NOTIFY(x) ((x) & BIT(31))
+#define SUPPORTS_STATE_SET_ASYNC(x) ((x) & BIT(30))
+#define SUPPORTS_STATE_SET_SYNC(x) ((x) & BIT(29))
+ u8 name[SCMI_MAX_STR_SIZE];
+};
+
+struct scmi_power_set_state {
+ __le32 flags;
+#define STATE_SET_ASYNC BIT(0)
+ __le32 domain;
+ __le32 state;
+};
+
+struct scmi_power_state_notify {
+ __le32 domain;
+ __le32 notify_enable;
+};
+
+struct power_dom_info {
+ bool state_set_sync;
+ bool state_set_async;
+ bool state_set_notify;
+ char name[SCMI_MAX_STR_SIZE];
+};
+
+struct scmi_power_info {
+ int num_domains;
+ u64 stats_addr;
+ u32 stats_size;
+ struct power_dom_info *dom_info;
+};
+
+static int scmi_power_attributes_get(const struct scmi_handle *handle,
+ struct scmi_power_info *pi)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_msg_resp_power_attributes *attr;
+
+ ret = scmi_one_xfer_init(handle, PROTOCOL_ATTRIBUTES,
+ SCMI_PROTOCOL_POWER, 0, sizeof(*attr), &t);
+ if (ret)
+ return ret;
+
+ attr = t->rx.buf;
+
+ ret = scmi_do_xfer(handle, t);
+ if (!ret) {
+ pi->num_domains = le16_to_cpu(attr->num_domains);
+ pi->stats_addr = le32_to_cpu(attr->stats_addr_low) |
+ (u64)le32_to_cpu(attr->stats_addr_high) << 32;
+ pi->stats_size = le32_to_cpu(attr->stats_size);
+ }
+
+ scmi_one_xfer_put(handle, t);
+ return ret;
+}
+
+static int
+scmi_power_domain_attributes_get(const struct scmi_handle *handle, u32 domain,
+ struct power_dom_info *dom_info)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_msg_resp_power_domain_attributes *attr;
+
+ ret = scmi_one_xfer_init(handle, POWER_DOMAIN_ATTRIBUTES,
+ SCMI_PROTOCOL_POWER, sizeof(domain),
+ sizeof(*attr), &t);
+ if (ret)
+ return ret;
+
+ *(__le32 *)t->tx.buf = cpu_to_le32(domain);
+ attr = t->rx.buf;
+
+ ret = scmi_do_xfer(handle, t);
+ if (!ret) {
+ u32 flags = le32_to_cpu(attr->flags);
+
+ dom_info->state_set_notify = SUPPORTS_STATE_SET_NOTIFY(flags);
+ dom_info->state_set_async = SUPPORTS_STATE_SET_ASYNC(flags);
+ dom_info->state_set_sync = SUPPORTS_STATE_SET_SYNC(flags);
+ memcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE);
+ }
+
+ scmi_one_xfer_put(handle, t);
+ return ret;
+}
+
+static int
+scmi_power_state_set(const struct scmi_handle *handle, u32 domain, u32 state)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_power_set_state *st;
+
+ ret = scmi_one_xfer_init(handle, POWER_STATE_SET, SCMI_PROTOCOL_POWER,
+ sizeof(*st), 0, &t);
+ if (ret)
+ return ret;
+
+ st = t->tx.buf;
+ st->flags = cpu_to_le32(0);
+ st->domain = cpu_to_le32(domain);
+ st->state = cpu_to_le32(state);
+
+ ret = scmi_do_xfer(handle, t);
+
+ scmi_one_xfer_put(handle, t);
+ return ret;
+}
+
+static int
+scmi_power_state_get(const struct scmi_handle *handle, u32 domain, u32 *state)
+{
+ int ret;
+ struct scmi_xfer *t;
+
+ ret = scmi_one_xfer_init(handle, POWER_STATE_GET, SCMI_PROTOCOL_POWER,
+ sizeof(u32), sizeof(u32), &t);
+ if (ret)
+ return ret;
+
+ *(__le32 *)t->tx.buf = cpu_to_le32(domain);
+
+ ret = scmi_do_xfer(handle, t);
+ if (!ret)
+ *state = le32_to_cpu(*(__le32 *)t->rx.buf);
+
+ scmi_one_xfer_put(handle, t);
+ return ret;
+}
+
+static int scmi_power_num_domains_get(const struct scmi_handle *handle)
+{
+ struct scmi_power_info *pi = handle->power_priv;
+
+ return pi->num_domains;
+}
+
+static char *scmi_power_name_get(const struct scmi_handle *handle, u32 domain)
+{
+ struct scmi_power_info *pi = handle->power_priv;
+ struct power_dom_info *dom = pi->dom_info + domain;
+
+ return dom->name;
+}
+
+static struct scmi_power_ops power_ops = {
+ .num_domains_get = scmi_power_num_domains_get,
+ .name_get = scmi_power_name_get,
+ .state_set = scmi_power_state_set,
+ .state_get = scmi_power_state_get,
+};
+
+static int scmi_power_protocol_init(struct scmi_handle *handle)
+{
+ int domain;
+ u32 version;
+ struct scmi_power_info *pinfo;
+
+ scmi_version_get(handle, SCMI_PROTOCOL_POWER, &version);
+
+ dev_dbg(handle->dev, "Power Version %d.%d\n",
+ PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+
+ pinfo = devm_kzalloc(handle->dev, sizeof(*pinfo), GFP_KERNEL);
+ if (!pinfo)
+ return -ENOMEM;
+
+ scmi_power_attributes_get(handle, pinfo);
+
+ pinfo->dom_info = devm_kcalloc(handle->dev, pinfo->num_domains,
+ sizeof(*pinfo->dom_info), GFP_KERNEL);
+ if (!pinfo->dom_info)
+ return -ENOMEM;
+
+ for (domain = 0; domain < pinfo->num_domains; domain++) {
+ struct power_dom_info *dom = pinfo->dom_info + domain;
+
+ scmi_power_domain_attributes_get(handle, domain, dom);
+ }
+
+ handle->power_ops = &power_ops;
+ handle->power_priv = pinfo;
+
+ return 0;
+}
+
+static int __init scmi_power_init(void)
+{
+ return scmi_protocol_register(SCMI_PROTOCOL_POWER,
+ &scmi_power_protocol_init);
+}
+subsys_initcall(scmi_power_init);
diff --git a/drivers/firmware/arm_scmi/scmi_pm_domain.c b/drivers/firmware/arm_scmi/scmi_pm_domain.c
new file mode 100644
index 000000000000..87f737e01473
--- /dev/null
+++ b/drivers/firmware/arm_scmi/scmi_pm_domain.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * SCMI Generic power domain support.
+ *
+ * Copyright (C) 2018 ARM Ltd.
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/pm_domain.h>
+#include <linux/scmi_protocol.h>
+
+struct scmi_pm_domain {
+ struct generic_pm_domain genpd;
+ const struct scmi_handle *handle;
+ const char *name;
+ u32 domain;
+};
+
+#define to_scmi_pd(gpd) container_of(gpd, struct scmi_pm_domain, genpd)
+
+static int scmi_pd_power(struct generic_pm_domain *domain, bool power_on)
+{
+ int ret;
+ u32 state, ret_state;
+ struct scmi_pm_domain *pd = to_scmi_pd(domain);
+ const struct scmi_power_ops *ops = pd->handle->power_ops;
+
+ if (power_on)
+ state = SCMI_POWER_STATE_GENERIC_ON;
+ else
+ state = SCMI_POWER_STATE_GENERIC_OFF;
+
+ ret = ops->state_set(pd->handle, pd->domain, state);
+ if (!ret)
+ ret = ops->state_get(pd->handle, pd->domain, &ret_state);
+ if (!ret && state != ret_state)
+ return -EIO;
+
+ return ret;
+}
+
+static int scmi_pd_power_on(struct generic_pm_domain *domain)
+{
+ return scmi_pd_power(domain, true);
+}
+
+static int scmi_pd_power_off(struct generic_pm_domain *domain)
+{
+ return scmi_pd_power(domain, false);
+}
+
+static int scmi_pm_domain_probe(struct scmi_device *sdev)
+{
+ int num_domains, i;
+ struct device *dev = &sdev->dev;
+ struct device_node *np = dev->of_node;
+ struct scmi_pm_domain *scmi_pd;
+ struct genpd_onecell_data *scmi_pd_data;
+ struct generic_pm_domain **domains;
+ const struct scmi_handle *handle = sdev->handle;
+
+ if (!handle || !handle->power_ops)
+ return -ENODEV;
+
+ num_domains = handle->power_ops->num_domains_get(handle);
+ if (num_domains < 0) {
+ dev_err(dev, "number of domains not found\n");
+ return num_domains;
+ }
+
+ scmi_pd = devm_kcalloc(dev, num_domains, sizeof(*scmi_pd), GFP_KERNEL);
+ if (!scmi_pd)
+ return -ENOMEM;
+
+ scmi_pd_data = devm_kzalloc(dev, sizeof(*scmi_pd_data), GFP_KERNEL);
+ if (!scmi_pd_data)
+ return -ENOMEM;
+
+ domains = devm_kcalloc(dev, num_domains, sizeof(*domains), GFP_KERNEL);
+ if (!domains)
+ return -ENOMEM;
+
+ for (i = 0; i < num_domains; i++, scmi_pd++) {
+ u32 state;
+
+ domains[i] = &scmi_pd->genpd;
+
+ scmi_pd->domain = i;
+ scmi_pd->handle = handle;
+ scmi_pd->name = handle->power_ops->name_get(handle, i);
+ scmi_pd->genpd.name = scmi_pd->name;
+ scmi_pd->genpd.power_off = scmi_pd_power_off;
+ scmi_pd->genpd.power_on = scmi_pd_power_on;
+
+ if (handle->power_ops->state_get(handle, i, &state)) {
+ dev_warn(dev, "failed to get state for domain %d\n", i);
+ continue;
+ }
+
+ pm_genpd_init(&scmi_pd->genpd, NULL,
+ state == SCMI_POWER_STATE_GENERIC_OFF);
+ }
+
+ scmi_pd_data->domains = domains;
+ scmi_pd_data->num_domains = num_domains;
+
+ of_genpd_add_provider_onecell(np, scmi_pd_data);
+
+ return 0;
+}
+
+static const struct scmi_device_id scmi_id_table[] = {
+ { SCMI_PROTOCOL_POWER },
+ { },
+};
+MODULE_DEVICE_TABLE(scmi, scmi_id_table);
+
+static struct scmi_driver scmi_power_domain_driver = {
+ .name = "scmi-power-domain",
+ .probe = scmi_pm_domain_probe,
+ .id_table = scmi_id_table,
+};
+module_scmi_driver(scmi_power_domain_driver);
+
+MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
+MODULE_DESCRIPTION("ARM SCMI power domain driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c
new file mode 100644
index 000000000000..bbb469fea0ed
--- /dev/null
+++ b/drivers/firmware/arm_scmi/sensors.c
@@ -0,0 +1,291 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Management Interface (SCMI) Sensor Protocol
+ *
+ * Copyright (C) 2018 ARM Ltd.
+ */
+
+#include "common.h"
+
+enum scmi_sensor_protocol_cmd {
+ SENSOR_DESCRIPTION_GET = 0x3,
+ SENSOR_CONFIG_SET = 0x4,
+ SENSOR_TRIP_POINT_SET = 0x5,
+ SENSOR_READING_GET = 0x6,
+};
+
+struct scmi_msg_resp_sensor_attributes {
+ __le16 num_sensors;
+ u8 max_requests;
+ u8 reserved;
+ __le32 reg_addr_low;
+ __le32 reg_addr_high;
+ __le32 reg_size;
+};
+
+struct scmi_msg_resp_sensor_description {
+ __le16 num_returned;
+ __le16 num_remaining;
+ struct {
+ __le32 id;
+ __le32 attributes_low;
+#define SUPPORTS_ASYNC_READ(x) ((x) & BIT(31))
+#define NUM_TRIP_POINTS(x) (((x) >> 4) & 0xff)
+ __le32 attributes_high;
+#define SENSOR_TYPE(x) ((x) & 0xff)
+#define SENSOR_SCALE(x) (((x) >> 11) & 0x3f)
+#define SENSOR_UPDATE_SCALE(x) (((x) >> 22) & 0x1f)
+#define SENSOR_UPDATE_BASE(x) (((x) >> 27) & 0x1f)
+ u8 name[SCMI_MAX_STR_SIZE];
+ } desc[0];
+};
+
+struct scmi_msg_set_sensor_config {
+ __le32 id;
+ __le32 event_control;
+};
+
+struct scmi_msg_set_sensor_trip_point {
+ __le32 id;
+ __le32 event_control;
+#define SENSOR_TP_EVENT_MASK (0x3)
+#define SENSOR_TP_DISABLED 0x0
+#define SENSOR_TP_POSITIVE 0x1
+#define SENSOR_TP_NEGATIVE 0x2
+#define SENSOR_TP_BOTH 0x3
+#define SENSOR_TP_ID(x) (((x) & 0xff) << 4)
+ __le32 value_low;
+ __le32 value_high;
+};
+
+struct scmi_msg_sensor_reading_get {
+ __le32 id;
+ __le32 flags;
+#define SENSOR_READ_ASYNC BIT(0)
+};
+
+struct sensors_info {
+ int num_sensors;
+ int max_requests;
+ u64 reg_addr;
+ u32 reg_size;
+ struct scmi_sensor_info *sensors;
+};
+
+static int scmi_sensor_attributes_get(const struct scmi_handle *handle,
+ struct sensors_info *si)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_msg_resp_sensor_attributes *attr;
+
+ ret = scmi_one_xfer_init(handle, PROTOCOL_ATTRIBUTES,
+ SCMI_PROTOCOL_SENSOR, 0, sizeof(*attr), &t);
+ if (ret)
+ return ret;
+
+ attr = t->rx.buf;
+
+ ret = scmi_do_xfer(handle, t);
+ if (!ret) {
+ si->num_sensors = le16_to_cpu(attr->num_sensors);
+ si->max_requests = attr->max_requests;
+ si->reg_addr = le32_to_cpu(attr->reg_addr_low) |
+ (u64)le32_to_cpu(attr->reg_addr_high) << 32;
+ si->reg_size = le32_to_cpu(attr->reg_size);
+ }
+
+ scmi_one_xfer_put(handle, t);
+ return ret;
+}
+
+static int scmi_sensor_description_get(const struct scmi_handle *handle,
+ struct sensors_info *si)
+{
+ int ret, cnt;
+ u32 desc_index = 0;
+ u16 num_returned, num_remaining;
+ struct scmi_xfer *t;
+ struct scmi_msg_resp_sensor_description *buf;
+
+ ret = scmi_one_xfer_init(handle, SENSOR_DESCRIPTION_GET,
+ SCMI_PROTOCOL_SENSOR, sizeof(__le32), 0, &t);
+ if (ret)
+ return ret;
+
+ buf = t->rx.buf;
+
+ do {
+ /* Set the number of sensors to be skipped/already read */
+ *(__le32 *)t->tx.buf = cpu_to_le32(desc_index);
+
+ ret = scmi_do_xfer(handle, t);
+ if (ret)
+ break;
+
+ num_returned = le16_to_cpu(buf->num_returned);
+ num_remaining = le16_to_cpu(buf->num_remaining);
+
+ if (desc_index + num_returned > si->num_sensors) {
+ dev_err(handle->dev, "No. of sensors can't exceed %d",
+ si->num_sensors);
+ break;
+ }
+
+ for (cnt = 0; cnt < num_returned; cnt++) {
+ u32 attrh;
+ struct scmi_sensor_info *s;
+
+ attrh = le32_to_cpu(buf->desc[cnt].attributes_high);
+ s = &si->sensors[desc_index + cnt];
+ s->id = le32_to_cpu(buf->desc[cnt].id);
+ s->type = SENSOR_TYPE(attrh);
+ memcpy(s->name, buf->desc[cnt].name, SCMI_MAX_STR_SIZE);
+ }
+
+ desc_index += num_returned;
+ /*
+ * check for both returned and remaining to avoid infinite
+ * loop due to buggy firmware
+ */
+ } while (num_returned && num_remaining);
+
+ scmi_one_xfer_put(handle, t);
+ return ret;
+}
+
+static int
+scmi_sensor_configuration_set(const struct scmi_handle *handle, u32 sensor_id)
+{
+ int ret;
+ u32 evt_cntl = BIT(0);
+ struct scmi_xfer *t;
+ struct scmi_msg_set_sensor_config *cfg;
+
+ ret = scmi_one_xfer_init(handle, SENSOR_CONFIG_SET,
+ SCMI_PROTOCOL_SENSOR, sizeof(*cfg), 0, &t);
+ if (ret)
+ return ret;
+
+ cfg = t->tx.buf;
+ cfg->id = cpu_to_le32(sensor_id);
+ cfg->event_control = cpu_to_le32(evt_cntl);
+
+ ret = scmi_do_xfer(handle, t);
+
+ scmi_one_xfer_put(handle, t);
+ return ret;
+}
+
+static int scmi_sensor_trip_point_set(const struct scmi_handle *handle,
+ u32 sensor_id, u8 trip_id, u64 trip_value)
+{
+ int ret;
+ u32 evt_cntl = SENSOR_TP_BOTH;
+ struct scmi_xfer *t;
+ struct scmi_msg_set_sensor_trip_point *trip;
+
+ ret = scmi_one_xfer_init(handle, SENSOR_TRIP_POINT_SET,
+ SCMI_PROTOCOL_SENSOR, sizeof(*trip), 0, &t);
+ if (ret)
+ return ret;
+
+ trip = t->tx.buf;
+ trip->id = cpu_to_le32(sensor_id);
+ trip->event_control = cpu_to_le32(evt_cntl | SENSOR_TP_ID(trip_id));
+ trip->value_low = cpu_to_le32(trip_value & 0xffffffff);
+ trip->value_high = cpu_to_le32(trip_value >> 32);
+
+ ret = scmi_do_xfer(handle, t);
+
+ scmi_one_xfer_put(handle, t);
+ return ret;
+}
+
+static int scmi_sensor_reading_get(const struct scmi_handle *handle,
+ u32 sensor_id, bool async, u64 *value)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_msg_sensor_reading_get *sensor;
+
+ ret = scmi_one_xfer_init(handle, SENSOR_READING_GET,
+ SCMI_PROTOCOL_SENSOR, sizeof(*sensor),
+ sizeof(u64), &t);
+ if (ret)
+ return ret;
+
+ sensor = t->tx.buf;
+ sensor->id = cpu_to_le32(sensor_id);
+ sensor->flags = cpu_to_le32(async ? SENSOR_READ_ASYNC : 0);
+
+ ret = scmi_do_xfer(handle, t);
+ if (!ret) {
+ __le32 *pval = t->rx.buf;
+
+ *value = le32_to_cpu(*pval);
+ *value |= (u64)le32_to_cpu(*(pval + 1)) << 32;
+ }
+
+ scmi_one_xfer_put(handle, t);
+ return ret;
+}
+
+static const struct scmi_sensor_info *
+scmi_sensor_info_get(const struct scmi_handle *handle, u32 sensor_id)
+{
+ struct sensors_info *si = handle->sensor_priv;
+
+ return si->sensors + sensor_id;
+}
+
+static int scmi_sensor_count_get(const struct scmi_handle *handle)
+{
+ struct sensors_info *si = handle->sensor_priv;
+
+ return si->num_sensors;
+}
+
+static struct scmi_sensor_ops sensor_ops = {
+ .count_get = scmi_sensor_count_get,
+ .info_get = scmi_sensor_info_get,
+ .configuration_set = scmi_sensor_configuration_set,
+ .trip_point_set = scmi_sensor_trip_point_set,
+ .reading_get = scmi_sensor_reading_get,
+};
+
+static int scmi_sensors_protocol_init(struct scmi_handle *handle)
+{
+ u32 version;
+ struct sensors_info *sinfo;
+
+ scmi_version_get(handle, SCMI_PROTOCOL_SENSOR, &version);
+
+ dev_dbg(handle->dev, "Sensor Version %d.%d\n",
+ PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+
+ sinfo = devm_kzalloc(handle->dev, sizeof(*sinfo), GFP_KERNEL);
+ if (!sinfo)
+ return -ENOMEM;
+
+ scmi_sensor_attributes_get(handle, sinfo);
+
+ sinfo->sensors = devm_kcalloc(handle->dev, sinfo->num_sensors,
+ sizeof(*sinfo->sensors), GFP_KERNEL);
+ if (!sinfo->sensors)
+ return -ENOMEM;
+
+ scmi_sensor_description_get(handle, sinfo);
+
+ handle->sensor_ops = &sensor_ops;
+ handle->sensor_priv = sinfo;
+
+ return 0;
+}
+
+static int __init scmi_sensors_init(void)
+{
+ return scmi_protocol_register(SCMI_PROTOCOL_SENSOR,
+ &scmi_sensors_protocol_init);
+}
+subsys_initcall(scmi_sensors_init);
diff --git a/drivers/firmware/arm_scpi.c b/drivers/firmware/arm_scpi.c
index 7da9f1b83ebe..6d7a6c0a5e07 100644
--- a/drivers/firmware/arm_scpi.c
+++ b/drivers/firmware/arm_scpi.c
@@ -28,6 +28,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/bitmap.h>
+#include <linux/bitfield.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/export.h>
@@ -45,48 +46,32 @@
#include <linux/sort.h>
#include <linux/spinlock.h>
-#define CMD_ID_SHIFT 0
-#define CMD_ID_MASK 0x7f
-#define CMD_TOKEN_ID_SHIFT 8
-#define CMD_TOKEN_ID_MASK 0xff
-#define CMD_DATA_SIZE_SHIFT 16
-#define CMD_DATA_SIZE_MASK 0x1ff
-#define CMD_LEGACY_DATA_SIZE_SHIFT 20
-#define CMD_LEGACY_DATA_SIZE_MASK 0x1ff
-#define PACK_SCPI_CMD(cmd_id, tx_sz) \
- ((((cmd_id) & CMD_ID_MASK) << CMD_ID_SHIFT) | \
- (((tx_sz) & CMD_DATA_SIZE_MASK) << CMD_DATA_SIZE_SHIFT))
-#define ADD_SCPI_TOKEN(cmd, token) \
- ((cmd) |= (((token) & CMD_TOKEN_ID_MASK) << CMD_TOKEN_ID_SHIFT))
-#define PACK_LEGACY_SCPI_CMD(cmd_id, tx_sz) \
- ((((cmd_id) & CMD_ID_MASK) << CMD_ID_SHIFT) | \
- (((tx_sz) & CMD_LEGACY_DATA_SIZE_MASK) << CMD_LEGACY_DATA_SIZE_SHIFT))
-
-#define CMD_SIZE(cmd) (((cmd) >> CMD_DATA_SIZE_SHIFT) & CMD_DATA_SIZE_MASK)
-#define CMD_LEGACY_SIZE(cmd) (((cmd) >> CMD_LEGACY_DATA_SIZE_SHIFT) & \
- CMD_LEGACY_DATA_SIZE_MASK)
-#define CMD_UNIQ_MASK (CMD_TOKEN_ID_MASK << CMD_TOKEN_ID_SHIFT | CMD_ID_MASK)
+#define CMD_ID_MASK GENMASK(6, 0)
+#define CMD_TOKEN_ID_MASK GENMASK(15, 8)
+#define CMD_DATA_SIZE_MASK GENMASK(24, 16)
+#define CMD_LEGACY_DATA_SIZE_MASK GENMASK(28, 20)
+#define PACK_SCPI_CMD(cmd_id, tx_sz) \
+ (FIELD_PREP(CMD_ID_MASK, cmd_id) | \
+ FIELD_PREP(CMD_DATA_SIZE_MASK, tx_sz))
+#define PACK_LEGACY_SCPI_CMD(cmd_id, tx_sz) \
+ (FIELD_PREP(CMD_ID_MASK, cmd_id) | \
+ FIELD_PREP(CMD_LEGACY_DATA_SIZE_MASK, tx_sz))
+
+#define CMD_SIZE(cmd) FIELD_GET(CMD_DATA_SIZE_MASK, cmd)
+#define CMD_UNIQ_MASK (CMD_TOKEN_ID_MASK | CMD_ID_MASK)
#define CMD_XTRACT_UNIQ(cmd) ((cmd) & CMD_UNIQ_MASK)
#define SCPI_SLOT 0
#define MAX_DVFS_DOMAINS 8
#define MAX_DVFS_OPPS 16
-#define DVFS_LATENCY(hdr) (le32_to_cpu(hdr) >> 16)
-#define DVFS_OPP_COUNT(hdr) ((le32_to_cpu(hdr) >> 8) & 0xff)
-
-#define PROTOCOL_REV_MINOR_BITS 16
-#define PROTOCOL_REV_MINOR_MASK ((1U << PROTOCOL_REV_MINOR_BITS) - 1)
-#define PROTOCOL_REV_MAJOR(x) ((x) >> PROTOCOL_REV_MINOR_BITS)
-#define PROTOCOL_REV_MINOR(x) ((x) & PROTOCOL_REV_MINOR_MASK)
-
-#define FW_REV_MAJOR_BITS 24
-#define FW_REV_MINOR_BITS 16
-#define FW_REV_PATCH_MASK ((1U << FW_REV_MINOR_BITS) - 1)
-#define FW_REV_MINOR_MASK ((1U << FW_REV_MAJOR_BITS) - 1)
-#define FW_REV_MAJOR(x) ((x) >> FW_REV_MAJOR_BITS)
-#define FW_REV_MINOR(x) (((x) & FW_REV_MINOR_MASK) >> FW_REV_MINOR_BITS)
-#define FW_REV_PATCH(x) ((x) & FW_REV_PATCH_MASK)
+
+#define PROTO_REV_MAJOR_MASK GENMASK(31, 16)
+#define PROTO_REV_MINOR_MASK GENMASK(15, 0)
+
+#define FW_REV_MAJOR_MASK GENMASK(31, 24)
+#define FW_REV_MINOR_MASK GENMASK(23, 16)
+#define FW_REV_PATCH_MASK GENMASK(15, 0)
#define MAX_RX_TIMEOUT (msecs_to_jiffies(30))
@@ -311,10 +296,6 @@ struct clk_get_info {
u8 name[20];
} __packed;
-struct clk_get_value {
- __le32 rate;
-} __packed;
-
struct clk_set_value {
__le16 id;
__le16 reserved;
@@ -328,7 +309,9 @@ struct legacy_clk_set_value {
} __packed;
struct dvfs_info {
- __le32 header;
+ u8 domain;
+ u8 opp_count;
+ __le16 latency;
struct {
__le32 freq;
__le32 m_volt;
@@ -340,10 +323,6 @@ struct dvfs_set {
u8 index;
} __packed;
-struct sensor_capabilities {
- __le16 sensors;
-} __packed;
-
struct _scpi_sensor_info {
__le16 sensor_id;
u8 class;
@@ -351,11 +330,6 @@ struct _scpi_sensor_info {
char name[20];
};
-struct sensor_value {
- __le32 lo_val;
- __le32 hi_val;
-} __packed;
-
struct dev_pstate_set {
__le16 dev_id;
u8 pstate;
@@ -419,19 +393,20 @@ static void scpi_process_cmd(struct scpi_chan *ch, u32 cmd)
unsigned int len;
if (scpi_info->is_legacy) {
- struct legacy_scpi_shared_mem *mem = ch->rx_payload;
+ struct legacy_scpi_shared_mem __iomem *mem =
+ ch->rx_payload;
/* RX Length is not replied by the legacy Firmware */
len = match->rx_len;
- match->status = le32_to_cpu(mem->status);
+ match->status = ioread32(&mem->status);
memcpy_fromio(match->rx_buf, mem->payload, len);
} else {
- struct scpi_shared_mem *mem = ch->rx_payload;
+ struct scpi_shared_mem __iomem *mem = ch->rx_payload;
- len = min(match->rx_len, CMD_SIZE(cmd));
+ len = min_t(unsigned int, match->rx_len, CMD_SIZE(cmd));
- match->status = le32_to_cpu(mem->status);
+ match->status = ioread32(&mem->status);
memcpy_fromio(match->rx_buf, mem->payload, len);
}
@@ -445,11 +420,11 @@ static void scpi_process_cmd(struct scpi_chan *ch, u32 cmd)
static void scpi_handle_remote_msg(struct mbox_client *c, void *msg)
{
struct scpi_chan *ch = container_of(c, struct scpi_chan, cl);
- struct scpi_shared_mem *mem = ch->rx_payload;
+ struct scpi_shared_mem __iomem *mem = ch->rx_payload;
u32 cmd = 0;
if (!scpi_info->is_legacy)
- cmd = le32_to_cpu(mem->command);
+ cmd = ioread32(&mem->command);
scpi_process_cmd(ch, cmd);
}
@@ -459,7 +434,7 @@ static void scpi_tx_prepare(struct mbox_client *c, void *msg)
unsigned long flags;
struct scpi_xfer *t = msg;
struct scpi_chan *ch = container_of(c, struct scpi_chan, cl);
- struct scpi_shared_mem *mem = (struct scpi_shared_mem *)ch->tx_payload;
+ struct scpi_shared_mem __iomem *mem = ch->tx_payload;
if (t->tx_buf) {
if (scpi_info->is_legacy)
@@ -471,14 +446,14 @@ static void scpi_tx_prepare(struct mbox_client *c, void *msg)
if (t->rx_buf) {
if (!(++ch->token))
++ch->token;
- ADD_SCPI_TOKEN(t->cmd, ch->token);
+ t->cmd |= FIELD_PREP(CMD_TOKEN_ID_MASK, ch->token);
spin_lock_irqsave(&ch->rx_lock, flags);
list_add_tail(&t->node, &ch->rx_pending);
spin_unlock_irqrestore(&ch->rx_lock, flags);
}
if (!scpi_info->is_legacy)
- mem->command = cpu_to_le32(t->cmd);
+ iowrite32(t->cmd, &mem->command);
}
static struct scpi_xfer *get_scpi_xfer(struct scpi_chan *ch)
@@ -583,13 +558,13 @@ scpi_clk_get_range(u16 clk_id, unsigned long *min, unsigned long *max)
static unsigned long scpi_clk_get_val(u16 clk_id)
{
int ret;
- struct clk_get_value clk;
+ __le32 rate;
__le16 le_clk_id = cpu_to_le16(clk_id);
ret = scpi_send_message(CMD_GET_CLOCK_VALUE, &le_clk_id,
- sizeof(le_clk_id), &clk, sizeof(clk));
+ sizeof(le_clk_id), &rate, sizeof(rate));
- return ret ? ret : le32_to_cpu(clk.rate);
+ return ret ? ret : le32_to_cpu(rate);
}
static int scpi_clk_set_val(u16 clk_id, unsigned long rate)
@@ -665,8 +640,8 @@ static struct scpi_dvfs_info *scpi_dvfs_get_info(u8 domain)
if (!info)
return ERR_PTR(-ENOMEM);
- info->count = DVFS_OPP_COUNT(buf.header);
- info->latency = DVFS_LATENCY(buf.header) * 1000; /* uS to nS */
+ info->count = buf.opp_count;
+ info->latency = le16_to_cpu(buf.latency) * 1000; /* uS to nS */
info->opps = kcalloc(info->count, sizeof(*opp), GFP_KERNEL);
if (!info->opps) {
@@ -713,9 +688,6 @@ static int scpi_dvfs_get_transition_latency(struct device *dev)
if (IS_ERR(info))
return PTR_ERR(info);
- if (!info->latency)
- return 0;
-
return info->latency;
}
@@ -746,13 +718,13 @@ static int scpi_dvfs_add_opps_to_device(struct device *dev)
static int scpi_sensor_get_capability(u16 *sensors)
{
- struct sensor_capabilities cap_buf;
+ __le16 cap;
int ret;
- ret = scpi_send_message(CMD_SENSOR_CAPABILITIES, NULL, 0, &cap_buf,
- sizeof(cap_buf));
+ ret = scpi_send_message(CMD_SENSOR_CAPABILITIES, NULL, 0, &cap,
+ sizeof(cap));
if (!ret)
- *sensors = le16_to_cpu(cap_buf.sensors);
+ *sensors = le16_to_cpu(cap);
return ret;
}
@@ -776,20 +748,19 @@ static int scpi_sensor_get_info(u16 sensor_id, struct scpi_sensor_info *info)
static int scpi_sensor_get_value(u16 sensor, u64 *val)
{
__le16 id = cpu_to_le16(sensor);
- struct sensor_value buf;
+ __le64 value;
int ret;
ret = scpi_send_message(CMD_SENSOR_VALUE, &id, sizeof(id),
- &buf, sizeof(buf));
+ &value, sizeof(value));
if (ret)
return ret;
if (scpi_info->is_legacy)
- /* only 32-bits supported, hi_val can be junk */
- *val = le32_to_cpu(buf.lo_val);
+ /* only 32-bits supported, upper 32 bits can be junk */
+ *val = le32_to_cpup((__le32 *)&value);
else
- *val = (u64)le32_to_cpu(buf.hi_val) << 32 |
- le32_to_cpu(buf.lo_val);
+ *val = le64_to_cpu(value);
return 0;
}
@@ -864,9 +835,9 @@ static ssize_t protocol_version_show(struct device *dev,
{
struct scpi_drvinfo *scpi_info = dev_get_drvdata(dev);
- return sprintf(buf, "%d.%d\n",
- PROTOCOL_REV_MAJOR(scpi_info->protocol_version),
- PROTOCOL_REV_MINOR(scpi_info->protocol_version));
+ return sprintf(buf, "%lu.%lu\n",
+ FIELD_GET(PROTO_REV_MAJOR_MASK, scpi_info->protocol_version),
+ FIELD_GET(PROTO_REV_MINOR_MASK, scpi_info->protocol_version));
}
static DEVICE_ATTR_RO(protocol_version);
@@ -875,10 +846,10 @@ static ssize_t firmware_version_show(struct device *dev,
{
struct scpi_drvinfo *scpi_info = dev_get_drvdata(dev);
- return sprintf(buf, "%d.%d.%d\n",
- FW_REV_MAJOR(scpi_info->firmware_version),
- FW_REV_MINOR(scpi_info->firmware_version),
- FW_REV_PATCH(scpi_info->firmware_version));
+ return sprintf(buf, "%lu.%lu.%lu\n",
+ FIELD_GET(FW_REV_MAJOR_MASK, scpi_info->firmware_version),
+ FIELD_GET(FW_REV_MINOR_MASK, scpi_info->firmware_version),
+ FIELD_GET(FW_REV_PATCH_MASK, scpi_info->firmware_version));
}
static DEVICE_ATTR_RO(firmware_version);
@@ -889,37 +860,26 @@ static struct attribute *versions_attrs[] = {
};
ATTRIBUTE_GROUPS(versions);
-static void
-scpi_free_channels(struct device *dev, struct scpi_chan *pchan, int count)
+static void scpi_free_channels(void *data)
{
+ struct scpi_drvinfo *info = data;
int i;
- for (i = 0; i < count && pchan->chan; i++, pchan++) {
- mbox_free_channel(pchan->chan);
- devm_kfree(dev, pchan->xfers);
- devm_iounmap(dev, pchan->rx_payload);
- }
+ for (i = 0; i < info->num_chans; i++)
+ mbox_free_channel(info->channels[i].chan);
}
static int scpi_remove(struct platform_device *pdev)
{
int i;
- struct device *dev = &pdev->dev;
struct scpi_drvinfo *info = platform_get_drvdata(pdev);
scpi_info = NULL; /* stop exporting SCPI ops through get_scpi_ops */
- of_platform_depopulate(dev);
- sysfs_remove_groups(&dev->kobj, versions_groups);
- scpi_free_channels(dev, info->channels, info->num_chans);
- platform_set_drvdata(pdev, NULL);
-
for (i = 0; i < MAX_DVFS_DOMAINS && info->dvfs[i]; i++) {
kfree(info->dvfs[i]->opps);
kfree(info->dvfs[i]);
}
- devm_kfree(dev, info->channels);
- devm_kfree(dev, info);
return 0;
}
@@ -952,7 +912,6 @@ static int scpi_probe(struct platform_device *pdev)
{
int count, idx, ret;
struct resource res;
- struct scpi_chan *scpi_chan;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
@@ -969,13 +928,19 @@ static int scpi_probe(struct platform_device *pdev)
return -ENODEV;
}
- scpi_chan = devm_kcalloc(dev, count, sizeof(*scpi_chan), GFP_KERNEL);
- if (!scpi_chan)
+ scpi_info->channels = devm_kcalloc(dev, count, sizeof(struct scpi_chan),
+ GFP_KERNEL);
+ if (!scpi_info->channels)
return -ENOMEM;
- for (idx = 0; idx < count; idx++) {
+ ret = devm_add_action(dev, scpi_free_channels, scpi_info);
+ if (ret)
+ return ret;
+
+ for (; scpi_info->num_chans < count; scpi_info->num_chans++) {
resource_size_t size;
- struct scpi_chan *pchan = scpi_chan + idx;
+ int idx = scpi_info->num_chans;
+ struct scpi_chan *pchan = scpi_info->channels + idx;
struct mbox_client *cl = &pchan->cl;
struct device_node *shmem = of_parse_phandle(np, "shmem", idx);
@@ -983,15 +948,14 @@ static int scpi_probe(struct platform_device *pdev)
of_node_put(shmem);
if (ret) {
dev_err(dev, "failed to get SCPI payload mem resource\n");
- goto err;
+ return ret;
}
size = resource_size(&res);
pchan->rx_payload = devm_ioremap(dev, res.start, size);
if (!pchan->rx_payload) {
dev_err(dev, "failed to ioremap SCPI payload\n");
- ret = -EADDRNOTAVAIL;
- goto err;
+ return -EADDRNOTAVAIL;
}
pchan->tx_payload = pchan->rx_payload + (size >> 1);
@@ -1017,14 +981,9 @@ static int scpi_probe(struct platform_device *pdev)
dev_err(dev, "failed to get channel%d err %d\n",
idx, ret);
}
-err:
- scpi_free_channels(dev, scpi_chan, idx);
- scpi_info = NULL;
return ret;
}
- scpi_info->channels = scpi_chan;
- scpi_info->num_chans = count;
scpi_info->commands = scpi_std_commands;
platform_set_drvdata(pdev, scpi_info);
@@ -1043,23 +1002,31 @@ err:
ret = scpi_init_versions(scpi_info);
if (ret) {
dev_err(dev, "incorrect or no SCP firmware found\n");
- scpi_remove(pdev);
return ret;
}
- _dev_info(dev, "SCP Protocol %d.%d Firmware %d.%d.%d version\n",
- PROTOCOL_REV_MAJOR(scpi_info->protocol_version),
- PROTOCOL_REV_MINOR(scpi_info->protocol_version),
- FW_REV_MAJOR(scpi_info->firmware_version),
- FW_REV_MINOR(scpi_info->firmware_version),
- FW_REV_PATCH(scpi_info->firmware_version));
+ if (scpi_info->is_legacy && !scpi_info->protocol_version &&
+ !scpi_info->firmware_version)
+ dev_info(dev, "SCP Protocol legacy pre-1.0 firmware\n");
+ else
+ dev_info(dev, "SCP Protocol %lu.%lu Firmware %lu.%lu.%lu version\n",
+ FIELD_GET(PROTO_REV_MAJOR_MASK,
+ scpi_info->protocol_version),
+ FIELD_GET(PROTO_REV_MINOR_MASK,
+ scpi_info->protocol_version),
+ FIELD_GET(FW_REV_MAJOR_MASK,
+ scpi_info->firmware_version),
+ FIELD_GET(FW_REV_MINOR_MASK,
+ scpi_info->firmware_version),
+ FIELD_GET(FW_REV_PATCH_MASK,
+ scpi_info->firmware_version));
scpi_info->scpi_ops = &scpi_ops;
- ret = sysfs_create_groups(&dev->kobj, versions_groups);
+ ret = devm_device_add_groups(dev, versions_groups);
if (ret)
dev_err(dev, "unable to create sysfs version group\n");
- return of_platform_populate(dev->of_node, NULL, NULL, dev);
+ return devm_of_platform_populate(dev);
}
static const struct of_device_id scpi_of_match[] = {
diff --git a/drivers/firmware/broadcom/Kconfig b/drivers/firmware/broadcom/Kconfig
index 3c7e5b741e37..f77cdb3a041f 100644
--- a/drivers/firmware/broadcom/Kconfig
+++ b/drivers/firmware/broadcom/Kconfig
@@ -13,6 +13,7 @@ config BCM47XX_NVRAM
config BCM47XX_SPROM
bool "Broadcom SPROM driver"
depends on BCM47XX_NVRAM
+ select GENERIC_NET_UTILS
help
Broadcom devices store configuration data in SPROM. Accessing it is
specific to the bus host type, e.g. PCI(e) devices have it mapped in
diff --git a/drivers/firmware/broadcom/bcm47xx_sprom.c b/drivers/firmware/broadcom/bcm47xx_sprom.c
index 62aa3cf09b4d..4787f86c8ac1 100644
--- a/drivers/firmware/broadcom/bcm47xx_sprom.c
+++ b/drivers/firmware/broadcom/bcm47xx_sprom.c
@@ -137,20 +137,6 @@ static void nvram_read_leddc(const char *prefix, const char *name,
*leddc_off_time = (val >> 16) & 0xff;
}
-static void bcm47xx_nvram_parse_macaddr(char *buf, u8 macaddr[6])
-{
- if (strchr(buf, ':'))
- sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &macaddr[0],
- &macaddr[1], &macaddr[2], &macaddr[3], &macaddr[4],
- &macaddr[5]);
- else if (strchr(buf, '-'))
- sscanf(buf, "%hhx-%hhx-%hhx-%hhx-%hhx-%hhx", &macaddr[0],
- &macaddr[1], &macaddr[2], &macaddr[3], &macaddr[4],
- &macaddr[5]);
- else
- pr_warn("Can not parse mac address: %s\n", buf);
-}
-
static void nvram_read_macaddr(const char *prefix, const char *name,
u8 val[6], bool fallback)
{
@@ -161,7 +147,9 @@ static void nvram_read_macaddr(const char *prefix, const char *name,
if (err < 0)
return;
- bcm47xx_nvram_parse_macaddr(buf, val);
+ strreplace(buf, '-', ':');
+ if (!mac_pton(buf, val))
+ pr_warn("Can not parse mac address: %s\n", buf);
}
static void nvram_read_alpha2(const char *prefix, const char *name,
diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c
index e763e1484331..54e66adef252 100644
--- a/drivers/firmware/dmi_scan.c
+++ b/drivers/firmware/dmi_scan.c
@@ -32,6 +32,7 @@ static char dmi_ids_string[128] __initdata;
static struct dmi_memdev_info {
const char *device;
const char *bank;
+ u64 size; /* bytes */
u16 handle;
} *dmi_memdev;
static int dmi_memdev_nr;
@@ -186,7 +187,7 @@ static void __init dmi_save_uuid(const struct dmi_header *dm, int slot,
char *s;
int is_ff = 1, is_00 = 1, i;
- if (dmi_ident[slot] || dm->length <= index + 16)
+ if (dmi_ident[slot] || dm->length < index + 16)
return;
d = (u8 *) dm + index;
@@ -210,9 +211,9 @@ static void __init dmi_save_uuid(const struct dmi_header *dm, int slot,
* says that this is the defacto standard.
*/
if (dmi_ver >= 0x020600)
- sprintf(s, "%pUL", d);
+ sprintf(s, "%pUl", d);
else
- sprintf(s, "%pUB", d);
+ sprintf(s, "%pUb", d);
dmi_ident[slot] = s;
}
@@ -386,6 +387,8 @@ static void __init save_mem_devices(const struct dmi_header *dm, void *v)
{
const char *d = (const char *)dm;
static int nr;
+ u64 bytes;
+ u16 size;
if (dm->type != DMI_ENTRY_MEM_DEVICE || dm->length < 0x12)
return;
@@ -396,6 +399,20 @@ static void __init save_mem_devices(const struct dmi_header *dm, void *v)
dmi_memdev[nr].handle = get_unaligned(&dm->handle);
dmi_memdev[nr].device = dmi_string(dm, d[0x10]);
dmi_memdev[nr].bank = dmi_string(dm, d[0x11]);
+
+ size = get_unaligned((u16 *)&d[0xC]);
+ if (size == 0)
+ bytes = 0;
+ else if (size == 0xffff)
+ bytes = ~0ull;
+ else if (size & 0x8000)
+ bytes = (u64)(size & 0x7fff) << 10;
+ else if (size != 0x7fff)
+ bytes = (u64)size << 20;
+ else
+ bytes = (u64)get_unaligned((u32 *)&d[0x1C]) << 20;
+
+ dmi_memdev[nr].size = bytes;
nr++;
}
@@ -775,7 +792,15 @@ static bool dmi_matches(const struct dmi_system_id *dmi)
int s = dmi->matches[i].slot;
if (s == DMI_NONE)
break;
- if (dmi_ident[s]) {
+ if (s == DMI_OEM_STRING) {
+ /* DMI_OEM_STRING must be exact match */
+ const struct dmi_device *valid;
+
+ valid = dmi_find_device(DMI_DEV_TYPE_OEM_STRING,
+ dmi->matches[i].substr, NULL);
+ if (valid)
+ continue;
+ } else if (dmi_ident[s]) {
if (dmi->matches[i].exact_match) {
if (!strcmp(dmi_ident[s],
dmi->matches[i].substr))
@@ -1005,6 +1030,26 @@ out:
EXPORT_SYMBOL(dmi_get_date);
/**
+ * dmi_get_bios_year - get a year out of DMI_BIOS_DATE field
+ *
+ * Returns year on success, -ENXIO if DMI is not selected,
+ * or a different negative error code if DMI field is not present
+ * or not parseable.
+ */
+int dmi_get_bios_year(void)
+{
+ bool exists;
+ int year;
+
+ exists = dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL);
+ if (!exists)
+ return -ENODATA;
+
+ return year ? year : -ERANGE;
+}
+EXPORT_SYMBOL(dmi_get_bios_year);
+
+/**
* dmi_walk - Walk the DMI table and get called back for every record
* @decode: Callback function
* @private_data: Private data to be passed to the callback function
@@ -1065,3 +1110,17 @@ void dmi_memdev_name(u16 handle, const char **bank, const char **device)
}
}
EXPORT_SYMBOL_GPL(dmi_memdev_name);
+
+u64 dmi_memdev_size(u16 handle)
+{
+ int n;
+
+ if (dmi_memdev) {
+ for (n = 0; n < dmi_memdev_nr; n++) {
+ if (handle == dmi_memdev[n].handle)
+ return dmi_memdev[n].size;
+ }
+ }
+ return ~0ull;
+}
+EXPORT_SYMBOL_GPL(dmi_memdev_size);
diff --git a/drivers/firmware/edd.c b/drivers/firmware/edd.c
index 60a8f1363a10..1b82c89a49df 100644
--- a/drivers/firmware/edd.c
+++ b/drivers/firmware/edd.c
@@ -748,14 +748,12 @@ edd_init(void)
int rc=0;
struct edd_device *edev;
+ if (!edd_num_devices())
+ return -ENODEV;
+
printk(KERN_INFO "BIOS EDD facility v%s %s, %d devices found\n",
EDD_VERSION, EDD_DATE, edd_num_devices());
- if (!edd_num_devices()) {
- printk(KERN_INFO "EDD information not available.\n");
- return -ENODEV;
- }
-
edd_kset = kset_create_and_add("edd", NULL, firmware_kobj);
if (!edd_kset)
return -ENOMEM;
diff --git a/drivers/firmware/efi/apple-properties.c b/drivers/firmware/efi/apple-properties.c
index 9f6bcf173b0e..adaa9a3714b9 100644
--- a/drivers/firmware/efi/apple-properties.c
+++ b/drivers/firmware/efi/apple-properties.c
@@ -19,6 +19,7 @@
#include <linux/bootmem.h>
#include <linux/efi.h>
+#include <linux/io.h>
#include <linux/platform_data/x86/apple.h>
#include <linux/property.h>
#include <linux/slab.h>
@@ -52,8 +53,6 @@ struct properties_header {
struct dev_header dev_header[0];
};
-static u8 one __initdata = 1;
-
static void __init unmarshal_key_value_pairs(struct dev_header *dev_header,
struct device *dev, void *ptr,
struct property_entry entry[])
@@ -95,14 +94,9 @@ static void __init unmarshal_key_value_pairs(struct dev_header *dev_header,
key_len - sizeof(key_len));
entry[i].name = key;
- entry[i].is_array = true;
entry[i].length = val_len - sizeof(val_len);
+ entry[i].is_array = !!entry[i].length;
entry[i].pointer.raw_data = ptr + key_len + sizeof(val_len);
- if (!entry[i].length) {
- /* driver core doesn't accept empty properties */
- entry[i].length = 1;
- entry[i].pointer.raw_data = &one;
- }
if (dump_properties) {
dev_info(dev, "property: %s\n", entry[i].name);
@@ -196,7 +190,7 @@ static int __init map_properties(void)
pa_data = boot_params.hdr.setup_data;
while (pa_data) {
- data = ioremap(pa_data, sizeof(*data));
+ data = memremap(pa_data, sizeof(*data), MEMREMAP_WB);
if (!data) {
pr_err("cannot map setup_data header\n");
return -ENOMEM;
@@ -204,14 +198,14 @@ static int __init map_properties(void)
if (data->type != SETUP_APPLE_PROPERTIES) {
pa_data = data->next;
- iounmap(data);
+ memunmap(data);
continue;
}
data_len = data->len;
- iounmap(data);
+ memunmap(data);
- data = ioremap(pa_data, sizeof(*data) + data_len);
+ data = memremap(pa_data, sizeof(*data) + data_len, MEMREMAP_WB);
if (!data) {
pr_err("cannot map setup_data payload\n");
return -ENOMEM;
@@ -236,7 +230,7 @@ static int __init map_properties(void)
* to avoid breaking the chain of ->next pointers.
*/
data->len = 0;
- iounmap(data);
+ memunmap(data);
free_bootmem_late(pa_data + sizeof(*data), data_len);
return ret;
diff --git a/drivers/firmware/efi/arm-runtime.c b/drivers/firmware/efi/arm-runtime.c
index 1cc41c3d6315..5889cbea60b8 100644
--- a/drivers/firmware/efi/arm-runtime.c
+++ b/drivers/firmware/efi/arm-runtime.c
@@ -31,15 +31,6 @@
extern u64 efi_system_table;
-static struct mm_struct efi_mm = {
- .mm_rb = RB_ROOT,
- .mm_users = ATOMIC_INIT(2),
- .mm_count = ATOMIC_INIT(1),
- .mmap_sem = __RWSEM_INITIALIZER(efi_mm.mmap_sem),
- .page_table_lock = __SPIN_LOCK_UNLOCKED(efi_mm.page_table_lock),
- .mmlist = LIST_HEAD_INIT(efi_mm.mmlist),
-};
-
#ifdef CONFIG_ARM64_PTDUMP_DEBUGFS
#include <asm/ptdump.h>
@@ -54,6 +45,9 @@ static struct ptdump_info efi_ptdump_info = {
static int __init ptdump_init(void)
{
+ if (!efi_enabled(EFI_RUNTIME_SERVICES))
+ return 0;
+
return ptdump_debugfs_register(&efi_ptdump_info, "efi_page_tables");
}
device_initcall(ptdump_init);
@@ -80,10 +74,7 @@ static bool __init efi_virtmap_init(void)
return false;
ret = efi_create_mapping(&efi_mm, md);
- if (!ret) {
- pr_info(" EFI remap %pa => %p\n",
- &phys, (void *)(unsigned long)md->virt_addr);
- } else {
+ if (ret) {
pr_warn(" EFI remap %pa: failed to create mapping (%d)\n",
&phys, ret);
return false;
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index cd42f66a7c85..232f4915223b 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -75,6 +75,15 @@ static unsigned long *efi_tables[] = {
&efi.mem_attr_table,
};
+struct mm_struct efi_mm = {
+ .mm_rb = RB_ROOT,
+ .mm_users = ATOMIC_INIT(2),
+ .mm_count = ATOMIC_INIT(1),
+ .mmap_sem = __RWSEM_INITIALIZER(efi_mm.mmap_sem),
+ .page_table_lock = __SPIN_LOCK_UNLOCKED(efi_mm.page_table_lock),
+ .mmlist = LIST_HEAD_INIT(efi_mm.mmlist),
+};
+
static bool disable_runtime;
static int __init setup_noefi(char *arg)
{
@@ -542,9 +551,9 @@ int __init efi_config_parse_tables(void *config_tables, int count, int sz,
seed = early_memremap(efi.rng_seed,
sizeof(*seed) + size);
if (seed != NULL) {
+ pr_notice("seeding entropy pool\n");
add_device_randomness(seed->bits, seed->size);
early_memunmap(seed, sizeof(*seed) + size);
- pr_notice("seeding entropy pool\n");
} else {
pr_err("Could not map UEFI random seed!\n");
}
diff --git a/drivers/firmware/efi/esrt.c b/drivers/firmware/efi/esrt.c
index c47e0c6ec00f..1ab80e06e7c5 100644
--- a/drivers/firmware/efi/esrt.c
+++ b/drivers/firmware/efi/esrt.c
@@ -279,6 +279,7 @@ void __init efi_esrt_init(void)
}
memcpy(&tmpesrt, va, sizeof(tmpesrt));
+ early_memunmap(va, size);
if (tmpesrt.fw_resource_version == 1) {
entry_size = sizeof (*v1_entries);
@@ -291,7 +292,7 @@ void __init efi_esrt_init(void)
if (tmpesrt.fw_resource_count > 0 && max - size < entry_size) {
pr_err("ESRT memory map entry can only hold the header. (max: %zu size: %zu)\n",
max - size, entry_size);
- goto err_memunmap;
+ return;
}
/*
@@ -304,7 +305,7 @@ void __init efi_esrt_init(void)
if (tmpesrt.fw_resource_count > 128) {
pr_err("ESRT says fw_resource_count has very large value %d.\n",
tmpesrt.fw_resource_count);
- goto err_memunmap;
+ return;
}
/*
@@ -315,18 +316,10 @@ void __init efi_esrt_init(void)
if (max < size + entries_size) {
pr_err("ESRT does not fit on single memory map entry (size: %zu max: %zu)\n",
size, max);
- goto err_memunmap;
+ return;
}
- /* remap it with our (plausible) new pages */
- early_memunmap(va, size);
size += entries_size;
- va = early_memremap(efi.esrt, size);
- if (!va) {
- pr_err("early_memremap(%p, %zu) failed.\n", (void *)efi.esrt,
- size);
- return;
- }
esrt_data = (phys_addr_t)efi.esrt;
esrt_data_size = size;
@@ -336,8 +329,6 @@ void __init efi_esrt_init(void)
efi_mem_reserve(esrt_data, esrt_data_size);
pr_debug("esrt-init: loaded.\n");
-err_memunmap:
- early_memunmap(va, size);
}
static int __init register_entries(void)
diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
index 7b3ba40f0745..a34e9290a699 100644
--- a/drivers/firmware/efi/libstub/Makefile
+++ b/drivers/firmware/efi/libstub/Makefile
@@ -9,7 +9,7 @@ cflags-$(CONFIG_X86_32) := -march=i386
cflags-$(CONFIG_X86_64) := -mcmodel=small
cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ -O2 \
-fPIC -fno-strict-aliasing -mno-red-zone \
- -mno-mmx -mno-sse
+ -mno-mmx -mno-sse -fshort-wchar
cflags-$(CONFIG_ARM64) := $(subst -pg,,$(KBUILD_CFLAGS)) -fpie
cflags-$(CONFIG_ARM) := $(subst -pg,,$(KBUILD_CFLAGS)) \
diff --git a/drivers/firmware/efi/libstub/secureboot.c b/drivers/firmware/efi/libstub/secureboot.c
index 959777ec8a77..8f07eb414c00 100644
--- a/drivers/firmware/efi/libstub/secureboot.c
+++ b/drivers/firmware/efi/libstub/secureboot.c
@@ -16,18 +16,12 @@
/* BIOS variables */
static const efi_guid_t efi_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
-static const efi_char16_t efi_SecureBoot_name[] = {
- 'S', 'e', 'c', 'u', 'r', 'e', 'B', 'o', 'o', 't', 0
-};
-static const efi_char16_t efi_SetupMode_name[] = {
- 'S', 'e', 't', 'u', 'p', 'M', 'o', 'd', 'e', 0
-};
+static const efi_char16_t efi_SecureBoot_name[] = L"SecureBoot";
+static const efi_char16_t efi_SetupMode_name[] = L"SetupMode";
/* SHIM variables */
static const efi_guid_t shim_guid = EFI_SHIM_LOCK_GUID;
-static efi_char16_t const shim_MokSBState_name[] = {
- 'M', 'o', 'k', 'S', 'B', 'S', 't', 'a', 't', 'e', 0
-};
+static const efi_char16_t shim_MokSBState_name[] = L"MokSBState";
#define get_efi_var(name, vendor, ...) \
efi_call_runtime(get_variable, \
diff --git a/drivers/firmware/efi/libstub/tpm.c b/drivers/firmware/efi/libstub/tpm.c
index 13c1edd37e96..9d08cea3f1b0 100644
--- a/drivers/firmware/efi/libstub/tpm.c
+++ b/drivers/firmware/efi/libstub/tpm.c
@@ -16,11 +16,8 @@
#include "efistub.h"
#ifdef CONFIG_RESET_ATTACK_MITIGATION
-static const efi_char16_t efi_MemoryOverWriteRequest_name[] = {
- 'M', 'e', 'm', 'o', 'r', 'y', 'O', 'v', 'e', 'r', 'w', 'r', 'i', 't',
- 'e', 'R', 'e', 'q', 'u', 'e', 's', 't', 'C', 'o', 'n', 't', 'r', 'o',
- 'l', 0
-};
+static const efi_char16_t efi_MemoryOverWriteRequest_name[] =
+ L"MemoryOverwriteRequestControl";
#define MEMORY_ONLY_RESET_CONTROL_GUID \
EFI_GUID(0xe20939be, 0x32d4, 0x41be, 0xa1, 0x50, 0x89, 0x7f, 0x85, 0xd4, 0x98, 0x29)
diff --git a/drivers/firmware/meson/meson_sm.c b/drivers/firmware/meson/meson_sm.c
index ff204421117b..0ec2ca87318c 100644
--- a/drivers/firmware/meson/meson_sm.c
+++ b/drivers/firmware/meson/meson_sm.c
@@ -17,8 +17,10 @@
#include <linux/arm-smccc.h>
#include <linux/bug.h>
#include <linux/io.h>
+#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/platform_device.h>
#include <linux/printk.h>
#include <linux/types.h>
#include <linux/sizes.h>
@@ -217,21 +219,11 @@ static const struct of_device_id meson_sm_ids[] = {
{ /* sentinel */ },
};
-int __init meson_sm_init(void)
+static int __init meson_sm_probe(struct platform_device *pdev)
{
const struct meson_sm_chip *chip;
- const struct of_device_id *matched_np;
- struct device_node *np;
- np = of_find_matching_node_and_match(NULL, meson_sm_ids, &matched_np);
- if (!np)
- return -ENODEV;
-
- chip = matched_np->data;
- if (!chip) {
- pr_err("unable to setup secure-monitor data\n");
- goto out;
- }
+ chip = of_match_device(meson_sm_ids, &pdev->dev)->data;
if (chip->cmd_shmem_in_base) {
fw.sm_shmem_in_base = meson_sm_map_shmem(chip->cmd_shmem_in_base,
@@ -257,4 +249,11 @@ out_in_base:
out:
return -EINVAL;
}
-device_initcall(meson_sm_init);
+
+static struct platform_driver meson_sm_driver = {
+ .driver = {
+ .name = "meson-sm",
+ .of_match_table = of_match_ptr(meson_sm_ids),
+ },
+};
+module_platform_driver_probe(meson_sm_driver, meson_sm_probe);
diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c
index a41b572eeeb1..14fedbeca724 100644
--- a/drivers/firmware/qemu_fw_cfg.c
+++ b/drivers/firmware/qemu_fw_cfg.c
@@ -10,20 +10,21 @@
* and select subsets of aarch64), a Device Tree node (on arm), or using
* a kernel module (or command line) parameter with the following syntax:
*
- * [qemu_fw_cfg.]ioport=<size>@<base>[:<ctrl_off>:<data_off>]
+ * [qemu_fw_cfg.]ioport=<size>@<base>[:<ctrl_off>:<data_off>[:<dma_off>]]
* or
- * [qemu_fw_cfg.]mmio=<size>@<base>[:<ctrl_off>:<data_off>]
+ * [qemu_fw_cfg.]mmio=<size>@<base>[:<ctrl_off>:<data_off>[:<dma_off>]]
*
* where:
* <size> := size of ioport or mmio range
* <base> := physical base address of ioport or mmio range
* <ctrl_off> := (optional) offset of control register
* <data_off> := (optional) offset of data register
+ * <dma_off> := (optional) offset of dma register
*
* e.g.:
- * qemu_fw_cfg.ioport=2@0x510:0:1 (the default on x86)
+ * qemu_fw_cfg.ioport=12@0x510:0:1:4 (the default on x86)
* or
- * qemu_fw_cfg.mmio=0xA@0x9020000:8:0 (the default on arm)
+ * qemu_fw_cfg.mmio=16@0x9020000:8:0:16 (the default on arm)
*/
#include <linux/module.h>
@@ -32,29 +33,17 @@
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/ioport.h>
+#include <uapi/linux/qemu_fw_cfg.h>
+#include <linux/delay.h>
+#include <linux/crash_dump.h>
+#include <linux/crash_core.h>
MODULE_AUTHOR("Gabriel L. Somlo <somlo@cmu.edu>");
MODULE_DESCRIPTION("QEMU fw_cfg sysfs support");
MODULE_LICENSE("GPL");
-/* selector key values for "well-known" fw_cfg entries */
-#define FW_CFG_SIGNATURE 0x00
-#define FW_CFG_ID 0x01
-#define FW_CFG_FILE_DIR 0x19
-
-/* size in bytes of fw_cfg signature */
-#define FW_CFG_SIG_SIZE 4
-
-/* fw_cfg "file name" is up to 56 characters (including terminating nul) */
-#define FW_CFG_MAX_FILE_PATH 56
-
-/* fw_cfg file directory entry type */
-struct fw_cfg_file {
- u32 size;
- u16 select;
- u16 reserved;
- char name[FW_CFG_MAX_FILE_PATH];
-};
+/* fw_cfg revision attribute, in /sys/firmware/qemu_fw_cfg top-level dir. */
+static u32 fw_cfg_rev;
/* fw_cfg device i/o register addresses */
static bool fw_cfg_is_mmio;
@@ -63,19 +52,83 @@ static resource_size_t fw_cfg_p_size;
static void __iomem *fw_cfg_dev_base;
static void __iomem *fw_cfg_reg_ctrl;
static void __iomem *fw_cfg_reg_data;
+static void __iomem *fw_cfg_reg_dma;
/* atomic access to fw_cfg device (potentially slow i/o, so using mutex) */
static DEFINE_MUTEX(fw_cfg_dev_lock);
/* pick appropriate endianness for selector key */
-static inline u16 fw_cfg_sel_endianness(u16 key)
+static void fw_cfg_sel_endianness(u16 key)
+{
+ if (fw_cfg_is_mmio)
+ iowrite16be(key, fw_cfg_reg_ctrl);
+ else
+ iowrite16(key, fw_cfg_reg_ctrl);
+}
+
+#ifdef CONFIG_CRASH_CORE
+static inline bool fw_cfg_dma_enabled(void)
+{
+ return (fw_cfg_rev & FW_CFG_VERSION_DMA) && fw_cfg_reg_dma;
+}
+
+/* qemu fw_cfg device is sync today, but spec says it may become async */
+static void fw_cfg_wait_for_control(struct fw_cfg_dma_access *d)
{
- return fw_cfg_is_mmio ? cpu_to_be16(key) : cpu_to_le16(key);
+ for (;;) {
+ u32 ctrl = be32_to_cpu(READ_ONCE(d->control));
+
+ /* do not reorder the read to d->control */
+ rmb();
+ if ((ctrl & ~FW_CFG_DMA_CTL_ERROR) == 0)
+ return;
+
+ cpu_relax();
+ }
+}
+
+static ssize_t fw_cfg_dma_transfer(void *address, u32 length, u32 control)
+{
+ phys_addr_t dma;
+ struct fw_cfg_dma_access *d = NULL;
+ ssize_t ret = length;
+
+ d = kmalloc(sizeof(*d), GFP_KERNEL);
+ if (!d) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ /* fw_cfg device does not need IOMMU protection, so use physical addresses */
+ *d = (struct fw_cfg_dma_access) {
+ .address = cpu_to_be64(address ? virt_to_phys(address) : 0),
+ .length = cpu_to_be32(length),
+ .control = cpu_to_be32(control)
+ };
+
+ dma = virt_to_phys(d);
+
+ iowrite32be((u64)dma >> 32, fw_cfg_reg_dma);
+ /* force memory to sync before notifying device via MMIO */
+ wmb();
+ iowrite32be(dma, fw_cfg_reg_dma + 4);
+
+ fw_cfg_wait_for_control(d);
+
+ if (be32_to_cpu(READ_ONCE(d->control)) & FW_CFG_DMA_CTL_ERROR) {
+ ret = -EIO;
+ }
+
+end:
+ kfree(d);
+
+ return ret;
}
+#endif
/* read chunk of given fw_cfg blob (caller responsible for sanity-check) */
-static inline void fw_cfg_read_blob(u16 key,
- void *buf, loff_t pos, size_t count)
+static ssize_t fw_cfg_read_blob(u16 key,
+ void *buf, loff_t pos, size_t count)
{
u32 glk = -1U;
acpi_status status;
@@ -88,18 +141,60 @@ static inline void fw_cfg_read_blob(u16 key,
/* Should never get here */
WARN(1, "fw_cfg_read_blob: Failed to lock ACPI!\n");
memset(buf, 0, count);
- return;
+ return -EINVAL;
}
mutex_lock(&fw_cfg_dev_lock);
- iowrite16(fw_cfg_sel_endianness(key), fw_cfg_reg_ctrl);
+ fw_cfg_sel_endianness(key);
while (pos-- > 0)
ioread8(fw_cfg_reg_data);
ioread8_rep(fw_cfg_reg_data, buf, count);
mutex_unlock(&fw_cfg_dev_lock);
acpi_release_global_lock(glk);
+ return count;
+}
+
+#ifdef CONFIG_CRASH_CORE
+/* write chunk of given fw_cfg blob (caller responsible for sanity-check) */
+static ssize_t fw_cfg_write_blob(u16 key,
+ void *buf, loff_t pos, size_t count)
+{
+ u32 glk = -1U;
+ acpi_status status;
+ ssize_t ret = count;
+
+ /* If we have ACPI, ensure mutual exclusion against any potential
+ * device access by the firmware, e.g. via AML methods:
+ */
+ status = acpi_acquire_global_lock(ACPI_WAIT_FOREVER, &glk);
+ if (ACPI_FAILURE(status) && status != AE_NOT_CONFIGURED) {
+ /* Should never get here */
+ WARN(1, "%s: Failed to lock ACPI!\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&fw_cfg_dev_lock);
+ if (pos == 0) {
+ ret = fw_cfg_dma_transfer(buf, count, key << 16
+ | FW_CFG_DMA_CTL_SELECT
+ | FW_CFG_DMA_CTL_WRITE);
+ } else {
+ fw_cfg_sel_endianness(key);
+ ret = fw_cfg_dma_transfer(NULL, pos, FW_CFG_DMA_CTL_SKIP);
+ if (ret < 0)
+ goto end;
+ ret = fw_cfg_dma_transfer(buf, count, FW_CFG_DMA_CTL_WRITE);
+ }
+
+end:
+ mutex_unlock(&fw_cfg_dev_lock);
+
+ acpi_release_global_lock(glk);
+
+ return ret;
}
+#endif /* CONFIG_CRASH_CORE */
/* clean up fw_cfg device i/o */
static void fw_cfg_io_cleanup(void)
@@ -118,12 +213,14 @@ static void fw_cfg_io_cleanup(void)
# if (defined(CONFIG_ARM) || defined(CONFIG_ARM64))
# define FW_CFG_CTRL_OFF 0x08
# define FW_CFG_DATA_OFF 0x00
+# define FW_CFG_DMA_OFF 0x10
# elif (defined(CONFIG_PPC_PMAC) || defined(CONFIG_SPARC32)) /* ppc/mac,sun4m */
# define FW_CFG_CTRL_OFF 0x00
# define FW_CFG_DATA_OFF 0x02
# elif (defined(CONFIG_X86) || defined(CONFIG_SPARC64)) /* x86, sun4u */
# define FW_CFG_CTRL_OFF 0x00
# define FW_CFG_DATA_OFF 0x01
+# define FW_CFG_DMA_OFF 0x04
# else
# error "QEMU FW_CFG not available on this architecture!"
# endif
@@ -133,7 +230,7 @@ static void fw_cfg_io_cleanup(void)
static int fw_cfg_do_platform_probe(struct platform_device *pdev)
{
char sig[FW_CFG_SIG_SIZE];
- struct resource *range, *ctrl, *data;
+ struct resource *range, *ctrl, *data, *dma;
/* acquire i/o range details */
fw_cfg_is_mmio = false;
@@ -170,6 +267,7 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev)
/* were custom register offsets provided (e.g. on the command line)? */
ctrl = platform_get_resource_byname(pdev, IORESOURCE_REG, "ctrl");
data = platform_get_resource_byname(pdev, IORESOURCE_REG, "data");
+ dma = platform_get_resource_byname(pdev, IORESOURCE_REG, "dma");
if (ctrl && data) {
fw_cfg_reg_ctrl = fw_cfg_dev_base + ctrl->start;
fw_cfg_reg_data = fw_cfg_dev_base + data->start;
@@ -179,9 +277,17 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev)
fw_cfg_reg_data = fw_cfg_dev_base + FW_CFG_DATA_OFF;
}
+ if (dma)
+ fw_cfg_reg_dma = fw_cfg_dev_base + dma->start;
+#ifdef FW_CFG_DMA_OFF
+ else
+ fw_cfg_reg_dma = fw_cfg_dev_base + FW_CFG_DMA_OFF;
+#endif
+
/* verify fw_cfg device signature */
- fw_cfg_read_blob(FW_CFG_SIGNATURE, sig, 0, FW_CFG_SIG_SIZE);
- if (memcmp(sig, "QEMU", FW_CFG_SIG_SIZE) != 0) {
+ if (fw_cfg_read_blob(FW_CFG_SIGNATURE, sig,
+ 0, FW_CFG_SIG_SIZE) < 0 ||
+ memcmp(sig, "QEMU", FW_CFG_SIG_SIZE) != 0) {
fw_cfg_io_cleanup();
return -ENODEV;
}
@@ -189,9 +295,6 @@ static int fw_cfg_do_platform_probe(struct platform_device *pdev)
return 0;
}
-/* fw_cfg revision attribute, in /sys/firmware/qemu_fw_cfg top-level dir. */
-static u32 fw_cfg_rev;
-
static ssize_t fw_cfg_showrev(struct kobject *k, struct attribute *a, char *buf)
{
return sprintf(buf, "%u\n", fw_cfg_rev);
@@ -208,10 +311,38 @@ static const struct {
/* fw_cfg_sysfs_entry type */
struct fw_cfg_sysfs_entry {
struct kobject kobj;
- struct fw_cfg_file f;
+ u32 size;
+ u16 select;
+ char name[FW_CFG_MAX_FILE_PATH];
struct list_head list;
};
+#ifdef CONFIG_CRASH_CORE
+static ssize_t fw_cfg_write_vmcoreinfo(const struct fw_cfg_file *f)
+{
+ static struct fw_cfg_vmcoreinfo *data;
+ ssize_t ret;
+
+ data = kmalloc(sizeof(struct fw_cfg_vmcoreinfo), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ *data = (struct fw_cfg_vmcoreinfo) {
+ .guest_format = cpu_to_le16(FW_CFG_VMCOREINFO_FORMAT_ELF),
+ .size = cpu_to_le32(VMCOREINFO_NOTE_SIZE),
+ .paddr = cpu_to_le64(paddr_vmcoreinfo_note())
+ };
+ /* spare ourself reading host format support for now since we
+ * don't know what else to format - host may ignore ours
+ */
+ ret = fw_cfg_write_blob(be16_to_cpu(f->select), data,
+ 0, sizeof(struct fw_cfg_vmcoreinfo));
+
+ kfree(data);
+ return ret;
+}
+#endif /* CONFIG_CRASH_CORE */
+
/* get fw_cfg_sysfs_entry from kobject member */
static inline struct fw_cfg_sysfs_entry *to_entry(struct kobject *kobj)
{
@@ -272,17 +403,17 @@ struct fw_cfg_sysfs_attribute fw_cfg_sysfs_attr_##_attr = { \
static ssize_t fw_cfg_sysfs_show_size(struct fw_cfg_sysfs_entry *e, char *buf)
{
- return sprintf(buf, "%u\n", e->f.size);
+ return sprintf(buf, "%u\n", e->size);
}
static ssize_t fw_cfg_sysfs_show_key(struct fw_cfg_sysfs_entry *e, char *buf)
{
- return sprintf(buf, "%u\n", e->f.select);
+ return sprintf(buf, "%u\n", e->select);
}
static ssize_t fw_cfg_sysfs_show_name(struct fw_cfg_sysfs_entry *e, char *buf)
{
- return sprintf(buf, "%s\n", e->f.name);
+ return sprintf(buf, "%s\n", e->name);
}
static FW_CFG_SYSFS_ATTR(size);
@@ -333,14 +464,13 @@ static ssize_t fw_cfg_sysfs_read_raw(struct file *filp, struct kobject *kobj,
{
struct fw_cfg_sysfs_entry *entry = to_entry(kobj);
- if (pos > entry->f.size)
+ if (pos > entry->size)
return -EINVAL;
- if (count > entry->f.size - pos)
- count = entry->f.size - pos;
+ if (count > entry->size - pos)
+ count = entry->size - pos;
- fw_cfg_read_blob(entry->f.select, buf, pos, count);
- return count;
+ return fw_cfg_read_blob(entry->select, buf, pos, count);
}
static struct bin_attribute fw_cfg_sysfs_attr_raw = {
@@ -452,17 +582,28 @@ static int fw_cfg_register_file(const struct fw_cfg_file *f)
int err;
struct fw_cfg_sysfs_entry *entry;
+#ifdef CONFIG_CRASH_CORE
+ if (fw_cfg_dma_enabled() &&
+ strcmp(f->name, FW_CFG_VMCOREINFO_FILENAME) == 0 &&
+ !is_kdump_kernel()) {
+ if (fw_cfg_write_vmcoreinfo(f) < 0)
+ pr_warn("fw_cfg: failed to write vmcoreinfo");
+ }
+#endif
+
/* allocate new entry */
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
if (!entry)
return -ENOMEM;
/* set file entry information */
- memcpy(&entry->f, f, sizeof(struct fw_cfg_file));
+ entry->size = be32_to_cpu(f->size);
+ entry->select = be16_to_cpu(f->select);
+ memcpy(entry->name, f->name, FW_CFG_MAX_FILE_PATH);
/* register entry under "/sys/firmware/qemu_fw_cfg/by_key/" */
err = kobject_init_and_add(&entry->kobj, &fw_cfg_sysfs_entry_ktype,
- fw_cfg_sel_ko, "%d", entry->f.select);
+ fw_cfg_sel_ko, "%d", entry->select);
if (err)
goto err_register;
@@ -472,7 +613,7 @@ static int fw_cfg_register_file(const struct fw_cfg_file *f)
goto err_add_raw;
/* try adding "/sys/firmware/qemu_fw_cfg/by_name/" symlink */
- fw_cfg_build_symlink(fw_cfg_fname_kset, &entry->kobj, entry->f.name);
+ fw_cfg_build_symlink(fw_cfg_fname_kset, &entry->kobj, entry->name);
/* success, add entry to global cache */
fw_cfg_sysfs_cache_enlist(entry);
@@ -489,28 +630,35 @@ err_register:
static int fw_cfg_register_dir_entries(void)
{
int ret = 0;
+ __be32 files_count;
u32 count, i;
struct fw_cfg_file *dir;
size_t dir_size;
- fw_cfg_read_blob(FW_CFG_FILE_DIR, &count, 0, sizeof(count));
- count = be32_to_cpu(count);
+ ret = fw_cfg_read_blob(FW_CFG_FILE_DIR, &files_count,
+ 0, sizeof(files_count));
+ if (ret < 0)
+ return ret;
+
+ count = be32_to_cpu(files_count);
dir_size = count * sizeof(struct fw_cfg_file);
dir = kmalloc(dir_size, GFP_KERNEL);
if (!dir)
return -ENOMEM;
- fw_cfg_read_blob(FW_CFG_FILE_DIR, dir, sizeof(count), dir_size);
+ ret = fw_cfg_read_blob(FW_CFG_FILE_DIR, dir,
+ sizeof(files_count), dir_size);
+ if (ret < 0)
+ goto end;
for (i = 0; i < count; i++) {
- dir[i].size = be32_to_cpu(dir[i].size);
- dir[i].select = be16_to_cpu(dir[i].select);
ret = fw_cfg_register_file(&dir[i]);
if (ret)
break;
}
+end:
kfree(dir);
return ret;
}
@@ -525,6 +673,7 @@ static inline void fw_cfg_kobj_cleanup(struct kobject *kobj)
static int fw_cfg_sysfs_probe(struct platform_device *pdev)
{
int err;
+ __le32 rev;
/* NOTE: If we supported multiple fw_cfg devices, we'd first create
* a subdirectory named after e.g. pdev->id, then hang per-device
@@ -550,8 +699,11 @@ static int fw_cfg_sysfs_probe(struct platform_device *pdev)
goto err_probe;
/* get revision number, add matching top-level attribute */
- fw_cfg_read_blob(FW_CFG_ID, &fw_cfg_rev, 0, sizeof(fw_cfg_rev));
- fw_cfg_rev = le32_to_cpu(fw_cfg_rev);
+ err = fw_cfg_read_blob(FW_CFG_ID, &rev, 0, sizeof(rev));
+ if (err < 0)
+ goto err_probe;
+
+ fw_cfg_rev = le32_to_cpu(rev);
err = sysfs_create_file(fw_cfg_top_ko, &fw_cfg_rev_attr.attr);
if (err)
goto err_rev;
@@ -597,7 +749,7 @@ MODULE_DEVICE_TABLE(of, fw_cfg_sysfs_mmio_match);
#ifdef CONFIG_ACPI
static const struct acpi_device_id fw_cfg_sysfs_acpi_match[] = {
- { "QEMU0002", },
+ { FW_CFG_ACPI_DEVICE_ID, },
{},
};
MODULE_DEVICE_TABLE(acpi, fw_cfg_sysfs_acpi_match);
@@ -629,6 +781,7 @@ static struct platform_device *fw_cfg_cmdline_dev;
/* use special scanf/printf modifier for phys_addr_t, resource_size_t */
#define PH_ADDR_SCAN_FMT "@%" __PHYS_ADDR_PREFIX "i%n" \
":%" __PHYS_ADDR_PREFIX "i" \
+ ":%" __PHYS_ADDR_PREFIX "i%n" \
":%" __PHYS_ADDR_PREFIX "i%n"
#define PH_ADDR_PR_1_FMT "0x%" __PHYS_ADDR_PREFIX "x@" \
@@ -638,12 +791,15 @@ static struct platform_device *fw_cfg_cmdline_dev;
":%" __PHYS_ADDR_PREFIX "u" \
":%" __PHYS_ADDR_PREFIX "u"
+#define PH_ADDR_PR_4_FMT PH_ADDR_PR_3_FMT \
+ ":%" __PHYS_ADDR_PREFIX "u"
+
static int fw_cfg_cmdline_set(const char *arg, const struct kernel_param *kp)
{
- struct resource res[3] = {};
+ struct resource res[4] = {};
char *str;
phys_addr_t base;
- resource_size_t size, ctrl_off, data_off;
+ resource_size_t size, ctrl_off, data_off, dma_off;
int processed, consumed = 0;
/* only one fw_cfg device can exist system-wide, so if one
@@ -659,19 +815,20 @@ static int fw_cfg_cmdline_set(const char *arg, const struct kernel_param *kp)
/* consume "<size>" portion of command line argument */
size = memparse(arg, &str);
- /* get "@<base>[:<ctrl_off>:<data_off>]" chunks */
+ /* get "@<base>[:<ctrl_off>:<data_off>[:<dma_off>]]" chunks */
processed = sscanf(str, PH_ADDR_SCAN_FMT,
&base, &consumed,
- &ctrl_off, &data_off, &consumed);
+ &ctrl_off, &data_off, &consumed,
+ &dma_off, &consumed);
- /* sscanf() must process precisely 1 or 3 chunks:
+ /* sscanf() must process precisely 1, 3 or 4 chunks:
* <base> is mandatory, optionally followed by <ctrl_off>
- * and <data_off>;
+ * and <data_off>, and <dma_off>;
* there must be no extra characters after the last chunk,
* so str[consumed] must be '\0'.
*/
if (str[consumed] ||
- (processed != 1 && processed != 3))
+ (processed != 1 && processed != 3 && processed != 4))
return -EINVAL;
res[0].start = base;
@@ -688,6 +845,11 @@ static int fw_cfg_cmdline_set(const char *arg, const struct kernel_param *kp)
res[2].start = data_off;
res[2].flags = IORESOURCE_REG;
}
+ if (processed > 3) {
+ res[3].name = "dma";
+ res[3].start = dma_off;
+ res[3].flags = IORESOURCE_REG;
+ }
/* "processed" happens to nicely match the number of resources
* we need to pass in to this platform device.
@@ -720,6 +882,13 @@ static int fw_cfg_cmdline_get(char *buf, const struct kernel_param *kp)
fw_cfg_cmdline_dev->resource[0].start,
fw_cfg_cmdline_dev->resource[1].start,
fw_cfg_cmdline_dev->resource[2].start);
+ case 4:
+ return snprintf(buf, PAGE_SIZE, PH_ADDR_PR_4_FMT,
+ resource_size(&fw_cfg_cmdline_dev->resource[0]),
+ fw_cfg_cmdline_dev->resource[0].start,
+ fw_cfg_cmdline_dev->resource[1].start,
+ fw_cfg_cmdline_dev->resource[2].start,
+ fw_cfg_cmdline_dev->resource[3].start);
}
/* Should never get here */
diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c
index a7f461f2e650..14a456afa379 100644
--- a/drivers/firmware/tegra/bpmp.c
+++ b/drivers/firmware/tegra/bpmp.c
@@ -70,57 +70,20 @@ void tegra_bpmp_put(struct tegra_bpmp *bpmp)
}
EXPORT_SYMBOL_GPL(tegra_bpmp_put);
-static int tegra_bpmp_channel_get_index(struct tegra_bpmp_channel *channel)
-{
- return channel - channel->bpmp->channels;
-}
-
static int
tegra_bpmp_channel_get_thread_index(struct tegra_bpmp_channel *channel)
{
struct tegra_bpmp *bpmp = channel->bpmp;
- unsigned int offset, count;
+ unsigned int count;
int index;
- offset = bpmp->soc->channels.thread.offset;
count = bpmp->soc->channels.thread.count;
- index = tegra_bpmp_channel_get_index(channel);
- if (index < 0)
- return index;
-
- if (index < offset || index >= offset + count)
+ index = channel - channel->bpmp->threaded_channels;
+ if (index < 0 || index >= count)
return -EINVAL;
- return index - offset;
-}
-
-static struct tegra_bpmp_channel *
-tegra_bpmp_channel_get_thread(struct tegra_bpmp *bpmp, unsigned int index)
-{
- unsigned int offset = bpmp->soc->channels.thread.offset;
- unsigned int count = bpmp->soc->channels.thread.count;
-
- if (index >= count)
- return NULL;
-
- return &bpmp->channels[offset + index];
-}
-
-static struct tegra_bpmp_channel *
-tegra_bpmp_channel_get_tx(struct tegra_bpmp *bpmp)
-{
- unsigned int offset = bpmp->soc->channels.cpu_tx.offset;
-
- return &bpmp->channels[offset + smp_processor_id()];
-}
-
-static struct tegra_bpmp_channel *
-tegra_bpmp_channel_get_rx(struct tegra_bpmp *bpmp)
-{
- unsigned int offset = bpmp->soc->channels.cpu_rx.offset;
-
- return &bpmp->channels[offset];
+ return index;
}
static bool tegra_bpmp_message_valid(const struct tegra_bpmp_message *msg)
@@ -271,11 +234,7 @@ tegra_bpmp_write_threaded(struct tegra_bpmp *bpmp, unsigned int mrq,
goto unlock;
}
- channel = tegra_bpmp_channel_get_thread(bpmp, index);
- if (!channel) {
- err = -EINVAL;
- goto unlock;
- }
+ channel = &bpmp->threaded_channels[index];
if (!tegra_bpmp_master_free(channel)) {
err = -EBUSY;
@@ -328,12 +287,18 @@ int tegra_bpmp_transfer_atomic(struct tegra_bpmp *bpmp,
if (!tegra_bpmp_message_valid(msg))
return -EINVAL;
- channel = tegra_bpmp_channel_get_tx(bpmp);
+ channel = bpmp->tx_channel;
+
+ spin_lock(&bpmp->atomic_tx_lock);
err = tegra_bpmp_channel_write(channel, msg->mrq, MSG_ACK,
msg->tx.data, msg->tx.size);
- if (err < 0)
+ if (err < 0) {
+ spin_unlock(&bpmp->atomic_tx_lock);
return err;
+ }
+
+ spin_unlock(&bpmp->atomic_tx_lock);
err = mbox_send_message(bpmp->mbox.channel, NULL);
if (err < 0)
@@ -607,7 +572,7 @@ static void tegra_bpmp_handle_rx(struct mbox_client *client, void *data)
unsigned int i, count;
unsigned long *busy;
- channel = tegra_bpmp_channel_get_rx(bpmp);
+ channel = bpmp->rx_channel;
count = bpmp->soc->channels.thread.count;
busy = bpmp->threaded.busy;
@@ -619,9 +584,7 @@ static void tegra_bpmp_handle_rx(struct mbox_client *client, void *data)
for_each_set_bit(i, busy, count) {
struct tegra_bpmp_channel *channel;
- channel = tegra_bpmp_channel_get_thread(bpmp, i);
- if (!channel)
- continue;
+ channel = &bpmp->threaded_channels[i];
if (tegra_bpmp_master_acked(channel)) {
tegra_bpmp_channel_signal(channel);
@@ -698,7 +661,6 @@ static void tegra_bpmp_channel_cleanup(struct tegra_bpmp_channel *channel)
static int tegra_bpmp_probe(struct platform_device *pdev)
{
- struct tegra_bpmp_channel *channel;
struct tegra_bpmp *bpmp;
unsigned int i;
char tag[32];
@@ -732,7 +694,7 @@ static int tegra_bpmp_probe(struct platform_device *pdev)
}
bpmp->rx.virt = gen_pool_dma_alloc(bpmp->rx.pool, 4096, &bpmp->rx.phys);
- if (!bpmp->rx.pool) {
+ if (!bpmp->rx.virt) {
dev_err(&pdev->dev, "failed to allocate from RX pool\n");
err = -ENOMEM;
goto free_tx;
@@ -758,24 +720,45 @@ static int tegra_bpmp_probe(struct platform_device *pdev)
goto free_rx;
}
- bpmp->num_channels = bpmp->soc->channels.cpu_tx.count +
- bpmp->soc->channels.thread.count +
- bpmp->soc->channels.cpu_rx.count;
+ spin_lock_init(&bpmp->atomic_tx_lock);
+ bpmp->tx_channel = devm_kzalloc(&pdev->dev, sizeof(*bpmp->tx_channel),
+ GFP_KERNEL);
+ if (!bpmp->tx_channel) {
+ err = -ENOMEM;
+ goto free_rx;
+ }
- bpmp->channels = devm_kcalloc(&pdev->dev, bpmp->num_channels,
- sizeof(*channel), GFP_KERNEL);
- if (!bpmp->channels) {
+ bpmp->rx_channel = devm_kzalloc(&pdev->dev, sizeof(*bpmp->rx_channel),
+ GFP_KERNEL);
+ if (!bpmp->rx_channel) {
err = -ENOMEM;
goto free_rx;
}
- /* message channel initialization */
- for (i = 0; i < bpmp->num_channels; i++) {
- struct tegra_bpmp_channel *channel = &bpmp->channels[i];
+ bpmp->threaded_channels = devm_kcalloc(&pdev->dev, bpmp->threaded.count,
+ sizeof(*bpmp->threaded_channels),
+ GFP_KERNEL);
+ if (!bpmp->threaded_channels) {
+ err = -ENOMEM;
+ goto free_rx;
+ }
- err = tegra_bpmp_channel_init(channel, bpmp, i);
+ err = tegra_bpmp_channel_init(bpmp->tx_channel, bpmp,
+ bpmp->soc->channels.cpu_tx.offset);
+ if (err < 0)
+ goto free_rx;
+
+ err = tegra_bpmp_channel_init(bpmp->rx_channel, bpmp,
+ bpmp->soc->channels.cpu_rx.offset);
+ if (err < 0)
+ goto cleanup_tx_channel;
+
+ for (i = 0; i < bpmp->threaded.count; i++) {
+ err = tegra_bpmp_channel_init(
+ &bpmp->threaded_channels[i], bpmp,
+ bpmp->soc->channels.thread.offset + i);
if (err < 0)
- goto cleanup_channels;
+ goto cleanup_threaded_channels;
}
/* mbox registration */
@@ -788,15 +771,14 @@ static int tegra_bpmp_probe(struct platform_device *pdev)
if (IS_ERR(bpmp->mbox.channel)) {
err = PTR_ERR(bpmp->mbox.channel);
dev_err(&pdev->dev, "failed to get HSP mailbox: %d\n", err);
- goto cleanup_channels;
+ goto cleanup_threaded_channels;
}
/* reset message channels */
- for (i = 0; i < bpmp->num_channels; i++) {
- struct tegra_bpmp_channel *channel = &bpmp->channels[i];
-
- tegra_bpmp_channel_reset(channel);
- }
+ tegra_bpmp_channel_reset(bpmp->tx_channel);
+ tegra_bpmp_channel_reset(bpmp->rx_channel);
+ for (i = 0; i < bpmp->threaded.count; i++)
+ tegra_bpmp_channel_reset(&bpmp->threaded_channels[i]);
err = tegra_bpmp_request_mrq(bpmp, MRQ_PING,
tegra_bpmp_mrq_handle_ping, bpmp);
@@ -845,9 +827,15 @@ free_mrq:
tegra_bpmp_free_mrq(bpmp, MRQ_PING, bpmp);
free_mbox:
mbox_free_channel(bpmp->mbox.channel);
-cleanup_channels:
- while (i--)
- tegra_bpmp_channel_cleanup(&bpmp->channels[i]);
+cleanup_threaded_channels:
+ for (i = 0; i < bpmp->threaded.count; i++) {
+ if (bpmp->threaded_channels[i].bpmp)
+ tegra_bpmp_channel_cleanup(&bpmp->threaded_channels[i]);
+ }
+
+ tegra_bpmp_channel_cleanup(bpmp->rx_channel);
+cleanup_tx_channel:
+ tegra_bpmp_channel_cleanup(bpmp->tx_channel);
free_rx:
gen_pool_free(bpmp->rx.pool, (unsigned long)bpmp->rx.virt, 4096);
free_tx:
@@ -858,18 +846,16 @@ free_tx:
static const struct tegra_bpmp_soc tegra186_soc = {
.channels = {
.cpu_tx = {
- .offset = 0,
- .count = 6,
+ .offset = 3,
.timeout = 60 * USEC_PER_SEC,
},
.thread = {
- .offset = 6,
- .count = 7,
+ .offset = 0,
+ .count = 3,
.timeout = 600 * USEC_PER_SEC,
},
.cpu_rx = {
.offset = 13,
- .count = 1,
.timeout = 0,
},
},
diff --git a/drivers/fmc/fmc-core.c b/drivers/fmc/fmc-core.c
index cec3b8db0d69..bbcb505d1522 100644
--- a/drivers/fmc/fmc-core.c
+++ b/drivers/fmc/fmc-core.c
@@ -244,7 +244,7 @@ int fmc_device_register_n_gw(struct fmc_device **devs, int n,
if (!fmc->carrier_name || !fmc->carrier_data ||
!fmc->device_id) {
dev_err(fmc->hwdev,
- "deivce nr %i: carrier name, "
+ "device nr %i: carrier name, "
"data or dev_id not set\n", i);
ret = -EINVAL;
}
diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
index 00e73d28077c..77b04e4b3254 100644
--- a/drivers/fpga/altera-cvp.c
+++ b/drivers/fpga/altera-cvp.c
@@ -384,8 +384,6 @@ static int altera_cvp_probe(struct pci_dev *pdev,
const struct pci_device_id *dev_id);
static void altera_cvp_remove(struct pci_dev *pdev);
-#define PCI_VENDOR_ID_ALTERA 0x1172
-
static struct pci_device_id altera_cvp_id_tbl[] = {
{ PCI_VDEVICE(ALTERA, PCI_ANY_ID) },
{ }
diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c
index 14f14efdf0d5..06d212a3d49d 100644
--- a/drivers/fpga/altera-ps-spi.c
+++ b/drivers/fpga/altera-ps-spi.c
@@ -249,7 +249,7 @@ static int altera_ps_probe(struct spi_device *spi)
conf->data = of_id->data;
conf->spi = spi;
- conf->config = devm_gpiod_get(&spi->dev, "nconfig", GPIOD_OUT_HIGH);
+ conf->config = devm_gpiod_get(&spi->dev, "nconfig", GPIOD_OUT_LOW);
if (IS_ERR(conf->config)) {
dev_err(&spi->dev, "Failed to get config gpio: %ld\n",
PTR_ERR(conf->config));
diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
index 513e35173aaa..a326ed663d3c 100644
--- a/drivers/fsi/Kconfig
+++ b/drivers/fsi/Kconfig
@@ -4,6 +4,7 @@
menuconfig FSI
tristate "FSI support"
+ depends on OF
select CRC4
---help---
FSI - the FRU Support Interface - is a simple bus for low-level
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index e318bf8c623c..4c03d6933646 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -18,6 +18,7 @@
#include <linux/fsi.h>
#include <linux/idr.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/slab.h>
#include <linux/bitops.h>
@@ -142,6 +143,7 @@ static void fsi_device_release(struct device *_device)
{
struct fsi_device *device = to_fsi_dev(_device);
+ of_node_put(device->dev.of_node);
kfree(device);
}
@@ -205,7 +207,7 @@ static int fsi_slave_report_and_clear_errors(struct fsi_slave *slave)
if (rc)
return rc;
- dev_info(&slave->dev, "status: 0x%08x, sisc: 0x%08x\n",
+ dev_dbg(&slave->dev, "status: 0x%08x, sisc: 0x%08x\n",
be32_to_cpu(stat), be32_to_cpu(irq));
/* clear interrupts */
@@ -334,6 +336,57 @@ extern void fsi_slave_release_range(struct fsi_slave *slave,
}
EXPORT_SYMBOL_GPL(fsi_slave_release_range);
+static bool fsi_device_node_matches(struct device *dev, struct device_node *np,
+ uint32_t addr, uint32_t size)
+{
+ unsigned int len, na, ns;
+ const __be32 *prop;
+ uint32_t psize;
+
+ na = of_n_addr_cells(np);
+ ns = of_n_size_cells(np);
+
+ if (na != 1 || ns != 1)
+ return false;
+
+ prop = of_get_property(np, "reg", &len);
+ if (!prop || len != 8)
+ return false;
+
+ if (of_read_number(prop, 1) != addr)
+ return false;
+
+ psize = of_read_number(prop + 1, 1);
+ if (psize != size) {
+ dev_warn(dev,
+ "node %s matches probed address, but not size (got 0x%x, expected 0x%x)",
+ of_node_full_name(np), psize, size);
+ }
+
+ return true;
+}
+
+/* Find a matching node for the slave engine at @address, using @size bytes
+ * of space. Returns NULL if not found, or a matching node with refcount
+ * already incremented.
+ */
+static struct device_node *fsi_device_find_of_node(struct fsi_device *dev)
+{
+ struct device_node *parent, *np;
+
+ parent = dev_of_node(&dev->slave->dev);
+ if (!parent)
+ return NULL;
+
+ for_each_child_of_node(parent, np) {
+ if (fsi_device_node_matches(&dev->dev, np,
+ dev->addr, dev->size))
+ return np;
+ }
+
+ return NULL;
+}
+
static int fsi_slave_scan(struct fsi_slave *slave)
{
uint32_t engine_addr;
@@ -402,6 +455,7 @@ static int fsi_slave_scan(struct fsi_slave *slave)
dev_set_name(&dev->dev, "%02x:%02x:%02x:%02x",
slave->master->idx, slave->link,
slave->id, i - 2);
+ dev->dev.of_node = fsi_device_find_of_node(dev);
rc = device_register(&dev->dev);
if (rc) {
@@ -558,9 +612,53 @@ static void fsi_slave_release(struct device *dev)
{
struct fsi_slave *slave = to_fsi_slave(dev);
+ of_node_put(dev->of_node);
kfree(slave);
}
+static bool fsi_slave_node_matches(struct device_node *np,
+ int link, uint8_t id)
+{
+ unsigned int len, na, ns;
+ const __be32 *prop;
+
+ na = of_n_addr_cells(np);
+ ns = of_n_size_cells(np);
+
+ /* Ensure we have the correct format for addresses and sizes in
+ * reg properties
+ */
+ if (na != 2 || ns != 0)
+ return false;
+
+ prop = of_get_property(np, "reg", &len);
+ if (!prop || len != 8)
+ return false;
+
+ return (of_read_number(prop, 1) == link) &&
+ (of_read_number(prop + 1, 1) == id);
+}
+
+/* Find a matching node for the slave at (link, id). Returns NULL if none
+ * found, or a matching node with refcount already incremented.
+ */
+static struct device_node *fsi_slave_find_of_node(struct fsi_master *master,
+ int link, uint8_t id)
+{
+ struct device_node *parent, *np;
+
+ parent = dev_of_node(&master->dev);
+ if (!parent)
+ return NULL;
+
+ for_each_child_of_node(parent, np) {
+ if (fsi_slave_node_matches(np, link, id))
+ return np;
+ }
+
+ return NULL;
+}
+
static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
{
uint32_t chip_id, llmode;
@@ -589,7 +687,7 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
return -EIO;
}
- dev_info(&master->dev, "fsi: found chip %08x at %02x:%02x:%02x\n",
+ dev_dbg(&master->dev, "fsi: found chip %08x at %02x:%02x:%02x\n",
chip_id, master->idx, link, id);
rc = fsi_slave_set_smode(master, link, id);
@@ -623,6 +721,7 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
slave->master = master;
slave->dev.parent = &master->dev;
+ slave->dev.of_node = fsi_slave_find_of_node(master, link, id);
slave->dev.release = fsi_slave_release;
slave->link = link;
slave->id = id;
@@ -656,10 +755,13 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
/* FSI master support */
static int fsi_check_access(uint32_t addr, size_t size)
{
- if (size != 1 && size != 2 && size != 4)
- return -EINVAL;
-
- if ((addr & 0x3) != (size & 0x3))
+ if (size == 4) {
+ if (addr & 0x3)
+ return -EINVAL;
+ } else if (size == 2) {
+ if (addr & 0x1)
+ return -EINVAL;
+ } else if (size != 1)
return -EINVAL;
return 0;
@@ -762,14 +864,20 @@ static void fsi_master_unscan(struct fsi_master *master)
device_for_each_child(&master->dev, NULL, fsi_master_remove_slave);
}
+int fsi_master_rescan(struct fsi_master *master)
+{
+ fsi_master_unscan(master);
+ return fsi_master_scan(master);
+}
+EXPORT_SYMBOL_GPL(fsi_master_rescan);
+
static ssize_t master_rescan_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct fsi_master *master = to_fsi_master(dev);
int rc;
- fsi_master_unscan(master);
- rc = fsi_master_scan(master);
+ rc = fsi_master_rescan(master);
if (rc < 0)
return rc;
@@ -793,6 +901,7 @@ static DEVICE_ATTR(break, 0200, NULL, master_break_store);
int fsi_master_register(struct fsi_master *master)
{
int rc;
+ struct device_node *np;
if (!master)
return -EINVAL;
@@ -820,7 +929,9 @@ int fsi_master_register(struct fsi_master *master)
return rc;
}
- fsi_master_scan(master);
+ np = dev_of_node(&master->dev);
+ if (!of_property_read_bool(np, "no-scan-on-init"))
+ fsi_master_scan(master);
return 0;
}
diff --git a/drivers/fsi/fsi-master-gpio.c b/drivers/fsi/fsi-master-gpio.c
index ae2618768508..3f487449a277 100644
--- a/drivers/fsi/fsi-master-gpio.c
+++ b/drivers/fsi/fsi-master-gpio.c
@@ -9,6 +9,7 @@
#include <linux/gpio/consumer.h>
#include <linux/io.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
@@ -59,6 +60,7 @@ struct fsi_master_gpio {
struct gpio_desc *gpio_trans; /* Voltage translator */
struct gpio_desc *gpio_enable; /* FSI enable */
struct gpio_desc *gpio_mux; /* Mux control */
+ bool external_mode;
};
#define CREATE_TRACE_POINTS
@@ -411,6 +413,12 @@ static int fsi_master_gpio_xfer(struct fsi_master_gpio *master, uint8_t slave,
int rc;
spin_lock_irqsave(&master->cmd_lock, flags);
+
+ if (master->external_mode) {
+ spin_unlock_irqrestore(&master->cmd_lock, flags);
+ return -EBUSY;
+ }
+
serial_out(master, cmd);
echo_delay(master);
rc = poll_for_response(master, slave, resp_len, resp);
@@ -461,12 +469,18 @@ static int fsi_master_gpio_term(struct fsi_master *_master,
static int fsi_master_gpio_break(struct fsi_master *_master, int link)
{
struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+ unsigned long flags;
if (link != 0)
return -ENODEV;
trace_fsi_master_gpio_break(master);
+ spin_lock_irqsave(&master->cmd_lock, flags);
+ if (master->external_mode) {
+ spin_unlock_irqrestore(&master->cmd_lock, flags);
+ return -EBUSY;
+ }
set_sda_output(master, 1);
sda_out(master, 1);
clock_toggle(master, FSI_PRE_BREAK_CLOCKS);
@@ -475,6 +489,7 @@ static int fsi_master_gpio_break(struct fsi_master *_master, int link)
echo_delay(master);
sda_out(master, 1);
clock_toggle(master, FSI_POST_BREAK_CLOCKS);
+ spin_unlock_irqrestore(&master->cmd_lock, flags);
/* Wait for logic reset to take effect */
udelay(200);
@@ -494,21 +509,84 @@ static void fsi_master_gpio_init(struct fsi_master_gpio *master)
clock_zeros(master, FSI_INIT_CLOCKS);
}
+static void fsi_master_gpio_init_external(struct fsi_master_gpio *master)
+{
+ gpiod_direction_output(master->gpio_mux, 0);
+ gpiod_direction_output(master->gpio_trans, 0);
+ gpiod_direction_output(master->gpio_enable, 1);
+ gpiod_direction_input(master->gpio_clk);
+ gpiod_direction_input(master->gpio_data);
+}
+
static int fsi_master_gpio_link_enable(struct fsi_master *_master, int link)
{
struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+ unsigned long flags;
+ int rc = -EBUSY;
if (link != 0)
return -ENODEV;
- gpiod_set_value(master->gpio_enable, 1);
- return 0;
+ spin_lock_irqsave(&master->cmd_lock, flags);
+ if (!master->external_mode) {
+ gpiod_set_value(master->gpio_enable, 1);
+ rc = 0;
+ }
+ spin_unlock_irqrestore(&master->cmd_lock, flags);
+
+ return rc;
+}
+
+static ssize_t external_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fsi_master_gpio *master = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE - 1, "%u\n",
+ master->external_mode ? 1 : 0);
}
+static ssize_t external_mode_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct fsi_master_gpio *master = dev_get_drvdata(dev);
+ unsigned long flags, val;
+ bool external_mode;
+ int err;
+
+ err = kstrtoul(buf, 0, &val);
+ if (err)
+ return err;
+
+ external_mode = !!val;
+
+ spin_lock_irqsave(&master->cmd_lock, flags);
+
+ if (external_mode == master->external_mode) {
+ spin_unlock_irqrestore(&master->cmd_lock, flags);
+ return count;
+ }
+
+ master->external_mode = external_mode;
+ if (master->external_mode)
+ fsi_master_gpio_init_external(master);
+ else
+ fsi_master_gpio_init(master);
+ spin_unlock_irqrestore(&master->cmd_lock, flags);
+
+ fsi_master_rescan(&master->master);
+
+ return count;
+}
+
+static DEVICE_ATTR(external_mode, 0664,
+ external_mode_show, external_mode_store);
+
static int fsi_master_gpio_probe(struct platform_device *pdev)
{
struct fsi_master_gpio *master;
struct gpio_desc *gpio;
+ int rc;
master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL);
if (!master)
@@ -516,6 +594,7 @@ static int fsi_master_gpio_probe(struct platform_device *pdev)
master->dev = &pdev->dev;
master->master.dev.parent = master->dev;
+ master->master.dev.of_node = of_node_get(dev_of_node(master->dev));
gpio = devm_gpiod_get(&pdev->dev, "clock", 0);
if (IS_ERR(gpio)) {
@@ -565,6 +644,10 @@ static int fsi_master_gpio_probe(struct platform_device *pdev)
fsi_master_gpio_init(master);
+ rc = device_create_file(&pdev->dev, &dev_attr_external_mode);
+ if (rc)
+ return rc;
+
return fsi_master_register(&master->master);
}
@@ -583,6 +666,8 @@ static int fsi_master_gpio_remove(struct platform_device *pdev)
devm_gpiod_put(&pdev->dev, master->gpio_mux);
fsi_master_unregister(&master->master);
+ of_node_put(master->master.dev.of_node);
+
return 0;
}
diff --git a/drivers/fsi/fsi-master-hub.c b/drivers/fsi/fsi-master-hub.c
index 133b9bff1d65..5885fc4a1ef0 100644
--- a/drivers/fsi/fsi-master-hub.c
+++ b/drivers/fsi/fsi-master-hub.c
@@ -16,6 +16,7 @@
#include <linux/delay.h>
#include <linux/fsi.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/slab.h>
#include "fsi-master.h"
@@ -253,7 +254,7 @@ static int hub_master_probe(struct device *dev)
reg = be32_to_cpu(__reg);
links = (reg >> 8) & 0xff;
- dev_info(dev, "hub version %08x (%d links)\n", reg, links);
+ dev_dbg(dev, "hub version %08x (%d links)\n", reg, links);
rc = fsi_slave_claim_range(fsi_dev->slave, FSI_HUB_LINK_OFFSET,
FSI_HUB_LINK_SIZE * links);
@@ -274,6 +275,7 @@ static int hub_master_probe(struct device *dev)
hub->master.dev.parent = dev;
hub->master.dev.release = hub_master_release;
+ hub->master.dev.of_node = of_node_get(dev_of_node(dev));
hub->master.n_links = links;
hub->master.read = hub_master_read;
@@ -286,10 +288,19 @@ static int hub_master_probe(struct device *dev)
hub_master_init(hub);
rc = fsi_master_register(&hub->master);
- if (!rc)
- return 0;
+ if (rc)
+ goto err_release;
+
+ /* At this point, fsi_master_register performs the device_initialize(),
+ * and holds the sole reference on master.dev. This means the device
+ * will be freed (via ->release) during any subsequent call to
+ * fsi_master_unregister. We add our own reference to it here, so we
+ * can perform cleanup (in _remove()) without it being freed before
+ * we're ready.
+ */
+ get_device(&hub->master.dev);
+ return 0;
- kfree(hub);
err_release:
fsi_slave_release_range(fsi_dev->slave, FSI_HUB_LINK_OFFSET,
FSI_HUB_LINK_SIZE * links);
@@ -302,6 +313,14 @@ static int hub_master_remove(struct device *dev)
fsi_master_unregister(&hub->master);
fsi_slave_release_range(hub->upstream->slave, hub->addr, hub->size);
+ of_node_put(hub->master.dev.of_node);
+
+ /*
+ * master.dev will likely be ->release()ed after this, which free()s
+ * the hub
+ */
+ put_device(&hub->master.dev);
+
return 0;
}
diff --git a/drivers/fsi/fsi-master.h b/drivers/fsi/fsi-master.h
index 12f7b119567d..ee0b46086026 100644
--- a/drivers/fsi/fsi-master.h
+++ b/drivers/fsi/fsi-master.h
@@ -37,7 +37,24 @@ struct fsi_master {
#define dev_to_fsi_master(d) container_of(d, struct fsi_master, dev)
+/**
+ * fsi_master registration & lifetime: the fsi_master_register() and
+ * fsi_master_unregister() functions will take ownership of the master, and
+ * ->dev in particular. The registration path performs a get_device(), which
+ * takes the first reference on the device. Similarly, the unregistration path
+ * performs a put_device(), which may well drop the last reference.
+ *
+ * This means that master implementations *may* need to hold their own
+ * reference (via get_device()) on master->dev. In particular, if the device's
+ * ->release callback frees the fsi_master, then fsi_master_unregister will
+ * invoke this free if no other reference is held.
+ *
+ * The same applies for the error path of fsi_master_register; if the call
+ * fails, dev->release will have been invoked.
+ */
extern int fsi_master_register(struct fsi_master *master);
extern void fsi_master_unregister(struct fsi_master *master);
+extern int fsi_master_rescan(struct fsi_master *master);
+
#endif /* DRIVERS_FSI_MASTER_H */
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 8dbb2280538d..b960f6f35abd 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -122,6 +122,17 @@ config GPIO_ATH79
Select this option to enable GPIO driver for
Atheros AR71XX/AR724X/AR913X SoC devices.
+config GPIO_RASPBERRYPI_EXP
+ tristate "Raspberry Pi 3 GPIO Expander"
+ default RASPBERRYPI_FIRMWARE
+ depends on OF_GPIO
+ # Make sure not 'y' when RASPBERRYPI_FIRMWARE is 'm'. This can only
+ # happen when COMPILE_TEST=y, hence the added !RASPBERRYPI_FIRMWARE.
+ depends on (ARCH_BCM2835 && RASPBERRYPI_FIRMWARE) || (COMPILE_TEST && !RASPBERRYPI_FIRMWARE)
+ help
+ Turn on GPIO support for the expander on Raspberry Pi 3 boards, using
+ the firmware mailbox to communicate with VideoCore on BCM283x chips.
+
config GPIO_BCM_KONA
bool "Broadcom Kona GPIO"
depends on OF_GPIO && (ARCH_BCM_MOBILE || COMPILE_TEST)
@@ -159,6 +170,14 @@ config GPIO_DWAPB
Say Y or M here to build support for the Synopsys DesignWare APB
GPIO block.
+config GPIO_EIC_SPRD
+ tristate "Spreadtrum EIC support"
+ depends on ARCH_SPRD || COMPILE_TEST
+ depends on OF_GPIO
+ select GPIOLIB_IRQCHIP
+ help
+ Say yes here to support Spreadtrum EIC device.
+
config GPIO_EM
tristate "Emma Mobile GPIO"
depends on (ARCH_EMEV2 || COMPILE_TEST) && OF_GPIO
@@ -170,15 +189,6 @@ config GPIO_EP93XX
depends on ARCH_EP93XX
select GPIO_GENERIC
-config GPIO_ETRAXFS
- bool "Axis ETRAX FS General I/O"
- depends on CRIS || COMPILE_TEST
- depends on OF_GPIO
- select GPIO_GENERIC
- select GPIOLIB_IRQCHIP
- help
- Say yes here to support the GPIO controller on Axis ETRAX FS SoCs.
-
config GPIO_EXAR
tristate "Support for GPIO pins on XR17V352/354/358"
depends on SERIAL_8250_EXAR
@@ -223,6 +233,15 @@ config GPIO_GRGPIO
Select this to support Aeroflex Gaisler GRGPIO cores from the GRLIB
VHDL IP core library.
+config GPIO_HLWD
+ tristate "Nintendo Wii (Hollywood) GPIO"
+ depends on OF_GPIO
+ select GPIO_GENERIC
+ help
+ Select this to support the GPIO controller of the Nintendo Wii.
+
+ If unsure, say N.
+
config GPIO_ICH
tristate "Intel ICH GPIO"
depends on PCI && X86
@@ -372,6 +391,14 @@ config GPIO_PL061
help
Say yes here to support the PrimeCell PL061 GPIO device
+config GPIO_PMIC_EIC_SPRD
+ tristate "Spreadtrum PMIC EIC support"
+ depends on MFD_SC27XX_PMIC || COMPILE_TEST
+ depends on OF_GPIO
+ select GPIOLIB_IRQCHIP
+ help
+ Say yes here to support Spreadtrum PMIC EIC device.
+
config GPIO_PXA
bool "PXA GPIO support"
depends on ARCH_PXA || ARCH_MMP
@@ -398,6 +425,14 @@ config GPIO_SPEAR_SPICS
help
Say yes here to support ST SPEAr SPI Chip Select as GPIO device
+config GPIO_SPRD
+ tristate "Spreadtrum GPIO support"
+ depends on ARCH_SPRD || COMPILE_TEST
+ depends on OF_GPIO
+ select GPIOLIB_IRQCHIP
+ help
+ Say yes here to support Spreadtrum GPIO device.
+
config GPIO_STA2X11
bool "STA2x11/ConneXt GPIO support"
depends on MFD_STA2X11
@@ -462,21 +497,6 @@ config GPIO_THUNDERX
Say yes here to support the on-chip GPIO lines on the ThunderX
and OCTEON-TX families of SoCs.
-config GPIO_TZ1090
- bool "Toumaz Xenif TZ1090 GPIO support"
- depends on SOC_TZ1090
- select GENERIC_IRQ_CHIP
- default y
- help
- Say yes here to support Toumaz Xenif TZ1090 GPIOs.
-
-config GPIO_TZ1090_PDC
- bool "Toumaz Xenif TZ1090 PDC GPIO support"
- depends on SOC_TZ1090
- default y
- help
- Say yes here to support Toumaz Xenif TZ1090 PDC GPIOs.
-
config GPIO_UNIPHIER
tristate "UniPhier GPIO support"
depends on ARCH_UNIPHIER || COMPILE_TEST
@@ -590,7 +610,8 @@ menu "Port-mapped I/O GPIO drivers"
config GPIO_104_DIO_48E
tristate "ACCES 104-DIO-48E GPIO support"
- depends on PC104 && ISA_BUS_API
+ depends on PC104
+ select ISA_BUS_API
select GPIOLIB_IRQCHIP
help
Enables GPIO support for the ACCES 104-DIO-48E series (104-DIO-48E,
@@ -600,7 +621,8 @@ config GPIO_104_DIO_48E
config GPIO_104_IDIO_16
tristate "ACCES 104-IDIO-16 GPIO support"
- depends on PC104 && ISA_BUS_API
+ depends on PC104
+ select ISA_BUS_API
select GPIOLIB_IRQCHIP
help
Enables GPIO support for the ACCES 104-IDIO-16 family (104-IDIO-16,
@@ -611,7 +633,8 @@ config GPIO_104_IDIO_16
config GPIO_104_IDI_48
tristate "ACCES 104-IDI-48 GPIO support"
- depends on PC104 && ISA_BUS_API
+ depends on PC104
+ select ISA_BUS_API
select GPIOLIB_IRQCHIP
help
Enables GPIO support for the ACCES 104-IDI-48 family (104-IDI-48A,
@@ -631,7 +654,8 @@ config GPIO_F7188X
config GPIO_GPIO_MM
tristate "Diamond Systems GPIO-MM GPIO support"
- depends on PC104 && ISA_BUS_API
+ depends on PC104
+ select ISA_BUS_API
help
Enables GPIO support for the Diamond Systems GPIO-MM and GPIO-MM-12.
@@ -700,7 +724,7 @@ config GPIO_TS5500
config GPIO_WINBOND
tristate "Winbond Super I/O GPIO support"
- depends on ISA_BUS_API
+ select ISA_BUS_API
help
This option enables support for GPIOs found on Winbond Super I/O
chips.
@@ -716,7 +740,7 @@ config GPIO_WINBOND
config GPIO_WS16C48
tristate "WinSystems WS16C48 GPIO support"
- depends on ISA_BUS_API
+ select ISA_BUS_API
select GPIOLIB_IRQCHIP
help
Enables GPIO support for the WinSystems WS16C48. The base port
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index cccb0d40846c..1324c8f966a7 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_GPIO_AMDPT) += gpio-amdpt.o
obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o
obj-$(CONFIG_GPIO_ATH79) += gpio-ath79.o
obj-$(CONFIG_GPIO_ASPEED) += gpio-aspeed.o
+obj-$(CONFIG_GPIO_RASPBERRYPI_EXP) += gpio-raspberrypi-exp.o
obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o
obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o
obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o
@@ -44,15 +45,16 @@ obj-$(CONFIG_GPIO_DA9055) += gpio-da9055.o
obj-$(CONFIG_GPIO_DAVINCI) += gpio-davinci.o
obj-$(CONFIG_GPIO_DLN2) += gpio-dln2.o
obj-$(CONFIG_GPIO_DWAPB) += gpio-dwapb.o
+obj-$(CONFIG_GPIO_EIC_SPRD) += gpio-eic-sprd.o
obj-$(CONFIG_GPIO_EM) += gpio-em.o
obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o
-obj-$(CONFIG_GPIO_ETRAXFS) += gpio-etraxfs.o
obj-$(CONFIG_GPIO_EXAR) += gpio-exar.o
obj-$(CONFIG_GPIO_F7188X) += gpio-f7188x.o
obj-$(CONFIG_GPIO_FTGPIO010) += gpio-ftgpio010.o
obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o
obj-$(CONFIG_GPIO_GPIO_MM) += gpio-gpio-mm.o
obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o
+obj-$(CONFIG_GPIO_HLWD) += gpio-hlwd.o
obj-$(CONFIG_HTC_EGPIO) += gpio-htc-egpio.o
obj-$(CONFIG_GPIO_ICH) += gpio-ich.o
obj-$(CONFIG_GPIO_INGENIC) += gpio-ingenic.o
@@ -98,6 +100,7 @@ obj-$(CONFIG_GPIO_PCI_IDIO_16) += gpio-pci-idio-16.o
obj-$(CONFIG_GPIO_PCIE_IDIO_24) += gpio-pcie-idio-24.o
obj-$(CONFIG_GPIO_PISOSR) += gpio-pisosr.o
obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o
+obj-$(CONFIG_GPIO_PMIC_EIC_SPRD) += gpio-pmic-eic-sprd.o
obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o
obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o
obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o
@@ -108,6 +111,7 @@ obj-$(CONFIG_GPIO_SCH) += gpio-sch.o
obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o
obj-$(CONFIG_GPIO_SODAVILLE) += gpio-sodaville.o
obj-$(CONFIG_GPIO_SPEAR_SPICS) += gpio-spear-spics.o
+obj-$(CONFIG_GPIO_SPRD) += gpio-sprd.o
obj-$(CONFIG_GPIO_STA2X11) += gpio-sta2x11.o
obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o
obj-$(CONFIG_GPIO_STP_XWAY) += gpio-stp-xway.o
@@ -131,8 +135,6 @@ obj-$(CONFIG_GPIO_TS4900) += gpio-ts4900.o
obj-$(CONFIG_GPIO_TS5500) += gpio-ts5500.o
obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o
obj-$(CONFIG_GPIO_TWL6040) += gpio-twl6040.o
-obj-$(CONFIG_GPIO_TZ1090) += gpio-tz1090.o
-obj-$(CONFIG_GPIO_TZ1090_PDC) += gpio-tz1090-pdc.o
obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o
obj-$(CONFIG_GPIO_UNIPHIER) += gpio-uniphier.o
obj-$(CONFIG_GPIO_VF610) += gpio-vf610.o
diff --git a/drivers/gpio/gpio-104-dio-48e.c b/drivers/gpio/gpio-104-dio-48e.c
index bab3b94c5cbc..31e22c93e844 100644
--- a/drivers/gpio/gpio-104-dio-48e.c
+++ b/drivers/gpio/gpio-104-dio-48e.c
@@ -14,6 +14,7 @@
* This driver supports the following ACCES devices: 104-DIO-48E and
* 104-DIO-24E.
*/
+#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/errno.h>
@@ -182,6 +183,51 @@ static int dio48e_gpio_get(struct gpio_chip *chip, unsigned offset)
return !!(port_state & mask);
}
+static int dio48e_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
+ unsigned long *bits)
+{
+ struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip);
+ size_t i;
+ const size_t ports[] = { 0, 1, 2, 4, 5, 6 };
+ const unsigned int gpio_reg_size = 8;
+ unsigned int bits_offset;
+ size_t word_index;
+ unsigned int word_offset;
+ unsigned long word_mask;
+ const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0);
+ unsigned long port_state;
+
+ /* clear bits array to a clean slate */
+ bitmap_zero(bits, chip->ngpio);
+
+ /* get bits are evaluated a gpio port register at a time */
+ for (i = 0; i < ARRAY_SIZE(ports); i++) {
+ /* gpio offset in bits array */
+ bits_offset = i * gpio_reg_size;
+
+ /* word index for bits array */
+ word_index = BIT_WORD(bits_offset);
+
+ /* gpio offset within current word of bits array */
+ word_offset = bits_offset % BITS_PER_LONG;
+
+ /* mask of get bits for current gpio within current word */
+ word_mask = mask[word_index] & (port_mask << word_offset);
+ if (!word_mask) {
+ /* no get bits in this port so skip to next one */
+ continue;
+ }
+
+ /* read bits from current gpio port */
+ port_state = inb(dio48egpio->base + ports[i]);
+
+ /* store acquired bits at respective bits array offset */
+ bits[word_index] |= port_state << word_offset;
+ }
+
+ return 0;
+}
+
static void dio48e_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip);
@@ -384,6 +430,7 @@ static int dio48e_probe(struct device *dev, unsigned int id)
dio48egpio->chip.direction_input = dio48e_gpio_direction_input;
dio48egpio->chip.direction_output = dio48e_gpio_direction_output;
dio48egpio->chip.get = dio48e_gpio_get;
+ dio48egpio->chip.get_multiple = dio48e_gpio_get_multiple;
dio48egpio->chip.set = dio48e_gpio_set;
dio48egpio->chip.set_multiple = dio48e_gpio_set_multiple;
dio48egpio->base = base[id];
diff --git a/drivers/gpio/gpio-104-idi-48.c b/drivers/gpio/gpio-104-idi-48.c
index add859d59766..f35632609379 100644
--- a/drivers/gpio/gpio-104-idi-48.c
+++ b/drivers/gpio/gpio-104-idi-48.c
@@ -14,6 +14,7 @@
* This driver supports the following ACCES devices: 104-IDI-48A,
* 104-IDI-48AC, 104-IDI-48B, and 104-IDI-48BC.
*/
+#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/errno.h>
@@ -88,6 +89,51 @@ static int idi_48_gpio_get(struct gpio_chip *chip, unsigned offset)
return 0;
}
+static int idi_48_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
+ unsigned long *bits)
+{
+ struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip);
+ size_t i;
+ const size_t ports[] = { 0, 1, 2, 4, 5, 6 };
+ const unsigned int gpio_reg_size = 8;
+ unsigned int bits_offset;
+ size_t word_index;
+ unsigned int word_offset;
+ unsigned long word_mask;
+ const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0);
+ unsigned long port_state;
+
+ /* clear bits array to a clean slate */
+ bitmap_zero(bits, chip->ngpio);
+
+ /* get bits are evaluated a gpio port register at a time */
+ for (i = 0; i < ARRAY_SIZE(ports); i++) {
+ /* gpio offset in bits array */
+ bits_offset = i * gpio_reg_size;
+
+ /* word index for bits array */
+ word_index = BIT_WORD(bits_offset);
+
+ /* gpio offset within current word of bits array */
+ word_offset = bits_offset % BITS_PER_LONG;
+
+ /* mask of get bits for current gpio within current word */
+ word_mask = mask[word_index] & (port_mask << word_offset);
+ if (!word_mask) {
+ /* no get bits in this port so skip to next one */
+ continue;
+ }
+
+ /* read bits from current gpio port */
+ port_state = inb(idi48gpio->base + ports[i]);
+
+ /* store acquired bits at respective bits array offset */
+ bits[word_index] |= port_state << word_offset;
+ }
+
+ return 0;
+}
+
static void idi_48_irq_ack(struct irq_data *data)
{
}
@@ -256,6 +302,7 @@ static int idi_48_probe(struct device *dev, unsigned int id)
idi48gpio->chip.get_direction = idi_48_gpio_get_direction;
idi48gpio->chip.direction_input = idi_48_gpio_direction_input;
idi48gpio->chip.get = idi_48_gpio_get;
+ idi48gpio->chip.get_multiple = idi_48_gpio_get_multiple;
idi48gpio->base = base[id];
raw_spin_lock_init(&idi48gpio->lock);
diff --git a/drivers/gpio/gpio-104-idio-16.c b/drivers/gpio/gpio-104-idio-16.c
index 2f16638a0589..96700308c1e3 100644
--- a/drivers/gpio/gpio-104-idio-16.c
+++ b/drivers/gpio/gpio-104-idio-16.c
@@ -90,6 +90,20 @@ static int idio_16_gpio_get(struct gpio_chip *chip, unsigned offset)
return !!(inb(idio16gpio->base + 5) & (mask>>8));
}
+static int idio_16_gpio_get_multiple(struct gpio_chip *chip,
+ unsigned long *mask, unsigned long *bits)
+{
+ struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
+
+ *bits = 0;
+ if (*mask & GENMASK(23, 16))
+ *bits |= (unsigned long)inb(idio16gpio->base + 1) << 16;
+ if (*mask & GENMASK(31, 24))
+ *bits |= (unsigned long)inb(idio16gpio->base + 5) << 24;
+
+ return 0;
+}
+
static void idio_16_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
@@ -244,6 +258,7 @@ static int idio_16_probe(struct device *dev, unsigned int id)
idio16gpio->chip.direction_input = idio_16_gpio_direction_input;
idio16gpio->chip.direction_output = idio_16_gpio_direction_output;
idio16gpio->chip.get = idio_16_gpio_get;
+ idio16gpio->chip.get_multiple = idio_16_gpio_get_multiple;
idio16gpio->chip.set = idio_16_gpio_set;
idio16gpio->chip.set_multiple = idio_16_gpio_set_multiple;
idio16gpio->base = base[id];
diff --git a/drivers/gpio/gpio-ath79.c b/drivers/gpio/gpio-ath79.c
index 3ae7c1876bf4..684e9d6d6623 100644
--- a/drivers/gpio/gpio-ath79.c
+++ b/drivers/gpio/gpio-ath79.c
@@ -258,6 +258,8 @@ static int ath79_gpio_probe(struct platform_device *pdev)
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
ctrl->base = devm_ioremap_nocache(
&pdev->dev, res->start, resource_size(res));
if (!ctrl->base)
diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c
index 0b951ca78ec4..987126c4c6f6 100644
--- a/drivers/gpio/gpio-davinci.c
+++ b/drivers/gpio/gpio-davinci.c
@@ -20,6 +20,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/platform_data/gpio-davinci.h>
#include <linux/irqchip/chained_irq.h>
@@ -225,6 +226,11 @@ static int davinci_gpio_probe(struct platform_device *pdev)
chips->chip.of_gpio_n_cells = 2;
chips->chip.parent = dev;
chips->chip.of_node = dev->of_node;
+
+ if (of_property_read_bool(dev->of_node, "gpio-ranges")) {
+ chips->chip.request = gpiochip_generic_request;
+ chips->chip.free = gpiochip_generic_free;
+ }
#endif
spin_lock_init(&chips->lock);
bank_base += ngpio;
diff --git a/drivers/gpio/gpio-dln2.c b/drivers/gpio/gpio-dln2.c
index 1dada68b9a27..c4e7953c093e 100644
--- a/drivers/gpio/gpio-dln2.c
+++ b/drivers/gpio/gpio-dln2.c
@@ -15,7 +15,6 @@
#include <linux/irqdomain.h>
#include <linux/irq.h>
#include <linux/irqchip/chained_irq.h>
-#include <linux/gpio.h>
#include <linux/gpio/driver.h>
#include <linux/platform_device.h>
#include <linux/mfd/dln2.h>
@@ -204,9 +203,9 @@ static int dln2_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
struct dln2_gpio *dln2 = gpiochip_get_data(chip);
if (test_bit(offset, dln2->output_enabled))
- return GPIOF_DIR_OUT;
+ return 0;
- return GPIOF_DIR_IN;
+ return 1;
}
static int dln2_gpio_get(struct gpio_chip *chip, unsigned int offset)
@@ -218,7 +217,7 @@ static int dln2_gpio_get(struct gpio_chip *chip, unsigned int offset)
if (dir < 0)
return dir;
- if (dir == GPIOF_DIR_IN)
+ if (dir == 1)
return dln2_gpio_pin_get_in_val(dln2, offset);
return dln2_gpio_pin_get_out_val(dln2, offset);
diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c
index 6730c6642ce3..226977f78482 100644
--- a/drivers/gpio/gpio-dwapb.c
+++ b/drivers/gpio/gpio-dwapb.c
@@ -8,10 +8,9 @@
* All enquiries to support@picochip.com
*/
#include <linux/acpi.h>
-#include <linux/gpio/driver.h>
-/* FIXME: for gpio_get_value(), replace this with direct register read */
-#include <linux/gpio.h>
+#include <linux/clk.h>
#include <linux/err.h>
+#include <linux/gpio/driver.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@@ -53,9 +52,9 @@
#define GPIO_EXT_PORTD 0x5c
#define DWAPB_MAX_PORTS 4
-#define GPIO_EXT_PORT_SIZE (GPIO_EXT_PORTB - GPIO_EXT_PORTA)
-#define GPIO_SWPORT_DR_SIZE (GPIO_SWPORTB_DR - GPIO_SWPORTA_DR)
-#define GPIO_SWPORT_DDR_SIZE (GPIO_SWPORTB_DDR - GPIO_SWPORTA_DDR)
+#define GPIO_EXT_PORT_STRIDE 0x04 /* register stride 32 bits */
+#define GPIO_SWPORT_DR_STRIDE 0x0c /* register stride 3*32 bits */
+#define GPIO_SWPORT_DDR_STRIDE 0x0c /* register stride 3*32 bits */
#define GPIO_REG_OFFSET_V2 1
@@ -100,6 +99,7 @@ struct dwapb_gpio {
struct irq_domain *domain;
unsigned int flags;
struct reset_control *rst;
+ struct clk *clk;
};
static inline u32 gpio_reg_v2_convert(unsigned int offset)
@@ -153,16 +153,40 @@ static int dwapb_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
return irq_find_mapping(gpio->domain, offset);
}
+static struct dwapb_gpio_port *dwapb_offs_to_port(struct dwapb_gpio *gpio, unsigned int offs)
+{
+ struct dwapb_gpio_port *port;
+ int i;
+
+ for (i = 0; i < gpio->nr_ports; i++) {
+ port = &gpio->ports[i];
+ if (port->idx == offs / 32)
+ return port;
+ }
+
+ return NULL;
+}
+
static void dwapb_toggle_trigger(struct dwapb_gpio *gpio, unsigned int offs)
{
- u32 v = dwapb_read(gpio, GPIO_INT_POLARITY);
+ struct dwapb_gpio_port *port = dwapb_offs_to_port(gpio, offs);
+ struct gpio_chip *gc;
+ u32 pol;
+ int val;
+
+ if (!port)
+ return;
+ gc = &port->gc;
- if (gpio_get_value(gpio->ports[0].gc.base + offs))
- v &= ~BIT(offs);
+ pol = dwapb_read(gpio, GPIO_INT_POLARITY);
+ /* Just read the current value right out of the data register */
+ val = gc->get(gc, offs % 32);
+ if (val)
+ pol &= ~BIT(offs);
else
- v |= BIT(offs);
+ pol |= BIT(offs);
- dwapb_write(gpio, GPIO_INT_POLARITY, v);
+ dwapb_write(gpio, GPIO_INT_POLARITY, pol);
}
static u32 dwapb_do_irq(struct dwapb_gpio *gpio)
@@ -476,11 +500,12 @@ static int dwapb_gpio_add_port(struct dwapb_gpio *gpio,
return -ENOMEM;
#endif
- dat = gpio->regs + GPIO_EXT_PORTA + (pp->idx * GPIO_EXT_PORT_SIZE);
- set = gpio->regs + GPIO_SWPORTA_DR + (pp->idx * GPIO_SWPORT_DR_SIZE);
+ dat = gpio->regs + GPIO_EXT_PORTA + (pp->idx * GPIO_EXT_PORT_STRIDE);
+ set = gpio->regs + GPIO_SWPORTA_DR + (pp->idx * GPIO_SWPORT_DR_STRIDE);
dirout = gpio->regs + GPIO_SWPORTA_DDR +
- (pp->idx * GPIO_SWPORT_DDR_SIZE);
+ (pp->idx * GPIO_SWPORT_DDR_STRIDE);
+ /* This registers 32 GPIO lines per port */
err = bgpio_init(&port->gc, gpio->dev, 4, dat, set, NULL, dirout,
NULL, 0);
if (err) {
@@ -647,6 +672,16 @@ static int dwapb_gpio_probe(struct platform_device *pdev)
if (IS_ERR(gpio->regs))
return PTR_ERR(gpio->regs);
+ /* Optional bus clock */
+ gpio->clk = devm_clk_get(&pdev->dev, "bus");
+ if (!IS_ERR(gpio->clk)) {
+ err = clk_prepare_enable(gpio->clk);
+ if (err) {
+ dev_info(&pdev->dev, "Cannot enable clock\n");
+ return err;
+ }
+ }
+
gpio->flags = 0;
if (dev->of_node) {
const struct of_device_id *of_devid;
@@ -689,6 +724,7 @@ static int dwapb_gpio_remove(struct platform_device *pdev)
dwapb_gpio_unregister(gpio);
dwapb_irq_teardown(gpio);
reset_control_assert(gpio->rst);
+ clk_disable_unprepare(gpio->clk);
return 0;
}
@@ -710,13 +746,13 @@ static int dwapb_gpio_suspend(struct device *dev)
BUG_ON(!ctx);
- offset = GPIO_SWPORTA_DDR + idx * GPIO_SWPORT_DDR_SIZE;
+ offset = GPIO_SWPORTA_DDR + idx * GPIO_SWPORT_DDR_STRIDE;
ctx->dir = dwapb_read(gpio, offset);
- offset = GPIO_SWPORTA_DR + idx * GPIO_SWPORT_DR_SIZE;
+ offset = GPIO_SWPORTA_DR + idx * GPIO_SWPORT_DR_STRIDE;
ctx->data = dwapb_read(gpio, offset);
- offset = GPIO_EXT_PORTA + idx * GPIO_EXT_PORT_SIZE;
+ offset = GPIO_EXT_PORTA + idx * GPIO_EXT_PORT_STRIDE;
ctx->ext = dwapb_read(gpio, offset);
/* Only port A can provide interrupts */
@@ -734,6 +770,8 @@ static int dwapb_gpio_suspend(struct device *dev)
}
spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+ clk_disable_unprepare(gpio->clk);
+
return 0;
}
@@ -745,6 +783,9 @@ static int dwapb_gpio_resume(struct device *dev)
unsigned long flags;
int i;
+ if (!IS_ERR(gpio->clk))
+ clk_prepare_enable(gpio->clk);
+
spin_lock_irqsave(&gc->bgpio_lock, flags);
for (i = 0; i < gpio->nr_ports; i++) {
unsigned int offset;
@@ -753,13 +794,13 @@ static int dwapb_gpio_resume(struct device *dev)
BUG_ON(!ctx);
- offset = GPIO_SWPORTA_DR + idx * GPIO_SWPORT_DR_SIZE;
+ offset = GPIO_SWPORTA_DR + idx * GPIO_SWPORT_DR_STRIDE;
dwapb_write(gpio, offset, ctx->data);
- offset = GPIO_SWPORTA_DDR + idx * GPIO_SWPORT_DDR_SIZE;
+ offset = GPIO_SWPORTA_DDR + idx * GPIO_SWPORT_DDR_STRIDE;
dwapb_write(gpio, offset, ctx->dir);
- offset = GPIO_EXT_PORTA + idx * GPIO_EXT_PORT_SIZE;
+ offset = GPIO_EXT_PORTA + idx * GPIO_EXT_PORT_STRIDE;
dwapb_write(gpio, offset, ctx->ext);
/* Only port A can provide interrupts */
diff --git a/drivers/gpio/gpio-eic-sprd.c b/drivers/gpio/gpio-eic-sprd.c
new file mode 100644
index 000000000000..de7dd939c043
--- /dev/null
+++ b/drivers/gpio/gpio-eic-sprd.c
@@ -0,0 +1,606 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Spreadtrum Communications Inc.
+ * Copyright (C) 2018 Linaro Ltd.
+ */
+
+#include <linux/bitops.h>
+#include <linux/gpio/driver.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+/* EIC registers definition */
+#define SPRD_EIC_DBNC_DATA 0x0
+#define SPRD_EIC_DBNC_DMSK 0x4
+#define SPRD_EIC_DBNC_IEV 0x14
+#define SPRD_EIC_DBNC_IE 0x18
+#define SPRD_EIC_DBNC_RIS 0x1c
+#define SPRD_EIC_DBNC_MIS 0x20
+#define SPRD_EIC_DBNC_IC 0x24
+#define SPRD_EIC_DBNC_TRIG 0x28
+#define SPRD_EIC_DBNC_CTRL0 0x40
+
+#define SPRD_EIC_LATCH_INTEN 0x0
+#define SPRD_EIC_LATCH_INTRAW 0x4
+#define SPRD_EIC_LATCH_INTMSK 0x8
+#define SPRD_EIC_LATCH_INTCLR 0xc
+#define SPRD_EIC_LATCH_INTPOL 0x10
+#define SPRD_EIC_LATCH_INTMODE 0x14
+
+#define SPRD_EIC_ASYNC_INTIE 0x0
+#define SPRD_EIC_ASYNC_INTRAW 0x4
+#define SPRD_EIC_ASYNC_INTMSK 0x8
+#define SPRD_EIC_ASYNC_INTCLR 0xc
+#define SPRD_EIC_ASYNC_INTMODE 0x10
+#define SPRD_EIC_ASYNC_INTBOTH 0x14
+#define SPRD_EIC_ASYNC_INTPOL 0x18
+#define SPRD_EIC_ASYNC_DATA 0x1c
+
+#define SPRD_EIC_SYNC_INTIE 0x0
+#define SPRD_EIC_SYNC_INTRAW 0x4
+#define SPRD_EIC_SYNC_INTMSK 0x8
+#define SPRD_EIC_SYNC_INTCLR 0xc
+#define SPRD_EIC_SYNC_INTMODE 0x10
+#define SPRD_EIC_SYNC_INTBOTH 0x14
+#define SPRD_EIC_SYNC_INTPOL 0x18
+#define SPRD_EIC_SYNC_DATA 0x1c
+
+/*
+ * The digital-chip EIC controller can support maximum 3 banks, and each bank
+ * contains 8 EICs.
+ */
+#define SPRD_EIC_MAX_BANK 3
+#define SPRD_EIC_PER_BANK_NR 8
+#define SPRD_EIC_DATA_MASK GENMASK(7, 0)
+#define SPRD_EIC_BIT(x) ((x) & (SPRD_EIC_PER_BANK_NR - 1))
+#define SPRD_EIC_DBNC_MASK GENMASK(11, 0)
+
+/*
+ * The Spreadtrum EIC (external interrupt controller) can be used only in
+ * input mode to generate interrupts if detecting input signals.
+ *
+ * The Spreadtrum digital-chip EIC controller contains 4 sub-modules:
+ * debounce EIC, latch EIC, async EIC and sync EIC,
+ *
+ * The debounce EIC is used to capture the input signals' stable status
+ * (millisecond resolution) and a single-trigger mechanism is introduced
+ * into this sub-module to enhance the input event detection reliability.
+ * The debounce range is from 1ms to 4s with a step size of 1ms.
+ *
+ * The latch EIC is used to latch some special power down signals and
+ * generate interrupts, since the latch EIC does not depend on the APB clock
+ * to capture signals.
+ *
+ * The async EIC uses a 32k clock to capture the short signals (microsecond
+ * resolution) to generate interrupts by level or edge trigger.
+ *
+ * The EIC-sync is similar with GPIO's input function, which is a synchronized
+ * signal input register.
+ */
+enum sprd_eic_type {
+ SPRD_EIC_DEBOUNCE,
+ SPRD_EIC_LATCH,
+ SPRD_EIC_ASYNC,
+ SPRD_EIC_SYNC,
+ SPRD_EIC_MAX,
+};
+
+struct sprd_eic {
+ struct gpio_chip chip;
+ struct irq_chip intc;
+ void __iomem *base[SPRD_EIC_MAX_BANK];
+ enum sprd_eic_type type;
+ spinlock_t lock;
+ int irq;
+};
+
+struct sprd_eic_variant_data {
+ enum sprd_eic_type type;
+ u32 num_eics;
+};
+
+static const char *sprd_eic_label_name[SPRD_EIC_MAX] = {
+ "eic-debounce", "eic-latch", "eic-async",
+ "eic-sync",
+};
+
+static const struct sprd_eic_variant_data sc9860_eic_dbnc_data = {
+ .type = SPRD_EIC_DEBOUNCE,
+ .num_eics = 8,
+};
+
+static const struct sprd_eic_variant_data sc9860_eic_latch_data = {
+ .type = SPRD_EIC_LATCH,
+ .num_eics = 8,
+};
+
+static const struct sprd_eic_variant_data sc9860_eic_async_data = {
+ .type = SPRD_EIC_ASYNC,
+ .num_eics = 8,
+};
+
+static const struct sprd_eic_variant_data sc9860_eic_sync_data = {
+ .type = SPRD_EIC_SYNC,
+ .num_eics = 8,
+};
+
+static inline void __iomem *sprd_eic_offset_base(struct sprd_eic *sprd_eic,
+ unsigned int bank)
+{
+ if (bank >= SPRD_EIC_MAX_BANK)
+ return NULL;
+
+ return sprd_eic->base[bank];
+}
+
+static void sprd_eic_update(struct gpio_chip *chip, unsigned int offset,
+ u16 reg, unsigned int val)
+{
+ struct sprd_eic *sprd_eic = gpiochip_get_data(chip);
+ void __iomem *base =
+ sprd_eic_offset_base(sprd_eic, offset / SPRD_EIC_PER_BANK_NR);
+ unsigned long flags;
+ u32 tmp;
+
+ spin_lock_irqsave(&sprd_eic->lock, flags);
+ tmp = readl_relaxed(base + reg);
+
+ if (val)
+ tmp |= BIT(SPRD_EIC_BIT(offset));
+ else
+ tmp &= ~BIT(SPRD_EIC_BIT(offset));
+
+ writel_relaxed(tmp, base + reg);
+ spin_unlock_irqrestore(&sprd_eic->lock, flags);
+}
+
+static int sprd_eic_read(struct gpio_chip *chip, unsigned int offset, u16 reg)
+{
+ struct sprd_eic *sprd_eic = gpiochip_get_data(chip);
+ void __iomem *base =
+ sprd_eic_offset_base(sprd_eic, offset / SPRD_EIC_PER_BANK_NR);
+
+ return !!(readl_relaxed(base + reg) & BIT(SPRD_EIC_BIT(offset)));
+}
+
+static int sprd_eic_request(struct gpio_chip *chip, unsigned int offset)
+{
+ sprd_eic_update(chip, offset, SPRD_EIC_DBNC_DMSK, 1);
+ return 0;
+}
+
+static void sprd_eic_free(struct gpio_chip *chip, unsigned int offset)
+{
+ sprd_eic_update(chip, offset, SPRD_EIC_DBNC_DMSK, 0);
+}
+
+static int sprd_eic_get(struct gpio_chip *chip, unsigned int offset)
+{
+ return sprd_eic_read(chip, offset, SPRD_EIC_DBNC_DATA);
+}
+
+static int sprd_eic_direction_input(struct gpio_chip *chip, unsigned int offset)
+{
+ /* EICs are always input, nothing need to do here. */
+ return 0;
+}
+
+static void sprd_eic_set(struct gpio_chip *chip, unsigned int offset, int value)
+{
+ /* EICs are always input, nothing need to do here. */
+}
+
+static int sprd_eic_set_debounce(struct gpio_chip *chip, unsigned int offset,
+ unsigned int debounce)
+{
+ struct sprd_eic *sprd_eic = gpiochip_get_data(chip);
+ void __iomem *base =
+ sprd_eic_offset_base(sprd_eic, offset / SPRD_EIC_PER_BANK_NR);
+ u32 reg = SPRD_EIC_DBNC_CTRL0 + SPRD_EIC_BIT(offset) * 0x4;
+ u32 value = readl_relaxed(base + reg) & ~SPRD_EIC_DBNC_MASK;
+
+ value |= (debounce / 1000) & SPRD_EIC_DBNC_MASK;
+ writel_relaxed(value, base + reg);
+
+ return 0;
+}
+
+static int sprd_eic_set_config(struct gpio_chip *chip, unsigned int offset,
+ unsigned long config)
+{
+ unsigned long param = pinconf_to_config_param(config);
+ u32 arg = pinconf_to_config_argument(config);
+
+ if (param == PIN_CONFIG_INPUT_DEBOUNCE)
+ return sprd_eic_set_debounce(chip, offset, arg);
+
+ return -ENOTSUPP;
+}
+
+static void sprd_eic_irq_mask(struct irq_data *data)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ struct sprd_eic *sprd_eic = gpiochip_get_data(chip);
+ u32 offset = irqd_to_hwirq(data);
+
+ switch (sprd_eic->type) {
+ case SPRD_EIC_DEBOUNCE:
+ sprd_eic_update(chip, offset, SPRD_EIC_DBNC_IE, 0);
+ sprd_eic_update(chip, offset, SPRD_EIC_DBNC_TRIG, 0);
+ break;
+ case SPRD_EIC_LATCH:
+ sprd_eic_update(chip, offset, SPRD_EIC_LATCH_INTEN, 0);
+ break;
+ case SPRD_EIC_ASYNC:
+ sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTIE, 0);
+ break;
+ case SPRD_EIC_SYNC:
+ sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTIE, 0);
+ break;
+ default:
+ dev_err(chip->parent, "Unsupported EIC type.\n");
+ }
+}
+
+static void sprd_eic_irq_unmask(struct irq_data *data)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ struct sprd_eic *sprd_eic = gpiochip_get_data(chip);
+ u32 offset = irqd_to_hwirq(data);
+
+ switch (sprd_eic->type) {
+ case SPRD_EIC_DEBOUNCE:
+ sprd_eic_update(chip, offset, SPRD_EIC_DBNC_IE, 1);
+ sprd_eic_update(chip, offset, SPRD_EIC_DBNC_TRIG, 1);
+ break;
+ case SPRD_EIC_LATCH:
+ sprd_eic_update(chip, offset, SPRD_EIC_LATCH_INTEN, 1);
+ break;
+ case SPRD_EIC_ASYNC:
+ sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTIE, 1);
+ break;
+ case SPRD_EIC_SYNC:
+ sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTIE, 1);
+ break;
+ default:
+ dev_err(chip->parent, "Unsupported EIC type.\n");
+ }
+}
+
+static void sprd_eic_irq_ack(struct irq_data *data)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ struct sprd_eic *sprd_eic = gpiochip_get_data(chip);
+ u32 offset = irqd_to_hwirq(data);
+
+ switch (sprd_eic->type) {
+ case SPRD_EIC_DEBOUNCE:
+ sprd_eic_update(chip, offset, SPRD_EIC_DBNC_IC, 1);
+ break;
+ case SPRD_EIC_LATCH:
+ sprd_eic_update(chip, offset, SPRD_EIC_LATCH_INTCLR, 1);
+ break;
+ case SPRD_EIC_ASYNC:
+ sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTCLR, 1);
+ break;
+ case SPRD_EIC_SYNC:
+ sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTCLR, 1);
+ break;
+ default:
+ dev_err(chip->parent, "Unsupported EIC type.\n");
+ }
+}
+
+static int sprd_eic_irq_set_type(struct irq_data *data, unsigned int flow_type)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ struct sprd_eic *sprd_eic = gpiochip_get_data(chip);
+ u32 offset = irqd_to_hwirq(data);
+
+ switch (sprd_eic->type) {
+ case SPRD_EIC_DEBOUNCE:
+ switch (flow_type) {
+ case IRQ_TYPE_LEVEL_HIGH:
+ sprd_eic_update(chip, offset, SPRD_EIC_DBNC_IEV, 1);
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ sprd_eic_update(chip, offset, SPRD_EIC_DBNC_IEV, 0);
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ irq_set_handler_locked(data, handle_level_irq);
+ break;
+ case SPRD_EIC_LATCH:
+ switch (flow_type) {
+ case IRQ_TYPE_LEVEL_HIGH:
+ sprd_eic_update(chip, offset, SPRD_EIC_LATCH_INTPOL, 0);
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ sprd_eic_update(chip, offset, SPRD_EIC_LATCH_INTPOL, 1);
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ irq_set_handler_locked(data, handle_level_irq);
+ break;
+ case SPRD_EIC_ASYNC:
+ switch (flow_type) {
+ case IRQ_TYPE_EDGE_RISING:
+ sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTBOTH, 0);
+ sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTMODE, 0);
+ sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTPOL, 1);
+ irq_set_handler_locked(data, handle_edge_irq);
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTBOTH, 0);
+ sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTMODE, 0);
+ sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTPOL, 0);
+ irq_set_handler_locked(data, handle_edge_irq);
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTBOTH, 1);
+ irq_set_handler_locked(data, handle_edge_irq);
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTBOTH, 0);
+ sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTMODE, 1);
+ sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTPOL, 1);
+ irq_set_handler_locked(data, handle_level_irq);
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTBOTH, 0);
+ sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTMODE, 1);
+ sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTPOL, 0);
+ irq_set_handler_locked(data, handle_level_irq);
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+ break;
+ case SPRD_EIC_SYNC:
+ switch (flow_type) {
+ case IRQ_TYPE_EDGE_RISING:
+ sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTBOTH, 0);
+ sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTMODE, 0);
+ sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTPOL, 1);
+ irq_set_handler_locked(data, handle_edge_irq);
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTBOTH, 0);
+ sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTMODE, 0);
+ sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTPOL, 0);
+ irq_set_handler_locked(data, handle_edge_irq);
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTBOTH, 1);
+ irq_set_handler_locked(data, handle_edge_irq);
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTBOTH, 0);
+ sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTMODE, 1);
+ sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTPOL, 1);
+ irq_set_handler_locked(data, handle_level_irq);
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTBOTH, 0);
+ sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTMODE, 1);
+ sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTPOL, 0);
+ irq_set_handler_locked(data, handle_level_irq);
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+ default:
+ dev_err(chip->parent, "Unsupported EIC type.\n");
+ return -ENOTSUPP;
+ }
+
+ return 0;
+}
+
+static int sprd_eic_match_chip_by_type(struct gpio_chip *chip, void *data)
+{
+ enum sprd_eic_type type = *(enum sprd_eic_type *)data;
+
+ return !strcmp(chip->label, sprd_eic_label_name[type]);
+}
+
+static void sprd_eic_handle_one_type(struct gpio_chip *chip)
+{
+ struct sprd_eic *sprd_eic = gpiochip_get_data(chip);
+ u32 bank, n, girq;
+
+ for (bank = 0; bank * SPRD_EIC_PER_BANK_NR < chip->ngpio; bank++) {
+ void __iomem *base = sprd_eic_offset_base(sprd_eic, bank);
+ unsigned long reg;
+
+ switch (sprd_eic->type) {
+ case SPRD_EIC_DEBOUNCE:
+ reg = readl_relaxed(base + SPRD_EIC_DBNC_MIS) &
+ SPRD_EIC_DATA_MASK;
+ break;
+ case SPRD_EIC_LATCH:
+ reg = readl_relaxed(base + SPRD_EIC_LATCH_INTMSK) &
+ SPRD_EIC_DATA_MASK;
+ break;
+ case SPRD_EIC_ASYNC:
+ reg = readl_relaxed(base + SPRD_EIC_ASYNC_INTMSK) &
+ SPRD_EIC_DATA_MASK;
+ break;
+ case SPRD_EIC_SYNC:
+ reg = readl_relaxed(base + SPRD_EIC_SYNC_INTMSK) &
+ SPRD_EIC_DATA_MASK;
+ break;
+ default:
+ dev_err(chip->parent, "Unsupported EIC type.\n");
+ return;
+ }
+
+ for_each_set_bit(n, &reg, SPRD_EIC_PER_BANK_NR) {
+ girq = irq_find_mapping(chip->irq.domain,
+ bank * SPRD_EIC_PER_BANK_NR + n);
+
+ generic_handle_irq(girq);
+ }
+ }
+}
+
+static void sprd_eic_irq_handler(struct irq_desc *desc)
+{
+ struct irq_chip *ic = irq_desc_get_chip(desc);
+ struct gpio_chip *chip;
+ enum sprd_eic_type type;
+
+ chained_irq_enter(ic, desc);
+
+ /*
+ * Since the digital-chip EIC 4 sub-modules (debounce, latch, async
+ * and sync) share one same interrupt line, we should iterate each
+ * EIC module to check if there are EIC interrupts were triggered.
+ */
+ for (type = SPRD_EIC_DEBOUNCE; type < SPRD_EIC_MAX; type++) {
+ chip = gpiochip_find(&type, sprd_eic_match_chip_by_type);
+ if (!chip)
+ continue;
+
+ sprd_eic_handle_one_type(chip);
+ }
+
+ chained_irq_exit(ic, desc);
+}
+
+static int sprd_eic_probe(struct platform_device *pdev)
+{
+ const struct sprd_eic_variant_data *pdata;
+ struct gpio_irq_chip *irq;
+ struct sprd_eic *sprd_eic;
+ struct resource *res;
+ int ret, i;
+
+ pdata = of_device_get_match_data(&pdev->dev);
+ if (!pdata) {
+ dev_err(&pdev->dev, "No matching driver data found.\n");
+ return -EINVAL;
+ }
+
+ sprd_eic = devm_kzalloc(&pdev->dev, sizeof(*sprd_eic), GFP_KERNEL);
+ if (!sprd_eic)
+ return -ENOMEM;
+
+ spin_lock_init(&sprd_eic->lock);
+ sprd_eic->type = pdata->type;
+
+ sprd_eic->irq = platform_get_irq(pdev, 0);
+ if (sprd_eic->irq < 0) {
+ dev_err(&pdev->dev, "Failed to get EIC interrupt.\n");
+ return sprd_eic->irq;
+ }
+
+ for (i = 0; i < SPRD_EIC_MAX_BANK; i++) {
+ /*
+ * We can have maximum 3 banks EICs, and each EIC has
+ * its own base address. But some platform maybe only
+ * have one bank EIC, thus base[1] and base[2] can be
+ * optional.
+ */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ if (!res)
+ continue;
+
+ sprd_eic->base[i] = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(sprd_eic->base[i]))
+ return PTR_ERR(sprd_eic->base[i]);
+ }
+
+ sprd_eic->chip.label = sprd_eic_label_name[sprd_eic->type];
+ sprd_eic->chip.ngpio = pdata->num_eics;
+ sprd_eic->chip.base = -1;
+ sprd_eic->chip.parent = &pdev->dev;
+ sprd_eic->chip.of_node = pdev->dev.of_node;
+ sprd_eic->chip.direction_input = sprd_eic_direction_input;
+ switch (sprd_eic->type) {
+ case SPRD_EIC_DEBOUNCE:
+ sprd_eic->chip.request = sprd_eic_request;
+ sprd_eic->chip.free = sprd_eic_free;
+ sprd_eic->chip.set_config = sprd_eic_set_config;
+ sprd_eic->chip.set = sprd_eic_set;
+ /* fall-through */
+ case SPRD_EIC_ASYNC:
+ /* fall-through */
+ case SPRD_EIC_SYNC:
+ sprd_eic->chip.get = sprd_eic_get;
+ break;
+ case SPRD_EIC_LATCH:
+ /* fall-through */
+ default:
+ break;
+ }
+
+ sprd_eic->intc.name = dev_name(&pdev->dev);
+ sprd_eic->intc.irq_ack = sprd_eic_irq_ack;
+ sprd_eic->intc.irq_mask = sprd_eic_irq_mask;
+ sprd_eic->intc.irq_unmask = sprd_eic_irq_unmask;
+ sprd_eic->intc.irq_set_type = sprd_eic_irq_set_type;
+ sprd_eic->intc.flags = IRQCHIP_SKIP_SET_WAKE;
+
+ irq = &sprd_eic->chip.irq;
+ irq->chip = &sprd_eic->intc;
+ irq->handler = handle_bad_irq;
+ irq->default_type = IRQ_TYPE_NONE;
+ irq->parent_handler = sprd_eic_irq_handler;
+ irq->parent_handler_data = sprd_eic;
+ irq->num_parents = 1;
+ irq->parents = &sprd_eic->irq;
+
+ ret = devm_gpiochip_add_data(&pdev->dev, &sprd_eic->chip, sprd_eic);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Could not register gpiochip %d.\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, sprd_eic);
+ return 0;
+}
+
+static const struct of_device_id sprd_eic_of_match[] = {
+ {
+ .compatible = "sprd,sc9860-eic-debounce",
+ .data = &sc9860_eic_dbnc_data,
+ },
+ {
+ .compatible = "sprd,sc9860-eic-latch",
+ .data = &sc9860_eic_latch_data,
+ },
+ {
+ .compatible = "sprd,sc9860-eic-async",
+ .data = &sc9860_eic_async_data,
+ },
+ {
+ .compatible = "sprd,sc9860-eic-sync",
+ .data = &sc9860_eic_sync_data,
+ },
+ {
+ /* end of list */
+ }
+};
+MODULE_DEVICE_TABLE(of, sprd_eic_of_match);
+
+static struct platform_driver sprd_eic_driver = {
+ .probe = sprd_eic_probe,
+ .driver = {
+ .name = "sprd-eic",
+ .of_match_table = sprd_eic_of_match,
+ },
+};
+
+module_platform_driver(sprd_eic_driver);
+
+MODULE_DESCRIPTION("Spreadtrum EIC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-em.c b/drivers/gpio/gpio-em.c
index b86e09e1b13b..2b466b80e70a 100644
--- a/drivers/gpio/gpio-em.c
+++ b/drivers/gpio/gpio-em.c
@@ -27,7 +27,7 @@
#include <linux/irqdomain.h>
#include <linux/bitops.h>
#include <linux/err.h>
-#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/pinctrl/consumer.h>
diff --git a/drivers/gpio/gpio-etraxfs.c b/drivers/gpio/gpio-etraxfs.c
deleted file mode 100644
index 94db1bf4bfdb..000000000000
--- a/drivers/gpio/gpio-etraxfs.c
+++ /dev/null
@@ -1,475 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/gpio/driver.h>
-#include <linux/of_gpio.h>
-#include <linux/io.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-
-#define ETRAX_FS_rw_pa_dout 0
-#define ETRAX_FS_r_pa_din 4
-#define ETRAX_FS_rw_pa_oe 8
-#define ETRAX_FS_rw_intr_cfg 12
-#define ETRAX_FS_rw_intr_mask 16
-#define ETRAX_FS_rw_ack_intr 20
-#define ETRAX_FS_r_intr 24
-#define ETRAX_FS_r_masked_intr 28
-#define ETRAX_FS_rw_pb_dout 32
-#define ETRAX_FS_r_pb_din 36
-#define ETRAX_FS_rw_pb_oe 40
-#define ETRAX_FS_rw_pc_dout 48
-#define ETRAX_FS_r_pc_din 52
-#define ETRAX_FS_rw_pc_oe 56
-#define ETRAX_FS_rw_pd_dout 64
-#define ETRAX_FS_r_pd_din 68
-#define ETRAX_FS_rw_pd_oe 72
-#define ETRAX_FS_rw_pe_dout 80
-#define ETRAX_FS_r_pe_din 84
-#define ETRAX_FS_rw_pe_oe 88
-
-#define ARTPEC3_r_pa_din 0
-#define ARTPEC3_rw_pa_dout 4
-#define ARTPEC3_rw_pa_oe 8
-#define ARTPEC3_r_pb_din 44
-#define ARTPEC3_rw_pb_dout 48
-#define ARTPEC3_rw_pb_oe 52
-#define ARTPEC3_r_pc_din 88
-#define ARTPEC3_rw_pc_dout 92
-#define ARTPEC3_rw_pc_oe 96
-#define ARTPEC3_r_pd_din 116
-#define ARTPEC3_rw_intr_cfg 120
-#define ARTPEC3_rw_intr_pins 124
-#define ARTPEC3_rw_intr_mask 128
-#define ARTPEC3_rw_ack_intr 132
-#define ARTPEC3_r_masked_intr 140
-
-#define GIO_CFG_OFF 0
-#define GIO_CFG_HI 1
-#define GIO_CFG_LO 2
-#define GIO_CFG_SET 3
-#define GIO_CFG_POSEDGE 5
-#define GIO_CFG_NEGEDGE 6
-#define GIO_CFG_ANYEDGE 7
-
-struct etraxfs_gpio_info;
-
-struct etraxfs_gpio_block {
- raw_spinlock_t lock;
- u32 mask;
- u32 cfg;
- u32 pins;
- unsigned int group[8];
-
- void __iomem *regs;
- const struct etraxfs_gpio_info *info;
-};
-
-struct etraxfs_gpio_chip {
- struct gpio_chip gc;
- struct etraxfs_gpio_block *block;
-};
-
-struct etraxfs_gpio_port {
- const char *label;
- unsigned int oe;
- unsigned int dout;
- unsigned int din;
- unsigned int ngpio;
-};
-
-struct etraxfs_gpio_info {
- unsigned int num_ports;
- const struct etraxfs_gpio_port *ports;
-
- unsigned int rw_ack_intr;
- unsigned int rw_intr_mask;
- unsigned int rw_intr_cfg;
- unsigned int rw_intr_pins;
- unsigned int r_masked_intr;
-};
-
-static const struct etraxfs_gpio_port etraxfs_gpio_etraxfs_ports[] = {
- {
- .label = "A",
- .ngpio = 8,
- .oe = ETRAX_FS_rw_pa_oe,
- .dout = ETRAX_FS_rw_pa_dout,
- .din = ETRAX_FS_r_pa_din,
- },
- {
- .label = "B",
- .ngpio = 18,
- .oe = ETRAX_FS_rw_pb_oe,
- .dout = ETRAX_FS_rw_pb_dout,
- .din = ETRAX_FS_r_pb_din,
- },
- {
- .label = "C",
- .ngpio = 18,
- .oe = ETRAX_FS_rw_pc_oe,
- .dout = ETRAX_FS_rw_pc_dout,
- .din = ETRAX_FS_r_pc_din,
- },
- {
- .label = "D",
- .ngpio = 18,
- .oe = ETRAX_FS_rw_pd_oe,
- .dout = ETRAX_FS_rw_pd_dout,
- .din = ETRAX_FS_r_pd_din,
- },
- {
- .label = "E",
- .ngpio = 18,
- .oe = ETRAX_FS_rw_pe_oe,
- .dout = ETRAX_FS_rw_pe_dout,
- .din = ETRAX_FS_r_pe_din,
- },
-};
-
-static const struct etraxfs_gpio_info etraxfs_gpio_etraxfs = {
- .num_ports = ARRAY_SIZE(etraxfs_gpio_etraxfs_ports),
- .ports = etraxfs_gpio_etraxfs_ports,
- .rw_ack_intr = ETRAX_FS_rw_ack_intr,
- .rw_intr_mask = ETRAX_FS_rw_intr_mask,
- .rw_intr_cfg = ETRAX_FS_rw_intr_cfg,
- .r_masked_intr = ETRAX_FS_r_masked_intr,
-};
-
-static const struct etraxfs_gpio_port etraxfs_gpio_artpec3_ports[] = {
- {
- .label = "A",
- .ngpio = 32,
- .oe = ARTPEC3_rw_pa_oe,
- .dout = ARTPEC3_rw_pa_dout,
- .din = ARTPEC3_r_pa_din,
- },
- {
- .label = "B",
- .ngpio = 32,
- .oe = ARTPEC3_rw_pb_oe,
- .dout = ARTPEC3_rw_pb_dout,
- .din = ARTPEC3_r_pb_din,
- },
- {
- .label = "C",
- .ngpio = 16,
- .oe = ARTPEC3_rw_pc_oe,
- .dout = ARTPEC3_rw_pc_dout,
- .din = ARTPEC3_r_pc_din,
- },
- {
- .label = "D",
- .ngpio = 32,
- .din = ARTPEC3_r_pd_din,
- },
-};
-
-static const struct etraxfs_gpio_info etraxfs_gpio_artpec3 = {
- .num_ports = ARRAY_SIZE(etraxfs_gpio_artpec3_ports),
- .ports = etraxfs_gpio_artpec3_ports,
- .rw_ack_intr = ARTPEC3_rw_ack_intr,
- .rw_intr_mask = ARTPEC3_rw_intr_mask,
- .rw_intr_cfg = ARTPEC3_rw_intr_cfg,
- .r_masked_intr = ARTPEC3_r_masked_intr,
- .rw_intr_pins = ARTPEC3_rw_intr_pins,
-};
-
-static unsigned int etraxfs_gpio_chip_to_port(struct gpio_chip *gc)
-{
- return gc->label[0] - 'A';
-}
-
-static int etraxfs_gpio_of_xlate(struct gpio_chip *gc,
- const struct of_phandle_args *gpiospec,
- u32 *flags)
-{
- /*
- * Port numbers are A to E, and the properties are integers, so we
- * specify them as 0xA - 0xE.
- */
- if (etraxfs_gpio_chip_to_port(gc) + 0xA != gpiospec->args[2])
- return -EINVAL;
-
- return of_gpio_simple_xlate(gc, gpiospec, flags);
-}
-
-static const struct of_device_id etraxfs_gpio_of_table[] = {
- {
- .compatible = "axis,etraxfs-gio",
- .data = &etraxfs_gpio_etraxfs,
- },
- {
- .compatible = "axis,artpec3-gio",
- .data = &etraxfs_gpio_artpec3,
- },
- {},
-};
-
-static unsigned int etraxfs_gpio_to_group_irq(unsigned int gpio)
-{
- return gpio % 8;
-}
-
-static unsigned int etraxfs_gpio_to_group_pin(struct etraxfs_gpio_chip *chip,
- unsigned int gpio)
-{
- return 4 * etraxfs_gpio_chip_to_port(&chip->gc) + gpio / 8;
-}
-
-static void etraxfs_gpio_irq_ack(struct irq_data *d)
-{
- struct etraxfs_gpio_chip *chip =
- gpiochip_get_data(irq_data_get_irq_chip_data(d));
- struct etraxfs_gpio_block *block = chip->block;
- unsigned int grpirq = etraxfs_gpio_to_group_irq(d->hwirq);
-
- writel(BIT(grpirq), block->regs + block->info->rw_ack_intr);
-}
-
-static void etraxfs_gpio_irq_mask(struct irq_data *d)
-{
- struct etraxfs_gpio_chip *chip =
- gpiochip_get_data(irq_data_get_irq_chip_data(d));
- struct etraxfs_gpio_block *block = chip->block;
- unsigned int grpirq = etraxfs_gpio_to_group_irq(d->hwirq);
-
- raw_spin_lock(&block->lock);
- block->mask &= ~BIT(grpirq);
- writel(block->mask, block->regs + block->info->rw_intr_mask);
- raw_spin_unlock(&block->lock);
-}
-
-static void etraxfs_gpio_irq_unmask(struct irq_data *d)
-{
- struct etraxfs_gpio_chip *chip =
- gpiochip_get_data(irq_data_get_irq_chip_data(d));
- struct etraxfs_gpio_block *block = chip->block;
- unsigned int grpirq = etraxfs_gpio_to_group_irq(d->hwirq);
-
- raw_spin_lock(&block->lock);
- block->mask |= BIT(grpirq);
- writel(block->mask, block->regs + block->info->rw_intr_mask);
- raw_spin_unlock(&block->lock);
-}
-
-static int etraxfs_gpio_irq_set_type(struct irq_data *d, u32 type)
-{
- struct etraxfs_gpio_chip *chip =
- gpiochip_get_data(irq_data_get_irq_chip_data(d));
- struct etraxfs_gpio_block *block = chip->block;
- unsigned int grpirq = etraxfs_gpio_to_group_irq(d->hwirq);
- u32 cfg;
-
- switch (type) {
- case IRQ_TYPE_EDGE_RISING:
- cfg = GIO_CFG_POSEDGE;
- break;
- case IRQ_TYPE_EDGE_FALLING:
- cfg = GIO_CFG_NEGEDGE;
- break;
- case IRQ_TYPE_EDGE_BOTH:
- cfg = GIO_CFG_ANYEDGE;
- break;
- case IRQ_TYPE_LEVEL_LOW:
- cfg = GIO_CFG_LO;
- break;
- case IRQ_TYPE_LEVEL_HIGH:
- cfg = GIO_CFG_HI;
- break;
- default:
- return -EINVAL;
- }
-
- raw_spin_lock(&block->lock);
- block->cfg &= ~(0x7 << (grpirq * 3));
- block->cfg |= (cfg << (grpirq * 3));
- writel(block->cfg, block->regs + block->info->rw_intr_cfg);
- raw_spin_unlock(&block->lock);
-
- return 0;
-}
-
-static int etraxfs_gpio_irq_request_resources(struct irq_data *d)
-{
- struct etraxfs_gpio_chip *chip =
- gpiochip_get_data(irq_data_get_irq_chip_data(d));
- struct etraxfs_gpio_block *block = chip->block;
- unsigned int grpirq = etraxfs_gpio_to_group_irq(d->hwirq);
- int ret = -EBUSY;
-
- raw_spin_lock(&block->lock);
- if (block->group[grpirq])
- goto out;
-
- ret = gpiochip_lock_as_irq(&chip->gc, d->hwirq);
- if (ret)
- goto out;
-
- block->group[grpirq] = d->irq;
- if (block->info->rw_intr_pins) {
- unsigned int pin = etraxfs_gpio_to_group_pin(chip, d->hwirq);
-
- block->pins &= ~(0xf << (grpirq * 4));
- block->pins |= (pin << (grpirq * 4));
-
- writel(block->pins, block->regs + block->info->rw_intr_pins);
- }
-
-out:
- raw_spin_unlock(&block->lock);
- return ret;
-}
-
-static void etraxfs_gpio_irq_release_resources(struct irq_data *d)
-{
- struct etraxfs_gpio_chip *chip =
- gpiochip_get_data(irq_data_get_irq_chip_data(d));
- struct etraxfs_gpio_block *block = chip->block;
- unsigned int grpirq = etraxfs_gpio_to_group_irq(d->hwirq);
-
- raw_spin_lock(&block->lock);
- block->group[grpirq] = 0;
- gpiochip_unlock_as_irq(&chip->gc, d->hwirq);
- raw_spin_unlock(&block->lock);
-}
-
-static struct irq_chip etraxfs_gpio_irq_chip = {
- .name = "gpio-etraxfs",
- .irq_ack = etraxfs_gpio_irq_ack,
- .irq_mask = etraxfs_gpio_irq_mask,
- .irq_unmask = etraxfs_gpio_irq_unmask,
- .irq_set_type = etraxfs_gpio_irq_set_type,
- .irq_request_resources = etraxfs_gpio_irq_request_resources,
- .irq_release_resources = etraxfs_gpio_irq_release_resources,
-};
-
-static irqreturn_t etraxfs_gpio_interrupt(int irq, void *dev_id)
-{
- struct etraxfs_gpio_block *block = dev_id;
- unsigned long intr = readl(block->regs + block->info->r_masked_intr);
- int bit;
-
- for_each_set_bit(bit, &intr, 8)
- generic_handle_irq(block->group[bit]);
-
- return IRQ_RETVAL(intr & 0xff);
-}
-
-static int etraxfs_gpio_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- const struct etraxfs_gpio_info *info;
- const struct of_device_id *match;
- struct etraxfs_gpio_block *block;
- struct etraxfs_gpio_chip *chips;
- struct resource *res, *irq;
- bool allportsirq = false;
- void __iomem *regs;
- int ret;
- int i;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- regs = devm_ioremap_resource(dev, res);
- if (IS_ERR(regs))
- return PTR_ERR(regs);
-
- match = of_match_node(etraxfs_gpio_of_table, dev->of_node);
- if (!match)
- return -EINVAL;
-
- info = match->data;
-
- chips = devm_kzalloc(dev, sizeof(*chips) * info->num_ports, GFP_KERNEL);
- if (!chips)
- return -ENOMEM;
-
- irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!irq)
- return -EINVAL;
-
- block = devm_kzalloc(dev, sizeof(*block), GFP_KERNEL);
- if (!block)
- return -ENOMEM;
-
- raw_spin_lock_init(&block->lock);
-
- block->regs = regs;
- block->info = info;
-
- writel(0, block->regs + info->rw_intr_mask);
- writel(0, block->regs + info->rw_intr_cfg);
- if (info->rw_intr_pins) {
- allportsirq = true;
- writel(0, block->regs + info->rw_intr_pins);
- }
-
- ret = devm_request_irq(dev, irq->start, etraxfs_gpio_interrupt,
- IRQF_SHARED, dev_name(dev), block);
- if (ret) {
- dev_err(dev, "Unable to request irq %d\n", ret);
- return ret;
- }
-
- for (i = 0; i < info->num_ports; i++) {
- struct etraxfs_gpio_chip *chip = &chips[i];
- struct gpio_chip *gc = &chip->gc;
- const struct etraxfs_gpio_port *port = &info->ports[i];
- unsigned long flags = BGPIOF_READ_OUTPUT_REG_SET;
- void __iomem *dat = regs + port->din;
- void __iomem *set = regs + port->dout;
- void __iomem *dirout = regs + port->oe;
-
- chip->block = block;
-
- if (dirout == set) {
- dirout = set = NULL;
- flags = BGPIOF_NO_OUTPUT;
- }
-
- ret = bgpio_init(gc, dev, 4,
- dat, set, NULL, dirout, NULL,
- flags);
- if (ret) {
- dev_err(dev, "Unable to init port %s\n",
- port->label);
- continue;
- }
-
- gc->ngpio = port->ngpio;
- gc->label = port->label;
-
- gc->of_node = dev->of_node;
- gc->of_gpio_n_cells = 3;
- gc->of_xlate = etraxfs_gpio_of_xlate;
-
- ret = gpiochip_add_data(gc, chip);
- if (ret) {
- dev_err(dev, "Unable to register port %s\n",
- gc->label);
- continue;
- }
-
- if (i > 0 && !allportsirq)
- continue;
-
- ret = gpiochip_irqchip_add(gc, &etraxfs_gpio_irq_chip, 0,
- handle_level_irq, IRQ_TYPE_NONE);
- if (ret) {
- dev_err(dev, "Unable to add irqchip to port %s\n",
- gc->label);
- }
- }
-
- return 0;
-}
-
-static struct platform_driver etraxfs_gpio_driver = {
- .driver = {
- .name = "etraxfs-gpio",
- .of_match_table = of_match_ptr(etraxfs_gpio_of_table),
- },
- .probe = etraxfs_gpio_probe,
-};
-
-builtin_platform_driver(etraxfs_gpio_driver);
diff --git a/drivers/gpio/gpio-ftgpio010.c b/drivers/gpio/gpio-ftgpio010.c
index b7a3a2db699b..868bf8501560 100644
--- a/drivers/gpio/gpio-ftgpio010.c
+++ b/drivers/gpio/gpio-ftgpio010.c
@@ -14,19 +14,20 @@
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
-#include <linux/of_gpio.h>
#include <linux/bitops.h>
/* GPIO registers definition */
#define GPIO_DATA_OUT 0x00
#define GPIO_DATA_IN 0x04
#define GPIO_DIR 0x08
+#define GPIO_BYPASS_IN 0x0C
#define GPIO_DATA_SET 0x10
#define GPIO_DATA_CLR 0x14
#define GPIO_PULL_EN 0x18
#define GPIO_PULL_TYPE 0x1C
#define GPIO_INT_EN 0x20
-#define GPIO_INT_STAT 0x24
+#define GPIO_INT_STAT_RAW 0x24
+#define GPIO_INT_STAT_MASKED 0x28
#define GPIO_INT_MASK 0x2C
#define GPIO_INT_CLR 0x30
#define GPIO_INT_TYPE 0x34
@@ -147,7 +148,7 @@ static void ftgpio_gpio_irq_handler(struct irq_desc *desc)
chained_irq_enter(irqchip, desc);
- stat = readl(g->base + GPIO_INT_STAT);
+ stat = readl(g->base + GPIO_INT_STAT_RAW);
if (stat)
for_each_set_bit(offset, &stat, gc->ngpio)
generic_handle_irq(irq_find_mapping(gc->irq.domain,
diff --git a/drivers/gpio/gpio-ge.c b/drivers/gpio/gpio-ge.c
index 6f5a7fe9787d..1fe2d3418f2f 100644
--- a/drivers/gpio/gpio-ge.c
+++ b/drivers/gpio/gpio-ge.c
@@ -21,7 +21,6 @@
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/of_device.h>
-#include <linux/of_gpio.h>
#include <linux/of_address.h>
#include <linux/module.h>
#include <linux/gpio/driver.h>
diff --git a/drivers/gpio/gpio-gpio-mm.c b/drivers/gpio/gpio-gpio-mm.c
index 11ade5b288f8..d496cc56c2a2 100644
--- a/drivers/gpio/gpio-gpio-mm.c
+++ b/drivers/gpio/gpio-gpio-mm.c
@@ -14,6 +14,7 @@
* This driver supports the following Diamond Systems devices: GPIO-MM and
* GPIO-MM-12.
*/
+#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/errno.h>
@@ -171,6 +172,51 @@ static int gpiomm_gpio_get(struct gpio_chip *chip, unsigned int offset)
return !!(port_state & mask);
}
+static int gpiomm_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
+ unsigned long *bits)
+{
+ struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
+ size_t i;
+ const size_t ports[] = { 0, 1, 2, 4, 5, 6 };
+ const unsigned int gpio_reg_size = 8;
+ unsigned int bits_offset;
+ size_t word_index;
+ unsigned int word_offset;
+ unsigned long word_mask;
+ const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0);
+ unsigned long port_state;
+
+ /* clear bits array to a clean slate */
+ bitmap_zero(bits, chip->ngpio);
+
+ /* get bits are evaluated a gpio port register at a time */
+ for (i = 0; i < ARRAY_SIZE(ports); i++) {
+ /* gpio offset in bits array */
+ bits_offset = i * gpio_reg_size;
+
+ /* word index for bits array */
+ word_index = BIT_WORD(bits_offset);
+
+ /* gpio offset within current word of bits array */
+ word_offset = bits_offset % BITS_PER_LONG;
+
+ /* mask of get bits for current gpio within current word */
+ word_mask = mask[word_index] & (port_mask << word_offset);
+ if (!word_mask) {
+ /* no get bits in this port so skip to next one */
+ continue;
+ }
+
+ /* read bits from current gpio port */
+ port_state = inb(gpiommgpio->base + ports[i]);
+
+ /* store acquired bits at respective bits array offset */
+ bits[word_index] |= port_state << word_offset;
+ }
+
+ return 0;
+}
+
static void gpiomm_gpio_set(struct gpio_chip *chip, unsigned int offset,
int value)
{
@@ -268,6 +314,7 @@ static int gpiomm_probe(struct device *dev, unsigned int id)
gpiommgpio->chip.direction_input = gpiomm_gpio_direction_input;
gpiommgpio->chip.direction_output = gpiomm_gpio_direction_output;
gpiommgpio->chip.get = gpiomm_gpio_get;
+ gpiommgpio->chip.get_multiple = gpiomm_gpio_get_multiple;
gpiommgpio->chip.set = gpiomm_gpio_set;
gpiommgpio->chip.set_multiple = gpiomm_gpio_set_multiple;
gpiommgpio->base = base[id];
diff --git a/drivers/gpio/gpio-grgpio.c b/drivers/gpio/gpio-grgpio.c
index e2fc561f4315..60a1556c570a 100644
--- a/drivers/gpio/gpio-grgpio.c
+++ b/drivers/gpio/gpio-grgpio.c
@@ -26,9 +26,8 @@
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
#include <linux/of_platform.h>
-#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/gpio/driver.h>
diff --git a/drivers/gpio/gpio-hlwd.c b/drivers/gpio/gpio-hlwd.c
new file mode 100644
index 000000000000..a63136a68ba3
--- /dev/null
+++ b/drivers/gpio/gpio-hlwd.c
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright (C) 2008-2009 The GameCube Linux Team
+// Copyright (C) 2008,2009 Albert Herranz
+// Copyright (C) 2017-2018 Jonathan Neuschäfer
+//
+// Nintendo Wii (Hollywood) GPIO driver
+
+#include <linux/gpio/driver.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+
+/*
+ * Register names and offsets courtesy of WiiBrew:
+ * https://wiibrew.org/wiki/Hardware/Hollywood_GPIOs
+ *
+ * Note that for most registers, there are two versions:
+ * - HW_GPIOB_* Is always accessible by the Broadway PowerPC core, but does
+ * always give access to all GPIO lines
+ * - HW_GPIO_* Is only accessible by the Broadway PowerPC code if the memory
+ * firewall (AHBPROT) in the Hollywood chipset has been configured to allow
+ * such access.
+ *
+ * The ownership of each GPIO line can be configured in the HW_GPIO_OWNER
+ * register: A one bit configures the line for access via the HW_GPIOB_*
+ * registers, a zero bit indicates access via HW_GPIO_*. This driver uses
+ * HW_GPIOB_*.
+ */
+#define HW_GPIOB_OUT 0x00
+#define HW_GPIOB_DIR 0x04
+#define HW_GPIOB_IN 0x08
+#define HW_GPIOB_INTLVL 0x0c
+#define HW_GPIOB_INTFLAG 0x10
+#define HW_GPIOB_INTMASK 0x14
+#define HW_GPIOB_INMIR 0x18
+#define HW_GPIO_ENABLE 0x1c
+#define HW_GPIO_OUT 0x20
+#define HW_GPIO_DIR 0x24
+#define HW_GPIO_IN 0x28
+#define HW_GPIO_INTLVL 0x2c
+#define HW_GPIO_INTFLAG 0x30
+#define HW_GPIO_INTMASK 0x34
+#define HW_GPIO_INMIR 0x38
+#define HW_GPIO_OWNER 0x3c
+
+struct hlwd_gpio {
+ struct gpio_chip gpioc;
+ void __iomem *regs;
+};
+
+static int hlwd_gpio_probe(struct platform_device *pdev)
+{
+ struct hlwd_gpio *hlwd;
+ struct resource *regs_resource;
+ u32 ngpios;
+ int res;
+
+ hlwd = devm_kzalloc(&pdev->dev, sizeof(*hlwd), GFP_KERNEL);
+ if (!hlwd)
+ return -ENOMEM;
+
+ regs_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hlwd->regs = devm_ioremap_resource(&pdev->dev, regs_resource);
+ if (IS_ERR(hlwd->regs))
+ return PTR_ERR(hlwd->regs);
+
+ /*
+ * Claim all GPIOs using the OWNER register. This will not work on
+ * systems where the AHBPROT memory firewall hasn't been configured to
+ * permit PPC access to HW_GPIO_*.
+ *
+ * Note that this has to happen before bgpio_init reads the
+ * HW_GPIOB_OUT and HW_GPIOB_DIR, because otherwise it reads the wrong
+ * values.
+ */
+ iowrite32be(0xffffffff, hlwd->regs + HW_GPIO_OWNER);
+
+ res = bgpio_init(&hlwd->gpioc, &pdev->dev, 4,
+ hlwd->regs + HW_GPIOB_IN, hlwd->regs + HW_GPIOB_OUT,
+ NULL, hlwd->regs + HW_GPIOB_DIR, NULL,
+ BGPIOF_BIG_ENDIAN_BYTE_ORDER);
+ if (res < 0) {
+ dev_warn(&pdev->dev, "bgpio_init failed: %d\n", res);
+ return res;
+ }
+
+ res = of_property_read_u32(pdev->dev.of_node, "ngpios", &ngpios);
+ if (res)
+ ngpios = 32;
+ hlwd->gpioc.ngpio = ngpios;
+
+ return devm_gpiochip_add_data(&pdev->dev, &hlwd->gpioc, hlwd);
+}
+
+static const struct of_device_id hlwd_gpio_match[] = {
+ { .compatible = "nintendo,hollywood-gpio", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, hlwd_gpio_match);
+
+static struct platform_driver hlwd_gpio_driver = {
+ .driver = {
+ .name = "gpio-hlwd",
+ .of_match_table = hlwd_gpio_match,
+ },
+ .probe = hlwd_gpio_probe,
+};
+module_platform_driver(hlwd_gpio_driver);
+
+MODULE_AUTHOR("Jonathan Neuschäfer <j.neuschaefer@gmx.net>");
+MODULE_DESCRIPTION("Nintendo Wii GPIO driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-htc-egpio.c b/drivers/gpio/gpio-htc-egpio.c
index 271356effb2e..516383934945 100644
--- a/drivers/gpio/gpio-htc-egpio.c
+++ b/drivers/gpio/gpio-htc-egpio.c
@@ -18,6 +18,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/init.h>
+#include <linux/gpio/driver.h>
struct egpio_chip {
int reg_start;
diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c
index 4f6d643516b7..dba392221042 100644
--- a/drivers/gpio/gpio-ich.c
+++ b/drivers/gpio/gpio-ich.c
@@ -23,9 +23,10 @@
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/pci.h>
-#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
#include <linux/platform_device.h>
#include <linux/mfd/lpc_ich.h>
+#include <linux/bitops.h>
#define DRV_NAME "gpio_ich"
@@ -131,9 +132,9 @@ static int ichx_write_bit(int reg, unsigned nr, int val, int verify)
ichx_priv.gpio_base);
if (val)
- data |= 1 << bit;
+ data |= BIT(bit);
else
- data &= ~(1 << bit);
+ data &= ~BIT(bit);
ICHX_WRITE(data, ichx_priv.desc->regs[reg][reg_nr],
ichx_priv.gpio_base);
if (reg == GPIO_LVL && ichx_priv.desc->use_outlvl_cache)
@@ -166,17 +167,17 @@ static int ichx_read_bit(int reg, unsigned nr)
spin_unlock_irqrestore(&ichx_priv.lock, flags);
- return data & (1 << bit) ? 1 : 0;
+ return !!(data & BIT(bit));
}
static bool ichx_gpio_check_available(struct gpio_chip *gpio, unsigned nr)
{
- return !!(ichx_priv.use_gpio & (1 << (nr / 32)));
+ return !!(ichx_priv.use_gpio & BIT(nr / 32));
}
static int ichx_gpio_get_direction(struct gpio_chip *gpio, unsigned nr)
{
- return ichx_read_bit(GPIO_IO_SEL, nr) ? GPIOF_DIR_IN : GPIOF_DIR_OUT;
+ return ichx_read_bit(GPIO_IO_SEL, nr);
}
static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
@@ -232,12 +233,12 @@ static int ich6_gpio_get(struct gpio_chip *chip, unsigned nr)
spin_lock_irqsave(&ichx_priv.lock, flags);
/* GPI 0 - 15 are latched, write 1 to clear*/
- ICHX_WRITE(1 << (16 + nr), 0, ichx_priv.pm_base);
+ ICHX_WRITE(BIT(16 + nr), 0, ichx_priv.pm_base);
data = ICHX_READ(0, ichx_priv.pm_base);
spin_unlock_irqrestore(&ichx_priv.lock, flags);
- return (data >> 16) & (1 << nr) ? 1 : 0;
+ return !!((data >> 16) & BIT(nr));
} else {
return ichx_gpio_get(chip, nr);
}
@@ -254,7 +255,7 @@ static int ichx_gpio_request(struct gpio_chip *chip, unsigned nr)
* the chipset's USE value can be trusted for this specific bit.
* If it can't be trusted, assume that the pin can be used as a GPIO.
*/
- if (ichx_priv.desc->use_sel_ignore[nr / 32] & (1 << (nr & 0x1f)))
+ if (ichx_priv.desc->use_sel_ignore[nr / 32] & BIT(nr & 0x1f))
return 0;
return ichx_read_bit(GPIO_USE_SEL, nr) ? 0 : -ENODEV;
@@ -394,7 +395,7 @@ static int ichx_gpio_request_regions(struct device *dev,
return -ENODEV;
for (i = 0; i < ARRAY_SIZE(ichx_priv.desc->regs[0]); i++) {
- if (!(use_gpio & (1 << i)))
+ if (!(use_gpio & BIT(i)))
continue;
if (!devm_request_region(dev,
res_base->start + ichx_priv.desc->regs[0][i],
diff --git a/drivers/gpio/gpio-intel-mid.c b/drivers/gpio/gpio-intel-mid.c
index 629575ea46a0..028d64c2cb1e 100644
--- a/drivers/gpio/gpio-intel-mid.c
+++ b/drivers/gpio/gpio-intel-mid.c
@@ -361,10 +361,8 @@ static int intel_gpio_probe(struct pci_dev *pdev,
pcim_iounmap_regions(pdev, 1 << 1);
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv) {
- dev_err(&pdev->dev, "can't allocate chip data\n");
+ if (!priv)
return -ENOMEM;
- }
priv->reg_base = pcim_iomap_table(pdev)[0];
priv->chip.label = dev_name(&pdev->dev);
diff --git a/drivers/gpio/gpio-it87.c b/drivers/gpio/gpio-it87.c
index efb46edff81f..7cad14d3f127 100644
--- a/drivers/gpio/gpio-it87.c
+++ b/drivers/gpio/gpio-it87.c
@@ -31,7 +31,7 @@
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/slab.h>
-#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
/* Chip Id numbers */
#define NO_DEV_ID 0xffff
diff --git a/drivers/gpio/gpio-janz-ttl.c b/drivers/gpio/gpio-janz-ttl.c
index a8d0a6b8025a..6b9bf8b7bf16 100644
--- a/drivers/gpio/gpio-janz-ttl.c
+++ b/drivers/gpio/gpio-janz-ttl.c
@@ -16,8 +16,9 @@
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/io.h>
-#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
#include <linux/slab.h>
+#include <linux/bitops.h>
#include <linux/mfd/janz.h>
@@ -33,9 +34,9 @@
#define MASTER_INT_CTL 0x00
#define MASTER_CONF_CTL 0x01
-#define CONF_PAE (1 << 2)
-#define CONF_PBE (1 << 7)
-#define CONF_PCE (1 << 4)
+#define CONF_PAE BIT(2)
+#define CONF_PBE BIT(7)
+#define CONF_PCE BIT(4)
struct ttl_control_regs {
__be16 portc;
@@ -74,7 +75,7 @@ static int ttl_get_value(struct gpio_chip *gpio, unsigned offset)
}
spin_lock(&mod->lock);
- ret = *shadow & (1 << offset);
+ ret = *shadow & BIT(offset);
spin_unlock(&mod->lock);
return !!ret;
}
@@ -100,9 +101,9 @@ static void ttl_set_value(struct gpio_chip *gpio, unsigned offset, int value)
spin_lock(&mod->lock);
if (value)
- *shadow |= (1 << offset);
+ *shadow |= BIT(offset);
else
- *shadow &= ~(1 << offset);
+ *shadow &= ~BIT(offset);
iowrite16be(*shadow, port);
spin_unlock(&mod->lock);
diff --git a/drivers/gpio/gpio-kempld.c b/drivers/gpio/gpio-kempld.c
index 701f1510328c..7bb96f48ef76 100644
--- a/drivers/gpio/gpio-kempld.c
+++ b/drivers/gpio/gpio-kempld.c
@@ -20,7 +20,7 @@
#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/platform_device.h>
-#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
#include <linux/mfd/kempld.h>
#define KEMPLD_GPIO_MAX_NUM 16
diff --git a/drivers/gpio/gpio-ks8695.c b/drivers/gpio/gpio-ks8695.c
index 179723d02f55..55d562e1278e 100644
--- a/drivers/gpio/gpio-ks8695.c
+++ b/drivers/gpio/gpio-ks8695.c
@@ -18,7 +18,7 @@
* 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/gpio.h>
+#include <linux/gpio/driver.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/init.h>
diff --git a/drivers/gpio/gpio-max3191x.c b/drivers/gpio/gpio-max3191x.c
index f74b1072e84b..b5b9cb1fda50 100644
--- a/drivers/gpio/gpio-max3191x.c
+++ b/drivers/gpio/gpio-max3191x.c
@@ -315,12 +315,17 @@ static void gpiod_set_array_single_value_cansleep(unsigned int ndescs,
struct gpio_desc **desc,
int value)
{
- int i, values[ndescs];
+ int i, *values;
+
+ values = kmalloc_array(ndescs, sizeof(*values), GFP_KERNEL);
+ if (!values)
+ return;
for (i = 0; i < ndescs; i++)
values[i] = value;
gpiod_set_array_value_cansleep(ndescs, desc, values);
+ kfree(values);
}
static struct gpio_descs *devm_gpiod_get_array_optional_count(
diff --git a/drivers/gpio/gpio-merrifield.c b/drivers/gpio/gpio-merrifield.c
index c38624ea0251..97421bd4a60f 100644
--- a/drivers/gpio/gpio-merrifield.c
+++ b/drivers/gpio/gpio-merrifield.c
@@ -416,10 +416,8 @@ static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id
pcim_iounmap_regions(pdev, BIT(1));
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv) {
- dev_err(&pdev->dev, "can't allocate chip data\n");
+ if (!priv)
return -ENOMEM;
- }
priv->dev = &pdev->dev;
priv->reg_base = pcim_iomap_table(pdev)[0];
diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c
index 4b80e996d976..b3678bd1c120 100644
--- a/drivers/gpio/gpio-ml-ioh.c
+++ b/drivers/gpio/gpio-ml-ioh.c
@@ -445,7 +445,6 @@ static int ioh_gpio_probe(struct pci_dev *pdev,
chip_save = kzalloc(sizeof(*chip) * 8, GFP_KERNEL);
if (chip_save == NULL) {
- dev_err(&pdev->dev, "%s : kzalloc failed", __func__);
ret = -ENOMEM;
goto err_kzalloc;
}
diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c
index 3a545ad17817..76c2fe91a901 100644
--- a/drivers/gpio/gpio-mockup.c
+++ b/drivers/gpio/gpio-mockup.c
@@ -2,7 +2,7 @@
* GPIO Testing Device Driver
*
* Copyright (C) 2014 Kamlakant Patel <kamlakant.patel@broadcom.com>
- * Copyright (C) 2015-2016 Bamvor Jian Zhang <bamvor.zhangjian@linaro.org>
+ * Copyright (C) 2015-2016 Bamvor Jian Zhang <bamv2005@gmail.com>
* Copyright (C) 2017 Bartosz Golaszewski <brgl@bgdev.pl>
*
* This program is free software; you can redistribute it and/or modify it
@@ -314,7 +314,7 @@ static int gpio_mockup_probe(struct platform_device *pdev)
if (rv)
return rv;
- if (gpio_mockup_dbg_dir)
+ if (!IS_ERR_OR_NULL(gpio_mockup_dbg_dir))
gpio_mockup_debugfs_setup(dev, chip);
return 0;
@@ -411,7 +411,7 @@ module_init(gpio_mockup_init);
module_exit(gpio_mockup_exit);
MODULE_AUTHOR("Kamlakant Patel <kamlakant.patel@broadcom.com>");
-MODULE_AUTHOR("Bamvor Jian Zhang <bamvor.zhangjian@linaro.org>");
+MODULE_AUTHOR("Bamvor Jian Zhang <bamv2005@gmail.com>");
MODULE_AUTHOR("Bartosz Golaszewski <brgl@bgdev.pl>");
MODULE_DESCRIPTION("GPIO Testing driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index ab5035b96886..35971a341c40 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -1157,11 +1157,9 @@ static int omap_gpio_probe(struct platform_device *pdev)
if (!pdata)
return -EINVAL;
- bank = devm_kzalloc(dev, sizeof(struct gpio_bank), GFP_KERNEL);
- if (!bank) {
- dev_err(dev, "Memory alloc failed\n");
+ bank = devm_kzalloc(dev, sizeof(*bank), GFP_KERNEL);
+ if (!bank)
return -ENOMEM;
- }
irqc = devm_kzalloc(dev, sizeof(*irqc), GFP_KERNEL);
if (!irqc)
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index a0a5f9730aa7..d2ead4b1cf61 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -70,6 +70,7 @@ static const struct i2c_device_id pca953x_id[] = {
{ "pca9575", 16 | PCA957X_TYPE | PCA_INT, },
{ "pca9698", 40 | PCA953X_TYPE, },
+ { "pcal6524", 24 | PCA953X_TYPE | PCA_INT | PCA_PCAL, },
{ "pcal9555a", 16 | PCA953X_TYPE | PCA_INT | PCA_PCAL, },
{ "max7310", 8 | PCA953X_TYPE, },
@@ -935,6 +936,9 @@ static const struct of_device_id pca953x_dt_ids[] = {
{ .compatible = "nxp,pca9575", .data = OF_957X(16, PCA_INT), },
{ .compatible = "nxp,pca9698", .data = OF_953X(40, 0), },
+ { .compatible = "nxp,pcal6524", .data = OF_953X(24, PCA_INT), },
+ { .compatible = "nxp,pcal9555a", .data = OF_953X(16, PCA_INT), },
+
{ .compatible = "maxim,max7310", .data = OF_953X( 8, 0), },
{ .compatible = "maxim,max7312", .data = OF_953X(16, PCA_INT), },
{ .compatible = "maxim,max7313", .data = OF_953X(16, PCA_INT), },
diff --git a/drivers/gpio/gpio-pci-idio-16.c b/drivers/gpio/gpio-pci-idio-16.c
index 57d1b7fbf07b..1948724d8c36 100644
--- a/drivers/gpio/gpio-pci-idio-16.c
+++ b/drivers/gpio/gpio-pci-idio-16.c
@@ -11,6 +11,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
+#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/errno.h>
@@ -103,6 +104,54 @@ static int idio_16_gpio_get(struct gpio_chip *chip, unsigned int offset)
return !!(ioread8(&idio16gpio->reg->in8_15) & (mask >> 24));
}
+static int idio_16_gpio_get_multiple(struct gpio_chip *chip,
+ unsigned long *mask, unsigned long *bits)
+{
+ struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
+ size_t i;
+ const unsigned int gpio_reg_size = 8;
+ unsigned int bits_offset;
+ size_t word_index;
+ unsigned int word_offset;
+ unsigned long word_mask;
+ const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0);
+ unsigned long port_state;
+ u8 __iomem ports[] = {
+ idio16gpio->reg->out0_7, idio16gpio->reg->out8_15,
+ idio16gpio->reg->in0_7, idio16gpio->reg->in8_15,
+ };
+
+ /* clear bits array to a clean slate */
+ bitmap_zero(bits, chip->ngpio);
+
+ /* get bits are evaluated a gpio port register at a time */
+ for (i = 0; i < ARRAY_SIZE(ports); i++) {
+ /* gpio offset in bits array */
+ bits_offset = i * gpio_reg_size;
+
+ /* word index for bits array */
+ word_index = BIT_WORD(bits_offset);
+
+ /* gpio offset within current word of bits array */
+ word_offset = bits_offset % BITS_PER_LONG;
+
+ /* mask of get bits for current gpio within current word */
+ word_mask = mask[word_index] & (port_mask << word_offset);
+ if (!word_mask) {
+ /* no get bits in this port so skip to next one */
+ continue;
+ }
+
+ /* read bits from current gpio port */
+ port_state = ioread8(ports + i);
+
+ /* store acquired bits at respective bits array offset */
+ bits[word_index] |= port_state << word_offset;
+ }
+
+ return 0;
+}
+
static void idio_16_gpio_set(struct gpio_chip *chip, unsigned int offset,
int value)
{
@@ -299,6 +348,7 @@ static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id)
idio16gpio->chip.direction_input = idio_16_gpio_direction_input;
idio16gpio->chip.direction_output = idio_16_gpio_direction_output;
idio16gpio->chip.get = idio_16_gpio_get;
+ idio16gpio->chip.get_multiple = idio_16_gpio_get_multiple;
idio16gpio->chip.set = idio_16_gpio_set;
idio16gpio->chip.set_multiple = idio_16_gpio_set_multiple;
diff --git a/drivers/gpio/gpio-pcie-idio-24.c b/drivers/gpio/gpio-pcie-idio-24.c
index f666e2e69074..835607ecf658 100644
--- a/drivers/gpio/gpio-pcie-idio-24.c
+++ b/drivers/gpio/gpio-pcie-idio-24.c
@@ -15,6 +15,7 @@
* This driver supports the following ACCES devices: PCIe-IDIO-24,
* PCIe-IDI-24, PCIe-IDO-24, and PCIe-IDIO-12.
*/
+#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/errno.h>
@@ -193,6 +194,61 @@ static int idio_24_gpio_get(struct gpio_chip *chip, unsigned int offset)
return !!(ioread8(&idio24gpio->reg->ttl_in0_7) & offset_mask);
}
+static int idio_24_gpio_get_multiple(struct gpio_chip *chip,
+ unsigned long *mask, unsigned long *bits)
+{
+ struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
+ size_t i;
+ const unsigned int gpio_reg_size = 8;
+ unsigned int bits_offset;
+ size_t word_index;
+ unsigned int word_offset;
+ unsigned long word_mask;
+ const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0);
+ unsigned long port_state;
+ u8 __iomem ports[] = {
+ idio24gpio->reg->out0_7, idio24gpio->reg->out8_15,
+ idio24gpio->reg->out16_23, idio24gpio->reg->in0_7,
+ idio24gpio->reg->in8_15, idio24gpio->reg->in16_23,
+ };
+ const unsigned long out_mode_mask = BIT(1);
+
+ /* clear bits array to a clean slate */
+ bitmap_zero(bits, chip->ngpio);
+
+ /* get bits are evaluated a gpio port register at a time */
+ for (i = 0; i < ARRAY_SIZE(ports); i++) {
+ /* gpio offset in bits array */
+ bits_offset = i * gpio_reg_size;
+
+ /* word index for bits array */
+ word_index = BIT_WORD(bits_offset);
+
+ /* gpio offset within current word of bits array */
+ word_offset = bits_offset % BITS_PER_LONG;
+
+ /* mask of get bits for current gpio within current word */
+ word_mask = mask[word_index] & (port_mask << word_offset);
+ if (!word_mask) {
+ /* no get bits in this port so skip to next one */
+ continue;
+ }
+
+ /* read bits from current gpio port (port 6 is TTL GPIO) */
+ if (i < 6)
+ port_state = ioread8(ports + i);
+ else if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask)
+ port_state = ioread8(&idio24gpio->reg->ttl_out0_7);
+ else
+ port_state = ioread8(&idio24gpio->reg->ttl_in0_7);
+
+ /* store acquired bits at respective bits array offset */
+ bits[word_index] |= port_state << word_offset;
+ }
+
+ return 0;
+}
+
static void idio_24_gpio_set(struct gpio_chip *chip, unsigned int offset,
int value)
{
@@ -234,6 +290,65 @@ static void idio_24_gpio_set(struct gpio_chip *chip, unsigned int offset,
raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
}
+static void idio_24_gpio_set_multiple(struct gpio_chip *chip,
+ unsigned long *mask, unsigned long *bits)
+{
+ struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
+ size_t i;
+ unsigned long bits_offset;
+ unsigned long gpio_mask;
+ const unsigned int gpio_reg_size = 8;
+ const unsigned long port_mask = GENMASK(gpio_reg_size, 0);
+ unsigned long flags;
+ unsigned int out_state;
+ u8 __iomem ports[] = {
+ idio24gpio->reg->out0_7, idio24gpio->reg->out8_15,
+ idio24gpio->reg->out16_23
+ };
+ const unsigned long out_mode_mask = BIT(1);
+ const unsigned int ttl_offset = 48;
+ const size_t ttl_i = BIT_WORD(ttl_offset);
+ const unsigned int word_offset = ttl_offset % BITS_PER_LONG;
+ const unsigned long ttl_mask = (mask[ttl_i] >> word_offset) & port_mask;
+ const unsigned long ttl_bits = (bits[ttl_i] >> word_offset) & ttl_mask;
+
+ /* set bits are processed a gpio port register at a time */
+ for (i = 0; i < ARRAY_SIZE(ports); i++) {
+ /* gpio offset in bits array */
+ bits_offset = i * gpio_reg_size;
+
+ /* check if any set bits for current port */
+ gpio_mask = (*mask >> bits_offset) & port_mask;
+ if (!gpio_mask) {
+ /* no set bits for this port so move on to next port */
+ continue;
+ }
+
+ raw_spin_lock_irqsave(&idio24gpio->lock, flags);
+
+ /* process output lines */
+ out_state = ioread8(ports + i) & ~gpio_mask;
+ out_state |= (*bits >> bits_offset) & gpio_mask;
+ iowrite8(out_state, ports + i);
+
+ raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
+ }
+
+ /* check if setting TTL lines and if they are in output mode */
+ if (!ttl_mask || !(ioread8(&idio24gpio->reg->ctl) & out_mode_mask))
+ return;
+
+ /* handle TTL output */
+ raw_spin_lock_irqsave(&idio24gpio->lock, flags);
+
+ /* process output lines */
+ out_state = ioread8(&idio24gpio->reg->ttl_out0_7) & ~ttl_mask;
+ out_state |= ttl_bits;
+ iowrite8(out_state, &idio24gpio->reg->ttl_out0_7);
+
+ raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
+}
+
static void idio_24_irq_ack(struct irq_data *data)
{
}
@@ -397,7 +512,9 @@ static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id)
idio24gpio->chip.direction_input = idio_24_gpio_direction_input;
idio24gpio->chip.direction_output = idio_24_gpio_direction_output;
idio24gpio->chip.get = idio_24_gpio_get;
+ idio24gpio->chip.get_multiple = idio_24_gpio_get_multiple;
idio24gpio->chip.set = idio_24_gpio_set;
+ idio24gpio->chip.set_multiple = idio_24_gpio_set_multiple;
raw_spin_lock_init(&idio24gpio->lock);
diff --git a/drivers/gpio/gpio-pmic-eic-sprd.c b/drivers/gpio/gpio-pmic-eic-sprd.c
new file mode 100644
index 000000000000..66d68d991162
--- /dev/null
+++ b/drivers/gpio/gpio-pmic-eic-sprd.c
@@ -0,0 +1,330 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Spreadtrum Communications Inc.
+ * Copyright (C) 2018 Linaro Ltd.
+ */
+
+#include <linux/gpio/driver.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+/* EIC registers definition */
+#define SPRD_PMIC_EIC_DATA 0x0
+#define SPRD_PMIC_EIC_DMSK 0x4
+#define SPRD_PMIC_EIC_IEV 0x14
+#define SPRD_PMIC_EIC_IE 0x18
+#define SPRD_PMIC_EIC_RIS 0x1c
+#define SPRD_PMIC_EIC_MIS 0x20
+#define SPRD_PMIC_EIC_IC 0x24
+#define SPRD_PMIC_EIC_TRIG 0x28
+#define SPRD_PMIC_EIC_CTRL0 0x40
+
+/*
+ * The PMIC EIC controller only has one bank, and each bank now can contain
+ * 16 EICs.
+ */
+#define SPRD_PMIC_EIC_PER_BANK_NR 16
+#define SPRD_PMIC_EIC_NR SPRD_PMIC_EIC_PER_BANK_NR
+#define SPRD_PMIC_EIC_DATA_MASK GENMASK(15, 0)
+#define SPRD_PMIC_EIC_BIT(x) ((x) & (SPRD_PMIC_EIC_PER_BANK_NR - 1))
+#define SPRD_PMIC_EIC_DBNC_MASK GENMASK(11, 0)
+
+/*
+ * These registers are modified under the irq bus lock and cached to avoid
+ * unnecessary writes in bus_sync_unlock.
+ */
+enum {
+ REG_IEV,
+ REG_IE,
+ REG_TRIG,
+ CACHE_NR_REGS
+};
+
+/**
+ * struct sprd_pmic_eic - PMIC EIC controller
+ * @chip: the gpio_chip structure.
+ * @intc: the irq_chip structure.
+ * @regmap: the regmap from the parent device.
+ * @offset: the EIC controller's offset address of the PMIC.
+ * @reg: the array to cache the EIC registers.
+ * @buslock: for bus lock/sync and unlock.
+ * @irq: the interrupt number of the PMIC EIC conteroller.
+ */
+struct sprd_pmic_eic {
+ struct gpio_chip chip;
+ struct irq_chip intc;
+ struct regmap *map;
+ u32 offset;
+ u8 reg[CACHE_NR_REGS];
+ struct mutex buslock;
+ int irq;
+};
+
+static void sprd_pmic_eic_update(struct gpio_chip *chip, unsigned int offset,
+ u16 reg, unsigned int val)
+{
+ struct sprd_pmic_eic *pmic_eic = gpiochip_get_data(chip);
+ u32 shift = SPRD_PMIC_EIC_BIT(offset);
+
+ regmap_update_bits(pmic_eic->map, pmic_eic->offset + reg,
+ BIT(shift), val << shift);
+}
+
+static int sprd_pmic_eic_read(struct gpio_chip *chip, unsigned int offset,
+ u16 reg)
+{
+ struct sprd_pmic_eic *pmic_eic = gpiochip_get_data(chip);
+ u32 value;
+ int ret;
+
+ ret = regmap_read(pmic_eic->map, pmic_eic->offset + reg, &value);
+ if (ret)
+ return ret;
+
+ return !!(value & BIT(SPRD_PMIC_EIC_BIT(offset)));
+}
+
+static int sprd_pmic_eic_request(struct gpio_chip *chip, unsigned int offset)
+{
+ sprd_pmic_eic_update(chip, offset, SPRD_PMIC_EIC_DMSK, 1);
+ return 0;
+}
+
+static void sprd_pmic_eic_free(struct gpio_chip *chip, unsigned int offset)
+{
+ sprd_pmic_eic_update(chip, offset, SPRD_PMIC_EIC_DMSK, 0);
+}
+
+static int sprd_pmic_eic_get(struct gpio_chip *chip, unsigned int offset)
+{
+ return sprd_pmic_eic_read(chip, offset, SPRD_PMIC_EIC_DATA);
+}
+
+static int sprd_pmic_eic_direction_input(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ /* EICs are always input, nothing need to do here. */
+ return 0;
+}
+
+static void sprd_pmic_eic_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
+{
+ /* EICs are always input, nothing need to do here. */
+}
+
+static int sprd_pmic_eic_set_debounce(struct gpio_chip *chip,
+ unsigned int offset,
+ unsigned int debounce)
+{
+ struct sprd_pmic_eic *pmic_eic = gpiochip_get_data(chip);
+ u32 reg, value;
+ int ret;
+
+ reg = SPRD_PMIC_EIC_CTRL0 + SPRD_PMIC_EIC_BIT(offset) * 0x4;
+ ret = regmap_read(pmic_eic->map, pmic_eic->offset + reg, &value);
+ if (ret)
+ return ret;
+
+ value &= ~SPRD_PMIC_EIC_DBNC_MASK;
+ value |= (debounce / 1000) & SPRD_PMIC_EIC_DBNC_MASK;
+ return regmap_write(pmic_eic->map, pmic_eic->offset + reg, value);
+}
+
+static int sprd_pmic_eic_set_config(struct gpio_chip *chip, unsigned int offset,
+ unsigned long config)
+{
+ unsigned long param = pinconf_to_config_param(config);
+ u32 arg = pinconf_to_config_argument(config);
+
+ if (param == PIN_CONFIG_INPUT_DEBOUNCE)
+ return sprd_pmic_eic_set_debounce(chip, offset, arg);
+
+ return -ENOTSUPP;
+}
+
+static void sprd_pmic_eic_irq_mask(struct irq_data *data)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ struct sprd_pmic_eic *pmic_eic = gpiochip_get_data(chip);
+
+ pmic_eic->reg[REG_IE] = 0;
+ pmic_eic->reg[REG_TRIG] = 0;
+}
+
+static void sprd_pmic_eic_irq_unmask(struct irq_data *data)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ struct sprd_pmic_eic *pmic_eic = gpiochip_get_data(chip);
+
+ pmic_eic->reg[REG_IE] = 1;
+ pmic_eic->reg[REG_TRIG] = 1;
+}
+
+static int sprd_pmic_eic_irq_set_type(struct irq_data *data,
+ unsigned int flow_type)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ struct sprd_pmic_eic *pmic_eic = gpiochip_get_data(chip);
+
+ switch (flow_type) {
+ case IRQ_TYPE_LEVEL_HIGH:
+ pmic_eic->reg[REG_IEV] = 1;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ pmic_eic->reg[REG_IEV] = 0;
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ return 0;
+}
+
+static void sprd_pmic_eic_bus_lock(struct irq_data *data)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ struct sprd_pmic_eic *pmic_eic = gpiochip_get_data(chip);
+
+ mutex_lock(&pmic_eic->buslock);
+}
+
+static void sprd_pmic_eic_bus_sync_unlock(struct irq_data *data)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ struct sprd_pmic_eic *pmic_eic = gpiochip_get_data(chip);
+ u32 offset = irqd_to_hwirq(data);
+
+ /* Set irq type */
+ sprd_pmic_eic_update(chip, offset, SPRD_PMIC_EIC_IEV,
+ pmic_eic->reg[REG_IEV]);
+ /* Set irq unmask */
+ sprd_pmic_eic_update(chip, offset, SPRD_PMIC_EIC_IE,
+ pmic_eic->reg[REG_IE]);
+ /* Generate trigger start pulse for debounce EIC */
+ sprd_pmic_eic_update(chip, offset, SPRD_PMIC_EIC_TRIG,
+ pmic_eic->reg[REG_TRIG]);
+
+ mutex_unlock(&pmic_eic->buslock);
+}
+
+static irqreturn_t sprd_pmic_eic_irq_handler(int irq, void *data)
+{
+ struct sprd_pmic_eic *pmic_eic = data;
+ struct gpio_chip *chip = &pmic_eic->chip;
+ unsigned long status;
+ u32 n, girq, val;
+ int ret;
+
+ ret = regmap_read(pmic_eic->map, pmic_eic->offset + SPRD_PMIC_EIC_MIS,
+ &val);
+ if (ret)
+ return IRQ_RETVAL(ret);
+
+ status = val & SPRD_PMIC_EIC_DATA_MASK;
+
+ for_each_set_bit(n, &status, chip->ngpio) {
+ /* Clear the interrupt */
+ sprd_pmic_eic_update(chip, n, SPRD_PMIC_EIC_IC, 1);
+
+ girq = irq_find_mapping(chip->irq.domain, n);
+ handle_nested_irq(girq);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int sprd_pmic_eic_probe(struct platform_device *pdev)
+{
+ struct gpio_irq_chip *irq;
+ struct sprd_pmic_eic *pmic_eic;
+ int ret;
+
+ pmic_eic = devm_kzalloc(&pdev->dev, sizeof(*pmic_eic), GFP_KERNEL);
+ if (!pmic_eic)
+ return -ENOMEM;
+
+ mutex_init(&pmic_eic->buslock);
+
+ pmic_eic->irq = platform_get_irq(pdev, 0);
+ if (pmic_eic->irq < 0) {
+ dev_err(&pdev->dev, "Failed to get PMIC EIC interrupt.\n");
+ return pmic_eic->irq;
+ }
+
+ pmic_eic->map = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!pmic_eic->map)
+ return -ENODEV;
+
+ ret = of_property_read_u32(pdev->dev.of_node, "reg", &pmic_eic->offset);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to get PMIC EIC base address.\n");
+ return ret;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, pmic_eic->irq, NULL,
+ sprd_pmic_eic_irq_handler,
+ IRQF_TRIGGER_LOW |
+ IRQF_ONESHOT | IRQF_NO_SUSPEND,
+ dev_name(&pdev->dev), pmic_eic);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request PMIC EIC IRQ.\n");
+ return ret;
+ }
+
+ pmic_eic->chip.label = dev_name(&pdev->dev);
+ pmic_eic->chip.ngpio = SPRD_PMIC_EIC_NR;
+ pmic_eic->chip.base = -1;
+ pmic_eic->chip.parent = &pdev->dev;
+ pmic_eic->chip.of_node = pdev->dev.of_node;
+ pmic_eic->chip.direction_input = sprd_pmic_eic_direction_input;
+ pmic_eic->chip.request = sprd_pmic_eic_request;
+ pmic_eic->chip.free = sprd_pmic_eic_free;
+ pmic_eic->chip.set_config = sprd_pmic_eic_set_config;
+ pmic_eic->chip.set = sprd_pmic_eic_set;
+ pmic_eic->chip.get = sprd_pmic_eic_get;
+
+ pmic_eic->intc.name = dev_name(&pdev->dev);
+ pmic_eic->intc.irq_mask = sprd_pmic_eic_irq_mask;
+ pmic_eic->intc.irq_unmask = sprd_pmic_eic_irq_unmask;
+ pmic_eic->intc.irq_set_type = sprd_pmic_eic_irq_set_type;
+ pmic_eic->intc.irq_bus_lock = sprd_pmic_eic_bus_lock;
+ pmic_eic->intc.irq_bus_sync_unlock = sprd_pmic_eic_bus_sync_unlock;
+ pmic_eic->intc.flags = IRQCHIP_SKIP_SET_WAKE;
+
+ irq = &pmic_eic->chip.irq;
+ irq->chip = &pmic_eic->intc;
+ irq->threaded = true;
+
+ ret = devm_gpiochip_add_data(&pdev->dev, &pmic_eic->chip, pmic_eic);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Could not register gpiochip %d.\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, pmic_eic);
+ return 0;
+}
+
+static const struct of_device_id sprd_pmic_eic_of_match[] = {
+ { .compatible = "sprd,sc27xx-eic", },
+ { /* end of list */ }
+};
+MODULE_DEVICE_TABLE(of, sprd_pmic_eic_of_match);
+
+static struct platform_driver sprd_pmic_eic_driver = {
+ .probe = sprd_pmic_eic_probe,
+ .driver = {
+ .name = "sprd-pmic-eic",
+ .of_match_table = sprd_pmic_eic_of_match,
+ },
+};
+
+module_platform_driver(sprd_pmic_eic_driver);
+
+MODULE_DESCRIPTION("Spreadtrum PMIC EIC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-raspberrypi-exp.c b/drivers/gpio/gpio-raspberrypi-exp.c
new file mode 100644
index 000000000000..d6d36d537e37
--- /dev/null
+++ b/drivers/gpio/gpio-raspberrypi-exp.c
@@ -0,0 +1,252 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Raspberry Pi 3 expander GPIO driver
+ *
+ * Uses the firmware mailbox service to communicate with the
+ * GPIO expander on the VPU.
+ *
+ * Copyright (C) 2017 Raspberry Pi Trading Ltd.
+ */
+
+#include <linux/err.h>
+#include <linux/gpio/driver.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <soc/bcm2835/raspberrypi-firmware.h>
+
+#define MODULE_NAME "raspberrypi-exp-gpio"
+#define NUM_GPIO 8
+
+#define RPI_EXP_GPIO_BASE 128
+
+#define RPI_EXP_GPIO_DIR_IN 0
+#define RPI_EXP_GPIO_DIR_OUT 1
+
+struct rpi_exp_gpio {
+ struct gpio_chip gc;
+ struct rpi_firmware *fw;
+};
+
+/* VC4 firmware mailbox interface data structures */
+
+struct gpio_set_config {
+ u32 gpio;
+ u32 direction;
+ u32 polarity;
+ u32 term_en;
+ u32 term_pull_up;
+ u32 state;
+};
+
+struct gpio_get_config {
+ u32 gpio;
+ u32 direction;
+ u32 polarity;
+ u32 term_en;
+ u32 term_pull_up;
+};
+
+struct gpio_get_set_state {
+ u32 gpio;
+ u32 state;
+};
+
+static int rpi_exp_gpio_get_polarity(struct gpio_chip *gc, unsigned int off)
+{
+ struct rpi_exp_gpio *gpio;
+ struct gpio_get_config get;
+ int ret;
+
+ gpio = gpiochip_get_data(gc);
+
+ get.gpio = off + RPI_EXP_GPIO_BASE; /* GPIO to update */
+
+ ret = rpi_firmware_property(gpio->fw, RPI_FIRMWARE_GET_GPIO_CONFIG,
+ &get, sizeof(get));
+ if (ret || get.gpio != 0) {
+ dev_err(gc->parent, "Failed to get GPIO %u config (%d %x)\n",
+ off, ret, get.gpio);
+ return ret ? ret : -EIO;
+ }
+ return get.polarity;
+}
+
+static int rpi_exp_gpio_dir_in(struct gpio_chip *gc, unsigned int off)
+{
+ struct rpi_exp_gpio *gpio;
+ struct gpio_set_config set_in;
+ int ret;
+
+ gpio = gpiochip_get_data(gc);
+
+ set_in.gpio = off + RPI_EXP_GPIO_BASE; /* GPIO to update */
+ set_in.direction = RPI_EXP_GPIO_DIR_IN;
+ set_in.term_en = 0; /* termination disabled */
+ set_in.term_pull_up = 0; /* n/a as termination disabled */
+ set_in.state = 0; /* n/a as configured as an input */
+
+ ret = rpi_exp_gpio_get_polarity(gc, off);
+ if (ret < 0)
+ return ret;
+ set_in.polarity = ret; /* Retain existing setting */
+
+ ret = rpi_firmware_property(gpio->fw, RPI_FIRMWARE_SET_GPIO_CONFIG,
+ &set_in, sizeof(set_in));
+ if (ret || set_in.gpio != 0) {
+ dev_err(gc->parent, "Failed to set GPIO %u to input (%d %x)\n",
+ off, ret, set_in.gpio);
+ return ret ? ret : -EIO;
+ }
+ return 0;
+}
+
+static int rpi_exp_gpio_dir_out(struct gpio_chip *gc, unsigned int off, int val)
+{
+ struct rpi_exp_gpio *gpio;
+ struct gpio_set_config set_out;
+ int ret;
+
+ gpio = gpiochip_get_data(gc);
+
+ set_out.gpio = off + RPI_EXP_GPIO_BASE; /* GPIO to update */
+ set_out.direction = RPI_EXP_GPIO_DIR_OUT;
+ set_out.term_en = 0; /* n/a as an output */
+ set_out.term_pull_up = 0; /* n/a as termination disabled */
+ set_out.state = val; /* Output state */
+
+ ret = rpi_exp_gpio_get_polarity(gc, off);
+ if (ret < 0)
+ return ret;
+ set_out.polarity = ret; /* Retain existing setting */
+
+ ret = rpi_firmware_property(gpio->fw, RPI_FIRMWARE_SET_GPIO_CONFIG,
+ &set_out, sizeof(set_out));
+ if (ret || set_out.gpio != 0) {
+ dev_err(gc->parent, "Failed to set GPIO %u to output (%d %x)\n",
+ off, ret, set_out.gpio);
+ return ret ? ret : -EIO;
+ }
+ return 0;
+}
+
+static int rpi_exp_gpio_get_direction(struct gpio_chip *gc, unsigned int off)
+{
+ struct rpi_exp_gpio *gpio;
+ struct gpio_get_config get;
+ int ret;
+
+ gpio = gpiochip_get_data(gc);
+
+ get.gpio = off + RPI_EXP_GPIO_BASE; /* GPIO to update */
+
+ ret = rpi_firmware_property(gpio->fw, RPI_FIRMWARE_GET_GPIO_CONFIG,
+ &get, sizeof(get));
+ if (ret || get.gpio != 0) {
+ dev_err(gc->parent,
+ "Failed to get GPIO %u config (%d %x)\n", off, ret,
+ get.gpio);
+ return ret ? ret : -EIO;
+ }
+ return !get.direction;
+}
+
+static int rpi_exp_gpio_get(struct gpio_chip *gc, unsigned int off)
+{
+ struct rpi_exp_gpio *gpio;
+ struct gpio_get_set_state get;
+ int ret;
+
+ gpio = gpiochip_get_data(gc);
+
+ get.gpio = off + RPI_EXP_GPIO_BASE; /* GPIO to update */
+ get.state = 0; /* storage for returned value */
+
+ ret = rpi_firmware_property(gpio->fw, RPI_FIRMWARE_GET_GPIO_STATE,
+ &get, sizeof(get));
+ if (ret || get.gpio != 0) {
+ dev_err(gc->parent,
+ "Failed to get GPIO %u state (%d %x)\n", off, ret,
+ get.gpio);
+ return ret ? ret : -EIO;
+ }
+ return !!get.state;
+}
+
+static void rpi_exp_gpio_set(struct gpio_chip *gc, unsigned int off, int val)
+{
+ struct rpi_exp_gpio *gpio;
+ struct gpio_get_set_state set;
+ int ret;
+
+ gpio = gpiochip_get_data(gc);
+
+ set.gpio = off + RPI_EXP_GPIO_BASE; /* GPIO to update */
+ set.state = val; /* Output state */
+
+ ret = rpi_firmware_property(gpio->fw, RPI_FIRMWARE_SET_GPIO_STATE,
+ &set, sizeof(set));
+ if (ret || set.gpio != 0)
+ dev_err(gc->parent,
+ "Failed to set GPIO %u state (%d %x)\n", off, ret,
+ set.gpio);
+}
+
+static int rpi_exp_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct device_node *fw_node;
+ struct rpi_firmware *fw;
+ struct rpi_exp_gpio *rpi_gpio;
+
+ fw_node = of_get_parent(np);
+ if (!fw_node) {
+ dev_err(dev, "Missing firmware node\n");
+ return -ENOENT;
+ }
+
+ fw = rpi_firmware_get(fw_node);
+ if (!fw)
+ return -EPROBE_DEFER;
+
+ rpi_gpio = devm_kzalloc(dev, sizeof(*rpi_gpio), GFP_KERNEL);
+ if (!rpi_gpio)
+ return -ENOMEM;
+
+ rpi_gpio->fw = fw;
+ rpi_gpio->gc.parent = dev;
+ rpi_gpio->gc.label = MODULE_NAME;
+ rpi_gpio->gc.owner = THIS_MODULE;
+ rpi_gpio->gc.of_node = np;
+ rpi_gpio->gc.base = -1;
+ rpi_gpio->gc.ngpio = NUM_GPIO;
+
+ rpi_gpio->gc.direction_input = rpi_exp_gpio_dir_in;
+ rpi_gpio->gc.direction_output = rpi_exp_gpio_dir_out;
+ rpi_gpio->gc.get_direction = rpi_exp_gpio_get_direction;
+ rpi_gpio->gc.get = rpi_exp_gpio_get;
+ rpi_gpio->gc.set = rpi_exp_gpio_set;
+ rpi_gpio->gc.can_sleep = true;
+
+ return devm_gpiochip_add_data(dev, &rpi_gpio->gc, rpi_gpio);
+}
+
+static const struct of_device_id rpi_exp_gpio_ids[] = {
+ { .compatible = "raspberrypi,firmware-gpio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rpi_exp_gpio_ids);
+
+static struct platform_driver rpi_exp_gpio_driver = {
+ .driver = {
+ .name = MODULE_NAME,
+ .of_match_table = of_match_ptr(rpi_exp_gpio_ids),
+ },
+ .probe = rpi_exp_gpio_probe,
+};
+module_platform_driver(rpi_exp_gpio_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dave Stevenson <dave.stevenson@raspberrypi.org>");
+MODULE_DESCRIPTION("Raspberry Pi 3 expander GPIO driver");
+MODULE_ALIAS("platform:rpi-exp-gpio");
diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c
index ebaea8b1594b..350390c0b290 100644
--- a/drivers/gpio/gpio-rcar.c
+++ b/drivers/gpio/gpio-rcar.c
@@ -30,6 +30,16 @@
#include <linux/spinlock.h>
#include <linux/slab.h>
+struct gpio_rcar_bank_info {
+ u32 iointsel;
+ u32 inoutsel;
+ u32 outdt;
+ u32 posneg;
+ u32 edglevel;
+ u32 bothedge;
+ u32 intmsk;
+};
+
struct gpio_rcar_priv {
void __iomem *base;
spinlock_t lock;
@@ -39,6 +49,7 @@ struct gpio_rcar_priv {
unsigned int irq_parent;
atomic_t wakeup_path;
bool has_both_edge_trigger;
+ struct gpio_rcar_bank_info bank_info;
};
#define IOINTSEL 0x00 /* General IO/Interrupt Switching Register */
@@ -512,17 +523,62 @@ static int gpio_rcar_remove(struct platform_device *pdev)
return 0;
}
-static int __maybe_unused gpio_rcar_suspend(struct device *dev)
+#ifdef CONFIG_PM_SLEEP
+static int gpio_rcar_suspend(struct device *dev)
{
struct gpio_rcar_priv *p = dev_get_drvdata(dev);
+ p->bank_info.iointsel = gpio_rcar_read(p, IOINTSEL);
+ p->bank_info.inoutsel = gpio_rcar_read(p, INOUTSEL);
+ p->bank_info.outdt = gpio_rcar_read(p, OUTDT);
+ p->bank_info.intmsk = gpio_rcar_read(p, INTMSK);
+ p->bank_info.posneg = gpio_rcar_read(p, POSNEG);
+ p->bank_info.edglevel = gpio_rcar_read(p, EDGLEVEL);
+ if (p->has_both_edge_trigger)
+ p->bank_info.bothedge = gpio_rcar_read(p, BOTHEDGE);
+
if (atomic_read(&p->wakeup_path))
device_set_wakeup_path(dev);
return 0;
}
-static SIMPLE_DEV_PM_OPS(gpio_rcar_pm_ops, gpio_rcar_suspend, NULL);
+static int gpio_rcar_resume(struct device *dev)
+{
+ struct gpio_rcar_priv *p = dev_get_drvdata(dev);
+ unsigned int offset;
+ u32 mask;
+
+ for (offset = 0; offset < p->gpio_chip.ngpio; offset++) {
+ mask = BIT(offset);
+ /* I/O pin */
+ if (!(p->bank_info.iointsel & mask)) {
+ if (p->bank_info.inoutsel & mask)
+ gpio_rcar_direction_output(
+ &p->gpio_chip, offset,
+ !!(p->bank_info.outdt & mask));
+ else
+ gpio_rcar_direction_input(&p->gpio_chip,
+ offset);
+ } else {
+ /* Interrupt pin */
+ gpio_rcar_config_interrupt_input_mode(
+ p,
+ offset,
+ !(p->bank_info.posneg & mask),
+ !(p->bank_info.edglevel & mask),
+ !!(p->bank_info.bothedge & mask));
+
+ if (p->bank_info.intmsk & mask)
+ gpio_rcar_write(p, MSKCLR, mask);
+ }
+ }
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP*/
+
+static SIMPLE_DEV_PM_OPS(gpio_rcar_pm_ops, gpio_rcar_suspend, gpio_rcar_resume);
static struct platform_driver gpio_rcar_device_driver = {
.probe = gpio_rcar_probe,
diff --git a/drivers/gpio/gpio-sprd.c b/drivers/gpio/gpio-sprd.c
new file mode 100644
index 000000000000..55072d2b367f
--- /dev/null
+++ b/drivers/gpio/gpio-sprd.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Spreadtrum Communications Inc.
+ * Copyright (C) 2018 Linaro Ltd.
+ */
+
+#include <linux/bitops.h>
+#include <linux/gpio/driver.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+/* GPIO registers definition */
+#define SPRD_GPIO_DATA 0x0
+#define SPRD_GPIO_DMSK 0x4
+#define SPRD_GPIO_DIR 0x8
+#define SPRD_GPIO_IS 0xc
+#define SPRD_GPIO_IBE 0x10
+#define SPRD_GPIO_IEV 0x14
+#define SPRD_GPIO_IE 0x18
+#define SPRD_GPIO_RIS 0x1c
+#define SPRD_GPIO_MIS 0x20
+#define SPRD_GPIO_IC 0x24
+#define SPRD_GPIO_INEN 0x28
+
+/* We have 16 banks GPIOs and each bank contain 16 GPIOs */
+#define SPRD_GPIO_BANK_NR 16
+#define SPRD_GPIO_NR 256
+#define SPRD_GPIO_BANK_SIZE 0x80
+#define SPRD_GPIO_BANK_MASK GENMASK(15, 0)
+#define SPRD_GPIO_BIT(x) ((x) & (SPRD_GPIO_BANK_NR - 1))
+
+struct sprd_gpio {
+ struct gpio_chip chip;
+ void __iomem *base;
+ spinlock_t lock;
+ int irq;
+};
+
+static inline void __iomem *sprd_gpio_bank_base(struct sprd_gpio *sprd_gpio,
+ unsigned int bank)
+{
+ return sprd_gpio->base + SPRD_GPIO_BANK_SIZE * bank;
+}
+
+static void sprd_gpio_update(struct gpio_chip *chip, unsigned int offset,
+ u16 reg, int val)
+{
+ struct sprd_gpio *sprd_gpio = gpiochip_get_data(chip);
+ void __iomem *base = sprd_gpio_bank_base(sprd_gpio,
+ offset / SPRD_GPIO_BANK_NR);
+ unsigned long flags;
+ u32 tmp;
+
+ spin_lock_irqsave(&sprd_gpio->lock, flags);
+ tmp = readl_relaxed(base + reg);
+
+ if (val)
+ tmp |= BIT(SPRD_GPIO_BIT(offset));
+ else
+ tmp &= ~BIT(SPRD_GPIO_BIT(offset));
+
+ writel_relaxed(tmp, base + reg);
+ spin_unlock_irqrestore(&sprd_gpio->lock, flags);
+}
+
+static int sprd_gpio_read(struct gpio_chip *chip, unsigned int offset, u16 reg)
+{
+ struct sprd_gpio *sprd_gpio = gpiochip_get_data(chip);
+ void __iomem *base = sprd_gpio_bank_base(sprd_gpio,
+ offset / SPRD_GPIO_BANK_NR);
+
+ return !!(readl_relaxed(base + reg) & BIT(SPRD_GPIO_BIT(offset)));
+}
+
+static int sprd_gpio_request(struct gpio_chip *chip, unsigned int offset)
+{
+ sprd_gpio_update(chip, offset, SPRD_GPIO_DMSK, 1);
+ return 0;
+}
+
+static void sprd_gpio_free(struct gpio_chip *chip, unsigned int offset)
+{
+ sprd_gpio_update(chip, offset, SPRD_GPIO_DMSK, 0);
+}
+
+static int sprd_gpio_direction_input(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ sprd_gpio_update(chip, offset, SPRD_GPIO_DIR, 0);
+ sprd_gpio_update(chip, offset, SPRD_GPIO_INEN, 1);
+ return 0;
+}
+
+static int sprd_gpio_direction_output(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ sprd_gpio_update(chip, offset, SPRD_GPIO_DIR, 1);
+ sprd_gpio_update(chip, offset, SPRD_GPIO_INEN, 0);
+ sprd_gpio_update(chip, offset, SPRD_GPIO_DATA, value);
+ return 0;
+}
+
+static int sprd_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ return sprd_gpio_read(chip, offset, SPRD_GPIO_DATA);
+}
+
+static void sprd_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
+{
+ sprd_gpio_update(chip, offset, SPRD_GPIO_DATA, value);
+}
+
+static void sprd_gpio_irq_mask(struct irq_data *data)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ u32 offset = irqd_to_hwirq(data);
+
+ sprd_gpio_update(chip, offset, SPRD_GPIO_IE, 0);
+}
+
+static void sprd_gpio_irq_ack(struct irq_data *data)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ u32 offset = irqd_to_hwirq(data);
+
+ sprd_gpio_update(chip, offset, SPRD_GPIO_IC, 1);
+}
+
+static void sprd_gpio_irq_unmask(struct irq_data *data)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ u32 offset = irqd_to_hwirq(data);
+
+ sprd_gpio_update(chip, offset, SPRD_GPIO_IE, 1);
+}
+
+static int sprd_gpio_irq_set_type(struct irq_data *data,
+ unsigned int flow_type)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ u32 offset = irqd_to_hwirq(data);
+
+ switch (flow_type) {
+ case IRQ_TYPE_EDGE_RISING:
+ sprd_gpio_update(chip, offset, SPRD_GPIO_IS, 0);
+ sprd_gpio_update(chip, offset, SPRD_GPIO_IBE, 0);
+ sprd_gpio_update(chip, offset, SPRD_GPIO_IEV, 1);
+ irq_set_handler_locked(data, handle_edge_irq);
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ sprd_gpio_update(chip, offset, SPRD_GPIO_IS, 0);
+ sprd_gpio_update(chip, offset, SPRD_GPIO_IBE, 0);
+ sprd_gpio_update(chip, offset, SPRD_GPIO_IEV, 0);
+ irq_set_handler_locked(data, handle_edge_irq);
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ sprd_gpio_update(chip, offset, SPRD_GPIO_IS, 0);
+ sprd_gpio_update(chip, offset, SPRD_GPIO_IBE, 1);
+ irq_set_handler_locked(data, handle_edge_irq);
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ sprd_gpio_update(chip, offset, SPRD_GPIO_IS, 1);
+ sprd_gpio_update(chip, offset, SPRD_GPIO_IBE, 0);
+ sprd_gpio_update(chip, offset, SPRD_GPIO_IEV, 1);
+ irq_set_handler_locked(data, handle_level_irq);
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ sprd_gpio_update(chip, offset, SPRD_GPIO_IS, 1);
+ sprd_gpio_update(chip, offset, SPRD_GPIO_IBE, 0);
+ sprd_gpio_update(chip, offset, SPRD_GPIO_IEV, 0);
+ irq_set_handler_locked(data, handle_level_irq);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void sprd_gpio_irq_handler(struct irq_desc *desc)
+{
+ struct gpio_chip *chip = irq_desc_get_handler_data(desc);
+ struct irq_chip *ic = irq_desc_get_chip(desc);
+ struct sprd_gpio *sprd_gpio = gpiochip_get_data(chip);
+ u32 bank, n, girq;
+
+ chained_irq_enter(ic, desc);
+
+ for (bank = 0; bank * SPRD_GPIO_BANK_NR < chip->ngpio; bank++) {
+ void __iomem *base = sprd_gpio_bank_base(sprd_gpio, bank);
+ unsigned long reg = readl_relaxed(base + SPRD_GPIO_MIS) &
+ SPRD_GPIO_BANK_MASK;
+
+ for_each_set_bit(n, &reg, SPRD_GPIO_BANK_NR) {
+ girq = irq_find_mapping(chip->irq.domain,
+ bank * SPRD_GPIO_BANK_NR + n);
+
+ generic_handle_irq(girq);
+ }
+
+ }
+ chained_irq_exit(ic, desc);
+}
+
+static struct irq_chip sprd_gpio_irqchip = {
+ .name = "sprd-gpio",
+ .irq_ack = sprd_gpio_irq_ack,
+ .irq_mask = sprd_gpio_irq_mask,
+ .irq_unmask = sprd_gpio_irq_unmask,
+ .irq_set_type = sprd_gpio_irq_set_type,
+ .flags = IRQCHIP_SKIP_SET_WAKE,
+};
+
+static int sprd_gpio_probe(struct platform_device *pdev)
+{
+ struct gpio_irq_chip *irq;
+ struct sprd_gpio *sprd_gpio;
+ struct resource *res;
+ int ret;
+
+ sprd_gpio = devm_kzalloc(&pdev->dev, sizeof(*sprd_gpio), GFP_KERNEL);
+ if (!sprd_gpio)
+ return -ENOMEM;
+
+ sprd_gpio->irq = platform_get_irq(pdev, 0);
+ if (sprd_gpio->irq < 0) {
+ dev_err(&pdev->dev, "Failed to get GPIO interrupt.\n");
+ return sprd_gpio->irq;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ sprd_gpio->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(sprd_gpio->base))
+ return PTR_ERR(sprd_gpio->base);
+
+ spin_lock_init(&sprd_gpio->lock);
+
+ sprd_gpio->chip.label = dev_name(&pdev->dev);
+ sprd_gpio->chip.ngpio = SPRD_GPIO_NR;
+ sprd_gpio->chip.base = -1;
+ sprd_gpio->chip.parent = &pdev->dev;
+ sprd_gpio->chip.of_node = pdev->dev.of_node;
+ sprd_gpio->chip.request = sprd_gpio_request;
+ sprd_gpio->chip.free = sprd_gpio_free;
+ sprd_gpio->chip.get = sprd_gpio_get;
+ sprd_gpio->chip.set = sprd_gpio_set;
+ sprd_gpio->chip.direction_input = sprd_gpio_direction_input;
+ sprd_gpio->chip.direction_output = sprd_gpio_direction_output;
+
+ irq = &sprd_gpio->chip.irq;
+ irq->chip = &sprd_gpio_irqchip;
+ irq->handler = handle_bad_irq;
+ irq->default_type = IRQ_TYPE_NONE;
+ irq->parent_handler = sprd_gpio_irq_handler;
+ irq->parent_handler_data = sprd_gpio;
+ irq->num_parents = 1;
+ irq->parents = &sprd_gpio->irq;
+
+ ret = devm_gpiochip_add_data(&pdev->dev, &sprd_gpio->chip, sprd_gpio);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Could not register gpiochip %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, sprd_gpio);
+ return 0;
+}
+
+static const struct of_device_id sprd_gpio_of_match[] = {
+ { .compatible = "sprd,sc9860-gpio", },
+ { /* end of list */ }
+};
+MODULE_DEVICE_TABLE(of, sprd_gpio_of_match);
+
+static struct platform_driver sprd_gpio_driver = {
+ .probe = sprd_gpio_probe,
+ .driver = {
+ .name = "sprd-gpio",
+ .of_match_table = sprd_gpio_of_match,
+ },
+};
+
+module_platform_driver_probe(sprd_gpio_driver, sprd_gpio_probe);
+
+MODULE_DESCRIPTION("Spreadtrum GPIO driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c
index 02fa8fe2292a..94396caaca75 100644
--- a/drivers/gpio/gpio-tegra.c
+++ b/drivers/gpio/gpio-tegra.c
@@ -506,7 +506,7 @@ static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable)
#include <linux/debugfs.h>
#include <linux/seq_file.h>
-static int dbg_gpio_show(struct seq_file *s, void *unused)
+static int tegra_dbg_gpio_show(struct seq_file *s, void *unused)
{
struct tegra_gpio_info *tgi = s->private;
unsigned int i, j;
@@ -530,22 +530,12 @@ static int dbg_gpio_show(struct seq_file *s, void *unused)
return 0;
}
-static int dbg_gpio_open(struct inode *inode, struct file *file)
-{
- return single_open(file, dbg_gpio_show, inode->i_private);
-}
-
-static const struct file_operations debug_fops = {
- .open = dbg_gpio_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(tegra_dbg_gpio);
static void tegra_gpio_debuginit(struct tegra_gpio_info *tgi)
{
(void) debugfs_create_file("tegra_gpio", 0444,
- NULL, tgi, &debug_fops);
+ NULL, tgi, &tegra_dbg_gpio_fops);
}
#else
diff --git a/drivers/gpio/gpio-timberdale.c b/drivers/gpio/gpio-timberdale.c
index 181f86ce00cd..6520a8475910 100644
--- a/drivers/gpio/gpio-timberdale.c
+++ b/drivers/gpio/gpio-timberdale.c
@@ -238,11 +238,10 @@ static int timbgpio_probe(struct platform_device *pdev)
return -EINVAL;
}
- tgpio = devm_kzalloc(dev, sizeof(struct timbgpio), GFP_KERNEL);
- if (!tgpio) {
- dev_err(dev, "Memory alloc failed\n");
+ tgpio = devm_kzalloc(dev, sizeof(*tgpio), GFP_KERNEL);
+ if (!tgpio)
return -EINVAL;
- }
+
tgpio->irq_base = pdata->irq_base;
spin_lock_init(&tgpio->lock);
diff --git a/drivers/gpio/gpio-tps68470.c b/drivers/gpio/gpio-tps68470.c
index fa2662f8b026..aff6e504c666 100644
--- a/drivers/gpio/gpio-tps68470.c
+++ b/drivers/gpio/gpio-tps68470.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* GPIO driver for TPS68470 PMIC
*
@@ -8,15 +9,6 @@
* Tianshu Qiu <tian.shu.qiu@intel.com>
* Jian Xu Zheng <jian.xu.zheng@intel.com>
* Yuning Pu <yuning.pu@intel.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 program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/gpio/driver.h>
diff --git a/drivers/gpio/gpio-tz1090-pdc.c b/drivers/gpio/gpio-tz1090-pdc.c
deleted file mode 100644
index 5b7781741ee9..000000000000
--- a/drivers/gpio/gpio-tz1090-pdc.c
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Toumaz Xenif TZ1090 PDC GPIO handling.
- *
- * Copyright (C) 2012-2013 Imagination Technologies Ltd.
- *
- * 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.
- */
-
-#include <linux/bitops.h>
-#include <linux/gpio.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/of_irq.h>
-#include <linux/pinctrl/consumer.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/syscore_ops.h>
-#include <asm/global_lock.h>
-
-/* Register offsets from SOC_GPIO_CONTROL0 */
-#define REG_SOC_GPIO_CONTROL0 0x00
-#define REG_SOC_GPIO_CONTROL1 0x04
-#define REG_SOC_GPIO_CONTROL2 0x08
-#define REG_SOC_GPIO_CONTROL3 0x0c
-#define REG_SOC_GPIO_STATUS 0x80
-
-/* PDC GPIOs go after normal GPIOs */
-#define GPIO_PDC_BASE 90
-#define GPIO_PDC_NGPIO 7
-
-/* Out of PDC gpios, only syswakes have irqs */
-#define GPIO_PDC_IRQ_FIRST 2
-#define GPIO_PDC_NIRQ 3
-
-/**
- * struct tz1090_pdc_gpio - GPIO bank private data
- * @chip: Generic GPIO chip for GPIO bank
- * @reg: Base of registers, offset for this GPIO bank
- * @irq: IRQ numbers for Syswake GPIOs
- *
- * This is the main private data for the PDC GPIO driver. It encapsulates a
- * gpio_chip, and the callbacks for the gpio_chip can access the private data
- * with the to_pdc() macro below.
- */
-struct tz1090_pdc_gpio {
- struct gpio_chip chip;
- void __iomem *reg;
- int irq[GPIO_PDC_NIRQ];
-};
-
-/* Register accesses into the PDC MMIO area */
-
-static inline void pdc_write(struct tz1090_pdc_gpio *priv, unsigned int reg_offs,
- unsigned int data)
-{
- writel(data, priv->reg + reg_offs);
-}
-
-static inline unsigned int pdc_read(struct tz1090_pdc_gpio *priv,
- unsigned int reg_offs)
-{
- return readl(priv->reg + reg_offs);
-}
-
-/* Generic GPIO interface */
-
-static int tz1090_pdc_gpio_direction_input(struct gpio_chip *chip,
- unsigned int offset)
-{
- struct tz1090_pdc_gpio *priv = gpiochip_get_data(chip);
- u32 value;
- int lstat;
-
- __global_lock2(lstat);
- value = pdc_read(priv, REG_SOC_GPIO_CONTROL1);
- value |= BIT(offset);
- pdc_write(priv, REG_SOC_GPIO_CONTROL1, value);
- __global_unlock2(lstat);
-
- return 0;
-}
-
-static int tz1090_pdc_gpio_direction_output(struct gpio_chip *chip,
- unsigned int offset,
- int output_value)
-{
- struct tz1090_pdc_gpio *priv = gpiochip_get_data(chip);
- u32 value;
- int lstat;
-
- __global_lock2(lstat);
- /* EXT_POWER doesn't seem to have an output value bit */
- if (offset < 6) {
- value = pdc_read(priv, REG_SOC_GPIO_CONTROL0);
- if (output_value)
- value |= BIT(offset);
- else
- value &= ~BIT(offset);
- pdc_write(priv, REG_SOC_GPIO_CONTROL0, value);
- }
-
- value = pdc_read(priv, REG_SOC_GPIO_CONTROL1);
- value &= ~BIT(offset);
- pdc_write(priv, REG_SOC_GPIO_CONTROL1, value);
- __global_unlock2(lstat);
-
- return 0;
-}
-
-static int tz1090_pdc_gpio_get(struct gpio_chip *chip, unsigned int offset)
-{
- struct tz1090_pdc_gpio *priv = gpiochip_get_data(chip);
- return !!(pdc_read(priv, REG_SOC_GPIO_STATUS) & BIT(offset));
-}
-
-static void tz1090_pdc_gpio_set(struct gpio_chip *chip, unsigned int offset,
- int output_value)
-{
- struct tz1090_pdc_gpio *priv = gpiochip_get_data(chip);
- u32 value;
- int lstat;
-
- /* EXT_POWER doesn't seem to have an output value bit */
- if (offset >= 6)
- return;
-
- __global_lock2(lstat);
- value = pdc_read(priv, REG_SOC_GPIO_CONTROL0);
- if (output_value)
- value |= BIT(offset);
- else
- value &= ~BIT(offset);
- pdc_write(priv, REG_SOC_GPIO_CONTROL0, value);
- __global_unlock2(lstat);
-}
-
-static int tz1090_pdc_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
-{
- struct tz1090_pdc_gpio *priv = gpiochip_get_data(chip);
- unsigned int syswake = offset - GPIO_PDC_IRQ_FIRST;
- int irq;
-
- /* only syswakes have irqs */
- if (syswake >= GPIO_PDC_NIRQ)
- return -EINVAL;
-
- irq = priv->irq[syswake];
- if (!irq)
- return -EINVAL;
-
- return irq;
-}
-
-static int tz1090_pdc_gpio_probe(struct platform_device *pdev)
-{
- struct device_node *np = pdev->dev.of_node;
- struct resource *res_regs;
- struct tz1090_pdc_gpio *priv;
- unsigned int i;
-
- if (!np) {
- dev_err(&pdev->dev, "must be instantiated via devicetree\n");
- return -ENOENT;
- }
-
- res_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res_regs) {
- dev_err(&pdev->dev, "cannot find registers resource\n");
- return -ENOENT;
- }
-
- priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv) {
- dev_err(&pdev->dev, "unable to allocate driver data\n");
- return -ENOMEM;
- }
-
- /* Ioremap the registers */
- priv->reg = devm_ioremap(&pdev->dev, res_regs->start,
- resource_size(res_regs));
- if (!priv->reg) {
- dev_err(&pdev->dev, "unable to ioremap registers\n");
- return -ENOMEM;
- }
-
- /* Set up GPIO chip */
- priv->chip.label = "tz1090-pdc-gpio";
- priv->chip.parent = &pdev->dev;
- priv->chip.direction_input = tz1090_pdc_gpio_direction_input;
- priv->chip.direction_output = tz1090_pdc_gpio_direction_output;
- priv->chip.get = tz1090_pdc_gpio_get;
- priv->chip.set = tz1090_pdc_gpio_set;
- priv->chip.free = gpiochip_generic_free;
- priv->chip.request = gpiochip_generic_request;
- priv->chip.to_irq = tz1090_pdc_gpio_to_irq;
- priv->chip.of_node = np;
-
- /* GPIO numbering */
- priv->chip.base = GPIO_PDC_BASE;
- priv->chip.ngpio = GPIO_PDC_NGPIO;
-
- /* Map the syswake irqs */
- for (i = 0; i < GPIO_PDC_NIRQ; ++i)
- priv->irq[i] = irq_of_parse_and_map(np, i);
-
- /* Add the GPIO bank */
- gpiochip_add_data(&priv->chip, priv);
-
- return 0;
-}
-
-static struct of_device_id tz1090_pdc_gpio_of_match[] = {
- { .compatible = "img,tz1090-pdc-gpio" },
- { },
-};
-
-static struct platform_driver tz1090_pdc_gpio_driver = {
- .driver = {
- .name = "tz1090-pdc-gpio",
- .of_match_table = tz1090_pdc_gpio_of_match,
- },
- .probe = tz1090_pdc_gpio_probe,
-};
-
-static int __init tz1090_pdc_gpio_init(void)
-{
- return platform_driver_register(&tz1090_pdc_gpio_driver);
-}
-subsys_initcall(tz1090_pdc_gpio_init);
diff --git a/drivers/gpio/gpio-tz1090.c b/drivers/gpio/gpio-tz1090.c
deleted file mode 100644
index 0bb9bb583889..000000000000
--- a/drivers/gpio/gpio-tz1090.c
+++ /dev/null
@@ -1,602 +0,0 @@
-/*
- * Toumaz Xenif TZ1090 GPIO handling.
- *
- * Copyright (C) 2008-2013 Imagination Technologies Ltd.
- *
- * Based on ARM PXA code and others.
- *
- * 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.
- */
-
-#include <linux/bitops.h>
-#include <linux/export.h>
-#include <linux/gpio.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/irqdomain.h>
-#include <linux/kernel.h>
-#include <linux/of_irq.h>
-#include <linux/pinctrl/consumer.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/syscore_ops.h>
-#include <asm/global_lock.h>
-
-/* Register offsets from bank base address */
-#define REG_GPIO_DIR 0x00
-#define REG_GPIO_IRQ_PLRT 0x20
-#define REG_GPIO_IRQ_TYPE 0x30
-#define REG_GPIO_IRQ_EN 0x40
-#define REG_GPIO_IRQ_STS 0x50
-#define REG_GPIO_BIT_EN 0x60
-#define REG_GPIO_DIN 0x70
-#define REG_GPIO_DOUT 0x80
-
-/* REG_GPIO_IRQ_PLRT */
-#define REG_GPIO_IRQ_PLRT_LOW 0
-#define REG_GPIO_IRQ_PLRT_HIGH 1
-
-/* REG_GPIO_IRQ_TYPE */
-#define REG_GPIO_IRQ_TYPE_LEVEL 0
-#define REG_GPIO_IRQ_TYPE_EDGE 1
-
-/**
- * struct tz1090_gpio_bank - GPIO bank private data
- * @chip: Generic GPIO chip for GPIO bank
- * @domain: IRQ domain for GPIO bank (may be NULL)
- * @reg: Base of registers, offset for this GPIO bank
- * @irq: IRQ number for GPIO bank
- * @label: Debug GPIO bank label, used for storage of chip->label
- *
- * This is the main private data for a GPIO bank. It encapsulates a gpio_chip,
- * and the callbacks for the gpio_chip can access the private data with the
- * to_bank() macro below.
- */
-struct tz1090_gpio_bank {
- struct gpio_chip chip;
- struct irq_domain *domain;
- void __iomem *reg;
- int irq;
- char label[16];
-};
-
-/**
- * struct tz1090_gpio - Overall GPIO device private data
- * @dev: Device (from platform device)
- * @reg: Base of GPIO registers
- *
- * Represents the overall GPIO device. This structure is actually only
- * temporary, and used during init.
- */
-struct tz1090_gpio {
- struct device *dev;
- void __iomem *reg;
-};
-
-/**
- * struct tz1090_gpio_bank_info - Temporary registration info for GPIO bank
- * @priv: Overall GPIO device private data
- * @node: Device tree node specific to this GPIO bank
- * @index: Index of bank in range 0-2
- */
-struct tz1090_gpio_bank_info {
- struct tz1090_gpio *priv;
- struct device_node *node;
- unsigned int index;
-};
-
-/* Convenience register accessors */
-static inline void tz1090_gpio_write(struct tz1090_gpio_bank *bank,
- unsigned int reg_offs, u32 data)
-{
- iowrite32(data, bank->reg + reg_offs);
-}
-
-static inline u32 tz1090_gpio_read(struct tz1090_gpio_bank *bank,
- unsigned int reg_offs)
-{
- return ioread32(bank->reg + reg_offs);
-}
-
-/* caller must hold LOCK2 */
-static inline void _tz1090_gpio_clear_bit(struct tz1090_gpio_bank *bank,
- unsigned int reg_offs,
- unsigned int offset)
-{
- u32 value;
-
- value = tz1090_gpio_read(bank, reg_offs);
- value &= ~BIT(offset);
- tz1090_gpio_write(bank, reg_offs, value);
-}
-
-static void tz1090_gpio_clear_bit(struct tz1090_gpio_bank *bank,
- unsigned int reg_offs,
- unsigned int offset)
-{
- int lstat;
-
- __global_lock2(lstat);
- _tz1090_gpio_clear_bit(bank, reg_offs, offset);
- __global_unlock2(lstat);
-}
-
-/* caller must hold LOCK2 */
-static inline void _tz1090_gpio_set_bit(struct tz1090_gpio_bank *bank,
- unsigned int reg_offs,
- unsigned int offset)
-{
- u32 value;
-
- value = tz1090_gpio_read(bank, reg_offs);
- value |= BIT(offset);
- tz1090_gpio_write(bank, reg_offs, value);
-}
-
-static void tz1090_gpio_set_bit(struct tz1090_gpio_bank *bank,
- unsigned int reg_offs,
- unsigned int offset)
-{
- int lstat;
-
- __global_lock2(lstat);
- _tz1090_gpio_set_bit(bank, reg_offs, offset);
- __global_unlock2(lstat);
-}
-
-/* caller must hold LOCK2 */
-static inline void _tz1090_gpio_mod_bit(struct tz1090_gpio_bank *bank,
- unsigned int reg_offs,
- unsigned int offset,
- bool val)
-{
- u32 value;
-
- value = tz1090_gpio_read(bank, reg_offs);
- value &= ~BIT(offset);
- if (val)
- value |= BIT(offset);
- tz1090_gpio_write(bank, reg_offs, value);
-}
-
-static void tz1090_gpio_mod_bit(struct tz1090_gpio_bank *bank,
- unsigned int reg_offs,
- unsigned int offset,
- bool val)
-{
- int lstat;
-
- __global_lock2(lstat);
- _tz1090_gpio_mod_bit(bank, reg_offs, offset, val);
- __global_unlock2(lstat);
-}
-
-static inline int tz1090_gpio_read_bit(struct tz1090_gpio_bank *bank,
- unsigned int reg_offs,
- unsigned int offset)
-{
- return tz1090_gpio_read(bank, reg_offs) & BIT(offset);
-}
-
-/* GPIO chip callbacks */
-
-static int tz1090_gpio_direction_input(struct gpio_chip *chip,
- unsigned int offset)
-{
- struct tz1090_gpio_bank *bank = gpiochip_get_data(chip);
- tz1090_gpio_set_bit(bank, REG_GPIO_DIR, offset);
-
- return 0;
-}
-
-static int tz1090_gpio_direction_output(struct gpio_chip *chip,
- unsigned int offset, int output_value)
-{
- struct tz1090_gpio_bank *bank = gpiochip_get_data(chip);
- int lstat;
-
- __global_lock2(lstat);
- _tz1090_gpio_mod_bit(bank, REG_GPIO_DOUT, offset, output_value);
- _tz1090_gpio_clear_bit(bank, REG_GPIO_DIR, offset);
- __global_unlock2(lstat);
-
- return 0;
-}
-
-/*
- * Return GPIO level
- */
-static int tz1090_gpio_get(struct gpio_chip *chip, unsigned int offset)
-{
- struct tz1090_gpio_bank *bank = gpiochip_get_data(chip);
-
- return !!tz1090_gpio_read_bit(bank, REG_GPIO_DIN, offset);
-}
-
-/*
- * Set output GPIO level
- */
-static void tz1090_gpio_set(struct gpio_chip *chip, unsigned int offset,
- int output_value)
-{
- struct tz1090_gpio_bank *bank = gpiochip_get_data(chip);
-
- tz1090_gpio_mod_bit(bank, REG_GPIO_DOUT, offset, output_value);
-}
-
-static int tz1090_gpio_request(struct gpio_chip *chip, unsigned int offset)
-{
- struct tz1090_gpio_bank *bank = gpiochip_get_data(chip);
- int ret;
-
- ret = pinctrl_gpio_request(chip->base + offset);
- if (ret)
- return ret;
-
- tz1090_gpio_set_bit(bank, REG_GPIO_DIR, offset);
- tz1090_gpio_set_bit(bank, REG_GPIO_BIT_EN, offset);
-
- return 0;
-}
-
-static void tz1090_gpio_free(struct gpio_chip *chip, unsigned int offset)
-{
- struct tz1090_gpio_bank *bank = gpiochip_get_data(chip);
-
- pinctrl_gpio_free(chip->base + offset);
-
- tz1090_gpio_clear_bit(bank, REG_GPIO_BIT_EN, offset);
-}
-
-static int tz1090_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
-{
- struct tz1090_gpio_bank *bank = gpiochip_get_data(chip);
-
- if (!bank->domain)
- return -EINVAL;
-
- return irq_create_mapping(bank->domain, offset);
-}
-
-/* IRQ chip handlers */
-
-/* Get TZ1090 GPIO chip from irq data provided to generic IRQ callbacks */
-static inline struct tz1090_gpio_bank *irqd_to_gpio_bank(struct irq_data *data)
-{
- return (struct tz1090_gpio_bank *)data->domain->host_data;
-}
-
-static void tz1090_gpio_irq_polarity(struct tz1090_gpio_bank *bank,
- unsigned int offset, unsigned int polarity)
-{
- tz1090_gpio_mod_bit(bank, REG_GPIO_IRQ_PLRT, offset, polarity);
-}
-
-static void tz1090_gpio_irq_type(struct tz1090_gpio_bank *bank,
- unsigned int offset, unsigned int type)
-{
- tz1090_gpio_mod_bit(bank, REG_GPIO_IRQ_TYPE, offset, type);
-}
-
-/* set polarity to trigger on next edge, whether rising or falling */
-static void tz1090_gpio_irq_next_edge(struct tz1090_gpio_bank *bank,
- unsigned int offset)
-{
- unsigned int value_p, value_i;
- int lstat;
-
- /*
- * Set the GPIO's interrupt polarity to the opposite of the current
- * input value so that the next edge triggers an interrupt.
- */
- __global_lock2(lstat);
- value_i = ~tz1090_gpio_read(bank, REG_GPIO_DIN);
- value_p = tz1090_gpio_read(bank, REG_GPIO_IRQ_PLRT);
- value_p &= ~BIT(offset);
- value_p |= value_i & BIT(offset);
- tz1090_gpio_write(bank, REG_GPIO_IRQ_PLRT, value_p);
- __global_unlock2(lstat);
-}
-
-static unsigned int gpio_startup_irq(struct irq_data *data)
-{
- /*
- * This warning indicates that the type of the irq hasn't been set
- * before enabling the irq. This would normally be done by passing some
- * trigger flags to request_irq().
- */
- WARN(irqd_get_trigger_type(data) == IRQ_TYPE_NONE,
- "irq type not set before enabling gpio irq %d", data->irq);
-
- irq_gc_ack_clr_bit(data);
- irq_gc_mask_set_bit(data);
- return 0;
-}
-
-static int gpio_set_irq_type(struct irq_data *data, unsigned int flow_type)
-{
- struct tz1090_gpio_bank *bank = irqd_to_gpio_bank(data);
- unsigned int type;
- unsigned int polarity;
-
- switch (flow_type) {
- case IRQ_TYPE_EDGE_BOTH:
- type = REG_GPIO_IRQ_TYPE_EDGE;
- polarity = REG_GPIO_IRQ_PLRT_LOW;
- break;
- case IRQ_TYPE_EDGE_RISING:
- type = REG_GPIO_IRQ_TYPE_EDGE;
- polarity = REG_GPIO_IRQ_PLRT_HIGH;
- break;
- case IRQ_TYPE_EDGE_FALLING:
- type = REG_GPIO_IRQ_TYPE_EDGE;
- polarity = REG_GPIO_IRQ_PLRT_LOW;
- break;
- case IRQ_TYPE_LEVEL_HIGH:
- type = REG_GPIO_IRQ_TYPE_LEVEL;
- polarity = REG_GPIO_IRQ_PLRT_HIGH;
- break;
- case IRQ_TYPE_LEVEL_LOW:
- type = REG_GPIO_IRQ_TYPE_LEVEL;
- polarity = REG_GPIO_IRQ_PLRT_LOW;
- break;
- default:
- return -EINVAL;
- }
-
- tz1090_gpio_irq_type(bank, data->hwirq, type);
- irq_setup_alt_chip(data, flow_type);
-
- if (flow_type == IRQ_TYPE_EDGE_BOTH)
- tz1090_gpio_irq_next_edge(bank, data->hwirq);
- else
- tz1090_gpio_irq_polarity(bank, data->hwirq, polarity);
-
- return 0;
-}
-
-#ifdef CONFIG_SUSPEND
-static int gpio_set_irq_wake(struct irq_data *data, unsigned int on)
-{
- struct tz1090_gpio_bank *bank = irqd_to_gpio_bank(data);
-
-#ifdef CONFIG_PM_DEBUG
- pr_info("irq_wake irq%d state:%d\n", data->irq, on);
-#endif
-
- /* wake on gpio block interrupt */
- return irq_set_irq_wake(bank->irq, on);
-}
-#else
-#define gpio_set_irq_wake NULL
-#endif
-
-static void tz1090_gpio_irq_handler(struct irq_desc *desc)
-{
- irq_hw_number_t hw;
- unsigned int irq_stat, irq_no;
- struct tz1090_gpio_bank *bank;
- struct irq_desc *child_desc;
-
- bank = (struct tz1090_gpio_bank *)irq_desc_get_handler_data(desc);
- irq_stat = tz1090_gpio_read(bank, REG_GPIO_DIR) &
- tz1090_gpio_read(bank, REG_GPIO_IRQ_STS) &
- tz1090_gpio_read(bank, REG_GPIO_IRQ_EN) &
- 0x3FFFFFFF; /* 30 bits only */
-
- for (hw = 0; irq_stat; irq_stat >>= 1, ++hw) {
- if (!(irq_stat & 1))
- continue;
-
- irq_no = irq_linear_revmap(bank->domain, hw);
- child_desc = irq_to_desc(irq_no);
-
- /* Toggle edge for pin with both edges triggering enabled */
- if (irqd_get_trigger_type(&child_desc->irq_data)
- == IRQ_TYPE_EDGE_BOTH)
- tz1090_gpio_irq_next_edge(bank, hw);
-
- generic_handle_irq_desc(child_desc);
- }
-}
-
-static int tz1090_gpio_bank_probe(struct tz1090_gpio_bank_info *info)
-{
- struct device_node *np = info->node;
- struct device *dev = info->priv->dev;
- struct tz1090_gpio_bank *bank;
- struct irq_chip_generic *gc;
- int err;
-
- bank = devm_kzalloc(dev, sizeof(*bank), GFP_KERNEL);
- if (!bank) {
- dev_err(dev, "unable to allocate driver data\n");
- return -ENOMEM;
- }
-
- /* Offset the main registers to the first register in this bank */
- bank->reg = info->priv->reg + info->index * 4;
-
- /* Set up GPIO chip */
- snprintf(bank->label, sizeof(bank->label), "tz1090-gpio-%u",
- info->index);
- bank->chip.label = bank->label;
- bank->chip.parent = dev;
- bank->chip.direction_input = tz1090_gpio_direction_input;
- bank->chip.direction_output = tz1090_gpio_direction_output;
- bank->chip.get = tz1090_gpio_get;
- bank->chip.set = tz1090_gpio_set;
- bank->chip.free = tz1090_gpio_free;
- bank->chip.request = tz1090_gpio_request;
- bank->chip.to_irq = tz1090_gpio_to_irq;
- bank->chip.of_node = np;
-
- /* GPIO numbering from 0 */
- bank->chip.base = info->index * 30;
- bank->chip.ngpio = 30;
-
- /* Add the GPIO bank */
- gpiochip_add_data(&bank->chip, bank);
-
- /* Get the GPIO bank IRQ if provided */
- bank->irq = irq_of_parse_and_map(np, 0);
-
- /* The interrupt is optional (it may be used by another core on chip) */
- if (!bank->irq) {
- dev_info(dev, "IRQ not provided for bank %u, IRQs disabled\n",
- info->index);
- return 0;
- }
-
- dev_info(dev, "Setting up IRQs for GPIO bank %u\n",
- info->index);
-
- /*
- * Initialise all interrupts to disabled so we don't get
- * spurious ones on a dirty boot and hit the BUG_ON in the
- * handler.
- */
- tz1090_gpio_write(bank, REG_GPIO_IRQ_EN, 0);
-
- /* Add a virtual IRQ for each GPIO */
- bank->domain = irq_domain_add_linear(np,
- bank->chip.ngpio,
- &irq_generic_chip_ops,
- bank);
-
- /* Set up a generic irq chip with 2 chip types (level and edge) */
- err = irq_alloc_domain_generic_chips(bank->domain, bank->chip.ngpio, 2,
- bank->label, handle_bad_irq, 0, 0,
- IRQ_GC_INIT_NESTED_LOCK);
- if (err) {
- dev_info(dev,
- "irq_alloc_domain_generic_chips failed for bank %u, IRQs disabled\n",
- info->index);
- irq_domain_remove(bank->domain);
- return 0;
- }
-
- gc = irq_get_domain_generic_chip(bank->domain, 0);
- gc->reg_base = bank->reg;
-
- /* level chip type */
- gc->chip_types[0].type = IRQ_TYPE_LEVEL_MASK;
- gc->chip_types[0].handler = handle_level_irq;
- gc->chip_types[0].regs.ack = REG_GPIO_IRQ_STS;
- gc->chip_types[0].regs.mask = REG_GPIO_IRQ_EN;
- gc->chip_types[0].chip.irq_startup = gpio_startup_irq;
- gc->chip_types[0].chip.irq_ack = irq_gc_ack_clr_bit;
- gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
- gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
- gc->chip_types[0].chip.irq_set_type = gpio_set_irq_type;
- gc->chip_types[0].chip.irq_set_wake = gpio_set_irq_wake;
- gc->chip_types[0].chip.flags = IRQCHIP_MASK_ON_SUSPEND;
-
- /* edge chip type */
- gc->chip_types[1].type = IRQ_TYPE_EDGE_BOTH;
- gc->chip_types[1].handler = handle_edge_irq;
- gc->chip_types[1].regs.ack = REG_GPIO_IRQ_STS;
- gc->chip_types[1].regs.mask = REG_GPIO_IRQ_EN;
- gc->chip_types[1].chip.irq_startup = gpio_startup_irq;
- gc->chip_types[1].chip.irq_ack = irq_gc_ack_clr_bit;
- gc->chip_types[1].chip.irq_mask = irq_gc_mask_clr_bit;
- gc->chip_types[1].chip.irq_unmask = irq_gc_mask_set_bit;
- gc->chip_types[1].chip.irq_set_type = gpio_set_irq_type;
- gc->chip_types[1].chip.irq_set_wake = gpio_set_irq_wake;
- gc->chip_types[1].chip.flags = IRQCHIP_MASK_ON_SUSPEND;
-
- /* Setup chained handler for this GPIO bank */
- irq_set_chained_handler_and_data(bank->irq, tz1090_gpio_irq_handler,
- bank);
-
- return 0;
-}
-
-static void tz1090_gpio_register_banks(struct tz1090_gpio *priv)
-{
- struct device_node *np = priv->dev->of_node;
- struct device_node *node;
-
- for_each_available_child_of_node(np, node) {
- struct tz1090_gpio_bank_info info;
- u32 addr;
- int ret;
-
- ret = of_property_read_u32(node, "reg", &addr);
- if (ret) {
- dev_err(priv->dev, "invalid reg on %pOF\n", node);
- continue;
- }
- if (addr >= 3) {
- dev_err(priv->dev, "index %u in %pOF out of range\n",
- addr, node);
- continue;
- }
-
- info.index = addr;
- info.node = of_node_get(node);
- info.priv = priv;
-
- ret = tz1090_gpio_bank_probe(&info);
- if (ret) {
- dev_err(priv->dev, "failure registering %pOF\n", node);
- of_node_put(node);
- continue;
- }
- }
-}
-
-static int tz1090_gpio_probe(struct platform_device *pdev)
-{
- struct device_node *np = pdev->dev.of_node;
- struct resource *res_regs;
- struct tz1090_gpio priv;
-
- if (!np) {
- dev_err(&pdev->dev, "must be instantiated via devicetree\n");
- return -ENOENT;
- }
-
- res_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res_regs) {
- dev_err(&pdev->dev, "cannot find registers resource\n");
- return -ENOENT;
- }
-
- priv.dev = &pdev->dev;
-
- /* Ioremap the registers */
- priv.reg = devm_ioremap(&pdev->dev, res_regs->start,
- resource_size(res_regs));
- if (!priv.reg) {
- dev_err(&pdev->dev, "unable to ioremap registers\n");
- return -ENOMEM;
- }
-
- /* Look for banks */
- tz1090_gpio_register_banks(&priv);
-
- return 0;
-}
-
-static struct of_device_id tz1090_gpio_of_match[] = {
- { .compatible = "img,tz1090-gpio" },
- { },
-};
-
-static struct platform_driver tz1090_gpio_driver = {
- .driver = {
- .name = "tz1090-gpio",
- .of_match_table = tz1090_gpio_of_match,
- },
- .probe = tz1090_gpio_probe,
-};
-
-static int __init tz1090_gpio_init(void)
-{
- return platform_driver_register(&tz1090_gpio_driver);
-}
-subsys_initcall(tz1090_gpio_init);
diff --git a/drivers/gpio/gpio-wm831x.c b/drivers/gpio/gpio-wm831x.c
index 938bbe3f831c..324813e8304e 100644
--- a/drivers/gpio/gpio-wm831x.c
+++ b/drivers/gpio/gpio-wm831x.c
@@ -182,7 +182,7 @@ static void wm831x_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
dev_err(wm831x->dev,
"GPIO control %d read failed: %d\n",
gpio, reg);
- seq_printf(s, "\n");
+ seq_putc(s, '\n');
continue;
}
diff --git a/drivers/gpio/gpio-ws16c48.c b/drivers/gpio/gpio-ws16c48.c
index 746648244bf3..c7028eb0b8e1 100644
--- a/drivers/gpio/gpio-ws16c48.c
+++ b/drivers/gpio/gpio-ws16c48.c
@@ -11,6 +11,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
+#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/errno.h>
@@ -129,6 +130,51 @@ static int ws16c48_gpio_get(struct gpio_chip *chip, unsigned offset)
return !!(port_state & mask);
}
+static int ws16c48_gpio_get_multiple(struct gpio_chip *chip,
+ unsigned long *mask, unsigned long *bits)
+{
+ struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
+ const unsigned int gpio_reg_size = 8;
+ size_t i;
+ const size_t num_ports = chip->ngpio / gpio_reg_size;
+ unsigned int bits_offset;
+ size_t word_index;
+ unsigned int word_offset;
+ unsigned long word_mask;
+ const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0);
+ unsigned long port_state;
+
+ /* clear bits array to a clean slate */
+ bitmap_zero(bits, chip->ngpio);
+
+ /* get bits are evaluated a gpio port register at a time */
+ for (i = 0; i < num_ports; i++) {
+ /* gpio offset in bits array */
+ bits_offset = i * gpio_reg_size;
+
+ /* word index for bits array */
+ word_index = BIT_WORD(bits_offset);
+
+ /* gpio offset within current word of bits array */
+ word_offset = bits_offset % BITS_PER_LONG;
+
+ /* mask of get bits for current gpio within current word */
+ word_mask = mask[word_index] & (port_mask << word_offset);
+ if (!word_mask) {
+ /* no get bits in this port so skip to next one */
+ continue;
+ }
+
+ /* read bits from current gpio port */
+ port_state = inb(ws16c48gpio->base + i);
+
+ /* store acquired bits at respective bits array offset */
+ bits[word_index] |= port_state << word_offset;
+ }
+
+ return 0;
+}
+
static void ws16c48_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
@@ -383,6 +429,7 @@ static int ws16c48_probe(struct device *dev, unsigned int id)
ws16c48gpio->chip.direction_input = ws16c48_gpio_direction_input;
ws16c48gpio->chip.direction_output = ws16c48_gpio_direction_output;
ws16c48gpio->chip.get = ws16c48_gpio_get;
+ ws16c48gpio->chip.get_multiple = ws16c48_gpio_get_multiple;
ws16c48gpio->chip.set = ws16c48_gpio_set;
ws16c48gpio->chip.set_multiple = ws16c48_gpio_set_multiple;
ws16c48gpio->base = base[id];
diff --git a/drivers/gpio/gpio-xra1403.c b/drivers/gpio/gpio-xra1403.c
index 0230e4b7a2fb..8d4c8e99b251 100644
--- a/drivers/gpio/gpio-xra1403.c
+++ b/drivers/gpio/gpio-xra1403.c
@@ -126,11 +126,16 @@ static void xra1403_dbg_show(struct seq_file *s, struct gpio_chip *chip)
{
int reg;
struct xra1403 *xra = gpiochip_get_data(chip);
- int value[xra1403_regmap_cfg.max_register];
+ int *value;
int i;
unsigned int gcr;
unsigned int gsr;
+ value = kmalloc_array(xra1403_regmap_cfg.max_register, sizeof(*value),
+ GFP_KERNEL);
+ if (!value)
+ return;
+
seq_puts(s, "xra reg:");
for (reg = 0; reg <= xra1403_regmap_cfg.max_register; reg++)
seq_printf(s, " %2.2x", reg);
@@ -154,6 +159,7 @@ static void xra1403_dbg_show(struct seq_file *s, struct gpio_chip *chip)
(gcr & BIT(i)) ? "in" : "out",
(gsr & BIT(i)) ? "hi" : "lo");
}
+ kfree(value);
}
#else
#define xra1403_dbg_show NULL
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index 84e5a9df2344..586d15137c03 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -111,8 +111,8 @@ struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
struct gpio_desc *desc;
int ret;
- ret = of_parse_phandle_with_args(np, propname, "#gpio-cells", index,
- &gpiospec);
+ ret = of_parse_phandle_with_args_map(np, propname, "gpio", index,
+ &gpiospec);
if (ret) {
pr_debug("%s: can't parse '%s' property of node '%pOF[%d]'\n",
__func__, propname, np, index);
@@ -511,6 +511,28 @@ void of_mm_gpiochip_remove(struct of_mm_gpio_chip *mm_gc)
}
EXPORT_SYMBOL(of_mm_gpiochip_remove);
+static void of_gpiochip_init_valid_mask(struct gpio_chip *chip)
+{
+ int len, i;
+ u32 start, count;
+ struct device_node *np = chip->of_node;
+
+ len = of_property_count_u32_elems(np, "gpio-reserved-ranges");
+ if (len < 0 || len % 2 != 0)
+ return;
+
+ for (i = 0; i < len; i += 2) {
+ of_property_read_u32_index(np, "gpio-reserved-ranges",
+ i, &start);
+ of_property_read_u32_index(np, "gpio-reserved-ranges",
+ i + 1, &count);
+ if (start >= chip->ngpio || start + count >= chip->ngpio)
+ continue;
+
+ bitmap_clear(chip->valid_mask, start, count);
+ }
+};
+
#ifdef CONFIG_PINCTRL
static int of_gpiochip_add_pin_range(struct gpio_chip *chip)
{
@@ -615,6 +637,8 @@ int of_gpiochip_add(struct gpio_chip *chip)
if (chip->of_gpio_n_cells > MAX_PHANDLE_ARGS)
return -EINVAL;
+ of_gpiochip_init_valid_mask(chip);
+
status = of_gpiochip_add_pin_range(chip);
if (status)
return status;
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index d66de67ef307..43aeb07343ec 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -337,6 +337,57 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc)
return 0;
}
+static unsigned long *gpiochip_allocate_mask(struct gpio_chip *chip)
+{
+ unsigned long *p;
+
+ p = kmalloc_array(BITS_TO_LONGS(chip->ngpio), sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return NULL;
+
+ /* Assume by default all GPIOs are valid */
+ bitmap_fill(p, chip->ngpio);
+
+ return p;
+}
+
+static int gpiochip_init_valid_mask(struct gpio_chip *gpiochip)
+{
+#ifdef CONFIG_OF_GPIO
+ int size;
+ struct device_node *np = gpiochip->of_node;
+
+ size = of_property_count_u32_elems(np, "gpio-reserved-ranges");
+ if (size > 0 && size % 2 == 0)
+ gpiochip->need_valid_mask = true;
+#endif
+
+ if (!gpiochip->need_valid_mask)
+ return 0;
+
+ gpiochip->valid_mask = gpiochip_allocate_mask(gpiochip);
+ if (!gpiochip->valid_mask)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void gpiochip_free_valid_mask(struct gpio_chip *gpiochip)
+{
+ kfree(gpiochip->valid_mask);
+ gpiochip->valid_mask = NULL;
+}
+
+bool gpiochip_line_is_valid(const struct gpio_chip *gpiochip,
+ unsigned int offset)
+{
+ /* No mask means all valid */
+ if (likely(!gpiochip->valid_mask))
+ return true;
+ return test_bit(offset, gpiochip->valid_mask);
+}
+EXPORT_SYMBOL_GPL(gpiochip_line_is_valid);
+
/*
* GPIO line handle management
*/
@@ -1261,6 +1312,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
if (status)
goto err_remove_from_list;
+ status = gpiochip_init_valid_mask(chip);
+ if (status)
+ goto err_remove_irqchip_mask;
+
status = gpiochip_add_irqchip(chip, lock_key, request_key);
if (status)
goto err_remove_chip;
@@ -1290,6 +1345,8 @@ err_remove_chip:
acpi_gpiochip_remove(chip);
gpiochip_free_hogs(chip);
of_gpiochip_remove(chip);
+ gpiochip_free_valid_mask(chip);
+err_remove_irqchip_mask:
gpiochip_irqchip_free_valid_mask(chip);
err_remove_from_list:
spin_lock_irqsave(&gpio_lock, flags);
@@ -1346,6 +1403,7 @@ void gpiochip_remove(struct gpio_chip *chip)
acpi_gpiochip_remove(chip);
gpiochip_remove_pin_ranges(chip);
of_gpiochip_remove(chip);
+ gpiochip_free_valid_mask(chip);
/*
* We accept no more calls into the driver from this point, so
* NULL the driver data pointer
@@ -1506,14 +1564,10 @@ static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip)
if (!gpiochip->irq.need_valid_mask)
return 0;
- gpiochip->irq.valid_mask = kcalloc(BITS_TO_LONGS(gpiochip->ngpio),
- sizeof(long), GFP_KERNEL);
+ gpiochip->irq.valid_mask = gpiochip_allocate_mask(gpiochip);
if (!gpiochip->irq.valid_mask)
return -ENOMEM;
- /* Assume by default all GPIOs are valid */
- bitmap_fill(gpiochip->irq.valid_mask, gpiochip->ngpio);
-
return 0;
}
@@ -1526,6 +1580,8 @@ static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gpiochip)
bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gpiochip,
unsigned int offset)
{
+ if (!gpiochip_line_is_valid(gpiochip, offset))
+ return false;
/* No mask means all valid */
if (likely(!gpiochip->irq.valid_mask))
return true;
@@ -3689,7 +3745,7 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
}
if (IS_ERR(desc)) {
- dev_dbg(dev, "lookup for GPIO %s failed\n", con_id);
+ dev_dbg(dev, "No GPIO consumer %s found\n", con_id);
return desc;
}
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index b17ec6795c81..ad456b6f9d8b 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -13,13 +13,13 @@
#define GPIOLIB_H
#include <linux/gpio/driver.h>
+#include <linux/gpio/consumer.h> /* for enum gpiod_flags */
#include <linux/err.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/cdev.h>
enum of_gpio_flags;
-enum gpiod_flags;
enum gpio_lookup_flags;
struct acpi_device;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index f44a83ab2bf4..c8b605f3dc05 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -890,6 +890,7 @@ struct amdgpu_gfx_funcs {
void (*read_wave_data)(struct amdgpu_device *adev, uint32_t simd, uint32_t wave, uint32_t *dst, int *no_fields);
void (*read_wave_vgprs)(struct amdgpu_device *adev, uint32_t simd, uint32_t wave, uint32_t thread, uint32_t start, uint32_t size, uint32_t *dst);
void (*read_wave_sgprs)(struct amdgpu_device *adev, uint32_t simd, uint32_t wave, uint32_t start, uint32_t size, uint32_t *dst);
+ void (*select_me_pipe_q)(struct amdgpu_device *adev, u32 me, u32 pipe, u32 queue);
};
struct amdgpu_ngg_buf {
@@ -1378,6 +1379,7 @@ enum amd_hw_ip_block_type {
ATHUB_HWIP,
NBIO_HWIP,
MP0_HWIP,
+ MP1_HWIP,
UVD_HWIP,
VCN_HWIP = UVD_HWIP,
VCE_HWIP,
@@ -1387,6 +1389,7 @@ enum amd_hw_ip_block_type {
SMUIO_HWIP,
PWR_HWIP,
NBIF_HWIP,
+ THM_HWIP,
MAX_HWIP
};
@@ -1812,6 +1815,7 @@ amdgpu_get_sdma_instance(struct amdgpu_ring *ring)
#define amdgpu_gfx_select_se_sh(adev, se, sh, instance) (adev)->gfx.funcs->select_se_sh((adev), (se), (sh), (instance))
#define amdgpu_gds_switch(adev, r, v, d, w, a) (adev)->gds.funcs->patch_gds_switch((r), (v), (d), (w), (a))
#define amdgpu_psp_check_fw_loading_status(adev, i) (adev)->firmware.funcs->check_fw_loading_status((adev), (i))
+#define amdgpu_gfx_select_me_pipe_q(adev, me, pipe, q) (adev)->gfx.funcs->select_me_pipe_q((adev), (me), (pipe), (q))
/* Common functions */
int amdgpu_device_gpu_recover(struct amdgpu_device *adev,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c
index 369beb5041a2..448d69fe3756 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c
@@ -64,16 +64,21 @@ int amdgpu_debugfs_add_files(struct amdgpu_device *adev,
#if defined(CONFIG_DEBUG_FS)
-static ssize_t amdgpu_debugfs_regs_read(struct file *f, char __user *buf,
- size_t size, loff_t *pos)
+
+static int amdgpu_debugfs_process_reg_op(bool read, struct file *f,
+ char __user *buf, size_t size, loff_t *pos)
{
struct amdgpu_device *adev = file_inode(f)->i_private;
ssize_t result = 0;
int r;
- bool pm_pg_lock, use_bank;
- unsigned instance_bank, sh_bank, se_bank;
+ bool pm_pg_lock, use_bank, use_ring;
+ unsigned instance_bank, sh_bank, se_bank, me, pipe, queue;
- if (size & 0x3 || *pos & 0x3)
+ pm_pg_lock = use_bank = use_ring = false;
+ instance_bank = sh_bank = se_bank = me = pipe = queue = 0;
+
+ if (size & 0x3 || *pos & 0x3 ||
+ ((*pos & (1ULL << 62)) && (*pos & (1ULL << 61))))
return -EINVAL;
/* are we reading registers for which a PG lock is necessary? */
@@ -91,8 +96,15 @@ static ssize_t amdgpu_debugfs_regs_read(struct file *f, char __user *buf,
if (instance_bank == 0x3FF)
instance_bank = 0xFFFFFFFF;
use_bank = 1;
+ } else if (*pos & (1ULL << 61)) {
+
+ me = (*pos & GENMASK_ULL(33, 24)) >> 24;
+ pipe = (*pos & GENMASK_ULL(43, 34)) >> 34;
+ queue = (*pos & GENMASK_ULL(53, 44)) >> 44;
+
+ use_ring = 1;
} else {
- use_bank = 0;
+ use_bank = use_ring = 0;
}
*pos &= (1UL << 22) - 1;
@@ -104,6 +116,9 @@ static ssize_t amdgpu_debugfs_regs_read(struct file *f, char __user *buf,
mutex_lock(&adev->grbm_idx_mutex);
amdgpu_gfx_select_se_sh(adev, se_bank,
sh_bank, instance_bank);
+ } else if (use_ring) {
+ mutex_lock(&adev->srbm_mutex);
+ amdgpu_gfx_select_me_pipe_q(adev, me, pipe, queue);
}
if (pm_pg_lock)
@@ -115,8 +130,14 @@ static ssize_t amdgpu_debugfs_regs_read(struct file *f, char __user *buf,
if (*pos > adev->rmmio_size)
goto end;
- value = RREG32(*pos >> 2);
- r = put_user(value, (uint32_t *)buf);
+ if (read) {
+ value = RREG32(*pos >> 2);
+ r = put_user(value, (uint32_t *)buf);
+ } else {
+ r = get_user(value, (uint32_t *)buf);
+ if (!r)
+ WREG32(*pos >> 2, value);
+ }
if (r) {
result = r;
goto end;
@@ -132,6 +153,9 @@ end:
if (use_bank) {
amdgpu_gfx_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff);
mutex_unlock(&adev->grbm_idx_mutex);
+ } else if (use_ring) {
+ amdgpu_gfx_select_me_pipe_q(adev, 0, 0, 0);
+ mutex_unlock(&adev->srbm_mutex);
}
if (pm_pg_lock)
@@ -140,78 +164,17 @@ end:
return result;
}
+
+static ssize_t amdgpu_debugfs_regs_read(struct file *f, char __user *buf,
+ size_t size, loff_t *pos)
+{
+ return amdgpu_debugfs_process_reg_op(true, f, buf, size, pos);
+}
+
static ssize_t amdgpu_debugfs_regs_write(struct file *f, const char __user *buf,
size_t size, loff_t *pos)
{
- struct amdgpu_device *adev = file_inode(f)->i_private;
- ssize_t result = 0;
- int r;
- bool pm_pg_lock, use_bank;
- unsigned instance_bank, sh_bank, se_bank;
-
- if (size & 0x3 || *pos & 0x3)
- return -EINVAL;
-
- /* are we reading registers for which a PG lock is necessary? */
- pm_pg_lock = (*pos >> 23) & 1;
-
- if (*pos & (1ULL << 62)) {
- se_bank = (*pos & GENMASK_ULL(33, 24)) >> 24;
- sh_bank = (*pos & GENMASK_ULL(43, 34)) >> 34;
- instance_bank = (*pos & GENMASK_ULL(53, 44)) >> 44;
-
- if (se_bank == 0x3FF)
- se_bank = 0xFFFFFFFF;
- if (sh_bank == 0x3FF)
- sh_bank = 0xFFFFFFFF;
- if (instance_bank == 0x3FF)
- instance_bank = 0xFFFFFFFF;
- use_bank = 1;
- } else {
- use_bank = 0;
- }
-
- *pos &= (1UL << 22) - 1;
-
- if (use_bank) {
- if ((sh_bank != 0xFFFFFFFF && sh_bank >= adev->gfx.config.max_sh_per_se) ||
- (se_bank != 0xFFFFFFFF && se_bank >= adev->gfx.config.max_shader_engines))
- return -EINVAL;
- mutex_lock(&adev->grbm_idx_mutex);
- amdgpu_gfx_select_se_sh(adev, se_bank,
- sh_bank, instance_bank);
- }
-
- if (pm_pg_lock)
- mutex_lock(&adev->pm.mutex);
-
- while (size) {
- uint32_t value;
-
- if (*pos > adev->rmmio_size)
- return result;
-
- r = get_user(value, (uint32_t *)buf);
- if (r)
- return r;
-
- WREG32(*pos >> 2, value);
-
- result += 4;
- buf += 4;
- *pos += 4;
- size -= 4;
- }
-
- if (use_bank) {
- amdgpu_gfx_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff);
- mutex_unlock(&adev->grbm_idx_mutex);
- }
-
- if (pm_pg_lock)
- mutex_unlock(&adev->pm.mutex);
-
- return result;
+ return amdgpu_debugfs_process_reg_op(false, f, (char __user *)buf, size, pos);
}
static ssize_t amdgpu_debugfs_regs_pcie_read(struct file *f, char __user *buf,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
index 7379aa5a6849..0b19482b36b8 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
@@ -922,6 +922,11 @@ static int __init amdgpu_init(void)
{
int r;
+ if (vgacon_text_force()) {
+ DRM_ERROR("VGACON disables amdgpu kernel modesetting.\n");
+ return -EINVAL;
+ }
+
r = amdgpu_sync_init();
if (r)
goto error_sync;
@@ -930,10 +935,6 @@ static int __init amdgpu_init(void)
if (r)
goto error_fence;
- if (vgacon_text_force()) {
- DRM_ERROR("VGACON disables amdgpu kernel modesetting.\n");
- return -EINVAL;
- }
DRM_INFO("amdgpu kernel modesetting enabled.\n");
driver = &kms_driver;
pdriver = &amdgpu_kms_pci_driver;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c
index 455a81e4c246..97449e06a242 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c
@@ -410,6 +410,7 @@ int amdgpu_fence_driver_start_ring(struct amdgpu_ring *ring,
int amdgpu_fence_driver_init_ring(struct amdgpu_ring *ring,
unsigned num_hw_submission)
{
+ long timeout;
int r;
/* Check that num_hw_submission is a power of two */
@@ -433,11 +434,16 @@ int amdgpu_fence_driver_init_ring(struct amdgpu_ring *ring,
/* No need to setup the GPU scheduler for KIQ ring */
if (ring->funcs->type != AMDGPU_RING_TYPE_KIQ) {
+ /* for non-sriov case, no timeout enforce on compute ring */
+ if ((ring->funcs->type == AMDGPU_RING_TYPE_COMPUTE)
+ && !amdgpu_sriov_vf(ring->adev))
+ timeout = MAX_SCHEDULE_TIMEOUT;
+ else
+ timeout = msecs_to_jiffies(amdgpu_lockup_timeout);
+
r = drm_sched_init(&ring->sched, &amdgpu_sched_ops,
num_hw_submission, amdgpu_job_hang_limit,
- (ring->funcs->type == AMDGPU_RING_TYPE_COMPUTE) ?
- MAX_SCHEDULE_TIMEOUT : msecs_to_jiffies(amdgpu_lockup_timeout),
- ring->name);
+ timeout, ring->name);
if (r) {
DRM_ERROR("Failed to create scheduler on ring %s.\n",
ring->name);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
index 28c2706e48d7..46b9ea4e6103 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
@@ -56,11 +56,23 @@ int amdgpu_gem_object_create(struct amdgpu_device *adev, unsigned long size,
alignment = PAGE_SIZE;
}
+retry:
r = amdgpu_bo_create(adev, size, alignment, initial_domain,
flags, type, resv, &bo);
if (r) {
- DRM_DEBUG("Failed to allocate GEM object (%ld, %d, %u, %d)\n",
- size, initial_domain, alignment, r);
+ if (r != -ERESTARTSYS) {
+ if (flags & AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED) {
+ flags &= ~AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED;
+ goto retry;
+ }
+
+ if (initial_domain == AMDGPU_GEM_DOMAIN_VRAM) {
+ initial_domain |= AMDGPU_GEM_DOMAIN_GTT;
+ goto retry;
+ }
+ DRM_DEBUG("Failed to allocate GEM object (%ld, %d, %u, %d)\n",
+ size, initial_domain, alignment, r);
+ }
return r;
}
*obj = &bo->gem_base;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
index fac4b6067efd..6d08cde8443c 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
@@ -356,7 +356,6 @@ static int amdgpu_bo_do_create(struct amdgpu_device *adev, unsigned long size,
struct amdgpu_bo *bo;
unsigned long page_align;
size_t acc_size;
- u32 domains;
int r;
page_align = roundup(byte_align, PAGE_SIZE) >> PAGE_SHIFT;
@@ -418,23 +417,12 @@ static int amdgpu_bo_do_create(struct amdgpu_device *adev, unsigned long size,
#endif
bo->tbo.bdev = &adev->mman.bdev;
- domains = bo->preferred_domains;
-retry:
- amdgpu_ttm_placement_from_domain(bo, domains);
+ amdgpu_ttm_placement_from_domain(bo, domain);
+
r = ttm_bo_init_reserved(&adev->mman.bdev, &bo->tbo, size, type,
&bo->placement, page_align, &ctx, acc_size,
NULL, resv, &amdgpu_ttm_bo_destroy);
-
- if (unlikely(r && r != -ERESTARTSYS)) {
- if (bo->flags & AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED) {
- bo->flags &= ~AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED;
- goto retry;
- } else if (domains != bo->preferred_domains) {
- domains = bo->allowed_domains;
- goto retry;
- }
- }
- if (unlikely(r))
+ if (unlikely(r != 0))
return r;
if (adev->gmc.visible_vram_size < adev->gmc.real_vram_size &&
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c
index 19e71f4a8ac2..c7d43e064fc7 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c
@@ -505,6 +505,9 @@ failed:
int psp_gpu_reset(struct amdgpu_device *adev)
{
+ if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP)
+ return 0;
+
return psp_mode1_reset(&adev->psp);
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.c
index dd6f98921918..5916cc25e28b 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.c
@@ -277,7 +277,7 @@ amdgpu_ucode_get_load_type(struct amdgpu_device *adev, int load_type)
else
return AMDGPU_FW_LOAD_PSP;
default:
- DRM_ERROR("Unknow firmware load type\n");
+ DRM_ERROR("Unknown firmware load type\n");
}
return AMDGPU_FW_LOAD_DIRECT;
diff --git a/drivers/gpu/drm/amd/amdgpu/cik_sdma.c b/drivers/gpu/drm/amd/amdgpu/cik_sdma.c
index f48ea0dad875..a7576255cc30 100644
--- a/drivers/gpu/drm/amd/amdgpu/cik_sdma.c
+++ b/drivers/gpu/drm/amd/amdgpu/cik_sdma.c
@@ -859,7 +859,7 @@ static void cik_sdma_ring_emit_pipeline_sync(struct amdgpu_ring *ring)
amdgpu_ring_write(ring, addr & 0xfffffffc);
amdgpu_ring_write(ring, upper_32_bits(addr) & 0xffffffff);
amdgpu_ring_write(ring, seq); /* reference */
- amdgpu_ring_write(ring, 0xfffffff); /* mask */
+ amdgpu_ring_write(ring, 0xffffffff); /* mask */
amdgpu_ring_write(ring, (0xfff << 16) | 4); /* retry count, poll interval */
}
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c
index 0fff5b8cd318..cd6bf291a853 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c
@@ -3061,11 +3061,18 @@ static void gfx_v6_0_read_wave_sgprs(struct amdgpu_device *adev, uint32_t simd,
start + SQIND_WAVE_SGPRS_OFFSET, size, dst);
}
+static void gfx_v6_0_select_me_pipe_q(struct amdgpu_device *adev,
+ u32 me, u32 pipe, u32 q)
+{
+ DRM_INFO("Not implemented\n");
+}
+
static const struct amdgpu_gfx_funcs gfx_v6_0_gfx_funcs = {
.get_gpu_clock_counter = &gfx_v6_0_get_gpu_clock_counter,
.select_se_sh = &gfx_v6_0_select_se_sh,
.read_wave_data = &gfx_v6_0_read_wave_data,
.read_wave_sgprs = &gfx_v6_0_read_wave_sgprs,
+ .select_me_pipe_q = &gfx_v6_0_select_me_pipe_q
};
static int gfx_v6_0_early_init(void *handle)
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c
index e13d9d83767b..42b6144c1fd5 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c
@@ -4270,11 +4270,18 @@ static void gfx_v7_0_read_wave_sgprs(struct amdgpu_device *adev, uint32_t simd,
start + SQIND_WAVE_SGPRS_OFFSET, size, dst);
}
+static void gfx_v7_0_select_me_pipe_q(struct amdgpu_device *adev,
+ u32 me, u32 pipe, u32 q)
+{
+ cik_srbm_select(adev, me, pipe, q, 0);
+}
+
static const struct amdgpu_gfx_funcs gfx_v7_0_gfx_funcs = {
.get_gpu_clock_counter = &gfx_v7_0_get_gpu_clock_counter,
.select_se_sh = &gfx_v7_0_select_se_sh,
.read_wave_data = &gfx_v7_0_read_wave_data,
.read_wave_sgprs = &gfx_v7_0_read_wave_sgprs,
+ .select_me_pipe_q = &gfx_v7_0_select_me_pipe_q
};
static const struct amdgpu_rlc_funcs gfx_v7_0_rlc_funcs = {
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c
index 27943e57681c..e14263fca1c9 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c
@@ -1459,10 +1459,11 @@ static const u32 sgpr_init_compute_shader[] =
static const u32 vgpr_init_regs[] =
{
mmCOMPUTE_STATIC_THREAD_MGMT_SE0, 0xffffffff,
- mmCOMPUTE_RESOURCE_LIMITS, 0,
+ mmCOMPUTE_RESOURCE_LIMITS, 0x1000000, /* CU_GROUP_COUNT=1 */
mmCOMPUTE_NUM_THREAD_X, 256*4,
mmCOMPUTE_NUM_THREAD_Y, 1,
mmCOMPUTE_NUM_THREAD_Z, 1,
+ mmCOMPUTE_PGM_RSRC1, 0x100004f, /* VGPRS=15 (64 logical VGPRs), SGPRS=1 (16 SGPRs), BULKY=1 */
mmCOMPUTE_PGM_RSRC2, 20,
mmCOMPUTE_USER_DATA_0, 0xedcedc00,
mmCOMPUTE_USER_DATA_1, 0xedcedc01,
@@ -1479,10 +1480,11 @@ static const u32 vgpr_init_regs[] =
static const u32 sgpr1_init_regs[] =
{
mmCOMPUTE_STATIC_THREAD_MGMT_SE0, 0x0f,
- mmCOMPUTE_RESOURCE_LIMITS, 0x1000000,
+ mmCOMPUTE_RESOURCE_LIMITS, 0x1000000, /* CU_GROUP_COUNT=1 */
mmCOMPUTE_NUM_THREAD_X, 256*5,
mmCOMPUTE_NUM_THREAD_Y, 1,
mmCOMPUTE_NUM_THREAD_Z, 1,
+ mmCOMPUTE_PGM_RSRC1, 0x240, /* SGPRS=9 (80 GPRS) */
mmCOMPUTE_PGM_RSRC2, 20,
mmCOMPUTE_USER_DATA_0, 0xedcedc00,
mmCOMPUTE_USER_DATA_1, 0xedcedc01,
@@ -1503,6 +1505,7 @@ static const u32 sgpr2_init_regs[] =
mmCOMPUTE_NUM_THREAD_X, 256*5,
mmCOMPUTE_NUM_THREAD_Y, 1,
mmCOMPUTE_NUM_THREAD_Z, 1,
+ mmCOMPUTE_PGM_RSRC1, 0x240, /* SGPRS=9 (80 GPRS) */
mmCOMPUTE_PGM_RSRC2, 20,
mmCOMPUTE_USER_DATA_0, 0xedcedc00,
mmCOMPUTE_USER_DATA_1, 0xedcedc01,
@@ -3475,6 +3478,12 @@ static void gfx_v8_0_select_se_sh(struct amdgpu_device *adev,
WREG32(mmGRBM_GFX_INDEX, data);
}
+static void gfx_v8_0_select_me_pipe_q(struct amdgpu_device *adev,
+ u32 me, u32 pipe, u32 q)
+{
+ vi_srbm_select(adev, me, pipe, q, 0);
+}
+
static u32 gfx_v8_0_get_rb_active_bitmap(struct amdgpu_device *adev)
{
u32 data, mask;
@@ -5442,6 +5451,7 @@ static const struct amdgpu_gfx_funcs gfx_v8_0_gfx_funcs = {
.select_se_sh = &gfx_v8_0_select_se_sh,
.read_wave_data = &gfx_v8_0_read_wave_data,
.read_wave_sgprs = &gfx_v8_0_read_wave_sgprs,
+ .select_me_pipe_q = &gfx_v8_0_select_me_pipe_q
};
static int gfx_v8_0_early_init(void *handle)
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c
index 1ae3de1094f9..9d39fd5b1822 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c
@@ -998,12 +998,19 @@ static void gfx_v9_0_read_wave_vgprs(struct amdgpu_device *adev, uint32_t simd,
start + SQIND_WAVE_VGPRS_OFFSET, size, dst);
}
+static void gfx_v9_0_select_me_pipe_q(struct amdgpu_device *adev,
+ u32 me, u32 pipe, u32 q)
+{
+ soc15_grbm_select(adev, me, pipe, q, 0);
+}
+
static const struct amdgpu_gfx_funcs gfx_v9_0_gfx_funcs = {
.get_gpu_clock_counter = &gfx_v9_0_get_gpu_clock_counter,
.select_se_sh = &gfx_v9_0_select_se_sh,
.read_wave_data = &gfx_v9_0_read_wave_data,
.read_wave_sgprs = &gfx_v9_0_read_wave_sgprs,
.read_wave_vgprs = &gfx_v9_0_read_wave_vgprs,
+ .select_me_pipe_q = &gfx_v9_0_select_me_pipe_q
};
static void gfx_v9_0_gpu_early_init(struct amdgpu_device *adev)
@@ -2757,6 +2764,45 @@ static int gfx_v9_0_kiq_init_register(struct amdgpu_ring *ring)
return 0;
}
+static int gfx_v9_0_kiq_fini_register(struct amdgpu_ring *ring)
+{
+ struct amdgpu_device *adev = ring->adev;
+ int j;
+
+ /* disable the queue if it's active */
+ if (RREG32_SOC15(GC, 0, mmCP_HQD_ACTIVE) & 1) {
+
+ WREG32_SOC15(GC, 0, mmCP_HQD_DEQUEUE_REQUEST, 1);
+
+ for (j = 0; j < adev->usec_timeout; j++) {
+ if (!(RREG32_SOC15(GC, 0, mmCP_HQD_ACTIVE) & 1))
+ break;
+ udelay(1);
+ }
+
+ if (j == AMDGPU_MAX_USEC_TIMEOUT) {
+ DRM_DEBUG("KIQ dequeue request failed.\n");
+
+ /* Manual disable if dequeue request times out */
+ WREG32_SOC15(GC, 0, mmCP_HQD_ACTIVE, 0);
+ }
+
+ WREG32_SOC15(GC, 0, mmCP_HQD_DEQUEUE_REQUEST,
+ 0);
+ }
+
+ WREG32_SOC15(GC, 0, mmCP_HQD_IQ_TIMER, 0);
+ WREG32_SOC15(GC, 0, mmCP_HQD_IB_CONTROL, 0);
+ WREG32_SOC15(GC, 0, mmCP_HQD_PERSISTENT_STATE, 0);
+ WREG32_SOC15(GC, 0, mmCP_HQD_PQ_DOORBELL_CONTROL, 0x40000000);
+ WREG32_SOC15(GC, 0, mmCP_HQD_PQ_DOORBELL_CONTROL, 0);
+ WREG32_SOC15(GC, 0, mmCP_HQD_PQ_RPTR, 0);
+ WREG32_SOC15(GC, 0, mmCP_HQD_PQ_WPTR_HI, 0);
+ WREG32_SOC15(GC, 0, mmCP_HQD_PQ_WPTR_LO, 0);
+
+ return 0;
+}
+
static int gfx_v9_0_kiq_init_queue(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
@@ -3010,7 +3056,6 @@ static int gfx_v9_0_kcq_disable(struct amdgpu_ring *kiq_ring,struct amdgpu_ring
return r;
}
-
static int gfx_v9_0_hw_fini(void *handle)
{
struct amdgpu_device *adev = (struct amdgpu_device *)handle;
@@ -3033,6 +3078,20 @@ static int gfx_v9_0_hw_fini(void *handle)
WREG32_FIELD15(GC, 0, CP_PQ_WPTR_POLL_CNTL, EN, 0);
return 0;
}
+
+ /* Use deinitialize sequence from CAIL when unbinding device from driver,
+ * otherwise KIQ is hanging when binding back
+ */
+ if (!adev->in_gpu_reset && !adev->gfx.in_suspend) {
+ mutex_lock(&adev->srbm_mutex);
+ soc15_grbm_select(adev, adev->gfx.kiq.ring.me,
+ adev->gfx.kiq.ring.pipe,
+ adev->gfx.kiq.ring.queue, 0);
+ gfx_v9_0_kiq_fini_register(&adev->gfx.kiq.ring);
+ soc15_grbm_select(adev, 0, 0, 0, 0);
+ mutex_unlock(&adev->srbm_mutex);
+ }
+
gfx_v9_0_cp_enable(adev, false);
gfx_v9_0_rlc_stop(adev);
diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c b/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c
index 6452101c7aab..c7190c39c4f5 100644
--- a/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c
+++ b/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c
@@ -837,7 +837,7 @@ static void sdma_v2_4_ring_emit_pipeline_sync(struct amdgpu_ring *ring)
amdgpu_ring_write(ring, addr & 0xfffffffc);
amdgpu_ring_write(ring, upper_32_bits(addr) & 0xffffffff);
amdgpu_ring_write(ring, seq); /* reference */
- amdgpu_ring_write(ring, 0xfffffff); /* mask */
+ amdgpu_ring_write(ring, 0xffffffff); /* mask */
amdgpu_ring_write(ring, SDMA_PKT_POLL_REGMEM_DW5_RETRY_COUNT(0xfff) |
SDMA_PKT_POLL_REGMEM_DW5_INTERVAL(4)); /* retry count, poll interval */
}
diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c
index ecaef084dab1..be20a387d961 100644
--- a/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c
@@ -1105,7 +1105,7 @@ static void sdma_v3_0_ring_emit_pipeline_sync(struct amdgpu_ring *ring)
amdgpu_ring_write(ring, addr & 0xfffffffc);
amdgpu_ring_write(ring, upper_32_bits(addr) & 0xffffffff);
amdgpu_ring_write(ring, seq); /* reference */
- amdgpu_ring_write(ring, 0xfffffff); /* mask */
+ amdgpu_ring_write(ring, 0xffffffff); /* mask */
amdgpu_ring_write(ring, SDMA_PKT_POLL_REGMEM_DW5_RETRY_COUNT(0xfff) |
SDMA_PKT_POLL_REGMEM_DW5_INTERVAL(4)); /* retry count, poll interval */
}
diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c
index 2a8184082cd1..399f876f9cad 100644
--- a/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c
@@ -1121,7 +1121,7 @@ static void sdma_v4_0_ring_emit_pipeline_sync(struct amdgpu_ring *ring)
amdgpu_ring_write(ring, addr & 0xfffffffc);
amdgpu_ring_write(ring, upper_32_bits(addr) & 0xffffffff);
amdgpu_ring_write(ring, seq); /* reference */
- amdgpu_ring_write(ring, 0xfffffff); /* mask */
+ amdgpu_ring_write(ring, 0xffffffff); /* mask */
amdgpu_ring_write(ring, SDMA_PKT_POLL_REGMEM_DW5_RETRY_COUNT(0xfff) |
SDMA_PKT_POLL_REGMEM_DW5_INTERVAL(4)); /* retry count, poll interval */
}
diff --git a/drivers/gpu/drm/amd/amdgpu/si.c b/drivers/gpu/drm/amd/amdgpu/si.c
index b154667a8fd9..a675ec6d2811 100644
--- a/drivers/gpu/drm/amd/amdgpu/si.c
+++ b/drivers/gpu/drm/amd/amdgpu/si.c
@@ -1252,6 +1252,71 @@ static void si_invalidate_hdp(struct amdgpu_device *adev,
}
}
+static int si_get_pcie_lanes(struct amdgpu_device *adev)
+{
+ u32 link_width_cntl;
+
+ if (adev->flags & AMD_IS_APU)
+ return 0;
+
+ link_width_cntl = RREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL);
+
+ switch ((link_width_cntl & LC_LINK_WIDTH_RD_MASK) >> LC_LINK_WIDTH_RD_SHIFT) {
+ case LC_LINK_WIDTH_X1:
+ return 1;
+ case LC_LINK_WIDTH_X2:
+ return 2;
+ case LC_LINK_WIDTH_X4:
+ return 4;
+ case LC_LINK_WIDTH_X8:
+ return 8;
+ case LC_LINK_WIDTH_X0:
+ case LC_LINK_WIDTH_X16:
+ default:
+ return 16;
+ }
+}
+
+static void si_set_pcie_lanes(struct amdgpu_device *adev, int lanes)
+{
+ u32 link_width_cntl, mask;
+
+ if (adev->flags & AMD_IS_APU)
+ return;
+
+ switch (lanes) {
+ case 0:
+ mask = LC_LINK_WIDTH_X0;
+ break;
+ case 1:
+ mask = LC_LINK_WIDTH_X1;
+ break;
+ case 2:
+ mask = LC_LINK_WIDTH_X2;
+ break;
+ case 4:
+ mask = LC_LINK_WIDTH_X4;
+ break;
+ case 8:
+ mask = LC_LINK_WIDTH_X8;
+ break;
+ case 16:
+ mask = LC_LINK_WIDTH_X16;
+ break;
+ default:
+ DRM_ERROR("invalid pcie lane request: %d\n", lanes);
+ return;
+ }
+
+ link_width_cntl = RREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL);
+ link_width_cntl &= ~LC_LINK_WIDTH_MASK;
+ link_width_cntl |= mask << LC_LINK_WIDTH_SHIFT;
+ link_width_cntl |= (LC_RECONFIG_NOW |
+ LC_RECONFIG_ARC_MISSING_ESCAPE);
+
+ WREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl);
+}
+
static const struct amdgpu_asic_funcs si_asic_funcs =
{
.read_disabled_bios = &si_read_disabled_bios,
@@ -1262,6 +1327,8 @@ static const struct amdgpu_asic_funcs si_asic_funcs =
.get_xclk = &si_get_xclk,
.set_uvd_clocks = &si_set_uvd_clocks,
.set_vce_clocks = NULL,
+ .get_pcie_lanes = &si_get_pcie_lanes,
+ .set_pcie_lanes = &si_set_pcie_lanes,
.get_config_memsize = &si_get_config_memsize,
.flush_hdp = &si_flush_hdp,
.invalidate_hdp = &si_invalidate_hdp,
diff --git a/drivers/gpu/drm/amd/amdgpu/si_dpm.c b/drivers/gpu/drm/amd/amdgpu/si_dpm.c
index 672eaffac0a5..797d505bf9ee 100644
--- a/drivers/gpu/drm/amd/amdgpu/si_dpm.c
+++ b/drivers/gpu/drm/amd/amdgpu/si_dpm.c
@@ -6372,9 +6372,9 @@ static void si_set_pcie_lane_width_in_smc(struct amdgpu_device *adev,
{
u32 lane_width;
u32 new_lane_width =
- (amdgpu_new_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT;
+ ((amdgpu_new_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT) + 1;
u32 current_lane_width =
- (amdgpu_current_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT;
+ ((amdgpu_current_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT) + 1;
if (new_lane_width != current_lane_width) {
amdgpu_set_pcie_lanes(adev, new_lane_width);
diff --git a/drivers/gpu/drm/amd/amdgpu/vce_v4_0.c b/drivers/gpu/drm/amd/amdgpu/vce_v4_0.c
index 73fd48d6c756..73fd48d6c756 100755..100644
--- a/drivers/gpu/drm/amd/amdgpu/vce_v4_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/vce_v4_0.c
diff --git a/drivers/gpu/drm/amd/amdgpu/vega10_reg_init.c b/drivers/gpu/drm/amd/amdgpu/vega10_reg_init.c
index 4c45db7f1157..45aafca7f315 100644
--- a/drivers/gpu/drm/amd/amdgpu/vega10_reg_init.c
+++ b/drivers/gpu/drm/amd/amdgpu/vega10_reg_init.c
@@ -38,6 +38,7 @@ int vega10_reg_base_init(struct amdgpu_device *adev)
adev->reg_offset[ATHUB_HWIP][i] = (uint32_t *)(&(ATHUB_BASE.instance[i]));
adev->reg_offset[NBIO_HWIP][i] = (uint32_t *)(&(NBIO_BASE.instance[i]));
adev->reg_offset[MP0_HWIP][i] = (uint32_t *)(&(MP0_BASE.instance[i]));
+ adev->reg_offset[MP1_HWIP][i] = (uint32_t *)(&(MP1_BASE.instance[i]));
adev->reg_offset[UVD_HWIP][i] = (uint32_t *)(&(UVD_BASE.instance[i]));
adev->reg_offset[VCE_HWIP][i] = (uint32_t *)(&(VCE_BASE.instance[i]));
adev->reg_offset[VCN_HWIP][i] = (uint32_t *)(&(VCN_BASE.instance[i]));
@@ -49,7 +50,7 @@ int vega10_reg_base_init(struct amdgpu_device *adev)
adev->reg_offset[SMUIO_HWIP][i] = (uint32_t *)(&(SMUIO_BASE.instance[i]));
adev->reg_offset[PWR_HWIP][i] = (uint32_t *)(&(PWR_BASE.instance[i]));
adev->reg_offset[NBIF_HWIP][i] = (uint32_t *)(&(NBIF_BASE.instance[i]));
-
+ adev->reg_offset[THM_HWIP][i] = (uint32_t *)(&(THM_BASE.instance[i]));
}
return 0;
}
diff --git a/drivers/gpu/drm/amd/amdkfd/Kconfig b/drivers/gpu/drm/amd/amdkfd/Kconfig
index ed2f06c9f346..3858820a0055 100644
--- a/drivers/gpu/drm/amd/amdkfd/Kconfig
+++ b/drivers/gpu/drm/amd/amdkfd/Kconfig
@@ -6,5 +6,6 @@ config HSA_AMD
tristate "HSA kernel driver for AMD GPU devices"
depends on DRM_AMDGPU && X86_64
imply AMD_IOMMU_V2
+ select MMU_NOTIFIER
help
Enable this if you want to use HSA features on AMD GPU devices.
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
index cd679cf1fd30..59808a39ecf4 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
@@ -749,12 +749,13 @@ static int kfd_ioctl_get_clock_counters(struct file *filep,
struct timespec64 time;
dev = kfd_device_by_id(args->gpu_id);
- if (dev == NULL)
- return -EINVAL;
-
- /* Reading GPU clock counter from KGD */
- args->gpu_clock_counter =
- dev->kfd2kgd->get_gpu_clock_counter(dev->kgd);
+ if (dev)
+ /* Reading GPU clock counter from KGD */
+ args->gpu_clock_counter =
+ dev->kfd2kgd->get_gpu_clock_counter(dev->kgd);
+ else
+ /* Node without GPU resource */
+ args->gpu_clock_counter = 0;
/* No access to rdtsc. Using raw monotonic time */
getrawmonotonic64(&time);
@@ -1147,7 +1148,7 @@ err_unlock:
return ret;
}
-bool kfd_dev_is_large_bar(struct kfd_dev *dev)
+static bool kfd_dev_is_large_bar(struct kfd_dev *dev)
{
struct kfd_local_mem_info mem_info;
@@ -1421,7 +1422,7 @@ static int kfd_ioctl_unmap_memory_from_gpu(struct file *filep,
pdd = kfd_get_process_device_data(dev, p);
if (!pdd) {
- err = PTR_ERR(pdd);
+ err = -EINVAL;
goto bind_process_to_device_failed;
}
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
index c18e048f23c6..d55d29d31da4 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
@@ -1058,13 +1058,13 @@ static int create_queue_cpsch(struct device_queue_manager *dqm, struct queue *q,
pr_warn("Can't create new usermode queue because %d queues were already created\n",
dqm->total_queue_count);
retval = -EPERM;
- goto out;
+ goto out_unlock;
}
if (q->properties.type == KFD_QUEUE_TYPE_SDMA) {
retval = allocate_sdma_queue(dqm, &q->sdma_id);
if (retval)
- goto out;
+ goto out_unlock;
q->properties.sdma_queue_id =
q->sdma_id / CIK_SDMA_QUEUES_PER_ENGINE;
q->properties.sdma_engine_id =
@@ -1075,7 +1075,7 @@ static int create_queue_cpsch(struct device_queue_manager *dqm, struct queue *q,
if (!mqd) {
retval = -ENOMEM;
- goto out;
+ goto out_deallocate_sdma_queue;
}
/*
* Eviction state logic: we only mark active queues as evicted
@@ -1093,7 +1093,7 @@ static int create_queue_cpsch(struct device_queue_manager *dqm, struct queue *q,
retval = mqd->init_mqd(mqd, &q->mqd, &q->mqd_mem_obj,
&q->gart_mqd_addr, &q->properties);
if (retval)
- goto out;
+ goto out_deallocate_sdma_queue;
list_add(&q->list, &qpd->queues_list);
qpd->queue_count++;
@@ -1114,7 +1114,13 @@ static int create_queue_cpsch(struct device_queue_manager *dqm, struct queue *q,
pr_debug("Total of %d queues are accountable so far\n",
dqm->total_queue_count);
-out:
+ mutex_unlock(&dqm->lock);
+ return retval;
+
+out_deallocate_sdma_queue:
+ if (q->properties.type == KFD_QUEUE_TYPE_SDMA)
+ deallocate_sdma_queue(dqm, q->sdma_id);
+out_unlock:
mutex_unlock(&dqm->lock);
return retval;
}
@@ -1433,8 +1439,10 @@ static int process_termination_cpsch(struct device_queue_manager *dqm,
/* Clear all user mode queues */
list_for_each_entry(q, &qpd->queues_list, list) {
- if (q->properties.type == KFD_QUEUE_TYPE_SDMA)
+ if (q->properties.type == KFD_QUEUE_TYPE_SDMA) {
dqm->sdma_queue_count--;
+ deallocate_sdma_queue(dqm, q->sdma_id);
+ }
if (q->properties.is_active)
dqm->queue_count--;
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_packet_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_packet_manager.c
index 7614375489a4..89ba4c670ec5 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_packet_manager.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_packet_manager.c
@@ -188,8 +188,7 @@ static int pm_create_map_process(struct packet_manager *pm, uint32_t *buffer,
packet->sh_mem_ape1_base = qpd->sh_mem_ape1_base;
packet->sh_mem_ape1_limit = qpd->sh_mem_ape1_limit;
- /* TODO: scratch support */
- packet->sh_hidden_private_base_vmid = 0;
+ packet->sh_hidden_private_base_vmid = qpd->sh_hidden_private_base;
packet->gds_addr_lo = lower_32_bits(qpd->gds_context_area);
packet->gds_addr_hi = upper_32_bits(qpd->gds_context_area);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index e42a28e3adc5..1dd1142246c2 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -1403,6 +1403,28 @@ static int initialize_plane(struct amdgpu_display_manager *dm,
return ret;
}
+
+static void register_backlight_device(struct amdgpu_display_manager *dm,
+ struct dc_link *link)
+{
+#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) ||\
+ defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE)
+
+ if ((link->connector_signal & (SIGNAL_TYPE_EDP | SIGNAL_TYPE_LVDS)) &&
+ link->type != dc_connection_none) {
+ /* Event if registration failed, we should continue with
+ * DM initialization because not having a backlight control
+ * is better then a black screen.
+ */
+ amdgpu_dm_register_backlight_device(dm);
+
+ if (dm->backlight_dev)
+ dm->backlight_link = link;
+ }
+#endif
+}
+
+
/* In this architecture, the association
* connector -> encoder -> crtc
* id not really requried. The crtc and connector will hold the
@@ -1456,6 +1478,7 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev)
/* loops over all connectors on the board */
for (i = 0; i < link_cnt; i++) {
+ struct dc_link *link = NULL;
if (i > AMDGPU_DM_MAX_DISPLAY_INDEX) {
DRM_ERROR(
@@ -1482,9 +1505,14 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev)
goto fail;
}
- if (dc_link_detect(dc_get_link_at_index(dm->dc, i),
- DETECT_REASON_BOOT))
+ link = dc_get_link_at_index(dm->dc, i);
+
+ if (dc_link_detect(link, DETECT_REASON_BOOT)) {
amdgpu_dm_update_connector_after_detect(aconnector);
+ register_backlight_device(dm, link);
+ }
+
+
}
/* Software is initialized. Now we can register interrupt handlers. */
@@ -2685,7 +2713,8 @@ static void amdgpu_dm_connector_destroy(struct drm_connector *connector)
#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) ||\
defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE)
- if (link->connector_signal & (SIGNAL_TYPE_EDP | SIGNAL_TYPE_LVDS)) {
+ if ((link->connector_signal & (SIGNAL_TYPE_EDP | SIGNAL_TYPE_LVDS)) &&
+ link->type != dc_connection_none) {
amdgpu_dm_register_backlight_device(dm);
if (dm->backlight_dev) {
@@ -3561,6 +3590,7 @@ create_i2c(struct ddc_service *ddc_service,
return i2c;
}
+
/* Note: this function assumes that dc_link_detect() was called for the
* dc_link which will be represented by this aconnector.
*/
@@ -3630,28 +3660,6 @@ static int amdgpu_dm_connector_init(struct amdgpu_display_manager *dm,
|| connector_type == DRM_MODE_CONNECTOR_eDP)
amdgpu_dm_initialize_dp_connector(dm, aconnector);
-#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) ||\
- defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE)
-
- /* NOTE: this currently will create backlight device even if a panel
- * is not connected to the eDP/LVDS connector.
- *
- * This is less than ideal but we don't have sink information at this
- * stage since detection happens after. We can't do detection earlier
- * since MST detection needs connectors to be created first.
- */
- if (link->connector_signal & (SIGNAL_TYPE_EDP | SIGNAL_TYPE_LVDS)) {
- /* Event if registration failed, we should continue with
- * DM initialization because not having a backlight control
- * is better then a black screen.
- */
- amdgpu_dm_register_backlight_device(dm);
-
- if (dm->backlight_dev)
- dm->backlight_link = link;
- }
-#endif
-
out_free:
if (res) {
kfree(i2c);
@@ -4549,6 +4557,7 @@ static int dm_update_crtcs_state(struct dc *dc,
struct amdgpu_dm_connector *aconnector = NULL;
struct drm_connector_state *new_con_state = NULL;
struct dm_connector_state *dm_conn_state = NULL;
+ struct drm_plane_state *new_plane_state = NULL;
new_stream = NULL;
@@ -4556,6 +4565,13 @@ static int dm_update_crtcs_state(struct dc *dc,
dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
acrtc = to_amdgpu_crtc(crtc);
+ new_plane_state = drm_atomic_get_new_plane_state(state, new_crtc_state->crtc->primary);
+
+ if (new_crtc_state->enable && new_plane_state && !new_plane_state->fb) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
aconnector = amdgpu_dm_find_first_crtc_matching_connector(state, crtc);
/* TODO This hack should go away */
@@ -4752,7 +4768,7 @@ static int dm_update_planes_state(struct dc *dc,
if (!dm_old_crtc_state->stream)
continue;
- DRM_DEBUG_DRIVER("Disabling DRM plane: %d on DRM crtc %d\n",
+ DRM_DEBUG_ATOMIC("Disabling DRM plane: %d on DRM crtc %d\n",
plane->base.id, old_plane_crtc->base.id);
if (!dc_remove_plane_from_context(
@@ -4840,33 +4856,6 @@ static int dm_update_planes_state(struct dc *dc,
return ret;
}
-static int dm_atomic_check_plane_state_fb(struct drm_atomic_state *state,
- struct drm_crtc *crtc)
-{
- struct drm_plane *plane;
- struct drm_crtc_state *crtc_state;
-
- WARN_ON(!drm_atomic_get_new_crtc_state(state, crtc));
-
- drm_for_each_plane_mask(plane, state->dev, crtc->state->plane_mask) {
- struct drm_plane_state *plane_state =
- drm_atomic_get_plane_state(state, plane);
-
- if (IS_ERR(plane_state))
- return -EDEADLK;
-
- crtc_state = drm_atomic_get_crtc_state(plane_state->state, crtc);
- if (IS_ERR(crtc_state))
- return PTR_ERR(crtc_state);
-
- if (crtc->primary == plane && crtc_state->active) {
- if (!plane_state->fb)
- return -EINVAL;
- }
- }
- return 0;
-}
-
static int amdgpu_dm_atomic_check(struct drm_device *dev,
struct drm_atomic_state *state)
{
@@ -4890,10 +4879,6 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
goto fail;
for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
- ret = dm_atomic_check_plane_state_fb(state, crtc);
- if (ret)
- goto fail;
-
if (!drm_atomic_crtc_needs_modeset(new_crtc_state) &&
!new_crtc_state->color_mgmt_changed)
continue;
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
index f6cb502c303f..25f064c01038 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
@@ -138,13 +138,6 @@ int amdgpu_dm_set_regamma_lut(struct dm_crtc_state *crtc)
lut = (struct drm_color_lut *)blob->data;
lut_size = blob->length / sizeof(struct drm_color_lut);
- if (__is_lut_linear(lut, lut_size)) {
- /* Set to bypass if lut is set to linear */
- stream->out_transfer_func->type = TF_TYPE_BYPASS;
- stream->out_transfer_func->tf = TRANSFER_FUNCTION_LINEAR;
- return 0;
- }
-
gamma = dc_create_gamma();
if (!gamma)
return -ENOMEM;
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
index 490017df371d..4be21bf54749 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
@@ -329,14 +329,15 @@ void amdgpu_dm_irq_fini(struct amdgpu_device *adev)
{
int src;
struct irq_list_head *lh;
+ unsigned long irq_table_flags;
DRM_DEBUG_KMS("DM_IRQ: releasing resources.\n");
-
for (src = 0; src < DAL_IRQ_SOURCES_NUMBER; src++) {
-
+ DM_IRQ_TABLE_LOCK(adev, irq_table_flags);
/* The handler was removed from the table,
* it means it is safe to flush all the 'work'
* (because no code can schedule a new one). */
lh = &adev->dm.irq_handler_list_low_tab[src];
+ DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags);
flush_work(&lh->work);
}
}
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
index 8291d74f26bc..ace9ad578ca0 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
@@ -161,6 +161,11 @@ dm_dp_mst_connector_destroy(struct drm_connector *connector)
struct amdgpu_dm_connector *amdgpu_dm_connector = to_amdgpu_dm_connector(connector);
struct amdgpu_encoder *amdgpu_encoder = amdgpu_dm_connector->mst_encoder;
+ if (amdgpu_dm_connector->edid) {
+ kfree(amdgpu_dm_connector->edid);
+ amdgpu_dm_connector->edid = NULL;
+ }
+
drm_encoder_cleanup(&amdgpu_encoder->base);
kfree(amdgpu_encoder);
drm_connector_cleanup(connector);
@@ -181,28 +186,22 @@ static const struct drm_connector_funcs dm_dp_mst_connector_funcs = {
void dm_dp_mst_dc_sink_create(struct drm_connector *connector)
{
struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
- struct edid *edid;
struct dc_sink *dc_sink;
struct dc_sink_init_data init_params = {
.link = aconnector->dc_link,
.sink_signal = SIGNAL_TYPE_DISPLAY_PORT_MST };
+ /* FIXME none of this is safe. we shouldn't touch aconnector here in
+ * atomic_check
+ */
+
/*
* TODO: Need to further figure out why ddc.algo is NULL while MST port exists
*/
if (!aconnector->port || !aconnector->port->aux.ddc.algo)
return;
- edid = drm_dp_mst_get_edid(connector, &aconnector->mst_port->mst_mgr, aconnector->port);
-
- if (!edid) {
- drm_mode_connector_update_edid_property(
- &aconnector->base,
- NULL);
- return;
- }
-
- aconnector->edid = edid;
+ ASSERT(aconnector->edid);
dc_sink = dc_link_add_remote_sink(
aconnector->dc_link,
@@ -215,9 +214,6 @@ void dm_dp_mst_dc_sink_create(struct drm_connector *connector)
amdgpu_dm_add_sink_to_freesync_module(
connector, aconnector->edid);
-
- drm_mode_connector_update_edid_property(
- &aconnector->base, aconnector->edid);
}
static int dm_dp_mst_get_modes(struct drm_connector *connector)
@@ -230,10 +226,6 @@ static int dm_dp_mst_get_modes(struct drm_connector *connector)
if (!aconnector->edid) {
struct edid *edid;
- struct dc_sink *dc_sink;
- struct dc_sink_init_data init_params = {
- .link = aconnector->dc_link,
- .sink_signal = SIGNAL_TYPE_DISPLAY_PORT_MST };
edid = drm_dp_mst_get_edid(connector, &aconnector->mst_port->mst_mgr, aconnector->port);
if (!edid) {
@@ -244,11 +236,17 @@ static int dm_dp_mst_get_modes(struct drm_connector *connector)
}
aconnector->edid = edid;
+ }
+ if (!aconnector->dc_sink) {
+ struct dc_sink *dc_sink;
+ struct dc_sink_init_data init_params = {
+ .link = aconnector->dc_link,
+ .sink_signal = SIGNAL_TYPE_DISPLAY_PORT_MST };
dc_sink = dc_link_add_remote_sink(
aconnector->dc_link,
- (uint8_t *)edid,
- (edid->extensions + 1) * EDID_LENGTH,
+ (uint8_t *)aconnector->edid,
+ (aconnector->edid->extensions + 1) * EDID_LENGTH,
&init_params);
dc_sink->priv = aconnector;
@@ -256,12 +254,12 @@ static int dm_dp_mst_get_modes(struct drm_connector *connector)
if (aconnector->dc_sink)
amdgpu_dm_add_sink_to_freesync_module(
- connector, edid);
-
- drm_mode_connector_update_edid_property(
- &aconnector->base, edid);
+ connector, aconnector->edid);
}
+ drm_mode_connector_update_edid_property(
+ &aconnector->base, aconnector->edid);
+
ret = drm_add_edid_modes(connector, aconnector->edid);
return ret;
@@ -424,14 +422,6 @@ static void dm_dp_destroy_mst_connector(struct drm_dp_mst_topology_mgr *mgr,
dc_sink_release(aconnector->dc_sink);
aconnector->dc_sink = NULL;
}
- if (aconnector->edid) {
- kfree(aconnector->edid);
- aconnector->edid = NULL;
- }
-
- drm_mode_connector_update_edid_property(
- &aconnector->base,
- NULL);
aconnector->mst_connected = false;
}
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c
index 63a3d468939a..9cd3566def8d 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
@@ -589,7 +589,7 @@ static void disable_dangling_plane(struct dc *dc, struct dc_state *context)
******************************************************************************/
struct dc *dc_create(const struct dc_init_data *init_params)
- {
+{
struct dc *dc = kzalloc(sizeof(*dc), GFP_KERNEL);
unsigned int full_pipe_count;
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link.c b/drivers/gpu/drm/amd/display/dc/core/dc_link.c
index eeb04471b2f5..6d1c4981a185 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_link.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_link.c
@@ -1997,6 +1997,19 @@ bool dc_link_set_backlight_level(const struct dc_link *link, uint32_t level,
return true;
}
+bool dc_link_set_abm_disable(const struct dc_link *link)
+{
+ struct dc *core_dc = link->ctx->dc;
+ struct abm *abm = core_dc->res_pool->abm;
+
+ if ((abm == NULL) || (abm->funcs->set_backlight_level == NULL))
+ return false;
+
+ abm->funcs->set_abm_immediate_disable(abm);
+
+ return true;
+}
+
bool dc_link_set_psr_enable(const struct dc_link *link, bool enable, bool wait)
{
struct dc *core_dc = link->ctx->dc;
diff --git a/drivers/gpu/drm/amd/display/dc/dc_link.h b/drivers/gpu/drm/amd/display/dc/dc_link.h
index fb4d9eafdc6e..dc34515ef01f 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_link.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_link.h
@@ -132,6 +132,8 @@ static inline struct dc_link *dc_get_link_at_index(struct dc *dc, uint32_t link_
bool dc_link_set_backlight_level(const struct dc_link *dc_link, uint32_t level,
uint32_t frame_ramp, const struct dc_stream_state *stream);
+bool dc_link_set_abm_disable(const struct dc_link *dc_link);
+
bool dc_link_set_psr_enable(const struct dc_link *dc_link, bool enable, bool wait);
bool dc_link_get_psr_state(const struct dc_link *dc_link, uint32_t *psr_state);
diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c
index 444558ca6533..162f6a6c4208 100644
--- a/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c
+++ b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c
@@ -735,6 +735,8 @@ static void dce110_stream_encoder_update_hdmi_info_packets(
if (info_frame->avi.valid) {
const uint32_t *content =
(const uint32_t *) &info_frame->avi.sb[0];
+ /*we need turn on clock before programming AFMT block*/
+ REG_UPDATE(AFMT_CNTL, AFMT_AUDIO_CLOCK_EN, 1);
REG_WRITE(AFMT_AVI_INFO0, content[0]);
diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.c
index 775d3bf0bd39..9150d2694450 100644
--- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.c
+++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.c
@@ -102,6 +102,43 @@ static uint32_t align_to_chunks_number_per_line(uint32_t pixels)
return 256 * ((pixels + 255) / 256);
}
+static void reset_lb_on_vblank(struct dc_context *ctx)
+{
+ uint32_t value, frame_count;
+ uint32_t retry = 0;
+ uint32_t status_pos =
+ dm_read_reg(ctx, mmCRTC_STATUS_POSITION);
+
+
+ /* Only if CRTC is enabled and counter is moving we wait for one frame. */
+ if (status_pos != dm_read_reg(ctx, mmCRTC_STATUS_POSITION)) {
+ /* Resetting LB on VBlank */
+ value = dm_read_reg(ctx, mmLB_SYNC_RESET_SEL);
+ set_reg_field_value(value, 3, LB_SYNC_RESET_SEL, LB_SYNC_RESET_SEL);
+ set_reg_field_value(value, 1, LB_SYNC_RESET_SEL, LB_SYNC_RESET_SEL2);
+ dm_write_reg(ctx, mmLB_SYNC_RESET_SEL, value);
+
+ frame_count = dm_read_reg(ctx, mmCRTC_STATUS_FRAME_COUNT);
+
+
+ for (retry = 100; retry > 0; retry--) {
+ if (frame_count != dm_read_reg(ctx, mmCRTC_STATUS_FRAME_COUNT))
+ break;
+ msleep(1);
+ }
+ if (!retry)
+ dm_error("Frame count did not increase for 100ms.\n");
+
+ /* Resetting LB on VBlank */
+ value = dm_read_reg(ctx, mmLB_SYNC_RESET_SEL);
+ set_reg_field_value(value, 2, LB_SYNC_RESET_SEL, LB_SYNC_RESET_SEL);
+ set_reg_field_value(value, 0, LB_SYNC_RESET_SEL, LB_SYNC_RESET_SEL2);
+ dm_write_reg(ctx, mmLB_SYNC_RESET_SEL, value);
+
+ }
+
+}
+
static void wait_for_fbc_state_changed(
struct dce110_compressor *cp110,
bool enabled)
@@ -232,19 +269,23 @@ void dce110_compressor_disable_fbc(struct compressor *compressor)
{
struct dce110_compressor *cp110 = TO_DCE110_COMPRESSOR(compressor);
- if (compressor->options.bits.FBC_SUPPORT &&
- dce110_compressor_is_fbc_enabled_in_hw(compressor, NULL)) {
- uint32_t reg_data;
- /* Turn off compression */
- reg_data = dm_read_reg(compressor->ctx, mmFBC_CNTL);
- set_reg_field_value(reg_data, 0, FBC_CNTL, FBC_GRPH_COMP_EN);
- dm_write_reg(compressor->ctx, mmFBC_CNTL, reg_data);
-
- /* Reset enum controller_id to undefined */
- compressor->attached_inst = 0;
- compressor->is_enabled = false;
-
- wait_for_fbc_state_changed(cp110, false);
+ if (compressor->options.bits.FBC_SUPPORT) {
+ if (dce110_compressor_is_fbc_enabled_in_hw(compressor, NULL)) {
+ uint32_t reg_data;
+ /* Turn off compression */
+ reg_data = dm_read_reg(compressor->ctx, mmFBC_CNTL);
+ set_reg_field_value(reg_data, 0, FBC_CNTL, FBC_GRPH_COMP_EN);
+ dm_write_reg(compressor->ctx, mmFBC_CNTL, reg_data);
+
+ /* Reset enum controller_id to undefined */
+ compressor->attached_inst = 0;
+ compressor->is_enabled = false;
+
+ wait_for_fbc_state_changed(cp110, false);
+ }
+
+ /* Sync line buffer - dce100/110 only*/
+ reset_lb_on_vblank(compressor->ctx);
}
}
diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c
index 30dd62f0f5fa..d0575999f172 100644
--- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c
+++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c
@@ -453,10 +453,13 @@ dce110_translate_regamma_to_hw_format(const struct dc_transfer_func *output_tf,
} else {
/* 10 segments
- * segment is from 2^-10 to 2^0
+ * segment is from 2^-10 to 2^1
+ * We include an extra segment for range [2^0, 2^1). This is to
+ * ensure that colors with normalized values of 1 don't miss the
+ * LUT.
*/
region_start = -10;
- region_end = 0;
+ region_end = 1;
seg_distr[0] = 4;
seg_distr[1] = 4;
@@ -468,7 +471,7 @@ dce110_translate_regamma_to_hw_format(const struct dc_transfer_func *output_tf,
seg_distr[7] = 4;
seg_distr[8] = 4;
seg_distr[9] = 4;
- seg_distr[10] = -1;
+ seg_distr[10] = 0;
seg_distr[11] = -1;
seg_distr[12] = -1;
seg_distr[13] = -1;
@@ -1016,8 +1019,10 @@ void dce110_blank_stream(struct pipe_ctx *pipe_ctx)
struct dc_stream_state *stream = pipe_ctx->stream;
struct dc_link *link = stream->sink->link;
- if (link->local_sink && link->local_sink->sink_signal == SIGNAL_TYPE_EDP)
+ if (link->local_sink && link->local_sink->sink_signal == SIGNAL_TYPE_EDP) {
link->dc->hwss.edp_backlight_control(link, false);
+ dc_link_set_abm_disable(link);
+ }
if (dc_is_dp_signal(pipe_ctx->stream->signal))
pipe_ctx->stream_res.stream_enc->funcs->dp_blank(pipe_ctx->stream_res.stream_enc);
diff --git a/drivers/gpu/drm/amd/include/atomfirmware.h b/drivers/gpu/drm/amd/include/atomfirmware.h
index 3ae3da4e7c14..0f5ad54d3fd3 100644
--- a/drivers/gpu/drm/amd/include/atomfirmware.h
+++ b/drivers/gpu/drm/amd/include/atomfirmware.h
@@ -1264,9 +1264,9 @@ struct atom_smc_dpm_info_v4_1
uint8_t ledpin2;
uint8_t padding8_4;
- uint8_t gfxclkspreadenabled;
- uint8_t gfxclkspreadpercent;
- uint16_t gfxclkspreadfreq;
+ uint8_t pllgfxclkspreadenabled;
+ uint8_t pllgfxclkspreadpercent;
+ uint16_t pllgfxclkspreadfreq;
uint8_t uclkspreadenabled;
uint8_t uclkspreadpercent;
@@ -1276,7 +1276,11 @@ struct atom_smc_dpm_info_v4_1
uint8_t socclkspreadpercent;
uint16_t socclkspreadfreq;
- uint32_t boardreserved[3];
+ uint8_t acggfxclkspreadenabled;
+ uint8_t acggfxclkspreadpercent;
+ uint16_t acggfxclkspreadfreq;
+
+ uint32_t boardreserved[10];
};
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/Makefile b/drivers/gpu/drm/amd/powerplay/hwmgr/Makefile
index faf9c880e4f7..210fb3ecd213 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/Makefile
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/Makefile
@@ -32,7 +32,7 @@ HARDWARE_MGR = hwmgr.o processpptables.o \
vega10_processpptables.o vega10_hwmgr.o vega10_powertune.o \
vega10_thermal.o smu10_hwmgr.o pp_psm.o\
vega12_processpptables.o vega12_hwmgr.o \
- vega12_powertune.o vega12_thermal.o \
+ vega12_thermal.o \
pp_overdriver.o smu_helper.o
AMD_PP_HWMGR = $(addprefix $(AMD_PP_PATH)/hwmgr/,$(HARDWARE_MGR))
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomfwctrl.c b/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomfwctrl.c
index 55f9b30513ff..ad42caac033e 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomfwctrl.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomfwctrl.c
@@ -616,9 +616,9 @@ int pp_atomfwctrl_get_smc_dpm_information(struct pp_hwmgr *hwmgr,
param->ledpin1 = info->ledpin1;
param->ledpin2 = info->ledpin2;
- param->gfxclkspreadenabled = info->gfxclkspreadenabled;
- param->gfxclkspreadpercent = info->gfxclkspreadpercent;
- param->gfxclkspreadfreq = info->gfxclkspreadfreq;
+ param->pllgfxclkspreadenabled = info->pllgfxclkspreadenabled;
+ param->pllgfxclkspreadpercent = info->pllgfxclkspreadpercent;
+ param->pllgfxclkspreadfreq = info->pllgfxclkspreadfreq;
param->uclkspreadenabled = info->uclkspreadenabled;
param->uclkspreadpercent = info->uclkspreadpercent;
@@ -628,5 +628,9 @@ int pp_atomfwctrl_get_smc_dpm_information(struct pp_hwmgr *hwmgr,
param->socclkspreadpercent = info->socclkspreadpercent;
param->socclkspreadfreq = info->socclkspreadfreq;
+ param->acggfxclkspreadenabled = info->acggfxclkspreadenabled;
+ param->acggfxclkspreadpercent = info->acggfxclkspreadpercent;
+ param->acggfxclkspreadfreq = info->acggfxclkspreadfreq;
+
return 0;
}
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomfwctrl.h b/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomfwctrl.h
index a957d8f08029..8df1e84f27c9 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomfwctrl.h
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomfwctrl.h
@@ -192,9 +192,9 @@ struct pp_atomfwctrl_smc_dpm_parameters
uint8_t ledpin1;
uint8_t ledpin2;
- uint8_t gfxclkspreadenabled;
- uint8_t gfxclkspreadpercent;
- uint16_t gfxclkspreadfreq;
+ uint8_t pllgfxclkspreadenabled;
+ uint8_t pllgfxclkspreadpercent;
+ uint16_t pllgfxclkspreadfreq;
uint8_t uclkspreadenabled;
uint8_t uclkspreadpercent;
@@ -203,6 +203,10 @@ struct pp_atomfwctrl_smc_dpm_parameters
uint8_t socclkspreadenabled;
uint8_t socclkspreadpercent;
uint16_t socclkspreadfreq;
+
+ uint8_t acggfxclkspreadenabled;
+ uint8_t acggfxclkspreadpercent;
+ uint16_t acggfxclkspreadfreq;
};
int pp_atomfwctrl_get_gpu_pll_dividers_vega10(struct pp_hwmgr *hwmgr,
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c
index 2b0c366d6149..26fbeafc3c96 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c
@@ -3374,7 +3374,8 @@ static int smu7_get_gpu_power(struct pp_hwmgr *hwmgr,
"Failed to start pm status log!",
return -1);
- msleep_interruptible(20);
+ /* Sampling period from 50ms to 4sec */
+ msleep_interruptible(200);
PP_ASSERT_WITH_CODE(!smum_send_msg_to_smc(hwmgr,
PPSMC_MSG_PmStatusLogSample),
@@ -4742,23 +4743,27 @@ static void smu7_check_dpm_table_updated(struct pp_hwmgr *hwmgr)
for (i=0; i < dep_table->count; i++) {
if (dep_table->entries[i].vddc != odn_dep_table->entries[i].vddc) {
- data->need_update_smu7_dpm_table |= DPMTABLE_OD_UPDATE_VDDC;
- break;
+ data->need_update_smu7_dpm_table |= DPMTABLE_OD_UPDATE_VDDC | DPMTABLE_OD_UPDATE_MCLK;
+ return;
}
}
- if (i == dep_table->count)
+ if (i == dep_table->count && data->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_VDDC) {
data->need_update_smu7_dpm_table &= ~DPMTABLE_OD_UPDATE_VDDC;
+ data->need_update_smu7_dpm_table |= DPMTABLE_OD_UPDATE_MCLK;
+ }
dep_table = table_info->vdd_dep_on_sclk;
odn_dep_table = (struct phm_ppt_v1_clock_voltage_dependency_table *)&(odn_table->vdd_dependency_on_sclk);
for (i=0; i < dep_table->count; i++) {
if (dep_table->entries[i].vddc != odn_dep_table->entries[i].vddc) {
- data->need_update_smu7_dpm_table |= DPMTABLE_OD_UPDATE_VDDC;
- break;
+ data->need_update_smu7_dpm_table |= DPMTABLE_OD_UPDATE_VDDC | DPMTABLE_OD_UPDATE_SCLK;
+ return;
}
}
- if (i == dep_table->count)
+ if (i == dep_table->count && data->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_VDDC) {
data->need_update_smu7_dpm_table &= ~DPMTABLE_OD_UPDATE_VDDC;
+ data->need_update_smu7_dpm_table |= DPMTABLE_OD_UPDATE_SCLK;
+ }
}
static int smu7_odn_edit_dpm_table(struct pp_hwmgr *hwmgr,
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu8_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/smu8_hwmgr.c
index 75a465f771f0..7b26607c646a 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/smu8_hwmgr.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/smu8_hwmgr.c
@@ -319,13 +319,13 @@ static int smu8_get_system_info_data(struct pp_hwmgr *hwmgr)
GetIndexIntoMasterTable(DATA, IntegratedSystemInfo),
&size, &frev, &crev);
- if (crev != 9) {
- pr_err("Unsupported IGP table: %d %d\n", frev, crev);
+ if (info == NULL) {
+ pr_err("Could not retrieve the Integrated System Info Table!\n");
return -EINVAL;
}
- if (info == NULL) {
- pr_err("Could not retrieve the Integrated System Info Table!\n");
+ if (crev != 9) {
+ pr_err("Unsupported IGP table: %d %d\n", frev, crev);
return -EINVAL;
}
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_hwmgr.c
index 15ce1e825021..200de46bd06b 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_hwmgr.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_hwmgr.c
@@ -33,7 +33,6 @@
#include "ppatomfwctrl.h"
#include "atomfirmware.h"
#include "cgs_common.h"
-#include "vega12_powertune.h"
#include "vega12_inc.h"
#include "pp_soc15.h"
#include "pppcielanes.h"
@@ -893,6 +892,28 @@ static int vega12_odn_initialize_default_settings(
return 0;
}
+static int vega12_set_overdrive_target_percentage(struct pp_hwmgr *hwmgr,
+ uint32_t adjust_percent)
+{
+ return smum_send_msg_to_smc_with_parameter(hwmgr,
+ PPSMC_MSG_OverDriveSetPercentage, adjust_percent);
+}
+
+static int vega12_power_control_set_level(struct pp_hwmgr *hwmgr)
+{
+ int adjust_percent, result = 0;
+
+ if (PP_CAP(PHM_PlatformCaps_PowerContainment)) {
+ adjust_percent =
+ hwmgr->platform_descriptor.TDPAdjustmentPolarity ?
+ hwmgr->platform_descriptor.TDPAdjustment :
+ (-1 * hwmgr->platform_descriptor.TDPAdjustment);
+ result = vega12_set_overdrive_target_percentage(hwmgr,
+ (uint32_t)adjust_percent);
+ }
+ return result;
+}
+
static int vega12_enable_dpm_tasks(struct pp_hwmgr *hwmgr)
{
int tmp_result, result = 0;
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_powertune.c b/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_powertune.c
deleted file mode 100644
index 76e60c0181ac..000000000000
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_powertune.c
+++ /dev/null
@@ -1,1364 +0,0 @@
-/*
- * Copyright 2017 Advanced Micro Devices, Inc.
- *
- * 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
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * 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 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 NONINFRINGEMENT. IN NO EVENT SHALL
- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 "hwmgr.h"
-#include "vega12_hwmgr.h"
-#include "vega12_powertune.h"
-#include "vega12_smumgr.h"
-#include "vega12_ppsmc.h"
-#include "vega12_inc.h"
-#include "pp_debug.h"
-#include "pp_soc15.h"
-
-static const struct vega12_didt_config_reg SEDiDtTuningCtrlConfig_Vega12[] =
-{
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * Offset Mask Shift Value
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
- /* DIDT_SQ */
- { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT, 0x3853 },
- { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT, 0x3153 },
-
- /* DIDT_TD */
- { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT, 0x0dde },
- { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT, 0x0dde },
-
- /* DIDT_TCP */
- { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT, 0x3dde },
- { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT, 0x3dde },
-
- /* DIDT_DB */
- { ixDIDT_DB_TUNING_CTRL, DIDT_DB_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK, DIDT_DB_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT, 0x3dde },
- { ixDIDT_DB_TUNING_CTRL, DIDT_DB_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK, DIDT_DB_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT, 0x3dde },
-
- { 0xFFFFFFFF } /* End of list */
-};
-
-static const struct vega12_didt_config_reg SEDiDtCtrl3Config_vega12[] =
-{
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * Offset Mask Shift Value
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
- /*DIDT_SQ_CTRL3 */
- { ixDIDT_SQ_CTRL3, DIDT_SQ_CTRL3__GC_DIDT_ENABLE_MASK, DIDT_SQ_CTRL3__GC_DIDT_ENABLE__SHIFT, 0x0000 },
- { ixDIDT_SQ_CTRL3, DIDT_SQ_CTRL3__GC_DIDT_CLK_EN_OVERRIDE_MASK, DIDT_SQ_CTRL3__GC_DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
- { ixDIDT_SQ_CTRL3, DIDT_SQ_CTRL3__THROTTLE_POLICY_MASK, DIDT_SQ_CTRL3__THROTTLE_POLICY__SHIFT, 0x0003 },
- { ixDIDT_SQ_CTRL3, DIDT_SQ_CTRL3__DIDT_TRIGGER_THROTTLE_LOWBIT_MASK, DIDT_SQ_CTRL3__DIDT_TRIGGER_THROTTLE_LOWBIT__SHIFT, 0x0000 },
- { ixDIDT_SQ_CTRL3, DIDT_SQ_CTRL3__DIDT_POWER_LEVEL_LOWBIT_MASK, DIDT_SQ_CTRL3__DIDT_POWER_LEVEL_LOWBIT__SHIFT, 0x0000 },
- { ixDIDT_SQ_CTRL3, DIDT_SQ_CTRL3__DIDT_STALL_PATTERN_BIT_NUMS_MASK, DIDT_SQ_CTRL3__DIDT_STALL_PATTERN_BIT_NUMS__SHIFT, 0x0003 },
- { ixDIDT_SQ_CTRL3, DIDT_SQ_CTRL3__GC_DIDT_LEVEL_COMB_EN_MASK, DIDT_SQ_CTRL3__GC_DIDT_LEVEL_COMB_EN__SHIFT, 0x0000 },
- { ixDIDT_SQ_CTRL3, DIDT_SQ_CTRL3__SE_DIDT_LEVEL_COMB_EN_MASK, DIDT_SQ_CTRL3__SE_DIDT_LEVEL_COMB_EN__SHIFT, 0x0000 },
- { ixDIDT_SQ_CTRL3, DIDT_SQ_CTRL3__QUALIFY_STALL_EN_MASK, DIDT_SQ_CTRL3__QUALIFY_STALL_EN__SHIFT, 0x0000 },
- { ixDIDT_SQ_CTRL3, DIDT_SQ_CTRL3__DIDT_STALL_SEL_MASK, DIDT_SQ_CTRL3__DIDT_STALL_SEL__SHIFT, 0x0000 },
- { ixDIDT_SQ_CTRL3, DIDT_SQ_CTRL3__DIDT_FORCE_STALL_MASK, DIDT_SQ_CTRL3__DIDT_FORCE_STALL__SHIFT, 0x0000 },
- { ixDIDT_SQ_CTRL3, DIDT_SQ_CTRL3__DIDT_STALL_DELAY_EN_MASK, DIDT_SQ_CTRL3__DIDT_STALL_DELAY_EN__SHIFT, 0x0000 },
-
- /*DIDT_TCP_CTRL3 */
- { ixDIDT_TCP_CTRL3, DIDT_TCP_CTRL3__GC_DIDT_ENABLE_MASK, DIDT_TCP_CTRL3__GC_DIDT_ENABLE__SHIFT, 0x0000 },
- { ixDIDT_TCP_CTRL3, DIDT_TCP_CTRL3__GC_DIDT_CLK_EN_OVERRIDE_MASK, DIDT_TCP_CTRL3__GC_DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
- { ixDIDT_TCP_CTRL3, DIDT_TCP_CTRL3__THROTTLE_POLICY_MASK, DIDT_TCP_CTRL3__THROTTLE_POLICY__SHIFT, 0x0003 },
- { ixDIDT_TCP_CTRL3, DIDT_TCP_CTRL3__DIDT_TRIGGER_THROTTLE_LOWBIT_MASK, DIDT_TCP_CTRL3__DIDT_TRIGGER_THROTTLE_LOWBIT__SHIFT, 0x0000 },
- { ixDIDT_TCP_CTRL3, DIDT_TCP_CTRL3__DIDT_POWER_LEVEL_LOWBIT_MASK, DIDT_TCP_CTRL3__DIDT_POWER_LEVEL_LOWBIT__SHIFT, 0x0000 },
- { ixDIDT_TCP_CTRL3, DIDT_TCP_CTRL3__DIDT_STALL_PATTERN_BIT_NUMS_MASK, DIDT_TCP_CTRL3__DIDT_STALL_PATTERN_BIT_NUMS__SHIFT, 0x0003 },
- { ixDIDT_TCP_CTRL3, DIDT_TCP_CTRL3__GC_DIDT_LEVEL_COMB_EN_MASK, DIDT_TCP_CTRL3__GC_DIDT_LEVEL_COMB_EN__SHIFT, 0x0000 },
- { ixDIDT_TCP_CTRL3, DIDT_TCP_CTRL3__SE_DIDT_LEVEL_COMB_EN_MASK, DIDT_TCP_CTRL3__SE_DIDT_LEVEL_COMB_EN__SHIFT, 0x0000 },
- { ixDIDT_TCP_CTRL3, DIDT_TCP_CTRL3__QUALIFY_STALL_EN_MASK, DIDT_TCP_CTRL3__QUALIFY_STALL_EN__SHIFT, 0x0000 },
- { ixDIDT_TCP_CTRL3, DIDT_TCP_CTRL3__DIDT_STALL_SEL_MASK, DIDT_TCP_CTRL3__DIDT_STALL_SEL__SHIFT, 0x0000 },
- { ixDIDT_TCP_CTRL3, DIDT_TCP_CTRL3__DIDT_FORCE_STALL_MASK, DIDT_TCP_CTRL3__DIDT_FORCE_STALL__SHIFT, 0x0000 },
- { ixDIDT_TCP_CTRL3, DIDT_TCP_CTRL3__DIDT_STALL_DELAY_EN_MASK, DIDT_TCP_CTRL3__DIDT_STALL_DELAY_EN__SHIFT, 0x0000 },
-
- /*DIDT_TD_CTRL3 */
- { ixDIDT_TD_CTRL3, DIDT_TD_CTRL3__GC_DIDT_ENABLE_MASK, DIDT_TD_CTRL3__GC_DIDT_ENABLE__SHIFT, 0x0000 },
- { ixDIDT_TD_CTRL3, DIDT_TD_CTRL3__GC_DIDT_CLK_EN_OVERRIDE_MASK, DIDT_TD_CTRL3__GC_DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
- { ixDIDT_TD_CTRL3, DIDT_TD_CTRL3__THROTTLE_POLICY_MASK, DIDT_TD_CTRL3__THROTTLE_POLICY__SHIFT, 0x0003 },
- { ixDIDT_TD_CTRL3, DIDT_TD_CTRL3__DIDT_TRIGGER_THROTTLE_LOWBIT_MASK, DIDT_TD_CTRL3__DIDT_TRIGGER_THROTTLE_LOWBIT__SHIFT, 0x0000 },
- { ixDIDT_TD_CTRL3, DIDT_TD_CTRL3__DIDT_POWER_LEVEL_LOWBIT_MASK, DIDT_TD_CTRL3__DIDT_POWER_LEVEL_LOWBIT__SHIFT, 0x0000 },
- { ixDIDT_TD_CTRL3, DIDT_TD_CTRL3__DIDT_STALL_PATTERN_BIT_NUMS_MASK, DIDT_TD_CTRL3__DIDT_STALL_PATTERN_BIT_NUMS__SHIFT, 0x0003 },
- { ixDIDT_TD_CTRL3, DIDT_TD_CTRL3__GC_DIDT_LEVEL_COMB_EN_MASK, DIDT_TD_CTRL3__GC_DIDT_LEVEL_COMB_EN__SHIFT, 0x0000 },
- { ixDIDT_TD_CTRL3, DIDT_TD_CTRL3__SE_DIDT_LEVEL_COMB_EN_MASK, DIDT_TD_CTRL3__SE_DIDT_LEVEL_COMB_EN__SHIFT, 0x0000 },
- { ixDIDT_TD_CTRL3, DIDT_TD_CTRL3__QUALIFY_STALL_EN_MASK, DIDT_TD_CTRL3__QUALIFY_STALL_EN__SHIFT, 0x0000 },
- { ixDIDT_TD_CTRL3, DIDT_TD_CTRL3__DIDT_STALL_SEL_MASK, DIDT_TD_CTRL3__DIDT_STALL_SEL__SHIFT, 0x0000 },
- { ixDIDT_TD_CTRL3, DIDT_TD_CTRL3__DIDT_FORCE_STALL_MASK, DIDT_TD_CTRL3__DIDT_FORCE_STALL__SHIFT, 0x0000 },
- { ixDIDT_TD_CTRL3, DIDT_TD_CTRL3__DIDT_STALL_DELAY_EN_MASK, DIDT_TD_CTRL3__DIDT_STALL_DELAY_EN__SHIFT, 0x0000 },
-
- /*DIDT_DB_CTRL3 */
- { ixDIDT_DB_CTRL3, DIDT_DB_CTRL3__GC_DIDT_ENABLE_MASK, DIDT_DB_CTRL3__GC_DIDT_ENABLE__SHIFT, 0x0000 },
- { ixDIDT_DB_CTRL3, DIDT_DB_CTRL3__GC_DIDT_CLK_EN_OVERRIDE_MASK, DIDT_DB_CTRL3__GC_DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
- { ixDIDT_DB_CTRL3, DIDT_DB_CTRL3__THROTTLE_POLICY_MASK, DIDT_DB_CTRL3__THROTTLE_POLICY__SHIFT, 0x0003 },
- { ixDIDT_DB_CTRL3, DIDT_DB_CTRL3__DIDT_TRIGGER_THROTTLE_LOWBIT_MASK, DIDT_DB_CTRL3__DIDT_TRIGGER_THROTTLE_LOWBIT__SHIFT, 0x0000 },
- { ixDIDT_DB_CTRL3, DIDT_DB_CTRL3__DIDT_POWER_LEVEL_LOWBIT_MASK, DIDT_DB_CTRL3__DIDT_POWER_LEVEL_LOWBIT__SHIFT, 0x0000 },
- { ixDIDT_DB_CTRL3, DIDT_DB_CTRL3__DIDT_STALL_PATTERN_BIT_NUMS_MASK, DIDT_DB_CTRL3__DIDT_STALL_PATTERN_BIT_NUMS__SHIFT, 0x0003 },
- { ixDIDT_DB_CTRL3, DIDT_DB_CTRL3__GC_DIDT_LEVEL_COMB_EN_MASK, DIDT_DB_CTRL3__GC_DIDT_LEVEL_COMB_EN__SHIFT, 0x0000 },
- { ixDIDT_DB_CTRL3, DIDT_DB_CTRL3__SE_DIDT_LEVEL_COMB_EN_MASK, DIDT_DB_CTRL3__SE_DIDT_LEVEL_COMB_EN__SHIFT, 0x0000 },
- { ixDIDT_DB_CTRL3, DIDT_DB_CTRL3__QUALIFY_STALL_EN_MASK, DIDT_DB_CTRL3__QUALIFY_STALL_EN__SHIFT, 0x0000 },
- { ixDIDT_DB_CTRL3, DIDT_DB_CTRL3__DIDT_STALL_SEL_MASK, DIDT_DB_CTRL3__DIDT_STALL_SEL__SHIFT, 0x0000 },
- { ixDIDT_DB_CTRL3, DIDT_DB_CTRL3__DIDT_FORCE_STALL_MASK, DIDT_DB_CTRL3__DIDT_FORCE_STALL__SHIFT, 0x0000 },
- { ixDIDT_DB_CTRL3, DIDT_DB_CTRL3__DIDT_STALL_DELAY_EN_MASK, DIDT_DB_CTRL3__DIDT_STALL_DELAY_EN__SHIFT, 0x0000 },
-
- { 0xFFFFFFFF } /* End of list */
-};
-
-static const struct vega12_didt_config_reg SEDiDtCtrl2Config_Vega12[] =
-{
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * Offset Mask Shift Value
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
- /* DIDT_SQ */
- { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__MAX_POWER_DELTA_MASK, DIDT_SQ_CTRL2__MAX_POWER_DELTA__SHIFT, 0x3853 },
- { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK, DIDT_SQ_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT, 0x00c0 },
- { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK, DIDT_SQ_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT, 0x0000 },
-
- /* DIDT_TD */
- { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__MAX_POWER_DELTA_MASK, DIDT_TD_CTRL2__MAX_POWER_DELTA__SHIFT, 0x3fff },
- { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK, DIDT_TD_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT, 0x00c0 },
- { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK, DIDT_TD_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT, 0x0001 },
-
- /* DIDT_TCP */
- { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__MAX_POWER_DELTA_MASK, DIDT_TCP_CTRL2__MAX_POWER_DELTA__SHIFT, 0x3dde },
- { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK, DIDT_TCP_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT, 0x00c0 },
- { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK, DIDT_TCP_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT, 0x0001 },
-
- /* DIDT_DB */
- { ixDIDT_DB_CTRL2, DIDT_DB_CTRL2__MAX_POWER_DELTA_MASK, DIDT_DB_CTRL2__MAX_POWER_DELTA__SHIFT, 0x3dde },
- { ixDIDT_DB_CTRL2, DIDT_DB_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK, DIDT_DB_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT, 0x00c0 },
- { ixDIDT_DB_CTRL2, DIDT_DB_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK, DIDT_DB_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT, 0x0001 },
-
- { 0xFFFFFFFF } /* End of list */
-};
-
-static const struct vega12_didt_config_reg SEDiDtCtrl1Config_Vega12[] =
-{
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * Offset Mask Shift Value
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
- /* DIDT_SQ */
- { ixDIDT_SQ_CTRL1, DIDT_SQ_CTRL1__MIN_POWER_MASK, DIDT_SQ_CTRL1__MIN_POWER__SHIFT, 0x0000 },
- { ixDIDT_SQ_CTRL1, DIDT_SQ_CTRL1__MAX_POWER_MASK, DIDT_SQ_CTRL1__MAX_POWER__SHIFT, 0xffff },
- /* DIDT_TD */
- { ixDIDT_TD_CTRL1, DIDT_TD_CTRL1__MIN_POWER_MASK, DIDT_TD_CTRL1__MIN_POWER__SHIFT, 0x0000 },
- { ixDIDT_TD_CTRL1, DIDT_TD_CTRL1__MAX_POWER_MASK, DIDT_TD_CTRL1__MAX_POWER__SHIFT, 0xffff },
- /* DIDT_TCP */
- { ixDIDT_TCP_CTRL1, DIDT_TCP_CTRL1__MIN_POWER_MASK, DIDT_TCP_CTRL1__MIN_POWER__SHIFT, 0x0000 },
- { ixDIDT_TCP_CTRL1, DIDT_TCP_CTRL1__MAX_POWER_MASK, DIDT_TCP_CTRL1__MAX_POWER__SHIFT, 0xffff },
- /* DIDT_DB */
- { ixDIDT_DB_CTRL1, DIDT_DB_CTRL1__MIN_POWER_MASK, DIDT_DB_CTRL1__MIN_POWER__SHIFT, 0x0000 },
- { ixDIDT_DB_CTRL1, DIDT_DB_CTRL1__MAX_POWER_MASK, DIDT_DB_CTRL1__MAX_POWER__SHIFT, 0xffff },
-
- { 0xFFFFFFFF } /* End of list */
-};
-
-
-static const struct vega12_didt_config_reg SEDiDtWeightConfig_Vega12[] =
-{
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * Offset Mask Shift Value
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
- /* DIDT_SQ */
- { ixDIDT_SQ_WEIGHT0_3, 0xFFFFFFFF, 0, 0x2B363B1A },
- { ixDIDT_SQ_WEIGHT4_7, 0xFFFFFFFF, 0, 0x270B2432 },
- { ixDIDT_SQ_WEIGHT8_11, 0xFFFFFFFF, 0, 0x00000018 },
-
- /* DIDT_TD */
- { ixDIDT_TD_WEIGHT0_3, 0xFFFFFFFF, 0, 0x2B1D220F },
- { ixDIDT_TD_WEIGHT4_7, 0xFFFFFFFF, 0, 0x00007558 },
- { ixDIDT_TD_WEIGHT8_11, 0xFFFFFFFF, 0, 0x00000000 },
-
- /* DIDT_TCP */
- { ixDIDT_TCP_WEIGHT0_3, 0xFFFFFFFF, 0, 0x5ACE160D },
- { ixDIDT_TCP_WEIGHT4_7, 0xFFFFFFFF, 0, 0x00000000 },
- { ixDIDT_TCP_WEIGHT8_11, 0xFFFFFFFF, 0, 0x00000000 },
-
- /* DIDT_DB */
- { ixDIDT_DB_WEIGHT0_3, 0xFFFFFFFF, 0, 0x0E152A0F },
- { ixDIDT_DB_WEIGHT4_7, 0xFFFFFFFF, 0, 0x09061813 },
- { ixDIDT_DB_WEIGHT8_11, 0xFFFFFFFF, 0, 0x00000013 },
-
- { 0xFFFFFFFF } /* End of list */
-};
-
-static const struct vega12_didt_config_reg SEDiDtCtrl0Config_Vega12[] =
-{
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * Offset Mask Shift Value
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
- /* DIDT_SQ */
- { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_CTRL_EN_MASK, DIDT_SQ_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0000 },
- { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__PHASE_OFFSET_MASK, DIDT_SQ_CTRL0__PHASE_OFFSET__SHIFT, 0x0000 },
- { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_CTRL_RST_MASK, DIDT_SQ_CTRL0__DIDT_CTRL_RST__SHIFT, 0x0000 },
- { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, DIDT_SQ_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
- { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_STALL_CTRL_EN_MASK, DIDT_SQ_CTRL0__DIDT_STALL_CTRL_EN__SHIFT, 0x0001 },
- { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_TUNING_CTRL_EN_MASK, DIDT_SQ_CTRL0__DIDT_TUNING_CTRL_EN__SHIFT, 0x0001 },
- { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_STALL_AUTO_RELEASE_EN_MASK, DIDT_SQ_CTRL0__DIDT_STALL_AUTO_RELEASE_EN__SHIFT, 0x0001 },
- { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_HI_POWER_THRESHOLD_MASK, DIDT_SQ_CTRL0__DIDT_HI_POWER_THRESHOLD__SHIFT, 0xffff },
- { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_AUTO_MPD_EN_MASK, DIDT_SQ_CTRL0__DIDT_AUTO_MPD_EN__SHIFT, 0x0000 },
- { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_STALL_EVENT_EN_MASK, DIDT_SQ_CTRL0__DIDT_STALL_EVENT_EN__SHIFT, 0x0000 },
- { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_STALL_EVENT_COUNTER_CLEAR_MASK, DIDT_SQ_CTRL0__DIDT_STALL_EVENT_COUNTER_CLEAR__SHIFT, 0x0000 },
- /* DIDT_TD */
- { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_CTRL_EN_MASK, DIDT_TD_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0000 },
- { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__PHASE_OFFSET_MASK, DIDT_TD_CTRL0__PHASE_OFFSET__SHIFT, 0x0000 },
- { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_CTRL_RST_MASK, DIDT_TD_CTRL0__DIDT_CTRL_RST__SHIFT, 0x0000 },
- { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, DIDT_TD_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
- { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_STALL_CTRL_EN_MASK, DIDT_TD_CTRL0__DIDT_STALL_CTRL_EN__SHIFT, 0x0001 },
- { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_TUNING_CTRL_EN_MASK, DIDT_TD_CTRL0__DIDT_TUNING_CTRL_EN__SHIFT, 0x0001 },
- { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_STALL_AUTO_RELEASE_EN_MASK, DIDT_TD_CTRL0__DIDT_STALL_AUTO_RELEASE_EN__SHIFT, 0x0001 },
- { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_HI_POWER_THRESHOLD_MASK, DIDT_TD_CTRL0__DIDT_HI_POWER_THRESHOLD__SHIFT, 0xffff },
- { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_AUTO_MPD_EN_MASK, DIDT_TD_CTRL0__DIDT_AUTO_MPD_EN__SHIFT, 0x0000 },
- { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_STALL_EVENT_EN_MASK, DIDT_TD_CTRL0__DIDT_STALL_EVENT_EN__SHIFT, 0x0000 },
- { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_STALL_EVENT_COUNTER_CLEAR_MASK, DIDT_TD_CTRL0__DIDT_STALL_EVENT_COUNTER_CLEAR__SHIFT, 0x0000 },
- /* DIDT_TCP */
- { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_CTRL_EN_MASK, DIDT_TCP_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0000 },
- { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__PHASE_OFFSET_MASK, DIDT_TCP_CTRL0__PHASE_OFFSET__SHIFT, 0x0000 },
- { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_CTRL_RST_MASK, DIDT_TCP_CTRL0__DIDT_CTRL_RST__SHIFT, 0x0000 },
- { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, DIDT_TCP_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
- { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_STALL_CTRL_EN_MASK, DIDT_TCP_CTRL0__DIDT_STALL_CTRL_EN__SHIFT, 0x0001 },
- { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_TUNING_CTRL_EN_MASK, DIDT_TCP_CTRL0__DIDT_TUNING_CTRL_EN__SHIFT, 0x0001 },
- { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_STALL_AUTO_RELEASE_EN_MASK, DIDT_TCP_CTRL0__DIDT_STALL_AUTO_RELEASE_EN__SHIFT, 0x0001 },
- { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_HI_POWER_THRESHOLD_MASK, DIDT_TCP_CTRL0__DIDT_HI_POWER_THRESHOLD__SHIFT, 0xffff },
- { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_AUTO_MPD_EN_MASK, DIDT_TCP_CTRL0__DIDT_AUTO_MPD_EN__SHIFT, 0x0000 },
- { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_STALL_EVENT_EN_MASK, DIDT_TCP_CTRL0__DIDT_STALL_EVENT_EN__SHIFT, 0x0000 },
- { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_STALL_EVENT_COUNTER_CLEAR_MASK, DIDT_TCP_CTRL0__DIDT_STALL_EVENT_COUNTER_CLEAR__SHIFT, 0x0000 },
- /* DIDT_DB */
- { ixDIDT_DB_CTRL0, DIDT_DB_CTRL0__DIDT_CTRL_EN_MASK, DIDT_DB_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0000 },
- { ixDIDT_DB_CTRL0, DIDT_DB_CTRL0__PHASE_OFFSET_MASK, DIDT_DB_CTRL0__PHASE_OFFSET__SHIFT, 0x0000 },
- { ixDIDT_DB_CTRL0, DIDT_DB_CTRL0__DIDT_CTRL_RST_MASK, DIDT_DB_CTRL0__DIDT_CTRL_RST__SHIFT, 0x0000 },
- { ixDIDT_DB_CTRL0, DIDT_DB_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, DIDT_DB_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
- { ixDIDT_DB_CTRL0, DIDT_DB_CTRL0__DIDT_STALL_CTRL_EN_MASK, DIDT_DB_CTRL0__DIDT_STALL_CTRL_EN__SHIFT, 0x0001 },
- { ixDIDT_DB_CTRL0, DIDT_DB_CTRL0__DIDT_TUNING_CTRL_EN_MASK, DIDT_DB_CTRL0__DIDT_TUNING_CTRL_EN__SHIFT, 0x0001 },
- { ixDIDT_DB_CTRL0, DIDT_DB_CTRL0__DIDT_STALL_AUTO_RELEASE_EN_MASK, DIDT_DB_CTRL0__DIDT_STALL_AUTO_RELEASE_EN__SHIFT, 0x0001 },
- { ixDIDT_DB_CTRL0, DIDT_DB_CTRL0__DIDT_HI_POWER_THRESHOLD_MASK, DIDT_DB_CTRL0__DIDT_HI_POWER_THRESHOLD__SHIFT, 0xffff },
- { ixDIDT_DB_CTRL0, DIDT_DB_CTRL0__DIDT_AUTO_MPD_EN_MASK, DIDT_DB_CTRL0__DIDT_AUTO_MPD_EN__SHIFT, 0x0000 },
- { ixDIDT_DB_CTRL0, DIDT_DB_CTRL0__DIDT_STALL_EVENT_EN_MASK, DIDT_DB_CTRL0__DIDT_STALL_EVENT_EN__SHIFT, 0x0000 },
- { ixDIDT_DB_CTRL0, DIDT_DB_CTRL0__DIDT_STALL_EVENT_COUNTER_CLEAR_MASK, DIDT_DB_CTRL0__DIDT_STALL_EVENT_COUNTER_CLEAR__SHIFT, 0x0000 },
-
- { 0xFFFFFFFF } /* End of list */
-};
-
-
-static const struct vega12_didt_config_reg SEDiDtStallCtrlConfig_vega12[] =
-{
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * Offset Mask Shift Value
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
- /* DIDT_SQ */
- { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT, 0x0004 },
- { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT, 0x0004 },
- { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_SQ_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x000a },
- { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_SQ_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x000a },
-
- /* DIDT_TD */
- { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT, 0x0001 },
- { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT, 0x0001 },
- { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_TD_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x000a },
- { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_TD_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x000a },
-
- /* DIDT_TCP */
- { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT, 0x0001 },
- { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT, 0x0001 },
- { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_TCP_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x000a },
- { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_TCP_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x000a },
-
- /* DIDT_DB */
- { ixDIDT_DB_STALL_CTRL, DIDT_DB_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK, DIDT_DB_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT, 0x0004 },
- { ixDIDT_DB_STALL_CTRL, DIDT_DB_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK, DIDT_DB_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT, 0x0004 },
- { ixDIDT_DB_STALL_CTRL, DIDT_DB_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_DB_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x000a },
- { ixDIDT_DB_STALL_CTRL, DIDT_DB_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_DB_STALL_CTRL__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x000a },
-
- { 0xFFFFFFFF } /* End of list */
-};
-
-static const struct vega12_didt_config_reg SEDiDtStallPatternConfig_vega12[] =
-{
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * Offset Mask Shift Value
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
- /* DIDT_SQ_STALL_PATTERN_1_2 */
- { ixDIDT_SQ_STALL_PATTERN_1_2, DIDT_SQ_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_1_MASK, DIDT_SQ_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_1__SHIFT, 0x0001 },
- { ixDIDT_SQ_STALL_PATTERN_1_2, DIDT_SQ_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_2_MASK, DIDT_SQ_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_2__SHIFT, 0x0001 },
-
- /* DIDT_SQ_STALL_PATTERN_3_4 */
- { ixDIDT_SQ_STALL_PATTERN_3_4, DIDT_SQ_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_3_MASK, DIDT_SQ_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_3__SHIFT, 0x0001 },
- { ixDIDT_SQ_STALL_PATTERN_3_4, DIDT_SQ_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_4_MASK, DIDT_SQ_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_4__SHIFT, 0x0001 },
-
- /* DIDT_SQ_STALL_PATTERN_5_6 */
- { ixDIDT_SQ_STALL_PATTERN_5_6, DIDT_SQ_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_5_MASK, DIDT_SQ_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_5__SHIFT, 0x0000 },
- { ixDIDT_SQ_STALL_PATTERN_5_6, DIDT_SQ_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_6_MASK, DIDT_SQ_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_6__SHIFT, 0x0000 },
-
- /* DIDT_SQ_STALL_PATTERN_7 */
- { ixDIDT_SQ_STALL_PATTERN_7, DIDT_SQ_STALL_PATTERN_7__DIDT_STALL_PATTERN_7_MASK, DIDT_SQ_STALL_PATTERN_7__DIDT_STALL_PATTERN_7__SHIFT, 0x0000 },
-
- /* DIDT_TCP_STALL_PATTERN_1_2 */
- { ixDIDT_TCP_STALL_PATTERN_1_2, DIDT_TCP_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_1_MASK, DIDT_TCP_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_1__SHIFT, 0x0001 },
- { ixDIDT_TCP_STALL_PATTERN_1_2, DIDT_TCP_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_2_MASK, DIDT_TCP_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_2__SHIFT, 0x0001 },
-
- /* DIDT_TCP_STALL_PATTERN_3_4 */
- { ixDIDT_TCP_STALL_PATTERN_3_4, DIDT_TCP_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_3_MASK, DIDT_TCP_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_3__SHIFT, 0x0001 },
- { ixDIDT_TCP_STALL_PATTERN_3_4, DIDT_TCP_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_4_MASK, DIDT_TCP_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_4__SHIFT, 0x0001 },
-
- /* DIDT_TCP_STALL_PATTERN_5_6 */
- { ixDIDT_TCP_STALL_PATTERN_5_6, DIDT_TCP_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_5_MASK, DIDT_TCP_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_5__SHIFT, 0x0000 },
- { ixDIDT_TCP_STALL_PATTERN_5_6, DIDT_TCP_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_6_MASK, DIDT_TCP_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_6__SHIFT, 0x0000 },
-
- /* DIDT_TCP_STALL_PATTERN_7 */
- { ixDIDT_TCP_STALL_PATTERN_7, DIDT_TCP_STALL_PATTERN_7__DIDT_STALL_PATTERN_7_MASK, DIDT_TCP_STALL_PATTERN_7__DIDT_STALL_PATTERN_7__SHIFT, 0x0000 },
-
- /* DIDT_TD_STALL_PATTERN_1_2 */
- { ixDIDT_TD_STALL_PATTERN_1_2, DIDT_TD_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_1_MASK, DIDT_TD_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_1__SHIFT, 0x0001 },
- { ixDIDT_TD_STALL_PATTERN_1_2, DIDT_TD_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_2_MASK, DIDT_TD_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_2__SHIFT, 0x0001 },
-
- /* DIDT_TD_STALL_PATTERN_3_4 */
- { ixDIDT_TD_STALL_PATTERN_3_4, DIDT_TD_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_3_MASK, DIDT_TD_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_3__SHIFT, 0x0001 },
- { ixDIDT_TD_STALL_PATTERN_3_4, DIDT_TD_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_4_MASK, DIDT_TD_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_4__SHIFT, 0x0001 },
-
- /* DIDT_TD_STALL_PATTERN_5_6 */
- { ixDIDT_TD_STALL_PATTERN_5_6, DIDT_TD_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_5_MASK, DIDT_TD_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_5__SHIFT, 0x0000 },
- { ixDIDT_TD_STALL_PATTERN_5_6, DIDT_TD_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_6_MASK, DIDT_TD_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_6__SHIFT, 0x0000 },
-
- /* DIDT_TD_STALL_PATTERN_7 */
- { ixDIDT_TD_STALL_PATTERN_7, DIDT_TD_STALL_PATTERN_7__DIDT_STALL_PATTERN_7_MASK, DIDT_TD_STALL_PATTERN_7__DIDT_STALL_PATTERN_7__SHIFT, 0x0000 },
-
- /* DIDT_DB_STALL_PATTERN_1_2 */
- { ixDIDT_DB_STALL_PATTERN_1_2, DIDT_DB_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_1_MASK, DIDT_DB_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_1__SHIFT, 0x0001 },
- { ixDIDT_DB_STALL_PATTERN_1_2, DIDT_DB_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_2_MASK, DIDT_DB_STALL_PATTERN_1_2__DIDT_STALL_PATTERN_2__SHIFT, 0x0001 },
-
- /* DIDT_DB_STALL_PATTERN_3_4 */
- { ixDIDT_DB_STALL_PATTERN_3_4, DIDT_DB_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_3_MASK, DIDT_DB_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_3__SHIFT, 0x0001 },
- { ixDIDT_DB_STALL_PATTERN_3_4, DIDT_DB_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_4_MASK, DIDT_DB_STALL_PATTERN_3_4__DIDT_STALL_PATTERN_4__SHIFT, 0x0001 },
-
- /* DIDT_DB_STALL_PATTERN_5_6 */
- { ixDIDT_DB_STALL_PATTERN_5_6, DIDT_DB_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_5_MASK, DIDT_DB_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_5__SHIFT, 0x0000 },
- { ixDIDT_DB_STALL_PATTERN_5_6, DIDT_DB_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_6_MASK, DIDT_DB_STALL_PATTERN_5_6__DIDT_STALL_PATTERN_6__SHIFT, 0x0000 },
-
- /* DIDT_DB_STALL_PATTERN_7 */
- { ixDIDT_DB_STALL_PATTERN_7, DIDT_DB_STALL_PATTERN_7__DIDT_STALL_PATTERN_7_MASK, DIDT_DB_STALL_PATTERN_7__DIDT_STALL_PATTERN_7__SHIFT, 0x0000 },
-
- { 0xFFFFFFFF } /* End of list */
-};
-
-static const struct vega12_didt_config_reg SELCacConfig_Vega12[] =
-{
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * Offset Mask Shift Value
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
- /* SQ */
- { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x00060021 },
- { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x00860021 },
- { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x01060021 },
- { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x01860021 },
- { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x02060021 },
- { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x02860021 },
- { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x03060021 },
- { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x03860021 },
- { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x04060021 },
- /* TD */
- { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x000E0020 },
- { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x008E0020 },
- { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x010E0020 },
- { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x018E0020 },
- { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x020E0020 },
- { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x028E0020 },
- /* TCP */
- { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x001c0020 },
- { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x009c0020 },
- { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x011c0020 },
- { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x019c0020 },
- { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x021c0020 },
- /* DB */
- { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x00200008 },
- { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x00820008 },
- { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x01020008 },
- { ixSE_CAC_CNTL, 0xFFFFFFFF, 0, 0x01820008 },
-
- { 0xFFFFFFFF } /* End of list */
-};
-
-
-static const struct vega12_didt_config_reg SEEDCStallPatternConfig_Vega12[] =
-{
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * Offset Mask Shift Value
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
- /* SQ */
- { ixDIDT_SQ_EDC_STALL_PATTERN_1_2, 0xFFFFFFFF, 0, 0x00030001 },
- { ixDIDT_SQ_EDC_STALL_PATTERN_3_4, 0xFFFFFFFF, 0, 0x000F0007 },
- { ixDIDT_SQ_EDC_STALL_PATTERN_5_6, 0xFFFFFFFF, 0, 0x003F001F },
- { ixDIDT_SQ_EDC_STALL_PATTERN_7, 0xFFFFFFFF, 0, 0x0000007F },
- /* TD */
- { ixDIDT_TD_EDC_STALL_PATTERN_1_2, 0xFFFFFFFF, 0, 0x00000000 },
- { ixDIDT_TD_EDC_STALL_PATTERN_3_4, 0xFFFFFFFF, 0, 0x00000000 },
- { ixDIDT_TD_EDC_STALL_PATTERN_5_6, 0xFFFFFFFF, 0, 0x00000000 },
- { ixDIDT_TD_EDC_STALL_PATTERN_7, 0xFFFFFFFF, 0, 0x00000000 },
- /* TCP */
- { ixDIDT_TCP_EDC_STALL_PATTERN_1_2, 0xFFFFFFFF, 0, 0x00000000 },
- { ixDIDT_TCP_EDC_STALL_PATTERN_3_4, 0xFFFFFFFF, 0, 0x00000000 },
- { ixDIDT_TCP_EDC_STALL_PATTERN_5_6, 0xFFFFFFFF, 0, 0x00000000 },
- { ixDIDT_TCP_EDC_STALL_PATTERN_7, 0xFFFFFFFF, 0, 0x00000000 },
- /* DB */
- { ixDIDT_DB_EDC_STALL_PATTERN_1_2, 0xFFFFFFFF, 0, 0x00000000 },
- { ixDIDT_DB_EDC_STALL_PATTERN_3_4, 0xFFFFFFFF, 0, 0x00000000 },
- { ixDIDT_DB_EDC_STALL_PATTERN_5_6, 0xFFFFFFFF, 0, 0x00000000 },
- { ixDIDT_DB_EDC_STALL_PATTERN_7, 0xFFFFFFFF, 0, 0x00000000 },
-
- { 0xFFFFFFFF } /* End of list */
-};
-
-static const struct vega12_didt_config_reg SEEDCForceStallPatternConfig_Vega12[] =
-{
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * Offset Mask Shift Value
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
- /* SQ */
- { ixDIDT_SQ_EDC_STALL_PATTERN_1_2, 0xFFFFFFFF, 0, 0x00000015 },
- { ixDIDT_SQ_EDC_STALL_PATTERN_3_4, 0xFFFFFFFF, 0, 0x00000000 },
- { ixDIDT_SQ_EDC_STALL_PATTERN_5_6, 0xFFFFFFFF, 0, 0x00000000 },
- { ixDIDT_SQ_EDC_STALL_PATTERN_7, 0xFFFFFFFF, 0, 0x00000000 },
- /* TD */
- { ixDIDT_TD_EDC_STALL_PATTERN_1_2, 0xFFFFFFFF, 0, 0x00000015 },
- { ixDIDT_TD_EDC_STALL_PATTERN_3_4, 0xFFFFFFFF, 0, 0x00000000 },
- { ixDIDT_TD_EDC_STALL_PATTERN_5_6, 0xFFFFFFFF, 0, 0x00000000 },
- { ixDIDT_TD_EDC_STALL_PATTERN_7, 0xFFFFFFFF, 0, 0x00000000 },
-
- { 0xFFFFFFFF } /* End of list */
-};
-
-static const struct vega12_didt_config_reg SEEDCStallDelayConfig_Vega12[] =
-{
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * Offset Mask Shift Value
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
- /* SQ */
- { ixDIDT_SQ_EDC_STALL_DELAY_1, 0xFFFFFFFF, 0, 0x00000000 },
- { ixDIDT_SQ_EDC_STALL_DELAY_2, 0xFFFFFFFF, 0, 0x00000000 },
- /* TD */
- { ixDIDT_TD_EDC_STALL_DELAY_1, 0xFFFFFFFF, 0, 0x00000000 },
- { ixDIDT_TD_EDC_STALL_DELAY_2, 0xFFFFFFFF, 0, 0x00000000 },
- /* TCP */
- { ixDIDT_TCP_EDC_STALL_DELAY_1, 0xFFFFFFFF, 0, 0x00000000 },
- { ixDIDT_TCP_EDC_STALL_DELAY_2, 0xFFFFFFFF, 0, 0x00000000 },
- /* DB */
- { ixDIDT_DB_EDC_STALL_DELAY_1, 0xFFFFFFFF, 0, 0x00000000 },
-
- { 0xFFFFFFFF } /* End of list */
-};
-
-static const struct vega12_didt_config_reg SEEDCThresholdConfig_Vega12[] =
-{
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * Offset Mask Shift Value
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
- { ixDIDT_SQ_EDC_THRESHOLD, 0xFFFFFFFF, 0, 0x0000010E },
- { ixDIDT_TD_EDC_THRESHOLD, 0xFFFFFFFF, 0, 0xFFFFFFFF },
- { ixDIDT_TCP_EDC_THRESHOLD, 0xFFFFFFFF, 0, 0xFFFFFFFF },
- { ixDIDT_DB_EDC_THRESHOLD, 0xFFFFFFFF, 0, 0xFFFFFFFF },
-
- { 0xFFFFFFFF } /* End of list */
-};
-
-static const struct vega12_didt_config_reg SEEDCCtrlResetConfig_Vega12[] =
-{
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * Offset Mask Shift Value
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
- /* SQ */
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_EN_MASK, DIDT_SQ_EDC_CTRL__EDC_EN__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_SW_RST_MASK, DIDT_SQ_EDC_CTRL__EDC_SW_RST__SHIFT, 0x0001 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_CLK_EN_OVERRIDE_MASK, DIDT_SQ_EDC_CTRL__EDC_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_FORCE_STALL_MASK, DIDT_SQ_EDC_CTRL__EDC_FORCE_STALL__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT_MASK, DIDT_SQ_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_STALL_PATTERN_BIT_NUMS_MASK, DIDT_SQ_EDC_CTRL__EDC_STALL_PATTERN_BIT_NUMS__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA_MASK, DIDT_SQ_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__GC_EDC_EN_MASK, DIDT_SQ_EDC_CTRL__GC_EDC_EN__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__GC_EDC_STALL_POLICY_MASK, DIDT_SQ_EDC_CTRL__GC_EDC_STALL_POLICY__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__GC_EDC_LEVEL_COMB_EN_MASK, DIDT_SQ_EDC_CTRL__GC_EDC_LEVEL_COMB_EN__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__SE_EDC_LEVEL_COMB_EN_MASK, DIDT_SQ_EDC_CTRL__SE_EDC_LEVEL_COMB_EN__SHIFT, 0x0000 },
-
- { 0xFFFFFFFF } /* End of list */
-};
-
-static const struct vega12_didt_config_reg SEEDCCtrlConfig_Vega12[] =
-{
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * Offset Mask Shift Value
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
- /* SQ */
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_EN_MASK, DIDT_SQ_EDC_CTRL__EDC_EN__SHIFT, 0x0001 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_SW_RST_MASK, DIDT_SQ_EDC_CTRL__EDC_SW_RST__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_CLK_EN_OVERRIDE_MASK, DIDT_SQ_EDC_CTRL__EDC_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_FORCE_STALL_MASK, DIDT_SQ_EDC_CTRL__EDC_FORCE_STALL__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT_MASK, DIDT_SQ_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT__SHIFT, 0x0004 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_STALL_PATTERN_BIT_NUMS_MASK, DIDT_SQ_EDC_CTRL__EDC_STALL_PATTERN_BIT_NUMS__SHIFT, 0x0006 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA_MASK, DIDT_SQ_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__GC_EDC_EN_MASK, DIDT_SQ_EDC_CTRL__GC_EDC_EN__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__GC_EDC_STALL_POLICY_MASK, DIDT_SQ_EDC_CTRL__GC_EDC_STALL_POLICY__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__GC_EDC_LEVEL_COMB_EN_MASK, DIDT_SQ_EDC_CTRL__GC_EDC_LEVEL_COMB_EN__SHIFT, 0x0001 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__SE_EDC_LEVEL_COMB_EN_MASK, DIDT_SQ_EDC_CTRL__SE_EDC_LEVEL_COMB_EN__SHIFT, 0x0000 },
-
- { 0xFFFFFFFF } /* End of list */
-};
-
-static const struct vega12_didt_config_reg SEEDCCtrlForceStallConfig_Vega12[] =
-{
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * Offset Mask Shift Value
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
- /* SQ */
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_EN_MASK, DIDT_SQ_EDC_CTRL__EDC_EN__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_SW_RST_MASK, DIDT_SQ_EDC_CTRL__EDC_SW_RST__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_CLK_EN_OVERRIDE_MASK, DIDT_SQ_EDC_CTRL__EDC_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_FORCE_STALL_MASK, DIDT_SQ_EDC_CTRL__EDC_FORCE_STALL__SHIFT, 0x0001 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT_MASK, DIDT_SQ_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT__SHIFT, 0x0001 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_STALL_PATTERN_BIT_NUMS_MASK, DIDT_SQ_EDC_CTRL__EDC_STALL_PATTERN_BIT_NUMS__SHIFT, 0x000C },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA_MASK, DIDT_SQ_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__GC_EDC_EN_MASK, DIDT_SQ_EDC_CTRL__GC_EDC_EN__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__GC_EDC_STALL_POLICY_MASK, DIDT_SQ_EDC_CTRL__GC_EDC_STALL_POLICY__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__GC_EDC_LEVEL_COMB_EN_MASK, DIDT_SQ_EDC_CTRL__GC_EDC_LEVEL_COMB_EN__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__SE_EDC_LEVEL_COMB_EN_MASK, DIDT_SQ_EDC_CTRL__SE_EDC_LEVEL_COMB_EN__SHIFT, 0x0001 },
-
- /* TD */
- { ixDIDT_TD_EDC_CTRL, DIDT_TD_EDC_CTRL__EDC_EN_MASK, DIDT_TD_EDC_CTRL__EDC_EN__SHIFT, 0x0000 },
- { ixDIDT_TD_EDC_CTRL, DIDT_TD_EDC_CTRL__EDC_SW_RST_MASK, DIDT_TD_EDC_CTRL__EDC_SW_RST__SHIFT, 0x0000 },
- { ixDIDT_TD_EDC_CTRL, DIDT_TD_EDC_CTRL__EDC_CLK_EN_OVERRIDE_MASK, DIDT_TD_EDC_CTRL__EDC_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
- { ixDIDT_TD_EDC_CTRL, DIDT_TD_EDC_CTRL__EDC_FORCE_STALL_MASK, DIDT_TD_EDC_CTRL__EDC_FORCE_STALL__SHIFT, 0x0001 },
- { ixDIDT_TD_EDC_CTRL, DIDT_TD_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT_MASK, DIDT_TD_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT__SHIFT, 0x0001 },
- { ixDIDT_TD_EDC_CTRL, DIDT_TD_EDC_CTRL__EDC_STALL_PATTERN_BIT_NUMS_MASK, DIDT_TD_EDC_CTRL__EDC_STALL_PATTERN_BIT_NUMS__SHIFT, 0x000E },
- { ixDIDT_TD_EDC_CTRL, DIDT_TD_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA_MASK, DIDT_TD_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA__SHIFT, 0x0000 },
- { ixDIDT_TD_EDC_CTRL, DIDT_TD_EDC_CTRL__GC_EDC_EN_MASK, DIDT_TD_EDC_CTRL__GC_EDC_EN__SHIFT, 0x0000 },
- { ixDIDT_TD_EDC_CTRL, DIDT_TD_EDC_CTRL__GC_EDC_STALL_POLICY_MASK, DIDT_TD_EDC_CTRL__GC_EDC_STALL_POLICY__SHIFT, 0x0000 },
- { ixDIDT_TD_EDC_CTRL, DIDT_TD_EDC_CTRL__GC_EDC_LEVEL_COMB_EN_MASK, DIDT_TD_EDC_CTRL__GC_EDC_LEVEL_COMB_EN__SHIFT, 0x0000 },
- { ixDIDT_TD_EDC_CTRL, DIDT_TD_EDC_CTRL__SE_EDC_LEVEL_COMB_EN_MASK, DIDT_TD_EDC_CTRL__SE_EDC_LEVEL_COMB_EN__SHIFT, 0x0001 },
-
- { 0xFFFFFFFF } /* End of list */
-};
-
-static const struct vega12_didt_config_reg GCDiDtDroopCtrlConfig_vega12[] =
-{
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * Offset Mask Shift Value
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
- { mmGC_DIDT_DROOP_CTRL, GC_DIDT_DROOP_CTRL__DIDT_DROOP_LEVEL_EN_MASK, GC_DIDT_DROOP_CTRL__DIDT_DROOP_LEVEL_EN__SHIFT, 0x0000 },
- { mmGC_DIDT_DROOP_CTRL, GC_DIDT_DROOP_CTRL__DIDT_DROOP_THRESHOLD_MASK, GC_DIDT_DROOP_CTRL__DIDT_DROOP_THRESHOLD__SHIFT, 0x0000 },
- { mmGC_DIDT_DROOP_CTRL, GC_DIDT_DROOP_CTRL__DIDT_DROOP_LEVEL_INDEX_MASK, GC_DIDT_DROOP_CTRL__DIDT_DROOP_LEVEL_INDEX__SHIFT, 0x0000 },
- { mmGC_DIDT_DROOP_CTRL, GC_DIDT_DROOP_CTRL__DIDT_LEVEL_SEL_MASK, GC_DIDT_DROOP_CTRL__DIDT_LEVEL_SEL__SHIFT, 0x0000 },
- { mmGC_DIDT_DROOP_CTRL, GC_DIDT_DROOP_CTRL__DIDT_DROOP_LEVEL_OVERFLOW_MASK, GC_DIDT_DROOP_CTRL__DIDT_DROOP_LEVEL_OVERFLOW__SHIFT, 0x0000 },
-
- { 0xFFFFFFFF } /* End of list */
-};
-
-static const struct vega12_didt_config_reg GCDiDtCtrl0Config_vega12[] =
-{
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * Offset Mask Shift Value
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
- { mmGC_DIDT_CTRL0, GC_DIDT_CTRL0__DIDT_CTRL_EN_MASK, GC_DIDT_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0000 },
- { mmGC_DIDT_CTRL0, GC_DIDT_CTRL0__PHASE_OFFSET_MASK, GC_DIDT_CTRL0__PHASE_OFFSET__SHIFT, 0x0000 },
- { mmGC_DIDT_CTRL0, GC_DIDT_CTRL0__DIDT_SW_RST_MASK, GC_DIDT_CTRL0__DIDT_SW_RST__SHIFT, 0x0000 },
- { mmGC_DIDT_CTRL0, GC_DIDT_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, GC_DIDT_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
- { mmGC_DIDT_CTRL0, GC_DIDT_CTRL0__DIDT_TRIGGER_THROTTLE_LOWBIT_MASK, GC_DIDT_CTRL0__DIDT_TRIGGER_THROTTLE_LOWBIT__SHIFT, 0x0000 },
- { 0xFFFFFFFF } /* End of list */
-};
-
-
-static const struct vega12_didt_config_reg PSMSEEDCStallPatternConfig_Vega12[] =
-{
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * Offset Mask Shift Value
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
- /* SQ EDC STALL PATTERNs */
- { ixDIDT_SQ_EDC_STALL_PATTERN_1_2, DIDT_SQ_EDC_STALL_PATTERN_1_2__EDC_STALL_PATTERN_1_MASK, DIDT_SQ_EDC_STALL_PATTERN_1_2__EDC_STALL_PATTERN_1__SHIFT, 0x0101 },
- { ixDIDT_SQ_EDC_STALL_PATTERN_1_2, DIDT_SQ_EDC_STALL_PATTERN_1_2__EDC_STALL_PATTERN_2_MASK, DIDT_SQ_EDC_STALL_PATTERN_1_2__EDC_STALL_PATTERN_2__SHIFT, 0x0101 },
- { ixDIDT_SQ_EDC_STALL_PATTERN_3_4, DIDT_SQ_EDC_STALL_PATTERN_3_4__EDC_STALL_PATTERN_3_MASK, DIDT_SQ_EDC_STALL_PATTERN_3_4__EDC_STALL_PATTERN_3__SHIFT, 0x1111 },
- { ixDIDT_SQ_EDC_STALL_PATTERN_3_4, DIDT_SQ_EDC_STALL_PATTERN_3_4__EDC_STALL_PATTERN_4_MASK, DIDT_SQ_EDC_STALL_PATTERN_3_4__EDC_STALL_PATTERN_4__SHIFT, 0x1111 },
-
- { ixDIDT_SQ_EDC_STALL_PATTERN_5_6, DIDT_SQ_EDC_STALL_PATTERN_5_6__EDC_STALL_PATTERN_5_MASK, DIDT_SQ_EDC_STALL_PATTERN_5_6__EDC_STALL_PATTERN_5__SHIFT, 0x1515 },
- { ixDIDT_SQ_EDC_STALL_PATTERN_5_6, DIDT_SQ_EDC_STALL_PATTERN_5_6__EDC_STALL_PATTERN_6_MASK, DIDT_SQ_EDC_STALL_PATTERN_5_6__EDC_STALL_PATTERN_6__SHIFT, 0x1515 },
-
- { ixDIDT_SQ_EDC_STALL_PATTERN_7, DIDT_SQ_EDC_STALL_PATTERN_7__EDC_STALL_PATTERN_7_MASK, DIDT_SQ_EDC_STALL_PATTERN_7__EDC_STALL_PATTERN_7__SHIFT, 0x5555 },
-
- { 0xFFFFFFFF } /* End of list */
-};
-
-static const struct vega12_didt_config_reg PSMSEEDCStallDelayConfig_Vega12[] =
-{
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * Offset Mask Shift Value
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
- /* SQ EDC STALL DELAYs */
- { ixDIDT_SQ_EDC_STALL_DELAY_1, DIDT_SQ_EDC_STALL_DELAY_1__EDC_STALL_DELAY_SQ0_MASK, DIDT_SQ_EDC_STALL_DELAY_1__EDC_STALL_DELAY_SQ0__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_STALL_DELAY_1, DIDT_SQ_EDC_STALL_DELAY_1__EDC_STALL_DELAY_SQ1_MASK, DIDT_SQ_EDC_STALL_DELAY_1__EDC_STALL_DELAY_SQ1__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_STALL_DELAY_1, DIDT_SQ_EDC_STALL_DELAY_1__EDC_STALL_DELAY_SQ2_MASK, DIDT_SQ_EDC_STALL_DELAY_1__EDC_STALL_DELAY_SQ2__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_STALL_DELAY_1, DIDT_SQ_EDC_STALL_DELAY_1__EDC_STALL_DELAY_SQ3_MASK, DIDT_SQ_EDC_STALL_DELAY_1__EDC_STALL_DELAY_SQ3__SHIFT, 0x0000 },
-
- { ixDIDT_SQ_EDC_STALL_DELAY_2, DIDT_SQ_EDC_STALL_DELAY_2__EDC_STALL_DELAY_SQ4_MASK, DIDT_SQ_EDC_STALL_DELAY_2__EDC_STALL_DELAY_SQ4__SHIFT, 0x0000 },
-
- { 0xFFFFFFFF } /* End of list */
-};
-
-static const struct vega12_didt_config_reg PSMSEEDCThresholdConfig_Vega12[] =
-{
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * Offset Mask Shift Value
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
- /* SQ EDC THRESHOLD */
- { ixDIDT_SQ_EDC_THRESHOLD, DIDT_SQ_EDC_THRESHOLD__EDC_THRESHOLD_MASK, DIDT_SQ_EDC_THRESHOLD__EDC_THRESHOLD__SHIFT, 0x0000 },
-
- { 0xFFFFFFFF } /* End of list */
-};
-
-static const struct vega12_didt_config_reg PSMSEEDCCtrlResetConfig_Vega12[] =
-{
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * Offset Mask Shift Value
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
- /* SQ EDC CTRL */
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_EN_MASK, DIDT_SQ_EDC_CTRL__EDC_EN__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_SW_RST_MASK, DIDT_SQ_EDC_CTRL__EDC_SW_RST__SHIFT, 0x0001 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_CLK_EN_OVERRIDE_MASK, DIDT_SQ_EDC_CTRL__EDC_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_FORCE_STALL_MASK, DIDT_SQ_EDC_CTRL__EDC_FORCE_STALL__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT_MASK, DIDT_SQ_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_STALL_PATTERN_BIT_NUMS_MASK, DIDT_SQ_EDC_CTRL__EDC_STALL_PATTERN_BIT_NUMS__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA_MASK, DIDT_SQ_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__GC_EDC_EN_MASK, DIDT_SQ_EDC_CTRL__GC_EDC_EN__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__GC_EDC_STALL_POLICY_MASK, DIDT_SQ_EDC_CTRL__GC_EDC_STALL_POLICY__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__GC_EDC_LEVEL_COMB_EN_MASK, DIDT_SQ_EDC_CTRL__GC_EDC_LEVEL_COMB_EN__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__SE_EDC_LEVEL_COMB_EN_MASK, DIDT_SQ_EDC_CTRL__SE_EDC_LEVEL_COMB_EN__SHIFT, 0x0000 },
-
- { 0xFFFFFFFF } /* End of list */
-};
-
-static const struct vega12_didt_config_reg PSMSEEDCCtrlConfig_Vega12[] =
-{
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * Offset Mask Shift Value
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
- /* SQ EDC CTRL */
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_EN_MASK, DIDT_SQ_EDC_CTRL__EDC_EN__SHIFT, 0x0001 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_SW_RST_MASK, DIDT_SQ_EDC_CTRL__EDC_SW_RST__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_CLK_EN_OVERRIDE_MASK, DIDT_SQ_EDC_CTRL__EDC_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_FORCE_STALL_MASK, DIDT_SQ_EDC_CTRL__EDC_FORCE_STALL__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT_MASK, DIDT_SQ_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_STALL_PATTERN_BIT_NUMS_MASK, DIDT_SQ_EDC_CTRL__EDC_STALL_PATTERN_BIT_NUMS__SHIFT, 0x000E },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA_MASK, DIDT_SQ_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA__SHIFT, 0x0000 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__GC_EDC_EN_MASK, DIDT_SQ_EDC_CTRL__GC_EDC_EN__SHIFT, 0x0001 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__GC_EDC_STALL_POLICY_MASK, DIDT_SQ_EDC_CTRL__GC_EDC_STALL_POLICY__SHIFT, 0x0003 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__GC_EDC_LEVEL_COMB_EN_MASK, DIDT_SQ_EDC_CTRL__GC_EDC_LEVEL_COMB_EN__SHIFT, 0x0001 },
- { ixDIDT_SQ_EDC_CTRL, DIDT_SQ_EDC_CTRL__SE_EDC_LEVEL_COMB_EN_MASK, DIDT_SQ_EDC_CTRL__SE_EDC_LEVEL_COMB_EN__SHIFT, 0x0000 },
-
- { 0xFFFFFFFF } /* End of list */
-};
-
-static const struct vega12_didt_config_reg PSMGCEDCThresholdConfig_vega12[] =
-{
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * Offset Mask Shift Value
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
- { mmGC_EDC_THRESHOLD, GC_EDC_THRESHOLD__EDC_THRESHOLD_MASK, GC_EDC_THRESHOLD__EDC_THRESHOLD__SHIFT, 0x0000000 },
-
- { 0xFFFFFFFF } /* End of list */
-};
-
-static const struct vega12_didt_config_reg PSMGCEDCDroopCtrlConfig_vega12[] =
-{
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * Offset Mask Shift Value
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
- { mmGC_EDC_DROOP_CTRL, GC_EDC_DROOP_CTRL__EDC_DROOP_LEVEL_EN_MASK, GC_EDC_DROOP_CTRL__EDC_DROOP_LEVEL_EN__SHIFT, 0x0001 },
- { mmGC_EDC_DROOP_CTRL, GC_EDC_DROOP_CTRL__EDC_DROOP_THRESHOLD_MASK, GC_EDC_DROOP_CTRL__EDC_DROOP_THRESHOLD__SHIFT, 0x0384 },
- { mmGC_EDC_DROOP_CTRL, GC_EDC_DROOP_CTRL__EDC_DROOP_LEVEL_INDEX_MASK, GC_EDC_DROOP_CTRL__EDC_DROOP_LEVEL_INDEX__SHIFT, 0x0001 },
- { mmGC_EDC_DROOP_CTRL, GC_EDC_DROOP_CTRL__AVG_PSM_SEL_MASK, GC_EDC_DROOP_CTRL__AVG_PSM_SEL__SHIFT, 0x0001 },
- { mmGC_EDC_DROOP_CTRL, GC_EDC_DROOP_CTRL__EDC_LEVEL_SEL_MASK, GC_EDC_DROOP_CTRL__EDC_LEVEL_SEL__SHIFT, 0x0001 },
-
- { 0xFFFFFFFF } /* End of list */
-};
-
-static const struct vega12_didt_config_reg PSMGCEDCCtrlResetConfig_vega12[] =
-{
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * Offset Mask Shift Value
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
- { mmGC_EDC_CTRL, GC_EDC_CTRL__EDC_EN_MASK, GC_EDC_CTRL__EDC_EN__SHIFT, 0x0000 },
- { mmGC_EDC_CTRL, GC_EDC_CTRL__EDC_SW_RST_MASK, GC_EDC_CTRL__EDC_SW_RST__SHIFT, 0x0001 },
- { mmGC_EDC_CTRL, GC_EDC_CTRL__EDC_CLK_EN_OVERRIDE_MASK, GC_EDC_CTRL__EDC_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
- { mmGC_EDC_CTRL, GC_EDC_CTRL__EDC_FORCE_STALL_MASK, GC_EDC_CTRL__EDC_FORCE_STALL__SHIFT, 0x0000 },
- { mmGC_EDC_CTRL, GC_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT_MASK, GC_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT__SHIFT, 0x0000 },
- { mmGC_EDC_CTRL, GC_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA_MASK, GC_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA__SHIFT, 0x0000 },
-
- { 0xFFFFFFFF } /* End of list */
-};
-
-static const struct vega12_didt_config_reg PSMGCEDCCtrlConfig_vega12[] =
-{
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * Offset Mask Shift Value
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
- { mmGC_EDC_CTRL, GC_EDC_CTRL__EDC_EN_MASK, GC_EDC_CTRL__EDC_EN__SHIFT, 0x0001 },
- { mmGC_EDC_CTRL, GC_EDC_CTRL__EDC_SW_RST_MASK, GC_EDC_CTRL__EDC_SW_RST__SHIFT, 0x0000 },
- { mmGC_EDC_CTRL, GC_EDC_CTRL__EDC_CLK_EN_OVERRIDE_MASK, GC_EDC_CTRL__EDC_CLK_EN_OVERRIDE__SHIFT, 0x0000 },
- { mmGC_EDC_CTRL, GC_EDC_CTRL__EDC_FORCE_STALL_MASK, GC_EDC_CTRL__EDC_FORCE_STALL__SHIFT, 0x0000 },
- { mmGC_EDC_CTRL, GC_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT_MASK, GC_EDC_CTRL__EDC_TRIGGER_THROTTLE_LOWBIT__SHIFT, 0x0000 },
- { mmGC_EDC_CTRL, GC_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA_MASK, GC_EDC_CTRL__EDC_ALLOW_WRITE_PWRDELTA__SHIFT, 0x0000 },
-
- { 0xFFFFFFFF } /* End of list */
-};
-
-static const struct vega12_didt_config_reg AvfsPSMResetConfig_vega12[]=
-{
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * Offset Mask Shift Value
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
- { 0x16A02, 0xFFFFFFFF, 0x0, 0x0000005F },
- { 0x16A05, 0xFFFFFFFF, 0x0, 0x00000001 },
- { 0x16A06, 0x00000001, 0x0, 0x02000000 },
- { 0x16A01, 0xFFFFFFFF, 0x0, 0x00003027 },
-
- { 0xFFFFFFFF } /* End of list */
-};
-
-static const struct vega12_didt_config_reg AvfsPSMInitConfig_vega12[] =
-{
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- * Offset Mask Shift Value
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
- { 0x16A05, 0xFFFFFFFF, 0x18, 0x00000001 },
- { 0x16A05, 0xFFFFFFFF, 0x8, 0x00000003 },
- { 0x16A05, 0xFFFFFFFF, 0xa, 0x00000006 },
- { 0x16A05, 0xFFFFFFFF, 0x7, 0x00000000 },
- { 0x16A06, 0xFFFFFFFF, 0x18, 0x00000001 },
- { 0x16A06, 0xFFFFFFFF, 0x19, 0x00000001 },
- { 0x16A01, 0xFFFFFFFF, 0x0, 0x00003027 },
-
- { 0xFFFFFFFF } /* End of list */
-};
-
-static int vega12_program_didt_config_registers(struct pp_hwmgr *hwmgr, const struct vega12_didt_config_reg *config_regs, enum vega12_didt_config_reg_type reg_type)
-{
- uint32_t data;
-
- PP_ASSERT_WITH_CODE((config_regs != NULL), "[vega12_program_didt_config_registers] Invalid config register table!", return -EINVAL);
-
- while (config_regs->offset != 0xFFFFFFFF) {
- switch (reg_type) {
- case VEGA12_CONFIGREG_DIDT:
- data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, config_regs->offset);
- data &= ~config_regs->mask;
- data |= ((config_regs->value << config_regs->shift) & config_regs->mask);
- cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, config_regs->offset, data);
- break;
- case VEGA12_CONFIGREG_GCCAC:
- data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG_GC_CAC, config_regs->offset);
- data &= ~config_regs->mask;
- data |= ((config_regs->value << config_regs->shift) & config_regs->mask);
- cgs_write_ind_register(hwmgr->device, CGS_IND_REG_GC_CAC, config_regs->offset, data);
- break;
- case VEGA12_CONFIGREG_SECAC:
- data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG_SE_CAC, config_regs->offset);
- data &= ~config_regs->mask;
- data |= ((config_regs->value << config_regs->shift) & config_regs->mask);
- cgs_write_ind_register(hwmgr->device, CGS_IND_REG_SE_CAC, config_regs->offset, data);
- break;
- default:
- return -EINVAL;
- }
-
- config_regs++;
- }
-
- return 0;
-}
-
-static int vega12_program_gc_didt_config_registers(struct pp_hwmgr *hwmgr, const struct vega12_didt_config_reg *config_regs)
-{
- uint32_t data;
-
- while (config_regs->offset != 0xFFFFFFFF) {
- data = cgs_read_register(hwmgr->device, config_regs->offset);
- data &= ~config_regs->mask;
- data |= ((config_regs->value << config_regs->shift) & config_regs->mask);
- cgs_write_register(hwmgr->device, config_regs->offset, data);
- config_regs++;
- }
-
- return 0;
-}
-
-static void vega12_didt_set_mask(struct pp_hwmgr *hwmgr, const bool enable)
-{
- uint32_t data;
- int result;
- uint32_t en = (enable ? 1 : 0);
- uint32_t didt_block_info = SQ_IR_MASK | TCP_IR_MASK | TD_PCC_MASK;
-
- if (PP_CAP(PHM_PlatformCaps_SQRamping)) {
- CGS_WREG32_FIELD_IND(hwmgr->device, CGS_IND_REG__DIDT,
- DIDT_SQ_CTRL0, DIDT_CTRL_EN, en);
- didt_block_info &= ~SQ_Enable_MASK;
- didt_block_info |= en << SQ_Enable_SHIFT;
- }
-
- if (PP_CAP(PHM_PlatformCaps_DBRamping)) {
- CGS_WREG32_FIELD_IND(hwmgr->device, CGS_IND_REG__DIDT,
- DIDT_DB_CTRL0, DIDT_CTRL_EN, en);
- didt_block_info &= ~DB_Enable_MASK;
- didt_block_info |= en << DB_Enable_SHIFT;
- }
-
- if (PP_CAP(PHM_PlatformCaps_TDRamping)) {
- CGS_WREG32_FIELD_IND(hwmgr->device, CGS_IND_REG__DIDT,
- DIDT_TD_CTRL0, DIDT_CTRL_EN, en);
- didt_block_info &= ~TD_Enable_MASK;
- didt_block_info |= en << TD_Enable_SHIFT;
- }
-
- if (PP_CAP(PHM_PlatformCaps_TCPRamping)) {
- CGS_WREG32_FIELD_IND(hwmgr->device, CGS_IND_REG__DIDT,
- DIDT_TCP_CTRL0, DIDT_CTRL_EN, en);
- didt_block_info &= ~TCP_Enable_MASK;
- didt_block_info |= en << TCP_Enable_SHIFT;
- }
-
-#if 0
- if (PP_CAP(PHM_PlatformCaps_DBRRamping)) {
- CGS_WREG32_FIELD_IND(hwmgr->device, CGS_IND_REG__DIDT,
- DIDT_DBR_CTRL0, DIDT_CTRL_EN, en);
- }
-#endif
-
- if (PP_CAP(PHM_PlatformCaps_DiDtEDCEnable)) {
- if (PP_CAP(PHM_PlatformCaps_SQRamping)) {
- data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_SQ_EDC_CTRL);
- data = CGS_REG_SET_FIELD(data, DIDT_SQ_EDC_CTRL, EDC_EN, en);
- data = CGS_REG_SET_FIELD(data, DIDT_SQ_EDC_CTRL, EDC_SW_RST, ~en);
- cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_SQ_EDC_CTRL, data);
- }
-
- if (PP_CAP(PHM_PlatformCaps_DBRamping)) {
- data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_DB_EDC_CTRL);
- data = CGS_REG_SET_FIELD(data, DIDT_DB_EDC_CTRL, EDC_EN, en);
- data = CGS_REG_SET_FIELD(data, DIDT_DB_EDC_CTRL, EDC_SW_RST, ~en);
- cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_DB_EDC_CTRL, data);
- }
-
- if (PP_CAP(PHM_PlatformCaps_TDRamping)) {
- data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_TD_EDC_CTRL);
- data = CGS_REG_SET_FIELD(data, DIDT_TD_EDC_CTRL, EDC_EN, en);
- data = CGS_REG_SET_FIELD(data, DIDT_TD_EDC_CTRL, EDC_SW_RST, ~en);
- cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_TD_EDC_CTRL, data);
- }
-
- if (PP_CAP(PHM_PlatformCaps_TCPRamping)) {
- data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_TCP_EDC_CTRL);
- data = CGS_REG_SET_FIELD(data, DIDT_TCP_EDC_CTRL, EDC_EN, en);
- data = CGS_REG_SET_FIELD(data, DIDT_TCP_EDC_CTRL, EDC_SW_RST, ~en);
- cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_TCP_EDC_CTRL, data);
- }
-
-#if 0
- if (PP_CAP(PHM_PlatformCaps_DBRRamping)) {
- data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_DBR_EDC_CTRL);
- data = CGS_REG_SET_FIELD(data, DIDT_DBR_EDC_CTRL, EDC_EN, en);
- data = CGS_REG_SET_FIELD(data, DIDT_DBR_EDC_CTRL, EDC_SW_RST, ~en);
- cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_DBR_EDC_CTRL, data);
- }
-#endif
- }
-
- if (enable) {
- /* For Vega12, SMC does not support any mask yet. */
- result = smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_ConfigureGfxDidt, didt_block_info);
- PP_ASSERT((0 == result), "[EnableDiDtConfig] SMC Configure Gfx Didt Failed!");
- }
-}
-
-static int vega12_enable_cac_driving_se_didt_config(struct pp_hwmgr *hwmgr)
-{
- int result;
- uint32_t num_se = 0, count, data;
- struct amdgpu_device *adev = hwmgr->adev;
- uint32_t reg;
-
- num_se = adev->gfx.config.max_shader_engines;
-
- cgs_enter_safe_mode(hwmgr->device, true);
-
- cgs_lock_grbm_idx(hwmgr->device, true);
- reg = soc15_get_register_offset(GC_HWID, 0, mmGRBM_GFX_INDEX_BASE_IDX, mmGRBM_GFX_INDEX);
- for (count = 0; count < num_se; count++) {
- data = GRBM_GFX_INDEX__INSTANCE_BROADCAST_WRITES_MASK | GRBM_GFX_INDEX__SH_BROADCAST_WRITES_MASK | ( count << GRBM_GFX_INDEX__SE_INDEX__SHIFT);
- cgs_write_register(hwmgr->device, reg, data);
-
- result = vega12_program_didt_config_registers(hwmgr, SEDiDtStallCtrlConfig_vega12, VEGA12_CONFIGREG_DIDT);
- result |= vega12_program_didt_config_registers(hwmgr, SEDiDtStallPatternConfig_vega12, VEGA12_CONFIGREG_DIDT);
- result |= vega12_program_didt_config_registers(hwmgr, SEDiDtWeightConfig_Vega12, VEGA12_CONFIGREG_DIDT);
- result |= vega12_program_didt_config_registers(hwmgr, SEDiDtCtrl1Config_Vega12, VEGA12_CONFIGREG_DIDT);
- result |= vega12_program_didt_config_registers(hwmgr, SEDiDtCtrl2Config_Vega12, VEGA12_CONFIGREG_DIDT);
- result |= vega12_program_didt_config_registers(hwmgr, SEDiDtCtrl3Config_vega12, VEGA12_CONFIGREG_DIDT);
- result |= vega12_program_didt_config_registers(hwmgr, SEDiDtTuningCtrlConfig_Vega12, VEGA12_CONFIGREG_DIDT);
- result |= vega12_program_didt_config_registers(hwmgr, SELCacConfig_Vega12, VEGA12_CONFIGREG_SECAC);
- result |= vega12_program_didt_config_registers(hwmgr, SEDiDtCtrl0Config_Vega12, VEGA12_CONFIGREG_DIDT);
-
- if (0 != result)
- break;
- }
- cgs_write_register(hwmgr->device, reg, 0xE0000000);
- cgs_lock_grbm_idx(hwmgr->device, false);
-
- vega12_didt_set_mask(hwmgr, true);
-
- cgs_enter_safe_mode(hwmgr->device, false);
-
- return 0;
-}
-
-static int vega12_disable_cac_driving_se_didt_config(struct pp_hwmgr *hwmgr)
-{
- cgs_enter_safe_mode(hwmgr->device, true);
-
- vega12_didt_set_mask(hwmgr, false);
-
- cgs_enter_safe_mode(hwmgr->device, false);
-
- return 0;
-}
-
-static int vega12_enable_psm_gc_didt_config(struct pp_hwmgr *hwmgr)
-{
- int result;
- uint32_t num_se = 0, count, data;
- struct amdgpu_device *adev = hwmgr->adev;
- uint32_t reg;
-
- num_se = adev->gfx.config.max_shader_engines;
-
- cgs_enter_safe_mode(hwmgr->device, true);
-
- cgs_lock_grbm_idx(hwmgr->device, true);
- reg = soc15_get_register_offset(GC_HWID, 0, mmGRBM_GFX_INDEX_BASE_IDX, mmGRBM_GFX_INDEX);
- for (count = 0; count < num_se; count++) {
- data = GRBM_GFX_INDEX__INSTANCE_BROADCAST_WRITES_MASK | GRBM_GFX_INDEX__SH_BROADCAST_WRITES_MASK | ( count << GRBM_GFX_INDEX__SE_INDEX__SHIFT);
- cgs_write_register(hwmgr->device, reg, data);
-
- result = vega12_program_didt_config_registers(hwmgr, SEDiDtStallCtrlConfig_vega12, VEGA12_CONFIGREG_DIDT);
- result |= vega12_program_didt_config_registers(hwmgr, SEDiDtStallPatternConfig_vega12, VEGA12_CONFIGREG_DIDT);
- result |= vega12_program_didt_config_registers(hwmgr, SEDiDtCtrl3Config_vega12, VEGA12_CONFIGREG_DIDT);
- result |= vega12_program_didt_config_registers(hwmgr, SEDiDtCtrl0Config_Vega12, VEGA12_CONFIGREG_DIDT);
- if (0 != result)
- break;
- }
- cgs_write_register(hwmgr->device, reg, 0xE0000000);
- cgs_lock_grbm_idx(hwmgr->device, false);
-
- vega12_didt_set_mask(hwmgr, true);
-
- cgs_enter_safe_mode(hwmgr->device, false);
-
- vega12_program_gc_didt_config_registers(hwmgr, GCDiDtDroopCtrlConfig_vega12);
- if (PP_CAP(PHM_PlatformCaps_GCEDC))
- vega12_program_gc_didt_config_registers(hwmgr, GCDiDtCtrl0Config_vega12);
-
- if (PP_CAP(PHM_PlatformCaps_PSM))
- vega12_program_gc_didt_config_registers(hwmgr, AvfsPSMInitConfig_vega12);
-
- return 0;
-}
-
-static int vega12_disable_psm_gc_didt_config(struct pp_hwmgr *hwmgr)
-{
- uint32_t data;
-
- cgs_enter_safe_mode(hwmgr->device, true);
-
- vega12_didt_set_mask(hwmgr, false);
-
- cgs_enter_safe_mode(hwmgr->device, false);
-
- if (PP_CAP(PHM_PlatformCaps_GCEDC)) {
- data = 0x00000000;
- cgs_write_register(hwmgr->device, mmGC_DIDT_CTRL0, data);
- }
-
- if (PP_CAP(PHM_PlatformCaps_PSM))
- vega12_program_gc_didt_config_registers(hwmgr, AvfsPSMResetConfig_vega12);
-
- return 0;
-}
-
-static int vega12_enable_se_edc_config(struct pp_hwmgr *hwmgr)
-{
- int result;
- uint32_t num_se = 0, count, data;
- struct amdgpu_device *adev = hwmgr->adev;
- uint32_t reg;
-
- num_se = adev->gfx.config.max_shader_engines;
-
- cgs_enter_safe_mode(hwmgr->device, true);
-
- cgs_lock_grbm_idx(hwmgr->device, true);
- reg = soc15_get_register_offset(GC_HWID, 0, mmGRBM_GFX_INDEX_BASE_IDX, mmGRBM_GFX_INDEX);
- for (count = 0; count < num_se; count++) {
- data = GRBM_GFX_INDEX__INSTANCE_BROADCAST_WRITES_MASK | GRBM_GFX_INDEX__SH_BROADCAST_WRITES_MASK | ( count << GRBM_GFX_INDEX__SE_INDEX__SHIFT);
- cgs_write_register(hwmgr->device, reg, data);
- result = vega12_program_didt_config_registers(hwmgr, SEDiDtWeightConfig_Vega12, VEGA12_CONFIGREG_DIDT);
- result |= vega12_program_didt_config_registers(hwmgr, SEEDCStallPatternConfig_Vega12, VEGA12_CONFIGREG_DIDT);
- result |= vega12_program_didt_config_registers(hwmgr, SEEDCStallDelayConfig_Vega12, VEGA12_CONFIGREG_DIDT);
- result |= vega12_program_didt_config_registers(hwmgr, SEEDCThresholdConfig_Vega12, VEGA12_CONFIGREG_DIDT);
- result |= vega12_program_didt_config_registers(hwmgr, SEEDCCtrlResetConfig_Vega12, VEGA12_CONFIGREG_DIDT);
- result |= vega12_program_didt_config_registers(hwmgr, SEEDCCtrlConfig_Vega12, VEGA12_CONFIGREG_DIDT);
-
- if (0 != result)
- break;
- }
- cgs_write_register(hwmgr->device, reg, 0xE0000000);
- cgs_lock_grbm_idx(hwmgr->device, false);
-
- vega12_didt_set_mask(hwmgr, true);
-
- cgs_enter_safe_mode(hwmgr->device, false);
-
- return 0;
-}
-
-static int vega12_disable_se_edc_config(struct pp_hwmgr *hwmgr)
-{
- cgs_enter_safe_mode(hwmgr->device, true);
-
- vega12_didt_set_mask(hwmgr, false);
-
- cgs_enter_safe_mode(hwmgr->device, false);
-
- return 0;
-}
-
-static int vega12_enable_psm_gc_edc_config(struct pp_hwmgr *hwmgr)
-{
- int result;
- uint32_t num_se = 0;
- uint32_t count, data;
- struct amdgpu_device *adev = hwmgr->adev;
- uint32_t reg;
-
- num_se = adev->gfx.config.max_shader_engines;
-
- cgs_enter_safe_mode(hwmgr->device, true);
-
- vega12_program_gc_didt_config_registers(hwmgr, AvfsPSMResetConfig_vega12);
-
- cgs_lock_grbm_idx(hwmgr->device, true);
- reg = soc15_get_register_offset(GC_HWID, 0, mmGRBM_GFX_INDEX_BASE_IDX, mmGRBM_GFX_INDEX);
- for (count = 0; count < num_se; count++) {
- data = GRBM_GFX_INDEX__INSTANCE_BROADCAST_WRITES_MASK | GRBM_GFX_INDEX__SH_BROADCAST_WRITES_MASK | ( count << GRBM_GFX_INDEX__SE_INDEX__SHIFT);
- cgs_write_register(hwmgr->device, reg, data);
- result |= vega12_program_didt_config_registers(hwmgr, PSMSEEDCStallPatternConfig_Vega12, VEGA12_CONFIGREG_DIDT);
- result |= vega12_program_didt_config_registers(hwmgr, PSMSEEDCStallDelayConfig_Vega12, VEGA12_CONFIGREG_DIDT);
- result |= vega12_program_didt_config_registers(hwmgr, PSMSEEDCCtrlResetConfig_Vega12, VEGA12_CONFIGREG_DIDT);
- result |= vega12_program_didt_config_registers(hwmgr, PSMSEEDCCtrlConfig_Vega12, VEGA12_CONFIGREG_DIDT);
-
- if (0 != result)
- break;
- }
- cgs_write_register(hwmgr->device, reg, 0xE0000000);
- cgs_lock_grbm_idx(hwmgr->device, false);
-
- vega12_didt_set_mask(hwmgr, true);
-
- cgs_enter_safe_mode(hwmgr->device, false);
-
- vega12_program_gc_didt_config_registers(hwmgr, PSMGCEDCDroopCtrlConfig_vega12);
-
- if (PP_CAP(PHM_PlatformCaps_GCEDC)) {
- vega12_program_gc_didt_config_registers(hwmgr, PSMGCEDCCtrlResetConfig_vega12);
- vega12_program_gc_didt_config_registers(hwmgr, PSMGCEDCCtrlConfig_vega12);
- }
-
- if (PP_CAP(PHM_PlatformCaps_PSM))
- vega12_program_gc_didt_config_registers(hwmgr, AvfsPSMInitConfig_vega12);
-
- return 0;
-}
-
-static int vega12_disable_psm_gc_edc_config(struct pp_hwmgr *hwmgr)
-{
- uint32_t data;
-
- cgs_enter_safe_mode(hwmgr->device, true);
-
- vega12_didt_set_mask(hwmgr, false);
-
- cgs_enter_safe_mode(hwmgr->device, false);
-
- if (PP_CAP(PHM_PlatformCaps_GCEDC)) {
- data = 0x00000000;
- cgs_write_register(hwmgr->device, mmGC_EDC_CTRL, data);
- }
-
- if (PP_CAP(PHM_PlatformCaps_PSM))
- vega12_program_gc_didt_config_registers(hwmgr, AvfsPSMResetConfig_vega12);
-
- return 0;
-}
-
-static int vega12_enable_se_edc_force_stall_config(struct pp_hwmgr *hwmgr)
-{
- uint32_t reg;
- int result;
-
- cgs_enter_safe_mode(hwmgr->device, true);
-
- cgs_lock_grbm_idx(hwmgr->device, true);
- reg = soc15_get_register_offset(GC_HWID, 0, mmGRBM_GFX_INDEX_BASE_IDX, mmGRBM_GFX_INDEX);
- cgs_write_register(hwmgr->device, reg, 0xE0000000);
- cgs_lock_grbm_idx(hwmgr->device, false);
-
- result = vega12_program_didt_config_registers(hwmgr, SEEDCForceStallPatternConfig_Vega12, VEGA12_CONFIGREG_DIDT);
- result |= vega12_program_didt_config_registers(hwmgr, SEEDCCtrlForceStallConfig_Vega12, VEGA12_CONFIGREG_DIDT);
- if (0 != result)
- return result;
-
- vega12_didt_set_mask(hwmgr, false);
-
- cgs_enter_safe_mode(hwmgr->device, false);
-
- return 0;
-}
-
-static int vega12_disable_se_edc_force_stall_config(struct pp_hwmgr *hwmgr)
-{
- int result;
-
- result = vega12_disable_se_edc_config(hwmgr);
- PP_ASSERT_WITH_CODE((0 == result), "[DisableDiDtConfig] Pre DIDT disable clock gating failed!", return result);
-
- return 0;
-}
-
-int vega12_enable_didt_config(struct pp_hwmgr *hwmgr)
-{
- int result = 0;
- struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
-
- if (data->smu_features[GNLD_DIDT].supported) {
- if (data->smu_features[GNLD_DIDT].enabled)
- PP_DBG_LOG("[EnableDiDtConfig] Feature DiDt Already enabled!\n");
-
- switch (data->registry_data.didt_mode) {
- case 0:
- result = vega12_enable_cac_driving_se_didt_config(hwmgr);
- PP_ASSERT_WITH_CODE((0 == result), "[EnableDiDt] Attempt to enable DiDt Mode 0 Failed!", return result);
- break;
- case 2:
- result = vega12_enable_psm_gc_didt_config(hwmgr);
- PP_ASSERT_WITH_CODE((0 == result), "[EnableDiDt] Attempt to enable DiDt Mode 2 Failed!", return result);
- break;
- case 3:
- result = vega12_enable_se_edc_config(hwmgr);
- PP_ASSERT_WITH_CODE((0 == result), "[EnableDiDt] Attempt to enable DiDt Mode 3 Failed!", return result);
- break;
- case 1:
- case 4:
- case 5:
- result = vega12_enable_psm_gc_edc_config(hwmgr);
- PP_ASSERT_WITH_CODE((0 == result), "[EnableDiDt] Attempt to enable DiDt Mode 5 Failed!", return result);
- break;
- case 6:
- result = vega12_enable_se_edc_force_stall_config(hwmgr);
- PP_ASSERT_WITH_CODE((0 == result), "[EnableDiDt] Attempt to enable DiDt Mode 6 Failed!", return result);
- break;
- default:
- result = -EINVAL;
- break;
- }
-
-#if 0
- if (0 == result) {
- result = vega12_enable_smc_features(hwmgr, true, data->smu_features[GNLD_DIDT].smu_feature_bitmap);
- PP_ASSERT_WITH_CODE((0 == result), "[EnableDiDtConfig] Attempt to Enable DiDt feature Failed!", return result);
- data->smu_features[GNLD_DIDT].enabled = true;
- }
-#endif
- }
-
- return result;
-}
-
-int vega12_disable_didt_config(struct pp_hwmgr *hwmgr)
-{
- int result = 0;
- struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend);
-
- if (data->smu_features[GNLD_DIDT].supported) {
- if (!data->smu_features[GNLD_DIDT].enabled)
- PP_DBG_LOG("[DisableDiDtConfig] Feature DiDt Already Disabled!\n");
-
- switch (data->registry_data.didt_mode) {
- case 0:
- result = vega12_disable_cac_driving_se_didt_config(hwmgr);
- PP_ASSERT_WITH_CODE((0 == result), "[DisableDiDt] Attempt to disable DiDt Mode 0 Failed!", return result);
- break;
- case 2:
- result = vega12_disable_psm_gc_didt_config(hwmgr);
- PP_ASSERT_WITH_CODE((0 == result), "[DisableDiDt] Attempt to disable DiDt Mode 2 Failed!", return result);
- break;
- case 3:
- result = vega12_disable_se_edc_config(hwmgr);
- PP_ASSERT_WITH_CODE((0 == result), "[DisableDiDt] Attempt to disable DiDt Mode 3 Failed!", return result);
- break;
- case 1:
- case 4:
- case 5:
- result = vega12_disable_psm_gc_edc_config(hwmgr);
- PP_ASSERT_WITH_CODE((0 == result), "[DisableDiDt] Attempt to disable DiDt Mode 5 Failed!", return result);
- break;
- case 6:
- result = vega12_disable_se_edc_force_stall_config(hwmgr);
- PP_ASSERT_WITH_CODE((0 == result), "[DisableDiDt] Attempt to disable DiDt Mode 6 Failed!", return result);
- break;
- default:
- result = -EINVAL;
- break;
- }
-
- if (0 == result) {
- result = vega12_enable_smc_features(hwmgr, false, data->smu_features[GNLD_DIDT].smu_feature_bitmap);
- PP_ASSERT_WITH_CODE((0 == result), "[DisableDiDtConfig] Attempt to Disable DiDt feature Failed!", return result);
- data->smu_features[GNLD_DIDT].enabled = false;
- }
- }
-
- return result;
-}
-
-int vega12_set_power_limit(struct pp_hwmgr *hwmgr, uint32_t n)
-{
- struct vega12_hwmgr *data =
- (struct vega12_hwmgr *)(hwmgr->backend);
-
- if (data->smu_features[GNLD_PPT].enabled)
- return smum_send_msg_to_smc_with_parameter(hwmgr,
- PPSMC_MSG_SetPptLimit, n);
-
- return 0;
-}
-
-int vega12_enable_power_containment(struct pp_hwmgr *hwmgr)
-{
- struct vega12_hwmgr *data =
- (struct vega12_hwmgr *)(hwmgr->backend);
- struct phm_ppt_v2_information *table_info =
- (struct phm_ppt_v2_information *)(hwmgr->pptable);
- struct phm_tdp_table *tdp_table = table_info->tdp_table;
- uint32_t default_pwr_limit =
- (uint32_t)(tdp_table->usMaximumPowerDeliveryLimit);
- int result = 0;
-
- if (PP_CAP(PHM_PlatformCaps_PowerContainment)) {
- if (data->smu_features[GNLD_PPT].supported)
- PP_ASSERT_WITH_CODE(!vega12_enable_smc_features(hwmgr,
- true, data->smu_features[GNLD_PPT].smu_feature_bitmap),
- "Attempt to enable PPT feature Failed!",
- data->smu_features[GNLD_PPT].supported = false);
-
- if (data->smu_features[GNLD_TDC].supported)
- PP_ASSERT_WITH_CODE(!vega12_enable_smc_features(hwmgr,
- true, data->smu_features[GNLD_TDC].smu_feature_bitmap),
- "Attempt to enable PPT feature Failed!",
- data->smu_features[GNLD_TDC].supported = false);
-
- result = vega12_set_power_limit(hwmgr, default_pwr_limit);
- PP_ASSERT_WITH_CODE(!result,
- "Failed to set Default Power Limit in SMC!",
- return result);
- }
-
- return result;
-}
-
-int vega12_disable_power_containment(struct pp_hwmgr *hwmgr)
-{
- struct vega12_hwmgr *data =
- (struct vega12_hwmgr *)(hwmgr->backend);
-
- if (PP_CAP(PHM_PlatformCaps_PowerContainment)) {
- if (data->smu_features[GNLD_PPT].supported)
- PP_ASSERT_WITH_CODE(!vega12_enable_smc_features(hwmgr,
- false, data->smu_features[GNLD_PPT].smu_feature_bitmap),
- "Attempt to disable PPT feature Failed!",
- data->smu_features[GNLD_PPT].supported = false);
-
- if (data->smu_features[GNLD_TDC].supported)
- PP_ASSERT_WITH_CODE(!vega12_enable_smc_features(hwmgr,
- false, data->smu_features[GNLD_TDC].smu_feature_bitmap),
- "Attempt to disable PPT feature Failed!",
- data->smu_features[GNLD_TDC].supported = false);
- }
-
- return 0;
-}
-
-static int vega12_set_overdrive_target_percentage(struct pp_hwmgr *hwmgr,
- uint32_t adjust_percent)
-{
- return smum_send_msg_to_smc_with_parameter(hwmgr,
- PPSMC_MSG_OverDriveSetPercentage, adjust_percent);
-}
-
-int vega12_power_control_set_level(struct pp_hwmgr *hwmgr)
-{
- int adjust_percent, result = 0;
-
- if (PP_CAP(PHM_PlatformCaps_PowerContainment)) {
- adjust_percent =
- hwmgr->platform_descriptor.TDPAdjustmentPolarity ?
- hwmgr->platform_descriptor.TDPAdjustment :
- (-1 * hwmgr->platform_descriptor.TDPAdjustment);
- result = vega12_set_overdrive_target_percentage(hwmgr,
- (uint32_t)adjust_percent);
- }
- return result;
-}
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_powertune.h b/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_powertune.h
deleted file mode 100644
index 78d31a6747dd..000000000000
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_powertune.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2017 Advanced Micro Devices, Inc.
- *
- * 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
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * 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 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 NONINFRINGEMENT. IN NO EVENT SHALL
- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
- *
- */
-#ifndef _VEGA12_POWERTUNE_H_
-#define _VEGA12_POWERTUNE_H_
-
-enum vega12_didt_config_reg_type {
- VEGA12_CONFIGREG_DIDT = 0,
- VEGA12_CONFIGREG_GCCAC,
- VEGA12_CONFIGREG_SECAC
-};
-
-/* PowerContainment Features */
-#define POWERCONTAINMENT_FEATURE_DTE 0x00000001
-#define POWERCONTAINMENT_FEATURE_TDCLimit 0x00000002
-#define POWERCONTAINMENT_FEATURE_PkgPwrLimit 0x00000004
-
-struct vega12_didt_config_reg {
- uint32_t offset;
- uint32_t mask;
- uint32_t shift;
- uint32_t value;
-};
-
-int vega12_enable_power_containment(struct pp_hwmgr *hwmgr);
-int vega12_set_power_limit(struct pp_hwmgr *hwmgr, uint32_t n);
-int vega12_power_control_set_level(struct pp_hwmgr *hwmgr);
-int vega12_disable_power_containment(struct pp_hwmgr *hwmgr);
-
-int vega12_enable_didt_config(struct pp_hwmgr *hwmgr);
-int vega12_disable_didt_config(struct pp_hwmgr *hwmgr);
-
-#endif /* _VEGA12_POWERTUNE_H_ */
-
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_processpptables.c b/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_processpptables.c
index e7d794980b84..b34113f45904 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_processpptables.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_processpptables.c
@@ -208,9 +208,9 @@ static int append_vbios_pptable(struct pp_hwmgr *hwmgr, PPTable_t *ppsmc_pptable
ppsmc_pptable->LedPin1 = smc_dpm_table.ledpin1;
ppsmc_pptable->LedPin2 = smc_dpm_table.ledpin2;
- ppsmc_pptable->GfxclkSpreadEnabled = smc_dpm_table.gfxclkspreadenabled;
- ppsmc_pptable->GfxclkSpreadPercent = smc_dpm_table.gfxclkspreadpercent;
- ppsmc_pptable->GfxclkSpreadFreq = smc_dpm_table.gfxclkspreadfreq;
+ ppsmc_pptable->PllGfxclkSpreadEnabled = smc_dpm_table.pllgfxclkspreadenabled;
+ ppsmc_pptable->PllGfxclkSpreadPercent = smc_dpm_table.pllgfxclkspreadpercent;
+ ppsmc_pptable->PllGfxclkSpreadFreq = smc_dpm_table.pllgfxclkspreadfreq;
ppsmc_pptable->UclkSpreadEnabled = 0;
ppsmc_pptable->UclkSpreadPercent = smc_dpm_table.uclkspreadpercent;
@@ -220,6 +220,11 @@ static int append_vbios_pptable(struct pp_hwmgr *hwmgr, PPTable_t *ppsmc_pptable
ppsmc_pptable->SocclkSpreadPercent = smc_dpm_table.socclkspreadpercent;
ppsmc_pptable->SocclkSpreadFreq = smc_dpm_table.socclkspreadfreq;
+ ppsmc_pptable->AcgGfxclkSpreadEnabled = smc_dpm_table.acggfxclkspreadenabled;
+ ppsmc_pptable->AcgGfxclkSpreadPercent = smc_dpm_table.acggfxclkspreadpercent;
+ ppsmc_pptable->AcgGfxclkSpreadFreq = smc_dpm_table.acggfxclkspreadfreq;
+
+
return 0;
}
diff --git a/drivers/gpu/drm/amd/powerplay/inc/vega12/smu9_driver_if.h b/drivers/gpu/drm/amd/powerplay/inc/vega12/smu9_driver_if.h
index cd2e503a87da..2f8a3b983cce 100644
--- a/drivers/gpu/drm/amd/powerplay/inc/vega12/smu9_driver_if.h
+++ b/drivers/gpu/drm/amd/powerplay/inc/vega12/smu9_driver_if.h
@@ -127,7 +127,7 @@
#define FEATURE_GFX_EDC_MASK (1 << FEATURE_GFX_EDC_BIT )
#define FEATURE_GFXOFF_MASK (1 << FEATURE_GFXOFF_BIT )
#define FEATURE_CG_MASK (1 << FEATURE_CG_BIT )
-#define FEATURE_ACG_MASK (1 << FEATURE_ACG_BIT )
+#define FEATURE_ACG_MASK (1 << FEATURE_ACG_BIT)
#define FEATURE_SPARE_29_MASK (1 << FEATURE_SPARE_29_BIT )
#define FEATURE_SPARE_30_MASK (1 << FEATURE_SPARE_30_BIT )
#define FEATURE_SPARE_31_MASK (1 << FEATURE_SPARE_31_BIT )
@@ -412,8 +412,10 @@ typedef struct {
QuadraticInt_t ReservedEquation2;
QuadraticInt_t ReservedEquation3;
+ uint16_t MinVoltageUlvGfx;
+ uint16_t MinVoltageUlvSoc;
- uint32_t Reserved[15];
+ uint32_t Reserved[14];
@@ -481,9 +483,9 @@ typedef struct {
uint8_t padding8_4;
- uint8_t GfxclkSpreadEnabled;
- uint8_t GfxclkSpreadPercent;
- uint16_t GfxclkSpreadFreq;
+ uint8_t PllGfxclkSpreadEnabled;
+ uint8_t PllGfxclkSpreadPercent;
+ uint16_t PllGfxclkSpreadFreq;
uint8_t UclkSpreadEnabled;
uint8_t UclkSpreadPercent;
@@ -493,7 +495,11 @@ typedef struct {
uint8_t SocclkSpreadPercent;
uint16_t SocclkSpreadFreq;
- uint32_t BoardReserved[3];
+ uint8_t AcgGfxclkSpreadEnabled;
+ uint8_t AcgGfxclkSpreadPercent;
+ uint16_t AcgGfxclkSpreadFreq;
+
+ uint32_t BoardReserved[10];
uint32_t MmHubPadding[7];
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/vega12_smumgr.c b/drivers/gpu/drm/amd/powerplay/smumgr/vega12_smumgr.c
index 55cd204c1789..651a3f28734b 100644
--- a/drivers/gpu/drm/amd/powerplay/smumgr/vega12_smumgr.c
+++ b/drivers/gpu/drm/amd/powerplay/smumgr/vega12_smumgr.c
@@ -30,8 +30,7 @@
#include "ppatomctrl.h"
#include "pp_debug.h"
-#include "smu_ucode_xfer_vi.h"
-#include "smu7_smumgr.h"
+
/* MP Apertures */
#define MP0_Public 0x03800000
@@ -392,8 +391,7 @@ static int vega12_smu_init(struct pp_hwmgr *hwmgr)
struct cgs_firmware_info info = {0};
int ret;
- ret = cgs_get_firmware_info(hwmgr->device,
- smu7_convert_fw_type_to_cgs(UCODE_ID_SMU),
+ ret = cgs_get_firmware_info(hwmgr->device, CGS_UCODE_ID_SMU,
&info);
if (ret || !info.kptr)
return -EINVAL;
diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c
index 86789f8918a4..7ab36042a822 100644
--- a/drivers/gpu/drm/bridge/sil-sii8620.c
+++ b/drivers/gpu/drm/bridge/sil-sii8620.c
@@ -17,6 +17,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/extcon.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
@@ -25,6 +26,7 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/of_graph.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
@@ -81,6 +83,10 @@ struct sii8620 {
struct edid *edid;
unsigned int gen2_write_burst:1;
enum sii8620_mt_state mt_state;
+ struct extcon_dev *extcon;
+ struct notifier_block extcon_nb;
+ struct work_struct extcon_wq;
+ int cable_state;
struct list_head mt_queue;
struct {
int r_size;
@@ -2170,6 +2176,77 @@ static void sii8620_init_rcp_input_dev(struct sii8620 *ctx)
ctx->rc_dev = rc_dev;
}
+static void sii8620_cable_out(struct sii8620 *ctx)
+{
+ disable_irq(to_i2c_client(ctx->dev)->irq);
+ sii8620_hw_off(ctx);
+}
+
+static void sii8620_extcon_work(struct work_struct *work)
+{
+ struct sii8620 *ctx =
+ container_of(work, struct sii8620, extcon_wq);
+ int state = extcon_get_state(ctx->extcon, EXTCON_DISP_MHL);
+
+ if (state == ctx->cable_state)
+ return;
+
+ ctx->cable_state = state;
+
+ if (state > 0)
+ sii8620_cable_in(ctx);
+ else
+ sii8620_cable_out(ctx);
+}
+
+static int sii8620_extcon_notifier(struct notifier_block *self,
+ unsigned long event, void *ptr)
+{
+ struct sii8620 *ctx =
+ container_of(self, struct sii8620, extcon_nb);
+
+ schedule_work(&ctx->extcon_wq);
+
+ return NOTIFY_DONE;
+}
+
+static int sii8620_extcon_init(struct sii8620 *ctx)
+{
+ struct extcon_dev *edev;
+ struct device_node *musb, *muic;
+ int ret;
+
+ /* get micro-USB connector node */
+ musb = of_graph_get_remote_node(ctx->dev->of_node, 1, -1);
+ /* next get micro-USB Interface Controller node */
+ muic = of_get_next_parent(musb);
+
+ if (!muic) {
+ dev_info(ctx->dev, "no extcon found, switching to 'always on' mode\n");
+ return 0;
+ }
+
+ edev = extcon_find_edev_by_node(muic);
+ of_node_put(muic);
+ if (IS_ERR(edev)) {
+ if (PTR_ERR(edev) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ dev_err(ctx->dev, "Invalid or missing extcon\n");
+ return PTR_ERR(edev);
+ }
+
+ ctx->extcon = edev;
+ ctx->extcon_nb.notifier_call = sii8620_extcon_notifier;
+ INIT_WORK(&ctx->extcon_wq, sii8620_extcon_work);
+ ret = extcon_register_notifier(edev, EXTCON_DISP_MHL, &ctx->extcon_nb);
+ if (ret) {
+ dev_err(ctx->dev, "failed to register notifier for MHL\n");
+ return ret;
+ }
+
+ return 0;
+}
+
static inline struct sii8620 *bridge_to_sii8620(struct drm_bridge *bridge)
{
return container_of(bridge, struct sii8620, bridge);
@@ -2302,13 +2379,20 @@ static int sii8620_probe(struct i2c_client *client,
if (ret)
return ret;
+ ret = sii8620_extcon_init(ctx);
+ if (ret < 0) {
+ dev_err(ctx->dev, "failed to initialize EXTCON\n");
+ return ret;
+ }
+
i2c_set_clientdata(client, ctx);
ctx->bridge.funcs = &sii8620_bridge_funcs;
ctx->bridge.of_node = dev->of_node;
drm_bridge_add(&ctx->bridge);
- sii8620_cable_in(ctx);
+ if (!ctx->extcon)
+ sii8620_cable_in(ctx);
return 0;
}
@@ -2317,8 +2401,15 @@ static int sii8620_remove(struct i2c_client *client)
{
struct sii8620 *ctx = i2c_get_clientdata(client);
- disable_irq(to_i2c_client(ctx->dev)->irq);
- sii8620_hw_off(ctx);
+ if (ctx->extcon) {
+ extcon_unregister_notifier(ctx->extcon, EXTCON_DISP_MHL,
+ &ctx->extcon_nb);
+ flush_work(&ctx->extcon_wq);
+ if (ctx->cable_state > 0)
+ sii8620_cable_out(ctx);
+ } else {
+ sii8620_cable_out(ctx);
+ }
drm_bridge_remove(&ctx->bridge);
return 0;
diff --git a/drivers/gpu/drm/drm_color_mgmt.c b/drivers/gpu/drm/drm_color_mgmt.c
index 4ff064623836..b97e2de2c029 100644
--- a/drivers/gpu/drm/drm_color_mgmt.c
+++ b/drivers/gpu/drm/drm_color_mgmt.c
@@ -417,8 +417,8 @@ int drm_plane_create_color_properties(struct drm_plane *plane,
{
struct drm_device *dev = plane->dev;
struct drm_property *prop;
- struct drm_prop_enum_list enum_list[max(DRM_COLOR_ENCODING_MAX,
- DRM_COLOR_RANGE_MAX)];
+ struct drm_prop_enum_list enum_list[max_t(int, DRM_COLOR_ENCODING_MAX,
+ DRM_COLOR_RANGE_MAX)];
int i, len;
if (WARN_ON(supported_encodings == 0 ||
diff --git a/drivers/gpu/drm/drm_dp_aux_dev.c b/drivers/gpu/drm/drm_dp_aux_dev.c
index 053044201e31..0e4f25d63fd2 100644
--- a/drivers/gpu/drm/drm_dp_aux_dev.c
+++ b/drivers/gpu/drm/drm_dp_aux_dev.c
@@ -177,8 +177,9 @@ static ssize_t auxdev_read_iter(struct kiocb *iocb, struct iov_iter *to)
res = pos - iocb->ki_pos;
iocb->ki_pos = pos;
- atomic_dec(&aux_dev->usecount);
- wake_up_atomic_t(&aux_dev->usecount);
+ if (atomic_dec_and_test(&aux_dev->usecount))
+ wake_up_var(&aux_dev->usecount);
+
return res;
}
@@ -218,8 +219,9 @@ static ssize_t auxdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
res = pos - iocb->ki_pos;
iocb->ki_pos = pos;
- atomic_dec(&aux_dev->usecount);
- wake_up_atomic_t(&aux_dev->usecount);
+ if (atomic_dec_and_test(&aux_dev->usecount))
+ wake_up_var(&aux_dev->usecount);
+
return res;
}
@@ -277,8 +279,7 @@ void drm_dp_aux_unregister_devnode(struct drm_dp_aux *aux)
mutex_unlock(&aux_idr_mutex);
atomic_dec(&aux_dev->usecount);
- wait_on_atomic_t(&aux_dev->usecount, atomic_t_wait,
- TASK_UNINTERRUPTIBLE);
+ wait_var_event(&aux_dev->usecount, !atomic_read(&aux_dev->usecount));
minor = aux_dev->index;
if (aux_dev->dev)
diff --git a/drivers/gpu/drm/drm_dp_dual_mode_helper.c b/drivers/gpu/drm/drm_dp_dual_mode_helper.c
index 02a50929af67..e7f4fe2848a5 100644
--- a/drivers/gpu/drm/drm_dp_dual_mode_helper.c
+++ b/drivers/gpu/drm/drm_dp_dual_mode_helper.c
@@ -350,19 +350,44 @@ int drm_dp_dual_mode_set_tmds_output(enum drm_dp_dual_mode_type type,
{
uint8_t tmds_oen = enable ? 0 : DP_DUAL_MODE_TMDS_DISABLE;
ssize_t ret;
+ int retry;
if (type < DRM_DP_DUAL_MODE_TYPE2_DVI)
return 0;
- ret = drm_dp_dual_mode_write(adapter, DP_DUAL_MODE_TMDS_OEN,
- &tmds_oen, sizeof(tmds_oen));
- if (ret) {
- DRM_DEBUG_KMS("Failed to %s TMDS output buffers\n",
- enable ? "enable" : "disable");
- return ret;
+ /*
+ * LSPCON adapters in low-power state may ignore the first write, so
+ * read back and verify the written value a few times.
+ */
+ for (retry = 0; retry < 3; retry++) {
+ uint8_t tmp;
+
+ ret = drm_dp_dual_mode_write(adapter, DP_DUAL_MODE_TMDS_OEN,
+ &tmds_oen, sizeof(tmds_oen));
+ if (ret) {
+ DRM_DEBUG_KMS("Failed to %s TMDS output buffers (%d attempts)\n",
+ enable ? "enable" : "disable",
+ retry + 1);
+ return ret;
+ }
+
+ ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_TMDS_OEN,
+ &tmp, sizeof(tmp));
+ if (ret) {
+ DRM_DEBUG_KMS("I2C read failed during TMDS output buffer %s (%d attempts)\n",
+ enable ? "enabling" : "disabling",
+ retry + 1);
+ return ret;
+ }
+
+ if (tmp == tmds_oen)
+ return 0;
}
- return 0;
+ DRM_DEBUG_KMS("I2C write value mismatch during TMDS output buffer %s\n",
+ enable ? "enabling" : "disabling");
+
+ return -EIO;
}
EXPORT_SYMBOL(drm_dp_dual_mode_set_tmds_output);
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 42a7e871aa2a..40e1e24f2ff0 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -4461,6 +4461,7 @@ drm_reset_display_info(struct drm_connector *connector)
info->max_tmds_clock = 0;
info->dvi_dual = false;
info->has_hdmi_infoframe = false;
+ memset(&info->hdmi, 0, sizeof(info->hdmi));
info->non_desktop = 0;
}
@@ -4471,17 +4472,11 @@ u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edi
u32 quirks = edid_get_quirks(edid);
+ drm_reset_display_info(connector);
+
info->width_mm = edid->width_cm * 10;
info->height_mm = edid->height_cm * 10;
- /* driver figures it out in this case */
- info->bpc = 0;
- info->color_formats = 0;
- info->cea_rev = 0;
- info->max_tmds_clock = 0;
- info->dvi_dual = false;
- info->has_hdmi_infoframe = false;
-
info->non_desktop = !!(quirks & EDID_QUIRK_NON_DESKTOP);
DRM_DEBUG_KMS("non_desktop set to %d\n", info->non_desktop);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c
index 2379d732da67..7fcc1a7ab1a0 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fb.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c
@@ -18,6 +18,7 @@
#include <drm/drm_fb_helper.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
#include <uapi/drm/exynos_drm.h>
#include "exynos_drm_drv.h"
@@ -26,20 +27,6 @@
#include "exynos_drm_iommu.h"
#include "exynos_drm_crtc.h"
-#define to_exynos_fb(x) container_of(x, struct exynos_drm_fb, fb)
-
-/*
- * exynos specific framebuffer structure.
- *
- * @fb: drm framebuffer obejct.
- * @exynos_gem: array of exynos specific gem object containing a gem object.
- */
-struct exynos_drm_fb {
- struct drm_framebuffer fb;
- struct exynos_drm_gem *exynos_gem[MAX_FB_BUFFER];
- dma_addr_t dma_addr[MAX_FB_BUFFER];
-};
-
static int check_fb_gem_memory_type(struct drm_device *drm_dev,
struct exynos_drm_gem *exynos_gem)
{
@@ -66,40 +53,9 @@ static int check_fb_gem_memory_type(struct drm_device *drm_dev,
return 0;
}
-static void exynos_drm_fb_destroy(struct drm_framebuffer *fb)
-{
- struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb);
- unsigned int i;
-
- drm_framebuffer_cleanup(fb);
-
- for (i = 0; i < ARRAY_SIZE(exynos_fb->exynos_gem); i++) {
- struct drm_gem_object *obj;
-
- if (exynos_fb->exynos_gem[i] == NULL)
- continue;
-
- obj = &exynos_fb->exynos_gem[i]->base;
- drm_gem_object_unreference_unlocked(obj);
- }
-
- kfree(exynos_fb);
- exynos_fb = NULL;
-}
-
-static int exynos_drm_fb_create_handle(struct drm_framebuffer *fb,
- struct drm_file *file_priv,
- unsigned int *handle)
-{
- struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb);
-
- return drm_gem_handle_create(file_priv,
- &exynos_fb->exynos_gem[0]->base, handle);
-}
-
static const struct drm_framebuffer_funcs exynos_drm_fb_funcs = {
- .destroy = exynos_drm_fb_destroy,
- .create_handle = exynos_drm_fb_create_handle,
+ .destroy = drm_gem_fb_destroy,
+ .create_handle = drm_gem_fb_create_handle,
};
struct drm_framebuffer *
@@ -108,12 +64,12 @@ exynos_drm_framebuffer_init(struct drm_device *dev,
struct exynos_drm_gem **exynos_gem,
int count)
{
- struct exynos_drm_fb *exynos_fb;
+ struct drm_framebuffer *fb;
int i;
int ret;
- exynos_fb = kzalloc(sizeof(*exynos_fb), GFP_KERNEL);
- if (!exynos_fb)
+ fb = kzalloc(sizeof(*fb), GFP_KERNEL);
+ if (!fb)
return ERR_PTR(-ENOMEM);
for (i = 0; i < count; i++) {
@@ -121,23 +77,21 @@ exynos_drm_framebuffer_init(struct drm_device *dev,
if (ret < 0)
goto err;
- exynos_fb->exynos_gem[i] = exynos_gem[i];
- exynos_fb->dma_addr[i] = exynos_gem[i]->dma_addr
- + mode_cmd->offsets[i];
+ fb->obj[i] = &exynos_gem[i]->base;
}
- drm_helper_mode_fill_fb_struct(dev, &exynos_fb->fb, mode_cmd);
+ drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd);
- ret = drm_framebuffer_init(dev, &exynos_fb->fb, &exynos_drm_fb_funcs);
+ ret = drm_framebuffer_init(dev, fb, &exynos_drm_fb_funcs);
if (ret < 0) {
DRM_ERROR("failed to initialize framebuffer\n");
goto err;
}
- return &exynos_fb->fb;
+ return fb;
err:
- kfree(exynos_fb);
+ kfree(fb);
return ERR_PTR(ret);
}
@@ -191,12 +145,13 @@ err:
dma_addr_t exynos_drm_fb_dma_addr(struct drm_framebuffer *fb, int index)
{
- struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb);
+ struct exynos_drm_gem *exynos_gem;
if (WARN_ON_ONCE(index >= MAX_FB_BUFFER))
return 0;
- return exynos_fb->dma_addr[index];
+ exynos_gem = to_exynos_gem(fb->obj[index]);
+ return exynos_gem->dma_addr + fb->offsets[index];
}
static struct drm_mode_config_helper_funcs exynos_drm_mode_config_helpers = {
diff --git a/drivers/gpu/drm/i915/Kconfig.debug b/drivers/gpu/drm/i915/Kconfig.debug
index 8c7972df9f0f..9de8b1c51a5c 100644
--- a/drivers/gpu/drm/i915/Kconfig.debug
+++ b/drivers/gpu/drm/i915/Kconfig.debug
@@ -25,6 +25,7 @@ config DRM_I915_DEBUG
select X86_MSR # used by igt/pm_rpm
select DRM_VGEM # used by igt/prime_vgem (dmabuf interop checks)
select DRM_DEBUG_MM if DRM=y
+ select STACKDEPOT if DRM=y # for DRM_DEBUG_MM
select DRM_DEBUG_SELFTEST
select SW_SYNC # signaling validation framework (igt/syncobj*)
select DRM_I915_SW_FENCE_DEBUG_OBJECTS
@@ -89,6 +90,18 @@ config DRM_I915_SW_FENCE_CHECK_DAG
If in doubt, say "N".
+config DRM_I915_DEBUG_GUC
+ bool "Enable additional driver debugging for GuC"
+ depends on DRM_I915
+ default n
+ help
+ Choose this option to turn on extra driver debugging that may affect
+ performance but will help resolve GuC related issues.
+
+ Recommended for driver developers only.
+
+ If in doubt, say "N".
+
config DRM_I915_SELFTEST
bool "Enable selftests upon driver load"
depends on DRM_I915
diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index 4eee91a3a236..9bee52a949a9 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -12,7 +12,7 @@
# Note the danger in using -Wall -Wextra is that when CI updates gcc we
# will most likely get a sudden build breakage... Hopefully we will fix
# new warnings before CI updates!
-subdir-ccflags-y := -Wall -Wextra
+subdir-ccflags-y := -Wall -Wextra -Wvla
subdir-ccflags-y += $(call cc-disable-warning, unused-parameter)
subdir-ccflags-y += $(call cc-disable-warning, type-limits)
subdir-ccflags-y += $(call cc-disable-warning, missing-field-initializers)
@@ -43,7 +43,8 @@ i915-y := i915_drv.o \
intel_csr.o \
intel_device_info.o \
intel_pm.o \
- intel_runtime_pm.o
+ intel_runtime_pm.o \
+ intel_workarounds.o
i915-$(CONFIG_COMPAT) += i915_ioc32.o
i915-$(CONFIG_DEBUG_FS) += i915_debugfs.o intel_pipe_crc.o
@@ -79,7 +80,8 @@ i915-y += i915_cmd_parser.o \
intel_lrc.o \
intel_mocs.o \
intel_ringbuffer.o \
- intel_uncore.o
+ intel_uncore.o \
+ intel_wopcm.o
# general-purpose microcontroller (GuC) support
i915-y += intel_uc.o \
@@ -171,7 +173,8 @@ i915-y += i915_perf.o \
i915_oa_glk.o \
i915_oa_cflgt2.o \
i915_oa_cflgt3.o \
- i915_oa_cnl.o
+ i915_oa_cnl.o \
+ i915_oa_icl.o
ifeq ($(CONFIG_DRM_I915_GVT),y)
i915-y += intel_gvt.o
diff --git a/drivers/gpu/drm/i915/gvt/cmd_parser.c b/drivers/gpu/drm/i915/gvt/cmd_parser.c
index db6b94dda5df..d85939bd7b47 100644
--- a/drivers/gpu/drm/i915/gvt/cmd_parser.c
+++ b/drivers/gpu/drm/i915/gvt/cmd_parser.c
@@ -1080,6 +1080,7 @@ static int cmd_handler_mi_user_interrupt(struct parser_exec_state *s)
{
set_bit(cmd_interrupt_events[s->ring_id].mi_user_interrupt,
s->workload->pending_events);
+ patch_value(s, cmd_ptr(s, 0), MI_NOOP);
return 0;
}
diff --git a/drivers/gpu/drm/i915/gvt/debugfs.c b/drivers/gpu/drm/i915/gvt/debugfs.c
index 32a66dfdf112..f7d0078eb61b 100644
--- a/drivers/gpu/drm/i915/gvt/debugfs.c
+++ b/drivers/gpu/drm/i915/gvt/debugfs.c
@@ -122,18 +122,7 @@ static int vgpu_mmio_diff_show(struct seq_file *s, void *unused)
seq_printf(s, "Total: %d, Diff: %d\n", param.total, param.diff);
return 0;
}
-
-static int vgpu_mmio_diff_open(struct inode *inode, struct file *file)
-{
- return single_open(file, vgpu_mmio_diff_show, inode->i_private);
-}
-
-static const struct file_operations vgpu_mmio_diff_fops = {
- .open = vgpu_mmio_diff_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(vgpu_mmio_diff);
/**
* intel_gvt_debugfs_add_vgpu - register debugfs entries for a vGPU
diff --git a/drivers/gpu/drm/i915/gvt/display.c b/drivers/gpu/drm/i915/gvt/display.c
index dd96ffc878ac..6d8180e8d1e2 100644
--- a/drivers/gpu/drm/i915/gvt/display.c
+++ b/drivers/gpu/drm/i915/gvt/display.c
@@ -169,6 +169,8 @@ static u8 dpcd_fix_data[DPCD_HEADER_SIZE] = {
static void emulate_monitor_status_change(struct intel_vgpu *vgpu)
{
struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv;
+ int pipe;
+
vgpu_vreg_t(vgpu, SDEISR) &= ~(SDE_PORTB_HOTPLUG_CPT |
SDE_PORTC_HOTPLUG_CPT |
SDE_PORTD_HOTPLUG_CPT);
@@ -267,6 +269,14 @@ static void emulate_monitor_status_change(struct intel_vgpu *vgpu)
if (IS_BROADWELL(dev_priv))
vgpu_vreg_t(vgpu, PCH_ADPA) &= ~ADPA_CRT_HOTPLUG_MONITOR_MASK;
+ /* Disable Primary/Sprite/Cursor plane */
+ for_each_pipe(dev_priv, pipe) {
+ vgpu_vreg_t(vgpu, DSPCNTR(pipe)) &= ~DISPLAY_PLANE_ENABLE;
+ vgpu_vreg_t(vgpu, SPRCTL(pipe)) &= ~SPRITE_ENABLE;
+ vgpu_vreg_t(vgpu, CURCNTR(pipe)) &= ~CURSOR_MODE;
+ vgpu_vreg_t(vgpu, CURCNTR(pipe)) |= CURSOR_MODE_DISABLE;
+ }
+
vgpu_vreg_t(vgpu, PIPECONF(PIPE_A)) |= PIPECONF_ENABLE;
}
diff --git a/drivers/gpu/drm/i915/gvt/dmabuf.c b/drivers/gpu/drm/i915/gvt/dmabuf.c
index b555eb26f9ce..6f4f8e941fc2 100644
--- a/drivers/gpu/drm/i915/gvt/dmabuf.c
+++ b/drivers/gpu/drm/i915/gvt/dmabuf.c
@@ -323,6 +323,7 @@ static void update_fb_info(struct vfio_device_gfx_plane_info *gvt_dmabuf,
struct intel_vgpu_fb_info *fb_info)
{
gvt_dmabuf->drm_format = fb_info->drm_format;
+ gvt_dmabuf->drm_format_mod = fb_info->drm_format_mod;
gvt_dmabuf->width = fb_info->width;
gvt_dmabuf->height = fb_info->height;
gvt_dmabuf->stride = fb_info->stride;
diff --git a/drivers/gpu/drm/i915/gvt/fb_decoder.c b/drivers/gpu/drm/i915/gvt/fb_decoder.c
index 6b50fe78dc1b..1c120683e958 100644
--- a/drivers/gpu/drm/i915/gvt/fb_decoder.c
+++ b/drivers/gpu/drm/i915/gvt/fb_decoder.c
@@ -245,16 +245,13 @@ int intel_vgpu_decode_primary_plane(struct intel_vgpu *vgpu,
plane->hw_format = fmt;
plane->base = vgpu_vreg_t(vgpu, DSPSURF(pipe)) & I915_GTT_PAGE_MASK;
- if (!intel_gvt_ggtt_validate_range(vgpu, plane->base, 0)) {
- gvt_vgpu_err("invalid gma address: %lx\n",
- (unsigned long)plane->base);
+ if (!intel_gvt_ggtt_validate_range(vgpu, plane->base, 0))
return -EINVAL;
- }
plane->base_gpa = intel_vgpu_gma_to_gpa(vgpu->gtt.ggtt_mm, plane->base);
if (plane->base_gpa == INTEL_GVT_INVALID_ADDR) {
- gvt_vgpu_err("invalid gma address: %lx\n",
- (unsigned long)plane->base);
+ gvt_vgpu_err("Translate primary plane gma 0x%x to gpa fail\n",
+ plane->base);
return -EINVAL;
}
@@ -371,16 +368,13 @@ int intel_vgpu_decode_cursor_plane(struct intel_vgpu *vgpu,
alpha_plane, alpha_force);
plane->base = vgpu_vreg_t(vgpu, CURBASE(pipe)) & I915_GTT_PAGE_MASK;
- if (!intel_gvt_ggtt_validate_range(vgpu, plane->base, 0)) {
- gvt_vgpu_err("invalid gma address: %lx\n",
- (unsigned long)plane->base);
+ if (!intel_gvt_ggtt_validate_range(vgpu, plane->base, 0))
return -EINVAL;
- }
plane->base_gpa = intel_vgpu_gma_to_gpa(vgpu->gtt.ggtt_mm, plane->base);
if (plane->base_gpa == INTEL_GVT_INVALID_ADDR) {
- gvt_vgpu_err("invalid gma address: %lx\n",
- (unsigned long)plane->base);
+ gvt_vgpu_err("Translate cursor plane gma 0x%x to gpa fail\n",
+ plane->base);
return -EINVAL;
}
@@ -476,16 +470,13 @@ int intel_vgpu_decode_sprite_plane(struct intel_vgpu *vgpu,
plane->drm_format = drm_format;
plane->base = vgpu_vreg_t(vgpu, SPRSURF(pipe)) & I915_GTT_PAGE_MASK;
- if (!intel_gvt_ggtt_validate_range(vgpu, plane->base, 0)) {
- gvt_vgpu_err("invalid gma address: %lx\n",
- (unsigned long)plane->base);
+ if (!intel_gvt_ggtt_validate_range(vgpu, plane->base, 0))
return -EINVAL;
- }
plane->base_gpa = intel_vgpu_gma_to_gpa(vgpu->gtt.ggtt_mm, plane->base);
if (plane->base_gpa == INTEL_GVT_INVALID_ADDR) {
- gvt_vgpu_err("invalid gma address: %lx\n",
- (unsigned long)plane->base);
+ gvt_vgpu_err("Translate sprite plane gma 0x%x to gpa fail\n",
+ plane->base);
return -EINVAL;
}
diff --git a/drivers/gpu/drm/i915/gvt/gtt.c b/drivers/gpu/drm/i915/gvt/gtt.c
index d29281231507..78e55aafc8bc 100644
--- a/drivers/gpu/drm/i915/gvt/gtt.c
+++ b/drivers/gpu/drm/i915/gvt/gtt.c
@@ -530,6 +530,16 @@ static void ggtt_set_guest_entry(struct intel_vgpu_mm *mm,
false, 0, mm->vgpu);
}
+static void ggtt_get_host_entry(struct intel_vgpu_mm *mm,
+ struct intel_gvt_gtt_entry *entry, unsigned long index)
+{
+ struct intel_gvt_gtt_pte_ops *pte_ops = mm->vgpu->gvt->gtt.pte_ops;
+
+ GEM_BUG_ON(mm->type != INTEL_GVT_MM_GGTT);
+
+ pte_ops->get_entry(NULL, entry, index, false, 0, mm->vgpu);
+}
+
static void ggtt_set_host_entry(struct intel_vgpu_mm *mm,
struct intel_gvt_gtt_entry *entry, unsigned long index)
{
@@ -1818,6 +1828,18 @@ int intel_vgpu_emulate_ggtt_mmio_read(struct intel_vgpu *vgpu, unsigned int off,
return ret;
}
+static void ggtt_invalidate_pte(struct intel_vgpu *vgpu,
+ struct intel_gvt_gtt_entry *entry)
+{
+ struct intel_gvt_gtt_pte_ops *pte_ops = vgpu->gvt->gtt.pte_ops;
+ unsigned long pfn;
+
+ pfn = pte_ops->get_pfn(entry);
+ if (pfn != vgpu->gvt->gtt.scratch_mfn)
+ intel_gvt_hypervisor_dma_unmap_guest_page(vgpu,
+ pfn << PAGE_SHIFT);
+}
+
static int emulate_ggtt_mmio_write(struct intel_vgpu *vgpu, unsigned int off,
void *p_data, unsigned int bytes)
{
@@ -1844,10 +1866,10 @@ static int emulate_ggtt_mmio_write(struct intel_vgpu *vgpu, unsigned int off,
memcpy((void *)&e.val64 + (off & (info->gtt_entry_size - 1)), p_data,
bytes);
- m = e;
if (ops->test_present(&e)) {
gfn = ops->get_pfn(&e);
+ m = e;
/* one PTE update may be issued in multiple writes and the
* first write may not construct a valid gfn
@@ -1868,8 +1890,12 @@ static int emulate_ggtt_mmio_write(struct intel_vgpu *vgpu, unsigned int off,
ops->set_pfn(&m, gvt->gtt.scratch_mfn);
} else
ops->set_pfn(&m, dma_addr >> PAGE_SHIFT);
- } else
+ } else {
+ ggtt_get_host_entry(ggtt_mm, &m, g_gtt_index);
+ ggtt_invalidate_pte(vgpu, &m);
ops->set_pfn(&m, gvt->gtt.scratch_mfn);
+ ops->clear_present(&m);
+ }
out:
ggtt_set_host_entry(ggtt_mm, &m, g_gtt_index);
@@ -2030,7 +2056,7 @@ int intel_vgpu_init_gtt(struct intel_vgpu *vgpu)
return PTR_ERR(gtt->ggtt_mm);
}
- intel_vgpu_reset_ggtt(vgpu);
+ intel_vgpu_reset_ggtt(vgpu, false);
return create_scratch_page_tree(vgpu);
}
@@ -2315,17 +2341,19 @@ void intel_vgpu_invalidate_ppgtt(struct intel_vgpu *vgpu)
/**
* intel_vgpu_reset_ggtt - reset the GGTT entry
* @vgpu: a vGPU
+ * @invalidate_old: invalidate old entries
*
* This function is called at the vGPU create stage
* to reset all the GGTT entries.
*
*/
-void intel_vgpu_reset_ggtt(struct intel_vgpu *vgpu)
+void intel_vgpu_reset_ggtt(struct intel_vgpu *vgpu, bool invalidate_old)
{
struct intel_gvt *gvt = vgpu->gvt;
struct drm_i915_private *dev_priv = gvt->dev_priv;
struct intel_gvt_gtt_pte_ops *pte_ops = vgpu->gvt->gtt.pte_ops;
struct intel_gvt_gtt_entry entry = {.type = GTT_TYPE_GGTT_PTE};
+ struct intel_gvt_gtt_entry old_entry;
u32 index;
u32 num_entries;
@@ -2334,13 +2362,23 @@ void intel_vgpu_reset_ggtt(struct intel_vgpu *vgpu)
index = vgpu_aperture_gmadr_base(vgpu) >> PAGE_SHIFT;
num_entries = vgpu_aperture_sz(vgpu) >> PAGE_SHIFT;
- while (num_entries--)
+ while (num_entries--) {
+ if (invalidate_old) {
+ ggtt_get_host_entry(vgpu->gtt.ggtt_mm, &old_entry, index);
+ ggtt_invalidate_pte(vgpu, &old_entry);
+ }
ggtt_set_host_entry(vgpu->gtt.ggtt_mm, &entry, index++);
+ }
index = vgpu_hidden_gmadr_base(vgpu) >> PAGE_SHIFT;
num_entries = vgpu_hidden_sz(vgpu) >> PAGE_SHIFT;
- while (num_entries--)
+ while (num_entries--) {
+ if (invalidate_old) {
+ ggtt_get_host_entry(vgpu->gtt.ggtt_mm, &old_entry, index);
+ ggtt_invalidate_pte(vgpu, &old_entry);
+ }
ggtt_set_host_entry(vgpu->gtt.ggtt_mm, &entry, index++);
+ }
ggtt_invalidate(dev_priv);
}
@@ -2360,5 +2398,5 @@ void intel_vgpu_reset_gtt(struct intel_vgpu *vgpu)
* removing the shadow pages.
*/
intel_vgpu_destroy_all_ppgtt_mm(vgpu);
- intel_vgpu_reset_ggtt(vgpu);
+ intel_vgpu_reset_ggtt(vgpu, true);
}
diff --git a/drivers/gpu/drm/i915/gvt/gtt.h b/drivers/gpu/drm/i915/gvt/gtt.h
index a8b369cd352b..3792f2b7f4ff 100644
--- a/drivers/gpu/drm/i915/gvt/gtt.h
+++ b/drivers/gpu/drm/i915/gvt/gtt.h
@@ -193,7 +193,7 @@ struct intel_vgpu_gtt {
extern int intel_vgpu_init_gtt(struct intel_vgpu *vgpu);
extern void intel_vgpu_clean_gtt(struct intel_vgpu *vgpu);
-void intel_vgpu_reset_ggtt(struct intel_vgpu *vgpu);
+void intel_vgpu_reset_ggtt(struct intel_vgpu *vgpu, bool invalidate_old);
void intel_vgpu_invalidate_ppgtt(struct intel_vgpu *vgpu);
extern int intel_gvt_init_gtt(struct intel_gvt *gvt);
diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c
index 8c5d5d005854..a33c1c3e4a21 100644
--- a/drivers/gpu/drm/i915/gvt/handlers.c
+++ b/drivers/gpu/drm/i915/gvt/handlers.c
@@ -1150,6 +1150,7 @@ static int handle_g2v_notification(struct intel_vgpu *vgpu, int notification)
switch (notification) {
case VGT_G2V_PPGTT_L3_PAGE_TABLE_CREATE:
root_entry_type = GTT_TYPE_PPGTT_ROOT_L3_ENTRY;
+ /* fall through */
case VGT_G2V_PPGTT_L4_PAGE_TABLE_CREATE:
mm = intel_vgpu_get_ppgtt_mm(vgpu, root_entry_type, pdps);
return PTR_ERR_OR_ZERO(mm);
diff --git a/drivers/gpu/drm/i915/gvt/kvmgt.c b/drivers/gpu/drm/i915/gvt/kvmgt.c
index c16a492449d7..1466d8769ec9 100644
--- a/drivers/gpu/drm/i915/gvt/kvmgt.c
+++ b/drivers/gpu/drm/i915/gvt/kvmgt.c
@@ -1301,7 +1301,7 @@ static long intel_vgpu_ioctl(struct mdev_device *mdev, unsigned int cmd,
}
- return 0;
+ return -ENOTTY;
}
static ssize_t
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index 89f7ff2c652e..2e6652a9bb9e 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -1215,20 +1215,20 @@ static int i915_frequency_info(struct seq_file *m, void *unused)
max_freq = (IS_GEN9_LP(dev_priv) ? rp_state_cap >> 0 :
rp_state_cap >> 16) & 0xff;
max_freq *= (IS_GEN9_BC(dev_priv) ||
- IS_CANNONLAKE(dev_priv) ? GEN9_FREQ_SCALER : 1);
+ INTEL_GEN(dev_priv) >= 10 ? GEN9_FREQ_SCALER : 1);
seq_printf(m, "Lowest (RPN) frequency: %dMHz\n",
intel_gpu_freq(dev_priv, max_freq));
max_freq = (rp_state_cap & 0xff00) >> 8;
max_freq *= (IS_GEN9_BC(dev_priv) ||
- IS_CANNONLAKE(dev_priv) ? GEN9_FREQ_SCALER : 1);
+ INTEL_GEN(dev_priv) >= 10 ? GEN9_FREQ_SCALER : 1);
seq_printf(m, "Nominal (RP1) frequency: %dMHz\n",
intel_gpu_freq(dev_priv, max_freq));
max_freq = (IS_GEN9_LP(dev_priv) ? rp_state_cap >> 16 :
rp_state_cap >> 0) & 0xff;
max_freq *= (IS_GEN9_BC(dev_priv) ||
- IS_CANNONLAKE(dev_priv) ? GEN9_FREQ_SCALER : 1);
+ INTEL_GEN(dev_priv) >= 10 ? GEN9_FREQ_SCALER : 1);
seq_printf(m, "Max non-overclocked (RP0) frequency: %dMHz\n",
intel_gpu_freq(dev_priv, max_freq));
seq_printf(m, "Max overclocked frequency: %dMHz\n",
@@ -1796,9 +1796,9 @@ static int i915_ring_freq_table(struct seq_file *m, void *unused)
{
struct drm_i915_private *dev_priv = node_to_i915(m->private);
struct intel_rps *rps = &dev_priv->gt_pm.rps;
- int ret = 0;
- int gpu_freq, ia_freq;
unsigned int max_gpu_freq, min_gpu_freq;
+ int gpu_freq, ia_freq;
+ int ret;
if (!HAS_LLC(dev_priv))
return -ENODEV;
@@ -1809,13 +1809,12 @@ static int i915_ring_freq_table(struct seq_file *m, void *unused)
if (ret)
goto out;
- if (IS_GEN9_BC(dev_priv) || IS_CANNONLAKE(dev_priv)) {
+ min_gpu_freq = rps->min_freq;
+ max_gpu_freq = rps->max_freq;
+ if (IS_GEN9_BC(dev_priv) || INTEL_GEN(dev_priv) >= 10) {
/* Convert GT frequency to 50 HZ units */
- min_gpu_freq = rps->min_freq_softlimit / GEN9_FREQ_SCALER;
- max_gpu_freq = rps->max_freq_softlimit / GEN9_FREQ_SCALER;
- } else {
- min_gpu_freq = rps->min_freq_softlimit;
- max_gpu_freq = rps->max_freq_softlimit;
+ min_gpu_freq /= GEN9_FREQ_SCALER;
+ max_gpu_freq /= GEN9_FREQ_SCALER;
}
seq_puts(m, "GPU freq (MHz)\tEffective CPU freq (MHz)\tEffective Ring freq (MHz)\n");
@@ -1828,7 +1827,7 @@ static int i915_ring_freq_table(struct seq_file *m, void *unused)
seq_printf(m, "%d\t\t%d\t\t\t\t%d\n",
intel_gpu_freq(dev_priv, (gpu_freq *
(IS_GEN9_BC(dev_priv) ||
- IS_CANNONLAKE(dev_priv) ?
+ INTEL_GEN(dev_priv) >= 10 ?
GEN9_FREQ_SCALER : 1))),
((ia_freq >> 0) & 0xff) * 100,
((ia_freq >> 8) & 0xff) * 100);
@@ -1923,8 +1922,8 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data)
static void describe_ctx_ring(struct seq_file *m, struct intel_ring *ring)
{
- seq_printf(m, " (ringbuffer, space: %d, head: %u, tail: %u)",
- ring->space, ring->head, ring->tail);
+ seq_printf(m, " (ringbuffer, space: %d, head: %u, tail: %u, emit: %u)",
+ ring->space, ring->head, ring->tail, ring->emit);
}
static int i915_context_status(struct seq_file *m, void *unused)
@@ -2326,30 +2325,45 @@ static int i915_guc_load_status_info(struct seq_file *m, void *data)
return 0;
}
-static void i915_guc_log_info(struct seq_file *m,
- struct drm_i915_private *dev_priv)
+static const char *
+stringify_guc_log_type(enum guc_log_buffer_type type)
{
- struct intel_guc *guc = &dev_priv->guc;
+ switch (type) {
+ case GUC_ISR_LOG_BUFFER:
+ return "ISR";
+ case GUC_DPC_LOG_BUFFER:
+ return "DPC";
+ case GUC_CRASH_DUMP_LOG_BUFFER:
+ return "CRASH";
+ default:
+ MISSING_CASE(type);
+ }
- seq_puts(m, "\nGuC logging stats:\n");
+ return "";
+}
- seq_printf(m, "\tISR: flush count %10u, overflow count %10u\n",
- guc->log.flush_count[GUC_ISR_LOG_BUFFER],
- guc->log.total_overflow_count[GUC_ISR_LOG_BUFFER]);
+static void i915_guc_log_info(struct seq_file *m,
+ struct drm_i915_private *dev_priv)
+{
+ struct intel_guc_log *log = &dev_priv->guc.log;
+ enum guc_log_buffer_type type;
- seq_printf(m, "\tDPC: flush count %10u, overflow count %10u\n",
- guc->log.flush_count[GUC_DPC_LOG_BUFFER],
- guc->log.total_overflow_count[GUC_DPC_LOG_BUFFER]);
+ if (!intel_guc_log_relay_enabled(log)) {
+ seq_puts(m, "GuC log relay disabled\n");
+ return;
+ }
- seq_printf(m, "\tCRASH: flush count %10u, overflow count %10u\n",
- guc->log.flush_count[GUC_CRASH_DUMP_LOG_BUFFER],
- guc->log.total_overflow_count[GUC_CRASH_DUMP_LOG_BUFFER]);
+ seq_puts(m, "GuC logging stats:\n");
- seq_printf(m, "\tTotal flush interrupt count: %u\n",
- guc->log.flush_interrupt_count);
+ seq_printf(m, "\tRelay full count: %u\n",
+ log->relay.full_count);
- seq_printf(m, "\tCapture miss count: %u\n",
- guc->log.capture_miss_count);
+ for (type = GUC_ISR_LOG_BUFFER; type < GUC_MAX_LOG_BUFFER; type++) {
+ seq_printf(m, "\t%s:\tflush count %10u, overflow count %10u\n",
+ stringify_guc_log_type(type),
+ log->stats[type].flush,
+ log->stats[type].sampled_overflow);
+ }
}
static void i915_guc_client_info(struct seq_file *m,
@@ -2379,14 +2393,19 @@ static int i915_guc_info(struct seq_file *m, void *data)
struct drm_i915_private *dev_priv = node_to_i915(m->private);
const struct intel_guc *guc = &dev_priv->guc;
- if (!USES_GUC_SUBMISSION(dev_priv))
+ if (!USES_GUC(dev_priv))
return -ENODEV;
+ i915_guc_log_info(m, dev_priv);
+
+ if (!USES_GUC_SUBMISSION(dev_priv))
+ return 0;
+
GEM_BUG_ON(!guc->execbuf_client);
- seq_printf(m, "Doorbell map:\n");
+ seq_printf(m, "\nDoorbell map:\n");
seq_printf(m, "\t%*pb\n", GUC_NUM_DOORBELLS, guc->doorbell_bitmap);
- seq_printf(m, "Doorbell next cacheline: 0x%x\n\n", guc->db_cacheline);
+ seq_printf(m, "Doorbell next cacheline: 0x%x\n", guc->db_cacheline);
seq_printf(m, "\nGuC execbuf client @ %p:\n", guc->execbuf_client);
i915_guc_client_info(m, dev_priv, guc->execbuf_client);
@@ -2396,8 +2415,6 @@ static int i915_guc_info(struct seq_file *m, void *data)
i915_guc_client_info(m, dev_priv, guc->preempt_client);
}
- i915_guc_log_info(m, dev_priv);
-
/* Add more as required ... */
return 0;
@@ -2496,35 +2513,73 @@ static int i915_guc_log_dump(struct seq_file *m, void *data)
return 0;
}
-static int i915_guc_log_control_get(void *data, u64 *val)
+static int i915_guc_log_level_get(void *data, u64 *val)
{
struct drm_i915_private *dev_priv = data;
- if (!HAS_GUC(dev_priv))
+ if (!USES_GUC(dev_priv))
return -ENODEV;
- if (!dev_priv->guc.log.vma)
- return -EINVAL;
-
- *val = i915_modparams.guc_log_level;
+ *val = intel_guc_log_level_get(&dev_priv->guc.log);
return 0;
}
-static int i915_guc_log_control_set(void *data, u64 val)
+static int i915_guc_log_level_set(void *data, u64 val)
{
struct drm_i915_private *dev_priv = data;
- if (!HAS_GUC(dev_priv))
+ if (!USES_GUC(dev_priv))
return -ENODEV;
- return intel_guc_log_control(&dev_priv->guc, val);
+ return intel_guc_log_level_set(&dev_priv->guc.log, val);
}
-DEFINE_SIMPLE_ATTRIBUTE(i915_guc_log_control_fops,
- i915_guc_log_control_get, i915_guc_log_control_set,
+DEFINE_SIMPLE_ATTRIBUTE(i915_guc_log_level_fops,
+ i915_guc_log_level_get, i915_guc_log_level_set,
"%lld\n");
+static int i915_guc_log_relay_open(struct inode *inode, struct file *file)
+{
+ struct drm_i915_private *dev_priv = inode->i_private;
+
+ if (!USES_GUC(dev_priv))
+ return -ENODEV;
+
+ file->private_data = &dev_priv->guc.log;
+
+ return intel_guc_log_relay_open(&dev_priv->guc.log);
+}
+
+static ssize_t
+i915_guc_log_relay_write(struct file *filp,
+ const char __user *ubuf,
+ size_t cnt,
+ loff_t *ppos)
+{
+ struct intel_guc_log *log = filp->private_data;
+
+ intel_guc_log_relay_flush(log);
+
+ return cnt;
+}
+
+static int i915_guc_log_relay_release(struct inode *inode, struct file *file)
+{
+ struct drm_i915_private *dev_priv = inode->i_private;
+
+ intel_guc_log_relay_close(&dev_priv->guc.log);
+
+ return 0;
+}
+
+static const struct file_operations i915_guc_log_relay_fops = {
+ .owner = THIS_MODULE,
+ .open = i915_guc_log_relay_open,
+ .write = i915_guc_log_relay_write,
+ .release = i915_guc_log_relay_release,
+};
+
static const char *psr2_live_status(u32 val)
{
static const char * const live_status[] = {
@@ -2569,14 +2624,13 @@ static int i915_edp_psr_status(struct seq_file *m, void *data)
mutex_lock(&dev_priv->psr.lock);
seq_printf(m, "Enabled: %s\n", yesno((bool)dev_priv->psr.enabled));
- seq_printf(m, "Active: %s\n", yesno(dev_priv->psr.active));
seq_printf(m, "Busy frontbuffer bits: 0x%03x\n",
dev_priv->psr.busy_frontbuffer_bits);
seq_printf(m, "Re-enable work scheduled: %s\n",
yesno(work_busy(&dev_priv->psr.work.work)));
if (HAS_DDI(dev_priv)) {
- if (dev_priv->psr.psr2_support)
+ if (dev_priv->psr.psr2_enabled)
enabled = I915_READ(EDP_PSR2_CTL) & EDP_PSR2_ENABLE;
else
enabled = I915_READ(EDP_PSR_CTL) & EDP_PSR_ENABLE;
@@ -2624,7 +2678,7 @@ static int i915_edp_psr_status(struct seq_file *m, void *data)
seq_printf(m, "Performance_Counter: %u\n", psrperf);
}
- if (dev_priv->psr.psr2_support) {
+ if (dev_priv->psr.psr2_enabled) {
u32 psr2 = I915_READ(EDP_PSR2_STATUS);
seq_printf(m, "EDP_PSR2_STATUS: %x [%s]\n",
@@ -3231,7 +3285,8 @@ static int i915_shared_dplls_info(struct seq_file *m, void *unused)
for (i = 0; i < dev_priv->num_shared_dpll; i++) {
struct intel_shared_dpll *pll = &dev_priv->shared_dplls[i];
- seq_printf(m, "DPLL%i: %s, id: %i\n", i, pll->name, pll->id);
+ seq_printf(m, "DPLL%i: %s, id: %i\n", i, pll->info->name,
+ pll->info->id);
seq_printf(m, " crtc_mask: 0x%08x, active: 0x%x, on: %s\n",
pll->state.crtc_mask, pll->active_mask, yesno(pll->on));
seq_printf(m, " tracked hardware state:\n");
@@ -3567,7 +3622,8 @@ static ssize_t i915_displayport_test_active_write(struct file *file,
static int i915_displayport_test_active_show(struct seq_file *m, void *data)
{
- struct drm_device *dev = m->private;
+ struct drm_i915_private *dev_priv = m->private;
+ struct drm_device *dev = &dev_priv->drm;
struct drm_connector *connector;
struct drm_connector_list_iter conn_iter;
struct intel_dp *intel_dp;
@@ -3601,10 +3657,8 @@ static int i915_displayport_test_active_show(struct seq_file *m, void *data)
static int i915_displayport_test_active_open(struct inode *inode,
struct file *file)
{
- struct drm_i915_private *dev_priv = inode->i_private;
-
return single_open(file, i915_displayport_test_active_show,
- &dev_priv->drm);
+ inode->i_private);
}
static const struct file_operations i915_displayport_test_active_fops = {
@@ -3618,7 +3672,8 @@ static const struct file_operations i915_displayport_test_active_fops = {
static int i915_displayport_test_data_show(struct seq_file *m, void *data)
{
- struct drm_device *dev = m->private;
+ struct drm_i915_private *dev_priv = m->private;
+ struct drm_device *dev = &dev_priv->drm;
struct drm_connector *connector;
struct drm_connector_list_iter conn_iter;
struct intel_dp *intel_dp;
@@ -3657,26 +3712,12 @@ static int i915_displayport_test_data_show(struct seq_file *m, void *data)
return 0;
}
-static int i915_displayport_test_data_open(struct inode *inode,
- struct file *file)
-{
- struct drm_i915_private *dev_priv = inode->i_private;
-
- return single_open(file, i915_displayport_test_data_show,
- &dev_priv->drm);
-}
-
-static const struct file_operations i915_displayport_test_data_fops = {
- .owner = THIS_MODULE,
- .open = i915_displayport_test_data_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release
-};
+DEFINE_SHOW_ATTRIBUTE(i915_displayport_test_data);
static int i915_displayport_test_type_show(struct seq_file *m, void *data)
{
- struct drm_device *dev = m->private;
+ struct drm_i915_private *dev_priv = m->private;
+ struct drm_device *dev = &dev_priv->drm;
struct drm_connector *connector;
struct drm_connector_list_iter conn_iter;
struct intel_dp *intel_dp;
@@ -3703,23 +3744,7 @@ static int i915_displayport_test_type_show(struct seq_file *m, void *data)
return 0;
}
-
-static int i915_displayport_test_type_open(struct inode *inode,
- struct file *file)
-{
- struct drm_i915_private *dev_priv = inode->i_private;
-
- return single_open(file, i915_displayport_test_type_show,
- &dev_priv->drm);
-}
-
-static const struct file_operations i915_displayport_test_type_fops = {
- .owner = THIS_MODULE,
- .open = i915_displayport_test_type_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release
-};
+DEFINE_SHOW_ATTRIBUTE(i915_displayport_test_type);
static void wm_latency_show(struct seq_file *m, const uint16_t wm[8])
{
@@ -3987,8 +4012,8 @@ i915_wedged_set(void *data, u64 val)
engine->hangcheck.stalled = true;
}
- i915_handle_error(i915, val, "Manually set wedged engine mask = %llx",
- val);
+ i915_handle_error(i915, val, I915_ERROR_CAPTURE,
+ "Manually set wedged engine mask = %llx", val);
wait_on_bit(&i915->gpu_error.flags,
I915_RESET_HANDOFF,
@@ -4316,9 +4341,10 @@ DEFINE_SIMPLE_ATTRIBUTE(i915_cache_sharing_fops,
static void cherryview_sseu_device_status(struct drm_i915_private *dev_priv,
struct sseu_dev_info *sseu)
{
- int ss_max = 2;
+#define SS_MAX 2
+ const int ss_max = SS_MAX;
+ u32 sig1[SS_MAX], sig2[SS_MAX];
int ss;
- u32 sig1[ss_max], sig2[ss_max];
sig1[0] = I915_READ(CHV_POWER_SS0_SIG1);
sig1[1] = I915_READ(CHV_POWER_SS1_SIG1);
@@ -4342,15 +4368,16 @@ static void cherryview_sseu_device_status(struct drm_i915_private *dev_priv,
sseu->eu_per_subslice = max_t(unsigned int,
sseu->eu_per_subslice, eu_cnt);
}
+#undef SS_MAX
}
static void gen10_sseu_device_status(struct drm_i915_private *dev_priv,
struct sseu_dev_info *sseu)
{
+#define SS_MAX 6
const struct intel_device_info *info = INTEL_INFO(dev_priv);
+ u32 s_reg[SS_MAX], eu_reg[2 * SS_MAX], eu_mask[2];
int s, ss;
- u32 s_reg[info->sseu.max_slices];
- u32 eu_reg[2 * info->sseu.max_subslices], eu_mask[2];
for (s = 0; s < info->sseu.max_slices; s++) {
/*
@@ -4397,15 +4424,16 @@ static void gen10_sseu_device_status(struct drm_i915_private *dev_priv,
eu_cnt);
}
}
+#undef SS_MAX
}
static void gen9_sseu_device_status(struct drm_i915_private *dev_priv,
struct sseu_dev_info *sseu)
{
+#define SS_MAX 3
const struct intel_device_info *info = INTEL_INFO(dev_priv);
+ u32 s_reg[SS_MAX], eu_reg[2 * SS_MAX], eu_mask[2];
int s, ss;
- u32 s_reg[info->sseu.max_slices];
- u32 eu_reg[2 * info->sseu.max_subslices], eu_mask[2];
for (s = 0; s < info->sseu.max_slices; s++) {
s_reg[s] = I915_READ(GEN9_SLICE_PGCTL_ACK(s));
@@ -4452,6 +4480,7 @@ static void gen9_sseu_device_status(struct drm_i915_private *dev_priv,
eu_cnt);
}
}
+#undef SS_MAX
}
static void broadwell_sseu_device_status(struct drm_i915_private *dev_priv,
@@ -4703,6 +4732,67 @@ static int i915_drrs_ctl_set(void *data, u64 val)
DEFINE_SIMPLE_ATTRIBUTE(i915_drrs_ctl_fops, NULL, i915_drrs_ctl_set, "%llu\n");
+static ssize_t
+i915_fifo_underrun_reset_write(struct file *filp,
+ const char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ struct drm_i915_private *dev_priv = filp->private_data;
+ struct intel_crtc *intel_crtc;
+ struct drm_device *dev = &dev_priv->drm;
+ int ret;
+ bool reset;
+
+ ret = kstrtobool_from_user(ubuf, cnt, &reset);
+ if (ret)
+ return ret;
+
+ if (!reset)
+ return cnt;
+
+ for_each_intel_crtc(dev, intel_crtc) {
+ struct drm_crtc_commit *commit;
+ struct intel_crtc_state *crtc_state;
+
+ ret = drm_modeset_lock_single_interruptible(&intel_crtc->base.mutex);
+ if (ret)
+ return ret;
+
+ crtc_state = to_intel_crtc_state(intel_crtc->base.state);
+ commit = crtc_state->base.commit;
+ if (commit) {
+ ret = wait_for_completion_interruptible(&commit->hw_done);
+ if (!ret)
+ ret = wait_for_completion_interruptible(&commit->flip_done);
+ }
+
+ if (!ret && crtc_state->base.active) {
+ DRM_DEBUG_KMS("Re-arming FIFO underruns on pipe %c\n",
+ pipe_name(intel_crtc->pipe));
+
+ intel_crtc_arm_fifo_underrun(intel_crtc, crtc_state);
+ }
+
+ drm_modeset_unlock(&intel_crtc->base.mutex);
+
+ if (ret)
+ return ret;
+ }
+
+ ret = intel_fbc_reset_underrun(dev_priv);
+ if (ret)
+ return ret;
+
+ return cnt;
+}
+
+static const struct file_operations i915_fifo_underrun_reset_ops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .write = i915_fifo_underrun_reset_write,
+ .llseek = default_llseek,
+};
+
static const struct drm_info_list i915_debugfs_list[] = {
{"i915_capabilities", i915_capabilities, 0},
{"i915_gem_objects", i915_gem_object_info, 0},
@@ -4770,6 +4860,7 @@ static const struct i915_debugfs_files {
{"i915_error_state", &i915_error_state_fops},
{"i915_gpu_info", &i915_gpu_info_fops},
#endif
+ {"i915_fifo_underrun_reset", &i915_fifo_underrun_reset_ops},
{"i915_next_seqno", &i915_next_seqno_fops},
{"i915_display_crc_ctl", &i915_display_crc_ctl_fops},
{"i915_pri_wm_latency", &i915_pri_wm_latency_fops},
@@ -4779,7 +4870,8 @@ static const struct i915_debugfs_files {
{"i915_dp_test_data", &i915_displayport_test_data_fops},
{"i915_dp_test_type", &i915_displayport_test_type_fops},
{"i915_dp_test_active", &i915_displayport_test_active_fops},
- {"i915_guc_log_control", &i915_guc_log_control_fops},
+ {"i915_guc_log_level", &i915_guc_log_level_fops},
+ {"i915_guc_log_relay", &i915_guc_log_relay_fops},
{"i915_hpd_storm_ctl", &i915_hpd_storm_ctl_fops},
{"i915_ipc_status", &i915_ipc_status_fops},
{"i915_drrs_ctl", &i915_drrs_ctl_fops}
@@ -4876,19 +4968,7 @@ static int i915_dpcd_show(struct seq_file *m, void *data)
return 0;
}
-
-static int i915_dpcd_open(struct inode *inode, struct file *file)
-{
- return single_open(file, i915_dpcd_show, inode->i_private);
-}
-
-static const struct file_operations i915_dpcd_fops = {
- .owner = THIS_MODULE,
- .open = i915_dpcd_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(i915_dpcd);
static int i915_panel_show(struct seq_file *m, void *data)
{
@@ -4910,19 +4990,7 @@ static int i915_panel_show(struct seq_file *m, void *data)
return 0;
}
-
-static int i915_panel_open(struct inode *inode, struct file *file)
-{
- return single_open(file, i915_panel_show, inode->i_private);
-}
-
-static const struct file_operations i915_panel_fops = {
- .owner = THIS_MODULE,
- .open = i915_panel_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(i915_panel);
/**
* i915_debugfs_connector_add - add i915 specific connector debugfs files
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 154414832d86..c471a7528a50 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -377,9 +377,9 @@ static int i915_getparam_ioctl(struct drm_device *dev, void *data,
value = INTEL_INFO(dev_priv)->sseu.min_eu_in_pool;
break;
case I915_PARAM_HUC_STATUS:
- intel_runtime_pm_get(dev_priv);
- value = I915_READ(HUC_STATUS2) & HUC_FW_VERIFIED;
- intel_runtime_pm_put(dev_priv);
+ value = intel_huc_check_status(&dev_priv->huc);
+ if (value < 0)
+ return value;
break;
case I915_PARAM_MMAP_GTT_VERSION:
/* Though we've started our numbering from 1, and so class all
@@ -449,7 +449,10 @@ static int i915_getparam_ioctl(struct drm_device *dev, void *data,
static int i915_get_bridge_dev(struct drm_i915_private *dev_priv)
{
- dev_priv->bridge_dev = pci_get_bus_and_slot(0, PCI_DEVFN(0, 0));
+ int domain = pci_domain_nr(dev_priv->drm.pdev->bus);
+
+ dev_priv->bridge_dev =
+ pci_get_domain_bus_and_slot(domain, 0, PCI_DEVFN(0, 0));
if (!dev_priv->bridge_dev) {
DRM_ERROR("bridge device not found\n");
return -1;
@@ -692,11 +695,9 @@ static int i915_load_modeset_init(struct drm_device *dev)
if (ret)
goto cleanup_irq;
- intel_uc_init_fw(dev_priv);
-
ret = i915_gem_init(dev_priv);
if (ret)
- goto cleanup_uc;
+ goto cleanup_irq;
intel_setup_overlay(dev_priv);
@@ -716,8 +717,6 @@ cleanup_gem:
if (i915_gem_suspend(dev_priv))
DRM_ERROR("failed to idle hardware; continuing to unload!\n");
i915_gem_fini(dev_priv);
-cleanup_uc:
- intel_uc_fini_fw(dev_priv);
cleanup_irq:
drm_irq_uninstall(dev);
intel_teardown_gmbus(dev_priv);
@@ -919,16 +918,21 @@ static int i915_driver_init_early(struct drm_i915_private *dev_priv,
mutex_init(&dev_priv->wm.wm_mutex);
mutex_init(&dev_priv->pps_mutex);
- intel_uc_init_early(dev_priv);
i915_memcpy_init_early(dev_priv);
ret = i915_workqueues_init(dev_priv);
if (ret < 0)
goto err_engines;
+ ret = i915_gem_init_early(dev_priv);
+ if (ret < 0)
+ goto err_workqueues;
+
/* This must be called before any calls to HAS_PCH_* */
intel_detect_pch(dev_priv);
+ intel_wopcm_init_early(&dev_priv->wopcm);
+ intel_uc_init_early(dev_priv);
intel_pm_setup(dev_priv);
intel_init_dpio(dev_priv);
intel_power_domains_init(dev_priv);
@@ -937,18 +941,13 @@ static int i915_driver_init_early(struct drm_i915_private *dev_priv,
intel_init_display_hooks(dev_priv);
intel_init_clock_gating_hooks(dev_priv);
intel_init_audio_hooks(dev_priv);
- ret = i915_gem_load_init(dev_priv);
- if (ret < 0)
- goto err_irq;
-
intel_display_crc_init(dev_priv);
intel_detect_preproduction_hw(dev_priv);
return 0;
-err_irq:
- intel_irq_fini(dev_priv);
+err_workqueues:
i915_workqueues_cleanup(dev_priv);
err_engines:
i915_engines_cleanup(dev_priv);
@@ -961,8 +960,9 @@ err_engines:
*/
static void i915_driver_cleanup_early(struct drm_i915_private *dev_priv)
{
- i915_gem_load_cleanup(dev_priv);
intel_irq_fini(dev_priv);
+ intel_uc_cleanup_early(dev_priv);
+ i915_gem_cleanup_early(dev_priv);
i915_workqueues_cleanup(dev_priv);
i915_engines_cleanup(dev_priv);
}
@@ -1032,6 +1032,10 @@ static int i915_driver_init_mmio(struct drm_i915_private *dev_priv)
intel_uncore_init(dev_priv);
+ intel_device_info_init_mmio(dev_priv);
+
+ intel_uncore_prune(dev_priv);
+
intel_uc_init_mmio(dev_priv);
ret = intel_engines_init_mmio(dev_priv);
@@ -1074,8 +1078,6 @@ static void intel_sanitize_options(struct drm_i915_private *dev_priv)
i915_modparams.enable_ppgtt);
DRM_DEBUG_DRIVER("ppgtt mode: %i\n", i915_modparams.enable_ppgtt);
- intel_uc_sanitize_options(dev_priv);
-
intel_gvt_sanitize_options(dev_priv);
}
@@ -1102,30 +1104,32 @@ static int i915_driver_init_hw(struct drm_i915_private *dev_priv)
ret = i915_ggtt_probe_hw(dev_priv);
if (ret)
- return ret;
+ goto err_perf;
- /* WARNING: Apparently we must kick fbdev drivers before vgacon,
- * otherwise the vga fbdev driver falls over. */
+ /*
+ * WARNING: Apparently we must kick fbdev drivers before vgacon,
+ * otherwise the vga fbdev driver falls over.
+ */
ret = i915_kick_out_firmware_fb(dev_priv);
if (ret) {
DRM_ERROR("failed to remove conflicting framebuffer drivers\n");
- goto out_ggtt;
+ goto err_ggtt;
}
ret = i915_kick_out_vgacon(dev_priv);
if (ret) {
DRM_ERROR("failed to remove conflicting VGA console\n");
- goto out_ggtt;
+ goto err_ggtt;
}
ret = i915_ggtt_init_hw(dev_priv);
if (ret)
- return ret;
+ goto err_ggtt;
ret = i915_ggtt_enable_hw(dev_priv);
if (ret) {
DRM_ERROR("failed to enable GGTT\n");
- goto out_ggtt;
+ goto err_ggtt;
}
pci_set_master(pdev);
@@ -1136,7 +1140,7 @@ static int i915_driver_init_hw(struct drm_i915_private *dev_priv)
if (ret) {
DRM_ERROR("failed to set DMA mask\n");
- goto out_ggtt;
+ goto err_ggtt;
}
}
@@ -1154,7 +1158,7 @@ static int i915_driver_init_hw(struct drm_i915_private *dev_priv)
if (ret) {
DRM_ERROR("failed to set DMA mask\n");
- goto out_ggtt;
+ goto err_ggtt;
}
}
@@ -1187,13 +1191,14 @@ static int i915_driver_init_hw(struct drm_i915_private *dev_priv)
ret = intel_gvt_init(dev_priv);
if (ret)
- goto out_ggtt;
+ goto err_ggtt;
return 0;
-out_ggtt:
+err_ggtt:
i915_ggtt_cleanup_hw(dev_priv);
-
+err_perf:
+ i915_perf_fini(dev_priv);
return ret;
}
@@ -1238,7 +1243,6 @@ static void i915_driver_register(struct drm_i915_private *dev_priv)
/* Reveal our presence to userspace */
if (drm_dev_register(dev, 0) == 0) {
i915_debugfs_register(dev_priv);
- i915_guc_log_register(dev_priv);
i915_setup_sysfs(dev_priv);
/* Depends on sysfs having been initialized */
@@ -1298,7 +1302,6 @@ static void i915_driver_unregister(struct drm_i915_private *dev_priv)
i915_pmu_unregister(dev_priv);
i915_teardown_sysfs(dev_priv);
- i915_guc_log_unregister(dev_priv);
drm_dev_unregister(&dev_priv->drm);
i915_gem_shrinker_unregister(dev_priv);
@@ -1457,7 +1460,6 @@ void i915_driver_unload(struct drm_device *dev)
i915_reset_error_state(dev_priv);
i915_gem_fini(dev_priv);
- intel_uc_fini_fw(dev_priv);
intel_fbc_cleanup_cfb(dev_priv);
intel_power_domains_fini(dev_priv);
@@ -1870,7 +1872,8 @@ static int i915_resume_switcheroo(struct drm_device *dev)
/**
* i915_reset - reset chip after a hang
* @i915: #drm_i915_private to reset
- * @flags: Instructions
+ * @stalled_mask: mask of the stalled engines with the guilty requests
+ * @reason: user error message for why we are resetting
*
* Reset the chip. Useful if a hang is detected. Marks the device as wedged
* on failure.
@@ -1885,12 +1888,16 @@ static int i915_resume_switcheroo(struct drm_device *dev)
* - re-init interrupt state
* - re-init display
*/
-void i915_reset(struct drm_i915_private *i915, unsigned int flags)
+void i915_reset(struct drm_i915_private *i915,
+ unsigned int stalled_mask,
+ const char *reason)
{
struct i915_gpu_error *error = &i915->gpu_error;
int ret;
int i;
+ GEM_TRACE("flags=%lx\n", error->flags);
+
might_sleep();
lockdep_assert_held(&i915->drm.struct_mutex);
GEM_BUG_ON(!test_bit(I915_RESET_BACKOFF, &error->flags));
@@ -1902,8 +1909,8 @@ void i915_reset(struct drm_i915_private *i915, unsigned int flags)
if (!i915_gem_unset_wedged(i915))
goto wakeup;
- if (!(flags & I915_RESET_QUIET))
- dev_notice(i915->drm.dev, "Resetting chip after gpu hang\n");
+ if (reason)
+ dev_notice(i915->drm.dev, "Resetting chip for %s\n", reason);
error->reset_count++;
disable_irq(i915->drm.irq);
@@ -1946,7 +1953,7 @@ void i915_reset(struct drm_i915_private *i915, unsigned int flags)
goto error;
}
- i915_gem_reset(i915);
+ i915_gem_reset(i915, stalled_mask);
intel_overlay_reset(i915);
/*
@@ -1992,7 +1999,6 @@ taint:
error:
i915_gem_set_wedged(i915);
i915_retire_requests(i915);
- intel_gpu_reset(i915, ALL_ENGINES);
goto finish;
}
@@ -2005,7 +2011,7 @@ static inline int intel_gt_reset_engine(struct drm_i915_private *dev_priv,
/**
* i915_reset_engine - reset GPU engine to recover from a hang
* @engine: engine to reset
- * @flags: options
+ * @msg: reason for GPU reset; or NULL for no dev_notice()
*
* Reset a specific GPU engine. Useful if a hang is detected.
* Returns zero on successful reset or otherwise an error code.
@@ -2015,12 +2021,13 @@ static inline int intel_gt_reset_engine(struct drm_i915_private *dev_priv,
* - reset engine (which will force the engine to idle)
* - re-init/configure engine
*/
-int i915_reset_engine(struct intel_engine_cs *engine, unsigned int flags)
+int i915_reset_engine(struct intel_engine_cs *engine, const char *msg)
{
struct i915_gpu_error *error = &engine->i915->gpu_error;
struct i915_request *active_request;
int ret;
+ GEM_TRACE("%s flags=%lx\n", engine->name, error->flags);
GEM_BUG_ON(!test_bit(I915_RESET_ENGINE + engine->id, &error->flags));
active_request = i915_gem_reset_prepare_engine(engine);
@@ -2030,10 +2037,9 @@ int i915_reset_engine(struct intel_engine_cs *engine, unsigned int flags)
goto out;
}
- if (!(flags & I915_RESET_QUIET)) {
+ if (msg)
dev_notice(engine->i915->drm.dev,
- "Resetting %s after gpu hang\n", engine->name);
- }
+ "Resetting %s for %s\n", engine->name, msg);
error->reset_engine_count[engine->id]++;
if (!engine->i915->guc.execbuf_client)
@@ -2053,7 +2059,7 @@ int i915_reset_engine(struct intel_engine_cs *engine, unsigned int flags)
* active request and can drop it, adjust head to skip the offending
* request to resume executing remaining requests in the queue.
*/
- i915_gem_reset_engine(engine, active_request);
+ i915_gem_reset_engine(engine, active_request, true);
/*
* The engine and its registers (and workarounds in case of render)
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index ce18b6cf6e68..e50d9589d6e3 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -64,6 +64,7 @@
#include "intel_opregion.h"
#include "intel_ringbuffer.h"
#include "intel_uncore.h"
+#include "intel_wopcm.h"
#include "intel_uc.h"
#include "i915_gem.h"
@@ -72,7 +73,7 @@
#include "i915_gem_object.h"
#include "i915_gem_gtt.h"
#include "i915_gem_timeline.h"
-
+#include "i915_gpu_error.h"
#include "i915_request.h"
#include "i915_vma.h"
@@ -83,8 +84,8 @@
#define DRIVER_NAME "i915"
#define DRIVER_DESC "Intel Graphics"
-#define DRIVER_DATE "20180308"
-#define DRIVER_TIMESTAMP 1520513379
+#define DRIVER_DATE "20180413"
+#define DRIVER_TIMESTAMP 1523611258
/* Use I915_STATE_WARN(x) and I915_STATE_WARN_ON() (rather than WARN() and
* WARN_ON()) for hw state sanity checks to check for unexpected conditions
@@ -261,6 +262,7 @@ enum hpd_pin {
HPD_PORT_C,
HPD_PORT_D,
HPD_PORT_E,
+ HPD_PORT_F,
HPD_NUM_PINS
};
@@ -453,172 +455,6 @@ struct intel_csr {
uint32_t allowed_dc_mask;
};
-struct intel_display_error_state;
-
-struct i915_gpu_state {
- struct kref ref;
- ktime_t time;
- ktime_t boottime;
- ktime_t uptime;
-
- struct drm_i915_private *i915;
-
- char error_msg[128];
- bool simulated;
- bool awake;
- bool wakelock;
- bool suspended;
- int iommu;
- u32 reset_count;
- u32 suspend_count;
- struct intel_device_info device_info;
- struct intel_driver_caps driver_caps;
- struct i915_params params;
-
- struct i915_error_uc {
- struct intel_uc_fw guc_fw;
- struct intel_uc_fw huc_fw;
- struct drm_i915_error_object *guc_log;
- } uc;
-
- /* Generic register state */
- u32 eir;
- u32 pgtbl_er;
- u32 ier;
- u32 gtier[4], ngtier;
- u32 ccid;
- u32 derrmr;
- u32 forcewake;
- u32 error; /* gen6+ */
- u32 err_int; /* gen7 */
- u32 fault_data0; /* gen8, gen9 */
- u32 fault_data1; /* gen8, gen9 */
- u32 done_reg;
- u32 gac_eco;
- u32 gam_ecochk;
- u32 gab_ctl;
- u32 gfx_mode;
-
- u32 nfence;
- u64 fence[I915_MAX_NUM_FENCES];
- struct intel_overlay_error_state *overlay;
- struct intel_display_error_state *display;
-
- struct drm_i915_error_engine {
- int engine_id;
- /* Software tracked state */
- bool idle;
- bool waiting;
- int num_waiters;
- unsigned long hangcheck_timestamp;
- bool hangcheck_stalled;
- enum intel_engine_hangcheck_action hangcheck_action;
- struct i915_address_space *vm;
- int num_requests;
- u32 reset_count;
-
- /* position of active request inside the ring */
- u32 rq_head, rq_post, rq_tail;
-
- /* our own tracking of ring head and tail */
- u32 cpu_ring_head;
- u32 cpu_ring_tail;
-
- u32 last_seqno;
-
- /* Register state */
- u32 start;
- u32 tail;
- u32 head;
- u32 ctl;
- u32 mode;
- u32 hws;
- u32 ipeir;
- u32 ipehr;
- u32 bbstate;
- u32 instpm;
- u32 instps;
- u32 seqno;
- u64 bbaddr;
- u64 acthd;
- u32 fault_reg;
- u64 faddr;
- u32 rc_psmi; /* sleep state */
- u32 semaphore_mboxes[I915_NUM_ENGINES - 1];
- struct intel_instdone instdone;
-
- struct drm_i915_error_context {
- char comm[TASK_COMM_LEN];
- pid_t pid;
- u32 handle;
- u32 hw_id;
- int priority;
- int ban_score;
- int active;
- int guilty;
- bool bannable;
- } context;
-
- struct drm_i915_error_object {
- u64 gtt_offset;
- u64 gtt_size;
- int page_count;
- int unused;
- u32 *pages[0];
- } *ringbuffer, *batchbuffer, *wa_batchbuffer, *ctx, *hws_page;
-
- struct drm_i915_error_object **user_bo;
- long user_bo_count;
-
- struct drm_i915_error_object *wa_ctx;
- struct drm_i915_error_object *default_state;
-
- struct drm_i915_error_request {
- long jiffies;
- pid_t pid;
- u32 context;
- int priority;
- int ban_score;
- u32 seqno;
- u32 head;
- u32 tail;
- } *requests, execlist[EXECLIST_MAX_PORTS];
- unsigned int num_ports;
-
- struct drm_i915_error_waiter {
- char comm[TASK_COMM_LEN];
- pid_t pid;
- u32 seqno;
- } *waiters;
-
- struct {
- u32 gfx_mode;
- union {
- u64 pdp[4];
- u32 pp_dir_base;
- };
- } vm_info;
- } engine[I915_NUM_ENGINES];
-
- struct drm_i915_error_buffer {
- u32 size;
- u32 name;
- u32 rseqno[I915_NUM_ENGINES], wseqno;
- u64 gtt_offset;
- u32 read_domains;
- u32 write_domain;
- s32 fence_reg:I915_MAX_NUM_FENCE_BITS;
- u32 tiling:2;
- u32 dirty:1;
- u32 purgeable:1;
- u32 userptr:1;
- s32 engine:4;
- u32 cache_level:3;
- } *active_bo[I915_NUM_ENGINES], *pinned_bo;
- u32 active_bo_count[I915_NUM_ENGINES], pinned_bo_count;
- struct i915_address_space *active_vm[I915_NUM_ENGINES];
-};
-
enum i915_cache_level {
I915_CACHE_NONE = 0,
I915_CACHE_LLC, /* also used for snoopable memory on non-LLC */
@@ -766,12 +602,13 @@ struct i915_psr {
bool active;
struct delayed_work work;
unsigned busy_frontbuffer_bits;
- bool psr2_support;
- bool aux_frame_sync;
+ bool sink_psr2_support;
bool link_standby;
- bool y_cord_support;
bool colorimetry_support;
bool alpm;
+ bool has_hw_tracking;
+ bool psr2_enabled;
+ u8 sink_sync_latency;
void (*enable_source)(struct intel_dp *,
const struct intel_crtc_state *);
@@ -1146,16 +983,6 @@ struct i915_gem_mm {
u32 object_count;
};
-struct drm_i915_error_state_buf {
- struct drm_i915_private *i915;
- unsigned bytes;
- unsigned size;
- int err;
- u8 *buf;
- loff_t start;
- loff_t pos;
-};
-
#define I915_IDLE_ENGINES_TIMEOUT (200) /* in ms */
#define I915_RESET_TIMEOUT (10 * HZ) /* 10s */
@@ -1164,102 +991,6 @@ struct drm_i915_error_state_buf {
#define I915_ENGINE_DEAD_TIMEOUT (4 * HZ) /* Seqno, head and subunits dead */
#define I915_SEQNO_DEAD_TIMEOUT (12 * HZ) /* Seqno dead with active head */
-struct i915_gpu_error {
- /* For hangcheck timer */
-#define DRM_I915_HANGCHECK_PERIOD 1500 /* in ms */
-#define DRM_I915_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD)
-
- struct delayed_work hangcheck_work;
-
- /* For reset and error_state handling. */
- spinlock_t lock;
- /* Protected by the above dev->gpu_error.lock. */
- struct i915_gpu_state *first_error;
-
- atomic_t pending_fb_pin;
-
- unsigned long missed_irq_rings;
-
- /**
- * State variable controlling the reset flow and count
- *
- * This is a counter which gets incremented when reset is triggered,
- *
- * Before the reset commences, the I915_RESET_BACKOFF bit is set
- * meaning that any waiters holding onto the struct_mutex should
- * relinquish the lock immediately in order for the reset to start.
- *
- * If reset is not completed succesfully, the I915_WEDGE bit is
- * set meaning that hardware is terminally sour and there is no
- * recovery. All waiters on the reset_queue will be woken when
- * that happens.
- *
- * This counter is used by the wait_seqno code to notice that reset
- * event happened and it needs to restart the entire ioctl (since most
- * likely the seqno it waited for won't ever signal anytime soon).
- *
- * This is important for lock-free wait paths, where no contended lock
- * naturally enforces the correct ordering between the bail-out of the
- * waiter and the gpu reset work code.
- */
- unsigned long reset_count;
-
- /**
- * flags: Control various stages of the GPU reset
- *
- * #I915_RESET_BACKOFF - When we start a reset, we want to stop any
- * other users acquiring the struct_mutex. To do this we set the
- * #I915_RESET_BACKOFF bit in the error flags when we detect a reset
- * and then check for that bit before acquiring the struct_mutex (in
- * i915_mutex_lock_interruptible()?). I915_RESET_BACKOFF serves a
- * secondary role in preventing two concurrent global reset attempts.
- *
- * #I915_RESET_HANDOFF - To perform the actual GPU reset, we need the
- * struct_mutex. We try to acquire the struct_mutex in the reset worker,
- * but it may be held by some long running waiter (that we cannot
- * interrupt without causing trouble). Once we are ready to do the GPU
- * reset, we set the I915_RESET_HANDOFF bit and wakeup any waiters. If
- * they already hold the struct_mutex and want to participate they can
- * inspect the bit and do the reset directly, otherwise the worker
- * waits for the struct_mutex.
- *
- * #I915_RESET_ENGINE[num_engines] - Since the driver doesn't need to
- * acquire the struct_mutex to reset an engine, we need an explicit
- * flag to prevent two concurrent reset attempts in the same engine.
- * As the number of engines continues to grow, allocate the flags from
- * the most significant bits.
- *
- * #I915_WEDGED - If reset fails and we can no longer use the GPU,
- * we set the #I915_WEDGED bit. Prior to command submission, e.g.
- * i915_request_alloc(), this bit is checked and the sequence
- * aborted (with -EIO reported to userspace) if set.
- */
- unsigned long flags;
-#define I915_RESET_BACKOFF 0
-#define I915_RESET_HANDOFF 1
-#define I915_RESET_MODESET 2
-#define I915_WEDGED (BITS_PER_LONG - 1)
-#define I915_RESET_ENGINE (I915_WEDGED - I915_NUM_ENGINES)
-
- /** Number of times an engine has been reset */
- u32 reset_engine_count[I915_NUM_ENGINES];
-
- /**
- * Waitqueue to signal when a hang is detected. Used to for waiters
- * to release the struct_mutex for the reset to procede.
- */
- wait_queue_head_t wait_queue;
-
- /**
- * Waitqueue to signal when the reset has completed. Used by clients
- * that wait for dev_priv->mm.wedged to settle.
- */
- wait_queue_head_t reset_queue;
-
- /* For missed irq/seqno simulation. */
- unsigned long test_irq_rings;
-};
-
enum modeset_restore {
MODESET_ON_LID_OPEN,
MODESET_DONE,
@@ -1451,11 +1182,12 @@ static inline bool skl_ddb_entry_equal(const struct skl_ddb_entry *e1,
}
struct skl_ddb_allocation {
- struct skl_ddb_entry plane[I915_MAX_PIPES][I915_MAX_PLANES]; /* packed/uv */
- struct skl_ddb_entry y_plane[I915_MAX_PIPES][I915_MAX_PLANES];
+ /* packed/y */
+ struct skl_ddb_entry plane[I915_MAX_PIPES][I915_MAX_PLANES];
+ struct skl_ddb_entry uv_plane[I915_MAX_PIPES][I915_MAX_PLANES];
};
-struct skl_wm_values {
+struct skl_ddb_values {
unsigned dirty_pipes;
struct skl_ddb_allocation ddb;
};
@@ -1470,6 +1202,7 @@ struct skl_wm_level {
struct skl_wm_params {
bool x_tiled, y_tiled;
bool rc_surface;
+ bool is_planar;
uint32_t width;
uint8_t cpp;
uint32_t plane_pixel_rate;
@@ -1860,6 +1593,8 @@ struct drm_i915_private {
struct intel_gvt *gvt;
+ struct intel_wopcm wopcm;
+
struct intel_huc huc;
struct intel_guc guc;
@@ -2152,7 +1887,7 @@ struct drm_i915_private {
/* current hardware state */
union {
struct ilk_wm_values hw;
- struct skl_wm_values skl_hw;
+ struct skl_ddb_values skl_hw;
struct vlv_wm_values vlv;
struct g4x_wm_values g4x;
};
@@ -2392,6 +2127,11 @@ static inline struct drm_i915_private *kdev_to_i915(struct device *kdev)
return to_i915(dev_get_drvdata(kdev));
}
+static inline struct drm_i915_private *wopcm_to_i915(struct intel_wopcm *wopcm)
+{
+ return container_of(wopcm, struct drm_i915_private, wopcm);
+}
+
static inline struct drm_i915_private *guc_to_i915(struct intel_guc *guc)
{
return container_of(guc, struct drm_i915_private, guc);
@@ -2411,8 +2151,10 @@ static inline struct drm_i915_private *huc_to_i915(struct intel_huc *huc)
/* Iterator over subset of engines selected by mask */
#define for_each_engine_masked(engine__, dev_priv__, mask__, tmp__) \
- for (tmp__ = mask__ & INTEL_INFO(dev_priv__)->ring_mask; \
- tmp__ ? (engine__ = (dev_priv__)->engine[__mask_next_bit(tmp__)]), 1 : 0; )
+ for ((tmp__) = (mask__) & INTEL_INFO(dev_priv__)->ring_mask; \
+ (tmp__) ? \
+ ((engine__) = (dev_priv__)->engine[__mask_next_bit(tmp__)]), 1 : \
+ 0;)
enum hdmi_force_audio {
HDMI_AUDIO_OFF_DVI = -2, /* no aux data for HDMI-DVI converter */
@@ -2963,10 +2705,11 @@ extern void i915_driver_unload(struct drm_device *dev);
extern int intel_gpu_reset(struct drm_i915_private *dev_priv, u32 engine_mask);
extern bool intel_has_gpu_reset(struct drm_i915_private *dev_priv);
-#define I915_RESET_QUIET BIT(0)
-extern void i915_reset(struct drm_i915_private *i915, unsigned int flags);
+extern void i915_reset(struct drm_i915_private *i915,
+ unsigned int stalled_mask,
+ const char *reason);
extern int i915_reset_engine(struct intel_engine_cs *engine,
- unsigned int flags);
+ const char *reason);
extern bool intel_has_reset_engine(struct drm_i915_private *dev_priv);
extern int intel_reset_guc(struct drm_i915_private *dev_priv);
@@ -3014,10 +2757,12 @@ static inline void i915_queue_hangcheck(struct drm_i915_private *dev_priv)
&dev_priv->gpu_error.hangcheck_work, delay);
}
-__printf(3, 4)
+__printf(4, 5)
void i915_handle_error(struct drm_i915_private *dev_priv,
u32 engine_mask,
+ unsigned long flags,
const char *fmt, ...);
+#define I915_ERROR_CAPTURE BIT(0)
extern void intel_irq_init(struct drm_i915_private *dev_priv);
extern void intel_irq_fini(struct drm_i915_private *dev_priv);
@@ -3132,8 +2877,8 @@ int i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data,
int i915_gem_wait_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
void i915_gem_sanitize(struct drm_i915_private *i915);
-int i915_gem_load_init(struct drm_i915_private *dev_priv);
-void i915_gem_load_cleanup(struct drm_i915_private *dev_priv);
+int i915_gem_init_early(struct drm_i915_private *dev_priv);
+void i915_gem_cleanup_early(struct drm_i915_private *dev_priv);
void i915_gem_load_init_fences(struct drm_i915_private *dev_priv);
int i915_gem_freeze(struct drm_i915_private *dev_priv);
int i915_gem_freeze_late(struct drm_i915_private *dev_priv);
@@ -3388,13 +3133,15 @@ static inline u32 i915_reset_engine_count(struct i915_gpu_error *error,
struct i915_request *
i915_gem_reset_prepare_engine(struct intel_engine_cs *engine);
int i915_gem_reset_prepare(struct drm_i915_private *dev_priv);
-void i915_gem_reset(struct drm_i915_private *dev_priv);
+void i915_gem_reset(struct drm_i915_private *dev_priv,
+ unsigned int stalled_mask);
void i915_gem_reset_finish_engine(struct intel_engine_cs *engine);
void i915_gem_reset_finish(struct drm_i915_private *dev_priv);
void i915_gem_set_wedged(struct drm_i915_private *dev_priv);
bool i915_gem_unset_wedged(struct drm_i915_private *dev_priv);
void i915_gem_reset_engine(struct intel_engine_cs *engine,
- struct i915_request *request);
+ struct i915_request *request,
+ bool stalled);
void i915_gem_init_mmio(struct drm_i915_private *i915);
int __must_check i915_gem_init(struct drm_i915_private *dev_priv);
@@ -3589,64 +3336,6 @@ static inline int i915_debugfs_connector_add(struct drm_connector *connector)
static inline void intel_display_crc_init(struct drm_i915_private *dev_priv) {}
#endif
-/* i915_gpu_error.c */
-#if IS_ENABLED(CONFIG_DRM_I915_CAPTURE_ERROR)
-
-__printf(2, 3)
-void i915_error_printf(struct drm_i915_error_state_buf *e, const char *f, ...);
-int i915_error_state_to_str(struct drm_i915_error_state_buf *estr,
- const struct i915_gpu_state *gpu);
-int i915_error_state_buf_init(struct drm_i915_error_state_buf *eb,
- struct drm_i915_private *i915,
- size_t count, loff_t pos);
-static inline void i915_error_state_buf_release(
- struct drm_i915_error_state_buf *eb)
-{
- kfree(eb->buf);
-}
-
-struct i915_gpu_state *i915_capture_gpu_state(struct drm_i915_private *i915);
-void i915_capture_error_state(struct drm_i915_private *dev_priv,
- u32 engine_mask,
- const char *error_msg);
-
-static inline struct i915_gpu_state *
-i915_gpu_state_get(struct i915_gpu_state *gpu)
-{
- kref_get(&gpu->ref);
- return gpu;
-}
-
-void __i915_gpu_state_free(struct kref *kref);
-static inline void i915_gpu_state_put(struct i915_gpu_state *gpu)
-{
- if (gpu)
- kref_put(&gpu->ref, __i915_gpu_state_free);
-}
-
-struct i915_gpu_state *i915_first_error_state(struct drm_i915_private *i915);
-void i915_reset_error_state(struct drm_i915_private *i915);
-
-#else
-
-static inline void i915_capture_error_state(struct drm_i915_private *dev_priv,
- u32 engine_mask,
- const char *error_msg)
-{
-}
-
-static inline struct i915_gpu_state *
-i915_first_error_state(struct drm_i915_private *i915)
-{
- return NULL;
-}
-
-static inline void i915_reset_error_state(struct drm_i915_private *i915)
-{
-}
-
-#endif
-
const char *i915_cache_level_str(struct drm_i915_private *i915, int type);
/* i915_cmd_parser.c */
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 7b5a9d7c9593..4c9d2a6f7d28 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -35,6 +35,7 @@
#include "intel_drv.h"
#include "intel_frontbuffer.h"
#include "intel_mocs.h"
+#include "intel_workarounds.h"
#include "i915_gemfs.h"
#include <linux/dma-fence-array.h>
#include <linux/kthread.h>
@@ -136,6 +137,100 @@ int i915_mutex_lock_interruptible(struct drm_device *dev)
return 0;
}
+static u32 __i915_gem_park(struct drm_i915_private *i915)
+{
+ lockdep_assert_held(&i915->drm.struct_mutex);
+ GEM_BUG_ON(i915->gt.active_requests);
+
+ if (!i915->gt.awake)
+ return I915_EPOCH_INVALID;
+
+ GEM_BUG_ON(i915->gt.epoch == I915_EPOCH_INVALID);
+
+ /*
+ * Be paranoid and flush a concurrent interrupt to make sure
+ * we don't reactivate any irq tasklets after parking.
+ *
+ * FIXME: Note that even though we have waited for execlists to be idle,
+ * there may still be an in-flight interrupt even though the CSB
+ * is now empty. synchronize_irq() makes sure that a residual interrupt
+ * is completed before we continue, but it doesn't prevent the HW from
+ * raising a spurious interrupt later. To complete the shield we should
+ * coordinate disabling the CS irq with flushing the interrupts.
+ */
+ synchronize_irq(i915->drm.irq);
+
+ intel_engines_park(i915);
+ i915_gem_timelines_park(i915);
+
+ i915_pmu_gt_parked(i915);
+
+ i915->gt.awake = false;
+
+ if (INTEL_GEN(i915) >= 6)
+ gen6_rps_idle(i915);
+
+ intel_display_power_put(i915, POWER_DOMAIN_GT_IRQ);
+
+ intel_runtime_pm_put(i915);
+
+ return i915->gt.epoch;
+}
+
+void i915_gem_park(struct drm_i915_private *i915)
+{
+ lockdep_assert_held(&i915->drm.struct_mutex);
+ GEM_BUG_ON(i915->gt.active_requests);
+
+ if (!i915->gt.awake)
+ return;
+
+ /* Defer the actual call to __i915_gem_park() to prevent ping-pongs */
+ mod_delayed_work(i915->wq, &i915->gt.idle_work, msecs_to_jiffies(100));
+}
+
+void i915_gem_unpark(struct drm_i915_private *i915)
+{
+ lockdep_assert_held(&i915->drm.struct_mutex);
+ GEM_BUG_ON(!i915->gt.active_requests);
+
+ if (i915->gt.awake)
+ return;
+
+ intel_runtime_pm_get_noresume(i915);
+
+ /*
+ * It seems that the DMC likes to transition between the DC states a lot
+ * when there are no connected displays (no active power domains) during
+ * command submission.
+ *
+ * This activity has negative impact on the performance of the chip with
+ * huge latencies observed in the interrupt handler and elsewhere.
+ *
+ * Work around it by grabbing a GT IRQ power domain whilst there is any
+ * GT activity, preventing any DC state transitions.
+ */
+ intel_display_power_get(i915, POWER_DOMAIN_GT_IRQ);
+
+ i915->gt.awake = true;
+ if (unlikely(++i915->gt.epoch == 0)) /* keep 0 as invalid */
+ i915->gt.epoch = 1;
+
+ intel_enable_gt_powersave(i915);
+ i915_update_gfx_val(i915);
+ if (INTEL_GEN(i915) >= 6)
+ gen6_rps_busy(i915);
+ i915_pmu_gt_unparked(i915);
+
+ intel_engines_unpark(i915);
+
+ i915_queue_hangcheck(i915);
+
+ queue_delayed_work(i915->wq,
+ &i915->gt.retire_work,
+ round_jiffies_up_relative(HZ));
+}
+
int
i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data,
struct drm_file *file)
@@ -2896,20 +2991,6 @@ i915_gem_find_active_request(struct intel_engine_cs *engine)
return active;
}
-static bool engine_stalled(struct intel_engine_cs *engine)
-{
- if (!engine->hangcheck.stalled)
- return false;
-
- /* Check for possible seqno movement after hang declaration */
- if (engine->hangcheck.seqno != intel_engine_get_seqno(engine)) {
- DRM_DEBUG_DRIVER("%s pardoned\n", engine->name);
- return false;
- }
-
- return true;
-}
-
/*
* Ensure irq handler finishes, and not run again.
* Also return the active request so that we only search for it once.
@@ -2998,6 +3079,7 @@ int i915_gem_reset_prepare(struct drm_i915_private *dev_priv)
}
i915_gem_revoke_fences(dev_priv);
+ intel_uc_sanitize(dev_priv);
return err;
}
@@ -3047,7 +3129,8 @@ static void engine_skip_context(struct i915_request *request)
/* Returns the request if it was guilty of the hang */
static struct i915_request *
i915_gem_reset_request(struct intel_engine_cs *engine,
- struct i915_request *request)
+ struct i915_request *request,
+ bool stalled)
{
/* The guilty request will get skipped on a hung engine.
*
@@ -3070,7 +3153,15 @@ i915_gem_reset_request(struct intel_engine_cs *engine,
* subsequent hangs.
*/
- if (engine_stalled(engine)) {
+ if (i915_request_completed(request)) {
+ GEM_TRACE("%s pardoned global=%d (fence %llx:%d), current %d\n",
+ engine->name, request->global_seqno,
+ request->fence.context, request->fence.seqno,
+ intel_engine_get_seqno(engine));
+ stalled = false;
+ }
+
+ if (stalled) {
i915_gem_context_mark_guilty(request->ctx);
skip_request(request);
@@ -3101,7 +3192,8 @@ i915_gem_reset_request(struct intel_engine_cs *engine,
}
void i915_gem_reset_engine(struct intel_engine_cs *engine,
- struct i915_request *request)
+ struct i915_request *request,
+ bool stalled)
{
/*
* Make sure this write is visible before we re-enable the interrupt
@@ -3111,7 +3203,7 @@ void i915_gem_reset_engine(struct intel_engine_cs *engine,
smp_store_mb(engine->irq_posted, 0);
if (request)
- request = i915_gem_reset_request(engine, request);
+ request = i915_gem_reset_request(engine, request, stalled);
if (request) {
DRM_DEBUG_DRIVER("resetting %s to restart from tail of request 0x%x\n",
@@ -3122,7 +3214,8 @@ void i915_gem_reset_engine(struct intel_engine_cs *engine,
engine->reset_hw(engine, request);
}
-void i915_gem_reset(struct drm_i915_private *dev_priv)
+void i915_gem_reset(struct drm_i915_private *dev_priv,
+ unsigned int stalled_mask)
{
struct intel_engine_cs *engine;
enum intel_engine_id id;
@@ -3134,7 +3227,9 @@ void i915_gem_reset(struct drm_i915_private *dev_priv)
for_each_engine(engine, dev_priv, id) {
struct i915_gem_context *ctx;
- i915_gem_reset_engine(engine, engine->hangcheck.active_request);
+ i915_gem_reset_engine(engine,
+ engine->hangcheck.active_request,
+ stalled_mask & ENGINE_MASK(id));
ctx = fetch_and_zero(&engine->last_retired_context);
if (ctx)
engine->context_unpin(engine, ctx);
@@ -3160,13 +3255,6 @@ void i915_gem_reset(struct drm_i915_private *dev_priv)
}
i915_gem_restore_fences(dev_priv);
-
- if (dev_priv->gt.awake) {
- intel_sanitize_gt_powersave(dev_priv);
- intel_enable_gt_powersave(dev_priv);
- if (INTEL_GEN(dev_priv) >= 6)
- gen6_rps_busy(dev_priv);
- }
}
void i915_gem_reset_finish_engine(struct intel_engine_cs *engine)
@@ -3192,6 +3280,9 @@ void i915_gem_reset_finish(struct drm_i915_private *dev_priv)
static void nop_submit_request(struct i915_request *request)
{
+ GEM_TRACE("%s fence %llx:%d -> -EIO\n",
+ request->engine->name,
+ request->fence.context, request->fence.seqno);
dma_fence_set_error(&request->fence, -EIO);
i915_request_submit(request);
@@ -3201,6 +3292,9 @@ static void nop_complete_submit_request(struct i915_request *request)
{
unsigned long flags;
+ GEM_TRACE("%s fence %llx:%d -> -EIO\n",
+ request->engine->name,
+ request->fence.context, request->fence.seqno);
dma_fence_set_error(&request->fence, -EIO);
spin_lock_irqsave(&request->engine->timeline->lock, flags);
@@ -3214,6 +3308,8 @@ void i915_gem_set_wedged(struct drm_i915_private *i915)
struct intel_engine_cs *engine;
enum intel_engine_id id;
+ GEM_TRACE("start\n");
+
if (drm_debug & DRM_UT_DRIVER) {
struct drm_printer p = drm_debug_printer(__func__);
@@ -3237,6 +3333,9 @@ void i915_gem_set_wedged(struct drm_i915_private *i915)
}
i915->caps.scheduler = 0;
+ /* Even if the GPU reset fails, it should still stop the engines */
+ intel_gpu_reset(i915, ALL_ENGINES);
+
/*
* Make sure no one is running the old callback before we proceed with
* cancelling requests and resetting the completion tracking. Otherwise
@@ -3278,6 +3377,8 @@ void i915_gem_set_wedged(struct drm_i915_private *i915)
i915_gem_reset_finish_engine(engine);
}
+ GEM_TRACE("end\n");
+
wake_up_all(&i915->gpu_error.reset_queue);
}
@@ -3290,7 +3391,10 @@ bool i915_gem_unset_wedged(struct drm_i915_private *i915)
if (!test_bit(I915_WEDGED, &i915->gpu_error.flags))
return true;
- /* Before unwedging, make sure that all pending operations
+ GEM_TRACE("start\n");
+
+ /*
+ * Before unwedging, make sure that all pending operations
* are flushed and errored out - we may have requests waiting upon
* third party fences. We marked all inflight requests as EIO, and
* every execbuf since returned EIO, for consistency we want all
@@ -3308,7 +3412,8 @@ bool i915_gem_unset_wedged(struct drm_i915_private *i915)
if (!rq)
continue;
- /* We can't use our normal waiter as we want to
+ /*
+ * We can't use our normal waiter as we want to
* avoid recursively trying to handle the current
* reset. The basic dma_fence_default_wait() installs
* a callback for dma_fence_signal(), which is
@@ -3323,8 +3428,11 @@ bool i915_gem_unset_wedged(struct drm_i915_private *i915)
return false;
}
}
+ i915_retire_requests(i915);
+ GEM_BUG_ON(i915->gt.active_requests);
- /* Undo nop_submit_request. We prevent all new i915 requests from
+ /*
+ * Undo nop_submit_request. We prevent all new i915 requests from
* being queued (by disallowing execbuf whilst wedged) so having
* waited for all active requests above, we know the system is idle
* and do not have to worry about a thread being inside
@@ -3335,6 +3443,8 @@ bool i915_gem_unset_wedged(struct drm_i915_private *i915)
intel_engines_reset_default_submission(i915);
i915_gem_contexts_lost(i915);
+ GEM_TRACE("end\n");
+
smp_mb__before_atomic(); /* complete takeover before enabling execbuf */
clear_bit(I915_WEDGED, &i915->gpu_error.flags);
@@ -3473,36 +3583,9 @@ i915_gem_idle_work_handler(struct work_struct *work)
if (new_requests_since_last_retire(dev_priv))
goto out_unlock;
- /*
- * Be paranoid and flush a concurrent interrupt to make sure
- * we don't reactivate any irq tasklets after parking.
- *
- * FIXME: Note that even though we have waited for execlists to be idle,
- * there may still be an in-flight interrupt even though the CSB
- * is now empty. synchronize_irq() makes sure that a residual interrupt
- * is completed before we continue, but it doesn't prevent the HW from
- * raising a spurious interrupt later. To complete the shield we should
- * coordinate disabling the CS irq with flushing the interrupts.
- */
- synchronize_irq(dev_priv->drm.irq);
-
- intel_engines_park(dev_priv);
- i915_gem_timelines_park(dev_priv);
-
- i915_pmu_gt_parked(dev_priv);
+ epoch = __i915_gem_park(dev_priv);
- GEM_BUG_ON(!dev_priv->gt.awake);
- dev_priv->gt.awake = false;
- epoch = dev_priv->gt.epoch;
- GEM_BUG_ON(epoch == I915_EPOCH_INVALID);
rearm_hangcheck = false;
-
- if (INTEL_GEN(dev_priv) >= 6)
- gen6_rps_idle(dev_priv);
-
- intel_display_power_put(dev_priv, POWER_DOMAIN_GT_IRQ);
-
- intel_runtime_pm_put(dev_priv);
out_unlock:
mutex_unlock(&dev_priv->drm.struct_mutex);
@@ -3666,16 +3749,7 @@ static int wait_for_engines(struct drm_i915_private *i915)
if (wait_for(intel_engines_are_idle(i915), I915_IDLE_ENGINES_TIMEOUT)) {
dev_err(i915->drm.dev,
"Failed to idle engines, declaring wedged!\n");
- if (drm_debug & DRM_UT_DRIVER) {
- struct drm_printer p = drm_debug_printer(__func__);
- struct intel_engine_cs *engine;
- enum intel_engine_id id;
-
- for_each_engine(engine, i915, id)
- intel_engine_dump(engine, &p,
- "%s\n", engine->name);
- }
-
+ GEM_TRACE_DUMP();
i915_gem_set_wedged(i915);
return -EIO;
}
@@ -4088,9 +4162,10 @@ out:
}
/*
- * Prepare buffer for display plane (scanout, cursors, etc).
- * Can be called from an uninterruptible phase (modesetting) and allows
- * any flushes to be pipelined (for pageflips).
+ * Prepare buffer for display plane (scanout, cursors, etc). Can be called from
+ * an uninterruptible phase (modesetting) and allows any flushes to be pipelined
+ * (for pageflips). We only flush the caches while preparing the buffer for
+ * display, the callers are responsible for frontbuffer flush.
*/
struct i915_vma *
i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
@@ -4146,9 +4221,7 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
vma->display_alignment = max_t(u64, vma->display_alignment, alignment);
- /* Treat this as an end-of-frame, like intel_user_framebuffer_dirty() */
__i915_gem_object_flush_for_display(obj);
- intel_fb_obj_flush(obj, ORIGIN_DIRTYFB);
/* It should now be out of any other write domains, and we can update
* the domain values for our changes.
@@ -4973,6 +5046,7 @@ int i915_gem_suspend(struct drm_i915_private *dev_priv)
* machines is a good idea, we don't - just in case it leaves the
* machine in an unusable condition.
*/
+ intel_uc_sanitize(dev_priv);
i915_gem_sanitize(dev_priv);
intel_runtime_pm_put(dev_priv);
@@ -5118,6 +5192,8 @@ int i915_gem_init_hw(struct drm_i915_private *dev_priv)
}
}
+ intel_gt_workarounds_apply(dev_priv);
+
i915_gem_init_swizzling(dev_priv);
/*
@@ -5140,6 +5216,12 @@ int i915_gem_init_hw(struct drm_i915_private *dev_priv)
goto out;
}
+ ret = intel_wopcm_init_hw(&dev_priv->wopcm);
+ if (ret) {
+ DRM_ERROR("Enabling WOPCM failed (%d)\n", ret);
+ goto out;
+ }
+
/* We can't enable contexts until all firmware is loaded */
ret = intel_uc_init_hw(dev_priv);
if (ret) {
@@ -5297,6 +5379,10 @@ int i915_gem_init(struct drm_i915_private *dev_priv)
if (ret)
return ret;
+ ret = intel_wopcm_init(&dev_priv->wopcm);
+ if (ret)
+ return ret;
+
ret = intel_uc_init_misc(dev_priv);
if (ret)
return ret;
@@ -5478,8 +5564,7 @@ static void i915_gem_init__mm(struct drm_i915_private *i915)
INIT_WORK(&i915->mm.free_work, __i915_gem_free_work);
}
-int
-i915_gem_load_init(struct drm_i915_private *dev_priv)
+int i915_gem_init_early(struct drm_i915_private *dev_priv)
{
int err = -ENOMEM;
@@ -5554,7 +5639,7 @@ err_out:
return err;
}
-void i915_gem_load_cleanup(struct drm_i915_private *dev_priv)
+void i915_gem_cleanup_early(struct drm_i915_private *dev_priv)
{
i915_gem_drain_freed_objects(dev_priv);
GEM_BUG_ON(!llist_empty(&dev_priv->mm.free_list));
diff --git a/drivers/gpu/drm/i915/i915_gem.h b/drivers/gpu/drm/i915/i915_gem.h
index f54c4ff74ded..deaf78d2ae8b 100644
--- a/drivers/gpu/drm/i915/i915_gem.h
+++ b/drivers/gpu/drm/i915/i915_gem.h
@@ -27,6 +27,8 @@
#include <linux/bug.h>
+struct drm_i915_private;
+
#ifdef CONFIG_DRM_I915_DEBUG_GEM
#define GEM_BUG_ON(condition) do { if (unlikely((condition))) { \
pr_err("%s:%d GEM_BUG_ON(%s)\n", \
@@ -53,10 +55,15 @@
#if IS_ENABLED(CONFIG_DRM_I915_TRACE_GEM)
#define GEM_TRACE(...) trace_printk(__VA_ARGS__)
+#define GEM_TRACE_DUMP() ftrace_dump(DUMP_ALL)
#else
#define GEM_TRACE(...) do { } while (0)
+#define GEM_TRACE_DUMP() do { } while (0)
#endif
#define I915_NUM_ENGINES 8
+void i915_gem_park(struct drm_i915_private *i915);
+void i915_gem_unpark(struct drm_i915_private *i915);
+
#endif /* __I915_GEM_H__ */
diff --git a/drivers/gpu/drm/i915/i915_gem_batch_pool.c b/drivers/gpu/drm/i915/i915_gem_batch_pool.c
index d3cbe8432f48..f3890b664e3f 100644
--- a/drivers/gpu/drm/i915/i915_gem_batch_pool.c
+++ b/drivers/gpu/drm/i915/i915_gem_batch_pool.c
@@ -1,29 +1,11 @@
/*
- * Copyright © 2014 Intel Corporation
- *
- * 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
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * 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 NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS 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.
+ * SPDX-License-Identifier: MIT
*
+ * Copyright © 2014-2018 Intel Corporation
*/
-#include "i915_drv.h"
#include "i915_gem_batch_pool.h"
+#include "i915_drv.h"
/**
* DOC: batch pool
@@ -41,11 +23,11 @@
/**
* i915_gem_batch_pool_init() - initialize a batch buffer pool
- * @engine: the associated request submission engine
* @pool: the batch buffer pool
+ * @engine: the associated request submission engine
*/
-void i915_gem_batch_pool_init(struct intel_engine_cs *engine,
- struct i915_gem_batch_pool *pool)
+void i915_gem_batch_pool_init(struct i915_gem_batch_pool *pool,
+ struct intel_engine_cs *engine)
{
int n;
diff --git a/drivers/gpu/drm/i915/i915_gem_batch_pool.h b/drivers/gpu/drm/i915/i915_gem_batch_pool.h
index 10d5ac4c00d3..56947daaaf65 100644
--- a/drivers/gpu/drm/i915/i915_gem_batch_pool.h
+++ b/drivers/gpu/drm/i915/i915_gem_batch_pool.h
@@ -1,31 +1,13 @@
/*
- * Copyright © 2014 Intel Corporation
- *
- * 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
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * 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 NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS 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.
+ * SPDX-License-Identifier: MIT
*
+ * Copyright © 2014-2018 Intel Corporation
*/
#ifndef I915_GEM_BATCH_POOL_H
#define I915_GEM_BATCH_POOL_H
-#include "i915_drv.h"
+#include <linux/types.h>
struct intel_engine_cs;
@@ -34,9 +16,8 @@ struct i915_gem_batch_pool {
struct list_head cache_list[4];
};
-/* i915_gem_batch_pool.c */
-void i915_gem_batch_pool_init(struct intel_engine_cs *engine,
- struct i915_gem_batch_pool *pool);
+void i915_gem_batch_pool_init(struct i915_gem_batch_pool *pool,
+ struct intel_engine_cs *engine);
void i915_gem_batch_pool_fini(struct i915_gem_batch_pool *pool);
struct drm_i915_gem_object*
i915_gem_batch_pool_get(struct i915_gem_batch_pool *pool, size_t size);
diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c
index f2cbea7cf940..9b3834a846e8 100644
--- a/drivers/gpu/drm/i915/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/i915_gem_context.c
@@ -90,6 +90,7 @@
#include <drm/i915_drm.h>
#include "i915_drv.h"
#include "i915_trace.h"
+#include "intel_workarounds.h"
#define ALL_L3_SLICES(dev) (1 << NUM_L3_SLICES(dev)) - 1
@@ -318,12 +319,13 @@ __create_hw_context(struct drm_i915_private *dev_priv,
ctx->desc_template =
default_desc_template(dev_priv, dev_priv->mm.aliasing_ppgtt);
- /* GuC requires the ring to be placed above GUC_WOPCM_TOP. If GuC is not
+ /*
+ * GuC requires the ring to be placed in Non-WOPCM memory. If GuC is not
* present or not in use we still need a small bias as ring wraparound
* at offset 0 sometimes hangs. No idea why.
*/
if (USES_GUC(dev_priv))
- ctx->ggtt_offset_bias = GUC_WOPCM_TOP;
+ ctx->ggtt_offset_bias = dev_priv->guc.ggtt_pin_bias;
else
ctx->ggtt_offset_bias = I915_GTT_PAGE_SIZE;
@@ -458,11 +460,16 @@ static bool needs_preempt_context(struct drm_i915_private *i915)
int i915_gem_contexts_init(struct drm_i915_private *dev_priv)
{
struct i915_gem_context *ctx;
+ int ret;
/* Reassure ourselves we are only called once */
GEM_BUG_ON(dev_priv->kernel_context);
GEM_BUG_ON(dev_priv->preempt_context);
+ ret = intel_ctx_workarounds_init(dev_priv);
+ if (ret)
+ return ret;
+
INIT_LIST_HEAD(&dev_priv->contexts.list);
INIT_WORK(&dev_priv->contexts.free_work, contexts_free_worker);
init_llist_head(&dev_priv->contexts.free_list);
diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
index 8c170db8495d..c74f5df3fb5a 100644
--- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
@@ -81,6 +81,35 @@ enum {
* but this remains just a hint as the kernel may choose a new location for
* any object in the future.
*
+ * At the level of talking to the hardware, submitting a batchbuffer for the
+ * GPU to execute is to add content to a buffer from which the HW
+ * command streamer is reading.
+ *
+ * 1. Add a command to load the HW context. For Logical Ring Contexts, i.e.
+ * Execlists, this command is not placed on the same buffer as the
+ * remaining items.
+ *
+ * 2. Add a command to invalidate caches to the buffer.
+ *
+ * 3. Add a batchbuffer start command to the buffer; the start command is
+ * essentially a token together with the GPU address of the batchbuffer
+ * to be executed.
+ *
+ * 4. Add a pipeline flush to the buffer.
+ *
+ * 5. Add a memory write command to the buffer to record when the GPU
+ * is done executing the batchbuffer. The memory write writes the
+ * global sequence number of the request, ``i915_request::global_seqno``;
+ * the i915 driver uses the current value in the register to determine
+ * if the GPU has completed the batchbuffer.
+ *
+ * 6. Add a user interrupt command to the buffer. This command instructs
+ * the GPU to issue an interrupt when the command, pipeline flush and
+ * memory write are completed.
+ *
+ * 7. Inform the hardware of the additional commands added to the buffer
+ * (by updating the tail pointer).
+ *
* Processing an execbuf ioctl is conceptually split up into a few phases.
*
* 1. Validation - Ensure all the pointers, handles and flags are valid.
@@ -728,7 +757,7 @@ static int eb_lookup_vmas(struct i915_execbuffer *eb)
err = radix_tree_insert(handles_vma, handle, vma);
if (unlikely(err)) {
- kfree(lut);
+ kmem_cache_free(eb->i915->luts, lut);
goto err_obj;
}
diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c
index 62aa67960bf4..af915d041281 100644
--- a/drivers/gpu/drm/i915/i915_gem_stolen.c
+++ b/drivers/gpu/drm/i915/i915_gem_stolen.c
@@ -121,8 +121,8 @@ static int i915_adjust_stolen(struct drm_i915_private *dev_priv,
if (stolen[0].start != stolen[1].start ||
stolen[0].end != stolen[1].end) {
- DRM_DEBUG_KMS("GTT within stolen memory at %pR\n", &ggtt_res);
- DRM_DEBUG_KMS("Stolen memory adjusted to %pR\n", dsm);
+ DRM_DEBUG_DRIVER("GTT within stolen memory at %pR\n", &ggtt_res);
+ DRM_DEBUG_DRIVER("Stolen memory adjusted to %pR\n", dsm);
}
}
@@ -174,18 +174,19 @@ void i915_gem_cleanup_stolen(struct drm_device *dev)
}
static void g4x_get_stolen_reserved(struct drm_i915_private *dev_priv,
- resource_size_t *base, resource_size_t *size)
+ resource_size_t *base,
+ resource_size_t *size)
{
- uint32_t reg_val = I915_READ(IS_GM45(dev_priv) ?
- CTG_STOLEN_RESERVED :
- ELK_STOLEN_RESERVED);
+ u32 reg_val = I915_READ(IS_GM45(dev_priv) ?
+ CTG_STOLEN_RESERVED :
+ ELK_STOLEN_RESERVED);
resource_size_t stolen_top = dev_priv->dsm.end + 1;
- if ((reg_val & G4X_STOLEN_RESERVED_ENABLE) == 0) {
- *base = 0;
- *size = 0;
+ DRM_DEBUG_DRIVER("%s_STOLEN_RESERVED = %08x\n",
+ IS_GM45(dev_priv) ? "CTG" : "ELK", reg_val);
+
+ if ((reg_val & G4X_STOLEN_RESERVED_ENABLE) == 0)
return;
- }
/*
* Whether ILK really reuses the ELK register for this is unclear.
@@ -193,30 +194,25 @@ static void g4x_get_stolen_reserved(struct drm_i915_private *dev_priv,
*/
WARN(IS_GEN5(dev_priv), "ILK stolen reserved found? 0x%08x\n", reg_val);
- *base = (reg_val & G4X_STOLEN_RESERVED_ADDR2_MASK) << 16;
+ if (!(reg_val & G4X_STOLEN_RESERVED_ADDR2_MASK))
+ return;
+ *base = (reg_val & G4X_STOLEN_RESERVED_ADDR2_MASK) << 16;
WARN_ON((reg_val & G4X_STOLEN_RESERVED_ADDR1_MASK) < *base);
- /* On these platforms, the register doesn't have a size field, so the
- * size is the distance between the base and the top of the stolen
- * memory. We also have the genuine case where base is zero and there's
- * nothing reserved. */
- if (*base == 0)
- *size = 0;
- else
- *size = stolen_top - *base;
+ *size = stolen_top - *base;
}
static void gen6_get_stolen_reserved(struct drm_i915_private *dev_priv,
- resource_size_t *base, resource_size_t *size)
+ resource_size_t *base,
+ resource_size_t *size)
{
- uint32_t reg_val = I915_READ(GEN6_STOLEN_RESERVED);
+ u32 reg_val = I915_READ(GEN6_STOLEN_RESERVED);
+
+ DRM_DEBUG_DRIVER("GEN6_STOLEN_RESERVED = %08x\n", reg_val);
- if ((reg_val & GEN6_STOLEN_RESERVED_ENABLE) == 0) {
- *base = 0;
- *size = 0;
+ if (!(reg_val & GEN6_STOLEN_RESERVED_ENABLE))
return;
- }
*base = reg_val & GEN6_STOLEN_RESERVED_ADDR_MASK;
@@ -239,17 +235,44 @@ static void gen6_get_stolen_reserved(struct drm_i915_private *dev_priv,
}
}
-static void gen7_get_stolen_reserved(struct drm_i915_private *dev_priv,
- resource_size_t *base, resource_size_t *size)
+static void vlv_get_stolen_reserved(struct drm_i915_private *dev_priv,
+ resource_size_t *base,
+ resource_size_t *size)
{
- uint32_t reg_val = I915_READ(GEN6_STOLEN_RESERVED);
+ u32 reg_val = I915_READ(GEN6_STOLEN_RESERVED);
+ resource_size_t stolen_top = dev_priv->dsm.end + 1;
- if ((reg_val & GEN6_STOLEN_RESERVED_ENABLE) == 0) {
- *base = 0;
- *size = 0;
+ DRM_DEBUG_DRIVER("GEN6_STOLEN_RESERVED = %08x\n", reg_val);
+
+ if (!(reg_val & GEN6_STOLEN_RESERVED_ENABLE))
return;
+
+ switch (reg_val & GEN7_STOLEN_RESERVED_SIZE_MASK) {
+ default:
+ MISSING_CASE(reg_val & GEN7_STOLEN_RESERVED_SIZE_MASK);
+ case GEN7_STOLEN_RESERVED_1M:
+ *size = 1024 * 1024;
+ break;
}
+ /*
+ * On vlv, the ADDR_MASK portion is left as 0 and HW deduces the
+ * reserved location as (top - size).
+ */
+ *base = stolen_top - *size;
+}
+
+static void gen7_get_stolen_reserved(struct drm_i915_private *dev_priv,
+ resource_size_t *base,
+ resource_size_t *size)
+{
+ u32 reg_val = I915_READ(GEN6_STOLEN_RESERVED);
+
+ DRM_DEBUG_DRIVER("GEN6_STOLEN_RESERVED = %08x\n", reg_val);
+
+ if (!(reg_val & GEN6_STOLEN_RESERVED_ENABLE))
+ return;
+
*base = reg_val & GEN7_STOLEN_RESERVED_ADDR_MASK;
switch (reg_val & GEN7_STOLEN_RESERVED_SIZE_MASK) {
@@ -266,15 +289,15 @@ static void gen7_get_stolen_reserved(struct drm_i915_private *dev_priv,
}
static void chv_get_stolen_reserved(struct drm_i915_private *dev_priv,
- resource_size_t *base, resource_size_t *size)
+ resource_size_t *base,
+ resource_size_t *size)
{
- uint32_t reg_val = I915_READ(GEN6_STOLEN_RESERVED);
+ u32 reg_val = I915_READ(GEN6_STOLEN_RESERVED);
- if ((reg_val & GEN6_STOLEN_RESERVED_ENABLE) == 0) {
- *base = 0;
- *size = 0;
+ DRM_DEBUG_DRIVER("GEN6_STOLEN_RESERVED = %08x\n", reg_val);
+
+ if (!(reg_val & GEN6_STOLEN_RESERVED_ENABLE))
return;
- }
*base = reg_val & GEN6_STOLEN_RESERVED_ADDR_MASK;
@@ -298,29 +321,22 @@ static void chv_get_stolen_reserved(struct drm_i915_private *dev_priv,
}
static void bdw_get_stolen_reserved(struct drm_i915_private *dev_priv,
- resource_size_t *base, resource_size_t *size)
+ resource_size_t *base,
+ resource_size_t *size)
{
- uint32_t reg_val = I915_READ(GEN6_STOLEN_RESERVED);
- resource_size_t stolen_top;
+ u32 reg_val = I915_READ(GEN6_STOLEN_RESERVED);
+ resource_size_t stolen_top = dev_priv->dsm.end + 1;
- if ((reg_val & GEN6_STOLEN_RESERVED_ENABLE) == 0) {
- *base = 0;
- *size = 0;
+ DRM_DEBUG_DRIVER("GEN6_STOLEN_RESERVED = %08x\n", reg_val);
+
+ if (!(reg_val & GEN6_STOLEN_RESERVED_ENABLE))
return;
- }
- stolen_top = dev_priv->dsm.end + 1;
+ if (!(reg_val & GEN6_STOLEN_RESERVED_ADDR_MASK))
+ return;
*base = reg_val & GEN6_STOLEN_RESERVED_ADDR_MASK;
-
- /* On these platforms, the register doesn't have a size field, so the
- * size is the distance between the base and the top of the stolen
- * memory. We also have the genuine case where base is zero and there's
- * nothing reserved. */
- if (*base == 0)
- *size = 0;
- else
- *size = stolen_top - *base;
+ *size = stolen_top - *base;
}
int i915_gem_init_stolen(struct drm_i915_private *dev_priv)
@@ -353,7 +369,7 @@ int i915_gem_init_stolen(struct drm_i915_private *dev_priv)
GEM_BUG_ON(dev_priv->dsm.end <= dev_priv->dsm.start);
stolen_top = dev_priv->dsm.end + 1;
- reserved_base = 0;
+ reserved_base = stolen_top;
reserved_size = 0;
switch (INTEL_GEN(dev_priv)) {
@@ -373,8 +389,12 @@ int i915_gem_init_stolen(struct drm_i915_private *dev_priv)
&reserved_base, &reserved_size);
break;
case 7:
- gen7_get_stolen_reserved(dev_priv,
- &reserved_base, &reserved_size);
+ if (IS_VALLEYVIEW(dev_priv))
+ vlv_get_stolen_reserved(dev_priv,
+ &reserved_base, &reserved_size);
+ else
+ gen7_get_stolen_reserved(dev_priv,
+ &reserved_base, &reserved_size);
break;
default:
if (IS_LP(dev_priv))
@@ -386,11 +406,16 @@ int i915_gem_init_stolen(struct drm_i915_private *dev_priv)
break;
}
- /* It is possible for the reserved base to be zero, but the register
- * field for size doesn't have a zero option. */
- if (reserved_base == 0) {
- reserved_size = 0;
+ /*
+ * Our expectation is that the reserved space is at the top of the
+ * stolen region and *never* at the bottom. If we see !reserved_base,
+ * it likely means we failed to read the registers correctly.
+ */
+ if (!reserved_base) {
+ DRM_ERROR("inconsistent reservation %pa + %pa; ignoring\n",
+ &reserved_base, &reserved_size);
reserved_base = stolen_top;
+ reserved_size = 0;
}
dev_priv->dsm_reserved =
@@ -406,9 +431,9 @@ int i915_gem_init_stolen(struct drm_i915_private *dev_priv)
* memory, so just consider the start. */
reserved_total = stolen_top - reserved_base;
- DRM_DEBUG_KMS("Memory reserved for graphics device: %lluK, usable: %lluK\n",
- (u64)resource_size(&dev_priv->dsm) >> 10,
- ((u64)resource_size(&dev_priv->dsm) - reserved_total) >> 10);
+ DRM_DEBUG_DRIVER("Memory reserved for graphics device: %lluK, usable: %lluK\n",
+ (u64)resource_size(&dev_priv->dsm) >> 10,
+ ((u64)resource_size(&dev_priv->dsm) - reserved_total) >> 10);
stolen_usable_start = 0;
/* WaSkipStolenMemoryFirstPage:bdw+ */
@@ -580,8 +605,8 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_i915_private *dev_priv
lockdep_assert_held(&dev_priv->drm.struct_mutex);
- DRM_DEBUG_KMS("creating preallocated stolen object: stolen_offset=%pa, gtt_offset=%pa, size=%pa\n",
- &stolen_offset, &gtt_offset, &size);
+ DRM_DEBUG_DRIVER("creating preallocated stolen object: stolen_offset=%pa, gtt_offset=%pa, size=%pa\n",
+ &stolen_offset, &gtt_offset, &size);
/* KISS and expect everything to be page-aligned */
if (WARN_ON(size == 0) ||
@@ -599,14 +624,14 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_i915_private *dev_priv
ret = drm_mm_reserve_node(&dev_priv->mm.stolen, stolen);
mutex_unlock(&dev_priv->mm.stolen_lock);
if (ret) {
- DRM_DEBUG_KMS("failed to allocate stolen space\n");
+ DRM_DEBUG_DRIVER("failed to allocate stolen space\n");
kfree(stolen);
return NULL;
}
obj = _i915_gem_object_create_stolen(dev_priv, stolen);
if (obj == NULL) {
- DRM_DEBUG_KMS("failed to allocate stolen object\n");
+ DRM_DEBUG_DRIVER("failed to allocate stolen object\n");
i915_gem_stolen_remove_node(dev_priv, stolen);
kfree(stolen);
return NULL;
@@ -635,7 +660,7 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_i915_private *dev_priv
size, gtt_offset, obj->cache_level,
0);
if (ret) {
- DRM_DEBUG_KMS("failed to allocate stolen GTT space\n");
+ DRM_DEBUG_DRIVER("failed to allocate stolen GTT space\n");
goto err_pages;
}
diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c
index f89ac7a8f95f..effaf982b19b 100644
--- a/drivers/gpu/drm/i915/i915_gpu_error.c
+++ b/drivers/gpu/drm/i915/i915_gpu_error.c
@@ -32,6 +32,7 @@
#include <linux/zlib.h>
#include <drm/drm_print.h>
+#include "i915_gpu_error.h"
#include "i915_drv.h"
static inline const struct intel_engine_cs *
diff --git a/drivers/gpu/drm/i915/i915_gpu_error.h b/drivers/gpu/drm/i915/i915_gpu_error.h
new file mode 100644
index 000000000000..c05b6034d718
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_gpu_error.h
@@ -0,0 +1,362 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright � 2008-2018 Intel Corporation
+ */
+
+#ifndef _I915_GPU_ERROR_H_
+#define _I915_GPU_ERROR_H_
+
+#include <linux/kref.h>
+#include <linux/ktime.h>
+#include <linux/sched.h>
+
+#include <drm/drm_mm.h>
+
+#include "intel_device_info.h"
+#include "intel_ringbuffer.h"
+#include "intel_uc_fw.h"
+
+#include "i915_gem.h"
+#include "i915_gem_gtt.h"
+#include "i915_params.h"
+
+struct drm_i915_private;
+struct intel_overlay_error_state;
+struct intel_display_error_state;
+
+struct i915_gpu_state {
+ struct kref ref;
+ ktime_t time;
+ ktime_t boottime;
+ ktime_t uptime;
+
+ struct drm_i915_private *i915;
+
+ char error_msg[128];
+ bool simulated;
+ bool awake;
+ bool wakelock;
+ bool suspended;
+ int iommu;
+ u32 reset_count;
+ u32 suspend_count;
+ struct intel_device_info device_info;
+ struct intel_driver_caps driver_caps;
+ struct i915_params params;
+
+ struct i915_error_uc {
+ struct intel_uc_fw guc_fw;
+ struct intel_uc_fw huc_fw;
+ struct drm_i915_error_object *guc_log;
+ } uc;
+
+ /* Generic register state */
+ u32 eir;
+ u32 pgtbl_er;
+ u32 ier;
+ u32 gtier[4], ngtier;
+ u32 ccid;
+ u32 derrmr;
+ u32 forcewake;
+ u32 error; /* gen6+ */
+ u32 err_int; /* gen7 */
+ u32 fault_data0; /* gen8, gen9 */
+ u32 fault_data1; /* gen8, gen9 */
+ u32 done_reg;
+ u32 gac_eco;
+ u32 gam_ecochk;
+ u32 gab_ctl;
+ u32 gfx_mode;
+
+ u32 nfence;
+ u64 fence[I915_MAX_NUM_FENCES];
+ struct intel_overlay_error_state *overlay;
+ struct intel_display_error_state *display;
+
+ struct drm_i915_error_engine {
+ int engine_id;
+ /* Software tracked state */
+ bool idle;
+ bool waiting;
+ int num_waiters;
+ unsigned long hangcheck_timestamp;
+ bool hangcheck_stalled;
+ enum intel_engine_hangcheck_action hangcheck_action;
+ struct i915_address_space *vm;
+ int num_requests;
+ u32 reset_count;
+
+ /* position of active request inside the ring */
+ u32 rq_head, rq_post, rq_tail;
+
+ /* our own tracking of ring head and tail */
+ u32 cpu_ring_head;
+ u32 cpu_ring_tail;
+
+ u32 last_seqno;
+
+ /* Register state */
+ u32 start;
+ u32 tail;
+ u32 head;
+ u32 ctl;
+ u32 mode;
+ u32 hws;
+ u32 ipeir;
+ u32 ipehr;
+ u32 bbstate;
+ u32 instpm;
+ u32 instps;
+ u32 seqno;
+ u64 bbaddr;
+ u64 acthd;
+ u32 fault_reg;
+ u64 faddr;
+ u32 rc_psmi; /* sleep state */
+ u32 semaphore_mboxes[I915_NUM_ENGINES - 1];
+ struct intel_instdone instdone;
+
+ struct drm_i915_error_context {
+ char comm[TASK_COMM_LEN];
+ pid_t pid;
+ u32 handle;
+ u32 hw_id;
+ int priority;
+ int ban_score;
+ int active;
+ int guilty;
+ bool bannable;
+ } context;
+
+ struct drm_i915_error_object {
+ u64 gtt_offset;
+ u64 gtt_size;
+ int page_count;
+ int unused;
+ u32 *pages[0];
+ } *ringbuffer, *batchbuffer, *wa_batchbuffer, *ctx, *hws_page;
+
+ struct drm_i915_error_object **user_bo;
+ long user_bo_count;
+
+ struct drm_i915_error_object *wa_ctx;
+ struct drm_i915_error_object *default_state;
+
+ struct drm_i915_error_request {
+ long jiffies;
+ pid_t pid;
+ u32 context;
+ int priority;
+ int ban_score;
+ u32 seqno;
+ u32 head;
+ u32 tail;
+ } *requests, execlist[EXECLIST_MAX_PORTS];
+ unsigned int num_ports;
+
+ struct drm_i915_error_waiter {
+ char comm[TASK_COMM_LEN];
+ pid_t pid;
+ u32 seqno;
+ } *waiters;
+
+ struct {
+ u32 gfx_mode;
+ union {
+ u64 pdp[4];
+ u32 pp_dir_base;
+ };
+ } vm_info;
+ } engine[I915_NUM_ENGINES];
+
+ struct drm_i915_error_buffer {
+ u32 size;
+ u32 name;
+ u32 rseqno[I915_NUM_ENGINES], wseqno;
+ u64 gtt_offset;
+ u32 read_domains;
+ u32 write_domain;
+ s32 fence_reg:I915_MAX_NUM_FENCE_BITS;
+ u32 tiling:2;
+ u32 dirty:1;
+ u32 purgeable:1;
+ u32 userptr:1;
+ s32 engine:4;
+ u32 cache_level:3;
+ } *active_bo[I915_NUM_ENGINES], *pinned_bo;
+ u32 active_bo_count[I915_NUM_ENGINES], pinned_bo_count;
+ struct i915_address_space *active_vm[I915_NUM_ENGINES];
+};
+
+struct i915_gpu_error {
+ /* For hangcheck timer */
+#define DRM_I915_HANGCHECK_PERIOD 1500 /* in ms */
+#define DRM_I915_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD)
+
+ struct delayed_work hangcheck_work;
+
+ /* For reset and error_state handling. */
+ spinlock_t lock;
+ /* Protected by the above dev->gpu_error.lock. */
+ struct i915_gpu_state *first_error;
+
+ atomic_t pending_fb_pin;
+
+ unsigned long missed_irq_rings;
+
+ /**
+ * State variable controlling the reset flow and count
+ *
+ * This is a counter which gets incremented when reset is triggered,
+ *
+ * Before the reset commences, the I915_RESET_BACKOFF bit is set
+ * meaning that any waiters holding onto the struct_mutex should
+ * relinquish the lock immediately in order for the reset to start.
+ *
+ * If reset is not completed successfully, the I915_WEDGE bit is
+ * set meaning that hardware is terminally sour and there is no
+ * recovery. All waiters on the reset_queue will be woken when
+ * that happens.
+ *
+ * This counter is used by the wait_seqno code to notice that reset
+ * event happened and it needs to restart the entire ioctl (since most
+ * likely the seqno it waited for won't ever signal anytime soon).
+ *
+ * This is important for lock-free wait paths, where no contended lock
+ * naturally enforces the correct ordering between the bail-out of the
+ * waiter and the gpu reset work code.
+ */
+ unsigned long reset_count;
+
+ /**
+ * flags: Control various stages of the GPU reset
+ *
+ * #I915_RESET_BACKOFF - When we start a reset, we want to stop any
+ * other users acquiring the struct_mutex. To do this we set the
+ * #I915_RESET_BACKOFF bit in the error flags when we detect a reset
+ * and then check for that bit before acquiring the struct_mutex (in
+ * i915_mutex_lock_interruptible()?). I915_RESET_BACKOFF serves a
+ * secondary role in preventing two concurrent global reset attempts.
+ *
+ * #I915_RESET_HANDOFF - To perform the actual GPU reset, we need the
+ * struct_mutex. We try to acquire the struct_mutex in the reset worker,
+ * but it may be held by some long running waiter (that we cannot
+ * interrupt without causing trouble). Once we are ready to do the GPU
+ * reset, we set the I915_RESET_HANDOFF bit and wakeup any waiters. If
+ * they already hold the struct_mutex and want to participate they can
+ * inspect the bit and do the reset directly, otherwise the worker
+ * waits for the struct_mutex.
+ *
+ * #I915_RESET_ENGINE[num_engines] - Since the driver doesn't need to
+ * acquire the struct_mutex to reset an engine, we need an explicit
+ * flag to prevent two concurrent reset attempts in the same engine.
+ * As the number of engines continues to grow, allocate the flags from
+ * the most significant bits.
+ *
+ * #I915_WEDGED - If reset fails and we can no longer use the GPU,
+ * we set the #I915_WEDGED bit. Prior to command submission, e.g.
+ * i915_request_alloc(), this bit is checked and the sequence
+ * aborted (with -EIO reported to userspace) if set.
+ */
+ unsigned long flags;
+#define I915_RESET_BACKOFF 0
+#define I915_RESET_HANDOFF 1
+#define I915_RESET_MODESET 2
+#define I915_WEDGED (BITS_PER_LONG - 1)
+#define I915_RESET_ENGINE (I915_WEDGED - I915_NUM_ENGINES)
+
+ /** Number of times an engine has been reset */
+ u32 reset_engine_count[I915_NUM_ENGINES];
+
+ /** Set of stalled engines with guilty requests, in the current reset */
+ u32 stalled_mask;
+
+ /** Reason for the current *global* reset */
+ const char *reason;
+
+ /**
+ * Waitqueue to signal when a hang is detected. Used to for waiters
+ * to release the struct_mutex for the reset to procede.
+ */
+ wait_queue_head_t wait_queue;
+
+ /**
+ * Waitqueue to signal when the reset has completed. Used by clients
+ * that wait for dev_priv->mm.wedged to settle.
+ */
+ wait_queue_head_t reset_queue;
+
+ /* For missed irq/seqno simulation. */
+ unsigned long test_irq_rings;
+};
+
+struct drm_i915_error_state_buf {
+ struct drm_i915_private *i915;
+ unsigned int bytes;
+ unsigned int size;
+ int err;
+ u8 *buf;
+ loff_t start;
+ loff_t pos;
+};
+
+#if IS_ENABLED(CONFIG_DRM_I915_CAPTURE_ERROR)
+
+__printf(2, 3)
+void i915_error_printf(struct drm_i915_error_state_buf *e, const char *f, ...);
+int i915_error_state_to_str(struct drm_i915_error_state_buf *estr,
+ const struct i915_gpu_state *gpu);
+int i915_error_state_buf_init(struct drm_i915_error_state_buf *eb,
+ struct drm_i915_private *i915,
+ size_t count, loff_t pos);
+
+static inline void
+i915_error_state_buf_release(struct drm_i915_error_state_buf *eb)
+{
+ kfree(eb->buf);
+}
+
+struct i915_gpu_state *i915_capture_gpu_state(struct drm_i915_private *i915);
+void i915_capture_error_state(struct drm_i915_private *dev_priv,
+ u32 engine_mask,
+ const char *error_msg);
+
+static inline struct i915_gpu_state *
+i915_gpu_state_get(struct i915_gpu_state *gpu)
+{
+ kref_get(&gpu->ref);
+ return gpu;
+}
+
+void __i915_gpu_state_free(struct kref *kref);
+static inline void i915_gpu_state_put(struct i915_gpu_state *gpu)
+{
+ if (gpu)
+ kref_put(&gpu->ref, __i915_gpu_state_free);
+}
+
+struct i915_gpu_state *i915_first_error_state(struct drm_i915_private *i915);
+void i915_reset_error_state(struct drm_i915_private *i915);
+
+#else
+
+static inline void i915_capture_error_state(struct drm_i915_private *dev_priv,
+ u32 engine_mask,
+ const char *error_msg)
+{
+}
+
+static inline struct i915_gpu_state *
+i915_first_error_state(struct drm_i915_private *i915)
+{
+ return NULL;
+}
+
+static inline void i915_reset_error_state(struct drm_i915_private *i915)
+{
+}
+
+#endif /* IS_ENABLED(CONFIG_DRM_I915_CAPTURE_ERROR) */
+
+#endif /* _I915_GPU_ERROR_H_ */
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 633c18785c1e..b03d18561b55 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -243,6 +243,41 @@ void i915_hotplug_interrupt_update(struct drm_i915_private *dev_priv,
spin_unlock_irq(&dev_priv->irq_lock);
}
+static u32
+gen11_gt_engine_identity(struct drm_i915_private * const i915,
+ const unsigned int bank, const unsigned int bit);
+
+static bool gen11_reset_one_iir(struct drm_i915_private * const i915,
+ const unsigned int bank,
+ const unsigned int bit)
+{
+ void __iomem * const regs = i915->regs;
+ u32 dw;
+
+ lockdep_assert_held(&i915->irq_lock);
+
+ dw = raw_reg_read(regs, GEN11_GT_INTR_DW(bank));
+ if (dw & BIT(bit)) {
+ /*
+ * According to the BSpec, DW_IIR bits cannot be cleared without
+ * first servicing the Selector & Shared IIR registers.
+ */
+ gen11_gt_engine_identity(i915, bank, bit);
+
+ /*
+ * We locked GT INT DW by reading it. If we want to (try
+ * to) recover from this succesfully, we need to clear
+ * our bit, otherwise we are locking the register for
+ * everybody.
+ */
+ raw_reg_write(regs, GEN11_GT_INTR_DW(bank), BIT(bit));
+
+ return true;
+ }
+
+ return false;
+}
+
/**
* ilk_update_display_irq - update DEIMR
* @dev_priv: driver private
@@ -308,17 +343,29 @@ void gen5_disable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask)
static i915_reg_t gen6_pm_iir(struct drm_i915_private *dev_priv)
{
+ WARN_ON_ONCE(INTEL_GEN(dev_priv) >= 11);
+
return INTEL_GEN(dev_priv) >= 8 ? GEN8_GT_IIR(2) : GEN6_PMIIR;
}
static i915_reg_t gen6_pm_imr(struct drm_i915_private *dev_priv)
{
- return INTEL_GEN(dev_priv) >= 8 ? GEN8_GT_IMR(2) : GEN6_PMIMR;
+ if (INTEL_GEN(dev_priv) >= 11)
+ return GEN11_GPM_WGBOXPERF_INTR_MASK;
+ else if (INTEL_GEN(dev_priv) >= 8)
+ return GEN8_GT_IMR(2);
+ else
+ return GEN6_PMIMR;
}
static i915_reg_t gen6_pm_ier(struct drm_i915_private *dev_priv)
{
- return INTEL_GEN(dev_priv) >= 8 ? GEN8_GT_IER(2) : GEN6_PMIER;
+ if (INTEL_GEN(dev_priv) >= 11)
+ return GEN11_GPM_WGBOXPERF_INTR_ENABLE;
+ else if (INTEL_GEN(dev_priv) >= 8)
+ return GEN8_GT_IER(2);
+ else
+ return GEN6_PMIER;
}
/**
@@ -400,6 +447,18 @@ static void gen6_disable_pm_irq(struct drm_i915_private *dev_priv, u32 disable_m
/* though a barrier is missing here, but don't really need a one */
}
+void gen11_reset_rps_interrupts(struct drm_i915_private *dev_priv)
+{
+ spin_lock_irq(&dev_priv->irq_lock);
+
+ while (gen11_reset_one_iir(dev_priv, 0, GEN11_GTPM))
+ ;
+
+ dev_priv->gt_pm.rps.pm_iir = 0;
+
+ spin_unlock_irq(&dev_priv->irq_lock);
+}
+
void gen6_reset_rps_interrupts(struct drm_i915_private *dev_priv)
{
spin_lock_irq(&dev_priv->irq_lock);
@@ -415,12 +474,14 @@ void gen6_enable_rps_interrupts(struct drm_i915_private *dev_priv)
if (READ_ONCE(rps->interrupts_enabled))
return;
- if (WARN_ON_ONCE(IS_GEN11(dev_priv)))
- return;
-
spin_lock_irq(&dev_priv->irq_lock);
WARN_ON_ONCE(rps->pm_iir);
- WARN_ON_ONCE(I915_READ(gen6_pm_iir(dev_priv)) & dev_priv->pm_rps_events);
+
+ if (INTEL_GEN(dev_priv) >= 11)
+ WARN_ON_ONCE(gen11_reset_one_iir(dev_priv, 0, GEN11_GTPM));
+ else
+ WARN_ON_ONCE(I915_READ(gen6_pm_iir(dev_priv)) & dev_priv->pm_rps_events);
+
rps->interrupts_enabled = true;
gen6_enable_pm_irq(dev_priv, dev_priv->pm_rps_events);
@@ -434,9 +495,6 @@ void gen6_disable_rps_interrupts(struct drm_i915_private *dev_priv)
if (!READ_ONCE(rps->interrupts_enabled))
return;
- if (WARN_ON_ONCE(IS_GEN11(dev_priv)))
- return;
-
spin_lock_irq(&dev_priv->irq_lock);
rps->interrupts_enabled = false;
@@ -453,7 +511,10 @@ void gen6_disable_rps_interrupts(struct drm_i915_private *dev_priv)
* state of the worker can be discarded.
*/
cancel_work_sync(&rps->work);
- gen6_reset_rps_interrupts(dev_priv);
+ if (INTEL_GEN(dev_priv) >= 11)
+ gen11_reset_rps_interrupts(dev_priv);
+ else
+ gen6_reset_rps_interrupts(dev_priv);
}
void gen9_reset_guc_interrupts(struct drm_i915_private *dev_priv)
@@ -1399,19 +1460,18 @@ static void snb_gt_irq_handler(struct drm_i915_private *dev_priv,
}
static void
-gen8_cs_irq_handler(struct intel_engine_cs *engine, u32 iir, int test_shift)
+gen8_cs_irq_handler(struct intel_engine_cs *engine, u32 iir)
{
struct intel_engine_execlists * const execlists = &engine->execlists;
bool tasklet = false;
- if (iir & (GT_CONTEXT_SWITCH_INTERRUPT << test_shift)) {
- if (READ_ONCE(engine->execlists.active)) {
- __set_bit(ENGINE_IRQ_EXECLIST, &engine->irq_posted);
- tasklet = true;
- }
+ if (iir & GT_CONTEXT_SWITCH_INTERRUPT) {
+ if (READ_ONCE(engine->execlists.active))
+ tasklet = !test_and_set_bit(ENGINE_IRQ_EXECLIST,
+ &engine->irq_posted);
}
- if (iir & (GT_RENDER_USER_INTERRUPT << test_shift)) {
+ if (iir & GT_RENDER_USER_INTERRUPT) {
notify_ring(engine);
tasklet |= USES_GUC_SUBMISSION(engine->i915);
}
@@ -1466,21 +1526,21 @@ static void gen8_gt_irq_handler(struct drm_i915_private *i915,
{
if (master_ctl & (GEN8_GT_RCS_IRQ | GEN8_GT_BCS_IRQ)) {
gen8_cs_irq_handler(i915->engine[RCS],
- gt_iir[0], GEN8_RCS_IRQ_SHIFT);
+ gt_iir[0] >> GEN8_RCS_IRQ_SHIFT);
gen8_cs_irq_handler(i915->engine[BCS],
- gt_iir[0], GEN8_BCS_IRQ_SHIFT);
+ gt_iir[0] >> GEN8_BCS_IRQ_SHIFT);
}
if (master_ctl & (GEN8_GT_VCS1_IRQ | GEN8_GT_VCS2_IRQ)) {
gen8_cs_irq_handler(i915->engine[VCS],
- gt_iir[1], GEN8_VCS1_IRQ_SHIFT);
+ gt_iir[1] >> GEN8_VCS1_IRQ_SHIFT);
gen8_cs_irq_handler(i915->engine[VCS2],
- gt_iir[1], GEN8_VCS2_IRQ_SHIFT);
+ gt_iir[1] >> GEN8_VCS2_IRQ_SHIFT);
}
if (master_ctl & GEN8_GT_VECS_IRQ) {
gen8_cs_irq_handler(i915->engine[VECS],
- gt_iir[3], GEN8_VECS_IRQ_SHIFT);
+ gt_iir[3] >> GEN8_VECS_IRQ_SHIFT);
}
if (master_ctl & (GEN8_GT_PM_IRQ | GEN8_GT_GUC_IRQ)) {
@@ -1627,7 +1687,7 @@ static void display_pipe_crc_irq_handler(struct drm_i915_private *dev_priv,
int head, tail;
spin_lock(&pipe_crc->lock);
- if (pipe_crc->source) {
+ if (pipe_crc->source && !crtc->base.crc.opened) {
if (!pipe_crc->entries) {
spin_unlock(&pipe_crc->lock);
DRM_DEBUG_KMS("spurious interrupt\n");
@@ -1667,7 +1727,7 @@ static void display_pipe_crc_irq_handler(struct drm_i915_private *dev_priv,
* On GEN8+ sometimes the second CRC is bonkers as well, so
* don't trust that one either.
*/
- if (pipe_crc->skipped == 0 ||
+ if (pipe_crc->skipped <= 0 ||
(INTEL_GEN(dev_priv) >= 8 && pipe_crc->skipped == 1)) {
pipe_crc->skipped++;
spin_unlock(&pipe_crc->lock);
@@ -1766,37 +1826,8 @@ static void gen6_rps_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir)
static void gen9_guc_irq_handler(struct drm_i915_private *dev_priv, u32 gt_iir)
{
- if (gt_iir & GEN9_GUC_TO_HOST_INT_EVENT) {
- /* Sample the log buffer flush related bits & clear them out now
- * itself from the message identity register to minimize the
- * probability of losing a flush interrupt, when there are back
- * to back flush interrupts.
- * There can be a new flush interrupt, for different log buffer
- * type (like for ISR), whilst Host is handling one (for DPC).
- * Since same bit is used in message register for ISR & DPC, it
- * could happen that GuC sets the bit for 2nd interrupt but Host
- * clears out the bit on handling the 1st interrupt.
- */
- u32 msg, flush;
-
- msg = I915_READ(SOFT_SCRATCH(15));
- flush = msg & (INTEL_GUC_RECV_MSG_CRASH_DUMP_POSTED |
- INTEL_GUC_RECV_MSG_FLUSH_LOG_BUFFER);
- if (flush) {
- /* Clear the message bits that are handled */
- I915_WRITE(SOFT_SCRATCH(15), msg & ~flush);
-
- /* Handle flush interrupt in bottom half */
- queue_work(dev_priv->guc.log.runtime.flush_wq,
- &dev_priv->guc.log.runtime.flush_work);
-
- dev_priv->guc.log.flush_interrupt_count++;
- } else {
- /* Not clearing of unhandled event bits won't result in
- * re-triggering of the interrupt.
- */
- }
- }
+ if (gt_iir & GEN9_GUC_TO_HOST_INT_EVENT)
+ intel_guc_to_host_event_handler(&dev_priv->guc);
}
static void i9xx_pipestat_irq_reset(struct drm_i915_private *dev_priv)
@@ -2762,58 +2793,16 @@ static void __fini_wedge(struct wedge_me *w)
(W)->i915; \
__fini_wedge((W)))
-static __always_inline void
-gen11_cs_irq_handler(struct intel_engine_cs * const engine, const u32 iir)
-{
- gen8_cs_irq_handler(engine, iir, 0);
-}
-
-static void
-gen11_gt_engine_irq_handler(struct drm_i915_private * const i915,
- const unsigned int bank,
- const unsigned int engine_n,
- const u16 iir)
-{
- struct intel_engine_cs ** const engine = i915->engine;
-
- switch (bank) {
- case 0:
- switch (engine_n) {
-
- case GEN11_RCS0:
- return gen11_cs_irq_handler(engine[RCS], iir);
-
- case GEN11_BCS:
- return gen11_cs_irq_handler(engine[BCS], iir);
- }
- case 1:
- switch (engine_n) {
-
- case GEN11_VCS(0):
- return gen11_cs_irq_handler(engine[_VCS(0)], iir);
- case GEN11_VCS(1):
- return gen11_cs_irq_handler(engine[_VCS(1)], iir);
- case GEN11_VCS(2):
- return gen11_cs_irq_handler(engine[_VCS(2)], iir);
- case GEN11_VCS(3):
- return gen11_cs_irq_handler(engine[_VCS(3)], iir);
-
- case GEN11_VECS(0):
- return gen11_cs_irq_handler(engine[_VECS(0)], iir);
- case GEN11_VECS(1):
- return gen11_cs_irq_handler(engine[_VECS(1)], iir);
- }
- }
-}
-
static u32
-gen11_gt_engine_intr(struct drm_i915_private * const i915,
- const unsigned int bank, const unsigned int bit)
+gen11_gt_engine_identity(struct drm_i915_private * const i915,
+ const unsigned int bank, const unsigned int bit)
{
void __iomem * const regs = i915->regs;
u32 timeout_ts;
u32 ident;
+ lockdep_assert_held(&i915->irq_lock);
+
raw_reg_write(regs, GEN11_IIR_REG_SELECTOR(bank), BIT(bit));
/*
@@ -2835,42 +2824,101 @@ gen11_gt_engine_intr(struct drm_i915_private * const i915,
raw_reg_write(regs, GEN11_INTR_IDENTITY_REG(bank),
GEN11_INTR_DATA_VALID);
- return ident & GEN11_INTR_ENGINE_MASK;
+ return ident;
}
static void
-gen11_gt_irq_handler(struct drm_i915_private * const i915,
- const u32 master_ctl)
+gen11_other_irq_handler(struct drm_i915_private * const i915,
+ const u8 instance, const u16 iir)
+{
+ if (instance == OTHER_GTPM_INSTANCE)
+ return gen6_rps_irq_handler(i915, iir);
+
+ WARN_ONCE(1, "unhandled other interrupt instance=0x%x, iir=0x%x\n",
+ instance, iir);
+}
+
+static void
+gen11_engine_irq_handler(struct drm_i915_private * const i915,
+ const u8 class, const u8 instance, const u16 iir)
+{
+ struct intel_engine_cs *engine;
+
+ if (instance <= MAX_ENGINE_INSTANCE)
+ engine = i915->engine_class[class][instance];
+ else
+ engine = NULL;
+
+ if (likely(engine))
+ return gen8_cs_irq_handler(engine, iir);
+
+ WARN_ONCE(1, "unhandled engine interrupt class=0x%x, instance=0x%x\n",
+ class, instance);
+}
+
+static void
+gen11_gt_identity_handler(struct drm_i915_private * const i915,
+ const u32 identity)
+{
+ const u8 class = GEN11_INTR_ENGINE_CLASS(identity);
+ const u8 instance = GEN11_INTR_ENGINE_INSTANCE(identity);
+ const u16 intr = GEN11_INTR_ENGINE_INTR(identity);
+
+ if (unlikely(!intr))
+ return;
+
+ if (class <= COPY_ENGINE_CLASS)
+ return gen11_engine_irq_handler(i915, class, instance, intr);
+
+ if (class == OTHER_CLASS)
+ return gen11_other_irq_handler(i915, instance, intr);
+
+ WARN_ONCE(1, "unknown interrupt class=0x%x, instance=0x%x, intr=0x%x\n",
+ class, instance, intr);
+}
+
+static void
+gen11_gt_bank_handler(struct drm_i915_private * const i915,
+ const unsigned int bank)
{
void __iomem * const regs = i915->regs;
- unsigned int bank;
+ unsigned long intr_dw;
+ unsigned int bit;
- for (bank = 0; bank < 2; bank++) {
- unsigned long intr_dw;
- unsigned int bit;
+ lockdep_assert_held(&i915->irq_lock);
- if (!(master_ctl & GEN11_GT_DW_IRQ(bank)))
- continue;
+ intr_dw = raw_reg_read(regs, GEN11_GT_INTR_DW(bank));
- intr_dw = raw_reg_read(regs, GEN11_GT_INTR_DW(bank));
+ if (unlikely(!intr_dw)) {
+ DRM_ERROR("GT_INTR_DW%u blank!\n", bank);
+ return;
+ }
- if (unlikely(!intr_dw)) {
- DRM_ERROR("GT_INTR_DW%u blank!\n", bank);
- continue;
- }
+ for_each_set_bit(bit, &intr_dw, 32) {
+ const u32 ident = gen11_gt_engine_identity(i915,
+ bank, bit);
- for_each_set_bit(bit, &intr_dw, 32) {
- const u16 iir = gen11_gt_engine_intr(i915, bank, bit);
+ gen11_gt_identity_handler(i915, ident);
+ }
- if (unlikely(!iir))
- continue;
+ /* Clear must be after shared has been served for engine */
+ raw_reg_write(regs, GEN11_GT_INTR_DW(bank), intr_dw);
+}
- gen11_gt_engine_irq_handler(i915, bank, bit, iir);
- }
+static void
+gen11_gt_irq_handler(struct drm_i915_private * const i915,
+ const u32 master_ctl)
+{
+ unsigned int bank;
- /* Clear must be after shared has been served for engine */
- raw_reg_write(regs, GEN11_GT_INTR_DW(bank), intr_dw);
+ spin_lock(&i915->irq_lock);
+
+ for (bank = 0; bank < 2; bank++) {
+ if (master_ctl & GEN11_GT_DW_IRQ(bank))
+ gen11_gt_bank_handler(i915, bank);
}
+
+ spin_unlock(&i915->irq_lock);
}
static irqreturn_t gen11_irq_handler(int irq, void *arg)
@@ -2912,15 +2960,11 @@ static irqreturn_t gen11_irq_handler(int irq, void *arg)
return IRQ_HANDLED;
}
-/**
- * i915_reset_device - do process context error handling work
- * @dev_priv: i915 device private
- *
- * Fire an error uevent so userspace can see that a hang or error
- * was detected.
- */
-static void i915_reset_device(struct drm_i915_private *dev_priv)
+static void i915_reset_device(struct drm_i915_private *dev_priv,
+ u32 engine_mask,
+ const char *reason)
{
+ struct i915_gpu_error *error = &dev_priv->gpu_error;
struct kobject *kobj = &dev_priv->drm.primary->kdev->kobj;
char *error_event[] = { I915_ERROR_UEVENT "=1", NULL };
char *reset_event[] = { I915_RESET_UEVENT "=1", NULL };
@@ -2936,29 +2980,35 @@ static void i915_reset_device(struct drm_i915_private *dev_priv)
i915_wedge_on_timeout(&w, dev_priv, 5*HZ) {
intel_prepare_reset(dev_priv);
+ error->reason = reason;
+ error->stalled_mask = engine_mask;
+
/* Signal that locked waiters should reset the GPU */
- set_bit(I915_RESET_HANDOFF, &dev_priv->gpu_error.flags);
- wake_up_all(&dev_priv->gpu_error.wait_queue);
+ smp_mb__before_atomic();
+ set_bit(I915_RESET_HANDOFF, &error->flags);
+ wake_up_all(&error->wait_queue);
/* Wait for anyone holding the lock to wakeup, without
* blocking indefinitely on struct_mutex.
*/
do {
if (mutex_trylock(&dev_priv->drm.struct_mutex)) {
- i915_reset(dev_priv, 0);
+ i915_reset(dev_priv, engine_mask, reason);
mutex_unlock(&dev_priv->drm.struct_mutex);
}
- } while (wait_on_bit_timeout(&dev_priv->gpu_error.flags,
+ } while (wait_on_bit_timeout(&error->flags,
I915_RESET_HANDOFF,
TASK_UNINTERRUPTIBLE,
1));
+ error->stalled_mask = 0;
+ error->reason = NULL;
+
intel_finish_reset(dev_priv);
}
- if (!test_bit(I915_WEDGED, &dev_priv->gpu_error.flags))
- kobject_uevent_env(kobj,
- KOBJ_CHANGE, reset_done_event);
+ if (!test_bit(I915_WEDGED, &error->flags))
+ kobject_uevent_env(kobj, KOBJ_CHANGE, reset_done_event);
}
static void i915_clear_error_registers(struct drm_i915_private *dev_priv)
@@ -2990,6 +3040,7 @@ static void i915_clear_error_registers(struct drm_i915_private *dev_priv)
* i915_handle_error - handle a gpu error
* @dev_priv: i915 device private
* @engine_mask: mask representing engines that are hung
+ * @flags: control flags
* @fmt: Error message format string
*
* Do some basic checking of register state at error time and
@@ -3000,16 +3051,23 @@ static void i915_clear_error_registers(struct drm_i915_private *dev_priv)
*/
void i915_handle_error(struct drm_i915_private *dev_priv,
u32 engine_mask,
+ unsigned long flags,
const char *fmt, ...)
{
struct intel_engine_cs *engine;
unsigned int tmp;
- va_list args;
char error_msg[80];
+ char *msg = NULL;
- va_start(args, fmt);
- vscnprintf(error_msg, sizeof(error_msg), fmt, args);
- va_end(args);
+ if (fmt) {
+ va_list args;
+
+ va_start(args, fmt);
+ vscnprintf(error_msg, sizeof(error_msg), fmt, args);
+ va_end(args);
+
+ msg = error_msg;
+ }
/*
* In most cases it's guaranteed that we get here with an RPM
@@ -3020,8 +3078,12 @@ void i915_handle_error(struct drm_i915_private *dev_priv,
*/
intel_runtime_pm_get(dev_priv);
- i915_capture_error_state(dev_priv, engine_mask, error_msg);
- i915_clear_error_registers(dev_priv);
+ engine_mask &= INTEL_INFO(dev_priv)->ring_mask;
+
+ if (flags & I915_ERROR_CAPTURE) {
+ i915_capture_error_state(dev_priv, engine_mask, msg);
+ i915_clear_error_registers(dev_priv);
+ }
/*
* Try engine reset when available. We fall back to full reset if
@@ -3034,7 +3096,7 @@ void i915_handle_error(struct drm_i915_private *dev_priv,
&dev_priv->gpu_error.flags))
continue;
- if (i915_reset_engine(engine, 0) == 0)
+ if (i915_reset_engine(engine, msg) == 0)
engine_mask &= ~intel_engine_flag(engine);
clear_bit(I915_RESET_ENGINE + engine->id,
@@ -3064,7 +3126,7 @@ void i915_handle_error(struct drm_i915_private *dev_priv,
TASK_UNINTERRUPTIBLE);
}
- i915_reset_device(dev_priv);
+ i915_reset_device(dev_priv, engine_mask, msg);
for_each_engine(engine, dev_priv, tmp) {
clear_bit(I915_RESET_ENGINE + engine->id,
@@ -3349,6 +3411,9 @@ static void gen11_gt_irq_reset(struct drm_i915_private *dev_priv)
I915_WRITE(GEN11_VCS0_VCS1_INTR_MASK, ~0);
I915_WRITE(GEN11_VCS2_VCS3_INTR_MASK, ~0);
I915_WRITE(GEN11_VECS0_VECS1_INTR_MASK, ~0);
+
+ I915_WRITE(GEN11_GPM_WGBOXPERF_INTR_ENABLE, 0);
+ I915_WRITE(GEN11_GPM_WGBOXPERF_INTR_MASK, ~0);
}
static void gen11_irq_reset(struct drm_device *dev)
@@ -3887,7 +3952,14 @@ static void gen11_gt_irq_postinstall(struct drm_i915_private *dev_priv)
I915_WRITE(GEN11_VCS2_VCS3_INTR_MASK, ~(irqs | irqs << 16));
I915_WRITE(GEN11_VECS0_VECS1_INTR_MASK, ~(irqs | irqs << 16));
- dev_priv->pm_imr = 0xffffffff; /* TODO */
+ /*
+ * RPS interrupts will get enabled/disabled on demand when RPS itself
+ * is enabled/disabled.
+ */
+ dev_priv->pm_ier = 0x0;
+ dev_priv->pm_imr = ~dev_priv->pm_ier;
+ I915_WRITE(GEN11_GPM_WGBOXPERF_INTR_ENABLE, 0);
+ I915_WRITE(GEN11_GPM_WGBOXPERF_INTR_MASK, ~0);
}
static int gen11_irq_postinstall(struct drm_device *dev)
diff --git a/drivers/gpu/drm/i915/i915_oa_icl.c b/drivers/gpu/drm/i915/i915_oa_icl.c
new file mode 100644
index 000000000000..a5667926e3de
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_oa_icl.c
@@ -0,0 +1,118 @@
+/*
+ * Autogenerated file by GPU Top : https://github.com/rib/gputop
+ * DO NOT EDIT manually!
+ *
+ *
+ * Copyright (c) 2015 Intel Corporation
+ *
+ * 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
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * 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 NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS 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/sysfs.h>
+
+#include "i915_drv.h"
+#include "i915_oa_icl.h"
+
+static const struct i915_oa_reg b_counter_config_test_oa[] = {
+ { _MMIO(0x2740), 0x00000000 },
+ { _MMIO(0x2710), 0x00000000 },
+ { _MMIO(0x2714), 0xf0800000 },
+ { _MMIO(0x2720), 0x00000000 },
+ { _MMIO(0x2724), 0xf0800000 },
+ { _MMIO(0x2770), 0x00000004 },
+ { _MMIO(0x2774), 0x0000ffff },
+ { _MMIO(0x2778), 0x00000003 },
+ { _MMIO(0x277c), 0x0000ffff },
+ { _MMIO(0x2780), 0x00000007 },
+ { _MMIO(0x2784), 0x0000ffff },
+ { _MMIO(0x2788), 0x00100002 },
+ { _MMIO(0x278c), 0x0000fff7 },
+ { _MMIO(0x2790), 0x00100002 },
+ { _MMIO(0x2794), 0x0000ffcf },
+ { _MMIO(0x2798), 0x00100082 },
+ { _MMIO(0x279c), 0x0000ffef },
+ { _MMIO(0x27a0), 0x001000c2 },
+ { _MMIO(0x27a4), 0x0000ffe7 },
+ { _MMIO(0x27a8), 0x00100001 },
+ { _MMIO(0x27ac), 0x0000ffe7 },
+};
+
+static const struct i915_oa_reg flex_eu_config_test_oa[] = {
+};
+
+static const struct i915_oa_reg mux_config_test_oa[] = {
+ { _MMIO(0xd04), 0x00000200 },
+ { _MMIO(0x9840), 0x00000000 },
+ { _MMIO(0x9884), 0x00000000 },
+ { _MMIO(0x9888), 0x10060000 },
+ { _MMIO(0x9888), 0x22060000 },
+ { _MMIO(0x9888), 0x16060000 },
+ { _MMIO(0x9888), 0x24060000 },
+ { _MMIO(0x9888), 0x18060000 },
+ { _MMIO(0x9888), 0x1a060000 },
+ { _MMIO(0x9888), 0x12060000 },
+ { _MMIO(0x9888), 0x14060000 },
+ { _MMIO(0x9888), 0x10060000 },
+ { _MMIO(0x9888), 0x22060000 },
+ { _MMIO(0x9884), 0x00000003 },
+ { _MMIO(0x9888), 0x16130000 },
+ { _MMIO(0x9888), 0x24000001 },
+ { _MMIO(0x9888), 0x0e130056 },
+ { _MMIO(0x9888), 0x10130000 },
+ { _MMIO(0x9888), 0x1a130000 },
+ { _MMIO(0x9888), 0x541f0001 },
+ { _MMIO(0x9888), 0x181f0000 },
+ { _MMIO(0x9888), 0x4c1f0000 },
+ { _MMIO(0x9888), 0x301f0000 },
+};
+
+static ssize_t
+show_test_oa_id(struct device *kdev, struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "1\n");
+}
+
+void
+i915_perf_load_test_config_icl(struct drm_i915_private *dev_priv)
+{
+ strlcpy(dev_priv->perf.oa.test_config.uuid,
+ "a291665e-244b-4b76-9b9a-01de9d3c8068",
+ sizeof(dev_priv->perf.oa.test_config.uuid));
+ dev_priv->perf.oa.test_config.id = 1;
+
+ dev_priv->perf.oa.test_config.mux_regs = mux_config_test_oa;
+ dev_priv->perf.oa.test_config.mux_regs_len = ARRAY_SIZE(mux_config_test_oa);
+
+ dev_priv->perf.oa.test_config.b_counter_regs = b_counter_config_test_oa;
+ dev_priv->perf.oa.test_config.b_counter_regs_len = ARRAY_SIZE(b_counter_config_test_oa);
+
+ dev_priv->perf.oa.test_config.flex_regs = flex_eu_config_test_oa;
+ dev_priv->perf.oa.test_config.flex_regs_len = ARRAY_SIZE(flex_eu_config_test_oa);
+
+ dev_priv->perf.oa.test_config.sysfs_metric.name = "a291665e-244b-4b76-9b9a-01de9d3c8068";
+ dev_priv->perf.oa.test_config.sysfs_metric.attrs = dev_priv->perf.oa.test_config.attrs;
+
+ dev_priv->perf.oa.test_config.attrs[0] = &dev_priv->perf.oa.test_config.sysfs_metric_id.attr;
+
+ dev_priv->perf.oa.test_config.sysfs_metric_id.attr.name = "id";
+ dev_priv->perf.oa.test_config.sysfs_metric_id.attr.mode = 0444;
+ dev_priv->perf.oa.test_config.sysfs_metric_id.show = show_test_oa_id;
+}
diff --git a/drivers/gpu/drm/i915/i915_oa_icl.h b/drivers/gpu/drm/i915/i915_oa_icl.h
new file mode 100644
index 000000000000..ae1c24aafe4f
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_oa_icl.h
@@ -0,0 +1,34 @@
+/*
+ * Autogenerated file by GPU Top : https://github.com/rib/gputop
+ * DO NOT EDIT manually!
+ *
+ *
+ * Copyright (c) 2015 Intel Corporation
+ *
+ * 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
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * 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 NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS 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.
+ *
+ */
+
+#ifndef __I915_OA_ICL_H__
+#define __I915_OA_ICL_H__
+
+extern void i915_perf_load_test_config_icl(struct drm_i915_private *dev_priv);
+
+#endif
diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h
index 430f5f9d0ff4..c96360398072 100644
--- a/drivers/gpu/drm/i915/i915_params.h
+++ b/drivers/gpu/drm/i915/i915_params.h
@@ -48,7 +48,7 @@ struct drm_printer;
param(int, enable_ips, 1) \
param(int, invert_brightness, 0) \
param(int, enable_guc, 0) \
- param(int, guc_log_level, 0) \
+ param(int, guc_log_level, -1) \
param(char *, guc_firmware_path, NULL) \
param(char *, huc_firmware_path, NULL) \
param(int, mmio_debug, 0) \
diff --git a/drivers/gpu/drm/i915/i915_pci.c b/drivers/gpu/drm/i915/i915_pci.c
index 062e91b39085..4364922e935d 100644
--- a/drivers/gpu/drm/i915/i915_pci.c
+++ b/drivers/gpu/drm/i915/i915_pci.c
@@ -602,6 +602,7 @@ static const struct intel_device_info intel_icelake_11_info = {
PLATFORM(INTEL_ICELAKE),
.is_alpha_support = 1,
.has_resource_streamer = 0,
+ .ring_mask = RENDER_RING | BLT_RING | VEBOX_RING | BSD_RING | BSD3_RING,
};
#undef GEN
diff --git a/drivers/gpu/drm/i915/i915_perf.c b/drivers/gpu/drm/i915/i915_perf.c
index abaca6edeb71..bfc906cd4e5e 100644
--- a/drivers/gpu/drm/i915/i915_perf.c
+++ b/drivers/gpu/drm/i915/i915_perf.c
@@ -209,6 +209,7 @@
#include "i915_oa_cflgt2.h"
#include "i915_oa_cflgt3.h"
#include "i915_oa_cnl.h"
+#include "i915_oa_icl.h"
/* HW requires this to be a power of two, between 128k and 16M, though driver
* is currently generally designed assuming the largest 16M size is used such
@@ -1042,7 +1043,7 @@ static int gen7_append_oa_reports(struct i915_perf_stream *stream,
I915_WRITE(GEN7_OASTATUS2,
((head & GEN7_OASTATUS2_HEAD_MASK) |
- OA_MEM_SELECT_GGTT));
+ GEN7_OASTATUS2_MEM_SELECT_GGTT));
dev_priv->perf.oa.oa_buffer.head = head;
spin_unlock_irqrestore(&dev_priv->perf.oa.oa_buffer.ptr_lock, flags);
@@ -1332,7 +1333,8 @@ static void gen7_init_oa_buffer(struct drm_i915_private *dev_priv)
/* Pre-DevBDW: OABUFFER must be set with counters off,
* before OASTATUS1, but after OASTATUS2
*/
- I915_WRITE(GEN7_OASTATUS2, gtt_offset | OA_MEM_SELECT_GGTT); /* head */
+ I915_WRITE(GEN7_OASTATUS2,
+ gtt_offset | GEN7_OASTATUS2_MEM_SELECT_GGTT); /* head */
dev_priv->perf.oa.oa_buffer.head = gtt_offset;
I915_WRITE(GEN7_OABUFFER, gtt_offset);
@@ -1392,7 +1394,7 @@ static void gen8_init_oa_buffer(struct drm_i915_private *dev_priv)
* bit."
*/
I915_WRITE(GEN8_OABUFFER, gtt_offset |
- OABUFFER_SIZE_16M | OA_MEM_SELECT_GGTT);
+ OABUFFER_SIZE_16M | GEN8_OABUFFER_MEM_SELECT_GGTT);
I915_WRITE(GEN8_OATAILPTR, gtt_offset & GEN8_OATAILPTR_MASK);
/* Mark that we need updated tail pointers to read from... */
@@ -1840,7 +1842,7 @@ static int gen8_enable_metric_set(struct drm_i915_private *dev_priv,
* be read back from automatically triggered reports, as part of the
* RPT_ID field.
*/
- if (IS_GEN9(dev_priv) || IS_GEN10(dev_priv)) {
+ if (IS_GEN(dev_priv, 9, 11)) {
I915_WRITE(GEN8_OA_DEBUG,
_MASKED_BIT_ENABLE(GEN9_OA_DEBUG_DISABLE_CLK_RATIO_REPORTS |
GEN9_OA_DEBUG_INCLUDE_CLK_RATIO));
@@ -1870,7 +1872,6 @@ static void gen8_disable_metric_set(struct drm_i915_private *dev_priv)
I915_WRITE(GDT_CHICKEN_BITS, (I915_READ(GDT_CHICKEN_BITS) &
~GT_NOA_ENABLE));
-
}
static void gen10_disable_metric_set(struct drm_i915_private *dev_priv)
@@ -1885,6 +1886,13 @@ static void gen10_disable_metric_set(struct drm_i915_private *dev_priv)
static void gen7_oa_enable(struct drm_i915_private *dev_priv)
{
+ struct i915_gem_context *ctx =
+ dev_priv->perf.oa.exclusive_stream->ctx;
+ u32 ctx_id = dev_priv->perf.oa.specific_ctx_id;
+ bool periodic = dev_priv->perf.oa.periodic;
+ u32 period_exponent = dev_priv->perf.oa.period_exponent;
+ u32 report_format = dev_priv->perf.oa.oa_buffer.format;
+
/*
* Reset buf pointers so we don't forward reports from before now.
*
@@ -1896,25 +1904,14 @@ static void gen7_oa_enable(struct drm_i915_private *dev_priv)
*/
gen7_init_oa_buffer(dev_priv);
- if (dev_priv->perf.oa.exclusive_stream->enabled) {
- struct i915_gem_context *ctx =
- dev_priv->perf.oa.exclusive_stream->ctx;
- u32 ctx_id = dev_priv->perf.oa.specific_ctx_id;
-
- bool periodic = dev_priv->perf.oa.periodic;
- u32 period_exponent = dev_priv->perf.oa.period_exponent;
- u32 report_format = dev_priv->perf.oa.oa_buffer.format;
-
- I915_WRITE(GEN7_OACONTROL,
- (ctx_id & GEN7_OACONTROL_CTX_MASK) |
- (period_exponent <<
- GEN7_OACONTROL_TIMER_PERIOD_SHIFT) |
- (periodic ? GEN7_OACONTROL_TIMER_ENABLE : 0) |
- (report_format << GEN7_OACONTROL_FORMAT_SHIFT) |
- (ctx ? GEN7_OACONTROL_PER_CTX_ENABLE : 0) |
- GEN7_OACONTROL_ENABLE);
- } else
- I915_WRITE(GEN7_OACONTROL, 0);
+ I915_WRITE(GEN7_OACONTROL,
+ (ctx_id & GEN7_OACONTROL_CTX_MASK) |
+ (period_exponent <<
+ GEN7_OACONTROL_TIMER_PERIOD_SHIFT) |
+ (periodic ? GEN7_OACONTROL_TIMER_ENABLE : 0) |
+ (report_format << GEN7_OACONTROL_FORMAT_SHIFT) |
+ (ctx ? GEN7_OACONTROL_PER_CTX_ENABLE : 0) |
+ GEN7_OACONTROL_ENABLE);
}
static void gen8_oa_enable(struct drm_i915_private *dev_priv)
@@ -2099,13 +2096,17 @@ static int i915_oa_stream_init(struct i915_perf_stream *stream,
if (stream->ctx) {
ret = oa_get_render_ctx_id(stream);
- if (ret)
+ if (ret) {
+ DRM_DEBUG("Invalid context id to filter with\n");
return ret;
+ }
}
ret = get_oa_config(dev_priv, props->metrics_set, &stream->oa_config);
- if (ret)
+ if (ret) {
+ DRM_DEBUG("Invalid OA config id=%i\n", props->metrics_set);
goto err_config;
+ }
/* PRM - observability performance counters:
*
@@ -2132,8 +2133,10 @@ static int i915_oa_stream_init(struct i915_perf_stream *stream,
ret = dev_priv->perf.oa.ops.enable_metric_set(dev_priv,
stream->oa_config);
- if (ret)
+ if (ret) {
+ DRM_DEBUG("Unable to enable metric set\n");
goto err_enable;
+ }
stream->ops = &i915_oa_stream_ops;
@@ -2745,7 +2748,8 @@ static int read_properties_unlocked(struct drm_i915_private *dev_priv,
props->ctx_handle = value;
break;
case DRM_I915_PERF_PROP_SAMPLE_OA:
- props->sample_flags |= SAMPLE_OA_REPORT;
+ if (value)
+ props->sample_flags |= SAMPLE_OA_REPORT;
break;
case DRM_I915_PERF_PROP_OA_METRICS_SET:
if (value == 0) {
@@ -2935,6 +2939,8 @@ void i915_perf_register(struct drm_i915_private *dev_priv)
i915_perf_load_test_config_cflgt3(dev_priv);
} else if (IS_CANNONLAKE(dev_priv)) {
i915_perf_load_test_config_cnl(dev_priv);
+ } else if (IS_ICELAKE(dev_priv)) {
+ i915_perf_load_test_config_icl(dev_priv);
}
if (dev_priv->perf.oa.test_config.id == 0)
@@ -3292,6 +3298,8 @@ int i915_perf_add_config_ioctl(struct drm_device *dev, void *data,
mutex_unlock(&dev_priv->perf.metrics_lock);
+ DRM_DEBUG("Added config %s id=%i\n", oa_config->uuid, oa_config->id);
+
return oa_config->id;
sysfs_err:
@@ -3348,6 +3356,9 @@ int i915_perf_remove_config_ioctl(struct drm_device *dev, void *data,
&oa_config->sysfs_metric);
idr_remove(&dev_priv->perf.metrics_idr, *arg);
+
+ DRM_DEBUG("Removed config %s id=%i\n", oa_config->uuid, oa_config->id);
+
put_oa_config(dev_priv, oa_config);
config_err:
@@ -3467,7 +3478,7 @@ void i915_perf_init(struct drm_i915_private *dev_priv)
dev_priv->perf.oa.gen8_valid_ctx_bit = (1<<16);
}
- } else if (IS_GEN10(dev_priv)) {
+ } else if (IS_GEN(dev_priv, 10, 11)) {
dev_priv->perf.oa.ops.is_valid_b_counter_reg =
gen7_is_valid_b_counter_addr;
dev_priv->perf.oa.ops.is_valid_mux_reg =
diff --git a/drivers/gpu/drm/i915/i915_pmu.c b/drivers/gpu/drm/i915/i915_pmu.c
index d8feb9053e0c..dc87797db500 100644
--- a/drivers/gpu/drm/i915/i915_pmu.c
+++ b/drivers/gpu/drm/i915/i915_pmu.c
@@ -1,33 +1,12 @@
/*
- * Copyright © 2017 Intel Corporation
- *
- * 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
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * 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 NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS 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.
+ * SPDX-License-Identifier: MIT
*
+ * Copyright © 2017-2018 Intel Corporation
*/
-#include <linux/perf_event.h>
-#include <linux/pm_runtime.h>
-
-#include "i915_drv.h"
#include "i915_pmu.h"
#include "intel_ringbuffer.h"
+#include "i915_drv.h"
/* Frequency for the sampling timer for events which need it. */
#define FREQUENCY 200
@@ -473,20 +452,37 @@ static u64 get_rc6(struct drm_i915_private *i915)
spin_lock_irqsave(&i915->pmu.lock, flags);
spin_lock(&kdev->power.lock);
- if (!i915->pmu.sample[__I915_SAMPLE_RC6_ESTIMATED].cur)
- i915->pmu.suspended_jiffies_last =
- kdev->power.suspended_jiffies;
+ /*
+ * After the above branch intel_runtime_pm_get_if_in_use failed
+ * to get the runtime PM reference we cannot assume we are in
+ * runtime suspend since we can either: a) race with coming out
+ * of it before we took the power.lock, or b) there are other
+ * states than suspended which can bring us here.
+ *
+ * We need to double-check that we are indeed currently runtime
+ * suspended and if not we cannot do better than report the last
+ * known RC6 value.
+ */
+ if (kdev->power.runtime_status == RPM_SUSPENDED) {
+ if (!i915->pmu.sample[__I915_SAMPLE_RC6_ESTIMATED].cur)
+ i915->pmu.suspended_jiffies_last =
+ kdev->power.suspended_jiffies;
- val = kdev->power.suspended_jiffies -
- i915->pmu.suspended_jiffies_last;
- val += jiffies - kdev->power.accounting_timestamp;
+ val = kdev->power.suspended_jiffies -
+ i915->pmu.suspended_jiffies_last;
+ val += jiffies - kdev->power.accounting_timestamp;
- spin_unlock(&kdev->power.lock);
+ val = jiffies_to_nsecs(val);
+ val += i915->pmu.sample[__I915_SAMPLE_RC6].cur;
- val = jiffies_to_nsecs(val);
- val += i915->pmu.sample[__I915_SAMPLE_RC6].cur;
- i915->pmu.sample[__I915_SAMPLE_RC6_ESTIMATED].cur = val;
+ i915->pmu.sample[__I915_SAMPLE_RC6_ESTIMATED].cur = val;
+ } else if (i915->pmu.sample[__I915_SAMPLE_RC6_ESTIMATED].cur) {
+ val = i915->pmu.sample[__I915_SAMPLE_RC6_ESTIMATED].cur;
+ } else {
+ val = i915->pmu.sample[__I915_SAMPLE_RC6].cur;
+ }
+ spin_unlock(&kdev->power.lock);
spin_unlock_irqrestore(&i915->pmu.lock, flags);
}
diff --git a/drivers/gpu/drm/i915/i915_pmu.h b/drivers/gpu/drm/i915/i915_pmu.h
index aa1b1a987ea1..2ba735299f7c 100644
--- a/drivers/gpu/drm/i915/i915_pmu.h
+++ b/drivers/gpu/drm/i915/i915_pmu.h
@@ -1,29 +1,19 @@
/*
- * Copyright © 2017 Intel Corporation
- *
- * 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
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * 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 NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS 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.
+ * SPDX-License-Identifier: MIT
*
+ * Copyright © 2017-2018 Intel Corporation
*/
+
#ifndef __I915_PMU_H__
#define __I915_PMU_H__
+#include <linux/hrtimer.h>
+#include <linux/perf_event.h>
+#include <linux/spinlock_types.h>
+#include <drm/i915_drm.h>
+
+struct drm_i915_private;
+
enum {
__I915_SAMPLE_FREQ_ACT = 0,
__I915_SAMPLE_FREQ_REQ,
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index e6a8c0ee7df1..fb106026a1f4 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -153,9 +153,6 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg)
#define _MMIO_PORT3(pipe, a, b, c) _MMIO(_PICK(pipe, a, b, c))
#define _PLL(pll, a, b) ((a) + (pll)*((b)-(a)))
#define _MMIO_PLL(pll, a, b) _MMIO(_PLL(pll, a, b))
-#define _MMIO_PORT6(port, a, b, c, d, e, f) _MMIO(_PICK(port, a, b, c, d, e, f))
-#define _MMIO_PORT6_LN(port, ln, a0, a1, b, c, d, e, f) \
- _MMIO(_PICK(port, a0, b, c, d, e, f) + (ln * (a1 - a0)))
#define _PHY3(phy, ...) _PICK(phy, __VA_ARGS__)
#define _MMIO_PHY3(phy, a, b, c) _MMIO(_PHY3(phy, a, b, c))
@@ -191,6 +188,7 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg)
#define OTHER_CLASS 4
#define MAX_ENGINE_CLASS 4
+#define OTHER_GTPM_INSTANCE 1
#define MAX_ENGINE_INSTANCE 3
/* PCI config space */
@@ -304,6 +302,17 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg)
#define GEN6_GRDOM_VECS (1 << 4)
#define GEN9_GRDOM_GUC (1 << 5)
#define GEN8_GRDOM_MEDIA2 (1 << 7)
+/* GEN11 changed all bit defs except for FULL & RENDER */
+#define GEN11_GRDOM_FULL GEN6_GRDOM_FULL
+#define GEN11_GRDOM_RENDER GEN6_GRDOM_RENDER
+#define GEN11_GRDOM_BLT (1 << 2)
+#define GEN11_GRDOM_GUC (1 << 3)
+#define GEN11_GRDOM_MEDIA (1 << 5)
+#define GEN11_GRDOM_MEDIA2 (1 << 6)
+#define GEN11_GRDOM_MEDIA3 (1 << 7)
+#define GEN11_GRDOM_MEDIA4 (1 << 8)
+#define GEN11_GRDOM_VECS (1 << 13)
+#define GEN11_GRDOM_VECS2 (1 << 14)
#define RING_PP_DIR_BASE(engine) _MMIO((engine)->mmio_base+0x228)
#define RING_PP_DIR_BASE_READ(engine) _MMIO((engine)->mmio_base+0x518)
@@ -430,145 +439,6 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg)
#define VGA_CR_INDEX_CGA 0x3d4
#define VGA_CR_DATA_CGA 0x3d5
-/*
- * Instruction field definitions used by the command parser
- */
-#define INSTR_CLIENT_SHIFT 29
-#define INSTR_MI_CLIENT 0x0
-#define INSTR_BC_CLIENT 0x2
-#define INSTR_RC_CLIENT 0x3
-#define INSTR_SUBCLIENT_SHIFT 27
-#define INSTR_SUBCLIENT_MASK 0x18000000
-#define INSTR_MEDIA_SUBCLIENT 0x2
-#define INSTR_26_TO_24_MASK 0x7000000
-#define INSTR_26_TO_24_SHIFT 24
-
-/*
- * Memory interface instructions used by the kernel
- */
-#define MI_INSTR(opcode, flags) (((opcode) << 23) | (flags))
-/* Many MI commands use bit 22 of the header dword for GGTT vs PPGTT */
-#define MI_GLOBAL_GTT (1<<22)
-
-#define MI_NOOP MI_INSTR(0, 0)
-#define MI_USER_INTERRUPT MI_INSTR(0x02, 0)
-#define MI_WAIT_FOR_EVENT MI_INSTR(0x03, 0)
-#define MI_WAIT_FOR_OVERLAY_FLIP (1<<16)
-#define MI_WAIT_FOR_PLANE_B_FLIP (1<<6)
-#define MI_WAIT_FOR_PLANE_A_FLIP (1<<2)
-#define MI_WAIT_FOR_PLANE_A_SCANLINES (1<<1)
-#define MI_FLUSH MI_INSTR(0x04, 0)
-#define MI_READ_FLUSH (1 << 0)
-#define MI_EXE_FLUSH (1 << 1)
-#define MI_NO_WRITE_FLUSH (1 << 2)
-#define MI_SCENE_COUNT (1 << 3) /* just increment scene count */
-#define MI_END_SCENE (1 << 4) /* flush binner and incr scene count */
-#define MI_INVALIDATE_ISP (1 << 5) /* invalidate indirect state pointers */
-#define MI_REPORT_HEAD MI_INSTR(0x07, 0)
-#define MI_ARB_ON_OFF MI_INSTR(0x08, 0)
-#define MI_ARB_ENABLE (1<<0)
-#define MI_ARB_DISABLE (0<<0)
-#define MI_BATCH_BUFFER_END MI_INSTR(0x0a, 0)
-#define MI_SUSPEND_FLUSH MI_INSTR(0x0b, 0)
-#define MI_SUSPEND_FLUSH_EN (1<<0)
-#define MI_SET_APPID MI_INSTR(0x0e, 0)
-#define MI_OVERLAY_FLIP MI_INSTR(0x11, 0)
-#define MI_OVERLAY_CONTINUE (0x0<<21)
-#define MI_OVERLAY_ON (0x1<<21)
-#define MI_OVERLAY_OFF (0x2<<21)
-#define MI_LOAD_SCAN_LINES_INCL MI_INSTR(0x12, 0)
-#define MI_DISPLAY_FLIP MI_INSTR(0x14, 2)
-#define MI_DISPLAY_FLIP_I915 MI_INSTR(0x14, 1)
-#define MI_DISPLAY_FLIP_PLANE(n) ((n) << 20)
-/* IVB has funny definitions for which plane to flip. */
-#define MI_DISPLAY_FLIP_IVB_PLANE_A (0 << 19)
-#define MI_DISPLAY_FLIP_IVB_PLANE_B (1 << 19)
-#define MI_DISPLAY_FLIP_IVB_SPRITE_A (2 << 19)
-#define MI_DISPLAY_FLIP_IVB_SPRITE_B (3 << 19)
-#define MI_DISPLAY_FLIP_IVB_PLANE_C (4 << 19)
-#define MI_DISPLAY_FLIP_IVB_SPRITE_C (5 << 19)
-/* SKL ones */
-#define MI_DISPLAY_FLIP_SKL_PLANE_1_A (0 << 8)
-#define MI_DISPLAY_FLIP_SKL_PLANE_1_B (1 << 8)
-#define MI_DISPLAY_FLIP_SKL_PLANE_1_C (2 << 8)
-#define MI_DISPLAY_FLIP_SKL_PLANE_2_A (4 << 8)
-#define MI_DISPLAY_FLIP_SKL_PLANE_2_B (5 << 8)
-#define MI_DISPLAY_FLIP_SKL_PLANE_2_C (6 << 8)
-#define MI_DISPLAY_FLIP_SKL_PLANE_3_A (7 << 8)
-#define MI_DISPLAY_FLIP_SKL_PLANE_3_B (8 << 8)
-#define MI_DISPLAY_FLIP_SKL_PLANE_3_C (9 << 8)
-#define MI_SEMAPHORE_MBOX MI_INSTR(0x16, 1) /* gen6, gen7 */
-#define MI_SEMAPHORE_GLOBAL_GTT (1<<22)
-#define MI_SEMAPHORE_UPDATE (1<<21)
-#define MI_SEMAPHORE_COMPARE (1<<20)
-#define MI_SEMAPHORE_REGISTER (1<<18)
-#define MI_SEMAPHORE_SYNC_VR (0<<16) /* RCS wait for VCS (RVSYNC) */
-#define MI_SEMAPHORE_SYNC_VER (1<<16) /* RCS wait for VECS (RVESYNC) */
-#define MI_SEMAPHORE_SYNC_BR (2<<16) /* RCS wait for BCS (RBSYNC) */
-#define MI_SEMAPHORE_SYNC_BV (0<<16) /* VCS wait for BCS (VBSYNC) */
-#define MI_SEMAPHORE_SYNC_VEV (1<<16) /* VCS wait for VECS (VVESYNC) */
-#define MI_SEMAPHORE_SYNC_RV (2<<16) /* VCS wait for RCS (VRSYNC) */
-#define MI_SEMAPHORE_SYNC_RB (0<<16) /* BCS wait for RCS (BRSYNC) */
-#define MI_SEMAPHORE_SYNC_VEB (1<<16) /* BCS wait for VECS (BVESYNC) */
-#define MI_SEMAPHORE_SYNC_VB (2<<16) /* BCS wait for VCS (BVSYNC) */
-#define MI_SEMAPHORE_SYNC_BVE (0<<16) /* VECS wait for BCS (VEBSYNC) */
-#define MI_SEMAPHORE_SYNC_VVE (1<<16) /* VECS wait for VCS (VEVSYNC) */
-#define MI_SEMAPHORE_SYNC_RVE (2<<16) /* VECS wait for RCS (VERSYNC) */
-#define MI_SEMAPHORE_SYNC_INVALID (3<<16)
-#define MI_SEMAPHORE_SYNC_MASK (3<<16)
-#define MI_SET_CONTEXT MI_INSTR(0x18, 0)
-#define MI_MM_SPACE_GTT (1<<8)
-#define MI_MM_SPACE_PHYSICAL (0<<8)
-#define MI_SAVE_EXT_STATE_EN (1<<3)
-#define MI_RESTORE_EXT_STATE_EN (1<<2)
-#define MI_FORCE_RESTORE (1<<1)
-#define MI_RESTORE_INHIBIT (1<<0)
-#define HSW_MI_RS_SAVE_STATE_EN (1<<3)
-#define HSW_MI_RS_RESTORE_STATE_EN (1<<2)
-#define MI_SEMAPHORE_SIGNAL MI_INSTR(0x1b, 0) /* GEN8+ */
-#define MI_SEMAPHORE_TARGET(engine) ((engine)<<15)
-#define MI_SEMAPHORE_WAIT MI_INSTR(0x1c, 2) /* GEN8+ */
-#define MI_SEMAPHORE_POLL (1<<15)
-#define MI_SEMAPHORE_SAD_GTE_SDD (1<<12)
-#define MI_STORE_DWORD_IMM MI_INSTR(0x20, 1)
-#define MI_STORE_DWORD_IMM_GEN4 MI_INSTR(0x20, 2)
-#define MI_MEM_VIRTUAL (1 << 22) /* 945,g33,965 */
-#define MI_USE_GGTT (1 << 22) /* g4x+ */
-#define MI_STORE_DWORD_INDEX MI_INSTR(0x21, 1)
-#define MI_STORE_DWORD_INDEX_SHIFT 2
-/* Official intel docs are somewhat sloppy concerning MI_LOAD_REGISTER_IMM:
- * - Always issue a MI_NOOP _before_ the MI_LOAD_REGISTER_IMM - otherwise hw
- * simply ignores the register load under certain conditions.
- * - One can actually load arbitrary many arbitrary registers: Simply issue x
- * address/value pairs. Don't overdue it, though, x <= 2^4 must hold!
- */
-#define MI_LOAD_REGISTER_IMM(x) MI_INSTR(0x22, 2*(x)-1)
-#define MI_LRI_FORCE_POSTED (1<<12)
-#define MI_STORE_REGISTER_MEM MI_INSTR(0x24, 1)
-#define MI_STORE_REGISTER_MEM_GEN8 MI_INSTR(0x24, 2)
-#define MI_SRM_LRM_GLOBAL_GTT (1<<22)
-#define MI_FLUSH_DW MI_INSTR(0x26, 1) /* for GEN6 */
-#define MI_FLUSH_DW_STORE_INDEX (1<<21)
-#define MI_INVALIDATE_TLB (1<<18)
-#define MI_FLUSH_DW_OP_STOREDW (1<<14)
-#define MI_FLUSH_DW_OP_MASK (3<<14)
-#define MI_FLUSH_DW_NOTIFY (1<<8)
-#define MI_INVALIDATE_BSD (1<<7)
-#define MI_FLUSH_DW_USE_GTT (1<<2)
-#define MI_FLUSH_DW_USE_PPGTT (0<<2)
-#define MI_LOAD_REGISTER_MEM MI_INSTR(0x29, 1)
-#define MI_LOAD_REGISTER_MEM_GEN8 MI_INSTR(0x29, 2)
-#define MI_BATCH_BUFFER MI_INSTR(0x30, 1)
-#define MI_BATCH_NON_SECURE (1)
-/* for snb/ivb/vlv this also means "batch in ppgtt" when ppgtt is enabled. */
-#define MI_BATCH_NON_SECURE_I965 (1<<8)
-#define MI_BATCH_PPGTT_HSW (1<<8)
-#define MI_BATCH_NON_SECURE_HSW (1<<13)
-#define MI_BATCH_BUFFER_START MI_INSTR(0x31, 0)
-#define MI_BATCH_GTT (2<<6) /* aliased with (1<<7) on gen4 */
-#define MI_BATCH_BUFFER_START_GEN8 MI_INSTR(0x31, 1)
-#define MI_BATCH_RESOURCE_STREAMER (1<<10)
-
#define MI_PREDICATE_SRC0 _MMIO(0x2400)
#define MI_PREDICATE_SRC0_UDW _MMIO(0x2400 + 4)
#define MI_PREDICATE_SRC1 _MMIO(0x2408)
@@ -579,130 +449,6 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg)
#define LOWER_SLICE_DISABLED (0<<0)
/*
- * 3D instructions used by the kernel
- */
-#define GFX_INSTR(opcode, flags) ((0x3 << 29) | ((opcode) << 24) | (flags))
-
-#define GEN9_MEDIA_POOL_STATE ((0x3 << 29) | (0x2 << 27) | (0x5 << 16) | 4)
-#define GEN9_MEDIA_POOL_ENABLE (1 << 31)
-#define GFX_OP_RASTER_RULES ((0x3<<29)|(0x7<<24))
-#define GFX_OP_SCISSOR ((0x3<<29)|(0x1c<<24)|(0x10<<19))
-#define SC_UPDATE_SCISSOR (0x1<<1)
-#define SC_ENABLE_MASK (0x1<<0)
-#define SC_ENABLE (0x1<<0)
-#define GFX_OP_LOAD_INDIRECT ((0x3<<29)|(0x1d<<24)|(0x7<<16))
-#define GFX_OP_SCISSOR_INFO ((0x3<<29)|(0x1d<<24)|(0x81<<16)|(0x1))
-#define SCI_YMIN_MASK (0xffff<<16)
-#define SCI_XMIN_MASK (0xffff<<0)
-#define SCI_YMAX_MASK (0xffff<<16)
-#define SCI_XMAX_MASK (0xffff<<0)
-#define GFX_OP_SCISSOR_ENABLE ((0x3<<29)|(0x1c<<24)|(0x10<<19))
-#define GFX_OP_SCISSOR_RECT ((0x3<<29)|(0x1d<<24)|(0x81<<16)|1)
-#define GFX_OP_COLOR_FACTOR ((0x3<<29)|(0x1d<<24)|(0x1<<16)|0x0)
-#define GFX_OP_STIPPLE ((0x3<<29)|(0x1d<<24)|(0x83<<16))
-#define GFX_OP_MAP_INFO ((0x3<<29)|(0x1d<<24)|0x4)
-#define GFX_OP_DESTBUFFER_VARS ((0x3<<29)|(0x1d<<24)|(0x85<<16)|0x0)
-#define GFX_OP_DESTBUFFER_INFO ((0x3<<29)|(0x1d<<24)|(0x8e<<16)|1)
-#define GFX_OP_DRAWRECT_INFO ((0x3<<29)|(0x1d<<24)|(0x80<<16)|(0x3))
-#define GFX_OP_DRAWRECT_INFO_I965 ((0x7900<<16)|0x2)
-
-#define COLOR_BLT_CMD (2<<29 | 0x40<<22 | (5-2))
-#define SRC_COPY_BLT_CMD ((2<<29)|(0x43<<22)|4)
-#define XY_SRC_COPY_BLT_CMD ((2<<29)|(0x53<<22)|6)
-#define XY_MONO_SRC_COPY_IMM_BLT ((2<<29)|(0x71<<22)|5)
-#define BLT_WRITE_A (2<<20)
-#define BLT_WRITE_RGB (1<<20)
-#define BLT_WRITE_RGBA (BLT_WRITE_RGB | BLT_WRITE_A)
-#define BLT_DEPTH_8 (0<<24)
-#define BLT_DEPTH_16_565 (1<<24)
-#define BLT_DEPTH_16_1555 (2<<24)
-#define BLT_DEPTH_32 (3<<24)
-#define BLT_ROP_SRC_COPY (0xcc<<16)
-#define BLT_ROP_COLOR_COPY (0xf0<<16)
-#define XY_SRC_COPY_BLT_SRC_TILED (1<<15) /* 965+ only */
-#define XY_SRC_COPY_BLT_DST_TILED (1<<11) /* 965+ only */
-#define CMD_OP_DISPLAYBUFFER_INFO ((0x0<<29)|(0x14<<23)|2)
-#define ASYNC_FLIP (1<<22)
-#define DISPLAY_PLANE_A (0<<20)
-#define DISPLAY_PLANE_B (1<<20)
-#define GFX_OP_PIPE_CONTROL(len) ((0x3<<29)|(0x3<<27)|(0x2<<24)|((len)-2))
-#define PIPE_CONTROL_FLUSH_L3 (1<<27)
-#define PIPE_CONTROL_GLOBAL_GTT_IVB (1<<24) /* gen7+ */
-#define PIPE_CONTROL_MMIO_WRITE (1<<23)
-#define PIPE_CONTROL_STORE_DATA_INDEX (1<<21)
-#define PIPE_CONTROL_CS_STALL (1<<20)
-#define PIPE_CONTROL_TLB_INVALIDATE (1<<18)
-#define PIPE_CONTROL_MEDIA_STATE_CLEAR (1<<16)
-#define PIPE_CONTROL_QW_WRITE (1<<14)
-#define PIPE_CONTROL_POST_SYNC_OP_MASK (3<<14)
-#define PIPE_CONTROL_DEPTH_STALL (1<<13)
-#define PIPE_CONTROL_WRITE_FLUSH (1<<12)
-#define PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH (1<<12) /* gen6+ */
-#define PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE (1<<11) /* MBZ on Ironlake */
-#define PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE (1<<10) /* GM45+ only */
-#define PIPE_CONTROL_INDIRECT_STATE_DISABLE (1<<9)
-#define PIPE_CONTROL_NOTIFY (1<<8)
-#define PIPE_CONTROL_FLUSH_ENABLE (1<<7) /* gen7+ */
-#define PIPE_CONTROL_DC_FLUSH_ENABLE (1<<5)
-#define PIPE_CONTROL_VF_CACHE_INVALIDATE (1<<4)
-#define PIPE_CONTROL_CONST_CACHE_INVALIDATE (1<<3)
-#define PIPE_CONTROL_STATE_CACHE_INVALIDATE (1<<2)
-#define PIPE_CONTROL_STALL_AT_SCOREBOARD (1<<1)
-#define PIPE_CONTROL_DEPTH_CACHE_FLUSH (1<<0)
-#define PIPE_CONTROL_GLOBAL_GTT (1<<2) /* in addr dword */
-
-/*
- * Commands used only by the command parser
- */
-#define MI_SET_PREDICATE MI_INSTR(0x01, 0)
-#define MI_ARB_CHECK MI_INSTR(0x05, 0)
-#define MI_RS_CONTROL MI_INSTR(0x06, 0)
-#define MI_URB_ATOMIC_ALLOC MI_INSTR(0x09, 0)
-#define MI_PREDICATE MI_INSTR(0x0C, 0)
-#define MI_RS_CONTEXT MI_INSTR(0x0F, 0)
-#define MI_TOPOLOGY_FILTER MI_INSTR(0x0D, 0)
-#define MI_LOAD_SCAN_LINES_EXCL MI_INSTR(0x13, 0)
-#define MI_URB_CLEAR MI_INSTR(0x19, 0)
-#define MI_UPDATE_GTT MI_INSTR(0x23, 0)
-#define MI_CLFLUSH MI_INSTR(0x27, 0)
-#define MI_REPORT_PERF_COUNT MI_INSTR(0x28, 0)
-#define MI_REPORT_PERF_COUNT_GGTT (1<<0)
-#define MI_LOAD_REGISTER_REG MI_INSTR(0x2A, 0)
-#define MI_RS_STORE_DATA_IMM MI_INSTR(0x2B, 0)
-#define MI_LOAD_URB_MEM MI_INSTR(0x2C, 0)
-#define MI_STORE_URB_MEM MI_INSTR(0x2D, 0)
-#define MI_CONDITIONAL_BATCH_BUFFER_END MI_INSTR(0x36, 0)
-
-#define PIPELINE_SELECT ((0x3<<29)|(0x1<<27)|(0x1<<24)|(0x4<<16))
-#define GFX_OP_3DSTATE_VF_STATISTICS ((0x3<<29)|(0x1<<27)|(0x0<<24)|(0xB<<16))
-#define MEDIA_VFE_STATE ((0x3<<29)|(0x2<<27)|(0x0<<24)|(0x0<<16))
-#define MEDIA_VFE_STATE_MMIO_ACCESS_MASK (0x18)
-#define GPGPU_OBJECT ((0x3<<29)|(0x2<<27)|(0x1<<24)|(0x4<<16))
-#define GPGPU_WALKER ((0x3<<29)|(0x2<<27)|(0x1<<24)|(0x5<<16))
-#define GFX_OP_3DSTATE_DX9_CONSTANTF_VS \
- ((0x3<<29)|(0x3<<27)|(0x0<<24)|(0x39<<16))
-#define GFX_OP_3DSTATE_DX9_CONSTANTF_PS \
- ((0x3<<29)|(0x3<<27)|(0x0<<24)|(0x3A<<16))
-#define GFX_OP_3DSTATE_SO_DECL_LIST \
- ((0x3<<29)|(0x3<<27)|(0x1<<24)|(0x17<<16))
-
-#define GFX_OP_3DSTATE_BINDING_TABLE_EDIT_VS \
- ((0x3<<29)|(0x3<<27)|(0x0<<24)|(0x43<<16))
-#define GFX_OP_3DSTATE_BINDING_TABLE_EDIT_GS \
- ((0x3<<29)|(0x3<<27)|(0x0<<24)|(0x44<<16))
-#define GFX_OP_3DSTATE_BINDING_TABLE_EDIT_HS \
- ((0x3<<29)|(0x3<<27)|(0x0<<24)|(0x45<<16))
-#define GFX_OP_3DSTATE_BINDING_TABLE_EDIT_DS \
- ((0x3<<29)|(0x3<<27)|(0x0<<24)|(0x46<<16))
-#define GFX_OP_3DSTATE_BINDING_TABLE_EDIT_PS \
- ((0x3<<29)|(0x3<<27)|(0x0<<24)|(0x47<<16))
-
-#define MFX_WAIT ((0x3<<29)|(0x1<<27)|(0x0<<16))
-
-#define COLOR_BLT ((0x2<<29)|(0x40<<22))
-#define SRC_COPY_BLT ((0x2<<29)|(0x43<<22))
-
-/*
* Registers used only by the command parser
*/
#define BCS_SWCTRL _MMIO(0x22200)
@@ -802,6 +548,7 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg)
#define GEN8_OABUFFER_UDW _MMIO(0x23b4)
#define GEN8_OABUFFER _MMIO(0x2b14)
+#define GEN8_OABUFFER_MEM_SELECT_GGTT (1 << 0) /* 0: PPGTT, 1: GGTT */
#define GEN7_OASTATUS1 _MMIO(0x2364)
#define GEN7_OASTATUS1_TAIL_MASK 0xffffffc0
@@ -810,7 +557,8 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg)
#define GEN7_OASTATUS1_REPORT_LOST (1<<0)
#define GEN7_OASTATUS2 _MMIO(0x2368)
-#define GEN7_OASTATUS2_HEAD_MASK 0xffffffc0
+#define GEN7_OASTATUS2_HEAD_MASK 0xffffffc0
+#define GEN7_OASTATUS2_MEM_SELECT_GGTT (1 << 0) /* 0: PPGTT, 1: GGTT */
#define GEN8_OASTATUS _MMIO(0x2b08)
#define GEN8_OASTATUS_OVERRUN_STATUS (1<<3)
@@ -832,8 +580,6 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg)
#define OABUFFER_SIZE_8M (6<<3)
#define OABUFFER_SIZE_16M (7<<3)
-#define OA_MEM_SELECT_GGTT (1<<0)
-
/*
* Flexible, Aggregate EU Counter Registers.
* Note: these aren't contiguous
@@ -1127,6 +873,12 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg)
#define GEN9_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_MASK (1 << GEN9_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_SHIFT)
#define GEN9_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_19_2_MHZ 0
#define GEN9_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_24_MHZ 1
+#define GEN11_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_SHIFT 3
+#define GEN11_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_MASK (0x7 << GEN11_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_SHIFT)
+#define GEN11_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_24_MHZ 0
+#define GEN11_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_19_2_MHZ 1
+#define GEN11_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_38_4_MHZ 2
+#define GEN11_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_25_MHZ 3
#define GEN10_RPM_CONFIG0_CTC_SHIFT_PARAMETER_SHIFT 1
#define GEN10_RPM_CONFIG0_CTC_SHIFT_PARAMETER_MASK (0x3 << GEN10_RPM_CONFIG0_CTC_SHIFT_PARAMETER_SHIFT)
@@ -1948,79 +1700,100 @@ enum i915_power_well_id {
#define _CNL_PORT_PCS_DW1_LN0_C 0x162C04
#define _CNL_PORT_PCS_DW1_LN0_D 0x162E04
#define _CNL_PORT_PCS_DW1_LN0_F 0x162804
-#define CNL_PORT_PCS_DW1_GRP(port) _MMIO_PORT6(port, \
+#define CNL_PORT_PCS_DW1_GRP(port) _MMIO(_PICK(port, \
_CNL_PORT_PCS_DW1_GRP_AE, \
_CNL_PORT_PCS_DW1_GRP_B, \
_CNL_PORT_PCS_DW1_GRP_C, \
_CNL_PORT_PCS_DW1_GRP_D, \
_CNL_PORT_PCS_DW1_GRP_AE, \
- _CNL_PORT_PCS_DW1_GRP_F)
-#define CNL_PORT_PCS_DW1_LN0(port) _MMIO_PORT6(port, \
+ _CNL_PORT_PCS_DW1_GRP_F))
+
+#define CNL_PORT_PCS_DW1_LN0(port) _MMIO(_PICK(port, \
_CNL_PORT_PCS_DW1_LN0_AE, \
_CNL_PORT_PCS_DW1_LN0_B, \
_CNL_PORT_PCS_DW1_LN0_C, \
_CNL_PORT_PCS_DW1_LN0_D, \
_CNL_PORT_PCS_DW1_LN0_AE, \
- _CNL_PORT_PCS_DW1_LN0_F)
+ _CNL_PORT_PCS_DW1_LN0_F))
+#define _ICL_PORT_PCS_DW1_GRP_A 0x162604
+#define _ICL_PORT_PCS_DW1_GRP_B 0x6C604
+#define _ICL_PORT_PCS_DW1_LN0_A 0x162804
+#define _ICL_PORT_PCS_DW1_LN0_B 0x6C804
+#define ICL_PORT_PCS_DW1_GRP(port) _MMIO_PORT(port,\
+ _ICL_PORT_PCS_DW1_GRP_A, \
+ _ICL_PORT_PCS_DW1_GRP_B)
+#define ICL_PORT_PCS_DW1_LN0(port) _MMIO_PORT(port, \
+ _ICL_PORT_PCS_DW1_LN0_A, \
+ _ICL_PORT_PCS_DW1_LN0_B)
#define COMMON_KEEPER_EN (1 << 26)
-#define _CNL_PORT_TX_DW2_GRP_AE 0x162348
-#define _CNL_PORT_TX_DW2_GRP_B 0x1623C8
-#define _CNL_PORT_TX_DW2_GRP_C 0x162B48
-#define _CNL_PORT_TX_DW2_GRP_D 0x162BC8
-#define _CNL_PORT_TX_DW2_GRP_F 0x162A48
-#define _CNL_PORT_TX_DW2_LN0_AE 0x162448
-#define _CNL_PORT_TX_DW2_LN0_B 0x162648
-#define _CNL_PORT_TX_DW2_LN0_C 0x162C48
-#define _CNL_PORT_TX_DW2_LN0_D 0x162E48
-#define _CNL_PORT_TX_DW2_LN0_F 0x162848
-#define CNL_PORT_TX_DW2_GRP(port) _MMIO_PORT6(port, \
- _CNL_PORT_TX_DW2_GRP_AE, \
- _CNL_PORT_TX_DW2_GRP_B, \
- _CNL_PORT_TX_DW2_GRP_C, \
- _CNL_PORT_TX_DW2_GRP_D, \
- _CNL_PORT_TX_DW2_GRP_AE, \
- _CNL_PORT_TX_DW2_GRP_F)
-#define CNL_PORT_TX_DW2_LN0(port) _MMIO_PORT6(port, \
- _CNL_PORT_TX_DW2_LN0_AE, \
- _CNL_PORT_TX_DW2_LN0_B, \
- _CNL_PORT_TX_DW2_LN0_C, \
- _CNL_PORT_TX_DW2_LN0_D, \
- _CNL_PORT_TX_DW2_LN0_AE, \
- _CNL_PORT_TX_DW2_LN0_F)
-#define SWING_SEL_UPPER(x) ((x >> 3) << 15)
+/* CNL Port TX registers */
+#define _CNL_PORT_TX_AE_GRP_OFFSET 0x162340
+#define _CNL_PORT_TX_B_GRP_OFFSET 0x1623C0
+#define _CNL_PORT_TX_C_GRP_OFFSET 0x162B40
+#define _CNL_PORT_TX_D_GRP_OFFSET 0x162BC0
+#define _CNL_PORT_TX_F_GRP_OFFSET 0x162A40
+#define _CNL_PORT_TX_AE_LN0_OFFSET 0x162440
+#define _CNL_PORT_TX_B_LN0_OFFSET 0x162640
+#define _CNL_PORT_TX_C_LN0_OFFSET 0x162C40
+#define _CNL_PORT_TX_D_LN0_OFFSET 0x162E40
+#define _CNL_PORT_TX_F_LN0_OFFSET 0x162840
+#define _CNL_PORT_TX_DW_GRP(port, dw) (_PICK((port), \
+ _CNL_PORT_TX_AE_GRP_OFFSET, \
+ _CNL_PORT_TX_B_GRP_OFFSET, \
+ _CNL_PORT_TX_B_GRP_OFFSET, \
+ _CNL_PORT_TX_D_GRP_OFFSET, \
+ _CNL_PORT_TX_AE_GRP_OFFSET, \
+ _CNL_PORT_TX_F_GRP_OFFSET) + \
+ 4*(dw))
+#define _CNL_PORT_TX_DW_LN0(port, dw) (_PICK((port), \
+ _CNL_PORT_TX_AE_LN0_OFFSET, \
+ _CNL_PORT_TX_B_LN0_OFFSET, \
+ _CNL_PORT_TX_B_LN0_OFFSET, \
+ _CNL_PORT_TX_D_LN0_OFFSET, \
+ _CNL_PORT_TX_AE_LN0_OFFSET, \
+ _CNL_PORT_TX_F_LN0_OFFSET) + \
+ 4*(dw))
+
+#define CNL_PORT_TX_DW2_GRP(port) _MMIO(_CNL_PORT_TX_DW_GRP((port), 2))
+#define CNL_PORT_TX_DW2_LN0(port) _MMIO(_CNL_PORT_TX_DW_LN0((port), 2))
+#define _ICL_PORT_TX_DW2_GRP_A 0x162688
+#define _ICL_PORT_TX_DW2_GRP_B 0x6C688
+#define _ICL_PORT_TX_DW2_LN0_A 0x162888
+#define _ICL_PORT_TX_DW2_LN0_B 0x6C888
+#define ICL_PORT_TX_DW2_GRP(port) _MMIO_PORT(port, \
+ _ICL_PORT_TX_DW2_GRP_A, \
+ _ICL_PORT_TX_DW2_GRP_B)
+#define ICL_PORT_TX_DW2_LN0(port) _MMIO_PORT(port, \
+ _ICL_PORT_TX_DW2_LN0_A, \
+ _ICL_PORT_TX_DW2_LN0_B)
+#define SWING_SEL_UPPER(x) (((x) >> 3) << 15)
#define SWING_SEL_UPPER_MASK (1 << 15)
-#define SWING_SEL_LOWER(x) ((x & 0x7) << 11)
+#define SWING_SEL_LOWER(x) (((x) & 0x7) << 11)
#define SWING_SEL_LOWER_MASK (0x7 << 11)
#define RCOMP_SCALAR(x) ((x) << 0)
#define RCOMP_SCALAR_MASK (0xFF << 0)
-#define _CNL_PORT_TX_DW4_GRP_AE 0x162350
-#define _CNL_PORT_TX_DW4_GRP_B 0x1623D0
-#define _CNL_PORT_TX_DW4_GRP_C 0x162B50
-#define _CNL_PORT_TX_DW4_GRP_D 0x162BD0
-#define _CNL_PORT_TX_DW4_GRP_F 0x162A50
#define _CNL_PORT_TX_DW4_LN0_AE 0x162450
#define _CNL_PORT_TX_DW4_LN1_AE 0x1624D0
-#define _CNL_PORT_TX_DW4_LN0_B 0x162650
-#define _CNL_PORT_TX_DW4_LN0_C 0x162C50
-#define _CNL_PORT_TX_DW4_LN0_D 0x162E50
-#define _CNL_PORT_TX_DW4_LN0_F 0x162850
-#define CNL_PORT_TX_DW4_GRP(port) _MMIO_PORT6(port, \
- _CNL_PORT_TX_DW4_GRP_AE, \
- _CNL_PORT_TX_DW4_GRP_B, \
- _CNL_PORT_TX_DW4_GRP_C, \
- _CNL_PORT_TX_DW4_GRP_D, \
- _CNL_PORT_TX_DW4_GRP_AE, \
- _CNL_PORT_TX_DW4_GRP_F)
-#define CNL_PORT_TX_DW4_LN(port, ln) _MMIO_PORT6_LN(port, ln, \
- _CNL_PORT_TX_DW4_LN0_AE, \
- _CNL_PORT_TX_DW4_LN1_AE, \
- _CNL_PORT_TX_DW4_LN0_B, \
- _CNL_PORT_TX_DW4_LN0_C, \
- _CNL_PORT_TX_DW4_LN0_D, \
- _CNL_PORT_TX_DW4_LN0_AE, \
- _CNL_PORT_TX_DW4_LN0_F)
+#define CNL_PORT_TX_DW4_GRP(port) _MMIO(_CNL_PORT_TX_DW_GRP((port), 4))
+#define CNL_PORT_TX_DW4_LN0(port) _MMIO(_CNL_PORT_TX_DW_LN0((port), 4))
+#define CNL_PORT_TX_DW4_LN(port, ln) _MMIO(_CNL_PORT_TX_DW_LN0((port), 4) + \
+ (ln * (_CNL_PORT_TX_DW4_LN1_AE - \
+ _CNL_PORT_TX_DW4_LN0_AE)))
+#define _ICL_PORT_TX_DW4_GRP_A 0x162690
+#define _ICL_PORT_TX_DW4_GRP_B 0x6C690
+#define _ICL_PORT_TX_DW4_LN0_A 0x162890
+#define _ICL_PORT_TX_DW4_LN1_A 0x162990
+#define _ICL_PORT_TX_DW4_LN0_B 0x6C890
+#define ICL_PORT_TX_DW4_GRP(port) _MMIO_PORT(port, \
+ _ICL_PORT_TX_DW4_GRP_A, \
+ _ICL_PORT_TX_DW4_GRP_B)
+#define ICL_PORT_TX_DW4_LN(port, ln) _MMIO(_PORT(port, \
+ _ICL_PORT_TX_DW4_LN0_A, \
+ _ICL_PORT_TX_DW4_LN0_B) + \
+ (ln * (_ICL_PORT_TX_DW4_LN1_A - \
+ _ICL_PORT_TX_DW4_LN0_A)))
#define LOADGEN_SELECT (1 << 31)
#define POST_CURSOR_1(x) ((x) << 12)
#define POST_CURSOR_1_MASK (0x3F << 12)
@@ -2029,64 +1802,147 @@ enum i915_power_well_id {
#define CURSOR_COEFF(x) ((x) << 0)
#define CURSOR_COEFF_MASK (0x3F << 0)
-#define _CNL_PORT_TX_DW5_GRP_AE 0x162354
-#define _CNL_PORT_TX_DW5_GRP_B 0x1623D4
-#define _CNL_PORT_TX_DW5_GRP_C 0x162B54
-#define _CNL_PORT_TX_DW5_GRP_D 0x162BD4
-#define _CNL_PORT_TX_DW5_GRP_F 0x162A54
-#define _CNL_PORT_TX_DW5_LN0_AE 0x162454
-#define _CNL_PORT_TX_DW5_LN0_B 0x162654
-#define _CNL_PORT_TX_DW5_LN0_C 0x162C54
-#define _CNL_PORT_TX_DW5_LN0_D 0x162E54
-#define _CNL_PORT_TX_DW5_LN0_F 0x162854
-#define CNL_PORT_TX_DW5_GRP(port) _MMIO_PORT6(port, \
- _CNL_PORT_TX_DW5_GRP_AE, \
- _CNL_PORT_TX_DW5_GRP_B, \
- _CNL_PORT_TX_DW5_GRP_C, \
- _CNL_PORT_TX_DW5_GRP_D, \
- _CNL_PORT_TX_DW5_GRP_AE, \
- _CNL_PORT_TX_DW5_GRP_F)
-#define CNL_PORT_TX_DW5_LN0(port) _MMIO_PORT6(port, \
- _CNL_PORT_TX_DW5_LN0_AE, \
- _CNL_PORT_TX_DW5_LN0_B, \
- _CNL_PORT_TX_DW5_LN0_C, \
- _CNL_PORT_TX_DW5_LN0_D, \
- _CNL_PORT_TX_DW5_LN0_AE, \
- _CNL_PORT_TX_DW5_LN0_F)
+#define CNL_PORT_TX_DW5_GRP(port) _MMIO(_CNL_PORT_TX_DW_GRP((port), 5))
+#define CNL_PORT_TX_DW5_LN0(port) _MMIO(_CNL_PORT_TX_DW_LN0((port), 5))
+#define _ICL_PORT_TX_DW5_GRP_A 0x162694
+#define _ICL_PORT_TX_DW5_GRP_B 0x6C694
+#define _ICL_PORT_TX_DW5_LN0_A 0x162894
+#define _ICL_PORT_TX_DW5_LN0_B 0x6C894
+#define ICL_PORT_TX_DW5_GRP(port) _MMIO_PORT(port, \
+ _ICL_PORT_TX_DW5_GRP_A, \
+ _ICL_PORT_TX_DW5_GRP_B)
+#define ICL_PORT_TX_DW5_LN0(port) _MMIO_PORT(port, \
+ _ICL_PORT_TX_DW5_LN0_A, \
+ _ICL_PORT_TX_DW5_LN0_B)
#define TX_TRAINING_EN (1 << 31)
+#define TAP2_DISABLE (1 << 30)
#define TAP3_DISABLE (1 << 29)
#define SCALING_MODE_SEL(x) ((x) << 18)
#define SCALING_MODE_SEL_MASK (0x7 << 18)
#define RTERM_SELECT(x) ((x) << 3)
#define RTERM_SELECT_MASK (0x7 << 3)
-#define _CNL_PORT_TX_DW7_GRP_AE 0x16235C
-#define _CNL_PORT_TX_DW7_GRP_B 0x1623DC
-#define _CNL_PORT_TX_DW7_GRP_C 0x162B5C
-#define _CNL_PORT_TX_DW7_GRP_D 0x162BDC
-#define _CNL_PORT_TX_DW7_GRP_F 0x162A5C
-#define _CNL_PORT_TX_DW7_LN0_AE 0x16245C
-#define _CNL_PORT_TX_DW7_LN0_B 0x16265C
-#define _CNL_PORT_TX_DW7_LN0_C 0x162C5C
-#define _CNL_PORT_TX_DW7_LN0_D 0x162E5C
-#define _CNL_PORT_TX_DW7_LN0_F 0x16285C
-#define CNL_PORT_TX_DW7_GRP(port) _MMIO_PORT6(port, \
- _CNL_PORT_TX_DW7_GRP_AE, \
- _CNL_PORT_TX_DW7_GRP_B, \
- _CNL_PORT_TX_DW7_GRP_C, \
- _CNL_PORT_TX_DW7_GRP_D, \
- _CNL_PORT_TX_DW7_GRP_AE, \
- _CNL_PORT_TX_DW7_GRP_F)
-#define CNL_PORT_TX_DW7_LN0(port) _MMIO_PORT6(port, \
- _CNL_PORT_TX_DW7_LN0_AE, \
- _CNL_PORT_TX_DW7_LN0_B, \
- _CNL_PORT_TX_DW7_LN0_C, \
- _CNL_PORT_TX_DW7_LN0_D, \
- _CNL_PORT_TX_DW7_LN0_AE, \
- _CNL_PORT_TX_DW7_LN0_F)
+#define CNL_PORT_TX_DW7_GRP(port) _MMIO(_CNL_PORT_TX_DW_GRP((port), 7))
+#define CNL_PORT_TX_DW7_LN0(port) _MMIO(_CNL_PORT_TX_DW_LN0((port), 7))
#define N_SCALAR(x) ((x) << 24)
#define N_SCALAR_MASK (0x7F << 24)
+#define _ICL_MG_PHY_PORT_LN(port, ln, ln0p1, ln0p2, ln1p1) \
+ _MMIO(_PORT((port) - PORT_C, ln0p1, ln0p2) + (ln) * ((ln1p1) - (ln0p1)))
+
+#define _ICL_MG_TX_LINK_PARAMS_TX1LN0_PORT1 0x16812C
+#define _ICL_MG_TX_LINK_PARAMS_TX1LN1_PORT1 0x16852C
+#define _ICL_MG_TX_LINK_PARAMS_TX1LN0_PORT2 0x16912C
+#define _ICL_MG_TX_LINK_PARAMS_TX1LN1_PORT2 0x16952C
+#define _ICL_MG_TX_LINK_PARAMS_TX1LN0_PORT3 0x16A12C
+#define _ICL_MG_TX_LINK_PARAMS_TX1LN1_PORT3 0x16A52C
+#define _ICL_MG_TX_LINK_PARAMS_TX1LN0_PORT4 0x16B12C
+#define _ICL_MG_TX_LINK_PARAMS_TX1LN1_PORT4 0x16B52C
+#define ICL_PORT_MG_TX1_LINK_PARAMS(port, ln) \
+ _ICL_MG_PHY_PORT_LN(port, ln, _ICL_MG_TX_LINK_PARAMS_TX1LN0_PORT1, \
+ _ICL_MG_TX_LINK_PARAMS_TX1LN0_PORT2, \
+ _ICL_MG_TX_LINK_PARAMS_TX1LN1_PORT1)
+
+#define _ICL_MG_TX_LINK_PARAMS_TX2LN0_PORT1 0x1680AC
+#define _ICL_MG_TX_LINK_PARAMS_TX2LN1_PORT1 0x1684AC
+#define _ICL_MG_TX_LINK_PARAMS_TX2LN0_PORT2 0x1690AC
+#define _ICL_MG_TX_LINK_PARAMS_TX2LN1_PORT2 0x1694AC
+#define _ICL_MG_TX_LINK_PARAMS_TX2LN0_PORT3 0x16A0AC
+#define _ICL_MG_TX_LINK_PARAMS_TX2LN1_PORT3 0x16A4AC
+#define _ICL_MG_TX_LINK_PARAMS_TX2LN0_PORT4 0x16B0AC
+#define _ICL_MG_TX_LINK_PARAMS_TX2LN1_PORT4 0x16B4AC
+#define ICL_PORT_MG_TX2_LINK_PARAMS(port, ln) \
+ _ICL_MG_PHY_PORT_LN(port, ln, _ICL_MG_TX_LINK_PARAMS_TX2LN0_PORT1, \
+ _ICL_MG_TX_LINK_PARAMS_TX2LN0_PORT2, \
+ _ICL_MG_TX_LINK_PARAMS_TX2LN1_PORT1)
+#define CRI_USE_FS32 (1 << 5)
+
+#define _ICL_MG_TX_PISO_READLOAD_TX1LN0_PORT1 0x16814C
+#define _ICL_MG_TX_PISO_READLOAD_TX1LN1_PORT1 0x16854C
+#define _ICL_MG_TX_PISO_READLOAD_TX1LN0_PORT2 0x16914C
+#define _ICL_MG_TX_PISO_READLOAD_TX1LN1_PORT2 0x16954C
+#define _ICL_MG_TX_PISO_READLOAD_TX1LN0_PORT3 0x16A14C
+#define _ICL_MG_TX_PISO_READLOAD_TX1LN1_PORT3 0x16A54C
+#define _ICL_MG_TX_PISO_READLOAD_TX1LN0_PORT4 0x16B14C
+#define _ICL_MG_TX_PISO_READLOAD_TX1LN1_PORT4 0x16B54C
+#define ICL_PORT_MG_TX1_PISO_READLOAD(port, ln) \
+ _ICL_MG_PHY_PORT_LN(port, ln, _ICL_MG_TX_PISO_READLOAD_TX1LN0_PORT1, \
+ _ICL_MG_TX_PISO_READLOAD_TX1LN0_PORT2, \
+ _ICL_MG_TX_PISO_READLOAD_TX1LN1_PORT1)
+
+#define _ICL_MG_TX_PISO_READLOAD_TX2LN0_PORT1 0x1680CC
+#define _ICL_MG_TX_PISO_READLOAD_TX2LN1_PORT1 0x1684CC
+#define _ICL_MG_TX_PISO_READLOAD_TX2LN0_PORT2 0x1690CC
+#define _ICL_MG_TX_PISO_READLOAD_TX2LN1_PORT2 0x1694CC
+#define _ICL_MG_TX_PISO_READLOAD_TX2LN0_PORT3 0x16A0CC
+#define _ICL_MG_TX_PISO_READLOAD_TX2LN1_PORT3 0x16A4CC
+#define _ICL_MG_TX_PISO_READLOAD_TX2LN0_PORT4 0x16B0CC
+#define _ICL_MG_TX_PISO_READLOAD_TX2LN1_PORT4 0x16B4CC
+#define ICL_PORT_MG_TX2_PISO_READLOAD(port, ln) \
+ _ICL_MG_PHY_PORT_LN(port, ln, _ICL_MG_TX_PISO_READLOAD_TX2LN0_PORT1, \
+ _ICL_MG_TX_PISO_READLOAD_TX2LN0_PORT2, \
+ _ICL_MG_TX_PISO_READLOAD_TX2LN1_PORT1)
+#define CRI_CALCINIT (1 << 1)
+
+#define _ICL_MG_TX_SWINGCTRL_TX1LN0_PORT1 0x168148
+#define _ICL_MG_TX_SWINGCTRL_TX1LN1_PORT1 0x168548
+#define _ICL_MG_TX_SWINGCTRL_TX1LN0_PORT2 0x169148
+#define _ICL_MG_TX_SWINGCTRL_TX1LN1_PORT2 0x169548
+#define _ICL_MG_TX_SWINGCTRL_TX1LN0_PORT3 0x16A148
+#define _ICL_MG_TX_SWINGCTRL_TX1LN1_PORT3 0x16A548
+#define _ICL_MG_TX_SWINGCTRL_TX1LN0_PORT4 0x16B148
+#define _ICL_MG_TX_SWINGCTRL_TX1LN1_PORT4 0x16B548
+#define ICL_PORT_MG_TX1_SWINGCTRL(port, ln) \
+ _ICL_MG_PHY_PORT_LN(port, ln, _ICL_MG_TX_SWINGCTRL_TX1LN0_PORT1, \
+ _ICL_MG_TX_SWINGCTRL_TX1LN0_PORT2, \
+ _ICL_MG_TX_SWINGCTRL_TX1LN1_PORT1)
+
+#define _ICL_MG_TX_SWINGCTRL_TX2LN0_PORT1 0x1680C8
+#define _ICL_MG_TX_SWINGCTRL_TX2LN1_PORT1 0x1684C8
+#define _ICL_MG_TX_SWINGCTRL_TX2LN0_PORT2 0x1690C8
+#define _ICL_MG_TX_SWINGCTRL_TX2LN1_PORT2 0x1694C8
+#define _ICL_MG_TX_SWINGCTRL_TX2LN0_PORT3 0x16A0C8
+#define _ICL_MG_TX_SWINGCTRL_TX2LN1_PORT3 0x16A4C8
+#define _ICL_MG_TX_SWINGCTRL_TX2LN0_PORT4 0x16B0C8
+#define _ICL_MG_TX_SWINGCTRL_TX2LN1_PORT4 0x16B4C8
+#define ICL_PORT_MG_TX2_SWINGCTRL(port, ln) \
+ _ICL_MG_PHY_PORT_LN(port, ln, _ICL_MG_TX_SWINGCTRL_TX2LN0_PORT1, \
+ _ICL_MG_TX_SWINGCTRL_TX2LN0_PORT2, \
+ _ICL_MG_TX_SWINGCTRL_TX2LN1_PORT1)
+#define CRI_TXDEEMPH_OVERRIDE_17_12(x) ((x) << 0)
+#define CRI_TXDEEMPH_OVERRIDE_17_12_MASK (0x3F << 0)
+
+#define _ICL_MG_TX_DRVCTRL_TX1LN0_PORT1 0x168144
+#define _ICL_MG_TX_DRVCTRL_TX1LN1_PORT1 0x168544
+#define _ICL_MG_TX_DRVCTRL_TX1LN0_PORT2 0x169144
+#define _ICL_MG_TX_DRVCTRL_TX1LN1_PORT2 0x169544
+#define _ICL_MG_TX_DRVCTRL_TX1LN0_PORT3 0x16A144
+#define _ICL_MG_TX_DRVCTRL_TX1LN1_PORT3 0x16A544
+#define _ICL_MG_TX_DRVCTRL_TX1LN0_PORT4 0x16B144
+#define _ICL_MG_TX_DRVCTRL_TX1LN1_PORT4 0x16B544
+#define ICL_PORT_MG_TX1_DRVCTRL(port, ln) \
+ _ICL_MG_PHY_PORT_LN(port, ln, _ICL_MG_TX_DRVCTRL_TX1LN0_PORT1, \
+ _ICL_MG_TX_DRVCTRL_TX1LN0_PORT2, \
+ _ICL_MG_TX_DRVCTRL_TX1LN1_PORT1)
+
+#define _ICL_MG_TX_DRVCTRL_TX2LN0_PORT1 0x1680C4
+#define _ICL_MG_TX_DRVCTRL_TX2LN1_PORT1 0x1684C4
+#define _ICL_MG_TX_DRVCTRL_TX2LN0_PORT2 0x1690C4
+#define _ICL_MG_TX_DRVCTRL_TX2LN1_PORT2 0x1694C4
+#define _ICL_MG_TX_DRVCTRL_TX2LN0_PORT3 0x16A0C4
+#define _ICL_MG_TX_DRVCTRL_TX2LN1_PORT3 0x16A4C4
+#define _ICL_MG_TX_DRVCTRL_TX2LN0_PORT4 0x16B0C4
+#define _ICL_MG_TX_DRVCTRL_TX2LN1_PORT4 0x16B4C4
+#define ICL_PORT_MG_TX2_DRVCTRL(port, ln) \
+ _ICL_MG_PHY_PORT_LN(port, ln, _ICL_MG_TX_DRVCTRL_TX2LN0_PORT1, \
+ _ICL_MG_TX_DRVCTRL_TX2LN0_PORT2, \
+ _ICL_MG_TX_DRVCTRL_TX2LN1_PORT1)
+#define CRI_TXDEEMPH_OVERRIDE_11_6(x) ((x) << 24)
+#define CRI_TXDEEMPH_OVERRIDE_11_6_MASK (0x3F << 24)
+#define CRI_TXDEEMPH_OVERRIDE_EN (1 << 22)
+#define CRI_TXDEEMPH_OVERRIDE_5_0(x) ((x) << 16)
+#define CRI_TXDEEMPH_OVERRIDE_5_0_MASK (0x3F << 16)
+
/* The spec defines this only for BXT PHY0, but lets assume that this
* would exist for PHY1 too if it had a second channel.
*/
@@ -2473,6 +2329,10 @@ enum i915_power_well_id {
#define GEN8_MCR_SLICE_MASK GEN8_MCR_SLICE(3)
#define GEN8_MCR_SUBSLICE(subslice) (((subslice) & 3) << 24)
#define GEN8_MCR_SUBSLICE_MASK GEN8_MCR_SUBSLICE(3)
+#define GEN11_MCR_SLICE(slice) (((slice) & 0xf) << 27)
+#define GEN11_MCR_SLICE_MASK GEN11_MCR_SLICE(0xf)
+#define GEN11_MCR_SUBSLICE(subslice) (((subslice) & 0x7) << 24)
+#define GEN11_MCR_SUBSLICE_MASK GEN11_MCR_SUBSLICE(0x7)
#define RING_IPEIR(base) _MMIO((base)+0x64)
#define RING_IPEHR(base) _MMIO((base)+0x68)
/*
@@ -2867,6 +2727,19 @@ enum i915_power_well_id {
#define GEN10_EU_DISABLE3 _MMIO(0x9140)
#define GEN10_EU_DIS_SS_MASK 0xff
+#define GEN11_GT_VEBOX_VDBOX_DISABLE _MMIO(0x9140)
+#define GEN11_GT_VDBOX_DISABLE_MASK 0xff
+#define GEN11_GT_VEBOX_DISABLE_SHIFT 16
+#define GEN11_GT_VEBOX_DISABLE_MASK (0xff << GEN11_GT_VEBOX_DISABLE_SHIFT)
+
+#define GEN11_EU_DISABLE _MMIO(0x9134)
+#define GEN11_EU_DIS_MASK 0xFF
+
+#define GEN11_GT_SLICE_ENABLE _MMIO(0x9138)
+#define GEN11_GT_S_ENA_MASK 0xFF
+
+#define GEN11_GT_SUBSLICE_DISABLE _MMIO(0x913C)
+
#define GEN6_BSD_SLEEP_PSMI_CONTROL _MMIO(0x12050)
#define GEN6_BSD_SLEEP_MSG_DISABLE (1 << 0)
#define GEN6_BSD_SLEEP_FLUSH_DISABLE (1 << 2)
@@ -3951,6 +3824,9 @@ enum {
#define _CLKGATE_DIS_PSL_A 0x46520
#define _CLKGATE_DIS_PSL_B 0x46524
#define _CLKGATE_DIS_PSL_C 0x46528
+#define DUPS1_GATING_DIS (1 << 15)
+#define DUPS2_GATING_DIS (1 << 19)
+#define DUPS3_GATING_DIS (1 << 23)
#define DPF_GATING_DIS (1 << 10)
#define DPF_RAM_GATING_DIS (1 << 9)
#define DPFR_GATING_DIS (1 << 8)
@@ -4151,6 +4027,12 @@ enum {
#define EDP_PSR_IDLE_FRAME_SHIFT 0
#define EDP_PSR_AUX_CTL _MMIO(dev_priv->psr_mmio_base + 0x10)
+#define EDP_PSR_AUX_CTL_TIME_OUT_MASK (3 << 26)
+#define EDP_PSR_AUX_CTL_MESSAGE_SIZE_MASK (0x1f << 20)
+#define EDP_PSR_AUX_CTL_PRECHARGE_2US_MASK (0xf << 16)
+#define EDP_PSR_AUX_CTL_ERROR_INTERRUPT (1 << 11)
+#define EDP_PSR_AUX_CTL_BIT_CLOCK_2X_MASK (0x7ff)
+
#define EDP_PSR_AUX_DATA(i) _MMIO(dev_priv->psr_mmio_base + 0x14 + (i) * 4) /* 5 registers */
#define EDP_PSR_STATUS _MMIO(dev_priv->psr_mmio_base + 0x40)
@@ -4180,17 +4062,19 @@ enum {
#define EDP_PSR_PERF_CNT _MMIO(dev_priv->psr_mmio_base + 0x44)
#define EDP_PSR_PERF_CNT_MASK 0xffffff
-#define EDP_PSR_DEBUG _MMIO(dev_priv->psr_mmio_base + 0x60)
+#define EDP_PSR_DEBUG _MMIO(dev_priv->psr_mmio_base + 0x60) /* PSR_MASK on SKL+ */
#define EDP_PSR_DEBUG_MASK_MAX_SLEEP (1<<28)
#define EDP_PSR_DEBUG_MASK_LPSP (1<<27)
#define EDP_PSR_DEBUG_MASK_MEMUP (1<<26)
#define EDP_PSR_DEBUG_MASK_HPD (1<<25)
#define EDP_PSR_DEBUG_MASK_DISP_REG_WRITE (1<<16)
-#define EDP_PSR_DEBUG_EXIT_ON_PIXEL_UNDERRUN (1<<15)
+#define EDP_PSR_DEBUG_EXIT_ON_PIXEL_UNDERRUN (1<<15) /* SKL+ */
#define EDP_PSR2_CTL _MMIO(0x6f900)
#define EDP_PSR2_ENABLE (1<<31)
#define EDP_SU_TRACK_ENABLE (1<<30)
+#define EDP_Y_COORDINATE_VALID (1<<26) /* GLK and CNL+ */
+#define EDP_Y_COORDINATE_ENABLE (1<<25) /* GLK and CNL+ */
#define EDP_MAX_SU_DISABLE_TIME(t) ((t)<<20)
#define EDP_MAX_SU_DISABLE_TIME_MASK (0x1f<<20)
#define EDP_PSR2_TP2_TIME_500 (0<<8)
@@ -4200,8 +4084,9 @@ enum {
#define EDP_PSR2_TP2_TIME_MASK (3<<8)
#define EDP_PSR2_FRAME_BEFORE_SU_SHIFT 4
#define EDP_PSR2_FRAME_BEFORE_SU_MASK (0xf<<4)
-#define EDP_PSR2_IDLE_MASK 0xf
#define EDP_PSR2_FRAME_BEFORE_SU(a) ((a)<<4)
+#define EDP_PSR2_IDLE_FRAME_MASK 0xf
+#define EDP_PSR2_IDLE_FRAME_SHIFT 0
#define EDP_PSR2_STATUS _MMIO(0x6f940)
#define EDP_PSR2_STATUS_STATE_MASK (0xf<<28)
@@ -5265,8 +5150,6 @@ enum {
#define DP_LINK_TRAIN_OFF (3 << 28)
#define DP_LINK_TRAIN_MASK (3 << 28)
#define DP_LINK_TRAIN_SHIFT 28
-#define DP_LINK_TRAIN_PAT_3_CHV (1 << 14)
-#define DP_LINK_TRAIN_MASK_CHV ((3 << 28)|(1<<14))
/* CPT Link training mode */
#define DP_LINK_TRAIN_PAT_1_CPT (0 << 8)
@@ -6009,6 +5892,7 @@ enum {
#define CURSIZE _MMIO(0x700a0) /* 845/865 */
#define _CUR_FBC_CTL_A 0x700a0 /* ivb+ */
#define CUR_FBC_CTL_EN (1 << 31)
+#define _CURASURFLIVE 0x700ac /* g4x+ */
#define _CURBCNTR 0x700c0
#define _CURBBASE 0x700c4
#define _CURBPOS 0x700c8
@@ -6025,6 +5909,7 @@ enum {
#define CURBASE(pipe) _CURSOR2(pipe, _CURABASE)
#define CURPOS(pipe) _CURSOR2(pipe, _CURAPOS)
#define CUR_FBC_CTL(pipe) _CURSOR2(pipe, _CUR_FBC_CTL_A)
+#define CURSURFLIVE(pipe) _CURSOR2(pipe, _CURASURFLIVE)
#define CURSOR_A_OFFSET 0x70080
#define CURSOR_B_OFFSET 0x700c0
@@ -6779,6 +6664,8 @@ enum {
#define PS_SCALER_MODE_MASK (3 << 28)
#define PS_SCALER_MODE_DYN (0 << 28)
#define PS_SCALER_MODE_HQ (1 << 28)
+#define SKL_PS_SCALER_MODE_NV12 (2 << 28)
+#define PS_SCALER_MODE_PLANAR (1 << 29)
#define PS_PLANE_SEL_MASK (7 << 25)
#define PS_PLANE_SEL(plane) (((plane) + 1) << 25)
#define PS_FILTER_MASK (3 << 23)
@@ -7117,7 +7004,9 @@ enum {
#define GEN11_INTR_IDENTITY_REG0 _MMIO(0x190060)
#define GEN11_INTR_IDENTITY_REG1 _MMIO(0x190064)
#define GEN11_INTR_DATA_VALID (1 << 31)
-#define GEN11_INTR_ENGINE_MASK (0xffff)
+#define GEN11_INTR_ENGINE_CLASS(x) (((x) & GENMASK(18, 16)) >> 16)
+#define GEN11_INTR_ENGINE_INSTANCE(x) (((x) & GENMASK(25, 20)) >> 20)
+#define GEN11_INTR_ENGINE_INTR(x) ((x) & 0xffff)
#define GEN11_INTR_IDENTITY_REG(x) _MMIO(0x190060 + (x * 4))
@@ -7197,6 +7086,7 @@ enum {
#define CHICKEN_TRANS_A 0x420c0
#define CHICKEN_TRANS_B 0x420c4
#define CHICKEN_TRANS(trans) _MMIO_TRANS(trans, CHICKEN_TRANS_A, CHICKEN_TRANS_B)
+#define VSC_DATA_SEL_SOFTWARE_CONTROL (1<<25) /* GLK and CNL+ */
#define DDI_TRAINING_OVERRIDE_ENABLE (1<<19)
#define DDI_TRAINING_OVERRIDE_VALUE (1<<18)
#define DDIE_TRAINING_OVERRIDE_ENABLE (1<<17) /* CHICKEN_TRANS_A only */
diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c
index 282f57630cc1..9ca9c24b4421 100644
--- a/drivers/gpu/drm/i915/i915_request.c
+++ b/drivers/gpu/drm/i915/i915_request.c
@@ -59,11 +59,7 @@ static bool i915_fence_signaled(struct dma_fence *fence)
static bool i915_fence_enable_signaling(struct dma_fence *fence)
{
- if (i915_fence_signaled(fence))
- return false;
-
- intel_engine_enable_signaling(to_request(fence), true);
- return !i915_fence_signaled(fence);
+ return intel_engine_enable_signaling(to_request(fence), true);
}
static signed long i915_fence_wait(struct dma_fence *fence,
@@ -211,11 +207,19 @@ static int reset_all_global_seqno(struct drm_i915_private *i915, u32 seqno)
if (ret)
return ret;
+ GEM_BUG_ON(i915->gt.active_requests);
+
/* If the seqno wraps around, we need to clear the breadcrumb rbtree */
for_each_engine(engine, i915, id) {
struct i915_gem_timeline *timeline;
struct intel_timeline *tl = engine->timeline;
+ GEM_TRACE("%s seqno %d (current %d) -> %d\n",
+ engine->name,
+ tl->seqno,
+ intel_engine_get_seqno(engine),
+ seqno);
+
if (!i915_seqno_passed(seqno, tl->seqno)) {
/* Flush any waiters before we reuse the seqno */
intel_engine_disarm_breadcrumbs(engine);
@@ -251,47 +255,6 @@ int i915_gem_set_global_seqno(struct drm_device *dev, u32 seqno)
return reset_all_global_seqno(i915, seqno - 1);
}
-static void mark_busy(struct drm_i915_private *i915)
-{
- if (i915->gt.awake)
- return;
-
- GEM_BUG_ON(!i915->gt.active_requests);
-
- intel_runtime_pm_get_noresume(i915);
-
- /*
- * It seems that the DMC likes to transition between the DC states a lot
- * when there are no connected displays (no active power domains) during
- * command submission.
- *
- * This activity has negative impact on the performance of the chip with
- * huge latencies observed in the interrupt handler and elsewhere.
- *
- * Work around it by grabbing a GT IRQ power domain whilst there is any
- * GT activity, preventing any DC state transitions.
- */
- intel_display_power_get(i915, POWER_DOMAIN_GT_IRQ);
-
- i915->gt.awake = true;
- if (unlikely(++i915->gt.epoch == 0)) /* keep 0 as invalid */
- i915->gt.epoch = 1;
-
- intel_enable_gt_powersave(i915);
- i915_update_gfx_val(i915);
- if (INTEL_GEN(i915) >= 6)
- gen6_rps_busy(i915);
- i915_pmu_gt_unparked(i915);
-
- intel_engines_unpark(i915);
-
- i915_queue_hangcheck(i915);
-
- queue_delayed_work(i915->wq,
- &i915->gt.retire_work,
- round_jiffies_up_relative(HZ));
-}
-
static int reserve_engine(struct intel_engine_cs *engine)
{
struct drm_i915_private *i915 = engine->i915;
@@ -309,7 +272,7 @@ static int reserve_engine(struct intel_engine_cs *engine)
}
if (!i915->gt.active_requests++)
- mark_busy(i915);
+ i915_gem_unpark(i915);
return 0;
}
@@ -318,13 +281,8 @@ static void unreserve_engine(struct intel_engine_cs *engine)
{
struct drm_i915_private *i915 = engine->i915;
- if (!--i915->gt.active_requests) {
- /* Cancel the mark_busy() from our reserve_engine() */
- GEM_BUG_ON(!i915->gt.awake);
- mod_delayed_work(i915->wq,
- &i915->gt.idle_work,
- msecs_to_jiffies(100));
- }
+ if (!--i915->gt.active_requests)
+ i915_gem_park(i915);
GEM_BUG_ON(!engine->timeline->inflight_seqnos);
engine->timeline->inflight_seqnos--;
@@ -358,7 +316,7 @@ static void advance_ring(struct i915_request *request)
* is just about to be. Either works, if we miss the last two
* noops - they are safe to be replayed on a reset.
*/
- tail = READ_ONCE(request->ring->tail);
+ tail = READ_ONCE(request->tail);
} else {
tail = request->postfix;
}
@@ -385,6 +343,12 @@ static void i915_request_retire(struct i915_request *request)
struct intel_engine_cs *engine = request->engine;
struct i915_gem_active *active, *next;
+ GEM_TRACE("%s fence %llx:%d, global=%d, current %d\n",
+ engine->name,
+ request->fence.context, request->fence.seqno,
+ request->global_seqno,
+ intel_engine_get_seqno(engine));
+
lockdep_assert_held(&request->i915->drm.struct_mutex);
GEM_BUG_ON(!i915_sw_fence_signaled(&request->submit));
GEM_BUG_ON(!i915_request_completed(request));
@@ -486,21 +450,34 @@ static u32 timeline_get_seqno(struct intel_timeline *tl)
return ++tl->seqno;
}
+static void move_to_timeline(struct i915_request *request,
+ struct intel_timeline *timeline)
+{
+ GEM_BUG_ON(request->timeline == request->engine->timeline);
+ lockdep_assert_held(&request->engine->timeline->lock);
+
+ spin_lock(&request->timeline->lock);
+ list_move_tail(&request->link, &timeline->requests);
+ spin_unlock(&request->timeline->lock);
+}
+
void __i915_request_submit(struct i915_request *request)
{
struct intel_engine_cs *engine = request->engine;
- struct intel_timeline *timeline;
u32 seqno;
+ GEM_TRACE("%s fence %llx:%d -> global=%d, current %d\n",
+ engine->name,
+ request->fence.context, request->fence.seqno,
+ engine->timeline->seqno + 1,
+ intel_engine_get_seqno(engine));
+
GEM_BUG_ON(!irqs_disabled());
lockdep_assert_held(&engine->timeline->lock);
- /* Transfer from per-context onto the global per-engine timeline */
- timeline = engine->timeline;
- GEM_BUG_ON(timeline == request->timeline);
GEM_BUG_ON(request->global_seqno);
- seqno = timeline_get_seqno(timeline);
+ seqno = timeline_get_seqno(engine->timeline);
GEM_BUG_ON(!seqno);
GEM_BUG_ON(i915_seqno_passed(intel_engine_get_seqno(engine), seqno));
@@ -514,9 +491,8 @@ void __i915_request_submit(struct i915_request *request)
engine->emit_breadcrumb(request,
request->ring->vaddr + request->postfix);
- spin_lock(&request->timeline->lock);
- list_move_tail(&request->link, &timeline->requests);
- spin_unlock(&request->timeline->lock);
+ /* Transfer from per-context onto the global per-engine timeline */
+ move_to_timeline(request, engine->timeline);
trace_i915_request_execute(request);
@@ -539,7 +515,12 @@ void i915_request_submit(struct i915_request *request)
void __i915_request_unsubmit(struct i915_request *request)
{
struct intel_engine_cs *engine = request->engine;
- struct intel_timeline *timeline;
+
+ GEM_TRACE("%s fence %llx:%d <- global=%d, current %d\n",
+ engine->name,
+ request->fence.context, request->fence.seqno,
+ request->global_seqno,
+ intel_engine_get_seqno(engine));
GEM_BUG_ON(!irqs_disabled());
lockdep_assert_held(&engine->timeline->lock);
@@ -562,12 +543,7 @@ void __i915_request_unsubmit(struct i915_request *request)
spin_unlock(&request->lock);
/* Transfer back from the global per-engine timeline to per-context */
- timeline = request->timeline;
- GEM_BUG_ON(timeline == engine->timeline);
-
- spin_lock(&timeline->lock);
- list_move(&request->link, &timeline->requests);
- spin_unlock(&timeline->lock);
+ move_to_timeline(request, request->timeline);
/*
* We don't need to wake_up any waiters on request->execute, they
@@ -1000,6 +976,9 @@ void __i915_request_add(struct i915_request *request, bool flush_caches)
u32 *cs;
int err;
+ GEM_TRACE("%s fence %llx:%d\n",
+ engine->name, request->fence.context, request->fence.seqno);
+
lockdep_assert_held(&request->i915->drm.struct_mutex);
trace_i915_request_add(request);
@@ -1206,11 +1185,13 @@ static bool __i915_spin_request(const struct i915_request *rq,
static bool __i915_wait_request_check_and_reset(struct i915_request *request)
{
- if (likely(!i915_reset_handoff(&request->i915->gpu_error)))
+ struct i915_gpu_error *error = &request->i915->gpu_error;
+
+ if (likely(!i915_reset_handoff(error)))
return false;
__set_current_state(TASK_RUNNING);
- i915_reset(request->i915, 0);
+ i915_reset(request->i915, error->stalled_mask, error->reason);
return true;
}
diff --git a/drivers/gpu/drm/i915/i915_utils.h b/drivers/gpu/drm/i915/i915_utils.h
index 51dbfe5bb418..0695717522ea 100644
--- a/drivers/gpu/drm/i915/i915_utils.h
+++ b/drivers/gpu/drm/i915/i915_utils.h
@@ -40,8 +40,8 @@
#undef WARN_ON_ONCE
#define WARN_ON_ONCE(x) WARN_ONCE((x), "%s", "WARN_ON_ONCE(" __stringify(x) ")")
-#define MISSING_CASE(x) WARN(1, "Missing switch case (%lu) in %s\n", \
- (long)(x), __func__)
+#define MISSING_CASE(x) WARN(1, "Missing case (%s == %ld)\n", \
+ __stringify(x), (long)(x))
#if GCC_VERSION >= 70000
#define add_overflows(A, B) \
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c
index e9fb692076d7..40285d1b91b7 100644
--- a/drivers/gpu/drm/i915/intel_atomic.c
+++ b/drivers/gpu/drm/i915/intel_atomic.c
@@ -227,6 +227,7 @@ int intel_atomic_setup_scalers(struct drm_i915_private *dev_priv,
struct intel_crtc_scaler_state *scaler_state =
&crtc_state->scaler_state;
struct drm_atomic_state *drm_state = crtc_state->base.state;
+ struct intel_atomic_state *intel_state = to_intel_atomic_state(drm_state);
int num_scalers_need;
int i, j;
@@ -304,8 +305,8 @@ int intel_atomic_setup_scalers(struct drm_i915_private *dev_priv,
continue;
}
- plane_state = intel_atomic_get_existing_plane_state(drm_state,
- intel_plane);
+ plane_state = intel_atomic_get_new_plane_state(intel_state,
+ intel_plane);
scaler_id = &plane_state->scaler_id;
}
@@ -328,8 +329,18 @@ int intel_atomic_setup_scalers(struct drm_i915_private *dev_priv,
}
/* set scaler mode */
- if (IS_GEMINILAKE(dev_priv) || IS_CANNONLAKE(dev_priv)) {
- scaler_state->scalers[*scaler_id].mode = 0;
+ if ((INTEL_GEN(dev_priv) >= 9) &&
+ plane_state && plane_state->base.fb &&
+ plane_state->base.fb->format->format ==
+ DRM_FORMAT_NV12) {
+ if (INTEL_GEN(dev_priv) == 9 &&
+ !IS_GEMINILAKE(dev_priv) &&
+ !IS_SKYLAKE(dev_priv))
+ scaler_state->scalers[*scaler_id].mode =
+ SKL_PS_SCALER_MODE_NV12;
+ else
+ scaler_state->scalers[*scaler_id].mode =
+ PS_SCALER_MODE_PLANAR;
} else if (num_scalers_need == 1 && intel_crtc->pipe != PIPE_C) {
/*
* when only 1 scaler is in use on either pipe A or B,
diff --git a/drivers/gpu/drm/i915/intel_audio.c b/drivers/gpu/drm/i915/intel_audio.c
index 709d6ca68074..3ea566f99450 100644
--- a/drivers/gpu/drm/i915/intel_audio.c
+++ b/drivers/gpu/drm/i915/intel_audio.c
@@ -729,7 +729,7 @@ static void i915_audio_component_codec_wake_override(struct device *kdev,
struct drm_i915_private *dev_priv = kdev_to_i915(kdev);
u32 tmp;
- if (!IS_GEN9_BC(dev_priv))
+ if (!IS_GEN9(dev_priv))
return;
i915_audio_component_get_power(kdev);
diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c
index c5c7530ba157..702d3fab97fc 100644
--- a/drivers/gpu/drm/i915/intel_bios.c
+++ b/drivers/gpu/drm/i915/intel_bios.c
@@ -1215,10 +1215,8 @@ static void parse_ddi_port(struct drm_i915_private *dev_priv, enum port port,
{
struct child_device_config *it, *child = NULL;
struct ddi_vbt_port_info *info = &dev_priv->vbt.ddi_port_info[port];
- uint8_t hdmi_level_shift;
int i, j;
bool is_dvi, is_hdmi, is_dp, is_edp, is_crt;
- uint8_t aux_channel, ddc_pin;
/* Each DDI port can have more than one value on the "DVO Port" field,
* so look for all the possible values for each port.
*/
@@ -1255,9 +1253,6 @@ static void parse_ddi_port(struct drm_i915_private *dev_priv, enum port port,
if (!child)
return;
- aux_channel = child->aux_channel;
- ddc_pin = child->ddc_pin;
-
is_dvi = child->device_type & DEVICE_TYPE_TMDS_DVI_SIGNALING;
is_dp = child->device_type & DEVICE_TYPE_DISPLAYPORT_OUTPUT;
is_crt = child->device_type & DEVICE_TYPE_ANALOG_OUTPUT;
@@ -1271,13 +1266,6 @@ static void parse_ddi_port(struct drm_i915_private *dev_priv, enum port port,
is_hdmi = false;
}
- if (port == PORT_A && is_dvi) {
- DRM_DEBUG_KMS("VBT claims port A supports DVI%s, ignoring\n",
- is_hdmi ? "/HDMI" : "");
- is_dvi = false;
- is_hdmi = false;
- }
-
info->supports_dvi = is_dvi;
info->supports_hdmi = is_hdmi;
info->supports_dp = is_dp;
@@ -1303,20 +1291,28 @@ static void parse_ddi_port(struct drm_i915_private *dev_priv, enum port port,
DRM_DEBUG_KMS("Port %c is internal DP\n", port_name(port));
if (is_dvi) {
- info->alternate_ddc_pin = map_ddc_pin(dev_priv, ddc_pin);
+ u8 ddc_pin;
- sanitize_ddc_pin(dev_priv, port);
+ ddc_pin = map_ddc_pin(dev_priv, child->ddc_pin);
+ if (intel_gmbus_is_valid_pin(dev_priv, ddc_pin)) {
+ info->alternate_ddc_pin = ddc_pin;
+ sanitize_ddc_pin(dev_priv, port);
+ } else {
+ DRM_DEBUG_KMS("Port %c has invalid DDC pin %d, "
+ "sticking to defaults\n",
+ port_name(port), ddc_pin);
+ }
}
if (is_dp) {
- info->alternate_aux_channel = aux_channel;
+ info->alternate_aux_channel = child->aux_channel;
sanitize_aux_ch(dev_priv, port);
}
if (bdb_version >= 158) {
/* The VBT HDMI level shift values match the table we have. */
- hdmi_level_shift = child->hdmi_level_shifter_value;
+ u8 hdmi_level_shift = child->hdmi_level_shifter_value;
DRM_DEBUG_KMS("VBT HDMI level shift for port %c: %d\n",
port_name(port),
hdmi_level_shift);
diff --git a/drivers/gpu/drm/i915/intel_breadcrumbs.c b/drivers/gpu/drm/i915/intel_breadcrumbs.c
index 1f79e7a47433..671a6d61e29d 100644
--- a/drivers/gpu/drm/i915/intel_breadcrumbs.c
+++ b/drivers/gpu/drm/i915/intel_breadcrumbs.c
@@ -730,10 +730,11 @@ static void insert_signal(struct intel_breadcrumbs *b,
list_add(&request->signaling.link, &iter->signaling.link);
}
-void intel_engine_enable_signaling(struct i915_request *request, bool wakeup)
+bool intel_engine_enable_signaling(struct i915_request *request, bool wakeup)
{
struct intel_engine_cs *engine = request->engine;
struct intel_breadcrumbs *b = &engine->breadcrumbs;
+ struct intel_wait *wait = &request->signaling.wait;
u32 seqno;
/*
@@ -750,12 +751,12 @@ void intel_engine_enable_signaling(struct i915_request *request, bool wakeup)
seqno = i915_request_global_seqno(request);
if (!seqno) /* will be enabled later upon execution */
- return;
+ return true;
- GEM_BUG_ON(request->signaling.wait.seqno);
- request->signaling.wait.tsk = b->signaler;
- request->signaling.wait.request = request;
- request->signaling.wait.seqno = seqno;
+ GEM_BUG_ON(wait->seqno);
+ wait->tsk = b->signaler;
+ wait->request = request;
+ wait->seqno = seqno;
/*
* Add ourselves into the list of waiters, but registering our
@@ -768,11 +769,15 @@ void intel_engine_enable_signaling(struct i915_request *request, bool wakeup)
*/
spin_lock(&b->rb_lock);
insert_signal(b, request, seqno);
- wakeup &= __intel_engine_add_wait(engine, &request->signaling.wait);
+ wakeup &= __intel_engine_add_wait(engine, wait);
spin_unlock(&b->rb_lock);
- if (wakeup)
+ if (wakeup) {
wake_up_process(b->signaler);
+ return !intel_wait_complete(wait);
+ }
+
+ return true;
}
void intel_engine_cancel_signaling(struct i915_request *request)
diff --git a/drivers/gpu/drm/i915/intel_cdclk.c b/drivers/gpu/drm/i915/intel_cdclk.c
index dc7db8a2caf8..32d24c69da3c 100644
--- a/drivers/gpu/drm/i915/intel_cdclk.c
+++ b/drivers/gpu/drm/i915/intel_cdclk.c
@@ -1626,7 +1626,7 @@ static void cnl_cdclk_pll_disable(struct drm_i915_private *dev_priv)
/* Timeout 200us */
if (wait_for((I915_READ(BXT_DE_PLL_ENABLE) & BXT_DE_PLL_LOCK) == 0, 1))
- DRM_ERROR("timout waiting for CDCLK PLL unlock\n");
+ DRM_ERROR("timeout waiting for CDCLK PLL unlock\n");
dev_priv->cdclk.hw.vco = 0;
}
@@ -1644,7 +1644,7 @@ static void cnl_cdclk_pll_enable(struct drm_i915_private *dev_priv, int vco)
/* Timeout 200us */
if (wait_for((I915_READ(BXT_DE_PLL_ENABLE) & BXT_DE_PLL_LOCK) != 0, 1))
- DRM_ERROR("timout waiting for CDCLK PLL lock\n");
+ DRM_ERROR("timeout waiting for CDCLK PLL lock\n");
dev_priv->cdclk.hw.vco = vco;
}
@@ -2140,10 +2140,22 @@ int intel_crtc_compute_min_cdclk(const struct intel_crtc_state *crtc_state)
}
}
- /* According to BSpec, "The CD clock frequency must be at least twice
+ /*
+ * According to BSpec, "The CD clock frequency must be at least twice
* the frequency of the Azalia BCLK." and BCLK is 96 MHz by default.
+ *
+ * FIXME: Check the actual, not default, BCLK being used.
+ *
+ * FIXME: This does not depend on ->has_audio because the higher CDCLK
+ * is required for audio probe, also when there are no audio capable
+ * displays connected at probe time. This leads to unnecessarily high
+ * CDCLK when audio is not required.
+ *
+ * FIXME: This limit is only applied when there are displays connected
+ * at probe time. If we probe without displays, we'll still end up using
+ * the platform minimum CDCLK, failing audio probe.
*/
- if (crtc_state->has_audio && INTEL_GEN(dev_priv) >= 9)
+ if (INTEL_GEN(dev_priv) >= 9)
min_cdclk = max(2 * 96000, min_cdclk);
/*
diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
index 8c2d778560f0..92cb26b18a9b 100644
--- a/drivers/gpu/drm/i915/intel_ddi.c
+++ b/drivers/gpu/drm/i915/intel_ddi.c
@@ -493,6 +493,125 @@ static const struct cnl_ddi_buf_trans cnl_ddi_translations_edp_1_05V[] = {
{ 0x2, 0x7F, 0x3F, 0x00, 0x00 }, /* 400 400 0.0 */
};
+struct icl_combo_phy_ddi_buf_trans {
+ u32 dw2_swing_select;
+ u32 dw2_swing_scalar;
+ u32 dw4_scaling;
+};
+
+/* Voltage Swing Programming for VccIO 0.85V for DP */
+static const struct icl_combo_phy_ddi_buf_trans icl_combo_phy_ddi_translations_dp_hdmi_0_85V[] = {
+ /* Voltage mV db */
+ { 0x2, 0x98, 0x0018 }, /* 400 0.0 */
+ { 0x2, 0x98, 0x3015 }, /* 400 3.5 */
+ { 0x2, 0x98, 0x6012 }, /* 400 6.0 */
+ { 0x2, 0x98, 0x900F }, /* 400 9.5 */
+ { 0xB, 0x70, 0x0018 }, /* 600 0.0 */
+ { 0xB, 0x70, 0x3015 }, /* 600 3.5 */
+ { 0xB, 0x70, 0x6012 }, /* 600 6.0 */
+ { 0x5, 0x00, 0x0018 }, /* 800 0.0 */
+ { 0x5, 0x00, 0x3015 }, /* 800 3.5 */
+ { 0x6, 0x98, 0x0018 }, /* 1200 0.0 */
+};
+
+/* FIXME - After table is updated in Bspec */
+/* Voltage Swing Programming for VccIO 0.85V for eDP */
+static const struct icl_combo_phy_ddi_buf_trans icl_combo_phy_ddi_translations_edp_0_85V[] = {
+ /* Voltage mV db */
+ { 0x0, 0x00, 0x00 }, /* 200 0.0 */
+ { 0x0, 0x00, 0x00 }, /* 200 1.5 */
+ { 0x0, 0x00, 0x00 }, /* 200 4.0 */
+ { 0x0, 0x00, 0x00 }, /* 200 6.0 */
+ { 0x0, 0x00, 0x00 }, /* 250 0.0 */
+ { 0x0, 0x00, 0x00 }, /* 250 1.5 */
+ { 0x0, 0x00, 0x00 }, /* 250 4.0 */
+ { 0x0, 0x00, 0x00 }, /* 300 0.0 */
+ { 0x0, 0x00, 0x00 }, /* 300 1.5 */
+ { 0x0, 0x00, 0x00 }, /* 350 0.0 */
+};
+
+/* Voltage Swing Programming for VccIO 0.95V for DP */
+static const struct icl_combo_phy_ddi_buf_trans icl_combo_phy_ddi_translations_dp_hdmi_0_95V[] = {
+ /* Voltage mV db */
+ { 0x2, 0x98, 0x0018 }, /* 400 0.0 */
+ { 0x2, 0x98, 0x3015 }, /* 400 3.5 */
+ { 0x2, 0x98, 0x6012 }, /* 400 6.0 */
+ { 0x2, 0x98, 0x900F }, /* 400 9.5 */
+ { 0x4, 0x98, 0x0018 }, /* 600 0.0 */
+ { 0x4, 0x98, 0x3015 }, /* 600 3.5 */
+ { 0x4, 0x98, 0x6012 }, /* 600 6.0 */
+ { 0x5, 0x76, 0x0018 }, /* 800 0.0 */
+ { 0x5, 0x76, 0x3015 }, /* 800 3.5 */
+ { 0x6, 0x98, 0x0018 }, /* 1200 0.0 */
+};
+
+/* FIXME - After table is updated in Bspec */
+/* Voltage Swing Programming for VccIO 0.95V for eDP */
+static const struct icl_combo_phy_ddi_buf_trans icl_combo_phy_ddi_translations_edp_0_95V[] = {
+ /* Voltage mV db */
+ { 0x0, 0x00, 0x00 }, /* 200 0.0 */
+ { 0x0, 0x00, 0x00 }, /* 200 1.5 */
+ { 0x0, 0x00, 0x00 }, /* 200 4.0 */
+ { 0x0, 0x00, 0x00 }, /* 200 6.0 */
+ { 0x0, 0x00, 0x00 }, /* 250 0.0 */
+ { 0x0, 0x00, 0x00 }, /* 250 1.5 */
+ { 0x0, 0x00, 0x00 }, /* 250 4.0 */
+ { 0x0, 0x00, 0x00 }, /* 300 0.0 */
+ { 0x0, 0x00, 0x00 }, /* 300 1.5 */
+ { 0x0, 0x00, 0x00 }, /* 350 0.0 */
+};
+
+/* Voltage Swing Programming for VccIO 1.05V for DP */
+static const struct icl_combo_phy_ddi_buf_trans icl_combo_phy_ddi_translations_dp_hdmi_1_05V[] = {
+ /* Voltage mV db */
+ { 0x2, 0x98, 0x0018 }, /* 400 0.0 */
+ { 0x2, 0x98, 0x3015 }, /* 400 3.5 */
+ { 0x2, 0x98, 0x6012 }, /* 400 6.0 */
+ { 0x2, 0x98, 0x900F }, /* 400 9.5 */
+ { 0x4, 0x98, 0x0018 }, /* 600 0.0 */
+ { 0x4, 0x98, 0x3015 }, /* 600 3.5 */
+ { 0x4, 0x98, 0x6012 }, /* 600 6.0 */
+ { 0x5, 0x71, 0x0018 }, /* 800 0.0 */
+ { 0x5, 0x71, 0x3015 }, /* 800 3.5 */
+ { 0x6, 0x98, 0x0018 }, /* 1200 0.0 */
+};
+
+/* FIXME - After table is updated in Bspec */
+/* Voltage Swing Programming for VccIO 1.05V for eDP */
+static const struct icl_combo_phy_ddi_buf_trans icl_combo_phy_ddi_translations_edp_1_05V[] = {
+ /* Voltage mV db */
+ { 0x0, 0x00, 0x00 }, /* 200 0.0 */
+ { 0x0, 0x00, 0x00 }, /* 200 1.5 */
+ { 0x0, 0x00, 0x00 }, /* 200 4.0 */
+ { 0x0, 0x00, 0x00 }, /* 200 6.0 */
+ { 0x0, 0x00, 0x00 }, /* 250 0.0 */
+ { 0x0, 0x00, 0x00 }, /* 250 1.5 */
+ { 0x0, 0x00, 0x00 }, /* 250 4.0 */
+ { 0x0, 0x00, 0x00 }, /* 300 0.0 */
+ { 0x0, 0x00, 0x00 }, /* 300 1.5 */
+ { 0x0, 0x00, 0x00 }, /* 350 0.0 */
+};
+
+struct icl_mg_phy_ddi_buf_trans {
+ u32 cri_txdeemph_override_5_0;
+ u32 cri_txdeemph_override_11_6;
+ u32 cri_txdeemph_override_17_12;
+};
+
+static const struct icl_mg_phy_ddi_buf_trans icl_mg_phy_ddi_translations[] = {
+ /* Voltage swing pre-emphasis */
+ { 0x0, 0x1B, 0x00 }, /* 0 0 */
+ { 0x0, 0x23, 0x08 }, /* 0 1 */
+ { 0x0, 0x2D, 0x12 }, /* 0 2 */
+ { 0x0, 0x00, 0x00 }, /* 0 3 */
+ { 0x0, 0x23, 0x00 }, /* 1 0 */
+ { 0x0, 0x2B, 0x09 }, /* 1 1 */
+ { 0x0, 0x2E, 0x11 }, /* 1 2 */
+ { 0x0, 0x2F, 0x00 }, /* 2 0 */
+ { 0x0, 0x33, 0x0C }, /* 2 1 */
+ { 0x0, 0x00, 0x00 }, /* 3 0 */
+};
+
static const struct ddi_buf_trans *
bdw_get_buf_trans_edp(struct drm_i915_private *dev_priv, int *n_entries)
{
@@ -875,7 +994,7 @@ static void intel_wait_ddi_buf_idle(struct drm_i915_private *dev_priv,
static uint32_t hsw_pll_to_ddi_pll_sel(const struct intel_shared_dpll *pll)
{
- switch (pll->id) {
+ switch (pll->info->id) {
case DPLL_ID_WRPLL1:
return PORT_CLK_SEL_WRPLL1;
case DPLL_ID_WRPLL2:
@@ -889,7 +1008,7 @@ static uint32_t hsw_pll_to_ddi_pll_sel(const struct intel_shared_dpll *pll)
case DPLL_ID_LCPLL_2700:
return PORT_CLK_SEL_LCPLL_2700;
default:
- MISSING_CASE(pll->id);
+ MISSING_CASE(pll->info->id);
return PORT_CLK_SEL_NONE;
}
}
@@ -2131,7 +2250,7 @@ static void intel_ddi_clk_select(struct intel_encoder *encoder,
/* Configure DPCLKA_CFGCR0 to map the DPLL to the DDI. */
val = I915_READ(DPCLKA_CFGCR0);
val &= ~DPCLKA_CFGCR0_DDI_CLK_SEL_MASK(port);
- val |= DPCLKA_CFGCR0_DDI_CLK_SEL(pll->id, port);
+ val |= DPCLKA_CFGCR0_DDI_CLK_SEL(pll->info->id, port);
I915_WRITE(DPCLKA_CFGCR0, val);
/*
@@ -2148,7 +2267,7 @@ static void intel_ddi_clk_select(struct intel_encoder *encoder,
val &= ~(DPLL_CTRL2_DDI_CLK_OFF(port) |
DPLL_CTRL2_DDI_CLK_SEL_MASK(port));
- val |= (DPLL_CTRL2_DDI_CLK_SEL(pll->id, port) |
+ val |= (DPLL_CTRL2_DDI_CLK_SEL(pll->info->id, port) |
DPLL_CTRL2_DDI_SEL_OVERRIDE(port));
I915_WRITE(DPLL_CTRL2, val);
@@ -2205,7 +2324,8 @@ static void intel_ddi_pre_enable_dp(struct intel_encoder *encoder,
intel_prepare_dp_ddi_buffers(encoder, crtc_state);
intel_ddi_init_dp_buf_reg(encoder);
- intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
+ if (!is_mst)
+ intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
intel_dp_start_link_train(intel_dp);
if (port != PORT_A || INTEL_GEN(dev_priv) >= 9)
intel_dp_stop_link_train(intel_dp);
@@ -2303,12 +2423,15 @@ static void intel_ddi_post_disable_dp(struct intel_encoder *encoder,
struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
struct intel_digital_port *dig_port = enc_to_dig_port(&encoder->base);
struct intel_dp *intel_dp = &dig_port->dp;
+ bool is_mst = intel_crtc_has_type(old_crtc_state,
+ INTEL_OUTPUT_DP_MST);
/*
* Power down sink before disabling the port, otherwise we end
* up getting interrupts from the sink on detecting link loss.
*/
- intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_OFF);
+ if (!is_mst)
+ intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_OFF);
intel_disable_ddi_buf(encoder);
@@ -2424,12 +2547,14 @@ static void intel_enable_ddi_hdmi(struct intel_encoder *encoder,
{
struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
struct intel_digital_port *dig_port = enc_to_dig_port(&encoder->base);
+ struct drm_connector *connector = conn_state->connector;
enum port port = encoder->port;
- intel_hdmi_handle_sink_scrambling(encoder,
- conn_state->connector,
- crtc_state->hdmi_high_tmds_clock_ratio,
- crtc_state->hdmi_scrambling);
+ if (!intel_hdmi_handle_sink_scrambling(encoder, connector,
+ crtc_state->hdmi_high_tmds_clock_ratio,
+ crtc_state->hdmi_scrambling))
+ DRM_ERROR("[CONNECTOR:%d:%s] Failed to configure sink scrambling/TMDS bit clock ratio\n",
+ connector->base.id, connector->name);
/* Display WA #1143: skl,kbl,cfl */
if (IS_GEN9_BC(dev_priv)) {
@@ -2520,13 +2645,16 @@ static void intel_disable_ddi_hdmi(struct intel_encoder *encoder,
const struct intel_crtc_state *old_crtc_state,
const struct drm_connector_state *old_conn_state)
{
+ struct drm_connector *connector = old_conn_state->connector;
+
if (old_crtc_state->has_audio)
intel_audio_codec_disable(encoder,
old_crtc_state, old_conn_state);
- intel_hdmi_handle_sink_scrambling(encoder,
- old_conn_state->connector,
- false, false);
+ if (!intel_hdmi_handle_sink_scrambling(encoder, connector,
+ false, false))
+ DRM_DEBUG_KMS("[CONNECTOR:%d:%s] Failed to reset sink scrambling/TMDS bit clock ratio\n",
+ connector->base.id, connector->name);
}
static void intel_disable_ddi(struct intel_encoder *encoder,
diff --git a/drivers/gpu/drm/i915/intel_device_info.c b/drivers/gpu/drm/i915/intel_device_info.c
index 3dd350f7b8e6..a32ba72c514e 100644
--- a/drivers/gpu/drm/i915/intel_device_info.c
+++ b/drivers/gpu/drm/i915/intel_device_info.c
@@ -83,11 +83,11 @@ static void sseu_dump(const struct sseu_dev_info *sseu, struct drm_printer *p)
{
int s;
- drm_printf(p, "slice mask: %04x\n", sseu->slice_mask);
- drm_printf(p, "slice total: %u\n", hweight8(sseu->slice_mask));
+ drm_printf(p, "slice total: %u, mask=%04x\n",
+ hweight8(sseu->slice_mask), sseu->slice_mask);
drm_printf(p, "subslice total: %u\n", sseu_subslice_total(sseu));
- for (s = 0; s < ARRAY_SIZE(sseu->subslice_mask); s++) {
- drm_printf(p, "slice%d %u subslices mask=%04x\n",
+ for (s = 0; s < sseu->max_slices; s++) {
+ drm_printf(p, "slice%d: %u subslices, mask=%04x\n",
s, hweight8(sseu->subslice_mask[s]),
sseu->subslice_mask[s]);
}
@@ -158,6 +158,45 @@ static u16 compute_eu_total(const struct sseu_dev_info *sseu)
return total;
}
+static void gen11_sseu_info_init(struct drm_i915_private *dev_priv)
+{
+ struct sseu_dev_info *sseu = &mkwrite_device_info(dev_priv)->sseu;
+ u8 s_en;
+ u32 ss_en, ss_en_mask;
+ u8 eu_en;
+ int s;
+
+ sseu->max_slices = 1;
+ sseu->max_subslices = 8;
+ sseu->max_eus_per_subslice = 8;
+
+ s_en = I915_READ(GEN11_GT_SLICE_ENABLE) & GEN11_GT_S_ENA_MASK;
+ ss_en = ~I915_READ(GEN11_GT_SUBSLICE_DISABLE);
+ ss_en_mask = BIT(sseu->max_subslices) - 1;
+ eu_en = ~(I915_READ(GEN11_EU_DISABLE) & GEN11_EU_DIS_MASK);
+
+ for (s = 0; s < sseu->max_slices; s++) {
+ if (s_en & BIT(s)) {
+ int ss_idx = sseu->max_subslices * s;
+ int ss;
+
+ sseu->slice_mask |= BIT(s);
+ sseu->subslice_mask[s] = (ss_en >> ss_idx) & ss_en_mask;
+ for (ss = 0; ss < sseu->max_subslices; ss++) {
+ if (sseu->subslice_mask[s] & BIT(ss))
+ sseu_set_eus(sseu, s, ss, eu_en);
+ }
+ }
+ }
+ sseu->eu_per_subslice = hweight8(eu_en);
+ sseu->eu_total = compute_eu_total(sseu);
+
+ /* ICL has no power gating restrictions. */
+ sseu->has_slice_pg = 1;
+ sseu->has_subslice_pg = 1;
+ sseu->has_eu_pg = 1;
+}
+
static void gen10_sseu_info_init(struct drm_i915_private *dev_priv)
{
struct sseu_dev_info *sseu = &mkwrite_device_info(dev_priv)->sseu;
@@ -557,6 +596,52 @@ static u32 read_reference_ts_freq(struct drm_i915_private *dev_priv)
return base_freq + frac_freq;
}
+static u32 gen10_get_crystal_clock_freq(struct drm_i915_private *dev_priv,
+ u32 rpm_config_reg)
+{
+ u32 f19_2_mhz = 19200;
+ u32 f24_mhz = 24000;
+ u32 crystal_clock = (rpm_config_reg &
+ GEN9_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_MASK) >>
+ GEN9_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_SHIFT;
+
+ switch (crystal_clock) {
+ case GEN9_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_19_2_MHZ:
+ return f19_2_mhz;
+ case GEN9_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_24_MHZ:
+ return f24_mhz;
+ default:
+ MISSING_CASE(crystal_clock);
+ return 0;
+ }
+}
+
+static u32 gen11_get_crystal_clock_freq(struct drm_i915_private *dev_priv,
+ u32 rpm_config_reg)
+{
+ u32 f19_2_mhz = 19200;
+ u32 f24_mhz = 24000;
+ u32 f25_mhz = 25000;
+ u32 f38_4_mhz = 38400;
+ u32 crystal_clock = (rpm_config_reg &
+ GEN11_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_MASK) >>
+ GEN11_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_SHIFT;
+
+ switch (crystal_clock) {
+ case GEN11_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_24_MHZ:
+ return f24_mhz;
+ case GEN11_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_19_2_MHZ:
+ return f19_2_mhz;
+ case GEN11_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_38_4_MHZ:
+ return f38_4_mhz;
+ case GEN11_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_25_MHZ:
+ return f25_mhz;
+ default:
+ MISSING_CASE(crystal_clock);
+ return 0;
+ }
+}
+
static u32 read_timestamp_frequency(struct drm_i915_private *dev_priv)
{
u32 f12_5_mhz = 12500;
@@ -597,10 +682,9 @@ static u32 read_timestamp_frequency(struct drm_i915_private *dev_priv)
}
return freq;
- } else if (INTEL_GEN(dev_priv) <= 10) {
+ } else if (INTEL_GEN(dev_priv) <= 11) {
u32 ctc_reg = I915_READ(CTC_MODE);
u32 freq = 0;
- u32 rpm_config_reg = 0;
/* First figure out the reference frequency. There are 2 ways
* we can compute the frequency, either through the
@@ -610,20 +694,14 @@ static u32 read_timestamp_frequency(struct drm_i915_private *dev_priv)
if ((ctc_reg & CTC_SOURCE_PARAMETER_MASK) == CTC_SOURCE_DIVIDE_LOGIC) {
freq = read_reference_ts_freq(dev_priv);
} else {
- u32 crystal_clock;
-
- rpm_config_reg = I915_READ(RPM_CONFIG0);
- crystal_clock = (rpm_config_reg &
- GEN9_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_MASK) >>
- GEN9_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_SHIFT;
- switch (crystal_clock) {
- case GEN9_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_19_2_MHZ:
- freq = f19_2_mhz;
- break;
- case GEN9_RPM_CONFIG0_CRYSTAL_CLOCK_FREQ_24_MHZ:
- freq = f24_mhz;
- break;
- }
+ u32 rpm_config_reg = I915_READ(RPM_CONFIG0);
+
+ if (INTEL_GEN(dev_priv) <= 10)
+ freq = gen10_get_crystal_clock_freq(dev_priv,
+ rpm_config_reg);
+ else
+ freq = gen11_get_crystal_clock_freq(dev_priv,
+ rpm_config_reg);
/* Now figure out how the command stream's timestamp
* register increments from this frequency (it might
@@ -768,8 +846,10 @@ void intel_device_info_runtime_init(struct intel_device_info *info)
broadwell_sseu_info_init(dev_priv);
else if (INTEL_GEN(dev_priv) == 9)
gen9_sseu_info_init(dev_priv);
- else if (INTEL_GEN(dev_priv) >= 10)
+ else if (INTEL_GEN(dev_priv) == 10)
gen10_sseu_info_init(dev_priv);
+ else if (INTEL_INFO(dev_priv)->gen >= 11)
+ gen11_sseu_info_init(dev_priv);
/* Initialize command stream timestamp frequency */
info->cs_timestamp_frequency_khz = read_timestamp_frequency(dev_priv);
@@ -780,3 +860,50 @@ void intel_driver_caps_print(const struct intel_driver_caps *caps,
{
drm_printf(p, "scheduler: %x\n", caps->scheduler);
}
+
+/*
+ * Determine which engines are fused off in our particular hardware. Since the
+ * fuse register is in the blitter powerwell, we need forcewake to be ready at
+ * this point (but later we need to prune the forcewake domains for engines that
+ * are indeed fused off).
+ */
+void intel_device_info_init_mmio(struct drm_i915_private *dev_priv)
+{
+ struct intel_device_info *info = mkwrite_device_info(dev_priv);
+ u8 vdbox_disable, vebox_disable;
+ u32 media_fuse;
+ int i;
+
+ if (INTEL_GEN(dev_priv) < 11)
+ return;
+
+ media_fuse = I915_READ(GEN11_GT_VEBOX_VDBOX_DISABLE);
+
+ vdbox_disable = media_fuse & GEN11_GT_VDBOX_DISABLE_MASK;
+ vebox_disable = (media_fuse & GEN11_GT_VEBOX_DISABLE_MASK) >>
+ GEN11_GT_VEBOX_DISABLE_SHIFT;
+
+ DRM_DEBUG_DRIVER("vdbox disable: %04x\n", vdbox_disable);
+ for (i = 0; i < I915_MAX_VCS; i++) {
+ if (!HAS_ENGINE(dev_priv, _VCS(i)))
+ continue;
+
+ if (!(BIT(i) & vdbox_disable))
+ continue;
+
+ info->ring_mask &= ~ENGINE_MASK(_VCS(i));
+ DRM_DEBUG_DRIVER("vcs%u fused off\n", i);
+ }
+
+ DRM_DEBUG_DRIVER("vebox disable: %04x\n", vebox_disable);
+ for (i = 0; i < I915_MAX_VECS; i++) {
+ if (!HAS_ENGINE(dev_priv, _VECS(i)))
+ continue;
+
+ if (!(BIT(i) & vebox_disable))
+ continue;
+
+ info->ring_mask &= ~ENGINE_MASK(_VECS(i));
+ DRM_DEBUG_DRIVER("vecs%u fused off\n", i);
+ }
+}
diff --git a/drivers/gpu/drm/i915/intel_device_info.h b/drivers/gpu/drm/i915/intel_device_info.h
index 0835752c8b22..933e31669557 100644
--- a/drivers/gpu/drm/i915/intel_device_info.h
+++ b/drivers/gpu/drm/i915/intel_device_info.h
@@ -114,7 +114,7 @@ enum intel_platform {
func(has_ipc);
#define GEN_MAX_SLICES (6) /* CNL upper bound */
-#define GEN_MAX_SUBSLICES (7)
+#define GEN_MAX_SUBSLICES (8) /* ICL upper bound */
struct sseu_dev_info {
u8 slice_mask;
@@ -247,6 +247,8 @@ void intel_device_info_dump_runtime(const struct intel_device_info *info,
void intel_device_info_dump_topology(const struct sseu_dev_info *sseu,
struct drm_printer *p);
+void intel_device_info_init_mmio(struct drm_i915_private *dev_priv);
+
void intel_driver_caps_print(const struct intel_driver_caps *caps,
struct drm_printer *p);
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 182f9bf98484..020900e08d42 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -488,6 +488,21 @@ static const struct intel_limit intel_limits_bxt = {
.p2 = { .p2_slow = 1, .p2_fast = 20 },
};
+static void
+skl_wa_clkgate(struct drm_i915_private *dev_priv, int pipe, bool enable)
+{
+ if (IS_SKYLAKE(dev_priv))
+ return;
+
+ if (enable)
+ I915_WRITE(CLKGATE_DIS_PSL(pipe),
+ DUPS1_GATING_DIS | DUPS2_GATING_DIS);
+ else
+ I915_WRITE(CLKGATE_DIS_PSL(pipe),
+ I915_READ(CLKGATE_DIS_PSL(pipe)) &
+ ~(DUPS1_GATING_DIS | DUPS2_GATING_DIS));
+}
+
static bool
needs_modeset(const struct drm_crtc_state *state)
{
@@ -2657,11 +2672,13 @@ static int i9xx_format_to_fourcc(int format)
}
}
-static int skl_format_to_fourcc(int format, bool rgb_order, bool alpha)
+int skl_format_to_fourcc(int format, bool rgb_order, bool alpha)
{
switch (format) {
case PLANE_CTL_FORMAT_RGB_565:
return DRM_FORMAT_RGB565;
+ case PLANE_CTL_FORMAT_NV12:
+ return DRM_FORMAT_NV12;
default:
case PLANE_CTL_FORMAT_XRGB_8888:
if (rgb_order) {
@@ -2858,6 +2875,9 @@ valid_fb:
return;
}
+ obj = intel_fb_obj(fb);
+ intel_fb_obj_flush(obj, ORIGIN_DIRTYFB);
+
plane_state->src_x = 0;
plane_state->src_y = 0;
plane_state->src_w = fb->width << 16;
@@ -2871,7 +2891,6 @@ valid_fb:
intel_state->base.src = drm_plane_state_src(plane_state);
intel_state->base.dst = drm_plane_state_dest(plane_state);
- obj = intel_fb_obj(fb);
if (i915_gem_object_is_tiled(obj))
dev_priv->preserve_bios_swizzle = true;
@@ -3464,6 +3483,8 @@ static u32 skl_plane_ctl_format(uint32_t pixel_format)
return PLANE_CTL_FORMAT_YUV422 | PLANE_CTL_YUV422_UYVY;
case DRM_FORMAT_VYUY:
return PLANE_CTL_FORMAT_YUV422 | PLANE_CTL_YUV422_VYUY;
+ case DRM_FORMAT_NV12:
+ return PLANE_CTL_FORMAT_NV12;
default:
MISSING_CASE(pixel_format);
}
@@ -3611,6 +3632,11 @@ u32 glk_plane_color_ctl(const struct intel_crtc_state *crtc_state,
plane_color_ctl |= glk_plane_color_ctl_alpha(fb->format->format);
if (intel_format_is_yuv(fb->format->format)) {
+ if (fb->format->format == DRM_FORMAT_NV12) {
+ plane_color_ctl |=
+ PLANE_COLOR_CSC_MODE_YUV709_TO_RGB709;
+ goto out;
+ }
if (plane_state->base.color_encoding == DRM_COLOR_YCBCR_BT709)
plane_color_ctl |= PLANE_COLOR_CSC_MODE_YUV709_TO_RGB709;
else
@@ -3619,7 +3645,7 @@ u32 glk_plane_color_ctl(const struct intel_crtc_state *crtc_state,
if (plane_state->base.color_range == DRM_COLOR_YCBCR_FULL_RANGE)
plane_color_ctl |= PLANE_COLOR_YUV_RANGE_CORRECTION_DISABLE;
}
-
+out:
return plane_color_ctl;
}
@@ -3675,7 +3701,6 @@ void intel_prepare_reset(struct drm_i915_private *dev_priv)
struct drm_atomic_state *state;
int ret;
-
/* reset doesn't touch the display */
if (!i915_modparams.force_reset_modeset_test &&
!gpu_reset_clobbers_display(dev_priv))
@@ -3729,19 +3754,17 @@ void intel_finish_reset(struct drm_i915_private *dev_priv)
{
struct drm_device *dev = &dev_priv->drm;
struct drm_modeset_acquire_ctx *ctx = &dev_priv->reset_ctx;
- struct drm_atomic_state *state = dev_priv->modeset_restore_state;
+ struct drm_atomic_state *state;
int ret;
/* reset doesn't touch the display */
- if (!i915_modparams.force_reset_modeset_test &&
- !gpu_reset_clobbers_display(dev_priv))
+ if (!test_bit(I915_RESET_MODESET, &dev_priv->gpu_error.flags))
return;
+ state = fetch_and_zero(&dev_priv->modeset_restore_state);
if (!state)
goto unlock;
- dev_priv->modeset_restore_state = NULL;
-
/* reset doesn't touch the display */
if (!gpu_reset_clobbers_display(dev_priv)) {
/* for testing only restore the display */
@@ -4703,7 +4726,9 @@ static void cpt_verify_modeset(struct drm_device *dev, int pipe)
static int
skl_update_scaler(struct intel_crtc_state *crtc_state, bool force_detach,
unsigned int scaler_user, int *scaler_id,
- int src_w, int src_h, int dst_w, int dst_h)
+ int src_w, int src_h, int dst_w, int dst_h,
+ bool plane_scaler_check,
+ uint32_t pixel_format)
{
struct intel_crtc_scaler_state *scaler_state =
&crtc_state->scaler_state;
@@ -4721,6 +4746,10 @@ skl_update_scaler(struct intel_crtc_state *crtc_state, bool force_detach,
*/
need_scaling = src_w != dst_w || src_h != dst_h;
+ if (plane_scaler_check)
+ if (pixel_format == DRM_FORMAT_NV12)
+ need_scaling = true;
+
if (crtc_state->ycbcr420 && scaler_user == SKL_CRTC_INDEX)
need_scaling = true;
@@ -4760,12 +4789,22 @@ skl_update_scaler(struct intel_crtc_state *crtc_state, bool force_detach,
return 0;
}
+ if (plane_scaler_check && pixel_format == DRM_FORMAT_NV12 &&
+ (src_h < SKL_MIN_YUV_420_SRC_H || (src_w % 4) != 0 ||
+ (src_h % 4) != 0)) {
+ DRM_DEBUG_KMS("NV12: src dimensions not met\n");
+ return -EINVAL;
+ }
+
/* range checks */
if (src_w < SKL_MIN_SRC_W || src_h < SKL_MIN_SRC_H ||
- dst_w < SKL_MIN_DST_W || dst_h < SKL_MIN_DST_H ||
-
- src_w > SKL_MAX_SRC_W || src_h > SKL_MAX_SRC_H ||
- dst_w > SKL_MAX_DST_W || dst_h > SKL_MAX_DST_H) {
+ dst_w < SKL_MIN_DST_W || dst_h < SKL_MIN_DST_H ||
+ (IS_GEN11(dev_priv) &&
+ (src_w > ICL_MAX_SRC_W || src_h > ICL_MAX_SRC_H ||
+ dst_w > ICL_MAX_DST_W || dst_h > ICL_MAX_DST_H)) ||
+ (!IS_GEN11(dev_priv) &&
+ (src_w > SKL_MAX_SRC_W || src_h > SKL_MAX_SRC_H ||
+ dst_w > SKL_MAX_DST_W || dst_h > SKL_MAX_DST_H))) {
DRM_DEBUG_KMS("scaler_user index %u.%u: src %ux%u dst %ux%u "
"size is out of scaler range\n",
intel_crtc->pipe, scaler_user, src_w, src_h, dst_w, dst_h);
@@ -4796,9 +4835,10 @@ int skl_update_scaler_crtc(struct intel_crtc_state *state)
const struct drm_display_mode *adjusted_mode = &state->base.adjusted_mode;
return skl_update_scaler(state, !state->base.active, SKL_CRTC_INDEX,
- &state->scaler_state.scaler_id,
- state->pipe_src_w, state->pipe_src_h,
- adjusted_mode->crtc_hdisplay, adjusted_mode->crtc_vdisplay);
+ &state->scaler_state.scaler_id,
+ state->pipe_src_w, state->pipe_src_h,
+ adjusted_mode->crtc_hdisplay,
+ adjusted_mode->crtc_vdisplay, false, 0);
}
/**
@@ -4827,7 +4867,8 @@ static int skl_update_scaler_plane(struct intel_crtc_state *crtc_state,
drm_rect_width(&plane_state->base.src) >> 16,
drm_rect_height(&plane_state->base.src) >> 16,
drm_rect_width(&plane_state->base.dst),
- drm_rect_height(&plane_state->base.dst));
+ drm_rect_height(&plane_state->base.dst),
+ fb ? true : false, fb ? fb->format->format : 0);
if (ret || plane_state->scaler_id < 0)
return ret;
@@ -4853,6 +4894,7 @@ static int skl_update_scaler_plane(struct intel_crtc_state *crtc_state,
case DRM_FORMAT_YVYU:
case DRM_FORMAT_UYVY:
case DRM_FORMAT_VYUY:
+ case DRM_FORMAT_NV12:
break;
default:
DRM_DEBUG_KMS("[PLANE:%d:%s] FB:%d unsupported scaling format 0x%x\n",
@@ -5099,13 +5141,15 @@ static bool hsw_post_update_enable_ips(const struct intel_crtc_state *old_crtc_s
static void intel_post_plane_update(struct intel_crtc_state *old_crtc_state)
{
struct intel_crtc *crtc = to_intel_crtc(old_crtc_state->base.crtc);
+ struct drm_device *dev = crtc->base.dev;
+ struct drm_i915_private *dev_priv = to_i915(dev);
struct drm_atomic_state *old_state = old_crtc_state->base.state;
struct intel_crtc_state *pipe_config =
intel_atomic_get_new_crtc_state(to_intel_atomic_state(old_state),
crtc);
struct drm_plane *primary = crtc->base.primary;
- struct drm_plane_state *old_pri_state =
- drm_atomic_get_existing_plane_state(old_state, primary);
+ struct drm_plane_state *old_primary_state =
+ drm_atomic_get_old_plane_state(old_state, primary);
intel_frontbuffer_flip(to_i915(crtc->base.dev), pipe_config->fb_bits);
@@ -5115,19 +5159,25 @@ static void intel_post_plane_update(struct intel_crtc_state *old_crtc_state)
if (hsw_post_update_enable_ips(old_crtc_state, pipe_config))
hsw_enable_ips(pipe_config);
- if (old_pri_state) {
- struct intel_plane_state *primary_state =
- intel_atomic_get_new_plane_state(to_intel_atomic_state(old_state),
- to_intel_plane(primary));
- struct intel_plane_state *old_primary_state =
- to_intel_plane_state(old_pri_state);
+ if (old_primary_state) {
+ struct drm_plane_state *new_primary_state =
+ drm_atomic_get_new_plane_state(old_state, primary);
+ struct drm_framebuffer *fb = new_primary_state->fb;
intel_fbc_post_update(crtc);
- if (primary_state->base.visible &&
+ if (new_primary_state->visible &&
(needs_modeset(&pipe_config->base) ||
- !old_primary_state->base.visible))
+ !old_primary_state->visible))
intel_post_enable_primary(&crtc->base, pipe_config);
+
+ /* Display WA 827 */
+ if ((INTEL_GEN(dev_priv) == 9 && !IS_GEMINILAKE(dev_priv)) ||
+ IS_CANNONLAKE(dev_priv)) {
+ if (fb && fb->format->format == DRM_FORMAT_NV12)
+ skl_wa_clkgate(dev_priv, crtc->pipe, false);
+ }
+
}
}
@@ -5139,8 +5189,8 @@ static void intel_pre_plane_update(struct intel_crtc_state *old_crtc_state,
struct drm_i915_private *dev_priv = to_i915(dev);
struct drm_atomic_state *old_state = old_crtc_state->base.state;
struct drm_plane *primary = crtc->base.primary;
- struct drm_plane_state *old_pri_state =
- drm_atomic_get_existing_plane_state(old_state, primary);
+ struct drm_plane_state *old_primary_state =
+ drm_atomic_get_old_plane_state(old_state, primary);
bool modeset = needs_modeset(&pipe_config->base);
struct intel_atomic_state *old_intel_state =
to_intel_atomic_state(old_state);
@@ -5148,20 +5198,26 @@ static void intel_pre_plane_update(struct intel_crtc_state *old_crtc_state,
if (hsw_pre_update_disable_ips(old_crtc_state, pipe_config))
hsw_disable_ips(old_crtc_state);
- if (old_pri_state) {
- struct intel_plane_state *primary_state =
+ if (old_primary_state) {
+ struct intel_plane_state *new_primary_state =
intel_atomic_get_new_plane_state(old_intel_state,
to_intel_plane(primary));
- struct intel_plane_state *old_primary_state =
- to_intel_plane_state(old_pri_state);
+ struct drm_framebuffer *fb = new_primary_state->base.fb;
+
+ /* Display WA 827 */
+ if ((INTEL_GEN(dev_priv) == 9 && !IS_GEMINILAKE(dev_priv)) ||
+ IS_CANNONLAKE(dev_priv)) {
+ if (fb && fb->format->format == DRM_FORMAT_NV12)
+ skl_wa_clkgate(dev_priv, crtc->pipe, true);
+ }
- intel_fbc_pre_update(crtc, pipe_config, primary_state);
+ intel_fbc_pre_update(crtc, pipe_config, new_primary_state);
/*
* Gen2 reports pipe underruns whenever all planes are disabled.
* So disable underrun reporting before all the planes get disabled.
*/
- if (IS_GEN2(dev_priv) && old_primary_state->base.visible &&
- (modeset || !primary_state->base.visible))
+ if (IS_GEN2(dev_priv) && old_primary_state->visible &&
+ (modeset || !new_primary_state->base.visible))
intel_set_cpu_fifo_underrun_reporting(dev_priv, crtc->pipe, false);
}
@@ -8766,8 +8822,8 @@ static bool ironlake_get_pipe_config(struct intel_crtc *crtc,
intel_get_shared_dpll_by_id(dev_priv, pll_id);
pll = pipe_config->shared_dpll;
- WARN_ON(!pll->funcs.get_hw_state(dev_priv, pll,
- &pipe_config->dpll_hw_state));
+ WARN_ON(!pll->info->funcs->get_hw_state(dev_priv, pll,
+ &pipe_config->dpll_hw_state));
tmp = pipe_config->dpll_hw_state.dpll;
pipe_config->pixel_multiplier =
@@ -9243,8 +9299,8 @@ static void haswell_get_ddi_port_state(struct intel_crtc *crtc,
pll = pipe_config->shared_dpll;
if (pll) {
- WARN_ON(!pll->funcs.get_hw_state(dev_priv, pll,
- &pipe_config->dpll_hw_state));
+ WARN_ON(!pll->info->funcs->get_hw_state(dev_priv, pll,
+ &pipe_config->dpll_hw_state));
}
/*
@@ -10775,7 +10831,7 @@ static bool check_digital_port_conflicts(struct drm_atomic_state *state)
struct drm_connector_state *connector_state;
struct intel_encoder *encoder;
- connector_state = drm_atomic_get_existing_connector_state(state, connector);
+ connector_state = drm_atomic_get_new_connector_state(state, connector);
if (!connector_state)
connector_state = connector->state;
@@ -11645,11 +11701,11 @@ verify_single_dpll_state(struct drm_i915_private *dev_priv,
memset(&dpll_hw_state, 0, sizeof(dpll_hw_state));
- DRM_DEBUG_KMS("%s\n", pll->name);
+ DRM_DEBUG_KMS("%s\n", pll->info->name);
- active = pll->funcs.get_hw_state(dev_priv, pll, &dpll_hw_state);
+ active = pll->info->funcs->get_hw_state(dev_priv, pll, &dpll_hw_state);
- if (!(pll->flags & INTEL_DPLL_ALWAYS_ON)) {
+ if (!(pll->info->flags & INTEL_DPLL_ALWAYS_ON)) {
I915_STATE_WARN(!pll->on && pll->active_mask,
"pll in active use but not on in sw tracking\n");
I915_STATE_WARN(pll->on && !pll->active_mask,
@@ -12138,20 +12194,23 @@ static void intel_update_crtc(struct drm_crtc *crtc,
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct intel_crtc_state *pipe_config = to_intel_crtc_state(new_crtc_state);
bool modeset = needs_modeset(new_crtc_state);
+ struct intel_plane_state *new_plane_state =
+ intel_atomic_get_new_plane_state(to_intel_atomic_state(state),
+ to_intel_plane(crtc->primary));
if (modeset) {
update_scanline_offset(intel_crtc);
dev_priv->display.crtc_enable(pipe_config, state);
+
+ /* vblanks work again, re-enable pipe CRC. */
+ intel_crtc_enable_pipe_crc(intel_crtc);
} else {
intel_pre_plane_update(to_intel_crtc_state(old_crtc_state),
pipe_config);
}
- if (drm_atomic_get_existing_plane_state(state, crtc->primary)) {
- intel_fbc_enable(
- intel_crtc, pipe_config,
- to_intel_plane_state(crtc->primary->state));
- }
+ if (new_plane_state)
+ intel_fbc_enable(intel_crtc, pipe_config, new_plane_state);
drm_atomic_helper_commit_planes_on_crtc(old_crtc_state);
}
@@ -12322,6 +12381,13 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
if (old_crtc_state->active) {
intel_crtc_disable_planes(crtc, old_crtc_state->plane_mask);
+
+ /*
+ * We need to disable pipe CRC before disabling the pipe,
+ * or we race against vblank off.
+ */
+ intel_crtc_disable_pipe_crc(intel_crtc);
+
dev_priv->display.crtc_disable(to_intel_crtc_state(old_crtc_state), state);
intel_crtc->active = false;
intel_fbc_disable(intel_crtc);
@@ -12725,8 +12791,8 @@ intel_prepare_plane_fb(struct drm_plane *plane,
if (old_obj) {
struct drm_crtc_state *crtc_state =
- drm_atomic_get_existing_crtc_state(new_state->state,
- plane->state->crtc);
+ drm_atomic_get_new_crtc_state(new_state->state,
+ plane->state->crtc);
/* Big Hammer, we also need to ensure that any pending
* MI_WAIT_FOR_EVENT inside a user batch buffer on the
@@ -12780,6 +12846,8 @@ intel_prepare_plane_fb(struct drm_plane *plane,
if (ret)
return ret;
+ intel_fb_obj_flush(obj, ORIGIN_DIRTYFB);
+
if (!new_state->fence) { /* implicit fencing */
struct dma_fence *fence;
@@ -12824,11 +12892,13 @@ intel_cleanup_plane_fb(struct drm_plane *plane,
}
int
-skl_max_scale(struct intel_crtc *intel_crtc, struct intel_crtc_state *crtc_state)
+skl_max_scale(struct intel_crtc *intel_crtc,
+ struct intel_crtc_state *crtc_state,
+ uint32_t pixel_format)
{
struct drm_i915_private *dev_priv;
- int max_scale;
- int crtc_clock, max_dotclk;
+ int max_scale, mult;
+ int crtc_clock, max_dotclk, tmpclk1, tmpclk2;
if (!intel_crtc || !crtc_state->base.enable)
return DRM_PLANE_HELPER_NO_SCALING;
@@ -12850,8 +12920,10 @@ skl_max_scale(struct intel_crtc *intel_crtc, struct intel_crtc_state *crtc_state
* or
* cdclk/crtc_clock
*/
- max_scale = min((1 << 16) * 3 - 1,
- (1 << 8) * ((max_dotclk << 8) / crtc_clock));
+ mult = pixel_format == DRM_FORMAT_NV12 ? 2 : 3;
+ tmpclk1 = (1 << 16) * mult - 1;
+ tmpclk2 = (1 << 8) * ((max_dotclk << 8) / crtc_clock);
+ max_scale = min(tmpclk1, tmpclk2);
return max_scale;
}
@@ -12867,12 +12939,16 @@ intel_check_primary_plane(struct intel_plane *plane,
int max_scale = DRM_PLANE_HELPER_NO_SCALING;
bool can_position = false;
int ret;
+ uint32_t pixel_format = 0;
if (INTEL_GEN(dev_priv) >= 9) {
/* use scaler when colorkey is not required */
if (!state->ckey.flags) {
min_scale = 1;
- max_scale = skl_max_scale(to_intel_crtc(crtc), crtc_state);
+ if (state->base.fb)
+ pixel_format = state->base.fb->format->format;
+ max_scale = skl_max_scale(to_intel_crtc(crtc),
+ crtc_state, pixel_format);
}
can_position = true;
}
@@ -12945,10 +13021,25 @@ out:
intel_cstate);
}
+void intel_crtc_arm_fifo_underrun(struct intel_crtc *crtc,
+ struct intel_crtc_state *crtc_state)
+{
+ struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+
+ if (!IS_GEN2(dev_priv))
+ intel_set_cpu_fifo_underrun_reporting(dev_priv, crtc->pipe, true);
+
+ if (crtc_state->has_pch_encoder) {
+ enum pipe pch_transcoder =
+ intel_crtc_pch_transcoder(crtc);
+
+ intel_set_pch_fifo_underrun_reporting(dev_priv, pch_transcoder, true);
+ }
+}
+
static void intel_finish_crtc_commit(struct drm_crtc *crtc,
struct drm_crtc_state *old_crtc_state)
{
- struct drm_i915_private *dev_priv = to_i915(crtc->dev);
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct intel_atomic_state *old_intel_state =
to_intel_atomic_state(old_crtc_state->state);
@@ -12959,17 +13050,8 @@ static void intel_finish_crtc_commit(struct drm_crtc *crtc,
if (new_crtc_state->update_pipe &&
!needs_modeset(&new_crtc_state->base) &&
- old_crtc_state->mode.private_flags & I915_MODE_FLAG_INHERITED) {
- if (!IS_GEN2(dev_priv))
- intel_set_cpu_fifo_underrun_reporting(dev_priv, intel_crtc->pipe, true);
-
- if (new_crtc_state->has_pch_encoder) {
- enum pipe pch_transcoder =
- intel_crtc_pch_transcoder(intel_crtc);
-
- intel_set_pch_fifo_underrun_reporting(dev_priv, pch_transcoder, true);
- }
- }
+ old_crtc_state->mode.private_flags & I915_MODE_FLAG_INHERITED)
+ intel_crtc_arm_fifo_underrun(intel_crtc, new_crtc_state);
}
/**
@@ -13167,8 +13249,9 @@ intel_legacy_cursor_update(struct drm_plane *plane,
if (ret)
goto out_unlock;
- old_fb = old_plane_state->fb;
+ intel_fb_obj_flush(intel_fb_obj(fb), ORIGIN_FLIP);
+ old_fb = old_plane_state->fb;
i915_gem_track_fb(intel_fb_obj(old_fb), intel_fb_obj(fb),
intel_plane->frontbuffer_bit);
@@ -13555,10 +13638,17 @@ static int intel_crtc_init(struct drm_i915_private *dev_priv, enum pipe pipe)
/* initialize shared scalers */
intel_crtc_init_scalers(intel_crtc, crtc_state);
- BUG_ON(pipe >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) ||
- dev_priv->plane_to_crtc_mapping[primary->i9xx_plane] != NULL);
- dev_priv->plane_to_crtc_mapping[primary->i9xx_plane] = intel_crtc;
- dev_priv->pipe_to_crtc_mapping[intel_crtc->pipe] = intel_crtc;
+ BUG_ON(pipe >= ARRAY_SIZE(dev_priv->pipe_to_crtc_mapping) ||
+ dev_priv->pipe_to_crtc_mapping[pipe] != NULL);
+ dev_priv->pipe_to_crtc_mapping[pipe] = intel_crtc;
+
+ if (INTEL_GEN(dev_priv) < 9) {
+ enum i9xx_plane_id i9xx_plane = primary->i9xx_plane;
+
+ BUG_ON(i9xx_plane >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) ||
+ dev_priv->plane_to_crtc_mapping[i9xx_plane] != NULL);
+ dev_priv->plane_to_crtc_mapping[i9xx_plane] = intel_crtc;
+ }
drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs);
@@ -15103,8 +15193,8 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
for (i = 0; i < dev_priv->num_shared_dpll; i++) {
struct intel_shared_dpll *pll = &dev_priv->shared_dplls[i];
- pll->on = pll->funcs.get_hw_state(dev_priv, pll,
- &pll->state.hw_state);
+ pll->on = pll->info->funcs->get_hw_state(dev_priv, pll,
+ &pll->state.hw_state);
pll->state.crtc_mask = 0;
for_each_intel_crtc(dev, crtc) {
struct intel_crtc_state *crtc_state =
@@ -15117,7 +15207,7 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
pll->active_mask = pll->state.crtc_mask;
DRM_DEBUG_KMS("%s hw state readout: crtc_mask 0x%08x, on %i\n",
- pll->name, pll->state.crtc_mask, pll->on);
+ pll->info->name, pll->state.crtc_mask, pll->on);
}
for_each_intel_encoder(dev, encoder) {
@@ -15291,9 +15381,10 @@ intel_modeset_setup_hw_state(struct drm_device *dev,
if (!pll->on || pll->active_mask)
continue;
- DRM_DEBUG_KMS("%s enabled but not in use, disabling\n", pll->name);
+ DRM_DEBUG_KMS("%s enabled but not in use, disabling\n",
+ pll->info->name);
- pll->funcs.disable(dev_priv, pll);
+ pll->info->funcs->disable(dev_priv, pll);
pll->on = false;
}
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 9a4a51e79fa1..62f82c4298ac 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -43,7 +43,6 @@
#include <drm/i915_drm.h>
#include "i915_drv.h"
-#define DP_LINK_CHECK_TIMEOUT (10 * 1000)
#define DP_DPRX_ESI_LEN 14
/* Compliance test status bits */
@@ -92,8 +91,6 @@ static const struct dp_link_dpll chv_dpll[] = {
{ .p1 = 4, .p2 = 2, .n = 1, .m1 = 2, .m2 = 0x819999a } },
{ 270000, /* m2_int = 27, m2_fraction = 0 */
{ .p1 = 4, .p2 = 1, .n = 1, .m1 = 2, .m2 = 0x6c00000 } },
- { 540000, /* m2_int = 27, m2_fraction = 0 */
- { .p1 = 2, .p2 = 1, .n = 1, .m1 = 2, .m2 = 0x6c00000 } }
};
/**
@@ -2901,10 +2898,7 @@ _intel_dp_set_link_train(struct intel_dp *intel_dp,
}
} else {
- if (IS_CHERRYVIEW(dev_priv))
- *DP &= ~DP_LINK_TRAIN_MASK_CHV;
- else
- *DP &= ~DP_LINK_TRAIN_MASK;
+ *DP &= ~DP_LINK_TRAIN_MASK;
switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) {
case DP_TRAINING_PATTERN_DISABLE:
@@ -2917,12 +2911,8 @@ _intel_dp_set_link_train(struct intel_dp *intel_dp,
*DP |= DP_LINK_TRAIN_PAT_2;
break;
case DP_TRAINING_PATTERN_3:
- if (IS_CHERRYVIEW(dev_priv)) {
- *DP |= DP_LINK_TRAIN_PAT_3_CHV;
- } else {
- DRM_DEBUG_KMS("TPS3 not supported, using TPS2 instead\n");
- *DP |= DP_LINK_TRAIN_PAT_2;
- }
+ DRM_DEBUG_KMS("TPS3 not supported, using TPS2 instead\n");
+ *DP |= DP_LINK_TRAIN_PAT_2;
break;
}
}
@@ -3661,10 +3651,7 @@ intel_dp_link_down(struct intel_encoder *encoder,
DP &= ~DP_LINK_TRAIN_MASK_CPT;
DP |= DP_LINK_TRAIN_PAT_IDLE_CPT;
} else {
- if (IS_CHERRYVIEW(dev_priv))
- DP &= ~DP_LINK_TRAIN_MASK_CHV;
- else
- DP &= ~DP_LINK_TRAIN_MASK;
+ DP &= ~DP_LINK_TRAIN_MASK;
DP |= DP_LINK_TRAIN_PAT_IDLE;
}
I915_WRITE(intel_dp->output_reg, DP);
diff --git a/drivers/gpu/drm/i915/intel_dp_mst.c b/drivers/gpu/drm/i915/intel_dp_mst.c
index c3de0918ee13..9e6956c08688 100644
--- a/drivers/gpu/drm/i915/intel_dp_mst.c
+++ b/drivers/gpu/drm/i915/intel_dp_mst.c
@@ -180,9 +180,11 @@ static void intel_mst_post_disable_dp(struct intel_encoder *encoder,
intel_dp->active_mst_links--;
intel_mst->connector = NULL;
- if (intel_dp->active_mst_links == 0)
+ if (intel_dp->active_mst_links == 0) {
+ intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_OFF);
intel_dig_port->base.post_disable(&intel_dig_port->base,
old_crtc_state, NULL);
+ }
DRM_DEBUG_KMS("active links %d\n", intel_dp->active_mst_links);
}
@@ -223,7 +225,11 @@ static void intel_mst_pre_enable_dp(struct intel_encoder *encoder,
DRM_DEBUG_KMS("active links %d\n", intel_dp->active_mst_links);
+ if (intel_dp->active_mst_links == 0)
+ intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
+
drm_dp_send_power_updown_phy(&intel_dp->mst_mgr, connector->port, true);
+
if (intel_dp->active_mst_links == 0)
intel_dig_port->base.pre_enable(&intel_dig_port->base,
pipe_config, NULL);
diff --git a/drivers/gpu/drm/i915/intel_dpio_phy.c b/drivers/gpu/drm/i915/intel_dpio_phy.c
index c8e9e44e5981..00b3ab656b06 100644
--- a/drivers/gpu/drm/i915/intel_dpio_phy.c
+++ b/drivers/gpu/drm/i915/intel_dpio_phy.c
@@ -380,13 +380,14 @@ static void _bxt_ddi_phy_init(struct drm_i915_private *dev_priv,
* all 1s. Eventually they become accessible as they power up, then
* the reserved bit will give the default 0. Poll on the reserved bit
* becoming 0 to find when the PHY is accessible.
- * HW team confirmed that the time to reach phypowergood status is
- * anywhere between 50 us and 100us.
+ * The flag should get set in 100us according to the HW team, but
+ * use 1ms due to occasional timeouts observed with that.
*/
- if (wait_for_us(((I915_READ(BXT_PORT_CL1CM_DW0(phy)) &
- (PHY_RESERVED | PHY_POWER_GOOD)) == PHY_POWER_GOOD), 100)) {
+ if (intel_wait_for_register_fw(dev_priv, BXT_PORT_CL1CM_DW0(phy),
+ PHY_RESERVED | PHY_POWER_GOOD,
+ PHY_POWER_GOOD,
+ 1))
DRM_ERROR("timeout during PHY%d power on\n", phy);
- }
/* Program PLL Rcomp code offset */
val = I915_READ(BXT_PORT_CL1CM_DW9(phy));
diff --git a/drivers/gpu/drm/i915/intel_dpll_mgr.c b/drivers/gpu/drm/i915/intel_dpll_mgr.c
index 51c5ae4e9116..d5e114e9660b 100644
--- a/drivers/gpu/drm/i915/intel_dpll_mgr.c
+++ b/drivers/gpu/drm/i915/intel_dpll_mgr.c
@@ -118,10 +118,10 @@ void assert_shared_dpll(struct drm_i915_private *dev_priv,
if (WARN(!pll, "asserting DPLL %s with no DPLL\n", onoff(state)))
return;
- cur_state = pll->funcs.get_hw_state(dev_priv, pll, &hw_state);
+ cur_state = pll->info->funcs->get_hw_state(dev_priv, pll, &hw_state);
I915_STATE_WARN(cur_state != state,
"%s assertion failure (expected %s, current %s)\n",
- pll->name, onoff(state), onoff(cur_state));
+ pll->info->name, onoff(state), onoff(cur_state));
}
/**
@@ -143,11 +143,11 @@ void intel_prepare_shared_dpll(struct intel_crtc *crtc)
mutex_lock(&dev_priv->dpll_lock);
WARN_ON(!pll->state.crtc_mask);
if (!pll->active_mask) {
- DRM_DEBUG_DRIVER("setting up %s\n", pll->name);
+ DRM_DEBUG_DRIVER("setting up %s\n", pll->info->name);
WARN_ON(pll->on);
assert_shared_dpll_disabled(dev_priv, pll);
- pll->funcs.prepare(dev_priv, pll);
+ pll->info->funcs->prepare(dev_priv, pll);
}
mutex_unlock(&dev_priv->dpll_lock);
}
@@ -179,7 +179,7 @@ void intel_enable_shared_dpll(struct intel_crtc *crtc)
pll->active_mask |= crtc_mask;
DRM_DEBUG_KMS("enable %s (active %x, on? %d) for crtc %d\n",
- pll->name, pll->active_mask, pll->on,
+ pll->info->name, pll->active_mask, pll->on,
crtc->base.base.id);
if (old_mask) {
@@ -189,8 +189,8 @@ void intel_enable_shared_dpll(struct intel_crtc *crtc)
}
WARN_ON(pll->on);
- DRM_DEBUG_KMS("enabling %s\n", pll->name);
- pll->funcs.enable(dev_priv, pll);
+ DRM_DEBUG_KMS("enabling %s\n", pll->info->name);
+ pll->info->funcs->enable(dev_priv, pll);
pll->on = true;
out:
@@ -221,7 +221,7 @@ void intel_disable_shared_dpll(struct intel_crtc *crtc)
goto out;
DRM_DEBUG_KMS("disable %s (active %x, on? %d) for crtc %d\n",
- pll->name, pll->active_mask, pll->on,
+ pll->info->name, pll->active_mask, pll->on,
crtc->base.base.id);
assert_shared_dpll_enabled(dev_priv, pll);
@@ -231,8 +231,8 @@ void intel_disable_shared_dpll(struct intel_crtc *crtc)
if (pll->active_mask)
goto out;
- DRM_DEBUG_KMS("disabling %s\n", pll->name);
- pll->funcs.disable(dev_priv, pll);
+ DRM_DEBUG_KMS("disabling %s\n", pll->info->name);
+ pll->info->funcs->disable(dev_priv, pll);
pll->on = false;
out:
@@ -263,7 +263,8 @@ intel_find_shared_dpll(struct intel_crtc *crtc,
&shared_dpll[i].hw_state,
sizeof(crtc_state->dpll_hw_state)) == 0) {
DRM_DEBUG_KMS("[CRTC:%d:%s] sharing existing %s (crtc mask 0x%08x, active %x)\n",
- crtc->base.base.id, crtc->base.name, pll->name,
+ crtc->base.base.id, crtc->base.name,
+ pll->info->name,
shared_dpll[i].crtc_mask,
pll->active_mask);
return pll;
@@ -275,7 +276,8 @@ intel_find_shared_dpll(struct intel_crtc *crtc,
pll = &dev_priv->shared_dplls[i];
if (shared_dpll[i].crtc_mask == 0) {
DRM_DEBUG_KMS("[CRTC:%d:%s] allocated %s\n",
- crtc->base.base.id, crtc->base.name, pll->name);
+ crtc->base.base.id, crtc->base.name,
+ pll->info->name);
return pll;
}
}
@@ -289,19 +291,19 @@ intel_reference_shared_dpll(struct intel_shared_dpll *pll,
{
struct intel_shared_dpll_state *shared_dpll;
struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
- enum intel_dpll_id i = pll->id;
+ const enum intel_dpll_id id = pll->info->id;
shared_dpll = intel_atomic_get_shared_dpll_state(crtc_state->base.state);
- if (shared_dpll[i].crtc_mask == 0)
- shared_dpll[i].hw_state =
+ if (shared_dpll[id].crtc_mask == 0)
+ shared_dpll[id].hw_state =
crtc_state->dpll_hw_state;
crtc_state->shared_dpll = pll;
- DRM_DEBUG_DRIVER("using %s for pipe %c\n", pll->name,
+ DRM_DEBUG_DRIVER("using %s for pipe %c\n", pll->info->name,
pipe_name(crtc->pipe));
- shared_dpll[pll->id].crtc_mask |= 1 << crtc->pipe;
+ shared_dpll[id].crtc_mask |= 1 << crtc->pipe;
}
/**
@@ -341,15 +343,16 @@ static bool ibx_pch_dpll_get_hw_state(struct drm_i915_private *dev_priv,
struct intel_shared_dpll *pll,
struct intel_dpll_hw_state *hw_state)
{
+ const enum intel_dpll_id id = pll->info->id;
uint32_t val;
if (!intel_display_power_get_if_enabled(dev_priv, POWER_DOMAIN_PLLS))
return false;
- val = I915_READ(PCH_DPLL(pll->id));
+ val = I915_READ(PCH_DPLL(id));
hw_state->dpll = val;
- hw_state->fp0 = I915_READ(PCH_FP0(pll->id));
- hw_state->fp1 = I915_READ(PCH_FP1(pll->id));
+ hw_state->fp0 = I915_READ(PCH_FP0(id));
+ hw_state->fp1 = I915_READ(PCH_FP1(id));
intel_display_power_put(dev_priv, POWER_DOMAIN_PLLS);
@@ -359,8 +362,10 @@ static bool ibx_pch_dpll_get_hw_state(struct drm_i915_private *dev_priv,
static void ibx_pch_dpll_prepare(struct drm_i915_private *dev_priv,
struct intel_shared_dpll *pll)
{
- I915_WRITE(PCH_FP0(pll->id), pll->state.hw_state.fp0);
- I915_WRITE(PCH_FP1(pll->id), pll->state.hw_state.fp1);
+ const enum intel_dpll_id id = pll->info->id;
+
+ I915_WRITE(PCH_FP0(id), pll->state.hw_state.fp0);
+ I915_WRITE(PCH_FP1(id), pll->state.hw_state.fp1);
}
static void ibx_assert_pch_refclk_enabled(struct drm_i915_private *dev_priv)
@@ -379,13 +384,15 @@ static void ibx_assert_pch_refclk_enabled(struct drm_i915_private *dev_priv)
static void ibx_pch_dpll_enable(struct drm_i915_private *dev_priv,
struct intel_shared_dpll *pll)
{
+ const enum intel_dpll_id id = pll->info->id;
+
/* PCH refclock must be enabled first */
ibx_assert_pch_refclk_enabled(dev_priv);
- I915_WRITE(PCH_DPLL(pll->id), pll->state.hw_state.dpll);
+ I915_WRITE(PCH_DPLL(id), pll->state.hw_state.dpll);
/* Wait for the clocks to stabilize. */
- POSTING_READ(PCH_DPLL(pll->id));
+ POSTING_READ(PCH_DPLL(id));
udelay(150);
/* The pixel multiplier can only be updated once the
@@ -393,14 +400,15 @@ static void ibx_pch_dpll_enable(struct drm_i915_private *dev_priv,
*
* So write it again.
*/
- I915_WRITE(PCH_DPLL(pll->id), pll->state.hw_state.dpll);
- POSTING_READ(PCH_DPLL(pll->id));
+ I915_WRITE(PCH_DPLL(id), pll->state.hw_state.dpll);
+ POSTING_READ(PCH_DPLL(id));
udelay(200);
}
static void ibx_pch_dpll_disable(struct drm_i915_private *dev_priv,
struct intel_shared_dpll *pll)
{
+ const enum intel_dpll_id id = pll->info->id;
struct drm_device *dev = &dev_priv->drm;
struct intel_crtc *crtc;
@@ -410,8 +418,8 @@ static void ibx_pch_dpll_disable(struct drm_i915_private *dev_priv,
assert_pch_transcoder_disabled(dev_priv, crtc->pipe);
}
- I915_WRITE(PCH_DPLL(pll->id), 0);
- POSTING_READ(PCH_DPLL(pll->id));
+ I915_WRITE(PCH_DPLL(id), 0);
+ POSTING_READ(PCH_DPLL(id));
udelay(200);
}
@@ -429,7 +437,8 @@ ibx_get_dpll(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state,
pll = &dev_priv->shared_dplls[i];
DRM_DEBUG_KMS("[CRTC:%d:%s] using pre-allocated %s\n",
- crtc->base.base.id, crtc->base.name, pll->name);
+ crtc->base.base.id, crtc->base.name,
+ pll->info->name);
} else {
pll = intel_find_shared_dpll(crtc, crtc_state,
DPLL_ID_PCH_PLL_A,
@@ -466,8 +475,10 @@ static const struct intel_shared_dpll_funcs ibx_pch_dpll_funcs = {
static void hsw_ddi_wrpll_enable(struct drm_i915_private *dev_priv,
struct intel_shared_dpll *pll)
{
- I915_WRITE(WRPLL_CTL(pll->id), pll->state.hw_state.wrpll);
- POSTING_READ(WRPLL_CTL(pll->id));
+ const enum intel_dpll_id id = pll->info->id;
+
+ I915_WRITE(WRPLL_CTL(id), pll->state.hw_state.wrpll);
+ POSTING_READ(WRPLL_CTL(id));
udelay(20);
}
@@ -482,11 +493,12 @@ static void hsw_ddi_spll_enable(struct drm_i915_private *dev_priv,
static void hsw_ddi_wrpll_disable(struct drm_i915_private *dev_priv,
struct intel_shared_dpll *pll)
{
+ const enum intel_dpll_id id = pll->info->id;
uint32_t val;
- val = I915_READ(WRPLL_CTL(pll->id));
- I915_WRITE(WRPLL_CTL(pll->id), val & ~WRPLL_PLL_ENABLE);
- POSTING_READ(WRPLL_CTL(pll->id));
+ val = I915_READ(WRPLL_CTL(id));
+ I915_WRITE(WRPLL_CTL(id), val & ~WRPLL_PLL_ENABLE);
+ POSTING_READ(WRPLL_CTL(id));
}
static void hsw_ddi_spll_disable(struct drm_i915_private *dev_priv,
@@ -503,12 +515,13 @@ static bool hsw_ddi_wrpll_get_hw_state(struct drm_i915_private *dev_priv,
struct intel_shared_dpll *pll,
struct intel_dpll_hw_state *hw_state)
{
+ const enum intel_dpll_id id = pll->info->id;
uint32_t val;
if (!intel_display_power_get_if_enabled(dev_priv, POWER_DOMAIN_PLLS))
return false;
- val = I915_READ(WRPLL_CTL(pll->id));
+ val = I915_READ(WRPLL_CTL(id));
hw_state->wrpll = val;
intel_display_power_put(dev_priv, POWER_DOMAIN_PLLS);
@@ -914,13 +927,15 @@ static const struct skl_dpll_regs skl_dpll_regs[4] = {
static void skl_ddi_pll_write_ctrl1(struct drm_i915_private *dev_priv,
struct intel_shared_dpll *pll)
{
+ const enum intel_dpll_id id = pll->info->id;
uint32_t val;
val = I915_READ(DPLL_CTRL1);
- val &= ~(DPLL_CTRL1_HDMI_MODE(pll->id) | DPLL_CTRL1_SSC(pll->id) |
- DPLL_CTRL1_LINK_RATE_MASK(pll->id));
- val |= pll->state.hw_state.ctrl1 << (pll->id * 6);
+ val &= ~(DPLL_CTRL1_HDMI_MODE(id) |
+ DPLL_CTRL1_SSC(id) |
+ DPLL_CTRL1_LINK_RATE_MASK(id));
+ val |= pll->state.hw_state.ctrl1 << (id * 6);
I915_WRITE(DPLL_CTRL1, val);
POSTING_READ(DPLL_CTRL1);
@@ -930,24 +945,25 @@ static void skl_ddi_pll_enable(struct drm_i915_private *dev_priv,
struct intel_shared_dpll *pll)
{
const struct skl_dpll_regs *regs = skl_dpll_regs;
+ const enum intel_dpll_id id = pll->info->id;
skl_ddi_pll_write_ctrl1(dev_priv, pll);
- I915_WRITE(regs[pll->id].cfgcr1, pll->state.hw_state.cfgcr1);
- I915_WRITE(regs[pll->id].cfgcr2, pll->state.hw_state.cfgcr2);
- POSTING_READ(regs[pll->id].cfgcr1);
- POSTING_READ(regs[pll->id].cfgcr2);
+ I915_WRITE(regs[id].cfgcr1, pll->state.hw_state.cfgcr1);
+ I915_WRITE(regs[id].cfgcr2, pll->state.hw_state.cfgcr2);
+ POSTING_READ(regs[id].cfgcr1);
+ POSTING_READ(regs[id].cfgcr2);
/* the enable bit is always bit 31 */
- I915_WRITE(regs[pll->id].ctl,
- I915_READ(regs[pll->id].ctl) | LCPLL_PLL_ENABLE);
+ I915_WRITE(regs[id].ctl,
+ I915_READ(regs[id].ctl) | LCPLL_PLL_ENABLE);
if (intel_wait_for_register(dev_priv,
DPLL_STATUS,
- DPLL_LOCK(pll->id),
- DPLL_LOCK(pll->id),
+ DPLL_LOCK(id),
+ DPLL_LOCK(id),
5))
- DRM_ERROR("DPLL %d not locked\n", pll->id);
+ DRM_ERROR("DPLL %d not locked\n", id);
}
static void skl_ddi_dpll0_enable(struct drm_i915_private *dev_priv,
@@ -960,11 +976,12 @@ static void skl_ddi_pll_disable(struct drm_i915_private *dev_priv,
struct intel_shared_dpll *pll)
{
const struct skl_dpll_regs *regs = skl_dpll_regs;
+ const enum intel_dpll_id id = pll->info->id;
/* the enable bit is always bit 31 */
- I915_WRITE(regs[pll->id].ctl,
- I915_READ(regs[pll->id].ctl) & ~LCPLL_PLL_ENABLE);
- POSTING_READ(regs[pll->id].ctl);
+ I915_WRITE(regs[id].ctl,
+ I915_READ(regs[id].ctl) & ~LCPLL_PLL_ENABLE);
+ POSTING_READ(regs[id].ctl);
}
static void skl_ddi_dpll0_disable(struct drm_i915_private *dev_priv,
@@ -978,6 +995,7 @@ static bool skl_ddi_pll_get_hw_state(struct drm_i915_private *dev_priv,
{
uint32_t val;
const struct skl_dpll_regs *regs = skl_dpll_regs;
+ const enum intel_dpll_id id = pll->info->id;
bool ret;
if (!intel_display_power_get_if_enabled(dev_priv, POWER_DOMAIN_PLLS))
@@ -985,17 +1003,17 @@ static bool skl_ddi_pll_get_hw_state(struct drm_i915_private *dev_priv,
ret = false;
- val = I915_READ(regs[pll->id].ctl);
+ val = I915_READ(regs[id].ctl);
if (!(val & LCPLL_PLL_ENABLE))
goto out;
val = I915_READ(DPLL_CTRL1);
- hw_state->ctrl1 = (val >> (pll->id * 6)) & 0x3f;
+ hw_state->ctrl1 = (val >> (id * 6)) & 0x3f;
/* avoid reading back stale values if HDMI mode is not enabled */
- if (val & DPLL_CTRL1_HDMI_MODE(pll->id)) {
- hw_state->cfgcr1 = I915_READ(regs[pll->id].cfgcr1);
- hw_state->cfgcr2 = I915_READ(regs[pll->id].cfgcr2);
+ if (val & DPLL_CTRL1_HDMI_MODE(id)) {
+ hw_state->cfgcr1 = I915_READ(regs[id].cfgcr1);
+ hw_state->cfgcr2 = I915_READ(regs[id].cfgcr2);
}
ret = true;
@@ -1011,6 +1029,7 @@ static bool skl_ddi_dpll0_get_hw_state(struct drm_i915_private *dev_priv,
{
uint32_t val;
const struct skl_dpll_regs *regs = skl_dpll_regs;
+ const enum intel_dpll_id id = pll->info->id;
bool ret;
if (!intel_display_power_get_if_enabled(dev_priv, POWER_DOMAIN_PLLS))
@@ -1019,12 +1038,12 @@ static bool skl_ddi_dpll0_get_hw_state(struct drm_i915_private *dev_priv,
ret = false;
/* DPLL0 is always enabled since it drives CDCLK */
- val = I915_READ(regs[pll->id].ctl);
+ val = I915_READ(regs[id].ctl);
if (WARN_ON(!(val & LCPLL_PLL_ENABLE)))
goto out;
val = I915_READ(DPLL_CTRL1);
- hw_state->ctrl1 = (val >> (pll->id * 6)) & 0x3f;
+ hw_state->ctrl1 = (val >> (id * 6)) & 0x3f;
ret = true;
@@ -1424,7 +1443,7 @@ static void bxt_ddi_pll_enable(struct drm_i915_private *dev_priv,
struct intel_shared_dpll *pll)
{
uint32_t temp;
- enum port port = (enum port)pll->id; /* 1:1 port->PLL mapping */
+ enum port port = (enum port)pll->info->id; /* 1:1 port->PLL mapping */
enum dpio_phy phy;
enum dpio_channel ch;
@@ -1543,7 +1562,7 @@ static void bxt_ddi_pll_enable(struct drm_i915_private *dev_priv,
static void bxt_ddi_pll_disable(struct drm_i915_private *dev_priv,
struct intel_shared_dpll *pll)
{
- enum port port = (enum port)pll->id; /* 1:1 port->PLL mapping */
+ enum port port = (enum port)pll->info->id; /* 1:1 port->PLL mapping */
uint32_t temp;
temp = I915_READ(BXT_PORT_PLL_ENABLE(port));
@@ -1566,7 +1585,7 @@ static bool bxt_ddi_pll_get_hw_state(struct drm_i915_private *dev_priv,
struct intel_shared_dpll *pll,
struct intel_dpll_hw_state *hw_state)
{
- enum port port = (enum port)pll->id; /* 1:1 port->PLL mapping */
+ enum port port = (enum port)pll->info->id; /* 1:1 port->PLL mapping */
uint32_t val;
bool ret;
enum dpio_phy phy;
@@ -1824,7 +1843,7 @@ bxt_get_dpll(struct intel_crtc *crtc,
pll = intel_get_shared_dpll_by_id(dev_priv, i);
DRM_DEBUG_KMS("[CRTC:%d:%s] using pre-allocated %s\n",
- crtc->base.base.id, crtc->base.name, pll->name);
+ crtc->base.base.id, crtc->base.name, pll->info->name);
intel_reference_shared_dpll(pll, crtc_state);
@@ -1877,13 +1896,6 @@ static void intel_ddi_pll_init(struct drm_device *dev)
}
}
-struct dpll_info {
- const char *name;
- const int id;
- const struct intel_shared_dpll_funcs *funcs;
- uint32_t flags;
-};
-
struct intel_dpll_mgr {
const struct dpll_info *dpll_info;
@@ -1896,9 +1908,9 @@ struct intel_dpll_mgr {
};
static const struct dpll_info pch_plls[] = {
- { "PCH DPLL A", DPLL_ID_PCH_PLL_A, &ibx_pch_dpll_funcs, 0 },
- { "PCH DPLL B", DPLL_ID_PCH_PLL_B, &ibx_pch_dpll_funcs, 0 },
- { NULL, -1, NULL, 0 },
+ { "PCH DPLL A", &ibx_pch_dpll_funcs, DPLL_ID_PCH_PLL_A, 0 },
+ { "PCH DPLL B", &ibx_pch_dpll_funcs, DPLL_ID_PCH_PLL_B, 0 },
+ { },
};
static const struct intel_dpll_mgr pch_pll_mgr = {
@@ -1908,13 +1920,13 @@ static const struct intel_dpll_mgr pch_pll_mgr = {
};
static const struct dpll_info hsw_plls[] = {
- { "WRPLL 1", DPLL_ID_WRPLL1, &hsw_ddi_wrpll_funcs, 0 },
- { "WRPLL 2", DPLL_ID_WRPLL2, &hsw_ddi_wrpll_funcs, 0 },
- { "SPLL", DPLL_ID_SPLL, &hsw_ddi_spll_funcs, 0 },
- { "LCPLL 810", DPLL_ID_LCPLL_810, &hsw_ddi_lcpll_funcs, INTEL_DPLL_ALWAYS_ON },
- { "LCPLL 1350", DPLL_ID_LCPLL_1350, &hsw_ddi_lcpll_funcs, INTEL_DPLL_ALWAYS_ON },
- { "LCPLL 2700", DPLL_ID_LCPLL_2700, &hsw_ddi_lcpll_funcs, INTEL_DPLL_ALWAYS_ON },
- { NULL, -1, NULL, },
+ { "WRPLL 1", &hsw_ddi_wrpll_funcs, DPLL_ID_WRPLL1, 0 },
+ { "WRPLL 2", &hsw_ddi_wrpll_funcs, DPLL_ID_WRPLL2, 0 },
+ { "SPLL", &hsw_ddi_spll_funcs, DPLL_ID_SPLL, 0 },
+ { "LCPLL 810", &hsw_ddi_lcpll_funcs, DPLL_ID_LCPLL_810, INTEL_DPLL_ALWAYS_ON },
+ { "LCPLL 1350", &hsw_ddi_lcpll_funcs, DPLL_ID_LCPLL_1350, INTEL_DPLL_ALWAYS_ON },
+ { "LCPLL 2700", &hsw_ddi_lcpll_funcs, DPLL_ID_LCPLL_2700, INTEL_DPLL_ALWAYS_ON },
+ { },
};
static const struct intel_dpll_mgr hsw_pll_mgr = {
@@ -1924,11 +1936,11 @@ static const struct intel_dpll_mgr hsw_pll_mgr = {
};
static const struct dpll_info skl_plls[] = {
- { "DPLL 0", DPLL_ID_SKL_DPLL0, &skl_ddi_dpll0_funcs, INTEL_DPLL_ALWAYS_ON },
- { "DPLL 1", DPLL_ID_SKL_DPLL1, &skl_ddi_pll_funcs, 0 },
- { "DPLL 2", DPLL_ID_SKL_DPLL2, &skl_ddi_pll_funcs, 0 },
- { "DPLL 3", DPLL_ID_SKL_DPLL3, &skl_ddi_pll_funcs, 0 },
- { NULL, -1, NULL, },
+ { "DPLL 0", &skl_ddi_dpll0_funcs, DPLL_ID_SKL_DPLL0, INTEL_DPLL_ALWAYS_ON },
+ { "DPLL 1", &skl_ddi_pll_funcs, DPLL_ID_SKL_DPLL1, 0 },
+ { "DPLL 2", &skl_ddi_pll_funcs, DPLL_ID_SKL_DPLL2, 0 },
+ { "DPLL 3", &skl_ddi_pll_funcs, DPLL_ID_SKL_DPLL3, 0 },
+ { },
};
static const struct intel_dpll_mgr skl_pll_mgr = {
@@ -1938,10 +1950,10 @@ static const struct intel_dpll_mgr skl_pll_mgr = {
};
static const struct dpll_info bxt_plls[] = {
- { "PORT PLL A", DPLL_ID_SKL_DPLL0, &bxt_ddi_pll_funcs, 0 },
- { "PORT PLL B", DPLL_ID_SKL_DPLL1, &bxt_ddi_pll_funcs, 0 },
- { "PORT PLL C", DPLL_ID_SKL_DPLL2, &bxt_ddi_pll_funcs, 0 },
- { NULL, -1, NULL, },
+ { "PORT PLL A", &bxt_ddi_pll_funcs, DPLL_ID_SKL_DPLL0, 0 },
+ { "PORT PLL B", &bxt_ddi_pll_funcs, DPLL_ID_SKL_DPLL1, 0 },
+ { "PORT PLL C", &bxt_ddi_pll_funcs, DPLL_ID_SKL_DPLL2, 0 },
+ { },
};
static const struct intel_dpll_mgr bxt_pll_mgr = {
@@ -1953,38 +1965,39 @@ static const struct intel_dpll_mgr bxt_pll_mgr = {
static void cnl_ddi_pll_enable(struct drm_i915_private *dev_priv,
struct intel_shared_dpll *pll)
{
+ const enum intel_dpll_id id = pll->info->id;
uint32_t val;
/* 1. Enable DPLL power in DPLL_ENABLE. */
- val = I915_READ(CNL_DPLL_ENABLE(pll->id));
+ val = I915_READ(CNL_DPLL_ENABLE(id));
val |= PLL_POWER_ENABLE;
- I915_WRITE(CNL_DPLL_ENABLE(pll->id), val);
+ I915_WRITE(CNL_DPLL_ENABLE(id), val);
/* 2. Wait for DPLL power state enabled in DPLL_ENABLE. */
if (intel_wait_for_register(dev_priv,
- CNL_DPLL_ENABLE(pll->id),
+ CNL_DPLL_ENABLE(id),
PLL_POWER_STATE,
PLL_POWER_STATE,
5))
- DRM_ERROR("PLL %d Power not enabled\n", pll->id);
+ DRM_ERROR("PLL %d Power not enabled\n", id);
/*
* 3. Configure DPLL_CFGCR0 to set SSC enable/disable,
* select DP mode, and set DP link rate.
*/
val = pll->state.hw_state.cfgcr0;
- I915_WRITE(CNL_DPLL_CFGCR0(pll->id), val);
+ I915_WRITE(CNL_DPLL_CFGCR0(id), val);
/* 4. Reab back to ensure writes completed */
- POSTING_READ(CNL_DPLL_CFGCR0(pll->id));
+ POSTING_READ(CNL_DPLL_CFGCR0(id));
/* 3. Configure DPLL_CFGCR0 */
/* Avoid touch CFGCR1 if HDMI mode is not enabled */
if (pll->state.hw_state.cfgcr0 & DPLL_CFGCR0_HDMI_MODE) {
val = pll->state.hw_state.cfgcr1;
- I915_WRITE(CNL_DPLL_CFGCR1(pll->id), val);
+ I915_WRITE(CNL_DPLL_CFGCR1(id), val);
/* 4. Reab back to ensure writes completed */
- POSTING_READ(CNL_DPLL_CFGCR1(pll->id));
+ POSTING_READ(CNL_DPLL_CFGCR1(id));
}
/*
@@ -1997,17 +2010,17 @@ static void cnl_ddi_pll_enable(struct drm_i915_private *dev_priv,
*/
/* 6. Enable DPLL in DPLL_ENABLE. */
- val = I915_READ(CNL_DPLL_ENABLE(pll->id));
+ val = I915_READ(CNL_DPLL_ENABLE(id));
val |= PLL_ENABLE;
- I915_WRITE(CNL_DPLL_ENABLE(pll->id), val);
+ I915_WRITE(CNL_DPLL_ENABLE(id), val);
/* 7. Wait for PLL lock status in DPLL_ENABLE. */
if (intel_wait_for_register(dev_priv,
- CNL_DPLL_ENABLE(pll->id),
+ CNL_DPLL_ENABLE(id),
PLL_LOCK,
PLL_LOCK,
5))
- DRM_ERROR("PLL %d not locked\n", pll->id);
+ DRM_ERROR("PLL %d not locked\n", id);
/*
* 8. If the frequency will result in a change to the voltage
@@ -2027,6 +2040,7 @@ static void cnl_ddi_pll_enable(struct drm_i915_private *dev_priv,
static void cnl_ddi_pll_disable(struct drm_i915_private *dev_priv,
struct intel_shared_dpll *pll)
{
+ const enum intel_dpll_id id = pll->info->id;
uint32_t val;
/*
@@ -2044,17 +2058,17 @@ static void cnl_ddi_pll_disable(struct drm_i915_private *dev_priv,
*/
/* 3. Disable DPLL through DPLL_ENABLE. */
- val = I915_READ(CNL_DPLL_ENABLE(pll->id));
+ val = I915_READ(CNL_DPLL_ENABLE(id));
val &= ~PLL_ENABLE;
- I915_WRITE(CNL_DPLL_ENABLE(pll->id), val);
+ I915_WRITE(CNL_DPLL_ENABLE(id), val);
/* 4. Wait for PLL not locked status in DPLL_ENABLE. */
if (intel_wait_for_register(dev_priv,
- CNL_DPLL_ENABLE(pll->id),
+ CNL_DPLL_ENABLE(id),
PLL_LOCK,
0,
5))
- DRM_ERROR("PLL %d locked\n", pll->id);
+ DRM_ERROR("PLL %d locked\n", id);
/*
* 5. If the frequency will result in a change to the voltage
@@ -2066,23 +2080,24 @@ static void cnl_ddi_pll_disable(struct drm_i915_private *dev_priv,
*/
/* 6. Disable DPLL power in DPLL_ENABLE. */
- val = I915_READ(CNL_DPLL_ENABLE(pll->id));
+ val = I915_READ(CNL_DPLL_ENABLE(id));
val &= ~PLL_POWER_ENABLE;
- I915_WRITE(CNL_DPLL_ENABLE(pll->id), val);
+ I915_WRITE(CNL_DPLL_ENABLE(id), val);
/* 7. Wait for DPLL power state disabled in DPLL_ENABLE. */
if (intel_wait_for_register(dev_priv,
- CNL_DPLL_ENABLE(pll->id),
+ CNL_DPLL_ENABLE(id),
PLL_POWER_STATE,
0,
5))
- DRM_ERROR("PLL %d Power not disabled\n", pll->id);
+ DRM_ERROR("PLL %d Power not disabled\n", id);
}
static bool cnl_ddi_pll_get_hw_state(struct drm_i915_private *dev_priv,
struct intel_shared_dpll *pll,
struct intel_dpll_hw_state *hw_state)
{
+ const enum intel_dpll_id id = pll->info->id;
uint32_t val;
bool ret;
@@ -2091,16 +2106,16 @@ static bool cnl_ddi_pll_get_hw_state(struct drm_i915_private *dev_priv,
ret = false;
- val = I915_READ(CNL_DPLL_ENABLE(pll->id));
+ val = I915_READ(CNL_DPLL_ENABLE(id));
if (!(val & PLL_ENABLE))
goto out;
- val = I915_READ(CNL_DPLL_CFGCR0(pll->id));
+ val = I915_READ(CNL_DPLL_CFGCR0(id));
hw_state->cfgcr0 = val;
/* avoid reading back stale values if HDMI mode is not enabled */
if (val & DPLL_CFGCR0_HDMI_MODE) {
- hw_state->cfgcr1 = I915_READ(CNL_DPLL_CFGCR1(pll->id));
+ hw_state->cfgcr1 = I915_READ(CNL_DPLL_CFGCR1(id));
}
ret = true;
@@ -2372,10 +2387,10 @@ static const struct intel_shared_dpll_funcs cnl_ddi_pll_funcs = {
};
static const struct dpll_info cnl_plls[] = {
- { "DPLL 0", DPLL_ID_SKL_DPLL0, &cnl_ddi_pll_funcs, 0 },
- { "DPLL 1", DPLL_ID_SKL_DPLL1, &cnl_ddi_pll_funcs, 0 },
- { "DPLL 2", DPLL_ID_SKL_DPLL2, &cnl_ddi_pll_funcs, 0 },
- { NULL, -1, NULL, },
+ { "DPLL 0", &cnl_ddi_pll_funcs, DPLL_ID_SKL_DPLL0, 0 },
+ { "DPLL 1", &cnl_ddi_pll_funcs, DPLL_ID_SKL_DPLL1, 0 },
+ { "DPLL 2", &cnl_ddi_pll_funcs, DPLL_ID_SKL_DPLL2, 0 },
+ { },
};
static const struct intel_dpll_mgr cnl_pll_mgr = {
@@ -2415,13 +2430,9 @@ void intel_shared_dpll_init(struct drm_device *dev)
dpll_info = dpll_mgr->dpll_info;
- for (i = 0; dpll_info[i].id >= 0; i++) {
+ for (i = 0; dpll_info[i].name; i++) {
WARN_ON(i != dpll_info[i].id);
-
- dev_priv->shared_dplls[i].id = dpll_info[i].id;
- dev_priv->shared_dplls[i].name = dpll_info[i].name;
- dev_priv->shared_dplls[i].funcs = *dpll_info[i].funcs;
- dev_priv->shared_dplls[i].flags = dpll_info[i].flags;
+ dev_priv->shared_dplls[i].info = &dpll_info[i];
}
dev_priv->dpll_mgr = dpll_mgr;
@@ -2481,7 +2492,7 @@ void intel_release_shared_dpll(struct intel_shared_dpll *dpll,
struct intel_shared_dpll_state *shared_dpll_state;
shared_dpll_state = intel_atomic_get_shared_dpll_state(state);
- shared_dpll_state[dpll->id].crtc_mask &= ~(1 << crtc->pipe);
+ shared_dpll_state[dpll->info->id].crtc_mask &= ~(1 << crtc->pipe);
}
/**
diff --git a/drivers/gpu/drm/i915/intel_dpll_mgr.h b/drivers/gpu/drm/i915/intel_dpll_mgr.h
index f24ccf443d25..4febfaa90bde 100644
--- a/drivers/gpu/drm/i915/intel_dpll_mgr.h
+++ b/drivers/gpu/drm/i915/intel_dpll_mgr.h
@@ -206,6 +206,37 @@ struct intel_shared_dpll_funcs {
};
/**
+ * struct dpll_info - display PLL platform specific info
+ */
+struct dpll_info {
+ /**
+ * @name: DPLL name; used for logging
+ */
+ const char *name;
+
+ /**
+ * @funcs: platform specific hooks
+ */
+ const struct intel_shared_dpll_funcs *funcs;
+
+ /**
+ * @id: unique indentifier for this DPLL; should match the index in the
+ * dev_priv->shared_dplls array
+ */
+ enum intel_dpll_id id;
+
+#define INTEL_DPLL_ALWAYS_ON (1 << 0)
+ /**
+ * @flags:
+ *
+ * INTEL_DPLL_ALWAYS_ON
+ * Inform the state checker that the DPLL is kept enabled even if
+ * not in use by any CRTC.
+ */
+ uint32_t flags;
+};
+
+/**
* struct intel_shared_dpll - display PLL with tracked state and users
*/
struct intel_shared_dpll {
@@ -228,30 +259,9 @@ struct intel_shared_dpll {
bool on;
/**
- * @name: DPLL name; used for logging
+ * @info: platform specific info
*/
- const char *name;
-
- /**
- * @id: unique indentifier for this DPLL; should match the index in the
- * dev_priv->shared_dplls array
- */
- enum intel_dpll_id id;
-
- /**
- * @funcs: platform specific hooks
- */
- struct intel_shared_dpll_funcs funcs;
-
-#define INTEL_DPLL_ALWAYS_ON (1 << 0)
- /**
- * @flags:
- *
- * INTEL_DPLL_ALWAYS_ON
- * Inform the state checker that the DPLL is kept enabled even if
- * not in use by any CRTC.
- */
- uint32_t flags;
+ const struct dpll_info *info;
};
#define SKL_DPLL0 0
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index d4368589b355..6bd7bff0947a 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -49,12 +49,12 @@
* check the condition before the timeout.
*/
#define __wait_for(OP, COND, US, Wmin, Wmax) ({ \
- unsigned long timeout__ = jiffies + usecs_to_jiffies(US) + 1; \
+ const ktime_t end__ = ktime_add_ns(ktime_get_raw(), 1000ll * (US)); \
long wait__ = (Wmin); /* recommended min for usleep is 10 us */ \
int ret__; \
might_sleep(); \
for (;;) { \
- bool expired__ = time_after(jiffies, timeout__); \
+ const bool expired__ = ktime_after(ktime_get_raw(), end__); \
OP; \
if (COND) { \
ret__ = 0; \
@@ -482,7 +482,7 @@ struct intel_atomic_state {
bool skip_intermediate_wm;
/* Gen9+ only */
- struct skl_wm_values wm_results;
+ struct skl_ddb_values wm_results;
struct i915_sw_fence commit_ready;
@@ -548,6 +548,12 @@ struct intel_initial_plane_config {
#define SKL_MAX_DST_W 4096
#define SKL_MIN_DST_H 8
#define SKL_MAX_DST_H 4096
+#define ICL_MAX_SRC_W 5120
+#define ICL_MAX_SRC_H 4096
+#define ICL_MAX_DST_W 5120
+#define ICL_MAX_DST_H 4096
+#define SKL_MIN_YUV_420_SRC_W 16
+#define SKL_MIN_YUV_420_SRC_H 16
struct intel_scaler {
int in_use;
@@ -598,7 +604,9 @@ struct intel_pipe_wm {
struct skl_plane_wm {
struct skl_wm_level wm[8];
+ struct skl_wm_level uv_wm[8];
struct skl_wm_level trans_wm;
+ bool is_planar;
};
struct skl_pipe_wm {
@@ -1325,6 +1333,7 @@ void gen5_enable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask);
void gen5_disable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask);
void gen6_mask_pm_irq(struct drm_i915_private *dev_priv, u32 mask);
void gen6_unmask_pm_irq(struct drm_i915_private *dev_priv, u32 mask);
+void gen11_reset_rps_interrupts(struct drm_i915_private *dev_priv);
void gen6_reset_rps_interrupts(struct drm_i915_private *dev_priv);
void gen6_enable_rps_interrupts(struct drm_i915_private *dev_priv);
void gen6_disable_rps_interrupts(struct drm_i915_private *dev_priv);
@@ -1588,9 +1597,12 @@ void hsw_disable_ips(const struct intel_crtc_state *crtc_state);
enum intel_display_power_domain intel_port_to_power_domain(enum port port);
void intel_mode_from_pipe_config(struct drm_display_mode *mode,
struct intel_crtc_state *pipe_config);
+void intel_crtc_arm_fifo_underrun(struct intel_crtc *crtc,
+ struct intel_crtc_state *crtc_state);
int skl_update_scaler_crtc(struct intel_crtc_state *crtc_state);
-int skl_max_scale(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state);
+int skl_max_scale(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state,
+ uint32_t pixel_format);
static inline u32 intel_plane_ggtt_offset(const struct intel_plane_state *state)
{
@@ -1607,6 +1619,7 @@ u32 skl_plane_stride(const struct drm_framebuffer *fb, int plane,
int skl_check_plane_surface(const struct intel_crtc_state *crtc_state,
struct intel_plane_state *plane_state);
int i9xx_check_plane_surface(struct intel_plane_state *plane_state);
+int skl_format_to_fourcc(int format, bool rgb_order, bool alpha);
/* intel_csr.c */
void intel_csr_ucode_init(struct drm_i915_private *);
@@ -1773,6 +1786,7 @@ void intel_fbc_flush(struct drm_i915_private *dev_priv,
unsigned int frontbuffer_bits, enum fb_op_origin origin);
void intel_fbc_cleanup_cfb(struct drm_i915_private *dev_priv);
void intel_fbc_handle_fifo_underrun_irq(struct drm_i915_private *dev_priv);
+int intel_fbc_reset_underrun(struct drm_i915_private *dev_priv);
/* intel_hdmi.c */
void intel_hdmi_init(struct drm_i915_private *dev_priv, i915_reg_t hdmi_reg,
@@ -1783,7 +1797,7 @@ struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder);
bool intel_hdmi_compute_config(struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config,
struct drm_connector_state *conn_state);
-void intel_hdmi_handle_sink_scrambling(struct intel_encoder *intel_encoder,
+bool intel_hdmi_handle_sink_scrambling(struct intel_encoder *encoder,
struct drm_connector *connector,
bool high_tmds_clock_ratio,
bool scrambling);
@@ -1877,7 +1891,8 @@ void intel_psr_enable(struct intel_dp *intel_dp,
void intel_psr_disable(struct intel_dp *intel_dp,
const struct intel_crtc_state *old_crtc_state);
void intel_psr_invalidate(struct drm_i915_private *dev_priv,
- unsigned frontbuffer_bits);
+ unsigned frontbuffer_bits,
+ enum fb_op_origin origin);
void intel_psr_flush(struct drm_i915_private *dev_priv,
unsigned frontbuffer_bits,
enum fb_op_origin origin);
@@ -2046,6 +2061,7 @@ void skl_disable_plane(struct intel_plane *plane, struct intel_crtc *crtc);
bool skl_plane_get_hw_state(struct intel_plane *plane);
bool skl_plane_has_ccs(struct drm_i915_private *dev_priv,
enum pipe pipe, enum plane_id plane_id);
+bool intel_format_is_yuv(uint32_t format);
/* intel_tv.c */
void intel_tv_init(struct drm_i915_private *dev_priv);
@@ -2082,31 +2098,6 @@ intel_atomic_get_crtc_state(struct drm_atomic_state *state,
return to_intel_crtc_state(crtc_state);
}
-static inline struct intel_crtc_state *
-intel_atomic_get_existing_crtc_state(struct drm_atomic_state *state,
- struct intel_crtc *crtc)
-{
- struct drm_crtc_state *crtc_state;
-
- crtc_state = drm_atomic_get_existing_crtc_state(state, &crtc->base);
-
- if (crtc_state)
- return to_intel_crtc_state(crtc_state);
- else
- return NULL;
-}
-
-static inline struct intel_plane_state *
-intel_atomic_get_existing_plane_state(struct drm_atomic_state *state,
- struct intel_plane *plane)
-{
- struct drm_plane_state *plane_state;
-
- plane_state = drm_atomic_get_existing_plane_state(state, &plane->base);
-
- return to_intel_plane_state(plane_state);
-}
-
int intel_atomic_setup_scalers(struct drm_i915_private *dev_priv,
struct intel_crtc *intel_crtc,
struct intel_crtc_state *crtc_state);
@@ -2138,8 +2129,17 @@ int intel_pipe_crc_create(struct drm_minor *minor);
#ifdef CONFIG_DEBUG_FS
int intel_crtc_set_crc_source(struct drm_crtc *crtc, const char *source_name,
size_t *values_cnt);
+void intel_crtc_disable_pipe_crc(struct intel_crtc *crtc);
+void intel_crtc_enable_pipe_crc(struct intel_crtc *crtc);
#else
#define intel_crtc_set_crc_source NULL
+static inline void intel_crtc_disable_pipe_crc(struct intel_crtc *crtc)
+{
+}
+
+static inline void intel_crtc_enable_pipe_crc(struct intel_crtc *crtc)
+{
+}
#endif
extern const struct file_operations i915_display_crc_ctl_fops;
#endif /* __INTEL_DRV_H__ */
diff --git a/drivers/gpu/drm/i915/intel_engine_cs.c b/drivers/gpu/drm/i915/intel_engine_cs.c
index 4ba139c27fba..1a8370779bbb 100644
--- a/drivers/gpu/drm/i915/intel_engine_cs.c
+++ b/drivers/gpu/drm/i915/intel_engine_cs.c
@@ -81,13 +81,17 @@ static const struct engine_class_info intel_engine_classes[] = {
},
};
+#define MAX_MMIO_BASES 3
struct engine_info {
unsigned int hw_id;
unsigned int uabi_id;
u8 class;
u8 instance;
- u32 mmio_base;
- unsigned irq_shift;
+ /* mmio bases table *must* be sorted in reverse gen order */
+ struct engine_mmio_base {
+ u32 gen : 8;
+ u32 base : 24;
+ } mmio_bases[MAX_MMIO_BASES];
};
static const struct engine_info intel_engines[] = {
@@ -96,64 +100,76 @@ static const struct engine_info intel_engines[] = {
.uabi_id = I915_EXEC_RENDER,
.class = RENDER_CLASS,
.instance = 0,
- .mmio_base = RENDER_RING_BASE,
- .irq_shift = GEN8_RCS_IRQ_SHIFT,
+ .mmio_bases = {
+ { .gen = 1, .base = RENDER_RING_BASE }
+ },
},
[BCS] = {
.hw_id = BCS_HW,
.uabi_id = I915_EXEC_BLT,
.class = COPY_ENGINE_CLASS,
.instance = 0,
- .mmio_base = BLT_RING_BASE,
- .irq_shift = GEN8_BCS_IRQ_SHIFT,
+ .mmio_bases = {
+ { .gen = 6, .base = BLT_RING_BASE }
+ },
},
[VCS] = {
.hw_id = VCS_HW,
.uabi_id = I915_EXEC_BSD,
.class = VIDEO_DECODE_CLASS,
.instance = 0,
- .mmio_base = GEN6_BSD_RING_BASE,
- .irq_shift = GEN8_VCS1_IRQ_SHIFT,
+ .mmio_bases = {
+ { .gen = 11, .base = GEN11_BSD_RING_BASE },
+ { .gen = 6, .base = GEN6_BSD_RING_BASE },
+ { .gen = 4, .base = BSD_RING_BASE }
+ },
},
[VCS2] = {
.hw_id = VCS2_HW,
.uabi_id = I915_EXEC_BSD,
.class = VIDEO_DECODE_CLASS,
.instance = 1,
- .mmio_base = GEN8_BSD2_RING_BASE,
- .irq_shift = GEN8_VCS2_IRQ_SHIFT,
+ .mmio_bases = {
+ { .gen = 11, .base = GEN11_BSD2_RING_BASE },
+ { .gen = 8, .base = GEN8_BSD2_RING_BASE }
+ },
},
[VCS3] = {
.hw_id = VCS3_HW,
.uabi_id = I915_EXEC_BSD,
.class = VIDEO_DECODE_CLASS,
.instance = 2,
- .mmio_base = GEN11_BSD3_RING_BASE,
- .irq_shift = 0, /* not used */
+ .mmio_bases = {
+ { .gen = 11, .base = GEN11_BSD3_RING_BASE }
+ },
},
[VCS4] = {
.hw_id = VCS4_HW,
.uabi_id = I915_EXEC_BSD,
.class = VIDEO_DECODE_CLASS,
.instance = 3,
- .mmio_base = GEN11_BSD4_RING_BASE,
- .irq_shift = 0, /* not used */
+ .mmio_bases = {
+ { .gen = 11, .base = GEN11_BSD4_RING_BASE }
+ },
},
[VECS] = {
.hw_id = VECS_HW,
.uabi_id = I915_EXEC_VEBOX,
.class = VIDEO_ENHANCEMENT_CLASS,
.instance = 0,
- .mmio_base = VEBOX_RING_BASE,
- .irq_shift = GEN8_VECS_IRQ_SHIFT,
+ .mmio_bases = {
+ { .gen = 11, .base = GEN11_VEBOX_RING_BASE },
+ { .gen = 7, .base = VEBOX_RING_BASE }
+ },
},
[VECS2] = {
.hw_id = VECS2_HW,
.uabi_id = I915_EXEC_VEBOX,
.class = VIDEO_ENHANCEMENT_CLASS,
.instance = 1,
- .mmio_base = GEN11_VEBOX2_RING_BASE,
- .irq_shift = 0, /* not used */
+ .mmio_bases = {
+ { .gen = 11, .base = GEN11_VEBOX2_RING_BASE }
+ },
},
};
@@ -223,16 +239,36 @@ __intel_engine_context_size(struct drm_i915_private *dev_priv, u8 class)
}
}
+static u32 __engine_mmio_base(struct drm_i915_private *i915,
+ const struct engine_mmio_base *bases)
+{
+ int i;
+
+ for (i = 0; i < MAX_MMIO_BASES; i++)
+ if (INTEL_GEN(i915) >= bases[i].gen)
+ break;
+
+ GEM_BUG_ON(i == MAX_MMIO_BASES);
+ GEM_BUG_ON(!bases[i].base);
+
+ return bases[i].base;
+}
+
+static void __sprint_engine_name(char *name, const struct engine_info *info)
+{
+ WARN_ON(snprintf(name, INTEL_ENGINE_CS_MAX_NAME, "%s%u",
+ intel_engine_classes[info->class].name,
+ info->instance) >= INTEL_ENGINE_CS_MAX_NAME);
+}
+
static int
intel_engine_setup(struct drm_i915_private *dev_priv,
enum intel_engine_id id)
{
const struct engine_info *info = &intel_engines[id];
- const struct engine_class_info *class_info;
struct intel_engine_cs *engine;
GEM_BUG_ON(info->class >= ARRAY_SIZE(intel_engine_classes));
- class_info = &intel_engine_classes[info->class];
BUILD_BUG_ON(MAX_ENGINE_CLASS >= BIT(GEN11_ENGINE_CLASS_WIDTH));
BUILD_BUG_ON(MAX_ENGINE_INSTANCE >= BIT(GEN11_ENGINE_INSTANCE_WIDTH));
@@ -253,35 +289,14 @@ intel_engine_setup(struct drm_i915_private *dev_priv,
engine->id = id;
engine->i915 = dev_priv;
- WARN_ON(snprintf(engine->name, sizeof(engine->name), "%s%u",
- class_info->name, info->instance) >=
- sizeof(engine->name));
+ __sprint_engine_name(engine->name, info);
engine->hw_id = engine->guc_id = info->hw_id;
- if (INTEL_GEN(dev_priv) >= 11) {
- switch (engine->id) {
- case VCS:
- engine->mmio_base = GEN11_BSD_RING_BASE;
- break;
- case VCS2:
- engine->mmio_base = GEN11_BSD2_RING_BASE;
- break;
- case VECS:
- engine->mmio_base = GEN11_VEBOX_RING_BASE;
- break;
- default:
- /* take the original value for all other engines */
- engine->mmio_base = info->mmio_base;
- break;
- }
- } else {
- engine->mmio_base = info->mmio_base;
- }
- engine->irq_shift = info->irq_shift;
+ engine->mmio_base = __engine_mmio_base(dev_priv, info->mmio_bases);
engine->class = info->class;
engine->instance = info->instance;
engine->uabi_id = info->uabi_id;
- engine->uabi_class = class_info->uabi_class;
+ engine->uabi_class = intel_engine_classes[info->class].uabi_class;
engine->context_size = __intel_engine_context_size(dev_priv,
engine->class);
@@ -441,6 +456,11 @@ static void intel_engine_init_timeline(struct intel_engine_cs *engine)
engine->timeline = &engine->i915->gt.global_timeline.engine[engine->id];
}
+static void intel_engine_init_batch_pool(struct intel_engine_cs *engine)
+{
+ i915_gem_batch_pool_init(&engine->batch_pool, engine);
+}
+
static bool csb_force_mmio(struct drm_i915_private *i915)
{
/*
@@ -455,6 +475,9 @@ static bool csb_force_mmio(struct drm_i915_private *i915)
if (intel_vgpu_active(i915) && !intel_vgpu_has_hwsp_emulation(i915))
return true;
+ if (IS_CANNONLAKE(i915))
+ return true;
+
return false;
}
@@ -485,11 +508,9 @@ static void intel_engine_init_execlist(struct intel_engine_cs *engine)
void intel_engine_setup_common(struct intel_engine_cs *engine)
{
intel_engine_init_execlist(engine);
-
intel_engine_init_timeline(engine);
intel_engine_init_hangcheck(engine);
- i915_gem_batch_pool_init(engine, &engine->batch_pool);
-
+ intel_engine_init_batch_pool(engine);
intel_engine_init_cmd_parser(engine);
}
@@ -782,10 +803,24 @@ static inline uint32_t
read_subslice_reg(struct drm_i915_private *dev_priv, int slice,
int subslice, i915_reg_t reg)
{
+ uint32_t mcr_slice_subslice_mask;
+ uint32_t mcr_slice_subslice_select;
uint32_t mcr;
uint32_t ret;
enum forcewake_domains fw_domains;
+ if (INTEL_GEN(dev_priv) >= 11) {
+ mcr_slice_subslice_mask = GEN11_MCR_SLICE_MASK |
+ GEN11_MCR_SUBSLICE_MASK;
+ mcr_slice_subslice_select = GEN11_MCR_SLICE(slice) |
+ GEN11_MCR_SUBSLICE(subslice);
+ } else {
+ mcr_slice_subslice_mask = GEN8_MCR_SLICE_MASK |
+ GEN8_MCR_SUBSLICE_MASK;
+ mcr_slice_subslice_select = GEN8_MCR_SLICE(slice) |
+ GEN8_MCR_SUBSLICE(subslice);
+ }
+
fw_domains = intel_uncore_forcewake_for_reg(dev_priv, reg,
FW_REG_READ);
fw_domains |= intel_uncore_forcewake_for_reg(dev_priv,
@@ -800,14 +835,14 @@ read_subslice_reg(struct drm_i915_private *dev_priv, int slice,
* The HW expects the slice and sublice selectors to be reset to 0
* after reading out the registers.
*/
- WARN_ON_ONCE(mcr & (GEN8_MCR_SLICE_MASK | GEN8_MCR_SUBSLICE_MASK));
- mcr &= ~(GEN8_MCR_SLICE_MASK | GEN8_MCR_SUBSLICE_MASK);
- mcr |= GEN8_MCR_SLICE(slice) | GEN8_MCR_SUBSLICE(subslice);
+ WARN_ON_ONCE(mcr & mcr_slice_subslice_mask);
+ mcr &= ~mcr_slice_subslice_mask;
+ mcr |= mcr_slice_subslice_select;
I915_WRITE_FW(GEN8_MCR_SELECTOR, mcr);
ret = I915_READ_FW(reg);
- mcr &= ~(GEN8_MCR_SLICE_MASK | GEN8_MCR_SUBSLICE_MASK);
+ mcr &= ~mcr_slice_subslice_mask;
I915_WRITE_FW(GEN8_MCR_SELECTOR, mcr);
intel_uncore_forcewake_put__locked(dev_priv, fw_domains);
@@ -871,640 +906,6 @@ void intel_engine_get_instdone(struct intel_engine_cs *engine,
}
}
-static int wa_add(struct drm_i915_private *dev_priv,
- i915_reg_t addr,
- const u32 mask, const u32 val)
-{
- const u32 idx = dev_priv->workarounds.count;
-
- if (WARN_ON(idx >= I915_MAX_WA_REGS))
- return -ENOSPC;
-
- dev_priv->workarounds.reg[idx].addr = addr;
- dev_priv->workarounds.reg[idx].value = val;
- dev_priv->workarounds.reg[idx].mask = mask;
-
- dev_priv->workarounds.count++;
-
- return 0;
-}
-
-#define WA_REG(addr, mask, val) do { \
- const int r = wa_add(dev_priv, (addr), (mask), (val)); \
- if (r) \
- return r; \
- } while (0)
-
-#define WA_SET_BIT_MASKED(addr, mask) \
- WA_REG(addr, (mask), _MASKED_BIT_ENABLE(mask))
-
-#define WA_CLR_BIT_MASKED(addr, mask) \
- WA_REG(addr, (mask), _MASKED_BIT_DISABLE(mask))
-
-#define WA_SET_FIELD_MASKED(addr, mask, value) \
- WA_REG(addr, mask, _MASKED_FIELD(mask, value))
-
-static int wa_ring_whitelist_reg(struct intel_engine_cs *engine,
- i915_reg_t reg)
-{
- struct drm_i915_private *dev_priv = engine->i915;
- struct i915_workarounds *wa = &dev_priv->workarounds;
- const uint32_t index = wa->hw_whitelist_count[engine->id];
-
- if (WARN_ON(index >= RING_MAX_NONPRIV_SLOTS))
- return -EINVAL;
-
- I915_WRITE(RING_FORCE_TO_NONPRIV(engine->mmio_base, index),
- i915_mmio_reg_offset(reg));
- wa->hw_whitelist_count[engine->id]++;
-
- return 0;
-}
-
-static int gen8_init_workarounds(struct intel_engine_cs *engine)
-{
- struct drm_i915_private *dev_priv = engine->i915;
-
- WA_SET_BIT_MASKED(INSTPM, INSTPM_FORCE_ORDERING);
-
- /* WaDisableAsyncFlipPerfMode:bdw,chv */
- WA_SET_BIT_MASKED(MI_MODE, ASYNC_FLIP_PERF_DISABLE);
-
- /* WaDisablePartialInstShootdown:bdw,chv */
- WA_SET_BIT_MASKED(GEN8_ROW_CHICKEN,
- PARTIAL_INSTRUCTION_SHOOTDOWN_DISABLE);
-
- /* Use Force Non-Coherent whenever executing a 3D context. This is a
- * workaround for for a possible hang in the unlikely event a TLB
- * invalidation occurs during a PSD flush.
- */
- /* WaForceEnableNonCoherent:bdw,chv */
- /* WaHdcDisableFetchWhenMasked:bdw,chv */
- WA_SET_BIT_MASKED(HDC_CHICKEN0,
- HDC_DONOT_FETCH_MEM_WHEN_MASKED |
- HDC_FORCE_NON_COHERENT);
-
- /* From the Haswell PRM, Command Reference: Registers, CACHE_MODE_0:
- * "The Hierarchical Z RAW Stall Optimization allows non-overlapping
- * polygons in the same 8x4 pixel/sample area to be processed without
- * stalling waiting for the earlier ones to write to Hierarchical Z
- * buffer."
- *
- * This optimization is off by default for BDW and CHV; turn it on.
- */
- WA_CLR_BIT_MASKED(CACHE_MODE_0_GEN7, HIZ_RAW_STALL_OPT_DISABLE);
-
- /* Wa4x4STCOptimizationDisable:bdw,chv */
- WA_SET_BIT_MASKED(CACHE_MODE_1, GEN8_4x4_STC_OPTIMIZATION_DISABLE);
-
- /*
- * BSpec recommends 8x4 when MSAA is used,
- * however in practice 16x4 seems fastest.
- *
- * Note that PS/WM thread counts depend on the WIZ hashing
- * disable bit, which we don't touch here, but it's good
- * to keep in mind (see 3DSTATE_PS and 3DSTATE_WM).
- */
- WA_SET_FIELD_MASKED(GEN7_GT_MODE,
- GEN6_WIZ_HASHING_MASK,
- GEN6_WIZ_HASHING_16x4);
-
- return 0;
-}
-
-static int bdw_init_workarounds(struct intel_engine_cs *engine)
-{
- struct drm_i915_private *dev_priv = engine->i915;
- int ret;
-
- ret = gen8_init_workarounds(engine);
- if (ret)
- return ret;
-
- /* WaDisableThreadStallDopClockGating:bdw (pre-production) */
- WA_SET_BIT_MASKED(GEN8_ROW_CHICKEN, STALL_DOP_GATING_DISABLE);
-
- /* WaDisableDopClockGating:bdw
- *
- * Also see the related UCGTCL1 write in broadwell_init_clock_gating()
- * to disable EUTC clock gating.
- */
- WA_SET_BIT_MASKED(GEN7_ROW_CHICKEN2,
- DOP_CLOCK_GATING_DISABLE);
-
- WA_SET_BIT_MASKED(HALF_SLICE_CHICKEN3,
- GEN8_SAMPLER_POWER_BYPASS_DIS);
-
- WA_SET_BIT_MASKED(HDC_CHICKEN0,
- /* WaForceContextSaveRestoreNonCoherent:bdw */
- HDC_FORCE_CONTEXT_SAVE_RESTORE_NON_COHERENT |
- /* WaDisableFenceDestinationToSLM:bdw (pre-prod) */
- (IS_BDW_GT3(dev_priv) ? HDC_FENCE_DEST_SLM_DISABLE : 0));
-
- return 0;
-}
-
-static int chv_init_workarounds(struct intel_engine_cs *engine)
-{
- struct drm_i915_private *dev_priv = engine->i915;
- int ret;
-
- ret = gen8_init_workarounds(engine);
- if (ret)
- return ret;
-
- /* WaDisableThreadStallDopClockGating:chv */
- WA_SET_BIT_MASKED(GEN8_ROW_CHICKEN, STALL_DOP_GATING_DISABLE);
-
- /* Improve HiZ throughput on CHV. */
- WA_SET_BIT_MASKED(HIZ_CHICKEN, CHV_HZ_8X8_MODE_IN_1X);
-
- return 0;
-}
-
-static int gen9_init_workarounds(struct intel_engine_cs *engine)
-{
- struct drm_i915_private *dev_priv = engine->i915;
- int ret;
-
- /* WaConextSwitchWithConcurrentTLBInvalidate:skl,bxt,kbl,glk,cfl */
- I915_WRITE(GEN9_CSFE_CHICKEN1_RCS, _MASKED_BIT_ENABLE(GEN9_PREEMPT_GPGPU_SYNC_SWITCH_DISABLE));
-
- /* WaEnableLbsSlaRetryTimerDecrement:skl,bxt,kbl,glk,cfl */
- I915_WRITE(BDW_SCRATCH1, I915_READ(BDW_SCRATCH1) |
- GEN9_LBS_SLA_RETRY_TIMER_DECREMENT_ENABLE);
-
- /* WaDisableKillLogic:bxt,skl,kbl */
- if (!IS_COFFEELAKE(dev_priv))
- I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) |
- ECOCHK_DIS_TLB);
-
- if (HAS_LLC(dev_priv)) {
- /* WaCompressedResourceSamplerPbeMediaNewHashMode:skl,kbl
- *
- * Must match Display Engine. See
- * WaCompressedResourceDisplayNewHashMode.
- */
- WA_SET_BIT_MASKED(COMMON_SLICE_CHICKEN2,
- GEN9_PBE_COMPRESSED_HASH_SELECTION);
- WA_SET_BIT_MASKED(GEN9_HALF_SLICE_CHICKEN7,
- GEN9_SAMPLER_HASH_COMPRESSED_READ_ADDR);
-
- I915_WRITE(MMCD_MISC_CTRL,
- I915_READ(MMCD_MISC_CTRL) |
- MMCD_PCLA |
- MMCD_HOTSPOT_EN);
- }
-
- /* WaClearFlowControlGpgpuContextSave:skl,bxt,kbl,glk,cfl */
- /* WaDisablePartialInstShootdown:skl,bxt,kbl,glk,cfl */
- WA_SET_BIT_MASKED(GEN8_ROW_CHICKEN,
- FLOW_CONTROL_ENABLE |
- PARTIAL_INSTRUCTION_SHOOTDOWN_DISABLE);
-
- /* Syncing dependencies between camera and graphics:skl,bxt,kbl */
- if (!IS_COFFEELAKE(dev_priv))
- WA_SET_BIT_MASKED(HALF_SLICE_CHICKEN3,
- GEN9_DISABLE_OCL_OOB_SUPPRESS_LOGIC);
-
- /* WaEnableYV12BugFixInHalfSliceChicken7:skl,bxt,kbl,glk,cfl */
- /* WaEnableSamplerGPGPUPreemptionSupport:skl,bxt,kbl,cfl */
- WA_SET_BIT_MASKED(GEN9_HALF_SLICE_CHICKEN7,
- GEN9_ENABLE_YV12_BUGFIX |
- GEN9_ENABLE_GPGPU_PREEMPTION);
-
- /* Wa4x4STCOptimizationDisable:skl,bxt,kbl,glk,cfl */
- /* WaDisablePartialResolveInVc:skl,bxt,kbl,cfl */
- WA_SET_BIT_MASKED(CACHE_MODE_1, (GEN8_4x4_STC_OPTIMIZATION_DISABLE |
- GEN9_PARTIAL_RESOLVE_IN_VC_DISABLE));
-
- /* WaCcsTlbPrefetchDisable:skl,bxt,kbl,glk,cfl */
- WA_CLR_BIT_MASKED(GEN9_HALF_SLICE_CHICKEN5,
- GEN9_CCS_TLB_PREFETCH_ENABLE);
-
- /* WaForceContextSaveRestoreNonCoherent:skl,bxt,kbl,cfl */
- WA_SET_BIT_MASKED(HDC_CHICKEN0,
- HDC_FORCE_CONTEXT_SAVE_RESTORE_NON_COHERENT |
- HDC_FORCE_CSR_NON_COHERENT_OVR_DISABLE);
-
- /* WaForceEnableNonCoherent and WaDisableHDCInvalidation are
- * both tied to WaForceContextSaveRestoreNonCoherent
- * in some hsds for skl. We keep the tie for all gen9. The
- * documentation is a bit hazy and so we want to get common behaviour,
- * even though there is no clear evidence we would need both on kbl/bxt.
- * This area has been source of system hangs so we play it safe
- * and mimic the skl regardless of what bspec says.
- *
- * Use Force Non-Coherent whenever executing a 3D context. This
- * is a workaround for a possible hang in the unlikely event
- * a TLB invalidation occurs during a PSD flush.
- */
-
- /* WaForceEnableNonCoherent:skl,bxt,kbl,cfl */
- WA_SET_BIT_MASKED(HDC_CHICKEN0,
- HDC_FORCE_NON_COHERENT);
-
- /* WaDisableHDCInvalidation:skl,bxt,kbl,cfl */
- I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) |
- BDW_DISABLE_HDC_INVALIDATION);
-
- /* WaDisableSamplerPowerBypassForSOPingPong:skl,bxt,kbl,cfl */
- if (IS_SKYLAKE(dev_priv) ||
- IS_KABYLAKE(dev_priv) ||
- IS_COFFEELAKE(dev_priv))
- WA_SET_BIT_MASKED(HALF_SLICE_CHICKEN3,
- GEN8_SAMPLER_POWER_BYPASS_DIS);
-
- /* WaDisableSTUnitPowerOptimization:skl,bxt,kbl,glk,cfl */
- WA_SET_BIT_MASKED(HALF_SLICE_CHICKEN2, GEN8_ST_PO_DISABLE);
-
- /* WaProgramL3SqcReg1DefaultForPerf:bxt,glk */
- if (IS_GEN9_LP(dev_priv)) {
- u32 val = I915_READ(GEN8_L3SQCREG1);
-
- val &= ~L3_PRIO_CREDITS_MASK;
- val |= L3_GENERAL_PRIO_CREDITS(62) | L3_HIGH_PRIO_CREDITS(2);
- I915_WRITE(GEN8_L3SQCREG1, val);
- }
-
- /* WaOCLCoherentLineFlush:skl,bxt,kbl,cfl */
- I915_WRITE(GEN8_L3SQCREG4, (I915_READ(GEN8_L3SQCREG4) |
- GEN8_LQSC_FLUSH_COHERENT_LINES));
-
- /*
- * Supporting preemption with fine-granularity requires changes in the
- * batch buffer programming. Since we can't break old userspace, we
- * need to set our default preemption level to safe value. Userspace is
- * still able to use more fine-grained preemption levels, since in
- * WaEnablePreemptionGranularityControlByUMD we're whitelisting the
- * per-ctx register. As such, WaDisable{3D,GPGPU}MidCmdPreemption are
- * not real HW workarounds, but merely a way to start using preemption
- * while maintaining old contract with userspace.
- */
-
- /* WaDisable3DMidCmdPreemption:skl,bxt,glk,cfl,[cnl] */
- WA_CLR_BIT_MASKED(GEN8_CS_CHICKEN1, GEN9_PREEMPT_3D_OBJECT_LEVEL);
-
- /* WaDisableGPGPUMidCmdPreemption:skl,bxt,blk,cfl,[cnl] */
- WA_SET_FIELD_MASKED(GEN8_CS_CHICKEN1, GEN9_PREEMPT_GPGPU_LEVEL_MASK,
- GEN9_PREEMPT_GPGPU_COMMAND_LEVEL);
-
- /* WaVFEStateAfterPipeControlwithMediaStateClear:skl,bxt,glk,cfl */
- ret = wa_ring_whitelist_reg(engine, GEN9_CTX_PREEMPT_REG);
- if (ret)
- return ret;
-
- /* WaEnablePreemptionGranularityControlByUMD:skl,bxt,kbl,cfl,[cnl] */
- I915_WRITE(GEN7_FF_SLICE_CS_CHICKEN1,
- _MASKED_BIT_ENABLE(GEN9_FFSC_PERCTX_PREEMPT_CTRL));
- ret = wa_ring_whitelist_reg(engine, GEN8_CS_CHICKEN1);
- if (ret)
- return ret;
-
- /* WaAllowUMDToModifyHDCChicken1:skl,bxt,kbl,glk,cfl */
- ret = wa_ring_whitelist_reg(engine, GEN8_HDC_CHICKEN1);
- if (ret)
- return ret;
-
- return 0;
-}
-
-static int skl_tune_iz_hashing(struct intel_engine_cs *engine)
-{
- struct drm_i915_private *dev_priv = engine->i915;
- u8 vals[3] = { 0, 0, 0 };
- unsigned int i;
-
- for (i = 0; i < 3; i++) {
- u8 ss;
-
- /*
- * Only consider slices where one, and only one, subslice has 7
- * EUs
- */
- if (!is_power_of_2(INTEL_INFO(dev_priv)->sseu.subslice_7eu[i]))
- continue;
-
- /*
- * subslice_7eu[i] != 0 (because of the check above) and
- * ss_max == 4 (maximum number of subslices possible per slice)
- *
- * -> 0 <= ss <= 3;
- */
- ss = ffs(INTEL_INFO(dev_priv)->sseu.subslice_7eu[i]) - 1;
- vals[i] = 3 - ss;
- }
-
- if (vals[0] == 0 && vals[1] == 0 && vals[2] == 0)
- return 0;
-
- /* Tune IZ hashing. See intel_device_info_runtime_init() */
- WA_SET_FIELD_MASKED(GEN7_GT_MODE,
- GEN9_IZ_HASHING_MASK(2) |
- GEN9_IZ_HASHING_MASK(1) |
- GEN9_IZ_HASHING_MASK(0),
- GEN9_IZ_HASHING(2, vals[2]) |
- GEN9_IZ_HASHING(1, vals[1]) |
- GEN9_IZ_HASHING(0, vals[0]));
-
- return 0;
-}
-
-static int skl_init_workarounds(struct intel_engine_cs *engine)
-{
- struct drm_i915_private *dev_priv = engine->i915;
- int ret;
-
- ret = gen9_init_workarounds(engine);
- if (ret)
- return ret;
-
- /* WaEnableGapsTsvCreditFix:skl */
- I915_WRITE(GEN8_GARBCNTL, (I915_READ(GEN8_GARBCNTL) |
- GEN9_GAPS_TSV_CREDIT_DISABLE));
-
- /* WaDisableGafsUnitClkGating:skl */
- I915_WRITE(GEN7_UCGCTL4, (I915_READ(GEN7_UCGCTL4) |
- GEN8_EU_GAUNIT_CLOCK_GATE_DISABLE));
-
- /* WaInPlaceDecompressionHang:skl */
- if (IS_SKL_REVID(dev_priv, SKL_REVID_H0, REVID_FOREVER))
- I915_WRITE(GEN9_GAMT_ECO_REG_RW_IA,
- (I915_READ(GEN9_GAMT_ECO_REG_RW_IA) |
- GAMT_ECO_ENABLE_IN_PLACE_DECOMPRESS));
-
- /* WaDisableLSQCROPERFforOCL:skl */
- ret = wa_ring_whitelist_reg(engine, GEN8_L3SQCREG4);
- if (ret)
- return ret;
-
- return skl_tune_iz_hashing(engine);
-}
-
-static int bxt_init_workarounds(struct intel_engine_cs *engine)
-{
- struct drm_i915_private *dev_priv = engine->i915;
- int ret;
-
- ret = gen9_init_workarounds(engine);
- if (ret)
- return ret;
-
- /* WaDisableThreadStallDopClockGating:bxt */
- WA_SET_BIT_MASKED(GEN8_ROW_CHICKEN,
- STALL_DOP_GATING_DISABLE);
-
- /* WaDisablePooledEuLoadBalancingFix:bxt */
- I915_WRITE(FF_SLICE_CS_CHICKEN2,
- _MASKED_BIT_ENABLE(GEN9_POOLED_EU_LOAD_BALANCING_FIX_DISABLE));
-
- /* WaToEnableHwFixForPushConstHWBug:bxt */
- WA_SET_BIT_MASKED(COMMON_SLICE_CHICKEN2,
- GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION);
-
- /* WaInPlaceDecompressionHang:bxt */
- I915_WRITE(GEN9_GAMT_ECO_REG_RW_IA,
- (I915_READ(GEN9_GAMT_ECO_REG_RW_IA) |
- GAMT_ECO_ENABLE_IN_PLACE_DECOMPRESS));
-
- return 0;
-}
-
-static int cnl_init_workarounds(struct intel_engine_cs *engine)
-{
- struct drm_i915_private *dev_priv = engine->i915;
- int ret;
-
- /* WaDisableI2mCycleOnWRPort:cnl (pre-prod) */
- if (IS_CNL_REVID(dev_priv, CNL_REVID_B0, CNL_REVID_B0))
- I915_WRITE(GAMT_CHKN_BIT_REG,
- (I915_READ(GAMT_CHKN_BIT_REG) |
- GAMT_CHKN_DISABLE_I2M_CYCLE_ON_WR_PORT));
-
- /* WaForceContextSaveRestoreNonCoherent:cnl */
- WA_SET_BIT_MASKED(CNL_HDC_CHICKEN0,
- HDC_FORCE_CONTEXT_SAVE_RESTORE_NON_COHERENT);
-
- /* WaThrottleEUPerfToAvoidTDBackPressure:cnl(pre-prod) */
- if (IS_CNL_REVID(dev_priv, CNL_REVID_B0, CNL_REVID_B0))
- WA_SET_BIT_MASKED(GEN8_ROW_CHICKEN, THROTTLE_12_5);
-
- /* WaDisableReplayBufferBankArbitrationOptimization:cnl */
- WA_SET_BIT_MASKED(COMMON_SLICE_CHICKEN2,
- GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION);
-
- /* WaDisableEnhancedSBEVertexCaching:cnl (pre-prod) */
- if (IS_CNL_REVID(dev_priv, 0, CNL_REVID_B0))
- WA_SET_BIT_MASKED(COMMON_SLICE_CHICKEN2,
- GEN8_CSC2_SBE_VUE_CACHE_CONSERVATIVE);
-
- /* WaInPlaceDecompressionHang:cnl */
- I915_WRITE(GEN9_GAMT_ECO_REG_RW_IA,
- (I915_READ(GEN9_GAMT_ECO_REG_RW_IA) |
- GAMT_ECO_ENABLE_IN_PLACE_DECOMPRESS));
-
- /* WaPushConstantDereferenceHoldDisable:cnl */
- WA_SET_BIT_MASKED(GEN7_ROW_CHICKEN2, PUSH_CONSTANT_DEREF_DISABLE);
-
- /* FtrEnableFastAnisoL1BankingFix: cnl */
- WA_SET_BIT_MASKED(HALF_SLICE_CHICKEN3, CNL_FAST_ANISO_L1_BANKING_FIX);
-
- /* WaDisable3DMidCmdPreemption:cnl */
- WA_CLR_BIT_MASKED(GEN8_CS_CHICKEN1, GEN9_PREEMPT_3D_OBJECT_LEVEL);
-
- /* WaDisableGPGPUMidCmdPreemption:cnl */
- WA_SET_FIELD_MASKED(GEN8_CS_CHICKEN1, GEN9_PREEMPT_GPGPU_LEVEL_MASK,
- GEN9_PREEMPT_GPGPU_COMMAND_LEVEL);
-
- /* WaEnablePreemptionGranularityControlByUMD:cnl */
- I915_WRITE(GEN7_FF_SLICE_CS_CHICKEN1,
- _MASKED_BIT_ENABLE(GEN9_FFSC_PERCTX_PREEMPT_CTRL));
- ret= wa_ring_whitelist_reg(engine, GEN8_CS_CHICKEN1);
- if (ret)
- return ret;
-
- /* WaDisableEarlyEOT:cnl */
- WA_SET_BIT_MASKED(GEN8_ROW_CHICKEN, DISABLE_EARLY_EOT);
-
- return 0;
-}
-
-static int kbl_init_workarounds(struct intel_engine_cs *engine)
-{
- struct drm_i915_private *dev_priv = engine->i915;
- int ret;
-
- ret = gen9_init_workarounds(engine);
- if (ret)
- return ret;
-
- /* WaEnableGapsTsvCreditFix:kbl */
- I915_WRITE(GEN8_GARBCNTL, (I915_READ(GEN8_GARBCNTL) |
- GEN9_GAPS_TSV_CREDIT_DISABLE));
-
- /* WaDisableDynamicCreditSharing:kbl */
- if (IS_KBL_REVID(dev_priv, 0, KBL_REVID_B0))
- I915_WRITE(GAMT_CHKN_BIT_REG,
- (I915_READ(GAMT_CHKN_BIT_REG) |
- GAMT_CHKN_DISABLE_DYNAMIC_CREDIT_SHARING));
-
- /* WaDisableFenceDestinationToSLM:kbl (pre-prod) */
- if (IS_KBL_REVID(dev_priv, KBL_REVID_A0, KBL_REVID_A0))
- WA_SET_BIT_MASKED(HDC_CHICKEN0,
- HDC_FENCE_DEST_SLM_DISABLE);
-
- /* WaToEnableHwFixForPushConstHWBug:kbl */
- if (IS_KBL_REVID(dev_priv, KBL_REVID_C0, REVID_FOREVER))
- WA_SET_BIT_MASKED(COMMON_SLICE_CHICKEN2,
- GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION);
-
- /* WaDisableGafsUnitClkGating:kbl */
- I915_WRITE(GEN7_UCGCTL4, (I915_READ(GEN7_UCGCTL4) |
- GEN8_EU_GAUNIT_CLOCK_GATE_DISABLE));
-
- /* WaDisableSbeCacheDispatchPortSharing:kbl */
- WA_SET_BIT_MASKED(
- GEN7_HALF_SLICE_CHICKEN1,
- GEN7_SBE_SS_CACHE_DISPATCH_PORT_SHARING_DISABLE);
-
- /* WaInPlaceDecompressionHang:kbl */
- I915_WRITE(GEN9_GAMT_ECO_REG_RW_IA,
- (I915_READ(GEN9_GAMT_ECO_REG_RW_IA) |
- GAMT_ECO_ENABLE_IN_PLACE_DECOMPRESS));
-
- /* WaDisableLSQCROPERFforOCL:kbl */
- ret = wa_ring_whitelist_reg(engine, GEN8_L3SQCREG4);
- if (ret)
- return ret;
-
- return 0;
-}
-
-static int glk_init_workarounds(struct intel_engine_cs *engine)
-{
- struct drm_i915_private *dev_priv = engine->i915;
- int ret;
-
- ret = gen9_init_workarounds(engine);
- if (ret)
- return ret;
-
- /* WA #0862: Userspace has to set "Barrier Mode" to avoid hangs. */
- ret = wa_ring_whitelist_reg(engine, GEN9_SLICE_COMMON_ECO_CHICKEN1);
- if (ret)
- return ret;
-
- /* WaToEnableHwFixForPushConstHWBug:glk */
- WA_SET_BIT_MASKED(COMMON_SLICE_CHICKEN2,
- GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION);
-
- return 0;
-}
-
-static int cfl_init_workarounds(struct intel_engine_cs *engine)
-{
- struct drm_i915_private *dev_priv = engine->i915;
- int ret;
-
- ret = gen9_init_workarounds(engine);
- if (ret)
- return ret;
-
- /* WaEnableGapsTsvCreditFix:cfl */
- I915_WRITE(GEN8_GARBCNTL, (I915_READ(GEN8_GARBCNTL) |
- GEN9_GAPS_TSV_CREDIT_DISABLE));
-
- /* WaToEnableHwFixForPushConstHWBug:cfl */
- WA_SET_BIT_MASKED(COMMON_SLICE_CHICKEN2,
- GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION);
-
- /* WaDisableGafsUnitClkGating:cfl */
- I915_WRITE(GEN7_UCGCTL4, (I915_READ(GEN7_UCGCTL4) |
- GEN8_EU_GAUNIT_CLOCK_GATE_DISABLE));
-
- /* WaDisableSbeCacheDispatchPortSharing:cfl */
- WA_SET_BIT_MASKED(
- GEN7_HALF_SLICE_CHICKEN1,
- GEN7_SBE_SS_CACHE_DISPATCH_PORT_SHARING_DISABLE);
-
- /* WaInPlaceDecompressionHang:cfl */
- I915_WRITE(GEN9_GAMT_ECO_REG_RW_IA,
- (I915_READ(GEN9_GAMT_ECO_REG_RW_IA) |
- GAMT_ECO_ENABLE_IN_PLACE_DECOMPRESS));
-
- return 0;
-}
-
-int init_workarounds_ring(struct intel_engine_cs *engine)
-{
- struct drm_i915_private *dev_priv = engine->i915;
- int err;
-
- if (GEM_WARN_ON(engine->id != RCS))
- return -EINVAL;
-
- dev_priv->workarounds.count = 0;
- dev_priv->workarounds.hw_whitelist_count[engine->id] = 0;
-
- if (IS_BROADWELL(dev_priv))
- err = bdw_init_workarounds(engine);
- else if (IS_CHERRYVIEW(dev_priv))
- err = chv_init_workarounds(engine);
- else if (IS_SKYLAKE(dev_priv))
- err = skl_init_workarounds(engine);
- else if (IS_BROXTON(dev_priv))
- err = bxt_init_workarounds(engine);
- else if (IS_KABYLAKE(dev_priv))
- err = kbl_init_workarounds(engine);
- else if (IS_GEMINILAKE(dev_priv))
- err = glk_init_workarounds(engine);
- else if (IS_COFFEELAKE(dev_priv))
- err = cfl_init_workarounds(engine);
- else if (IS_CANNONLAKE(dev_priv))
- err = cnl_init_workarounds(engine);
- else
- err = 0;
- if (err)
- return err;
-
- DRM_DEBUG_DRIVER("%s: Number of context specific w/a: %d\n",
- engine->name, dev_priv->workarounds.count);
- return 0;
-}
-
-int intel_ring_workarounds_emit(struct i915_request *rq)
-{
- struct i915_workarounds *w = &rq->i915->workarounds;
- u32 *cs;
- int ret, i;
-
- if (w->count == 0)
- return 0;
-
- ret = rq->engine->emit_flush(rq, EMIT_BARRIER);
- if (ret)
- return ret;
-
- cs = intel_ring_begin(rq, w->count * 2 + 2);
- if (IS_ERR(cs))
- return PTR_ERR(cs);
-
- *cs++ = MI_LOAD_REGISTER_IMM(w->count);
- for (i = 0; i < w->count; i++) {
- *cs++ = i915_mmio_reg_offset(w->reg[i].addr);
- *cs++ = w->reg[i].value;
- }
- *cs++ = MI_NOOP;
-
- intel_ring_advance(rq, cs);
-
- ret = rq->engine->emit_flush(rq, EMIT_BARRIER);
- if (ret)
- return ret;
-
- return 0;
-}
-
static bool ring_is_idle(struct intel_engine_cs *engine)
{
struct drm_i915_private *dev_priv = engine->i915;
@@ -1655,6 +1056,9 @@ void intel_engines_park(struct drm_i915_private *i915)
intel_engine_dump(engine, &p, NULL);
}
+ /* Must be reset upon idling, or we may miss the busy wakeup. */
+ GEM_BUG_ON(engine->execlists.queue_priority != INT_MIN);
+
if (engine->park)
engine->park(engine);
@@ -1713,13 +1117,15 @@ static void print_request(struct drm_printer *m,
struct i915_request *rq,
const char *prefix)
{
+ const char *name = rq->fence.ops->get_timeline_name(&rq->fence);
+
drm_printf(m, "%s%x%s [%llx:%x] prio=%d @ %dms: %s\n", prefix,
rq->global_seqno,
i915_request_completed(rq) ? "!" : "",
rq->fence.context, rq->fence.seqno,
rq->priotree.priority,
jiffies_to_msecs(jiffies - rq->emitted_jiffies),
- rq->timeline->common->name);
+ name);
}
static void hexdump(struct drm_printer *m, const void *buf, size_t len)
@@ -1825,12 +1231,15 @@ static void intel_engine_print_registers(const struct intel_engine_cs *engine,
ptr = I915_READ(RING_CONTEXT_STATUS_PTR(engine));
read = GEN8_CSB_READ_PTR(ptr);
write = GEN8_CSB_WRITE_PTR(ptr);
- drm_printf(m, "\tExeclist CSB read %d [%d cached], write %d [%d from hws], interrupt posted? %s\n",
+ drm_printf(m, "\tExeclist CSB read %d [%d cached], write %d [%d from hws], interrupt posted? %s, tasklet queued? %s (%s)\n",
read, execlists->csb_head,
write,
intel_read_status_page(engine, intel_hws_csb_write_index(engine->i915)),
yesno(test_bit(ENGINE_IRQ_EXECLIST,
- &engine->irq_posted)));
+ &engine->irq_posted)),
+ yesno(test_bit(TASKLET_STATE_SCHED,
+ &engine->execlists.tasklet.state)),
+ enableddisabled(!atomic_read(&engine->execlists.tasklet.count)));
if (read >= GEN8_CSB_ENTRIES)
read = 0;
if (write >= GEN8_CSB_ENTRIES)
@@ -1929,12 +1338,16 @@ void intel_engine_dump(struct intel_engine_cs *engine,
rq->head, rq->postfix, rq->tail,
rq->batch ? upper_32_bits(rq->batch->node.start) : ~0u,
rq->batch ? lower_32_bits(rq->batch->node.start) : ~0u);
- drm_printf(m, "\t\tring->start: 0x%08x\n",
+ drm_printf(m, "\t\tring->start: 0x%08x\n",
i915_ggtt_offset(rq->ring->vma));
- drm_printf(m, "\t\tring->head: 0x%08x\n",
+ drm_printf(m, "\t\tring->head: 0x%08x\n",
rq->ring->head);
- drm_printf(m, "\t\tring->tail: 0x%08x\n",
+ drm_printf(m, "\t\tring->tail: 0x%08x\n",
rq->ring->tail);
+ drm_printf(m, "\t\tring->emit: 0x%08x\n",
+ rq->ring->emit);
+ drm_printf(m, "\t\tring->space: 0x%08x\n",
+ rq->ring->space);
}
rcu_read_unlock();
@@ -2109,4 +1522,5 @@ void intel_disable_engine_stats(struct intel_engine_cs *engine)
#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
#include "selftests/mock_engine.c"
+#include "selftests/intel_engine_cs.c"
#endif
diff --git a/drivers/gpu/drm/i915/intel_fbc.c b/drivers/gpu/drm/i915/intel_fbc.c
index 707d49c12638..b431b6733cc1 100644
--- a/drivers/gpu/drm/i915/intel_fbc.c
+++ b/drivers/gpu/drm/i915/intel_fbc.c
@@ -1272,6 +1272,34 @@ out:
mutex_unlock(&fbc->lock);
}
+/*
+ * intel_fbc_reset_underrun - reset FBC fifo underrun status.
+ * @dev_priv: i915 device instance
+ *
+ * See intel_fbc_handle_fifo_underrun_irq(). For automated testing we
+ * want to re-enable FBC after an underrun to increase test coverage.
+ */
+int intel_fbc_reset_underrun(struct drm_i915_private *dev_priv)
+{
+ int ret;
+
+ cancel_work_sync(&dev_priv->fbc.underrun_work);
+
+ ret = mutex_lock_interruptible(&dev_priv->fbc.lock);
+ if (ret)
+ return ret;
+
+ if (dev_priv->fbc.underrun_detected) {
+ DRM_DEBUG_KMS("Re-allowing FBC after fifo underrun\n");
+ dev_priv->fbc.no_fbc_reason = "FIFO underrun cleared";
+ }
+
+ dev_priv->fbc.underrun_detected = false;
+ mutex_unlock(&dev_priv->fbc.lock);
+
+ return 0;
+}
+
/**
* intel_fbc_handle_fifo_underrun_irq - disable FBC when we get a FIFO underrun
* @dev_priv: i915 device instance
diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c
index 89592ecc44ca..e9e02b58b7be 100644
--- a/drivers/gpu/drm/i915/intel_fbdev.c
+++ b/drivers/gpu/drm/i915/intel_fbdev.c
@@ -221,6 +221,9 @@ static int intelfb_create(struct drm_fb_helper *helper,
goto out_unlock;
}
+ fb = &ifbdev->fb->base;
+ intel_fb_obj_flush(intel_fb_obj(fb), ORIGIN_DIRTYFB);
+
info = drm_fb_helper_alloc_fbi(helper);
if (IS_ERR(info)) {
DRM_ERROR("Failed to allocate fb_info\n");
@@ -230,8 +233,6 @@ static int intelfb_create(struct drm_fb_helper *helper,
info->par = helper;
- fb = &ifbdev->fb->base;
-
ifbdev->helper.fb = fb;
strcpy(info->fix.id, "inteldrmfb");
@@ -806,7 +807,7 @@ void intel_fbdev_output_poll_changed(struct drm_device *dev)
return;
intel_fbdev_sync(ifbdev);
- if (ifbdev->vma)
+ if (ifbdev->vma || ifbdev->helper.deferred_setup)
drm_fb_helper_hotplug_event(&ifbdev->helper);
}
diff --git a/drivers/gpu/drm/i915/intel_frontbuffer.c b/drivers/gpu/drm/i915/intel_frontbuffer.c
index 3a8d3d06c26a..7fff0a0eceb4 100644
--- a/drivers/gpu/drm/i915/intel_frontbuffer.c
+++ b/drivers/gpu/drm/i915/intel_frontbuffer.c
@@ -80,7 +80,7 @@ void __intel_fb_obj_invalidate(struct drm_i915_gem_object *obj,
}
might_sleep();
- intel_psr_invalidate(dev_priv, frontbuffer_bits);
+ intel_psr_invalidate(dev_priv, frontbuffer_bits, origin);
intel_edp_drrs_invalidate(dev_priv, frontbuffer_bits);
intel_fbc_invalidate(dev_priv, frontbuffer_bits, origin);
}
diff --git a/drivers/gpu/drm/i915/intel_gpu_commands.h b/drivers/gpu/drm/i915/intel_gpu_commands.h
new file mode 100644
index 000000000000..105e2a9e874a
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_gpu_commands.h
@@ -0,0 +1,274 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright � 2003-2018 Intel Corporation
+ */
+
+#ifndef _INTEL_GPU_COMMANDS_H_
+#define _INTEL_GPU_COMMANDS_H_
+
+/*
+ * Instruction field definitions used by the command parser
+ */
+#define INSTR_CLIENT_SHIFT 29
+#define INSTR_MI_CLIENT 0x0
+#define INSTR_BC_CLIENT 0x2
+#define INSTR_RC_CLIENT 0x3
+#define INSTR_SUBCLIENT_SHIFT 27
+#define INSTR_SUBCLIENT_MASK 0x18000000
+#define INSTR_MEDIA_SUBCLIENT 0x2
+#define INSTR_26_TO_24_MASK 0x7000000
+#define INSTR_26_TO_24_SHIFT 24
+
+/*
+ * Memory interface instructions used by the kernel
+ */
+#define MI_INSTR(opcode, flags) (((opcode) << 23) | (flags))
+/* Many MI commands use bit 22 of the header dword for GGTT vs PPGTT */
+#define MI_GLOBAL_GTT (1<<22)
+
+#define MI_NOOP MI_INSTR(0, 0)
+#define MI_USER_INTERRUPT MI_INSTR(0x02, 0)
+#define MI_WAIT_FOR_EVENT MI_INSTR(0x03, 0)
+#define MI_WAIT_FOR_OVERLAY_FLIP (1<<16)
+#define MI_WAIT_FOR_PLANE_B_FLIP (1<<6)
+#define MI_WAIT_FOR_PLANE_A_FLIP (1<<2)
+#define MI_WAIT_FOR_PLANE_A_SCANLINES (1<<1)
+#define MI_FLUSH MI_INSTR(0x04, 0)
+#define MI_READ_FLUSH (1 << 0)
+#define MI_EXE_FLUSH (1 << 1)
+#define MI_NO_WRITE_FLUSH (1 << 2)
+#define MI_SCENE_COUNT (1 << 3) /* just increment scene count */
+#define MI_END_SCENE (1 << 4) /* flush binner and incr scene count */
+#define MI_INVALIDATE_ISP (1 << 5) /* invalidate indirect state pointers */
+#define MI_REPORT_HEAD MI_INSTR(0x07, 0)
+#define MI_ARB_ON_OFF MI_INSTR(0x08, 0)
+#define MI_ARB_ENABLE (1<<0)
+#define MI_ARB_DISABLE (0<<0)
+#define MI_BATCH_BUFFER_END MI_INSTR(0x0a, 0)
+#define MI_SUSPEND_FLUSH MI_INSTR(0x0b, 0)
+#define MI_SUSPEND_FLUSH_EN (1<<0)
+#define MI_SET_APPID MI_INSTR(0x0e, 0)
+#define MI_OVERLAY_FLIP MI_INSTR(0x11, 0)
+#define MI_OVERLAY_CONTINUE (0x0<<21)
+#define MI_OVERLAY_ON (0x1<<21)
+#define MI_OVERLAY_OFF (0x2<<21)
+#define MI_LOAD_SCAN_LINES_INCL MI_INSTR(0x12, 0)
+#define MI_DISPLAY_FLIP MI_INSTR(0x14, 2)
+#define MI_DISPLAY_FLIP_I915 MI_INSTR(0x14, 1)
+#define MI_DISPLAY_FLIP_PLANE(n) ((n) << 20)
+/* IVB has funny definitions for which plane to flip. */
+#define MI_DISPLAY_FLIP_IVB_PLANE_A (0 << 19)
+#define MI_DISPLAY_FLIP_IVB_PLANE_B (1 << 19)
+#define MI_DISPLAY_FLIP_IVB_SPRITE_A (2 << 19)
+#define MI_DISPLAY_FLIP_IVB_SPRITE_B (3 << 19)
+#define MI_DISPLAY_FLIP_IVB_PLANE_C (4 << 19)
+#define MI_DISPLAY_FLIP_IVB_SPRITE_C (5 << 19)
+/* SKL ones */
+#define MI_DISPLAY_FLIP_SKL_PLANE_1_A (0 << 8)
+#define MI_DISPLAY_FLIP_SKL_PLANE_1_B (1 << 8)
+#define MI_DISPLAY_FLIP_SKL_PLANE_1_C (2 << 8)
+#define MI_DISPLAY_FLIP_SKL_PLANE_2_A (4 << 8)
+#define MI_DISPLAY_FLIP_SKL_PLANE_2_B (5 << 8)
+#define MI_DISPLAY_FLIP_SKL_PLANE_2_C (6 << 8)
+#define MI_DISPLAY_FLIP_SKL_PLANE_3_A (7 << 8)
+#define MI_DISPLAY_FLIP_SKL_PLANE_3_B (8 << 8)
+#define MI_DISPLAY_FLIP_SKL_PLANE_3_C (9 << 8)
+#define MI_SEMAPHORE_MBOX MI_INSTR(0x16, 1) /* gen6, gen7 */
+#define MI_SEMAPHORE_GLOBAL_GTT (1<<22)
+#define MI_SEMAPHORE_UPDATE (1<<21)
+#define MI_SEMAPHORE_COMPARE (1<<20)
+#define MI_SEMAPHORE_REGISTER (1<<18)
+#define MI_SEMAPHORE_SYNC_VR (0<<16) /* RCS wait for VCS (RVSYNC) */
+#define MI_SEMAPHORE_SYNC_VER (1<<16) /* RCS wait for VECS (RVESYNC) */
+#define MI_SEMAPHORE_SYNC_BR (2<<16) /* RCS wait for BCS (RBSYNC) */
+#define MI_SEMAPHORE_SYNC_BV (0<<16) /* VCS wait for BCS (VBSYNC) */
+#define MI_SEMAPHORE_SYNC_VEV (1<<16) /* VCS wait for VECS (VVESYNC) */
+#define MI_SEMAPHORE_SYNC_RV (2<<16) /* VCS wait for RCS (VRSYNC) */
+#define MI_SEMAPHORE_SYNC_RB (0<<16) /* BCS wait for RCS (BRSYNC) */
+#define MI_SEMAPHORE_SYNC_VEB (1<<16) /* BCS wait for VECS (BVESYNC) */
+#define MI_SEMAPHORE_SYNC_VB (2<<16) /* BCS wait for VCS (BVSYNC) */
+#define MI_SEMAPHORE_SYNC_BVE (0<<16) /* VECS wait for BCS (VEBSYNC) */
+#define MI_SEMAPHORE_SYNC_VVE (1<<16) /* VECS wait for VCS (VEVSYNC) */
+#define MI_SEMAPHORE_SYNC_RVE (2<<16) /* VECS wait for RCS (VERSYNC) */
+#define MI_SEMAPHORE_SYNC_INVALID (3<<16)
+#define MI_SEMAPHORE_SYNC_MASK (3<<16)
+#define MI_SET_CONTEXT MI_INSTR(0x18, 0)
+#define MI_MM_SPACE_GTT (1<<8)
+#define MI_MM_SPACE_PHYSICAL (0<<8)
+#define MI_SAVE_EXT_STATE_EN (1<<3)
+#define MI_RESTORE_EXT_STATE_EN (1<<2)
+#define MI_FORCE_RESTORE (1<<1)
+#define MI_RESTORE_INHIBIT (1<<0)
+#define HSW_MI_RS_SAVE_STATE_EN (1<<3)
+#define HSW_MI_RS_RESTORE_STATE_EN (1<<2)
+#define MI_SEMAPHORE_SIGNAL MI_INSTR(0x1b, 0) /* GEN8+ */
+#define MI_SEMAPHORE_TARGET(engine) ((engine)<<15)
+#define MI_SEMAPHORE_WAIT MI_INSTR(0x1c, 2) /* GEN8+ */
+#define MI_SEMAPHORE_POLL (1<<15)
+#define MI_SEMAPHORE_SAD_GTE_SDD (1<<12)
+#define MI_STORE_DWORD_IMM MI_INSTR(0x20, 1)
+#define MI_STORE_DWORD_IMM_GEN4 MI_INSTR(0x20, 2)
+#define MI_MEM_VIRTUAL (1 << 22) /* 945,g33,965 */
+#define MI_USE_GGTT (1 << 22) /* g4x+ */
+#define MI_STORE_DWORD_INDEX MI_INSTR(0x21, 1)
+#define MI_STORE_DWORD_INDEX_SHIFT 2
+/*
+ * Official intel docs are somewhat sloppy concerning MI_LOAD_REGISTER_IMM:
+ * - Always issue a MI_NOOP _before_ the MI_LOAD_REGISTER_IMM - otherwise hw
+ * simply ignores the register load under certain conditions.
+ * - One can actually load arbitrary many arbitrary registers: Simply issue x
+ * address/value pairs. Don't overdue it, though, x <= 2^4 must hold!
+ */
+#define MI_LOAD_REGISTER_IMM(x) MI_INSTR(0x22, 2*(x)-1)
+#define MI_LRI_FORCE_POSTED (1<<12)
+#define MI_STORE_REGISTER_MEM MI_INSTR(0x24, 1)
+#define MI_STORE_REGISTER_MEM_GEN8 MI_INSTR(0x24, 2)
+#define MI_SRM_LRM_GLOBAL_GTT (1<<22)
+#define MI_FLUSH_DW MI_INSTR(0x26, 1) /* for GEN6 */
+#define MI_FLUSH_DW_STORE_INDEX (1<<21)
+#define MI_INVALIDATE_TLB (1<<18)
+#define MI_FLUSH_DW_OP_STOREDW (1<<14)
+#define MI_FLUSH_DW_OP_MASK (3<<14)
+#define MI_FLUSH_DW_NOTIFY (1<<8)
+#define MI_INVALIDATE_BSD (1<<7)
+#define MI_FLUSH_DW_USE_GTT (1<<2)
+#define MI_FLUSH_DW_USE_PPGTT (0<<2)
+#define MI_LOAD_REGISTER_MEM MI_INSTR(0x29, 1)
+#define MI_LOAD_REGISTER_MEM_GEN8 MI_INSTR(0x29, 2)
+#define MI_BATCH_BUFFER MI_INSTR(0x30, 1)
+#define MI_BATCH_NON_SECURE (1)
+/* for snb/ivb/vlv this also means "batch in ppgtt" when ppgtt is enabled. */
+#define MI_BATCH_NON_SECURE_I965 (1<<8)
+#define MI_BATCH_PPGTT_HSW (1<<8)
+#define MI_BATCH_NON_SECURE_HSW (1<<13)
+#define MI_BATCH_BUFFER_START MI_INSTR(0x31, 0)
+#define MI_BATCH_GTT (2<<6) /* aliased with (1<<7) on gen4 */
+#define MI_BATCH_BUFFER_START_GEN8 MI_INSTR(0x31, 1)
+#define MI_BATCH_RESOURCE_STREAMER (1<<10)
+
+/*
+ * 3D instructions used by the kernel
+ */
+#define GFX_INSTR(opcode, flags) ((0x3 << 29) | ((opcode) << 24) | (flags))
+
+#define GEN9_MEDIA_POOL_STATE ((0x3 << 29) | (0x2 << 27) | (0x5 << 16) | 4)
+#define GEN9_MEDIA_POOL_ENABLE (1 << 31)
+#define GFX_OP_RASTER_RULES ((0x3<<29)|(0x7<<24))
+#define GFX_OP_SCISSOR ((0x3<<29)|(0x1c<<24)|(0x10<<19))
+#define SC_UPDATE_SCISSOR (0x1<<1)
+#define SC_ENABLE_MASK (0x1<<0)
+#define SC_ENABLE (0x1<<0)
+#define GFX_OP_LOAD_INDIRECT ((0x3<<29)|(0x1d<<24)|(0x7<<16))
+#define GFX_OP_SCISSOR_INFO ((0x3<<29)|(0x1d<<24)|(0x81<<16)|(0x1))
+#define SCI_YMIN_MASK (0xffff<<16)
+#define SCI_XMIN_MASK (0xffff<<0)
+#define SCI_YMAX_MASK (0xffff<<16)
+#define SCI_XMAX_MASK (0xffff<<0)
+#define GFX_OP_SCISSOR_ENABLE ((0x3<<29)|(0x1c<<24)|(0x10<<19))
+#define GFX_OP_SCISSOR_RECT ((0x3<<29)|(0x1d<<24)|(0x81<<16)|1)
+#define GFX_OP_COLOR_FACTOR ((0x3<<29)|(0x1d<<24)|(0x1<<16)|0x0)
+#define GFX_OP_STIPPLE ((0x3<<29)|(0x1d<<24)|(0x83<<16))
+#define GFX_OP_MAP_INFO ((0x3<<29)|(0x1d<<24)|0x4)
+#define GFX_OP_DESTBUFFER_VARS ((0x3<<29)|(0x1d<<24)|(0x85<<16)|0x0)
+#define GFX_OP_DESTBUFFER_INFO ((0x3<<29)|(0x1d<<24)|(0x8e<<16)|1)
+#define GFX_OP_DRAWRECT_INFO ((0x3<<29)|(0x1d<<24)|(0x80<<16)|(0x3))
+#define GFX_OP_DRAWRECT_INFO_I965 ((0x7900<<16)|0x2)
+
+#define COLOR_BLT_CMD (2<<29 | 0x40<<22 | (5-2))
+#define SRC_COPY_BLT_CMD ((2<<29)|(0x43<<22)|4)
+#define XY_SRC_COPY_BLT_CMD ((2<<29)|(0x53<<22)|6)
+#define XY_MONO_SRC_COPY_IMM_BLT ((2<<29)|(0x71<<22)|5)
+#define BLT_WRITE_A (2<<20)
+#define BLT_WRITE_RGB (1<<20)
+#define BLT_WRITE_RGBA (BLT_WRITE_RGB | BLT_WRITE_A)
+#define BLT_DEPTH_8 (0<<24)
+#define BLT_DEPTH_16_565 (1<<24)
+#define BLT_DEPTH_16_1555 (2<<24)
+#define BLT_DEPTH_32 (3<<24)
+#define BLT_ROP_SRC_COPY (0xcc<<16)
+#define BLT_ROP_COLOR_COPY (0xf0<<16)
+#define XY_SRC_COPY_BLT_SRC_TILED (1<<15) /* 965+ only */
+#define XY_SRC_COPY_BLT_DST_TILED (1<<11) /* 965+ only */
+#define CMD_OP_DISPLAYBUFFER_INFO ((0x0<<29)|(0x14<<23)|2)
+#define ASYNC_FLIP (1<<22)
+#define DISPLAY_PLANE_A (0<<20)
+#define DISPLAY_PLANE_B (1<<20)
+#define GFX_OP_PIPE_CONTROL(len) ((0x3<<29)|(0x3<<27)|(0x2<<24)|((len)-2))
+#define PIPE_CONTROL_FLUSH_L3 (1<<27)
+#define PIPE_CONTROL_GLOBAL_GTT_IVB (1<<24) /* gen7+ */
+#define PIPE_CONTROL_MMIO_WRITE (1<<23)
+#define PIPE_CONTROL_STORE_DATA_INDEX (1<<21)
+#define PIPE_CONTROL_CS_STALL (1<<20)
+#define PIPE_CONTROL_TLB_INVALIDATE (1<<18)
+#define PIPE_CONTROL_MEDIA_STATE_CLEAR (1<<16)
+#define PIPE_CONTROL_QW_WRITE (1<<14)
+#define PIPE_CONTROL_POST_SYNC_OP_MASK (3<<14)
+#define PIPE_CONTROL_DEPTH_STALL (1<<13)
+#define PIPE_CONTROL_WRITE_FLUSH (1<<12)
+#define PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH (1<<12) /* gen6+ */
+#define PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE (1<<11) /* MBZ on ILK */
+#define PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE (1<<10) /* GM45+ only */
+#define PIPE_CONTROL_INDIRECT_STATE_DISABLE (1<<9)
+#define PIPE_CONTROL_NOTIFY (1<<8)
+#define PIPE_CONTROL_FLUSH_ENABLE (1<<7) /* gen7+ */
+#define PIPE_CONTROL_DC_FLUSH_ENABLE (1<<5)
+#define PIPE_CONTROL_VF_CACHE_INVALIDATE (1<<4)
+#define PIPE_CONTROL_CONST_CACHE_INVALIDATE (1<<3)
+#define PIPE_CONTROL_STATE_CACHE_INVALIDATE (1<<2)
+#define PIPE_CONTROL_STALL_AT_SCOREBOARD (1<<1)
+#define PIPE_CONTROL_DEPTH_CACHE_FLUSH (1<<0)
+#define PIPE_CONTROL_GLOBAL_GTT (1<<2) /* in addr dword */
+
+/*
+ * Commands used only by the command parser
+ */
+#define MI_SET_PREDICATE MI_INSTR(0x01, 0)
+#define MI_ARB_CHECK MI_INSTR(0x05, 0)
+#define MI_RS_CONTROL MI_INSTR(0x06, 0)
+#define MI_URB_ATOMIC_ALLOC MI_INSTR(0x09, 0)
+#define MI_PREDICATE MI_INSTR(0x0C, 0)
+#define MI_RS_CONTEXT MI_INSTR(0x0F, 0)
+#define MI_TOPOLOGY_FILTER MI_INSTR(0x0D, 0)
+#define MI_LOAD_SCAN_LINES_EXCL MI_INSTR(0x13, 0)
+#define MI_URB_CLEAR MI_INSTR(0x19, 0)
+#define MI_UPDATE_GTT MI_INSTR(0x23, 0)
+#define MI_CLFLUSH MI_INSTR(0x27, 0)
+#define MI_REPORT_PERF_COUNT MI_INSTR(0x28, 0)
+#define MI_REPORT_PERF_COUNT_GGTT (1<<0)
+#define MI_LOAD_REGISTER_REG MI_INSTR(0x2A, 0)
+#define MI_RS_STORE_DATA_IMM MI_INSTR(0x2B, 0)
+#define MI_LOAD_URB_MEM MI_INSTR(0x2C, 0)
+#define MI_STORE_URB_MEM MI_INSTR(0x2D, 0)
+#define MI_CONDITIONAL_BATCH_BUFFER_END MI_INSTR(0x36, 0)
+
+#define PIPELINE_SELECT ((0x3<<29)|(0x1<<27)|(0x1<<24)|(0x4<<16))
+#define GFX_OP_3DSTATE_VF_STATISTICS ((0x3<<29)|(0x1<<27)|(0x0<<24)|(0xB<<16))
+#define MEDIA_VFE_STATE ((0x3<<29)|(0x2<<27)|(0x0<<24)|(0x0<<16))
+#define MEDIA_VFE_STATE_MMIO_ACCESS_MASK (0x18)
+#define GPGPU_OBJECT ((0x3<<29)|(0x2<<27)|(0x1<<24)|(0x4<<16))
+#define GPGPU_WALKER ((0x3<<29)|(0x2<<27)|(0x1<<24)|(0x5<<16))
+#define GFX_OP_3DSTATE_DX9_CONSTANTF_VS \
+ ((0x3<<29)|(0x3<<27)|(0x0<<24)|(0x39<<16))
+#define GFX_OP_3DSTATE_DX9_CONSTANTF_PS \
+ ((0x3<<29)|(0x3<<27)|(0x0<<24)|(0x3A<<16))
+#define GFX_OP_3DSTATE_SO_DECL_LIST \
+ ((0x3<<29)|(0x3<<27)|(0x1<<24)|(0x17<<16))
+
+#define GFX_OP_3DSTATE_BINDING_TABLE_EDIT_VS \
+ ((0x3<<29)|(0x3<<27)|(0x0<<24)|(0x43<<16))
+#define GFX_OP_3DSTATE_BINDING_TABLE_EDIT_GS \
+ ((0x3<<29)|(0x3<<27)|(0x0<<24)|(0x44<<16))
+#define GFX_OP_3DSTATE_BINDING_TABLE_EDIT_HS \
+ ((0x3<<29)|(0x3<<27)|(0x0<<24)|(0x45<<16))
+#define GFX_OP_3DSTATE_BINDING_TABLE_EDIT_DS \
+ ((0x3<<29)|(0x3<<27)|(0x0<<24)|(0x46<<16))
+#define GFX_OP_3DSTATE_BINDING_TABLE_EDIT_PS \
+ ((0x3<<29)|(0x3<<27)|(0x0<<24)|(0x47<<16))
+
+#define MFX_WAIT ((0x3<<29)|(0x1<<27)|(0x0<<16))
+
+#define COLOR_BLT ((0x2<<29)|(0x40<<22))
+#define SRC_COPY_BLT ((0x2<<29)|(0x43<<22))
+
+#endif /* _INTEL_GPU_COMMANDS_H_ */
diff --git a/drivers/gpu/drm/i915/intel_guc.c b/drivers/gpu/drm/i915/intel_guc.c
index ff08ea0ebf49..a00a59a7d9ec 100644
--- a/drivers/gpu/drm/i915/intel_guc.c
+++ b/drivers/gpu/drm/i915/intel_guc.c
@@ -64,10 +64,12 @@ void intel_guc_init_early(struct intel_guc *guc)
{
intel_guc_fw_init_early(guc);
intel_guc_ct_init_early(&guc->ct);
- intel_guc_log_init_early(guc);
+ intel_guc_log_init_early(&guc->log);
mutex_init(&guc->send_mutex);
+ spin_lock_init(&guc->irq_lock);
guc->send = intel_guc_send_nop;
+ guc->handler = intel_guc_to_host_event_handler_nop;
guc->notify = gen8_guc_raise_irq;
}
@@ -86,9 +88,10 @@ int intel_guc_init_wq(struct intel_guc *guc)
* or scheduled later on resume. This way the handling of work
* item can be kept same between system suspend & rpm suspend.
*/
- guc->log.runtime.flush_wq = alloc_ordered_workqueue("i915-guc_log",
- WQ_HIGHPRI | WQ_FREEZABLE);
- if (!guc->log.runtime.flush_wq) {
+ guc->log.relay.flush_wq =
+ alloc_ordered_workqueue("i915-guc_log",
+ WQ_HIGHPRI | WQ_FREEZABLE);
+ if (!guc->log.relay.flush_wq) {
DRM_ERROR("Couldn't allocate workqueue for GuC log\n");
return -ENOMEM;
}
@@ -111,7 +114,7 @@ int intel_guc_init_wq(struct intel_guc *guc)
guc->preempt_wq = alloc_ordered_workqueue("i915-guc_preempt",
WQ_HIGHPRI);
if (!guc->preempt_wq) {
- destroy_workqueue(guc->log.runtime.flush_wq);
+ destroy_workqueue(guc->log.relay.flush_wq);
DRM_ERROR("Couldn't allocate workqueue for GuC "
"preemption\n");
return -ENOMEM;
@@ -129,7 +132,7 @@ void intel_guc_fini_wq(struct intel_guc *guc)
USES_GUC_SUBMISSION(dev_priv))
destroy_workqueue(guc->preempt_wq);
- destroy_workqueue(guc->log.runtime.flush_wq);
+ destroy_workqueue(guc->log.relay.flush_wq);
}
static int guc_shared_data_create(struct intel_guc *guc)
@@ -169,7 +172,7 @@ int intel_guc_init(struct intel_guc *guc)
return ret;
GEM_BUG_ON(!guc->shared_data);
- ret = intel_guc_log_create(guc);
+ ret = intel_guc_log_create(&guc->log);
if (ret)
goto err_shared;
@@ -184,7 +187,7 @@ int intel_guc_init(struct intel_guc *guc)
return 0;
err_log:
- intel_guc_log_destroy(guc);
+ intel_guc_log_destroy(&guc->log);
err_shared:
guc_shared_data_destroy(guc);
return ret;
@@ -196,7 +199,7 @@ void intel_guc_fini(struct intel_guc *guc)
i915_ggtt_disable_guc(dev_priv);
intel_guc_ads_destroy(guc);
- intel_guc_log_destroy(guc);
+ intel_guc_log_destroy(&guc->log);
guc_shared_data_destroy(guc);
}
@@ -220,17 +223,23 @@ static u32 get_core_family(struct drm_i915_private *dev_priv)
}
}
-static u32 get_log_verbosity_flags(void)
+static u32 get_log_control_flags(void)
{
- if (i915_modparams.guc_log_level > 0) {
- u32 verbosity = i915_modparams.guc_log_level - 1;
+ u32 level = i915_modparams.guc_log_level;
+ u32 flags = 0;
- GEM_BUG_ON(verbosity > GUC_LOG_VERBOSITY_MAX);
- return verbosity << GUC_LOG_VERBOSITY_SHIFT;
- }
+ GEM_BUG_ON(level < 0);
+
+ if (!GUC_LOG_LEVEL_IS_ENABLED(level))
+ flags |= GUC_LOG_DEFAULT_DISABLED;
+
+ if (!GUC_LOG_LEVEL_IS_VERBOSE(level))
+ flags |= GUC_LOG_DISABLED;
+ else
+ flags |= GUC_LOG_LEVEL_TO_VERBOSITY(level) <<
+ GUC_LOG_VERBOSITY_SHIFT;
- GEM_BUG_ON(i915_modparams.enable_guc < 0);
- return GUC_LOG_DISABLED;
+ return flags;
}
/*
@@ -265,12 +274,13 @@ void intel_guc_init_params(struct intel_guc *guc)
params[GUC_CTL_LOG_PARAMS] = guc->log.flags;
- params[GUC_CTL_DEBUG] = get_log_verbosity_flags();
+ params[GUC_CTL_DEBUG] = get_log_control_flags();
/* If GuC submission is enabled, set up additional parameters here */
if (USES_GUC_SUBMISSION(dev_priv)) {
- u32 ads = guc_ggtt_offset(guc->ads_vma) >> PAGE_SHIFT;
- u32 pgs = guc_ggtt_offset(dev_priv->guc.stage_desc_pool);
+ u32 ads = intel_guc_ggtt_offset(guc,
+ guc->ads_vma) >> PAGE_SHIFT;
+ u32 pgs = intel_guc_ggtt_offset(guc, guc->stage_desc_pool);
u32 ctx_in_16 = GUC_MAX_STAGE_DESCRIPTORS / 16;
params[GUC_CTL_DEBUG] |= ads << GUC_ADS_ADDR_SHIFT;
@@ -301,16 +311,23 @@ void intel_guc_init_params(struct intel_guc *guc)
intel_uncore_forcewake_put(dev_priv, FORCEWAKE_BLITTER);
}
-int intel_guc_send_nop(struct intel_guc *guc, const u32 *action, u32 len)
+int intel_guc_send_nop(struct intel_guc *guc, const u32 *action, u32 len,
+ u32 *response_buf, u32 response_buf_size)
{
WARN(1, "Unexpected send: action=%#x\n", *action);
return -ENODEV;
}
+void intel_guc_to_host_event_handler_nop(struct intel_guc *guc)
+{
+ WARN(1, "Unexpected event: no suitable handler\n");
+}
+
/*
* This function implements the MMIO based host to GuC interface.
*/
-int intel_guc_send_mmio(struct intel_guc *guc, const u32 *action, u32 len)
+int intel_guc_send_mmio(struct intel_guc *guc, const u32 *action, u32 len,
+ u32 *response_buf, u32 response_buf_size)
{
struct drm_i915_private *dev_priv = guc_to_i915(guc);
u32 status;
@@ -320,6 +337,9 @@ int intel_guc_send_mmio(struct intel_guc *guc, const u32 *action, u32 len)
GEM_BUG_ON(!len);
GEM_BUG_ON(len > guc->send_regs.count);
+ /* We expect only action code */
+ GEM_BUG_ON(*action & ~INTEL_GUC_MSG_CODE_MASK);
+
/* If CT is available, we expect to use MMIO only during init/fini */
GEM_BUG_ON(HAS_GUC_CT(dev_priv) &&
*action != INTEL_GUC_ACTION_REGISTER_COMMAND_TRANSPORT_BUFFER &&
@@ -341,29 +361,74 @@ int intel_guc_send_mmio(struct intel_guc *guc, const u32 *action, u32 len)
*/
ret = __intel_wait_for_register_fw(dev_priv,
guc_send_reg(guc, 0),
- INTEL_GUC_RECV_MASK,
- INTEL_GUC_RECV_MASK,
+ INTEL_GUC_MSG_TYPE_MASK,
+ INTEL_GUC_MSG_TYPE_RESPONSE <<
+ INTEL_GUC_MSG_TYPE_SHIFT,
10, 10, &status);
- if (status != INTEL_GUC_STATUS_SUCCESS) {
- /*
- * Either the GuC explicitly returned an error (which
- * we convert to -EIO here) or no response at all was
- * received within the timeout limit (-ETIMEDOUT)
- */
- if (ret != -ETIMEDOUT)
- ret = -EIO;
-
- DRM_WARN("INTEL_GUC_SEND: Action 0x%X failed;"
- " ret=%d status=0x%08X response=0x%08X\n",
- action[0], ret, status, I915_READ(SOFT_SCRATCH(15)));
+ /* If GuC explicitly returned an error, convert it to -EIO */
+ if (!ret && !INTEL_GUC_MSG_IS_RESPONSE_SUCCESS(status))
+ ret = -EIO;
+
+ if (ret) {
+ DRM_DEBUG_DRIVER("INTEL_GUC_SEND: Action 0x%X failed;"
+ " ret=%d status=0x%08X response=0x%08X\n",
+ action[0], ret, status,
+ I915_READ(SOFT_SCRATCH(15)));
+ goto out;
}
+ if (response_buf) {
+ int count = min(response_buf_size, guc->send_regs.count - 1);
+
+ for (i = 0; i < count; i++)
+ response_buf[i] = I915_READ(guc_send_reg(guc, i + 1));
+ }
+
+ /* Use data from the GuC response as our return value */
+ ret = INTEL_GUC_MSG_TO_DATA(status);
+
+out:
intel_uncore_forcewake_put(dev_priv, guc->send_regs.fw_domains);
mutex_unlock(&guc->send_mutex);
return ret;
}
+void intel_guc_to_host_event_handler_mmio(struct intel_guc *guc)
+{
+ struct drm_i915_private *dev_priv = guc_to_i915(guc);
+ u32 msg, val;
+
+ /*
+ * Sample the log buffer flush related bits & clear them out now
+ * itself from the message identity register to minimize the
+ * probability of losing a flush interrupt, when there are back
+ * to back flush interrupts.
+ * There can be a new flush interrupt, for different log buffer
+ * type (like for ISR), whilst Host is handling one (for DPC).
+ * Since same bit is used in message register for ISR & DPC, it
+ * could happen that GuC sets the bit for 2nd interrupt but Host
+ * clears out the bit on handling the 1st interrupt.
+ */
+ spin_lock(&guc->irq_lock);
+ val = I915_READ(SOFT_SCRATCH(15));
+ msg = val & guc->msg_enabled_mask;
+ I915_WRITE(SOFT_SCRATCH(15), val & ~msg);
+ spin_unlock(&guc->irq_lock);
+
+ intel_guc_to_host_process_recv_msg(guc, msg);
+}
+
+void intel_guc_to_host_process_recv_msg(struct intel_guc *guc, u32 msg)
+{
+ /* Make sure to handle only enabled messages */
+ msg &= guc->msg_enabled_mask;
+
+ if (msg & (INTEL_GUC_RECV_MSG_FLUSH_LOG_BUFFER |
+ INTEL_GUC_RECV_MSG_CRASH_DUMP_POSTED))
+ intel_guc_log_handle_flush_event(&guc->log);
+}
+
int intel_guc_sample_forcewake(struct intel_guc *guc)
{
struct drm_i915_private *dev_priv = guc_to_i915(guc);
@@ -410,7 +475,7 @@ int intel_guc_suspend(struct intel_guc *guc)
u32 data[] = {
INTEL_GUC_ACTION_ENTER_S_STATE,
GUC_POWER_D1, /* any value greater than GUC_POWER_D0 */
- guc_ggtt_offset(guc->shared_data)
+ intel_guc_ggtt_offset(guc, guc->shared_data)
};
return intel_guc_send(guc, data, ARRAY_SIZE(data));
@@ -434,7 +499,7 @@ int intel_guc_reset_engine(struct intel_guc *guc,
data[3] = 0;
data[4] = 0;
data[5] = guc->execbuf_client->stage_id;
- data[6] = guc_ggtt_offset(guc->shared_data);
+ data[6] = intel_guc_ggtt_offset(guc, guc->shared_data);
return intel_guc_send(guc, data, ARRAY_SIZE(data));
}
@@ -448,13 +513,66 @@ int intel_guc_resume(struct intel_guc *guc)
u32 data[] = {
INTEL_GUC_ACTION_EXIT_S_STATE,
GUC_POWER_D0,
- guc_ggtt_offset(guc->shared_data)
+ intel_guc_ggtt_offset(guc, guc->shared_data)
};
return intel_guc_send(guc, data, ARRAY_SIZE(data));
}
/**
+ * DOC: GuC Address Space
+ *
+ * The layout of GuC address space is shown below:
+ *
+ * ::
+ *
+ * +==============> +====================+ <== GUC_GGTT_TOP
+ * ^ | |
+ * | | |
+ * | | DRAM |
+ * | | Memory |
+ * | | |
+ * GuC | |
+ * Address +========> +====================+ <== WOPCM Top
+ * Space ^ | HW contexts RSVD |
+ * | | | WOPCM |
+ * | | +==> +--------------------+ <== GuC WOPCM Top
+ * | GuC ^ | |
+ * | GGTT | | |
+ * | Pin GuC | GuC |
+ * | Bias WOPCM | WOPCM |
+ * | | Size | |
+ * | | | | |
+ * v v v | |
+ * +=====+=====+==> +====================+ <== GuC WOPCM Base
+ * | Non-GuC WOPCM |
+ * | (HuC/Reserved) |
+ * +====================+ <== WOPCM Base
+ *
+ * The lower part of GuC Address Space [0, ggtt_pin_bias) is mapped to WOPCM
+ * while upper part of GuC Address Space [ggtt_pin_bias, GUC_GGTT_TOP) is mapped
+ * to DRAM. The value of the GuC ggtt_pin_bias is determined by WOPCM size and
+ * actual GuC WOPCM size.
+ */
+
+/**
+ * intel_guc_init_ggtt_pin_bias() - Initialize the GuC ggtt_pin_bias value.
+ * @guc: intel_guc structure.
+ *
+ * This function will calculate and initialize the ggtt_pin_bias value based on
+ * overall WOPCM size and GuC WOPCM size.
+ */
+void intel_guc_init_ggtt_pin_bias(struct intel_guc *guc)
+{
+ struct drm_i915_private *i915 = guc_to_i915(guc);
+
+ GEM_BUG_ON(!i915->wopcm.size);
+ GEM_BUG_ON(i915->wopcm.size < i915->wopcm.guc.base);
+
+ guc->ggtt_pin_bias = i915->wopcm.size - i915->wopcm.guc.base;
+}
+
+/**
* intel_guc_allocate_vma() - Allocate a GGTT VMA for GuC usage
* @guc: the guc
* @size: size of area to allocate (both virtual space and memory)
@@ -462,7 +580,7 @@ int intel_guc_resume(struct intel_guc *guc)
* This is a wrapper to create an object for use with the GuC. In order to
* use it inside the GuC, an object needs to be pinned lifetime, so we allocate
* both some backing storage and a range inside the Global GTT. We must pin
- * it in the GGTT somewhere other than than [0, GUC_WOPCM_TOP) because that
+ * it in the GGTT somewhere other than than [0, GUC ggtt_pin_bias) because that
* range is reserved inside GuC.
*
* Return: A i915_vma if successful, otherwise an ERR_PTR.
@@ -483,7 +601,7 @@ struct i915_vma *intel_guc_allocate_vma(struct intel_guc *guc, u32 size)
goto err;
ret = i915_vma_pin(vma, 0, PAGE_SIZE,
- PIN_GLOBAL | PIN_OFFSET_BIAS | GUC_WOPCM_TOP);
+ PIN_GLOBAL | PIN_OFFSET_BIAS | guc->ggtt_pin_bias);
if (ret) {
vma = ERR_PTR(ret);
goto err;
@@ -495,14 +613,3 @@ err:
i915_gem_object_put(obj);
return vma;
}
-
-u32 intel_guc_wopcm_size(struct drm_i915_private *dev_priv)
-{
- u32 wopcm_size = GUC_WOPCM_TOP;
-
- /* On BXT, the top of WOPCM is reserved for RC6 context */
- if (IS_GEN9_LP(dev_priv))
- wopcm_size -= BXT_GUC_WOPCM_RC6_RESERVED;
-
- return wopcm_size;
-}
diff --git a/drivers/gpu/drm/i915/intel_guc.h b/drivers/gpu/drm/i915/intel_guc.h
index b9424ac644ac..f1265e122d30 100644
--- a/drivers/gpu/drm/i915/intel_guc.h
+++ b/drivers/gpu/drm/i915/intel_guc.h
@@ -49,11 +49,16 @@ struct intel_guc {
struct intel_guc_log log;
struct intel_guc_ct ct;
+ /* Offset where Non-WOPCM memory starts. */
+ u32 ggtt_pin_bias;
+
/* Log snapshot if GuC errors during load */
struct drm_i915_gem_object *load_err_log;
/* intel_guc_recv interrupt related state */
+ spinlock_t irq_lock;
bool interrupts_enabled;
+ unsigned int msg_enabled_mask;
struct i915_vma *ads_vma;
struct i915_vma *stage_desc_pool;
@@ -83,7 +88,11 @@ struct intel_guc {
struct mutex send_mutex;
/* GuC's FW specific send function */
- int (*send)(struct intel_guc *guc, const u32 *data, u32 len);
+ int (*send)(struct intel_guc *guc, const u32 *data, u32 len,
+ u32 *response_buf, u32 response_buf_size);
+
+ /* GuC's FW specific event handler function */
+ void (*handler)(struct intel_guc *guc);
/* GuC's FW specific notify function */
void (*notify)(struct intel_guc *guc);
@@ -92,7 +101,14 @@ struct intel_guc {
static
inline int intel_guc_send(struct intel_guc *guc, const u32 *action, u32 len)
{
- return guc->send(guc, action, len);
+ return guc->send(guc, action, len, NULL, 0);
+}
+
+static inline int
+intel_guc_send_and_receive(struct intel_guc *guc, const u32 *action, u32 len,
+ u32 *response_buf, u32 response_buf_size)
+{
+ return guc->send(guc, action, len, response_buf, response_buf_size);
}
static inline void intel_guc_notify(struct intel_guc *guc)
@@ -100,17 +116,33 @@ static inline void intel_guc_notify(struct intel_guc *guc)
guc->notify(guc);
}
-/*
- * GuC does not allow any gfx GGTT address that falls into range [0, WOPCM_TOP),
- * which is reserved for Boot ROM, SRAM and WOPCM. Currently this top address is
- * 512K. In order to exclude 0-512K address space from GGTT, all gfx objects
- * used by GuC is pinned with PIN_OFFSET_BIAS along with size of WOPCM.
+static inline void intel_guc_to_host_event_handler(struct intel_guc *guc)
+{
+ guc->handler(guc);
+}
+
+/* GuC addresses above GUC_GGTT_TOP also don't map through the GTT */
+#define GUC_GGTT_TOP 0xFEE00000
+
+/**
+ * intel_guc_ggtt_offset() - Get and validate the GGTT offset of @vma
+ * @guc: intel_guc structure.
+ * @vma: i915 graphics virtual memory area.
+ *
+ * GuC does not allow any gfx GGTT address that falls into range
+ * [0, GuC ggtt_pin_bias), which is reserved for Boot ROM, SRAM and WOPCM.
+ * Currently, in order to exclude [0, GuC ggtt_pin_bias) address space from
+ * GGTT, all gfx objects used by GuC are allocated with intel_guc_allocate_vma()
+ * and pinned with PIN_OFFSET_BIAS along with the value of GuC ggtt_pin_bias.
+ *
+ * Return: GGTT offset of the @vma.
*/
-static inline u32 guc_ggtt_offset(struct i915_vma *vma)
+static inline u32 intel_guc_ggtt_offset(struct intel_guc *guc,
+ struct i915_vma *vma)
{
u32 offset = i915_ggtt_offset(vma);
- GEM_BUG_ON(offset < GUC_WOPCM_TOP);
+ GEM_BUG_ON(offset < guc->ggtt_pin_bias);
GEM_BUG_ON(range_overflows_t(u64, offset, vma->size, GUC_GGTT_TOP));
return offset;
@@ -119,17 +151,43 @@ static inline u32 guc_ggtt_offset(struct i915_vma *vma)
void intel_guc_init_early(struct intel_guc *guc);
void intel_guc_init_send_regs(struct intel_guc *guc);
void intel_guc_init_params(struct intel_guc *guc);
+void intel_guc_init_ggtt_pin_bias(struct intel_guc *guc);
int intel_guc_init_wq(struct intel_guc *guc);
void intel_guc_fini_wq(struct intel_guc *guc);
int intel_guc_init(struct intel_guc *guc);
void intel_guc_fini(struct intel_guc *guc);
-int intel_guc_send_nop(struct intel_guc *guc, const u32 *action, u32 len);
-int intel_guc_send_mmio(struct intel_guc *guc, const u32 *action, u32 len);
+int intel_guc_send_nop(struct intel_guc *guc, const u32 *action, u32 len,
+ u32 *response_buf, u32 response_buf_size);
+int intel_guc_send_mmio(struct intel_guc *guc, const u32 *action, u32 len,
+ u32 *response_buf, u32 response_buf_size);
+void intel_guc_to_host_event_handler(struct intel_guc *guc);
+void intel_guc_to_host_event_handler_nop(struct intel_guc *guc);
+void intel_guc_to_host_event_handler_mmio(struct intel_guc *guc);
+void intel_guc_to_host_process_recv_msg(struct intel_guc *guc, u32 msg);
int intel_guc_sample_forcewake(struct intel_guc *guc);
int intel_guc_auth_huc(struct intel_guc *guc, u32 rsa_offset);
int intel_guc_suspend(struct intel_guc *guc);
int intel_guc_resume(struct intel_guc *guc);
struct i915_vma *intel_guc_allocate_vma(struct intel_guc *guc, u32 size);
-u32 intel_guc_wopcm_size(struct drm_i915_private *dev_priv);
+
+static inline int intel_guc_sanitize(struct intel_guc *guc)
+{
+ intel_uc_fw_sanitize(&guc->fw);
+ return 0;
+}
+
+static inline void intel_guc_enable_msg(struct intel_guc *guc, u32 mask)
+{
+ spin_lock_irq(&guc->irq_lock);
+ guc->msg_enabled_mask |= mask;
+ spin_unlock_irq(&guc->irq_lock);
+}
+
+static inline void intel_guc_disable_msg(struct intel_guc *guc, u32 mask)
+{
+ spin_lock_irq(&guc->irq_lock);
+ guc->msg_enabled_mask &= ~mask;
+ spin_unlock_irq(&guc->irq_lock);
+}
#endif
diff --git a/drivers/gpu/drm/i915/intel_guc_ads.c b/drivers/gpu/drm/i915/intel_guc_ads.c
index ac627534667d..334cb5202e1c 100644
--- a/drivers/gpu/drm/i915/intel_guc_ads.c
+++ b/drivers/gpu/drm/i915/intel_guc_ads.c
@@ -75,7 +75,7 @@ static void guc_policies_init(struct guc_policies *policies)
int intel_guc_ads_create(struct intel_guc *guc)
{
struct drm_i915_private *dev_priv = guc_to_i915(guc);
- struct i915_vma *vma;
+ struct i915_vma *vma, *kernel_ctx_vma;
struct page *page;
/* The ads obj includes the struct itself and buffers passed to GuC */
struct {
@@ -121,9 +121,9 @@ int intel_guc_ads_create(struct intel_guc *guc)
* to find it. Note that we have to skip our header (1 page),
* because our GuC shared data is there.
*/
+ kernel_ctx_vma = dev_priv->kernel_context->engine[RCS].state;
blob->ads.golden_context_lrca =
- guc_ggtt_offset(dev_priv->kernel_context->engine[RCS].state) +
- skipped_offset;
+ intel_guc_ggtt_offset(guc, kernel_ctx_vma) + skipped_offset;
/*
* The GuC expects us to exclude the portion of the context image that
@@ -135,7 +135,7 @@ int intel_guc_ads_create(struct intel_guc *guc)
blob->ads.eng_state_size[engine->guc_id] =
engine->context_size - skipped_size;
- base = guc_ggtt_offset(vma);
+ base = intel_guc_ggtt_offset(guc, vma);
blob->ads.scheduler_policies = base + ptr_offset(blob, policies);
blob->ads.reg_state_buffer = base + ptr_offset(blob, reg_state_buffer);
blob->ads.reg_state_addr = base + ptr_offset(blob, reg_state);
diff --git a/drivers/gpu/drm/i915/intel_guc_ct.c b/drivers/gpu/drm/i915/intel_guc_ct.c
index 24ad55752396..371b6005954a 100644
--- a/drivers/gpu/drm/i915/intel_guc_ct.c
+++ b/drivers/gpu/drm/i915/intel_guc_ct.c
@@ -24,14 +24,49 @@
#include "i915_drv.h"
#include "intel_guc_ct.h"
+#ifdef CONFIG_DRM_I915_DEBUG_GUC
+#define CT_DEBUG_DRIVER(...) DRM_DEBUG_DRIVER(__VA_ARGS__)
+#else
+#define CT_DEBUG_DRIVER(...) do { } while (0)
+#endif
+
+struct ct_request {
+ struct list_head link;
+ u32 fence;
+ u32 status;
+ u32 response_len;
+ u32 *response_buf;
+};
+
+struct ct_incoming_request {
+ struct list_head link;
+ u32 msg[];
+};
+
enum { CTB_SEND = 0, CTB_RECV = 1 };
enum { CTB_OWNER_HOST = 0 };
+static void ct_incoming_request_worker_func(struct work_struct *w);
+
+/**
+ * intel_guc_ct_init_early - Initialize CT state without requiring device access
+ * @ct: pointer to CT struct
+ */
void intel_guc_ct_init_early(struct intel_guc_ct *ct)
{
/* we're using static channel owners */
ct->host_channel.owner = CTB_OWNER_HOST;
+
+ spin_lock_init(&ct->lock);
+ INIT_LIST_HEAD(&ct->pending_requests);
+ INIT_LIST_HEAD(&ct->incoming_requests);
+ INIT_WORK(&ct->worker, ct_incoming_request_worker_func);
+}
+
+static inline struct intel_guc *ct_to_guc(struct intel_guc_ct *ct)
+{
+ return container_of(ct, struct intel_guc, ct);
}
static inline const char *guc_ct_buffer_type_to_str(u32 type)
@@ -49,8 +84,8 @@ static inline const char *guc_ct_buffer_type_to_str(u32 type)
static void guc_ct_buffer_desc_init(struct guc_ct_buffer_desc *desc,
u32 cmds_addr, u32 size, u32 owner)
{
- DRM_DEBUG_DRIVER("CT: desc %p init addr=%#x size=%u owner=%u\n",
- desc, cmds_addr, size, owner);
+ CT_DEBUG_DRIVER("CT: desc %p init addr=%#x size=%u owner=%u\n",
+ desc, cmds_addr, size, owner);
memset(desc, 0, sizeof(*desc));
desc->addr = cmds_addr;
desc->size = size;
@@ -59,8 +94,8 @@ static void guc_ct_buffer_desc_init(struct guc_ct_buffer_desc *desc,
static void guc_ct_buffer_desc_reset(struct guc_ct_buffer_desc *desc)
{
- DRM_DEBUG_DRIVER("CT: desc %p reset head=%u tail=%u\n",
- desc, desc->head, desc->tail);
+ CT_DEBUG_DRIVER("CT: desc %p reset head=%u tail=%u\n",
+ desc, desc->head, desc->tail);
desc->head = 0;
desc->tail = 0;
desc->is_in_error = 0;
@@ -79,7 +114,7 @@ static int guc_action_register_ct_buffer(struct intel_guc *guc,
int err;
/* Can't use generic send(), CT registration must go over MMIO */
- err = intel_guc_send_mmio(guc, action, ARRAY_SIZE(action));
+ err = intel_guc_send_mmio(guc, action, ARRAY_SIZE(action), NULL, 0);
if (err)
DRM_ERROR("CT: register %s buffer failed; err=%d\n",
guc_ct_buffer_type_to_str(type), err);
@@ -98,7 +133,7 @@ static int guc_action_deregister_ct_buffer(struct intel_guc *guc,
int err;
/* Can't use generic send(), CT deregistration must go over MMIO */
- err = intel_guc_send_mmio(guc, action, ARRAY_SIZE(action));
+ err = intel_guc_send_mmio(guc, action, ARRAY_SIZE(action), NULL, 0);
if (err)
DRM_ERROR("CT: deregister %s buffer failed; owner=%d err=%d\n",
guc_ct_buffer_type_to_str(type), owner, err);
@@ -156,7 +191,8 @@ static int ctch_init(struct intel_guc *guc,
err = PTR_ERR(blob);
goto err_vma;
}
- DRM_DEBUG_DRIVER("CT: vma base=%#x\n", guc_ggtt_offset(ctch->vma));
+ CT_DEBUG_DRIVER("CT: vma base=%#x\n",
+ intel_guc_ggtt_offset(guc, ctch->vma));
/* store pointers to desc and cmds */
for (i = 0; i < ARRAY_SIZE(ctch->ctbs); i++) {
@@ -170,8 +206,8 @@ static int ctch_init(struct intel_guc *guc,
err_vma:
i915_vma_unpin_and_release(&ctch->vma);
err_out:
- DRM_DEBUG_DRIVER("CT: channel %d initialization failed; err=%d\n",
- ctch->owner, err);
+ CT_DEBUG_DRIVER("CT: channel %d initialization failed; err=%d\n",
+ ctch->owner, err);
return err;
}
@@ -191,8 +227,8 @@ static int ctch_open(struct intel_guc *guc,
int err;
int i;
- DRM_DEBUG_DRIVER("CT: channel %d reopen=%s\n",
- ctch->owner, yesno(ctch_is_open(ctch)));
+ CT_DEBUG_DRIVER("CT: channel %d reopen=%s\n",
+ ctch->owner, yesno(ctch_is_open(ctch)));
if (!ctch->vma) {
err = ctch_init(guc, ctch);
@@ -202,7 +238,7 @@ static int ctch_open(struct intel_guc *guc,
}
/* vma should be already allocated and map'ed */
- base = guc_ggtt_offset(ctch->vma);
+ base = intel_guc_ggtt_offset(guc, ctch->vma);
/* (re)initialize descriptors
* cmds buffers are in the second half of the blob page
@@ -263,10 +299,29 @@ static u32 ctch_get_next_fence(struct intel_guc_ct_channel *ctch)
return ++ctch->next_fence;
}
+/**
+ * DOC: CTB Host to GuC request
+ *
+ * Format of the CTB Host to GuC request message is as follows::
+ *
+ * +------------+---------+---------+---------+---------+
+ * | msg[0] | [1] | [2] | ... | [n-1] |
+ * +------------+---------+---------+---------+---------+
+ * | MESSAGE | MESSAGE PAYLOAD |
+ * + HEADER +---------+---------+---------+---------+
+ * | | 0 | 1 | ... | n |
+ * +============+=========+=========+=========+=========+
+ * | len >= 1 | FENCE | request specific data |
+ * +------+-----+---------+---------+---------+---------+
+ *
+ * ^-----------------len-------------------^
+ */
+
static int ctb_write(struct intel_guc_ct_buffer *ctb,
const u32 *action,
u32 len /* in dwords */,
- u32 fence)
+ u32 fence,
+ bool want_response)
{
struct guc_ct_buffer_desc *desc = ctb->desc;
u32 head = desc->head / 4; /* in dwords */
@@ -295,15 +350,21 @@ static int ctb_write(struct intel_guc_ct_buffer *ctb,
if (unlikely(used + len + 1 >= size))
return -ENOSPC;
- /* Write the message. The format is the following:
+ /*
+ * Write the message. The format is the following:
* DW0: header (including action code)
* DW1: fence
* DW2+: action data
*/
header = (len << GUC_CT_MSG_LEN_SHIFT) |
(GUC_CT_MSG_WRITE_FENCE_TO_DESC) |
+ (want_response ? GUC_CT_MSG_SEND_STATUS : 0) |
(action[0] << GUC_CT_MSG_ACTION_SHIFT);
+ CT_DEBUG_DRIVER("CT: writing %*ph %*ph %*ph\n",
+ 4, &header, 4, &fence,
+ 4 * (len - 1), &action[1]);
+
cmds[tail] = header;
tail = (tail + 1) % size;
@@ -322,16 +383,25 @@ static int ctb_write(struct intel_guc_ct_buffer *ctb,
return 0;
}
-/* Wait for the response from the GuC.
+/**
+ * wait_for_ctb_desc_update - Wait for the CT buffer descriptor update.
+ * @desc: buffer descriptor
* @fence: response fence
* @status: placeholder for status
- * return: 0 response received (status is valid)
- * -ETIMEDOUT no response within hardcoded timeout
- * -EPROTO no response, ct buffer was in error
+ *
+ * Guc will update CT buffer descriptor with new fence and status
+ * after processing the command identified by the fence. Wait for
+ * specified fence and then read from the descriptor status of the
+ * command.
+ *
+ * Return:
+ * * 0 response received (status is valid)
+ * * -ETIMEDOUT no response within hardcoded timeout
+ * * -EPROTO no response, CT buffer is in error
*/
-static int wait_for_response(struct guc_ct_buffer_desc *desc,
- u32 fence,
- u32 *status)
+static int wait_for_ctb_desc_update(struct guc_ct_buffer_desc *desc,
+ u32 fence,
+ u32 *status)
{
int err;
@@ -363,71 +433,440 @@ static int wait_for_response(struct guc_ct_buffer_desc *desc,
return err;
}
-static int ctch_send(struct intel_guc *guc,
+/**
+ * wait_for_ct_request_update - Wait for CT request state update.
+ * @req: pointer to pending request
+ * @status: placeholder for status
+ *
+ * For each sent request, Guc shall send bac CT response message.
+ * Our message handler will update status of tracked request once
+ * response message with given fence is received. Wait here and
+ * check for valid response status value.
+ *
+ * Return:
+ * * 0 response received (status is valid)
+ * * -ETIMEDOUT no response within hardcoded timeout
+ */
+static int wait_for_ct_request_update(struct ct_request *req, u32 *status)
+{
+ int err;
+
+ /*
+ * Fast commands should complete in less than 10us, so sample quickly
+ * up to that length of time, then switch to a slower sleep-wait loop.
+ * No GuC command should ever take longer than 10ms.
+ */
+#define done INTEL_GUC_MSG_IS_RESPONSE(READ_ONCE(req->status))
+ err = wait_for_us(done, 10);
+ if (err)
+ err = wait_for(done, 10);
+#undef done
+
+ if (unlikely(err))
+ DRM_ERROR("CT: fence %u err %d\n", req->fence, err);
+
+ *status = req->status;
+ return err;
+}
+
+static int ctch_send(struct intel_guc_ct *ct,
struct intel_guc_ct_channel *ctch,
const u32 *action,
u32 len,
+ u32 *response_buf,
+ u32 response_buf_size,
u32 *status)
{
struct intel_guc_ct_buffer *ctb = &ctch->ctbs[CTB_SEND];
struct guc_ct_buffer_desc *desc = ctb->desc;
+ struct ct_request request;
+ unsigned long flags;
u32 fence;
int err;
GEM_BUG_ON(!ctch_is_open(ctch));
GEM_BUG_ON(!len);
GEM_BUG_ON(len & ~GUC_CT_MSG_LEN_MASK);
+ GEM_BUG_ON(!response_buf && response_buf_size);
fence = ctch_get_next_fence(ctch);
- err = ctb_write(ctb, action, len, fence);
+ request.fence = fence;
+ request.status = 0;
+ request.response_len = response_buf_size;
+ request.response_buf = response_buf;
+
+ spin_lock_irqsave(&ct->lock, flags);
+ list_add_tail(&request.link, &ct->pending_requests);
+ spin_unlock_irqrestore(&ct->lock, flags);
+
+ err = ctb_write(ctb, action, len, fence, !!response_buf);
if (unlikely(err))
- return err;
+ goto unlink;
- intel_guc_notify(guc);
+ intel_guc_notify(ct_to_guc(ct));
- err = wait_for_response(desc, fence, status);
+ if (response_buf)
+ err = wait_for_ct_request_update(&request, status);
+ else
+ err = wait_for_ctb_desc_update(desc, fence, status);
if (unlikely(err))
- return err;
- if (*status != INTEL_GUC_STATUS_SUCCESS)
- return -EIO;
- return 0;
+ goto unlink;
+
+ if (!INTEL_GUC_MSG_IS_RESPONSE_SUCCESS(*status)) {
+ err = -EIO;
+ goto unlink;
+ }
+
+ if (response_buf) {
+ /* There shall be no data in the status */
+ WARN_ON(INTEL_GUC_MSG_TO_DATA(request.status));
+ /* Return actual response len */
+ err = request.response_len;
+ } else {
+ /* There shall be no response payload */
+ WARN_ON(request.response_len);
+ /* Return data decoded from the status dword */
+ err = INTEL_GUC_MSG_TO_DATA(*status);
+ }
+
+unlink:
+ spin_lock_irqsave(&ct->lock, flags);
+ list_del(&request.link);
+ spin_unlock_irqrestore(&ct->lock, flags);
+
+ return err;
}
/*
* Command Transport (CT) buffer based GuC send function.
*/
-static int intel_guc_send_ct(struct intel_guc *guc, const u32 *action, u32 len)
+static int intel_guc_send_ct(struct intel_guc *guc, const u32 *action, u32 len,
+ u32 *response_buf, u32 response_buf_size)
{
- struct intel_guc_ct_channel *ctch = &guc->ct.host_channel;
+ struct intel_guc_ct *ct = &guc->ct;
+ struct intel_guc_ct_channel *ctch = &ct->host_channel;
u32 status = ~0; /* undefined */
- int err;
+ int ret;
mutex_lock(&guc->send_mutex);
- err = ctch_send(guc, ctch, action, len, &status);
- if (unlikely(err)) {
+ ret = ctch_send(ct, ctch, action, len, response_buf, response_buf_size,
+ &status);
+ if (unlikely(ret < 0)) {
DRM_ERROR("CT: send action %#X failed; err=%d status=%#X\n",
- action[0], err, status);
+ action[0], ret, status);
+ } else if (unlikely(ret)) {
+ CT_DEBUG_DRIVER("CT: send action %#x returned %d (%#x)\n",
+ action[0], ret, ret);
}
mutex_unlock(&guc->send_mutex);
- return err;
+ return ret;
+}
+
+static inline unsigned int ct_header_get_len(u32 header)
+{
+ return (header >> GUC_CT_MSG_LEN_SHIFT) & GUC_CT_MSG_LEN_MASK;
+}
+
+static inline unsigned int ct_header_get_action(u32 header)
+{
+ return (header >> GUC_CT_MSG_ACTION_SHIFT) & GUC_CT_MSG_ACTION_MASK;
+}
+
+static inline bool ct_header_is_response(u32 header)
+{
+ return ct_header_get_action(header) == INTEL_GUC_ACTION_DEFAULT;
+}
+
+static int ctb_read(struct intel_guc_ct_buffer *ctb, u32 *data)
+{
+ struct guc_ct_buffer_desc *desc = ctb->desc;
+ u32 head = desc->head / 4; /* in dwords */
+ u32 tail = desc->tail / 4; /* in dwords */
+ u32 size = desc->size / 4; /* in dwords */
+ u32 *cmds = ctb->cmds;
+ s32 available; /* in dwords */
+ unsigned int len;
+ unsigned int i;
+
+ GEM_BUG_ON(desc->size % 4);
+ GEM_BUG_ON(desc->head % 4);
+ GEM_BUG_ON(desc->tail % 4);
+ GEM_BUG_ON(tail >= size);
+ GEM_BUG_ON(head >= size);
+
+ /* tail == head condition indicates empty */
+ available = tail - head;
+ if (unlikely(available == 0))
+ return -ENODATA;
+
+ /* beware of buffer wrap case */
+ if (unlikely(available < 0))
+ available += size;
+ CT_DEBUG_DRIVER("CT: available %d (%u:%u)\n", available, head, tail);
+ GEM_BUG_ON(available < 0);
+
+ data[0] = cmds[head];
+ head = (head + 1) % size;
+
+ /* message len with header */
+ len = ct_header_get_len(data[0]) + 1;
+ if (unlikely(len > (u32)available)) {
+ DRM_ERROR("CT: incomplete message %*ph %*ph %*ph\n",
+ 4, data,
+ 4 * (head + available - 1 > size ?
+ size - head : available - 1), &cmds[head],
+ 4 * (head + available - 1 > size ?
+ available - 1 - size + head : 0), &cmds[0]);
+ return -EPROTO;
+ }
+
+ for (i = 1; i < len; i++) {
+ data[i] = cmds[head];
+ head = (head + 1) % size;
+ }
+ CT_DEBUG_DRIVER("CT: received %*ph\n", 4 * len, data);
+
+ desc->head = head * 4;
+ return 0;
}
/**
- * Enable buffer based command transport
+ * DOC: CTB GuC to Host response
+ *
+ * Format of the CTB GuC to Host response message is as follows::
+ *
+ * +------------+---------+---------+---------+---------+---------+
+ * | msg[0] | [1] | [2] | [3] | ... | [n-1] |
+ * +------------+---------+---------+---------+---------+---------+
+ * | MESSAGE | MESSAGE PAYLOAD |
+ * + HEADER +---------+---------+---------+---------+---------+
+ * | | 0 | 1 | 2 | ... | n |
+ * +============+=========+=========+=========+=========+=========+
+ * | len >= 2 | FENCE | STATUS | response specific data |
+ * +------+-----+---------+---------+---------+---------+---------+
+ *
+ * ^-----------------------len-----------------------^
+ */
+
+static int ct_handle_response(struct intel_guc_ct *ct, const u32 *msg)
+{
+ u32 header = msg[0];
+ u32 len = ct_header_get_len(header);
+ u32 msglen = len + 1; /* total message length including header */
+ u32 fence;
+ u32 status;
+ u32 datalen;
+ struct ct_request *req;
+ bool found = false;
+
+ GEM_BUG_ON(!ct_header_is_response(header));
+ GEM_BUG_ON(!in_irq());
+
+ /* Response payload shall at least include fence and status */
+ if (unlikely(len < 2)) {
+ DRM_ERROR("CT: corrupted response %*ph\n", 4 * msglen, msg);
+ return -EPROTO;
+ }
+
+ fence = msg[1];
+ status = msg[2];
+ datalen = len - 2;
+
+ /* Format of the status follows RESPONSE message */
+ if (unlikely(!INTEL_GUC_MSG_IS_RESPONSE(status))) {
+ DRM_ERROR("CT: corrupted response %*ph\n", 4 * msglen, msg);
+ return -EPROTO;
+ }
+
+ CT_DEBUG_DRIVER("CT: response fence %u status %#x\n", fence, status);
+
+ spin_lock(&ct->lock);
+ list_for_each_entry(req, &ct->pending_requests, link) {
+ if (unlikely(fence != req->fence)) {
+ CT_DEBUG_DRIVER("CT: request %u awaits response\n",
+ req->fence);
+ continue;
+ }
+ if (unlikely(datalen > req->response_len)) {
+ DRM_ERROR("CT: response %u too long %*ph\n",
+ req->fence, 4 * msglen, msg);
+ datalen = 0;
+ }
+ if (datalen)
+ memcpy(req->response_buf, msg + 3, 4 * datalen);
+ req->response_len = datalen;
+ WRITE_ONCE(req->status, status);
+ found = true;
+ break;
+ }
+ spin_unlock(&ct->lock);
+
+ if (!found)
+ DRM_ERROR("CT: unsolicited response %*ph\n", 4 * msglen, msg);
+ return 0;
+}
+
+static void ct_process_request(struct intel_guc_ct *ct,
+ u32 action, u32 len, const u32 *payload)
+{
+ struct intel_guc *guc = ct_to_guc(ct);
+
+ CT_DEBUG_DRIVER("CT: request %x %*ph\n", action, 4 * len, payload);
+
+ switch (action) {
+ case INTEL_GUC_ACTION_DEFAULT:
+ if (unlikely(len < 1))
+ goto fail_unexpected;
+ intel_guc_to_host_process_recv_msg(guc, *payload);
+ break;
+
+ default:
+fail_unexpected:
+ DRM_ERROR("CT: unexpected request %x %*ph\n",
+ action, 4 * len, payload);
+ break;
+ }
+}
+
+static bool ct_process_incoming_requests(struct intel_guc_ct *ct)
+{
+ unsigned long flags;
+ struct ct_incoming_request *request;
+ u32 header;
+ u32 *payload;
+ bool done;
+
+ spin_lock_irqsave(&ct->lock, flags);
+ request = list_first_entry_or_null(&ct->incoming_requests,
+ struct ct_incoming_request, link);
+ if (request)
+ list_del(&request->link);
+ done = !!list_empty(&ct->incoming_requests);
+ spin_unlock_irqrestore(&ct->lock, flags);
+
+ if (!request)
+ return true;
+
+ header = request->msg[0];
+ payload = &request->msg[1];
+ ct_process_request(ct,
+ ct_header_get_action(header),
+ ct_header_get_len(header),
+ payload);
+
+ kfree(request);
+ return done;
+}
+
+static void ct_incoming_request_worker_func(struct work_struct *w)
+{
+ struct intel_guc_ct *ct = container_of(w, struct intel_guc_ct, worker);
+ bool done;
+
+ done = ct_process_incoming_requests(ct);
+ if (!done)
+ queue_work(system_unbound_wq, &ct->worker);
+}
+
+/**
+ * DOC: CTB GuC to Host request
+ *
+ * Format of the CTB GuC to Host request message is as follows::
+ *
+ * +------------+---------+---------+---------+---------+---------+
+ * | msg[0] | [1] | [2] | [3] | ... | [n-1] |
+ * +------------+---------+---------+---------+---------+---------+
+ * | MESSAGE | MESSAGE PAYLOAD |
+ * + HEADER +---------+---------+---------+---------+---------+
+ * | | 0 | 1 | 2 | ... | n |
+ * +============+=========+=========+=========+=========+=========+
+ * | len | request specific data |
+ * +------+-----+---------+---------+---------+---------+---------+
+ *
+ * ^-----------------------len-----------------------^
+ */
+
+static int ct_handle_request(struct intel_guc_ct *ct, const u32 *msg)
+{
+ u32 header = msg[0];
+ u32 len = ct_header_get_len(header);
+ u32 msglen = len + 1; /* total message length including header */
+ struct ct_incoming_request *request;
+ unsigned long flags;
+
+ GEM_BUG_ON(ct_header_is_response(header));
+
+ request = kmalloc(sizeof(*request) + 4 * msglen, GFP_ATOMIC);
+ if (unlikely(!request)) {
+ DRM_ERROR("CT: dropping request %*ph\n", 4 * msglen, msg);
+ return 0; /* XXX: -ENOMEM ? */
+ }
+ memcpy(request->msg, msg, 4 * msglen);
+
+ spin_lock_irqsave(&ct->lock, flags);
+ list_add_tail(&request->link, &ct->incoming_requests);
+ spin_unlock_irqrestore(&ct->lock, flags);
+
+ queue_work(system_unbound_wq, &ct->worker);
+ return 0;
+}
+
+static void ct_process_host_channel(struct intel_guc_ct *ct)
+{
+ struct intel_guc_ct_channel *ctch = &ct->host_channel;
+ struct intel_guc_ct_buffer *ctb = &ctch->ctbs[CTB_RECV];
+ u32 msg[GUC_CT_MSG_LEN_MASK + 1]; /* one extra dw for the header */
+ int err = 0;
+
+ if (!ctch_is_open(ctch))
+ return;
+
+ do {
+ err = ctb_read(ctb, msg);
+ if (err)
+ break;
+
+ if (ct_header_is_response(msg[0]))
+ err = ct_handle_response(ct, msg);
+ else
+ err = ct_handle_request(ct, msg);
+ } while (!err);
+
+ if (GEM_WARN_ON(err == -EPROTO)) {
+ DRM_ERROR("CT: corrupted message detected!\n");
+ ctb->desc->is_in_error = 1;
+ }
+}
+
+/*
+ * When we're communicating with the GuC over CT, GuC uses events
+ * to notify us about new messages being posted on the RECV buffer.
+ */
+static void intel_guc_to_host_event_handler_ct(struct intel_guc *guc)
+{
+ struct intel_guc_ct *ct = &guc->ct;
+
+ ct_process_host_channel(ct);
+}
+
+/**
+ * intel_guc_ct_enable - Enable buffer based command transport.
+ * @ct: pointer to CT struct
+ *
* Shall only be called for platforms with HAS_GUC_CT.
- * @guc: the guc
- * return: 0 on success
- * non-zero on failure
+ *
+ * Return: 0 on success, a negative errno code on failure.
*/
-int intel_guc_enable_ct(struct intel_guc *guc)
+int intel_guc_ct_enable(struct intel_guc_ct *ct)
{
- struct drm_i915_private *dev_priv = guc_to_i915(guc);
- struct intel_guc_ct_channel *ctch = &guc->ct.host_channel;
+ struct intel_guc *guc = ct_to_guc(ct);
+ struct drm_i915_private *i915 = guc_to_i915(guc);
+ struct intel_guc_ct_channel *ctch = &ct->host_channel;
int err;
- GEM_BUG_ON(!HAS_GUC_CT(dev_priv));
+ GEM_BUG_ON(!HAS_GUC_CT(i915));
err = ctch_open(guc, ctch);
if (unlikely(err))
@@ -435,21 +874,24 @@ int intel_guc_enable_ct(struct intel_guc *guc)
/* Switch into cmd transport buffer based send() */
guc->send = intel_guc_send_ct;
+ guc->handler = intel_guc_to_host_event_handler_ct;
DRM_INFO("CT: %s\n", enableddisabled(true));
return 0;
}
/**
- * Disable buffer based command transport.
+ * intel_guc_ct_disable - Disable buffer based command transport.
+ * @ct: pointer to CT struct
+ *
* Shall only be called for platforms with HAS_GUC_CT.
- * @guc: the guc
*/
-void intel_guc_disable_ct(struct intel_guc *guc)
+void intel_guc_ct_disable(struct intel_guc_ct *ct)
{
- struct drm_i915_private *dev_priv = guc_to_i915(guc);
- struct intel_guc_ct_channel *ctch = &guc->ct.host_channel;
+ struct intel_guc *guc = ct_to_guc(ct);
+ struct drm_i915_private *i915 = guc_to_i915(guc);
+ struct intel_guc_ct_channel *ctch = &ct->host_channel;
- GEM_BUG_ON(!HAS_GUC_CT(dev_priv));
+ GEM_BUG_ON(!HAS_GUC_CT(i915));
if (!ctch_is_open(ctch))
return;
@@ -458,5 +900,6 @@ void intel_guc_disable_ct(struct intel_guc *guc)
/* Disable send */
guc->send = intel_guc_send_nop;
+ guc->handler = intel_guc_to_host_event_handler_nop;
DRM_INFO("CT: %s\n", enableddisabled(false));
}
diff --git a/drivers/gpu/drm/i915/intel_guc_ct.h b/drivers/gpu/drm/i915/intel_guc_ct.h
index 6d97f36fcc62..d774895ab143 100644
--- a/drivers/gpu/drm/i915/intel_guc_ct.h
+++ b/drivers/gpu/drm/i915/intel_guc_ct.h
@@ -75,12 +75,22 @@ struct intel_guc_ct_channel {
struct intel_guc_ct {
struct intel_guc_ct_channel host_channel;
/* other channels are tbd */
+
+ /** @lock: protects pending requests list */
+ spinlock_t lock;
+
+ /** @pending_requests: list of requests waiting for response */
+ struct list_head pending_requests;
+
+ /** @incoming_requests: list of incoming requests */
+ struct list_head incoming_requests;
+
+ /** @worker: worker for handling incoming requests */
+ struct work_struct worker;
};
void intel_guc_ct_init_early(struct intel_guc_ct *ct);
-
-/* XXX: move to intel_uc.h ? don't fit there either */
-int intel_guc_enable_ct(struct intel_guc *guc);
-void intel_guc_disable_ct(struct intel_guc *guc);
+int intel_guc_ct_enable(struct intel_guc_ct *ct);
+void intel_guc_ct_disable(struct intel_guc_ct *ct);
#endif /* _INTEL_GUC_CT_H_ */
diff --git a/drivers/gpu/drm/i915/intel_guc_fw.c b/drivers/gpu/drm/i915/intel_guc_fw.c
index d07f2b985f1c..a9e6fcce467c 100644
--- a/drivers/gpu/drm/i915/intel_guc_fw.c
+++ b/drivers/gpu/drm/i915/intel_guc_fw.c
@@ -165,7 +165,7 @@ static int guc_xfer_ucode(struct intel_guc *guc, struct i915_vma *vma)
I915_WRITE(DMA_COPY_SIZE, guc_fw->header_size + guc_fw->ucode_size);
/* Set the source address for the new blob */
- offset = guc_ggtt_offset(vma) + guc_fw->header_offset;
+ offset = intel_guc_ggtt_offset(guc, vma) + guc_fw->header_offset;
I915_WRITE(DMA_ADDR_0_LOW, lower_32_bits(offset));
I915_WRITE(DMA_ADDR_0_HIGH, upper_32_bits(offset) & 0xFFFF);
@@ -275,9 +275,8 @@ static int guc_fw_xfer(struct intel_uc_fw *guc_fw, struct i915_vma *vma)
* Called from intel_uc_init_hw() during driver load, resume from sleep and
* after a GPU reset.
*
- * The firmware image should have already been fetched into memory by the
- * earlier call to intel_uc_init_fw(), so here we need to only check that
- * fetch succeeded, and then transfer the image to the h/w.
+ * The firmware image should have already been fetched into memory, so only
+ * check that fetch succeeded, and then transfer the image to the h/w.
*
* Return: non-zero code on error
*/
diff --git a/drivers/gpu/drm/i915/intel_guc_fwif.h b/drivers/gpu/drm/i915/intel_guc_fwif.h
index 6a10aa6f04d3..d73673f5d30c 100644
--- a/drivers/gpu/drm/i915/intel_guc_fwif.h
+++ b/drivers/gpu/drm/i915/intel_guc_fwif.h
@@ -127,7 +127,7 @@
#define GUC_PROFILE_ENABLED (1 << 7)
#define GUC_WQ_TRACK_ENABLED (1 << 8)
#define GUC_ADS_ENABLED (1 << 9)
-#define GUC_DEBUG_RESERVED (1 << 10)
+#define GUC_LOG_DEFAULT_DISABLED (1 << 10)
#define GUC_ADS_ADDR_SHIFT 11
#define GUC_ADS_ADDR_MASK 0xfffff800
@@ -327,6 +327,58 @@ struct guc_stage_desc {
u64 desc_private;
} __packed;
+/**
+ * DOC: CTB based communication
+ *
+ * The CTB (command transport buffer) communication between Host and GuC
+ * is based on u32 data stream written to the shared buffer. One buffer can
+ * be used to transmit data only in one direction (one-directional channel).
+ *
+ * Current status of the each buffer is stored in the buffer descriptor.
+ * Buffer descriptor holds tail and head fields that represents active data
+ * stream. The tail field is updated by the data producer (sender), and head
+ * field is updated by the data consumer (receiver)::
+ *
+ * +------------+
+ * | DESCRIPTOR | +=================+============+========+
+ * +============+ | | MESSAGE(s) | |
+ * | address |--------->+=================+============+========+
+ * +------------+
+ * | head | ^-----head--------^
+ * +------------+
+ * | tail | ^---------tail-----------------^
+ * +------------+
+ * | size | ^---------------size--------------------^
+ * +------------+
+ *
+ * Each message in data stream starts with the single u32 treated as a header,
+ * followed by optional set of u32 data that makes message specific payload::
+ *
+ * +------------+---------+---------+---------+
+ * | MESSAGE |
+ * +------------+---------+---------+---------+
+ * | msg[0] | [1] | ... | [n-1] |
+ * +------------+---------+---------+---------+
+ * | MESSAGE | MESSAGE PAYLOAD |
+ * + HEADER +---------+---------+---------+
+ * | | 0 | ... | n |
+ * +======+=====+=========+=========+=========+
+ * | 31:16| code| | | |
+ * +------+-----+ | | |
+ * | 15:5|flags| | | |
+ * +------+-----+ | | |
+ * | 4:0| len| | | |
+ * +------+-----+---------+---------+---------+
+ *
+ * ^-------------len-------------^
+ *
+ * The message header consists of:
+ *
+ * - **len**, indicates length of the message payload (in u32)
+ * - **code**, indicates message code
+ * - **flags**, holds various bits to control message handling
+ */
+
/*
* Describes single command transport buffer.
* Used by both guc-master and clients.
@@ -534,16 +586,6 @@ struct guc_log_buffer_state {
u32 version;
} __packed;
-union guc_log_control {
- struct {
- u32 logging_enabled:1;
- u32 reserved1:3;
- u32 verbosity:4;
- u32 reserved2:24;
- };
- u32 value;
-} __packed;
-
struct guc_ctx_report {
u32 report_return_status;
u32 reserved1[64];
@@ -570,7 +612,68 @@ struct guc_shared_ctx_data {
struct guc_ctx_report preempt_ctx_report[GUC_MAX_ENGINES_NUM];
} __packed;
-/* This Action will be programmed in C180 - SOFT_SCRATCH_O_REG */
+/**
+ * DOC: MMIO based communication
+ *
+ * The MMIO based communication between Host and GuC uses software scratch
+ * registers, where first register holds data treated as message header,
+ * and other registers are used to hold message payload.
+ *
+ * For Gen9+, GuC uses software scratch registers 0xC180-0xC1B8
+ *
+ * +-----------+---------+---------+---------+
+ * | MMIO[0] | MMIO[1] | ... | MMIO[n] |
+ * +-----------+---------+---------+---------+
+ * | header | optional payload |
+ * +======+====+=========+=========+=========+
+ * | 31:28|type| | | |
+ * +------+----+ | | |
+ * | 27:16|data| | | |
+ * +------+----+ | | |
+ * | 15:0|code| | | |
+ * +------+----+---------+---------+---------+
+ *
+ * The message header consists of:
+ *
+ * - **type**, indicates message type
+ * - **code**, indicates message code, is specific for **type**
+ * - **data**, indicates message data, optional, depends on **code**
+ *
+ * The following message **types** are supported:
+ *
+ * - **REQUEST**, indicates Host-to-GuC request, requested GuC action code
+ * must be priovided in **code** field. Optional action specific parameters
+ * can be provided in remaining payload registers or **data** field.
+ *
+ * - **RESPONSE**, indicates GuC-to-Host response from earlier GuC request,
+ * action response status will be provided in **code** field. Optional
+ * response data can be returned in remaining payload registers or **data**
+ * field.
+ */
+
+#define INTEL_GUC_MSG_TYPE_SHIFT 28
+#define INTEL_GUC_MSG_TYPE_MASK (0xF << INTEL_GUC_MSG_TYPE_SHIFT)
+#define INTEL_GUC_MSG_DATA_SHIFT 16
+#define INTEL_GUC_MSG_DATA_MASK (0xFFF << INTEL_GUC_MSG_DATA_SHIFT)
+#define INTEL_GUC_MSG_CODE_SHIFT 0
+#define INTEL_GUC_MSG_CODE_MASK (0xFFFF << INTEL_GUC_MSG_CODE_SHIFT)
+
+#define __INTEL_GUC_MSG_GET(T, m) \
+ (((m) & INTEL_GUC_MSG_ ## T ## _MASK) >> INTEL_GUC_MSG_ ## T ## _SHIFT)
+#define INTEL_GUC_MSG_TO_TYPE(m) __INTEL_GUC_MSG_GET(TYPE, m)
+#define INTEL_GUC_MSG_TO_DATA(m) __INTEL_GUC_MSG_GET(DATA, m)
+#define INTEL_GUC_MSG_TO_CODE(m) __INTEL_GUC_MSG_GET(CODE, m)
+
+enum intel_guc_msg_type {
+ INTEL_GUC_MSG_TYPE_REQUEST = 0x0,
+ INTEL_GUC_MSG_TYPE_RESPONSE = 0xF,
+};
+
+#define __INTEL_GUC_MSG_TYPE_IS(T, m) \
+ (INTEL_GUC_MSG_TO_TYPE(m) == INTEL_GUC_MSG_TYPE_ ## T)
+#define INTEL_GUC_MSG_IS_REQUEST(m) __INTEL_GUC_MSG_TYPE_IS(REQUEST, m)
+#define INTEL_GUC_MSG_IS_RESPONSE(m) __INTEL_GUC_MSG_TYPE_IS(RESPONSE, m)
+
enum intel_guc_action {
INTEL_GUC_ACTION_DEFAULT = 0x0,
INTEL_GUC_ACTION_REQUEST_PREEMPTION = 0x2,
@@ -602,24 +705,22 @@ enum intel_guc_report_status {
INTEL_GUC_REPORT_STATUS_COMPLETE = 0x4,
};
-/*
- * The GuC sends its response to a command by overwriting the
- * command in SS0. The response is distinguishable from a command
- * by the fact that all the MASK bits are set. The remaining bits
- * give more detail.
- */
-#define INTEL_GUC_RECV_MASK ((u32)0xF0000000)
-#define INTEL_GUC_RECV_IS_RESPONSE(x) ((u32)(x) >= INTEL_GUC_RECV_MASK)
-#define INTEL_GUC_RECV_STATUS(x) (INTEL_GUC_RECV_MASK | (x))
-
-/* GUC will return status back to SOFT_SCRATCH_O_REG */
-enum intel_guc_status {
- INTEL_GUC_STATUS_SUCCESS = INTEL_GUC_RECV_STATUS(0x0),
- INTEL_GUC_STATUS_ALLOCATE_DOORBELL_FAIL = INTEL_GUC_RECV_STATUS(0x10),
- INTEL_GUC_STATUS_DEALLOCATE_DOORBELL_FAIL = INTEL_GUC_RECV_STATUS(0x20),
- INTEL_GUC_STATUS_GENERIC_FAIL = INTEL_GUC_RECV_STATUS(0x0000F000)
+#define GUC_LOG_CONTROL_LOGGING_ENABLED (1 << 0)
+#define GUC_LOG_CONTROL_VERBOSITY_SHIFT 4
+#define GUC_LOG_CONTROL_VERBOSITY_MASK (0xF << GUC_LOG_CONTROL_VERBOSITY_SHIFT)
+#define GUC_LOG_CONTROL_DEFAULT_LOGGING (1 << 8)
+
+enum intel_guc_response_status {
+ INTEL_GUC_RESPONSE_STATUS_SUCCESS = 0x0,
+ INTEL_GUC_RESPONSE_STATUS_GENERIC_FAIL = 0xF000,
};
+#define INTEL_GUC_MSG_IS_RESPONSE_SUCCESS(m) \
+ (typecheck(u32, (m)) && \
+ ((m) & (INTEL_GUC_MSG_TYPE_MASK | INTEL_GUC_MSG_CODE_MASK)) == \
+ ((INTEL_GUC_MSG_TYPE_RESPONSE << INTEL_GUC_MSG_TYPE_SHIFT) | \
+ (INTEL_GUC_RESPONSE_STATUS_SUCCESS << INTEL_GUC_MSG_CODE_SHIFT)))
+
/* This action will be programmed in C1BC - SOFT_SCRATCH_15_REG */
enum intel_guc_recv_message {
INTEL_GUC_RECV_MSG_CRASH_DUMP_POSTED = BIT(1),
diff --git a/drivers/gpu/drm/i915/intel_guc_log.c b/drivers/gpu/drm/i915/intel_guc_log.c
index c0c2e7d1c7d7..401e1704d61e 100644
--- a/drivers/gpu/drm/i915/intel_guc_log.c
+++ b/drivers/gpu/drm/i915/intel_guc_log.c
@@ -23,12 +23,11 @@
*/
#include <linux/debugfs.h>
-#include <linux/relay.h>
#include "intel_guc_log.h"
#include "i915_drv.h"
-static void guc_log_capture_logs(struct intel_guc *guc);
+static void guc_log_capture_logs(struct intel_guc_log *log);
/**
* DOC: GuC firmware log
@@ -39,7 +38,7 @@ static void guc_log_capture_logs(struct intel_guc *guc);
* registers value.
*/
-static int guc_log_flush_complete(struct intel_guc *guc)
+static int guc_action_flush_log_complete(struct intel_guc *guc)
{
u32 action[] = {
INTEL_GUC_ACTION_LOG_BUFFER_FILE_FLUSH_COMPLETE
@@ -48,7 +47,7 @@ static int guc_log_flush_complete(struct intel_guc *guc)
return intel_guc_send(guc, action, ARRAY_SIZE(action));
}
-static int guc_log_flush(struct intel_guc *guc)
+static int guc_action_flush_log(struct intel_guc *guc)
{
u32 action[] = {
INTEL_GUC_ACTION_FORCE_LOG_BUFFER_FLUSH,
@@ -58,22 +57,40 @@ static int guc_log_flush(struct intel_guc *guc)
return intel_guc_send(guc, action, ARRAY_SIZE(action));
}
-static int guc_log_control(struct intel_guc *guc, bool enable, u32 verbosity)
+static int guc_action_control_log(struct intel_guc *guc, bool enable,
+ bool default_logging, u32 verbosity)
{
- union guc_log_control control_val = {
- {
- .logging_enabled = enable,
- .verbosity = verbosity,
- },
- };
u32 action[] = {
INTEL_GUC_ACTION_UK_LOG_ENABLE_LOGGING,
- control_val.value
+ (enable ? GUC_LOG_CONTROL_LOGGING_ENABLED : 0) |
+ (verbosity << GUC_LOG_CONTROL_VERBOSITY_SHIFT) |
+ (default_logging ? GUC_LOG_CONTROL_DEFAULT_LOGGING : 0)
};
+ GEM_BUG_ON(verbosity > GUC_LOG_VERBOSITY_MAX);
+
return intel_guc_send(guc, action, ARRAY_SIZE(action));
}
+static inline struct intel_guc *log_to_guc(struct intel_guc_log *log)
+{
+ return container_of(log, struct intel_guc, log);
+}
+
+static void guc_log_enable_flush_events(struct intel_guc_log *log)
+{
+ intel_guc_enable_msg(log_to_guc(log),
+ INTEL_GUC_RECV_MSG_FLUSH_LOG_BUFFER |
+ INTEL_GUC_RECV_MSG_CRASH_DUMP_POSTED);
+}
+
+static void guc_log_disable_flush_events(struct intel_guc_log *log)
+{
+ intel_guc_disable_msg(log_to_guc(log),
+ INTEL_GUC_RECV_MSG_FLUSH_LOG_BUFFER |
+ INTEL_GUC_RECV_MSG_CRASH_DUMP_POSTED);
+}
+
/*
* Sub buffer switch callback. Called whenever relay has to switch to a new
* sub buffer, relay stays on the same sub buffer if 0 is returned.
@@ -121,14 +138,7 @@ static struct dentry *create_buf_file_callback(const char *filename,
if (!parent)
return NULL;
- /*
- * Not using the channel filename passed as an argument, since for each
- * channel relay appends the corresponding CPU number to the filename
- * passed in relay_open(). This should be fine as relay just needs a
- * dentry of the file associated with the channel buffer and that file's
- * name need not be same as the filename passed as an argument.
- */
- buf_file = debugfs_create_file("guc_log", mode,
+ buf_file = debugfs_create_file(filename, mode,
parent, buf, &relay_file_operations);
return buf_file;
}
@@ -149,59 +159,7 @@ static struct rchan_callbacks relay_callbacks = {
.remove_buf_file = remove_buf_file_callback,
};
-static int guc_log_relay_file_create(struct intel_guc *guc)
-{
- struct drm_i915_private *dev_priv = guc_to_i915(guc);
- struct dentry *log_dir;
- int ret;
-
- if (!i915_modparams.guc_log_level)
- return 0;
-
- mutex_lock(&guc->log.runtime.relay_lock);
-
- /* For now create the log file in /sys/kernel/debug/dri/0 dir */
- log_dir = dev_priv->drm.primary->debugfs_root;
-
- /*
- * If /sys/kernel/debug/dri/0 location do not exist, then debugfs is
- * not mounted and so can't create the relay file.
- * The relay API seems to fit well with debugfs only, for availing relay
- * there are 3 requirements which can be met for debugfs file only in a
- * straightforward/clean manner :-
- * i) Need the associated dentry pointer of the file, while opening the
- * relay channel.
- * ii) Should be able to use 'relay_file_operations' fops for the file.
- * iii) Set the 'i_private' field of file's inode to the pointer of
- * relay channel buffer.
- */
- if (!log_dir) {
- DRM_ERROR("Debugfs dir not available yet for GuC log file\n");
- ret = -ENODEV;
- goto out_unlock;
- }
-
- ret = relay_late_setup_files(guc->log.runtime.relay_chan, "guc_log", log_dir);
- if (ret < 0 && ret != -EEXIST) {
- DRM_ERROR("Couldn't associate relay chan with file %d\n", ret);
- goto out_unlock;
- }
-
- ret = 0;
-
-out_unlock:
- mutex_unlock(&guc->log.runtime.relay_lock);
- return ret;
-}
-
-static bool guc_log_has_relay(struct intel_guc *guc)
-{
- lockdep_assert_held(&guc->log.runtime.relay_lock);
-
- return guc->log.runtime.relay_chan != NULL;
-}
-
-static void guc_move_to_next_buf(struct intel_guc *guc)
+static void guc_move_to_next_buf(struct intel_guc_log *log)
{
/*
* Make sure the updates made in the sub buffer are visible when
@@ -209,21 +167,15 @@ static void guc_move_to_next_buf(struct intel_guc *guc)
*/
smp_wmb();
- if (!guc_log_has_relay(guc))
- return;
-
/* All data has been written, so now move the offset of sub buffer. */
- relay_reserve(guc->log.runtime.relay_chan, guc->log.vma->obj->base.size);
+ relay_reserve(log->relay.channel, log->vma->obj->base.size);
/* Switch to the next sub buffer */
- relay_flush(guc->log.runtime.relay_chan);
+ relay_flush(log->relay.channel);
}
-static void *guc_get_write_buffer(struct intel_guc *guc)
+static void *guc_get_write_buffer(struct intel_guc_log *log)
{
- if (!guc_log_has_relay(guc))
- return NULL;
-
/*
* Just get the base address of a new sub buffer and copy data into it
* ourselves. NULL will be returned in no-overwrite mode, if all sub
@@ -233,25 +185,25 @@ static void *guc_get_write_buffer(struct intel_guc *guc)
* done without using relay_reserve() along with relay_write(). So its
* better to use relay_reserve() alone.
*/
- return relay_reserve(guc->log.runtime.relay_chan, 0);
+ return relay_reserve(log->relay.channel, 0);
}
-static bool guc_check_log_buf_overflow(struct intel_guc *guc,
+static bool guc_check_log_buf_overflow(struct intel_guc_log *log,
enum guc_log_buffer_type type,
unsigned int full_cnt)
{
- unsigned int prev_full_cnt = guc->log.prev_overflow_count[type];
+ unsigned int prev_full_cnt = log->stats[type].sampled_overflow;
bool overflow = false;
if (full_cnt != prev_full_cnt) {
overflow = true;
- guc->log.prev_overflow_count[type] = full_cnt;
- guc->log.total_overflow_count[type] += full_cnt - prev_full_cnt;
+ log->stats[type].overflow = full_cnt;
+ log->stats[type].sampled_overflow += full_cnt - prev_full_cnt;
if (full_cnt < prev_full_cnt) {
/* buffer_full_cnt is a 4 bit counter */
- guc->log.total_overflow_count[type] += 16;
+ log->stats[type].sampled_overflow += 16;
}
DRM_ERROR_RATELIMITED("GuC log buffer overflow\n");
}
@@ -275,7 +227,7 @@ static unsigned int guc_get_log_buffer_size(enum guc_log_buffer_type type)
return 0;
}
-static void guc_read_update_log_buffer(struct intel_guc *guc)
+static void guc_read_update_log_buffer(struct intel_guc_log *log)
{
unsigned int buffer_size, read_offset, write_offset, bytes_to_copy, full_cnt;
struct guc_log_buffer_state *log_buf_state, *log_buf_snapshot_state;
@@ -284,16 +236,16 @@ static void guc_read_update_log_buffer(struct intel_guc *guc)
void *src_data, *dst_data;
bool new_overflow;
- if (WARN_ON(!guc->log.runtime.buf_addr))
- return;
+ mutex_lock(&log->relay.lock);
- /* Get the pointer to shared GuC log buffer */
- log_buf_state = src_data = guc->log.runtime.buf_addr;
+ if (WARN_ON(!intel_guc_log_relay_enabled(log)))
+ goto out_unlock;
- mutex_lock(&guc->log.runtime.relay_lock);
+ /* Get the pointer to shared GuC log buffer */
+ log_buf_state = src_data = log->relay.buf_addr;
/* Get the pointer to local buffer to store the logs */
- log_buf_snapshot_state = dst_data = guc_get_write_buffer(guc);
+ log_buf_snapshot_state = dst_data = guc_get_write_buffer(log);
if (unlikely(!log_buf_snapshot_state)) {
/*
@@ -301,10 +253,9 @@ static void guc_read_update_log_buffer(struct intel_guc *guc)
* getting consumed by User at a slow rate.
*/
DRM_ERROR_RATELIMITED("no sub-buffer to capture logs\n");
- guc->log.capture_miss_count++;
- mutex_unlock(&guc->log.runtime.relay_lock);
+ log->relay.full_count++;
- return;
+ goto out_unlock;
}
/* Actual logs are present from the 2nd page */
@@ -325,8 +276,8 @@ static void guc_read_update_log_buffer(struct intel_guc *guc)
full_cnt = log_buf_state_local.buffer_full_cnt;
/* Bookkeeping stuff */
- guc->log.flush_count[type] += log_buf_state_local.flush_to_file;
- new_overflow = guc_check_log_buf_overflow(guc, type, full_cnt);
+ log->stats[type].flush += log_buf_state_local.flush_to_file;
+ new_overflow = guc_check_log_buf_overflow(log, type, full_cnt);
/* Update the state of shared log buffer */
log_buf_state->read_ptr = write_offset;
@@ -373,38 +324,35 @@ static void guc_read_update_log_buffer(struct intel_guc *guc)
dst_data += buffer_size;
}
- guc_move_to_next_buf(guc);
+ guc_move_to_next_buf(log);
- mutex_unlock(&guc->log.runtime.relay_lock);
+out_unlock:
+ mutex_unlock(&log->relay.lock);
}
static void capture_logs_work(struct work_struct *work)
{
- struct intel_guc *guc =
- container_of(work, struct intel_guc, log.runtime.flush_work);
-
- guc_log_capture_logs(guc);
-}
+ struct intel_guc_log *log =
+ container_of(work, struct intel_guc_log, relay.flush_work);
-static bool guc_log_has_runtime(struct intel_guc *guc)
-{
- return guc->log.runtime.buf_addr != NULL;
+ guc_log_capture_logs(log);
}
-static int guc_log_runtime_create(struct intel_guc *guc)
+static int guc_log_map(struct intel_guc_log *log)
{
+ struct intel_guc *guc = log_to_guc(log);
struct drm_i915_private *dev_priv = guc_to_i915(guc);
void *vaddr;
int ret;
- lockdep_assert_held(&dev_priv->drm.struct_mutex);
+ lockdep_assert_held(&log->relay.lock);
- if (!guc->log.vma)
+ if (!log->vma)
return -ENODEV;
- GEM_BUG_ON(guc_log_has_runtime(guc));
-
- ret = i915_gem_object_set_to_wc_domain(guc->log.vma->obj, true);
+ mutex_lock(&dev_priv->drm.struct_mutex);
+ ret = i915_gem_object_set_to_wc_domain(log->vma->obj, true);
+ mutex_unlock(&dev_priv->drm.struct_mutex);
if (ret)
return ret;
@@ -413,49 +361,40 @@ static int guc_log_runtime_create(struct intel_guc *guc)
* buffer pages, so that we can directly get the data
* (up-to-date) from memory.
*/
- vaddr = i915_gem_object_pin_map(guc->log.vma->obj, I915_MAP_WC);
+ vaddr = i915_gem_object_pin_map(log->vma->obj, I915_MAP_WC);
if (IS_ERR(vaddr)) {
DRM_ERROR("Couldn't map log buffer pages %d\n", ret);
return PTR_ERR(vaddr);
}
- guc->log.runtime.buf_addr = vaddr;
+ log->relay.buf_addr = vaddr;
return 0;
}
-static void guc_log_runtime_destroy(struct intel_guc *guc)
+static void guc_log_unmap(struct intel_guc_log *log)
{
- /*
- * It's possible that the runtime stuff was never allocated because
- * GuC log was disabled at the boot time.
- */
- if (!guc_log_has_runtime(guc))
- return;
+ lockdep_assert_held(&log->relay.lock);
- i915_gem_object_unpin_map(guc->log.vma->obj);
- guc->log.runtime.buf_addr = NULL;
+ i915_gem_object_unpin_map(log->vma->obj);
+ log->relay.buf_addr = NULL;
}
-void intel_guc_log_init_early(struct intel_guc *guc)
+void intel_guc_log_init_early(struct intel_guc_log *log)
{
- mutex_init(&guc->log.runtime.relay_lock);
- INIT_WORK(&guc->log.runtime.flush_work, capture_logs_work);
+ mutex_init(&log->relay.lock);
+ INIT_WORK(&log->relay.flush_work, capture_logs_work);
}
-int intel_guc_log_relay_create(struct intel_guc *guc)
+static int guc_log_relay_create(struct intel_guc_log *log)
{
+ struct intel_guc *guc = log_to_guc(log);
struct drm_i915_private *dev_priv = guc_to_i915(guc);
struct rchan *guc_log_relay_chan;
size_t n_subbufs, subbuf_size;
int ret;
- if (!i915_modparams.guc_log_level)
- return 0;
-
- mutex_lock(&guc->log.runtime.relay_lock);
-
- GEM_BUG_ON(guc_log_has_relay(guc));
+ lockdep_assert_held(&log->relay.lock);
/* Keep the size of sub buffers same as shared log buffer */
subbuf_size = GUC_LOG_SIZE;
@@ -468,157 +407,56 @@ int intel_guc_log_relay_create(struct intel_guc *guc)
*/
n_subbufs = 8;
- /*
- * Create a relay channel, so that we have buffers for storing
- * the GuC firmware logs, the channel will be linked with a file
- * later on when debugfs is registered.
- */
- guc_log_relay_chan = relay_open(NULL, NULL, subbuf_size,
- n_subbufs, &relay_callbacks, dev_priv);
+ guc_log_relay_chan = relay_open("guc_log",
+ dev_priv->drm.primary->debugfs_root,
+ subbuf_size, n_subbufs,
+ &relay_callbacks, dev_priv);
if (!guc_log_relay_chan) {
DRM_ERROR("Couldn't create relay chan for GuC logging\n");
ret = -ENOMEM;
- goto err;
+ return ret;
}
GEM_BUG_ON(guc_log_relay_chan->subbuf_size < subbuf_size);
- guc->log.runtime.relay_chan = guc_log_relay_chan;
-
- mutex_unlock(&guc->log.runtime.relay_lock);
+ log->relay.channel = guc_log_relay_chan;
return 0;
-
-err:
- mutex_unlock(&guc->log.runtime.relay_lock);
- /* logging will be off */
- i915_modparams.guc_log_level = 0;
- return ret;
-}
-
-void intel_guc_log_relay_destroy(struct intel_guc *guc)
-{
- mutex_lock(&guc->log.runtime.relay_lock);
-
- /*
- * It's possible that the relay was never allocated because
- * GuC log was disabled at the boot time.
- */
- if (!guc_log_has_relay(guc))
- goto out_unlock;
-
- relay_close(guc->log.runtime.relay_chan);
- guc->log.runtime.relay_chan = NULL;
-
-out_unlock:
- mutex_unlock(&guc->log.runtime.relay_lock);
}
-static int guc_log_late_setup(struct intel_guc *guc)
+static void guc_log_relay_destroy(struct intel_guc_log *log)
{
- struct drm_i915_private *dev_priv = guc_to_i915(guc);
- int ret;
-
- if (!guc_log_has_runtime(guc)) {
- /*
- * If log was disabled at boot time, then setup needed to handle
- * log buffer flush interrupts would not have been done yet, so
- * do that now.
- */
- ret = intel_guc_log_relay_create(guc);
- if (ret)
- goto err;
-
- mutex_lock(&dev_priv->drm.struct_mutex);
- intel_runtime_pm_get(dev_priv);
- ret = guc_log_runtime_create(guc);
- intel_runtime_pm_put(dev_priv);
- mutex_unlock(&dev_priv->drm.struct_mutex);
-
- if (ret)
- goto err_relay;
- }
-
- ret = guc_log_relay_file_create(guc);
- if (ret)
- goto err_runtime;
-
- return 0;
+ lockdep_assert_held(&log->relay.lock);
-err_runtime:
- mutex_lock(&dev_priv->drm.struct_mutex);
- guc_log_runtime_destroy(guc);
- mutex_unlock(&dev_priv->drm.struct_mutex);
-err_relay:
- intel_guc_log_relay_destroy(guc);
-err:
- /* logging will remain off */
- i915_modparams.guc_log_level = 0;
- return ret;
+ relay_close(log->relay.channel);
+ log->relay.channel = NULL;
}
-static void guc_log_capture_logs(struct intel_guc *guc)
+static void guc_log_capture_logs(struct intel_guc_log *log)
{
+ struct intel_guc *guc = log_to_guc(log);
struct drm_i915_private *dev_priv = guc_to_i915(guc);
- guc_read_update_log_buffer(guc);
+ guc_read_update_log_buffer(log);
/*
* Generally device is expected to be active only at this
* time, so get/put should be really quick.
*/
intel_runtime_pm_get(dev_priv);
- guc_log_flush_complete(guc);
- intel_runtime_pm_put(dev_priv);
-}
-
-static void guc_flush_logs(struct intel_guc *guc)
-{
- struct drm_i915_private *dev_priv = guc_to_i915(guc);
-
- if (!USES_GUC_SUBMISSION(dev_priv) || !i915_modparams.guc_log_level)
- return;
-
- /* First disable the interrupts, will be renabled afterwards */
- mutex_lock(&dev_priv->drm.struct_mutex);
- intel_runtime_pm_get(dev_priv);
- gen9_disable_guc_interrupts(dev_priv);
- intel_runtime_pm_put(dev_priv);
- mutex_unlock(&dev_priv->drm.struct_mutex);
-
- /*
- * Before initiating the forceful flush, wait for any pending/ongoing
- * flush to complete otherwise forceful flush may not actually happen.
- */
- flush_work(&guc->log.runtime.flush_work);
-
- /* Ask GuC to update the log buffer state */
- intel_runtime_pm_get(dev_priv);
- guc_log_flush(guc);
+ guc_action_flush_log_complete(guc);
intel_runtime_pm_put(dev_priv);
-
- /* GuC would have updated log buffer by now, so capture it */
- guc_log_capture_logs(guc);
}
-int intel_guc_log_create(struct intel_guc *guc)
+int intel_guc_log_create(struct intel_guc_log *log)
{
+ struct intel_guc *guc = log_to_guc(log);
struct i915_vma *vma;
unsigned long offset;
u32 flags;
int ret;
- GEM_BUG_ON(guc->log.vma);
-
- /*
- * We require SSE 4.1 for fast reads from the GuC log buffer and
- * it should be present on the chipsets supporting GuC based
- * submisssions.
- */
- if (WARN_ON(!i915_has_memcpy_from_wc())) {
- ret = -EINVAL;
- goto err;
- }
+ GEM_BUG_ON(log->vma);
vma = intel_guc_allocate_vma(guc, GUC_LOG_SIZE);
if (IS_ERR(vma)) {
@@ -626,13 +464,7 @@ int intel_guc_log_create(struct intel_guc *guc)
goto err;
}
- guc->log.vma = vma;
-
- if (i915_modparams.guc_log_level) {
- ret = guc_log_runtime_create(guc);
- if (ret < 0)
- goto err_vma;
- }
+ log->vma = vma;
/* each allocated unit is a page */
flags = GUC_LOG_VALID | GUC_LOG_NOTIFY_ON_HALF_FULL |
@@ -640,117 +472,159 @@ int intel_guc_log_create(struct intel_guc *guc)
(GUC_LOG_ISR_PAGES << GUC_LOG_ISR_SHIFT) |
(GUC_LOG_CRASH_PAGES << GUC_LOG_CRASH_SHIFT);
- offset = guc_ggtt_offset(vma) >> PAGE_SHIFT; /* in pages */
- guc->log.flags = (offset << GUC_LOG_BUF_ADDR_SHIFT) | flags;
+ offset = intel_guc_ggtt_offset(guc, vma) >> PAGE_SHIFT;
+ log->flags = (offset << GUC_LOG_BUF_ADDR_SHIFT) | flags;
return 0;
-err_vma:
- i915_vma_unpin_and_release(&guc->log.vma);
err:
/* logging will be off */
i915_modparams.guc_log_level = 0;
return ret;
}
-void intel_guc_log_destroy(struct intel_guc *guc)
+void intel_guc_log_destroy(struct intel_guc_log *log)
+{
+ i915_vma_unpin_and_release(&log->vma);
+}
+
+int intel_guc_log_level_get(struct intel_guc_log *log)
{
- guc_log_runtime_destroy(guc);
- i915_vma_unpin_and_release(&guc->log.vma);
+ GEM_BUG_ON(!log->vma);
+ GEM_BUG_ON(i915_modparams.guc_log_level < 0);
+
+ return i915_modparams.guc_log_level;
}
-int intel_guc_log_control(struct intel_guc *guc, u64 control_val)
+int intel_guc_log_level_set(struct intel_guc_log *log, u64 val)
{
+ struct intel_guc *guc = log_to_guc(log);
struct drm_i915_private *dev_priv = guc_to_i915(guc);
- bool enable_logging = control_val > 0;
- u32 verbosity;
int ret;
- if (!guc->log.vma)
- return -ENODEV;
+ BUILD_BUG_ON(GUC_LOG_VERBOSITY_MIN != 0);
+ GEM_BUG_ON(!log->vma);
+ GEM_BUG_ON(i915_modparams.guc_log_level < 0);
- BUILD_BUG_ON(GUC_LOG_VERBOSITY_MIN);
- if (control_val > 1 + GUC_LOG_VERBOSITY_MAX)
+ /*
+ * GuC is recognizing log levels starting from 0 to max, we're using 0
+ * as indication that logging should be disabled.
+ */
+ if (val < GUC_LOG_LEVEL_DISABLED || val > GUC_LOG_LEVEL_MAX)
return -EINVAL;
- /* This combination doesn't make sense & won't have any effect */
- if (!enable_logging && !i915_modparams.guc_log_level)
- return 0;
+ mutex_lock(&dev_priv->drm.struct_mutex);
- verbosity = enable_logging ? control_val - 1 : 0;
+ if (i915_modparams.guc_log_level == val) {
+ ret = 0;
+ goto out_unlock;
+ }
- ret = mutex_lock_interruptible(&dev_priv->drm.struct_mutex);
- if (ret)
- return ret;
intel_runtime_pm_get(dev_priv);
- ret = guc_log_control(guc, enable_logging, verbosity);
+ ret = guc_action_control_log(guc, GUC_LOG_LEVEL_IS_VERBOSE(val),
+ GUC_LOG_LEVEL_IS_ENABLED(val),
+ GUC_LOG_LEVEL_TO_VERBOSITY(val));
intel_runtime_pm_put(dev_priv);
+ if (ret) {
+ DRM_DEBUG_DRIVER("guc_log_control action failed %d\n", ret);
+ goto out_unlock;
+ }
+
+ i915_modparams.guc_log_level = val;
+
+out_unlock:
mutex_unlock(&dev_priv->drm.struct_mutex);
- if (ret < 0) {
- DRM_DEBUG_DRIVER("guc_logging_control action failed %d\n", ret);
- return ret;
- }
+ return ret;
+}
- if (enable_logging) {
- i915_modparams.guc_log_level = 1 + verbosity;
+bool intel_guc_log_relay_enabled(const struct intel_guc_log *log)
+{
+ return log->relay.buf_addr;
+}
- /*
- * If log was disabled at boot time, then the relay channel file
- * wouldn't have been created by now and interrupts also would
- * not have been enabled. Try again now, just in case.
- */
- ret = guc_log_late_setup(guc);
- if (ret < 0) {
- DRM_DEBUG_DRIVER("GuC log late setup failed %d\n", ret);
- return ret;
- }
+int intel_guc_log_relay_open(struct intel_guc_log *log)
+{
+ int ret;
- /* GuC logging is currently the only user of Guc2Host interrupts */
- mutex_lock(&dev_priv->drm.struct_mutex);
- intel_runtime_pm_get(dev_priv);
- gen9_enable_guc_interrupts(dev_priv);
- intel_runtime_pm_put(dev_priv);
- mutex_unlock(&dev_priv->drm.struct_mutex);
- } else {
- /*
- * Once logging is disabled, GuC won't generate logs & send an
- * interrupt. But there could be some data in the log buffer
- * which is yet to be captured. So request GuC to update the log
- * buffer state and then collect the left over logs.
- */
- guc_flush_logs(guc);
+ mutex_lock(&log->relay.lock);
- /* As logging is disabled, update log level to reflect that */
- i915_modparams.guc_log_level = 0;
+ if (intel_guc_log_relay_enabled(log)) {
+ ret = -EEXIST;
+ goto out_unlock;
}
- return ret;
-}
+ /*
+ * We require SSE 4.1 for fast reads from the GuC log buffer and
+ * it should be present on the chipsets supporting GuC based
+ * submisssions.
+ */
+ if (!i915_has_memcpy_from_wc()) {
+ ret = -ENXIO;
+ goto out_unlock;
+ }
-void i915_guc_log_register(struct drm_i915_private *dev_priv)
-{
- if (!USES_GUC_SUBMISSION(dev_priv) || !i915_modparams.guc_log_level)
- return;
+ ret = guc_log_relay_create(log);
+ if (ret)
+ goto out_unlock;
+
+ ret = guc_log_map(log);
+ if (ret)
+ goto out_relay;
- guc_log_late_setup(&dev_priv->guc);
+ mutex_unlock(&log->relay.lock);
+
+ guc_log_enable_flush_events(log);
+
+ /*
+ * When GuC is logging without us relaying to userspace, we're ignoring
+ * the flush notification. This means that we need to unconditionally
+ * flush on relay enabling, since GuC only notifies us once.
+ */
+ queue_work(log->relay.flush_wq, &log->relay.flush_work);
+
+ return 0;
+
+out_relay:
+ guc_log_relay_destroy(log);
+out_unlock:
+ mutex_unlock(&log->relay.lock);
+
+ return ret;
}
-void i915_guc_log_unregister(struct drm_i915_private *dev_priv)
+void intel_guc_log_relay_flush(struct intel_guc_log *log)
{
- struct intel_guc *guc = &dev_priv->guc;
+ struct intel_guc *guc = log_to_guc(log);
+ struct drm_i915_private *i915 = guc_to_i915(guc);
+
+ /*
+ * Before initiating the forceful flush, wait for any pending/ongoing
+ * flush to complete otherwise forceful flush may not actually happen.
+ */
+ flush_work(&log->relay.flush_work);
- if (!USES_GUC_SUBMISSION(dev_priv))
- return;
+ intel_runtime_pm_get(i915);
+ guc_action_flush_log(guc);
+ intel_runtime_pm_put(i915);
- mutex_lock(&dev_priv->drm.struct_mutex);
- /* GuC logging is currently the only user of Guc2Host interrupts */
- intel_runtime_pm_get(dev_priv);
- gen9_disable_guc_interrupts(dev_priv);
- intel_runtime_pm_put(dev_priv);
+ /* GuC would have updated log buffer by now, so capture it */
+ guc_log_capture_logs(log);
+}
- guc_log_runtime_destroy(guc);
- mutex_unlock(&dev_priv->drm.struct_mutex);
+void intel_guc_log_relay_close(struct intel_guc_log *log)
+{
+ guc_log_disable_flush_events(log);
+ flush_work(&log->relay.flush_work);
+
+ mutex_lock(&log->relay.lock);
+ GEM_BUG_ON(!intel_guc_log_relay_enabled(log));
+ guc_log_unmap(log);
+ guc_log_relay_destroy(log);
+ mutex_unlock(&log->relay.lock);
+}
- intel_guc_log_relay_destroy(guc);
+void intel_guc_log_handle_flush_event(struct intel_guc_log *log)
+{
+ queue_work(log->relay.flush_wq, &log->relay.flush_work);
}
diff --git a/drivers/gpu/drm/i915/intel_guc_log.h b/drivers/gpu/drm/i915/intel_guc_log.h
index dab0e949567a..fa80535a6f9d 100644
--- a/drivers/gpu/drm/i915/intel_guc_log.h
+++ b/drivers/gpu/drm/i915/intel_guc_log.h
@@ -25,11 +25,12 @@
#ifndef _INTEL_GUC_LOG_H_
#define _INTEL_GUC_LOG_H_
+#include <linux/mutex.h>
+#include <linux/relay.h>
#include <linux/workqueue.h>
#include "intel_guc_fwif.h"
-struct drm_i915_private;
struct intel_guc;
/*
@@ -39,33 +40,53 @@ struct intel_guc;
#define GUC_LOG_SIZE ((1 + GUC_LOG_DPC_PAGES + 1 + GUC_LOG_ISR_PAGES + \
1 + GUC_LOG_CRASH_PAGES + 1) << PAGE_SHIFT)
+/*
+ * While we're using plain log level in i915, GuC controls are much more...
+ * "elaborate"? We have a couple of bits for verbosity, separate bit for actual
+ * log enabling, and separate bit for default logging - which "conveniently"
+ * ignores the enable bit.
+ */
+#define GUC_LOG_LEVEL_DISABLED 0
+#define GUC_LOG_LEVEL_NON_VERBOSE 1
+#define GUC_LOG_LEVEL_IS_ENABLED(x) ((x) > GUC_LOG_LEVEL_DISABLED)
+#define GUC_LOG_LEVEL_IS_VERBOSE(x) ((x) > GUC_LOG_LEVEL_NON_VERBOSE)
+#define GUC_LOG_LEVEL_TO_VERBOSITY(x) ({ \
+ typeof(x) _x = (x); \
+ GUC_LOG_LEVEL_IS_VERBOSE(_x) ? _x - 2 : 0; \
+})
+#define GUC_VERBOSITY_TO_LOG_LEVEL(x) ((x) + 2)
+#define GUC_LOG_LEVEL_MAX GUC_VERBOSITY_TO_LOG_LEVEL(GUC_LOG_VERBOSITY_MAX)
+
struct intel_guc_log {
u32 flags;
struct i915_vma *vma;
- /* The runtime stuff gets created only when GuC logging gets enabled */
struct {
void *buf_addr;
struct workqueue_struct *flush_wq;
struct work_struct flush_work;
- struct rchan *relay_chan;
- /* To serialize the access to relay_chan */
- struct mutex relay_lock;
- } runtime;
+ struct rchan *channel;
+ struct mutex lock;
+ u32 full_count;
+ } relay;
/* logging related stats */
- u32 capture_miss_count;
- u32 flush_interrupt_count;
- u32 prev_overflow_count[GUC_MAX_LOG_BUFFER];
- u32 total_overflow_count[GUC_MAX_LOG_BUFFER];
- u32 flush_count[GUC_MAX_LOG_BUFFER];
+ struct {
+ u32 sampled_overflow;
+ u32 overflow;
+ u32 flush;
+ } stats[GUC_MAX_LOG_BUFFER];
};
-int intel_guc_log_create(struct intel_guc *guc);
-void intel_guc_log_destroy(struct intel_guc *guc);
-void intel_guc_log_init_early(struct intel_guc *guc);
-int intel_guc_log_relay_create(struct intel_guc *guc);
-void intel_guc_log_relay_destroy(struct intel_guc *guc);
-int intel_guc_log_control(struct intel_guc *guc, u64 control_val);
-void i915_guc_log_register(struct drm_i915_private *dev_priv);
-void i915_guc_log_unregister(struct drm_i915_private *dev_priv);
+void intel_guc_log_init_early(struct intel_guc_log *log);
+int intel_guc_log_create(struct intel_guc_log *log);
+void intel_guc_log_destroy(struct intel_guc_log *log);
+
+int intel_guc_log_level_get(struct intel_guc_log *log);
+int intel_guc_log_level_set(struct intel_guc_log *log, u64 control_val);
+bool intel_guc_log_relay_enabled(const struct intel_guc_log *log);
+int intel_guc_log_relay_open(struct intel_guc_log *log);
+void intel_guc_log_relay_flush(struct intel_guc_log *log);
+void intel_guc_log_relay_close(struct intel_guc_log *log);
+
+void intel_guc_log_handle_flush_event(struct intel_guc_log *log);
#endif
diff --git a/drivers/gpu/drm/i915/intel_guc_reg.h b/drivers/gpu/drm/i915/intel_guc_reg.h
index 19a9247c5664..d86084742a4a 100644
--- a/drivers/gpu/drm/i915/intel_guc_reg.h
+++ b/drivers/gpu/drm/i915/intel_guc_reg.h
@@ -66,22 +66,20 @@
#define UOS_MOVE (1<<4)
#define START_DMA (1<<0)
#define DMA_GUC_WOPCM_OFFSET _MMIO(0xc340)
+#define GUC_WOPCM_OFFSET_VALID (1<<0)
#define HUC_LOADING_AGENT_VCR (0<<1)
#define HUC_LOADING_AGENT_GUC (1<<1)
-#define GUC_WOPCM_OFFSET_VALUE 0x80000 /* 512KB */
+#define GUC_WOPCM_OFFSET_SHIFT 14
+#define GUC_WOPCM_OFFSET_MASK (0x3ffff << GUC_WOPCM_OFFSET_SHIFT)
#define GUC_MAX_IDLE_COUNT _MMIO(0xC3E4)
#define HUC_STATUS2 _MMIO(0xD3B0)
#define HUC_FW_VERIFIED (1<<7)
-/* Defines WOPCM space available to GuC firmware */
#define GUC_WOPCM_SIZE _MMIO(0xc050)
-/* GuC addresses below GUC_WOPCM_TOP don't map through the GTT */
-#define GUC_WOPCM_TOP (0x80 << 12) /* 512KB */
-#define BXT_GUC_WOPCM_RC6_RESERVED (0x10 << 12) /* 64KB */
-
-/* GuC addresses above GUC_GGTT_TOP also don't map through the GTT */
-#define GUC_GGTT_TOP 0xFEE00000
+#define GUC_WOPCM_SIZE_LOCKED (1<<0)
+#define GUC_WOPCM_SIZE_SHIFT 12
+#define GUC_WOPCM_SIZE_MASK (0xfffff << GUC_WOPCM_SIZE_SHIFT)
#define GEN8_GT_PM_CONFIG _MMIO(0x138140)
#define GEN9LP_GT_PM_CONFIG _MMIO(0x138140)
diff --git a/drivers/gpu/drm/i915/intel_guc_submission.c b/drivers/gpu/drm/i915/intel_guc_submission.c
index 8a8ad2fe158d..97121230656c 100644
--- a/drivers/gpu/drm/i915/intel_guc_submission.c
+++ b/drivers/gpu/drm/i915/intel_guc_submission.c
@@ -231,8 +231,8 @@ static int create_doorbell(struct intel_guc_client *client)
if (ret) {
__destroy_doorbell(client);
__update_doorbell_desc(client, GUC_DOORBELL_INVALID);
- DRM_ERROR("Couldn't create client %u doorbell: %d\n",
- client->stage_id, ret);
+ DRM_DEBUG_DRIVER("Couldn't create client %u doorbell: %d\n",
+ client->stage_id, ret);
return ret;
}
@@ -386,8 +386,8 @@ static void guc_stage_desc_init(struct intel_guc *guc,
lrc->context_desc = lower_32_bits(ce->lrc_desc);
/* The state page is after PPHWSP */
- lrc->ring_lrca =
- guc_ggtt_offset(ce->state) + LRC_STATE_PN * PAGE_SIZE;
+ lrc->ring_lrca = intel_guc_ggtt_offset(guc, ce->state) +
+ LRC_STATE_PN * PAGE_SIZE;
/* XXX: In direct submission, the GuC wants the HW context id
* here. In proxy submission, it wants the stage id
@@ -395,7 +395,7 @@ static void guc_stage_desc_init(struct intel_guc *guc,
lrc->context_id = (client->stage_id << GUC_ELC_CTXID_OFFSET) |
(guc_engine_id << GUC_ELC_ENGINE_OFFSET);
- lrc->ring_begin = guc_ggtt_offset(ce->ring->vma);
+ lrc->ring_begin = intel_guc_ggtt_offset(guc, ce->ring->vma);
lrc->ring_end = lrc->ring_begin + ce->ring->size - 1;
lrc->ring_next_free_location = lrc->ring_begin;
lrc->ring_current_tail_pointer_value = 0;
@@ -411,7 +411,7 @@ static void guc_stage_desc_init(struct intel_guc *guc,
* The doorbell, process descriptor, and workqueue are all parts
* of the client object, which the GuC will reference via the GGTT
*/
- gfx_addr = guc_ggtt_offset(client->vma);
+ gfx_addr = intel_guc_ggtt_offset(guc, client->vma);
desc->db_trigger_phy = sg_dma_address(client->vma->pages->sgl) +
client->doorbell_offset;
desc->db_trigger_cpu = ptr_to_u64(__get_doorbell(client));
@@ -584,7 +584,7 @@ static void inject_preempt_context(struct work_struct *work)
data[3] = engine->guc_id;
data[4] = guc->execbuf_client->priority;
data[5] = guc->execbuf_client->stage_id;
- data[6] = guc_ggtt_offset(guc->shared_data);
+ data[6] = intel_guc_ggtt_offset(guc, guc->shared_data);
if (WARN_ON(intel_guc_send(guc, data, ARRAY_SIZE(data)))) {
execlists_clear_active(&engine->execlists,
@@ -657,6 +657,16 @@ static void port_assign(struct execlist_port *port, struct i915_request *rq)
port_set(port, i915_request_get(rq));
}
+static inline int rq_prio(const struct i915_request *rq)
+{
+ return rq->priotree.priority;
+}
+
+static inline int port_prio(const struct execlist_port *port)
+{
+ return rq_prio(port_request(port));
+}
+
static void guc_dequeue(struct intel_engine_cs *engine)
{
struct intel_engine_execlists * const execlists = &engine->execlists;
@@ -672,12 +682,12 @@ static void guc_dequeue(struct intel_engine_cs *engine)
GEM_BUG_ON(rb_first(&execlists->queue) != rb);
if (port_isset(port)) {
- if (engine->i915->preempt_context) {
+ if (intel_engine_has_preemption(engine)) {
struct guc_preempt_work *preempt_work =
&engine->i915->guc.preempt_work[engine->id];
+ int prio = execlists->queue_priority;
- if (execlists->queue_priority >
- max(port_request(port)->priotree.priority, 0)) {
+ if (__execlists_need_preempt(prio, port_prio(port))) {
execlists_set_active(execlists,
EXECLISTS_ACTIVE_PREEMPT);
queue_work(engine->i915->guc.preempt_wq,
@@ -728,7 +738,7 @@ done:
execlists->first = rb;
if (submit) {
port_assign(port, last);
- execlists_set_active(execlists, EXECLISTS_ACTIVE_USER);
+ execlists_user_begin(execlists, execlists->port);
guc_submit(engine);
}
@@ -748,17 +758,20 @@ static void guc_submission_tasklet(unsigned long data)
struct execlist_port *port = execlists->port;
struct i915_request *rq;
- rq = port_request(&port[0]);
+ rq = port_request(port);
while (rq && i915_request_completed(rq)) {
trace_i915_request_out(rq);
i915_request_put(rq);
- execlists_port_complete(execlists, port);
-
- rq = port_request(&port[0]);
+ port = execlists_port_complete(execlists, port);
+ if (port_isset(port)) {
+ execlists_user_begin(execlists, port);
+ rq = port_request(port);
+ } else {
+ execlists_user_end(execlists);
+ rq = NULL;
+ }
}
- if (!rq)
- execlists_clear_active(execlists, EXECLISTS_ACTIVE_USER);
if (execlists_is_active(execlists, EXECLISTS_ACTIVE_PREEMPT) &&
intel_read_status_page(engine, I915_GEM_HWS_PREEMPT_INDEX) ==
diff --git a/drivers/gpu/drm/i915/intel_hangcheck.c b/drivers/gpu/drm/i915/intel_hangcheck.c
index c8ea510629fa..fd0ffb8328d0 100644
--- a/drivers/gpu/drm/i915/intel_hangcheck.c
+++ b/drivers/gpu/drm/i915/intel_hangcheck.c
@@ -246,9 +246,8 @@ engine_stuck(struct intel_engine_cs *engine, u64 acthd)
*/
tmp = I915_READ_CTL(engine);
if (tmp & RING_WAIT) {
- i915_handle_error(dev_priv, BIT(engine->id),
- "Kicking stuck wait on %s",
- engine->name);
+ i915_handle_error(dev_priv, BIT(engine->id), 0,
+ "stuck wait on %s", engine->name);
I915_WRITE_CTL(engine, tmp);
return ENGINE_WAIT_KICK;
}
@@ -258,8 +257,8 @@ engine_stuck(struct intel_engine_cs *engine, u64 acthd)
default:
return ENGINE_DEAD;
case 1:
- i915_handle_error(dev_priv, ALL_ENGINES,
- "Kicking stuck semaphore on %s",
+ i915_handle_error(dev_priv, ALL_ENGINES, 0,
+ "stuck semaphore on %s",
engine->name);
I915_WRITE_CTL(engine, tmp);
return ENGINE_WAIT_KICK;
@@ -386,13 +385,13 @@ static void hangcheck_declare_hang(struct drm_i915_private *i915,
if (stuck != hung)
hung &= ~stuck;
len = scnprintf(msg, sizeof(msg),
- "%s on ", stuck == hung ? "No progress" : "Hang");
+ "%s on ", stuck == hung ? "no progress" : "hang");
for_each_engine_masked(engine, i915, hung, tmp)
len += scnprintf(msg + len, sizeof(msg) - len,
"%s, ", engine->name);
msg[len-2] = '\0';
- return i915_handle_error(i915, hung, "%s", msg);
+ return i915_handle_error(i915, hung, I915_ERROR_CAPTURE, "%s", msg);
}
/*
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 14ca5d3057a7..2db5da550a1c 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -37,6 +37,43 @@ static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *intel_dig_port,
return 0;
}
+static bool hdcp_key_loadable(struct drm_i915_private *dev_priv)
+{
+ struct i915_power_domains *power_domains = &dev_priv->power_domains;
+ struct i915_power_well *power_well;
+ enum i915_power_well_id id;
+ bool enabled = false;
+
+ /*
+ * On HSW and BDW, Display HW loads the Key as soon as Display resumes.
+ * On all BXT+, SW can load the keys only when the PW#1 is turned on.
+ */
+ if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv))
+ id = HSW_DISP_PW_GLOBAL;
+ else
+ id = SKL_DISP_PW_1;
+
+ mutex_lock(&power_domains->lock);
+
+ /* PG1 (power well #1) needs to be enabled */
+ for_each_power_well(dev_priv, power_well) {
+ if (power_well->id == id) {
+ enabled = power_well->ops->is_enabled(dev_priv,
+ power_well);
+ break;
+ }
+ }
+ mutex_unlock(&power_domains->lock);
+
+ /*
+ * Another req for hdcp key loadability is enabled state of pll for
+ * cdclk. Without active crtc we wont land here. So we are assuming that
+ * cdclk is already on.
+ */
+
+ return enabled;
+}
+
static void intel_hdcp_clear_keys(struct drm_i915_private *dev_priv)
{
I915_WRITE(HDCP_KEY_CONF, HDCP_CLEAR_KEYS_TRIGGER);
@@ -142,53 +179,17 @@ bool intel_hdcp_is_ksv_valid(u8 *ksv)
return true;
}
-/* Implements Part 2 of the HDCP authorization procedure */
static
-int intel_hdcp_auth_downstream(struct intel_digital_port *intel_dig_port,
- const struct intel_hdcp_shim *shim)
+int intel_hdcp_validate_v_prime(struct intel_digital_port *intel_dig_port,
+ const struct intel_hdcp_shim *shim,
+ u8 *ksv_fifo, u8 num_downstream, u8 *bstatus)
{
struct drm_i915_private *dev_priv;
u32 vprime, sha_text, sha_leftovers, rep_ctl;
- u8 bstatus[2], num_downstream, *ksv_fifo;
int ret, i, j, sha_idx;
dev_priv = intel_dig_port->base.base.dev->dev_private;
- ret = intel_hdcp_poll_ksv_fifo(intel_dig_port, shim);
- if (ret) {
- DRM_ERROR("KSV list failed to become ready (%d)\n", ret);
- return ret;
- }
-
- ret = shim->read_bstatus(intel_dig_port, bstatus);
- if (ret)
- return ret;
-
- if (DRM_HDCP_MAX_DEVICE_EXCEEDED(bstatus[0]) ||
- DRM_HDCP_MAX_CASCADE_EXCEEDED(bstatus[1])) {
- DRM_ERROR("Max Topology Limit Exceeded\n");
- return -EPERM;
- }
-
- /*
- * When repeater reports 0 device count, HDCP1.4 spec allows disabling
- * the HDCP encryption. That implies that repeater can't have its own
- * display. As there is no consumption of encrypted content in the
- * repeater with 0 downstream devices, we are failing the
- * authentication.
- */
- num_downstream = DRM_HDCP_NUM_DOWNSTREAM(bstatus[0]);
- if (num_downstream == 0)
- return -EINVAL;
-
- ksv_fifo = kzalloc(num_downstream * DRM_HDCP_KSV_LEN, GFP_KERNEL);
- if (!ksv_fifo)
- return -ENOMEM;
-
- ret = shim->read_ksv_fifo(intel_dig_port, num_downstream, ksv_fifo);
- if (ret)
- return ret;
-
/* Process V' values from the receiver */
for (i = 0; i < DRM_HDCP_V_PRIME_NUM_PARTS; i++) {
ret = shim->read_v_prime_part(intel_dig_port, i, &vprime);
@@ -353,7 +354,8 @@ int intel_hdcp_auth_downstream(struct intel_digital_port *intel_dig_port,
return ret;
sha_idx += sizeof(sha_text);
} else {
- DRM_ERROR("Invalid number of leftovers %d\n", sha_leftovers);
+ DRM_DEBUG_KMS("Invalid number of leftovers %d\n",
+ sha_leftovers);
return -EINVAL;
}
@@ -381,17 +383,83 @@ int intel_hdcp_auth_downstream(struct intel_digital_port *intel_dig_port,
if (intel_wait_for_register(dev_priv, HDCP_REP_CTL,
HDCP_SHA1_COMPLETE,
HDCP_SHA1_COMPLETE, 1)) {
- DRM_ERROR("Timed out waiting for SHA1 complete\n");
+ DRM_DEBUG_KMS("Timed out waiting for SHA1 complete\n");
return -ETIMEDOUT;
}
if (!(I915_READ(HDCP_REP_CTL) & HDCP_SHA1_V_MATCH)) {
- DRM_ERROR("SHA-1 mismatch, HDCP failed\n");
+ DRM_DEBUG_KMS("SHA-1 mismatch, HDCP failed\n");
return -ENXIO;
}
+ return 0;
+}
+
+/* Implements Part 2 of the HDCP authorization procedure */
+static
+int intel_hdcp_auth_downstream(struct intel_digital_port *intel_dig_port,
+ const struct intel_hdcp_shim *shim)
+{
+ u8 bstatus[2], num_downstream, *ksv_fifo;
+ int ret, i, tries = 3;
+
+ ret = intel_hdcp_poll_ksv_fifo(intel_dig_port, shim);
+ if (ret) {
+ DRM_ERROR("KSV list failed to become ready (%d)\n", ret);
+ return ret;
+ }
+
+ ret = shim->read_bstatus(intel_dig_port, bstatus);
+ if (ret)
+ return ret;
+
+ if (DRM_HDCP_MAX_DEVICE_EXCEEDED(bstatus[0]) ||
+ DRM_HDCP_MAX_CASCADE_EXCEEDED(bstatus[1])) {
+ DRM_ERROR("Max Topology Limit Exceeded\n");
+ return -EPERM;
+ }
+
+ /*
+ * When repeater reports 0 device count, HDCP1.4 spec allows disabling
+ * the HDCP encryption. That implies that repeater can't have its own
+ * display. As there is no consumption of encrypted content in the
+ * repeater with 0 downstream devices, we are failing the
+ * authentication.
+ */
+ num_downstream = DRM_HDCP_NUM_DOWNSTREAM(bstatus[0]);
+ if (num_downstream == 0)
+ return -EINVAL;
+
+ ksv_fifo = kzalloc(num_downstream * DRM_HDCP_KSV_LEN, GFP_KERNEL);
+ if (!ksv_fifo)
+ return -ENOMEM;
+
+ ret = shim->read_ksv_fifo(intel_dig_port, num_downstream, ksv_fifo);
+ if (ret)
+ goto err;
+
+ /*
+ * When V prime mismatches, DP Spec mandates re-read of
+ * V prime atleast twice.
+ */
+ for (i = 0; i < tries; i++) {
+ ret = intel_hdcp_validate_v_prime(intel_dig_port, shim,
+ ksv_fifo, num_downstream,
+ bstatus);
+ if (!ret)
+ break;
+ }
+
+ if (i == tries) {
+ DRM_ERROR("V Prime validation failed.(%d)\n", ret);
+ goto err;
+ }
+
DRM_DEBUG_KMS("HDCP is enabled (%d downstream devices)\n",
num_downstream);
- return 0;
+ ret = 0;
+err:
+ kfree(ksv_fifo);
+ return ret;
}
/* Implements Part 1 of the HDCP authorization procedure */
@@ -506,15 +574,26 @@ static int intel_hdcp_auth(struct intel_digital_port *intel_dig_port,
*/
wait_remaining_ms_from_jiffies(r0_prime_gen_start, 300);
- ri.reg = 0;
- ret = shim->read_ri_prime(intel_dig_port, ri.shim);
- if (ret)
- return ret;
- I915_WRITE(PORT_HDCP_RPRIME(port), ri.reg);
+ tries = 3;
- /* Wait for Ri prime match */
- if (wait_for(I915_READ(PORT_HDCP_STATUS(port)) &
- (HDCP_STATUS_RI_MATCH | HDCP_STATUS_ENC), 1)) {
+ /*
+ * DP HDCP Spec mandates the two more reattempt to read R0, incase
+ * of R0 mismatch.
+ */
+ for (i = 0; i < tries; i++) {
+ ri.reg = 0;
+ ret = shim->read_ri_prime(intel_dig_port, ri.shim);
+ if (ret)
+ return ret;
+ I915_WRITE(PORT_HDCP_RPRIME(port), ri.reg);
+
+ /* Wait for Ri prime match */
+ if (!wait_for(I915_READ(PORT_HDCP_STATUS(port)) &
+ (HDCP_STATUS_RI_MATCH | HDCP_STATUS_ENC), 1))
+ break;
+ }
+
+ if (i == tries) {
DRM_ERROR("Timed out waiting for Ri prime match (%x)\n",
I915_READ(PORT_HDCP_STATUS(port)));
return -ETIMEDOUT;
@@ -580,8 +659,8 @@ static int _intel_hdcp_enable(struct intel_connector *connector)
DRM_DEBUG_KMS("[%s:%d] HDCP is being enabled...\n",
connector->base.name, connector->base.base.id);
- if (!(I915_READ(SKL_FUSE_STATUS) & SKL_FUSE_PG_DIST_STATUS(1))) {
- DRM_ERROR("PG1 is disabled, cannot load keys\n");
+ if (!hdcp_key_loadable(dev_priv)) {
+ DRM_ERROR("HDCP key Load is not possible\n");
return -ENXIO;
}
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index 1baef4ac7ecb..ee929f31f7db 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -2082,41 +2082,33 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *c
* it enables scrambling. This should be called before enabling the HDMI
* 2.0 port, as the sink can choose to disable the scrambling if it doesn't
* detect a scrambled clock within 100 ms.
+ *
+ * Returns:
+ * True on success, false on failure.
*/
-void intel_hdmi_handle_sink_scrambling(struct intel_encoder *encoder,
+bool intel_hdmi_handle_sink_scrambling(struct intel_encoder *encoder,
struct drm_connector *connector,
bool high_tmds_clock_ratio,
bool scrambling)
{
+ struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
- struct drm_i915_private *dev_priv = connector->dev->dev_private;
struct drm_scrambling *sink_scrambling =
- &connector->display_info.hdmi.scdc.scrambling;
- struct i2c_adapter *adptr = intel_gmbus_get_adapter(dev_priv,
- intel_hdmi->ddc_bus);
- bool ret;
+ &connector->display_info.hdmi.scdc.scrambling;
+ struct i2c_adapter *adapter =
+ intel_gmbus_get_adapter(dev_priv, intel_hdmi->ddc_bus);
if (!sink_scrambling->supported)
- return;
-
- DRM_DEBUG_KMS("Setting sink scrambling for enc:%s connector:%s\n",
- encoder->base.name, connector->name);
+ return true;
- /* Set TMDS bit clock ratio to 1/40 or 1/10 */
- ret = drm_scdc_set_high_tmds_clock_ratio(adptr, high_tmds_clock_ratio);
- if (!ret) {
- DRM_ERROR("Set TMDS ratio failed\n");
- return;
- }
-
- /* Enable/disable sink scrambling */
- ret = drm_scdc_set_scrambling(adptr, scrambling);
- if (!ret) {
- DRM_ERROR("Set sink scrambling failed\n");
- return;
- }
+ DRM_DEBUG_KMS("[CONNECTOR:%d:%s] scrambling=%s, TMDS bit clock ratio=1/%d\n",
+ connector->base.id, connector->name,
+ yesno(scrambling), high_tmds_clock_ratio ? 40 : 10);
- DRM_DEBUG_KMS("sink scrambling handled\n");
+ /* Set TMDS bit clock ratio to 1/40 or 1/10, and enable/disable scrambling */
+ return drm_scdc_set_high_tmds_clock_ratio(adapter,
+ high_tmds_clock_ratio) &&
+ drm_scdc_set_scrambling(adapter, scrambling);
}
static u8 chv_port_to_ddc_pin(struct drm_i915_private *dev_priv, enum port port)
diff --git a/drivers/gpu/drm/i915/intel_hotplug.c b/drivers/gpu/drm/i915/intel_hotplug.c
index 0e3d3e89d66a..43aa92beff2a 100644
--- a/drivers/gpu/drm/i915/intel_hotplug.c
+++ b/drivers/gpu/drm/i915/intel_hotplug.c
@@ -100,6 +100,8 @@ enum port intel_hpd_pin_to_port(struct drm_i915_private *dev_priv,
if (IS_CNL_WITH_PORT_F(dev_priv))
return PORT_F;
return PORT_E;
+ case HPD_PORT_F:
+ return PORT_F;
default:
return PORT_NONE; /* no port for this pin */
}
@@ -132,6 +134,7 @@ enum hpd_pin intel_hpd_pin_default(struct drm_i915_private *dev_priv,
case PORT_F:
if (IS_CNL_WITH_PORT_F(dev_priv))
return HPD_PORT_E;
+ return HPD_PORT_F;
default:
MISSING_CASE(port);
return HPD_NONE;
diff --git a/drivers/gpu/drm/i915/intel_huc.c b/drivers/gpu/drm/i915/intel_huc.c
index 65e2afb9b955..291285277403 100644
--- a/drivers/gpu/drm/i915/intel_huc.c
+++ b/drivers/gpu/drm/i915/intel_huc.c
@@ -55,7 +55,7 @@ int intel_huc_auth(struct intel_huc *huc)
return -ENOEXEC;
vma = i915_gem_object_ggtt_pin(huc->fw.obj, NULL, 0, 0,
- PIN_OFFSET_BIAS | GUC_WOPCM_TOP);
+ PIN_OFFSET_BIAS | guc->ggtt_pin_bias);
if (IS_ERR(vma)) {
ret = PTR_ERR(vma);
DRM_ERROR("HuC: Failed to pin huc fw object %d\n", ret);
@@ -63,7 +63,8 @@ int intel_huc_auth(struct intel_huc *huc)
}
ret = intel_guc_auth_huc(guc,
- guc_ggtt_offset(vma) + huc->fw.rsa_offset);
+ intel_guc_ggtt_offset(guc, vma) +
+ huc->fw.rsa_offset);
if (ret) {
DRM_ERROR("HuC: GuC did not ack Auth request %d\n", ret);
goto fail_unpin;
@@ -91,3 +92,28 @@ fail:
DRM_ERROR("HuC: Authentication failed %d\n", ret);
return ret;
}
+
+/**
+ * intel_huc_check_status() - check HuC status
+ * @huc: intel_huc structure
+ *
+ * This function reads status register to verify if HuC
+ * firmware was successfully loaded.
+ *
+ * Returns positive value if HuC firmware is loaded and verified
+ * and -ENODEV if HuC is not present.
+ */
+int intel_huc_check_status(struct intel_huc *huc)
+{
+ struct drm_i915_private *dev_priv = huc_to_i915(huc);
+ u32 status;
+
+ if (!HAS_HUC(dev_priv))
+ return -ENODEV;
+
+ intel_runtime_pm_get(dev_priv);
+ status = I915_READ(HUC_STATUS2) & HUC_FW_VERIFIED;
+ intel_runtime_pm_put(dev_priv);
+
+ return status;
+}
diff --git a/drivers/gpu/drm/i915/intel_huc.h b/drivers/gpu/drm/i915/intel_huc.h
index 5d6e804f9771..aa854907abac 100644
--- a/drivers/gpu/drm/i915/intel_huc.h
+++ b/drivers/gpu/drm/i915/intel_huc.h
@@ -37,5 +37,12 @@ struct intel_huc {
void intel_huc_init_early(struct intel_huc *huc);
int intel_huc_auth(struct intel_huc *huc);
+int intel_huc_check_status(struct intel_huc *huc);
+
+static inline int intel_huc_sanitize(struct intel_huc *huc)
+{
+ intel_uc_fw_sanitize(&huc->fw);
+ return 0;
+}
#endif
diff --git a/drivers/gpu/drm/i915/intel_huc_fw.c b/drivers/gpu/drm/i915/intel_huc_fw.c
index c66afa9b989a..f93d2384d482 100644
--- a/drivers/gpu/drm/i915/intel_huc_fw.c
+++ b/drivers/gpu/drm/i915/intel_huc_fw.c
@@ -118,7 +118,8 @@ static int huc_fw_xfer(struct intel_uc_fw *huc_fw, struct i915_vma *vma)
intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
/* Set the source address for the uCode */
- offset = guc_ggtt_offset(vma) + huc_fw->header_offset;
+ offset = intel_guc_ggtt_offset(&dev_priv->guc, vma) +
+ huc_fw->header_offset;
I915_WRITE(DMA_ADDR_0_LOW, lower_32_bits(offset));
I915_WRITE(DMA_ADDR_0_HIGH, upper_32_bits(offset) & 0xFFFF);
@@ -154,9 +155,8 @@ static int huc_fw_xfer(struct intel_uc_fw *huc_fw, struct i915_vma *vma)
* Called from intel_uc_init_hw() during driver load, resume from sleep and
* after a GPU reset. Note that HuC must be loaded before GuC.
*
- * The firmware image should have already been fetched into memory by the
- * earlier call to intel_uc_init_fw(), so here we need to only check that
- * fetch succeeded, and then transfer the image to the h/w.
+ * The firmware image should have already been fetched into memory, so only
+ * check that fetch succeeded, and then transfer the image to the h/w.
*
* Return: non-zero code on error
*/
diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c
index 697af5add78b..c7c85134a84a 100644
--- a/drivers/gpu/drm/i915/intel_lrc.c
+++ b/drivers/gpu/drm/i915/intel_lrc.c
@@ -139,6 +139,7 @@
#include "i915_gem_render_state.h"
#include "intel_lrc_reg.h"
#include "intel_mocs.h"
+#include "intel_workarounds.h"
#define RING_EXECLIST_QFULL (1 << 0x2)
#define RING_EXECLIST1_VALID (1 << 0x3)
@@ -183,7 +184,8 @@ static inline bool need_preempt(const struct intel_engine_cs *engine,
const struct i915_request *last,
int prio)
{
- return engine->i915->preempt_context && prio > max(rq_prio(last), 0);
+ return (intel_engine_has_preemption(engine) &&
+ __execlists_need_preempt(prio, rq_prio(last)));
}
/**
@@ -374,6 +376,19 @@ execlists_context_status_change(struct i915_request *rq, unsigned long status)
status, rq);
}
+inline void
+execlists_user_begin(struct intel_engine_execlists *execlists,
+ const struct execlist_port *port)
+{
+ execlists_set_active_once(execlists, EXECLISTS_ACTIVE_USER);
+}
+
+inline void
+execlists_user_end(struct intel_engine_execlists *execlists)
+{
+ execlists_clear_active(execlists, EXECLISTS_ACTIVE_USER);
+}
+
static inline void
execlists_context_schedule_in(struct i915_request *rq)
{
@@ -454,10 +469,12 @@ static void execlists_submit_ports(struct intel_engine_cs *engine)
desc = execlists_update_context(rq);
GEM_DEBUG_EXEC(port[n].context_id = upper_32_bits(desc));
- GEM_TRACE("%s in[%d]: ctx=%d.%d, seqno=%x, prio=%d\n",
+ GEM_TRACE("%s in[%d]: ctx=%d.%d, global=%d (fence %llx:%d) (current %d), prio=%d\n",
engine->name, n,
port[n].context_id, count,
rq->global_seqno,
+ rq->fence.context, rq->fence.seqno,
+ intel_engine_get_seqno(engine),
rq_prio(rq));
} else {
GEM_BUG_ON(!n);
@@ -577,6 +594,8 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
* know the next preemption status we see corresponds
* to this ELSP update.
*/
+ GEM_BUG_ON(!execlists_is_active(execlists,
+ EXECLISTS_ACTIVE_USER));
GEM_BUG_ON(!port_count(&port[0]));
if (port_count(&port[0]) > 1)
goto unlock;
@@ -695,8 +714,27 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
if (p->priority != I915_PRIORITY_NORMAL)
kmem_cache_free(engine->i915->priorities, p);
}
+
done:
- execlists->queue_priority = rb ? to_priolist(rb)->priority : INT_MIN;
+ /*
+ * Here be a bit of magic! Or sleight-of-hand, whichever you prefer.
+ *
+ * We choose queue_priority such that if we add a request of greater
+ * priority than this, we kick the submission tasklet to decide on
+ * the right order of submitting the requests to hardware. We must
+ * also be prepared to reorder requests as they are in-flight on the
+ * HW. We derive the queue_priority then as the first "hole" in
+ * the HW submission ports and if there are no available slots,
+ * the priority of the lowest executing request, i.e. last.
+ *
+ * When we do receive a higher priority request ready to run from the
+ * user, see queue_request(), the queue_priority is bumped to that
+ * request triggering preemption on the next dequeue (or subsequent
+ * interrupt for secondary ports).
+ */
+ execlists->queue_priority =
+ port != execlists->port ? rq_prio(last) : INT_MIN;
+
execlists->first = rb;
if (submit)
port_assign(port, last);
@@ -708,7 +746,7 @@ unlock:
spin_unlock_irq(&engine->timeline->lock);
if (submit) {
- execlists_set_active(execlists, EXECLISTS_ACTIVE_USER);
+ execlists_user_begin(execlists, execlists->port);
execlists_submit_ports(engine);
}
@@ -725,6 +763,13 @@ execlists_cancel_port_requests(struct intel_engine_execlists * const execlists)
while (num_ports-- && port_isset(port)) {
struct i915_request *rq = port_request(port);
+ GEM_TRACE("%s:port%u global=%d (fence %llx:%d), (current %d)\n",
+ rq->engine->name,
+ (unsigned int)(port - execlists->port),
+ rq->global_seqno,
+ rq->fence.context, rq->fence.seqno,
+ intel_engine_get_seqno(rq->engine));
+
GEM_BUG_ON(!execlists->active);
intel_engine_context_out(rq->engine);
@@ -738,6 +783,59 @@ execlists_cancel_port_requests(struct intel_engine_execlists * const execlists)
memset(port, 0, sizeof(*port));
port++;
}
+
+ execlists_user_end(execlists);
+}
+
+static void clear_gtiir(struct intel_engine_cs *engine)
+{
+ static const u8 gtiir[] = {
+ [RCS] = 0,
+ [BCS] = 0,
+ [VCS] = 1,
+ [VCS2] = 1,
+ [VECS] = 3,
+ };
+ struct drm_i915_private *dev_priv = engine->i915;
+ int i;
+
+ /* TODO: correctly reset irqs for gen11 */
+ if (WARN_ON_ONCE(INTEL_GEN(engine->i915) >= 11))
+ return;
+
+ GEM_BUG_ON(engine->id >= ARRAY_SIZE(gtiir));
+
+ /*
+ * Clear any pending interrupt state.
+ *
+ * We do it twice out of paranoia that some of the IIR are
+ * double buffered, and so if we only reset it once there may
+ * still be an interrupt pending.
+ */
+ for (i = 0; i < 2; i++) {
+ I915_WRITE(GEN8_GT_IIR(gtiir[engine->id]),
+ engine->irq_keep_mask);
+ POSTING_READ(GEN8_GT_IIR(gtiir[engine->id]));
+ }
+ GEM_BUG_ON(I915_READ(GEN8_GT_IIR(gtiir[engine->id])) &
+ engine->irq_keep_mask);
+}
+
+static void reset_irq(struct intel_engine_cs *engine)
+{
+ /* Mark all CS interrupts as complete */
+ smp_store_mb(engine->execlists.active, 0);
+ synchronize_hardirq(engine->i915->drm.irq);
+
+ clear_gtiir(engine);
+
+ /*
+ * The port is checked prior to scheduling a tasklet, but
+ * just in case we have suspended the tasklet to do the
+ * wedging make sure that when it wakes, it decides there
+ * is no work to do by clearing the irq_posted bit.
+ */
+ clear_bit(ENGINE_IRQ_EXECLIST, &engine->irq_posted);
}
static void execlists_cancel_requests(struct intel_engine_cs *engine)
@@ -747,7 +845,8 @@ static void execlists_cancel_requests(struct intel_engine_cs *engine)
struct rb_node *rb;
unsigned long flags;
- GEM_TRACE("%s\n", engine->name);
+ GEM_TRACE("%s current %d\n",
+ engine->name, intel_engine_get_seqno(engine));
/*
* Before we call engine->cancel_requests(), we should have exclusive
@@ -767,6 +866,7 @@ static void execlists_cancel_requests(struct intel_engine_cs *engine)
/* Cancel the requests on the HW and clear the ELSP tracker. */
execlists_cancel_port_requests(execlists);
+ reset_irq(engine);
spin_lock(&engine->timeline->lock);
@@ -805,17 +905,6 @@ static void execlists_cancel_requests(struct intel_engine_cs *engine)
spin_unlock(&engine->timeline->lock);
- /*
- * The port is checked prior to scheduling a tasklet, but
- * just in case we have suspended the tasklet to do the
- * wedging make sure that when it wakes, it decides there
- * is no work to do by clearing the irq_posted bit.
- */
- clear_bit(ENGINE_IRQ_EXECLIST, &engine->irq_posted);
-
- /* Mark all CS interrupts as complete */
- execlists->active = 0;
-
local_irq_restore(flags);
}
@@ -827,7 +916,7 @@ static void execlists_submission_tasklet(unsigned long data)
{
struct intel_engine_cs * const engine = (struct intel_engine_cs *)data;
struct intel_engine_execlists * const execlists = &engine->execlists;
- struct execlist_port * const port = execlists->port;
+ struct execlist_port *port = execlists->port;
struct drm_i915_private *dev_priv = engine->i915;
bool fw = false;
@@ -954,10 +1043,13 @@ static void execlists_submission_tasklet(unsigned long data)
EXECLISTS_ACTIVE_USER));
rq = port_unpack(port, &count);
- GEM_TRACE("%s out[0]: ctx=%d.%d, seqno=%x, prio=%d\n",
+ GEM_TRACE("%s out[0]: ctx=%d.%d, global=%d (fence %llx:%d) (current %d), prio=%d\n",
engine->name,
port->context_id, count,
rq ? rq->global_seqno : 0,
+ rq ? rq->fence.context : 0,
+ rq ? rq->fence.seqno : 0,
+ intel_engine_get_seqno(engine),
rq ? rq_prio(rq) : 0);
/* Check the context/desc id for this event matches */
@@ -965,10 +1057,28 @@ static void execlists_submission_tasklet(unsigned long data)
GEM_BUG_ON(count == 0);
if (--count == 0) {
+ /*
+ * On the final event corresponding to the
+ * submission of this context, we expect either
+ * an element-switch event or a completion
+ * event (and on completion, the active-idle
+ * marker). No more preemptions, lite-restore
+ * or otherwise.
+ */
GEM_BUG_ON(status & GEN8_CTX_STATUS_PREEMPTED);
GEM_BUG_ON(port_isset(&port[1]) &&
!(status & GEN8_CTX_STATUS_ELEMENT_SWITCH));
+ GEM_BUG_ON(!port_isset(&port[1]) &&
+ !(status & GEN8_CTX_STATUS_ACTIVE_IDLE));
+
+ /*
+ * We rely on the hardware being strongly
+ * ordered, that the breadcrumb write is
+ * coherent (visible from the CPU) before the
+ * user interrupt and CSB is processed.
+ */
GEM_BUG_ON(!i915_request_completed(rq));
+
execlists_context_schedule_out(rq);
trace_i915_request_out(rq);
i915_request_put(rq);
@@ -976,17 +1086,14 @@ static void execlists_submission_tasklet(unsigned long data)
GEM_TRACE("%s completed ctx=%d\n",
engine->name, port->context_id);
- execlists_port_complete(execlists, port);
+ port = execlists_port_complete(execlists, port);
+ if (port_isset(port))
+ execlists_user_begin(execlists, port);
+ else
+ execlists_user_end(execlists);
} else {
port_set(port, port_pack(rq, count));
}
-
- /* After the final element, the hw should be idle */
- GEM_BUG_ON(port_count(port) == 0 &&
- !(status & GEN8_CTX_STATUS_ACTIVE_IDLE));
- if (port_count(port) == 0)
- execlists_clear_active(execlists,
- EXECLISTS_ACTIVE_USER);
}
if (head != execlists->csb_head) {
@@ -1001,6 +1108,11 @@ static void execlists_submission_tasklet(unsigned long data)
if (fw)
intel_uncore_forcewake_put(dev_priv, execlists->fw_domains);
+
+ /* If the engine is now idle, so should be the flag; and vice versa. */
+ GEM_BUG_ON(execlists_is_active(&engine->execlists,
+ EXECLISTS_ACTIVE_USER) ==
+ !port_isset(engine->execlists.port));
}
static void queue_request(struct intel_engine_cs *engine,
@@ -1010,12 +1122,16 @@ static void queue_request(struct intel_engine_cs *engine,
list_add_tail(&pt->link, &lookup_priolist(engine, pt, prio)->requests);
}
+static void __submit_queue(struct intel_engine_cs *engine, int prio)
+{
+ engine->execlists.queue_priority = prio;
+ tasklet_hi_schedule(&engine->execlists.tasklet);
+}
+
static void submit_queue(struct intel_engine_cs *engine, int prio)
{
- if (prio > engine->execlists.queue_priority) {
- engine->execlists.queue_priority = prio;
- tasklet_hi_schedule(&engine->execlists.tasklet);
- }
+ if (prio > engine->execlists.queue_priority)
+ __submit_queue(engine, prio);
}
static void execlists_submit_request(struct i915_request *request)
@@ -1148,7 +1264,10 @@ static void execlists_schedule(struct i915_request *request, int prio)
__list_del_entry(&pt->link);
queue_request(engine, pt, prio);
}
- submit_queue(engine, prio);
+
+ if (prio > engine->execlists.queue_priority &&
+ i915_sw_fence_done(&pt_to_request(pt)->submit))
+ __submit_queue(engine, prio);
}
spin_unlock_irq(&engine->timeline->lock);
@@ -1215,6 +1334,7 @@ execlists_context_pin(struct intel_engine_cs *engine,
ce->lrc_reg_state = vaddr + LRC_STATE_PN * PAGE_SIZE;
ce->lrc_reg_state[CTX_RING_BUFFER_START+1] =
i915_ggtt_offset(ce->ring->vma);
+ ce->lrc_reg_state[CTX_RING_HEAD+1] = ce->ring->head;
ce->state->obj->pin_global++;
i915_gem_context_get(ctx);
@@ -1565,14 +1685,6 @@ static int intel_init_workaround_bb(struct intel_engine_cs *engine)
return ret;
}
-static u8 gtiir[] = {
- [RCS] = 0,
- [BCS] = 0,
- [VCS] = 1,
- [VCS2] = 1,
- [VECS] = 3,
-};
-
static void enable_execlists(struct intel_engine_cs *engine)
{
struct drm_i915_private *dev_priv = engine->i915;
@@ -1632,6 +1744,10 @@ static int gen8_init_render_ring(struct intel_engine_cs *engine)
if (ret)
return ret;
+ ret = intel_whitelist_workarounds_apply(engine);
+ if (ret)
+ return ret;
+
/* We need to disable the AsyncFlip performance optimisations in order
* to use MI_WAIT_FOR_EVENT within the CS. It should already be
* programmed to '1' on all products.
@@ -1642,7 +1758,7 @@ static int gen8_init_render_ring(struct intel_engine_cs *engine)
I915_WRITE(INSTPM, _MASKED_BIT_ENABLE(INSTPM_FORCE_ORDERING));
- return init_workarounds_ring(engine);
+ return 0;
}
static int gen9_init_render_ring(struct intel_engine_cs *engine)
@@ -1653,32 +1769,11 @@ static int gen9_init_render_ring(struct intel_engine_cs *engine)
if (ret)
return ret;
- return init_workarounds_ring(engine);
-}
-
-static void reset_irq(struct intel_engine_cs *engine)
-{
- struct drm_i915_private *dev_priv = engine->i915;
- int i;
-
- GEM_BUG_ON(engine->id >= ARRAY_SIZE(gtiir));
-
- /*
- * Clear any pending interrupt state.
- *
- * We do it twice out of paranoia that some of the IIR are double
- * buffered, and if we only reset it once there may still be
- * an interrupt pending.
- */
- for (i = 0; i < 2; i++) {
- I915_WRITE(GEN8_GT_IIR(gtiir[engine->id]),
- GT_CONTEXT_SWITCH_INTERRUPT << engine->irq_shift);
- POSTING_READ(GEN8_GT_IIR(gtiir[engine->id]));
- }
- GEM_BUG_ON(I915_READ(GEN8_GT_IIR(gtiir[engine->id])) &
- (GT_CONTEXT_SWITCH_INTERRUPT << engine->irq_shift));
+ ret = intel_whitelist_workarounds_apply(engine);
+ if (ret)
+ return ret;
- clear_bit(ENGINE_IRQ_EXECLIST, &engine->irq_posted);
+ return 0;
}
static void reset_common_ring(struct intel_engine_cs *engine,
@@ -1688,14 +1783,13 @@ static void reset_common_ring(struct intel_engine_cs *engine,
struct intel_context *ce;
unsigned long flags;
- GEM_TRACE("%s seqno=%x\n",
- engine->name, request ? request->global_seqno : 0);
+ GEM_TRACE("%s request global=%x, current=%d\n",
+ engine->name, request ? request->global_seqno : 0,
+ intel_engine_get_seqno(engine));
/* See execlists_cancel_requests() for the irq/spinlock split. */
local_irq_save(flags);
- reset_irq(engine);
-
/*
* Catch up with any missed context-switch interrupts.
*
@@ -1706,15 +1800,13 @@ static void reset_common_ring(struct intel_engine_cs *engine,
* requests were completed.
*/
execlists_cancel_port_requests(execlists);
+ reset_irq(engine);
/* Push back any incomplete requests for replay after the reset. */
spin_lock(&engine->timeline->lock);
__unwind_incomplete_requests(engine);
spin_unlock(&engine->timeline->lock);
- /* Mark all CS interrupts as complete */
- execlists->active = 0;
-
local_irq_restore(flags);
/*
@@ -2006,7 +2098,7 @@ static int gen8_init_rcs_context(struct i915_request *rq)
{
int ret;
- ret = intel_ring_workarounds_emit(rq);
+ ret = intel_ctx_workarounds_emit(rq);
if (ret)
return ret;
@@ -2066,11 +2158,13 @@ static void execlists_set_default_submission(struct intel_engine_cs *engine)
engine->unpark = NULL;
engine->flags |= I915_ENGINE_SUPPORTS_STATS;
+ if (engine->i915->preempt_context)
+ engine->flags |= I915_ENGINE_HAS_PREEMPTION;
engine->i915->caps.scheduler =
I915_SCHEDULER_CAP_ENABLED |
I915_SCHEDULER_CAP_PRIORITY;
- if (engine->i915->preempt_context)
+ if (intel_engine_has_preemption(engine))
engine->i915->caps.scheduler |= I915_SCHEDULER_CAP_PREEMPTION;
}
@@ -2109,7 +2203,20 @@ logical_ring_default_vfuncs(struct intel_engine_cs *engine)
static inline void
logical_ring_default_irqs(struct intel_engine_cs *engine)
{
- unsigned shift = engine->irq_shift;
+ unsigned int shift = 0;
+
+ if (INTEL_GEN(engine->i915) < 11) {
+ const u8 irq_shifts[] = {
+ [RCS] = GEN8_RCS_IRQ_SHIFT,
+ [BCS] = GEN8_BCS_IRQ_SHIFT,
+ [VCS] = GEN8_VCS1_IRQ_SHIFT,
+ [VCS2] = GEN8_VCS2_IRQ_SHIFT,
+ [VECS] = GEN8_VECS_IRQ_SHIFT,
+ };
+
+ shift = irq_shifts[engine->id];
+ }
+
engine->irq_enable_mask = GT_RENDER_USER_INTERRUPT << shift;
engine->irq_keep_mask = GT_CONTEXT_SWITCH_INTERRUPT << shift;
}
@@ -2542,3 +2649,7 @@ void intel_lr_context_resume(struct drm_i915_private *dev_priv)
}
}
}
+
+#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
+#include "selftests/intel_lrc.c"
+#endif
diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c
index 36671a937fa4..c2f10d899329 100644
--- a/drivers/gpu/drm/i915/intel_overlay.c
+++ b/drivers/gpu/drm/i915/intel_overlay.c
@@ -807,6 +807,7 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay,
ret = PTR_ERR(vma);
goto out_pin_section;
}
+ intel_fb_obj_flush(new_bo, ORIGIN_DIRTYFB);
ret = i915_vma_put_fence(vma);
if (ret)
diff --git a/drivers/gpu/drm/i915/intel_pipe_crc.c b/drivers/gpu/drm/i915/intel_pipe_crc.c
index 1f5cd572a7ff..4f367c16e9e5 100644
--- a/drivers/gpu/drm/i915/intel_pipe_crc.c
+++ b/drivers/gpu/drm/i915/intel_pipe_crc.c
@@ -569,7 +569,8 @@ unlock:
static int ivb_pipe_crc_ctl_reg(struct drm_i915_private *dev_priv,
enum pipe pipe,
enum intel_pipe_crc_source *source,
- uint32_t *val)
+ uint32_t *val,
+ bool set_wa)
{
if (*source == INTEL_PIPE_CRC_SOURCE_AUTO)
*source = INTEL_PIPE_CRC_SOURCE_PF;
@@ -582,7 +583,7 @@ static int ivb_pipe_crc_ctl_reg(struct drm_i915_private *dev_priv,
*val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_SPRITE_IVB;
break;
case INTEL_PIPE_CRC_SOURCE_PF:
- if ((IS_HASWELL(dev_priv) ||
+ if (set_wa && (IS_HASWELL(dev_priv) ||
IS_BROADWELL(dev_priv)) && pipe == PIPE_A)
hsw_pipe_A_crc_wa(dev_priv, true);
@@ -600,7 +601,8 @@ static int ivb_pipe_crc_ctl_reg(struct drm_i915_private *dev_priv,
static int get_new_crc_ctl_reg(struct drm_i915_private *dev_priv,
enum pipe pipe,
- enum intel_pipe_crc_source *source, u32 *val)
+ enum intel_pipe_crc_source *source, u32 *val,
+ bool set_wa)
{
if (IS_GEN2(dev_priv))
return i8xx_pipe_crc_ctl_reg(source, val);
@@ -611,7 +613,7 @@ static int get_new_crc_ctl_reg(struct drm_i915_private *dev_priv,
else if (IS_GEN5(dev_priv) || IS_GEN6(dev_priv))
return ilk_pipe_crc_ctl_reg(source, val);
else
- return ivb_pipe_crc_ctl_reg(dev_priv, pipe, source, val);
+ return ivb_pipe_crc_ctl_reg(dev_priv, pipe, source, val, set_wa);
}
static int pipe_crc_set_source(struct drm_i915_private *dev_priv,
@@ -636,7 +638,7 @@ static int pipe_crc_set_source(struct drm_i915_private *dev_priv,
return -EIO;
}
- ret = get_new_crc_ctl_reg(dev_priv, pipe, &source, &val);
+ ret = get_new_crc_ctl_reg(dev_priv, pipe, &source, &val, true);
if (ret != 0)
goto out;
@@ -916,7 +918,7 @@ int intel_pipe_crc_create(struct drm_minor *minor)
int intel_crtc_set_crc_source(struct drm_crtc *crtc, const char *source_name,
size_t *values_cnt)
{
- struct drm_i915_private *dev_priv = crtc->dev->dev_private;
+ struct drm_i915_private *dev_priv = to_i915(crtc->dev);
struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[crtc->index];
enum intel_display_power_domain power_domain;
enum intel_pipe_crc_source source;
@@ -934,10 +936,11 @@ int intel_crtc_set_crc_source(struct drm_crtc *crtc, const char *source_name,
return -EIO;
}
- ret = get_new_crc_ctl_reg(dev_priv, crtc->index, &source, &val);
+ ret = get_new_crc_ctl_reg(dev_priv, crtc->index, &source, &val, true);
if (ret != 0)
goto out;
+ pipe_crc->source = source;
I915_WRITE(PIPE_CRC_CTL(crtc->index), val);
POSTING_READ(PIPE_CRC_CTL(crtc->index));
@@ -959,3 +962,39 @@ out:
return ret;
}
+
+void intel_crtc_enable_pipe_crc(struct intel_crtc *intel_crtc)
+{
+ struct drm_crtc *crtc = &intel_crtc->base;
+ struct drm_i915_private *dev_priv = to_i915(crtc->dev);
+ struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[crtc->index];
+ u32 val = 0;
+
+ if (!crtc->crc.opened)
+ return;
+
+ if (get_new_crc_ctl_reg(dev_priv, crtc->index, &pipe_crc->source, &val, false) < 0)
+ return;
+
+ /* Don't need pipe_crc->lock here, IRQs are not generated. */
+ pipe_crc->skipped = 0;
+
+ I915_WRITE(PIPE_CRC_CTL(crtc->index), val);
+ POSTING_READ(PIPE_CRC_CTL(crtc->index));
+}
+
+void intel_crtc_disable_pipe_crc(struct intel_crtc *intel_crtc)
+{
+ struct drm_crtc *crtc = &intel_crtc->base;
+ struct drm_i915_private *dev_priv = to_i915(crtc->dev);
+ struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[crtc->index];
+
+ /* Swallow crc's until we stop generating them. */
+ spin_lock_irq(&pipe_crc->lock);
+ pipe_crc->skipped = INT_MIN;
+ spin_unlock_irq(&pipe_crc->lock);
+
+ I915_WRITE(PIPE_CRC_CTL(crtc->index), 0);
+ POSTING_READ(PIPE_CRC_CTL(crtc->index));
+ synchronize_irq(dev_priv->drm.irq);
+}
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index b8da4dcdd584..4baab858e442 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -3825,6 +3825,44 @@ static void skl_ddb_entry_init_from_hw(struct skl_ddb_entry *entry, u32 reg)
entry->end += 1;
}
+static void
+skl_ddb_get_hw_plane_state(struct drm_i915_private *dev_priv,
+ const enum pipe pipe,
+ const enum plane_id plane_id,
+ struct skl_ddb_allocation *ddb /* out */)
+{
+ u32 val, val2 = 0;
+ int fourcc, pixel_format;
+
+ /* Cursor doesn't support NV12/planar, so no extra calculation needed */
+ if (plane_id == PLANE_CURSOR) {
+ val = I915_READ(CUR_BUF_CFG(pipe));
+ skl_ddb_entry_init_from_hw(&ddb->plane[pipe][plane_id], val);
+ return;
+ }
+
+ val = I915_READ(PLANE_CTL(pipe, plane_id));
+
+ /* No DDB allocated for disabled planes */
+ if (!(val & PLANE_CTL_ENABLE))
+ return;
+
+ pixel_format = val & PLANE_CTL_FORMAT_MASK;
+ fourcc = skl_format_to_fourcc(pixel_format,
+ val & PLANE_CTL_ORDER_RGBX,
+ val & PLANE_CTL_ALPHA_MASK);
+
+ val = I915_READ(PLANE_BUF_CFG(pipe, plane_id));
+ val2 = I915_READ(PLANE_NV12_BUF_CFG(pipe, plane_id));
+
+ if (fourcc == DRM_FORMAT_NV12) {
+ skl_ddb_entry_init_from_hw(&ddb->plane[pipe][plane_id], val2);
+ skl_ddb_entry_init_from_hw(&ddb->uv_plane[pipe][plane_id], val);
+ } else {
+ skl_ddb_entry_init_from_hw(&ddb->plane[pipe][plane_id], val);
+ }
+}
+
void skl_ddb_get_hw_state(struct drm_i915_private *dev_priv,
struct skl_ddb_allocation *ddb /* out */)
{
@@ -3841,16 +3879,9 @@ void skl_ddb_get_hw_state(struct drm_i915_private *dev_priv,
if (!intel_display_power_get_if_enabled(dev_priv, power_domain))
continue;
- for_each_plane_id_on_crtc(crtc, plane_id) {
- u32 val;
-
- if (plane_id != PLANE_CURSOR)
- val = I915_READ(PLANE_BUF_CFG(pipe, plane_id));
- else
- val = I915_READ(CUR_BUF_CFG(pipe));
-
- skl_ddb_entry_init_from_hw(&ddb->plane[pipe][plane_id], val);
- }
+ for_each_plane_id_on_crtc(crtc, plane_id)
+ skl_ddb_get_hw_plane_state(dev_priv, pipe,
+ plane_id, ddb);
intel_display_power_put(dev_priv, power_domain);
}
@@ -4009,9 +4040,9 @@ int skl_check_pipe_max_pixel_rate(struct intel_crtc *intel_crtc,
static unsigned int
skl_plane_relative_data_rate(const struct intel_crtc_state *cstate,
const struct drm_plane_state *pstate,
- int y)
+ const int plane)
{
- struct intel_plane *plane = to_intel_plane(pstate->plane);
+ struct intel_plane *intel_plane = to_intel_plane(pstate->plane);
struct intel_plane_state *intel_pstate = to_intel_plane_state(pstate);
uint32_t data_rate;
uint32_t width = 0, height = 0;
@@ -4025,9 +4056,9 @@ skl_plane_relative_data_rate(const struct intel_crtc_state *cstate,
fb = pstate->fb;
format = fb->format->format;
- if (plane->id == PLANE_CURSOR)
+ if (intel_plane->id == PLANE_CURSOR)
return 0;
- if (y && format != DRM_FORMAT_NV12)
+ if (plane == 1 && format != DRM_FORMAT_NV12)
return 0;
/*
@@ -4038,19 +4069,14 @@ skl_plane_relative_data_rate(const struct intel_crtc_state *cstate,
width = drm_rect_width(&intel_pstate->base.src) >> 16;
height = drm_rect_height(&intel_pstate->base.src) >> 16;
- /* for planar format */
- if (format == DRM_FORMAT_NV12) {
- if (y) /* y-plane data rate */
- data_rate = width * height *
- fb->format->cpp[0];
- else /* uv-plane data rate */
- data_rate = (width / 2) * (height / 2) *
- fb->format->cpp[1];
- } else {
- /* for packed formats */
- data_rate = width * height * fb->format->cpp[0];
+ /* UV plane does 1/2 pixel sub-sampling */
+ if (plane == 1 && format == DRM_FORMAT_NV12) {
+ width /= 2;
+ height /= 2;
}
+ data_rate = width * height * fb->format->cpp[plane];
+
down_scale_amount = skl_plane_downscale_amount(cstate, intel_pstate);
return mul_round_up_u32_fixed16(data_rate, down_scale_amount);
@@ -4063,8 +4089,8 @@ skl_plane_relative_data_rate(const struct intel_crtc_state *cstate,
*/
static unsigned int
skl_get_total_relative_data_rate(struct intel_crtc_state *intel_cstate,
- unsigned *plane_data_rate,
- unsigned *plane_y_data_rate)
+ unsigned int *plane_data_rate,
+ unsigned int *uv_plane_data_rate)
{
struct drm_crtc_state *cstate = &intel_cstate->base;
struct drm_atomic_state *state = cstate->state;
@@ -4080,17 +4106,17 @@ skl_get_total_relative_data_rate(struct intel_crtc_state *intel_cstate,
enum plane_id plane_id = to_intel_plane(plane)->id;
unsigned int rate;
- /* packed/uv */
+ /* packed/y */
rate = skl_plane_relative_data_rate(intel_cstate,
pstate, 0);
plane_data_rate[plane_id] = rate;
total_data_rate += rate;
- /* y-plane */
+ /* uv-plane */
rate = skl_plane_relative_data_rate(intel_cstate,
pstate, 1);
- plane_y_data_rate[plane_id] = rate;
+ uv_plane_data_rate[plane_id] = rate;
total_data_rate += rate;
}
@@ -4099,8 +4125,7 @@ skl_get_total_relative_data_rate(struct intel_crtc_state *intel_cstate,
}
static uint16_t
-skl_ddb_min_alloc(const struct drm_plane_state *pstate,
- const int y)
+skl_ddb_min_alloc(const struct drm_plane_state *pstate, const int plane)
{
struct drm_framebuffer *fb = pstate->fb;
struct intel_plane_state *intel_pstate = to_intel_plane_state(pstate);
@@ -4111,8 +4136,8 @@ skl_ddb_min_alloc(const struct drm_plane_state *pstate,
if (WARN_ON(!fb))
return 0;
- /* For packed formats, no y-plane, return 0 */
- if (y && fb->format->format != DRM_FORMAT_NV12)
+ /* For packed formats, and uv-plane, return 0 */
+ if (plane == 1 && fb->format->format != DRM_FORMAT_NV12)
return 0;
/* For Non Y-tile return 8-blocks */
@@ -4131,15 +4156,12 @@ skl_ddb_min_alloc(const struct drm_plane_state *pstate,
src_h = drm_rect_height(&intel_pstate->base.src) >> 16;
/* Halve UV plane width and height for NV12 */
- if (fb->format->format == DRM_FORMAT_NV12 && !y) {
+ if (plane == 1) {
src_w /= 2;
src_h /= 2;
}
- if (fb->format->format == DRM_FORMAT_NV12 && !y)
- plane_bpp = fb->format->cpp[1];
- else
- plane_bpp = fb->format->cpp[0];
+ plane_bpp = fb->format->cpp[plane];
if (drm_rotation_90_or_270(pstate->rotation)) {
switch (plane_bpp) {
@@ -4167,7 +4189,7 @@ skl_ddb_min_alloc(const struct drm_plane_state *pstate,
static void
skl_ddb_calc_min(const struct intel_crtc_state *cstate, int num_active,
- uint16_t *minimum, uint16_t *y_minimum)
+ uint16_t *minimum, uint16_t *uv_minimum)
{
const struct drm_plane_state *pstate;
struct drm_plane *plane;
@@ -4182,7 +4204,7 @@ skl_ddb_calc_min(const struct intel_crtc_state *cstate, int num_active,
continue;
minimum[plane_id] = skl_ddb_min_alloc(pstate, 0);
- y_minimum[plane_id] = skl_ddb_min_alloc(pstate, 1);
+ uv_minimum[plane_id] = skl_ddb_min_alloc(pstate, 1);
}
minimum[PLANE_CURSOR] = skl_cursor_allocation(num_active);
@@ -4200,17 +4222,17 @@ skl_allocate_pipe_ddb(struct intel_crtc_state *cstate,
struct skl_ddb_entry *alloc = &cstate->wm.skl.ddb;
uint16_t alloc_size, start;
uint16_t minimum[I915_MAX_PLANES] = {};
- uint16_t y_minimum[I915_MAX_PLANES] = {};
+ uint16_t uv_minimum[I915_MAX_PLANES] = {};
unsigned int total_data_rate;
enum plane_id plane_id;
int num_active;
- unsigned plane_data_rate[I915_MAX_PLANES] = {};
- unsigned plane_y_data_rate[I915_MAX_PLANES] = {};
+ unsigned int plane_data_rate[I915_MAX_PLANES] = {};
+ unsigned int uv_plane_data_rate[I915_MAX_PLANES] = {};
uint16_t total_min_blocks = 0;
/* Clear the partitioning for disabled planes. */
memset(ddb->plane[pipe], 0, sizeof(ddb->plane[pipe]));
- memset(ddb->y_plane[pipe], 0, sizeof(ddb->y_plane[pipe]));
+ memset(ddb->uv_plane[pipe], 0, sizeof(ddb->uv_plane[pipe]));
if (WARN_ON(!state))
return 0;
@@ -4225,7 +4247,7 @@ skl_allocate_pipe_ddb(struct intel_crtc_state *cstate,
if (alloc_size == 0)
return 0;
- skl_ddb_calc_min(cstate, num_active, minimum, y_minimum);
+ skl_ddb_calc_min(cstate, num_active, minimum, uv_minimum);
/*
* 1. Allocate the mininum required blocks for each active plane
@@ -4235,7 +4257,7 @@ skl_allocate_pipe_ddb(struct intel_crtc_state *cstate,
for_each_plane_id_on_crtc(intel_crtc, plane_id) {
total_min_blocks += minimum[plane_id];
- total_min_blocks += y_minimum[plane_id];
+ total_min_blocks += uv_minimum[plane_id];
}
if (total_min_blocks > alloc_size) {
@@ -4257,14 +4279,14 @@ skl_allocate_pipe_ddb(struct intel_crtc_state *cstate,
*/
total_data_rate = skl_get_total_relative_data_rate(cstate,
plane_data_rate,
- plane_y_data_rate);
+ uv_plane_data_rate);
if (total_data_rate == 0)
return 0;
start = alloc->start;
for_each_plane_id_on_crtc(intel_crtc, plane_id) {
- unsigned int data_rate, y_data_rate;
- uint16_t plane_blocks, y_plane_blocks = 0;
+ unsigned int data_rate, uv_data_rate;
+ uint16_t plane_blocks, uv_plane_blocks;
if (plane_id == PLANE_CURSOR)
continue;
@@ -4288,21 +4310,20 @@ skl_allocate_pipe_ddb(struct intel_crtc_state *cstate,
start += plane_blocks;
- /*
- * allocation for y_plane part of planar format:
- */
- y_data_rate = plane_y_data_rate[plane_id];
+ /* Allocate DDB for UV plane for planar format/NV12 */
+ uv_data_rate = uv_plane_data_rate[plane_id];
- y_plane_blocks = y_minimum[plane_id];
- y_plane_blocks += div_u64((uint64_t)alloc_size * y_data_rate,
- total_data_rate);
+ uv_plane_blocks = uv_minimum[plane_id];
+ uv_plane_blocks += div_u64((uint64_t)alloc_size * uv_data_rate,
+ total_data_rate);
- if (y_data_rate) {
- ddb->y_plane[pipe][plane_id].start = start;
- ddb->y_plane[pipe][plane_id].end = start + y_plane_blocks;
+ if (uv_data_rate) {
+ ddb->uv_plane[pipe][plane_id].start = start;
+ ddb->uv_plane[pipe][plane_id].end =
+ start + uv_plane_blocks;
}
- start += y_plane_blocks;
+ start += uv_plane_blocks;
}
return 0;
@@ -4398,7 +4419,7 @@ static int
skl_compute_plane_wm_params(const struct drm_i915_private *dev_priv,
struct intel_crtc_state *cstate,
const struct intel_plane_state *intel_pstate,
- struct skl_wm_params *wp)
+ struct skl_wm_params *wp, int plane_id)
{
struct intel_plane *plane = to_intel_plane(intel_pstate->base.plane);
const struct drm_plane_state *pstate = &intel_pstate->base;
@@ -4411,6 +4432,12 @@ skl_compute_plane_wm_params(const struct drm_i915_private *dev_priv,
if (!intel_wm_plane_visible(cstate, intel_pstate))
return 0;
+ /* only NV12 format has two planes */
+ if (plane_id == 1 && fb->format->format != DRM_FORMAT_NV12) {
+ DRM_DEBUG_KMS("Non NV12 format have single plane\n");
+ return -EINVAL;
+ }
+
wp->y_tiled = fb->modifier == I915_FORMAT_MOD_Y_TILED ||
fb->modifier == I915_FORMAT_MOD_Yf_TILED ||
fb->modifier == I915_FORMAT_MOD_Y_TILED_CCS ||
@@ -4418,6 +4445,7 @@ skl_compute_plane_wm_params(const struct drm_i915_private *dev_priv,
wp->x_tiled = fb->modifier == I915_FORMAT_MOD_X_TILED;
wp->rc_surface = fb->modifier == I915_FORMAT_MOD_Y_TILED_CCS ||
fb->modifier == I915_FORMAT_MOD_Yf_TILED_CCS;
+ wp->is_planar = fb->format->format == DRM_FORMAT_NV12;
if (plane->id == PLANE_CURSOR) {
wp->width = intel_pstate->base.crtc_w;
@@ -4430,8 +4458,10 @@ skl_compute_plane_wm_params(const struct drm_i915_private *dev_priv,
wp->width = drm_rect_width(&intel_pstate->base.src) >> 16;
}
- wp->cpp = (fb->format->format == DRM_FORMAT_NV12) ? fb->format->cpp[1] :
- fb->format->cpp[0];
+ if (plane_id == 1 && wp->is_planar)
+ wp->width /= 2;
+
+ wp->cpp = fb->format->cpp[plane_id];
wp->plane_pixel_rate = skl_adjusted_plane_pixel_rate(cstate,
intel_pstate);
@@ -4499,9 +4529,8 @@ static int skl_compute_plane_wm(const struct drm_i915_private *dev_priv,
uint16_t ddb_allocation,
int level,
const struct skl_wm_params *wp,
- uint16_t *out_blocks, /* out */
- uint8_t *out_lines, /* out */
- bool *enabled /* out */)
+ const struct skl_wm_level *result_prev,
+ struct skl_wm_level *result /* out */)
{
const struct drm_plane_state *pstate = &intel_pstate->base;
uint32_t latency = dev_priv->wm.skl_latency[level];
@@ -4515,7 +4544,7 @@ static int skl_compute_plane_wm(const struct drm_i915_private *dev_priv,
if (latency == 0 ||
!intel_wm_plane_visible(cstate, intel_pstate)) {
- *enabled = false;
+ result->plane_en = false;
return 0;
}
@@ -4568,6 +4597,15 @@ static int skl_compute_plane_wm(const struct drm_i915_private *dev_priv,
} else {
res_blocks++;
}
+
+ /*
+ * Make sure result blocks for higher latency levels are atleast
+ * as high as level below the current level.
+ * Assumption in DDB algorithm optimization for special cases.
+ * Also covers Display WA #1125 for RC.
+ */
+ if (result_prev->plane_res_b > res_blocks)
+ res_blocks = result_prev->plane_res_b;
}
if (INTEL_GEN(dev_priv) >= 11) {
@@ -4596,7 +4634,7 @@ static int skl_compute_plane_wm(const struct drm_i915_private *dev_priv,
if ((level > 0 && res_lines > 31) ||
res_blocks >= ddb_allocation ||
min_disp_buf_needed >= ddb_allocation) {
- *enabled = false;
+ result->plane_en = false;
/*
* If there are no valid level 0 watermarks, then we can't
@@ -4615,10 +4653,21 @@ static int skl_compute_plane_wm(const struct drm_i915_private *dev_priv,
}
}
+ /*
+ * Display WA #826 (SKL:ALL, BXT:ALL) & #1059 (CNL:A)
+ * disable wm level 1-7 on NV12 planes
+ */
+ if (wp->is_planar && level >= 1 &&
+ (IS_SKYLAKE(dev_priv) || IS_BROXTON(dev_priv) ||
+ IS_CNL_REVID(dev_priv, CNL_REVID_A0, CNL_REVID_A0))) {
+ result->plane_en = false;
+ return 0;
+ }
+
/* The number of lines are ignored for the level 0 watermark. */
- *out_lines = level ? res_lines : 0;
- *out_blocks = res_blocks;
- *enabled = true;
+ result->plane_res_b = res_blocks;
+ result->plane_res_l = res_lines;
+ result->plane_en = true;
return 0;
}
@@ -4629,7 +4678,8 @@ skl_compute_wm_levels(const struct drm_i915_private *dev_priv,
struct intel_crtc_state *cstate,
const struct intel_plane_state *intel_pstate,
const struct skl_wm_params *wm_params,
- struct skl_plane_wm *wm)
+ struct skl_plane_wm *wm,
+ int plane_id)
{
struct intel_crtc *intel_crtc = to_intel_crtc(cstate->base.crtc);
struct drm_plane *plane = intel_pstate->base.plane;
@@ -4637,15 +4687,26 @@ skl_compute_wm_levels(const struct drm_i915_private *dev_priv,
uint16_t ddb_blocks;
enum pipe pipe = intel_crtc->pipe;
int level, max_level = ilk_wm_max_level(dev_priv);
+ enum plane_id intel_plane_id = intel_plane->id;
int ret;
if (WARN_ON(!intel_pstate->base.fb))
return -EINVAL;
- ddb_blocks = skl_ddb_entry_size(&ddb->plane[pipe][intel_plane->id]);
+ ddb_blocks = plane_id ?
+ skl_ddb_entry_size(&ddb->uv_plane[pipe][intel_plane_id]) :
+ skl_ddb_entry_size(&ddb->plane[pipe][intel_plane_id]);
for (level = 0; level <= max_level; level++) {
- struct skl_wm_level *result = &wm->wm[level];
+ struct skl_wm_level *result = plane_id ? &wm->uv_wm[level] :
+ &wm->wm[level];
+ struct skl_wm_level *result_prev;
+
+ if (level)
+ result_prev = plane_id ? &wm->uv_wm[level - 1] :
+ &wm->wm[level - 1];
+ else
+ result_prev = plane_id ? &wm->uv_wm[0] : &wm->wm[0];
ret = skl_compute_plane_wm(dev_priv,
cstate,
@@ -4653,13 +4714,15 @@ skl_compute_wm_levels(const struct drm_i915_private *dev_priv,
ddb_blocks,
level,
wm_params,
- &result->plane_res_b,
- &result->plane_res_l,
- &result->plane_en);
+ result_prev,
+ result);
if (ret)
return ret;
}
+ if (intel_pstate->base.fb->format->format == DRM_FORMAT_NV12)
+ wm->is_planar = true;
+
return 0;
}
@@ -4769,20 +4832,39 @@ static int skl_build_pipe_wm(struct intel_crtc_state *cstate,
wm = &pipe_wm->planes[plane_id];
ddb_blocks = skl_ddb_entry_size(&ddb->plane[pipe][plane_id]);
- memset(&wm_params, 0, sizeof(struct skl_wm_params));
ret = skl_compute_plane_wm_params(dev_priv, cstate,
- intel_pstate, &wm_params);
+ intel_pstate, &wm_params, 0);
if (ret)
return ret;
ret = skl_compute_wm_levels(dev_priv, ddb, cstate,
- intel_pstate, &wm_params, wm);
+ intel_pstate, &wm_params, wm, 0);
if (ret)
return ret;
+
skl_compute_transition_wm(cstate, &wm_params, &wm->wm[0],
ddb_blocks, &wm->trans_wm);
+
+ /* uv plane watermarks must also be validated for NV12/Planar */
+ if (wm_params.is_planar) {
+ memset(&wm_params, 0, sizeof(struct skl_wm_params));
+ wm->is_planar = true;
+
+ ret = skl_compute_plane_wm_params(dev_priv, cstate,
+ intel_pstate,
+ &wm_params, 1);
+ if (ret)
+ return ret;
+
+ ret = skl_compute_wm_levels(dev_priv, ddb, cstate,
+ intel_pstate, &wm_params,
+ wm, 1);
+ if (ret)
+ return ret;
+ }
}
+
pipe_wm->linetime = skl_compute_linetime_wm(cstate);
return 0;
@@ -4833,10 +4915,21 @@ static void skl_write_plane_wm(struct intel_crtc *intel_crtc,
skl_ddb_entry_write(dev_priv, PLANE_BUF_CFG(pipe, plane_id),
&ddb->plane[pipe][plane_id]);
- if (INTEL_GEN(dev_priv) < 11)
+ if (INTEL_GEN(dev_priv) >= 11)
+ return skl_ddb_entry_write(dev_priv,
+ PLANE_BUF_CFG(pipe, plane_id),
+ &ddb->plane[pipe][plane_id]);
+ if (wm->is_planar) {
+ skl_ddb_entry_write(dev_priv, PLANE_BUF_CFG(pipe, plane_id),
+ &ddb->uv_plane[pipe][plane_id]);
skl_ddb_entry_write(dev_priv,
PLANE_NV12_BUF_CFG(pipe, plane_id),
- &ddb->y_plane[pipe][plane_id]);
+ &ddb->plane[pipe][plane_id]);
+ } else {
+ skl_ddb_entry_write(dev_priv, PLANE_BUF_CFG(pipe, plane_id),
+ &ddb->plane[pipe][plane_id]);
+ I915_WRITE(PLANE_NV12_BUF_CFG(pipe, plane_id), 0x0);
+ }
}
static void skl_write_cursor_wm(struct intel_crtc *intel_crtc,
@@ -4944,15 +5037,13 @@ skl_ddb_add_affected_planes(struct intel_crtc_state *cstate)
struct drm_plane *plane;
enum pipe pipe = intel_crtc->pipe;
- WARN_ON(!drm_atomic_get_existing_crtc_state(state, crtc));
-
drm_for_each_plane_mask(plane, dev, cstate->base.plane_mask) {
enum plane_id plane_id = to_intel_plane(plane)->id;
if (skl_ddb_entry_equal(&cur_ddb->plane[pipe][plane_id],
&new_ddb->plane[pipe][plane_id]) &&
- skl_ddb_entry_equal(&cur_ddb->y_plane[pipe][plane_id],
- &new_ddb->y_plane[pipe][plane_id]))
+ skl_ddb_entry_equal(&cur_ddb->uv_plane[pipe][plane_id],
+ &new_ddb->uv_plane[pipe][plane_id]))
continue;
plane_state = drm_atomic_get_plane_state(state, plane);
@@ -4966,69 +5057,16 @@ skl_ddb_add_affected_planes(struct intel_crtc_state *cstate)
static int
skl_compute_ddb(struct drm_atomic_state *state)
{
- struct drm_device *dev = state->dev;
- struct drm_i915_private *dev_priv = to_i915(dev);
+ const struct drm_i915_private *dev_priv = to_i915(state->dev);
struct intel_atomic_state *intel_state = to_intel_atomic_state(state);
- struct intel_crtc *intel_crtc;
struct skl_ddb_allocation *ddb = &intel_state->wm_results.ddb;
- uint32_t realloc_pipes = pipes_modified(state);
- int ret;
-
- /*
- * If this is our first atomic update following hardware readout,
- * we can't trust the DDB that the BIOS programmed for us. Let's
- * pretend that all pipes switched active status so that we'll
- * ensure a full DDB recompute.
- */
- if (dev_priv->wm.distrust_bios_wm) {
- ret = drm_modeset_lock(&dev->mode_config.connection_mutex,
- state->acquire_ctx);
- if (ret)
- return ret;
-
- intel_state->active_pipe_changes = ~0;
-
- /*
- * We usually only initialize intel_state->active_crtcs if we
- * we're doing a modeset; make sure this field is always
- * initialized during the sanitization process that happens
- * on the first commit too.
- */
- if (!intel_state->modeset)
- intel_state->active_crtcs = dev_priv->active_crtcs;
- }
-
- /*
- * If the modeset changes which CRTC's are active, we need to
- * recompute the DDB allocation for *all* active pipes, even
- * those that weren't otherwise being modified in any way by this
- * atomic commit. Due to the shrinking of the per-pipe allocations
- * when new active CRTC's are added, it's possible for a pipe that
- * we were already using and aren't changing at all here to suddenly
- * become invalid if its DDB needs exceeds its new allocation.
- *
- * Note that if we wind up doing a full DDB recompute, we can't let
- * any other display updates race with this transaction, so we need
- * to grab the lock on *all* CRTC's.
- */
- if (intel_state->active_pipe_changes) {
- realloc_pipes = ~0;
- intel_state->wm_results.dirty_pipes = ~0;
- }
+ struct intel_crtc *crtc;
+ struct intel_crtc_state *cstate;
+ int ret, i;
- /*
- * We're not recomputing for the pipes not included in the commit, so
- * make sure we start with the current state.
- */
memcpy(ddb, &dev_priv->wm.skl_hw.ddb, sizeof(*ddb));
- for_each_intel_crtc_mask(dev, intel_crtc, realloc_pipes) {
- struct intel_crtc_state *cstate;
-
- cstate = intel_atomic_get_crtc_state(state, intel_crtc);
- if (IS_ERR(cstate))
- return PTR_ERR(cstate);
-
+ for_each_new_intel_crtc_in_state(intel_state, crtc, cstate, i) {
ret = skl_allocate_pipe_ddb(cstate, ddb);
if (ret)
return ret;
@@ -5042,12 +5080,12 @@ skl_compute_ddb(struct drm_atomic_state *state)
}
static void
-skl_copy_wm_for_pipe(struct skl_wm_values *dst,
- struct skl_wm_values *src,
- enum pipe pipe)
+skl_copy_ddb_for_pipe(struct skl_ddb_values *dst,
+ struct skl_ddb_values *src,
+ enum pipe pipe)
{
- memcpy(dst->ddb.y_plane[pipe], src->ddb.y_plane[pipe],
- sizeof(dst->ddb.y_plane[pipe]));
+ memcpy(dst->ddb.uv_plane[pipe], src->ddb.uv_plane[pipe],
+ sizeof(dst->ddb.uv_plane[pipe]));
memcpy(dst->ddb.plane[pipe], src->ddb.plane[pipe],
sizeof(dst->ddb.plane[pipe]));
}
@@ -5090,23 +5128,23 @@ skl_print_wm_changes(const struct drm_atomic_state *state)
}
static int
-skl_compute_wm(struct drm_atomic_state *state)
+skl_ddb_add_affected_pipes(struct drm_atomic_state *state, bool *changed)
{
- struct drm_crtc *crtc;
- struct drm_crtc_state *cstate;
- struct intel_atomic_state *intel_state = to_intel_atomic_state(state);
- struct skl_wm_values *results = &intel_state->wm_results;
struct drm_device *dev = state->dev;
- struct skl_pipe_wm *pipe_wm;
- bool changed = false;
+ const struct drm_i915_private *dev_priv = to_i915(dev);
+ const struct drm_crtc *crtc;
+ const struct drm_crtc_state *cstate;
+ struct intel_crtc *intel_crtc;
+ struct intel_atomic_state *intel_state = to_intel_atomic_state(state);
+ uint32_t realloc_pipes = pipes_modified(state);
int ret, i;
/*
* When we distrust bios wm we always need to recompute to set the
* expected DDB allocations for each CRTC.
*/
- if (to_i915(dev)->wm.distrust_bios_wm)
- changed = true;
+ if (dev_priv->wm.distrust_bios_wm)
+ (*changed) = true;
/*
* If this transaction isn't actually touching any CRTC's, don't
@@ -5117,14 +5155,86 @@ skl_compute_wm(struct drm_atomic_state *state)
* hold _all_ CRTC state mutexes.
*/
for_each_new_crtc_in_state(state, crtc, cstate, i)
- changed = true;
+ (*changed) = true;
- if (!changed)
+ if (!*changed)
return 0;
+ /*
+ * If this is our first atomic update following hardware readout,
+ * we can't trust the DDB that the BIOS programmed for us. Let's
+ * pretend that all pipes switched active status so that we'll
+ * ensure a full DDB recompute.
+ */
+ if (dev_priv->wm.distrust_bios_wm) {
+ ret = drm_modeset_lock(&dev->mode_config.connection_mutex,
+ state->acquire_ctx);
+ if (ret)
+ return ret;
+
+ intel_state->active_pipe_changes = ~0;
+
+ /*
+ * We usually only initialize intel_state->active_crtcs if we
+ * we're doing a modeset; make sure this field is always
+ * initialized during the sanitization process that happens
+ * on the first commit too.
+ */
+ if (!intel_state->modeset)
+ intel_state->active_crtcs = dev_priv->active_crtcs;
+ }
+
+ /*
+ * If the modeset changes which CRTC's are active, we need to
+ * recompute the DDB allocation for *all* active pipes, even
+ * those that weren't otherwise being modified in any way by this
+ * atomic commit. Due to the shrinking of the per-pipe allocations
+ * when new active CRTC's are added, it's possible for a pipe that
+ * we were already using and aren't changing at all here to suddenly
+ * become invalid if its DDB needs exceeds its new allocation.
+ *
+ * Note that if we wind up doing a full DDB recompute, we can't let
+ * any other display updates race with this transaction, so we need
+ * to grab the lock on *all* CRTC's.
+ */
+ if (intel_state->active_pipe_changes) {
+ realloc_pipes = ~0;
+ intel_state->wm_results.dirty_pipes = ~0;
+ }
+
+ /*
+ * We're not recomputing for the pipes not included in the commit, so
+ * make sure we start with the current state.
+ */
+ for_each_intel_crtc_mask(dev, intel_crtc, realloc_pipes) {
+ struct intel_crtc_state *cstate;
+
+ cstate = intel_atomic_get_crtc_state(state, intel_crtc);
+ if (IS_ERR(cstate))
+ return PTR_ERR(cstate);
+ }
+
+ return 0;
+}
+
+static int
+skl_compute_wm(struct drm_atomic_state *state)
+{
+ struct drm_crtc *crtc;
+ struct drm_crtc_state *cstate;
+ struct intel_atomic_state *intel_state = to_intel_atomic_state(state);
+ struct skl_ddb_values *results = &intel_state->wm_results;
+ struct skl_pipe_wm *pipe_wm;
+ bool changed = false;
+ int ret, i;
+
/* Clear all dirty flags */
results->dirty_pipes = 0;
+ ret = skl_ddb_add_affected_pipes(state, &changed);
+ if (ret || !changed)
+ return ret;
+
ret = skl_compute_ddb(state);
if (ret)
return ret;
@@ -5197,8 +5307,8 @@ static void skl_initial_wm(struct intel_atomic_state *state,
struct intel_crtc *intel_crtc = to_intel_crtc(cstate->base.crtc);
struct drm_device *dev = intel_crtc->base.dev;
struct drm_i915_private *dev_priv = to_i915(dev);
- struct skl_wm_values *results = &state->wm_results;
- struct skl_wm_values *hw_vals = &dev_priv->wm.skl_hw;
+ struct skl_ddb_values *results = &state->wm_results;
+ struct skl_ddb_values *hw_vals = &dev_priv->wm.skl_hw;
enum pipe pipe = intel_crtc->pipe;
if ((results->dirty_pipes & drm_crtc_mask(&intel_crtc->base)) == 0)
@@ -5209,7 +5319,7 @@ static void skl_initial_wm(struct intel_atomic_state *state,
if (cstate->base.active_changed)
skl_atomic_update_crtc_wm(state, cstate);
- skl_copy_wm_for_pipe(hw_vals, results, pipe);
+ skl_copy_ddb_for_pipe(hw_vals, results, pipe);
mutex_unlock(&dev_priv->wm.wm_mutex);
}
@@ -5341,7 +5451,7 @@ void skl_pipe_wm_get_hw_state(struct drm_crtc *crtc,
void skl_wm_get_hw_state(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = to_i915(dev);
- struct skl_wm_values *hw = &dev_priv->wm.skl_hw;
+ struct skl_ddb_values *hw = &dev_priv->wm.skl_hw;
struct skl_ddb_allocation *ddb = &dev_priv->wm.skl_hw.ddb;
struct drm_crtc *crtc;
struct intel_crtc *intel_crtc;
@@ -6572,7 +6682,7 @@ static void gen6_init_rps_frequencies(struct drm_i915_private *dev_priv)
rps->efficient_freq = rps->rp1_freq;
if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv) ||
- IS_GEN9_BC(dev_priv) || IS_CANNONLAKE(dev_priv)) {
+ IS_GEN9_BC(dev_priv) || INTEL_GEN(dev_priv) >= 10) {
u32 ddcc_status = 0;
if (sandybridge_pcode_read(dev_priv,
@@ -6585,7 +6695,7 @@ static void gen6_init_rps_frequencies(struct drm_i915_private *dev_priv)
rps->max_freq);
}
- if (IS_GEN9_BC(dev_priv) || IS_CANNONLAKE(dev_priv)) {
+ if (IS_GEN9_BC(dev_priv) || INTEL_GEN(dev_priv) >= 10) {
/* Store the frequency values in 16.66 MHZ units, which is
* the natural hardware unit for SKL
*/
@@ -6890,15 +7000,18 @@ static void gen6_enable_rps(struct drm_i915_private *dev_priv)
static void gen6_update_ring_freq(struct drm_i915_private *dev_priv)
{
struct intel_rps *rps = &dev_priv->gt_pm.rps;
- int min_freq = 15;
+ const int min_freq = 15;
+ const int scaling_factor = 180;
unsigned int gpu_freq;
unsigned int max_ia_freq, min_ring_freq;
unsigned int max_gpu_freq, min_gpu_freq;
- int scaling_factor = 180;
struct cpufreq_policy *policy;
WARN_ON(!mutex_is_locked(&dev_priv->pcu_lock));
+ if (rps->max_freq <= rps->min_freq)
+ return;
+
policy = cpufreq_cpu_get(0);
if (policy) {
max_ia_freq = policy->cpuinfo.max_freq;
@@ -6918,13 +7031,12 @@ static void gen6_update_ring_freq(struct drm_i915_private *dev_priv)
/* convert DDR frequency from units of 266.6MHz to bandwidth */
min_ring_freq = mult_frac(min_ring_freq, 8, 3);
- if (IS_GEN9_BC(dev_priv) || IS_CANNONLAKE(dev_priv)) {
+ min_gpu_freq = rps->min_freq;
+ max_gpu_freq = rps->max_freq;
+ if (IS_GEN9_BC(dev_priv) || INTEL_GEN(dev_priv) >= 10) {
/* Convert GT frequency to 50 HZ units */
- min_gpu_freq = rps->min_freq / GEN9_FREQ_SCALER;
- max_gpu_freq = rps->max_freq / GEN9_FREQ_SCALER;
- } else {
- min_gpu_freq = rps->min_freq;
- max_gpu_freq = rps->max_freq;
+ min_gpu_freq /= GEN9_FREQ_SCALER;
+ max_gpu_freq /= GEN9_FREQ_SCALER;
}
/*
@@ -6933,10 +7045,10 @@ static void gen6_update_ring_freq(struct drm_i915_private *dev_priv)
* the PCU should use as a reference to determine the ring frequency.
*/
for (gpu_freq = max_gpu_freq; gpu_freq >= min_gpu_freq; gpu_freq--) {
- int diff = max_gpu_freq - gpu_freq;
+ const int diff = max_gpu_freq - gpu_freq;
unsigned int ia_freq = 0, ring_freq = 0;
- if (IS_GEN9_BC(dev_priv) || IS_CANNONLAKE(dev_priv)) {
+ if (IS_GEN9_BC(dev_priv) || INTEL_GEN(dev_priv) >= 10) {
/*
* ring_freq = 2 * GT. ring_freq is in 100MHz units
* No floor required for ring frequency on SKL.
@@ -8026,10 +8138,10 @@ void intel_sanitize_gt_powersave(struct drm_i915_private *dev_priv)
dev_priv->gt_pm.rc6.enabled = true; /* force RC6 disabling */
intel_disable_gt_powersave(dev_priv);
- if (INTEL_GEN(dev_priv) < 11)
- gen6_reset_rps_interrupts(dev_priv);
+ if (INTEL_GEN(dev_priv) >= 11)
+ gen11_reset_rps_interrupts(dev_priv);
else
- WARN_ON_ONCE(1);
+ gen6_reset_rps_interrupts(dev_priv);
}
static inline void intel_disable_llc_pstate(struct drm_i915_private *i915)
@@ -8142,8 +8254,6 @@ static void intel_enable_rps(struct drm_i915_private *dev_priv)
cherryview_enable_rps(dev_priv);
} else if (IS_VALLEYVIEW(dev_priv)) {
valleyview_enable_rps(dev_priv);
- } else if (WARN_ON_ONCE(INTEL_GEN(dev_priv) >= 11)) {
- /* TODO */
} else if (INTEL_GEN(dev_priv) >= 9) {
gen9_enable_rps(dev_priv);
} else if (IS_BROADWELL(dev_priv)) {
diff --git a/drivers/gpu/drm/i915/intel_psr.c b/drivers/gpu/drm/i915/intel_psr.c
index 23175c5c4a50..69a5b276f4d8 100644
--- a/drivers/gpu/drm/i915/intel_psr.c
+++ b/drivers/gpu/drm/i915/intel_psr.c
@@ -93,7 +93,7 @@ static void psr_aux_io_power_put(struct intel_dp *intel_dp)
intel_display_power_put(dev_priv, psr_aux_domain(intel_dp));
}
-static bool intel_dp_get_y_cord_status(struct intel_dp *intel_dp)
+static bool intel_dp_get_y_coord_required(struct intel_dp *intel_dp)
{
uint8_t psr_caps = 0;
@@ -122,6 +122,18 @@ static bool intel_dp_get_alpm_status(struct intel_dp *intel_dp)
return alpm_caps & DP_ALPM_CAP;
}
+static u8 intel_dp_get_sink_sync_latency(struct intel_dp *intel_dp)
+{
+ u8 val = 0;
+
+ if (drm_dp_dpcd_readb(&intel_dp->aux,
+ DP_SYNCHRONIZATION_LATENCY_IN_SINK, &val) == 1)
+ val &= DP_MAX_RESYNC_FRAME_COUNT_MASK;
+ else
+ DRM_ERROR("Unable to get sink synchronization latency\n");
+ return val;
+}
+
void intel_psr_init_dpcd(struct intel_dp *intel_dp)
{
struct drm_i915_private *dev_priv =
@@ -130,33 +142,36 @@ void intel_psr_init_dpcd(struct intel_dp *intel_dp)
drm_dp_dpcd_read(&intel_dp->aux, DP_PSR_SUPPORT, intel_dp->psr_dpcd,
sizeof(intel_dp->psr_dpcd));
- if (intel_dp->psr_dpcd[0] & DP_PSR_IS_SUPPORTED) {
+ if (intel_dp->psr_dpcd[0]) {
dev_priv->psr.sink_support = true;
DRM_DEBUG_KMS("Detected EDP PSR Panel.\n");
}
if (INTEL_GEN(dev_priv) >= 9 &&
- (intel_dp->psr_dpcd[0] & DP_PSR2_IS_SUPPORTED)) {
- uint8_t frame_sync_cap;
+ (intel_dp->psr_dpcd[0] == DP_PSR2_WITH_Y_COORD_IS_SUPPORTED)) {
+ /*
+ * All panels that supports PSR version 03h (PSR2 +
+ * Y-coordinate) can handle Y-coordinates in VSC but we are
+ * only sure that it is going to be used when required by the
+ * panel. This way panel is capable to do selective update
+ * without a aux frame sync.
+ *
+ * To support PSR version 02h and PSR version 03h without
+ * Y-coordinate requirement panels we would need to enable
+ * GTC first.
+ */
+ dev_priv->psr.sink_psr2_support =
+ intel_dp_get_y_coord_required(intel_dp);
+ DRM_DEBUG_KMS("PSR2 %s on sink", dev_priv->psr.sink_psr2_support
+ ? "supported" : "not supported");
- dev_priv->psr.sink_support = true;
- if (drm_dp_dpcd_readb(&intel_dp->aux,
- DP_SINK_DEVICE_AUX_FRAME_SYNC_CAP,
- &frame_sync_cap) != 1)
- frame_sync_cap = 0;
- dev_priv->psr.aux_frame_sync = frame_sync_cap & DP_AUX_FRAME_SYNC_CAP;
- /* PSR2 needs frame sync as well */
- dev_priv->psr.psr2_support = dev_priv->psr.aux_frame_sync;
- DRM_DEBUG_KMS("PSR2 %s on sink",
- dev_priv->psr.psr2_support ? "supported" : "not supported");
-
- if (dev_priv->psr.psr2_support) {
- dev_priv->psr.y_cord_support =
- intel_dp_get_y_cord_status(intel_dp);
+ if (dev_priv->psr.sink_psr2_support) {
dev_priv->psr.colorimetry_support =
intel_dp_get_colorimetry_status(intel_dp);
dev_priv->psr.alpm =
intel_dp_get_alpm_status(intel_dp);
+ dev_priv->psr.sink_sync_latency =
+ intel_dp_get_sink_sync_latency(intel_dp);
}
}
}
@@ -193,21 +208,17 @@ static void hsw_psr_setup_vsc(struct intel_dp *intel_dp,
struct drm_i915_private *dev_priv = to_i915(intel_dig_port->base.base.dev);
struct edp_vsc_psr psr_vsc;
- if (dev_priv->psr.psr2_support) {
+ if (dev_priv->psr.psr2_enabled) {
/* Prepare VSC Header for SU as per EDP 1.4 spec, Table 6.11 */
memset(&psr_vsc, 0, sizeof(psr_vsc));
psr_vsc.sdp_header.HB0 = 0;
psr_vsc.sdp_header.HB1 = 0x7;
- if (dev_priv->psr.colorimetry_support &&
- dev_priv->psr.y_cord_support) {
+ if (dev_priv->psr.colorimetry_support) {
psr_vsc.sdp_header.HB2 = 0x5;
psr_vsc.sdp_header.HB3 = 0x13;
- } else if (dev_priv->psr.y_cord_support) {
+ } else {
psr_vsc.sdp_header.HB2 = 0x4;
psr_vsc.sdp_header.HB3 = 0xe;
- } else {
- psr_vsc.sdp_header.HB2 = 0x3;
- psr_vsc.sdp_header.HB3 = 0xc;
}
} else {
/* Prepare VSC packet as per EDP 1.3 spec, Table 3.10 */
@@ -228,31 +239,12 @@ static void vlv_psr_enable_sink(struct intel_dp *intel_dp)
DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE);
}
-static i915_reg_t psr_aux_ctl_reg(struct drm_i915_private *dev_priv,
- enum port port)
-{
- if (INTEL_GEN(dev_priv) >= 9)
- return DP_AUX_CH_CTL(port);
- else
- return EDP_PSR_AUX_CTL;
-}
-
-static i915_reg_t psr_aux_data_reg(struct drm_i915_private *dev_priv,
- enum port port, int index)
-{
- if (INTEL_GEN(dev_priv) >= 9)
- return DP_AUX_CH_DATA(port, index);
- else
- return EDP_PSR_AUX_DATA(index);
-}
-
-static void hsw_psr_enable_sink(struct intel_dp *intel_dp)
+static void hsw_psr_setup_aux(struct intel_dp *intel_dp)
{
struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
- struct drm_device *dev = dig_port->base.base.dev;
- struct drm_i915_private *dev_priv = to_i915(dev);
- uint32_t aux_clock_divider;
- i915_reg_t aux_ctl_reg;
+ struct drm_i915_private *dev_priv = to_i915(dig_port->base.base.dev);
+ u32 aux_clock_divider, aux_ctl;
+ int i;
static const uint8_t aux_msg[] = {
[0] = DP_AUX_NATIVE_WRITE << 4,
[1] = DP_SET_POWER >> 8,
@@ -260,41 +252,47 @@ static void hsw_psr_enable_sink(struct intel_dp *intel_dp)
[3] = 1 - 1,
[4] = DP_SET_POWER_D0,
};
- enum port port = dig_port->base.port;
- u32 aux_ctl;
- int i;
+ u32 psr_aux_mask = EDP_PSR_AUX_CTL_TIME_OUT_MASK |
+ EDP_PSR_AUX_CTL_MESSAGE_SIZE_MASK |
+ EDP_PSR_AUX_CTL_PRECHARGE_2US_MASK |
+ EDP_PSR_AUX_CTL_BIT_CLOCK_2X_MASK;
BUILD_BUG_ON(sizeof(aux_msg) > 20);
+ for (i = 0; i < sizeof(aux_msg); i += 4)
+ I915_WRITE(EDP_PSR_AUX_DATA(i >> 2),
+ intel_dp_pack_aux(&aux_msg[i], sizeof(aux_msg) - i));
aux_clock_divider = intel_dp->get_aux_clock_divider(intel_dp, 0);
- /* Enable AUX frame sync at sink */
- if (dev_priv->psr.aux_frame_sync)
- drm_dp_dpcd_writeb(&intel_dp->aux,
- DP_SINK_DEVICE_AUX_FRAME_SYNC_CONF,
- DP_AUX_FRAME_SYNC_ENABLE);
+ /* Start with bits set for DDI_AUX_CTL register */
+ aux_ctl = intel_dp->get_aux_send_ctl(intel_dp, 0, sizeof(aux_msg),
+ aux_clock_divider);
+
+ /* Select only valid bits for SRD_AUX_CTL */
+ aux_ctl &= psr_aux_mask;
+ I915_WRITE(EDP_PSR_AUX_CTL, aux_ctl);
+}
+
+static void hsw_psr_enable_sink(struct intel_dp *intel_dp)
+{
+ struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
+ struct drm_device *dev = dig_port->base.base.dev;
+ struct drm_i915_private *dev_priv = to_i915(dev);
+ u8 dpcd_val = DP_PSR_ENABLE;
+
/* Enable ALPM at sink for psr2 */
- if (dev_priv->psr.psr2_support && dev_priv->psr.alpm)
+ if (dev_priv->psr.psr2_enabled && dev_priv->psr.alpm)
drm_dp_dpcd_writeb(&intel_dp->aux,
DP_RECEIVER_ALPM_CONFIG,
DP_ALPM_ENABLE);
- if (dev_priv->psr.link_standby)
- drm_dp_dpcd_writeb(&intel_dp->aux, DP_PSR_EN_CFG,
- DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE);
- else
- drm_dp_dpcd_writeb(&intel_dp->aux, DP_PSR_EN_CFG,
- DP_PSR_ENABLE);
-
- aux_ctl_reg = psr_aux_ctl_reg(dev_priv, port);
- /* Setup AUX registers */
- for (i = 0; i < sizeof(aux_msg); i += 4)
- I915_WRITE(psr_aux_data_reg(dev_priv, port, i >> 2),
- intel_dp_pack_aux(&aux_msg[i], sizeof(aux_msg) - i));
+ if (dev_priv->psr.psr2_enabled)
+ dpcd_val |= DP_PSR_ENABLE_PSR2;
+ if (dev_priv->psr.link_standby)
+ dpcd_val |= DP_PSR_MAIN_LINK_ACTIVE;
+ drm_dp_dpcd_writeb(&intel_dp->aux, DP_PSR_EN_CFG, dpcd_val);
- aux_ctl = intel_dp->get_aux_send_ctl(intel_dp, 0, sizeof(aux_msg),
- aux_clock_divider);
- I915_WRITE(aux_ctl_reg, aux_ctl);
+ drm_dp_dpcd_writeb(&intel_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
}
static void vlv_psr_enable_source(struct intel_dp *intel_dp,
@@ -396,25 +394,17 @@ static void hsw_activate_psr2(struct intel_dp *intel_dp)
* with the 5 or 6 idle patterns.
*/
uint32_t idle_frames = max(6, dev_priv->vbt.psr.idle_frames);
- uint32_t val;
- uint8_t sink_latency;
-
- val = idle_frames << EDP_PSR_IDLE_FRAME_SHIFT;
+ u32 val = idle_frames << EDP_PSR2_IDLE_FRAME_SHIFT;
/* FIXME: selective update is probably totally broken because it doesn't
* mesh at all with our frontbuffer tracking. And the hw alone isn't
* good enough. */
- val |= EDP_PSR2_ENABLE |
- EDP_SU_TRACK_ENABLE;
-
- if (drm_dp_dpcd_readb(&intel_dp->aux,
- DP_SYNCHRONIZATION_LATENCY_IN_SINK,
- &sink_latency) == 1) {
- sink_latency &= DP_MAX_RESYNC_FRAME_COUNT_MASK;
- } else {
- sink_latency = 0;
+ val |= EDP_PSR2_ENABLE | EDP_SU_TRACK_ENABLE;
+ if (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv)) {
+ val |= EDP_Y_COORDINATE_VALID | EDP_Y_COORDINATE_ENABLE;
}
- val |= EDP_PSR2_FRAME_BEFORE_SU(sink_latency + 1);
+
+ val |= EDP_PSR2_FRAME_BEFORE_SU(dev_priv->psr.sink_sync_latency + 1);
if (dev_priv->vbt.psr.tp2_tp3_wakeup_time > 5)
val |= EDP_PSR2_TP2_TIME_2500;
@@ -440,7 +430,7 @@ static void hsw_psr_activate(struct intel_dp *intel_dp)
*/
/* psr1 and psr2 are mutually exclusive.*/
- if (dev_priv->psr.psr2_support)
+ if (dev_priv->psr.psr2_enabled)
hsw_activate_psr2(intel_dp);
else
hsw_activate_psr1(intel_dp);
@@ -460,7 +450,7 @@ static bool intel_psr2_config_valid(struct intel_dp *intel_dp,
* dynamically during PSR enable, and extracted from sink
* caps during eDP detection.
*/
- if (!dev_priv->psr.psr2_support)
+ if (!dev_priv->psr.sink_psr2_support)
return false;
if (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv)) {
@@ -478,15 +468,6 @@ static bool intel_psr2_config_valid(struct intel_dp *intel_dp,
return false;
}
- /*
- * FIXME:enable psr2 only for y-cordinate psr2 panels
- * After gtc implementation , remove this restriction.
- */
- if (!dev_priv->psr.y_cord_support) {
- DRM_DEBUG_KMS("PSR2 not enabled, panel does not support Y coordinate\n");
- return false;
- }
-
return true;
}
@@ -568,7 +549,7 @@ static void intel_psr_activate(struct intel_dp *intel_dp)
struct drm_device *dev = intel_dig_port->base.base.dev;
struct drm_i915_private *dev_priv = to_i915(dev);
- if (dev_priv->psr.psr2_support)
+ if (dev_priv->psr.psr2_enabled)
WARN_ON(I915_READ(EDP_PSR2_CTL) & EDP_PSR2_ENABLE);
else
WARN_ON(I915_READ(EDP_PSR_CTL) & EDP_PSR_ENABLE);
@@ -586,14 +567,24 @@ static void hsw_psr_enable_source(struct intel_dp *intel_dp,
struct drm_device *dev = dig_port->base.base.dev;
struct drm_i915_private *dev_priv = to_i915(dev);
enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
- u32 chicken;
psr_aux_io_power_get(intel_dp);
- if (dev_priv->psr.psr2_support) {
- chicken = PSR2_VSC_ENABLE_PROG_HEADER;
- if (dev_priv->psr.y_cord_support)
- chicken |= PSR2_ADD_VERTICAL_LINE_COUNT;
+ /* Only HSW and BDW have PSR AUX registers that need to be setup. SKL+
+ * use hardcoded values PSR AUX transactions
+ */
+ if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv))
+ hsw_psr_setup_aux(intel_dp);
+
+ if (dev_priv->psr.psr2_enabled) {
+ u32 chicken = I915_READ(CHICKEN_TRANS(cpu_transcoder));
+
+ if (INTEL_GEN(dev_priv) == 9 && !IS_GEMINILAKE(dev_priv))
+ chicken |= (PSR2_VSC_ENABLE_PROG_HEADER
+ | PSR2_ADD_VERTICAL_LINE_COUNT);
+
+ else
+ chicken &= ~VSC_DATA_SEL_SOFTWARE_CONTROL;
I915_WRITE(CHICKEN_TRANS(cpu_transcoder), chicken);
I915_WRITE(EDP_PSR_DEBUG,
@@ -644,7 +635,7 @@ void intel_psr_enable(struct intel_dp *intel_dp,
goto unlock;
}
- dev_priv->psr.psr2_support = crtc_state->has_psr2;
+ dev_priv->psr.psr2_enabled = crtc_state->has_psr2;
dev_priv->psr.busy_frontbuffer_bits = 0;
dev_priv->psr.setup_vsc(intel_dp, crtc_state);
@@ -714,12 +705,7 @@ static void hsw_psr_disable(struct intel_dp *intel_dp,
i915_reg_t psr_status;
u32 psr_status_mask;
- if (dev_priv->psr.aux_frame_sync)
- drm_dp_dpcd_writeb(&intel_dp->aux,
- DP_SINK_DEVICE_AUX_FRAME_SYNC_CONF,
- 0);
-
- if (dev_priv->psr.psr2_support) {
+ if (dev_priv->psr.psr2_enabled) {
psr_status = EDP_PSR2_STATUS;
psr_status_mask = EDP_PSR2_STATUS_STATE_MASK;
@@ -743,7 +729,7 @@ static void hsw_psr_disable(struct intel_dp *intel_dp,
dev_priv->psr.active = false;
} else {
- if (dev_priv->psr.psr2_support)
+ if (dev_priv->psr.psr2_enabled)
WARN_ON(I915_READ(EDP_PSR2_CTL) & EDP_PSR2_ENABLE);
else
WARN_ON(I915_READ(EDP_PSR_CTL) & EDP_PSR_ENABLE);
@@ -789,53 +775,59 @@ void intel_psr_disable(struct intel_dp *intel_dp,
cancel_delayed_work_sync(&dev_priv->psr.work);
}
-static void intel_psr_work(struct work_struct *work)
+static bool psr_wait_for_idle(struct drm_i915_private *dev_priv)
{
- struct drm_i915_private *dev_priv =
- container_of(work, typeof(*dev_priv), psr.work.work);
- struct intel_dp *intel_dp = dev_priv->psr.enabled;
- struct drm_crtc *crtc = dp_to_dig_port(intel_dp)->base.base.crtc;
- enum pipe pipe = to_intel_crtc(crtc)->pipe;
+ struct intel_dp *intel_dp;
+ i915_reg_t reg;
+ u32 mask;
+ int err;
+
+ intel_dp = dev_priv->psr.enabled;
+ if (!intel_dp)
+ return false;
- /* We have to make sure PSR is ready for re-enable
- * otherwise it keeps disabled until next full enable/disable cycle.
- * PSR might take some time to get fully disabled
- * and be ready for re-enable.
- */
if (HAS_DDI(dev_priv)) {
- if (dev_priv->psr.psr2_support) {
- if (intel_wait_for_register(dev_priv,
- EDP_PSR2_STATUS,
- EDP_PSR2_STATUS_STATE_MASK,
- 0,
- 50)) {
- DRM_ERROR("Timed out waiting for PSR2 Idle for re-enable\n");
- return;
- }
+ if (dev_priv->psr.psr2_enabled) {
+ reg = EDP_PSR2_STATUS;
+ mask = EDP_PSR2_STATUS_STATE_MASK;
} else {
- if (intel_wait_for_register(dev_priv,
- EDP_PSR_STATUS,
- EDP_PSR_STATUS_STATE_MASK,
- 0,
- 50)) {
- DRM_ERROR("Timed out waiting for PSR Idle for re-enable\n");
- return;
- }
+ reg = EDP_PSR_STATUS;
+ mask = EDP_PSR_STATUS_STATE_MASK;
}
} else {
- if (intel_wait_for_register(dev_priv,
- VLV_PSRSTAT(pipe),
- VLV_EDP_PSR_IN_TRANS,
- 0,
- 1)) {
- DRM_ERROR("Timed out waiting for PSR Idle for re-enable\n");
- return;
- }
+ struct drm_crtc *crtc =
+ dp_to_dig_port(intel_dp)->base.base.crtc;
+ enum pipe pipe = to_intel_crtc(crtc)->pipe;
+
+ reg = VLV_PSRSTAT(pipe);
+ mask = VLV_EDP_PSR_IN_TRANS;
}
+
+ mutex_unlock(&dev_priv->psr.lock);
+
+ err = intel_wait_for_register(dev_priv, reg, mask, 0, 50);
+ if (err)
+ DRM_ERROR("Timed out waiting for PSR Idle for re-enable\n");
+
+ /* After the unlocked wait, verify that PSR is still wanted! */
mutex_lock(&dev_priv->psr.lock);
- intel_dp = dev_priv->psr.enabled;
+ return err == 0 && dev_priv->psr.enabled;
+}
- if (!intel_dp)
+static void intel_psr_work(struct work_struct *work)
+{
+ struct drm_i915_private *dev_priv =
+ container_of(work, typeof(*dev_priv), psr.work.work);
+
+ mutex_lock(&dev_priv->psr.lock);
+
+ /*
+ * We have to make sure PSR is ready for re-enable
+ * otherwise it keeps disabled until next full enable/disable cycle.
+ * PSR might take some time to get fully disabled
+ * and be ready for re-enable.
+ */
+ if (!psr_wait_for_idle(dev_priv))
goto unlock;
/*
@@ -846,7 +838,7 @@ static void intel_psr_work(struct work_struct *work)
if (dev_priv->psr.busy_frontbuffer_bits)
goto unlock;
- intel_psr_activate(intel_dp);
+ intel_psr_activate(dev_priv->psr.enabled);
unlock:
mutex_unlock(&dev_priv->psr.lock);
}
@@ -862,11 +854,7 @@ static void intel_psr_exit(struct drm_i915_private *dev_priv)
return;
if (HAS_DDI(dev_priv)) {
- if (dev_priv->psr.aux_frame_sync)
- drm_dp_dpcd_writeb(&intel_dp->aux,
- DP_SINK_DEVICE_AUX_FRAME_SYNC_CONF,
- 0);
- if (dev_priv->psr.psr2_support) {
+ if (dev_priv->psr.psr2_enabled) {
val = I915_READ(EDP_PSR2_CTL);
WARN_ON(!(val & EDP_PSR2_ENABLE));
I915_WRITE(EDP_PSR2_CTL, val & ~EDP_PSR2_ENABLE);
@@ -957,6 +945,7 @@ void intel_psr_single_frame_update(struct drm_i915_private *dev_priv,
* intel_psr_invalidate - Invalidade PSR
* @dev_priv: i915 device
* @frontbuffer_bits: frontbuffer plane tracking bits
+ * @origin: which operation caused the invalidate
*
* Since the hardware frontbuffer tracking has gaps we need to integrate
* with the software frontbuffer tracking. This function gets called every
@@ -966,7 +955,7 @@ void intel_psr_single_frame_update(struct drm_i915_private *dev_priv,
* Dirty frontbuffers relevant to PSR are tracked in busy_frontbuffer_bits."
*/
void intel_psr_invalidate(struct drm_i915_private *dev_priv,
- unsigned frontbuffer_bits)
+ unsigned frontbuffer_bits, enum fb_op_origin origin)
{
struct drm_crtc *crtc;
enum pipe pipe;
@@ -974,6 +963,9 @@ void intel_psr_invalidate(struct drm_i915_private *dev_priv,
if (!CAN_PSR(dev_priv))
return;
+ if (dev_priv->psr.has_hw_tracking && origin == ORIGIN_FLIP)
+ return;
+
mutex_lock(&dev_priv->psr.lock);
if (!dev_priv->psr.enabled) {
mutex_unlock(&dev_priv->psr.lock);
@@ -1014,6 +1006,9 @@ void intel_psr_flush(struct drm_i915_private *dev_priv,
if (!CAN_PSR(dev_priv))
return;
+ if (dev_priv->psr.has_hw_tracking && origin == ORIGIN_FLIP)
+ return;
+
mutex_lock(&dev_priv->psr.lock);
if (!dev_priv->psr.enabled) {
mutex_unlock(&dev_priv->psr.lock);
@@ -1027,8 +1022,23 @@ void intel_psr_flush(struct drm_i915_private *dev_priv,
dev_priv->psr.busy_frontbuffer_bits &= ~frontbuffer_bits;
/* By definition flush = invalidate + flush */
- if (frontbuffer_bits)
- intel_psr_exit(dev_priv);
+ if (frontbuffer_bits) {
+ if (dev_priv->psr.psr2_enabled ||
+ IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
+ intel_psr_exit(dev_priv);
+ } else {
+ /*
+ * Display WA #0884: all
+ * This documented WA for bxt can be safely applied
+ * broadly so we can force HW tracking to exit PSR
+ * instead of disabling and re-enabling.
+ * Workaround tells us to write 0 to CUR_SURFLIVE_A,
+ * but it makes more sense write to the current active
+ * pipe.
+ */
+ I915_WRITE(CURSURFLIVE(pipe), 0);
+ }
+ }
if (!dev_priv->psr.active && !dev_priv->psr.busy_frontbuffer_bits)
if (!work_busy(&dev_priv->psr.work.work))
@@ -1090,6 +1100,7 @@ void intel_psr_init(struct drm_i915_private *dev_priv)
dev_priv->psr.activate = vlv_psr_activate;
dev_priv->psr.setup_vsc = vlv_psr_setup_vsc;
} else {
+ dev_priv->psr.has_hw_tracking = true;
dev_priv->psr.enable_source = hsw_psr_enable_source;
dev_priv->psr.disable_source = hsw_psr_disable;
dev_priv->psr.enable_sink = hsw_psr_enable_sink;
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c
index 1d599524a759..757bb0990c07 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.c
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.c
@@ -36,6 +36,7 @@
#include "i915_gem_render_state.h"
#include "i915_trace.h"
#include "intel_drv.h"
+#include "intel_workarounds.h"
/* Rough estimate of the typical request size, performing a flush,
* set-context and then emitting the batch.
@@ -599,7 +600,7 @@ static int intel_rcs_ctx_init(struct i915_request *rq)
{
int ret;
- ret = intel_ring_workarounds_emit(rq);
+ ret = intel_ctx_workarounds_emit(rq);
if (ret != 0)
return ret;
@@ -617,6 +618,10 @@ static int init_render_ring(struct intel_engine_cs *engine)
if (ret)
return ret;
+ ret = intel_whitelist_workarounds_apply(engine);
+ if (ret)
+ return ret;
+
/* WaTimedSingleVertexDispatch:cl,bw,ctg,elk,ilk,snb */
if (IS_GEN(dev_priv, 4, 6))
I915_WRITE(MI_MODE, _MASKED_BIT_ENABLE(VS_TIMER_DISPATCH));
@@ -658,7 +663,7 @@ static int init_render_ring(struct intel_engine_cs *engine)
if (INTEL_GEN(dev_priv) >= 6)
I915_WRITE_IMR(engine, ~engine->irq_keep_mask);
- return init_workarounds_ring(engine);
+ return 0;
}
static u32 *gen6_signal(struct i915_request *rq, u32 *cs)
@@ -1593,6 +1598,7 @@ static noinline int wait_for_space(struct intel_ring *ring, unsigned int bytes)
if (intel_ring_update_space(ring) >= bytes)
return 0;
+ GEM_BUG_ON(list_empty(&ring->request_list));
list_for_each_entry(target, &ring->request_list, ring_link) {
/* Would completion of this request free enough space? */
if (bytes <= __intel_ring_space(target->postfix,
@@ -1692,17 +1698,18 @@ u32 *intel_ring_begin(struct i915_request *rq, unsigned int num_dwords)
need_wrap &= ~1;
GEM_BUG_ON(need_wrap > ring->space);
GEM_BUG_ON(ring->emit + need_wrap > ring->size);
+ GEM_BUG_ON(!IS_ALIGNED(need_wrap, sizeof(u64)));
/* Fill the tail with MI_NOOP */
- memset(ring->vaddr + ring->emit, 0, need_wrap);
- ring->emit = 0;
+ memset64(ring->vaddr + ring->emit, 0, need_wrap / sizeof(u64));
ring->space -= need_wrap;
+ ring->emit = 0;
}
GEM_BUG_ON(ring->emit > ring->size - bytes);
GEM_BUG_ON(ring->space < bytes);
cs = ring->vaddr + ring->emit;
- GEM_DEBUG_EXEC(memset(cs, POISON_INUSE, bytes));
+ GEM_DEBUG_EXEC(memset32(cs, POISON_INUSE, bytes / sizeof(*cs)));
ring->emit += bytes;
ring->space -= bytes;
@@ -1943,8 +1950,6 @@ static void intel_ring_init_semaphores(struct drm_i915_private *dev_priv,
static void intel_ring_init_irq(struct drm_i915_private *dev_priv,
struct intel_engine_cs *engine)
{
- engine->irq_enable_mask = GT_RENDER_USER_INTERRUPT << engine->irq_shift;
-
if (INTEL_GEN(dev_priv) >= 6) {
engine->irq_enable = gen6_irq_enable;
engine->irq_disable = gen6_irq_disable;
@@ -2029,6 +2034,8 @@ int intel_init_render_ring_buffer(struct intel_engine_cs *engine)
if (HAS_L3_DPF(dev_priv))
engine->irq_keep_mask = GT_RENDER_L3_PARITY_ERROR_INTERRUPT;
+ engine->irq_enable_mask = GT_RENDER_USER_INTERRUPT;
+
if (INTEL_GEN(dev_priv) >= 6) {
engine->init_context = intel_rcs_ctx_init;
engine->emit_flush = gen7_render_ring_flush;
@@ -2079,7 +2086,6 @@ int intel_init_bsd_ring_buffer(struct intel_engine_cs *engine)
engine->emit_flush = gen6_bsd_ring_flush;
engine->irq_enable_mask = GT_BSD_USER_INTERRUPT;
} else {
- engine->mmio_base = BSD_RING_BASE;
engine->emit_flush = bsd_ring_flush;
if (IS_GEN5(dev_priv))
engine->irq_enable_mask = ILK_BSD_USER_INTERRUPT;
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h
index 0320c2c4cfba..717041640135 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.h
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.h
@@ -7,9 +7,11 @@
#include "i915_gem_batch_pool.h"
#include "i915_gem_timeline.h"
+#include "i915_reg.h"
#include "i915_pmu.h"
#include "i915_request.h"
#include "i915_selftest.h"
+#include "intel_gpu_commands.h"
struct drm_printer;
@@ -84,7 +86,7 @@ hangcheck_action_to_str(const enum intel_engine_hangcheck_action a)
}
#define I915_MAX_SLICES 3
-#define I915_MAX_SUBSLICES 3
+#define I915_MAX_SUBSLICES 8
#define instdone_slice_mask(dev_priv__) \
(INTEL_GEN(dev_priv__) == 7 ? \
@@ -330,7 +332,6 @@ struct intel_engine_cs {
u8 instance;
u32 context_size;
u32 mmio_base;
- unsigned int irq_shift;
struct intel_ring *buffer;
struct intel_timeline *timeline;
@@ -561,6 +562,7 @@ struct intel_engine_cs {
#define I915_ENGINE_NEEDS_CMD_PARSER BIT(0)
#define I915_ENGINE_SUPPORTS_STATS BIT(1)
+#define I915_ENGINE_HAS_PREEMPTION BIT(2)
unsigned int flags;
/*
@@ -620,16 +622,29 @@ struct intel_engine_cs {
} stats;
};
-static inline bool intel_engine_needs_cmd_parser(struct intel_engine_cs *engine)
+static inline bool
+intel_engine_needs_cmd_parser(const struct intel_engine_cs *engine)
{
return engine->flags & I915_ENGINE_NEEDS_CMD_PARSER;
}
-static inline bool intel_engine_supports_stats(struct intel_engine_cs *engine)
+static inline bool
+intel_engine_supports_stats(const struct intel_engine_cs *engine)
{
return engine->flags & I915_ENGINE_SUPPORTS_STATS;
}
+static inline bool
+intel_engine_has_preemption(const struct intel_engine_cs *engine)
+{
+ return engine->flags & I915_ENGINE_HAS_PREEMPTION;
+}
+
+static inline bool __execlists_need_preempt(int prio, int last)
+{
+ return prio > max(0, last);
+}
+
static inline void
execlists_set_active(struct intel_engine_execlists *execlists,
unsigned int bit)
@@ -637,6 +652,13 @@ execlists_set_active(struct intel_engine_execlists *execlists,
__set_bit(bit, (unsigned long *)&execlists->active);
}
+static inline bool
+execlists_set_active_once(struct intel_engine_execlists *execlists,
+ unsigned int bit)
+{
+ return !__test_and_set_bit(bit, (unsigned long *)&execlists->active);
+}
+
static inline void
execlists_clear_active(struct intel_engine_execlists *execlists,
unsigned int bit)
@@ -651,6 +673,10 @@ execlists_is_active(const struct intel_engine_execlists *execlists,
return test_bit(bit, (unsigned long *)&execlists->active);
}
+void execlists_user_begin(struct intel_engine_execlists *execlists,
+ const struct execlist_port *port);
+void execlists_user_end(struct intel_engine_execlists *execlists);
+
void
execlists_cancel_port_requests(struct intel_engine_execlists * const execlists);
@@ -663,7 +689,7 @@ execlists_num_ports(const struct intel_engine_execlists * const execlists)
return execlists->port_mask + 1;
}
-static inline void
+static inline struct execlist_port *
execlists_port_complete(struct intel_engine_execlists * const execlists,
struct execlist_port * const port)
{
@@ -674,6 +700,8 @@ execlists_port_complete(struct intel_engine_execlists * const execlists,
memmove(port, port + 1, m * sizeof(struct execlist_port));
memset(port + m, 0, sizeof(struct execlist_port));
+
+ return port;
}
static inline unsigned int
@@ -857,9 +885,6 @@ static inline u32 intel_engine_last_submit(struct intel_engine_cs *engine)
return READ_ONCE(engine->timeline->seqno);
}
-int init_workarounds_ring(struct intel_engine_cs *engine);
-int intel_ring_workarounds_emit(struct i915_request *rq);
-
void intel_engine_get_instdone(struct intel_engine_cs *engine,
struct intel_instdone *instdone);
@@ -939,7 +964,7 @@ bool intel_engine_add_wait(struct intel_engine_cs *engine,
struct intel_wait *wait);
void intel_engine_remove_wait(struct intel_engine_cs *engine,
struct intel_wait *wait);
-void intel_engine_enable_signaling(struct i915_request *request, bool wakeup);
+bool intel_engine_enable_signaling(struct i915_request *request, bool wakeup);
void intel_engine_cancel_signaling(struct i915_request *request);
static inline bool intel_engine_has_waiter(const struct intel_engine_cs *engine)
diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c
index 53ea564f971e..66de4b2dc8b7 100644
--- a/drivers/gpu/drm/i915/intel_runtime_pm.c
+++ b/drivers/gpu/drm/i915/intel_runtime_pm.c
@@ -641,19 +641,18 @@ void skl_enable_dc6(struct drm_i915_private *dev_priv)
DRM_DEBUG_KMS("Enabling DC6\n");
- gen9_set_dc_state(dev_priv, DC_STATE_EN_UPTO_DC6);
+ /* Wa Display #1183: skl,kbl,cfl */
+ if (IS_GEN9_BC(dev_priv))
+ I915_WRITE(GEN8_CHICKEN_DCPR_1, I915_READ(GEN8_CHICKEN_DCPR_1) |
+ SKL_SELECT_ALTERNATE_DC_EXIT);
+ gen9_set_dc_state(dev_priv, DC_STATE_EN_UPTO_DC6);
}
void skl_disable_dc6(struct drm_i915_private *dev_priv)
{
DRM_DEBUG_KMS("Disabling DC6\n");
- /* Wa Display #1183: skl,kbl,cfl */
- if (IS_GEN9_BC(dev_priv))
- I915_WRITE(GEN8_CHICKEN_DCPR_1, I915_READ(GEN8_CHICKEN_DCPR_1) |
- SKL_SELECT_ALTERNATE_DC_EXIT);
-
gen9_set_dc_state(dev_priv, DC_STATE_DISABLE);
}
diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
index 344228b640b9..970015dcc6f1 100644
--- a/drivers/gpu/drm/i915/intel_sprite.c
+++ b/drivers/gpu/drm/i915/intel_sprite.c
@@ -48,6 +48,7 @@ bool intel_format_is_yuv(u32 format)
case DRM_FORMAT_UYVY:
case DRM_FORMAT_VYUY:
case DRM_FORMAT_YVYU:
+ case DRM_FORMAT_NV12:
return true;
default:
return false;
@@ -939,6 +940,7 @@ intel_check_sprite_plane(struct intel_plane *plane,
int max_scale, min_scale;
bool can_scale;
int ret;
+ uint32_t pixel_format = 0;
if (!fb) {
state->base.visible = false;
@@ -959,11 +961,14 @@ intel_check_sprite_plane(struct intel_plane *plane,
/* setup can_scale, min_scale, max_scale */
if (INTEL_GEN(dev_priv) >= 9) {
+ if (state->base.fb)
+ pixel_format = state->base.fb->format->format;
/* use scaler when colorkey is not required */
if (!state->ckey.flags) {
can_scale = 1;
min_scale = 1;
- max_scale = skl_max_scale(crtc, crtc_state);
+ max_scale =
+ skl_max_scale(crtc, crtc_state, pixel_format);
} else {
can_scale = 0;
min_scale = DRM_PLANE_HELPER_NO_SCALING;
diff --git a/drivers/gpu/drm/i915/intel_uc.c b/drivers/gpu/drm/i915/intel_uc.c
index e5bf0d37bf43..1cffaf7b5dbe 100644
--- a/drivers/gpu/drm/i915/intel_uc.c
+++ b/drivers/gpu/drm/i915/intel_uc.c
@@ -69,13 +69,15 @@ static int __get_platform_enable_guc(struct drm_i915_private *dev_priv)
static int __get_default_guc_log_level(struct drm_i915_private *dev_priv)
{
- int guc_log_level = 0; /* disabled */
+ int guc_log_level;
- /* Enable if we're running on platform with GuC and debug config */
- if (HAS_GUC(dev_priv) && intel_uc_is_using_guc() &&
- (IS_ENABLED(CONFIG_DRM_I915_DEBUG) ||
- IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)))
- guc_log_level = 1 + GUC_LOG_VERBOSITY_MAX;
+ if (!HAS_GUC(dev_priv) || !intel_uc_is_using_guc())
+ guc_log_level = GUC_LOG_LEVEL_DISABLED;
+ else if (IS_ENABLED(CONFIG_DRM_I915_DEBUG) ||
+ IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM))
+ guc_log_level = GUC_LOG_LEVEL_MAX;
+ else
+ guc_log_level = GUC_LOG_LEVEL_NON_VERBOSE;
/* Any platform specific fine-tuning can be done here */
@@ -83,7 +85,7 @@ static int __get_default_guc_log_level(struct drm_i915_private *dev_priv)
}
/**
- * intel_uc_sanitize_options - sanitize uC related modparam options
+ * sanitize_options_early - sanitize uC related modparam options
* @dev_priv: device private
*
* In case of "enable_guc" option this function will attempt to modify
@@ -99,7 +101,7 @@ static int __get_default_guc_log_level(struct drm_i915_private *dev_priv)
* unless GuC is enabled on given platform and the driver is compiled with
* debug config when this modparam will default to "enable(1..4)".
*/
-void intel_uc_sanitize_options(struct drm_i915_private *dev_priv)
+static void sanitize_options_early(struct drm_i915_private *dev_priv)
{
struct intel_uc_fw *guc_fw = &dev_priv->guc.fw;
struct intel_uc_fw *huc_fw = &dev_priv->huc.fw;
@@ -142,51 +144,53 @@ void intel_uc_sanitize_options(struct drm_i915_private *dev_priv)
i915_modparams.guc_log_level = 0;
}
- if (i915_modparams.guc_log_level > 1 + GUC_LOG_VERBOSITY_MAX) {
+ if (i915_modparams.guc_log_level > GUC_LOG_LEVEL_MAX) {
DRM_WARN("Incompatible option detected: %s=%d, %s!\n",
"guc_log_level", i915_modparams.guc_log_level,
"verbosity too high");
- i915_modparams.guc_log_level = 1 + GUC_LOG_VERBOSITY_MAX;
+ i915_modparams.guc_log_level = GUC_LOG_LEVEL_MAX;
}
- DRM_DEBUG_DRIVER("guc_log_level=%d (enabled:%s verbosity:%d)\n",
+ DRM_DEBUG_DRIVER("guc_log_level=%d (enabled:%s, verbose:%s, verbosity:%d)\n",
i915_modparams.guc_log_level,
yesno(i915_modparams.guc_log_level),
- i915_modparams.guc_log_level - 1);
+ yesno(GUC_LOG_LEVEL_IS_VERBOSE(i915_modparams.guc_log_level)),
+ GUC_LOG_LEVEL_TO_VERBOSITY(i915_modparams.guc_log_level));
/* Make sure that sanitization was done */
GEM_BUG_ON(i915_modparams.enable_guc < 0);
GEM_BUG_ON(i915_modparams.guc_log_level < 0);
}
-void intel_uc_init_early(struct drm_i915_private *dev_priv)
+void intel_uc_init_early(struct drm_i915_private *i915)
{
- intel_guc_init_early(&dev_priv->guc);
- intel_huc_init_early(&dev_priv->huc);
-}
+ struct intel_guc *guc = &i915->guc;
+ struct intel_huc *huc = &i915->huc;
-void intel_uc_init_fw(struct drm_i915_private *dev_priv)
-{
- if (!USES_GUC(dev_priv))
- return;
+ intel_guc_init_early(guc);
+ intel_huc_init_early(huc);
- if (USES_HUC(dev_priv))
- intel_uc_fw_fetch(dev_priv, &dev_priv->huc.fw);
+ sanitize_options_early(i915);
- intel_uc_fw_fetch(dev_priv, &dev_priv->guc.fw);
+ if (USES_GUC(i915))
+ intel_uc_fw_fetch(i915, &guc->fw);
+
+ if (USES_HUC(i915))
+ intel_uc_fw_fetch(i915, &huc->fw);
}
-void intel_uc_fini_fw(struct drm_i915_private *dev_priv)
+void intel_uc_cleanup_early(struct drm_i915_private *i915)
{
- if (!USES_GUC(dev_priv))
- return;
+ struct intel_guc *guc = &i915->guc;
+ struct intel_huc *huc = &i915->huc;
- intel_uc_fw_fini(&dev_priv->guc.fw);
+ if (USES_HUC(i915))
+ intel_uc_fw_fini(&huc->fw);
- if (USES_HUC(dev_priv))
- intel_uc_fw_fini(&dev_priv->huc.fw);
+ if (USES_GUC(i915))
+ intel_uc_fw_fini(&guc->fw);
- guc_free_load_err_log(&dev_priv->guc);
+ guc_free_load_err_log(guc);
}
/**
@@ -223,10 +227,13 @@ static int guc_enable_communication(struct intel_guc *guc)
{
struct drm_i915_private *dev_priv = guc_to_i915(guc);
+ gen9_enable_guc_interrupts(dev_priv);
+
if (HAS_GUC_CT(dev_priv))
- return intel_guc_enable_ct(guc);
+ return intel_guc_ct_enable(&guc->ct);
guc->send = intel_guc_send_mmio;
+ guc->handler = intel_guc_to_host_event_handler_mmio;
return 0;
}
@@ -235,9 +242,12 @@ static void guc_disable_communication(struct intel_guc *guc)
struct drm_i915_private *dev_priv = guc_to_i915(guc);
if (HAS_GUC_CT(dev_priv))
- intel_guc_disable_ct(guc);
+ intel_guc_ct_disable(&guc->ct);
+
+ gen9_disable_guc_interrupts(dev_priv);
guc->send = intel_guc_send_nop;
+ guc->handler = intel_guc_to_host_event_handler_nop;
}
int intel_uc_init_misc(struct drm_i915_private *dev_priv)
@@ -248,24 +258,13 @@ int intel_uc_init_misc(struct drm_i915_private *dev_priv)
if (!USES_GUC(dev_priv))
return 0;
- ret = intel_guc_init_wq(guc);
- if (ret) {
- DRM_ERROR("Couldn't allocate workqueues for GuC\n");
- goto err;
- }
+ intel_guc_init_ggtt_pin_bias(guc);
- ret = intel_guc_log_relay_create(guc);
- if (ret) {
- DRM_ERROR("Couldn't allocate relay for GuC log\n");
- goto err_relay;
- }
+ ret = intel_guc_init_wq(guc);
+ if (ret)
+ return ret;
return 0;
-
-err_relay:
- intel_guc_fini_wq(guc);
-err:
- return ret;
}
void intel_uc_fini_misc(struct drm_i915_private *dev_priv)
@@ -276,8 +275,6 @@ void intel_uc_fini_misc(struct drm_i915_private *dev_priv)
return;
intel_guc_fini_wq(guc);
-
- intel_guc_log_relay_destroy(guc);
}
int intel_uc_init(struct drm_i915_private *dev_priv)
@@ -325,6 +322,24 @@ void intel_uc_fini(struct drm_i915_private *dev_priv)
intel_guc_fini(guc);
}
+void intel_uc_sanitize(struct drm_i915_private *i915)
+{
+ struct intel_guc *guc = &i915->guc;
+ struct intel_huc *huc = &i915->huc;
+
+ if (!USES_GUC(i915))
+ return;
+
+ GEM_BUG_ON(!HAS_GUC(i915));
+
+ guc_disable_communication(guc);
+
+ intel_huc_sanitize(huc);
+ intel_guc_sanitize(guc);
+
+ __intel_uc_reset_hw(i915);
+}
+
int intel_uc_init_hw(struct drm_i915_private *dev_priv)
{
struct intel_guc *guc = &dev_priv->guc;
@@ -336,14 +351,8 @@ int intel_uc_init_hw(struct drm_i915_private *dev_priv)
GEM_BUG_ON(!HAS_GUC(dev_priv));
- guc_disable_communication(guc);
gen9_reset_guc_interrupts(dev_priv);
- /* init WOPCM */
- I915_WRITE(GUC_WOPCM_SIZE, intel_guc_wopcm_size(dev_priv));
- I915_WRITE(DMA_GUC_WOPCM_OFFSET,
- GUC_WOPCM_OFFSET_VALUE | HUC_LOADING_AGENT_GUC);
-
/* WaEnableuKernelHeaderValidFix:skl */
/* WaEnableGuCBootHashCheckNotSet:skl,bxt,kbl */
if (IS_GEN9(dev_priv))
@@ -390,12 +399,9 @@ int intel_uc_init_hw(struct drm_i915_private *dev_priv)
}
if (USES_GUC_SUBMISSION(dev_priv)) {
- if (i915_modparams.guc_log_level)
- gen9_enable_guc_interrupts(dev_priv);
-
ret = intel_guc_submission_enable(guc);
if (ret)
- goto err_interrupts;
+ goto err_communication;
}
dev_info(dev_priv->drm.dev, "GuC firmware version %u.%u\n",
@@ -410,8 +416,6 @@ int intel_uc_init_hw(struct drm_i915_private *dev_priv)
/*
* We've failed to load the firmware :(
*/
-err_interrupts:
- gen9_disable_guc_interrupts(dev_priv);
err_communication:
guc_disable_communication(guc);
err_log_capture:
@@ -441,9 +445,6 @@ void intel_uc_fini_hw(struct drm_i915_private *dev_priv)
intel_guc_submission_disable(guc);
guc_disable_communication(guc);
-
- if (USES_GUC_SUBMISSION(dev_priv))
- gen9_disable_guc_interrupts(dev_priv);
}
int intel_uc_suspend(struct drm_i915_private *i915)
@@ -479,8 +480,7 @@ int intel_uc_resume(struct drm_i915_private *i915)
if (guc->fw.load_status != INTEL_UC_FIRMWARE_SUCCESS)
return 0;
- if (i915_modparams.guc_log_level)
- gen9_enable_guc_interrupts(i915);
+ gen9_enable_guc_interrupts(i915);
err = intel_guc_resume(guc);
if (err) {
diff --git a/drivers/gpu/drm/i915/intel_uc.h b/drivers/gpu/drm/i915/intel_uc.h
index f76d51d1ce70..25d73ada74ae 100644
--- a/drivers/gpu/drm/i915/intel_uc.h
+++ b/drivers/gpu/drm/i915/intel_uc.h
@@ -28,13 +28,12 @@
#include "intel_huc.h"
#include "i915_params.h"
-void intel_uc_sanitize_options(struct drm_i915_private *dev_priv);
void intel_uc_init_early(struct drm_i915_private *dev_priv);
+void intel_uc_cleanup_early(struct drm_i915_private *dev_priv);
void intel_uc_init_mmio(struct drm_i915_private *dev_priv);
-void intel_uc_init_fw(struct drm_i915_private *dev_priv);
-void intel_uc_fini_fw(struct drm_i915_private *dev_priv);
int intel_uc_init_misc(struct drm_i915_private *dev_priv);
void intel_uc_fini_misc(struct drm_i915_private *dev_priv);
+void intel_uc_sanitize(struct drm_i915_private *dev_priv);
int intel_uc_init_hw(struct drm_i915_private *dev_priv);
void intel_uc_fini_hw(struct drm_i915_private *dev_priv);
int intel_uc_init(struct drm_i915_private *dev_priv);
diff --git a/drivers/gpu/drm/i915/intel_uc_fw.c b/drivers/gpu/drm/i915/intel_uc_fw.c
index 3ec0ce505b76..6e8e0b546743 100644
--- a/drivers/gpu/drm/i915/intel_uc_fw.c
+++ b/drivers/gpu/drm/i915/intel_uc_fw.c
@@ -95,15 +95,6 @@ void intel_uc_fw_fetch(struct drm_i915_private *dev_priv,
uc_fw->ucode_offset = uc_fw->header_offset + uc_fw->header_size;
uc_fw->ucode_size = (css->size_dw - css->header_size_dw) * sizeof(u32);
- /* Header and uCode will be loaded to WOPCM */
- size = uc_fw->header_size + uc_fw->ucode_size;
- if (size > intel_guc_wopcm_size(dev_priv)) {
- DRM_WARN("%s: Firmware is too large to fit in WOPCM\n",
- intel_uc_fw_type_repr(uc_fw->type));
- err = -E2BIG;
- goto fail;
- }
-
/* now RSA */
if (css->key_size_dw != UOS_RSA_SCRATCH_COUNT) {
DRM_WARN("%s: Mismatched firmware RSA key size (%u)\n",
@@ -209,6 +200,7 @@ int intel_uc_fw_upload(struct intel_uc_fw *uc_fw,
struct i915_vma *vma))
{
struct i915_vma *vma;
+ u32 ggtt_pin_bias;
int err;
DRM_DEBUG_DRIVER("%s fw load %s\n",
@@ -230,8 +222,9 @@ int intel_uc_fw_upload(struct intel_uc_fw *uc_fw,
goto fail;
}
+ ggtt_pin_bias = to_i915(uc_fw->obj->base.dev)->guc.ggtt_pin_bias;
vma = i915_gem_object_ggtt_pin(uc_fw->obj, NULL, 0, 0,
- PIN_OFFSET_BIAS | GUC_WOPCM_TOP);
+ PIN_OFFSET_BIAS | ggtt_pin_bias);
if (IS_ERR(vma)) {
err = PTR_ERR(vma);
DRM_DEBUG_DRIVER("%s fw ggtt-pin err=%d\n",
diff --git a/drivers/gpu/drm/i915/intel_uc_fw.h b/drivers/gpu/drm/i915/intel_uc_fw.h
index d5fd4609c785..dc33b12394de 100644
--- a/drivers/gpu/drm/i915/intel_uc_fw.h
+++ b/drivers/gpu/drm/i915/intel_uc_fw.h
@@ -115,6 +115,28 @@ static inline bool intel_uc_fw_is_selected(struct intel_uc_fw *uc_fw)
return uc_fw->path != NULL;
}
+static inline void intel_uc_fw_sanitize(struct intel_uc_fw *uc_fw)
+{
+ if (uc_fw->load_status == INTEL_UC_FIRMWARE_SUCCESS)
+ uc_fw->load_status = INTEL_UC_FIRMWARE_PENDING;
+}
+
+/**
+ * intel_uc_fw_get_upload_size() - Get size of firmware needed to be uploaded.
+ * @uc_fw: uC firmware.
+ *
+ * Get the size of the firmware and header that will be uploaded to WOPCM.
+ *
+ * Return: Upload firmware size, or zero on firmware fetch failure.
+ */
+static inline u32 intel_uc_fw_get_upload_size(struct intel_uc_fw *uc_fw)
+{
+ if (uc_fw->fetch_status != INTEL_UC_FIRMWARE_SUCCESS)
+ return 0;
+
+ return uc_fw->header_size + uc_fw->ucode_size;
+}
+
void intel_uc_fw_fetch(struct drm_i915_private *dev_priv,
struct intel_uc_fw *uc_fw);
int intel_uc_fw_upload(struct intel_uc_fw *uc_fw,
diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c
index 4df7c2ef8576..d6e20f0f4c28 100644
--- a/drivers/gpu/drm/i915/intel_uncore.c
+++ b/drivers/gpu/drm/i915/intel_uncore.c
@@ -62,6 +62,11 @@ static inline void
fw_domain_reset(struct drm_i915_private *i915,
const struct intel_uncore_forcewake_domain *d)
{
+ /*
+ * We don't really know if the powerwell for the forcewake domain we are
+ * trying to reset here does exist at this point (engines could be fused
+ * off in ICL+), so no waiting for acks
+ */
__raw_i915_write32(i915, d->reg_set, i915->uncore.fw_reset);
}
@@ -1353,6 +1358,23 @@ static void fw_domain_init(struct drm_i915_private *dev_priv,
fw_domain_reset(dev_priv, d);
}
+static void fw_domain_fini(struct drm_i915_private *dev_priv,
+ enum forcewake_domain_id domain_id)
+{
+ struct intel_uncore_forcewake_domain *d;
+
+ if (WARN_ON(domain_id >= FW_DOMAIN_ID_COUNT))
+ return;
+
+ d = &dev_priv->uncore.fw_domain[domain_id];
+
+ WARN_ON(d->wake_count);
+ WARN_ON(hrtimer_cancel(&d->timer));
+ memset(d, 0, sizeof(*d));
+
+ dev_priv->uncore.fw_domains &= ~BIT(domain_id);
+}
+
static void intel_uncore_fw_domains_init(struct drm_i915_private *dev_priv)
{
if (INTEL_GEN(dev_priv) <= 5 || intel_vgpu_active(dev_priv))
@@ -1565,6 +1587,40 @@ void intel_uncore_init(struct drm_i915_private *dev_priv)
&dev_priv->uncore.pmic_bus_access_nb);
}
+/*
+ * We might have detected that some engines are fused off after we initialized
+ * the forcewake domains. Prune them, to make sure they only reference existing
+ * engines.
+ */
+void intel_uncore_prune(struct drm_i915_private *dev_priv)
+{
+ if (INTEL_GEN(dev_priv) >= 11) {
+ enum forcewake_domains fw_domains = dev_priv->uncore.fw_domains;
+ enum forcewake_domain_id domain_id;
+ int i;
+
+ for (i = 0; i < I915_MAX_VCS; i++) {
+ domain_id = FW_DOMAIN_ID_MEDIA_VDBOX0 + i;
+
+ if (HAS_ENGINE(dev_priv, _VCS(i)))
+ continue;
+
+ if (fw_domains & BIT(domain_id))
+ fw_domain_fini(dev_priv, domain_id);
+ }
+
+ for (i = 0; i < I915_MAX_VECS; i++) {
+ domain_id = FW_DOMAIN_ID_MEDIA_VEBOX0 + i;
+
+ if (HAS_ENGINE(dev_priv, _VECS(i)))
+ continue;
+
+ if (fw_domains & BIT(domain_id))
+ fw_domain_fini(dev_priv, domain_id);
+ }
+ }
+}
+
void intel_uncore_fini(struct drm_i915_private *dev_priv)
{
/* Paranoia: make sure we have disabled everything before we exit. */
@@ -1646,11 +1702,10 @@ static void gen3_stop_engine(struct intel_engine_cs *engine)
const i915_reg_t mode = RING_MI_MODE(base);
I915_WRITE_FW(mode, _MASKED_BIT_ENABLE(STOP_RING));
- if (intel_wait_for_register_fw(dev_priv,
- mode,
- MODE_IDLE,
- MODE_IDLE,
- 500))
+ if (__intel_wait_for_register_fw(dev_priv,
+ mode, MODE_IDLE, MODE_IDLE,
+ 500, 0,
+ NULL))
DRM_DEBUG_DRIVER("%s: timed out on STOP_RING\n",
engine->name);
@@ -1804,9 +1859,10 @@ static int gen6_hw_domain_reset(struct drm_i915_private *dev_priv,
__raw_i915_write32(dev_priv, GEN6_GDRST, hw_domain_mask);
/* Wait for the device to ack the reset requests */
- err = intel_wait_for_register_fw(dev_priv,
- GEN6_GDRST, hw_domain_mask, 0,
- 500);
+ err = __intel_wait_for_register_fw(dev_priv,
+ GEN6_GDRST, hw_domain_mask, 0,
+ 500, 0,
+ NULL);
if (err)
DRM_DEBUG_DRIVER("Wait for 0x%08x engines reset failed\n",
hw_domain_mask);
@@ -1854,6 +1910,50 @@ static int gen6_reset_engines(struct drm_i915_private *dev_priv,
}
/**
+ * gen11_reset_engines - reset individual engines
+ * @dev_priv: i915 device
+ * @engine_mask: mask of intel_ring_flag() engines or ALL_ENGINES for full reset
+ *
+ * This function will reset the individual engines that are set in engine_mask.
+ * If you provide ALL_ENGINES as mask, full global domain reset will be issued.
+ *
+ * Note: It is responsibility of the caller to handle the difference between
+ * asking full domain reset versus reset for all available individual engines.
+ *
+ * Returns 0 on success, nonzero on error.
+ */
+static int gen11_reset_engines(struct drm_i915_private *dev_priv,
+ unsigned engine_mask)
+{
+ struct intel_engine_cs *engine;
+ const u32 hw_engine_mask[I915_NUM_ENGINES] = {
+ [RCS] = GEN11_GRDOM_RENDER,
+ [BCS] = GEN11_GRDOM_BLT,
+ [VCS] = GEN11_GRDOM_MEDIA,
+ [VCS2] = GEN11_GRDOM_MEDIA2,
+ [VCS3] = GEN11_GRDOM_MEDIA3,
+ [VCS4] = GEN11_GRDOM_MEDIA4,
+ [VECS] = GEN11_GRDOM_VECS,
+ [VECS2] = GEN11_GRDOM_VECS2,
+ };
+ u32 hw_mask;
+
+ BUILD_BUG_ON(VECS2 + 1 != I915_NUM_ENGINES);
+
+ if (engine_mask == ALL_ENGINES) {
+ hw_mask = GEN11_GRDOM_FULL;
+ } else {
+ unsigned int tmp;
+
+ hw_mask = 0;
+ for_each_engine_masked(engine, dev_priv, engine_mask, tmp)
+ hw_mask |= hw_engine_mask[engine->id];
+ }
+
+ return gen6_hw_domain_reset(dev_priv, hw_mask);
+}
+
+/**
* __intel_wait_for_register_fw - wait until register matches expected state
* @dev_priv: the i915 device
* @reg: the register to read
@@ -1940,7 +2040,7 @@ int __intel_wait_for_register(struct drm_i915_private *dev_priv,
u32 reg_value;
int ret;
- might_sleep();
+ might_sleep_if(slow_timeout_ms);
spin_lock_irq(&dev_priv->uncore.lock);
intel_uncore_forcewake_get__locked(dev_priv, fw);
@@ -1952,7 +2052,7 @@ int __intel_wait_for_register(struct drm_i915_private *dev_priv,
intel_uncore_forcewake_put__locked(dev_priv, fw);
spin_unlock_irq(&dev_priv->uncore.lock);
- if (ret)
+ if (ret && slow_timeout_ms)
ret = __wait_for(reg_value = I915_READ_NOTRACE(reg),
(reg_value & mask) == value,
slow_timeout_ms * 1000, 10, 1000);
@@ -1971,11 +2071,12 @@ static int gen8_reset_engine_start(struct intel_engine_cs *engine)
I915_WRITE_FW(RING_RESET_CTL(engine->mmio_base),
_MASKED_BIT_ENABLE(RESET_CTL_REQUEST_RESET));
- ret = intel_wait_for_register_fw(dev_priv,
- RING_RESET_CTL(engine->mmio_base),
- RESET_CTL_READY_TO_RESET,
- RESET_CTL_READY_TO_RESET,
- 700);
+ ret = __intel_wait_for_register_fw(dev_priv,
+ RING_RESET_CTL(engine->mmio_base),
+ RESET_CTL_READY_TO_RESET,
+ RESET_CTL_READY_TO_RESET,
+ 700, 0,
+ NULL);
if (ret)
DRM_ERROR("%s: reset request timeout\n", engine->name);
@@ -2000,7 +2101,10 @@ static int gen8_reset_engines(struct drm_i915_private *dev_priv,
if (gen8_reset_engine_start(engine))
goto not_ready;
- return gen6_reset_engines(dev_priv, engine_mask);
+ if (INTEL_GEN(dev_priv) >= 11)
+ return gen11_reset_engines(dev_priv, engine_mask);
+ else
+ return gen6_reset_engines(dev_priv, engine_mask);
not_ready:
for_each_engine_masked(engine, dev_priv, engine_mask, tmp)
@@ -2038,15 +2142,31 @@ int intel_gpu_reset(struct drm_i915_private *dev_priv, unsigned engine_mask)
int retry;
int ret;
- might_sleep();
+ /*
+ * We want to perform per-engine reset from atomic context (e.g.
+ * softirq), which imposes the constraint that we cannot sleep.
+ * However, experience suggests that spending a bit of time waiting
+ * for a reset helps in various cases, so for a full-device reset
+ * we apply the opposite rule and wait if we want to. As we should
+ * always follow up a failed per-engine reset with a full device reset,
+ * being a little faster, stricter and more error prone for the
+ * atomic case seems an acceptable compromise.
+ *
+ * Unfortunately this leads to a bimodal routine, when the goal was
+ * to have a single reset function that worked for resetting any
+ * number of engines simultaneously.
+ */
+ might_sleep_if(engine_mask == ALL_ENGINES);
- /* If the power well sleeps during the reset, the reset
+ /*
+ * If the power well sleeps during the reset, the reset
* request may be dropped and never completes (causing -EIO).
*/
intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
for (retry = 0; retry < 3; retry++) {
- /* We stop engines, otherwise we might get failed reset and a
+ /*
+ * We stop engines, otherwise we might get failed reset and a
* dead gpu (on elk). Also as modern gpu as kbl can suffer
* from system hang if batchbuffer is progressing when
* the reset is issued, regardless of READY_TO_RESET ack.
@@ -2060,9 +2180,11 @@ int intel_gpu_reset(struct drm_i915_private *dev_priv, unsigned engine_mask)
i915_stop_engines(dev_priv, engine_mask);
ret = -ENODEV;
- if (reset)
+ if (reset) {
+ GEM_TRACE("engine_mask=%x\n", engine_mask);
ret = reset(dev_priv, engine_mask);
- if (ret != -ETIMEDOUT)
+ }
+ if (ret != -ETIMEDOUT || engine_mask != ALL_ENGINES)
break;
cond_resched();
@@ -2085,12 +2207,14 @@ bool intel_has_reset_engine(struct drm_i915_private *dev_priv)
int intel_reset_guc(struct drm_i915_private *dev_priv)
{
+ u32 guc_domain = INTEL_GEN(dev_priv) >= 11 ? GEN11_GRDOM_GUC :
+ GEN9_GRDOM_GUC;
int ret;
GEM_BUG_ON(!HAS_GUC(dev_priv));
intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
- ret = gen6_hw_domain_reset(dev_priv, GEN9_GRDOM_GUC);
+ ret = gen6_hw_domain_reset(dev_priv, guc_domain);
intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
return ret;
diff --git a/drivers/gpu/drm/i915/intel_uncore.h b/drivers/gpu/drm/i915/intel_uncore.h
index dfdf444e4bcc..47478d609630 100644
--- a/drivers/gpu/drm/i915/intel_uncore.h
+++ b/drivers/gpu/drm/i915/intel_uncore.h
@@ -140,6 +140,7 @@ struct intel_uncore {
void intel_uncore_sanitize(struct drm_i915_private *dev_priv);
void intel_uncore_init(struct drm_i915_private *dev_priv);
+void intel_uncore_prune(struct drm_i915_private *dev_priv);
bool intel_uncore_unclaimed_mmio(struct drm_i915_private *dev_priv);
bool intel_uncore_arm_unclaimed_mmio_detection(struct drm_i915_private *dev_priv);
void intel_uncore_fini(struct drm_i915_private *dev_priv);
diff --git a/drivers/gpu/drm/i915/intel_wopcm.c b/drivers/gpu/drm/i915/intel_wopcm.c
new file mode 100644
index 000000000000..74bf76f3fddc
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_wopcm.c
@@ -0,0 +1,275 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright © 2017-2018 Intel Corporation
+ */
+
+#include "intel_wopcm.h"
+#include "i915_drv.h"
+
+/**
+ * DOC: WOPCM Layout
+ *
+ * The layout of the WOPCM will be fixed after writing to GuC WOPCM size and
+ * offset registers whose values are calculated and determined by HuC/GuC
+ * firmware size and set of hardware requirements/restrictions as shown below:
+ *
+ * ::
+ *
+ * +=========> +====================+ <== WOPCM Top
+ * ^ | HW contexts RSVD |
+ * | +===> +====================+ <== GuC WOPCM Top
+ * | ^ | |
+ * | | | |
+ * | | | |
+ * | GuC | |
+ * | WOPCM | |
+ * | Size +--------------------+
+ * WOPCM | | GuC FW RSVD |
+ * | | +--------------------+
+ * | | | GuC Stack RSVD |
+ * | | +------------------- +
+ * | v | GuC WOPCM RSVD |
+ * | +===> +====================+ <== GuC WOPCM base
+ * | | WOPCM RSVD |
+ * | +------------------- + <== HuC Firmware Top
+ * v | HuC FW |
+ * +=========> +====================+ <== WOPCM Base
+ *
+ * GuC accessible WOPCM starts at GuC WOPCM base and ends at GuC WOPCM top.
+ * The top part of the WOPCM is reserved for hardware contexts (e.g. RC6
+ * context).
+ */
+
+/* Default WOPCM size 1MB. */
+#define GEN9_WOPCM_SIZE (1024 * 1024)
+/* 16KB WOPCM (RSVD WOPCM) is reserved from HuC firmware top. */
+#define WOPCM_RESERVED_SIZE (16 * 1024)
+
+/* 16KB reserved at the beginning of GuC WOPCM. */
+#define GUC_WOPCM_RESERVED (16 * 1024)
+/* 8KB from GUC_WOPCM_RESERVED is reserved for GuC stack. */
+#define GUC_WOPCM_STACK_RESERVED (8 * 1024)
+
+/* GuC WOPCM Offset value needs to be aligned to 16KB. */
+#define GUC_WOPCM_OFFSET_ALIGNMENT (1UL << GUC_WOPCM_OFFSET_SHIFT)
+
+/* 24KB at the end of WOPCM is reserved for RC6 CTX on BXT. */
+#define BXT_WOPCM_RC6_CTX_RESERVED (24 * 1024)
+/* 36KB WOPCM reserved at the end of WOPCM on CNL. */
+#define CNL_WOPCM_HW_CTX_RESERVED (36 * 1024)
+
+/* 128KB from GUC_WOPCM_RESERVED is reserved for FW on Gen9. */
+#define GEN9_GUC_FW_RESERVED (128 * 1024)
+#define GEN9_GUC_WOPCM_OFFSET (GUC_WOPCM_RESERVED + GEN9_GUC_FW_RESERVED)
+
+/**
+ * intel_wopcm_init_early() - Early initialization of the WOPCM.
+ * @wopcm: pointer to intel_wopcm.
+ *
+ * Setup the size of WOPCM which will be used by later on WOPCM partitioning.
+ */
+void intel_wopcm_init_early(struct intel_wopcm *wopcm)
+{
+ wopcm->size = GEN9_WOPCM_SIZE;
+
+ DRM_DEBUG_DRIVER("WOPCM size: %uKiB\n", wopcm->size / 1024);
+}
+
+static inline u32 context_reserved_size(struct drm_i915_private *i915)
+{
+ if (IS_GEN9_LP(i915))
+ return BXT_WOPCM_RC6_CTX_RESERVED;
+ else if (INTEL_GEN(i915) >= 10)
+ return CNL_WOPCM_HW_CTX_RESERVED;
+ else
+ return 0;
+}
+
+static inline int gen9_check_dword_gap(u32 guc_wopcm_base, u32 guc_wopcm_size)
+{
+ u32 offset;
+
+ /*
+ * GuC WOPCM size shall be at least a dword larger than the offset from
+ * WOPCM base (GuC WOPCM offset from WOPCM base + GEN9_GUC_WOPCM_OFFSET)
+ * due to hardware limitation on Gen9.
+ */
+ offset = guc_wopcm_base + GEN9_GUC_WOPCM_OFFSET;
+ if (offset > guc_wopcm_size ||
+ (guc_wopcm_size - offset) < sizeof(u32)) {
+ DRM_ERROR("GuC WOPCM size %uKiB is too small. %uKiB needed.\n",
+ guc_wopcm_size / 1024,
+ (u32)(offset + sizeof(u32)) / 1024);
+ return -E2BIG;
+ }
+
+ return 0;
+}
+
+static inline int gen9_check_huc_fw_fits(u32 guc_wopcm_size, u32 huc_fw_size)
+{
+ /*
+ * On Gen9 & CNL A0, hardware requires the total available GuC WOPCM
+ * size to be larger than or equal to HuC firmware size. Otherwise,
+ * firmware uploading would fail.
+ */
+ if (huc_fw_size > guc_wopcm_size - GUC_WOPCM_RESERVED) {
+ DRM_ERROR("HuC FW (%uKiB) won't fit in GuC WOPCM (%uKiB).\n",
+ huc_fw_size / 1024,
+ (guc_wopcm_size - GUC_WOPCM_RESERVED) / 1024);
+ return -E2BIG;
+ }
+
+ return 0;
+}
+
+static inline int check_hw_restriction(struct drm_i915_private *i915,
+ u32 guc_wopcm_base, u32 guc_wopcm_size,
+ u32 huc_fw_size)
+{
+ int err = 0;
+
+ if (IS_GEN9(i915))
+ err = gen9_check_dword_gap(guc_wopcm_base, guc_wopcm_size);
+
+ if (!err &&
+ (IS_GEN9(i915) || IS_CNL_REVID(i915, CNL_REVID_A0, CNL_REVID_A0)))
+ err = gen9_check_huc_fw_fits(guc_wopcm_size, huc_fw_size);
+
+ return err;
+}
+
+/**
+ * intel_wopcm_init() - Initialize the WOPCM structure.
+ * @wopcm: pointer to intel_wopcm.
+ *
+ * This function will partition WOPCM space based on GuC and HuC firmware sizes
+ * and will allocate max remaining for use by GuC. This function will also
+ * enforce platform dependent hardware restrictions on GuC WOPCM offset and
+ * size. It will fail the WOPCM init if any of these checks were failed, so that
+ * the following GuC firmware uploading would be aborted.
+ *
+ * Return: 0 on success, non-zero error code on failure.
+ */
+int intel_wopcm_init(struct intel_wopcm *wopcm)
+{
+ struct drm_i915_private *i915 = wopcm_to_i915(wopcm);
+ u32 guc_fw_size = intel_uc_fw_get_upload_size(&i915->guc.fw);
+ u32 huc_fw_size = intel_uc_fw_get_upload_size(&i915->huc.fw);
+ u32 ctx_rsvd = context_reserved_size(i915);
+ u32 guc_wopcm_base;
+ u32 guc_wopcm_size;
+ u32 guc_wopcm_rsvd;
+ int err;
+
+ GEM_BUG_ON(!wopcm->size);
+
+ if (guc_fw_size >= wopcm->size) {
+ DRM_ERROR("GuC FW (%uKiB) is too big to fit in WOPCM.",
+ guc_fw_size / 1024);
+ return -E2BIG;
+ }
+
+ if (huc_fw_size >= wopcm->size) {
+ DRM_ERROR("HuC FW (%uKiB) is too big to fit in WOPCM.",
+ huc_fw_size / 1024);
+ return -E2BIG;
+ }
+
+ guc_wopcm_base = ALIGN(huc_fw_size + WOPCM_RESERVED_SIZE,
+ GUC_WOPCM_OFFSET_ALIGNMENT);
+ if ((guc_wopcm_base + ctx_rsvd) >= wopcm->size) {
+ DRM_ERROR("GuC WOPCM base (%uKiB) is too big.\n",
+ guc_wopcm_base / 1024);
+ return -E2BIG;
+ }
+
+ guc_wopcm_size = wopcm->size - guc_wopcm_base - ctx_rsvd;
+ guc_wopcm_size &= GUC_WOPCM_SIZE_MASK;
+
+ DRM_DEBUG_DRIVER("Calculated GuC WOPCM Region: [%uKiB, %uKiB)\n",
+ guc_wopcm_base / 1024, guc_wopcm_size / 1024);
+
+ guc_wopcm_rsvd = GUC_WOPCM_RESERVED + GUC_WOPCM_STACK_RESERVED;
+ if ((guc_fw_size + guc_wopcm_rsvd) > guc_wopcm_size) {
+ DRM_ERROR("Need %uKiB WOPCM for GuC, %uKiB available.\n",
+ (guc_fw_size + guc_wopcm_rsvd) / 1024,
+ guc_wopcm_size / 1024);
+ return -E2BIG;
+ }
+
+ err = check_hw_restriction(i915, guc_wopcm_base, guc_wopcm_size,
+ huc_fw_size);
+ if (err)
+ return err;
+
+ wopcm->guc.base = guc_wopcm_base;
+ wopcm->guc.size = guc_wopcm_size;
+
+ return 0;
+}
+
+static inline int write_and_verify(struct drm_i915_private *dev_priv,
+ i915_reg_t reg, u32 val, u32 mask,
+ u32 locked_bit)
+{
+ u32 reg_val;
+
+ GEM_BUG_ON(val & ~mask);
+
+ I915_WRITE(reg, val);
+
+ reg_val = I915_READ(reg);
+
+ return (reg_val & mask) != (val | locked_bit) ? -EIO : 0;
+}
+
+/**
+ * intel_wopcm_init_hw() - Setup GuC WOPCM registers.
+ * @wopcm: pointer to intel_wopcm.
+ *
+ * Setup the GuC WOPCM size and offset registers with the calculated values. It
+ * will verify the register values to make sure the registers are locked with
+ * correct values.
+ *
+ * Return: 0 on success. -EIO if registers were locked with incorrect values.
+ */
+int intel_wopcm_init_hw(struct intel_wopcm *wopcm)
+{
+ struct drm_i915_private *dev_priv = wopcm_to_i915(wopcm);
+ u32 huc_agent;
+ u32 mask;
+ int err;
+
+ if (!USES_GUC(dev_priv))
+ return 0;
+
+ GEM_BUG_ON(!HAS_GUC(dev_priv));
+ GEM_BUG_ON(!wopcm->guc.size);
+ GEM_BUG_ON(!wopcm->guc.base);
+
+ err = write_and_verify(dev_priv, GUC_WOPCM_SIZE, wopcm->guc.size,
+ GUC_WOPCM_SIZE_MASK | GUC_WOPCM_SIZE_LOCKED,
+ GUC_WOPCM_SIZE_LOCKED);
+ if (err)
+ goto err_out;
+
+ huc_agent = USES_HUC(dev_priv) ? HUC_LOADING_AGENT_GUC : 0;
+ mask = GUC_WOPCM_OFFSET_MASK | GUC_WOPCM_OFFSET_VALID | huc_agent;
+ err = write_and_verify(dev_priv, DMA_GUC_WOPCM_OFFSET,
+ wopcm->guc.base | huc_agent, mask,
+ GUC_WOPCM_OFFSET_VALID);
+ if (err)
+ goto err_out;
+
+ return 0;
+
+err_out:
+ DRM_ERROR("Failed to init WOPCM registers:\n");
+ DRM_ERROR("DMA_GUC_WOPCM_OFFSET=%#x\n",
+ I915_READ(DMA_GUC_WOPCM_OFFSET));
+ DRM_ERROR("GUC_WOPCM_SIZE=%#x\n", I915_READ(GUC_WOPCM_SIZE));
+
+ return err;
+}
diff --git a/drivers/gpu/drm/i915/intel_wopcm.h b/drivers/gpu/drm/i915/intel_wopcm.h
new file mode 100644
index 000000000000..6298910a384c
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_wopcm.h
@@ -0,0 +1,31 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright © 2017-2018 Intel Corporation
+ */
+
+#ifndef _INTEL_WOPCM_H_
+#define _INTEL_WOPCM_H_
+
+#include <linux/types.h>
+
+/**
+ * struct intel_wopcm - Overall WOPCM info and WOPCM regions.
+ * @size: Size of overall WOPCM.
+ * @guc: GuC WOPCM Region info.
+ * @guc.base: GuC WOPCM base which is offset from WOPCM base.
+ * @guc.size: Size of the GuC WOPCM region.
+ */
+struct intel_wopcm {
+ u32 size;
+ struct {
+ u32 base;
+ u32 size;
+ } guc;
+};
+
+void intel_wopcm_init_early(struct intel_wopcm *wopcm);
+int intel_wopcm_init(struct intel_wopcm *wopcm);
+int intel_wopcm_init_hw(struct intel_wopcm *wopcm);
+
+#endif
diff --git a/drivers/gpu/drm/i915/intel_workarounds.c b/drivers/gpu/drm/i915/intel_workarounds.c
new file mode 100644
index 000000000000..bbbf4ed4aa97
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_workarounds.c
@@ -0,0 +1,856 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright © 2014-2018 Intel Corporation
+ */
+
+#include "i915_drv.h"
+#include "intel_workarounds.h"
+
+/**
+ * DOC: Hardware workarounds
+ *
+ * This file is intended as a central place to implement most [1]_ of the
+ * required workarounds for hardware to work as originally intended. They fall
+ * in five basic categories depending on how/when they are applied:
+ *
+ * - Workarounds that touch registers that are saved/restored to/from the HW
+ * context image. The list is emitted (via Load Register Immediate commands)
+ * everytime a new context is created.
+ * - GT workarounds. The list of these WAs is applied whenever these registers
+ * revert to default values (on GPU reset, suspend/resume [2]_, etc..).
+ * - Display workarounds. The list is applied during display clock-gating
+ * initialization.
+ * - Workarounds that whitelist a privileged register, so that UMDs can manage
+ * them directly. This is just a special case of a MMMIO workaround (as we
+ * write the list of these to/be-whitelisted registers to some special HW
+ * registers).
+ * - Workaround batchbuffers, that get executed automatically by the hardware
+ * on every HW context restore.
+ *
+ * .. [1] Please notice that there are other WAs that, due to their nature,
+ * cannot be applied from a central place. Those are peppered around the rest
+ * of the code, as needed.
+ *
+ * .. [2] Technically, some registers are powercontext saved & restored, so they
+ * survive a suspend/resume. In practice, writing them again is not too
+ * costly and simplifies things. We can revisit this in the future.
+ *
+ * Layout
+ * ''''''
+ *
+ * Keep things in this file ordered by WA type, as per the above (context, GT,
+ * display, register whitelist, batchbuffer). Then, inside each type, keep the
+ * following order:
+ *
+ * - Infrastructure functions and macros
+ * - WAs per platform in standard gen/chrono order
+ * - Public functions to init or apply the given workaround type.
+ */
+
+static int wa_add(struct drm_i915_private *dev_priv,
+ i915_reg_t addr,
+ const u32 mask, const u32 val)
+{
+ const unsigned int idx = dev_priv->workarounds.count;
+
+ if (WARN_ON(idx >= I915_MAX_WA_REGS))
+ return -ENOSPC;
+
+ dev_priv->workarounds.reg[idx].addr = addr;
+ dev_priv->workarounds.reg[idx].value = val;
+ dev_priv->workarounds.reg[idx].mask = mask;
+
+ dev_priv->workarounds.count++;
+
+ return 0;
+}
+
+#define WA_REG(addr, mask, val) do { \
+ const int r = wa_add(dev_priv, (addr), (mask), (val)); \
+ if (r) \
+ return r; \
+ } while (0)
+
+#define WA_SET_BIT_MASKED(addr, mask) \
+ WA_REG(addr, (mask), _MASKED_BIT_ENABLE(mask))
+
+#define WA_CLR_BIT_MASKED(addr, mask) \
+ WA_REG(addr, (mask), _MASKED_BIT_DISABLE(mask))
+
+#define WA_SET_FIELD_MASKED(addr, mask, value) \
+ WA_REG(addr, (mask), _MASKED_FIELD(mask, value))
+
+static int gen8_ctx_workarounds_init(struct drm_i915_private *dev_priv)
+{
+ WA_SET_BIT_MASKED(INSTPM, INSTPM_FORCE_ORDERING);
+
+ /* WaDisableAsyncFlipPerfMode:bdw,chv */
+ WA_SET_BIT_MASKED(MI_MODE, ASYNC_FLIP_PERF_DISABLE);
+
+ /* WaDisablePartialInstShootdown:bdw,chv */
+ WA_SET_BIT_MASKED(GEN8_ROW_CHICKEN,
+ PARTIAL_INSTRUCTION_SHOOTDOWN_DISABLE);
+
+ /* Use Force Non-Coherent whenever executing a 3D context. This is a
+ * workaround for for a possible hang in the unlikely event a TLB
+ * invalidation occurs during a PSD flush.
+ */
+ /* WaForceEnableNonCoherent:bdw,chv */
+ /* WaHdcDisableFetchWhenMasked:bdw,chv */
+ WA_SET_BIT_MASKED(HDC_CHICKEN0,
+ HDC_DONOT_FETCH_MEM_WHEN_MASKED |
+ HDC_FORCE_NON_COHERENT);
+
+ /* From the Haswell PRM, Command Reference: Registers, CACHE_MODE_0:
+ * "The Hierarchical Z RAW Stall Optimization allows non-overlapping
+ * polygons in the same 8x4 pixel/sample area to be processed without
+ * stalling waiting for the earlier ones to write to Hierarchical Z
+ * buffer."
+ *
+ * This optimization is off by default for BDW and CHV; turn it on.
+ */
+ WA_CLR_BIT_MASKED(CACHE_MODE_0_GEN7, HIZ_RAW_STALL_OPT_DISABLE);
+
+ /* Wa4x4STCOptimizationDisable:bdw,chv */
+ WA_SET_BIT_MASKED(CACHE_MODE_1, GEN8_4x4_STC_OPTIMIZATION_DISABLE);
+
+ /*
+ * BSpec recommends 8x4 when MSAA is used,
+ * however in practice 16x4 seems fastest.
+ *
+ * Note that PS/WM thread counts depend on the WIZ hashing
+ * disable bit, which we don't touch here, but it's good
+ * to keep in mind (see 3DSTATE_PS and 3DSTATE_WM).
+ */
+ WA_SET_FIELD_MASKED(GEN7_GT_MODE,
+ GEN6_WIZ_HASHING_MASK,
+ GEN6_WIZ_HASHING_16x4);
+
+ return 0;
+}
+
+static int bdw_ctx_workarounds_init(struct drm_i915_private *dev_priv)
+{
+ int ret;
+
+ ret = gen8_ctx_workarounds_init(dev_priv);
+ if (ret)
+ return ret;
+
+ /* WaDisableThreadStallDopClockGating:bdw (pre-production) */
+ WA_SET_BIT_MASKED(GEN8_ROW_CHICKEN, STALL_DOP_GATING_DISABLE);
+
+ /* WaDisableDopClockGating:bdw
+ *
+ * Also see the related UCGTCL1 write in broadwell_init_clock_gating()
+ * to disable EUTC clock gating.
+ */
+ WA_SET_BIT_MASKED(GEN7_ROW_CHICKEN2,
+ DOP_CLOCK_GATING_DISABLE);
+
+ WA_SET_BIT_MASKED(HALF_SLICE_CHICKEN3,
+ GEN8_SAMPLER_POWER_BYPASS_DIS);
+
+ WA_SET_BIT_MASKED(HDC_CHICKEN0,
+ /* WaForceContextSaveRestoreNonCoherent:bdw */
+ HDC_FORCE_CONTEXT_SAVE_RESTORE_NON_COHERENT |
+ /* WaDisableFenceDestinationToSLM:bdw (pre-prod) */
+ (IS_BDW_GT3(dev_priv) ? HDC_FENCE_DEST_SLM_DISABLE : 0));
+
+ return 0;
+}
+
+static int chv_ctx_workarounds_init(struct drm_i915_private *dev_priv)
+{
+ int ret;
+
+ ret = gen8_ctx_workarounds_init(dev_priv);
+ if (ret)
+ return ret;
+
+ /* WaDisableThreadStallDopClockGating:chv */
+ WA_SET_BIT_MASKED(GEN8_ROW_CHICKEN, STALL_DOP_GATING_DISABLE);
+
+ /* Improve HiZ throughput on CHV. */
+ WA_SET_BIT_MASKED(HIZ_CHICKEN, CHV_HZ_8X8_MODE_IN_1X);
+
+ return 0;
+}
+
+static int gen9_ctx_workarounds_init(struct drm_i915_private *dev_priv)
+{
+ if (HAS_LLC(dev_priv)) {
+ /* WaCompressedResourceSamplerPbeMediaNewHashMode:skl,kbl
+ *
+ * Must match Display Engine. See
+ * WaCompressedResourceDisplayNewHashMode.
+ */
+ WA_SET_BIT_MASKED(COMMON_SLICE_CHICKEN2,
+ GEN9_PBE_COMPRESSED_HASH_SELECTION);
+ WA_SET_BIT_MASKED(GEN9_HALF_SLICE_CHICKEN7,
+ GEN9_SAMPLER_HASH_COMPRESSED_READ_ADDR);
+ }
+
+ /* WaClearFlowControlGpgpuContextSave:skl,bxt,kbl,glk,cfl */
+ /* WaDisablePartialInstShootdown:skl,bxt,kbl,glk,cfl */
+ WA_SET_BIT_MASKED(GEN8_ROW_CHICKEN,
+ FLOW_CONTROL_ENABLE |
+ PARTIAL_INSTRUCTION_SHOOTDOWN_DISABLE);
+
+ /* Syncing dependencies between camera and graphics:skl,bxt,kbl */
+ if (!IS_COFFEELAKE(dev_priv))
+ WA_SET_BIT_MASKED(HALF_SLICE_CHICKEN3,
+ GEN9_DISABLE_OCL_OOB_SUPPRESS_LOGIC);
+
+ /* WaEnableYV12BugFixInHalfSliceChicken7:skl,bxt,kbl,glk,cfl */
+ /* WaEnableSamplerGPGPUPreemptionSupport:skl,bxt,kbl,cfl */
+ WA_SET_BIT_MASKED(GEN9_HALF_SLICE_CHICKEN7,
+ GEN9_ENABLE_YV12_BUGFIX |
+ GEN9_ENABLE_GPGPU_PREEMPTION);
+
+ /* Wa4x4STCOptimizationDisable:skl,bxt,kbl,glk,cfl */
+ /* WaDisablePartialResolveInVc:skl,bxt,kbl,cfl */
+ WA_SET_BIT_MASKED(CACHE_MODE_1,
+ GEN8_4x4_STC_OPTIMIZATION_DISABLE |
+ GEN9_PARTIAL_RESOLVE_IN_VC_DISABLE);
+
+ /* WaCcsTlbPrefetchDisable:skl,bxt,kbl,glk,cfl */
+ WA_CLR_BIT_MASKED(GEN9_HALF_SLICE_CHICKEN5,
+ GEN9_CCS_TLB_PREFETCH_ENABLE);
+
+ /* WaForceContextSaveRestoreNonCoherent:skl,bxt,kbl,cfl */
+ WA_SET_BIT_MASKED(HDC_CHICKEN0,
+ HDC_FORCE_CONTEXT_SAVE_RESTORE_NON_COHERENT |
+ HDC_FORCE_CSR_NON_COHERENT_OVR_DISABLE);
+
+ /* WaForceEnableNonCoherent and WaDisableHDCInvalidation are
+ * both tied to WaForceContextSaveRestoreNonCoherent
+ * in some hsds for skl. We keep the tie for all gen9. The
+ * documentation is a bit hazy and so we want to get common behaviour,
+ * even though there is no clear evidence we would need both on kbl/bxt.
+ * This area has been source of system hangs so we play it safe
+ * and mimic the skl regardless of what bspec says.
+ *
+ * Use Force Non-Coherent whenever executing a 3D context. This
+ * is a workaround for a possible hang in the unlikely event
+ * a TLB invalidation occurs during a PSD flush.
+ */
+
+ /* WaForceEnableNonCoherent:skl,bxt,kbl,cfl */
+ WA_SET_BIT_MASKED(HDC_CHICKEN0,
+ HDC_FORCE_NON_COHERENT);
+
+ /* WaDisableSamplerPowerBypassForSOPingPong:skl,bxt,kbl,cfl */
+ if (IS_SKYLAKE(dev_priv) ||
+ IS_KABYLAKE(dev_priv) ||
+ IS_COFFEELAKE(dev_priv))
+ WA_SET_BIT_MASKED(HALF_SLICE_CHICKEN3,
+ GEN8_SAMPLER_POWER_BYPASS_DIS);
+
+ /* WaDisableSTUnitPowerOptimization:skl,bxt,kbl,glk,cfl */
+ WA_SET_BIT_MASKED(HALF_SLICE_CHICKEN2, GEN8_ST_PO_DISABLE);
+
+ /*
+ * Supporting preemption with fine-granularity requires changes in the
+ * batch buffer programming. Since we can't break old userspace, we
+ * need to set our default preemption level to safe value. Userspace is
+ * still able to use more fine-grained preemption levels, since in
+ * WaEnablePreemptionGranularityControlByUMD we're whitelisting the
+ * per-ctx register. As such, WaDisable{3D,GPGPU}MidCmdPreemption are
+ * not real HW workarounds, but merely a way to start using preemption
+ * while maintaining old contract with userspace.
+ */
+
+ /* WaDisable3DMidCmdPreemption:skl,bxt,glk,cfl,[cnl] */
+ WA_CLR_BIT_MASKED(GEN8_CS_CHICKEN1, GEN9_PREEMPT_3D_OBJECT_LEVEL);
+
+ /* WaDisableGPGPUMidCmdPreemption:skl,bxt,blk,cfl,[cnl] */
+ WA_SET_FIELD_MASKED(GEN8_CS_CHICKEN1,
+ GEN9_PREEMPT_GPGPU_LEVEL_MASK,
+ GEN9_PREEMPT_GPGPU_COMMAND_LEVEL);
+
+ return 0;
+}
+
+static int skl_tune_iz_hashing(struct drm_i915_private *dev_priv)
+{
+ u8 vals[3] = { 0, 0, 0 };
+ unsigned int i;
+
+ for (i = 0; i < 3; i++) {
+ u8 ss;
+
+ /*
+ * Only consider slices where one, and only one, subslice has 7
+ * EUs
+ */
+ if (!is_power_of_2(INTEL_INFO(dev_priv)->sseu.subslice_7eu[i]))
+ continue;
+
+ /*
+ * subslice_7eu[i] != 0 (because of the check above) and
+ * ss_max == 4 (maximum number of subslices possible per slice)
+ *
+ * -> 0 <= ss <= 3;
+ */
+ ss = ffs(INTEL_INFO(dev_priv)->sseu.subslice_7eu[i]) - 1;
+ vals[i] = 3 - ss;
+ }
+
+ if (vals[0] == 0 && vals[1] == 0 && vals[2] == 0)
+ return 0;
+
+ /* Tune IZ hashing. See intel_device_info_runtime_init() */
+ WA_SET_FIELD_MASKED(GEN7_GT_MODE,
+ GEN9_IZ_HASHING_MASK(2) |
+ GEN9_IZ_HASHING_MASK(1) |
+ GEN9_IZ_HASHING_MASK(0),
+ GEN9_IZ_HASHING(2, vals[2]) |
+ GEN9_IZ_HASHING(1, vals[1]) |
+ GEN9_IZ_HASHING(0, vals[0]));
+
+ return 0;
+}
+
+static int skl_ctx_workarounds_init(struct drm_i915_private *dev_priv)
+{
+ int ret;
+
+ ret = gen9_ctx_workarounds_init(dev_priv);
+ if (ret)
+ return ret;
+
+ return skl_tune_iz_hashing(dev_priv);
+}
+
+static int bxt_ctx_workarounds_init(struct drm_i915_private *dev_priv)
+{
+ int ret;
+
+ ret = gen9_ctx_workarounds_init(dev_priv);
+ if (ret)
+ return ret;
+
+ /* WaDisableThreadStallDopClockGating:bxt */
+ WA_SET_BIT_MASKED(GEN8_ROW_CHICKEN,
+ STALL_DOP_GATING_DISABLE);
+
+ /* WaToEnableHwFixForPushConstHWBug:bxt */
+ WA_SET_BIT_MASKED(COMMON_SLICE_CHICKEN2,
+ GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION);
+
+ return 0;
+}
+
+static int kbl_ctx_workarounds_init(struct drm_i915_private *dev_priv)
+{
+ int ret;
+
+ ret = gen9_ctx_workarounds_init(dev_priv);
+ if (ret)
+ return ret;
+
+ /* WaDisableFenceDestinationToSLM:kbl (pre-prod) */
+ if (IS_KBL_REVID(dev_priv, KBL_REVID_A0, KBL_REVID_A0))
+ WA_SET_BIT_MASKED(HDC_CHICKEN0,
+ HDC_FENCE_DEST_SLM_DISABLE);
+
+ /* WaToEnableHwFixForPushConstHWBug:kbl */
+ if (IS_KBL_REVID(dev_priv, KBL_REVID_C0, REVID_FOREVER))
+ WA_SET_BIT_MASKED(COMMON_SLICE_CHICKEN2,
+ GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION);
+
+ /* WaDisableSbeCacheDispatchPortSharing:kbl */
+ WA_SET_BIT_MASKED(GEN7_HALF_SLICE_CHICKEN1,
+ GEN7_SBE_SS_CACHE_DISPATCH_PORT_SHARING_DISABLE);
+
+ return 0;
+}
+
+static int glk_ctx_workarounds_init(struct drm_i915_private *dev_priv)
+{
+ int ret;
+
+ ret = gen9_ctx_workarounds_init(dev_priv);
+ if (ret)
+ return ret;
+
+ /* WaToEnableHwFixForPushConstHWBug:glk */
+ WA_SET_BIT_MASKED(COMMON_SLICE_CHICKEN2,
+ GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION);
+
+ return 0;
+}
+
+static int cfl_ctx_workarounds_init(struct drm_i915_private *dev_priv)
+{
+ int ret;
+
+ ret = gen9_ctx_workarounds_init(dev_priv);
+ if (ret)
+ return ret;
+
+ /* WaToEnableHwFixForPushConstHWBug:cfl */
+ WA_SET_BIT_MASKED(COMMON_SLICE_CHICKEN2,
+ GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION);
+
+ /* WaDisableSbeCacheDispatchPortSharing:cfl */
+ WA_SET_BIT_MASKED(GEN7_HALF_SLICE_CHICKEN1,
+ GEN7_SBE_SS_CACHE_DISPATCH_PORT_SHARING_DISABLE);
+
+ return 0;
+}
+
+static int cnl_ctx_workarounds_init(struct drm_i915_private *dev_priv)
+{
+ /* WaForceContextSaveRestoreNonCoherent:cnl */
+ WA_SET_BIT_MASKED(CNL_HDC_CHICKEN0,
+ HDC_FORCE_CONTEXT_SAVE_RESTORE_NON_COHERENT);
+
+ /* WaThrottleEUPerfToAvoidTDBackPressure:cnl(pre-prod) */
+ if (IS_CNL_REVID(dev_priv, CNL_REVID_B0, CNL_REVID_B0))
+ WA_SET_BIT_MASKED(GEN8_ROW_CHICKEN, THROTTLE_12_5);
+
+ /* WaDisableReplayBufferBankArbitrationOptimization:cnl */
+ WA_SET_BIT_MASKED(COMMON_SLICE_CHICKEN2,
+ GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION);
+
+ /* WaDisableEnhancedSBEVertexCaching:cnl (pre-prod) */
+ if (IS_CNL_REVID(dev_priv, 0, CNL_REVID_B0))
+ WA_SET_BIT_MASKED(COMMON_SLICE_CHICKEN2,
+ GEN8_CSC2_SBE_VUE_CACHE_CONSERVATIVE);
+
+ /* WaPushConstantDereferenceHoldDisable:cnl */
+ WA_SET_BIT_MASKED(GEN7_ROW_CHICKEN2, PUSH_CONSTANT_DEREF_DISABLE);
+
+ /* FtrEnableFastAnisoL1BankingFix:cnl */
+ WA_SET_BIT_MASKED(HALF_SLICE_CHICKEN3, CNL_FAST_ANISO_L1_BANKING_FIX);
+
+ /* WaDisable3DMidCmdPreemption:cnl */
+ WA_CLR_BIT_MASKED(GEN8_CS_CHICKEN1, GEN9_PREEMPT_3D_OBJECT_LEVEL);
+
+ /* WaDisableGPGPUMidCmdPreemption:cnl */
+ WA_SET_FIELD_MASKED(GEN8_CS_CHICKEN1,
+ GEN9_PREEMPT_GPGPU_LEVEL_MASK,
+ GEN9_PREEMPT_GPGPU_COMMAND_LEVEL);
+
+ /* WaDisableEarlyEOT:cnl */
+ WA_SET_BIT_MASKED(GEN8_ROW_CHICKEN, DISABLE_EARLY_EOT);
+
+ return 0;
+}
+
+int intel_ctx_workarounds_init(struct drm_i915_private *dev_priv)
+{
+ int err = 0;
+
+ dev_priv->workarounds.count = 0;
+
+ if (INTEL_GEN(dev_priv) < 8)
+ err = 0;
+ else if (IS_BROADWELL(dev_priv))
+ err = bdw_ctx_workarounds_init(dev_priv);
+ else if (IS_CHERRYVIEW(dev_priv))
+ err = chv_ctx_workarounds_init(dev_priv);
+ else if (IS_SKYLAKE(dev_priv))
+ err = skl_ctx_workarounds_init(dev_priv);
+ else if (IS_BROXTON(dev_priv))
+ err = bxt_ctx_workarounds_init(dev_priv);
+ else if (IS_KABYLAKE(dev_priv))
+ err = kbl_ctx_workarounds_init(dev_priv);
+ else if (IS_GEMINILAKE(dev_priv))
+ err = glk_ctx_workarounds_init(dev_priv);
+ else if (IS_COFFEELAKE(dev_priv))
+ err = cfl_ctx_workarounds_init(dev_priv);
+ else if (IS_CANNONLAKE(dev_priv))
+ err = cnl_ctx_workarounds_init(dev_priv);
+ else
+ MISSING_CASE(INTEL_GEN(dev_priv));
+ if (err)
+ return err;
+
+ DRM_DEBUG_DRIVER("Number of context specific w/a: %d\n",
+ dev_priv->workarounds.count);
+ return 0;
+}
+
+int intel_ctx_workarounds_emit(struct i915_request *rq)
+{
+ struct i915_workarounds *w = &rq->i915->workarounds;
+ u32 *cs;
+ int ret, i;
+
+ if (w->count == 0)
+ return 0;
+
+ ret = rq->engine->emit_flush(rq, EMIT_BARRIER);
+ if (ret)
+ return ret;
+
+ cs = intel_ring_begin(rq, (w->count * 2 + 2));
+ if (IS_ERR(cs))
+ return PTR_ERR(cs);
+
+ *cs++ = MI_LOAD_REGISTER_IMM(w->count);
+ for (i = 0; i < w->count; i++) {
+ *cs++ = i915_mmio_reg_offset(w->reg[i].addr);
+ *cs++ = w->reg[i].value;
+ }
+ *cs++ = MI_NOOP;
+
+ intel_ring_advance(rq, cs);
+
+ ret = rq->engine->emit_flush(rq, EMIT_BARRIER);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void bdw_gt_workarounds_apply(struct drm_i915_private *dev_priv)
+{
+}
+
+static void chv_gt_workarounds_apply(struct drm_i915_private *dev_priv)
+{
+}
+
+static void gen9_gt_workarounds_apply(struct drm_i915_private *dev_priv)
+{
+ /* WaContextSwitchWithConcurrentTLBInvalidate:skl,bxt,kbl,glk,cfl */
+ I915_WRITE(GEN9_CSFE_CHICKEN1_RCS,
+ _MASKED_BIT_ENABLE(GEN9_PREEMPT_GPGPU_SYNC_SWITCH_DISABLE));
+
+ /* WaEnableLbsSlaRetryTimerDecrement:skl,bxt,kbl,glk,cfl */
+ I915_WRITE(BDW_SCRATCH1, I915_READ(BDW_SCRATCH1) |
+ GEN9_LBS_SLA_RETRY_TIMER_DECREMENT_ENABLE);
+
+ /* WaDisableKillLogic:bxt,skl,kbl */
+ if (!IS_COFFEELAKE(dev_priv))
+ I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) |
+ ECOCHK_DIS_TLB);
+
+ if (HAS_LLC(dev_priv)) {
+ /* WaCompressedResourceSamplerPbeMediaNewHashMode:skl,kbl
+ *
+ * Must match Display Engine. See
+ * WaCompressedResourceDisplayNewHashMode.
+ */
+ I915_WRITE(MMCD_MISC_CTRL,
+ I915_READ(MMCD_MISC_CTRL) |
+ MMCD_PCLA |
+ MMCD_HOTSPOT_EN);
+ }
+
+ /* WaDisableHDCInvalidation:skl,bxt,kbl,cfl */
+ I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) |
+ BDW_DISABLE_HDC_INVALIDATION);
+
+ /* WaProgramL3SqcReg1DefaultForPerf:bxt,glk */
+ if (IS_GEN9_LP(dev_priv)) {
+ u32 val = I915_READ(GEN8_L3SQCREG1);
+
+ val &= ~L3_PRIO_CREDITS_MASK;
+ val |= L3_GENERAL_PRIO_CREDITS(62) | L3_HIGH_PRIO_CREDITS(2);
+ I915_WRITE(GEN8_L3SQCREG1, val);
+ }
+
+ /* WaOCLCoherentLineFlush:skl,bxt,kbl,cfl */
+ I915_WRITE(GEN8_L3SQCREG4,
+ I915_READ(GEN8_L3SQCREG4) | GEN8_LQSC_FLUSH_COHERENT_LINES);
+
+ /* WaEnablePreemptionGranularityControlByUMD:skl,bxt,kbl,cfl,[cnl] */
+ I915_WRITE(GEN7_FF_SLICE_CS_CHICKEN1,
+ _MASKED_BIT_ENABLE(GEN9_FFSC_PERCTX_PREEMPT_CTRL));
+}
+
+static void skl_gt_workarounds_apply(struct drm_i915_private *dev_priv)
+{
+ gen9_gt_workarounds_apply(dev_priv);
+
+ /* WaEnableGapsTsvCreditFix:skl */
+ I915_WRITE(GEN8_GARBCNTL,
+ I915_READ(GEN8_GARBCNTL) | GEN9_GAPS_TSV_CREDIT_DISABLE);
+
+ /* WaDisableGafsUnitClkGating:skl */
+ I915_WRITE(GEN7_UCGCTL4,
+ I915_READ(GEN7_UCGCTL4) | GEN8_EU_GAUNIT_CLOCK_GATE_DISABLE);
+
+ /* WaInPlaceDecompressionHang:skl */
+ if (IS_SKL_REVID(dev_priv, SKL_REVID_H0, REVID_FOREVER))
+ I915_WRITE(GEN9_GAMT_ECO_REG_RW_IA,
+ I915_READ(GEN9_GAMT_ECO_REG_RW_IA) |
+ GAMT_ECO_ENABLE_IN_PLACE_DECOMPRESS);
+}
+
+static void bxt_gt_workarounds_apply(struct drm_i915_private *dev_priv)
+{
+ gen9_gt_workarounds_apply(dev_priv);
+
+ /* WaDisablePooledEuLoadBalancingFix:bxt */
+ I915_WRITE(FF_SLICE_CS_CHICKEN2,
+ _MASKED_BIT_ENABLE(GEN9_POOLED_EU_LOAD_BALANCING_FIX_DISABLE));
+
+ /* WaInPlaceDecompressionHang:bxt */
+ I915_WRITE(GEN9_GAMT_ECO_REG_RW_IA,
+ I915_READ(GEN9_GAMT_ECO_REG_RW_IA) |
+ GAMT_ECO_ENABLE_IN_PLACE_DECOMPRESS);
+}
+
+static void kbl_gt_workarounds_apply(struct drm_i915_private *dev_priv)
+{
+ gen9_gt_workarounds_apply(dev_priv);
+
+ /* WaEnableGapsTsvCreditFix:kbl */
+ I915_WRITE(GEN8_GARBCNTL,
+ I915_READ(GEN8_GARBCNTL) | GEN9_GAPS_TSV_CREDIT_DISABLE);
+
+ /* WaDisableDynamicCreditSharing:kbl */
+ if (IS_KBL_REVID(dev_priv, 0, KBL_REVID_B0))
+ I915_WRITE(GAMT_CHKN_BIT_REG,
+ I915_READ(GAMT_CHKN_BIT_REG) |
+ GAMT_CHKN_DISABLE_DYNAMIC_CREDIT_SHARING);
+
+ /* WaDisableGafsUnitClkGating:kbl */
+ I915_WRITE(GEN7_UCGCTL4,
+ I915_READ(GEN7_UCGCTL4) | GEN8_EU_GAUNIT_CLOCK_GATE_DISABLE);
+
+ /* WaInPlaceDecompressionHang:kbl */
+ I915_WRITE(GEN9_GAMT_ECO_REG_RW_IA,
+ I915_READ(GEN9_GAMT_ECO_REG_RW_IA) |
+ GAMT_ECO_ENABLE_IN_PLACE_DECOMPRESS);
+}
+
+static void glk_gt_workarounds_apply(struct drm_i915_private *dev_priv)
+{
+ gen9_gt_workarounds_apply(dev_priv);
+}
+
+static void cfl_gt_workarounds_apply(struct drm_i915_private *dev_priv)
+{
+ gen9_gt_workarounds_apply(dev_priv);
+
+ /* WaEnableGapsTsvCreditFix:cfl */
+ I915_WRITE(GEN8_GARBCNTL,
+ I915_READ(GEN8_GARBCNTL) | GEN9_GAPS_TSV_CREDIT_DISABLE);
+
+ /* WaDisableGafsUnitClkGating:cfl */
+ I915_WRITE(GEN7_UCGCTL4,
+ I915_READ(GEN7_UCGCTL4) | GEN8_EU_GAUNIT_CLOCK_GATE_DISABLE);
+
+ /* WaInPlaceDecompressionHang:cfl */
+ I915_WRITE(GEN9_GAMT_ECO_REG_RW_IA,
+ I915_READ(GEN9_GAMT_ECO_REG_RW_IA) |
+ GAMT_ECO_ENABLE_IN_PLACE_DECOMPRESS);
+}
+
+static void cnl_gt_workarounds_apply(struct drm_i915_private *dev_priv)
+{
+ /* WaDisableI2mCycleOnWRPort:cnl (pre-prod) */
+ if (IS_CNL_REVID(dev_priv, CNL_REVID_B0, CNL_REVID_B0))
+ I915_WRITE(GAMT_CHKN_BIT_REG,
+ I915_READ(GAMT_CHKN_BIT_REG) |
+ GAMT_CHKN_DISABLE_I2M_CYCLE_ON_WR_PORT);
+
+ /* WaInPlaceDecompressionHang:cnl */
+ I915_WRITE(GEN9_GAMT_ECO_REG_RW_IA,
+ I915_READ(GEN9_GAMT_ECO_REG_RW_IA) |
+ GAMT_ECO_ENABLE_IN_PLACE_DECOMPRESS);
+
+ /* WaEnablePreemptionGranularityControlByUMD:cnl */
+ I915_WRITE(GEN7_FF_SLICE_CS_CHICKEN1,
+ _MASKED_BIT_ENABLE(GEN9_FFSC_PERCTX_PREEMPT_CTRL));
+}
+
+void intel_gt_workarounds_apply(struct drm_i915_private *dev_priv)
+{
+ if (INTEL_GEN(dev_priv) < 8)
+ return;
+ else if (IS_BROADWELL(dev_priv))
+ bdw_gt_workarounds_apply(dev_priv);
+ else if (IS_CHERRYVIEW(dev_priv))
+ chv_gt_workarounds_apply(dev_priv);
+ else if (IS_SKYLAKE(dev_priv))
+ skl_gt_workarounds_apply(dev_priv);
+ else if (IS_BROXTON(dev_priv))
+ bxt_gt_workarounds_apply(dev_priv);
+ else if (IS_KABYLAKE(dev_priv))
+ kbl_gt_workarounds_apply(dev_priv);
+ else if (IS_GEMINILAKE(dev_priv))
+ glk_gt_workarounds_apply(dev_priv);
+ else if (IS_COFFEELAKE(dev_priv))
+ cfl_gt_workarounds_apply(dev_priv);
+ else if (IS_CANNONLAKE(dev_priv))
+ cnl_gt_workarounds_apply(dev_priv);
+ else
+ MISSING_CASE(INTEL_GEN(dev_priv));
+}
+
+static int wa_ring_whitelist_reg(struct intel_engine_cs *engine,
+ i915_reg_t reg)
+{
+ struct drm_i915_private *dev_priv = engine->i915;
+ struct i915_workarounds *wa = &dev_priv->workarounds;
+ const unsigned int index = wa->hw_whitelist_count[engine->id];
+
+ if (WARN_ON(index >= RING_MAX_NONPRIV_SLOTS))
+ return -EINVAL;
+
+ I915_WRITE(RING_FORCE_TO_NONPRIV(engine->mmio_base, index),
+ i915_mmio_reg_offset(reg));
+ wa->hw_whitelist_count[engine->id]++;
+
+ return 0;
+}
+
+static int bdw_whitelist_workarounds_apply(struct intel_engine_cs *engine)
+{
+ return 0;
+}
+
+static int chv_whitelist_workarounds_apply(struct intel_engine_cs *engine)
+{
+ return 0;
+}
+
+static int gen9_whitelist_workarounds_apply(struct intel_engine_cs *engine)
+{
+ int ret;
+
+ /* WaVFEStateAfterPipeControlwithMediaStateClear:skl,bxt,glk,cfl */
+ ret = wa_ring_whitelist_reg(engine, GEN9_CTX_PREEMPT_REG);
+ if (ret)
+ return ret;
+
+ /* WaEnablePreemptionGranularityControlByUMD:skl,bxt,kbl,cfl,[cnl] */
+ ret = wa_ring_whitelist_reg(engine, GEN8_CS_CHICKEN1);
+ if (ret)
+ return ret;
+
+ /* WaAllowUMDToModifyHDCChicken1:skl,bxt,kbl,glk,cfl */
+ ret = wa_ring_whitelist_reg(engine, GEN8_HDC_CHICKEN1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int skl_whitelist_workarounds_apply(struct intel_engine_cs *engine)
+{
+ int ret;
+
+ ret = gen9_whitelist_workarounds_apply(engine);
+ if (ret)
+ return ret;
+
+ /* WaDisableLSQCROPERFforOCL:skl */
+ ret = wa_ring_whitelist_reg(engine, GEN8_L3SQCREG4);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int bxt_whitelist_workarounds_apply(struct intel_engine_cs *engine)
+{
+ int ret;
+
+ ret = gen9_whitelist_workarounds_apply(engine);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int kbl_whitelist_workarounds_apply(struct intel_engine_cs *engine)
+{
+ int ret;
+
+ ret = gen9_whitelist_workarounds_apply(engine);
+ if (ret)
+ return ret;
+
+ /* WaDisableLSQCROPERFforOCL:kbl */
+ ret = wa_ring_whitelist_reg(engine, GEN8_L3SQCREG4);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int glk_whitelist_workarounds_apply(struct intel_engine_cs *engine)
+{
+ int ret;
+
+ ret = gen9_whitelist_workarounds_apply(engine);
+ if (ret)
+ return ret;
+
+ /* WA #0862: Userspace has to set "Barrier Mode" to avoid hangs. */
+ ret = wa_ring_whitelist_reg(engine, GEN9_SLICE_COMMON_ECO_CHICKEN1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int cfl_whitelist_workarounds_apply(struct intel_engine_cs *engine)
+{
+ int ret;
+
+ ret = gen9_whitelist_workarounds_apply(engine);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int cnl_whitelist_workarounds_apply(struct intel_engine_cs *engine)
+{
+ int ret;
+
+ /* WaEnablePreemptionGranularityControlByUMD:cnl */
+ ret = wa_ring_whitelist_reg(engine, GEN8_CS_CHICKEN1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int intel_whitelist_workarounds_apply(struct intel_engine_cs *engine)
+{
+ struct drm_i915_private *dev_priv = engine->i915;
+ int err = 0;
+
+ WARN_ON(engine->id != RCS);
+
+ dev_priv->workarounds.hw_whitelist_count[engine->id] = 0;
+
+ if (INTEL_GEN(dev_priv) < 8)
+ err = 0;
+ else if (IS_BROADWELL(dev_priv))
+ err = bdw_whitelist_workarounds_apply(engine);
+ else if (IS_CHERRYVIEW(dev_priv))
+ err = chv_whitelist_workarounds_apply(engine);
+ else if (IS_SKYLAKE(dev_priv))
+ err = skl_whitelist_workarounds_apply(engine);
+ else if (IS_BROXTON(dev_priv))
+ err = bxt_whitelist_workarounds_apply(engine);
+ else if (IS_KABYLAKE(dev_priv))
+ err = kbl_whitelist_workarounds_apply(engine);
+ else if (IS_GEMINILAKE(dev_priv))
+ err = glk_whitelist_workarounds_apply(engine);
+ else if (IS_COFFEELAKE(dev_priv))
+ err = cfl_whitelist_workarounds_apply(engine);
+ else if (IS_CANNONLAKE(dev_priv))
+ err = cnl_whitelist_workarounds_apply(engine);
+ else
+ MISSING_CASE(INTEL_GEN(dev_priv));
+ if (err)
+ return err;
+
+ DRM_DEBUG_DRIVER("%s: Number of whitelist w/a: %d\n", engine->name,
+ dev_priv->workarounds.hw_whitelist_count[engine->id]);
+ return 0;
+}
diff --git a/drivers/gpu/drm/i915/intel_workarounds.h b/drivers/gpu/drm/i915/intel_workarounds.h
new file mode 100644
index 000000000000..d9b0cc5afb4a
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_workarounds.h
@@ -0,0 +1,17 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright © 2014-2018 Intel Corporation
+ */
+
+#ifndef _I915_WORKAROUNDS_H_
+#define _I915_WORKAROUNDS_H_
+
+int intel_ctx_workarounds_init(struct drm_i915_private *dev_priv);
+int intel_ctx_workarounds_emit(struct i915_request *rq);
+
+void intel_gt_workarounds_apply(struct drm_i915_private *dev_priv);
+
+int intel_whitelist_workarounds_apply(struct intel_engine_cs *engine);
+
+#endif
diff --git a/drivers/gpu/drm/i915/selftests/i915_live_selftests.h b/drivers/gpu/drm/i915/selftests/i915_live_selftests.h
index 9c76f0305b6a..8bf6aa573226 100644
--- a/drivers/gpu/drm/i915/selftests/i915_live_selftests.h
+++ b/drivers/gpu/drm/i915/selftests/i915_live_selftests.h
@@ -20,4 +20,5 @@ selftest(evict, i915_gem_evict_live_selftests)
selftest(hugepages, i915_gem_huge_page_live_selftests)
selftest(contexts, i915_gem_context_live_selftests)
selftest(hangcheck, intel_hangcheck_live_selftests)
+selftest(execlists, intel_execlists_live_selftests)
selftest(guc, intel_guc_live_selftest)
diff --git a/drivers/gpu/drm/i915/selftests/i915_mock_selftests.h b/drivers/gpu/drm/i915/selftests/i915_mock_selftests.h
index 9a48aa441743..d16d74178e9d 100644
--- a/drivers/gpu/drm/i915/selftests/i915_mock_selftests.h
+++ b/drivers/gpu/drm/i915/selftests/i915_mock_selftests.h
@@ -14,6 +14,7 @@ selftest(fence, i915_sw_fence_mock_selftests)
selftest(scatterlist, scatterlist_mock_selftests)
selftest(syncmap, i915_syncmap_mock_selftests)
selftest(uncore, intel_uncore_mock_selftests)
+selftest(engine, intel_engine_cs_mock_selftests)
selftest(breadcrumbs, intel_breadcrumbs_mock_selftests)
selftest(timelines, i915_gem_timeline_mock_selftests)
selftest(requests, i915_request_mock_selftests)
diff --git a/drivers/gpu/drm/i915/selftests/intel_breadcrumbs.c b/drivers/gpu/drm/i915/selftests/intel_breadcrumbs.c
index 54fc571b1102..46580026c7fc 100644
--- a/drivers/gpu/drm/i915/selftests/intel_breadcrumbs.c
+++ b/drivers/gpu/drm/i915/selftests/intel_breadcrumbs.c
@@ -271,18 +271,13 @@ struct igt_wakeup {
u32 seqno;
};
-static int wait_atomic_timeout(atomic_t *p, unsigned int mode)
-{
- return schedule_timeout(10 * HZ) ? 0 : -ETIMEDOUT;
-}
-
static bool wait_for_ready(struct igt_wakeup *w)
{
DEFINE_WAIT(ready);
set_bit(IDLE, &w->flags);
if (atomic_dec_and_test(w->done))
- wake_up_atomic_t(w->done);
+ wake_up_var(w->done);
if (test_bit(STOP, &w->flags))
goto out;
@@ -299,7 +294,7 @@ static bool wait_for_ready(struct igt_wakeup *w)
out:
clear_bit(IDLE, &w->flags);
if (atomic_dec_and_test(w->set))
- wake_up_atomic_t(w->set);
+ wake_up_var(w->set);
return !test_bit(STOP, &w->flags);
}
@@ -342,7 +337,7 @@ static void igt_wake_all_sync(atomic_t *ready,
atomic_set(ready, 0);
wake_up_all(wq);
- wait_on_atomic_t(set, atomic_t_wait, TASK_UNINTERRUPTIBLE);
+ wait_var_event(set, !atomic_read(set));
atomic_set(ready, count);
atomic_set(done, count);
}
@@ -350,7 +345,6 @@ static void igt_wake_all_sync(atomic_t *ready,
static int igt_wakeup(void *arg)
{
I915_RND_STATE(prng);
- const int state = TASK_UNINTERRUPTIBLE;
struct intel_engine_cs *engine = arg;
struct igt_wakeup *waiters;
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
@@ -418,7 +412,7 @@ static int igt_wakeup(void *arg)
* that they are ready for the next test. We wait until all
* threads are complete and waiting for us (i.e. not a seqno).
*/
- err = wait_on_atomic_t(&done, wait_atomic_timeout, state);
+ err = wait_var_event_timeout(&done, !atomic_read(&done), 10 * HZ);
if (err) {
pr_err("Timed out waiting for %d remaining waiters\n",
atomic_read(&done));
diff --git a/drivers/gpu/drm/i915/selftests/intel_engine_cs.c b/drivers/gpu/drm/i915/selftests/intel_engine_cs.c
new file mode 100644
index 000000000000..cfaa6b296835
--- /dev/null
+++ b/drivers/gpu/drm/i915/selftests/intel_engine_cs.c
@@ -0,0 +1,58 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright © 2018 Intel Corporation
+ */
+
+#include "../i915_selftest.h"
+
+static int intel_mmio_bases_check(void *arg)
+{
+ int i, j;
+
+ for (i = 0; i < ARRAY_SIZE(intel_engines); i++) {
+ const struct engine_info *info = &intel_engines[i];
+ char name[INTEL_ENGINE_CS_MAX_NAME];
+ u8 prev = U8_MAX;
+
+ __sprint_engine_name(name, info);
+
+ for (j = 0; j < MAX_MMIO_BASES; j++) {
+ u8 gen = info->mmio_bases[j].gen;
+ u32 base = info->mmio_bases[j].base;
+
+ if (gen >= prev) {
+ pr_err("%s: %s: mmio base for gen %x "
+ "is before the one for gen %x\n",
+ __func__, name, prev, gen);
+ return -EINVAL;
+ }
+
+ if (gen == 0)
+ break;
+
+ if (!base) {
+ pr_err("%s: %s: invalid mmio base (%x) "
+ "for gen %x at entry %u\n",
+ __func__, name, base, gen, j);
+ return -EINVAL;
+ }
+
+ prev = gen;
+ }
+
+ pr_info("%s: min gen supported for %s = %d\n",
+ __func__, name, prev);
+ }
+
+ return 0;
+}
+
+int intel_engine_cs_mock_selftests(void)
+{
+ static const struct i915_subtest tests[] = {
+ SUBTEST(intel_mmio_bases_check),
+ };
+
+ return i915_subtests(tests, NULL);
+}
diff --git a/drivers/gpu/drm/i915/selftests/intel_hangcheck.c b/drivers/gpu/drm/i915/selftests/intel_hangcheck.c
index df7898c8edcb..24f913f26a7b 100644
--- a/drivers/gpu/drm/i915/selftests/intel_hangcheck.c
+++ b/drivers/gpu/drm/i915/selftests/intel_hangcheck.c
@@ -25,6 +25,7 @@
#include <linux/kthread.h>
#include "../i915_selftest.h"
+#include "i915_random.h"
#include "mock_context.h"
#include "mock_drm.h"
@@ -260,8 +261,11 @@ static void wedge_me(struct work_struct *work)
{
struct wedge_me *w = container_of(work, typeof(*w), work.work);
- pr_err("%pS timed out, cancelling all further testing.\n",
- w->symbol);
+ pr_err("%pS timed out, cancelling all further testing.\n", w->symbol);
+
+ GEM_TRACE("%pS timed out.\n", w->symbol);
+ GEM_TRACE_DUMP();
+
i915_gem_set_wedged(w->i915);
}
@@ -318,7 +322,7 @@ static void hang_fini(struct hang *h)
flush_test(h->i915, I915_WAIT_LOCKED);
}
-static bool wait_for_hang(struct hang *h, struct i915_request *rq)
+static bool wait_until_running(struct hang *h, struct i915_request *rq)
{
return !(wait_for_us(i915_seqno_passed(hws_seqno(h, rq),
rq->fence.seqno),
@@ -433,7 +437,7 @@ static int igt_global_reset(void *arg)
mutex_lock(&i915->drm.struct_mutex);
reset_count = i915_reset_count(&i915->gpu_error);
- i915_reset(i915, I915_RESET_QUIET);
+ i915_reset(i915, ALL_ENGINES, NULL);
if (i915_reset_count(&i915->gpu_error) == reset_count) {
pr_err("No GPU reset recorded!\n");
@@ -483,6 +487,8 @@ static int __igt_reset_engine(struct drm_i915_private *i915, bool active)
set_bit(I915_RESET_ENGINE + id, &i915->gpu_error.flags);
do {
+ u32 seqno = intel_engine_get_seqno(engine);
+
if (active) {
struct i915_request *rq;
@@ -498,7 +504,7 @@ static int __igt_reset_engine(struct drm_i915_private *i915, bool active)
__i915_request_add(rq, true);
mutex_unlock(&i915->drm.struct_mutex);
- if (!wait_for_hang(&h, rq)) {
+ if (!wait_until_running(&h, rq)) {
struct drm_printer p = drm_info_printer(i915->drm.dev);
pr_err("%s: Failed to start request %x, at %x\n",
@@ -511,14 +517,12 @@ static int __igt_reset_engine(struct drm_i915_private *i915, bool active)
break;
}
+ GEM_BUG_ON(!rq->global_seqno);
+ seqno = rq->global_seqno - 1;
i915_request_put(rq);
}
- engine->hangcheck.stalled = true;
- engine->hangcheck.seqno =
- intel_engine_get_seqno(engine);
-
- err = i915_reset_engine(engine, I915_RESET_QUIET);
+ err = i915_reset_engine(engine, NULL);
if (err) {
pr_err("i915_reset_engine failed\n");
break;
@@ -538,8 +542,6 @@ static int __igt_reset_engine(struct drm_i915_private *i915, bool active)
err = -EINVAL;
break;
}
-
- engine->hangcheck.stalled = false;
} while (time_before(jiffies, end_time));
clear_bit(I915_RESET_ENGINE + id, &i915->gpu_error.flags);
@@ -573,11 +575,25 @@ static int igt_reset_active_engine(void *arg)
return __igt_reset_engine(arg, true);
}
+struct active_engine {
+ struct task_struct *task;
+ struct intel_engine_cs *engine;
+ unsigned long resets;
+ unsigned int flags;
+};
+
+#define TEST_ACTIVE BIT(0)
+#define TEST_OTHERS BIT(1)
+#define TEST_SELF BIT(2)
+#define TEST_PRIORITY BIT(3)
+
static int active_engine(void *data)
{
- struct intel_engine_cs *engine = data;
- struct i915_request *rq[2] = {};
- struct i915_gem_context *ctx[2];
+ I915_RND_STATE(prng);
+ struct active_engine *arg = data;
+ struct intel_engine_cs *engine = arg->engine;
+ struct i915_request *rq[8] = {};
+ struct i915_gem_context *ctx[ARRAY_SIZE(rq)];
struct drm_file *file;
unsigned long count = 0;
int err = 0;
@@ -586,25 +602,20 @@ static int active_engine(void *data)
if (IS_ERR(file))
return PTR_ERR(file);
- mutex_lock(&engine->i915->drm.struct_mutex);
- ctx[0] = live_context(engine->i915, file);
- mutex_unlock(&engine->i915->drm.struct_mutex);
- if (IS_ERR(ctx[0])) {
- err = PTR_ERR(ctx[0]);
- goto err_file;
- }
-
- mutex_lock(&engine->i915->drm.struct_mutex);
- ctx[1] = live_context(engine->i915, file);
- mutex_unlock(&engine->i915->drm.struct_mutex);
- if (IS_ERR(ctx[1])) {
- err = PTR_ERR(ctx[1]);
- i915_gem_context_put(ctx[0]);
- goto err_file;
+ for (count = 0; count < ARRAY_SIZE(ctx); count++) {
+ mutex_lock(&engine->i915->drm.struct_mutex);
+ ctx[count] = live_context(engine->i915, file);
+ mutex_unlock(&engine->i915->drm.struct_mutex);
+ if (IS_ERR(ctx[count])) {
+ err = PTR_ERR(ctx[count]);
+ while (--count)
+ i915_gem_context_put(ctx[count]);
+ goto err_file;
+ }
}
while (!kthread_should_stop()) {
- unsigned int idx = count++ & 1;
+ unsigned int idx = count++ & (ARRAY_SIZE(rq) - 1);
struct i915_request *old = rq[idx];
struct i915_request *new;
@@ -616,14 +627,28 @@ static int active_engine(void *data)
break;
}
+ if (arg->flags & TEST_PRIORITY)
+ ctx[idx]->priority =
+ i915_prandom_u32_max_state(512, &prng);
+
rq[idx] = i915_request_get(new);
i915_request_add(new);
mutex_unlock(&engine->i915->drm.struct_mutex);
if (old) {
- i915_request_wait(old, 0, MAX_SCHEDULE_TIMEOUT);
+ if (i915_request_wait(old, 0, HZ) < 0) {
+ GEM_TRACE("%s timed out.\n", engine->name);
+ GEM_TRACE_DUMP();
+
+ i915_gem_set_wedged(engine->i915);
+ i915_request_put(old);
+ err = -EIO;
+ break;
+ }
i915_request_put(old);
}
+
+ cond_resched();
}
for (count = 0; count < ARRAY_SIZE(rq); count++)
@@ -634,8 +659,9 @@ err_file:
return err;
}
-static int __igt_reset_engine_others(struct drm_i915_private *i915,
- bool active)
+static int __igt_reset_engines(struct drm_i915_private *i915,
+ const char *test_name,
+ unsigned int flags)
{
struct intel_engine_cs *engine, *other;
enum intel_engine_id id, tmp;
@@ -649,50 +675,61 @@ static int __igt_reset_engine_others(struct drm_i915_private *i915,
if (!intel_has_reset_engine(i915))
return 0;
- if (active) {
+ if (flags & TEST_ACTIVE) {
mutex_lock(&i915->drm.struct_mutex);
err = hang_init(&h, i915);
mutex_unlock(&i915->drm.struct_mutex);
if (err)
return err;
+
+ if (flags & TEST_PRIORITY)
+ h.ctx->priority = 1024;
}
for_each_engine(engine, i915, id) {
- struct task_struct *threads[I915_NUM_ENGINES] = {};
- unsigned long resets[I915_NUM_ENGINES];
+ struct active_engine threads[I915_NUM_ENGINES] = {};
unsigned long global = i915_reset_count(&i915->gpu_error);
- unsigned long count = 0;
+ unsigned long count = 0, reported;
IGT_TIMEOUT(end_time);
- if (active && !intel_engine_can_store_dword(engine))
+ if (flags & TEST_ACTIVE &&
+ !intel_engine_can_store_dword(engine))
continue;
memset(threads, 0, sizeof(threads));
for_each_engine(other, i915, tmp) {
struct task_struct *tsk;
- resets[tmp] = i915_reset_engine_count(&i915->gpu_error,
- other);
+ threads[tmp].resets =
+ i915_reset_engine_count(&i915->gpu_error,
+ other);
- if (other == engine)
+ if (!(flags & TEST_OTHERS))
continue;
- tsk = kthread_run(active_engine, other,
+ if (other == engine && !(flags & TEST_SELF))
+ continue;
+
+ threads[tmp].engine = other;
+ threads[tmp].flags = flags;
+
+ tsk = kthread_run(active_engine, &threads[tmp],
"igt/%s", other->name);
if (IS_ERR(tsk)) {
err = PTR_ERR(tsk);
goto unwind;
}
- threads[tmp] = tsk;
+ threads[tmp].task = tsk;
get_task_struct(tsk);
}
set_bit(I915_RESET_ENGINE + id, &i915->gpu_error.flags);
do {
- if (active) {
- struct i915_request *rq;
+ u32 seqno = intel_engine_get_seqno(engine);
+ struct i915_request *rq = NULL;
+ if (flags & TEST_ACTIVE) {
mutex_lock(&i915->drm.struct_mutex);
rq = hang_create_request(&h, engine);
if (IS_ERR(rq)) {
@@ -705,7 +742,7 @@ static int __igt_reset_engine_others(struct drm_i915_private *i915,
__i915_request_add(rq, true);
mutex_unlock(&i915->drm.struct_mutex);
- if (!wait_for_hang(&h, rq)) {
+ if (!wait_until_running(&h, rq)) {
struct drm_printer p = drm_info_printer(i915->drm.dev);
pr_err("%s: Failed to start request %x, at %x\n",
@@ -718,33 +755,34 @@ static int __igt_reset_engine_others(struct drm_i915_private *i915,
break;
}
- i915_request_put(rq);
+ GEM_BUG_ON(!rq->global_seqno);
+ seqno = rq->global_seqno - 1;
}
- engine->hangcheck.stalled = true;
- engine->hangcheck.seqno =
- intel_engine_get_seqno(engine);
-
- err = i915_reset_engine(engine, I915_RESET_QUIET);
+ err = i915_reset_engine(engine, NULL);
if (err) {
- pr_err("i915_reset_engine(%s:%s) failed, err=%d\n",
- engine->name, active ? "active" : "idle", err);
+ pr_err("i915_reset_engine(%s:%s): failed, err=%d\n",
+ engine->name, test_name, err);
break;
}
- engine->hangcheck.stalled = false;
count++;
+
+ if (rq) {
+ i915_request_wait(rq, 0, MAX_SCHEDULE_TIMEOUT);
+ i915_request_put(rq);
+ }
} while (time_before(jiffies, end_time));
clear_bit(I915_RESET_ENGINE + id, &i915->gpu_error.flags);
pr_info("i915_reset_engine(%s:%s): %lu resets\n",
- engine->name, active ? "active" : "idle", count);
-
- if (i915_reset_engine_count(&i915->gpu_error, engine) -
- resets[engine->id] != (active ? count : 0)) {
- pr_err("i915_reset_engine(%s:%s): reset %lu times, but reported %lu\n",
- engine->name, active ? "active" : "idle", count,
- i915_reset_engine_count(&i915->gpu_error,
- engine) - resets[engine->id]);
+ engine->name, test_name, count);
+
+ reported = i915_reset_engine_count(&i915->gpu_error, engine);
+ reported -= threads[engine->id].resets;
+ if (reported != (flags & TEST_ACTIVE ? count : 0)) {
+ pr_err("i915_reset_engine(%s:%s): reset %lu times, but reported %lu, expected %lu reported\n",
+ engine->name, test_name, count, reported,
+ (flags & TEST_ACTIVE ? count : 0));
if (!err)
err = -EINVAL;
}
@@ -753,24 +791,26 @@ unwind:
for_each_engine(other, i915, tmp) {
int ret;
- if (!threads[tmp])
+ if (!threads[tmp].task)
continue;
- ret = kthread_stop(threads[tmp]);
+ ret = kthread_stop(threads[tmp].task);
if (ret) {
pr_err("kthread for other engine %s failed, err=%d\n",
other->name, ret);
if (!err)
err = ret;
}
- put_task_struct(threads[tmp]);
+ put_task_struct(threads[tmp].task);
- if (resets[tmp] != i915_reset_engine_count(&i915->gpu_error,
- other)) {
+ if (other != engine &&
+ threads[tmp].resets !=
+ i915_reset_engine_count(&i915->gpu_error, other)) {
pr_err("Innocent engine %s was reset (count=%ld)\n",
other->name,
i915_reset_engine_count(&i915->gpu_error,
- other) - resets[tmp]);
+ other) -
+ threads[tmp].resets);
if (!err)
err = -EINVAL;
}
@@ -794,7 +834,7 @@ unwind:
if (i915_terminally_wedged(&i915->gpu_error))
err = -EIO;
- if (active) {
+ if (flags & TEST_ACTIVE) {
mutex_lock(&i915->drm.struct_mutex);
hang_fini(&h);
mutex_unlock(&i915->drm.struct_mutex);
@@ -803,27 +843,56 @@ unwind:
return err;
}
-static int igt_reset_idle_engine_others(void *arg)
+static int igt_reset_engines(void *arg)
{
- return __igt_reset_engine_others(arg, false);
-}
+ static const struct {
+ const char *name;
+ unsigned int flags;
+ } phases[] = {
+ { "idle", 0 },
+ { "active", TEST_ACTIVE },
+ { "others-idle", TEST_OTHERS },
+ { "others-active", TEST_OTHERS | TEST_ACTIVE },
+ {
+ "others-priority",
+ TEST_OTHERS | TEST_ACTIVE | TEST_PRIORITY
+ },
+ {
+ "self-priority",
+ TEST_OTHERS | TEST_ACTIVE | TEST_PRIORITY | TEST_SELF,
+ },
+ { }
+ };
+ struct drm_i915_private *i915 = arg;
+ typeof(*phases) *p;
+ int err;
-static int igt_reset_active_engine_others(void *arg)
-{
- return __igt_reset_engine_others(arg, true);
+ for (p = phases; p->name; p++) {
+ if (p->flags & TEST_PRIORITY) {
+ if (!(i915->caps.scheduler & I915_SCHEDULER_CAP_PRIORITY))
+ continue;
+ }
+
+ err = __igt_reset_engines(arg, p->name, p->flags);
+ if (err)
+ return err;
+ }
+
+ return 0;
}
-static u32 fake_hangcheck(struct i915_request *rq)
+static u32 fake_hangcheck(struct i915_request *rq, u32 mask)
{
- u32 reset_count;
+ struct i915_gpu_error *error = &rq->i915->gpu_error;
+ u32 reset_count = i915_reset_count(error);
- rq->engine->hangcheck.stalled = true;
- rq->engine->hangcheck.seqno = intel_engine_get_seqno(rq->engine);
+ error->stalled_mask = mask;
- reset_count = i915_reset_count(&rq->i915->gpu_error);
+ /* set_bit() must be after we have setup the backchannel (mask) */
+ smp_mb__before_atomic();
+ set_bit(I915_RESET_HANDOFF, &error->flags);
- set_bit(I915_RESET_HANDOFF, &rq->i915->gpu_error.flags);
- wake_up_all(&rq->i915->gpu_error.wait_queue);
+ wake_up_all(&error->wait_queue);
return reset_count;
}
@@ -858,21 +927,20 @@ static int igt_wait_reset(void *arg)
i915_request_get(rq);
__i915_request_add(rq, true);
- if (!wait_for_hang(&h, rq)) {
+ if (!wait_until_running(&h, rq)) {
struct drm_printer p = drm_info_printer(i915->drm.dev);
pr_err("%s: Failed to start request %x, at %x\n",
__func__, rq->fence.seqno, hws_seqno(&h, rq));
intel_engine_dump(rq->engine, &p, "%s\n", rq->engine->name);
- i915_reset(i915, 0);
i915_gem_set_wedged(i915);
err = -EIO;
goto out_rq;
}
- reset_count = fake_hangcheck(rq);
+ reset_count = fake_hangcheck(rq, ALL_ENGINES);
timeout = i915_request_wait(rq, I915_WAIT_LOCKED, 10);
if (timeout < 0) {
@@ -903,6 +971,23 @@ unlock:
return err;
}
+static int wait_for_others(struct drm_i915_private *i915,
+ struct intel_engine_cs *exclude)
+{
+ struct intel_engine_cs *engine;
+ enum intel_engine_id id;
+
+ for_each_engine(engine, i915, id) {
+ if (engine == exclude)
+ continue;
+
+ if (wait_for(intel_engine_is_idle(engine), 10))
+ return -EIO;
+ }
+
+ return 0;
+}
+
static int igt_reset_queue(void *arg)
{
struct drm_i915_private *i915 = arg;
@@ -951,27 +1036,49 @@ static int igt_reset_queue(void *arg)
i915_request_get(rq);
__i915_request_add(rq, true);
- if (!wait_for_hang(&h, prev)) {
+ /*
+ * XXX We don't handle resetting the kernel context
+ * very well. If we trigger a device reset twice in
+ * quick succession while the kernel context is
+ * executing, we may end up skipping the breadcrumb.
+ * This is really only a problem for the selftest as
+ * normally there is a large interlude between resets
+ * (hangcheck), or we focus on resetting just one
+ * engine and so avoid repeatedly resetting innocents.
+ */
+ err = wait_for_others(i915, engine);
+ if (err) {
+ pr_err("%s(%s): Failed to idle other inactive engines after device reset\n",
+ __func__, engine->name);
+ i915_request_put(rq);
+ i915_request_put(prev);
+
+ GEM_TRACE_DUMP();
+ i915_gem_set_wedged(i915);
+ goto fini;
+ }
+
+ if (!wait_until_running(&h, prev)) {
struct drm_printer p = drm_info_printer(i915->drm.dev);
- pr_err("%s: Failed to start request %x, at %x\n",
- __func__, prev->fence.seqno, hws_seqno(&h, prev));
- intel_engine_dump(prev->engine, &p,
- "%s\n", prev->engine->name);
+ pr_err("%s(%s): Failed to start request %x, at %x\n",
+ __func__, engine->name,
+ prev->fence.seqno, hws_seqno(&h, prev));
+ intel_engine_dump(engine, &p,
+ "%s\n", engine->name);
i915_request_put(rq);
i915_request_put(prev);
- i915_reset(i915, 0);
i915_gem_set_wedged(i915);
err = -EIO;
goto fini;
}
- reset_count = fake_hangcheck(prev);
+ reset_count = fake_hangcheck(prev, ENGINE_MASK(id));
- i915_reset(i915, I915_RESET_QUIET);
+ i915_reset(i915, ENGINE_MASK(id), NULL);
GEM_BUG_ON(test_bit(I915_RESET_HANDOFF,
&i915->gpu_error.flags));
@@ -1044,7 +1151,7 @@ static int igt_handle_error(void *arg)
if (!intel_has_reset_engine(i915))
return 0;
- if (!intel_engine_can_store_dword(i915->engine[RCS]))
+ if (!engine || !intel_engine_can_store_dword(engine))
return 0;
mutex_lock(&i915->drm.struct_mutex);
@@ -1062,14 +1169,13 @@ static int igt_handle_error(void *arg)
i915_request_get(rq);
__i915_request_add(rq, true);
- if (!wait_for_hang(&h, rq)) {
+ if (!wait_until_running(&h, rq)) {
struct drm_printer p = drm_info_printer(i915->drm.dev);
pr_err("%s: Failed to start request %x, at %x\n",
__func__, rq->fence.seqno, hws_seqno(&h, rq));
intel_engine_dump(rq->engine, &p, "%s\n", rq->engine->name);
- i915_reset(i915, 0);
i915_gem_set_wedged(i915);
err = -EIO;
@@ -1081,10 +1187,7 @@ static int igt_handle_error(void *arg)
/* Temporarily disable error capture */
error = xchg(&i915->gpu_error.first_error, (void *)-1);
- engine->hangcheck.stalled = true;
- engine->hangcheck.seqno = intel_engine_get_seqno(engine);
-
- i915_handle_error(i915, intel_engine_flag(engine), "%s", __func__);
+ i915_handle_error(i915, ENGINE_MASK(engine->id), 0, NULL);
xchg(&i915->gpu_error.first_error, error);
@@ -1112,8 +1215,7 @@ int intel_hangcheck_live_selftests(struct drm_i915_private *i915)
SUBTEST(igt_hang_sanitycheck),
SUBTEST(igt_reset_idle_engine),
SUBTEST(igt_reset_active_engine),
- SUBTEST(igt_reset_idle_engine_others),
- SUBTEST(igt_reset_active_engine_others),
+ SUBTEST(igt_reset_engines),
SUBTEST(igt_wait_reset),
SUBTEST(igt_reset_queue),
SUBTEST(igt_handle_error),
@@ -1129,6 +1231,10 @@ int intel_hangcheck_live_selftests(struct drm_i915_private *i915)
err = i915_subtests(tests, i915);
+ mutex_lock(&i915->drm.struct_mutex);
+ flush_test(i915, I915_WAIT_LOCKED);
+ mutex_unlock(&i915->drm.struct_mutex);
+
i915_modparams.enable_hangcheck = saved_hangcheck;
intel_runtime_pm_put(i915);
diff --git a/drivers/gpu/drm/i915/selftests/intel_lrc.c b/drivers/gpu/drm/i915/selftests/intel_lrc.c
new file mode 100644
index 000000000000..0481e2e01146
--- /dev/null
+++ b/drivers/gpu/drm/i915/selftests/intel_lrc.c
@@ -0,0 +1,507 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright © 2018 Intel Corporation
+ */
+
+#include "../i915_selftest.h"
+
+#include "mock_context.h"
+
+struct spinner {
+ struct drm_i915_private *i915;
+ struct drm_i915_gem_object *hws;
+ struct drm_i915_gem_object *obj;
+ u32 *batch;
+ void *seqno;
+};
+
+static int spinner_init(struct spinner *spin, struct drm_i915_private *i915)
+{
+ unsigned int mode;
+ void *vaddr;
+ int err;
+
+ GEM_BUG_ON(INTEL_GEN(i915) < 8);
+
+ memset(spin, 0, sizeof(*spin));
+ spin->i915 = i915;
+
+ spin->hws = i915_gem_object_create_internal(i915, PAGE_SIZE);
+ if (IS_ERR(spin->hws)) {
+ err = PTR_ERR(spin->hws);
+ goto err;
+ }
+
+ spin->obj = i915_gem_object_create_internal(i915, PAGE_SIZE);
+ if (IS_ERR(spin->obj)) {
+ err = PTR_ERR(spin->obj);
+ goto err_hws;
+ }
+
+ i915_gem_object_set_cache_level(spin->hws, I915_CACHE_LLC);
+ vaddr = i915_gem_object_pin_map(spin->hws, I915_MAP_WB);
+ if (IS_ERR(vaddr)) {
+ err = PTR_ERR(vaddr);
+ goto err_obj;
+ }
+ spin->seqno = memset(vaddr, 0xff, PAGE_SIZE);
+
+ mode = HAS_LLC(i915) ? I915_MAP_WB : I915_MAP_WC;
+ vaddr = i915_gem_object_pin_map(spin->obj, mode);
+ if (IS_ERR(vaddr)) {
+ err = PTR_ERR(vaddr);
+ goto err_unpin_hws;
+ }
+ spin->batch = vaddr;
+
+ return 0;
+
+err_unpin_hws:
+ i915_gem_object_unpin_map(spin->hws);
+err_obj:
+ i915_gem_object_put(spin->obj);
+err_hws:
+ i915_gem_object_put(spin->hws);
+err:
+ return err;
+}
+
+static unsigned int seqno_offset(u64 fence)
+{
+ return offset_in_page(sizeof(u32) * fence);
+}
+
+static u64 hws_address(const struct i915_vma *hws,
+ const struct i915_request *rq)
+{
+ return hws->node.start + seqno_offset(rq->fence.context);
+}
+
+static int emit_recurse_batch(struct spinner *spin,
+ struct i915_request *rq,
+ u32 arbitration_command)
+{
+ struct i915_address_space *vm = &rq->ctx->ppgtt->base;
+ struct i915_vma *hws, *vma;
+ u32 *batch;
+ int err;
+
+ vma = i915_vma_instance(spin->obj, vm, NULL);
+ if (IS_ERR(vma))
+ return PTR_ERR(vma);
+
+ hws = i915_vma_instance(spin->hws, vm, NULL);
+ if (IS_ERR(hws))
+ return PTR_ERR(hws);
+
+ err = i915_vma_pin(vma, 0, 0, PIN_USER);
+ if (err)
+ return err;
+
+ err = i915_vma_pin(hws, 0, 0, PIN_USER);
+ if (err)
+ goto unpin_vma;
+
+ i915_vma_move_to_active(vma, rq, 0);
+ if (!i915_gem_object_has_active_reference(vma->obj)) {
+ i915_gem_object_get(vma->obj);
+ i915_gem_object_set_active_reference(vma->obj);
+ }
+
+ i915_vma_move_to_active(hws, rq, 0);
+ if (!i915_gem_object_has_active_reference(hws->obj)) {
+ i915_gem_object_get(hws->obj);
+ i915_gem_object_set_active_reference(hws->obj);
+ }
+
+ batch = spin->batch;
+
+ *batch++ = MI_STORE_DWORD_IMM_GEN4;
+ *batch++ = lower_32_bits(hws_address(hws, rq));
+ *batch++ = upper_32_bits(hws_address(hws, rq));
+ *batch++ = rq->fence.seqno;
+
+ *batch++ = arbitration_command;
+
+ *batch++ = MI_BATCH_BUFFER_START | 1 << 8 | 1;
+ *batch++ = lower_32_bits(vma->node.start);
+ *batch++ = upper_32_bits(vma->node.start);
+ *batch++ = MI_BATCH_BUFFER_END; /* not reached */
+
+ i915_gem_chipset_flush(spin->i915);
+
+ err = rq->engine->emit_bb_start(rq, vma->node.start, PAGE_SIZE, 0);
+
+ i915_vma_unpin(hws);
+unpin_vma:
+ i915_vma_unpin(vma);
+ return err;
+}
+
+static struct i915_request *
+spinner_create_request(struct spinner *spin,
+ struct i915_gem_context *ctx,
+ struct intel_engine_cs *engine,
+ u32 arbitration_command)
+{
+ struct i915_request *rq;
+ int err;
+
+ rq = i915_request_alloc(engine, ctx);
+ if (IS_ERR(rq))
+ return rq;
+
+ err = emit_recurse_batch(spin, rq, arbitration_command);
+ if (err) {
+ __i915_request_add(rq, false);
+ return ERR_PTR(err);
+ }
+
+ return rq;
+}
+
+static u32 hws_seqno(const struct spinner *spin, const struct i915_request *rq)
+{
+ u32 *seqno = spin->seqno + seqno_offset(rq->fence.context);
+
+ return READ_ONCE(*seqno);
+}
+
+struct wedge_me {
+ struct delayed_work work;
+ struct drm_i915_private *i915;
+ const void *symbol;
+};
+
+static void wedge_me(struct work_struct *work)
+{
+ struct wedge_me *w = container_of(work, typeof(*w), work.work);
+
+ pr_err("%pS timed out, cancelling all further testing.\n", w->symbol);
+
+ GEM_TRACE("%pS timed out.\n", w->symbol);
+ GEM_TRACE_DUMP();
+
+ i915_gem_set_wedged(w->i915);
+}
+
+static void __init_wedge(struct wedge_me *w,
+ struct drm_i915_private *i915,
+ long timeout,
+ const void *symbol)
+{
+ w->i915 = i915;
+ w->symbol = symbol;
+
+ INIT_DELAYED_WORK_ONSTACK(&w->work, wedge_me);
+ schedule_delayed_work(&w->work, timeout);
+}
+
+static void __fini_wedge(struct wedge_me *w)
+{
+ cancel_delayed_work_sync(&w->work);
+ destroy_delayed_work_on_stack(&w->work);
+ w->i915 = NULL;
+}
+
+#define wedge_on_timeout(W, DEV, TIMEOUT) \
+ for (__init_wedge((W), (DEV), (TIMEOUT), __builtin_return_address(0)); \
+ (W)->i915; \
+ __fini_wedge((W)))
+
+static noinline int
+flush_test(struct drm_i915_private *i915, unsigned int flags)
+{
+ struct wedge_me w;
+
+ cond_resched();
+
+ wedge_on_timeout(&w, i915, HZ)
+ i915_gem_wait_for_idle(i915, flags);
+
+ return i915_terminally_wedged(&i915->gpu_error) ? -EIO : 0;
+}
+
+static void spinner_end(struct spinner *spin)
+{
+ *spin->batch = MI_BATCH_BUFFER_END;
+ i915_gem_chipset_flush(spin->i915);
+}
+
+static void spinner_fini(struct spinner *spin)
+{
+ spinner_end(spin);
+
+ i915_gem_object_unpin_map(spin->obj);
+ i915_gem_object_put(spin->obj);
+
+ i915_gem_object_unpin_map(spin->hws);
+ i915_gem_object_put(spin->hws);
+}
+
+static bool wait_for_spinner(struct spinner *spin, struct i915_request *rq)
+{
+ if (!wait_event_timeout(rq->execute,
+ READ_ONCE(rq->global_seqno),
+ msecs_to_jiffies(10)))
+ return false;
+
+ return !(wait_for_us(i915_seqno_passed(hws_seqno(spin, rq),
+ rq->fence.seqno),
+ 10) &&
+ wait_for(i915_seqno_passed(hws_seqno(spin, rq),
+ rq->fence.seqno),
+ 1000));
+}
+
+static int live_sanitycheck(void *arg)
+{
+ struct drm_i915_private *i915 = arg;
+ struct intel_engine_cs *engine;
+ struct i915_gem_context *ctx;
+ enum intel_engine_id id;
+ struct spinner spin;
+ int err = -ENOMEM;
+
+ if (!HAS_LOGICAL_RING_CONTEXTS(i915))
+ return 0;
+
+ mutex_lock(&i915->drm.struct_mutex);
+
+ if (spinner_init(&spin, i915))
+ goto err_unlock;
+
+ ctx = kernel_context(i915);
+ if (!ctx)
+ goto err_spin;
+
+ for_each_engine(engine, i915, id) {
+ struct i915_request *rq;
+
+ rq = spinner_create_request(&spin, ctx, engine, MI_NOOP);
+ if (IS_ERR(rq)) {
+ err = PTR_ERR(rq);
+ goto err_ctx;
+ }
+
+ i915_request_add(rq);
+ if (!wait_for_spinner(&spin, rq)) {
+ GEM_TRACE("spinner failed to start\n");
+ GEM_TRACE_DUMP();
+ i915_gem_set_wedged(i915);
+ err = -EIO;
+ goto err_ctx;
+ }
+
+ spinner_end(&spin);
+ if (flush_test(i915, I915_WAIT_LOCKED)) {
+ err = -EIO;
+ goto err_ctx;
+ }
+ }
+
+ err = 0;
+err_ctx:
+ kernel_context_close(ctx);
+err_spin:
+ spinner_fini(&spin);
+err_unlock:
+ flush_test(i915, I915_WAIT_LOCKED);
+ mutex_unlock(&i915->drm.struct_mutex);
+ return err;
+}
+
+static int live_preempt(void *arg)
+{
+ struct drm_i915_private *i915 = arg;
+ struct i915_gem_context *ctx_hi, *ctx_lo;
+ struct spinner spin_hi, spin_lo;
+ struct intel_engine_cs *engine;
+ enum intel_engine_id id;
+ int err = -ENOMEM;
+
+ if (!HAS_LOGICAL_RING_PREEMPTION(i915))
+ return 0;
+
+ mutex_lock(&i915->drm.struct_mutex);
+
+ if (spinner_init(&spin_hi, i915))
+ goto err_unlock;
+
+ if (spinner_init(&spin_lo, i915))
+ goto err_spin_hi;
+
+ ctx_hi = kernel_context(i915);
+ if (!ctx_hi)
+ goto err_spin_lo;
+ ctx_hi->priority = I915_CONTEXT_MAX_USER_PRIORITY;
+
+ ctx_lo = kernel_context(i915);
+ if (!ctx_lo)
+ goto err_ctx_hi;
+ ctx_lo->priority = I915_CONTEXT_MIN_USER_PRIORITY;
+
+ for_each_engine(engine, i915, id) {
+ struct i915_request *rq;
+
+ rq = spinner_create_request(&spin_lo, ctx_lo, engine,
+ MI_ARB_CHECK);
+ if (IS_ERR(rq)) {
+ err = PTR_ERR(rq);
+ goto err_ctx_lo;
+ }
+
+ i915_request_add(rq);
+ if (!wait_for_spinner(&spin_lo, rq)) {
+ GEM_TRACE("lo spinner failed to start\n");
+ GEM_TRACE_DUMP();
+ i915_gem_set_wedged(i915);
+ err = -EIO;
+ goto err_ctx_lo;
+ }
+
+ rq = spinner_create_request(&spin_hi, ctx_hi, engine,
+ MI_ARB_CHECK);
+ if (IS_ERR(rq)) {
+ spinner_end(&spin_lo);
+ err = PTR_ERR(rq);
+ goto err_ctx_lo;
+ }
+
+ i915_request_add(rq);
+ if (!wait_for_spinner(&spin_hi, rq)) {
+ GEM_TRACE("hi spinner failed to start\n");
+ GEM_TRACE_DUMP();
+ i915_gem_set_wedged(i915);
+ err = -EIO;
+ goto err_ctx_lo;
+ }
+
+ spinner_end(&spin_hi);
+ spinner_end(&spin_lo);
+ if (flush_test(i915, I915_WAIT_LOCKED)) {
+ err = -EIO;
+ goto err_ctx_lo;
+ }
+ }
+
+ err = 0;
+err_ctx_lo:
+ kernel_context_close(ctx_lo);
+err_ctx_hi:
+ kernel_context_close(ctx_hi);
+err_spin_lo:
+ spinner_fini(&spin_lo);
+err_spin_hi:
+ spinner_fini(&spin_hi);
+err_unlock:
+ flush_test(i915, I915_WAIT_LOCKED);
+ mutex_unlock(&i915->drm.struct_mutex);
+ return err;
+}
+
+static int live_late_preempt(void *arg)
+{
+ struct drm_i915_private *i915 = arg;
+ struct i915_gem_context *ctx_hi, *ctx_lo;
+ struct spinner spin_hi, spin_lo;
+ struct intel_engine_cs *engine;
+ enum intel_engine_id id;
+ int err = -ENOMEM;
+
+ if (!HAS_LOGICAL_RING_PREEMPTION(i915))
+ return 0;
+
+ mutex_lock(&i915->drm.struct_mutex);
+
+ if (spinner_init(&spin_hi, i915))
+ goto err_unlock;
+
+ if (spinner_init(&spin_lo, i915))
+ goto err_spin_hi;
+
+ ctx_hi = kernel_context(i915);
+ if (!ctx_hi)
+ goto err_spin_lo;
+
+ ctx_lo = kernel_context(i915);
+ if (!ctx_lo)
+ goto err_ctx_hi;
+
+ for_each_engine(engine, i915, id) {
+ struct i915_request *rq;
+
+ rq = spinner_create_request(&spin_lo, ctx_lo, engine,
+ MI_ARB_CHECK);
+ if (IS_ERR(rq)) {
+ err = PTR_ERR(rq);
+ goto err_ctx_lo;
+ }
+
+ i915_request_add(rq);
+ if (!wait_for_spinner(&spin_lo, rq)) {
+ pr_err("First context failed to start\n");
+ goto err_wedged;
+ }
+
+ rq = spinner_create_request(&spin_hi, ctx_hi, engine, MI_NOOP);
+ if (IS_ERR(rq)) {
+ spinner_end(&spin_lo);
+ err = PTR_ERR(rq);
+ goto err_ctx_lo;
+ }
+
+ i915_request_add(rq);
+ if (wait_for_spinner(&spin_hi, rq)) {
+ pr_err("Second context overtook first?\n");
+ goto err_wedged;
+ }
+
+ engine->schedule(rq, I915_PRIORITY_MAX);
+
+ if (!wait_for_spinner(&spin_hi, rq)) {
+ pr_err("High priority context failed to preempt the low priority context\n");
+ GEM_TRACE_DUMP();
+ goto err_wedged;
+ }
+
+ spinner_end(&spin_hi);
+ spinner_end(&spin_lo);
+ if (flush_test(i915, I915_WAIT_LOCKED)) {
+ err = -EIO;
+ goto err_ctx_lo;
+ }
+ }
+
+ err = 0;
+err_ctx_lo:
+ kernel_context_close(ctx_lo);
+err_ctx_hi:
+ kernel_context_close(ctx_hi);
+err_spin_lo:
+ spinner_fini(&spin_lo);
+err_spin_hi:
+ spinner_fini(&spin_hi);
+err_unlock:
+ flush_test(i915, I915_WAIT_LOCKED);
+ mutex_unlock(&i915->drm.struct_mutex);
+ return err;
+
+err_wedged:
+ spinner_end(&spin_hi);
+ spinner_end(&spin_lo);
+ i915_gem_set_wedged(i915);
+ err = -EIO;
+ goto err_ctx_lo;
+}
+
+int intel_execlists_live_selftests(struct drm_i915_private *i915)
+{
+ static const struct i915_subtest tests[] = {
+ SUBTEST(live_sanitycheck),
+ SUBTEST(live_preempt),
+ SUBTEST(live_late_preempt),
+ };
+ return i915_subtests(tests, i915);
+}
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
index a4f68affc13b..d39400e5bc42 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
@@ -89,14 +89,14 @@ static int zap_shader_load_mdt(struct msm_gpu *gpu, const char *fwname)
*/
if (to_adreno_gpu(gpu)->fwloc == FW_LOCATION_LEGACY) {
ret = qcom_mdt_load(dev, fw, fwname, GPU_PAS_ID,
- mem_region, mem_phys, mem_size);
+ mem_region, mem_phys, mem_size, NULL);
} else {
char newname[strlen("qcom/") + strlen(fwname) + 1];
sprintf(newname, "qcom/%s", fwname);
ret = qcom_mdt_load(dev, fw, newname, GPU_PAS_ID,
- mem_region, mem_phys, mem_size);
+ mem_region, mem_phys, mem_size, NULL);
}
if (ret)
goto out;
diff --git a/drivers/gpu/drm/msm/disp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/disp/mdp4/mdp4_crtc.c
index 6e5e1aa54ce1..b001699297c4 100644
--- a/drivers/gpu/drm/msm/disp/mdp4/mdp4_crtc.c
+++ b/drivers/gpu/drm/msm/disp/mdp4/mdp4_crtc.c
@@ -351,6 +351,7 @@ static void mdp4_crtc_atomic_flush(struct drm_crtc *crtc,
spin_lock_irqsave(&dev->event_lock, flags);
mdp4_crtc->event = crtc->state->event;
+ crtc->state->event = NULL;
spin_unlock_irqrestore(&dev->event_lock, flags);
blend_setup(crtc);
diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c
index 9893e43ba6c5..76b96081916f 100644
--- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c
+++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c
@@ -708,6 +708,7 @@ static void mdp5_crtc_atomic_flush(struct drm_crtc *crtc,
spin_lock_irqsave(&dev->event_lock, flags);
mdp5_crtc->event = crtc->state->event;
+ crtc->state->event = NULL;
spin_unlock_irqrestore(&dev->event_lock, flags);
/*
diff --git a/drivers/gpu/drm/msm/disp/mdp_format.c b/drivers/gpu/drm/msm/disp/mdp_format.c
index b4a8aa4490ee..005760bee708 100644
--- a/drivers/gpu/drm/msm/disp/mdp_format.c
+++ b/drivers/gpu/drm/msm/disp/mdp_format.c
@@ -171,7 +171,8 @@ uint32_t mdp_get_formats(uint32_t *pixel_formats, uint32_t max_formats,
return i;
}
-const struct msm_format *mdp_get_format(struct msm_kms *kms, uint32_t format)
+const struct msm_format *mdp_get_format(struct msm_kms *kms, uint32_t format,
+ uint64_t modifier)
{
int i;
for (i = 0; i < ARRAY_SIZE(formats); i++) {
diff --git a/drivers/gpu/drm/msm/disp/mdp_kms.h b/drivers/gpu/drm/msm/disp/mdp_kms.h
index 1185487e7e5e..4fa8dbe4e165 100644
--- a/drivers/gpu/drm/msm/disp/mdp_kms.h
+++ b/drivers/gpu/drm/msm/disp/mdp_kms.h
@@ -98,7 +98,7 @@ struct mdp_format {
#define MDP_FORMAT_IS_YUV(mdp_format) ((mdp_format)->is_yuv)
uint32_t mdp_get_formats(uint32_t *formats, uint32_t max_formats, bool rgb_only);
-const struct msm_format *mdp_get_format(struct msm_kms *kms, uint32_t format);
+const struct msm_format *mdp_get_format(struct msm_kms *kms, uint32_t format, uint64_t modifier);
/* MDP capabilities */
#define MDP_CAP_SMP BIT(0) /* Shared Memory Pool */
diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c
index 7a03a9489708..8baba30d6c65 100644
--- a/drivers/gpu/drm/msm/dsi/dsi_host.c
+++ b/drivers/gpu/drm/msm/dsi/dsi_host.c
@@ -173,6 +173,7 @@ struct msm_dsi_host {
bool registered;
bool power_on;
+ bool enabled;
int irq;
};
@@ -775,7 +776,7 @@ static inline enum dsi_cmd_dst_format dsi_get_cmd_fmt(
switch (mipi_fmt) {
case MIPI_DSI_FMT_RGB888: return CMD_DST_FORMAT_RGB888;
case MIPI_DSI_FMT_RGB666_PACKED:
- case MIPI_DSI_FMT_RGB666: return VID_DST_FORMAT_RGB666;
+ case MIPI_DSI_FMT_RGB666: return CMD_DST_FORMAT_RGB666;
case MIPI_DSI_FMT_RGB565: return CMD_DST_FORMAT_RGB565;
default: return CMD_DST_FORMAT_RGB888;
}
@@ -986,13 +987,19 @@ static void dsi_set_tx_power_mode(int mode, struct msm_dsi_host *msm_host)
static void dsi_wait4video_done(struct msm_dsi_host *msm_host)
{
+ u32 ret = 0;
+ struct device *dev = &msm_host->pdev->dev;
+
dsi_intr_ctrl(msm_host, DSI_IRQ_MASK_VIDEO_DONE, 1);
reinit_completion(&msm_host->video_comp);
- wait_for_completion_timeout(&msm_host->video_comp,
+ ret = wait_for_completion_timeout(&msm_host->video_comp,
msecs_to_jiffies(70));
+ if (ret <= 0)
+ dev_err(dev, "wait for video done timed out\n");
+
dsi_intr_ctrl(msm_host, DSI_IRQ_MASK_VIDEO_DONE, 0);
}
@@ -1001,7 +1008,7 @@ static void dsi_wait4video_eng_busy(struct msm_dsi_host *msm_host)
if (!(msm_host->mode_flags & MIPI_DSI_MODE_VIDEO))
return;
- if (msm_host->power_on) {
+ if (msm_host->power_on && msm_host->enabled) {
dsi_wait4video_done(msm_host);
/* delay 4 ms to skip BLLP */
usleep_range(2000, 4000);
@@ -2203,7 +2210,7 @@ int msm_dsi_host_enable(struct mipi_dsi_host *host)
* pm_runtime_put_autosuspend(&msm_host->pdev->dev);
* }
*/
-
+ msm_host->enabled = true;
return 0;
}
@@ -2211,6 +2218,7 @@ int msm_dsi_host_disable(struct mipi_dsi_host *host)
{
struct msm_dsi_host *msm_host = to_msm_dsi_host(host);
+ msm_host->enabled = false;
dsi_op_mode_config(msm_host,
!!(msm_host->mode_flags & MIPI_DSI_MODE_VIDEO), false);
diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c
index 8e9d5c255820..9a9fa0c75a13 100644
--- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c
+++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c
@@ -265,6 +265,115 @@ int msm_dsi_dphy_timing_calc_v2(struct msm_dsi_dphy_timing *timing,
return 0;
}
+int msm_dsi_dphy_timing_calc_v3(struct msm_dsi_dphy_timing *timing,
+ struct msm_dsi_phy_clk_request *clk_req)
+{
+ const unsigned long bit_rate = clk_req->bitclk_rate;
+ const unsigned long esc_rate = clk_req->escclk_rate;
+ s32 ui, ui_x8, lpx;
+ s32 tmax, tmin;
+ s32 pcnt0 = 50;
+ s32 pcnt1 = 50;
+ s32 pcnt2 = 10;
+ s32 pcnt3 = 30;
+ s32 pcnt4 = 10;
+ s32 pcnt5 = 2;
+ s32 coeff = 1000; /* Precision, should avoid overflow */
+ s32 hb_en, hb_en_ckln;
+ s32 temp;
+
+ if (!bit_rate || !esc_rate)
+ return -EINVAL;
+
+ timing->hs_halfbyte_en = 0;
+ hb_en = 0;
+ timing->hs_halfbyte_en_ckln = 0;
+ hb_en_ckln = 0;
+
+ ui = mult_frac(NSEC_PER_MSEC, coeff, bit_rate / 1000);
+ ui_x8 = ui << 3;
+ lpx = mult_frac(NSEC_PER_MSEC, coeff, esc_rate / 1000);
+
+ temp = S_DIV_ROUND_UP(38 * coeff, ui_x8);
+ tmin = max_t(s32, temp, 0);
+ temp = (95 * coeff) / ui_x8;
+ tmax = max_t(s32, temp, 0);
+ timing->clk_prepare = linear_inter(tmax, tmin, pcnt0, 0, false);
+
+ temp = 300 * coeff - (timing->clk_prepare << 3) * ui;
+ tmin = S_DIV_ROUND_UP(temp, ui_x8) - 1;
+ tmax = (tmin > 255) ? 511 : 255;
+ timing->clk_zero = linear_inter(tmax, tmin, pcnt5, 0, false);
+
+ tmin = DIV_ROUND_UP(60 * coeff + 3 * ui, ui_x8);
+ temp = 105 * coeff + 12 * ui - 20 * coeff;
+ tmax = (temp + 3 * ui) / ui_x8;
+ timing->clk_trail = linear_inter(tmax, tmin, pcnt3, 0, false);
+
+ temp = S_DIV_ROUND_UP(40 * coeff + 4 * ui, ui_x8);
+ tmin = max_t(s32, temp, 0);
+ temp = (85 * coeff + 6 * ui) / ui_x8;
+ tmax = max_t(s32, temp, 0);
+ timing->hs_prepare = linear_inter(tmax, tmin, pcnt1, 0, false);
+
+ temp = 145 * coeff + 10 * ui - (timing->hs_prepare << 3) * ui;
+ tmin = S_DIV_ROUND_UP(temp, ui_x8) - 1;
+ tmax = 255;
+ timing->hs_zero = linear_inter(tmax, tmin, pcnt4, 0, false);
+
+ tmin = DIV_ROUND_UP(60 * coeff + 4 * ui, ui_x8) - 1;
+ temp = 105 * coeff + 12 * ui - 20 * coeff;
+ tmax = (temp / ui_x8) - 1;
+ timing->hs_trail = linear_inter(tmax, tmin, pcnt3, 0, false);
+
+ temp = 50 * coeff + ((hb_en << 2) - 8) * ui;
+ timing->hs_rqst = S_DIV_ROUND_UP(temp, ui_x8);
+
+ tmin = DIV_ROUND_UP(100 * coeff, ui_x8) - 1;
+ tmax = 255;
+ timing->hs_exit = linear_inter(tmax, tmin, pcnt2, 0, false);
+
+ temp = 50 * coeff + ((hb_en_ckln << 2) - 8) * ui;
+ timing->hs_rqst_ckln = S_DIV_ROUND_UP(temp, ui_x8);
+
+ temp = 60 * coeff + 52 * ui - 43 * ui;
+ tmin = DIV_ROUND_UP(temp, ui_x8) - 1;
+ tmax = 63;
+ timing->shared_timings.clk_post =
+ linear_inter(tmax, tmin, pcnt2, 0, false);
+
+ temp = 8 * ui + (timing->clk_prepare << 3) * ui;
+ temp += (((timing->clk_zero + 3) << 3) + 11) * ui;
+ temp += hb_en_ckln ? (((timing->hs_rqst_ckln << 3) + 4) * ui) :
+ (((timing->hs_rqst_ckln << 3) + 8) * ui);
+ tmin = S_DIV_ROUND_UP(temp, ui_x8) - 1;
+ tmax = 63;
+ if (tmin > tmax) {
+ temp = linear_inter(tmax << 1, tmin, pcnt2, 0, false);
+ timing->shared_timings.clk_pre = temp >> 1;
+ timing->shared_timings.clk_pre_inc_by_2 = 1;
+ } else {
+ timing->shared_timings.clk_pre =
+ linear_inter(tmax, tmin, pcnt2, 0, false);
+ timing->shared_timings.clk_pre_inc_by_2 = 0;
+ }
+
+ timing->ta_go = 3;
+ timing->ta_sure = 0;
+ timing->ta_get = 4;
+
+ DBG("%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d",
+ timing->shared_timings.clk_pre, timing->shared_timings.clk_post,
+ timing->shared_timings.clk_pre_inc_by_2, timing->clk_zero,
+ timing->clk_trail, timing->clk_prepare, timing->hs_exit,
+ timing->hs_zero, timing->hs_prepare, timing->hs_trail,
+ timing->hs_rqst, timing->hs_rqst_ckln, timing->hs_halfbyte_en,
+ timing->hs_halfbyte_en_ckln, timing->hs_prep_dly,
+ timing->hs_prep_dly_ckln);
+
+ return 0;
+}
+
void msm_dsi_phy_set_src_pll(struct msm_dsi_phy *phy, int pll_id, u32 reg,
u32 bit_mask)
{
diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h
index c56268cbdb3d..a24ab80994a3 100644
--- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h
+++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h
@@ -101,6 +101,8 @@ int msm_dsi_dphy_timing_calc(struct msm_dsi_dphy_timing *timing,
struct msm_dsi_phy_clk_request *clk_req);
int msm_dsi_dphy_timing_calc_v2(struct msm_dsi_dphy_timing *timing,
struct msm_dsi_phy_clk_request *clk_req);
+int msm_dsi_dphy_timing_calc_v3(struct msm_dsi_dphy_timing *timing,
+ struct msm_dsi_phy_clk_request *clk_req);
void msm_dsi_phy_set_src_pll(struct msm_dsi_phy *phy, int pll_id, u32 reg,
u32 bit_mask);
int msm_dsi_phy_init_common(struct msm_dsi_phy *phy);
diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_10nm.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_10nm.c
index 0af951aaeea1..b3fffc8dbb2a 100644
--- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_10nm.c
+++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_10nm.c
@@ -79,34 +79,6 @@ static void dsi_phy_hw_v3_0_lane_settings(struct msm_dsi_phy *phy)
dsi_phy_write(lane_base + REG_DSI_10nm_PHY_LN_TX_DCTRL(3), 0x04);
}
-static int msm_dsi_dphy_timing_calc_v3(struct msm_dsi_dphy_timing *timing,
- struct msm_dsi_phy_clk_request *clk_req)
-{
- /*
- * TODO: These params need to be computed, they're currently hardcoded
- * for a 1440x2560@60Hz panel with a byteclk of 100.618 Mhz, and a
- * default escape clock of 19.2 Mhz.
- */
-
- timing->hs_halfbyte_en = 0;
- timing->clk_zero = 0x1c;
- timing->clk_prepare = 0x07;
- timing->clk_trail = 0x07;
- timing->hs_exit = 0x23;
- timing->hs_zero = 0x21;
- timing->hs_prepare = 0x07;
- timing->hs_trail = 0x07;
- timing->hs_rqst = 0x05;
- timing->ta_sure = 0x00;
- timing->ta_go = 0x03;
- timing->ta_get = 0x04;
-
- timing->shared_timings.clk_pre = 0x2d;
- timing->shared_timings.clk_post = 0x0d;
-
- return 0;
-}
-
static int dsi_10nm_phy_enable(struct msm_dsi_phy *phy, int src_pll_id,
struct msm_dsi_phy_clk_request *clk_req)
{
diff --git a/drivers/gpu/drm/msm/msm_fb.c b/drivers/gpu/drm/msm/msm_fb.c
index 0e0c87252ab0..7a16242bf8bf 100644
--- a/drivers/gpu/drm/msm/msm_fb.c
+++ b/drivers/gpu/drm/msm/msm_fb.c
@@ -183,7 +183,8 @@ static struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev,
hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format);
vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format);
- format = kms->funcs->get_format(kms, mode_cmd->pixel_format);
+ format = kms->funcs->get_format(kms, mode_cmd->pixel_format,
+ mode_cmd->modifier[0]);
if (!format) {
dev_err(dev->dev, "unsupported pixel format: %4.4s\n",
(char *)&mode_cmd->pixel_format);
diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c
index c178563fcd4d..456622b46335 100644
--- a/drivers/gpu/drm/msm/msm_fbdev.c
+++ b/drivers/gpu/drm/msm/msm_fbdev.c
@@ -92,8 +92,7 @@ static int msm_fbdev_create(struct drm_fb_helper *helper,
if (IS_ERR(fb)) {
dev_err(dev->dev, "failed to allocate fb\n");
- ret = PTR_ERR(fb);
- goto fail;
+ return PTR_ERR(fb);
}
bo = msm_framebuffer_bo(fb, 0);
@@ -151,13 +150,7 @@ static int msm_fbdev_create(struct drm_fb_helper *helper,
fail_unlock:
mutex_unlock(&dev->struct_mutex);
-fail:
-
- if (ret) {
- if (fb)
- drm_framebuffer_remove(fb);
- }
-
+ drm_framebuffer_remove(fb);
return ret;
}
diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c
index 95196479f651..f583bb4222f9 100644
--- a/drivers/gpu/drm/msm/msm_gem.c
+++ b/drivers/gpu/drm/msm/msm_gem.c
@@ -132,17 +132,19 @@ static void put_pages(struct drm_gem_object *obj)
struct msm_gem_object *msm_obj = to_msm_bo(obj);
if (msm_obj->pages) {
- /* For non-cached buffers, ensure the new pages are clean
- * because display controller, GPU, etc. are not coherent:
- */
- if (msm_obj->flags & (MSM_BO_WC|MSM_BO_UNCACHED))
- dma_unmap_sg(obj->dev->dev, msm_obj->sgt->sgl,
- msm_obj->sgt->nents, DMA_BIDIRECTIONAL);
+ if (msm_obj->sgt) {
+ /* For non-cached buffers, ensure the new
+ * pages are clean because display controller,
+ * GPU, etc. are not coherent:
+ */
+ if (msm_obj->flags & (MSM_BO_WC|MSM_BO_UNCACHED))
+ dma_unmap_sg(obj->dev->dev, msm_obj->sgt->sgl,
+ msm_obj->sgt->nents,
+ DMA_BIDIRECTIONAL);
- if (msm_obj->sgt)
sg_free_table(msm_obj->sgt);
-
- kfree(msm_obj->sgt);
+ kfree(msm_obj->sgt);
+ }
if (use_pages(obj))
drm_gem_put_pages(obj, msm_obj->pages, true, false);
diff --git a/drivers/gpu/drm/msm/msm_kms.h b/drivers/gpu/drm/msm/msm_kms.h
index 17d5824417ad..aaa329dc020e 100644
--- a/drivers/gpu/drm/msm/msm_kms.h
+++ b/drivers/gpu/drm/msm/msm_kms.h
@@ -48,8 +48,11 @@ struct msm_kms_funcs {
/* functions to wait for atomic commit completed on each CRTC */
void (*wait_for_crtc_commit_done)(struct msm_kms *kms,
struct drm_crtc *crtc);
+ /* get msm_format w/ optional format modifiers from drm_mode_fb_cmd2 */
+ const struct msm_format *(*get_format)(struct msm_kms *kms,
+ const uint32_t format,
+ const uint64_t modifiers);
/* misc: */
- const struct msm_format *(*get_format)(struct msm_kms *kms, uint32_t format);
long (*round_pixclk)(struct msm_kms *kms, unsigned long rate,
struct drm_encoder *encoder);
int (*set_split_display)(struct msm_kms *kms,
diff --git a/drivers/gpu/drm/omapdrm/dss/dpi.c b/drivers/gpu/drm/omapdrm/dss/dpi.c
index fb1c27f69e3a..3d662e6805eb 100644
--- a/drivers/gpu/drm/omapdrm/dss/dpi.c
+++ b/drivers/gpu/drm/omapdrm/dss/dpi.c
@@ -142,7 +142,7 @@ static enum dss_clk_source dpi_get_clk_src(struct dpi_data *dpi)
}
struct dpi_clk_calc_ctx {
- struct dss_pll *pll;
+ struct dpi_data *dpi;
unsigned int clkout_idx;
/* inputs */
@@ -191,7 +191,7 @@ static bool dpi_calc_hsdiv_cb(int m_dispc, unsigned long dispc,
ctx->pll_cinfo.mX[ctx->clkout_idx] = m_dispc;
ctx->pll_cinfo.clkout[ctx->clkout_idx] = dispc;
- return dispc_div_calc(ctx->pll->dss->dispc, dispc,
+ return dispc_div_calc(ctx->dpi->dss->dispc, dispc,
ctx->pck_min, ctx->pck_max,
dpi_calc_dispc_cb, ctx);
}
@@ -208,8 +208,8 @@ static bool dpi_calc_pll_cb(int n, int m, unsigned long fint,
ctx->pll_cinfo.fint = fint;
ctx->pll_cinfo.clkdco = clkdco;
- return dss_pll_hsdiv_calc_a(ctx->pll, clkdco,
- ctx->pck_min, dss_get_max_fck_rate(ctx->pll->dss),
+ return dss_pll_hsdiv_calc_a(ctx->dpi->pll, clkdco,
+ ctx->pck_min, dss_get_max_fck_rate(ctx->dpi->dss),
dpi_calc_hsdiv_cb, ctx);
}
@@ -219,7 +219,7 @@ static bool dpi_calc_dss_cb(unsigned long fck, void *data)
ctx->fck = fck;
- return dispc_div_calc(ctx->pll->dss->dispc, fck,
+ return dispc_div_calc(ctx->dpi->dss->dispc, fck,
ctx->pck_min, ctx->pck_max,
dpi_calc_dispc_cb, ctx);
}
@@ -230,7 +230,7 @@ static bool dpi_pll_clk_calc(struct dpi_data *dpi, unsigned long pck,
unsigned long clkin;
memset(ctx, 0, sizeof(*ctx));
- ctx->pll = dpi->pll;
+ ctx->dpi = dpi;
ctx->clkout_idx = dss_pll_get_clkout_idx_for_src(dpi->clk_src);
clkin = clk_get_rate(dpi->pll->clkin);
@@ -244,7 +244,7 @@ static bool dpi_pll_clk_calc(struct dpi_data *dpi, unsigned long pck,
pll_min = 0;
pll_max = 0;
- return dss_pll_calc_a(ctx->pll, clkin,
+ return dss_pll_calc_a(ctx->dpi->pll, clkin,
pll_min, pll_max,
dpi_calc_pll_cb, ctx);
} else { /* DSS_PLL_TYPE_B */
@@ -275,6 +275,7 @@ static bool dpi_dss_clk_calc(struct dpi_data *dpi, unsigned long pck,
bool ok;
memset(ctx, 0, sizeof(*ctx));
+ ctx->dpi = dpi;
if (pck > 1000 * i * i * i)
ctx->pck_min = max(pck - 1000 * i * i * i, 0lu);
else
diff --git a/drivers/gpu/drm/qxl/qxl_cmd.c b/drivers/gpu/drm/qxl/qxl_cmd.c
index 95db20f2145f..208af9f37914 100644
--- a/drivers/gpu/drm/qxl/qxl_cmd.c
+++ b/drivers/gpu/drm/qxl/qxl_cmd.c
@@ -179,10 +179,9 @@ qxl_push_command_ring_release(struct qxl_device *qdev, struct qxl_release *relea
uint32_t type, bool interruptible)
{
struct qxl_command cmd;
- struct qxl_bo_list *entry = list_first_entry(&release->bos, struct qxl_bo_list, tv.head);
cmd.type = type;
- cmd.data = qxl_bo_physical_address(qdev, to_qxl_bo(entry->tv.bo), release->release_offset);
+ cmd.data = qxl_bo_physical_address(qdev, release->release_bo, release->release_offset);
return qxl_ring_push(qdev->command_ring, &cmd, interruptible);
}
@@ -192,10 +191,9 @@ qxl_push_cursor_ring_release(struct qxl_device *qdev, struct qxl_release *releas
uint32_t type, bool interruptible)
{
struct qxl_command cmd;
- struct qxl_bo_list *entry = list_first_entry(&release->bos, struct qxl_bo_list, tv.head);
cmd.type = type;
- cmd.data = qxl_bo_physical_address(qdev, to_qxl_bo(entry->tv.bo), release->release_offset);
+ cmd.data = qxl_bo_physical_address(qdev, release->release_bo, release->release_offset);
return qxl_ring_push(qdev->cursor_ring, &cmd, interruptible);
}
diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h
index 4b8984017373..01220d386b0a 100644
--- a/drivers/gpu/drm/qxl/qxl_drv.h
+++ b/drivers/gpu/drm/qxl/qxl_drv.h
@@ -167,6 +167,7 @@ struct qxl_release {
int id;
int type;
+ struct qxl_bo *release_bo;
uint32_t release_offset;
uint32_t surface_release_id;
struct ww_acquire_ctx ticket;
diff --git a/drivers/gpu/drm/qxl/qxl_ioctl.c b/drivers/gpu/drm/qxl/qxl_ioctl.c
index e238a1a2eca1..6cc9f3367fa0 100644
--- a/drivers/gpu/drm/qxl/qxl_ioctl.c
+++ b/drivers/gpu/drm/qxl/qxl_ioctl.c
@@ -182,9 +182,9 @@ static int qxl_process_single_command(struct qxl_device *qdev,
goto out_free_reloc;
/* TODO copy slow path code from i915 */
- fb_cmd = qxl_bo_kmap_atomic_page(qdev, cmd_bo, (release->release_offset & PAGE_SIZE));
+ fb_cmd = qxl_bo_kmap_atomic_page(qdev, cmd_bo, (release->release_offset & PAGE_MASK));
unwritten = __copy_from_user_inatomic_nocache
- (fb_cmd + sizeof(union qxl_release_info) + (release->release_offset & ~PAGE_SIZE),
+ (fb_cmd + sizeof(union qxl_release_info) + (release->release_offset & ~PAGE_MASK),
u64_to_user_ptr(cmd->command), cmd->command_size);
{
diff --git a/drivers/gpu/drm/qxl/qxl_release.c b/drivers/gpu/drm/qxl/qxl_release.c
index 5d84a66fed36..7cb214577275 100644
--- a/drivers/gpu/drm/qxl/qxl_release.c
+++ b/drivers/gpu/drm/qxl/qxl_release.c
@@ -173,6 +173,7 @@ qxl_release_free_list(struct qxl_release *release)
list_del(&entry->tv.head);
kfree(entry);
}
+ release->release_bo = NULL;
}
void
@@ -296,7 +297,6 @@ int qxl_alloc_surface_release_reserved(struct qxl_device *qdev,
{
if (surface_cmd_type == QXL_SURFACE_CMD_DESTROY && create_rel) {
int idr_ret;
- struct qxl_bo_list *entry = list_first_entry(&create_rel->bos, struct qxl_bo_list, tv.head);
struct qxl_bo *bo;
union qxl_release_info *info;
@@ -304,8 +304,9 @@ int qxl_alloc_surface_release_reserved(struct qxl_device *qdev,
idr_ret = qxl_release_alloc(qdev, QXL_RELEASE_SURFACE_CMD, release);
if (idr_ret < 0)
return idr_ret;
- bo = to_qxl_bo(entry->tv.bo);
+ bo = create_rel->release_bo;
+ (*release)->release_bo = bo;
(*release)->release_offset = create_rel->release_offset + 64;
qxl_release_list_add(*release, bo);
@@ -365,6 +366,7 @@ int qxl_alloc_release_reserved(struct qxl_device *qdev, unsigned long size,
bo = qxl_bo_ref(qdev->current_release_bo[cur_idx]);
+ (*release)->release_bo = bo;
(*release)->release_offset = qdev->current_release_bo_offset[cur_idx] * release_size_per_bo[cur_idx];
qdev->current_release_bo_offset[cur_idx]++;
@@ -408,13 +410,12 @@ union qxl_release_info *qxl_release_map(struct qxl_device *qdev,
{
void *ptr;
union qxl_release_info *info;
- struct qxl_bo_list *entry = list_first_entry(&release->bos, struct qxl_bo_list, tv.head);
- struct qxl_bo *bo = to_qxl_bo(entry->tv.bo);
+ struct qxl_bo *bo = release->release_bo;
- ptr = qxl_bo_kmap_atomic_page(qdev, bo, release->release_offset & PAGE_SIZE);
+ ptr = qxl_bo_kmap_atomic_page(qdev, bo, release->release_offset & PAGE_MASK);
if (!ptr)
return NULL;
- info = ptr + (release->release_offset & ~PAGE_SIZE);
+ info = ptr + (release->release_offset & ~PAGE_MASK);
return info;
}
@@ -422,11 +423,10 @@ void qxl_release_unmap(struct qxl_device *qdev,
struct qxl_release *release,
union qxl_release_info *info)
{
- struct qxl_bo_list *entry = list_first_entry(&release->bos, struct qxl_bo_list, tv.head);
- struct qxl_bo *bo = to_qxl_bo(entry->tv.bo);
+ struct qxl_bo *bo = release->release_bo;
void *ptr;
- ptr = ((void *)info) - (release->release_offset & ~PAGE_SIZE);
+ ptr = ((void *)info) - (release->release_offset & ~PAGE_MASK);
qxl_bo_kunmap_atomic_page(qdev, bo, ptr);
}
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index e415d2c097a7..48d0e6bd0508 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -140,6 +140,10 @@ static struct radeon_px_quirk radeon_px_quirk_list[] = {
* https://bugs.freedesktop.org/show_bug.cgi?id=101491
*/
{ PCI_VENDOR_ID_ATI, 0x6741, 0x1043, 0x2122, RADEON_PX_QUIRK_DISABLE_PX },
+ /* Asus K73TK laptop with AMD A6-3420M APU and Radeon 7670m GPU
+ * https://bugzilla.kernel.org/show_bug.cgi?id=51381#c52
+ */
+ { PCI_VENDOR_ID_ATI, 0x6840, 0x1043, 0x2123, RADEON_PX_QUIRK_DISABLE_PX },
{ 0, 0, 0, 0, 0 },
};
diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c
index 97a0a639dad9..90d5b41007bf 100644
--- a/drivers/gpu/drm/radeon/si_dpm.c
+++ b/drivers/gpu/drm/radeon/si_dpm.c
@@ -5912,9 +5912,9 @@ static void si_set_pcie_lane_width_in_smc(struct radeon_device *rdev,
{
u32 lane_width;
u32 new_lane_width =
- (radeon_new_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT;
+ ((radeon_new_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT) + 1;
u32 current_lane_width =
- (radeon_current_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT;
+ ((radeon_current_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT) + 1;
if (new_lane_width != current_lane_width) {
radeon_set_pcie_lanes(rdev, new_lane_width);
diff --git a/drivers/gpu/drm/sun4i/sun4i_lvds.c b/drivers/gpu/drm/sun4i/sun4i_lvds.c
index bffff4c9fbf5..be3f14d7746d 100644
--- a/drivers/gpu/drm/sun4i/sun4i_lvds.c
+++ b/drivers/gpu/drm/sun4i/sun4i_lvds.c
@@ -94,64 +94,9 @@ static void sun4i_lvds_encoder_disable(struct drm_encoder *encoder)
}
}
-static enum drm_mode_status sun4i_lvds_encoder_mode_valid(struct drm_encoder *crtc,
- const struct drm_display_mode *mode)
-{
- struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(crtc);
- struct sun4i_tcon *tcon = lvds->tcon;
- u32 hsync = mode->hsync_end - mode->hsync_start;
- u32 vsync = mode->vsync_end - mode->vsync_start;
- unsigned long rate = mode->clock * 1000;
- long rounded_rate;
-
- DRM_DEBUG_DRIVER("Validating modes...\n");
-
- if (hsync < 1)
- return MODE_HSYNC_NARROW;
-
- if (hsync > 0x3ff)
- return MODE_HSYNC_WIDE;
-
- if ((mode->hdisplay < 1) || (mode->htotal < 1))
- return MODE_H_ILLEGAL;
-
- if ((mode->hdisplay > 0x7ff) || (mode->htotal > 0xfff))
- return MODE_BAD_HVALUE;
-
- DRM_DEBUG_DRIVER("Horizontal parameters OK\n");
-
- if (vsync < 1)
- return MODE_VSYNC_NARROW;
-
- if (vsync > 0x3ff)
- return MODE_VSYNC_WIDE;
-
- if ((mode->vdisplay < 1) || (mode->vtotal < 1))
- return MODE_V_ILLEGAL;
-
- if ((mode->vdisplay > 0x7ff) || (mode->vtotal > 0xfff))
- return MODE_BAD_VVALUE;
-
- DRM_DEBUG_DRIVER("Vertical parameters OK\n");
-
- tcon->dclk_min_div = 7;
- tcon->dclk_max_div = 7;
- rounded_rate = clk_round_rate(tcon->dclk, rate);
- if (rounded_rate < rate)
- return MODE_CLOCK_LOW;
-
- if (rounded_rate > rate)
- return MODE_CLOCK_HIGH;
-
- DRM_DEBUG_DRIVER("Clock rate OK\n");
-
- return MODE_OK;
-}
-
static const struct drm_encoder_helper_funcs sun4i_lvds_enc_helper_funcs = {
.disable = sun4i_lvds_encoder_disable,
.enable = sun4i_lvds_encoder_enable,
- .mode_valid = sun4i_lvds_encoder_mode_valid,
};
static const struct drm_encoder_funcs sun4i_lvds_enc_funcs = {
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 616c9634585e..9f83a65b5ea9 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -2025,9 +2025,9 @@ static const struct tegra_dc_soc_info tegra124_dc_soc_info = {
.coupled_pm = false,
.has_nvdisplay = false,
.num_primary_formats = ARRAY_SIZE(tegra124_primary_formats),
- .primary_formats = tegra114_primary_formats,
+ .primary_formats = tegra124_primary_formats,
.num_overlay_formats = ARRAY_SIZE(tegra124_overlay_formats),
- .overlay_formats = tegra114_overlay_formats,
+ .overlay_formats = tegra124_overlay_formats,
.modifiers = tegra124_modifiers,
};
@@ -2178,7 +2178,7 @@ static int tegra_dc_couple(struct tegra_dc *dc)
struct device_link *link;
struct device *partner;
- partner = driver_find_device(dc->dev->driver, NULL, 0,
+ partner = driver_find_device(dc->dev->driver, NULL, NULL,
tegra_dc_match_by_pipe);
if (!partner)
return -EPROBE_DEFER;
diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c
index 2decc8e2c79f..add9cc97a3b6 100644
--- a/drivers/gpu/drm/vc4/vc4_bo.c
+++ b/drivers/gpu/drm/vc4/vc4_bo.c
@@ -195,6 +195,7 @@ static void vc4_bo_destroy(struct vc4_bo *bo)
vc4_bo_set_label(obj, -1);
if (bo->validated_shader) {
+ kfree(bo->validated_shader->uniform_addr_offsets);
kfree(bo->validated_shader->texture_samples);
kfree(bo->validated_shader);
bo->validated_shader = NULL;
@@ -591,6 +592,7 @@ void vc4_free_object(struct drm_gem_object *gem_bo)
}
if (bo->validated_shader) {
+ kfree(bo->validated_shader->uniform_addr_offsets);
kfree(bo->validated_shader->texture_samples);
kfree(bo->validated_shader);
bo->validated_shader = NULL;
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index 1a6db291d48b..b8d50533e2bb 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -995,15 +995,17 @@ static const struct snd_soc_dapm_route vc4_hdmi_audio_routes[] = {
{ "TX", NULL, "Playback" },
};
-static const struct snd_soc_codec_driver vc4_hdmi_audio_codec_drv = {
- .component_driver = {
- .controls = vc4_hdmi_audio_controls,
- .num_controls = ARRAY_SIZE(vc4_hdmi_audio_controls),
- .dapm_widgets = vc4_hdmi_audio_widgets,
- .num_dapm_widgets = ARRAY_SIZE(vc4_hdmi_audio_widgets),
- .dapm_routes = vc4_hdmi_audio_routes,
- .num_dapm_routes = ARRAY_SIZE(vc4_hdmi_audio_routes),
- },
+static const struct snd_soc_component_driver vc4_hdmi_audio_component_drv = {
+ .controls = vc4_hdmi_audio_controls,
+ .num_controls = ARRAY_SIZE(vc4_hdmi_audio_controls),
+ .dapm_widgets = vc4_hdmi_audio_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(vc4_hdmi_audio_widgets),
+ .dapm_routes = vc4_hdmi_audio_routes,
+ .num_dapm_routes = ARRAY_SIZE(vc4_hdmi_audio_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+ .non_legacy_dai_naming = 1,
};
static const struct snd_soc_dai_ops vc4_hdmi_audio_dai_ops = {
@@ -1101,11 +1103,11 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *hdmi)
return ret;
}
- /* register codec and codec dai */
- ret = snd_soc_register_codec(dev, &vc4_hdmi_audio_codec_drv,
+ /* register component and codec dai */
+ ret = devm_snd_soc_register_component(dev, &vc4_hdmi_audio_component_drv,
&vc4_hdmi_audio_codec_dai_drv, 1);
if (ret) {
- dev_err(dev, "Could not register codec: %d\n", ret);
+ dev_err(dev, "Could not register component: %d\n", ret);
return ret;
}
@@ -1130,29 +1132,11 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *hdmi)
*/
snd_soc_card_set_drvdata(card, hdmi);
ret = devm_snd_soc_register_card(dev, card);
- if (ret) {
+ if (ret)
dev_err(dev, "Could not register sound card: %d\n", ret);
- goto unregister_codec;
- }
-
- return 0;
-
-unregister_codec:
- snd_soc_unregister_codec(dev);
return ret;
-}
-
-static void vc4_hdmi_audio_cleanup(struct vc4_hdmi *hdmi)
-{
- struct device *dev = &hdmi->pdev->dev;
- /*
- * If drvdata is not set this means the audio card was not
- * registered, just skip codec unregistration in this case.
- */
- if (dev_get_drvdata(dev))
- snd_soc_unregister_codec(dev);
}
#ifdef CONFIG_DRM_VC4_HDMI_CEC
@@ -1480,7 +1464,6 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master,
struct vc4_dev *vc4 = drm->dev_private;
struct vc4_hdmi *hdmi = vc4->hdmi;
- vc4_hdmi_audio_cleanup(hdmi);
cec_unregister_adapter(hdmi->cec_adap);
vc4_hdmi_connector_destroy(hdmi->connector);
vc4_hdmi_encoder_destroy(hdmi->encoder);
diff --git a/drivers/gpu/drm/vc4/vc4_validate_shaders.c b/drivers/gpu/drm/vc4/vc4_validate_shaders.c
index d3f15bf60900..7cf82b071de2 100644
--- a/drivers/gpu/drm/vc4/vc4_validate_shaders.c
+++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c
@@ -942,6 +942,7 @@ vc4_validate_shader(struct drm_gem_cma_object *shader_obj)
fail:
kfree(validation_state.branch_targets);
if (validated_shader) {
+ kfree(validated_shader->uniform_addr_offsets);
kfree(validated_shader->texture_samples);
kfree(validated_shader);
}
diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c
index 48e4f1df6e5d..020070d483d3 100644
--- a/drivers/gpu/drm/virtio/virtgpu_vq.c
+++ b/drivers/gpu/drm/virtio/virtgpu_vq.c
@@ -293,7 +293,7 @@ retry:
ret = virtqueue_add_sgs(vq, sgs, outcnt, incnt, vbuf, GFP_ATOMIC);
if (ret == -ENOSPC) {
spin_unlock(&vgdev->ctrlq.qlock);
- wait_event(vgdev->ctrlq.ack_queue, vq->num_free);
+ wait_event(vgdev->ctrlq.ack_queue, vq->num_free >= outcnt + incnt);
spin_lock(&vgdev->ctrlq.qlock);
goto retry;
} else {
@@ -368,7 +368,7 @@ retry:
ret = virtqueue_add_sgs(vq, sgs, outcnt, 0, vbuf, GFP_ATOMIC);
if (ret == -ENOSPC) {
spin_unlock(&vgdev->cursorq.qlock);
- wait_event(vgdev->cursorq.ack_queue, vq->num_free);
+ wait_event(vgdev->cursorq.ack_queue, vq->num_free >= outcnt);
spin_lock(&vgdev->cursorq.qlock);
goto retry;
} else {
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 19c499f5623d..60252fd796f6 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -274,15 +274,23 @@ config HID_EMS_FF
Currently the following devices are known to be supported:
- Trio Linker Plus II
+config HID_ELAN
+ tristate "ELAN USB Touchpad Support"
+ depends on LEDS_CLASS && USB_HID
+ ---help---
+ Say Y to enable support for the USB ELAN touchpad
+ Currently the following devices are known to be supported:
+ - HP Pavilion X2 10-p0XX.
+
config HID_ELECOM
tristate "ELECOM HID devices"
depends on HID
---help---
Support for ELECOM devices:
- BM084 Bluetooth Mouse
- - EX-G Trackball (Wired and wireless)
- - DEFT Trackball (Wired and wireless)
- - HUGE Trackball (Wired and wireless)
+ - EX-G Trackballs (M-XT3DRBK, M-XT3URBK)
+ - DEFT Trackballs (M-DT1DRBK, M-DT1URBK, M-DT2DRBK, M-DT2URBK)
+ - HUGE Trackballs (M-HT1DRBK, M-HT1URBK)
config HID_ELO
tristate "ELO USB 4000/4500 touchscreen"
@@ -331,6 +339,12 @@ config HOLTEK_FF
Say Y here if you have a Holtek On Line Grip based game controller
and want to have force feedback support for it.
+config HID_GOOGLE_HAMMER
+ tristate "Google Hammer Keyboard"
+ depends on USB_HID && LEDS_CLASS
+ ---help---
+ Say Y here if you have a Google Hammer device.
+
config HID_GT683R
tristate "MSI GT68xR LED support"
depends on LEDS_CLASS && USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index eb13b9e92d85..17a8bd97da9d 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -39,11 +39,13 @@ obj-$(CONFIG_HID_CP2112) += hid-cp2112.o
obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o
obj-$(CONFIG_HID_DRAGONRISE) += hid-dr.o
obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o
+obj-$(CONFIG_HID_ELAN) += hid-elan.o
obj-$(CONFIG_HID_ELECOM) += hid-elecom.o
obj-$(CONFIG_HID_ELO) += hid-elo.o
obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o
obj-$(CONFIG_HID_GEMBIRD) += hid-gembird.o
obj-$(CONFIG_HID_GFRM) += hid-gfrm.o
+obj-$(CONFIG_HID_GOOGLE_HAMMER) += hid-google-hammer.o
obj-$(CONFIG_HID_GT683R) += hid-gt683r.o
obj-$(CONFIG_HID_GYRATION) += hid-gyration.o
obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index 88b9703318e4..88a5672f42cd 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -570,7 +570,9 @@ static int asus_input_mapping(struct hid_device *hdev,
static int asus_start_multitouch(struct hid_device *hdev)
{
int ret;
- const unsigned char buf[] = { FEATURE_REPORT_ID, 0x00, 0x03, 0x01, 0x00 };
+ static const unsigned char buf[] = {
+ FEATURE_REPORT_ID, 0x00, 0x03, 0x01, 0x00
+ };
unsigned char *dmabuf = kmemdup(buf, sizeof(buf), GFP_KERNEL);
if (!dmabuf) {
@@ -644,8 +646,7 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
* All functionality is on a single HID interface and for
* userspace the touchpad must be a separate input_dev.
*/
- hdev->quirks |= HID_QUIRK_MULTI_INPUT |
- HID_QUIRK_NO_EMPTY_INPUT;
+ hdev->quirks |= HID_QUIRK_MULTI_INPUT;
drvdata->tp = &asus_t100chi_tp;
}
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index c2560aae5542..5d7cc6bbbac6 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1365,7 +1365,7 @@ u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags)
* of implement() working on 8 byte chunks
*/
- int len = hid_report_len(report) + 7;
+ u32 len = hid_report_len(report) + 7;
return kmalloc(len, flags);
}
@@ -1430,7 +1430,7 @@ void __hid_request(struct hid_device *hid, struct hid_report *report,
{
char *buf;
int ret;
- int len;
+ u32 len;
buf = hid_alloc_report_buf(report, GFP_KERNEL);
if (!buf)
@@ -1456,14 +1456,14 @@ out:
}
EXPORT_SYMBOL_GPL(__hid_request);
-int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size,
+int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size,
int interrupt)
{
struct hid_report_enum *report_enum = hid->report_enum + type;
struct hid_report *report;
struct hid_driver *hdrv;
unsigned int a;
- int rsize, csize = size;
+ u32 rsize, csize = size;
u8 *cdata = data;
int ret = 0;
@@ -1521,7 +1521,7 @@ EXPORT_SYMBOL_GPL(hid_report_raw_event);
*
* This is data entry for lower layers.
*/
-int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int interrupt)
+int hid_input_report(struct hid_device *hid, int type, u8 *data, u32 size, int interrupt)
{
struct hid_report_enum *report_enum;
struct hid_driver *hdrv;
@@ -1966,6 +1966,8 @@ static int hid_device_probe(struct device *dev)
}
}
+ /* reset the quirks that has been previously set */
+ hdev->quirks = hid_lookup_quirk(hdev);
hdev->driver = hdrv;
if (hdrv->probe) {
ret = hdrv->probe(hdev, id);
@@ -2197,31 +2199,40 @@ void hid_destroy_device(struct hid_device *hdev)
EXPORT_SYMBOL_GPL(hid_destroy_device);
-static int __bus_add_driver(struct device_driver *drv, void *data)
+static int __hid_bus_reprobe_drivers(struct device *dev, void *data)
{
- struct hid_driver *added_hdrv = data;
- struct hid_driver *hdrv = to_hid_driver(drv);
+ struct hid_driver *hdrv = data;
+ struct hid_device *hdev = to_hid_device(dev);
- if (hdrv->bus_add_driver)
- hdrv->bus_add_driver(added_hdrv);
+ if (hdev->driver == hdrv &&
+ !hdrv->match(hdev, hid_ignore_special_drivers))
+ return device_reprobe(dev);
return 0;
}
-static int __bus_removed_driver(struct device_driver *drv, void *data)
+static int __hid_bus_driver_added(struct device_driver *drv, void *data)
{
- struct hid_driver *removed_hdrv = data;
struct hid_driver *hdrv = to_hid_driver(drv);
- if (hdrv->bus_removed_driver)
- hdrv->bus_removed_driver(removed_hdrv);
+ if (hdrv->match) {
+ bus_for_each_dev(&hid_bus_type, NULL, hdrv,
+ __hid_bus_reprobe_drivers);
+ }
return 0;
}
+static int __bus_removed_driver(struct device_driver *drv, void *data)
+{
+ return bus_rescan_devices(&hid_bus_type);
+}
+
int __hid_register_driver(struct hid_driver *hdrv, struct module *owner,
const char *mod_name)
{
+ int ret;
+
hdrv->driver.name = hdrv->name;
hdrv->driver.bus = &hid_bus_type;
hdrv->driver.owner = owner;
@@ -2230,9 +2241,13 @@ int __hid_register_driver(struct hid_driver *hdrv, struct module *owner,
INIT_LIST_HEAD(&hdrv->dyn_list);
spin_lock_init(&hdrv->dyn_lock);
- bus_for_each_drv(&hid_bus_type, NULL, hdrv, __bus_add_driver);
+ ret = driver_register(&hdrv->driver);
+
+ if (ret == 0)
+ bus_for_each_drv(&hid_bus_type, NULL, NULL,
+ __hid_bus_driver_added);
- return driver_register(&hdrv->driver);
+ return ret;
}
EXPORT_SYMBOL_GPL(__hid_register_driver);
diff --git a/drivers/hid/hid-corsair.c b/drivers/hid/hid-corsair.c
index 9ba5d98a1180..ec9e060ec46c 100644
--- a/drivers/hid/hid-corsair.c
+++ b/drivers/hid/hid-corsair.c
@@ -2,11 +2,14 @@
* HID driver for Corsair devices
*
* Supported devices:
+ * - Vengeance K70 Keyboard
+ * - K70 RAPIDFIRE Keyboard
* - Vengeance K90 Keyboard
* - Scimitar PRO RGB Gaming Mouse
*
* Copyright (c) 2015 Clement Vuchener
* Copyright (c) 2017 Oscar Campos
+ * Copyright (c) 2017 Aaron Bottegal
*/
/*
@@ -673,7 +676,7 @@ static int corsair_input_mapping(struct hid_device *dev,
}
/*
- * The report descriptor of Corsair Scimitar RGB Pro gaming mouse is
+ * The report descriptor of some of the Corsair gaming mice is
* non parseable as they define two consecutive Logical Minimum for
* the Usage Page (Consumer) in rdescs bytes 75 and 77 being 77 0x16
* that should be obviousy 0x26 for Logical Magimum of 16 bits. This
@@ -681,7 +684,8 @@ static int corsair_input_mapping(struct hid_device *dev,
* Minimum being larger than Logical Maximum.
*
* This driver fixes the report descriptor for:
- * - USB ID b1c:1b3e, sold as Scimitar RGB Pro Gaming mouse
+ * - USB ID 1b1c:1b34, sold as GLAIVE RGB Gaming mouse
+ * - USB ID 1b1c:1b3e, sold as Scimitar RGB Pro Gaming mouse
*/
static __u8 *corsair_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc,
@@ -691,13 +695,14 @@ static __u8 *corsair_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc,
if (intf->cur_altsetting->desc.bInterfaceNumber == 1) {
/*
- * Corsair Scimitar RGB Pro report descriptor is broken and
- * defines two different Logical Minimum for the Consumer
- * Application. The byte 77 should be a 0x26 defining a 16
- * bits integer for the Logical Maximum but it is a 0x16
+ * Corsair GLAIVE RGB and Scimitar RGB Pro report descriptor is
+ * broken and defines two different Logical Minimum for the
+ * Consumer Application. The byte 77 should be a 0x26 defining
+ * a 16 bits integer for the Logical Maximum but it is a 0x16
* instead (Logical Minimum)
*/
switch (hdev->product) {
+ case USB_DEVICE_ID_CORSAIR_GLAIVE_RGB:
case USB_DEVICE_ID_CORSAIR_SCIMITAR_PRO_RGB:
if (*rsize >= 172 && rdesc[75] == 0x15 && rdesc[77] == 0x16
&& rdesc[78] == 0xff && rdesc[79] == 0x0f) {
@@ -716,7 +721,14 @@ static const struct hid_device_id corsair_devices[] = {
.driver_data = CORSAIR_USE_K90_MACRO |
CORSAIR_USE_K90_BACKLIGHT },
{ HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR,
+ USB_DEVICE_ID_CORSAIR_GLAIVE_RGB) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR,
USB_DEVICE_ID_CORSAIR_SCIMITAR_PRO_RGB) },
+ /*
+ * Vengeance K70 and K70 RAPIDFIRE share product IDs.
+ */
+ { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR,
+ USB_DEVICE_ID_CORSAIR_K70R) },
{}
};
diff --git a/drivers/hid/hid-elan.c b/drivers/hid/hid-elan.c
new file mode 100644
index 000000000000..803a725785cf
--- /dev/null
+++ b/drivers/hid/hid-elan.c
@@ -0,0 +1,421 @@
+/*
+ * HID Driver for ELAN Touchpad
+ *
+ * Currently only supports touchpad found on HP Pavilion X2 10
+ *
+ * Copyright (c) 2016 Alexandrov Stanislav <neko@nya.ai>
+ *
+ * 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/hid.h>
+#include <linux/input/mt.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#include "hid-ids.h"
+
+#define ELAN_SINGLE_FINGER 0x81
+#define ELAN_MT_FIRST_FINGER 0x82
+#define ELAN_MT_SECOND_FINGER 0x83
+#define ELAN_INPUT_REPORT_SIZE 8
+
+#define ELAN_MUTE_LED_REPORT 0xBC
+#define ELAN_LED_REPORT_SIZE 8
+
+struct elan_touchpad_settings {
+ u8 max_fingers;
+ u16 max_x;
+ u16 max_y;
+ u8 max_area_x;
+ u8 max_area_y;
+ u8 max_w;
+ int usb_bInterfaceNumber;
+};
+
+struct elan_drvdata {
+ struct input_dev *input;
+ u8 prev_report[ELAN_INPUT_REPORT_SIZE];
+ struct led_classdev mute_led;
+ u8 mute_led_state;
+ struct elan_touchpad_settings *settings;
+};
+
+static int is_not_elan_touchpad(struct hid_device *hdev)
+{
+ struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+ struct elan_drvdata *drvdata = hid_get_drvdata(hdev);
+
+ return (intf->altsetting->desc.bInterfaceNumber != drvdata->settings->usb_bInterfaceNumber);
+}
+
+static int elan_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ if (is_not_elan_touchpad(hdev))
+ return 0;
+
+ if (field->report->id == ELAN_SINGLE_FINGER ||
+ field->report->id == ELAN_MT_FIRST_FINGER ||
+ field->report->id == ELAN_MT_SECOND_FINGER)
+ return -1;
+
+ return 0;
+}
+
+static int elan_input_configured(struct hid_device *hdev, struct hid_input *hi)
+{
+ int ret;
+ struct input_dev *input;
+ struct elan_drvdata *drvdata = hid_get_drvdata(hdev);
+
+ if (is_not_elan_touchpad(hdev))
+ return 0;
+
+ input = devm_input_allocate_device(&hdev->dev);
+ if (!input)
+ return -ENOMEM;
+
+ input->name = "Elan Touchpad";
+ input->phys = hdev->phys;
+ input->uniq = hdev->uniq;
+ input->id.bustype = hdev->bus;
+ input->id.vendor = hdev->vendor;
+ input->id.product = hdev->product;
+ input->id.version = hdev->version;
+ input->dev.parent = &hdev->dev;
+
+ input_set_abs_params(input, ABS_MT_POSITION_X, 0,
+ drvdata->settings->max_x, 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
+ drvdata->settings->max_y, 0, 0);
+ input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0,
+ drvdata->settings->max_fingers, 0, 0);
+ input_set_abs_params(input, ABS_TOOL_WIDTH, 0,
+ drvdata->settings->max_w, 0, 0);
+
+ __set_bit(BTN_LEFT, input->keybit);
+ __set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
+
+ ret = input_mt_init_slots(input, drvdata->settings->max_fingers,
+ INPUT_MT_POINTER);
+ if (ret) {
+ hid_err(hdev, "Failed to init elan MT slots: %d\n", ret);
+ return ret;
+ }
+
+ ret = input_register_device(input);
+ if (ret) {
+ hid_err(hdev, "Failed to register elan input device: %d\n",
+ ret);
+ input_free_device(input);
+ return ret;
+ }
+
+ drvdata->input = input;
+
+ return 0;
+}
+
+static void elan_report_mt_slot(struct elan_drvdata *drvdata, u8 *data,
+ unsigned int slot_num)
+{
+ struct input_dev *input = drvdata->input;
+ int x, y, w;
+
+ bool active = !!data;
+
+ input_mt_slot(input, slot_num);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER, active);
+ if (active) {
+ x = ((data[0] & 0xF0) << 4) | data[1];
+ y = drvdata->settings->max_y -
+ (((data[0] & 0x07) << 8) | data[2]);
+ w = data[4];
+
+ input_report_abs(input, ABS_MT_POSITION_X, x);
+ input_report_abs(input, ABS_MT_POSITION_Y, y);
+ input_report_abs(input, ABS_TOOL_WIDTH, w);
+ }
+}
+
+static void elan_report_input(struct elan_drvdata *drvdata, u8 *data)
+{
+ int i;
+ struct input_dev *input = drvdata->input;
+
+ /*
+ * There is 3 types of reports: for single touch,
+ * for multitouch - first finger and for multitouch - second finger
+ *
+ * packet structure for ELAN_SINGLE_FINGER and ELAN_MT_FIRST_FINGER:
+ *
+ * byte 1: 1 0 0 0 0 0 0 1 // 0x81 or 0x82
+ * byte 2: 0 0 0 0 0 0 0 0 // looks like unused
+ * byte 3: f5 f4 f3 f2 f1 0 0 L
+ * byte 4: x12 x11 x10 x9 0? y11 y10 y9
+ * byte 5: x8 x7 x6 x5 x4 x3 x2 x1
+ * byte 6: y8 y7 y6 y5 y4 y3 y2 y1
+ * byte 7: sy4 sy3 sy2 sy1 sx4 sx3 sx2 sx1
+ * byte 8: w8 w7 w6 w5 w4 w3 w2 w1
+ *
+ * packet structure for ELAN_MT_SECOND_FINGER:
+ *
+ * byte 1: 1 0 0 0 0 0 1 1 // 0x83
+ * byte 2: x12 x11 x10 x9 0 y11 y10 y9
+ * byte 3: x8 x7 x6 x5 x4 x3 x2 x1
+ * byte 4: y8 y7 y6 y5 y4 y3 y2 y1
+ * byte 5: sy4 sy3 sy2 sy1 sx4 sx3 sx2 sx1
+ * byte 6: w8 w7 w6 w5 w4 w3 w2 w1
+ * byte 7: 0 0 0 0 0 0 0 0
+ * byte 8: 0 0 0 0 0 0 0 0
+ *
+ * f5-f1: finger touch bits
+ * L: clickpad button
+ * sy / sx: not sure yet, but this looks like rectangular
+ * area for finger
+ * w: looks like finger width
+ */
+
+ if (data[0] == ELAN_SINGLE_FINGER) {
+ for (i = 0; i < drvdata->settings->max_fingers; i++) {
+ if (data[2] & BIT(i + 3))
+ elan_report_mt_slot(drvdata, data + 3, i);
+ else
+ elan_report_mt_slot(drvdata, NULL, i);
+ }
+ input_report_key(input, BTN_LEFT, data[2] & 0x01);
+ }
+ /*
+ * When touched with two fingers Elan touchpad will emit two HID reports
+ * first is ELAN_MT_FIRST_FINGER and second is ELAN_MT_SECOND_FINGER
+ * we will save ELAN_MT_FIRST_FINGER report and wait for
+ * ELAN_MT_SECOND_FINGER to finish multitouch
+ */
+ if (data[0] == ELAN_MT_FIRST_FINGER) {
+ memcpy(drvdata->prev_report, data,
+ sizeof(drvdata->prev_report));
+ return;
+ }
+
+ if (data[0] == ELAN_MT_SECOND_FINGER) {
+ int first = 0;
+ u8 *prev_report = drvdata->prev_report;
+
+ if (prev_report[0] != ELAN_MT_FIRST_FINGER)
+ return;
+
+ for (i = 0; i < drvdata->settings->max_fingers; i++) {
+ if (prev_report[2] & BIT(i + 3)) {
+ if (!first) {
+ first = 1;
+ elan_report_mt_slot(drvdata, prev_report + 3, i);
+ } else {
+ elan_report_mt_slot(drvdata, data + 1, i);
+ }
+ } else {
+ elan_report_mt_slot(drvdata, NULL, i);
+ }
+ }
+ input_report_key(input, BTN_LEFT, prev_report[2] & 0x01);
+ }
+
+ input_mt_sync_frame(input);
+ input_sync(input);
+}
+
+static int elan_raw_event(struct hid_device *hdev,
+ struct hid_report *report, u8 *data, int size)
+{
+ struct elan_drvdata *drvdata = hid_get_drvdata(hdev);
+
+ if (is_not_elan_touchpad(hdev))
+ return 0;
+
+ if (data[0] == ELAN_SINGLE_FINGER ||
+ data[0] == ELAN_MT_FIRST_FINGER ||
+ data[0] == ELAN_MT_SECOND_FINGER) {
+ if (size == ELAN_INPUT_REPORT_SIZE) {
+ elan_report_input(drvdata, data);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int elan_start_multitouch(struct hid_device *hdev)
+{
+ int ret;
+
+ /*
+ * This byte sequence will enable multitouch mode and disable
+ * mouse emulation
+ */
+ const unsigned char buf[] = { 0x0D, 0x00, 0x03, 0x21, 0x00 };
+ unsigned char *dmabuf = kmemdup(buf, sizeof(buf), GFP_KERNEL);
+
+ if (!dmabuf)
+ return -ENOMEM;
+
+ ret = hid_hw_raw_request(hdev, dmabuf[0], dmabuf, sizeof(buf),
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+
+ kfree(dmabuf);
+
+ if (ret != sizeof(buf)) {
+ hid_err(hdev, "Failed to start multitouch: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static enum led_brightness elan_mute_led_get_brigtness(struct led_classdev *led_cdev)
+{
+ struct device *dev = led_cdev->dev->parent;
+ struct hid_device *hdev = to_hid_device(dev);
+ struct elan_drvdata *drvdata = hid_get_drvdata(hdev);
+
+ return drvdata->mute_led_state;
+}
+
+static int elan_mute_led_set_brigtness(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ int ret;
+ u8 led_state;
+ struct device *dev = led_cdev->dev->parent;
+ struct hid_device *hdev = to_hid_device(dev);
+ struct elan_drvdata *drvdata = hid_get_drvdata(hdev);
+
+ unsigned char *dmabuf = kzalloc(ELAN_LED_REPORT_SIZE, GFP_KERNEL);
+
+ if (!dmabuf)
+ return -ENOMEM;
+
+ led_state = !!value;
+
+ dmabuf[0] = ELAN_MUTE_LED_REPORT;
+ dmabuf[1] = 0x02;
+ dmabuf[2] = led_state;
+
+ ret = hid_hw_raw_request(hdev, dmabuf[0], dmabuf, ELAN_LED_REPORT_SIZE,
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+
+ kfree(dmabuf);
+
+ if (ret != ELAN_LED_REPORT_SIZE) {
+ hid_err(hdev, "Failed to set mute led brightness: %d\n", ret);
+ return ret;
+ }
+
+ drvdata->mute_led_state = led_state;
+ return 0;
+}
+
+static int elan_init_mute_led(struct hid_device *hdev)
+{
+ struct elan_drvdata *drvdata = hid_get_drvdata(hdev);
+ struct led_classdev *mute_led = &drvdata->mute_led;
+
+ mute_led->name = "elan:red:mute";
+ mute_led->brightness_get = elan_mute_led_get_brigtness;
+ mute_led->brightness_set_blocking = elan_mute_led_set_brigtness;
+ mute_led->max_brightness = LED_ON;
+ mute_led->dev = &hdev->dev;
+
+ return devm_led_classdev_register(&hdev->dev, mute_led);
+}
+
+static int elan_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+ int ret;
+ struct elan_drvdata *drvdata;
+
+ drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL);
+
+ if (!drvdata)
+ return -ENOMEM;
+
+ drvdata->settings = (struct elan_touchpad_settings *)id->driver_data;
+ hid_set_drvdata(hdev, drvdata);
+
+ ret = hid_parse(hdev);
+ if (ret) {
+ hid_err(hdev, "Hid Parse failed\n");
+ return ret;
+ }
+
+ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+ if (ret) {
+ hid_err(hdev, "Hid hw start failed\n");
+ return ret;
+ }
+
+ if (is_not_elan_touchpad(hdev))
+ return 0;
+
+ if (!drvdata->input) {
+ hid_err(hdev, "Input device is not registred\n");
+ ret = -ENAVAIL;
+ goto err;
+ }
+
+ ret = elan_start_multitouch(hdev);
+ if (ret)
+ goto err;
+
+ ret = elan_init_mute_led(hdev);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ hid_hw_stop(hdev);
+ return ret;
+}
+
+static void elan_remove(struct hid_device *hdev)
+{
+ hid_hw_stop(hdev);
+}
+
+static const struct elan_touchpad_settings hp_x2_10_touchpad_data = {
+ .max_fingers = 5,
+ .max_x = 2930,
+ .max_y = 1250,
+ .max_area_x = 15,
+ .max_area_y = 15,
+ .max_w = 255,
+ .usb_bInterfaceNumber = 1,
+};
+
+static const struct hid_device_id elan_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_HP_X2_10_COVER),
+ (kernel_ulong_t)&hp_x2_10_touchpad_data},
+ { }
+};
+
+MODULE_DEVICE_TABLE(hid, elan_devices);
+
+static struct hid_driver elan_driver = {
+ .name = "elan",
+ .id_table = elan_devices,
+ .input_mapping = elan_input_mapping,
+ .input_configured = elan_input_configured,
+ .raw_event = elan_raw_event,
+ .probe = elan_probe,
+ .remove = elan_remove,
+};
+
+module_hid_driver(elan_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Alexandrov Stanislav");
+MODULE_DESCRIPTION("Driver for HID ELAN Touchpads");
diff --git a/drivers/hid/hid-elecom.c b/drivers/hid/hid-elecom.c
index 1a1ecc491c02..ae8e9413c79d 100644
--- a/drivers/hid/hid-elecom.c
+++ b/drivers/hid/hid-elecom.c
@@ -1,9 +1,9 @@
/*
* HID driver for ELECOM devices:
* - BM084 Bluetooth Mouse
- * - EX-G Trackball (Wired and wireless)
- * - DEFT Trackball (Wired and wireless)
- * - HUGE Trackball (Wired and wireless)
+ * - EX-G Trackballs (M-XT3DRBK, M-XT3URBK, M-XT4DRBK)
+ * - DEFT Trackballs (M-DT1DRBK, M-DT1URBK, M-DT2DRBK, M-DT2URBK)
+ * - HUGE Trackballs (M-HT1DRBK, M-HT1URBK)
*
* Copyright (c) 2010 Richard Nauber <Richard.Nauber@gmail.com>
* Copyright (c) 2016 Yuxuan Shui <yshuiv7@gmail.com>
@@ -65,14 +65,15 @@ static __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc,
rdesc[47] = 0x00;
}
break;
- case USB_DEVICE_ID_ELECOM_EX_G_WIRED:
- case USB_DEVICE_ID_ELECOM_EX_G_WIRELESS:
+ case USB_DEVICE_ID_ELECOM_M_XT3URBK:
+ case USB_DEVICE_ID_ELECOM_M_XT3DRBK:
+ case USB_DEVICE_ID_ELECOM_M_XT4DRBK:
mouse_button_fixup(hdev, rdesc, *rsize, 6);
break;
- case USB_DEVICE_ID_ELECOM_DEFT_WIRED:
- case USB_DEVICE_ID_ELECOM_DEFT_WIRELESS:
- case USB_DEVICE_ID_ELECOM_HUGE_WIRED:
- case USB_DEVICE_ID_ELECOM_HUGE_WIRELESS:
+ case USB_DEVICE_ID_ELECOM_M_DT1URBK:
+ case USB_DEVICE_ID_ELECOM_M_DT1DRBK:
+ case USB_DEVICE_ID_ELECOM_M_HT1URBK:
+ case USB_DEVICE_ID_ELECOM_M_HT1DRBK:
mouse_button_fixup(hdev, rdesc, *rsize, 8);
break;
}
@@ -81,12 +82,13 @@ static __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc,
static const struct hid_device_id elecom_devices[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) },
- { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_EX_G_WIRED) },
- { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_EX_G_WIRELESS) },
- { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_DEFT_WIRED) },
- { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_DEFT_WIRELESS) },
- { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_HUGE_WIRED) },
- { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_HUGE_WIRELESS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3DRBK) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1URBK) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1DRBK) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1URBK) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1DRBK) },
{ }
};
MODULE_DEVICE_TABLE(hid, elecom_devices);
diff --git a/drivers/hid/hid-generic.c b/drivers/hid/hid-generic.c
index 3c0a1bf433d7..c25b4718de44 100644
--- a/drivers/hid/hid-generic.c
+++ b/drivers/hid/hid-generic.c
@@ -26,37 +26,6 @@
static struct hid_driver hid_generic;
-static int __unmap_hid_generic(struct device *dev, void *data)
-{
- struct hid_driver *hdrv = data;
- struct hid_device *hdev = to_hid_device(dev);
-
- /* only unbind matching devices already bound to hid-generic */
- if (hdev->driver != &hid_generic ||
- hid_match_device(hdev, hdrv) == NULL)
- return 0;
-
- if (dev->parent) /* Needed for USB */
- device_lock(dev->parent);
- device_release_driver(dev);
- if (dev->parent)
- device_unlock(dev->parent);
-
- return 0;
-}
-
-static void hid_generic_add_driver(struct hid_driver *hdrv)
-{
- bus_for_each_dev(&hid_bus_type, NULL, hdrv, __unmap_hid_generic);
-}
-
-static void hid_generic_removed_driver(struct hid_driver *hdrv)
-{
- int ret;
-
- ret = driver_attach(&hid_generic.driver);
-}
-
static int __check_hid_generic(struct device_driver *drv, void *data)
{
struct hid_driver *hdrv = to_hid_driver(drv);
@@ -97,8 +66,6 @@ static struct hid_driver hid_generic = {
.name = "hid-generic",
.id_table = hid_table,
.match = hid_generic_match,
- .bus_add_driver = hid_generic_add_driver,
- .bus_removed_driver = hid_generic_removed_driver,
};
module_hid_driver(hid_generic);
diff --git a/drivers/hid/hid-google-hammer.c b/drivers/hid/hid-google-hammer.c
new file mode 100644
index 000000000000..7b8e17b03cb8
--- /dev/null
+++ b/drivers/hid/hid-google-hammer.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * HID driver for Google Hammer device.
+ *
+ * Copyright (c) 2017 Google Inc.
+ * Author: Wei-Ning Huang <wnhuang@google.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.
+ */
+
+#include <linux/hid.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define MAX_BRIGHTNESS 100
+
+/* HID usage for keyboard backlight (Alphanumeric display brightness) */
+#define HID_AD_BRIGHTNESS 0x00140046
+
+struct hammer_kbd_leds {
+ struct led_classdev cdev;
+ struct hid_device *hdev;
+ u8 buf[2] ____cacheline_aligned;
+};
+
+static int hammer_kbd_brightness_set_blocking(struct led_classdev *cdev,
+ enum led_brightness br)
+{
+ struct hammer_kbd_leds *led = container_of(cdev,
+ struct hammer_kbd_leds,
+ cdev);
+ int ret;
+
+ led->buf[0] = 0;
+ led->buf[1] = br;
+
+ /*
+ * Request USB HID device to be in Full On mode, so that sending
+ * hardware output report and hardware raw request won't fail.
+ */
+ ret = hid_hw_power(led->hdev, PM_HINT_FULLON);
+ if (ret < 0) {
+ hid_err(led->hdev, "failed: device not resumed %d\n", ret);
+ return ret;
+ }
+
+ ret = hid_hw_output_report(led->hdev, led->buf, sizeof(led->buf));
+ if (ret == -ENOSYS)
+ ret = hid_hw_raw_request(led->hdev, 0, led->buf,
+ sizeof(led->buf),
+ HID_OUTPUT_REPORT,
+ HID_REQ_SET_REPORT);
+ if (ret < 0)
+ hid_err(led->hdev, "failed to set keyboard backlight: %d\n",
+ ret);
+
+ /* Request USB HID device back to Normal Mode. */
+ hid_hw_power(led->hdev, PM_HINT_NORMAL);
+
+ return ret;
+}
+
+static int hammer_register_leds(struct hid_device *hdev)
+{
+ struct hammer_kbd_leds *kbd_backlight;
+
+ kbd_backlight = devm_kzalloc(&hdev->dev,
+ sizeof(*kbd_backlight),
+ GFP_KERNEL);
+ if (!kbd_backlight)
+ return -ENOMEM;
+
+ kbd_backlight->hdev = hdev;
+ kbd_backlight->cdev.name = "hammer::kbd_backlight";
+ kbd_backlight->cdev.max_brightness = MAX_BRIGHTNESS;
+ kbd_backlight->cdev.brightness_set_blocking =
+ hammer_kbd_brightness_set_blocking;
+ kbd_backlight->cdev.flags = LED_HW_PLUGGABLE;
+
+ /* Set backlight to 0% initially. */
+ hammer_kbd_brightness_set_blocking(&kbd_backlight->cdev, 0);
+
+ return devm_led_classdev_register(&hdev->dev, &kbd_backlight->cdev);
+}
+
+static int hammer_input_configured(struct hid_device *hdev,
+ struct hid_input *hi)
+{
+ struct list_head *report_list =
+ &hdev->report_enum[HID_OUTPUT_REPORT].report_list;
+ struct hid_report *report;
+
+ if (list_empty(report_list))
+ return 0;
+
+ report = list_first_entry(report_list, struct hid_report, list);
+
+ if (report->maxfield == 1 &&
+ report->field[0]->application == HID_GD_KEYBOARD &&
+ report->field[0]->maxusage == 1 &&
+ report->field[0]->usage[0].hid == HID_AD_BRIGHTNESS) {
+ int err = hammer_register_leds(hdev);
+
+ if (err)
+ hid_warn(hdev,
+ "Failed to register keyboard backlight: %d\n",
+ err);
+ }
+
+ return 0;
+}
+
+static const struct hid_device_id hammer_devices[] = {
+ { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
+ USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_HAMMER) },
+ { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
+ USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_STAFF) },
+ { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
+ USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_WAND) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, hammer_devices);
+
+static struct hid_driver hammer_driver = {
+ .name = "hammer",
+ .id_table = hammer_devices,
+ .input_configured = hammer_input_configured,
+};
+module_hid_driver(hammer_driver);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 9454ac134ce2..0b5cc910f62e 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -291,6 +291,7 @@
#define USB_DEVICE_ID_CORSAIR_K70RGB 0x1b13
#define USB_DEVICE_ID_CORSAIR_STRAFE 0x1b15
#define USB_DEVICE_ID_CORSAIR_K65RGB 0x1b17
+#define USB_DEVICE_ID_CORSAIR_GLAIVE_RGB 0x1b34
#define USB_DEVICE_ID_CORSAIR_K70RGB_RAPIDFIRE 0x1b38
#define USB_DEVICE_ID_CORSAIR_K65RGB_RAPIDFIRE 0x1b39
#define USB_DEVICE_ID_CORSAIR_SCIMITAR_PRO_RGB 0x1b3e
@@ -368,15 +369,17 @@
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001
#define USB_VENDOR_ID_ELAN 0x04f3
+#define USB_DEVICE_ID_HP_X2_10_COVER 0x0755
#define USB_VENDOR_ID_ELECOM 0x056e
#define USB_DEVICE_ID_ELECOM_BM084 0x0061
-#define USB_DEVICE_ID_ELECOM_EX_G_WIRED 0x00fb
-#define USB_DEVICE_ID_ELECOM_EX_G_WIRELESS 0x00fc
-#define USB_DEVICE_ID_ELECOM_DEFT_WIRED 0x00fe
-#define USB_DEVICE_ID_ELECOM_DEFT_WIRELESS 0x00ff
-#define USB_DEVICE_ID_ELECOM_HUGE_WIRED 0x010c
-#define USB_DEVICE_ID_ELECOM_HUGE_WIRELESS 0x010d
+#define USB_DEVICE_ID_ELECOM_M_XT3URBK 0x00fb
+#define USB_DEVICE_ID_ELECOM_M_XT3DRBK 0x00fc
+#define USB_DEVICE_ID_ELECOM_M_XT4DRBK 0x00fd
+#define USB_DEVICE_ID_ELECOM_M_DT1URBK 0x00fe
+#define USB_DEVICE_ID_ELECOM_M_DT1DRBK 0x00ff
+#define USB_DEVICE_ID_ELECOM_M_HT1URBK 0x010c
+#define USB_DEVICE_ID_ELECOM_M_HT1DRBK 0x010d
#define USB_VENDOR_ID_DREAM_CHEEKY 0x1d34
#define USB_DEVICE_ID_DREAM_CHEEKY_WN 0x0004
@@ -445,7 +448,10 @@
#define USB_DEVICE_ID_GOODTOUCH_000f 0x000f
#define USB_VENDOR_ID_GOOGLE 0x18d1
+#define USB_DEVICE_ID_GOOGLE_HAMMER 0x5022
#define USB_DEVICE_ID_GOOGLE_TOUCH_ROSE 0x5028
+#define USB_DEVICE_ID_GOOGLE_STAFF 0x502b
+#define USB_DEVICE_ID_GOOGLE_WAND 0x502d
#define USB_VENDOR_ID_GOTOP 0x08f2
#define USB_DEVICE_ID_SUPER_Q2 0x007f
@@ -519,6 +525,9 @@
#define I2C_VENDOR_ID_HANTICK 0x0911
#define I2C_PRODUCT_ID_HANTICK_5288 0x5288
+#define I2C_VENDOR_ID_RAYD 0x2386
+#define I2C_PRODUCT_ID_RAYD_3118 0x3118
+
#define USB_VENDOR_ID_HANWANG 0x0b57
#define USB_DEVICE_ID_HANWANG_TABLET_FIRST 0x5000
#define USB_DEVICE_ID_HANWANG_TABLET_LAST 0x8fff
@@ -961,6 +970,9 @@
#define USB_VENDOR_ID_SMK 0x0609
#define USB_DEVICE_ID_SMK_PS3_BDREMOTE 0x0306
+#define USB_DEVICE_ID_SMK_NSG_MR5U_REMOTE 0x0368
+#define USB_DEVICE_ID_SMK_NSG_MR7U_REMOTE 0x0369
+
#define USB_VENDOR_ID_SONY 0x054c
#define USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE 0x024b
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 04d01b57d94c..930652c25120 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -387,7 +387,8 @@ static int hidinput_get_battery_property(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_CAPACITY:
- if (dev->battery_report_type == HID_FEATURE_REPORT) {
+ if (dev->battery_status != HID_BATTERY_REPORTED &&
+ !dev->battery_avoid_query) {
value = hidinput_query_battery_capacity(dev);
if (value < 0)
return value;
@@ -403,17 +404,17 @@ static int hidinput_get_battery_property(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_STATUS:
- if (!dev->battery_reported &&
- dev->battery_report_type == HID_FEATURE_REPORT) {
+ if (dev->battery_status != HID_BATTERY_REPORTED &&
+ !dev->battery_avoid_query) {
value = hidinput_query_battery_capacity(dev);
if (value < 0)
return value;
dev->battery_capacity = value;
- dev->battery_reported = true;
+ dev->battery_status = HID_BATTERY_QUERIED;
}
- if (!dev->battery_reported)
+ if (dev->battery_status == HID_BATTERY_UNKNOWN)
val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
else if (dev->battery_capacity == 100)
val->intval = POWER_SUPPLY_STATUS_FULL;
@@ -486,6 +487,14 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
dev->battery_report_type = report_type;
dev->battery_report_id = field->report->id;
+ /*
+ * Stylus is normally not connected to the device and thus we
+ * can't query the device and get meaningful battery strength.
+ * We have to wait for the device to report it on its own.
+ */
+ dev->battery_avoid_query = report_type == HID_INPUT_REPORT &&
+ field->physical == HID_DG_STYLUS;
+
dev->battery = power_supply_register(&dev->dev, psy_desc, &psy_cfg);
if (IS_ERR(dev->battery)) {
error = PTR_ERR(dev->battery);
@@ -530,9 +539,10 @@ static void hidinput_update_battery(struct hid_device *dev, int value)
capacity = hidinput_scale_battery_capacity(dev, value);
- if (!dev->battery_reported || capacity != dev->battery_capacity) {
+ if (dev->battery_status != HID_BATTERY_REPORTED ||
+ capacity != dev->battery_capacity) {
dev->battery_capacity = capacity;
- dev->battery_reported = true;
+ dev->battery_status = HID_BATTERY_REPORTED;
power_supply_changed(dev->battery);
}
}
@@ -1368,7 +1378,8 @@ static void hidinput_led_worker(struct work_struct *work)
led_work);
struct hid_field *field;
struct hid_report *report;
- int len, ret;
+ int ret;
+ u32 len;
__u8 *buf;
field = hidinput_get_led_field(hid);
@@ -1656,16 +1667,16 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
}
list_for_each_entry_safe(hidinput, next, &hid->inputs, list) {
- if ((hid->quirks & HID_QUIRK_NO_EMPTY_INPUT) &&
- !hidinput_has_been_populated(hidinput)) {
+ if (drv->input_configured &&
+ drv->input_configured(hid, hidinput))
+ goto out_unwind;
+
+ if (!hidinput_has_been_populated(hidinput)) {
/* no need to register an input device not populated */
hidinput_cleanup_hidinput(hid, hidinput);
continue;
}
- if (drv->input_configured &&
- drv->input_configured(hid, hidinput))
- goto out_unwind;
if (input_register_device(hidinput->input))
goto out_unwind;
hidinput->registered = true;
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 3b4739bde05d..dad2fbb0e3f8 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -74,6 +74,7 @@ MODULE_LICENSE("GPL");
#define MT_QUIRK_TOUCH_SIZE_SCALING BIT(15)
#define MT_QUIRK_STICKY_FINGERS BIT(16)
#define MT_QUIRK_ASUS_CUSTOM_UP BIT(17)
+#define MT_QUIRK_WIN8_PTP_BUTTONS BIT(18)
#define MT_INPUTMODE_TOUCHSCREEN 0x02
#define MT_INPUTMODE_TOUCHPAD 0x03
@@ -126,7 +127,6 @@ struct mt_device {
int left_button_state; /* left button state */
unsigned last_slot_field; /* the last field of a slot */
unsigned mt_report_id; /* the report ID of the multitouch device */
- unsigned long initial_quirks; /* initial quirks state */
__s16 inputmode; /* InputMode HID feature, -1 if non-existent */
__s16 inputmode_index; /* InputMode HID feature index in the report */
__s16 maxcontact_report_id; /* Maximum Contact Number HID feature,
@@ -183,6 +183,7 @@ static void mt_post_parse(struct mt_device *td);
#define MT_CLS_ASUS 0x010b
#define MT_CLS_VTL 0x0110
#define MT_CLS_GOOGLE 0x0111
+#define MT_CLS_RAZER_BLADE_STEALTH 0x0112
#define MT_DEFAULT_MAXCONTACT 10
#define MT_MAX_MAXCONTACT 250
@@ -241,7 +242,8 @@ static struct mt_class mt_classes[] = {
MT_QUIRK_IGNORE_DUPLICATES |
MT_QUIRK_HOVERING |
MT_QUIRK_CONTACT_CNT_ACCURATE |
- MT_QUIRK_STICKY_FINGERS },
+ MT_QUIRK_STICKY_FINGERS |
+ MT_QUIRK_WIN8_PTP_BUTTONS },
{ .name = MT_CLS_EXPORT_ALL_INPUTS,
.quirks = MT_QUIRK_ALWAYS_VALID |
MT_QUIRK_CONTACT_CNT_ACCURATE,
@@ -250,7 +252,8 @@ static struct mt_class mt_classes[] = {
.quirks = MT_QUIRK_ALWAYS_VALID |
MT_QUIRK_IGNORE_DUPLICATES |
MT_QUIRK_HOVERING |
- MT_QUIRK_CONTACT_CNT_ACCURATE,
+ MT_QUIRK_CONTACT_CNT_ACCURATE |
+ MT_QUIRK_WIN8_PTP_BUTTONS,
.export_all_inputs = true },
/*
@@ -323,6 +326,13 @@ static struct mt_class mt_classes[] = {
MT_QUIRK_SLOT_IS_CONTACTID |
MT_QUIRK_HOVERING
},
+ { .name = MT_CLS_RAZER_BLADE_STEALTH,
+ .quirks = MT_QUIRK_ALWAYS_VALID |
+ MT_QUIRK_IGNORE_DUPLICATES |
+ MT_QUIRK_HOVERING |
+ MT_QUIRK_CONTACT_CNT_ACCURATE |
+ MT_QUIRK_WIN8_PTP_BUTTONS,
+ },
{ }
};
@@ -369,15 +379,15 @@ static const struct attribute_group mt_attribute_group = {
static void mt_get_feature(struct hid_device *hdev, struct hid_report *report)
{
- struct mt_device *td = hid_get_drvdata(hdev);
- int ret, size = hid_report_len(report);
+ int ret;
+ u32 size = hid_report_len(report);
u8 *buf;
/*
* Do not fetch the feature report if the device has been explicitly
* marked as non-capable.
*/
- if (td->initial_quirks & HID_QUIRK_NO_INIT_REPORTS)
+ if (hdev->quirks & HID_QUIRK_NO_INIT_REPORTS)
return;
buf = hid_alloc_report_buf(report, GFP_KERNEL);
@@ -659,8 +669,7 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
* MS PTP spec says that external buttons left and right have
* usages 2 and 3.
*/
- if ((cls->name == MT_CLS_WIN_8 ||
- cls->name == MT_CLS_WIN_8_DUAL) &&
+ if ((cls->quirks & MT_QUIRK_WIN8_PTP_BUTTONS) &&
field->application == HID_DG_TOUCHPAD &&
(usage->hid & HID_USAGE) > 1)
code--;
@@ -722,7 +731,7 @@ static void mt_complete_slot(struct mt_device *td, struct input_dev *input)
}
if (!(td->mtclass.quirks & MT_QUIRK_CONFIDENCE))
- s->confidence_state = 1;
+ s->confidence_state = true;
active = (s->touch_state || s->inrange_state) &&
s->confidence_state;
@@ -772,9 +781,7 @@ static void mt_complete_slot(struct mt_device *td, struct input_dev *input)
*/
static void mt_sync_frame(struct mt_device *td, struct input_dev *input)
{
- __s32 cls = td->mtclass.name;
-
- if (cls == MT_CLS_WIN_8 || cls == MT_CLS_WIN_8_DUAL)
+ if (td->mtclass.quirks & MT_QUIRK_WIN8_PTP_BUTTONS)
input_event(input, EV_KEY, BTN_LEFT, td->left_button_state);
input_mt_sync_frame(input);
@@ -826,7 +833,6 @@ static void mt_process_mt_event(struct hid_device *hid, struct hid_field *field,
bool first_packet)
{
struct mt_device *td = hid_get_drvdata(hid);
- __s32 cls = td->mtclass.name;
__s32 quirks = td->mtclass.quirks;
struct input_dev *input = field->hidinput->input;
@@ -904,7 +910,7 @@ static void mt_process_mt_event(struct hid_device *hid, struct hid_field *field,
* non finger/touch events in the first_packet of
* a (possible) multi-packet frame.
*/
- if ((cls == MT_CLS_WIN_8 || cls == MT_CLS_WIN_8_DUAL) &&
+ if ((quirks & MT_QUIRK_WIN8_PTP_BUTTONS) &&
!first_packet)
return;
@@ -915,7 +921,7 @@ static void mt_process_mt_event(struct hid_device *hid, struct hid_field *field,
* BTN_LEFT if either is pressed, so we or all values
* together and report the result in mt_sync_frame().
*/
- if ((cls == MT_CLS_WIN_8 || cls == MT_CLS_WIN_8_DUAL) &&
+ if ((quirks & MT_QUIRK_WIN8_PTP_BUTTONS) &&
usage->type == EV_KEY && usage->code == BTN_LEFT) {
td->left_button_state |= value;
return;
@@ -939,7 +945,6 @@ static void mt_process_mt_event(struct hid_device *hid, struct hid_field *field,
static void mt_touch_report(struct hid_device *hid, struct hid_report *report)
{
struct mt_device *td = hid_get_drvdata(hid);
- __s32 cls = td->mtclass.name;
struct hid_field *field;
bool first_packet;
unsigned count;
@@ -968,7 +973,7 @@ static void mt_touch_report(struct hid_device *hid, struct hid_report *report)
* of a possible multi-packet frame be checking that the
* timestamp has changed.
*/
- if ((cls == MT_CLS_WIN_8 || cls == MT_CLS_WIN_8_DUAL) &&
+ if ((td->mtclass.quirks & MT_QUIRK_WIN8_PTP_BUTTONS) &&
td->num_received == 0 && td->prev_scantime != scantime)
td->num_expected = value;
/* A non 0 contact count always indicates a first packet */
@@ -1183,7 +1188,7 @@ static void mt_set_input_mode(struct hid_device *hdev)
struct hid_report_enum *re;
struct mt_class *cls = &td->mtclass;
char *buf;
- int report_len;
+ u32 report_len;
if (td->inputmode < 0)
return;
@@ -1447,11 +1452,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (id->vendor == HID_ANY_ID && id->product == HID_ANY_ID)
td->serial_maybe = true;
- /*
- * Store the initial quirk state
- */
- td->initial_quirks = hdev->quirks;
-
/* This allows the driver to correctly support devices
* that emit events over several HID messages.
*/
@@ -1463,22 +1463,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
* device.
*/
hdev->quirks |= HID_QUIRK_MULTI_INPUT;
- hdev->quirks |= HID_QUIRK_NO_EMPTY_INPUT;
-
- /*
- * Some multitouch screens do not like to be polled for input
- * reports. Fortunately, the Win8 spec says that all touches
- * should be sent during each report, making the initialization
- * of input reports unnecessary. For Win7 devices, well, let's hope
- * they will still be happy (this is only be a problem if a touch
- * was already there while probing the device).
- *
- * In addition some touchpads do not behave well if we read
- * all feature reports from them. Instead we prevent
- * initial report fetching and then selectively fetch each
- * report we are interested in.
- */
- hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
timer_setup(&td->release_timer, mt_expired_timeout, 0);
@@ -1537,7 +1521,6 @@ static void mt_remove(struct hid_device *hdev)
sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group);
hid_hw_stop(hdev);
- hdev->quirks = td->initial_quirks;
}
/*
@@ -1793,6 +1776,11 @@ static const struct hid_device_id mt_devices[] = {
MT_USB_DEVICE(USB_VENDOR_ID_QUANTA,
USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001) },
+ /* Razer touchpads */
+ { .driver_data = MT_CLS_RAZER_BLADE_STEALTH,
+ HID_DEVICE(BUS_I2C, HID_GROUP_MULTITOUCH_WIN_8,
+ USB_VENDOR_ID_SYNAPTICS, 0x8323) },
+
/* Stantum panels */
{ .driver_data = MT_CLS_CONFIDENCE,
MT_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM,
diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c
index 3d121d8ee980..43b1c7234316 100644
--- a/drivers/hid/hid-ntrig.c
+++ b/drivers/hid/hid-ntrig.c
@@ -591,8 +591,8 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
switch (usage->hid) {
case 0xff000001:
/* Tag indicating the start of a multitouch group */
- nd->reading_mt = 1;
- nd->first_contact_touch = 0;
+ nd->reading_mt = true;
+ nd->first_contact_touch = false;
break;
case HID_DG_TIPSWITCH:
nd->tipswitch = value;
@@ -663,7 +663,7 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
* even if deactivation slack is turned off.
*/
nd->act_state = deactivate_slack - 1;
- nd->confidence = 0;
+ nd->confidence = false;
break;
}
@@ -679,7 +679,7 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
*/
if (nd->w < nd->min_width ||
nd->h < nd->min_height)
- nd->confidence = 0;
+ nd->confidence = false;
} else
break;
@@ -758,7 +758,7 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
if (!nd->reading_mt) /* Just to be sure */
break;
- nd->reading_mt = 0;
+ nd->reading_mt = false;
/*
@@ -910,7 +910,7 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id)
return -ENOMEM;
}
- nd->reading_mt = 0;
+ nd->reading_mt = false;
nd->min_width = 0;
nd->min_height = 0;
nd->activate_slack = activate_slack;
diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
index e92b77fa574a..587e2681a53f 100644
--- a/drivers/hid/hid-quirks.c
+++ b/drivers/hid/hid-quirks.c
@@ -62,6 +62,7 @@ static const struct hid_device_id hid_quirks[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K70R), HID_QUIRK_NO_INIT_REPORTS },
{ HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K95RGB), HID_QUIRK_NO_INIT_REPORTS | HID_QUIRK_ALWAYS_POLL },
{ HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_M65RGB), HID_QUIRK_NO_INIT_REPORTS },
+ { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_GLAIVE_RGB), HID_QUIRK_NO_INIT_REPORTS | HID_QUIRK_ALWAYS_POLL },
{ HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_SCIMITAR_PRO_RGB), HID_QUIRK_NO_INIT_REPORTS | HID_QUIRK_ALWAYS_POLL },
{ HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_STRAFE), HID_QUIRK_NO_INIT_REPORTS | HID_QUIRK_ALWAYS_POLL },
{ HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_CREATIVE_SB_OMNI_SURROUND_51), HID_QUIRK_NOGET },
@@ -317,6 +318,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
#endif
#if IS_ENABLED(CONFIG_HID_CORSAIR)
{ HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K90) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_GLAIVE_RGB) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_SCIMITAR_PRO_RGB) },
#endif
#if IS_ENABLED(CONFIG_HID_CP2112)
@@ -333,14 +335,18 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) },
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011) },
#endif
+#if IS_ENABLED(CONFIG_HID_ELAN)
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_HP_X2_10_COVER) },
+#endif
#if IS_ENABLED(CONFIG_HID_ELECOM)
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) },
- { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_EX_G_WIRED) },
- { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_EX_G_WIRELESS) },
- { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_DEFT_WIRED) },
- { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_DEFT_WIRELESS) },
- { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_HUGE_WIRED) },
- { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_HUGE_WIRELESS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3DRBK) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1URBK) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1DRBK) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1URBK) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1DRBK) },
#endif
#if IS_ENABLED(CONFIG_HID_ELO)
{ HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0009) },
@@ -608,6 +614,8 @@ static const struct hid_device_id hid_have_special_driver[] = {
#if IS_ENABLED(CONFIG_HID_SONY)
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_PS3_BDREMOTE) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_NSG_MR5U_REMOTE) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_NSG_MR7U_REMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_BUZZ_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER) },
diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c
index c6c05df3e8d2..9c9362149641 100644
--- a/drivers/hid/hid-rmi.c
+++ b/drivers/hid/hid-rmi.c
@@ -89,8 +89,8 @@ struct rmi_data {
u8 *writeReport;
u8 *readReport;
- int input_report_size;
- int output_report_size;
+ u32 input_report_size;
+ u32 output_report_size;
unsigned long flags;
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index ccdc5f2d01b1..e475c5073c99 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -9,6 +9,7 @@
* Copyright (c) 2006-2013 Jiri Kosina
* Copyright (c) 2013 Colin Leitner <colin.leitner@gmail.com>
* Copyright (c) 2014-2016 Frank Praznik <frank.praznik@gmail.com>
+ * Copyright (c) 2018 Todd Kelner
*/
/*
@@ -55,6 +56,8 @@
#define NAVIGATION_CONTROLLER_BT BIT(11)
#define SINO_LITE_CONTROLLER BIT(12)
#define FUTUREMAX_DANCE_MAT BIT(13)
+#define NSG_MR5U_REMOTE_BT BIT(14)
+#define NSG_MR7U_REMOTE_BT BIT(15)
#define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT)
#define MOTION_CONTROLLER (MOTION_CONTROLLER_USB | MOTION_CONTROLLER_BT)
@@ -72,8 +75,11 @@
MOTION_CONTROLLER)
#define SONY_BT_DEVICE (SIXAXIS_CONTROLLER_BT | DUALSHOCK4_CONTROLLER_BT |\
MOTION_CONTROLLER_BT | NAVIGATION_CONTROLLER_BT)
+#define NSG_MRXU_REMOTE (NSG_MR5U_REMOTE_BT | NSG_MR7U_REMOTE_BT)
#define MAX_LEDS 4
+#define NSG_MRXU_MAX_X 1667
+#define NSG_MRXU_MAX_Y 1868
/* PS/3 Motion controller */
@@ -1098,6 +1104,80 @@ static void dualshock4_parse_report(struct sony_sc *sc, u8 *rd, int size)
}
}
+static void nsg_mrxu_parse_report(struct sony_sc *sc, u8 *rd, int size)
+{
+ int n, offset, relx, rely;
+ u8 active;
+
+ /*
+ * The NSG-MRxU multi-touch trackpad data starts at offset 1 and
+ * the touch-related data starts at offset 2.
+ * For the first byte, bit 0 is set when touchpad button is pressed.
+ * Bit 2 is set when a touch is active and the drag (Fn) key is pressed.
+ * This drag key is mapped to BTN_LEFT. It is operational only when a
+ * touch point is active.
+ * Bit 4 is set when only the first touch point is active.
+ * Bit 6 is set when only the second touch point is active.
+ * Bits 5 and 7 are set when both touch points are active.
+ * The next 3 bytes are two 12 bit X/Y coordinates for the first touch.
+ * The following byte, offset 5, has the touch width and length.
+ * Bits 0-4=X (width), bits 5-7=Y (length).
+ * A signed relative X coordinate is at offset 6.
+ * The bytes at offset 7-9 are the second touch X/Y coordinates.
+ * Offset 10 has the second touch width and length.
+ * Offset 11 has the relative Y coordinate.
+ */
+ offset = 1;
+
+ input_report_key(sc->touchpad, BTN_LEFT, rd[offset] & 0x0F);
+ active = (rd[offset] >> 4);
+ relx = (s8) rd[offset+5];
+ rely = ((s8) rd[offset+10]) * -1;
+
+ offset++;
+
+ for (n = 0; n < 2; n++) {
+ u16 x, y;
+ u8 contactx, contacty;
+
+ x = rd[offset] | ((rd[offset+1] & 0x0F) << 8);
+ y = ((rd[offset+1] & 0xF0) >> 4) | (rd[offset+2] << 4);
+
+ input_mt_slot(sc->touchpad, n);
+ input_mt_report_slot_state(sc->touchpad, MT_TOOL_FINGER, active & 0x03);
+
+ if (active & 0x03) {
+ contactx = rd[offset+3] & 0x0F;
+ contacty = rd[offset+3] >> 4;
+ input_report_abs(sc->touchpad, ABS_MT_TOUCH_MAJOR,
+ max(contactx, contacty));
+ input_report_abs(sc->touchpad, ABS_MT_TOUCH_MINOR,
+ min(contactx, contacty));
+ input_report_abs(sc->touchpad, ABS_MT_ORIENTATION,
+ (bool) (contactx > contacty));
+ input_report_abs(sc->touchpad, ABS_MT_POSITION_X, x);
+ input_report_abs(sc->touchpad, ABS_MT_POSITION_Y,
+ NSG_MRXU_MAX_Y - y);
+ /*
+ * The relative coordinates belong to the first touch
+ * point, when present, or to the second touch point
+ * when the first is not active.
+ */
+ if ((n == 0) || ((n == 1) && (active & 0x01))) {
+ input_report_rel(sc->touchpad, REL_X, relx);
+ input_report_rel(sc->touchpad, REL_Y, rely);
+ }
+ }
+
+ offset += 5;
+ active >>= 2;
+ }
+
+ input_mt_sync_frame(sc->touchpad);
+
+ input_sync(sc->touchpad);
+}
+
static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
u8 *rd, int size)
{
@@ -1206,6 +1286,10 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
}
dualshock4_parse_report(sc, rd, size);
+
+ } else if ((sc->quirks & NSG_MRXU_REMOTE) && rd[0] == 0x02) {
+ nsg_mrxu_parse_report(sc, rd, size);
+ return 1;
}
if (sc->defer_initialization) {
@@ -1263,7 +1347,7 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi,
}
static int sony_register_touchpad(struct sony_sc *sc, int touch_count,
- int w, int h)
+ int w, int h, int touch_major, int touch_minor, int orientation)
{
size_t name_sz;
char *name;
@@ -1294,10 +1378,6 @@ static int sony_register_touchpad(struct sony_sc *sc, int touch_count,
snprintf(name, name_sz, "%s" DS4_TOUCHPAD_SUFFIX, sc->hdev->name);
sc->touchpad->name = name;
- ret = input_mt_init_slots(sc->touchpad, touch_count, INPUT_MT_POINTER);
- if (ret < 0)
- goto err;
-
/* We map the button underneath the touchpad to BTN_LEFT. */
__set_bit(EV_KEY, sc->touchpad->evbit);
__set_bit(BTN_LEFT, sc->touchpad->keybit);
@@ -1306,6 +1386,25 @@ static int sony_register_touchpad(struct sony_sc *sc, int touch_count,
input_set_abs_params(sc->touchpad, ABS_MT_POSITION_X, 0, w, 0, 0);
input_set_abs_params(sc->touchpad, ABS_MT_POSITION_Y, 0, h, 0, 0);
+ if (touch_major > 0) {
+ input_set_abs_params(sc->touchpad, ABS_MT_TOUCH_MAJOR,
+ 0, touch_major, 0, 0);
+ if (touch_minor > 0)
+ input_set_abs_params(sc->touchpad, ABS_MT_TOUCH_MINOR,
+ 0, touch_minor, 0, 0);
+ if (orientation > 0)
+ input_set_abs_params(sc->touchpad, ABS_MT_ORIENTATION,
+ 0, orientation, 0, 0);
+ }
+
+ if (sc->quirks & NSG_MRXU_REMOTE) {
+ __set_bit(EV_REL, sc->touchpad->evbit);
+ }
+
+ ret = input_mt_init_slots(sc->touchpad, touch_count, INPUT_MT_POINTER);
+ if (ret < 0)
+ goto err;
+
ret = input_register_device(sc->touchpad);
if (ret < 0)
goto err;
@@ -2690,7 +2789,7 @@ static int sony_input_configured(struct hid_device *hdev,
* The Dualshock 4 touchpad supports 2 touches and has a
* resolution of 1920x942 (44.86 dots/mm).
*/
- ret = sony_register_touchpad(sc, 2, 1920, 942);
+ ret = sony_register_touchpad(sc, 2, 1920, 942, 0, 0, 0);
if (ret) {
hid_err(sc->hdev,
"Unable to initialize multi-touch slots: %d\n",
@@ -2721,6 +2820,20 @@ static int sony_input_configured(struct hid_device *hdev,
}
sony_init_output_report(sc, dualshock4_send_output_report);
+ } else if (sc->quirks & NSG_MRXU_REMOTE) {
+ /*
+ * The NSG-MRxU touchpad supports 2 touches and has a
+ * resolution of 1667x1868
+ */
+ ret = sony_register_touchpad(sc, 2,
+ NSG_MRXU_MAX_X, NSG_MRXU_MAX_Y, 15, 15, 1);
+ if (ret) {
+ hid_err(sc->hdev,
+ "Unable to initialize multi-touch slots: %d\n",
+ ret);
+ goto err_stop;
+ }
+
} else if (sc->quirks & MOTION_CONTROLLER) {
sony_init_output_report(sc, motion_send_output_report);
} else {
@@ -2969,6 +3082,12 @@ static const struct hid_device_id sony_devices[] = {
/* Nyko Core Controller for PS3 */
{ HID_USB_DEVICE(USB_VENDOR_ID_SINO_LITE, USB_DEVICE_ID_SINO_LITE_CONTROLLER),
.driver_data = SIXAXIS_CONTROLLER_USB | SINO_LITE_CONTROLLER },
+ /* SMK-Link NSG-MR5U Remote Control */
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_NSG_MR5U_REMOTE),
+ .driver_data = NSG_MR5U_REMOTE_BT },
+ /* SMK-Link NSG-MR7U Remote Control */
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_NSG_MR7U_REMOTE),
+ .driver_data = NSG_MR7U_REMOTE_BT },
{ }
};
MODULE_DEVICE_TABLE(hid, sony_devices);
diff --git a/drivers/hid/hid-uclogic.c b/drivers/hid/hid-uclogic.c
index e3e6e5c893cc..56b196d60041 100644
--- a/drivers/hid/hid-uclogic.c
+++ b/drivers/hid/hid-uclogic.c
@@ -946,7 +946,6 @@ static int uclogic_probe(struct hid_device *hdev,
* than the pen, so use QUIRK_MULTI_INPUT for all tablets.
*/
hdev->quirks |= HID_QUIRK_MULTI_INPUT;
- hdev->quirks |= HID_QUIRK_NO_EMPTY_INPUT;
/* Allocate and assign driver data */
drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL);
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c
index fbfcc8009432..b39844adea47 100644
--- a/drivers/hid/hidraw.c
+++ b/drivers/hid/hidraw.c
@@ -192,6 +192,11 @@ static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t
int ret = 0, len;
unsigned char report_number;
+ if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
+ ret = -ENODEV;
+ goto out;
+ }
+
dev = hidraw_table[minor]->hid;
if (!dev->ll_driver->raw_request) {
diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c
index 7230243b94d3..963328674e93 100644
--- a/drivers/hid/i2c-hid/i2c-hid.c
+++ b/drivers/hid/i2c-hid/i2c-hid.c
@@ -47,6 +47,7 @@
/* quirks to control the device */
#define I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV BIT(0)
#define I2C_HID_QUIRK_NO_IRQ_AFTER_RESET BIT(1)
+#define I2C_HID_QUIRK_RESEND_REPORT_DESCR BIT(2)
/* flags */
#define I2C_HID_STARTED 0
@@ -144,10 +145,10 @@ struct i2c_hid {
* register of the HID
* descriptor. */
unsigned int bufsize; /* i2c buffer size */
- char *inbuf; /* Input buffer */
- char *rawbuf; /* Raw Input buffer */
- char *cmdbuf; /* Command buffer */
- char *argsbuf; /* Command arguments buffer */
+ u8 *inbuf; /* Input buffer */
+ u8 *rawbuf; /* Raw Input buffer */
+ u8 *cmdbuf; /* Command buffer */
+ u8 *argsbuf; /* Command arguments buffer */
unsigned long flags; /* device flags */
unsigned long quirks; /* Various quirks */
@@ -171,6 +172,8 @@ static const struct i2c_hid_quirks {
I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV },
{ I2C_VENDOR_ID_HANTICK, I2C_PRODUCT_ID_HANTICK_5288,
I2C_HID_QUIRK_NO_IRQ_AFTER_RESET },
+ { I2C_VENDOR_ID_RAYD, I2C_PRODUCT_ID_RAYD_3118,
+ I2C_HID_QUIRK_RESEND_REPORT_DESCR },
{ 0, 0 }
};
@@ -455,7 +458,8 @@ out_unlock:
static void i2c_hid_get_input(struct i2c_hid *ihid)
{
- int ret, ret_size;
+ int ret;
+ u32 ret_size;
int size = le16_to_cpu(ihid->hdesc.wMaxInputLength);
if (size > ihid->bufsize)
@@ -480,7 +484,7 @@ static void i2c_hid_get_input(struct i2c_hid *ihid)
return;
}
- if (ret_size > size) {
+ if ((ret_size > size) || (ret_size <= 2)) {
dev_err(&ihid->client->dev, "%s: incomplete report (%d/%d)\n",
__func__, size, ret_size);
return;
@@ -891,10 +895,10 @@ static int i2c_hid_acpi_pdata(struct i2c_client *client,
static void i2c_hid_acpi_fix_up_power(struct device *dev)
{
- acpi_handle handle = ACPI_HANDLE(dev);
struct acpi_device *adev;
- if (handle && acpi_bus_get_device(handle, &adev) == 0)
+ adev = ACPI_COMPANION(dev);
+ if (adev)
acpi_device_fix_up_power(adev);
}
@@ -1219,6 +1223,16 @@ static int i2c_hid_resume(struct device *dev)
if (ret)
return ret;
+ /* RAYDIUM device (2386:3118) need to re-send report descr cmd
+ * after resume, after this it will be back normal.
+ * otherwise it issues too many incomplete reports.
+ */
+ if (ihid->quirks & I2C_HID_QUIRK_RESEND_REPORT_DESCR) {
+ ret = i2c_hid_command(client, &hid_report_descr_cmd, NULL, 0);
+ if (ret)
+ return ret;
+ }
+
if (hid->driver && hid->driver->reset_resume) {
ret = hid->driver->reset_resume(hid);
return ret;
diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c
index 4e0e7baf8513..3c5507313606 100644
--- a/drivers/hid/uhid.c
+++ b/drivers/hid/uhid.c
@@ -496,12 +496,12 @@ static int uhid_dev_create2(struct uhid_device *uhid,
goto err_free;
}
- len = min(sizeof(hid->name), sizeof(ev->u.create2.name)) - 1;
- strncpy(hid->name, ev->u.create2.name, len);
- len = min(sizeof(hid->phys), sizeof(ev->u.create2.phys)) - 1;
- strncpy(hid->phys, ev->u.create2.phys, len);
- len = min(sizeof(hid->uniq), sizeof(ev->u.create2.uniq)) - 1;
- strncpy(hid->uniq, ev->u.create2.uniq, len);
+ len = min(sizeof(hid->name), sizeof(ev->u.create2.name));
+ strlcpy(hid->name, ev->u.create2.name, len);
+ len = min(sizeof(hid->phys), sizeof(ev->u.create2.phys));
+ strlcpy(hid->phys, ev->u.create2.phys, len);
+ len = min(sizeof(hid->uniq), sizeof(ev->u.create2.uniq));
+ strlcpy(hid->uniq, ev->u.create2.uniq, len);
hid->ll_driver = &uhid_hid_driver;
hid->bus = ev->u.create2.bus;
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index 77c50cdfff97..af0e0d061b15 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -56,6 +56,10 @@ static unsigned int hid_jspoll_interval;
module_param_named(jspoll, hid_jspoll_interval, uint, 0644);
MODULE_PARM_DESC(jspoll, "Polling interval of joysticks");
+static unsigned int hid_kbpoll_interval;
+module_param_named(kbpoll, hid_kbpoll_interval, uint, 0644);
+MODULE_PARM_DESC(kbpoll, "Polling interval of keyboards");
+
static unsigned int ignoreled;
module_param_named(ignoreled, ignoreled, uint, 0644);
MODULE_PARM_DESC(ignoreled, "Autosuspend with active leds");
@@ -1094,7 +1098,9 @@ static int usbhid_start(struct hid_device *hid)
hid->name, endpoint->bInterval, interval);
}
- /* Change the polling interval of mice and joysticks. */
+ /* Change the polling interval of mice, joysticks
+ * and keyboards.
+ */
switch (hid->collection->usage) {
case HID_GD_MOUSE:
if (hid_mousepoll_interval > 0)
@@ -1104,6 +1110,10 @@ static int usbhid_start(struct hid_device *hid)
if (hid_jspoll_interval > 0)
interval = hid_jspoll_interval;
break;
+ case HID_GD_KEYBOARD:
+ if (hid_kbpoll_interval > 0)
+ interval = hid_kbpoll_interval;
+ break;
}
ret = -ENOMEM;
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index 409543160af7..b54ef1ffcbec 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -219,7 +219,7 @@ static void wacom_feature_mapping(struct hid_device *hdev,
unsigned int equivalent_usage = wacom_equivalent_usage(usage->hid);
u8 *data;
int ret;
- int n;
+ u32 n;
switch (equivalent_usage) {
case HID_DG_CONTACTMAX:
@@ -519,7 +519,7 @@ static int wacom_set_device_mode(struct hid_device *hdev,
u8 *rep_data;
struct hid_report *r;
struct hid_report_enum *re;
- int length;
+ u32 length;
int error = -ENOMEM, limit = 0;
if (wacom_wac->mode_report < 0)
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index 90c38a0523e9..5f947ec20dcb 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -689,6 +689,45 @@ static int wacom_intuos_get_tool_type(int tool_id)
return tool_type;
}
+static void wacom_exit_report(struct wacom_wac *wacom)
+{
+ struct input_dev *input = wacom->pen_input;
+ struct wacom_features *features = &wacom->features;
+ unsigned char *data = wacom->data;
+ int idx = (features->type == INTUOS) ? (data[1] & 0x01) : 0;
+
+ /*
+ * Reset all states otherwise we lose the initial states
+ * when in-prox next time
+ */
+ input_report_abs(input, ABS_X, 0);
+ input_report_abs(input, ABS_Y, 0);
+ input_report_abs(input, ABS_DISTANCE, 0);
+ input_report_abs(input, ABS_TILT_X, 0);
+ input_report_abs(input, ABS_TILT_Y, 0);
+ if (wacom->tool[idx] >= BTN_TOOL_MOUSE) {
+ input_report_key(input, BTN_LEFT, 0);
+ input_report_key(input, BTN_MIDDLE, 0);
+ input_report_key(input, BTN_RIGHT, 0);
+ input_report_key(input, BTN_SIDE, 0);
+ input_report_key(input, BTN_EXTRA, 0);
+ input_report_abs(input, ABS_THROTTLE, 0);
+ input_report_abs(input, ABS_RZ, 0);
+ } else {
+ input_report_abs(input, ABS_PRESSURE, 0);
+ input_report_key(input, BTN_STYLUS, 0);
+ input_report_key(input, BTN_STYLUS2, 0);
+ input_report_key(input, BTN_TOUCH, 0);
+ input_report_abs(input, ABS_WHEEL, 0);
+ if (features->type >= INTUOS3S)
+ input_report_abs(input, ABS_Z, 0);
+ }
+ input_report_key(input, wacom->tool[idx], 0);
+ input_report_abs(input, ABS_MISC, 0); /* reset tool id */
+ input_event(input, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
+ wacom->id[idx] = 0;
+}
+
static int wacom_intuos_inout(struct wacom_wac *wacom)
{
struct wacom_features *features = &wacom->features;
@@ -741,36 +780,7 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
if (!wacom->id[idx])
return 1;
- /*
- * Reset all states otherwise we lose the initial states
- * when in-prox next time
- */
- input_report_abs(input, ABS_X, 0);
- input_report_abs(input, ABS_Y, 0);
- input_report_abs(input, ABS_DISTANCE, 0);
- input_report_abs(input, ABS_TILT_X, 0);
- input_report_abs(input, ABS_TILT_Y, 0);
- if (wacom->tool[idx] >= BTN_TOOL_MOUSE) {
- input_report_key(input, BTN_LEFT, 0);
- input_report_key(input, BTN_MIDDLE, 0);
- input_report_key(input, BTN_RIGHT, 0);
- input_report_key(input, BTN_SIDE, 0);
- input_report_key(input, BTN_EXTRA, 0);
- input_report_abs(input, ABS_THROTTLE, 0);
- input_report_abs(input, ABS_RZ, 0);
- } else {
- input_report_abs(input, ABS_PRESSURE, 0);
- input_report_key(input, BTN_STYLUS, 0);
- input_report_key(input, BTN_STYLUS2, 0);
- input_report_key(input, BTN_TOUCH, 0);
- input_report_abs(input, ABS_WHEEL, 0);
- if (features->type >= INTUOS3S)
- input_report_abs(input, ABS_Z, 0);
- }
- input_report_key(input, wacom->tool[idx], 0);
- input_report_abs(input, ABS_MISC, 0); /* reset tool id */
- input_event(input, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
- wacom->id[idx] = 0;
+ wacom_exit_report(wacom);
return 2;
}
@@ -1202,15 +1212,24 @@ static int wacom_wac_finger_count_touches(struct wacom_wac *wacom)
static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom)
{
- const int pen_frame_len = 14;
- const int pen_frames = 7;
+ int pen_frame_len, pen_frames;
struct input_dev *pen_input = wacom->pen_input;
unsigned char *data = wacom->data;
int i;
- wacom->serial[0] = get_unaligned_le64(&data[99]);
- wacom->id[0] = get_unaligned_le16(&data[107]);
+ if (wacom->features.type == INTUOSP2_BT) {
+ wacom->serial[0] = get_unaligned_le64(&data[99]);
+ wacom->id[0] = get_unaligned_le16(&data[107]);
+ pen_frame_len = 14;
+ pen_frames = 7;
+ } else {
+ wacom->serial[0] = get_unaligned_le64(&data[33]);
+ wacom->id[0] = get_unaligned_le16(&data[41]);
+ pen_frame_len = 8;
+ pen_frames = 4;
+ }
+
if (wacom->serial[0] >> 52 == 1) {
/* Add back in missing bits of ID for non-USI pens */
wacom->id[0] |= (wacom->serial[0] >> 32) & 0xFFFFF;
@@ -1226,22 +1245,42 @@ static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom)
if (!valid)
continue;
+ if (!prox) {
+ wacom->shared->stylus_in_proximity = false;
+ wacom_exit_report(wacom);
+ input_sync(pen_input);
+ return;
+ }
if (range) {
- /* Fix rotation alignment: userspace expects zero at left */
- int16_t rotation = (int16_t)get_unaligned_le16(&frame[9]);
- rotation += 1800/4;
- if (rotation > 899)
- rotation -= 1800;
-
input_report_abs(pen_input, ABS_X, get_unaligned_le16(&frame[1]));
input_report_abs(pen_input, ABS_Y, get_unaligned_le16(&frame[3]));
- input_report_abs(pen_input, ABS_TILT_X, (char)frame[7]);
- input_report_abs(pen_input, ABS_TILT_Y, (char)frame[8]);
- input_report_abs(pen_input, ABS_Z, rotation);
- input_report_abs(pen_input, ABS_WHEEL, get_unaligned_le16(&frame[11]));
+
+ if (wacom->features.type == INTUOSP2_BT) {
+ /* Fix rotation alignment: userspace expects zero at left */
+ int16_t rotation =
+ (int16_t)get_unaligned_le16(&frame[9]);
+ rotation += 1800/4;
+
+ if (rotation > 899)
+ rotation -= 1800;
+
+ input_report_abs(pen_input, ABS_TILT_X,
+ (char)frame[7]);
+ input_report_abs(pen_input, ABS_TILT_Y,
+ (char)frame[8]);
+ input_report_abs(pen_input, ABS_Z, rotation);
+ input_report_abs(pen_input, ABS_WHEEL,
+ get_unaligned_le16(&frame[11]));
+ }
}
input_report_abs(pen_input, ABS_PRESSURE, get_unaligned_le16(&frame[5]));
- input_report_abs(pen_input, ABS_DISTANCE, range ? frame[13] : wacom->features.distance_max);
+ if (wacom->features.type == INTUOSP2_BT) {
+ input_report_abs(pen_input, ABS_DISTANCE,
+ range ? frame[13] : wacom->features.distance_max);
+ } else {
+ input_report_abs(pen_input, ABS_DISTANCE,
+ range ? frame[7] : wacom->features.distance_max);
+ }
input_report_key(pen_input, BTN_TOUCH, frame[0] & 0x01);
input_report_key(pen_input, BTN_STYLUS, frame[0] & 0x02);
@@ -1357,20 +1396,52 @@ static void wacom_intuos_pro2_bt_battery(struct wacom_wac *wacom)
battery_status, chg, 1, chg);
}
+static void wacom_intuos_gen3_bt_pad(struct wacom_wac *wacom)
+{
+ struct input_dev *pad_input = wacom->pad_input;
+ unsigned char *data = wacom->data;
+
+ int buttons = data[44];
+
+ wacom_report_numbered_buttons(pad_input, 4, buttons);
+
+ input_report_key(pad_input, wacom->tool[1], buttons ? 1 : 0);
+ input_report_abs(pad_input, ABS_MISC, buttons ? PAD_DEVICE_ID : 0);
+ input_event(pad_input, EV_MSC, MSC_SERIAL, 0xffffffff);
+
+ input_sync(pad_input);
+}
+
+static void wacom_intuos_gen3_bt_battery(struct wacom_wac *wacom)
+{
+ unsigned char *data = wacom->data;
+
+ bool chg = data[45] & 0x80;
+ int battery_status = data[45] & 0x7F;
+
+ wacom_notify_battery(wacom, WACOM_POWER_SUPPLY_STATUS_AUTO,
+ battery_status, chg, 1, chg);
+}
+
static int wacom_intuos_pro2_bt_irq(struct wacom_wac *wacom, size_t len)
{
unsigned char *data = wacom->data;
- if (data[0] != 0x80) {
+ if (data[0] != 0x80 && data[0] != 0x81) {
dev_dbg(wacom->pen_input->dev.parent,
"%s: received unknown report #%d\n", __func__, data[0]);
return 0;
}
wacom_intuos_pro2_bt_pen(wacom);
- wacom_intuos_pro2_bt_touch(wacom);
- wacom_intuos_pro2_bt_pad(wacom);
- wacom_intuos_pro2_bt_battery(wacom);
+ if (wacom->features.type == INTUOSP2_BT) {
+ wacom_intuos_pro2_bt_touch(wacom);
+ wacom_intuos_pro2_bt_pad(wacom);
+ wacom_intuos_pro2_bt_battery(wacom);
+ } else {
+ wacom_intuos_gen3_bt_pad(wacom);
+ wacom_intuos_gen3_bt_battery(wacom);
+ }
return 0;
}
@@ -1660,7 +1731,8 @@ int wacom_equivalent_usage(int usage)
usage == WACOM_HID_WD_TOUCHSTRIP ||
usage == WACOM_HID_WD_TOUCHSTRIP2 ||
usage == WACOM_HID_WD_TOUCHRING ||
- usage == WACOM_HID_WD_TOUCHRINGSTATUS) {
+ usage == WACOM_HID_WD_TOUCHRINGSTATUS ||
+ usage == WACOM_HID_WD_REPORT_VALID) {
return usage;
}
@@ -2017,7 +2089,7 @@ static void wacom_wac_pad_pre_report(struct hid_device *hdev,
}
static void wacom_wac_pad_report(struct hid_device *hdev,
- struct hid_report *report)
+ struct hid_report *report, struct hid_field *field)
{
struct wacom *wacom = hid_get_drvdata(hdev);
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
@@ -2025,7 +2097,7 @@ static void wacom_wac_pad_report(struct hid_device *hdev,
bool active = wacom_wac->hid_data.inrange_state != 0;
/* report prox for expresskey events */
- if ((wacom_equivalent_usage(report->field[0]->physical) == HID_DG_TABLETFUNCTIONKEY) &&
+ if ((wacom_equivalent_usage(field->physical) == HID_DG_TABLETFUNCTIONKEY) &&
wacom_wac->hid_data.pad_input_event_flag) {
input_event(input, EV_ABS, ABS_MISC, active ? PAD_DEVICE_ID : 0);
input_sync(input);
@@ -2144,6 +2216,9 @@ static void wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field
struct input_dev *input = wacom_wac->pen_input;
unsigned equivalent_usage = wacom_equivalent_usage(usage->hid);
+ if (wacom_wac->is_invalid_bt_frame)
+ return;
+
switch (equivalent_usage) {
case HID_GD_Z:
/*
@@ -2240,6 +2315,9 @@ static void wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field
features->offset_bottom);
features->offset_bottom = value;
return;
+ case WACOM_HID_WD_REPORT_VALID:
+ wacom_wac->is_invalid_bt_frame = !value;
+ return;
}
/* send pen events only when touch is up or forced out
@@ -2258,6 +2336,10 @@ static void wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field
static void wacom_wac_pen_pre_report(struct hid_device *hdev,
struct hid_report *report)
{
+ struct wacom *wacom = hid_get_drvdata(hdev);
+ struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+
+ wacom_wac->is_invalid_bt_frame = false;
return;
}
@@ -2270,6 +2352,9 @@ static void wacom_wac_pen_report(struct hid_device *hdev,
bool range = wacom_wac->hid_data.inrange_state;
bool sense = wacom_wac->hid_data.sense_state;
+ if (wacom_wac->is_invalid_bt_frame)
+ return;
+
if (!wacom_wac->tool[0] && range) { /* first in range */
/* Going into range select tool */
if (wacom_wac->hid_data.invert_state)
@@ -2572,11 +2657,13 @@ void wacom_wac_event(struct hid_device *hdev, struct hid_field *field,
wacom_wac_finger_event(hdev, field, usage, value);
}
-static void wacom_report_events(struct hid_device *hdev, struct hid_report *report)
+static void wacom_report_events(struct hid_device *hdev,
+ struct hid_report *report, int collection_index,
+ int field_index)
{
int r;
- for (r = 0; r < report->maxfield; r++) {
+ for (r = field_index; r < report->maxfield; r++) {
struct hid_field *field;
unsigned count, n;
@@ -2586,30 +2673,23 @@ static void wacom_report_events(struct hid_device *hdev, struct hid_report *repo
if (!(HID_MAIN_ITEM_VARIABLE & field->flags))
continue;
- for (n = 0; n < count; n++)
- wacom_wac_event(hdev, field, &field->usage[n], field->value[n]);
+ for (n = 0 ; n < count; n++) {
+ if (field->usage[n].collection_index == collection_index)
+ wacom_wac_event(hdev, field, &field->usage[n],
+ field->value[n]);
+ else
+ return;
+ }
}
}
-void wacom_wac_report(struct hid_device *hdev, struct hid_report *report)
+static int wacom_wac_collection(struct hid_device *hdev, struct hid_report *report,
+ int collection_index, struct hid_field *field,
+ int field_index)
{
struct wacom *wacom = hid_get_drvdata(hdev);
- struct wacom_wac *wacom_wac = &wacom->wacom_wac;
- struct hid_field *field = report->field[0];
- if (wacom_wac->features.type != HID_GENERIC)
- return;
-
- wacom_wac_battery_pre_report(hdev, report);
-
- if (WACOM_PAD_FIELD(field) && wacom->wacom_wac.pad_input)
- wacom_wac_pad_pre_report(hdev, report);
- else if (WACOM_PEN_FIELD(field) && wacom->wacom_wac.pen_input)
- wacom_wac_pen_pre_report(hdev, report);
- else if (WACOM_FINGER_FIELD(field) && wacom->wacom_wac.touch_input)
- wacom_wac_finger_pre_report(hdev, report);
-
- wacom_report_events(hdev, report);
+ wacom_report_events(hdev, report, collection_index, field_index);
/*
* Non-input reports may be sent prior to the device being
@@ -2619,16 +2699,63 @@ void wacom_wac_report(struct hid_device *hdev, struct hid_report *report)
* processing functions.
*/
if (report->type != HID_INPUT_REPORT)
- return;
-
- wacom_wac_battery_report(hdev, report);
+ return -1;
if (WACOM_PAD_FIELD(field) && wacom->wacom_wac.pad_input)
- wacom_wac_pad_report(hdev, report);
+ wacom_wac_pad_report(hdev, report, field);
else if (WACOM_PEN_FIELD(field) && wacom->wacom_wac.pen_input)
wacom_wac_pen_report(hdev, report);
else if (WACOM_FINGER_FIELD(field) && wacom->wacom_wac.touch_input)
wacom_wac_finger_report(hdev, report);
+
+ return 0;
+}
+
+void wacom_wac_report(struct hid_device *hdev, struct hid_report *report)
+{
+ struct wacom *wacom = hid_get_drvdata(hdev);
+ struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+ struct hid_field *field;
+ bool pad_in_hid_field = false, pen_in_hid_field = false,
+ finger_in_hid_field = false;
+ int r;
+ int prev_collection = -1;
+
+ if (wacom_wac->features.type != HID_GENERIC)
+ return;
+
+ for (r = 0; r < report->maxfield; r++) {
+ field = report->field[r];
+
+ if (WACOM_PAD_FIELD(field))
+ pad_in_hid_field = true;
+ if (WACOM_PEN_FIELD(field))
+ pen_in_hid_field = true;
+ if (WACOM_FINGER_FIELD(field))
+ finger_in_hid_field = true;
+ }
+
+ wacom_wac_battery_pre_report(hdev, report);
+
+ if (pad_in_hid_field && wacom->wacom_wac.pad_input)
+ wacom_wac_pad_pre_report(hdev, report);
+ if (pen_in_hid_field && wacom->wacom_wac.pen_input)
+ wacom_wac_pen_pre_report(hdev, report);
+ if (finger_in_hid_field && wacom->wacom_wac.touch_input)
+ wacom_wac_finger_pre_report(hdev, report);
+
+ for (r = 0; r < report->maxfield; r++) {
+ field = report->field[r];
+
+ if (field->usage[0].collection_index != prev_collection) {
+ if (wacom_wac_collection(hdev, report,
+ field->usage[0].collection_index, field, r) < 0)
+ return;
+ prev_collection = field->usage[0].collection_index;
+ }
+ }
+
+ wacom_wac_battery_report(hdev, report);
}
static int wacom_bpt_touch(struct wacom_wac *wacom)
@@ -3093,6 +3220,7 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
break;
case INTUOSP2_BT:
+ case INTUOSHT3_BT:
sync = wacom_intuos_pro2_bt_irq(wacom_wac, len);
break;
@@ -3272,6 +3400,12 @@ void wacom_setup_device_quirks(struct wacom *wacom)
features->quirks |= WACOM_QUIRK_BATTERY;
}
+ if (features->type == INTUOSHT3_BT) {
+ features->device_type |= WACOM_DEVICETYPE_PEN |
+ WACOM_DEVICETYPE_PAD;
+ features->quirks |= WACOM_QUIRK_BATTERY;
+ }
+
switch (features->type) {
case PL:
case DTU:
@@ -3466,7 +3600,9 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev,
case BAMBOO_PT:
case BAMBOO_PEN:
case INTUOSHT2:
- if (features->type == INTUOSHT2) {
+ case INTUOSHT3_BT:
+ if (features->type == INTUOSHT2 ||
+ features->type == INTUOSHT3_BT) {
wacom_setup_basic_pro_pen(wacom_wac);
} else {
__clear_bit(ABS_MISC, input_dev->absbit);
@@ -3887,6 +4023,7 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
break;
+ case INTUOSHT3_BT:
case HID_GENERIC:
break;
@@ -4415,6 +4552,12 @@ static const struct wacom_features wacom_features_0x360 =
static const struct wacom_features wacom_features_0x361 =
{ "Wacom Intuos Pro L", 62200, 43200, 8191, 63,
INTUOSP2_BT, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9, .touch_max = 10 };
+static const struct wacom_features wacom_features_0x377 =
+ { "Wacom Intuos BT S", 15200, 9500, 4095, 63,
+ INTUOSHT3_BT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, 4 };
+static const struct wacom_features wacom_features_0x379 =
+ { "Wacom Intuos BT M", 21600, 13500, 4095, 63,
+ INTUOSHT3_BT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, 4 };
static const struct wacom_features wacom_features_0x37A =
{ "Wacom One by Wacom S", 15200, 9500, 2047, 63,
BAMBOO_PEN, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
@@ -4589,6 +4732,8 @@ const struct hid_device_id wacom_ids[] = {
{ USB_DEVICE_WACOM(0x343) },
{ BT_DEVICE_WACOM(0x360) },
{ BT_DEVICE_WACOM(0x361) },
+ { BT_DEVICE_WACOM(0x377) },
+ { BT_DEVICE_WACOM(0x379) },
{ USB_DEVICE_WACOM(0x37A) },
{ USB_DEVICE_WACOM(0x37B) },
{ USB_DEVICE_WACOM(0x4001) },
diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h
index 15d9c14fbdf7..295fd3718caa 100644
--- a/drivers/hid/wacom_wac.h
+++ b/drivers/hid/wacom_wac.h
@@ -118,6 +118,7 @@
#define WACOM_HID_WD_TOUCHSTRIP2 (WACOM_HID_UP_WACOMDIGITIZER | 0x0137)
#define WACOM_HID_WD_TOUCHRING (WACOM_HID_UP_WACOMDIGITIZER | 0x0138)
#define WACOM_HID_WD_TOUCHRINGSTATUS (WACOM_HID_UP_WACOMDIGITIZER | 0x0139)
+#define WACOM_HID_WD_REPORT_VALID (WACOM_HID_UP_WACOMDIGITIZER | 0x01d0)
#define WACOM_HID_WD_ACCELEROMETER_X (WACOM_HID_UP_WACOMDIGITIZER | 0x0401)
#define WACOM_HID_WD_ACCELEROMETER_Y (WACOM_HID_UP_WACOMDIGITIZER | 0x0402)
#define WACOM_HID_WD_ACCELEROMETER_Z (WACOM_HID_UP_WACOMDIGITIZER | 0x0403)
@@ -213,6 +214,7 @@ enum {
INTUOSPM,
INTUOSPL,
INTUOSP2_BT,
+ INTUOSHT3_BT,
WACOM_21UX2,
WACOM_22HD,
DTK,
@@ -352,7 +354,7 @@ struct wacom_wac {
bool has_mute_touch_switch;
bool has_mode_change;
bool is_direct_mode;
-
+ bool is_invalid_bt_frame;
};
#endif
diff --git a/drivers/hsi/clients/hsi_char.c b/drivers/hsi/clients/hsi_char.c
index cf9c2a332ad8..9d2a6d64dfb8 100644
--- a/drivers/hsi/clients/hsi_char.c
+++ b/drivers/hsi/clients/hsi_char.c
@@ -697,10 +697,9 @@ static int hsc_probe(struct device *dev)
int i;
cl_data = kzalloc(sizeof(*cl_data), GFP_KERNEL);
- if (!cl_data) {
- dev_err(dev, "Could not allocate hsc_client_data\n");
+ if (!cl_data)
return -ENOMEM;
- }
+
hsc_baseminor = HSC_BASEMINOR(hsi_id(cl), hsi_port_id(cl));
if (!hsc_major) {
ret = alloc_chrdev_region(&hsc_dev, hsc_baseminor,
diff --git a/drivers/hsi/clients/ssi_protocol.c b/drivers/hsi/clients/ssi_protocol.c
index 9b167bc6eee4..561abf7bdf1f 100644
--- a/drivers/hsi/clients/ssi_protocol.c
+++ b/drivers/hsi/clients/ssi_protocol.c
@@ -495,7 +495,7 @@ static void ssip_rx_wd(struct timer_list *t)
struct ssi_protocol *ssi = from_timer(ssi, t, rx_wd);
struct hsi_client *cl = ssi->cl;
- dev_err(&cl->device, "Watchdog trigerred\n");
+ dev_err(&cl->device, "Watchdog triggered\n");
ssip_error(cl);
}
@@ -504,7 +504,7 @@ static void ssip_tx_wd(struct timer_list *t)
struct ssi_protocol *ssi = from_timer(ssi, t, tx_wd);
struct hsi_client *cl = ssi->cl;
- dev_err(&cl->device, "Watchdog trigerred\n");
+ dev_err(&cl->device, "Watchdog triggered\n");
ssip_error(cl);
}
@@ -993,8 +993,8 @@ static int ssip_pn_xmit(struct sk_buff *skb, struct net_device *dev)
goto inc_dropped;
/*
- * Modem sends Phonet messages over SSI with its own endianess...
- * Assume that modem has the same endianess as we do.
+ * Modem sends Phonet messages over SSI with its own endianness.
+ * Assume that modem has the same endianness as we do.
*/
if (skb_cow_head(skb, 0))
goto drop;
@@ -1088,10 +1088,8 @@ static int ssi_protocol_probe(struct device *dev)
int err;
ssi = kzalloc(sizeof(*ssi), GFP_KERNEL);
- if (!ssi) {
- dev_err(dev, "No memory for ssi protocol\n");
+ if (!ssi)
return -ENOMEM;
- }
spin_lock_init(&ssi->lock);
timer_setup(&ssi->rx_wd, ssip_rx_wd, TIMER_DEFERRABLE);
diff --git a/drivers/hv/Kconfig b/drivers/hv/Kconfig
index 50b89ea0e60f..97954f575c3f 100644
--- a/drivers/hv/Kconfig
+++ b/drivers/hv/Kconfig
@@ -1,3 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
menu "Microsoft Hyper-V guest support"
config HYPERV
diff --git a/drivers/hv/Makefile b/drivers/hv/Makefile
index 14c22786b519..a1eec7177c2d 100644
--- a/drivers/hv/Makefile
+++ b/drivers/hv/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_HYPERV_UTILS) += hv_utils.o
obj-$(CONFIG_HYPERV_BALLOON) += hv_balloon.o
CFLAGS_hv_trace.o = -I$(src)
+CFLAGS_hv_balloon.o = -I$(src)
hv_vmbus-y := vmbus_drv.o \
hv.o connection.o channel.o \
diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c
index c21020b69114..ecc2bd275a73 100644
--- a/drivers/hv/channel_mgmt.c
+++ b/drivers/hv/channel_mgmt.c
@@ -71,7 +71,7 @@ static const struct vmbus_device vmbus_devs[] = {
/* PCIE */
{ .dev_type = HV_PCIE,
HV_PCIE_GUID,
- .perf_device = true,
+ .perf_device = false,
},
/* Synthetic Frame Buffer */
@@ -596,10 +596,8 @@ static int next_numa_node_id;
/*
* Starting with Win8, we can statically distribute the incoming
* channel interrupt load by binding a channel to VCPU.
- * We do this in a hierarchical fashion:
- * First distribute the primary channels across available NUMA nodes
- * and then distribute the subchannels amongst the CPUs in the NUMA
- * node assigned to the primary channel.
+ * We distribute the interrupt loads to one or more NUMA nodes based on
+ * the channel's affinity_policy.
*
* For pre-win8 hosts or non-performance critical channels we assign the
* first CPU in the first NUMA node.
diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
index 447371f4de56..72855182b191 100644
--- a/drivers/hv/connection.c
+++ b/drivers/hv/connection.c
@@ -31,7 +31,6 @@
#include <linux/vmalloc.h>
#include <linux/hyperv.h>
#include <linux/export.h>
-#include <asm/hyperv.h>
#include <asm/mshyperv.h>
#include "hyperv_vmbus.h"
diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c
index fe96aab9e794..9b82549cbbc8 100644
--- a/drivers/hv/hv.c
+++ b/drivers/hv/hv.c
@@ -27,9 +27,8 @@
#include <linux/vmalloc.h>
#include <linux/hyperv.h>
#include <linux/version.h>
-#include <linux/interrupt.h>
+#include <linux/random.h>
#include <linux/clockchips.h>
-#include <asm/hyperv.h>
#include <asm/mshyperv.h>
#include "hyperv_vmbus.h"
@@ -38,6 +37,17 @@ struct hv_context hv_context = {
.synic_initialized = false,
};
+/*
+ * If false, we're using the old mechanism for stimer0 interrupts
+ * where it sends a VMbus message when it expires. The old
+ * mechanism is used when running on older versions of Hyper-V
+ * that don't support Direct Mode. While Hyper-V provides
+ * four stimer's per CPU, Linux uses only stimer0.
+ */
+static bool direct_mode_enabled;
+static int stimer0_irq;
+static int stimer0_vector;
+
#define HV_TIMER_FREQUENCY (10 * 1000 * 1000) /* 100ns period */
#define HV_MAX_MAX_DELTA_TICKS 0xffffffff
#define HV_MIN_DELTA_TICKS 1
@@ -53,6 +63,8 @@ int hv_init(void)
if (!hv_context.cpu_context)
return -ENOMEM;
+ direct_mode_enabled = ms_hyperv.misc_features &
+ HV_X64_STIMER_DIRECT_MODE_AVAILABLE;
return 0;
}
@@ -91,6 +103,21 @@ int hv_post_message(union hv_connection_id connection_id,
return status & 0xFFFF;
}
+/*
+ * ISR for when stimer0 is operating in Direct Mode. Direct Mode
+ * does not use VMbus or any VMbus messages, so process here and not
+ * in the VMbus driver code.
+ */
+
+static void hv_stimer0_isr(void)
+{
+ struct hv_per_cpu_context *hv_cpu;
+
+ hv_cpu = this_cpu_ptr(hv_context.cpu_context);
+ hv_cpu->clk_evt->event_handler(hv_cpu->clk_evt);
+ add_interrupt_randomness(stimer0_vector, 0);
+}
+
static int hv_ce_set_next_event(unsigned long delta,
struct clock_event_device *evt)
{
@@ -108,6 +135,8 @@ static int hv_ce_shutdown(struct clock_event_device *evt)
{
hv_init_timer(HV_X64_MSR_STIMER0_COUNT, 0);
hv_init_timer_config(HV_X64_MSR_STIMER0_CONFIG, 0);
+ if (direct_mode_enabled)
+ hv_disable_stimer0_percpu_irq(stimer0_irq);
return 0;
}
@@ -116,11 +145,26 @@ static int hv_ce_set_oneshot(struct clock_event_device *evt)
{
union hv_timer_config timer_cfg;
+ timer_cfg.as_uint64 = 0;
timer_cfg.enable = 1;
timer_cfg.auto_enable = 1;
- timer_cfg.sintx = VMBUS_MESSAGE_SINT;
+ if (direct_mode_enabled) {
+ /*
+ * When it expires, the timer will directly interrupt
+ * on the specified hardware vector/IRQ.
+ */
+ timer_cfg.direct_mode = 1;
+ timer_cfg.apic_vector = stimer0_vector;
+ hv_enable_stimer0_percpu_irq(stimer0_irq);
+ } else {
+ /*
+ * When it expires, the timer will generate a VMbus message,
+ * to be handled by the normal VMbus interrupt handler.
+ */
+ timer_cfg.direct_mode = 0;
+ timer_cfg.sintx = VMBUS_MESSAGE_SINT;
+ }
hv_init_timer_config(HV_X64_MSR_STIMER0_CONFIG, timer_cfg.as_uint64);
-
return 0;
}
@@ -147,7 +191,7 @@ int hv_synic_alloc(void)
int cpu;
hv_context.hv_numa_map = kzalloc(sizeof(struct cpumask) * nr_node_ids,
- GFP_ATOMIC);
+ GFP_KERNEL);
if (hv_context.hv_numa_map == NULL) {
pr_err("Unable to allocate NUMA map\n");
goto err;
@@ -191,6 +235,11 @@ int hv_synic_alloc(void)
INIT_LIST_HEAD(&hv_cpu->chan_list);
}
+ if (direct_mode_enabled &&
+ hv_setup_stimer0_irq(&stimer0_irq, &stimer0_vector,
+ hv_stimer0_isr))
+ goto err;
+
return 0;
err:
return -ENOMEM;
@@ -217,7 +266,7 @@ void hv_synic_free(void)
}
/*
- * hv_synic_init - Initialize the Synthethic Interrupt Controller.
+ * hv_synic_init - Initialize the Synthetic Interrupt Controller.
*
* If it is already initialized by another entity (ie x2v shim), we need to
* retrieve the initialized message and event pages. Otherwise, we create and
@@ -252,7 +301,6 @@ int hv_synic_init(unsigned int cpu)
hv_get_synint_state(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT,
shared_sint.as_uint64);
- shared_sint.as_uint64 = 0;
shared_sint.vector = HYPERVISOR_CALLBACK_VECTOR;
shared_sint.masked = false;
if (ms_hyperv.hints & HV_X64_DEPRECATING_AEOI_RECOMMENDED)
@@ -292,6 +340,9 @@ void hv_synic_clockevents_cleanup(void)
if (!(ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE))
return;
+ if (direct_mode_enabled)
+ hv_remove_stimer0_irq(stimer0_irq);
+
for_each_present_cpu(cpu) {
struct hv_per_cpu_context *hv_cpu
= per_cpu_ptr(hv_context.cpu_context, cpu);
diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c
index db0e6652d7ef..b3e9f13f8bc3 100644
--- a/drivers/hv/hv_balloon.c
+++ b/drivers/hv/hv_balloon.c
@@ -34,6 +34,9 @@
#include <linux/hyperv.h>
+#define CREATE_TRACE_POINTS
+#include "hv_trace_balloon.h"
+
/*
* We begin with definitions supporting the Dynamic Memory protocol
* with the host.
@@ -576,11 +579,65 @@ static struct hv_dynmem_device dm_device;
static void post_status(struct hv_dynmem_device *dm);
#ifdef CONFIG_MEMORY_HOTPLUG
+static inline bool has_pfn_is_backed(struct hv_hotadd_state *has,
+ unsigned long pfn)
+{
+ struct hv_hotadd_gap *gap;
+
+ /* The page is not backed. */
+ if ((pfn < has->covered_start_pfn) || (pfn >= has->covered_end_pfn))
+ return false;
+
+ /* Check for gaps. */
+ list_for_each_entry(gap, &has->gap_list, list) {
+ if ((pfn >= gap->start_pfn) && (pfn < gap->end_pfn))
+ return false;
+ }
+
+ return true;
+}
+
+static unsigned long hv_page_offline_check(unsigned long start_pfn,
+ unsigned long nr_pages)
+{
+ unsigned long pfn = start_pfn, count = 0;
+ struct hv_hotadd_state *has;
+ bool found;
+
+ while (pfn < start_pfn + nr_pages) {
+ /*
+ * Search for HAS which covers the pfn and when we find one
+ * count how many consequitive PFNs are covered.
+ */
+ found = false;
+ list_for_each_entry(has, &dm_device.ha_region_list, list) {
+ while ((pfn >= has->start_pfn) &&
+ (pfn < has->end_pfn) &&
+ (pfn < start_pfn + nr_pages)) {
+ found = true;
+ if (has_pfn_is_backed(has, pfn))
+ count++;
+ pfn++;
+ }
+ }
+
+ /*
+ * This PFN is not in any HAS (e.g. we're offlining a region
+ * which was present at boot), no need to account for it. Go
+ * to the next one.
+ */
+ if (!found)
+ pfn++;
+ }
+
+ return count;
+}
+
static int hv_memory_notifier(struct notifier_block *nb, unsigned long val,
void *v)
{
struct memory_notify *mem = (struct memory_notify *)v;
- unsigned long flags;
+ unsigned long flags, pfn_count;
switch (val) {
case MEM_ONLINE:
@@ -593,7 +650,19 @@ static int hv_memory_notifier(struct notifier_block *nb, unsigned long val,
case MEM_OFFLINE:
spin_lock_irqsave(&dm_device.ha_lock, flags);
- dm_device.num_pages_onlined -= mem->nr_pages;
+ pfn_count = hv_page_offline_check(mem->start_pfn,
+ mem->nr_pages);
+ if (pfn_count <= dm_device.num_pages_onlined) {
+ dm_device.num_pages_onlined -= pfn_count;
+ } else {
+ /*
+ * We're offlining more pages than we managed to online.
+ * This is unexpected. In any case don't let
+ * num_pages_onlined wrap around zero.
+ */
+ WARN_ON_ONCE(1);
+ dm_device.num_pages_onlined = 0;
+ }
spin_unlock_irqrestore(&dm_device.ha_lock, flags);
break;
case MEM_GOING_ONLINE:
@@ -612,30 +681,9 @@ static struct notifier_block hv_memory_nb = {
/* Check if the particular page is backed and can be onlined and online it. */
static void hv_page_online_one(struct hv_hotadd_state *has, struct page *pg)
{
- unsigned long cur_start_pgp;
- unsigned long cur_end_pgp;
- struct hv_hotadd_gap *gap;
-
- cur_start_pgp = (unsigned long)pfn_to_page(has->covered_start_pfn);
- cur_end_pgp = (unsigned long)pfn_to_page(has->covered_end_pfn);
-
- /* The page is not backed. */
- if (((unsigned long)pg < cur_start_pgp) ||
- ((unsigned long)pg >= cur_end_pgp))
+ if (!has_pfn_is_backed(has, page_to_pfn(pg)))
return;
- /* Check for gaps. */
- list_for_each_entry(gap, &has->gap_list, list) {
- cur_start_pgp = (unsigned long)
- pfn_to_page(gap->start_pfn);
- cur_end_pgp = (unsigned long)
- pfn_to_page(gap->end_pfn);
- if (((unsigned long)pg >= cur_start_pgp) &&
- ((unsigned long)pg < cur_end_pgp)) {
- return;
- }
- }
-
/* This frame is currently backed; online the page. */
__online_page_set_limits(pg);
__online_page_increment_counters(pg);
@@ -691,7 +739,7 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
(HA_CHUNK << PAGE_SHIFT));
if (ret) {
- pr_warn("hot_add memory failed error is %d\n", ret);
+ pr_err("hot_add memory failed error is %d\n", ret);
if (ret == -EEXIST) {
/*
* This error indicates that the error
@@ -726,19 +774,13 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
static void hv_online_page(struct page *pg)
{
struct hv_hotadd_state *has;
- unsigned long cur_start_pgp;
- unsigned long cur_end_pgp;
unsigned long flags;
+ unsigned long pfn = page_to_pfn(pg);
spin_lock_irqsave(&dm_device.ha_lock, flags);
list_for_each_entry(has, &dm_device.ha_region_list, list) {
- cur_start_pgp = (unsigned long)
- pfn_to_page(has->start_pfn);
- cur_end_pgp = (unsigned long)pfn_to_page(has->end_pfn);
-
/* The page belongs to a different HAS. */
- if (((unsigned long)pg < cur_start_pgp) ||
- ((unsigned long)pg >= cur_end_pgp))
+ if ((pfn < has->start_pfn) || (pfn >= has->end_pfn))
continue;
hv_page_online_one(has, pg);
@@ -1014,7 +1056,7 @@ static void hot_add_req(struct work_struct *dummy)
resp.result = 0;
if (!do_hot_add || (resp.page_count == 0))
- pr_info("Memory hot add failed\n");
+ pr_err("Memory hot add failed\n");
dm->state = DM_INITIALIZED;
resp.hdr.trans_id = atomic_inc_return(&trans_id);
@@ -1041,7 +1083,7 @@ static void process_info(struct hv_dynmem_device *dm, struct dm_info_msg *msg)
break;
default:
- pr_info("Received Unknown type: %d\n", info_hdr->type);
+ pr_warn("Received Unknown type: %d\n", info_hdr->type);
}
}
@@ -1120,6 +1162,9 @@ static void post_status(struct hv_dynmem_device *dm)
dm->num_pages_added - dm->num_pages_onlined : 0) +
compute_balloon_floor();
+ trace_balloon_status(status.num_avail, status.num_committed,
+ vm_memory_committed(), dm->num_pages_ballooned,
+ dm->num_pages_added, dm->num_pages_onlined);
/*
* If our transaction ID is no longer current, just don't
* send the status. This can happen if we were interrupted
@@ -1290,7 +1335,7 @@ static void balloon_up(struct work_struct *dummy)
/*
* Free up the memory we allocatted.
*/
- pr_info("Balloon response failed\n");
+ pr_err("Balloon response failed\n");
for (i = 0; i < bl_resp->range_count; i++)
free_balloon_pages(&dm_device,
@@ -1421,7 +1466,7 @@ static void cap_resp(struct hv_dynmem_device *dm,
struct dm_capabilities_resp_msg *cap_resp)
{
if (!cap_resp->is_accepted) {
- pr_info("Capabilities not accepted by host\n");
+ pr_err("Capabilities not accepted by host\n");
dm->state = DM_INIT_ERROR;
}
complete(&dm->host_event);
@@ -1508,7 +1553,7 @@ static void balloon_onchannelcallback(void *context)
break;
default:
- pr_err("Unhandled message: type: %d\n", dm_hdr->type);
+ pr_warn("Unhandled message: type: %d\n", dm_hdr->type);
}
}
diff --git a/drivers/hv/hv_trace.c b/drivers/hv/hv_trace.c
index df47acd01a81..38d359cf1e70 100644
--- a/drivers/hv/hv_trace.c
+++ b/drivers/hv/hv_trace.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0
+
#include "hyperv_vmbus.h"
#define CREATE_TRACE_POINTS
diff --git a/drivers/hv/hv_trace.h b/drivers/hv/hv_trace.h
index d635ee95b20d..999f80a63bff 100644
--- a/drivers/hv/hv_trace.h
+++ b/drivers/hv/hv_trace.h
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0
+
#undef TRACE_SYSTEM
#define TRACE_SYSTEM hyperv
diff --git a/drivers/hv/hv_trace_balloon.h b/drivers/hv/hv_trace_balloon.h
new file mode 100644
index 000000000000..93082888aec3
--- /dev/null
+++ b/drivers/hv/hv_trace_balloon.h
@@ -0,0 +1,48 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM hyperv
+
+#if !defined(_HV_TRACE_BALLOON_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _HV_TRACE_BALLOON_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(balloon_status,
+ TP_PROTO(u64 available, u64 committed,
+ unsigned long vm_memory_committed,
+ unsigned long pages_ballooned,
+ unsigned long pages_added,
+ unsigned long pages_onlined),
+ TP_ARGS(available, committed, vm_memory_committed,
+ pages_ballooned, pages_added, pages_onlined),
+ TP_STRUCT__entry(
+ __field(u64, available)
+ __field(u64, committed)
+ __field(unsigned long, vm_memory_committed)
+ __field(unsigned long, pages_ballooned)
+ __field(unsigned long, pages_added)
+ __field(unsigned long, pages_onlined)
+ ),
+ TP_fast_assign(
+ __entry->available = available;
+ __entry->committed = committed;
+ __entry->vm_memory_committed = vm_memory_committed;
+ __entry->pages_ballooned = pages_ballooned;
+ __entry->pages_added = pages_added;
+ __entry->pages_onlined = pages_onlined;
+ ),
+ TP_printk("available %lld, committed %lld; vm_memory_committed %ld;"
+ " pages_ballooned %ld, pages_added %ld, pages_onlined %ld",
+ __entry->available, __entry->committed,
+ __entry->vm_memory_committed, __entry->pages_ballooned,
+ __entry->pages_added, __entry->pages_onlined
+ )
+ );
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE hv_trace_balloon
+#endif /* _HV_TRACE_BALLOON_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
index 22300ec7b556..f761bef36e77 100644
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -27,6 +27,7 @@
#include <linux/list.h>
#include <asm/sync_bitops.h>
+#include <asm/hyperv-tlfs.h>
#include <linux/atomic.h>
#include <linux/hyperv.h>
#include <linux/interrupt.h>
@@ -57,7 +58,9 @@ union hv_timer_config {
u64 periodic:1;
u64 lazy:1;
u64 auto_enable:1;
- u64 reserved_z0:12;
+ u64 apic_vector:8;
+ u64 direct_mode:1;
+ u64 reserved_z0:3;
u64 sintx:4;
u64 reserved_z1:44;
};
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index bc65c4d79c1f..b10fe26c4891 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -36,7 +36,6 @@
#include <linux/cpu.h>
#include <linux/sched/task_stack.h>
-#include <asm/hyperv.h>
#include <asm/mshyperv.h>
#include <linux/notifier.h>
#include <linux/ptrace.h>
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index ef23553ff5cb..f249a4428458 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -317,6 +317,18 @@ config SENSORS_APPLESMC
Say Y here if you have an applicable laptop and want to experience
the awesome power of applesmc.
+config SENSORS_ARM_SCMI
+ tristate "ARM SCMI Sensors"
+ depends on ARM_SCMI_PROTOCOL
+ depends on THERMAL || !THERMAL_OF
+ help
+ This driver provides support for temperature, voltage, current
+ and power sensors available on SCMI based platforms. The actual
+ number and type of sensors exported depend on the platform.
+
+ This driver can also be built as a module. If so, the module
+ will be called scmi-hwmon.
+
config SENSORS_ARM_SCPI
tristate "ARM SCPI Sensors"
depends on ARM_SCPI_PROTOCOL
@@ -1219,8 +1231,9 @@ config SENSORS_NCT6775
help
If you say yes here you get support for the hardware monitoring
functionality of the Nuvoton NCT6106D, NCT6775F, NCT6776F, NCT6779D,
- NCT6791D, NCT6792D, NCT6793D, and compatible Super-I/O chips. This
- driver replaces the w83627ehf driver for NCT6775F and NCT6776F.
+ NCT6791D, NCT6792D, NCT6793D, NCT6795D, NCT6796D, and compatible
+ Super-I/O chips. This driver replaces the w83627ehf driver for
+ NCT6775F and NCT6776F.
This driver can also be built as a module. If so, the module
will be called nct6775.
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index f814b4ace138..e7d52a36e6c4 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -46,6 +46,7 @@ obj-$(CONFIG_SENSORS_ADT7462) += adt7462.o
obj-$(CONFIG_SENSORS_ADT7470) += adt7470.o
obj-$(CONFIG_SENSORS_ADT7475) += adt7475.o
obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o
+obj-$(CONFIG_SENSORS_ARM_SCMI) += scmi-hwmon.o
obj-$(CONFIG_SENSORS_ARM_SCPI) += scpi-hwmon.o
obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o
obj-$(CONFIG_SENSORS_ASPEED) += aspeed-pwm-tacho.o
diff --git a/drivers/hwmon/g762.c b/drivers/hwmon/g762.c
index 6d1208b2b6d2..6c83c385a7ca 100644
--- a/drivers/hwmon/g762.c
+++ b/drivers/hwmon/g762.c
@@ -128,7 +128,6 @@ enum g762_regs {
G762_REG_FAN_CMD2_GEAR_MODE_1)) >> 2))
struct g762_data {
- struct device *hwmon_dev;
struct i2c_client *client;
struct clk *clk;
@@ -594,6 +593,14 @@ MODULE_DEVICE_TABLE(of, g762_dt_match);
* call to g762_of_clock_disable(). Note that a reference to clock is kept
* in our private data structure to be used in this function.
*/
+static void g762_of_clock_disable(void *data)
+{
+ struct g762_data *g762 = data;
+
+ clk_disable_unprepare(g762->clk);
+ clk_put(g762->clk);
+}
+
static int g762_of_clock_enable(struct i2c_client *client)
{
struct g762_data *data;
@@ -626,6 +633,7 @@ static int g762_of_clock_enable(struct i2c_client *client)
data = i2c_get_clientdata(client);
data->clk = clk;
+ devm_add_action(&client->dev, g762_of_clock_disable, data);
return 0;
clk_unprep:
@@ -637,17 +645,6 @@ static int g762_of_clock_enable(struct i2c_client *client)
return ret;
}
-static void g762_of_clock_disable(struct i2c_client *client)
-{
- struct g762_data *data = i2c_get_clientdata(client);
-
- if (!data->clk)
- return;
-
- clk_disable_unprepare(data->clk);
- clk_put(data->clk);
-}
-
static int g762_of_prop_import_one(struct i2c_client *client,
const char *pname,
int (*psetter)(struct device *dev,
@@ -698,8 +695,6 @@ static int g762_of_clock_enable(struct i2c_client *client)
{
return 0;
}
-
-static void g762_of_clock_disable(struct i2c_client *client) { }
#endif
/*
@@ -1054,6 +1049,7 @@ static inline int g762_fan_init(struct device *dev)
static int g762_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
+ struct device *hwmon_dev;
struct g762_data *data;
int ret;
@@ -1080,35 +1076,15 @@ static int g762_probe(struct i2c_client *client, const struct i2c_device_id *id)
return ret;
ret = g762_of_prop_import(client);
if (ret)
- goto clock_dis;
+ return ret;
/* ... or platform_data */
ret = g762_pdata_prop_import(client);
if (ret)
- goto clock_dis;
+ return ret;
- data->hwmon_dev = hwmon_device_register_with_groups(dev, client->name,
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
data, g762_groups);
- if (IS_ERR(data->hwmon_dev)) {
- ret = PTR_ERR(data->hwmon_dev);
- goto clock_dis;
- }
-
- return 0;
-
- clock_dis:
- g762_of_clock_disable(client);
-
- return ret;
-}
-
-static int g762_remove(struct i2c_client *client)
-{
- struct g762_data *data = i2c_get_clientdata(client);
-
- hwmon_device_unregister(data->hwmon_dev);
- g762_of_clock_disable(client);
-
- return 0;
+ return PTR_ERR_OR_ZERO(hwmon_dev);
}
static struct i2c_driver g762_driver = {
@@ -1117,7 +1093,6 @@ static struct i2c_driver g762_driver = {
.of_match_table = of_match_ptr(g762_dt_match),
},
.probe = g762_probe,
- .remove = g762_remove,
.id_table = g762_id,
};
diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c
index 051a72eecb24..d2cc55e21374 100644
--- a/drivers/hwmon/k10temp.c
+++ b/drivers/hwmon/k10temp.c
@@ -40,6 +40,10 @@ static DEFINE_MUTEX(nb_smu_ind_mutex);
#define PCI_DEVICE_ID_AMD_17H_DF_F3 0x1463
#endif
+#ifndef PCI_DEVICE_ID_AMD_17H_RR_NB
+#define PCI_DEVICE_ID_AMD_17H_RR_NB 0x15d0
+#endif
+
/* CPUID function 0x80000001, ebx */
#define CPUID_PKGTYPE_MASK 0xf0000000
#define CPUID_PKGTYPE_F 0x00000000
@@ -72,6 +76,7 @@ struct k10temp_data {
struct pci_dev *pdev;
void (*read_tempreg)(struct pci_dev *pdev, u32 *regval);
int temp_offset;
+ u32 temp_adjust_mask;
};
struct tctl_offset {
@@ -84,6 +89,7 @@ static const struct tctl_offset tctl_offset_table[] = {
{ 0x17, "AMD Ryzen 5 1600X", 20000 },
{ 0x17, "AMD Ryzen 7 1700X", 20000 },
{ 0x17, "AMD Ryzen 7 1800X", 20000 },
+ { 0x17, "AMD Ryzen 7 2700X", 10000 },
{ 0x17, "AMD Ryzen Threadripper 1950X", 27000 },
{ 0x17, "AMD Ryzen Threadripper 1920X", 27000 },
{ 0x17, "AMD Ryzen Threadripper 1900X", 27000 },
@@ -129,6 +135,8 @@ static ssize_t temp1_input_show(struct device *dev,
data->read_tempreg(data->pdev, &regval);
temp = (regval >> 21) * 125;
+ if (regval & data->temp_adjust_mask)
+ temp -= 49000;
if (temp > data->temp_offset)
temp -= data->temp_offset;
else
@@ -259,12 +267,14 @@ static int k10temp_probe(struct pci_dev *pdev,
data->pdev = pdev;
if (boot_cpu_data.x86 == 0x15 && (boot_cpu_data.x86_model == 0x60 ||
- boot_cpu_data.x86_model == 0x70))
+ boot_cpu_data.x86_model == 0x70)) {
data->read_tempreg = read_tempreg_nb_f15;
- else if (boot_cpu_data.x86 == 0x17)
+ } else if (boot_cpu_data.x86 == 0x17) {
+ data->temp_adjust_mask = 0x80000;
data->read_tempreg = read_tempreg_nb_f17;
- else
+ } else {
data->read_tempreg = read_tempreg_pci;
+ }
for (i = 0; i < ARRAY_SIZE(tctl_offset_table); i++) {
const struct tctl_offset *entry = &tctl_offset_table[i];
@@ -292,6 +302,7 @@ static const struct pci_device_id k10temp_id_table[] = {
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F3) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F3) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_DF_F3) },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_RR_NB) },
{}
};
MODULE_DEVICE_TABLE(pci, k10temp_id_table);
diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c
index 2a91974a10bb..d40fe5122e94 100644
--- a/drivers/hwmon/lm92.c
+++ b/drivers/hwmon/lm92.c
@@ -52,6 +52,7 @@
*/
static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b,
I2C_CLIENT_END };
+enum chips { lm92, max6635 };
/* The LM92 registers */
#define LM92_REG_CONFIG 0x01 /* 8-bit, RW */
@@ -259,62 +260,6 @@ static void lm92_init_client(struct i2c_client *client)
config & 0xFE);
}
-/*
- * The MAX6635 has no identification register, so we have to use tricks
- * to identify it reliably. This is somewhat slow.
- * Note that we do NOT rely on the 2 MSB of the configuration register
- * always reading 0, as suggested by the datasheet, because it was once
- * reported not to be true.
- */
-static int max6635_check(struct i2c_client *client)
-{
- u16 temp_low, temp_high, temp_hyst, temp_crit;
- u8 conf;
- int i;
-
- /*
- * No manufacturer ID register, so a read from this address will
- * always return the last read value.
- */
- temp_low = i2c_smbus_read_word_data(client, LM92_REG_TEMP_LOW);
- if (i2c_smbus_read_word_data(client, LM92_REG_MAN_ID) != temp_low)
- return 0;
- temp_high = i2c_smbus_read_word_data(client, LM92_REG_TEMP_HIGH);
- if (i2c_smbus_read_word_data(client, LM92_REG_MAN_ID) != temp_high)
- return 0;
-
- /* Limits are stored as integer values (signed, 9-bit). */
- if ((temp_low & 0x7f00) || (temp_high & 0x7f00))
- return 0;
- temp_hyst = i2c_smbus_read_word_data(client, LM92_REG_TEMP_HYST);
- temp_crit = i2c_smbus_read_word_data(client, LM92_REG_TEMP_CRIT);
- if ((temp_hyst & 0x7f00) || (temp_crit & 0x7f00))
- return 0;
-
- /*
- * Registers addresses were found to cycle over 16-byte boundaries.
- * We don't test all registers with all offsets so as to save some
- * reads and time, but this should still be sufficient to dismiss
- * non-MAX6635 chips.
- */
- conf = i2c_smbus_read_byte_data(client, LM92_REG_CONFIG);
- for (i = 16; i < 96; i *= 2) {
- if (temp_hyst != i2c_smbus_read_word_data(client,
- LM92_REG_TEMP_HYST + i - 16)
- || temp_crit != i2c_smbus_read_word_data(client,
- LM92_REG_TEMP_CRIT + i)
- || temp_low != i2c_smbus_read_word_data(client,
- LM92_REG_TEMP_LOW + i + 16)
- || temp_high != i2c_smbus_read_word_data(client,
- LM92_REG_TEMP_HIGH + i + 32)
- || conf != i2c_smbus_read_byte_data(client,
- LM92_REG_CONFIG + i))
- return 0;
- }
-
- return 1;
-}
-
static struct attribute *lm92_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp1_crit.dev_attr.attr,
@@ -348,8 +293,6 @@ static int lm92_detect(struct i2c_client *new_client,
if ((config & 0xe0) == 0x00 && man_id == 0x0180)
pr_info("lm92: Found National Semiconductor LM92 chip\n");
- else if (max6635_check(new_client))
- pr_info("lm92: Found Maxim MAX6635 chip\n");
else
return -ENODEV;
@@ -387,8 +330,8 @@ static int lm92_probe(struct i2c_client *new_client,
*/
static const struct i2c_device_id lm92_id[] = {
- { "lm92", 0 },
- /* max6635 could be added here */
+ { "lm92", lm92 },
+ { "max6635", max6635 },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm92_id);
diff --git a/drivers/hwmon/nct6683.c b/drivers/hwmon/nct6683.c
index 8b0bc4fc06e8..b0bc77bf2cd9 100644
--- a/drivers/hwmon/nct6683.c
+++ b/drivers/hwmon/nct6683.c
@@ -1380,8 +1380,8 @@ static int __init nct6683_find(int sioaddr, struct nct6683_sio_data *sio_data)
/* Activate logical device if needed */
val = superio_inb(sioaddr, SIO_REG_ENABLE);
if (!(val & 0x01)) {
- pr_err("EC is disabled\n");
- goto fail;
+ pr_warn("Forcibly enabling EC access. Data may be unusable.\n");
+ superio_outb(sioaddr, SIO_REG_ENABLE, val | 0x01);
}
superio_exit(sioaddr);
diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c
index c219e43b8f02..aebce560bfaf 100644
--- a/drivers/hwmon/nct6775.c
+++ b/drivers/hwmon/nct6775.c
@@ -41,7 +41,7 @@
* nct6792d 15 6 6 2+6 0xc910 0xc1 0x5ca3
* nct6793d 15 6 6 2+6 0xd120 0xc1 0x5ca3
* nct6795d 14 6 6 2+6 0xd350 0xc1 0x5ca3
- *
+ * nct6796d 14 7 7 2+6 0xd420 0xc1 0x5ca3
*
* #temp lists the number of monitored temperature sources (first value) plus
* the number of directly connectable temperature sensors (second value).
@@ -68,7 +68,7 @@
#define USE_ALTERNATE
enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793,
- nct6795 };
+ nct6795, nct6796 };
/* used to set data->name = nct6775_device_names[data->sio_kind] */
static const char * const nct6775_device_names[] = {
@@ -80,6 +80,7 @@ static const char * const nct6775_device_names[] = {
"nct6792",
"nct6793",
"nct6795",
+ "nct6796",
};
static const char * const nct6775_sio_names[] __initconst = {
@@ -91,6 +92,7 @@ static const char * const nct6775_sio_names[] __initconst = {
"NCT6792D",
"NCT6793D",
"NCT6795D",
+ "NCT6796D",
};
static unsigned short force_id;
@@ -125,6 +127,7 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal");
#define SIO_NCT6792_ID 0xc910
#define SIO_NCT6793_ID 0xd120
#define SIO_NCT6795_ID 0xd350
+#define SIO_NCT6796_ID 0xd420
#define SIO_ID_MASK 0xFFF0
enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 };
@@ -201,7 +204,7 @@ superio_exit(int ioreg)
#define NUM_REG_ALARM 7 /* Max number of alarm registers */
#define NUM_REG_BEEP 5 /* Max number of beep registers */
-#define NUM_FAN 6
+#define NUM_FAN 7
#define TEMP_SOURCE_VIRTUAL 0x1f
@@ -272,26 +275,26 @@ static const u8 NCT6775_PWM_MODE_MASK[] = { 0x01, 0x02, 0x01 };
/* Advanced Fan control, some values are common for all fans */
static const u16 NCT6775_REG_TARGET[] = {
- 0x101, 0x201, 0x301, 0x801, 0x901, 0xa01 };
+ 0x101, 0x201, 0x301, 0x801, 0x901, 0xa01, 0xb01 };
static const u16 NCT6775_REG_FAN_MODE[] = {
- 0x102, 0x202, 0x302, 0x802, 0x902, 0xa02 };
+ 0x102, 0x202, 0x302, 0x802, 0x902, 0xa02, 0xb02 };
static const u16 NCT6775_REG_FAN_STEP_DOWN_TIME[] = {
- 0x103, 0x203, 0x303, 0x803, 0x903, 0xa03 };
+ 0x103, 0x203, 0x303, 0x803, 0x903, 0xa03, 0xb03 };
static const u16 NCT6775_REG_FAN_STEP_UP_TIME[] = {
- 0x104, 0x204, 0x304, 0x804, 0x904, 0xa04 };
+ 0x104, 0x204, 0x304, 0x804, 0x904, 0xa04, 0xb04 };
static const u16 NCT6775_REG_FAN_STOP_OUTPUT[] = {
- 0x105, 0x205, 0x305, 0x805, 0x905, 0xa05 };
+ 0x105, 0x205, 0x305, 0x805, 0x905, 0xa05, 0xb05 };
static const u16 NCT6775_REG_FAN_START_OUTPUT[] = {
- 0x106, 0x206, 0x306, 0x806, 0x906, 0xa06 };
+ 0x106, 0x206, 0x306, 0x806, 0x906, 0xa06, 0xb06 };
static const u16 NCT6775_REG_FAN_MAX_OUTPUT[] = { 0x10a, 0x20a, 0x30a };
static const u16 NCT6775_REG_FAN_STEP_OUTPUT[] = { 0x10b, 0x20b, 0x30b };
static const u16 NCT6775_REG_FAN_STOP_TIME[] = {
- 0x107, 0x207, 0x307, 0x807, 0x907, 0xa07 };
+ 0x107, 0x207, 0x307, 0x807, 0x907, 0xa07, 0xb07 };
static const u16 NCT6775_REG_PWM[] = {
- 0x109, 0x209, 0x309, 0x809, 0x909, 0xa09 };
+ 0x109, 0x209, 0x309, 0x809, 0x909, 0xa09, 0xb09 };
static const u16 NCT6775_REG_PWM_READ[] = {
- 0x01, 0x03, 0x11, 0x13, 0x15, 0xa09 };
+ 0x01, 0x03, 0x11, 0x13, 0x15, 0xa09, 0xb09 };
static const u16 NCT6775_REG_FAN[] = { 0x630, 0x632, 0x634, 0x636, 0x638 };
static const u16 NCT6775_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d };
@@ -314,7 +317,7 @@ static const u16 NCT6775_REG_TEMP_SOURCE[ARRAY_SIZE(NCT6775_REG_TEMP)] = {
0x621, 0x622, 0x623, 0x624, 0x625, 0x626 };
static const u16 NCT6775_REG_TEMP_SEL[] = {
- 0x100, 0x200, 0x300, 0x800, 0x900, 0xa00 };
+ 0x100, 0x200, 0x300, 0x800, 0x900, 0xa00, 0xb00 };
static const u16 NCT6775_REG_WEIGHT_TEMP_SEL[] = {
0x139, 0x239, 0x339, 0x839, 0x939, 0xa39 };
@@ -330,9 +333,9 @@ static const u16 NCT6775_REG_WEIGHT_TEMP_BASE[] = {
static const u16 NCT6775_REG_TEMP_OFFSET[] = { 0x454, 0x455, 0x456 };
static const u16 NCT6775_REG_AUTO_TEMP[] = {
- 0x121, 0x221, 0x321, 0x821, 0x921, 0xa21 };
+ 0x121, 0x221, 0x321, 0x821, 0x921, 0xa21, 0xb21 };
static const u16 NCT6775_REG_AUTO_PWM[] = {
- 0x127, 0x227, 0x327, 0x827, 0x927, 0xa27 };
+ 0x127, 0x227, 0x327, 0x827, 0x927, 0xa27, 0xb27 };
#define NCT6775_AUTO_TEMP(data, nr, p) ((data)->REG_AUTO_TEMP[nr] + (p))
#define NCT6775_AUTO_PWM(data, nr, p) ((data)->REG_AUTO_PWM[nr] + (p))
@@ -340,9 +343,9 @@ static const u16 NCT6775_REG_AUTO_PWM[] = {
static const u16 NCT6775_REG_CRITICAL_ENAB[] = { 0x134, 0x234, 0x334 };
static const u16 NCT6775_REG_CRITICAL_TEMP[] = {
- 0x135, 0x235, 0x335, 0x835, 0x935, 0xa35 };
+ 0x135, 0x235, 0x335, 0x835, 0x935, 0xa35, 0xb35 };
static const u16 NCT6775_REG_CRITICAL_TEMP_TOLERANCE[] = {
- 0x138, 0x238, 0x338, 0x838, 0x938, 0xa38 };
+ 0x138, 0x238, 0x338, 0x838, 0x938, 0xa38, 0xb38 };
static const char *const nct6775_temp_label[] = {
"",
@@ -414,13 +417,15 @@ static const s8 NCT6776_BEEP_BITS[] = {
30, 31 }; /* intrusion0, intrusion1 */
static const u16 NCT6776_REG_TOLERANCE_H[] = {
- 0x10c, 0x20c, 0x30c, 0x80c, 0x90c, 0xa0c };
+ 0x10c, 0x20c, 0x30c, 0x80c, 0x90c, 0xa0c, 0xb0c };
static const u8 NCT6776_REG_PWM_MODE[] = { 0x04, 0, 0, 0, 0, 0 };
static const u8 NCT6776_PWM_MODE_MASK[] = { 0x01, 0, 0, 0, 0, 0 };
-static const u16 NCT6776_REG_FAN_MIN[] = { 0x63a, 0x63c, 0x63e, 0x640, 0x642 };
-static const u16 NCT6776_REG_FAN_PULSES[] = { 0x644, 0x645, 0x646, 0, 0 };
+static const u16 NCT6776_REG_FAN_MIN[] = {
+ 0x63a, 0x63c, 0x63e, 0x640, 0x642, 0x64a, 0x64c };
+static const u16 NCT6776_REG_FAN_PULSES[] = {
+ 0x644, 0x645, 0x646, 0x647, 0x648, 0x649, 0 };
static const u16 NCT6776_REG_WEIGHT_DUTY_BASE[] = {
0x13e, 0x23e, 0x33e, 0x83e, 0x93e, 0xa3e };
@@ -495,15 +500,15 @@ static const s8 NCT6779_BEEP_BITS[] = {
30, 31 }; /* intrusion0, intrusion1 */
static const u16 NCT6779_REG_FAN[] = {
- 0x4b0, 0x4b2, 0x4b4, 0x4b6, 0x4b8, 0x4ba };
+ 0x4b0, 0x4b2, 0x4b4, 0x4b6, 0x4b8, 0x4ba, 0x660 };
static const u16 NCT6779_REG_FAN_PULSES[] = {
- 0x644, 0x645, 0x646, 0x647, 0x648, 0x649 };
+ 0x644, 0x645, 0x646, 0x647, 0x648, 0x649, 0 };
static const u16 NCT6779_REG_CRITICAL_PWM_ENABLE[] = {
- 0x136, 0x236, 0x336, 0x836, 0x936, 0xa36 };
+ 0x136, 0x236, 0x336, 0x836, 0x936, 0xa36, 0xb36 };
#define NCT6779_CRITICAL_PWM_ENABLE_MASK 0x01
static const u16 NCT6779_REG_CRITICAL_PWM[] = {
- 0x137, 0x237, 0x337, 0x837, 0x937, 0xa37 };
+ 0x137, 0x237, 0x337, 0x837, 0x937, 0xa37, 0xb37 };
static const u16 NCT6779_REG_TEMP[] = { 0x27, 0x150 };
static const u16 NCT6779_REG_TEMP_MON[] = { 0x73, 0x75, 0x77, 0x79, 0x7b };
@@ -570,12 +575,12 @@ static const u16 NCT6779_REG_TEMP_CRIT[32] = {
#define NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE 0x28
-static const u16 NCT6791_REG_WEIGHT_TEMP_SEL[6] = { 0, 0x239 };
-static const u16 NCT6791_REG_WEIGHT_TEMP_STEP[6] = { 0, 0x23a };
-static const u16 NCT6791_REG_WEIGHT_TEMP_STEP_TOL[6] = { 0, 0x23b };
-static const u16 NCT6791_REG_WEIGHT_DUTY_STEP[6] = { 0, 0x23c };
-static const u16 NCT6791_REG_WEIGHT_TEMP_BASE[6] = { 0, 0x23d };
-static const u16 NCT6791_REG_WEIGHT_DUTY_BASE[6] = { 0, 0x23e };
+static const u16 NCT6791_REG_WEIGHT_TEMP_SEL[NUM_FAN] = { 0, 0x239 };
+static const u16 NCT6791_REG_WEIGHT_TEMP_STEP[NUM_FAN] = { 0, 0x23a };
+static const u16 NCT6791_REG_WEIGHT_TEMP_STEP_TOL[NUM_FAN] = { 0, 0x23b };
+static const u16 NCT6791_REG_WEIGHT_DUTY_STEP[NUM_FAN] = { 0, 0x23c };
+static const u16 NCT6791_REG_WEIGHT_TEMP_BASE[NUM_FAN] = { 0, 0x23d };
+static const u16 NCT6791_REG_WEIGHT_DUTY_BASE[NUM_FAN] = { 0, 0x23e };
static const u16 NCT6791_REG_ALARM[NUM_REG_ALARM] = {
0x459, 0x45A, 0x45B, 0x568, 0x45D };
@@ -707,6 +712,43 @@ static const char *const nct6795_temp_label[] = {
#define NCT6795_TEMP_MASK 0xbfffff7e
+static const char *const nct6796_temp_label[] = {
+ "",
+ "SYSTIN",
+ "CPUTIN",
+ "AUXTIN0",
+ "AUXTIN1",
+ "AUXTIN2",
+ "AUXTIN3",
+ "AUXTIN4",
+ "SMBUSMASTER 0",
+ "SMBUSMASTER 1",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "PECI Agent 0",
+ "PECI Agent 1",
+ "PCH_CHIP_CPU_MAX_TEMP",
+ "PCH_CHIP_TEMP",
+ "PCH_CPU_TEMP",
+ "PCH_MCH_TEMP",
+ "PCH_DIM0_TEMP",
+ "PCH_DIM1_TEMP",
+ "PCH_DIM2_TEMP",
+ "PCH_DIM3_TEMP",
+ "BYTE_TEMP0",
+ "BYTE_TEMP1",
+ "PECI Agent 0 Calibration",
+ "PECI Agent 1 Calibration",
+ "",
+ "Virtual_TEMP"
+};
+
+#define NCT6796_TEMP_MASK 0xbfff03fe
+
/* NCT6102D/NCT6106D specific data */
#define NCT6106_REG_VBAT 0x318
@@ -1231,11 +1273,13 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg)
case nct6792:
case nct6793:
case nct6795:
+ case nct6796:
return reg == 0x150 || reg == 0x153 || reg == 0x155 ||
((reg & 0xfff0) == 0x4b0 && (reg & 0x000f) < 0x0b) ||
reg == 0x402 ||
reg == 0x63a || reg == 0x63c || reg == 0x63e ||
- reg == 0x640 || reg == 0x642 ||
+ reg == 0x640 || reg == 0x642 || reg == 0x64a ||
+ reg == 0x64c || reg == 0x660 ||
reg == 0x73 || reg == 0x75 || reg == 0x77 || reg == 0x79 ||
reg == 0x7b || reg == 0x7d;
}
@@ -1469,7 +1513,7 @@ static void nct6775_update_pwm(struct device *dev)
duty_is_dc = data->REG_PWM_MODE[i] &&
(nct6775_read_value(data, data->REG_PWM_MODE[i])
& data->PWM_MODE_MASK[i]);
- data->pwm_mode[i] = duty_is_dc;
+ data->pwm_mode[i] = !duty_is_dc;
fanmodecfg = nct6775_read_value(data, data->REG_FAN_MODE[i]);
for (j = 0; j < ARRAY_SIZE(data->REG_PWM); j++) {
@@ -1584,6 +1628,7 @@ static void nct6775_update_pwm_limits(struct device *dev)
case nct6792:
case nct6793:
case nct6795:
+ case nct6796:
reg = nct6775_read_value(data,
data->REG_CRITICAL_PWM_ENABLE[i]);
if (reg & data->CRITICAL_PWM_ENABLE_MASK)
@@ -2092,6 +2137,8 @@ static umode_t nct6775_fan_is_visible(struct kobject *kobj,
return 0;
if (nr == 2 && data->BEEP_BITS[FAN_ALARM_BASE + fan] == -1)
return 0;
+ if (nr == 3 && !data->REG_FAN_PULSES[fan])
+ return 0;
if (nr == 4 && !(data->has_fan_min & BIT(fan)))
return 0;
if (nr == 5 && data->kind != nct6775)
@@ -2350,7 +2397,7 @@ show_pwm_mode(struct device *dev, struct device_attribute *attr, char *buf)
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
- return sprintf(buf, "%d\n", !data->pwm_mode[sattr->index]);
+ return sprintf(buf, "%d\n", data->pwm_mode[sattr->index]);
}
static ssize_t
@@ -2371,9 +2418,9 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr,
if (val > 1)
return -EINVAL;
- /* Setting DC mode is not supported for all chips/channels */
+ /* Setting DC mode (0) is not supported for all chips/channels */
if (data->REG_PWM_MODE[nr] == 0) {
- if (val)
+ if (!val)
return -EINVAL;
return count;
}
@@ -2382,7 +2429,7 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr,
data->pwm_mode[nr] = val;
reg = nct6775_read_value(data, data->REG_PWM_MODE[nr]);
reg &= ~data->PWM_MODE_MASK[nr];
- if (val)
+ if (!val)
reg |= data->PWM_MODE_MASK[nr];
nct6775_write_value(data, data->REG_PWM_MODE[nr], reg);
mutex_unlock(&data->update_lock);
@@ -3004,6 +3051,7 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr,
case nct6792:
case nct6793:
case nct6795:
+ case nct6796:
nct6775_write_value(data, data->REG_CRITICAL_PWM[nr],
val);
reg = nct6775_read_value(data,
@@ -3358,8 +3406,10 @@ static inline void nct6775_init_device(struct nct6775_data *data)
static void
nct6775_check_fan_inputs(struct nct6775_data *data)
{
- bool fan3pin, fan4pin, fan4min, fan5pin, fan6pin;
- bool pwm3pin, pwm4pin, pwm5pin, pwm6pin;
+ bool fan3pin = false, fan4pin = false, fan4min = false;
+ bool fan5pin = false, fan6pin = false, fan7pin = false;
+ bool pwm3pin = false, pwm4pin = false, pwm5pin = false;
+ bool pwm6pin = false, pwm7pin = false;
int sioreg = data->sioreg;
int regval;
@@ -3376,12 +3426,6 @@ nct6775_check_fan_inputs(struct nct6775_data *data)
/* On NCT6775, fan4 shares pins with the fdc interface */
fan4pin = !(superio_inb(sioreg, 0x2A) & 0x80);
- fan4min = false;
- fan5pin = false;
- fan6pin = false;
- pwm4pin = false;
- pwm5pin = false;
- pwm6pin = false;
} else if (data->kind == nct6776) {
bool gpok = superio_inb(sioreg, 0x27) & 0x80;
const char *board_vendor, *board_name;
@@ -3421,25 +3465,15 @@ nct6775_check_fan_inputs(struct nct6775_data *data)
fan5pin = superio_inb(sioreg, 0x1C) & 0x02;
fan4min = fan4pin;
- fan6pin = false;
pwm3pin = fan3pin;
- pwm4pin = false;
- pwm5pin = false;
- pwm6pin = false;
} else if (data->kind == nct6106) {
regval = superio_inb(sioreg, 0x24);
fan3pin = !(regval & 0x80);
pwm3pin = regval & 0x08;
-
- fan4pin = false;
- fan4min = false;
- fan5pin = false;
- fan6pin = false;
- pwm4pin = false;
- pwm5pin = false;
- pwm6pin = false;
- } else { /* NCT6779D, NCT6791D, NCT6792D, NCT6793D, or NCT6795D */
- int regval_1b, regval_2a, regval_eb;
+ } else {
+ /* NCT6779D, NCT6791D, NCT6792D, NCT6793D, NCT6795D, NCT6796D */
+ int regval_1b, regval_2a, regval_2f;
+ bool dsw_en;
regval = superio_inb(sioreg, 0x1c);
@@ -3460,31 +3494,60 @@ nct6775_check_fan_inputs(struct nct6775_data *data)
break;
case nct6793:
case nct6795:
+ case nct6796:
regval_1b = superio_inb(sioreg, 0x1b);
regval_2a = superio_inb(sioreg, 0x2a);
+ regval_2f = superio_inb(sioreg, 0x2f);
+ dsw_en = regval_2f & BIT(3);
if (!pwm5pin)
pwm5pin = regval & BIT(7);
- fan6pin = regval & BIT(1);
- pwm6pin = regval & BIT(0);
+
if (!fan5pin)
fan5pin = regval_1b & BIT(5);
superio_select(sioreg, NCT6775_LD_12);
- regval_eb = superio_inb(sioreg, 0xeb);
- if (!fan5pin)
- fan5pin = regval_eb & BIT(5);
- if (!pwm5pin)
- pwm5pin = (regval_eb & BIT(4)) &&
- !(regval_2a & BIT(0));
- if (!fan6pin)
- fan6pin = regval_eb & BIT(3);
- if (!pwm6pin)
- pwm6pin = regval_eb & BIT(2);
+ if (data->kind != nct6796) {
+ int regval_eb = superio_inb(sioreg, 0xeb);
+
+ if (!dsw_en) {
+ fan6pin = regval & BIT(1);
+ pwm6pin = regval & BIT(0);
+ }
+
+ if (!fan5pin)
+ fan5pin = regval_eb & BIT(5);
+ if (!pwm5pin)
+ pwm5pin = (regval_eb & BIT(4)) &&
+ !(regval_2a & BIT(0));
+ if (!fan6pin)
+ fan6pin = regval_eb & BIT(3);
+ if (!pwm6pin)
+ pwm6pin = regval_eb & BIT(2);
+ }
+
+ if (data->kind == nct6795 || data->kind == nct6796) {
+ int regval_ed = superio_inb(sioreg, 0xed);
+
+ if (!fan6pin)
+ fan6pin = (regval_2a & BIT(4)) &&
+ (!dsw_en ||
+ (dsw_en && (regval_ed & BIT(4))));
+ if (!pwm6pin)
+ pwm6pin = (regval_2a & BIT(3)) &&
+ (regval_ed & BIT(2));
+ }
+
+ if (data->kind == nct6796) {
+ int regval_1d = superio_inb(sioreg, 0x1d);
+ int regval_2b = superio_inb(sioreg, 0x2b);
+
+ fan7pin = !(regval_2b & BIT(2));
+ pwm7pin = !(regval_1d & (BIT(2) | BIT(3)));
+ }
+
break;
default: /* NCT6779D */
- fan6pin = false;
- pwm6pin = false;
break;
}
@@ -3493,11 +3556,11 @@ nct6775_check_fan_inputs(struct nct6775_data *data)
/* fan 1 and 2 (0x03) are always present */
data->has_fan = 0x03 | (fan3pin << 2) | (fan4pin << 3) |
- (fan5pin << 4) | (fan6pin << 5);
+ (fan5pin << 4) | (fan6pin << 5) | (fan7pin << 6);
data->has_fan_min = 0x03 | (fan3pin << 2) | (fan4min << 3) |
- (fan5pin << 4);
+ (fan5pin << 4) | (fan6pin << 5) | (fan7pin << 6);
data->has_pwm = 0x03 | (pwm3pin << 2) | (pwm4pin << 3) |
- (pwm5pin << 4) | (pwm6pin << 5);
+ (pwm5pin << 4) | (pwm6pin << 5) | (pwm7pin << 6);
}
static void add_temp_sensors(struct nct6775_data *data, const u16 *regp,
@@ -3856,8 +3919,9 @@ static int nct6775_probe(struct platform_device *pdev)
case nct6792:
case nct6793:
case nct6795:
+ case nct6796:
data->in_num = 15;
- data->pwm_num = 6;
+ data->pwm_num = (data->kind == nct6796) ? 7 : 6;
data->auto_pwm_num = 4;
data->has_fan_div = false;
data->temp_fixed_num = 6;
@@ -3891,6 +3955,10 @@ static int nct6775_probe(struct platform_device *pdev)
data->temp_label = nct6795_temp_label;
data->temp_mask = NCT6795_TEMP_MASK;
break;
+ case nct6796:
+ data->temp_label = nct6796_temp_label;
+ data->temp_mask = NCT6796_TEMP_MASK;
+ break;
}
data->REG_CONFIG = NCT6775_REG_CONFIG;
@@ -4159,6 +4227,7 @@ static int nct6775_probe(struct platform_device *pdev)
case nct6792:
case nct6793:
case nct6795:
+ case nct6796:
break;
}
@@ -4193,6 +4262,7 @@ static int nct6775_probe(struct platform_device *pdev)
case nct6792:
case nct6793:
case nct6795:
+ case nct6796:
tmp |= 0x7e;
break;
}
@@ -4291,7 +4361,8 @@ static int __maybe_unused nct6775_resume(struct device *dev)
superio_outb(sioreg, SIO_REG_ENABLE, data->sio_reg_enable);
if (data->kind == nct6791 || data->kind == nct6792 ||
- data->kind == nct6793 || data->kind == nct6795)
+ data->kind == nct6793 || data->kind == nct6795 ||
+ data->kind == nct6796)
nct6791_enable_io_mapping(sioreg);
superio_exit(sioreg);
@@ -4391,6 +4462,9 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data)
case SIO_NCT6795_ID:
sio_data->kind = nct6795;
break;
+ case SIO_NCT6796_ID:
+ sio_data->kind = nct6796;
+ break;
default:
if (val != 0xffff)
pr_debug("unsupported chip ID: 0x%04x\n", val);
@@ -4417,7 +4491,8 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data)
}
if (sio_data->kind == nct6791 || sio_data->kind == nct6792 ||
- sio_data->kind == nct6793 || sio_data->kind == nct6795)
+ sio_data->kind == nct6793 || sio_data->kind == nct6795 ||
+ sio_data->kind == nct6796)
nct6791_enable_io_mapping(sioaddr);
superio_exit(sioaddr);
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index 6e4298e99222..e71aec69e76e 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -31,8 +31,8 @@ config SENSORS_ADM1275
default n
help
If you say yes here you get hardware monitoring support for Analog
- Devices ADM1075, ADM1275, ADM1276, ADM1278, ADM1293, and ADM1294
- Hot-Swap Controller and Digital Power Monitors.
+ Devices ADM1075, ADM1272, ADM1275, ADM1276, ADM1278, ADM1293,
+ and ADM1294 Hot-Swap Controller and Digital Power Monitors.
This driver can also be built as a module. If so, the module will
be called adm1275.
diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c
index 00d6995af4c2..13600fa79e7f 100644
--- a/drivers/hwmon/pmbus/adm1275.c
+++ b/drivers/hwmon/pmbus/adm1275.c
@@ -3,6 +3,7 @@
* and Digital Power Monitor
*
* Copyright (c) 2011 Ericsson AB.
+ * Copyright (c) 2018 Guenter Roeck
*
* 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,7 +25,7 @@
#include <linux/bitops.h>
#include "pmbus.h"
-enum chips { adm1075, adm1275, adm1276, adm1278, adm1293, adm1294 };
+enum chips { adm1075, adm1272, adm1275, adm1276, adm1278, adm1293, adm1294 };
#define ADM1275_MFR_STATUS_IOUT_WARN2 BIT(0)
#define ADM1293_MFR_STATUS_VAUX_UV_WARN BIT(5)
@@ -41,6 +42,8 @@ enum chips { adm1075, adm1275, adm1276, adm1278, adm1293, adm1294 };
#define ADM1075_IRANGE_25 BIT(3)
#define ADM1075_IRANGE_MASK (BIT(3) | BIT(4))
+#define ADM1272_IRANGE BIT(0)
+
#define ADM1278_TEMP1_EN BIT(3)
#define ADM1278_VIN_EN BIT(2)
#define ADM1278_VOUT_EN BIT(1)
@@ -105,6 +108,19 @@ static const struct coefficients adm1075_coefficients[] = {
[4] = { 4279, 0, -1 }, /* power, irange50 */
};
+static const struct coefficients adm1272_coefficients[] = {
+ [0] = { 6770, 0, -2 }, /* voltage, vrange 60V */
+ [1] = { 4062, 0, -2 }, /* voltage, vrange 100V */
+ [2] = { 1326, 20480, -1 }, /* current, vsense range 15mV */
+ [3] = { 663, 20480, -1 }, /* current, vsense range 30mV */
+ [4] = { 3512, 0, -2 }, /* power, vrange 60V, irange 15mV */
+ [5] = { 21071, 0, -3 }, /* power, vrange 100V, irange 15mV */
+ [6] = { 17561, 0, -3 }, /* power, vrange 60V, irange 30mV */
+ [7] = { 10535, 0, -3 }, /* power, vrange 100V, irange 30mV */
+ [8] = { 42, 31871, -1 }, /* temperature */
+
+};
+
static const struct coefficients adm1275_coefficients[] = {
[0] = { 19199, 0, -2 }, /* voltage, vrange set */
[1] = { 6720, 0, -1 }, /* voltage, vrange not set */
@@ -154,7 +170,7 @@ static int adm1275_read_word_data(struct i2c_client *client, int page, int reg)
const struct adm1275_data *data = to_adm1275_data(info);
int ret = 0;
- if (page)
+ if (page > 0)
return -ENXIO;
switch (reg) {
@@ -240,7 +256,7 @@ static int adm1275_write_word_data(struct i2c_client *client, int page, int reg,
const struct adm1275_data *data = to_adm1275_data(info);
int ret;
- if (page)
+ if (page > 0)
return -ENXIO;
switch (reg) {
@@ -335,6 +351,7 @@ static int adm1275_read_byte_data(struct i2c_client *client, int page, int reg)
static const struct i2c_device_id adm1275_id[] = {
{ "adm1075", adm1075 },
+ { "adm1272", adm1272 },
{ "adm1275", adm1275 },
{ "adm1276", adm1276 },
{ "adm1278", adm1278 },
@@ -451,6 +468,54 @@ static int adm1275_probe(struct i2c_client *client,
info->func[0] |=
PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
break;
+ case adm1272:
+ data->have_vout = true;
+ data->have_pin_max = true;
+ data->have_temp_max = true;
+
+ coefficients = adm1272_coefficients;
+ vindex = (config & ADM1275_VRANGE) ? 1 : 0;
+ cindex = (config & ADM1272_IRANGE) ? 3 : 2;
+ /* pindex depends on the combination of the above */
+ switch (config & (ADM1275_VRANGE | ADM1272_IRANGE)) {
+ case 0:
+ default:
+ pindex = 4;
+ break;
+ case ADM1275_VRANGE:
+ pindex = 5;
+ break;
+ case ADM1272_IRANGE:
+ pindex = 6;
+ break;
+ case ADM1275_VRANGE | ADM1272_IRANGE:
+ pindex = 7;
+ break;
+ }
+ tindex = 8;
+
+ info->func[0] |= PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT |
+ PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
+
+ /* Enable VOUT if not enabled (it is disabled by default) */
+ if (!(config & ADM1278_VOUT_EN)) {
+ config |= ADM1278_VOUT_EN;
+ ret = i2c_smbus_write_byte_data(client,
+ ADM1275_PMON_CONFIG,
+ config);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "Failed to enable VOUT monitoring\n");
+ return -ENODEV;
+ }
+ }
+
+ if (config & ADM1278_TEMP1_EN)
+ info->func[0] |=
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
+ if (config & ADM1278_VIN_EN)
+ info->func[0] |= PMBUS_HAVE_VIN;
+ break;
case adm1275:
if (device_config & ADM1275_IOUT_WARN2_SELECT)
data->have_oc_fault = true;
diff --git a/drivers/hwmon/pmbus/max8688.c b/drivers/hwmon/pmbus/max8688.c
index dd4883a19045..e951f9b87abb 100644
--- a/drivers/hwmon/pmbus/max8688.c
+++ b/drivers/hwmon/pmbus/max8688.c
@@ -45,7 +45,7 @@ static int max8688_read_word_data(struct i2c_client *client, int page, int reg)
{
int ret;
- if (page)
+ if (page > 0)
return -ENXIO;
switch (reg) {
diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c
index b74dbeca2e8d..70cecb06f93c 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -19,6 +19,7 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include <linux/debugfs.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_device.h>
@@ -27,6 +28,8 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/pmbus.h>
+#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
#include "pmbus.h"
enum chips { ucd9000, ucd90120, ucd90124, ucd90160, ucd9090, ucd90910 };
@@ -35,8 +38,19 @@ enum chips { ucd9000, ucd90120, ucd90124, ucd90160, ucd9090, ucd90910 };
#define UCD9000_NUM_PAGES 0xd6
#define UCD9000_FAN_CONFIG_INDEX 0xe7
#define UCD9000_FAN_CONFIG 0xe8
+#define UCD9000_MFR_STATUS 0xf3
+#define UCD9000_GPIO_SELECT 0xfa
+#define UCD9000_GPIO_CONFIG 0xfb
#define UCD9000_DEVICE_ID 0xfd
+/* GPIO CONFIG bits */
+#define UCD9000_GPIO_CONFIG_ENABLE BIT(0)
+#define UCD9000_GPIO_CONFIG_OUT_ENABLE BIT(1)
+#define UCD9000_GPIO_CONFIG_OUT_VALUE BIT(2)
+#define UCD9000_GPIO_CONFIG_STATUS BIT(3)
+#define UCD9000_GPIO_INPUT 0
+#define UCD9000_GPIO_OUTPUT 1
+
#define UCD9000_MON_TYPE(x) (((x) >> 5) & 0x07)
#define UCD9000_MON_PAGE(x) ((x) & 0x0f)
@@ -47,12 +61,29 @@ enum chips { ucd9000, ucd90120, ucd90124, ucd90160, ucd9090, ucd90910 };
#define UCD9000_NUM_FAN 4
+#define UCD9000_GPIO_NAME_LEN 16
+#define UCD9090_NUM_GPIOS 23
+#define UCD901XX_NUM_GPIOS 26
+#define UCD90910_NUM_GPIOS 26
+
+#define UCD9000_DEBUGFS_NAME_LEN 24
+#define UCD9000_GPI_COUNT 8
+
struct ucd9000_data {
u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX];
struct pmbus_driver_info info;
+#ifdef CONFIG_GPIOLIB
+ struct gpio_chip gpio;
+#endif
+ struct dentry *debugfs;
};
#define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info)
+struct ucd9000_debugfs_entry {
+ struct i2c_client *client;
+ u8 index;
+};
+
static int ucd9000_get_fan_config(struct i2c_client *client, int fan)
{
int fan_config = 0;
@@ -149,6 +180,312 @@ static const struct of_device_id ucd9000_of_match[] = {
};
MODULE_DEVICE_TABLE(of, ucd9000_of_match);
+#ifdef CONFIG_GPIOLIB
+static int ucd9000_gpio_read_config(struct i2c_client *client,
+ unsigned int offset)
+{
+ int ret;
+
+ /* No page set required */
+ ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_SELECT, offset);
+ if (ret < 0)
+ return ret;
+
+ return i2c_smbus_read_byte_data(client, UCD9000_GPIO_CONFIG);
+}
+
+static int ucd9000_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+ struct i2c_client *client = gpiochip_get_data(gc);
+ int ret;
+
+ ret = ucd9000_gpio_read_config(client, offset);
+ if (ret < 0)
+ return ret;
+
+ return !!(ret & UCD9000_GPIO_CONFIG_STATUS);
+}
+
+static void ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset,
+ int value)
+{
+ struct i2c_client *client = gpiochip_get_data(gc);
+ int ret;
+
+ ret = ucd9000_gpio_read_config(client, offset);
+ if (ret < 0) {
+ dev_dbg(&client->dev, "failed to read GPIO %d config: %d\n",
+ offset, ret);
+ return;
+ }
+
+ if (value) {
+ if (ret & UCD9000_GPIO_CONFIG_STATUS)
+ return;
+
+ ret |= UCD9000_GPIO_CONFIG_STATUS;
+ } else {
+ if (!(ret & UCD9000_GPIO_CONFIG_STATUS))
+ return;
+
+ ret &= ~UCD9000_GPIO_CONFIG_STATUS;
+ }
+
+ ret |= UCD9000_GPIO_CONFIG_ENABLE;
+
+ /* Page set not required */
+ ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, ret);
+ if (ret < 0) {
+ dev_dbg(&client->dev, "Failed to write GPIO %d config: %d\n",
+ offset, ret);
+ return;
+ }
+
+ ret &= ~UCD9000_GPIO_CONFIG_ENABLE;
+
+ ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, ret);
+ if (ret < 0)
+ dev_dbg(&client->dev, "Failed to write GPIO %d config: %d\n",
+ offset, ret);
+}
+
+static int ucd9000_gpio_get_direction(struct gpio_chip *gc,
+ unsigned int offset)
+{
+ struct i2c_client *client = gpiochip_get_data(gc);
+ int ret;
+
+ ret = ucd9000_gpio_read_config(client, offset);
+ if (ret < 0)
+ return ret;
+
+ return !(ret & UCD9000_GPIO_CONFIG_OUT_ENABLE);
+}
+
+static int ucd9000_gpio_set_direction(struct gpio_chip *gc,
+ unsigned int offset, bool direction_out,
+ int requested_out)
+{
+ struct i2c_client *client = gpiochip_get_data(gc);
+ int ret, config, out_val;
+
+ ret = ucd9000_gpio_read_config(client, offset);
+ if (ret < 0)
+ return ret;
+
+ if (direction_out) {
+ out_val = requested_out ? UCD9000_GPIO_CONFIG_OUT_VALUE : 0;
+
+ if (ret & UCD9000_GPIO_CONFIG_OUT_ENABLE) {
+ if ((ret & UCD9000_GPIO_CONFIG_OUT_VALUE) == out_val)
+ return 0;
+ } else {
+ ret |= UCD9000_GPIO_CONFIG_OUT_ENABLE;
+ }
+
+ if (out_val)
+ ret |= UCD9000_GPIO_CONFIG_OUT_VALUE;
+ else
+ ret &= ~UCD9000_GPIO_CONFIG_OUT_VALUE;
+
+ } else {
+ if (!(ret & UCD9000_GPIO_CONFIG_OUT_ENABLE))
+ return 0;
+
+ ret &= ~UCD9000_GPIO_CONFIG_OUT_ENABLE;
+ }
+
+ ret |= UCD9000_GPIO_CONFIG_ENABLE;
+ config = ret;
+
+ /* Page set not required */
+ ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, config);
+ if (ret < 0)
+ return ret;
+
+ config &= ~UCD9000_GPIO_CONFIG_ENABLE;
+
+ return i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, config);
+}
+
+static int ucd9000_gpio_direction_input(struct gpio_chip *gc,
+ unsigned int offset)
+{
+ return ucd9000_gpio_set_direction(gc, offset, UCD9000_GPIO_INPUT, 0);
+}
+
+static int ucd9000_gpio_direction_output(struct gpio_chip *gc,
+ unsigned int offset, int val)
+{
+ return ucd9000_gpio_set_direction(gc, offset, UCD9000_GPIO_OUTPUT,
+ val);
+}
+
+static void ucd9000_probe_gpio(struct i2c_client *client,
+ const struct i2c_device_id *mid,
+ struct ucd9000_data *data)
+{
+ int rc;
+
+ switch (mid->driver_data) {
+ case ucd9090:
+ data->gpio.ngpio = UCD9090_NUM_GPIOS;
+ break;
+ case ucd90120:
+ case ucd90124:
+ case ucd90160:
+ data->gpio.ngpio = UCD901XX_NUM_GPIOS;
+ break;
+ case ucd90910:
+ data->gpio.ngpio = UCD90910_NUM_GPIOS;
+ break;
+ default:
+ return; /* GPIO support is optional. */
+ }
+
+ /*
+ * Pinmux support has not been added to the new gpio_chip.
+ * This support should be added when possible given the mux
+ * behavior of these IO devices.
+ */
+ data->gpio.label = client->name;
+ data->gpio.get_direction = ucd9000_gpio_get_direction;
+ data->gpio.direction_input = ucd9000_gpio_direction_input;
+ data->gpio.direction_output = ucd9000_gpio_direction_output;
+ data->gpio.get = ucd9000_gpio_get;
+ data->gpio.set = ucd9000_gpio_set;
+ data->gpio.can_sleep = true;
+ data->gpio.base = -1;
+ data->gpio.parent = &client->dev;
+
+ rc = devm_gpiochip_add_data(&client->dev, &data->gpio, client);
+ if (rc)
+ dev_warn(&client->dev, "Could not add gpiochip: %d\n", rc);
+}
+#else
+static void ucd9000_probe_gpio(struct i2c_client *client,
+ const struct i2c_device_id *mid,
+ struct ucd9000_data *data)
+{
+}
+#endif /* CONFIG_GPIOLIB */
+
+#ifdef CONFIG_DEBUG_FS
+static int ucd9000_get_mfr_status(struct i2c_client *client, u8 *buffer)
+{
+ int ret = pmbus_set_page(client, 0);
+
+ if (ret < 0)
+ return ret;
+
+ return i2c_smbus_read_block_data(client, UCD9000_MFR_STATUS, buffer);
+}
+
+static int ucd9000_debugfs_show_mfr_status_bit(void *data, u64 *val)
+{
+ struct ucd9000_debugfs_entry *entry = data;
+ struct i2c_client *client = entry->client;
+ u8 buffer[I2C_SMBUS_BLOCK_MAX];
+ int ret;
+
+ ret = ucd9000_get_mfr_status(client, buffer);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Attribute only created for devices with gpi fault bits at bits
+ * 16-23, which is the second byte of the response.
+ */
+ *val = !!(buffer[1] & BIT(entry->index));
+
+ return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(ucd9000_debugfs_mfr_status_bit,
+ ucd9000_debugfs_show_mfr_status_bit, NULL, "%1lld\n");
+
+static ssize_t ucd9000_debugfs_read_mfr_status(struct file *file,
+ char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ struct i2c_client *client = file->private_data;
+ u8 buffer[I2C_SMBUS_BLOCK_MAX];
+ char str[(I2C_SMBUS_BLOCK_MAX * 2) + 2];
+ char *res;
+ int rc;
+
+ rc = ucd9000_get_mfr_status(client, buffer);
+ if (rc < 0)
+ return rc;
+
+ res = bin2hex(str, buffer, min(rc, I2C_SMBUS_BLOCK_MAX));
+ *res++ = '\n';
+ *res = 0;
+
+ return simple_read_from_buffer(buf, count, ppos, str, res - str);
+}
+
+static const struct file_operations ucd9000_debugfs_show_mfr_status_fops = {
+ .llseek = noop_llseek,
+ .read = ucd9000_debugfs_read_mfr_status,
+ .open = simple_open,
+};
+
+static int ucd9000_init_debugfs(struct i2c_client *client,
+ const struct i2c_device_id *mid,
+ struct ucd9000_data *data)
+{
+ struct dentry *debugfs;
+ struct ucd9000_debugfs_entry *entries;
+ int i;
+ char name[UCD9000_DEBUGFS_NAME_LEN];
+
+ debugfs = pmbus_get_debugfs_dir(client);
+ if (!debugfs)
+ return -ENOENT;
+
+ data->debugfs = debugfs_create_dir(client->name, debugfs);
+ if (!data->debugfs)
+ return -ENOENT;
+
+ /*
+ * Of the chips this driver supports, only the UCD9090, UCD90160,
+ * and UCD90910 report GPI faults in their MFR_STATUS register, so only
+ * create the GPI fault debugfs attributes for those chips.
+ */
+ if (mid->driver_data == ucd9090 || mid->driver_data == ucd90160 ||
+ mid->driver_data == ucd90910) {
+ entries = devm_kzalloc(&client->dev,
+ sizeof(*entries) * UCD9000_GPI_COUNT,
+ GFP_KERNEL);
+ if (!entries)
+ return -ENOMEM;
+
+ for (i = 0; i < UCD9000_GPI_COUNT; i++) {
+ entries[i].client = client;
+ entries[i].index = i;
+ scnprintf(name, UCD9000_DEBUGFS_NAME_LEN,
+ "gpi%d_alarm", i + 1);
+ debugfs_create_file(name, 0444, data->debugfs,
+ &entries[i],
+ &ucd9000_debugfs_mfr_status_bit);
+ }
+ }
+
+ scnprintf(name, UCD9000_DEBUGFS_NAME_LEN, "mfr_status");
+ debugfs_create_file(name, 0444, data->debugfs, client,
+ &ucd9000_debugfs_show_mfr_status_fops);
+
+ return 0;
+}
+#else
+static int ucd9000_init_debugfs(struct i2c_client *client,
+ const struct i2c_device_id *mid,
+ struct ucd9000_data *data)
+{
+ return 0;
+}
+#endif /* CONFIG_DEBUG_FS */
+
static int ucd9000_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -263,7 +600,18 @@ static int ucd9000_probe(struct i2c_client *client,
| PMBUS_HAVE_FAN34 | PMBUS_HAVE_STATUS_FAN34;
}
- return pmbus_do_probe(client, mid, info);
+ ucd9000_probe_gpio(client, mid, data);
+
+ ret = pmbus_do_probe(client, mid, info);
+ if (ret)
+ return ret;
+
+ ret = ucd9000_init_debugfs(client, mid, data);
+ if (ret)
+ dev_warn(&client->dev, "Failed to register debugfs: %d\n",
+ ret);
+
+ return 0;
}
/* This is the driver that will be inserted */
diff --git a/drivers/hwmon/scmi-hwmon.c b/drivers/hwmon/scmi-hwmon.c
new file mode 100644
index 000000000000..91976b6ca300
--- /dev/null
+++ b/drivers/hwmon/scmi-hwmon.c
@@ -0,0 +1,228 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Management Interface(SCMI) based hwmon sensor driver
+ *
+ * Copyright (C) 2018 ARM Ltd.
+ * Sudeep Holla <sudeep.holla@arm.com>
+ */
+
+#include <linux/hwmon.h>
+#include <linux/module.h>
+#include <linux/scmi_protocol.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/thermal.h>
+
+struct scmi_sensors {
+ const struct scmi_handle *handle;
+ const struct scmi_sensor_info **info[hwmon_max];
+};
+
+static int scmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ int ret;
+ u64 value;
+ const struct scmi_sensor_info *sensor;
+ struct scmi_sensors *scmi_sensors = dev_get_drvdata(dev);
+ const struct scmi_handle *h = scmi_sensors->handle;
+
+ sensor = *(scmi_sensors->info[type] + channel);
+ ret = h->sensor_ops->reading_get(h, sensor->id, false, &value);
+ if (!ret)
+ *val = value;
+
+ return ret;
+}
+
+static int
+scmi_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, const char **str)
+{
+ const struct scmi_sensor_info *sensor;
+ struct scmi_sensors *scmi_sensors = dev_get_drvdata(dev);
+
+ sensor = *(scmi_sensors->info[type] + channel);
+ *str = sensor->name;
+
+ return 0;
+}
+
+static umode_t
+scmi_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ const struct scmi_sensor_info *sensor;
+ const struct scmi_sensors *scmi_sensors = drvdata;
+
+ sensor = *(scmi_sensors->info[type] + channel);
+ if (sensor && sensor->name)
+ return S_IRUGO;
+
+ return 0;
+}
+
+static const struct hwmon_ops scmi_hwmon_ops = {
+ .is_visible = scmi_hwmon_is_visible,
+ .read = scmi_hwmon_read,
+ .read_string = scmi_hwmon_read_string,
+};
+
+static struct hwmon_chip_info scmi_chip_info = {
+ .ops = &scmi_hwmon_ops,
+ .info = NULL,
+};
+
+static int scmi_hwmon_add_chan_info(struct hwmon_channel_info *scmi_hwmon_chan,
+ struct device *dev, int num,
+ enum hwmon_sensor_types type, u32 config)
+{
+ int i;
+ u32 *cfg = devm_kcalloc(dev, num + 1, sizeof(*cfg), GFP_KERNEL);
+
+ if (!cfg)
+ return -ENOMEM;
+
+ scmi_hwmon_chan->type = type;
+ scmi_hwmon_chan->config = cfg;
+ for (i = 0; i < num; i++, cfg++)
+ *cfg = config;
+
+ return 0;
+}
+
+static enum hwmon_sensor_types scmi_types[] = {
+ [TEMPERATURE_C] = hwmon_temp,
+ [VOLTAGE] = hwmon_in,
+ [CURRENT] = hwmon_curr,
+ [POWER] = hwmon_power,
+ [ENERGY] = hwmon_energy,
+};
+
+static u32 hwmon_attributes[] = {
+ [hwmon_chip] = HWMON_C_REGISTER_TZ,
+ [hwmon_temp] = HWMON_T_INPUT | HWMON_T_LABEL,
+ [hwmon_in] = HWMON_I_INPUT | HWMON_I_LABEL,
+ [hwmon_curr] = HWMON_C_INPUT | HWMON_C_LABEL,
+ [hwmon_power] = HWMON_P_INPUT | HWMON_P_LABEL,
+ [hwmon_energy] = HWMON_E_INPUT | HWMON_E_LABEL,
+};
+
+static int scmi_hwmon_probe(struct scmi_device *sdev)
+{
+ int i, idx;
+ u16 nr_sensors;
+ enum hwmon_sensor_types type;
+ struct scmi_sensors *scmi_sensors;
+ const struct scmi_sensor_info *sensor;
+ int nr_count[hwmon_max] = {0}, nr_types = 0;
+ const struct hwmon_chip_info *chip_info;
+ struct device *hwdev, *dev = &sdev->dev;
+ struct hwmon_channel_info *scmi_hwmon_chan;
+ const struct hwmon_channel_info **ptr_scmi_ci;
+ const struct scmi_handle *handle = sdev->handle;
+
+ if (!handle || !handle->sensor_ops)
+ return -ENODEV;
+
+ nr_sensors = handle->sensor_ops->count_get(handle);
+ if (!nr_sensors)
+ return -EIO;
+
+ scmi_sensors = devm_kzalloc(dev, sizeof(*scmi_sensors), GFP_KERNEL);
+ if (!scmi_sensors)
+ return -ENOMEM;
+
+ scmi_sensors->handle = handle;
+
+ for (i = 0; i < nr_sensors; i++) {
+ sensor = handle->sensor_ops->info_get(handle, i);
+ if (!sensor)
+ return -EINVAL;
+
+ switch (sensor->type) {
+ case TEMPERATURE_C:
+ case VOLTAGE:
+ case CURRENT:
+ case POWER:
+ case ENERGY:
+ type = scmi_types[sensor->type];
+ if (!nr_count[type])
+ nr_types++;
+ nr_count[type]++;
+ break;
+ }
+ }
+
+ if (nr_count[hwmon_temp])
+ nr_count[hwmon_chip]++, nr_types++;
+
+ scmi_hwmon_chan = devm_kcalloc(dev, nr_types, sizeof(*scmi_hwmon_chan),
+ GFP_KERNEL);
+ if (!scmi_hwmon_chan)
+ return -ENOMEM;
+
+ ptr_scmi_ci = devm_kcalloc(dev, nr_types + 1, sizeof(*ptr_scmi_ci),
+ GFP_KERNEL);
+ if (!ptr_scmi_ci)
+ return -ENOMEM;
+
+ scmi_chip_info.info = ptr_scmi_ci;
+ chip_info = &scmi_chip_info;
+
+ for (type = 0; type < hwmon_max; type++) {
+ if (!nr_count[type])
+ continue;
+
+ scmi_hwmon_add_chan_info(scmi_hwmon_chan, dev, nr_count[type],
+ type, hwmon_attributes[type]);
+ *ptr_scmi_ci++ = scmi_hwmon_chan++;
+
+ scmi_sensors->info[type] =
+ devm_kcalloc(dev, nr_count[type],
+ sizeof(*scmi_sensors->info), GFP_KERNEL);
+ if (!scmi_sensors->info[type])
+ return -ENOMEM;
+ }
+
+ for (i = nr_sensors - 1; i >= 0 ; i--) {
+ sensor = handle->sensor_ops->info_get(handle, i);
+ if (!sensor)
+ continue;
+
+ switch (sensor->type) {
+ case TEMPERATURE_C:
+ case VOLTAGE:
+ case CURRENT:
+ case POWER:
+ case ENERGY:
+ type = scmi_types[sensor->type];
+ idx = --nr_count[type];
+ *(scmi_sensors->info[type] + idx) = sensor;
+ break;
+ }
+ }
+
+ hwdev = devm_hwmon_device_register_with_info(dev, "scmi_sensors",
+ scmi_sensors, chip_info,
+ NULL);
+
+ return PTR_ERR_OR_ZERO(hwdev);
+}
+
+static const struct scmi_device_id scmi_id_table[] = {
+ { SCMI_PROTOCOL_SENSOR },
+ { },
+};
+MODULE_DEVICE_TABLE(scmi, scmi_id_table);
+
+static struct scmi_driver scmi_hwmon_drv = {
+ .name = "scmi-hwmon",
+ .probe = scmi_hwmon_probe,
+ .id_table = scmi_id_table,
+};
+module_scmi_driver(scmi_hwmon_drv);
+
+MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
+MODULE_DESCRIPTION("ARM SCMI HWMON interface driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/sht21.c b/drivers/hwmon/sht21.c
index 190e7b39ce32..2c7ba70921f5 100644
--- a/drivers/hwmon/sht21.c
+++ b/drivers/hwmon/sht21.c
@@ -16,8 +16,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA
*
- * Data sheet available (5/2010) at
- * http://www.sensirion.com/en/pdf/product_information/Datasheet-humidity-sensor-SHT21.pdf
+ * Data sheet available at http://www.sensirion.com/file/datasheet_sht21
*/
#include <linux/module.h>
diff --git a/drivers/hwmon/via-cputemp.c b/drivers/hwmon/via-cputemp.c
index 07a0cb0a1f28..0e81f287d305 100644
--- a/drivers/hwmon/via-cputemp.c
+++ b/drivers/hwmon/via-cputemp.c
@@ -136,20 +136,24 @@ static int via_cputemp_probe(struct platform_device *pdev)
data->id = pdev->id;
data->name = "via_cputemp";
- switch (c->x86_model) {
- case 0xA:
- /* C7 A */
- case 0xD:
- /* C7 D */
- data->msr_temp = 0x1169;
- data->msr_vid = 0x198;
- break;
- case 0xF:
- /* Nano */
+ if (c->x86 == 7) {
data->msr_temp = 0x1423;
- break;
- default:
- return -ENODEV;
+ } else {
+ switch (c->x86_model) {
+ case 0xA:
+ /* C7 A */
+ case 0xD:
+ /* C7 D */
+ data->msr_temp = 0x1169;
+ data->msr_vid = 0x198;
+ break;
+ case 0xF:
+ /* Nano */
+ data->msr_temp = 0x1423;
+ break;
+ default:
+ return -ENODEV;
+ }
}
/* test if we can access the TEMPERATURE MSR */
@@ -283,6 +287,7 @@ static const struct x86_cpu_id __initconst cputemp_ids[] = {
{ X86_VENDOR_CENTAUR, 6, 0xa, }, /* C7 A */
{ X86_VENDOR_CENTAUR, 6, 0xd, }, /* C7 D */
{ X86_VENDOR_CENTAUR, 6, 0xf, }, /* Nano */
+ { X86_VENDOR_CENTAUR, 7, X86_MODEL_ANY, },
{}
};
MODULE_DEVICE_TABLE(x86cpu, cputemp_ids);
diff --git a/drivers/hwtracing/Kconfig b/drivers/hwtracing/Kconfig
new file mode 100644
index 000000000000..f68e025c5131
--- /dev/null
+++ b/drivers/hwtracing/Kconfig
@@ -0,0 +1,7 @@
+menu "HW tracing support"
+
+source "drivers/hwtracing/stm/Kconfig"
+
+source "drivers/hwtracing/intel_th/Kconfig"
+
+endmenu
diff --git a/drivers/hwtracing/coresight/coresight-cpu-debug.c b/drivers/hwtracing/coresight/coresight-cpu-debug.c
index 6ea62c62ff27..9cdb3fbc8c1f 100644
--- a/drivers/hwtracing/coresight/coresight-cpu-debug.c
+++ b/drivers/hwtracing/coresight/coresight-cpu-debug.c
@@ -315,7 +315,7 @@ static void debug_dump_regs(struct debug_drvdata *drvdata)
}
pc = debug_adjust_pc(drvdata);
- dev_emerg(dev, " EDPCSR: [<%p>] %pS\n", (void *)pc, (void *)pc);
+ dev_emerg(dev, " EDPCSR: [<%px>] %pS\n", (void *)pc, (void *)pc);
if (drvdata->edcidsr_present)
dev_emerg(dev, " EDCIDSR: %08x\n", drvdata->edcidsr);
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c
index 8a0ad77574e7..4e5ed6597f2f 100644
--- a/drivers/hwtracing/coresight/coresight-etm-perf.c
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.c
@@ -393,35 +393,26 @@ static int etm_addr_filters_validate(struct list_head *filters)
if (++index > ETM_ADDR_CMP_MAX)
return -EOPNOTSUPP;
+ /* filter::size==0 means single address trigger */
+ if (filter->size) {
+ /*
+ * The existing code relies on START/STOP filters
+ * being address filters.
+ */
+ if (filter->action == PERF_ADDR_FILTER_ACTION_START ||
+ filter->action == PERF_ADDR_FILTER_ACTION_STOP)
+ return -EOPNOTSUPP;
+
+ range = true;
+ } else
+ address = true;
+
/*
- * As taken from the struct perf_addr_filter documentation:
- * @range: 1: range, 0: address
- *
* At this time we don't allow range and start/stop filtering
* to cohabitate, they have to be mutually exclusive.
*/
- if ((filter->range == 1) && address)
+ if (range && address)
return -EOPNOTSUPP;
-
- if ((filter->range == 0) && range)
- return -EOPNOTSUPP;
-
- /*
- * For range filtering, the second address in the address
- * range comparator needs to be higher than the first.
- * Invalid otherwise.
- */
- if (filter->range && filter->size == 0)
- return -EINVAL;
-
- /*
- * Everything checks out with this filter, record what we've
- * received before moving on to the next one.
- */
- if (filter->range)
- range = true;
- else
- address = true;
}
return 0;
@@ -441,18 +432,20 @@ static void etm_addr_filters_sync(struct perf_event *event)
stop = start + filter->size;
etm_filter = &filters->etm_filter[i];
- if (filter->range == 1) {
+ switch (filter->action) {
+ case PERF_ADDR_FILTER_ACTION_FILTER:
etm_filter->start_addr = start;
etm_filter->stop_addr = stop;
etm_filter->type = ETM_ADDR_TYPE_RANGE;
- } else {
- if (filter->filter == 1) {
- etm_filter->start_addr = start;
- etm_filter->type = ETM_ADDR_TYPE_START;
- } else {
- etm_filter->stop_addr = stop;
- etm_filter->type = ETM_ADDR_TYPE_STOP;
- }
+ break;
+ case PERF_ADDR_FILTER_ACTION_START:
+ etm_filter->start_addr = start;
+ etm_filter->type = ETM_ADDR_TYPE_START;
+ break;
+ case PERF_ADDR_FILTER_ACTION_STOP:
+ etm_filter->stop_addr = stop;
+ etm_filter->type = ETM_ADDR_TYPE_STOP;
+ break;
}
i++;
}
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c
index 4e6eab53e34e..d21961710713 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c
@@ -1780,7 +1780,7 @@ static ssize_t ctxid_masks_store(struct device *dev,
*/
for (j = 0; j < 8; j++) {
if (maskbyte & 1)
- config->ctxid_pid[i] &= ~(0xFF << (j * 8));
+ config->ctxid_pid[i] &= ~(0xFFUL << (j * 8));
maskbyte >>= 1;
}
/* Select the next ctxid comparator mask value */
@@ -1963,7 +1963,7 @@ static ssize_t vmid_masks_store(struct device *dev,
*/
for (j = 0; j < 8; j++) {
if (maskbyte & 1)
- config->vmid_val[i] &= ~(0xFF << (j * 8));
+ config->vmid_val[i] &= ~(0xFFUL << (j * 8));
maskbyte >>= 1;
}
/* Select the next vmid comparator mask value */
diff --git a/drivers/hwtracing/intel_th/Kconfig b/drivers/hwtracing/intel_th/Kconfig
index 1b412f8a56b5..ca0527d588e9 100644
--- a/drivers/hwtracing/intel_th/Kconfig
+++ b/drivers/hwtracing/intel_th/Kconfig
@@ -25,6 +25,18 @@ config INTEL_TH_PCI
Say Y here to enable PCI Intel TH support.
+config INTEL_TH_ACPI
+ tristate "Intel(R) Trace Hub ACPI controller"
+ depends on ACPI
+ help
+ Intel(R) Trace Hub may exist as an ACPI device. This option enables
+ support glue layer for ACPI-based Intel TH. This typically implies
+ 'host debugger' mode, that is, the trace configuration and capture
+ is handled by an external debug host and corresponding controls will
+ not be available on the target.
+
+ Say Y here to enable ACPI Intel TH support.
+
config INTEL_TH_GTH
tristate "Intel(R) Trace Hub Global Trace Hub"
help
diff --git a/drivers/hwtracing/intel_th/Makefile b/drivers/hwtracing/intel_th/Makefile
index 880c9b5e8566..d9252fa8d9ca 100644
--- a/drivers/hwtracing/intel_th/Makefile
+++ b/drivers/hwtracing/intel_th/Makefile
@@ -6,6 +6,9 @@ intel_th-$(CONFIG_INTEL_TH_DEBUG) += debug.o
obj-$(CONFIG_INTEL_TH_PCI) += intel_th_pci.o
intel_th_pci-y := pci.o
+obj-$(CONFIG_INTEL_TH_ACPI) += intel_th_acpi.o
+intel_th_acpi-y := acpi.o
+
obj-$(CONFIG_INTEL_TH_GTH) += intel_th_gth.o
intel_th_gth-y := gth.o
diff --git a/drivers/hwtracing/intel_th/acpi.c b/drivers/hwtracing/intel_th/acpi.c
new file mode 100644
index 000000000000..87bc3744755f
--- /dev/null
+++ b/drivers/hwtracing/intel_th/acpi.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel(R) Trace Hub ACPI driver
+ *
+ * Copyright (C) 2017 Intel Corporation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/platform_device.h>
+#include <linux/acpi.h>
+
+#include "intel_th.h"
+
+#define DRIVER_NAME "intel_th_acpi"
+
+static const struct intel_th_drvdata intel_th_acpi_pch = {
+ .host_mode_only = 1,
+};
+
+static const struct intel_th_drvdata intel_th_acpi_uncore = {
+ .host_mode_only = 1,
+};
+
+static const struct acpi_device_id intel_th_acpi_ids[] = {
+ { "INTC1000", (kernel_ulong_t)&intel_th_acpi_uncore },
+ { "INTC1001", (kernel_ulong_t)&intel_th_acpi_pch },
+ { "", 0 },
+};
+
+MODULE_DEVICE_TABLE(acpi, intel_th_acpi_ids);
+
+static int intel_th_acpi_probe(struct platform_device *pdev)
+{
+ struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
+ const struct acpi_device_id *id;
+ struct intel_th *th;
+
+ id = acpi_match_device(intel_th_acpi_ids, &pdev->dev);
+ if (!id)
+ return -ENODEV;
+
+ th = intel_th_alloc(&pdev->dev, (void *)id->driver_data,
+ pdev->resource, pdev->num_resources, -1);
+ if (IS_ERR(th))
+ return PTR_ERR(th);
+
+ adev->driver_data = th;
+
+ return 0;
+}
+
+static int intel_th_acpi_remove(struct platform_device *pdev)
+{
+ struct intel_th *th = platform_get_drvdata(pdev);
+
+ intel_th_free(th);
+
+ return 0;
+}
+
+static struct platform_driver intel_th_acpi_driver = {
+ .probe = intel_th_acpi_probe,
+ .remove = intel_th_acpi_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .acpi_match_table = intel_th_acpi_ids,
+ },
+};
+
+module_platform_driver(intel_th_acpi_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel(R) Trace Hub ACPI controller driver");
+MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@intel.com>");
diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c
index 1a023e30488c..da962aa2cef5 100644
--- a/drivers/hwtracing/intel_th/core.c
+++ b/drivers/hwtracing/intel_th/core.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Intel(R) Trace Hub driver core
*
* Copyright (C) 2014-2015 Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -638,7 +630,8 @@ intel_th_subdevice_alloc(struct intel_th *th,
thdev->output.port = -1;
thdev->output.scratchpad = subdev->scrpd;
} else if (subdev->type == INTEL_TH_SWITCH) {
- thdev->host_mode = host_mode;
+ thdev->host_mode =
+ INTEL_TH_CAP(th, host_mode_only) ? true : host_mode;
th->hub = thdev;
}
@@ -737,7 +730,8 @@ static int intel_th_populate(struct intel_th *th)
struct intel_th_device *thdev;
/* only allow SOURCE and SWITCH devices in host mode */
- if (host_mode && subdev->type == INTEL_TH_OUTPUT)
+ if ((INTEL_TH_CAP(th, host_mode_only) || host_mode) &&
+ subdev->type == INTEL_TH_OUTPUT)
continue;
/*
@@ -813,7 +807,14 @@ intel_th_alloc(struct device *dev, struct intel_th_drvdata *drvdata,
struct resource *devres, unsigned int ndevres, int irq)
{
struct intel_th *th;
- int err;
+ int err, r;
+
+ if (irq == -1)
+ for (r = 0; r < ndevres; r++)
+ if (devres[r].flags & IORESOURCE_IRQ) {
+ irq = devres[r].start;
+ break;
+ }
th = kzalloc(sizeof(*th), GFP_KERNEL);
if (!th)
@@ -935,9 +936,13 @@ EXPORT_SYMBOL_GPL(intel_th_trace_disable);
int intel_th_set_output(struct intel_th_device *thdev,
unsigned int master)
{
- struct intel_th_device *hub = to_intel_th_device(thdev->dev.parent);
+ struct intel_th_device *hub = to_intel_th_hub(thdev);
struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver);
+ /* In host mode, this is up to the external debugger, do nothing. */
+ if (hub->host_mode)
+ return 0;
+
if (!hubdrv->set_output)
return -ENOTSUPP;
diff --git a/drivers/hwtracing/intel_th/debug.c b/drivers/hwtracing/intel_th/debug.c
index 788a1f0a97ad..ff79063118a0 100644
--- a/drivers/hwtracing/intel_th/debug.c
+++ b/drivers/hwtracing/intel_th/debug.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Intel(R) Trace Hub driver debugging
*
* Copyright (C) 2014-2015 Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
*/
#include <linux/types.h>
diff --git a/drivers/hwtracing/intel_th/debug.h b/drivers/hwtracing/intel_th/debug.h
index 88311bad3ba4..78bd7e4bf9ce 100644
--- a/drivers/hwtracing/intel_th/debug.h
+++ b/drivers/hwtracing/intel_th/debug.h
@@ -1,16 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Intel(R) Trace Hub driver debugging
*
* Copyright (C) 2014-2015 Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
*/
#ifndef __INTEL_TH_DEBUG_H__
diff --git a/drivers/hwtracing/intel_th/gth.c b/drivers/hwtracing/intel_th/gth.c
index 018678ec3c13..8426b7970c14 100644
--- a/drivers/hwtracing/intel_th/gth.c
+++ b/drivers/hwtracing/intel_th/gth.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Intel(R) Trace Hub Global Trace Hub
*
* Copyright (C) 2014-2015 Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
diff --git a/drivers/hwtracing/intel_th/gth.h b/drivers/hwtracing/intel_th/gth.h
index f3d234251a12..6f2b0b930875 100644
--- a/drivers/hwtracing/intel_th/gth.h
+++ b/drivers/hwtracing/intel_th/gth.h
@@ -1,16 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Intel(R) Trace Hub Global Trace Hub (GTH) data structures
*
* Copyright (C) 2014-2015 Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
*/
#ifndef __INTEL_TH_GTH_H__
diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h
index 99ad563fc40d..780206dc9012 100644
--- a/drivers/hwtracing/intel_th/intel_th.h
+++ b/drivers/hwtracing/intel_th/intel_th.h
@@ -1,16 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Intel(R) Trace Hub data structures
*
* Copyright (C) 2014-2015 Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
*/
#ifndef __INTEL_TH_H__
@@ -50,9 +42,11 @@ struct intel_th_output {
/**
* struct intel_th_drvdata - describes hardware capabilities and quirks
* @tscu_enable: device needs SW to enable time stamping unit
+ * @host_mode_only: device can only operate in 'host debugger' mode
*/
struct intel_th_drvdata {
- unsigned int tscu_enable : 1;
+ unsigned int tscu_enable : 1,
+ host_mode_only : 1;
};
#define INTEL_TH_CAP(_th, _cap) ((_th)->drvdata ? (_th)->drvdata->_cap : 0)
diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c
index dfb57eaa9f22..ede388309376 100644
--- a/drivers/hwtracing/intel_th/msu.c
+++ b/drivers/hwtracing/intel_th/msu.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Intel(R) Trace Hub Memory Storage Unit
*
* Copyright (C) 2014-2015 Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
diff --git a/drivers/hwtracing/intel_th/msu.h b/drivers/hwtracing/intel_th/msu.h
index 9b710e4aa98a..9cc8aced6116 100644
--- a/drivers/hwtracing/intel_th/msu.h
+++ b/drivers/hwtracing/intel_th/msu.h
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Intel(R) Trace Hub Memory Storage Unit (MSU) data structures
*
* Copyright (C) 2014-2015 Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
*/
#ifndef __INTEL_TH_MSU_H__
diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c
index c2a2ce8ee541..c2e55e5d97f6 100644
--- a/drivers/hwtracing/intel_th/pci.c
+++ b/drivers/hwtracing/intel_th/pci.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Intel(R) Trace Hub pci driver
*
* Copyright (C) 2014-2015 Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
diff --git a/drivers/hwtracing/intel_th/pti.c b/drivers/hwtracing/intel_th/pti.c
index e96a1fcb57b2..56694339cb06 100644
--- a/drivers/hwtracing/intel_th/pti.c
+++ b/drivers/hwtracing/intel_th/pti.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Intel(R) Trace Hub PTI output driver
*
* Copyright (C) 2014-2016 Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
diff --git a/drivers/hwtracing/intel_th/pti.h b/drivers/hwtracing/intel_th/pti.h
index 30827be67b4c..e9381babc84c 100644
--- a/drivers/hwtracing/intel_th/pti.h
+++ b/drivers/hwtracing/intel_th/pti.h
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Intel(R) Trace Hub PTI output data structures
*
* Copyright (C) 2014-2015 Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
*/
#ifndef __INTEL_TH_STH_H__
diff --git a/drivers/hwtracing/intel_th/sth.c b/drivers/hwtracing/intel_th/sth.c
index b03444624648..4b7ae47789d2 100644
--- a/drivers/hwtracing/intel_th/sth.c
+++ b/drivers/hwtracing/intel_th/sth.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Intel(R) Trace Hub Software Trace Hub support
*
* Copyright (C) 2014-2015 Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
diff --git a/drivers/hwtracing/intel_th/sth.h b/drivers/hwtracing/intel_th/sth.h
index f1390cd4f2ed..f97fc0c51739 100644
--- a/drivers/hwtracing/intel_th/sth.h
+++ b/drivers/hwtracing/intel_th/sth.h
@@ -1,16 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Intel(R) Trace Hub Software Trace Hub (STH) data structures
*
* Copyright (C) 2014-2015 Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
*/
#ifndef __INTEL_TH_STH_H__
diff --git a/drivers/hwtracing/stm/console.c b/drivers/hwtracing/stm/console.c
index c9d9a8d2ff52..a00f65e21747 100644
--- a/drivers/hwtracing/stm/console.c
+++ b/drivers/hwtracing/stm/console.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Simple kernel console driver for STM devices
* Copyright (c) 2014, Intel Corporation.
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- *
* STM console will send kernel messages over STM devices to a trace host.
*/
diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c
index f129869e05a9..05386b76465e 100644
--- a/drivers/hwtracing/stm/core.c
+++ b/drivers/hwtracing/stm/core.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* System Trace Module (STM) infrastructure
* Copyright (c) 2014, Intel Corporation.
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- *
* STM class implements generic infrastructure for System Trace Module devices
* as defined in MIPI STPv2 specification.
*/
diff --git a/drivers/hwtracing/stm/dummy_stm.c b/drivers/hwtracing/stm/dummy_stm.c
index c5f94ca31c4d..38528ffdc0b3 100644
--- a/drivers/hwtracing/stm/dummy_stm.c
+++ b/drivers/hwtracing/stm/dummy_stm.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* A dummy STM device for stm/stm_source class testing.
* Copyright (c) 2014, Intel Corporation.
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- *
* STM class implements generic infrastructure for System Trace Module devices
* as defined in MIPI STPv2 specification.
*/
@@ -20,6 +12,7 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/stm.h>
+#include <uapi/linux/stm.h>
static ssize_t notrace
dummy_stm_packet(struct stm_data *stm_data, unsigned int master,
@@ -52,6 +45,18 @@ static unsigned int fail_mode;
module_param(fail_mode, int, 0600);
+static unsigned int master_min;
+
+module_param(master_min, int, 0400);
+
+static unsigned int master_max = STP_MASTER_MAX;
+
+module_param(master_max, int, 0400);
+
+static unsigned int nr_channels = STP_CHANNEL_MAX;
+
+module_param(nr_channels, int, 0400);
+
static int dummy_stm_link(struct stm_data *data, unsigned int master,
unsigned int channel)
{
@@ -68,14 +73,19 @@ static int dummy_stm_init(void)
if (nr_dummies < 0 || nr_dummies > DUMMY_STM_MAX)
return -EINVAL;
+ if (master_min > master_max ||
+ master_max > STP_MASTER_MAX ||
+ nr_channels > STP_CHANNEL_MAX)
+ return -EINVAL;
+
for (i = 0; i < nr_dummies; i++) {
dummy_stm[i].name = kasprintf(GFP_KERNEL, "dummy_stm.%d", i);
if (!dummy_stm[i].name)
goto fail_unregister;
- dummy_stm[i].sw_start = 0x0000;
- dummy_stm[i].sw_end = 0xffff;
- dummy_stm[i].sw_nchannels = 0xffff;
+ dummy_stm[i].sw_start = master_min;
+ dummy_stm[i].sw_end = master_max;
+ dummy_stm[i].sw_nchannels = nr_channels;
dummy_stm[i].packet = dummy_stm_packet;
dummy_stm[i].link = dummy_stm_link;
diff --git a/drivers/hwtracing/stm/heartbeat.c b/drivers/hwtracing/stm/heartbeat.c
index 3da7b673aab2..7db42395e131 100644
--- a/drivers/hwtracing/stm/heartbeat.c
+++ b/drivers/hwtracing/stm/heartbeat.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Simple heartbeat STM source driver
* Copyright (c) 2016, Intel Corporation.
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- *
* Heartbeat STM source will send repetitive messages over STM devices to a
* trace host.
*/
diff --git a/drivers/hwtracing/stm/policy.c b/drivers/hwtracing/stm/policy.c
index 33e9a1b6ea7c..3fd07e275b34 100644
--- a/drivers/hwtracing/stm/policy.c
+++ b/drivers/hwtracing/stm/policy.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* System Trace Module (STM) master/channel allocation policy management
* Copyright (c) 2014, Intel Corporation.
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- *
* A master/channel allocation policy allows mapping string identifiers to
* master and channel ranges, where allocation can be done.
*/
diff --git a/drivers/hwtracing/stm/stm.h b/drivers/hwtracing/stm/stm.h
index 4e8c6926260f..923571adc6f4 100644
--- a/drivers/hwtracing/stm/stm.h
+++ b/drivers/hwtracing/stm/stm.h
@@ -1,16 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* System Trace Module (STM) infrastructure
* Copyright (c) 2014, Intel Corporation.
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- *
* STM class implements generic infrastructure for System Trace Module devices
* as defined in MIPI STPv2 specification.
*/
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index e2954fb86d65..8d21b9825d71 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -445,24 +445,6 @@ config I2C_BRCMSTB
If you do not need I2C interface, say N.
-config I2C_BLACKFIN_TWI
- tristate "Blackfin TWI I2C support"
- depends on BLACKFIN
- depends on !BF561 && !BF531 && !BF532 && !BF533
- help
- This is the I2C bus driver for Blackfin on-chip TWI interface.
-
- This driver can also be built as a module. If so, the module
- will be called i2c-bfin-twi.
-
-config I2C_BLACKFIN_TWI_CLK_KHZ
- int "Blackfin TWI I2C clock (kHz)"
- depends on I2C_BLACKFIN_TWI
- range 21 400
- default 50
- help
- The unit of the TWI clock is kHz.
-
config I2C_CADENCE
tristate "Cadence I2C Controller"
depends on ARCH_ZYNQ || ARM64 || XTENSA
@@ -637,7 +619,7 @@ config I2C_IBM_IIC
config I2C_IMG
tristate "Imagination Technologies I2C SCB Controller"
- depends on MIPS || METAG || COMPILE_TEST
+ depends on MIPS || COMPILE_TEST
help
Say Y here if you want to use the IMG I2C SCB controller,
available on the TZ1090 and other IMG SoCs.
@@ -725,7 +707,6 @@ config I2C_MPC
config I2C_MT65XX
tristate "MediaTek I2C adapter"
depends on ARCH_MEDIATEK || COMPILE_TEST
- depends on HAS_DMA
help
This selects the MediaTek(R) Integrated Inter Circuit bus driver
for MT65xx and MT81xx.
@@ -903,7 +884,6 @@ config I2C_SH7760
config I2C_SH_MOBILE
tristate "SuperH Mobile I2C Controller"
- depends on HAS_DMA
depends on ARCH_SHMOBILE || ARCH_RENESAS || COMPILE_TEST
help
If you say yes to this option, support will be included for the
@@ -997,6 +977,16 @@ config I2C_SUN6I_P2WI
This interface is used to connect to specific PMIC devices (like the
AXP221).
+config I2C_SYNQUACER
+ tristate "Socionext SynQuacer I2C controller"
+ depends on ARCH_SYNQUACER || COMPILE_TEST
+ help
+ Say Y here to include support for the I2C controller used in some
+ Fujitsu and Socionext SoCs.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-synquacer.
+
config I2C_TEGRA
tristate "NVIDIA Tegra internal I2C controller"
depends on ARCH_TEGRA
@@ -1106,7 +1096,6 @@ config I2C_XLP9XX
config I2C_RCAR
tristate "Renesas R-Car I2C Controller"
- depends on HAS_DMA
depends on ARCH_RENESAS || COMPILE_TEST
select I2C_SLAVE
help
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 2ce8576540a2..189e34ba050f 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -38,7 +38,6 @@ obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o
obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o
obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o
obj-$(CONFIG_I2C_BCM_IPROC) += i2c-bcm-iproc.o
-obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o
obj-$(CONFIG_I2C_CADENCE) += i2c-cadence.o
obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o
obj-$(CONFIG_I2C_CPM) += i2c-cpm.o
@@ -98,6 +97,7 @@ obj-$(CONFIG_I2C_STM32F4) += i2c-stm32f4.o
obj-$(CONFIG_I2C_STM32F7) += i2c-stm32f7.o
obj-$(CONFIG_I2C_STU300) += i2c-stu300.o
obj-$(CONFIG_I2C_SUN6I_P2WI) += i2c-sun6i-p2wi.o
+obj-$(CONFIG_I2C_SYNQUACER) += i2c-synquacer.o
obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o
obj-$(CONFIG_I2C_TEGRA_BPMP) += i2c-tegra-bpmp.o
obj-$(CONFIG_I2C_UNIPHIER) += i2c-uniphier.o
diff --git a/drivers/i2c/busses/i2c-bfin-twi.c b/drivers/i2c/busses/i2c-bfin-twi.c
deleted file mode 100644
index ff3343186a82..000000000000
--- a/drivers/i2c/busses/i2c-bfin-twi.c
+++ /dev/null
@@ -1,737 +0,0 @@
-/*
- * Blackfin On-Chip Two Wire Interface Driver
- *
- * Copyright 2005-2007 Analog Devices Inc.
- *
- * Enter bugs at http://blackfin.uclinux.org/
- *
- * Licensed under the GPL-2 or later.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/i2c.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-#include <linux/mm.h>
-#include <linux/timer.h>
-#include <linux/spinlock.h>
-#include <linux/completion.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/delay.h>
-
-#include <asm/irq.h>
-#include <asm/portmux.h>
-#include <asm/bfin_twi.h>
-
-/* SMBus mode*/
-#define TWI_I2C_MODE_STANDARD 1
-#define TWI_I2C_MODE_STANDARDSUB 2
-#define TWI_I2C_MODE_COMBINED 3
-#define TWI_I2C_MODE_REPEAT 4
-
-static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface,
- unsigned short twi_int_status)
-{
- unsigned short mast_stat = read_MASTER_STAT(iface);
-
- if (twi_int_status & XMTSERV) {
- if (iface->writeNum <= 0) {
- /* start receive immediately after complete sending in
- * combine mode.
- */
- if (iface->cur_mode == TWI_I2C_MODE_COMBINED)
- write_MASTER_CTL(iface,
- read_MASTER_CTL(iface) | MDIR);
- else if (iface->manual_stop)
- write_MASTER_CTL(iface,
- read_MASTER_CTL(iface) | STOP);
- else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
- iface->cur_msg + 1 < iface->msg_num) {
- if (iface->pmsg[iface->cur_msg + 1].flags &
- I2C_M_RD)
- write_MASTER_CTL(iface,
- read_MASTER_CTL(iface) |
- MDIR);
- else
- write_MASTER_CTL(iface,
- read_MASTER_CTL(iface) &
- ~MDIR);
- }
- }
- /* Transmit next data */
- while (iface->writeNum > 0 &&
- (read_FIFO_STAT(iface) & XMTSTAT) != XMT_FULL) {
- write_XMT_DATA8(iface, *(iface->transPtr++));
- iface->writeNum--;
- }
- }
- if (twi_int_status & RCVSERV) {
- while (iface->readNum > 0 &&
- (read_FIFO_STAT(iface) & RCVSTAT)) {
- /* Receive next data */
- *(iface->transPtr) = read_RCV_DATA8(iface);
- if (iface->cur_mode == TWI_I2C_MODE_COMBINED) {
- /* Change combine mode into sub mode after
- * read first data.
- */
- iface->cur_mode = TWI_I2C_MODE_STANDARDSUB;
- /* Get read number from first byte in block
- * combine mode.
- */
- if (iface->readNum == 1 && iface->manual_stop)
- iface->readNum = *iface->transPtr + 1;
- }
- iface->transPtr++;
- iface->readNum--;
- }
-
- if (iface->readNum == 0) {
- if (iface->manual_stop) {
- /* Temporary workaround to avoid possible bus stall -
- * Flush FIFO before issuing the STOP condition
- */
- read_RCV_DATA16(iface);
- write_MASTER_CTL(iface,
- read_MASTER_CTL(iface) | STOP);
- } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
- iface->cur_msg + 1 < iface->msg_num) {
- if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD)
- write_MASTER_CTL(iface,
- read_MASTER_CTL(iface) | MDIR);
- else
- write_MASTER_CTL(iface,
- read_MASTER_CTL(iface) & ~MDIR);
- }
- }
- }
- if (twi_int_status & MERR) {
- write_INT_MASK(iface, 0);
- write_MASTER_STAT(iface, 0x3e);
- write_MASTER_CTL(iface, 0);
- iface->result = -EIO;
-
- if (mast_stat & LOSTARB)
- dev_dbg(&iface->adap.dev, "Lost Arbitration\n");
- if (mast_stat & ANAK)
- dev_dbg(&iface->adap.dev, "Address Not Acknowledged\n");
- if (mast_stat & DNAK)
- dev_dbg(&iface->adap.dev, "Data Not Acknowledged\n");
- if (mast_stat & BUFRDERR)
- dev_dbg(&iface->adap.dev, "Buffer Read Error\n");
- if (mast_stat & BUFWRERR)
- dev_dbg(&iface->adap.dev, "Buffer Write Error\n");
-
- /* Faulty slave devices, may drive SDA low after a transfer
- * finishes. To release the bus this code generates up to 9
- * extra clocks until SDA is released.
- */
-
- if (read_MASTER_STAT(iface) & SDASEN) {
- int cnt = 9;
- do {
- write_MASTER_CTL(iface, SCLOVR);
- udelay(6);
- write_MASTER_CTL(iface, 0);
- udelay(6);
- } while ((read_MASTER_STAT(iface) & SDASEN) && cnt--);
-
- write_MASTER_CTL(iface, SDAOVR | SCLOVR);
- udelay(6);
- write_MASTER_CTL(iface, SDAOVR);
- udelay(6);
- write_MASTER_CTL(iface, 0);
- }
-
- /* If it is a quick transfer, only address without data,
- * not an err, return 1.
- */
- if (iface->cur_mode == TWI_I2C_MODE_STANDARD &&
- iface->transPtr == NULL &&
- (twi_int_status & MCOMP) && (mast_stat & DNAK))
- iface->result = 1;
-
- complete(&iface->complete);
- return;
- }
- if (twi_int_status & MCOMP) {
- if (twi_int_status & (XMTSERV | RCVSERV) &&
- (read_MASTER_CTL(iface) & MEN) == 0 &&
- (iface->cur_mode == TWI_I2C_MODE_REPEAT ||
- iface->cur_mode == TWI_I2C_MODE_COMBINED)) {
- iface->result = -1;
- write_INT_MASK(iface, 0);
- write_MASTER_CTL(iface, 0);
- } else if (iface->cur_mode == TWI_I2C_MODE_COMBINED) {
- if (iface->readNum == 0) {
- /* set the read number to 1 and ask for manual
- * stop in block combine mode
- */
- iface->readNum = 1;
- iface->manual_stop = 1;
- write_MASTER_CTL(iface,
- read_MASTER_CTL(iface) | (0xff << 6));
- } else {
- /* set the readd number in other
- * combine mode.
- */
- write_MASTER_CTL(iface,
- (read_MASTER_CTL(iface) &
- (~(0xff << 6))) |
- (iface->readNum << 6));
- }
- /* remove restart bit and enable master receive */
- write_MASTER_CTL(iface,
- read_MASTER_CTL(iface) & ~RSTART);
- } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
- iface->cur_msg + 1 < iface->msg_num) {
- iface->cur_msg++;
- iface->transPtr = iface->pmsg[iface->cur_msg].buf;
- iface->writeNum = iface->readNum =
- iface->pmsg[iface->cur_msg].len;
- /* Set Transmit device address */
- write_MASTER_ADDR(iface,
- iface->pmsg[iface->cur_msg].addr);
- if (iface->pmsg[iface->cur_msg].flags & I2C_M_RD)
- iface->read_write = I2C_SMBUS_READ;
- else {
- iface->read_write = I2C_SMBUS_WRITE;
- /* Transmit first data */
- if (iface->writeNum > 0) {
- write_XMT_DATA8(iface,
- *(iface->transPtr++));
- iface->writeNum--;
- }
- }
-
- if (iface->pmsg[iface->cur_msg].len <= 255) {
- write_MASTER_CTL(iface,
- (read_MASTER_CTL(iface) &
- (~(0xff << 6))) |
- (iface->pmsg[iface->cur_msg].len << 6));
- iface->manual_stop = 0;
- } else {
- write_MASTER_CTL(iface,
- (read_MASTER_CTL(iface) |
- (0xff << 6)));
- iface->manual_stop = 1;
- }
- /* remove restart bit before last message */
- if (iface->cur_msg + 1 == iface->msg_num)
- write_MASTER_CTL(iface,
- read_MASTER_CTL(iface) & ~RSTART);
- } else {
- iface->result = 1;
- write_INT_MASK(iface, 0);
- write_MASTER_CTL(iface, 0);
- }
- complete(&iface->complete);
- }
-}
-
-/* Interrupt handler */
-static irqreturn_t bfin_twi_interrupt_entry(int irq, void *dev_id)
-{
- struct bfin_twi_iface *iface = dev_id;
- unsigned long flags;
- unsigned short twi_int_status;
-
- spin_lock_irqsave(&iface->lock, flags);
- while (1) {
- twi_int_status = read_INT_STAT(iface);
- if (!twi_int_status)
- break;
- /* Clear interrupt status */
- write_INT_STAT(iface, twi_int_status);
- bfin_twi_handle_interrupt(iface, twi_int_status);
- }
- spin_unlock_irqrestore(&iface->lock, flags);
- return IRQ_HANDLED;
-}
-
-/*
- * One i2c master transfer
- */
-static int bfin_twi_do_master_xfer(struct i2c_adapter *adap,
- struct i2c_msg *msgs, int num)
-{
- struct bfin_twi_iface *iface = adap->algo_data;
- struct i2c_msg *pmsg;
- int rc = 0;
-
- if (!(read_CONTROL(iface) & TWI_ENA))
- return -ENXIO;
-
- if (read_MASTER_STAT(iface) & BUSBUSY)
- return -EAGAIN;
-
- iface->pmsg = msgs;
- iface->msg_num = num;
- iface->cur_msg = 0;
-
- pmsg = &msgs[0];
- if (pmsg->flags & I2C_M_TEN) {
- dev_err(&adap->dev, "10 bits addr not supported!\n");
- return -EINVAL;
- }
-
- if (iface->msg_num > 1)
- iface->cur_mode = TWI_I2C_MODE_REPEAT;
- iface->manual_stop = 0;
- iface->transPtr = pmsg->buf;
- iface->writeNum = iface->readNum = pmsg->len;
- iface->result = 0;
- init_completion(&(iface->complete));
- /* Set Transmit device address */
- write_MASTER_ADDR(iface, pmsg->addr);
-
- /* FIFO Initiation. Data in FIFO should be
- * discarded before start a new operation.
- */
- write_FIFO_CTL(iface, 0x3);
- write_FIFO_CTL(iface, 0);
-
- if (pmsg->flags & I2C_M_RD)
- iface->read_write = I2C_SMBUS_READ;
- else {
- iface->read_write = I2C_SMBUS_WRITE;
- /* Transmit first data */
- if (iface->writeNum > 0) {
- write_XMT_DATA8(iface, *(iface->transPtr++));
- iface->writeNum--;
- }
- }
-
- /* clear int stat */
- write_INT_STAT(iface, MERR | MCOMP | XMTSERV | RCVSERV);
-
- /* Interrupt mask . Enable XMT, RCV interrupt */
- write_INT_MASK(iface, MCOMP | MERR | RCVSERV | XMTSERV);
-
- if (pmsg->len <= 255)
- write_MASTER_CTL(iface, pmsg->len << 6);
- else {
- write_MASTER_CTL(iface, 0xff << 6);
- iface->manual_stop = 1;
- }
-
- /* Master enable */
- write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN |
- (iface->msg_num > 1 ? RSTART : 0) |
- ((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) |
- ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100) ? FAST : 0));
-
- while (!iface->result) {
- if (!wait_for_completion_timeout(&iface->complete,
- adap->timeout)) {
- iface->result = -1;
- dev_err(&adap->dev, "master transfer timeout\n");
- }
- }
-
- if (iface->result == 1)
- rc = iface->cur_msg + 1;
- else
- rc = iface->result;
-
- return rc;
-}
-
-/*
- * Generic i2c master transfer entrypoint
- */
-static int bfin_twi_master_xfer(struct i2c_adapter *adap,
- struct i2c_msg *msgs, int num)
-{
- return bfin_twi_do_master_xfer(adap, msgs, num);
-}
-
-/*
- * One I2C SMBus transfer
- */
-int bfin_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr,
- unsigned short flags, char read_write,
- u8 command, int size, union i2c_smbus_data *data)
-{
- struct bfin_twi_iface *iface = adap->algo_data;
- int rc = 0;
-
- if (!(read_CONTROL(iface) & TWI_ENA))
- return -ENXIO;
-
- if (read_MASTER_STAT(iface) & BUSBUSY)
- return -EAGAIN;
-
- iface->writeNum = 0;
- iface->readNum = 0;
-
- /* Prepare datas & select mode */
- switch (size) {
- case I2C_SMBUS_QUICK:
- iface->transPtr = NULL;
- iface->cur_mode = TWI_I2C_MODE_STANDARD;
- break;
- case I2C_SMBUS_BYTE:
- if (data == NULL)
- iface->transPtr = NULL;
- else {
- if (read_write == I2C_SMBUS_READ)
- iface->readNum = 1;
- else
- iface->writeNum = 1;
- iface->transPtr = &data->byte;
- }
- iface->cur_mode = TWI_I2C_MODE_STANDARD;
- break;
- case I2C_SMBUS_BYTE_DATA:
- if (read_write == I2C_SMBUS_READ) {
- iface->readNum = 1;
- iface->cur_mode = TWI_I2C_MODE_COMBINED;
- } else {
- iface->writeNum = 1;
- iface->cur_mode = TWI_I2C_MODE_STANDARDSUB;
- }
- iface->transPtr = &data->byte;
- break;
- case I2C_SMBUS_WORD_DATA:
- if (read_write == I2C_SMBUS_READ) {
- iface->readNum = 2;
- iface->cur_mode = TWI_I2C_MODE_COMBINED;
- } else {
- iface->writeNum = 2;
- iface->cur_mode = TWI_I2C_MODE_STANDARDSUB;
- }
- iface->transPtr = (u8 *)&data->word;
- break;
- case I2C_SMBUS_PROC_CALL:
- iface->writeNum = 2;
- iface->readNum = 2;
- iface->cur_mode = TWI_I2C_MODE_COMBINED;
- iface->transPtr = (u8 *)&data->word;
- break;
- case I2C_SMBUS_BLOCK_DATA:
- if (read_write == I2C_SMBUS_READ) {
- iface->readNum = 0;
- iface->cur_mode = TWI_I2C_MODE_COMBINED;
- } else {
- iface->writeNum = data->block[0] + 1;
- iface->cur_mode = TWI_I2C_MODE_STANDARDSUB;
- }
- iface->transPtr = data->block;
- break;
- case I2C_SMBUS_I2C_BLOCK_DATA:
- if (read_write == I2C_SMBUS_READ) {
- iface->readNum = data->block[0];
- iface->cur_mode = TWI_I2C_MODE_COMBINED;
- } else {
- iface->writeNum = data->block[0];
- iface->cur_mode = TWI_I2C_MODE_STANDARDSUB;
- }
- iface->transPtr = (u8 *)&data->block[1];
- break;
- default:
- return -1;
- }
-
- iface->result = 0;
- iface->manual_stop = 0;
- iface->read_write = read_write;
- iface->command = command;
- init_completion(&(iface->complete));
-
- /* FIFO Initiation. Data in FIFO should be discarded before
- * start a new operation.
- */
- write_FIFO_CTL(iface, 0x3);
- write_FIFO_CTL(iface, 0);
-
- /* clear int stat */
- write_INT_STAT(iface, MERR | MCOMP | XMTSERV | RCVSERV);
-
- /* Set Transmit device address */
- write_MASTER_ADDR(iface, addr);
-
- switch (iface->cur_mode) {
- case TWI_I2C_MODE_STANDARDSUB:
- write_XMT_DATA8(iface, iface->command);
- write_INT_MASK(iface, MCOMP | MERR |
- ((iface->read_write == I2C_SMBUS_READ) ?
- RCVSERV : XMTSERV));
-
- if (iface->writeNum + 1 <= 255)
- write_MASTER_CTL(iface, (iface->writeNum + 1) << 6);
- else {
- write_MASTER_CTL(iface, 0xff << 6);
- iface->manual_stop = 1;
- }
- /* Master enable */
- write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN |
- ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ>100) ? FAST : 0));
- break;
- case TWI_I2C_MODE_COMBINED:
- write_XMT_DATA8(iface, iface->command);
- write_INT_MASK(iface, MCOMP | MERR | RCVSERV | XMTSERV);
-
- if (iface->writeNum > 0)
- write_MASTER_CTL(iface, (iface->writeNum + 1) << 6);
- else
- write_MASTER_CTL(iface, 0x1 << 6);
- /* Master enable */
- write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN | RSTART |
- ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ>100) ? FAST : 0));
- break;
- default:
- write_MASTER_CTL(iface, 0);
- if (size != I2C_SMBUS_QUICK) {
- /* Don't access xmit data register when this is a
- * read operation.
- */
- if (iface->read_write != I2C_SMBUS_READ) {
- if (iface->writeNum > 0) {
- write_XMT_DATA8(iface,
- *(iface->transPtr++));
- if (iface->writeNum <= 255)
- write_MASTER_CTL(iface,
- iface->writeNum << 6);
- else {
- write_MASTER_CTL(iface,
- 0xff << 6);
- iface->manual_stop = 1;
- }
- iface->writeNum--;
- } else {
- write_XMT_DATA8(iface, iface->command);
- write_MASTER_CTL(iface, 1 << 6);
- }
- } else {
- if (iface->readNum > 0 && iface->readNum <= 255)
- write_MASTER_CTL(iface,
- iface->readNum << 6);
- else if (iface->readNum > 255) {
- write_MASTER_CTL(iface, 0xff << 6);
- iface->manual_stop = 1;
- } else
- break;
- }
- }
- write_INT_MASK(iface, MCOMP | MERR |
- ((iface->read_write == I2C_SMBUS_READ) ?
- RCVSERV : XMTSERV));
-
- /* Master enable */
- write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN |
- ((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) |
- ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100) ? FAST : 0));
- break;
- }
-
- while (!iface->result) {
- if (!wait_for_completion_timeout(&iface->complete,
- adap->timeout)) {
- iface->result = -1;
- dev_err(&adap->dev, "smbus transfer timeout\n");
- }
- }
-
- rc = (iface->result >= 0) ? 0 : -1;
-
- return rc;
-}
-
-/*
- * Generic I2C SMBus transfer entrypoint
- */
-int bfin_twi_smbus_xfer(struct i2c_adapter *adap, u16 addr,
- unsigned short flags, char read_write,
- u8 command, int size, union i2c_smbus_data *data)
-{
- return bfin_twi_do_smbus_xfer(adap, addr, flags,
- read_write, command, size, data);
-}
-
-/*
- * Return what the adapter supports
- */
-static u32 bfin_twi_functionality(struct i2c_adapter *adap)
-{
- return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
- I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
- I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL |
- I2C_FUNC_I2C | I2C_FUNC_SMBUS_I2C_BLOCK;
-}
-
-static const struct i2c_algorithm bfin_twi_algorithm = {
- .master_xfer = bfin_twi_master_xfer,
- .smbus_xfer = bfin_twi_smbus_xfer,
- .functionality = bfin_twi_functionality,
-};
-
-#ifdef CONFIG_PM_SLEEP
-static int i2c_bfin_twi_suspend(struct device *dev)
-{
- struct bfin_twi_iface *iface = dev_get_drvdata(dev);
-
- iface->saved_clkdiv = read_CLKDIV(iface);
- iface->saved_control = read_CONTROL(iface);
-
- free_irq(iface->irq, iface);
-
- /* Disable TWI */
- write_CONTROL(iface, iface->saved_control & ~TWI_ENA);
-
- return 0;
-}
-
-static int i2c_bfin_twi_resume(struct device *dev)
-{
- struct bfin_twi_iface *iface = dev_get_drvdata(dev);
-
- int rc = request_irq(iface->irq, bfin_twi_interrupt_entry,
- 0, to_platform_device(dev)->name, iface);
- if (rc) {
- dev_err(dev, "Can't get IRQ %d !\n", iface->irq);
- return -ENODEV;
- }
-
- /* Resume TWI interface clock as specified */
- write_CLKDIV(iface, iface->saved_clkdiv);
-
- /* Resume TWI */
- write_CONTROL(iface, iface->saved_control);
-
- return 0;
-}
-
-static SIMPLE_DEV_PM_OPS(i2c_bfin_twi_pm,
- i2c_bfin_twi_suspend, i2c_bfin_twi_resume);
-#define I2C_BFIN_TWI_PM_OPS (&i2c_bfin_twi_pm)
-#else
-#define I2C_BFIN_TWI_PM_OPS NULL
-#endif
-
-static int i2c_bfin_twi_probe(struct platform_device *pdev)
-{
- struct bfin_twi_iface *iface;
- struct i2c_adapter *p_adap;
- struct resource *res;
- int rc;
- unsigned int clkhilow;
-
- iface = devm_kzalloc(&pdev->dev, sizeof(struct bfin_twi_iface),
- GFP_KERNEL);
- if (!iface) {
- dev_err(&pdev->dev, "Cannot allocate memory\n");
- return -ENOMEM;
- }
-
- spin_lock_init(&(iface->lock));
-
- /* Find and map our resources */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- iface->regs_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(iface->regs_base)) {
- dev_err(&pdev->dev, "Cannot map IO\n");
- return PTR_ERR(iface->regs_base);
- }
-
- iface->irq = platform_get_irq(pdev, 0);
- if (iface->irq < 0) {
- dev_err(&pdev->dev, "No IRQ specified\n");
- return -ENOENT;
- }
-
- p_adap = &iface->adap;
- p_adap->nr = pdev->id;
- strlcpy(p_adap->name, pdev->name, sizeof(p_adap->name));
- p_adap->algo = &bfin_twi_algorithm;
- p_adap->algo_data = iface;
- p_adap->class = I2C_CLASS_DEPRECATED;
- p_adap->dev.parent = &pdev->dev;
- p_adap->timeout = 5 * HZ;
- p_adap->retries = 3;
-
- rc = peripheral_request_list(
- dev_get_platdata(&pdev->dev),
- "i2c-bfin-twi");
- if (rc) {
- dev_err(&pdev->dev, "Can't setup pin mux!\n");
- return -EBUSY;
- }
-
- rc = devm_request_irq(&pdev->dev, iface->irq, bfin_twi_interrupt_entry,
- 0, pdev->name, iface);
- if (rc) {
- dev_err(&pdev->dev, "Can't get IRQ %d !\n", iface->irq);
- rc = -ENODEV;
- goto out_error;
- }
-
- /* Set TWI internal clock as 10MHz */
- write_CONTROL(iface, ((get_sclk() / 1000 / 1000 + 5) / 10) & 0x7F);
-
- /*
- * We will not end up with a CLKDIV=0 because no one will specify
- * 20kHz SCL or less in Kconfig now. (5 * 1000 / 20 = 250)
- */
- clkhilow = ((10 * 1000 / CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ) + 1) / 2;
-
- /* Set Twi interface clock as specified */
- write_CLKDIV(iface, (clkhilow << 8) | clkhilow);
-
- /* Enable TWI */
- write_CONTROL(iface, read_CONTROL(iface) | TWI_ENA);
-
- rc = i2c_add_numbered_adapter(p_adap);
- if (rc < 0)
- goto out_error;
-
- platform_set_drvdata(pdev, iface);
-
- dev_info(&pdev->dev, "Blackfin BF5xx on-chip I2C TWI Controller, "
- "regs_base@%p\n", iface->regs_base);
-
- return 0;
-
-out_error:
- peripheral_free_list(dev_get_platdata(&pdev->dev));
- return rc;
-}
-
-static int i2c_bfin_twi_remove(struct platform_device *pdev)
-{
- struct bfin_twi_iface *iface = platform_get_drvdata(pdev);
-
- i2c_del_adapter(&(iface->adap));
- peripheral_free_list(dev_get_platdata(&pdev->dev));
-
- return 0;
-}
-
-static struct platform_driver i2c_bfin_twi_driver = {
- .probe = i2c_bfin_twi_probe,
- .remove = i2c_bfin_twi_remove,
- .driver = {
- .name = "i2c-bfin-twi",
- .pm = I2C_BFIN_TWI_PM_OPS,
- },
-};
-
-static int __init i2c_bfin_twi_init(void)
-{
- return platform_driver_register(&i2c_bfin_twi_driver);
-}
-
-static void __exit i2c_bfin_twi_exit(void)
-{
- platform_driver_unregister(&i2c_bfin_twi_driver);
-}
-
-subsys_initcall(i2c_bfin_twi_init);
-module_exit(i2c_bfin_twi_exit);
-
-MODULE_AUTHOR("Bryan Wu, Sonic Zhang");
-MODULE_DESCRIPTION("Blackfin BF5xx on-chip I2C TWI Controller Driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:i2c-bfin-twi");
diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c
index 05732531829f..fd36c39ddf4e 100644
--- a/drivers/i2c/busses/i2c-designware-master.c
+++ b/drivers/i2c/busses/i2c-designware-master.c
@@ -163,7 +163,7 @@ static int i2c_dw_init_master(struct dw_i2c_dev *dev)
if (!(dev->sda_hold_time & DW_IC_SDA_HOLD_RX_MASK))
dev->sda_hold_time |= 1 << DW_IC_SDA_HOLD_RX_SHIFT;
dw_writel(dev, dev->sda_hold_time, DW_IC_SDA_HOLD);
- } else {
+ } else if (dev->sda_hold_time) {
dev_warn(dev->dev,
"Hardware too old to adjust SDA hold time.\n");
}
diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c
index b02428498f6d..12ec8484e653 100644
--- a/drivers/i2c/busses/i2c-exynos5.c
+++ b/drivers/i2c/busses/i2c-exynos5.c
@@ -128,6 +128,10 @@
#define HSI2C_TIMEOUT_EN (1u << 31)
#define HSI2C_TIMEOUT_MASK 0xff
+/* I2C_MANUAL_CMD register bits */
+#define HSI2C_CMD_READ_DATA (1u << 4)
+#define HSI2C_CMD_SEND_STOP (1u << 2)
+
/* I2C_TRANS_STATUS register bits */
#define HSI2C_MASTER_BUSY (1u << 17)
#define HSI2C_SLAVE_BUSY (1u << 16)
@@ -441,12 +445,6 @@ static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id)
i2c->state = -ETIMEDOUT;
goto stop;
}
-
- trans_status = readl(i2c->regs + HSI2C_TRANS_STATUS);
- if ((trans_status & HSI2C_MASTER_ST_MASK) == HSI2C_MASTER_ST_LOSE) {
- i2c->state = -EAGAIN;
- goto stop;
- }
} else if (int_status & HSI2C_INT_I2C) {
trans_status = readl(i2c->regs + HSI2C_TRANS_STATUS);
if (trans_status & HSI2C_NO_DEV_ACK) {
@@ -544,6 +542,57 @@ static int exynos5_i2c_wait_bus_idle(struct exynos5_i2c *i2c)
return -EBUSY;
}
+static void exynos5_i2c_bus_recover(struct exynos5_i2c *i2c)
+{
+ u32 val;
+
+ val = readl(i2c->regs + HSI2C_CTL) | HSI2C_RXCHON;
+ writel(val, i2c->regs + HSI2C_CTL);
+ val = readl(i2c->regs + HSI2C_CONF) & ~HSI2C_AUTO_MODE;
+ writel(val, i2c->regs + HSI2C_CONF);
+
+ /*
+ * Specification says master should send nine clock pulses. It can be
+ * emulated by sending manual read command (nine pulses for read eight
+ * bits + one pulse for NACK).
+ */
+ writel(HSI2C_CMD_READ_DATA, i2c->regs + HSI2C_MANUAL_CMD);
+ exynos5_i2c_wait_bus_idle(i2c);
+ writel(HSI2C_CMD_SEND_STOP, i2c->regs + HSI2C_MANUAL_CMD);
+ exynos5_i2c_wait_bus_idle(i2c);
+
+ val = readl(i2c->regs + HSI2C_CTL) & ~HSI2C_RXCHON;
+ writel(val, i2c->regs + HSI2C_CTL);
+ val = readl(i2c->regs + HSI2C_CONF) | HSI2C_AUTO_MODE;
+ writel(val, i2c->regs + HSI2C_CONF);
+}
+
+static void exynos5_i2c_bus_check(struct exynos5_i2c *i2c)
+{
+ unsigned long timeout;
+
+ if (i2c->variant->hw != HSI2C_EXYNOS7)
+ return;
+
+ /*
+ * HSI2C_MASTER_ST_LOSE state in EXYNOS7 variant before transaction
+ * indicates that bus is stuck (SDA is low). In such case bus recovery
+ * can be performed.
+ */
+ timeout = jiffies + msecs_to_jiffies(100);
+ for (;;) {
+ u32 st = readl(i2c->regs + HSI2C_TRANS_STATUS);
+
+ if ((st & HSI2C_MASTER_ST_MASK) != HSI2C_MASTER_ST_LOSE)
+ return;
+
+ if (time_is_before_jiffies(timeout))
+ return;
+
+ exynos5_i2c_bus_recover(i2c);
+ }
+}
+
/*
* exynos5_i2c_message_start: Configures the bus and starts the xfer
* i2c: struct exynos5_i2c pointer for the current bus
@@ -598,6 +647,8 @@ static void exynos5_i2c_message_start(struct exynos5_i2c *i2c, int stop)
writel(fifo_ctl, i2c->regs + HSI2C_FIFO_CTL);
writel(i2c_ctl, i2c->regs + HSI2C_CTL);
+ exynos5_i2c_bus_check(i2c);
+
/*
* Enable interrupts before starting the transfer so that we don't
* miss any INT_I2C interrupts.
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 692b34125866..e0d59e9ff3c6 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -966,8 +966,6 @@ static void i801_enable_host_notify(struct i2c_adapter *adapter)
if (!(priv->features & FEATURE_HOST_NOTIFY))
return;
- priv->original_slvcmd = inb_p(SMBSLVCMD(priv));
-
if (!(SMBSLVCMD_HST_NTFY_INTREN & priv->original_slvcmd))
outb_p(SMBSLVCMD_HST_NTFY_INTREN | priv->original_slvcmd,
SMBSLVCMD(priv));
@@ -1615,6 +1613,10 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
outb_p(inb_p(SMBAUXCTL(priv)) &
~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL(priv));
+ /* Remember original Host Notify setting */
+ if (priv->features & FEATURE_HOST_NOTIFY)
+ priv->original_slvcmd = inb_p(SMBSLVCMD(priv));
+
/* Default timeout in interrupt mode: 200 ms */
priv->adapter.timeout = HZ / 5;
@@ -1699,6 +1701,15 @@ static void i801_remove(struct pci_dev *dev)
*/
}
+static void i801_shutdown(struct pci_dev *dev)
+{
+ struct i801_priv *priv = pci_get_drvdata(dev);
+
+ /* Restore config registers to avoid hard hang on some systems */
+ i801_disable_host_notify(priv);
+ pci_write_config_byte(dev, SMBHSTCFG, priv->original_hstcfg);
+}
+
#ifdef CONFIG_PM
static int i801_suspend(struct device *dev)
{
@@ -1728,6 +1739,7 @@ static struct pci_driver i801_driver = {
.id_table = i801_ids,
.probe = i801_probe,
.remove = i801_remove,
+ .shutdown = i801_shutdown,
.driver = {
.pm = &i801_pm_ops,
},
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index 999557729ad2..d7267dd9c7bf 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -194,6 +194,7 @@ struct imx_i2c_dma {
struct imx_i2c_struct {
struct i2c_adapter adapter;
struct clk *clk;
+ struct notifier_block clk_change_nb;
void __iomem *base;
wait_queue_head_t queue;
unsigned long i2csr;
@@ -467,15 +468,14 @@ static int i2c_imx_acked(struct imx_i2c_struct *i2c_imx)
return 0;
}
-static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx)
+static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx,
+ unsigned int i2c_clk_rate)
{
struct imx_i2c_clk_pair *i2c_clk_div = i2c_imx->hwdata->clk_div;
- unsigned int i2c_clk_rate;
unsigned int div;
int i;
/* Divider value calculation */
- i2c_clk_rate = clk_get_rate(i2c_imx->clk);
if (i2c_imx->cur_clk == i2c_clk_rate)
return;
@@ -510,6 +510,20 @@ static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx)
#endif
}
+static int i2c_imx_clk_notifier_call(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct clk_notifier_data *ndata = data;
+ struct imx_i2c_struct *i2c_imx = container_of(&ndata->clk,
+ struct imx_i2c_struct,
+ clk);
+
+ if (action & POST_RATE_CHANGE)
+ i2c_imx_set_clk(i2c_imx, ndata->new_rate);
+
+ return NOTIFY_OK;
+}
+
static int i2c_imx_start(struct imx_i2c_struct *i2c_imx)
{
unsigned int temp = 0;
@@ -517,8 +531,6 @@ static int i2c_imx_start(struct imx_i2c_struct *i2c_imx)
dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
- i2c_imx_set_clk(i2c_imx);
-
imx_i2c_write_reg(i2c_imx->ifdr, i2c_imx, IMX_I2C_IFDR);
/* Enable I2C controller */
imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);
@@ -1131,6 +1143,9 @@ static int i2c_imx_probe(struct platform_device *pdev)
"clock-frequency", &i2c_imx->bitrate);
if (ret < 0 && pdata && pdata->bitrate)
i2c_imx->bitrate = pdata->bitrate;
+ i2c_imx->clk_change_nb.notifier_call = i2c_imx_clk_notifier_call;
+ clk_notifier_register(i2c_imx->clk, &i2c_imx->clk_change_nb);
+ i2c_imx_set_clk(i2c_imx, clk_get_rate(i2c_imx->clk));
/* Set up chip registers to defaults */
imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
@@ -1141,12 +1156,12 @@ static int i2c_imx_probe(struct platform_device *pdev)
ret = i2c_imx_init_recovery_info(i2c_imx, pdev);
/* Give it another chance if pinctrl used is not ready yet */
if (ret == -EPROBE_DEFER)
- goto rpm_disable;
+ goto clk_notifier_unregister;
/* Add I2C adapter */
ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
if (ret < 0)
- goto rpm_disable;
+ goto clk_notifier_unregister;
pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev);
@@ -1162,6 +1177,8 @@ static int i2c_imx_probe(struct platform_device *pdev)
return 0; /* Return OK */
+clk_notifier_unregister:
+ clk_notifier_unregister(i2c_imx->clk, &i2c_imx->clk_change_nb);
rpm_disable:
pm_runtime_put_noidle(&pdev->dev);
pm_runtime_disable(&pdev->dev);
@@ -1195,6 +1212,7 @@ static int i2c_imx_remove(struct platform_device *pdev)
imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2CR);
imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2SR);
+ clk_notifier_unregister(i2c_imx->clk, &i2c_imx->clk_change_nb);
clk_disable_unprepare(i2c_imx->clk);
pm_runtime_put_noidle(&pdev->dev);
@@ -1208,7 +1226,7 @@ static int i2c_imx_runtime_suspend(struct device *dev)
{
struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev);
- clk_disable_unprepare(i2c_imx->clk);
+ clk_disable(i2c_imx->clk);
return 0;
}
@@ -1218,7 +1236,7 @@ static int i2c_imx_runtime_resume(struct device *dev)
struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev);
int ret;
- ret = clk_prepare_enable(i2c_imx->clk);
+ ret = clk_enable(i2c_imx->clk);
if (ret)
dev_err(dev, "can't enable I2C clock, ret=%d\n", ret);
diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
index 440fe4a96e68..a5a95ea5b81a 100644
--- a/drivers/i2c/busses/i2c-mv64xxx.c
+++ b/drivers/i2c/busses/i2c-mv64xxx.c
@@ -845,12 +845,16 @@ mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
*/
if (of_device_is_compatible(np, "marvell,mv78230-i2c")) {
drv_data->offload_enabled = true;
- drv_data->errata_delay = true;
+ /* The delay is only needed in standard mode (100kHz) */
+ if (bus_freq <= 100000)
+ drv_data->errata_delay = true;
}
if (of_device_is_compatible(np, "marvell,mv78230-a0-i2c")) {
drv_data->offload_enabled = false;
- drv_data->errata_delay = true;
+ /* The delay is only needed in standard mode (100kHz) */
+ if (bus_freq <= 100000)
+ drv_data->errata_delay = true;
}
if (of_device_is_compatible(np, "allwinner,sun6i-a31-i2c"))
diff --git a/drivers/i2c/busses/i2c-pca-platform.c b/drivers/i2c/busses/i2c-pca-platform.c
index 853a2abedb05..bc2707ffd409 100644
--- a/drivers/i2c/busses/i2c-pca-platform.c
+++ b/drivers/i2c/busses/i2c-pca-platform.c
@@ -173,33 +173,19 @@ static int i2c_pca_pf_probe(struct platform_device *pdev)
i2c->adap.dev.parent = &pdev->dev;
i2c->adap.dev.of_node = np;
+ i2c->gpio = devm_gpiod_get_optional(&pdev->dev, "reset-gpios", GPIOD_OUT_LOW);
+ if (IS_ERR(i2c->gpio))
+ return PTR_ERR(i2c->gpio);
+
+ i2c->adap.timeout = HZ;
+ ret = device_property_read_u32(&pdev->dev, "clock-frequency",
+ &i2c->algo_data.i2c_clock);
+ if (ret)
+ i2c->algo_data.i2c_clock = 59000;
+
if (platform_data) {
i2c->adap.timeout = platform_data->timeout;
i2c->algo_data.i2c_clock = platform_data->i2c_clock_speed;
- if (gpio_is_valid(platform_data->gpio)) {
- ret = devm_gpio_request_one(&pdev->dev,
- platform_data->gpio,
- GPIOF_ACTIVE_LOW,
- i2c->adap.name);
- if (ret == 0) {
- i2c->gpio = gpio_to_desc(platform_data->gpio);
- gpiod_direction_output(i2c->gpio, 0);
- } else {
- dev_warn(&pdev->dev, "Registering gpio failed!\n");
- i2c->gpio = NULL;
- }
- }
- } else if (np) {
- i2c->adap.timeout = HZ;
- i2c->gpio = devm_gpiod_get_optional(&pdev->dev, "reset-gpios", GPIOD_OUT_LOW);
- if (IS_ERR(i2c->gpio))
- return PTR_ERR(i2c->gpio);
- of_property_read_u32_index(np, "clock-frequency", 0,
- &i2c->algo_data.i2c_clock);
- } else {
- i2c->adap.timeout = HZ;
- i2c->algo_data.i2c_clock = 59000;
- i2c->gpio = NULL;
}
i2c->algo_data.data = i2c;
diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c
index 462948e2c535..90946a8b9a75 100644
--- a/drivers/i2c/busses/i2c-piix4.c
+++ b/drivers/i2c/busses/i2c-piix4.c
@@ -40,7 +40,6 @@
#include <linux/dmi.h>
#include <linux/acpi.h>
#include <linux/io.h>
-#include <linux/mutex.h>
/* PIIX4 SMBus address offsets */
@@ -153,10 +152,7 @@ static const struct dmi_system_id piix4_dmi_ibm[] = {
/*
* SB800 globals
- * piix4_mutex_sb800 protects piix4_port_sel_sb800 and the pair
- * of I/O ports at SB800_PIIX4_SMB_IDX.
*/
-static DEFINE_MUTEX(piix4_mutex_sb800);
static u8 piix4_port_sel_sb800;
static u8 piix4_port_mask_sb800;
static u8 piix4_port_shift_sb800;
@@ -298,12 +294,19 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
else
smb_en = (aux) ? 0x28 : 0x2c;
- mutex_lock(&piix4_mutex_sb800);
+ if (!request_muxed_region(SB800_PIIX4_SMB_IDX, 2, "sb800_piix4_smb")) {
+ dev_err(&PIIX4_dev->dev,
+ "SMB base address index region 0x%x already in use.\n",
+ SB800_PIIX4_SMB_IDX);
+ return -EBUSY;
+ }
+
outb_p(smb_en, SB800_PIIX4_SMB_IDX);
smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1);
outb_p(smb_en + 1, SB800_PIIX4_SMB_IDX);
smba_en_hi = inb_p(SB800_PIIX4_SMB_IDX + 1);
- mutex_unlock(&piix4_mutex_sb800);
+
+ release_region(SB800_PIIX4_SMB_IDX, 2);
if (!smb_en) {
smb_en_status = smba_en_lo & 0x10;
@@ -373,7 +376,12 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
break;
}
} else {
- mutex_lock(&piix4_mutex_sb800);
+ if (!request_muxed_region(SB800_PIIX4_SMB_IDX, 2,
+ "sb800_piix4_smb")) {
+ release_region(piix4_smba, SMBIOSIZE);
+ return -EBUSY;
+ }
+
outb_p(SB800_PIIX4_PORT_IDX_SEL, SB800_PIIX4_SMB_IDX);
port_sel = inb_p(SB800_PIIX4_SMB_IDX + 1);
piix4_port_sel_sb800 = (port_sel & 0x01) ?
@@ -381,7 +389,7 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
SB800_PIIX4_PORT_IDX;
piix4_port_mask_sb800 = SB800_PIIX4_PORT_IDX_MASK;
piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT;
- mutex_unlock(&piix4_mutex_sb800);
+ release_region(SB800_PIIX4_SMB_IDX, 2);
}
dev_info(&PIIX4_dev->dev,
@@ -462,13 +470,13 @@ static int piix4_transaction(struct i2c_adapter *piix4_adapter)
/* We will always wait for a fraction of a second! (See PIIX4 docs errata) */
if (srvrworks_csb5_delay) /* Extra delay for SERVERWORKS_CSB5 */
- msleep(2);
+ usleep_range(2000, 2100);
else
- msleep(1);
+ usleep_range(250, 500);
while ((++timeout < MAX_TIMEOUT) &&
((temp = inb_p(SMBHSTSTS)) & 0x01))
- msleep(1);
+ usleep_range(250, 500);
/* If the SMBus is still busy, we give up */
if (timeout == MAX_TIMEOUT) {
@@ -679,7 +687,8 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr,
u8 port;
int retval;
- mutex_lock(&piix4_mutex_sb800);
+ if (!request_muxed_region(SB800_PIIX4_SMB_IDX, 2, "sb800_piix4_smb"))
+ return -EBUSY;
/* Request the SMBUS semaphore, avoid conflicts with the IMC */
smbslvcnt = inb_p(SMBSLVCNT);
@@ -695,8 +704,8 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr,
} while (--retries);
/* SMBus is still owned by the IMC, we give up */
if (!retries) {
- mutex_unlock(&piix4_mutex_sb800);
- return -EBUSY;
+ retval = -EBUSY;
+ goto release;
}
/*
@@ -753,8 +762,8 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr,
if ((size == I2C_SMBUS_BLOCK_DATA) && adapdata->notify_imc)
piix4_imc_wakeup();
- mutex_unlock(&piix4_mutex_sb800);
-
+release:
+ release_region(SB800_PIIX4_SMB_IDX, 2);
return retval;
}
@@ -899,13 +908,6 @@ static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id)
bool notify_imc = false;
is_sb800 = true;
- if (!request_region(SB800_PIIX4_SMB_IDX, 2, "smba_idx")) {
- dev_err(&dev->dev,
- "SMBus base address index region 0x%x already in use!\n",
- SB800_PIIX4_SMB_IDX);
- return -EBUSY;
- }
-
if (dev->vendor == PCI_VENDOR_ID_AMD &&
dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS) {
u8 imc;
@@ -922,20 +924,16 @@ static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id)
/* base address location etc changed in SB800 */
retval = piix4_setup_sb800(dev, id, 0);
- if (retval < 0) {
- release_region(SB800_PIIX4_SMB_IDX, 2);
+ if (retval < 0)
return retval;
- }
/*
* Try to register multiplexed main SMBus adapter,
* give up if we can't
*/
retval = piix4_add_adapters_sb800(dev, retval, notify_imc);
- if (retval < 0) {
- release_region(SB800_PIIX4_SMB_IDX, 2);
+ if (retval < 0)
return retval;
- }
} else {
retval = piix4_setup(dev, id);
if (retval < 0)
@@ -983,11 +981,8 @@ static void piix4_adap_remove(struct i2c_adapter *adap)
if (adapdata->smba) {
i2c_del_adapter(adap);
- if (adapdata->port == (0 << piix4_port_shift_sb800)) {
+ if (adapdata->port == (0 << piix4_port_shift_sb800))
release_region(adapdata->smba, SMBIOSIZE);
- if (adapdata->sb800_main)
- release_region(SB800_PIIX4_SMB_IDX, 2);
- }
kfree(adapdata);
kfree(adap);
}
diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index 08f8e0107642..904dfec7ab96 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
- * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2009-2013, 2016-2018, The Linux Foundation. All rights reserved.
* Copyright (c) 2014, Sony Mobile Communications AB.
*
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * 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.
- *
*/
#include <linux/acpi.h>
@@ -73,8 +64,11 @@
#define QUP_IN_SVC_FLAG BIT(9)
#define QUP_MX_OUTPUT_DONE BIT(10)
#define QUP_MX_INPUT_DONE BIT(11)
+#define OUT_BLOCK_WRITE_REQ BIT(12)
+#define IN_BLOCK_READ_REQ BIT(13)
/* I2C mini core related values */
+#define QUP_NO_INPUT BIT(7)
#define QUP_CLOCK_AUTO_GATE BIT(13)
#define I2C_MINI_CORE (2 << 8)
#define I2C_N_VAL 15
@@ -113,6 +107,7 @@
#define QUP_TAG_V2_DATAWR 0x82
#define QUP_TAG_V2_DATAWR_STOP 0x83
#define QUP_TAG_V2_DATARD 0x85
+#define QUP_TAG_V2_DATARD_NACK 0x86
#define QUP_TAG_V2_DATARD_STOP 0x87
/* Status, Error flags */
@@ -127,23 +122,87 @@
#define ONE_BYTE 0x1
#define QUP_I2C_MX_CONFIG_DURING_RUN BIT(31)
+/* Maximum transfer length for single DMA descriptor */
#define MX_TX_RX_LEN SZ_64K
#define MX_BLOCKS (MX_TX_RX_LEN / QUP_READ_LIMIT)
+/* Maximum transfer length for all DMA descriptors */
+#define MX_DMA_TX_RX_LEN (2 * MX_TX_RX_LEN)
+#define MX_DMA_BLOCKS (MX_DMA_TX_RX_LEN / QUP_READ_LIMIT)
-/* Max timeout in ms for 32k bytes */
-#define TOUT_MAX 300
+/*
+ * Minimum transfer timeout for i2c transfers in seconds. It will be added on
+ * the top of maximum transfer time calculated from i2c bus speed to compensate
+ * the overheads.
+ */
+#define TOUT_MIN 2
/* Default values. Use these if FW query fails */
#define DEFAULT_CLK_FREQ 100000
#define DEFAULT_SRC_CLK 20000000
+/*
+ * Max tags length (start, stop and maximum 2 bytes address) for each QUP
+ * data transfer
+ */
+#define QUP_MAX_TAGS_LEN 4
+/* Max data length for each DATARD tags */
+#define RECV_MAX_DATA_LEN 254
+/* TAG length for DATA READ in RX FIFO */
+#define READ_RX_TAGS_LEN 2
+
+/*
+ * count: no of blocks
+ * pos: current block number
+ * tx_tag_len: tx tag length for current block
+ * rx_tag_len: rx tag length for current block
+ * data_len: remaining data length for current message
+ * cur_blk_len: data length for current block
+ * total_tx_len: total tx length including tag bytes for current QUP transfer
+ * total_rx_len: total rx length including tag bytes for current QUP transfer
+ * tx_fifo_data_pos: current byte number in TX FIFO word
+ * tx_fifo_free: number of free bytes in current QUP block write.
+ * rx_fifo_data_pos: current byte number in RX FIFO word
+ * fifo_available: number of available bytes in RX FIFO for current
+ * QUP block read
+ * tx_fifo_data: QUP TX FIFO write works on word basis (4 bytes). New byte write
+ * to TX FIFO will be appended in this data and will be written to
+ * TX FIFO when all the 4 bytes are available.
+ * rx_fifo_data: QUP RX FIFO read works on word basis (4 bytes). This will
+ * contains the 4 bytes of RX data.
+ * cur_data: pointer to tell cur data position for current message
+ * cur_tx_tags: pointer to tell cur position in tags
+ * tx_tags_sent: all tx tag bytes have been written in FIFO word
+ * send_last_word: for tx FIFO, last word send is pending in current block
+ * rx_bytes_read: if all the bytes have been read from rx FIFO.
+ * rx_tags_fetched: all the rx tag bytes have been fetched from rx fifo word
+ * is_tx_blk_mode: whether tx uses block or FIFO mode in case of non BAM xfer.
+ * is_rx_blk_mode: whether rx uses block or FIFO mode in case of non BAM xfer.
+ * tags: contains tx tag bytes for current QUP transfer
+ */
struct qup_i2c_block {
- int count;
- int pos;
- int tx_tag_len;
- int rx_tag_len;
- int data_len;
- u8 tags[6];
+ int count;
+ int pos;
+ int tx_tag_len;
+ int rx_tag_len;
+ int data_len;
+ int cur_blk_len;
+ int total_tx_len;
+ int total_rx_len;
+ int tx_fifo_data_pos;
+ int tx_fifo_free;
+ int rx_fifo_data_pos;
+ int fifo_available;
+ u32 tx_fifo_data;
+ u32 rx_fifo_data;
+ u8 *cur_data;
+ u8 *cur_tx_tags;
+ bool tx_tags_sent;
+ bool send_last_word;
+ bool rx_tags_fetched;
+ bool rx_bytes_read;
+ bool is_tx_blk_mode;
+ bool is_rx_blk_mode;
+ u8 tags[6];
};
struct qup_i2c_tag {
@@ -155,6 +214,7 @@ struct qup_i2c_bam {
struct qup_i2c_tag tag;
struct dma_chan *dma;
struct scatterlist *sg;
+ unsigned int sg_cnt;
};
struct qup_i2c_dev {
@@ -171,7 +231,9 @@ struct qup_i2c_dev {
int out_blk_sz;
int in_blk_sz;
+ int blk_xfer_limit;
unsigned long one_byte_t;
+ unsigned long xfer_timeout;
struct qup_i2c_block blk;
struct i2c_msg *msg;
@@ -184,23 +246,37 @@ struct qup_i2c_dev {
/* To check if this is the last msg */
bool is_last;
+ bool is_smbus_read;
/* To configure when bus is in run state */
- int config_run;
+ u32 config_run;
/* dma parameters */
bool is_dma;
+ /* To check if the current transfer is using DMA */
+ bool use_dma;
+ unsigned int max_xfer_sg_len;
+ unsigned int tag_buf_pos;
+ /* The threshold length above which block mode will be used */
+ unsigned int blk_mode_threshold;
struct dma_pool *dpool;
struct qup_i2c_tag start_tag;
struct qup_i2c_bam brx;
struct qup_i2c_bam btx;
struct completion xfer;
+ /* function to write data in tx fifo */
+ void (*write_tx_fifo)(struct qup_i2c_dev *qup);
+ /* function to read data from rx fifo */
+ void (*read_rx_fifo)(struct qup_i2c_dev *qup);
+ /* function to write tags in tx fifo for i2c read transfer */
+ void (*write_rx_tags)(struct qup_i2c_dev *qup);
};
static irqreturn_t qup_i2c_interrupt(int irq, void *dev)
{
struct qup_i2c_dev *qup = dev;
+ struct qup_i2c_block *blk = &qup->blk;
u32 bus_err;
u32 qup_err;
u32 opflags;
@@ -226,17 +302,65 @@ static irqreturn_t qup_i2c_interrupt(int irq, void *dev)
if (bus_err)
writel(bus_err, qup->base + QUP_I2C_STATUS);
+ /*
+ * Check for BAM mode and returns if already error has come for current
+ * transfer. In Error case, sometimes, QUP generates more than one
+ * interrupt.
+ */
+ if (qup->use_dma && (qup->qup_err || qup->bus_err))
+ return IRQ_HANDLED;
+
/* Reset the QUP State in case of error */
if (qup_err || bus_err) {
- writel(QUP_RESET_STATE, qup->base + QUP_STATE);
+ /*
+ * Don’t reset the QUP state in case of BAM mode. The BAM
+ * flush operation needs to be scheduled in transfer function
+ * which will clear the remaining schedule descriptors in BAM
+ * HW FIFO and generates the BAM interrupt.
+ */
+ if (!qup->use_dma)
+ writel(QUP_RESET_STATE, qup->base + QUP_STATE);
goto done;
}
- if (opflags & QUP_IN_SVC_FLAG)
+ if (opflags & QUP_OUT_SVC_FLAG) {
+ writel(QUP_OUT_SVC_FLAG, qup->base + QUP_OPERATIONAL);
+
+ if (opflags & OUT_BLOCK_WRITE_REQ) {
+ blk->tx_fifo_free += qup->out_blk_sz;
+ if (qup->msg->flags & I2C_M_RD)
+ qup->write_rx_tags(qup);
+ else
+ qup->write_tx_fifo(qup);
+ }
+ }
+
+ if (opflags & QUP_IN_SVC_FLAG) {
writel(QUP_IN_SVC_FLAG, qup->base + QUP_OPERATIONAL);
- if (opflags & QUP_OUT_SVC_FLAG)
- writel(QUP_OUT_SVC_FLAG, qup->base + QUP_OPERATIONAL);
+ if (!blk->is_rx_blk_mode) {
+ blk->fifo_available += qup->in_fifo_sz;
+ qup->read_rx_fifo(qup);
+ } else if (opflags & IN_BLOCK_READ_REQ) {
+ blk->fifo_available += qup->in_blk_sz;
+ qup->read_rx_fifo(qup);
+ }
+ }
+
+ if (qup->msg->flags & I2C_M_RD) {
+ if (!blk->rx_bytes_read)
+ return IRQ_HANDLED;
+ } else {
+ /*
+ * Ideally, QUP_MAX_OUTPUT_DONE_FLAG should be checked
+ * for FIFO mode also. But, QUP_MAX_OUTPUT_DONE_FLAG lags
+ * behind QUP_OUTPUT_SERVICE_FLAG sometimes. The only reason
+ * of interrupt for write message in FIFO mode is
+ * QUP_MAX_OUTPUT_DONE_FLAG condition.
+ */
+ if (blk->is_tx_blk_mode && !(opflags & QUP_MX_OUTPUT_DONE))
+ return IRQ_HANDLED;
+ }
done:
qup->qup_err = qup_err;
@@ -303,147 +427,47 @@ static int qup_i2c_change_state(struct qup_i2c_dev *qup, u32 state)
return 0;
}
-/**
- * qup_i2c_wait_ready - wait for a give number of bytes in tx/rx path
- * @qup: The qup_i2c_dev device
- * @op: The bit/event to wait on
- * @val: value of the bit to wait on, 0 or 1
- * @len: The length the bytes to be transferred
- */
-static int qup_i2c_wait_ready(struct qup_i2c_dev *qup, int op, bool val,
- int len)
+/* Check if I2C bus returns to IDLE state */
+static int qup_i2c_bus_active(struct qup_i2c_dev *qup, int len)
{
unsigned long timeout;
- u32 opflags;
u32 status;
- u32 shift = __ffs(op);
int ret = 0;
- len *= qup->one_byte_t;
- /* timeout after a wait of twice the max time */
timeout = jiffies + len * 4;
-
for (;;) {
- opflags = readl(qup->base + QUP_OPERATIONAL);
status = readl(qup->base + QUP_I2C_STATUS);
+ if (!(status & I2C_STATUS_BUS_ACTIVE))
+ break;
- if (((opflags & op) >> shift) == val) {
- if ((op == QUP_OUT_NOT_EMPTY) && qup->is_last) {
- if (!(status & I2C_STATUS_BUS_ACTIVE)) {
- ret = 0;
- goto done;
- }
- } else {
- ret = 0;
- goto done;
- }
- }
-
- if (time_after(jiffies, timeout)) {
+ if (time_after(jiffies, timeout))
ret = -ETIMEDOUT;
- goto done;
- }
- usleep_range(len, len * 2);
- }
-
-done:
- if (qup->bus_err || qup->qup_err)
- ret = (qup->bus_err & QUP_I2C_NACK_FLAG) ? -ENXIO : -EIO;
-
- return ret;
-}
-
-static void qup_i2c_set_write_mode_v2(struct qup_i2c_dev *qup,
- struct i2c_msg *msg)
-{
- /* Number of entries to shift out, including the tags */
- int total = msg->len + qup->blk.tx_tag_len;
-
- total |= qup->config_run;
-
- if (total < qup->out_fifo_sz) {
- /* FIFO mode */
- writel(QUP_REPACK_EN, qup->base + QUP_IO_MODE);
- writel(total, qup->base + QUP_MX_WRITE_CNT);
- } else {
- /* BLOCK mode (transfer data on chunks) */
- writel(QUP_OUTPUT_BLK_MODE | QUP_REPACK_EN,
- qup->base + QUP_IO_MODE);
- writel(total, qup->base + QUP_MX_OUTPUT_CNT);
- }
-}
-
-static void qup_i2c_set_write_mode(struct qup_i2c_dev *qup, struct i2c_msg *msg)
-{
- /* Number of entries to shift out, including the start */
- int total = msg->len + 1;
-
- if (total < qup->out_fifo_sz) {
- /* FIFO mode */
- writel(QUP_REPACK_EN, qup->base + QUP_IO_MODE);
- writel(total, qup->base + QUP_MX_WRITE_CNT);
- } else {
- /* BLOCK mode (transfer data on chunks) */
- writel(QUP_OUTPUT_BLK_MODE | QUP_REPACK_EN,
- qup->base + QUP_IO_MODE);
- writel(total, qup->base + QUP_MX_OUTPUT_CNT);
- }
-}
-
-static int check_for_fifo_space(struct qup_i2c_dev *qup)
-{
- int ret;
-
- ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
- if (ret)
- goto out;
-
- ret = qup_i2c_wait_ready(qup, QUP_OUT_FULL,
- RESET_BIT, 4 * ONE_BYTE);
- if (ret) {
- /* Fifo is full. Drain out the fifo */
- ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
- if (ret)
- goto out;
- ret = qup_i2c_wait_ready(qup, QUP_OUT_NOT_EMPTY,
- RESET_BIT, 256 * ONE_BYTE);
- if (ret) {
- dev_err(qup->dev, "timeout for fifo out full");
- goto out;
- }
-
- ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
- if (ret)
- goto out;
+ usleep_range(len, len * 2);
}
-out:
return ret;
}
-static int qup_i2c_issue_write(struct qup_i2c_dev *qup, struct i2c_msg *msg)
+static void qup_i2c_write_tx_fifo_v1(struct qup_i2c_dev *qup)
{
+ struct qup_i2c_block *blk = &qup->blk;
+ struct i2c_msg *msg = qup->msg;
u32 addr = msg->addr << 1;
u32 qup_tag;
int idx;
u32 val;
- int ret = 0;
if (qup->pos == 0) {
val = QUP_TAG_START | addr;
idx = 1;
+ blk->tx_fifo_free--;
} else {
val = 0;
idx = 0;
}
- while (qup->pos < msg->len) {
- /* Check that there's space in the FIFO for our pair */
- ret = check_for_fifo_space(qup);
- if (ret)
- return ret;
-
+ while (blk->tx_fifo_free && qup->pos < msg->len) {
if (qup->pos == msg->len - 1)
qup_tag = QUP_TAG_STOP;
else
@@ -460,70 +484,24 @@ static int qup_i2c_issue_write(struct qup_i2c_dev *qup, struct i2c_msg *msg)
qup->pos++;
idx++;
+ blk->tx_fifo_free--;
}
-
- ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
-
- return ret;
}
static void qup_i2c_set_blk_data(struct qup_i2c_dev *qup,
struct i2c_msg *msg)
{
- memset(&qup->blk, 0, sizeof(qup->blk));
-
+ qup->blk.pos = 0;
qup->blk.data_len = msg->len;
- qup->blk.count = (msg->len + QUP_READ_LIMIT - 1) / QUP_READ_LIMIT;
-
- /* 4 bytes for first block and 2 writes for rest */
- qup->blk.tx_tag_len = 4 + (qup->blk.count - 1) * 2;
-
- /* There are 2 tag bytes that are read in to fifo for every block */
- if (msg->flags & I2C_M_RD)
- qup->blk.rx_tag_len = qup->blk.count * 2;
-}
-
-static int qup_i2c_send_data(struct qup_i2c_dev *qup, int tlen, u8 *tbuf,
- int dlen, u8 *dbuf)
-{
- u32 val = 0, idx = 0, pos = 0, i = 0, t;
- int len = tlen + dlen;
- u8 *buf = tbuf;
- int ret = 0;
-
- while (len > 0) {
- ret = check_for_fifo_space(qup);
- if (ret)
- return ret;
-
- t = (len >= 4) ? 4 : len;
-
- while (idx < t) {
- if (!i && (pos >= tlen)) {
- buf = dbuf;
- pos = 0;
- i = 1;
- }
- val |= buf[pos++] << (idx++ * 8);
- }
-
- writel(val, qup->base + QUP_OUT_FIFO_BASE);
- idx = 0;
- val = 0;
- len -= 4;
- }
-
- ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
-
- return ret;
+ qup->blk.count = DIV_ROUND_UP(msg->len, qup->blk_xfer_limit);
}
static int qup_i2c_get_data_len(struct qup_i2c_dev *qup)
{
int data_len;
- if (qup->blk.data_len > QUP_READ_LIMIT)
- data_len = QUP_READ_LIMIT;
+ if (qup->blk.data_len > qup->blk_xfer_limit)
+ data_len = qup->blk_xfer_limit;
else
data_len = qup->blk.data_len;
@@ -540,9 +518,9 @@ static int qup_i2c_set_tags_smb(u16 addr, u8 *tags, struct qup_i2c_dev *qup,
{
int len = 0;
- if (msg->len > 1) {
+ if (qup->is_smbus_read) {
tags[len++] = QUP_TAG_V2_DATARD_STOP;
- tags[len++] = qup_i2c_get_data_len(qup) - 1;
+ tags[len++] = qup_i2c_get_data_len(qup);
} else {
tags[len++] = QUP_TAG_V2_START;
tags[len++] = addr & 0xff;
@@ -558,7 +536,7 @@ static int qup_i2c_set_tags_smb(u16 addr, u8 *tags, struct qup_i2c_dev *qup,
}
static int qup_i2c_set_tags(u8 *tags, struct qup_i2c_dev *qup,
- struct i2c_msg *msg, int is_dma)
+ struct i2c_msg *msg)
{
u16 addr = i2c_8bit_addr_from_msg(msg);
int len = 0;
@@ -586,7 +564,9 @@ static int qup_i2c_set_tags(u8 *tags, struct qup_i2c_dev *qup,
tags[len++] = QUP_TAG_V2_DATAWR_STOP;
} else {
if (msg->flags & I2C_M_RD)
- tags[len++] = QUP_TAG_V2_DATARD;
+ tags[len++] = qup->blk.pos == (qup->blk.count - 1) ?
+ QUP_TAG_V2_DATARD_NACK :
+ QUP_TAG_V2_DATARD;
else
tags[len++] = QUP_TAG_V2_DATAWR;
}
@@ -599,32 +579,9 @@ static int qup_i2c_set_tags(u8 *tags, struct qup_i2c_dev *qup,
else
tags[len++] = data_len;
- if ((msg->flags & I2C_M_RD) && last && is_dma) {
- tags[len++] = QUP_BAM_INPUT_EOT;
- tags[len++] = QUP_BAM_FLUSH_STOP;
- }
-
return len;
}
-static int qup_i2c_issue_xfer_v2(struct qup_i2c_dev *qup, struct i2c_msg *msg)
-{
- int data_len = 0, tag_len, index;
- int ret;
-
- tag_len = qup_i2c_set_tags(qup->blk.tags, qup, msg, 0);
- index = msg->len - qup->blk.data_len;
-
- /* only tags are written for read */
- if (!(msg->flags & I2C_M_RD))
- data_len = qup_i2c_get_data_len(qup);
-
- ret = qup_i2c_send_data(qup, tag_len, qup->blk.tags,
- data_len, &msg->buf[index]);
- qup->blk.data_len -= data_len;
-
- return ret;
-}
static void qup_i2c_bam_cb(void *data)
{
@@ -684,115 +641,109 @@ static int qup_i2c_req_dma(struct qup_i2c_dev *qup)
return 0;
}
-static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
- int num)
+static int qup_i2c_bam_make_desc(struct qup_i2c_dev *qup, struct i2c_msg *msg)
{
- struct dma_async_tx_descriptor *txd, *rxd = NULL;
- int ret = 0, idx = 0, limit = QUP_READ_LIMIT;
- dma_cookie_t cookie_rx, cookie_tx;
- u32 rx_nents = 0, tx_nents = 0, len, blocks, rem;
- u32 i, tlen, tx_len, tx_buf = 0, rx_buf = 0, off = 0;
+ int ret = 0, limit = QUP_READ_LIMIT;
+ u32 len = 0, blocks, rem;
+ u32 i = 0, tlen, tx_len = 0;
u8 *tags;
- while (idx < num) {
- tx_len = 0, len = 0, i = 0;
-
- qup->is_last = (idx == (num - 1));
-
- qup_i2c_set_blk_data(qup, msg);
+ qup->blk_xfer_limit = QUP_READ_LIMIT;
+ qup_i2c_set_blk_data(qup, msg);
- blocks = qup->blk.count;
- rem = msg->len - (blocks - 1) * limit;
+ blocks = qup->blk.count;
+ rem = msg->len - (blocks - 1) * limit;
- if (msg->flags & I2C_M_RD) {
- rx_nents += (blocks * 2) + 1;
- tx_nents += 1;
+ if (msg->flags & I2C_M_RD) {
+ while (qup->blk.pos < blocks) {
+ tlen = (i == (blocks - 1)) ? rem : limit;
+ tags = &qup->start_tag.start[qup->tag_buf_pos + len];
+ len += qup_i2c_set_tags(tags, qup, msg);
+ qup->blk.data_len -= tlen;
- while (qup->blk.pos < blocks) {
- tlen = (i == (blocks - 1)) ? rem : limit;
- tags = &qup->start_tag.start[off + len];
- len += qup_i2c_set_tags(tags, qup, msg, 1);
- qup->blk.data_len -= tlen;
+ /* scratch buf to read the start and len tags */
+ ret = qup_sg_set_buf(&qup->brx.sg[qup->brx.sg_cnt++],
+ &qup->brx.tag.start[0],
+ 2, qup, DMA_FROM_DEVICE);
- /* scratch buf to read the start and len tags */
- ret = qup_sg_set_buf(&qup->brx.sg[rx_buf++],
- &qup->brx.tag.start[0],
- 2, qup, DMA_FROM_DEVICE);
+ if (ret)
+ return ret;
- if (ret)
- return ret;
+ ret = qup_sg_set_buf(&qup->brx.sg[qup->brx.sg_cnt++],
+ &msg->buf[limit * i],
+ tlen, qup,
+ DMA_FROM_DEVICE);
+ if (ret)
+ return ret;
- ret = qup_sg_set_buf(&qup->brx.sg[rx_buf++],
- &msg->buf[limit * i],
- tlen, qup,
- DMA_FROM_DEVICE);
- if (ret)
- return ret;
+ i++;
+ qup->blk.pos = i;
+ }
+ ret = qup_sg_set_buf(&qup->btx.sg[qup->btx.sg_cnt++],
+ &qup->start_tag.start[qup->tag_buf_pos],
+ len, qup, DMA_TO_DEVICE);
+ if (ret)
+ return ret;
- i++;
- qup->blk.pos = i;
- }
- ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++],
- &qup->start_tag.start[off],
- len, qup, DMA_TO_DEVICE);
+ qup->tag_buf_pos += len;
+ } else {
+ while (qup->blk.pos < blocks) {
+ tlen = (i == (blocks - 1)) ? rem : limit;
+ tags = &qup->start_tag.start[qup->tag_buf_pos + tx_len];
+ len = qup_i2c_set_tags(tags, qup, msg);
+ qup->blk.data_len -= tlen;
+
+ ret = qup_sg_set_buf(&qup->btx.sg[qup->btx.sg_cnt++],
+ tags, len,
+ qup, DMA_TO_DEVICE);
if (ret)
return ret;
- off += len;
- /* scratch buf to read the BAM EOT and FLUSH tags */
- ret = qup_sg_set_buf(&qup->brx.sg[rx_buf++],
- &qup->brx.tag.start[0],
- 2, qup, DMA_FROM_DEVICE);
+ tx_len += len;
+ ret = qup_sg_set_buf(&qup->btx.sg[qup->btx.sg_cnt++],
+ &msg->buf[limit * i],
+ tlen, qup, DMA_TO_DEVICE);
if (ret)
return ret;
- } else {
- tx_nents += (blocks * 2);
-
- while (qup->blk.pos < blocks) {
- tlen = (i == (blocks - 1)) ? rem : limit;
- tags = &qup->start_tag.start[off + tx_len];
- len = qup_i2c_set_tags(tags, qup, msg, 1);
- qup->blk.data_len -= tlen;
-
- ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++],
- tags, len,
- qup, DMA_TO_DEVICE);
- if (ret)
- return ret;
-
- tx_len += len;
- ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++],
- &msg->buf[limit * i],
- tlen, qup, DMA_TO_DEVICE);
- if (ret)
- return ret;
- i++;
- qup->blk.pos = i;
- }
- off += tx_len;
-
- if (idx == (num - 1)) {
- len = 1;
- if (rx_nents) {
- qup->btx.tag.start[0] =
- QUP_BAM_INPUT_EOT;
- len++;
- }
- qup->btx.tag.start[len - 1] =
- QUP_BAM_FLUSH_STOP;
- ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++],
- &qup->btx.tag.start[0],
- len, qup, DMA_TO_DEVICE);
- if (ret)
- return ret;
- tx_nents += 1;
- }
+ i++;
+ qup->blk.pos = i;
}
- idx++;
- msg++;
+
+ qup->tag_buf_pos += tx_len;
}
- txd = dmaengine_prep_slave_sg(qup->btx.dma, qup->btx.sg, tx_nents,
+ return 0;
+}
+
+static int qup_i2c_bam_schedule_desc(struct qup_i2c_dev *qup)
+{
+ struct dma_async_tx_descriptor *txd, *rxd = NULL;
+ int ret = 0;
+ dma_cookie_t cookie_rx, cookie_tx;
+ u32 len = 0;
+ u32 tx_cnt = qup->btx.sg_cnt, rx_cnt = qup->brx.sg_cnt;
+
+ /* schedule the EOT and FLUSH I2C tags */
+ len = 1;
+ if (rx_cnt) {
+ qup->btx.tag.start[0] = QUP_BAM_INPUT_EOT;
+ len++;
+
+ /* scratch buf to read the BAM EOT FLUSH tags */
+ ret = qup_sg_set_buf(&qup->brx.sg[rx_cnt++],
+ &qup->brx.tag.start[0],
+ 1, qup, DMA_FROM_DEVICE);
+ if (ret)
+ return ret;
+ }
+
+ qup->btx.tag.start[len - 1] = QUP_BAM_FLUSH_STOP;
+ ret = qup_sg_set_buf(&qup->btx.sg[tx_cnt++], &qup->btx.tag.start[0],
+ len, qup, DMA_TO_DEVICE);
+ if (ret)
+ return ret;
+
+ txd = dmaengine_prep_slave_sg(qup->btx.dma, qup->btx.sg, tx_cnt,
DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_PREP_FENCE);
if (!txd) {
@@ -801,7 +752,7 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
goto desc_err;
}
- if (!rx_nents) {
+ if (!rx_cnt) {
txd->callback = qup_i2c_bam_cb;
txd->callback_param = qup;
}
@@ -814,9 +765,9 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
dma_async_issue_pending(qup->btx.dma);
- if (rx_nents) {
+ if (rx_cnt) {
rxd = dmaengine_prep_slave_sg(qup->brx.dma, qup->brx.sg,
- rx_nents, DMA_DEV_TO_MEM,
+ rx_cnt, DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT);
if (!rxd) {
dev_err(qup->dev, "failed to get rx desc\n");
@@ -838,49 +789,51 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
dma_async_issue_pending(qup->brx.dma);
}
- if (!wait_for_completion_timeout(&qup->xfer, TOUT_MAX * HZ)) {
+ if (!wait_for_completion_timeout(&qup->xfer, qup->xfer_timeout)) {
dev_err(qup->dev, "normal trans timed out\n");
ret = -ETIMEDOUT;
}
if (ret || qup->bus_err || qup->qup_err) {
+ reinit_completion(&qup->xfer);
+
if (qup_i2c_change_state(qup, QUP_RUN_STATE)) {
dev_err(qup->dev, "change to run state timed out");
goto desc_err;
}
- if (rx_nents)
- writel(QUP_BAM_INPUT_EOT,
- qup->base + QUP_OUT_FIFO_BASE);
-
- writel(QUP_BAM_FLUSH_STOP, qup->base + QUP_OUT_FIFO_BASE);
-
qup_i2c_flush(qup);
/* wait for remaining interrupts to occur */
if (!wait_for_completion_timeout(&qup->xfer, HZ))
dev_err(qup->dev, "flush timed out\n");
- qup_i2c_rel_dma(qup);
-
ret = (qup->bus_err & QUP_I2C_NACK_FLAG) ? -ENXIO : -EIO;
}
desc_err:
- dma_unmap_sg(qup->dev, qup->btx.sg, tx_nents, DMA_TO_DEVICE);
+ dma_unmap_sg(qup->dev, qup->btx.sg, tx_cnt, DMA_TO_DEVICE);
- if (rx_nents)
- dma_unmap_sg(qup->dev, qup->brx.sg, rx_nents,
+ if (rx_cnt)
+ dma_unmap_sg(qup->dev, qup->brx.sg, rx_cnt,
DMA_FROM_DEVICE);
return ret;
}
+static void qup_i2c_bam_clear_tag_buffers(struct qup_i2c_dev *qup)
+{
+ qup->btx.sg_cnt = 0;
+ qup->brx.sg_cnt = 0;
+ qup->tag_buf_pos = 0;
+}
+
static int qup_i2c_bam_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
int num)
{
struct qup_i2c_dev *qup = i2c_get_adapdata(adap);
int ret = 0;
+ int idx = 0;
enable_irq(qup->irq);
ret = qup_i2c_req_dma(qup);
@@ -903,9 +856,34 @@ static int qup_i2c_bam_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
goto out;
writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
+ qup_i2c_bam_clear_tag_buffers(qup);
+
+ for (idx = 0; idx < num; idx++) {
+ qup->msg = msg + idx;
+ qup->is_last = idx == (num - 1);
+
+ ret = qup_i2c_bam_make_desc(qup, qup->msg);
+ if (ret)
+ break;
+
+ /*
+ * Make DMA descriptor and schedule the BAM transfer if its
+ * already crossed the maximum length. Since the memory for all
+ * tags buffers have been taken for 2 maximum possible
+ * transfers length so it will never cross the buffer actual
+ * length.
+ */
+ if (qup->btx.sg_cnt > qup->max_xfer_sg_len ||
+ qup->brx.sg_cnt > qup->max_xfer_sg_len ||
+ qup->is_last) {
+ ret = qup_i2c_bam_schedule_desc(qup);
+ if (ret)
+ break;
+
+ qup_i2c_bam_clear_tag_buffers(qup);
+ }
+ }
- qup->msg = msg;
- ret = qup_i2c_bam_do_xfer(qup, qup->msg, num);
out:
disable_irq(qup->irq);
@@ -919,7 +897,7 @@ static int qup_i2c_wait_for_complete(struct qup_i2c_dev *qup,
unsigned long left;
int ret = 0;
- left = wait_for_completion_timeout(&qup->xfer, HZ);
+ left = wait_for_completion_timeout(&qup->xfer, qup->xfer_timeout);
if (!left) {
writel(1, qup->base + QUP_SW_RESET);
ret = -ETIMEDOUT;
@@ -931,363 +909,635 @@ static int qup_i2c_wait_for_complete(struct qup_i2c_dev *qup,
return ret;
}
-static int qup_i2c_write_one_v2(struct qup_i2c_dev *qup, struct i2c_msg *msg)
+static void qup_i2c_read_rx_fifo_v1(struct qup_i2c_dev *qup)
{
- int ret = 0;
+ struct qup_i2c_block *blk = &qup->blk;
+ struct i2c_msg *msg = qup->msg;
+ u32 val = 0;
+ int idx = 0;
- qup->msg = msg;
- qup->pos = 0;
- enable_irq(qup->irq);
- qup_i2c_set_blk_data(qup, msg);
- qup_i2c_set_write_mode_v2(qup, msg);
+ while (blk->fifo_available && qup->pos < msg->len) {
+ if ((idx & 1) == 0) {
+ /* Reading 2 words at time */
+ val = readl(qup->base + QUP_IN_FIFO_BASE);
+ msg->buf[qup->pos++] = val & 0xFF;
+ } else {
+ msg->buf[qup->pos++] = val >> QUP_MSW_SHIFT;
+ }
+ idx++;
+ blk->fifo_available--;
+ }
+ if (qup->pos == msg->len)
+ blk->rx_bytes_read = true;
+}
+
+static void qup_i2c_write_rx_tags_v1(struct qup_i2c_dev *qup)
+{
+ struct i2c_msg *msg = qup->msg;
+ u32 addr, len, val;
+
+ addr = i2c_8bit_addr_from_msg(msg);
+
+ /* 0 is used to specify a length 256 (QUP_READ_LIMIT) */
+ len = (msg->len == QUP_READ_LIMIT) ? 0 : msg->len;
+
+ val = ((QUP_TAG_REC | len) << QUP_MSW_SHIFT) | QUP_TAG_START | addr;
+ writel(val, qup->base + QUP_OUT_FIFO_BASE);
+}
+
+static void qup_i2c_conf_v1(struct qup_i2c_dev *qup)
+{
+ struct qup_i2c_block *blk = &qup->blk;
+ u32 qup_config = I2C_MINI_CORE | I2C_N_VAL;
+ u32 io_mode = QUP_REPACK_EN;
+
+ blk->is_tx_blk_mode =
+ blk->total_tx_len > qup->out_fifo_sz ? true : false;
+ blk->is_rx_blk_mode =
+ blk->total_rx_len > qup->in_fifo_sz ? true : false;
+
+ if (blk->is_tx_blk_mode) {
+ io_mode |= QUP_OUTPUT_BLK_MODE;
+ writel(0, qup->base + QUP_MX_WRITE_CNT);
+ writel(blk->total_tx_len, qup->base + QUP_MX_OUTPUT_CNT);
+ } else {
+ writel(0, qup->base + QUP_MX_OUTPUT_CNT);
+ writel(blk->total_tx_len, qup->base + QUP_MX_WRITE_CNT);
+ }
+
+ if (blk->total_rx_len) {
+ if (blk->is_rx_blk_mode) {
+ io_mode |= QUP_INPUT_BLK_MODE;
+ writel(0, qup->base + QUP_MX_READ_CNT);
+ writel(blk->total_rx_len, qup->base + QUP_MX_INPUT_CNT);
+ } else {
+ writel(0, qup->base + QUP_MX_INPUT_CNT);
+ writel(blk->total_rx_len, qup->base + QUP_MX_READ_CNT);
+ }
+ } else {
+ qup_config |= QUP_NO_INPUT;
+ }
+
+ writel(qup_config, qup->base + QUP_CONFIG);
+ writel(io_mode, qup->base + QUP_IO_MODE);
+}
+
+static void qup_i2c_clear_blk_v1(struct qup_i2c_block *blk)
+{
+ blk->tx_fifo_free = 0;
+ blk->fifo_available = 0;
+ blk->rx_bytes_read = false;
+}
+
+static int qup_i2c_conf_xfer_v1(struct qup_i2c_dev *qup, bool is_rx)
+{
+ struct qup_i2c_block *blk = &qup->blk;
+ int ret;
+
+ qup_i2c_clear_blk_v1(blk);
+ qup_i2c_conf_v1(qup);
ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
if (ret)
- goto err;
+ return ret;
writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
- do {
- ret = qup_i2c_issue_xfer_v2(qup, msg);
- if (ret)
- goto err;
+ ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
+ if (ret)
+ return ret;
- ret = qup_i2c_wait_for_complete(qup, msg);
- if (ret)
- goto err;
+ reinit_completion(&qup->xfer);
+ enable_irq(qup->irq);
+ if (!blk->is_tx_blk_mode) {
+ blk->tx_fifo_free = qup->out_fifo_sz;
+
+ if (is_rx)
+ qup_i2c_write_rx_tags_v1(qup);
+ else
+ qup_i2c_write_tx_fifo_v1(qup);
+ }
+
+ ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
+ if (ret)
+ goto err;
- qup->blk.pos++;
- } while (qup->blk.pos < qup->blk.count);
+ ret = qup_i2c_wait_for_complete(qup, qup->msg);
+ if (ret)
+ goto err;
- ret = qup_i2c_wait_ready(qup, QUP_OUT_NOT_EMPTY, RESET_BIT, ONE_BYTE);
+ ret = qup_i2c_bus_active(qup, ONE_BYTE);
err:
disable_irq(qup->irq);
- qup->msg = NULL;
-
return ret;
}
-static int qup_i2c_write_one(struct qup_i2c_dev *qup, struct i2c_msg *msg)
+static int qup_i2c_write_one(struct qup_i2c_dev *qup)
{
- int ret;
+ struct i2c_msg *msg = qup->msg;
+ struct qup_i2c_block *blk = &qup->blk;
- qup->msg = msg;
qup->pos = 0;
+ blk->total_tx_len = msg->len + 1;
+ blk->total_rx_len = 0;
- enable_irq(qup->irq);
+ return qup_i2c_conf_xfer_v1(qup, false);
+}
- qup_i2c_set_write_mode(qup, msg);
+static int qup_i2c_read_one(struct qup_i2c_dev *qup)
+{
+ struct qup_i2c_block *blk = &qup->blk;
- ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
+ qup->pos = 0;
+ blk->total_tx_len = 2;
+ blk->total_rx_len = qup->msg->len;
+
+ return qup_i2c_conf_xfer_v1(qup, true);
+}
+
+static int qup_i2c_xfer(struct i2c_adapter *adap,
+ struct i2c_msg msgs[],
+ int num)
+{
+ struct qup_i2c_dev *qup = i2c_get_adapdata(adap);
+ int ret, idx;
+
+ ret = pm_runtime_get_sync(qup->dev);
+ if (ret < 0)
+ goto out;
+
+ qup->bus_err = 0;
+ qup->qup_err = 0;
+
+ writel(1, qup->base + QUP_SW_RESET);
+ ret = qup_i2c_poll_state(qup, QUP_RESET_STATE);
if (ret)
- goto err;
+ goto out;
- writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
+ /* Configure QUP as I2C mini core */
+ writel(I2C_MINI_CORE | I2C_N_VAL, qup->base + QUP_CONFIG);
- do {
- ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
- if (ret)
- goto err;
+ for (idx = 0; idx < num; idx++) {
+ if (msgs[idx].len == 0) {
+ ret = -EINVAL;
+ goto out;
+ }
- ret = qup_i2c_issue_write(qup, msg);
- if (ret)
- goto err;
+ if (qup_i2c_poll_state_i2c_master(qup)) {
+ ret = -EIO;
+ goto out;
+ }
+
+ if (qup_i2c_check_msg_len(&msgs[idx])) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ qup->msg = &msgs[idx];
+ if (msgs[idx].flags & I2C_M_RD)
+ ret = qup_i2c_read_one(qup);
+ else
+ ret = qup_i2c_write_one(qup);
- ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
if (ret)
- goto err;
+ break;
- ret = qup_i2c_wait_for_complete(qup, msg);
+ ret = qup_i2c_change_state(qup, QUP_RESET_STATE);
if (ret)
- goto err;
- } while (qup->pos < msg->len);
+ break;
+ }
- /* Wait for the outstanding data in the fifo to drain */
- ret = qup_i2c_wait_ready(qup, QUP_OUT_NOT_EMPTY, RESET_BIT, ONE_BYTE);
-err:
- disable_irq(qup->irq);
- qup->msg = NULL;
+ if (ret == 0)
+ ret = num;
+out:
+
+ pm_runtime_mark_last_busy(qup->dev);
+ pm_runtime_put_autosuspend(qup->dev);
return ret;
}
-static void qup_i2c_set_read_mode(struct qup_i2c_dev *qup, int len)
+/*
+ * Configure registers related with reconfiguration during run and call it
+ * before each i2c sub transfer.
+ */
+static void qup_i2c_conf_count_v2(struct qup_i2c_dev *qup)
{
- if (len < qup->in_fifo_sz) {
- /* FIFO mode */
- writel(QUP_REPACK_EN, qup->base + QUP_IO_MODE);
- writel(len, qup->base + QUP_MX_READ_CNT);
+ struct qup_i2c_block *blk = &qup->blk;
+ u32 qup_config = I2C_MINI_CORE | I2C_N_VAL_V2;
+
+ if (blk->is_tx_blk_mode)
+ writel(qup->config_run | blk->total_tx_len,
+ qup->base + QUP_MX_OUTPUT_CNT);
+ else
+ writel(qup->config_run | blk->total_tx_len,
+ qup->base + QUP_MX_WRITE_CNT);
+
+ if (blk->total_rx_len) {
+ if (blk->is_rx_blk_mode)
+ writel(qup->config_run | blk->total_rx_len,
+ qup->base + QUP_MX_INPUT_CNT);
+ else
+ writel(qup->config_run | blk->total_rx_len,
+ qup->base + QUP_MX_READ_CNT);
} else {
- /* BLOCK mode (transfer data on chunks) */
- writel(QUP_INPUT_BLK_MODE | QUP_REPACK_EN,
- qup->base + QUP_IO_MODE);
- writel(len, qup->base + QUP_MX_INPUT_CNT);
+ qup_config |= QUP_NO_INPUT;
}
+
+ writel(qup_config, qup->base + QUP_CONFIG);
}
-static void qup_i2c_set_read_mode_v2(struct qup_i2c_dev *qup, int len)
+/*
+ * Configure registers related with transfer mode (FIFO/Block)
+ * before starting of i2c transfer. It will be called only once in
+ * QUP RESET state.
+ */
+static void qup_i2c_conf_mode_v2(struct qup_i2c_dev *qup)
{
- int tx_len = qup->blk.tx_tag_len;
+ struct qup_i2c_block *blk = &qup->blk;
+ u32 io_mode = QUP_REPACK_EN;
- len += qup->blk.rx_tag_len;
- len |= qup->config_run;
- tx_len |= qup->config_run;
+ if (blk->is_tx_blk_mode) {
+ io_mode |= QUP_OUTPUT_BLK_MODE;
+ writel(0, qup->base + QUP_MX_WRITE_CNT);
+ } else {
+ writel(0, qup->base + QUP_MX_OUTPUT_CNT);
+ }
- if (len < qup->in_fifo_sz) {
- /* FIFO mode */
- writel(QUP_REPACK_EN, qup->base + QUP_IO_MODE);
- writel(tx_len, qup->base + QUP_MX_WRITE_CNT);
- writel(len, qup->base + QUP_MX_READ_CNT);
+ if (blk->is_rx_blk_mode) {
+ io_mode |= QUP_INPUT_BLK_MODE;
+ writel(0, qup->base + QUP_MX_READ_CNT);
} else {
- /* BLOCK mode (transfer data on chunks) */
- writel(QUP_INPUT_BLK_MODE | QUP_REPACK_EN,
- qup->base + QUP_IO_MODE);
- writel(tx_len, qup->base + QUP_MX_OUTPUT_CNT);
- writel(len, qup->base + QUP_MX_INPUT_CNT);
+ writel(0, qup->base + QUP_MX_INPUT_CNT);
}
+
+ writel(io_mode, qup->base + QUP_IO_MODE);
}
-static void qup_i2c_issue_read(struct qup_i2c_dev *qup, struct i2c_msg *msg)
+/* Clear required variables before starting of any QUP v2 sub transfer. */
+static void qup_i2c_clear_blk_v2(struct qup_i2c_block *blk)
{
- u32 addr, len, val;
-
- addr = i2c_8bit_addr_from_msg(msg);
-
- /* 0 is used to specify a length 256 (QUP_READ_LIMIT) */
- len = (msg->len == QUP_READ_LIMIT) ? 0 : msg->len;
-
- val = ((QUP_TAG_REC | len) << QUP_MSW_SHIFT) | QUP_TAG_START | addr;
- writel(val, qup->base + QUP_OUT_FIFO_BASE);
+ blk->send_last_word = false;
+ blk->tx_tags_sent = false;
+ blk->tx_fifo_data = 0;
+ blk->tx_fifo_data_pos = 0;
+ blk->tx_fifo_free = 0;
+
+ blk->rx_tags_fetched = false;
+ blk->rx_bytes_read = false;
+ blk->rx_fifo_data = 0;
+ blk->rx_fifo_data_pos = 0;
+ blk->fifo_available = 0;
}
-
-static int qup_i2c_read_fifo(struct qup_i2c_dev *qup, struct i2c_msg *msg)
+/* Receive data from RX FIFO for read message in QUP v2 i2c transfer. */
+static void qup_i2c_recv_data(struct qup_i2c_dev *qup)
{
- u32 val = 0;
- int idx;
- int ret = 0;
+ struct qup_i2c_block *blk = &qup->blk;
+ int j;
- for (idx = 0; qup->pos < msg->len; idx++) {
- if ((idx & 1) == 0) {
- /* Check that FIFO have data */
- ret = qup_i2c_wait_ready(qup, QUP_IN_NOT_EMPTY,
- SET_BIT, 4 * ONE_BYTE);
- if (ret)
- return ret;
+ for (j = blk->rx_fifo_data_pos;
+ blk->cur_blk_len && blk->fifo_available;
+ blk->cur_blk_len--, blk->fifo_available--) {
+ if (j == 0)
+ blk->rx_fifo_data = readl(qup->base + QUP_IN_FIFO_BASE);
- /* Reading 2 words at time */
- val = readl(qup->base + QUP_IN_FIFO_BASE);
+ *(blk->cur_data++) = blk->rx_fifo_data;
+ blk->rx_fifo_data >>= 8;
- msg->buf[qup->pos++] = val & 0xFF;
- } else {
- msg->buf[qup->pos++] = val >> QUP_MSW_SHIFT;
- }
+ if (j == 3)
+ j = 0;
+ else
+ j++;
}
- return ret;
+ blk->rx_fifo_data_pos = j;
}
-static int qup_i2c_read_fifo_v2(struct qup_i2c_dev *qup,
- struct i2c_msg *msg)
+/* Receive tags for read message in QUP v2 i2c transfer. */
+static void qup_i2c_recv_tags(struct qup_i2c_dev *qup)
{
- u32 val;
- int idx, pos = 0, ret = 0, total, msg_offset = 0;
+ struct qup_i2c_block *blk = &qup->blk;
- /*
- * If the message length is already read in
- * the first byte of the buffer, account for
- * that by setting the offset
- */
- if (qup_i2c_check_msg_len(msg) && (msg->len > 1))
- msg_offset = 1;
- total = qup_i2c_get_data_len(qup);
- total -= msg_offset;
-
- /* 2 extra bytes for read tags */
- while (pos < (total + 2)) {
- /* Check that FIFO have data */
- ret = qup_i2c_wait_ready(qup, QUP_IN_NOT_EMPTY,
- SET_BIT, 4 * ONE_BYTE);
- if (ret) {
- dev_err(qup->dev, "timeout for fifo not empty");
- return ret;
- }
- val = readl(qup->base + QUP_IN_FIFO_BASE);
+ blk->rx_fifo_data = readl(qup->base + QUP_IN_FIFO_BASE);
+ blk->rx_fifo_data >>= blk->rx_tag_len * 8;
+ blk->rx_fifo_data_pos = blk->rx_tag_len;
+ blk->fifo_available -= blk->rx_tag_len;
+}
- for (idx = 0; idx < 4; idx++, val >>= 8, pos++) {
- /* first 2 bytes are tag bytes */
- if (pos < 2)
- continue;
+/*
+ * Read the data and tags from RX FIFO. Since in read case, the tags will be
+ * preceded by received data bytes so
+ * 1. Check if rx_tags_fetched is false i.e. the start of QUP block so receive
+ * all tag bytes and discard that.
+ * 2. Read the data from RX FIFO. When all the data bytes have been read then
+ * set rx_bytes_read to true.
+ */
+static void qup_i2c_read_rx_fifo_v2(struct qup_i2c_dev *qup)
+{
+ struct qup_i2c_block *blk = &qup->blk;
- if (pos >= (total + 2))
- goto out;
- msg->buf[qup->pos + msg_offset] = val & 0xff;
- qup->pos++;
- }
+ if (!blk->rx_tags_fetched) {
+ qup_i2c_recv_tags(qup);
+ blk->rx_tags_fetched = true;
}
-out:
- qup->blk.data_len -= total;
-
- return ret;
+ qup_i2c_recv_data(qup);
+ if (!blk->cur_blk_len)
+ blk->rx_bytes_read = true;
}
-static int qup_i2c_read_one_v2(struct qup_i2c_dev *qup, struct i2c_msg *msg)
+/*
+ * Write bytes in TX FIFO for write message in QUP v2 i2c transfer. QUP TX FIFO
+ * write works on word basis (4 bytes). Append new data byte write for TX FIFO
+ * in tx_fifo_data and write to TX FIFO when all the 4 bytes are present.
+ */
+static void
+qup_i2c_write_blk_data(struct qup_i2c_dev *qup, u8 **data, unsigned int *len)
{
- int ret = 0;
+ struct qup_i2c_block *blk = &qup->blk;
+ unsigned int j;
+
+ for (j = blk->tx_fifo_data_pos; *len && blk->tx_fifo_free;
+ (*len)--, blk->tx_fifo_free--) {
+ blk->tx_fifo_data |= *(*data)++ << (j * 8);
+ if (j == 3) {
+ writel(blk->tx_fifo_data,
+ qup->base + QUP_OUT_FIFO_BASE);
+ blk->tx_fifo_data = 0x0;
+ j = 0;
+ } else {
+ j++;
+ }
+ }
- qup->msg = msg;
- qup->pos = 0;
- enable_irq(qup->irq);
- qup_i2c_set_blk_data(qup, msg);
- qup_i2c_set_read_mode_v2(qup, msg->len);
+ blk->tx_fifo_data_pos = j;
+}
- ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
- if (ret)
- goto err;
+/* Transfer tags for read message in QUP v2 i2c transfer. */
+static void qup_i2c_write_rx_tags_v2(struct qup_i2c_dev *qup)
+{
+ struct qup_i2c_block *blk = &qup->blk;
- writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
+ qup_i2c_write_blk_data(qup, &blk->cur_tx_tags, &blk->tx_tag_len);
+ if (blk->tx_fifo_data_pos)
+ writel(blk->tx_fifo_data, qup->base + QUP_OUT_FIFO_BASE);
+}
- do {
- ret = qup_i2c_issue_xfer_v2(qup, msg);
- if (ret)
- goto err;
+/*
+ * Write the data and tags in TX FIFO. Since in write case, both tags and data
+ * need to be written and QUP write tags can have maximum 256 data length, so
+ *
+ * 1. Check if tx_tags_sent is false i.e. the start of QUP block so write the
+ * tags to TX FIFO and set tx_tags_sent to true.
+ * 2. Check if send_last_word is true. It will be set when last few data bytes
+ * (less than 4 bytes) are reamining to be written in FIFO because of no FIFO
+ * space. All this data bytes are available in tx_fifo_data so write this
+ * in FIFO.
+ * 3. Write the data to TX FIFO and check for cur_blk_len. If it is non zero
+ * then more data is pending otherwise following 3 cases can be possible
+ * a. if tx_fifo_data_pos is zero i.e. all the data bytes in this block
+ * have been written in TX FIFO so nothing else is required.
+ * b. tx_fifo_free is non zero i.e tx FIFO is free so copy the remaining data
+ * from tx_fifo_data to tx FIFO. Since, qup_i2c_write_blk_data do write
+ * in 4 bytes and FIFO space is in multiple of 4 bytes so tx_fifo_free
+ * will be always greater than or equal to 4 bytes.
+ * c. tx_fifo_free is zero. In this case, last few bytes (less than 4
+ * bytes) are copied to tx_fifo_data but couldn't be sent because of
+ * FIFO full so make send_last_word true.
+ */
+static void qup_i2c_write_tx_fifo_v2(struct qup_i2c_dev *qup)
+{
+ struct qup_i2c_block *blk = &qup->blk;
- ret = qup_i2c_wait_for_complete(qup, msg);
- if (ret)
- goto err;
+ if (!blk->tx_tags_sent) {
+ qup_i2c_write_blk_data(qup, &blk->cur_tx_tags,
+ &blk->tx_tag_len);
+ blk->tx_tags_sent = true;
+ }
- ret = qup_i2c_read_fifo_v2(qup, msg);
- if (ret)
- goto err;
+ if (blk->send_last_word)
+ goto send_last_word;
- qup->blk.pos++;
+ qup_i2c_write_blk_data(qup, &blk->cur_data, &blk->cur_blk_len);
+ if (!blk->cur_blk_len) {
+ if (!blk->tx_fifo_data_pos)
+ return;
- /* Handle SMBus block read length */
- if (qup_i2c_check_msg_len(msg) && (msg->len == 1)) {
- if (msg->buf[0] > I2C_SMBUS_BLOCK_MAX) {
- ret = -EPROTO;
- goto err;
- }
- msg->len += msg->buf[0];
- qup->pos = 0;
- qup_i2c_set_blk_data(qup, msg);
- /* set tag length for block read */
- qup->blk.tx_tag_len = 2;
- qup_i2c_set_read_mode_v2(qup, msg->buf[0]);
- }
- } while (qup->blk.pos < qup->blk.count);
+ if (blk->tx_fifo_free)
+ goto send_last_word;
-err:
- disable_irq(qup->irq);
- qup->msg = NULL;
+ blk->send_last_word = true;
+ }
- return ret;
+ return;
+
+send_last_word:
+ writel(blk->tx_fifo_data, qup->base + QUP_OUT_FIFO_BASE);
}
-static int qup_i2c_read_one(struct qup_i2c_dev *qup, struct i2c_msg *msg)
+/*
+ * Main transfer function which read or write i2c data.
+ * The QUP v2 supports reconfiguration during run in which multiple i2c sub
+ * transfers can be scheduled.
+ */
+static int
+qup_i2c_conf_xfer_v2(struct qup_i2c_dev *qup, bool is_rx, bool is_first,
+ bool change_pause_state)
{
+ struct qup_i2c_block *blk = &qup->blk;
+ struct i2c_msg *msg = qup->msg;
int ret;
- qup->msg = msg;
- qup->pos = 0;
+ /*
+ * Check if its SMBus Block read for which the top level read will be
+ * done into 2 QUP reads. One with message length 1 while other one is
+ * with actual length.
+ */
+ if (qup_i2c_check_msg_len(msg)) {
+ if (qup->is_smbus_read) {
+ /*
+ * If the message length is already read in
+ * the first byte of the buffer, account for
+ * that by setting the offset
+ */
+ blk->cur_data += 1;
+ is_first = false;
+ } else {
+ change_pause_state = false;
+ }
+ }
- enable_irq(qup->irq);
- qup_i2c_set_read_mode(qup, msg->len);
+ qup->config_run = is_first ? 0 : QUP_I2C_MX_CONFIG_DURING_RUN;
- ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
- if (ret)
- goto err;
+ qup_i2c_clear_blk_v2(blk);
+ qup_i2c_conf_count_v2(qup);
- writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
+ /* If it is first sub transfer, then configure i2c bus clocks */
+ if (is_first) {
+ ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
+ if (ret)
+ return ret;
- ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
- if (ret)
- goto err;
+ writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
- qup_i2c_issue_read(qup, msg);
+ ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
+ if (ret)
+ return ret;
+ }
+
+ reinit_completion(&qup->xfer);
+ enable_irq(qup->irq);
+ /*
+ * In FIFO mode, tx FIFO can be written directly while in block mode the
+ * it will be written after getting OUT_BLOCK_WRITE_REQ interrupt
+ */
+ if (!blk->is_tx_blk_mode) {
+ blk->tx_fifo_free = qup->out_fifo_sz;
+
+ if (is_rx)
+ qup_i2c_write_rx_tags_v2(qup);
+ else
+ qup_i2c_write_tx_fifo_v2(qup);
+ }
ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
if (ret)
goto err;
- do {
- ret = qup_i2c_wait_for_complete(qup, msg);
- if (ret)
- goto err;
+ ret = qup_i2c_wait_for_complete(qup, msg);
+ if (ret)
+ goto err;
- ret = qup_i2c_read_fifo(qup, msg);
+ /* Move to pause state for all the transfers, except last one */
+ if (change_pause_state) {
+ ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
if (ret)
goto err;
- } while (qup->pos < msg->len);
+ }
err:
disable_irq(qup->irq);
- qup->msg = NULL;
-
return ret;
}
-static int qup_i2c_xfer(struct i2c_adapter *adap,
- struct i2c_msg msgs[],
- int num)
+/*
+ * Transfer one read/write message in i2c transfer. It splits the message into
+ * multiple of blk_xfer_limit data length blocks and schedule each
+ * QUP block individually.
+ */
+static int qup_i2c_xfer_v2_msg(struct qup_i2c_dev *qup, int msg_id, bool is_rx)
{
- struct qup_i2c_dev *qup = i2c_get_adapdata(adap);
- int ret, idx;
+ int ret = 0;
+ unsigned int data_len, i;
+ struct i2c_msg *msg = qup->msg;
+ struct qup_i2c_block *blk = &qup->blk;
+ u8 *msg_buf = msg->buf;
- ret = pm_runtime_get_sync(qup->dev);
- if (ret < 0)
- goto out;
+ qup->blk_xfer_limit = is_rx ? RECV_MAX_DATA_LEN : QUP_READ_LIMIT;
+ qup_i2c_set_blk_data(qup, msg);
- qup->bus_err = 0;
- qup->qup_err = 0;
+ for (i = 0; i < blk->count; i++) {
+ data_len = qup_i2c_get_data_len(qup);
+ blk->pos = i;
+ blk->cur_tx_tags = blk->tags;
+ blk->cur_blk_len = data_len;
+ blk->tx_tag_len =
+ qup_i2c_set_tags(blk->cur_tx_tags, qup, qup->msg);
- writel(1, qup->base + QUP_SW_RESET);
- ret = qup_i2c_poll_state(qup, QUP_RESET_STATE);
- if (ret)
- goto out;
+ blk->cur_data = msg_buf;
- /* Configure QUP as I2C mini core */
- writel(I2C_MINI_CORE | I2C_N_VAL, qup->base + QUP_CONFIG);
-
- for (idx = 0; idx < num; idx++) {
- if (msgs[idx].len == 0) {
- ret = -EINVAL;
- goto out;
+ if (is_rx) {
+ blk->total_tx_len = blk->tx_tag_len;
+ blk->rx_tag_len = 2;
+ blk->total_rx_len = blk->rx_tag_len + data_len;
+ } else {
+ blk->total_tx_len = blk->tx_tag_len + data_len;
+ blk->total_rx_len = 0;
}
- if (qup_i2c_poll_state_i2c_master(qup)) {
- ret = -EIO;
- goto out;
- }
+ ret = qup_i2c_conf_xfer_v2(qup, is_rx, !msg_id && !i,
+ !qup->is_last || i < blk->count - 1);
+ if (ret)
+ return ret;
- if (qup_i2c_check_msg_len(&msgs[idx])) {
- ret = -EINVAL;
- goto out;
+ /* Handle SMBus block read length */
+ if (qup_i2c_check_msg_len(msg) && msg->len == 1 &&
+ !qup->is_smbus_read) {
+ if (msg->buf[0] > I2C_SMBUS_BLOCK_MAX)
+ return -EPROTO;
+
+ msg->len = msg->buf[0];
+ qup->is_smbus_read = true;
+ ret = qup_i2c_xfer_v2_msg(qup, msg_id, true);
+ qup->is_smbus_read = false;
+ if (ret)
+ return ret;
+
+ msg->len += 1;
}
+ msg_buf += data_len;
+ blk->data_len -= qup->blk_xfer_limit;
+ }
+
+ return ret;
+}
+
+/*
+ * QUP v2 supports 3 modes
+ * Programmed IO using FIFO mode : Less than FIFO size
+ * Programmed IO using Block mode : Greater than FIFO size
+ * DMA using BAM : Appropriate for any transaction size but the address should
+ * be DMA applicable
+ *
+ * This function determines the mode which will be used for this transfer. An
+ * i2c transfer contains multiple message. Following are the rules to determine
+ * the mode used.
+ * 1. Determine complete length, maximum tx and rx length for complete transfer.
+ * 2. If complete transfer length is greater than fifo size then use the DMA
+ * mode.
+ * 3. In FIFO or block mode, tx and rx can operate in different mode so check
+ * for maximum tx and rx length to determine mode.
+ */
+static int
+qup_i2c_determine_mode_v2(struct qup_i2c_dev *qup,
+ struct i2c_msg msgs[], int num)
+{
+ int idx;
+ bool no_dma = false;
+ unsigned int max_tx_len = 0, max_rx_len = 0, total_len = 0;
+
+ /* All i2c_msgs should be transferred using either dma or cpu */
+ for (idx = 0; idx < num; idx++) {
+ if (msgs[idx].len == 0)
+ return -EINVAL;
+
if (msgs[idx].flags & I2C_M_RD)
- ret = qup_i2c_read_one(qup, &msgs[idx]);
+ max_rx_len = max_t(unsigned int, max_rx_len,
+ msgs[idx].len);
else
- ret = qup_i2c_write_one(qup, &msgs[idx]);
+ max_tx_len = max_t(unsigned int, max_tx_len,
+ msgs[idx].len);
- if (ret)
- break;
+ if (is_vmalloc_addr(msgs[idx].buf))
+ no_dma = true;
- ret = qup_i2c_change_state(qup, QUP_RESET_STATE);
- if (ret)
- break;
+ total_len += msgs[idx].len;
}
- if (ret == 0)
- ret = num;
-out:
-
- pm_runtime_mark_last_busy(qup->dev);
- pm_runtime_put_autosuspend(qup->dev);
+ if (!no_dma && qup->is_dma &&
+ (total_len > qup->out_fifo_sz || total_len > qup->in_fifo_sz)) {
+ qup->use_dma = true;
+ } else {
+ qup->blk.is_tx_blk_mode = max_tx_len > qup->out_fifo_sz -
+ QUP_MAX_TAGS_LEN ? true : false;
+ qup->blk.is_rx_blk_mode = max_rx_len > qup->in_fifo_sz -
+ READ_RX_TAGS_LEN ? true : false;
+ }
- return ret;
+ return 0;
}
static int qup_i2c_xfer_v2(struct i2c_adapter *adap,
@@ -1295,7 +1545,7 @@ static int qup_i2c_xfer_v2(struct i2c_adapter *adap,
int num)
{
struct qup_i2c_dev *qup = i2c_get_adapdata(adap);
- int ret, len, idx = 0, use_dma = 0;
+ int ret, idx = 0;
qup->bus_err = 0;
qup->qup_err = 0;
@@ -1304,6 +1554,10 @@ static int qup_i2c_xfer_v2(struct i2c_adapter *adap,
if (ret < 0)
goto out;
+ ret = qup_i2c_determine_mode_v2(qup, msgs, num);
+ if (ret)
+ goto out;
+
writel(1, qup->base + QUP_SW_RESET);
ret = qup_i2c_poll_state(qup, QUP_RESET_STATE);
if (ret)
@@ -1313,59 +1567,35 @@ static int qup_i2c_xfer_v2(struct i2c_adapter *adap,
writel(I2C_MINI_CORE | I2C_N_VAL_V2, qup->base + QUP_CONFIG);
writel(QUP_V2_TAGS_EN, qup->base + QUP_I2C_MASTER_GEN);
- if ((qup->is_dma)) {
- /* All i2c_msgs should be transferred using either dma or cpu */
- for (idx = 0; idx < num; idx++) {
- if (msgs[idx].len == 0) {
- ret = -EINVAL;
- goto out;
- }
-
- len = (msgs[idx].len > qup->out_fifo_sz) ||
- (msgs[idx].len > qup->in_fifo_sz);
-
- if ((!is_vmalloc_addr(msgs[idx].buf)) && len) {
- use_dma = 1;
- } else {
- use_dma = 0;
- break;
- }
- }
+ if (qup_i2c_poll_state_i2c_master(qup)) {
+ ret = -EIO;
+ goto out;
}
- idx = 0;
+ if (qup->use_dma) {
+ reinit_completion(&qup->xfer);
+ ret = qup_i2c_bam_xfer(adap, &msgs[0], num);
+ qup->use_dma = false;
+ } else {
+ qup_i2c_conf_mode_v2(qup);
- do {
- if (msgs[idx].len == 0) {
- ret = -EINVAL;
- goto out;
- }
+ for (idx = 0; idx < num; idx++) {
+ qup->msg = &msgs[idx];
+ qup->is_last = idx == (num - 1);
- if (qup_i2c_poll_state_i2c_master(qup)) {
- ret = -EIO;
- goto out;
+ ret = qup_i2c_xfer_v2_msg(qup, idx,
+ !!(msgs[idx].flags & I2C_M_RD));
+ if (ret)
+ break;
}
+ qup->msg = NULL;
+ }
- qup->is_last = (idx == (num - 1));
- if (idx)
- qup->config_run = QUP_I2C_MX_CONFIG_DURING_RUN;
- else
- qup->config_run = 0;
-
- reinit_completion(&qup->xfer);
-
- if (use_dma) {
- ret = qup_i2c_bam_xfer(adap, &msgs[idx], num);
- } else {
- if (msgs[idx].flags & I2C_M_RD)
- ret = qup_i2c_read_one_v2(qup, &msgs[idx]);
- else
- ret = qup_i2c_write_one_v2(qup, &msgs[idx]);
- }
- } while ((idx++ < (num - 1)) && !use_dma && !ret);
+ if (!ret)
+ ret = qup_i2c_bus_active(qup, ONE_BYTE);
if (!ret)
- ret = qup_i2c_change_state(qup, QUP_RESET_STATE);
+ qup_i2c_change_state(qup, QUP_RESET_STATE);
if (ret == 0)
ret = num;
@@ -1429,6 +1659,7 @@ static int qup_i2c_probe(struct platform_device *pdev)
u32 src_clk_freq = DEFAULT_SRC_CLK;
u32 clk_freq = DEFAULT_CLK_FREQ;
int blocks;
+ bool is_qup_v1;
qup = devm_kzalloc(&pdev->dev, sizeof(*qup), GFP_KERNEL);
if (!qup)
@@ -1447,8 +1678,10 @@ static int qup_i2c_probe(struct platform_device *pdev)
if (of_device_is_compatible(pdev->dev.of_node, "qcom,i2c-qup-v1.1.1")) {
qup->adap.algo = &qup_i2c_algo;
qup->adap.quirks = &qup_i2c_quirks;
+ is_qup_v1 = true;
} else {
qup->adap.algo = &qup_i2c_algo_v2;
+ is_qup_v1 = false;
ret = qup_i2c_req_dma(qup);
if (ret == -EPROBE_DEFER)
@@ -1456,7 +1689,8 @@ static int qup_i2c_probe(struct platform_device *pdev)
else if (ret != 0)
goto nodma;
- blocks = (MX_BLOCKS << 1) + 1;
+ qup->max_xfer_sg_len = (MX_BLOCKS << 1);
+ blocks = (MX_DMA_BLOCKS << 1) + 1;
qup->btx.sg = devm_kzalloc(&pdev->dev,
sizeof(*qup->btx.sg) * blocks,
GFP_KERNEL);
@@ -1573,14 +1807,31 @@ nodma:
ret = -EIO;
goto fail;
}
- qup->out_blk_sz = blk_sizes[size] / 2;
+ qup->out_blk_sz = blk_sizes[size];
size = QUP_INPUT_BLOCK_SIZE(io_mode);
if (size >= ARRAY_SIZE(blk_sizes)) {
ret = -EIO;
goto fail;
}
- qup->in_blk_sz = blk_sizes[size] / 2;
+ qup->in_blk_sz = blk_sizes[size];
+
+ if (is_qup_v1) {
+ /*
+ * in QUP v1, QUP_CONFIG uses N as 15 i.e 16 bits constitutes a
+ * single transfer but the block size is in bytes so divide the
+ * in_blk_sz and out_blk_sz by 2
+ */
+ qup->in_blk_sz /= 2;
+ qup->out_blk_sz /= 2;
+ qup->write_tx_fifo = qup_i2c_write_tx_fifo_v1;
+ qup->read_rx_fifo = qup_i2c_read_rx_fifo_v1;
+ qup->write_rx_tags = qup_i2c_write_rx_tags_v1;
+ } else {
+ qup->write_tx_fifo = qup_i2c_write_tx_fifo_v2;
+ qup->read_rx_fifo = qup_i2c_read_rx_fifo_v2;
+ qup->write_rx_tags = qup_i2c_write_rx_tags_v2;
+ }
size = QUP_OUTPUT_FIFO_SIZE(io_mode);
qup->out_fifo_sz = qup->out_blk_sz * (2 << size);
@@ -1598,6 +1849,8 @@ nodma:
*/
one_bit_t = (USEC_PER_SEC / clk_freq) + 1;
qup->one_byte_t = one_bit_t * 9;
+ qup->xfer_timeout = TOUT_MIN * HZ +
+ usecs_to_jiffies(MX_DMA_TX_RX_LEN * qup->one_byte_t);
dev_dbg(qup->dev, "IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n",
qup->in_blk_sz, qup->in_fifo_sz,
diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c
index 4159ebcec2bb..c6915b835396 100644
--- a/drivers/i2c/busses/i2c-rcar.c
+++ b/drivers/i2c/busses/i2c-rcar.c
@@ -102,8 +102,8 @@
#define RCAR_IRQ_RECV (MNR | MAL | MST | MAT | MDR)
#define RCAR_IRQ_STOP (MST)
-#define RCAR_IRQ_ACK_SEND (~(MAT | MDE) & 0xFF)
-#define RCAR_IRQ_ACK_RECV (~(MAT | MDR) & 0xFF)
+#define RCAR_IRQ_ACK_SEND (~(MAT | MDE) & 0x7F)
+#define RCAR_IRQ_ACK_RECV (~(MAT | MDR) & 0x7F)
#define ID_LAST_MSG (1 << 0)
#define ID_FIRST_MSG (1 << 1)
diff --git a/drivers/i2c/busses/i2c-scmi.c b/drivers/i2c/busses/i2c-scmi.c
index 7aa7b9cb6203..a01389b85f13 100644
--- a/drivers/i2c/busses/i2c-scmi.c
+++ b/drivers/i2c/busses/i2c-scmi.c
@@ -182,7 +182,8 @@ acpi_smbus_cmi_access(struct i2c_adapter *adap, u16 addr, unsigned short flags,
status = acpi_evaluate_object(smbus_cmi->handle, method, &input,
&buffer);
if (ACPI_FAILURE(status)) {
- ACPI_ERROR((AE_INFO, "Evaluating %s: %i", method, status));
+ acpi_handle_err(smbus_cmi->handle,
+ "Failed to evaluate %s: %i\n", method, status);
return -EIO;
}
@@ -190,19 +191,19 @@ acpi_smbus_cmi_access(struct i2c_adapter *adap, u16 addr, unsigned short flags,
if (pkg && pkg->type == ACPI_TYPE_PACKAGE)
obj = pkg->package.elements;
else {
- ACPI_ERROR((AE_INFO, "Invalid argument type"));
+ acpi_handle_err(smbus_cmi->handle, "Invalid argument type\n");
result = -EIO;
goto out;
}
if (obj == NULL || obj->type != ACPI_TYPE_INTEGER) {
- ACPI_ERROR((AE_INFO, "Invalid argument type"));
+ acpi_handle_err(smbus_cmi->handle, "Invalid argument type\n");
result = -EIO;
goto out;
}
result = obj->integer.value;
- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s return status: %i\n",
- method, result));
+ acpi_handle_debug(smbus_cmi->handle, "%s return status: %i\n", method,
+ result);
switch (result) {
case ACPI_SMBUS_STATUS_OK:
@@ -227,7 +228,7 @@ acpi_smbus_cmi_access(struct i2c_adapter *adap, u16 addr, unsigned short flags,
obj = pkg->package.elements + 1;
if (obj->type != ACPI_TYPE_INTEGER) {
- ACPI_ERROR((AE_INFO, "Invalid argument type"));
+ acpi_handle_err(smbus_cmi->handle, "Invalid argument type\n");
result = -EIO;
goto out;
}
@@ -239,7 +240,8 @@ acpi_smbus_cmi_access(struct i2c_adapter *adap, u16 addr, unsigned short flags,
case I2C_SMBUS_BYTE_DATA:
case I2C_SMBUS_WORD_DATA:
if (obj->type != ACPI_TYPE_INTEGER) {
- ACPI_ERROR((AE_INFO, "Invalid argument type"));
+ acpi_handle_err(smbus_cmi->handle,
+ "Invalid argument type\n");
result = -EIO;
goto out;
}
@@ -250,7 +252,8 @@ acpi_smbus_cmi_access(struct i2c_adapter *adap, u16 addr, unsigned short flags,
break;
case I2C_SMBUS_BLOCK_DATA:
if (obj->type != ACPI_TYPE_BUFFER) {
- ACPI_ERROR((AE_INFO, "Invalid argument type"));
+ acpi_handle_err(smbus_cmi->handle,
+ "Invalid argument type\n");
result = -EIO;
goto out;
}
@@ -300,6 +303,7 @@ static int acpi_smbus_cmi_add_cap(struct acpi_smbus_cmi *smbus_cmi,
const char *name)
{
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_handle *handle = smbus_cmi->handle;
union acpi_object *obj;
acpi_status status;
@@ -308,8 +312,8 @@ static int acpi_smbus_cmi_add_cap(struct acpi_smbus_cmi *smbus_cmi,
smbus_cmi->methods->mt_info,
NULL, &buffer);
if (ACPI_FAILURE(status)) {
- ACPI_ERROR((AE_INFO, "Evaluating %s: %i",
- smbus_cmi->methods->mt_info, status));
+ acpi_handle_err(handle, "Failed to evaluate %s: %i\n",
+ smbus_cmi->methods->mt_info, status);
return -EIO;
}
@@ -317,18 +321,18 @@ static int acpi_smbus_cmi_add_cap(struct acpi_smbus_cmi *smbus_cmi,
if (obj && obj->type == ACPI_TYPE_PACKAGE)
obj = obj->package.elements;
else {
- ACPI_ERROR((AE_INFO, "Invalid argument type"));
+ acpi_handle_err(handle, "Invalid argument type\n");
kfree(buffer.pointer);
return -EIO;
}
if (obj->type != ACPI_TYPE_INTEGER) {
- ACPI_ERROR((AE_INFO, "Invalid argument type"));
+ acpi_handle_err(handle, "Invalid argument type\n");
kfree(buffer.pointer);
return -EIO;
} else
- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "SMBus CMI Version %x"
- "\n", (int)obj->integer.value));
+ acpi_handle_debug(handle, "SMBus CMI Version %x\n",
+ (int)obj->integer.value);
kfree(buffer.pointer);
smbus_cmi->cap_info = 1;
@@ -337,8 +341,7 @@ static int acpi_smbus_cmi_add_cap(struct acpi_smbus_cmi *smbus_cmi,
else if (!strcmp(name, smbus_cmi->methods->mt_sbw))
smbus_cmi->cap_write = 1;
else
- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Unsupported CMI method: %s\n",
- name));
+ acpi_handle_debug(handle, "Unsupported CMI method: %s\n", name);
return 0;
}
diff --git a/drivers/i2c/busses/i2c-sprd.c b/drivers/i2c/busses/i2c-sprd.c
index 25fcc3c1e32b..4053259bccb8 100644
--- a/drivers/i2c/busses/i2c-sprd.c
+++ b/drivers/i2c/busses/i2c-sprd.c
@@ -86,6 +86,7 @@ struct sprd_i2c {
u32 count;
int irq;
int err;
+ bool is_suspended;
};
static void sprd_i2c_set_count(struct sprd_i2c *i2c_dev, u32 count)
@@ -283,6 +284,9 @@ static int sprd_i2c_master_xfer(struct i2c_adapter *i2c_adap,
struct sprd_i2c *i2c_dev = i2c_adap->algo_data;
int im, ret;
+ if (i2c_dev->is_suspended)
+ return -EBUSY;
+
ret = pm_runtime_get_sync(i2c_dev->dev);
if (ret < 0)
return ret;
@@ -364,13 +368,12 @@ static irqreturn_t sprd_i2c_isr_thread(int irq, void *dev_id)
struct sprd_i2c *i2c_dev = dev_id;
struct i2c_msg *msg = i2c_dev->msg;
bool ack = !(readl(i2c_dev->base + I2C_STATUS) & I2C_RX_ACK);
- u32 i2c_count = readl(i2c_dev->base + I2C_COUNT);
u32 i2c_tran;
if (msg->flags & I2C_M_RD)
i2c_tran = i2c_dev->count >= I2C_FIFO_FULL_THLD;
else
- i2c_tran = i2c_count;
+ i2c_tran = i2c_dev->count;
/*
* If we got one ACK from slave when writing data, and we did not
@@ -408,14 +411,13 @@ static irqreturn_t sprd_i2c_isr(int irq, void *dev_id)
{
struct sprd_i2c *i2c_dev = dev_id;
struct i2c_msg *msg = i2c_dev->msg;
- u32 i2c_count = readl(i2c_dev->base + I2C_COUNT);
bool ack = !(readl(i2c_dev->base + I2C_STATUS) & I2C_RX_ACK);
u32 i2c_tran;
if (msg->flags & I2C_M_RD)
i2c_tran = i2c_dev->count >= I2C_FIFO_FULL_THLD;
else
- i2c_tran = i2c_count;
+ i2c_tran = i2c_dev->count;
/*
* If we did not get one ACK from slave when writing data, then we
@@ -586,11 +588,23 @@ static int sprd_i2c_remove(struct platform_device *pdev)
static int __maybe_unused sprd_i2c_suspend_noirq(struct device *pdev)
{
+ struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev);
+
+ i2c_lock_adapter(&i2c_dev->adap);
+ i2c_dev->is_suspended = true;
+ i2c_unlock_adapter(&i2c_dev->adap);
+
return pm_runtime_force_suspend(pdev);
}
static int __maybe_unused sprd_i2c_resume_noirq(struct device *pdev)
{
+ struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev);
+
+ i2c_lock_adapter(&i2c_dev->adap);
+ i2c_dev->is_suspended = false;
+ i2c_unlock_adapter(&i2c_dev->adap);
+
return pm_runtime_force_resume(pdev);
}
diff --git a/drivers/i2c/busses/i2c-stm32f4.c b/drivers/i2c/busses/i2c-stm32f4.c
index 47c8d00de53f..ba600d77a3f8 100644
--- a/drivers/i2c/busses/i2c-stm32f4.c
+++ b/drivers/i2c/busses/i2c-stm32f4.c
@@ -349,7 +349,7 @@ static void stm32f4_i2c_read_msg(struct stm32f4_i2c_dev *i2c_dev)
static void stm32f4_i2c_terminate_xfer(struct stm32f4_i2c_dev *i2c_dev)
{
struct stm32f4_i2c_msg *msg = &i2c_dev->msg;
- void __iomem *reg = i2c_dev->base + STM32F4_I2C_CR2;
+ void __iomem *reg;
stm32f4_i2c_disable_irq(i2c_dev);
diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c
index b445b3bb0bb1..f273e28c39db 100644
--- a/drivers/i2c/busses/i2c-stm32f7.c
+++ b/drivers/i2c/busses/i2c-stm32f7.c
@@ -888,6 +888,11 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
}
setup = of_device_get_match_data(&pdev->dev);
+ if (!setup) {
+ dev_err(&pdev->dev, "Can't get device data\n");
+ ret = -ENODEV;
+ goto clk_free;
+ }
i2c_dev->setup = *setup;
ret = device_property_read_u32(i2c_dev->dev, "i2c-scl-rising-time-ns",
diff --git a/drivers/i2c/busses/i2c-synquacer.c b/drivers/i2c/busses/i2c-synquacer.c
new file mode 100644
index 000000000000..a021f866d8c2
--- /dev/null
+++ b/drivers/i2c/busses/i2c-synquacer.c
@@ -0,0 +1,667 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2012 FUJITSU SEMICONDUCTOR LIMITED
+ */
+
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#define WAIT_PCLK(n, rate) \
+ ndelay(DIV_ROUND_UP(DIV_ROUND_UP(1000000000, rate), n) + 10)
+
+/* I2C register address definitions */
+#define SYNQUACER_I2C_REG_BSR (0x00 << 2) // Bus Status
+#define SYNQUACER_I2C_REG_BCR (0x01 << 2) // Bus Control
+#define SYNQUACER_I2C_REG_CCR (0x02 << 2) // Clock Control
+#define SYNQUACER_I2C_REG_ADR (0x03 << 2) // Address
+#define SYNQUACER_I2C_REG_DAR (0x04 << 2) // Data
+#define SYNQUACER_I2C_REG_CSR (0x05 << 2) // Expansion CS
+#define SYNQUACER_I2C_REG_FSR (0x06 << 2) // Bus Clock Freq
+#define SYNQUACER_I2C_REG_BC2R (0x07 << 2) // Bus Control 2
+
+/* I2C register bit definitions */
+#define SYNQUACER_I2C_BSR_FBT BIT(0) // First Byte Transfer
+#define SYNQUACER_I2C_BSR_GCA BIT(1) // General Call Address
+#define SYNQUACER_I2C_BSR_AAS BIT(2) // Address as Slave
+#define SYNQUACER_I2C_BSR_TRX BIT(3) // Transfer/Receive
+#define SYNQUACER_I2C_BSR_LRB BIT(4) // Last Received Bit
+#define SYNQUACER_I2C_BSR_AL BIT(5) // Arbitration Lost
+#define SYNQUACER_I2C_BSR_RSC BIT(6) // Repeated Start Cond.
+#define SYNQUACER_I2C_BSR_BB BIT(7) // Bus Busy
+
+#define SYNQUACER_I2C_BCR_INT BIT(0) // Interrupt
+#define SYNQUACER_I2C_BCR_INTE BIT(1) // Interrupt Enable
+#define SYNQUACER_I2C_BCR_GCAA BIT(2) // Gen. Call Access Ack.
+#define SYNQUACER_I2C_BCR_ACK BIT(3) // Acknowledge
+#define SYNQUACER_I2C_BCR_MSS BIT(4) // Master Slave Select
+#define SYNQUACER_I2C_BCR_SCC BIT(5) // Start Condition Cont.
+#define SYNQUACER_I2C_BCR_BEIE BIT(6) // Bus Error Int Enable
+#define SYNQUACER_I2C_BCR_BER BIT(7) // Bus Error
+
+#define SYNQUACER_I2C_CCR_CS_MASK (0x1f) // CCR Clock Period Sel.
+#define SYNQUACER_I2C_CCR_EN BIT(5) // Enable
+#define SYNQUACER_I2C_CCR_FM BIT(6) // Speed Mode Select
+
+#define SYNQUACER_I2C_CSR_CS_MASK (0x3f) // CSR Clock Period Sel.
+
+#define SYNQUACER_I2C_BC2R_SCLL BIT(0) // SCL Low Drive
+#define SYNQUACER_I2C_BC2R_SDAL BIT(1) // SDA Low Drive
+#define SYNQUACER_I2C_BC2R_SCLS BIT(4) // SCL Status
+#define SYNQUACER_I2C_BC2R_SDAS BIT(5) // SDA Status
+
+/* PCLK frequency */
+#define SYNQUACER_I2C_BUS_CLK_FR(rate) (((rate) / 20000000) + 1)
+
+/* STANDARD MODE frequency */
+#define SYNQUACER_I2C_CLK_MASTER_STD(rate) \
+ DIV_ROUND_UP(DIV_ROUND_UP((rate), 100000) - 2, 2)
+/* FAST MODE frequency */
+#define SYNQUACER_I2C_CLK_MASTER_FAST(rate) \
+ DIV_ROUND_UP((DIV_ROUND_UP((rate), 400000) - 2) * 2, 3)
+
+/* (clkrate <= 18000000) */
+/* calculate the value of CS bits in CCR register on standard mode */
+#define SYNQUACER_I2C_CCR_CS_STD_MAX_18M(rate) \
+ ((SYNQUACER_I2C_CLK_MASTER_STD(rate) - 65) \
+ & SYNQUACER_I2C_CCR_CS_MASK)
+
+/* calculate the value of CS bits in CSR register on standard mode */
+#define SYNQUACER_I2C_CSR_CS_STD_MAX_18M(rate) 0x00
+
+/* calculate the value of CS bits in CCR register on fast mode */
+#define SYNQUACER_I2C_CCR_CS_FAST_MAX_18M(rate) \
+ ((SYNQUACER_I2C_CLK_MASTER_FAST(rate) - 1) \
+ & SYNQUACER_I2C_CCR_CS_MASK)
+
+/* calculate the value of CS bits in CSR register on fast mode */
+#define SYNQUACER_I2C_CSR_CS_FAST_MAX_18M(rate) 0x00
+
+/* (clkrate > 18000000) */
+/* calculate the value of CS bits in CCR register on standard mode */
+#define SYNQUACER_I2C_CCR_CS_STD_MIN_18M(rate) \
+ ((SYNQUACER_I2C_CLK_MASTER_STD(rate) - 1) \
+ & SYNQUACER_I2C_CCR_CS_MASK)
+
+/* calculate the value of CS bits in CSR register on standard mode */
+#define SYNQUACER_I2C_CSR_CS_STD_MIN_18M(rate) \
+ (((SYNQUACER_I2C_CLK_MASTER_STD(rate) - 1) >> 5) \
+ & SYNQUACER_I2C_CSR_CS_MASK)
+
+/* calculate the value of CS bits in CCR register on fast mode */
+#define SYNQUACER_I2C_CCR_CS_FAST_MIN_18M(rate) \
+ ((SYNQUACER_I2C_CLK_MASTER_FAST(rate) - 1) \
+ & SYNQUACER_I2C_CCR_CS_MASK)
+
+/* calculate the value of CS bits in CSR register on fast mode */
+#define SYNQUACER_I2C_CSR_CS_FAST_MIN_18M(rate) \
+ (((SYNQUACER_I2C_CLK_MASTER_FAST(rate) - 1) >> 5) \
+ & SYNQUACER_I2C_CSR_CS_MASK)
+
+/* min I2C clock frequency 14M */
+#define SYNQUACER_I2C_MIN_CLK_RATE (14 * 1000000)
+/* max I2C clock frequency 200M */
+#define SYNQUACER_I2C_MAX_CLK_RATE (200 * 1000000)
+/* I2C clock frequency 18M */
+#define SYNQUACER_I2C_CLK_RATE_18M (18 * 1000000)
+
+#define SYNQUACER_I2C_SPEED_FM 400 // Fast Mode
+#define SYNQUACER_I2C_SPEED_SM 100 // Standard Mode
+
+enum i2c_state {
+ STATE_IDLE,
+ STATE_START,
+ STATE_READ,
+ STATE_WRITE
+};
+
+struct synquacer_i2c {
+ struct completion completion;
+
+ struct i2c_msg *msg;
+ u32 msg_num;
+ u32 msg_idx;
+ u32 msg_ptr;
+
+ int irq;
+ struct device *dev;
+ void __iomem *base;
+ struct clk *pclk;
+ u32 pclkrate;
+ u32 speed_khz;
+ u32 timeout_ms;
+ enum i2c_state state;
+ struct i2c_adapter adapter;
+
+ bool is_suspended;
+};
+
+static inline int is_lastmsg(struct synquacer_i2c *i2c)
+{
+ return i2c->msg_idx >= (i2c->msg_num - 1);
+}
+
+static inline int is_msglast(struct synquacer_i2c *i2c)
+{
+ return i2c->msg_ptr == (i2c->msg->len - 1);
+}
+
+static inline int is_msgend(struct synquacer_i2c *i2c)
+{
+ return i2c->msg_ptr >= i2c->msg->len;
+}
+
+static inline unsigned long calc_timeout_ms(struct synquacer_i2c *i2c,
+ struct i2c_msg *msgs,
+ int num)
+{
+ unsigned long bit_count = 0;
+ int i;
+
+ for (i = 0; i < num; i++, msgs++)
+ bit_count += msgs->len;
+
+ return DIV_ROUND_UP((bit_count * 9 + num * 10) * 3, 200) + 10;
+}
+
+static void synquacer_i2c_stop(struct synquacer_i2c *i2c, int ret)
+{
+ /*
+ * clear IRQ (INT=0, BER=0)
+ * set Stop Condition (MSS=0)
+ * Interrupt Disable
+ */
+ writeb(0, i2c->base + SYNQUACER_I2C_REG_BCR);
+
+ i2c->state = STATE_IDLE;
+
+ i2c->msg_ptr = 0;
+ i2c->msg = NULL;
+ i2c->msg_idx++;
+ i2c->msg_num = 0;
+ if (ret)
+ i2c->msg_idx = ret;
+
+ complete(&i2c->completion);
+}
+
+static void synquacer_i2c_hw_init(struct synquacer_i2c *i2c)
+{
+ unsigned char ccr_cs, csr_cs;
+ u32 rt = i2c->pclkrate;
+
+ /* Set own Address */
+ writeb(0, i2c->base + SYNQUACER_I2C_REG_ADR);
+
+ /* Set PCLK frequency */
+ writeb(SYNQUACER_I2C_BUS_CLK_FR(i2c->pclkrate),
+ i2c->base + SYNQUACER_I2C_REG_FSR);
+
+ switch (i2c->speed_khz) {
+ case SYNQUACER_I2C_SPEED_FM:
+ if (i2c->pclkrate <= SYNQUACER_I2C_CLK_RATE_18M) {
+ ccr_cs = SYNQUACER_I2C_CCR_CS_FAST_MAX_18M(rt);
+ csr_cs = SYNQUACER_I2C_CSR_CS_FAST_MAX_18M(rt);
+ } else {
+ ccr_cs = SYNQUACER_I2C_CCR_CS_FAST_MIN_18M(rt);
+ csr_cs = SYNQUACER_I2C_CSR_CS_FAST_MIN_18M(rt);
+ }
+
+ /* Set Clock and enable, Set fast mode */
+ writeb(ccr_cs | SYNQUACER_I2C_CCR_FM |
+ SYNQUACER_I2C_CCR_EN,
+ i2c->base + SYNQUACER_I2C_REG_CCR);
+ writeb(csr_cs, i2c->base + SYNQUACER_I2C_REG_CSR);
+ break;
+ case SYNQUACER_I2C_SPEED_SM:
+ if (i2c->pclkrate <= SYNQUACER_I2C_CLK_RATE_18M) {
+ ccr_cs = SYNQUACER_I2C_CCR_CS_STD_MAX_18M(rt);
+ csr_cs = SYNQUACER_I2C_CSR_CS_STD_MAX_18M(rt);
+ } else {
+ ccr_cs = SYNQUACER_I2C_CCR_CS_STD_MIN_18M(rt);
+ csr_cs = SYNQUACER_I2C_CSR_CS_STD_MIN_18M(rt);
+ }
+
+ /* Set Clock and enable, Set standard mode */
+ writeb(ccr_cs | SYNQUACER_I2C_CCR_EN,
+ i2c->base + SYNQUACER_I2C_REG_CCR);
+ writeb(csr_cs, i2c->base + SYNQUACER_I2C_REG_CSR);
+ break;
+ default:
+ WARN_ON(1);
+ }
+
+ /* clear IRQ (INT=0, BER=0), Interrupt Disable */
+ writeb(0, i2c->base + SYNQUACER_I2C_REG_BCR);
+ writeb(0, i2c->base + SYNQUACER_I2C_REG_BC2R);
+}
+
+static void synquacer_i2c_hw_reset(struct synquacer_i2c *i2c)
+{
+ /* Disable clock */
+ writeb(0, i2c->base + SYNQUACER_I2C_REG_CCR);
+ writeb(0, i2c->base + SYNQUACER_I2C_REG_CSR);
+
+ WAIT_PCLK(100, i2c->pclkrate);
+}
+
+static int synquacer_i2c_master_start(struct synquacer_i2c *i2c,
+ struct i2c_msg *pmsg)
+{
+ unsigned char bsr, bcr;
+
+ writeb(i2c_8bit_addr_from_msg(pmsg), i2c->base + SYNQUACER_I2C_REG_DAR);
+
+ dev_dbg(i2c->dev, "slave:0x%02x\n", pmsg->addr);
+
+ /* Generate Start Condition */
+ bsr = readb(i2c->base + SYNQUACER_I2C_REG_BSR);
+ bcr = readb(i2c->base + SYNQUACER_I2C_REG_BCR);
+ dev_dbg(i2c->dev, "bsr:0x%02x, bcr:0x%02x\n", bsr, bcr);
+
+ if ((bsr & SYNQUACER_I2C_BSR_BB) &&
+ !(bcr & SYNQUACER_I2C_BCR_MSS)) {
+ dev_dbg(i2c->dev, "bus is busy");
+ return -EBUSY;
+ }
+
+ if (bsr & SYNQUACER_I2C_BSR_BB) { /* Bus is busy */
+ dev_dbg(i2c->dev, "Continuous Start");
+ writeb(bcr | SYNQUACER_I2C_BCR_SCC,
+ i2c->base + SYNQUACER_I2C_REG_BCR);
+ } else {
+ if (bcr & SYNQUACER_I2C_BCR_MSS) {
+ dev_dbg(i2c->dev, "not in master mode");
+ return -EAGAIN;
+ }
+ dev_dbg(i2c->dev, "Start Condition");
+ /* Start Condition + Enable Interrupts */
+ writeb(bcr | SYNQUACER_I2C_BCR_MSS |
+ SYNQUACER_I2C_BCR_INTE | SYNQUACER_I2C_BCR_BEIE,
+ i2c->base + SYNQUACER_I2C_REG_BCR);
+ }
+
+ WAIT_PCLK(10, i2c->pclkrate);
+
+ /* get BSR & BCR registers */
+ bsr = readb(i2c->base + SYNQUACER_I2C_REG_BSR);
+ bcr = readb(i2c->base + SYNQUACER_I2C_REG_BCR);
+ dev_dbg(i2c->dev, "bsr:0x%02x, bcr:0x%02x\n", bsr, bcr);
+
+ if ((bsr & SYNQUACER_I2C_BSR_AL) ||
+ !(bcr & SYNQUACER_I2C_BCR_MSS)) {
+ dev_dbg(i2c->dev, "arbitration lost\n");
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static int synquacer_i2c_doxfer(struct synquacer_i2c *i2c,
+ struct i2c_msg *msgs, int num)
+{
+ unsigned char bsr;
+ unsigned long timeout;
+ int ret;
+
+ if (i2c->is_suspended)
+ return -EBUSY;
+
+ synquacer_i2c_hw_init(i2c);
+ bsr = readb(i2c->base + SYNQUACER_I2C_REG_BSR);
+ if (bsr & SYNQUACER_I2C_BSR_BB) {
+ dev_err(i2c->dev, "cannot get bus (bus busy)\n");
+ return -EBUSY;
+ }
+
+ reinit_completion(&i2c->completion);
+
+ i2c->msg = msgs;
+ i2c->msg_num = num;
+ i2c->msg_ptr = 0;
+ i2c->msg_idx = 0;
+ i2c->state = STATE_START;
+
+ ret = synquacer_i2c_master_start(i2c, i2c->msg);
+ if (ret < 0) {
+ dev_dbg(i2c->dev, "Address failed: (%d)\n", ret);
+ return ret;
+ }
+
+ timeout = wait_for_completion_timeout(&i2c->completion,
+ msecs_to_jiffies(i2c->timeout_ms));
+ if (timeout == 0) {
+ dev_dbg(i2c->dev, "timeout\n");
+ return -EAGAIN;
+ }
+
+ ret = i2c->msg_idx;
+ if (ret != num) {
+ dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret);
+ return -EAGAIN;
+ }
+
+ /* wait 2 clock periods to ensure the stop has been through the bus */
+ udelay(DIV_ROUND_UP(2 * 1000, i2c->speed_khz));
+
+ return 0;
+}
+
+static irqreturn_t synquacer_i2c_isr(int irq, void *dev_id)
+{
+ struct synquacer_i2c *i2c = dev_id;
+
+ unsigned char byte;
+ unsigned char bsr, bcr;
+ int ret;
+
+ bcr = readb(i2c->base + SYNQUACER_I2C_REG_BCR);
+ bsr = readb(i2c->base + SYNQUACER_I2C_REG_BSR);
+ dev_dbg(i2c->dev, "bsr:0x%02x, bcr:0x%02x\n", bsr, bcr);
+
+ if (bcr & SYNQUACER_I2C_BCR_BER) {
+ dev_err(i2c->dev, "bus error\n");
+ synquacer_i2c_stop(i2c, -EAGAIN);
+ goto out;
+ }
+ if ((bsr & SYNQUACER_I2C_BSR_AL) ||
+ !(bcr & SYNQUACER_I2C_BCR_MSS)) {
+ dev_dbg(i2c->dev, "arbitration lost\n");
+ synquacer_i2c_stop(i2c, -EAGAIN);
+ goto out;
+ }
+
+ switch (i2c->state) {
+ case STATE_START:
+ if (bsr & SYNQUACER_I2C_BSR_LRB) {
+ dev_dbg(i2c->dev, "ack was not received\n");
+ synquacer_i2c_stop(i2c, -EAGAIN);
+ goto out;
+ }
+
+ if (i2c->msg->flags & I2C_M_RD)
+ i2c->state = STATE_READ;
+ else
+ i2c->state = STATE_WRITE;
+
+ if (is_lastmsg(i2c) && i2c->msg->len == 0) {
+ synquacer_i2c_stop(i2c, 0);
+ goto out;
+ }
+
+ if (i2c->state == STATE_READ)
+ goto prepare_read;
+
+ /* fallthru */
+
+ case STATE_WRITE:
+ if (bsr & SYNQUACER_I2C_BSR_LRB) {
+ dev_dbg(i2c->dev, "WRITE: No Ack\n");
+ synquacer_i2c_stop(i2c, -EAGAIN);
+ goto out;
+ }
+
+ if (!is_msgend(i2c)) {
+ writeb(i2c->msg->buf[i2c->msg_ptr++],
+ i2c->base + SYNQUACER_I2C_REG_DAR);
+
+ /* clear IRQ, and continue */
+ writeb(SYNQUACER_I2C_BCR_BEIE |
+ SYNQUACER_I2C_BCR_MSS |
+ SYNQUACER_I2C_BCR_INTE,
+ i2c->base + SYNQUACER_I2C_REG_BCR);
+ break;
+ }
+ if (is_lastmsg(i2c)) {
+ synquacer_i2c_stop(i2c, 0);
+ break;
+ }
+ dev_dbg(i2c->dev, "WRITE: Next Message\n");
+
+ i2c->msg_ptr = 0;
+ i2c->msg_idx++;
+ i2c->msg++;
+
+ /* send the new start */
+ ret = synquacer_i2c_master_start(i2c, i2c->msg);
+ if (ret < 0) {
+ dev_dbg(i2c->dev, "restart error (%d)\n", ret);
+ synquacer_i2c_stop(i2c, -EAGAIN);
+ break;
+ }
+ i2c->state = STATE_START;
+ break;
+
+ case STATE_READ:
+ byte = readb(i2c->base + SYNQUACER_I2C_REG_DAR);
+ if (!(bsr & SYNQUACER_I2C_BSR_FBT)) /* data */
+ i2c->msg->buf[i2c->msg_ptr++] = byte;
+ else /* address */
+ dev_dbg(i2c->dev, "address:0x%02x. ignore it.\n", byte);
+
+prepare_read:
+ if (is_msglast(i2c)) {
+ writeb(SYNQUACER_I2C_BCR_MSS |
+ SYNQUACER_I2C_BCR_BEIE |
+ SYNQUACER_I2C_BCR_INTE,
+ i2c->base + SYNQUACER_I2C_REG_BCR);
+ break;
+ }
+ if (!is_msgend(i2c)) {
+ writeb(SYNQUACER_I2C_BCR_MSS |
+ SYNQUACER_I2C_BCR_BEIE |
+ SYNQUACER_I2C_BCR_INTE |
+ SYNQUACER_I2C_BCR_ACK,
+ i2c->base + SYNQUACER_I2C_REG_BCR);
+ break;
+ }
+ if (is_lastmsg(i2c)) {
+ /* last message, send stop and complete */
+ dev_dbg(i2c->dev, "READ: Send Stop\n");
+ synquacer_i2c_stop(i2c, 0);
+ break;
+ }
+ dev_dbg(i2c->dev, "READ: Next Transfer\n");
+
+ i2c->msg_ptr = 0;
+ i2c->msg_idx++;
+ i2c->msg++;
+
+ ret = synquacer_i2c_master_start(i2c, i2c->msg);
+ if (ret < 0) {
+ dev_dbg(i2c->dev, "restart error (%d)\n", ret);
+ synquacer_i2c_stop(i2c, -EAGAIN);
+ } else {
+ i2c->state = STATE_START;
+ }
+ break;
+ default:
+ dev_err(i2c->dev, "called in err STATE (%d)\n", i2c->state);
+ break;
+ }
+
+out:
+ WAIT_PCLK(10, i2c->pclkrate);
+ return IRQ_HANDLED;
+}
+
+static int synquacer_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num)
+{
+ struct synquacer_i2c *i2c;
+ int retry;
+ int ret;
+
+ i2c = i2c_get_adapdata(adap);
+ i2c->timeout_ms = calc_timeout_ms(i2c, msgs, num);
+
+ dev_dbg(i2c->dev, "calculated timeout %d ms\n", i2c->timeout_ms);
+
+ for (retry = 0; retry < adap->retries; retry++) {
+ ret = synquacer_i2c_doxfer(i2c, msgs, num);
+ if (ret != -EAGAIN)
+ return ret;
+
+ dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);
+
+ synquacer_i2c_hw_reset(i2c);
+ }
+ return -EIO;
+}
+
+static u32 synquacer_i2c_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm synquacer_i2c_algo = {
+ .master_xfer = synquacer_i2c_xfer,
+ .functionality = synquacer_i2c_functionality,
+};
+
+static struct i2c_adapter synquacer_i2c_ops = {
+ .owner = THIS_MODULE,
+ .name = "synquacer_i2c-adapter",
+ .algo = &synquacer_i2c_algo,
+ .retries = 5,
+};
+
+static int synquacer_i2c_probe(struct platform_device *pdev)
+{
+ struct synquacer_i2c *i2c;
+ struct resource *r;
+ u32 bus_speed;
+ int ret;
+
+ i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
+ if (!i2c)
+ return -ENOMEM;
+
+ bus_speed = i2c_acpi_find_bus_speed(&pdev->dev);
+ if (!bus_speed)
+ device_property_read_u32(&pdev->dev, "clock-frequency",
+ &bus_speed);
+
+ device_property_read_u32(&pdev->dev, "socionext,pclk-rate",
+ &i2c->pclkrate);
+
+ i2c->pclk = devm_clk_get(&pdev->dev, "pclk");
+ if (IS_ERR(i2c->pclk) && PTR_ERR(i2c->pclk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ if (!IS_ERR_OR_NULL(i2c->pclk)) {
+ dev_dbg(&pdev->dev, "clock source %p\n", i2c->pclk);
+
+ ret = clk_prepare_enable(i2c->pclk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable clock (%d)\n",
+ ret);
+ return ret;
+ }
+ i2c->pclkrate = clk_get_rate(i2c->pclk);
+ }
+
+ if (i2c->pclkrate < SYNQUACER_I2C_MIN_CLK_RATE ||
+ i2c->pclkrate > SYNQUACER_I2C_MAX_CLK_RATE) {
+ dev_err(&pdev->dev, "PCLK missing or out of range (%d)\n",
+ i2c->pclkrate);
+ return -EINVAL;
+ }
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ i2c->base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(i2c->base))
+ return PTR_ERR(i2c->base);
+
+ i2c->irq = platform_get_irq(pdev, 0);
+ if (i2c->irq < 0) {
+ dev_err(&pdev->dev, "no IRQ resource found\n");
+ return -ENODEV;
+ }
+
+ ret = devm_request_irq(&pdev->dev, i2c->irq, synquacer_i2c_isr,
+ 0, dev_name(&pdev->dev), i2c);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
+ return ret;
+ }
+
+ i2c->state = STATE_IDLE;
+ i2c->dev = &pdev->dev;
+ i2c->adapter = synquacer_i2c_ops;
+ i2c_set_adapdata(&i2c->adapter, i2c);
+ i2c->adapter.dev.parent = &pdev->dev;
+ i2c->adapter.nr = pdev->id;
+ init_completion(&i2c->completion);
+
+ if (bus_speed < 400000)
+ i2c->speed_khz = SYNQUACER_I2C_SPEED_SM;
+ else
+ i2c->speed_khz = SYNQUACER_I2C_SPEED_FM;
+
+ synquacer_i2c_hw_init(i2c);
+
+ ret = i2c_add_numbered_adapter(&i2c->adapter);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to add bus to i2c core\n");
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, i2c);
+
+ dev_info(&pdev->dev, "%s: synquacer_i2c adapter\n",
+ dev_name(&i2c->adapter.dev));
+
+ return 0;
+}
+
+static int synquacer_i2c_remove(struct platform_device *pdev)
+{
+ struct synquacer_i2c *i2c = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(&i2c->adapter);
+ if (!IS_ERR(i2c->pclk))
+ clk_disable_unprepare(i2c->pclk);
+
+ return 0;
+};
+
+static const struct of_device_id synquacer_i2c_dt_ids[] = {
+ { .compatible = "socionext,synquacer-i2c" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, synquacer_i2c_dt_ids);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id synquacer_i2c_acpi_ids[] = {
+ { "SCX0003" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(acpi, synquacer_i2c_acpi_ids);
+#endif
+
+static struct platform_driver synquacer_i2c_driver = {
+ .probe = synquacer_i2c_probe,
+ .remove = synquacer_i2c_remove,
+ .driver = {
+ .name = "synquacer_i2c",
+ .of_match_table = of_match_ptr(synquacer_i2c_dt_ids),
+ .acpi_match_table = ACPI_PTR(synquacer_i2c_acpi_ids),
+ },
+};
+module_platform_driver(synquacer_i2c_driver);
+
+MODULE_AUTHOR("Fujitsu Semiconductor Ltd");
+MODULE_DESCRIPTION("Socionext SynQuacer I2C Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c
index ae6ed254e01d..c80527816ad0 100644
--- a/drivers/i2c/busses/i2c-xiic.c
+++ b/drivers/i2c/busses/i2c-xiic.c
@@ -851,7 +851,7 @@ static const struct of_device_id xiic_of_match[] = {
MODULE_DEVICE_TABLE(of, xiic_of_match);
#endif
-static int __maybe_unused cdns_i2c_runtime_suspend(struct device *dev)
+static int __maybe_unused xiic_i2c_runtime_suspend(struct device *dev)
{
struct xiic_i2c *i2c = dev_get_drvdata(dev);
@@ -860,7 +860,7 @@ static int __maybe_unused cdns_i2c_runtime_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused cdns_i2c_runtime_resume(struct device *dev)
+static int __maybe_unused xiic_i2c_runtime_resume(struct device *dev)
{
struct xiic_i2c *i2c = dev_get_drvdata(dev);
int ret;
@@ -875,8 +875,8 @@ static int __maybe_unused cdns_i2c_runtime_resume(struct device *dev)
}
static const struct dev_pm_ops xiic_dev_pm_ops = {
- SET_RUNTIME_PM_OPS(cdns_i2c_runtime_suspend,
- cdns_i2c_runtime_resume, NULL)
+ SET_RUNTIME_PM_OPS(xiic_i2c_runtime_suspend,
+ xiic_i2c_runtime_resume, NULL)
};
static struct platform_driver xiic_i2c_driver = {
.probe = xiic_i2c_probe,
diff --git a/drivers/i2c/busses/i2c-xlp9xx.c b/drivers/i2c/busses/i2c-xlp9xx.c
index b970bf8f38e5..eb8913eba0c5 100644
--- a/drivers/i2c/busses/i2c-xlp9xx.c
+++ b/drivers/i2c/busses/i2c-xlp9xx.c
@@ -16,6 +16,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/delay.h>
#define XLP9XX_I2C_DIV 0x0
#define XLP9XX_I2C_CTRL 0x1
@@ -36,6 +37,8 @@
#define XLP9XX_I2C_TIMEOUT 0X10
#define XLP9XX_I2C_GENCALLADDR 0x11
+#define XLP9XX_I2C_STATUS_BUSY BIT(0)
+
#define XLP9XX_I2C_CMD_START BIT(7)
#define XLP9XX_I2C_CMD_STOP BIT(6)
#define XLP9XX_I2C_CMD_READ BIT(5)
@@ -71,6 +74,7 @@
#define XLP9XX_I2C_HIGH_FREQ 400000
#define XLP9XX_I2C_FIFO_SIZE 0x80U
#define XLP9XX_I2C_TIMEOUT_MS 1000
+#define XLP9XX_I2C_BUSY_TIMEOUT 50
#define XLP9XX_I2C_FIFO_WCNT_MASK 0xff
#define XLP9XX_I2C_STATUS_ERRMASK (XLP9XX_I2C_INTEN_ARLOST | \
@@ -125,7 +129,16 @@ static void xlp9xx_i2c_update_rx_fifo_thres(struct xlp9xx_i2c_dev *priv)
{
u32 thres;
- thres = min(priv->msg_buf_remaining, XLP9XX_I2C_FIFO_SIZE);
+ if (priv->len_recv)
+ /* interrupt after the first read to examine
+ * the length byte before proceeding further
+ */
+ thres = 1;
+ else if (priv->msg_buf_remaining > XLP9XX_I2C_FIFO_SIZE)
+ thres = XLP9XX_I2C_FIFO_SIZE;
+ else
+ thres = priv->msg_buf_remaining;
+
xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_MFIFOCTRL,
thres << XLP9XX_I2C_MFIFOCTRL_HITH_SHIFT);
}
@@ -144,7 +157,7 @@ static void xlp9xx_i2c_fill_tx_fifo(struct xlp9xx_i2c_dev *priv)
static void xlp9xx_i2c_drain_rx_fifo(struct xlp9xx_i2c_dev *priv)
{
- u32 len, i;
+ u32 len, i, val;
u8 rlen, *buf = priv->msg_buf;
len = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_FIFOWCNT) &
@@ -156,19 +169,27 @@ static void xlp9xx_i2c_drain_rx_fifo(struct xlp9xx_i2c_dev *priv)
rlen = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_MRXFIFO);
*buf++ = rlen;
len--;
+
if (priv->client_pec)
++rlen;
/* update remaining bytes and message length */
priv->msg_buf_remaining = rlen;
priv->msg_len = rlen + 1;
priv->len_recv = false;
- }
- len = min(priv->msg_buf_remaining, len);
- for (i = 0; i < len; i++, buf++)
- *buf = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_MRXFIFO);
+ /* Update transfer length to read only actual data */
+ val = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_CTRL);
+ val = (val & ~XLP9XX_I2C_CTRL_MCTLEN_MASK) |
+ ((rlen + 1) << XLP9XX_I2C_CTRL_MCTLEN_SHIFT);
+ xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CTRL, val);
+ } else {
+ len = min(priv->msg_buf_remaining, len);
+ for (i = 0; i < len; i++, buf++)
+ *buf = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_MRXFIFO);
+
+ priv->msg_buf_remaining -= len;
+ }
- priv->msg_buf_remaining -= len;
priv->msg_buf = buf;
if (priv->msg_buf_remaining)
@@ -224,6 +245,26 @@ xfer_done:
return IRQ_HANDLED;
}
+static int xlp9xx_i2c_check_bus_status(struct xlp9xx_i2c_dev *priv)
+{
+ u32 status;
+ u32 busy_timeout = XLP9XX_I2C_BUSY_TIMEOUT;
+
+ while (busy_timeout) {
+ status = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_STATUS);
+ if ((status & XLP9XX_I2C_STATUS_BUSY) == 0)
+ break;
+
+ busy_timeout--;
+ usleep_range(1000, 1100);
+ }
+
+ if (!busy_timeout)
+ return -EIO;
+
+ return 0;
+}
+
static int xlp9xx_i2c_init(struct xlp9xx_i2c_dev *priv)
{
u32 prescale;
@@ -311,7 +352,9 @@ static int xlp9xx_i2c_xfer_msg(struct xlp9xx_i2c_dev *priv, struct i2c_msg *msg,
/* set cmd reg */
cmd = XLP9XX_I2C_CMD_START;
- cmd |= (priv->msg_read ? XLP9XX_I2C_CMD_READ : XLP9XX_I2C_CMD_WRITE);
+ if (msg->len)
+ cmd |= (priv->msg_read ?
+ XLP9XX_I2C_CMD_READ : XLP9XX_I2C_CMD_WRITE);
if (last_msg)
cmd |= XLP9XX_I2C_CMD_STOP;
@@ -320,11 +363,12 @@ static int xlp9xx_i2c_xfer_msg(struct xlp9xx_i2c_dev *priv, struct i2c_msg *msg,
timeleft = msecs_to_jiffies(XLP9XX_I2C_TIMEOUT_MS);
timeleft = wait_for_completion_timeout(&priv->msg_complete, timeleft);
- if (priv->msg_err) {
+ if (priv->msg_err & XLP9XX_I2C_INTEN_BUSERR) {
dev_dbg(priv->dev, "transfer error %x!\n", priv->msg_err);
- if (priv->msg_err & XLP9XX_I2C_INTEN_BUSERR)
- xlp9xx_i2c_init(priv);
+ xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CMD, XLP9XX_I2C_CMD_STOP);
return -EIO;
+ } else if (priv->msg_err & XLP9XX_I2C_INTEN_NACKADDR) {
+ return -ENXIO;
}
if (timeleft == 0) {
@@ -345,6 +389,14 @@ static int xlp9xx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
int i, ret;
struct xlp9xx_i2c_dev *priv = i2c_get_adapdata(adap);
+ ret = xlp9xx_i2c_check_bus_status(priv);
+ if (ret) {
+ xlp9xx_i2c_init(priv);
+ ret = xlp9xx_i2c_check_bus_status(priv);
+ if (ret)
+ return ret;
+ }
+
for (i = 0; i < num; i++) {
ret = xlp9xx_i2c_xfer_msg(priv, &msgs[i], i == num - 1);
if (ret != 0)
@@ -356,8 +408,8 @@ static int xlp9xx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
static u32 xlp9xx_i2c_functionality(struct i2c_adapter *adapter)
{
- return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C |
- I2C_FUNC_10BIT_ADDR;
+ return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_READ_BLOCK_DATA |
+ I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR;
}
static const struct i2c_algorithm xlp9xx_i2c_algo = {
diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
index 5a00bf443d06..1ba40bb2b966 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -58,6 +58,8 @@
#define I2C_ADDR_7BITS_MAX 0x77
#define I2C_ADDR_7BITS_COUNT (I2C_ADDR_7BITS_MAX + 1)
+#define I2C_ADDR_DEVICE_ID 0x7c
+
/*
* core_lock protects i2c_adapter_idr, and guarantees that device detection,
* deletion of detected devices, and attach_adapter calls are serialized
@@ -67,18 +69,18 @@ static DEFINE_IDR(i2c_adapter_idr);
static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver);
-static struct static_key i2c_trace_msg = STATIC_KEY_INIT_FALSE;
+static DEFINE_STATIC_KEY_FALSE(i2c_trace_msg_key);
static bool is_registered;
int i2c_transfer_trace_reg(void)
{
- static_key_slow_inc(&i2c_trace_msg);
+ static_branch_inc(&i2c_trace_msg_key);
return 0;
}
void i2c_transfer_trace_unreg(void)
{
- static_key_slow_dec(&i2c_trace_msg);
+ static_branch_dec(&i2c_trace_msg_key);
}
const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
@@ -124,6 +126,10 @@ static int i2c_device_uevent(struct device *dev, struct kobj_uevent_env *env)
struct i2c_client *client = to_i2c_client(dev);
int rc;
+ rc = of_device_uevent_modalias(dev, env);
+ if (rc != -ENODEV)
+ return rc;
+
rc = acpi_device_uevent_modalias(dev, env);
if (rc != -ENODEV)
return rc;
@@ -439,6 +445,10 @@ show_modalias(struct device *dev, struct device_attribute *attr, char *buf)
struct i2c_client *client = to_i2c_client(dev);
int len;
+ len = of_device_modalias(dev, buf, PAGE_SIZE);
+ if (len != -ENODEV)
+ return len;
+
len = acpi_device_modalias(dev, buf, PAGE_SIZE -1);
if (len != -ENODEV)
return len;
@@ -507,7 +517,7 @@ static unsigned short i2c_encode_flags_to_addr(struct i2c_client *client)
/* This is a permissive address validity check, I2C address map constraints
* are purposely not enforced, except for the general call address. */
-int i2c_check_addr_validity(unsigned addr, unsigned short flags)
+static int i2c_check_addr_validity(unsigned int addr, unsigned short flags)
{
if (flags & I2C_CLIENT_TEN) {
/* 10-bit address, all values are valid */
@@ -1835,14 +1845,18 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
unsigned long orig_jiffies;
int ret, try;
+ if (WARN_ON(!msgs || num < 1))
+ return -EINVAL;
+
if (adap->quirks && i2c_check_for_quirks(adap, msgs, num))
return -EOPNOTSUPP;
- /* i2c_trace_msg gets enabled when tracepoint i2c_transfer gets
+ /*
+ * i2c_trace_msg_key gets enabled when tracepoint i2c_transfer gets
* enabled. This is an efficient way of keeping the for-loop from
* being executed when not needed.
*/
- if (static_key_false(&i2c_trace_msg)) {
+ if (static_branch_unlikely(&i2c_trace_msg_key)) {
int i;
for (i = 0; i < num; i++)
if (msgs[i].flags & I2C_M_RD)
@@ -1861,12 +1875,12 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
break;
}
- if (static_key_false(&i2c_trace_msg)) {
+ if (static_branch_unlikely(&i2c_trace_msg_key)) {
int i;
for (i = 0; i < ret; i++)
if (msgs[i].flags & I2C_M_RD)
trace_i2c_reply(adap, &msgs[i], i);
- trace_i2c_result(adap, i, ret);
+ trace_i2c_result(adap, num, ret);
}
return ret;
@@ -1968,6 +1982,37 @@ int i2c_transfer_buffer_flags(const struct i2c_client *client, char *buf,
}
EXPORT_SYMBOL(i2c_transfer_buffer_flags);
+/**
+ * i2c_get_device_id - get manufacturer, part id and die revision of a device
+ * @client: The device to query
+ * @id: The queried information
+ *
+ * Returns negative errno on error, zero on success.
+ */
+int i2c_get_device_id(const struct i2c_client *client,
+ struct i2c_device_identity *id)
+{
+ struct i2c_adapter *adap = client->adapter;
+ union i2c_smbus_data raw_id;
+ int ret;
+
+ if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_I2C_BLOCK))
+ return -EOPNOTSUPP;
+
+ raw_id.block[0] = 3;
+ ret = i2c_smbus_xfer(adap, I2C_ADDR_DEVICE_ID, 0,
+ I2C_SMBUS_READ, client->addr << 1,
+ I2C_SMBUS_I2C_BLOCK_DATA, &raw_id);
+ if (ret)
+ return ret;
+
+ id->manufacturer_id = (raw_id.block[1] << 4) | (raw_id.block[2] >> 4);
+ id->part_id = ((raw_id.block[2] & 0xf) << 5) | (raw_id.block[3] >> 3);
+ id->die_revision = raw_id.block[3] & 0x7;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(i2c_get_device_id);
+
/* ----------------------------------------------------
* the i2c address scanning function
* Will not work for 10-bit addresses!
diff --git a/drivers/i2c/i2c-core-of.c b/drivers/i2c/i2c-core-of.c
index 8d474bb1dc15..c405270a98b4 100644
--- a/drivers/i2c/i2c-core-of.c
+++ b/drivers/i2c/i2c-core-of.c
@@ -4,7 +4,7 @@
* Copyright (C) 2008 Jochen Friedrich <jochen@scram.de>
* based on a previous patch from Jon Smirl <jonsmirl@gmail.com>
*
- * Copyright (C) 2013 Wolfram Sang <wsa@the-dreams.de>
+ * Copyright (C) 2013, 2018 Wolfram Sang <wsa@the-dreams.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
@@ -25,12 +25,11 @@
static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
struct device_node *node)
{
- struct i2c_client *result;
+ struct i2c_client *client;
struct i2c_board_info info = {};
struct dev_archdata dev_ad = {};
- const __be32 *addr_be;
u32 addr;
- int len;
+ int ret;
dev_dbg(&adap->dev, "of_i2c: register %pOF\n", node);
@@ -40,13 +39,12 @@ static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
return ERR_PTR(-EINVAL);
}
- addr_be = of_get_property(node, "reg", &len);
- if (!addr_be || (len < sizeof(*addr_be))) {
+ ret = of_property_read_u32(node, "reg", &addr);
+ if (ret) {
dev_err(&adap->dev, "of_i2c: invalid reg on %pOF\n", node);
- return ERR_PTR(-EINVAL);
+ return ERR_PTR(ret);
}
- addr = be32_to_cpup(addr_be);
if (addr & I2C_TEN_BIT_ADDRESS) {
addr &= ~I2C_TEN_BIT_ADDRESS;
info.flags |= I2C_CLIENT_TEN;
@@ -57,15 +55,9 @@ static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
info.flags |= I2C_CLIENT_SLAVE;
}
- if (i2c_check_addr_validity(addr, info.flags)) {
- dev_err(&adap->dev, "of_i2c: invalid addr=%x on %pOF\n",
- addr, node);
- return ERR_PTR(-EINVAL);
- }
-
info.addr = addr;
- info.of_node = of_node_get(node);
info.archdata = &dev_ad;
+ info.of_node = of_node_get(node);
if (of_property_read_bool(node, "host-notify"))
info.flags |= I2C_CLIENT_HOST_NOTIFY;
@@ -73,13 +65,13 @@ static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
if (of_get_property(node, "wakeup-source", NULL))
info.flags |= I2C_CLIENT_WAKE;
- result = i2c_new_device(adap, &info);
- if (result == NULL) {
+ client = i2c_new_device(adap, &info);
+ if (!client) {
dev_err(&adap->dev, "of_i2c: Failure registering %pOF\n", node);
of_node_put(node);
return ERR_PTR(-EINVAL);
}
- return result;
+ return client;
}
void of_i2c_register_devices(struct i2c_adapter *adap)
@@ -103,7 +95,7 @@ void of_i2c_register_devices(struct i2c_adapter *adap)
client = of_i2c_register_device(adap, node);
if (IS_ERR(client)) {
- dev_warn(&adap->dev,
+ dev_err(&adap->dev,
"Failed to create I2C device for %pOF\n",
node);
of_node_clear_flag(node, OF_POPULATED);
diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c
index 59d5cf376f6a..b5aec33002c3 100644
--- a/drivers/i2c/i2c-core-smbus.c
+++ b/drivers/i2c/i2c-core-smbus.c
@@ -308,17 +308,21 @@ static void i2c_smbus_try_get_dmabuf(struct i2c_msg *msg, u8 init_val)
msg->buf[0] = init_val;
}
-/* Simulate a SMBus command using the i2c protocol
- No checking of parameters is done! */
+/*
+ * Simulate a SMBus command using the I2C protocol.
+ * No checking of parameters is done!
+ */
static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
unsigned short flags,
char read_write, u8 command, int size,
union i2c_smbus_data *data)
{
- /* So we need to generate a series of msgs. In the case of writing, we
- need to use only one message; when reading, we need two. We initialize
- most things with sane defaults, to keep the code below somewhat
- simpler. */
+ /*
+ * So we need to generate a series of msgs. In the case of writing, we
+ * need to use only one message; when reading, we need two. We
+ * initialize most things with sane defaults, to keep the code below
+ * somewhat simpler.
+ */
unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3];
unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];
int num = read_write == I2C_SMBUS_READ ? 2 : 1;
diff --git a/drivers/i2c/i2c-core.h b/drivers/i2c/i2c-core.h
index 3d3d9bf02101..37576f50fe20 100644
--- a/drivers/i2c/i2c-core.h
+++ b/drivers/i2c/i2c-core.h
@@ -27,7 +27,6 @@ extern struct rw_semaphore __i2c_board_lock;
extern struct list_head __i2c_board_list;
extern int __i2c_first_dynamic_bus_num;
-int i2c_check_addr_validity(unsigned addr, unsigned short flags);
int i2c_check_7bit_addr_validity_strict(unsigned short addr);
#ifdef CONFIG_ACPI
diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c
index 036a03f0d0a6..1667b6e7674f 100644
--- a/drivers/i2c/i2c-dev.c
+++ b/drivers/i2c/i2c-dev.c
@@ -280,7 +280,7 @@ static noinline int i2cdev_ioctl_rdwr(struct i2c_client *client,
*/
if (msgs[i].flags & I2C_M_RECV_LEN) {
if (!(msgs[i].flags & I2C_M_RD) ||
- msgs[i].buf[0] < 1 ||
+ msgs[i].len < 1 || msgs[i].buf[0] < 1 ||
msgs[i].len < msgs[i].buf[0] +
I2C_SMBUS_BLOCK_MAX) {
res = -EINVAL;
diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
index fbb84c7ef282..09bafd3e68fa 100644
--- a/drivers/i2c/muxes/i2c-mux-pca954x.c
+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
@@ -77,6 +77,7 @@ struct chip_desc {
pca954x_ismux = 0,
pca954x_isswi
} muxtype;
+ struct i2c_device_identity id;
};
struct pca954x {
@@ -97,59 +98,83 @@ static const struct chip_desc chips[] = {
.nchans = 2,
.enable = 0x4,
.muxtype = pca954x_ismux,
+ .id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9542] = {
.nchans = 2,
.enable = 0x4,
.has_irq = 1,
.muxtype = pca954x_ismux,
+ .id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9543] = {
.nchans = 2,
.has_irq = 1,
.muxtype = pca954x_isswi,
+ .id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9544] = {
.nchans = 4,
.enable = 0x4,
.has_irq = 1,
.muxtype = pca954x_ismux,
+ .id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9545] = {
.nchans = 4,
.has_irq = 1,
.muxtype = pca954x_isswi,
+ .id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9546] = {
.nchans = 4,
.muxtype = pca954x_isswi,
+ .id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9547] = {
.nchans = 8,
.enable = 0x8,
.muxtype = pca954x_ismux,
+ .id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9548] = {
.nchans = 8,
.muxtype = pca954x_isswi,
+ .id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9846] = {
.nchans = 4,
.muxtype = pca954x_isswi,
+ .id = {
+ .manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS,
+ .part_id = 0x10b,
+ },
},
[pca_9847] = {
.nchans = 8,
.enable = 0x8,
.muxtype = pca954x_ismux,
+ .id = {
+ .manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS,
+ .part_id = 0x108,
+ },
},
[pca_9848] = {
.nchans = 8,
.muxtype = pca954x_isswi,
+ .id = {
+ .manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS,
+ .part_id = 0x10a,
+ },
},
[pca_9849] = {
.nchans = 4,
.enable = 0x4,
.muxtype = pca954x_ismux,
+ .id = {
+ .manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS,
+ .part_id = 0x109,
+ },
},
};
@@ -369,6 +394,30 @@ static int pca954x_probe(struct i2c_client *client,
if (IS_ERR(gpio))
return PTR_ERR(gpio);
+ match = of_match_device(of_match_ptr(pca954x_of_match), &client->dev);
+ if (match)
+ data->chip = of_device_get_match_data(&client->dev);
+ else
+ data->chip = &chips[id->driver_data];
+
+ if (data->chip->id.manufacturer_id != I2C_DEVICE_ID_NONE) {
+ struct i2c_device_identity id;
+
+ ret = i2c_get_device_id(client, &id);
+ if (ret && ret != -EOPNOTSUPP)
+ return ret;
+
+ if (!ret &&
+ (id.manufacturer_id != data->chip->id.manufacturer_id ||
+ id.part_id != data->chip->id.part_id)) {
+ dev_warn(&client->dev,
+ "unexpected device id %03x-%03x-%x\n",
+ id.manufacturer_id, id.part_id,
+ id.die_revision);
+ return -ENODEV;
+ }
+ }
+
/* Write the mux register at addr to verify
* that the mux is in fact present. This also
* initializes the mux to disconnected state.
@@ -378,12 +427,6 @@ static int pca954x_probe(struct i2c_client *client,
return -ENODEV;
}
- match = of_match_device(of_match_ptr(pca954x_of_match), &client->dev);
- if (match)
- data->chip = of_device_get_match_data(&client->dev);
- else
- data->chip = &chips[id->driver_data];
-
data->last_chan = 0; /* force the first selection */
idle_disconnect_dt = of_node &&
diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig
index cf1fb3fb5d26..901b8833847f 100644
--- a/drivers/ide/Kconfig
+++ b/drivers/ide/Kconfig
@@ -200,7 +200,7 @@ comment "IDE chipset support/bugfixes"
config IDE_GENERIC
tristate "generic/default IDE chipset support"
- depends on ALPHA || X86 || IA64 || M32R || MIPS || ARCH_RPC
+ depends on ALPHA || X86 || IA64 || MIPS || ARCH_RPC
default ARM && ARCH_RPC
help
This is the generic IDE driver. This driver attaches to the
diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c
index 7c3ed7c9af77..5a8e8e3c22cd 100644
--- a/drivers/ide/ide-cd.c
+++ b/drivers/ide/ide-cd.c
@@ -712,7 +712,7 @@ static ide_startstop_t cdrom_start_rw(ide_drive_t *drive, struct request *rq)
struct request_queue *q = drive->queue;
int write = rq_data_dir(rq) == WRITE;
unsigned short sectors_per_frame =
- queue_logical_block_size(q) >> SECTOR_BITS;
+ queue_logical_block_size(q) >> SECTOR_SHIFT;
ide_debug_log(IDE_DBG_RQ, "rq->cmd[0]: 0x%x, rq->cmd_flags: 0x%x, "
"secs_per_frame: %u",
@@ -919,7 +919,7 @@ static int cdrom_read_capacity(ide_drive_t *drive, unsigned long *capacity,
* end up being bogus.
*/
blocklen = be32_to_cpu(capbuf.blocklen);
- blocklen = (blocklen >> SECTOR_BITS) << SECTOR_BITS;
+ blocklen = (blocklen >> SECTOR_SHIFT) << SECTOR_SHIFT;
switch (blocklen) {
case 512:
case 1024:
@@ -935,7 +935,7 @@ static int cdrom_read_capacity(ide_drive_t *drive, unsigned long *capacity,
}
*capacity = 1 + be32_to_cpu(capbuf.lba);
- *sectors_per_frame = blocklen >> SECTOR_BITS;
+ *sectors_per_frame = blocklen >> SECTOR_SHIFT;
ide_debug_log(IDE_DBG_PROBE, "cap: %lu, sectors_per_frame: %lu",
*capacity, *sectors_per_frame);
@@ -1012,7 +1012,7 @@ int ide_cd_read_toc(ide_drive_t *drive, struct request_sense *sense)
drive->probed_capacity = toc->capacity * sectors_per_frame;
blk_queue_logical_block_size(drive->queue,
- sectors_per_frame << SECTOR_BITS);
+ sectors_per_frame << SECTOR_SHIFT);
/* first read just the header, so we know how long the TOC is */
stat = cdrom_read_tocentry(drive, 0, 1, 0, (char *) &toc->hdr,
@@ -1613,6 +1613,8 @@ static int idecd_open(struct block_device *bdev, fmode_t mode)
struct cdrom_info *info;
int rc = -ENXIO;
+ check_disk_change(bdev);
+
mutex_lock(&ide_cd_mutex);
info = ide_cd_get(bdev->bd_disk);
if (!info)
diff --git a/drivers/ide/ide-cd.h b/drivers/ide/ide-cd.h
index 264e822eba58..04f0f310a856 100644
--- a/drivers/ide/ide-cd.h
+++ b/drivers/ide/ide-cd.h
@@ -21,11 +21,7 @@
/************************************************************************/
-#define SECTOR_BITS 9
-#ifndef SECTOR_SIZE
-#define SECTOR_SIZE (1 << SECTOR_BITS)
-#endif
-#define SECTORS_PER_FRAME (CD_FRAMESIZE >> SECTOR_BITS)
+#define SECTORS_PER_FRAME (CD_FRAMESIZE >> SECTOR_SHIFT)
#define SECTOR_BUFFER_SIZE (CD_FRAMESIZE * 32)
/* Capabilities Page size including 8 bytes of Mode Page Header */
diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c
index 188d1b03715d..f1a7c58fe418 100644
--- a/drivers/ide/ide-disk.c
+++ b/drivers/ide/ide-disk.c
@@ -187,7 +187,7 @@ static ide_startstop_t ide_do_rw_disk(ide_drive_t *drive, struct request *rq,
BUG_ON(drive->dev_flags & IDE_DFLAG_BLOCKED);
BUG_ON(blk_rq_is_passthrough(rq));
- ledtrig_disk_activity();
+ ledtrig_disk_activity(rq_data_dir(rq) == WRITE);
pr_debug("%s: %sing: block=%llu, sectors=%u\n",
drive->name, rq_data_dir(rq) == READ ? "read" : "writ",
@@ -687,8 +687,8 @@ static void ide_disk_setup(ide_drive_t *drive)
queue_max_sectors(q) / 2);
if (ata_id_is_ssd(id)) {
- queue_flag_set_unlocked(QUEUE_FLAG_NONROT, q);
- queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, q);
+ blk_queue_flag_set(QUEUE_FLAG_NONROT, q);
+ blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, q);
}
/* calculate drive capacity, and select LBA if possible */
diff --git a/drivers/ide/ide-generic.c b/drivers/ide/ide-generic.c
index 54d7c4685d23..80c0d69b83ac 100644
--- a/drivers/ide/ide-generic.c
+++ b/drivers/ide/ide-generic.c
@@ -13,13 +13,10 @@
#include <linux/ide.h>
#include <linux/pci_ids.h>
-/* FIXME: convert arm and m32r to use ide_platform host driver */
+/* FIXME: convert arm to use ide_platform host driver */
#ifdef CONFIG_ARM
#include <asm/irq.h>
#endif
-#ifdef CONFIG_M32R
-#include <asm/m32r.h>
-#endif
#define DRV_NAME "ide_generic"
@@ -35,13 +32,6 @@ static const struct ide_port_info ide_generic_port_info = {
#ifdef CONFIG_ARM
static const u16 legacy_bases[] = { 0x1f0 };
static const int legacy_irqs[] = { IRQ_HARDDISK };
-#elif defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_MAPPI2) || \
- defined(CONFIG_PLAT_OPSPUT)
-static const u16 legacy_bases[] = { 0x1f0 };
-static const int legacy_irqs[] = { PLD_IRQ_CFIREQ };
-#elif defined(CONFIG_PLAT_MAPPI3)
-static const u16 legacy_bases[] = { 0x1f0, 0x170 };
-static const int legacy_irqs[] = { PLD_IRQ_CFIREQ, PLD_IRQ_IDEIREQ };
#elif defined(CONFIG_ALPHA)
static const u16 legacy_bases[] = { 0x1f0, 0x170, 0x1e8, 0x168 };
static const int legacy_irqs[] = { 14, 15, 11, 10 };
diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c
index caa20eb5f26b..2019e66eada7 100644
--- a/drivers/ide/ide-probe.c
+++ b/drivers/ide/ide-probe.c
@@ -766,14 +766,14 @@ static int ide_init_queue(ide_drive_t *drive)
* limits and LBA48 we could raise it but as yet
* do not.
*/
- q = blk_alloc_queue_node(GFP_KERNEL, hwif_to_node(hwif));
+ q = blk_alloc_queue_node(GFP_KERNEL, hwif_to_node(hwif), NULL);
if (!q)
return 1;
q->request_fn = do_ide_request;
q->initialize_rq_fn = ide_initialize_rq;
q->cmd_size = sizeof(struct ide_request);
- queue_flag_set_unlocked(QUEUE_FLAG_SCSI_PASSTHROUGH, q);
+ blk_queue_flag_set(QUEUE_FLAG_SCSI_PASSTHROUGH, q);
if (blk_init_allocated_queue(q) < 0) {
blk_cleanup_queue(q);
return 1;
diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c
index 870f92ef61c2..208f2d9f0e8a 100644
--- a/drivers/iio/accel/bmc150-accel-core.c
+++ b/drivers/iio/accel/bmc150-accel-core.c
@@ -336,8 +336,7 @@ static int bmc150_accel_update_slope(struct bmc150_accel_data *data)
return ret;
}
- dev_dbg(dev, "%s: %x %x\n", __func__, data->slope_thres,
- data->slope_dur);
+ dev_dbg(dev, "%x %x\n", data->slope_thres, data->slope_dur);
return ret;
}
@@ -1716,7 +1715,6 @@ static int bmc150_accel_runtime_suspend(struct device *dev)
struct bmc150_accel_data *data = iio_priv(indio_dev);
int ret;
- dev_dbg(dev, __func__);
ret = bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_SUSPEND, 0);
if (ret < 0)
return -EAGAIN;
@@ -1731,8 +1729,6 @@ static int bmc150_accel_runtime_resume(struct device *dev)
int ret;
int sleep_val;
- dev_dbg(dev, __func__);
-
ret = bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_NORMAL, 0);
if (ret < 0)
return ret;
diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c
index c066a3bdbff7..41d97faf5013 100644
--- a/drivers/iio/accel/hid-sensor-accel-3d.c
+++ b/drivers/iio/accel/hid-sensor-accel-3d.c
@@ -155,7 +155,7 @@ static int accel_3d_read_raw(struct iio_dev *indio_dev,
*val = 0;
*val2 = 0;
switch (mask) {
- case 0:
+ case IIO_CHAN_INFO_RAW:
hid_sensor_power_state(&accel_state->common_attributes, true);
report_id = accel_state->accel[chan->scan_index].report_id;
address = accel_3d_addresses[chan->scan_index];
diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c
index 363429b5686c..6bdec8c451e0 100644
--- a/drivers/iio/accel/st_accel_i2c.c
+++ b/drivers/iio/accel/st_accel_i2c.c
@@ -159,9 +159,8 @@ static int st_accel_i2c_probe(struct i2c_client *client,
if ((ret < 0) || (ret >= ST_ACCEL_MAX))
return -ENODEV;
- strncpy(client->name, st_accel_id_table[ret].name,
+ strlcpy(client->name, st_accel_id_table[ret].name,
sizeof(client->name));
- client->name[sizeof(client->name) - 1] = '\0';
} else if (!id)
return -ENODEV;
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 72bc2b71765a..15606f237480 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -144,10 +144,9 @@ config ASPEED_ADC
config AT91_ADC
tristate "Atmel AT91 ADC"
depends on ARCH_AT91
- depends on INPUT
+ depends on INPUT && SYSFS
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
- select SYSFS
help
Say yes here to build support for Atmel AT91 ADC.
@@ -698,7 +697,8 @@ config STM32_DFSDM_ADC
config STX104
tristate "Apex Embedded Systems STX104 driver"
- depends on PC104 && X86 && ISA_BUS_API
+ depends on PC104 && X86
+ select ISA_BUS_API
select GPIOLIB
help
Say yes here to build support for the Apex Embedded Systems STX104
diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c
index b7706bf10ffe..fbaae47746a8 100644
--- a/drivers/iio/adc/ad7476.c
+++ b/drivers/iio/adc/ad7476.c
@@ -1,9 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
/*
- * AD7466/7/8 AD7476/5/7/8 (A) SPI ADC driver
+ * Analog Devices AD7466/7/8 AD7476/5/7/8 (A) SPI ADC driver
+ * TI ADC081S/ADC101S/ADC121S 8/10/12-bit SPI ADC driver
*
* Copyright 2010 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/device.h>
@@ -56,6 +56,9 @@ enum ad7476_supported_device_ids {
ID_AD7468,
ID_AD7495,
ID_AD7940,
+ ID_ADC081S,
+ ID_ADC101S,
+ ID_ADC121S,
};
static irqreturn_t ad7476_trigger_handler(int irq, void *p)
@@ -147,6 +150,8 @@ static int ad7476_read_raw(struct iio_dev *indio_dev,
}, \
}
+#define ADC081S_CHAN(bits) _AD7476_CHAN((bits), 12 - (bits), \
+ BIT(IIO_CHAN_INFO_RAW))
#define AD7476_CHAN(bits) _AD7476_CHAN((bits), 13 - (bits), \
BIT(IIO_CHAN_INFO_RAW))
#define AD7940_CHAN(bits) _AD7476_CHAN((bits), 15 - (bits), \
@@ -192,6 +197,18 @@ static const struct ad7476_chip_info ad7476_chip_info_tbl[] = {
.channel[0] = AD7940_CHAN(14),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
+ [ID_ADC081S] = {
+ .channel[0] = ADC081S_CHAN(8),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ },
+ [ID_ADC101S] = {
+ .channel[0] = ADC081S_CHAN(10),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ },
+ [ID_ADC121S] = {
+ .channel[0] = ADC081S_CHAN(12),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ },
};
static const struct iio_info ad7476_info = {
@@ -294,6 +311,9 @@ static const struct spi_device_id ad7476_id[] = {
{"ad7910", ID_AD7467},
{"ad7920", ID_AD7466},
{"ad7940", ID_AD7940},
+ {"adc081s", ID_ADC081S},
+ {"adc101s", ID_ADC101S},
+ {"adc121s", ID_ADC121S},
{}
};
MODULE_DEVICE_TABLE(spi, ad7476_id);
diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c
index a30a97245e91..5be789269353 100644
--- a/drivers/iio/adc/axp20x_adc.c
+++ b/drivers/iio/adc/axp20x_adc.c
@@ -35,8 +35,13 @@
#define AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(x) (((x) & BIT(0)) << 1)
#define AXP20X_ADC_RATE_MASK GENMASK(7, 6)
+#define AXP813_V_I_ADC_RATE_MASK GENMASK(5, 4)
+#define AXP813_ADC_RATE_MASK (AXP20X_ADC_RATE_MASK | AXP813_V_I_ADC_RATE_MASK)
#define AXP20X_ADC_RATE_HZ(x) ((ilog2((x) / 25) << 6) & AXP20X_ADC_RATE_MASK)
#define AXP22X_ADC_RATE_HZ(x) ((ilog2((x) / 100) << 6) & AXP20X_ADC_RATE_MASK)
+#define AXP813_TS_GPIO0_ADC_RATE_HZ(x) AXP20X_ADC_RATE_HZ(x)
+#define AXP813_V_I_ADC_RATE_HZ(x) ((ilog2((x) / 100) << 4) & AXP813_V_I_ADC_RATE_MASK)
+#define AXP813_ADC_RATE_HZ(x) (AXP20X_ADC_RATE_HZ(x) | AXP813_V_I_ADC_RATE_HZ(x))
#define AXP20X_ADC_CHANNEL(_channel, _name, _type, _reg) \
{ \
@@ -95,6 +100,12 @@ enum axp22x_adc_channel_i {
AXP22X_BATT_DISCHRG_I,
};
+enum axp813_adc_channel_v {
+ AXP813_TS_IN = 0,
+ AXP813_GPIO0_V,
+ AXP813_BATT_V,
+};
+
static struct iio_map axp20x_maps[] = {
{
.consumer_dev_name = "axp20x-usb-power-supply",
@@ -197,6 +208,25 @@ static const struct iio_chan_spec axp22x_adc_channels[] = {
AXP20X_BATT_DISCHRG_I_H),
};
+static const struct iio_chan_spec axp813_adc_channels[] = {
+ {
+ .type = IIO_TEMP,
+ .address = AXP22X_PMIC_TEMP_H,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .datasheet_name = "pmic_temp",
+ },
+ AXP20X_ADC_CHANNEL(AXP813_GPIO0_V, "gpio0_v", IIO_VOLTAGE,
+ AXP288_GP_ADC_H),
+ AXP20X_ADC_CHANNEL(AXP813_BATT_V, "batt_v", IIO_VOLTAGE,
+ AXP20X_BATT_V_H),
+ AXP20X_ADC_CHANNEL(AXP22X_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT,
+ AXP20X_BATT_CHRG_I_H),
+ AXP20X_ADC_CHANNEL(AXP22X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT,
+ AXP20X_BATT_DISCHRG_I_H),
+};
+
static int axp20x_adc_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val)
{
@@ -243,6 +273,18 @@ static int axp22x_adc_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT;
}
+static int axp813_adc_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val)
+{
+ struct axp20x_adc_iio *info = iio_priv(indio_dev);
+
+ *val = axp20x_read_variable_width(info->regmap, chan->address, 12);
+ if (*val < 0)
+ return *val;
+
+ return IIO_VAL_INT;
+}
+
static int axp20x_adc_scale_voltage(int channel, int *val, int *val2)
{
switch (channel) {
@@ -273,6 +315,24 @@ static int axp20x_adc_scale_voltage(int channel, int *val, int *val2)
}
}
+static int axp813_adc_scale_voltage(int channel, int *val, int *val2)
+{
+ switch (channel) {
+ case AXP813_GPIO0_V:
+ *val = 0;
+ *val2 = 800000;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ case AXP813_BATT_V:
+ *val = 1;
+ *val2 = 100000;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ default:
+ return -EINVAL;
+ }
+}
+
static int axp20x_adc_scale_current(int channel, int *val, int *val2)
{
switch (channel) {
@@ -342,6 +402,26 @@ static int axp22x_adc_scale(struct iio_chan_spec const *chan, int *val,
}
}
+static int axp813_adc_scale(struct iio_chan_spec const *chan, int *val,
+ int *val2)
+{
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ return axp813_adc_scale_voltage(chan->channel, val, val2);
+
+ case IIO_CURRENT:
+ *val = 1;
+ return IIO_VAL_INT;
+
+ case IIO_TEMP:
+ *val = 100;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
static int axp20x_adc_offset_voltage(struct iio_dev *indio_dev, int channel,
int *val)
{
@@ -365,7 +445,7 @@ static int axp20x_adc_offset_voltage(struct iio_dev *indio_dev, int channel,
return -EINVAL;
}
- *val = !!(*val) * 700000;
+ *val = *val ? 700000 : 0;
return IIO_VAL_INT;
}
@@ -425,6 +505,26 @@ static int axp22x_read_raw(struct iio_dev *indio_dev,
}
}
+static int axp813_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_OFFSET:
+ *val = -2667;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ return axp813_adc_scale(chan, val, val2);
+
+ case IIO_CHAN_INFO_RAW:
+ return axp813_adc_raw(indio_dev, chan, val);
+
+ default:
+ return -EINVAL;
+ }
+}
+
static int axp20x_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val, int val2,
long mask)
@@ -442,15 +542,17 @@ static int axp20x_write_raw(struct iio_dev *indio_dev,
if (val != 0 && val != 700000)
return -EINVAL;
+ val = val ? 1 : 0;
+
switch (chan->channel) {
case AXP20X_GPIO0_V:
reg = AXP20X_GPIO10_IN_RANGE_GPIO0;
- regval = AXP20X_GPIO10_IN_RANGE_GPIO0_VAL(!!val);
+ regval = AXP20X_GPIO10_IN_RANGE_GPIO0_VAL(val);
break;
case AXP20X_GPIO1_V:
reg = AXP20X_GPIO10_IN_RANGE_GPIO1;
- regval = AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(!!val);
+ regval = AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(val);
break;
default:
@@ -470,14 +572,29 @@ static const struct iio_info axp22x_adc_iio_info = {
.read_raw = axp22x_read_raw,
};
-static int axp20x_adc_rate(int rate)
+static const struct iio_info axp813_adc_iio_info = {
+ .read_raw = axp813_read_raw,
+};
+
+static int axp20x_adc_rate(struct axp20x_adc_iio *info, int rate)
+{
+ return regmap_update_bits(info->regmap, AXP20X_ADC_RATE,
+ AXP20X_ADC_RATE_MASK,
+ AXP20X_ADC_RATE_HZ(rate));
+}
+
+static int axp22x_adc_rate(struct axp20x_adc_iio *info, int rate)
{
- return AXP20X_ADC_RATE_HZ(rate);
+ return regmap_update_bits(info->regmap, AXP20X_ADC_RATE,
+ AXP20X_ADC_RATE_MASK,
+ AXP22X_ADC_RATE_HZ(rate));
}
-static int axp22x_adc_rate(int rate)
+static int axp813_adc_rate(struct axp20x_adc_iio *info, int rate)
{
- return AXP22X_ADC_RATE_HZ(rate);
+ return regmap_update_bits(info->regmap, AXP813_ADC_RATE,
+ AXP813_ADC_RATE_MASK,
+ AXP813_ADC_RATE_HZ(rate));
}
struct axp_data {
@@ -485,7 +602,8 @@ struct axp_data {
int num_channels;
struct iio_chan_spec const *channels;
unsigned long adc_en1_mask;
- int (*adc_rate)(int rate);
+ int (*adc_rate)(struct axp20x_adc_iio *info,
+ int rate);
bool adc_en2;
struct iio_map *maps;
};
@@ -510,9 +628,28 @@ static const struct axp_data axp22x_data = {
.maps = axp22x_maps,
};
+static const struct axp_data axp813_data = {
+ .iio_info = &axp813_adc_iio_info,
+ .num_channels = ARRAY_SIZE(axp813_adc_channels),
+ .channels = axp813_adc_channels,
+ .adc_en1_mask = AXP22X_ADC_EN1_MASK,
+ .adc_rate = axp813_adc_rate,
+ .adc_en2 = false,
+ .maps = axp22x_maps,
+};
+
+static const struct of_device_id axp20x_adc_of_match[] = {
+ { .compatible = "x-powers,axp209-adc", .data = (void *)&axp20x_data, },
+ { .compatible = "x-powers,axp221-adc", .data = (void *)&axp22x_data, },
+ { .compatible = "x-powers,axp813-adc", .data = (void *)&axp813_data, },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, axp20x_adc_of_match);
+
static const struct platform_device_id axp20x_adc_id_match[] = {
{ .name = "axp20x-adc", .driver_data = (kernel_ulong_t)&axp20x_data, },
{ .name = "axp22x-adc", .driver_data = (kernel_ulong_t)&axp22x_data, },
+ { .name = "axp813-adc", .driver_data = (kernel_ulong_t)&axp813_data, },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(platform, axp20x_adc_id_match);
@@ -538,7 +675,16 @@ static int axp20x_probe(struct platform_device *pdev)
indio_dev->dev.of_node = pdev->dev.of_node;
indio_dev->modes = INDIO_DIRECT_MODE;
- info->data = (struct axp_data *)platform_get_device_id(pdev)->driver_data;
+ if (!pdev->dev.of_node) {
+ const struct platform_device_id *id;
+
+ id = platform_get_device_id(pdev);
+ info->data = (struct axp_data *)id->driver_data;
+ } else {
+ struct device *dev = &pdev->dev;
+
+ info->data = (struct axp_data *)of_device_get_match_data(dev);
+ }
indio_dev->name = platform_get_device_id(pdev)->name;
indio_dev->info = info->data->iio_info;
@@ -554,8 +700,7 @@ static int axp20x_probe(struct platform_device *pdev)
AXP20X_ADC_EN2_MASK, AXP20X_ADC_EN2_MASK);
/* Configure ADCs rate */
- regmap_update_bits(info->regmap, AXP20X_ADC_RATE, AXP20X_ADC_RATE_MASK,
- info->data->adc_rate(100));
+ info->data->adc_rate(info, 100);
ret = iio_map_array_register(indio_dev, info->data->maps);
if (ret < 0) {
@@ -602,6 +747,7 @@ static int axp20x_remove(struct platform_device *pdev)
static struct platform_driver axp20x_adc_driver = {
.driver = {
.name = "axp20x-adc",
+ .of_match_table = of_match_ptr(axp20x_adc_of_match),
},
.id_table = axp20x_adc_id_match,
.probe = axp20x_probe,
diff --git a/drivers/iio/adc/ep93xx_adc.c b/drivers/iio/adc/ep93xx_adc.c
index 81c901507ad2..5036c392cb20 100644
--- a/drivers/iio/adc/ep93xx_adc.c
+++ b/drivers/iio/adc/ep93xx_adc.c
@@ -167,10 +167,6 @@ static int ep93xx_adc_probe(struct platform_device *pdev)
priv = iio_priv(iiodev);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "Cannot obtain memory resource\n");
- return -ENXIO;
- }
priv->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(priv->base)) {
dev_err(&pdev->dev, "Cannot map memory resource\n");
diff --git a/drivers/iio/adc/ti-adc161s626.c b/drivers/iio/adc/ti-adc161s626.c
index 10fa7677ac4b..3bbc9b9ddbfe 100644
--- a/drivers/iio/adc/ti-adc161s626.c
+++ b/drivers/iio/adc/ti-adc161s626.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* ti-adc161s626.c - Texas Instruments ADC161S626 1-channel differential ADC
*
@@ -5,17 +6,8 @@
* adc141s626 - 14-bit ADC
* adc161s626 - 16-bit ADC
*
- * Copyright (C) 2016 Matt Ranostay <mranostay@gmail.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.
+ * Copyright (C) 2016-2018
+ * Author: Matt Ranostay <matt.ranostay@konsulko.com>
*/
#include <linux/module.h>
@@ -275,6 +267,6 @@ static struct spi_driver ti_adc_driver = {
};
module_spi_driver(ti_adc_driver);
-MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
MODULE_DESCRIPTION("Texas Instruments ADC1x1S 1-channel differential ADC");
MODULE_LICENSE("GPL");
diff --git a/drivers/iio/chemical/ams-iaq-core.c b/drivers/iio/chemical/ams-iaq-core.c
index d9e5950ad24a..a0646ba2ad88 100644
--- a/drivers/iio/chemical/ams-iaq-core.c
+++ b/drivers/iio/chemical/ams-iaq-core.c
@@ -1,18 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* ams-iaq-core.c - Support for AMS iAQ-Core VOC sensors
*
- * Copyright (C) 2015 Matt Ranostay <mranostay@gmail.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.
- *
+ * Copyright (C) 2015, 2018
+ * Author: Matt Ranostay <matt.ranostay@konsulko.com>
*/
#include <linux/module.h>
@@ -194,6 +185,6 @@ static struct i2c_driver ams_iaqcore_driver = {
};
module_i2c_driver(ams_iaqcore_driver);
-MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
MODULE_DESCRIPTION("AMS iAQ-Core VOC sensors");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/chemical/atlas-ph-sensor.c b/drivers/iio/chemical/atlas-ph-sensor.c
index 8c4e05580091..abfc4bbc4cfc 100644
--- a/drivers/iio/chemical/atlas-ph-sensor.c
+++ b/drivers/iio/chemical/atlas-ph-sensor.c
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* atlas-ph-sensor.c - Support for Atlas Scientific OEM pH-SM sensor
*
- * Copyright (C) 2015 Matt Ranostay <mranostay@gmail.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.
+ * Copyright (C) 2015-2018 Matt Ranostay
+ * Author: Matt Ranostay <matt.ranostay@konsulko.com>
*/
#include <linux/module.h>
@@ -689,6 +681,6 @@ static struct i2c_driver atlas_driver = {
};
module_i2c_driver(atlas_driver);
-MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
MODULE_DESCRIPTION("Atlas Scientific pH-SM sensor");
MODULE_LICENSE("GPL");
diff --git a/drivers/iio/chemical/ccs811.c b/drivers/iio/chemical/ccs811.c
index 1ea9f5513b02..b4a46eb45789 100644
--- a/drivers/iio/chemical/ccs811.c
+++ b/drivers/iio/chemical/ccs811.c
@@ -32,7 +32,7 @@
#define CCS811_ALG_RESULT_DATA 0x02
#define CCS811_RAW_DATA 0x03
#define CCS811_HW_ID 0x20
-#define CCS881_HW_ID_VALUE 0x81
+#define CCS811_HW_ID_VALUE 0x81
#define CCS811_HW_VERSION 0x21
#define CCS811_HW_VERSION_VALUE 0x10
#define CCS811_HW_VERSION_MASK 0xF0
@@ -69,7 +69,7 @@ struct ccs811_reading {
__be16 voc;
u8 status;
u8 error;
- __be16 resistance;
+ __be16 raw_data;
} __attribute__((__packed__));
struct ccs811_data {
@@ -213,12 +213,12 @@ static int ccs811_read_raw(struct iio_dev *indio_dev,
switch (chan->type) {
case IIO_VOLTAGE:
- *val = be16_to_cpu(data->buffer.resistance) &
+ *val = be16_to_cpu(data->buffer.raw_data) &
CCS811_VOLTAGE_MASK;
ret = IIO_VAL_INT;
break;
case IIO_CURRENT:
- *val = be16_to_cpu(data->buffer.resistance) >> 10;
+ *val = be16_to_cpu(data->buffer.raw_data) >> 10;
ret = IIO_VAL_INT;
break;
case IIO_CONCENTRATION:
@@ -356,7 +356,7 @@ static int ccs811_probe(struct i2c_client *client,
if (ret < 0)
return ret;
- if (ret != CCS881_HW_ID_VALUE) {
+ if (ret != CCS811_HW_ID_VALUE) {
dev_err(&client->dev, "hardware id doesn't match CCS81x\n");
return -ENODEV;
}
diff --git a/drivers/iio/chemical/vz89x.c b/drivers/iio/chemical/vz89x.c
index 9c9095ba4227..415b39339d4e 100644
--- a/drivers/iio/chemical/vz89x.c
+++ b/drivers/iio/chemical/vz89x.c
@@ -1,18 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vz89x.c - Support for SGX Sensortech MiCS VZ89X VOC sensors
*
- * Copyright (C) 2015 Matt Ranostay <mranostay@gmail.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.
- *
+ * Copyright (C) 2015-2018
+ * Author: Matt Ranostay <matt.ranostay@konsulko.com>
*/
#include <linux/module.h>
@@ -419,6 +410,6 @@ static struct i2c_driver vz89x_driver = {
};
module_i2c_driver(vz89x_driver);
-MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
MODULE_DESCRIPTION("SGX Sensortech MiCS VZ89X VOC sensors");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
index 7d30c59da3e2..705cb3e72663 100644
--- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
@@ -289,6 +289,7 @@ MODULE_DEVICE_TABLE(platform, cros_ec_sensors_ids);
static struct platform_driver cros_ec_sensors_platform_driver = {
.driver = {
.name = "cros-ec-sensors",
+ .pm = &cros_ec_sensors_pm_ops,
},
.probe = cros_ec_sensors_probe,
.id_table = cros_ec_sensors_ids,
diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
index 416cae5ebbd0..a620eb5ce202 100644
--- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
@@ -446,5 +446,54 @@ int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
}
EXPORT_SYMBOL_GPL(cros_ec_sensors_core_write);
+static int __maybe_unused cros_ec_sensors_prepare(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+
+ if (st->curr_sampl_freq == 0)
+ return 0;
+
+ /*
+ * If the sensors are sampled at high frequency, we will not be able to
+ * sleep. Set sampling to a long period if necessary.
+ */
+ if (st->curr_sampl_freq < CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY) {
+ mutex_lock(&st->cmd_lock);
+ st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
+ st->param.ec_rate.data = CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY;
+ cros_ec_motion_send_host_cmd(st, 0);
+ mutex_unlock(&st->cmd_lock);
+ }
+ return 0;
+}
+
+static void __maybe_unused cros_ec_sensors_complete(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+
+ if (st->curr_sampl_freq == 0)
+ return;
+
+ if (st->curr_sampl_freq < CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY) {
+ mutex_lock(&st->cmd_lock);
+ st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
+ st->param.ec_rate.data = st->curr_sampl_freq;
+ cros_ec_motion_send_host_cmd(st, 0);
+ mutex_unlock(&st->cmd_lock);
+ }
+}
+
+const struct dev_pm_ops cros_ec_sensors_pm_ops = {
+#ifdef CONFIG_PM_SLEEP
+ .prepare = cros_ec_sensors_prepare,
+ .complete = cros_ec_sensors_complete
+#endif
+};
+EXPORT_SYMBOL_GPL(cros_ec_sensors_pm_ops);
+
MODULE_DESCRIPTION("ChromeOS EC sensor hub core functions");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h
index 8bc2ca3c2e2e..2edf68dc7336 100644
--- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h
@@ -169,6 +169,8 @@ int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
struct iio_chan_spec const *chan,
int val, int val2, long mask);
+extern const struct dev_pm_ops cros_ec_sensors_pm_ops;
+
/* List of extended channel specification for all sensors */
extern const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[];
diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
index 474e1ac4e7c0..bf1e559ad7cd 100644
--- a/drivers/iio/counter/Kconfig
+++ b/drivers/iio/counter/Kconfig
@@ -7,7 +7,8 @@ menu "Counters"
config 104_QUAD_8
tristate "ACCES 104-QUAD-8 driver"
- depends on PC104 && X86 && ISA_BUS_API
+ depends on PC104 && X86
+ select ISA_BUS_API
help
Say yes here to build support for the ACCES 104-QUAD-8 quadrature
encoder counter/interface device family (104-QUAD-8, 104-QUAD-4).
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index 965d5c0d2468..76db0768e454 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -195,7 +195,8 @@ config AD7303
config CIO_DAC
tristate "Measurement Computing CIO-DAC IIO driver"
- depends on X86 && ISA_BUS_API
+ depends on X86 && (ISA_BUS || PC104)
+ select ISA_BUS_API
help
Say yes here to build support for the Measurement Computing CIO-DAC
analog output device family (CIO-DAC16, CIO-DAC08, PC104-DAC06). The
diff --git a/drivers/iio/dac/ad5380.c b/drivers/iio/dac/ad5380.c
index 845fd1c0fd9d..873c2bf637c0 100644
--- a/drivers/iio/dac/ad5380.c
+++ b/drivers/iio/dac/ad5380.c
@@ -158,7 +158,7 @@ static unsigned int ad5380_info_to_reg(struct iio_chan_spec const *chan,
long info)
{
switch (info) {
- case 0:
+ case IIO_CHAN_INFO_RAW:
return AD5380_REG_DATA(chan->address);
case IIO_CHAN_INFO_CALIBBIAS:
return AD5380_REG_OFFSET(chan->address);
diff --git a/drivers/iio/dac/ad5764.c b/drivers/iio/dac/ad5764.c
index 033f20eca616..9333177062c0 100644
--- a/drivers/iio/dac/ad5764.c
+++ b/drivers/iio/dac/ad5764.c
@@ -168,7 +168,7 @@ static int ad5764_read(struct iio_dev *indio_dev, unsigned int reg,
static int ad5764_chan_info_to_reg(struct iio_chan_spec const *chan, long info)
{
switch (info) {
- case 0:
+ case IIO_CHAN_INFO_RAW:
return AD5764_REG_DATA(chan->address);
case IIO_CHAN_INFO_CALIBBIAS:
return AD5764_REG_OFFSET(chan->address);
diff --git a/drivers/iio/dummy/Kconfig b/drivers/iio/dummy/Kconfig
index 5a29fbd3c531..c4fd108e91d3 100644
--- a/drivers/iio/dummy/Kconfig
+++ b/drivers/iio/dummy/Kconfig
@@ -9,20 +9,24 @@ config IIO_DUMMY_EVGEN
tristate
config IIO_SIMPLE_DUMMY
- tristate "An example driver with no hardware requirements"
- depends on IIO_SW_DEVICE
- help
- Driver intended mainly as documentation for how to write
- a driver. May also be useful for testing userspace code
- without hardware.
+ tristate "An example driver with no hardware requirements"
+ depends on IIO_SW_DEVICE
+ help
+ Driver intended mainly as documentation for how to write
+ a driver. May also be useful for testing userspace code
+ without hardware.
if IIO_SIMPLE_DUMMY
config IIO_SIMPLE_DUMMY_EVENTS
- bool "Event generation support"
- select IIO_DUMMY_EVGEN
- help
- Add some dummy events to the simple dummy driver.
+ bool "Event generation support"
+ select IIO_DUMMY_EVGEN
+ help
+ Add some dummy events to the simple dummy driver.
+
+ The purpose of this is to generate 'fake' event interrupts thus
+ allowing that driver's code to be as close as possible to that
+ a normal driver talking to hardware.
config IIO_SIMPLE_DUMMY_BUFFER
bool "Buffered capture support"
@@ -32,6 +36,9 @@ config IIO_SIMPLE_DUMMY_BUFFER
help
Add buffered data capture to the simple dummy driver.
+ Buffer handling elements of industrial I/O reference driver.
+ Uses the kfifo buffer.
+
endif # IIO_SIMPLE_DUMMY
endmenu
diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c
index f59995a90387..36941e69f959 100644
--- a/drivers/iio/gyro/hid-sensor-gyro-3d.c
+++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c
@@ -115,7 +115,7 @@ static int gyro_3d_read_raw(struct iio_dev *indio_dev,
*val = 0;
*val2 = 0;
switch (mask) {
- case 0:
+ case IIO_CHAN_INFO_RAW:
hid_sensor_power_state(&gyro_state->common_attributes, true);
report_id = gyro_state->gyro[chan->scan_index].report_id;
address = gyro_3d_addresses[chan->scan_index];
diff --git a/drivers/iio/health/max30100.c b/drivers/iio/health/max30100.c
index 91aef5df24a1..84010501762d 100644
--- a/drivers/iio/health/max30100.c
+++ b/drivers/iio/health/max30100.c
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* max30100.c - Support for MAX30100 heart rate and pulse oximeter sensor
*
- * Copyright (C) 2015 Matt Ranostay <mranostay@gmail.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.
+ * Copyright (C) 2015, 2018
+ * Author: Matt Ranostay <matt.ranostay@konsulko.com>
*
* TODO: enable pulse length controls via device tree properties
*/
@@ -518,6 +510,6 @@ static struct i2c_driver max30100_driver = {
};
module_i2c_driver(max30100_driver);
-MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
MODULE_DESCRIPTION("MAX30100 heart rate and pulse oximeter sensor");
MODULE_LICENSE("GPL");
diff --git a/drivers/iio/humidity/Kconfig b/drivers/iio/humidity/Kconfig
index 2c0fc9a400b8..1a0d458e4f4e 100644
--- a/drivers/iio/humidity/Kconfig
+++ b/drivers/iio/humidity/Kconfig
@@ -68,10 +68,12 @@ config HTS221
config HTS221_I2C
tristate
depends on HTS221
+ select REGMAP_I2C
config HTS221_SPI
tristate
depends on HTS221
+ select REGMAP_SPI
config HTU21
tristate "Measurement Specialties HTU21 humidity & temperature sensor"
diff --git a/drivers/iio/humidity/dht11.c b/drivers/iio/humidity/dht11.c
index df6bab40d6fa..1a9f8f4ffb88 100644
--- a/drivers/iio/humidity/dht11.c
+++ b/drivers/iio/humidity/dht11.c
@@ -159,7 +159,7 @@ static int dht11_decode(struct dht11 *dht11, int offset)
}
dht11->timestamp = ktime_get_boot_ns();
- if (hum_int < 20) { /* DHT22 */
+ if (hum_int < 4) { /* DHT22: 100000 = (3*256+232)*100 */
dht11->temperature = (((temp_int & 0x7f) << 8) + temp_dec) *
((temp_int & 0x80) ? -100 : 100);
dht11->humidity = ((hum_int << 8) + hum_dec) * 100;
diff --git a/drivers/iio/humidity/hdc100x.c b/drivers/iio/humidity/hdc100x.c
index d8438310b6d4..066e05f92081 100644
--- a/drivers/iio/humidity/hdc100x.c
+++ b/drivers/iio/humidity/hdc100x.c
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* hdc100x.c - Support for the TI HDC100x temperature + humidity sensors
*
- * Copyright (C) 2015 Matt Ranostay <mranostay@gmail.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.
+ * Copyright (C) 2015, 2018
+ * Author: Matt Ranostay <matt.ranostay@konsulko.com>
*
* Datasheets:
* http://www.ti.com/product/HDC1000/datasheet
@@ -449,6 +441,6 @@ static struct i2c_driver hdc100x_driver = {
};
module_i2c_driver(hdc100x_driver);
-MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
MODULE_DESCRIPTION("TI HDC100x humidity and temperature sensor driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/iio/humidity/hts221.h b/drivers/iio/humidity/hts221.h
index c581af8c0f5d..e41a3d83e95d 100644
--- a/drivers/iio/humidity/hts221.h
+++ b/drivers/iio/humidity/hts221.h
@@ -15,21 +15,8 @@
#include <linux/iio/iio.h>
-#define HTS221_RX_MAX_LENGTH 8
-#define HTS221_TX_MAX_LENGTH 8
-
#define HTS221_DATA_SIZE 2
-struct hts221_transfer_buffer {
- u8 rx_buf[HTS221_RX_MAX_LENGTH];
- u8 tx_buf[HTS221_TX_MAX_LENGTH] ____cacheline_aligned;
-};
-
-struct hts221_transfer_function {
- int (*read)(struct device *dev, u8 addr, int len, u8 *data);
- int (*write)(struct device *dev, u8 addr, int len, u8 *data);
-};
-
enum hts221_sensor_type {
HTS221_SENSOR_H,
HTS221_SENSOR_T,
@@ -44,8 +31,8 @@ struct hts221_sensor {
struct hts221_hw {
const char *name;
struct device *dev;
+ struct regmap *regmap;
- struct mutex lock;
struct iio_trigger *trig;
int irq;
@@ -53,16 +40,12 @@ struct hts221_hw {
bool enabled;
u8 odr;
-
- const struct hts221_transfer_function *tf;
- struct hts221_transfer_buffer tb;
};
extern const struct dev_pm_ops hts221_pm_ops;
-int hts221_write_with_mask(struct hts221_hw *hw, u8 addr, u8 mask, u8 val);
int hts221_probe(struct device *dev, int irq, const char *name,
- const struct hts221_transfer_function *tf_ops);
+ struct regmap *regmap);
int hts221_set_enable(struct hts221_hw *hw, bool enable);
int hts221_allocate_buffers(struct hts221_hw *hw);
int hts221_allocate_trigger(struct hts221_hw *hw);
diff --git a/drivers/iio/humidity/hts221_buffer.c b/drivers/iio/humidity/hts221_buffer.c
index e971ea425268..1a94b0b91721 100644
--- a/drivers/iio/humidity/hts221_buffer.c
+++ b/drivers/iio/humidity/hts221_buffer.c
@@ -12,6 +12,8 @@
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
+#include <linux/regmap.h>
+#include <linux/bitfield.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger.h>
@@ -38,12 +40,10 @@ static int hts221_trig_set_state(struct iio_trigger *trig, bool state)
{
struct iio_dev *iio_dev = iio_trigger_get_drvdata(trig);
struct hts221_hw *hw = iio_priv(iio_dev);
- int err;
-
- err = hts221_write_with_mask(hw, HTS221_REG_DRDY_EN_ADDR,
- HTS221_REG_DRDY_EN_MASK, state);
- return err < 0 ? err : 0;
+ return regmap_update_bits(hw->regmap, HTS221_REG_DRDY_EN_ADDR,
+ HTS221_REG_DRDY_EN_MASK,
+ FIELD_PREP(HTS221_REG_DRDY_EN_MASK, state));
}
static const struct iio_trigger_ops hts221_trigger_ops = {
@@ -53,15 +53,13 @@ static const struct iio_trigger_ops hts221_trigger_ops = {
static irqreturn_t hts221_trigger_handler_thread(int irq, void *private)
{
struct hts221_hw *hw = private;
- u8 status;
- int err;
+ int err, status;
- err = hw->tf->read(hw->dev, HTS221_REG_STATUS_ADDR, sizeof(status),
- &status);
+ err = regmap_read(hw->regmap, HTS221_REG_STATUS_ADDR, &status);
if (err < 0)
return IRQ_HANDLED;
- /*
+ /*
* H_DA bit (humidity data available) is routed to DRDY line.
* Humidity sample is computed after temperature one.
* Here we can assume data channels are both available if H_DA bit
@@ -102,8 +100,10 @@ int hts221_allocate_trigger(struct hts221_hw *hw)
break;
}
- err = hts221_write_with_mask(hw, HTS221_REG_DRDY_HL_ADDR,
- HTS221_REG_DRDY_HL_MASK, irq_active_low);
+ err = regmap_update_bits(hw->regmap, HTS221_REG_DRDY_HL_ADDR,
+ HTS221_REG_DRDY_HL_MASK,
+ FIELD_PREP(HTS221_REG_DRDY_HL_MASK,
+ irq_active_low));
if (err < 0)
return err;
@@ -114,9 +114,10 @@ int hts221_allocate_trigger(struct hts221_hw *hw)
open_drain = true;
}
- err = hts221_write_with_mask(hw, HTS221_REG_DRDY_PP_OD_ADDR,
- HTS221_REG_DRDY_PP_OD_MASK,
- open_drain);
+ err = regmap_update_bits(hw->regmap, HTS221_REG_DRDY_PP_OD_ADDR,
+ HTS221_REG_DRDY_PP_OD_MASK,
+ FIELD_PREP(HTS221_REG_DRDY_PP_OD_MASK,
+ open_drain));
if (err < 0)
return err;
@@ -171,15 +172,15 @@ static irqreturn_t hts221_buffer_handler_thread(int irq, void *p)
/* humidity data */
ch = &iio_dev->channels[HTS221_SENSOR_H];
- err = hw->tf->read(hw->dev, ch->address, HTS221_DATA_SIZE,
- buffer);
+ err = regmap_bulk_read(hw->regmap, ch->address,
+ buffer, HTS221_DATA_SIZE);
if (err < 0)
goto out;
/* temperature data */
ch = &iio_dev->channels[HTS221_SENSOR_T];
- err = hw->tf->read(hw->dev, ch->address, HTS221_DATA_SIZE,
- buffer + HTS221_DATA_SIZE);
+ err = regmap_bulk_read(hw->regmap, ch->address,
+ buffer + HTS221_DATA_SIZE, HTS221_DATA_SIZE);
if (err < 0)
goto out;
diff --git a/drivers/iio/humidity/hts221_core.c b/drivers/iio/humidity/hts221_core.c
index d3f7904766bd..166946d4978d 100644
--- a/drivers/iio/humidity/hts221_core.c
+++ b/drivers/iio/humidity/hts221_core.c
@@ -14,7 +14,8 @@
#include <linux/iio/sysfs.h>
#include <linux/delay.h>
#include <linux/pm.h>
-#include <asm/unaligned.h>
+#include <linux/regmap.h>
+#include <linux/bitfield.h>
#include "hts221.h"
@@ -131,38 +132,11 @@ static const struct iio_chan_spec hts221_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(2),
};
-int hts221_write_with_mask(struct hts221_hw *hw, u8 addr, u8 mask, u8 val)
-{
- u8 data;
- int err;
-
- mutex_lock(&hw->lock);
-
- err = hw->tf->read(hw->dev, addr, sizeof(data), &data);
- if (err < 0) {
- dev_err(hw->dev, "failed to read %02x register\n", addr);
- goto unlock;
- }
-
- data = (data & ~mask) | ((val << __ffs(mask)) & mask);
-
- err = hw->tf->write(hw->dev, addr, sizeof(data), &data);
- if (err < 0)
- dev_err(hw->dev, "failed to write %02x register\n", addr);
-
-unlock:
- mutex_unlock(&hw->lock);
-
- return err;
-}
-
static int hts221_check_whoami(struct hts221_hw *hw)
{
- u8 data;
- int err;
+ int err, data;
- err = hw->tf->read(hw->dev, HTS221_REG_WHOAMI_ADDR, sizeof(data),
- &data);
+ err = regmap_read(hw->regmap, HTS221_REG_WHOAMI_ADDR, &data);
if (err < 0) {
dev_err(hw->dev, "failed to read whoami register\n");
return err;
@@ -188,8 +162,10 @@ static int hts221_update_odr(struct hts221_hw *hw, u8 odr)
if (i == ARRAY_SIZE(hts221_odr_table))
return -EINVAL;
- err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR,
- HTS221_ODR_MASK, hts221_odr_table[i].val);
+ err = regmap_update_bits(hw->regmap, HTS221_REG_CNTRL1_ADDR,
+ HTS221_ODR_MASK,
+ FIELD_PREP(HTS221_ODR_MASK,
+ hts221_odr_table[i].val));
if (err < 0)
return err;
@@ -202,8 +178,8 @@ static int hts221_update_avg(struct hts221_hw *hw,
enum hts221_sensor_type type,
u16 val)
{
- int i, err;
const struct hts221_avg *avg = &hts221_avg_list[type];
+ int i, err, data;
for (i = 0; i < HTS221_AVG_DEPTH; i++)
if (avg->avg_avl[i] == val)
@@ -212,7 +188,9 @@ static int hts221_update_avg(struct hts221_hw *hw,
if (i == HTS221_AVG_DEPTH)
return -EINVAL;
- err = hts221_write_with_mask(hw, avg->addr, avg->mask, i);
+ data = ((i << __ffs(avg->mask)) & avg->mask);
+ err = regmap_update_bits(hw->regmap, avg->addr,
+ avg->mask, data);
if (err < 0)
return err;
@@ -274,8 +252,9 @@ int hts221_set_enable(struct hts221_hw *hw, bool enable)
{
int err;
- err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR,
- HTS221_ENABLE_MASK, enable);
+ err = regmap_update_bits(hw->regmap, HTS221_REG_CNTRL1_ADDR,
+ HTS221_ENABLE_MASK,
+ FIELD_PREP(HTS221_ENABLE_MASK, enable));
if (err < 0)
return err;
@@ -286,38 +265,35 @@ int hts221_set_enable(struct hts221_hw *hw, bool enable)
static int hts221_parse_temp_caldata(struct hts221_hw *hw)
{
- int err, *slope, *b_gen;
+ int err, *slope, *b_gen, cal0, cal1;
s16 cal_x0, cal_x1, cal_y0, cal_y1;
- u8 cal0, cal1;
+ __le16 val;
- err = hw->tf->read(hw->dev, HTS221_REG_0T_CAL_Y_H,
- sizeof(cal0), &cal0);
+ err = regmap_read(hw->regmap, HTS221_REG_0T_CAL_Y_H, &cal0);
if (err < 0)
return err;
- err = hw->tf->read(hw->dev, HTS221_REG_T1_T0_CAL_Y_H,
- sizeof(cal1), &cal1);
+ err = regmap_read(hw->regmap, HTS221_REG_T1_T0_CAL_Y_H, &cal1);
if (err < 0)
return err;
- cal_y0 = (le16_to_cpu(cal1 & 0x3) << 8) | cal0;
+ cal_y0 = ((cal1 & 0x3) << 8) | cal0;
- err = hw->tf->read(hw->dev, HTS221_REG_1T_CAL_Y_H,
- sizeof(cal0), &cal0);
+ err = regmap_read(hw->regmap, HTS221_REG_1T_CAL_Y_H, &cal0);
if (err < 0)
return err;
cal_y1 = (((cal1 & 0xc) >> 2) << 8) | cal0;
- err = hw->tf->read(hw->dev, HTS221_REG_0T_CAL_X_L, sizeof(cal_x0),
- (u8 *)&cal_x0);
+ err = regmap_bulk_read(hw->regmap, HTS221_REG_0T_CAL_X_L,
+ &val, sizeof(val));
if (err < 0)
return err;
- cal_x0 = le16_to_cpu(cal_x0);
+ cal_x0 = le16_to_cpu(val);
- err = hw->tf->read(hw->dev, HTS221_REG_1T_CAL_X_L, sizeof(cal_x1),
- (u8 *)&cal_x1);
+ err = regmap_bulk_read(hw->regmap, HTS221_REG_1T_CAL_X_L,
+ &val, sizeof(val));
if (err < 0)
return err;
- cal_x1 = le16_to_cpu(cal_x1);
+ cal_x1 = le16_to_cpu(val);
slope = &hw->sensors[HTS221_SENSOR_T].slope;
b_gen = &hw->sensors[HTS221_SENSOR_T].b_gen;
@@ -332,33 +308,31 @@ static int hts221_parse_temp_caldata(struct hts221_hw *hw)
static int hts221_parse_rh_caldata(struct hts221_hw *hw)
{
- int err, *slope, *b_gen;
+ int err, *slope, *b_gen, data;
s16 cal_x0, cal_x1, cal_y0, cal_y1;
- u8 data;
+ __le16 val;
- err = hw->tf->read(hw->dev, HTS221_REG_0RH_CAL_Y_H, sizeof(data),
- &data);
+ err = regmap_read(hw->regmap, HTS221_REG_0RH_CAL_Y_H, &data);
if (err < 0)
return err;
cal_y0 = data;
- err = hw->tf->read(hw->dev, HTS221_REG_1RH_CAL_Y_H, sizeof(data),
- &data);
+ err = regmap_read(hw->regmap, HTS221_REG_1RH_CAL_Y_H, &data);
if (err < 0)
return err;
cal_y1 = data;
- err = hw->tf->read(hw->dev, HTS221_REG_0RH_CAL_X_H, sizeof(cal_x0),
- (u8 *)&cal_x0);
+ err = regmap_bulk_read(hw->regmap, HTS221_REG_0RH_CAL_X_H,
+ &val, sizeof(val));
if (err < 0)
return err;
- cal_x0 = le16_to_cpu(cal_x0);
+ cal_x0 = le16_to_cpu(val);
- err = hw->tf->read(hw->dev, HTS221_REG_1RH_CAL_X_H, sizeof(cal_x1),
- (u8 *)&cal_x1);
+ err = regmap_bulk_read(hw->regmap, HTS221_REG_1RH_CAL_X_H,
+ &val, sizeof(val));
if (err < 0)
return err;
- cal_x1 = le16_to_cpu(cal_x1);
+ cal_x1 = le16_to_cpu(val);
slope = &hw->sensors[HTS221_SENSOR_H].slope;
b_gen = &hw->sensors[HTS221_SENSOR_H].b_gen;
@@ -431,7 +405,7 @@ static int hts221_get_sensor_offset(struct hts221_hw *hw,
static int hts221_read_oneshot(struct hts221_hw *hw, u8 addr, int *val)
{
- u8 data[HTS221_DATA_SIZE];
+ __le16 data;
int err;
err = hts221_set_enable(hw, true);
@@ -440,13 +414,13 @@ static int hts221_read_oneshot(struct hts221_hw *hw, u8 addr, int *val)
msleep(50);
- err = hw->tf->read(hw->dev, addr, sizeof(data), data);
+ err = regmap_bulk_read(hw->regmap, addr, &data, sizeof(data));
if (err < 0)
return err;
hts221_set_enable(hw, false);
- *val = (s16)get_unaligned_le16(data);
+ *val = (s16)le16_to_cpu(data);
return IIO_VAL_INT;
}
@@ -582,7 +556,7 @@ static const struct iio_info hts221_info = {
static const unsigned long hts221_scan_masks[] = {0x3, 0x0};
int hts221_probe(struct device *dev, int irq, const char *name,
- const struct hts221_transfer_function *tf_ops)
+ struct regmap *regmap)
{
struct iio_dev *iio_dev;
struct hts221_hw *hw;
@@ -599,9 +573,7 @@ int hts221_probe(struct device *dev, int irq, const char *name,
hw->name = name;
hw->dev = dev;
hw->irq = irq;
- hw->tf = tf_ops;
-
- mutex_init(&hw->lock);
+ hw->regmap = regmap;
err = hts221_check_whoami(hw);
if (err < 0)
@@ -616,8 +588,9 @@ int hts221_probe(struct device *dev, int irq, const char *name,
iio_dev->info = &hts221_info;
/* enable Block Data Update */
- err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR,
- HTS221_BDU_MASK, 1);
+ err = regmap_update_bits(hw->regmap, HTS221_REG_CNTRL1_ADDR,
+ HTS221_BDU_MASK,
+ FIELD_PREP(HTS221_BDU_MASK, 1));
if (err < 0)
return err;
@@ -673,12 +646,10 @@ static int __maybe_unused hts221_suspend(struct device *dev)
{
struct iio_dev *iio_dev = dev_get_drvdata(dev);
struct hts221_hw *hw = iio_priv(iio_dev);
- int err;
- err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR,
- HTS221_ENABLE_MASK, false);
-
- return err < 0 ? err : 0;
+ return regmap_update_bits(hw->regmap, HTS221_REG_CNTRL1_ADDR,
+ HTS221_ENABLE_MASK,
+ FIELD_PREP(HTS221_ENABLE_MASK, false));
}
static int __maybe_unused hts221_resume(struct device *dev)
@@ -688,9 +659,10 @@ static int __maybe_unused hts221_resume(struct device *dev)
int err = 0;
if (hw->enabled)
- err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR,
- HTS221_ENABLE_MASK, true);
-
+ err = regmap_update_bits(hw->regmap, HTS221_REG_CNTRL1_ADDR,
+ HTS221_ENABLE_MASK,
+ FIELD_PREP(HTS221_ENABLE_MASK,
+ true));
return err;
}
diff --git a/drivers/iio/humidity/hts221_i2c.c b/drivers/iio/humidity/hts221_i2c.c
index 2c97350a0f76..b5b3f408a658 100644
--- a/drivers/iio/humidity/hts221_i2c.c
+++ b/drivers/iio/humidity/hts221_i2c.c
@@ -13,61 +13,33 @@
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <linux/slab.h>
-#include "hts221.h"
-
-#define I2C_AUTO_INCREMENT 0x80
-
-static int hts221_i2c_read(struct device *dev, u8 addr, int len, u8 *data)
-{
- struct i2c_msg msg[2];
- struct i2c_client *client = to_i2c_client(dev);
-
- if (len > 1)
- addr |= I2C_AUTO_INCREMENT;
-
- msg[0].addr = client->addr;
- msg[0].flags = client->flags;
- msg[0].len = 1;
- msg[0].buf = &addr;
-
- msg[1].addr = client->addr;
- msg[1].flags = client->flags | I2C_M_RD;
- msg[1].len = len;
- msg[1].buf = data;
-
- return i2c_transfer(client->adapter, msg, 2);
-}
+#include <linux/regmap.h>
-static int hts221_i2c_write(struct device *dev, u8 addr, int len, u8 *data)
-{
- u8 send[len + 1];
- struct i2c_msg msg;
- struct i2c_client *client = to_i2c_client(dev);
-
- if (len > 1)
- addr |= I2C_AUTO_INCREMENT;
-
- send[0] = addr;
- memcpy(&send[1], data, len * sizeof(u8));
-
- msg.addr = client->addr;
- msg.flags = client->flags;
- msg.len = len + 1;
- msg.buf = send;
+#include "hts221.h"
- return i2c_transfer(client->adapter, &msg, 1);
-}
+#define HTS221_I2C_AUTO_INCREMENT BIT(7)
-static const struct hts221_transfer_function hts221_transfer_fn = {
- .read = hts221_i2c_read,
- .write = hts221_i2c_write,
+static const struct regmap_config hts221_i2c_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .write_flag_mask = HTS221_I2C_AUTO_INCREMENT,
+ .read_flag_mask = HTS221_I2C_AUTO_INCREMENT,
};
static int hts221_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_i2c(client, &hts221_i2c_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&client->dev, "Failed to register i2c regmap %d\n",
+ (int)PTR_ERR(regmap));
+ return PTR_ERR(regmap);
+ }
+
return hts221_probe(&client->dev, client->irq,
- client->name, &hts221_transfer_fn);
+ client->name, regmap);
}
static const struct acpi_device_id hts221_acpi_match[] = {
diff --git a/drivers/iio/humidity/hts221_spi.c b/drivers/iio/humidity/hts221_spi.c
index 55b29b53b9d1..9c005f037026 100644
--- a/drivers/iio/humidity/hts221_spi.c
+++ b/drivers/iio/humidity/hts221_spi.c
@@ -12,76 +12,33 @@
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
-#include "hts221.h"
-
-#define SENSORS_SPI_READ 0x80
-#define SPI_AUTO_INCREMENT 0x40
-
-static int hts221_spi_read(struct device *dev, u8 addr, int len, u8 *data)
-{
- int err;
- struct spi_device *spi = to_spi_device(dev);
- struct iio_dev *iio_dev = spi_get_drvdata(spi);
- struct hts221_hw *hw = iio_priv(iio_dev);
-
- struct spi_transfer xfers[] = {
- {
- .tx_buf = hw->tb.tx_buf,
- .bits_per_word = 8,
- .len = 1,
- },
- {
- .rx_buf = hw->tb.rx_buf,
- .bits_per_word = 8,
- .len = len,
- }
- };
-
- if (len > 1)
- addr |= SPI_AUTO_INCREMENT;
- hw->tb.tx_buf[0] = addr | SENSORS_SPI_READ;
-
- err = spi_sync_transfer(spi, xfers, ARRAY_SIZE(xfers));
- if (err < 0)
- return err;
-
- memcpy(data, hw->tb.rx_buf, len * sizeof(u8));
-
- return len;
-}
-
-static int hts221_spi_write(struct device *dev, u8 addr, int len, u8 *data)
-{
- struct spi_device *spi = to_spi_device(dev);
- struct iio_dev *iio_dev = spi_get_drvdata(spi);
- struct hts221_hw *hw = iio_priv(iio_dev);
-
- struct spi_transfer xfers = {
- .tx_buf = hw->tb.tx_buf,
- .bits_per_word = 8,
- .len = len + 1,
- };
-
- if (len >= HTS221_TX_MAX_LENGTH)
- return -ENOMEM;
+#include <linux/regmap.h>
- if (len > 1)
- addr |= SPI_AUTO_INCREMENT;
- hw->tb.tx_buf[0] = addr;
- memcpy(&hw->tb.tx_buf[1], data, len);
+#include "hts221.h"
- return spi_sync_transfer(spi, &xfers, 1);
-}
+#define HTS221_SPI_READ BIT(7)
+#define HTS221_SPI_AUTO_INCREMENT BIT(6)
-static const struct hts221_transfer_function hts221_transfer_fn = {
- .read = hts221_spi_read,
- .write = hts221_spi_write,
+static const struct regmap_config hts221_spi_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .write_flag_mask = HTS221_SPI_AUTO_INCREMENT,
+ .read_flag_mask = HTS221_SPI_READ | HTS221_SPI_AUTO_INCREMENT,
};
static int hts221_spi_probe(struct spi_device *spi)
{
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_spi(spi, &hts221_spi_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&spi->dev, "Failed to register spi regmap %d\n",
+ (int)PTR_ERR(regmap));
+ return PTR_ERR(regmap);
+ }
+
return hts221_probe(&spi->dev, spi->irq,
- spi->modalias, &hts221_transfer_fn);
+ spi->modalias, regmap);
}
static const struct of_device_id hts221_spi_of_match[] = {
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
index 8fdd723afa05..a3cc7cd97026 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
@@ -27,7 +27,7 @@ enum st_lsm6dsx_hw_id {
ST_LSM6DSX_MAX_ID,
};
-#define ST_LSM6DSX_BUFF_SIZE 256
+#define ST_LSM6DSX_BUFF_SIZE 400
#define ST_LSM6DSX_CHAN_SIZE 2
#define ST_LSM6DSX_SAMPLE_SIZE 6
#define ST_LSM6DSX_MAX_WORD_LEN ((32 / ST_LSM6DSX_SAMPLE_SIZE) * \
@@ -58,12 +58,27 @@ struct st_lsm6dsx_fifo_ops {
};
/**
+ * struct st_lsm6dsx_hw_ts_settings - ST IMU hw timer settings
+ * @timer_en: Hw timer enable register info (addr + mask).
+ * @hr_timer: Hw timer resolution register info (addr + mask).
+ * @fifo_en: Hw timer FIFO enable register info (addr + mask).
+ * @decimator: Hw timer FIFO decimator register info (addr + mask).
+ */
+struct st_lsm6dsx_hw_ts_settings {
+ struct st_lsm6dsx_reg timer_en;
+ struct st_lsm6dsx_reg hr_timer;
+ struct st_lsm6dsx_reg fifo_en;
+ struct st_lsm6dsx_reg decimator;
+};
+
+/**
* struct st_lsm6dsx_settings - ST IMU sensor settings
* @wai: Sensor WhoAmI default value.
* @max_fifo_size: Sensor max fifo length in FIFO words.
* @id: List of hw id supported by the driver configuration.
* @decimator: List of decimator register info (addr + mask).
* @fifo_ops: Sensor hw FIFO parameters.
+ * @ts_settings: Hw timer related settings.
*/
struct st_lsm6dsx_settings {
u8 wai;
@@ -71,6 +86,7 @@ struct st_lsm6dsx_settings {
enum st_lsm6dsx_hw_id id[ST_LSM6DSX_MAX_ID];
struct st_lsm6dsx_reg decimator[ST_LSM6DSX_MAX_ID];
struct st_lsm6dsx_fifo_ops fifo_ops;
+ struct st_lsm6dsx_hw_ts_settings ts_settings;
};
enum st_lsm6dsx_sensor_id {
@@ -94,8 +110,7 @@ enum st_lsm6dsx_fifo_mode {
* @watermark: Sensor watermark level.
* @sip: Number of samples in a given pattern.
* @decimator: FIFO decimation factor.
- * @delta_ts: Delta time between two consecutive interrupts.
- * @ts: Latest timestamp from the interrupt handler.
+ * @ts_ref: Sensor timestamp reference for hw one.
*/
struct st_lsm6dsx_sensor {
char name[32];
@@ -108,9 +123,7 @@ struct st_lsm6dsx_sensor {
u16 watermark;
u8 sip;
u8 decimator;
-
- s64 delta_ts;
- s64 ts;
+ s64 ts_ref;
};
/**
@@ -122,7 +135,8 @@ struct st_lsm6dsx_sensor {
* @conf_lock: Mutex to prevent concurrent FIFO configuration update.
* @fifo_mode: FIFO operating mode supported by the device.
* @enable_mask: Enabled sensor bitmask.
- * @sip: Total number of samples (acc/gyro) in a given pattern.
+ * @ts_sip: Total number of timestamp samples in a given pattern.
+ * @sip: Total number of samples (acc/gyro/ts) in a given pattern.
* @buff: Device read buffer.
* @iio_devs: Pointers to acc/gyro iio_dev instances.
* @settings: Pointer to the specific sensor settings in use.
@@ -137,6 +151,7 @@ struct st_lsm6dsx_hw {
enum st_lsm6dsx_fifo_mode fifo_mode;
u8 enable_mask;
+ u8 ts_sip;
u8 sip;
u8 *buff;
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
index 1d6aa9b1a4cf..1045e025e92b 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
@@ -46,9 +46,13 @@
#define ST_LSM6DSX_FIFO_ODR_MASK GENMASK(6, 3)
#define ST_LSM6DSX_FIFO_EMPTY_MASK BIT(12)
#define ST_LSM6DSX_REG_FIFO_OUTL_ADDR 0x3e
+#define ST_LSM6DSX_REG_TS_RESET_ADDR 0x42
#define ST_LSM6DSX_MAX_FIFO_ODR_VAL 0x08
+#define ST_LSM6DSX_TS_SENSITIVITY 25000UL /* 25us */
+#define ST_LSM6DSX_TS_RESET_VAL 0xaa
+
struct st_lsm6dsx_decimator_entry {
u8 decimator;
u8 val;
@@ -98,9 +102,10 @@ static void st_lsm6dsx_get_max_min_odr(struct st_lsm6dsx_hw *hw,
static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw)
{
+ u16 max_odr, min_odr, sip = 0, ts_sip = 0;
+ const struct st_lsm6dsx_reg *ts_dec_reg;
struct st_lsm6dsx_sensor *sensor;
- u16 max_odr, min_odr, sip = 0;
- int err, i;
+ int err = 0, i;
u8 data;
st_lsm6dsx_get_max_min_odr(hw, &max_odr, &min_odr);
@@ -119,6 +124,7 @@ static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw)
sensor->decimator = 0;
data = 0;
}
+ ts_sip = max_t(u16, ts_sip, sensor->sip);
dec_reg = &hw->settings->decimator[sensor->id];
if (dec_reg->addr) {
@@ -131,9 +137,23 @@ static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw)
}
sip += sensor->sip;
}
- hw->sip = sip;
+ hw->sip = sip + ts_sip;
+ hw->ts_sip = ts_sip;
- return 0;
+ /*
+ * update hw ts decimator if necessary. Decimator for hw timestamp
+ * is always 1 or 0 in order to have a ts sample for each data
+ * sample in FIFO
+ */
+ ts_dec_reg = &hw->settings->ts_settings.decimator;
+ if (ts_dec_reg->addr) {
+ int val, ts_dec = !!hw->ts_sip;
+
+ val = ST_LSM6DSX_SHIFT_VAL(ts_dec, ts_dec_reg->mask);
+ err = regmap_update_bits(hw->regmap, ts_dec_reg->addr,
+ ts_dec_reg->mask, val);
+ }
+ return err;
}
int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw,
@@ -208,6 +228,28 @@ int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, u16 watermark)
&wdata, sizeof(wdata));
}
+static int st_lsm6dsx_reset_hw_ts(struct st_lsm6dsx_hw *hw)
+{
+ struct st_lsm6dsx_sensor *sensor;
+ int i, err;
+
+ /* reset hw ts counter */
+ err = regmap_write(hw->regmap, ST_LSM6DSX_REG_TS_RESET_ADDR,
+ ST_LSM6DSX_TS_RESET_VAL);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+ sensor = iio_priv(hw->iio_devs[i]);
+ /*
+ * store enable buffer timestamp as reference for
+ * hw timestamp
+ */
+ sensor->ts_ref = iio_get_time_ns(hw->iio_devs[i]);
+ }
+ return 0;
+}
+
/*
* Set max bulk read to ST_LSM6DSX_MAX_WORD_LEN in order to avoid
* a kmalloc for each bus access
@@ -231,6 +273,8 @@ static inline int st_lsm6dsx_read_block(struct st_lsm6dsx_hw *hw, u8 *data,
return 0;
}
+#define ST_LSM6DSX_IIO_BUFF_SIZE (ALIGN(ST_LSM6DSX_SAMPLE_SIZE, \
+ sizeof(s64)) + sizeof(s64))
/**
* st_lsm6dsx_read_fifo() - LSM6DS3-LSM6DS3H-LSM6DSL-LSM6DSM read FIFO routine
* @hw: Pointer to instance of struct st_lsm6dsx_hw.
@@ -243,11 +287,13 @@ static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
{
u16 fifo_len, pattern_len = hw->sip * ST_LSM6DSX_SAMPLE_SIZE;
u16 fifo_diff_mask = hw->settings->fifo_ops.fifo_diff.mask;
- int err, acc_sip, gyro_sip, read_len, samples, offset;
+ int err, acc_sip, gyro_sip, ts_sip, read_len, offset;
struct st_lsm6dsx_sensor *acc_sensor, *gyro_sensor;
- s64 acc_ts, acc_delta_ts, gyro_ts, gyro_delta_ts;
- u8 iio_buff[ALIGN(ST_LSM6DSX_SAMPLE_SIZE, sizeof(s64)) + sizeof(s64)];
+ u8 gyro_buff[ST_LSM6DSX_IIO_BUFF_SIZE];
+ u8 acc_buff[ST_LSM6DSX_IIO_BUFF_SIZE];
+ bool reset_ts = false;
__le16 fifo_status;
+ s64 ts = 0;
err = regmap_bulk_read(hw->regmap,
hw->settings->fifo_ops.fifo_diff.addr,
@@ -260,23 +306,10 @@ static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
fifo_len = (le16_to_cpu(fifo_status) & fifo_diff_mask) *
ST_LSM6DSX_CHAN_SIZE;
- samples = fifo_len / ST_LSM6DSX_SAMPLE_SIZE;
fifo_len = (fifo_len / pattern_len) * pattern_len;
- /*
- * compute delta timestamp between two consecutive samples
- * in order to estimate queueing time of data generated
- * by the sensor
- */
acc_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
- acc_ts = acc_sensor->ts - acc_sensor->delta_ts;
- acc_delta_ts = div_s64(acc_sensor->delta_ts * acc_sensor->decimator,
- samples);
-
gyro_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_GYRO]);
- gyro_ts = gyro_sensor->ts - gyro_sensor->delta_ts;
- gyro_delta_ts = div_s64(gyro_sensor->delta_ts * gyro_sensor->decimator,
- samples);
for (read_len = 0; read_len < fifo_len; read_len += pattern_len) {
err = st_lsm6dsx_read_block(hw, hw->buff, pattern_len);
@@ -287,7 +320,7 @@ static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
* Data are written to the FIFO with a specific pattern
* depending on the configured ODRs. The first sequence of data
* stored in FIFO contains the data of all enabled sensors
- * (e.g. Gx, Gy, Gz, Ax, Ay, Az), then data are repeated
+ * (e.g. Gx, Gy, Gz, Ax, Ay, Az, Ts), then data are repeated
* depending on the value of the decimation factor set for each
* sensor.
*
@@ -296,35 +329,65 @@ static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
* - gyroscope ODR = 208Hz, accelerometer ODR = 104Hz
* Since the gyroscope ODR is twice the accelerometer one, the
* following pattern is repeated every 9 samples:
- * - Gx, Gy, Gz, Ax, Ay, Az, Gx, Gy, Gz
+ * - Gx, Gy, Gz, Ax, Ay, Az, Ts, Gx, Gy, Gz, Ts, Gx, ..
*/
gyro_sip = gyro_sensor->sip;
acc_sip = acc_sensor->sip;
+ ts_sip = hw->ts_sip;
offset = 0;
while (acc_sip > 0 || gyro_sip > 0) {
- if (gyro_sip-- > 0) {
- memcpy(iio_buff, &hw->buff[offset],
+ if (gyro_sip > 0) {
+ memcpy(gyro_buff, &hw->buff[offset],
ST_LSM6DSX_SAMPLE_SIZE);
- iio_push_to_buffers_with_timestamp(
- hw->iio_devs[ST_LSM6DSX_ID_GYRO],
- iio_buff, gyro_ts);
offset += ST_LSM6DSX_SAMPLE_SIZE;
- gyro_ts += gyro_delta_ts;
}
-
- if (acc_sip-- > 0) {
- memcpy(iio_buff, &hw->buff[offset],
+ if (acc_sip > 0) {
+ memcpy(acc_buff, &hw->buff[offset],
ST_LSM6DSX_SAMPLE_SIZE);
- iio_push_to_buffers_with_timestamp(
- hw->iio_devs[ST_LSM6DSX_ID_ACC],
- iio_buff, acc_ts);
offset += ST_LSM6DSX_SAMPLE_SIZE;
- acc_ts += acc_delta_ts;
}
+
+ if (ts_sip-- > 0) {
+ u8 data[ST_LSM6DSX_SAMPLE_SIZE];
+
+ memcpy(data, &hw->buff[offset], sizeof(data));
+ /*
+ * hw timestamp is 3B long and it is stored
+ * in FIFO using 6B as 4th FIFO data set
+ * according to this schema:
+ * B0 = ts[15:8], B1 = ts[23:16], B3 = ts[7:0]
+ */
+ ts = data[1] << 16 | data[0] << 8 | data[3];
+ /*
+ * check if hw timestamp engine is going to
+ * reset (the sensor generates an interrupt
+ * to signal the hw timestamp will reset in
+ * 1.638s)
+ */
+ if (!reset_ts && ts >= 0xff0000)
+ reset_ts = true;
+ ts *= ST_LSM6DSX_TS_SENSITIVITY;
+
+ offset += ST_LSM6DSX_SAMPLE_SIZE;
+ }
+
+ if (gyro_sip-- > 0)
+ iio_push_to_buffers_with_timestamp(
+ hw->iio_devs[ST_LSM6DSX_ID_GYRO],
+ gyro_buff, gyro_sensor->ts_ref + ts);
+ if (acc_sip-- > 0)
+ iio_push_to_buffers_with_timestamp(
+ hw->iio_devs[ST_LSM6DSX_ID_ACC],
+ acc_buff, acc_sensor->ts_ref + ts);
}
}
+ if (unlikely(reset_ts)) {
+ err = st_lsm6dsx_reset_hw_ts(hw);
+ if (err < 0)
+ return err;
+ }
return read_len;
}
@@ -379,15 +442,12 @@ static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable)
goto out;
if (hw->enable_mask) {
- err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_CONT);
+ /* reset hw ts counter */
+ err = st_lsm6dsx_reset_hw_ts(hw);
if (err < 0)
goto out;
- /*
- * store enable buffer timestamp as reference to compute
- * first delta timestamp
- */
- sensor->ts = iio_get_time_ns(iio_dev);
+ err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_CONT);
}
out:
@@ -399,25 +459,8 @@ out:
static irqreturn_t st_lsm6dsx_handler_irq(int irq, void *private)
{
struct st_lsm6dsx_hw *hw = private;
- struct st_lsm6dsx_sensor *sensor;
- int i;
-
- if (!hw->sip)
- return IRQ_NONE;
-
- for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
- sensor = iio_priv(hw->iio_devs[i]);
-
- if (sensor->sip > 0) {
- s64 timestamp;
-
- timestamp = iio_get_time_ns(hw->iio_devs[i]);
- sensor->delta_ts = timestamp - sensor->ts;
- sensor->ts = timestamp;
- }
- }
- return IRQ_WAKE_THREAD;
+ return hw->sip > 0 ? IRQ_WAKE_THREAD : IRQ_NONE;
}
static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private)
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
index c2fa3239b9c6..8656d72ef4ee 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
@@ -181,6 +181,24 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
},
.th_wl = 3, /* 1LSB = 2B */
},
+ .ts_settings = {
+ .timer_en = {
+ .addr = 0x58,
+ .mask = BIT(7),
+ },
+ .hr_timer = {
+ .addr = 0x5c,
+ .mask = BIT(4),
+ },
+ .fifo_en = {
+ .addr = 0x07,
+ .mask = BIT(7),
+ },
+ .decimator = {
+ .addr = 0x09,
+ .mask = GENMASK(5, 3),
+ },
+ },
},
{
.wai = 0x69,
@@ -209,6 +227,24 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
},
.th_wl = 3, /* 1LSB = 2B */
},
+ .ts_settings = {
+ .timer_en = {
+ .addr = 0x58,
+ .mask = BIT(7),
+ },
+ .hr_timer = {
+ .addr = 0x5c,
+ .mask = BIT(4),
+ },
+ .fifo_en = {
+ .addr = 0x07,
+ .mask = BIT(7),
+ },
+ .decimator = {
+ .addr = 0x09,
+ .mask = GENMASK(5, 3),
+ },
+ },
},
{
.wai = 0x6a,
@@ -238,6 +274,24 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
},
.th_wl = 3, /* 1LSB = 2B */
},
+ .ts_settings = {
+ .timer_en = {
+ .addr = 0x19,
+ .mask = BIT(5),
+ },
+ .hr_timer = {
+ .addr = 0x5c,
+ .mask = BIT(4),
+ },
+ .fifo_en = {
+ .addr = 0x07,
+ .mask = BIT(7),
+ },
+ .decimator = {
+ .addr = 0x09,
+ .mask = GENMASK(5, 3),
+ },
+ },
},
};
@@ -630,6 +684,44 @@ static int st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, u8 *drdy_reg)
return err;
}
+static int st_lsm6dsx_init_hw_timer(struct st_lsm6dsx_hw *hw)
+{
+ const struct st_lsm6dsx_hw_ts_settings *ts_settings;
+ int err, val;
+
+ ts_settings = &hw->settings->ts_settings;
+ /* enable hw timestamp generation if necessary */
+ if (ts_settings->timer_en.addr) {
+ val = ST_LSM6DSX_SHIFT_VAL(1, ts_settings->timer_en.mask);
+ err = regmap_update_bits(hw->regmap,
+ ts_settings->timer_en.addr,
+ ts_settings->timer_en.mask, val);
+ if (err < 0)
+ return err;
+ }
+
+ /* enable high resolution for hw ts timer if necessary */
+ if (ts_settings->hr_timer.addr) {
+ val = ST_LSM6DSX_SHIFT_VAL(1, ts_settings->hr_timer.mask);
+ err = regmap_update_bits(hw->regmap,
+ ts_settings->hr_timer.addr,
+ ts_settings->hr_timer.mask, val);
+ if (err < 0)
+ return err;
+ }
+
+ /* enable ts queueing in FIFO if necessary */
+ if (ts_settings->fifo_en.addr) {
+ val = ST_LSM6DSX_SHIFT_VAL(1, ts_settings->fifo_en.mask);
+ err = regmap_update_bits(hw->regmap,
+ ts_settings->fifo_en.addr,
+ ts_settings->fifo_en.mask, val);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
{
u8 drdy_int_reg;
@@ -654,10 +746,14 @@ static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
if (err < 0)
return err;
- return regmap_update_bits(hw->regmap, drdy_int_reg,
- ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK,
- FIELD_PREP(ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK,
- 1));
+ err = regmap_update_bits(hw->regmap, drdy_int_reg,
+ ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK,
+ FIELD_PREP(ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK,
+ 1));
+ if (err < 0)
+ return err;
+
+ return st_lsm6dsx_init_hw_timer(hw);
}
static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index 93fd421b10d7..074e50657366 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -275,6 +275,16 @@ config LTR501
This driver can also be built as a module. If so, the module
will be called ltr501.
+config LV0104CS
+ tristate "LV0104CS Ambient Light Sensor"
+ depends on I2C
+ help
+ Say Y here if you want to build support for the On Semiconductor
+ LV0104CS ambient light sensor.
+
+ To compile this driver as a module, choose M here:
+ the module will be called lv0104cs.
+
config MAX44000
tristate "MAX44000 Ambient and Infrared Proximity Sensor"
depends on I2C
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
index f714067a7816..f1777036d4f8 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_ISL29125) += isl29125.o
obj-$(CONFIG_JSA1212) += jsa1212.o
obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
obj-$(CONFIG_LTR501) += ltr501.o
+obj-$(CONFIG_LV0104CS) += lv0104cs.o
obj-$(CONFIG_MAX44000) += max44000.o
obj-$(CONFIG_OPT3001) += opt3001.o
obj-$(CONFIG_PA12203001) += pa12203001.o
diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c
index a8fa00e31c39..1f112ae15f3c 100644
--- a/drivers/iio/light/apds9960.c
+++ b/drivers/iio/light/apds9960.c
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* apds9960.c - Support for Avago APDS9960 gesture/RGB/ALS/proximity sensor
*
- * Copyright (C) 2015 Matt Ranostay <mranostay@gmail.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.
+ * Copyright (C) 2015, 2018
+ * Author: Matt Ranostay <matt.ranostay@konsulko.com>
*
* TODO: gesture + proximity calib offsets
*/
@@ -1141,6 +1133,6 @@ static struct i2c_driver apds9960_driver = {
};
module_i2c_driver(apds9960_driver);
-MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
MODULE_DESCRIPTION("ADPS9960 Gesture/RGB/ALS/Proximity sensor");
MODULE_LICENSE("GPL");
diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c
index acfad4aeb27a..8e8a0e7f78d1 100644
--- a/drivers/iio/light/cros_ec_light_prox.c
+++ b/drivers/iio/light/cros_ec_light_prox.c
@@ -276,6 +276,7 @@ MODULE_DEVICE_TABLE(platform, cros_ec_light_prox_ids);
static struct platform_driver cros_ec_light_prox_platform_driver = {
.driver = {
.name = "cros-ec-light-prox",
+ .pm = &cros_ec_sensors_pm_ops,
},
.probe = cros_ec_light_prox_probe,
.id_table = cros_ec_light_prox_ids,
diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c
index befd693a4a31..406caaee9a3c 100644
--- a/drivers/iio/light/hid-sensor-als.c
+++ b/drivers/iio/light/hid-sensor-als.c
@@ -97,7 +97,7 @@ static int als_read_raw(struct iio_dev *indio_dev,
*val = 0;
*val2 = 0;
switch (mask) {
- case 0:
+ case IIO_CHAN_INFO_RAW:
switch (chan->scan_index) {
case CHANNEL_SCAN_INDEX_INTENSITY:
case CHANNEL_SCAN_INDEX_ILLUM:
diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c
index 36208a3652e9..ff5a3324b489 100644
--- a/drivers/iio/light/lm3533-als.c
+++ b/drivers/iio/light/lm3533-als.c
@@ -199,7 +199,7 @@ static int lm3533_als_read_raw(struct iio_dev *indio_dev,
int ret;
switch (mask) {
- case 0:
+ case IIO_CHAN_INFO_RAW:
switch (chan->type) {
case IIO_LIGHT:
ret = lm3533_als_get_adc(indio_dev, false, val);
diff --git a/drivers/iio/light/lv0104cs.c b/drivers/iio/light/lv0104cs.c
new file mode 100644
index 000000000000..55b8e2855647
--- /dev/null
+++ b/drivers/iio/light/lv0104cs.c
@@ -0,0 +1,531 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * lv0104cs.c: LV0104CS Ambient Light Sensor Driver
+ *
+ * Copyright (C) 2018
+ * Author: Jeff LaBundy <jeff@labundy.com>
+ *
+ * 7-bit I2C slave address: 0x13
+ *
+ * Link to data sheet: http://www.onsemi.com/pub/Collateral/LV0104CS-D.PDF
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define LV0104CS_REGVAL_MEASURE 0xE0
+#define LV0104CS_REGVAL_SLEEP 0x00
+
+#define LV0104CS_SCALE_0_25X 0
+#define LV0104CS_SCALE_1X 1
+#define LV0104CS_SCALE_2X 2
+#define LV0104CS_SCALE_8X 3
+#define LV0104CS_SCALE_SHIFT 3
+
+#define LV0104CS_INTEG_12_5MS 0
+#define LV0104CS_INTEG_100MS 1
+#define LV0104CS_INTEG_200MS 2
+#define LV0104CS_INTEG_SHIFT 1
+
+#define LV0104CS_CALIBSCALE_UNITY 31
+
+struct lv0104cs_private {
+ struct i2c_client *client;
+ struct mutex lock;
+ u8 calibscale;
+ u8 scale;
+ u8 int_time;
+};
+
+struct lv0104cs_mapping {
+ int val;
+ int val2;
+ u8 regval;
+};
+
+static const struct lv0104cs_mapping lv0104cs_calibscales[] = {
+ { 0, 666666, 0x81 },
+ { 0, 800000, 0x82 },
+ { 0, 857142, 0x83 },
+ { 0, 888888, 0x84 },
+ { 0, 909090, 0x85 },
+ { 0, 923076, 0x86 },
+ { 0, 933333, 0x87 },
+ { 0, 941176, 0x88 },
+ { 0, 947368, 0x89 },
+ { 0, 952380, 0x8A },
+ { 0, 956521, 0x8B },
+ { 0, 960000, 0x8C },
+ { 0, 962962, 0x8D },
+ { 0, 965517, 0x8E },
+ { 0, 967741, 0x8F },
+ { 0, 969696, 0x90 },
+ { 0, 971428, 0x91 },
+ { 0, 972972, 0x92 },
+ { 0, 974358, 0x93 },
+ { 0, 975609, 0x94 },
+ { 0, 976744, 0x95 },
+ { 0, 977777, 0x96 },
+ { 0, 978723, 0x97 },
+ { 0, 979591, 0x98 },
+ { 0, 980392, 0x99 },
+ { 0, 981132, 0x9A },
+ { 0, 981818, 0x9B },
+ { 0, 982456, 0x9C },
+ { 0, 983050, 0x9D },
+ { 0, 983606, 0x9E },
+ { 0, 984126, 0x9F },
+ { 1, 0, 0x80 },
+ { 1, 16129, 0xBF },
+ { 1, 16666, 0xBE },
+ { 1, 17241, 0xBD },
+ { 1, 17857, 0xBC },
+ { 1, 18518, 0xBB },
+ { 1, 19230, 0xBA },
+ { 1, 20000, 0xB9 },
+ { 1, 20833, 0xB8 },
+ { 1, 21739, 0xB7 },
+ { 1, 22727, 0xB6 },
+ { 1, 23809, 0xB5 },
+ { 1, 24999, 0xB4 },
+ { 1, 26315, 0xB3 },
+ { 1, 27777, 0xB2 },
+ { 1, 29411, 0xB1 },
+ { 1, 31250, 0xB0 },
+ { 1, 33333, 0xAF },
+ { 1, 35714, 0xAE },
+ { 1, 38461, 0xAD },
+ { 1, 41666, 0xAC },
+ { 1, 45454, 0xAB },
+ { 1, 50000, 0xAA },
+ { 1, 55555, 0xA9 },
+ { 1, 62500, 0xA8 },
+ { 1, 71428, 0xA7 },
+ { 1, 83333, 0xA6 },
+ { 1, 100000, 0xA5 },
+ { 1, 125000, 0xA4 },
+ { 1, 166666, 0xA3 },
+ { 1, 250000, 0xA2 },
+ { 1, 500000, 0xA1 },
+};
+
+static const struct lv0104cs_mapping lv0104cs_scales[] = {
+ { 0, 250000, LV0104CS_SCALE_0_25X << LV0104CS_SCALE_SHIFT },
+ { 1, 0, LV0104CS_SCALE_1X << LV0104CS_SCALE_SHIFT },
+ { 2, 0, LV0104CS_SCALE_2X << LV0104CS_SCALE_SHIFT },
+ { 8, 0, LV0104CS_SCALE_8X << LV0104CS_SCALE_SHIFT },
+};
+
+static const struct lv0104cs_mapping lv0104cs_int_times[] = {
+ { 0, 12500, LV0104CS_INTEG_12_5MS << LV0104CS_INTEG_SHIFT },
+ { 0, 100000, LV0104CS_INTEG_100MS << LV0104CS_INTEG_SHIFT },
+ { 0, 200000, LV0104CS_INTEG_200MS << LV0104CS_INTEG_SHIFT },
+};
+
+static int lv0104cs_write_reg(struct i2c_client *client, u8 regval)
+{
+ int ret;
+
+ ret = i2c_master_send(client, (char *)&regval, sizeof(regval));
+ if (ret < 0)
+ return ret;
+ if (ret != sizeof(regval))
+ return -EIO;
+
+ return 0;
+}
+
+static int lv0104cs_read_adc(struct i2c_client *client, u16 *adc_output)
+{
+ __be16 regval;
+ int ret;
+
+ ret = i2c_master_recv(client, (char *)&regval, sizeof(regval));
+ if (ret < 0)
+ return ret;
+ if (ret != sizeof(regval))
+ return -EIO;
+
+ *adc_output = be16_to_cpu(regval);
+
+ return 0;
+}
+
+static int lv0104cs_get_lux(struct lv0104cs_private *lv0104cs,
+ int *val, int *val2)
+{
+ u8 regval = LV0104CS_REGVAL_MEASURE;
+ u16 adc_output;
+ int ret;
+
+ regval |= lv0104cs_scales[lv0104cs->scale].regval;
+ regval |= lv0104cs_int_times[lv0104cs->int_time].regval;
+ ret = lv0104cs_write_reg(lv0104cs->client, regval);
+ if (ret)
+ return ret;
+
+ /* wait for integration time to pass (with margin) */
+ switch (lv0104cs->int_time) {
+ case LV0104CS_INTEG_12_5MS:
+ msleep(50);
+ break;
+
+ case LV0104CS_INTEG_100MS:
+ msleep(150);
+ break;
+
+ case LV0104CS_INTEG_200MS:
+ msleep(250);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ ret = lv0104cs_read_adc(lv0104cs->client, &adc_output);
+ if (ret)
+ return ret;
+
+ ret = lv0104cs_write_reg(lv0104cs->client, LV0104CS_REGVAL_SLEEP);
+ if (ret)
+ return ret;
+
+ /* convert ADC output to lux */
+ switch (lv0104cs->scale) {
+ case LV0104CS_SCALE_0_25X:
+ *val = adc_output * 4;
+ *val2 = 0;
+ return 0;
+
+ case LV0104CS_SCALE_1X:
+ *val = adc_output;
+ *val2 = 0;
+ return 0;
+
+ case LV0104CS_SCALE_2X:
+ *val = adc_output / 2;
+ *val2 = (adc_output % 2) * 500000;
+ return 0;
+
+ case LV0104CS_SCALE_8X:
+ *val = adc_output / 8;
+ *val2 = (adc_output % 8) * 125000;
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int lv0104cs_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct lv0104cs_private *lv0104cs = iio_priv(indio_dev);
+ int ret;
+
+ if (chan->type != IIO_LIGHT)
+ return -EINVAL;
+
+ mutex_lock(&lv0104cs->lock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ ret = lv0104cs_get_lux(lv0104cs, val, val2);
+ if (ret)
+ goto err_mutex;
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ break;
+
+ case IIO_CHAN_INFO_CALIBSCALE:
+ *val = lv0104cs_calibscales[lv0104cs->calibscale].val;
+ *val2 = lv0104cs_calibscales[lv0104cs->calibscale].val2;
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ break;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = lv0104cs_scales[lv0104cs->scale].val;
+ *val2 = lv0104cs_scales[lv0104cs->scale].val2;
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ break;
+
+ case IIO_CHAN_INFO_INT_TIME:
+ *val = lv0104cs_int_times[lv0104cs->int_time].val;
+ *val2 = lv0104cs_int_times[lv0104cs->int_time].val2;
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+err_mutex:
+ mutex_unlock(&lv0104cs->lock);
+
+ return ret;
+}
+
+static int lv0104cs_set_calibscale(struct lv0104cs_private *lv0104cs,
+ int val, int val2)
+{
+ int calibscale = val * 1000000 + val2;
+ int floor, ceil, mid;
+ int ret, i, index;
+
+ /* round to nearest quantized calibscale (sensitivity) */
+ for (i = 0; i < ARRAY_SIZE(lv0104cs_calibscales) - 1; i++) {
+ floor = lv0104cs_calibscales[i].val * 1000000
+ + lv0104cs_calibscales[i].val2;
+ ceil = lv0104cs_calibscales[i + 1].val * 1000000
+ + lv0104cs_calibscales[i + 1].val2;
+ mid = (floor + ceil) / 2;
+
+ /* round down */
+ if (calibscale >= floor && calibscale < mid) {
+ index = i;
+ break;
+ }
+
+ /* round up */
+ if (calibscale >= mid && calibscale <= ceil) {
+ index = i + 1;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(lv0104cs_calibscales) - 1)
+ return -EINVAL;
+
+ mutex_lock(&lv0104cs->lock);
+
+ /* set calibscale (sensitivity) */
+ ret = lv0104cs_write_reg(lv0104cs->client,
+ lv0104cs_calibscales[index].regval);
+ if (ret)
+ goto err_mutex;
+
+ lv0104cs->calibscale = index;
+
+err_mutex:
+ mutex_unlock(&lv0104cs->lock);
+
+ return ret;
+}
+
+static int lv0104cs_set_scale(struct lv0104cs_private *lv0104cs,
+ int val, int val2)
+{
+ int i;
+
+ /* hard matching */
+ for (i = 0; i < ARRAY_SIZE(lv0104cs_scales); i++) {
+ if (val != lv0104cs_scales[i].val)
+ continue;
+
+ if (val2 == lv0104cs_scales[i].val2)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(lv0104cs_scales))
+ return -EINVAL;
+
+ mutex_lock(&lv0104cs->lock);
+ lv0104cs->scale = i;
+ mutex_unlock(&lv0104cs->lock);
+
+ return 0;
+}
+
+static int lv0104cs_set_int_time(struct lv0104cs_private *lv0104cs,
+ int val, int val2)
+{
+ int i;
+
+ /* hard matching */
+ for (i = 0; i < ARRAY_SIZE(lv0104cs_int_times); i++) {
+ if (val != lv0104cs_int_times[i].val)
+ continue;
+
+ if (val2 == lv0104cs_int_times[i].val2)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(lv0104cs_int_times))
+ return -EINVAL;
+
+ mutex_lock(&lv0104cs->lock);
+ lv0104cs->int_time = i;
+ mutex_unlock(&lv0104cs->lock);
+
+ return 0;
+}
+
+static int lv0104cs_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct lv0104cs_private *lv0104cs = iio_priv(indio_dev);
+
+ if (chan->type != IIO_LIGHT)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBSCALE:
+ return lv0104cs_set_calibscale(lv0104cs, val, val2);
+
+ case IIO_CHAN_INFO_SCALE:
+ return lv0104cs_set_scale(lv0104cs, val, val2);
+
+ case IIO_CHAN_INFO_INT_TIME:
+ return lv0104cs_set_int_time(lv0104cs, val, val2);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static ssize_t lv0104cs_show_calibscale_avail(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t len = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(lv0104cs_calibscales); i++) {
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06d ",
+ lv0104cs_calibscales[i].val,
+ lv0104cs_calibscales[i].val2);
+ }
+
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static ssize_t lv0104cs_show_scale_avail(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t len = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(lv0104cs_scales); i++) {
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06d ",
+ lv0104cs_scales[i].val,
+ lv0104cs_scales[i].val2);
+ }
+
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static ssize_t lv0104cs_show_int_time_avail(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t len = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(lv0104cs_int_times); i++) {
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06d ",
+ lv0104cs_int_times[i].val,
+ lv0104cs_int_times[i].val2);
+ }
+
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(calibscale_available, 0444,
+ lv0104cs_show_calibscale_avail, NULL, 0);
+static IIO_DEVICE_ATTR(scale_available, 0444,
+ lv0104cs_show_scale_avail, NULL, 0);
+static IIO_DEV_ATTR_INT_TIME_AVAIL(lv0104cs_show_int_time_avail);
+
+static struct attribute *lv0104cs_attributes[] = {
+ &iio_dev_attr_calibscale_available.dev_attr.attr,
+ &iio_dev_attr_scale_available.dev_attr.attr,
+ &iio_dev_attr_integration_time_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group lv0104cs_attribute_group = {
+ .attrs = lv0104cs_attributes,
+};
+
+static const struct iio_info lv0104cs_info = {
+ .attrs = &lv0104cs_attribute_group,
+ .read_raw = &lv0104cs_read_raw,
+ .write_raw = &lv0104cs_write_raw,
+};
+
+static const struct iio_chan_spec lv0104cs_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_INT_TIME),
+ },
+};
+
+static int lv0104cs_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct iio_dev *indio_dev;
+ struct lv0104cs_private *lv0104cs;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*lv0104cs));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ lv0104cs = iio_priv(indio_dev);
+
+ i2c_set_clientdata(client, lv0104cs);
+ lv0104cs->client = client;
+
+ mutex_init(&lv0104cs->lock);
+
+ lv0104cs->calibscale = LV0104CS_CALIBSCALE_UNITY;
+ lv0104cs->scale = LV0104CS_SCALE_1X;
+ lv0104cs->int_time = LV0104CS_INTEG_200MS;
+
+ ret = lv0104cs_write_reg(lv0104cs->client,
+ lv0104cs_calibscales[LV0104CS_CALIBSCALE_UNITY].regval);
+ if (ret)
+ return ret;
+
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->channels = lv0104cs_channels;
+ indio_dev->num_channels = ARRAY_SIZE(lv0104cs_channels);
+ indio_dev->name = client->name;
+ indio_dev->info = &lv0104cs_info;
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct i2c_device_id lv0104cs_id[] = {
+ { "lv0104cs", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lv0104cs_id);
+
+static struct i2c_driver lv0104cs_i2c_driver = {
+ .driver = {
+ .name = "lv0104cs",
+ },
+ .id_table = lv0104cs_id,
+ .probe = lv0104cs_probe,
+};
+module_i2c_driver(lv0104cs_i2c_driver);
+
+MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
+MODULE_DESCRIPTION("LV0104CS Ambient Light Sensor Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c
index a1fd9d591818..d55c4885211a 100644
--- a/drivers/iio/magnetometer/hid-sensor-magn-3d.c
+++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c
@@ -167,7 +167,7 @@ static int magn_3d_read_raw(struct iio_dev *indio_dev,
*val = 0;
*val2 = 0;
switch (mask) {
- case 0:
+ case IIO_CHAN_INFO_RAW:
hid_sensor_power_state(&magn_state->magn_flux_attributes, true);
report_id =
magn_state->magn[chan->address].report_id;
diff --git a/drivers/iio/potentiometer/Kconfig b/drivers/iio/potentiometer/Kconfig
index 8bf282510be6..79ec2eba4969 100644
--- a/drivers/iio/potentiometer/Kconfig
+++ b/drivers/iio/potentiometer/Kconfig
@@ -5,6 +5,16 @@
menu "Digital potentiometers"
+config AD5272
+ tristate "Analog Devices AD5272 and similar Digital Potentiometer driver"
+ depends on I2C
+ help
+ Say yes here to build support for the Analog Devices AD5272 and AD5274
+ digital potentiometer chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad5272.
+
config DS1803
tristate "Maxim Integrated DS1803 Digital Potentiometer driver"
depends on I2C
@@ -37,6 +47,17 @@ config MAX5487
To compile this driver as a module, choose M here: the
module will be called max5487.
+config MCP4018
+ tristate "Microchip MCP4017/18/19 Digital Potentiometer driver"
+ depends on I2C
+ help
+ Say yes here to build support for the Microchip
+ MCP4017, MCP4018, MCP4019
+ digital potentiometer chips.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mcp4018.
+
config MCP4131
tristate "Microchip MCP413X/414X/415X/416X/423X/424X/425X/426X Digital Potentiometer driver"
depends on SPI
diff --git a/drivers/iio/potentiometer/Makefile b/drivers/iio/potentiometer/Makefile
index 1afd1e70f8cc..4af657883c3f 100644
--- a/drivers/iio/potentiometer/Makefile
+++ b/drivers/iio/potentiometer/Makefile
@@ -4,9 +4,11 @@
#
# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_AD5272) += ad5272.o
obj-$(CONFIG_DS1803) += ds1803.o
obj-$(CONFIG_MAX5481) += max5481.o
obj-$(CONFIG_MAX5487) += max5487.o
+obj-$(CONFIG_MCP4018) += mcp4018.o
obj-$(CONFIG_MCP4131) += mcp4131.o
obj-$(CONFIG_MCP4531) += mcp4531.o
obj-$(CONFIG_TPL0102) += tpl0102.o
diff --git a/drivers/iio/potentiometer/ad5272.c b/drivers/iio/potentiometer/ad5272.c
new file mode 100644
index 000000000000..154f9a5da8bc
--- /dev/null
+++ b/drivers/iio/potentiometer/ad5272.c
@@ -0,0 +1,231 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Analog Devices AD5272 digital potentiometer driver
+ * Copyright (C) 2018 Phil Reid <preid@electromag.com.au>
+ *
+ * Datasheet: http://www.analog.com/media/en/technical-documentation/data-sheets/AD5272_5274.pdf
+ *
+ * DEVID #Wipers #Positions Resistor Opts (kOhm) i2c address
+ * ad5272 1 1024 20, 50, 100 01011xx
+ * ad5274 1 256 20, 100 01011xx
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+
+#define AD5272_RDAC_WR 1
+#define AD5272_RDAC_RD 2
+#define AD5272_RESET 4
+#define AD5272_CTL 7
+
+#define AD5272_RDAC_WR_EN BIT(1)
+
+struct ad5272_cfg {
+ int max_pos;
+ int kohms;
+ int shift;
+};
+
+enum ad5272_type {
+ AD5272_020,
+ AD5272_050,
+ AD5272_100,
+ AD5274_020,
+ AD5274_100,
+};
+
+static const struct ad5272_cfg ad5272_cfg[] = {
+ [AD5272_020] = { .max_pos = 1024, .kohms = 20 },
+ [AD5272_050] = { .max_pos = 1024, .kohms = 50 },
+ [AD5272_100] = { .max_pos = 1024, .kohms = 100 },
+ [AD5274_020] = { .max_pos = 256, .kohms = 20, .shift = 2 },
+ [AD5274_100] = { .max_pos = 256, .kohms = 100, .shift = 2 },
+};
+
+struct ad5272_data {
+ struct i2c_client *client;
+ struct mutex lock;
+ const struct ad5272_cfg *cfg;
+ u8 buf[2] ____cacheline_aligned;
+};
+
+static const struct iio_chan_spec ad5272_channel = {
+ .type = IIO_RESISTANCE,
+ .output = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+};
+
+static int ad5272_write(struct ad5272_data *data, int reg, int val)
+{
+ int ret;
+
+ data->buf[0] = (reg << 2) | ((val >> 8) & 0x3);
+ data->buf[1] = (u8)val;
+
+ mutex_lock(&data->lock);
+ ret = i2c_master_send(data->client, data->buf, sizeof(data->buf));
+ mutex_unlock(&data->lock);
+ return ret < 0 ? ret : 0;
+}
+
+static int ad5272_read(struct ad5272_data *data, int reg, int *val)
+{
+ int ret;
+
+ data->buf[0] = reg << 2;
+ data->buf[1] = 0;
+
+ mutex_lock(&data->lock);
+ ret = i2c_master_send(data->client, data->buf, sizeof(data->buf));
+ if (ret < 0)
+ goto error;
+
+ ret = i2c_master_recv(data->client, data->buf, sizeof(data->buf));
+ if (ret < 0)
+ goto error;
+
+ *val = ((data->buf[0] & 0x3) << 8) | data->buf[1];
+ ret = 0;
+error:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+static int ad5272_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct ad5272_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW: {
+ ret = ad5272_read(data, AD5272_RDAC_RD, val);
+ *val = *val >> data->cfg->shift;
+ return ret ? ret : IIO_VAL_INT;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ *val = 1000 * data->cfg->kohms;
+ *val2 = data->cfg->max_pos;
+ return IIO_VAL_FRACTIONAL;
+ }
+
+ return -EINVAL;
+}
+
+static int ad5272_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct ad5272_data *data = iio_priv(indio_dev);
+
+ if (mask != IIO_CHAN_INFO_RAW)
+ return -EINVAL;
+
+ if (val >= data->cfg->max_pos || val < 0 || val2)
+ return -EINVAL;
+
+ return ad5272_write(data, AD5272_RDAC_WR, val << data->cfg->shift);
+}
+
+static const struct iio_info ad5272_info = {
+ .read_raw = ad5272_read_raw,
+ .write_raw = ad5272_write_raw,
+};
+
+static int ad5272_reset(struct ad5272_data *data)
+{
+ struct gpio_desc *reset_gpio;
+
+ reset_gpio = devm_gpiod_get_optional(&data->client->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(reset_gpio))
+ return PTR_ERR(reset_gpio);
+
+ if (reset_gpio) {
+ udelay(1);
+ gpiod_set_value(reset_gpio, 1);
+ } else {
+ ad5272_write(data, AD5272_RESET, 0);
+ }
+ usleep_range(1000, 2000);
+
+ return 0;
+}
+
+static int ad5272_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct iio_dev *indio_dev;
+ struct ad5272_data *data;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, indio_dev);
+
+ data = iio_priv(indio_dev);
+ data->client = client;
+ mutex_init(&data->lock);
+ data->cfg = &ad5272_cfg[id->driver_data];
+
+ ret = ad5272_reset(data);
+ if (ret)
+ return ret;
+
+ ret = ad5272_write(data, AD5272_CTL, AD5272_RDAC_WR_EN);
+ if (ret < 0)
+ return -ENODEV;
+
+ indio_dev->dev.parent = dev;
+ indio_dev->info = &ad5272_info;
+ indio_dev->channels = &ad5272_channel;
+ indio_dev->num_channels = 1;
+ indio_dev->name = client->name;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id ad5272_dt_ids[] = {
+ { .compatible = "adi,ad5272-020", .data = (void *)AD5272_020 },
+ { .compatible = "adi,ad5272-050", .data = (void *)AD5272_050 },
+ { .compatible = "adi,ad5272-100", .data = (void *)AD5272_100 },
+ { .compatible = "adi,ad5274-020", .data = (void *)AD5274_020 },
+ { .compatible = "adi,ad5274-100", .data = (void *)AD5274_100 },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ad5272_dt_ids);
+#endif /* CONFIG_OF */
+
+static const struct i2c_device_id ad5272_id[] = {
+ { "ad5272-020", AD5272_020 },
+ { "ad5272-050", AD5272_050 },
+ { "ad5272-100", AD5272_100 },
+ { "ad5274-020", AD5274_020 },
+ { "ad5274-100", AD5274_100 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ad5272_id);
+
+static struct i2c_driver ad5272_driver = {
+ .driver = {
+ .name = "ad5272",
+ .of_match_table = of_match_ptr(ad5272_dt_ids),
+ },
+ .probe = ad5272_probe,
+ .id_table = ad5272_id,
+};
+
+module_i2c_driver(ad5272_driver);
+
+MODULE_AUTHOR("Phil Reid <preid@eletromag.com.au>");
+MODULE_DESCRIPTION("AD5272 digital potentiometer");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/potentiometer/ds1803.c b/drivers/iio/potentiometer/ds1803.c
index 9b0ff4ab2f9c..6bf12c9eccbd 100644
--- a/drivers/iio/potentiometer/ds1803.c
+++ b/drivers/iio/potentiometer/ds1803.c
@@ -64,7 +64,7 @@ static int ds1803_read_raw(struct iio_dev *indio_dev,
struct ds1803_data *data = iio_priv(indio_dev);
int pot = chan->channel;
int ret;
- u8 result[indio_dev->num_channels];
+ u8 result[ARRAY_SIZE(ds1803_channels)];
switch (mask) {
case IIO_CHAN_INFO_RAW:
diff --git a/drivers/iio/potentiometer/mcp4018.c b/drivers/iio/potentiometer/mcp4018.c
new file mode 100644
index 000000000000..601b25d1f387
--- /dev/null
+++ b/drivers/iio/potentiometer/mcp4018.c
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Industrial I/O driver for Microchip digital potentiometers
+ * Copyright (c) 2018 Axentia Technologies AB
+ * Author: Peter Rosin <peda@axentia.se>
+ *
+ * Datasheet: http://www.microchip.com/downloads/en/DeviceDoc/22147a.pdf
+ *
+ * DEVID #Wipers #Positions Resistor Opts (kOhm)
+ * mcp4017 1 128 5, 10, 50, 100
+ * mcp4018 1 128 5, 10, 50, 100
+ * mcp4019 1 128 5, 10, 50, 100
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#define MCP4018_WIPER_MAX 127
+
+struct mcp4018_cfg {
+ int kohms;
+};
+
+enum mcp4018_type {
+ MCP4018_502,
+ MCP4018_103,
+ MCP4018_503,
+ MCP4018_104,
+};
+
+static const struct mcp4018_cfg mcp4018_cfg[] = {
+ [MCP4018_502] = { .kohms = 5, },
+ [MCP4018_103] = { .kohms = 10, },
+ [MCP4018_503] = { .kohms = 50, },
+ [MCP4018_104] = { .kohms = 100, },
+};
+
+struct mcp4018_data {
+ struct i2c_client *client;
+ const struct mcp4018_cfg *cfg;
+};
+
+static const struct iio_chan_spec mcp4018_channel = {
+ .type = IIO_RESISTANCE,
+ .indexed = 1,
+ .output = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+};
+
+static int mcp4018_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct mcp4018_data *data = iio_priv(indio_dev);
+ s32 ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = i2c_smbus_read_byte(data->client);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 1000 * data->cfg->kohms;
+ *val2 = MCP4018_WIPER_MAX;
+ return IIO_VAL_FRACTIONAL;
+ }
+
+ return -EINVAL;
+}
+
+static int mcp4018_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct mcp4018_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (val > MCP4018_WIPER_MAX || val < 0)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return i2c_smbus_write_byte(data->client, val);
+}
+
+static const struct iio_info mcp4018_info = {
+ .read_raw = mcp4018_read_raw,
+ .write_raw = mcp4018_write_raw,
+};
+
+#ifdef CONFIG_OF
+
+#define MCP4018_COMPATIBLE(of_compatible, cfg) { \
+ .compatible = of_compatible, \
+ .data = &mcp4018_cfg[cfg], \
+}
+
+static const struct of_device_id mcp4018_of_match[] = {
+ MCP4018_COMPATIBLE("microchip,mcp4017-502", MCP4018_502),
+ MCP4018_COMPATIBLE("microchip,mcp4017-103", MCP4018_103),
+ MCP4018_COMPATIBLE("microchip,mcp4017-503", MCP4018_503),
+ MCP4018_COMPATIBLE("microchip,mcp4017-104", MCP4018_104),
+ MCP4018_COMPATIBLE("microchip,mcp4018-502", MCP4018_502),
+ MCP4018_COMPATIBLE("microchip,mcp4018-103", MCP4018_103),
+ MCP4018_COMPATIBLE("microchip,mcp4018-503", MCP4018_503),
+ MCP4018_COMPATIBLE("microchip,mcp4018-104", MCP4018_104),
+ MCP4018_COMPATIBLE("microchip,mcp4019-502", MCP4018_502),
+ MCP4018_COMPATIBLE("microchip,mcp4019-103", MCP4018_103),
+ MCP4018_COMPATIBLE("microchip,mcp4019-503", MCP4018_503),
+ MCP4018_COMPATIBLE("microchip,mcp4019-104", MCP4018_104),
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mcp4018_of_match);
+
+#endif
+
+static int mcp4018_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct mcp4018_data *data;
+ struct iio_dev *indio_dev;
+ const struct of_device_id *match;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE)) {
+ dev_err(dev, "SMBUS Byte transfers not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+
+ match = of_match_device(of_match_ptr(mcp4018_of_match), dev);
+ if (match)
+ data->cfg = of_device_get_match_data(dev);
+ else
+ data->cfg = &mcp4018_cfg[id->driver_data];
+
+ indio_dev->dev.parent = dev;
+ indio_dev->info = &mcp4018_info;
+ indio_dev->channels = &mcp4018_channel;
+ indio_dev->num_channels = 1;
+ indio_dev->name = client->name;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct i2c_device_id mcp4018_id[] = {
+ { "mcp4017-502", MCP4018_502 },
+ { "mcp4017-103", MCP4018_103 },
+ { "mcp4017-503", MCP4018_503 },
+ { "mcp4017-104", MCP4018_104 },
+ { "mcp4018-502", MCP4018_502 },
+ { "mcp4018-103", MCP4018_103 },
+ { "mcp4018-503", MCP4018_503 },
+ { "mcp4018-104", MCP4018_104 },
+ { "mcp4019-502", MCP4018_502 },
+ { "mcp4019-103", MCP4018_103 },
+ { "mcp4019-503", MCP4018_503 },
+ { "mcp4019-104", MCP4018_104 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, mcp4018_id);
+
+static struct i2c_driver mcp4018_driver = {
+ .driver = {
+ .name = "mcp4018",
+ .of_match_table = of_match_ptr(mcp4018_of_match),
+ },
+ .probe = mcp4018_probe,
+ .id_table = mcp4018_id,
+};
+
+module_i2c_driver(mcp4018_driver);
+
+MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
+MODULE_DESCRIPTION("MCP4018 digital potentiometer");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/potentiometer/tpl0102.c b/drivers/iio/potentiometer/tpl0102.c
index 93f9d4a8c9aa..ca1cce58fe20 100644
--- a/drivers/iio/potentiometer/tpl0102.c
+++ b/drivers/iio/potentiometer/tpl0102.c
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* tpl0102.c - Support for Texas Instruments digital potentiometers
*
- * Copyright (C) 2016 Matt Ranostay <mranostay@gmail.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.
+ * Copyright (C) 2016, 2018
+ * Author: Matt Ranostay <matt.ranostay@konsulko.com>
*
* TODO: enable/disable hi-z output control
*/
@@ -156,6 +148,6 @@ static struct i2c_driver tpl0102_driver = {
module_i2c_driver(tpl0102_driver);
-MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
MODULE_DESCRIPTION("TPL0102 digital potentiometer");
MODULE_LICENSE("GPL");
diff --git a/drivers/iio/potentiostat/lmp91000.c b/drivers/iio/potentiostat/lmp91000.c
index 007710991f15..85714055cc74 100644
--- a/drivers/iio/potentiostat/lmp91000.c
+++ b/drivers/iio/potentiostat/lmp91000.c
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* lmp91000.c - Support for Texas Instruments digital potentiostats
*
- * Copyright (C) 2016 Matt Ranostay <mranostay@gmail.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.
+ * Copyright (C) 2016, 2018
+ * Author: Matt Ranostay <matt.ranostay@konsulko.com>
*
* TODO: bias voltage + polarity control, and multiple chip support
*/
@@ -440,6 +432,6 @@ static struct i2c_driver lmp91000_driver = {
};
module_i2c_driver(lmp91000_driver);
-MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
MODULE_DESCRIPTION("LMP91000 digital potentiostat");
MODULE_LICENSE("GPL");
diff --git a/drivers/iio/pressure/ms5611.h b/drivers/iio/pressure/ms5611.h
index ccda63c5b3c3..ead9e9f85894 100644
--- a/drivers/iio/pressure/ms5611.h
+++ b/drivers/iio/pressure/ms5611.h
@@ -63,7 +63,7 @@ struct ms5611_state {
};
int ms5611_probe(struct iio_dev *indio_dev, struct device *dev,
- const char* name, int type);
+ const char *name, int type);
int ms5611_remove(struct iio_dev *indio_dev);
#endif /* _MS5611_H */
diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c
index b6249af48014..f130388a16a0 100644
--- a/drivers/iio/proximity/as3935.c
+++ b/drivers/iio/proximity/as3935.c
@@ -1,18 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* as3935.c - Support for AS3935 Franklin lightning sensor
*
- * Copyright (C) 2014 Matt Ranostay <mranostay@gmail.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.
- *
+ * Copyright (C) 2014, 2017-2018
+ * Author: Matt Ranostay <matt.ranostay@konsulko.com>
*/
#include <linux/module.h>
@@ -502,6 +493,6 @@ static struct spi_driver as3935_driver = {
};
module_spi_driver(as3935_driver);
-MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
MODULE_DESCRIPTION("AS3935 lightning sensor");
MODULE_LICENSE("GPL");
diff --git a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c
index 4d56f67b24c6..47af54f14756 100644
--- a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c
+++ b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* pulsedlight-lidar-lite-v2.c - Support for PulsedLight LIDAR sensor
*
- * Copyright (C) 2015 Matt Ranostay <mranostay@gmail.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.
+ * Copyright (C) 2015, 2017-2018
+ * Author: Matt Ranostay <matt.ranostay@konsulko.com>
*
* TODO: interrupt mode, and signal strength reporting
*/
@@ -377,6 +369,6 @@ static struct i2c_driver lidar_driver = {
};
module_i2c_driver(lidar_driver);
-MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
MODULE_DESCRIPTION("PulsedLight LIDAR sensor");
MODULE_LICENSE("GPL");
diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c
index b8a2c2c8cac5..ff80409e0c44 100644
--- a/drivers/iio/proximity/sx9500.c
+++ b/drivers/iio/proximity/sx9500.c
@@ -32,9 +32,6 @@
#define SX9500_DRIVER_NAME "sx9500"
#define SX9500_IRQ_NAME "sx9500_event"
-#define SX9500_GPIO_INT "interrupt"
-#define SX9500_GPIO_RESET "reset"
-
/* Register definitions. */
#define SX9500_REG_IRQ_SRC 0x00
#define SX9500_REG_STAT 0x01
@@ -866,26 +863,44 @@ static int sx9500_init_device(struct iio_dev *indio_dev)
return sx9500_init_compensation(indio_dev);
}
+static const struct acpi_gpio_params reset_gpios = { 0, 0, false };
+static const struct acpi_gpio_params interrupt_gpios = { 2, 0, false };
+
+static const struct acpi_gpio_mapping acpi_sx9500_gpios[] = {
+ { "reset-gpios", &reset_gpios, 1 },
+ /*
+ * Some platforms have a bug in ACPI GPIO description making IRQ
+ * GPIO to be output only. Ask the GPIO core to ignore this limit.
+ */
+ { "interrupt-gpios", &interrupt_gpios, 1, ACPI_GPIO_QUIRK_NO_IO_RESTRICTION },
+ { },
+};
+
static void sx9500_gpio_probe(struct i2c_client *client,
struct sx9500_data *data)
{
struct gpio_desc *gpiod_int;
struct device *dev;
+ int ret;
if (!client)
return;
dev = &client->dev;
+ ret = devm_acpi_dev_add_driver_gpios(dev, acpi_sx9500_gpios);
+ if (ret)
+ dev_dbg(dev, "Unable to add GPIO mapping table\n");
+
if (client->irq <= 0) {
- gpiod_int = devm_gpiod_get(dev, SX9500_GPIO_INT, GPIOD_IN);
+ gpiod_int = devm_gpiod_get(dev, "interrupt", GPIOD_IN);
if (IS_ERR(gpiod_int))
dev_err(dev, "gpio get irq failed\n");
else
client->irq = gpiod_to_irq(gpiod_int);
}
- data->gpiod_rst = devm_gpiod_get(dev, SX9500_GPIO_RESET, GPIOD_OUT_HIGH);
+ data->gpiod_rst = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(data->gpiod_rst)) {
dev_warn(dev, "gpio get reset pin failed\n");
data->gpiod_rst = NULL;
diff --git a/drivers/iio/temperature/Kconfig b/drivers/iio/temperature/Kconfig
index 5378976d6d27..82e4a62745e2 100644
--- a/drivers/iio/temperature/Kconfig
+++ b/drivers/iio/temperature/Kconfig
@@ -43,6 +43,18 @@ config MLX90614
This driver can also be built as a module. If so, the module will
be called mlx90614.
+config MLX90632
+ tristate "MLX90632 contact-less infrared sensor with medical accuracy"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ If you say yes here you get support for the Melexis
+ MLX90632 contact-less infrared sensor with medical accuracy
+ connected with I2C.
+
+ This driver can also be built as a module. If so, the module will
+ be called mlx90632.
+
config TMP006
tristate "TMP006 infrared thermopile sensor"
depends on I2C
diff --git a/drivers/iio/temperature/Makefile b/drivers/iio/temperature/Makefile
index 34bd9023727b..34a31db0bb63 100644
--- a/drivers/iio/temperature/Makefile
+++ b/drivers/iio/temperature/Makefile
@@ -6,6 +6,7 @@
obj-$(CONFIG_HID_SENSOR_TEMP) += hid-sensor-temperature.o
obj-$(CONFIG_MAXIM_THERMOCOUPLE) += maxim_thermocouple.o
obj-$(CONFIG_MLX90614) += mlx90614.o
+obj-$(CONFIG_MLX90632) += mlx90632.o
obj-$(CONFIG_TMP006) += tmp006.o
obj-$(CONFIG_TMP007) += tmp007.o
obj-$(CONFIG_TSYS01) += tsys01.o
diff --git a/drivers/iio/temperature/maxim_thermocouple.c b/drivers/iio/temperature/maxim_thermocouple.c
index e8b7e0b6c8ad..54e383231d1e 100644
--- a/drivers/iio/temperature/maxim_thermocouple.c
+++ b/drivers/iio/temperature/maxim_thermocouple.c
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* maxim_thermocouple.c - Support for Maxim thermocouple chips
*
- * Copyright (C) 2016 Matt Ranostay <mranostay@gmail.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.
+ * Copyright (C) 2016-2018 Matt Ranostay
+ * Author: <matt.ranostay@konsulko.com>
*/
#include <linux/module.h>
@@ -281,6 +273,6 @@ static struct spi_driver maxim_thermocouple_driver = {
};
module_spi_driver(maxim_thermocouple_driver);
-MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
MODULE_DESCRIPTION("Maxim thermocouple sensors");
MODULE_LICENSE("GPL");
diff --git a/drivers/iio/temperature/mlx90632.c b/drivers/iio/temperature/mlx90632.c
new file mode 100644
index 000000000000..9851311aa3fd
--- /dev/null
+++ b/drivers/iio/temperature/mlx90632.c
@@ -0,0 +1,752 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mlx90632.c - Melexis MLX90632 contactless IR temperature sensor
+ *
+ * Copyright (c) 2017 Melexis <cmo@melexis.com>
+ *
+ * Driver for the Melexis MLX90632 I2C 16-bit IR thermopile sensor
+ */
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/math64.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+/* Memory sections addresses */
+#define MLX90632_ADDR_RAM 0x4000 /* Start address of ram */
+#define MLX90632_ADDR_EEPROM 0x2480 /* Start address of user eeprom */
+
+/* EEPROM addresses - used at startup */
+#define MLX90632_EE_CTRL 0x24d4 /* Control register initial value */
+#define MLX90632_EE_I2C_ADDR 0x24d5 /* I2C address register initial value */
+#define MLX90632_EE_VERSION 0x240b /* EEPROM version reg address */
+#define MLX90632_EE_P_R 0x240c /* P_R calibration register 32bit */
+#define MLX90632_EE_P_G 0x240e /* P_G calibration register 32bit */
+#define MLX90632_EE_P_T 0x2410 /* P_T calibration register 32bit */
+#define MLX90632_EE_P_O 0x2412 /* P_O calibration register 32bit */
+#define MLX90632_EE_Aa 0x2414 /* Aa calibration register 32bit */
+#define MLX90632_EE_Ab 0x2416 /* Ab calibration register 32bit */
+#define MLX90632_EE_Ba 0x2418 /* Ba calibration register 32bit */
+#define MLX90632_EE_Bb 0x241a /* Bb calibration register 32bit */
+#define MLX90632_EE_Ca 0x241c /* Ca calibration register 32bit */
+#define MLX90632_EE_Cb 0x241e /* Cb calibration register 32bit */
+#define MLX90632_EE_Da 0x2420 /* Da calibration register 32bit */
+#define MLX90632_EE_Db 0x2422 /* Db calibration register 32bit */
+#define MLX90632_EE_Ea 0x2424 /* Ea calibration register 32bit */
+#define MLX90632_EE_Eb 0x2426 /* Eb calibration register 32bit */
+#define MLX90632_EE_Fa 0x2428 /* Fa calibration register 32bit */
+#define MLX90632_EE_Fb 0x242a /* Fb calibration register 32bit */
+#define MLX90632_EE_Ga 0x242c /* Ga calibration register 32bit */
+
+#define MLX90632_EE_Gb 0x242e /* Gb calibration register 16bit */
+#define MLX90632_EE_Ka 0x242f /* Ka calibration register 16bit */
+
+#define MLX90632_EE_Ha 0x2481 /* Ha customer calib value reg 16bit */
+#define MLX90632_EE_Hb 0x2482 /* Hb customer calib value reg 16bit */
+
+/* Register addresses - volatile */
+#define MLX90632_REG_I2C_ADDR 0x3000 /* Chip I2C address register */
+
+/* Control register address - volatile */
+#define MLX90632_REG_CONTROL 0x3001 /* Control Register address */
+#define MLX90632_CFG_PWR_MASK GENMASK(2, 1) /* PowerMode Mask */
+/* PowerModes statuses */
+#define MLX90632_PWR_STATUS(ctrl_val) (ctrl_val << 1)
+#define MLX90632_PWR_STATUS_HALT MLX90632_PWR_STATUS(0) /* hold */
+#define MLX90632_PWR_STATUS_SLEEP_STEP MLX90632_PWR_STATUS(1) /* sleep step*/
+#define MLX90632_PWR_STATUS_STEP MLX90632_PWR_STATUS(2) /* step */
+#define MLX90632_PWR_STATUS_CONTINUOUS MLX90632_PWR_STATUS(3) /* continuous*/
+
+/* Device status register - volatile */
+#define MLX90632_REG_STATUS 0x3fff /* Device status register */
+#define MLX90632_STAT_BUSY BIT(10) /* Device busy indicator */
+#define MLX90632_STAT_EE_BUSY BIT(9) /* EEPROM busy indicator */
+#define MLX90632_STAT_BRST BIT(8) /* Brown out reset indicator */
+#define MLX90632_STAT_CYCLE_POS GENMASK(6, 2) /* Data position */
+#define MLX90632_STAT_DATA_RDY BIT(0) /* Data ready indicator */
+
+/* RAM_MEAS address-es for each channel */
+#define MLX90632_RAM_1(meas_num) (MLX90632_ADDR_RAM + 3 * meas_num)
+#define MLX90632_RAM_2(meas_num) (MLX90632_ADDR_RAM + 3 * meas_num + 1)
+#define MLX90632_RAM_3(meas_num) (MLX90632_ADDR_RAM + 3 * meas_num + 2)
+
+/* Magic constants */
+#define MLX90632_ID_MEDICAL 0x0105 /* EEPROM DSPv5 Medical device id */
+#define MLX90632_ID_CONSUMER 0x0205 /* EEPROM DSPv5 Consumer device id */
+#define MLX90632_RESET_CMD 0x0006 /* Reset sensor (address or global) */
+#define MLX90632_REF_12 12LL /**< ResCtrlRef value of Ch 1 or Ch 2 */
+#define MLX90632_REF_3 12LL /**< ResCtrlRef value of Channel 3 */
+#define MLX90632_MAX_MEAS_NUM 31 /**< Maximum measurements in list */
+#define MLX90632_SLEEP_DELAY_MS 3000 /**< Autosleep delay */
+
+struct mlx90632_data {
+ struct i2c_client *client;
+ struct mutex lock; /* Multiple reads for single measurement */
+ struct regmap *regmap;
+ u16 emissivity;
+};
+
+static const struct regmap_range mlx90632_volatile_reg_range[] = {
+ regmap_reg_range(MLX90632_REG_I2C_ADDR, MLX90632_REG_CONTROL),
+ regmap_reg_range(MLX90632_REG_STATUS, MLX90632_REG_STATUS),
+ regmap_reg_range(MLX90632_RAM_1(0),
+ MLX90632_RAM_3(MLX90632_MAX_MEAS_NUM)),
+};
+
+static const struct regmap_access_table mlx90632_volatile_regs_tbl = {
+ .yes_ranges = mlx90632_volatile_reg_range,
+ .n_yes_ranges = ARRAY_SIZE(mlx90632_volatile_reg_range),
+};
+
+static const struct regmap_range mlx90632_read_reg_range[] = {
+ regmap_reg_range(MLX90632_EE_VERSION, MLX90632_EE_Ka),
+ regmap_reg_range(MLX90632_EE_CTRL, MLX90632_EE_I2C_ADDR),
+ regmap_reg_range(MLX90632_EE_Ha, MLX90632_EE_Hb),
+ regmap_reg_range(MLX90632_REG_I2C_ADDR, MLX90632_REG_CONTROL),
+ regmap_reg_range(MLX90632_REG_STATUS, MLX90632_REG_STATUS),
+ regmap_reg_range(MLX90632_RAM_1(0),
+ MLX90632_RAM_3(MLX90632_MAX_MEAS_NUM)),
+};
+
+static const struct regmap_access_table mlx90632_readable_regs_tbl = {
+ .yes_ranges = mlx90632_read_reg_range,
+ .n_yes_ranges = ARRAY_SIZE(mlx90632_read_reg_range),
+};
+
+static const struct regmap_range mlx90632_no_write_reg_range[] = {
+ regmap_reg_range(MLX90632_EE_VERSION, MLX90632_EE_Ka),
+ regmap_reg_range(MLX90632_RAM_1(0),
+ MLX90632_RAM_3(MLX90632_MAX_MEAS_NUM)),
+};
+
+static const struct regmap_access_table mlx90632_writeable_regs_tbl = {
+ .no_ranges = mlx90632_no_write_reg_range,
+ .n_no_ranges = ARRAY_SIZE(mlx90632_no_write_reg_range),
+};
+
+static const struct regmap_config mlx90632_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+
+ .volatile_table = &mlx90632_volatile_regs_tbl,
+ .rd_table = &mlx90632_readable_regs_tbl,
+ .wr_table = &mlx90632_writeable_regs_tbl,
+
+ .use_single_rw = true,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static s32 mlx90632_pwr_set_sleep_step(struct regmap *regmap)
+{
+ return regmap_update_bits(regmap, MLX90632_REG_CONTROL,
+ MLX90632_CFG_PWR_MASK,
+ MLX90632_PWR_STATUS_SLEEP_STEP);
+}
+
+static s32 mlx90632_pwr_continuous(struct regmap *regmap)
+{
+ return regmap_update_bits(regmap, MLX90632_REG_CONTROL,
+ MLX90632_CFG_PWR_MASK,
+ MLX90632_PWR_STATUS_CONTINUOUS);
+}
+
+/**
+ * mlx90632_perform_measurement - Trigger and retrieve current measurement cycle
+ * @*data: pointer to mlx90632_data object containing regmap information
+ *
+ * Perform a measurement and return latest measurement cycle position reported
+ * by sensor. This is a blocking function for 500ms, as that is default sensor
+ * refresh rate.
+ */
+static int mlx90632_perform_measurement(struct mlx90632_data *data)
+{
+ int ret, tries = 100;
+ unsigned int reg_status;
+
+ ret = regmap_update_bits(data->regmap, MLX90632_REG_STATUS,
+ MLX90632_STAT_DATA_RDY, 0);
+ if (ret < 0)
+ return ret;
+
+ while (tries-- > 0) {
+ ret = regmap_read(data->regmap, MLX90632_REG_STATUS,
+ &reg_status);
+ if (ret < 0)
+ return ret;
+ if (reg_status & MLX90632_STAT_DATA_RDY)
+ break;
+ usleep_range(10000, 11000);
+ }
+
+ if (tries < 0) {
+ dev_err(&data->client->dev, "data not ready");
+ return -ETIMEDOUT;
+ }
+
+ return (reg_status & MLX90632_STAT_CYCLE_POS) >> 2;
+}
+
+static int mlx90632_channel_new_select(int perform_ret, uint8_t *channel_new,
+ uint8_t *channel_old)
+{
+ switch (perform_ret) {
+ case 1:
+ *channel_new = 1;
+ *channel_old = 2;
+ break;
+ case 2:
+ *channel_new = 2;
+ *channel_old = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mlx90632_read_ambient_raw(struct regmap *regmap,
+ s16 *ambient_new_raw, s16 *ambient_old_raw)
+{
+ int ret;
+ unsigned int read_tmp;
+
+ ret = regmap_read(regmap, MLX90632_RAM_3(1), &read_tmp);
+ if (ret < 0)
+ return ret;
+ *ambient_new_raw = (s16)read_tmp;
+
+ ret = regmap_read(regmap, MLX90632_RAM_3(2), &read_tmp);
+ if (ret < 0)
+ return ret;
+ *ambient_old_raw = (s16)read_tmp;
+
+ return ret;
+}
+
+static int mlx90632_read_object_raw(struct regmap *regmap,
+ int perform_measurement_ret,
+ s16 *object_new_raw, s16 *object_old_raw)
+{
+ int ret;
+ unsigned int read_tmp;
+ s16 read;
+ u8 channel = 0;
+ u8 channel_old = 0;
+
+ ret = mlx90632_channel_new_select(perform_measurement_ret, &channel,
+ &channel_old);
+ if (ret != 0)
+ return ret;
+
+ ret = regmap_read(regmap, MLX90632_RAM_2(channel), &read_tmp);
+ if (ret < 0)
+ return ret;
+
+ read = (s16)read_tmp;
+
+ ret = regmap_read(regmap, MLX90632_RAM_1(channel), &read_tmp);
+ if (ret < 0)
+ return ret;
+ *object_new_raw = (read + (s16)read_tmp) / 2;
+
+ ret = regmap_read(regmap, MLX90632_RAM_2(channel_old), &read_tmp);
+ if (ret < 0)
+ return ret;
+ read = (s16)read_tmp;
+
+ ret = regmap_read(regmap, MLX90632_RAM_1(channel_old), &read_tmp);
+ if (ret < 0)
+ return ret;
+ *object_old_raw = (read + (s16)read_tmp) / 2;
+
+ return ret;
+}
+
+static int mlx90632_read_all_channel(struct mlx90632_data *data,
+ s16 *ambient_new_raw, s16 *ambient_old_raw,
+ s16 *object_new_raw, s16 *object_old_raw)
+{
+ s32 ret, measurement;
+
+ mutex_lock(&data->lock);
+ measurement = mlx90632_perform_measurement(data);
+ if (measurement < 0) {
+ ret = measurement;
+ goto read_unlock;
+ }
+ ret = mlx90632_read_ambient_raw(data->regmap, ambient_new_raw,
+ ambient_old_raw);
+ if (ret < 0)
+ goto read_unlock;
+
+ ret = mlx90632_read_object_raw(data->regmap, measurement,
+ object_new_raw, object_old_raw);
+read_unlock:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+static int mlx90632_read_ee_register(struct regmap *regmap, u16 reg_lsb,
+ s32 *reg_value)
+{
+ s32 ret;
+ unsigned int read;
+ u32 value;
+
+ ret = regmap_read(regmap, reg_lsb, &read);
+ if (ret < 0)
+ return ret;
+
+ value = read;
+
+ ret = regmap_read(regmap, reg_lsb + 1, &read);
+ if (ret < 0)
+ return ret;
+
+ *reg_value = (read << 16) | (value & 0xffff);
+
+ return 0;
+}
+
+static s64 mlx90632_preprocess_temp_amb(s16 ambient_new_raw,
+ s16 ambient_old_raw, s16 Gb)
+{
+ s64 VR_Ta, kGb, tmp;
+
+ kGb = ((s64)Gb * 1000LL) >> 10ULL;
+ VR_Ta = (s64)ambient_old_raw * 1000000LL +
+ kGb * div64_s64(((s64)ambient_new_raw * 1000LL),
+ (MLX90632_REF_3));
+ tmp = div64_s64(
+ div64_s64(((s64)ambient_new_raw * 1000000000000LL),
+ (MLX90632_REF_3)), VR_Ta);
+ return div64_s64(tmp << 19ULL, 1000LL);
+}
+
+static s64 mlx90632_preprocess_temp_obj(s16 object_new_raw, s16 object_old_raw,
+ s16 ambient_new_raw,
+ s16 ambient_old_raw, s16 Ka)
+{
+ s64 VR_IR, kKa, tmp;
+
+ kKa = ((s64)Ka * 1000LL) >> 10ULL;
+ VR_IR = (s64)ambient_old_raw * 1000000LL +
+ kKa * div64_s64(((s64)ambient_new_raw * 1000LL),
+ (MLX90632_REF_3));
+ tmp = div64_s64(
+ div64_s64(((s64)((object_new_raw + object_old_raw) / 2)
+ * 1000000000000LL), (MLX90632_REF_12)),
+ VR_IR);
+ return div64_s64((tmp << 19ULL), 1000LL);
+}
+
+static s32 mlx90632_calc_temp_ambient(s16 ambient_new_raw, s16 ambient_old_raw,
+ s32 P_T, s32 P_R, s32 P_G, s32 P_O,
+ s16 Gb)
+{
+ s64 Asub, Bsub, Ablock, Bblock, Cblock, AMB, sum;
+
+ AMB = mlx90632_preprocess_temp_amb(ambient_new_raw, ambient_old_raw,
+ Gb);
+ Asub = ((s64)P_T * 10000000000LL) >> 44ULL;
+ Bsub = AMB - (((s64)P_R * 1000LL) >> 8ULL);
+ Ablock = Asub * (Bsub * Bsub);
+ Bblock = (div64_s64(Bsub * 10000000LL, P_G)) << 20ULL;
+ Cblock = ((s64)P_O * 10000000000LL) >> 8ULL;
+
+ sum = div64_s64(Ablock, 1000000LL) + Bblock + Cblock;
+
+ return div64_s64(sum, 10000000LL);
+}
+
+static s32 mlx90632_calc_temp_object_iteration(s32 prev_object_temp, s64 object,
+ s64 TAdut, s32 Fa, s32 Fb,
+ s32 Ga, s16 Ha, s16 Hb,
+ u16 emissivity)
+{
+ s64 calcedKsTO, calcedKsTA, ir_Alpha, TAdut4, Alpha_corr;
+ s64 Ha_customer, Hb_customer;
+
+ Ha_customer = ((s64)Ha * 1000000LL) >> 14ULL;
+ Hb_customer = ((s64)Hb * 100) >> 10ULL;
+
+ calcedKsTO = ((s64)((s64)Ga * (prev_object_temp - 25 * 1000LL)
+ * 1000LL)) >> 36LL;
+ calcedKsTA = ((s64)(Fb * (TAdut - 25 * 1000000LL))) >> 36LL;
+ Alpha_corr = div64_s64((((s64)(Fa * 10000000000LL) >> 46LL)
+ * Ha_customer), 1000LL);
+ Alpha_corr *= ((s64)(1 * 1000000LL + calcedKsTO + calcedKsTA));
+ Alpha_corr = emissivity * div64_s64(Alpha_corr, 100000LL);
+ Alpha_corr = div64_s64(Alpha_corr, 1000LL);
+ ir_Alpha = div64_s64((s64)object * 10000000LL, Alpha_corr);
+ TAdut4 = (div64_s64(TAdut, 10000LL) + 27315) *
+ (div64_s64(TAdut, 10000LL) + 27315) *
+ (div64_s64(TAdut, 10000LL) + 27315) *
+ (div64_s64(TAdut, 10000LL) + 27315);
+
+ return (int_sqrt64(int_sqrt64(ir_Alpha * 1000000000000LL + TAdut4))
+ - 27315 - Hb_customer) * 10;
+}
+
+static s32 mlx90632_calc_temp_object(s64 object, s64 ambient, s32 Ea, s32 Eb,
+ s32 Fa, s32 Fb, s32 Ga, s16 Ha, s16 Hb,
+ u16 tmp_emi)
+{
+ s64 kTA, kTA0, TAdut;
+ s64 temp = 25000;
+ s8 i;
+
+ kTA = (Ea * 1000LL) >> 16LL;
+ kTA0 = (Eb * 1000LL) >> 8LL;
+ TAdut = div64_s64(((ambient - kTA0) * 1000000LL), kTA) + 25 * 1000000LL;
+
+ /* Iterations of calculation as described in datasheet */
+ for (i = 0; i < 5; ++i) {
+ temp = mlx90632_calc_temp_object_iteration(temp, object, TAdut,
+ Fa, Fb, Ga, Ha, Hb,
+ tmp_emi);
+ }
+ return temp;
+}
+
+static int mlx90632_calc_object_dsp105(struct mlx90632_data *data, int *val)
+{
+ s32 ret;
+ s32 Ea, Eb, Fa, Fb, Ga;
+ unsigned int read_tmp;
+ s16 Ha, Hb, Gb, Ka;
+ s16 ambient_new_raw, ambient_old_raw, object_new_raw, object_old_raw;
+ s64 object, ambient;
+
+ ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_Ea, &Ea);
+ if (ret < 0)
+ return ret;
+ ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_Eb, &Eb);
+ if (ret < 0)
+ return ret;
+ ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_Fa, &Fa);
+ if (ret < 0)
+ return ret;
+ ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_Fb, &Fb);
+ if (ret < 0)
+ return ret;
+ ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_Ga, &Ga);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(data->regmap, MLX90632_EE_Ha, &read_tmp);
+ if (ret < 0)
+ return ret;
+ Ha = (s16)read_tmp;
+ ret = regmap_read(data->regmap, MLX90632_EE_Hb, &read_tmp);
+ if (ret < 0)
+ return ret;
+ Hb = (s16)read_tmp;
+ ret = regmap_read(data->regmap, MLX90632_EE_Gb, &read_tmp);
+ if (ret < 0)
+ return ret;
+ Gb = (s16)read_tmp;
+ ret = regmap_read(data->regmap, MLX90632_EE_Ka, &read_tmp);
+ if (ret < 0)
+ return ret;
+ Ka = (s16)read_tmp;
+
+ ret = mlx90632_read_all_channel(data,
+ &ambient_new_raw, &ambient_old_raw,
+ &object_new_raw, &object_old_raw);
+ if (ret < 0)
+ return ret;
+
+ ambient = mlx90632_preprocess_temp_amb(ambient_new_raw,
+ ambient_old_raw, Gb);
+ object = mlx90632_preprocess_temp_obj(object_new_raw,
+ object_old_raw,
+ ambient_new_raw,
+ ambient_old_raw, Ka);
+
+ *val = mlx90632_calc_temp_object(object, ambient, Ea, Eb, Fa, Fb, Ga,
+ Ha, Hb, data->emissivity);
+ return 0;
+}
+
+static int mlx90632_calc_ambient_dsp105(struct mlx90632_data *data, int *val)
+{
+ s32 ret;
+ unsigned int read_tmp;
+ s32 PT, PR, PG, PO;
+ s16 Gb;
+ s16 ambient_new_raw, ambient_old_raw;
+
+ ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_P_R, &PR);
+ if (ret < 0)
+ return ret;
+ ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_P_G, &PG);
+ if (ret < 0)
+ return ret;
+ ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_P_T, &PT);
+ if (ret < 0)
+ return ret;
+ ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_P_O, &PO);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(data->regmap, MLX90632_EE_Gb, &read_tmp);
+ if (ret < 0)
+ return ret;
+ Gb = (s16)read_tmp;
+
+ ret = mlx90632_read_ambient_raw(data->regmap, &ambient_new_raw,
+ &ambient_old_raw);
+ if (ret < 0)
+ return ret;
+ *val = mlx90632_calc_temp_ambient(ambient_new_raw, ambient_old_raw,
+ PT, PR, PG, PO, Gb);
+ return ret;
+}
+
+static int mlx90632_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *channel, int *val,
+ int *val2, long mask)
+{
+ struct mlx90632_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ switch (channel->channel2) {
+ case IIO_MOD_TEMP_AMBIENT:
+ ret = mlx90632_calc_ambient_dsp105(data, val);
+ if (ret < 0)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_MOD_TEMP_OBJECT:
+ ret = mlx90632_calc_object_dsp105(data, val);
+ if (ret < 0)
+ return ret;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_CALIBEMISSIVITY:
+ if (data->emissivity == 1000) {
+ *val = 1;
+ *val2 = 0;
+ } else {
+ *val = 0;
+ *val2 = data->emissivity * 1000;
+ }
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mlx90632_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *channel, int val,
+ int val2, long mask)
+{
+ struct mlx90632_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBEMISSIVITY:
+ /* Confirm we are within 0 and 1.0 */
+ if (val < 0 || val2 < 0 || val > 1 ||
+ (val == 1 && val2 != 0))
+ return -EINVAL;
+ data->emissivity = val * 1000 + val2 / 1000;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_chan_spec mlx90632_channels[] = {
+ {
+ .type = IIO_TEMP,
+ .modified = 1,
+ .channel2 = IIO_MOD_TEMP_AMBIENT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ },
+ {
+ .type = IIO_TEMP,
+ .modified = 1,
+ .channel2 = IIO_MOD_TEMP_OBJECT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_CALIBEMISSIVITY),
+ },
+};
+
+static const struct iio_info mlx90632_info = {
+ .read_raw = mlx90632_read_raw,
+ .write_raw = mlx90632_write_raw,
+};
+
+static int mlx90632_sleep(struct mlx90632_data *data)
+{
+ regcache_mark_dirty(data->regmap);
+
+ dev_dbg(&data->client->dev, "Requesting sleep");
+ return mlx90632_pwr_set_sleep_step(data->regmap);
+}
+
+static int mlx90632_wakeup(struct mlx90632_data *data)
+{
+ int ret;
+
+ ret = regcache_sync(data->regmap);
+ if (ret < 0) {
+ dev_err(&data->client->dev,
+ "Failed to sync regmap registers: %d\n", ret);
+ return ret;
+ }
+
+ dev_dbg(&data->client->dev, "Requesting wake-up\n");
+ return mlx90632_pwr_continuous(data->regmap);
+}
+
+static int mlx90632_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct iio_dev *indio_dev;
+ struct mlx90632_data *mlx90632;
+ struct regmap *regmap;
+ int ret;
+ unsigned int read;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*mlx90632));
+ if (!indio_dev) {
+ dev_err(&client->dev, "Failed to allocate device\n");
+ return -ENOMEM;
+ }
+
+ regmap = devm_regmap_init_i2c(client, &mlx90632_regmap);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ dev_err(&client->dev, "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ mlx90632 = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ mlx90632->client = client;
+ mlx90632->regmap = regmap;
+
+ mutex_init(&mlx90632->lock);
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->name = id->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &mlx90632_info;
+ indio_dev->channels = mlx90632_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mlx90632_channels);
+
+ ret = mlx90632_wakeup(mlx90632);
+ if (ret < 0) {
+ dev_err(&client->dev, "Wakeup failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read(mlx90632->regmap, MLX90632_EE_VERSION, &read);
+ if (ret < 0) {
+ dev_err(&client->dev, "read of version failed: %d\n", ret);
+ return ret;
+ }
+ if (read == MLX90632_ID_MEDICAL) {
+ dev_dbg(&client->dev,
+ "Detected Medical EEPROM calibration %x\n", read);
+ } else if (read == MLX90632_ID_CONSUMER) {
+ dev_dbg(&client->dev,
+ "Detected Consumer EEPROM calibration %x\n", read);
+ } else {
+ dev_err(&client->dev,
+ "EEPROM version mismatch %x (expected %x or %x)\n",
+ read, MLX90632_ID_CONSUMER, MLX90632_ID_MEDICAL);
+ return -EPROTONOSUPPORT;
+ }
+
+ mlx90632->emissivity = 1000;
+
+ pm_runtime_disable(&client->dev);
+ ret = pm_runtime_set_active(&client->dev);
+ if (ret < 0) {
+ mlx90632_sleep(mlx90632);
+ return ret;
+ }
+ pm_runtime_enable(&client->dev);
+ pm_runtime_set_autosuspend_delay(&client->dev, MLX90632_SLEEP_DELAY_MS);
+ pm_runtime_use_autosuspend(&client->dev);
+
+ return iio_device_register(indio_dev);
+}
+
+static int mlx90632_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct mlx90632_data *data = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ pm_runtime_disable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+ pm_runtime_put_noidle(&client->dev);
+
+ mlx90632_sleep(data);
+
+ return 0;
+}
+
+static const struct i2c_device_id mlx90632_id[] = {
+ { "mlx90632", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mlx90632_id);
+
+static const struct of_device_id mlx90632_of_match[] = {
+ { .compatible = "melexis,mlx90632" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mlx90632_of_match);
+
+static int __maybe_unused mlx90632_pm_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct mlx90632_data *data = iio_priv(indio_dev);
+
+ return mlx90632_sleep(data);
+}
+
+static int __maybe_unused mlx90632_pm_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct mlx90632_data *data = iio_priv(indio_dev);
+
+ return mlx90632_wakeup(data);
+}
+
+static UNIVERSAL_DEV_PM_OPS(mlx90632_pm_ops, mlx90632_pm_suspend,
+ mlx90632_pm_resume, NULL);
+
+static struct i2c_driver mlx90632_driver = {
+ .driver = {
+ .name = "mlx90632",
+ .of_match_table = mlx90632_of_match,
+ .pm = &mlx90632_pm_ops,
+ },
+ .probe = mlx90632_probe,
+ .remove = mlx90632_remove,
+ .id_table = mlx90632_id,
+};
+module_i2c_driver(mlx90632_driver);
+
+MODULE_AUTHOR("Crt Mori <cmo@melexis.com>");
+MODULE_DESCRIPTION("Melexis MLX90632 contactless Infra Red temperature sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/infiniband/Kconfig b/drivers/infiniband/Kconfig
index 8517d6ea91a6..ee270e065ba9 100644
--- a/drivers/infiniband/Kconfig
+++ b/drivers/infiniband/Kconfig
@@ -35,14 +35,13 @@ config INFINIBAND_USER_ACCESS
libibverbs, libibcm and a hardware driver library from
rdma-core <https://github.com/linux-rdma/rdma-core>.
-config INFINIBAND_EXP_USER_ACCESS
- bool "Enable the full uverbs ioctl interface (EXPERIMENTAL)"
+config INFINIBAND_EXP_LEGACY_VERBS_NEW_UAPI
+ bool "Allow experimental legacy verbs in new ioctl uAPI (EXPERIMENTAL)"
depends on INFINIBAND_USER_ACCESS
---help---
- IOCTL based ABI support for Infiniband. This allows userspace
- to invoke the experimental IOCTL based ABI.
- These commands are parsed via per-device parsing tree and
- enables per-device features.
+ IOCTL based uAPI support for Infiniband is enabled by default for
+ new verbs only. This allows userspace to invoke the IOCTL based uAPI
+ for current legacy verbs too.
config INFINIBAND_USER_MEM
bool
diff --git a/drivers/infiniband/core/Makefile b/drivers/infiniband/core/Makefile
index f69833db0a32..dda9e856e3fa 100644
--- a/drivers/infiniband/core/Makefile
+++ b/drivers/infiniband/core/Makefile
@@ -34,4 +34,6 @@ ib_ucm-y := ucm.o
ib_uverbs-y := uverbs_main.o uverbs_cmd.o uverbs_marshall.o \
rdma_core.o uverbs_std_types.o uverbs_ioctl.o \
- uverbs_ioctl_merge.o
+ uverbs_ioctl_merge.o uverbs_std_types_cq.o \
+ uverbs_std_types_flow_action.o uverbs_std_types_dm.o \
+ uverbs_std_types_mr.o
diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c
index 9183d148d644..88a7542d8c7b 100644
--- a/drivers/infiniband/core/addr.c
+++ b/drivers/infiniband/core/addr.c
@@ -207,6 +207,22 @@ int rdma_addr_size(struct sockaddr *addr)
}
EXPORT_SYMBOL(rdma_addr_size);
+int rdma_addr_size_in6(struct sockaddr_in6 *addr)
+{
+ int ret = rdma_addr_size((struct sockaddr *) addr);
+
+ return ret <= sizeof(*addr) ? ret : 0;
+}
+EXPORT_SYMBOL(rdma_addr_size_in6);
+
+int rdma_addr_size_kss(struct __kernel_sockaddr_storage *addr)
+{
+ int ret = rdma_addr_size((struct sockaddr *) addr);
+
+ return ret <= sizeof(*addr) ? ret : 0;
+}
+EXPORT_SYMBOL(rdma_addr_size_kss);
+
static struct rdma_addr_client self;
void rdma_addr_register_client(struct rdma_addr_client *client)
@@ -313,7 +329,8 @@ static void queue_req(struct addr_req *req)
mutex_unlock(&lock);
}
-static int ib_nl_fetch_ha(struct dst_entry *dst, struct rdma_dev_addr *dev_addr,
+static int ib_nl_fetch_ha(const struct dst_entry *dst,
+ struct rdma_dev_addr *dev_addr,
const void *daddr, u32 seq, u16 family)
{
if (rdma_nl_chk_listeners(RDMA_NL_GROUP_LS))
@@ -324,7 +341,8 @@ static int ib_nl_fetch_ha(struct dst_entry *dst, struct rdma_dev_addr *dev_addr,
return ib_nl_ip_send_msg(dev_addr, daddr, seq, family);
}
-static int dst_fetch_ha(struct dst_entry *dst, struct rdma_dev_addr *dev_addr,
+static int dst_fetch_ha(const struct dst_entry *dst,
+ struct rdma_dev_addr *dev_addr,
const void *daddr)
{
struct neighbour *n;
@@ -348,7 +366,7 @@ static int dst_fetch_ha(struct dst_entry *dst, struct rdma_dev_addr *dev_addr,
return ret;
}
-static bool has_gateway(struct dst_entry *dst, sa_family_t family)
+static bool has_gateway(const struct dst_entry *dst, sa_family_t family)
{
struct rtable *rt;
struct rt6_info *rt6;
@@ -362,7 +380,7 @@ static bool has_gateway(struct dst_entry *dst, sa_family_t family)
return rt6->rt6i_flags & RTF_GATEWAY;
}
-static int fetch_ha(struct dst_entry *dst, struct rdma_dev_addr *dev_addr,
+static int fetch_ha(const struct dst_entry *dst, struct rdma_dev_addr *dev_addr,
const struct sockaddr *dst_in, u32 seq)
{
const struct sockaddr_in *dst_in4 =
@@ -466,7 +484,7 @@ static int addr6_resolve(struct sockaddr_in6 *src_in,
}
#endif
-static int addr_resolve_neigh(struct dst_entry *dst,
+static int addr_resolve_neigh(const struct dst_entry *dst,
const struct sockaddr *dst_in,
struct rdma_dev_addr *addr,
u32 seq)
@@ -586,6 +604,15 @@ static void process_one_req(struct work_struct *_work)
list_del(&req->list);
mutex_unlock(&lock);
+ /*
+ * Although the work will normally have been canceled by the
+ * workqueue, it can still be requeued as long as it is on the
+ * req_list, so it could have been requeued before we grabbed &lock.
+ * We need to cancel it after it is removed from req_list to really be
+ * sure it is safe to free.
+ */
+ cancel_delayed_work(&req->work);
+
req->callback(req->status, (struct sockaddr *)&req->src_addr,
req->addr, req->context);
put_client(req->client);
@@ -711,7 +738,6 @@ int rdma_resolve_ip_route(struct sockaddr *src_addr,
return addr_resolve(src_in, dst_addr, addr, false, 0);
}
-EXPORT_SYMBOL(rdma_resolve_ip_route);
void rdma_addr_cancel(struct rdma_dev_addr *addr)
{
diff --git a/drivers/infiniband/core/cache.c b/drivers/infiniband/core/cache.c
index e9a409d7f4e2..e337b08de2ff 100644
--- a/drivers/infiniband/core/cache.c
+++ b/drivers/infiniband/core/cache.c
@@ -59,8 +59,6 @@ struct ib_update_work {
union ib_gid zgid;
EXPORT_SYMBOL(zgid);
-static const struct ib_gid_attr zattr;
-
enum gid_attr_find_mask {
GID_ATTR_FIND_MASK_GID = 1UL << 0,
GID_ATTR_FIND_MASK_NETDEV = 1UL << 1,
@@ -73,15 +71,6 @@ enum gid_table_entry_props {
GID_TABLE_ENTRY_DEFAULT = 1UL << 1,
};
-enum gid_table_write_action {
- GID_TABLE_WRITE_ACTION_ADD,
- GID_TABLE_WRITE_ACTION_DEL,
- /* MODIFY only updates the GID table. Currently only used by
- * ib_cache_update.
- */
- GID_TABLE_WRITE_ACTION_MODIFY
-};
-
struct ib_gid_table_entry {
unsigned long props;
union ib_gid gid;
@@ -100,31 +89,26 @@ struct ib_gid_table {
* (a) Find the GID
* (b) Delete it.
*
- * Add/delete should be carried out atomically.
- * This is done by locking this mutex from multiple
- * writers. We don't need this lock for IB, as the MAD
- * layer replaces all entries. All data_vec entries
- * are locked by this lock.
**/
- struct mutex lock;
- /* This lock protects the table entries from being
- * read and written simultaneously.
+ /* Any writer to data_vec must hold this lock and the write side of
+ * rwlock. readers must hold only rwlock. All writers must be in a
+ * sleepable context.
*/
+ struct mutex lock;
+ /* rwlock protects data_vec[ix]->props. */
rwlock_t rwlock;
struct ib_gid_table_entry *data_vec;
};
static void dispatch_gid_change_event(struct ib_device *ib_dev, u8 port)
{
- if (rdma_cap_roce_gid_table(ib_dev, port)) {
- struct ib_event event;
+ struct ib_event event;
- event.device = ib_dev;
- event.element.port_num = port;
- event.event = IB_EVENT_GID_CHANGE;
+ event.device = ib_dev;
+ event.element.port_num = port;
+ event.event = IB_EVENT_GID_CHANGE;
- ib_dispatch_event(&event);
- }
+ ib_dispatch_event(&event);
}
static const char * const gid_type_str[] = {
@@ -165,94 +149,127 @@ int ib_cache_gid_parse_type_str(const char *buf)
}
EXPORT_SYMBOL(ib_cache_gid_parse_type_str);
-/* This function expects that rwlock will be write locked in all
- * scenarios and that lock will be locked in sleep-able (RoCE)
- * scenarios.
- */
-static int write_gid(struct ib_device *ib_dev, u8 port,
- struct ib_gid_table *table, int ix,
- const union ib_gid *gid,
- const struct ib_gid_attr *attr,
- enum gid_table_write_action action,
- bool default_gid)
- __releases(&table->rwlock) __acquires(&table->rwlock)
+static void del_roce_gid(struct ib_device *device, u8 port_num,
+ struct ib_gid_table *table, int ix)
{
- int ret = 0;
- struct net_device *old_net_dev;
- enum ib_gid_type old_gid_type;
+ pr_debug("%s device=%s port=%d index=%d gid %pI6\n", __func__,
+ device->name, port_num, ix,
+ table->data_vec[ix].gid.raw);
+
+ if (rdma_cap_roce_gid_table(device, port_num))
+ device->del_gid(&table->data_vec[ix].attr,
+ &table->data_vec[ix].context);
+ dev_put(table->data_vec[ix].attr.ndev);
+}
- /* in rdma_cap_roce_gid_table, this funciton should be protected by a
- * sleep-able lock.
- */
+static int add_roce_gid(struct ib_gid_table *table,
+ const union ib_gid *gid,
+ const struct ib_gid_attr *attr)
+{
+ struct ib_gid_table_entry *entry;
+ int ix = attr->index;
+ int ret = 0;
- if (rdma_cap_roce_gid_table(ib_dev, port)) {
- table->data_vec[ix].props |= GID_TABLE_ENTRY_INVALID;
- write_unlock_irq(&table->rwlock);
- /* GID_TABLE_WRITE_ACTION_MODIFY currently isn't supported by
- * RoCE providers and thus only updates the cache.
- */
- if (action == GID_TABLE_WRITE_ACTION_ADD)
- ret = ib_dev->add_gid(ib_dev, port, ix, gid, attr,
- &table->data_vec[ix].context);
- else if (action == GID_TABLE_WRITE_ACTION_DEL)
- ret = ib_dev->del_gid(ib_dev, port, ix,
- &table->data_vec[ix].context);
- write_lock_irq(&table->rwlock);
+ if (!attr->ndev) {
+ pr_err("%s NULL netdev device=%s port=%d index=%d\n",
+ __func__, attr->device->name, attr->port_num,
+ attr->index);
+ return -EINVAL;
}
- old_net_dev = table->data_vec[ix].attr.ndev;
- old_gid_type = table->data_vec[ix].attr.gid_type;
- if (old_net_dev && old_net_dev != attr->ndev)
- dev_put(old_net_dev);
- /* if modify_gid failed, just delete the old gid */
- if (ret || action == GID_TABLE_WRITE_ACTION_DEL) {
- gid = &zgid;
- attr = &zattr;
- table->data_vec[ix].context = NULL;
+ entry = &table->data_vec[ix];
+ if ((entry->props & GID_TABLE_ENTRY_INVALID) == 0) {
+ WARN(1, "GID table corruption device=%s port=%d index=%d\n",
+ attr->device->name, attr->port_num,
+ attr->index);
+ return -EINVAL;
}
- memcpy(&table->data_vec[ix].gid, gid, sizeof(*gid));
- memcpy(&table->data_vec[ix].attr, attr, sizeof(*attr));
- if (default_gid) {
- table->data_vec[ix].props |= GID_TABLE_ENTRY_DEFAULT;
- if (action == GID_TABLE_WRITE_ACTION_DEL)
- table->data_vec[ix].attr.gid_type = old_gid_type;
+ if (rdma_cap_roce_gid_table(attr->device, attr->port_num)) {
+ ret = attr->device->add_gid(gid, attr, &entry->context);
+ if (ret) {
+ pr_err("%s GID add failed device=%s port=%d index=%d\n",
+ __func__, attr->device->name, attr->port_num,
+ attr->index);
+ goto add_err;
+ }
}
- if (table->data_vec[ix].attr.ndev &&
- table->data_vec[ix].attr.ndev != old_net_dev)
- dev_hold(table->data_vec[ix].attr.ndev);
-
- table->data_vec[ix].props &= ~GID_TABLE_ENTRY_INVALID;
+ dev_hold(attr->ndev);
+add_err:
+ if (!ret)
+ pr_debug("%s device=%s port=%d index=%d gid %pI6\n", __func__,
+ attr->device->name, attr->port_num, ix, gid->raw);
return ret;
}
-static int add_gid(struct ib_device *ib_dev, u8 port,
- struct ib_gid_table *table, int ix,
- const union ib_gid *gid,
- const struct ib_gid_attr *attr,
- bool default_gid) {
- return write_gid(ib_dev, port, table, ix, gid, attr,
- GID_TABLE_WRITE_ACTION_ADD, default_gid);
-}
+/**
+ * add_modify_gid - Add or modify GID table entry
+ *
+ * @table: GID table in which GID to be added or modified
+ * @gid: GID content
+ * @attr: Attributes of the GID
+ *
+ * Returns 0 on success or appropriate error code. It accepts zero
+ * GID addition for non RoCE ports for HCA's who report them as valid
+ * GID. However such zero GIDs are not added to the cache.
+ */
+static int add_modify_gid(struct ib_gid_table *table,
+ const union ib_gid *gid,
+ const struct ib_gid_attr *attr)
+{
+ int ret;
+
+ if (rdma_protocol_roce(attr->device, attr->port_num)) {
+ ret = add_roce_gid(table, gid, attr);
+ if (ret)
+ return ret;
+ } else {
+ /*
+ * Some HCA's report multiple GID entries with only one
+ * valid GID, but remaining as zero GID.
+ * So ignore such behavior for IB link layer and don't
+ * fail the call, but don't add such entry to GID cache.
+ */
+ if (!memcmp(gid, &zgid, sizeof(*gid)))
+ return 0;
+ }
+
+ lockdep_assert_held(&table->lock);
+ memcpy(&table->data_vec[attr->index].gid, gid, sizeof(*gid));
+ memcpy(&table->data_vec[attr->index].attr, attr, sizeof(*attr));
-static int modify_gid(struct ib_device *ib_dev, u8 port,
- struct ib_gid_table *table, int ix,
- const union ib_gid *gid,
- const struct ib_gid_attr *attr,
- bool default_gid) {
- return write_gid(ib_dev, port, table, ix, gid, attr,
- GID_TABLE_WRITE_ACTION_MODIFY, default_gid);
+ write_lock_irq(&table->rwlock);
+ table->data_vec[attr->index].props &= ~GID_TABLE_ENTRY_INVALID;
+ write_unlock_irq(&table->rwlock);
+ return 0;
}
-static int del_gid(struct ib_device *ib_dev, u8 port,
- struct ib_gid_table *table, int ix,
- bool default_gid) {
- return write_gid(ib_dev, port, table, ix, &zgid, &zattr,
- GID_TABLE_WRITE_ACTION_DEL, default_gid);
+/**
+ * del_gid - Delete GID table entry
+ *
+ * @ib_dev: IB device whose GID entry to be deleted
+ * @port: Port number of the IB device
+ * @table: GID table of the IB device for a port
+ * @ix: GID entry index to delete
+ *
+ */
+static void del_gid(struct ib_device *ib_dev, u8 port,
+ struct ib_gid_table *table, int ix)
+{
+ lockdep_assert_held(&table->lock);
+ write_lock_irq(&table->rwlock);
+ table->data_vec[ix].props |= GID_TABLE_ENTRY_INVALID;
+ write_unlock_irq(&table->rwlock);
+
+ if (rdma_protocol_roce(ib_dev, port))
+ del_roce_gid(ib_dev, port, table, ix);
+ memcpy(&table->data_vec[ix].gid, &zgid, sizeof(zgid));
+ memset(&table->data_vec[ix].attr, 0, sizeof(table->data_vec[ix].attr));
+ table->data_vec[ix].context = NULL;
}
-/* rwlock should be read locked */
+/* rwlock should be read locked, or lock should be held */
static int find_gid(struct ib_gid_table *table, const union ib_gid *gid,
const struct ib_gid_attr *val, bool default_gid,
unsigned long mask, int *pempty)
@@ -268,15 +285,32 @@ static int find_gid(struct ib_gid_table *table, const union ib_gid *gid,
i++;
+ /* find_gid() is used during GID addition where it is expected
+ * to return a free entry slot which is not duplicate.
+ * Free entry slot is requested and returned if pempty is set,
+ * so lookup free slot only if requested.
+ */
+ if (pempty && empty < 0) {
+ if (data->props & GID_TABLE_ENTRY_INVALID) {
+ /* Found an invalid (free) entry; allocate it */
+ if (data->props & GID_TABLE_ENTRY_DEFAULT) {
+ if (default_gid)
+ empty = curr_index;
+ } else {
+ empty = curr_index;
+ }
+ }
+ }
+
+ /*
+ * Additionally find_gid() is used to find valid entry during
+ * lookup operation, where validity needs to be checked. So
+ * find the empty entry first to continue to search for a free
+ * slot and ignore its INVALID flag.
+ */
if (data->props & GID_TABLE_ENTRY_INVALID)
continue;
- if (empty < 0)
- if (!memcmp(&data->gid, &zgid, sizeof(*gid)) &&
- !memcmp(attr, &zattr, sizeof(*attr)) &&
- !data->props)
- empty = curr_index;
-
if (found >= 0)
continue;
@@ -312,20 +346,56 @@ static void make_default_gid(struct net_device *dev, union ib_gid *gid)
addrconf_ifid_eui48(&gid->raw[8], dev);
}
-int ib_cache_gid_add(struct ib_device *ib_dev, u8 port,
- union ib_gid *gid, struct ib_gid_attr *attr)
+static int __ib_cache_gid_add(struct ib_device *ib_dev, u8 port,
+ union ib_gid *gid, struct ib_gid_attr *attr,
+ unsigned long mask, bool default_gid)
{
struct ib_gid_table *table;
- int ix;
int ret = 0;
- struct net_device *idev;
int empty;
+ int ix;
- table = ib_dev->cache.ports[port - rdma_start_port(ib_dev)].gid;
-
+ /* Do not allow adding zero GID in support of
+ * IB spec version 1.3 section 4.1.1 point (6) and
+ * section 12.7.10 and section 12.7.20
+ */
if (!memcmp(gid, &zgid, sizeof(*gid)))
return -EINVAL;
+ table = ib_dev->cache.ports[port - rdma_start_port(ib_dev)].gid;
+
+ mutex_lock(&table->lock);
+
+ ix = find_gid(table, gid, attr, default_gid, mask, &empty);
+ if (ix >= 0)
+ goto out_unlock;
+
+ if (empty < 0) {
+ ret = -ENOSPC;
+ goto out_unlock;
+ }
+ attr->device = ib_dev;
+ attr->index = empty;
+ attr->port_num = port;
+ ret = add_modify_gid(table, gid, attr);
+ if (!ret)
+ dispatch_gid_change_event(ib_dev, port);
+
+out_unlock:
+ mutex_unlock(&table->lock);
+ if (ret)
+ pr_warn("%s: unable to add gid %pI6 error=%d\n",
+ __func__, gid->raw, ret);
+ return ret;
+}
+
+int ib_cache_gid_add(struct ib_device *ib_dev, u8 port,
+ union ib_gid *gid, struct ib_gid_attr *attr)
+{
+ struct net_device *idev;
+ unsigned long mask;
+ int ret;
+
if (ib_dev->get_netdev) {
idev = ib_dev->get_netdev(ib_dev, port);
if (idev && attr->ndev != idev) {
@@ -342,27 +412,11 @@ int ib_cache_gid_add(struct ib_device *ib_dev, u8 port,
dev_put(idev);
}
- mutex_lock(&table->lock);
- write_lock_irq(&table->rwlock);
-
- ix = find_gid(table, gid, attr, false, GID_ATTR_FIND_MASK_GID |
- GID_ATTR_FIND_MASK_GID_TYPE |
- GID_ATTR_FIND_MASK_NETDEV, &empty);
- if (ix >= 0)
- goto out_unlock;
-
- if (empty < 0) {
- ret = -ENOSPC;
- goto out_unlock;
- }
-
- ret = add_gid(ib_dev, port, table, empty, gid, attr, false);
- if (!ret)
- dispatch_gid_change_event(ib_dev, port);
+ mask = GID_ATTR_FIND_MASK_GID |
+ GID_ATTR_FIND_MASK_GID_TYPE |
+ GID_ATTR_FIND_MASK_NETDEV;
-out_unlock:
- write_unlock_irq(&table->rwlock);
- mutex_unlock(&table->lock);
+ ret = __ib_cache_gid_add(ib_dev, port, gid, attr, mask, false);
return ret;
}
@@ -370,29 +424,32 @@ int ib_cache_gid_del(struct ib_device *ib_dev, u8 port,
union ib_gid *gid, struct ib_gid_attr *attr)
{
struct ib_gid_table *table;
+ int ret = 0;
int ix;
table = ib_dev->cache.ports[port - rdma_start_port(ib_dev)].gid;
mutex_lock(&table->lock);
- write_lock_irq(&table->rwlock);
ix = find_gid(table, gid, attr, false,
GID_ATTR_FIND_MASK_GID |
GID_ATTR_FIND_MASK_GID_TYPE |
- GID_ATTR_FIND_MASK_NETDEV |
- GID_ATTR_FIND_MASK_DEFAULT,
+ GID_ATTR_FIND_MASK_NETDEV,
NULL);
- if (ix < 0)
+ if (ix < 0) {
+ ret = -EINVAL;
goto out_unlock;
+ }
- if (!del_gid(ib_dev, port, table, ix, false))
- dispatch_gid_change_event(ib_dev, port);
+ del_gid(ib_dev, port, table, ix);
+ dispatch_gid_change_event(ib_dev, port);
out_unlock:
- write_unlock_irq(&table->rwlock);
mutex_unlock(&table->lock);
- return 0;
+ if (ret)
+ pr_debug("%s: can't delete gid %pI6 error=%d\n",
+ __func__, gid->raw, ret);
+ return ret;
}
int ib_cache_gid_del_all_netdev_gids(struct ib_device *ib_dev, u8 port,
@@ -405,16 +462,14 @@ int ib_cache_gid_del_all_netdev_gids(struct ib_device *ib_dev, u8 port,
table = ib_dev->cache.ports[port - rdma_start_port(ib_dev)].gid;
mutex_lock(&table->lock);
- write_lock_irq(&table->rwlock);
- for (ix = 0; ix < table->sz; ix++)
- if (table->data_vec[ix].attr.ndev == ndev)
- if (!del_gid(ib_dev, port, table, ix,
- !!(table->data_vec[ix].props &
- GID_TABLE_ENTRY_DEFAULT)))
- deleted = true;
+ for (ix = 0; ix < table->sz; ix++) {
+ if (table->data_vec[ix].attr.ndev == ndev) {
+ del_gid(ib_dev, port, table, ix);
+ deleted = true;
+ }
+ }
- write_unlock_irq(&table->rwlock);
mutex_unlock(&table->lock);
if (deleted)
@@ -492,6 +547,19 @@ static int ib_cache_gid_find(struct ib_device *ib_dev,
mask, port, index);
}
+/**
+ * ib_find_cached_gid_by_port - Returns the GID table index where a specified
+ * GID value occurs. It searches for the specified GID value in the local
+ * software cache.
+ * @device: The device to query.
+ * @gid: The GID value to search for.
+ * @gid_type: The GID type to search for.
+ * @port_num: The port number of the device where the GID value should be
+ * searched.
+ * @ndev: In RoCE, the net device of the device. Null means ignore.
+ * @index: The index into the cached GID table where the GID was found. This
+ * parameter may be NULL.
+ */
int ib_find_cached_gid_by_port(struct ib_device *ib_dev,
const union ib_gid *gid,
enum ib_gid_type gid_type,
@@ -528,7 +596,7 @@ int ib_find_cached_gid_by_port(struct ib_device *ib_dev,
EXPORT_SYMBOL(ib_find_cached_gid_by_port);
/**
- * ib_find_gid_by_filter - Returns the GID table index where a specified
+ * ib_cache_gid_find_by_filter - Returns the GID table index where a specified
* GID value occurs
* @device: The device to query.
* @gid: The GID value to search for.
@@ -539,7 +607,7 @@ EXPORT_SYMBOL(ib_find_cached_gid_by_port);
* otherwise, we continue searching the GID table. It's guaranteed that
* while filter is executed, ndev field is valid and the structure won't
* change. filter is executed in an atomic context. filter must not be NULL.
- * @index: The index into the cached GID table where the GID was found. This
+ * @index: The index into the cached GID table where the GID was found. This
* parameter may be NULL.
*
* ib_cache_gid_find_by_filter() searches for the specified GID value
@@ -598,6 +666,7 @@ static struct ib_gid_table *alloc_gid_table(int sz)
{
struct ib_gid_table *table =
kzalloc(sizeof(struct ib_gid_table), GFP_KERNEL);
+ int i;
if (!table)
return NULL;
@@ -611,6 +680,11 @@ static struct ib_gid_table *alloc_gid_table(int sz)
table->sz = sz;
rwlock_init(&table->rwlock);
+ /* Mark all entries as invalid so that allocator can allocate
+ * one of the invalid (free) entry.
+ */
+ for (i = 0; i < sz; i++)
+ table->data_vec[i].props |= GID_TABLE_ENTRY_INVALID;
return table;
err_free_table:
@@ -635,16 +709,15 @@ static void cleanup_gid_table_port(struct ib_device *ib_dev, u8 port,
if (!table)
return;
- write_lock_irq(&table->rwlock);
+ mutex_lock(&table->lock);
for (i = 0; i < table->sz; ++i) {
if (memcmp(&table->data_vec[i].gid, &zgid,
- sizeof(table->data_vec[i].gid)))
- if (!del_gid(ib_dev, port, table, i,
- table->data_vec[i].props &
- GID_ATTR_FIND_MASK_DEFAULT))
- deleted = true;
+ sizeof(table->data_vec[i].gid))) {
+ del_gid(ib_dev, port, table, i);
+ deleted = true;
+ }
}
- write_unlock_irq(&table->rwlock);
+ mutex_unlock(&table->lock);
if (deleted)
dispatch_gid_change_event(ib_dev, port);
@@ -657,9 +730,9 @@ void ib_cache_gid_set_default_gid(struct ib_device *ib_dev, u8 port,
{
union ib_gid gid;
struct ib_gid_attr gid_attr;
- struct ib_gid_attr zattr_type = zattr;
struct ib_gid_table *table;
unsigned int gid_type;
+ unsigned long mask;
table = ib_dev->cache.ports[port - rdma_start_port(ib_dev)].gid;
@@ -668,60 +741,19 @@ void ib_cache_gid_set_default_gid(struct ib_device *ib_dev, u8 port,
gid_attr.ndev = ndev;
for (gid_type = 0; gid_type < IB_GID_TYPE_SIZE; ++gid_type) {
- int ix;
- union ib_gid current_gid;
- struct ib_gid_attr current_gid_attr = {};
-
if (1UL << gid_type & ~gid_type_mask)
continue;
gid_attr.gid_type = gid_type;
- mutex_lock(&table->lock);
- write_lock_irq(&table->rwlock);
- ix = find_gid(table, NULL, &gid_attr, true,
- GID_ATTR_FIND_MASK_GID_TYPE |
- GID_ATTR_FIND_MASK_DEFAULT,
- NULL);
-
- /* Coudn't find default GID location */
- if (WARN_ON(ix < 0))
- goto release;
-
- zattr_type.gid_type = gid_type;
-
- if (!__ib_cache_gid_get(ib_dev, port, ix,
- &current_gid, &current_gid_attr) &&
- mode == IB_CACHE_GID_DEFAULT_MODE_SET &&
- !memcmp(&gid, &current_gid, sizeof(gid)) &&
- !memcmp(&gid_attr, &current_gid_attr, sizeof(gid_attr)))
- goto release;
-
- if (memcmp(&current_gid, &zgid, sizeof(current_gid)) ||
- memcmp(&current_gid_attr, &zattr_type,
- sizeof(current_gid_attr))) {
- if (del_gid(ib_dev, port, table, ix, true)) {
- pr_warn("ib_cache_gid: can't delete index %d for default gid %pI6\n",
- ix, gid.raw);
- goto release;
- } else {
- dispatch_gid_change_event(ib_dev, port);
- }
- }
-
if (mode == IB_CACHE_GID_DEFAULT_MODE_SET) {
- if (add_gid(ib_dev, port, table, ix, &gid, &gid_attr, true))
- pr_warn("ib_cache_gid: unable to add default gid %pI6\n",
- gid.raw);
- else
- dispatch_gid_change_event(ib_dev, port);
+ mask = GID_ATTR_FIND_MASK_GID_TYPE |
+ GID_ATTR_FIND_MASK_DEFAULT;
+ __ib_cache_gid_add(ib_dev, port, &gid,
+ &gid_attr, mask, true);
+ } else if (mode == IB_CACHE_GID_DEFAULT_MODE_DELETE) {
+ ib_cache_gid_del(ib_dev, port, &gid, &gid_attr);
}
-
-release:
- if (current_gid_attr.ndev)
- dev_put(current_gid_attr.ndev);
- write_unlock_irq(&table->rwlock);
- mutex_unlock(&table->lock);
}
}
@@ -848,6 +880,20 @@ int ib_get_cached_gid(struct ib_device *device,
}
EXPORT_SYMBOL(ib_get_cached_gid);
+/**
+ * ib_find_cached_gid - Returns the port number and GID table index where
+ * a specified GID value occurs.
+ * @device: The device to query.
+ * @gid: The GID value to search for.
+ * @gid_type: The GID type to search for.
+ * @ndev: In RoCE, the net device of the device. NULL means ignore.
+ * @port_num: The port number of the device where the GID value was found.
+ * @index: The index into the cached GID table where the GID was found. This
+ * parameter may be NULL.
+ *
+ * ib_find_cached_gid() searches for the specified GID value in
+ * the local software cache.
+ */
int ib_find_cached_gid(struct ib_device *device,
const union ib_gid *gid,
enum ib_gid_type gid_type,
@@ -868,7 +914,7 @@ int ib_find_gid_by_filter(struct ib_device *device,
void *context, u16 *index)
{
/* Only RoCE GID table supports filter function */
- if (!rdma_cap_roce_gid_table(device, port_num) && filter)
+ if (!rdma_protocol_roce(device, port_num) && filter)
return -EPROTONOSUPPORT;
return ib_cache_gid_find_by_filter(device, gid,
@@ -910,8 +956,7 @@ int ib_get_cached_subnet_prefix(struct ib_device *device,
unsigned long flags;
int p;
- if (port_num < rdma_start_port(device) ||
- port_num > rdma_end_port(device))
+ if (!rdma_is_port_valid(device, port_num))
return -EINVAL;
p = port_num - rdma_start_port(device);
@@ -1021,7 +1066,7 @@ int ib_get_cached_port_state(struct ib_device *device,
unsigned long flags;
int ret = 0;
- if (port_num < rdma_start_port(device) || port_num > rdma_end_port(device))
+ if (!rdma_is_port_valid(device, port_num))
return -EINVAL;
read_lock_irqsave(&device->cache.lock, flags);
@@ -1033,21 +1078,46 @@ int ib_get_cached_port_state(struct ib_device *device,
}
EXPORT_SYMBOL(ib_get_cached_port_state);
+static int config_non_roce_gid_cache(struct ib_device *device,
+ u8 port, int gid_tbl_len)
+{
+ struct ib_gid_attr gid_attr = {};
+ struct ib_gid_table *table;
+ union ib_gid gid;
+ int ret = 0;
+ int i;
+
+ gid_attr.device = device;
+ gid_attr.port_num = port;
+ table = device->cache.ports[port - rdma_start_port(device)].gid;
+
+ mutex_lock(&table->lock);
+ for (i = 0; i < gid_tbl_len; ++i) {
+ if (!device->query_gid)
+ continue;
+ ret = device->query_gid(device, port, i, &gid);
+ if (ret) {
+ pr_warn("query_gid failed (%d) for %s (index %d)\n",
+ ret, device->name, i);
+ goto err;
+ }
+ gid_attr.index = i;
+ add_modify_gid(table, &gid, &gid_attr);
+ }
+err:
+ mutex_unlock(&table->lock);
+ return ret;
+}
+
static void ib_cache_update(struct ib_device *device,
u8 port,
bool enforce_security)
{
struct ib_port_attr *tprops = NULL;
struct ib_pkey_cache *pkey_cache = NULL, *old_pkey_cache;
- struct ib_gid_cache {
- int table_len;
- union ib_gid table[0];
- } *gid_cache = NULL;
int i;
int ret;
struct ib_gid_table *table;
- bool use_roce_gid_table =
- rdma_cap_roce_gid_table(device, port);
if (!rdma_is_port_valid(device, port))
return;
@@ -1065,6 +1135,13 @@ static void ib_cache_update(struct ib_device *device,
goto err;
}
+ if (!rdma_protocol_roce(device, port)) {
+ ret = config_non_roce_gid_cache(device, port,
+ tprops->gid_tbl_len);
+ if (ret)
+ goto err;
+ }
+
pkey_cache = kmalloc(sizeof *pkey_cache + tprops->pkey_tbl_len *
sizeof *pkey_cache->table, GFP_KERNEL);
if (!pkey_cache)
@@ -1072,15 +1149,6 @@ static void ib_cache_update(struct ib_device *device,
pkey_cache->table_len = tprops->pkey_tbl_len;
- if (!use_roce_gid_table) {
- gid_cache = kmalloc(sizeof(*gid_cache) + tprops->gid_tbl_len *
- sizeof(*gid_cache->table), GFP_KERNEL);
- if (!gid_cache)
- goto err;
-
- gid_cache->table_len = tprops->gid_tbl_len;
- }
-
for (i = 0; i < pkey_cache->table_len; ++i) {
ret = ib_query_pkey(device, port, i, pkey_cache->table + i);
if (ret) {
@@ -1090,33 +1158,12 @@ static void ib_cache_update(struct ib_device *device,
}
}
- if (!use_roce_gid_table) {
- for (i = 0; i < gid_cache->table_len; ++i) {
- ret = ib_query_gid(device, port, i,
- gid_cache->table + i, NULL);
- if (ret) {
- pr_warn("ib_query_gid failed (%d) for %s (index %d)\n",
- ret, device->name, i);
- goto err;
- }
- }
- }
-
write_lock_irq(&device->cache.lock);
old_pkey_cache = device->cache.ports[port -
rdma_start_port(device)].pkey;
device->cache.ports[port - rdma_start_port(device)].pkey = pkey_cache;
- if (!use_roce_gid_table) {
- write_lock(&table->rwlock);
- for (i = 0; i < gid_cache->table_len; i++) {
- modify_gid(device, port, table, i, gid_cache->table + i,
- &zattr, false);
- }
- write_unlock(&table->rwlock);
- }
-
device->cache.ports[port - rdma_start_port(device)].lmc = tprops->lmc;
device->cache.ports[port - rdma_start_port(device)].port_state =
tprops->state;
@@ -1130,14 +1177,12 @@ static void ib_cache_update(struct ib_device *device,
port,
tprops->subnet_prefix);
- kfree(gid_cache);
kfree(old_pkey_cache);
kfree(tprops);
return;
err:
kfree(pkey_cache);
- kfree(gid_cache);
kfree(tprops);
}
diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c
index e6749157fd86..a92e1a5c202b 100644
--- a/drivers/infiniband/core/cm.c
+++ b/drivers/infiniband/core/cm.c
@@ -462,13 +462,31 @@ static int cm_init_av_for_response(struct cm_port *port, struct ib_wc *wc,
grh, &av->ah_attr);
}
-static int cm_init_av_by_path(struct sa_path_rec *path, struct cm_av *av,
- struct cm_id_private *cm_id_priv)
+static int add_cm_id_to_port_list(struct cm_id_private *cm_id_priv,
+ struct cm_av *av,
+ struct cm_port *port)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&cm.lock, flags);
+
+ if (&cm_id_priv->av == av)
+ list_add_tail(&cm_id_priv->prim_list, &port->cm_priv_prim_list);
+ else if (&cm_id_priv->alt_av == av)
+ list_add_tail(&cm_id_priv->altr_list, &port->cm_priv_altr_list);
+ else
+ ret = -EINVAL;
+
+ spin_unlock_irqrestore(&cm.lock, flags);
+ return ret;
+}
+
+static struct cm_port *get_cm_port_from_path(struct sa_path_rec *path)
{
struct cm_device *cm_dev;
struct cm_port *port = NULL;
unsigned long flags;
- int ret;
u8 p;
struct net_device *ndev = ib_get_ndev_from_path(path);
@@ -477,7 +495,7 @@ static int cm_init_av_by_path(struct sa_path_rec *path, struct cm_av *av,
if (!ib_find_cached_gid(cm_dev->ib_device, &path->sgid,
sa_conv_pathrec_to_gid_type(path),
ndev, &p, NULL)) {
- port = cm_dev->port[p-1];
+ port = cm_dev->port[p - 1];
break;
}
}
@@ -485,9 +503,20 @@ static int cm_init_av_by_path(struct sa_path_rec *path, struct cm_av *av,
if (ndev)
dev_put(ndev);
+ return port;
+}
+static int cm_init_av_by_path(struct sa_path_rec *path, struct cm_av *av,
+ struct cm_id_private *cm_id_priv)
+{
+ struct cm_device *cm_dev;
+ struct cm_port *port;
+ int ret;
+
+ port = get_cm_port_from_path(path);
if (!port)
return -EINVAL;
+ cm_dev = port->cm_dev;
ret = ib_find_cached_pkey(cm_dev->ib_device, port->port_num,
be16_to_cpu(path->pkey), &av->pkey_index);
@@ -502,16 +531,7 @@ static int cm_init_av_by_path(struct sa_path_rec *path, struct cm_av *av,
av->timeout = path->packet_life_time + 1;
- spin_lock_irqsave(&cm.lock, flags);
- if (&cm_id_priv->av == av)
- list_add_tail(&cm_id_priv->prim_list, &port->cm_priv_prim_list);
- else if (&cm_id_priv->alt_av == av)
- list_add_tail(&cm_id_priv->altr_list, &port->cm_priv_altr_list);
- else
- ret = -EINVAL;
-
- spin_unlock_irqrestore(&cm.lock, flags);
-
+ ret = add_cm_id_to_port_list(cm_id_priv, av, port);
return ret;
}
@@ -1523,6 +1543,8 @@ static void cm_format_paths_from_req(struct cm_req_msg *req_msg,
cm_req_get_primary_local_ack_timeout(req_msg);
primary_path->packet_life_time -= (primary_path->packet_life_time > 0);
primary_path->service_id = req_msg->service_id;
+ if (sa_path_is_roce(primary_path))
+ primary_path->roce.route_resolved = false;
if (cm_req_has_alt_path(req_msg)) {
alt_path->dgid = req_msg->alt_local_gid;
@@ -1542,6 +1564,9 @@ static void cm_format_paths_from_req(struct cm_req_msg *req_msg,
cm_req_get_alt_local_ack_timeout(req_msg);
alt_path->packet_life_time -= (alt_path->packet_life_time > 0);
alt_path->service_id = req_msg->service_id;
+
+ if (sa_path_is_roce(alt_path))
+ alt_path->roce.route_resolved = false;
}
cm_format_path_lid_from_req(req_msg, primary_path, alt_path);
}
@@ -3150,6 +3175,13 @@ static int cm_lap_handler(struct cm_work *work)
struct ib_mad_send_buf *msg = NULL;
int ret;
+ /* Currently Alternate path messages are not supported for
+ * RoCE link layer.
+ */
+ if (rdma_protocol_roce(work->port->cm_dev->ib_device,
+ work->port->port_num))
+ return -EINVAL;
+
/* todo: verify LAP request and send reject APR if invalid. */
lap_msg = (struct cm_lap_msg *)work->mad_recv_wc->recv_buf.mad;
cm_id_priv = cm_acquire_id(lap_msg->remote_comm_id,
@@ -3299,6 +3331,13 @@ static int cm_apr_handler(struct cm_work *work)
struct cm_apr_msg *apr_msg;
int ret;
+ /* Currently Alternate path messages are not supported for
+ * RoCE link layer.
+ */
+ if (rdma_protocol_roce(work->port->cm_dev->ib_device,
+ work->port->port_num))
+ return -EINVAL;
+
apr_msg = (struct cm_apr_msg *)work->mad_recv_wc->recv_buf.mad;
cm_id_priv = cm_acquire_id(apr_msg->remote_comm_id,
apr_msg->local_comm_id);
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index a5367c5efbe7..51a641002e10 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -62,6 +62,7 @@
#include <rdma/iw_cm.h>
#include "core_priv.h"
+#include "cma_priv.h"
MODULE_AUTHOR("Sean Hefty");
MODULE_DESCRIPTION("Generic RDMA CM Agent");
@@ -174,7 +175,7 @@ static struct cma_pernet *cma_pernet(struct net *net)
return net_generic(net, cma_pernet_id);
}
-static struct idr *cma_pernet_idr(struct net *net, enum rdma_port_space ps)
+static struct idr *cma_pernet_idr(struct net *net, enum rdma_ucm_port_space ps)
{
struct cma_pernet *pernet = cma_pernet(net);
@@ -203,7 +204,7 @@ struct cma_device {
};
struct rdma_bind_list {
- enum rdma_port_space ps;
+ enum rdma_ucm_port_space ps;
struct hlist_head owners;
unsigned short port;
};
@@ -216,7 +217,7 @@ struct class_port_info_context {
u8 port_num;
};
-static int cma_ps_alloc(struct net *net, enum rdma_port_space ps,
+static int cma_ps_alloc(struct net *net, enum rdma_ucm_port_space ps,
struct rdma_bind_list *bind_list, int snum)
{
struct idr *idr = cma_pernet_idr(net, ps);
@@ -225,14 +226,15 @@ static int cma_ps_alloc(struct net *net, enum rdma_port_space ps,
}
static struct rdma_bind_list *cma_ps_find(struct net *net,
- enum rdma_port_space ps, int snum)
+ enum rdma_ucm_port_space ps, int snum)
{
struct idr *idr = cma_pernet_idr(net, ps);
return idr_find(idr, snum);
}
-static void cma_ps_remove(struct net *net, enum rdma_port_space ps, int snum)
+static void cma_ps_remove(struct net *net, enum rdma_ucm_port_space ps,
+ int snum)
{
struct idr *idr = cma_pernet_idr(net, ps);
@@ -327,46 +329,6 @@ struct ib_device *cma_get_ib_dev(struct cma_device *cma_dev)
* We do this by disabling removal notification while a callback is in process,
* and reporting it after the callback completes.
*/
-struct rdma_id_private {
- struct rdma_cm_id id;
-
- struct rdma_bind_list *bind_list;
- struct hlist_node node;
- struct list_head list; /* listen_any_list or cma_device.list */
- struct list_head listen_list; /* per device listens */
- struct cma_device *cma_dev;
- struct list_head mc_list;
-
- int internal_id;
- enum rdma_cm_state state;
- spinlock_t lock;
- struct mutex qp_mutex;
-
- struct completion comp;
- atomic_t refcount;
- struct mutex handler_mutex;
-
- int backlog;
- int timeout_ms;
- struct ib_sa_query *query;
- int query_id;
- union {
- struct ib_cm_id *ib;
- struct iw_cm_id *iw;
- } cm_id;
-
- u32 seq_num;
- u32 qkey;
- u32 qp_num;
- pid_t owner;
- u32 options;
- u8 srq;
- u8 tos;
- bool tos_set;
- u8 reuseaddr;
- u8 afonly;
- enum ib_gid_type gid_type;
-};
struct cma_multicast {
struct rdma_id_private *id_priv;
@@ -505,6 +467,8 @@ static void _cma_attach_to_dev(struct rdma_id_private *id_priv,
id_priv->id.route.addr.dev_addr.transport =
rdma_node_get_transport(cma_dev->device->node_type);
list_add_tail(&id_priv->list, &cma_dev->id_list);
+ id_priv->res.type = RDMA_RESTRACK_CM_ID;
+ rdma_restrack_add(&id_priv->res);
}
static void cma_attach_to_dev(struct rdma_id_private *id_priv,
@@ -777,10 +741,10 @@ static void cma_deref_id(struct rdma_id_private *id_priv)
complete(&id_priv->comp);
}
-struct rdma_cm_id *rdma_create_id(struct net *net,
- rdma_cm_event_handler event_handler,
- void *context, enum rdma_port_space ps,
- enum ib_qp_type qp_type)
+struct rdma_cm_id *__rdma_create_id(struct net *net,
+ rdma_cm_event_handler event_handler,
+ void *context, enum rdma_ucm_port_space ps,
+ enum ib_qp_type qp_type, const char *caller)
{
struct rdma_id_private *id_priv;
@@ -788,7 +752,10 @@ struct rdma_cm_id *rdma_create_id(struct net *net,
if (!id_priv)
return ERR_PTR(-ENOMEM);
- id_priv->owner = task_pid_nr(current);
+ if (caller)
+ id_priv->res.kern_name = caller;
+ else
+ rdma_restrack_set_task(&id_priv->res, current);
id_priv->state = RDMA_CM_IDLE;
id_priv->id.context = context;
id_priv->id.event_handler = event_handler;
@@ -808,7 +775,7 @@ struct rdma_cm_id *rdma_create_id(struct net *net,
return &id_priv->id;
}
-EXPORT_SYMBOL(rdma_create_id);
+EXPORT_SYMBOL(__rdma_create_id);
static int cma_init_ud_qp(struct rdma_id_private *id_priv, struct ib_qp *qp)
{
@@ -1334,7 +1301,7 @@ static bool validate_ipv6_net_dev(struct net_device *net_dev,
IPV6_ADDR_LINKLOCAL;
struct rt6_info *rt = rt6_lookup(dev_net(net_dev), &dst_addr->sin6_addr,
&src_addr->sin6_addr, net_dev->ifindex,
- strict);
+ NULL, strict);
bool ret;
if (!rt)
@@ -1400,7 +1367,7 @@ static struct net_device *cma_get_net_dev(struct ib_cm_event *ib_event,
return net_dev;
}
-static enum rdma_port_space rdma_ps_from_service_id(__be64 service_id)
+static enum rdma_ucm_port_space rdma_ps_from_service_id(__be64 service_id)
{
return (be64_to_cpu(service_id) >> 16) & 0xffff;
}
@@ -1441,21 +1408,12 @@ static bool cma_match_private_data(struct rdma_id_private *id_priv,
return true;
}
-static bool cma_protocol_roce_dev_port(struct ib_device *device, int port_num)
-{
- enum rdma_link_layer ll = rdma_port_get_link_layer(device, port_num);
- enum rdma_transport_type transport =
- rdma_node_get_transport(device->node_type);
-
- return ll == IB_LINK_LAYER_ETHERNET && transport == RDMA_TRANSPORT_IB;
-}
-
static bool cma_protocol_roce(const struct rdma_cm_id *id)
{
struct ib_device *device = id->device;
const int port_num = id->port_num ?: rdma_start_port(device);
- return cma_protocol_roce_dev_port(device, port_num);
+ return rdma_protocol_roce(device, port_num);
}
static bool cma_match_net_dev(const struct rdma_cm_id *id,
@@ -1468,7 +1426,7 @@ static bool cma_match_net_dev(const struct rdma_cm_id *id,
/* This request is an AF_IB request or a RoCE request */
return (!id->port_num || id->port_num == port_num) &&
(addr->src_addr.ss_family == AF_IB ||
- cma_protocol_roce_dev_port(id->device, port_num));
+ rdma_protocol_roce(id->device, port_num));
return !addr->dev_addr.bound_dev_if ||
(net_eq(dev_net(net_dev), addr->dev_addr.net) &&
@@ -1523,7 +1481,7 @@ static struct rdma_id_private *cma_id_from_event(struct ib_cm_id *cm_id,
if (PTR_ERR(*net_dev) == -EAFNOSUPPORT) {
/* Assuming the protocol is AF_IB */
*net_dev = NULL;
- } else if (cma_protocol_roce_dev_port(req.device, req.port)) {
+ } else if (rdma_protocol_roce(req.device, req.port)) {
/* TODO find the net dev matching the request parameters
* through the RoCE GID table */
*net_dev = NULL;
@@ -1668,6 +1626,7 @@ void rdma_destroy_id(struct rdma_cm_id *id)
mutex_unlock(&id_priv->handler_mutex);
if (id_priv->cma_dev) {
+ rdma_restrack_del(&id_priv->res);
if (rdma_cap_ib_cm(id_priv->id.device, 1)) {
if (id_priv->cm_id.ib)
ib_destroy_cm_id(id_priv->cm_id.ib);
@@ -1817,6 +1776,7 @@ static struct rdma_id_private *cma_new_conn_id(struct rdma_cm_id *listen_id,
struct ib_cm_event *ib_event,
struct net_device *net_dev)
{
+ struct rdma_id_private *listen_id_priv;
struct rdma_id_private *id_priv;
struct rdma_cm_id *id;
struct rdma_route *rt;
@@ -1826,9 +1786,11 @@ static struct rdma_id_private *cma_new_conn_id(struct rdma_cm_id *listen_id,
ib_event->param.req_rcvd.primary_path->service_id;
int ret;
- id = rdma_create_id(listen_id->route.addr.dev_addr.net,
+ listen_id_priv = container_of(listen_id, struct rdma_id_private, id);
+ id = __rdma_create_id(listen_id->route.addr.dev_addr.net,
listen_id->event_handler, listen_id->context,
- listen_id->ps, ib_event->param.req_rcvd.qp_type);
+ listen_id->ps, ib_event->param.req_rcvd.qp_type,
+ listen_id_priv->res.kern_name);
if (IS_ERR(id))
return NULL;
@@ -1877,14 +1839,17 @@ static struct rdma_id_private *cma_new_udp_id(struct rdma_cm_id *listen_id,
struct ib_cm_event *ib_event,
struct net_device *net_dev)
{
+ struct rdma_id_private *listen_id_priv;
struct rdma_id_private *id_priv;
struct rdma_cm_id *id;
const sa_family_t ss_family = listen_id->route.addr.src_addr.ss_family;
struct net *net = listen_id->route.addr.dev_addr.net;
int ret;
- id = rdma_create_id(net, listen_id->event_handler, listen_id->context,
- listen_id->ps, IB_QPT_UD);
+ listen_id_priv = container_of(listen_id, struct rdma_id_private, id);
+ id = __rdma_create_id(net, listen_id->event_handler, listen_id->context,
+ listen_id->ps, IB_QPT_UD,
+ listen_id_priv->res.kern_name);
if (IS_ERR(id))
return NULL;
@@ -2150,10 +2115,11 @@ static int iw_conn_req_handler(struct iw_cm_id *cm_id,
goto out;
/* Create a new RDMA id for the new IW CM ID */
- new_cm_id = rdma_create_id(listen_id->id.route.addr.dev_addr.net,
- listen_id->id.event_handler,
- listen_id->id.context,
- RDMA_PS_TCP, IB_QPT_RC);
+ new_cm_id = __rdma_create_id(listen_id->id.route.addr.dev_addr.net,
+ listen_id->id.event_handler,
+ listen_id->id.context,
+ RDMA_PS_TCP, IB_QPT_RC,
+ listen_id->res.kern_name);
if (IS_ERR(new_cm_id)) {
ret = -ENOMEM;
goto out;
@@ -2278,8 +2244,8 @@ static void cma_listen_on_dev(struct rdma_id_private *id_priv,
if (cma_family(id_priv) == AF_IB && !rdma_cap_ib_cm(cma_dev->device, 1))
return;
- id = rdma_create_id(net, cma_listen_handler, id_priv, id_priv->id.ps,
- id_priv->id.qp_type);
+ id = __rdma_create_id(net, cma_listen_handler, id_priv, id_priv->id.ps,
+ id_priv->id.qp_type, id_priv->res.kern_name);
if (IS_ERR(id))
return;
@@ -2541,6 +2507,7 @@ cma_iboe_set_path_rec_l2_fields(struct rdma_id_private *id_priv)
gid_type = ib_network_to_gid_type(addr->dev_addr.network);
route->path_rec->rec_type = sa_conv_gid_to_pathrec_type(gid_type);
+ route->path_rec->roce.route_resolved = true;
sa_path_set_ndev(route->path_rec, addr->dev_addr.net);
sa_path_set_ifindex(route->path_rec, ndev->ifindex);
sa_path_set_dmac(route->path_rec, addr->dev_addr.dst_dev_addr);
@@ -3028,7 +2995,7 @@ static void cma_bind_port(struct rdma_bind_list *bind_list,
hlist_add_head(&id_priv->node, &bind_list->owners);
}
-static int cma_alloc_port(enum rdma_port_space ps,
+static int cma_alloc_port(enum rdma_ucm_port_space ps,
struct rdma_id_private *id_priv, unsigned short snum)
{
struct rdma_bind_list *bind_list;
@@ -3091,7 +3058,7 @@ static int cma_port_is_unique(struct rdma_bind_list *bind_list,
return 0;
}
-static int cma_alloc_any_port(enum rdma_port_space ps,
+static int cma_alloc_any_port(enum rdma_ucm_port_space ps,
struct rdma_id_private *id_priv)
{
static unsigned int last_used_port;
@@ -3169,7 +3136,7 @@ static int cma_check_port(struct rdma_bind_list *bind_list,
return 0;
}
-static int cma_use_port(enum rdma_port_space ps,
+static int cma_use_port(enum rdma_ucm_port_space ps,
struct rdma_id_private *id_priv)
{
struct rdma_bind_list *bind_list;
@@ -3203,8 +3170,8 @@ static int cma_bind_listen(struct rdma_id_private *id_priv)
return ret;
}
-static enum rdma_port_space cma_select_inet_ps(
- struct rdma_id_private *id_priv)
+static enum rdma_ucm_port_space
+cma_select_inet_ps(struct rdma_id_private *id_priv)
{
switch (id_priv->id.ps) {
case RDMA_PS_TCP:
@@ -3218,9 +3185,10 @@ static enum rdma_port_space cma_select_inet_ps(
}
}
-static enum rdma_port_space cma_select_ib_ps(struct rdma_id_private *id_priv)
+static enum rdma_ucm_port_space
+cma_select_ib_ps(struct rdma_id_private *id_priv)
{
- enum rdma_port_space ps = 0;
+ enum rdma_ucm_port_space ps = 0;
struct sockaddr_ib *sib;
u64 sid_ps, mask, sid;
@@ -3251,7 +3219,7 @@ static enum rdma_port_space cma_select_ib_ps(struct rdma_id_private *id_priv)
static int cma_get_port(struct rdma_id_private *id_priv)
{
- enum rdma_port_space ps;
+ enum rdma_ucm_port_space ps;
int ret;
if (cma_family(id_priv) != AF_IB)
@@ -3389,8 +3357,10 @@ int rdma_bind_addr(struct rdma_cm_id *id, struct sockaddr *addr)
return 0;
err2:
- if (id_priv->cma_dev)
+ if (id_priv->cma_dev) {
+ rdma_restrack_del(&id_priv->res);
cma_release_dev(id_priv);
+ }
err1:
cma_comp_exch(id_priv, RDMA_CM_ADDR_BOUND, RDMA_CM_IDLE);
return ret;
@@ -3773,14 +3743,18 @@ static int cma_send_sidr_rep(struct rdma_id_private *id_priv,
return ib_send_cm_sidr_rep(id_priv->cm_id.ib, &rep);
}
-int rdma_accept(struct rdma_cm_id *id, struct rdma_conn_param *conn_param)
+int __rdma_accept(struct rdma_cm_id *id, struct rdma_conn_param *conn_param,
+ const char *caller)
{
struct rdma_id_private *id_priv;
int ret;
id_priv = container_of(id, struct rdma_id_private, id);
- id_priv->owner = task_pid_nr(current);
+ if (caller)
+ id_priv->res.kern_name = caller;
+ else
+ rdma_restrack_set_task(&id_priv->res, current);
if (!cma_comp(id_priv, RDMA_CM_CONNECT))
return -EINVAL;
@@ -3820,7 +3794,7 @@ reject:
rdma_reject(id, NULL, 0);
return ret;
}
-EXPORT_SYMBOL(rdma_accept);
+EXPORT_SYMBOL(__rdma_accept);
int rdma_notify(struct rdma_cm_id *id, enum ib_event_type event)
{
@@ -3938,10 +3912,14 @@ static int cma_ib_mc_handler(int status, struct ib_sa_multicast *multicast)
rdma_start_port(id_priv->cma_dev->device)];
event.event = RDMA_CM_EVENT_MULTICAST_JOIN;
- ib_init_ah_from_mcmember(id_priv->id.device,
- id_priv->id.port_num, &multicast->rec,
- ndev, gid_type,
- &event.param.ud.ah_attr);
+ ret = ib_init_ah_from_mcmember(id_priv->id.device,
+ id_priv->id.port_num,
+ &multicast->rec,
+ ndev, gid_type,
+ &event.param.ud.ah_attr);
+ if (ret)
+ event.event = RDMA_CM_EVENT_MULTICAST_ERROR;
+
event.param.ud.qp_num = 0xFFFFFF;
event.param.ud.qkey = be32_to_cpu(multicast->rec.qkey);
if (ndev)
@@ -4501,7 +4479,7 @@ static int cma_get_id_stats(struct sk_buff *skb, struct netlink_callback *cb)
RDMA_NL_RDMA_CM_ATTR_DST_ADDR))
goto out;
- id_stats->pid = id_priv->owner;
+ id_stats->pid = task_pid_vnr(id_priv->res.task);
id_stats->port_space = id->ps;
id_stats->cm_state = id_priv->state;
id_stats->qp_num = id_priv->qp_num;
diff --git a/drivers/infiniband/core/cma_priv.h b/drivers/infiniband/core/cma_priv.h
new file mode 100644
index 000000000000..194cfe78c447
--- /dev/null
+++ b/drivers/infiniband/core/cma_priv.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2005 Voltaire Inc. All rights reserved.
+ * Copyright (c) 2002-2005, Network Appliance, Inc. All rights reserved.
+ * Copyright (c) 1999-2005, Mellanox Technologies, Inc. All rights reserved.
+ * Copyright (c) 2005-2006 Intel Corporation. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 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
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ */
+
+#ifndef _CMA_PRIV_H
+#define _CMA_PRIV_H
+
+enum rdma_cm_state {
+ RDMA_CM_IDLE,
+ RDMA_CM_ADDR_QUERY,
+ RDMA_CM_ADDR_RESOLVED,
+ RDMA_CM_ROUTE_QUERY,
+ RDMA_CM_ROUTE_RESOLVED,
+ RDMA_CM_CONNECT,
+ RDMA_CM_DISCONNECT,
+ RDMA_CM_ADDR_BOUND,
+ RDMA_CM_LISTEN,
+ RDMA_CM_DEVICE_REMOVAL,
+ RDMA_CM_DESTROYING
+};
+
+struct rdma_id_private {
+ struct rdma_cm_id id;
+
+ struct rdma_bind_list *bind_list;
+ struct hlist_node node;
+ struct list_head list; /* listen_any_list or cma_device.list */
+ struct list_head listen_list; /* per device listens */
+ struct cma_device *cma_dev;
+ struct list_head mc_list;
+
+ int internal_id;
+ enum rdma_cm_state state;
+ spinlock_t lock;
+ struct mutex qp_mutex;
+
+ struct completion comp;
+ atomic_t refcount;
+ struct mutex handler_mutex;
+
+ int backlog;
+ int timeout_ms;
+ struct ib_sa_query *query;
+ int query_id;
+ union {
+ struct ib_cm_id *ib;
+ struct iw_cm_id *iw;
+ } cm_id;
+
+ u32 seq_num;
+ u32 qkey;
+ u32 qp_num;
+ u32 options;
+ u8 srq;
+ u8 tos;
+ bool tos_set;
+ u8 reuseaddr;
+ u8 afonly;
+ enum ib_gid_type gid_type;
+
+ /*
+ * Internal to RDMA/core, don't use in the drivers
+ */
+ struct rdma_restrack_entry res;
+};
+#endif /* _CMA_PRIV_H */
diff --git a/drivers/infiniband/core/core_priv.h b/drivers/infiniband/core/core_priv.h
index 25bb178f6074..54163a6e4067 100644
--- a/drivers/infiniband/core/core_priv.h
+++ b/drivers/infiniband/core/core_priv.h
@@ -333,4 +333,15 @@ static inline struct ib_qp *_ib_create_qp(struct ib_device *dev,
return qp;
}
+
+struct rdma_dev_addr;
+int rdma_resolve_ip_route(struct sockaddr *src_addr,
+ const struct sockaddr *dst_addr,
+ struct rdma_dev_addr *addr);
+
+int rdma_addr_find_l2_eth_by_grh(const union ib_gid *sgid,
+ const union ib_gid *dgid,
+ u8 *dmac, const struct net_device *ndev,
+ int *hoplimit);
+
#endif /* _CORE_PRIV_H */
diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c
index bb065c9449be..ea9fbcfb21bd 100644
--- a/drivers/infiniband/core/device.c
+++ b/drivers/infiniband/core/device.c
@@ -103,7 +103,6 @@ static int ib_device_check_mandatory(struct ib_device *device)
IB_MANDATORY_FUNC(query_device),
IB_MANDATORY_FUNC(query_port),
IB_MANDATORY_FUNC(query_pkey),
- IB_MANDATORY_FUNC(query_gid),
IB_MANDATORY_FUNC(alloc_pd),
IB_MANDATORY_FUNC(dealloc_pd),
IB_MANDATORY_FUNC(create_ah),
@@ -290,6 +289,7 @@ void ib_dealloc_device(struct ib_device *device)
{
WARN_ON(device->reg_state != IB_DEV_UNREGISTERED &&
device->reg_state != IB_DEV_UNINITIALIZED);
+ rdma_restrack_clean(&device->res);
put_device(&device->dev);
}
EXPORT_SYMBOL(ib_dealloc_device);
@@ -600,8 +600,6 @@ void ib_unregister_device(struct ib_device *device)
}
up_read(&lists_rwsem);
- rdma_restrack_clean(&device->res);
-
ib_device_unregister_rdmacg(device);
ib_device_unregister_sysfs(device);
@@ -854,7 +852,7 @@ int ib_query_port(struct ib_device *device,
if (rdma_port_get_link_layer(device, port_num) != IB_LINK_LAYER_INFINIBAND)
return 0;
- err = ib_query_gid(device, port_num, 0, &gid, NULL);
+ err = device->query_gid(device, port_num, 0, &gid);
if (err)
return err;
@@ -872,19 +870,13 @@ EXPORT_SYMBOL(ib_query_port);
* @attr: Returned GID attributes related to this GID index (only in RoCE).
* NULL means ignore.
*
- * ib_query_gid() fetches the specified GID table entry.
+ * ib_query_gid() fetches the specified GID table entry from the cache.
*/
int ib_query_gid(struct ib_device *device,
u8 port_num, int index, union ib_gid *gid,
struct ib_gid_attr *attr)
{
- if (rdma_cap_roce_gid_table(device, port_num))
- return ib_get_cached_gid(device, port_num, index, gid, attr);
-
- if (attr)
- return -EINVAL;
-
- return device->query_gid(device, port_num, index, gid);
+ return ib_get_cached_gid(device, port_num, index, gid, attr);
}
EXPORT_SYMBOL(ib_query_gid);
@@ -1050,19 +1042,18 @@ EXPORT_SYMBOL(ib_modify_port);
* a specified GID value occurs. Its searches only for IB link layer.
* @device: The device to query.
* @gid: The GID value to search for.
- * @ndev: The ndev related to the GID to search for.
* @port_num: The port number of the device where the GID value was found.
* @index: The index into the GID table where the GID was found. This
* parameter may be NULL.
*/
int ib_find_gid(struct ib_device *device, union ib_gid *gid,
- struct net_device *ndev, u8 *port_num, u16 *index)
+ u8 *port_num, u16 *index)
{
union ib_gid tmp_gid;
int ret, port, i;
for (port = rdma_start_port(device); port <= rdma_end_port(device); ++port) {
- if (rdma_cap_roce_gid_table(device, port))
+ if (!rdma_protocol_ib(device, port))
continue;
for (i = 0; i < device->port_immutable[port].gid_tbl_len; ++i) {
diff --git a/drivers/infiniband/core/iwpm_util.c b/drivers/infiniband/core/iwpm_util.c
index 81528f64061a..9821ae900f6d 100644
--- a/drivers/infiniband/core/iwpm_util.c
+++ b/drivers/infiniband/core/iwpm_util.c
@@ -439,10 +439,9 @@ struct sk_buff *iwpm_create_nlmsg(u32 nl_op, struct nlmsghdr **nlh,
struct sk_buff *skb = NULL;
skb = dev_alloc_skb(IWPM_MSG_SIZE);
- if (!skb) {
- pr_err("%s Unable to allocate skb\n", __func__);
+ if (!skb)
goto create_nlmsg_exit;
- }
+
if (!(ibnl_put_msg(skb, nlh, 0, 0, nl_client, nl_op,
NLM_F_REQUEST))) {
pr_warn("%s: Unable to put the nlmsg header\n", __func__);
diff --git a/drivers/infiniband/core/multicast.c b/drivers/infiniband/core/multicast.c
index 45f2f095f793..4eb72ff539fc 100644
--- a/drivers/infiniband/core/multicast.c
+++ b/drivers/infiniband/core/multicast.c
@@ -724,21 +724,19 @@ int ib_init_ah_from_mcmember(struct ib_device *device, u8 port_num,
{
int ret;
u16 gid_index;
- u8 p;
-
- if (rdma_protocol_roce(device, port_num)) {
- ret = ib_find_cached_gid_by_port(device, &rec->port_gid,
- gid_type, port_num,
- ndev,
- &gid_index);
- } else if (rdma_protocol_ib(device, port_num)) {
- ret = ib_find_cached_gid(device, &rec->port_gid,
- IB_GID_TYPE_IB, NULL, &p,
- &gid_index);
- } else {
- ret = -EINVAL;
- }
+ /* GID table is not based on the netdevice for IB link layer,
+ * so ignore ndev during search.
+ */
+ if (rdma_protocol_ib(device, port_num))
+ ndev = NULL;
+ else if (!rdma_protocol_roce(device, port_num))
+ return -EINVAL;
+
+ ret = ib_find_cached_gid_by_port(device, &rec->port_gid,
+ gid_type, port_num,
+ ndev,
+ &gid_index);
if (ret)
return ret;
diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c
index 5326a684555f..eb567765f45c 100644
--- a/drivers/infiniband/core/nldev.c
+++ b/drivers/infiniband/core/nldev.c
@@ -34,9 +34,11 @@
#include <linux/pid.h>
#include <linux/pid_namespace.h>
#include <net/netlink.h>
+#include <rdma/rdma_cm.h>
#include <rdma/rdma_netlink.h>
#include "core_priv.h"
+#include "cma_priv.h"
static const struct nla_policy nldev_policy[RDMA_NLDEV_ATTR_MAX] = {
[RDMA_NLDEV_ATTR_DEV_INDEX] = { .type = NLA_U32 },
@@ -71,6 +73,31 @@ static const struct nla_policy nldev_policy[RDMA_NLDEV_ATTR_MAX] = {
[RDMA_NLDEV_ATTR_RES_PID] = { .type = NLA_U32 },
[RDMA_NLDEV_ATTR_RES_KERN_NAME] = { .type = NLA_NUL_STRING,
.len = TASK_COMM_LEN },
+ [RDMA_NLDEV_ATTR_RES_CM_ID] = { .type = NLA_NESTED },
+ [RDMA_NLDEV_ATTR_RES_CM_ID_ENTRY] = { .type = NLA_NESTED },
+ [RDMA_NLDEV_ATTR_RES_PS] = { .type = NLA_U32 },
+ [RDMA_NLDEV_ATTR_RES_SRC_ADDR] = {
+ .len = sizeof(struct __kernel_sockaddr_storage) },
+ [RDMA_NLDEV_ATTR_RES_DST_ADDR] = {
+ .len = sizeof(struct __kernel_sockaddr_storage) },
+ [RDMA_NLDEV_ATTR_RES_CQ] = { .type = NLA_NESTED },
+ [RDMA_NLDEV_ATTR_RES_CQ_ENTRY] = { .type = NLA_NESTED },
+ [RDMA_NLDEV_ATTR_RES_CQE] = { .type = NLA_U32 },
+ [RDMA_NLDEV_ATTR_RES_USECNT] = { .type = NLA_U64 },
+ [RDMA_NLDEV_ATTR_RES_POLL_CTX] = { .type = NLA_U8 },
+ [RDMA_NLDEV_ATTR_RES_MR] = { .type = NLA_NESTED },
+ [RDMA_NLDEV_ATTR_RES_MR_ENTRY] = { .type = NLA_NESTED },
+ [RDMA_NLDEV_ATTR_RES_RKEY] = { .type = NLA_U32 },
+ [RDMA_NLDEV_ATTR_RES_LKEY] = { .type = NLA_U32 },
+ [RDMA_NLDEV_ATTR_RES_IOVA] = { .type = NLA_U64 },
+ [RDMA_NLDEV_ATTR_RES_MRLEN] = { .type = NLA_U64 },
+ [RDMA_NLDEV_ATTR_RES_PD] = { .type = NLA_NESTED },
+ [RDMA_NLDEV_ATTR_RES_PD_ENTRY] = { .type = NLA_NESTED },
+ [RDMA_NLDEV_ATTR_RES_LOCAL_DMA_LKEY] = { .type = NLA_U32 },
+ [RDMA_NLDEV_ATTR_RES_UNSAFE_GLOBAL_RKEY] = { .type = NLA_U32 },
+ [RDMA_NLDEV_ATTR_NDEV_INDEX] = { .type = NLA_U32 },
+ [RDMA_NLDEV_ATTR_NDEV_NAME] = { .type = NLA_NUL_STRING,
+ .len = IFNAMSIZ },
};
static int fill_nldev_handle(struct sk_buff *msg, struct ib_device *device)
@@ -99,7 +126,7 @@ static int fill_dev_info(struct sk_buff *msg, struct ib_device *device)
return -EMSGSIZE;
ib_get_device_fw_str(device, fw);
- /* Device without FW has strlen(fw) */
+ /* Device without FW has strlen(fw) = 0 */
if (strlen(fw) && nla_put_string(msg, RDMA_NLDEV_ATTR_FW_VERSION, fw))
return -EMSGSIZE;
@@ -115,8 +142,10 @@ static int fill_dev_info(struct sk_buff *msg, struct ib_device *device)
}
static int fill_port_info(struct sk_buff *msg,
- struct ib_device *device, u32 port)
+ struct ib_device *device, u32 port,
+ const struct net *net)
{
+ struct net_device *netdev = NULL;
struct ib_port_attr attr;
int ret;
@@ -150,7 +179,23 @@ static int fill_port_info(struct sk_buff *msg,
return -EMSGSIZE;
if (nla_put_u8(msg, RDMA_NLDEV_ATTR_PORT_PHYS_STATE, attr.phys_state))
return -EMSGSIZE;
- return 0;
+
+ if (device->get_netdev)
+ netdev = device->get_netdev(device, port);
+
+ if (netdev && net_eq(dev_net(netdev), net)) {
+ ret = nla_put_u32(msg,
+ RDMA_NLDEV_ATTR_NDEV_INDEX, netdev->ifindex);
+ if (ret)
+ goto out;
+ ret = nla_put_string(msg,
+ RDMA_NLDEV_ATTR_NDEV_NAME, netdev->name);
+ }
+
+out:
+ if (netdev)
+ dev_put(netdev);
+ return ret;
}
static int fill_res_info_entry(struct sk_buff *msg,
@@ -182,6 +227,8 @@ static int fill_res_info(struct sk_buff *msg, struct ib_device *device)
[RDMA_RESTRACK_PD] = "pd",
[RDMA_RESTRACK_CQ] = "cq",
[RDMA_RESTRACK_QP] = "qp",
+ [RDMA_RESTRACK_CM_ID] = "cm_id",
+ [RDMA_RESTRACK_MR] = "mr",
};
struct rdma_restrack_root *res = &device->res;
@@ -212,10 +259,29 @@ err:
return ret;
}
-static int fill_res_qp_entry(struct sk_buff *msg,
- struct ib_qp *qp, uint32_t port)
+static int fill_res_name_pid(struct sk_buff *msg,
+ struct rdma_restrack_entry *res)
+{
+ /*
+ * For user resources, user is should read /proc/PID/comm to get the
+ * name of the task file.
+ */
+ if (rdma_is_kernel_res(res)) {
+ if (nla_put_string(msg, RDMA_NLDEV_ATTR_RES_KERN_NAME,
+ res->kern_name))
+ return -EMSGSIZE;
+ } else {
+ if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_PID,
+ task_pid_vnr(res->task)))
+ return -EMSGSIZE;
+ }
+ return 0;
+}
+
+static int fill_res_qp_entry(struct sk_buff *msg, struct netlink_callback *cb,
+ struct rdma_restrack_entry *res, uint32_t port)
{
- struct rdma_restrack_entry *res = &qp->res;
+ struct ib_qp *qp = container_of(res, struct ib_qp, res);
struct ib_qp_init_attr qp_init_attr;
struct nlattr *entry_attr;
struct ib_qp_attr qp_attr;
@@ -262,19 +328,172 @@ static int fill_res_qp_entry(struct sk_buff *msg,
if (nla_put_u8(msg, RDMA_NLDEV_ATTR_RES_STATE, qp_attr.qp_state))
goto err;
- /*
- * Existence of task means that it is user QP and netlink
- * user is invited to go and read /proc/PID/comm to get name
- * of the task file and res->task_com should be NULL.
- */
- if (rdma_is_kernel_res(res)) {
- if (nla_put_string(msg, RDMA_NLDEV_ATTR_RES_KERN_NAME, res->kern_name))
+ if (fill_res_name_pid(msg, res))
+ goto err;
+
+ nla_nest_end(msg, entry_attr);
+ return 0;
+
+err:
+ nla_nest_cancel(msg, entry_attr);
+out:
+ return -EMSGSIZE;
+}
+
+static int fill_res_cm_id_entry(struct sk_buff *msg,
+ struct netlink_callback *cb,
+ struct rdma_restrack_entry *res, uint32_t port)
+{
+ struct rdma_id_private *id_priv =
+ container_of(res, struct rdma_id_private, res);
+ struct rdma_cm_id *cm_id = &id_priv->id;
+ struct nlattr *entry_attr;
+
+ if (port && port != cm_id->port_num)
+ return 0;
+
+ entry_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_RES_CM_ID_ENTRY);
+ if (!entry_attr)
+ goto out;
+
+ if (cm_id->port_num &&
+ nla_put_u32(msg, RDMA_NLDEV_ATTR_PORT_INDEX, cm_id->port_num))
+ goto err;
+
+ if (id_priv->qp_num) {
+ if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_LQPN, id_priv->qp_num))
goto err;
- } else {
- if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_PID, task_pid_vnr(res->task)))
+ if (nla_put_u8(msg, RDMA_NLDEV_ATTR_RES_TYPE, cm_id->qp_type))
goto err;
}
+ if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_PS, cm_id->ps))
+ goto err;
+
+ if (nla_put_u8(msg, RDMA_NLDEV_ATTR_RES_STATE, id_priv->state))
+ goto err;
+
+ if (cm_id->route.addr.src_addr.ss_family &&
+ nla_put(msg, RDMA_NLDEV_ATTR_RES_SRC_ADDR,
+ sizeof(cm_id->route.addr.src_addr),
+ &cm_id->route.addr.src_addr))
+ goto err;
+ if (cm_id->route.addr.dst_addr.ss_family &&
+ nla_put(msg, RDMA_NLDEV_ATTR_RES_DST_ADDR,
+ sizeof(cm_id->route.addr.dst_addr),
+ &cm_id->route.addr.dst_addr))
+ goto err;
+
+ if (fill_res_name_pid(msg, res))
+ goto err;
+
+ nla_nest_end(msg, entry_attr);
+ return 0;
+
+err:
+ nla_nest_cancel(msg, entry_attr);
+out:
+ return -EMSGSIZE;
+}
+
+static int fill_res_cq_entry(struct sk_buff *msg, struct netlink_callback *cb,
+ struct rdma_restrack_entry *res, uint32_t port)
+{
+ struct ib_cq *cq = container_of(res, struct ib_cq, res);
+ struct nlattr *entry_attr;
+
+ entry_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_RES_CQ_ENTRY);
+ if (!entry_attr)
+ goto out;
+
+ if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_CQE, cq->cqe))
+ goto err;
+ if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_RES_USECNT,
+ atomic_read(&cq->usecnt), 0))
+ goto err;
+
+ /* Poll context is only valid for kernel CQs */
+ if (rdma_is_kernel_res(res) &&
+ nla_put_u8(msg, RDMA_NLDEV_ATTR_RES_POLL_CTX, cq->poll_ctx))
+ goto err;
+
+ if (fill_res_name_pid(msg, res))
+ goto err;
+
+ nla_nest_end(msg, entry_attr);
+ return 0;
+
+err:
+ nla_nest_cancel(msg, entry_attr);
+out:
+ return -EMSGSIZE;
+}
+
+static int fill_res_mr_entry(struct sk_buff *msg, struct netlink_callback *cb,
+ struct rdma_restrack_entry *res, uint32_t port)
+{
+ struct ib_mr *mr = container_of(res, struct ib_mr, res);
+ struct nlattr *entry_attr;
+
+ entry_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_RES_MR_ENTRY);
+ if (!entry_attr)
+ goto out;
+
+ if (netlink_capable(cb->skb, CAP_NET_ADMIN)) {
+ if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_RKEY, mr->rkey))
+ goto err;
+ if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_LKEY, mr->lkey))
+ goto err;
+ if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_RES_IOVA,
+ mr->iova, 0))
+ goto err;
+ }
+
+ if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_RES_MRLEN, mr->length, 0))
+ goto err;
+
+ if (fill_res_name_pid(msg, res))
+ goto err;
+
+ nla_nest_end(msg, entry_attr);
+ return 0;
+
+err:
+ nla_nest_cancel(msg, entry_attr);
+out:
+ return -EMSGSIZE;
+}
+
+static int fill_res_pd_entry(struct sk_buff *msg, struct netlink_callback *cb,
+ struct rdma_restrack_entry *res, uint32_t port)
+{
+ struct ib_pd *pd = container_of(res, struct ib_pd, res);
+ struct nlattr *entry_attr;
+
+ entry_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_RES_PD_ENTRY);
+ if (!entry_attr)
+ goto out;
+
+ if (netlink_capable(cb->skb, CAP_NET_ADMIN)) {
+ if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_LOCAL_DMA_LKEY,
+ pd->local_dma_lkey))
+ goto err;
+ if ((pd->flags & IB_PD_UNSAFE_GLOBAL_RKEY) &&
+ nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_UNSAFE_GLOBAL_RKEY,
+ pd->unsafe_global_rkey))
+ goto err;
+ }
+ if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_RES_USECNT,
+ atomic_read(&pd->usecnt), 0))
+ goto err;
+ if ((pd->flags & IB_PD_UNSAFE_GLOBAL_RKEY) &&
+ nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_UNSAFE_GLOBAL_RKEY,
+ pd->unsafe_global_rkey))
+ goto err;
+
+ if (fill_res_name_pid(msg, res))
+ goto err;
+
nla_nest_end(msg, entry_attr);
return 0;
@@ -405,7 +624,7 @@ static int nldev_port_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_GET),
0, 0);
- err = fill_port_info(msg, device, port);
+ err = fill_port_info(msg, device, port, sock_net(skb->sk));
if (err)
goto err_free;
@@ -465,7 +684,7 @@ static int nldev_port_get_dumpit(struct sk_buff *skb,
RDMA_NLDEV_CMD_PORT_GET),
0, NLM_F_MULTI);
- if (fill_port_info(skb, device, p)) {
+ if (fill_port_info(skb, device, p, sock_net(skb->sk))) {
nlmsg_cancel(skb, nlh);
goto out;
}
@@ -558,23 +777,60 @@ static int nldev_res_get_dumpit(struct sk_buff *skb,
return ib_enum_all_devs(_nldev_res_get_dumpit, skb, cb);
}
-static int nldev_res_get_qp_dumpit(struct sk_buff *skb,
- struct netlink_callback *cb)
+struct nldev_fill_res_entry {
+ int (*fill_res_func)(struct sk_buff *msg, struct netlink_callback *cb,
+ struct rdma_restrack_entry *res, u32 port);
+ enum rdma_nldev_attr nldev_attr;
+ enum rdma_nldev_command nldev_cmd;
+};
+
+static const struct nldev_fill_res_entry fill_entries[RDMA_RESTRACK_MAX] = {
+ [RDMA_RESTRACK_QP] = {
+ .fill_res_func = fill_res_qp_entry,
+ .nldev_cmd = RDMA_NLDEV_CMD_RES_QP_GET,
+ .nldev_attr = RDMA_NLDEV_ATTR_RES_QP,
+ },
+ [RDMA_RESTRACK_CM_ID] = {
+ .fill_res_func = fill_res_cm_id_entry,
+ .nldev_cmd = RDMA_NLDEV_CMD_RES_CM_ID_GET,
+ .nldev_attr = RDMA_NLDEV_ATTR_RES_CM_ID,
+ },
+ [RDMA_RESTRACK_CQ] = {
+ .fill_res_func = fill_res_cq_entry,
+ .nldev_cmd = RDMA_NLDEV_CMD_RES_CQ_GET,
+ .nldev_attr = RDMA_NLDEV_ATTR_RES_CQ,
+ },
+ [RDMA_RESTRACK_MR] = {
+ .fill_res_func = fill_res_mr_entry,
+ .nldev_cmd = RDMA_NLDEV_CMD_RES_MR_GET,
+ .nldev_attr = RDMA_NLDEV_ATTR_RES_MR,
+ },
+ [RDMA_RESTRACK_PD] = {
+ .fill_res_func = fill_res_pd_entry,
+ .nldev_cmd = RDMA_NLDEV_CMD_RES_PD_GET,
+ .nldev_attr = RDMA_NLDEV_ATTR_RES_PD,
+ },
+};
+
+static int res_get_common_dumpit(struct sk_buff *skb,
+ struct netlink_callback *cb,
+ enum rdma_restrack_type res_type)
{
+ const struct nldev_fill_res_entry *fe = &fill_entries[res_type];
struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
struct rdma_restrack_entry *res;
int err, ret = 0, idx = 0;
struct nlattr *table_attr;
struct ib_device *device;
int start = cb->args[0];
- struct ib_qp *qp = NULL;
struct nlmsghdr *nlh;
u32 index, port = 0;
+ bool filled = false;
err = nlmsg_parse(cb->nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
nldev_policy, NULL);
/*
- * Right now, we are expecting the device index to get QP information,
+ * Right now, we are expecting the device index to get res information,
* but it is possible to extend this code to return all devices in
* one shot by checking the existence of RDMA_NLDEV_ATTR_DEV_INDEX.
* if it doesn't exist, we will iterate over all devices.
@@ -601,7 +857,7 @@ static int nldev_res_get_qp_dumpit(struct sk_buff *skb,
}
nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
- RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_RES_QP_GET),
+ RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, fe->nldev_cmd),
0, NLM_F_MULTI);
if (fill_nldev_handle(skb, device)) {
@@ -609,24 +865,26 @@ static int nldev_res_get_qp_dumpit(struct sk_buff *skb,
goto err;
}
- table_attr = nla_nest_start(skb, RDMA_NLDEV_ATTR_RES_QP);
+ table_attr = nla_nest_start(skb, fe->nldev_attr);
if (!table_attr) {
ret = -EMSGSIZE;
goto err;
}
down_read(&device->res.rwsem);
- hash_for_each_possible(device->res.hash, res, node, RDMA_RESTRACK_QP) {
+ hash_for_each_possible(device->res.hash, res, node, res_type) {
if (idx < start)
goto next;
if ((rdma_is_kernel_res(res) &&
task_active_pid_ns(current) != &init_pid_ns) ||
- (!rdma_is_kernel_res(res) &&
- task_active_pid_ns(current) != task_active_pid_ns(res->task)))
+ (!rdma_is_kernel_res(res) && task_active_pid_ns(current) !=
+ task_active_pid_ns(res->task)))
/*
- * 1. Kernel QPs should be visible in init namspace only
- * 2. Present only QPs visible in the current namespace
+ * 1. Kern resources should be visible in init
+ * namspace only
+ * 2. Present only resources visible in the current
+ * namespace
*/
goto next;
@@ -638,10 +896,10 @@ static int nldev_res_get_qp_dumpit(struct sk_buff *skb,
*/
goto next;
- qp = container_of(res, struct ib_qp, res);
+ filled = true;
up_read(&device->res.rwsem);
- ret = fill_res_qp_entry(skb, qp, port);
+ ret = fe->fill_res_func(skb, cb, res, port);
down_read(&device->res.rwsem);
/*
* Return resource back, but it won't be released till
@@ -667,10 +925,10 @@ next: idx++;
cb->args[0] = idx;
/*
- * No more QPs to fill, cancel the message and
+ * No more entries to fill, cancel the message and
* return 0 to mark end of dumpit.
*/
- if (!qp)
+ if (!filled)
goto err;
put_device(&device->dev);
@@ -688,6 +946,36 @@ err_index:
return ret;
}
+static int nldev_res_get_qp_dumpit(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ return res_get_common_dumpit(skb, cb, RDMA_RESTRACK_QP);
+}
+
+static int nldev_res_get_cm_id_dumpit(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ return res_get_common_dumpit(skb, cb, RDMA_RESTRACK_CM_ID);
+}
+
+static int nldev_res_get_cq_dumpit(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ return res_get_common_dumpit(skb, cb, RDMA_RESTRACK_CQ);
+}
+
+static int nldev_res_get_mr_dumpit(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ return res_get_common_dumpit(skb, cb, RDMA_RESTRACK_MR);
+}
+
+static int nldev_res_get_pd_dumpit(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ return res_get_common_dumpit(skb, cb, RDMA_RESTRACK_PD);
+}
+
static const struct rdma_nl_cbs nldev_cb_table[RDMA_NLDEV_NUM_OPS] = {
[RDMA_NLDEV_CMD_GET] = {
.doit = nldev_get_doit,
@@ -714,6 +1002,18 @@ static const struct rdma_nl_cbs nldev_cb_table[RDMA_NLDEV_NUM_OPS] = {
* too.
*/
},
+ [RDMA_NLDEV_CMD_RES_CM_ID_GET] = {
+ .dump = nldev_res_get_cm_id_dumpit,
+ },
+ [RDMA_NLDEV_CMD_RES_CQ_GET] = {
+ .dump = nldev_res_get_cq_dumpit,
+ },
+ [RDMA_NLDEV_CMD_RES_MR_GET] = {
+ .dump = nldev_res_get_mr_dumpit,
+ },
+ [RDMA_NLDEV_CMD_RES_PD_GET] = {
+ .dump = nldev_res_get_pd_dumpit,
+ },
};
void __init nldev_init(void)
diff --git a/drivers/infiniband/core/rdma_core.c b/drivers/infiniband/core/rdma_core.c
index d8eead5d106d..a6e904973ba8 100644
--- a/drivers/infiniband/core/rdma_core.c
+++ b/drivers/infiniband/core/rdma_core.c
@@ -350,13 +350,6 @@ struct ib_uobject *rdma_alloc_begin_uobject(const struct uverbs_obj_type *type,
return type->type_class->alloc_begin(type, ucontext);
}
-static void uverbs_uobject_add(struct ib_uobject *uobject)
-{
- mutex_lock(&uobject->context->uobjects_lock);
- list_add(&uobject->list, &uobject->context->uobjects);
- mutex_unlock(&uobject->context->uobjects_lock);
-}
-
static int __must_check remove_commit_idr_uobject(struct ib_uobject *uobj,
enum rdma_remove_reason why)
{
@@ -502,7 +495,6 @@ out:
static void alloc_commit_idr_uobject(struct ib_uobject *uobj)
{
- uverbs_uobject_add(uobj);
spin_lock(&uobj->context->ufile->idr_lock);
/*
* We already allocated this IDR with a NULL object, so
@@ -518,7 +510,6 @@ static void alloc_commit_fd_uobject(struct ib_uobject *uobj)
struct ib_uobject_file *uobj_file =
container_of(uobj, struct ib_uobject_file, uobj);
- uverbs_uobject_add(&uobj_file->uobj);
fd_install(uobj_file->uobj.id, uobj->object);
/* This shouldn't be used anymore. Use the file object instead */
uobj_file->uobj.id = 0;
@@ -545,6 +536,10 @@ int rdma_alloc_commit_uobject(struct ib_uobject *uobj)
assert_uverbs_usecnt(uobj, true);
atomic_set(&uobj->usecnt, 0);
+ mutex_lock(&uobj->context->uobjects_lock);
+ list_add(&uobj->list, &uobj->context->uobjects);
+ mutex_unlock(&uobj->context->uobjects_lock);
+
uobj->type->type_class->alloc_commit(uobj);
up_read(&uobj->context->cleanup_rwsem);
diff --git a/drivers/infiniband/core/restrack.c b/drivers/infiniband/core/restrack.c
index 3dbc4e4cca41..efddd13e3edb 100644
--- a/drivers/infiniband/core/restrack.c
+++ b/drivers/infiniband/core/restrack.c
@@ -3,20 +3,66 @@
* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved.
*/
+#include <rdma/rdma_cm.h>
#include <rdma/ib_verbs.h>
#include <rdma/restrack.h>
#include <linux/mutex.h>
#include <linux/sched/task.h>
#include <linux/pid_namespace.h>
+#include "cma_priv.h"
+
void rdma_restrack_init(struct rdma_restrack_root *res)
{
init_rwsem(&res->rwsem);
}
+static const char *type2str(enum rdma_restrack_type type)
+{
+ static const char * const names[RDMA_RESTRACK_MAX] = {
+ [RDMA_RESTRACK_PD] = "PD",
+ [RDMA_RESTRACK_CQ] = "CQ",
+ [RDMA_RESTRACK_QP] = "QP",
+ [RDMA_RESTRACK_CM_ID] = "CM_ID",
+ [RDMA_RESTRACK_MR] = "MR",
+ };
+
+ return names[type];
+};
+
void rdma_restrack_clean(struct rdma_restrack_root *res)
{
- WARN_ON_ONCE(!hash_empty(res->hash));
+ struct rdma_restrack_entry *e;
+ char buf[TASK_COMM_LEN];
+ struct ib_device *dev;
+ const char *owner;
+ int bkt;
+
+ if (hash_empty(res->hash))
+ return;
+
+ dev = container_of(res, struct ib_device, res);
+ pr_err("restrack: %s", CUT_HERE);
+ pr_err("restrack: BUG: RESTRACK detected leak of resources on %s\n",
+ dev->name);
+ hash_for_each(res->hash, bkt, e, node) {
+ if (rdma_is_kernel_res(e)) {
+ owner = e->kern_name;
+ } else {
+ /*
+ * There is no need to call get_task_struct here,
+ * because we can be here only if there are more
+ * get_task_struct() call than put_task_struct().
+ */
+ get_task_comm(buf, e->task);
+ owner = buf;
+ }
+
+ pr_err("restrack: %s %s object allocated by %s is not freed\n",
+ rdma_is_kernel_res(e) ? "Kernel" : "User",
+ type2str(e->type), owner);
+ }
+ pr_err("restrack: %s", CUT_HERE);
}
int rdma_restrack_count(struct rdma_restrack_root *res,
@@ -40,51 +86,48 @@ EXPORT_SYMBOL(rdma_restrack_count);
static void set_kern_name(struct rdma_restrack_entry *res)
{
- enum rdma_restrack_type type = res->type;
- struct ib_qp *qp;
-
- if (type != RDMA_RESTRACK_QP)
- /* PD and CQ types already have this name embedded in */
- return;
+ struct ib_pd *pd;
- qp = container_of(res, struct ib_qp, res);
- if (!qp->pd) {
- WARN_ONCE(true, "XRC QPs are not supported\n");
- /* Survive, despite the programmer's error */
- res->kern_name = " ";
- return;
+ switch (res->type) {
+ case RDMA_RESTRACK_QP:
+ pd = container_of(res, struct ib_qp, res)->pd;
+ if (!pd) {
+ WARN_ONCE(true, "XRC QPs are not supported\n");
+ /* Survive, despite the programmer's error */
+ res->kern_name = " ";
+ }
+ break;
+ case RDMA_RESTRACK_MR:
+ pd = container_of(res, struct ib_mr, res)->pd;
+ break;
+ default:
+ /* Other types set kern_name directly */
+ pd = NULL;
+ break;
}
- res->kern_name = qp->pd->res.kern_name;
+ if (pd)
+ res->kern_name = pd->res.kern_name;
}
static struct ib_device *res_to_dev(struct rdma_restrack_entry *res)
{
- enum rdma_restrack_type type = res->type;
- struct ib_device *dev;
- struct ib_pd *pd;
- struct ib_cq *cq;
- struct ib_qp *qp;
-
- switch (type) {
+ switch (res->type) {
case RDMA_RESTRACK_PD:
- pd = container_of(res, struct ib_pd, res);
- dev = pd->device;
- break;
+ return container_of(res, struct ib_pd, res)->device;
case RDMA_RESTRACK_CQ:
- cq = container_of(res, struct ib_cq, res);
- dev = cq->device;
- break;
+ return container_of(res, struct ib_cq, res)->device;
case RDMA_RESTRACK_QP:
- qp = container_of(res, struct ib_qp, res);
- dev = qp->device;
- break;
+ return container_of(res, struct ib_qp, res)->device;
+ case RDMA_RESTRACK_CM_ID:
+ return container_of(res, struct rdma_id_private,
+ res)->id.device;
+ case RDMA_RESTRACK_MR:
+ return container_of(res, struct ib_mr, res)->device;
default:
- WARN_ONCE(true, "Wrong resource tracking type %u\n", type);
+ WARN_ONCE(true, "Wrong resource tracking type %u\n", res->type);
return NULL;
}
-
- return dev;
}
static bool res_is_user(struct rdma_restrack_entry *res)
@@ -96,6 +139,10 @@ static bool res_is_user(struct rdma_restrack_entry *res)
return container_of(res, struct ib_cq, res)->uobject;
case RDMA_RESTRACK_QP:
return container_of(res, struct ib_qp, res)->uobject;
+ case RDMA_RESTRACK_CM_ID:
+ return !res->kern_name;
+ case RDMA_RESTRACK_MR:
+ return container_of(res, struct ib_mr, res)->pd->uobject;
default:
WARN_ONCE(true, "Wrong resource tracking type %u\n", res->type);
return false;
@@ -109,13 +156,15 @@ void rdma_restrack_add(struct rdma_restrack_entry *res)
if (!dev)
return;
+ if (res->type != RDMA_RESTRACK_CM_ID || !res_is_user(res))
+ res->task = NULL;
+
if (res_is_user(res)) {
- get_task_struct(current);
- res->task = current;
+ if (!res->task)
+ rdma_restrack_set_task(res, current);
res->kern_name = NULL;
} else {
set_kern_name(res);
- res->task = NULL;
}
kref_init(&res->kref);
diff --git a/drivers/infiniband/core/roce_gid_mgmt.c b/drivers/infiniband/core/roce_gid_mgmt.c
index 5a52ec77940a..cc2966380c0c 100644
--- a/drivers/infiniband/core/roce_gid_mgmt.c
+++ b/drivers/infiniband/core/roce_gid_mgmt.c
@@ -403,10 +403,12 @@ static void enum_all_gids_of_dev_cb(struct ib_device *ib_dev,
* our feet
*/
rtnl_lock();
+ down_read(&net_rwsem);
for_each_net(net)
for_each_netdev(net, ndev)
if (is_eth_port_of_netdev(ib_dev, port, rdma_ndev, ndev))
add_netdev_ips(ib_dev, port, rdma_ndev, ndev);
+ up_read(&net_rwsem);
rtnl_unlock();
}
diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c
index 9f029a1ca5ea..a61ec7e33613 100644
--- a/drivers/infiniband/core/sa_query.c
+++ b/drivers/infiniband/core/sa_query.c
@@ -1227,118 +1227,130 @@ static u8 get_src_path_mask(struct ib_device *device, u8 port_num)
return src_path_mask;
}
-int ib_init_ah_attr_from_path(struct ib_device *device, u8 port_num,
- struct sa_path_rec *rec,
- struct rdma_ah_attr *ah_attr)
+static int
+roce_resolve_route_from_path(struct ib_device *device, u8 port_num,
+ struct sa_path_rec *rec)
{
+ struct net_device *resolved_dev;
+ struct net_device *ndev;
+ struct net_device *idev;
+ struct rdma_dev_addr dev_addr = {
+ .bound_dev_if = ((sa_path_get_ifindex(rec) >= 0) ?
+ sa_path_get_ifindex(rec) : 0),
+ .net = sa_path_get_ndev(rec) ?
+ sa_path_get_ndev(rec) :
+ &init_net
+ };
+ union {
+ struct sockaddr _sockaddr;
+ struct sockaddr_in _sockaddr_in;
+ struct sockaddr_in6 _sockaddr_in6;
+ } sgid_addr, dgid_addr;
int ret;
- u16 gid_index;
- int use_roce;
- struct net_device *ndev = NULL;
- memset(ah_attr, 0, sizeof *ah_attr);
- ah_attr->type = rdma_ah_find_type(device, port_num);
+ if (rec->roce.route_resolved)
+ return 0;
- rdma_ah_set_dlid(ah_attr, be32_to_cpu(sa_path_get_dlid(rec)));
+ if (!device->get_netdev)
+ return -EOPNOTSUPP;
- if ((ah_attr->type == RDMA_AH_ATTR_TYPE_OPA) &&
- (rdma_ah_get_dlid(ah_attr) == be16_to_cpu(IB_LID_PERMISSIVE)))
- rdma_ah_set_make_grd(ah_attr, true);
+ rdma_gid2ip(&sgid_addr._sockaddr, &rec->sgid);
+ rdma_gid2ip(&dgid_addr._sockaddr, &rec->dgid);
- rdma_ah_set_sl(ah_attr, rec->sl);
- rdma_ah_set_path_bits(ah_attr, be32_to_cpu(sa_path_get_slid(rec)) &
- get_src_path_mask(device, port_num));
- rdma_ah_set_port_num(ah_attr, port_num);
- rdma_ah_set_static_rate(ah_attr, rec->rate);
- use_roce = rdma_cap_eth_ah(device, port_num);
-
- if (use_roce) {
- struct net_device *idev;
- struct net_device *resolved_dev;
- struct rdma_dev_addr dev_addr = {
- .bound_dev_if = ((sa_path_get_ifindex(rec) >= 0) ?
- sa_path_get_ifindex(rec) : 0),
- .net = sa_path_get_ndev(rec) ?
- sa_path_get_ndev(rec) :
- &init_net
- };
- union {
- struct sockaddr _sockaddr;
- struct sockaddr_in _sockaddr_in;
- struct sockaddr_in6 _sockaddr_in6;
- } sgid_addr, dgid_addr;
-
- if (!device->get_netdev)
- return -EOPNOTSUPP;
-
- rdma_gid2ip(&sgid_addr._sockaddr, &rec->sgid);
- rdma_gid2ip(&dgid_addr._sockaddr, &rec->dgid);
-
- /* validate the route */
- ret = rdma_resolve_ip_route(&sgid_addr._sockaddr,
- &dgid_addr._sockaddr, &dev_addr);
- if (ret)
- return ret;
+ /* validate the route */
+ ret = rdma_resolve_ip_route(&sgid_addr._sockaddr,
+ &dgid_addr._sockaddr, &dev_addr);
+ if (ret)
+ return ret;
- if ((dev_addr.network == RDMA_NETWORK_IPV4 ||
- dev_addr.network == RDMA_NETWORK_IPV6) &&
- rec->rec_type != SA_PATH_REC_TYPE_ROCE_V2)
- return -EINVAL;
+ if ((dev_addr.network == RDMA_NETWORK_IPV4 ||
+ dev_addr.network == RDMA_NETWORK_IPV6) &&
+ rec->rec_type != SA_PATH_REC_TYPE_ROCE_V2)
+ return -EINVAL;
- idev = device->get_netdev(device, port_num);
- if (!idev)
- return -ENODEV;
+ idev = device->get_netdev(device, port_num);
+ if (!idev)
+ return -ENODEV;
- resolved_dev = dev_get_by_index(dev_addr.net,
- dev_addr.bound_dev_if);
- if (!resolved_dev) {
- dev_put(idev);
- return -ENODEV;
- }
- ndev = ib_get_ndev_from_path(rec);
- rcu_read_lock();
- if ((ndev && ndev != resolved_dev) ||
- (resolved_dev != idev &&
- !rdma_is_upper_dev_rcu(idev, resolved_dev)))
- ret = -EHOSTUNREACH;
- rcu_read_unlock();
- dev_put(idev);
- dev_put(resolved_dev);
- if (ret) {
- if (ndev)
- dev_put(ndev);
- return ret;
- }
+ resolved_dev = dev_get_by_index(dev_addr.net,
+ dev_addr.bound_dev_if);
+ if (!resolved_dev) {
+ ret = -ENODEV;
+ goto done;
}
+ ndev = ib_get_ndev_from_path(rec);
+ rcu_read_lock();
+ if ((ndev && ndev != resolved_dev) ||
+ (resolved_dev != idev &&
+ !rdma_is_upper_dev_rcu(idev, resolved_dev)))
+ ret = -EHOSTUNREACH;
+ rcu_read_unlock();
+ dev_put(resolved_dev);
+ if (ndev)
+ dev_put(ndev);
+done:
+ dev_put(idev);
+ if (!ret)
+ rec->roce.route_resolved = true;
+ return ret;
+}
- if (rec->hop_limit > 0 || use_roce) {
- enum ib_gid_type type = sa_conv_pathrec_to_gid_type(rec);
+static int init_ah_attr_grh_fields(struct ib_device *device, u8 port_num,
+ struct sa_path_rec *rec,
+ struct rdma_ah_attr *ah_attr)
+{
+ enum ib_gid_type type = sa_conv_pathrec_to_gid_type(rec);
+ struct net_device *ndev;
+ u16 gid_index;
+ int ret;
- ret = ib_find_cached_gid_by_port(device, &rec->sgid, type,
- port_num, ndev, &gid_index);
- if (ret) {
- if (ndev)
- dev_put(ndev);
- return ret;
- }
+ ndev = ib_get_ndev_from_path(rec);
+ ret = ib_find_cached_gid_by_port(device, &rec->sgid, type,
+ port_num, ndev, &gid_index);
+ if (ndev)
+ dev_put(ndev);
+ if (ret)
+ return ret;
- rdma_ah_set_grh(ah_attr, &rec->dgid,
- be32_to_cpu(rec->flow_label),
- gid_index, rec->hop_limit,
- rec->traffic_class);
- if (ndev)
- dev_put(ndev);
- }
+ rdma_ah_set_grh(ah_attr, &rec->dgid,
+ be32_to_cpu(rec->flow_label),
+ gid_index, rec->hop_limit,
+ rec->traffic_class);
+ return 0;
+}
- if (use_roce) {
- u8 *dmac = sa_path_get_dmac(rec);
+int ib_init_ah_attr_from_path(struct ib_device *device, u8 port_num,
+ struct sa_path_rec *rec,
+ struct rdma_ah_attr *ah_attr)
+{
+ int ret = 0;
+
+ memset(ah_attr, 0, sizeof(*ah_attr));
+ ah_attr->type = rdma_ah_find_type(device, port_num);
+ rdma_ah_set_sl(ah_attr, rec->sl);
+ rdma_ah_set_port_num(ah_attr, port_num);
+ rdma_ah_set_static_rate(ah_attr, rec->rate);
- if (!dmac)
- return -EINVAL;
- memcpy(ah_attr->roce.dmac, dmac, ETH_ALEN);
+ if (sa_path_is_roce(rec)) {
+ ret = roce_resolve_route_from_path(device, port_num, rec);
+ if (ret)
+ return ret;
+
+ memcpy(ah_attr->roce.dmac, sa_path_get_dmac(rec), ETH_ALEN);
+ } else {
+ rdma_ah_set_dlid(ah_attr, be32_to_cpu(sa_path_get_dlid(rec)));
+ if (sa_path_is_opa(rec) &&
+ rdma_ah_get_dlid(ah_attr) == be16_to_cpu(IB_LID_PERMISSIVE))
+ rdma_ah_set_make_grd(ah_attr, true);
+
+ rdma_ah_set_path_bits(ah_attr,
+ be32_to_cpu(sa_path_get_slid(rec)) &
+ get_src_path_mask(device, port_num));
}
- return 0;
+ if (rec->hop_limit > 0 || sa_path_is_roce(rec))
+ ret = init_ah_attr_grh_fields(device, port_num, rec, ah_attr);
+ return ret;
}
EXPORT_SYMBOL(ib_init_ah_attr_from_path);
diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c
index 8ae1308eecc7..31c7efaf8e7a 100644
--- a/drivers/infiniband/core/sysfs.c
+++ b/drivers/infiniband/core/sysfs.c
@@ -273,6 +273,7 @@ static ssize_t rate_show(struct ib_port *p, struct port_attribute *unused,
break;
case IB_SPEED_SDR:
default: /* default to SDR for invalid rates */
+ speed = " SDR";
rate = 25;
break;
}
@@ -388,14 +389,26 @@ static ssize_t show_port_gid(struct ib_port *p, struct port_attribute *attr,
{
struct port_table_attribute *tab_attr =
container_of(attr, struct port_table_attribute, attr);
+ union ib_gid *pgid;
union ib_gid gid;
ssize_t ret;
ret = ib_query_gid(p->ibdev, p->port_num, tab_attr->index, &gid, NULL);
- if (ret)
- return ret;
- return sprintf(buf, "%pI6\n", gid.raw);
+ /* If reading GID fails, it is likely due to GID entry being empty
+ * (invalid) or reserved GID in the table.
+ * User space expects to read GID table entries as long as it given
+ * index is within GID table size.
+ * Administrative/debugging tool fails to query rest of the GID entries
+ * if it hits error while querying a GID of the given index.
+ * To avoid user space throwing such error on fail to read gid, return
+ * zero GID as before. This maintains backward compatibility.
+ */
+ if (ret)
+ pgid = &zgid;
+ else
+ pgid = &gid;
+ return sprintf(buf, "%pI6\n", pgid->raw);
}
static ssize_t show_port_gid_attr_ndev(struct ib_port *p,
@@ -810,10 +823,15 @@ static ssize_t show_hw_stats(struct kobject *kobj, struct attribute *attr,
dev = port->ibdev;
stats = port->hw_stats;
}
+ mutex_lock(&stats->lock);
ret = update_hw_stats(dev, stats, hsa->port_num, hsa->index);
if (ret)
- return ret;
- return print_hw_stat(stats, hsa->index, buf);
+ goto unlock;
+ ret = print_hw_stat(stats, hsa->index, buf);
+unlock:
+ mutex_unlock(&stats->lock);
+
+ return ret;
}
static ssize_t show_stats_lifespan(struct kobject *kobj,
@@ -821,17 +839,25 @@ static ssize_t show_stats_lifespan(struct kobject *kobj,
char *buf)
{
struct hw_stats_attribute *hsa;
+ struct rdma_hw_stats *stats;
int msecs;
hsa = container_of(attr, struct hw_stats_attribute, attr);
if (!hsa->port_num) {
struct ib_device *dev = container_of((struct device *)kobj,
struct ib_device, dev);
- msecs = jiffies_to_msecs(dev->hw_stats->lifespan);
+
+ stats = dev->hw_stats;
} else {
struct ib_port *p = container_of(kobj, struct ib_port, kobj);
- msecs = jiffies_to_msecs(p->hw_stats->lifespan);
+
+ stats = p->hw_stats;
}
+
+ mutex_lock(&stats->lock);
+ msecs = jiffies_to_msecs(stats->lifespan);
+ mutex_unlock(&stats->lock);
+
return sprintf(buf, "%d\n", msecs);
}
@@ -840,6 +866,7 @@ static ssize_t set_stats_lifespan(struct kobject *kobj,
const char *buf, size_t count)
{
struct hw_stats_attribute *hsa;
+ struct rdma_hw_stats *stats;
int msecs;
int jiffies;
int ret;
@@ -854,11 +881,18 @@ static ssize_t set_stats_lifespan(struct kobject *kobj,
if (!hsa->port_num) {
struct ib_device *dev = container_of((struct device *)kobj,
struct ib_device, dev);
- dev->hw_stats->lifespan = jiffies;
+
+ stats = dev->hw_stats;
} else {
struct ib_port *p = container_of(kobj, struct ib_port, kobj);
- p->hw_stats->lifespan = jiffies;
+
+ stats = p->hw_stats;
}
+
+ mutex_lock(&stats->lock);
+ stats->lifespan = jiffies;
+ mutex_unlock(&stats->lock);
+
return count;
}
@@ -951,6 +985,7 @@ static void setup_hw_stats(struct ib_device *device, struct ib_port *port,
sysfs_attr_init(hsag->attrs[i]);
}
+ mutex_init(&stats->lock);
/* treat an error here as non-fatal */
hsag->attrs[i] = alloc_hsa_lifespan("lifespan", port_num);
if (hsag->attrs[i])
diff --git a/drivers/infiniband/core/ucm.c b/drivers/infiniband/core/ucm.c
index 01702265c1e1..9eef96dacbd7 100644
--- a/drivers/infiniband/core/ucm.c
+++ b/drivers/infiniband/core/ucm.c
@@ -430,7 +430,7 @@ static ssize_t ib_ucm_event(struct ib_ucm_file *file,
uevent->resp.id = ctx->id;
}
- if (copy_to_user((void __user *)(unsigned long)cmd.response,
+ if (copy_to_user(u64_to_user_ptr(cmd.response),
&uevent->resp, sizeof(uevent->resp))) {
result = -EFAULT;
goto done;
@@ -441,7 +441,7 @@ static ssize_t ib_ucm_event(struct ib_ucm_file *file,
result = -ENOMEM;
goto done;
}
- if (copy_to_user((void __user *)(unsigned long)cmd.data,
+ if (copy_to_user(u64_to_user_ptr(cmd.data),
uevent->data, uevent->data_len)) {
result = -EFAULT;
goto done;
@@ -453,7 +453,7 @@ static ssize_t ib_ucm_event(struct ib_ucm_file *file,
result = -ENOMEM;
goto done;
}
- if (copy_to_user((void __user *)(unsigned long)cmd.info,
+ if (copy_to_user(u64_to_user_ptr(cmd.info),
uevent->info, uevent->info_len)) {
result = -EFAULT;
goto done;
@@ -502,7 +502,7 @@ static ssize_t ib_ucm_create_id(struct ib_ucm_file *file,
}
resp.id = ctx->id;
- if (copy_to_user((void __user *)(unsigned long)cmd.response,
+ if (copy_to_user(u64_to_user_ptr(cmd.response),
&resp, sizeof(resp))) {
result = -EFAULT;
goto err2;
@@ -556,7 +556,7 @@ static ssize_t ib_ucm_destroy_id(struct ib_ucm_file *file,
ib_ucm_cleanup_events(ctx);
resp.events_reported = ctx->events_reported;
- if (copy_to_user((void __user *)(unsigned long)cmd.response,
+ if (copy_to_user(u64_to_user_ptr(cmd.response),
&resp, sizeof(resp)))
result = -EFAULT;
@@ -588,7 +588,7 @@ static ssize_t ib_ucm_attr_id(struct ib_ucm_file *file,
resp.local_id = ctx->cm_id->local_id;
resp.remote_id = ctx->cm_id->remote_id;
- if (copy_to_user((void __user *)(unsigned long)cmd.response,
+ if (copy_to_user(u64_to_user_ptr(cmd.response),
&resp, sizeof(resp)))
result = -EFAULT;
@@ -625,7 +625,7 @@ static ssize_t ib_ucm_init_qp_attr(struct ib_ucm_file *file,
ib_copy_qp_attr_to_user(ctx->cm_id->device, &resp, &qp_attr);
- if (copy_to_user((void __user *)(unsigned long)cmd.response,
+ if (copy_to_user(u64_to_user_ptr(cmd.response),
&resp, sizeof(resp)))
result = -EFAULT;
@@ -699,7 +699,7 @@ static int ib_ucm_alloc_data(const void **dest, u64 src, u32 len)
if (!len)
return 0;
- data = memdup_user((void __user *)(unsigned long)src, len);
+ data = memdup_user(u64_to_user_ptr(src), len);
if (IS_ERR(data))
return PTR_ERR(data);
@@ -721,7 +721,7 @@ static int ib_ucm_path_get(struct sa_path_rec **path, u64 src)
if (!sa_path)
return -ENOMEM;
- if (copy_from_user(&upath, (void __user *)(unsigned long)src,
+ if (copy_from_user(&upath, u64_to_user_ptr(src),
sizeof(upath))) {
kfree(sa_path);
diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c
index e5a1e7d81326..74329483af6d 100644
--- a/drivers/infiniband/core/ucma.c
+++ b/drivers/infiniband/core/ucma.c
@@ -382,7 +382,11 @@ static ssize_t ucma_get_event(struct ucma_file *file, const char __user *inbuf,
struct ucma_event *uevent;
int ret = 0;
- if (out_len < sizeof uevent->resp)
+ /*
+ * Old 32 bit user space does not send the 4 byte padding in the
+ * reserved field. We don't care, allow it to keep working.
+ */
+ if (out_len < sizeof(uevent->resp) - sizeof(uevent->resp.reserved))
return -ENOSPC;
if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
@@ -416,8 +420,9 @@ static ssize_t ucma_get_event(struct ucma_file *file, const char __user *inbuf,
uevent->resp.id = ctx->id;
}
- if (copy_to_user((void __user *)(unsigned long)cmd.response,
- &uevent->resp, sizeof uevent->resp)) {
+ if (copy_to_user(u64_to_user_ptr(cmd.response),
+ &uevent->resp,
+ min_t(size_t, out_len, sizeof(uevent->resp)))) {
ret = -EFAULT;
goto done;
}
@@ -477,15 +482,15 @@ static ssize_t ucma_create_id(struct ucma_file *file, const char __user *inbuf,
return -ENOMEM;
ctx->uid = cmd.uid;
- cm_id = rdma_create_id(current->nsproxy->net_ns,
- ucma_event_handler, ctx, cmd.ps, qp_type);
+ cm_id = __rdma_create_id(current->nsproxy->net_ns,
+ ucma_event_handler, ctx, cmd.ps, qp_type, NULL);
if (IS_ERR(cm_id)) {
ret = PTR_ERR(cm_id);
goto err1;
}
resp.id = ctx->id;
- if (copy_to_user((void __user *)(unsigned long)cmd.response,
+ if (copy_to_user(u64_to_user_ptr(cmd.response),
&resp, sizeof(resp))) {
ret = -EFAULT;
goto err2;
@@ -615,7 +620,7 @@ static ssize_t ucma_destroy_id(struct ucma_file *file, const char __user *inbuf,
}
resp.events_reported = ucma_free_ctx(ctx);
- if (copy_to_user((void __user *)(unsigned long)cmd.response,
+ if (copy_to_user(u64_to_user_ptr(cmd.response),
&resp, sizeof(resp)))
ret = -EFAULT;
@@ -632,6 +637,9 @@ static ssize_t ucma_bind_ip(struct ucma_file *file, const char __user *inbuf,
if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
return -EFAULT;
+ if (!rdma_addr_size_in6(&cmd.addr))
+ return -EINVAL;
+
ctx = ucma_get_ctx(file, cmd.id);
if (IS_ERR(ctx))
return PTR_ERR(ctx);
@@ -645,22 +653,21 @@ static ssize_t ucma_bind(struct ucma_file *file, const char __user *inbuf,
int in_len, int out_len)
{
struct rdma_ucm_bind cmd;
- struct sockaddr *addr;
struct ucma_context *ctx;
int ret;
if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
return -EFAULT;
- addr = (struct sockaddr *) &cmd.addr;
- if (cmd.reserved || !cmd.addr_size || (cmd.addr_size != rdma_addr_size(addr)))
+ if (cmd.reserved || !cmd.addr_size ||
+ cmd.addr_size != rdma_addr_size_kss(&cmd.addr))
return -EINVAL;
ctx = ucma_get_ctx(file, cmd.id);
if (IS_ERR(ctx))
return PTR_ERR(ctx);
- ret = rdma_bind_addr(ctx->cm_id, addr);
+ ret = rdma_bind_addr(ctx->cm_id, (struct sockaddr *) &cmd.addr);
ucma_put_ctx(ctx);
return ret;
}
@@ -670,23 +677,22 @@ static ssize_t ucma_resolve_ip(struct ucma_file *file,
int in_len, int out_len)
{
struct rdma_ucm_resolve_ip cmd;
- struct sockaddr *src, *dst;
struct ucma_context *ctx;
int ret;
if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
return -EFAULT;
- src = (struct sockaddr *) &cmd.src_addr;
- dst = (struct sockaddr *) &cmd.dst_addr;
- if (!rdma_addr_size(src) || !rdma_addr_size(dst))
+ if (!rdma_addr_size_in6(&cmd.src_addr) ||
+ !rdma_addr_size_in6(&cmd.dst_addr))
return -EINVAL;
ctx = ucma_get_ctx(file, cmd.id);
if (IS_ERR(ctx))
return PTR_ERR(ctx);
- ret = rdma_resolve_addr(ctx->cm_id, src, dst, cmd.timeout_ms);
+ ret = rdma_resolve_addr(ctx->cm_id, (struct sockaddr *) &cmd.src_addr,
+ (struct sockaddr *) &cmd.dst_addr, cmd.timeout_ms);
ucma_put_ctx(ctx);
return ret;
}
@@ -696,24 +702,23 @@ static ssize_t ucma_resolve_addr(struct ucma_file *file,
int in_len, int out_len)
{
struct rdma_ucm_resolve_addr cmd;
- struct sockaddr *src, *dst;
struct ucma_context *ctx;
int ret;
if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
return -EFAULT;
- src = (struct sockaddr *) &cmd.src_addr;
- dst = (struct sockaddr *) &cmd.dst_addr;
- if (cmd.reserved || (cmd.src_size && (cmd.src_size != rdma_addr_size(src))) ||
- !cmd.dst_size || (cmd.dst_size != rdma_addr_size(dst)))
+ if (cmd.reserved ||
+ (cmd.src_size && (cmd.src_size != rdma_addr_size_kss(&cmd.src_addr))) ||
+ !cmd.dst_size || (cmd.dst_size != rdma_addr_size_kss(&cmd.dst_addr)))
return -EINVAL;
ctx = ucma_get_ctx(file, cmd.id);
if (IS_ERR(ctx))
return PTR_ERR(ctx);
- ret = rdma_resolve_addr(ctx->cm_id, src, dst, cmd.timeout_ms);
+ ret = rdma_resolve_addr(ctx->cm_id, (struct sockaddr *) &cmd.src_addr,
+ (struct sockaddr *) &cmd.dst_addr, cmd.timeout_ms);
ucma_put_ctx(ctx);
return ret;
}
@@ -845,7 +850,7 @@ static ssize_t ucma_query_route(struct ucma_file *file,
ucma_copy_iw_route(&resp, &ctx->cm_id->route);
out:
- if (copy_to_user((void __user *)(unsigned long)cmd.response,
+ if (copy_to_user(u64_to_user_ptr(cmd.response),
&resp, sizeof(resp)))
ret = -EFAULT;
@@ -991,7 +996,7 @@ static ssize_t ucma_query(struct ucma_file *file,
if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
return -EFAULT;
- response = (void __user *)(unsigned long) cmd.response;
+ response = u64_to_user_ptr(cmd.response);
ctx = ucma_get_ctx(file, cmd.id);
if (IS_ERR(ctx))
return PTR_ERR(ctx);
@@ -1094,12 +1099,12 @@ static ssize_t ucma_accept(struct ucma_file *file, const char __user *inbuf,
if (cmd.conn_param.valid) {
ucma_copy_conn_param(ctx->cm_id, &conn_param, &cmd.conn_param);
mutex_lock(&file->mut);
- ret = rdma_accept(ctx->cm_id, &conn_param);
+ ret = __rdma_accept(ctx->cm_id, &conn_param, NULL);
if (!ret)
ctx->uid = cmd.uid;
mutex_unlock(&file->mut);
} else
- ret = rdma_accept(ctx->cm_id, NULL);
+ ret = __rdma_accept(ctx->cm_id, NULL, NULL);
ucma_put_ctx(ctx);
return ret;
@@ -1166,6 +1171,11 @@ static ssize_t ucma_init_qp_attr(struct ucma_file *file,
if (IS_ERR(ctx))
return PTR_ERR(ctx);
+ if (!ctx->cm_id->device) {
+ ret = -EINVAL;
+ goto out;
+ }
+
resp.qp_attr_mask = 0;
memset(&qp_attr, 0, sizeof qp_attr);
qp_attr.qp_state = cmd.qp_state;
@@ -1174,7 +1184,7 @@ static ssize_t ucma_init_qp_attr(struct ucma_file *file,
goto out;
ib_copy_qp_attr_to_user(ctx->cm_id->device, &resp, &qp_attr);
- if (copy_to_user((void __user *)(unsigned long)cmd.response,
+ if (copy_to_user(u64_to_user_ptr(cmd.response),
&resp, sizeof(resp)))
ret = -EFAULT;
@@ -1236,6 +1246,9 @@ static int ucma_set_ib_path(struct ucma_context *ctx,
if (!optlen)
return -EINVAL;
+ if (!ctx->cm_id->device)
+ return -EINVAL;
+
memset(&sa_path, 0, sizeof(sa_path));
sa_path.rec_type = SA_PATH_REC_TYPE_IB;
@@ -1307,10 +1320,10 @@ static ssize_t ucma_set_option(struct ucma_file *file, const char __user *inbuf,
if (IS_ERR(ctx))
return PTR_ERR(ctx);
- if (unlikely(cmd.optval > KMALLOC_MAX_SIZE))
+ if (unlikely(cmd.optlen > KMALLOC_MAX_SIZE))
return -EINVAL;
- optval = memdup_user((void __user *) (unsigned long) cmd.optval,
+ optval = memdup_user(u64_to_user_ptr(cmd.optval),
cmd.optlen);
if (IS_ERR(optval)) {
ret = PTR_ERR(optval);
@@ -1331,7 +1344,7 @@ static ssize_t ucma_notify(struct ucma_file *file, const char __user *inbuf,
{
struct rdma_ucm_notify cmd;
struct ucma_context *ctx;
- int ret;
+ int ret = -EINVAL;
if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
return -EFAULT;
@@ -1340,7 +1353,9 @@ static ssize_t ucma_notify(struct ucma_file *file, const char __user *inbuf,
if (IS_ERR(ctx))
return PTR_ERR(ctx);
- ret = rdma_notify(ctx->cm_id, (enum ib_event_type) cmd.event);
+ if (ctx->cm_id->device)
+ ret = rdma_notify(ctx->cm_id, (enum ib_event_type)cmd.event);
+
ucma_put_ctx(ctx);
return ret;
}
@@ -1388,7 +1403,7 @@ static ssize_t ucma_process_join(struct ucma_file *file,
goto err2;
resp.id = mc->id;
- if (copy_to_user((void __user *)(unsigned long) cmd->response,
+ if (copy_to_user(u64_to_user_ptr(cmd->response),
&resp, sizeof(resp))) {
ret = -EFAULT;
goto err3;
@@ -1426,7 +1441,7 @@ static ssize_t ucma_join_ip_multicast(struct ucma_file *file,
join_cmd.response = cmd.response;
join_cmd.uid = cmd.uid;
join_cmd.id = cmd.id;
- join_cmd.addr_size = rdma_addr_size((struct sockaddr *) &cmd.addr);
+ join_cmd.addr_size = rdma_addr_size_in6(&cmd.addr);
if (!join_cmd.addr_size)
return -EINVAL;
@@ -1445,7 +1460,7 @@ static ssize_t ucma_join_multicast(struct ucma_file *file,
if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
return -EFAULT;
- if (!rdma_addr_size((struct sockaddr *)&cmd.addr))
+ if (!rdma_addr_size_kss(&cmd.addr))
return -EINVAL;
return ucma_process_join(file, &cmd, out_len);
@@ -1493,7 +1508,7 @@ static ssize_t ucma_leave_multicast(struct ucma_file *file,
resp.events_reported = mc->events_reported;
kfree(mc);
- if (copy_to_user((void __user *)(unsigned long)cmd.response,
+ if (copy_to_user(u64_to_user_ptr(cmd.response),
&resp, sizeof(resp)))
ret = -EFAULT;
out:
@@ -1580,7 +1595,7 @@ static ssize_t ucma_migrate_id(struct ucma_file *new_file,
ucma_unlock_files(cur_file, new_file);
response:
- if (copy_to_user((void __user *)(unsigned long)cmd.response,
+ if (copy_to_user(u64_to_user_ptr(cmd.response),
&resp, sizeof(resp)))
ret = -EFAULT;
diff --git a/drivers/infiniband/core/uverbs.h b/drivers/infiniband/core/uverbs.h
index deccefb71a6b..cfb51618ab7a 100644
--- a/drivers/infiniband/core/uverbs.h
+++ b/drivers/infiniband/core/uverbs.h
@@ -46,6 +46,10 @@
#include <rdma/ib_verbs.h>
#include <rdma/ib_umem.h>
#include <rdma/ib_user_verbs.h>
+#include <rdma/uverbs_std_types.h>
+
+#define UVERBS_MODULE_NAME ib_uverbs
+#include <rdma/uverbs_named_ioctl.h>
static inline void
ib_uverbs_init_udata(struct ib_udata *udata,
@@ -199,11 +203,18 @@ struct ib_ucq_object {
u32 async_events_reported;
};
+struct ib_uflow_resources;
+struct ib_uflow_object {
+ struct ib_uobject uobject;
+ struct ib_uflow_resources *resources;
+};
+
extern const struct file_operations uverbs_event_fops;
void ib_uverbs_init_event_queue(struct ib_uverbs_event_queue *ev_queue);
struct file *ib_uverbs_alloc_async_event_file(struct ib_uverbs_file *uverbs_file,
struct ib_device *ib_dev);
void ib_uverbs_free_async_event_file(struct ib_uverbs_file *uverbs_file);
+void ib_uverbs_flow_resources_free(struct ib_uflow_resources *uflow_res);
void ib_uverbs_release_ucq(struct ib_uverbs_file *file,
struct ib_uverbs_completion_event_file *ev_file,
@@ -226,7 +237,13 @@ int uverbs_dealloc_mw(struct ib_mw *mw);
void ib_uverbs_detach_umcast(struct ib_qp *qp,
struct ib_uqp_object *uobj);
+void create_udata(struct uverbs_attr_bundle *ctx, struct ib_udata *udata);
+extern const struct uverbs_attr_def uverbs_uhw_compat_in;
+extern const struct uverbs_attr_def uverbs_uhw_compat_out;
long ib_uverbs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
+int uverbs_destroy_def_handler(struct ib_device *ib_dev,
+ struct ib_uverbs_file *file,
+ struct uverbs_attr_bundle *attrs);
struct ib_uverbs_flow_spec {
union {
@@ -240,13 +257,37 @@ struct ib_uverbs_flow_spec {
};
struct ib_uverbs_flow_spec_eth eth;
struct ib_uverbs_flow_spec_ipv4 ipv4;
+ struct ib_uverbs_flow_spec_esp esp;
struct ib_uverbs_flow_spec_tcp_udp tcp_udp;
struct ib_uverbs_flow_spec_ipv6 ipv6;
struct ib_uverbs_flow_spec_action_tag flow_tag;
struct ib_uverbs_flow_spec_action_drop drop;
+ struct ib_uverbs_flow_spec_action_handle action;
};
};
+int ib_uverbs_kern_spec_to_ib_spec_filter(enum ib_flow_spec_type type,
+ const void *kern_spec_mask,
+ const void *kern_spec_val,
+ size_t kern_filter_sz,
+ union ib_flow_spec *ib_spec);
+
+extern const struct uverbs_object_def UVERBS_OBJECT(UVERBS_OBJECT_DEVICE);
+extern const struct uverbs_object_def UVERBS_OBJECT(UVERBS_OBJECT_PD);
+extern const struct uverbs_object_def UVERBS_OBJECT(UVERBS_OBJECT_MR);
+extern const struct uverbs_object_def UVERBS_OBJECT(UVERBS_OBJECT_COMP_CHANNEL);
+extern const struct uverbs_object_def UVERBS_OBJECT(UVERBS_OBJECT_CQ);
+extern const struct uverbs_object_def UVERBS_OBJECT(UVERBS_OBJECT_QP);
+extern const struct uverbs_object_def UVERBS_OBJECT(UVERBS_OBJECT_AH);
+extern const struct uverbs_object_def UVERBS_OBJECT(UVERBS_OBJECT_MW);
+extern const struct uverbs_object_def UVERBS_OBJECT(UVERBS_OBJECT_SRQ);
+extern const struct uverbs_object_def UVERBS_OBJECT(UVERBS_OBJECT_FLOW);
+extern const struct uverbs_object_def UVERBS_OBJECT(UVERBS_OBJECT_WQ);
+extern const struct uverbs_object_def UVERBS_OBJECT(UVERBS_OBJECT_RWQ_IND_TBL);
+extern const struct uverbs_object_def UVERBS_OBJECT(UVERBS_OBJECT_XRCD);
+extern const struct uverbs_object_def UVERBS_OBJECT(UVERBS_OBJECT_FLOW_ACTION);
+extern const struct uverbs_object_def UVERBS_OBJECT(UVERBS_OBJECT_DM);
+
#define IB_UVERBS_DECLARE_CMD(name) \
ssize_t ib_uverbs_##name(struct ib_uverbs_file *file, \
struct ib_device *ib_dev, \
diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c
index a148de35df8d..13cb5e4deb86 100644
--- a/drivers/infiniband/core/uverbs_cmd.c
+++ b/drivers/infiniband/core/uverbs_cmd.c
@@ -50,7 +50,7 @@
static struct ib_uverbs_completion_event_file *
ib_uverbs_lookup_comp_file(int fd, struct ib_ucontext *context)
{
- struct ib_uobject *uobj = uobj_get_read(uobj_get_type(comp_channel),
+ struct ib_uobject *uobj = uobj_get_read(UVERBS_OBJECT_COMP_CHANNEL,
fd, context);
struct ib_uobject_file *uobj_file;
@@ -322,7 +322,7 @@ ssize_t ib_uverbs_alloc_pd(struct ib_uverbs_file *file,
in_len - sizeof(cmd) - sizeof(struct ib_uverbs_cmd_hdr),
out_len - sizeof(resp));
- uobj = uobj_alloc(uobj_get_type(pd), file->ucontext);
+ uobj = uobj_alloc(UVERBS_OBJECT_PD, file->ucontext);
if (IS_ERR(uobj))
return PTR_ERR(uobj);
@@ -372,7 +372,7 @@ ssize_t ib_uverbs_dealloc_pd(struct ib_uverbs_file *file,
if (copy_from_user(&cmd, buf, sizeof cmd))
return -EFAULT;
- uobj = uobj_get_write(uobj_get_type(pd), cmd.pd_handle,
+ uobj = uobj_get_write(UVERBS_OBJECT_PD, cmd.pd_handle,
file->ucontext);
if (IS_ERR(uobj))
return PTR_ERR(uobj);
@@ -517,7 +517,7 @@ ssize_t ib_uverbs_open_xrcd(struct ib_uverbs_file *file,
}
}
- obj = (struct ib_uxrcd_object *)uobj_alloc(uobj_get_type(xrcd),
+ obj = (struct ib_uxrcd_object *)uobj_alloc(UVERBS_OBJECT_XRCD,
file->ucontext);
if (IS_ERR(obj)) {
ret = PTR_ERR(obj);
@@ -602,7 +602,7 @@ ssize_t ib_uverbs_close_xrcd(struct ib_uverbs_file *file,
if (copy_from_user(&cmd, buf, sizeof cmd))
return -EFAULT;
- uobj = uobj_get_write(uobj_get_type(xrcd), cmd.xrcd_handle,
+ uobj = uobj_get_write(UVERBS_OBJECT_XRCD, cmd.xrcd_handle,
file->ucontext);
if (IS_ERR(uobj))
return PTR_ERR(uobj);
@@ -663,11 +663,11 @@ ssize_t ib_uverbs_reg_mr(struct ib_uverbs_file *file,
if (ret)
return ret;
- uobj = uobj_alloc(uobj_get_type(mr), file->ucontext);
+ uobj = uobj_alloc(UVERBS_OBJECT_MR, file->ucontext);
if (IS_ERR(uobj))
return PTR_ERR(uobj);
- pd = uobj_get_obj_read(pd, cmd.pd_handle, file->ucontext);
+ pd = uobj_get_obj_read(pd, UVERBS_OBJECT_PD, cmd.pd_handle, file->ucontext);
if (!pd) {
ret = -EINVAL;
goto err_free;
@@ -693,6 +693,8 @@ ssize_t ib_uverbs_reg_mr(struct ib_uverbs_file *file,
mr->pd = pd;
mr->uobject = uobj;
atomic_inc(&pd->usecnt);
+ mr->res.type = RDMA_RESTRACK_MR;
+ rdma_restrack_add(&mr->res);
uobj->object = mr;
@@ -756,7 +758,7 @@ ssize_t ib_uverbs_rereg_mr(struct ib_uverbs_file *file,
(cmd.start & ~PAGE_MASK) != (cmd.hca_va & ~PAGE_MASK)))
return -EINVAL;
- uobj = uobj_get_write(uobj_get_type(mr), cmd.mr_handle,
+ uobj = uobj_get_write(UVERBS_OBJECT_MR, cmd.mr_handle,
file->ucontext);
if (IS_ERR(uobj))
return PTR_ERR(uobj);
@@ -770,7 +772,7 @@ ssize_t ib_uverbs_rereg_mr(struct ib_uverbs_file *file,
}
if (cmd.flags & IB_MR_REREG_PD) {
- pd = uobj_get_obj_read(pd, cmd.pd_handle, file->ucontext);
+ pd = uobj_get_obj_read(pd, UVERBS_OBJECT_PD, cmd.pd_handle, file->ucontext);
if (!pd) {
ret = -EINVAL;
goto put_uobjs;
@@ -822,7 +824,7 @@ ssize_t ib_uverbs_dereg_mr(struct ib_uverbs_file *file,
if (copy_from_user(&cmd, buf, sizeof cmd))
return -EFAULT;
- uobj = uobj_get_write(uobj_get_type(mr), cmd.mr_handle,
+ uobj = uobj_get_write(UVERBS_OBJECT_MR, cmd.mr_handle,
file->ucontext);
if (IS_ERR(uobj))
return PTR_ERR(uobj);
@@ -851,11 +853,11 @@ ssize_t ib_uverbs_alloc_mw(struct ib_uverbs_file *file,
if (copy_from_user(&cmd, buf, sizeof(cmd)))
return -EFAULT;
- uobj = uobj_alloc(uobj_get_type(mw), file->ucontext);
+ uobj = uobj_alloc(UVERBS_OBJECT_MW, file->ucontext);
if (IS_ERR(uobj))
return PTR_ERR(uobj);
- pd = uobj_get_obj_read(pd, cmd.pd_handle, file->ucontext);
+ pd = uobj_get_obj_read(pd, UVERBS_OBJECT_PD, cmd.pd_handle, file->ucontext);
if (!pd) {
ret = -EINVAL;
goto err_free;
@@ -914,7 +916,7 @@ ssize_t ib_uverbs_dealloc_mw(struct ib_uverbs_file *file,
if (copy_from_user(&cmd, buf, sizeof(cmd)))
return -EFAULT;
- uobj = uobj_get_write(uobj_get_type(mw), cmd.mw_handle,
+ uobj = uobj_get_write(UVERBS_OBJECT_MW, cmd.mw_handle,
file->ucontext);
if (IS_ERR(uobj))
return PTR_ERR(uobj);
@@ -939,7 +941,7 @@ ssize_t ib_uverbs_create_comp_channel(struct ib_uverbs_file *file,
if (copy_from_user(&cmd, buf, sizeof cmd))
return -EFAULT;
- uobj = uobj_alloc(uobj_get_type(comp_channel), file->ucontext);
+ uobj = uobj_alloc(UVERBS_OBJECT_COMP_CHANNEL, file->ucontext);
if (IS_ERR(uobj))
return PTR_ERR(uobj);
@@ -984,7 +986,7 @@ static struct ib_ucq_object *create_cq(struct ib_uverbs_file *file,
if (cmd->comp_vector >= file->device->num_comp_vectors)
return ERR_PTR(-EINVAL);
- obj = (struct ib_ucq_object *)uobj_alloc(uobj_get_type(cq),
+ obj = (struct ib_ucq_object *)uobj_alloc(UVERBS_OBJECT_CQ,
file->ucontext);
if (IS_ERR(obj))
return obj;
@@ -1173,7 +1175,7 @@ ssize_t ib_uverbs_resize_cq(struct ib_uverbs_file *file,
in_len - sizeof(cmd) - sizeof(struct ib_uverbs_cmd_hdr),
out_len - sizeof(resp));
- cq = uobj_get_obj_read(cq, cmd.cq_handle, file->ucontext);
+ cq = uobj_get_obj_read(cq, UVERBS_OBJECT_CQ, cmd.cq_handle, file->ucontext);
if (!cq)
return -EINVAL;
@@ -1238,7 +1240,7 @@ ssize_t ib_uverbs_poll_cq(struct ib_uverbs_file *file,
if (copy_from_user(&cmd, buf, sizeof cmd))
return -EFAULT;
- cq = uobj_get_obj_read(cq, cmd.cq_handle, file->ucontext);
+ cq = uobj_get_obj_read(cq, UVERBS_OBJECT_CQ, cmd.cq_handle, file->ucontext);
if (!cq)
return -EINVAL;
@@ -1285,7 +1287,7 @@ ssize_t ib_uverbs_req_notify_cq(struct ib_uverbs_file *file,
if (copy_from_user(&cmd, buf, sizeof cmd))
return -EFAULT;
- cq = uobj_get_obj_read(cq, cmd.cq_handle, file->ucontext);
+ cq = uobj_get_obj_read(cq, UVERBS_OBJECT_CQ, cmd.cq_handle, file->ucontext);
if (!cq)
return -EINVAL;
@@ -1312,7 +1314,7 @@ ssize_t ib_uverbs_destroy_cq(struct ib_uverbs_file *file,
if (copy_from_user(&cmd, buf, sizeof cmd))
return -EFAULT;
- uobj = uobj_get_write(uobj_get_type(cq), cmd.cq_handle,
+ uobj = uobj_get_write(UVERBS_OBJECT_CQ, cmd.cq_handle,
file->ucontext);
if (IS_ERR(uobj))
return PTR_ERR(uobj);
@@ -1371,7 +1373,7 @@ static int create_qp(struct ib_uverbs_file *file,
if (cmd->qp_type == IB_QPT_RAW_PACKET && !capable(CAP_NET_RAW))
return -EPERM;
- obj = (struct ib_uqp_object *)uobj_alloc(uobj_get_type(qp),
+ obj = (struct ib_uqp_object *)uobj_alloc(UVERBS_OBJECT_QP,
file->ucontext);
if (IS_ERR(obj))
return PTR_ERR(obj);
@@ -1382,7 +1384,7 @@ static int create_qp(struct ib_uverbs_file *file,
if (cmd_sz >= offsetof(typeof(*cmd), rwq_ind_tbl_handle) +
sizeof(cmd->rwq_ind_tbl_handle) &&
(cmd->comp_mask & IB_UVERBS_CREATE_QP_MASK_IND_TABLE)) {
- ind_tbl = uobj_get_obj_read(rwq_ind_table,
+ ind_tbl = uobj_get_obj_read(rwq_ind_table, UVERBS_OBJECT_RWQ_IND_TBL,
cmd->rwq_ind_tbl_handle,
file->ucontext);
if (!ind_tbl) {
@@ -1409,7 +1411,7 @@ static int create_qp(struct ib_uverbs_file *file,
has_sq = false;
if (cmd->qp_type == IB_QPT_XRC_TGT) {
- xrcd_uobj = uobj_get_read(uobj_get_type(xrcd), cmd->pd_handle,
+ xrcd_uobj = uobj_get_read(UVERBS_OBJECT_XRCD, cmd->pd_handle,
file->ucontext);
if (IS_ERR(xrcd_uobj)) {
@@ -1429,7 +1431,7 @@ static int create_qp(struct ib_uverbs_file *file,
cmd->max_recv_sge = 0;
} else {
if (cmd->is_srq) {
- srq = uobj_get_obj_read(srq, cmd->srq_handle,
+ srq = uobj_get_obj_read(srq, UVERBS_OBJECT_SRQ, cmd->srq_handle,
file->ucontext);
if (!srq || srq->srq_type == IB_SRQT_XRC) {
ret = -EINVAL;
@@ -1439,7 +1441,7 @@ static int create_qp(struct ib_uverbs_file *file,
if (!ind_tbl) {
if (cmd->recv_cq_handle != cmd->send_cq_handle) {
- rcq = uobj_get_obj_read(cq, cmd->recv_cq_handle,
+ rcq = uobj_get_obj_read(cq, UVERBS_OBJECT_CQ, cmd->recv_cq_handle,
file->ucontext);
if (!rcq) {
ret = -EINVAL;
@@ -1450,11 +1452,11 @@ static int create_qp(struct ib_uverbs_file *file,
}
if (has_sq)
- scq = uobj_get_obj_read(cq, cmd->send_cq_handle,
+ scq = uobj_get_obj_read(cq, UVERBS_OBJECT_CQ, cmd->send_cq_handle,
file->ucontext);
if (!ind_tbl)
rcq = rcq ?: scq;
- pd = uobj_get_obj_read(pd, cmd->pd_handle, file->ucontext);
+ pd = uobj_get_obj_read(pd, UVERBS_OBJECT_PD, cmd->pd_handle, file->ucontext);
if (!pd || (!scq && has_sq)) {
ret = -EINVAL;
goto err_put;
@@ -1751,12 +1753,12 @@ ssize_t ib_uverbs_open_qp(struct ib_uverbs_file *file,
in_len - sizeof(cmd) - sizeof(struct ib_uverbs_cmd_hdr),
out_len - sizeof(resp));
- obj = (struct ib_uqp_object *)uobj_alloc(uobj_get_type(qp),
+ obj = (struct ib_uqp_object *)uobj_alloc(UVERBS_OBJECT_QP,
file->ucontext);
if (IS_ERR(obj))
return PTR_ERR(obj);
- xrcd_uobj = uobj_get_read(uobj_get_type(xrcd), cmd.pd_handle,
+ xrcd_uobj = uobj_get_read(UVERBS_OBJECT_XRCD, cmd.pd_handle,
file->ucontext);
if (IS_ERR(xrcd_uobj)) {
ret = -EINVAL;
@@ -1859,7 +1861,7 @@ ssize_t ib_uverbs_query_qp(struct ib_uverbs_file *file,
goto out;
}
- qp = uobj_get_obj_read(qp, cmd.qp_handle, file->ucontext);
+ qp = uobj_get_obj_read(qp, UVERBS_OBJECT_QP, cmd.qp_handle, file->ucontext);
if (!qp) {
ret = -EINVAL;
goto out;
@@ -1964,7 +1966,7 @@ static int modify_qp(struct ib_uverbs_file *file,
if (!attr)
return -ENOMEM;
- qp = uobj_get_obj_read(qp, cmd->base.qp_handle, file->ucontext);
+ qp = uobj_get_obj_read(qp, UVERBS_OBJECT_QP, cmd->base.qp_handle, file->ucontext);
if (!qp) {
ret = -EINVAL;
goto out;
@@ -1989,6 +1991,13 @@ static int modify_qp(struct ib_uverbs_file *file,
goto release_qp;
}
+ if ((cmd->base.attr_mask & IB_QP_CUR_STATE &&
+ cmd->base.cur_qp_state > IB_QPS_ERR) ||
+ cmd->base.qp_state > IB_QPS_ERR) {
+ ret = -EINVAL;
+ goto release_qp;
+ }
+
attr->qp_state = cmd->base.qp_state;
attr->cur_qp_state = cmd->base.cur_qp_state;
attr->path_mtu = cmd->base.path_mtu;
@@ -2112,7 +2121,7 @@ ssize_t ib_uverbs_destroy_qp(struct ib_uverbs_file *file,
memset(&resp, 0, sizeof resp);
- uobj = uobj_get_write(uobj_get_type(qp), cmd.qp_handle,
+ uobj = uobj_get_write(UVERBS_OBJECT_QP, cmd.qp_handle,
file->ucontext);
if (IS_ERR(uobj))
return PTR_ERR(uobj);
@@ -2178,7 +2187,7 @@ ssize_t ib_uverbs_post_send(struct ib_uverbs_file *file,
if (!user_wr)
return -ENOMEM;
- qp = uobj_get_obj_read(qp, cmd.qp_handle, file->ucontext);
+ qp = uobj_get_obj_read(qp, UVERBS_OBJECT_QP, cmd.qp_handle, file->ucontext);
if (!qp)
goto out;
@@ -2214,7 +2223,7 @@ ssize_t ib_uverbs_post_send(struct ib_uverbs_file *file,
goto out_put;
}
- ud->ah = uobj_get_obj_read(ah, user_wr->wr.ud.ah,
+ ud->ah = uobj_get_obj_read(ah, UVERBS_OBJECT_AH, user_wr->wr.ud.ah,
file->ucontext);
if (!ud->ah) {
kfree(ud);
@@ -2449,7 +2458,7 @@ ssize_t ib_uverbs_post_recv(struct ib_uverbs_file *file,
if (IS_ERR(wr))
return PTR_ERR(wr);
- qp = uobj_get_obj_read(qp, cmd.qp_handle, file->ucontext);
+ qp = uobj_get_obj_read(qp, UVERBS_OBJECT_QP, cmd.qp_handle, file->ucontext);
if (!qp)
goto out;
@@ -2498,7 +2507,7 @@ ssize_t ib_uverbs_post_srq_recv(struct ib_uverbs_file *file,
if (IS_ERR(wr))
return PTR_ERR(wr);
- srq = uobj_get_obj_read(srq, cmd.srq_handle, file->ucontext);
+ srq = uobj_get_obj_read(srq, UVERBS_OBJECT_SRQ, cmd.srq_handle, file->ucontext);
if (!srq)
goto out;
@@ -2555,11 +2564,11 @@ ssize_t ib_uverbs_create_ah(struct ib_uverbs_file *file,
in_len - sizeof(cmd) - sizeof(struct ib_uverbs_cmd_hdr),
out_len - sizeof(resp));
- uobj = uobj_alloc(uobj_get_type(ah), file->ucontext);
+ uobj = uobj_alloc(UVERBS_OBJECT_AH, file->ucontext);
if (IS_ERR(uobj))
return PTR_ERR(uobj);
- pd = uobj_get_obj_read(pd, cmd.pd_handle, file->ucontext);
+ pd = uobj_get_obj_read(pd, UVERBS_OBJECT_PD, cmd.pd_handle, file->ucontext);
if (!pd) {
ret = -EINVAL;
goto err;
@@ -2627,7 +2636,7 @@ ssize_t ib_uverbs_destroy_ah(struct ib_uverbs_file *file,
if (copy_from_user(&cmd, buf, sizeof cmd))
return -EFAULT;
- uobj = uobj_get_write(uobj_get_type(ah), cmd.ah_handle,
+ uobj = uobj_get_write(UVERBS_OBJECT_AH, cmd.ah_handle,
file->ucontext);
if (IS_ERR(uobj))
return PTR_ERR(uobj);
@@ -2650,7 +2659,7 @@ ssize_t ib_uverbs_attach_mcast(struct ib_uverbs_file *file,
if (copy_from_user(&cmd, buf, sizeof cmd))
return -EFAULT;
- qp = uobj_get_obj_read(qp, cmd.qp_handle, file->ucontext);
+ qp = uobj_get_obj_read(qp, UVERBS_OBJECT_QP, cmd.qp_handle, file->ucontext);
if (!qp)
return -EINVAL;
@@ -2701,7 +2710,7 @@ ssize_t ib_uverbs_detach_mcast(struct ib_uverbs_file *file,
if (copy_from_user(&cmd, buf, sizeof cmd))
return -EFAULT;
- qp = uobj_get_obj_read(qp, cmd.qp_handle, file->ucontext);
+ qp = uobj_get_obj_read(qp, UVERBS_OBJECT_QP, cmd.qp_handle, file->ucontext);
if (!qp)
return -EINVAL;
@@ -2730,8 +2739,52 @@ out_put:
return ret ? ret : in_len;
}
-static int kern_spec_to_ib_spec_action(struct ib_uverbs_flow_spec *kern_spec,
- union ib_flow_spec *ib_spec)
+struct ib_uflow_resources {
+ size_t max;
+ size_t num;
+ struct ib_flow_action *collection[0];
+};
+
+static struct ib_uflow_resources *flow_resources_alloc(size_t num_specs)
+{
+ struct ib_uflow_resources *resources;
+
+ resources =
+ kmalloc(sizeof(*resources) +
+ num_specs * sizeof(*resources->collection), GFP_KERNEL);
+
+ if (!resources)
+ return NULL;
+
+ resources->num = 0;
+ resources->max = num_specs;
+
+ return resources;
+}
+
+void ib_uverbs_flow_resources_free(struct ib_uflow_resources *uflow_res)
+{
+ unsigned int i;
+
+ for (i = 0; i < uflow_res->num; i++)
+ atomic_dec(&uflow_res->collection[i]->usecnt);
+
+ kfree(uflow_res);
+}
+
+static void flow_resources_add(struct ib_uflow_resources *uflow_res,
+ struct ib_flow_action *action)
+{
+ WARN_ON(uflow_res->num >= uflow_res->max);
+
+ atomic_inc(&action->usecnt);
+ uflow_res->collection[uflow_res->num++] = action;
+}
+
+static int kern_spec_to_ib_spec_action(struct ib_ucontext *ucontext,
+ struct ib_uverbs_flow_spec *kern_spec,
+ union ib_flow_spec *ib_spec,
+ struct ib_uflow_resources *uflow_res)
{
ib_spec->type = kern_spec->type;
switch (ib_spec->type) {
@@ -2750,19 +2803,34 @@ static int kern_spec_to_ib_spec_action(struct ib_uverbs_flow_spec *kern_spec,
ib_spec->drop.size = sizeof(struct ib_flow_spec_action_drop);
break;
+ case IB_FLOW_SPEC_ACTION_HANDLE:
+ if (kern_spec->action.size !=
+ sizeof(struct ib_uverbs_flow_spec_action_handle))
+ return -EOPNOTSUPP;
+ ib_spec->action.act = uobj_get_obj_read(flow_action,
+ UVERBS_OBJECT_FLOW_ACTION,
+ kern_spec->action.handle,
+ ucontext);
+ if (!ib_spec->action.act)
+ return -EINVAL;
+ ib_spec->action.size =
+ sizeof(struct ib_flow_spec_action_handle);
+ flow_resources_add(uflow_res, ib_spec->action.act);
+ uobj_put_obj_read(ib_spec->action.act);
+ break;
default:
return -EINVAL;
}
return 0;
}
-static size_t kern_spec_filter_sz(struct ib_uverbs_flow_spec_hdr *spec)
+static size_t kern_spec_filter_sz(const struct ib_uverbs_flow_spec_hdr *spec)
{
/* Returns user space filter size, includes padding */
return (spec->size - sizeof(struct ib_uverbs_flow_spec_hdr)) / 2;
}
-static ssize_t spec_filter_size(void *kern_spec_filter, u16 kern_filter_size,
+static ssize_t spec_filter_size(const void *kern_spec_filter, u16 kern_filter_size,
u16 ib_real_filter_sz)
{
/*
@@ -2780,28 +2848,21 @@ static ssize_t spec_filter_size(void *kern_spec_filter, u16 kern_filter_size,
return kern_filter_size;
}
-static int kern_spec_to_ib_spec_filter(struct ib_uverbs_flow_spec *kern_spec,
- union ib_flow_spec *ib_spec)
+int ib_uverbs_kern_spec_to_ib_spec_filter(enum ib_flow_spec_type type,
+ const void *kern_spec_mask,
+ const void *kern_spec_val,
+ size_t kern_filter_sz,
+ union ib_flow_spec *ib_spec)
{
ssize_t actual_filter_sz;
- ssize_t kern_filter_sz;
ssize_t ib_filter_sz;
- void *kern_spec_mask;
- void *kern_spec_val;
- if (kern_spec->reserved)
- return -EINVAL;
-
- ib_spec->type = kern_spec->type;
-
- kern_filter_sz = kern_spec_filter_sz(&kern_spec->hdr);
/* User flow spec size must be aligned to 4 bytes */
if (kern_filter_sz != ALIGN(kern_filter_sz, 4))
return -EINVAL;
- kern_spec_val = (void *)kern_spec +
- sizeof(struct ib_uverbs_flow_spec_hdr);
- kern_spec_mask = kern_spec_val + kern_filter_sz;
+ ib_spec->type = type;
+
if (ib_spec->type == (IB_FLOW_SPEC_INNER | IB_FLOW_SPEC_VXLAN_TUNNEL))
return -EINVAL;
@@ -2870,20 +2931,56 @@ static int kern_spec_to_ib_spec_filter(struct ib_uverbs_flow_spec *kern_spec,
(ntohl(ib_spec->tunnel.val.tunnel_id)) >= BIT(24))
return -EINVAL;
break;
+ case IB_FLOW_SPEC_ESP:
+ ib_filter_sz = offsetof(struct ib_flow_esp_filter, real_sz);
+ actual_filter_sz = spec_filter_size(kern_spec_mask,
+ kern_filter_sz,
+ ib_filter_sz);
+ if (actual_filter_sz <= 0)
+ return -EINVAL;
+ ib_spec->esp.size = sizeof(struct ib_flow_spec_esp);
+ memcpy(&ib_spec->esp.val, kern_spec_val, actual_filter_sz);
+ memcpy(&ib_spec->esp.mask, kern_spec_mask, actual_filter_sz);
+ break;
default:
return -EINVAL;
}
return 0;
}
-static int kern_spec_to_ib_spec(struct ib_uverbs_flow_spec *kern_spec,
- union ib_flow_spec *ib_spec)
+static int kern_spec_to_ib_spec_filter(struct ib_uverbs_flow_spec *kern_spec,
+ union ib_flow_spec *ib_spec)
+{
+ ssize_t kern_filter_sz;
+ void *kern_spec_mask;
+ void *kern_spec_val;
+
+ if (kern_spec->reserved)
+ return -EINVAL;
+
+ kern_filter_sz = kern_spec_filter_sz(&kern_spec->hdr);
+
+ kern_spec_val = (void *)kern_spec +
+ sizeof(struct ib_uverbs_flow_spec_hdr);
+ kern_spec_mask = kern_spec_val + kern_filter_sz;
+
+ return ib_uverbs_kern_spec_to_ib_spec_filter(kern_spec->type,
+ kern_spec_mask,
+ kern_spec_val,
+ kern_filter_sz, ib_spec);
+}
+
+static int kern_spec_to_ib_spec(struct ib_ucontext *ucontext,
+ struct ib_uverbs_flow_spec *kern_spec,
+ union ib_flow_spec *ib_spec,
+ struct ib_uflow_resources *uflow_res)
{
if (kern_spec->reserved)
return -EINVAL;
if (kern_spec->type >= IB_FLOW_SPEC_ACTION_TAG)
- return kern_spec_to_ib_spec_action(kern_spec, ib_spec);
+ return kern_spec_to_ib_spec_action(ucontext, kern_spec, ib_spec,
+ uflow_res);
else
return kern_spec_to_ib_spec_filter(kern_spec, ib_spec);
}
@@ -2925,18 +3022,18 @@ int ib_uverbs_ex_create_wq(struct ib_uverbs_file *file,
if (cmd.comp_mask)
return -EOPNOTSUPP;
- obj = (struct ib_uwq_object *)uobj_alloc(uobj_get_type(wq),
+ obj = (struct ib_uwq_object *)uobj_alloc(UVERBS_OBJECT_WQ,
file->ucontext);
if (IS_ERR(obj))
return PTR_ERR(obj);
- pd = uobj_get_obj_read(pd, cmd.pd_handle, file->ucontext);
+ pd = uobj_get_obj_read(pd, UVERBS_OBJECT_PD, cmd.pd_handle, file->ucontext);
if (!pd) {
err = -EINVAL;
goto err_uobj;
}
- cq = uobj_get_obj_read(cq, cmd.cq_handle, file->ucontext);
+ cq = uobj_get_obj_read(cq, UVERBS_OBJECT_CQ, cmd.cq_handle, file->ucontext);
if (!cq) {
err = -EINVAL;
goto err_put_pd;
@@ -3040,7 +3137,7 @@ int ib_uverbs_ex_destroy_wq(struct ib_uverbs_file *file,
return -EOPNOTSUPP;
resp.response_length = required_resp_len;
- uobj = uobj_get_write(uobj_get_type(wq), cmd.wq_handle,
+ uobj = uobj_get_write(UVERBS_OBJECT_WQ, cmd.wq_handle,
file->ucontext);
if (IS_ERR(uobj))
return PTR_ERR(uobj);
@@ -3091,7 +3188,7 @@ int ib_uverbs_ex_modify_wq(struct ib_uverbs_file *file,
if (cmd.attr_mask > (IB_WQ_STATE | IB_WQ_CUR_STATE | IB_WQ_FLAGS))
return -EINVAL;
- wq = uobj_get_obj_read(wq, cmd.wq_handle, file->ucontext);
+ wq = uobj_get_obj_read(wq, UVERBS_OBJECT_WQ, cmd.wq_handle, file->ucontext);
if (!wq)
return -EINVAL;
@@ -3185,7 +3282,7 @@ int ib_uverbs_ex_create_rwq_ind_table(struct ib_uverbs_file *file,
for (num_read_wqs = 0; num_read_wqs < num_wq_handles;
num_read_wqs++) {
- wq = uobj_get_obj_read(wq, wqs_handles[num_read_wqs],
+ wq = uobj_get_obj_read(wq, UVERBS_OBJECT_WQ, wqs_handles[num_read_wqs],
file->ucontext);
if (!wq) {
err = -EINVAL;
@@ -3195,7 +3292,7 @@ int ib_uverbs_ex_create_rwq_ind_table(struct ib_uverbs_file *file,
wqs[num_read_wqs] = wq;
}
- uobj = uobj_alloc(uobj_get_type(rwq_ind_table), file->ucontext);
+ uobj = uobj_alloc(UVERBS_OBJECT_RWQ_IND_TBL, file->ucontext);
if (IS_ERR(uobj)) {
err = PTR_ERR(uobj);
goto put_wqs;
@@ -3282,7 +3379,7 @@ int ib_uverbs_ex_destroy_rwq_ind_table(struct ib_uverbs_file *file,
if (cmd.comp_mask)
return -EOPNOTSUPP;
- uobj = uobj_get_write(uobj_get_type(rwq_ind_table), cmd.ind_tbl_handle,
+ uobj = uobj_get_write(UVERBS_OBJECT_RWQ_IND_TBL, cmd.ind_tbl_handle,
file->ucontext);
if (IS_ERR(uobj))
return PTR_ERR(uobj);
@@ -3298,10 +3395,12 @@ int ib_uverbs_ex_create_flow(struct ib_uverbs_file *file,
struct ib_uverbs_create_flow cmd;
struct ib_uverbs_create_flow_resp resp;
struct ib_uobject *uobj;
+ struct ib_uflow_object *uflow;
struct ib_flow *flow_id;
struct ib_uverbs_flow_attr *kern_flow_attr;
struct ib_flow_attr *flow_attr;
struct ib_qp *qp;
+ struct ib_uflow_resources *uflow_res;
int err = 0;
void *kern_spec;
void *ib_spec;
@@ -3361,13 +3460,13 @@ int ib_uverbs_ex_create_flow(struct ib_uverbs_file *file,
kern_flow_attr = &cmd.flow_attr;
}
- uobj = uobj_alloc(uobj_get_type(flow), file->ucontext);
+ uobj = uobj_alloc(UVERBS_OBJECT_FLOW, file->ucontext);
if (IS_ERR(uobj)) {
err = PTR_ERR(uobj);
goto err_free_attr;
}
- qp = uobj_get_obj_read(qp, cmd.qp_handle, file->ucontext);
+ qp = uobj_get_obj_read(qp, UVERBS_OBJECT_QP, cmd.qp_handle, file->ucontext);
if (!qp) {
err = -EINVAL;
goto err_uobj;
@@ -3379,6 +3478,11 @@ int ib_uverbs_ex_create_flow(struct ib_uverbs_file *file,
err = -ENOMEM;
goto err_put;
}
+ uflow_res = flow_resources_alloc(cmd.flow_attr.num_of_specs);
+ if (!uflow_res) {
+ err = -ENOMEM;
+ goto err_free_flow_attr;
+ }
flow_attr->type = kern_flow_attr->type;
flow_attr->priority = kern_flow_attr->priority;
@@ -3393,7 +3497,8 @@ int ib_uverbs_ex_create_flow(struct ib_uverbs_file *file,
cmd.flow_attr.size > offsetof(struct ib_uverbs_flow_spec, reserved) &&
cmd.flow_attr.size >=
((struct ib_uverbs_flow_spec *)kern_spec)->size; i++) {
- err = kern_spec_to_ib_spec(kern_spec, ib_spec);
+ err = kern_spec_to_ib_spec(file->ucontext, kern_spec, ib_spec,
+ uflow_res);
if (err)
goto err_free;
flow_attr->size +=
@@ -3415,6 +3520,8 @@ int ib_uverbs_ex_create_flow(struct ib_uverbs_file *file,
}
flow_id->uobject = uobj;
uobj->object = flow_id;
+ uflow = container_of(uobj, typeof(*uflow), uobject);
+ uflow->resources = uflow_res;
memset(&resp, 0, sizeof(resp));
resp.flow_handle = uobj->id;
@@ -3433,6 +3540,8 @@ int ib_uverbs_ex_create_flow(struct ib_uverbs_file *file,
err_copy:
ib_destroy_flow(flow_id);
err_free:
+ ib_uverbs_flow_resources_free(uflow_res);
+err_free_flow_attr:
kfree(flow_attr);
err_put:
uobj_put_obj_read(qp);
@@ -3463,7 +3572,7 @@ int ib_uverbs_ex_destroy_flow(struct ib_uverbs_file *file,
if (cmd.comp_mask)
return -EINVAL;
- uobj = uobj_get_write(uobj_get_type(flow), cmd.flow_handle,
+ uobj = uobj_get_write(UVERBS_OBJECT_FLOW, cmd.flow_handle,
file->ucontext);
if (IS_ERR(uobj))
return PTR_ERR(uobj);
@@ -3485,7 +3594,7 @@ static int __uverbs_create_xsrq(struct ib_uverbs_file *file,
struct ib_srq_init_attr attr;
int ret;
- obj = (struct ib_usrq_object *)uobj_alloc(uobj_get_type(srq),
+ obj = (struct ib_usrq_object *)uobj_alloc(UVERBS_OBJECT_SRQ,
file->ucontext);
if (IS_ERR(obj))
return PTR_ERR(obj);
@@ -3494,7 +3603,7 @@ static int __uverbs_create_xsrq(struct ib_uverbs_file *file,
attr.ext.tag_matching.max_num_tags = cmd->max_num_tags;
if (cmd->srq_type == IB_SRQT_XRC) {
- xrcd_uobj = uobj_get_read(uobj_get_type(xrcd), cmd->xrcd_handle,
+ xrcd_uobj = uobj_get_read(UVERBS_OBJECT_XRCD, cmd->xrcd_handle,
file->ucontext);
if (IS_ERR(xrcd_uobj)) {
ret = -EINVAL;
@@ -3512,7 +3621,7 @@ static int __uverbs_create_xsrq(struct ib_uverbs_file *file,
}
if (ib_srq_has_cq(cmd->srq_type)) {
- attr.ext.cq = uobj_get_obj_read(cq, cmd->cq_handle,
+ attr.ext.cq = uobj_get_obj_read(cq, UVERBS_OBJECT_CQ, cmd->cq_handle,
file->ucontext);
if (!attr.ext.cq) {
ret = -EINVAL;
@@ -3520,7 +3629,7 @@ static int __uverbs_create_xsrq(struct ib_uverbs_file *file,
}
}
- pd = uobj_get_obj_read(pd, cmd->pd_handle, file->ucontext);
+ pd = uobj_get_obj_read(pd, UVERBS_OBJECT_PD, cmd->pd_handle, file->ucontext);
if (!pd) {
ret = -EINVAL;
goto err_put_cq;
@@ -3572,7 +3681,7 @@ static int __uverbs_create_xsrq(struct ib_uverbs_file *file,
if (cmd->srq_type == IB_SRQT_XRC)
resp.srqn = srq->ext.xrc.srq_num;
- if (copy_to_user((void __user *) (unsigned long) cmd->response,
+ if (copy_to_user(u64_to_user_ptr(cmd->response),
&resp, sizeof resp)) {
ret = -EFAULT;
goto err_copy;
@@ -3692,7 +3801,7 @@ ssize_t ib_uverbs_modify_srq(struct ib_uverbs_file *file,
ib_uverbs_init_udata(&udata, buf + sizeof cmd, NULL, in_len - sizeof cmd,
out_len);
- srq = uobj_get_obj_read(srq, cmd.srq_handle, file->ucontext);
+ srq = uobj_get_obj_read(srq, UVERBS_OBJECT_SRQ, cmd.srq_handle, file->ucontext);
if (!srq)
return -EINVAL;
@@ -3723,7 +3832,7 @@ ssize_t ib_uverbs_query_srq(struct ib_uverbs_file *file,
if (copy_from_user(&cmd, buf, sizeof cmd))
return -EFAULT;
- srq = uobj_get_obj_read(srq, cmd.srq_handle, file->ucontext);
+ srq = uobj_get_obj_read(srq, UVERBS_OBJECT_SRQ, cmd.srq_handle, file->ucontext);
if (!srq)
return -EINVAL;
@@ -3760,7 +3869,7 @@ ssize_t ib_uverbs_destroy_srq(struct ib_uverbs_file *file,
if (copy_from_user(&cmd, buf, sizeof cmd))
return -EFAULT;
- uobj = uobj_get_write(uobj_get_type(srq), cmd.srq_handle,
+ uobj = uobj_get_write(UVERBS_OBJECT_SRQ, cmd.srq_handle,
file->ucontext);
if (IS_ERR(uobj))
return PTR_ERR(uobj);
@@ -3897,6 +4006,12 @@ int ib_uverbs_ex_query_device(struct ib_uverbs_file *file,
resp.cq_moderation_caps.max_cq_moderation_period =
attr.cq_caps.max_cq_moderation_period;
resp.response_length += sizeof(resp.cq_moderation_caps);
+
+ if (ucore->outlen < resp.response_length + sizeof(resp.max_dm_size))
+ goto end;
+
+ resp.max_dm_size = attr.max_dm_size;
+ resp.response_length += sizeof(resp.max_dm_size);
end:
err = ib_copy_to_udata(ucore, &resp, resp.response_length);
return err;
@@ -3933,7 +4048,7 @@ int ib_uverbs_ex_modify_cq(struct ib_uverbs_file *file,
if (cmd.attr_mask > IB_CQ_MODERATE)
return -EOPNOTSUPP;
- cq = uobj_get_obj_read(cq, cmd.cq_handle, file->ucontext);
+ cq = uobj_get_obj_read(cq, UVERBS_OBJECT_CQ, cmd.cq_handle, file->ucontext);
if (!cq)
return -EINVAL;
diff --git a/drivers/infiniband/core/uverbs_ioctl.c b/drivers/infiniband/core/uverbs_ioctl.c
index 339b85145044..8c93970dc8f1 100644
--- a/drivers/infiniband/core/uverbs_ioctl.c
+++ b/drivers/infiniband/core/uverbs_ioctl.c
@@ -35,6 +35,17 @@
#include "rdma_core.h"
#include "uverbs.h"
+static bool uverbs_is_attr_cleared(const struct ib_uverbs_attr *uattr,
+ u16 len)
+{
+ if (uattr->len > sizeof(((struct ib_uverbs_attr *)0)->data))
+ return ib_is_buffer_cleared(u64_to_user_ptr(uattr->data) + len,
+ uattr->len - len);
+
+ return !memchr_inv((const void *)&uattr->data + len,
+ 0, uattr->len - len);
+}
+
static int uverbs_process_attr(struct ib_device *ibdev,
struct ib_ucontext *ucontext,
const struct ib_uverbs_attr *uattr,
@@ -44,14 +55,12 @@ static int uverbs_process_attr(struct ib_device *ibdev,
struct ib_uverbs_attr __user *uattr_ptr)
{
const struct uverbs_attr_spec *spec;
+ const struct uverbs_attr_spec *val_spec;
struct uverbs_attr *e;
const struct uverbs_object_spec *object;
struct uverbs_obj_attr *o_attr;
struct uverbs_attr *elements = attr_bundle_h->attrs;
- if (uattr->reserved)
- return -EINVAL;
-
if (attr_id >= attr_spec_bucket->num_attrs) {
if (uattr->flags & UVERBS_ATTR_F_MANDATORY)
return -EINVAL;
@@ -63,15 +72,46 @@ static int uverbs_process_attr(struct ib_device *ibdev,
return -EINVAL;
spec = &attr_spec_bucket->attrs[attr_id];
+ val_spec = spec;
e = &elements[attr_id];
e->uattr = uattr_ptr;
switch (spec->type) {
+ case UVERBS_ATTR_TYPE_ENUM_IN:
+ if (uattr->attr_data.enum_data.elem_id >= spec->enum_def.num_elems)
+ return -EOPNOTSUPP;
+
+ if (uattr->attr_data.enum_data.reserved)
+ return -EINVAL;
+
+ val_spec = &spec->enum_def.ids[uattr->attr_data.enum_data.elem_id];
+
+ /* Currently we only support PTR_IN based enums */
+ if (val_spec->type != UVERBS_ATTR_TYPE_PTR_IN)
+ return -EOPNOTSUPP;
+
+ e->ptr_attr.enum_id = uattr->attr_data.enum_data.elem_id;
+ /* fall through */
case UVERBS_ATTR_TYPE_PTR_IN:
+ /* Ensure that any data provided by userspace beyond the known
+ * struct is zero. Userspace that knows how to use some future
+ * longer struct will fail here if used with an old kernel and
+ * non-zero content, making ABI compat/discovery simpler.
+ */
+ if (uattr->len > val_spec->ptr.len &&
+ val_spec->flags & UVERBS_ATTR_SPEC_F_MIN_SZ_OR_ZERO &&
+ !uverbs_is_attr_cleared(uattr, val_spec->ptr.len))
+ return -EOPNOTSUPP;
+
+ /* fall through */
case UVERBS_ATTR_TYPE_PTR_OUT:
- if (uattr->len < spec->len ||
- (!(spec->flags & UVERBS_ATTR_SPEC_F_MIN_SZ) &&
- uattr->len > spec->len))
+ if (uattr->len < val_spec->ptr.min_len ||
+ (!(val_spec->flags & UVERBS_ATTR_SPEC_F_MIN_SZ_OR_ZERO) &&
+ uattr->len > val_spec->ptr.len))
+ return -EINVAL;
+
+ if (spec->type != UVERBS_ATTR_TYPE_ENUM_IN &&
+ uattr->attr_data.reserved)
return -EINVAL;
e->ptr_attr.data = uattr->data;
@@ -84,6 +124,9 @@ static int uverbs_process_attr(struct ib_device *ibdev,
return -EINVAL;
/* fall through */
case UVERBS_ATTR_TYPE_FD:
+ if (uattr->attr_data.reserved)
+ return -EINVAL;
+
if (uattr->len != 0 || !ucontext || uattr->data > INT_MAX)
return -EINVAL;
@@ -246,6 +289,9 @@ static long ib_uverbs_cmd_verbs(struct ib_device *ib_dev,
size_t ctx_size;
uintptr_t data[UVERBS_OPTIMIZE_USING_STACK_SZ / sizeof(uintptr_t)];
+ if (hdr->driver_id != ib_dev->driver_id)
+ return -EINVAL;
+
object_spec = uverbs_get_object(ib_dev, hdr->object_id);
if (!object_spec)
return -EPROTONOSUPPORT;
@@ -350,7 +396,7 @@ long ib_uverbs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
goto out;
}
- if (hdr.reserved) {
+ if (hdr.reserved1 || hdr.reserved2) {
err = -EPROTONOSUPPORT;
goto out;
}
diff --git a/drivers/infiniband/core/uverbs_ioctl_merge.c b/drivers/infiniband/core/uverbs_ioctl_merge.c
index 62e1eb1d2a28..0f88a1919d51 100644
--- a/drivers/infiniband/core/uverbs_ioctl_merge.c
+++ b/drivers/infiniband/core/uverbs_ioctl_merge.c
@@ -379,7 +379,7 @@ static struct uverbs_method_spec *build_method_with_attrs(const struct uverbs_me
"ib_uverbs: Tried to merge attr (%d) but it's an object with new/destroy access but isn't mandatory\n",
min_id) ||
WARN(IS_ATTR_OBJECT(attr) &&
- attr->flags & UVERBS_ATTR_SPEC_F_MIN_SZ,
+ attr->flags & UVERBS_ATTR_SPEC_F_MIN_SZ_OR_ZERO,
"ib_uverbs: Tried to merge attr (%d) but it's an object with min_sz flag\n",
min_id)) {
res = -EINVAL;
diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c
index b1ca223aa380..4445d8ee9314 100644
--- a/drivers/infiniband/core/uverbs_main.c
+++ b/drivers/infiniband/core/uverbs_main.c
@@ -468,7 +468,7 @@ void ib_uverbs_comp_handler(struct ib_cq *cq, void *cq_context)
return;
}
- entry = kmalloc(sizeof *entry, GFP_ATOMIC);
+ entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
if (!entry) {
spin_unlock_irqrestore(&ev_queue->lock, flags);
return;
@@ -501,7 +501,7 @@ static void ib_uverbs_async_handler(struct ib_uverbs_file *file,
return;
}
- entry = kmalloc(sizeof *entry, GFP_ATOMIC);
+ entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
if (!entry) {
spin_unlock_irqrestore(&file->async_file->ev_queue.lock, flags);
return;
@@ -635,39 +635,87 @@ err_put_refs:
return filp;
}
-static int verify_command_mask(struct ib_device *ib_dev, __u32 command)
+static bool verify_command_mask(struct ib_device *ib_dev,
+ u32 command, bool extended)
{
- u64 mask;
+ if (!extended)
+ return ib_dev->uverbs_cmd_mask & BIT_ULL(command);
- if (command <= IB_USER_VERBS_CMD_OPEN_QP)
- mask = ib_dev->uverbs_cmd_mask;
- else
- mask = ib_dev->uverbs_ex_cmd_mask;
-
- if (mask & ((u64)1 << command))
- return 0;
-
- return -1;
+ return ib_dev->uverbs_ex_cmd_mask & BIT_ULL(command);
}
static bool verify_command_idx(u32 command, bool extended)
{
if (extended)
- return command < ARRAY_SIZE(uverbs_ex_cmd_table);
+ return command < ARRAY_SIZE(uverbs_ex_cmd_table) &&
+ uverbs_ex_cmd_table[command];
+
+ return command < ARRAY_SIZE(uverbs_cmd_table) &&
+ uverbs_cmd_table[command];
+}
+
+static ssize_t process_hdr(struct ib_uverbs_cmd_hdr *hdr,
+ u32 *command, bool *extended)
+{
+ if (hdr->command & ~(u32)(IB_USER_VERBS_CMD_FLAG_EXTENDED |
+ IB_USER_VERBS_CMD_COMMAND_MASK))
+ return -EINVAL;
+
+ *command = hdr->command & IB_USER_VERBS_CMD_COMMAND_MASK;
+ *extended = hdr->command & IB_USER_VERBS_CMD_FLAG_EXTENDED;
+
+ if (!verify_command_idx(*command, *extended))
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static ssize_t verify_hdr(struct ib_uverbs_cmd_hdr *hdr,
+ struct ib_uverbs_ex_cmd_hdr *ex_hdr,
+ size_t count, bool extended)
+{
+ if (extended) {
+ count -= sizeof(*hdr) + sizeof(*ex_hdr);
+
+ if ((hdr->in_words + ex_hdr->provider_in_words) * 8 != count)
+ return -EINVAL;
+
+ if (ex_hdr->cmd_hdr_reserved)
+ return -EINVAL;
- return command < ARRAY_SIZE(uverbs_cmd_table);
+ if (ex_hdr->response) {
+ if (!hdr->out_words && !ex_hdr->provider_out_words)
+ return -EINVAL;
+
+ if (!access_ok(VERIFY_WRITE,
+ u64_to_user_ptr(ex_hdr->response),
+ (hdr->out_words + ex_hdr->provider_out_words) * 8))
+ return -EFAULT;
+ } else {
+ if (hdr->out_words || ex_hdr->provider_out_words)
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+
+ /* not extended command */
+ if (hdr->in_words * 4 != count)
+ return -EINVAL;
+
+ return 0;
}
static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf,
size_t count, loff_t *pos)
{
struct ib_uverbs_file *file = filp->private_data;
+ struct ib_uverbs_ex_cmd_hdr ex_hdr;
struct ib_device *ib_dev;
struct ib_uverbs_cmd_hdr hdr;
- bool extended_command;
- __u32 command;
- __u32 flags;
+ bool extended;
int srcu_key;
+ u32 command;
ssize_t ret;
if (!ib_safe_file_access(filp)) {
@@ -676,12 +724,31 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf,
return -EACCES;
}
- if (count < sizeof hdr)
+ if (count < sizeof(hdr))
return -EINVAL;
- if (copy_from_user(&hdr, buf, sizeof hdr))
+ if (copy_from_user(&hdr, buf, sizeof(hdr)))
return -EFAULT;
+ ret = process_hdr(&hdr, &command, &extended);
+ if (ret)
+ return ret;
+
+ if (!file->ucontext &&
+ (command != IB_USER_VERBS_CMD_GET_CONTEXT || extended))
+ return -EINVAL;
+
+ if (extended) {
+ if (count < (sizeof(hdr) + sizeof(ex_hdr)))
+ return -EINVAL;
+ if (copy_from_user(&ex_hdr, buf + sizeof(hdr), sizeof(ex_hdr)))
+ return -EFAULT;
+ }
+
+ ret = verify_hdr(&hdr, &ex_hdr, count, extended);
+ if (ret)
+ return ret;
+
srcu_key = srcu_read_lock(&file->device->disassociate_srcu);
ib_dev = srcu_dereference(file->device->ib_dev,
&file->device->disassociate_srcu);
@@ -690,106 +757,22 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf,
goto out;
}
- if (hdr.command & ~(__u32)(IB_USER_VERBS_CMD_FLAGS_MASK |
- IB_USER_VERBS_CMD_COMMAND_MASK)) {
- ret = -EINVAL;
- goto out;
- }
-
- command = hdr.command & IB_USER_VERBS_CMD_COMMAND_MASK;
- flags = (hdr.command &
- IB_USER_VERBS_CMD_FLAGS_MASK) >> IB_USER_VERBS_CMD_FLAGS_SHIFT;
-
- extended_command = flags & IB_USER_VERBS_CMD_FLAG_EXTENDED;
- if (!verify_command_idx(command, extended_command)) {
- ret = -EINVAL;
- goto out;
- }
-
- if (verify_command_mask(ib_dev, command)) {
+ if (!verify_command_mask(ib_dev, command, extended)) {
ret = -EOPNOTSUPP;
goto out;
}
- if (!file->ucontext &&
- command != IB_USER_VERBS_CMD_GET_CONTEXT) {
- ret = -EINVAL;
- goto out;
- }
-
- if (!flags) {
- if (!uverbs_cmd_table[command]) {
- ret = -EINVAL;
- goto out;
- }
-
- if (hdr.in_words * 4 != count) {
- ret = -EINVAL;
- goto out;
- }
+ buf += sizeof(hdr);
- ret = uverbs_cmd_table[command](file, ib_dev,
- buf + sizeof(hdr),
- hdr.in_words * 4,
- hdr.out_words * 4);
-
- } else if (flags == IB_USER_VERBS_CMD_FLAG_EXTENDED) {
- struct ib_uverbs_ex_cmd_hdr ex_hdr;
+ if (!extended) {
+ ret = uverbs_cmd_table[command](file, ib_dev, buf,
+ hdr.in_words * 4,
+ hdr.out_words * 4);
+ } else {
struct ib_udata ucore;
struct ib_udata uhw;
- size_t written_count = count;
- if (!uverbs_ex_cmd_table[command]) {
- ret = -ENOSYS;
- goto out;
- }
-
- if (!file->ucontext) {
- ret = -EINVAL;
- goto out;
- }
-
- if (count < (sizeof(hdr) + sizeof(ex_hdr))) {
- ret = -EINVAL;
- goto out;
- }
-
- if (copy_from_user(&ex_hdr, buf + sizeof(hdr), sizeof(ex_hdr))) {
- ret = -EFAULT;
- goto out;
- }
-
- count -= sizeof(hdr) + sizeof(ex_hdr);
- buf += sizeof(hdr) + sizeof(ex_hdr);
-
- if ((hdr.in_words + ex_hdr.provider_in_words) * 8 != count) {
- ret = -EINVAL;
- goto out;
- }
-
- if (ex_hdr.cmd_hdr_reserved) {
- ret = -EINVAL;
- goto out;
- }
-
- if (ex_hdr.response) {
- if (!hdr.out_words && !ex_hdr.provider_out_words) {
- ret = -EINVAL;
- goto out;
- }
-
- if (!access_ok(VERIFY_WRITE,
- u64_to_user_ptr(ex_hdr.response),
- (hdr.out_words + ex_hdr.provider_out_words) * 8)) {
- ret = -EFAULT;
- goto out;
- }
- } else {
- if (hdr.out_words || ex_hdr.provider_out_words) {
- ret = -EINVAL;
- goto out;
- }
- }
+ buf += sizeof(ex_hdr);
ib_uverbs_init_udata_buf_or_null(&ucore, buf,
u64_to_user_ptr(ex_hdr.response),
@@ -802,10 +785,7 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf,
ex_hdr.provider_out_words * 8);
ret = uverbs_ex_cmd_table[command](file, ib_dev, &ucore, &uhw);
- if (!ret)
- ret = written_count;
- } else {
- ret = -ENOSYS;
+ ret = (ret) ? : count;
}
out:
@@ -953,10 +933,8 @@ static const struct file_operations uverbs_fops = {
.open = ib_uverbs_open,
.release = ib_uverbs_close,
.llseek = no_llseek,
-#if IS_ENABLED(CONFIG_INFINIBAND_EXP_USER_ACCESS)
.unlocked_ioctl = ib_uverbs_ioctl,
.compat_ioctl = ib_uverbs_ioctl,
-#endif
};
static const struct file_operations uverbs_mmap_fops = {
@@ -966,10 +944,8 @@ static const struct file_operations uverbs_mmap_fops = {
.open = ib_uverbs_open,
.release = ib_uverbs_close,
.llseek = no_llseek,
-#if IS_ENABLED(CONFIG_INFINIBAND_EXP_USER_ACCESS)
.unlocked_ioctl = ib_uverbs_ioctl,
.compat_ioctl = ib_uverbs_ioctl,
-#endif
};
static struct ib_client uverbs_client = {
@@ -1032,7 +1008,7 @@ static void ib_uverbs_add_one(struct ib_device *device)
if (!device->alloc_ucontext)
return;
- uverbs_dev = kzalloc(sizeof *uverbs_dev, GFP_KERNEL);
+ uverbs_dev = kzalloc(sizeof(*uverbs_dev), GFP_KERNEL);
if (!uverbs_dev)
return;
diff --git a/drivers/infiniband/core/uverbs_std_types.c b/drivers/infiniband/core/uverbs_std_types.c
index df1360e6774f..569f48bd821e 100644
--- a/drivers/infiniband/core/uverbs_std_types.c
+++ b/drivers/infiniband/core/uverbs_std_types.c
@@ -48,7 +48,16 @@ static int uverbs_free_ah(struct ib_uobject *uobject,
static int uverbs_free_flow(struct ib_uobject *uobject,
enum rdma_remove_reason why)
{
- return ib_destroy_flow((struct ib_flow *)uobject->object);
+ int ret;
+ struct ib_flow *flow = (struct ib_flow *)uobject->object;
+ struct ib_uflow_object *uflow =
+ container_of(uobject, struct ib_uflow_object, uobject);
+
+ ret = ib_destroy_flow(flow);
+ if (!ret)
+ ib_uverbs_flow_resources_free(uflow->resources);
+
+ return ret;
}
static int uverbs_free_mw(struct ib_uobject *uobject,
@@ -135,31 +144,6 @@ static int uverbs_free_srq(struct ib_uobject *uobject,
return ret;
}
-static int uverbs_free_cq(struct ib_uobject *uobject,
- enum rdma_remove_reason why)
-{
- struct ib_cq *cq = uobject->object;
- struct ib_uverbs_event_queue *ev_queue = cq->cq_context;
- struct ib_ucq_object *ucq =
- container_of(uobject, struct ib_ucq_object, uobject);
- int ret;
-
- ret = ib_destroy_cq(cq);
- if (!ret || why != RDMA_REMOVE_DESTROY)
- ib_uverbs_release_ucq(uobject->context->ufile, ev_queue ?
- container_of(ev_queue,
- struct ib_uverbs_completion_event_file,
- ev_queue) : NULL,
- ucq);
- return ret;
-}
-
-static int uverbs_free_mr(struct ib_uobject *uobject,
- enum rdma_remove_reason why)
-{
- return ib_dereg_mr((struct ib_mr *)uobject->object);
-}
-
static int uverbs_free_xrcd(struct ib_uobject *uobject,
enum rdma_remove_reason why)
{
@@ -210,18 +194,26 @@ static int uverbs_hot_unplug_completion_event_file(struct ib_uobject_file *uobj_
return 0;
};
+int uverbs_destroy_def_handler(struct ib_device *ib_dev,
+ struct ib_uverbs_file *file,
+ struct uverbs_attr_bundle *attrs)
+{
+ return 0;
+}
+
/*
* This spec is used in order to pass information to the hardware driver in a
* legacy way. Every verb that could get driver specific data should get this
* spec.
*/
-static const struct uverbs_attr_def uverbs_uhw_compat_in =
- UVERBS_ATTR_PTR_IN_SZ(UVERBS_UHW_IN, 0, UA_FLAGS(UVERBS_ATTR_SPEC_F_MIN_SZ));
-static const struct uverbs_attr_def uverbs_uhw_compat_out =
- UVERBS_ATTR_PTR_OUT_SZ(UVERBS_UHW_OUT, 0, UA_FLAGS(UVERBS_ATTR_SPEC_F_MIN_SZ));
-
-static void create_udata(struct uverbs_attr_bundle *ctx,
- struct ib_udata *udata)
+const struct uverbs_attr_def uverbs_uhw_compat_in =
+ UVERBS_ATTR_PTR_IN_SZ(UVERBS_ATTR_UHW_IN, UVERBS_ATTR_SIZE(0, USHRT_MAX),
+ UA_FLAGS(UVERBS_ATTR_SPEC_F_MIN_SZ_OR_ZERO));
+const struct uverbs_attr_def uverbs_uhw_compat_out =
+ UVERBS_ATTR_PTR_OUT_SZ(UVERBS_ATTR_UHW_OUT, UVERBS_ATTR_SIZE(0, USHRT_MAX),
+ UA_FLAGS(UVERBS_ATTR_SPEC_F_MIN_SZ_OR_ZERO));
+
+void create_udata(struct uverbs_attr_bundle *ctx, struct ib_udata *udata)
{
/*
* This is for ease of conversion. The purpose is to convert all drivers
@@ -229,9 +221,9 @@ static void create_udata(struct uverbs_attr_bundle *ctx,
* Assume attr == 0 is input and attr == 1 is output.
*/
const struct uverbs_attr *uhw_in =
- uverbs_attr_get(ctx, UVERBS_UHW_IN);
+ uverbs_attr_get(ctx, UVERBS_ATTR_UHW_IN);
const struct uverbs_attr *uhw_out =
- uverbs_attr_get(ctx, UVERBS_UHW_OUT);
+ uverbs_attr_get(ctx, UVERBS_ATTR_UHW_OUT);
if (!IS_ERR(uhw_in)) {
udata->inlen = uhw_in->ptr_attr.len;
@@ -253,207 +245,67 @@ static void create_udata(struct uverbs_attr_bundle *ctx,
}
}
-static int uverbs_create_cq_handler(struct ib_device *ib_dev,
- struct ib_uverbs_file *file,
- struct uverbs_attr_bundle *attrs)
+DECLARE_UVERBS_NAMED_OBJECT(UVERBS_OBJECT_COMP_CHANNEL,
+ &UVERBS_TYPE_ALLOC_FD(0,
+ sizeof(struct ib_uverbs_completion_event_file),
+ uverbs_hot_unplug_completion_event_file,
+ &uverbs_event_fops,
+ "[infinibandevent]", O_RDONLY));
+
+DECLARE_UVERBS_NAMED_OBJECT(UVERBS_OBJECT_QP,
+ &UVERBS_TYPE_ALLOC_IDR_SZ(sizeof(struct ib_uqp_object), 0,
+ uverbs_free_qp));
+
+DECLARE_UVERBS_NAMED_OBJECT(UVERBS_OBJECT_MW,
+ &UVERBS_TYPE_ALLOC_IDR(0, uverbs_free_mw));
+
+DECLARE_UVERBS_NAMED_OBJECT(UVERBS_OBJECT_SRQ,
+ &UVERBS_TYPE_ALLOC_IDR_SZ(sizeof(struct ib_usrq_object), 0,
+ uverbs_free_srq));
+
+DECLARE_UVERBS_NAMED_OBJECT(UVERBS_OBJECT_AH,
+ &UVERBS_TYPE_ALLOC_IDR(0, uverbs_free_ah));
+
+DECLARE_UVERBS_NAMED_OBJECT(UVERBS_OBJECT_FLOW,
+ &UVERBS_TYPE_ALLOC_IDR_SZ(sizeof(struct ib_uflow_object),
+ 0, uverbs_free_flow));
+
+DECLARE_UVERBS_NAMED_OBJECT(UVERBS_OBJECT_WQ,
+ &UVERBS_TYPE_ALLOC_IDR_SZ(sizeof(struct ib_uwq_object), 0,
+ uverbs_free_wq));
+
+DECLARE_UVERBS_NAMED_OBJECT(UVERBS_OBJECT_RWQ_IND_TBL,
+ &UVERBS_TYPE_ALLOC_IDR(0, uverbs_free_rwq_ind_tbl));
+
+DECLARE_UVERBS_NAMED_OBJECT(UVERBS_OBJECT_XRCD,
+ &UVERBS_TYPE_ALLOC_IDR_SZ(sizeof(struct ib_uxrcd_object), 0,
+ uverbs_free_xrcd));
+
+DECLARE_UVERBS_NAMED_OBJECT(UVERBS_OBJECT_PD,
+ /* 2 is used in order to free the PD after MRs */
+ &UVERBS_TYPE_ALLOC_IDR(2, uverbs_free_pd));
+
+DECLARE_UVERBS_NAMED_OBJECT(UVERBS_OBJECT_DEVICE, NULL);
+
+static DECLARE_UVERBS_OBJECT_TREE(uverbs_default_objects,
+ &UVERBS_OBJECT(UVERBS_OBJECT_DEVICE),
+ &UVERBS_OBJECT(UVERBS_OBJECT_PD),
+ &UVERBS_OBJECT(UVERBS_OBJECT_MR),
+ &UVERBS_OBJECT(UVERBS_OBJECT_COMP_CHANNEL),
+ &UVERBS_OBJECT(UVERBS_OBJECT_CQ),
+ &UVERBS_OBJECT(UVERBS_OBJECT_QP),
+ &UVERBS_OBJECT(UVERBS_OBJECT_AH),
+ &UVERBS_OBJECT(UVERBS_OBJECT_MW),
+ &UVERBS_OBJECT(UVERBS_OBJECT_SRQ),
+ &UVERBS_OBJECT(UVERBS_OBJECT_FLOW),
+ &UVERBS_OBJECT(UVERBS_OBJECT_WQ),
+ &UVERBS_OBJECT(UVERBS_OBJECT_RWQ_IND_TBL),
+ &UVERBS_OBJECT(UVERBS_OBJECT_XRCD),
+ &UVERBS_OBJECT(UVERBS_OBJECT_FLOW_ACTION),
+ &UVERBS_OBJECT(UVERBS_OBJECT_DM));
+
+const struct uverbs_object_tree_def *uverbs_default_get_objects(void)
{
- struct ib_ucontext *ucontext = file->ucontext;
- struct ib_ucq_object *obj;
- struct ib_udata uhw;
- int ret;
- u64 user_handle;
- struct ib_cq_init_attr attr = {};
- struct ib_cq *cq;
- struct ib_uverbs_completion_event_file *ev_file = NULL;
- const struct uverbs_attr *ev_file_attr;
- struct ib_uobject *ev_file_uobj;
-
- if (!(ib_dev->uverbs_cmd_mask & 1ULL << IB_USER_VERBS_CMD_CREATE_CQ))
- return -EOPNOTSUPP;
-
- ret = uverbs_copy_from(&attr.comp_vector, attrs, CREATE_CQ_COMP_VECTOR);
- if (!ret)
- ret = uverbs_copy_from(&attr.cqe, attrs, CREATE_CQ_CQE);
- if (!ret)
- ret = uverbs_copy_from(&user_handle, attrs, CREATE_CQ_USER_HANDLE);
- if (ret)
- return ret;
-
- /* Optional param, if it doesn't exist, we get -ENOENT and skip it */
- if (uverbs_copy_from(&attr.flags, attrs, CREATE_CQ_FLAGS) == -EFAULT)
- return -EFAULT;
-
- ev_file_attr = uverbs_attr_get(attrs, CREATE_CQ_COMP_CHANNEL);
- if (!IS_ERR(ev_file_attr)) {
- ev_file_uobj = ev_file_attr->obj_attr.uobject;
-
- ev_file = container_of(ev_file_uobj,
- struct ib_uverbs_completion_event_file,
- uobj_file.uobj);
- uverbs_uobject_get(ev_file_uobj);
- }
-
- if (attr.comp_vector >= ucontext->ufile->device->num_comp_vectors) {
- ret = -EINVAL;
- goto err_event_file;
- }
-
- obj = container_of(uverbs_attr_get(attrs, CREATE_CQ_HANDLE)->obj_attr.uobject,
- typeof(*obj), uobject);
- obj->uverbs_file = ucontext->ufile;
- obj->comp_events_reported = 0;
- obj->async_events_reported = 0;
- INIT_LIST_HEAD(&obj->comp_list);
- INIT_LIST_HEAD(&obj->async_list);
-
- /* Temporary, only until drivers get the new uverbs_attr_bundle */
- create_udata(attrs, &uhw);
-
- cq = ib_dev->create_cq(ib_dev, &attr, ucontext, &uhw);
- if (IS_ERR(cq)) {
- ret = PTR_ERR(cq);
- goto err_event_file;
- }
-
- cq->device = ib_dev;
- cq->uobject = &obj->uobject;
- cq->comp_handler = ib_uverbs_comp_handler;
- cq->event_handler = ib_uverbs_cq_event_handler;
- cq->cq_context = ev_file ? &ev_file->ev_queue : NULL;
- obj->uobject.object = cq;
- obj->uobject.user_handle = user_handle;
- atomic_set(&cq->usecnt, 0);
- cq->res.type = RDMA_RESTRACK_CQ;
- rdma_restrack_add(&cq->res);
-
- ret = uverbs_copy_to(attrs, CREATE_CQ_RESP_CQE, &cq->cqe,
- sizeof(cq->cqe));
- if (ret)
- goto err_cq;
-
- return 0;
-err_cq:
- ib_destroy_cq(cq);
-
-err_event_file:
- if (ev_file)
- uverbs_uobject_put(ev_file_uobj);
- return ret;
-};
-
-static DECLARE_UVERBS_METHOD(
- uverbs_method_cq_create, UVERBS_CQ_CREATE, uverbs_create_cq_handler,
- &UVERBS_ATTR_IDR(CREATE_CQ_HANDLE, UVERBS_OBJECT_CQ, UVERBS_ACCESS_NEW,
- UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
- &UVERBS_ATTR_PTR_IN(CREATE_CQ_CQE, u32,
- UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
- &UVERBS_ATTR_PTR_IN(CREATE_CQ_USER_HANDLE, u64,
- UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
- &UVERBS_ATTR_FD(CREATE_CQ_COMP_CHANNEL, UVERBS_OBJECT_COMP_CHANNEL,
- UVERBS_ACCESS_READ),
- &UVERBS_ATTR_PTR_IN(CREATE_CQ_COMP_VECTOR, u32,
- UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
- &UVERBS_ATTR_PTR_IN(CREATE_CQ_FLAGS, u32),
- &UVERBS_ATTR_PTR_OUT(CREATE_CQ_RESP_CQE, u32,
- UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
- &uverbs_uhw_compat_in, &uverbs_uhw_compat_out);
-
-static int uverbs_destroy_cq_handler(struct ib_device *ib_dev,
- struct ib_uverbs_file *file,
- struct uverbs_attr_bundle *attrs)
-{
- struct ib_uverbs_destroy_cq_resp resp;
- struct ib_uobject *uobj =
- uverbs_attr_get(attrs, DESTROY_CQ_HANDLE)->obj_attr.uobject;
- struct ib_ucq_object *obj = container_of(uobj, struct ib_ucq_object,
- uobject);
- int ret;
-
- if (!(ib_dev->uverbs_cmd_mask & 1ULL << IB_USER_VERBS_CMD_DESTROY_CQ))
- return -EOPNOTSUPP;
-
- ret = rdma_explicit_destroy(uobj);
- if (ret)
- return ret;
-
- resp.comp_events_reported = obj->comp_events_reported;
- resp.async_events_reported = obj->async_events_reported;
-
- return uverbs_copy_to(attrs, DESTROY_CQ_RESP, &resp, sizeof(resp));
+ return &uverbs_default_objects;
}
-
-static DECLARE_UVERBS_METHOD(
- uverbs_method_cq_destroy, UVERBS_CQ_DESTROY, uverbs_destroy_cq_handler,
- &UVERBS_ATTR_IDR(DESTROY_CQ_HANDLE, UVERBS_OBJECT_CQ,
- UVERBS_ACCESS_DESTROY,
- UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
- &UVERBS_ATTR_PTR_OUT(DESTROY_CQ_RESP, struct ib_uverbs_destroy_cq_resp,
- UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)));
-
-DECLARE_UVERBS_OBJECT(uverbs_object_comp_channel,
- UVERBS_OBJECT_COMP_CHANNEL,
- &UVERBS_TYPE_ALLOC_FD(0,
- sizeof(struct ib_uverbs_completion_event_file),
- uverbs_hot_unplug_completion_event_file,
- &uverbs_event_fops,
- "[infinibandevent]", O_RDONLY));
-
-DECLARE_UVERBS_OBJECT(uverbs_object_cq, UVERBS_OBJECT_CQ,
- &UVERBS_TYPE_ALLOC_IDR_SZ(sizeof(struct ib_ucq_object), 0,
- uverbs_free_cq),
- &uverbs_method_cq_create,
- &uverbs_method_cq_destroy);
-
-DECLARE_UVERBS_OBJECT(uverbs_object_qp, UVERBS_OBJECT_QP,
- &UVERBS_TYPE_ALLOC_IDR_SZ(sizeof(struct ib_uqp_object), 0,
- uverbs_free_qp));
-
-DECLARE_UVERBS_OBJECT(uverbs_object_mw, UVERBS_OBJECT_MW,
- &UVERBS_TYPE_ALLOC_IDR(0, uverbs_free_mw));
-
-DECLARE_UVERBS_OBJECT(uverbs_object_mr, UVERBS_OBJECT_MR,
- /* 1 is used in order to free the MR after all the MWs */
- &UVERBS_TYPE_ALLOC_IDR(1, uverbs_free_mr));
-
-DECLARE_UVERBS_OBJECT(uverbs_object_srq, UVERBS_OBJECT_SRQ,
- &UVERBS_TYPE_ALLOC_IDR_SZ(sizeof(struct ib_usrq_object), 0,
- uverbs_free_srq));
-
-DECLARE_UVERBS_OBJECT(uverbs_object_ah, UVERBS_OBJECT_AH,
- &UVERBS_TYPE_ALLOC_IDR(0, uverbs_free_ah));
-
-DECLARE_UVERBS_OBJECT(uverbs_object_flow, UVERBS_OBJECT_FLOW,
- &UVERBS_TYPE_ALLOC_IDR(0, uverbs_free_flow));
-
-DECLARE_UVERBS_OBJECT(uverbs_object_wq, UVERBS_OBJECT_WQ,
- &UVERBS_TYPE_ALLOC_IDR_SZ(sizeof(struct ib_uwq_object), 0,
- uverbs_free_wq));
-
-DECLARE_UVERBS_OBJECT(uverbs_object_rwq_ind_table,
- UVERBS_OBJECT_RWQ_IND_TBL,
- &UVERBS_TYPE_ALLOC_IDR(0, uverbs_free_rwq_ind_tbl));
-
-DECLARE_UVERBS_OBJECT(uverbs_object_xrcd, UVERBS_OBJECT_XRCD,
- &UVERBS_TYPE_ALLOC_IDR_SZ(sizeof(struct ib_uxrcd_object), 0,
- uverbs_free_xrcd));
-
-DECLARE_UVERBS_OBJECT(uverbs_object_pd, UVERBS_OBJECT_PD,
- /* 2 is used in order to free the PD after MRs */
- &UVERBS_TYPE_ALLOC_IDR(2, uverbs_free_pd));
-
-DECLARE_UVERBS_OBJECT(uverbs_object_device, UVERBS_OBJECT_DEVICE, NULL);
-
-DECLARE_UVERBS_OBJECT_TREE(uverbs_default_objects,
- &uverbs_object_device,
- &uverbs_object_pd,
- &uverbs_object_mr,
- &uverbs_object_comp_channel,
- &uverbs_object_cq,
- &uverbs_object_qp,
- &uverbs_object_ah,
- &uverbs_object_mw,
- &uverbs_object_srq,
- &uverbs_object_flow,
- &uverbs_object_wq,
- &uverbs_object_rwq_ind_table,
- &uverbs_object_xrcd);
+EXPORT_SYMBOL_GPL(uverbs_default_get_objects);
diff --git a/drivers/infiniband/core/uverbs_std_types_cq.c b/drivers/infiniband/core/uverbs_std_types_cq.c
new file mode 100644
index 000000000000..b0dbae9dd0d7
--- /dev/null
+++ b/drivers/infiniband/core/uverbs_std_types_cq.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2017, Mellanox Technologies inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 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
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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 <rdma/uverbs_std_types.h>
+#include "rdma_core.h"
+#include "uverbs.h"
+
+static int uverbs_free_cq(struct ib_uobject *uobject,
+ enum rdma_remove_reason why)
+{
+ struct ib_cq *cq = uobject->object;
+ struct ib_uverbs_event_queue *ev_queue = cq->cq_context;
+ struct ib_ucq_object *ucq =
+ container_of(uobject, struct ib_ucq_object, uobject);
+ int ret;
+
+ ret = ib_destroy_cq(cq);
+ if (!ret || why != RDMA_REMOVE_DESTROY)
+ ib_uverbs_release_ucq(uobject->context->ufile, ev_queue ?
+ container_of(ev_queue,
+ struct ib_uverbs_completion_event_file,
+ ev_queue) : NULL,
+ ucq);
+ return ret;
+}
+
+static int UVERBS_HANDLER(UVERBS_METHOD_CQ_CREATE)(struct ib_device *ib_dev,
+ struct ib_uverbs_file *file,
+ struct uverbs_attr_bundle *attrs)
+{
+ struct ib_ucontext *ucontext = file->ucontext;
+ struct ib_ucq_object *obj;
+ struct ib_udata uhw;
+ int ret;
+ u64 user_handle;
+ struct ib_cq_init_attr attr = {};
+ struct ib_cq *cq;
+ struct ib_uverbs_completion_event_file *ev_file = NULL;
+ const struct uverbs_attr *ev_file_attr;
+ struct ib_uobject *ev_file_uobj;
+
+ if (!(ib_dev->uverbs_cmd_mask & 1ULL << IB_USER_VERBS_CMD_CREATE_CQ))
+ return -EOPNOTSUPP;
+
+ ret = uverbs_copy_from(&attr.comp_vector, attrs,
+ UVERBS_ATTR_CREATE_CQ_COMP_VECTOR);
+ if (!ret)
+ ret = uverbs_copy_from(&attr.cqe, attrs,
+ UVERBS_ATTR_CREATE_CQ_CQE);
+ if (!ret)
+ ret = uverbs_copy_from(&user_handle, attrs,
+ UVERBS_ATTR_CREATE_CQ_USER_HANDLE);
+ if (ret)
+ return ret;
+
+ /* Optional param, if it doesn't exist, we get -ENOENT and skip it */
+ if (IS_UVERBS_COPY_ERR(uverbs_copy_from(&attr.flags, attrs,
+ UVERBS_ATTR_CREATE_CQ_FLAGS)))
+ return -EFAULT;
+
+ ev_file_attr = uverbs_attr_get(attrs, UVERBS_ATTR_CREATE_CQ_COMP_CHANNEL);
+ if (!IS_ERR(ev_file_attr)) {
+ ev_file_uobj = ev_file_attr->obj_attr.uobject;
+
+ ev_file = container_of(ev_file_uobj,
+ struct ib_uverbs_completion_event_file,
+ uobj_file.uobj);
+ uverbs_uobject_get(ev_file_uobj);
+ }
+
+ if (attr.comp_vector >= ucontext->ufile->device->num_comp_vectors) {
+ ret = -EINVAL;
+ goto err_event_file;
+ }
+
+ obj = container_of(uverbs_attr_get(attrs,
+ UVERBS_ATTR_CREATE_CQ_HANDLE)->obj_attr.uobject,
+ typeof(*obj), uobject);
+ obj->uverbs_file = ucontext->ufile;
+ obj->comp_events_reported = 0;
+ obj->async_events_reported = 0;
+ INIT_LIST_HEAD(&obj->comp_list);
+ INIT_LIST_HEAD(&obj->async_list);
+
+ /* Temporary, only until drivers get the new uverbs_attr_bundle */
+ create_udata(attrs, &uhw);
+
+ cq = ib_dev->create_cq(ib_dev, &attr, ucontext, &uhw);
+ if (IS_ERR(cq)) {
+ ret = PTR_ERR(cq);
+ goto err_event_file;
+ }
+
+ cq->device = ib_dev;
+ cq->uobject = &obj->uobject;
+ cq->comp_handler = ib_uverbs_comp_handler;
+ cq->event_handler = ib_uverbs_cq_event_handler;
+ cq->cq_context = ev_file ? &ev_file->ev_queue : NULL;
+ obj->uobject.object = cq;
+ obj->uobject.user_handle = user_handle;
+ atomic_set(&cq->usecnt, 0);
+ cq->res.type = RDMA_RESTRACK_CQ;
+ rdma_restrack_add(&cq->res);
+
+ ret = uverbs_copy_to(attrs, UVERBS_ATTR_CREATE_CQ_RESP_CQE, &cq->cqe,
+ sizeof(cq->cqe));
+ if (ret)
+ goto err_cq;
+
+ return 0;
+err_cq:
+ ib_destroy_cq(cq);
+
+err_event_file:
+ if (ev_file)
+ uverbs_uobject_put(ev_file_uobj);
+ return ret;
+};
+
+static DECLARE_UVERBS_NAMED_METHOD(UVERBS_METHOD_CQ_CREATE,
+ &UVERBS_ATTR_IDR(UVERBS_ATTR_CREATE_CQ_HANDLE, UVERBS_OBJECT_CQ,
+ UVERBS_ACCESS_NEW,
+ UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
+ &UVERBS_ATTR_PTR_IN(UVERBS_ATTR_CREATE_CQ_CQE,
+ UVERBS_ATTR_TYPE(u32),
+ UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
+ &UVERBS_ATTR_PTR_IN(UVERBS_ATTR_CREATE_CQ_USER_HANDLE,
+ UVERBS_ATTR_TYPE(u64),
+ UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
+ &UVERBS_ATTR_FD(UVERBS_ATTR_CREATE_CQ_COMP_CHANNEL,
+ UVERBS_OBJECT_COMP_CHANNEL,
+ UVERBS_ACCESS_READ),
+ &UVERBS_ATTR_PTR_IN(UVERBS_ATTR_CREATE_CQ_COMP_VECTOR, UVERBS_ATTR_TYPE(u32),
+ UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
+ &UVERBS_ATTR_PTR_IN(UVERBS_ATTR_CREATE_CQ_FLAGS, UVERBS_ATTR_TYPE(u32)),
+ &UVERBS_ATTR_PTR_OUT(UVERBS_ATTR_CREATE_CQ_RESP_CQE, UVERBS_ATTR_TYPE(u32),
+ UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
+ &uverbs_uhw_compat_in, &uverbs_uhw_compat_out);
+
+static int UVERBS_HANDLER(UVERBS_METHOD_CQ_DESTROY)(struct ib_device *ib_dev,
+ struct ib_uverbs_file *file,
+ struct uverbs_attr_bundle *attrs)
+{
+ struct ib_uverbs_destroy_cq_resp resp;
+ struct ib_uobject *uobj =
+ uverbs_attr_get(attrs, UVERBS_ATTR_DESTROY_CQ_HANDLE)->obj_attr.uobject;
+ struct ib_ucq_object *obj = container_of(uobj, struct ib_ucq_object,
+ uobject);
+ int ret;
+
+ if (!(ib_dev->uverbs_cmd_mask & 1ULL << IB_USER_VERBS_CMD_DESTROY_CQ))
+ return -EOPNOTSUPP;
+
+ ret = rdma_explicit_destroy(uobj);
+ if (ret)
+ return ret;
+
+ resp.comp_events_reported = obj->comp_events_reported;
+ resp.async_events_reported = obj->async_events_reported;
+
+ return uverbs_copy_to(attrs, UVERBS_ATTR_DESTROY_CQ_RESP, &resp,
+ sizeof(resp));
+}
+
+static DECLARE_UVERBS_NAMED_METHOD(UVERBS_METHOD_CQ_DESTROY,
+ &UVERBS_ATTR_IDR(UVERBS_ATTR_DESTROY_CQ_HANDLE, UVERBS_OBJECT_CQ,
+ UVERBS_ACCESS_DESTROY,
+ UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
+ &UVERBS_ATTR_PTR_OUT(UVERBS_ATTR_DESTROY_CQ_RESP,
+ UVERBS_ATTR_TYPE(struct ib_uverbs_destroy_cq_resp),
+ UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)));
+
+DECLARE_UVERBS_NAMED_OBJECT(UVERBS_OBJECT_CQ,
+ &UVERBS_TYPE_ALLOC_IDR_SZ(sizeof(struct ib_ucq_object), 0,
+ uverbs_free_cq),
+#if IS_ENABLED(CONFIG_INFINIBAND_EXP_LEGACY_VERBS_NEW_UAPI)
+ &UVERBS_METHOD(UVERBS_METHOD_CQ_CREATE),
+ &UVERBS_METHOD(UVERBS_METHOD_CQ_DESTROY)
+#endif
+ );
+
diff --git a/drivers/infiniband/core/uverbs_std_types_dm.c b/drivers/infiniband/core/uverbs_std_types_dm.c
new file mode 100644
index 000000000000..8b681575b615
--- /dev/null
+++ b/drivers/infiniband/core/uverbs_std_types_dm.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2018, Mellanox Technologies inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 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
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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 "uverbs.h"
+#include <rdma/uverbs_std_types.h>
+
+static int uverbs_free_dm(struct ib_uobject *uobject,
+ enum rdma_remove_reason why)
+{
+ struct ib_dm *dm = uobject->object;
+
+ if (why == RDMA_REMOVE_DESTROY && atomic_read(&dm->usecnt))
+ return -EBUSY;
+
+ return dm->device->dealloc_dm(dm);
+}
+
+static int UVERBS_HANDLER(UVERBS_METHOD_DM_ALLOC)(struct ib_device *ib_dev,
+ struct ib_uverbs_file *file,
+ struct uverbs_attr_bundle *attrs)
+{
+ struct ib_ucontext *ucontext = file->ucontext;
+ struct ib_dm_alloc_attr attr = {};
+ struct ib_uobject *uobj;
+ struct ib_dm *dm;
+ int ret;
+
+ if (!ib_dev->alloc_dm)
+ return -EOPNOTSUPP;
+
+ ret = uverbs_copy_from(&attr.length, attrs,
+ UVERBS_ATTR_ALLOC_DM_LENGTH);
+ if (ret)
+ return ret;
+
+ ret = uverbs_copy_from(&attr.alignment, attrs,
+ UVERBS_ATTR_ALLOC_DM_ALIGNMENT);
+ if (ret)
+ return ret;
+
+ uobj = uverbs_attr_get(attrs, UVERBS_ATTR_ALLOC_DM_HANDLE)->obj_attr.uobject;
+
+ dm = ib_dev->alloc_dm(ib_dev, ucontext, &attr, attrs);
+ if (IS_ERR(dm))
+ return PTR_ERR(dm);
+
+ dm->device = ib_dev;
+ dm->length = attr.length;
+ dm->uobject = uobj;
+ atomic_set(&dm->usecnt, 0);
+
+ uobj->object = dm;
+
+ return 0;
+}
+
+static DECLARE_UVERBS_NAMED_METHOD(UVERBS_METHOD_DM_ALLOC,
+ &UVERBS_ATTR_IDR(UVERBS_ATTR_ALLOC_DM_HANDLE, UVERBS_OBJECT_DM,
+ UVERBS_ACCESS_NEW,
+ UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
+ &UVERBS_ATTR_PTR_IN(UVERBS_ATTR_ALLOC_DM_LENGTH,
+ UVERBS_ATTR_TYPE(u64),
+ UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
+ &UVERBS_ATTR_PTR_IN(UVERBS_ATTR_ALLOC_DM_ALIGNMENT,
+ UVERBS_ATTR_TYPE(u32),
+ UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)));
+
+static DECLARE_UVERBS_NAMED_METHOD_WITH_HANDLER(UVERBS_METHOD_DM_FREE,
+ uverbs_destroy_def_handler,
+ &UVERBS_ATTR_IDR(UVERBS_ATTR_FREE_DM_HANDLE,
+ UVERBS_OBJECT_DM,
+ UVERBS_ACCESS_DESTROY,
+ UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)));
+
+DECLARE_UVERBS_NAMED_OBJECT(UVERBS_OBJECT_DM,
+ /* 1 is used in order to free the DM after MRs */
+ &UVERBS_TYPE_ALLOC_IDR(1, uverbs_free_dm),
+ &UVERBS_METHOD(UVERBS_METHOD_DM_ALLOC),
+ &UVERBS_METHOD(UVERBS_METHOD_DM_FREE));
diff --git a/drivers/infiniband/core/uverbs_std_types_flow_action.c b/drivers/infiniband/core/uverbs_std_types_flow_action.c
new file mode 100644
index 000000000000..cbcec3da12f6
--- /dev/null
+++ b/drivers/infiniband/core/uverbs_std_types_flow_action.c
@@ -0,0 +1,435 @@
+/*
+ * Copyright (c) 2018, Mellanox Technologies inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 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
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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 "uverbs.h"
+#include <rdma/uverbs_std_types.h>
+
+static int uverbs_free_flow_action(struct ib_uobject *uobject,
+ enum rdma_remove_reason why)
+{
+ struct ib_flow_action *action = uobject->object;
+
+ if (why == RDMA_REMOVE_DESTROY &&
+ atomic_read(&action->usecnt))
+ return -EBUSY;
+
+ return action->device->destroy_flow_action(action);
+}
+
+static u64 esp_flags_uverbs_to_verbs(struct uverbs_attr_bundle *attrs,
+ u32 flags, bool is_modify)
+{
+ u64 verbs_flags = flags;
+
+ if (uverbs_attr_is_valid(attrs, UVERBS_ATTR_FLOW_ACTION_ESP_ESN))
+ verbs_flags |= IB_FLOW_ACTION_ESP_FLAGS_ESN_TRIGGERED;
+
+ if (is_modify && uverbs_attr_is_valid(attrs,
+ UVERBS_ATTR_FLOW_ACTION_ESP_ATTRS))
+ verbs_flags |= IB_FLOW_ACTION_ESP_FLAGS_MOD_ESP_ATTRS;
+
+ return verbs_flags;
+};
+
+static int validate_flow_action_esp_keymat_aes_gcm(struct ib_flow_action_attrs_esp_keymats *keymat)
+{
+ struct ib_uverbs_flow_action_esp_keymat_aes_gcm *aes_gcm =
+ &keymat->keymat.aes_gcm;
+
+ if (aes_gcm->iv_algo > IB_UVERBS_FLOW_ACTION_IV_ALGO_SEQ)
+ return -EOPNOTSUPP;
+
+ if (aes_gcm->key_len != 32 &&
+ aes_gcm->key_len != 24 &&
+ aes_gcm->key_len != 16)
+ return -EINVAL;
+
+ if (aes_gcm->icv_len != 16 &&
+ aes_gcm->icv_len != 8 &&
+ aes_gcm->icv_len != 12)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int (* const flow_action_esp_keymat_validate[])(struct ib_flow_action_attrs_esp_keymats *keymat) = {
+ [IB_UVERBS_FLOW_ACTION_ESP_KEYMAT_AES_GCM] = validate_flow_action_esp_keymat_aes_gcm,
+};
+
+static int flow_action_esp_replay_none(struct ib_flow_action_attrs_esp_replays *replay,
+ bool is_modify)
+{
+ /* This is used in order to modify an esp flow action with an enabled
+ * replay protection to a disabled one. This is only supported via
+ * modify, as in create verb we can simply drop the REPLAY attribute and
+ * achieve the same thing.
+ */
+ return is_modify ? 0 : -EINVAL;
+}
+
+static int flow_action_esp_replay_def_ok(struct ib_flow_action_attrs_esp_replays *replay,
+ bool is_modify)
+{
+ /* Some replay protections could always be enabled without validating
+ * anything.
+ */
+ return 0;
+}
+
+static int (* const flow_action_esp_replay_validate[])(struct ib_flow_action_attrs_esp_replays *replay,
+ bool is_modify) = {
+ [IB_UVERBS_FLOW_ACTION_ESP_REPLAY_NONE] = flow_action_esp_replay_none,
+ [IB_UVERBS_FLOW_ACTION_ESP_REPLAY_BMP] = flow_action_esp_replay_def_ok,
+};
+
+static int parse_esp_ip(enum ib_flow_spec_type proto,
+ const void __user *val_ptr,
+ size_t len, union ib_flow_spec *out)
+{
+ int ret;
+ const struct ib_uverbs_flow_ipv4_filter ipv4 = {
+ .src_ip = cpu_to_be32(0xffffffffUL),
+ .dst_ip = cpu_to_be32(0xffffffffUL),
+ .proto = 0xff,
+ .tos = 0xff,
+ .ttl = 0xff,
+ .flags = 0xff,
+ };
+ const struct ib_uverbs_flow_ipv6_filter ipv6 = {
+ .src_ip = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ .dst_ip = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ .flow_label = cpu_to_be32(0xffffffffUL),
+ .next_hdr = 0xff,
+ .traffic_class = 0xff,
+ .hop_limit = 0xff,
+ };
+ union {
+ struct ib_uverbs_flow_ipv4_filter ipv4;
+ struct ib_uverbs_flow_ipv6_filter ipv6;
+ } user_val = {};
+ const void *user_pmask;
+ size_t val_len;
+
+ /* If the flow IPv4/IPv6 flow specifications are extended, the mask
+ * should be changed as well.
+ */
+ BUILD_BUG_ON(offsetof(struct ib_uverbs_flow_ipv4_filter, flags) +
+ sizeof(ipv4.flags) != sizeof(ipv4));
+ BUILD_BUG_ON(offsetof(struct ib_uverbs_flow_ipv6_filter, reserved) +
+ sizeof(ipv6.reserved) != sizeof(ipv6));
+
+ switch (proto) {
+ case IB_FLOW_SPEC_IPV4:
+ if (len > sizeof(user_val.ipv4) &&
+ !ib_is_buffer_cleared(val_ptr + sizeof(user_val.ipv4),
+ len - sizeof(user_val.ipv4)))
+ return -EOPNOTSUPP;
+
+ val_len = min_t(size_t, len, sizeof(user_val.ipv4));
+ ret = copy_from_user(&user_val.ipv4, val_ptr,
+ val_len);
+ if (ret)
+ return -EFAULT;
+
+ user_pmask = &ipv4;
+ break;
+ case IB_FLOW_SPEC_IPV6:
+ if (len > sizeof(user_val.ipv6) &&
+ !ib_is_buffer_cleared(val_ptr + sizeof(user_val.ipv6),
+ len - sizeof(user_val.ipv6)))
+ return -EOPNOTSUPP;
+
+ val_len = min_t(size_t, len, sizeof(user_val.ipv6));
+ ret = copy_from_user(&user_val.ipv6, val_ptr,
+ val_len);
+ if (ret)
+ return -EFAULT;
+
+ user_pmask = &ipv6;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return ib_uverbs_kern_spec_to_ib_spec_filter(proto, user_pmask,
+ &user_val,
+ val_len, out);
+}
+
+static int flow_action_esp_get_encap(struct ib_flow_spec_list *out,
+ struct uverbs_attr_bundle *attrs)
+{
+ struct ib_uverbs_flow_action_esp_encap uverbs_encap;
+ int ret;
+
+ ret = uverbs_copy_from(&uverbs_encap, attrs,
+ UVERBS_ATTR_FLOW_ACTION_ESP_ENCAP);
+ if (ret)
+ return ret;
+
+ /* We currently support only one encap */
+ if (uverbs_encap.next_ptr)
+ return -EOPNOTSUPP;
+
+ if (uverbs_encap.type != IB_FLOW_SPEC_IPV4 &&
+ uverbs_encap.type != IB_FLOW_SPEC_IPV6)
+ return -EOPNOTSUPP;
+
+ return parse_esp_ip(uverbs_encap.type,
+ u64_to_user_ptr(uverbs_encap.val_ptr),
+ uverbs_encap.len,
+ &out->spec);
+}
+
+struct ib_flow_action_esp_attr {
+ struct ib_flow_action_attrs_esp hdr;
+ struct ib_flow_action_attrs_esp_keymats keymat;
+ struct ib_flow_action_attrs_esp_replays replay;
+ /* We currently support only one spec */
+ struct ib_flow_spec_list encap;
+};
+
+#define ESP_LAST_SUPPORTED_FLAG IB_UVERBS_FLOW_ACTION_ESP_FLAGS_ESN_NEW_WINDOW
+static int parse_flow_action_esp(struct ib_device *ib_dev,
+ struct ib_uverbs_file *file,
+ struct uverbs_attr_bundle *attrs,
+ struct ib_flow_action_esp_attr *esp_attr,
+ bool is_modify)
+{
+ struct ib_uverbs_flow_action_esp uverbs_esp = {};
+ int ret;
+
+ /* Optional param, if it doesn't exist, we get -ENOENT and skip it */
+ ret = uverbs_copy_from(&esp_attr->hdr.esn, attrs,
+ UVERBS_ATTR_FLOW_ACTION_ESP_ESN);
+ if (IS_UVERBS_COPY_ERR(ret))
+ return ret;
+
+ /* This can be called from FLOW_ACTION_ESP_MODIFY where
+ * UVERBS_ATTR_FLOW_ACTION_ESP_ATTRS is optional
+ */
+ if (uverbs_attr_is_valid(attrs, UVERBS_ATTR_FLOW_ACTION_ESP_ATTRS)) {
+ ret = uverbs_copy_from_or_zero(&uverbs_esp, attrs,
+ UVERBS_ATTR_FLOW_ACTION_ESP_ATTRS);
+ if (ret)
+ return ret;
+
+ if (uverbs_esp.flags & ~((ESP_LAST_SUPPORTED_FLAG << 1) - 1))
+ return -EOPNOTSUPP;
+
+ esp_attr->hdr.spi = uverbs_esp.spi;
+ esp_attr->hdr.seq = uverbs_esp.seq;
+ esp_attr->hdr.tfc_pad = uverbs_esp.tfc_pad;
+ esp_attr->hdr.hard_limit_pkts = uverbs_esp.hard_limit_pkts;
+ }
+ esp_attr->hdr.flags = esp_flags_uverbs_to_verbs(attrs, uverbs_esp.flags,
+ is_modify);
+
+ if (uverbs_attr_is_valid(attrs, UVERBS_ATTR_FLOW_ACTION_ESP_KEYMAT)) {
+ esp_attr->keymat.protocol =
+ uverbs_attr_get_enum_id(attrs,
+ UVERBS_ATTR_FLOW_ACTION_ESP_KEYMAT);
+ ret = uverbs_copy_from_or_zero(&esp_attr->keymat.keymat,
+ attrs,
+ UVERBS_ATTR_FLOW_ACTION_ESP_KEYMAT);
+ if (ret)
+ return ret;
+
+ ret = flow_action_esp_keymat_validate[esp_attr->keymat.protocol](&esp_attr->keymat);
+ if (ret)
+ return ret;
+
+ esp_attr->hdr.keymat = &esp_attr->keymat;
+ }
+
+ if (uverbs_attr_is_valid(attrs, UVERBS_ATTR_FLOW_ACTION_ESP_REPLAY)) {
+ esp_attr->replay.protocol =
+ uverbs_attr_get_enum_id(attrs,
+ UVERBS_ATTR_FLOW_ACTION_ESP_REPLAY);
+
+ ret = uverbs_copy_from_or_zero(&esp_attr->replay.replay,
+ attrs,
+ UVERBS_ATTR_FLOW_ACTION_ESP_REPLAY);
+ if (ret)
+ return ret;
+
+ ret = flow_action_esp_replay_validate[esp_attr->replay.protocol](&esp_attr->replay,
+ is_modify);
+ if (ret)
+ return ret;
+
+ esp_attr->hdr.replay = &esp_attr->replay;
+ }
+
+ if (uverbs_attr_is_valid(attrs, UVERBS_ATTR_FLOW_ACTION_ESP_ENCAP)) {
+ ret = flow_action_esp_get_encap(&esp_attr->encap, attrs);
+ if (ret)
+ return ret;
+
+ esp_attr->hdr.encap = &esp_attr->encap;
+ }
+
+ return 0;
+}
+
+static int UVERBS_HANDLER(UVERBS_METHOD_FLOW_ACTION_ESP_CREATE)(struct ib_device *ib_dev,
+ struct ib_uverbs_file *file,
+ struct uverbs_attr_bundle *attrs)
+{
+ int ret;
+ struct ib_uobject *uobj;
+ struct ib_flow_action *action;
+ struct ib_flow_action_esp_attr esp_attr = {};
+
+ if (!ib_dev->create_flow_action_esp)
+ return -EOPNOTSUPP;
+
+ ret = parse_flow_action_esp(ib_dev, file, attrs, &esp_attr, false);
+ if (ret)
+ return ret;
+
+ /* No need to check as this attribute is marked as MANDATORY */
+ uobj = uverbs_attr_get(attrs, UVERBS_ATTR_FLOW_ACTION_ESP_HANDLE)->obj_attr.uobject;
+ action = ib_dev->create_flow_action_esp(ib_dev, &esp_attr.hdr, attrs);
+ if (IS_ERR(action))
+ return PTR_ERR(action);
+
+ atomic_set(&action->usecnt, 0);
+ action->device = ib_dev;
+ action->type = IB_FLOW_ACTION_ESP;
+ action->uobject = uobj;
+ uobj->object = action;
+
+ return 0;
+}
+
+static int UVERBS_HANDLER(UVERBS_METHOD_FLOW_ACTION_ESP_MODIFY)(struct ib_device *ib_dev,
+ struct ib_uverbs_file *file,
+ struct uverbs_attr_bundle *attrs)
+{
+ int ret;
+ struct ib_uobject *uobj;
+ struct ib_flow_action *action;
+ struct ib_flow_action_esp_attr esp_attr = {};
+
+ if (!ib_dev->modify_flow_action_esp)
+ return -EOPNOTSUPP;
+
+ ret = parse_flow_action_esp(ib_dev, file, attrs, &esp_attr, true);
+ if (ret)
+ return ret;
+
+ uobj = uverbs_attr_get(attrs, UVERBS_ATTR_FLOW_ACTION_ESP_HANDLE)->obj_attr.uobject;
+ action = uobj->object;
+
+ if (action->type != IB_FLOW_ACTION_ESP)
+ return -EINVAL;
+
+ return ib_dev->modify_flow_action_esp(action,
+ &esp_attr.hdr,
+ attrs);
+}
+
+static const struct uverbs_attr_spec uverbs_flow_action_esp_keymat[] = {
+ [IB_UVERBS_FLOW_ACTION_ESP_KEYMAT_AES_GCM] = {
+ .ptr = {
+ .type = UVERBS_ATTR_TYPE_PTR_IN,
+ UVERBS_ATTR_TYPE(struct ib_uverbs_flow_action_esp_keymat_aes_gcm),
+ .flags = UVERBS_ATTR_SPEC_F_MIN_SZ_OR_ZERO,
+ },
+ },
+};
+
+static const struct uverbs_attr_spec uverbs_flow_action_esp_replay[] = {
+ [IB_UVERBS_FLOW_ACTION_ESP_REPLAY_NONE] = {
+ .ptr = {
+ .type = UVERBS_ATTR_TYPE_PTR_IN,
+ /* No need to specify any data */
+ .len = 0,
+ }
+ },
+ [IB_UVERBS_FLOW_ACTION_ESP_REPLAY_BMP] = {
+ .ptr = {
+ .type = UVERBS_ATTR_TYPE_PTR_IN,
+ UVERBS_ATTR_STRUCT(struct ib_uverbs_flow_action_esp_replay_bmp, size),
+ .flags = UVERBS_ATTR_SPEC_F_MIN_SZ_OR_ZERO,
+ }
+ },
+};
+
+static DECLARE_UVERBS_NAMED_METHOD(UVERBS_METHOD_FLOW_ACTION_ESP_CREATE,
+ &UVERBS_ATTR_IDR(UVERBS_ATTR_FLOW_ACTION_ESP_HANDLE, UVERBS_OBJECT_FLOW_ACTION,
+ UVERBS_ACCESS_NEW,
+ UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
+ &UVERBS_ATTR_PTR_IN(UVERBS_ATTR_FLOW_ACTION_ESP_ATTRS,
+ UVERBS_ATTR_STRUCT(struct ib_uverbs_flow_action_esp, hard_limit_pkts),
+ UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY |
+ UVERBS_ATTR_SPEC_F_MIN_SZ_OR_ZERO)),
+ &UVERBS_ATTR_PTR_IN(UVERBS_ATTR_FLOW_ACTION_ESP_ESN, UVERBS_ATTR_TYPE(__u32)),
+ &UVERBS_ATTR_ENUM_IN(UVERBS_ATTR_FLOW_ACTION_ESP_KEYMAT,
+ uverbs_flow_action_esp_keymat,
+ UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
+ &UVERBS_ATTR_ENUM_IN(UVERBS_ATTR_FLOW_ACTION_ESP_REPLAY,
+ uverbs_flow_action_esp_replay),
+ &UVERBS_ATTR_PTR_IN(UVERBS_ATTR_FLOW_ACTION_ESP_ENCAP,
+ UVERBS_ATTR_STRUCT(struct ib_uverbs_flow_action_esp_encap, type)));
+
+static DECLARE_UVERBS_NAMED_METHOD(UVERBS_METHOD_FLOW_ACTION_ESP_MODIFY,
+ &UVERBS_ATTR_IDR(UVERBS_ATTR_FLOW_ACTION_ESP_HANDLE, UVERBS_OBJECT_FLOW_ACTION,
+ UVERBS_ACCESS_WRITE,
+ UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
+ &UVERBS_ATTR_PTR_IN(UVERBS_ATTR_FLOW_ACTION_ESP_ATTRS,
+ UVERBS_ATTR_STRUCT(struct ib_uverbs_flow_action_esp, hard_limit_pkts),
+ UA_FLAGS(UVERBS_ATTR_SPEC_F_MIN_SZ_OR_ZERO)),
+ &UVERBS_ATTR_PTR_IN(UVERBS_ATTR_FLOW_ACTION_ESP_ESN, UVERBS_ATTR_TYPE(__u32)),
+ &UVERBS_ATTR_ENUM_IN(UVERBS_ATTR_FLOW_ACTION_ESP_KEYMAT,
+ uverbs_flow_action_esp_keymat),
+ &UVERBS_ATTR_ENUM_IN(UVERBS_ATTR_FLOW_ACTION_ESP_REPLAY,
+ uverbs_flow_action_esp_replay),
+ &UVERBS_ATTR_PTR_IN(UVERBS_ATTR_FLOW_ACTION_ESP_ENCAP,
+ UVERBS_ATTR_STRUCT(struct ib_uverbs_flow_action_esp_encap, type)));
+
+static DECLARE_UVERBS_NAMED_METHOD_WITH_HANDLER(UVERBS_METHOD_FLOW_ACTION_DESTROY,
+ uverbs_destroy_def_handler,
+ &UVERBS_ATTR_IDR(UVERBS_ATTR_DESTROY_FLOW_ACTION_HANDLE,
+ UVERBS_OBJECT_FLOW_ACTION,
+ UVERBS_ACCESS_DESTROY,
+ UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)));
+
+DECLARE_UVERBS_NAMED_OBJECT(UVERBS_OBJECT_FLOW_ACTION,
+ &UVERBS_TYPE_ALLOC_IDR(0, uverbs_free_flow_action),
+ &UVERBS_METHOD(UVERBS_METHOD_FLOW_ACTION_ESP_CREATE),
+ &UVERBS_METHOD(UVERBS_METHOD_FLOW_ACTION_DESTROY),
+ &UVERBS_METHOD(UVERBS_METHOD_FLOW_ACTION_ESP_MODIFY));
+
diff --git a/drivers/infiniband/core/uverbs_std_types_mr.c b/drivers/infiniband/core/uverbs_std_types_mr.c
new file mode 100644
index 000000000000..68f7cadf088f
--- /dev/null
+++ b/drivers/infiniband/core/uverbs_std_types_mr.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2018, Mellanox Technologies inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 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
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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 "uverbs.h"
+#include <rdma/uverbs_std_types.h>
+
+static int uverbs_free_mr(struct ib_uobject *uobject,
+ enum rdma_remove_reason why)
+{
+ return ib_dereg_mr((struct ib_mr *)uobject->object);
+}
+
+static int UVERBS_HANDLER(UVERBS_METHOD_DM_MR_REG)(struct ib_device *ib_dev,
+ struct ib_uverbs_file *file,
+ struct uverbs_attr_bundle *attrs)
+{
+ struct ib_dm_mr_attr attr = {};
+ struct ib_uobject *uobj;
+ struct ib_dm *dm;
+ struct ib_pd *pd;
+ struct ib_mr *mr;
+ int ret;
+
+ if (!ib_dev->reg_dm_mr)
+ return -EOPNOTSUPP;
+
+ ret = uverbs_copy_from(&attr.offset, attrs, UVERBS_ATTR_REG_DM_MR_OFFSET);
+ if (ret)
+ return ret;
+
+ ret = uverbs_copy_from(&attr.length, attrs,
+ UVERBS_ATTR_REG_DM_MR_LENGTH);
+ if (ret)
+ return ret;
+
+ ret = uverbs_copy_from(&attr.access_flags, attrs,
+ UVERBS_ATTR_REG_DM_MR_ACCESS_FLAGS);
+ if (ret)
+ return ret;
+
+ if (!(attr.access_flags & IB_ZERO_BASED))
+ return -EINVAL;
+
+ ret = ib_check_mr_access(attr.access_flags);
+ if (ret)
+ return ret;
+
+ pd = uverbs_attr_get_obj(attrs, UVERBS_ATTR_REG_DM_MR_PD_HANDLE);
+
+ dm = uverbs_attr_get_obj(attrs, UVERBS_ATTR_REG_DM_MR_DM_HANDLE);
+
+ uobj = uverbs_attr_get(attrs, UVERBS_ATTR_REG_DM_MR_HANDLE)->obj_attr.uobject;
+
+ if (attr.offset > dm->length || attr.length > dm->length ||
+ attr.length > dm->length - attr.offset)
+ return -EINVAL;
+
+ mr = pd->device->reg_dm_mr(pd, dm, &attr, attrs);
+ if (IS_ERR(mr))
+ return PTR_ERR(mr);
+
+ mr->device = pd->device;
+ mr->pd = pd;
+ mr->dm = dm;
+ mr->uobject = uobj;
+ atomic_inc(&pd->usecnt);
+ atomic_inc(&dm->usecnt);
+
+ uobj->object = mr;
+
+ ret = uverbs_copy_to(attrs, UVERBS_ATTR_REG_DM_MR_RESP_LKEY, &mr->lkey,
+ sizeof(mr->lkey));
+ if (ret)
+ goto err_dereg;
+
+ ret = uverbs_copy_to(attrs, UVERBS_ATTR_REG_DM_MR_RESP_RKEY,
+ &mr->rkey, sizeof(mr->rkey));
+ if (ret)
+ goto err_dereg;
+
+ return 0;
+
+err_dereg:
+ ib_dereg_mr(mr);
+
+ return ret;
+}
+
+static DECLARE_UVERBS_NAMED_METHOD(UVERBS_METHOD_DM_MR_REG,
+ &UVERBS_ATTR_IDR(UVERBS_ATTR_REG_DM_MR_HANDLE, UVERBS_OBJECT_MR,
+ UVERBS_ACCESS_NEW,
+ UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
+ &UVERBS_ATTR_PTR_IN(UVERBS_ATTR_REG_DM_MR_OFFSET,
+ UVERBS_ATTR_TYPE(u64),
+ UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
+ &UVERBS_ATTR_PTR_IN(UVERBS_ATTR_REG_DM_MR_LENGTH,
+ UVERBS_ATTR_TYPE(u64),
+ UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
+ &UVERBS_ATTR_IDR(UVERBS_ATTR_REG_DM_MR_PD_HANDLE, UVERBS_OBJECT_PD,
+ UVERBS_ACCESS_READ,
+ UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
+ &UVERBS_ATTR_PTR_IN(UVERBS_ATTR_REG_DM_MR_ACCESS_FLAGS,
+ UVERBS_ATTR_TYPE(u32),
+ UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
+ &UVERBS_ATTR_IDR(UVERBS_ATTR_REG_DM_MR_DM_HANDLE, UVERBS_OBJECT_DM,
+ UVERBS_ACCESS_READ,
+ UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
+ &UVERBS_ATTR_PTR_OUT(UVERBS_ATTR_REG_DM_MR_RESP_LKEY,
+ UVERBS_ATTR_TYPE(u32),
+ UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
+ &UVERBS_ATTR_PTR_OUT(UVERBS_ATTR_REG_DM_MR_RESP_RKEY,
+ UVERBS_ATTR_TYPE(u32),
+ UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)));
+
+DECLARE_UVERBS_NAMED_OBJECT(UVERBS_OBJECT_MR,
+ /* 1 is used in order to free the MR after all the MWs */
+ &UVERBS_TYPE_ALLOC_IDR(1, uverbs_free_mr),
+ &UVERBS_METHOD(UVERBS_METHOD_DM_MR_REG));
diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c
index 93025d2009b8..7eff3aeffe01 100644
--- a/drivers/infiniband/core/verbs.c
+++ b/drivers/infiniband/core/verbs.c
@@ -655,7 +655,7 @@ int rdma_modify_ah(struct ib_ah *ah, struct rdma_ah_attr *ah_attr)
return ah->device->modify_ah ?
ah->device->modify_ah(ah, ah_attr) :
- -ENOSYS;
+ -EOPNOTSUPP;
}
EXPORT_SYMBOL(rdma_modify_ah);
@@ -663,7 +663,7 @@ int rdma_query_ah(struct ib_ah *ah, struct rdma_ah_attr *ah_attr)
{
return ah->device->query_ah ?
ah->device->query_ah(ah, ah_attr) :
- -ENOSYS;
+ -EOPNOTSUPP;
}
EXPORT_SYMBOL(rdma_query_ah);
@@ -689,7 +689,7 @@ struct ib_srq *ib_create_srq(struct ib_pd *pd,
struct ib_srq *srq;
if (!pd->device->create_srq)
- return ERR_PTR(-ENOSYS);
+ return ERR_PTR(-EOPNOTSUPP);
srq = pd->device->create_srq(pd, srq_init_attr, NULL);
@@ -722,7 +722,7 @@ int ib_modify_srq(struct ib_srq *srq,
{
return srq->device->modify_srq ?
srq->device->modify_srq(srq, srq_attr, srq_attr_mask, NULL) :
- -ENOSYS;
+ -EOPNOTSUPP;
}
EXPORT_SYMBOL(ib_modify_srq);
@@ -730,7 +730,7 @@ int ib_query_srq(struct ib_srq *srq,
struct ib_srq_attr *srq_attr)
{
return srq->device->query_srq ?
- srq->device->query_srq(srq, srq_attr) : -ENOSYS;
+ srq->device->query_srq(srq, srq_attr) : -EOPNOTSUPP;
}
EXPORT_SYMBOL(ib_query_srq);
@@ -1263,34 +1263,30 @@ static const struct {
}
};
-int ib_modify_qp_is_ok(enum ib_qp_state cur_state, enum ib_qp_state next_state,
- enum ib_qp_type type, enum ib_qp_attr_mask mask,
- enum rdma_link_layer ll)
+bool ib_modify_qp_is_ok(enum ib_qp_state cur_state, enum ib_qp_state next_state,
+ enum ib_qp_type type, enum ib_qp_attr_mask mask,
+ enum rdma_link_layer ll)
{
enum ib_qp_attr_mask req_param, opt_param;
- if (cur_state < 0 || cur_state > IB_QPS_ERR ||
- next_state < 0 || next_state > IB_QPS_ERR)
- return 0;
-
if (mask & IB_QP_CUR_STATE &&
cur_state != IB_QPS_RTR && cur_state != IB_QPS_RTS &&
cur_state != IB_QPS_SQD && cur_state != IB_QPS_SQE)
- return 0;
+ return false;
if (!qp_state_table[cur_state][next_state].valid)
- return 0;
+ return false;
req_param = qp_state_table[cur_state][next_state].req_param[type];
opt_param = qp_state_table[cur_state][next_state].opt_param[type];
if ((mask & req_param) != req_param)
- return 0;
+ return false;
if (mask & ~(req_param | opt_param | IB_QP_STATE))
- return 0;
+ return false;
- return 1;
+ return true;
}
EXPORT_SYMBOL(ib_modify_qp_is_ok);
@@ -1457,7 +1453,7 @@ int ib_query_qp(struct ib_qp *qp,
{
return qp->device->query_qp ?
qp->device->query_qp(qp->real_qp, qp_attr, qp_attr_mask, qp_init_attr) :
- -ENOSYS;
+ -EOPNOTSUPP;
}
EXPORT_SYMBOL(ib_query_qp);
@@ -1594,7 +1590,7 @@ EXPORT_SYMBOL(ib_create_cq);
int rdma_set_cq_moderation(struct ib_cq *cq, u16 cq_count, u16 cq_period)
{
return cq->device->modify_cq ?
- cq->device->modify_cq(cq, cq_count, cq_period) : -ENOSYS;
+ cq->device->modify_cq(cq, cq_count, cq_period) : -EOPNOTSUPP;
}
EXPORT_SYMBOL(rdma_set_cq_moderation);
@@ -1611,7 +1607,7 @@ EXPORT_SYMBOL(ib_destroy_cq);
int ib_resize_cq(struct ib_cq *cq, int cqe)
{
return cq->device->resize_cq ?
- cq->device->resize_cq(cq, cqe, NULL) : -ENOSYS;
+ cq->device->resize_cq(cq, cqe, NULL) : -EOPNOTSUPP;
}
EXPORT_SYMBOL(ib_resize_cq);
@@ -1620,11 +1616,16 @@ EXPORT_SYMBOL(ib_resize_cq);
int ib_dereg_mr(struct ib_mr *mr)
{
struct ib_pd *pd = mr->pd;
+ struct ib_dm *dm = mr->dm;
int ret;
+ rdma_restrack_del(&mr->res);
ret = mr->device->dereg_mr(mr);
- if (!ret)
+ if (!ret) {
atomic_dec(&pd->usecnt);
+ if (dm)
+ atomic_dec(&dm->usecnt);
+ }
return ret;
}
@@ -1649,7 +1650,7 @@ struct ib_mr *ib_alloc_mr(struct ib_pd *pd,
struct ib_mr *mr;
if (!pd->device->alloc_mr)
- return ERR_PTR(-ENOSYS);
+ return ERR_PTR(-EOPNOTSUPP);
mr = pd->device->alloc_mr(pd, mr_type, max_num_sg);
if (!IS_ERR(mr)) {
@@ -1658,6 +1659,8 @@ struct ib_mr *ib_alloc_mr(struct ib_pd *pd,
mr->uobject = NULL;
atomic_inc(&pd->usecnt);
mr->need_inval = false;
+ mr->res.type = RDMA_RESTRACK_MR;
+ rdma_restrack_add(&mr->res);
}
return mr;
@@ -1673,7 +1676,7 @@ struct ib_fmr *ib_alloc_fmr(struct ib_pd *pd,
struct ib_fmr *fmr;
if (!pd->device->alloc_fmr)
- return ERR_PTR(-ENOSYS);
+ return ERR_PTR(-EOPNOTSUPP);
fmr = pd->device->alloc_fmr(pd, mr_access_flags, fmr_attr);
if (!IS_ERR(fmr)) {
@@ -1757,7 +1760,7 @@ int ib_attach_mcast(struct ib_qp *qp, union ib_gid *gid, u16 lid)
int ret;
if (!qp->device->attach_mcast)
- return -ENOSYS;
+ return -EOPNOTSUPP;
if (!rdma_is_multicast_addr((struct in6_addr *)gid->raw) ||
qp->qp_type != IB_QPT_UD || !is_valid_mcast_lid(qp, lid))
@@ -1775,7 +1778,7 @@ int ib_detach_mcast(struct ib_qp *qp, union ib_gid *gid, u16 lid)
int ret;
if (!qp->device->detach_mcast)
- return -ENOSYS;
+ return -EOPNOTSUPP;
if (!rdma_is_multicast_addr((struct in6_addr *)gid->raw) ||
qp->qp_type != IB_QPT_UD || !is_valid_mcast_lid(qp, lid))
@@ -1793,7 +1796,7 @@ struct ib_xrcd *__ib_alloc_xrcd(struct ib_device *device, const char *caller)
struct ib_xrcd *xrcd;
if (!device->alloc_xrcd)
- return ERR_PTR(-ENOSYS);
+ return ERR_PTR(-EOPNOTSUPP);
xrcd = device->alloc_xrcd(device, NULL, NULL);
if (!IS_ERR(xrcd)) {
@@ -1847,7 +1850,7 @@ struct ib_wq *ib_create_wq(struct ib_pd *pd,
struct ib_wq *wq;
if (!pd->device->create_wq)
- return ERR_PTR(-ENOSYS);
+ return ERR_PTR(-EOPNOTSUPP);
wq = pd->device->create_wq(pd, wq_attr, NULL);
if (!IS_ERR(wq)) {
@@ -1902,7 +1905,7 @@ int ib_modify_wq(struct ib_wq *wq, struct ib_wq_attr *wq_attr,
int err;
if (!wq->device->modify_wq)
- return -ENOSYS;
+ return -EOPNOTSUPP;
err = wq->device->modify_wq(wq, wq_attr, wq_attr_mask, NULL);
return err;
@@ -1927,7 +1930,7 @@ struct ib_rwq_ind_table *ib_create_rwq_ind_table(struct ib_device *device,
u32 table_size;
if (!device->create_rwq_ind_table)
- return ERR_PTR(-ENOSYS);
+ return ERR_PTR(-EOPNOTSUPP);
table_size = (1 << init_attr->log_ind_tbl_size);
rwq_ind_table = device->create_rwq_ind_table(device,
@@ -1977,7 +1980,7 @@ struct ib_flow *ib_create_flow(struct ib_qp *qp,
{
struct ib_flow *flow_id;
if (!qp->device->create_flow)
- return ERR_PTR(-ENOSYS);
+ return ERR_PTR(-EOPNOTSUPP);
flow_id = qp->device->create_flow(qp, flow_attr, domain);
if (!IS_ERR(flow_id)) {
@@ -2004,7 +2007,7 @@ int ib_check_mr_status(struct ib_mr *mr, u32 check_mask,
struct ib_mr_status *mr_status)
{
return mr->device->check_mr_status ?
- mr->device->check_mr_status(mr, check_mask, mr_status) : -ENOSYS;
+ mr->device->check_mr_status(mr, check_mask, mr_status) : -EOPNOTSUPP;
}
EXPORT_SYMBOL(ib_check_mr_status);
@@ -2012,7 +2015,7 @@ int ib_set_vf_link_state(struct ib_device *device, int vf, u8 port,
int state)
{
if (!device->set_vf_link_state)
- return -ENOSYS;
+ return -EOPNOTSUPP;
return device->set_vf_link_state(device, vf, port, state);
}
@@ -2022,7 +2025,7 @@ int ib_get_vf_config(struct ib_device *device, int vf, u8 port,
struct ifla_vf_info *info)
{
if (!device->get_vf_config)
- return -ENOSYS;
+ return -EOPNOTSUPP;
return device->get_vf_config(device, vf, port, info);
}
@@ -2032,7 +2035,7 @@ int ib_get_vf_stats(struct ib_device *device, int vf, u8 port,
struct ifla_vf_stats *stats)
{
if (!device->get_vf_stats)
- return -ENOSYS;
+ return -EOPNOTSUPP;
return device->get_vf_stats(device, vf, port, stats);
}
@@ -2042,7 +2045,7 @@ int ib_set_vf_guid(struct ib_device *device, int vf, u8 port, u64 guid,
int type)
{
if (!device->set_vf_guid)
- return -ENOSYS;
+ return -EOPNOTSUPP;
return device->set_vf_guid(device, vf, port, guid, type);
}
@@ -2077,7 +2080,7 @@ int ib_map_mr_sg(struct ib_mr *mr, struct scatterlist *sg, int sg_nents,
unsigned int *sg_offset, unsigned int page_size)
{
if (unlikely(!mr->device->map_mr_sg))
- return -ENOSYS;
+ return -EOPNOTSUPP;
mr->page_size = page_size;
@@ -2194,7 +2197,14 @@ static void __ib_drain_sq(struct ib_qp *qp)
struct ib_cq *cq = qp->send_cq;
struct ib_qp_attr attr = { .qp_state = IB_QPS_ERR };
struct ib_drain_cqe sdrain;
- struct ib_send_wr swr = {}, *bad_swr;
+ struct ib_send_wr *bad_swr;
+ struct ib_rdma_wr swr = {
+ .wr = {
+ .next = NULL,
+ { .wr_cqe = &sdrain.cqe, },
+ .opcode = IB_WR_RDMA_WRITE,
+ },
+ };
int ret;
ret = ib_modify_qp(qp, &attr, IB_QP_STATE);
@@ -2203,11 +2213,10 @@ static void __ib_drain_sq(struct ib_qp *qp)
return;
}
- swr.wr_cqe = &sdrain.cqe;
sdrain.cqe.done = ib_drain_qp_done;
init_completion(&sdrain.done);
- ret = ib_post_send(qp, &swr, &bad_swr);
+ ret = ib_post_send(qp, &swr.wr, &bad_swr);
if (ret) {
WARN_ONCE(ret, "failed to drain send queue: %d\n", ret);
return;
diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c
index 8301d7e5fa8c..a76e206704d4 100644
--- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c
+++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c
@@ -314,12 +314,11 @@ int bnxt_re_query_gid(struct ib_device *ibdev, u8 port_num,
return rc;
}
-int bnxt_re_del_gid(struct ib_device *ibdev, u8 port_num,
- unsigned int index, void **context)
+int bnxt_re_del_gid(const struct ib_gid_attr *attr, void **context)
{
int rc = 0;
struct bnxt_re_gid_ctx *ctx, **ctx_tbl;
- struct bnxt_re_dev *rdev = to_bnxt_re_dev(ibdev, ibdev);
+ struct bnxt_re_dev *rdev = to_bnxt_re_dev(attr->device, ibdev);
struct bnxt_qplib_sgid_tbl *sgid_tbl = &rdev->qplib_res.sgid_tbl;
struct bnxt_qplib_gid *gid_to_del;
@@ -365,15 +364,14 @@ int bnxt_re_del_gid(struct ib_device *ibdev, u8 port_num,
return rc;
}
-int bnxt_re_add_gid(struct ib_device *ibdev, u8 port_num,
- unsigned int index, const union ib_gid *gid,
+int bnxt_re_add_gid(const union ib_gid *gid,
const struct ib_gid_attr *attr, void **context)
{
int rc;
u32 tbl_idx = 0;
u16 vlan_id = 0xFFFF;
struct bnxt_re_gid_ctx *ctx, **ctx_tbl;
- struct bnxt_re_dev *rdev = to_bnxt_re_dev(ibdev, ibdev);
+ struct bnxt_re_dev *rdev = to_bnxt_re_dev(attr->device, ibdev);
struct bnxt_qplib_sgid_tbl *sgid_tbl = &rdev->qplib_res.sgid_tbl;
if ((attr->ndev) && is_vlan_dev(attr->ndev))
@@ -718,8 +716,7 @@ struct ib_ah *bnxt_re_create_ah(struct ib_pd *ib_pd,
grh->sgid_index);
goto fail;
}
- if (sgid_attr.ndev)
- dev_put(sgid_attr.ndev);
+ dev_put(sgid_attr.ndev);
/* Get network header type for this GID */
nw_type = ib_gid_to_network_type(sgid_attr.gid_type, &sgid);
switch (nw_type) {
@@ -1540,14 +1537,13 @@ int bnxt_re_post_srq_recv(struct ib_srq *ib_srq, struct ib_recv_wr *wr,
ib_srq);
struct bnxt_qplib_swqe wqe;
unsigned long flags;
- int rc = 0, payload_sz = 0;
+ int rc = 0;
spin_lock_irqsave(&srq->lock, flags);
while (wr) {
/* Transcribe each ib_recv_wr to qplib_swqe */
wqe.num_sge = wr->num_sge;
- payload_sz = bnxt_re_build_sgl(wr->sg_list, wqe.sg_list,
- wr->num_sge);
+ bnxt_re_build_sgl(wr->sg_list, wqe.sg_list, wr->num_sge);
wqe.wr_id = wr->wr_id;
wqe.type = BNXT_QPLIB_SWQE_TYPE_RECV;
@@ -1698,7 +1694,7 @@ int bnxt_re_modify_qp(struct ib_qp *ib_qp, struct ib_qp_attr *qp_attr,
status = ib_get_cached_gid(&rdev->ibdev, 1,
grh->sgid_index,
&sgid, &sgid_attr);
- if (!status && sgid_attr.ndev) {
+ if (!status) {
memcpy(qp->qplib_qp.smac, sgid_attr.ndev->dev_addr,
ETH_ALEN);
dev_put(sgid_attr.ndev);
diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.h b/drivers/infiniband/hw/bnxt_re/ib_verbs.h
index e62b7c2c7da6..5c6414cad4af 100644
--- a/drivers/infiniband/hw/bnxt_re/ib_verbs.h
+++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.h
@@ -157,10 +157,8 @@ int bnxt_re_get_port_immutable(struct ib_device *ibdev, u8 port_num,
void bnxt_re_query_fw_str(struct ib_device *ibdev, char *str);
int bnxt_re_query_pkey(struct ib_device *ibdev, u8 port_num,
u16 index, u16 *pkey);
-int bnxt_re_del_gid(struct ib_device *ibdev, u8 port_num,
- unsigned int index, void **context);
-int bnxt_re_add_gid(struct ib_device *ibdev, u8 port_num,
- unsigned int index, const union ib_gid *gid,
+int bnxt_re_del_gid(const struct ib_gid_attr *attr, void **context);
+int bnxt_re_add_gid(const union ib_gid *gid,
const struct ib_gid_attr *attr, void **context);
int bnxt_re_query_gid(struct ib_device *ibdev, u8 port_num,
int index, union ib_gid *gid);
diff --git a/drivers/infiniband/hw/bnxt_re/main.c b/drivers/infiniband/hw/bnxt_re/main.c
index f6e361750466..f6c739ec8b62 100644
--- a/drivers/infiniband/hw/bnxt_re/main.c
+++ b/drivers/infiniband/hw/bnxt_re/main.c
@@ -574,7 +574,6 @@ static int bnxt_re_register_ib(struct bnxt_re_dev *rdev)
ibdev->get_port_immutable = bnxt_re_get_port_immutable;
ibdev->get_dev_fw_str = bnxt_re_query_fw_str;
ibdev->query_pkey = bnxt_re_query_pkey;
- ibdev->query_gid = bnxt_re_query_gid;
ibdev->get_netdev = bnxt_re_get_netdev;
ibdev->add_gid = bnxt_re_add_gid;
ibdev->del_gid = bnxt_re_del_gid;
@@ -619,6 +618,7 @@ static int bnxt_re_register_ib(struct bnxt_re_dev *rdev)
ibdev->get_hw_stats = bnxt_re_ib_get_hw_stats;
ibdev->alloc_hw_stats = bnxt_re_ib_alloc_hw_stats;
+ ibdev->driver_id = RDMA_DRIVER_BNXT_RE;
return ib_register_device(ibdev, NULL);
}
diff --git a/drivers/infiniband/hw/bnxt_re/qplib_sp.c b/drivers/infiniband/hw/bnxt_re/qplib_sp.c
index ee98e5efef84..2f3f32eaa1d5 100644
--- a/drivers/infiniband/hw/bnxt_re/qplib_sp.c
+++ b/drivers/infiniband/hw/bnxt_re/qplib_sp.c
@@ -154,7 +154,7 @@ int bnxt_qplib_get_dev_attr(struct bnxt_qplib_rcfw *rcfw,
attr->tqm_alloc_reqs[i * 4 + 3] = *(++tqm_alloc);
}
- attr->is_atomic = 0;
+ attr->is_atomic = false;
bail:
bnxt_qplib_rcfw_free_sbuf(rcfw, sbuf);
return rc;
diff --git a/drivers/infiniband/hw/cxgb3/Kconfig b/drivers/infiniband/hw/cxgb3/Kconfig
index 431be733fbbe..a7b77cb3d5d5 100644
--- a/drivers/infiniband/hw/cxgb3/Kconfig
+++ b/drivers/infiniband/hw/cxgb3/Kconfig
@@ -16,12 +16,3 @@ config INFINIBAND_CXGB3
To compile this driver as a module, choose M here: the module
will be called iw_cxgb3.
-
-config INFINIBAND_CXGB3_DEBUG
- bool "Verbose debugging output"
- depends on INFINIBAND_CXGB3
- default n
- ---help---
- This option causes the Chelsio RDMA driver to produce copious
- amounts of debug messages. Select this if you are developing
- the driver or trying to diagnose a problem.
diff --git a/drivers/infiniband/hw/cxgb3/Makefile b/drivers/infiniband/hw/cxgb3/Makefile
index 2c66d35d19bd..66fe0917aba0 100644
--- a/drivers/infiniband/hw/cxgb3/Makefile
+++ b/drivers/infiniband/hw/cxgb3/Makefile
@@ -5,5 +5,3 @@ obj-$(CONFIG_INFINIBAND_CXGB3) += iw_cxgb3.o
iw_cxgb3-y := iwch_cm.o iwch_ev.o iwch_cq.o iwch_qp.o iwch_mem.o \
iwch_provider.o iwch.o cxio_hal.o cxio_resource.o
-
-ccflags-$(CONFIG_INFINIBAND_CXGB3_DEBUG) += -DDEBUG
diff --git a/drivers/infiniband/hw/cxgb3/cxio_dbg.c b/drivers/infiniband/hw/cxgb3/cxio_dbg.c
deleted file mode 100644
index 97dbe728520a..000000000000
--- a/drivers/infiniband/hw/cxgb3/cxio_dbg.c
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Copyright (c) 2006 Chelsio, Inc. All rights reserved.
- *
- * This software is available to you under a choice of one of two
- * licenses. You may choose to be licensed under the terms of the GNU
- * General Public License (GPL) Version 2, available from the file
- * COPYING in the main directory of this source tree, or the
- * OpenIB.org BSD license below:
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above
- * copyright notice, this list of conditions and the following
- * disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials
- * provided with the distribution.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
- */
-#ifdef DEBUG
-#include <linux/types.h>
-#include <linux/slab.h>
-#include "common.h"
-#include "cxgb3_ioctl.h"
-#include "cxio_hal.h"
-#include "cxio_wr.h"
-
-void cxio_dump_tpt(struct cxio_rdev *rdev, u32 stag)
-{
- struct ch_mem_range *m;
- u64 *data;
- int rc;
- int size = 32;
-
- m = kmalloc(sizeof(*m) + size, GFP_ATOMIC);
- if (!m)
- return;
-
- m->mem_id = MEM_PMRX;
- m->addr = (stag>>8) * 32 + rdev->rnic_info.tpt_base;
- m->len = size;
- pr_debug("%s TPT addr 0x%x len %d\n", __func__, m->addr, m->len);
- rc = rdev->t3cdev_p->ctl(rdev->t3cdev_p, RDMA_GET_MEM, m);
- if (rc) {
- pr_debug("%s toectl returned error %d\n", __func__, rc);
- kfree(m);
- return;
- }
-
- data = (u64 *)m->buf;
- while (size > 0) {
- pr_debug("TPT %08x: %016llx\n",
- m->addr, (unsigned long long)*data);
- size -= 8;
- data++;
- m->addr += 8;
- }
- kfree(m);
-}
-
-void cxio_dump_pbl(struct cxio_rdev *rdev, u32 pbl_addr, uint len, u8 shift)
-{
- struct ch_mem_range *m;
- u64 *data;
- int rc;
- int size, npages;
-
- shift += 12;
- npages = (len + (1ULL << shift) - 1) >> shift;
- size = npages * sizeof(u64);
-
- m = kmalloc(sizeof(*m) + size, GFP_ATOMIC);
- if (!m)
- return;
-
- m->mem_id = MEM_PMRX;
- m->addr = pbl_addr;
- m->len = size;
- pr_debug("%s PBL addr 0x%x len %d depth %d\n",
- __func__, m->addr, m->len, npages);
- rc = rdev->t3cdev_p->ctl(rdev->t3cdev_p, RDMA_GET_MEM, m);
- if (rc) {
- pr_debug("%s toectl returned error %d\n", __func__, rc);
- kfree(m);
- return;
- }
-
- data = (u64 *)m->buf;
- while (size > 0) {
- pr_debug("PBL %08x: %016llx\n",
- m->addr, (unsigned long long)*data);
- size -= 8;
- data++;
- m->addr += 8;
- }
- kfree(m);
-}
-
-void cxio_dump_wqe(union t3_wr *wqe)
-{
- __be64 *data = (__be64 *)wqe;
- uint size = (uint)(be64_to_cpu(*data) & 0xff);
-
- if (size == 0)
- size = 8;
- while (size > 0) {
- pr_debug("WQE %p: %016llx\n",
- data, (unsigned long long)be64_to_cpu(*data));
- size--;
- data++;
- }
-}
-
-void cxio_dump_wce(struct t3_cqe *wce)
-{
- __be64 *data = (__be64 *)wce;
- int size = sizeof(*wce);
-
- while (size > 0) {
- pr_debug("WCE %p: %016llx\n",
- data, (unsigned long long)be64_to_cpu(*data));
- size -= 8;
- data++;
- }
-}
-
-void cxio_dump_rqt(struct cxio_rdev *rdev, u32 hwtid, int nents)
-{
- struct ch_mem_range *m;
- int size = nents * 64;
- u64 *data;
- int rc;
-
- m = kmalloc(sizeof(*m) + size, GFP_ATOMIC);
- if (!m)
- return;
-
- m->mem_id = MEM_PMRX;
- m->addr = ((hwtid)<<10) + rdev->rnic_info.rqt_base;
- m->len = size;
- pr_debug("%s RQT addr 0x%x len %d\n", __func__, m->addr, m->len);
- rc = rdev->t3cdev_p->ctl(rdev->t3cdev_p, RDMA_GET_MEM, m);
- if (rc) {
- pr_debug("%s toectl returned error %d\n", __func__, rc);
- kfree(m);
- return;
- }
-
- data = (u64 *)m->buf;
- while (size > 0) {
- pr_debug("RQT %08x: %016llx\n",
- m->addr, (unsigned long long)*data);
- size -= 8;
- data++;
- m->addr += 8;
- }
- kfree(m);
-}
-
-void cxio_dump_tcb(struct cxio_rdev *rdev, u32 hwtid)
-{
- struct ch_mem_range *m;
- int size = TCB_SIZE;
- u32 *data;
- int rc;
-
- m = kmalloc(sizeof(*m) + size, GFP_ATOMIC);
- if (!m)
- return;
-
- m->mem_id = MEM_CM;
- m->addr = hwtid * size;
- m->len = size;
- pr_debug("%s TCB %d len %d\n", __func__, m->addr, m->len);
- rc = rdev->t3cdev_p->ctl(rdev->t3cdev_p, RDMA_GET_MEM, m);
- if (rc) {
- pr_debug("%s toectl returned error %d\n", __func__, rc);
- kfree(m);
- return;
- }
-
- data = (u32 *)m->buf;
- while (size > 0) {
- printk("%2u: %08x %08x %08x %08x %08x %08x %08x %08x\n",
- m->addr,
- *(data+2), *(data+3), *(data),*(data+1),
- *(data+6), *(data+7), *(data+4), *(data+5));
- size -= 32;
- data += 8;
- m->addr += 32;
- }
- kfree(m);
-}
-#endif
diff --git a/drivers/infiniband/hw/cxgb3/cxio_hal.h b/drivers/infiniband/hw/cxgb3/cxio_hal.h
index 7e70c5492262..c64e50b5a548 100644
--- a/drivers/infiniband/hw/cxgb3/cxio_hal.h
+++ b/drivers/infiniband/hw/cxgb3/cxio_hal.h
@@ -202,13 +202,4 @@ int iwch_cxgb3_ofld_send(struct t3cdev *tdev, struct sk_buff *skb);
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#ifdef DEBUG
-void cxio_dump_tpt(struct cxio_rdev *rev, u32 stag);
-void cxio_dump_pbl(struct cxio_rdev *rev, u32 pbl_addr, uint len, u8 shift);
-void cxio_dump_wqe(union t3_wr *wqe);
-void cxio_dump_wce(struct t3_cqe *wce);
-void cxio_dump_rqt(struct cxio_rdev *rdev, u32 hwtid, int nents);
-void cxio_dump_tcb(struct cxio_rdev *rdev, u32 hwtid);
-#endif
-
#endif
diff --git a/drivers/infiniband/hw/cxgb3/iwch_cq.c b/drivers/infiniband/hw/cxgb3/iwch_cq.c
index dd5348e48806..0a8542c20804 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_cq.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_cq.c
@@ -200,9 +200,6 @@ int iwch_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc)
spin_lock_irqsave(&chp->lock, flags);
for (npolled = 0; npolled < num_entries; ++npolled) {
-#ifdef DEBUG
- int i=0;
-#endif
/*
* Because T3 can post CQEs that are _not_ associated
@@ -211,9 +208,6 @@ int iwch_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc)
*/
do {
err = iwch_poll_cq_one(rhp, chp, wc + npolled);
-#ifdef DEBUG
- BUG_ON(++i > 1000);
-#endif
} while (err == -EAGAIN);
if (err <= 0)
break;
diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c
index a578ca559e11..be097c6723c0 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_provider.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c
@@ -440,7 +440,9 @@ static struct ib_pd *iwch_allocate_pd(struct ib_device *ibdev,
php->pdid = pdid;
php->rhp = rhp;
if (context) {
- if (ib_copy_to_udata(udata, &php->pdid, sizeof (__u32))) {
+ struct iwch_alloc_pd_resp resp = {.pdid = php->pdid};
+
+ if (ib_copy_to_udata(udata, &resp, sizeof(resp))) {
iwch_deallocate_pd(&php->ibpd);
return ERR_PTR(-EFAULT);
}
@@ -1439,6 +1441,7 @@ int iwch_register_device(struct iwch_dev *dev)
memcpy(dev->ibdev.iwcm->ifname, dev->rdev.t3cdev_p->lldev->name,
sizeof(dev->ibdev.iwcm->ifname));
+ dev->ibdev.driver_id = RDMA_DRIVER_CXGB3;
ret = ib_register_device(&dev->ibdev, NULL);
if (ret)
goto bail1;
diff --git a/drivers/infiniband/hw/cxgb4/device.c b/drivers/infiniband/hw/cxgb4/device.c
index 7a9d0de89d6a..feeb8ee6f4a2 100644
--- a/drivers/infiniband/hw/cxgb4/device.c
+++ b/drivers/infiniband/hw/cxgb4/device.c
@@ -220,14 +220,14 @@ static void set_ep_sin_addrs(struct c4iw_ep *ep,
{
struct iw_cm_id *id = ep->com.cm_id;
- *lsin = (struct sockaddr_in *)&ep->com.local_addr;
- *rsin = (struct sockaddr_in *)&ep->com.remote_addr;
+ *m_lsin = (struct sockaddr_in *)&ep->com.local_addr;
+ *m_rsin = (struct sockaddr_in *)&ep->com.remote_addr;
if (id) {
- *m_lsin = (struct sockaddr_in *)&id->m_local_addr;
- *m_rsin = (struct sockaddr_in *)&id->m_remote_addr;
+ *lsin = (struct sockaddr_in *)&id->local_addr;
+ *rsin = (struct sockaddr_in *)&id->remote_addr;
} else {
- *m_lsin = &zero_sin;
- *m_rsin = &zero_sin;
+ *lsin = &zero_sin;
+ *rsin = &zero_sin;
}
}
@@ -239,14 +239,14 @@ static void set_ep_sin6_addrs(struct c4iw_ep *ep,
{
struct iw_cm_id *id = ep->com.cm_id;
- *lsin6 = (struct sockaddr_in6 *)&ep->com.local_addr;
- *rsin6 = (struct sockaddr_in6 *)&ep->com.remote_addr;
+ *m_lsin6 = (struct sockaddr_in6 *)&ep->com.local_addr;
+ *m_rsin6 = (struct sockaddr_in6 *)&ep->com.remote_addr;
if (id) {
- *m_lsin6 = (struct sockaddr_in6 *)&id->m_local_addr;
- *m_rsin6 = (struct sockaddr_in6 *)&id->m_remote_addr;
+ *lsin6 = (struct sockaddr_in6 *)&id->local_addr;
+ *rsin6 = (struct sockaddr_in6 *)&id->remote_addr;
} else {
- *m_lsin6 = &zero_sin6;
- *m_rsin6 = &zero_sin6;
+ *lsin6 = &zero_sin6;
+ *rsin6 = &zero_sin6;
}
}
@@ -1217,6 +1217,7 @@ static int c4iw_uld_state_change(void *handle, enum cxgb4_state new_state)
if (ctx->dev)
c4iw_remove(ctx);
break;
+ case CXGB4_STATE_FATAL_ERROR:
case CXGB4_STATE_START_RECOVERY:
pr_info("%s: Fatal Error\n", pci_name(ctx->lldi.pdev));
if (ctx->dev) {
diff --git a/drivers/infiniband/hw/cxgb4/mem.c b/drivers/infiniband/hw/cxgb4/mem.c
index 7e0eb201cc26..e90f2fd8dc16 100644
--- a/drivers/infiniband/hw/cxgb4/mem.c
+++ b/drivers/infiniband/hw/cxgb4/mem.c
@@ -391,6 +391,9 @@ static int finish_mem_reg(struct c4iw_mr *mhp, u32 stag)
mhp->attr.stag = stag;
mmid = stag >> 8;
mhp->ibmr.rkey = mhp->ibmr.lkey = stag;
+ mhp->ibmr.length = mhp->attr.len;
+ mhp->ibmr.iova = mhp->attr.va_fbo;
+ mhp->ibmr.page_size = 1U << (mhp->attr.page_size + 12);
pr_debug("mmid 0x%x mhp %p\n", mmid, mhp);
return insert_handle(mhp->rhp, &mhp->rhp->mmidr, mhp, mmid);
}
diff --git a/drivers/infiniband/hw/cxgb4/provider.c b/drivers/infiniband/hw/cxgb4/provider.c
index 1b5c6cd2ac4d..0b9cc73c3ded 100644
--- a/drivers/infiniband/hw/cxgb4/provider.c
+++ b/drivers/infiniband/hw/cxgb4/provider.c
@@ -281,7 +281,9 @@ static struct ib_pd *c4iw_allocate_pd(struct ib_device *ibdev,
php->pdid = pdid;
php->rhp = rhp;
if (context) {
- if (ib_copy_to_udata(udata, &php->pdid, sizeof(u32))) {
+ struct c4iw_alloc_pd_resp uresp = {.pdid = php->pdid};
+
+ if (ib_copy_to_udata(udata, &uresp, sizeof(uresp))) {
c4iw_deallocate_pd(&php->ibpd);
return ERR_PTR(-EFAULT);
}
@@ -531,6 +533,24 @@ static void get_dev_fw_str(struct ib_device *dev, char *str)
FW_HDR_FW_VER_BUILD_G(c4iw_dev->rdev.lldi.fw_vers));
}
+static struct net_device *get_netdev(struct ib_device *dev, u8 port)
+{
+ struct c4iw_dev *c4iw_dev = container_of(dev, struct c4iw_dev, ibdev);
+ struct c4iw_rdev *rdev = &c4iw_dev->rdev;
+ struct net_device *ndev;
+
+ if (!port || port > rdev->lldi.nports)
+ return NULL;
+
+ rcu_read_lock();
+ ndev = rdev->lldi.ports[port - 1];
+ if (ndev)
+ dev_hold(ndev);
+ rcu_read_unlock();
+
+ return ndev;
+}
+
void c4iw_register_device(struct work_struct *work)
{
int ret;
@@ -609,6 +629,7 @@ void c4iw_register_device(struct work_struct *work)
dev->ibdev.uverbs_abi_ver = C4IW_UVERBS_ABI_VERSION;
dev->ibdev.get_port_immutable = c4iw_port_immutable;
dev->ibdev.get_dev_fw_str = get_dev_fw_str;
+ dev->ibdev.get_netdev = get_netdev;
dev->ibdev.iwcm = kmalloc(sizeof(struct iw_cm_verbs), GFP_KERNEL);
if (!dev->ibdev.iwcm) {
@@ -627,6 +648,7 @@ void c4iw_register_device(struct work_struct *work)
memcpy(dev->ibdev.iwcm->ifname, dev->rdev.lldi.ports[0]->name,
sizeof(dev->ibdev.iwcm->ifname));
+ dev->ibdev.driver_id = RDMA_DRIVER_CXGB4;
ret = ib_register_device(&dev->ibdev, NULL);
if (ret)
goto err_kfree_iwcm;
diff --git a/drivers/infiniband/hw/hfi1/driver.c b/drivers/infiniband/hw/hfi1/driver.c
index addc68e83606..46d1475b2154 100644
--- a/drivers/infiniband/hw/hfi1/driver.c
+++ b/drivers/infiniband/hw/hfi1/driver.c
@@ -390,6 +390,7 @@ static void rcv_hdrerr(struct hfi1_ctxtdata *rcd, struct hfi1_pportdata *ppd,
svc_type = IB_CC_SVCTYPE_UC;
break;
default:
+ rcu_read_unlock();
goto drop;
}
diff --git a/drivers/infiniband/hw/hfi1/file_ops.c b/drivers/infiniband/hw/hfi1/file_ops.c
index 41fafebe3b0d..da4aa1a95b11 100644
--- a/drivers/infiniband/hw/hfi1/file_ops.c
+++ b/drivers/infiniband/hw/hfi1/file_ops.c
@@ -1153,7 +1153,7 @@ static int get_ctxt_info(struct hfi1_filedata *fd, unsigned long arg, u32 len)
cinfo.sdma_ring_size = fd->cq->nentries;
cinfo.rcvegr_size = uctxt->egrbufs.rcvtid_size;
- trace_hfi1_ctxt_info(uctxt->dd, uctxt->ctxt, fd->subctxt, cinfo);
+ trace_hfi1_ctxt_info(uctxt->dd, uctxt->ctxt, fd->subctxt, &cinfo);
if (copy_to_user((void __user *)arg, &cinfo, len))
return -EFAULT;
diff --git a/drivers/infiniband/hw/hfi1/hfi.h b/drivers/infiniband/hw/hfi1/hfi.h
index 90bc8c76d2ca..32c48265405e 100644
--- a/drivers/infiniband/hw/hfi1/hfi.h
+++ b/drivers/infiniband/hw/hfi1/hfi.h
@@ -70,7 +70,6 @@
#include <linux/rhashtable.h>
#include <linux/netdevice.h>
#include <rdma/rdma_vt.h>
-#include <rdma/opa_addr.h>
#include "chip_registers.h"
#include "common.h"
diff --git a/drivers/infiniband/hw/hfi1/qp.c b/drivers/infiniband/hw/hfi1/qp.c
index d30dd1a5b0a6..1697d96151bd 100644
--- a/drivers/infiniband/hw/hfi1/qp.c
+++ b/drivers/infiniband/hw/hfi1/qp.c
@@ -481,7 +481,6 @@ static void iowait_sdma_drained(struct iowait *wait)
}
/**
- *
* qp_to_sdma_engine - map a qp to a send engine
* @qp: the QP
* @sc5: the 5 bit sc
diff --git a/drivers/infiniband/hw/hfi1/trace_ctxts.h b/drivers/infiniband/hw/hfi1/trace_ctxts.h
index 4eb4cc798035..e00c8a7d559c 100644
--- a/drivers/infiniband/hw/hfi1/trace_ctxts.h
+++ b/drivers/infiniband/hw/hfi1/trace_ctxts.h
@@ -106,7 +106,7 @@ TRACE_EVENT(hfi1_uctxtdata,
TRACE_EVENT(hfi1_ctxt_info,
TP_PROTO(struct hfi1_devdata *dd, unsigned int ctxt,
unsigned int subctxt,
- struct hfi1_ctxt_info cinfo),
+ struct hfi1_ctxt_info *cinfo),
TP_ARGS(dd, ctxt, subctxt, cinfo),
TP_STRUCT__entry(DD_DEV_ENTRY(dd)
__field(unsigned int, ctxt)
@@ -120,11 +120,11 @@ TRACE_EVENT(hfi1_ctxt_info,
TP_fast_assign(DD_DEV_ASSIGN(dd);
__entry->ctxt = ctxt;
__entry->subctxt = subctxt;
- __entry->egrtids = cinfo.egrtids;
- __entry->rcvhdrq_cnt = cinfo.rcvhdrq_cnt;
- __entry->rcvhdrq_size = cinfo.rcvhdrq_entsize;
- __entry->sdma_ring_size = cinfo.sdma_ring_size;
- __entry->rcvegr_size = cinfo.rcvegr_size;
+ __entry->egrtids = cinfo->egrtids;
+ __entry->rcvhdrq_cnt = cinfo->rcvhdrq_cnt;
+ __entry->rcvhdrq_size = cinfo->rcvhdrq_entsize;
+ __entry->sdma_ring_size = cinfo->sdma_ring_size;
+ __entry->rcvegr_size = cinfo->rcvegr_size;
),
TP_printk("[%s] ctxt %u:%u " CINFO_FMT,
__get_str(dev),
diff --git a/drivers/infiniband/hw/hfi1/user_exp_rcv.c b/drivers/infiniband/hw/hfi1/user_exp_rcv.c
index c1c596adcd01..0d5330b7353d 100644
--- a/drivers/infiniband/hw/hfi1/user_exp_rcv.c
+++ b/drivers/infiniband/hw/hfi1/user_exp_rcv.c
@@ -473,7 +473,7 @@ nomem:
tinfo->tidcnt = tididx;
tinfo->length = mapped_pages * PAGE_SIZE;
- if (copy_to_user((void __user *)(unsigned long)tinfo->tidlist,
+ if (copy_to_user(u64_to_user_ptr(tinfo->tidlist),
tidlist, sizeof(tidlist[0]) * tididx)) {
/*
* On failure to copy to the user level, we need to undo
@@ -513,7 +513,7 @@ int hfi1_user_exp_rcv_clear(struct hfi1_filedata *fd,
if (unlikely(tinfo->tidcnt > fd->tid_used))
return -EINVAL;
- tidinfo = memdup_user((void __user *)(unsigned long)tinfo->tidlist,
+ tidinfo = memdup_user(u64_to_user_ptr(tinfo->tidlist),
sizeof(tidinfo[0]) * tinfo->tidcnt);
if (IS_ERR(tidinfo))
return PTR_ERR(tidinfo);
diff --git a/drivers/infiniband/hw/hfi1/verbs.c b/drivers/infiniband/hw/hfi1/verbs.c
index 471d55c50066..c8cf4d4984d3 100644
--- a/drivers/infiniband/hw/hfi1/verbs.c
+++ b/drivers/infiniband/hw/hfi1/verbs.c
@@ -1960,7 +1960,7 @@ int hfi1_register_ib_device(struct hfi1_devdata *dd)
i,
ppd->pkeys);
- ret = rvt_register_device(&dd->verbs_dev.rdi);
+ ret = rvt_register_device(&dd->verbs_dev.rdi, RDMA_DRIVER_HFI1);
if (ret)
goto err_verbs_txreq;
diff --git a/drivers/infiniband/hw/hns/Makefile b/drivers/infiniband/hw/hns/Makefile
index 97bf2cd1cacb..cf03404b9d58 100644
--- a/drivers/infiniband/hw/hns/Makefile
+++ b/drivers/infiniband/hw/hns/Makefile
@@ -7,7 +7,7 @@ ccflags-y := -Idrivers/net/ethernet/hisilicon/hns3
obj-$(CONFIG_INFINIBAND_HNS) += hns-roce.o
hns-roce-objs := hns_roce_main.o hns_roce_cmd.o hns_roce_pd.o \
hns_roce_ah.o hns_roce_hem.o hns_roce_mr.o hns_roce_qp.o \
- hns_roce_cq.o hns_roce_alloc.o
+ hns_roce_cq.o hns_roce_alloc.o hns_roce_db.o
obj-$(CONFIG_INFINIBAND_HNS_HIP06) += hns-roce-hw-v1.o
hns-roce-hw-v1-objs := hns_roce_hw_v1.o
obj-$(CONFIG_INFINIBAND_HNS_HIP08) += hns-roce-hw-v2.o
diff --git a/drivers/infiniband/hw/hns/hns_roce_ah.c b/drivers/infiniband/hw/hns/hns_roce_ah.c
index 7dd6a66ea244..d74928621559 100644
--- a/drivers/infiniband/hw/hns/hns_roce_ah.c
+++ b/drivers/infiniband/hw/hns/hns_roce_ah.c
@@ -68,11 +68,9 @@ struct ib_ah *hns_roce_create_ah(struct ib_pd *ibpd,
return ERR_PTR(ret);
}
- if (gid_attr.ndev) {
- if (is_vlan_dev(gid_attr.ndev))
- vlan_tag = vlan_dev_vlan_id(gid_attr.ndev);
- dev_put(gid_attr.ndev);
- }
+ if (is_vlan_dev(gid_attr.ndev))
+ vlan_tag = vlan_dev_vlan_id(gid_attr.ndev);
+ dev_put(gid_attr.ndev);
if (vlan_tag < 0x1000)
vlan_tag |= (rdma_ah_get_sl(ah_attr) &
diff --git a/drivers/infiniband/hw/hns/hns_roce_cq.c b/drivers/infiniband/hw/hns/hns_roce_cq.c
index bccc9b54c9ce..14734d0d0b76 100644
--- a/drivers/infiniband/hw/hns/hns_roce_cq.c
+++ b/drivers/infiniband/hw/hns/hns_roce_cq.c
@@ -315,6 +315,7 @@ struct ib_cq *hns_roce_ib_create_cq(struct ib_device *ib_dev,
struct hns_roce_dev *hr_dev = to_hr_dev(ib_dev);
struct device *dev = hr_dev->dev;
struct hns_roce_ib_create_cq ucmd;
+ struct hns_roce_ib_create_cq_resp resp = {};
struct hns_roce_cq *hr_cq = NULL;
struct hns_roce_uar *uar = NULL;
int vector = attr->comp_vector;
@@ -354,15 +355,36 @@ struct ib_cq *hns_roce_ib_create_cq(struct ib_device *ib_dev,
goto err_cq;
}
+ if ((hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_RECORD_DB) &&
+ (udata->outlen >= sizeof(resp))) {
+ ret = hns_roce_db_map_user(to_hr_ucontext(context),
+ ucmd.db_addr, &hr_cq->db);
+ if (ret) {
+ dev_err(dev, "cq record doorbell map failed!\n");
+ goto err_mtt;
+ }
+ hr_cq->db_en = 1;
+ resp.cap_flags |= HNS_ROCE_SUPPORT_CQ_RECORD_DB;
+ }
+
/* Get user space parameters */
uar = &to_hr_ucontext(context)->uar;
} else {
+ if (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_RECORD_DB) {
+ ret = hns_roce_alloc_db(hr_dev, &hr_cq->db, 1);
+ if (ret)
+ goto err_cq;
+
+ hr_cq->set_ci_db = hr_cq->db.db_record;
+ *hr_cq->set_ci_db = 0;
+ }
+
/* Init mmt table and write buff address to mtt table */
ret = hns_roce_ib_alloc_cq_buf(hr_dev, &hr_cq->hr_buf,
cq_entries);
if (ret) {
dev_err(dev, "Failed to alloc_cq_buf.\n");
- goto err_cq;
+ goto err_db;
}
uar = &hr_dev->priv_uar;
@@ -375,7 +397,7 @@ struct ib_cq *hns_roce_ib_create_cq(struct ib_device *ib_dev,
hr_cq, vector);
if (ret) {
dev_err(dev, "Creat CQ .Failed to cq_alloc.\n");
- goto err_mtt;
+ goto err_dbmap;
}
/*
@@ -393,10 +415,10 @@ struct ib_cq *hns_roce_ib_create_cq(struct ib_device *ib_dev,
hr_cq->cq_depth = cq_entries;
if (context) {
- if (ib_copy_to_udata(udata, &hr_cq->cqn, sizeof(u64))) {
- ret = -EFAULT;
+ resp.cqn = hr_cq->cqn;
+ ret = ib_copy_to_udata(udata, &resp, sizeof(resp));
+ if (ret)
goto err_cqc;
- }
}
return &hr_cq->ib_cq;
@@ -404,6 +426,12 @@ struct ib_cq *hns_roce_ib_create_cq(struct ib_device *ib_dev,
err_cqc:
hns_roce_free_cq(hr_dev, hr_cq);
+err_dbmap:
+ if (context && (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_RECORD_DB) &&
+ (udata->outlen >= sizeof(resp)))
+ hns_roce_db_unmap_user(to_hr_ucontext(context),
+ &hr_cq->db);
+
err_mtt:
hns_roce_mtt_cleanup(hr_dev, &hr_cq->hr_buf.hr_mtt);
if (context)
@@ -412,6 +440,10 @@ err_mtt:
hns_roce_ib_free_cq_buf(hr_dev, &hr_cq->hr_buf,
hr_cq->ib_cq.cqe);
+err_db:
+ if (!context && (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_RECORD_DB))
+ hns_roce_free_db(hr_dev, &hr_cq->db);
+
err_cq:
kfree(hr_cq);
return ERR_PTR(ret);
@@ -430,12 +462,20 @@ int hns_roce_ib_destroy_cq(struct ib_cq *ib_cq)
hns_roce_free_cq(hr_dev, hr_cq);
hns_roce_mtt_cleanup(hr_dev, &hr_cq->hr_buf.hr_mtt);
- if (ib_cq->uobject)
+ if (ib_cq->uobject) {
ib_umem_release(hr_cq->umem);
- else
+
+ if (hr_cq->db_en == 1)
+ hns_roce_db_unmap_user(
+ to_hr_ucontext(ib_cq->uobject->context),
+ &hr_cq->db);
+ } else {
/* Free the buff of stored cq */
hns_roce_ib_free_cq_buf(hr_dev, &hr_cq->hr_buf,
ib_cq->cqe);
+ if (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_RECORD_DB)
+ hns_roce_free_db(hr_dev, &hr_cq->db);
+ }
kfree(hr_cq);
}
diff --git a/drivers/infiniband/hw/hns/hns_roce_db.c b/drivers/infiniband/hw/hns/hns_roce_db.c
new file mode 100644
index 000000000000..ebee2782a573
--- /dev/null
+++ b/drivers/infiniband/hw/hns/hns_roce_db.c
@@ -0,0 +1,180 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */
+/*
+ * Copyright (c) 2017 Hisilicon Limited.
+ * Copyright (c) 2007, 2008 Mellanox Technologies. All rights reserved.
+ */
+
+#include <linux/platform_device.h>
+#include <rdma/ib_umem.h>
+#include "hns_roce_device.h"
+
+int hns_roce_db_map_user(struct hns_roce_ucontext *context, unsigned long virt,
+ struct hns_roce_db *db)
+{
+ struct hns_roce_user_db_page *page;
+ int ret = 0;
+
+ mutex_lock(&context->page_mutex);
+
+ list_for_each_entry(page, &context->page_list, list)
+ if (page->user_virt == (virt & PAGE_MASK))
+ goto found;
+
+ page = kmalloc(sizeof(*page), GFP_KERNEL);
+ if (!page) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ refcount_set(&page->refcount, 1);
+ page->user_virt = (virt & PAGE_MASK);
+ page->umem = ib_umem_get(&context->ibucontext, virt & PAGE_MASK,
+ PAGE_SIZE, 0, 0);
+ if (IS_ERR(page->umem)) {
+ ret = PTR_ERR(page->umem);
+ kfree(page);
+ goto out;
+ }
+
+ list_add(&page->list, &context->page_list);
+
+found:
+ db->dma = sg_dma_address(page->umem->sg_head.sgl) +
+ (virt & ~PAGE_MASK);
+ db->u.user_page = page;
+ refcount_inc(&page->refcount);
+
+out:
+ mutex_unlock(&context->page_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL(hns_roce_db_map_user);
+
+void hns_roce_db_unmap_user(struct hns_roce_ucontext *context,
+ struct hns_roce_db *db)
+{
+ mutex_lock(&context->page_mutex);
+
+ refcount_dec(&db->u.user_page->refcount);
+ if (refcount_dec_if_one(&db->u.user_page->refcount)) {
+ list_del(&db->u.user_page->list);
+ ib_umem_release(db->u.user_page->umem);
+ kfree(db->u.user_page);
+ }
+
+ mutex_unlock(&context->page_mutex);
+}
+EXPORT_SYMBOL(hns_roce_db_unmap_user);
+
+static struct hns_roce_db_pgdir *hns_roce_alloc_db_pgdir(
+ struct device *dma_device)
+{
+ struct hns_roce_db_pgdir *pgdir;
+
+ pgdir = kzalloc(sizeof(*pgdir), GFP_KERNEL);
+ if (!pgdir)
+ return NULL;
+
+ bitmap_fill(pgdir->order1, HNS_ROCE_DB_PER_PAGE / 2);
+ pgdir->bits[0] = pgdir->order0;
+ pgdir->bits[1] = pgdir->order1;
+ pgdir->page = dma_alloc_coherent(dma_device, PAGE_SIZE,
+ &pgdir->db_dma, GFP_KERNEL);
+ if (!pgdir->page) {
+ kfree(pgdir);
+ return NULL;
+ }
+
+ return pgdir;
+}
+
+static int hns_roce_alloc_db_from_pgdir(struct hns_roce_db_pgdir *pgdir,
+ struct hns_roce_db *db, int order)
+{
+ int o;
+ int i;
+
+ for (o = order; o <= 1; ++o) {
+ i = find_first_bit(pgdir->bits[o], HNS_ROCE_DB_PER_PAGE >> o);
+ if (i < HNS_ROCE_DB_PER_PAGE >> o)
+ goto found;
+ }
+
+ return -ENOMEM;
+
+found:
+ clear_bit(i, pgdir->bits[o]);
+
+ i <<= o;
+
+ if (o > order)
+ set_bit(i ^ 1, pgdir->bits[order]);
+
+ db->u.pgdir = pgdir;
+ db->index = i;
+ db->db_record = pgdir->page + db->index;
+ db->dma = pgdir->db_dma + db->index * 4;
+ db->order = order;
+
+ return 0;
+}
+
+int hns_roce_alloc_db(struct hns_roce_dev *hr_dev, struct hns_roce_db *db,
+ int order)
+{
+ struct hns_roce_db_pgdir *pgdir;
+ int ret = 0;
+
+ mutex_lock(&hr_dev->pgdir_mutex);
+
+ list_for_each_entry(pgdir, &hr_dev->pgdir_list, list)
+ if (!hns_roce_alloc_db_from_pgdir(pgdir, db, order))
+ goto out;
+
+ pgdir = hns_roce_alloc_db_pgdir(hr_dev->dev);
+ if (!pgdir) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ list_add(&pgdir->list, &hr_dev->pgdir_list);
+
+ /* This should never fail -- we just allocated an empty page: */
+ WARN_ON(hns_roce_alloc_db_from_pgdir(pgdir, db, order));
+
+out:
+ mutex_unlock(&hr_dev->pgdir_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(hns_roce_alloc_db);
+
+void hns_roce_free_db(struct hns_roce_dev *hr_dev, struct hns_roce_db *db)
+{
+ int o;
+ int i;
+
+ mutex_lock(&hr_dev->pgdir_mutex);
+
+ o = db->order;
+ i = db->index;
+
+ if (db->order == 0 && test_bit(i ^ 1, db->u.pgdir->order0)) {
+ clear_bit(i ^ 1, db->u.pgdir->order0);
+ ++o;
+ }
+
+ i >>= o;
+ set_bit(i, db->u.pgdir->bits[o]);
+
+ if (bitmap_full(db->u.pgdir->order1, HNS_ROCE_DB_PER_PAGE / 2)) {
+ dma_free_coherent(hr_dev->dev, PAGE_SIZE, db->u.pgdir->page,
+ db->u.pgdir->db_dma);
+ list_del(&db->u.pgdir->list);
+ kfree(db->u.pgdir);
+ }
+
+ mutex_unlock(&hr_dev->pgdir_mutex);
+}
+EXPORT_SYMBOL_GPL(hns_roce_free_db);
diff --git a/drivers/infiniband/hw/hns/hns_roce_device.h b/drivers/infiniband/hw/hns/hns_roce_device.h
index 165a09b314f6..fb305b7f99a8 100644
--- a/drivers/infiniband/hw/hns/hns_roce_device.h
+++ b/drivers/infiniband/hw/hns/hns_roce_device.h
@@ -105,6 +105,14 @@
#define PAGES_SHIFT_24 24
#define PAGES_SHIFT_32 32
+enum {
+ HNS_ROCE_SUPPORT_RQ_RECORD_DB = 1 << 0,
+};
+
+enum {
+ HNS_ROCE_SUPPORT_CQ_RECORD_DB = 1 << 0,
+};
+
enum hns_roce_qp_state {
HNS_ROCE_QP_STATE_RST,
HNS_ROCE_QP_STATE_INIT,
@@ -178,7 +186,8 @@ enum {
enum {
HNS_ROCE_CAP_FLAG_REREG_MR = BIT(0),
HNS_ROCE_CAP_FLAG_ROCE_V1_V2 = BIT(1),
- HNS_ROCE_CAP_FLAG_RQ_INLINE = BIT(2)
+ HNS_ROCE_CAP_FLAG_RQ_INLINE = BIT(2),
+ HNS_ROCE_CAP_FLAG_RECORD_DB = BIT(3)
};
enum hns_roce_mtt_type {
@@ -186,6 +195,10 @@ enum hns_roce_mtt_type {
MTT_TYPE_CQE,
};
+enum {
+ HNS_ROCE_DB_PER_PAGE = PAGE_SIZE / 4
+};
+
#define HNS_ROCE_CMD_SUCCESS 1
#define HNS_ROCE_PORT_DOWN 0
@@ -203,6 +216,8 @@ struct hns_roce_uar {
struct hns_roce_ucontext {
struct ib_ucontext ibucontext;
struct hns_roce_uar uar;
+ struct list_head page_list;
+ struct mutex page_mutex;
};
struct hns_roce_pd {
@@ -335,6 +350,33 @@ struct hns_roce_buf {
int page_shift;
};
+struct hns_roce_db_pgdir {
+ struct list_head list;
+ DECLARE_BITMAP(order0, HNS_ROCE_DB_PER_PAGE);
+ DECLARE_BITMAP(order1, HNS_ROCE_DB_PER_PAGE / 2);
+ unsigned long *bits[2];
+ u32 *page;
+ dma_addr_t db_dma;
+};
+
+struct hns_roce_user_db_page {
+ struct list_head list;
+ struct ib_umem *umem;
+ unsigned long user_virt;
+ refcount_t refcount;
+};
+
+struct hns_roce_db {
+ u32 *db_record;
+ union {
+ struct hns_roce_db_pgdir *pgdir;
+ struct hns_roce_user_db_page *user_page;
+ } u;
+ dma_addr_t dma;
+ int index;
+ int order;
+};
+
struct hns_roce_cq_buf {
struct hns_roce_buf hr_buf;
struct hns_roce_mtt hr_mtt;
@@ -343,6 +385,8 @@ struct hns_roce_cq_buf {
struct hns_roce_cq {
struct ib_cq ib_cq;
struct hns_roce_cq_buf hr_buf;
+ struct hns_roce_db db;
+ u8 db_en;
spinlock_t lock;
struct ib_umem *umem;
void (*comp)(struct hns_roce_cq *cq);
@@ -351,6 +395,7 @@ struct hns_roce_cq {
struct hns_roce_uar *uar;
u32 cq_depth;
u32 cons_index;
+ u32 *set_ci_db;
void __iomem *cq_db_l;
u16 *tptr_addr;
int arm_sn;
@@ -466,6 +511,8 @@ struct hns_roce_qp {
struct ib_qp ibqp;
struct hns_roce_buf hr_buf;
struct hns_roce_wq rq;
+ struct hns_roce_db rdb;
+ u8 rdb_en;
u32 doorbell_qpn;
__le32 sq_signal_bits;
u32 sq_next_wqe;
@@ -725,6 +772,8 @@ struct hns_roce_dev {
spinlock_t bt_cmd_lock;
struct hns_roce_ib_iboe iboe;
+ struct list_head pgdir_list;
+ struct mutex pgdir_mutex;
int irq[HNS_ROCE_MAX_IRQ_NUM];
u8 __iomem *reg_base;
struct hns_roce_caps caps;
@@ -930,6 +979,14 @@ struct ib_cq *hns_roce_ib_create_cq(struct ib_device *ib_dev,
int hns_roce_ib_destroy_cq(struct ib_cq *ib_cq);
void hns_roce_free_cq(struct hns_roce_dev *hr_dev, struct hns_roce_cq *hr_cq);
+int hns_roce_db_map_user(struct hns_roce_ucontext *context, unsigned long virt,
+ struct hns_roce_db *db);
+void hns_roce_db_unmap_user(struct hns_roce_ucontext *context,
+ struct hns_roce_db *db);
+int hns_roce_alloc_db(struct hns_roce_dev *hr_dev, struct hns_roce_db *db,
+ int order);
+void hns_roce_free_db(struct hns_roce_dev *hr_dev, struct hns_roce_db *db);
+
void hns_roce_cq_completion(struct hns_roce_dev *hr_dev, u32 cqn);
void hns_roce_cq_event(struct hns_roce_dev *hr_dev, u32 cqn, int event_type);
void hns_roce_qp_event(struct hns_roce_dev *hr_dev, u32 qpn, int event_type);
diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v1.c b/drivers/infiniband/hw/hns/hns_roce_hw_v1.c
index da13bd7c3ca9..47e1b6ac1e1a 100644
--- a/drivers/infiniband/hw/hns/hns_roce_hw_v1.c
+++ b/drivers/infiniband/hw/hns/hns_roce_hw_v1.c
@@ -1687,13 +1687,13 @@ static int hns_roce_v1_post_mbox(struct hns_roce_dev *hr_dev, u64 in_param,
roce_set_field(val, ROCEE_MB6_ROCEE_MB_TOKEN_M,
ROCEE_MB6_ROCEE_MB_TOKEN_S, token);
- __raw_writeq(cpu_to_le64(in_param), hcr + 0);
- __raw_writeq(cpu_to_le64(out_param), hcr + 2);
- __raw_writel(cpu_to_le32(in_modifier), hcr + 4);
+ writeq(in_param, hcr + 0);
+ writeq(out_param, hcr + 2);
+ writel(in_modifier, hcr + 4);
/* Memory barrier */
wmb();
- __raw_writel(cpu_to_le32(val), hcr + 5);
+ writel(val, hcr + 5);
mmiowb();
diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c
index db2ff352d75f..8b84ab7800d8 100644
--- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c
+++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c
@@ -498,7 +498,6 @@ static int hns_roce_v2_post_recv(struct ib_qp *ibqp, struct ib_recv_wr *wr,
struct hns_roce_v2_wqe_data_seg *dseg;
struct hns_roce_rinl_sge *sge_list;
struct device *dev = hr_dev->dev;
- struct hns_roce_v2_db rq_db;
unsigned long flags;
void *wqe = NULL;
int ret = 0;
@@ -509,7 +508,7 @@ static int hns_roce_v2_post_recv(struct ib_qp *ibqp, struct ib_recv_wr *wr,
spin_lock_irqsave(&hr_qp->rq.lock, flags);
ind = hr_qp->rq.head & (hr_qp->rq.wqe_cnt - 1);
- if (hr_qp->state == IB_QPS_RESET || hr_qp->state == IB_QPS_ERR) {
+ if (hr_qp->state == IB_QPS_RESET) {
spin_unlock_irqrestore(&hr_qp->rq.lock, flags);
*bad_wr = wr;
return -EINVAL;
@@ -564,17 +563,7 @@ out:
/* Memory barrier */
wmb();
- rq_db.byte_4 = 0;
- rq_db.parameter = 0;
-
- roce_set_field(rq_db.byte_4, V2_DB_BYTE_4_TAG_M,
- V2_DB_BYTE_4_TAG_S, hr_qp->qpn);
- roce_set_field(rq_db.byte_4, V2_DB_BYTE_4_CMD_M,
- V2_DB_BYTE_4_CMD_S, HNS_ROCE_V2_RQ_DB);
- roce_set_field(rq_db.parameter, V2_DB_PARAMETER_CONS_IDX_M,
- V2_DB_PARAMETER_CONS_IDX_S, hr_qp->rq.head);
-
- hns_roce_write64_k((__le32 *)&rq_db, hr_qp->rq.db_reg_l);
+ *hr_qp->rdb.db_record = hr_qp->rq.head & 0xffff;
}
spin_unlock_irqrestore(&hr_qp->rq.lock, flags);
@@ -1168,7 +1157,8 @@ static int hns_roce_v2_profile(struct hns_roce_dev *hr_dev)
caps->flags = HNS_ROCE_CAP_FLAG_REREG_MR |
HNS_ROCE_CAP_FLAG_ROCE_V1_V2 |
- HNS_ROCE_CAP_FLAG_RQ_INLINE;
+ HNS_ROCE_CAP_FLAG_RQ_INLINE |
+ HNS_ROCE_CAP_FLAG_RECORD_DB;
caps->pkey_table_len[0] = 1;
caps->gid_table_len[0] = HNS_ROCE_V2_GID_INDEX_NUM;
caps->ceqe_depth = HNS_ROCE_V2_COMP_EQE_NUM;
@@ -1228,14 +1218,14 @@ static int hns_roce_v2_post_mbox(struct hns_roce_dev *hr_dev, u64 in_param,
roce_set_field(val1, HNS_ROCE_VF_MB5_TOKEN_MASK,
HNS_ROCE_VF_MB5_TOKEN_SHIFT, token);
- __raw_writeq(cpu_to_le64(in_param), hcr + 0);
- __raw_writeq(cpu_to_le64(out_param), hcr + 2);
+ writeq(in_param, hcr + 0);
+ writeq(out_param, hcr + 2);
/* Memory barrier */
wmb();
- __raw_writel(cpu_to_le32(val0), hcr + 4);
- __raw_writel(cpu_to_le32(val1), hcr + 5);
+ writel(val0, hcr + 4);
+ writel(val1, hcr + 5);
mmiowb();
@@ -1507,24 +1497,7 @@ static struct hns_roce_v2_cqe *next_cqe_sw_v2(struct hns_roce_cq *hr_cq)
static void hns_roce_v2_cq_set_ci(struct hns_roce_cq *hr_cq, u32 cons_index)
{
- struct hns_roce_v2_cq_db cq_db;
-
- cq_db.byte_4 = 0;
- cq_db.parameter = 0;
-
- roce_set_field(cq_db.byte_4, V2_CQ_DB_BYTE_4_TAG_M,
- V2_CQ_DB_BYTE_4_TAG_S, hr_cq->cqn);
- roce_set_field(cq_db.byte_4, V2_CQ_DB_BYTE_4_CMD_M,
- V2_CQ_DB_BYTE_4_CMD_S, HNS_ROCE_V2_CQ_DB_PTR);
-
- roce_set_field(cq_db.parameter, V2_CQ_DB_PARAMETER_CONS_IDX_M,
- V2_CQ_DB_PARAMETER_CONS_IDX_S,
- cons_index & ((hr_cq->cq_depth << 1) - 1));
- roce_set_field(cq_db.parameter, V2_CQ_DB_PARAMETER_CMD_SN_M,
- V2_CQ_DB_PARAMETER_CMD_SN_S, 1);
-
- hns_roce_write64_k((__be32 *)&cq_db, hr_cq->cq_db_l);
-
+ *hr_cq->set_ci_db = cons_index & 0xffffff;
}
static void __hns_roce_v2_cq_clean(struct hns_roce_cq *hr_cq, u32 qpn,
@@ -1637,6 +1610,16 @@ static void hns_roce_v2_write_cqc(struct hns_roce_dev *hr_dev,
roce_set_field(cq_context->byte_40_cqe_ba, V2_CQC_BYTE_40_CQE_BA_M,
V2_CQC_BYTE_40_CQE_BA_S, (dma_handle >> (32 + 3)));
+ if (hr_cq->db_en)
+ roce_set_bit(cq_context->byte_44_db_record,
+ V2_CQC_BYTE_44_DB_RECORD_EN_S, 1);
+
+ roce_set_field(cq_context->byte_44_db_record,
+ V2_CQC_BYTE_44_DB_RECORD_ADDR_M,
+ V2_CQC_BYTE_44_DB_RECORD_ADDR_S,
+ ((u32)hr_cq->db.dma) >> 1);
+ cq_context->db_record_addr = hr_cq->db.dma >> 32;
+
roce_set_field(cq_context->byte_56_cqe_period_maxcnt,
V2_CQC_BYTE_56_CQ_MAX_CNT_M,
V2_CQC_BYTE_56_CQ_MAX_CNT_S,
@@ -2274,6 +2257,23 @@ static void modify_qp_reset_to_init(struct ib_qp *ibqp,
hr_qp->qkey = attr->qkey;
}
+ if (hr_qp->rdb_en) {
+ roce_set_bit(context->byte_68_rq_db,
+ V2_QPC_BYTE_68_RQ_RECORD_EN_S, 1);
+ roce_set_bit(qpc_mask->byte_68_rq_db,
+ V2_QPC_BYTE_68_RQ_RECORD_EN_S, 0);
+ }
+
+ roce_set_field(context->byte_68_rq_db,
+ V2_QPC_BYTE_68_RQ_DB_RECORD_ADDR_M,
+ V2_QPC_BYTE_68_RQ_DB_RECORD_ADDR_S,
+ ((u32)hr_qp->rdb.dma) >> 1);
+ roce_set_field(qpc_mask->byte_68_rq_db,
+ V2_QPC_BYTE_68_RQ_DB_RECORD_ADDR_M,
+ V2_QPC_BYTE_68_RQ_DB_RECORD_ADDR_S, 0);
+ context->rq_db_record_addr = hr_qp->rdb.dma >> 32;
+ qpc_mask->rq_db_record_addr = 0;
+
roce_set_bit(context->byte_76_srqn_op_en, V2_QPC_BYTE_76_RQIE_S, 1);
roce_set_bit(qpc_mask->byte_76_srqn_op_en, V2_QPC_BYTE_76_RQIE_S, 0);
@@ -3211,6 +3211,8 @@ static int hns_roce_v2_modify_qp(struct ib_qp *ibqp,
hr_qp->sq.tail = 0;
hr_qp->sq_next_wqe = 0;
hr_qp->next_sge = 0;
+ if (hr_qp->rq.wqe_cnt)
+ *hr_qp->rdb.db_record = 0;
}
out:
@@ -3437,11 +3439,17 @@ static int hns_roce_v2_destroy_qp_common(struct hns_roce_dev *hr_dev,
hns_roce_mtt_cleanup(hr_dev, &hr_qp->mtt);
if (is_user) {
+ if (hr_qp->rq.wqe_cnt && (hr_qp->rdb_en == 1))
+ hns_roce_db_unmap_user(
+ to_hr_ucontext(hr_qp->ibqp.uobject->context),
+ &hr_qp->rdb);
ib_umem_release(hr_qp->umem);
} else {
kfree(hr_qp->sq.wrid);
kfree(hr_qp->rq.wrid);
hns_roce_buf_free(hr_dev, hr_qp->buff_size, &hr_qp->hr_buf);
+ if (hr_qp->rq.wqe_cnt)
+ hns_roce_free_db(hr_dev, &hr_qp->rdb);
}
if (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_RQ_INLINE) {
@@ -4383,7 +4391,7 @@ err_dma_alloc_buf:
eq->l0_dma = 0;
if (mhop_num == 1)
- for (i -= i; i >= 0; i--)
+ for (i -= 1; i >= 0; i--)
dma_free_coherent(dev, buf_chk_sz, eq->buf[i],
eq->buf_dma[i]);
else if (mhop_num == 2) {
diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h
index 2bf8a47e3de3..182b6726f783 100644
--- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h
+++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h
@@ -299,6 +299,9 @@ struct hns_roce_v2_cq_context {
#define V2_CQC_BYTE_44_DB_RECORD_EN_S 0
+#define V2_CQC_BYTE_44_DB_RECORD_ADDR_S 1
+#define V2_CQC_BYTE_44_DB_RECORD_ADDR_M GENMASK(31, 1)
+
#define V2_CQC_BYTE_52_CQE_CNT_S 0
#define V2_CQC_BYTE_52_CQE_CNT_M GENMASK(23, 0)
diff --git a/drivers/infiniband/hw/hns/hns_roce_main.c b/drivers/infiniband/hw/hns/hns_roce_main.c
index eb9a69fc7bec..9d48bc07a9e6 100644
--- a/drivers/infiniband/hw/hns/hns_roce_main.c
+++ b/drivers/infiniband/hw/hns/hns_roce_main.c
@@ -74,12 +74,11 @@ static int hns_roce_set_mac(struct hns_roce_dev *hr_dev, u8 port, u8 *addr)
return hr_dev->hw->set_mac(hr_dev, phy_port, addr);
}
-static int hns_roce_add_gid(struct ib_device *device, u8 port_num,
- unsigned int index, const union ib_gid *gid,
+static int hns_roce_add_gid(const union ib_gid *gid,
const struct ib_gid_attr *attr, void **context)
{
- struct hns_roce_dev *hr_dev = to_hr_dev(device);
- u8 port = port_num - 1;
+ struct hns_roce_dev *hr_dev = to_hr_dev(attr->device);
+ u8 port = attr->port_num - 1;
unsigned long flags;
int ret;
@@ -88,20 +87,20 @@ static int hns_roce_add_gid(struct ib_device *device, u8 port_num,
spin_lock_irqsave(&hr_dev->iboe.lock, flags);
- ret = hr_dev->hw->set_gid(hr_dev, port, index, (union ib_gid *)gid,
- attr);
+ ret = hr_dev->hw->set_gid(hr_dev, port, attr->index,
+ (union ib_gid *)gid, attr);
spin_unlock_irqrestore(&hr_dev->iboe.lock, flags);
return ret;
}
-static int hns_roce_del_gid(struct ib_device *device, u8 port_num,
- unsigned int index, void **context)
+static int hns_roce_del_gid(const struct ib_gid_attr *attr, void **context)
{
- struct hns_roce_dev *hr_dev = to_hr_dev(device);
+ struct hns_roce_dev *hr_dev = to_hr_dev(attr->device);
+ struct ib_gid_attr zattr = { };
union ib_gid zgid = { {0} };
- u8 port = port_num - 1;
+ u8 port = attr->port_num - 1;
unsigned long flags;
int ret;
@@ -110,7 +109,7 @@ static int hns_roce_del_gid(struct ib_device *device, u8 port_num,
spin_lock_irqsave(&hr_dev->iboe.lock, flags);
- ret = hr_dev->hw->set_gid(hr_dev, port, index, &zgid, NULL);
+ ret = hr_dev->hw->set_gid(hr_dev, port, attr->index, &zgid, &zattr);
spin_unlock_irqrestore(&hr_dev->iboe.lock, flags);
@@ -295,12 +294,6 @@ static enum rdma_link_layer hns_roce_get_link_layer(struct ib_device *device,
return IB_LINK_LAYER_ETHERNET;
}
-static int hns_roce_query_gid(struct ib_device *ib_dev, u8 port_num, int index,
- union ib_gid *gid)
-{
- return 0;
-}
-
static int hns_roce_query_pkey(struct ib_device *ib_dev, u8 port, u16 index,
u16 *pkey)
{
@@ -337,7 +330,7 @@ static struct ib_ucontext *hns_roce_alloc_ucontext(struct ib_device *ib_dev,
{
int ret = 0;
struct hns_roce_ucontext *context;
- struct hns_roce_ib_alloc_ucontext_resp resp;
+ struct hns_roce_ib_alloc_ucontext_resp resp = {};
struct hns_roce_dev *hr_dev = to_hr_dev(ib_dev);
resp.qp_tab_size = hr_dev->caps.num_qps;
@@ -350,6 +343,11 @@ static struct ib_ucontext *hns_roce_alloc_ucontext(struct ib_device *ib_dev,
if (ret)
goto error_fail_uar_alloc;
+ if (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_RECORD_DB) {
+ INIT_LIST_HEAD(&context->page_list);
+ mutex_init(&context->page_mutex);
+ }
+
ret = ib_copy_to_udata(udata, &resp, sizeof(resp));
if (ret)
goto error_fail_copy_to_udata;
@@ -476,7 +474,6 @@ static int hns_roce_register_device(struct hns_roce_dev *hr_dev)
ib_dev->modify_port = hns_roce_modify_port;
ib_dev->get_link_layer = hns_roce_get_link_layer;
ib_dev->get_netdev = hns_roce_get_netdev;
- ib_dev->query_gid = hns_roce_query_gid;
ib_dev->add_gid = hns_roce_add_gid;
ib_dev->del_gid = hns_roce_del_gid;
ib_dev->query_pkey = hns_roce_query_pkey;
@@ -520,6 +517,7 @@ static int hns_roce_register_device(struct hns_roce_dev *hr_dev)
/* OTHERS */
ib_dev->get_port_immutable = hns_roce_port_immutable;
+ ib_dev->driver_id = RDMA_DRIVER_HNS;
ret = ib_register_device(ib_dev, NULL);
if (ret) {
dev_err(dev, "ib_register_device failed!\n");
@@ -659,6 +657,11 @@ static int hns_roce_setup_hca(struct hns_roce_dev *hr_dev)
spin_lock_init(&hr_dev->sm_lock);
spin_lock_init(&hr_dev->bt_cmd_lock);
+ if (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_RECORD_DB) {
+ INIT_LIST_HEAD(&hr_dev->pgdir_list);
+ mutex_init(&hr_dev->pgdir_mutex);
+ }
+
ret = hns_roce_init_uar_table(hr_dev);
if (ret) {
dev_err(dev, "Failed to initialize uar table. aborting\n");
diff --git a/drivers/infiniband/hw/hns/hns_roce_mr.c b/drivers/infiniband/hw/hns/hns_roce_mr.c
index da86a8117bd5..f7256d88d38f 100644
--- a/drivers/infiniband/hw/hns/hns_roce_mr.c
+++ b/drivers/infiniband/hw/hns/hns_roce_mr.c
@@ -933,7 +933,7 @@ int hns_roce_ib_umem_write_mtt(struct hns_roce_dev *hr_dev,
ret = hns_roce_write_mtt(hr_dev, mtt, n, i, pages);
out:
- free_page((unsigned long) pages);
+ free_pages((unsigned long) pages, order);
return ret;
}
diff --git a/drivers/infiniband/hw/hns/hns_roce_pd.c b/drivers/infiniband/hw/hns/hns_roce_pd.c
index bdab2188c04a..4b41e041799c 100644
--- a/drivers/infiniband/hw/hns/hns_roce_pd.c
+++ b/drivers/infiniband/hw/hns/hns_roce_pd.c
@@ -32,6 +32,7 @@
#include <linux/platform_device.h>
#include <linux/pci.h>
+#include <uapi/rdma/hns-abi.h>
#include "hns_roce_device.h"
static int hns_roce_pd_alloc(struct hns_roce_dev *hr_dev, unsigned long *pdn)
@@ -77,7 +78,9 @@ struct ib_pd *hns_roce_alloc_pd(struct ib_device *ib_dev,
}
if (context) {
- if (ib_copy_to_udata(udata, &pd->pdn, sizeof(u64))) {
+ struct hns_roce_ib_alloc_pd_resp uresp = {.pdn = pd->pdn};
+
+ if (ib_copy_to_udata(udata, &uresp, sizeof(uresp))) {
hns_roce_pd_free(to_hr_dev(ib_dev), pd->pdn);
dev_err(dev, "[alloc_pd]ib_copy_to_udata failed!\n");
kfree(pd);
diff --git a/drivers/infiniband/hw/hns/hns_roce_qp.c b/drivers/infiniband/hw/hns/hns_roce_qp.c
index 088973a05882..e289a924e789 100644
--- a/drivers/infiniband/hw/hns/hns_roce_qp.c
+++ b/drivers/infiniband/hw/hns/hns_roce_qp.c
@@ -489,6 +489,15 @@ static int hns_roce_set_kernel_sq_size(struct hns_roce_dev *hr_dev,
return 0;
}
+static int hns_roce_qp_has_rq(struct ib_qp_init_attr *attr)
+{
+ if (attr->qp_type == IB_QPT_XRC_INI ||
+ attr->qp_type == IB_QPT_XRC_TGT || attr->srq)
+ return 0;
+
+ return 1;
+}
+
static int hns_roce_create_qp_common(struct hns_roce_dev *hr_dev,
struct ib_pd *ib_pd,
struct ib_qp_init_attr *init_attr,
@@ -497,6 +506,7 @@ static int hns_roce_create_qp_common(struct hns_roce_dev *hr_dev,
{
struct device *dev = hr_dev->dev;
struct hns_roce_ib_create_qp ucmd;
+ struct hns_roce_ib_create_qp_resp resp = {};
unsigned long qpn = 0;
int ret = 0;
u32 page_shift;
@@ -602,6 +612,18 @@ static int hns_roce_create_qp_common(struct hns_roce_dev *hr_dev,
dev_err(dev, "hns_roce_ib_umem_write_mtt error for create qp\n");
goto err_mtt;
}
+
+ if ((hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_RECORD_DB) &&
+ (udata->outlen >= sizeof(resp)) &&
+ hns_roce_qp_has_rq(init_attr)) {
+ ret = hns_roce_db_map_user(
+ to_hr_ucontext(ib_pd->uobject->context),
+ ucmd.db_addr, &hr_qp->rdb);
+ if (ret) {
+ dev_err(dev, "rp record doorbell map failed!\n");
+ goto err_mtt;
+ }
+ }
} else {
if (init_attr->create_flags &
IB_QP_CREATE_BLOCK_MULTICAST_LOOPBACK) {
@@ -630,6 +652,16 @@ static int hns_roce_create_qp_common(struct hns_roce_dev *hr_dev,
hr_qp->rq.db_reg_l = hr_dev->reg_base + hr_dev->odb_offset +
DB_REG_OFFSET * hr_dev->priv_uar.index;
+ if ((hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_RECORD_DB) &&
+ hns_roce_qp_has_rq(init_attr)) {
+ ret = hns_roce_alloc_db(hr_dev, &hr_qp->rdb, 0);
+ if (ret) {
+ dev_err(dev, "rq record doorbell alloc failed!\n");
+ goto err_rq_sge_list;
+ }
+ *hr_qp->rdb.db_record = 0;
+ }
+
/* Allocate QP buf */
page_shift = PAGE_SHIFT + hr_dev->caps.mtt_buf_pg_sz;
if (hns_roce_buf_alloc(hr_dev, hr_qp->buff_size,
@@ -637,7 +669,7 @@ static int hns_roce_create_qp_common(struct hns_roce_dev *hr_dev,
&hr_qp->hr_buf, page_shift)) {
dev_err(dev, "hns_roce_buf_alloc error!\n");
ret = -ENOMEM;
- goto err_rq_sge_list;
+ goto err_db;
}
hr_qp->mtt.mtt_type = MTT_TYPE_WQE;
@@ -698,17 +730,44 @@ static int hns_roce_create_qp_common(struct hns_roce_dev *hr_dev,
else
hr_qp->doorbell_qpn = cpu_to_le64(hr_qp->qpn);
+ if (ib_pd->uobject && (udata->outlen >= sizeof(resp)) &&
+ (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_RECORD_DB)) {
+
+ /* indicate kernel supports record db */
+ resp.cap_flags |= HNS_ROCE_SUPPORT_RQ_RECORD_DB;
+ ret = ib_copy_to_udata(udata, &resp, sizeof(resp));
+ if (ret)
+ goto err_qp;
+
+ hr_qp->rdb_en = 1;
+ }
hr_qp->event = hns_roce_ib_qp_event;
return 0;
+err_qp:
+ if (init_attr->qp_type == IB_QPT_GSI &&
+ hr_dev->hw_rev == HNS_ROCE_HW_VER1)
+ hns_roce_qp_remove(hr_dev, hr_qp);
+ else
+ hns_roce_qp_free(hr_dev, hr_qp);
+
err_qpn:
if (!sqpn)
hns_roce_release_range_qp(hr_dev, qpn, 1);
err_wrid:
- kfree(hr_qp->sq.wrid);
- kfree(hr_qp->rq.wrid);
+ if (ib_pd->uobject) {
+ if ((hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_RECORD_DB) &&
+ (udata->outlen >= sizeof(resp)) &&
+ hns_roce_qp_has_rq(init_attr))
+ hns_roce_db_unmap_user(
+ to_hr_ucontext(ib_pd->uobject->context),
+ &hr_qp->rdb);
+ } else {
+ kfree(hr_qp->sq.wrid);
+ kfree(hr_qp->rq.wrid);
+ }
err_mtt:
hns_roce_mtt_cleanup(hr_dev, &hr_qp->mtt);
@@ -719,6 +778,11 @@ err_buf:
else
hns_roce_buf_free(hr_dev, hr_qp->buff_size, &hr_qp->hr_buf);
+err_db:
+ if (!ib_pd->uobject && hns_roce_qp_has_rq(init_attr) &&
+ (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_RECORD_DB))
+ hns_roce_free_db(hr_dev, &hr_qp->rdb);
+
err_rq_sge_list:
if (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_RQ_INLINE)
kfree(hr_qp->rq_inl_buf.wqe_list[0].sg_list);
diff --git a/drivers/infiniband/hw/i40iw/i40iw.h b/drivers/infiniband/hw/i40iw/i40iw.h
index bcddd7061fc0..d5d8c1be345a 100644
--- a/drivers/infiniband/hw/i40iw/i40iw.h
+++ b/drivers/infiniband/hw/i40iw/i40iw.h
@@ -60,7 +60,7 @@
#include <i40e_client.h>
#include "i40iw_type.h"
#include "i40iw_p.h"
-#include "i40iw_ucontext.h"
+#include <rdma/i40iw-abi.h>
#include "i40iw_pble.h"
#include "i40iw_verbs.h"
#include "i40iw_cm.h"
@@ -559,18 +559,25 @@ void i40iw_next_iw_state(struct i40iw_qp *iwqp,
u8 state, u8 del_hash,
u8 term, u8 term_len);
int i40iw_send_syn(struct i40iw_cm_node *cm_node, u32 sendack);
+int i40iw_send_reset(struct i40iw_cm_node *cm_node);
struct i40iw_cm_node *i40iw_find_node(struct i40iw_cm_core *cm_core,
u16 rem_port,
u32 *rem_addr,
u16 loc_port,
u32 *loc_addr,
- bool add_refcnt);
+ bool add_refcnt,
+ bool accelerated_list);
enum i40iw_status_code i40iw_hw_flush_wqes(struct i40iw_device *iwdev,
struct i40iw_sc_qp *qp,
struct i40iw_qp_flush_info *info,
bool wait);
+void i40iw_gen_ae(struct i40iw_device *iwdev,
+ struct i40iw_sc_qp *qp,
+ struct i40iw_gen_ae_info *info,
+ bool wait);
+
void i40iw_copy_ip_ntohl(u32 *dst, __be32 *src);
struct ib_mr *i40iw_reg_phys_mr(struct ib_pd *ib_pd,
u64 addr,
diff --git a/drivers/infiniband/hw/i40iw/i40iw_cm.c b/drivers/infiniband/hw/i40iw/i40iw_cm.c
index abf4cd897849..4cfa8f4647e2 100644
--- a/drivers/infiniband/hw/i40iw/i40iw_cm.c
+++ b/drivers/infiniband/hw/i40iw/i40iw_cm.c
@@ -539,7 +539,7 @@ static struct i40iw_puda_buf *i40iw_form_cm_frame(struct i40iw_cm_node *cm_node,
* i40iw_send_reset - Send RST packet
* @cm_node: connection's node
*/
-static int i40iw_send_reset(struct i40iw_cm_node *cm_node)
+int i40iw_send_reset(struct i40iw_cm_node *cm_node)
{
struct i40iw_puda_buf *sqbuf;
int flags = SET_RST | SET_ACK;
@@ -1183,6 +1183,26 @@ static void i40iw_handle_close_entry(struct i40iw_cm_node *cm_node, u32 rem_node
}
/**
+ * i40iw_build_timer_list - Add cm_nodes to timer list
+ * @timer_list: ptr to timer list
+ * @hte: ptr to accelerated or non-accelerated list
+ */
+static void i40iw_build_timer_list(struct list_head *timer_list,
+ struct list_head *hte)
+{
+ struct i40iw_cm_node *cm_node;
+ struct list_head *list_core_temp, *list_node;
+
+ list_for_each_safe(list_node, list_core_temp, hte) {
+ cm_node = container_of(list_node, struct i40iw_cm_node, list);
+ if (cm_node->close_entry || cm_node->send_entry) {
+ atomic_inc(&cm_node->ref_count);
+ list_add(&cm_node->timer_entry, timer_list);
+ }
+ }
+}
+
+/**
* i40iw_cm_timer_tick - system's timer expired callback
* @pass: Pointing to cm_core
*/
@@ -1202,15 +1222,10 @@ static void i40iw_cm_timer_tick(struct timer_list *t)
struct list_head timer_list;
INIT_LIST_HEAD(&timer_list);
- spin_lock_irqsave(&cm_core->ht_lock, flags);
- list_for_each_safe(list_node, list_core_temp, &cm_core->connected_nodes) {
- cm_node = container_of(list_node, struct i40iw_cm_node, list);
- if (cm_node->close_entry || cm_node->send_entry) {
- atomic_inc(&cm_node->ref_count);
- list_add(&cm_node->timer_entry, &timer_list);
- }
- }
+ spin_lock_irqsave(&cm_core->ht_lock, flags);
+ i40iw_build_timer_list(&timer_list, &cm_core->non_accelerated_list);
+ i40iw_build_timer_list(&timer_list, &cm_core->accelerated_list);
spin_unlock_irqrestore(&cm_core->ht_lock, flags);
list_for_each_safe(list_node, list_core_temp, &timer_list) {
@@ -1406,19 +1421,22 @@ static int i40iw_send_fin(struct i40iw_cm_node *cm_node)
* @loc_port: local tcp port num
* @loc_addr: loc ip addr
* @add_refcnt: flag to increment refcount of cm_node
+ * @accelerated_list: flag for accelerated vs non-accelerated list to search
*/
struct i40iw_cm_node *i40iw_find_node(struct i40iw_cm_core *cm_core,
u16 rem_port,
u32 *rem_addr,
u16 loc_port,
u32 *loc_addr,
- bool add_refcnt)
+ bool add_refcnt,
+ bool accelerated_list)
{
struct list_head *hte;
struct i40iw_cm_node *cm_node;
unsigned long flags;
- hte = &cm_core->connected_nodes;
+ hte = accelerated_list ?
+ &cm_core->accelerated_list : &cm_core->non_accelerated_list;
/* walk list and find cm_node associated with this session ID */
spin_lock_irqsave(&cm_core->ht_lock, flags);
@@ -1487,22 +1505,40 @@ static struct i40iw_cm_listener *i40iw_find_listener(
static void i40iw_add_hte_node(struct i40iw_cm_core *cm_core,
struct i40iw_cm_node *cm_node)
{
- struct list_head *hte;
unsigned long flags;
if (!cm_node || !cm_core) {
i40iw_pr_err("cm_node or cm_core == NULL\n");
return;
}
- spin_lock_irqsave(&cm_core->ht_lock, flags);
- /* get a handle on the hash table element (list head for this slot) */
- hte = &cm_core->connected_nodes;
- list_add_tail(&cm_node->list, hte);
+ spin_lock_irqsave(&cm_core->ht_lock, flags);
+ list_add_tail(&cm_node->list, &cm_core->non_accelerated_list);
spin_unlock_irqrestore(&cm_core->ht_lock, flags);
}
/**
+ * i40iw_find_port - find port that matches reference port
+ * @port: port number
+ * @accelerated_list: flag for accelerated vs non-accelerated list
+ */
+static bool i40iw_find_port(struct i40iw_cm_core *cm_core, u16 port,
+ bool accelerated_list)
+{
+ struct list_head *hte;
+ struct i40iw_cm_node *cm_node;
+
+ hte = accelerated_list ?
+ &cm_core->accelerated_list : &cm_core->non_accelerated_list;
+
+ list_for_each_entry(cm_node, hte, list) {
+ if (cm_node->loc_port == port)
+ return true;
+ }
+ return false;
+}
+
+/**
* i40iw_port_in_use - determine if port is in use
* @port: port number
* @active_side: flag for listener side vs active side
@@ -1510,19 +1546,14 @@ static void i40iw_add_hte_node(struct i40iw_cm_core *cm_core,
static bool i40iw_port_in_use(struct i40iw_cm_core *cm_core, u16 port, bool active_side)
{
struct i40iw_cm_listener *listen_node;
- struct i40iw_cm_node *cm_node;
unsigned long flags;
bool ret = false;
if (active_side) {
- /* search connected node list */
spin_lock_irqsave(&cm_core->ht_lock, flags);
- list_for_each_entry(cm_node, &cm_core->connected_nodes, list) {
- if (cm_node->loc_port == port) {
- ret = true;
- break;
- }
- }
+ ret = i40iw_find_port(cm_core, port, true);
+ if (!ret)
+ ret = i40iw_find_port(cm_core, port, false);
if (!ret)
clear_bit(port, cm_core->active_side_ports);
spin_unlock_irqrestore(&cm_core->ht_lock, flags);
@@ -1829,9 +1860,11 @@ static int i40iw_dec_refcnt_listen(struct i40iw_cm_core *cm_core,
INIT_LIST_HEAD(&reset_list);
if (free_hanging_nodes) {
spin_lock_irqsave(&cm_core->ht_lock, flags);
- list_for_each_safe(list_pos, list_temp, &cm_core->connected_nodes) {
+ list_for_each_safe(list_pos,
+ list_temp, &cm_core->non_accelerated_list) {
cm_node = container_of(list_pos, struct i40iw_cm_node, list);
- if ((cm_node->listener == listener) && !cm_node->accelerated) {
+ if ((cm_node->listener == listener) &&
+ !cm_node->accelerated) {
atomic_inc(&cm_node->ref_count);
list_add(&cm_node->reset_entry, &reset_list);
}
@@ -3144,7 +3177,8 @@ void i40iw_receive_ilq(struct i40iw_sc_vsi *vsi, struct i40iw_puda_buf *rbuf)
cm_info.rem_addr,
cm_info.loc_port,
cm_info.loc_addr,
- true);
+ true,
+ false);
if (!cm_node) {
/* Only type of packet accepted are for */
@@ -3202,7 +3236,8 @@ void i40iw_setup_cm_core(struct i40iw_device *iwdev)
cm_core->iwdev = iwdev;
cm_core->dev = &iwdev->sc_dev;
- INIT_LIST_HEAD(&cm_core->connected_nodes);
+ INIT_LIST_HEAD(&cm_core->accelerated_list);
+ INIT_LIST_HEAD(&cm_core->non_accelerated_list);
INIT_LIST_HEAD(&cm_core->listen_nodes);
timer_setup(&cm_core->tcp_timer, i40iw_cm_timer_tick, 0);
@@ -3585,6 +3620,7 @@ int i40iw_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
struct i40iw_qp *iwqp;
struct i40iw_device *iwdev;
struct i40iw_sc_dev *dev;
+ struct i40iw_cm_core *cm_core;
struct i40iw_cm_node *cm_node;
struct ib_qp_attr attr;
int passive_state;
@@ -3594,6 +3630,7 @@ int i40iw_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
struct i40iw_kmem_info accept;
enum i40iw_status_code status;
u64 tagged_offset;
+ unsigned long flags;
memset(&attr, 0, sizeof(attr));
ibqp = i40iw_get_qp(cm_id->device, conn_param->qpn);
@@ -3603,6 +3640,7 @@ int i40iw_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
iwqp = to_iwqp(ibqp);
iwdev = iwqp->iwdev;
dev = &iwdev->sc_dev;
+ cm_core = &iwdev->cm_core;
cm_node = (struct i40iw_cm_node *)cm_id->provider_data;
if (((struct sockaddr_in *)&cm_id->local_addr)->sin_family == AF_INET) {
@@ -3697,6 +3735,10 @@ int i40iw_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
i40iw_modify_qp(&iwqp->ibqp, &attr, IB_QP_STATE, NULL);
cm_node->accelerated = true;
+ spin_lock_irqsave(&cm_core->ht_lock, flags);
+ list_move_tail(&cm_node->list, &cm_core->accelerated_list);
+ spin_unlock_irqrestore(&cm_core->ht_lock, flags);
+
status =
i40iw_send_cm_event(cm_node, cm_id, IW_CM_EVENT_ESTABLISHED, 0);
if (status)
@@ -4026,10 +4068,12 @@ static void i40iw_cm_event_connected(struct i40iw_cm_event *event)
{
struct i40iw_qp *iwqp;
struct i40iw_device *iwdev;
+ struct i40iw_cm_core *cm_core;
struct i40iw_cm_node *cm_node;
struct i40iw_sc_dev *dev;
struct ib_qp_attr attr;
struct iw_cm_id *cm_id;
+ unsigned long flags;
int status;
bool read0;
@@ -4038,6 +4082,7 @@ static void i40iw_cm_event_connected(struct i40iw_cm_event *event)
iwqp = (struct i40iw_qp *)cm_id->provider_data;
iwdev = to_iwdev(iwqp->ibqp.device);
dev = &iwdev->sc_dev;
+ cm_core = &iwdev->cm_core;
if (iwqp->destroyed) {
status = -ETIMEDOUT;
@@ -4057,6 +4102,9 @@ static void i40iw_cm_event_connected(struct i40iw_cm_event *event)
i40iw_modify_qp(&iwqp->ibqp, &attr, IB_QP_STATE, NULL);
cm_node->accelerated = true;
+ spin_lock_irqsave(&cm_core->ht_lock, flags);
+ list_move_tail(&cm_node->list, &cm_core->accelerated_list);
+ spin_unlock_irqrestore(&cm_core->ht_lock, flags);
status = i40iw_send_cm_event(cm_node, cm_id, IW_CM_EVENT_CONNECT_REPLY,
0);
if (status)
@@ -4256,25 +4304,38 @@ void i40iw_cm_teardown_connections(struct i40iw_device *iwdev, u32 *ipaddr,
struct list_head *list_node;
struct i40iw_cm_node *cm_node;
unsigned long flags;
- struct list_head connected_list;
+ struct list_head teardown_list;
struct ib_qp_attr attr;
- INIT_LIST_HEAD(&connected_list);
+ INIT_LIST_HEAD(&teardown_list);
spin_lock_irqsave(&cm_core->ht_lock, flags);
- list_for_each_safe(list_node, list_core_temp, &cm_core->connected_nodes) {
+ list_for_each_safe(list_node, list_core_temp,
+ &cm_core->accelerated_list) {
+ cm_node = container_of(list_node, struct i40iw_cm_node, list);
+ if (disconnect_all ||
+ (nfo->vlan_id == cm_node->vlan_id &&
+ (!memcmp(cm_node->loc_addr, ipaddr, nfo->ipv4 ? 4 : 16) ||
+ !memcmp(cm_node->rem_addr, ipaddr, nfo->ipv4 ? 4 : 16)))) {
+ atomic_inc(&cm_node->ref_count);
+ list_add(&cm_node->teardown_entry, &teardown_list);
+ }
+ }
+ list_for_each_safe(list_node, list_core_temp,
+ &cm_core->non_accelerated_list) {
cm_node = container_of(list_node, struct i40iw_cm_node, list);
if (disconnect_all ||
(nfo->vlan_id == cm_node->vlan_id &&
(!memcmp(cm_node->loc_addr, ipaddr, nfo->ipv4 ? 4 : 16) ||
!memcmp(cm_node->rem_addr, ipaddr, nfo->ipv4 ? 4 : 16)))) {
atomic_inc(&cm_node->ref_count);
- list_add(&cm_node->connected_entry, &connected_list);
+ list_add(&cm_node->teardown_entry, &teardown_list);
}
}
spin_unlock_irqrestore(&cm_core->ht_lock, flags);
- list_for_each_safe(list_node, list_core_temp, &connected_list) {
- cm_node = container_of(list_node, struct i40iw_cm_node, connected_entry);
+ list_for_each_safe(list_node, list_core_temp, &teardown_list) {
+ cm_node = container_of(list_node, struct i40iw_cm_node,
+ teardown_entry);
attr.qp_state = IB_QPS_ERR;
i40iw_modify_qp(&cm_node->iwqp->ibqp, &attr, IB_QP_STATE, NULL);
if (iwdev->reset)
diff --git a/drivers/infiniband/hw/i40iw/i40iw_cm.h b/drivers/infiniband/hw/i40iw/i40iw_cm.h
index cf60c451e071..78ba36ae2bbe 100644
--- a/drivers/infiniband/hw/i40iw/i40iw_cm.h
+++ b/drivers/infiniband/hw/i40iw/i40iw_cm.h
@@ -341,7 +341,7 @@ struct i40iw_cm_node {
int accept_pend;
struct list_head timer_entry;
struct list_head reset_entry;
- struct list_head connected_entry;
+ struct list_head teardown_entry;
atomic_t passive_state;
bool qhash_set;
u8 user_pri;
@@ -403,7 +403,8 @@ struct i40iw_cm_core {
struct i40iw_sc_dev *dev;
struct list_head listen_nodes;
- struct list_head connected_nodes;
+ struct list_head accelerated_list;
+ struct list_head non_accelerated_list;
struct timer_list tcp_timer;
diff --git a/drivers/infiniband/hw/i40iw/i40iw_ctrl.c b/drivers/infiniband/hw/i40iw/i40iw_ctrl.c
index c74fd3309b93..4d841a3c68f3 100644
--- a/drivers/infiniband/hw/i40iw/i40iw_ctrl.c
+++ b/drivers/infiniband/hw/i40iw/i40iw_ctrl.c
@@ -2614,10 +2614,8 @@ static enum i40iw_status_code i40iw_sc_qp_flush_wqes(
qp->flush_sq |= flush_sq;
qp->flush_rq |= flush_rq;
- if (!flush_sq && !flush_rq) {
- if (info->ae_code != I40IW_AE_LLP_RECEIVED_MPA_CRC_ERROR)
- return 0;
- }
+ if (!flush_sq && !flush_rq)
+ return 0;
cqp = qp->pd->dev->cqp;
wqe = i40iw_sc_cqp_get_next_send_wqe(cqp, scratch);
@@ -2659,6 +2657,49 @@ static enum i40iw_status_code i40iw_sc_qp_flush_wqes(
}
/**
+ * i40iw_sc_gen_ae - generate AE, currently uses flush WQE CQP OP
+ * @qp: sc qp
+ * @info: gen ae information
+ * @scratch: u64 saved to be used during cqp completion
+ * @post_sq: flag for cqp db to ring
+ */
+static enum i40iw_status_code i40iw_sc_gen_ae(
+ struct i40iw_sc_qp *qp,
+ struct i40iw_gen_ae_info *info,
+ u64 scratch,
+ bool post_sq)
+{
+ u64 temp;
+ u64 *wqe;
+ struct i40iw_sc_cqp *cqp;
+ u64 header;
+
+ cqp = qp->pd->dev->cqp;
+ wqe = i40iw_sc_cqp_get_next_send_wqe(cqp, scratch);
+ if (!wqe)
+ return I40IW_ERR_RING_FULL;
+
+ temp = info->ae_code |
+ LS_64(info->ae_source, I40IW_CQPSQ_FWQE_AESOURCE);
+
+ set_64bit_val(wqe, 8, temp);
+
+ header = qp->qp_uk.qp_id |
+ LS_64(I40IW_CQP_OP_GEN_AE, I40IW_CQPSQ_OPCODE) |
+ LS_64(1, I40IW_CQPSQ_FWQE_GENERATE_AE) |
+ LS_64(cqp->polarity, I40IW_CQPSQ_WQEVALID);
+
+ i40iw_insert_wqe_hdr(wqe, header);
+
+ i40iw_debug_buf(cqp->dev, I40IW_DEBUG_WQE, "GEN_AE WQE",
+ wqe, I40IW_CQP_WQE_SIZE * 8);
+
+ if (post_sq)
+ i40iw_sc_cqp_post_sq(cqp);
+ return 0;
+}
+
+/**
* i40iw_sc_qp_upload_context - upload qp's context
* @dev: sc device struct
* @info: upload context info ptr for return
@@ -4148,6 +4189,13 @@ static enum i40iw_status_code i40iw_exec_cqp_cmd(struct i40iw_sc_dev *dev,
pcmdinfo->in.u.qp_flush_wqes.
scratch, pcmdinfo->post_sq);
break;
+ case OP_GEN_AE:
+ status = i40iw_sc_gen_ae(
+ pcmdinfo->in.u.gen_ae.qp,
+ &pcmdinfo->in.u.gen_ae.info,
+ pcmdinfo->in.u.gen_ae.scratch,
+ pcmdinfo->post_sq);
+ break;
case OP_ADD_ARP_CACHE_ENTRY:
status = i40iw_sc_add_arp_cache_entry(
pcmdinfo->in.u.add_arp_cache_entry.cqp,
diff --git a/drivers/infiniband/hw/i40iw/i40iw_d.h b/drivers/infiniband/hw/i40iw/i40iw_d.h
index 4b65e4140bd7..6ddaeec87d2f 100644
--- a/drivers/infiniband/hw/i40iw/i40iw_d.h
+++ b/drivers/infiniband/hw/i40iw/i40iw_d.h
@@ -418,6 +418,8 @@
#define I40IW_CQP_OP_QUERY_FPM_VALUES 0x20
#define I40IW_CQP_OP_COMMIT_FPM_VALUES 0x21
#define I40IW_CQP_OP_FLUSH_WQES 0x22
+/* I40IW_CQP_OP_GEN_AE is the same value as I40IW_CQP_OP_FLUSH_WQES */
+#define I40IW_CQP_OP_GEN_AE 0x22
#define I40IW_CQP_OP_MANAGE_APBVT 0x23
#define I40IW_CQP_OP_NOP 0x24
#define I40IW_CQP_OP_MANAGE_QUAD_HASH_TABLE_ENTRY 0x25
@@ -1729,6 +1731,7 @@ enum i40iw_alignment {
#define OP_COMMIT_FPM_VALUES 30
#define OP_REQUESTED_COMMANDS 31
#define OP_COMPLETED_COMMANDS 32
-#define OP_SIZE_CQP_STAT_ARRAY 33
+#define OP_GEN_AE 33
+#define OP_SIZE_CQP_STAT_ARRAY 34
#endif
diff --git a/drivers/infiniband/hw/i40iw/i40iw_hw.c b/drivers/infiniband/hw/i40iw/i40iw_hw.c
index 61540e14e4b9..6139836fb533 100644
--- a/drivers/infiniband/hw/i40iw/i40iw_hw.c
+++ b/drivers/infiniband/hw/i40iw/i40iw_hw.c
@@ -352,6 +352,8 @@ void i40iw_process_aeq(struct i40iw_device *iwdev)
else
i40iw_cm_disconn(iwqp);
break;
+ case I40IW_AE_BAD_CLOSE:
+ /* fall through */
case I40IW_AE_RESET_SENT:
i40iw_next_iw_state(iwqp, I40IW_QP_STATE_ERROR, 1, 0, 0);
i40iw_cm_disconn(iwqp);
@@ -668,6 +670,39 @@ enum i40iw_status_code i40iw_hw_flush_wqes(struct i40iw_device *iwdev,
}
/**
+ * i40iw_gen_ae - generate AE
+ * @iwdev: iwarp device
+ * @qp: qp associated with AE
+ * @info: info for ae
+ * @wait: wait for completion
+ */
+void i40iw_gen_ae(struct i40iw_device *iwdev,
+ struct i40iw_sc_qp *qp,
+ struct i40iw_gen_ae_info *info,
+ bool wait)
+{
+ struct i40iw_gen_ae_info *ae_info;
+ struct i40iw_cqp_request *cqp_request;
+ struct cqp_commands_info *cqp_info;
+
+ cqp_request = i40iw_get_cqp_request(&iwdev->cqp, wait);
+ if (!cqp_request)
+ return;
+
+ cqp_info = &cqp_request->info;
+ ae_info = &cqp_request->info.in.u.gen_ae.info;
+ memcpy(ae_info, info, sizeof(*ae_info));
+
+ cqp_info->cqp_cmd = OP_GEN_AE;
+ cqp_info->post_sq = 1;
+ cqp_info->in.u.gen_ae.qp = qp;
+ cqp_info->in.u.gen_ae.scratch = (uintptr_t)cqp_request;
+ if (i40iw_handle_cqp_op(iwdev, cqp_request))
+ i40iw_pr_err("CQP OP failed attempting to generate ae_code=0x%x\n",
+ info->ae_code);
+}
+
+/**
* i40iw_hw_manage_vf_pble_bp - manage vf pbles
* @iwdev: iwarp device
* @info: info for managing pble
diff --git a/drivers/infiniband/hw/i40iw/i40iw_main.c b/drivers/infiniband/hw/i40iw/i40iw_main.c
index b08862978de8..9cd0d3ef9057 100644
--- a/drivers/infiniband/hw/i40iw/i40iw_main.c
+++ b/drivers/infiniband/hw/i40iw/i40iw_main.c
@@ -1560,8 +1560,6 @@ static enum i40iw_status_code i40iw_setup_init_state(struct i40iw_handler *hdl,
enum i40iw_status_code status;
memcpy(&hdl->ldev, ldev, sizeof(*ldev));
- if (resource_profile == 1)
- resource_profile = 2;
iwdev->mpa_version = mpa_version;
iwdev->resource_profile = (resource_profile < I40IW_HMC_PROFILE_EQUAL) ?
diff --git a/drivers/infiniband/hw/i40iw/i40iw_puda.c b/drivers/infiniband/hw/i40iw/i40iw_puda.c
index 4c21197830b3..d9c7ae6a7030 100644
--- a/drivers/infiniband/hw/i40iw/i40iw_puda.c
+++ b/drivers/infiniband/hw/i40iw/i40iw_puda.c
@@ -348,8 +348,8 @@ enum i40iw_status_code i40iw_puda_poll_completion(struct i40iw_sc_dev *dev,
spin_lock_irqsave(&rsrc->bufpool_lock, flags);
rsrc->tx_wqe_avail_cnt++;
spin_unlock_irqrestore(&rsrc->bufpool_lock, flags);
- if (!list_empty(&rsrc->vsi->ilq->txpend))
- i40iw_puda_send_buf(rsrc->vsi->ilq, NULL);
+ if (!list_empty(&rsrc->txpend))
+ i40iw_puda_send_buf(rsrc, NULL);
}
done:
@@ -1471,10 +1471,6 @@ static void i40iw_ieq_tx_compl(struct i40iw_sc_vsi *vsi, void *sqwrid)
struct i40iw_puda_buf *buf = (struct i40iw_puda_buf *)sqwrid;
i40iw_puda_ret_bufpool(ieq, buf);
- if (!list_empty(&ieq->txpend)) {
- buf = i40iw_puda_get_listbuf(&ieq->txpend);
- i40iw_puda_send_buf(ieq, buf);
- }
}
/**
diff --git a/drivers/infiniband/hw/i40iw/i40iw_type.h b/drivers/infiniband/hw/i40iw/i40iw_type.h
index a27d392c92a2..adc8d2ec523d 100644
--- a/drivers/infiniband/hw/i40iw/i40iw_type.h
+++ b/drivers/infiniband/hw/i40iw/i40iw_type.h
@@ -1004,6 +1004,11 @@ struct i40iw_cqp_query_fpm_values {
u32 pbl_max;
};
+struct i40iw_gen_ae_info {
+ u16 ae_code;
+ u8 ae_source;
+};
+
struct i40iw_cqp_ops {
enum i40iw_status_code (*cqp_init)(struct i40iw_sc_cqp *,
struct i40iw_cqp_init_info *);
@@ -1291,6 +1296,12 @@ struct cqp_info {
} qp_flush_wqes;
struct {
+ struct i40iw_sc_qp *qp;
+ struct i40iw_gen_ae_info info;
+ u64 scratch;
+ } gen_ae;
+
+ struct {
struct i40iw_sc_cqp *cqp;
void *fpm_values_va;
u64 fpm_values_pa;
diff --git a/drivers/infiniband/hw/i40iw/i40iw_ucontext.h b/drivers/infiniband/hw/i40iw/i40iw_ucontext.h
deleted file mode 100644
index 57d3f1d11ff1..000000000000
--- a/drivers/infiniband/hw/i40iw/i40iw_ucontext.h
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (c) 2006 - 2016 Intel Corporation. All rights reserved.
- * Copyright (c) 2005 Topspin Communications. All rights reserved.
- * Copyright (c) 2005 Cisco Systems. All rights reserved.
- * Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved.
- *
- * This software is available to you under a choice of one of two
- * licenses. You may choose to be licensed under the terms of the GNU
- * General Public License (GPL) Version 2, available from the file
- * COPYING in the main directory of this source tree, or the
- * OpenIB.org BSD license below:
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above
- * copyright notice, this list of conditions and the following
- * disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials
- * provided with the distribution.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
- *
- */
-
-#ifndef I40IW_USER_CONTEXT_H
-#define I40IW_USER_CONTEXT_H
-
-#include <linux/types.h>
-
-#define I40IW_ABI_VER 5
-
-struct i40iw_alloc_ucontext_req {
- __u32 reserved32;
- __u8 userspace_ver;
- __u8 reserved8[3];
-};
-
-struct i40iw_alloc_ucontext_resp {
- __u32 max_pds; /* maximum pds allowed for this user process */
- __u32 max_qps; /* maximum qps allowed for this user process */
- __u32 wq_size; /* size of the WQs (sq+rq) allocated to the mmaped area */
- __u8 kernel_ver;
- __u8 reserved[3];
-};
-
-struct i40iw_alloc_pd_resp {
- __u32 pd_id;
- __u8 reserved[4];
-};
-
-struct i40iw_create_cq_req {
- __u64 user_cq_buffer;
- __u64 user_shadow_area;
-};
-
-struct i40iw_create_qp_req {
- __u64 user_wqe_buffers;
- __u64 user_compl_ctx;
-
- /* UDA QP PHB */
- __u64 user_sq_phb; /* place for VA of the sq phb buff */
- __u64 user_rq_phb; /* place for VA of the rq phb buff */
-};
-
-enum i40iw_memreg_type {
- IW_MEMREG_TYPE_MEM = 0x0000,
- IW_MEMREG_TYPE_QP = 0x0001,
- IW_MEMREG_TYPE_CQ = 0x0002,
-};
-
-struct i40iw_mem_reg_req {
- __u16 reg_type; /* Memory, QP or CQ */
- __u16 cq_pages;
- __u16 rq_pages;
- __u16 sq_pages;
-};
-
-struct i40iw_create_cq_resp {
- __u32 cq_id;
- __u32 cq_size;
- __u32 mmap_db_index;
- __u32 reserved;
-};
-
-struct i40iw_create_qp_resp {
- __u32 qp_id;
- __u32 actual_sq_size;
- __u32 actual_rq_size;
- __u32 i40iw_drv_opt;
- __u16 push_idx;
- __u8 lsmm;
- __u8 rsvd2;
-};
-
-#endif
diff --git a/drivers/infiniband/hw/i40iw/i40iw_utils.c b/drivers/infiniband/hw/i40iw/i40iw_utils.c
index ddc1056b0b4e..a9ea966877f2 100644
--- a/drivers/infiniband/hw/i40iw/i40iw_utils.c
+++ b/drivers/infiniband/hw/i40iw/i40iw_utils.c
@@ -1284,15 +1284,13 @@ void i40iw_cqp_qp_destroy_cmd(struct i40iw_sc_dev *dev, struct i40iw_sc_qp *qp)
*/
void i40iw_ieq_mpa_crc_ae(struct i40iw_sc_dev *dev, struct i40iw_sc_qp *qp)
{
- struct i40iw_qp_flush_info info;
+ struct i40iw_gen_ae_info info;
struct i40iw_device *iwdev = (struct i40iw_device *)dev->back_dev;
i40iw_debug(dev, I40IW_DEBUG_AEQ, "%s entered\n", __func__);
- memset(&info, 0, sizeof(info));
info.ae_code = I40IW_AE_LLP_RECEIVED_MPA_CRC_ERROR;
- info.generate_ae = true;
- info.ae_source = 0x3;
- (void)i40iw_hw_flush_wqes(iwdev, qp, &info, false);
+ info.ae_source = I40IW_AE_SOURCE_RQ;
+ i40iw_gen_ae(iwdev, qp, &info, false);
}
/**
@@ -1407,7 +1405,7 @@ struct i40iw_sc_qp *i40iw_ieq_get_qp(struct i40iw_sc_dev *dev,
rem_port = ntohs(tcph->source);
cm_node = i40iw_find_node(&iwdev->cm_core, rem_port, rem_addr, loc_port,
- loc_addr, false);
+ loc_addr, false, true);
if (!cm_node)
return NULL;
iwqp = cm_node->iwqp;
diff --git a/drivers/infiniband/hw/i40iw/i40iw_verbs.c b/drivers/infiniband/hw/i40iw/i40iw_verbs.c
index 70024e8e2692..40e4f5ab2b46 100644
--- a/drivers/infiniband/hw/i40iw/i40iw_verbs.c
+++ b/drivers/infiniband/hw/i40iw/i40iw_verbs.c
@@ -38,6 +38,7 @@
#include <linux/highmem.h>
#include <linux/time.h>
#include <linux/hugetlb.h>
+#include <linux/irq.h>
#include <asm/byteorder.h>
#include <net/ip.h>
#include <rdma/ib_verbs.h>
@@ -830,10 +831,10 @@ static int i40iw_query_qp(struct ib_qp *ibqp,
void i40iw_hw_modify_qp(struct i40iw_device *iwdev, struct i40iw_qp *iwqp,
struct i40iw_modify_qp_info *info, bool wait)
{
- enum i40iw_status_code status;
struct i40iw_cqp_request *cqp_request;
struct cqp_commands_info *cqp_info;
struct i40iw_modify_qp_info *m_info;
+ struct i40iw_gen_ae_info ae_info;
cqp_request = i40iw_get_cqp_request(&iwdev->cqp, wait);
if (!cqp_request)
@@ -846,9 +847,25 @@ void i40iw_hw_modify_qp(struct i40iw_device *iwdev, struct i40iw_qp *iwqp,
cqp_info->post_sq = 1;
cqp_info->in.u.qp_modify.qp = &iwqp->sc_qp;
cqp_info->in.u.qp_modify.scratch = (uintptr_t)cqp_request;
- status = i40iw_handle_cqp_op(iwdev, cqp_request);
- if (status)
- i40iw_pr_err("CQP-OP Modify QP fail");
+ if (!i40iw_handle_cqp_op(iwdev, cqp_request))
+ return;
+
+ switch (m_info->next_iwarp_state) {
+ case I40IW_QP_STATE_RTS:
+ if (iwqp->iwarp_state == I40IW_QP_STATE_IDLE)
+ i40iw_send_reset(iwqp->cm_node);
+ /* fall through */
+ case I40IW_QP_STATE_IDLE:
+ case I40IW_QP_STATE_TERMINATE:
+ case I40IW_QP_STATE_CLOSING:
+ ae_info.ae_code = I40IW_AE_BAD_CLOSE;
+ ae_info.ae_source = 0;
+ i40iw_gen_ae(iwdev, &iwqp->sc_qp, &ae_info, false);
+ break;
+ case I40IW_QP_STATE_ERROR:
+ default:
+ break;
+ }
}
/**
@@ -961,10 +978,6 @@ int i40iw_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
iwqp->ibqp_state = attr->qp_state;
- if (issue_modify_qp)
- iwqp->iwarp_state = info.next_iwarp_state;
- else
- info.next_iwarp_state = iwqp->iwarp_state;
}
if (attr_mask & IB_QP_ACCESS_FLAGS) {
ctx_info->iwarp_info_valid = true;
@@ -1002,9 +1015,14 @@ int i40iw_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
spin_unlock_irqrestore(&iwqp->lock, flags);
- if (issue_modify_qp)
+ if (issue_modify_qp) {
i40iw_hw_modify_qp(iwdev, iwqp, &info, true);
+ spin_lock_irqsave(&iwqp->lock, flags);
+ iwqp->iwarp_state = info.next_iwarp_state;
+ spin_unlock_irqrestore(&iwqp->lock, flags);
+ }
+
if (issue_modify_qp && (iwqp->ibqp_state > IB_QPS_RTS)) {
if (dont_wait) {
if (iwqp->cm_id && iwqp->hw_tcp_state) {
@@ -2729,6 +2747,25 @@ static int i40iw_destroy_ah(struct ib_ah *ah)
}
/**
+ * i40iw_get_vector_affinity - report IRQ affinity mask
+ * @ibdev: IB device
+ * @comp_vector: completion vector index
+ */
+static const struct cpumask *i40iw_get_vector_affinity(struct ib_device *ibdev,
+ int comp_vector)
+{
+ struct i40iw_device *iwdev = to_iwdev(ibdev);
+ struct i40iw_msix_vector *msix_vec;
+
+ if (iwdev->msix_shared)
+ msix_vec = &iwdev->iw_msixtbl[comp_vector];
+ else
+ msix_vec = &iwdev->iw_msixtbl[comp_vector + 1];
+
+ return irq_get_affinity_mask(msix_vec->irq);
+}
+
+/**
* i40iw_init_rdma_device - initialization of iwarp device
* @iwdev: iwarp device
*/
@@ -2824,6 +2861,7 @@ static struct i40iw_ib_device *i40iw_init_rdma_device(struct i40iw_device *iwdev
iwibdev->ibdev.req_notify_cq = i40iw_req_notify_cq;
iwibdev->ibdev.post_send = i40iw_post_send;
iwibdev->ibdev.post_recv = i40iw_post_recv;
+ iwibdev->ibdev.get_vector_affinity = i40iw_get_vector_affinity;
return iwibdev;
}
@@ -2889,6 +2927,7 @@ int i40iw_register_rdma_device(struct i40iw_device *iwdev)
return -ENOMEM;
iwibdev = iwdev->iwibdev;
+ iwibdev->ibdev.driver_id = RDMA_DRIVER_I40IW;
ret = ib_register_device(&iwibdev->ibdev, NULL);
if (ret)
goto error;
diff --git a/drivers/infiniband/hw/mlx4/ah.c b/drivers/infiniband/hw/mlx4/ah.c
index 6dee4fdc5d67..9345d5b546d1 100644
--- a/drivers/infiniband/hw/mlx4/ah.c
+++ b/drivers/infiniband/hw/mlx4/ah.c
@@ -101,12 +101,10 @@ static struct ib_ah *create_iboe_ah(struct ib_pd *pd,
if (ret)
return ERR_PTR(ret);
eth_zero_addr(ah->av.eth.s_mac);
- if (gid_attr.ndev) {
- if (is_vlan_dev(gid_attr.ndev))
- vlan_tag = vlan_dev_vlan_id(gid_attr.ndev);
- memcpy(ah->av.eth.s_mac, gid_attr.ndev->dev_addr, ETH_ALEN);
- dev_put(gid_attr.ndev);
- }
+ if (is_vlan_dev(gid_attr.ndev))
+ vlan_tag = vlan_dev_vlan_id(gid_attr.ndev);
+ memcpy(ah->av.eth.s_mac, gid_attr.ndev->dev_addr, ETH_ALEN);
+ dev_put(gid_attr.ndev);
if (vlan_tag < 0x1000)
vlan_tag |= (rdma_ah_get_sl(ah_attr) & 7) << 13;
ah->av.eth.port_pd = cpu_to_be32(to_mpd(pd)->pdn |
diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c
index 5a0e4fc4785a..5b70744f414a 100644
--- a/drivers/infiniband/hw/mlx4/main.c
+++ b/drivers/infiniband/hw/mlx4/main.c
@@ -246,14 +246,11 @@ static int mlx4_ib_update_gids(struct gid_entry *gids,
return mlx4_ib_update_gids_v1(gids, ibdev, port_num);
}
-static int mlx4_ib_add_gid(struct ib_device *device,
- u8 port_num,
- unsigned int index,
- const union ib_gid *gid,
+static int mlx4_ib_add_gid(const union ib_gid *gid,
const struct ib_gid_attr *attr,
void **context)
{
- struct mlx4_ib_dev *ibdev = to_mdev(device);
+ struct mlx4_ib_dev *ibdev = to_mdev(attr->device);
struct mlx4_ib_iboe *iboe = &ibdev->iboe;
struct mlx4_port_gid_table *port_gid_table;
int free = -1, found = -1;
@@ -262,16 +259,16 @@ static int mlx4_ib_add_gid(struct ib_device *device,
int i;
struct gid_entry *gids = NULL;
- if (!rdma_cap_roce_gid_table(device, port_num))
+ if (!rdma_cap_roce_gid_table(attr->device, attr->port_num))
return -EINVAL;
- if (port_num > MLX4_MAX_PORTS)
+ if (attr->port_num > MLX4_MAX_PORTS)
return -EINVAL;
if (!context)
return -EINVAL;
- port_gid_table = &iboe->gids[port_num - 1];
+ port_gid_table = &iboe->gids[attr->port_num - 1];
spin_lock_bh(&iboe->lock);
for (i = 0; i < MLX4_MAX_PORT_GIDS; ++i) {
if (!memcmp(&port_gid_table->gids[i].gid, gid, sizeof(*gid)) &&
@@ -318,33 +315,30 @@ static int mlx4_ib_add_gid(struct ib_device *device,
spin_unlock_bh(&iboe->lock);
if (!ret && hw_update) {
- ret = mlx4_ib_update_gids(gids, ibdev, port_num);
+ ret = mlx4_ib_update_gids(gids, ibdev, attr->port_num);
kfree(gids);
}
return ret;
}
-static int mlx4_ib_del_gid(struct ib_device *device,
- u8 port_num,
- unsigned int index,
- void **context)
+static int mlx4_ib_del_gid(const struct ib_gid_attr *attr, void **context)
{
struct gid_cache_context *ctx = *context;
- struct mlx4_ib_dev *ibdev = to_mdev(device);
+ struct mlx4_ib_dev *ibdev = to_mdev(attr->device);
struct mlx4_ib_iboe *iboe = &ibdev->iboe;
struct mlx4_port_gid_table *port_gid_table;
int ret = 0;
int hw_update = 0;
struct gid_entry *gids = NULL;
- if (!rdma_cap_roce_gid_table(device, port_num))
+ if (!rdma_cap_roce_gid_table(attr->device, attr->port_num))
return -EINVAL;
- if (port_num > MLX4_MAX_PORTS)
+ if (attr->port_num > MLX4_MAX_PORTS)
return -EINVAL;
- port_gid_table = &iboe->gids[port_num - 1];
+ port_gid_table = &iboe->gids[attr->port_num - 1];
spin_lock_bh(&iboe->lock);
if (ctx) {
ctx->refcount--;
@@ -376,7 +370,7 @@ static int mlx4_ib_del_gid(struct ib_device *device,
spin_unlock_bh(&iboe->lock);
if (!ret && hw_update) {
- ret = mlx4_ib_update_gids(gids, ibdev, port_num);
+ ret = mlx4_ib_update_gids(gids, ibdev, attr->port_num);
kfree(gids);
}
return ret;
@@ -411,9 +405,6 @@ int mlx4_ib_gid_index_to_real_index(struct mlx4_ib_dev *ibdev,
if (attr.ndev)
dev_put(attr.ndev);
- if (!memcmp(&gid, &zgid, sizeof(gid)))
- return -EINVAL;
-
spin_lock_irqsave(&iboe->lock, flags);
port_gid_table = &iboe->gids[port_num - 1];
@@ -429,6 +420,9 @@ int mlx4_ib_gid_index_to_real_index(struct mlx4_ib_dev *ibdev,
return real_index;
}
+#define field_avail(type, fld, sz) (offsetof(type, fld) + \
+ sizeof(((type *)0)->fld) <= (sz))
+
static int mlx4_ib_query_device(struct ib_device *ibdev,
struct ib_device_attr *props,
struct ib_udata *uhw)
@@ -556,14 +550,19 @@ static int mlx4_ib_query_device(struct ib_device *ibdev,
props->timestamp_mask = 0xFFFFFFFFFFFFULL;
props->max_ah = INT_MAX;
- if ((dev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_RSS) &&
- (mlx4_ib_port_link_layer(ibdev, 1) == IB_LINK_LAYER_ETHERNET ||
- mlx4_ib_port_link_layer(ibdev, 2) == IB_LINK_LAYER_ETHERNET)) {
- props->rss_caps.max_rwq_indirection_tables = props->max_qp;
- props->rss_caps.max_rwq_indirection_table_size =
- dev->dev->caps.max_rss_tbl_sz;
- props->rss_caps.supported_qpts = 1 << IB_QPT_RAW_PACKET;
- props->max_wq_type_rq = props->max_qp;
+ if (mlx4_ib_port_link_layer(ibdev, 1) == IB_LINK_LAYER_ETHERNET ||
+ mlx4_ib_port_link_layer(ibdev, 2) == IB_LINK_LAYER_ETHERNET) {
+ if (dev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_RSS) {
+ props->rss_caps.max_rwq_indirection_tables =
+ props->max_qp;
+ props->rss_caps.max_rwq_indirection_table_size =
+ dev->dev->caps.max_rss_tbl_sz;
+ props->rss_caps.supported_qpts = 1 << IB_QPT_RAW_PACKET;
+ props->max_wq_type_rq = props->max_qp;
+ }
+
+ if (dev->dev->caps.flags & MLX4_DEV_CAP_FLAG_FCS_KEEP)
+ props->raw_packet_caps |= IB_RAW_PACKET_CAP_SCATTER_FCS;
}
props->cq_caps.max_cq_moderation_count = MLX4_MAX_CQ_COUNT;
@@ -575,7 +574,7 @@ static int mlx4_ib_query_device(struct ib_device *ibdev,
if (uhw->outlen >= resp.response_length + sizeof(resp.hca_core_clock_offset)) {
resp.response_length += sizeof(resp.hca_core_clock_offset);
if (!err && !mlx4_is_slave(dev->dev)) {
- resp.comp_mask |= QUERY_DEVICE_RESP_MASK_TIMESTAMP;
+ resp.comp_mask |= MLX4_IB_QUERY_DEV_RESP_MASK_CORE_CLOCK_OFFSET;
resp.hca_core_clock_offset = clock_params.offset % PAGE_SIZE;
}
}
@@ -587,8 +586,7 @@ static int mlx4_ib_query_device(struct ib_device *ibdev,
sizeof(struct mlx4_wqe_data_seg);
}
- if (uhw->outlen >= resp.response_length + sizeof(resp.rss_caps)) {
- resp.response_length += sizeof(resp.rss_caps);
+ if (field_avail(typeof(resp), rss_caps, uhw->outlen)) {
if (props->rss_caps.supported_qpts) {
resp.rss_caps.rx_hash_function =
MLX4_IB_RX_HASH_FUNC_TOEPLITZ;
@@ -608,6 +606,22 @@ static int mlx4_ib_query_device(struct ib_device *ibdev,
resp.rss_caps.rx_hash_fields_mask |=
MLX4_IB_RX_HASH_INNER;
}
+ resp.response_length = offsetof(typeof(resp), rss_caps) +
+ sizeof(resp.rss_caps);
+ }
+
+ if (field_avail(typeof(resp), tso_caps, uhw->outlen)) {
+ if (dev->dev->caps.max_gso_sz &&
+ ((mlx4_ib_port_link_layer(ibdev, 1) ==
+ IB_LINK_LAYER_ETHERNET) ||
+ (mlx4_ib_port_link_layer(ibdev, 2) ==
+ IB_LINK_LAYER_ETHERNET))) {
+ resp.tso_caps.max_tso = dev->dev->caps.max_gso_sz;
+ resp.tso_caps.supported_qpts |=
+ 1 << IB_QPT_RAW_PACKET;
+ }
+ resp.response_length = offsetof(typeof(resp), tso_caps) +
+ sizeof(resp.tso_caps);
}
if (uhw->outlen) {
@@ -865,24 +879,9 @@ out:
static int mlx4_ib_query_gid(struct ib_device *ibdev, u8 port, int index,
union ib_gid *gid)
{
- int ret;
-
if (rdma_protocol_ib(ibdev, port))
return __mlx4_ib_query_gid(ibdev, port, index, gid, 0);
-
- if (!rdma_protocol_roce(ibdev, port))
- return -ENODEV;
-
- if (!rdma_cap_roce_gid_table(ibdev, port))
- return -ENODEV;
-
- ret = ib_get_cached_gid(ibdev, port, index, gid, NULL);
- if (ret == -EAGAIN) {
- memcpy(gid, &zgid, sizeof(*gid));
- return 0;
- }
-
- return ret;
+ return 0;
}
static int mlx4_ib_query_sl2vl(struct ib_device *ibdev, u8 port, u64 *sl2vl_tbl)
@@ -1330,7 +1329,7 @@ static struct ib_pd *mlx4_ib_alloc_pd(struct ib_device *ibdev,
struct mlx4_ib_pd *pd;
int err;
- pd = kmalloc(sizeof *pd, GFP_KERNEL);
+ pd = kzalloc(sizeof(*pd), GFP_KERNEL);
if (!pd)
return ERR_PTR(-ENOMEM);
@@ -1346,7 +1345,6 @@ static struct ib_pd *mlx4_ib_alloc_pd(struct ib_device *ibdev,
kfree(pd);
return ERR_PTR(-EFAULT);
}
-
return &pd->ibpd;
}
@@ -1860,6 +1858,9 @@ static struct ib_flow *mlx4_ib_create_flow(struct ib_qp *qp,
if (flow_attr->port < 1 || flow_attr->port > qp->device->phys_port_cnt)
return ERR_PTR(-EINVAL);
+ if (flow_attr->flags & ~IB_FLOW_ATTR_FLAGS_DONT_TRAP)
+ return ERR_PTR(-EOPNOTSUPP);
+
if ((flow_attr->flags & IB_FLOW_ATTR_FLAGS_DONT_TRAP) &&
(flow_attr->type != IB_FLOW_ATTR_NORMAL))
return ERR_PTR(-EOPNOTSUPP);
@@ -2933,6 +2934,7 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
if (mlx4_ib_alloc_diag_counters(ibdev))
goto err_steer_free_bitmap;
+ ibdev->ib_dev.driver_id = RDMA_DRIVER_MLX4;
if (ib_register_device(&ibdev->ib_dev, NULL))
goto err_diag_counters;
diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h
index e14919c15b06..7b1429917aba 100644
--- a/drivers/infiniband/hw/mlx4/mlx4_ib.h
+++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h
@@ -189,6 +189,7 @@ enum mlx4_ib_qp_flags {
MLX4_IB_QP_LSO = IB_QP_CREATE_IPOIB_UD_LSO,
MLX4_IB_QP_BLOCK_MULTICAST_LOOPBACK = IB_QP_CREATE_BLOCK_MULTICAST_LOOPBACK,
MLX4_IB_QP_NETIF = IB_QP_CREATE_NETIF_QP,
+ MLX4_IB_QP_SCATTER_FCS = IB_QP_CREATE_SCATTER_FCS,
/* Mellanox specific flags start from IB_QP_CREATE_RESERVED_START */
MLX4_IB_ROCE_V2_GSI_QP = MLX4_IB_QP_CREATE_ROCE_V2_GSI,
@@ -641,24 +642,6 @@ struct mlx4_uverbs_ex_query_device {
__u32 reserved;
};
-enum query_device_resp_mask {
- QUERY_DEVICE_RESP_MASK_TIMESTAMP = 1UL << 0,
-};
-
-struct mlx4_ib_rss_caps {
- __u64 rx_hash_fields_mask; /* enum mlx4_rx_hash_fields */
- __u8 rx_hash_function; /* enum mlx4_rx_hash_function_flags */
- __u8 reserved[7];
-};
-
-struct mlx4_uverbs_ex_query_device_resp {
- __u32 comp_mask;
- __u32 response_length;
- __u64 hca_core_clock_offset;
- __u32 max_inl_recv_sz;
- struct mlx4_ib_rss_caps rss_caps;
-};
-
static inline struct mlx4_ib_dev *to_mdev(struct ib_device *ibdev)
{
return container_of(ibdev, struct mlx4_ib_dev, ib_dev);
diff --git a/drivers/infiniband/hw/mlx4/mr.c b/drivers/infiniband/hw/mlx4/mr.c
index 4975f3e6596e..17f4f151a97f 100644
--- a/drivers/infiniband/hw/mlx4/mr.c
+++ b/drivers/infiniband/hw/mlx4/mr.c
@@ -407,6 +407,9 @@ struct ib_mr *mlx4_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
goto err_mr;
mr->ibmr.rkey = mr->ibmr.lkey = mr->mmr.key;
+ mr->ibmr.length = length;
+ mr->ibmr.iova = virt_addr;
+ mr->ibmr.page_size = 1U << shift;
return &mr->ibmr;
diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c
index f045491f2c14..50af8915e7ec 100644
--- a/drivers/infiniband/hw/mlx4/qp.c
+++ b/drivers/infiniband/hw/mlx4/qp.c
@@ -1096,6 +1096,17 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd,
qp->inl_recv_sz = ucmd.qp.inl_recv_sz;
}
+ if (init_attr->create_flags & IB_QP_CREATE_SCATTER_FCS) {
+ if (!(dev->dev->caps.flags &
+ MLX4_DEV_CAP_FLAG_FCS_KEEP)) {
+ pr_debug("scatter FCS is unsupported\n");
+ err = -EOPNOTSUPP;
+ goto err;
+ }
+
+ qp->flags |= MLX4_IB_QP_SCATTER_FCS;
+ }
+
err = set_rq_size(dev, &init_attr->cap, !!pd->uobject,
qp_has_rq(init_attr), qp, qp->inl_recv_sz);
if (err)
@@ -2234,6 +2245,9 @@ static int __mlx4_ib_modify_qp(void *src, enum mlx4_ib_source_type src_type,
if (qp->inl_recv_sz)
context->param3 |= cpu_to_be32(1 << 25);
+ if (qp->flags & MLX4_IB_QP_SCATTER_FCS)
+ context->param3 |= cpu_to_be32(1 << 29);
+
if (qp_type == IB_QPT_GSI || qp_type == IB_QPT_SMI)
context->mtu_msgmax = (IB_MTU_4096 << 5) | 11;
else if (qp_type == IB_QPT_RAW_PACKET)
@@ -2356,9 +2370,7 @@ static int __mlx4_ib_modify_qp(void *src, enum mlx4_ib_source_type src_type,
status = ib_get_cached_gid(&dev->ib_dev, port_num,
index, &gid, &gid_attr);
- if (!status && !memcmp(&gid, &zgid, sizeof(gid)))
- status = -ENOENT;
- if (!status && gid_attr.ndev) {
+ if (!status) {
vlan = rdma_vlan_dev_vlan_id(gid_attr.ndev);
memcpy(smac, gid_attr.ndev->dev_addr, ETH_ALEN);
dev_put(gid_attr.ndev);
@@ -3880,8 +3892,8 @@ out:
*/
wmb();
- writel(qp->doorbell_qpn,
- to_mdev(ibqp->device)->uar_map + MLX4_SEND_DOORBELL);
+ writel_relaxed(qp->doorbell_qpn,
+ to_mdev(ibqp->device)->uar_map + MLX4_SEND_DOORBELL);
/*
* Make sure doorbells don't leak out of SQ spinlock
@@ -4204,7 +4216,7 @@ struct ib_wq *mlx4_ib_create_wq(struct ib_pd *pd,
return ERR_PTR(-EOPNOTSUPP);
}
- if (init_attr->create_flags) {
+ if (init_attr->create_flags & ~IB_WQ_FLAGS_SCATTER_FCS) {
pr_debug("unsupported create_flags %u\n",
init_attr->create_flags);
return ERR_PTR(-EOPNOTSUPP);
@@ -4225,6 +4237,9 @@ struct ib_wq *mlx4_ib_create_wq(struct ib_pd *pd,
ib_qp_init_attr.recv_cq = init_attr->cq;
ib_qp_init_attr.send_cq = ib_qp_init_attr.recv_cq; /* Dummy CQ */
+ if (init_attr->create_flags & IB_WQ_FLAGS_SCATTER_FCS)
+ ib_qp_init_attr.create_flags |= IB_QP_CREATE_SCATTER_FCS;
+
err = create_qp_common(dev, pd, MLX4_IB_RWQ_SRC, &ib_qp_init_attr,
udata, 0, &qp);
if (err) {
diff --git a/drivers/infiniband/hw/mlx5/Makefile b/drivers/infiniband/hw/mlx5/Makefile
index bc6299697dda..d42b922bede8 100644
--- a/drivers/infiniband/hw/mlx5/Makefile
+++ b/drivers/infiniband/hw/mlx5/Makefile
@@ -2,3 +2,4 @@ obj-$(CONFIG_MLX5_INFINIBAND) += mlx5_ib.o
mlx5_ib-y := main.o cq.o doorbell.o qp.o mem.o srq.o mr.o ah.o mad.o gsi.o ib_virt.o cmd.o cong.o
mlx5_ib-$(CONFIG_INFINIBAND_ON_DEMAND_PAGING) += odp.o
+mlx5_ib-$(CONFIG_MLX5_ESWITCH) += ib_rep.o
diff --git a/drivers/infiniband/hw/mlx5/ah.c b/drivers/infiniband/hw/mlx5/ah.c
index fe269f680103..e6bde32a83f3 100644
--- a/drivers/infiniband/hw/mlx5/ah.c
+++ b/drivers/infiniband/hw/mlx5/ah.c
@@ -36,6 +36,9 @@ static struct ib_ah *create_ib_ah(struct mlx5_ib_dev *dev,
struct mlx5_ib_ah *ah,
struct rdma_ah_attr *ah_attr)
{
+ enum ib_gid_type gid_type;
+ int err;
+
if (rdma_ah_get_ah_flags(ah_attr) & IB_AH_GRH) {
const struct ib_global_route *grh = rdma_ah_read_grh(ah_attr);
@@ -50,6 +53,12 @@ static struct ib_ah *create_ib_ah(struct mlx5_ib_dev *dev,
ah->av.stat_rate_sl = (rdma_ah_get_static_rate(ah_attr) << 4);
if (ah_attr->type == RDMA_AH_ATTR_TYPE_ROCE) {
+ err = mlx5_get_roce_gid_type(dev, ah_attr->port_num,
+ ah_attr->grh.sgid_index,
+ &gid_type);
+ if (err)
+ return ERR_PTR(err);
+
memcpy(ah->av.rmac, ah_attr->roce.dmac,
sizeof(ah_attr->roce.dmac));
ah->av.udp_sport =
@@ -57,6 +66,9 @@ static struct ib_ah *create_ib_ah(struct mlx5_ib_dev *dev,
rdma_ah_get_port_num(ah_attr),
rdma_ah_read_grh(ah_attr)->sgid_index);
ah->av.stat_rate_sl |= (rdma_ah_get_sl(ah_attr) & 0x7) << 1;
+ if (gid_type == IB_GID_TYPE_ROCE_UDP_ENCAP)
+#define MLX5_ECN_ENABLED BIT(1)
+ ah->av.tclass |= MLX5_ECN_ENABLED;
} else {
ah->av.rlid = cpu_to_be16(rdma_ah_get_dlid(ah_attr));
ah->av.fl_mlid = rdma_ah_get_path_bits(ah_attr) & 0x7f;
diff --git a/drivers/infiniband/hw/mlx5/cmd.c b/drivers/infiniband/hw/mlx5/cmd.c
index 6f6712f87a73..188512bf46e6 100644
--- a/drivers/infiniband/hw/mlx5/cmd.c
+++ b/drivers/infiniband/hw/mlx5/cmd.c
@@ -66,3 +66,107 @@ int mlx5_cmd_modify_cong_params(struct mlx5_core_dev *dev,
return mlx5_cmd_exec(dev, in, in_size, out, sizeof(out));
}
+
+int mlx5_cmd_alloc_memic(struct mlx5_memic *memic, phys_addr_t *addr,
+ u64 length, u32 alignment)
+{
+ struct mlx5_core_dev *dev = memic->dev;
+ u64 num_memic_hw_pages = MLX5_CAP_DEV_MEM(dev, memic_bar_size)
+ >> PAGE_SHIFT;
+ u64 hw_start_addr = MLX5_CAP64_DEV_MEM(dev, memic_bar_start_addr);
+ u32 max_alignment = MLX5_CAP_DEV_MEM(dev, log_max_memic_addr_alignment);
+ u32 num_pages = DIV_ROUND_UP(length, PAGE_SIZE);
+ u32 out[MLX5_ST_SZ_DW(alloc_memic_out)] = {};
+ u32 in[MLX5_ST_SZ_DW(alloc_memic_in)] = {};
+ u32 mlx5_alignment;
+ u64 page_idx = 0;
+ int ret = 0;
+
+ if (!length || (length & MLX5_MEMIC_ALLOC_SIZE_MASK))
+ return -EINVAL;
+
+ /* mlx5 device sets alignment as 64*2^driver_value
+ * so normalizing is needed.
+ */
+ mlx5_alignment = (alignment < MLX5_MEMIC_BASE_ALIGN) ? 0 :
+ alignment - MLX5_MEMIC_BASE_ALIGN;
+ if (mlx5_alignment > max_alignment)
+ return -EINVAL;
+
+ MLX5_SET(alloc_memic_in, in, opcode, MLX5_CMD_OP_ALLOC_MEMIC);
+ MLX5_SET(alloc_memic_in, in, range_size, num_pages * PAGE_SIZE);
+ MLX5_SET(alloc_memic_in, in, memic_size, length);
+ MLX5_SET(alloc_memic_in, in, log_memic_addr_alignment,
+ mlx5_alignment);
+
+ while (page_idx < num_memic_hw_pages) {
+ spin_lock(&memic->memic_lock);
+ page_idx = bitmap_find_next_zero_area(memic->memic_alloc_pages,
+ num_memic_hw_pages,
+ page_idx,
+ num_pages, 0);
+
+ if (page_idx < num_memic_hw_pages)
+ bitmap_set(memic->memic_alloc_pages,
+ page_idx, num_pages);
+
+ spin_unlock(&memic->memic_lock);
+
+ if (page_idx >= num_memic_hw_pages)
+ break;
+
+ MLX5_SET64(alloc_memic_in, in, range_start_addr,
+ hw_start_addr + (page_idx * PAGE_SIZE));
+
+ ret = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+ if (ret) {
+ spin_lock(&memic->memic_lock);
+ bitmap_clear(memic->memic_alloc_pages,
+ page_idx, num_pages);
+ spin_unlock(&memic->memic_lock);
+
+ if (ret == -EAGAIN) {
+ page_idx++;
+ continue;
+ }
+
+ return ret;
+ }
+
+ *addr = pci_resource_start(dev->pdev, 0) +
+ MLX5_GET64(alloc_memic_out, out, memic_start_addr);
+
+ return 0;
+ }
+
+ return -ENOMEM;
+}
+
+int mlx5_cmd_dealloc_memic(struct mlx5_memic *memic, u64 addr, u64 length)
+{
+ struct mlx5_core_dev *dev = memic->dev;
+ u64 hw_start_addr = MLX5_CAP64_DEV_MEM(dev, memic_bar_start_addr);
+ u32 num_pages = DIV_ROUND_UP(length, PAGE_SIZE);
+ u32 out[MLX5_ST_SZ_DW(dealloc_memic_out)] = {0};
+ u32 in[MLX5_ST_SZ_DW(dealloc_memic_in)] = {0};
+ u64 start_page_idx;
+ int err;
+
+ addr -= pci_resource_start(dev->pdev, 0);
+ start_page_idx = (addr - hw_start_addr) >> PAGE_SHIFT;
+
+ MLX5_SET(dealloc_memic_in, in, opcode, MLX5_CMD_OP_DEALLOC_MEMIC);
+ MLX5_SET64(dealloc_memic_in, in, memic_start_addr, addr);
+ MLX5_SET(dealloc_memic_in, in, memic_size, length);
+
+ err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+
+ if (!err) {
+ spin_lock(&memic->memic_lock);
+ bitmap_clear(memic->memic_alloc_pages,
+ start_page_idx, num_pages);
+ spin_unlock(&memic->memic_lock);
+ }
+
+ return err;
+}
diff --git a/drivers/infiniband/hw/mlx5/cmd.h b/drivers/infiniband/hw/mlx5/cmd.h
index 78ffded7cc2c..e7206c8a8011 100644
--- a/drivers/infiniband/hw/mlx5/cmd.h
+++ b/drivers/infiniband/hw/mlx5/cmd.h
@@ -33,6 +33,7 @@
#ifndef MLX5_IB_CMD_H
#define MLX5_IB_CMD_H
+#include "mlx5_ib.h"
#include <linux/kernel.h>
#include <linux/mlx5/driver.h>
@@ -41,4 +42,7 @@ int mlx5_cmd_query_cong_params(struct mlx5_core_dev *dev, int cong_point,
void *out, int out_size);
int mlx5_cmd_modify_cong_params(struct mlx5_core_dev *mdev,
void *in, int in_size);
+int mlx5_cmd_alloc_memic(struct mlx5_memic *memic, phys_addr_t *addr,
+ u64 length, u32 alignment);
+int mlx5_cmd_dealloc_memic(struct mlx5_memic *memic, u64 addr, u64 length);
#endif /* MLX5_IB_CMD_H */
diff --git a/drivers/infiniband/hw/mlx5/cq.c b/drivers/infiniband/hw/mlx5/cq.c
index 15457c9569a7..77d257ec899b 100644
--- a/drivers/infiniband/hw/mlx5/cq.c
+++ b/drivers/infiniband/hw/mlx5/cq.c
@@ -64,14 +64,9 @@ static void mlx5_ib_cq_event(struct mlx5_core_cq *mcq, enum mlx5_event type)
}
}
-static void *get_cqe_from_buf(struct mlx5_ib_cq_buf *buf, int n, int size)
-{
- return mlx5_buf_offset(&buf->buf, n * size);
-}
-
static void *get_cqe(struct mlx5_ib_cq *cq, int n)
{
- return get_cqe_from_buf(&cq->buf, n, cq->mcq.cqe_sz);
+ return mlx5_frag_buf_get_wqe(&cq->buf.fbc, n);
}
static u8 sw_ownership_bit(int n, int nent)
@@ -272,14 +267,8 @@ static void handle_responder(struct ib_wc *wc, struct mlx5_cqe64 *cqe,
static void dump_cqe(struct mlx5_ib_dev *dev, struct mlx5_err_cqe *cqe)
{
- __be32 *p = (__be32 *)cqe;
- int i;
-
mlx5_ib_warn(dev, "dump error cqe\n");
- for (i = 0; i < sizeof(*cqe) / 16; i++, p += 4)
- pr_info("%08x %08x %08x %08x\n", be32_to_cpu(p[0]),
- be32_to_cpu(p[1]), be32_to_cpu(p[2]),
- be32_to_cpu(p[3]));
+ mlx5_dump_err_cqe(dev->mdev, cqe);
}
static void mlx5_handle_error_cqe(struct mlx5_ib_dev *dev,
@@ -404,7 +393,7 @@ static void handle_atomics(struct mlx5_ib_qp *qp, struct mlx5_cqe64 *cqe64,
static void free_cq_buf(struct mlx5_ib_dev *dev, struct mlx5_ib_cq_buf *buf)
{
- mlx5_buf_free(dev->mdev, &buf->buf);
+ mlx5_frag_buf_free(dev->mdev, &buf->fbc.frag_buf);
}
static void get_sig_err_item(struct mlx5_sig_err_cqe *cqe,
@@ -725,12 +714,25 @@ int mlx5_ib_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags)
return ret;
}
-static int alloc_cq_buf(struct mlx5_ib_dev *dev, struct mlx5_ib_cq_buf *buf,
- int nent, int cqe_size)
+static int alloc_cq_frag_buf(struct mlx5_ib_dev *dev,
+ struct mlx5_ib_cq_buf *buf,
+ int nent,
+ int cqe_size)
{
+ struct mlx5_frag_buf_ctrl *c = &buf->fbc;
+ struct mlx5_frag_buf *frag_buf = &c->frag_buf;
+ u32 cqc_buff[MLX5_ST_SZ_DW(cqc)] = {0};
int err;
- err = mlx5_buf_alloc(dev->mdev, nent * cqe_size, &buf->buf);
+ MLX5_SET(cqc, cqc_buff, log_cq_size, ilog2(cqe_size));
+ MLX5_SET(cqc, cqc_buff, cqe_sz, (cqe_size == 128) ? 1 : 0);
+
+ mlx5_core_init_cq_frag_buf(&buf->fbc, cqc_buff);
+
+ err = mlx5_frag_buf_alloc_node(dev->mdev,
+ nent * cqe_size,
+ frag_buf,
+ dev->mdev->priv.numa_node);
if (err)
return err;
@@ -863,14 +865,15 @@ static void destroy_cq_user(struct mlx5_ib_cq *cq, struct ib_ucontext *context)
ib_umem_release(cq->buf.umem);
}
-static void init_cq_buf(struct mlx5_ib_cq *cq, struct mlx5_ib_cq_buf *buf)
+static void init_cq_frag_buf(struct mlx5_ib_cq *cq,
+ struct mlx5_ib_cq_buf *buf)
{
int i;
void *cqe;
struct mlx5_cqe64 *cqe64;
for (i = 0; i < buf->nent; i++) {
- cqe = get_cqe_from_buf(buf, i, buf->cqe_size);
+ cqe = get_cqe(cq, i);
cqe64 = buf->cqe_size == 64 ? cqe : cqe + 64;
cqe64->op_own = MLX5_CQE_INVALID << 4;
}
@@ -892,14 +895,15 @@ static int create_cq_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_cq *cq,
cq->mcq.arm_db = cq->db.db + 1;
cq->mcq.cqe_sz = cqe_size;
- err = alloc_cq_buf(dev, &cq->buf, entries, cqe_size);
+ err = alloc_cq_frag_buf(dev, &cq->buf, entries, cqe_size);
if (err)
goto err_db;
- init_cq_buf(cq, &cq->buf);
+ init_cq_frag_buf(cq, &cq->buf);
*inlen = MLX5_ST_SZ_BYTES(create_cq_in) +
- MLX5_FLD_SZ_BYTES(create_cq_in, pas[0]) * cq->buf.buf.npages;
+ MLX5_FLD_SZ_BYTES(create_cq_in, pas[0]) *
+ cq->buf.fbc.frag_buf.npages;
*cqb = kvzalloc(*inlen, GFP_KERNEL);
if (!*cqb) {
err = -ENOMEM;
@@ -907,11 +911,12 @@ static int create_cq_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_cq *cq,
}
pas = (__be64 *)MLX5_ADDR_OF(create_cq_in, *cqb, pas);
- mlx5_fill_page_array(&cq->buf.buf, pas);
+ mlx5_fill_page_frag_array(&cq->buf.fbc.frag_buf, pas);
cqc = MLX5_ADDR_OF(create_cq_in, *cqb, cq_context);
MLX5_SET(cqc, cqc, log_page_size,
- cq->buf.buf.page_shift - MLX5_ADAPTER_PAGE_SHIFT);
+ cq->buf.fbc.frag_buf.page_shift -
+ MLX5_ADAPTER_PAGE_SHIFT);
*index = dev->mdev->priv.uar->index;
@@ -1213,11 +1218,11 @@ static int resize_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_cq *cq,
if (!cq->resize_buf)
return -ENOMEM;
- err = alloc_cq_buf(dev, cq->resize_buf, entries, cqe_size);
+ err = alloc_cq_frag_buf(dev, cq->resize_buf, entries, cqe_size);
if (err)
goto ex;
- init_cq_buf(cq, cq->resize_buf);
+ init_cq_frag_buf(cq, cq->resize_buf);
return 0;
@@ -1262,9 +1267,8 @@ static int copy_resize_cqes(struct mlx5_ib_cq *cq)
}
while ((scqe64->op_own >> 4) != MLX5_CQE_RESIZE_CQ) {
- dcqe = get_cqe_from_buf(cq->resize_buf,
- (i + 1) & (cq->resize_buf->nent),
- dsize);
+ dcqe = mlx5_frag_buf_get_wqe(&cq->resize_buf->fbc,
+ (i + 1) & cq->resize_buf->nent);
dcqe64 = dsize == 64 ? dcqe : dcqe + 64;
sw_own = sw_ownership_bit(i + 1, cq->resize_buf->nent);
memcpy(dcqe, scqe, dsize);
@@ -1330,8 +1334,11 @@ int mlx5_ib_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *udata)
cqe_size = 64;
err = resize_kernel(dev, cq, entries, cqe_size);
if (!err) {
- npas = cq->resize_buf->buf.npages;
- page_shift = cq->resize_buf->buf.page_shift;
+ struct mlx5_frag_buf_ctrl *c;
+
+ c = &cq->resize_buf->fbc;
+ npas = c->frag_buf.npages;
+ page_shift = c->frag_buf.page_shift;
}
}
@@ -1352,7 +1359,8 @@ int mlx5_ib_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *udata)
mlx5_ib_populate_pas(dev, cq->resize_umem, page_shift,
pas, 0);
else
- mlx5_fill_page_array(&cq->resize_buf->buf, pas);
+ mlx5_fill_page_frag_array(&cq->resize_buf->fbc.frag_buf,
+ pas);
MLX5_SET(modify_cq_in, in,
modify_field_select_resize_field_select.resize_field_select.resize_field_select,
diff --git a/drivers/infiniband/hw/mlx5/ib_rep.c b/drivers/infiniband/hw/mlx5/ib_rep.c
new file mode 100644
index 000000000000..0e04fdddf670
--- /dev/null
+++ b/drivers/infiniband/hw/mlx5/ib_rep.c
@@ -0,0 +1,192 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
+/*
+ * Copyright (c) 2018 Mellanox Technologies. All rights reserved.
+ */
+
+#include "ib_rep.h"
+
+static const struct mlx5_ib_profile rep_profile = {
+ STAGE_CREATE(MLX5_IB_STAGE_INIT,
+ mlx5_ib_stage_init_init,
+ mlx5_ib_stage_init_cleanup),
+ STAGE_CREATE(MLX5_IB_STAGE_FLOW_DB,
+ mlx5_ib_stage_rep_flow_db_init,
+ NULL),
+ STAGE_CREATE(MLX5_IB_STAGE_CAPS,
+ mlx5_ib_stage_caps_init,
+ NULL),
+ STAGE_CREATE(MLX5_IB_STAGE_NON_DEFAULT_CB,
+ mlx5_ib_stage_rep_non_default_cb,
+ NULL),
+ STAGE_CREATE(MLX5_IB_STAGE_ROCE,
+ mlx5_ib_stage_rep_roce_init,
+ mlx5_ib_stage_rep_roce_cleanup),
+ STAGE_CREATE(MLX5_IB_STAGE_DEVICE_RESOURCES,
+ mlx5_ib_stage_dev_res_init,
+ mlx5_ib_stage_dev_res_cleanup),
+ STAGE_CREATE(MLX5_IB_STAGE_COUNTERS,
+ mlx5_ib_stage_counters_init,
+ mlx5_ib_stage_counters_cleanup),
+ STAGE_CREATE(MLX5_IB_STAGE_BFREG,
+ mlx5_ib_stage_bfrag_init,
+ mlx5_ib_stage_bfrag_cleanup),
+ STAGE_CREATE(MLX5_IB_STAGE_PRE_IB_REG_UMR,
+ NULL,
+ mlx5_ib_stage_pre_ib_reg_umr_cleanup),
+ STAGE_CREATE(MLX5_IB_STAGE_IB_REG,
+ mlx5_ib_stage_ib_reg_init,
+ mlx5_ib_stage_ib_reg_cleanup),
+ STAGE_CREATE(MLX5_IB_STAGE_POST_IB_REG_UMR,
+ mlx5_ib_stage_post_ib_reg_umr_init,
+ NULL),
+ STAGE_CREATE(MLX5_IB_STAGE_CLASS_ATTR,
+ mlx5_ib_stage_class_attr_init,
+ NULL),
+};
+
+static int
+mlx5_ib_nic_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
+{
+ return 0;
+}
+
+static void
+mlx5_ib_nic_rep_unload(struct mlx5_eswitch_rep *rep)
+{
+ rep->rep_if[REP_IB].priv = NULL;
+}
+
+static int
+mlx5_ib_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
+{
+ struct mlx5_ib_dev *ibdev;
+
+ ibdev = (struct mlx5_ib_dev *)ib_alloc_device(sizeof(*ibdev));
+ if (!ibdev)
+ return -ENOMEM;
+
+ ibdev->rep = rep;
+ ibdev->mdev = dev;
+ ibdev->num_ports = max(MLX5_CAP_GEN(dev, num_ports),
+ MLX5_CAP_GEN(dev, num_vhca_ports));
+ if (!__mlx5_ib_add(ibdev, &rep_profile))
+ return -EINVAL;
+
+ rep->rep_if[REP_IB].priv = ibdev;
+
+ return 0;
+}
+
+static void
+mlx5_ib_vport_rep_unload(struct mlx5_eswitch_rep *rep)
+{
+ struct mlx5_ib_dev *dev;
+
+ if (!rep->rep_if[REP_IB].priv)
+ return;
+
+ dev = mlx5_ib_rep_to_dev(rep);
+ __mlx5_ib_remove(dev, dev->profile, MLX5_IB_STAGE_MAX);
+ rep->rep_if[REP_IB].priv = NULL;
+}
+
+static void *mlx5_ib_vport_get_proto_dev(struct mlx5_eswitch_rep *rep)
+{
+ return mlx5_ib_rep_to_dev(rep);
+}
+
+static void mlx5_ib_rep_register_vf_vports(struct mlx5_ib_dev *dev)
+{
+ struct mlx5_eswitch *esw = dev->mdev->priv.eswitch;
+ int total_vfs = MLX5_TOTAL_VPORTS(dev->mdev);
+ int vport;
+
+ for (vport = 1; vport < total_vfs; vport++) {
+ struct mlx5_eswitch_rep_if rep_if = {};
+
+ rep_if.load = mlx5_ib_vport_rep_load;
+ rep_if.unload = mlx5_ib_vport_rep_unload;
+ rep_if.get_proto_dev = mlx5_ib_vport_get_proto_dev;
+ mlx5_eswitch_register_vport_rep(esw, vport, &rep_if, REP_IB);
+ }
+}
+
+static void mlx5_ib_rep_unregister_vf_vports(struct mlx5_ib_dev *dev)
+{
+ struct mlx5_eswitch *esw = dev->mdev->priv.eswitch;
+ int total_vfs = MLX5_TOTAL_VPORTS(dev->mdev);
+ int vport;
+
+ for (vport = 1; vport < total_vfs; vport++)
+ mlx5_eswitch_unregister_vport_rep(esw, vport, REP_IB);
+}
+
+void mlx5_ib_register_vport_reps(struct mlx5_ib_dev *dev)
+{
+ struct mlx5_eswitch *esw = dev->mdev->priv.eswitch;
+ struct mlx5_eswitch_rep_if rep_if = {};
+
+ rep_if.load = mlx5_ib_nic_rep_load;
+ rep_if.unload = mlx5_ib_nic_rep_unload;
+ rep_if.get_proto_dev = mlx5_ib_vport_get_proto_dev;
+ rep_if.priv = dev;
+
+ mlx5_eswitch_register_vport_rep(esw, 0, &rep_if, REP_IB);
+
+ mlx5_ib_rep_register_vf_vports(dev);
+}
+
+void mlx5_ib_unregister_vport_reps(struct mlx5_ib_dev *dev)
+{
+ struct mlx5_eswitch *esw = dev->mdev->priv.eswitch;
+
+ mlx5_ib_rep_unregister_vf_vports(dev); /* VFs vports */
+ mlx5_eswitch_unregister_vport_rep(esw, 0, REP_IB); /* UPLINK PF*/
+}
+
+u8 mlx5_ib_eswitch_mode(struct mlx5_eswitch *esw)
+{
+ return mlx5_eswitch_mode(esw);
+}
+
+struct mlx5_ib_dev *mlx5_ib_get_rep_ibdev(struct mlx5_eswitch *esw,
+ int vport_index)
+{
+ return mlx5_eswitch_get_proto_dev(esw, vport_index, REP_IB);
+}
+
+struct net_device *mlx5_ib_get_rep_netdev(struct mlx5_eswitch *esw,
+ int vport_index)
+{
+ return mlx5_eswitch_get_proto_dev(esw, vport_index, REP_ETH);
+}
+
+struct mlx5_ib_dev *mlx5_ib_get_uplink_ibdev(struct mlx5_eswitch *esw)
+{
+ return mlx5_eswitch_uplink_get_proto_dev(esw, REP_IB);
+}
+
+struct mlx5_eswitch_rep *mlx5_ib_vport_rep(struct mlx5_eswitch *esw, int vport)
+{
+ return mlx5_eswitch_vport_rep(esw, vport);
+}
+
+int create_flow_rule_vport_sq(struct mlx5_ib_dev *dev,
+ struct mlx5_ib_sq *sq)
+{
+ struct mlx5_flow_handle *flow_rule;
+ struct mlx5_eswitch *esw = dev->mdev->priv.eswitch;
+
+ if (!dev->rep)
+ return 0;
+
+ flow_rule =
+ mlx5_eswitch_add_send_to_vport_rule(esw,
+ dev->rep->vport,
+ sq->base.mqp.qpn);
+ if (IS_ERR(flow_rule))
+ return PTR_ERR(flow_rule);
+ sq->flow_rule = flow_rule;
+
+ return 0;
+}
diff --git a/drivers/infiniband/hw/mlx5/ib_rep.h b/drivers/infiniband/hw/mlx5/ib_rep.h
new file mode 100644
index 000000000000..046fd942fd46
--- /dev/null
+++ b/drivers/infiniband/hw/mlx5/ib_rep.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
+/*
+ * Copyright (c) 2018 Mellanox Technologies. All rights reserved.
+ */
+
+#ifndef __MLX5_IB_REP_H__
+#define __MLX5_IB_REP_H__
+
+#include <linux/mlx5/eswitch.h>
+#include "mlx5_ib.h"
+
+#ifdef CONFIG_MLX5_ESWITCH
+u8 mlx5_ib_eswitch_mode(struct mlx5_eswitch *esw);
+struct mlx5_ib_dev *mlx5_ib_get_rep_ibdev(struct mlx5_eswitch *esw,
+ int vport_index);
+struct mlx5_ib_dev *mlx5_ib_get_uplink_ibdev(struct mlx5_eswitch *esw);
+struct mlx5_eswitch_rep *mlx5_ib_vport_rep(struct mlx5_eswitch *esw,
+ int vport_index);
+void mlx5_ib_register_vport_reps(struct mlx5_ib_dev *dev);
+void mlx5_ib_unregister_vport_reps(struct mlx5_ib_dev *dev);
+int create_flow_rule_vport_sq(struct mlx5_ib_dev *dev,
+ struct mlx5_ib_sq *sq);
+struct net_device *mlx5_ib_get_rep_netdev(struct mlx5_eswitch *esw,
+ int vport_index);
+#else /* CONFIG_MLX5_ESWITCH */
+static inline u8 mlx5_ib_eswitch_mode(struct mlx5_eswitch *esw)
+{
+ return SRIOV_NONE;
+}
+
+static inline
+struct mlx5_ib_dev *mlx5_ib_get_rep_ibdev(struct mlx5_eswitch *esw,
+ int vport_index)
+{
+ return NULL;
+}
+
+static inline
+struct mlx5_ib_dev *mlx5_ib_get_uplink_ibdev(struct mlx5_eswitch *esw)
+{
+ return NULL;
+}
+
+static inline
+struct mlx5_eswitch_rep *mlx5_ib_vport_rep(struct mlx5_eswitch *esw,
+ int vport_index)
+{
+ return NULL;
+}
+
+static inline void mlx5_ib_register_vport_reps(struct mlx5_ib_dev *dev) {}
+static inline void mlx5_ib_unregister_vport_reps(struct mlx5_ib_dev *dev) {}
+static inline int create_flow_rule_vport_sq(struct mlx5_ib_dev *dev,
+ struct mlx5_ib_sq *sq)
+{
+ return 0;
+}
+
+static inline
+struct net_device *mlx5_ib_get_rep_netdev(struct mlx5_eswitch *esw,
+ int vport_index)
+{
+ return NULL;
+}
+#endif
+
+static inline
+struct mlx5_ib_dev *mlx5_ib_rep_to_dev(struct mlx5_eswitch_rep *rep)
+{
+ return (struct mlx5_ib_dev *)rep->rep_if[REP_IB].priv;
+}
+#endif /* __MLX5_IB_REP_H__ */
diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c
index da091de4e69d..daa919e5a442 100644
--- a/drivers/infiniband/hw/mlx5/main.c
+++ b/drivers/infiniband/hw/mlx5/main.c
@@ -38,6 +38,7 @@
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
+#include <linux/bitmap.h>
#if defined(CONFIG_X86)
#include <asm/pat.h>
#endif
@@ -51,13 +52,23 @@
#include <linux/mlx5/port.h>
#include <linux/mlx5/vport.h>
#include <linux/mlx5/fs.h>
+#include <linux/mlx5/fs_helpers.h>
#include <linux/list.h>
#include <rdma/ib_smi.h>
#include <rdma/ib_umem.h>
#include <linux/in.h>
#include <linux/etherdevice.h>
#include "mlx5_ib.h"
+#include "ib_rep.h"
#include "cmd.h"
+#include <linux/mlx5/fs_helpers.h>
+#include <linux/mlx5/accel.h>
+#include <rdma/uverbs_std_types.h>
+#include <rdma/mlx5_user_ioctl_verbs.h>
+#include <rdma/mlx5_user_ioctl_cmds.h>
+
+#define UVERBS_MODULE_NAME mlx5_ib
+#include <rdma/uverbs_named_ioctl.h>
#define DRIVER_NAME "mlx5_ib"
#define DRIVER_VERSION "5.0-0"
@@ -90,6 +101,12 @@ static LIST_HEAD(mlx5_ib_dev_list);
*/
static DEFINE_MUTEX(mlx5_ib_multiport_mutex);
+/* We can't use an array for xlt_emergency_page because dma_map_single
+ * doesn't work on kernel modules memory
+ */
+static unsigned long xlt_emergency_page;
+static struct mutex xlt_emergency_page_mutex;
+
struct mlx5_ib_dev *mlx5_ib_get_ibdev_from_mpi(struct mlx5_ib_multiport_info *mpi)
{
struct mlx5_ib_dev *dev;
@@ -130,7 +147,7 @@ static int get_port_state(struct ib_device *ibdev,
int ret;
memset(&attr, 0, sizeof(attr));
- ret = mlx5_ib_query_port(ibdev, port_num, &attr);
+ ret = ibdev->query_port(ibdev, port_num, &attr);
if (!ret)
*state = attr.state;
return ret;
@@ -154,10 +171,19 @@ static int mlx5_netdev_event(struct notifier_block *this,
case NETDEV_REGISTER:
case NETDEV_UNREGISTER:
write_lock(&roce->netdev_lock);
-
- if (ndev->dev.parent == &mdev->pdev->dev)
- roce->netdev = (event == NETDEV_UNREGISTER) ?
+ if (ibdev->rep) {
+ struct mlx5_eswitch *esw = ibdev->mdev->priv.eswitch;
+ struct net_device *rep_ndev;
+
+ rep_ndev = mlx5_ib_get_rep_netdev(esw,
+ ibdev->rep->vport);
+ if (rep_ndev == ndev)
+ roce->netdev = (event == NETDEV_UNREGISTER) ?
NULL : ndev;
+ } else if (ndev->dev.parent == &ibdev->mdev->pdev->dev) {
+ roce->netdev = (event == NETDEV_UNREGISTER) ?
+ NULL : ndev;
+ }
write_unlock(&roce->netdev_lock);
break;
@@ -388,6 +414,9 @@ static int mlx5_query_port_roce(struct ib_device *device, u8 port_num,
if (err)
goto out;
+ props->active_width = IB_WIDTH_4X;
+ props->active_speed = IB_SPEED_QDR;
+
translate_eth_proto_oper(eth_prot_oper, &props->active_speed,
&props->active_width);
@@ -482,18 +511,19 @@ static int set_roce_addr(struct mlx5_ib_dev *dev, u8 port_num,
vlan_id, port_num);
}
-static int mlx5_ib_add_gid(struct ib_device *device, u8 port_num,
- unsigned int index, const union ib_gid *gid,
+static int mlx5_ib_add_gid(const union ib_gid *gid,
const struct ib_gid_attr *attr,
__always_unused void **context)
{
- return set_roce_addr(to_mdev(device), port_num, index, gid, attr);
+ return set_roce_addr(to_mdev(attr->device), attr->port_num,
+ attr->index, gid, attr);
}
-static int mlx5_ib_del_gid(struct ib_device *device, u8 port_num,
- unsigned int index, __always_unused void **context)
+static int mlx5_ib_del_gid(const struct ib_gid_attr *attr,
+ __always_unused void **context)
{
- return set_roce_addr(to_mdev(device), port_num, index, NULL, NULL);
+ return set_roce_addr(to_mdev(attr->device), attr->port_num,
+ attr->index, NULL, NULL);
}
__be16 mlx5_get_roce_udp_sport(struct mlx5_ib_dev *dev, u8 port_num,
@@ -505,9 +535,6 @@ __be16 mlx5_get_roce_udp_sport(struct mlx5_ib_dev *dev, u8 port_num,
if (ib_get_cached_gid(&dev->ib_dev, port_num, index, &gid, &attr))
return 0;
- if (!attr.ndev)
- return 0;
-
dev_put(attr.ndev);
if (attr.gid_type != IB_GID_TYPE_ROCE_UDP_ENCAP)
@@ -527,9 +554,6 @@ int mlx5_get_roce_gid_type(struct mlx5_ib_dev *dev, u8 port_num,
if (ret)
return ret;
- if (!attr.ndev)
- return -ENODEV;
-
dev_put(attr.ndev);
*gid_type = attr.gid_type;
@@ -833,6 +857,10 @@ static int mlx5_ib_query_device(struct ib_device *ibdev,
MLX5_RX_HASH_SRC_PORT_UDP |
MLX5_RX_HASH_DST_PORT_UDP |
MLX5_RX_HASH_INNER;
+ if (mlx5_accel_ipsec_device_caps(dev->mdev) &
+ MLX5_ACCEL_IPSEC_CAP_DEVICE)
+ resp.rss_caps.rx_hash_fields_mask |=
+ MLX5_RX_HASH_IPSEC_SPI;
resp.response_length += sizeof(resp.rss_caps);
}
} else {
@@ -864,6 +892,11 @@ static int mlx5_ib_query_device(struct ib_device *ibdev,
props->raw_packet_caps |= IB_RAW_PACKET_CAP_SCATTER_FCS;
}
+ if (MLX5_CAP_DEV_MEM(mdev, memic)) {
+ props->max_dm_size =
+ MLX5_CAP_DEV_MEM(mdev, max_memic_size);
+ }
+
if (mlx5_get_flow_namespace(dev->mdev, MLX5_FLOW_NAMESPACE_BYPASS))
props->device_cap_flags |= IB_DEVICE_MANAGED_FLOW_STEERING;
@@ -969,6 +1002,10 @@ static int mlx5_ib_query_device(struct ib_device *ibdev,
MLX5_CAP_QOS(mdev, packet_pacing_min_rate);
resp.packet_pacing_caps.supported_qpts |=
1 << IB_QPT_RAW_PACKET;
+ if (MLX5_CAP_QOS(mdev, packet_pacing_burst_bound) &&
+ MLX5_CAP_QOS(mdev, packet_pacing_typical_size))
+ resp.packet_pacing_caps.cap_flags |=
+ MLX5_IB_PP_SUPPORT_BURST;
}
resp.response_length += sizeof(resp.packet_pacing_caps);
}
@@ -1272,6 +1309,22 @@ int mlx5_ib_query_port(struct ib_device *ibdev, u8 port,
return ret;
}
+static int mlx5_ib_rep_query_port(struct ib_device *ibdev, u8 port,
+ struct ib_port_attr *props)
+{
+ int ret;
+
+ /* Only link layer == ethernet is valid for representors */
+ ret = mlx5_query_port_roce(ibdev, port, props);
+ if (ret || !props)
+ return ret;
+
+ /* We don't support GIDS */
+ props->gid_tbl_len = 0;
+
+ return ret;
+}
+
static int mlx5_ib_query_gid(struct ib_device *ibdev, u8 port, int index,
union ib_gid *gid)
{
@@ -1638,6 +1691,18 @@ static struct ib_ucontext *mlx5_ib_alloc_ucontext(struct ib_device *ibdev,
resp.response_length = min(offsetof(typeof(resp), response_length) +
sizeof(resp.response_length), udata->outlen);
+ if (mlx5_accel_ipsec_device_caps(dev->mdev) & MLX5_ACCEL_IPSEC_CAP_DEVICE) {
+ if (mlx5_get_flow_namespace(dev->mdev, MLX5_FLOW_NAMESPACE_EGRESS))
+ resp.flow_action_flags |= MLX5_USER_ALLOC_UCONTEXT_FLOW_ACTION_FLAGS_ESP_AES_GCM;
+ if (mlx5_accel_ipsec_device_caps(dev->mdev) & MLX5_ACCEL_IPSEC_CAP_REQUIRED_METADATA)
+ resp.flow_action_flags |= MLX5_USER_ALLOC_UCONTEXT_FLOW_ACTION_FLAGS_ESP_AES_GCM_REQ_METADATA;
+ if (MLX5_CAP_FLOWTABLE(dev->mdev, flow_table_properties_nic_receive.ft_field_support.outer_esp_spi))
+ resp.flow_action_flags |= MLX5_USER_ALLOC_UCONTEXT_FLOW_ACTION_FLAGS_ESP_AES_GCM_SPI_STEERING;
+ if (mlx5_accel_ipsec_device_caps(dev->mdev) & MLX5_ACCEL_IPSEC_CAP_TX_IV_IS_ESN)
+ resp.flow_action_flags |= MLX5_USER_ALLOC_UCONTEXT_FLOW_ACTION_FLAGS_ESP_AES_GCM_TX_IV_IS_ESN;
+ /* MLX5_USER_ALLOC_UCONTEXT_FLOW_ACTION_FLAGS_ESP_AES_GCM_FULL_OFFLOAD is currently always 0 */
+ }
+
context = kzalloc(sizeof(*context), GFP_KERNEL);
if (!context)
return ERR_PTR(-ENOMEM);
@@ -1675,17 +1740,10 @@ static struct ib_ucontext *mlx5_ib_alloc_ucontext(struct ib_device *ibdev,
context->ibucontext.invalidate_range = &mlx5_ib_invalidate_range;
#endif
- context->upd_xlt_page = __get_free_page(GFP_KERNEL);
- if (!context->upd_xlt_page) {
- err = -ENOMEM;
- goto out_uars;
- }
- mutex_init(&context->upd_xlt_page_mutex);
-
if (MLX5_CAP_GEN(dev->mdev, log_max_transport_domain)) {
err = mlx5_ib_alloc_transport_domain(dev, &context->tdn);
if (err)
- goto out_page;
+ goto out_uars;
}
INIT_LIST_HEAD(&context->vma_private_list);
@@ -1762,9 +1820,6 @@ out_td:
if (MLX5_CAP_GEN(dev->mdev, log_max_transport_domain))
mlx5_ib_dealloc_transport_domain(dev, context->tdn);
-out_page:
- free_page(context->upd_xlt_page);
-
out_uars:
deallocate_uars(dev, context);
@@ -1790,7 +1845,6 @@ static int mlx5_ib_dealloc_ucontext(struct ib_ucontext *ibcontext)
if (MLX5_CAP_GEN(dev->mdev, log_max_transport_domain))
mlx5_ib_dealloc_transport_domain(dev, context->tdn);
- free_page(context->upd_xlt_page);
deallocate_uars(dev, context);
kfree(bfregi->sys_pages);
kfree(bfregi->count);
@@ -1966,6 +2020,8 @@ static inline char *mmap_cmd2str(enum mlx5_ib_mmap_cmd cmd)
return "best effort WC";
case MLX5_IB_MMAP_NC_PAGE:
return "NC";
+ case MLX5_IB_MMAP_DEVICE_MEM:
+ return "Device Memory";
default:
return NULL;
}
@@ -2124,6 +2180,34 @@ free_bfreg:
return err;
}
+static int dm_mmap(struct ib_ucontext *context, struct vm_area_struct *vma)
+{
+ struct mlx5_ib_ucontext *mctx = to_mucontext(context);
+ struct mlx5_ib_dev *dev = to_mdev(context->device);
+ u16 page_idx = get_extended_index(vma->vm_pgoff);
+ size_t map_size = vma->vm_end - vma->vm_start;
+ u32 npages = map_size >> PAGE_SHIFT;
+ phys_addr_t pfn;
+ pgprot_t prot;
+
+ if (find_next_zero_bit(mctx->dm_pages, page_idx + npages, page_idx) !=
+ page_idx + npages)
+ return -EINVAL;
+
+ pfn = ((pci_resource_start(dev->mdev->pdev, 0) +
+ MLX5_CAP64_DEV_MEM(dev->mdev, memic_bar_start_addr)) >>
+ PAGE_SHIFT) +
+ page_idx;
+ prot = pgprot_writecombine(vma->vm_page_prot);
+ vma->vm_page_prot = prot;
+
+ if (io_remap_pfn_range(vma, vma->vm_start, pfn, map_size,
+ vma->vm_page_prot))
+ return -EAGAIN;
+
+ return mlx5_ib_set_vma_data(vma, mctx);
+}
+
static int mlx5_ib_mmap(struct ib_ucontext *ibcontext, struct vm_area_struct *vma)
{
struct mlx5_ib_ucontext *context = to_mucontext(ibcontext);
@@ -2168,6 +2252,9 @@ static int mlx5_ib_mmap(struct ib_ucontext *ibcontext, struct vm_area_struct *vm
case MLX5_IB_MMAP_CLOCK_INFO:
return mlx5_ib_mmap_clock_info_page(dev, vma, context);
+ case MLX5_IB_MMAP_DEVICE_MEM:
+ return dm_mmap(ibcontext, vma);
+
default:
return -EINVAL;
}
@@ -2175,6 +2262,87 @@ static int mlx5_ib_mmap(struct ib_ucontext *ibcontext, struct vm_area_struct *vm
return 0;
}
+struct ib_dm *mlx5_ib_alloc_dm(struct ib_device *ibdev,
+ struct ib_ucontext *context,
+ struct ib_dm_alloc_attr *attr,
+ struct uverbs_attr_bundle *attrs)
+{
+ u64 act_size = roundup(attr->length, MLX5_MEMIC_BASE_SIZE);
+ struct mlx5_memic *memic = &to_mdev(ibdev)->memic;
+ phys_addr_t memic_addr;
+ struct mlx5_ib_dm *dm;
+ u64 start_offset;
+ u32 page_idx;
+ int err;
+
+ dm = kzalloc(sizeof(*dm), GFP_KERNEL);
+ if (!dm)
+ return ERR_PTR(-ENOMEM);
+
+ mlx5_ib_dbg(to_mdev(ibdev), "alloc_memic req: user_length=0x%llx act_length=0x%llx log_alignment=%d\n",
+ attr->length, act_size, attr->alignment);
+
+ err = mlx5_cmd_alloc_memic(memic, &memic_addr,
+ act_size, attr->alignment);
+ if (err)
+ goto err_free;
+
+ start_offset = memic_addr & ~PAGE_MASK;
+ page_idx = (memic_addr - pci_resource_start(memic->dev->pdev, 0) -
+ MLX5_CAP64_DEV_MEM(memic->dev, memic_bar_start_addr)) >>
+ PAGE_SHIFT;
+
+ err = uverbs_copy_to(attrs,
+ MLX5_IB_ATTR_ALLOC_DM_RESP_START_OFFSET,
+ &start_offset, sizeof(start_offset));
+ if (err)
+ goto err_dealloc;
+
+ err = uverbs_copy_to(attrs,
+ MLX5_IB_ATTR_ALLOC_DM_RESP_PAGE_INDEX,
+ &page_idx, sizeof(page_idx));
+ if (err)
+ goto err_dealloc;
+
+ bitmap_set(to_mucontext(context)->dm_pages, page_idx,
+ DIV_ROUND_UP(act_size, PAGE_SIZE));
+
+ dm->dev_addr = memic_addr;
+
+ return &dm->ibdm;
+
+err_dealloc:
+ mlx5_cmd_dealloc_memic(memic, memic_addr,
+ act_size);
+err_free:
+ kfree(dm);
+ return ERR_PTR(err);
+}
+
+int mlx5_ib_dealloc_dm(struct ib_dm *ibdm)
+{
+ struct mlx5_memic *memic = &to_mdev(ibdm->device)->memic;
+ struct mlx5_ib_dm *dm = to_mdm(ibdm);
+ u64 act_size = roundup(dm->ibdm.length, MLX5_MEMIC_BASE_SIZE);
+ u32 page_idx;
+ int ret;
+
+ ret = mlx5_cmd_dealloc_memic(memic, dm->dev_addr, act_size);
+ if (ret)
+ return ret;
+
+ page_idx = (dm->dev_addr - pci_resource_start(memic->dev->pdev, 0) -
+ MLX5_CAP64_DEV_MEM(memic->dev, memic_bar_start_addr)) >>
+ PAGE_SHIFT;
+ bitmap_clear(to_mucontext(ibdm->uobject->context)->dm_pages,
+ page_idx,
+ DIV_ROUND_UP(act_size, PAGE_SIZE));
+
+ kfree(dm);
+
+ return 0;
+}
+
static struct ib_pd *mlx5_ib_alloc_pd(struct ib_device *ibdev,
struct ib_ucontext *context,
struct ib_udata *udata)
@@ -2290,11 +2458,29 @@ static void set_tos(void *outer_c, void *outer_v, u8 mask, u8 val)
offsetof(typeof(filter), field) -\
sizeof(filter.field))
-#define IPV4_VERSION 4
-#define IPV6_VERSION 6
+static int parse_flow_flow_action(const union ib_flow_spec *ib_spec,
+ const struct ib_flow_attr *flow_attr,
+ struct mlx5_flow_act *action)
+{
+ struct mlx5_ib_flow_action *maction = to_mflow_act(ib_spec->action.act);
+
+ switch (maction->ib_action.type) {
+ case IB_FLOW_ACTION_ESP:
+ /* Currently only AES_GCM keymat is supported by the driver */
+ action->esp_id = (uintptr_t)maction->esp_aes_gcm.ctx;
+ action->action |= flow_attr->flags & IB_FLOW_ATTR_FLAGS_EGRESS ?
+ MLX5_FLOW_CONTEXT_ACTION_ENCRYPT :
+ MLX5_FLOW_CONTEXT_ACTION_DECRYPT;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c,
u32 *match_v, const union ib_flow_spec *ib_spec,
- u32 *tag_id, bool *is_drop)
+ const struct ib_flow_attr *flow_attr,
+ struct mlx5_flow_act *action)
{
void *misc_params_c = MLX5_ADDR_OF(fte_match_param, match_c,
misc_parameters);
@@ -2303,6 +2489,7 @@ static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c,
void *headers_c;
void *headers_v;
int match_ipv;
+ int ret;
if (ib_spec->type & IB_FLOW_SPEC_INNER) {
headers_c = MLX5_ADDR_OF(fte_match_param, match_c,
@@ -2377,7 +2564,7 @@ static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c,
MLX5_SET(fte_match_set_lyr_2_4, headers_c,
ip_version, 0xf);
MLX5_SET(fte_match_set_lyr_2_4, headers_v,
- ip_version, IPV4_VERSION);
+ ip_version, MLX5_FS_IPV4_VERSION);
} else {
MLX5_SET(fte_match_set_lyr_2_4, headers_c,
ethertype, 0xffff);
@@ -2416,7 +2603,7 @@ static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c,
MLX5_SET(fte_match_set_lyr_2_4, headers_c,
ip_version, 0xf);
MLX5_SET(fte_match_set_lyr_2_4, headers_v,
- ip_version, IPV6_VERSION);
+ ip_version, MLX5_FS_IPV6_VERSION);
} else {
MLX5_SET(fte_match_set_lyr_2_4, headers_c,
ethertype, 0xffff);
@@ -2453,7 +2640,15 @@ static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c,
ntohl(ib_spec->ipv6.mask.flow_label),
ntohl(ib_spec->ipv6.val.flow_label),
ib_spec->type & IB_FLOW_SPEC_INNER);
+ break;
+ case IB_FLOW_SPEC_ESP:
+ if (ib_spec->esp.mask.seq)
+ return -EOPNOTSUPP;
+ MLX5_SET(fte_match_set_misc, misc_params_c, outer_esp_spi,
+ ntohl(ib_spec->esp.mask.spi));
+ MLX5_SET(fte_match_set_misc, misc_params_v, outer_esp_spi,
+ ntohl(ib_spec->esp.val.spi));
break;
case IB_FLOW_SPEC_TCP:
if (FIELDS_NOT_SUPPORTED(ib_spec->tcp_udp.mask,
@@ -2512,13 +2707,19 @@ static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c,
if (ib_spec->flow_tag.tag_id >= BIT(24))
return -EINVAL;
- *tag_id = ib_spec->flow_tag.tag_id;
+ action->flow_tag = ib_spec->flow_tag.tag_id;
+ action->has_flow_tag = true;
break;
case IB_FLOW_SPEC_ACTION_DROP:
if (FIELDS_NOT_SUPPORTED(ib_spec->drop,
LAST_DROP_FIELD))
return -EOPNOTSUPP;
- *is_drop = true;
+ action->action |= MLX5_FLOW_CONTEXT_ACTION_DROP;
+ break;
+ case IB_FLOW_SPEC_ACTION_HANDLE:
+ ret = parse_flow_flow_action(ib_spec, flow_attr, action);
+ if (ret)
+ return ret;
break;
default:
return -EINVAL;
@@ -2561,6 +2762,46 @@ static bool flow_is_multicast_only(const struct ib_flow_attr *ib_attr)
return false;
}
+enum valid_spec {
+ VALID_SPEC_INVALID,
+ VALID_SPEC_VALID,
+ VALID_SPEC_NA,
+};
+
+static enum valid_spec
+is_valid_esp_aes_gcm(struct mlx5_core_dev *mdev,
+ const struct mlx5_flow_spec *spec,
+ const struct mlx5_flow_act *flow_act,
+ bool egress)
+{
+ const u32 *match_c = spec->match_criteria;
+ bool is_crypto =
+ (flow_act->action & (MLX5_FLOW_CONTEXT_ACTION_ENCRYPT |
+ MLX5_FLOW_CONTEXT_ACTION_DECRYPT));
+ bool is_ipsec = mlx5_fs_is_ipsec_flow(match_c);
+ bool is_drop = flow_act->action & MLX5_FLOW_CONTEXT_ACTION_DROP;
+
+ /*
+ * Currently only crypto is supported in egress, when regular egress
+ * rules would be supported, always return VALID_SPEC_NA.
+ */
+ if (!is_crypto)
+ return egress ? VALID_SPEC_INVALID : VALID_SPEC_NA;
+
+ return is_crypto && is_ipsec &&
+ (!egress || (!is_drop && !flow_act->has_flow_tag)) ?
+ VALID_SPEC_VALID : VALID_SPEC_INVALID;
+}
+
+static bool is_valid_spec(struct mlx5_core_dev *mdev,
+ const struct mlx5_flow_spec *spec,
+ const struct mlx5_flow_act *flow_act,
+ bool egress)
+{
+ /* We curretly only support ipsec egress flow */
+ return is_valid_esp_aes_gcm(mdev, spec, flow_act, egress) != VALID_SPEC_INVALID;
+}
+
static bool is_valid_ethertype(struct mlx5_core_dev *mdev,
const struct ib_flow_attr *flow_attr,
bool check_inner)
@@ -2635,7 +2876,7 @@ static int mlx5_ib_destroy_flow(struct ib_flow *flow_id)
ibflow);
struct mlx5_ib_flow_handler *iter, *tmp;
- mutex_lock(&dev->flow_db.lock);
+ mutex_lock(&dev->flow_db->lock);
list_for_each_entry_safe(iter, tmp, &handler->list, list) {
mlx5_del_flow_rules(iter->rule);
@@ -2646,7 +2887,7 @@ static int mlx5_ib_destroy_flow(struct ib_flow *flow_id)
mlx5_del_flow_rules(handler->rule);
put_flow_table(dev, handler->prio, true);
- mutex_unlock(&dev->flow_db.lock);
+ mutex_unlock(&dev->flow_db->lock);
kfree(handler);
@@ -2685,17 +2926,21 @@ static struct mlx5_ib_flow_prio *get_flow_table(struct mlx5_ib_dev *dev,
max_table_size = BIT(MLX5_CAP_FLOWTABLE_NIC_RX(dev->mdev,
log_max_ft_size));
if (flow_attr->type == IB_FLOW_ATTR_NORMAL) {
- if (flow_is_multicast_only(flow_attr) &&
- !dont_trap)
+ if (ft_type == MLX5_IB_FT_TX)
+ priority = 0;
+ else if (flow_is_multicast_only(flow_attr) &&
+ !dont_trap)
priority = MLX5_IB_FLOW_MCAST_PRIO;
else
priority = ib_prio_to_core_prio(flow_attr->priority,
dont_trap);
ns = mlx5_get_flow_namespace(dev->mdev,
+ ft_type == MLX5_IB_FT_TX ?
+ MLX5_FLOW_NAMESPACE_EGRESS :
MLX5_FLOW_NAMESPACE_BYPASS);
num_entries = MLX5_FS_MAX_ENTRIES;
num_groups = MLX5_FS_MAX_TYPES;
- prio = &dev->flow_db.prios[priority];
+ prio = &dev->flow_db->prios[priority];
} else if (flow_attr->type == IB_FLOW_ATTR_ALL_DEFAULT ||
flow_attr->type == IB_FLOW_ATTR_MC_DEFAULT) {
ns = mlx5_get_flow_namespace(dev->mdev,
@@ -2703,7 +2948,7 @@ static struct mlx5_ib_flow_prio *get_flow_table(struct mlx5_ib_dev *dev,
build_leftovers_ft_param(&priority,
&num_entries,
&num_groups);
- prio = &dev->flow_db.prios[MLX5_IB_FLOW_LEFTOVERS_PRIO];
+ prio = &dev->flow_db->prios[MLX5_IB_FLOW_LEFTOVERS_PRIO];
} else if (flow_attr->type == IB_FLOW_ATTR_SNIFFER) {
if (!MLX5_CAP_FLOWTABLE(dev->mdev,
allow_sniffer_and_nic_rx_shared_tir))
@@ -2713,7 +2958,7 @@ static struct mlx5_ib_flow_prio *get_flow_table(struct mlx5_ib_dev *dev,
MLX5_FLOW_NAMESPACE_SNIFFER_RX :
MLX5_FLOW_NAMESPACE_SNIFFER_TX);
- prio = &dev->flow_db.sniffer[ft_type];
+ prio = &dev->flow_db->sniffer[ft_type];
priority = 0;
num_entries = 1;
num_groups = 1;
@@ -2771,15 +3016,14 @@ static struct mlx5_ib_flow_handler *_create_flow_rule(struct mlx5_ib_dev *dev,
{
struct mlx5_flow_table *ft = ft_prio->flow_table;
struct mlx5_ib_flow_handler *handler;
- struct mlx5_flow_act flow_act = {0};
+ struct mlx5_flow_act flow_act = {.flow_tag = MLX5_FS_DEFAULT_FLOW_TAG};
struct mlx5_flow_spec *spec;
struct mlx5_flow_destination *rule_dst = dst;
const void *ib_flow = (const void *)flow_attr + sizeof(*flow_attr);
unsigned int spec_index;
- u32 flow_tag = MLX5_FS_DEFAULT_FLOW_TAG;
- bool is_drop = false;
int err = 0;
int dest_num = 1;
+ bool is_egress = flow_attr->flags & IB_FLOW_ATTR_FLAGS_EGRESS;
if (!is_valid_attr(dev->mdev, flow_attr))
return ERR_PTR(-EINVAL);
@@ -2796,7 +3040,7 @@ static struct mlx5_ib_flow_handler *_create_flow_rule(struct mlx5_ib_dev *dev,
for (spec_index = 0; spec_index < flow_attr->num_of_specs; spec_index++) {
err = parse_flow_attr(dev->mdev, spec->match_criteria,
spec->match_value,
- ib_flow, &flow_tag, &is_drop);
+ ib_flow, flow_attr, &flow_act);
if (err < 0)
goto free;
@@ -2806,25 +3050,46 @@ static struct mlx5_ib_flow_handler *_create_flow_rule(struct mlx5_ib_dev *dev,
if (!flow_is_multicast_only(flow_attr))
set_underlay_qp(dev, spec, underlay_qpn);
+ if (dev->rep) {
+ void *misc;
+
+ misc = MLX5_ADDR_OF(fte_match_param, spec->match_value,
+ misc_parameters);
+ MLX5_SET(fte_match_set_misc, misc, source_port,
+ dev->rep->vport);
+ misc = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
+ misc_parameters);
+ MLX5_SET_TO_ONES(fte_match_set_misc, misc, source_port);
+ }
+
spec->match_criteria_enable = get_match_criteria_enable(spec->match_criteria);
- if (is_drop) {
- flow_act.action = MLX5_FLOW_CONTEXT_ACTION_DROP;
+
+ if (is_egress &&
+ !is_valid_spec(dev->mdev, spec, &flow_act, is_egress)) {
+ err = -EINVAL;
+ goto free;
+ }
+
+ if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_DROP) {
rule_dst = NULL;
dest_num = 0;
} else {
- flow_act.action = dst ? MLX5_FLOW_CONTEXT_ACTION_FWD_DEST :
- MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO;
+ if (is_egress)
+ flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_ALLOW;
+ else
+ flow_act.action |=
+ dst ? MLX5_FLOW_CONTEXT_ACTION_FWD_DEST :
+ MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO;
}
- if (flow_tag != MLX5_FS_DEFAULT_FLOW_TAG &&
+ if (flow_act.has_flow_tag &&
(flow_attr->type == IB_FLOW_ATTR_ALL_DEFAULT ||
flow_attr->type == IB_FLOW_ATTR_MC_DEFAULT)) {
mlx5_ib_warn(dev, "Flow tag %u and attribute type %x isn't allowed in leftovers\n",
- flow_tag, flow_attr->type);
+ flow_act.flow_tag, flow_attr->type);
err = -EINVAL;
goto free;
}
- flow_act.flow_tag = flow_tag;
handler->rule = mlx5_add_flow_rules(ft, spec,
&flow_act,
rule_dst, dest_num);
@@ -2988,6 +3253,7 @@ static struct ib_flow *mlx5_ib_create_flow(struct ib_qp *qp,
struct mlx5_flow_destination *dst = NULL;
struct mlx5_ib_flow_prio *ft_prio_tx = NULL;
struct mlx5_ib_flow_prio *ft_prio;
+ bool is_egress = flow_attr->flags & IB_FLOW_ATTR_FLAGS_EGRESS;
int err;
int underlay_qpn;
@@ -2996,16 +3262,23 @@ static struct ib_flow *mlx5_ib_create_flow(struct ib_qp *qp,
if (domain != IB_FLOW_DOMAIN_USER ||
flow_attr->port > dev->num_ports ||
- (flow_attr->flags & ~IB_FLOW_ATTR_FLAGS_DONT_TRAP))
+ (flow_attr->flags & ~(IB_FLOW_ATTR_FLAGS_DONT_TRAP |
+ IB_FLOW_ATTR_FLAGS_EGRESS)))
+ return ERR_PTR(-EINVAL);
+
+ if (is_egress &&
+ (flow_attr->type == IB_FLOW_ATTR_ALL_DEFAULT ||
+ flow_attr->type == IB_FLOW_ATTR_MC_DEFAULT))
return ERR_PTR(-EINVAL);
dst = kzalloc(sizeof(*dst), GFP_KERNEL);
if (!dst)
return ERR_PTR(-ENOMEM);
- mutex_lock(&dev->flow_db.lock);
+ mutex_lock(&dev->flow_db->lock);
- ft_prio = get_flow_table(dev, flow_attr, MLX5_IB_FT_RX);
+ ft_prio = get_flow_table(dev, flow_attr,
+ is_egress ? MLX5_IB_FT_TX : MLX5_IB_FT_RX);
if (IS_ERR(ft_prio)) {
err = PTR_ERR(ft_prio);
goto unlock;
@@ -3019,11 +3292,15 @@ static struct ib_flow *mlx5_ib_create_flow(struct ib_qp *qp,
}
}
- dst->type = MLX5_FLOW_DESTINATION_TYPE_TIR;
- if (mqp->flags & MLX5_IB_QP_RSS)
- dst->tir_num = mqp->rss_qp.tirn;
- else
- dst->tir_num = mqp->raw_packet_qp.rq.tirn;
+ if (is_egress) {
+ dst->type = MLX5_FLOW_DESTINATION_TYPE_PORT;
+ } else {
+ dst->type = MLX5_FLOW_DESTINATION_TYPE_TIR;
+ if (mqp->flags & MLX5_IB_QP_RSS)
+ dst->tir_num = mqp->rss_qp.tirn;
+ else
+ dst->tir_num = mqp->raw_packet_qp.rq.tirn;
+ }
if (flow_attr->type == IB_FLOW_ATTR_NORMAL) {
if (flow_attr->flags & IB_FLOW_ATTR_FLAGS_DONT_TRAP) {
@@ -3052,7 +3329,7 @@ static struct ib_flow *mlx5_ib_create_flow(struct ib_qp *qp,
goto destroy_ft;
}
- mutex_unlock(&dev->flow_db.lock);
+ mutex_unlock(&dev->flow_db->lock);
kfree(dst);
return &handler->ibflow;
@@ -3062,12 +3339,176 @@ destroy_ft:
if (ft_prio_tx)
put_flow_table(dev, ft_prio_tx, false);
unlock:
- mutex_unlock(&dev->flow_db.lock);
+ mutex_unlock(&dev->flow_db->lock);
kfree(dst);
kfree(handler);
return ERR_PTR(err);
}
+static u32 mlx5_ib_flow_action_flags_to_accel_xfrm_flags(u32 mlx5_flags)
+{
+ u32 flags = 0;
+
+ if (mlx5_flags & MLX5_IB_UAPI_FLOW_ACTION_FLAGS_REQUIRE_METADATA)
+ flags |= MLX5_ACCEL_XFRM_FLAG_REQUIRE_METADATA;
+
+ return flags;
+}
+
+#define MLX5_FLOW_ACTION_ESP_CREATE_LAST_SUPPORTED MLX5_IB_UAPI_FLOW_ACTION_FLAGS_REQUIRE_METADATA
+static struct ib_flow_action *
+mlx5_ib_create_flow_action_esp(struct ib_device *device,
+ const struct ib_flow_action_attrs_esp *attr,
+ struct uverbs_attr_bundle *attrs)
+{
+ struct mlx5_ib_dev *mdev = to_mdev(device);
+ struct ib_uverbs_flow_action_esp_keymat_aes_gcm *aes_gcm;
+ struct mlx5_accel_esp_xfrm_attrs accel_attrs = {};
+ struct mlx5_ib_flow_action *action;
+ u64 action_flags;
+ u64 flags;
+ int err = 0;
+
+ if (IS_UVERBS_COPY_ERR(uverbs_copy_from(&action_flags, attrs,
+ MLX5_IB_ATTR_CREATE_FLOW_ACTION_FLAGS)))
+ return ERR_PTR(-EFAULT);
+
+ if (action_flags >= (MLX5_FLOW_ACTION_ESP_CREATE_LAST_SUPPORTED << 1))
+ return ERR_PTR(-EOPNOTSUPP);
+
+ flags = mlx5_ib_flow_action_flags_to_accel_xfrm_flags(action_flags);
+
+ /* We current only support a subset of the standard features. Only a
+ * keymat of type AES_GCM, with icv_len == 16, iv_algo == SEQ and esn
+ * (with overlap). Full offload mode isn't supported.
+ */
+ if (!attr->keymat || attr->replay || attr->encap ||
+ attr->spi || attr->seq || attr->tfc_pad ||
+ attr->hard_limit_pkts ||
+ (attr->flags & ~(IB_FLOW_ACTION_ESP_FLAGS_ESN_TRIGGERED |
+ IB_UVERBS_FLOW_ACTION_ESP_FLAGS_ENCRYPT)))
+ return ERR_PTR(-EOPNOTSUPP);
+
+ if (attr->keymat->protocol !=
+ IB_UVERBS_FLOW_ACTION_ESP_KEYMAT_AES_GCM)
+ return ERR_PTR(-EOPNOTSUPP);
+
+ aes_gcm = &attr->keymat->keymat.aes_gcm;
+
+ if (aes_gcm->icv_len != 16 ||
+ aes_gcm->iv_algo != IB_UVERBS_FLOW_ACTION_IV_ALGO_SEQ)
+ return ERR_PTR(-EOPNOTSUPP);
+
+ action = kmalloc(sizeof(*action), GFP_KERNEL);
+ if (!action)
+ return ERR_PTR(-ENOMEM);
+
+ action->esp_aes_gcm.ib_flags = attr->flags;
+ memcpy(&accel_attrs.keymat.aes_gcm.aes_key, &aes_gcm->aes_key,
+ sizeof(accel_attrs.keymat.aes_gcm.aes_key));
+ accel_attrs.keymat.aes_gcm.key_len = aes_gcm->key_len * 8;
+ memcpy(&accel_attrs.keymat.aes_gcm.salt, &aes_gcm->salt,
+ sizeof(accel_attrs.keymat.aes_gcm.salt));
+ memcpy(&accel_attrs.keymat.aes_gcm.seq_iv, &aes_gcm->iv,
+ sizeof(accel_attrs.keymat.aes_gcm.seq_iv));
+ accel_attrs.keymat.aes_gcm.icv_len = aes_gcm->icv_len * 8;
+ accel_attrs.keymat.aes_gcm.iv_algo = MLX5_ACCEL_ESP_AES_GCM_IV_ALGO_SEQ;
+ accel_attrs.keymat_type = MLX5_ACCEL_ESP_KEYMAT_AES_GCM;
+
+ accel_attrs.esn = attr->esn;
+ if (attr->flags & IB_FLOW_ACTION_ESP_FLAGS_ESN_TRIGGERED)
+ accel_attrs.flags |= MLX5_ACCEL_ESP_FLAGS_ESN_TRIGGERED;
+ if (attr->flags & IB_UVERBS_FLOW_ACTION_ESP_FLAGS_ESN_NEW_WINDOW)
+ accel_attrs.flags |= MLX5_ACCEL_ESP_FLAGS_ESN_STATE_OVERLAP;
+
+ if (attr->flags & IB_UVERBS_FLOW_ACTION_ESP_FLAGS_ENCRYPT)
+ accel_attrs.action |= MLX5_ACCEL_ESP_ACTION_ENCRYPT;
+
+ action->esp_aes_gcm.ctx =
+ mlx5_accel_esp_create_xfrm(mdev->mdev, &accel_attrs, flags);
+ if (IS_ERR(action->esp_aes_gcm.ctx)) {
+ err = PTR_ERR(action->esp_aes_gcm.ctx);
+ goto err_parse;
+ }
+
+ action->esp_aes_gcm.ib_flags = attr->flags;
+
+ return &action->ib_action;
+
+err_parse:
+ kfree(action);
+ return ERR_PTR(err);
+}
+
+static int
+mlx5_ib_modify_flow_action_esp(struct ib_flow_action *action,
+ const struct ib_flow_action_attrs_esp *attr,
+ struct uverbs_attr_bundle *attrs)
+{
+ struct mlx5_ib_flow_action *maction = to_mflow_act(action);
+ struct mlx5_accel_esp_xfrm_attrs accel_attrs;
+ int err = 0;
+
+ if (attr->keymat || attr->replay || attr->encap ||
+ attr->spi || attr->seq || attr->tfc_pad ||
+ attr->hard_limit_pkts ||
+ (attr->flags & ~(IB_FLOW_ACTION_ESP_FLAGS_ESN_TRIGGERED |
+ IB_FLOW_ACTION_ESP_FLAGS_MOD_ESP_ATTRS |
+ IB_UVERBS_FLOW_ACTION_ESP_FLAGS_ESN_NEW_WINDOW)))
+ return -EOPNOTSUPP;
+
+ /* Only the ESN value or the MLX5_ACCEL_ESP_FLAGS_ESN_STATE_OVERLAP can
+ * be modified.
+ */
+ if (!(maction->esp_aes_gcm.ib_flags &
+ IB_FLOW_ACTION_ESP_FLAGS_ESN_TRIGGERED) &&
+ attr->flags & (IB_FLOW_ACTION_ESP_FLAGS_ESN_TRIGGERED |
+ IB_UVERBS_FLOW_ACTION_ESP_FLAGS_ESN_NEW_WINDOW))
+ return -EINVAL;
+
+ memcpy(&accel_attrs, &maction->esp_aes_gcm.ctx->attrs,
+ sizeof(accel_attrs));
+
+ accel_attrs.esn = attr->esn;
+ if (attr->flags & IB_UVERBS_FLOW_ACTION_ESP_FLAGS_ESN_NEW_WINDOW)
+ accel_attrs.flags |= MLX5_ACCEL_ESP_FLAGS_ESN_STATE_OVERLAP;
+ else
+ accel_attrs.flags &= ~MLX5_ACCEL_ESP_FLAGS_ESN_STATE_OVERLAP;
+
+ err = mlx5_accel_esp_modify_xfrm(maction->esp_aes_gcm.ctx,
+ &accel_attrs);
+ if (err)
+ return err;
+
+ maction->esp_aes_gcm.ib_flags &=
+ ~IB_UVERBS_FLOW_ACTION_ESP_FLAGS_ESN_NEW_WINDOW;
+ maction->esp_aes_gcm.ib_flags |=
+ attr->flags & IB_UVERBS_FLOW_ACTION_ESP_FLAGS_ESN_NEW_WINDOW;
+
+ return 0;
+}
+
+static int mlx5_ib_destroy_flow_action(struct ib_flow_action *action)
+{
+ struct mlx5_ib_flow_action *maction = to_mflow_act(action);
+
+ switch (action->type) {
+ case IB_FLOW_ACTION_ESP:
+ /*
+ * We only support aes_gcm by now, so we implicitly know this is
+ * the underline crypto.
+ */
+ mlx5_accel_esp_destroy_xfrm(maction->esp_aes_gcm.ctx);
+ break;
+ default:
+ WARN_ON(true);
+ break;
+ }
+
+ kfree(maction);
+ return 0;
+}
+
static int mlx5_ib_mcg_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
{
struct mlx5_ib_dev *dev = to_mdev(ibqp->device);
@@ -3448,9 +3889,12 @@ static void destroy_umrc_res(struct mlx5_ib_dev *dev)
if (err)
mlx5_ib_warn(dev, "mr cache cleanup failed\n");
- mlx5_ib_destroy_qp(dev->umrc.qp);
- ib_free_cq(dev->umrc.cq);
- ib_dealloc_pd(dev->umrc.pd);
+ if (dev->umrc.qp)
+ mlx5_ib_destroy_qp(dev->umrc.qp);
+ if (dev->umrc.cq)
+ ib_free_cq(dev->umrc.cq);
+ if (dev->umrc.pd)
+ ib_dealloc_pd(dev->umrc.pd);
}
enum {
@@ -3552,12 +3996,15 @@ static int create_umr_res(struct mlx5_ib_dev *dev)
error_4:
mlx5_ib_destroy_qp(qp);
+ dev->umrc.qp = NULL;
error_3:
ib_free_cq(cq);
+ dev->umrc.cq = NULL;
error_2:
ib_dealloc_pd(pd);
+ dev->umrc.pd = NULL;
error_0:
kfree(attr);
@@ -3769,6 +4216,25 @@ static int mlx5_port_immutable(struct ib_device *ibdev, u8 port_num,
return 0;
}
+static int mlx5_port_rep_immutable(struct ib_device *ibdev, u8 port_num,
+ struct ib_port_immutable *immutable)
+{
+ struct ib_port_attr attr;
+ int err;
+
+ immutable->core_cap_flags = RDMA_CORE_PORT_RAW_PACKET;
+
+ err = ib_query_port(ibdev, port_num, &attr);
+ if (err)
+ return err;
+
+ immutable->pkey_tbl_len = attr.pkey_tbl_len;
+ immutable->gid_tbl_len = attr.gid_tbl_len;
+ immutable->core_cap_flags = RDMA_CORE_PORT_RAW_PACKET;
+
+ return 0;
+}
+
static void get_dev_fw_str(struct ib_device *ibdev, char *str)
{
struct mlx5_ib_dev *dev =
@@ -3799,7 +4265,7 @@ static int mlx5_eth_lag_init(struct mlx5_ib_dev *dev)
goto err_destroy_vport_lag;
}
- dev->flow_db.lag_demux_ft = ft;
+ dev->flow_db->lag_demux_ft = ft;
return 0;
err_destroy_vport_lag:
@@ -3811,9 +4277,9 @@ static void mlx5_eth_lag_cleanup(struct mlx5_ib_dev *dev)
{
struct mlx5_core_dev *mdev = dev->mdev;
- if (dev->flow_db.lag_demux_ft) {
- mlx5_destroy_flow_table(dev->flow_db.lag_demux_ft);
- dev->flow_db.lag_demux_ft = NULL;
+ if (dev->flow_db->lag_demux_ft) {
+ mlx5_destroy_flow_table(dev->flow_db->lag_demux_ft);
+ dev->flow_db->lag_demux_ft = NULL;
mlx5_cmd_destroy_vport_lag(mdev);
}
@@ -3845,14 +4311,10 @@ static int mlx5_enable_eth(struct mlx5_ib_dev *dev, u8 port_num)
{
int err;
- err = mlx5_add_netdev_notifier(dev, port_num);
- if (err)
- return err;
-
if (MLX5_CAP_GEN(dev->mdev, roce)) {
err = mlx5_nic_vport_enable_roce(dev->mdev);
if (err)
- goto err_unregister_netdevice_notifier;
+ return err;
}
err = mlx5_eth_lag_init(dev);
@@ -3865,8 +4327,6 @@ err_disable_roce:
if (MLX5_CAP_GEN(dev->mdev, roce))
mlx5_nic_vport_disable_roce(dev->mdev);
-err_unregister_netdevice_notifier:
- mlx5_remove_netdev_notifier(dev, port_num);
return err;
}
@@ -4500,7 +4960,48 @@ static void mlx5_ib_cleanup_multiport_master(struct mlx5_ib_dev *dev)
mlx5_nic_vport_disable_roce(dev->mdev);
}
-static void mlx5_ib_stage_init_cleanup(struct mlx5_ib_dev *dev)
+ADD_UVERBS_ATTRIBUTES_SIMPLE(mlx5_ib_dm, UVERBS_OBJECT_DM,
+ UVERBS_METHOD_DM_ALLOC,
+ &UVERBS_ATTR_PTR_OUT(MLX5_IB_ATTR_ALLOC_DM_RESP_START_OFFSET,
+ UVERBS_ATTR_TYPE(u64),
+ UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)),
+ &UVERBS_ATTR_PTR_OUT(MLX5_IB_ATTR_ALLOC_DM_RESP_PAGE_INDEX,
+ UVERBS_ATTR_TYPE(u16),
+ UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)));
+
+ADD_UVERBS_ATTRIBUTES_SIMPLE(mlx5_ib_flow_action, UVERBS_OBJECT_FLOW_ACTION,
+ UVERBS_METHOD_FLOW_ACTION_ESP_CREATE,
+ &UVERBS_ATTR_PTR_IN(MLX5_IB_ATTR_CREATE_FLOW_ACTION_FLAGS,
+ UVERBS_ATTR_TYPE(u64),
+ UA_FLAGS(UVERBS_ATTR_SPEC_F_MANDATORY)));
+
+#define NUM_TREES 2
+static int populate_specs_root(struct mlx5_ib_dev *dev)
+{
+ const struct uverbs_object_tree_def *default_root[NUM_TREES + 1] = {
+ uverbs_default_get_objects()};
+ size_t num_trees = 1;
+
+ if (mlx5_accel_ipsec_device_caps(dev->mdev) & MLX5_ACCEL_IPSEC_CAP_DEVICE &&
+ !WARN_ON(num_trees >= ARRAY_SIZE(default_root)))
+ default_root[num_trees++] = &mlx5_ib_flow_action;
+
+ if (MLX5_CAP_DEV_MEM(dev->mdev, memic) &&
+ !WARN_ON(num_trees >= ARRAY_SIZE(default_root)))
+ default_root[num_trees++] = &mlx5_ib_dm;
+
+ dev->ib_dev.specs_root =
+ uverbs_alloc_spec_tree(num_trees, default_root);
+
+ return PTR_ERR_OR_ZERO(dev->ib_dev.specs_root);
+}
+
+static void depopulate_specs_root(struct mlx5_ib_dev *dev)
+{
+ uverbs_free_spec_tree(dev->ib_dev.specs_root);
+}
+
+void mlx5_ib_stage_init_cleanup(struct mlx5_ib_dev *dev)
{
mlx5_ib_cleanup_multiport_master(dev);
#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
@@ -4509,7 +5010,7 @@ static void mlx5_ib_stage_init_cleanup(struct mlx5_ib_dev *dev)
kfree(dev->port);
}
-static int mlx5_ib_stage_init_init(struct mlx5_ib_dev *dev)
+int mlx5_ib_stage_init_init(struct mlx5_ib_dev *dev)
{
struct mlx5_core_dev *mdev = dev->mdev;
const char *name;
@@ -4531,8 +5032,6 @@ static int mlx5_ib_stage_init_init(struct mlx5_ib_dev *dev)
goto err_free_port;
if (!mlx5_core_mp_enabled(mdev)) {
- int i;
-
for (i = 1; i <= dev->num_ports; i++) {
err = get_port_caps(dev, i);
if (err)
@@ -4561,11 +5060,13 @@ static int mlx5_ib_stage_init_init(struct mlx5_ib_dev *dev)
dev->mdev->priv.eq_table.num_comp_vectors;
dev->ib_dev.dev.parent = &mdev->pdev->dev;
- mutex_init(&dev->flow_db.lock);
mutex_init(&dev->cap_mask_mutex);
INIT_LIST_HEAD(&dev->qp_list);
spin_lock_init(&dev->reset_flow_resource_lock);
+ spin_lock_init(&dev->memic.memic_lock);
+ dev->memic.dev = mdev;
+
#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
err = init_srcu_struct(&dev->mr_srcu);
if (err)
@@ -4582,7 +5083,38 @@ err_free_port:
return -ENOMEM;
}
-static int mlx5_ib_stage_caps_init(struct mlx5_ib_dev *dev)
+static int mlx5_ib_stage_flow_db_init(struct mlx5_ib_dev *dev)
+{
+ dev->flow_db = kzalloc(sizeof(*dev->flow_db), GFP_KERNEL);
+
+ if (!dev->flow_db)
+ return -ENOMEM;
+
+ mutex_init(&dev->flow_db->lock);
+
+ return 0;
+}
+
+int mlx5_ib_stage_rep_flow_db_init(struct mlx5_ib_dev *dev)
+{
+ struct mlx5_ib_dev *nic_dev;
+
+ nic_dev = mlx5_ib_get_uplink_ibdev(dev->mdev->priv.eswitch);
+
+ if (!nic_dev)
+ return -EINVAL;
+
+ dev->flow_db = nic_dev->flow_db;
+
+ return 0;
+}
+
+static void mlx5_ib_stage_flow_db_cleanup(struct mlx5_ib_dev *dev)
+{
+ kfree(dev->flow_db);
+}
+
+int mlx5_ib_stage_caps_init(struct mlx5_ib_dev *dev)
{
struct mlx5_core_dev *mdev = dev->mdev;
int err;
@@ -4623,7 +5155,6 @@ static int mlx5_ib_stage_caps_init(struct mlx5_ib_dev *dev)
(1ull << IB_USER_VERBS_EX_CMD_MODIFY_CQ);
dev->ib_dev.query_device = mlx5_ib_query_device;
- dev->ib_dev.query_port = mlx5_ib_query_port;
dev->ib_dev.get_link_layer = mlx5_ib_port_link_layer;
dev->ib_dev.query_gid = mlx5_ib_query_gid;
dev->ib_dev.add_gid = mlx5_ib_add_gid;
@@ -4666,7 +5197,6 @@ static int mlx5_ib_stage_caps_init(struct mlx5_ib_dev *dev)
dev->ib_dev.alloc_mr = mlx5_ib_alloc_mr;
dev->ib_dev.map_mr_sg = mlx5_ib_map_mr_sg;
dev->ib_dev.check_mr_status = mlx5_ib_check_mr_status;
- dev->ib_dev.get_port_immutable = mlx5_port_immutable;
dev->ib_dev.get_dev_fw_str = get_dev_fw_str;
dev->ib_dev.get_vector_affinity = mlx5_ib_get_vector_affinity;
if (MLX5_CAP_GEN(mdev, ipoib_enhanced_offloads))
@@ -4699,11 +5229,21 @@ static int mlx5_ib_stage_caps_init(struct mlx5_ib_dev *dev)
(1ull << IB_USER_VERBS_CMD_CLOSE_XRCD);
}
+ if (MLX5_CAP_DEV_MEM(mdev, memic)) {
+ dev->ib_dev.alloc_dm = mlx5_ib_alloc_dm;
+ dev->ib_dev.dealloc_dm = mlx5_ib_dealloc_dm;
+ dev->ib_dev.reg_dm_mr = mlx5_ib_reg_dm_mr;
+ }
+
dev->ib_dev.create_flow = mlx5_ib_create_flow;
dev->ib_dev.destroy_flow = mlx5_ib_destroy_flow;
dev->ib_dev.uverbs_ex_cmd_mask |=
(1ull << IB_USER_VERBS_EX_CMD_CREATE_FLOW) |
(1ull << IB_USER_VERBS_EX_CMD_DESTROY_FLOW);
+ dev->ib_dev.create_flow_action_esp = mlx5_ib_create_flow_action_esp;
+ dev->ib_dev.destroy_flow_action = mlx5_ib_destroy_flow_action;
+ dev->ib_dev.modify_flow_action_esp = mlx5_ib_modify_flow_action_esp;
+ dev->ib_dev.driver_id = RDMA_DRIVER_MLX5;
err = init_node_data(dev);
if (err)
@@ -4717,6 +5257,80 @@ static int mlx5_ib_stage_caps_init(struct mlx5_ib_dev *dev)
return 0;
}
+static int mlx5_ib_stage_non_default_cb(struct mlx5_ib_dev *dev)
+{
+ dev->ib_dev.get_port_immutable = mlx5_port_immutable;
+ dev->ib_dev.query_port = mlx5_ib_query_port;
+
+ return 0;
+}
+
+int mlx5_ib_stage_rep_non_default_cb(struct mlx5_ib_dev *dev)
+{
+ dev->ib_dev.get_port_immutable = mlx5_port_rep_immutable;
+ dev->ib_dev.query_port = mlx5_ib_rep_query_port;
+
+ return 0;
+}
+
+static int mlx5_ib_stage_common_roce_init(struct mlx5_ib_dev *dev,
+ u8 port_num)
+{
+ int i;
+
+ for (i = 0; i < dev->num_ports; i++) {
+ dev->roce[i].dev = dev;
+ dev->roce[i].native_port_num = i + 1;
+ dev->roce[i].last_port_state = IB_PORT_DOWN;
+ }
+
+ dev->ib_dev.get_netdev = mlx5_ib_get_netdev;
+ dev->ib_dev.create_wq = mlx5_ib_create_wq;
+ dev->ib_dev.modify_wq = mlx5_ib_modify_wq;
+ dev->ib_dev.destroy_wq = mlx5_ib_destroy_wq;
+ dev->ib_dev.create_rwq_ind_table = mlx5_ib_create_rwq_ind_table;
+ dev->ib_dev.destroy_rwq_ind_table = mlx5_ib_destroy_rwq_ind_table;
+
+ dev->ib_dev.uverbs_ex_cmd_mask |=
+ (1ull << IB_USER_VERBS_EX_CMD_CREATE_WQ) |
+ (1ull << IB_USER_VERBS_EX_CMD_MODIFY_WQ) |
+ (1ull << IB_USER_VERBS_EX_CMD_DESTROY_WQ) |
+ (1ull << IB_USER_VERBS_EX_CMD_CREATE_RWQ_IND_TBL) |
+ (1ull << IB_USER_VERBS_EX_CMD_DESTROY_RWQ_IND_TBL);
+
+ return mlx5_add_netdev_notifier(dev, port_num);
+}
+
+static void mlx5_ib_stage_common_roce_cleanup(struct mlx5_ib_dev *dev)
+{
+ u8 port_num = mlx5_core_native_port_num(dev->mdev) - 1;
+
+ mlx5_remove_netdev_notifier(dev, port_num);
+}
+
+int mlx5_ib_stage_rep_roce_init(struct mlx5_ib_dev *dev)
+{
+ struct mlx5_core_dev *mdev = dev->mdev;
+ enum rdma_link_layer ll;
+ int port_type_cap;
+ int err = 0;
+ u8 port_num;
+
+ port_num = mlx5_core_native_port_num(dev->mdev) - 1;
+ port_type_cap = MLX5_CAP_GEN(mdev, port_type);
+ ll = mlx5_port_type_cap_to_rdma_ll(port_type_cap);
+
+ if (ll == IB_LINK_LAYER_ETHERNET)
+ err = mlx5_ib_stage_common_roce_init(dev, port_num);
+
+ return err;
+}
+
+void mlx5_ib_stage_rep_roce_cleanup(struct mlx5_ib_dev *dev)
+{
+ mlx5_ib_stage_common_roce_cleanup(dev);
+}
+
static int mlx5_ib_stage_roce_init(struct mlx5_ib_dev *dev)
{
struct mlx5_core_dev *mdev = dev->mdev;
@@ -4724,37 +5338,26 @@ static int mlx5_ib_stage_roce_init(struct mlx5_ib_dev *dev)
int port_type_cap;
u8 port_num;
int err;
- int i;
port_num = mlx5_core_native_port_num(dev->mdev) - 1;
port_type_cap = MLX5_CAP_GEN(mdev, port_type);
ll = mlx5_port_type_cap_to_rdma_ll(port_type_cap);
if (ll == IB_LINK_LAYER_ETHERNET) {
- for (i = 0; i < dev->num_ports; i++) {
- dev->roce[i].dev = dev;
- dev->roce[i].native_port_num = i + 1;
- dev->roce[i].last_port_state = IB_PORT_DOWN;
- }
+ err = mlx5_ib_stage_common_roce_init(dev, port_num);
+ if (err)
+ return err;
- dev->ib_dev.get_netdev = mlx5_ib_get_netdev;
- dev->ib_dev.create_wq = mlx5_ib_create_wq;
- dev->ib_dev.modify_wq = mlx5_ib_modify_wq;
- dev->ib_dev.destroy_wq = mlx5_ib_destroy_wq;
- dev->ib_dev.create_rwq_ind_table = mlx5_ib_create_rwq_ind_table;
- dev->ib_dev.destroy_rwq_ind_table = mlx5_ib_destroy_rwq_ind_table;
- dev->ib_dev.uverbs_ex_cmd_mask |=
- (1ull << IB_USER_VERBS_EX_CMD_CREATE_WQ) |
- (1ull << IB_USER_VERBS_EX_CMD_MODIFY_WQ) |
- (1ull << IB_USER_VERBS_EX_CMD_DESTROY_WQ) |
- (1ull << IB_USER_VERBS_EX_CMD_CREATE_RWQ_IND_TBL) |
- (1ull << IB_USER_VERBS_EX_CMD_DESTROY_RWQ_IND_TBL);
err = mlx5_enable_eth(dev, port_num);
if (err)
- return err;
+ goto cleanup;
}
return 0;
+cleanup:
+ mlx5_ib_stage_common_roce_cleanup(dev);
+
+ return err;
}
static void mlx5_ib_stage_roce_cleanup(struct mlx5_ib_dev *dev)
@@ -4770,16 +5373,16 @@ static void mlx5_ib_stage_roce_cleanup(struct mlx5_ib_dev *dev)
if (ll == IB_LINK_LAYER_ETHERNET) {
mlx5_disable_eth(dev);
- mlx5_remove_netdev_notifier(dev, port_num);
+ mlx5_ib_stage_common_roce_cleanup(dev);
}
}
-static int mlx5_ib_stage_dev_res_init(struct mlx5_ib_dev *dev)
+int mlx5_ib_stage_dev_res_init(struct mlx5_ib_dev *dev)
{
return create_dev_resources(&dev->devr);
}
-static void mlx5_ib_stage_dev_res_cleanup(struct mlx5_ib_dev *dev)
+void mlx5_ib_stage_dev_res_cleanup(struct mlx5_ib_dev *dev)
{
destroy_dev_resources(&dev->devr);
}
@@ -4791,7 +5394,7 @@ static int mlx5_ib_stage_odp_init(struct mlx5_ib_dev *dev)
return mlx5_ib_odp_init_one(dev);
}
-static int mlx5_ib_stage_counters_init(struct mlx5_ib_dev *dev)
+int mlx5_ib_stage_counters_init(struct mlx5_ib_dev *dev)
{
if (MLX5_CAP_GEN(dev->mdev, max_qp_cnt)) {
dev->ib_dev.get_hw_stats = mlx5_ib_get_hw_stats;
@@ -4803,7 +5406,7 @@ static int mlx5_ib_stage_counters_init(struct mlx5_ib_dev *dev)
return 0;
}
-static void mlx5_ib_stage_counters_cleanup(struct mlx5_ib_dev *dev)
+void mlx5_ib_stage_counters_cleanup(struct mlx5_ib_dev *dev)
{
if (MLX5_CAP_GEN(dev->mdev, max_qp_cnt))
mlx5_ib_dealloc_counters(dev);
@@ -4834,7 +5437,7 @@ static void mlx5_ib_stage_uar_cleanup(struct mlx5_ib_dev *dev)
mlx5_put_uars_page(dev->mdev, dev->mdev->priv.uar);
}
-static int mlx5_ib_stage_bfrag_init(struct mlx5_ib_dev *dev)
+int mlx5_ib_stage_bfrag_init(struct mlx5_ib_dev *dev)
{
int err;
@@ -4849,28 +5452,38 @@ static int mlx5_ib_stage_bfrag_init(struct mlx5_ib_dev *dev)
return err;
}
-static void mlx5_ib_stage_bfrag_cleanup(struct mlx5_ib_dev *dev)
+void mlx5_ib_stage_bfrag_cleanup(struct mlx5_ib_dev *dev)
{
mlx5_free_bfreg(dev->mdev, &dev->fp_bfreg);
mlx5_free_bfreg(dev->mdev, &dev->bfreg);
}
-static int mlx5_ib_stage_ib_reg_init(struct mlx5_ib_dev *dev)
+static int mlx5_ib_stage_populate_specs(struct mlx5_ib_dev *dev)
+{
+ return populate_specs_root(dev);
+}
+
+int mlx5_ib_stage_ib_reg_init(struct mlx5_ib_dev *dev)
{
return ib_register_device(&dev->ib_dev, NULL);
}
-static void mlx5_ib_stage_pre_ib_reg_umr_cleanup(struct mlx5_ib_dev *dev)
+static void mlx5_ib_stage_depopulate_specs(struct mlx5_ib_dev *dev)
+{
+ depopulate_specs_root(dev);
+}
+
+void mlx5_ib_stage_pre_ib_reg_umr_cleanup(struct mlx5_ib_dev *dev)
{
destroy_umrc_res(dev);
}
-static void mlx5_ib_stage_ib_reg_cleanup(struct mlx5_ib_dev *dev)
+void mlx5_ib_stage_ib_reg_cleanup(struct mlx5_ib_dev *dev)
{
ib_unregister_device(&dev->ib_dev);
}
-static int mlx5_ib_stage_post_ib_reg_umr_init(struct mlx5_ib_dev *dev)
+int mlx5_ib_stage_post_ib_reg_umr_init(struct mlx5_ib_dev *dev)
{
return create_umr_res(dev);
}
@@ -4887,7 +5500,7 @@ static void mlx5_ib_stage_delay_drop_cleanup(struct mlx5_ib_dev *dev)
cancel_delay_drop(dev);
}
-static int mlx5_ib_stage_class_attr_init(struct mlx5_ib_dev *dev)
+int mlx5_ib_stage_class_attr_init(struct mlx5_ib_dev *dev)
{
int err;
int i;
@@ -4902,9 +5515,21 @@ static int mlx5_ib_stage_class_attr_init(struct mlx5_ib_dev *dev)
return 0;
}
-static void __mlx5_ib_remove(struct mlx5_ib_dev *dev,
- const struct mlx5_ib_profile *profile,
- int stage)
+static int mlx5_ib_stage_rep_reg_init(struct mlx5_ib_dev *dev)
+{
+ mlx5_ib_register_vport_reps(dev);
+
+ return 0;
+}
+
+static void mlx5_ib_stage_rep_reg_cleanup(struct mlx5_ib_dev *dev)
+{
+ mlx5_ib_unregister_vport_reps(dev);
+}
+
+void __mlx5_ib_remove(struct mlx5_ib_dev *dev,
+ const struct mlx5_ib_profile *profile,
+ int stage)
{
/* Number of stages to cleanup */
while (stage) {
@@ -4918,23 +5543,14 @@ static void __mlx5_ib_remove(struct mlx5_ib_dev *dev,
static void *mlx5_ib_add_slave_port(struct mlx5_core_dev *mdev, u8 port_num);
-static void *__mlx5_ib_add(struct mlx5_core_dev *mdev,
- const struct mlx5_ib_profile *profile)
+void *__mlx5_ib_add(struct mlx5_ib_dev *dev,
+ const struct mlx5_ib_profile *profile)
{
- struct mlx5_ib_dev *dev;
int err;
int i;
printk_once(KERN_INFO "%s", mlx5_version);
- dev = (struct mlx5_ib_dev *)ib_alloc_device(sizeof(*dev));
- if (!dev)
- return NULL;
-
- dev->mdev = mdev;
- dev->num_ports = max(MLX5_CAP_GEN(mdev, num_ports),
- MLX5_CAP_GEN(mdev, num_vhca_ports));
-
for (i = 0; i < MLX5_IB_STAGE_MAX; i++) {
if (profile->stage[i].init) {
err = profile->stage[i].init(dev);
@@ -4958,9 +5574,15 @@ static const struct mlx5_ib_profile pf_profile = {
STAGE_CREATE(MLX5_IB_STAGE_INIT,
mlx5_ib_stage_init_init,
mlx5_ib_stage_init_cleanup),
+ STAGE_CREATE(MLX5_IB_STAGE_FLOW_DB,
+ mlx5_ib_stage_flow_db_init,
+ mlx5_ib_stage_flow_db_cleanup),
STAGE_CREATE(MLX5_IB_STAGE_CAPS,
mlx5_ib_stage_caps_init,
NULL),
+ STAGE_CREATE(MLX5_IB_STAGE_NON_DEFAULT_CB,
+ mlx5_ib_stage_non_default_cb,
+ NULL),
STAGE_CREATE(MLX5_IB_STAGE_ROCE,
mlx5_ib_stage_roce_init,
mlx5_ib_stage_roce_cleanup),
@@ -4985,6 +5607,9 @@ static const struct mlx5_ib_profile pf_profile = {
STAGE_CREATE(MLX5_IB_STAGE_PRE_IB_REG_UMR,
NULL,
mlx5_ib_stage_pre_ib_reg_umr_cleanup),
+ STAGE_CREATE(MLX5_IB_STAGE_SPECS,
+ mlx5_ib_stage_populate_specs,
+ mlx5_ib_stage_depopulate_specs),
STAGE_CREATE(MLX5_IB_STAGE_IB_REG,
mlx5_ib_stage_ib_reg_init,
mlx5_ib_stage_ib_reg_cleanup),
@@ -4999,6 +5624,54 @@ static const struct mlx5_ib_profile pf_profile = {
NULL),
};
+static const struct mlx5_ib_profile nic_rep_profile = {
+ STAGE_CREATE(MLX5_IB_STAGE_INIT,
+ mlx5_ib_stage_init_init,
+ mlx5_ib_stage_init_cleanup),
+ STAGE_CREATE(MLX5_IB_STAGE_FLOW_DB,
+ mlx5_ib_stage_flow_db_init,
+ mlx5_ib_stage_flow_db_cleanup),
+ STAGE_CREATE(MLX5_IB_STAGE_CAPS,
+ mlx5_ib_stage_caps_init,
+ NULL),
+ STAGE_CREATE(MLX5_IB_STAGE_NON_DEFAULT_CB,
+ mlx5_ib_stage_rep_non_default_cb,
+ NULL),
+ STAGE_CREATE(MLX5_IB_STAGE_ROCE,
+ mlx5_ib_stage_rep_roce_init,
+ mlx5_ib_stage_rep_roce_cleanup),
+ STAGE_CREATE(MLX5_IB_STAGE_DEVICE_RESOURCES,
+ mlx5_ib_stage_dev_res_init,
+ mlx5_ib_stage_dev_res_cleanup),
+ STAGE_CREATE(MLX5_IB_STAGE_COUNTERS,
+ mlx5_ib_stage_counters_init,
+ mlx5_ib_stage_counters_cleanup),
+ STAGE_CREATE(MLX5_IB_STAGE_UAR,
+ mlx5_ib_stage_uar_init,
+ mlx5_ib_stage_uar_cleanup),
+ STAGE_CREATE(MLX5_IB_STAGE_BFREG,
+ mlx5_ib_stage_bfrag_init,
+ mlx5_ib_stage_bfrag_cleanup),
+ STAGE_CREATE(MLX5_IB_STAGE_PRE_IB_REG_UMR,
+ NULL,
+ mlx5_ib_stage_pre_ib_reg_umr_cleanup),
+ STAGE_CREATE(MLX5_IB_STAGE_SPECS,
+ mlx5_ib_stage_populate_specs,
+ mlx5_ib_stage_depopulate_specs),
+ STAGE_CREATE(MLX5_IB_STAGE_IB_REG,
+ mlx5_ib_stage_ib_reg_init,
+ mlx5_ib_stage_ib_reg_cleanup),
+ STAGE_CREATE(MLX5_IB_STAGE_POST_IB_REG_UMR,
+ mlx5_ib_stage_post_ib_reg_umr_init,
+ NULL),
+ STAGE_CREATE(MLX5_IB_STAGE_CLASS_ATTR,
+ mlx5_ib_stage_class_attr_init,
+ NULL),
+ STAGE_CREATE(MLX5_IB_STAGE_REP_REG,
+ mlx5_ib_stage_rep_reg_init,
+ mlx5_ib_stage_rep_reg_cleanup),
+};
+
static void *mlx5_ib_add_slave_port(struct mlx5_core_dev *mdev, u8 port_num)
{
struct mlx5_ib_multiport_info *mpi;
@@ -5044,8 +5717,11 @@ static void *mlx5_ib_add_slave_port(struct mlx5_core_dev *mdev, u8 port_num)
static void *mlx5_ib_add(struct mlx5_core_dev *mdev)
{
enum rdma_link_layer ll;
+ struct mlx5_ib_dev *dev;
int port_type_cap;
+ printk_once(KERN_INFO "%s", mlx5_version);
+
port_type_cap = MLX5_CAP_GEN(mdev, port_type);
ll = mlx5_port_type_cap_to_rdma_ll(port_type_cap);
@@ -5055,7 +5731,22 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev)
return mlx5_ib_add_slave_port(mdev, port_num);
}
- return __mlx5_ib_add(mdev, &pf_profile);
+ dev = (struct mlx5_ib_dev *)ib_alloc_device(sizeof(*dev));
+ if (!dev)
+ return NULL;
+
+ dev->mdev = mdev;
+ dev->num_ports = max(MLX5_CAP_GEN(mdev, num_ports),
+ MLX5_CAP_GEN(mdev, num_vhca_ports));
+
+ if (MLX5_VPORT_MANAGER(mdev) &&
+ mlx5_ib_eswitch_mode(mdev->priv.eswitch) == SRIOV_OFFLOADS) {
+ dev->rep = mlx5_ib_vport_rep(mdev->priv.eswitch, 0);
+
+ return __mlx5_ib_add(dev, &nic_rep_profile);
+ }
+
+ return __mlx5_ib_add(dev, &pf_profile);
}
static void mlx5_ib_remove(struct mlx5_core_dev *mdev, void *context)
@@ -5087,13 +5778,32 @@ static struct mlx5_interface mlx5_ib_interface = {
.protocol = MLX5_INTERFACE_PROTOCOL_IB,
};
+unsigned long mlx5_ib_get_xlt_emergency_page(void)
+{
+ mutex_lock(&xlt_emergency_page_mutex);
+ return xlt_emergency_page;
+}
+
+void mlx5_ib_put_xlt_emergency_page(void)
+{
+ mutex_unlock(&xlt_emergency_page_mutex);
+}
+
static int __init mlx5_ib_init(void)
{
int err;
+ xlt_emergency_page = __get_free_page(GFP_KERNEL);
+ if (!xlt_emergency_page)
+ return -ENOMEM;
+
+ mutex_init(&xlt_emergency_page_mutex);
+
mlx5_ib_event_wq = alloc_ordered_workqueue("mlx5_ib_event_wq", 0);
- if (!mlx5_ib_event_wq)
+ if (!mlx5_ib_event_wq) {
+ free_page(xlt_emergency_page);
return -ENOMEM;
+ }
mlx5_ib_odp_init();
@@ -5106,6 +5816,8 @@ static void __exit mlx5_ib_cleanup(void)
{
mlx5_unregister_interface(&mlx5_ib_interface);
destroy_workqueue(mlx5_ib_event_wq);
+ mutex_destroy(&xlt_emergency_page_mutex);
+ free_page(xlt_emergency_page);
}
module_init(mlx5_ib_init);
diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h
index a5272499b600..49a1aa0ff429 100644
--- a/drivers/infiniband/hw/mlx5/mlx5_ib.h
+++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h
@@ -45,6 +45,7 @@
#include <linux/mlx5/transobj.h>
#include <rdma/ib_user_verbs.h>
#include <rdma/mlx5-abi.h>
+#include <rdma/uverbs_ioctl.h>
#define mlx5_ib_dbg(dev, format, arg...) \
pr_debug("%s:%s:%d:(pid %d): " format, (dev)->ib_dev.name, __func__, \
@@ -108,6 +109,16 @@ enum {
MLX5_IB_INVALID_BFREG = BIT(31),
};
+enum {
+ MLX5_MAX_MEMIC_PAGES = 0x100,
+ MLX5_MEMIC_ALLOC_SIZE_MASK = 0x3f,
+};
+
+enum {
+ MLX5_MEMIC_BASE_ALIGN = 6,
+ MLX5_MEMIC_BASE_SIZE = 1 << MLX5_MEMIC_BASE_ALIGN,
+};
+
struct mlx5_ib_vma_private_data {
struct list_head list;
struct vm_area_struct *vma;
@@ -130,10 +141,8 @@ struct mlx5_ib_ucontext {
/* protect vma_private_list add/del */
struct mutex vma_private_list_mutex;
- unsigned long upd_xlt_page;
- /* protect ODP/KSM */
- struct mutex upd_xlt_page_mutex;
u64 lib_caps;
+ DECLARE_BITMAP(dm_pages, MLX5_MAX_MEMIC_PAGES);
};
static inline struct mlx5_ib_ucontext *to_mucontext(struct ib_ucontext *ibucontext)
@@ -155,6 +164,7 @@ struct mlx5_ib_pd {
#define MLX5_IB_NUM_FLOW_FT (MLX5_IB_FLOW_LEFTOVERS_PRIO + 1)
#define MLX5_IB_NUM_SNIFFER_FTS 2
+#define MLX5_IB_NUM_EGRESS_FTS 1
struct mlx5_ib_flow_prio {
struct mlx5_flow_table *flow_table;
unsigned int refcount;
@@ -170,6 +180,7 @@ struct mlx5_ib_flow_handler {
struct mlx5_ib_flow_db {
struct mlx5_ib_flow_prio prios[MLX5_IB_NUM_FLOW_FT];
struct mlx5_ib_flow_prio sniffer[MLX5_IB_NUM_SNIFFER_FTS];
+ struct mlx5_ib_flow_prio egress[MLX5_IB_NUM_EGRESS_FTS];
struct mlx5_flow_table *lag_demux_ft;
/* Protect flow steering bypass flow tables
* when add/del flow rules.
@@ -343,6 +354,7 @@ struct mlx5_ib_sq {
struct mlx5_ib_wq *sq;
struct mlx5_ib_ubuffer ubuffer;
struct mlx5_db *doorbell;
+ struct mlx5_flow_handle *flow_rule;
u32 tisn;
u8 state;
};
@@ -371,7 +383,7 @@ struct mlx5_ib_qp {
struct mlx5_ib_rss_qp rss_qp;
struct mlx5_ib_dct dct;
};
- struct mlx5_buf buf;
+ struct mlx5_frag_buf buf;
struct mlx5_db db;
struct mlx5_ib_wq rq;
@@ -405,7 +417,7 @@ struct mlx5_ib_qp {
struct list_head qps_list;
struct list_head cq_recv_list;
struct list_head cq_send_list;
- u32 rate_limit;
+ struct mlx5_rate_limit rl;
u32 underlay_qpn;
bool tunnel_offload_en;
/* storage for qp sub type when core qp type is IB_QPT_DRIVER */
@@ -413,7 +425,7 @@ struct mlx5_ib_qp {
};
struct mlx5_ib_cq_buf {
- struct mlx5_buf buf;
+ struct mlx5_frag_buf_ctrl fbc;
struct ib_umem *umem;
int cqe_size;
int nent;
@@ -495,7 +507,7 @@ struct mlx5_ib_wc {
struct mlx5_ib_srq {
struct ib_srq ibsrq;
struct mlx5_core_srq msrq;
- struct mlx5_buf buf;
+ struct mlx5_frag_buf buf;
struct mlx5_db db;
u64 *wrid;
/* protect SRQ hanlding
@@ -521,8 +533,19 @@ enum mlx5_ib_mtt_access_flags {
MLX5_IB_MTT_WRITE = (1 << 1),
};
+struct mlx5_ib_dm {
+ struct ib_dm ibdm;
+ phys_addr_t dev_addr;
+};
+
#define MLX5_IB_MTT_PRESENT (MLX5_IB_MTT_READ | MLX5_IB_MTT_WRITE)
+#define MLX5_IB_DM_ALLOWED_ACCESS (IB_ACCESS_LOCAL_WRITE |\
+ IB_ACCESS_REMOTE_WRITE |\
+ IB_ACCESS_REMOTE_READ |\
+ IB_ACCESS_REMOTE_ATOMIC |\
+ IB_ZERO_BASED)
+
struct mlx5_ib_mr {
struct ib_mr ibmr;
void *descs;
@@ -731,7 +754,9 @@ struct mlx5_ib_delay_drop {
enum mlx5_ib_stages {
MLX5_IB_STAGE_INIT,
+ MLX5_IB_STAGE_FLOW_DB,
MLX5_IB_STAGE_CAPS,
+ MLX5_IB_STAGE_NON_DEFAULT_CB,
MLX5_IB_STAGE_ROCE,
MLX5_IB_STAGE_DEVICE_RESOURCES,
MLX5_IB_STAGE_ODP,
@@ -740,10 +765,12 @@ enum mlx5_ib_stages {
MLX5_IB_STAGE_UAR,
MLX5_IB_STAGE_BFREG,
MLX5_IB_STAGE_PRE_IB_REG_UMR,
+ MLX5_IB_STAGE_SPECS,
MLX5_IB_STAGE_IB_REG,
MLX5_IB_STAGE_POST_IB_REG_UMR,
MLX5_IB_STAGE_DELAY_DROP,
MLX5_IB_STAGE_CLASS_ATTR,
+ MLX5_IB_STAGE_REP_REG,
MLX5_IB_STAGE_MAX,
};
@@ -770,6 +797,22 @@ struct mlx5_ib_multiport_info {
bool unaffiliate;
};
+struct mlx5_ib_flow_action {
+ struct ib_flow_action ib_action;
+ union {
+ struct {
+ u64 ib_flags;
+ struct mlx5_accel_esp_xfrm *ctx;
+ } esp_aes_gcm;
+ };
+};
+
+struct mlx5_memic {
+ struct mlx5_core_dev *dev;
+ spinlock_t memic_lock;
+ DECLARE_BITMAP(memic_alloc_pages, MLX5_MAX_MEMIC_PAGES);
+};
+
struct mlx5_ib_dev {
struct ib_device ib_dev;
struct mlx5_core_dev *mdev;
@@ -798,7 +841,7 @@ struct mlx5_ib_dev {
struct srcu_struct mr_srcu;
u32 null_mkey;
#endif
- struct mlx5_ib_flow_db flow_db;
+ struct mlx5_ib_flow_db *flow_db;
/* protect resources needed as part of reset flow */
spinlock_t reset_flow_resource_lock;
struct list_head qp_list;
@@ -808,6 +851,7 @@ struct mlx5_ib_dev {
struct mlx5_sq_bfreg fp_bfreg;
struct mlx5_ib_delay_drop delay_drop;
const struct mlx5_ib_profile *profile;
+ struct mlx5_eswitch_rep *rep;
/* protect the user_td */
struct mutex lb_mutex;
@@ -815,6 +859,7 @@ struct mlx5_ib_dev {
u8 umr_fence;
struct list_head ib_dev_list;
u64 sys_image_guid;
+ struct mlx5_memic memic;
};
static inline struct mlx5_ib_cq *to_mibcq(struct mlx5_core_cq *mcq)
@@ -882,6 +927,11 @@ static inline struct mlx5_ib_srq *to_mibsrq(struct mlx5_core_srq *msrq)
return container_of(msrq, struct mlx5_ib_srq, msrq);
}
+static inline struct mlx5_ib_dm *to_mdm(struct ib_dm *ibdm)
+{
+ return container_of(ibdm, struct mlx5_ib_dm, ibdm);
+}
+
static inline struct mlx5_ib_mr *to_mmr(struct ib_mr *ibmr)
{
return container_of(ibmr, struct mlx5_ib_mr, ibmr);
@@ -892,6 +942,12 @@ static inline struct mlx5_ib_mw *to_mmw(struct ib_mw *ibmw)
return container_of(ibmw, struct mlx5_ib_mw, ibmw);
}
+static inline struct mlx5_ib_flow_action *
+to_mflow_act(struct ib_flow_action *ibact)
+{
+ return container_of(ibact, struct mlx5_ib_flow_action, ib_action);
+}
+
int mlx5_ib_db_map_user(struct mlx5_ib_ucontext *context, unsigned long virt,
struct mlx5_db *db);
void mlx5_ib_db_unmap_user(struct mlx5_ib_ucontext *context, struct mlx5_db *db);
@@ -1020,7 +1076,14 @@ struct ib_rwq_ind_table *mlx5_ib_create_rwq_ind_table(struct ib_device *device,
struct ib_udata *udata);
int mlx5_ib_destroy_rwq_ind_table(struct ib_rwq_ind_table *wq_ind_table);
bool mlx5_ib_dc_atomic_is_supported(struct mlx5_ib_dev *dev);
-
+struct ib_dm *mlx5_ib_alloc_dm(struct ib_device *ibdev,
+ struct ib_ucontext *context,
+ struct ib_dm_alloc_attr *attr,
+ struct uverbs_attr_bundle *attrs);
+int mlx5_ib_dealloc_dm(struct ib_dm *ibdm);
+struct ib_mr *mlx5_ib_reg_dm_mr(struct ib_pd *pd, struct ib_dm *dm,
+ struct ib_dm_mr_attr *attr,
+ struct uverbs_attr_bundle *attrs);
#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
void mlx5_ib_internal_fill_odp_caps(struct mlx5_ib_dev *dev);
@@ -1050,6 +1113,31 @@ static inline void mlx5_odp_populate_klm(struct mlx5_klm *pklm, size_t offset,
#endif /* CONFIG_INFINIBAND_ON_DEMAND_PAGING */
+/* Needed for rep profile */
+int mlx5_ib_stage_init_init(struct mlx5_ib_dev *dev);
+void mlx5_ib_stage_init_cleanup(struct mlx5_ib_dev *dev);
+int mlx5_ib_stage_rep_flow_db_init(struct mlx5_ib_dev *dev);
+int mlx5_ib_stage_caps_init(struct mlx5_ib_dev *dev);
+int mlx5_ib_stage_rep_non_default_cb(struct mlx5_ib_dev *dev);
+int mlx5_ib_stage_rep_roce_init(struct mlx5_ib_dev *dev);
+void mlx5_ib_stage_rep_roce_cleanup(struct mlx5_ib_dev *dev);
+int mlx5_ib_stage_dev_res_init(struct mlx5_ib_dev *dev);
+void mlx5_ib_stage_dev_res_cleanup(struct mlx5_ib_dev *dev);
+int mlx5_ib_stage_counters_init(struct mlx5_ib_dev *dev);
+void mlx5_ib_stage_counters_cleanup(struct mlx5_ib_dev *dev);
+int mlx5_ib_stage_bfrag_init(struct mlx5_ib_dev *dev);
+void mlx5_ib_stage_bfrag_cleanup(struct mlx5_ib_dev *dev);
+void mlx5_ib_stage_pre_ib_reg_umr_cleanup(struct mlx5_ib_dev *dev);
+int mlx5_ib_stage_ib_reg_init(struct mlx5_ib_dev *dev);
+void mlx5_ib_stage_ib_reg_cleanup(struct mlx5_ib_dev *dev);
+int mlx5_ib_stage_post_ib_reg_umr_init(struct mlx5_ib_dev *dev);
+int mlx5_ib_stage_class_attr_init(struct mlx5_ib_dev *dev);
+void __mlx5_ib_remove(struct mlx5_ib_dev *dev,
+ const struct mlx5_ib_profile *profile,
+ int stage);
+void *__mlx5_ib_add(struct mlx5_ib_dev *dev,
+ const struct mlx5_ib_profile *profile);
+
int mlx5_ib_get_vf_config(struct ib_device *device, int vf,
u8 port, struct ifla_vf_info *info);
int mlx5_ib_set_vf_link_state(struct ib_device *device, int vf,
@@ -1191,4 +1279,7 @@ static inline int get_num_static_uars(struct mlx5_ib_dev *dev,
return get_uars_per_sys_page(dev, bfregi->lib_uar_4k) * bfregi->num_static_sys_pages;
}
+unsigned long mlx5_ib_get_xlt_emergency_page(void);
+void mlx5_ib_put_xlt_emergency_page(void);
+
#endif /* MLX5_IB_H */
diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c
index c51c602f06d6..1520a2f20f98 100644
--- a/drivers/infiniband/hw/mlx5/mr.c
+++ b/drivers/infiniband/hw/mlx5/mr.c
@@ -47,10 +47,25 @@ enum {
#define MLX5_UMR_ALIGN 2048
-static int clean_mr(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr);
-static int dereg_mr(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr);
+static void clean_mr(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr);
+static void dereg_mr(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr);
static int mr_cache_max_order(struct mlx5_ib_dev *dev);
static int unreg_umr(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr);
+static bool umr_can_modify_entity_size(struct mlx5_ib_dev *dev)
+{
+ return !MLX5_CAP_GEN(dev->mdev, umr_modify_entity_size_disabled);
+}
+
+static bool umr_can_use_indirect_mkey(struct mlx5_ib_dev *dev)
+{
+ return !MLX5_CAP_GEN(dev->mdev, umr_indirect_mkey_disabled);
+}
+
+static bool use_umr(struct mlx5_ib_dev *dev, int order)
+{
+ return order <= mr_cache_max_order(dev) &&
+ umr_can_modify_entity_size(dev);
+}
static int destroy_mkey(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr)
{
@@ -189,7 +204,9 @@ static int add_keys(struct mlx5_ib_dev *dev, int c, int num)
MLX5_SET(mkc, mkc, free, 1);
MLX5_SET(mkc, mkc, umr_en, 1);
- MLX5_SET(mkc, mkc, access_mode, ent->access_mode);
+ MLX5_SET(mkc, mkc, access_mode_1_0, ent->access_mode & 0x3);
+ MLX5_SET(mkc, mkc, access_mode_4_2,
+ (ent->access_mode >> 2) & 0x7);
MLX5_SET(mkc, mkc, qpn, 0xffffff);
MLX5_SET(mkc, mkc, translations_octword_size, ent->xlt);
@@ -220,26 +237,32 @@ static void remove_keys(struct mlx5_ib_dev *dev, int c, int num)
{
struct mlx5_mr_cache *cache = &dev->cache;
struct mlx5_cache_ent *ent = &cache->ent[c];
+ struct mlx5_ib_mr *tmp_mr;
struct mlx5_ib_mr *mr;
- int err;
+ LIST_HEAD(del_list);
int i;
for (i = 0; i < num; i++) {
spin_lock_irq(&ent->lock);
if (list_empty(&ent->head)) {
spin_unlock_irq(&ent->lock);
- return;
+ break;
}
mr = list_first_entry(&ent->head, struct mlx5_ib_mr, list);
- list_del(&mr->list);
+ list_move(&mr->list, &del_list);
ent->cur--;
ent->size--;
spin_unlock_irq(&ent->lock);
- err = destroy_mkey(dev, mr);
- if (err)
- mlx5_ib_warn(dev, "failed destroy mkey\n");
- else
- kfree(mr);
+ mlx5_core_destroy_mkey(dev->mdev, &mr->mmkey);
+ }
+
+#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
+ synchronize_srcu(&dev->mr_srcu);
+#endif
+
+ list_for_each_entry_safe(mr, tmp_mr, &del_list, list) {
+ list_del(&mr->list);
+ kfree(mr);
}
}
@@ -562,32 +585,38 @@ static void clean_keys(struct mlx5_ib_dev *dev, int c)
{
struct mlx5_mr_cache *cache = &dev->cache;
struct mlx5_cache_ent *ent = &cache->ent[c];
+ struct mlx5_ib_mr *tmp_mr;
struct mlx5_ib_mr *mr;
- int err;
+ LIST_HEAD(del_list);
cancel_delayed_work(&ent->dwork);
while (1) {
spin_lock_irq(&ent->lock);
if (list_empty(&ent->head)) {
spin_unlock_irq(&ent->lock);
- return;
+ break;
}
mr = list_first_entry(&ent->head, struct mlx5_ib_mr, list);
- list_del(&mr->list);
+ list_move(&mr->list, &del_list);
ent->cur--;
ent->size--;
spin_unlock_irq(&ent->lock);
- err = destroy_mkey(dev, mr);
- if (err)
- mlx5_ib_warn(dev, "failed destroy mkey\n");
- else
- kfree(mr);
+ mlx5_core_destroy_mkey(dev->mdev, &mr->mmkey);
+ }
+
+#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
+ synchronize_srcu(&dev->mr_srcu);
+#endif
+
+ list_for_each_entry_safe(mr, tmp_mr, &del_list, list) {
+ list_del(&mr->list);
+ kfree(mr);
}
}
static void mlx5_mr_cache_debugfs_cleanup(struct mlx5_ib_dev *dev)
{
- if (!mlx5_debugfs_root)
+ if (!mlx5_debugfs_root || dev->rep)
return;
debugfs_remove_recursive(dev->cache.root);
@@ -600,7 +629,7 @@ static int mlx5_mr_cache_debugfs_init(struct mlx5_ib_dev *dev)
struct mlx5_cache_ent *ent;
int i;
- if (!mlx5_debugfs_root)
+ if (!mlx5_debugfs_root || dev->rep)
return 0;
cache->root = debugfs_create_dir("mr_cache", dev->mdev->priv.dbg_root);
@@ -690,6 +719,7 @@ int mlx5_mr_cache_init(struct mlx5_ib_dev *dev)
MLX5_IB_UMR_OCTOWORD;
ent->access_mode = MLX5_MKC_ACCESS_MODE_MTT;
if ((dev->mdev->profile->mask & MLX5_PROF_MASK_MR_CACHE) &&
+ !dev->rep &&
mlx5_core_is_pf(dev->mdev))
ent->limit = dev->mdev->profile->mr_cache[i].limit;
else
@@ -739,6 +769,9 @@ int mlx5_mr_cache_cleanup(struct mlx5_ib_dev *dev)
{
int i;
+ if (!dev->cache.wq)
+ return 0;
+
dev->cache.stopped = 1;
flush_workqueue(dev->cache.wq);
@@ -776,7 +809,7 @@ struct ib_mr *mlx5_ib_get_dma_mr(struct ib_pd *pd, int acc)
mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry);
- MLX5_SET(mkc, mkc, access_mode, MLX5_MKC_ACCESS_MODE_PA);
+ MLX5_SET(mkc, mkc, access_mode_1_0, MLX5_MKC_ACCESS_MODE_PA);
MLX5_SET(mkc, mkc, a, !!(acc & IB_ACCESS_REMOTE_ATOMIC));
MLX5_SET(mkc, mkc, rw, !!(acc & IB_ACCESS_REMOTE_WRITE));
MLX5_SET(mkc, mkc, rr, !!(acc & IB_ACCESS_REMOTE_READ));
@@ -943,7 +976,10 @@ static inline int populate_xlt(struct mlx5_ib_mr *mr, int idx, int npages,
{
struct mlx5_ib_dev *dev = mr->dev;
struct ib_umem *umem = mr->umem;
+
if (flags & MLX5_IB_UPD_XLT_INDIRECT) {
+ if (!umr_can_use_indirect_mkey(dev))
+ return -EPERM;
mlx5_odp_populate_klm(xlt, idx, npages, mr, flags);
return npages;
}
@@ -973,7 +1009,6 @@ int mlx5_ib_update_xlt(struct mlx5_ib_mr *mr, u64 idx, int npages,
{
struct mlx5_ib_dev *dev = mr->dev;
struct device *ddev = dev->ib_dev.dev.parent;
- struct mlx5_ib_ucontext *uctx = NULL;
int size;
void *xlt;
dma_addr_t dma;
@@ -989,6 +1024,11 @@ int mlx5_ib_update_xlt(struct mlx5_ib_mr *mr, u64 idx, int npages,
size_t pages_to_map = 0;
size_t pages_iter = 0;
gfp_t gfp;
+ bool use_emergency_page = false;
+
+ if ((flags & MLX5_IB_UPD_XLT_INDIRECT) &&
+ !umr_can_use_indirect_mkey(dev))
+ return -EPERM;
/* UMR copies MTTs in units of MLX5_UMR_MTT_ALIGNMENT bytes,
* so we need to align the offset and length accordingly
@@ -1015,12 +1055,11 @@ int mlx5_ib_update_xlt(struct mlx5_ib_mr *mr, u64 idx, int npages,
}
if (!xlt) {
- uctx = to_mucontext(mr->ibmr.pd->uobject->context);
mlx5_ib_warn(dev, "Using XLT emergency buffer\n");
+ xlt = (void *)mlx5_ib_get_xlt_emergency_page();
size = PAGE_SIZE;
- xlt = (void *)uctx->upd_xlt_page;
- mutex_lock(&uctx->upd_xlt_page_mutex);
memset(xlt, 0, size);
+ use_emergency_page = true;
}
pages_iter = size / desc_size;
dma = dma_map_single(ddev, xlt, size, DMA_TO_DEVICE);
@@ -1084,8 +1123,8 @@ int mlx5_ib_update_xlt(struct mlx5_ib_mr *mr, u64 idx, int npages,
dma_unmap_single(ddev, dma, size, DMA_TO_DEVICE);
free_xlt:
- if (uctx)
- mutex_unlock(&uctx->upd_xlt_page_mutex);
+ if (use_emergency_page)
+ mlx5_ib_put_xlt_emergency_page();
else
free_pages((unsigned long)xlt, get_order(size));
@@ -1137,7 +1176,7 @@ static struct mlx5_ib_mr *reg_create(struct ib_mr *ibmr, struct ib_pd *pd,
mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry);
MLX5_SET(mkc, mkc, free, !populate);
- MLX5_SET(mkc, mkc, access_mode, MLX5_MKC_ACCESS_MODE_MTT);
+ MLX5_SET(mkc, mkc, access_mode_1_0, MLX5_MKC_ACCESS_MODE_MTT);
MLX5_SET(mkc, mkc, a, !!(access_flags & IB_ACCESS_REMOTE_ATOMIC));
MLX5_SET(mkc, mkc, rw, !!(access_flags & IB_ACCESS_REMOTE_WRITE));
MLX5_SET(mkc, mkc, rr, !!(access_flags & IB_ACCESS_REMOTE_READ));
@@ -1193,22 +1232,96 @@ static void set_mr_fileds(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr,
mr->access_flags = access_flags;
}
+static struct ib_mr *mlx5_ib_get_memic_mr(struct ib_pd *pd, u64 memic_addr,
+ u64 length, int acc)
+{
+ struct mlx5_ib_dev *dev = to_mdev(pd->device);
+ int inlen = MLX5_ST_SZ_BYTES(create_mkey_in);
+ struct mlx5_core_dev *mdev = dev->mdev;
+ struct mlx5_ib_mr *mr;
+ void *mkc;
+ u32 *in;
+ int err;
+
+ mr = kzalloc(sizeof(*mr), GFP_KERNEL);
+ if (!mr)
+ return ERR_PTR(-ENOMEM);
+
+ in = kzalloc(inlen, GFP_KERNEL);
+ if (!in) {
+ err = -ENOMEM;
+ goto err_free;
+ }
+
+ mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry);
+
+ MLX5_SET(mkc, mkc, access_mode_1_0, MLX5_MKC_ACCESS_MODE_MEMIC & 0x3);
+ MLX5_SET(mkc, mkc, access_mode_4_2,
+ (MLX5_MKC_ACCESS_MODE_MEMIC >> 2) & 0x7);
+ MLX5_SET(mkc, mkc, a, !!(acc & IB_ACCESS_REMOTE_ATOMIC));
+ MLX5_SET(mkc, mkc, rw, !!(acc & IB_ACCESS_REMOTE_WRITE));
+ MLX5_SET(mkc, mkc, rr, !!(acc & IB_ACCESS_REMOTE_READ));
+ MLX5_SET(mkc, mkc, lw, !!(acc & IB_ACCESS_LOCAL_WRITE));
+ MLX5_SET(mkc, mkc, lr, 1);
+
+ MLX5_SET64(mkc, mkc, len, length);
+ MLX5_SET(mkc, mkc, pd, to_mpd(pd)->pdn);
+ MLX5_SET(mkc, mkc, qpn, 0xffffff);
+ MLX5_SET64(mkc, mkc, start_addr,
+ memic_addr - pci_resource_start(dev->mdev->pdev, 0));
+
+ err = mlx5_core_create_mkey(mdev, &mr->mmkey, in, inlen);
+ if (err)
+ goto err_in;
+
+ kfree(in);
+
+ mr->umem = NULL;
+ set_mr_fileds(dev, mr, 0, length, acc);
+
+ return &mr->ibmr;
+
+err_in:
+ kfree(in);
+
+err_free:
+ kfree(mr);
+
+ return ERR_PTR(err);
+}
+
+struct ib_mr *mlx5_ib_reg_dm_mr(struct ib_pd *pd, struct ib_dm *dm,
+ struct ib_dm_mr_attr *attr,
+ struct uverbs_attr_bundle *attrs)
+{
+ struct mlx5_ib_dm *mdm = to_mdm(dm);
+ u64 memic_addr;
+
+ if (attr->access_flags & ~MLX5_IB_DM_ALLOWED_ACCESS)
+ return ERR_PTR(-EINVAL);
+
+ memic_addr = mdm->dev_addr + attr->offset;
+
+ return mlx5_ib_get_memic_mr(pd, memic_addr, attr->length,
+ attr->access_flags);
+}
+
struct ib_mr *mlx5_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
u64 virt_addr, int access_flags,
struct ib_udata *udata)
{
struct mlx5_ib_dev *dev = to_mdev(pd->device);
struct mlx5_ib_mr *mr = NULL;
+ bool populate_mtts = false;
struct ib_umem *umem;
int page_shift;
int npages;
int ncont;
int order;
int err;
- bool use_umr = true;
if (!IS_ENABLED(CONFIG_INFINIBAND_USER_MEM))
- return ERR_PTR(-EINVAL);
+ return ERR_PTR(-EOPNOTSUPP);
mlx5_ib_dbg(dev, "start 0x%llx, virt_addr 0x%llx, length 0x%llx, access_flags 0x%x\n",
start, virt_addr, length, access_flags);
@@ -1220,6 +1333,8 @@ struct ib_mr *mlx5_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
return ERR_PTR(-EINVAL);
mr = mlx5_ib_alloc_implicit_mr(to_mpd(pd), access_flags);
+ if (IS_ERR(mr))
+ return ERR_CAST(mr);
return &mr->ibmr;
}
#endif
@@ -1230,26 +1345,29 @@ struct ib_mr *mlx5_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
if (err < 0)
return ERR_PTR(err);
- if (order <= mr_cache_max_order(dev)) {
+ if (use_umr(dev, order)) {
mr = alloc_mr_from_cache(pd, umem, virt_addr, length, ncont,
page_shift, order, access_flags);
if (PTR_ERR(mr) == -EAGAIN) {
mlx5_ib_dbg(dev, "cache empty for order %d\n", order);
mr = NULL;
}
+ populate_mtts = false;
} else if (!MLX5_CAP_GEN(dev->mdev, umr_extended_translation_offset)) {
if (access_flags & IB_ACCESS_ON_DEMAND) {
err = -EINVAL;
pr_err("Got MR registration for ODP MR > 512MB, not supported for Connect-IB\n");
goto error;
}
- use_umr = false;
+ populate_mtts = true;
}
if (!mr) {
+ if (!umr_can_modify_entity_size(dev))
+ populate_mtts = true;
mutex_lock(&dev->slow_path_mutex);
mr = reg_create(NULL, pd, virt_addr, length, umem, ncont,
- page_shift, access_flags, !use_umr);
+ page_shift, access_flags, populate_mtts);
mutex_unlock(&dev->slow_path_mutex);
}
@@ -1267,7 +1385,7 @@ struct ib_mr *mlx5_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
update_odp_mr(mr);
#endif
- if (use_umr) {
+ if (!populate_mtts) {
int update_xlt_flags = MLX5_IB_UPD_XLT_ENABLE;
if (access_flags & IB_ACCESS_ON_DEMAND)
@@ -1282,7 +1400,9 @@ struct ib_mr *mlx5_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
}
}
+#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
mr->live = 1;
+#endif
return &mr->ibmr;
error:
ib_umem_release(umem);
@@ -1361,36 +1481,34 @@ int mlx5_ib_rereg_user_mr(struct ib_mr *ib_mr, int flags, u64 start,
ib_umem_release(mr->umem);
err = mr_umem_get(pd, addr, len, access_flags, &mr->umem,
&npages, &page_shift, &ncont, &order);
- if (err < 0) {
- clean_mr(dev, mr);
- return err;
- }
+ if (err)
+ goto err;
}
if (flags & IB_MR_REREG_TRANS && !use_umr_mtt_update(mr, addr, len)) {
/*
* UMR can't be used - MKey needs to be replaced.
*/
- if (mr->allocated_from_cache) {
+ if (mr->allocated_from_cache)
err = unreg_umr(dev, mr);
- if (err)
- mlx5_ib_warn(dev, "Failed to unregister MR\n");
- } else {
+ else
err = destroy_mkey(dev, mr);
- if (err)
- mlx5_ib_warn(dev, "Failed to destroy MKey\n");
- }
if (err)
- return err;
+ goto err;
mr = reg_create(ib_mr, pd, addr, len, mr->umem, ncont,
page_shift, access_flags, true);
- if (IS_ERR(mr))
- return PTR_ERR(mr);
+ if (IS_ERR(mr)) {
+ err = PTR_ERR(mr);
+ mr = to_mmr(ib_mr);
+ goto err;
+ }
mr->allocated_from_cache = 0;
+#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
mr->live = 1;
+#endif
} else {
/*
* Send a UMR WQE
@@ -1413,13 +1531,8 @@ int mlx5_ib_rereg_user_mr(struct ib_mr *ib_mr, int flags, u64 start,
err = rereg_umr(pd, mr, access_flags, flags);
}
- if (err) {
- mlx5_ib_warn(dev, "Failed to rereg UMR\n");
- ib_umem_release(mr->umem);
- mr->umem = NULL;
- clean_mr(dev, mr);
- return err;
- }
+ if (err)
+ goto err;
}
set_mr_fileds(dev, mr, npages, len, access_flags);
@@ -1428,6 +1541,14 @@ int mlx5_ib_rereg_user_mr(struct ib_mr *ib_mr, int flags, u64 start,
update_odp_mr(mr);
#endif
return 0;
+
+err:
+ if (mr->umem) {
+ ib_umem_release(mr->umem);
+ mr->umem = NULL;
+ }
+ clean_mr(dev, mr);
+ return err;
}
static int
@@ -1476,10 +1597,9 @@ mlx5_free_priv_descs(struct mlx5_ib_mr *mr)
}
}
-static int clean_mr(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr)
+static void clean_mr(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr)
{
int allocated_from_cache = mr->allocated_from_cache;
- int err;
if (mr->sig) {
if (mlx5_core_destroy_psv(dev->mdev,
@@ -1496,21 +1616,11 @@ static int clean_mr(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr)
mlx5_free_priv_descs(mr);
- if (!allocated_from_cache) {
- u32 key = mr->mmkey.key;
-
- err = destroy_mkey(dev, mr);
- if (err) {
- mlx5_ib_warn(dev, "failed to destroy mkey 0x%x (%d)\n",
- key, err);
- return err;
- }
- }
-
- return 0;
+ if (!allocated_from_cache)
+ destroy_mkey(dev, mr);
}
-static int dereg_mr(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr)
+static void dereg_mr(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr)
{
int npages = mr->npages;
struct ib_umem *umem = mr->umem;
@@ -1551,16 +1661,12 @@ static int dereg_mr(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr)
kfree(mr);
else
mlx5_mr_cache_free(dev, mr);
-
- return 0;
}
int mlx5_ib_dereg_mr(struct ib_mr *ibmr)
{
- struct mlx5_ib_dev *dev = to_mdev(ibmr->device);
- struct mlx5_ib_mr *mr = to_mmr(ibmr);
-
- return dereg_mr(dev, mr);
+ dereg_mr(to_mdev(ibmr->device), to_mmr(ibmr));
+ return 0;
}
struct ib_mr *mlx5_ib_alloc_mr(struct ib_pd *pd,
@@ -1641,7 +1747,8 @@ struct ib_mr *mlx5_ib_alloc_mr(struct ib_pd *pd,
goto err_free_in;
}
- MLX5_SET(mkc, mkc, access_mode, mr->access_mode);
+ MLX5_SET(mkc, mkc, access_mode_1_0, mr->access_mode & 0x3);
+ MLX5_SET(mkc, mkc, access_mode_4_2, (mr->access_mode >> 2) & 0x7);
MLX5_SET(mkc, mkc, umr_en, 1);
mr->ibmr.device = pd->device;
@@ -1722,7 +1829,7 @@ struct ib_mw *mlx5_ib_alloc_mw(struct ib_pd *pd, enum ib_mw_type type,
MLX5_SET(mkc, mkc, pd, to_mpd(pd)->pdn);
MLX5_SET(mkc, mkc, umr_en, 1);
MLX5_SET(mkc, mkc, lr, 1);
- MLX5_SET(mkc, mkc, access_mode, MLX5_MKC_ACCESS_MODE_KLMS);
+ MLX5_SET(mkc, mkc, access_mode_1_0, MLX5_MKC_ACCESS_MODE_KLMS);
MLX5_SET(mkc, mkc, en_rinval, !!((type == IB_MW_TYPE_2)));
MLX5_SET(mkc, mkc, qpn, 0xffffff);
diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c
index a2e1aa86e133..7ed4b70f6447 100644
--- a/drivers/infiniband/hw/mlx5/qp.c
+++ b/drivers/infiniband/hw/mlx5/qp.c
@@ -36,6 +36,7 @@
#include <rdma/ib_user_verbs.h>
#include <linux/mlx5/fs.h>
#include "mlx5_ib.h"
+#include "ib_rep.h"
/* not supported currently */
static int wq_signature;
@@ -85,7 +86,9 @@ struct mlx5_modify_raw_qp_param {
u16 operation;
u32 set_mask; /* raw_qp_set_mask_map */
- u32 rate_limit;
+
+ struct mlx5_rate_limit rl;
+
u8 rq_q_ctr_id;
};
@@ -877,7 +880,7 @@ static int create_user_qp(struct mlx5_ib_dev *dev, struct ib_pd *pd,
goto err_free;
}
- err = ib_copy_to_udata(udata, resp, sizeof(*resp));
+ err = ib_copy_to_udata(udata, resp, min(udata->outlen, sizeof(*resp)));
if (err) {
mlx5_ib_dbg(dev, "copy failed\n");
goto err_unmap;
@@ -1082,6 +1085,13 @@ static void destroy_raw_packet_qp_tis(struct mlx5_ib_dev *dev,
mlx5_core_destroy_tis(dev->mdev, sq->tisn);
}
+static void destroy_flow_rule_vport_sq(struct mlx5_ib_dev *dev,
+ struct mlx5_ib_sq *sq)
+{
+ if (sq->flow_rule)
+ mlx5_del_flow_rules(sq->flow_rule);
+}
+
static int create_raw_packet_qp_sq(struct mlx5_ib_dev *dev,
struct mlx5_ib_sq *sq, void *qpin,
struct ib_pd *pd)
@@ -1145,8 +1155,15 @@ static int create_raw_packet_qp_sq(struct mlx5_ib_dev *dev,
if (err)
goto err_umem;
+ err = create_flow_rule_vport_sq(dev, sq);
+ if (err)
+ goto err_flow;
+
return 0;
+err_flow:
+ mlx5_core_destroy_sq_tracked(dev->mdev, &sq->base.mqp);
+
err_umem:
ib_umem_release(sq->ubuffer.umem);
sq->ubuffer.umem = NULL;
@@ -1157,6 +1174,7 @@ err_umem:
static void destroy_raw_packet_qp_sq(struct mlx5_ib_dev *dev,
struct mlx5_ib_sq *sq)
{
+ destroy_flow_rule_vport_sq(dev, sq);
mlx5_core_destroy_sq_tracked(dev->mdev, &sq->base.mqp);
ib_umem_release(sq->ubuffer.umem);
}
@@ -1267,6 +1285,10 @@ static int create_raw_packet_qp_tir(struct mlx5_ib_dev *dev,
if (tunnel_offload_en)
MLX5_SET(tirc, tirc, tunneled_offload_en, 1);
+ if (dev->rep)
+ MLX5_SET(tirc, tirc, self_lb_block,
+ MLX5_TIRC_SELF_LB_BLOCK_BLOCK_UNICAST_);
+
err = mlx5_core_create_tir(dev->mdev, in, inlen, &rq->tirn);
kvfree(in);
@@ -1391,6 +1413,7 @@ static int create_rss_raw_qp_tir(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp,
void *tirc;
void *hfso;
u32 selected_fields = 0;
+ u32 outer_l4;
size_t min_resp_len;
u32 tdn = mucontext->tdn;
struct mlx5_ib_create_qp_rss ucmd = {};
@@ -1446,7 +1469,7 @@ static int create_rss_raw_qp_tir(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp,
return -EOPNOTSUPP;
}
- err = ib_copy_to_udata(udata, &resp, min_resp_len);
+ err = ib_copy_to_udata(udata, &resp, min(udata->outlen, sizeof(resp)));
if (err) {
mlx5_ib_dbg(dev, "copy failed\n");
return -EINVAL;
@@ -1521,10 +1544,14 @@ static int create_rss_raw_qp_tir(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp,
MLX5_SET(rx_hash_field_select, hfso, l3_prot_type,
MLX5_L3_PROT_TYPE_IPV6);
- if (((ucmd.rx_hash_fields_mask & MLX5_RX_HASH_SRC_PORT_TCP) ||
- (ucmd.rx_hash_fields_mask & MLX5_RX_HASH_DST_PORT_TCP)) &&
- ((ucmd.rx_hash_fields_mask & MLX5_RX_HASH_SRC_PORT_UDP) ||
- (ucmd.rx_hash_fields_mask & MLX5_RX_HASH_DST_PORT_UDP))) {
+ outer_l4 = ((ucmd.rx_hash_fields_mask & MLX5_RX_HASH_SRC_PORT_TCP) ||
+ (ucmd.rx_hash_fields_mask & MLX5_RX_HASH_DST_PORT_TCP)) << 0 |
+ ((ucmd.rx_hash_fields_mask & MLX5_RX_HASH_SRC_PORT_UDP) ||
+ (ucmd.rx_hash_fields_mask & MLX5_RX_HASH_DST_PORT_UDP)) << 1 |
+ (ucmd.rx_hash_fields_mask & MLX5_RX_HASH_IPSEC_SPI) << 2;
+
+ /* Check that only one l4 protocol is set */
+ if (outer_l4 & (outer_l4 - 1)) {
err = -EINVAL;
goto err;
}
@@ -1555,9 +1582,16 @@ static int create_rss_raw_qp_tir(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp,
(ucmd.rx_hash_fields_mask & MLX5_RX_HASH_DST_PORT_UDP))
selected_fields |= MLX5_HASH_FIELD_SEL_L4_DPORT;
+ if (ucmd.rx_hash_fields_mask & MLX5_RX_HASH_IPSEC_SPI)
+ selected_fields |= MLX5_HASH_FIELD_SEL_IPSEC_SPI;
+
MLX5_SET(rx_hash_field_select, hfso, selected_fields, selected_fields);
create_tir:
+ if (dev->rep)
+ MLX5_SET(tirc, tirc, self_lb_block,
+ MLX5_TIRC_SELF_LB_BLOCK_BLOCK_UNICAST_);
+
err = mlx5_core_create_tir(dev->mdev, in, inlen, &qp->rss_qp.tirn);
if (err)
@@ -2143,7 +2177,6 @@ static struct ib_qp *mlx5_ib_create_dct(struct ib_pd *pd,
struct ib_qp_init_attr *attr,
struct mlx5_ib_create_qp *ucmd)
{
- struct mlx5_ib_dev *dev;
struct mlx5_ib_qp *qp;
int err = 0;
u32 uidx = MLX5_IB_DEFAULT_UIDX;
@@ -2152,8 +2185,6 @@ static struct ib_qp *mlx5_ib_create_dct(struct ib_pd *pd,
if (!attr->srq || !attr->recv_cq)
return ERR_PTR(-EINVAL);
- dev = to_mdev(pd->device);
-
err = get_qp_user_index(to_mucontext(pd->uobject->context),
ucmd, sizeof(*ucmd), &uidx);
if (err)
@@ -2753,8 +2784,9 @@ static int modify_raw_packet_qp_sq(struct mlx5_core_dev *dev,
const struct mlx5_modify_raw_qp_param *raw_qp_param)
{
struct mlx5_ib_qp *ibqp = sq->base.container_mibqp;
- u32 old_rate = ibqp->rate_limit;
- u32 new_rate = old_rate;
+ struct mlx5_rate_limit old_rl = ibqp->rl;
+ struct mlx5_rate_limit new_rl = old_rl;
+ bool new_rate_added = false;
u16 rl_index = 0;
void *in;
void *sqc;
@@ -2776,39 +2808,43 @@ static int modify_raw_packet_qp_sq(struct mlx5_core_dev *dev,
pr_warn("%s: Rate limit can only be changed when SQ is moving to RDY\n",
__func__);
else
- new_rate = raw_qp_param->rate_limit;
+ new_rl = raw_qp_param->rl;
}
- if (old_rate != new_rate) {
- if (new_rate) {
- err = mlx5_rl_add_rate(dev, new_rate, &rl_index);
+ if (!mlx5_rl_are_equal(&old_rl, &new_rl)) {
+ if (new_rl.rate) {
+ err = mlx5_rl_add_rate(dev, &rl_index, &new_rl);
if (err) {
- pr_err("Failed configuring rate %u: %d\n",
- new_rate, err);
+ pr_err("Failed configuring rate limit(err %d): \
+ rate %u, max_burst_sz %u, typical_pkt_sz %u\n",
+ err, new_rl.rate, new_rl.max_burst_sz,
+ new_rl.typical_pkt_sz);
+
goto out;
}
+ new_rate_added = true;
}
MLX5_SET64(modify_sq_in, in, modify_bitmask, 1);
+ /* index 0 means no limit */
MLX5_SET(sqc, sqc, packet_pacing_rate_limit_index, rl_index);
}
err = mlx5_core_modify_sq(dev, sq->base.mqp.qpn, in, inlen);
if (err) {
/* Remove new rate from table if failed */
- if (new_rate &&
- old_rate != new_rate)
- mlx5_rl_remove_rate(dev, new_rate);
+ if (new_rate_added)
+ mlx5_rl_remove_rate(dev, &new_rl);
goto out;
}
/* Only remove the old rate after new rate was set */
- if ((old_rate &&
- (old_rate != new_rate)) ||
+ if ((old_rl.rate &&
+ !mlx5_rl_are_equal(&old_rl, &new_rl)) ||
(new_state != MLX5_SQC_STATE_RDY))
- mlx5_rl_remove_rate(dev, old_rate);
+ mlx5_rl_remove_rate(dev, &old_rl);
- ibqp->rate_limit = new_rate;
+ ibqp->rl = new_rl;
sq->state = new_state;
out:
@@ -2885,7 +2921,8 @@ static int modify_raw_packet_qp(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp,
static int __mlx5_ib_modify_qp(struct ib_qp *ibqp,
const struct ib_qp_attr *attr, int attr_mask,
- enum ib_qp_state cur_state, enum ib_qp_state new_state)
+ enum ib_qp_state cur_state, enum ib_qp_state new_state,
+ const struct mlx5_ib_modify_qp *ucmd)
{
static const u16 optab[MLX5_QP_NUM_STATE][MLX5_QP_NUM_STATE] = {
[MLX5_QP_STATE_RST] = {
@@ -2938,18 +2975,16 @@ static int __mlx5_ib_modify_qp(struct ib_qp *ibqp,
u16 op;
u8 tx_affinity = 0;
+ mlx5_st = to_mlx5_st(ibqp->qp_type == IB_QPT_DRIVER ?
+ qp->qp_sub_type : ibqp->qp_type);
+ if (mlx5_st < 0)
+ return -EINVAL;
+
context = kzalloc(sizeof(*context), GFP_KERNEL);
if (!context)
return -ENOMEM;
- err = to_mlx5_st(ibqp->qp_type == IB_QPT_DRIVER ?
- qp->qp_sub_type : ibqp->qp_type);
- if (err < 0) {
- mlx5_ib_dbg(dev, "unsupported qp type %d\n", ibqp->qp_type);
- goto out;
- }
-
- context->flags = cpu_to_be32(err << 16);
+ context->flags = cpu_to_be32(mlx5_st << 16);
if (!(attr_mask & IB_QP_PATH_MIG_STATE)) {
context->flags |= cpu_to_be32(MLX5_QP_PM_MIGRATED << 11);
@@ -3103,10 +3138,6 @@ static int __mlx5_ib_modify_qp(struct ib_qp *ibqp,
mlx5_cur = to_mlx5_state(cur_state);
mlx5_new = to_mlx5_state(new_state);
- mlx5_st = to_mlx5_st(ibqp->qp_type == IB_QPT_DRIVER ?
- qp->qp_sub_type : ibqp->qp_type);
- if (mlx5_st < 0)
- goto out;
if (mlx5_cur >= MLX5_QP_NUM_STATE || mlx5_new >= MLX5_QP_NUM_STATE ||
!optab[mlx5_cur][mlx5_new]) {
@@ -3129,7 +3160,30 @@ static int __mlx5_ib_modify_qp(struct ib_qp *ibqp,
}
if (attr_mask & IB_QP_RATE_LIMIT) {
- raw_qp_param.rate_limit = attr->rate_limit;
+ raw_qp_param.rl.rate = attr->rate_limit;
+
+ if (ucmd->burst_info.max_burst_sz) {
+ if (attr->rate_limit &&
+ MLX5_CAP_QOS(dev->mdev, packet_pacing_burst_bound)) {
+ raw_qp_param.rl.max_burst_sz =
+ ucmd->burst_info.max_burst_sz;
+ } else {
+ err = -EINVAL;
+ goto out;
+ }
+ }
+
+ if (ucmd->burst_info.typical_pkt_sz) {
+ if (attr->rate_limit &&
+ MLX5_CAP_QOS(dev->mdev, packet_pacing_typical_size)) {
+ raw_qp_param.rl.typical_pkt_sz =
+ ucmd->burst_info.typical_pkt_sz;
+ } else {
+ err = -EINVAL;
+ goto out;
+ }
+ }
+
raw_qp_param.set_mask |= MLX5_RAW_QP_RATE_LIMIT;
}
@@ -3157,7 +3211,8 @@ static int __mlx5_ib_modify_qp(struct ib_qp *ibqp,
* If we moved a kernel QP to RESET, clean up all old CQ
* entries and reinitialize the QP.
*/
- if (new_state == IB_QPS_RESET && !ibqp->uobject) {
+ if (new_state == IB_QPS_RESET &&
+ !ibqp->uobject && ibqp->qp_type != IB_QPT_XRC_TGT) {
mlx5_ib_cq_clean(recv_cq, base->mqp.qpn,
ibqp->srq ? to_msrq(ibqp->srq) : NULL);
if (send_cq != recv_cq)
@@ -3316,8 +3371,10 @@ int mlx5_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
{
struct mlx5_ib_dev *dev = to_mdev(ibqp->device);
struct mlx5_ib_qp *qp = to_mqp(ibqp);
+ struct mlx5_ib_modify_qp ucmd = {};
enum ib_qp_type qp_type;
enum ib_qp_state cur_state, new_state;
+ size_t required_cmd_sz;
int err = -EINVAL;
int port;
enum rdma_link_layer ll = IB_LINK_LAYER_UNSPECIFIED;
@@ -3325,6 +3382,28 @@ int mlx5_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
if (ibqp->rwq_ind_tbl)
return -ENOSYS;
+ if (udata && udata->inlen) {
+ required_cmd_sz = offsetof(typeof(ucmd), reserved) +
+ sizeof(ucmd.reserved);
+ if (udata->inlen < required_cmd_sz)
+ return -EINVAL;
+
+ if (udata->inlen > sizeof(ucmd) &&
+ !ib_is_udata_cleared(udata, sizeof(ucmd),
+ udata->inlen - sizeof(ucmd)))
+ return -EOPNOTSUPP;
+
+ if (ib_copy_from_udata(&ucmd, udata,
+ min(udata->inlen, sizeof(ucmd))))
+ return -EFAULT;
+
+ if (ucmd.comp_mask ||
+ memchr_inv(&ucmd.reserved, 0, sizeof(ucmd.reserved)) ||
+ memchr_inv(&ucmd.burst_info.reserved, 0,
+ sizeof(ucmd.burst_info.reserved)))
+ return -EOPNOTSUPP;
+ }
+
if (unlikely(ibqp->qp_type == IB_QPT_GSI))
return mlx5_ib_gsi_modify_qp(ibqp, attr, attr_mask);
@@ -3405,7 +3484,8 @@ int mlx5_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
goto out;
}
- err = __mlx5_ib_modify_qp(ibqp, attr, attr_mask, cur_state, new_state);
+ err = __mlx5_ib_modify_qp(ibqp, attr, attr_mask, cur_state,
+ new_state, &ucmd);
out:
mutex_unlock(&qp->mutex);
@@ -3625,8 +3705,19 @@ static __be64 get_umr_update_pd_mask(void)
return cpu_to_be64(result);
}
-static void set_reg_umr_segment(struct mlx5_wqe_umr_ctrl_seg *umr,
- struct ib_send_wr *wr, int atomic)
+static int umr_check_mkey_mask(struct mlx5_ib_dev *dev, u64 mask)
+{
+ if ((mask & MLX5_MKEY_MASK_PAGE_SIZE &&
+ MLX5_CAP_GEN(dev->mdev, umr_modify_entity_size_disabled)) ||
+ (mask & MLX5_MKEY_MASK_A &&
+ MLX5_CAP_GEN(dev->mdev, umr_modify_atomic_disabled)))
+ return -EPERM;
+ return 0;
+}
+
+static int set_reg_umr_segment(struct mlx5_ib_dev *dev,
+ struct mlx5_wqe_umr_ctrl_seg *umr,
+ struct ib_send_wr *wr, int atomic)
{
struct mlx5_umr_wr *umrwr = umr_wr(wr);
@@ -3658,6 +3749,8 @@ static void set_reg_umr_segment(struct mlx5_wqe_umr_ctrl_seg *umr,
if (!wr->num_sge)
umr->flags |= MLX5_UMR_INLINE;
+
+ return umr_check_mkey_mask(dev, be64_to_cpu(umr->mkey_mask));
}
static u8 get_umr_flags(int acc)
@@ -4480,7 +4573,9 @@ int mlx5_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
}
qp->sq.wr_data[idx] = MLX5_IB_WR_UMR;
ctrl->imm = cpu_to_be32(umr_wr(wr)->mkey);
- set_reg_umr_segment(seg, wr, !!(MLX5_CAP_GEN(mdev, atomic)));
+ err = set_reg_umr_segment(dev, seg, wr, !!(MLX5_CAP_GEN(mdev, atomic)));
+ if (unlikely(err))
+ goto out;
seg += sizeof(struct mlx5_wqe_umr_ctrl_seg);
size += sizeof(struct mlx5_wqe_umr_ctrl_seg) / 16;
if (unlikely((seg == qend)))
@@ -4718,26 +4813,14 @@ static int query_raw_packet_qp_sq_state(struct mlx5_ib_dev *dev,
struct mlx5_ib_sq *sq,
u8 *sq_state)
{
- void *out;
- void *sqc;
- int inlen;
int err;
- inlen = MLX5_ST_SZ_BYTES(query_sq_out);
- out = kvzalloc(inlen, GFP_KERNEL);
- if (!out)
- return -ENOMEM;
-
- err = mlx5_core_query_sq(dev->mdev, sq->base.mqp.qpn, out);
+ err = mlx5_core_query_sq_state(dev->mdev, sq->base.mqp.qpn, sq_state);
if (err)
goto out;
-
- sqc = MLX5_ADDR_OF(query_sq_out, out, sq_context);
- *sq_state = MLX5_GET(sqc, sqc, state);
sq->state = *sq_state;
out:
- kvfree(out);
return err;
}
diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c
index 6fee7795d1c8..541f237965c7 100644
--- a/drivers/infiniband/hw/mthca/mthca_provider.c
+++ b/drivers/infiniband/hw/mthca/mthca_provider.c
@@ -1295,6 +1295,7 @@ int mthca_register_device(struct mthca_dev *dev)
mutex_init(&dev->cap_mask_mutex);
+ dev->ib_dev.driver_id = RDMA_DRIVER_MTHCA;
ret = ib_register_device(&dev->ib_dev, NULL);
if (ret)
return ret;
diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c
index 162475aeeedd..1040a6e34230 100644
--- a/drivers/infiniband/hw/nes/nes_verbs.c
+++ b/drivers/infiniband/hw/nes/nes_verbs.c
@@ -3854,6 +3854,7 @@ int nes_register_ofa_device(struct nes_ib_device *nesibdev)
struct nes_adapter *nesadapter = nesdev->nesadapter;
int i, ret;
+ nesvnic->nesibdev->ibdev.driver_id = RDMA_DRIVER_NES;
ret = ib_register_device(&nesvnic->nesibdev->ibdev, NULL);
if (ret) {
return ret;
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_ah.c b/drivers/infiniband/hw/ocrdma/ocrdma_ah.c
index dec650930ca6..3897b64532e1 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_ah.c
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_ah.c
@@ -193,11 +193,9 @@ struct ib_ah *ocrdma_create_ah(struct ib_pd *ibpd, struct rdma_ah_attr *attr,
__func__, status);
goto av_conf_err;
}
- if (sgid_attr.ndev) {
- if (is_vlan_dev(sgid_attr.ndev))
- vlan_tag = vlan_dev_vlan_id(sgid_attr.ndev);
- dev_put(sgid_attr.ndev);
- }
+ if (is_vlan_dev(sgid_attr.ndev))
+ vlan_tag = vlan_dev_vlan_id(sgid_attr.ndev);
+ dev_put(sgid_attr.ndev);
/* Get network header type for this GID */
ah->hdr_type = ib_gid_to_network_type(sgid_attr.gid_type, &sgid);
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_hw.c b/drivers/infiniband/hw/ocrdma/ocrdma_hw.c
index 9904918589a4..2c260e1c29d1 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_hw.c
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_hw.c
@@ -2014,7 +2014,7 @@ static int ocrdma_mbx_reg_mr_cont(struct ocrdma_dev *dev,
struct ocrdma_hw_mr *hwmr, u32 pbl_cnt,
u32 pbl_offset, u32 last)
{
- int status = -ENOMEM;
+ int status;
int i;
struct ocrdma_reg_nsmr_cont *cmd;
@@ -2033,9 +2033,7 @@ static int ocrdma_mbx_reg_mr_cont(struct ocrdma_dev *dev,
upper_32_bits(hwmr->pbl_table[i + pbl_offset].pa);
}
status = ocrdma_mbx_cmd(dev, (struct ocrdma_mqe *)cmd);
- if (status)
- goto mbx_err;
-mbx_err:
+
kfree(cmd);
return status;
}
@@ -2496,7 +2494,7 @@ static int ocrdma_set_av_params(struct ocrdma_qp *qp,
{
int status;
struct rdma_ah_attr *ah_attr = &attrs->ah_attr;
- union ib_gid sgid, zgid;
+ union ib_gid sgid;
struct ib_gid_attr sgid_attr;
u32 vlan_id = 0xFFFF;
u8 mac_addr[6], hdr_type;
@@ -2529,16 +2527,12 @@ static int ocrdma_set_av_params(struct ocrdma_qp *qp,
status = ib_get_cached_gid(&dev->ibdev, 1, grh->sgid_index,
&sgid, &sgid_attr);
- if (!status && sgid_attr.ndev) {
+ if (!status) {
vlan_id = rdma_vlan_dev_vlan_id(sgid_attr.ndev);
memcpy(mac_addr, sgid_attr.ndev->dev_addr, ETH_ALEN);
dev_put(sgid_attr.ndev);
}
- memset(&zgid, 0, sizeof(zgid));
- if (!memcmp(&sgid, &zgid, sizeof(zgid)))
- return -EINVAL;
-
qp->sgid_idx = grh->sgid_index;
memcpy(&cmd->params.sgid[0], &sgid.raw[0], sizeof(cmd->params.sgid));
status = ocrdma_resolve_dmac(dev, ah_attr, &mac_addr[0]);
@@ -3133,12 +3127,12 @@ done:
static int ocrdma_mbx_modify_eqd(struct ocrdma_dev *dev, struct ocrdma_eq *eq,
int num)
{
- int i, status = -ENOMEM;
+ int i, status;
struct ocrdma_modify_eqd_req *cmd;
cmd = ocrdma_init_emb_mqe(OCRDMA_CMD_MODIFY_EQ_DELAY, sizeof(*cmd));
if (!cmd)
- return status;
+ return -ENOMEM;
ocrdma_init_mch(&cmd->cmd.req, OCRDMA_CMD_MODIFY_EQ_DELAY,
OCRDMA_SUBSYS_COMMON, sizeof(*cmd));
@@ -3151,9 +3145,7 @@ static int ocrdma_mbx_modify_eqd(struct ocrdma_dev *dev, struct ocrdma_eq *eq,
(eq[i].aic_obj.prev_eqd * 65)/100;
}
status = ocrdma_mbx_cmd(dev, (struct ocrdma_mqe *)cmd);
- if (status)
- goto mbx_err;
-mbx_err:
+
kfree(cmd);
return status;
}
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_main.c b/drivers/infiniband/hw/ocrdma/ocrdma_main.c
index fbfbd9e96147..eb8b6a935016 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_main.c
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_main.c
@@ -158,10 +158,7 @@ static int ocrdma_register_device(struct ocrdma_dev *dev)
dev->ibdev.query_device = ocrdma_query_device;
dev->ibdev.query_port = ocrdma_query_port;
dev->ibdev.modify_port = ocrdma_modify_port;
- dev->ibdev.query_gid = ocrdma_query_gid;
dev->ibdev.get_netdev = ocrdma_get_netdev;
- dev->ibdev.add_gid = ocrdma_add_gid;
- dev->ibdev.del_gid = ocrdma_del_gid;
dev->ibdev.get_link_layer = ocrdma_link_layer;
dev->ibdev.alloc_pd = ocrdma_alloc_pd;
dev->ibdev.dealloc_pd = ocrdma_dealloc_pd;
@@ -217,6 +214,7 @@ static int ocrdma_register_device(struct ocrdma_dev *dev)
dev->ibdev.destroy_srq = ocrdma_destroy_srq;
dev->ibdev.post_srq_recv = ocrdma_post_srq_recv;
}
+ dev->ibdev.driver_id = RDMA_DRIVER_OCRDMA;
return ib_register_device(&dev->ibdev, NULL);
}
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
index 8009bdad4e5b..784ed6b09a46 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
@@ -62,40 +62,6 @@ int ocrdma_query_pkey(struct ib_device *ibdev, u8 port, u16 index, u16 *pkey)
return 0;
}
-int ocrdma_query_gid(struct ib_device *ibdev, u8 port,
- int index, union ib_gid *sgid)
-{
- int ret;
-
- memset(sgid, 0, sizeof(*sgid));
- if (index >= OCRDMA_MAX_SGID)
- return -EINVAL;
-
- ret = ib_get_cached_gid(ibdev, port, index, sgid, NULL);
- if (ret == -EAGAIN) {
- memcpy(sgid, &zgid, sizeof(*sgid));
- return 0;
- }
-
- return ret;
-}
-
-int ocrdma_add_gid(struct ib_device *device,
- u8 port_num,
- unsigned int index,
- const union ib_gid *gid,
- const struct ib_gid_attr *attr,
- void **context) {
- return 0;
-}
-
-int ocrdma_del_gid(struct ib_device *device,
- u8 port_num,
- unsigned int index,
- void **context) {
- return 0;
-}
-
int ocrdma_query_device(struct ib_device *ibdev, struct ib_device_attr *attr,
struct ib_udata *uhw)
{
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.h b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.h
index 704ef1e9271b..9a9971708646 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.h
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.h
@@ -61,19 +61,7 @@ enum rdma_protocol_type
ocrdma_query_protocol(struct ib_device *device, u8 port_num);
void ocrdma_get_guid(struct ocrdma_dev *, u8 *guid);
-int ocrdma_query_gid(struct ib_device *, u8 port,
- int index, union ib_gid *gid);
struct net_device *ocrdma_get_netdev(struct ib_device *device, u8 port_num);
-int ocrdma_add_gid(struct ib_device *device,
- u8 port_num,
- unsigned int index,
- const union ib_gid *gid,
- const struct ib_gid_attr *attr,
- void **context);
-int ocrdma_del_gid(struct ib_device *device,
- u8 port_num,
- unsigned int index,
- void **context);
int ocrdma_query_pkey(struct ib_device *, u8 port, u16 index, u16 *pkey);
struct ib_ucontext *ocrdma_alloc_ucontext(struct ib_device *,
diff --git a/drivers/infiniband/hw/qedr/main.c b/drivers/infiniband/hw/qedr/main.c
index db4bf97c0e15..f4cb60b658ea 100644
--- a/drivers/infiniband/hw/qedr/main.c
+++ b/drivers/infiniband/hw/qedr/main.c
@@ -90,8 +90,8 @@ static struct net_device *qedr_get_netdev(struct ib_device *dev, u8 port_num)
dev_hold(qdev->ndev);
/* The HW vendor's device driver must guarantee
- * that this function returns NULL before the net device reaches
- * NETDEV_UNREGISTER_FINAL state.
+ * that this function returns NULL before the net device has finished
+ * NETDEV_UNREGISTER state.
*/
return qdev->ndev;
}
@@ -162,10 +162,6 @@ static int qedr_iw_register_device(struct qedr_dev *dev)
static void qedr_roce_register_device(struct qedr_dev *dev)
{
dev->ibdev.node_type = RDMA_NODE_IB_CA;
- dev->ibdev.query_gid = qedr_query_gid;
-
- dev->ibdev.add_gid = qedr_add_gid;
- dev->ibdev.del_gid = qedr_del_gid;
dev->ibdev.get_port_immutable = qedr_roce_port_immutable;
}
@@ -257,6 +253,7 @@ static int qedr_register_device(struct qedr_dev *dev)
dev->ibdev.get_link_layer = qedr_link_layer;
dev->ibdev.get_dev_fw_str = qedr_get_dev_fw_str;
+ dev->ibdev.driver_id = RDMA_DRIVER_QEDR;
return ib_register_device(&dev->ibdev, NULL);
}
@@ -707,7 +704,7 @@ static void qedr_affiliated_event(void *context, u8 e_code, void *fw_handle)
"Error: CQ event with NULL pointer ibcq. Handle=%llx\n",
roce_handle64);
}
- DP_ERR(dev, "CQ event %d on hanlde %p\n", e_code, cq);
+ DP_ERR(dev, "CQ event %d on handle %p\n", e_code, cq);
break;
case EVENT_TYPE_QP:
qp = (struct qedr_qp *)(uintptr_t)roce_handle64;
@@ -723,7 +720,7 @@ static void qedr_affiliated_event(void *context, u8 e_code, void *fw_handle)
"Error: QP event with NULL pointer ibqp. Handle=%llx\n",
roce_handle64);
}
- DP_ERR(dev, "QP event %d on hanlde %p\n", e_code, qp);
+ DP_ERR(dev, "QP event %d on handle %p\n", e_code, qp);
break;
default:
break;
@@ -833,7 +830,8 @@ static struct qedr_dev *qedr_add(struct qed_dev *cdev, struct pci_dev *pdev,
dev->num_cnq = dev->ops->rdma_get_min_cnq_msix(cdev);
if (!dev->num_cnq) {
- DP_ERR(dev, "not enough CNQ resources.\n");
+ DP_ERR(dev, "Failed. At least one CNQ is required.\n");
+ rc = -ENOMEM;
goto init_err;
}
diff --git a/drivers/infiniband/hw/qedr/qedr_hsi_rdma.h b/drivers/infiniband/hw/qedr/qedr_hsi_rdma.h
index 78b49002fbd2..b816c80df50b 100644
--- a/drivers/infiniband/hw/qedr/qedr_hsi_rdma.h
+++ b/drivers/infiniband/hw/qedr/qedr_hsi_rdma.h
@@ -45,7 +45,7 @@ struct rdma_cqe_responder {
__le32 imm_data_or_inv_r_Key;
__le32 length;
__le32 imm_data_hi;
- __le16 rq_cons;
+ __le16 rq_cons_or_srq_id;
u8 flags;
#define RDMA_CQE_RESPONDER_TOGGLE_BIT_MASK 0x1
#define RDMA_CQE_RESPONDER_TOGGLE_BIT_SHIFT 0
@@ -115,6 +115,7 @@ enum rdma_cqe_requester_status_enum {
RDMA_CQE_REQ_STS_RNR_NAK_RETRY_CNT_ERR,
RDMA_CQE_REQ_STS_TRANSPORT_RETRY_CNT_ERR,
RDMA_CQE_REQ_STS_WORK_REQUEST_FLUSHED_ERR,
+ RDMA_CQE_REQ_STS_XRC_VOILATION_ERR,
MAX_RDMA_CQE_REQUESTER_STATUS_ENUM
};
@@ -136,6 +137,7 @@ enum rdma_cqe_type {
RDMA_CQE_TYPE_REQUESTER,
RDMA_CQE_TYPE_RESPONDER_RQ,
RDMA_CQE_TYPE_RESPONDER_SRQ,
+ RDMA_CQE_TYPE_RESPONDER_XRC_SRQ,
RDMA_CQE_TYPE_INVALID,
MAX_RDMA_CQE_TYPE
};
diff --git a/drivers/infiniband/hw/qedr/qedr_roce_cm.c b/drivers/infiniband/hw/qedr/qedr_roce_cm.c
index 2bdbb12bfc69..0f14e687bb91 100644
--- a/drivers/infiniband/hw/qedr/qedr_roce_cm.c
+++ b/drivers/infiniband/hw/qedr/qedr_roce_cm.c
@@ -412,19 +412,11 @@ static inline int qedr_gsi_build_header(struct qedr_dev *dev,
return rc;
}
- if (sgid_attr.ndev) {
- vlan_id = rdma_vlan_dev_vlan_id(sgid_attr.ndev);
- if (vlan_id < VLAN_CFI_MASK)
- has_vlan = true;
+ vlan_id = rdma_vlan_dev_vlan_id(sgid_attr.ndev);
+ if (vlan_id < VLAN_CFI_MASK)
+ has_vlan = true;
- dev_put(sgid_attr.ndev);
- }
-
- if (!memcmp(&sgid, &zgid, sizeof(sgid))) {
- DP_ERR(dev, "gsi post send: GID not found GID index %d\n",
- grh->sgid_index);
- return -ENOENT;
- }
+ dev_put(sgid_attr.ndev);
has_udp = (sgid_attr.gid_type == IB_GID_TYPE_ROCE_UDP_ENCAP);
if (!has_udp) {
diff --git a/drivers/infiniband/hw/qedr/verbs.c b/drivers/infiniband/hw/qedr/verbs.c
index 875b17272d65..7d3763b2e01c 100644
--- a/drivers/infiniband/hw/qedr/verbs.c
+++ b/drivers/infiniband/hw/qedr/verbs.c
@@ -84,58 +84,6 @@ int qedr_iw_query_gid(struct ib_device *ibdev, u8 port,
return 0;
}
-int qedr_query_gid(struct ib_device *ibdev, u8 port, int index,
- union ib_gid *sgid)
-{
- struct qedr_dev *dev = get_qedr_dev(ibdev);
- int rc = 0;
-
- if (!rdma_cap_roce_gid_table(ibdev, port))
- return -ENODEV;
-
- rc = ib_get_cached_gid(ibdev, port, index, sgid, NULL);
- if (rc == -EAGAIN) {
- memcpy(sgid, &zgid, sizeof(*sgid));
- return 0;
- }
-
- DP_DEBUG(dev, QEDR_MSG_INIT, "query gid: index=%d %llx:%llx\n", index,
- sgid->global.interface_id, sgid->global.subnet_prefix);
-
- return rc;
-}
-
-int qedr_add_gid(struct ib_device *device, u8 port_num,
- unsigned int index, const union ib_gid *gid,
- const struct ib_gid_attr *attr, void **context)
-{
- if (!rdma_cap_roce_gid_table(device, port_num))
- return -EINVAL;
-
- if (port_num > QEDR_MAX_PORT)
- return -EINVAL;
-
- if (!context)
- return -EINVAL;
-
- return 0;
-}
-
-int qedr_del_gid(struct ib_device *device, u8 port_num,
- unsigned int index, void **context)
-{
- if (!rdma_cap_roce_gid_table(device, port_num))
- return -EINVAL;
-
- if (port_num > QEDR_MAX_PORT)
- return -EINVAL;
-
- if (!context)
- return -EINVAL;
-
- return 0;
-}
-
int qedr_query_device(struct ib_device *ibdev,
struct ib_device_attr *attr, struct ib_udata *udata)
{
@@ -525,9 +473,9 @@ struct ib_pd *qedr_alloc_pd(struct ib_device *ibdev,
pd->pd_id = pd_id;
if (udata && context) {
- struct qedr_alloc_pd_uresp uresp;
-
- uresp.pd_id = pd_id;
+ struct qedr_alloc_pd_uresp uresp = {
+ .pd_id = pd_id,
+ };
rc = qedr_ib_copy_to_udata(udata, &uresp, sizeof(uresp));
if (rc) {
@@ -856,8 +804,6 @@ static inline void qedr_init_cq_params(struct qedr_cq *cq,
static void doorbell_cq(struct qedr_cq *cq, u32 cons, u8 flags)
{
- /* Flush data before signalling doorbell */
- wmb();
cq->db.data.agg_flags = flags;
cq->db.data.value = cpu_to_le32(cons);
writeq(cq->db.raw, cq->db_addr);
@@ -1145,46 +1091,41 @@ static inline int get_gid_info_from_table(struct ib_qp *ibqp,
if (rc)
return rc;
- if (!memcmp(&gid, &zgid, sizeof(gid)))
- return -ENOENT;
-
- if (gid_attr.ndev) {
- qp_params->vlan_id = rdma_vlan_dev_vlan_id(gid_attr.ndev);
-
- dev_put(gid_attr.ndev);
- nw_type = ib_gid_to_network_type(gid_attr.gid_type, &gid);
- switch (nw_type) {
- case RDMA_NETWORK_IPV6:
- memcpy(&qp_params->sgid.bytes[0], &gid.raw[0],
- sizeof(qp_params->sgid));
- memcpy(&qp_params->dgid.bytes[0],
- &grh->dgid,
- sizeof(qp_params->dgid));
- qp_params->roce_mode = ROCE_V2_IPV6;
- SET_FIELD(qp_params->modify_flags,
- QED_ROCE_MODIFY_QP_VALID_ROCE_MODE, 1);
- break;
- case RDMA_NETWORK_IB:
- memcpy(&qp_params->sgid.bytes[0], &gid.raw[0],
- sizeof(qp_params->sgid));
- memcpy(&qp_params->dgid.bytes[0],
- &grh->dgid,
- sizeof(qp_params->dgid));
- qp_params->roce_mode = ROCE_V1;
- break;
- case RDMA_NETWORK_IPV4:
- memset(&qp_params->sgid, 0, sizeof(qp_params->sgid));
- memset(&qp_params->dgid, 0, sizeof(qp_params->dgid));
- ipv4_addr = qedr_get_ipv4_from_gid(gid.raw);
- qp_params->sgid.ipv4_addr = ipv4_addr;
- ipv4_addr =
- qedr_get_ipv4_from_gid(grh->dgid.raw);
- qp_params->dgid.ipv4_addr = ipv4_addr;
- SET_FIELD(qp_params->modify_flags,
- QED_ROCE_MODIFY_QP_VALID_ROCE_MODE, 1);
- qp_params->roce_mode = ROCE_V2_IPV4;
- break;
- }
+ qp_params->vlan_id = rdma_vlan_dev_vlan_id(gid_attr.ndev);
+
+ dev_put(gid_attr.ndev);
+ nw_type = ib_gid_to_network_type(gid_attr.gid_type, &gid);
+ switch (nw_type) {
+ case RDMA_NETWORK_IPV6:
+ memcpy(&qp_params->sgid.bytes[0], &gid.raw[0],
+ sizeof(qp_params->sgid));
+ memcpy(&qp_params->dgid.bytes[0],
+ &grh->dgid,
+ sizeof(qp_params->dgid));
+ qp_params->roce_mode = ROCE_V2_IPV6;
+ SET_FIELD(qp_params->modify_flags,
+ QED_ROCE_MODIFY_QP_VALID_ROCE_MODE, 1);
+ break;
+ case RDMA_NETWORK_IB:
+ memcpy(&qp_params->sgid.bytes[0], &gid.raw[0],
+ sizeof(qp_params->sgid));
+ memcpy(&qp_params->dgid.bytes[0],
+ &grh->dgid,
+ sizeof(qp_params->dgid));
+ qp_params->roce_mode = ROCE_V1;
+ break;
+ case RDMA_NETWORK_IPV4:
+ memset(&qp_params->sgid, 0, sizeof(qp_params->sgid));
+ memset(&qp_params->dgid, 0, sizeof(qp_params->dgid));
+ ipv4_addr = qedr_get_ipv4_from_gid(gid.raw);
+ qp_params->sgid.ipv4_addr = ipv4_addr;
+ ipv4_addr =
+ qedr_get_ipv4_from_gid(grh->dgid.raw);
+ qp_params->dgid.ipv4_addr = ipv4_addr;
+ SET_FIELD(qp_params->modify_flags,
+ QED_ROCE_MODIFY_QP_VALID_ROCE_MODE, 1);
+ qp_params->roce_mode = ROCE_V2_IPV4;
+ break;
}
for (i = 0; i < 4; i++) {
@@ -1841,14 +1782,15 @@ static void qedr_reset_qp_hwq_info(struct qedr_qp_hwq_info *qph)
static int qedr_update_qp_state(struct qedr_dev *dev,
struct qedr_qp *qp,
+ enum qed_roce_qp_state cur_state,
enum qed_roce_qp_state new_state)
{
int status = 0;
- if (new_state == qp->state)
+ if (new_state == cur_state)
return 0;
- switch (qp->state) {
+ switch (cur_state) {
case QED_ROCE_QP_STATE_RESET:
switch (new_state) {
case QED_ROCE_QP_STATE_INIT:
@@ -1869,7 +1811,6 @@ static int qedr_update_qp_state(struct qedr_dev *dev,
*/
if (rdma_protocol_roce(&dev->ibdev, 1)) {
- wmb();
writel(qp->rq.db_data.raw, qp->rq.db);
/* Make sure write takes effect */
mmiowb();
@@ -1955,6 +1896,7 @@ int qedr_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
struct qedr_dev *dev = get_qedr_dev(&qp->dev->ibdev);
const struct ib_global_route *grh = rdma_ah_read_grh(&attr->ah_attr);
enum ib_qp_state old_qp_state, new_qp_state;
+ enum qed_roce_qp_state cur_state;
int rc = 0;
DP_DEBUG(dev, QEDR_MSG_QP,
@@ -2086,18 +2028,23 @@ int qedr_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
SET_FIELD(qp_params.modify_flags,
QED_ROCE_MODIFY_QP_VALID_ACK_TIMEOUT, 1);
- qp_params.ack_timeout = attr->timeout;
- if (attr->timeout) {
- u32 temp;
-
- temp = 4096 * (1UL << attr->timeout) / 1000 / 1000;
- /* FW requires [msec] */
- qp_params.ack_timeout = temp;
- } else {
- /* Infinite */
+ /* The received timeout value is an exponent used like this:
+ * "12.7.34 LOCAL ACK TIMEOUT
+ * Value representing the transport (ACK) timeout for use by
+ * the remote, expressed as: 4.096 * 2^timeout [usec]"
+ * The FW expects timeout in msec so we need to divide the usec
+ * result by 1000. We'll approximate 1000~2^10, and 4.096 ~ 2^2,
+ * so we get: 2^2 * 2^timeout / 2^10 = 2^(timeout - 8).
+ * The value of zero means infinite so we use a 'max_t' to make
+ * sure that sub 1 msec values will be configured as 1 msec.
+ */
+ if (attr->timeout)
+ qp_params.ack_timeout =
+ 1 << max_t(int, attr->timeout - 8, 0);
+ else
qp_params.ack_timeout = 0;
- }
}
+
if (attr_mask & IB_QP_RETRY_CNT) {
SET_FIELD(qp_params.modify_flags,
QED_ROCE_MODIFY_QP_VALID_RETRY_CNT, 1);
@@ -2170,13 +2117,25 @@ int qedr_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
qp->dest_qp_num = attr->dest_qp_num;
}
+ cur_state = qp->state;
+
+ /* Update the QP state before the actual ramrod to prevent a race with
+ * fast path. Modifying the QP state to error will cause the device to
+ * flush the CQEs and while polling the flushed CQEs will considered as
+ * a potential issue if the QP isn't in error state.
+ */
+ if ((attr_mask & IB_QP_STATE) && qp->qp_type != IB_QPT_GSI &&
+ !udata && qp_params.new_state == QED_ROCE_QP_STATE_ERR)
+ qp->state = QED_ROCE_QP_STATE_ERR;
+
if (qp->qp_type != IB_QPT_GSI)
rc = dev->ops->rdma_modify_qp(dev->rdma_ctx,
qp->qed_qp, &qp_params);
if (attr_mask & IB_QP_STATE) {
if ((qp->qp_type != IB_QPT_GSI) && (!udata))
- rc = qedr_update_qp_state(dev, qp, qp_params.new_state);
+ rc = qedr_update_qp_state(dev, qp, cur_state,
+ qp_params.new_state);
qp->state = qp_params.new_state;
}
@@ -3255,8 +3214,15 @@ int qedr_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
* vane. However this is not harmful (as long as the producer value is
* unchanged). For performance reasons we avoid checking for this
* redundant doorbell.
+ *
+ * qp->wqe_wr_id is accessed during qedr_poll_cq, as
+ * soon as we give the doorbell, we could get a completion
+ * for this wr, therefore we need to make sure that the
+ * memory is updated before giving the doorbell.
+ * During qedr_poll_cq, rmb is called before accessing the
+ * cqe. This covers for the smp_rmb as well.
*/
- wmb();
+ smp_wmb();
writel(qp->sq.db_data.raw, qp->sq.db);
/* Make sure write sticks */
@@ -3343,8 +3309,14 @@ int qedr_post_recv(struct ib_qp *ibqp, struct ib_recv_wr *wr,
qedr_inc_sw_prod(&qp->rq);
- /* Flush all the writes before signalling doorbell */
- wmb();
+ /* qp->rqe_wr_id is accessed during qedr_poll_cq, as
+ * soon as we give the doorbell, we could get a completion
+ * for this wr, therefore we need to make sure that the
+ * memory is update before giving the doorbell.
+ * During qedr_poll_cq, rmb is called before accessing the
+ * cqe. This covers for the smp_rmb as well.
+ */
+ smp_wmb();
qp->rq.db_data.data.value++;
@@ -3695,7 +3667,7 @@ static int process_resp_flush(struct qedr_qp *qp, struct qedr_cq *cq,
static void try_consume_resp_cqe(struct qedr_cq *cq, struct qedr_qp *qp,
struct rdma_cqe_responder *resp, int *update)
{
- if (le16_to_cpu(resp->rq_cons) == qp->rq.wqe_cons) {
+ if (le16_to_cpu(resp->rq_cons_or_srq_id) == qp->rq.wqe_cons) {
consume_cqe(cq);
*update |= 1;
}
@@ -3710,7 +3682,7 @@ static int qedr_poll_cq_resp(struct qedr_dev *dev, struct qedr_qp *qp,
if (resp->status == RDMA_CQE_RESP_STS_WORK_REQUEST_FLUSHED_ERR) {
cnt = process_resp_flush(qp, cq, num_entries, wc,
- resp->rq_cons);
+ resp->rq_cons_or_srq_id);
try_consume_resp_cqe(cq, qp, resp, update);
} else {
cnt = process_resp_one(dev, qp, cq, wc, resp);
diff --git a/drivers/infiniband/hw/qedr/verbs.h b/drivers/infiniband/hw/qedr/verbs.h
index 1a94425dea33..2c57e4c592a6 100644
--- a/drivers/infiniband/hw/qedr/verbs.h
+++ b/drivers/infiniband/hw/qedr/verbs.h
@@ -38,7 +38,6 @@ int qedr_query_port(struct ib_device *, u8 port, struct ib_port_attr *props);
int qedr_modify_port(struct ib_device *, u8 port, int mask,
struct ib_port_modify *props);
-int qedr_query_gid(struct ib_device *, u8 port, int index, union ib_gid *gid);
int qedr_iw_query_gid(struct ib_device *ibdev, u8 port,
int index, union ib_gid *gid);
@@ -48,11 +47,6 @@ struct ib_ucontext *qedr_alloc_ucontext(struct ib_device *, struct ib_udata *);
int qedr_dealloc_ucontext(struct ib_ucontext *);
int qedr_mmap(struct ib_ucontext *, struct vm_area_struct *vma);
-int qedr_del_gid(struct ib_device *device, u8 port_num,
- unsigned int index, void **context);
-int qedr_add_gid(struct ib_device *device, u8 port_num,
- unsigned int index, const union ib_gid *gid,
- const struct ib_gid_attr *attr, void **context);
struct ib_pd *qedr_alloc_pd(struct ib_device *,
struct ib_ucontext *, struct ib_udata *);
int qedr_dealloc_pd(struct ib_pd *pd);
diff --git a/drivers/infiniband/hw/qib/qib.h b/drivers/infiniband/hw/qib/qib.h
index 0235f76bbc72..46072455130c 100644
--- a/drivers/infiniband/hw/qib/qib.h
+++ b/drivers/infiniband/hw/qib/qib.h
@@ -472,9 +472,6 @@ enum qib_sdma_events {
qib_sdma_event_e90_timer_tick,
};
-extern char *qib_sdma_state_names[];
-extern char *qib_sdma_event_names[];
-
struct sdma_set_state_action {
unsigned op_enable:1;
unsigned op_intenable:1;
diff --git a/drivers/infiniband/hw/qib/qib_diag.c b/drivers/infiniband/hw/qib/qib_diag.c
index a9377eee8734..11da796dd1b7 100644
--- a/drivers/infiniband/hw/qib/qib_diag.c
+++ b/drivers/infiniband/hw/qib/qib_diag.c
@@ -614,7 +614,7 @@ static ssize_t qib_diagpkt_write(struct file *fp,
}
if (copy_from_user(tmpbuf,
- (const void __user *) (unsigned long) dp.data,
+ u64_to_user_ptr(dp.data),
dp.len)) {
ret = -EFAULT;
goto bail;
diff --git a/drivers/infiniband/hw/qib/qib_file_ops.c b/drivers/infiniband/hw/qib/qib_file_ops.c
index 52c29db3a2f4..6a8800b65047 100644
--- a/drivers/infiniband/hw/qib/qib_file_ops.c
+++ b/drivers/infiniband/hw/qib/qib_file_ops.c
@@ -443,7 +443,7 @@ cleanup:
ret = -EFAULT;
goto cleanup;
}
- if (copy_to_user((void __user *) (unsigned long) ti->tidmap,
+ if (copy_to_user(u64_to_user_ptr(ti->tidmap),
tidmap, sizeof(tidmap))) {
ret = -EFAULT;
goto cleanup;
@@ -490,7 +490,7 @@ static int qib_tid_free(struct qib_ctxtdata *rcd, unsigned subctxt,
goto done;
}
- if (copy_from_user(tidmap, (void __user *)(unsigned long)ti->tidmap,
+ if (copy_from_user(tidmap, u64_to_user_ptr(ti->tidmap),
sizeof(tidmap))) {
ret = -EFAULT;
goto done;
@@ -2168,8 +2168,8 @@ static ssize_t qib_write(struct file *fp, const char __user *data,
ret = qib_do_user_init(fp, &cmd.cmd.user_info);
if (ret)
goto bail;
- ret = qib_get_base_info(fp, (void __user *) (unsigned long)
- cmd.cmd.user_info.spu_base_info,
+ ret = qib_get_base_info(fp, u64_to_user_ptr(
+ cmd.cmd.user_info.spu_base_info),
cmd.cmd.user_info.spu_base_info_size);
break;
diff --git a/drivers/infiniband/hw/qib/qib_iba7322.c b/drivers/infiniband/hw/qib/qib_iba7322.c
index 6265dac415fc..8414ae44a518 100644
--- a/drivers/infiniband/hw/qib/qib_iba7322.c
+++ b/drivers/infiniband/hw/qib/qib_iba7322.c
@@ -463,6 +463,16 @@ static u8 ib_rate_to_delay[IB_RATE_120_GBPS + 1] = {
[IB_RATE_40_GBPS] = 1
};
+static const char * const qib_sdma_state_names[] = {
+ [qib_sdma_state_s00_hw_down] = "s00_HwDown",
+ [qib_sdma_state_s10_hw_start_up_wait] = "s10_HwStartUpWait",
+ [qib_sdma_state_s20_idle] = "s20_Idle",
+ [qib_sdma_state_s30_sw_clean_up_wait] = "s30_SwCleanUpWait",
+ [qib_sdma_state_s40_hw_clean_up_wait] = "s40_HwCleanUpWait",
+ [qib_sdma_state_s50_hw_halt_wait] = "s50_HwHaltWait",
+ [qib_sdma_state_s99_running] = "s99_Running",
+};
+
#define IBA7322_LINKSPEED_SHIFT SYM_LSB(IBCStatusA_0, LinkSpeedActive)
#define IBA7322_LINKWIDTH_SHIFT SYM_LSB(IBCStatusA_0, LinkWidthActive)
diff --git a/drivers/infiniband/hw/qib/qib_init.c b/drivers/infiniband/hw/qib/qib_init.c
index 3990f386aa32..6c68f8a97018 100644
--- a/drivers/infiniband/hw/qib/qib_init.c
+++ b/drivers/infiniband/hw/qib/qib_init.c
@@ -678,11 +678,9 @@ int qib_init(struct qib_devdata *dd, int reinit)
lastfail = qib_create_rcvhdrq(dd, rcd);
if (!lastfail)
lastfail = qib_setup_eagerbufs(rcd);
- if (lastfail) {
+ if (lastfail)
qib_dev_err(dd,
"failed to allocate kernel ctxt's rcvhdrq and/or egr bufs\n");
- continue;
- }
}
for (pidx = 0; pidx < dd->num_pports; ++pidx) {
diff --git a/drivers/infiniband/hw/qib/qib_sdma.c b/drivers/infiniband/hw/qib/qib_sdma.c
index c3690bd51582..d0723d4aef5c 100644
--- a/drivers/infiniband/hw/qib/qib_sdma.c
+++ b/drivers/infiniband/hw/qib/qib_sdma.c
@@ -54,30 +54,6 @@ MODULE_PARM_DESC(sdma_descq_cnt, "Number of SDMA descq entries");
#define SDMA_DESC_COUNT_LSB 16
#define SDMA_DESC_GEN_LSB 30
-char *qib_sdma_state_names[] = {
- [qib_sdma_state_s00_hw_down] = "s00_HwDown",
- [qib_sdma_state_s10_hw_start_up_wait] = "s10_HwStartUpWait",
- [qib_sdma_state_s20_idle] = "s20_Idle",
- [qib_sdma_state_s30_sw_clean_up_wait] = "s30_SwCleanUpWait",
- [qib_sdma_state_s40_hw_clean_up_wait] = "s40_HwCleanUpWait",
- [qib_sdma_state_s50_hw_halt_wait] = "s50_HwHaltWait",
- [qib_sdma_state_s99_running] = "s99_Running",
-};
-
-char *qib_sdma_event_names[] = {
- [qib_sdma_event_e00_go_hw_down] = "e00_GoHwDown",
- [qib_sdma_event_e10_go_hw_start] = "e10_GoHwStart",
- [qib_sdma_event_e20_hw_started] = "e20_HwStarted",
- [qib_sdma_event_e30_go_running] = "e30_GoRunning",
- [qib_sdma_event_e40_sw_cleaned] = "e40_SwCleaned",
- [qib_sdma_event_e50_hw_cleaned] = "e50_HwCleaned",
- [qib_sdma_event_e60_hw_halted] = "e60_HwHalted",
- [qib_sdma_event_e70_go_idle] = "e70_GoIdle",
- [qib_sdma_event_e7220_err_halted] = "e7220_ErrHalted",
- [qib_sdma_event_e7322_err_halted] = "e7322_ErrHalted",
- [qib_sdma_event_e90_timer_tick] = "e90_TimerTick",
-};
-
/* declare all statics here rather than keep sorting */
static int alloc_sdma(struct qib_pportdata *);
static void sdma_complete(struct kref *);
diff --git a/drivers/infiniband/hw/qib/qib_verbs.c b/drivers/infiniband/hw/qib/qib_verbs.c
index fabee760407e..3977abbc83ad 100644
--- a/drivers/infiniband/hw/qib/qib_verbs.c
+++ b/drivers/infiniband/hw/qib/qib_verbs.c
@@ -1646,7 +1646,7 @@ int qib_register_ib_device(struct qib_devdata *dd)
dd->rcd[ctxt]->pkeys);
}
- ret = rvt_register_device(&dd->verbs_dev.rdi);
+ ret = rvt_register_device(&dd->verbs_dev.rdi, RDMA_DRIVER_QIB);
if (ret)
goto err_tx;
diff --git a/drivers/infiniband/hw/usnic/usnic_ib_main.c b/drivers/infiniband/hw/usnic/usnic_ib_main.c
index f45e99a938e0..f0538a460328 100644
--- a/drivers/infiniband/hw/usnic/usnic_ib_main.c
+++ b/drivers/infiniband/hw/usnic/usnic_ib_main.c
@@ -95,24 +95,6 @@ void usnic_ib_log_vf(struct usnic_ib_vf *vf)
}
/* Start of netdev section */
-static inline const char *usnic_ib_netdev_event_to_string(unsigned long event)
-{
- const char *event2str[] = {"NETDEV_NONE", "NETDEV_UP", "NETDEV_DOWN",
- "NETDEV_REBOOT", "NETDEV_CHANGE",
- "NETDEV_REGISTER", "NETDEV_UNREGISTER", "NETDEV_CHANGEMTU",
- "NETDEV_CHANGEADDR", "NETDEV_GOING_DOWN", "NETDEV_FEAT_CHANGE",
- "NETDEV_BONDING_FAILOVER", "NETDEV_PRE_UP",
- "NETDEV_PRE_TYPE_CHANGE", "NETDEV_POST_TYPE_CHANGE",
- "NETDEV_POST_INT", "NETDEV_UNREGISTER_FINAL", "NETDEV_RELEASE",
- "NETDEV_NOTIFY_PEERS", "NETDEV_JOIN"
- };
-
- if (event >= ARRAY_SIZE(event2str))
- return "UNKNOWN_NETDEV_EVENT";
- else
- return event2str[event];
-}
-
static void usnic_ib_qp_grp_modify_active_to_err(struct usnic_ib_dev *us_ibdev)
{
struct usnic_ib_ucontext *ctx;
@@ -185,7 +167,7 @@ static void usnic_ib_handle_usdev_event(struct usnic_ib_dev *us_ibdev,
ib_dispatch_event(&ib_event);
} else {
usnic_dbg("Ignoring %s on %s\n",
- usnic_ib_netdev_event_to_string(event),
+ netdev_cmd_to_name(event),
us_ibdev->ib_dev.name);
}
break;
@@ -222,7 +204,7 @@ static void usnic_ib_handle_usdev_event(struct usnic_ib_dev *us_ibdev,
break;
default:
usnic_dbg("Ignoring event %s on %s",
- usnic_ib_netdev_event_to_string(event),
+ netdev_cmd_to_name(event),
us_ibdev->ib_dev.name);
}
mutex_unlock(&us_ibdev->usdev_lock);
@@ -264,7 +246,7 @@ static int usnic_ib_handle_inet_event(struct usnic_ib_dev *us_ibdev,
switch (event) {
case NETDEV_DOWN:
usnic_info("%s via ip notifiers",
- usnic_ib_netdev_event_to_string(event));
+ netdev_cmd_to_name(event));
usnic_fwd_del_ipaddr(us_ibdev->ufdev);
usnic_ib_qp_grp_modify_active_to_err(us_ibdev);
ib_event.event = IB_EVENT_GID_CHANGE;
@@ -275,7 +257,7 @@ static int usnic_ib_handle_inet_event(struct usnic_ib_dev *us_ibdev,
case NETDEV_UP:
usnic_fwd_add_ipaddr(us_ibdev->ufdev, ifa->ifa_address);
usnic_info("%s via ip notifiers: ip %pI4",
- usnic_ib_netdev_event_to_string(event),
+ netdev_cmd_to_name(event),
&us_ibdev->ufdev->inaddr);
ib_event.event = IB_EVENT_GID_CHANGE;
ib_event.device = &us_ibdev->ib_dev;
@@ -284,7 +266,7 @@ static int usnic_ib_handle_inet_event(struct usnic_ib_dev *us_ibdev,
break;
default:
usnic_info("Ignoring event %s on %s",
- usnic_ib_netdev_event_to_string(event),
+ netdev_cmd_to_name(event),
us_ibdev->ib_dev.name);
}
mutex_unlock(&us_ibdev->usdev_lock);
@@ -433,6 +415,7 @@ static void *usnic_ib_device_add(struct pci_dev *dev)
us_ibdev->ib_dev.get_dev_fw_str = usnic_get_dev_fw_str;
+ us_ibdev->ib_dev.driver_id = RDMA_DRIVER_USNIC;
if (ib_register_device(&us_ibdev->ib_dev, NULL))
goto err_fwd_dealloc;
diff --git a/drivers/infiniband/hw/usnic/usnic_transport.c b/drivers/infiniband/hw/usnic/usnic_transport.c
index de318389a301..e0a95538c364 100644
--- a/drivers/infiniband/hw/usnic/usnic_transport.c
+++ b/drivers/infiniband/hw/usnic/usnic_transport.c
@@ -174,14 +174,13 @@ void usnic_transport_put_socket(struct socket *sock)
int usnic_transport_sock_get_addr(struct socket *sock, int *proto,
uint32_t *addr, uint16_t *port)
{
- int len;
int err;
struct sockaddr_in sock_addr;
err = sock->ops->getname(sock,
(struct sockaddr *)&sock_addr,
- &len, 0);
- if (err)
+ 0);
+ if (err < 0)
return err;
if (sock_addr.sin_family != AF_INET)
@@ -201,10 +200,8 @@ int usnic_transport_sock_get_addr(struct socket *sock, int *proto,
int usnic_transport_init(void)
{
roce_bitmap = kzalloc(ROCE_BITMAP_SZ, GFP_KERNEL);
- if (!roce_bitmap) {
- usnic_err("Failed to allocate bit map");
+ if (!roce_bitmap)
return -ENOMEM;
- }
/* Do not ever allocate bit 0, hence set it here */
bitmap_set(roce_bitmap, 0, 1);
diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c
index d650a9fcde24..0be33a81bbe6 100644
--- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c
+++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c
@@ -62,17 +62,10 @@ static DEFINE_MUTEX(pvrdma_device_list_lock);
static LIST_HEAD(pvrdma_device_list);
static struct workqueue_struct *event_wq;
-static int pvrdma_add_gid(struct ib_device *ibdev,
- u8 port_num,
- unsigned int index,
- const union ib_gid *gid,
+static int pvrdma_add_gid(const union ib_gid *gid,
const struct ib_gid_attr *attr,
void **context);
-static int pvrdma_del_gid(struct ib_device *ibdev,
- u8 port_num,
- unsigned int index,
- void **context);
-
+static int pvrdma_del_gid(const struct ib_gid_attr *attr, void **context);
static ssize_t show_hca(struct device *device, struct device_attribute *attr,
char *buf)
@@ -276,6 +269,7 @@ static int pvrdma_register_device(struct pvrdma_dev *dev)
if (!dev->srq_tbl)
goto err_qp_free;
}
+ dev->ib_dev.driver_id = RDMA_DRIVER_VMW_PVRDMA;
spin_lock_init(&dev->srq_tbl_lock);
ret = ib_register_device(&dev->ib_dev, NULL);
@@ -656,18 +650,15 @@ static int pvrdma_add_gid_at_index(struct pvrdma_dev *dev,
return 0;
}
-static int pvrdma_add_gid(struct ib_device *ibdev,
- u8 port_num,
- unsigned int index,
- const union ib_gid *gid,
+static int pvrdma_add_gid(const union ib_gid *gid,
const struct ib_gid_attr *attr,
void **context)
{
- struct pvrdma_dev *dev = to_vdev(ibdev);
+ struct pvrdma_dev *dev = to_vdev(attr->device);
return pvrdma_add_gid_at_index(dev, gid,
ib_gid_type_to_pvrdma(attr->gid_type),
- index);
+ attr->index);
}
static int pvrdma_del_gid_at_index(struct pvrdma_dev *dev, int index)
@@ -697,17 +688,14 @@ static int pvrdma_del_gid_at_index(struct pvrdma_dev *dev, int index)
return 0;
}
-static int pvrdma_del_gid(struct ib_device *ibdev,
- u8 port_num,
- unsigned int index,
- void **context)
+static int pvrdma_del_gid(const struct ib_gid_attr *attr, void **context)
{
- struct pvrdma_dev *dev = to_vdev(ibdev);
+ struct pvrdma_dev *dev = to_vdev(attr->device);
dev_dbg(&dev->pdev->dev, "removing gid at index %u from %s",
- index, dev->netdev->name);
+ attr->index, dev->netdev->name);
- return pvrdma_del_gid_at_index(dev, index);
+ return pvrdma_del_gid_at_index(dev, attr->index);
}
static void pvrdma_netdevice_event_handle(struct pvrdma_dev *dev,
diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_qp.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_qp.c
index 7bf518bdbf21..eb5b1065ec08 100644
--- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_qp.c
+++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_qp.c
@@ -489,7 +489,7 @@ int pvrdma_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
union pvrdma_cmd_req req;
union pvrdma_cmd_resp rsp;
struct pvrdma_cmd_modify_qp *cmd = &req.modify_qp;
- int cur_state, next_state;
+ enum ib_qp_state cur_state, next_state;
int ret;
/* Sanity checking. Should need lock here */
diff --git a/drivers/infiniband/sw/rdmavt/vt.c b/drivers/infiniband/sw/rdmavt/vt.c
index a4553b2b3696..434199d0bc96 100644
--- a/drivers/infiniband/sw/rdmavt/vt.c
+++ b/drivers/infiniband/sw/rdmavt/vt.c
@@ -91,7 +91,7 @@ module_exit(rvt_cleanup);
*/
struct rvt_dev_info *rvt_alloc_device(size_t size, int nports)
{
- struct rvt_dev_info *rdi = ERR_PTR(-ENOMEM);
+ struct rvt_dev_info *rdi;
rdi = (struct rvt_dev_info *)ib_alloc_device(size);
if (!rdi)
@@ -730,7 +730,7 @@ static noinline int check_support(struct rvt_dev_info *rdi, int verb)
*
* Return: 0 on success otherwise an errno.
*/
-int rvt_register_device(struct rvt_dev_info *rdi)
+int rvt_register_device(struct rvt_dev_info *rdi, u32 driver_id)
{
int ret = 0, i;
@@ -831,6 +831,7 @@ int rvt_register_device(struct rvt_dev_info *rdi)
rdi->ibdev.node_type = RDMA_NODE_IB_CA;
rdi->ibdev.num_comp_vectors = 1;
+ rdi->ibdev.driver_id = driver_id;
/* We are now good to announce we exist */
ret = ib_register_device(&rdi->ibdev, rdi->driver_f.port_callback);
if (ret) {
diff --git a/drivers/infiniband/sw/rdmavt/vt.h b/drivers/infiniband/sw/rdmavt/vt.h
index 8823b2e7aac6..0675ea6c3872 100644
--- a/drivers/infiniband/sw/rdmavt/vt.h
+++ b/drivers/infiniband/sw/rdmavt/vt.h
@@ -59,7 +59,6 @@
#include "mmap.h"
#include "cq.h"
#include "mad.h"
-#include "mmap.h"
#define rvt_pr_info(rdi, fmt, ...) \
__rvt_pr_info(rdi->driver_f.get_pci_dev(rdi), \
diff --git a/drivers/infiniband/sw/rxe/rxe.c b/drivers/infiniband/sw/rxe/rxe.c
index b7debb6f2eac..e493fdbd61c6 100644
--- a/drivers/infiniband/sw/rxe/rxe.c
+++ b/drivers/infiniband/sw/rxe/rxe.c
@@ -78,7 +78,7 @@ void rxe_release(struct kref *kref)
}
/* initialize rxe device parameters */
-static int rxe_init_device_param(struct rxe_dev *rxe)
+static void rxe_init_device_param(struct rxe_dev *rxe)
{
rxe->max_inline_data = RXE_MAX_INLINE_DATA;
@@ -122,8 +122,6 @@ static int rxe_init_device_param(struct rxe_dev *rxe)
rxe->attr.local_ca_ack_delay = RXE_LOCAL_CA_ACK_DELAY;
rxe->max_ucontext = RXE_MAX_UCONTEXT;
-
- return 0;
}
/* initialize port attributes */
diff --git a/drivers/infiniband/sw/rxe/rxe.h b/drivers/infiniband/sw/rxe/rxe.h
index 7d232611303f..561ad307c6ec 100644
--- a/drivers/infiniband/sw/rxe/rxe.h
+++ b/drivers/infiniband/sw/rxe/rxe.h
@@ -59,7 +59,11 @@
#include "rxe_verbs.h"
#include "rxe_loc.h"
-#define RXE_UVERBS_ABI_VERSION (1)
+/*
+ * Version 1 and Version 2 are identical on 64 bit machines, but on 32 bit
+ * machines Version 2 has a different struct layout.
+ */
+#define RXE_UVERBS_ABI_VERSION 2
#define IB_PHYS_STATE_LINK_UP (5)
#define IB_PHYS_STATE_LINK_DOWN (3)
diff --git a/drivers/infiniband/sw/rxe/rxe_av.c b/drivers/infiniband/sw/rxe/rxe_av.c
index 7522d1af3ae2..7f1ae364088a 100644
--- a/drivers/infiniband/sw/rxe/rxe_av.c
+++ b/drivers/infiniband/sw/rxe/rxe_av.c
@@ -74,8 +74,9 @@ void rxe_av_fill_ip_info(struct rxe_av *av,
struct ib_gid_attr *sgid_attr,
union ib_gid *sgid)
{
- rdma_gid2ip(&av->sgid_addr._sockaddr, sgid);
- rdma_gid2ip(&av->dgid_addr._sockaddr, &rdma_ah_read_grh(attr)->dgid);
+ rdma_gid2ip((struct sockaddr *)&av->sgid_addr, sgid);
+ rdma_gid2ip((struct sockaddr *)&av->dgid_addr,
+ &rdma_ah_read_grh(attr)->dgid);
av->network_type = ib_gid_to_network_type(sgid_attr->gid_type, sgid);
}
diff --git a/drivers/infiniband/sw/rxe/rxe_cq.c b/drivers/infiniband/sw/rxe/rxe_cq.c
index c4aabf78dc90..2ee4b08b00ea 100644
--- a/drivers/infiniband/sw/rxe/rxe_cq.c
+++ b/drivers/infiniband/sw/rxe/rxe_cq.c
@@ -36,7 +36,7 @@
#include "rxe_queue.h"
int rxe_cq_chk_attr(struct rxe_dev *rxe, struct rxe_cq *cq,
- int cqe, int comp_vector, struct ib_udata *udata)
+ int cqe, int comp_vector)
{
int count;
@@ -83,7 +83,7 @@ static void rxe_send_complete(unsigned long data)
int rxe_cq_from_init(struct rxe_dev *rxe, struct rxe_cq *cq, int cqe,
int comp_vector, struct ib_ucontext *context,
- struct ib_udata *udata)
+ struct rxe_create_cq_resp __user *uresp)
{
int err;
@@ -94,15 +94,15 @@ int rxe_cq_from_init(struct rxe_dev *rxe, struct rxe_cq *cq, int cqe,
return -ENOMEM;
}
- err = do_mmap_info(rxe, udata, false, context, cq->queue->buf,
- cq->queue->buf_size, &cq->queue->ip);
+ err = do_mmap_info(rxe, uresp ? &uresp->mi : NULL, context,
+ cq->queue->buf, cq->queue->buf_size, &cq->queue->ip);
if (err) {
kvfree(cq->queue->buf);
kfree(cq->queue);
return err;
}
- if (udata)
+ if (uresp)
cq->is_user = 1;
cq->is_dying = false;
@@ -114,14 +114,15 @@ int rxe_cq_from_init(struct rxe_dev *rxe, struct rxe_cq *cq, int cqe,
return 0;
}
-int rxe_cq_resize_queue(struct rxe_cq *cq, int cqe, struct ib_udata *udata)
+int rxe_cq_resize_queue(struct rxe_cq *cq, int cqe,
+ struct rxe_resize_cq_resp __user *uresp)
{
int err;
err = rxe_queue_resize(cq->queue, (unsigned int *)&cqe,
sizeof(struct rxe_cqe),
cq->queue->ip ? cq->queue->ip->context : NULL,
- udata, NULL, &cq->cq_lock);
+ uresp ? &uresp->mi : NULL, NULL, &cq->cq_lock);
if (!err)
cq->ibcq.cqe = cqe;
diff --git a/drivers/infiniband/sw/rxe/rxe_loc.h b/drivers/infiniband/sw/rxe/rxe_loc.h
index 4ef75d5b729b..b71023c1c58b 100644
--- a/drivers/infiniband/sw/rxe/rxe_loc.h
+++ b/drivers/infiniband/sw/rxe/rxe_loc.h
@@ -52,13 +52,14 @@ struct rxe_av *rxe_get_av(struct rxe_pkt_info *pkt);
/* rxe_cq.c */
int rxe_cq_chk_attr(struct rxe_dev *rxe, struct rxe_cq *cq,
- int cqe, int comp_vector, struct ib_udata *udata);
+ int cqe, int comp_vector);
int rxe_cq_from_init(struct rxe_dev *rxe, struct rxe_cq *cq, int cqe,
int comp_vector, struct ib_ucontext *context,
- struct ib_udata *udata);
+ struct rxe_create_cq_resp __user *uresp);
-int rxe_cq_resize_queue(struct rxe_cq *cq, int new_cqe, struct ib_udata *udata);
+int rxe_cq_resize_queue(struct rxe_cq *cq, int new_cqe,
+ struct rxe_resize_cq_resp __user *uresp);
int rxe_cq_post(struct rxe_cq *cq, struct rxe_cqe *cqe, int solicited);
@@ -143,8 +144,7 @@ int advance_dma_data(struct rxe_dma_info *dma, unsigned int length);
/* rxe_net.c */
int rxe_loopback(struct sk_buff *skb);
-int rxe_send(struct rxe_dev *rxe, struct rxe_pkt_info *pkt,
- struct sk_buff *skb);
+int rxe_send(struct rxe_pkt_info *pkt, struct sk_buff *skb);
struct sk_buff *rxe_init_packet(struct rxe_dev *rxe, struct rxe_av *av,
int paylen, struct rxe_pkt_info *pkt);
int rxe_prepare(struct rxe_dev *rxe, struct rxe_pkt_info *pkt,
@@ -159,7 +159,8 @@ int rxe_mcast_delete(struct rxe_dev *rxe, union ib_gid *mgid);
int rxe_qp_chk_init(struct rxe_dev *rxe, struct ib_qp_init_attr *init);
int rxe_qp_from_init(struct rxe_dev *rxe, struct rxe_qp *qp, struct rxe_pd *pd,
- struct ib_qp_init_attr *init, struct ib_udata *udata,
+ struct ib_qp_init_attr *init,
+ struct rxe_create_qp_resp __user *uresp,
struct ib_pd *ibpd);
int rxe_qp_to_init(struct rxe_qp *qp, struct ib_qp_init_attr *init);
@@ -227,11 +228,12 @@ int rxe_srq_chk_attr(struct rxe_dev *rxe, struct rxe_srq *srq,
int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq,
struct ib_srq_init_attr *init,
- struct ib_ucontext *context, struct ib_udata *udata);
+ struct ib_ucontext *context,
+ struct rxe_create_srq_resp __user *uresp);
int rxe_srq_from_attr(struct rxe_dev *rxe, struct rxe_srq *srq,
struct ib_srq_attr *attr, enum ib_srq_attr_mask mask,
- struct ib_udata *udata);
+ struct rxe_modify_srq_cmd *ucmd);
void rxe_release(struct kref *kref);
@@ -268,7 +270,7 @@ static inline int rxe_xmit_packet(struct rxe_dev *rxe, struct rxe_qp *qp,
memcpy(SKB_TO_PKT(skb), pkt, sizeof(*pkt));
err = rxe_loopback(skb);
} else {
- err = rxe_send(rxe, pkt, skb);
+ err = rxe_send(pkt, skb);
}
if (err) {
diff --git a/drivers/infiniband/sw/rxe/rxe_net.c b/drivers/infiniband/sw/rxe/rxe_net.c
index 159246b03867..9da6e37fb70c 100644
--- a/drivers/infiniband/sw/rxe/rxe_net.c
+++ b/drivers/infiniband/sw/rxe/rxe_net.c
@@ -182,11 +182,39 @@ static struct dst_entry *rxe_find_route6(struct net_device *ndev,
#endif
+/*
+ * Derive the net_device from the av.
+ * For physical devices, this will just return rxe->ndev.
+ * But for VLAN devices, it will return the vlan dev.
+ * Caller should dev_put() the returned net_device.
+ */
+static struct net_device *rxe_netdev_from_av(struct rxe_dev *rxe,
+ int port_num,
+ struct rxe_av *av)
+{
+ union ib_gid gid;
+ struct ib_gid_attr attr;
+ struct net_device *ndev = rxe->ndev;
+
+ if (ib_get_cached_gid(&rxe->ib_dev, port_num, av->grh.sgid_index,
+ &gid, &attr) == 0 &&
+ attr.ndev && attr.ndev != ndev)
+ ndev = attr.ndev;
+ else
+ /* Only to ensure that caller may call dev_put() */
+ dev_hold(ndev);
+
+ return ndev;
+}
+
static struct dst_entry *rxe_find_route(struct rxe_dev *rxe,
struct rxe_qp *qp,
struct rxe_av *av)
{
struct dst_entry *dst = NULL;
+ struct net_device *ndev;
+
+ ndev = rxe_netdev_from_av(rxe, qp->attr.port_num, av);
if (qp_type(qp) == IB_QPT_RC)
dst = sk_dst_get(qp->sk->sk);
@@ -201,14 +229,14 @@ static struct dst_entry *rxe_find_route(struct rxe_dev *rxe,
saddr = &av->sgid_addr._sockaddr_in.sin_addr;
daddr = &av->dgid_addr._sockaddr_in.sin_addr;
- dst = rxe_find_route4(rxe->ndev, saddr, daddr);
+ dst = rxe_find_route4(ndev, saddr, daddr);
} else if (av->network_type == RDMA_NETWORK_IPV6) {
struct in6_addr *saddr6;
struct in6_addr *daddr6;
saddr6 = &av->sgid_addr._sockaddr_in6.sin6_addr;
daddr6 = &av->dgid_addr._sockaddr_in6.sin6_addr;
- dst = rxe_find_route6(rxe->ndev, saddr6, daddr6);
+ dst = rxe_find_route6(ndev, saddr6, daddr6);
#if IS_ENABLED(CONFIG_IPV6)
if (dst)
qp->dst_cookie =
@@ -217,6 +245,7 @@ static struct dst_entry *rxe_find_route(struct rxe_dev *rxe,
}
}
+ dev_put(ndev);
return dst;
}
@@ -224,9 +253,14 @@ static int rxe_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
{
struct udphdr *udph;
struct net_device *ndev = skb->dev;
+ struct net_device *rdev = ndev;
struct rxe_dev *rxe = net_to_rxe(ndev);
struct rxe_pkt_info *pkt = SKB_TO_PKT(skb);
+ if (!rxe && is_vlan_dev(rdev)) {
+ rdev = vlan_dev_real_dev(ndev);
+ rxe = net_to_rxe(rdev);
+ }
if (!rxe)
goto drop;
@@ -450,7 +484,7 @@ static void rxe_skb_tx_dtor(struct sk_buff *skb)
rxe_drop_ref(qp);
}
-int rxe_send(struct rxe_dev *rxe, struct rxe_pkt_info *pkt, struct sk_buff *skb)
+int rxe_send(struct rxe_pkt_info *pkt, struct sk_buff *skb)
{
struct rxe_av *av;
int err;
@@ -498,6 +532,10 @@ struct sk_buff *rxe_init_packet(struct rxe_dev *rxe, struct rxe_av *av,
{
unsigned int hdr_len;
struct sk_buff *skb;
+ struct net_device *ndev;
+ const int port_num = 1;
+
+ ndev = rxe_netdev_from_av(rxe, port_num, av);
if (av->network_type == RDMA_NETWORK_IPV4)
hdr_len = ETH_HLEN + sizeof(struct udphdr) +
@@ -506,26 +544,30 @@ struct sk_buff *rxe_init_packet(struct rxe_dev *rxe, struct rxe_av *av,
hdr_len = ETH_HLEN + sizeof(struct udphdr) +
sizeof(struct ipv6hdr);
- skb = alloc_skb(paylen + hdr_len + LL_RESERVED_SPACE(rxe->ndev),
+ skb = alloc_skb(paylen + hdr_len + LL_RESERVED_SPACE(ndev),
GFP_ATOMIC);
- if (unlikely(!skb))
+
+ if (unlikely(!skb)) {
+ dev_put(ndev);
return NULL;
+ }
skb_reserve(skb, hdr_len + LL_RESERVED_SPACE(rxe->ndev));
- skb->dev = rxe->ndev;
+ skb->dev = ndev;
if (av->network_type == RDMA_NETWORK_IPV4)
skb->protocol = htons(ETH_P_IP);
else
skb->protocol = htons(ETH_P_IPV6);
pkt->rxe = rxe;
- pkt->port_num = 1;
+ pkt->port_num = port_num;
pkt->hdr = skb_put(skb, paylen);
pkt->mask |= RXE_GRH_MASK;
memset(pkt->hdr, 0, paylen);
+ dev_put(ndev);
return skb;
}
diff --git a/drivers/infiniband/sw/rxe/rxe_qp.c b/drivers/infiniband/sw/rxe/rxe_qp.c
index 2fcf1cab7678..b9f7aa1114b2 100644
--- a/drivers/infiniband/sw/rxe/rxe_qp.c
+++ b/drivers/infiniband/sw/rxe/rxe_qp.c
@@ -40,15 +40,6 @@
#include "rxe_queue.h"
#include "rxe_task.h"
-char *rxe_qp_state_name[] = {
- [QP_STATE_RESET] = "RESET",
- [QP_STATE_INIT] = "INIT",
- [QP_STATE_READY] = "READY",
- [QP_STATE_DRAIN] = "DRAIN",
- [QP_STATE_DRAINED] = "DRAINED",
- [QP_STATE_ERROR] = "ERROR",
-};
-
static int rxe_qp_chk_cap(struct rxe_dev *rxe, struct ib_qp_cap *cap,
int has_srq)
{
@@ -225,7 +216,8 @@ static void rxe_qp_init_misc(struct rxe_dev *rxe, struct rxe_qp *qp,
static int rxe_qp_init_req(struct rxe_dev *rxe, struct rxe_qp *qp,
struct ib_qp_init_attr *init,
- struct ib_ucontext *context, struct ib_udata *udata)
+ struct ib_ucontext *context,
+ struct rxe_create_qp_resp __user *uresp)
{
int err;
int wqe_size;
@@ -250,9 +242,9 @@ static int rxe_qp_init_req(struct rxe_dev *rxe, struct rxe_qp *qp,
if (!qp->sq.queue)
return -ENOMEM;
- err = do_mmap_info(rxe, udata, true,
- context, qp->sq.queue->buf,
- qp->sq.queue->buf_size, &qp->sq.queue->ip);
+ err = do_mmap_info(rxe, uresp ? &uresp->sq_mi : NULL, context,
+ qp->sq.queue->buf, qp->sq.queue->buf_size,
+ &qp->sq.queue->ip);
if (err) {
kvfree(qp->sq.queue->buf);
@@ -283,7 +275,8 @@ static int rxe_qp_init_req(struct rxe_dev *rxe, struct rxe_qp *qp,
static int rxe_qp_init_resp(struct rxe_dev *rxe, struct rxe_qp *qp,
struct ib_qp_init_attr *init,
- struct ib_ucontext *context, struct ib_udata *udata)
+ struct ib_ucontext *context,
+ struct rxe_create_qp_resp __user *uresp)
{
int err;
int wqe_size;
@@ -303,9 +296,8 @@ static int rxe_qp_init_resp(struct rxe_dev *rxe, struct rxe_qp *qp,
if (!qp->rq.queue)
return -ENOMEM;
- err = do_mmap_info(rxe, udata, false, context,
- qp->rq.queue->buf,
- qp->rq.queue->buf_size,
+ err = do_mmap_info(rxe, uresp ? &uresp->rq_mi : NULL, context,
+ qp->rq.queue->buf, qp->rq.queue->buf_size,
&qp->rq.queue->ip);
if (err) {
kvfree(qp->rq.queue->buf);
@@ -331,14 +323,15 @@ static int rxe_qp_init_resp(struct rxe_dev *rxe, struct rxe_qp *qp,
/* called by the create qp verb */
int rxe_qp_from_init(struct rxe_dev *rxe, struct rxe_qp *qp, struct rxe_pd *pd,
- struct ib_qp_init_attr *init, struct ib_udata *udata,
+ struct ib_qp_init_attr *init,
+ struct rxe_create_qp_resp __user *uresp,
struct ib_pd *ibpd)
{
int err;
struct rxe_cq *rcq = to_rcq(init->recv_cq);
struct rxe_cq *scq = to_rcq(init->send_cq);
struct rxe_srq *srq = init->srq ? to_rsrq(init->srq) : NULL;
- struct ib_ucontext *context = udata ? ibpd->uobject->context : NULL;
+ struct ib_ucontext *context = ibpd->uobject ? ibpd->uobject->context : NULL;
rxe_add_ref(pd);
rxe_add_ref(rcq);
@@ -353,11 +346,11 @@ int rxe_qp_from_init(struct rxe_dev *rxe, struct rxe_qp *qp, struct rxe_pd *pd,
rxe_qp_init_misc(rxe, qp, init);
- err = rxe_qp_init_req(rxe, qp, init, context, udata);
+ err = rxe_qp_init_req(rxe, qp, init, context, uresp);
if (err)
goto err1;
- err = rxe_qp_init_resp(rxe, qp, init, context, udata);
+ err = rxe_qp_init_resp(rxe, qp, init, context, uresp);
if (err)
goto err2;
diff --git a/drivers/infiniband/sw/rxe/rxe_queue.c b/drivers/infiniband/sw/rxe/rxe_queue.c
index d14bf496d62d..f84ab4469261 100644
--- a/drivers/infiniband/sw/rxe/rxe_queue.c
+++ b/drivers/infiniband/sw/rxe/rxe_queue.c
@@ -37,35 +37,21 @@
#include "rxe_queue.h"
int do_mmap_info(struct rxe_dev *rxe,
- struct ib_udata *udata,
- bool is_req,
+ struct mminfo __user *outbuf,
struct ib_ucontext *context,
struct rxe_queue_buf *buf,
size_t buf_size,
struct rxe_mmap_info **ip_p)
{
int err;
- u32 len, offset;
struct rxe_mmap_info *ip = NULL;
- if (udata) {
- if (is_req) {
- len = udata->outlen - sizeof(struct mminfo);
- offset = sizeof(struct mminfo);
- } else {
- len = udata->outlen;
- offset = 0;
- }
-
- if (len < sizeof(ip->info))
- goto err1;
-
+ if (outbuf) {
ip = rxe_create_mmap_info(rxe, buf_size, context, buf);
if (!ip)
goto err1;
- err = copy_to_user(udata->outbuf + offset, &ip->info,
- sizeof(ip->info));
+ err = copy_to_user(outbuf, &ip->info, sizeof(ip->info));
if (err)
goto err2;
@@ -171,7 +157,7 @@ int rxe_queue_resize(struct rxe_queue *q,
unsigned int *num_elem_p,
unsigned int elem_size,
struct ib_ucontext *context,
- struct ib_udata *udata,
+ struct mminfo __user *outbuf,
spinlock_t *producer_lock,
spinlock_t *consumer_lock)
{
@@ -184,7 +170,7 @@ int rxe_queue_resize(struct rxe_queue *q,
if (!new_q)
return -ENOMEM;
- err = do_mmap_info(new_q->rxe, udata, false, context, new_q->buf,
+ err = do_mmap_info(new_q->rxe, outbuf, context, new_q->buf,
new_q->buf_size, &new_q->ip);
if (err) {
vfree(new_q->buf);
diff --git a/drivers/infiniband/sw/rxe/rxe_queue.h b/drivers/infiniband/sw/rxe/rxe_queue.h
index 8c8641c87817..79ba4b320054 100644
--- a/drivers/infiniband/sw/rxe/rxe_queue.h
+++ b/drivers/infiniband/sw/rxe/rxe_queue.h
@@ -77,8 +77,7 @@ struct rxe_queue {
};
int do_mmap_info(struct rxe_dev *rxe,
- struct ib_udata *udata,
- bool is_req,
+ struct mminfo __user *outbuf,
struct ib_ucontext *context,
struct rxe_queue_buf *buf,
size_t buf_size,
@@ -94,7 +93,7 @@ int rxe_queue_resize(struct rxe_queue *q,
unsigned int *num_elem_p,
unsigned int elem_size,
struct ib_ucontext *context,
- struct ib_udata *udata,
+ struct mminfo __user *outbuf,
/* Protect producers while resizing queue */
spinlock_t *producer_lock,
/* Protect consumers while resizing queue */
diff --git a/drivers/infiniband/sw/rxe/rxe_recv.c b/drivers/infiniband/sw/rxe/rxe_recv.c
index 4c3f899241d4..dd80c7d9074a 100644
--- a/drivers/infiniband/sw/rxe/rxe_recv.c
+++ b/drivers/infiniband/sw/rxe/rxe_recv.c
@@ -276,7 +276,6 @@ static void rxe_rcv_mcast_pkt(struct rxe_dev *rxe, struct sk_buff *skb)
{
struct rxe_pkt_info *pkt = SKB_TO_PKT(skb);
struct rxe_mc_grp *mcg;
- struct sk_buff *skb_copy;
struct rxe_mc_elem *mce;
struct rxe_qp *qp;
union ib_gid dgid;
@@ -309,18 +308,14 @@ static void rxe_rcv_mcast_pkt(struct rxe_dev *rxe, struct sk_buff *skb)
continue;
/* if *not* the last qp in the list
- * make a copy of the skb to post to the next qp
+ * increase the users of the skb then post to the next qp
*/
- skb_copy = (mce->qp_list.next != &mcg->qp_list) ?
- skb_clone(skb, GFP_ATOMIC) : NULL;
+ if (mce->qp_list.next != &mcg->qp_list)
+ refcount_inc(&skb->users);
pkt->qp = qp;
rxe_add_ref(qp);
rxe_rcv_pkt(rxe, pkt, skb);
-
- skb = skb_copy;
- if (!skb)
- break;
}
spin_unlock_bh(&mcg->mcg_lock);
@@ -328,8 +323,7 @@ static void rxe_rcv_mcast_pkt(struct rxe_dev *rxe, struct sk_buff *skb)
rxe_drop_ref(mcg); /* drop ref from rxe_pool_get_key. */
err1:
- if (skb)
- kfree_skb(skb);
+ kfree_skb(skb);
}
static int rxe_match_dgid(struct rxe_dev *rxe, struct sk_buff *skb)
@@ -347,7 +341,7 @@ static int rxe_match_dgid(struct rxe_dev *rxe, struct sk_buff *skb)
return ib_find_cached_gid_by_port(&rxe->ib_dev, pdgid,
IB_GID_TYPE_ROCE_UDP_ENCAP,
- 1, rxe->ndev, NULL);
+ 1, skb->dev, NULL);
}
/* rxe_rcv is called from the interface driver */
diff --git a/drivers/infiniband/sw/rxe/rxe_resp.c b/drivers/infiniband/sw/rxe/rxe_resp.c
index d37bb9b97569..a65c9969f7fc 100644
--- a/drivers/infiniband/sw/rxe/rxe_resp.c
+++ b/drivers/infiniband/sw/rxe/rxe_resp.c
@@ -969,7 +969,6 @@ static int send_atomic_ack(struct rxe_qp *qp, struct rxe_pkt_info *pkt,
int rc = 0;
struct rxe_pkt_info ack_pkt;
struct sk_buff *skb;
- struct sk_buff *skb_copy;
struct rxe_dev *rxe = to_rdev(qp->ibqp.device);
struct resp_res *res;
@@ -981,14 +980,7 @@ static int send_atomic_ack(struct rxe_qp *qp, struct rxe_pkt_info *pkt,
goto out;
}
- skb_copy = skb_clone(skb, GFP_ATOMIC);
- if (skb_copy)
- rxe_add_ref(qp); /* for the new SKB */
- else {
- pr_warn("Could not clone atomic response\n");
- rc = -ENOMEM;
- goto out;
- }
+ rxe_add_ref(qp);
res = &qp->resp.resources[qp->resp.res_head];
free_rd_atomic_resource(qp, res);
@@ -998,19 +990,18 @@ static int send_atomic_ack(struct rxe_qp *qp, struct rxe_pkt_info *pkt,
memset((unsigned char *)SKB_TO_PKT(skb) + sizeof(ack_pkt), 0,
sizeof(skb->cb) - sizeof(ack_pkt));
+ refcount_inc(&skb->users);
res->type = RXE_ATOMIC_MASK;
res->atomic.skb = skb;
res->first_psn = ack_pkt.psn;
res->last_psn = ack_pkt.psn;
res->cur_psn = ack_pkt.psn;
- rc = rxe_xmit_packet(rxe, qp, &ack_pkt, skb_copy);
+ rc = rxe_xmit_packet(rxe, qp, &ack_pkt, skb);
if (rc) {
pr_err_ratelimited("Failed sending ack\n");
rxe_drop_ref(qp);
- kfree_skb(skb_copy);
}
-
out:
return rc;
}
diff --git a/drivers/infiniband/sw/rxe/rxe_srq.c b/drivers/infiniband/sw/rxe/rxe_srq.c
index efc832a2d7c6..0d6c04ba7fc3 100644
--- a/drivers/infiniband/sw/rxe/rxe_srq.c
+++ b/drivers/infiniband/sw/rxe/rxe_srq.c
@@ -99,7 +99,8 @@ err1:
int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq,
struct ib_srq_init_attr *init,
- struct ib_ucontext *context, struct ib_udata *udata)
+ struct ib_ucontext *context,
+ struct rxe_create_srq_resp __user *uresp)
{
int err;
int srq_wqe_size;
@@ -126,55 +127,41 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq,
srq->rq.queue = q;
- err = do_mmap_info(rxe, udata, false, context, q->buf,
+ err = do_mmap_info(rxe, uresp ? &uresp->mi : NULL, context, q->buf,
q->buf_size, &q->ip);
if (err)
return err;
- if (udata && udata->outlen >= sizeof(struct mminfo) + sizeof(u32)) {
- if (copy_to_user(udata->outbuf + sizeof(struct mminfo),
- &srq->srq_num, sizeof(u32)))
+ if (uresp) {
+ if (copy_to_user(&uresp->srq_num, &srq->srq_num,
+ sizeof(uresp->srq_num)))
return -EFAULT;
}
+
return 0;
}
int rxe_srq_from_attr(struct rxe_dev *rxe, struct rxe_srq *srq,
struct ib_srq_attr *attr, enum ib_srq_attr_mask mask,
- struct ib_udata *udata)
+ struct rxe_modify_srq_cmd *ucmd)
{
int err;
struct rxe_queue *q = srq->rq.queue;
- struct mminfo mi = { .offset = 1, .size = 0};
+ struct mminfo __user *mi = NULL;
if (mask & IB_SRQ_MAX_WR) {
- /* Check that we can write the mminfo struct to user space */
- if (udata && udata->inlen >= sizeof(__u64)) {
- __u64 mi_addr;
-
- /* Get address of user space mminfo struct */
- err = ib_copy_from_udata(&mi_addr, udata,
- sizeof(mi_addr));
- if (err)
- goto err1;
-
- udata->outbuf = (void __user *)(unsigned long)mi_addr;
- udata->outlen = sizeof(mi);
-
- if (!access_ok(VERIFY_WRITE,
- (void __user *)udata->outbuf,
- udata->outlen)) {
- err = -EFAULT;
- goto err1;
- }
- }
+ /*
+ * This is completely screwed up, the response is supposed to
+ * be in the outbuf not like this.
+ */
+ mi = u64_to_user_ptr(ucmd->mmap_info_addr);
err = rxe_queue_resize(q, &attr->max_wr,
rcv_wqe_size(srq->rq.max_sge),
srq->rq.queue->ip ?
srq->rq.queue->ip->context :
NULL,
- udata, &srq->rq.producer_lock,
+ mi, &srq->rq.producer_lock,
&srq->rq.consumer_lock);
if (err)
goto err2;
@@ -188,6 +175,5 @@ int rxe_srq_from_attr(struct rxe_dev *rxe, struct rxe_srq *srq,
err2:
rxe_queue_cleanup(q);
srq->rq.queue = NULL;
-err1:
return err;
}
diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.c b/drivers/infiniband/sw/rxe/rxe_verbs.c
index f4bab2cd0ec2..2cb52fd48cf1 100644
--- a/drivers/infiniband/sw/rxe/rxe_verbs.c
+++ b/drivers/infiniband/sw/rxe/rxe_verbs.c
@@ -77,40 +77,6 @@ out:
return rc;
}
-static int rxe_query_gid(struct ib_device *device,
- u8 port_num, int index, union ib_gid *gid)
-{
- int ret;
-
- if (index > RXE_PORT_GID_TBL_LEN)
- return -EINVAL;
-
- ret = ib_get_cached_gid(device, port_num, index, gid, NULL);
- if (ret == -EAGAIN) {
- memcpy(gid, &zgid, sizeof(*gid));
- return 0;
- }
-
- return ret;
-}
-
-static int rxe_add_gid(struct ib_device *device, u8 port_num, unsigned int
- index, const union ib_gid *gid,
- const struct ib_gid_attr *attr, void **context)
-{
- if (index >= RXE_PORT_GID_TBL_LEN)
- return -EINVAL;
- return 0;
-}
-
-static int rxe_del_gid(struct ib_device *device, u8 port_num, unsigned int
- index, void **context)
-{
- if (index >= RXE_PORT_GID_TBL_LEN)
- return -EINVAL;
- return 0;
-}
-
static struct net_device *rxe_get_netdev(struct ib_device *device,
u8 port_num)
{
@@ -273,9 +239,7 @@ static int rxe_init_av(struct rxe_dev *rxe, struct rdma_ah_attr *attr,
rxe_av_from_attr(rdma_ah_get_port_num(attr), av, attr);
rxe_av_fill_ip_info(av, attr, &sgid_attr, &sgid);
-
- if (sgid_attr.ndev)
- dev_put(sgid_attr.ndev);
+ dev_put(sgid_attr.ndev);
return 0;
}
@@ -407,6 +371,13 @@ static struct ib_srq *rxe_create_srq(struct ib_pd *ibpd,
struct rxe_pd *pd = to_rpd(ibpd);
struct rxe_srq *srq;
struct ib_ucontext *context = udata ? ibpd->uobject->context : NULL;
+ struct rxe_create_srq_resp __user *uresp = NULL;
+
+ if (udata) {
+ if (udata->outlen < sizeof(*uresp))
+ return ERR_PTR(-EINVAL);
+ uresp = udata->outbuf;
+ }
err = rxe_srq_chk_attr(rxe, NULL, &init->attr, IB_SRQ_INIT_MASK);
if (err)
@@ -422,7 +393,7 @@ static struct ib_srq *rxe_create_srq(struct ib_pd *ibpd,
rxe_add_ref(pd);
srq->pd = pd;
- err = rxe_srq_from_init(rxe, srq, init, context, udata);
+ err = rxe_srq_from_init(rxe, srq, init, context, uresp);
if (err)
goto err2;
@@ -443,12 +414,22 @@ static int rxe_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr,
int err;
struct rxe_srq *srq = to_rsrq(ibsrq);
struct rxe_dev *rxe = to_rdev(ibsrq->device);
+ struct rxe_modify_srq_cmd ucmd = {};
+
+ if (udata) {
+ if (udata->inlen < sizeof(ucmd))
+ return -EINVAL;
+
+ err = ib_copy_from_udata(&ucmd, udata, sizeof(ucmd));
+ if (err)
+ return err;
+ }
err = rxe_srq_chk_attr(rxe, srq, attr, mask);
if (err)
goto err1;
- err = rxe_srq_from_attr(rxe, srq, attr, mask, udata);
+ err = rxe_srq_from_attr(rxe, srq, attr, mask, &ucmd);
if (err)
goto err1;
@@ -517,6 +498,13 @@ static struct ib_qp *rxe_create_qp(struct ib_pd *ibpd,
struct rxe_dev *rxe = to_rdev(ibpd->device);
struct rxe_pd *pd = to_rpd(ibpd);
struct rxe_qp *qp;
+ struct rxe_create_qp_resp __user *uresp = NULL;
+
+ if (udata) {
+ if (udata->outlen < sizeof(*uresp))
+ return ERR_PTR(-EINVAL);
+ uresp = udata->outbuf;
+ }
err = rxe_qp_chk_init(rxe, init);
if (err)
@@ -538,7 +526,7 @@ static struct ib_qp *rxe_create_qp(struct ib_pd *ibpd,
rxe_add_index(qp);
- err = rxe_qp_from_init(rxe, qp, pd, init, udata, ibpd);
+ err = rxe_qp_from_init(rxe, qp, pd, init, uresp, ibpd);
if (err)
goto err3;
@@ -711,9 +699,8 @@ static int init_send_wqe(struct rxe_qp *qp, struct ib_send_wr *ibwr,
memcpy(wqe->dma.sge, ibwr->sg_list,
num_sge * sizeof(struct ib_sge));
- wqe->iova = (mask & WR_ATOMIC_MASK) ?
- atomic_wr(ibwr)->remote_addr :
- rdma_wr(ibwr)->remote_addr;
+ wqe->iova = mask & WR_ATOMIC_MASK ? atomic_wr(ibwr)->remote_addr :
+ mask & WR_READ_OR_WRITE_MASK ? rdma_wr(ibwr)->remote_addr : 0;
wqe->mask = mask;
wqe->dma.length = length;
wqe->dma.resid = length;
@@ -889,11 +876,18 @@ static struct ib_cq *rxe_create_cq(struct ib_device *dev,
int err;
struct rxe_dev *rxe = to_rdev(dev);
struct rxe_cq *cq;
+ struct rxe_create_cq_resp __user *uresp = NULL;
+
+ if (udata) {
+ if (udata->outlen < sizeof(*uresp))
+ return ERR_PTR(-EINVAL);
+ uresp = udata->outbuf;
+ }
if (attr->flags)
return ERR_PTR(-EINVAL);
- err = rxe_cq_chk_attr(rxe, NULL, attr->cqe, attr->comp_vector, udata);
+ err = rxe_cq_chk_attr(rxe, NULL, attr->cqe, attr->comp_vector);
if (err)
goto err1;
@@ -904,7 +898,7 @@ static struct ib_cq *rxe_create_cq(struct ib_device *dev,
}
err = rxe_cq_from_init(rxe, cq, attr->cqe, attr->comp_vector,
- context, udata);
+ context, uresp);
if (err)
goto err2;
@@ -931,12 +925,19 @@ static int rxe_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata)
int err;
struct rxe_cq *cq = to_rcq(ibcq);
struct rxe_dev *rxe = to_rdev(ibcq->device);
+ struct rxe_resize_cq_resp __user *uresp = NULL;
+
+ if (udata) {
+ if (udata->outlen < sizeof(*uresp))
+ return -EINVAL;
+ uresp = udata->outbuf;
+ }
- err = rxe_cq_chk_attr(rxe, cq, cqe, 0, udata);
+ err = rxe_cq_chk_attr(rxe, cq, cqe, 0);
if (err)
goto err1;
- err = rxe_cq_resize_queue(cq, cqe, udata);
+ err = rxe_cq_resize_queue(cq, cqe, uresp);
if (err)
goto err1;
@@ -1207,7 +1208,7 @@ int rxe_register_device(struct rxe_dev *rxe)
rxe->ndev->dev_addr);
dev->dev.dma_ops = &dma_virt_ops;
dma_coerce_mask_and_coherent(&dev->dev,
- dma_get_required_mask(dev->dev.parent));
+ dma_get_required_mask(&dev->dev));
dev->uverbs_abi_ver = RXE_UVERBS_ABI_VERSION;
dev->uverbs_cmd_mask = BIT_ULL(IB_USER_VERBS_CMD_GET_CONTEXT)
@@ -1248,10 +1249,7 @@ int rxe_register_device(struct rxe_dev *rxe)
dev->query_port = rxe_query_port;
dev->modify_port = rxe_modify_port;
dev->get_link_layer = rxe_get_link_layer;
- dev->query_gid = rxe_query_gid;
dev->get_netdev = rxe_get_netdev;
- dev->add_gid = rxe_add_gid;
- dev->del_gid = rxe_del_gid;
dev->query_pkey = rxe_query_pkey;
dev->alloc_ucontext = rxe_alloc_ucontext;
dev->dealloc_ucontext = rxe_dealloc_ucontext;
@@ -1298,6 +1296,7 @@ int rxe_register_device(struct rxe_dev *rxe)
}
rxe->tfm = tfm;
+ dev->driver_id = RDMA_DRIVER_RXE;
err = ib_register_device(dev, NULL);
if (err) {
pr_warn("%s failed with error %d\n", __func__, err);
diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.h b/drivers/infiniband/sw/rxe/rxe_verbs.h
index 1019f5e7dbdd..af1470d29391 100644
--- a/drivers/infiniband/sw/rxe/rxe_verbs.h
+++ b/drivers/infiniband/sw/rxe/rxe_verbs.h
@@ -139,8 +139,6 @@ enum rxe_qp_state {
QP_STATE_ERROR
};
-extern char *rxe_qp_state_name[];
-
struct rxe_req_info {
enum rxe_qp_state state;
int wqe_index;
diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h
index 8033a006277f..308e0ce49289 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib.h
+++ b/drivers/infiniband/ulp/ipoib/ipoib.h
@@ -193,11 +193,6 @@ struct ipoib_tx_buf {
u64 mapping[MAX_SKB_FRAGS + 1];
};
-struct ipoib_cm_tx_buf {
- struct sk_buff *skb;
- u64 mapping;
-};
-
struct ib_cm_id;
struct ipoib_cm_data {
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
index 10384ea50bed..f47f9ace1f48 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
@@ -1085,7 +1085,7 @@ static bool ipoib_dev_addr_changed_valid(struct ipoib_dev_priv *priv)
netif_addr_unlock_bh(priv->dev);
- err = ib_find_gid(priv->ca, &search_gid, priv->dev, &port, &index);
+ err = ib_find_gid(priv->ca, &search_gid, &port, &index);
netif_addr_lock_bh(priv->dev);
diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c
index b48843833d69..c35d2cd37d70 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
@@ -327,29 +327,10 @@ static int srp_new_ib_cm_id(struct srp_rdma_ch *ch)
return 0;
}
-static const char *inet_ntop(const void *sa, char *dst, unsigned int size)
-{
- switch (((struct sockaddr *)sa)->sa_family) {
- case AF_INET:
- snprintf(dst, size, "%pI4",
- &((struct sockaddr_in *)sa)->sin_addr);
- break;
- case AF_INET6:
- snprintf(dst, size, "%pI6",
- &((struct sockaddr_in6 *)sa)->sin6_addr);
- break;
- default:
- snprintf(dst, size, "???");
- break;
- }
- return dst;
-}
-
static int srp_new_rdma_cm_id(struct srp_rdma_ch *ch)
{
struct srp_target_port *target = ch->target;
struct rdma_cm_id *new_cm_id;
- char src_addr[64], dst_addr[64];
int ret;
new_cm_id = rdma_create_id(target->net, srp_rdma_cm_handler, ch,
@@ -366,13 +347,8 @@ static int srp_new_rdma_cm_id(struct srp_rdma_ch *ch)
(struct sockaddr *)&target->rdma_cm.dst,
SRP_PATH_REC_TIMEOUT_MS);
if (ret) {
- pr_err("No route available from %s to %s (%d)\n",
- target->rdma_cm.src_specified ?
- inet_ntop(&target->rdma_cm.src, src_addr,
- sizeof(src_addr)) : "(any)",
- inet_ntop(&target->rdma_cm.dst, dst_addr,
- sizeof(dst_addr)),
- ret);
+ pr_err("No route available from %pIS to %pIS (%d)\n",
+ &target->rdma_cm.src, &target->rdma_cm.dst, ret);
goto out;
}
ret = wait_for_completion_interruptible(&ch->done);
@@ -381,10 +357,8 @@ static int srp_new_rdma_cm_id(struct srp_rdma_ch *ch)
ret = ch->status;
if (ret) {
- pr_err("Resolving address %s failed (%d)\n",
- inet_ntop(&target->rdma_cm.dst, dst_addr,
- sizeof(dst_addr)),
- ret);
+ pr_err("Resolving address %pIS failed (%d)\n",
+ &target->rdma_cm.dst, ret);
goto out;
}
@@ -457,6 +431,7 @@ static struct srp_fr_pool *srp_create_fr_pool(struct ib_device *device,
struct srp_fr_desc *d;
struct ib_mr *mr;
int i, ret = -EINVAL;
+ enum ib_mr_type mr_type;
if (pool_size <= 0)
goto err;
@@ -470,9 +445,13 @@ static struct srp_fr_pool *srp_create_fr_pool(struct ib_device *device,
spin_lock_init(&pool->lock);
INIT_LIST_HEAD(&pool->free_list);
+ if (device->attrs.device_cap_flags & IB_DEVICE_SG_GAPS_REG)
+ mr_type = IB_MR_TYPE_SG_GAPS;
+ else
+ mr_type = IB_MR_TYPE_MEM_REG;
+
for (i = 0, d = &pool->desc[0]; i < pool->size; i++, d++) {
- mr = ib_alloc_mr(pd, IB_MR_TYPE_MEM_REG,
- max_page_list_len);
+ mr = ib_alloc_mr(pd, mr_type, max_page_list_len);
if (IS_ERR(mr)) {
ret = PTR_ERR(mr);
if (ret == -ENOMEM)
@@ -765,19 +744,12 @@ static void srp_path_rec_completion(int status,
static int srp_ib_lookup_path(struct srp_rdma_ch *ch)
{
struct srp_target_port *target = ch->target;
- int ret = -ENODEV;
+ int ret;
ch->ib_cm.path.numb_path = 1;
init_completion(&ch->done);
- /*
- * Avoid that the SCSI host can be removed by srp_remove_target()
- * before srp_path_rec_completion() is called.
- */
- if (!scsi_host_get(target->scsi_host))
- goto out;
-
ch->ib_cm.path_query_id = ib_sa_path_rec_get(&srp_sa_client,
target->srp_host->srp_dev->dev,
target->srp_host->port,
@@ -791,27 +763,21 @@ static int srp_ib_lookup_path(struct srp_rdma_ch *ch)
GFP_KERNEL,
srp_path_rec_completion,
ch, &ch->ib_cm.path_query);
- ret = ch->ib_cm.path_query_id;
- if (ret < 0)
- goto put;
+ if (ch->ib_cm.path_query_id < 0)
+ return ch->ib_cm.path_query_id;
ret = wait_for_completion_interruptible(&ch->done);
if (ret < 0)
- goto put;
+ return ret;
- ret = ch->status;
- if (ret < 0)
+ if (ch->status < 0)
shost_printk(KERN_WARNING, target->scsi_host,
PFX "Path record query failed: sgid %pI6, dgid %pI6, pkey %#04x, service_id %#16llx\n",
ch->ib_cm.path.sgid.raw, ch->ib_cm.path.dgid.raw,
be16_to_cpu(target->ib_cm.pkey),
be64_to_cpu(target->ib_cm.service_id));
-put:
- scsi_host_put(target->scsi_host);
-
-out:
- return ret;
+ return ch->status;
}
static int srp_rdma_lookup_path(struct srp_rdma_ch *ch)
@@ -2974,9 +2940,11 @@ static int srp_abort(struct scsi_cmnd *scmnd)
ret = FAST_IO_FAIL;
else
ret = FAILED;
- srp_free_req(ch, req, scmnd, 0);
- scmnd->result = DID_ABORT << 16;
- scmnd->scsi_done(scmnd);
+ if (ret == SUCCESS) {
+ srp_free_req(ch, req, scmnd, 0);
+ scmnd->result = DID_ABORT << 16;
+ scmnd->scsi_done(scmnd);
+ }
return ret;
}
@@ -3033,8 +3001,9 @@ static int srp_slave_alloc(struct scsi_device *sdev)
struct Scsi_Host *shost = sdev->host;
struct srp_target_port *target = host_to_target(shost);
struct srp_device *srp_dev = target->srp_host->srp_dev;
+ struct ib_device *ibdev = srp_dev->dev;
- if (true)
+ if (!(ibdev->attrs.device_cap_flags & IB_DEVICE_SG_GAPS_REG))
blk_queue_virt_boundary(sdev->request_queue,
~srp_dev->mr_page_mask);
@@ -3365,9 +3334,6 @@ static bool srp_conn_unique(struct srp_host *host,
if (t != target &&
target->id_ext == t->id_ext &&
target->ioc_guid == t->ioc_guid &&
- (!target->using_rdma_cm ||
- memcmp(&target->rdma_cm.dst, &t->rdma_cm.dst,
- sizeof(target->rdma_cm.dst)) == 0) &&
target->initiator_ext == t->initiator_ext) {
ret = false;
break;
@@ -3445,18 +3411,37 @@ static const match_table_t srp_opt_tokens = {
{ SRP_OPT_ERR, NULL }
};
+/**
+ * srp_parse_in - parse an IP address and port number combination
+ *
+ * Parse the following address formats:
+ * - IPv4: <ip_address>:<port>, e.g. 1.2.3.4:5.
+ * - IPv6: \[<ipv6_address>\]:<port>, e.g. [1::2:3%4]:5.
+ */
static int srp_parse_in(struct net *net, struct sockaddr_storage *sa,
const char *addr_port_str)
{
- char *addr = kstrdup(addr_port_str, GFP_KERNEL);
- char *port_str = addr;
+ char *addr_end, *addr = kstrdup(addr_port_str, GFP_KERNEL);
+ char *port_str;
int ret;
if (!addr)
return -ENOMEM;
- strsep(&port_str, ":");
- ret = inet_pton_with_scope(net, AF_UNSPEC, addr, port_str, sa);
+ port_str = strrchr(addr, ':');
+ if (!port_str)
+ return -EINVAL;
+ *port_str++ = '\0';
+ ret = inet_pton_with_scope(net, AF_INET, addr, port_str, sa);
+ if (ret && addr[0]) {
+ addr_end = addr + strlen(addr) - 1;
+ if (addr[0] == '[' && *addr_end == ']') {
+ *addr_end = '\0';
+ ret = inet_pton_with_scope(net, AF_INET6, addr + 1,
+ port_str, sa);
+ }
+ }
kfree(addr);
+ pr_debug("%s -> %pISpfsc\n", addr_port_str, sa);
return ret;
}
@@ -3789,14 +3774,11 @@ static ssize_t srp_create_target(struct device *dev,
if (!srp_conn_unique(target->srp_host, target)) {
if (target->using_rdma_cm) {
- char dst_addr[64];
-
shost_printk(KERN_INFO, target->scsi_host,
- PFX "Already connected to target port with id_ext=%016llx;ioc_guid=%016llx;dest=%s\n",
+ PFX "Already connected to target port with id_ext=%016llx;ioc_guid=%016llx;dest=%pIS\n",
be64_to_cpu(target->id_ext),
be64_to_cpu(target->ioc_guid),
- inet_ntop(&target->rdma_cm.dst, dst_addr,
- sizeof(dst_addr)));
+ &target->rdma_cm.dst);
} else {
shost_printk(KERN_INFO, target->scsi_host,
PFX "Already connected to target port with id_ext=%016llx;ioc_guid=%016llx;initiator_ext=%016llx\n",
@@ -3815,26 +3797,36 @@ static ssize_t srp_create_target(struct device *dev,
}
if (srp_dev->use_fast_reg || srp_dev->use_fmr) {
- /*
- * FR and FMR can only map one HCA page per entry. If the
- * start address is not aligned on a HCA page boundary two
- * entries will be used for the head and the tail although
- * these two entries combined contain at most one HCA page of
- * data. Hence the "+ 1" in the calculation below.
- *
- * The indirect data buffer descriptor is contiguous so the
- * memory for that buffer will only be registered if
- * register_always is true. Hence add one to mr_per_cmd if
- * register_always has been set.
- */
+ bool gaps_reg = (ibdev->attrs.device_cap_flags &
+ IB_DEVICE_SG_GAPS_REG);
+
max_sectors_per_mr = srp_dev->max_pages_per_mr <<
(ilog2(srp_dev->mr_page_size) - 9);
- mr_per_cmd = register_always +
- (target->scsi_host->max_sectors + 1 +
- max_sectors_per_mr - 1) / max_sectors_per_mr;
+ if (!gaps_reg) {
+ /*
+ * FR and FMR can only map one HCA page per entry. If
+ * the start address is not aligned on a HCA page
+ * boundary two entries will be used for the head and
+ * the tail although these two entries combined
+ * contain at most one HCA page of data. Hence the "+
+ * 1" in the calculation below.
+ *
+ * The indirect data buffer descriptor is contiguous
+ * so the memory for that buffer will only be
+ * registered if register_always is true. Hence add
+ * one to mr_per_cmd if register_always has been set.
+ */
+ mr_per_cmd = register_always +
+ (target->scsi_host->max_sectors + 1 +
+ max_sectors_per_mr - 1) / max_sectors_per_mr;
+ } else {
+ mr_per_cmd = register_always +
+ (target->sg_tablesize +
+ srp_dev->max_pages_per_mr - 1) /
+ srp_dev->max_pages_per_mr;
+ }
pr_debug("max_sectors = %u; max_pages_per_mr = %u; mr_page_size = %u; max_sectors_per_mr = %u; mr_per_cmd = %u\n",
- target->scsi_host->max_sectors,
- srp_dev->max_pages_per_mr, srp_dev->mr_page_size,
+ target->scsi_host->max_sectors, srp_dev->max_pages_per_mr, srp_dev->mr_page_size,
max_sectors_per_mr, mr_per_cmd);
}
@@ -3871,12 +3863,10 @@ static ssize_t srp_create_target(struct device *dev,
num_online_nodes());
const int ch_end = ((node_idx + 1) * target->ch_count /
num_online_nodes());
- const int cv_start = (node_idx * ibdev->num_comp_vectors /
- num_online_nodes() + target->comp_vector)
- % ibdev->num_comp_vectors;
- const int cv_end = ((node_idx + 1) * ibdev->num_comp_vectors /
- num_online_nodes() + target->comp_vector)
- % ibdev->num_comp_vectors;
+ const int cv_start = node_idx * ibdev->num_comp_vectors /
+ num_online_nodes();
+ const int cv_end = (node_idx + 1) * ibdev->num_comp_vectors /
+ num_online_nodes();
int cpu_idx = 0;
for_each_online_cpu(cpu) {
@@ -3907,8 +3897,8 @@ static ssize_t srp_create_target(struct device *dev,
char dst[64];
if (target->using_rdma_cm)
- inet_ntop(&target->rdma_cm.dst, dst,
- sizeof(dst));
+ snprintf(dst, sizeof(dst), "%pIS",
+ &target->rdma_cm.dst);
else
snprintf(dst, sizeof(dst), "%pI6",
target->ib_cm.orig_dgid.raw);
@@ -3941,14 +3931,11 @@ connected:
if (target->state != SRP_TARGET_REMOVED) {
if (target->using_rdma_cm) {
- char dst[64];
-
- inet_ntop(&target->rdma_cm.dst, dst, sizeof(dst));
shost_printk(KERN_DEBUG, target->scsi_host, PFX
- "new target: id_ext %016llx ioc_guid %016llx sgid %pI6 dest %s\n",
+ "new target: id_ext %016llx ioc_guid %016llx sgid %pI6 dest %pIS\n",
be64_to_cpu(target->id_ext),
be64_to_cpu(target->ioc_guid),
- target->sgid.raw, dst);
+ target->sgid.raw, &target->rdma_cm.dst);
} else {
shost_printk(KERN_DEBUG, target->scsi_host, PFX
"new target: id_ext %016llx ioc_guid %016llx pkey %04x service_id %016llx sgid %pI6 dgid %pI6\n",
diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c
index 0373b7c40902..dfec0e1fac29 100644
--- a/drivers/infiniband/ulp/srpt/ib_srpt.c
+++ b/drivers/infiniband/ulp/srpt/ib_srpt.c
@@ -41,6 +41,7 @@
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/atomic.h>
+#include <linux/inet.h>
#include <rdma/ib_cache.h>
#include <scsi/scsi_proto.h>
#include <scsi/scsi_tcq.h>
@@ -92,6 +93,11 @@ MODULE_PARM_DESC(srpt_service_guid,
" instead of using the node_guid of the first HCA.");
static struct ib_client srpt_client;
+/* Protects both rdma_cm_port and rdma_cm_id. */
+static DEFINE_MUTEX(rdma_cm_mutex);
+/* Port number RDMA/CM will bind to. */
+static u16 rdma_cm_port;
+static struct rdma_cm_id *rdma_cm_id;
static void srpt_release_cmd(struct se_cmd *se_cmd);
static void srpt_free_ch(struct kref *kref);
static int srpt_queue_status(struct se_cmd *cmd);
@@ -220,7 +226,10 @@ static void srpt_qp_event(struct ib_event *event, struct srpt_rdma_ch *ch)
switch (event->event) {
case IB_EVENT_COMM_EST:
- ib_cm_notify(ch->ib_cm.cm_id, event->event);
+ if (ch->using_rdma_cm)
+ rdma_notify(ch->rdma_cm.cm_id, event->event);
+ else
+ ib_cm_notify(ch->ib_cm.cm_id, event->event);
break;
case IB_EVENT_QP_LAST_WQE_REACHED:
pr_debug("%s-%d, state %s: received Last WQE event.\n",
@@ -838,16 +847,20 @@ static int srpt_post_recv(struct srpt_device *sdev, struct srpt_rdma_ch *ch,
*/
static int srpt_zerolength_write(struct srpt_rdma_ch *ch)
{
- struct ib_send_wr wr, *bad_wr;
+ struct ib_send_wr *bad_wr;
+ struct ib_rdma_wr wr = {
+ .wr = {
+ .next = NULL,
+ { .wr_cqe = &ch->zw_cqe, },
+ .opcode = IB_WR_RDMA_WRITE,
+ .send_flags = IB_SEND_SIGNALED,
+ }
+ };
pr_debug("%s-%d: queued zerolength write\n", ch->sess_name,
ch->qp->qp_num);
- memset(&wr, 0, sizeof(wr));
- wr.opcode = IB_WR_RDMA_WRITE;
- wr.wr_cqe = &ch->zw_cqe;
- wr.send_flags = IB_SEND_SIGNALED;
- return ib_post_send(ch->qp, &wr, &bad_wr);
+ return ib_post_send(ch->qp, &wr.wr, &bad_wr);
}
static void srpt_zerolength_write_done(struct ib_cq *cq, struct ib_wc *wc)
@@ -1057,6 +1070,8 @@ static int srpt_init_ch_qp(struct srpt_rdma_ch *ch, struct ib_qp *qp)
struct ib_qp_attr *attr;
int ret;
+ WARN_ON_ONCE(ch->using_rdma_cm);
+
attr = kzalloc(sizeof(*attr), GFP_KERNEL);
if (!attr)
return -ENOMEM;
@@ -1096,6 +1111,8 @@ static int srpt_ch_qp_rtr(struct srpt_rdma_ch *ch, struct ib_qp *qp)
int attr_mask;
int ret;
+ WARN_ON_ONCE(ch->using_rdma_cm);
+
qp_attr.qp_state = IB_QPS_RTR;
ret = ib_cm_init_qp_attr(ch->ib_cm.cm_id, &qp_attr, &attr_mask);
if (ret)
@@ -1746,18 +1763,33 @@ retry:
qp_init->cap.max_recv_sge = qp_init->cap.max_send_sge;
}
- ch->qp = ib_create_qp(sdev->pd, qp_init);
- if (IS_ERR(ch->qp)) {
- ret = PTR_ERR(ch->qp);
- if (ret == -ENOMEM) {
- sq_size /= 2;
- if (sq_size >= MIN_SRPT_SQ_SIZE) {
- ib_destroy_cq(ch->cq);
- goto retry;
- }
+ if (ch->using_rdma_cm) {
+ ret = rdma_create_qp(ch->rdma_cm.cm_id, sdev->pd, qp_init);
+ ch->qp = ch->rdma_cm.cm_id->qp;
+ } else {
+ ch->qp = ib_create_qp(sdev->pd, qp_init);
+ if (!IS_ERR(ch->qp)) {
+ ret = srpt_init_ch_qp(ch, ch->qp);
+ if (ret)
+ ib_destroy_qp(ch->qp);
+ } else {
+ ret = PTR_ERR(ch->qp);
+ }
+ }
+ if (ret) {
+ bool retry = sq_size > MIN_SRPT_SQ_SIZE;
+
+ if (retry) {
+ pr_debug("failed to create queue pair with sq_size = %d (%d) - retrying\n",
+ sq_size, ret);
+ ib_free_cq(ch->cq);
+ sq_size = max(sq_size / 2, MIN_SRPT_SQ_SIZE);
+ goto retry;
+ } else {
+ pr_err("failed to create queue pair with sq_size = %d (%d)\n",
+ sq_size, ret);
+ goto err_destroy_cq;
}
- pr_err("failed to create_qp ret= %d\n", ret);
- goto err_destroy_cq;
}
atomic_set(&ch->sq_wr_avail, qp_init->cap.max_send_wr);
@@ -1766,10 +1798,6 @@ retry:
__func__, ch->cq->cqe, qp_init->cap.max_send_sge,
qp_init->cap.max_send_wr, ch);
- ret = srpt_init_ch_qp(ch, ch->qp);
- if (ret)
- goto err_destroy_qp;
-
if (!sdev->use_srq)
for (i = 0; i < ch->rq_size; i++)
srpt_post_recv(sdev, ch, ch->ioctx_recv_ring[i]);
@@ -1778,9 +1806,8 @@ out:
kfree(qp_init);
return ret;
-err_destroy_qp:
- ib_destroy_qp(ch->qp);
err_destroy_cq:
+ ch->qp = NULL;
ib_free_cq(ch->cq);
goto out;
}
@@ -1849,9 +1876,13 @@ static int srpt_disconnect_ch(struct srpt_rdma_ch *ch)
if (!srpt_set_ch_state(ch, CH_DISCONNECTING))
return -ENOTCONN;
- ret = ib_send_cm_dreq(ch->ib_cm.cm_id, NULL, 0);
- if (ret < 0)
- ret = ib_send_cm_drep(ch->ib_cm.cm_id, NULL, 0);
+ if (ch->using_rdma_cm) {
+ ret = rdma_disconnect(ch->rdma_cm.cm_id);
+ } else {
+ ret = ib_send_cm_dreq(ch->ib_cm.cm_id, NULL, 0);
+ if (ret < 0)
+ ret = ib_send_cm_drep(ch->ib_cm.cm_id, NULL, 0);
+ }
if (ret < 0 && srpt_close_ch(ch))
ret = 0;
@@ -2002,7 +2033,10 @@ static void srpt_release_channel_work(struct work_struct *w)
transport_deregister_session(se_sess);
ch->sess = NULL;
- ib_destroy_cm_id(ch->ib_cm.cm_id);
+ if (ch->using_rdma_cm)
+ rdma_destroy_id(ch->rdma_cm.cm_id);
+ else
+ ib_destroy_cm_id(ch->ib_cm.cm_id);
srpt_destroy_ch_ib(ch);
@@ -2026,26 +2060,33 @@ static void srpt_release_channel_work(struct work_struct *w)
/**
* srpt_cm_req_recv - process the event IB_CM_REQ_RECEIVED
- * @cm_id: IB/CM connection identifier.
- * @port_num: Port through which the IB/CM REQ message was received.
+ * @sdev: HCA through which the login request was received.
+ * @ib_cm_id: IB/CM connection identifier in case of IB/CM.
+ * @rdma_cm_id: RDMA/CM connection identifier in case of RDMA/CM.
+ * @port_num: Port through which the REQ message was received.
* @pkey: P_Key of the incoming connection.
* @req: SRP login request.
- * @src_addr: GID of the port that submitted the login request.
+ * @src_addr: GID (IB/CM) or IP address (RDMA/CM) of the port that submitted
+ * the login request.
*
* Ownership of the cm_id is transferred to the target session if this
- * functions returns zero. Otherwise the caller remains the owner of cm_id.
+ * function returns zero. Otherwise the caller remains the owner of cm_id.
*/
-static int srpt_cm_req_recv(struct ib_cm_id *cm_id,
+static int srpt_cm_req_recv(struct srpt_device *const sdev,
+ struct ib_cm_id *ib_cm_id,
+ struct rdma_cm_id *rdma_cm_id,
u8 port_num, __be16 pkey,
const struct srp_login_req *req,
const char *src_addr)
{
- struct srpt_device *sdev = cm_id->context;
struct srpt_port *sport = &sdev->port[port_num - 1];
struct srpt_nexus *nexus;
struct srp_login_rsp *rsp = NULL;
struct srp_login_rej *rej = NULL;
- struct ib_cm_rep_param *rep_param = NULL;
+ union {
+ struct rdma_conn_param rdma_cm;
+ struct ib_cm_rep_param ib_cm;
+ } *rep_param = NULL;
struct srpt_rdma_ch *ch;
char i_port_id[36];
u32 it_iu_len;
@@ -2115,8 +2156,14 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id,
ch->zw_cqe.done = srpt_zerolength_write_done;
INIT_WORK(&ch->release_work, srpt_release_channel_work);
ch->sport = sport;
- ch->ib_cm.cm_id = cm_id;
- cm_id->context = ch;
+ if (ib_cm_id) {
+ ch->ib_cm.cm_id = ib_cm_id;
+ ib_cm_id->context = ch;
+ } else {
+ ch->using_rdma_cm = true;
+ ch->rdma_cm.cm_id = rdma_cm_id;
+ rdma_cm_id->context = ch;
+ }
/*
* ch->rq_size should be at least as large as the initiator queue
* depth to avoid that the initiator driver has to report QUEUE_FULL
@@ -2227,7 +2274,7 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id,
mutex_unlock(&sport->mutex);
- ret = srpt_ch_qp_rtr(ch, ch->qp);
+ ret = ch->using_rdma_cm ? 0 : srpt_ch_qp_rtr(ch, ch->qp);
if (ret) {
rej->reason = cpu_to_be32(SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES);
pr_err("rejected SRP_LOGIN_REQ because enabling RTR failed (error code = %d)\n",
@@ -2251,25 +2298,38 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id,
atomic_set(&ch->req_lim_delta, 0);
/* create cm reply */
- rep_param->qp_num = ch->qp->qp_num;
- rep_param->private_data = (void *)rsp;
- rep_param->private_data_len = sizeof(*rsp);
- rep_param->rnr_retry_count = 7;
- rep_param->flow_control = 1;
- rep_param->failover_accepted = 0;
- rep_param->srq = 1;
- rep_param->responder_resources = 4;
- rep_param->initiator_depth = 4;
+ if (ch->using_rdma_cm) {
+ rep_param->rdma_cm.private_data = (void *)rsp;
+ rep_param->rdma_cm.private_data_len = sizeof(*rsp);
+ rep_param->rdma_cm.rnr_retry_count = 7;
+ rep_param->rdma_cm.flow_control = 1;
+ rep_param->rdma_cm.responder_resources = 4;
+ rep_param->rdma_cm.initiator_depth = 4;
+ } else {
+ rep_param->ib_cm.qp_num = ch->qp->qp_num;
+ rep_param->ib_cm.private_data = (void *)rsp;
+ rep_param->ib_cm.private_data_len = sizeof(*rsp);
+ rep_param->ib_cm.rnr_retry_count = 7;
+ rep_param->ib_cm.flow_control = 1;
+ rep_param->ib_cm.failover_accepted = 0;
+ rep_param->ib_cm.srq = 1;
+ rep_param->ib_cm.responder_resources = 4;
+ rep_param->ib_cm.initiator_depth = 4;
+ }
/*
* Hold the sport mutex while accepting a connection to avoid that
* srpt_disconnect_ch() is invoked concurrently with this code.
*/
mutex_lock(&sport->mutex);
- if (sport->enabled && ch->state == CH_CONNECTING)
- ret = ib_send_cm_rep(cm_id, rep_param);
- else
+ if (sport->enabled && ch->state == CH_CONNECTING) {
+ if (ch->using_rdma_cm)
+ ret = rdma_accept(rdma_cm_id, &rep_param->rdma_cm);
+ else
+ ret = ib_send_cm_rep(ib_cm_id, &rep_param->ib_cm);
+ } else {
ret = -EINVAL;
+ }
mutex_unlock(&sport->mutex);
switch (ret) {
@@ -2299,7 +2359,8 @@ free_ring:
ch->sport->sdev, ch->rq_size,
ch->max_rsp_size, DMA_TO_DEVICE);
free_ch:
- cm_id->context = NULL;
+ if (ib_cm_id)
+ ib_cm_id->context = NULL;
kfree(ch);
ch = NULL;
@@ -2312,8 +2373,11 @@ reject:
rej->buf_fmt = cpu_to_be16(SRP_BUF_FORMAT_DIRECT |
SRP_BUF_FORMAT_INDIRECT);
- ib_send_cm_rej(cm_id, IB_CM_REJ_CONSUMER_DEFINED, NULL, 0,
- (void *)rej, sizeof(*rej));
+ if (rdma_cm_id)
+ rdma_reject(rdma_cm_id, rej, sizeof(*rej));
+ else
+ ib_send_cm_rej(ib_cm_id, IB_CM_REJ_CONSUMER_DEFINED, NULL, 0,
+ rej, sizeof(*rej));
out:
kfree(rep_param);
@@ -2332,10 +2396,44 @@ static int srpt_ib_cm_req_recv(struct ib_cm_id *cm_id,
srpt_format_guid(sguid, sizeof(sguid),
&param->primary_path->dgid.global.interface_id);
- return srpt_cm_req_recv(cm_id, param->port, param->primary_path->pkey,
+ return srpt_cm_req_recv(cm_id->context, cm_id, NULL, param->port,
+ param->primary_path->pkey,
private_data, sguid);
}
+static int srpt_rdma_cm_req_recv(struct rdma_cm_id *cm_id,
+ struct rdma_cm_event *event)
+{
+ struct srpt_device *sdev;
+ struct srp_login_req req;
+ const struct srp_login_req_rdma *req_rdma;
+ char src_addr[40];
+
+ sdev = ib_get_client_data(cm_id->device, &srpt_client);
+ if (!sdev)
+ return -ECONNREFUSED;
+
+ if (event->param.conn.private_data_len < sizeof(*req_rdma))
+ return -EINVAL;
+
+ /* Transform srp_login_req_rdma into srp_login_req. */
+ req_rdma = event->param.conn.private_data;
+ memset(&req, 0, sizeof(req));
+ req.opcode = req_rdma->opcode;
+ req.tag = req_rdma->tag;
+ req.req_it_iu_len = req_rdma->req_it_iu_len;
+ req.req_buf_fmt = req_rdma->req_buf_fmt;
+ req.req_flags = req_rdma->req_flags;
+ memcpy(req.initiator_port_id, req_rdma->initiator_port_id, 16);
+ memcpy(req.target_port_id, req_rdma->target_port_id, 16);
+
+ snprintf(src_addr, sizeof(src_addr), "%pIS",
+ &cm_id->route.addr.src_addr);
+
+ return srpt_cm_req_recv(sdev, NULL, cm_id, cm_id->port_num,
+ cm_id->route.path_rec->pkey, &req, src_addr);
+}
+
static void srpt_cm_rej_recv(struct srpt_rdma_ch *ch,
enum ib_cm_rej_reason reason,
const u8 *private_data,
@@ -2359,14 +2457,14 @@ static void srpt_cm_rej_recv(struct srpt_rdma_ch *ch,
* srpt_cm_rtu_recv - process an IB_CM_RTU_RECEIVED or USER_ESTABLISHED event
* @ch: SRPT RDMA channel.
*
- * An IB_CM_RTU_RECEIVED message indicates that the connection is established
- * and that the recipient may begin transmitting (RTU = ready to use).
+ * An RTU (ready to use) message indicates that the connection has been
+ * established and that the recipient may begin transmitting.
*/
static void srpt_cm_rtu_recv(struct srpt_rdma_ch *ch)
{
int ret;
- ret = srpt_ch_qp_rts(ch, ch->qp);
+ ret = ch->using_rdma_cm ? 0 : srpt_ch_qp_rts(ch, ch->qp);
if (ret < 0) {
pr_err("%s-%d: QP transition to RTS failed\n", ch->sess_name,
ch->qp->qp_num);
@@ -2453,6 +2551,49 @@ static int srpt_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event)
return ret;
}
+static int srpt_rdma_cm_handler(struct rdma_cm_id *cm_id,
+ struct rdma_cm_event *event)
+{
+ struct srpt_rdma_ch *ch = cm_id->context;
+ int ret = 0;
+
+ switch (event->event) {
+ case RDMA_CM_EVENT_CONNECT_REQUEST:
+ ret = srpt_rdma_cm_req_recv(cm_id, event);
+ break;
+ case RDMA_CM_EVENT_REJECTED:
+ srpt_cm_rej_recv(ch, event->status,
+ event->param.conn.private_data,
+ event->param.conn.private_data_len);
+ break;
+ case RDMA_CM_EVENT_ESTABLISHED:
+ srpt_cm_rtu_recv(ch);
+ break;
+ case RDMA_CM_EVENT_DISCONNECTED:
+ if (ch->state < CH_DISCONNECTING)
+ srpt_disconnect_ch(ch);
+ else
+ srpt_close_ch(ch);
+ break;
+ case RDMA_CM_EVENT_TIMEWAIT_EXIT:
+ srpt_close_ch(ch);
+ break;
+ case RDMA_CM_EVENT_UNREACHABLE:
+ pr_info("Received CM REP error for ch %s-%d.\n", ch->sess_name,
+ ch->qp->qp_num);
+ break;
+ case RDMA_CM_EVENT_DEVICE_REMOVAL:
+ case RDMA_CM_EVENT_ADDR_CHANGE:
+ break;
+ default:
+ pr_err("received unrecognized RDMA CM event %d\n",
+ event->event);
+ break;
+ }
+
+ return ret;
+}
+
static int srpt_write_pending_status(struct se_cmd *se_cmd)
{
struct srpt_send_ioctx *ioctx;
@@ -2824,7 +2965,7 @@ static void srpt_add_one(struct ib_device *device)
{
struct srpt_device *sdev;
struct srpt_port *sport;
- int i;
+ int i, ret;
pr_debug("device = %p\n", device);
@@ -2848,9 +2989,15 @@ static void srpt_add_one(struct ib_device *device)
if (!srpt_service_guid)
srpt_service_guid = be64_to_cpu(device->node_guid);
- sdev->cm_id = ib_create_cm_id(device, srpt_cm_handler, sdev);
- if (IS_ERR(sdev->cm_id))
- goto err_ring;
+ if (rdma_port_get_link_layer(device, 1) == IB_LINK_LAYER_INFINIBAND)
+ sdev->cm_id = ib_create_cm_id(device, srpt_cm_handler, sdev);
+ if (IS_ERR(sdev->cm_id)) {
+ pr_info("ib_create_cm_id() failed: %ld\n",
+ PTR_ERR(sdev->cm_id));
+ sdev->cm_id = NULL;
+ if (!rdma_cm_id)
+ goto err_ring;
+ }
/* print out target login information */
pr_debug("Target login info: id_ext=%016llx,ioc_guid=%016llx,"
@@ -2863,8 +3010,14 @@ static void srpt_add_one(struct ib_device *device)
* in the system as service_id; therefore, the target_id will change
* if this HCA is gone bad and replaced by different HCA
*/
- if (ib_cm_listen(sdev->cm_id, cpu_to_be64(srpt_service_guid), 0))
+ ret = sdev->cm_id ?
+ ib_cm_listen(sdev->cm_id, cpu_to_be64(srpt_service_guid), 0) :
+ 0;
+ if (ret < 0) {
+ pr_err("ib_cm_listen() failed: %d (cm_id state = %d)\n", ret,
+ sdev->cm_id->state);
goto err_cm;
+ }
INIT_IB_EVENT_HANDLER(&sdev->event_handler, sdev->device,
srpt_event_handler);
@@ -2904,7 +3057,8 @@ out:
err_event:
ib_unregister_event_handler(&sdev->event_handler);
err_cm:
- ib_destroy_cm_id(sdev->cm_id);
+ if (sdev->cm_id)
+ ib_destroy_cm_id(sdev->cm_id);
err_ring:
srpt_free_srq(sdev);
ib_dealloc_pd(sdev->pd);
@@ -2939,7 +3093,10 @@ static void srpt_remove_one(struct ib_device *device, void *client_data)
for (i = 0; i < sdev->device->phys_port_cnt; i++)
cancel_work_sync(&sdev->port[i].work);
- ib_destroy_cm_id(sdev->cm_id);
+ if (sdev->cm_id)
+ ib_destroy_cm_id(sdev->cm_id);
+
+ ib_set_client_data(device, &srpt_client, NULL);
/*
* Unregistering a target must happen after destroying sdev->cm_id
@@ -3103,18 +3260,26 @@ static int srpt_parse_i_port_id(u8 i_port_id[16], const char *name)
leading_zero_bytes = 16 - count;
memset(i_port_id, 0, leading_zero_bytes);
ret = hex2bin(i_port_id + leading_zero_bytes, p, count);
- if (ret < 0)
- pr_debug("hex2bin failed for srpt_parse_i_port_id: %d\n", ret);
+
out:
return ret;
}
/*
- * configfs callback function invoked for
- * mkdir /sys/kernel/config/target/$driver/$port/$tpg/acls/$i_port_id
+ * configfs callback function invoked for mkdir
+ * /sys/kernel/config/target/$driver/$port/$tpg/acls/$i_port_id
+ *
+ * i_port_id must be an initiator port GUID, GID or IP address. See also the
+ * target_alloc_session() calls in this driver. Examples of valid initiator
+ * port IDs:
+ * 0x0000000000000000505400fffe4a0b7b
+ * 0000000000000000505400fffe4a0b7b
+ * 5054:00ff:fe4a:0b7b
+ * 192.168.122.76
*/
static int srpt_init_nodeacl(struct se_node_acl *se_nacl, const char *name)
{
+ struct sockaddr_storage sa;
u64 guid;
u8 i_port_id[16];
int ret;
@@ -3123,6 +3288,9 @@ static int srpt_init_nodeacl(struct se_node_acl *se_nacl, const char *name)
if (ret < 0)
ret = srpt_parse_i_port_id(i_port_id, name);
if (ret < 0)
+ ret = inet_pton_with_scope(&init_net, AF_UNSPEC, name, NULL,
+ &sa);
+ if (ret < 0)
pr_err("invalid initiator port ID %s\n", name);
return ret;
}
@@ -3296,6 +3464,95 @@ static struct configfs_attribute *srpt_tpg_attrib_attrs[] = {
NULL,
};
+static struct rdma_cm_id *srpt_create_rdma_id(struct sockaddr *listen_addr)
+{
+ struct rdma_cm_id *rdma_cm_id;
+ int ret;
+
+ rdma_cm_id = rdma_create_id(&init_net, srpt_rdma_cm_handler,
+ NULL, RDMA_PS_TCP, IB_QPT_RC);
+ if (IS_ERR(rdma_cm_id)) {
+ pr_err("RDMA/CM ID creation failed: %ld\n",
+ PTR_ERR(rdma_cm_id));
+ goto out;
+ }
+
+ ret = rdma_bind_addr(rdma_cm_id, listen_addr);
+ if (ret) {
+ char addr_str[64];
+
+ snprintf(addr_str, sizeof(addr_str), "%pISp", listen_addr);
+ pr_err("Binding RDMA/CM ID to address %s failed: %d\n",
+ addr_str, ret);
+ rdma_destroy_id(rdma_cm_id);
+ rdma_cm_id = ERR_PTR(ret);
+ goto out;
+ }
+
+ ret = rdma_listen(rdma_cm_id, 128);
+ if (ret) {
+ pr_err("rdma_listen() failed: %d\n", ret);
+ rdma_destroy_id(rdma_cm_id);
+ rdma_cm_id = ERR_PTR(ret);
+ }
+
+out:
+ return rdma_cm_id;
+}
+
+static ssize_t srpt_rdma_cm_port_show(struct config_item *item, char *page)
+{
+ return sprintf(page, "%d\n", rdma_cm_port);
+}
+
+static ssize_t srpt_rdma_cm_port_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct sockaddr_in addr4 = { .sin_family = AF_INET };
+ struct sockaddr_in6 addr6 = { .sin6_family = AF_INET6 };
+ struct rdma_cm_id *new_id = NULL;
+ u16 val;
+ int ret;
+
+ ret = kstrtou16(page, 0, &val);
+ if (ret < 0)
+ return ret;
+ ret = count;
+ if (rdma_cm_port == val)
+ goto out;
+
+ if (val) {
+ addr6.sin6_port = cpu_to_be16(val);
+ new_id = srpt_create_rdma_id((struct sockaddr *)&addr6);
+ if (IS_ERR(new_id)) {
+ addr4.sin_port = cpu_to_be16(val);
+ new_id = srpt_create_rdma_id((struct sockaddr *)&addr4);
+ if (IS_ERR(new_id)) {
+ ret = PTR_ERR(new_id);
+ goto out;
+ }
+ }
+ }
+
+ mutex_lock(&rdma_cm_mutex);
+ rdma_cm_port = val;
+ swap(rdma_cm_id, new_id);
+ mutex_unlock(&rdma_cm_mutex);
+
+ if (new_id)
+ rdma_destroy_id(new_id);
+ ret = count;
+out:
+ return ret;
+}
+
+CONFIGFS_ATTR(srpt_, rdma_cm_port);
+
+static struct configfs_attribute *srpt_da_attrs[] = {
+ &srpt_attr_rdma_cm_port,
+ NULL,
+};
+
static ssize_t srpt_tpg_enable_show(struct config_item *item, char *page)
{
struct se_portal_group *se_tpg = to_tpg(item);
@@ -3441,6 +3698,7 @@ static const struct target_core_fabric_ops srpt_template = {
.fabric_drop_tpg = srpt_drop_tpg,
.fabric_init_nodeacl = srpt_init_nodeacl,
+ .tfc_discovery_attrs = srpt_da_attrs,
.tfc_wwn_attrs = srpt_wwn_attrs,
.tfc_tpg_base_attrs = srpt_tpg_attrs,
.tfc_tpg_attrib_attrs = srpt_tpg_attrib_attrs,
@@ -3494,6 +3752,8 @@ out:
static void __exit srpt_cleanup_module(void)
{
+ if (rdma_cm_id)
+ rdma_destroy_id(rdma_cm_id);
ib_unregister_client(&srpt_client);
target_unregister_template(&srpt_template);
}
diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.h b/drivers/infiniband/ulp/srpt/ib_srpt.h
index 4d9199fd00dc..2361483476a0 100644
--- a/drivers/infiniband/ulp/srpt/ib_srpt.h
+++ b/drivers/infiniband/ulp/srpt/ib_srpt.h
@@ -42,6 +42,7 @@
#include <rdma/ib_verbs.h>
#include <rdma/ib_sa.h>
#include <rdma/ib_cm.h>
+#include <rdma/rdma_cm.h>
#include <rdma/rw.h>
#include <scsi/srp.h>
@@ -261,6 +262,7 @@ enum rdma_ch_state {
* @spinlock: Protects free_list and state.
* @free_list: Head of list with free send I/O contexts.
* @state: channel state. See also enum rdma_ch_state.
+ * @using_rdma_cm: Whether the RDMA/CM or IB/CM is used for this channel.
* @processing_wait_list: Whether or not cmd_wait_list is being processed.
* @ioctx_ring: Send ring.
* @ioctx_recv_ring: Receive I/O context ring.
@@ -280,6 +282,9 @@ struct srpt_rdma_ch {
struct {
struct ib_cm_id *cm_id;
} ib_cm;
+ struct {
+ struct rdma_cm_id *cm_id;
+ } rdma_cm;
};
struct ib_cq *cq;
struct ib_cqe zw_cqe;
@@ -300,9 +305,10 @@ struct srpt_rdma_ch {
struct list_head list;
struct list_head cmd_wait_list;
uint16_t pkey;
+ bool using_rdma_cm;
bool processing_wait_list;
struct se_session *sess;
- u8 sess_name[24];
+ u8 sess_name[40];
struct work_struct release_work;
};
diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
index f3c2f6ea8b44..9591fc04a8ab 100644
--- a/drivers/input/joystick/Kconfig
+++ b/drivers/input/joystick/Kconfig
@@ -351,4 +351,14 @@ config JOYSTICK_PSXPAD_SPI_FF
To drive rumble motor a dedicated power supply is required.
+config JOYSTICK_PXRC
+ tristate "PhoenixRC Flight Controller Adapter"
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ Say Y here if you want to use the PhoenixRC Flight Controller Adapter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pxrc.
+
endif
diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile
index 67651efda2e1..dd0492ebbed7 100644
--- a/drivers/input/joystick/Makefile
+++ b/drivers/input/joystick/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_JOYSTICK_JOYDUMP) += joydump.o
obj-$(CONFIG_JOYSTICK_MAGELLAN) += magellan.o
obj-$(CONFIG_JOYSTICK_MAPLE) += maplecontrol.o
obj-$(CONFIG_JOYSTICK_PSXPAD_SPI) += psxpad-spi.o
+obj-$(CONFIG_JOYSTICK_PXRC) += pxrc.o
obj-$(CONFIG_JOYSTICK_SIDEWINDER) += sidewinder.o
obj-$(CONFIG_JOYSTICK_SPACEBALL) += spaceball.o
obj-$(CONFIG_JOYSTICK_SPACEORB) += spaceorb.o
diff --git a/drivers/input/joystick/analog.c b/drivers/input/joystick/analog.c
index c868a878c84f..c79dbcb4d146 100644
--- a/drivers/input/joystick/analog.c
+++ b/drivers/input/joystick/analog.c
@@ -163,7 +163,7 @@ static unsigned int get_time_pit(void)
#define GET_TIME(x) do { x = (unsigned int)rdtsc(); } while (0)
#define DELTA(x,y) ((y)-(x))
#define TIME_NAME "TSC"
-#elif defined(__alpha__) || defined(CONFIG_MN10300) || defined(CONFIG_ARM) || defined(CONFIG_ARM64) || defined(CONFIG_RISCV) || defined(CONFIG_TILE)
+#elif defined(__alpha__) || defined(CONFIG_ARM) || defined(CONFIG_ARM64) || defined(CONFIG_PPC) || defined(CONFIG_RISCV)
#define GET_TIME(x) do { x = get_cycles(); } while (0)
#define DELTA(x,y) ((y)-(x))
#define TIME_NAME "get_cycles"
diff --git a/drivers/input/joystick/pxrc.c b/drivers/input/joystick/pxrc.c
new file mode 100644
index 000000000000..07a0dbd3ced2
--- /dev/null
+++ b/drivers/input/joystick/pxrc.c
@@ -0,0 +1,303 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for Phoenix RC Flight Controller Adapter
+ *
+ * Copyright (C) 2018 Marcus Folkesson <marcus.folkesson@gmail.com>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <linux/mutex.h>
+#include <linux/input.h>
+
+#define PXRC_VENDOR_ID (0x1781)
+#define PXRC_PRODUCT_ID (0x0898)
+
+static const struct usb_device_id pxrc_table[] = {
+ { USB_DEVICE(PXRC_VENDOR_ID, PXRC_PRODUCT_ID) },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, pxrc_table);
+
+struct pxrc {
+ struct input_dev *input;
+ struct usb_device *udev;
+ struct usb_interface *intf;
+ struct urb *urb;
+ struct mutex pm_mutex;
+ bool is_open;
+ __u8 epaddr;
+ char phys[64];
+ unsigned char *data;
+ size_t bsize;
+};
+
+static void pxrc_usb_irq(struct urb *urb)
+{
+ struct pxrc *pxrc = urb->context;
+ int error;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ETIME:
+ /* this urb is timing out */
+ dev_dbg(&pxrc->intf->dev,
+ "%s - urb timed out - was the device unplugged?\n",
+ __func__);
+ return;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ case -EPIPE:
+ /* this urb is terminated, clean up */
+ dev_dbg(&pxrc->intf->dev, "%s - urb shutting down with status: %d\n",
+ __func__, urb->status);
+ return;
+ default:
+ dev_dbg(&pxrc->intf->dev, "%s - nonzero urb status received: %d\n",
+ __func__, urb->status);
+ goto exit;
+ }
+
+ if (urb->actual_length == 8) {
+ input_report_abs(pxrc->input, ABS_X, pxrc->data[0]);
+ input_report_abs(pxrc->input, ABS_Y, pxrc->data[2]);
+ input_report_abs(pxrc->input, ABS_RX, pxrc->data[3]);
+ input_report_abs(pxrc->input, ABS_RY, pxrc->data[4]);
+ input_report_abs(pxrc->input, ABS_RUDDER, pxrc->data[5]);
+ input_report_abs(pxrc->input, ABS_THROTTLE, pxrc->data[6]);
+ input_report_abs(pxrc->input, ABS_MISC, pxrc->data[7]);
+
+ input_report_key(pxrc->input, BTN_A, pxrc->data[1]);
+ }
+
+exit:
+ /* Resubmit to fetch new fresh URBs */
+ error = usb_submit_urb(urb, GFP_ATOMIC);
+ if (error && error != -EPERM)
+ dev_err(&pxrc->intf->dev,
+ "%s - usb_submit_urb failed with result: %d",
+ __func__, error);
+}
+
+static int pxrc_open(struct input_dev *input)
+{
+ struct pxrc *pxrc = input_get_drvdata(input);
+ int retval;
+
+ mutex_lock(&pxrc->pm_mutex);
+ retval = usb_submit_urb(pxrc->urb, GFP_KERNEL);
+ if (retval) {
+ dev_err(&pxrc->intf->dev,
+ "%s - usb_submit_urb failed, error: %d\n",
+ __func__, retval);
+ retval = -EIO;
+ goto out;
+ }
+
+ pxrc->is_open = true;
+
+out:
+ mutex_unlock(&pxrc->pm_mutex);
+ return retval;
+}
+
+static void pxrc_close(struct input_dev *input)
+{
+ struct pxrc *pxrc = input_get_drvdata(input);
+
+ mutex_lock(&pxrc->pm_mutex);
+ usb_kill_urb(pxrc->urb);
+ pxrc->is_open = false;
+ mutex_unlock(&pxrc->pm_mutex);
+}
+
+static int pxrc_usb_init(struct pxrc *pxrc)
+{
+ struct usb_endpoint_descriptor *epirq;
+ unsigned int pipe;
+ int retval;
+
+ /* Set up the endpoint information */
+ /* This device only has an interrupt endpoint */
+ retval = usb_find_common_endpoints(pxrc->intf->cur_altsetting,
+ NULL, NULL, &epirq, NULL);
+ if (retval) {
+ dev_err(&pxrc->intf->dev,
+ "Could not find endpoint\n");
+ goto error;
+ }
+
+ pxrc->bsize = usb_endpoint_maxp(epirq);
+ pxrc->epaddr = epirq->bEndpointAddress;
+ pxrc->data = devm_kmalloc(&pxrc->intf->dev, pxrc->bsize, GFP_KERNEL);
+ if (!pxrc->data) {
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ usb_set_intfdata(pxrc->intf, pxrc);
+ usb_make_path(pxrc->udev, pxrc->phys, sizeof(pxrc->phys));
+ strlcat(pxrc->phys, "/input0", sizeof(pxrc->phys));
+
+ pxrc->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!pxrc->urb) {
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ pipe = usb_rcvintpipe(pxrc->udev, pxrc->epaddr),
+ usb_fill_int_urb(pxrc->urb, pxrc->udev, pipe, pxrc->data, pxrc->bsize,
+ pxrc_usb_irq, pxrc, 1);
+
+error:
+ return retval;
+
+
+}
+
+static int pxrc_input_init(struct pxrc *pxrc)
+{
+ pxrc->input = devm_input_allocate_device(&pxrc->intf->dev);
+ if (pxrc->input == NULL) {
+ dev_err(&pxrc->intf->dev, "couldn't allocate input device\n");
+ return -ENOMEM;
+ }
+
+ pxrc->input->name = "PXRC Flight Controller Adapter";
+ pxrc->input->phys = pxrc->phys;
+ usb_to_input_id(pxrc->udev, &pxrc->input->id);
+
+ pxrc->input->open = pxrc_open;
+ pxrc->input->close = pxrc_close;
+
+ input_set_capability(pxrc->input, EV_KEY, BTN_A);
+ input_set_abs_params(pxrc->input, ABS_X, 0, 255, 0, 0);
+ input_set_abs_params(pxrc->input, ABS_Y, 0, 255, 0, 0);
+ input_set_abs_params(pxrc->input, ABS_RX, 0, 255, 0, 0);
+ input_set_abs_params(pxrc->input, ABS_RY, 0, 255, 0, 0);
+ input_set_abs_params(pxrc->input, ABS_RUDDER, 0, 255, 0, 0);
+ input_set_abs_params(pxrc->input, ABS_THROTTLE, 0, 255, 0, 0);
+ input_set_abs_params(pxrc->input, ABS_MISC, 0, 255, 0, 0);
+
+ input_set_drvdata(pxrc->input, pxrc);
+
+ return input_register_device(pxrc->input);
+}
+
+static int pxrc_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct pxrc *pxrc;
+ int retval;
+
+ pxrc = devm_kzalloc(&intf->dev, sizeof(*pxrc), GFP_KERNEL);
+ if (!pxrc)
+ return -ENOMEM;
+
+ mutex_init(&pxrc->pm_mutex);
+ pxrc->udev = usb_get_dev(interface_to_usbdev(intf));
+ pxrc->intf = intf;
+
+ retval = pxrc_usb_init(pxrc);
+ if (retval)
+ goto error;
+
+ retval = pxrc_input_init(pxrc);
+ if (retval)
+ goto err_free_urb;
+
+ return 0;
+
+err_free_urb:
+ usb_free_urb(pxrc->urb);
+
+error:
+ return retval;
+}
+
+static void pxrc_disconnect(struct usb_interface *intf)
+{
+ struct pxrc *pxrc = usb_get_intfdata(intf);
+
+ usb_free_urb(pxrc->urb);
+ usb_set_intfdata(intf, NULL);
+}
+
+static int pxrc_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct pxrc *pxrc = usb_get_intfdata(intf);
+
+ mutex_lock(&pxrc->pm_mutex);
+ if (pxrc->is_open)
+ usb_kill_urb(pxrc->urb);
+ mutex_unlock(&pxrc->pm_mutex);
+
+ return 0;
+}
+
+static int pxrc_resume(struct usb_interface *intf)
+{
+ struct pxrc *pxrc = usb_get_intfdata(intf);
+ int retval = 0;
+
+ mutex_lock(&pxrc->pm_mutex);
+ if (pxrc->is_open && usb_submit_urb(pxrc->urb, GFP_KERNEL) < 0)
+ retval = -EIO;
+
+ mutex_unlock(&pxrc->pm_mutex);
+ return retval;
+}
+
+static int pxrc_pre_reset(struct usb_interface *intf)
+{
+ struct pxrc *pxrc = usb_get_intfdata(intf);
+
+ mutex_lock(&pxrc->pm_mutex);
+ usb_kill_urb(pxrc->urb);
+ return 0;
+}
+
+static int pxrc_post_reset(struct usb_interface *intf)
+{
+ struct pxrc *pxrc = usb_get_intfdata(intf);
+ int retval = 0;
+
+ if (pxrc->is_open && usb_submit_urb(pxrc->urb, GFP_KERNEL) < 0)
+ retval = -EIO;
+
+ mutex_unlock(&pxrc->pm_mutex);
+
+ return retval;
+}
+
+static int pxrc_reset_resume(struct usb_interface *intf)
+{
+ return pxrc_resume(intf);
+}
+
+static struct usb_driver pxrc_driver = {
+ .name = "pxrc",
+ .probe = pxrc_probe,
+ .disconnect = pxrc_disconnect,
+ .id_table = pxrc_table,
+ .suspend = pxrc_suspend,
+ .resume = pxrc_resume,
+ .pre_reset = pxrc_pre_reset,
+ .post_reset = pxrc_post_reset,
+ .reset_resume = pxrc_reset_resume,
+};
+
+module_usb_driver(pxrc_driver);
+
+MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>");
+MODULE_DESCRIPTION("PhoenixRC Flight Controller Adapter");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index 9d2688f3f961..06e9650b3b30 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -227,6 +227,7 @@ static const struct xpad_device {
{ 0x0e6f, 0x021f, "Rock Candy Gamepad for Xbox 360", 0, XTYPE_XBOX360 },
{ 0x0e6f, 0x0246, "Rock Candy Gamepad for Xbox One 2015", 0, XTYPE_XBOXONE },
{ 0x0e6f, 0x02ab, "PDP Controller for Xbox One", 0, XTYPE_XBOXONE },
+ { 0x0e6f, 0x02a4, "PDP Wired Controller for Xbox One - Stealth Series", 0, XTYPE_XBOXONE },
{ 0x0e6f, 0x0301, "Logic3 Controller", 0, XTYPE_XBOX360 },
{ 0x0e6f, 0x0346, "Rock Candy Gamepad for Xbox One 2016", 0, XTYPE_XBOXONE },
{ 0x0e6f, 0x0401, "Logic3 Controller", 0, XTYPE_XBOX360 },
@@ -475,7 +476,8 @@ static const u8 xboxone_hori_init[] = {
/*
* This packet is required for some of the PDP pads to start
- * sending input reports. One of those pads is (0x0e6f:0x02ab).
+ * sending input reports. These pads include: (0x0e6f:0x02ab),
+ * (0x0e6f:0x02a4).
*/
static const u8 xboxone_pdp_init1[] = {
0x0a, 0x20, 0x00, 0x03, 0x00, 0x01, 0x14
@@ -483,7 +485,8 @@ static const u8 xboxone_pdp_init1[] = {
/*
* This packet is required for some of the PDP pads to start
- * sending input reports. One of those pads is (0x0e6f:0x02ab).
+ * sending input reports. These pads include: (0x0e6f:0x02ab),
+ * (0x0e6f:0x02a4).
*/
static const u8 xboxone_pdp_init2[] = {
0x06, 0x20, 0x00, 0x02, 0x01, 0x00
@@ -521,6 +524,8 @@ static const struct xboxone_init_packet xboxone_init_packets[] = {
XBOXONE_INIT_PKT(0x0000, 0x0000, xboxone_fw2015_init),
XBOXONE_INIT_PKT(0x0e6f, 0x02ab, xboxone_pdp_init1),
XBOXONE_INIT_PKT(0x0e6f, 0x02ab, xboxone_pdp_init2),
+ XBOXONE_INIT_PKT(0x0e6f, 0x02a4, xboxone_pdp_init1),
+ XBOXONE_INIT_PKT(0x0e6f, 0x02a4, xboxone_pdp_init2),
XBOXONE_INIT_PKT(0x24c6, 0x541a, xboxone_rumblebegin_init),
XBOXONE_INIT_PKT(0x24c6, 0x542a, xboxone_rumblebegin_init),
XBOXONE_INIT_PKT(0x24c6, 0x543a, xboxone_rumblebegin_init),
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 4c4ab1ced235..2b469cc47a78 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -157,15 +157,6 @@ config KEYBOARD_QT2160
This driver can also be built as a module. If so, the module
will be called qt2160.
-config KEYBOARD_BFIN
- tristate "Blackfin BF54x keypad support"
- depends on (BF54x && !BF544)
- help
- Say Y here if you want to use the BF54x keypad.
-
- To compile this driver as a module, choose M here: the
- module will be called bf54x-keys.
-
config KEYBOARD_CLPS711X
tristate "CLPS711X Keypad support"
depends on OF_GPIO && (ARCH_CLPS711X || COMPILE_TEST)
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 526e68294e6e..8fab920afa58 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -13,7 +13,6 @@ obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o
obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o
obj-$(CONFIG_KEYBOARD_BCM) += bcm-keypad.o
-obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
obj-$(CONFIG_KEYBOARD_CAP11XX) += cap11xx.o
obj-$(CONFIG_KEYBOARD_CLPS711X) += clps711x-keypad.o
obj-$(CONFIG_KEYBOARD_CROS_EC) += cros_ec_keyb.o
diff --git a/drivers/input/keyboard/bf54x-keys.c b/drivers/input/keyboard/bf54x-keys.c
deleted file mode 100644
index 8a07a426f88e..000000000000
--- a/drivers/input/keyboard/bf54x-keys.c
+++ /dev/null
@@ -1,396 +0,0 @@
-/*
- * File: drivers/input/keyboard/bf54x-keys.c
- * Based on:
- * Author: Michael Hennerich <hennerich@blackfin.uclinux.org>
- *
- * Created:
- * Description: keypad driver for Analog Devices Blackfin BF54x Processors
- *
- *
- * Modified:
- * Copyright 2007-2008 Analog Devices Inc.
- *
- * Bugs: Enter bugs at http://blackfin.uclinux.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, see the file COPYING, or write
- * to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <linux/module.h>
-
-#include <linux/fs.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/pm.h>
-#include <linux/sysctl.h>
-#include <linux/proc_fs.h>
-#include <linux/delay.h>
-#include <linux/platform_device.h>
-#include <linux/input.h>
-
-#include <asm/portmux.h>
-#include <mach/bf54x_keys.h>
-
-#define DRV_NAME "bf54x-keys"
-#define TIME_SCALE 100 /* 100 ns */
-#define MAX_MULT (0xFF * TIME_SCALE)
-#define MAX_RC 8 /* Max Row/Col */
-
-static const u16 per_rows[] = {
- P_KEY_ROW7,
- P_KEY_ROW6,
- P_KEY_ROW5,
- P_KEY_ROW4,
- P_KEY_ROW3,
- P_KEY_ROW2,
- P_KEY_ROW1,
- P_KEY_ROW0,
- 0
-};
-
-static const u16 per_cols[] = {
- P_KEY_COL7,
- P_KEY_COL6,
- P_KEY_COL5,
- P_KEY_COL4,
- P_KEY_COL3,
- P_KEY_COL2,
- P_KEY_COL1,
- P_KEY_COL0,
- 0
-};
-
-struct bf54x_kpad {
- struct input_dev *input;
- int irq;
- unsigned short lastkey;
- unsigned short *keycode;
- struct timer_list timer;
- unsigned int keyup_test_jiffies;
- unsigned short kpad_msel;
- unsigned short kpad_prescale;
- unsigned short kpad_ctl;
-};
-
-static inline int bfin_kpad_find_key(struct bf54x_kpad *bf54x_kpad,
- struct input_dev *input, u16 keyident)
-{
- u16 i;
-
- for (i = 0; i < input->keycodemax; i++)
- if (bf54x_kpad->keycode[i + input->keycodemax] == keyident)
- return bf54x_kpad->keycode[i];
- return -1;
-}
-
-static inline void bfin_keycodecpy(unsigned short *keycode,
- const unsigned int *pdata_kc,
- unsigned short keymapsize)
-{
- unsigned int i;
-
- for (i = 0; i < keymapsize; i++) {
- keycode[i] = pdata_kc[i] & 0xffff;
- keycode[i + keymapsize] = pdata_kc[i] >> 16;
- }
-}
-
-static inline u16 bfin_kpad_get_prescale(u32 timescale)
-{
- u32 sclk = get_sclk();
-
- return ((((sclk / 1000) * timescale) / 1024) - 1);
-}
-
-static inline u16 bfin_kpad_get_keypressed(struct bf54x_kpad *bf54x_kpad)
-{
- return (bfin_read_KPAD_STAT() & KPAD_PRESSED);
-}
-
-static inline void bfin_kpad_clear_irq(void)
-{
- bfin_write_KPAD_STAT(0xFFFF);
- bfin_write_KPAD_ROWCOL(0xFFFF);
-}
-
-static void bfin_kpad_timer(struct timer_list *t)
-{
- struct bf54x_kpad *bf54x_kpad = from_timer(bf54x_kpad, t, timer);
-
- if (bfin_kpad_get_keypressed(bf54x_kpad)) {
- /* Try again later */
- mod_timer(&bf54x_kpad->timer,
- jiffies + bf54x_kpad->keyup_test_jiffies);
- return;
- }
-
- input_report_key(bf54x_kpad->input, bf54x_kpad->lastkey, 0);
- input_sync(bf54x_kpad->input);
-
- /* Clear IRQ Status */
-
- bfin_kpad_clear_irq();
- enable_irq(bf54x_kpad->irq);
-}
-
-static irqreturn_t bfin_kpad_isr(int irq, void *dev_id)
-{
- struct platform_device *pdev = dev_id;
- struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
- struct input_dev *input = bf54x_kpad->input;
- int key;
- u16 rowcol = bfin_read_KPAD_ROWCOL();
-
- key = bfin_kpad_find_key(bf54x_kpad, input, rowcol);
-
- input_report_key(input, key, 1);
- input_sync(input);
-
- if (bfin_kpad_get_keypressed(bf54x_kpad)) {
- disable_irq_nosync(bf54x_kpad->irq);
- bf54x_kpad->lastkey = key;
- mod_timer(&bf54x_kpad->timer,
- jiffies + bf54x_kpad->keyup_test_jiffies);
- } else {
- input_report_key(input, key, 0);
- input_sync(input);
-
- bfin_kpad_clear_irq();
- }
-
- return IRQ_HANDLED;
-}
-
-static int bfin_kpad_probe(struct platform_device *pdev)
-{
- struct bf54x_kpad *bf54x_kpad;
- struct bfin_kpad_platform_data *pdata = dev_get_platdata(&pdev->dev);
- struct input_dev *input;
- int i, error;
-
- if (!pdata->rows || !pdata->cols || !pdata->keymap) {
- dev_err(&pdev->dev, "no rows, cols or keymap from pdata\n");
- return -EINVAL;
- }
-
- if (!pdata->keymapsize ||
- pdata->keymapsize > (pdata->rows * pdata->cols)) {
- dev_err(&pdev->dev, "invalid keymapsize\n");
- return -EINVAL;
- }
-
- bf54x_kpad = kzalloc(sizeof(struct bf54x_kpad), GFP_KERNEL);
- if (!bf54x_kpad)
- return -ENOMEM;
-
- platform_set_drvdata(pdev, bf54x_kpad);
-
- /* Allocate memory for keymap followed by private LUT */
- bf54x_kpad->keycode = kmalloc(pdata->keymapsize *
- sizeof(unsigned short) * 2, GFP_KERNEL);
- if (!bf54x_kpad->keycode) {
- error = -ENOMEM;
- goto out;
- }
-
- if (!pdata->debounce_time || pdata->debounce_time > MAX_MULT ||
- !pdata->coldrive_time || pdata->coldrive_time > MAX_MULT) {
- dev_warn(&pdev->dev,
- "invalid platform debounce/columndrive time\n");
- bfin_write_KPAD_MSEL(0xFF0); /* Default MSEL */
- } else {
- bfin_write_KPAD_MSEL(
- ((pdata->debounce_time / TIME_SCALE)
- & DBON_SCALE) |
- (((pdata->coldrive_time / TIME_SCALE) << 8)
- & COLDRV_SCALE));
-
- }
-
- if (!pdata->keyup_test_interval)
- bf54x_kpad->keyup_test_jiffies = msecs_to_jiffies(50);
- else
- bf54x_kpad->keyup_test_jiffies =
- msecs_to_jiffies(pdata->keyup_test_interval);
-
- if (peripheral_request_list((u16 *)&per_rows[MAX_RC - pdata->rows],
- DRV_NAME)) {
- dev_err(&pdev->dev, "requesting peripherals failed\n");
- error = -EFAULT;
- goto out0;
- }
-
- if (peripheral_request_list((u16 *)&per_cols[MAX_RC - pdata->cols],
- DRV_NAME)) {
- dev_err(&pdev->dev, "requesting peripherals failed\n");
- error = -EFAULT;
- goto out1;
- }
-
- bf54x_kpad->irq = platform_get_irq(pdev, 0);
- if (bf54x_kpad->irq < 0) {
- error = -ENODEV;
- goto out2;
- }
-
- error = request_irq(bf54x_kpad->irq, bfin_kpad_isr,
- 0, DRV_NAME, pdev);
- if (error) {
- dev_err(&pdev->dev, "unable to claim irq %d\n",
- bf54x_kpad->irq);
- goto out2;
- }
-
- input = input_allocate_device();
- if (!input) {
- error = -ENOMEM;
- goto out3;
- }
-
- bf54x_kpad->input = input;
-
- input->name = pdev->name;
- input->phys = "bf54x-keys/input0";
- input->dev.parent = &pdev->dev;
-
- input->id.bustype = BUS_HOST;
- input->id.vendor = 0x0001;
- input->id.product = 0x0001;
- input->id.version = 0x0100;
-
- input->keycodesize = sizeof(unsigned short);
- input->keycodemax = pdata->keymapsize;
- input->keycode = bf54x_kpad->keycode;
-
- bfin_keycodecpy(bf54x_kpad->keycode, pdata->keymap, pdata->keymapsize);
-
- /* setup input device */
- __set_bit(EV_KEY, input->evbit);
-
- if (pdata->repeat)
- __set_bit(EV_REP, input->evbit);
-
- for (i = 0; i < input->keycodemax; i++)
- if (bf54x_kpad->keycode[i] <= KEY_MAX)
- __set_bit(bf54x_kpad->keycode[i], input->keybit);
- __clear_bit(KEY_RESERVED, input->keybit);
-
- error = input_register_device(input);
- if (error) {
- dev_err(&pdev->dev, "unable to register input device\n");
- goto out4;
- }
-
- /* Init Keypad Key Up/Release test timer */
-
- timer_setup(&bf54x_kpad->timer, bfin_kpad_timer, 0);
-
- bfin_write_KPAD_PRESCALE(bfin_kpad_get_prescale(TIME_SCALE));
-
- bfin_write_KPAD_CTL((((pdata->cols - 1) << 13) & KPAD_COLEN) |
- (((pdata->rows - 1) << 10) & KPAD_ROWEN) |
- (2 & KPAD_IRQMODE));
-
- bfin_write_KPAD_CTL(bfin_read_KPAD_CTL() | KPAD_EN);
-
- device_init_wakeup(&pdev->dev, 1);
-
- return 0;
-
-out4:
- input_free_device(input);
-out3:
- free_irq(bf54x_kpad->irq, pdev);
-out2:
- peripheral_free_list((u16 *)&per_cols[MAX_RC - pdata->cols]);
-out1:
- peripheral_free_list((u16 *)&per_rows[MAX_RC - pdata->rows]);
-out0:
- kfree(bf54x_kpad->keycode);
-out:
- kfree(bf54x_kpad);
-
- return error;
-}
-
-static int bfin_kpad_remove(struct platform_device *pdev)
-{
- struct bfin_kpad_platform_data *pdata = dev_get_platdata(&pdev->dev);
- struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
-
- del_timer_sync(&bf54x_kpad->timer);
- free_irq(bf54x_kpad->irq, pdev);
-
- input_unregister_device(bf54x_kpad->input);
-
- peripheral_free_list((u16 *)&per_rows[MAX_RC - pdata->rows]);
- peripheral_free_list((u16 *)&per_cols[MAX_RC - pdata->cols]);
-
- kfree(bf54x_kpad->keycode);
- kfree(bf54x_kpad);
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int bfin_kpad_suspend(struct platform_device *pdev, pm_message_t state)
-{
- struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
-
- bf54x_kpad->kpad_msel = bfin_read_KPAD_MSEL();
- bf54x_kpad->kpad_prescale = bfin_read_KPAD_PRESCALE();
- bf54x_kpad->kpad_ctl = bfin_read_KPAD_CTL();
-
- if (device_may_wakeup(&pdev->dev))
- enable_irq_wake(bf54x_kpad->irq);
-
- return 0;
-}
-
-static int bfin_kpad_resume(struct platform_device *pdev)
-{
- struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
-
- bfin_write_KPAD_MSEL(bf54x_kpad->kpad_msel);
- bfin_write_KPAD_PRESCALE(bf54x_kpad->kpad_prescale);
- bfin_write_KPAD_CTL(bf54x_kpad->kpad_ctl);
-
- if (device_may_wakeup(&pdev->dev))
- disable_irq_wake(bf54x_kpad->irq);
-
- return 0;
-}
-#else
-# define bfin_kpad_suspend NULL
-# define bfin_kpad_resume NULL
-#endif
-
-static struct platform_driver bfin_kpad_device_driver = {
- .driver = {
- .name = DRV_NAME,
- },
- .probe = bfin_kpad_probe,
- .remove = bfin_kpad_remove,
- .suspend = bfin_kpad_suspend,
- .resume = bfin_kpad_resume,
-};
-module_platform_driver(bfin_kpad_device_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
-MODULE_DESCRIPTION("Keypad driver for BF54x Processors");
-MODULE_ALIAS("platform:bf54x-keys");
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index 87e613dc33b8..052e37675086 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -30,6 +30,7 @@
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/spinlock.h>
+#include <dt-bindings/input/gpio-keys.h>
struct gpio_button_data {
const struct gpio_keys_button *button;
@@ -45,6 +46,7 @@ struct gpio_button_data {
unsigned int software_debounce; /* in msecs, for GPIO-driven buttons */
unsigned int irq;
+ unsigned int wakeup_trigger_type;
spinlock_t lock;
bool disabled;
bool key_pressed;
@@ -540,6 +542,8 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
}
if (bdata->gpiod) {
+ bool active_low = gpiod_is_active_low(bdata->gpiod);
+
if (button->debounce_interval) {
error = gpiod_set_debounce(bdata->gpiod,
button->debounce_interval * 1000);
@@ -568,6 +572,24 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
isr = gpio_keys_gpio_isr;
irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
+ switch (button->wakeup_event_action) {
+ case EV_ACT_ASSERTED:
+ bdata->wakeup_trigger_type = active_low ?
+ IRQ_TYPE_EDGE_FALLING : IRQ_TYPE_EDGE_RISING;
+ break;
+ case EV_ACT_DEASSERTED:
+ bdata->wakeup_trigger_type = active_low ?
+ IRQ_TYPE_EDGE_RISING : IRQ_TYPE_EDGE_FALLING;
+ break;
+ case EV_ACT_ANY:
+ /* fall through */
+ default:
+ /*
+ * For other cases, we are OK letting suspend/resume
+ * not reconfigure the trigger type.
+ */
+ break;
+ }
} else {
if (!button->irq) {
dev_err(dev, "Found button without gpio or irq\n");
@@ -586,6 +608,11 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
isr = gpio_keys_irq_isr;
irqflags = 0;
+
+ /*
+ * For IRQ buttons, there is no interrupt for release.
+ * So we don't need to reconfigure the trigger type for wakeup.
+ */
}
bdata->code = &ddata->keymap[idx];
@@ -718,6 +745,9 @@ gpio_keys_get_devtree_pdata(struct device *dev)
/* legacy name */
fwnode_property_read_bool(child, "gpio-key,wakeup");
+ fwnode_property_read_u32(child, "wakeup-event-action",
+ &button->wakeup_event_action);
+
button->can_disable =
fwnode_property_read_bool(child, "linux,can-disable");
@@ -845,19 +875,112 @@ static int gpio_keys_probe(struct platform_device *pdev)
return 0;
}
+static int __maybe_unused
+gpio_keys_button_enable_wakeup(struct gpio_button_data *bdata)
+{
+ int error;
+
+ error = enable_irq_wake(bdata->irq);
+ if (error) {
+ dev_err(bdata->input->dev.parent,
+ "failed to configure IRQ %d as wakeup source: %d\n",
+ bdata->irq, error);
+ return error;
+ }
+
+ if (bdata->wakeup_trigger_type) {
+ error = irq_set_irq_type(bdata->irq,
+ bdata->wakeup_trigger_type);
+ if (error) {
+ dev_err(bdata->input->dev.parent,
+ "failed to set wakeup trigger %08x for IRQ %d: %d\n",
+ bdata->wakeup_trigger_type, bdata->irq, error);
+ disable_irq_wake(bdata->irq);
+ return error;
+ }
+ }
+
+ return 0;
+}
+
+static void __maybe_unused
+gpio_keys_button_disable_wakeup(struct gpio_button_data *bdata)
+{
+ int error;
+
+ /*
+ * The trigger type is always both edges for gpio-based keys and we do
+ * not support changing wakeup trigger for interrupt-based keys.
+ */
+ if (bdata->wakeup_trigger_type) {
+ error = irq_set_irq_type(bdata->irq, IRQ_TYPE_EDGE_BOTH);
+ if (error)
+ dev_warn(bdata->input->dev.parent,
+ "failed to restore interrupt trigger for IRQ %d: %d\n",
+ bdata->irq, error);
+ }
+
+ error = disable_irq_wake(bdata->irq);
+ if (error)
+ dev_warn(bdata->input->dev.parent,
+ "failed to disable IRQ %d as wake source: %d\n",
+ bdata->irq, error);
+}
+
+static int __maybe_unused
+gpio_keys_enable_wakeup(struct gpio_keys_drvdata *ddata)
+{
+ struct gpio_button_data *bdata;
+ int error;
+ int i;
+
+ for (i = 0; i < ddata->pdata->nbuttons; i++) {
+ bdata = &ddata->data[i];
+ if (bdata->button->wakeup) {
+ error = gpio_keys_button_enable_wakeup(bdata);
+ if (error)
+ goto err_out;
+ }
+ bdata->suspended = true;
+ }
+
+ return 0;
+
+err_out:
+ while (i--) {
+ bdata = &ddata->data[i];
+ if (bdata->button->wakeup)
+ gpio_keys_button_disable_wakeup(bdata);
+ bdata->suspended = false;
+ }
+
+ return error;
+}
+
+static void __maybe_unused
+gpio_keys_disable_wakeup(struct gpio_keys_drvdata *ddata)
+{
+ struct gpio_button_data *bdata;
+ int i;
+
+ for (i = 0; i < ddata->pdata->nbuttons; i++) {
+ bdata = &ddata->data[i];
+ bdata->suspended = false;
+ if (irqd_is_wakeup_set(irq_get_irq_data(bdata->irq)))
+ gpio_keys_button_disable_wakeup(bdata);
+ }
+}
+
static int __maybe_unused gpio_keys_suspend(struct device *dev)
{
struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
struct input_dev *input = ddata->input;
- int i;
+ int error;
if (device_may_wakeup(dev)) {
- for (i = 0; i < ddata->pdata->nbuttons; i++) {
- struct gpio_button_data *bdata = &ddata->data[i];
- if (bdata->button->wakeup)
- enable_irq_wake(bdata->irq);
- bdata->suspended = true;
- }
+ error = gpio_keys_enable_wakeup(ddata);
+ if (error)
+ return error;
} else {
mutex_lock(&input->mutex);
if (input->users)
@@ -873,15 +996,9 @@ static int __maybe_unused gpio_keys_resume(struct device *dev)
struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
struct input_dev *input = ddata->input;
int error = 0;
- int i;
if (device_may_wakeup(dev)) {
- for (i = 0; i < ddata->pdata->nbuttons; i++) {
- struct gpio_button_data *bdata = &ddata->data[i];
- if (bdata->button->wakeup)
- disable_irq_wake(bdata->irq);
- bdata->suspended = false;
- }
+ gpio_keys_disable_wakeup(ddata);
} else {
mutex_lock(&input->mutex);
if (input->users)
diff --git a/drivers/input/keyboard/stmpe-keypad.c b/drivers/input/keyboard/stmpe-keypad.c
index 8c6c0b9109c7..d69e631cfa0a 100644
--- a/drivers/input/keyboard/stmpe-keypad.c
+++ b/drivers/input/keyboard/stmpe-keypad.c
@@ -48,6 +48,14 @@
#define STMPE_KEYPAD_KEYMAP_MAX_SIZE \
(STMPE_KEYPAD_MAX_ROWS * STMPE_KEYPAD_MAX_COLS)
+
+#define STMPE1601_NUM_DATA 5
+#define STMPE2401_NUM_DATA 3
+#define STMPE2403_NUM_DATA 5
+
+/* Make sure it covers all cases above */
+#define MAX_NUM_DATA 5
+
/**
* struct stmpe_keypad_variant - model-specific attributes
* @auto_increment: whether the KPC_DATA_BYTE register address
@@ -74,7 +82,7 @@ struct stmpe_keypad_variant {
static const struct stmpe_keypad_variant stmpe_keypad_variants[] = {
[STMPE1601] = {
.auto_increment = true,
- .num_data = 5,
+ .num_data = STMPE1601_NUM_DATA,
.num_normal_data = 3,
.max_cols = 8,
.max_rows = 8,
@@ -84,7 +92,7 @@ static const struct stmpe_keypad_variant stmpe_keypad_variants[] = {
[STMPE2401] = {
.auto_increment = false,
.set_pullup = true,
- .num_data = 3,
+ .num_data = STMPE2401_NUM_DATA,
.num_normal_data = 2,
.max_cols = 8,
.max_rows = 12,
@@ -94,7 +102,7 @@ static const struct stmpe_keypad_variant stmpe_keypad_variants[] = {
[STMPE2403] = {
.auto_increment = true,
.set_pullup = true,
- .num_data = 5,
+ .num_data = STMPE2403_NUM_DATA,
.num_normal_data = 3,
.max_cols = 8,
.max_rows = 12,
@@ -156,7 +164,7 @@ static irqreturn_t stmpe_keypad_irq(int irq, void *dev)
struct stmpe_keypad *keypad = dev;
struct input_dev *input = keypad->input;
const struct stmpe_keypad_variant *variant = keypad->variant;
- u8 fifo[variant->num_data];
+ u8 fifo[MAX_NUM_DATA];
int ret;
int i;
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 62a1312a7387..572b15fa18c2 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -655,15 +655,6 @@ config INPUT_DM355EVM
To compile this driver as a module, choose M here: the
module will be called dm355evm_keys.
-config INPUT_BFIN_ROTARY
- tristate "Blackfin Rotary support"
- depends on BF54x || BF52x
- help
- Say Y here if you want to use the Blackfin Rotary.
-
- To compile this driver as a module, choose M here: the
- module will be called bfin-rotary.
-
config INPUT_WM831X_ON
tristate "WM831X ON pin"
depends on MFD_WM831X
@@ -841,4 +832,13 @@ config INPUT_HISI_POWERKEY
To compile this driver as a module, choose M here: the
module will be called hisi_powerkey.
+config INPUT_RAVE_SP_PWRBUTTON
+ tristate "RAVE SP Power button Driver"
+ depends on RAVE_SP_CORE
+ help
+ Say Y here if you want to enable power key reporting from RAVE SP
+
+ To compile this driver as a module, choose M here: the
+ module will be called rave-sp-pwrbutton.
+
endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index a8f61af865aa..72cde28649e2 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -19,7 +19,6 @@ obj-$(CONFIG_INPUT_ARIZONA_HAPTICS) += arizona-haptics.o
obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
obj-$(CONFIG_INPUT_ATMEL_CAPTOUCH) += atmel_captouch.o
-obj-$(CONFIG_INPUT_BFIN_ROTARY) += bfin_rotary.o
obj-$(CONFIG_INPUT_BMA150) += bma150.o
obj-$(CONFIG_INPUT_CM109) += cm109.o
obj-$(CONFIG_INPUT_CMA3000) += cma3000_d0x.o
@@ -60,6 +59,7 @@ obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o
obj-$(CONFIG_INPUT_POWERMATE) += powermate.o
obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.o
obj-$(CONFIG_INPUT_PWM_VIBRA) += pwm-vibra.o
+obj-$(CONFIG_INPUT_RAVE_SP_PWRBUTTON) += rave-sp-pwrbutton.o
obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o
obj-$(CONFIG_INPUT_REGULATOR_HAPTIC) += regulator-haptic.o
obj-$(CONFIG_INPUT_RETU_PWRBUTTON) += retu-pwrbutton.o
diff --git a/drivers/input/misc/bfin_rotary.c b/drivers/input/misc/bfin_rotary.c
deleted file mode 100644
index 799ce3d2820e..000000000000
--- a/drivers/input/misc/bfin_rotary.c
+++ /dev/null
@@ -1,294 +0,0 @@
-/*
- * Rotary counter driver for Analog Devices Blackfin Processors
- *
- * Copyright 2008-2009 Analog Devices Inc.
- * Licensed under the GPL-2 or later.
- */
-
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/pm.h>
-#include <linux/platform_device.h>
-#include <linux/input.h>
-#include <linux/slab.h>
-#include <linux/platform_data/bfin_rotary.h>
-
-#include <asm/portmux.h>
-
-#define CNT_CONFIG_OFF 0 /* CNT Config Offset */
-#define CNT_IMASK_OFF 4 /* CNT Interrupt Mask Offset */
-#define CNT_STATUS_OFF 8 /* CNT Status Offset */
-#define CNT_COMMAND_OFF 12 /* CNT Command Offset */
-#define CNT_DEBOUNCE_OFF 16 /* CNT Debounce Offset */
-#define CNT_COUNTER_OFF 20 /* CNT Counter Offset */
-#define CNT_MAX_OFF 24 /* CNT Maximum Count Offset */
-#define CNT_MIN_OFF 28 /* CNT Minimum Count Offset */
-
-struct bfin_rot {
- struct input_dev *input;
- void __iomem *base;
- int irq;
- unsigned int up_key;
- unsigned int down_key;
- unsigned int button_key;
- unsigned int rel_code;
-
- unsigned short mode;
- unsigned short debounce;
-
- unsigned short cnt_config;
- unsigned short cnt_imask;
- unsigned short cnt_debounce;
-};
-
-static void report_key_event(struct input_dev *input, int keycode)
-{
- /* simulate a press-n-release */
- input_report_key(input, keycode, 1);
- input_sync(input);
- input_report_key(input, keycode, 0);
- input_sync(input);
-}
-
-static void report_rotary_event(struct bfin_rot *rotary, int delta)
-{
- struct input_dev *input = rotary->input;
-
- if (rotary->up_key) {
- report_key_event(input,
- delta > 0 ? rotary->up_key : rotary->down_key);
- } else {
- input_report_rel(input, rotary->rel_code, delta);
- input_sync(input);
- }
-}
-
-static irqreturn_t bfin_rotary_isr(int irq, void *dev_id)
-{
- struct bfin_rot *rotary = dev_id;
- int delta;
-
- switch (readw(rotary->base + CNT_STATUS_OFF)) {
-
- case ICII:
- break;
-
- case UCII:
- case DCII:
- delta = readl(rotary->base + CNT_COUNTER_OFF);
- if (delta)
- report_rotary_event(rotary, delta);
- break;
-
- case CZMII:
- report_key_event(rotary->input, rotary->button_key);
- break;
-
- default:
- break;
- }
-
- writew(W1LCNT_ZERO, rotary->base + CNT_COMMAND_OFF); /* Clear COUNTER */
- writew(-1, rotary->base + CNT_STATUS_OFF); /* Clear STATUS */
-
- return IRQ_HANDLED;
-}
-
-static int bfin_rotary_open(struct input_dev *input)
-{
- struct bfin_rot *rotary = input_get_drvdata(input);
- unsigned short val;
-
- if (rotary->mode & ROT_DEBE)
- writew(rotary->debounce & DPRESCALE,
- rotary->base + CNT_DEBOUNCE_OFF);
-
- writew(rotary->mode & ~CNTE, rotary->base + CNT_CONFIG_OFF);
-
- val = UCIE | DCIE;
- if (rotary->button_key)
- val |= CZMIE;
- writew(val, rotary->base + CNT_IMASK_OFF);
-
- writew(rotary->mode | CNTE, rotary->base + CNT_CONFIG_OFF);
-
- return 0;
-}
-
-static void bfin_rotary_close(struct input_dev *input)
-{
- struct bfin_rot *rotary = input_get_drvdata(input);
-
- writew(0, rotary->base + CNT_CONFIG_OFF);
- writew(0, rotary->base + CNT_IMASK_OFF);
-}
-
-static void bfin_rotary_free_action(void *data)
-{
- peripheral_free_list(data);
-}
-
-static int bfin_rotary_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- const struct bfin_rotary_platform_data *pdata = dev_get_platdata(dev);
- struct bfin_rot *rotary;
- struct resource *res;
- struct input_dev *input;
- int error;
-
- /* Basic validation */
- if ((pdata->rotary_up_key && !pdata->rotary_down_key) ||
- (!pdata->rotary_up_key && pdata->rotary_down_key)) {
- return -EINVAL;
- }
-
- if (pdata->pin_list) {
- error = peripheral_request_list(pdata->pin_list,
- dev_name(dev));
- if (error) {
- dev_err(dev, "requesting peripherals failed: %d\n",
- error);
- return error;
- }
-
- error = devm_add_action_or_reset(dev, bfin_rotary_free_action,
- pdata->pin_list);
- if (error) {
- dev_err(dev, "setting cleanup action failed: %d\n",
- error);
- return error;
- }
- }
-
- rotary = devm_kzalloc(dev, sizeof(struct bfin_rot), GFP_KERNEL);
- if (!rotary)
- return -ENOMEM;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- rotary->base = devm_ioremap_resource(dev, res);
- if (IS_ERR(rotary->base))
- return PTR_ERR(rotary->base);
-
- input = devm_input_allocate_device(dev);
- if (!input)
- return -ENOMEM;
-
- rotary->input = input;
-
- rotary->up_key = pdata->rotary_up_key;
- rotary->down_key = pdata->rotary_down_key;
- rotary->button_key = pdata->rotary_button_key;
- rotary->rel_code = pdata->rotary_rel_code;
-
- rotary->mode = pdata->mode;
- rotary->debounce = pdata->debounce;
-
- input->name = pdev->name;
- input->phys = "bfin-rotary/input0";
- input->dev.parent = dev;
-
- input_set_drvdata(input, rotary);
-
- input->id.bustype = BUS_HOST;
- input->id.vendor = 0x0001;
- input->id.product = 0x0001;
- input->id.version = 0x0100;
-
- input->open = bfin_rotary_open;
- input->close = bfin_rotary_close;
-
- if (rotary->up_key) {
- __set_bit(EV_KEY, input->evbit);
- __set_bit(rotary->up_key, input->keybit);
- __set_bit(rotary->down_key, input->keybit);
- } else {
- __set_bit(EV_REL, input->evbit);
- __set_bit(rotary->rel_code, input->relbit);
- }
-
- if (rotary->button_key) {
- __set_bit(EV_KEY, input->evbit);
- __set_bit(rotary->button_key, input->keybit);
- }
-
- /* Quiesce the device before requesting irq */
- bfin_rotary_close(input);
-
- rotary->irq = platform_get_irq(pdev, 0);
- if (rotary->irq < 0) {
- dev_err(dev, "No rotary IRQ specified\n");
- return -ENOENT;
- }
-
- error = devm_request_irq(dev, rotary->irq, bfin_rotary_isr,
- 0, dev_name(dev), rotary);
- if (error) {
- dev_err(dev, "unable to claim irq %d; error %d\n",
- rotary->irq, error);
- return error;
- }
-
- error = input_register_device(input);
- if (error) {
- dev_err(dev, "unable to register input device (%d)\n", error);
- return error;
- }
-
- platform_set_drvdata(pdev, rotary);
- device_init_wakeup(dev, 1);
-
- return 0;
-}
-
-static int __maybe_unused bfin_rotary_suspend(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct bfin_rot *rotary = platform_get_drvdata(pdev);
-
- rotary->cnt_config = readw(rotary->base + CNT_CONFIG_OFF);
- rotary->cnt_imask = readw(rotary->base + CNT_IMASK_OFF);
- rotary->cnt_debounce = readw(rotary->base + CNT_DEBOUNCE_OFF);
-
- if (device_may_wakeup(&pdev->dev))
- enable_irq_wake(rotary->irq);
-
- return 0;
-}
-
-static int __maybe_unused bfin_rotary_resume(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct bfin_rot *rotary = platform_get_drvdata(pdev);
-
- writew(rotary->cnt_debounce, rotary->base + CNT_DEBOUNCE_OFF);
- writew(rotary->cnt_imask, rotary->base + CNT_IMASK_OFF);
- writew(rotary->cnt_config & ~CNTE, rotary->base + CNT_CONFIG_OFF);
-
- if (device_may_wakeup(&pdev->dev))
- disable_irq_wake(rotary->irq);
-
- if (rotary->cnt_config & CNTE)
- writew(rotary->cnt_config, rotary->base + CNT_CONFIG_OFF);
-
- return 0;
-}
-
-static SIMPLE_DEV_PM_OPS(bfin_rotary_pm_ops,
- bfin_rotary_suspend, bfin_rotary_resume);
-
-static struct platform_driver bfin_rotary_device_driver = {
- .probe = bfin_rotary_probe,
- .driver = {
- .name = "bfin-rotary",
- .pm = &bfin_rotary_pm_ops,
- },
-};
-module_platform_driver(bfin_rotary_device_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
-MODULE_DESCRIPTION("Rotary Counter driver for Blackfin Processors");
-MODULE_ALIAS("platform:bfin-rotary");
diff --git a/drivers/input/misc/rave-sp-pwrbutton.c b/drivers/input/misc/rave-sp-pwrbutton.c
new file mode 100644
index 000000000000..bcab3cdb7ebd
--- /dev/null
+++ b/drivers/input/misc/rave-sp-pwrbutton.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Power Button driver for RAVE SP
+//
+// Copyright (C) 2017 Zodiac Inflight Innovations
+//
+//
+
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mfd/rave-sp.h>
+#include <linux/platform_device.h>
+
+#define RAVE_SP_EVNT_BUTTON_PRESS (RAVE_SP_EVNT_BASE + 0x00)
+
+struct rave_sp_power_button {
+ struct input_dev *idev;
+ struct notifier_block nb;
+};
+
+static int rave_sp_power_button_event(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct rave_sp_power_button *pb =
+ container_of(nb, struct rave_sp_power_button, nb);
+ const u8 event = rave_sp_action_unpack_event(action);
+ const u8 value = rave_sp_action_unpack_value(action);
+ struct input_dev *idev = pb->idev;
+
+ if (event == RAVE_SP_EVNT_BUTTON_PRESS) {
+ input_report_key(idev, KEY_POWER, value);
+ input_sync(idev);
+
+ return NOTIFY_STOP;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int rave_sp_pwrbutton_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rave_sp_power_button *pb;
+ struct input_dev *idev;
+ int error;
+
+ pb = devm_kzalloc(dev, sizeof(*pb), GFP_KERNEL);
+ if (!pb)
+ return -ENOMEM;
+
+ idev = devm_input_allocate_device(dev);
+ if (!idev)
+ return -ENOMEM;
+
+ idev->name = pdev->name;
+
+ input_set_capability(idev, EV_KEY, KEY_POWER);
+
+ error = input_register_device(idev);
+ if (error)
+ return error;
+
+ pb->idev = idev;
+ pb->nb.notifier_call = rave_sp_power_button_event;
+ pb->nb.priority = 128;
+
+ error = devm_rave_sp_register_event_notifier(dev, &pb->nb);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+static const struct of_device_id rave_sp_pwrbutton_of_match[] = {
+ { .compatible = "zii,rave-sp-pwrbutton" },
+ {}
+};
+
+static struct platform_driver rave_sp_pwrbutton_driver = {
+ .probe = rave_sp_pwrbutton_probe,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = rave_sp_pwrbutton_of_match,
+ },
+};
+module_platform_driver(rave_sp_pwrbutton_driver);
+
+MODULE_DEVICE_TABLE(of, rave_sp_pwrbutton_of_match);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Andrey Vostrikov <andrey.vostrikov@cogentembedded.com>");
+MODULE_AUTHOR("Nikita Yushchenko <nikita.yoush@cogentembedded.com>");
+MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
+MODULE_DESCRIPTION("RAVE SP Power Button driver");
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
index dbe57da8c1a1..0a67f235ba88 100644
--- a/drivers/input/mouse/alps.c
+++ b/drivers/input/mouse/alps.c
@@ -139,11 +139,11 @@ static const struct alps_model_info alps_model_data[] = {
};
static const struct alps_protocol_info alps_v3_protocol_data = {
- ALPS_PROTO_V3, 0x8f, 0x8f, ALPS_DUALPOINT
+ ALPS_PROTO_V3, 0x8f, 0x8f, ALPS_DUALPOINT | ALPS_DUALPOINT_WITH_PRESSURE
};
static const struct alps_protocol_info alps_v3_rushmore_data = {
- ALPS_PROTO_V3_RUSHMORE, 0x8f, 0x8f, ALPS_DUALPOINT
+ ALPS_PROTO_V3_RUSHMORE, 0x8f, 0x8f, ALPS_DUALPOINT | ALPS_DUALPOINT_WITH_PRESSURE
};
static const struct alps_protocol_info alps_v4_protocol_data = {
@@ -155,7 +155,7 @@ static const struct alps_protocol_info alps_v5_protocol_data = {
};
static const struct alps_protocol_info alps_v7_protocol_data = {
- ALPS_PROTO_V7, 0x48, 0x48, ALPS_DUALPOINT
+ ALPS_PROTO_V7, 0x48, 0x48, ALPS_DUALPOINT | ALPS_DUALPOINT_WITH_PRESSURE
};
static const struct alps_protocol_info alps_v8_protocol_data = {
@@ -583,7 +583,7 @@ static void alps_process_trackstick_packet_v3(struct psmouse *psmouse)
x = (s8)(((packet[0] & 0x20) << 2) | (packet[1] & 0x7f));
y = (s8)(((packet[0] & 0x10) << 3) | (packet[2] & 0x7f));
- z = (packet[4] & 0x7c) >> 2;
+ z = packet[4] & 0x7c;
/*
* The x and y values tend to be quite large, and when used
@@ -595,6 +595,7 @@ static void alps_process_trackstick_packet_v3(struct psmouse *psmouse)
input_report_rel(dev, REL_X, x);
input_report_rel(dev, REL_Y, -y);
+ input_report_abs(dev, ABS_PRESSURE, z);
/*
* Most ALPS models report the trackstick buttons in the touchpad
@@ -827,7 +828,7 @@ static void alps_process_packet_v6(struct psmouse *psmouse)
unsigned char *packet = psmouse->packet;
struct input_dev *dev = psmouse->dev;
struct input_dev *dev2 = priv->dev2;
- int x, y, z, left, right, middle;
+ int x, y, z;
/*
* We can use Byte5 to distinguish if the packet is from Touchpad
@@ -847,9 +848,6 @@ static void alps_process_packet_v6(struct psmouse *psmouse)
x = packet[1] | ((packet[3] & 0x20) << 2);
y = packet[2] | ((packet[3] & 0x40) << 1);
z = packet[4];
- left = packet[3] & 0x01;
- right = packet[3] & 0x02;
- middle = packet[3] & 0x04;
/* To prevent the cursor jump when finger lifted */
if (x == 0x7F && y == 0x7F && z == 0x7F)
@@ -859,9 +857,7 @@ static void alps_process_packet_v6(struct psmouse *psmouse)
input_report_rel(dev2, REL_X, (char)x / 4);
input_report_rel(dev2, REL_Y, -((char)y / 4));
- input_report_key(dev2, BTN_LEFT, left);
- input_report_key(dev2, BTN_RIGHT, right);
- input_report_key(dev2, BTN_MIDDLE, middle);
+ psmouse_report_standard_buttons(dev2, packet[3]);
input_sync(dev2);
return;
@@ -871,8 +867,6 @@ static void alps_process_packet_v6(struct psmouse *psmouse)
x = packet[1] | ((packet[3] & 0x78) << 4);
y = packet[2] | ((packet[4] & 0x78) << 4);
z = packet[5];
- left = packet[3] & 0x01;
- right = packet[3] & 0x02;
if (z > 30)
input_report_key(dev, BTN_TOUCH, 1);
@@ -888,8 +882,8 @@ static void alps_process_packet_v6(struct psmouse *psmouse)
input_report_key(dev, BTN_TOOL_FINGER, z > 0);
/* v6 touchpad does not have middle button */
- input_report_key(dev, BTN_LEFT, left);
- input_report_key(dev, BTN_RIGHT, right);
+ packet[3] &= ~BIT(2);
+ psmouse_report_standard_buttons(dev2, packet[3]);
input_sync(dev);
}
@@ -1098,7 +1092,7 @@ static void alps_process_trackstick_packet_v7(struct psmouse *psmouse)
struct alps_data *priv = psmouse->private;
unsigned char *packet = psmouse->packet;
struct input_dev *dev2 = priv->dev2;
- int x, y, z, left, right, middle;
+ int x, y, z;
/* It should be a DualPoint when received trackstick packet */
if (!(priv->flags & ALPS_DUALPOINT)) {
@@ -1112,16 +1106,11 @@ static void alps_process_trackstick_packet_v7(struct psmouse *psmouse)
((packet[3] & 0x20) << 1);
z = (packet[5] & 0x3f) | ((packet[3] & 0x80) >> 1);
- left = (packet[1] & 0x01);
- right = (packet[1] & 0x02) >> 1;
- middle = (packet[1] & 0x04) >> 2;
-
input_report_rel(dev2, REL_X, (char)x);
input_report_rel(dev2, REL_Y, -((char)y));
+ input_report_abs(dev2, ABS_PRESSURE, z);
- input_report_key(dev2, BTN_LEFT, left);
- input_report_key(dev2, BTN_RIGHT, right);
- input_report_key(dev2, BTN_MIDDLE, middle);
+ psmouse_report_standard_buttons(dev2, packet[1]);
input_sync(dev2);
}
@@ -1503,10 +1492,7 @@ static void alps_report_bare_ps2_packet(struct psmouse *psmouse,
alps_report_buttons(dev, dev2,
packet[0] & 1, packet[0] & 2, packet[0] & 4);
- input_report_rel(dev, REL_X,
- packet[1] ? packet[1] - ((packet[0] << 4) & 0x100) : 0);
- input_report_rel(dev, REL_Y,
- packet[2] ? ((packet[0] << 3) & 0x100) - packet[2] : 0);
+ psmouse_report_standard_motion(dev, packet);
input_sync(dev);
}
@@ -2544,13 +2530,31 @@ static int alps_update_btn_info_ss4_v2(unsigned char otp[][4],
}
static int alps_update_dual_info_ss4_v2(unsigned char otp[][4],
- struct alps_data *priv)
+ struct alps_data *priv,
+ struct psmouse *psmouse)
{
bool is_dual = false;
+ int reg_val = 0;
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
- if (IS_SS4PLUS_DEV(priv->dev_id))
+ if (IS_SS4PLUS_DEV(priv->dev_id)) {
is_dual = (otp[0][0] >> 4) & 0x01;
+ if (!is_dual) {
+ /* For support TrackStick of Thinkpad L/E series */
+ if (alps_exit_command_mode(psmouse) == 0 &&
+ alps_enter_command_mode(psmouse) == 0) {
+ reg_val = alps_command_mode_read_reg(psmouse,
+ 0xD7);
+ }
+ alps_exit_command_mode(psmouse);
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
+
+ if (reg_val == 0x0C || reg_val == 0x1D)
+ is_dual = true;
+ }
+ }
+
if (is_dual)
priv->flags |= ALPS_DUALPOINT |
ALPS_DUALPOINT_WITH_PRESSURE;
@@ -2573,7 +2577,7 @@ static int alps_set_defaults_ss4_v2(struct psmouse *psmouse,
alps_update_btn_info_ss4_v2(otp, priv);
- alps_update_dual_info_ss4_v2(otp, priv);
+ alps_update_dual_info_ss4_v2(otp, priv, psmouse);
return 0;
}
diff --git a/drivers/input/mouse/appletouch.c b/drivers/input/mouse/appletouch.c
index 81a695d0b4e0..032d27983b6c 100644
--- a/drivers/input/mouse/appletouch.c
+++ b/drivers/input/mouse/appletouch.c
@@ -587,7 +587,7 @@ static void atp_complete_geyser_1_2(struct urb *urb)
/* Perform size detection, if not done already */
if (unlikely(!dev->size_detect_done)) {
atp_detect_size(dev);
- dev->size_detect_done = 1;
+ dev->size_detect_done = true;
goto exit;
}
}
@@ -813,7 +813,7 @@ static int atp_open(struct input_dev *input)
if (usb_submit_urb(dev->urb, GFP_ATOMIC))
return -EIO;
- dev->open = 1;
+ dev->open = true;
return 0;
}
@@ -823,7 +823,7 @@ static void atp_close(struct input_dev *input)
usb_kill_urb(dev->urb);
cancel_work_sync(&dev->work);
- dev->open = 0;
+ dev->open = false;
}
static int atp_handle_geyser(struct atp *dev)
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index a4aaa748e987..db47a5e1d114 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -35,7 +35,7 @@
static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c,
unsigned char *param)
{
- if (psmouse_sliced_command(psmouse, c) ||
+ if (ps2_sliced_command(&psmouse->ps2dev, c) ||
ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) {
psmouse_err(psmouse, "%s query 0x%02x failed.\n", __func__, c);
return -1;
@@ -107,8 +107,8 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
switch (etd->hw_version) {
case 1:
- if (psmouse_sliced_command(psmouse, ETP_REGISTER_READ) ||
- psmouse_sliced_command(psmouse, reg) ||
+ if (ps2_sliced_command(&psmouse->ps2dev, ETP_REGISTER_READ) ||
+ ps2_sliced_command(&psmouse->ps2dev, reg) ||
ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) {
rc = -1;
}
@@ -162,9 +162,9 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,
switch (etd->hw_version) {
case 1:
- if (psmouse_sliced_command(psmouse, ETP_REGISTER_WRITE) ||
- psmouse_sliced_command(psmouse, reg) ||
- psmouse_sliced_command(psmouse, val) ||
+ if (ps2_sliced_command(&psmouse->ps2dev, ETP_REGISTER_WRITE) ||
+ ps2_sliced_command(&psmouse->ps2dev, reg) ||
+ ps2_sliced_command(&psmouse->ps2dev, val) ||
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) {
rc = -1;
}
@@ -279,8 +279,8 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)
input_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
- input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
- input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
+
+ psmouse_report_standard_buttons(dev, packet[0]);
if (etd->fw_version < 0x020000 &&
(etd->capabilities[0] & ETP_CAP_HAS_ROCKER)) {
@@ -390,8 +390,7 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4);
- input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
- input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
+ psmouse_report_standard_buttons(dev, packet[0]);
if (etd->reports_pressure) {
input_report_abs(dev, ABS_PRESSURE, pres);
input_report_abs(dev, ABS_TOOL_WIDTH, width);
@@ -434,9 +433,7 @@ static void elantech_report_trackpoint(struct psmouse *psmouse,
x = packet[4] - (int)((packet[1]^0x80) << 1);
y = (int)((packet[2]^0x80) << 1) - packet[5];
- input_report_key(tp_dev, BTN_LEFT, packet[0] & 0x01);
- input_report_key(tp_dev, BTN_RIGHT, packet[0] & 0x02);
- input_report_key(tp_dev, BTN_MIDDLE, packet[0] & 0x04);
+ psmouse_report_standard_buttons(tp_dev, packet[0]);
input_report_rel(tp_dev, REL_X, x);
input_report_rel(tp_dev, REL_Y, y);
@@ -526,12 +523,10 @@ static void elantech_report_absolute_v3(struct psmouse *psmouse,
input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
/* For clickpads map both buttons to BTN_LEFT */
- if (etd->fw_version & 0x001000) {
+ if (etd->fw_version & 0x001000)
input_report_key(dev, BTN_LEFT, packet[0] & 0x03);
- } else {
- input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
- input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
- }
+ else
+ psmouse_report_standard_buttons(dev, packet[0]);
input_report_abs(dev, ABS_PRESSURE, pres);
input_report_abs(dev, ABS_TOOL_WIDTH, width);
@@ -546,13 +541,10 @@ static void elantech_input_sync_v4(struct psmouse *psmouse)
unsigned char *packet = psmouse->packet;
/* For clickpads map both buttons to BTN_LEFT */
- if (etd->fw_version & 0x001000) {
+ if (etd->fw_version & 0x001000)
input_report_key(dev, BTN_LEFT, packet[0] & 0x03);
- } else {
- input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
- input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
- input_report_key(dev, BTN_MIDDLE, packet[0] & 0x04);
- }
+ else
+ psmouse_report_standard_buttons(dev, packet[0]);
input_mt_report_pointer_emulation(dev, true);
input_sync(dev);
diff --git a/drivers/input/mouse/lifebook.c b/drivers/input/mouse/lifebook.c
index 13d324cef7df..a5765f747c02 100644
--- a/drivers/input/mouse/lifebook.c
+++ b/drivers/input/mouse/lifebook.c
@@ -17,6 +17,7 @@
#include <linux/libps2.h>
#include <linux/dmi.h>
#include <linux/slab.h>
+#include <linux/types.h>
#include "psmouse.h"
#include "lifebook.h"
@@ -136,7 +137,7 @@ static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse)
struct lifebook_data *priv = psmouse->private;
struct input_dev *dev1 = psmouse->dev;
struct input_dev *dev2 = priv ? priv->dev2 : NULL;
- unsigned char *packet = psmouse->packet;
+ u8 *packet = psmouse->packet;
bool relative_packet = packet[0] & 0x08;
if (relative_packet || !lifebook_use_6byte_proto) {
@@ -188,14 +189,10 @@ static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse)
}
if (dev2) {
- if (relative_packet) {
- input_report_rel(dev2, REL_X,
- ((packet[0] & 0x10) ? packet[1] - 256 : packet[1]));
- input_report_rel(dev2, REL_Y,
- -(int)((packet[0] & 0x20) ? packet[2] - 256 : packet[2]));
- }
- input_report_key(dev2, BTN_LEFT, packet[0] & 0x01);
- input_report_key(dev2, BTN_RIGHT, packet[0] & 0x02);
+ if (relative_packet)
+ psmouse_report_standard_motion(dev2, packet);
+
+ psmouse_report_standard_buttons(dev2, packet[0]);
input_sync(dev2);
}
@@ -205,10 +202,12 @@ static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse)
static int lifebook_absolute_mode(struct psmouse *psmouse)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
- unsigned char param;
+ u8 param;
+ int error;
- if (psmouse_reset(psmouse))
- return -1;
+ error = psmouse_reset(psmouse);
+ if (error)
+ return error;
/*
* Enable absolute output -- ps2_command fails always but if
@@ -224,15 +223,15 @@ static int lifebook_absolute_mode(struct psmouse *psmouse)
static void lifebook_relative_mode(struct psmouse *psmouse)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
- unsigned char param = 0x06;
+ u8 param = 0x06;
ps2_command(ps2dev, &param, PSMOUSE_CMD_SETRES);
}
static void lifebook_set_resolution(struct psmouse *psmouse, unsigned int resolution)
{
- static const unsigned char params[] = { 0, 1, 2, 2, 3 };
- unsigned char p;
+ static const u8 params[] = { 0, 1, 2, 2, 3 };
+ u8 p;
if (resolution == 0 || resolution > 400)
resolution = 400;
@@ -257,11 +256,11 @@ static void lifebook_disconnect(struct psmouse *psmouse)
int lifebook_detect(struct psmouse *psmouse, bool set_properties)
{
if (!lifebook_present)
- return -1;
+ return -ENXIO;
if (desired_serio_phys &&
strcmp(psmouse->ps2dev.serio->phys, desired_serio_phys))
- return -1;
+ return -ENXIO;
if (set_properties) {
psmouse->vendor = "Fujitsu";
@@ -294,10 +293,10 @@ static int lifebook_create_relative_device(struct psmouse *psmouse)
dev2->id.version = 0x0000;
dev2->dev.parent = &psmouse->ps2dev.serio->dev;
- dev2->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
- dev2->relbit[BIT_WORD(REL_X)] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
- dev2->keybit[BIT_WORD(BTN_LEFT)] =
- BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT);
+ input_set_capability(dev2, EV_REL, REL_X);
+ input_set_capability(dev2, EV_REL, REL_Y);
+ input_set_capability(dev2, EV_KEY, BTN_LEFT);
+ input_set_capability(dev2, EV_KEY, BTN_RIGHT);
error = input_register_device(priv->dev2);
if (error)
@@ -316,21 +315,26 @@ int lifebook_init(struct psmouse *psmouse)
{
struct input_dev *dev1 = psmouse->dev;
int max_coord = lifebook_use_6byte_proto ? 4096 : 1024;
+ int error;
+
+ error = lifebook_absolute_mode(psmouse);
+ if (error)
+ return error;
- if (lifebook_absolute_mode(psmouse))
- return -1;
+ /* Clear default capabilities */
+ bitmap_zero(dev1->evbit, EV_CNT);
+ bitmap_zero(dev1->relbit, REL_CNT);
+ bitmap_zero(dev1->keybit, KEY_CNT);
- dev1->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
- dev1->relbit[0] = 0;
- dev1->keybit[BIT_WORD(BTN_MOUSE)] = 0;
- dev1->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_capability(dev1, EV_KEY, BTN_TOUCH);
input_set_abs_params(dev1, ABS_X, 0, max_coord, 0, 0);
input_set_abs_params(dev1, ABS_Y, 0, max_coord, 0, 0);
if (!desired_serio_phys) {
- if (lifebook_create_relative_device(psmouse)) {
+ error = lifebook_create_relative_device(psmouse);
+ if (error) {
lifebook_relative_mode(psmouse);
- return -1;
+ return error;
}
}
diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c
index ef9c97f5e3d7..3d5637e6fa5f 100644
--- a/drivers/input/mouse/logips2pp.c
+++ b/drivers/input/mouse/logips2pp.c
@@ -9,9 +9,11 @@
* the Free Software Foundation.
*/
+#include <linux/bitops.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/libps2.h>
+#include <linux/types.h>
#include "psmouse.h"
#include "logips2pp.h"
@@ -22,12 +24,12 @@
#define PS2PP_KIND_TRACKMAN 4
/* Logitech mouse features */
-#define PS2PP_WHEEL 0x01
-#define PS2PP_HWHEEL 0x02
-#define PS2PP_SIDE_BTN 0x04
-#define PS2PP_EXTRA_BTN 0x08
-#define PS2PP_TASK_BTN 0x10
-#define PS2PP_NAV_BTN 0x20
+#define PS2PP_WHEEL BIT(0)
+#define PS2PP_HWHEEL BIT(1)
+#define PS2PP_SIDE_BTN BIT(2)
+#define PS2PP_EXTRA_BTN BIT(3)
+#define PS2PP_TASK_BTN BIT(4)
+#define PS2PP_NAV_BTN BIT(5)
struct ps2pp_info {
u8 model;
@@ -42,7 +44,7 @@ struct ps2pp_info {
static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse)
{
struct input_dev *dev = psmouse->dev;
- unsigned char *packet = psmouse->packet;
+ u8 *packet = psmouse->packet;
if (psmouse->pktcnt < 3)
return PSMOUSE_GOOD_DATA;
@@ -58,28 +60,30 @@ static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse)
case 0x0d: /* Mouse extra info */
- input_report_rel(dev, packet[2] & 0x80 ? REL_HWHEEL : REL_WHEEL,
- (int) (packet[2] & 8) - (int) (packet[2] & 7));
- input_report_key(dev, BTN_SIDE, (packet[2] >> 4) & 1);
- input_report_key(dev, BTN_EXTRA, (packet[2] >> 5) & 1);
+ input_report_rel(dev,
+ packet[2] & 0x80 ? REL_HWHEEL : REL_WHEEL,
+ -sign_extend32(packet[2], 3));
+ input_report_key(dev, BTN_SIDE, packet[2] & BIT(4));
+ input_report_key(dev, BTN_EXTRA, packet[2] & BIT(5));
break;
case 0x0e: /* buttons 4, 5, 6, 7, 8, 9, 10 info */
- input_report_key(dev, BTN_SIDE, (packet[2]) & 1);
- input_report_key(dev, BTN_EXTRA, (packet[2] >> 1) & 1);
- input_report_key(dev, BTN_BACK, (packet[2] >> 3) & 1);
- input_report_key(dev, BTN_FORWARD, (packet[2] >> 4) & 1);
- input_report_key(dev, BTN_TASK, (packet[2] >> 2) & 1);
+ input_report_key(dev, BTN_SIDE, packet[2] & BIT(0));
+ input_report_key(dev, BTN_EXTRA, packet[2] & BIT(1));
+ input_report_key(dev, BTN_TASK, packet[2] & BIT(2));
+ input_report_key(dev, BTN_BACK, packet[2] & BIT(3));
+ input_report_key(dev, BTN_FORWARD, packet[2] & BIT(4));
break;
case 0x0f: /* TouchPad extra info */
- input_report_rel(dev, packet[2] & 0x08 ? REL_HWHEEL : REL_WHEEL,
- (int) ((packet[2] >> 4) & 8) - (int) ((packet[2] >> 4) & 7));
- packet[0] = packet[2] | 0x08;
+ input_report_rel(dev,
+ packet[2] & 0x08 ? REL_HWHEEL : REL_WHEEL,
+ -sign_extend32(packet[2] >> 4, 3));
+ packet[0] = packet[2] | BIT(3);
break;
default:
@@ -88,16 +92,14 @@ static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse)
(packet[1] >> 4) | (packet[0] & 0x30));
break;
}
+
+ psmouse_report_standard_buttons(dev, packet[0]);
+
} else {
/* Standard PS/2 motion data */
- input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0);
- input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0);
+ psmouse_report_standard_packet(dev, packet);
}
- input_report_key(dev, BTN_LEFT, packet[0] & 1);
- input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1);
- input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1);
-
input_sync(dev);
return PSMOUSE_FULL_PACKET;
@@ -111,13 +113,17 @@ static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse)
* Ugly.
*/
-static int ps2pp_cmd(struct psmouse *psmouse, unsigned char *param, unsigned char command)
+static int ps2pp_cmd(struct psmouse *psmouse, u8 *param, u8 command)
{
- if (psmouse_sliced_command(psmouse, command))
- return -1;
+ int error;
- if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_POLL | 0x0300))
- return -1;
+ error = ps2_sliced_command(&psmouse->ps2dev, command);
+ if (error)
+ return error;
+
+ error = ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_POLL | 0x0300);
+ if (error)
+ return error;
return 0;
}
@@ -133,7 +139,7 @@ static int ps2pp_cmd(struct psmouse *psmouse, unsigned char *param, unsigned cha
static void ps2pp_set_smartscroll(struct psmouse *psmouse, bool smartscroll)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
- unsigned char param[4];
+ u8 param[4];
ps2pp_cmd(psmouse, param, 0x32);
@@ -171,7 +177,7 @@ static ssize_t ps2pp_attr_set_smartscroll(struct psmouse *psmouse, void *data,
}
PSMOUSE_DEFINE_ATTR(smartscroll, S_IWUSR | S_IRUGO, NULL,
- ps2pp_attr_show_smartscroll, ps2pp_attr_set_smartscroll);
+ ps2pp_attr_show_smartscroll, ps2pp_attr_set_smartscroll);
/*
* Support 800 dpi resolution _only_ if the user wants it (there are good
@@ -179,11 +185,12 @@ PSMOUSE_DEFINE_ATTR(smartscroll, S_IWUSR | S_IRUGO, NULL,
* also good reasons to use it, let the user decide).
*/
-static void ps2pp_set_resolution(struct psmouse *psmouse, unsigned int resolution)
+static void ps2pp_set_resolution(struct psmouse *psmouse,
+ unsigned int resolution)
{
if (resolution > 400) {
struct ps2dev *ps2dev = &psmouse->ps2dev;
- unsigned char param = 3;
+ u8 param = 3;
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
@@ -196,7 +203,8 @@ static void ps2pp_set_resolution(struct psmouse *psmouse, unsigned int resolutio
static void ps2pp_disconnect(struct psmouse *psmouse)
{
- device_remove_file(&psmouse->ps2dev.serio->dev, &psmouse_attr_smartscroll.dattr);
+ device_remove_file(&psmouse->ps2dev.serio->dev,
+ &psmouse_attr_smartscroll.dattr);
}
static const struct ps2pp_info *get_model_info(unsigned char model)
@@ -269,24 +277,24 @@ static void ps2pp_set_model_properties(struct psmouse *psmouse,
struct input_dev *input_dev = psmouse->dev;
if (model_info->features & PS2PP_SIDE_BTN)
- __set_bit(BTN_SIDE, input_dev->keybit);
+ input_set_capability(input_dev, EV_KEY, BTN_SIDE);
if (model_info->features & PS2PP_EXTRA_BTN)
- __set_bit(BTN_EXTRA, input_dev->keybit);
+ input_set_capability(input_dev, EV_KEY, BTN_EXTRA);
if (model_info->features & PS2PP_TASK_BTN)
- __set_bit(BTN_TASK, input_dev->keybit);
+ input_set_capability(input_dev, EV_KEY, BTN_TASK);
if (model_info->features & PS2PP_NAV_BTN) {
- __set_bit(BTN_FORWARD, input_dev->keybit);
- __set_bit(BTN_BACK, input_dev->keybit);
+ input_set_capability(input_dev, EV_KEY, BTN_FORWARD);
+ input_set_capability(input_dev, EV_KEY, BTN_BACK);
}
if (model_info->features & PS2PP_WHEEL)
- __set_bit(REL_WHEEL, input_dev->relbit);
+ input_set_capability(input_dev, EV_REL, REL_WHEEL);
if (model_info->features & PS2PP_HWHEEL)
- __set_bit(REL_HWHEEL, input_dev->relbit);
+ input_set_capability(input_dev, EV_REL, REL_HWHEEL);
switch (model_info->kind) {
@@ -318,6 +326,30 @@ static void ps2pp_set_model_properties(struct psmouse *psmouse,
}
}
+static int ps2pp_setup_protocol(struct psmouse *psmouse,
+ const struct ps2pp_info *model_info)
+{
+ int error;
+
+ psmouse->protocol_handler = ps2pp_process_byte;
+ psmouse->pktsize = 3;
+
+ if (model_info->kind != PS2PP_KIND_TP3) {
+ psmouse->set_resolution = ps2pp_set_resolution;
+ psmouse->disconnect = ps2pp_disconnect;
+
+ error = device_create_file(&psmouse->ps2dev.serio->dev,
+ &psmouse_attr_smartscroll.dattr);
+ if (error) {
+ psmouse_err(psmouse,
+ "failed to create smartscroll sysfs attribute, error: %d\n",
+ error);
+ return error;
+ }
+ }
+
+ return 0;
+}
/*
* Logitech magic init. Detect whether the mouse is a Logitech one
@@ -328,9 +360,9 @@ static void ps2pp_set_model_properties(struct psmouse *psmouse,
int ps2pp_detect(struct psmouse *psmouse, bool set_properties)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
- unsigned char param[4];
- unsigned char model, buttons;
const struct ps2pp_info *model_info;
+ u8 param[4];
+ u8 model, buttons;
bool use_ps2pp = false;
int error;
@@ -346,7 +378,7 @@ int ps2pp_detect(struct psmouse *psmouse, bool set_properties)
buttons = param[1];
if (!model || !buttons)
- return -1;
+ return -ENXIO;
model_info = get_model_info(model);
if (model_info) {
@@ -368,7 +400,8 @@ int ps2pp_detect(struct psmouse *psmouse, bool set_properties)
param[0] = 0;
if (!ps2_command(ps2dev, param, 0x13d1) &&
- param[0] == 0x06 && param[1] == 0x00 && param[2] == 0x14) {
+ param[0] == 0x06 && param[1] == 0x00 &&
+ param[2] == 0x14) {
use_ps2pp = true;
}
@@ -387,7 +420,9 @@ int ps2pp_detect(struct psmouse *psmouse, bool set_properties)
}
} else {
- psmouse_warn(psmouse, "Detected unknown Logitech mouse model %d\n", model);
+ psmouse_warn(psmouse,
+ "Detected unknown Logitech mouse model %d\n",
+ model);
}
if (set_properties) {
@@ -395,31 +430,18 @@ int ps2pp_detect(struct psmouse *psmouse, bool set_properties)
psmouse->model = model;
if (use_ps2pp) {
- psmouse->protocol_handler = ps2pp_process_byte;
- psmouse->pktsize = 3;
-
- if (model_info->kind != PS2PP_KIND_TP3) {
- psmouse->set_resolution = ps2pp_set_resolution;
- psmouse->disconnect = ps2pp_disconnect;
-
- error = device_create_file(&ps2dev->serio->dev,
- &psmouse_attr_smartscroll.dattr);
- if (error) {
- psmouse_err(psmouse,
- "failed to create smartscroll sysfs attribute, error: %d\n",
- error);
- return -1;
- }
- }
+ error = ps2pp_setup_protocol(psmouse, model_info);
+ if (error)
+ return error;
}
if (buttons >= 3)
- __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
+ input_set_capability(psmouse->dev, EV_KEY, BTN_MIDDLE);
if (model_info)
ps2pp_set_model_properties(psmouse, model_info, use_ps2pp);
}
- return use_ps2pp ? 0 : -1;
+ return use_ps2pp ? 0 : -ENXIO;
}
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index 8ac9e03c05b4..8900c3166ebf 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -14,6 +14,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#define psmouse_fmt(fmt) fmt
+#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/slab.h>
@@ -23,6 +24,7 @@
#include <linux/init.h>
#include <linux/libps2.h>
#include <linux/mutex.h>
+#include <linux/types.h>
#include "psmouse.h"
#include "synaptics.h"
@@ -68,6 +70,10 @@ static bool psmouse_smartscroll = true;
module_param_named(smartscroll, psmouse_smartscroll, bool, 0644);
MODULE_PARM_DESC(smartscroll, "Logitech Smartscroll autorepeat, 1 = enabled (default), 0 = disabled.");
+static bool psmouse_a4tech_2wheels;
+module_param_named(a4tech_workaround, psmouse_a4tech_2wheels, bool, 0644);
+MODULE_PARM_DESC(a4tech_workaround, "A4Tech second scroll wheel workaround, 1 = enabled, 0 = disabled (default).");
+
static unsigned int psmouse_resetafter = 5;
module_param_named(resetafter, psmouse_resetafter, uint, 0644);
MODULE_PARM_DESC(resetafter, "Reset device after so many bad packets (0 = never).");
@@ -116,13 +122,30 @@ static DEFINE_MUTEX(psmouse_mutex);
static struct workqueue_struct *kpsmoused_wq;
-static void psmouse_report_standard_buttons(struct input_dev *dev, u8 buttons)
+void psmouse_report_standard_buttons(struct input_dev *dev, u8 buttons)
{
input_report_key(dev, BTN_LEFT, buttons & BIT(0));
input_report_key(dev, BTN_MIDDLE, buttons & BIT(2));
input_report_key(dev, BTN_RIGHT, buttons & BIT(1));
}
+void psmouse_report_standard_motion(struct input_dev *dev, u8 *packet)
+{
+ int x, y;
+
+ x = packet[1] ? packet[1] - ((packet[0] << 4) & 0x100) : 0;
+ y = packet[2] ? packet[2] - ((packet[0] << 3) & 0x100) : 0;
+
+ input_report_rel(dev, REL_X, x);
+ input_report_rel(dev, REL_Y, -y);
+}
+
+void psmouse_report_standard_packet(struct input_dev *dev, u8 *packet)
+{
+ psmouse_report_standard_buttons(dev, packet[0]);
+ psmouse_report_standard_motion(dev, packet);
+}
+
/*
* psmouse_process_byte() analyzes the PS/2 data stream and reports
* relevant events to the input module once full packet has arrived.
@@ -130,7 +153,8 @@ static void psmouse_report_standard_buttons(struct input_dev *dev, u8 buttons)
psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
{
struct input_dev *dev = psmouse->dev;
- unsigned char *packet = psmouse->packet;
+ u8 *packet = psmouse->packet;
+ int wheel;
if (psmouse->pktcnt < psmouse->pktsize)
return PSMOUSE_GOOD_DATA;
@@ -140,39 +164,52 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
switch (psmouse->protocol->type) {
case PSMOUSE_IMPS:
/* IntelliMouse has scroll wheel */
- input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]);
+ input_report_rel(dev, REL_WHEEL, -(s8) packet[3]);
break;
case PSMOUSE_IMEX:
/* Scroll wheel and buttons on IntelliMouse Explorer */
switch (packet[3] & 0xC0) {
case 0x80: /* vertical scroll on IntelliMouse Explorer 4.0 */
- input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31));
+ input_report_rel(dev, REL_WHEEL,
+ -sign_extend32(packet[3], 5));
break;
case 0x40: /* horizontal scroll on IntelliMouse Explorer 4.0 */
- input_report_rel(dev, REL_HWHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31));
+ input_report_rel(dev, REL_HWHEEL,
+ -sign_extend32(packet[3], 5));
break;
case 0x00:
case 0xC0:
- input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 8) - (int) (packet[3] & 7));
- input_report_key(dev, BTN_SIDE, (packet[3] >> 4) & 1);
- input_report_key(dev, BTN_EXTRA, (packet[3] >> 5) & 1);
+ wheel = sign_extend32(packet[3], 3);
+
+ /*
+ * Some A4Tech mice have two scroll wheels, with first
+ * one reporting +/-1 in the lower nibble, and second
+ * one reporting +/-2.
+ */
+ if (psmouse_a4tech_2wheels && abs(wheel) > 1)
+ input_report_rel(dev, REL_HWHEEL, wheel / 2);
+ else
+ input_report_rel(dev, REL_WHEEL, -wheel);
+
+ input_report_key(dev, BTN_SIDE, BIT(4));
+ input_report_key(dev, BTN_EXTRA, BIT(5));
break;
}
break;
case PSMOUSE_GENPS:
/* Report scroll buttons on NetMice */
- input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]);
+ input_report_rel(dev, REL_WHEEL, -(s8) packet[3]);
/* Extra buttons on Genius NewNet 3D */
- input_report_key(dev, BTN_SIDE, (packet[0] >> 6) & 1);
- input_report_key(dev, BTN_EXTRA, (packet[0] >> 7) & 1);
+ input_report_key(dev, BTN_SIDE, BIT(6));
+ input_report_key(dev, BTN_EXTRA, BIT(7));
break;
case PSMOUSE_THINKPS:
/* Extra button on ThinkingMouse */
- input_report_key(dev, BTN_EXTRA, (packet[0] >> 3) & 1);
+ input_report_key(dev, BTN_EXTRA, BIT(3));
/*
* Without this bit of weirdness moving up gives wildly
@@ -186,8 +223,8 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
* Cortron PS2 Trackball reports SIDE button in the
* 4th bit of the first byte.
*/
- input_report_key(dev, BTN_SIDE, (packet[0] >> 3) & 1);
- packet[0] |= 0x08;
+ input_report_key(dev, BTN_SIDE, BIT(3));
+ packet[0] |= BIT(3);
break;
default:
@@ -195,11 +232,8 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
}
/* Generic PS/2 Mouse */
- psmouse_report_standard_buttons(dev,
- packet[0] | psmouse->extra_buttons);
-
- input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0);
- input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0);
+ packet[0] |= psmouse->extra_buttons;
+ psmouse_report_standard_packet(dev, packet);
input_sync(dev);
@@ -255,7 +289,7 @@ static int psmouse_handle_byte(struct psmouse *psmouse)
psmouse_notice(psmouse,
"issuing reconnect request\n");
serio_reconnect(psmouse->ps2dev.serio);
- return -1;
+ return -EIO;
}
}
psmouse->pktcnt = 0;
@@ -306,7 +340,7 @@ static void psmouse_handle_oob_data(struct psmouse *psmouse, u8 data)
* for normal processing or gathering them as command response.
*/
static irqreturn_t psmouse_interrupt(struct serio *serio,
- unsigned char data, unsigned int flags)
+ u8 data, unsigned int flags)
{
struct psmouse *psmouse = serio_get_drvdata(serio);
@@ -398,40 +432,19 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
}
/*
- * psmouse_sliced_command() sends an extended PS/2 command to the mouse
- * using sliced syntax, understood by advanced devices, such as Logitech
- * or Synaptics touchpads. The command is encoded as:
- * 0xE6 0xE8 rr 0xE8 ss 0xE8 tt 0xE8 uu where (rr*64)+(ss*16)+(tt*4)+uu
- * is the command.
- */
-int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command)
-{
- int i;
-
- if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11))
- return -1;
-
- for (i = 6; i >= 0; i -= 2) {
- unsigned char d = (command >> i) & 3;
- if (ps2_command(&psmouse->ps2dev, &d, PSMOUSE_CMD_SETRES))
- return -1;
- }
-
- return 0;
-}
-
-/*
* psmouse_reset() resets the mouse into power-on state.
*/
int psmouse_reset(struct psmouse *psmouse)
{
- unsigned char param[2];
+ u8 param[2];
+ int error;
- if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_RESET_BAT))
- return -1;
+ error = ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_RESET_BAT);
+ if (error)
+ return error;
if (param[0] != PSMOUSE_RET_BAT && param[1] != PSMOUSE_RET_ID)
- return -1;
+ return -EIO;
return 0;
}
@@ -441,8 +454,8 @@ int psmouse_reset(struct psmouse *psmouse)
*/
void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution)
{
- static const unsigned char params[] = { 0, 1, 2, 2, 3 };
- unsigned char p;
+ static const u8 params[] = { 0, 1, 2, 2, 3 };
+ u8 p;
if (resolution == 0 || resolution > 200)
resolution = 200;
@@ -457,11 +470,12 @@ void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution)
*/
static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate)
{
- static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 };
- unsigned char r;
+ static const u8 rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 };
+ u8 r;
int i = 0;
- while (rates[i] > rate) i++;
+ while (rates[i] > rate)
+ i++;
r = rates[i];
ps2_command(&psmouse->ps2dev, &r, PSMOUSE_CMD_SETRATE);
psmouse->rate = r;
@@ -533,7 +547,7 @@ bool psmouse_matches_pnp_id(struct psmouse *psmouse, const char * const ids[])
static int genius_detect(struct psmouse *psmouse, bool set_properties)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
- unsigned char param[4];
+ u8 param[4];
param[0] = 3;
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
@@ -543,7 +557,7 @@ static int genius_detect(struct psmouse *psmouse, bool set_properties)
ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
if (param[0] != 0x00 || param[1] != 0x33 || param[2] != 0x55)
- return -1;
+ return -ENODEV;
if (set_properties) {
__set_bit(BTN_MIDDLE, psmouse->dev->keybit);
@@ -565,7 +579,7 @@ static int genius_detect(struct psmouse *psmouse, bool set_properties)
static int intellimouse_detect(struct psmouse *psmouse, bool set_properties)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
- unsigned char param[2];
+ u8 param[2];
param[0] = 200;
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
@@ -576,7 +590,7 @@ static int intellimouse_detect(struct psmouse *psmouse, bool set_properties)
ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
if (param[0] != 3)
- return -1;
+ return -ENODEV;
if (set_properties) {
__set_bit(BTN_MIDDLE, psmouse->dev->keybit);
@@ -598,7 +612,7 @@ static int intellimouse_detect(struct psmouse *psmouse, bool set_properties)
static int im_explorer_detect(struct psmouse *psmouse, bool set_properties)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
- unsigned char param[2];
+ u8 param[2];
intellimouse_detect(psmouse, 0);
@@ -611,7 +625,7 @@ static int im_explorer_detect(struct psmouse *psmouse, bool set_properties)
ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
if (param[0] != 4)
- return -1;
+ return -ENODEV;
/* Magic to enable horizontal scrolling on IntelliMouse 4.0 */
param[0] = 200;
@@ -644,8 +658,8 @@ static int im_explorer_detect(struct psmouse *psmouse, bool set_properties)
static int thinking_detect(struct psmouse *psmouse, bool set_properties)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
- unsigned char param[2];
- static const unsigned char seq[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20 };
+ u8 param[2];
+ static const u8 seq[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20 };
int i;
param[0] = 10;
@@ -659,7 +673,7 @@ static int thinking_detect(struct psmouse *psmouse, bool set_properties)
ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
if (param[0] != 2)
- return -1;
+ return -ENODEV;
if (set_properties) {
__set_bit(BTN_MIDDLE, psmouse->dev->keybit);
@@ -687,7 +701,7 @@ static int ps2bare_detect(struct psmouse *psmouse, bool set_properties)
* We have no way of figuring true number of buttons so let's
* assume that the device has 3.
*/
- __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
+ input_set_capability(psmouse->dev, EV_KEY, BTN_MIDDLE);
}
return 0;
@@ -942,20 +956,17 @@ static void psmouse_apply_defaults(struct psmouse *psmouse)
{
struct input_dev *input_dev = psmouse->dev;
- memset(input_dev->evbit, 0, sizeof(input_dev->evbit));
- memset(input_dev->keybit, 0, sizeof(input_dev->keybit));
- memset(input_dev->relbit, 0, sizeof(input_dev->relbit));
- memset(input_dev->absbit, 0, sizeof(input_dev->absbit));
- memset(input_dev->mscbit, 0, sizeof(input_dev->mscbit));
-
- __set_bit(EV_KEY, input_dev->evbit);
- __set_bit(EV_REL, input_dev->evbit);
+ bitmap_zero(input_dev->evbit, EV_CNT);
+ bitmap_zero(input_dev->keybit, KEY_CNT);
+ bitmap_zero(input_dev->relbit, REL_CNT);
+ bitmap_zero(input_dev->absbit, ABS_CNT);
+ bitmap_zero(input_dev->mscbit, MSC_CNT);
- __set_bit(BTN_LEFT, input_dev->keybit);
- __set_bit(BTN_RIGHT, input_dev->keybit);
+ input_set_capability(input_dev, EV_KEY, BTN_LEFT);
+ input_set_capability(input_dev, EV_KEY, BTN_RIGHT);
- __set_bit(REL_X, input_dev->relbit);
- __set_bit(REL_Y, input_dev->relbit);
+ input_set_capability(input_dev, EV_REL, REL_X);
+ input_set_capability(input_dev, EV_REL, REL_Y);
__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
@@ -1225,7 +1236,8 @@ static int psmouse_extensions(struct psmouse *psmouse,
static int psmouse_probe(struct psmouse *psmouse)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
- unsigned char param[2];
+ u8 param[2];
+ int error;
/*
* First, we check if it's a mouse. It should send 0x00 or 0x03 in
@@ -1234,20 +1246,22 @@ static int psmouse_probe(struct psmouse *psmouse)
* subsequent ID queries, probably due to a firmware bug.
*/
param[0] = 0xa5;
- if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETID))
- return -1;
+ error = ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
+ if (error)
+ return error;
if (param[0] != 0x00 && param[0] != 0x03 &&
param[0] != 0x04 && param[0] != 0xff)
- return -1;
+ return -ENODEV;
/*
* Then we reset and disable the mouse so that it doesn't generate
* events.
*/
- if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS))
- psmouse_warn(psmouse, "Failed to reset mouse on %s\n",
- ps2dev->serio->phys);
+ error = ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
+ if (error)
+ psmouse_warn(psmouse, "Failed to reset mouse on %s: %d\n",
+ ps2dev->serio->phys, error);
return 0;
}
@@ -1288,10 +1302,13 @@ int psmouse_activate(struct psmouse *psmouse)
*/
int psmouse_deactivate(struct psmouse *psmouse)
{
- if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE)) {
- psmouse_warn(psmouse, "Failed to deactivate mouse on %s\n",
- psmouse->ps2dev.serio->phys);
- return -1;
+ int error;
+
+ error = ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE);
+ if (error) {
+ psmouse_warn(psmouse, "Failed to deactivate mouse on %s: %d\n",
+ psmouse->ps2dev.serio->phys, error);
+ return error;
}
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
index 8cd453808cc7..71ac50082c8b 100644
--- a/drivers/input/mouse/psmouse.h
+++ b/drivers/input/mouse/psmouse.h
@@ -131,7 +131,6 @@ struct psmouse {
void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work,
unsigned long delay);
-int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command);
int psmouse_reset(struct psmouse *psmouse);
void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state);
void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution);
@@ -140,6 +139,10 @@ int psmouse_activate(struct psmouse *psmouse);
int psmouse_deactivate(struct psmouse *psmouse);
bool psmouse_matches_pnp_id(struct psmouse *psmouse, const char * const ids[]);
+void psmouse_report_standard_buttons(struct input_dev *, u8 buttons);
+void psmouse_report_standard_motion(struct input_dev *, u8 *packet);
+void psmouse_report_standard_packet(struct input_dev *, u8 *packet);
+
struct psmouse_attribute {
struct device_attribute dattr;
void *data;
diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c
index 11c32ac8234b..1d6010d463e2 100644
--- a/drivers/input/mouse/sentelic.c
+++ b/drivers/input/mouse/sentelic.c
@@ -710,7 +710,6 @@ static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse)
unsigned char *packet = psmouse->packet;
unsigned char button_status = 0, lscroll = 0, rscroll = 0;
unsigned short abs_x, abs_y, fgrs = 0;
- int rel_x, rel_y;
if (psmouse->pktcnt < 4)
return PSMOUSE_GOOD_DATA;
@@ -840,15 +839,7 @@ static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse)
/*
* Standard PS/2 Mouse
*/
- input_report_key(dev, BTN_LEFT, packet[0] & 1);
- input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1);
- input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1);
-
- rel_x = packet[1] ? (int)packet[1] - (int)((packet[0] << 4) & 0x100) : 0;
- rel_y = packet[2] ? (int)((packet[0] << 3) & 0x100) - (int)packet[2] : 0;
-
- input_report_rel(dev, REL_X, rel_x);
- input_report_rel(dev, REL_Y, rel_y);
+ psmouse_report_standard_packet(dev, packet);
break;
}
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index a246fc686bb7..60f2c463d1cc 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -84,7 +84,7 @@ static int synaptics_mode_cmd(struct psmouse *psmouse, u8 mode)
u8 param[1];
int error;
- error = psmouse_sliced_command(psmouse, mode);
+ error = ps2_sliced_command(&psmouse->ps2dev, mode);
if (error)
return error;
@@ -189,7 +189,7 @@ static int synaptics_send_cmd(struct psmouse *psmouse, u8 cmd, u8 *param)
{
int error;
- error = psmouse_sliced_command(psmouse, cmd);
+ error = ps2_sliced_command(&psmouse->ps2dev, cmd);
if (error)
return error;
@@ -546,7 +546,7 @@ static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse)
static u8 param = 0xc8;
int error;
- error = psmouse_sliced_command(psmouse, SYN_QUE_MODEL);
+ error = ps2_sliced_command(&psmouse->ps2dev, SYN_QUE_MODEL);
if (error)
return error;
@@ -613,7 +613,7 @@ static int synaptics_pt_write(struct serio *serio, u8 c)
u8 rate_param = SYN_PS_CLIENT_CMD; /* indicates that we want pass-through port */
int error;
- error = psmouse_sliced_command(parent, c);
+ error = ps2_sliced_command(&parent->ps2dev, c);
if (error)
return error;
@@ -1227,32 +1227,39 @@ static void set_abs_position_params(struct input_dev *dev,
input_abs_set_res(dev, y_code, info->y_res);
}
-static void set_input_params(struct psmouse *psmouse,
- struct synaptics_data *priv)
+static int set_input_params(struct psmouse *psmouse,
+ struct synaptics_data *priv)
{
struct input_dev *dev = psmouse->dev;
struct synaptics_device_info *info = &priv->info;
int i;
+ int error;
+
+ /* Reset default psmouse capabilities */
+ __clear_bit(EV_REL, dev->evbit);
+ bitmap_zero(dev->relbit, REL_CNT);
+ bitmap_zero(dev->keybit, KEY_CNT);
/* Things that apply to both modes */
__set_bit(INPUT_PROP_POINTER, dev->propbit);
- __set_bit(EV_KEY, dev->evbit);
- __set_bit(BTN_LEFT, dev->keybit);
- __set_bit(BTN_RIGHT, dev->keybit);
- if (SYN_CAP_MIDDLE_BUTTON(info->capabilities))
- __set_bit(BTN_MIDDLE, dev->keybit);
+ input_set_capability(dev, EV_KEY, BTN_LEFT);
+
+ /* Clickpads report only left button */
+ if (!SYN_CAP_CLICKPAD(info->ext_cap_0c)) {
+ input_set_capability(dev, EV_KEY, BTN_RIGHT);
+ if (SYN_CAP_MIDDLE_BUTTON(info->capabilities))
+ input_set_capability(dev, EV_KEY, BTN_MIDDLE);
+ }
if (!priv->absolute_mode) {
/* Relative mode */
- __set_bit(EV_REL, dev->evbit);
- __set_bit(REL_X, dev->relbit);
- __set_bit(REL_Y, dev->relbit);
- return;
+ input_set_capability(dev, EV_REL, REL_X);
+ input_set_capability(dev, EV_REL, REL_Y);
+ return 0;
}
/* Absolute mode */
- __set_bit(EV_ABS, dev->evbit);
set_abs_position_params(dev, &priv->info, ABS_X, ABS_Y);
input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
@@ -1264,11 +1271,15 @@ static void set_input_params(struct psmouse *psmouse,
ABS_MT_POSITION_X, ABS_MT_POSITION_Y);
/* Image sensors can report per-contact pressure */
input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
- input_mt_init_slots(dev, 2, INPUT_MT_POINTER | INPUT_MT_TRACK);
+
+ error = input_mt_init_slots(dev, 2,
+ INPUT_MT_POINTER | INPUT_MT_TRACK);
+ if (error)
+ return error;
/* Image sensors can signal 4 and 5 finger clicks */
- __set_bit(BTN_TOOL_QUADTAP, dev->keybit);
- __set_bit(BTN_TOOL_QUINTTAP, dev->keybit);
+ input_set_capability(dev, EV_KEY, BTN_TOOL_QUADTAP);
+ input_set_capability(dev, EV_KEY, BTN_TOOL_QUINTTAP);
} else if (SYN_CAP_ADV_GESTURE(info->ext_cap_0c)) {
set_abs_position_params(dev, info,
ABS_MT_POSITION_X, ABS_MT_POSITION_Y);
@@ -1276,10 +1287,13 @@ static void set_input_params(struct psmouse *psmouse,
* Profile sensor in CR-48 tracks contacts reasonably well,
* other non-image sensors with AGM use semi-mt.
*/
- input_mt_init_slots(dev, 2,
- INPUT_MT_POINTER |
- (cr48_profile_sensor ?
- INPUT_MT_TRACK : INPUT_MT_SEMI_MT));
+ error = input_mt_init_slots(dev, 2,
+ INPUT_MT_POINTER |
+ (cr48_profile_sensor ?
+ INPUT_MT_TRACK :
+ INPUT_MT_SEMI_MT));
+ if (error)
+ return error;
/*
* For semi-mt devices we send ABS_X/Y ourselves instead of
@@ -1295,37 +1309,32 @@ static void set_input_params(struct psmouse *psmouse,
if (SYN_CAP_PALMDETECT(info->capabilities))
input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
- __set_bit(BTN_TOUCH, dev->keybit);
- __set_bit(BTN_TOOL_FINGER, dev->keybit);
+ input_set_capability(dev, EV_KEY, BTN_TOUCH);
+ input_set_capability(dev, EV_KEY, BTN_TOOL_FINGER);
if (synaptics_has_multifinger(priv)) {
- __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
- __set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
+ input_set_capability(dev, EV_KEY, BTN_TOOL_DOUBLETAP);
+ input_set_capability(dev, EV_KEY, BTN_TOOL_TRIPLETAP);
}
if (SYN_CAP_FOUR_BUTTON(info->capabilities) ||
SYN_CAP_MIDDLE_BUTTON(info->capabilities)) {
- __set_bit(BTN_FORWARD, dev->keybit);
- __set_bit(BTN_BACK, dev->keybit);
+ input_set_capability(dev, EV_KEY, BTN_FORWARD);
+ input_set_capability(dev, EV_KEY, BTN_BACK);
}
if (!SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10))
for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(info->ext_cap); i++)
- __set_bit(BTN_0 + i, dev->keybit);
-
- __clear_bit(EV_REL, dev->evbit);
- __clear_bit(REL_X, dev->relbit);
- __clear_bit(REL_Y, dev->relbit);
+ input_set_capability(dev, EV_KEY, BTN_0 + i);
if (SYN_CAP_CLICKPAD(info->ext_cap_0c)) {
__set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
if (psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) &&
!SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10))
__set_bit(INPUT_PROP_TOPBUTTONPAD, dev->propbit);
- /* Clickpads report only left button */
- __clear_bit(BTN_RIGHT, dev->keybit);
- __clear_bit(BTN_MIDDLE, dev->keybit);
}
+
+ return 0;
}
static ssize_t synaptics_show_disable_gesture(struct psmouse *psmouse,
@@ -1563,7 +1572,12 @@ static int synaptics_init_ps2(struct psmouse *psmouse,
info->capabilities, info->ext_cap, info->ext_cap_0c,
info->ext_cap_10, info->board_id, info->firmware_id);
- set_input_params(psmouse, priv);
+ err = set_input_params(psmouse, priv);
+ if (err) {
+ psmouse_err(psmouse,
+ "failed to set up capabilities: %d\n", err);
+ goto init_fail;
+ }
/*
* Encode touchpad model so that it can be used to set
diff --git a/drivers/input/mouse/synaptics_usb.c b/drivers/input/mouse/synaptics_usb.c
index cb7d15d826d0..83d2412a64cf 100644
--- a/drivers/input/mouse/synaptics_usb.c
+++ b/drivers/input/mouse/synaptics_usb.c
@@ -82,6 +82,10 @@ struct synusb {
struct urb *urb;
unsigned char *data;
+ /* serialize access to open/suspend */
+ struct mutex pm_mutex;
+ bool is_open;
+
/* input device related data structures */
struct input_dev *input;
char name[128];
@@ -252,6 +256,7 @@ static int synusb_open(struct input_dev *dev)
return retval;
}
+ mutex_lock(&synusb->pm_mutex);
retval = usb_submit_urb(synusb->urb, GFP_KERNEL);
if (retval) {
dev_err(&synusb->intf->dev,
@@ -262,8 +267,10 @@ static int synusb_open(struct input_dev *dev)
}
synusb->intf->needs_remote_wakeup = 1;
+ synusb->is_open = true;
out:
+ mutex_unlock(&synusb->pm_mutex);
usb_autopm_put_interface(synusb->intf);
return retval;
}
@@ -275,8 +282,11 @@ static void synusb_close(struct input_dev *dev)
autopm_error = usb_autopm_get_interface(synusb->intf);
+ mutex_lock(&synusb->pm_mutex);
usb_kill_urb(synusb->urb);
synusb->intf->needs_remote_wakeup = 0;
+ synusb->is_open = false;
+ mutex_unlock(&synusb->pm_mutex);
if (!autopm_error)
usb_autopm_put_interface(synusb->intf);
@@ -315,6 +325,7 @@ static int synusb_probe(struct usb_interface *intf,
synusb->udev = udev;
synusb->intf = intf;
synusb->input = input_dev;
+ mutex_init(&synusb->pm_mutex);
synusb->flags = id->driver_info;
if (synusb->flags & SYNUSB_COMBO) {
@@ -466,11 +477,10 @@ static void synusb_disconnect(struct usb_interface *intf)
static int synusb_suspend(struct usb_interface *intf, pm_message_t message)
{
struct synusb *synusb = usb_get_intfdata(intf);
- struct input_dev *input_dev = synusb->input;
- mutex_lock(&input_dev->mutex);
+ mutex_lock(&synusb->pm_mutex);
usb_kill_urb(synusb->urb);
- mutex_unlock(&input_dev->mutex);
+ mutex_unlock(&synusb->pm_mutex);
return 0;
}
@@ -478,17 +488,16 @@ static int synusb_suspend(struct usb_interface *intf, pm_message_t message)
static int synusb_resume(struct usb_interface *intf)
{
struct synusb *synusb = usb_get_intfdata(intf);
- struct input_dev *input_dev = synusb->input;
int retval = 0;
- mutex_lock(&input_dev->mutex);
+ mutex_lock(&synusb->pm_mutex);
- if ((input_dev->users || (synusb->flags & SYNUSB_IO_ALWAYS)) &&
+ if ((synusb->is_open || (synusb->flags & SYNUSB_IO_ALWAYS)) &&
usb_submit_urb(synusb->urb, GFP_NOIO) < 0) {
retval = -EIO;
}
- mutex_unlock(&input_dev->mutex);
+ mutex_unlock(&synusb->pm_mutex);
return retval;
}
@@ -496,9 +505,8 @@ static int synusb_resume(struct usb_interface *intf)
static int synusb_pre_reset(struct usb_interface *intf)
{
struct synusb *synusb = usb_get_intfdata(intf);
- struct input_dev *input_dev = synusb->input;
- mutex_lock(&input_dev->mutex);
+ mutex_lock(&synusb->pm_mutex);
usb_kill_urb(synusb->urb);
return 0;
@@ -507,15 +515,14 @@ static int synusb_pre_reset(struct usb_interface *intf)
static int synusb_post_reset(struct usb_interface *intf)
{
struct synusb *synusb = usb_get_intfdata(intf);
- struct input_dev *input_dev = synusb->input;
int retval = 0;
- if ((input_dev->users || (synusb->flags & SYNUSB_IO_ALWAYS)) &&
+ if ((synusb->is_open || (synusb->flags & SYNUSB_IO_ALWAYS)) &&
usb_submit_urb(synusb->urb, GFP_NOIO) < 0) {
retval = -EIO;
}
- mutex_unlock(&input_dev->mutex);
+ mutex_unlock(&synusb->pm_mutex);
return retval;
}
diff --git a/drivers/input/mouse/trackpoint.c b/drivers/input/mouse/trackpoint.c
index bbd29220dbe9..6590d10f166f 100644
--- a/drivers/input/mouse/trackpoint.c
+++ b/drivers/input/mouse/trackpoint.c
@@ -33,18 +33,15 @@ static const char * const trackpoint_variants[] = {
*/
static int trackpoint_power_on_reset(struct ps2dev *ps2dev)
{
- u8 results[2];
- int tries = 0;
+ u8 param[2] = { TP_POR };
+ int err;
- /* Issue POR command, and repeat up to once if 0xFC00 received */
- do {
- if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||
- ps2_command(ps2dev, results, MAKE_PS2_CMD(0, 2, TP_POR)))
- return -1;
- } while (results[0] == 0xFC && results[1] == 0x00 && ++tries < 2);
+ err = ps2_command(ps2dev, param, MAKE_PS2_CMD(1, 2, TP_COMMAND));
+ if (err)
+ return err;
/* Check for success response -- 0xAA00 */
- if (results[0] != 0xAA || results[1] != 0x00)
+ if (param[0] != 0xAA || param[1] != 0x00)
return -ENODEV;
return 0;
@@ -55,49 +52,39 @@ static int trackpoint_power_on_reset(struct ps2dev *ps2dev)
*/
static int trackpoint_read(struct ps2dev *ps2dev, u8 loc, u8 *results)
{
- if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||
- ps2_command(ps2dev, results, MAKE_PS2_CMD(0, 1, loc))) {
- return -1;
- }
+ results[0] = loc;
- return 0;
+ return ps2_command(ps2dev, results, MAKE_PS2_CMD(1, 1, TP_COMMAND));
}
static int trackpoint_write(struct ps2dev *ps2dev, u8 loc, u8 val)
{
- if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||
- ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_WRITE_MEM)) ||
- ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, loc)) ||
- ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, val))) {
- return -1;
- }
+ u8 param[3] = { TP_WRITE_MEM, loc, val };
- return 0;
+ return ps2_command(ps2dev, param, MAKE_PS2_CMD(3, 0, TP_COMMAND));
}
static int trackpoint_toggle_bit(struct ps2dev *ps2dev, u8 loc, u8 mask)
{
+ u8 param[3] = { TP_TOGGLE, loc, mask };
+
/* Bad things will happen if the loc param isn't in this range */
if (loc < 0x20 || loc >= 0x2F)
- return -1;
-
- if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||
- ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_TOGGLE)) ||
- ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, loc)) ||
- ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, mask))) {
- return -1;
- }
+ return -EINVAL;
- return 0;
+ return ps2_command(ps2dev, param, MAKE_PS2_CMD(3, 0, TP_COMMAND));
}
static int trackpoint_update_bit(struct ps2dev *ps2dev,
u8 loc, u8 mask, u8 value)
{
- int retval = 0;
+ int retval;
u8 data;
- trackpoint_read(ps2dev, loc, &data);
+ retval = trackpoint_read(ps2dev, loc, &data);
+ if (retval)
+ return retval;
+
if (((data & mask) == mask) != !!value)
retval = trackpoint_toggle_bit(ps2dev, loc, mask);
@@ -142,9 +129,9 @@ static ssize_t trackpoint_set_int_attr(struct psmouse *psmouse, void *data,
return err;
*field = value;
- trackpoint_write(&psmouse->ps2dev, attr->command, value);
+ err = trackpoint_write(&psmouse->ps2dev, attr->command, value);
- return count;
+ return err ?: count;
}
#define TRACKPOINT_INT_ATTR(_name, _command, _default) \
@@ -175,10 +162,11 @@ static ssize_t trackpoint_set_bit_attr(struct psmouse *psmouse, void *data,
if (*field != value) {
*field = value;
- trackpoint_toggle_bit(&psmouse->ps2dev, attr->command, attr->mask);
+ err = trackpoint_toggle_bit(&psmouse->ps2dev,
+ attr->command, attr->mask);
}
- return count;
+ return err ?: count;
}
diff --git a/drivers/input/serio/gscps2.c b/drivers/input/serio/gscps2.c
index aa9f29b875de..49d8d53e50b7 100644
--- a/drivers/input/serio/gscps2.c
+++ b/drivers/input/serio/gscps2.c
@@ -91,7 +91,7 @@ struct gscps2port {
struct parisc_device *padev;
struct serio *port;
spinlock_t lock;
- char *addr;
+ char __iomem *addr;
u8 act, append; /* position in buffer[] */
struct {
u8 data;
@@ -114,7 +114,7 @@ struct gscps2port {
* wait_TBE() - wait for Transmit Buffer Empty
*/
-static int wait_TBE(char *addr)
+static int wait_TBE(char __iomem *addr)
{
int timeout = 25000; /* device is expected to react within 250 msec */
while (gscps2_readb_status(addr) & GSC_STAT_TBNE) {
@@ -146,14 +146,14 @@ static void gscps2_flush(struct gscps2port *ps2port)
static inline int gscps2_writeb_output(struct gscps2port *ps2port, u8 data)
{
unsigned long flags;
- char *addr = ps2port->addr;
+ char __iomem *addr = ps2port->addr;
if (!wait_TBE(addr)) {
printk(KERN_DEBUG PFX "timeout - could not write byte %#x\n", data);
return 0;
}
- while (gscps2_readb_status(ps2port->addr) & GSC_STAT_RBNE)
+ while (gscps2_readb_status(addr) & GSC_STAT_RBNE)
/* wait */;
spin_lock_irqsave(&ps2port->lock, flags);
@@ -200,13 +200,12 @@ static void gscps2_enable(struct gscps2port *ps2port, int enable)
static void gscps2_reset(struct gscps2port *ps2port)
{
- char *addr = ps2port->addr;
unsigned long flags;
/* reset the interface */
spin_lock_irqsave(&ps2port->lock, flags);
gscps2_flush(ps2port);
- writeb(0xff, addr+GSC_RESET);
+ writeb(0xff, ps2port->addr + GSC_RESET);
gscps2_flush(ps2port);
spin_unlock_irqrestore(&ps2port->lock, flags);
}
diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
index 6cbbdc6e9687..b353d494ad40 100644
--- a/drivers/input/serio/i8042-x86ia64io.h
+++ b/drivers/input/serio/i8042-x86ia64io.h
@@ -530,6 +530,20 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = {
{ }
};
+static const struct dmi_system_id i8042_dmi_forcemux_table[] __initconst = {
+ {
+ /*
+ * Sony Vaio VGN-CS series require MUX or the touch sensor
+ * buttons will disturb touchpad operation
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VGN-CS"),
+ },
+ },
+ { }
+};
+
/*
* On some Asus laptops, just running self tests cause problems.
*/
@@ -621,6 +635,13 @@ static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = {
},
},
{
+ /* Lenovo ThinkPad L460 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad L460"),
+ },
+ },
+ {
/* Clevo P650RS, 650RP6, Sager NP8152-S, and others */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Notebook"),
@@ -1163,6 +1184,9 @@ static int __init i8042_platform_init(void)
if (dmi_check_system(i8042_dmi_nomux_table))
i8042_nomux = true;
+ if (dmi_check_system(i8042_dmi_forcemux_table))
+ i8042_nomux = false;
+
if (dmi_check_system(i8042_dmi_notimeout_table))
i8042_notimeout = true;
diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c
index 83e9c663aa67..e6a07e68d1ff 100644
--- a/drivers/input/serio/libps2.c
+++ b/drivers/input/serio/libps2.c
@@ -26,31 +26,79 @@ MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
MODULE_DESCRIPTION("PS/2 driver library");
MODULE_LICENSE("GPL");
+static int ps2_do_sendbyte(struct ps2dev *ps2dev, u8 byte,
+ unsigned int timeout, unsigned int max_attempts)
+ __releases(&ps2dev->serio->lock) __acquires(&ps2dev->serio->lock)
+{
+ int attempt = 0;
+ int error;
+
+ lockdep_assert_held(&ps2dev->serio->lock);
+
+ do {
+ ps2dev->nak = 1;
+ ps2dev->flags |= PS2_FLAG_ACK;
+
+ serio_continue_rx(ps2dev->serio);
+
+ error = serio_write(ps2dev->serio, byte);
+ if (error)
+ dev_dbg(&ps2dev->serio->dev,
+ "failed to write %#02x: %d\n", byte, error);
+ else
+ wait_event_timeout(ps2dev->wait,
+ !(ps2dev->flags & PS2_FLAG_ACK),
+ msecs_to_jiffies(timeout));
+
+ serio_pause_rx(ps2dev->serio);
+ } while (ps2dev->nak == PS2_RET_NAK && ++attempt < max_attempts);
+
+ ps2dev->flags &= ~PS2_FLAG_ACK;
+
+ if (!error) {
+ switch (ps2dev->nak) {
+ case 0:
+ break;
+ case PS2_RET_NAK:
+ error = -EAGAIN;
+ break;
+ case PS2_RET_ERR:
+ error = -EPROTO;
+ break;
+ default:
+ error = -EIO;
+ break;
+ }
+ }
+
+ if (error || attempt > 1)
+ dev_dbg(&ps2dev->serio->dev,
+ "%02x - %d (%x), attempt %d\n",
+ byte, error, ps2dev->nak, attempt);
+
+ return error;
+}
+
/*
* ps2_sendbyte() sends a byte to the device and waits for acknowledge.
- * It doesn't handle retransmission, though it could - because if there
- * is a need for retransmissions device has to be replaced anyway.
+ * It doesn't handle retransmission, the caller is expected to handle
+ * it when needed.
*
* ps2_sendbyte() can only be called from a process context.
*/
-int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout)
+int ps2_sendbyte(struct ps2dev *ps2dev, u8 byte, unsigned int timeout)
{
+ int retval;
+
serio_pause_rx(ps2dev->serio);
- ps2dev->nak = 1;
- ps2dev->flags |= PS2_FLAG_ACK;
- serio_continue_rx(ps2dev->serio);
- if (serio_write(ps2dev->serio, byte) == 0)
- wait_event_timeout(ps2dev->wait,
- !(ps2dev->flags & PS2_FLAG_ACK),
- msecs_to_jiffies(timeout));
+ retval = ps2_do_sendbyte(ps2dev, byte, timeout, 1);
+ dev_dbg(&ps2dev->serio->dev, "%02x - %x\n", byte, ps2dev->nak);
- serio_pause_rx(ps2dev->serio);
- ps2dev->flags &= ~PS2_FLAG_ACK;
serio_continue_rx(ps2dev->serio);
- return -ps2dev->nak;
+ return retval;
}
EXPORT_SYMBOL(ps2_sendbyte);
@@ -75,7 +123,7 @@ EXPORT_SYMBOL(ps2_end_command);
* and discards them.
*/
-void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout)
+void ps2_drain(struct ps2dev *ps2dev, size_t maxbytes, unsigned int timeout)
{
if (maxbytes > sizeof(ps2dev->cmdbuf)) {
WARN_ON(1);
@@ -102,9 +150,9 @@ EXPORT_SYMBOL(ps2_drain);
* known keyboard IDs.
*/
-int ps2_is_keyboard_id(char id_byte)
+bool ps2_is_keyboard_id(u8 id_byte)
{
- static const char keyboard_ids[] = {
+ static const u8 keyboard_ids[] = {
0xab, /* Regular keyboards */
0xac, /* NCD Sun keyboard */
0x2b, /* Trust keyboard, translated */
@@ -123,49 +171,50 @@ EXPORT_SYMBOL(ps2_is_keyboard_id);
* completion.
*/
-static int ps2_adjust_timeout(struct ps2dev *ps2dev, int command, int timeout)
+static int ps2_adjust_timeout(struct ps2dev *ps2dev,
+ unsigned int command, unsigned int timeout)
{
switch (command) {
- case PS2_CMD_RESET_BAT:
- /*
- * Device has sent the first response byte after
- * reset command, reset is thus done, so we can
- * shorten the timeout.
- * The next byte will come soon (keyboard) or not
- * at all (mouse).
- */
- if (timeout > msecs_to_jiffies(100))
- timeout = msecs_to_jiffies(100);
- break;
+ case PS2_CMD_RESET_BAT:
+ /*
+ * Device has sent the first response byte after
+ * reset command, reset is thus done, so we can
+ * shorten the timeout.
+ * The next byte will come soon (keyboard) or not
+ * at all (mouse).
+ */
+ if (timeout > msecs_to_jiffies(100))
+ timeout = msecs_to_jiffies(100);
+ break;
- case PS2_CMD_GETID:
- /*
- * Microsoft Natural Elite keyboard responds to
- * the GET ID command as it were a mouse, with
- * a single byte. Fail the command so atkbd will
- * use alternative probe to detect it.
- */
- if (ps2dev->cmdbuf[1] == 0xaa) {
- serio_pause_rx(ps2dev->serio);
- ps2dev->flags = 0;
- serio_continue_rx(ps2dev->serio);
- timeout = 0;
- }
-
- /*
- * If device behind the port is not a keyboard there
- * won't be 2nd byte of ID response.
- */
- if (!ps2_is_keyboard_id(ps2dev->cmdbuf[1])) {
- serio_pause_rx(ps2dev->serio);
- ps2dev->flags = ps2dev->cmdcnt = 0;
- serio_continue_rx(ps2dev->serio);
- timeout = 0;
- }
- break;
+ case PS2_CMD_GETID:
+ /*
+ * Microsoft Natural Elite keyboard responds to
+ * the GET ID command as it were a mouse, with
+ * a single byte. Fail the command so atkbd will
+ * use alternative probe to detect it.
+ */
+ if (ps2dev->cmdbuf[1] == 0xaa) {
+ serio_pause_rx(ps2dev->serio);
+ ps2dev->flags = 0;
+ serio_continue_rx(ps2dev->serio);
+ timeout = 0;
+ }
- default:
- break;
+ /*
+ * If device behind the port is not a keyboard there
+ * won't be 2nd byte of ID response.
+ */
+ if (!ps2_is_keyboard_id(ps2dev->cmdbuf[1])) {
+ serio_pause_rx(ps2dev->serio);
+ ps2dev->flags = ps2dev->cmdcnt = 0;
+ serio_continue_rx(ps2dev->serio);
+ timeout = 0;
+ }
+ break;
+
+ default:
+ break;
}
return timeout;
@@ -178,50 +227,60 @@ static int ps2_adjust_timeout(struct ps2dev *ps2dev, int command, int timeout)
* ps2_command() can only be called from a process context
*/
-int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
+int __ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command)
{
- int timeout;
- int send = (command >> 12) & 0xf;
- int receive = (command >> 8) & 0xf;
- int rc = -1;
+ unsigned int timeout;
+ unsigned int send = (command >> 12) & 0xf;
+ unsigned int receive = (command >> 8) & 0xf;
+ int rc;
int i;
+ u8 send_param[16];
if (receive > sizeof(ps2dev->cmdbuf)) {
WARN_ON(1);
- return -1;
+ return -EINVAL;
}
if (send && !param) {
WARN_ON(1);
- return -1;
+ return -EINVAL;
}
+ memcpy(send_param, param, send);
+
serio_pause_rx(ps2dev->serio);
+
ps2dev->flags = command == PS2_CMD_GETID ? PS2_FLAG_WAITID : 0;
ps2dev->cmdcnt = receive;
if (receive && param)
for (i = 0; i < receive; i++)
ps2dev->cmdbuf[(receive - 1) - i] = param[i];
- serio_continue_rx(ps2dev->serio);
+
+ /* Signal that we are sending the command byte */
+ ps2dev->flags |= PS2_FLAG_ACK_CMD;
/*
* Some devices (Synaptics) peform the reset before
* ACKing the reset command, and so it can take a long
* time before the ACK arrives.
*/
- if (ps2_sendbyte(ps2dev, command & 0xff,
- command == PS2_CMD_RESET_BAT ? 1000 : 200)) {
- serio_pause_rx(ps2dev->serio);
+ timeout = command == PS2_CMD_RESET_BAT ? 1000 : 200;
+
+ rc = ps2_do_sendbyte(ps2dev, command & 0xff, timeout, 2);
+ if (rc)
goto out_reset_flags;
- }
+
+ /* Now we are sending command parameters, if any */
+ ps2dev->flags &= ~PS2_FLAG_ACK_CMD;
for (i = 0; i < send; i++) {
- if (ps2_sendbyte(ps2dev, param[i], 200)) {
- serio_pause_rx(ps2dev->serio);
+ rc = ps2_do_sendbyte(ps2dev, param[i], 200, 2);
+ if (rc)
goto out_reset_flags;
- }
}
+ serio_continue_rx(ps2dev->serio);
+
/*
* The reset command takes a long time to execute.
*/
@@ -243,8 +302,11 @@ int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
for (i = 0; i < receive; i++)
param[i] = ps2dev->cmdbuf[(receive - 1) - i];
- if (ps2dev->cmdcnt && (command != PS2_CMD_RESET_BAT || ps2dev->cmdcnt != 1))
+ if (ps2dev->cmdcnt &&
+ (command != PS2_CMD_RESET_BAT || ps2dev->cmdcnt != 1)) {
+ rc = -EPROTO;
goto out_reset_flags;
+ }
rc = 0;
@@ -252,11 +314,21 @@ int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
ps2dev->flags = 0;
serio_continue_rx(ps2dev->serio);
- return rc;
+ dev_dbg(&ps2dev->serio->dev,
+ "%02x [%*ph] - %x/%08lx [%*ph]\n",
+ command & 0xff, send, send_param,
+ ps2dev->nak, ps2dev->flags,
+ receive, param ?: send_param);
+
+ /*
+ * ps_command() handles resends itself, so do not leak -EAGAIN
+ * to the callers.
+ */
+ return rc != -EAGAIN ? rc : -EPROTO;
}
EXPORT_SYMBOL(__ps2_command);
-int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
+int ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command)
{
int rc;
@@ -269,6 +341,39 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
EXPORT_SYMBOL(ps2_command);
/*
+ * ps2_sliced_command() sends an extended PS/2 command to the mouse
+ * using sliced syntax, understood by advanced devices, such as Logitech
+ * or Synaptics touchpads. The command is encoded as:
+ * 0xE6 0xE8 rr 0xE8 ss 0xE8 tt 0xE8 uu where (rr*64)+(ss*16)+(tt*4)+uu
+ * is the command.
+ */
+
+int ps2_sliced_command(struct ps2dev *ps2dev, u8 command)
+{
+ int i;
+ int retval;
+
+ ps2_begin_command(ps2dev);
+
+ retval = __ps2_command(ps2dev, NULL, PS2_CMD_SETSCALE11);
+ if (retval)
+ goto out;
+
+ for (i = 6; i >= 0; i -= 2) {
+ u8 d = (command >> i) & 3;
+ retval = __ps2_command(ps2dev, &d, PS2_CMD_SETRES);
+ if (retval)
+ break;
+ }
+
+out:
+ dev_dbg(&ps2dev->serio->dev, "%02x - %d\n", command, retval);
+ ps2_end_command(ps2dev);
+ return retval;
+}
+EXPORT_SYMBOL(ps2_sliced_command);
+
+/*
* ps2_init() initializes ps2dev structure
*/
@@ -286,42 +391,53 @@ EXPORT_SYMBOL(ps2_init);
* to properly process ACK/NAK of a command from a PS/2 device.
*/
-int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data)
+bool ps2_handle_ack(struct ps2dev *ps2dev, u8 data)
{
switch (data) {
- case PS2_RET_ACK:
- ps2dev->nak = 0;
+ case PS2_RET_ACK:
+ ps2dev->nak = 0;
+ break;
+
+ case PS2_RET_NAK:
+ ps2dev->flags |= PS2_FLAG_NAK;
+ ps2dev->nak = PS2_RET_NAK;
+ break;
+
+ case PS2_RET_ERR:
+ if (ps2dev->flags & PS2_FLAG_NAK) {
+ ps2dev->flags &= ~PS2_FLAG_NAK;
+ ps2dev->nak = PS2_RET_ERR;
break;
+ }
- case PS2_RET_NAK:
- ps2dev->flags |= PS2_FLAG_NAK;
- ps2dev->nak = PS2_RET_NAK;
+ /*
+ * Workaround for mice which don't ACK the Get ID command.
+ * These are valid mouse IDs that we recognize.
+ */
+ case 0x00:
+ case 0x03:
+ case 0x04:
+ if (ps2dev->flags & PS2_FLAG_WAITID) {
+ ps2dev->nak = 0;
break;
-
- case PS2_RET_ERR:
- if (ps2dev->flags & PS2_FLAG_NAK) {
- ps2dev->flags &= ~PS2_FLAG_NAK;
- ps2dev->nak = PS2_RET_ERR;
- break;
- }
-
+ }
+ /* Fall through */
+ default:
/*
- * Workaround for mice which don't ACK the Get ID command.
- * These are valid mouse IDs that we recognize.
+ * Do not signal errors if we get unexpected reply while
+ * waiting for an ACK to the initial (first) command byte:
+ * the device might not be quiesced yet and continue
+ * delivering data.
+ * Note that we reset PS2_FLAG_WAITID flag, so the workaround
+ * for mice not acknowledging the Get ID command only triggers
+ * on the 1st byte; if device spews data we really want to see
+ * a real ACK from it.
*/
- case 0x00:
- case 0x03:
- case 0x04:
- if (ps2dev->flags & PS2_FLAG_WAITID) {
- ps2dev->nak = 0;
- break;
- }
- /* Fall through */
- default:
- return 0;
+ dev_dbg(&ps2dev->serio->dev, "unexpected %#02x\n", data);
+ ps2dev->flags &= ~PS2_FLAG_WAITID;
+ return ps2dev->flags & PS2_FLAG_ACK_CMD;
}
-
if (!ps2dev->nak) {
ps2dev->flags &= ~PS2_FLAG_NAK;
if (ps2dev->cmdcnt)
@@ -334,7 +450,7 @@ int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data)
if (data != PS2_RET_ACK)
ps2_handle_response(ps2dev, data);
- return 1;
+ return true;
}
EXPORT_SYMBOL(ps2_handle_ack);
@@ -344,7 +460,7 @@ EXPORT_SYMBOL(ps2_handle_ack);
* waiting for completion of the command.
*/
-int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data)
+bool ps2_handle_response(struct ps2dev *ps2dev, u8 data)
{
if (ps2dev->cmdcnt)
ps2dev->cmdbuf[--ps2dev->cmdcnt] = data;
@@ -360,7 +476,7 @@ int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data)
wake_up(&ps2dev->wait);
}
- return 1;
+ return true;
}
EXPORT_SYMBOL(ps2_handle_response);
diff --git a/drivers/input/tablet/pegasus_notetaker.c b/drivers/input/tablet/pegasus_notetaker.c
index 47de5a81172f..ffd03cfe3131 100644
--- a/drivers/input/tablet/pegasus_notetaker.c
+++ b/drivers/input/tablet/pegasus_notetaker.c
@@ -41,6 +41,7 @@
#include <linux/usb/input.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
+#include <linux/mutex.h>
/* USB HID defines */
#define USB_REQ_GET_REPORT 0x01
@@ -76,6 +77,11 @@ struct pegasus {
struct usb_device *usbdev;
struct usb_interface *intf;
struct urb *irq;
+
+ /* serialize access to open/suspend */
+ struct mutex pm_mutex;
+ bool is_open;
+
char name[128];
char phys[64];
struct work_struct init;
@@ -216,6 +222,7 @@ static int pegasus_open(struct input_dev *dev)
if (error)
return error;
+ mutex_lock(&pegasus->pm_mutex);
pegasus->irq->dev = pegasus->usbdev;
if (usb_submit_urb(pegasus->irq, GFP_KERNEL)) {
error = -EIO;
@@ -226,12 +233,15 @@ static int pegasus_open(struct input_dev *dev)
if (error)
goto err_kill_urb;
+ pegasus->is_open = true;
+ mutex_unlock(&pegasus->pm_mutex);
return 0;
err_kill_urb:
usb_kill_urb(pegasus->irq);
cancel_work_sync(&pegasus->init);
err_autopm_put:
+ mutex_unlock(&pegasus->pm_mutex);
usb_autopm_put_interface(pegasus->intf);
return error;
}
@@ -240,8 +250,12 @@ static void pegasus_close(struct input_dev *dev)
{
struct pegasus *pegasus = input_get_drvdata(dev);
+ mutex_lock(&pegasus->pm_mutex);
usb_kill_urb(pegasus->irq);
cancel_work_sync(&pegasus->init);
+ pegasus->is_open = false;
+ mutex_unlock(&pegasus->pm_mutex);
+
usb_autopm_put_interface(pegasus->intf);
}
@@ -274,6 +288,8 @@ static int pegasus_probe(struct usb_interface *intf,
goto err_free_mem;
}
+ mutex_init(&pegasus->pm_mutex);
+
pegasus->usbdev = dev;
pegasus->dev = input_dev;
pegasus->intf = intf;
@@ -388,10 +404,10 @@ static int pegasus_suspend(struct usb_interface *intf, pm_message_t message)
{
struct pegasus *pegasus = usb_get_intfdata(intf);
- mutex_lock(&pegasus->dev->mutex);
+ mutex_lock(&pegasus->pm_mutex);
usb_kill_urb(pegasus->irq);
cancel_work_sync(&pegasus->init);
- mutex_unlock(&pegasus->dev->mutex);
+ mutex_unlock(&pegasus->pm_mutex);
return 0;
}
@@ -401,10 +417,10 @@ static int pegasus_resume(struct usb_interface *intf)
struct pegasus *pegasus = usb_get_intfdata(intf);
int retval = 0;
- mutex_lock(&pegasus->dev->mutex);
- if (pegasus->dev->users && usb_submit_urb(pegasus->irq, GFP_NOIO) < 0)
+ mutex_lock(&pegasus->pm_mutex);
+ if (pegasus->is_open && usb_submit_urb(pegasus->irq, GFP_NOIO) < 0)
retval = -EIO;
- mutex_unlock(&pegasus->dev->mutex);
+ mutex_unlock(&pegasus->pm_mutex);
return retval;
}
@@ -414,14 +430,14 @@ static int pegasus_reset_resume(struct usb_interface *intf)
struct pegasus *pegasus = usb_get_intfdata(intf);
int retval = 0;
- mutex_lock(&pegasus->dev->mutex);
- if (pegasus->dev->users) {
+ mutex_lock(&pegasus->pm_mutex);
+ if (pegasus->is_open) {
retval = pegasus_set_mode(pegasus, PEN_MODE_XY,
NOTETAKER_LED_MOUSE);
if (!retval && usb_submit_urb(pegasus->irq, GFP_NOIO) < 0)
retval = -EIO;
}
- mutex_unlock(&pegasus->dev->mutex);
+ mutex_unlock(&pegasus->pm_mutex);
return retval;
}
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 7659bc48f1db..5d9699fe1b55 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -23,12 +23,13 @@
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/i2c.h>
-#include <linux/platform_data/atmel_mxt_ts.h>
#include <linux/input/mt.h>
#include <linux/interrupt.h>
#include <linux/of.h>
+#include <linux/property.h>
#include <linux/slab.h>
#include <linux/gpio/consumer.h>
+#include <linux/property.h>
#include <asm/unaligned.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
@@ -268,12 +269,16 @@ static const struct v4l2_file_operations mxt_video_fops = {
.poll = vb2_fop_poll,
};
+enum mxt_suspend_mode {
+ MXT_SUSPEND_DEEP_SLEEP = 0,
+ MXT_SUSPEND_T9_CTRL = 1,
+};
+
/* Each client has this additional data */
struct mxt_data {
struct i2c_client *client;
struct input_dev *input_dev;
char phys[64]; /* device physical location */
- const struct mxt_platform_data *pdata;
struct mxt_object *object_table;
struct mxt_info info;
unsigned int irq;
@@ -324,6 +329,11 @@ struct mxt_data {
/* for config update handling */
struct completion crc_completion;
+
+ u32 *t19_keymap;
+ unsigned int t19_num_keys;
+
+ enum mxt_suspend_mode suspend_mode;
};
struct mxt_vb2_buffer {
@@ -742,15 +752,14 @@ static int mxt_write_object(struct mxt_data *data,
static void mxt_input_button(struct mxt_data *data, u8 *message)
{
struct input_dev *input = data->input_dev;
- const struct mxt_platform_data *pdata = data->pdata;
int i;
- for (i = 0; i < pdata->t19_num_keys; i++) {
- if (pdata->t19_keymap[i] == KEY_RESERVED)
+ for (i = 0; i < data->t19_num_keys; i++) {
+ if (data->t19_keymap[i] == KEY_RESERVED)
continue;
/* Active-low switch */
- input_report_key(input, pdata->t19_keymap[i],
+ input_report_key(input, data->t19_keymap[i],
!(message[1] & BIT(i)));
}
}
@@ -758,7 +767,7 @@ static void mxt_input_button(struct mxt_data *data, u8 *message)
static void mxt_input_sync(struct mxt_data *data)
{
input_mt_report_pointer_emulation(data->input_dev,
- data->pdata->t19_num_keys);
+ data->t19_num_keys);
input_sync(data->input_dev);
}
@@ -1858,7 +1867,6 @@ static void mxt_input_close(struct input_dev *dev);
static void mxt_set_up_as_touchpad(struct input_dev *input_dev,
struct mxt_data *data)
{
- const struct mxt_platform_data *pdata = data->pdata;
int i;
input_dev->name = "Atmel maXTouch Touchpad";
@@ -1872,15 +1880,14 @@ static void mxt_set_up_as_touchpad(struct input_dev *input_dev,
input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
MXT_PIXELS_PER_MM);
- for (i = 0; i < pdata->t19_num_keys; i++)
- if (pdata->t19_keymap[i] != KEY_RESERVED)
+ for (i = 0; i < data->t19_num_keys; i++)
+ if (data->t19_keymap[i] != KEY_RESERVED)
input_set_capability(input_dev, EV_KEY,
- pdata->t19_keymap[i]);
+ data->t19_keymap[i]);
}
static int mxt_initialize_input_device(struct mxt_data *data)
{
- const struct mxt_platform_data *pdata = data->pdata;
struct device *dev = &data->client->dev;
struct input_dev *input_dev;
int error;
@@ -1946,7 +1953,7 @@ static int mxt_initialize_input_device(struct mxt_data *data)
}
/* If device has buttons we assume it is a touchpad */
- if (pdata->t19_num_keys) {
+ if (data->t19_num_keys) {
mxt_set_up_as_touchpad(input_dev, data);
mt_flags |= INPUT_MT_POINTER;
} else {
@@ -2868,7 +2875,7 @@ static const struct attribute_group mxt_attr_group = {
static void mxt_start(struct mxt_data *data)
{
- switch (data->pdata->suspend_mode) {
+ switch (data->suspend_mode) {
case MXT_SUSPEND_T9_CTRL:
mxt_soft_reset(data);
@@ -2886,12 +2893,11 @@ static void mxt_start(struct mxt_data *data)
mxt_t6_command(data, MXT_COMMAND_CALIBRATE, 1, false);
break;
}
-
}
static void mxt_stop(struct mxt_data *data)
{
- switch (data->pdata->suspend_mode) {
+ switch (data->suspend_mode) {
case MXT_SUSPEND_T9_CTRL:
/* Touch disable */
mxt_write_object(data,
@@ -2921,55 +2927,49 @@ static void mxt_input_close(struct input_dev *dev)
mxt_stop(data);
}
-#ifdef CONFIG_OF
-static const struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
+static int mxt_parse_device_properties(struct mxt_data *data)
{
- struct mxt_platform_data *pdata;
- struct device_node *np = client->dev.of_node;
+ static const char keymap_property[] = "linux,gpio-keymap";
+ struct device *dev = &data->client->dev;
u32 *keymap;
- int proplen, ret;
-
- if (!np)
- return ERR_PTR(-ENOENT);
-
- pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return ERR_PTR(-ENOMEM);
+ int n_keys;
+ int error;
- if (of_find_property(np, "linux,gpio-keymap", &proplen)) {
- pdata->t19_num_keys = proplen / sizeof(u32);
+ if (device_property_present(dev, keymap_property)) {
+ n_keys = device_property_read_u32_array(dev, keymap_property,
+ NULL, 0);
+ if (n_keys <= 0) {
+ error = n_keys < 0 ? n_keys : -EINVAL;
+ dev_err(dev, "invalid/malformed '%s' property: %d\n",
+ keymap_property, error);
+ return error;
+ }
- keymap = devm_kzalloc(&client->dev,
- pdata->t19_num_keys * sizeof(keymap[0]),
- GFP_KERNEL);
+ keymap = devm_kmalloc_array(dev, n_keys, sizeof(*keymap),
+ GFP_KERNEL);
if (!keymap)
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
- ret = of_property_read_u32_array(np, "linux,gpio-keymap",
- keymap, pdata->t19_num_keys);
- if (ret)
- dev_warn(&client->dev,
- "Couldn't read linux,gpio-keymap: %d\n", ret);
+ error = device_property_read_u32_array(dev, keymap_property,
+ keymap, n_keys);
+ if (error) {
+ dev_err(dev, "failed to parse '%s' property: %d\n",
+ keymap_property, error);
+ return error;
+ }
- pdata->t19_keymap = keymap;
+ data->t19_keymap = keymap;
+ data->t19_num_keys = n_keys;
}
- pdata->suspend_mode = MXT_SUSPEND_DEEP_SLEEP;
-
- return pdata;
-}
-#else
-static const struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
-{
- return ERR_PTR(-ENOENT);
+ return 0;
}
-#endif
#ifdef CONFIG_ACPI
struct mxt_acpi_platform_data {
const char *hid;
- struct mxt_platform_data pdata;
+ const struct property_entry *props;
};
static unsigned int samus_touchpad_buttons[] = {
@@ -2979,14 +2979,16 @@ static unsigned int samus_touchpad_buttons[] = {
BTN_LEFT
};
+static const struct property_entry samus_touchpad_props[] = {
+ PROPERTY_ENTRY_U32_ARRAY("linux,gpio-keymap", samus_touchpad_buttons),
+ { }
+};
+
static struct mxt_acpi_platform_data samus_platform_data[] = {
{
/* Touchpad */
.hid = "ATML0000",
- .pdata = {
- .t19_num_keys = ARRAY_SIZE(samus_touchpad_buttons),
- .t19_keymap = samus_touchpad_buttons,
- },
+ .props = samus_touchpad_props,
},
{
/* Touchscreen */
@@ -3004,14 +3006,16 @@ static unsigned int chromebook_tp_buttons[] = {
BTN_LEFT
};
+static const struct property_entry chromebook_tp_props[] = {
+ PROPERTY_ENTRY_U32_ARRAY("linux,gpio-keymap", chromebook_tp_buttons),
+ { }
+};
+
static struct mxt_acpi_platform_data chromebook_platform_data[] = {
{
/* Touchpad */
.hid = "ATML0000",
- .pdata = {
- .t19_num_keys = ARRAY_SIZE(chromebook_tp_buttons),
- .t19_keymap = chromebook_tp_buttons,
- },
+ .props = chromebook_tp_props,
},
{
/* Touchscreen */
@@ -3041,83 +3045,85 @@ static const struct dmi_system_id mxt_dmi_table[] = {
{ }
};
-static const struct mxt_platform_data *mxt_parse_acpi(struct i2c_client *client)
+static int mxt_prepare_acpi_properties(struct i2c_client *client)
{
struct acpi_device *adev;
const struct dmi_system_id *system_id;
const struct mxt_acpi_platform_data *acpi_pdata;
- /*
- * Ignore ACPI devices representing bootloader mode.
- *
- * This is a bit of a hack: Google Chromebook BIOS creates ACPI
- * devices for both application and bootloader modes, but we are
- * interested in application mode only (if device is in bootloader
- * mode we'll end up switching into application anyway). So far
- * application mode addresses were all above 0x40, so we'll use it
- * as a threshold.
- */
- if (client->addr < 0x40)
- return ERR_PTR(-ENXIO);
-
adev = ACPI_COMPANION(&client->dev);
if (!adev)
- return ERR_PTR(-ENOENT);
+ return -ENOENT;
system_id = dmi_first_match(mxt_dmi_table);
if (!system_id)
- return ERR_PTR(-ENOENT);
+ return -ENOENT;
acpi_pdata = system_id->driver_data;
if (!acpi_pdata)
- return ERR_PTR(-ENOENT);
+ return -ENOENT;
while (acpi_pdata->hid) {
- if (!strcmp(acpi_device_hid(adev), acpi_pdata->hid))
- return &acpi_pdata->pdata;
+ if (!strcmp(acpi_device_hid(adev), acpi_pdata->hid)) {
+ /*
+ * Remove previously installed properties if we
+ * are probing this device not for the very first
+ * time.
+ */
+ device_remove_properties(&client->dev);
+
+ /*
+ * Now install the platform-specific properties
+ * that are missing from ACPI.
+ */
+ device_add_properties(&client->dev, acpi_pdata->props);
+ break;
+ }
acpi_pdata++;
}
- return ERR_PTR(-ENOENT);
+ return 0;
}
#else
-static const struct mxt_platform_data *mxt_parse_acpi(struct i2c_client *client)
+static int mxt_prepare_acpi_properties(struct i2c_client *client)
{
- return ERR_PTR(-ENOENT);
+ return -ENOENT;
}
#endif
-static const struct mxt_platform_data *
-mxt_get_platform_data(struct i2c_client *client)
-{
- const struct mxt_platform_data *pdata;
-
- pdata = dev_get_platdata(&client->dev);
- if (pdata)
- return pdata;
-
- pdata = mxt_parse_dt(client);
- if (!IS_ERR(pdata) || PTR_ERR(pdata) != -ENOENT)
- return pdata;
-
- pdata = mxt_parse_acpi(client);
- if (!IS_ERR(pdata) || PTR_ERR(pdata) != -ENOENT)
- return pdata;
-
- dev_err(&client->dev, "No platform data specified\n");
- return ERR_PTR(-EINVAL);
-}
+static const struct dmi_system_id chromebook_T9_suspend_dmi[] = {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Peppy"),
+ },
+ },
+ { }
+};
static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct mxt_data *data;
- const struct mxt_platform_data *pdata;
int error;
- pdata = mxt_get_platform_data(client);
- if (IS_ERR(pdata))
- return PTR_ERR(pdata);
+ /*
+ * Ignore ACPI devices representing bootloader mode.
+ *
+ * This is a bit of a hack: Google Chromebook BIOS creates ACPI
+ * devices for both application and bootloader modes, but we are
+ * interested in application mode only (if device is in bootloader
+ * mode we'll end up switching into application anyway). So far
+ * application mode addresses were all above 0x40, so we'll use it
+ * as a threshold.
+ */
+ if (ACPI_COMPANION(&client->dev) && client->addr < 0x40)
+ return -ENXIO;
data = devm_kzalloc(&client->dev, sizeof(struct mxt_data), GFP_KERNEL);
if (!data)
@@ -3127,7 +3133,6 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
client->adapter->nr, client->addr);
data->client = client;
- data->pdata = pdata;
data->irq = client->irq;
i2c_set_clientdata(client, data);
@@ -3135,6 +3140,17 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
init_completion(&data->reset_completion);
init_completion(&data->crc_completion);
+ data->suspend_mode = dmi_check_system(chromebook_T9_suspend_dmi) ?
+ MXT_SUSPEND_T9_CTRL : MXT_SUSPEND_DEEP_SLEEP;
+
+ error = mxt_prepare_acpi_properties(client);
+ if (error && error != -ENOENT)
+ return error;
+
+ error = mxt_parse_device_properties(data);
+ if (error)
+ return error;
+
data->reset_gpio = devm_gpiod_get_optional(&client->dev,
"reset", GPIOD_OUT_LOW);
if (IS_ERR(data->reset_gpio)) {
@@ -3144,8 +3160,7 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
}
error = devm_request_threaded_irq(&client->dev, client->irq,
- NULL, mxt_interrupt,
- pdata->irqflags | IRQF_ONESHOT,
+ NULL, mxt_interrupt, IRQF_ONESHOT,
client->name, data);
if (error) {
dev_err(&client->dev, "Failed to register interrupt\n");
@@ -3265,7 +3280,7 @@ MODULE_DEVICE_TABLE(i2c, mxt_id);
static struct i2c_driver mxt_driver = {
.driver = {
.name = "atmel_mxt_ts",
- .of_match_table = of_match_ptr(mxt_of_match),
+ .of_match_table = mxt_of_match,
.acpi_match_table = ACPI_PTR(mxt_acpi_id),
.pm = &mxt_pm_ops,
},
diff --git a/drivers/input/touchscreen/s6sy761.c b/drivers/input/touchscreen/s6sy761.c
index 675efa93d444..b63d7fdf0cd2 100644
--- a/drivers/input/touchscreen/s6sy761.c
+++ b/drivers/input/touchscreen/s6sy761.c
@@ -2,7 +2,7 @@
// Samsung S6SY761 Touchscreen device driver
//
// Copyright (c) 2017 Samsung Electronics Co., Ltd.
-// Copyright (c) 2017 Andi Shyti <andi.shyti@samsung.com>
+// Copyright (c) 2017 Andi Shyti <andi@etezian.org>
#include <asm/unaligned.h>
#include <linux/delay.h>
diff --git a/drivers/input/touchscreen/silead.c b/drivers/input/touchscreen/silead.c
index 646b1e768e6b..ff7043f74a3d 100644
--- a/drivers/input/touchscreen/silead.c
+++ b/drivers/input/touchscreen/silead.c
@@ -602,6 +602,7 @@ static const struct acpi_device_id silead_ts_acpi_match[] = {
{ "GSL3675", 0 },
{ "GSL3692", 0 },
{ "MSSL1680", 0 },
+ { "MSSL0001", 0 },
{ }
};
MODULE_DEVICE_TABLE(acpi, silead_ts_acpi_match);
diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c
index efdb1a75a163..704e99046916 100644
--- a/drivers/input/touchscreen/stmfts.c
+++ b/drivers/input/touchscreen/stmfts.c
@@ -2,7 +2,7 @@
// STMicroelectronics FTS Touchscreen device driver
//
// Copyright (c) 2017 Samsung Electronics Co., Ltd.
-// Copyright (c) 2017 Andi Shyti <andi.shyti@samsung.com>
+// Copyright (c) 2017 Andi Shyti <andi@etezian.org>
#include <linux/delay.h>
#include <linux/i2c.h>
@@ -730,6 +730,7 @@ static int stmfts_probe(struct i2c_client *client,
return err;
pm_runtime_enable(&client->dev);
+ device_enable_async_suspend(&client->dev);
return 0;
}
@@ -805,6 +806,7 @@ static struct i2c_driver stmfts_driver = {
.name = STMFTS_DEV_NAME,
.of_match_table = of_match_ptr(stmfts_of_match),
.pm = &stmfts_pm_ops,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
.probe = stmfts_probe,
.remove = stmfts_remove,
diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c
index f16f8358c70a..894843a7ec7b 100644
--- a/drivers/input/touchscreen/sur40.c
+++ b/drivers/input/touchscreen/sur40.c
@@ -38,6 +38,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-dma-sg.h>
@@ -81,7 +82,10 @@ struct sur40_blob {
__le32 area; /* size in pixels/pressure (?) */
- u8 padding[32];
+ u8 padding[24];
+
+ __le32 tag_id; /* valid when type == 0x04 (SUR40_TAG) */
+ __le32 unknown;
} __packed;
@@ -146,6 +150,40 @@ struct sur40_image_header {
#define SUR40_TOUCH 0x02
#define SUR40_TAG 0x04
+/* video controls */
+#define SUR40_BRIGHTNESS_MAX 0xff
+#define SUR40_BRIGHTNESS_MIN 0x00
+#define SUR40_BRIGHTNESS_DEF 0xff
+
+#define SUR40_CONTRAST_MAX 0x0f
+#define SUR40_CONTRAST_MIN 0x00
+#define SUR40_CONTRAST_DEF 0x0a
+
+#define SUR40_GAIN_MAX 0x09
+#define SUR40_GAIN_MIN 0x00
+#define SUR40_GAIN_DEF 0x08
+
+#define SUR40_BACKLIGHT_MAX 0x01
+#define SUR40_BACKLIGHT_MIN 0x00
+#define SUR40_BACKLIGHT_DEF 0x01
+
+#define sur40_str(s) #s
+#define SUR40_PARAM_RANGE(lo, hi) " (range " sur40_str(lo) "-" sur40_str(hi) ")"
+
+/* module parameters */
+static uint brightness = SUR40_BRIGHTNESS_DEF;
+module_param(brightness, uint, 0644);
+MODULE_PARM_DESC(brightness, "set initial brightness"
+ SUR40_PARAM_RANGE(SUR40_BRIGHTNESS_MIN, SUR40_BRIGHTNESS_MAX));
+static uint contrast = SUR40_CONTRAST_DEF;
+module_param(contrast, uint, 0644);
+MODULE_PARM_DESC(contrast, "set initial contrast"
+ SUR40_PARAM_RANGE(SUR40_CONTRAST_MIN, SUR40_CONTRAST_MAX));
+static uint gain = SUR40_GAIN_DEF;
+module_param(gain, uint, 0644);
+MODULE_PARM_DESC(gain, "set initial gain"
+ SUR40_PARAM_RANGE(SUR40_GAIN_MIN, SUR40_GAIN_MAX));
+
static const struct v4l2_pix_format sur40_pix_format[] = {
{
.pixelformat = V4L2_TCH_FMT_TU08,
@@ -178,6 +216,7 @@ struct sur40_state {
struct video_device vdev;
struct mutex lock;
struct v4l2_pix_format pix_fmt;
+ struct v4l2_ctrl_handler hdl;
struct vb2_queue queue;
struct list_head buf_list;
@@ -187,6 +226,7 @@ struct sur40_state {
struct sur40_data *bulk_in_buffer;
size_t bulk_in_size;
u8 bulk_in_epaddr;
+ u8 vsvideo;
char phys[64];
};
@@ -200,6 +240,11 @@ struct sur40_buffer {
static const struct video_device sur40_video_device;
static const struct vb2_queue sur40_queue;
static void sur40_process_video(struct sur40_state *sur40);
+static int sur40_s_ctrl(struct v4l2_ctrl *ctrl);
+
+static const struct v4l2_ctrl_ops sur40_ctrl_ops = {
+ .s_ctrl = sur40_s_ctrl,
+};
/*
* Note: an earlier, non-public version of this driver used USB_RECIP_ENDPOINT
@@ -220,6 +265,81 @@ static int sur40_command(struct sur40_state *dev,
0x00, index, buffer, size, 1000);
}
+/* poke a byte in the panel register space */
+static int sur40_poke(struct sur40_state *dev, u8 offset, u8 value)
+{
+ int result;
+ u8 index = 0x96; // 0xae for permanent write
+
+ result = usb_control_msg(dev->usbdev, usb_sndctrlpipe(dev->usbdev, 0),
+ SUR40_POKE, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+ 0x32, index, NULL, 0, 1000);
+ if (result < 0)
+ goto error;
+ msleep(5);
+
+ result = usb_control_msg(dev->usbdev, usb_sndctrlpipe(dev->usbdev, 0),
+ SUR40_POKE, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+ 0x72, offset, NULL, 0, 1000);
+ if (result < 0)
+ goto error;
+ msleep(5);
+
+ result = usb_control_msg(dev->usbdev, usb_sndctrlpipe(dev->usbdev, 0),
+ SUR40_POKE, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+ 0xb2, value, NULL, 0, 1000);
+ if (result < 0)
+ goto error;
+ msleep(5);
+
+error:
+ return result;
+}
+
+static int sur40_set_preprocessor(struct sur40_state *dev, u8 value)
+{
+ u8 setting_07[2] = { 0x01, 0x00 };
+ u8 setting_17[2] = { 0x85, 0x80 };
+ int result;
+
+ if (value > 1)
+ return -ERANGE;
+
+ result = usb_control_msg(dev->usbdev, usb_sndctrlpipe(dev->usbdev, 0),
+ SUR40_POKE, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+ 0x07, setting_07[value], NULL, 0, 1000);
+ if (result < 0)
+ goto error;
+ msleep(5);
+
+ result = usb_control_msg(dev->usbdev, usb_sndctrlpipe(dev->usbdev, 0),
+ SUR40_POKE, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+ 0x17, setting_17[value], NULL, 0, 1000);
+ if (result < 0)
+ goto error;
+ msleep(5);
+
+error:
+ return result;
+}
+
+static void sur40_set_vsvideo(struct sur40_state *handle, u8 value)
+{
+ int i;
+
+ for (i = 0; i < 4; i++)
+ sur40_poke(handle, 0x1c+i, value);
+ handle->vsvideo = value;
+}
+
+static void sur40_set_irlevel(struct sur40_state *handle, u8 value)
+{
+ int i;
+
+ for (i = 0; i < 8; i++)
+ sur40_poke(handle, 0x08+(2*i), value);
+}
+
/* Initialization routine, called from sur40_open */
static int sur40_init(struct sur40_state *dev)
{
@@ -631,6 +751,36 @@ static int sur40_probe(struct usb_interface *interface,
sur40->vdev.queue = &sur40->queue;
video_set_drvdata(&sur40->vdev, sur40);
+ /* initialize the control handler for 4 controls */
+ v4l2_ctrl_handler_init(&sur40->hdl, 4);
+ sur40->v4l2.ctrl_handler = &sur40->hdl;
+ sur40->vsvideo = (SUR40_CONTRAST_DEF << 4) | SUR40_GAIN_DEF;
+
+ v4l2_ctrl_new_std(&sur40->hdl, &sur40_ctrl_ops, V4L2_CID_BRIGHTNESS,
+ SUR40_BRIGHTNESS_MIN, SUR40_BRIGHTNESS_MAX, 1, clamp(brightness,
+ (uint)SUR40_BRIGHTNESS_MIN, (uint)SUR40_BRIGHTNESS_MAX));
+
+ v4l2_ctrl_new_std(&sur40->hdl, &sur40_ctrl_ops, V4L2_CID_CONTRAST,
+ SUR40_CONTRAST_MIN, SUR40_CONTRAST_MAX, 1, clamp(contrast,
+ (uint)SUR40_CONTRAST_MIN, (uint)SUR40_CONTRAST_MAX));
+
+ v4l2_ctrl_new_std(&sur40->hdl, &sur40_ctrl_ops, V4L2_CID_GAIN,
+ SUR40_GAIN_MIN, SUR40_GAIN_MAX, 1, clamp(gain,
+ (uint)SUR40_GAIN_MIN, (uint)SUR40_GAIN_MAX));
+
+ v4l2_ctrl_new_std(&sur40->hdl, &sur40_ctrl_ops,
+ V4L2_CID_BACKLIGHT_COMPENSATION, SUR40_BACKLIGHT_MIN,
+ SUR40_BACKLIGHT_MAX, 1, SUR40_BACKLIGHT_DEF);
+
+ v4l2_ctrl_handler_setup(&sur40->hdl);
+
+ if (sur40->hdl.error) {
+ dev_err(&interface->dev,
+ "Unable to register video controls.");
+ v4l2_ctrl_handler_free(&sur40->hdl);
+ goto err_unreg_v4l2;
+ }
+
error = video_register_device(&sur40->vdev, VFL_TYPE_TOUCH, -1);
if (error) {
dev_err(&interface->dev,
@@ -663,6 +813,7 @@ static void sur40_disconnect(struct usb_interface *interface)
{
struct sur40_state *sur40 = usb_get_intfdata(interface);
+ v4l2_ctrl_handler_free(&sur40->hdl);
video_unregister_device(&sur40->vdev);
v4l2_device_unregister(&sur40->v4l2);
@@ -856,6 +1007,31 @@ static int sur40_vidioc_g_fmt(struct file *file, void *priv,
return 0;
}
+static int sur40_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct sur40_state *sur40 = container_of(ctrl->handler,
+ struct sur40_state, hdl);
+ u8 value = sur40->vsvideo;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ sur40_set_irlevel(sur40, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ value = (value & 0x0f) | (ctrl->val << 4);
+ sur40_set_vsvideo(sur40, value);
+ break;
+ case V4L2_CID_GAIN:
+ value = (value & 0xf0) | (ctrl->val);
+ sur40_set_vsvideo(sur40, value);
+ break;
+ case V4L2_CID_BACKLIGHT_COMPENSATION:
+ sur40_set_preprocessor(sur40, ctrl->val);
+ break;
+ }
+ return 0;
+}
+
static int sur40_ioctl_parm(struct file *file, void *priv,
struct v4l2_streamparm *p)
{
diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c
index aa77d243b786..c6cf90868503 100644
--- a/drivers/input/touchscreen/usbtouchscreen.c
+++ b/drivers/input/touchscreen/usbtouchscreen.c
@@ -54,6 +54,7 @@
#include <linux/usb.h>
#include <linux/usb/input.h>
#include <linux/hid.h>
+#include <linux/mutex.h>
static bool swap_xy;
module_param(swap_xy, bool, 0644);
@@ -107,6 +108,8 @@ struct usbtouch_usb {
struct usb_interface *interface;
struct input_dev *input;
struct usbtouch_device_info *type;
+ struct mutex pm_mutex; /* serialize access to open/suspend */
+ bool is_open;
char name[128];
char phys[64];
void *priv;
@@ -1450,6 +1453,7 @@ static int usbtouch_open(struct input_dev *input)
if (r < 0)
goto out;
+ mutex_lock(&usbtouch->pm_mutex);
if (!usbtouch->type->irq_always) {
if (usb_submit_urb(usbtouch->irq, GFP_KERNEL)) {
r = -EIO;
@@ -1458,7 +1462,9 @@ static int usbtouch_open(struct input_dev *input)
}
usbtouch->interface->needs_remote_wakeup = 1;
+ usbtouch->is_open = true;
out_put:
+ mutex_unlock(&usbtouch->pm_mutex);
usb_autopm_put_interface(usbtouch->interface);
out:
return r;
@@ -1469,8 +1475,12 @@ static void usbtouch_close(struct input_dev *input)
struct usbtouch_usb *usbtouch = input_get_drvdata(input);
int r;
+ mutex_lock(&usbtouch->pm_mutex);
if (!usbtouch->type->irq_always)
usb_kill_urb(usbtouch->irq);
+ usbtouch->is_open = false;
+ mutex_unlock(&usbtouch->pm_mutex);
+
r = usb_autopm_get_interface(usbtouch->interface);
usbtouch->interface->needs_remote_wakeup = 0;
if (!r)
@@ -1490,13 +1500,12 @@ static int usbtouch_suspend
static int usbtouch_resume(struct usb_interface *intf)
{
struct usbtouch_usb *usbtouch = usb_get_intfdata(intf);
- struct input_dev *input = usbtouch->input;
int result = 0;
- mutex_lock(&input->mutex);
- if (input->users || usbtouch->type->irq_always)
+ mutex_lock(&usbtouch->pm_mutex);
+ if (usbtouch->is_open || usbtouch->type->irq_always)
result = usb_submit_urb(usbtouch->irq, GFP_NOIO);
- mutex_unlock(&input->mutex);
+ mutex_unlock(&usbtouch->pm_mutex);
return result;
}
@@ -1504,7 +1513,6 @@ static int usbtouch_resume(struct usb_interface *intf)
static int usbtouch_reset_resume(struct usb_interface *intf)
{
struct usbtouch_usb *usbtouch = usb_get_intfdata(intf);
- struct input_dev *input = usbtouch->input;
int err = 0;
/* reinit the device */
@@ -1519,10 +1527,10 @@ static int usbtouch_reset_resume(struct usb_interface *intf)
}
/* restart IO if needed */
- mutex_lock(&input->mutex);
- if (input->users)
+ mutex_lock(&usbtouch->pm_mutex);
+ if (usbtouch->is_open)
err = usb_submit_urb(usbtouch->irq, GFP_NOIO);
- mutex_unlock(&input->mutex);
+ mutex_unlock(&usbtouch->pm_mutex);
return err;
}
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index f3a21343e636..df171cb85822 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -107,6 +107,7 @@ config IOMMU_PGTABLES_L2
# AMD IOMMU support
config AMD_IOMMU
bool "AMD IOMMU support"
+ select DMA_DIRECT_OPS
select SWIOTLB
select PCI_MSI
select PCI_ATS
@@ -142,6 +143,7 @@ config DMAR_TABLE
config INTEL_IOMMU
bool "Support for Intel IOMMU using DMA Remapping Devices"
depends on PCI_MSI && ACPI && (X86 || IA64_GENERIC)
+ select DMA_DIRECT_OPS
select IOMMU_API
select IOMMU_IOVA
select DMAR_TABLE
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 74788fdeb773..2a99f0f14795 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -28,6 +28,7 @@
#include <linux/debugfs.h>
#include <linux/scatterlist.h>
#include <linux/dma-mapping.h>
+#include <linux/dma-direct.h>
#include <linux/iommu-helper.h>
#include <linux/iommu.h>
#include <linux/delay.h>
@@ -80,11 +81,12 @@
*/
#define AMD_IOMMU_PGSIZES ((~0xFFFUL) & ~(2ULL << 38))
-static DEFINE_RWLOCK(amd_iommu_devtable_lock);
+static DEFINE_SPINLOCK(amd_iommu_devtable_lock);
+static DEFINE_SPINLOCK(pd_bitmap_lock);
+static DEFINE_SPINLOCK(iommu_table_lock);
/* List of all available dev_data structures */
-static LIST_HEAD(dev_data_list);
-static DEFINE_SPINLOCK(dev_data_list_lock);
+static LLIST_HEAD(dev_data_list);
LIST_HEAD(ioapic_map);
LIST_HEAD(hpet_map);
@@ -203,40 +205,33 @@ static struct dma_ops_domain* to_dma_ops_domain(struct protection_domain *domain
static struct iommu_dev_data *alloc_dev_data(u16 devid)
{
struct iommu_dev_data *dev_data;
- unsigned long flags;
dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL);
if (!dev_data)
return NULL;
dev_data->devid = devid;
-
- spin_lock_irqsave(&dev_data_list_lock, flags);
- list_add_tail(&dev_data->dev_data_list, &dev_data_list);
- spin_unlock_irqrestore(&dev_data_list_lock, flags);
-
ratelimit_default_init(&dev_data->rs);
+ llist_add(&dev_data->dev_data_list, &dev_data_list);
return dev_data;
}
static struct iommu_dev_data *search_dev_data(u16 devid)
{
struct iommu_dev_data *dev_data;
- unsigned long flags;
+ struct llist_node *node;
- spin_lock_irqsave(&dev_data_list_lock, flags);
- list_for_each_entry(dev_data, &dev_data_list, dev_data_list) {
+ if (llist_empty(&dev_data_list))
+ return NULL;
+
+ node = dev_data_list.first;
+ llist_for_each_entry(dev_data, node, dev_data_list) {
if (dev_data->devid == devid)
- goto out_unlock;
+ return dev_data;
}
- dev_data = NULL;
-
-out_unlock:
- spin_unlock_irqrestore(&dev_data_list_lock, flags);
-
- return dev_data;
+ return NULL;
}
static int __last_alias(struct pci_dev *pdev, u16 alias, void *data)
@@ -310,6 +305,8 @@ static struct iommu_dev_data *find_dev_data(u16 devid)
if (dev_data == NULL) {
dev_data = alloc_dev_data(devid);
+ if (!dev_data)
+ return NULL;
if (translation_pre_enabled(iommu))
dev_data->defer_attach = true;
@@ -547,6 +544,7 @@ static void amd_iommu_report_page_fault(u16 devid, u16 domain_id,
static void iommu_print_event(struct amd_iommu *iommu, void *__evt)
{
+ struct device *dev = iommu->iommu.dev;
int type, devid, domid, flags;
volatile u32 *event = __evt;
int count = 0;
@@ -573,53 +571,53 @@ retry:
amd_iommu_report_page_fault(devid, domid, address, flags);
return;
} else {
- printk(KERN_ERR "AMD-Vi: Event logged [");
+ dev_err(dev, "AMD-Vi: Event logged [");
}
switch (type) {
case EVENT_TYPE_ILL_DEV:
- printk("ILLEGAL_DEV_TABLE_ENTRY device=%02x:%02x.%x "
- "address=0x%016llx flags=0x%04x]\n",
- PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid),
- address, flags);
+ dev_err(dev, "ILLEGAL_DEV_TABLE_ENTRY device=%02x:%02x.%x "
+ "address=0x%016llx flags=0x%04x]\n",
+ PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid),
+ address, flags);
dump_dte_entry(devid);
break;
case EVENT_TYPE_DEV_TAB_ERR:
- printk("DEV_TAB_HARDWARE_ERROR device=%02x:%02x.%x "
- "address=0x%016llx flags=0x%04x]\n",
- PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid),
- address, flags);
+ dev_err(dev, "DEV_TAB_HARDWARE_ERROR device=%02x:%02x.%x "
+ "address=0x%016llx flags=0x%04x]\n",
+ PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid),
+ address, flags);
break;
case EVENT_TYPE_PAGE_TAB_ERR:
- printk("PAGE_TAB_HARDWARE_ERROR device=%02x:%02x.%x "
- "domain=0x%04x address=0x%016llx flags=0x%04x]\n",
- PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid),
- domid, address, flags);
+ dev_err(dev, "PAGE_TAB_HARDWARE_ERROR device=%02x:%02x.%x "
+ "domain=0x%04x address=0x%016llx flags=0x%04x]\n",
+ PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid),
+ domid, address, flags);
break;
case EVENT_TYPE_ILL_CMD:
- printk("ILLEGAL_COMMAND_ERROR address=0x%016llx]\n", address);
+ dev_err(dev, "ILLEGAL_COMMAND_ERROR address=0x%016llx]\n", address);
dump_command(address);
break;
case EVENT_TYPE_CMD_HARD_ERR:
- printk("COMMAND_HARDWARE_ERROR address=0x%016llx "
- "flags=0x%04x]\n", address, flags);
+ dev_err(dev, "COMMAND_HARDWARE_ERROR address=0x%016llx "
+ "flags=0x%04x]\n", address, flags);
break;
case EVENT_TYPE_IOTLB_INV_TO:
- printk("IOTLB_INV_TIMEOUT device=%02x:%02x.%x "
- "address=0x%016llx]\n",
- PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid),
- address);
+ dev_err(dev, "IOTLB_INV_TIMEOUT device=%02x:%02x.%x "
+ "address=0x%016llx]\n",
+ PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid),
+ address);
break;
case EVENT_TYPE_INV_DEV_REQ:
- printk("INVALID_DEVICE_REQUEST device=%02x:%02x.%x "
- "address=0x%016llx flags=0x%04x]\n",
- PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid),
- address, flags);
+ dev_err(dev, "INVALID_DEVICE_REQUEST device=%02x:%02x.%x "
+ "address=0x%016llx flags=0x%04x]\n",
+ PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid),
+ address, flags);
break;
default:
- printk(KERN_ERR "UNKNOWN type=0x%02x event[0]=0x%08x "
- "event[1]=0x%08x event[2]=0x%08x event[3]=0x%08x\n",
- type, event[0], event[1], event[2], event[3]);
+ dev_err(dev, KERN_ERR "UNKNOWN event[0]=0x%08x event[1]=0x%08x "
+ "event[2]=0x%08x event[3]=0x%08x\n",
+ event[0], event[1], event[2], event[3]);
}
memset(__evt, 0, 4 * sizeof(u32));
@@ -1056,9 +1054,9 @@ static int iommu_queue_command_sync(struct amd_iommu *iommu,
unsigned long flags;
int ret;
- spin_lock_irqsave(&iommu->lock, flags);
+ raw_spin_lock_irqsave(&iommu->lock, flags);
ret = __iommu_queue_command_sync(iommu, cmd, sync);
- spin_unlock_irqrestore(&iommu->lock, flags);
+ raw_spin_unlock_irqrestore(&iommu->lock, flags);
return ret;
}
@@ -1084,7 +1082,7 @@ static int iommu_completion_wait(struct amd_iommu *iommu)
build_completion_wait(&cmd, (u64)&iommu->cmd_sem);
- spin_lock_irqsave(&iommu->lock, flags);
+ raw_spin_lock_irqsave(&iommu->lock, flags);
iommu->cmd_sem = 0;
@@ -1095,7 +1093,7 @@ static int iommu_completion_wait(struct amd_iommu *iommu)
ret = wait_on_sem(&iommu->cmd_sem);
out_unlock:
- spin_unlock_irqrestore(&iommu->lock, flags);
+ raw_spin_unlock_irqrestore(&iommu->lock, flags);
return ret;
}
@@ -1605,29 +1603,26 @@ static void del_domain_from_list(struct protection_domain *domain)
static u16 domain_id_alloc(void)
{
- unsigned long flags;
int id;
- write_lock_irqsave(&amd_iommu_devtable_lock, flags);
+ spin_lock(&pd_bitmap_lock);
id = find_first_zero_bit(amd_iommu_pd_alloc_bitmap, MAX_DOMAIN_ID);
BUG_ON(id == 0);
if (id > 0 && id < MAX_DOMAIN_ID)
__set_bit(id, amd_iommu_pd_alloc_bitmap);
else
id = 0;
- write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
+ spin_unlock(&pd_bitmap_lock);
return id;
}
static void domain_id_free(int id)
{
- unsigned long flags;
-
- write_lock_irqsave(&amd_iommu_devtable_lock, flags);
+ spin_lock(&pd_bitmap_lock);
if (id > 0 && id < MAX_DOMAIN_ID)
__clear_bit(id, amd_iommu_pd_alloc_bitmap);
- write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
+ spin_unlock(&pd_bitmap_lock);
}
#define DEFINE_FREE_PT_FN(LVL, FN) \
@@ -2103,9 +2098,9 @@ static int attach_device(struct device *dev,
}
skip_ats_check:
- write_lock_irqsave(&amd_iommu_devtable_lock, flags);
+ spin_lock_irqsave(&amd_iommu_devtable_lock, flags);
ret = __attach_device(dev_data, domain);
- write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
+ spin_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
/*
* We might boot into a crash-kernel here. The crashed kernel
@@ -2155,9 +2150,9 @@ static void detach_device(struct device *dev)
domain = dev_data->domain;
/* lock device table */
- write_lock_irqsave(&amd_iommu_devtable_lock, flags);
+ spin_lock_irqsave(&amd_iommu_devtable_lock, flags);
__detach_device(dev_data);
- write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
+ spin_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
if (!dev_is_pci(dev))
return;
@@ -2193,7 +2188,7 @@ static int amd_iommu_add_device(struct device *dev)
dev_name(dev));
iommu_ignore_device(dev);
- dev->dma_ops = &nommu_dma_ops;
+ dev->dma_ops = &dma_direct_ops;
goto out;
}
init_iommu_group(dev);
@@ -2599,51 +2594,32 @@ static void *alloc_coherent(struct device *dev, size_t size,
unsigned long attrs)
{
u64 dma_mask = dev->coherent_dma_mask;
- struct protection_domain *domain;
- struct dma_ops_domain *dma_dom;
- struct page *page;
-
- domain = get_domain(dev);
- if (PTR_ERR(domain) == -EINVAL) {
- page = alloc_pages(flag, get_order(size));
- *dma_addr = page_to_phys(page);
- return page_address(page);
- } else if (IS_ERR(domain))
- return NULL;
-
- dma_dom = to_dma_ops_domain(domain);
- size = PAGE_ALIGN(size);
- dma_mask = dev->coherent_dma_mask;
- flag &= ~(__GFP_DMA | __GFP_HIGHMEM | __GFP_DMA32);
- flag |= __GFP_ZERO;
-
- page = alloc_pages(flag | __GFP_NOWARN, get_order(size));
- if (!page) {
- if (!gfpflags_allow_blocking(flag))
- return NULL;
+ struct protection_domain *domain = get_domain(dev);
+ bool is_direct = false;
+ void *virt_addr;
- page = dma_alloc_from_contiguous(dev, size >> PAGE_SHIFT,
- get_order(size), flag);
- if (!page)
+ if (IS_ERR(domain)) {
+ if (PTR_ERR(domain) != -EINVAL)
return NULL;
+ is_direct = true;
}
+ virt_addr = dma_direct_alloc(dev, size, dma_addr, flag, attrs);
+ if (!virt_addr || is_direct)
+ return virt_addr;
+
if (!dma_mask)
dma_mask = *dev->dma_mask;
- *dma_addr = __map_single(dev, dma_dom, page_to_phys(page),
- size, DMA_BIDIRECTIONAL, dma_mask);
-
+ *dma_addr = __map_single(dev, to_dma_ops_domain(domain),
+ virt_to_phys(virt_addr), PAGE_ALIGN(size),
+ DMA_BIDIRECTIONAL, dma_mask);
if (*dma_addr == AMD_IOMMU_MAPPING_ERROR)
goto out_free;
-
- return page_address(page);
+ return virt_addr;
out_free:
-
- if (!dma_release_from_contiguous(dev, page, size >> PAGE_SHIFT))
- __free_pages(page, get_order(size));
-
+ dma_direct_free(dev, size, virt_addr, *dma_addr, attrs);
return NULL;
}
@@ -2654,24 +2630,17 @@ static void free_coherent(struct device *dev, size_t size,
void *virt_addr, dma_addr_t dma_addr,
unsigned long attrs)
{
- struct protection_domain *domain;
- struct dma_ops_domain *dma_dom;
- struct page *page;
+ struct protection_domain *domain = get_domain(dev);
- page = virt_to_page(virt_addr);
size = PAGE_ALIGN(size);
- domain = get_domain(dev);
- if (IS_ERR(domain))
- goto free_mem;
-
- dma_dom = to_dma_ops_domain(domain);
+ if (!IS_ERR(domain)) {
+ struct dma_ops_domain *dma_dom = to_dma_ops_domain(domain);
- __unmap_single(dma_dom, dma_addr, size, DMA_BIDIRECTIONAL);
+ __unmap_single(dma_dom, dma_addr, size, DMA_BIDIRECTIONAL);
+ }
-free_mem:
- if (!dma_release_from_contiguous(dev, page, size >> PAGE_SHIFT))
- __free_pages(page, get_order(size));
+ dma_direct_free(dev, size, virt_addr, dma_addr, attrs);
}
/*
@@ -2680,7 +2649,7 @@ free_mem:
*/
static int amd_iommu_dma_supported(struct device *dev, u64 mask)
{
- if (!x86_dma_supported(dev, mask))
+ if (!dma_direct_supported(dev, mask))
return 0;
return check_device(dev);
}
@@ -2794,7 +2763,7 @@ int __init amd_iommu_init_dma_ops(void)
* continue to be SWIOTLB.
*/
if (!swiotlb)
- dma_ops = &nommu_dma_ops;
+ dma_ops = &dma_direct_ops;
if (amd_iommu_unmap_flush)
pr_info("AMD-Vi: IO/TLB flush on unmap enabled\n");
@@ -2820,7 +2789,7 @@ static void cleanup_domain(struct protection_domain *domain)
struct iommu_dev_data *entry;
unsigned long flags;
- write_lock_irqsave(&amd_iommu_devtable_lock, flags);
+ spin_lock_irqsave(&amd_iommu_devtable_lock, flags);
while (!list_empty(&domain->dev_list)) {
entry = list_first_entry(&domain->dev_list,
@@ -2828,7 +2797,7 @@ static void cleanup_domain(struct protection_domain *domain)
__detach_device(entry);
}
- write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
+ spin_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
}
static void protection_domain_free(struct protection_domain *domain)
@@ -3050,15 +3019,12 @@ static size_t amd_iommu_unmap(struct iommu_domain *dom, unsigned long iova,
size_t unmap_size;
if (domain->mode == PAGE_MODE_NONE)
- return -EINVAL;
+ return 0;
mutex_lock(&domain->api_lock);
unmap_size = iommu_unmap_page(domain, iova, page_size);
mutex_unlock(&domain->api_lock);
- domain_flush_tlb_pde(domain);
- domain_flush_complete(domain);
-
return unmap_size;
}
@@ -3176,6 +3142,19 @@ static bool amd_iommu_is_attach_deferred(struct iommu_domain *domain,
return dev_data->defer_attach;
}
+static void amd_iommu_flush_iotlb_all(struct iommu_domain *domain)
+{
+ struct protection_domain *dom = to_pdomain(domain);
+
+ domain_flush_tlb_pde(dom);
+ domain_flush_complete(dom);
+}
+
+static void amd_iommu_iotlb_range_add(struct iommu_domain *domain,
+ unsigned long iova, size_t size)
+{
+}
+
const struct iommu_ops amd_iommu_ops = {
.capable = amd_iommu_capable,
.domain_alloc = amd_iommu_domain_alloc,
@@ -3194,6 +3173,9 @@ const struct iommu_ops amd_iommu_ops = {
.apply_resv_region = amd_iommu_apply_resv_region,
.is_attach_deferred = amd_iommu_is_attach_deferred,
.pgsize_bitmap = AMD_IOMMU_PGSIZES,
+ .flush_iotlb_all = amd_iommu_flush_iotlb_all,
+ .iotlb_range_add = amd_iommu_iotlb_range_add,
+ .iotlb_sync = amd_iommu_flush_iotlb_all,
};
/*****************************************************************************
@@ -3595,14 +3577,62 @@ static void set_dte_irq_entry(u16 devid, struct irq_remap_table *table)
amd_iommu_dev_table[devid].data[2] = dte;
}
-static struct irq_remap_table *get_irq_table(u16 devid, bool ioapic)
+static struct irq_remap_table *get_irq_table(u16 devid)
+{
+ struct irq_remap_table *table;
+
+ if (WARN_ONCE(!amd_iommu_rlookup_table[devid],
+ "%s: no iommu for devid %x\n", __func__, devid))
+ return NULL;
+
+ table = irq_lookup_table[devid];
+ if (WARN_ONCE(!table, "%s: no table for devid %x\n", __func__, devid))
+ return NULL;
+
+ return table;
+}
+
+static struct irq_remap_table *__alloc_irq_table(void)
+{
+ struct irq_remap_table *table;
+
+ table = kzalloc(sizeof(*table), GFP_KERNEL);
+ if (!table)
+ return NULL;
+
+ table->table = kmem_cache_alloc(amd_iommu_irq_cache, GFP_KERNEL);
+ if (!table->table) {
+ kfree(table);
+ return NULL;
+ }
+ raw_spin_lock_init(&table->lock);
+
+ if (!AMD_IOMMU_GUEST_IR_GA(amd_iommu_guest_ir))
+ memset(table->table, 0,
+ MAX_IRQS_PER_TABLE * sizeof(u32));
+ else
+ memset(table->table, 0,
+ (MAX_IRQS_PER_TABLE * (sizeof(u64) * 2)));
+ return table;
+}
+
+static void set_remap_table_entry(struct amd_iommu *iommu, u16 devid,
+ struct irq_remap_table *table)
+{
+ irq_lookup_table[devid] = table;
+ set_dte_irq_entry(devid, table);
+ iommu_flush_dte(iommu, devid);
+}
+
+static struct irq_remap_table *alloc_irq_table(u16 devid)
{
struct irq_remap_table *table = NULL;
+ struct irq_remap_table *new_table = NULL;
struct amd_iommu *iommu;
unsigned long flags;
u16 alias;
- write_lock_irqsave(&amd_iommu_devtable_lock, flags);
+ spin_lock_irqsave(&iommu_table_lock, flags);
iommu = amd_iommu_rlookup_table[devid];
if (!iommu)
@@ -3615,60 +3645,45 @@ static struct irq_remap_table *get_irq_table(u16 devid, bool ioapic)
alias = amd_iommu_alias_table[devid];
table = irq_lookup_table[alias];
if (table) {
- irq_lookup_table[devid] = table;
- set_dte_irq_entry(devid, table);
- iommu_flush_dte(iommu, devid);
- goto out;
+ set_remap_table_entry(iommu, devid, table);
+ goto out_wait;
}
+ spin_unlock_irqrestore(&iommu_table_lock, flags);
/* Nothing there yet, allocate new irq remapping table */
- table = kzalloc(sizeof(*table), GFP_ATOMIC);
- if (!table)
- goto out_unlock;
-
- /* Initialize table spin-lock */
- spin_lock_init(&table->lock);
+ new_table = __alloc_irq_table();
+ if (!new_table)
+ return NULL;
- if (ioapic)
- /* Keep the first 32 indexes free for IOAPIC interrupts */
- table->min_index = 32;
+ spin_lock_irqsave(&iommu_table_lock, flags);
- table->table = kmem_cache_alloc(amd_iommu_irq_cache, GFP_ATOMIC);
- if (!table->table) {
- kfree(table);
- table = NULL;
+ table = irq_lookup_table[devid];
+ if (table)
goto out_unlock;
- }
-
- if (!AMD_IOMMU_GUEST_IR_GA(amd_iommu_guest_ir))
- memset(table->table, 0,
- MAX_IRQS_PER_TABLE * sizeof(u32));
- else
- memset(table->table, 0,
- (MAX_IRQS_PER_TABLE * (sizeof(u64) * 2)));
-
- if (ioapic) {
- int i;
- for (i = 0; i < 32; ++i)
- iommu->irte_ops->set_allocated(table, i);
+ table = irq_lookup_table[alias];
+ if (table) {
+ set_remap_table_entry(iommu, devid, table);
+ goto out_wait;
}
- irq_lookup_table[devid] = table;
- set_dte_irq_entry(devid, table);
- iommu_flush_dte(iommu, devid);
- if (devid != alias) {
- irq_lookup_table[alias] = table;
- set_dte_irq_entry(alias, table);
- iommu_flush_dte(iommu, alias);
- }
+ table = new_table;
+ new_table = NULL;
-out:
+ set_remap_table_entry(iommu, devid, table);
+ if (devid != alias)
+ set_remap_table_entry(iommu, alias, table);
+
+out_wait:
iommu_completion_wait(iommu);
out_unlock:
- write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
+ spin_unlock_irqrestore(&iommu_table_lock, flags);
+ if (new_table) {
+ kmem_cache_free(amd_iommu_irq_cache, new_table->table);
+ kfree(new_table);
+ }
return table;
}
@@ -3682,14 +3697,14 @@ static int alloc_irq_index(u16 devid, int count, bool align)
if (!iommu)
return -ENODEV;
- table = get_irq_table(devid, false);
+ table = alloc_irq_table(devid);
if (!table)
return -ENODEV;
if (align)
alignment = roundup_pow_of_two(count);
- spin_lock_irqsave(&table->lock, flags);
+ raw_spin_lock_irqsave(&table->lock, flags);
/* Scan table for free entries */
for (index = ALIGN(table->min_index, alignment), c = 0;
@@ -3716,7 +3731,7 @@ static int alloc_irq_index(u16 devid, int count, bool align)
index = -ENOSPC;
out:
- spin_unlock_irqrestore(&table->lock, flags);
+ raw_spin_unlock_irqrestore(&table->lock, flags);
return index;
}
@@ -3733,11 +3748,11 @@ static int modify_irte_ga(u16 devid, int index, struct irte_ga *irte,
if (iommu == NULL)
return -EINVAL;
- table = get_irq_table(devid, false);
+ table = get_irq_table(devid);
if (!table)
return -ENOMEM;
- spin_lock_irqsave(&table->lock, flags);
+ raw_spin_lock_irqsave(&table->lock, flags);
entry = (struct irte_ga *)table->table;
entry = &entry[index];
@@ -3748,7 +3763,7 @@ static int modify_irte_ga(u16 devid, int index, struct irte_ga *irte,
if (data)
data->ref = entry;
- spin_unlock_irqrestore(&table->lock, flags);
+ raw_spin_unlock_irqrestore(&table->lock, flags);
iommu_flush_irt(iommu, devid);
iommu_completion_wait(iommu);
@@ -3766,13 +3781,13 @@ static int modify_irte(u16 devid, int index, union irte *irte)
if (iommu == NULL)
return -EINVAL;
- table = get_irq_table(devid, false);
+ table = get_irq_table(devid);
if (!table)
return -ENOMEM;
- spin_lock_irqsave(&table->lock, flags);
+ raw_spin_lock_irqsave(&table->lock, flags);
table->table[index] = irte->val;
- spin_unlock_irqrestore(&table->lock, flags);
+ raw_spin_unlock_irqrestore(&table->lock, flags);
iommu_flush_irt(iommu, devid);
iommu_completion_wait(iommu);
@@ -3790,13 +3805,13 @@ static void free_irte(u16 devid, int index)
if (iommu == NULL)
return;
- table = get_irq_table(devid, false);
+ table = get_irq_table(devid);
if (!table)
return;
- spin_lock_irqsave(&table->lock, flags);
+ raw_spin_lock_irqsave(&table->lock, flags);
iommu->irte_ops->clear_allocated(table, index);
- spin_unlock_irqrestore(&table->lock, flags);
+ raw_spin_unlock_irqrestore(&table->lock, flags);
iommu_flush_irt(iommu, devid);
iommu_completion_wait(iommu);
@@ -3877,10 +3892,8 @@ static void irte_ga_set_affinity(void *entry, u16 devid, u16 index,
u8 vector, u32 dest_apicid)
{
struct irte_ga *irte = (struct irte_ga *) entry;
- struct iommu_dev_data *dev_data = search_dev_data(devid);
- if (!dev_data || !dev_data->use_vapic ||
- !irte->lo.fields_remap.guest_mode) {
+ if (!irte->lo.fields_remap.guest_mode) {
irte->hi.fields.vector = vector;
irte->lo.fields_remap.destination = dest_apicid;
modify_irte_ga(devid, index, irte, NULL);
@@ -4086,7 +4099,7 @@ static int irq_remapping_alloc(struct irq_domain *domain, unsigned int virq,
struct amd_ir_data *data = NULL;
struct irq_cfg *cfg;
int i, ret, devid;
- int index = -1;
+ int index;
if (!info)
return -EINVAL;
@@ -4110,10 +4123,26 @@ static int irq_remapping_alloc(struct irq_domain *domain, unsigned int virq,
return ret;
if (info->type == X86_IRQ_ALLOC_TYPE_IOAPIC) {
- if (get_irq_table(devid, true))
+ struct irq_remap_table *table;
+ struct amd_iommu *iommu;
+
+ table = alloc_irq_table(devid);
+ if (table) {
+ if (!table->min_index) {
+ /*
+ * Keep the first 32 indexes free for IOAPIC
+ * interrupts.
+ */
+ table->min_index = 32;
+ iommu = amd_iommu_rlookup_table[devid];
+ for (i = 0; i < 32; ++i)
+ iommu->irte_ops->set_allocated(table, i);
+ }
+ WARN_ON(table->min_index != 32);
index = info->ioapic_pin;
- else
- ret = -ENOMEM;
+ } else {
+ index = -ENOMEM;
+ }
} else {
bool align = (info->type == X86_IRQ_ALLOC_TYPE_MSI);
@@ -4379,7 +4408,7 @@ int amd_iommu_update_ga(int cpu, bool is_run, void *data)
{
unsigned long flags;
struct amd_iommu *iommu;
- struct irq_remap_table *irt;
+ struct irq_remap_table *table;
struct amd_ir_data *ir_data = (struct amd_ir_data *)data;
int devid = ir_data->irq_2_irte.devid;
struct irte_ga *entry = (struct irte_ga *) ir_data->entry;
@@ -4393,11 +4422,11 @@ int amd_iommu_update_ga(int cpu, bool is_run, void *data)
if (!iommu)
return -ENODEV;
- irt = get_irq_table(devid, false);
- if (!irt)
+ table = get_irq_table(devid);
+ if (!table)
return -ENODEV;
- spin_lock_irqsave(&irt->lock, flags);
+ raw_spin_lock_irqsave(&table->lock, flags);
if (ref->lo.fields_vapic.guest_mode) {
if (cpu >= 0)
@@ -4406,7 +4435,7 @@ int amd_iommu_update_ga(int cpu, bool is_run, void *data)
barrier();
}
- spin_unlock_irqrestore(&irt->lock, flags);
+ raw_spin_unlock_irqrestore(&table->lock, flags);
iommu_flush_irt(iommu, devid);
iommu_completion_wait(iommu);
diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c
index 4e4a615bf13f..904c575d1677 100644
--- a/drivers/iommu/amd_iommu_init.c
+++ b/drivers/iommu/amd_iommu_init.c
@@ -1474,7 +1474,7 @@ static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h)
{
int ret;
- spin_lock_init(&iommu->lock);
+ raw_spin_lock_init(&iommu->lock);
/* Add IOMMU to internal data structures */
list_add_tail(&iommu->list, &amd_iommu_list);
diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h
index 6a877ebd058b..1c9b080276c9 100644
--- a/drivers/iommu/amd_iommu_types.h
+++ b/drivers/iommu/amd_iommu_types.h
@@ -408,7 +408,7 @@ extern bool amd_iommu_iotlb_sup;
#define IRQ_TABLE_ALIGNMENT 128
struct irq_remap_table {
- spinlock_t lock;
+ raw_spinlock_t lock;
unsigned min_index;
u32 *table;
};
@@ -490,7 +490,7 @@ struct amd_iommu {
int index;
/* locks the accesses to the hardware */
- spinlock_t lock;
+ raw_spinlock_t lock;
/* Pointer to PCI device of this IOMMU */
struct pci_dev *dev;
@@ -627,7 +627,7 @@ struct devid_map {
*/
struct iommu_dev_data {
struct list_head list; /* For domain->dev_list */
- struct list_head dev_data_list; /* For global dev_data_list */
+ struct llist_node dev_data_list; /* For global dev_data_list */
struct protection_domain *domain; /* Domain the device is bound to */
u16 devid; /* PCI Device ID */
u16 alias; /* Alias Device ID */
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index 3f2f1fc68b52..1d647104bccc 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -22,6 +22,8 @@
#include <linux/acpi.h>
#include <linux/acpi_iort.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/dma-iommu.h>
#include <linux/err.h>
@@ -43,18 +45,15 @@
/* MMIO registers */
#define ARM_SMMU_IDR0 0x0
-#define IDR0_ST_LVL_SHIFT 27
-#define IDR0_ST_LVL_MASK 0x3
-#define IDR0_ST_LVL_2LVL (1 << IDR0_ST_LVL_SHIFT)
-#define IDR0_STALL_MODEL_SHIFT 24
-#define IDR0_STALL_MODEL_MASK 0x3
-#define IDR0_STALL_MODEL_STALL (0 << IDR0_STALL_MODEL_SHIFT)
-#define IDR0_STALL_MODEL_FORCE (2 << IDR0_STALL_MODEL_SHIFT)
-#define IDR0_TTENDIAN_SHIFT 21
-#define IDR0_TTENDIAN_MASK 0x3
-#define IDR0_TTENDIAN_LE (2 << IDR0_TTENDIAN_SHIFT)
-#define IDR0_TTENDIAN_BE (3 << IDR0_TTENDIAN_SHIFT)
-#define IDR0_TTENDIAN_MIXED (0 << IDR0_TTENDIAN_SHIFT)
+#define IDR0_ST_LVL GENMASK(28, 27)
+#define IDR0_ST_LVL_2LVL 1
+#define IDR0_STALL_MODEL GENMASK(25, 24)
+#define IDR0_STALL_MODEL_STALL 0
+#define IDR0_STALL_MODEL_FORCE 2
+#define IDR0_TTENDIAN GENMASK(22, 21)
+#define IDR0_TTENDIAN_MIXED 0
+#define IDR0_TTENDIAN_LE 2
+#define IDR0_TTENDIAN_BE 3
#define IDR0_CD2L (1 << 19)
#define IDR0_VMID16 (1 << 18)
#define IDR0_PRI (1 << 16)
@@ -64,10 +63,9 @@
#define IDR0_ATS (1 << 10)
#define IDR0_HYP (1 << 9)
#define IDR0_COHACC (1 << 4)
-#define IDR0_TTF_SHIFT 2
-#define IDR0_TTF_MASK 0x3
-#define IDR0_TTF_AARCH64 (2 << IDR0_TTF_SHIFT)
-#define IDR0_TTF_AARCH32_64 (3 << IDR0_TTF_SHIFT)
+#define IDR0_TTF GENMASK(3, 2)
+#define IDR0_TTF_AARCH64 2
+#define IDR0_TTF_AARCH32_64 3
#define IDR0_S1P (1 << 1)
#define IDR0_S2P (1 << 0)
@@ -75,31 +73,27 @@
#define IDR1_TABLES_PRESET (1 << 30)
#define IDR1_QUEUES_PRESET (1 << 29)
#define IDR1_REL (1 << 28)
-#define IDR1_CMDQ_SHIFT 21
-#define IDR1_CMDQ_MASK 0x1f
-#define IDR1_EVTQ_SHIFT 16
-#define IDR1_EVTQ_MASK 0x1f
-#define IDR1_PRIQ_SHIFT 11
-#define IDR1_PRIQ_MASK 0x1f
-#define IDR1_SSID_SHIFT 6
-#define IDR1_SSID_MASK 0x1f
-#define IDR1_SID_SHIFT 0
-#define IDR1_SID_MASK 0x3f
+#define IDR1_CMDQS GENMASK(25, 21)
+#define IDR1_EVTQS GENMASK(20, 16)
+#define IDR1_PRIQS GENMASK(15, 11)
+#define IDR1_SSIDSIZE GENMASK(10, 6)
+#define IDR1_SIDSIZE GENMASK(5, 0)
#define ARM_SMMU_IDR5 0x14
-#define IDR5_STALL_MAX_SHIFT 16
-#define IDR5_STALL_MAX_MASK 0xffff
+#define IDR5_STALL_MAX GENMASK(31, 16)
#define IDR5_GRAN64K (1 << 6)
#define IDR5_GRAN16K (1 << 5)
#define IDR5_GRAN4K (1 << 4)
-#define IDR5_OAS_SHIFT 0
-#define IDR5_OAS_MASK 0x7
-#define IDR5_OAS_32_BIT (0 << IDR5_OAS_SHIFT)
-#define IDR5_OAS_36_BIT (1 << IDR5_OAS_SHIFT)
-#define IDR5_OAS_40_BIT (2 << IDR5_OAS_SHIFT)
-#define IDR5_OAS_42_BIT (3 << IDR5_OAS_SHIFT)
-#define IDR5_OAS_44_BIT (4 << IDR5_OAS_SHIFT)
-#define IDR5_OAS_48_BIT (5 << IDR5_OAS_SHIFT)
+#define IDR5_OAS GENMASK(2, 0)
+#define IDR5_OAS_32_BIT 0
+#define IDR5_OAS_36_BIT 1
+#define IDR5_OAS_40_BIT 2
+#define IDR5_OAS_42_BIT 3
+#define IDR5_OAS_44_BIT 4
+#define IDR5_OAS_48_BIT 5
+#define IDR5_OAS_52_BIT 6
+#define IDR5_VAX GENMASK(11, 10)
+#define IDR5_VAX_52_BIT 1
#define ARM_SMMU_CR0 0x20
#define CR0_CMDQEN (1 << 3)
@@ -110,18 +104,16 @@
#define ARM_SMMU_CR0ACK 0x24
#define ARM_SMMU_CR1 0x28
-#define CR1_SH_NSH 0
-#define CR1_SH_OSH 2
-#define CR1_SH_ISH 3
+#define CR1_TABLE_SH GENMASK(11, 10)
+#define CR1_TABLE_OC GENMASK(9, 8)
+#define CR1_TABLE_IC GENMASK(7, 6)
+#define CR1_QUEUE_SH GENMASK(5, 4)
+#define CR1_QUEUE_OC GENMASK(3, 2)
+#define CR1_QUEUE_IC GENMASK(1, 0)
+/* CR1 cacheability fields don't quite follow the usual TCR-style encoding */
#define CR1_CACHE_NC 0
#define CR1_CACHE_WB 1
#define CR1_CACHE_WT 2
-#define CR1_TABLE_SH_SHIFT 10
-#define CR1_TABLE_OC_SHIFT 8
-#define CR1_TABLE_IC_SHIFT 6
-#define CR1_QUEUE_SH_SHIFT 4
-#define CR1_QUEUE_OC_SHIFT 2
-#define CR1_QUEUE_IC_SHIFT 0
#define ARM_SMMU_CR2 0x2c
#define CR2_PTM (1 << 2)
@@ -129,8 +121,8 @@
#define CR2_E2H (1 << 0)
#define ARM_SMMU_GBPA 0x44
-#define GBPA_ABORT (1 << 20)
#define GBPA_UPDATE (1 << 31)
+#define GBPA_ABORT (1 << 20)
#define ARM_SMMU_IRQ_CTRL 0x50
#define IRQ_CTRL_EVTQ_IRQEN (1 << 2)
@@ -158,18 +150,14 @@
#define ARM_SMMU_STRTAB_BASE 0x80
#define STRTAB_BASE_RA (1UL << 62)
-#define STRTAB_BASE_ADDR_SHIFT 6
-#define STRTAB_BASE_ADDR_MASK 0x3ffffffffffUL
+#define STRTAB_BASE_ADDR_MASK GENMASK_ULL(51, 6)
#define ARM_SMMU_STRTAB_BASE_CFG 0x88
-#define STRTAB_BASE_CFG_LOG2SIZE_SHIFT 0
-#define STRTAB_BASE_CFG_LOG2SIZE_MASK 0x3f
-#define STRTAB_BASE_CFG_SPLIT_SHIFT 6
-#define STRTAB_BASE_CFG_SPLIT_MASK 0x1f
-#define STRTAB_BASE_CFG_FMT_SHIFT 16
-#define STRTAB_BASE_CFG_FMT_MASK 0x3
-#define STRTAB_BASE_CFG_FMT_LINEAR (0 << STRTAB_BASE_CFG_FMT_SHIFT)
-#define STRTAB_BASE_CFG_FMT_2LVL (1 << STRTAB_BASE_CFG_FMT_SHIFT)
+#define STRTAB_BASE_CFG_FMT GENMASK(17, 16)
+#define STRTAB_BASE_CFG_FMT_LINEAR 0
+#define STRTAB_BASE_CFG_FMT_2LVL 1
+#define STRTAB_BASE_CFG_SPLIT GENMASK(10, 6)
+#define STRTAB_BASE_CFG_LOG2SIZE GENMASK(5, 0)
#define ARM_SMMU_CMDQ_BASE 0x90
#define ARM_SMMU_CMDQ_PROD 0x98
@@ -190,14 +178,16 @@
#define ARM_SMMU_PRIQ_IRQ_CFG2 0xdc
/* Common MSI config fields */
-#define MSI_CFG0_ADDR_SHIFT 2
-#define MSI_CFG0_ADDR_MASK 0x3fffffffffffUL
-#define MSI_CFG2_SH_SHIFT 4
-#define MSI_CFG2_SH_NSH (0UL << MSI_CFG2_SH_SHIFT)
-#define MSI_CFG2_SH_OSH (2UL << MSI_CFG2_SH_SHIFT)
-#define MSI_CFG2_SH_ISH (3UL << MSI_CFG2_SH_SHIFT)
-#define MSI_CFG2_MEMATTR_SHIFT 0
-#define MSI_CFG2_MEMATTR_DEVICE_nGnRE (0x1 << MSI_CFG2_MEMATTR_SHIFT)
+#define MSI_CFG0_ADDR_MASK GENMASK_ULL(51, 2)
+#define MSI_CFG2_SH GENMASK(5, 4)
+#define MSI_CFG2_MEMATTR GENMASK(3, 0)
+
+/* Common memory attribute values */
+#define ARM_SMMU_SH_NSH 0
+#define ARM_SMMU_SH_OSH 2
+#define ARM_SMMU_SH_ISH 3
+#define ARM_SMMU_MEMATTR_DEVICE_nGnRE 0x1
+#define ARM_SMMU_MEMATTR_OIWB 0xf
#define Q_IDX(q, p) ((p) & ((1 << (q)->max_n_shift) - 1))
#define Q_WRP(q, p) ((p) & (1 << (q)->max_n_shift))
@@ -207,10 +197,8 @@
Q_IDX(q, p) * (q)->ent_dwords)
#define Q_BASE_RWA (1UL << 62)
-#define Q_BASE_ADDR_SHIFT 5
-#define Q_BASE_ADDR_MASK 0xfffffffffffUL
-#define Q_BASE_LOG2SIZE_SHIFT 0
-#define Q_BASE_LOG2SIZE_MASK 0x1fUL
+#define Q_BASE_ADDR_MASK GENMASK_ULL(51, 5)
+#define Q_BASE_LOG2SIZE GENMASK(4, 0)
/*
* Stream table.
@@ -223,187 +211,143 @@
#define STRTAB_SPLIT 8
#define STRTAB_L1_DESC_DWORDS 1
-#define STRTAB_L1_DESC_SPAN_SHIFT 0
-#define STRTAB_L1_DESC_SPAN_MASK 0x1fUL
-#define STRTAB_L1_DESC_L2PTR_SHIFT 6
-#define STRTAB_L1_DESC_L2PTR_MASK 0x3ffffffffffUL
+#define STRTAB_L1_DESC_SPAN GENMASK_ULL(4, 0)
+#define STRTAB_L1_DESC_L2PTR_MASK GENMASK_ULL(51, 6)
#define STRTAB_STE_DWORDS 8
#define STRTAB_STE_0_V (1UL << 0)
-#define STRTAB_STE_0_CFG_SHIFT 1
-#define STRTAB_STE_0_CFG_MASK 0x7UL
-#define STRTAB_STE_0_CFG_ABORT (0UL << STRTAB_STE_0_CFG_SHIFT)
-#define STRTAB_STE_0_CFG_BYPASS (4UL << STRTAB_STE_0_CFG_SHIFT)
-#define STRTAB_STE_0_CFG_S1_TRANS (5UL << STRTAB_STE_0_CFG_SHIFT)
-#define STRTAB_STE_0_CFG_S2_TRANS (6UL << STRTAB_STE_0_CFG_SHIFT)
-
-#define STRTAB_STE_0_S1FMT_SHIFT 4
-#define STRTAB_STE_0_S1FMT_LINEAR (0UL << STRTAB_STE_0_S1FMT_SHIFT)
-#define STRTAB_STE_0_S1CTXPTR_SHIFT 6
-#define STRTAB_STE_0_S1CTXPTR_MASK 0x3ffffffffffUL
-#define STRTAB_STE_0_S1CDMAX_SHIFT 59
-#define STRTAB_STE_0_S1CDMAX_MASK 0x1fUL
+#define STRTAB_STE_0_CFG GENMASK_ULL(3, 1)
+#define STRTAB_STE_0_CFG_ABORT 0
+#define STRTAB_STE_0_CFG_BYPASS 4
+#define STRTAB_STE_0_CFG_S1_TRANS 5
+#define STRTAB_STE_0_CFG_S2_TRANS 6
+
+#define STRTAB_STE_0_S1FMT GENMASK_ULL(5, 4)
+#define STRTAB_STE_0_S1FMT_LINEAR 0
+#define STRTAB_STE_0_S1CTXPTR_MASK GENMASK_ULL(51, 6)
+#define STRTAB_STE_0_S1CDMAX GENMASK_ULL(63, 59)
#define STRTAB_STE_1_S1C_CACHE_NC 0UL
#define STRTAB_STE_1_S1C_CACHE_WBRA 1UL
#define STRTAB_STE_1_S1C_CACHE_WT 2UL
#define STRTAB_STE_1_S1C_CACHE_WB 3UL
-#define STRTAB_STE_1_S1C_SH_NSH 0UL
-#define STRTAB_STE_1_S1C_SH_OSH 2UL
-#define STRTAB_STE_1_S1C_SH_ISH 3UL
-#define STRTAB_STE_1_S1CIR_SHIFT 2
-#define STRTAB_STE_1_S1COR_SHIFT 4
-#define STRTAB_STE_1_S1CSH_SHIFT 6
+#define STRTAB_STE_1_S1CIR GENMASK_ULL(3, 2)
+#define STRTAB_STE_1_S1COR GENMASK_ULL(5, 4)
+#define STRTAB_STE_1_S1CSH GENMASK_ULL(7, 6)
#define STRTAB_STE_1_S1STALLD (1UL << 27)
+#define STRTAB_STE_1_EATS GENMASK_ULL(29, 28)
#define STRTAB_STE_1_EATS_ABT 0UL
#define STRTAB_STE_1_EATS_TRANS 1UL
#define STRTAB_STE_1_EATS_S1CHK 2UL
-#define STRTAB_STE_1_EATS_SHIFT 28
+#define STRTAB_STE_1_STRW GENMASK_ULL(31, 30)
#define STRTAB_STE_1_STRW_NSEL1 0UL
#define STRTAB_STE_1_STRW_EL2 2UL
-#define STRTAB_STE_1_STRW_SHIFT 30
+#define STRTAB_STE_1_SHCFG GENMASK_ULL(45, 44)
#define STRTAB_STE_1_SHCFG_INCOMING 1UL
-#define STRTAB_STE_1_SHCFG_SHIFT 44
-#define STRTAB_STE_2_S2VMID_SHIFT 0
-#define STRTAB_STE_2_S2VMID_MASK 0xffffUL
-#define STRTAB_STE_2_VTCR_SHIFT 32
-#define STRTAB_STE_2_VTCR_MASK 0x7ffffUL
+#define STRTAB_STE_2_S2VMID GENMASK_ULL(15, 0)
+#define STRTAB_STE_2_VTCR GENMASK_ULL(50, 32)
#define STRTAB_STE_2_S2AA64 (1UL << 51)
#define STRTAB_STE_2_S2ENDI (1UL << 52)
#define STRTAB_STE_2_S2PTW (1UL << 54)
#define STRTAB_STE_2_S2R (1UL << 58)
-#define STRTAB_STE_3_S2TTB_SHIFT 4
-#define STRTAB_STE_3_S2TTB_MASK 0xfffffffffffUL
+#define STRTAB_STE_3_S2TTB_MASK GENMASK_ULL(51, 4)
/* Context descriptor (stage-1 only) */
#define CTXDESC_CD_DWORDS 8
-#define CTXDESC_CD_0_TCR_T0SZ_SHIFT 0
-#define ARM64_TCR_T0SZ_SHIFT 0
-#define ARM64_TCR_T0SZ_MASK 0x1fUL
-#define CTXDESC_CD_0_TCR_TG0_SHIFT 6
-#define ARM64_TCR_TG0_SHIFT 14
-#define ARM64_TCR_TG0_MASK 0x3UL
-#define CTXDESC_CD_0_TCR_IRGN0_SHIFT 8
-#define ARM64_TCR_IRGN0_SHIFT 8
-#define ARM64_TCR_IRGN0_MASK 0x3UL
-#define CTXDESC_CD_0_TCR_ORGN0_SHIFT 10
-#define ARM64_TCR_ORGN0_SHIFT 10
-#define ARM64_TCR_ORGN0_MASK 0x3UL
-#define CTXDESC_CD_0_TCR_SH0_SHIFT 12
-#define ARM64_TCR_SH0_SHIFT 12
-#define ARM64_TCR_SH0_MASK 0x3UL
-#define CTXDESC_CD_0_TCR_EPD0_SHIFT 14
-#define ARM64_TCR_EPD0_SHIFT 7
-#define ARM64_TCR_EPD0_MASK 0x1UL
-#define CTXDESC_CD_0_TCR_EPD1_SHIFT 30
-#define ARM64_TCR_EPD1_SHIFT 23
-#define ARM64_TCR_EPD1_MASK 0x1UL
+#define CTXDESC_CD_0_TCR_T0SZ GENMASK_ULL(5, 0)
+#define ARM64_TCR_T0SZ GENMASK_ULL(5, 0)
+#define CTXDESC_CD_0_TCR_TG0 GENMASK_ULL(7, 6)
+#define ARM64_TCR_TG0 GENMASK_ULL(15, 14)
+#define CTXDESC_CD_0_TCR_IRGN0 GENMASK_ULL(9, 8)
+#define ARM64_TCR_IRGN0 GENMASK_ULL(9, 8)
+#define CTXDESC_CD_0_TCR_ORGN0 GENMASK_ULL(11, 10)
+#define ARM64_TCR_ORGN0 GENMASK_ULL(11, 10)
+#define CTXDESC_CD_0_TCR_SH0 GENMASK_ULL(13, 12)
+#define ARM64_TCR_SH0 GENMASK_ULL(13, 12)
+#define CTXDESC_CD_0_TCR_EPD0 (1ULL << 14)
+#define ARM64_TCR_EPD0 (1ULL << 7)
+#define CTXDESC_CD_0_TCR_EPD1 (1ULL << 30)
+#define ARM64_TCR_EPD1 (1ULL << 23)
#define CTXDESC_CD_0_ENDI (1UL << 15)
#define CTXDESC_CD_0_V (1UL << 31)
-#define CTXDESC_CD_0_TCR_IPS_SHIFT 32
-#define ARM64_TCR_IPS_SHIFT 32
-#define ARM64_TCR_IPS_MASK 0x7UL
-#define CTXDESC_CD_0_TCR_TBI0_SHIFT 38
-#define ARM64_TCR_TBI0_SHIFT 37
-#define ARM64_TCR_TBI0_MASK 0x1UL
+#define CTXDESC_CD_0_TCR_IPS GENMASK_ULL(34, 32)
+#define ARM64_TCR_IPS GENMASK_ULL(34, 32)
+#define CTXDESC_CD_0_TCR_TBI0 (1ULL << 38)
+#define ARM64_TCR_TBI0 (1ULL << 37)
#define CTXDESC_CD_0_AA64 (1UL << 41)
#define CTXDESC_CD_0_S (1UL << 44)
#define CTXDESC_CD_0_R (1UL << 45)
#define CTXDESC_CD_0_A (1UL << 46)
-#define CTXDESC_CD_0_ASET_SHIFT 47
-#define CTXDESC_CD_0_ASET_SHARED (0UL << CTXDESC_CD_0_ASET_SHIFT)
-#define CTXDESC_CD_0_ASET_PRIVATE (1UL << CTXDESC_CD_0_ASET_SHIFT)
-#define CTXDESC_CD_0_ASID_SHIFT 48
-#define CTXDESC_CD_0_ASID_MASK 0xffffUL
-
-#define CTXDESC_CD_1_TTB0_SHIFT 4
-#define CTXDESC_CD_1_TTB0_MASK 0xfffffffffffUL
+#define CTXDESC_CD_0_ASET (1UL << 47)
+#define CTXDESC_CD_0_ASID GENMASK_ULL(63, 48)
-#define CTXDESC_CD_3_MAIR_SHIFT 0
+#define CTXDESC_CD_1_TTB0_MASK GENMASK_ULL(51, 4)
/* Convert between AArch64 (CPU) TCR format and SMMU CD format */
-#define ARM_SMMU_TCR2CD(tcr, fld) \
- (((tcr) >> ARM64_TCR_##fld##_SHIFT & ARM64_TCR_##fld##_MASK) \
- << CTXDESC_CD_0_TCR_##fld##_SHIFT)
+#define ARM_SMMU_TCR2CD(tcr, fld) FIELD_PREP(CTXDESC_CD_0_TCR_##fld, \
+ FIELD_GET(ARM64_TCR_##fld, tcr))
/* Command queue */
#define CMDQ_ENT_DWORDS 2
#define CMDQ_MAX_SZ_SHIFT 8
-#define CMDQ_ERR_SHIFT 24
-#define CMDQ_ERR_MASK 0x7f
+#define CMDQ_CONS_ERR GENMASK(30, 24)
#define CMDQ_ERR_CERROR_NONE_IDX 0
#define CMDQ_ERR_CERROR_ILL_IDX 1
#define CMDQ_ERR_CERROR_ABT_IDX 2
-#define CMDQ_0_OP_SHIFT 0
-#define CMDQ_0_OP_MASK 0xffUL
+#define CMDQ_0_OP GENMASK_ULL(7, 0)
#define CMDQ_0_SSV (1UL << 11)
-#define CMDQ_PREFETCH_0_SID_SHIFT 32
-#define CMDQ_PREFETCH_1_SIZE_SHIFT 0
-#define CMDQ_PREFETCH_1_ADDR_MASK ~0xfffUL
+#define CMDQ_PREFETCH_0_SID GENMASK_ULL(63, 32)
+#define CMDQ_PREFETCH_1_SIZE GENMASK_ULL(4, 0)
+#define CMDQ_PREFETCH_1_ADDR_MASK GENMASK_ULL(63, 12)
-#define CMDQ_CFGI_0_SID_SHIFT 32
-#define CMDQ_CFGI_0_SID_MASK 0xffffffffUL
+#define CMDQ_CFGI_0_SID GENMASK_ULL(63, 32)
#define CMDQ_CFGI_1_LEAF (1UL << 0)
-#define CMDQ_CFGI_1_RANGE_SHIFT 0
-#define CMDQ_CFGI_1_RANGE_MASK 0x1fUL
+#define CMDQ_CFGI_1_RANGE GENMASK_ULL(4, 0)
-#define CMDQ_TLBI_0_VMID_SHIFT 32
-#define CMDQ_TLBI_0_ASID_SHIFT 48
+#define CMDQ_TLBI_0_VMID GENMASK_ULL(47, 32)
+#define CMDQ_TLBI_0_ASID GENMASK_ULL(63, 48)
#define CMDQ_TLBI_1_LEAF (1UL << 0)
-#define CMDQ_TLBI_1_VA_MASK ~0xfffUL
-#define CMDQ_TLBI_1_IPA_MASK 0xfffffffff000UL
-
-#define CMDQ_PRI_0_SSID_SHIFT 12
-#define CMDQ_PRI_0_SSID_MASK 0xfffffUL
-#define CMDQ_PRI_0_SID_SHIFT 32
-#define CMDQ_PRI_0_SID_MASK 0xffffffffUL
-#define CMDQ_PRI_1_GRPID_SHIFT 0
-#define CMDQ_PRI_1_GRPID_MASK 0x1ffUL
-#define CMDQ_PRI_1_RESP_SHIFT 12
-#define CMDQ_PRI_1_RESP_DENY (0UL << CMDQ_PRI_1_RESP_SHIFT)
-#define CMDQ_PRI_1_RESP_FAIL (1UL << CMDQ_PRI_1_RESP_SHIFT)
-#define CMDQ_PRI_1_RESP_SUCC (2UL << CMDQ_PRI_1_RESP_SHIFT)
-
-#define CMDQ_SYNC_0_CS_SHIFT 12
-#define CMDQ_SYNC_0_CS_NONE (0UL << CMDQ_SYNC_0_CS_SHIFT)
-#define CMDQ_SYNC_0_CS_IRQ (1UL << CMDQ_SYNC_0_CS_SHIFT)
-#define CMDQ_SYNC_0_CS_SEV (2UL << CMDQ_SYNC_0_CS_SHIFT)
-#define CMDQ_SYNC_0_MSH_SHIFT 22
-#define CMDQ_SYNC_0_MSH_ISH (3UL << CMDQ_SYNC_0_MSH_SHIFT)
-#define CMDQ_SYNC_0_MSIATTR_SHIFT 24
-#define CMDQ_SYNC_0_MSIATTR_OIWB (0xfUL << CMDQ_SYNC_0_MSIATTR_SHIFT)
-#define CMDQ_SYNC_0_MSIDATA_SHIFT 32
-#define CMDQ_SYNC_0_MSIDATA_MASK 0xffffffffUL
-#define CMDQ_SYNC_1_MSIADDR_SHIFT 0
-#define CMDQ_SYNC_1_MSIADDR_MASK 0xffffffffffffcUL
+#define CMDQ_TLBI_1_VA_MASK GENMASK_ULL(63, 12)
+#define CMDQ_TLBI_1_IPA_MASK GENMASK_ULL(51, 12)
+
+#define CMDQ_PRI_0_SSID GENMASK_ULL(31, 12)
+#define CMDQ_PRI_0_SID GENMASK_ULL(63, 32)
+#define CMDQ_PRI_1_GRPID GENMASK_ULL(8, 0)
+#define CMDQ_PRI_1_RESP GENMASK_ULL(13, 12)
+
+#define CMDQ_SYNC_0_CS GENMASK_ULL(13, 12)
+#define CMDQ_SYNC_0_CS_NONE 0
+#define CMDQ_SYNC_0_CS_IRQ 1
+#define CMDQ_SYNC_0_CS_SEV 2
+#define CMDQ_SYNC_0_MSH GENMASK_ULL(23, 22)
+#define CMDQ_SYNC_0_MSIATTR GENMASK_ULL(27, 24)
+#define CMDQ_SYNC_0_MSIDATA GENMASK_ULL(63, 32)
+#define CMDQ_SYNC_1_MSIADDR_MASK GENMASK_ULL(51, 2)
/* Event queue */
#define EVTQ_ENT_DWORDS 4
#define EVTQ_MAX_SZ_SHIFT 7
-#define EVTQ_0_ID_SHIFT 0
-#define EVTQ_0_ID_MASK 0xffUL
+#define EVTQ_0_ID GENMASK_ULL(7, 0)
/* PRI queue */
#define PRIQ_ENT_DWORDS 2
#define PRIQ_MAX_SZ_SHIFT 8
-#define PRIQ_0_SID_SHIFT 0
-#define PRIQ_0_SID_MASK 0xffffffffUL
-#define PRIQ_0_SSID_SHIFT 32
-#define PRIQ_0_SSID_MASK 0xfffffUL
+#define PRIQ_0_SID GENMASK_ULL(31, 0)
+#define PRIQ_0_SSID GENMASK_ULL(51, 32)
#define PRIQ_0_PERM_PRIV (1UL << 58)
#define PRIQ_0_PERM_EXEC (1UL << 59)
#define PRIQ_0_PERM_READ (1UL << 60)
@@ -411,10 +355,8 @@
#define PRIQ_0_PRG_LAST (1UL << 62)
#define PRIQ_0_SSID_V (1UL << 63)
-#define PRIQ_1_PRG_IDX_SHIFT 0
-#define PRIQ_1_PRG_IDX_MASK 0x1ffUL
-#define PRIQ_1_ADDR_SHIFT 12
-#define PRIQ_1_ADDR_MASK 0xfffffffffffffUL
+#define PRIQ_1_PRG_IDX GENMASK_ULL(8, 0)
+#define PRIQ_1_ADDR_MASK GENMASK_ULL(63, 12)
/* High-level queue structures */
#define ARM_SMMU_POLL_TIMEOUT_US 100
@@ -430,9 +372,9 @@ MODULE_PARM_DESC(disable_bypass,
"Disable bypass streams such that incoming transactions from devices that are not attached to an iommu domain will report an abort back to the device and will not be allowed to pass through the SMMU.");
enum pri_resp {
- PRI_RESP_DENY,
- PRI_RESP_FAIL,
- PRI_RESP_SUCC,
+ PRI_RESP_DENY = 0,
+ PRI_RESP_FAIL = 1,
+ PRI_RESP_SUCC = 2,
};
enum arm_smmu_msi_index {
@@ -611,6 +553,7 @@ struct arm_smmu_device {
#define ARM_SMMU_FEAT_STALLS (1 << 11)
#define ARM_SMMU_FEAT_HYP (1 << 12)
#define ARM_SMMU_FEAT_STALL_FORCE (1 << 13)
+#define ARM_SMMU_FEAT_VAX (1 << 14)
u32 features;
#define ARM_SMMU_OPT_SKIP_PREFETCH (1 << 0)
@@ -836,67 +779,64 @@ static int queue_remove_raw(struct arm_smmu_queue *q, u64 *ent)
static int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent)
{
memset(cmd, 0, CMDQ_ENT_DWORDS << 3);
- cmd[0] |= (ent->opcode & CMDQ_0_OP_MASK) << CMDQ_0_OP_SHIFT;
+ cmd[0] |= FIELD_PREP(CMDQ_0_OP, ent->opcode);
switch (ent->opcode) {
case CMDQ_OP_TLBI_EL2_ALL:
case CMDQ_OP_TLBI_NSNH_ALL:
break;
case CMDQ_OP_PREFETCH_CFG:
- cmd[0] |= (u64)ent->prefetch.sid << CMDQ_PREFETCH_0_SID_SHIFT;
- cmd[1] |= ent->prefetch.size << CMDQ_PREFETCH_1_SIZE_SHIFT;
+ cmd[0] |= FIELD_PREP(CMDQ_PREFETCH_0_SID, ent->prefetch.sid);
+ cmd[1] |= FIELD_PREP(CMDQ_PREFETCH_1_SIZE, ent->prefetch.size);
cmd[1] |= ent->prefetch.addr & CMDQ_PREFETCH_1_ADDR_MASK;
break;
case CMDQ_OP_CFGI_STE:
- cmd[0] |= (u64)ent->cfgi.sid << CMDQ_CFGI_0_SID_SHIFT;
- cmd[1] |= ent->cfgi.leaf ? CMDQ_CFGI_1_LEAF : 0;
+ cmd[0] |= FIELD_PREP(CMDQ_CFGI_0_SID, ent->cfgi.sid);
+ cmd[1] |= FIELD_PREP(CMDQ_CFGI_1_LEAF, ent->cfgi.leaf);
break;
case CMDQ_OP_CFGI_ALL:
/* Cover the entire SID range */
- cmd[1] |= CMDQ_CFGI_1_RANGE_MASK << CMDQ_CFGI_1_RANGE_SHIFT;
+ cmd[1] |= FIELD_PREP(CMDQ_CFGI_1_RANGE, 31);
break;
case CMDQ_OP_TLBI_NH_VA:
- cmd[0] |= (u64)ent->tlbi.asid << CMDQ_TLBI_0_ASID_SHIFT;
- cmd[1] |= ent->tlbi.leaf ? CMDQ_TLBI_1_LEAF : 0;
+ cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_ASID, ent->tlbi.asid);
+ cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_LEAF, ent->tlbi.leaf);
cmd[1] |= ent->tlbi.addr & CMDQ_TLBI_1_VA_MASK;
break;
case CMDQ_OP_TLBI_S2_IPA:
- cmd[0] |= (u64)ent->tlbi.vmid << CMDQ_TLBI_0_VMID_SHIFT;
- cmd[1] |= ent->tlbi.leaf ? CMDQ_TLBI_1_LEAF : 0;
+ cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_VMID, ent->tlbi.vmid);
+ cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_LEAF, ent->tlbi.leaf);
cmd[1] |= ent->tlbi.addr & CMDQ_TLBI_1_IPA_MASK;
break;
case CMDQ_OP_TLBI_NH_ASID:
- cmd[0] |= (u64)ent->tlbi.asid << CMDQ_TLBI_0_ASID_SHIFT;
+ cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_ASID, ent->tlbi.asid);
/* Fallthrough */
case CMDQ_OP_TLBI_S12_VMALL:
- cmd[0] |= (u64)ent->tlbi.vmid << CMDQ_TLBI_0_VMID_SHIFT;
+ cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_VMID, ent->tlbi.vmid);
break;
case CMDQ_OP_PRI_RESP:
- cmd[0] |= ent->substream_valid ? CMDQ_0_SSV : 0;
- cmd[0] |= ent->pri.ssid << CMDQ_PRI_0_SSID_SHIFT;
- cmd[0] |= (u64)ent->pri.sid << CMDQ_PRI_0_SID_SHIFT;
- cmd[1] |= ent->pri.grpid << CMDQ_PRI_1_GRPID_SHIFT;
+ cmd[0] |= FIELD_PREP(CMDQ_0_SSV, ent->substream_valid);
+ cmd[0] |= FIELD_PREP(CMDQ_PRI_0_SSID, ent->pri.ssid);
+ cmd[0] |= FIELD_PREP(CMDQ_PRI_0_SID, ent->pri.sid);
+ cmd[1] |= FIELD_PREP(CMDQ_PRI_1_GRPID, ent->pri.grpid);
switch (ent->pri.resp) {
case PRI_RESP_DENY:
- cmd[1] |= CMDQ_PRI_1_RESP_DENY;
- break;
case PRI_RESP_FAIL:
- cmd[1] |= CMDQ_PRI_1_RESP_FAIL;
- break;
case PRI_RESP_SUCC:
- cmd[1] |= CMDQ_PRI_1_RESP_SUCC;
break;
default:
return -EINVAL;
}
+ cmd[1] |= FIELD_PREP(CMDQ_PRI_1_RESP, ent->pri.resp);
break;
case CMDQ_OP_CMD_SYNC:
if (ent->sync.msiaddr)
- cmd[0] |= CMDQ_SYNC_0_CS_IRQ;
+ cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_CS, CMDQ_SYNC_0_CS_IRQ);
else
- cmd[0] |= CMDQ_SYNC_0_CS_SEV;
- cmd[0] |= CMDQ_SYNC_0_MSH_ISH | CMDQ_SYNC_0_MSIATTR_OIWB;
- cmd[0] |= (u64)ent->sync.msidata << CMDQ_SYNC_0_MSIDATA_SHIFT;
+ cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_CS, CMDQ_SYNC_0_CS_SEV);
+ cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_MSH, ARM_SMMU_SH_ISH);
+ cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_MSIATTR, ARM_SMMU_MEMATTR_OIWB);
+ cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_MSIDATA, ent->sync.msidata);
cmd[1] |= ent->sync.msiaddr & CMDQ_SYNC_1_MSIADDR_MASK;
break;
default:
@@ -918,7 +858,7 @@ static void arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu)
u64 cmd[CMDQ_ENT_DWORDS];
struct arm_smmu_queue *q = &smmu->cmdq.q;
u32 cons = readl_relaxed(q->cons_reg);
- u32 idx = cons >> CMDQ_ERR_SHIFT & CMDQ_ERR_MASK;
+ u32 idx = FIELD_GET(CMDQ_CONS_ERR, cons);
struct arm_smmu_cmdq_ent cmd_sync = {
.opcode = CMDQ_OP_CMD_SYNC,
};
@@ -1083,8 +1023,8 @@ static void arm_smmu_write_ctx_desc(struct arm_smmu_device *smmu,
#ifdef __BIG_ENDIAN
CTXDESC_CD_0_ENDI |
#endif
- CTXDESC_CD_0_R | CTXDESC_CD_0_A | CTXDESC_CD_0_ASET_PRIVATE |
- CTXDESC_CD_0_AA64 | (u64)cfg->cd.asid << CTXDESC_CD_0_ASID_SHIFT |
+ CTXDESC_CD_0_R | CTXDESC_CD_0_A | CTXDESC_CD_0_ASET |
+ CTXDESC_CD_0_AA64 | FIELD_PREP(CTXDESC_CD_0_ASID, cfg->cd.asid) |
CTXDESC_CD_0_V;
/* STALL_MODEL==0b10 && CD.S==0 is ILLEGAL */
@@ -1093,10 +1033,10 @@ static void arm_smmu_write_ctx_desc(struct arm_smmu_device *smmu,
cfg->cdptr[0] = cpu_to_le64(val);
- val = cfg->cd.ttbr & CTXDESC_CD_1_TTB0_MASK << CTXDESC_CD_1_TTB0_SHIFT;
+ val = cfg->cd.ttbr & CTXDESC_CD_1_TTB0_MASK;
cfg->cdptr[1] = cpu_to_le64(val);
- cfg->cdptr[3] = cpu_to_le64(cfg->cd.mair << CTXDESC_CD_3_MAIR_SHIFT);
+ cfg->cdptr[3] = cpu_to_le64(cfg->cd.mair);
}
/* Stream table manipulation functions */
@@ -1105,10 +1045,8 @@ arm_smmu_write_strtab_l1_desc(__le64 *dst, struct arm_smmu_strtab_l1_desc *desc)
{
u64 val = 0;
- val |= (desc->span & STRTAB_L1_DESC_SPAN_MASK)
- << STRTAB_L1_DESC_SPAN_SHIFT;
- val |= desc->l2ptr_dma &
- STRTAB_L1_DESC_L2PTR_MASK << STRTAB_L1_DESC_L2PTR_SHIFT;
+ val |= FIELD_PREP(STRTAB_L1_DESC_SPAN, desc->span);
+ val |= desc->l2ptr_dma & STRTAB_L1_DESC_L2PTR_MASK;
*dst = cpu_to_le64(val);
}
@@ -1156,10 +1094,7 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_device *smmu, u32 sid,
};
if (val & STRTAB_STE_0_V) {
- u64 cfg;
-
- cfg = val & STRTAB_STE_0_CFG_MASK << STRTAB_STE_0_CFG_SHIFT;
- switch (cfg) {
+ switch (FIELD_GET(STRTAB_STE_0_CFG, val)) {
case STRTAB_STE_0_CFG_BYPASS:
break;
case STRTAB_STE_0_CFG_S1_TRANS:
@@ -1180,13 +1115,13 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_device *smmu, u32 sid,
/* Bypass/fault */
if (!ste->assigned || !(ste->s1_cfg || ste->s2_cfg)) {
if (!ste->assigned && disable_bypass)
- val |= STRTAB_STE_0_CFG_ABORT;
+ val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_ABORT);
else
- val |= STRTAB_STE_0_CFG_BYPASS;
+ val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_BYPASS);
dst[0] = cpu_to_le64(val);
- dst[1] = cpu_to_le64(STRTAB_STE_1_SHCFG_INCOMING
- << STRTAB_STE_1_SHCFG_SHIFT);
+ dst[1] = cpu_to_le64(FIELD_PREP(STRTAB_STE_1_SHCFG,
+ STRTAB_STE_1_SHCFG_INCOMING));
dst[2] = 0; /* Nuke the VMID */
/*
* The SMMU can perform negative caching, so we must sync
@@ -1200,41 +1135,36 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_device *smmu, u32 sid,
if (ste->s1_cfg) {
BUG_ON(ste_live);
dst[1] = cpu_to_le64(
- STRTAB_STE_1_S1C_CACHE_WBRA
- << STRTAB_STE_1_S1CIR_SHIFT |
- STRTAB_STE_1_S1C_CACHE_WBRA
- << STRTAB_STE_1_S1COR_SHIFT |
- STRTAB_STE_1_S1C_SH_ISH << STRTAB_STE_1_S1CSH_SHIFT |
+ FIELD_PREP(STRTAB_STE_1_S1CIR, STRTAB_STE_1_S1C_CACHE_WBRA) |
+ FIELD_PREP(STRTAB_STE_1_S1COR, STRTAB_STE_1_S1C_CACHE_WBRA) |
+ FIELD_PREP(STRTAB_STE_1_S1CSH, ARM_SMMU_SH_ISH) |
#ifdef CONFIG_PCI_ATS
- STRTAB_STE_1_EATS_TRANS << STRTAB_STE_1_EATS_SHIFT |
+ FIELD_PREP(STRTAB_STE_1_EATS, STRTAB_STE_1_EATS_TRANS) |
#endif
- STRTAB_STE_1_STRW_NSEL1 << STRTAB_STE_1_STRW_SHIFT);
+ FIELD_PREP(STRTAB_STE_1_STRW, STRTAB_STE_1_STRW_NSEL1));
if (smmu->features & ARM_SMMU_FEAT_STALLS &&
!(smmu->features & ARM_SMMU_FEAT_STALL_FORCE))
dst[1] |= cpu_to_le64(STRTAB_STE_1_S1STALLD);
- val |= (ste->s1_cfg->cdptr_dma & STRTAB_STE_0_S1CTXPTR_MASK
- << STRTAB_STE_0_S1CTXPTR_SHIFT) |
- STRTAB_STE_0_CFG_S1_TRANS;
+ val |= (ste->s1_cfg->cdptr_dma & STRTAB_STE_0_S1CTXPTR_MASK) |
+ FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_S1_TRANS);
}
if (ste->s2_cfg) {
BUG_ON(ste_live);
dst[2] = cpu_to_le64(
- ste->s2_cfg->vmid << STRTAB_STE_2_S2VMID_SHIFT |
- (ste->s2_cfg->vtcr & STRTAB_STE_2_VTCR_MASK)
- << STRTAB_STE_2_VTCR_SHIFT |
+ FIELD_PREP(STRTAB_STE_2_S2VMID, ste->s2_cfg->vmid) |
+ FIELD_PREP(STRTAB_STE_2_VTCR, ste->s2_cfg->vtcr) |
#ifdef __BIG_ENDIAN
STRTAB_STE_2_S2ENDI |
#endif
STRTAB_STE_2_S2PTW | STRTAB_STE_2_S2AA64 |
STRTAB_STE_2_S2R);
- dst[3] = cpu_to_le64(ste->s2_cfg->vttbr &
- STRTAB_STE_3_S2TTB_MASK << STRTAB_STE_3_S2TTB_SHIFT);
+ dst[3] = cpu_to_le64(ste->s2_cfg->vttbr & STRTAB_STE_3_S2TTB_MASK);
- val |= STRTAB_STE_0_CFG_S2_TRANS;
+ val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_S2_TRANS);
}
arm_smmu_sync_ste_for_sid(smmu, sid);
@@ -1295,7 +1225,7 @@ static irqreturn_t arm_smmu_evtq_thread(int irq, void *dev)
do {
while (!queue_remove_raw(q, evt)) {
- u8 id = evt[0] >> EVTQ_0_ID_SHIFT & EVTQ_0_ID_MASK;
+ u8 id = FIELD_GET(EVTQ_0_ID, evt[0]);
dev_info(smmu->dev, "event 0x%02x received:\n", id);
for (i = 0; i < ARRAY_SIZE(evt); ++i)
@@ -1323,11 +1253,11 @@ static void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt)
u16 grpid;
bool ssv, last;
- sid = evt[0] >> PRIQ_0_SID_SHIFT & PRIQ_0_SID_MASK;
- ssv = evt[0] & PRIQ_0_SSID_V;
- ssid = ssv ? evt[0] >> PRIQ_0_SSID_SHIFT & PRIQ_0_SSID_MASK : 0;
- last = evt[0] & PRIQ_0_PRG_LAST;
- grpid = evt[1] >> PRIQ_1_PRG_IDX_SHIFT & PRIQ_1_PRG_IDX_MASK;
+ sid = FIELD_GET(PRIQ_0_SID, evt[0]);
+ ssv = FIELD_GET(PRIQ_0_SSID_V, evt[0]);
+ ssid = ssv ? FIELD_GET(PRIQ_0_SSID, evt[0]) : 0;
+ last = FIELD_GET(PRIQ_0_PRG_LAST, evt[0]);
+ grpid = FIELD_GET(PRIQ_1_PRG_IDX, evt[1]);
dev_info(smmu->dev, "unexpected PRI request received:\n");
dev_info(smmu->dev,
@@ -1337,7 +1267,7 @@ static void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt)
evt[0] & PRIQ_0_PERM_READ ? "R" : "",
evt[0] & PRIQ_0_PERM_WRITE ? "W" : "",
evt[0] & PRIQ_0_PERM_EXEC ? "X" : "",
- evt[1] & PRIQ_1_ADDR_MASK << PRIQ_1_ADDR_SHIFT);
+ evt[1] & PRIQ_1_ADDR_MASK);
if (last) {
struct arm_smmu_cmdq_ent cmd = {
@@ -1664,7 +1594,8 @@ static int arm_smmu_domain_finalise(struct iommu_domain *domain)
switch (smmu_domain->stage) {
case ARM_SMMU_DOMAIN_S1:
- ias = VA_BITS;
+ ias = (smmu->features & ARM_SMMU_FEAT_VAX) ? 52 : 48;
+ ias = min_t(unsigned long, ias, VA_BITS);
oas = smmu->ias;
fmt = ARM_64_LPAE_S1;
finalise_stage_fn = arm_smmu_domain_finalise_s1;
@@ -1696,7 +1627,7 @@ static int arm_smmu_domain_finalise(struct iommu_domain *domain)
return -ENOMEM;
domain->pgsize_bitmap = pgtbl_cfg.pgsize_bitmap;
- domain->geometry.aperture_end = (1UL << ias) - 1;
+ domain->geometry.aperture_end = (1UL << pgtbl_cfg.ias) - 1;
domain->geometry.force_aperture = true;
ret = finalise_stage_fn(smmu_domain, &pgtbl_cfg);
@@ -2102,9 +2033,8 @@ static int arm_smmu_init_one_queue(struct arm_smmu_device *smmu,
q->ent_dwords = dwords;
q->q_base = Q_BASE_RWA;
- q->q_base |= q->base_dma & Q_BASE_ADDR_MASK << Q_BASE_ADDR_SHIFT;
- q->q_base |= (q->max_n_shift & Q_BASE_LOG2SIZE_MASK)
- << Q_BASE_LOG2SIZE_SHIFT;
+ q->q_base |= q->base_dma & Q_BASE_ADDR_MASK;
+ q->q_base |= FIELD_PREP(Q_BASE_LOG2SIZE, q->max_n_shift);
q->prod = q->cons = 0;
return 0;
@@ -2186,11 +2116,9 @@ static int arm_smmu_init_strtab_2lvl(struct arm_smmu_device *smmu)
cfg->strtab = strtab;
/* Configure strtab_base_cfg for 2 levels */
- reg = STRTAB_BASE_CFG_FMT_2LVL;
- reg |= (size & STRTAB_BASE_CFG_LOG2SIZE_MASK)
- << STRTAB_BASE_CFG_LOG2SIZE_SHIFT;
- reg |= (STRTAB_SPLIT & STRTAB_BASE_CFG_SPLIT_MASK)
- << STRTAB_BASE_CFG_SPLIT_SHIFT;
+ reg = FIELD_PREP(STRTAB_BASE_CFG_FMT, STRTAB_BASE_CFG_FMT_2LVL);
+ reg |= FIELD_PREP(STRTAB_BASE_CFG_LOG2SIZE, size);
+ reg |= FIELD_PREP(STRTAB_BASE_CFG_SPLIT, STRTAB_SPLIT);
cfg->strtab_base_cfg = reg;
return arm_smmu_init_l1_strtab(smmu);
@@ -2216,9 +2144,8 @@ static int arm_smmu_init_strtab_linear(struct arm_smmu_device *smmu)
cfg->num_l1_ents = 1 << smmu->sid_bits;
/* Configure strtab_base_cfg for a linear table covering all SIDs */
- reg = STRTAB_BASE_CFG_FMT_LINEAR;
- reg |= (smmu->sid_bits & STRTAB_BASE_CFG_LOG2SIZE_MASK)
- << STRTAB_BASE_CFG_LOG2SIZE_SHIFT;
+ reg = FIELD_PREP(STRTAB_BASE_CFG_FMT, STRTAB_BASE_CFG_FMT_LINEAR);
+ reg |= FIELD_PREP(STRTAB_BASE_CFG_LOG2SIZE, smmu->sid_bits);
cfg->strtab_base_cfg = reg;
arm_smmu_init_bypass_stes(strtab, cfg->num_l1_ents);
@@ -2239,8 +2166,7 @@ static int arm_smmu_init_strtab(struct arm_smmu_device *smmu)
return ret;
/* Set the strtab base address */
- reg = smmu->strtab_cfg.strtab_dma &
- STRTAB_BASE_ADDR_MASK << STRTAB_BASE_ADDR_SHIFT;
+ reg = smmu->strtab_cfg.strtab_dma & STRTAB_BASE_ADDR_MASK;
reg |= STRTAB_BASE_RA;
smmu->strtab_cfg.strtab_base = reg;
@@ -2303,11 +2229,11 @@ static void arm_smmu_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg)
phys_addr_t *cfg = arm_smmu_msi_cfg[desc->platform.msi_index];
doorbell = (((u64)msg->address_hi) << 32) | msg->address_lo;
- doorbell &= MSI_CFG0_ADDR_MASK << MSI_CFG0_ADDR_SHIFT;
+ doorbell &= MSI_CFG0_ADDR_MASK;
writeq_relaxed(doorbell, smmu->base + cfg[0]);
writel_relaxed(msg->data, smmu->base + cfg[1]);
- writel_relaxed(MSI_CFG2_MEMATTR_DEVICE_nGnRE, smmu->base + cfg[2]);
+ writel_relaxed(ARM_SMMU_MEMATTR_DEVICE_nGnRE, smmu->base + cfg[2]);
}
static void arm_smmu_setup_msis(struct arm_smmu_device *smmu)
@@ -2328,10 +2254,15 @@ static void arm_smmu_setup_msis(struct arm_smmu_device *smmu)
if (!(smmu->features & ARM_SMMU_FEAT_MSI))
return;
+ if (!dev->msi_domain) {
+ dev_info(smmu->dev, "msi_domain absent - falling back to wired irqs\n");
+ return;
+ }
+
/* Allocate MSIs for evtq, gerror and priq. Ignore cmdq */
ret = platform_msi_domain_alloc_irqs(dev, nvec, arm_smmu_write_msi_msg);
if (ret) {
- dev_warn(dev, "failed to allocate MSIs\n");
+ dev_warn(dev, "failed to allocate MSIs - falling back to wired irqs\n");
return;
}
@@ -2370,6 +2301,8 @@ static void arm_smmu_setup_unique_irqs(struct arm_smmu_device *smmu)
"arm-smmu-v3-evtq", smmu);
if (ret < 0)
dev_warn(smmu->dev, "failed to enable evtq irq\n");
+ } else {
+ dev_warn(smmu->dev, "no evtq irq - events will not be reported!\n");
}
irq = smmu->gerr_irq;
@@ -2378,6 +2311,8 @@ static void arm_smmu_setup_unique_irqs(struct arm_smmu_device *smmu)
0, "arm-smmu-v3-gerror", smmu);
if (ret < 0)
dev_warn(smmu->dev, "failed to enable gerror irq\n");
+ } else {
+ dev_warn(smmu->dev, "no gerr irq - errors will not be reported!\n");
}
if (smmu->features & ARM_SMMU_FEAT_PRI) {
@@ -2391,6 +2326,8 @@ static void arm_smmu_setup_unique_irqs(struct arm_smmu_device *smmu)
if (ret < 0)
dev_warn(smmu->dev,
"failed to enable priq irq\n");
+ } else {
+ dev_warn(smmu->dev, "no priq irq - PRI will be broken\n");
}
}
}
@@ -2463,12 +2400,12 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu, bool bypass)
return ret;
/* CR1 (table and queue memory attributes) */
- reg = (CR1_SH_ISH << CR1_TABLE_SH_SHIFT) |
- (CR1_CACHE_WB << CR1_TABLE_OC_SHIFT) |
- (CR1_CACHE_WB << CR1_TABLE_IC_SHIFT) |
- (CR1_SH_ISH << CR1_QUEUE_SH_SHIFT) |
- (CR1_CACHE_WB << CR1_QUEUE_OC_SHIFT) |
- (CR1_CACHE_WB << CR1_QUEUE_IC_SHIFT);
+ reg = FIELD_PREP(CR1_TABLE_SH, ARM_SMMU_SH_ISH) |
+ FIELD_PREP(CR1_TABLE_OC, CR1_CACHE_WB) |
+ FIELD_PREP(CR1_TABLE_IC, CR1_CACHE_WB) |
+ FIELD_PREP(CR1_QUEUE_SH, ARM_SMMU_SH_ISH) |
+ FIELD_PREP(CR1_QUEUE_OC, CR1_CACHE_WB) |
+ FIELD_PREP(CR1_QUEUE_IC, CR1_CACHE_WB);
writel_relaxed(reg, smmu->base + ARM_SMMU_CR1);
/* CR2 (random crap) */
@@ -2578,7 +2515,7 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
reg = readl_relaxed(smmu->base + ARM_SMMU_IDR0);
/* 2-level structures */
- if ((reg & IDR0_ST_LVL_MASK << IDR0_ST_LVL_SHIFT) == IDR0_ST_LVL_2LVL)
+ if (FIELD_GET(IDR0_ST_LVL, reg) == IDR0_ST_LVL_2LVL)
smmu->features |= ARM_SMMU_FEAT_2_LVL_STRTAB;
if (reg & IDR0_CD2L)
@@ -2589,7 +2526,7 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
* We currently require the same endianness as the CPU, but this
* could be changed later by adding a new IO_PGTABLE_QUIRK.
*/
- switch (reg & IDR0_TTENDIAN_MASK << IDR0_TTENDIAN_SHIFT) {
+ switch (FIELD_GET(IDR0_TTENDIAN, reg)) {
case IDR0_TTENDIAN_MIXED:
smmu->features |= ARM_SMMU_FEAT_TT_LE | ARM_SMMU_FEAT_TT_BE;
break;
@@ -2631,7 +2568,7 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
dev_warn(smmu->dev, "IDR0.COHACC overridden by FW configuration (%s)\n",
coherent ? "true" : "false");
- switch (reg & IDR0_STALL_MODEL_MASK << IDR0_STALL_MODEL_SHIFT) {
+ switch (FIELD_GET(IDR0_STALL_MODEL, reg)) {
case IDR0_STALL_MODEL_FORCE:
smmu->features |= ARM_SMMU_FEAT_STALL_FORCE;
/* Fallthrough */
@@ -2651,7 +2588,7 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
}
/* We only support the AArch64 table format at present */
- switch (reg & IDR0_TTF_MASK << IDR0_TTF_SHIFT) {
+ switch (FIELD_GET(IDR0_TTF, reg)) {
case IDR0_TTF_AARCH32_64:
smmu->ias = 40;
/* Fallthrough */
@@ -2674,22 +2611,22 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
}
/* Queue sizes, capped at 4k */
- smmu->cmdq.q.max_n_shift = min((u32)CMDQ_MAX_SZ_SHIFT,
- reg >> IDR1_CMDQ_SHIFT & IDR1_CMDQ_MASK);
+ smmu->cmdq.q.max_n_shift = min_t(u32, CMDQ_MAX_SZ_SHIFT,
+ FIELD_GET(IDR1_CMDQS, reg));
if (!smmu->cmdq.q.max_n_shift) {
/* Odd alignment restrictions on the base, so ignore for now */
dev_err(smmu->dev, "unit-length command queue not supported\n");
return -ENXIO;
}
- smmu->evtq.q.max_n_shift = min((u32)EVTQ_MAX_SZ_SHIFT,
- reg >> IDR1_EVTQ_SHIFT & IDR1_EVTQ_MASK);
- smmu->priq.q.max_n_shift = min((u32)PRIQ_MAX_SZ_SHIFT,
- reg >> IDR1_PRIQ_SHIFT & IDR1_PRIQ_MASK);
+ smmu->evtq.q.max_n_shift = min_t(u32, EVTQ_MAX_SZ_SHIFT,
+ FIELD_GET(IDR1_EVTQS, reg));
+ smmu->priq.q.max_n_shift = min_t(u32, PRIQ_MAX_SZ_SHIFT,
+ FIELD_GET(IDR1_PRIQS, reg));
/* SID/SSID sizes */
- smmu->ssid_bits = reg >> IDR1_SSID_SHIFT & IDR1_SSID_MASK;
- smmu->sid_bits = reg >> IDR1_SID_SHIFT & IDR1_SID_MASK;
+ smmu->ssid_bits = FIELD_GET(IDR1_SSIDSIZE, reg);
+ smmu->sid_bits = FIELD_GET(IDR1_SIDSIZE, reg);
/*
* If the SMMU supports fewer bits than would fill a single L2 stream
@@ -2702,8 +2639,7 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
reg = readl_relaxed(smmu->base + ARM_SMMU_IDR5);
/* Maximum number of outstanding stalls */
- smmu->evtq.max_stalls = reg >> IDR5_STALL_MAX_SHIFT
- & IDR5_STALL_MAX_MASK;
+ smmu->evtq.max_stalls = FIELD_GET(IDR5_STALL_MAX, reg);
/* Page sizes */
if (reg & IDR5_GRAN64K)
@@ -2713,13 +2649,12 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
if (reg & IDR5_GRAN4K)
smmu->pgsize_bitmap |= SZ_4K | SZ_2M | SZ_1G;
- if (arm_smmu_ops.pgsize_bitmap == -1UL)
- arm_smmu_ops.pgsize_bitmap = smmu->pgsize_bitmap;
- else
- arm_smmu_ops.pgsize_bitmap |= smmu->pgsize_bitmap;
+ /* Input address size */
+ if (FIELD_GET(IDR5_VAX, reg) == IDR5_VAX_52_BIT)
+ smmu->features |= ARM_SMMU_FEAT_VAX;
/* Output address size */
- switch (reg & IDR5_OAS_MASK << IDR5_OAS_SHIFT) {
+ switch (FIELD_GET(IDR5_OAS, reg)) {
case IDR5_OAS_32_BIT:
smmu->oas = 32;
break;
@@ -2735,6 +2670,10 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
case IDR5_OAS_44_BIT:
smmu->oas = 44;
break;
+ case IDR5_OAS_52_BIT:
+ smmu->oas = 52;
+ smmu->pgsize_bitmap |= 1ULL << 42; /* 4TB */
+ break;
default:
dev_info(smmu->dev,
"unknown output address size. Truncating to 48-bit\n");
@@ -2743,6 +2682,11 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
smmu->oas = 48;
}
+ if (arm_smmu_ops.pgsize_bitmap == -1UL)
+ arm_smmu_ops.pgsize_bitmap = smmu->pgsize_bitmap;
+ else
+ arm_smmu_ops.pgsize_bitmap |= smmu->pgsize_bitmap;
+
/* Set the DMA mask for our table walker */
if (dma_set_mask_and_coherent(smmu->dev, DMA_BIT_MASK(smmu->oas)))
dev_warn(smmu->dev,
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 25914d36c5ac..f05f3cf90756 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -19,6 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <linux/acpi_iort.h>
#include <linux/device.h>
#include <linux/dma-iommu.h>
#include <linux/gfp.h>
@@ -167,13 +168,18 @@ EXPORT_SYMBOL(iommu_put_dma_cookie);
*
* IOMMU drivers can use this to implement their .get_resv_regions callback
* for general non-IOMMU-specific reservations. Currently, this covers host
- * bridge windows for PCI devices.
+ * bridge windows for PCI devices and GICv3 ITS region reservation on ACPI
+ * based ARM platforms that may require HW MSI reservation.
*/
void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list)
{
struct pci_host_bridge *bridge;
struct resource_entry *window;
+ if (!is_of_node(dev->iommu_fwspec->iommu_fwnode) &&
+ iort_iommu_msi_get_resv_regions(dev, list) < 0)
+ return;
+
if (!dev_is_pci(dev))
return;
diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c
index 9a7ffd13c7f0..accf58388bdb 100644
--- a/drivers/iommu/dmar.c
+++ b/drivers/iommu/dmar.c
@@ -806,7 +806,7 @@ int __init dmar_dev_scope_init(void)
return dmar_dev_scope_status;
}
-void dmar_register_bus_notifier(void)
+void __init dmar_register_bus_notifier(void)
{
bus_register_notifier(&pci_bus_type, &dmar_pci_bus_nb);
}
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c
index 2138102ef611..85879cfec52f 100644
--- a/drivers/iommu/exynos-iommu.c
+++ b/drivers/iommu/exynos-iommu.c
@@ -17,6 +17,7 @@
#include <linux/io.h>
#include <linux/iommu.h>
#include <linux/interrupt.h>
+#include <linux/kmemleak.h>
#include <linux/list.h>
#include <linux/of.h>
#include <linux/of_iommu.h>
@@ -1238,17 +1239,6 @@ static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *iommu_domain,
return phys;
}
-static struct iommu_group *get_device_iommu_group(struct device *dev)
-{
- struct iommu_group *group;
-
- group = iommu_group_get(dev);
- if (!group)
- group = iommu_group_alloc();
-
- return group;
-}
-
static int exynos_iommu_add_device(struct device *dev)
{
struct exynos_iommu_owner *owner = dev->archdata.iommu;
@@ -1344,7 +1334,7 @@ static const struct iommu_ops exynos_iommu_ops = {
.unmap = exynos_iommu_unmap,
.map_sg = default_iommu_map_sg,
.iova_to_phys = exynos_iommu_iova_to_phys,
- .device_group = get_device_iommu_group,
+ .device_group = generic_device_group,
.add_device = exynos_iommu_add_device,
.remove_device = exynos_iommu_remove_device,
.pgsize_bitmap = SECT_SIZE | LPAGE_SIZE | SPAGE_SIZE,
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 582fd01cb7d1..749d8f235346 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -31,6 +31,7 @@
#include <linux/pci.h>
#include <linux/dmar.h>
#include <linux/dma-mapping.h>
+#include <linux/dma-direct.h>
#include <linux/mempool.h>
#include <linux/memory.h>
#include <linux/cpu.h>
@@ -45,6 +46,7 @@
#include <linux/pci-ats.h>
#include <linux/memblock.h>
#include <linux/dma-contiguous.h>
+#include <linux/dma-direct.h>
#include <linux/crash_dump.h>
#include <asm/irq_remapping.h>
#include <asm/cacheflush.h>
@@ -3707,61 +3709,30 @@ static void *intel_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t flags,
unsigned long attrs)
{
- struct page *page = NULL;
- int order;
+ void *vaddr;
- size = PAGE_ALIGN(size);
- order = get_order(size);
+ vaddr = dma_direct_alloc(dev, size, dma_handle, flags, attrs);
+ if (iommu_no_mapping(dev) || !vaddr)
+ return vaddr;
- if (!iommu_no_mapping(dev))
- flags &= ~(GFP_DMA | GFP_DMA32);
- else if (dev->coherent_dma_mask < dma_get_required_mask(dev)) {
- if (dev->coherent_dma_mask < DMA_BIT_MASK(32))
- flags |= GFP_DMA;
- else
- flags |= GFP_DMA32;
- }
-
- if (gfpflags_allow_blocking(flags)) {
- unsigned int count = size >> PAGE_SHIFT;
-
- page = dma_alloc_from_contiguous(dev, count, order, flags);
- if (page && iommu_no_mapping(dev) &&
- page_to_phys(page) + size > dev->coherent_dma_mask) {
- dma_release_from_contiguous(dev, page, count);
- page = NULL;
- }
- }
-
- if (!page)
- page = alloc_pages(flags, order);
- if (!page)
- return NULL;
- memset(page_address(page), 0, size);
-
- *dma_handle = __intel_map_single(dev, page_to_phys(page), size,
- DMA_BIDIRECTIONAL,
- dev->coherent_dma_mask);
- if (*dma_handle)
- return page_address(page);
- if (!dma_release_from_contiguous(dev, page, size >> PAGE_SHIFT))
- __free_pages(page, order);
+ *dma_handle = __intel_map_single(dev, virt_to_phys(vaddr),
+ PAGE_ALIGN(size), DMA_BIDIRECTIONAL,
+ dev->coherent_dma_mask);
+ if (!*dma_handle)
+ goto out_free_pages;
+ return vaddr;
+out_free_pages:
+ dma_direct_free(dev, size, vaddr, *dma_handle, attrs);
return NULL;
}
static void intel_free_coherent(struct device *dev, size_t size, void *vaddr,
dma_addr_t dma_handle, unsigned long attrs)
{
- int order;
- struct page *page = virt_to_page(vaddr);
-
- size = PAGE_ALIGN(size);
- order = get_order(size);
-
- intel_unmap(dev, dma_handle, size);
- if (!dma_release_from_contiguous(dev, page, size >> PAGE_SHIFT))
- __free_pages(page, order);
+ if (!iommu_no_mapping(dev))
+ intel_unmap(dev, dma_handle, PAGE_ALIGN(size));
+ dma_direct_free(dev, size, vaddr, dma_handle, attrs);
}
static void intel_unmap_sg(struct device *dev, struct scatterlist *sglist,
@@ -3871,7 +3842,7 @@ const struct dma_map_ops intel_dma_ops = {
.unmap_page = intel_unmap_page,
.mapping_error = intel_mapping_error,
#ifdef CONFIG_X86
- .dma_supported = x86_dma_supported,
+ .dma_supported = dma_direct_supported,
#endif
};
@@ -5072,7 +5043,6 @@ static size_t intel_iommu_unmap(struct iommu_domain *domain,
{
struct dmar_domain *dmar_domain = to_dmar_domain(domain);
struct page *freelist = NULL;
- struct intel_iommu *iommu;
unsigned long start_pfn, last_pfn;
unsigned int npages;
int iommu_id, level = 0;
@@ -5091,12 +5061,9 @@ static size_t intel_iommu_unmap(struct iommu_domain *domain,
npages = last_pfn - start_pfn + 1;
- for_each_domain_iommu(iommu_id, dmar_domain) {
- iommu = g_iommus[iommu_id];
-
+ for_each_domain_iommu(iommu_id, dmar_domain)
iommu_flush_iotlb_psi(g_iommus[iommu_id], dmar_domain,
start_pfn, npages, !freelist, 0);
- }
dma_free_pagelist(freelist);
diff --git a/drivers/iommu/intel-svm.c b/drivers/iommu/intel-svm.c
index 99bc9bd64b9e..e8cd984cf9c8 100644
--- a/drivers/iommu/intel-svm.c
+++ b/drivers/iommu/intel-svm.c
@@ -396,6 +396,7 @@ int intel_svm_bind_mm(struct device *dev, int *pasid, int flags, struct svm_dev_
pasid_max - 1, GFP_KERNEL);
if (ret < 0) {
kfree(svm);
+ kfree(sdev);
goto out;
}
svm->pasid = ret;
@@ -422,17 +423,13 @@ int intel_svm_bind_mm(struct device *dev, int *pasid, int flags, struct svm_dev_
iommu->pasid_table[svm->pasid].val = pasid_entry_val;
wmb();
- /* In caching mode, we still have to flush with PASID 0 when
- * a PASID table entry becomes present. Not entirely clear
- * *why* that would be the case — surely we could just issue
- * a flush with the PASID value that we've changed? The PASID
- * is the index into the table, after all. It's not like domain
- * IDs in the case of the equivalent context-entry change in
- * caching mode. And for that matter it's not entirely clear why
- * a VMM would be in the business of caching the PASID table
- * anyway. Surely that can be left entirely to the guest? */
+
+ /*
+ * Flush PASID cache when a PASID table entry becomes
+ * present.
+ */
if (cap_caching_mode(iommu->cap))
- intel_flush_pasid_dev(svm, sdev, 0);
+ intel_flush_pasid_dev(svm, sdev, svm->pasid);
}
list_add_rcu(&sdev->list, &svm->devs);
diff --git a/drivers/iommu/io-pgtable-arm-v7s.c b/drivers/iommu/io-pgtable-arm-v7s.c
index 2ca08dc9331c..10e4a3d11c02 100644
--- a/drivers/iommu/io-pgtable-arm-v7s.c
+++ b/drivers/iommu/io-pgtable-arm-v7s.c
@@ -357,8 +357,8 @@ static bool arm_v7s_pte_is_cont(arm_v7s_iopte pte, int lvl)
return false;
}
-static int __arm_v7s_unmap(struct arm_v7s_io_pgtable *, unsigned long,
- size_t, int, arm_v7s_iopte *);
+static size_t __arm_v7s_unmap(struct arm_v7s_io_pgtable *, unsigned long,
+ size_t, int, arm_v7s_iopte *);
static int arm_v7s_init_pte(struct arm_v7s_io_pgtable *data,
unsigned long iova, phys_addr_t paddr, int prot,
@@ -541,9 +541,10 @@ static arm_v7s_iopte arm_v7s_split_cont(struct arm_v7s_io_pgtable *data,
return pte;
}
-static int arm_v7s_split_blk_unmap(struct arm_v7s_io_pgtable *data,
- unsigned long iova, size_t size,
- arm_v7s_iopte blk_pte, arm_v7s_iopte *ptep)
+static size_t arm_v7s_split_blk_unmap(struct arm_v7s_io_pgtable *data,
+ unsigned long iova, size_t size,
+ arm_v7s_iopte blk_pte,
+ arm_v7s_iopte *ptep)
{
struct io_pgtable_cfg *cfg = &data->iop.cfg;
arm_v7s_iopte pte, *tablep;
@@ -584,9 +585,9 @@ static int arm_v7s_split_blk_unmap(struct arm_v7s_io_pgtable *data,
return size;
}
-static int __arm_v7s_unmap(struct arm_v7s_io_pgtable *data,
- unsigned long iova, size_t size, int lvl,
- arm_v7s_iopte *ptep)
+static size_t __arm_v7s_unmap(struct arm_v7s_io_pgtable *data,
+ unsigned long iova, size_t size, int lvl,
+ arm_v7s_iopte *ptep)
{
arm_v7s_iopte pte[ARM_V7S_CONT_PAGES];
struct io_pgtable *iop = &data->iop;
@@ -656,8 +657,8 @@ static int __arm_v7s_unmap(struct arm_v7s_io_pgtable *data,
return __arm_v7s_unmap(data, iova, size, lvl + 1, ptep);
}
-static int arm_v7s_unmap(struct io_pgtable_ops *ops, unsigned long iova,
- size_t size)
+static size_t arm_v7s_unmap(struct io_pgtable_ops *ops, unsigned long iova,
+ size_t size)
{
struct arm_v7s_io_pgtable *data = io_pgtable_ops_to_data(ops);
diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
index 51e5c43caed1..39c2a056da21 100644
--- a/drivers/iommu/io-pgtable-arm.c
+++ b/drivers/iommu/io-pgtable-arm.c
@@ -21,6 +21,7 @@
#define pr_fmt(fmt) "arm-lpae io-pgtable: " fmt
#include <linux/atomic.h>
+#include <linux/bitops.h>
#include <linux/iommu.h>
#include <linux/kernel.h>
#include <linux/sizes.h>
@@ -32,7 +33,7 @@
#include "io-pgtable.h"
-#define ARM_LPAE_MAX_ADDR_BITS 48
+#define ARM_LPAE_MAX_ADDR_BITS 52
#define ARM_LPAE_S2_MAX_CONCAT_PAGES 16
#define ARM_LPAE_MAX_LEVELS 4
@@ -86,6 +87,8 @@
#define ARM_LPAE_PTE_TYPE_TABLE 3
#define ARM_LPAE_PTE_TYPE_PAGE 3
+#define ARM_LPAE_PTE_ADDR_MASK GENMASK_ULL(47,12)
+
#define ARM_LPAE_PTE_NSTABLE (((arm_lpae_iopte)1) << 63)
#define ARM_LPAE_PTE_XN (((arm_lpae_iopte)3) << 53)
#define ARM_LPAE_PTE_AF (((arm_lpae_iopte)1) << 10)
@@ -159,6 +162,7 @@
#define ARM_LPAE_TCR_PS_42_BIT 0x3ULL
#define ARM_LPAE_TCR_PS_44_BIT 0x4ULL
#define ARM_LPAE_TCR_PS_48_BIT 0x5ULL
+#define ARM_LPAE_TCR_PS_52_BIT 0x6ULL
#define ARM_LPAE_MAIR_ATTR_SHIFT(n) ((n) << 3)
#define ARM_LPAE_MAIR_ATTR_MASK 0xff
@@ -170,9 +174,7 @@
#define ARM_LPAE_MAIR_ATTR_IDX_DEV 2
/* IOPTE accessors */
-#define iopte_deref(pte,d) \
- (__va((pte) & ((1ULL << ARM_LPAE_MAX_ADDR_BITS) - 1) \
- & ~(ARM_LPAE_GRANULE(d) - 1ULL)))
+#define iopte_deref(pte,d) __va(iopte_to_paddr(pte, d))
#define iopte_type(pte,l) \
(((pte) >> ARM_LPAE_PTE_TYPE_SHIFT) & ARM_LPAE_PTE_TYPE_MASK)
@@ -184,12 +186,6 @@
(iopte_type(pte,l) == ARM_LPAE_PTE_TYPE_PAGE) : \
(iopte_type(pte,l) == ARM_LPAE_PTE_TYPE_BLOCK))
-#define iopte_to_pfn(pte,d) \
- (((pte) & ((1ULL << ARM_LPAE_MAX_ADDR_BITS) - 1)) >> (d)->pg_shift)
-
-#define pfn_to_iopte(pfn,d) \
- (((pfn) << (d)->pg_shift) & ((1ULL << ARM_LPAE_MAX_ADDR_BITS) - 1))
-
struct arm_lpae_io_pgtable {
struct io_pgtable iop;
@@ -203,6 +199,27 @@ struct arm_lpae_io_pgtable {
typedef u64 arm_lpae_iopte;
+static arm_lpae_iopte paddr_to_iopte(phys_addr_t paddr,
+ struct arm_lpae_io_pgtable *data)
+{
+ arm_lpae_iopte pte = paddr;
+
+ /* Of the bits which overlap, either 51:48 or 15:12 are always RES0 */
+ return (pte | (pte >> (48 - 12))) & ARM_LPAE_PTE_ADDR_MASK;
+}
+
+static phys_addr_t iopte_to_paddr(arm_lpae_iopte pte,
+ struct arm_lpae_io_pgtable *data)
+{
+ u64 paddr = pte & ARM_LPAE_PTE_ADDR_MASK;
+
+ if (data->pg_shift < 16)
+ return paddr;
+
+ /* Rotate the packed high-order bits back to the top */
+ return (paddr | (paddr << (48 - 12))) & (ARM_LPAE_PTE_ADDR_MASK << 4);
+}
+
static bool selftest_running = false;
static dma_addr_t __arm_lpae_dma_addr(void *pages)
@@ -268,9 +285,9 @@ static void __arm_lpae_set_pte(arm_lpae_iopte *ptep, arm_lpae_iopte pte,
__arm_lpae_sync_pte(ptep, cfg);
}
-static int __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
- unsigned long iova, size_t size, int lvl,
- arm_lpae_iopte *ptep);
+static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
+ unsigned long iova, size_t size, int lvl,
+ arm_lpae_iopte *ptep);
static void __arm_lpae_init_pte(struct arm_lpae_io_pgtable *data,
phys_addr_t paddr, arm_lpae_iopte prot,
@@ -287,7 +304,7 @@ static void __arm_lpae_init_pte(struct arm_lpae_io_pgtable *data,
pte |= ARM_LPAE_PTE_TYPE_BLOCK;
pte |= ARM_LPAE_PTE_AF | ARM_LPAE_PTE_SH_IS;
- pte |= pfn_to_iopte(paddr >> data->pg_shift, data);
+ pte |= paddr_to_iopte(paddr, data);
__arm_lpae_set_pte(ptep, pte, &data->iop.cfg);
}
@@ -506,10 +523,10 @@ static void arm_lpae_free_pgtable(struct io_pgtable *iop)
kfree(data);
}
-static int arm_lpae_split_blk_unmap(struct arm_lpae_io_pgtable *data,
- unsigned long iova, size_t size,
- arm_lpae_iopte blk_pte, int lvl,
- arm_lpae_iopte *ptep)
+static size_t arm_lpae_split_blk_unmap(struct arm_lpae_io_pgtable *data,
+ unsigned long iova, size_t size,
+ arm_lpae_iopte blk_pte, int lvl,
+ arm_lpae_iopte *ptep)
{
struct io_pgtable_cfg *cfg = &data->iop.cfg;
arm_lpae_iopte pte, *tablep;
@@ -528,7 +545,7 @@ static int arm_lpae_split_blk_unmap(struct arm_lpae_io_pgtable *data,
if (size == split_sz)
unmap_idx = ARM_LPAE_LVL_IDX(iova, lvl, data);
- blk_paddr = iopte_to_pfn(blk_pte, data) << data->pg_shift;
+ blk_paddr = iopte_to_paddr(blk_pte, data);
pte = iopte_prot(blk_pte);
for (i = 0; i < tablesz / sizeof(pte); i++, blk_paddr += split_sz) {
@@ -560,9 +577,9 @@ static int arm_lpae_split_blk_unmap(struct arm_lpae_io_pgtable *data,
return size;
}
-static int __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
- unsigned long iova, size_t size, int lvl,
- arm_lpae_iopte *ptep)
+static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
+ unsigned long iova, size_t size, int lvl,
+ arm_lpae_iopte *ptep)
{
arm_lpae_iopte pte;
struct io_pgtable *iop = &data->iop;
@@ -606,8 +623,8 @@ static int __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
return __arm_lpae_unmap(data, iova, size, lvl + 1, ptep);
}
-static int arm_lpae_unmap(struct io_pgtable_ops *ops, unsigned long iova,
- size_t size)
+static size_t arm_lpae_unmap(struct io_pgtable_ops *ops, unsigned long iova,
+ size_t size)
{
struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops);
arm_lpae_iopte *ptep = data->pgd;
@@ -652,12 +669,13 @@ static phys_addr_t arm_lpae_iova_to_phys(struct io_pgtable_ops *ops,
found_translation:
iova &= (ARM_LPAE_BLOCK_SIZE(lvl, data) - 1);
- return ((phys_addr_t)iopte_to_pfn(pte,data) << data->pg_shift) | iova;
+ return iopte_to_paddr(pte, data) | iova;
}
static void arm_lpae_restrict_pgsizes(struct io_pgtable_cfg *cfg)
{
- unsigned long granule;
+ unsigned long granule, page_sizes;
+ unsigned int max_addr_bits = 48;
/*
* We need to restrict the supported page sizes to match the
@@ -677,17 +695,24 @@ static void arm_lpae_restrict_pgsizes(struct io_pgtable_cfg *cfg)
switch (granule) {
case SZ_4K:
- cfg->pgsize_bitmap &= (SZ_4K | SZ_2M | SZ_1G);
+ page_sizes = (SZ_4K | SZ_2M | SZ_1G);
break;
case SZ_16K:
- cfg->pgsize_bitmap &= (SZ_16K | SZ_32M);
+ page_sizes = (SZ_16K | SZ_32M);
break;
case SZ_64K:
- cfg->pgsize_bitmap &= (SZ_64K | SZ_512M);
+ max_addr_bits = 52;
+ page_sizes = (SZ_64K | SZ_512M);
+ if (cfg->oas > 48)
+ page_sizes |= 1ULL << 42; /* 4TB */
break;
default:
- cfg->pgsize_bitmap = 0;
+ page_sizes = 0;
}
+
+ cfg->pgsize_bitmap &= page_sizes;
+ cfg->ias = min(cfg->ias, max_addr_bits);
+ cfg->oas = min(cfg->oas, max_addr_bits);
}
static struct arm_lpae_io_pgtable *
@@ -784,6 +809,9 @@ arm_64_lpae_alloc_pgtable_s1(struct io_pgtable_cfg *cfg, void *cookie)
case 48:
reg |= (ARM_LPAE_TCR_PS_48_BIT << ARM_LPAE_TCR_IPS_SHIFT);
break;
+ case 52:
+ reg |= (ARM_LPAE_TCR_PS_52_BIT << ARM_LPAE_TCR_IPS_SHIFT);
+ break;
default:
goto out_free_data;
}
@@ -891,6 +919,9 @@ arm_64_lpae_alloc_pgtable_s2(struct io_pgtable_cfg *cfg, void *cookie)
case 48:
reg |= (ARM_LPAE_TCR_PS_48_BIT << ARM_LPAE_TCR_PS_SHIFT);
break;
+ case 52:
+ reg |= (ARM_LPAE_TCR_PS_52_BIT << ARM_LPAE_TCR_PS_SHIFT);
+ break;
default:
goto out_free_data;
}
diff --git a/drivers/iommu/io-pgtable.h b/drivers/iommu/io-pgtable.h
index cd2e1eafffe6..2df79093cad9 100644
--- a/drivers/iommu/io-pgtable.h
+++ b/drivers/iommu/io-pgtable.h
@@ -119,8 +119,8 @@ struct io_pgtable_cfg {
struct io_pgtable_ops {
int (*map)(struct io_pgtable_ops *ops, unsigned long iova,
phys_addr_t paddr, size_t size, int prot);
- int (*unmap)(struct io_pgtable_ops *ops, unsigned long iova,
- size_t size);
+ size_t (*unmap)(struct io_pgtable_ops *ops, unsigned long iova,
+ size_t size);
phys_addr_t (*iova_to_phys)(struct io_pgtable_ops *ops,
unsigned long iova);
};
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 69fef991c651..d2aa23202bb9 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -1573,10 +1573,10 @@ static size_t __iommu_unmap(struct iommu_domain *domain,
if (unlikely(ops->unmap == NULL ||
domain->pgsize_bitmap == 0UL))
- return -ENODEV;
+ return 0;
if (unlikely(!(domain->type & __IOMMU_DOMAIN_PAGING)))
- return -EINVAL;
+ return 0;
/* find out the minimum page size supported */
min_pagesz = 1 << __ffs(domain->pgsize_bitmap);
@@ -1589,7 +1589,7 @@ static size_t __iommu_unmap(struct iommu_domain *domain,
if (!IS_ALIGNED(iova | size, min_pagesz)) {
pr_err("unaligned: iova 0x%lx size 0x%zx min_pagesz 0x%x\n",
iova, size, min_pagesz);
- return -EINVAL;
+ return 0;
}
pr_debug("unmap this: iova 0x%lx size 0x%zx\n", iova, size);
diff --git a/drivers/iommu/irq_remapping.c b/drivers/iommu/irq_remapping.c
index 49721b4e1975..496deee3ae3a 100644
--- a/drivers/iommu/irq_remapping.c
+++ b/drivers/iommu/irq_remapping.c
@@ -27,7 +27,7 @@ int disable_irq_post = 0;
static int disable_irq_remap;
static struct irq_remap_ops *remap_ops;
-static void irq_remapping_disable_io_apic(void)
+static void irq_remapping_restore_boot_irq_mode(void)
{
/*
* With interrupt-remapping, for now we will use virtual wire A
@@ -42,7 +42,7 @@ static void irq_remapping_disable_io_apic(void)
static void __init irq_remapping_modify_x86_ops(void)
{
- x86_io_apic_ops.disable = irq_remapping_disable_io_apic;
+ x86_apic_ops.restore = irq_remapping_restore_boot_irq_mode;
}
static __init int setup_nointremap(char *str)
diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c
index f227d73e7bf6..f2832a10fcea 100644
--- a/drivers/iommu/mtk_iommu.c
+++ b/drivers/iommu/mtk_iommu.c
@@ -60,7 +60,7 @@
(((prot) & 0x3) << F_MMU_TF_PROTECT_SEL_SHIFT(data))
#define REG_MMU_IVRP_PADDR 0x114
-#define F_MMU_IVRP_PA_SET(pa, ext) (((pa) >> 1) | ((!!(ext)) << 31))
+
#define REG_MMU_VLD_PA_RNG 0x118
#define F_MMU_VLD_PA_RNG(EA, SA) (((EA) << 8) | (SA))
@@ -539,8 +539,13 @@ static int mtk_iommu_hw_init(const struct mtk_iommu_data *data)
F_INT_PRETETCH_TRANSATION_FIFO_FAULT;
writel_relaxed(regval, data->base + REG_MMU_INT_MAIN_CONTROL);
- writel_relaxed(F_MMU_IVRP_PA_SET(data->protect_base, data->enable_4GB),
- data->base + REG_MMU_IVRP_PADDR);
+ if (data->m4u_plat == M4U_MT8173)
+ regval = (data->protect_base >> 1) | (data->enable_4GB << 31);
+ else
+ regval = lower_32_bits(data->protect_base) |
+ upper_32_bits(data->protect_base);
+ writel_relaxed(regval, data->base + REG_MMU_IVRP_PADDR);
+
if (data->enable_4GB && data->m4u_plat != M4U_MT8173) {
/*
* If 4GB mode is enabled, the validate PA range is from
@@ -695,6 +700,7 @@ static int __maybe_unused mtk_iommu_suspend(struct device *dev)
reg->ctrl_reg = readl_relaxed(base + REG_MMU_CTRL_REG);
reg->int_control0 = readl_relaxed(base + REG_MMU_INT_CONTROL0);
reg->int_main_control = readl_relaxed(base + REG_MMU_INT_MAIN_CONTROL);
+ reg->ivrp_paddr = readl_relaxed(base + REG_MMU_IVRP_PADDR);
clk_disable_unprepare(data->bclk);
return 0;
}
@@ -717,8 +723,7 @@ static int __maybe_unused mtk_iommu_resume(struct device *dev)
writel_relaxed(reg->ctrl_reg, base + REG_MMU_CTRL_REG);
writel_relaxed(reg->int_control0, base + REG_MMU_INT_CONTROL0);
writel_relaxed(reg->int_main_control, base + REG_MMU_INT_MAIN_CONTROL);
- writel_relaxed(F_MMU_IVRP_PA_SET(data->protect_base, data->enable_4GB),
- base + REG_MMU_IVRP_PADDR);
+ writel_relaxed(reg->ivrp_paddr, base + REG_MMU_IVRP_PADDR);
if (data->m4u_dom)
writel(data->m4u_dom->cfg.arm_v7s_cfg.ttbr[0],
base + REG_MMU_PT_BASE_ADDR);
diff --git a/drivers/iommu/mtk_iommu.h b/drivers/iommu/mtk_iommu.h
index b4451a1c7c2f..778498b8633f 100644
--- a/drivers/iommu/mtk_iommu.h
+++ b/drivers/iommu/mtk_iommu.h
@@ -32,6 +32,7 @@ struct mtk_iommu_suspend_reg {
u32 ctrl_reg;
u32 int_control0;
u32 int_main_control;
+ u32 ivrp_paddr;
};
enum mtk_iommu_plat {
diff --git a/drivers/iommu/mtk_iommu_v1.c b/drivers/iommu/mtk_iommu_v1.c
index 542930cd183d..a7c2a973784f 100644
--- a/drivers/iommu/mtk_iommu_v1.c
+++ b/drivers/iommu/mtk_iommu_v1.c
@@ -25,7 +25,6 @@
#include <linux/io.h>
#include <linux/iommu.h>
#include <linux/iopoll.h>
-#include <linux/kmemleak.h>
#include <linux/list.h>
#include <linux/of_address.h>
#include <linux/of_iommu.h>
@@ -418,20 +417,12 @@ static int mtk_iommu_create_mapping(struct device *dev,
m4udev->archdata.iommu = mtk_mapping;
}
- ret = arm_iommu_attach_device(dev, mtk_mapping);
- if (ret)
- goto err_release_mapping;
-
return 0;
-
-err_release_mapping:
- arm_iommu_release_mapping(mtk_mapping);
- m4udev->archdata.iommu = NULL;
- return ret;
}
static int mtk_iommu_add_device(struct device *dev)
{
+ struct dma_iommu_mapping *mtk_mapping;
struct of_phandle_args iommu_spec;
struct of_phandle_iterator it;
struct mtk_iommu_data *data;
@@ -452,15 +443,30 @@ static int mtk_iommu_add_device(struct device *dev)
if (!dev->iommu_fwspec || dev->iommu_fwspec->ops != &mtk_iommu_ops)
return -ENODEV; /* Not a iommu client device */
- data = dev->iommu_fwspec->iommu_priv;
- iommu_device_link(&data->iommu, dev);
-
- group = iommu_group_get_for_dev(dev);
+ /*
+ * This is a short-term bodge because the ARM DMA code doesn't
+ * understand multi-device groups, but we have to call into it
+ * successfully (and not just rely on a normal IOMMU API attach
+ * here) in order to set the correct DMA API ops on @dev.
+ */
+ group = iommu_group_alloc();
if (IS_ERR(group))
return PTR_ERR(group);
+ err = iommu_group_add_device(group, dev);
iommu_group_put(group);
- return 0;
+ if (err)
+ return err;
+
+ data = dev->iommu_fwspec->iommu_priv;
+ mtk_mapping = data->dev->archdata.iommu;
+ err = arm_iommu_attach_device(dev, mtk_mapping);
+ if (err) {
+ iommu_group_remove_device(dev);
+ return err;
+ }
+
+ return iommu_device_link(&data->iommu, dev);;
}
static void mtk_iommu_remove_device(struct device *dev)
@@ -477,24 +483,6 @@ static void mtk_iommu_remove_device(struct device *dev)
iommu_fwspec_free(dev);
}
-static struct iommu_group *mtk_iommu_device_group(struct device *dev)
-{
- struct mtk_iommu_data *data = dev->iommu_fwspec->iommu_priv;
-
- if (!data)
- return ERR_PTR(-ENODEV);
-
- /* All the client devices are in the same m4u iommu-group */
- if (!data->m4u_group) {
- data->m4u_group = iommu_group_alloc();
- if (IS_ERR(data->m4u_group))
- dev_err(dev, "Failed to allocate M4U IOMMU group\n");
- } else {
- iommu_group_ref_get(data->m4u_group);
- }
- return data->m4u_group;
-}
-
static int mtk_iommu_hw_init(const struct mtk_iommu_data *data)
{
u32 regval;
@@ -547,7 +535,6 @@ static struct iommu_ops mtk_iommu_ops = {
.iova_to_phys = mtk_iommu_iova_to_phys,
.add_device = mtk_iommu_add_device,
.remove_device = mtk_iommu_remove_device,
- .device_group = mtk_iommu_device_group,
.pgsize_bitmap = ~0UL << MT2701_IOMMU_PAGE_SHIFT,
};
diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c
index e135ab830ebf..c33b7b104e72 100644
--- a/drivers/iommu/omap-iommu.c
+++ b/drivers/iommu/omap-iommu.c
@@ -1536,7 +1536,7 @@ static struct iommu_group *omap_iommu_device_group(struct device *dev)
struct iommu_group *group = ERR_PTR(-EINVAL);
if (arch_data->iommu_dev)
- group = arch_data->iommu_dev->group;
+ group = iommu_group_ref_get(arch_data->iommu_dev->group);
return group;
}
diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
index 9d991c2d8767..5fc8656c60f9 100644
--- a/drivers/iommu/rockchip-iommu.c
+++ b/drivers/iommu/rockchip-iommu.c
@@ -4,6 +4,7 @@
* published by the Free Software Foundation.
*/
+#include <linux/clk.h>
#include <linux/compiler.h>
#include <linux/delay.h>
#include <linux/device.h>
@@ -13,13 +14,15 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iommu.h>
-#include <linux/jiffies.h>
+#include <linux/iopoll.h>
#include <linux/list.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_iommu.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
@@ -36,7 +39,10 @@
#define RK_MMU_AUTO_GATING 0x24
#define DTE_ADDR_DUMMY 0xCAFEBABE
-#define FORCE_RESET_TIMEOUT 100 /* ms */
+
+#define RK_MMU_POLL_PERIOD_US 100
+#define RK_MMU_FORCE_RESET_TIMEOUT_US 100000
+#define RK_MMU_POLL_TIMEOUT_US 1000
/* RK_MMU_STATUS fields */
#define RK_MMU_STATUS_PAGING_ENABLED BIT(0)
@@ -73,11 +79,8 @@
*/
#define RK_IOMMU_PGSIZE_BITMAP 0x007ff000
-#define IOMMU_REG_POLL_COUNT_FAST 1000
-
struct rk_iommu_domain {
struct list_head iommus;
- struct platform_device *pdev;
u32 *dt; /* page directory table */
dma_addr_t dt_dma;
spinlock_t iommus_lock; /* lock for iommus list */
@@ -86,24 +89,37 @@ struct rk_iommu_domain {
struct iommu_domain domain;
};
+/* list of clocks required by IOMMU */
+static const char * const rk_iommu_clocks[] = {
+ "aclk", "iface",
+};
+
struct rk_iommu {
struct device *dev;
void __iomem **bases;
int num_mmu;
- int *irq;
- int num_irq;
+ struct clk_bulk_data *clocks;
+ int num_clocks;
bool reset_disabled;
struct iommu_device iommu;
struct list_head node; /* entry in rk_iommu_domain.iommus */
struct iommu_domain *domain; /* domain to which iommu is attached */
+ struct iommu_group *group;
+};
+
+struct rk_iommudata {
+ struct device_link *link; /* runtime PM link from IOMMU to master */
+ struct rk_iommu *iommu;
};
+static struct device *dma_dev;
+
static inline void rk_table_flush(struct rk_iommu_domain *dom, dma_addr_t dma,
unsigned int count)
{
size_t size = count * sizeof(u32); /* count of u32 entry */
- dma_sync_single_for_device(&dom->pdev->dev, dma, size, DMA_TO_DEVICE);
+ dma_sync_single_for_device(dma_dev, dma, size, DMA_TO_DEVICE);
}
static struct rk_iommu_domain *to_rk_domain(struct iommu_domain *dom)
@@ -111,27 +127,6 @@ static struct rk_iommu_domain *to_rk_domain(struct iommu_domain *dom)
return container_of(dom, struct rk_iommu_domain, domain);
}
-/**
- * Inspired by _wait_for in intel_drv.h
- * This is NOT safe for use in interrupt context.
- *
- * Note that it's important that we check the condition again after having
- * timed out, since the timeout could be due to preemption or similar and
- * we've never had a chance to check the condition before the timeout.
- */
-#define rk_wait_for(COND, MS) ({ \
- unsigned long timeout__ = jiffies + msecs_to_jiffies(MS) + 1; \
- int ret__ = 0; \
- while (!(COND)) { \
- if (time_after(jiffies, timeout__)) { \
- ret__ = (COND) ? 0 : -ETIMEDOUT; \
- break; \
- } \
- usleep_range(50, 100); \
- } \
- ret__; \
-})
-
/*
* The Rockchip rk3288 iommu uses a 2-level page table.
* The first level is the "Directory Table" (DT).
@@ -296,19 +291,21 @@ static void rk_iommu_base_command(void __iomem *base, u32 command)
{
writel(command, base + RK_MMU_COMMAND);
}
-static void rk_iommu_zap_lines(struct rk_iommu *iommu, dma_addr_t iova,
+static void rk_iommu_zap_lines(struct rk_iommu *iommu, dma_addr_t iova_start,
size_t size)
{
int i;
-
- dma_addr_t iova_end = iova + size;
+ dma_addr_t iova_end = iova_start + size;
/*
* TODO(djkurtz): Figure out when it is more efficient to shootdown the
* entire iotlb rather than iterate over individual iovas.
*/
- for (i = 0; i < iommu->num_mmu; i++)
- for (; iova < iova_end; iova += SPAGE_SIZE)
+ for (i = 0; i < iommu->num_mmu; i++) {
+ dma_addr_t iova;
+
+ for (iova = iova_start; iova < iova_end; iova += SPAGE_SIZE)
rk_iommu_write(iommu->bases[i], RK_MMU_ZAP_ONE_LINE, iova);
+ }
}
static bool rk_iommu_is_stall_active(struct rk_iommu *iommu)
@@ -335,9 +332,21 @@ static bool rk_iommu_is_paging_enabled(struct rk_iommu *iommu)
return enable;
}
+static bool rk_iommu_is_reset_done(struct rk_iommu *iommu)
+{
+ bool done = true;
+ int i;
+
+ for (i = 0; i < iommu->num_mmu; i++)
+ done &= rk_iommu_read(iommu->bases[i], RK_MMU_DTE_ADDR) == 0;
+
+ return done;
+}
+
static int rk_iommu_enable_stall(struct rk_iommu *iommu)
{
int ret, i;
+ bool val;
if (rk_iommu_is_stall_active(iommu))
return 0;
@@ -348,7 +357,9 @@ static int rk_iommu_enable_stall(struct rk_iommu *iommu)
rk_iommu_command(iommu, RK_MMU_CMD_ENABLE_STALL);
- ret = rk_wait_for(rk_iommu_is_stall_active(iommu), 1);
+ ret = readx_poll_timeout(rk_iommu_is_stall_active, iommu, val,
+ val, RK_MMU_POLL_PERIOD_US,
+ RK_MMU_POLL_TIMEOUT_US);
if (ret)
for (i = 0; i < iommu->num_mmu; i++)
dev_err(iommu->dev, "Enable stall request timed out, status: %#08x\n",
@@ -360,13 +371,16 @@ static int rk_iommu_enable_stall(struct rk_iommu *iommu)
static int rk_iommu_disable_stall(struct rk_iommu *iommu)
{
int ret, i;
+ bool val;
if (!rk_iommu_is_stall_active(iommu))
return 0;
rk_iommu_command(iommu, RK_MMU_CMD_DISABLE_STALL);
- ret = rk_wait_for(!rk_iommu_is_stall_active(iommu), 1);
+ ret = readx_poll_timeout(rk_iommu_is_stall_active, iommu, val,
+ !val, RK_MMU_POLL_PERIOD_US,
+ RK_MMU_POLL_TIMEOUT_US);
if (ret)
for (i = 0; i < iommu->num_mmu; i++)
dev_err(iommu->dev, "Disable stall request timed out, status: %#08x\n",
@@ -378,13 +392,16 @@ static int rk_iommu_disable_stall(struct rk_iommu *iommu)
static int rk_iommu_enable_paging(struct rk_iommu *iommu)
{
int ret, i;
+ bool val;
if (rk_iommu_is_paging_enabled(iommu))
return 0;
rk_iommu_command(iommu, RK_MMU_CMD_ENABLE_PAGING);
- ret = rk_wait_for(rk_iommu_is_paging_enabled(iommu), 1);
+ ret = readx_poll_timeout(rk_iommu_is_paging_enabled, iommu, val,
+ val, RK_MMU_POLL_PERIOD_US,
+ RK_MMU_POLL_TIMEOUT_US);
if (ret)
for (i = 0; i < iommu->num_mmu; i++)
dev_err(iommu->dev, "Enable paging request timed out, status: %#08x\n",
@@ -396,13 +413,16 @@ static int rk_iommu_enable_paging(struct rk_iommu *iommu)
static int rk_iommu_disable_paging(struct rk_iommu *iommu)
{
int ret, i;
+ bool val;
if (!rk_iommu_is_paging_enabled(iommu))
return 0;
rk_iommu_command(iommu, RK_MMU_CMD_DISABLE_PAGING);
- ret = rk_wait_for(!rk_iommu_is_paging_enabled(iommu), 1);
+ ret = readx_poll_timeout(rk_iommu_is_paging_enabled, iommu, val,
+ !val, RK_MMU_POLL_PERIOD_US,
+ RK_MMU_POLL_TIMEOUT_US);
if (ret)
for (i = 0; i < iommu->num_mmu; i++)
dev_err(iommu->dev, "Disable paging request timed out, status: %#08x\n",
@@ -415,6 +435,7 @@ static int rk_iommu_force_reset(struct rk_iommu *iommu)
{
int ret, i;
u32 dte_addr;
+ bool val;
if (iommu->reset_disabled)
return 0;
@@ -435,13 +456,12 @@ static int rk_iommu_force_reset(struct rk_iommu *iommu)
rk_iommu_command(iommu, RK_MMU_CMD_FORCE_RESET);
- for (i = 0; i < iommu->num_mmu; i++) {
- ret = rk_wait_for(rk_iommu_read(iommu->bases[i], RK_MMU_DTE_ADDR) == 0x00000000,
- FORCE_RESET_TIMEOUT);
- if (ret) {
- dev_err(iommu->dev, "FORCE_RESET command timed out\n");
- return ret;
- }
+ ret = readx_poll_timeout(rk_iommu_is_reset_done, iommu, val,
+ val, RK_MMU_FORCE_RESET_TIMEOUT_US,
+ RK_MMU_POLL_TIMEOUT_US);
+ if (ret) {
+ dev_err(iommu->dev, "FORCE_RESET command timed out\n");
+ return ret;
}
return 0;
@@ -503,6 +523,12 @@ static irqreturn_t rk_iommu_irq(int irq, void *dev_id)
irqreturn_t ret = IRQ_NONE;
int i;
+ if (WARN_ON(!pm_runtime_get_if_in_use(iommu->dev)))
+ return 0;
+
+ if (WARN_ON(clk_bulk_enable(iommu->num_clocks, iommu->clocks)))
+ goto out;
+
for (i = 0; i < iommu->num_mmu; i++) {
int_status = rk_iommu_read(iommu->bases[i], RK_MMU_INT_STATUS);
if (int_status == 0)
@@ -549,6 +575,10 @@ static irqreturn_t rk_iommu_irq(int irq, void *dev_id)
rk_iommu_write(iommu->bases[i], RK_MMU_INT_CLEAR, int_status);
}
+ clk_bulk_disable(iommu->num_clocks, iommu->clocks);
+
+out:
+ pm_runtime_put(iommu->dev);
return ret;
}
@@ -590,8 +620,17 @@ static void rk_iommu_zap_iova(struct rk_iommu_domain *rk_domain,
spin_lock_irqsave(&rk_domain->iommus_lock, flags);
list_for_each(pos, &rk_domain->iommus) {
struct rk_iommu *iommu;
+
iommu = list_entry(pos, struct rk_iommu, node);
- rk_iommu_zap_lines(iommu, iova, size);
+
+ /* Only zap TLBs of IOMMUs that are powered on. */
+ if (pm_runtime_get_if_in_use(iommu->dev)) {
+ WARN_ON(clk_bulk_enable(iommu->num_clocks,
+ iommu->clocks));
+ rk_iommu_zap_lines(iommu, iova, size);
+ clk_bulk_disable(iommu->num_clocks, iommu->clocks);
+ pm_runtime_put(iommu->dev);
+ }
}
spin_unlock_irqrestore(&rk_domain->iommus_lock, flags);
}
@@ -608,7 +647,6 @@ static void rk_iommu_zap_iova_first_last(struct rk_iommu_domain *rk_domain,
static u32 *rk_dte_get_page_table(struct rk_iommu_domain *rk_domain,
dma_addr_t iova)
{
- struct device *dev = &rk_domain->pdev->dev;
u32 *page_table, *dte_addr;
u32 dte_index, dte;
phys_addr_t pt_phys;
@@ -626,9 +664,9 @@ static u32 *rk_dte_get_page_table(struct rk_iommu_domain *rk_domain,
if (!page_table)
return ERR_PTR(-ENOMEM);
- pt_dma = dma_map_single(dev, page_table, SPAGE_SIZE, DMA_TO_DEVICE);
- if (dma_mapping_error(dev, pt_dma)) {
- dev_err(dev, "DMA mapping error while allocating page table\n");
+ pt_dma = dma_map_single(dma_dev, page_table, SPAGE_SIZE, DMA_TO_DEVICE);
+ if (dma_mapping_error(dma_dev, pt_dma)) {
+ dev_err(dma_dev, "DMA mapping error while allocating page table\n");
free_page((unsigned long)page_table);
return ERR_PTR(-ENOMEM);
}
@@ -790,52 +828,46 @@ static size_t rk_iommu_unmap(struct iommu_domain *domain, unsigned long _iova,
static struct rk_iommu *rk_iommu_from_dev(struct device *dev)
{
- struct iommu_group *group;
- struct device *iommu_dev;
- struct rk_iommu *rk_iommu;
+ struct rk_iommudata *data = dev->archdata.iommu;
- group = iommu_group_get(dev);
- if (!group)
- return NULL;
- iommu_dev = iommu_group_get_iommudata(group);
- rk_iommu = dev_get_drvdata(iommu_dev);
- iommu_group_put(group);
+ return data ? data->iommu : NULL;
+}
+
+/* Must be called with iommu powered on and attached */
+static void rk_iommu_disable(struct rk_iommu *iommu)
+{
+ int i;
- return rk_iommu;
+ /* Ignore error while disabling, just keep going */
+ WARN_ON(clk_bulk_enable(iommu->num_clocks, iommu->clocks));
+ rk_iommu_enable_stall(iommu);
+ rk_iommu_disable_paging(iommu);
+ for (i = 0; i < iommu->num_mmu; i++) {
+ rk_iommu_write(iommu->bases[i], RK_MMU_INT_MASK, 0);
+ rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, 0);
+ }
+ rk_iommu_disable_stall(iommu);
+ clk_bulk_disable(iommu->num_clocks, iommu->clocks);
}
-static int rk_iommu_attach_device(struct iommu_domain *domain,
- struct device *dev)
+/* Must be called with iommu powered on and attached */
+static int rk_iommu_enable(struct rk_iommu *iommu)
{
- struct rk_iommu *iommu;
+ struct iommu_domain *domain = iommu->domain;
struct rk_iommu_domain *rk_domain = to_rk_domain(domain);
- unsigned long flags;
int ret, i;
- /*
- * Allow 'virtual devices' (e.g., drm) to attach to domain.
- * Such a device does not belong to an iommu group.
- */
- iommu = rk_iommu_from_dev(dev);
- if (!iommu)
- return 0;
+ ret = clk_bulk_enable(iommu->num_clocks, iommu->clocks);
+ if (ret)
+ return ret;
ret = rk_iommu_enable_stall(iommu);
if (ret)
- return ret;
+ goto out_disable_clocks;
ret = rk_iommu_force_reset(iommu);
if (ret)
- return ret;
-
- iommu->domain = domain;
-
- for (i = 0; i < iommu->num_irq; i++) {
- ret = devm_request_irq(iommu->dev, iommu->irq[i], rk_iommu_irq,
- IRQF_SHARED, dev_name(dev), iommu);
- if (ret)
- return ret;
- }
+ goto out_disable_stall;
for (i = 0; i < iommu->num_mmu; i++) {
rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR,
@@ -845,18 +877,12 @@ static int rk_iommu_attach_device(struct iommu_domain *domain,
}
ret = rk_iommu_enable_paging(iommu);
- if (ret)
- return ret;
-
- spin_lock_irqsave(&rk_domain->iommus_lock, flags);
- list_add_tail(&iommu->node, &rk_domain->iommus);
- spin_unlock_irqrestore(&rk_domain->iommus_lock, flags);
-
- dev_dbg(dev, "Attached to iommu domain\n");
+out_disable_stall:
rk_iommu_disable_stall(iommu);
-
- return 0;
+out_disable_clocks:
+ clk_bulk_disable(iommu->num_clocks, iommu->clocks);
+ return ret;
}
static void rk_iommu_detach_device(struct iommu_domain *domain,
@@ -865,60 +891,90 @@ static void rk_iommu_detach_device(struct iommu_domain *domain,
struct rk_iommu *iommu;
struct rk_iommu_domain *rk_domain = to_rk_domain(domain);
unsigned long flags;
- int i;
/* Allow 'virtual devices' (eg drm) to detach from domain */
iommu = rk_iommu_from_dev(dev);
if (!iommu)
return;
+ dev_dbg(dev, "Detaching from iommu domain\n");
+
+ /* iommu already detached */
+ if (iommu->domain != domain)
+ return;
+
+ iommu->domain = NULL;
+
spin_lock_irqsave(&rk_domain->iommus_lock, flags);
list_del_init(&iommu->node);
spin_unlock_irqrestore(&rk_domain->iommus_lock, flags);
- /* Ignore error while disabling, just keep going */
- rk_iommu_enable_stall(iommu);
- rk_iommu_disable_paging(iommu);
- for (i = 0; i < iommu->num_mmu; i++) {
- rk_iommu_write(iommu->bases[i], RK_MMU_INT_MASK, 0);
- rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, 0);
+ if (pm_runtime_get_if_in_use(iommu->dev)) {
+ rk_iommu_disable(iommu);
+ pm_runtime_put(iommu->dev);
}
- rk_iommu_disable_stall(iommu);
+}
- for (i = 0; i < iommu->num_irq; i++)
- devm_free_irq(iommu->dev, iommu->irq[i], iommu);
+static int rk_iommu_attach_device(struct iommu_domain *domain,
+ struct device *dev)
+{
+ struct rk_iommu *iommu;
+ struct rk_iommu_domain *rk_domain = to_rk_domain(domain);
+ unsigned long flags;
+ int ret;
- iommu->domain = NULL;
+ /*
+ * Allow 'virtual devices' (e.g., drm) to attach to domain.
+ * Such a device does not belong to an iommu group.
+ */
+ iommu = rk_iommu_from_dev(dev);
+ if (!iommu)
+ return 0;
+
+ dev_dbg(dev, "Attaching to iommu domain\n");
+
+ /* iommu already attached */
+ if (iommu->domain == domain)
+ return 0;
- dev_dbg(dev, "Detached from iommu domain\n");
+ if (iommu->domain)
+ rk_iommu_detach_device(iommu->domain, dev);
+
+ iommu->domain = domain;
+
+ spin_lock_irqsave(&rk_domain->iommus_lock, flags);
+ list_add_tail(&iommu->node, &rk_domain->iommus);
+ spin_unlock_irqrestore(&rk_domain->iommus_lock, flags);
+
+ if (!pm_runtime_get_if_in_use(iommu->dev))
+ return 0;
+
+ ret = rk_iommu_enable(iommu);
+ if (ret)
+ rk_iommu_detach_device(iommu->domain, dev);
+
+ pm_runtime_put(iommu->dev);
+
+ return ret;
}
static struct iommu_domain *rk_iommu_domain_alloc(unsigned type)
{
struct rk_iommu_domain *rk_domain;
- struct platform_device *pdev;
- struct device *iommu_dev;
if (type != IOMMU_DOMAIN_UNMANAGED && type != IOMMU_DOMAIN_DMA)
return NULL;
- /* Register a pdev per domain, so DMA API can base on this *dev
- * even some virtual master doesn't have an iommu slave
- */
- pdev = platform_device_register_simple("rk_iommu_domain",
- PLATFORM_DEVID_AUTO, NULL, 0);
- if (IS_ERR(pdev))
+ if (!dma_dev)
return NULL;
- rk_domain = devm_kzalloc(&pdev->dev, sizeof(*rk_domain), GFP_KERNEL);
+ rk_domain = devm_kzalloc(dma_dev, sizeof(*rk_domain), GFP_KERNEL);
if (!rk_domain)
- goto err_unreg_pdev;
-
- rk_domain->pdev = pdev;
+ return NULL;
if (type == IOMMU_DOMAIN_DMA &&
iommu_get_dma_cookie(&rk_domain->domain))
- goto err_unreg_pdev;
+ return NULL;
/*
* rk32xx iommus use a 2 level pagetable.
@@ -929,11 +985,10 @@ static struct iommu_domain *rk_iommu_domain_alloc(unsigned type)
if (!rk_domain->dt)
goto err_put_cookie;
- iommu_dev = &pdev->dev;
- rk_domain->dt_dma = dma_map_single(iommu_dev, rk_domain->dt,
+ rk_domain->dt_dma = dma_map_single(dma_dev, rk_domain->dt,
SPAGE_SIZE, DMA_TO_DEVICE);
- if (dma_mapping_error(iommu_dev, rk_domain->dt_dma)) {
- dev_err(iommu_dev, "DMA map error for DT\n");
+ if (dma_mapping_error(dma_dev, rk_domain->dt_dma)) {
+ dev_err(dma_dev, "DMA map error for DT\n");
goto err_free_dt;
}
@@ -954,8 +1009,6 @@ err_free_dt:
err_put_cookie:
if (type == IOMMU_DOMAIN_DMA)
iommu_put_dma_cookie(&rk_domain->domain);
-err_unreg_pdev:
- platform_device_unregister(pdev);
return NULL;
}
@@ -972,126 +1025,82 @@ static void rk_iommu_domain_free(struct iommu_domain *domain)
if (rk_dte_is_pt_valid(dte)) {
phys_addr_t pt_phys = rk_dte_pt_address(dte);
u32 *page_table = phys_to_virt(pt_phys);
- dma_unmap_single(&rk_domain->pdev->dev, pt_phys,
+ dma_unmap_single(dma_dev, pt_phys,
SPAGE_SIZE, DMA_TO_DEVICE);
free_page((unsigned long)page_table);
}
}
- dma_unmap_single(&rk_domain->pdev->dev, rk_domain->dt_dma,
+ dma_unmap_single(dma_dev, rk_domain->dt_dma,
SPAGE_SIZE, DMA_TO_DEVICE);
free_page((unsigned long)rk_domain->dt);
if (domain->type == IOMMU_DOMAIN_DMA)
iommu_put_dma_cookie(&rk_domain->domain);
-
- platform_device_unregister(rk_domain->pdev);
}
-static bool rk_iommu_is_dev_iommu_master(struct device *dev)
+static int rk_iommu_add_device(struct device *dev)
{
- struct device_node *np = dev->of_node;
- int ret;
-
- /*
- * An iommu master has an iommus property containing a list of phandles
- * to iommu nodes, each with an #iommu-cells property with value 0.
- */
- ret = of_count_phandle_with_args(np, "iommus", "#iommu-cells");
- return (ret > 0);
-}
+ struct iommu_group *group;
+ struct rk_iommu *iommu;
+ struct rk_iommudata *data;
-static int rk_iommu_group_set_iommudata(struct iommu_group *group,
- struct device *dev)
-{
- struct device_node *np = dev->of_node;
- struct platform_device *pd;
- int ret;
- struct of_phandle_args args;
+ data = dev->archdata.iommu;
+ if (!data)
+ return -ENODEV;
- /*
- * An iommu master has an iommus property containing a list of phandles
- * to iommu nodes, each with an #iommu-cells property with value 0.
- */
- ret = of_parse_phandle_with_args(np, "iommus", "#iommu-cells", 0,
- &args);
- if (ret) {
- dev_err(dev, "of_parse_phandle_with_args(%pOF) => %d\n",
- np, ret);
- return ret;
- }
- if (args.args_count != 0) {
- dev_err(dev, "incorrect number of iommu params found for %pOF (found %d, expected 0)\n",
- args.np, args.args_count);
- return -EINVAL;
- }
+ iommu = rk_iommu_from_dev(dev);
- pd = of_find_device_by_node(args.np);
- of_node_put(args.np);
- if (!pd) {
- dev_err(dev, "iommu %pOF not found\n", args.np);
- return -EPROBE_DEFER;
- }
+ group = iommu_group_get_for_dev(dev);
+ if (IS_ERR(group))
+ return PTR_ERR(group);
+ iommu_group_put(group);
- /* TODO(djkurtz): handle multiple slave iommus for a single master */
- iommu_group_set_iommudata(group, &pd->dev, NULL);
+ iommu_device_link(&iommu->iommu, dev);
+ data->link = device_link_add(dev, iommu->dev, DL_FLAG_PM_RUNTIME);
return 0;
}
-static int rk_iommu_add_device(struct device *dev)
+static void rk_iommu_remove_device(struct device *dev)
{
- struct iommu_group *group;
struct rk_iommu *iommu;
- int ret;
-
- if (!rk_iommu_is_dev_iommu_master(dev))
- return -ENODEV;
+ struct rk_iommudata *data = dev->archdata.iommu;
- group = iommu_group_get(dev);
- if (!group) {
- group = iommu_group_alloc();
- if (IS_ERR(group)) {
- dev_err(dev, "Failed to allocate IOMMU group\n");
- return PTR_ERR(group);
- }
- }
+ iommu = rk_iommu_from_dev(dev);
- ret = iommu_group_add_device(group, dev);
- if (ret)
- goto err_put_group;
+ device_link_del(data->link);
+ iommu_device_unlink(&iommu->iommu, dev);
+ iommu_group_remove_device(dev);
+}
- ret = rk_iommu_group_set_iommudata(group, dev);
- if (ret)
- goto err_remove_device;
+static struct iommu_group *rk_iommu_device_group(struct device *dev)
+{
+ struct rk_iommu *iommu;
iommu = rk_iommu_from_dev(dev);
- if (iommu)
- iommu_device_link(&iommu->iommu, dev);
-
- iommu_group_put(group);
-
- return 0;
-err_remove_device:
- iommu_group_remove_device(dev);
-err_put_group:
- iommu_group_put(group);
- return ret;
+ return iommu_group_ref_get(iommu->group);
}
-static void rk_iommu_remove_device(struct device *dev)
+static int rk_iommu_of_xlate(struct device *dev,
+ struct of_phandle_args *args)
{
- struct rk_iommu *iommu;
+ struct platform_device *iommu_dev;
+ struct rk_iommudata *data;
- if (!rk_iommu_is_dev_iommu_master(dev))
- return;
+ data = devm_kzalloc(dma_dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
- iommu = rk_iommu_from_dev(dev);
- if (iommu)
- iommu_device_unlink(&iommu->iommu, dev);
+ iommu_dev = of_find_device_by_node(args->np);
- iommu_group_remove_device(dev);
+ data->iommu = platform_get_drvdata(iommu_dev);
+ dev->archdata.iommu = data;
+
+ of_dev_put(iommu_dev);
+
+ return 0;
}
static const struct iommu_ops rk_iommu_ops = {
@@ -1105,31 +1114,9 @@ static const struct iommu_ops rk_iommu_ops = {
.add_device = rk_iommu_add_device,
.remove_device = rk_iommu_remove_device,
.iova_to_phys = rk_iommu_iova_to_phys,
+ .device_group = rk_iommu_device_group,
.pgsize_bitmap = RK_IOMMU_PGSIZE_BITMAP,
-};
-
-static int rk_iommu_domain_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
-
- dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms), GFP_KERNEL);
- if (!dev->dma_parms)
- return -ENOMEM;
-
- /* Set dma_ops for dev, otherwise it would be dummy_dma_ops */
- arch_setup_dma_ops(dev, 0, DMA_BIT_MASK(32), NULL, false);
-
- dma_set_max_seg_size(dev, DMA_BIT_MASK(32));
- dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
-
- return 0;
-}
-
-static struct platform_driver rk_iommu_domain_driver = {
- .probe = rk_iommu_domain_probe,
- .driver = {
- .name = "rk_iommu_domain",
- },
+ .of_xlate = rk_iommu_of_xlate,
};
static int rk_iommu_probe(struct platform_device *pdev)
@@ -1138,7 +1125,7 @@ static int rk_iommu_probe(struct platform_device *pdev)
struct rk_iommu *iommu;
struct resource *res;
int num_res = pdev->num_resources;
- int err, i;
+ int err, i, irq;
iommu = devm_kzalloc(dev, sizeof(*iommu), GFP_KERNEL);
if (!iommu)
@@ -1165,50 +1152,108 @@ static int rk_iommu_probe(struct platform_device *pdev)
if (iommu->num_mmu == 0)
return PTR_ERR(iommu->bases[0]);
- iommu->num_irq = platform_irq_count(pdev);
- if (iommu->num_irq < 0)
- return iommu->num_irq;
- if (iommu->num_irq == 0)
- return -ENXIO;
-
- iommu->irq = devm_kcalloc(dev, iommu->num_irq, sizeof(*iommu->irq),
- GFP_KERNEL);
- if (!iommu->irq)
- return -ENOMEM;
+ i = 0;
+ while ((irq = platform_get_irq(pdev, i++)) != -ENXIO) {
+ if (irq < 0)
+ return irq;
- for (i = 0; i < iommu->num_irq; i++) {
- iommu->irq[i] = platform_get_irq(pdev, i);
- if (iommu->irq[i] < 0) {
- dev_err(dev, "Failed to get IRQ, %d\n", iommu->irq[i]);
- return -ENXIO;
- }
+ err = devm_request_irq(iommu->dev, irq, rk_iommu_irq,
+ IRQF_SHARED, dev_name(dev), iommu);
+ if (err)
+ return err;
}
iommu->reset_disabled = device_property_read_bool(dev,
"rockchip,disable-mmu-reset");
- err = iommu_device_sysfs_add(&iommu->iommu, dev, NULL, dev_name(dev));
+ iommu->num_clocks = ARRAY_SIZE(rk_iommu_clocks);
+ iommu->clocks = devm_kcalloc(iommu->dev, iommu->num_clocks,
+ sizeof(*iommu->clocks), GFP_KERNEL);
+ if (!iommu->clocks)
+ return -ENOMEM;
+
+ for (i = 0; i < iommu->num_clocks; ++i)
+ iommu->clocks[i].id = rk_iommu_clocks[i];
+
+ err = devm_clk_bulk_get(iommu->dev, iommu->num_clocks, iommu->clocks);
+ if (err)
+ return err;
+
+ err = clk_bulk_prepare(iommu->num_clocks, iommu->clocks);
if (err)
return err;
+ iommu->group = iommu_group_alloc();
+ if (IS_ERR(iommu->group)) {
+ err = PTR_ERR(iommu->group);
+ goto err_unprepare_clocks;
+ }
+
+ err = iommu_device_sysfs_add(&iommu->iommu, dev, NULL, dev_name(dev));
+ if (err)
+ goto err_put_group;
+
iommu_device_set_ops(&iommu->iommu, &rk_iommu_ops);
+ iommu_device_set_fwnode(&iommu->iommu, &dev->of_node->fwnode);
+
err = iommu_device_register(&iommu->iommu);
+ if (err)
+ goto err_remove_sysfs;
+
+ /*
+ * Use the first registered IOMMU device for domain to use with DMA
+ * API, since a domain might not physically correspond to a single
+ * IOMMU device..
+ */
+ if (!dma_dev)
+ dma_dev = &pdev->dev;
+
+ bus_set_iommu(&platform_bus_type, &rk_iommu_ops);
+ pm_runtime_enable(dev);
+
+ return 0;
+err_remove_sysfs:
+ iommu_device_sysfs_remove(&iommu->iommu);
+err_put_group:
+ iommu_group_put(iommu->group);
+err_unprepare_clocks:
+ clk_bulk_unprepare(iommu->num_clocks, iommu->clocks);
return err;
}
-static int rk_iommu_remove(struct platform_device *pdev)
+static void rk_iommu_shutdown(struct platform_device *pdev)
{
- struct rk_iommu *iommu = platform_get_drvdata(pdev);
+ pm_runtime_force_suspend(&pdev->dev);
+}
- if (iommu) {
- iommu_device_sysfs_remove(&iommu->iommu);
- iommu_device_unregister(&iommu->iommu);
- }
+static int __maybe_unused rk_iommu_suspend(struct device *dev)
+{
+ struct rk_iommu *iommu = dev_get_drvdata(dev);
+ if (!iommu->domain)
+ return 0;
+
+ rk_iommu_disable(iommu);
return 0;
}
+static int __maybe_unused rk_iommu_resume(struct device *dev)
+{
+ struct rk_iommu *iommu = dev_get_drvdata(dev);
+
+ if (!iommu->domain)
+ return 0;
+
+ return rk_iommu_enable(iommu);
+}
+
+static const struct dev_pm_ops rk_iommu_pm_ops = {
+ SET_RUNTIME_PM_OPS(rk_iommu_suspend, rk_iommu_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+};
+
static const struct of_device_id rk_iommu_dt_ids[] = {
{ .compatible = "rockchip,iommu" },
{ /* sentinel */ }
@@ -1217,45 +1262,22 @@ MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids);
static struct platform_driver rk_iommu_driver = {
.probe = rk_iommu_probe,
- .remove = rk_iommu_remove,
+ .shutdown = rk_iommu_shutdown,
.driver = {
.name = "rk_iommu",
.of_match_table = rk_iommu_dt_ids,
+ .pm = &rk_iommu_pm_ops,
+ .suppress_bind_attrs = true,
},
};
static int __init rk_iommu_init(void)
{
- struct device_node *np;
- int ret;
-
- np = of_find_matching_node(NULL, rk_iommu_dt_ids);
- if (!np)
- return 0;
-
- of_node_put(np);
-
- ret = bus_set_iommu(&platform_bus_type, &rk_iommu_ops);
- if (ret)
- return ret;
-
- ret = platform_driver_register(&rk_iommu_domain_driver);
- if (ret)
- return ret;
-
- ret = platform_driver_register(&rk_iommu_driver);
- if (ret)
- platform_driver_unregister(&rk_iommu_domain_driver);
- return ret;
+ return platform_driver_register(&rk_iommu_driver);
}
-static void __exit rk_iommu_exit(void)
-{
- platform_driver_unregister(&rk_iommu_driver);
- platform_driver_unregister(&rk_iommu_domain_driver);
-}
-
subsys_initcall(rk_iommu_init);
-module_exit(rk_iommu_exit);
+
+IOMMU_OF_DECLARE(rk_iommu_of, "rockchip,iommu");
MODULE_DESCRIPTION("IOMMU API for Rockchip");
MODULE_AUTHOR("Simon Xue <xxm@rock-chips.com> and Daniel Kurtz <djkurtz@chromium.org>");
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index d913aec85109..e9233db16e03 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -51,6 +51,12 @@ config ARM_GIC_V3_ITS_PCI
depends on PCI_MSI
default ARM_GIC_V3_ITS
+config ARM_GIC_V3_ITS_FSL_MC
+ bool
+ depends on ARM_GIC_V3_ITS
+ depends on FSL_MC_BUS
+ default ARM_GIC_V3_ITS
+
config ARM_NVIC
bool
select IRQ_DOMAIN
@@ -286,6 +292,11 @@ config IRQ_MXS
select IRQ_DOMAIN
select STMP_DEVICE
+config MSCC_OCELOT_IRQ
+ bool
+ select IRQ_DOMAIN
+ select GENERIC_IRQ_CHIP
+
config MVEBU_GICP
bool
@@ -351,4 +362,13 @@ config GOLDFISH_PIC
Say yes here to enable Goldfish interrupt controller driver used
for Goldfish based virtual platforms.
+config QCOM_PDC
+ bool "QCOM PDC"
+ depends on ARCH_QCOM
+ select IRQ_DOMAIN
+ select IRQ_DOMAIN_HIERARCHY
+ help
+ Power Domain Controller driver to manage and configure wakeup
+ IRQs for Qualcomm Technologies Inc (QTI) mobile chips.
+
endmenu
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index d27e3e3619e0..5ed465ab1c76 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -15,8 +15,6 @@ obj-$(CONFIG_IRQ_MXS) += irq-mxs.o
obj-$(CONFIG_ARCH_TEGRA) += irq-tegra.o
obj-$(CONFIG_ARCH_S3C24XX) += irq-s3c24xx.o
obj-$(CONFIG_DW_APB_ICTL) += irq-dw-apb-ictl.o
-obj-$(CONFIG_METAG) += irq-metag-ext.o
-obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o
obj-$(CONFIG_CLPS711X_IRQCHIP) += irq-clps711x.o
obj-$(CONFIG_OMPIC) += irq-ompic.o
obj-$(CONFIG_OR1K_PIC) += irq-or1k-pic.o
@@ -32,6 +30,7 @@ obj-$(CONFIG_ARM_GIC_V2M) += irq-gic-v2m.o
obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-common.o
obj-$(CONFIG_ARM_GIC_V3_ITS) += irq-gic-v3-its.o irq-gic-v3-its-platform-msi.o irq-gic-v4.o
obj-$(CONFIG_ARM_GIC_V3_ITS_PCI) += irq-gic-v3-its-pci-msi.o
+obj-$(CONFIG_ARM_GIC_V3_ITS_FSL_MC) += irq-gic-v3-its-fsl-mc-msi.o
obj-$(CONFIG_PARTITION_PERCPU) += irq-partition-percpu.o
obj-$(CONFIG_HISILICON_IRQ_MBIGEN) += irq-mbigen.o
obj-$(CONFIG_ARM_NVIC) += irq-nvic.o
@@ -72,6 +71,7 @@ obj-$(CONFIG_ARCH_SA1100) += irq-sa11x0.o
obj-$(CONFIG_INGENIC_IRQ) += irq-ingenic.o
obj-$(CONFIG_IMX_GPCV2) += irq-imx-gpcv2.o
obj-$(CONFIG_PIC32_EVIC) += irq-pic32-evic.o
+obj-$(CONFIG_MSCC_OCELOT_IRQ) += irq-mscc-ocelot.o
obj-$(CONFIG_MVEBU_GICP) += irq-mvebu-gicp.o
obj-$(CONFIG_MVEBU_ICU) += irq-mvebu-icu.o
obj-$(CONFIG_MVEBU_ODMI) += irq-mvebu-odmi.o
@@ -85,3 +85,5 @@ obj-$(CONFIG_IRQ_UNIPHIER_AIDET) += irq-uniphier-aidet.o
obj-$(CONFIG_ARCH_SYNQUACER) += irq-sni-exiu.o
obj-$(CONFIG_MESON_IRQ_GPIO) += irq-meson-gpio.o
obj-$(CONFIG_GOLDFISH_PIC) += irq-goldfish-pic.o
+obj-$(CONFIG_NDS32) += irq-ativic32.o
+obj-$(CONFIG_QCOM_PDC) += qcom-pdc.o
diff --git a/drivers/irqchip/irq-ativic32.c b/drivers/irqchip/irq-ativic32.c
new file mode 100644
index 000000000000..f69a8588521c
--- /dev/null
+++ b/drivers/irqchip/irq-ativic32.c
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2005-2017 Andes Technology Corporation
+
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip.h>
+#include <nds32_intrinsic.h>
+
+static void ativic32_ack_irq(struct irq_data *data)
+{
+ __nds32__mtsr_dsb(BIT(data->hwirq), NDS32_SR_INT_PEND2);
+}
+
+static void ativic32_mask_irq(struct irq_data *data)
+{
+ unsigned long int_mask2 = __nds32__mfsr(NDS32_SR_INT_MASK2);
+ __nds32__mtsr_dsb(int_mask2 & (~(BIT(data->hwirq))), NDS32_SR_INT_MASK2);
+}
+
+static void ativic32_unmask_irq(struct irq_data *data)
+{
+ unsigned long int_mask2 = __nds32__mfsr(NDS32_SR_INT_MASK2);
+ __nds32__mtsr_dsb(int_mask2 | (BIT(data->hwirq)), NDS32_SR_INT_MASK2);
+}
+
+static struct irq_chip ativic32_chip = {
+ .name = "ativic32",
+ .irq_ack = ativic32_ack_irq,
+ .irq_mask = ativic32_mask_irq,
+ .irq_unmask = ativic32_unmask_irq,
+};
+
+static unsigned int __initdata nivic_map[6] = { 6, 2, 10, 16, 24, 32 };
+
+static struct irq_domain *root_domain;
+static int ativic32_irq_domain_map(struct irq_domain *id, unsigned int virq,
+ irq_hw_number_t hw)
+{
+
+ unsigned long int_trigger_type;
+ u32 type;
+ struct irq_data *irq_data;
+ int_trigger_type = __nds32__mfsr(NDS32_SR_INT_TRIGGER);
+ irq_data = irq_get_irq_data(virq);
+ if (!irq_data)
+ return -EINVAL;
+
+ if (int_trigger_type & (BIT(hw))) {
+ irq_set_chip_and_handler(virq, &ativic32_chip, handle_edge_irq);
+ type = IRQ_TYPE_EDGE_RISING;
+ } else {
+ irq_set_chip_and_handler(virq, &ativic32_chip, handle_level_irq);
+ type = IRQ_TYPE_LEVEL_HIGH;
+ }
+
+ irqd_set_trigger_type(irq_data, type);
+ return 0;
+}
+
+static struct irq_domain_ops ativic32_ops = {
+ .map = ativic32_irq_domain_map,
+ .xlate = irq_domain_xlate_onecell
+};
+
+static irq_hw_number_t get_intr_src(void)
+{
+ return ((__nds32__mfsr(NDS32_SR_ITYPE) & ITYPE_mskVECTOR) >> ITYPE_offVECTOR)
+ - NDS32_VECTOR_offINTERRUPT;
+}
+
+asmlinkage void asm_do_IRQ(struct pt_regs *regs)
+{
+ irq_hw_number_t hwirq = get_intr_src();
+ handle_domain_irq(root_domain, hwirq, regs);
+}
+
+int __init ativic32_init_irq(struct device_node *node, struct device_node *parent)
+{
+ unsigned long int_vec_base, nivic, nr_ints;
+
+ if (WARN(parent, "non-root ativic32 are not supported"))
+ return -EINVAL;
+
+ int_vec_base = __nds32__mfsr(NDS32_SR_IVB);
+
+ if (((int_vec_base & IVB_mskIVIC_VER) >> IVB_offIVIC_VER) == 0)
+ panic("Unable to use atcivic32 for this cpu.\n");
+
+ nivic = (int_vec_base & IVB_mskNIVIC) >> IVB_offNIVIC;
+ if (nivic >= ARRAY_SIZE(nivic_map))
+ panic("The number of input for ativic32 is not supported.\n");
+
+ nr_ints = nivic_map[nivic];
+
+ root_domain = irq_domain_add_linear(node, nr_ints,
+ &ativic32_ops, NULL);
+
+ if (!root_domain)
+ panic("%s: unable to create IRQ domain\n", node->full_name);
+
+ return 0;
+}
+IRQCHIP_DECLARE(ativic32, "andestech,ativic32", ativic32_init_irq);
diff --git a/drivers/irqchip/irq-gic-common.c b/drivers/irqchip/irq-gic-common.c
index 30017df5b54c..01e673c680cd 100644
--- a/drivers/irqchip/irq-gic-common.c
+++ b/drivers/irqchip/irq-gic-common.c
@@ -21,6 +21,8 @@
#include "irq-gic-common.h"
+static DEFINE_RAW_SPINLOCK(irq_controller_lock);
+
static const struct gic_kvm_info *gic_kvm_info;
const struct gic_kvm_info *gic_get_kvm_info(void)
@@ -53,11 +55,13 @@ int gic_configure_irq(unsigned int irq, unsigned int type,
u32 confoff = (irq / 16) * 4;
u32 val, oldval;
int ret = 0;
+ unsigned long flags;
/*
* Read current configuration register, and insert the config
* for "irq", depending on "type".
*/
+ raw_spin_lock_irqsave(&irq_controller_lock, flags);
val = oldval = readl_relaxed(base + GIC_DIST_CONFIG + confoff);
if (type & IRQ_TYPE_LEVEL_MASK)
val &= ~confmask;
@@ -65,8 +69,10 @@ int gic_configure_irq(unsigned int irq, unsigned int type,
val |= confmask;
/* If the current configuration is the same, then we are done */
- if (val == oldval)
+ if (val == oldval) {
+ raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
return 0;
+ }
/*
* Write back the new configuration, and possibly re-enable
@@ -84,6 +90,7 @@ int gic_configure_irq(unsigned int irq, unsigned int type,
pr_warn("GIC: PPI%d is secure or misconfigured\n",
irq - 16);
}
+ raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
if (sync_access)
sync_access();
diff --git a/drivers/irqchip/irq-gic-v3-its-fsl-mc-msi.c b/drivers/irqchip/irq-gic-v3-its-fsl-mc-msi.c
new file mode 100644
index 000000000000..4eca5c763766
--- /dev/null
+++ b/drivers/irqchip/irq-gic-v3-its-fsl-mc-msi.c
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Freescale Management Complex (MC) bus driver MSI support
+ *
+ * Copyright (C) 2015-2016 Freescale Semiconductor, Inc.
+ * Author: German Rivera <German.Rivera@freescale.com>
+ *
+ */
+
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/irq.h>
+#include <linux/msi.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/fsl/mc.h>
+
+static struct irq_chip its_msi_irq_chip = {
+ .name = "ITS-fMSI",
+ .irq_mask = irq_chip_mask_parent,
+ .irq_unmask = irq_chip_unmask_parent,
+ .irq_eoi = irq_chip_eoi_parent,
+ .irq_set_affinity = msi_domain_set_affinity
+};
+
+static int its_fsl_mc_msi_prepare(struct irq_domain *msi_domain,
+ struct device *dev,
+ int nvec, msi_alloc_info_t *info)
+{
+ struct fsl_mc_device *mc_bus_dev;
+ struct msi_domain_info *msi_info;
+
+ if (!dev_is_fsl_mc(dev))
+ return -EINVAL;
+
+ mc_bus_dev = to_fsl_mc_device(dev);
+ if (!(mc_bus_dev->flags & FSL_MC_IS_DPRC))
+ return -EINVAL;
+
+ /*
+ * Set the device Id to be passed to the GIC-ITS:
+ *
+ * NOTE: This device id corresponds to the IOMMU stream ID
+ * associated with the DPRC object (ICID).
+ */
+ info->scratchpad[0].ul = mc_bus_dev->icid;
+ msi_info = msi_get_domain_info(msi_domain->parent);
+ return msi_info->ops->msi_prepare(msi_domain->parent, dev, nvec, info);
+}
+
+static struct msi_domain_ops its_fsl_mc_msi_ops __ro_after_init = {
+ .msi_prepare = its_fsl_mc_msi_prepare,
+};
+
+static struct msi_domain_info its_fsl_mc_msi_domain_info = {
+ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
+ .ops = &its_fsl_mc_msi_ops,
+ .chip = &its_msi_irq_chip,
+};
+
+static const struct of_device_id its_device_id[] = {
+ { .compatible = "arm,gic-v3-its", },
+ {},
+};
+
+static int __init its_fsl_mc_msi_init(void)
+{
+ struct device_node *np;
+ struct irq_domain *parent;
+ struct irq_domain *mc_msi_domain;
+
+ for (np = of_find_matching_node(NULL, its_device_id); np;
+ np = of_find_matching_node(np, its_device_id)) {
+ if (!of_device_is_available(np))
+ continue;
+ if (!of_property_read_bool(np, "msi-controller"))
+ continue;
+
+ parent = irq_find_matching_host(np, DOMAIN_BUS_NEXUS);
+ if (!parent || !msi_get_domain_info(parent)) {
+ pr_err("%pOF: unable to locate ITS domain\n", np);
+ continue;
+ }
+
+ mc_msi_domain = fsl_mc_msi_create_irq_domain(
+ of_node_to_fwnode(np),
+ &its_fsl_mc_msi_domain_info,
+ parent);
+ if (!mc_msi_domain) {
+ pr_err("%pOF: unable to create fsl-mc domain\n", np);
+ continue;
+ }
+
+ pr_info("fsl-mc MSI: %pOF domain created\n", np);
+ }
+
+ return 0;
+}
+
+early_initcall(its_fsl_mc_msi_init);
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index 2cbb19cddbf8..5416f2b2ac21 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -33,6 +33,7 @@
#include <linux/of_platform.h>
#include <linux/percpu.h>
#include <linux/slab.h>
+#include <linux/syscore_ops.h>
#include <linux/irqchip.h>
#include <linux/irqchip/arm-gic-v3.h>
@@ -46,6 +47,7 @@
#define ITS_FLAGS_CMDQ_NEEDS_FLUSHING (1ULL << 0)
#define ITS_FLAGS_WORKAROUND_CAVIUM_22375 (1ULL << 1)
#define ITS_FLAGS_WORKAROUND_CAVIUM_23144 (1ULL << 2)
+#define ITS_FLAGS_SAVE_SUSPEND_STATE (1ULL << 3)
#define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING (1 << 0)
@@ -101,6 +103,8 @@ struct its_node {
struct its_collection *collections;
struct fwnode_handle *fwnode_handle;
u64 (*get_msi_base)(struct its_device *its_dev);
+ u64 cbaser_save;
+ u32 ctlr_save;
struct list_head its_device_list;
u64 flags;
unsigned long list_nr;
@@ -1875,16 +1879,6 @@ static void its_cpu_init_lpis(void)
gic_data_rdist()->pend_page = pend_page;
}
- /* Disable LPIs */
- val = readl_relaxed(rbase + GICR_CTLR);
- val &= ~GICR_CTLR_ENABLE_LPIS;
- writel_relaxed(val, rbase + GICR_CTLR);
-
- /*
- * Make sure any change to the table is observable by the GIC.
- */
- dsb(sy);
-
/* set PROPBASE */
val = (page_to_phys(gic_rdists->prop_page) |
GICR_PROPBASER_InnerShareable |
@@ -1938,52 +1932,53 @@ static void its_cpu_init_lpis(void)
dsb(sy);
}
-static void its_cpu_init_collection(void)
+static void its_cpu_init_collection(struct its_node *its)
{
- struct its_node *its;
- int cpu;
-
- spin_lock(&its_lock);
- cpu = smp_processor_id();
-
- list_for_each_entry(its, &its_nodes, entry) {
- u64 target;
+ int cpu = smp_processor_id();
+ u64 target;
- /* avoid cross node collections and its mapping */
- if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_23144) {
- struct device_node *cpu_node;
+ /* avoid cross node collections and its mapping */
+ if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_23144) {
+ struct device_node *cpu_node;
- cpu_node = of_get_cpu_node(cpu, NULL);
- if (its->numa_node != NUMA_NO_NODE &&
- its->numa_node != of_node_to_nid(cpu_node))
- continue;
- }
+ cpu_node = of_get_cpu_node(cpu, NULL);
+ if (its->numa_node != NUMA_NO_NODE &&
+ its->numa_node != of_node_to_nid(cpu_node))
+ return;
+ }
+ /*
+ * We now have to bind each collection to its target
+ * redistributor.
+ */
+ if (gic_read_typer(its->base + GITS_TYPER) & GITS_TYPER_PTA) {
/*
- * We now have to bind each collection to its target
+ * This ITS wants the physical address of the
* redistributor.
*/
- if (gic_read_typer(its->base + GITS_TYPER) & GITS_TYPER_PTA) {
- /*
- * This ITS wants the physical address of the
- * redistributor.
- */
- target = gic_data_rdist()->phys_base;
- } else {
- /*
- * This ITS wants a linear CPU number.
- */
- target = gic_read_typer(gic_data_rdist_rd_base() + GICR_TYPER);
- target = GICR_TYPER_CPU_NUMBER(target) << 16;
- }
+ target = gic_data_rdist()->phys_base;
+ } else {
+ /* This ITS wants a linear CPU number. */
+ target = gic_read_typer(gic_data_rdist_rd_base() + GICR_TYPER);
+ target = GICR_TYPER_CPU_NUMBER(target) << 16;
+ }
- /* Perform collection mapping */
- its->collections[cpu].target_address = target;
- its->collections[cpu].col_id = cpu;
+ /* Perform collection mapping */
+ its->collections[cpu].target_address = target;
+ its->collections[cpu].col_id = cpu;
- its_send_mapc(its, &its->collections[cpu], 1);
- its_send_invall(its, &its->collections[cpu]);
- }
+ its_send_mapc(its, &its->collections[cpu], 1);
+ its_send_invall(its, &its->collections[cpu]);
+}
+
+static void its_cpu_init_collections(void)
+{
+ struct its_node *its;
+
+ spin_lock(&its_lock);
+
+ list_for_each_entry(its, &its_nodes, entry)
+ its_cpu_init_collection(its);
spin_unlock(&its_lock);
}
@@ -3041,6 +3036,113 @@ static void its_enable_quirks(struct its_node *its)
gic_enable_quirks(iidr, its_quirks, its);
}
+static int its_save_disable(void)
+{
+ struct its_node *its;
+ int err = 0;
+
+ spin_lock(&its_lock);
+ list_for_each_entry(its, &its_nodes, entry) {
+ void __iomem *base;
+
+ if (!(its->flags & ITS_FLAGS_SAVE_SUSPEND_STATE))
+ continue;
+
+ base = its->base;
+ its->ctlr_save = readl_relaxed(base + GITS_CTLR);
+ err = its_force_quiescent(base);
+ if (err) {
+ pr_err("ITS@%pa: failed to quiesce: %d\n",
+ &its->phys_base, err);
+ writel_relaxed(its->ctlr_save, base + GITS_CTLR);
+ goto err;
+ }
+
+ its->cbaser_save = gits_read_cbaser(base + GITS_CBASER);
+ }
+
+err:
+ if (err) {
+ list_for_each_entry_continue_reverse(its, &its_nodes, entry) {
+ void __iomem *base;
+
+ if (!(its->flags & ITS_FLAGS_SAVE_SUSPEND_STATE))
+ continue;
+
+ base = its->base;
+ writel_relaxed(its->ctlr_save, base + GITS_CTLR);
+ }
+ }
+ spin_unlock(&its_lock);
+
+ return err;
+}
+
+static void its_restore_enable(void)
+{
+ struct its_node *its;
+ int ret;
+
+ spin_lock(&its_lock);
+ list_for_each_entry(its, &its_nodes, entry) {
+ void __iomem *base;
+ int i;
+
+ if (!(its->flags & ITS_FLAGS_SAVE_SUSPEND_STATE))
+ continue;
+
+ base = its->base;
+
+ /*
+ * Make sure that the ITS is disabled. If it fails to quiesce,
+ * don't restore it since writing to CBASER or BASER<n>
+ * registers is undefined according to the GIC v3 ITS
+ * Specification.
+ */
+ ret = its_force_quiescent(base);
+ if (ret) {
+ pr_err("ITS@%pa: failed to quiesce on resume: %d\n",
+ &its->phys_base, ret);
+ continue;
+ }
+
+ gits_write_cbaser(its->cbaser_save, base + GITS_CBASER);
+
+ /*
+ * Writing CBASER resets CREADR to 0, so make CWRITER and
+ * cmd_write line up with it.
+ */
+ its->cmd_write = its->cmd_base;
+ gits_write_cwriter(0, base + GITS_CWRITER);
+
+ /* Restore GITS_BASER from the value cache. */
+ for (i = 0; i < GITS_BASER_NR_REGS; i++) {
+ struct its_baser *baser = &its->tables[i];
+
+ if (!(baser->val & GITS_BASER_VALID))
+ continue;
+
+ its_write_baser(its, baser, baser->val);
+ }
+ writel_relaxed(its->ctlr_save, base + GITS_CTLR);
+
+ /*
+ * Reinit the collection if it's stored in the ITS. This is
+ * indicated by the col_id being less than the HCC field.
+ * CID < HCC as specified in the GIC v3 Documentation.
+ */
+ if (its->collections[smp_processor_id()].col_id <
+ GITS_TYPER_HCC(gic_read_typer(base + GITS_TYPER)))
+ its_cpu_init_collection(its);
+ }
+ spin_unlock(&its_lock);
+}
+
+static struct syscore_ops its_syscore_ops = {
+ .suspend = its_save_disable,
+ .resume = its_restore_enable,
+};
+
static int its_init_domain(struct fwnode_handle *handle, struct its_node *its)
{
struct irq_domain *inner_domain;
@@ -3260,6 +3362,9 @@ static int __init its_probe_one(struct resource *res,
ctlr |= GITS_CTLR_ImDe;
writel_relaxed(ctlr, its->base + GITS_CTLR);
+ if (GITS_TYPER_HCC(typer))
+ its->flags |= ITS_FLAGS_SAVE_SUSPEND_STATE;
+
err = its_init_domain(handle, its);
if (err)
goto out_free_tables;
@@ -3287,15 +3392,71 @@ static bool gic_rdists_supports_plpis(void)
return !!(gic_read_typer(gic_data_rdist_rd_base() + GICR_TYPER) & GICR_TYPER_PLPIS);
}
+static int redist_disable_lpis(void)
+{
+ void __iomem *rbase = gic_data_rdist_rd_base();
+ u64 timeout = USEC_PER_SEC;
+ u64 val;
+
+ if (!gic_rdists_supports_plpis()) {
+ pr_info("CPU%d: LPIs not supported\n", smp_processor_id());
+ return -ENXIO;
+ }
+
+ val = readl_relaxed(rbase + GICR_CTLR);
+ if (!(val & GICR_CTLR_ENABLE_LPIS))
+ return 0;
+
+ pr_warn("CPU%d: Booted with LPIs enabled, memory probably corrupted\n",
+ smp_processor_id());
+ add_taint(TAINT_CRAP, LOCKDEP_STILL_OK);
+
+ /* Disable LPIs */
+ val &= ~GICR_CTLR_ENABLE_LPIS;
+ writel_relaxed(val, rbase + GICR_CTLR);
+
+ /* Make sure any change to GICR_CTLR is observable by the GIC */
+ dsb(sy);
+
+ /*
+ * Software must observe RWP==0 after clearing GICR_CTLR.EnableLPIs
+ * from 1 to 0 before programming GICR_PEND{PROP}BASER registers.
+ * Error out if we time out waiting for RWP to clear.
+ */
+ while (readl_relaxed(rbase + GICR_CTLR) & GICR_CTLR_RWP) {
+ if (!timeout) {
+ pr_err("CPU%d: Timeout while disabling LPIs\n",
+ smp_processor_id());
+ return -ETIMEDOUT;
+ }
+ udelay(1);
+ timeout--;
+ }
+
+ /*
+ * After it has been written to 1, it is IMPLEMENTATION
+ * DEFINED whether GICR_CTLR.EnableLPI becomes RES1 or can be
+ * cleared to 0. Error out if clearing the bit failed.
+ */
+ if (readl_relaxed(rbase + GICR_CTLR) & GICR_CTLR_ENABLE_LPIS) {
+ pr_err("CPU%d: Failed to disable LPIs\n", smp_processor_id());
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
int its_cpu_init(void)
{
if (!list_empty(&its_nodes)) {
- if (!gic_rdists_supports_plpis()) {
- pr_info("CPU%d: LPIs not supported\n", smp_processor_id());
- return -ENXIO;
- }
+ int ret;
+
+ ret = redist_disable_lpis();
+ if (ret)
+ return ret;
+
its_cpu_init_lpis();
- its_cpu_init_collection();
+ its_cpu_init_collections();
}
return 0;
@@ -3451,7 +3612,8 @@ static int __init gic_acpi_parse_madt_its(struct acpi_subtable_header *header,
return -ENOMEM;
}
- err = iort_register_domain_token(its_entry->translation_id, dom_handle);
+ err = iort_register_domain_token(its_entry->translation_id, res.start,
+ dom_handle);
if (err) {
pr_err("ITS@%pa: Unable to register GICv3 ITS domain token (ITS ID %d) to IORT\n",
&res.start, its_entry->translation_id);
@@ -3516,5 +3678,7 @@ int __init its_init(struct fwnode_handle *handle, struct rdists *rdists,
}
}
+ register_syscore_ops(&its_syscore_ops);
+
return 0;
}
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index d99cc07903ec..e5d101418390 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -61,7 +61,7 @@ struct gic_chip_data {
};
static struct gic_chip_data gic_data __read_mostly;
-static struct static_key supports_deactivate = STATIC_KEY_INIT_TRUE;
+static DEFINE_STATIC_KEY_TRUE(supports_deactivate_key);
static struct gic_kvm_info gic_v3_kvm_info;
static DEFINE_PER_CPU(bool, has_rss);
@@ -354,7 +354,7 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs
if (likely(irqnr > 15 && irqnr < 1020) || irqnr >= 8192) {
int err;
- if (static_key_true(&supports_deactivate))
+ if (static_branch_likely(&supports_deactivate_key))
gic_write_eoir(irqnr);
else
isb();
@@ -362,7 +362,7 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs
err = handle_domain_irq(gic_data.domain, irqnr, regs);
if (err) {
WARN_ONCE(true, "Unexpected interrupt received!\n");
- if (static_key_true(&supports_deactivate)) {
+ if (static_branch_likely(&supports_deactivate_key)) {
if (irqnr < 8192)
gic_write_dir(irqnr);
} else {
@@ -373,7 +373,7 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs
}
if (irqnr < 16) {
gic_write_eoir(irqnr);
- if (static_key_true(&supports_deactivate))
+ if (static_branch_likely(&supports_deactivate_key))
gic_write_dir(irqnr);
#ifdef CONFIG_SMP
/*
@@ -532,6 +532,8 @@ static void gic_cpu_sys_reg_init(void)
int i, cpu = smp_processor_id();
u64 mpidr = cpu_logical_map(cpu);
u64 need_rss = MPIDR_RS(mpidr);
+ bool group0;
+ u32 val, pribits;
/*
* Need to check that the SRE bit has actually been set. If
@@ -543,8 +545,28 @@ static void gic_cpu_sys_reg_init(void)
if (!gic_enable_sre())
pr_err("GIC: unable to set SRE (disabled at EL2), panic ahead\n");
+ pribits = gic_read_ctlr();
+ pribits &= ICC_CTLR_EL1_PRI_BITS_MASK;
+ pribits >>= ICC_CTLR_EL1_PRI_BITS_SHIFT;
+ pribits++;
+
+ /*
+ * Let's find out if Group0 is under control of EL3 or not by
+ * setting the highest possible, non-zero priority in PMR.
+ *
+ * If SCR_EL3.FIQ is set, the priority gets shifted down in
+ * order for the CPU interface to set bit 7, and keep the
+ * actual priority in the non-secure range. In the process, it
+ * looses the least significant bit and the actual priority
+ * becomes 0x80. Reading it back returns 0, indicating that
+ * we're don't have access to Group0.
+ */
+ write_gicreg(BIT(8 - pribits), ICC_PMR_EL1);
+ val = read_gicreg(ICC_PMR_EL1);
+ group0 = val != 0;
+
/* Set priority mask register */
- gic_write_pmr(DEFAULT_PMR_VALUE);
+ write_gicreg(DEFAULT_PMR_VALUE, ICC_PMR_EL1);
/*
* Some firmwares hand over to the kernel with the BPR changed from
@@ -554,7 +576,7 @@ static void gic_cpu_sys_reg_init(void)
*/
gic_write_bpr1(0);
- if (static_key_true(&supports_deactivate)) {
+ if (static_branch_likely(&supports_deactivate_key)) {
/* EOI drops priority only (mode 1) */
gic_write_ctlr(ICC_CTLR_EL1_EOImode_drop);
} else {
@@ -562,6 +584,37 @@ static void gic_cpu_sys_reg_init(void)
gic_write_ctlr(ICC_CTLR_EL1_EOImode_drop_dir);
}
+ /* Always whack Group0 before Group1 */
+ if (group0) {
+ switch(pribits) {
+ case 8:
+ case 7:
+ write_gicreg(0, ICC_AP0R3_EL1);
+ write_gicreg(0, ICC_AP0R2_EL1);
+ case 6:
+ write_gicreg(0, ICC_AP0R1_EL1);
+ case 5:
+ case 4:
+ write_gicreg(0, ICC_AP0R0_EL1);
+ }
+
+ isb();
+ }
+
+ switch(pribits) {
+ case 8:
+ case 7:
+ write_gicreg(0, ICC_AP1R3_EL1);
+ write_gicreg(0, ICC_AP1R2_EL1);
+ case 6:
+ write_gicreg(0, ICC_AP1R1_EL1);
+ case 5:
+ case 4:
+ write_gicreg(0, ICC_AP1R0_EL1);
+ }
+
+ isb();
+
/* ... and let's hit the road... */
gic_write_grpen1(1);
@@ -590,9 +643,17 @@ static void gic_cpu_sys_reg_init(void)
pr_crit_once("RSS is required but GICD doesn't support it\n");
}
+static bool gicv3_nolpi;
+
+static int __init gicv3_nolpi_cfg(char *buf)
+{
+ return strtobool(buf, &gicv3_nolpi);
+}
+early_param("irqchip.gicv3_nolpi", gicv3_nolpi_cfg);
+
static int gic_dist_supports_lpis(void)
{
- return !!(readl_relaxed(gic_data.dist_base + GICD_TYPER) & GICD_TYPER_LPIS);
+ return !!(readl_relaxed(gic_data.dist_base + GICD_TYPER) & GICD_TYPER_LPIS) && !gicv3_nolpi;
}
static void gic_cpu_init(void)
@@ -823,7 +884,7 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
{
struct irq_chip *chip = &gic_chip;
- if (static_key_true(&supports_deactivate))
+ if (static_branch_likely(&supports_deactivate_key))
chip = &gic_eoimode1_chip;
/* SGIs are private to the core kernel */
@@ -861,6 +922,8 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
return 0;
}
+#define GIC_IRQ_TYPE_PARTITION (GIC_IRQ_TYPE_LPI + 1)
+
static int gic_irq_domain_translate(struct irq_domain *d,
struct irq_fwspec *fwspec,
unsigned long *hwirq,
@@ -875,6 +938,7 @@ static int gic_irq_domain_translate(struct irq_domain *d,
*hwirq = fwspec->param[1] + 32;
break;
case 1: /* PPI */
+ case GIC_IRQ_TYPE_PARTITION:
*hwirq = fwspec->param[1] + 16;
break;
case GIC_IRQ_TYPE_LPI: /* LPI */
@@ -885,6 +949,13 @@ static int gic_irq_domain_translate(struct irq_domain *d,
}
*type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
+
+ /*
+ * Make it clear that broken DTs are... broken.
+ * Partitionned PPIs are an unfortunate exception.
+ */
+ WARN_ON(*type == IRQ_TYPE_NONE &&
+ fwspec->param[0] != GIC_IRQ_TYPE_PARTITION);
return 0;
}
@@ -894,6 +965,8 @@ static int gic_irq_domain_translate(struct irq_domain *d,
*hwirq = fwspec->param[0];
*type = fwspec->param[1];
+
+ WARN_ON(*type == IRQ_TYPE_NONE);
return 0;
}
@@ -1002,9 +1075,9 @@ static int __init gic_init_bases(void __iomem *dist_base,
int err;
if (!is_hyp_mode_available())
- static_key_slow_dec(&supports_deactivate);
+ static_branch_disable(&supports_deactivate_key);
- if (static_key_true(&supports_deactivate))
+ if (static_branch_likely(&supports_deactivate_key))
pr_info("GIC: Using split EOI/Deactivate mode\n");
gic_data.fwnode = handle;
@@ -1140,7 +1213,7 @@ static void __init gic_populate_ppi_partitions(struct device_node *gic_node)
.fwnode = gic_data.fwnode,
.param_count = 3,
.param = {
- [0] = 1,
+ [0] = GIC_IRQ_TYPE_PARTITION,
[1] = i,
[2] = IRQ_TYPE_NONE,
},
@@ -1239,7 +1312,7 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
gic_populate_ppi_partitions(node);
- if (static_key_true(&supports_deactivate))
+ if (static_branch_likely(&supports_deactivate_key))
gic_of_setup_kvm_info(node);
return 0;
@@ -1541,7 +1614,7 @@ gic_acpi_init(struct acpi_subtable_header *header, const unsigned long end)
acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, domain_handle);
- if (static_key_true(&supports_deactivate))
+ if (static_branch_likely(&supports_deactivate_key))
gic_acpi_setup_kvm_info();
return 0;
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 121af5cf688f..ced10c44b68a 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -121,7 +121,7 @@ static DEFINE_RAW_SPINLOCK(cpu_map_lock);
#define NR_GIC_CPU_IF 8
static u8 gic_cpu_map[NR_GIC_CPU_IF] __read_mostly;
-static struct static_key supports_deactivate = STATIC_KEY_INIT_TRUE;
+static DEFINE_STATIC_KEY_TRUE(supports_deactivate_key);
static struct gic_chip_data gic_data[CONFIG_ARM_GIC_MAX_NR] __read_mostly;
@@ -361,7 +361,7 @@ static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
irqnr = irqstat & GICC_IAR_INT_ID_MASK;
if (likely(irqnr > 15 && irqnr < 1020)) {
- if (static_key_true(&supports_deactivate))
+ if (static_branch_likely(&supports_deactivate_key))
writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
isb();
handle_domain_irq(gic->domain, irqnr, regs);
@@ -369,7 +369,7 @@ static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
}
if (irqnr < 16) {
writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
- if (static_key_true(&supports_deactivate))
+ if (static_branch_likely(&supports_deactivate_key))
writel_relaxed(irqstat, cpu_base + GIC_CPU_DEACTIVATE);
#ifdef CONFIG_SMP
/*
@@ -453,15 +453,26 @@ static u8 gic_get_cpumask(struct gic_chip_data *gic)
return mask;
}
+static bool gic_check_gicv2(void __iomem *base)
+{
+ u32 val = readl_relaxed(base + GIC_CPU_IDENT);
+ return (val & 0xff0fff) == 0x02043B;
+}
+
static void gic_cpu_if_up(struct gic_chip_data *gic)
{
void __iomem *cpu_base = gic_data_cpu_base(gic);
u32 bypass = 0;
u32 mode = 0;
+ int i;
- if (gic == &gic_data[0] && static_key_true(&supports_deactivate))
+ if (gic == &gic_data[0] && static_branch_likely(&supports_deactivate_key))
mode = GIC_CPU_CTRL_EOImodeNS;
+ if (gic_check_gicv2(cpu_base))
+ for (i = 0; i < 4; i++)
+ writel_relaxed(0, cpu_base + GIC_CPU_ACTIVEPRIO + i * 4);
+
/*
* Preserve bypass disable bits to be written back later
*/
@@ -1000,6 +1011,9 @@ static int gic_irq_domain_translate(struct irq_domain *d,
*hwirq += 16;
*type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
+
+ /* Make it clear that broken DTs are... broken */
+ WARN_ON(*type == IRQ_TYPE_NONE);
return 0;
}
@@ -1009,6 +1023,8 @@ static int gic_irq_domain_translate(struct irq_domain *d,
*hwirq = fwspec->param[0];
*type = fwspec->param[1];
+
+ WARN_ON(*type == IRQ_TYPE_NONE);
return 0;
}
@@ -1203,11 +1219,11 @@ static int __init __gic_init_bases(struct gic_chip_data *gic,
"irqchip/arm/gic:starting",
gic_starting_cpu, NULL);
set_handle_irq(gic_handle_irq);
- if (static_key_true(&supports_deactivate))
+ if (static_branch_likely(&supports_deactivate_key))
pr_info("GIC: Using split EOI/Deactivate mode\n");
}
- if (static_key_true(&supports_deactivate) && gic == &gic_data[0]) {
+ if (static_branch_likely(&supports_deactivate_key) && gic == &gic_data[0]) {
name = kasprintf(GFP_KERNEL, "GICv2");
gic_init_chip(gic, NULL, name, true);
} else {
@@ -1234,7 +1250,7 @@ void __init gic_init(unsigned int gic_nr, int irq_start,
* Non-DT/ACPI systems won't run a hypervisor, so let's not
* bother with these...
*/
- static_key_slow_dec(&supports_deactivate);
+ static_branch_disable(&supports_deactivate_key);
gic = &gic_data[gic_nr];
gic->raw_dist_base = dist_base;
@@ -1264,12 +1280,6 @@ static int __init gicv2_force_probe_cfg(char *buf)
}
early_param("irqchip.gicv2_force_probe", gicv2_force_probe_cfg);
-static bool gic_check_gicv2(void __iomem *base)
-{
- u32 val = readl_relaxed(base + GIC_CPU_IDENT);
- return (val & 0xff0fff) == 0x02043B;
-}
-
static bool gic_check_eoimode(struct device_node *node, void __iomem **base)
{
struct resource cpuif_res;
@@ -1420,7 +1430,7 @@ static void __init gic_of_setup_kvm_info(struct device_node *node)
if (ret)
return;
- if (static_key_true(&supports_deactivate))
+ if (static_branch_likely(&supports_deactivate_key))
gic_set_kvm_info(&gic_v2_kvm_info);
}
@@ -1447,7 +1457,7 @@ gic_of_init(struct device_node *node, struct device_node *parent)
* or the CPU interface is too small.
*/
if (gic_cnt == 0 && !gic_check_eoimode(node, &gic->raw_cpu_base))
- static_key_slow_dec(&supports_deactivate);
+ static_branch_disable(&supports_deactivate_key);
ret = __gic_init_bases(gic, -1, &node->fwnode);
if (ret) {
@@ -1628,7 +1638,7 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header,
* interface will always be the right size.
*/
if (!is_hyp_mode_available())
- static_key_slow_dec(&supports_deactivate);
+ static_branch_disable(&supports_deactivate_key);
/*
* Initialize GIC instance zero (no multi-GIC support).
@@ -1653,7 +1663,7 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header,
if (IS_ENABLED(CONFIG_ARM_GIC_V2M))
gicv2m_init(NULL, gic_data[0].domain);
- if (static_key_true(&supports_deactivate))
+ if (static_branch_likely(&supports_deactivate_key))
gic_acpi_setup_kvm_info();
return 0;
diff --git a/drivers/irqchip/irq-metag-ext.c b/drivers/irqchip/irq-metag-ext.c
deleted file mode 100644
index e67483161f0f..000000000000
--- a/drivers/irqchip/irq-metag-ext.c
+++ /dev/null
@@ -1,871 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Meta External interrupt code.
- *
- * Copyright (C) 2005-2012 Imagination Technologies Ltd.
- *
- * External interrupts on Meta are configured at two-levels, in the CPU core and
- * in the external trigger block. Interrupts from SoC peripherals are
- * multiplexed onto a single Meta CPU "trigger" - traditionally it has always
- * been trigger 2 (TR2). For info on how de-multiplexing happens check out
- * meta_intc_irq_demux().
- */
-
-#include <linux/interrupt.h>
-#include <linux/irqchip/metag-ext.h>
-#include <linux/irqdomain.h>
-#include <linux/io.h>
-#include <linux/of.h>
-#include <linux/slab.h>
-#include <linux/syscore_ops.h>
-
-#include <asm/irq.h>
-#include <asm/hwthread.h>
-
-#define HWSTAT_STRIDE 8
-#define HWVEC_BLK_STRIDE 0x1000
-
-/**
- * struct meta_intc_priv - private meta external interrupt data
- * @nr_banks: Number of interrupt banks
- * @domain: IRQ domain for all banks of external IRQs
- * @unmasked: Record of unmasked IRQs
- * @levels_altered: Record of altered level bits
- */
-struct meta_intc_priv {
- unsigned int nr_banks;
- struct irq_domain *domain;
-
- unsigned long unmasked[4];
-
-#ifdef CONFIG_METAG_SUSPEND_MEM
- unsigned long levels_altered[4];
-#endif
-};
-
-/* Private data for the one and only external interrupt controller */
-static struct meta_intc_priv meta_intc_priv;
-
-/**
- * meta_intc_offset() - Get the offset into the bank of a hardware IRQ number
- * @hw: Hardware IRQ number (within external trigger block)
- *
- * Returns: Bit offset into the IRQ's bank registers
- */
-static unsigned int meta_intc_offset(irq_hw_number_t hw)
-{
- return hw & 0x1f;
-}
-
-/**
- * meta_intc_bank() - Get the bank number of a hardware IRQ number
- * @hw: Hardware IRQ number (within external trigger block)
- *
- * Returns: Bank number indicating which register the IRQ's bits are
- */
-static unsigned int meta_intc_bank(irq_hw_number_t hw)
-{
- return hw >> 5;
-}
-
-/**
- * meta_intc_stat_addr() - Get the address of a HWSTATEXT register
- * @hw: Hardware IRQ number (within external trigger block)
- *
- * Returns: Address of a HWSTATEXT register containing the status bit for
- * the specified hardware IRQ number
- */
-static void __iomem *meta_intc_stat_addr(irq_hw_number_t hw)
-{
- return (void __iomem *)(HWSTATEXT +
- HWSTAT_STRIDE * meta_intc_bank(hw));
-}
-
-/**
- * meta_intc_level_addr() - Get the address of a HWLEVELEXT register
- * @hw: Hardware IRQ number (within external trigger block)
- *
- * Returns: Address of a HWLEVELEXT register containing the sense bit for
- * the specified hardware IRQ number
- */
-static void __iomem *meta_intc_level_addr(irq_hw_number_t hw)
-{
- return (void __iomem *)(HWLEVELEXT +
- HWSTAT_STRIDE * meta_intc_bank(hw));
-}
-
-/**
- * meta_intc_mask_addr() - Get the address of a HWMASKEXT register
- * @hw: Hardware IRQ number (within external trigger block)
- *
- * Returns: Address of a HWMASKEXT register containing the mask bit for the
- * specified hardware IRQ number
- */
-static void __iomem *meta_intc_mask_addr(irq_hw_number_t hw)
-{
- return (void __iomem *)(HWMASKEXT +
- HWSTAT_STRIDE * meta_intc_bank(hw));
-}
-
-/**
- * meta_intc_vec_addr() - Get the vector address of a hardware interrupt
- * @hw: Hardware IRQ number (within external trigger block)
- *
- * Returns: Address of a HWVECEXT register controlling the core trigger to
- * vector the IRQ onto
- */
-static inline void __iomem *meta_intc_vec_addr(irq_hw_number_t hw)
-{
- return (void __iomem *)(HWVEC0EXT +
- HWVEC_BLK_STRIDE * meta_intc_bank(hw) +
- HWVECnEXT_STRIDE * meta_intc_offset(hw));
-}
-
-/**
- * meta_intc_startup_irq() - set up an external irq
- * @data: data for the external irq to start up
- *
- * Multiplex interrupts for irq onto TR2. Clear any pending interrupts and
- * unmask irq, both using the appropriate callbacks.
- */
-static unsigned int meta_intc_startup_irq(struct irq_data *data)
-{
- irq_hw_number_t hw = data->hwirq;
- void __iomem *vec_addr = meta_intc_vec_addr(hw);
- int thread = hard_processor_id();
-
- /* Perform any necessary acking. */
- if (data->chip->irq_ack)
- data->chip->irq_ack(data);
-
- /* Wire up this interrupt to the core with HWVECxEXT. */
- metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR2(thread)), vec_addr);
-
- /* Perform any necessary unmasking. */
- data->chip->irq_unmask(data);
-
- return 0;
-}
-
-/**
- * meta_intc_shutdown_irq() - turn off an external irq
- * @data: data for the external irq to turn off
- *
- * Mask irq using the appropriate callback and stop muxing it onto TR2.
- */
-static void meta_intc_shutdown_irq(struct irq_data *data)
-{
- irq_hw_number_t hw = data->hwirq;
- void __iomem *vec_addr = meta_intc_vec_addr(hw);
-
- /* Mask the IRQ */
- data->chip->irq_mask(data);
-
- /*
- * Disable the IRQ at the core by removing the interrupt from
- * the HW vector mapping.
- */
- metag_out32(0, vec_addr);
-}
-
-/**
- * meta_intc_ack_irq() - acknowledge an external irq
- * @data: data for the external irq to ack
- *
- * Clear down an edge interrupt in the status register.
- */
-static void meta_intc_ack_irq(struct irq_data *data)
-{
- irq_hw_number_t hw = data->hwirq;
- unsigned int bit = 1 << meta_intc_offset(hw);
- void __iomem *stat_addr = meta_intc_stat_addr(hw);
-
- /* Ack the int, if it is still 'on'.
- * NOTE - this only works for edge triggered interrupts.
- */
- if (metag_in32(stat_addr) & bit)
- metag_out32(bit, stat_addr);
-}
-
-/**
- * record_irq_is_masked() - record the IRQ masked so it doesn't get handled
- * @data: data for the external irq to record
- *
- * This should get called whenever an external IRQ is masked (by whichever
- * callback is used). It records the IRQ masked so that it doesn't get handled
- * if it still shows up in the status register.
- */
-static void record_irq_is_masked(struct irq_data *data)
-{
- struct meta_intc_priv *priv = &meta_intc_priv;
- irq_hw_number_t hw = data->hwirq;
-
- clear_bit(meta_intc_offset(hw), &priv->unmasked[meta_intc_bank(hw)]);
-}
-
-/**
- * record_irq_is_unmasked() - record the IRQ unmasked so it can be handled
- * @data: data for the external irq to record
- *
- * This should get called whenever an external IRQ is unmasked (by whichever
- * callback is used). It records the IRQ unmasked so that it gets handled if it
- * shows up in the status register.
- */
-static void record_irq_is_unmasked(struct irq_data *data)
-{
- struct meta_intc_priv *priv = &meta_intc_priv;
- irq_hw_number_t hw = data->hwirq;
-
- set_bit(meta_intc_offset(hw), &priv->unmasked[meta_intc_bank(hw)]);
-}
-
-/*
- * For use by wrapper IRQ drivers
- */
-
-/**
- * meta_intc_mask_irq_simple() - minimal mask used by wrapper IRQ drivers
- * @data: data for the external irq being masked
- *
- * This should be called by any wrapper IRQ driver mask functions. it doesn't do
- * any masking but records the IRQ as masked so that the core code knows the
- * mask has taken place. It is the callers responsibility to ensure that the IRQ
- * won't trigger an interrupt to the core.
- */
-void meta_intc_mask_irq_simple(struct irq_data *data)
-{
- record_irq_is_masked(data);
-}
-
-/**
- * meta_intc_unmask_irq_simple() - minimal unmask used by wrapper IRQ drivers
- * @data: data for the external irq being unmasked
- *
- * This should be called by any wrapper IRQ driver unmask functions. it doesn't
- * do any unmasking but records the IRQ as unmasked so that the core code knows
- * the unmask has taken place. It is the callers responsibility to ensure that
- * the IRQ can now trigger an interrupt to the core.
- */
-void meta_intc_unmask_irq_simple(struct irq_data *data)
-{
- record_irq_is_unmasked(data);
-}
-
-
-/**
- * meta_intc_mask_irq() - mask an external irq using HWMASKEXT
- * @data: data for the external irq to mask
- *
- * This is a default implementation of a mask function which makes use of the
- * HWMASKEXT registers available in newer versions.
- *
- * Earlier versions without these registers should use SoC level IRQ masking
- * which call the meta_intc_*_simple() functions above, or if that isn't
- * available should use the fallback meta_intc_*_nomask() functions below.
- */
-static void meta_intc_mask_irq(struct irq_data *data)
-{
- irq_hw_number_t hw = data->hwirq;
- unsigned int bit = 1 << meta_intc_offset(hw);
- void __iomem *mask_addr = meta_intc_mask_addr(hw);
- unsigned long flags;
-
- record_irq_is_masked(data);
-
- /* update the interrupt mask */
- __global_lock2(flags);
- metag_out32(metag_in32(mask_addr) & ~bit, mask_addr);
- __global_unlock2(flags);
-}
-
-/**
- * meta_intc_unmask_irq() - unmask an external irq using HWMASKEXT
- * @data: data for the external irq to unmask
- *
- * This is a default implementation of an unmask function which makes use of the
- * HWMASKEXT registers available on new versions. It should be paired with
- * meta_intc_mask_irq() above.
- */
-static void meta_intc_unmask_irq(struct irq_data *data)
-{
- irq_hw_number_t hw = data->hwirq;
- unsigned int bit = 1 << meta_intc_offset(hw);
- void __iomem *mask_addr = meta_intc_mask_addr(hw);
- unsigned long flags;
-
- record_irq_is_unmasked(data);
-
- /* update the interrupt mask */
- __global_lock2(flags);
- metag_out32(metag_in32(mask_addr) | bit, mask_addr);
- __global_unlock2(flags);
-}
-
-/**
- * meta_intc_mask_irq_nomask() - mask an external irq by unvectoring
- * @data: data for the external irq to mask
- *
- * This is the version of the mask function for older versions which don't have
- * HWMASKEXT registers, or a SoC level means of masking IRQs. Instead the IRQ is
- * unvectored from the core and retriggered if necessary later.
- */
-static void meta_intc_mask_irq_nomask(struct irq_data *data)
-{
- irq_hw_number_t hw = data->hwirq;
- void __iomem *vec_addr = meta_intc_vec_addr(hw);
-
- record_irq_is_masked(data);
-
- /* there is no interrupt mask, so unvector the interrupt */
- metag_out32(0, vec_addr);
-}
-
-/**
- * meta_intc_unmask_edge_irq_nomask() - unmask an edge irq by revectoring
- * @data: data for the external irq to unmask
- *
- * This is the version of the unmask function for older versions which don't
- * have HWMASKEXT registers, or a SoC level means of masking IRQs. Instead the
- * IRQ is revectored back to the core and retriggered if necessary.
- *
- * The retriggering done by this function is specific to edge interrupts.
- */
-static void meta_intc_unmask_edge_irq_nomask(struct irq_data *data)
-{
- irq_hw_number_t hw = data->hwirq;
- unsigned int bit = 1 << meta_intc_offset(hw);
- void __iomem *stat_addr = meta_intc_stat_addr(hw);
- void __iomem *vec_addr = meta_intc_vec_addr(hw);
- unsigned int thread = hard_processor_id();
-
- record_irq_is_unmasked(data);
-
- /* there is no interrupt mask, so revector the interrupt */
- metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR2(thread)), vec_addr);
-
- /*
- * Re-trigger interrupt
- *
- * Writing a 1 toggles, and a 0->1 transition triggers. We only
- * retrigger if the status bit is already set, which means we
- * need to clear it first. Retriggering is fundamentally racy
- * because if the interrupt fires again after we clear it we
- * could end up clearing it again and the interrupt handler
- * thinking it hasn't fired. Therefore we need to keep trying to
- * retrigger until the bit is set.
- */
- if (metag_in32(stat_addr) & bit) {
- metag_out32(bit, stat_addr);
- while (!(metag_in32(stat_addr) & bit))
- metag_out32(bit, stat_addr);
- }
-}
-
-/**
- * meta_intc_unmask_level_irq_nomask() - unmask a level irq by revectoring
- * @data: data for the external irq to unmask
- *
- * This is the version of the unmask function for older versions which don't
- * have HWMASKEXT registers, or a SoC level means of masking IRQs. Instead the
- * IRQ is revectored back to the core and retriggered if necessary.
- *
- * The retriggering done by this function is specific to level interrupts.
- */
-static void meta_intc_unmask_level_irq_nomask(struct irq_data *data)
-{
- irq_hw_number_t hw = data->hwirq;
- unsigned int bit = 1 << meta_intc_offset(hw);
- void __iomem *stat_addr = meta_intc_stat_addr(hw);
- void __iomem *vec_addr = meta_intc_vec_addr(hw);
- unsigned int thread = hard_processor_id();
-
- record_irq_is_unmasked(data);
-
- /* there is no interrupt mask, so revector the interrupt */
- metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR2(thread)), vec_addr);
-
- /* Re-trigger interrupt */
- /* Writing a 1 triggers interrupt */
- if (metag_in32(stat_addr) & bit)
- metag_out32(bit, stat_addr);
-}
-
-/**
- * meta_intc_irq_set_type() - set the type of an external irq
- * @data: data for the external irq to set the type of
- * @flow_type: new irq flow type
- *
- * Set the flow type of an external interrupt. This updates the irq chip and irq
- * handler depending on whether the irq is edge or level sensitive (the polarity
- * is ignored), and also sets up the bit in HWLEVELEXT so the hardware knows
- * when to trigger.
- */
-static int meta_intc_irq_set_type(struct irq_data *data, unsigned int flow_type)
-{
-#ifdef CONFIG_METAG_SUSPEND_MEM
- struct meta_intc_priv *priv = &meta_intc_priv;
-#endif
- irq_hw_number_t hw = data->hwirq;
- unsigned int bit = 1 << meta_intc_offset(hw);
- void __iomem *level_addr = meta_intc_level_addr(hw);
- unsigned long flags;
- unsigned int level;
-
- /* update the chip/handler */
- if (flow_type & IRQ_TYPE_LEVEL_MASK)
- irq_set_chip_handler_name_locked(data, &meta_intc_level_chip,
- handle_level_irq, NULL);
- else
- irq_set_chip_handler_name_locked(data, &meta_intc_edge_chip,
- handle_edge_irq, NULL);
-
- /* and clear/set the bit in HWLEVELEXT */
- __global_lock2(flags);
- level = metag_in32(level_addr);
- if (flow_type & IRQ_TYPE_LEVEL_MASK)
- level |= bit;
- else
- level &= ~bit;
- metag_out32(level, level_addr);
-#ifdef CONFIG_METAG_SUSPEND_MEM
- priv->levels_altered[meta_intc_bank(hw)] |= bit;
-#endif
- __global_unlock2(flags);
-
- return 0;
-}
-
-/**
- * meta_intc_irq_demux() - external irq de-multiplexer
- * @desc: the interrupt description structure for this irq
- *
- * The cpu receives an interrupt on TR2 when a SoC interrupt has occurred. It is
- * this function's job to demux this irq and figure out exactly which external
- * irq needs servicing.
- *
- * Whilst using TR2 to detect external interrupts is a software convention it is
- * (hopefully) unlikely to change.
- */
-static void meta_intc_irq_demux(struct irq_desc *desc)
-{
- struct meta_intc_priv *priv = &meta_intc_priv;
- irq_hw_number_t hw;
- unsigned int bank, irq_no, status;
- void __iomem *stat_addr = meta_intc_stat_addr(0);
-
- /*
- * Locate which interrupt has caused our handler to run.
- */
- for (bank = 0; bank < priv->nr_banks; ++bank) {
- /* Which interrupts are currently pending in this bank? */
-recalculate:
- status = metag_in32(stat_addr) & priv->unmasked[bank];
-
- for (hw = bank*32; status; status >>= 1, ++hw) {
- if (status & 0x1) {
- /*
- * Map the hardware IRQ number to a virtual
- * Linux IRQ number.
- */
- irq_no = irq_linear_revmap(priv->domain, hw);
-
- /*
- * Only fire off external interrupts that are
- * registered to be handled by the kernel.
- * Other external interrupts are probably being
- * handled by other Meta hardware threads.
- */
- generic_handle_irq(irq_no);
-
- /*
- * The handler may have re-enabled interrupts
- * which could have caused a nested invocation
- * of this code and make the copy of the
- * status register we are using invalid.
- */
- goto recalculate;
- }
- }
- stat_addr += HWSTAT_STRIDE;
- }
-}
-
-#ifdef CONFIG_SMP
-/**
- * meta_intc_set_affinity() - set the affinity for an interrupt
- * @data: data for the external irq to set the affinity of
- * @cpumask: cpu mask representing cpus which can handle the interrupt
- * @force: whether to force (ignored)
- *
- * Revector the specified external irq onto a specific cpu's TR2 trigger, so
- * that that cpu tends to be the one who handles it.
- */
-static int meta_intc_set_affinity(struct irq_data *data,
- const struct cpumask *cpumask, bool force)
-{
- irq_hw_number_t hw = data->hwirq;
- void __iomem *vec_addr = meta_intc_vec_addr(hw);
- unsigned int cpu, thread;
-
- /*
- * Wire up this interrupt from HWVECxEXT to the Meta core.
- *
- * Note that we can't wire up HWVECxEXT to interrupt more than
- * one cpu (the interrupt code doesn't support it), so we just
- * pick the first cpu we find in 'cpumask'.
- */
- cpu = cpumask_any_and(cpumask, cpu_online_mask);
- thread = cpu_2_hwthread_id[cpu];
-
- metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR2(thread)), vec_addr);
-
- irq_data_update_effective_affinity(data, cpumask_of(cpu));
-
- return 0;
-}
-#else
-#define meta_intc_set_affinity NULL
-#endif
-
-#ifdef CONFIG_PM_SLEEP
-#define META_INTC_CHIP_FLAGS (IRQCHIP_MASK_ON_SUSPEND \
- | IRQCHIP_SKIP_SET_WAKE)
-#else
-#define META_INTC_CHIP_FLAGS 0
-#endif
-
-/* public edge/level irq chips which SoCs can override */
-
-struct irq_chip meta_intc_edge_chip = {
- .irq_startup = meta_intc_startup_irq,
- .irq_shutdown = meta_intc_shutdown_irq,
- .irq_ack = meta_intc_ack_irq,
- .irq_mask = meta_intc_mask_irq,
- .irq_unmask = meta_intc_unmask_irq,
- .irq_set_type = meta_intc_irq_set_type,
- .irq_set_affinity = meta_intc_set_affinity,
- .flags = META_INTC_CHIP_FLAGS,
-};
-
-struct irq_chip meta_intc_level_chip = {
- .irq_startup = meta_intc_startup_irq,
- .irq_shutdown = meta_intc_shutdown_irq,
- .irq_set_type = meta_intc_irq_set_type,
- .irq_mask = meta_intc_mask_irq,
- .irq_unmask = meta_intc_unmask_irq,
- .irq_set_affinity = meta_intc_set_affinity,
- .flags = META_INTC_CHIP_FLAGS,
-};
-
-/**
- * meta_intc_map() - map an external irq
- * @d: irq domain of external trigger block
- * @irq: virtual irq number
- * @hw: hardware irq number within external trigger block
- *
- * This sets up a virtual irq for a specified hardware interrupt. The irq chip
- * and handler is configured, using the HWLEVELEXT registers to determine
- * edge/level flow type. These registers will have been set when the irq type is
- * set (or set to a default at init time).
- */
-static int meta_intc_map(struct irq_domain *d, unsigned int irq,
- irq_hw_number_t hw)
-{
- unsigned int bit = 1 << meta_intc_offset(hw);
- void __iomem *level_addr = meta_intc_level_addr(hw);
-
- /* Go by the current sense in the HWLEVELEXT register */
- if (metag_in32(level_addr) & bit)
- irq_set_chip_and_handler(irq, &meta_intc_level_chip,
- handle_level_irq);
- else
- irq_set_chip_and_handler(irq, &meta_intc_edge_chip,
- handle_edge_irq);
-
- irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(irq)));
- return 0;
-}
-
-static const struct irq_domain_ops meta_intc_domain_ops = {
- .map = meta_intc_map,
- .xlate = irq_domain_xlate_twocell,
-};
-
-#ifdef CONFIG_METAG_SUSPEND_MEM
-
-/**
- * struct meta_intc_context - suspend context
- * @levels: State of HWLEVELEXT registers
- * @masks: State of HWMASKEXT registers
- * @vectors: State of HWVECEXT registers
- * @txvecint: State of TxVECINT registers
- *
- * This structure stores the IRQ state across suspend.
- */
-struct meta_intc_context {
- u32 levels[4];
- u32 masks[4];
- u8 vectors[4*32];
-
- u8 txvecint[4][4];
-};
-
-/* suspend context */
-static struct meta_intc_context *meta_intc_context;
-
-/**
- * meta_intc_suspend() - store irq state
- *
- * To avoid interfering with other threads we only save the IRQ state of IRQs in
- * use by Linux.
- */
-static int meta_intc_suspend(void)
-{
- struct meta_intc_priv *priv = &meta_intc_priv;
- int i, j;
- irq_hw_number_t hw;
- unsigned int bank;
- unsigned long flags;
- struct meta_intc_context *context;
- void __iomem *level_addr, *mask_addr, *vec_addr;
- u32 mask, bit;
-
- context = kzalloc(sizeof(*context), GFP_ATOMIC);
- if (!context)
- return -ENOMEM;
-
- hw = 0;
- level_addr = meta_intc_level_addr(0);
- mask_addr = meta_intc_mask_addr(0);
- for (bank = 0; bank < priv->nr_banks; ++bank) {
- vec_addr = meta_intc_vec_addr(hw);
-
- /* create mask of interrupts in use */
- mask = 0;
- for (bit = 1; bit; bit <<= 1) {
- i = irq_linear_revmap(priv->domain, hw);
- /* save mapped irqs which are enabled or have actions */
- if (i && (!irqd_irq_disabled(irq_get_irq_data(i)) ||
- irq_has_action(i))) {
- mask |= bit;
-
- /* save trigger vector */
- context->vectors[hw] = metag_in32(vec_addr);
- }
-
- ++hw;
- vec_addr += HWVECnEXT_STRIDE;
- }
-
- /* save level state if any IRQ levels altered */
- if (priv->levels_altered[bank])
- context->levels[bank] = metag_in32(level_addr);
- /* save mask state if any IRQs in use */
- if (mask)
- context->masks[bank] = metag_in32(mask_addr);
-
- level_addr += HWSTAT_STRIDE;
- mask_addr += HWSTAT_STRIDE;
- }
-
- /* save trigger matrixing */
- __global_lock2(flags);
- for (i = 0; i < 4; ++i)
- for (j = 0; j < 4; ++j)
- context->txvecint[i][j] = metag_in32(T0VECINT_BHALT +
- TnVECINT_STRIDE*i +
- 8*j);
- __global_unlock2(flags);
-
- meta_intc_context = context;
- return 0;
-}
-
-/**
- * meta_intc_resume() - restore saved irq state
- *
- * Restore the saved IRQ state and drop it.
- */
-static void meta_intc_resume(void)
-{
- struct meta_intc_priv *priv = &meta_intc_priv;
- int i, j;
- irq_hw_number_t hw;
- unsigned int bank;
- unsigned long flags;
- struct meta_intc_context *context = meta_intc_context;
- void __iomem *level_addr, *mask_addr, *vec_addr;
- u32 mask, bit, tmp;
-
- meta_intc_context = NULL;
-
- hw = 0;
- level_addr = meta_intc_level_addr(0);
- mask_addr = meta_intc_mask_addr(0);
- for (bank = 0; bank < priv->nr_banks; ++bank) {
- vec_addr = meta_intc_vec_addr(hw);
-
- /* create mask of interrupts in use */
- mask = 0;
- for (bit = 1; bit; bit <<= 1) {
- i = irq_linear_revmap(priv->domain, hw);
- /* restore mapped irqs, enabled or with actions */
- if (i && (!irqd_irq_disabled(irq_get_irq_data(i)) ||
- irq_has_action(i))) {
- mask |= bit;
-
- /* restore trigger vector */
- metag_out32(context->vectors[hw], vec_addr);
- }
-
- ++hw;
- vec_addr += HWVECnEXT_STRIDE;
- }
-
- if (mask) {
- /* restore mask state */
- __global_lock2(flags);
- tmp = metag_in32(mask_addr);
- tmp = (tmp & ~mask) | (context->masks[bank] & mask);
- metag_out32(tmp, mask_addr);
- __global_unlock2(flags);
- }
-
- mask = priv->levels_altered[bank];
- if (mask) {
- /* restore level state */
- __global_lock2(flags);
- tmp = metag_in32(level_addr);
- tmp = (tmp & ~mask) | (context->levels[bank] & mask);
- metag_out32(tmp, level_addr);
- __global_unlock2(flags);
- }
-
- level_addr += HWSTAT_STRIDE;
- mask_addr += HWSTAT_STRIDE;
- }
-
- /* restore trigger matrixing */
- __global_lock2(flags);
- for (i = 0; i < 4; ++i) {
- for (j = 0; j < 4; ++j) {
- metag_out32(context->txvecint[i][j],
- T0VECINT_BHALT +
- TnVECINT_STRIDE*i +
- 8*j);
- }
- }
- __global_unlock2(flags);
-
- kfree(context);
-}
-
-static struct syscore_ops meta_intc_syscore_ops = {
- .suspend = meta_intc_suspend,
- .resume = meta_intc_resume,
-};
-
-static void __init meta_intc_init_syscore_ops(struct meta_intc_priv *priv)
-{
- register_syscore_ops(&meta_intc_syscore_ops);
-}
-#else
-#define meta_intc_init_syscore_ops(priv) do {} while (0)
-#endif
-
-/**
- * meta_intc_init_cpu() - register with a Meta cpu
- * @priv: private interrupt controller data
- * @cpu: the CPU to register on
- *
- * Configure @cpu's TR2 irq so that we can demux external irqs.
- */
-static void __init meta_intc_init_cpu(struct meta_intc_priv *priv, int cpu)
-{
- unsigned int thread = cpu_2_hwthread_id[cpu];
- unsigned int signum = TBID_SIGNUM_TR2(thread);
- int irq = tbisig_map(signum);
-
- /* Register the multiplexed IRQ handler */
- irq_set_chained_handler(irq, meta_intc_irq_demux);
- irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
-}
-
-/**
- * meta_intc_no_mask() - indicate lack of HWMASKEXT registers
- *
- * Called from SoC code (or init code below) to dynamically indicate the lack of
- * HWMASKEXT registers (for example depending on some SoC revision register).
- * This alters the irq mask and unmask callbacks to use the fallback
- * unvectoring/retriggering technique instead of using HWMASKEXT registers.
- */
-void __init meta_intc_no_mask(void)
-{
- meta_intc_edge_chip.irq_mask = meta_intc_mask_irq_nomask;
- meta_intc_edge_chip.irq_unmask = meta_intc_unmask_edge_irq_nomask;
- meta_intc_level_chip.irq_mask = meta_intc_mask_irq_nomask;
- meta_intc_level_chip.irq_unmask = meta_intc_unmask_level_irq_nomask;
-}
-
-/**
- * init_external_IRQ() - initialise the external irq controller
- *
- * Set up the external irq controller using device tree properties. This is
- * called from init_IRQ().
- */
-int __init init_external_IRQ(void)
-{
- struct meta_intc_priv *priv = &meta_intc_priv;
- struct device_node *node;
- int ret, cpu;
- u32 val;
- bool no_masks = false;
-
- node = of_find_compatible_node(NULL, NULL, "img,meta-intc");
- if (!node)
- return -ENOENT;
-
- /* Get number of banks */
- ret = of_property_read_u32(node, "num-banks", &val);
- if (ret) {
- pr_err("meta-intc: No num-banks property found\n");
- return ret;
- }
- if (val < 1 || val > 4) {
- pr_err("meta-intc: num-banks (%u) out of range\n", val);
- return -EINVAL;
- }
- priv->nr_banks = val;
-
- /* Are any mask registers present? */
- if (of_get_property(node, "no-mask", NULL))
- no_masks = true;
-
- /* No HWMASKEXT registers present? */
- if (no_masks)
- meta_intc_no_mask();
-
- /* Set up an IRQ domain */
- /*
- * This is a legacy IRQ domain for now until all the platform setup code
- * has been converted to devicetree.
- */
- priv->domain = irq_domain_add_linear(node, priv->nr_banks*32,
- &meta_intc_domain_ops, priv);
- if (unlikely(!priv->domain)) {
- pr_err("meta-intc: cannot add IRQ domain\n");
- return -ENOMEM;
- }
-
- /* Setup TR2 for all cpus. */
- for_each_possible_cpu(cpu)
- meta_intc_init_cpu(priv, cpu);
-
- /* Set up system suspend/resume callbacks */
- meta_intc_init_syscore_ops(priv);
-
- pr_info("meta-intc: External IRQ controller initialised (%u IRQs)\n",
- priv->nr_banks*32);
-
- return 0;
-}
diff --git a/drivers/irqchip/irq-metag.c b/drivers/irqchip/irq-metag.c
deleted file mode 100644
index 857b946747eb..000000000000
--- a/drivers/irqchip/irq-metag.c
+++ /dev/null
@@ -1,343 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Meta internal (HWSTATMETA) interrupt code.
- *
- * Copyright (C) 2011-2012 Imagination Technologies Ltd.
- *
- * This code is based on the code in SoC/common/irq.c and SoC/comet/irq.c
- * The code base could be generalised/merged as a lot of the functionality is
- * similar. Until this is done, we try to keep the code simple here.
- */
-
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/irqdomain.h>
-
-#include <asm/irq.h>
-#include <asm/hwthread.h>
-
-#define PERF0VECINT 0x04820580
-#define PERF1VECINT 0x04820588
-#define PERF0TRIG_OFFSET 16
-#define PERF1TRIG_OFFSET 17
-
-/**
- * struct metag_internal_irq_priv - private meta internal interrupt data
- * @domain: IRQ domain for all internal Meta IRQs (HWSTATMETA)
- * @unmasked: Record of unmasked IRQs
- */
-struct metag_internal_irq_priv {
- struct irq_domain *domain;
-
- unsigned long unmasked;
-};
-
-/* Private data for the one and only internal interrupt controller */
-static struct metag_internal_irq_priv metag_internal_irq_priv;
-
-static unsigned int metag_internal_irq_startup(struct irq_data *data);
-static void metag_internal_irq_shutdown(struct irq_data *data);
-static void metag_internal_irq_ack(struct irq_data *data);
-static void metag_internal_irq_mask(struct irq_data *data);
-static void metag_internal_irq_unmask(struct irq_data *data);
-#ifdef CONFIG_SMP
-static int metag_internal_irq_set_affinity(struct irq_data *data,
- const struct cpumask *cpumask, bool force);
-#endif
-
-static struct irq_chip internal_irq_edge_chip = {
- .name = "HWSTATMETA-IRQ",
- .irq_startup = metag_internal_irq_startup,
- .irq_shutdown = metag_internal_irq_shutdown,
- .irq_ack = metag_internal_irq_ack,
- .irq_mask = metag_internal_irq_mask,
- .irq_unmask = metag_internal_irq_unmask,
-#ifdef CONFIG_SMP
- .irq_set_affinity = metag_internal_irq_set_affinity,
-#endif
-};
-
-/*
- * metag_hwvec_addr - get the address of *VECINT regs of irq
- *
- * This function is a table of supported triggers on HWSTATMETA
- * Could do with a structure, but better keep it simple. Changes
- * in this code should be rare.
- */
-static inline void __iomem *metag_hwvec_addr(irq_hw_number_t hw)
-{
- void __iomem *addr;
-
- switch (hw) {
- case PERF0TRIG_OFFSET:
- addr = (void __iomem *)PERF0VECINT;
- break;
- case PERF1TRIG_OFFSET:
- addr = (void __iomem *)PERF1VECINT;
- break;
- default:
- addr = NULL;
- break;
- }
- return addr;
-}
-
-/*
- * metag_internal_startup - setup an internal irq
- * @irq: the irq to startup
- *
- * Multiplex interrupts for @irq onto TR1. Clear any pending
- * interrupts.
- */
-static unsigned int metag_internal_irq_startup(struct irq_data *data)
-{
- /* Clear (toggle) the bit in HWSTATMETA for our interrupt. */
- metag_internal_irq_ack(data);
-
- /* Enable the interrupt by unmasking it */
- metag_internal_irq_unmask(data);
-
- return 0;
-}
-
-/*
- * metag_internal_irq_shutdown - turn off the irq
- * @irq: the irq number to turn off
- *
- * Mask @irq and clear any pending interrupts.
- * Stop muxing @irq onto TR1.
- */
-static void metag_internal_irq_shutdown(struct irq_data *data)
-{
- /* Disable the IRQ at the core by masking it. */
- metag_internal_irq_mask(data);
-
- /* Clear (toggle) the bit in HWSTATMETA for our interrupt. */
- metag_internal_irq_ack(data);
-}
-
-/*
- * metag_internal_irq_ack - acknowledge irq
- * @irq: the irq to ack
- */
-static void metag_internal_irq_ack(struct irq_data *data)
-{
- irq_hw_number_t hw = data->hwirq;
- unsigned int bit = 1 << hw;
-
- if (metag_in32(HWSTATMETA) & bit)
- metag_out32(bit, HWSTATMETA);
-}
-
-/**
- * metag_internal_irq_mask() - mask an internal irq by unvectoring
- * @data: data for the internal irq to mask
- *
- * HWSTATMETA has no mask register. Instead the IRQ is unvectored from the core
- * and retriggered if necessary later.
- */
-static void metag_internal_irq_mask(struct irq_data *data)
-{
- struct metag_internal_irq_priv *priv = &metag_internal_irq_priv;
- irq_hw_number_t hw = data->hwirq;
- void __iomem *vec_addr = metag_hwvec_addr(hw);
-
- clear_bit(hw, &priv->unmasked);
-
- /* there is no interrupt mask, so unvector the interrupt */
- metag_out32(0, vec_addr);
-}
-
-/**
- * meta_intc_unmask_edge_irq_nomask() - unmask an edge irq by revectoring
- * @data: data for the internal irq to unmask
- *
- * HWSTATMETA has no mask register. Instead the IRQ is revectored back to the
- * core and retriggered if necessary.
- */
-static void metag_internal_irq_unmask(struct irq_data *data)
-{
- struct metag_internal_irq_priv *priv = &metag_internal_irq_priv;
- irq_hw_number_t hw = data->hwirq;
- unsigned int bit = 1 << hw;
- void __iomem *vec_addr = metag_hwvec_addr(hw);
- unsigned int thread = hard_processor_id();
-
- set_bit(hw, &priv->unmasked);
-
- /* there is no interrupt mask, so revector the interrupt */
- metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR1(thread)), vec_addr);
-
- /*
- * Re-trigger interrupt
- *
- * Writing a 1 toggles, and a 0->1 transition triggers. We only
- * retrigger if the status bit is already set, which means we
- * need to clear it first. Retriggering is fundamentally racy
- * because if the interrupt fires again after we clear it we
- * could end up clearing it again and the interrupt handler
- * thinking it hasn't fired. Therefore we need to keep trying to
- * retrigger until the bit is set.
- */
- if (metag_in32(HWSTATMETA) & bit) {
- metag_out32(bit, HWSTATMETA);
- while (!(metag_in32(HWSTATMETA) & bit))
- metag_out32(bit, HWSTATMETA);
- }
-}
-
-#ifdef CONFIG_SMP
-/*
- * metag_internal_irq_set_affinity - set the affinity for an interrupt
- */
-static int metag_internal_irq_set_affinity(struct irq_data *data,
- const struct cpumask *cpumask, bool force)
-{
- unsigned int cpu, thread;
- irq_hw_number_t hw = data->hwirq;
- /*
- * Wire up this interrupt from *VECINT to the Meta core.
- *
- * Note that we can't wire up *VECINT to interrupt more than
- * one cpu (the interrupt code doesn't support it), so we just
- * pick the first cpu we find in 'cpumask'.
- */
- cpu = cpumask_any_and(cpumask, cpu_online_mask);
- thread = cpu_2_hwthread_id[cpu];
-
- metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR1(thread)),
- metag_hwvec_addr(hw));
-
- return 0;
-}
-#endif
-
-/*
- * metag_internal_irq_demux - irq de-multiplexer
- * @irq: the interrupt number
- * @desc: the interrupt description structure for this irq
- *
- * The cpu receives an interrupt on TR1 when an interrupt has
- * occurred. It is this function's job to demux this irq and
- * figure out exactly which trigger needs servicing.
- */
-static void metag_internal_irq_demux(struct irq_desc *desc)
-{
- struct metag_internal_irq_priv *priv = irq_desc_get_handler_data(desc);
- irq_hw_number_t hw;
- unsigned int irq_no;
- u32 status;
-
-recalculate:
- status = metag_in32(HWSTATMETA) & priv->unmasked;
-
- for (hw = 0; status != 0; status >>= 1, ++hw) {
- if (status & 0x1) {
- /*
- * Map the hardware IRQ number to a virtual Linux IRQ
- * number.
- */
- irq_no = irq_linear_revmap(priv->domain, hw);
-
- /*
- * Only fire off interrupts that are
- * registered to be handled by the kernel.
- * Other interrupts are probably being
- * handled by other Meta hardware threads.
- */
- generic_handle_irq(irq_no);
-
- /*
- * The handler may have re-enabled interrupts
- * which could have caused a nested invocation
- * of this code and make the copy of the
- * status register we are using invalid.
- */
- goto recalculate;
- }
- }
-}
-
-/**
- * internal_irq_map() - Map an internal meta IRQ to a virtual IRQ number.
- * @hw: Number of the internal IRQ. Must be in range.
- *
- * Returns: The virtual IRQ number of the Meta internal IRQ specified by
- * @hw.
- */
-int internal_irq_map(unsigned int hw)
-{
- struct metag_internal_irq_priv *priv = &metag_internal_irq_priv;
- if (!priv->domain)
- return -ENODEV;
- return irq_create_mapping(priv->domain, hw);
-}
-
-/**
- * metag_internal_irq_init_cpu - regsister with the Meta cpu
- * @cpu: the CPU to register on
- *
- * Configure @cpu's TR1 irq so that we can demux irqs.
- */
-static void metag_internal_irq_init_cpu(struct metag_internal_irq_priv *priv,
- int cpu)
-{
- unsigned int thread = cpu_2_hwthread_id[cpu];
- unsigned int signum = TBID_SIGNUM_TR1(thread);
- int irq = tbisig_map(signum);
-
- /* Register the multiplexed IRQ handler */
- irq_set_chained_handler_and_data(irq, metag_internal_irq_demux, priv);
- irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
-}
-
-/**
- * metag_internal_intc_map() - map an internal irq
- * @d: irq domain of internal trigger block
- * @irq: virtual irq number
- * @hw: hardware irq number within internal trigger block
- *
- * This sets up a virtual irq for a specified hardware interrupt. The irq chip
- * and handler is configured.
- */
-static int metag_internal_intc_map(struct irq_domain *d, unsigned int irq,
- irq_hw_number_t hw)
-{
- /* only register interrupt if it is mapped */
- if (!metag_hwvec_addr(hw))
- return -EINVAL;
-
- irq_set_chip_and_handler(irq, &internal_irq_edge_chip,
- handle_edge_irq);
- return 0;
-}
-
-static const struct irq_domain_ops metag_internal_intc_domain_ops = {
- .map = metag_internal_intc_map,
-};
-
-/**
- * metag_internal_irq_register - register internal IRQs
- *
- * Register the irq chip and handler function for all internal IRQs
- */
-int __init init_internal_IRQ(void)
-{
- struct metag_internal_irq_priv *priv = &metag_internal_irq_priv;
- unsigned int cpu;
-
- /* Set up an IRQ domain */
- priv->domain = irq_domain_add_linear(NULL, 32,
- &metag_internal_intc_domain_ops,
- priv);
- if (unlikely(!priv->domain)) {
- pr_err("meta-internal-intc: cannot add IRQ domain\n");
- return -ENOMEM;
- }
-
- /* Setup TR1 for all cpus. */
- for_each_possible_cpu(cpu)
- metag_internal_irq_init_cpu(priv, cpu);
-
- return 0;
-};
diff --git a/drivers/irqchip/irq-mscc-ocelot.c b/drivers/irqchip/irq-mscc-ocelot.c
new file mode 100644
index 000000000000..b63e40c00a02
--- /dev/null
+++ b/drivers/irqchip/irq-mscc-ocelot.c
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Microsemi Ocelot IRQ controller driver
+ *
+ * Copyright (c) 2017 Microsemi Corporation
+ */
+#include <linux/bitops.h>
+#include <linux/irq.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/interrupt.h>
+
+#define ICPU_CFG_INTR_INTR_STICKY 0x10
+#define ICPU_CFG_INTR_INTR_ENA 0x18
+#define ICPU_CFG_INTR_INTR_ENA_CLR 0x1c
+#define ICPU_CFG_INTR_INTR_ENA_SET 0x20
+#define ICPU_CFG_INTR_DST_INTR_IDENT(x) (0x38 + 0x4 * (x))
+#define ICPU_CFG_INTR_INTR_TRIGGER(x) (0x5c + 0x4 * (x))
+
+#define OCELOT_NR_IRQ 24
+
+static void ocelot_irq_unmask(struct irq_data *data)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
+ struct irq_chip_type *ct = irq_data_get_chip_type(data);
+ unsigned int mask = data->mask;
+ u32 val;
+
+ irq_gc_lock(gc);
+ val = irq_reg_readl(gc, ICPU_CFG_INTR_INTR_TRIGGER(0)) |
+ irq_reg_readl(gc, ICPU_CFG_INTR_INTR_TRIGGER(1));
+ if (!(val & mask))
+ irq_reg_writel(gc, mask, ICPU_CFG_INTR_INTR_STICKY);
+
+ *ct->mask_cache &= ~mask;
+ irq_reg_writel(gc, mask, ICPU_CFG_INTR_INTR_ENA_SET);
+ irq_gc_unlock(gc);
+}
+
+static void ocelot_irq_handler(struct irq_desc *desc)
+{
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct irq_domain *d = irq_desc_get_handler_data(desc);
+ struct irq_chip_generic *gc = irq_get_domain_generic_chip(d, 0);
+ u32 reg = irq_reg_readl(gc, ICPU_CFG_INTR_DST_INTR_IDENT(0));
+
+ chained_irq_enter(chip, desc);
+
+ while (reg) {
+ u32 hwirq = __fls(reg);
+
+ generic_handle_irq(irq_find_mapping(d, hwirq));
+ reg &= ~(BIT(hwirq));
+ }
+
+ chained_irq_exit(chip, desc);
+}
+
+static int __init ocelot_irq_init(struct device_node *node,
+ struct device_node *parent)
+{
+ struct irq_domain *domain;
+ struct irq_chip_generic *gc;
+ int parent_irq, ret;
+
+ parent_irq = irq_of_parse_and_map(node, 0);
+ if (!parent_irq)
+ return -EINVAL;
+
+ domain = irq_domain_add_linear(node, OCELOT_NR_IRQ,
+ &irq_generic_chip_ops, NULL);
+ if (!domain) {
+ pr_err("%s: unable to add irq domain\n", node->name);
+ return -ENOMEM;
+ }
+
+ ret = irq_alloc_domain_generic_chips(domain, OCELOT_NR_IRQ, 1,
+ "icpu", handle_level_irq,
+ 0, 0, 0);
+ if (ret) {
+ pr_err("%s: unable to alloc irq domain gc\n", node->name);
+ goto err_domain_remove;
+ }
+
+ gc = irq_get_domain_generic_chip(domain, 0);
+ gc->reg_base = of_iomap(node, 0);
+ if (!gc->reg_base) {
+ pr_err("%s: unable to map resource\n", node->name);
+ ret = -ENOMEM;
+ goto err_gc_free;
+ }
+
+ gc->chip_types[0].regs.ack = ICPU_CFG_INTR_INTR_STICKY;
+ gc->chip_types[0].regs.mask = ICPU_CFG_INTR_INTR_ENA_CLR;
+ gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
+ gc->chip_types[0].chip.irq_mask = irq_gc_mask_set_bit;
+ gc->chip_types[0].chip.irq_unmask = ocelot_irq_unmask;
+
+ /* Mask and ack all interrupts */
+ irq_reg_writel(gc, 0, ICPU_CFG_INTR_INTR_ENA);
+ irq_reg_writel(gc, 0xffffffff, ICPU_CFG_INTR_INTR_STICKY);
+
+ irq_set_chained_handler_and_data(parent_irq, ocelot_irq_handler,
+ domain);
+
+ return 0;
+
+err_gc_free:
+ irq_free_generic_chip(gc);
+
+err_domain_remove:
+ irq_domain_remove(domain);
+
+ return ret;
+}
+IRQCHIP_DECLARE(ocelot_icpu, "mscc,ocelot-icpu-intr", ocelot_irq_init);
diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c
index cee59fe1321c..c6e6c9e9137a 100644
--- a/drivers/irqchip/irq-renesas-intc-irqpin.c
+++ b/drivers/irqchip/irq-renesas-intc-irqpin.c
@@ -17,7 +17,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <linux/clk.h>
#include <linux/init.h>
#include <linux/of.h>
#include <linux/platform_device.h>
@@ -78,16 +77,14 @@ struct intc_irqpin_priv {
struct platform_device *pdev;
struct irq_chip irq_chip;
struct irq_domain *irq_domain;
- struct clk *clk;
+ atomic_t wakeup_path;
unsigned shared_irqs:1;
- unsigned needs_clk:1;
u8 shared_irq_mask;
};
struct intc_irqpin_config {
unsigned int irlm_bit;
unsigned needs_irlm:1;
- unsigned needs_clk:1;
};
static unsigned long intc_irqpin_read32(void __iomem *iomem)
@@ -287,14 +284,10 @@ static int intc_irqpin_irq_set_wake(struct irq_data *d, unsigned int on)
int hw_irq = irqd_to_hwirq(d);
irq_set_irq_wake(p->irq[hw_irq].requested_irq, on);
-
- if (!p->clk)
- return 0;
-
if (on)
- clk_enable(p->clk);
+ atomic_inc(&p->wakeup_path);
else
- clk_disable(p->clk);
+ atomic_dec(&p->wakeup_path);
return 0;
}
@@ -369,12 +362,10 @@ static const struct irq_domain_ops intc_irqpin_irq_domain_ops = {
static const struct intc_irqpin_config intc_irqpin_irlm_r8a777x = {
.irlm_bit = 23, /* ICR0.IRLM0 */
.needs_irlm = 1,
- .needs_clk = 0,
};
static const struct intc_irqpin_config intc_irqpin_rmobile = {
.needs_irlm = 0,
- .needs_clk = 1,
};
static const struct of_device_id intc_irqpin_dt_ids[] = {
@@ -426,18 +417,6 @@ static int intc_irqpin_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, p);
config = of_device_get_match_data(dev);
- if (config)
- p->needs_clk = config->needs_clk;
-
- p->clk = devm_clk_get(dev, NULL);
- if (IS_ERR(p->clk)) {
- if (p->needs_clk) {
- dev_err(dev, "unable to get clock\n");
- ret = PTR_ERR(p->clk);
- goto err0;
- }
- p->clk = NULL;
- }
pm_runtime_enable(dev);
pm_runtime_get_sync(dev);
@@ -606,12 +585,25 @@ static int intc_irqpin_remove(struct platform_device *pdev)
return 0;
}
+static int __maybe_unused intc_irqpin_suspend(struct device *dev)
+{
+ struct intc_irqpin_priv *p = dev_get_drvdata(dev);
+
+ if (atomic_read(&p->wakeup_path))
+ device_set_wakeup_path(dev);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(intc_irqpin_pm_ops, intc_irqpin_suspend, NULL);
+
static struct platform_driver intc_irqpin_device_driver = {
.probe = intc_irqpin_probe,
.remove = intc_irqpin_remove,
.driver = {
.name = "renesas_intc_irqpin",
.of_match_table = intc_irqpin_dt_ids,
+ .pm = &intc_irqpin_pm_ops,
}
};
diff --git a/drivers/irqchip/irq-renesas-irqc.c b/drivers/irqchip/irq-renesas-irqc.c
index 52304b139aa4..a4f11124024d 100644
--- a/drivers/irqchip/irq-renesas-irqc.c
+++ b/drivers/irqchip/irq-renesas-irqc.c
@@ -17,7 +17,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <linux/clk.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
@@ -64,7 +63,7 @@ struct irqc_priv {
struct platform_device *pdev;
struct irq_chip_generic *gc;
struct irq_domain *irq_domain;
- struct clk *clk;
+ atomic_t wakeup_path;
};
static struct irqc_priv *irq_data_to_priv(struct irq_data *data)
@@ -111,14 +110,10 @@ static int irqc_irq_set_wake(struct irq_data *d, unsigned int on)
int hw_irq = irqd_to_hwirq(d);
irq_set_irq_wake(p->irq[hw_irq].requested_irq, on);
-
- if (!p->clk)
- return 0;
-
if (on)
- clk_enable(p->clk);
+ atomic_inc(&p->wakeup_path);
else
- clk_disable(p->clk);
+ atomic_dec(&p->wakeup_path);
return 0;
}
@@ -159,12 +154,6 @@ static int irqc_probe(struct platform_device *pdev)
p->pdev = pdev;
platform_set_drvdata(pdev, p);
- p->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(p->clk)) {
- dev_warn(&pdev->dev, "unable to get clock\n");
- p->clk = NULL;
- }
-
pm_runtime_enable(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);
@@ -276,6 +265,18 @@ static int irqc_remove(struct platform_device *pdev)
return 0;
}
+static int __maybe_unused irqc_suspend(struct device *dev)
+{
+ struct irqc_priv *p = dev_get_drvdata(dev);
+
+ if (atomic_read(&p->wakeup_path))
+ device_set_wakeup_path(dev);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(irqc_pm_ops, irqc_suspend, NULL);
+
static const struct of_device_id irqc_dt_ids[] = {
{ .compatible = "renesas,irqc", },
{},
@@ -288,6 +289,7 @@ static struct platform_driver irqc_device_driver = {
.driver = {
.name = "renesas_irqc",
.of_match_table = irqc_dt_ids,
+ .pm = &irqc_pm_ops,
}
};
diff --git a/drivers/irqchip/qcom-pdc.c b/drivers/irqchip/qcom-pdc.c
new file mode 100644
index 000000000000..b1b47a40a278
--- /dev/null
+++ b/drivers/irqchip/qcom-pdc.c
@@ -0,0 +1,311 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#define PDC_MAX_IRQS 126
+
+#define CLEAR_INTR(reg, intr) (reg & ~(1 << intr))
+#define ENABLE_INTR(reg, intr) (reg | (1 << intr))
+
+#define IRQ_ENABLE_BANK 0x10
+#define IRQ_i_CFG 0x110
+
+struct pdc_pin_region {
+ u32 pin_base;
+ u32 parent_base;
+ u32 cnt;
+};
+
+static DEFINE_RAW_SPINLOCK(pdc_lock);
+static void __iomem *pdc_base;
+static struct pdc_pin_region *pdc_region;
+static int pdc_region_cnt;
+
+static void pdc_reg_write(int reg, u32 i, u32 val)
+{
+ writel_relaxed(val, pdc_base + reg + i * sizeof(u32));
+}
+
+static u32 pdc_reg_read(int reg, u32 i)
+{
+ return readl_relaxed(pdc_base + reg + i * sizeof(u32));
+}
+
+static void pdc_enable_intr(struct irq_data *d, bool on)
+{
+ int pin_out = d->hwirq;
+ u32 index, mask;
+ u32 enable;
+
+ index = pin_out / 32;
+ mask = pin_out % 32;
+
+ raw_spin_lock(&pdc_lock);
+ enable = pdc_reg_read(IRQ_ENABLE_BANK, index);
+ enable = on ? ENABLE_INTR(enable, mask) : CLEAR_INTR(enable, mask);
+ pdc_reg_write(IRQ_ENABLE_BANK, index, enable);
+ raw_spin_unlock(&pdc_lock);
+}
+
+static void qcom_pdc_gic_mask(struct irq_data *d)
+{
+ pdc_enable_intr(d, false);
+ irq_chip_mask_parent(d);
+}
+
+static void qcom_pdc_gic_unmask(struct irq_data *d)
+{
+ pdc_enable_intr(d, true);
+ irq_chip_unmask_parent(d);
+}
+
+/*
+ * GIC does not handle falling edge or active low. To allow falling edge and
+ * active low interrupts to be handled at GIC, PDC has an inverter that inverts
+ * falling edge into a rising edge and active low into an active high.
+ * For the inverter to work, the polarity bit in the IRQ_CONFIG register has to
+ * set as per the table below.
+ * Level sensitive active low LOW
+ * Rising edge sensitive NOT USED
+ * Falling edge sensitive LOW
+ * Dual Edge sensitive NOT USED
+ * Level sensitive active High HIGH
+ * Falling Edge sensitive NOT USED
+ * Rising edge sensitive HIGH
+ * Dual Edge sensitive HIGH
+ */
+enum pdc_irq_config_bits {
+ PDC_LEVEL_LOW = 0b000,
+ PDC_EDGE_FALLING = 0b010,
+ PDC_LEVEL_HIGH = 0b100,
+ PDC_EDGE_RISING = 0b110,
+ PDC_EDGE_DUAL = 0b111,
+};
+
+/**
+ * qcom_pdc_gic_set_type: Configure PDC for the interrupt
+ *
+ * @d: the interrupt data
+ * @type: the interrupt type
+ *
+ * If @type is edge triggered, forward that as Rising edge as PDC
+ * takes care of converting falling edge to rising edge signal
+ * If @type is level, then forward that as level high as PDC
+ * takes care of converting falling edge to rising edge signal
+ */
+static int qcom_pdc_gic_set_type(struct irq_data *d, unsigned int type)
+{
+ int pin_out = d->hwirq;
+ enum pdc_irq_config_bits pdc_type;
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ pdc_type = PDC_EDGE_RISING;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ pdc_type = PDC_EDGE_FALLING;
+ type = IRQ_TYPE_EDGE_RISING;
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ pdc_type = PDC_EDGE_DUAL;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ pdc_type = PDC_LEVEL_HIGH;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ pdc_type = PDC_LEVEL_LOW;
+ type = IRQ_TYPE_LEVEL_HIGH;
+ break;
+ default:
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ pdc_reg_write(IRQ_i_CFG, pin_out, pdc_type);
+
+ return irq_chip_set_type_parent(d, type);
+}
+
+static struct irq_chip qcom_pdc_gic_chip = {
+ .name = "PDC",
+ .irq_eoi = irq_chip_eoi_parent,
+ .irq_mask = qcom_pdc_gic_mask,
+ .irq_unmask = qcom_pdc_gic_unmask,
+ .irq_retrigger = irq_chip_retrigger_hierarchy,
+ .irq_set_type = qcom_pdc_gic_set_type,
+ .flags = IRQCHIP_MASK_ON_SUSPEND |
+ IRQCHIP_SET_TYPE_MASKED |
+ IRQCHIP_SKIP_SET_WAKE,
+ .irq_set_vcpu_affinity = irq_chip_set_vcpu_affinity_parent,
+ .irq_set_affinity = irq_chip_set_affinity_parent,
+};
+
+static irq_hw_number_t get_parent_hwirq(int pin)
+{
+ int i;
+ struct pdc_pin_region *region;
+
+ for (i = 0; i < pdc_region_cnt; i++) {
+ region = &pdc_region[i];
+ if (pin >= region->pin_base &&
+ pin < region->pin_base + region->cnt)
+ return (region->parent_base + pin - region->pin_base);
+ }
+
+ WARN_ON(1);
+ return ~0UL;
+}
+
+static int qcom_pdc_translate(struct irq_domain *d, struct irq_fwspec *fwspec,
+ unsigned long *hwirq, unsigned int *type)
+{
+ if (is_of_node(fwspec->fwnode)) {
+ if (fwspec->param_count != 2)
+ return -EINVAL;
+
+ *hwirq = fwspec->param[0];
+ *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int qcom_pdc_alloc(struct irq_domain *domain, unsigned int virq,
+ unsigned int nr_irqs, void *data)
+{
+ struct irq_fwspec *fwspec = data;
+ struct irq_fwspec parent_fwspec;
+ irq_hw_number_t hwirq, parent_hwirq;
+ unsigned int type;
+ int ret;
+
+ ret = qcom_pdc_translate(domain, fwspec, &hwirq, &type);
+ if (ret)
+ return -EINVAL;
+
+ parent_hwirq = get_parent_hwirq(hwirq);
+ if (parent_hwirq == ~0UL)
+ return -EINVAL;
+
+ ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
+ &qcom_pdc_gic_chip, NULL);
+ if (ret)
+ return ret;
+
+ if (type & IRQ_TYPE_EDGE_BOTH)
+ type = IRQ_TYPE_EDGE_RISING;
+
+ if (type & IRQ_TYPE_LEVEL_MASK)
+ type = IRQ_TYPE_LEVEL_HIGH;
+
+ parent_fwspec.fwnode = domain->parent->fwnode;
+ parent_fwspec.param_count = 3;
+ parent_fwspec.param[0] = 0;
+ parent_fwspec.param[1] = parent_hwirq;
+ parent_fwspec.param[2] = type;
+
+ return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
+ &parent_fwspec);
+}
+
+static const struct irq_domain_ops qcom_pdc_ops = {
+ .translate = qcom_pdc_translate,
+ .alloc = qcom_pdc_alloc,
+ .free = irq_domain_free_irqs_common,
+};
+
+static int pdc_setup_pin_mapping(struct device_node *np)
+{
+ int ret, n;
+
+ n = of_property_count_elems_of_size(np, "qcom,pdc-ranges", sizeof(u32));
+ if (n <= 0 || n % 3)
+ return -EINVAL;
+
+ pdc_region_cnt = n / 3;
+ pdc_region = kcalloc(pdc_region_cnt, sizeof(*pdc_region), GFP_KERNEL);
+ if (!pdc_region) {
+ pdc_region_cnt = 0;
+ return -ENOMEM;
+ }
+
+ for (n = 0; n < pdc_region_cnt; n++) {
+ ret = of_property_read_u32_index(np, "qcom,pdc-ranges",
+ n * 3 + 0,
+ &pdc_region[n].pin_base);
+ if (ret)
+ return ret;
+ ret = of_property_read_u32_index(np, "qcom,pdc-ranges",
+ n * 3 + 1,
+ &pdc_region[n].parent_base);
+ if (ret)
+ return ret;
+ ret = of_property_read_u32_index(np, "qcom,pdc-ranges",
+ n * 3 + 2,
+ &pdc_region[n].cnt);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int qcom_pdc_init(struct device_node *node, struct device_node *parent)
+{
+ struct irq_domain *parent_domain, *pdc_domain;
+ int ret;
+
+ pdc_base = of_iomap(node, 0);
+ if (!pdc_base) {
+ pr_err("%pOF: unable to map PDC registers\n", node);
+ return -ENXIO;
+ }
+
+ parent_domain = irq_find_host(parent);
+ if (!parent_domain) {
+ pr_err("%pOF: unable to find PDC's parent domain\n", node);
+ ret = -ENXIO;
+ goto fail;
+ }
+
+ ret = pdc_setup_pin_mapping(node);
+ if (ret) {
+ pr_err("%pOF: failed to init PDC pin-hwirq mapping\n", node);
+ goto fail;
+ }
+
+ pdc_domain = irq_domain_create_hierarchy(parent_domain, 0, PDC_MAX_IRQS,
+ of_fwnode_handle(node),
+ &qcom_pdc_ops, NULL);
+ if (!pdc_domain) {
+ pr_err("%pOF: GIC domain add failed\n", node);
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ kfree(pdc_region);
+ iounmap(pdc_base);
+ return ret;
+}
+
+IRQCHIP_DECLARE(pdc_sdm845, "qcom,sdm845-pdc", qcom_pdc_init);
diff --git a/drivers/isdn/hisax/Kconfig b/drivers/isdn/hisax/Kconfig
index eb83d94ab4fe..38cfc8baae19 100644
--- a/drivers/isdn/hisax/Kconfig
+++ b/drivers/isdn/hisax/Kconfig
@@ -109,7 +109,7 @@ config HISAX_16_3
config HISAX_TELESPCI
bool "Teles PCI"
- depends on PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV || (XTENSA && !CPU_LITTLE_ENDIAN)))
+ depends on PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || (XTENSA && !CPU_LITTLE_ENDIAN)))
help
This enables HiSax support for the Teles PCI.
See <file:Documentation/isdn/README.HiSax> on how to configure it.
@@ -237,7 +237,7 @@ config HISAX_MIC
config HISAX_NETJET
bool "NETjet card"
- depends on PCI && (BROKEN || !(PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV || (XTENSA && !CPU_LITTLE_ENDIAN) || MICROBLAZE))
+ depends on PCI && (BROKEN || !(PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || (XTENSA && !CPU_LITTLE_ENDIAN) || MICROBLAZE))
depends on VIRT_TO_BUS
help
This enables HiSax support for the NetJet from Traverse
@@ -249,7 +249,7 @@ config HISAX_NETJET
config HISAX_NETJET_U
bool "NETspider U card"
- depends on PCI && (BROKEN || !(PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV || (XTENSA && !CPU_LITTLE_ENDIAN) || MICROBLAZE))
+ depends on PCI && (BROKEN || !(PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || (XTENSA && !CPU_LITTLE_ENDIAN) || MICROBLAZE))
depends on VIRT_TO_BUS
help
This enables HiSax support for the Netspider U interface ISDN card
@@ -318,7 +318,7 @@ config HISAX_GAZEL
config HISAX_HFC_PCI
bool "HFC PCI-Bus cards"
- depends on PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV || (XTENSA && !CPU_LITTLE_ENDIAN)))
+ depends on PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || (XTENSA && !CPU_LITTLE_ENDIAN)))
help
This enables HiSax support for the HFC-S PCI 2BDS0 based cards.
@@ -343,7 +343,7 @@ config HISAX_HFC_SX
config HISAX_ENTERNOW_PCI
bool "Formula-n enter:now PCI card"
- depends on HISAX_NETJET && PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV || (XTENSA && !CPU_LITTLE_ENDIAN)))
+ depends on HISAX_NETJET && PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || (XTENSA && !CPU_LITTLE_ENDIAN)))
help
This enables HiSax support for the Formula-n enter:now PCI
ISDN card.
diff --git a/drivers/isdn/mISDN/dsp_hwec.c b/drivers/isdn/mISDN/dsp_hwec.c
index a6e87076acc2..5336bbdbfdc5 100644
--- a/drivers/isdn/mISDN/dsp_hwec.c
+++ b/drivers/isdn/mISDN/dsp_hwec.c
@@ -68,12 +68,12 @@ void dsp_hwec_enable(struct dsp *dsp, const char *arg)
goto _do;
{
- char _dup[len + 1];
char *dup, *tok, *name, *val;
int tmp;
- strcpy(_dup, arg);
- dup = _dup;
+ dup = kstrdup(arg, GFP_ATOMIC);
+ if (!dup)
+ return;
while ((tok = strsep(&dup, ","))) {
if (!strlen(tok))
@@ -89,6 +89,8 @@ void dsp_hwec_enable(struct dsp *dsp, const char *arg)
deftaps = tmp;
}
}
+
+ kfree(dup);
}
_do:
diff --git a/drivers/isdn/mISDN/l1oip_core.c b/drivers/isdn/mISDN/l1oip_core.c
index 21d50e4cc5e1..b05022f94f18 100644
--- a/drivers/isdn/mISDN/l1oip_core.c
+++ b/drivers/isdn/mISDN/l1oip_core.c
@@ -279,7 +279,7 @@ l1oip_socket_send(struct l1oip *hc, u8 localcodec, u8 channel, u32 chanmask,
u16 timebase, u8 *buf, int len)
{
u8 *p;
- u8 frame[len + 32];
+ u8 frame[MAX_DFRAME_LEN_L1 + 32];
struct socket *socket = NULL;
if (debug & DEBUG_L1OIP_MSG)
@@ -902,7 +902,11 @@ handle_dmsg(struct mISDNchannel *ch, struct sk_buff *skb)
p = skb->data;
l = skb->len;
while (l) {
- ll = (l < L1OIP_MAX_PERFRAME) ? l : L1OIP_MAX_PERFRAME;
+ /*
+ * This is technically bounded by L1OIP_MAX_PERFRAME but
+ * MAX_DFRAME_LEN_L1 < L1OIP_MAX_PERFRAME
+ */
+ ll = (l < MAX_DFRAME_LEN_L1) ? l : MAX_DFRAME_LEN_L1;
l1oip_socket_send(hc, 0, dch->slot, 0,
hc->chan[dch->slot].tx_counter++, p, ll);
p += ll;
@@ -1140,7 +1144,11 @@ handle_bmsg(struct mISDNchannel *ch, struct sk_buff *skb)
p = skb->data;
l = skb->len;
while (l) {
- ll = (l < L1OIP_MAX_PERFRAME) ? l : L1OIP_MAX_PERFRAME;
+ /*
+ * This is technically bounded by L1OIP_MAX_PERFRAME but
+ * MAX_DFRAME_LEN_L1 < L1OIP_MAX_PERFRAME
+ */
+ ll = (l < MAX_DFRAME_LEN_L1) ? l : MAX_DFRAME_LEN_L1;
l1oip_socket_send(hc, hc->codec, bch->slot, 0,
hc->chan[bch->slot].tx_counter, p, ll);
hc->chan[bch->slot].tx_counter += ll;
diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c
index c5603d1a07d6..1f8f489b4167 100644
--- a/drivers/isdn/mISDN/socket.c
+++ b/drivers/isdn/mISDN/socket.c
@@ -560,7 +560,7 @@ done:
static int
data_sock_getname(struct socket *sock, struct sockaddr *addr,
- int *addr_len, int peer)
+ int peer)
{
struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
struct sock *sk = sock->sk;
@@ -570,14 +570,13 @@ data_sock_getname(struct socket *sock, struct sockaddr *addr,
lock_sock(sk);
- *addr_len = sizeof(*maddr);
maddr->family = AF_ISDN;
maddr->dev = _pms(sk)->dev->id;
maddr->channel = _pms(sk)->ch.nr;
maddr->sapi = _pms(sk)->ch.addr & 0xff;
maddr->tei = (_pms(sk)->ch.addr >> 8) & 0xff;
release_sock(sk);
- return 0;
+ return sizeof(*maddr);
}
static const struct proto_ops data_sock_ops = {
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 3e763d2a0cb3..2c896c0e69e1 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -69,7 +69,7 @@ config LEDS_APU
module will be called leds-apu.
config LEDS_AS3645A
- tristate "AS3645A LED flash controller support"
+ tristate "AS3645A and LM3555 LED flash controllers support"
depends on I2C && LEDS_CLASS_FLASH
depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS
help
@@ -521,7 +521,7 @@ config LEDS_MC13783
depends on LEDS_CLASS
depends on MFD_MC13XXX
help
- This option enable support for on-chip LED drivers found
+ This option enables support for on-chip LED drivers found
on Freescale Semiconductor MC13783/MC13892/MC34708 PMIC.
config LEDS_NS2
@@ -544,7 +544,7 @@ config LEDS_NETXBIG
depends on MACH_KIRKWOOD
default y
help
- This option enable support for LEDs found on the LaCie 2Big
+ This option enables support for LEDs found on the LaCie 2Big
and 5Big Network v2 boards. The LEDs are wired to a CPLD and are
controlled through a GPIO extension bus.
@@ -594,12 +594,12 @@ config LEDS_MAX8997
MAXIM MAX8997 PMIC.
config LEDS_LM355x
- tristate "LED support for LM355x Chips, LM3554 and LM3556"
+ tristate "LED support for LM3554 and LM3556 chips"
depends on LEDS_CLASS && I2C
select REGMAP_I2C
help
- This option enables support for LEDs connected to LM355x.
- LM355x includes Torch, Flash and Indicator functions.
+ This option enables support for LEDs connected to LM3554
+ and LM3556. It includes Torch, Flash and Indicator functions.
config LEDS_OT200
tristate "LED support for the Bachmann OT200"
@@ -674,7 +674,7 @@ config LEDS_SYSCON
depends on MFD_SYSCON
depends on OF
help
- This option enabled support for the LEDs on syscon type
+ This option enables support for the LEDs on syscon type
devices. This will only work with device tree enabled
devices.
@@ -688,11 +688,20 @@ config LEDS_PM8058
config LEDS_MLXCPLD
tristate "LED support for the Mellanox boards"
- depends on X86_64 && DMI
+ depends on X86 && DMI
+ depends on LEDS_CLASS
+ help
+ This option enables support for the LEDs on the Mellanox
+ boards. Say Y to enable these.
+
+config LEDS_MLXREG
+ tristate "LED support for the Mellanox switches management control"
depends on LEDS_CLASS
help
- This option enabled support for the LEDs on the Mellanox
- boards. Say Y to enabled these.
+ This option enables support for the LEDs on the Mellanox Ethernet and
+ InfiniBand switches. The driver can be activated by the platform device
+ device add call. Say Y to enable these. To compile this driver as a
+ module, choose 'M' here: the module will be called leds-mlxreg.
config LEDS_USER
tristate "Userspace LED support"
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 987884a5b9a5..91eca81cae82 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -72,6 +72,7 @@ obj-$(CONFIG_LEDS_IS31FL319X) += leds-is31fl319x.o
obj-$(CONFIG_LEDS_IS31FL32XX) += leds-is31fl32xx.o
obj-$(CONFIG_LEDS_PM8058) += leds-pm8058.o
obj-$(CONFIG_LEDS_MLXCPLD) += leds-mlxcpld.o
+obj-$(CONFIG_LEDS_MLXREG) += leds-mlxreg.o
obj-$(CONFIG_LEDS_NIC78BX) += leds-nic78bx.o
obj-$(CONFIG_LEDS_MT6323) += leds-mt6323.o
obj-$(CONFIG_LEDS_LM3692X) += leds-lm3692x.o
diff --git a/drivers/leds/leds-apu.c b/drivers/leds/leds-apu.c
index 74820aab9497..90eeedcbf371 100644
--- a/drivers/leds/leds-apu.c
+++ b/drivers/leds/leds-apu.c
@@ -110,6 +110,7 @@ static const struct dmi_system_id apu_led_dmi_table[] __initconst = {
DMI_MATCH(DMI_PRODUCT_NAME, "APU")
}
},
+ /* PC Engines APU2 with "Legacy" bios < 4.0.8 */
{
.ident = "apu2",
.matches = {
@@ -117,6 +118,22 @@ static const struct dmi_system_id apu_led_dmi_table[] __initconst = {
DMI_MATCH(DMI_BOARD_NAME, "APU2")
}
},
+ /* PC Engines APU2 with "Legacy" bios >= 4.0.8 */
+ {
+ .ident = "apu2",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
+ DMI_MATCH(DMI_BOARD_NAME, "apu2")
+ }
+ },
+ /* PC Engines APU2 with "Mainline" bios */
+ {
+ .ident = "apu2",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
+ DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu2")
+ }
+ },
{}
};
MODULE_DEVICE_TABLE(dmi, apu_led_dmi_table);
@@ -206,12 +223,14 @@ static int __init apu_led_probe(struct platform_device *pdev)
apu_led->pdev = pdev;
- if (dmi_match(DMI_BOARD_NAME, "APU")) {
+ if (dmi_match(DMI_PRODUCT_NAME, "APU")) {
apu_led->profile = apu1_led_profile;
apu_led->platform = APU1_LED_PLATFORM;
apu_led->num_led_instances = ARRAY_SIZE(apu1_led_profile);
apu_led->iosize = APU1_IOSIZE;
- } else if (dmi_match(DMI_BOARD_NAME, "APU2")) {
+ } else if (dmi_match(DMI_BOARD_NAME, "APU2") ||
+ dmi_match(DMI_BOARD_NAME, "apu2") ||
+ dmi_match(DMI_BOARD_NAME, "PC Engines apu2")) {
apu_led->profile = apu2_led_profile;
apu_led->platform = APU2_LED_PLATFORM;
apu_led->num_led_instances = ARRAY_SIZE(apu2_led_profile);
@@ -237,7 +256,10 @@ static int __init apu_led_init(void)
pr_err("No PC Engines board detected\n");
return -ENODEV;
}
- if (!(dmi_match(DMI_PRODUCT_NAME, "APU") || dmi_match(DMI_PRODUCT_NAME, "APU2"))) {
+ if (!(dmi_match(DMI_PRODUCT_NAME, "APU") ||
+ dmi_match(DMI_PRODUCT_NAME, "APU2") ||
+ dmi_match(DMI_PRODUCT_NAME, "apu2") ||
+ dmi_match(DMI_PRODUCT_NAME, "PC Engines apu2"))) {
pr_err("Unknown PC Engines board: %s\n",
dmi_get_system_info(DMI_PRODUCT_NAME));
return -ENODEV;
diff --git a/drivers/leds/leds-mlxreg.c b/drivers/leds/leds-mlxreg.c
new file mode 100644
index 000000000000..1ee48cb21df9
--- /dev/null
+++ b/drivers/leds/leds-mlxreg.c
@@ -0,0 +1,281 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+//
+// Copyright (c) 2018 Mellanox Technologies. All rights reserved.
+// Copyright (c) 2018 Vadim Pasternak <vadimp@mellanox.com>
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_data/mlxreg.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+/* Codes for LEDs. */
+#define MLXREG_LED_OFFSET_BLINK_3HZ 0x01 /* Offset from solid: 3Hz blink */
+#define MLXREG_LED_OFFSET_BLINK_6HZ 0x02 /* Offset from solid: 6Hz blink */
+#define MLXREG_LED_IS_OFF 0x00 /* Off */
+#define MLXREG_LED_RED_SOLID 0x05 /* Solid red */
+#define MLXREG_LED_GREEN_SOLID 0x0D /* Solid green */
+#define MLXREG_LED_AMBER_SOLID 0x09 /* Solid amber */
+#define MLXREG_LED_BLINK_3HZ 167 /* ~167 msec off/on - HW support */
+#define MLXREG_LED_BLINK_6HZ 83 /* ~83 msec off/on - HW support */
+
+/**
+ * struct mlxreg_led_data - led control data:
+ *
+ * @data: led configuration data;
+ * @led_classdev: led class data;
+ * @base_color: base led color (other colors have constant offset from base);
+ * @led_data: led data;
+ * @data_parent: pointer to private device control data of parent;
+ */
+struct mlxreg_led_data {
+ struct mlxreg_core_data *data;
+ struct led_classdev led_cdev;
+ u8 base_color;
+ void *data_parent;
+ char led_cdev_name[MLXREG_CORE_LABEL_MAX_SIZE];
+};
+
+#define cdev_to_priv(c) container_of(c, struct mlxreg_led_data, led_cdev)
+
+/**
+ * struct mlxreg_led_priv_data - platform private data:
+ *
+ * @pdev: platform device;
+ * @pdata: platform data;
+ * @access_lock: mutex for attribute IO access;
+ */
+struct mlxreg_led_priv_data {
+ struct platform_device *pdev;
+ struct mlxreg_core_platform_data *pdata;
+ struct mutex access_lock; /* protect IO operations */
+};
+
+static int
+mlxreg_led_store_hw(struct mlxreg_led_data *led_data, u8 vset)
+{
+ struct mlxreg_led_priv_data *priv = led_data->data_parent;
+ struct mlxreg_core_platform_data *led_pdata = priv->pdata;
+ struct mlxreg_core_data *data = led_data->data;
+ u32 regval;
+ u32 nib;
+ int ret;
+
+ /*
+ * Each LED is controlled through low or high nibble of the relevant
+ * register byte. Register offset is specified by off parameter.
+ * Parameter vset provides color code: 0x0 for off, 0x5 for solid red,
+ * 0x6 for 3Hz blink red, 0xd for solid green, 0xe for 3Hz blink
+ * green.
+ * Parameter mask specifies which nibble is used for specific LED: mask
+ * 0xf0 - lower nibble is to be used (bits from 0 to 3), mask 0x0f -
+ * higher nibble (bits from 4 to 7).
+ */
+ mutex_lock(&priv->access_lock);
+
+ ret = regmap_read(led_pdata->regmap, data->reg, &regval);
+ if (ret)
+ goto access_error;
+
+ nib = (ror32(data->mask, data->bit) == 0xf0) ? rol32(vset, data->bit) :
+ rol32(vset, data->bit + 4);
+ regval = (regval & data->mask) | nib;
+
+ ret = regmap_write(led_pdata->regmap, data->reg, regval);
+
+access_error:
+ mutex_unlock(&priv->access_lock);
+
+ return ret;
+}
+
+static enum led_brightness
+mlxreg_led_get_hw(struct mlxreg_led_data *led_data)
+{
+ struct mlxreg_led_priv_data *priv = led_data->data_parent;
+ struct mlxreg_core_platform_data *led_pdata = priv->pdata;
+ struct mlxreg_core_data *data = led_data->data;
+ u32 regval;
+ int err;
+
+ /*
+ * Each LED is controlled through low or high nibble of the relevant
+ * register byte. Register offset is specified by off parameter.
+ * Parameter vset provides color code: 0x0 for off, 0x5 for solid red,
+ * 0x6 for 3Hz blink red, 0xd for solid green, 0xe for 3Hz blink
+ * green.
+ * Parameter mask specifies which nibble is used for specific LED: mask
+ * 0xf0 - lower nibble is to be used (bits from 0 to 3), mask 0x0f -
+ * higher nibble (bits from 4 to 7).
+ */
+ err = regmap_read(led_pdata->regmap, data->reg, &regval);
+ if (err < 0) {
+ dev_warn(led_data->led_cdev.dev, "Failed to get current brightness, error: %d\n",
+ err);
+ /* Assume the LED is OFF */
+ return LED_OFF;
+ }
+
+ regval = regval & ~data->mask;
+ regval = (ror32(data->mask, data->bit) == 0xf0) ? ror32(regval,
+ data->bit) : ror32(regval, data->bit + 4);
+ if (regval >= led_data->base_color &&
+ regval <= (led_data->base_color + MLXREG_LED_OFFSET_BLINK_6HZ))
+ return LED_FULL;
+
+ return LED_OFF;
+}
+
+static int
+mlxreg_led_brightness_set(struct led_classdev *cled, enum led_brightness value)
+{
+ struct mlxreg_led_data *led_data = cdev_to_priv(cled);
+
+ if (value)
+ return mlxreg_led_store_hw(led_data, led_data->base_color);
+ else
+ return mlxreg_led_store_hw(led_data, MLXREG_LED_IS_OFF);
+}
+
+static enum led_brightness
+mlxreg_led_brightness_get(struct led_classdev *cled)
+{
+ struct mlxreg_led_data *led_data = cdev_to_priv(cled);
+
+ return mlxreg_led_get_hw(led_data);
+}
+
+static int
+mlxreg_led_blink_set(struct led_classdev *cled, unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ struct mlxreg_led_data *led_data = cdev_to_priv(cled);
+ int err;
+
+ /*
+ * HW supports two types of blinking: full (6Hz) and half (3Hz).
+ * For delay on/off zero LED is setting to solid color. For others
+ * combination blinking is to be controlled by the software timer.
+ */
+ if (!(*delay_on == 0 && *delay_off == 0) &&
+ !(*delay_on == MLXREG_LED_BLINK_3HZ &&
+ *delay_off == MLXREG_LED_BLINK_3HZ) &&
+ !(*delay_on == MLXREG_LED_BLINK_6HZ &&
+ *delay_off == MLXREG_LED_BLINK_6HZ))
+ return -EINVAL;
+
+ if (*delay_on == MLXREG_LED_BLINK_6HZ)
+ err = mlxreg_led_store_hw(led_data, led_data->base_color +
+ MLXREG_LED_OFFSET_BLINK_6HZ);
+ else if (*delay_on == MLXREG_LED_BLINK_3HZ)
+ err = mlxreg_led_store_hw(led_data, led_data->base_color +
+ MLXREG_LED_OFFSET_BLINK_3HZ);
+ else
+ err = mlxreg_led_store_hw(led_data, led_data->base_color);
+
+ return err;
+}
+
+static int mlxreg_led_config(struct mlxreg_led_priv_data *priv)
+{
+ struct mlxreg_core_platform_data *led_pdata = priv->pdata;
+ struct mlxreg_core_data *data = led_pdata->data;
+ struct mlxreg_led_data *led_data;
+ struct led_classdev *led_cdev;
+ enum led_brightness brightness;
+ int i;
+ int err;
+
+ for (i = 0; i < led_pdata->counter; i++, data++) {
+ led_data = devm_kzalloc(&priv->pdev->dev, sizeof(*led_data),
+ GFP_KERNEL);
+ if (!led_data)
+ return -ENOMEM;
+
+ led_cdev = &led_data->led_cdev;
+ led_data->data_parent = priv;
+ if (strstr(data->label, "red") ||
+ strstr(data->label, "orange")) {
+ brightness = LED_OFF;
+ led_data->base_color = MLXREG_LED_RED_SOLID;
+ } else if (strstr(data->label, "amber")) {
+ brightness = LED_OFF;
+ led_data->base_color = MLXREG_LED_AMBER_SOLID;
+ } else {
+ brightness = LED_OFF;
+ led_data->base_color = MLXREG_LED_GREEN_SOLID;
+ }
+ sprintf(led_data->led_cdev_name, "%s:%s", "mlxreg",
+ data->label);
+ led_cdev->name = led_data->led_cdev_name;
+ led_cdev->brightness = brightness;
+ led_cdev->max_brightness = LED_ON;
+ led_cdev->brightness_set_blocking =
+ mlxreg_led_brightness_set;
+ led_cdev->brightness_get = mlxreg_led_brightness_get;
+ led_cdev->blink_set = mlxreg_led_blink_set;
+ led_cdev->flags = LED_CORE_SUSPENDRESUME;
+ led_data->data = data;
+ err = devm_led_classdev_register(&priv->pdev->dev, led_cdev);
+ if (err)
+ return err;
+
+ if (led_cdev->brightness)
+ mlxreg_led_brightness_set(led_cdev,
+ led_cdev->brightness);
+ dev_info(led_cdev->dev, "label: %s, mask: 0x%02x, offset:0x%02x\n",
+ data->label, data->mask, data->reg);
+ }
+
+ return 0;
+}
+
+static int mlxreg_led_probe(struct platform_device *pdev)
+{
+ struct mlxreg_core_platform_data *led_pdata;
+ struct mlxreg_led_priv_data *priv;
+
+ led_pdata = dev_get_platdata(&pdev->dev);
+ if (!led_pdata) {
+ dev_err(&pdev->dev, "Failed to get platform data.\n");
+ return -EINVAL;
+ }
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ mutex_init(&priv->access_lock);
+ priv->pdev = pdev;
+ priv->pdata = led_pdata;
+
+ return mlxreg_led_config(priv);
+}
+
+static int mlxreg_led_remove(struct platform_device *pdev)
+{
+ struct mlxreg_led_priv_data *priv = dev_get_drvdata(&pdev->dev);
+
+ mutex_destroy(&priv->access_lock);
+
+ return 0;
+}
+
+static struct platform_driver mlxreg_led_driver = {
+ .driver = {
+ .name = "leds-mlxreg",
+ },
+ .probe = mlxreg_led_probe,
+ .remove = mlxreg_led_remove,
+};
+
+module_platform_driver(mlxreg_led_driver);
+
+MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
+MODULE_DESCRIPTION("Mellanox LED regmap driver");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS("platform:leds-mlxreg");
diff --git a/drivers/leds/trigger/ledtrig-disk.c b/drivers/leds/trigger/ledtrig-disk.c
index cd525b4125eb..9816b0d60270 100644
--- a/drivers/leds/trigger/ledtrig-disk.c
+++ b/drivers/leds/trigger/ledtrig-disk.c
@@ -18,9 +18,11 @@
#define BLINK_DELAY 30
DEFINE_LED_TRIGGER(ledtrig_disk);
+DEFINE_LED_TRIGGER(ledtrig_disk_read);
+DEFINE_LED_TRIGGER(ledtrig_disk_write);
DEFINE_LED_TRIGGER(ledtrig_ide);
-void ledtrig_disk_activity(void)
+void ledtrig_disk_activity(bool write)
{
unsigned long blink_delay = BLINK_DELAY;
@@ -28,12 +30,20 @@ void ledtrig_disk_activity(void)
&blink_delay, &blink_delay, 0);
led_trigger_blink_oneshot(ledtrig_ide,
&blink_delay, &blink_delay, 0);
+ if (write)
+ led_trigger_blink_oneshot(ledtrig_disk_write,
+ &blink_delay, &blink_delay, 0);
+ else
+ led_trigger_blink_oneshot(ledtrig_disk_read,
+ &blink_delay, &blink_delay, 0);
}
EXPORT_SYMBOL(ledtrig_disk_activity);
static int __init ledtrig_disk_init(void)
{
led_trigger_register_simple("disk-activity", &ledtrig_disk);
+ led_trigger_register_simple("disk-read", &ledtrig_disk_read);
+ led_trigger_register_simple("disk-write", &ledtrig_disk_write);
led_trigger_register_simple("ide-disk", &ledtrig_ide);
return 0;
diff --git a/drivers/lightnvm/core.c b/drivers/lightnvm/core.c
index dcc9e621e651..63171cdce270 100644
--- a/drivers/lightnvm/core.c
+++ b/drivers/lightnvm/core.c
@@ -36,13 +36,13 @@ static DECLARE_RWSEM(nvm_lock);
/* Map between virtual and physical channel and lun */
struct nvm_ch_map {
int ch_off;
- int nr_luns;
+ int num_lun;
int *lun_offs;
};
struct nvm_dev_map {
struct nvm_ch_map *chnls;
- int nr_chnls;
+ int num_ch;
};
static struct nvm_target *nvm_find_target(struct nvm_dev *dev, const char *name)
@@ -114,15 +114,15 @@ static void nvm_remove_tgt_dev(struct nvm_tgt_dev *tgt_dev, int clear)
struct nvm_dev_map *dev_map = tgt_dev->map;
int i, j;
- for (i = 0; i < dev_map->nr_chnls; i++) {
+ for (i = 0; i < dev_map->num_ch; i++) {
struct nvm_ch_map *ch_map = &dev_map->chnls[i];
int *lun_offs = ch_map->lun_offs;
int ch = i + ch_map->ch_off;
if (clear) {
- for (j = 0; j < ch_map->nr_luns; j++) {
+ for (j = 0; j < ch_map->num_lun; j++) {
int lun = j + lun_offs[j];
- int lunid = (ch * dev->geo.nr_luns) + lun;
+ int lunid = (ch * dev->geo.num_lun) + lun;
WARN_ON(!test_and_clear_bit(lunid,
dev->lun_map));
@@ -147,47 +147,46 @@ static struct nvm_tgt_dev *nvm_create_tgt_dev(struct nvm_dev *dev,
struct nvm_dev_map *dev_rmap = dev->rmap;
struct nvm_dev_map *dev_map;
struct ppa_addr *luns;
- int nr_luns = lun_end - lun_begin + 1;
- int luns_left = nr_luns;
- int nr_chnls = nr_luns / dev->geo.nr_luns;
- int nr_chnls_mod = nr_luns % dev->geo.nr_luns;
- int bch = lun_begin / dev->geo.nr_luns;
- int blun = lun_begin % dev->geo.nr_luns;
+ int num_lun = lun_end - lun_begin + 1;
+ int luns_left = num_lun;
+ int num_ch = num_lun / dev->geo.num_lun;
+ int num_ch_mod = num_lun % dev->geo.num_lun;
+ int bch = lun_begin / dev->geo.num_lun;
+ int blun = lun_begin % dev->geo.num_lun;
int lunid = 0;
int lun_balanced = 1;
- int prev_nr_luns;
+ int sec_per_lun, prev_num_lun;
int i, j;
- nr_chnls = (nr_chnls_mod == 0) ? nr_chnls : nr_chnls + 1;
+ num_ch = (num_ch_mod == 0) ? num_ch : num_ch + 1;
dev_map = kmalloc(sizeof(struct nvm_dev_map), GFP_KERNEL);
if (!dev_map)
goto err_dev;
- dev_map->chnls = kcalloc(nr_chnls, sizeof(struct nvm_ch_map),
- GFP_KERNEL);
+ dev_map->chnls = kcalloc(num_ch, sizeof(struct nvm_ch_map), GFP_KERNEL);
if (!dev_map->chnls)
goto err_chnls;
- luns = kcalloc(nr_luns, sizeof(struct ppa_addr), GFP_KERNEL);
+ luns = kcalloc(num_lun, sizeof(struct ppa_addr), GFP_KERNEL);
if (!luns)
goto err_luns;
- prev_nr_luns = (luns_left > dev->geo.nr_luns) ?
- dev->geo.nr_luns : luns_left;
- for (i = 0; i < nr_chnls; i++) {
+ prev_num_lun = (luns_left > dev->geo.num_lun) ?
+ dev->geo.num_lun : luns_left;
+ for (i = 0; i < num_ch; i++) {
struct nvm_ch_map *ch_rmap = &dev_rmap->chnls[i + bch];
int *lun_roffs = ch_rmap->lun_offs;
struct nvm_ch_map *ch_map = &dev_map->chnls[i];
int *lun_offs;
- int luns_in_chnl = (luns_left > dev->geo.nr_luns) ?
- dev->geo.nr_luns : luns_left;
+ int luns_in_chnl = (luns_left > dev->geo.num_lun) ?
+ dev->geo.num_lun : luns_left;
- if (lun_balanced && prev_nr_luns != luns_in_chnl)
+ if (lun_balanced && prev_num_lun != luns_in_chnl)
lun_balanced = 0;
ch_map->ch_off = ch_rmap->ch_off = bch;
- ch_map->nr_luns = luns_in_chnl;
+ ch_map->num_lun = luns_in_chnl;
lun_offs = kcalloc(luns_in_chnl, sizeof(int), GFP_KERNEL);
if (!lun_offs)
@@ -195,8 +194,8 @@ static struct nvm_tgt_dev *nvm_create_tgt_dev(struct nvm_dev *dev,
for (j = 0; j < luns_in_chnl; j++) {
luns[lunid].ppa = 0;
- luns[lunid].g.ch = i;
- luns[lunid++].g.lun = j;
+ luns[lunid].a.ch = i;
+ luns[lunid++].a.lun = j;
lun_offs[j] = blun;
lun_roffs[j + blun] = blun;
@@ -209,24 +208,29 @@ static struct nvm_tgt_dev *nvm_create_tgt_dev(struct nvm_dev *dev,
luns_left -= luns_in_chnl;
}
- dev_map->nr_chnls = nr_chnls;
+ dev_map->num_ch = num_ch;
tgt_dev = kmalloc(sizeof(struct nvm_tgt_dev), GFP_KERNEL);
if (!tgt_dev)
goto err_ch;
+ /* Inherit device geometry from parent */
memcpy(&tgt_dev->geo, &dev->geo, sizeof(struct nvm_geo));
+
/* Target device only owns a portion of the physical device */
- tgt_dev->geo.nr_chnls = nr_chnls;
- tgt_dev->geo.all_luns = nr_luns;
- tgt_dev->geo.nr_luns = (lun_balanced) ? prev_nr_luns : -1;
+ tgt_dev->geo.num_ch = num_ch;
+ tgt_dev->geo.num_lun = (lun_balanced) ? prev_num_lun : -1;
+ tgt_dev->geo.all_luns = num_lun;
+ tgt_dev->geo.all_chunks = num_lun * dev->geo.num_chk;
+
tgt_dev->geo.op = op;
- tgt_dev->total_secs = nr_luns * tgt_dev->geo.sec_per_lun;
+
+ sec_per_lun = dev->geo.clba * dev->geo.num_chk;
+ tgt_dev->geo.total_secs = num_lun * sec_per_lun;
+
tgt_dev->q = dev->q;
tgt_dev->map = dev_map;
tgt_dev->luns = luns;
- memcpy(&tgt_dev->identity, &dev->identity, sizeof(struct nvm_id));
-
tgt_dev->parent = dev;
return tgt_dev;
@@ -296,24 +300,20 @@ static int __nvm_config_simple(struct nvm_dev *dev,
static int __nvm_config_extended(struct nvm_dev *dev,
struct nvm_ioctl_create_extended *e)
{
- struct nvm_geo *geo = &dev->geo;
-
if (e->lun_begin == 0xFFFF && e->lun_end == 0xFFFF) {
e->lun_begin = 0;
e->lun_end = dev->geo.all_luns - 1;
}
/* op not set falls into target's default */
- if (e->op == 0xFFFF)
+ if (e->op == 0xFFFF) {
e->op = NVM_TARGET_DEFAULT_OP;
-
- if (e->op < NVM_TARGET_MIN_OP ||
- e->op > NVM_TARGET_MAX_OP) {
+ } else if (e->op < NVM_TARGET_MIN_OP || e->op > NVM_TARGET_MAX_OP) {
pr_err("nvm: invalid over provisioning value\n");
return -EINVAL;
}
- return nvm_config_check_luns(geo, e->lun_begin, e->lun_end);
+ return nvm_config_check_luns(&dev->geo, e->lun_begin, e->lun_end);
}
static int nvm_create_tgt(struct nvm_dev *dev, struct nvm_ioctl_create *create)
@@ -384,7 +384,7 @@ static int nvm_create_tgt(struct nvm_dev *dev, struct nvm_ioctl_create *create)
goto err_dev;
}
- tqueue = blk_alloc_queue_node(GFP_KERNEL, dev->q->node);
+ tqueue = blk_alloc_queue_node(GFP_KERNEL, dev->q->node, NULL);
if (!tqueue) {
ret = -ENOMEM;
goto err_disk;
@@ -407,7 +407,8 @@ static int nvm_create_tgt(struct nvm_dev *dev, struct nvm_ioctl_create *create)
tdisk->private_data = targetdata;
tqueue->queuedata = targetdata;
- blk_queue_max_hw_sectors(tqueue, 8 * dev->ops->max_phys_sect);
+ blk_queue_max_hw_sectors(tqueue,
+ (dev->geo.csecs >> 9) * NVM_MAX_VLBA);
set_capacity(tdisk, tt->capacity(targetdata));
add_disk(tdisk);
@@ -503,20 +504,20 @@ static int nvm_register_map(struct nvm_dev *dev)
if (!rmap)
goto err_rmap;
- rmap->chnls = kcalloc(dev->geo.nr_chnls, sizeof(struct nvm_ch_map),
+ rmap->chnls = kcalloc(dev->geo.num_ch, sizeof(struct nvm_ch_map),
GFP_KERNEL);
if (!rmap->chnls)
goto err_chnls;
- for (i = 0; i < dev->geo.nr_chnls; i++) {
+ for (i = 0; i < dev->geo.num_ch; i++) {
struct nvm_ch_map *ch_rmap;
int *lun_roffs;
- int luns_in_chnl = dev->geo.nr_luns;
+ int luns_in_chnl = dev->geo.num_lun;
ch_rmap = &rmap->chnls[i];
ch_rmap->ch_off = -1;
- ch_rmap->nr_luns = luns_in_chnl;
+ ch_rmap->num_lun = luns_in_chnl;
lun_roffs = kcalloc(luns_in_chnl, sizeof(int), GFP_KERNEL);
if (!lun_roffs)
@@ -545,7 +546,7 @@ static void nvm_unregister_map(struct nvm_dev *dev)
struct nvm_dev_map *rmap = dev->rmap;
int i;
- for (i = 0; i < dev->geo.nr_chnls; i++)
+ for (i = 0; i < dev->geo.num_ch; i++)
kfree(rmap->chnls[i].lun_offs);
kfree(rmap->chnls);
@@ -555,22 +556,22 @@ static void nvm_unregister_map(struct nvm_dev *dev)
static void nvm_map_to_dev(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *p)
{
struct nvm_dev_map *dev_map = tgt_dev->map;
- struct nvm_ch_map *ch_map = &dev_map->chnls[p->g.ch];
- int lun_off = ch_map->lun_offs[p->g.lun];
+ struct nvm_ch_map *ch_map = &dev_map->chnls[p->a.ch];
+ int lun_off = ch_map->lun_offs[p->a.lun];
- p->g.ch += ch_map->ch_off;
- p->g.lun += lun_off;
+ p->a.ch += ch_map->ch_off;
+ p->a.lun += lun_off;
}
static void nvm_map_to_tgt(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *p)
{
struct nvm_dev *dev = tgt_dev->parent;
struct nvm_dev_map *dev_rmap = dev->rmap;
- struct nvm_ch_map *ch_rmap = &dev_rmap->chnls[p->g.ch];
- int lun_roff = ch_rmap->lun_offs[p->g.lun];
+ struct nvm_ch_map *ch_rmap = &dev_rmap->chnls[p->a.ch];
+ int lun_roff = ch_rmap->lun_offs[p->a.lun];
- p->g.ch -= ch_rmap->ch_off;
- p->g.lun -= lun_roff;
+ p->a.ch -= ch_rmap->ch_off;
+ p->a.lun -= lun_roff;
}
static void nvm_ppa_tgt_to_dev(struct nvm_tgt_dev *tgt_dev,
@@ -580,7 +581,7 @@ static void nvm_ppa_tgt_to_dev(struct nvm_tgt_dev *tgt_dev,
for (i = 0; i < nr_ppas; i++) {
nvm_map_to_dev(tgt_dev, &ppa_list[i]);
- ppa_list[i] = generic_to_dev_addr(tgt_dev, ppa_list[i]);
+ ppa_list[i] = generic_to_dev_addr(tgt_dev->parent, ppa_list[i]);
}
}
@@ -590,7 +591,7 @@ static void nvm_ppa_dev_to_tgt(struct nvm_tgt_dev *tgt_dev,
int i;
for (i = 0; i < nr_ppas; i++) {
- ppa_list[i] = dev_to_generic_addr(tgt_dev, ppa_list[i]);
+ ppa_list[i] = dev_to_generic_addr(tgt_dev->parent, ppa_list[i]);
nvm_map_to_tgt(tgt_dev, &ppa_list[i]);
}
}
@@ -674,7 +675,7 @@ static int nvm_set_rqd_ppalist(struct nvm_tgt_dev *tgt_dev, struct nvm_rq *rqd,
int i, plane_cnt, pl_idx;
struct ppa_addr ppa;
- if (geo->plane_mode == NVM_PLANE_SINGLE && nr_ppas == 1) {
+ if (geo->pln_mode == NVM_PLANE_SINGLE && nr_ppas == 1) {
rqd->nr_ppas = nr_ppas;
rqd->ppa_addr = ppas[0];
@@ -688,7 +689,7 @@ static int nvm_set_rqd_ppalist(struct nvm_tgt_dev *tgt_dev, struct nvm_rq *rqd,
return -ENOMEM;
}
- plane_cnt = geo->plane_mode;
+ plane_cnt = geo->pln_mode;
rqd->nr_ppas *= plane_cnt;
for (i = 0; i < nr_ppas; i++) {
@@ -711,6 +712,17 @@ static void nvm_free_rqd_ppalist(struct nvm_tgt_dev *tgt_dev,
nvm_dev_dma_free(tgt_dev->parent, rqd->ppa_list, rqd->dma_ppa_list);
}
+int nvm_get_chunk_meta(struct nvm_tgt_dev *tgt_dev, struct nvm_chk_meta *meta,
+ struct ppa_addr ppa, int nchks)
+{
+ struct nvm_dev *dev = tgt_dev->parent;
+
+ nvm_ppa_tgt_to_dev(tgt_dev, &ppa, 1);
+
+ return dev->ops->get_chk_meta(tgt_dev->parent, meta,
+ (sector_t)ppa.ppa, nchks);
+}
+EXPORT_SYMBOL(nvm_get_chunk_meta);
int nvm_set_tgt_bb_tbl(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *ppas,
int nr_ppas, int type)
@@ -719,7 +731,7 @@ int nvm_set_tgt_bb_tbl(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *ppas,
struct nvm_rq rqd;
int ret;
- if (nr_ppas > dev->ops->max_phys_sect) {
+ if (nr_ppas > NVM_MAX_VLBA) {
pr_err("nvm: unable to update all blocks atomically\n");
return -EINVAL;
}
@@ -740,14 +752,6 @@ int nvm_set_tgt_bb_tbl(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *ppas,
}
EXPORT_SYMBOL(nvm_set_tgt_bb_tbl);
-int nvm_max_phys_sects(struct nvm_tgt_dev *tgt_dev)
-{
- struct nvm_dev *dev = tgt_dev->parent;
-
- return dev->ops->max_phys_sect;
-}
-EXPORT_SYMBOL(nvm_max_phys_sects);
-
int nvm_submit_io(struct nvm_tgt_dev *tgt_dev, struct nvm_rq *rqd)
{
struct nvm_dev *dev = tgt_dev->parent;
@@ -814,15 +818,15 @@ int nvm_bb_tbl_fold(struct nvm_dev *dev, u8 *blks, int nr_blks)
struct nvm_geo *geo = &dev->geo;
int blk, offset, pl, blktype;
- if (nr_blks != geo->nr_chks * geo->plane_mode)
+ if (nr_blks != geo->num_chk * geo->pln_mode)
return -EINVAL;
- for (blk = 0; blk < geo->nr_chks; blk++) {
- offset = blk * geo->plane_mode;
+ for (blk = 0; blk < geo->num_chk; blk++) {
+ offset = blk * geo->pln_mode;
blktype = blks[offset];
/* Bad blocks on any planes take precedence over other types */
- for (pl = 0; pl < geo->plane_mode; pl++) {
+ for (pl = 0; pl < geo->pln_mode; pl++) {
if (blks[offset + pl] &
(NVM_BLK_T_BAD|NVM_BLK_T_GRWN_BAD)) {
blktype = blks[offset + pl];
@@ -833,7 +837,7 @@ int nvm_bb_tbl_fold(struct nvm_dev *dev, u8 *blks, int nr_blks)
blks[blk] = blktype;
}
- return geo->nr_chks;
+ return geo->num_chk;
}
EXPORT_SYMBOL(nvm_bb_tbl_fold);
@@ -850,44 +854,9 @@ EXPORT_SYMBOL(nvm_get_tgt_bb_tbl);
static int nvm_core_init(struct nvm_dev *dev)
{
- struct nvm_id *id = &dev->identity;
- struct nvm_id_group *grp = &id->grp;
struct nvm_geo *geo = &dev->geo;
int ret;
- memcpy(&geo->ppaf, &id->ppaf, sizeof(struct nvm_addr_format));
-
- if (grp->mtype != 0) {
- pr_err("nvm: memory type not supported\n");
- return -EINVAL;
- }
-
- /* Whole device values */
- geo->nr_chnls = grp->num_ch;
- geo->nr_luns = grp->num_lun;
-
- /* Generic device geometry values */
- geo->ws_min = grp->ws_min;
- geo->ws_opt = grp->ws_opt;
- geo->ws_seq = grp->ws_seq;
- geo->ws_per_chk = grp->ws_per_chk;
- geo->nr_chks = grp->num_chk;
- geo->sec_size = grp->csecs;
- geo->oob_size = grp->sos;
- geo->mccap = grp->mccap;
- geo->max_rq_size = dev->ops->max_phys_sect * geo->sec_size;
-
- geo->sec_per_chk = grp->clba;
- geo->sec_per_lun = geo->sec_per_chk * geo->nr_chks;
- geo->all_luns = geo->nr_luns * geo->nr_chnls;
-
- /* 1.2 spec device geometry values */
- geo->plane_mode = 1 << geo->ws_seq;
- geo->nr_planes = geo->ws_opt / geo->ws_min;
- geo->sec_per_pg = geo->ws_min;
- geo->sec_per_pl = geo->sec_per_pg * geo->nr_planes;
-
- dev->total_secs = geo->all_luns * geo->sec_per_lun;
dev->lun_map = kcalloc(BITS_TO_LONGS(geo->all_luns),
sizeof(unsigned long), GFP_KERNEL);
if (!dev->lun_map)
@@ -902,7 +871,6 @@ static int nvm_core_init(struct nvm_dev *dev)
if (ret)
goto err_fmtype;
- blk_queue_logical_block_size(dev->q, geo->sec_size);
return 0;
err_fmtype:
kfree(dev->lun_map);
@@ -927,18 +895,14 @@ static int nvm_init(struct nvm_dev *dev)
struct nvm_geo *geo = &dev->geo;
int ret = -EINVAL;
- if (dev->ops->identity(dev, &dev->identity)) {
+ if (dev->ops->identity(dev)) {
pr_err("nvm: device could not be identified\n");
goto err;
}
- pr_debug("nvm: ver:%x nvm_vendor:%x\n",
- dev->identity.ver_id, dev->identity.vmnt);
-
- if (dev->identity.ver_id != 1) {
- pr_err("nvm: device not supported by kernel.");
- goto err;
- }
+ pr_debug("nvm: ver:%u.%u nvm_vendor:%x\n",
+ geo->major_ver_id, geo->minor_ver_id,
+ geo->vmnt);
ret = nvm_core_init(dev);
if (ret) {
@@ -946,10 +910,10 @@ static int nvm_init(struct nvm_dev *dev)
goto err;
}
- pr_info("nvm: registered %s [%u/%u/%u/%u/%u/%u]\n",
- dev->name, geo->sec_per_pg, geo->nr_planes,
- geo->ws_per_chk, geo->nr_chks,
- geo->all_luns, geo->nr_chnls);
+ pr_info("nvm: registered %s [%u/%u/%u/%u/%u]\n",
+ dev->name, dev->geo.ws_min, dev->geo.ws_opt,
+ dev->geo.num_chk, dev->geo.all_luns,
+ dev->geo.num_ch);
return 0;
err:
pr_err("nvm: failed to initialize nvm\n");
@@ -969,17 +933,10 @@ int nvm_register(struct nvm_dev *dev)
if (!dev->q || !dev->ops)
return -EINVAL;
- if (dev->ops->max_phys_sect > 256) {
- pr_info("nvm: max sectors supported is 256.\n");
- return -EINVAL;
- }
-
- if (dev->ops->max_phys_sect > 1) {
- dev->dma_pool = dev->ops->create_dma_pool(dev, "ppalist");
- if (!dev->dma_pool) {
- pr_err("nvm: could not create dma pool\n");
- return -ENOMEM;
- }
+ dev->dma_pool = dev->ops->create_dma_pool(dev, "ppalist");
+ if (!dev->dma_pool) {
+ pr_err("nvm: could not create dma pool\n");
+ return -ENOMEM;
}
ret = nvm_init(dev);
@@ -1040,9 +997,6 @@ static long nvm_ioctl_info(struct file *file, void __user *arg)
struct nvm_tgt_type *tt;
int tgt_iter = 0;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
-
info = memdup_user(arg, sizeof(struct nvm_ioctl_info));
if (IS_ERR(info))
return -EFAULT;
@@ -1081,9 +1035,6 @@ static long nvm_ioctl_get_devices(struct file *file, void __user *arg)
struct nvm_dev *dev;
int i = 0;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
-
devices = kzalloc(sizeof(struct nvm_ioctl_get_devices), GFP_KERNEL);
if (!devices)
return -ENOMEM;
@@ -1124,9 +1075,6 @@ static long nvm_ioctl_dev_create(struct file *file, void __user *arg)
{
struct nvm_ioctl_create create;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
-
if (copy_from_user(&create, arg, sizeof(struct nvm_ioctl_create)))
return -EFAULT;
@@ -1162,9 +1110,6 @@ static long nvm_ioctl_dev_remove(struct file *file, void __user *arg)
struct nvm_dev *dev;
int ret = 0;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
-
if (copy_from_user(&remove, arg, sizeof(struct nvm_ioctl_remove)))
return -EFAULT;
@@ -1189,9 +1134,6 @@ static long nvm_ioctl_dev_init(struct file *file, void __user *arg)
{
struct nvm_ioctl_dev_init init;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
-
if (copy_from_user(&init, arg, sizeof(struct nvm_ioctl_dev_init)))
return -EFAULT;
@@ -1208,9 +1150,6 @@ static long nvm_ioctl_dev_factory(struct file *file, void __user *arg)
{
struct nvm_ioctl_dev_factory fact;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
-
if (copy_from_user(&fact, arg, sizeof(struct nvm_ioctl_dev_factory)))
return -EFAULT;
@@ -1226,6 +1165,9 @@ static long nvm_ctl_ioctl(struct file *file, uint cmd, unsigned long arg)
{
void __user *argp = (void __user *)arg;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
switch (cmd) {
case NVM_INFO:
return nvm_ioctl_info(file, argp);
diff --git a/drivers/lightnvm/pblk-cache.c b/drivers/lightnvm/pblk-cache.c
index 000fcad38136..29a23111b31c 100644
--- a/drivers/lightnvm/pblk-cache.c
+++ b/drivers/lightnvm/pblk-cache.c
@@ -63,6 +63,8 @@ retry:
bio_advance(bio, PBLK_EXPOSED_PAGE_SIZE);
}
+ atomic64_add(nr_entries, &pblk->user_wa);
+
#ifdef CONFIG_NVM_DEBUG
atomic_long_add(nr_entries, &pblk->inflight_writes);
atomic_long_add(nr_entries, &pblk->req_writes);
@@ -117,6 +119,8 @@ retry:
WARN_ONCE(gc_rq->secs_to_gc != valid_entries,
"pblk: inconsistent GC write\n");
+ atomic64_add(valid_entries, &pblk->gc_wa);
+
#ifdef CONFIG_NVM_DEBUG
atomic_long_add(valid_entries, &pblk->inflight_writes);
atomic_long_add(valid_entries, &pblk->recov_gc_writes);
diff --git a/drivers/lightnvm/pblk-core.c b/drivers/lightnvm/pblk-core.c
index 0487b9340c1d..94d5d97c9d8a 100644
--- a/drivers/lightnvm/pblk-core.c
+++ b/drivers/lightnvm/pblk-core.c
@@ -44,11 +44,12 @@ static void pblk_line_mark_bb(struct work_struct *work)
}
static void pblk_mark_bb(struct pblk *pblk, struct pblk_line *line,
- struct ppa_addr *ppa)
+ struct ppa_addr ppa_addr)
{
struct nvm_tgt_dev *dev = pblk->dev;
struct nvm_geo *geo = &dev->geo;
- int pos = pblk_ppa_to_pos(geo, *ppa);
+ struct ppa_addr *ppa;
+ int pos = pblk_ppa_to_pos(geo, ppa_addr);
pr_debug("pblk: erase failed: line:%d, pos:%d\n", line->id, pos);
atomic_long_inc(&pblk->erase_failed);
@@ -58,26 +59,38 @@ static void pblk_mark_bb(struct pblk *pblk, struct pblk_line *line,
pr_err("pblk: attempted to erase bb: line:%d, pos:%d\n",
line->id, pos);
+ /* Not necessary to mark bad blocks on 2.0 spec. */
+ if (geo->version == NVM_OCSSD_SPEC_20)
+ return;
+
+ ppa = kmalloc(sizeof(struct ppa_addr), GFP_ATOMIC);
+ if (!ppa)
+ return;
+
+ *ppa = ppa_addr;
pblk_gen_run_ws(pblk, NULL, ppa, pblk_line_mark_bb,
GFP_ATOMIC, pblk->bb_wq);
}
static void __pblk_end_io_erase(struct pblk *pblk, struct nvm_rq *rqd)
{
+ struct nvm_tgt_dev *dev = pblk->dev;
+ struct nvm_geo *geo = &dev->geo;
+ struct nvm_chk_meta *chunk;
struct pblk_line *line;
+ int pos;
line = &pblk->lines[pblk_ppa_to_line(rqd->ppa_addr)];
+ pos = pblk_ppa_to_pos(geo, rqd->ppa_addr);
+ chunk = &line->chks[pos];
+
atomic_dec(&line->left_seblks);
if (rqd->error) {
- struct ppa_addr *ppa;
-
- ppa = kmalloc(sizeof(struct ppa_addr), GFP_ATOMIC);
- if (!ppa)
- return;
-
- *ppa = rqd->ppa_addr;
- pblk_mark_bb(pblk, line, ppa);
+ chunk->state = NVM_CHK_ST_OFFLINE;
+ pblk_mark_bb(pblk, line, rqd->ppa_addr);
+ } else {
+ chunk->state = NVM_CHK_ST_FREE;
}
atomic_dec(&pblk->inflight_io);
@@ -92,6 +105,49 @@ static void pblk_end_io_erase(struct nvm_rq *rqd)
mempool_free(rqd, pblk->e_rq_pool);
}
+/*
+ * Get information for all chunks from the device.
+ *
+ * The caller is responsible for freeing the returned structure
+ */
+struct nvm_chk_meta *pblk_chunk_get_info(struct pblk *pblk)
+{
+ struct nvm_tgt_dev *dev = pblk->dev;
+ struct nvm_geo *geo = &dev->geo;
+ struct nvm_chk_meta *meta;
+ struct ppa_addr ppa;
+ unsigned long len;
+ int ret;
+
+ ppa.ppa = 0;
+
+ len = geo->all_chunks * sizeof(*meta);
+ meta = kzalloc(len, GFP_KERNEL);
+ if (!meta)
+ return ERR_PTR(-ENOMEM);
+
+ ret = nvm_get_chunk_meta(dev, meta, ppa, geo->all_chunks);
+ if (ret) {
+ kfree(meta);
+ return ERR_PTR(-EIO);
+ }
+
+ return meta;
+}
+
+struct nvm_chk_meta *pblk_chunk_get_off(struct pblk *pblk,
+ struct nvm_chk_meta *meta,
+ struct ppa_addr ppa)
+{
+ struct nvm_tgt_dev *dev = pblk->dev;
+ struct nvm_geo *geo = &dev->geo;
+ int ch_off = ppa.m.grp * geo->num_chk * geo->num_lun;
+ int lun_off = ppa.m.pu * geo->num_chk;
+ int chk_off = ppa.m.chk;
+
+ return meta + ch_off + lun_off + chk_off;
+}
+
void __pblk_map_invalidate(struct pblk *pblk, struct pblk_line *line,
u64 paddr)
{
@@ -613,7 +669,7 @@ next_rq:
memset(&rqd, 0, sizeof(struct nvm_rq));
rq_ppas = pblk_calc_secs(pblk, left_ppas, 0);
- rq_len = rq_ppas * geo->sec_size;
+ rq_len = rq_ppas * geo->csecs;
bio = pblk_bio_map_addr(pblk, emeta_buf, rq_ppas, rq_len,
l_mg->emeta_alloc_type, GFP_KERNEL);
@@ -722,7 +778,7 @@ u64 pblk_line_smeta_start(struct pblk *pblk, struct pblk_line *line)
if (bit >= lm->blk_per_line)
return -1;
- return bit * geo->sec_per_pl;
+ return bit * geo->ws_opt;
}
static int pblk_line_submit_smeta_io(struct pblk *pblk, struct pblk_line *line,
@@ -885,7 +941,7 @@ int pblk_line_erase(struct pblk *pblk, struct pblk_line *line)
}
ppa = pblk->luns[bit].bppa; /* set ch and lun */
- ppa.g.blk = line->id;
+ ppa.a.blk = line->id;
atomic_dec(&line->left_eblks);
WARN_ON(test_and_set_bit(bit, line->erase_bitmap));
@@ -975,7 +1031,8 @@ static int pblk_line_init_metadata(struct pblk *pblk, struct pblk_line *line,
memcpy(smeta_buf->header.uuid, pblk->instance_uuid, 16);
smeta_buf->header.id = cpu_to_le32(line->id);
smeta_buf->header.type = cpu_to_le16(line->type);
- smeta_buf->header.version = SMETA_VERSION;
+ smeta_buf->header.version_major = SMETA_VERSION_MAJOR;
+ smeta_buf->header.version_minor = SMETA_VERSION_MINOR;
/* Start metadata */
smeta_buf->seq_nr = cpu_to_le64(line->seq_nr);
@@ -998,6 +1055,12 @@ static int pblk_line_init_metadata(struct pblk *pblk, struct pblk_line *line,
/* End metadata */
memcpy(&emeta_buf->header, &smeta_buf->header,
sizeof(struct line_header));
+
+ emeta_buf->header.version_major = EMETA_VERSION_MAJOR;
+ emeta_buf->header.version_minor = EMETA_VERSION_MINOR;
+ emeta_buf->header.crc = cpu_to_le32(
+ pblk_calc_meta_header_crc(pblk, &emeta_buf->header));
+
emeta_buf->seq_nr = cpu_to_le64(line->seq_nr);
emeta_buf->nr_lbas = cpu_to_le64(line->sec_in_line);
emeta_buf->nr_valid_lbas = cpu_to_le64(0);
@@ -1018,28 +1081,26 @@ static int pblk_line_init_bb(struct pblk *pblk, struct pblk_line *line,
struct nvm_geo *geo = &dev->geo;
struct pblk_line_meta *lm = &pblk->lm;
struct pblk_line_mgmt *l_mg = &pblk->l_mg;
- int nr_bb = 0;
u64 off;
int bit = -1;
+ int emeta_secs;
line->sec_in_line = lm->sec_per_line;
/* Capture bad block information on line mapping bitmaps */
while ((bit = find_next_bit(line->blk_bitmap, lm->blk_per_line,
bit + 1)) < lm->blk_per_line) {
- off = bit * geo->sec_per_pl;
+ off = bit * geo->ws_opt;
bitmap_shift_left(l_mg->bb_aux, l_mg->bb_template, off,
lm->sec_per_line);
bitmap_or(line->map_bitmap, line->map_bitmap, l_mg->bb_aux,
lm->sec_per_line);
- line->sec_in_line -= geo->sec_per_chk;
- if (bit >= lm->emeta_bb)
- nr_bb++;
+ line->sec_in_line -= geo->clba;
}
/* Mark smeta metadata sectors as bad sectors */
bit = find_first_zero_bit(line->blk_bitmap, lm->blk_per_line);
- off = bit * geo->sec_per_pl;
+ off = bit * geo->ws_opt;
bitmap_set(line->map_bitmap, off, lm->smeta_sec);
line->sec_in_line -= lm->smeta_sec;
line->smeta_ssec = off;
@@ -1055,18 +1116,18 @@ static int pblk_line_init_bb(struct pblk *pblk, struct pblk_line *line,
/* Mark emeta metadata sectors as bad sectors. We need to consider bad
* blocks to make sure that there are enough sectors to store emeta
*/
- off = lm->sec_per_line - lm->emeta_sec[0];
- bitmap_set(line->invalid_bitmap, off, lm->emeta_sec[0]);
- while (nr_bb) {
- off -= geo->sec_per_pl;
+ emeta_secs = lm->emeta_sec[0];
+ off = lm->sec_per_line;
+ while (emeta_secs) {
+ off -= geo->ws_opt;
if (!test_bit(off, line->invalid_bitmap)) {
- bitmap_set(line->invalid_bitmap, off, geo->sec_per_pl);
- nr_bb--;
+ bitmap_set(line->invalid_bitmap, off, geo->ws_opt);
+ emeta_secs -= geo->ws_opt;
}
}
- line->sec_in_line -= lm->emeta_sec[0];
line->emeta_ssec = off;
+ line->sec_in_line -= lm->emeta_sec[0];
line->nr_valid_lbas = 0;
line->left_msecs = line->sec_in_line;
*line->vsc = cpu_to_le32(line->sec_in_line);
@@ -1086,10 +1147,34 @@ static int pblk_line_init_bb(struct pblk *pblk, struct pblk_line *line,
return 1;
}
+static int pblk_prepare_new_line(struct pblk *pblk, struct pblk_line *line)
+{
+ struct pblk_line_meta *lm = &pblk->lm;
+ struct nvm_tgt_dev *dev = pblk->dev;
+ struct nvm_geo *geo = &dev->geo;
+ int blk_to_erase = atomic_read(&line->blk_in_line);
+ int i;
+
+ for (i = 0; i < lm->blk_per_line; i++) {
+ struct pblk_lun *rlun = &pblk->luns[i];
+ int pos = pblk_ppa_to_pos(geo, rlun->bppa);
+ int state = line->chks[pos].state;
+
+ /* Free chunks should not be erased */
+ if (state & NVM_CHK_ST_FREE) {
+ set_bit(pblk_ppa_to_pos(geo, rlun->bppa),
+ line->erase_bitmap);
+ blk_to_erase--;
+ }
+ }
+
+ return blk_to_erase;
+}
+
static int pblk_line_prepare(struct pblk *pblk, struct pblk_line *line)
{
struct pblk_line_meta *lm = &pblk->lm;
- int blk_in_line = atomic_read(&line->blk_in_line);
+ int blk_to_erase;
line->map_bitmap = kzalloc(lm->sec_bitmap_len, GFP_ATOMIC);
if (!line->map_bitmap)
@@ -1102,7 +1187,21 @@ static int pblk_line_prepare(struct pblk *pblk, struct pblk_line *line)
return -ENOMEM;
}
+ /* Bad blocks do not need to be erased */
+ bitmap_copy(line->erase_bitmap, line->blk_bitmap, lm->blk_per_line);
+
spin_lock(&line->lock);
+
+ /* If we have not written to this line, we need to mark up free chunks
+ * as already erased
+ */
+ if (line->state == PBLK_LINESTATE_NEW) {
+ blk_to_erase = pblk_prepare_new_line(pblk, line);
+ line->state = PBLK_LINESTATE_FREE;
+ } else {
+ blk_to_erase = atomic_read(&line->blk_in_line);
+ }
+
if (line->state != PBLK_LINESTATE_FREE) {
kfree(line->map_bitmap);
kfree(line->invalid_bitmap);
@@ -1114,15 +1213,12 @@ static int pblk_line_prepare(struct pblk *pblk, struct pblk_line *line)
line->state = PBLK_LINESTATE_OPEN;
- atomic_set(&line->left_eblks, blk_in_line);
- atomic_set(&line->left_seblks, blk_in_line);
+ atomic_set(&line->left_eblks, blk_to_erase);
+ atomic_set(&line->left_seblks, blk_to_erase);
line->meta_distance = lm->meta_distance;
spin_unlock(&line->lock);
- /* Bad blocks do not need to be erased */
- bitmap_copy(line->erase_bitmap, line->blk_bitmap, lm->blk_per_line);
-
kref_init(&line->ref);
return 0;
@@ -1399,13 +1495,6 @@ struct pblk_line *pblk_line_replace_data(struct pblk *pblk)
l_mg->data_line = new;
spin_lock(&l_mg->free_lock);
- if (pblk->state != PBLK_STATE_RUNNING) {
- l_mg->data_line = NULL;
- l_mg->data_next = NULL;
- spin_unlock(&l_mg->free_lock);
- goto out;
- }
-
pblk_line_setup_metadata(new, l_mg, &pblk->lm);
spin_unlock(&l_mg->free_lock);
@@ -1585,12 +1674,14 @@ static void pblk_line_should_sync_meta(struct pblk *pblk)
void pblk_line_close(struct pblk *pblk, struct pblk_line *line)
{
+ struct nvm_tgt_dev *dev = pblk->dev;
+ struct nvm_geo *geo = &dev->geo;
+ struct pblk_line_meta *lm = &pblk->lm;
struct pblk_line_mgmt *l_mg = &pblk->l_mg;
struct list_head *move_list;
+ int i;
#ifdef CONFIG_NVM_DEBUG
- struct pblk_line_meta *lm = &pblk->lm;
-
WARN(!bitmap_full(line->map_bitmap, lm->sec_per_line),
"pblk: corrupt closed line %d\n", line->id);
#endif
@@ -1612,6 +1703,15 @@ void pblk_line_close(struct pblk *pblk, struct pblk_line *line)
line->smeta = NULL;
line->emeta = NULL;
+ for (i = 0; i < lm->blk_per_line; i++) {
+ struct pblk_lun *rlun = &pblk->luns[i];
+ int pos = pblk_ppa_to_pos(geo, rlun->bppa);
+ int state = line->chks[pos].state;
+
+ if (!(state & NVM_CHK_ST_OFFLINE))
+ state = NVM_CHK_ST_CLOSED;
+ }
+
spin_unlock(&line->lock);
spin_unlock(&l_mg->gc_lock);
}
@@ -1622,11 +1722,16 @@ void pblk_line_close_meta(struct pblk *pblk, struct pblk_line *line)
struct pblk_line_meta *lm = &pblk->lm;
struct pblk_emeta *emeta = line->emeta;
struct line_emeta *emeta_buf = emeta->buf;
+ struct wa_counters *wa = emeta_to_wa(lm, emeta_buf);
/* No need for exact vsc value; avoid a big line lock and take aprox. */
memcpy(emeta_to_vsc(pblk, emeta_buf), l_mg->vsc_list, lm->vsc_list_len);
memcpy(emeta_to_bb(emeta_buf), line->blk_bitmap, lm->blk_bitmap_len);
+ wa->user = cpu_to_le64(atomic64_read(&pblk->user_wa));
+ wa->pad = cpu_to_le64(atomic64_read(&pblk->pad_wa));
+ wa->gc = cpu_to_le64(atomic64_read(&pblk->gc_wa));
+
emeta_buf->nr_valid_lbas = cpu_to_le64(line->nr_valid_lbas);
emeta_buf->crc = cpu_to_le32(pblk_calc_emeta_crc(pblk, emeta_buf));
@@ -1680,8 +1785,8 @@ static void __pblk_down_page(struct pblk *pblk, struct ppa_addr *ppa_list,
int i;
for (i = 1; i < nr_ppas; i++)
- WARN_ON(ppa_list[0].g.lun != ppa_list[i].g.lun ||
- ppa_list[0].g.ch != ppa_list[i].g.ch);
+ WARN_ON(ppa_list[0].a.lun != ppa_list[i].a.lun ||
+ ppa_list[0].a.ch != ppa_list[i].a.ch);
#endif
ret = down_timeout(&rlun->wr_sem, msecs_to_jiffies(30000));
@@ -1725,8 +1830,8 @@ void pblk_up_page(struct pblk *pblk, struct ppa_addr *ppa_list, int nr_ppas)
int i;
for (i = 1; i < nr_ppas; i++)
- WARN_ON(ppa_list[0].g.lun != ppa_list[i].g.lun ||
- ppa_list[0].g.ch != ppa_list[i].g.ch);
+ WARN_ON(ppa_list[0].a.lun != ppa_list[i].a.lun ||
+ ppa_list[0].a.ch != ppa_list[i].a.ch);
#endif
rlun = &pblk->luns[pos];
@@ -1739,10 +1844,10 @@ void pblk_up_rq(struct pblk *pblk, struct ppa_addr *ppa_list, int nr_ppas,
struct nvm_tgt_dev *dev = pblk->dev;
struct nvm_geo *geo = &dev->geo;
struct pblk_lun *rlun;
- int nr_luns = geo->all_luns;
+ int num_lun = geo->all_luns;
int bit = -1;
- while ((bit = find_next_bit(lun_bitmap, nr_luns, bit + 1)) < nr_luns) {
+ while ((bit = find_next_bit(lun_bitmap, num_lun, bit + 1)) < num_lun) {
rlun = &pblk->luns[bit];
up(&rlun->wr_sem);
}
@@ -1829,6 +1934,7 @@ void pblk_update_map_dev(struct pblk *pblk, sector_t lba,
#endif
/* Invalidate and discard padded entries */
if (lba == ADDR_EMPTY) {
+ atomic64_inc(&pblk->pad_wa);
#ifdef CONFIG_NVM_DEBUG
atomic_long_inc(&pblk->padded_wb);
#endif
diff --git a/drivers/lightnvm/pblk-gc.c b/drivers/lightnvm/pblk-gc.c
index 3d899383666e..6851a5c67189 100644
--- a/drivers/lightnvm/pblk-gc.c
+++ b/drivers/lightnvm/pblk-gc.c
@@ -88,7 +88,7 @@ static void pblk_gc_line_ws(struct work_struct *work)
up(&gc->gc_sem);
- gc_rq->data = vmalloc(gc_rq->nr_secs * geo->sec_size);
+ gc_rq->data = vmalloc(gc_rq->nr_secs * geo->csecs);
if (!gc_rq->data) {
pr_err("pblk: could not GC line:%d (%d/%d)\n",
line->id, *line->vsc, gc_rq->nr_secs);
@@ -147,10 +147,8 @@ static void pblk_gc_line_prepare_ws(struct work_struct *work)
int ret;
invalid_bitmap = kmalloc(lm->sec_bitmap_len, GFP_KERNEL);
- if (!invalid_bitmap) {
- pr_err("pblk: could not allocate GC invalid bitmap\n");
+ if (!invalid_bitmap)
goto fail_free_ws;
- }
emeta_buf = pblk_malloc(lm->emeta_len[0], l_mg->emeta_alloc_type,
GFP_KERNEL);
@@ -666,12 +664,10 @@ void pblk_gc_exit(struct pblk *pblk)
kthread_stop(gc->gc_reader_ts);
flush_workqueue(gc->gc_reader_wq);
- if (gc->gc_reader_wq)
- destroy_workqueue(gc->gc_reader_wq);
+ destroy_workqueue(gc->gc_reader_wq);
flush_workqueue(gc->gc_line_reader_wq);
- if (gc->gc_line_reader_wq)
- destroy_workqueue(gc->gc_line_reader_wq);
+ destroy_workqueue(gc->gc_line_reader_wq);
if (gc->gc_writer_ts)
kthread_stop(gc->gc_writer_ts);
diff --git a/drivers/lightnvm/pblk-init.c b/drivers/lightnvm/pblk-init.c
index 93d671ca518e..91a5bc2556a3 100644
--- a/drivers/lightnvm/pblk-init.c
+++ b/drivers/lightnvm/pblk-init.c
@@ -80,7 +80,7 @@ static size_t pblk_trans_map_size(struct pblk *pblk)
{
int entry_size = 8;
- if (pblk->ppaf_bitsize < 32)
+ if (pblk->addrf_len < 32)
entry_size = 4;
return entry_size * pblk->rl.nr_secs;
@@ -103,7 +103,40 @@ static void pblk_l2p_free(struct pblk *pblk)
vfree(pblk->trans_map);
}
-static int pblk_l2p_init(struct pblk *pblk)
+static int pblk_l2p_recover(struct pblk *pblk, bool factory_init)
+{
+ struct pblk_line *line = NULL;
+
+ if (factory_init) {
+ pblk_setup_uuid(pblk);
+ } else {
+ line = pblk_recov_l2p(pblk);
+ if (IS_ERR(line)) {
+ pr_err("pblk: could not recover l2p table\n");
+ return -EFAULT;
+ }
+ }
+
+#ifdef CONFIG_NVM_DEBUG
+ pr_info("pblk init: L2P CRC: %x\n", pblk_l2p_crc(pblk));
+#endif
+
+ /* Free full lines directly as GC has not been started yet */
+ pblk_gc_free_full_lines(pblk);
+
+ if (!line) {
+ /* Configure next line for user data */
+ line = pblk_line_get_first_data(pblk);
+ if (!line) {
+ pr_err("pblk: line list corrupted\n");
+ return -EFAULT;
+ }
+ }
+
+ return 0;
+}
+
+static int pblk_l2p_init(struct pblk *pblk, bool factory_init)
{
sector_t i;
struct ppa_addr ppa;
@@ -119,7 +152,7 @@ static int pblk_l2p_init(struct pblk *pblk)
for (i = 0; i < pblk->rl.nr_secs; i++)
pblk_trans_map_set(pblk, i, ppa);
- return 0;
+ return pblk_l2p_recover(pblk, factory_init);
}
static void pblk_rwb_free(struct pblk *pblk)
@@ -146,7 +179,7 @@ static int pblk_rwb_init(struct pblk *pblk)
return -ENOMEM;
power_size = get_count_order(nr_entries);
- power_seg_sz = get_count_order(geo->sec_size);
+ power_seg_sz = get_count_order(geo->csecs);
return pblk_rb_init(&pblk->rwb, entries, power_size, power_seg_sz);
}
@@ -154,47 +187,103 @@ static int pblk_rwb_init(struct pblk *pblk)
/* Minimum pages needed within a lun */
#define ADDR_POOL_SIZE 64
-static int pblk_set_ppaf(struct pblk *pblk)
+static int pblk_set_addrf_12(struct nvm_geo *geo, struct nvm_addrf_12 *dst)
{
- struct nvm_tgt_dev *dev = pblk->dev;
- struct nvm_geo *geo = &dev->geo;
- struct nvm_addr_format ppaf = geo->ppaf;
+ struct nvm_addrf_12 *src = (struct nvm_addrf_12 *)&geo->addrf;
int power_len;
/* Re-calculate channel and lun format to adapt to configuration */
- power_len = get_count_order(geo->nr_chnls);
- if (1 << power_len != geo->nr_chnls) {
+ power_len = get_count_order(geo->num_ch);
+ if (1 << power_len != geo->num_ch) {
pr_err("pblk: supports only power-of-two channel config.\n");
return -EINVAL;
}
- ppaf.ch_len = power_len;
+ dst->ch_len = power_len;
- power_len = get_count_order(geo->nr_luns);
- if (1 << power_len != geo->nr_luns) {
+ power_len = get_count_order(geo->num_lun);
+ if (1 << power_len != geo->num_lun) {
pr_err("pblk: supports only power-of-two LUN config.\n");
return -EINVAL;
}
- ppaf.lun_len = power_len;
-
- pblk->ppaf.sec_offset = 0;
- pblk->ppaf.pln_offset = ppaf.sect_len;
- pblk->ppaf.ch_offset = pblk->ppaf.pln_offset + ppaf.pln_len;
- pblk->ppaf.lun_offset = pblk->ppaf.ch_offset + ppaf.ch_len;
- pblk->ppaf.pg_offset = pblk->ppaf.lun_offset + ppaf.lun_len;
- pblk->ppaf.blk_offset = pblk->ppaf.pg_offset + ppaf.pg_len;
- pblk->ppaf.sec_mask = (1ULL << ppaf.sect_len) - 1;
- pblk->ppaf.pln_mask = ((1ULL << ppaf.pln_len) - 1) <<
- pblk->ppaf.pln_offset;
- pblk->ppaf.ch_mask = ((1ULL << ppaf.ch_len) - 1) <<
- pblk->ppaf.ch_offset;
- pblk->ppaf.lun_mask = ((1ULL << ppaf.lun_len) - 1) <<
- pblk->ppaf.lun_offset;
- pblk->ppaf.pg_mask = ((1ULL << ppaf.pg_len) - 1) <<
- pblk->ppaf.pg_offset;
- pblk->ppaf.blk_mask = ((1ULL << ppaf.blk_len) - 1) <<
- pblk->ppaf.blk_offset;
-
- pblk->ppaf_bitsize = pblk->ppaf.blk_offset + ppaf.blk_len;
+ dst->lun_len = power_len;
+
+ dst->blk_len = src->blk_len;
+ dst->pg_len = src->pg_len;
+ dst->pln_len = src->pln_len;
+ dst->sec_len = src->sec_len;
+
+ dst->sec_offset = 0;
+ dst->pln_offset = dst->sec_len;
+ dst->ch_offset = dst->pln_offset + dst->pln_len;
+ dst->lun_offset = dst->ch_offset + dst->ch_len;
+ dst->pg_offset = dst->lun_offset + dst->lun_len;
+ dst->blk_offset = dst->pg_offset + dst->pg_len;
+
+ dst->sec_mask = ((1ULL << dst->sec_len) - 1) << dst->sec_offset;
+ dst->pln_mask = ((1ULL << dst->pln_len) - 1) << dst->pln_offset;
+ dst->ch_mask = ((1ULL << dst->ch_len) - 1) << dst->ch_offset;
+ dst->lun_mask = ((1ULL << dst->lun_len) - 1) << dst->lun_offset;
+ dst->pg_mask = ((1ULL << dst->pg_len) - 1) << dst->pg_offset;
+ dst->blk_mask = ((1ULL << dst->blk_len) - 1) << dst->blk_offset;
+
+ return dst->blk_offset + src->blk_len;
+}
+
+static int pblk_set_addrf_20(struct nvm_geo *geo, struct nvm_addrf *adst,
+ struct pblk_addrf *udst)
+{
+ struct nvm_addrf *src = &geo->addrf;
+
+ adst->ch_len = get_count_order(geo->num_ch);
+ adst->lun_len = get_count_order(geo->num_lun);
+ adst->chk_len = src->chk_len;
+ adst->sec_len = src->sec_len;
+
+ adst->sec_offset = 0;
+ adst->ch_offset = adst->sec_len;
+ adst->lun_offset = adst->ch_offset + adst->ch_len;
+ adst->chk_offset = adst->lun_offset + adst->lun_len;
+
+ adst->sec_mask = ((1ULL << adst->sec_len) - 1) << adst->sec_offset;
+ adst->chk_mask = ((1ULL << adst->chk_len) - 1) << adst->chk_offset;
+ adst->lun_mask = ((1ULL << adst->lun_len) - 1) << adst->lun_offset;
+ adst->ch_mask = ((1ULL << adst->ch_len) - 1) << adst->ch_offset;
+
+ udst->sec_stripe = geo->ws_opt;
+ udst->ch_stripe = geo->num_ch;
+ udst->lun_stripe = geo->num_lun;
+
+ udst->sec_lun_stripe = udst->sec_stripe * udst->ch_stripe;
+ udst->sec_ws_stripe = udst->sec_lun_stripe * udst->lun_stripe;
+
+ return adst->chk_offset + adst->chk_len;
+}
+
+static int pblk_set_addrf(struct pblk *pblk)
+{
+ struct nvm_tgt_dev *dev = pblk->dev;
+ struct nvm_geo *geo = &dev->geo;
+ int mod;
+
+ switch (geo->version) {
+ case NVM_OCSSD_SPEC_12:
+ div_u64_rem(geo->clba, pblk->min_write_pgs, &mod);
+ if (mod) {
+ pr_err("pblk: bad configuration of sectors/pages\n");
+ return -EINVAL;
+ }
+
+ pblk->addrf_len = pblk_set_addrf_12(geo, (void *)&pblk->addrf);
+ break;
+ case NVM_OCSSD_SPEC_20:
+ pblk->addrf_len = pblk_set_addrf_20(geo, (void *)&pblk->addrf,
+ &pblk->uaddrf);
+ break;
+ default:
+ pr_err("pblk: OCSSD revision not supported (%d)\n",
+ geo->version);
+ return -EINVAL;
+ }
return 0;
}
@@ -252,16 +341,41 @@ static int pblk_core_init(struct pblk *pblk)
{
struct nvm_tgt_dev *dev = pblk->dev;
struct nvm_geo *geo = &dev->geo;
+ int max_write_ppas;
- pblk->pgs_in_buffer = NVM_MEM_PAGE_WRITE * geo->sec_per_pg *
- geo->nr_planes * geo->all_luns;
+ atomic64_set(&pblk->user_wa, 0);
+ atomic64_set(&pblk->pad_wa, 0);
+ atomic64_set(&pblk->gc_wa, 0);
+ pblk->user_rst_wa = 0;
+ pblk->pad_rst_wa = 0;
+ pblk->gc_rst_wa = 0;
- if (pblk_init_global_caches(pblk))
+ atomic64_set(&pblk->nr_flush, 0);
+ pblk->nr_flush_rst = 0;
+
+ pblk->pgs_in_buffer = geo->mw_cunits * geo->all_luns;
+
+ pblk->min_write_pgs = geo->ws_opt * (geo->csecs / PAGE_SIZE);
+ max_write_ppas = pblk->min_write_pgs * geo->all_luns;
+ pblk->max_write_pgs = min_t(int, max_write_ppas, NVM_MAX_VLBA);
+ pblk_set_sec_per_write(pblk, pblk->min_write_pgs);
+
+ if (pblk->max_write_pgs > PBLK_MAX_REQ_ADDRS) {
+ pr_err("pblk: vector list too big(%u > %u)\n",
+ pblk->max_write_pgs, PBLK_MAX_REQ_ADDRS);
+ return -EINVAL;
+ }
+
+ pblk->pad_dist = kzalloc((pblk->min_write_pgs - 1) * sizeof(atomic64_t),
+ GFP_KERNEL);
+ if (!pblk->pad_dist)
return -ENOMEM;
+ if (pblk_init_global_caches(pblk))
+ goto fail_free_pad_dist;
+
/* Internal bios can be at most the sectors signaled by the device. */
- pblk->page_bio_pool = mempool_create_page_pool(nvm_max_phys_sects(dev),
- 0);
+ pblk->page_bio_pool = mempool_create_page_pool(NVM_MAX_VLBA, 0);
if (!pblk->page_bio_pool)
goto free_global_caches;
@@ -305,13 +419,11 @@ static int pblk_core_init(struct pblk *pblk)
if (!pblk->r_end_wq)
goto free_bb_wq;
- if (pblk_set_ppaf(pblk))
- goto free_r_end_wq;
-
- if (pblk_rwb_init(pblk))
+ if (pblk_set_addrf(pblk))
goto free_r_end_wq;
INIT_LIST_HEAD(&pblk->compl_list);
+
return 0;
free_r_end_wq:
@@ -334,6 +446,8 @@ free_page_bio_pool:
mempool_destroy(pblk->page_bio_pool);
free_global_caches:
pblk_free_global_caches(pblk);
+fail_free_pad_dist:
+ kfree(pblk->pad_dist);
return -ENOMEM;
}
@@ -355,20 +469,31 @@ static void pblk_core_free(struct pblk *pblk)
mempool_destroy(pblk->e_rq_pool);
mempool_destroy(pblk->w_rq_pool);
- pblk_rwb_free(pblk);
-
pblk_free_global_caches(pblk);
+ kfree(pblk->pad_dist);
}
-static void pblk_luns_free(struct pblk *pblk)
+static void pblk_line_mg_free(struct pblk *pblk)
{
- kfree(pblk->luns);
+ struct pblk_line_mgmt *l_mg = &pblk->l_mg;
+ int i;
+
+ kfree(l_mg->bb_template);
+ kfree(l_mg->bb_aux);
+ kfree(l_mg->vsc_list);
+
+ for (i = 0; i < PBLK_DATA_LINES; i++) {
+ kfree(l_mg->sline_meta[i]);
+ pblk_mfree(l_mg->eline_meta[i]->buf, l_mg->emeta_alloc_type);
+ kfree(l_mg->eline_meta[i]);
+ }
}
-static void pblk_free_line_bitmaps(struct pblk_line *line)
+static void pblk_line_meta_free(struct pblk_line *line)
{
kfree(line->blk_bitmap);
kfree(line->erase_bitmap);
+ kfree(line->chks);
}
static void pblk_lines_free(struct pblk *pblk)
@@ -382,40 +507,21 @@ static void pblk_lines_free(struct pblk *pblk)
line = &pblk->lines[i];
pblk_line_free(pblk, line);
- pblk_free_line_bitmaps(line);
+ pblk_line_meta_free(line);
}
spin_unlock(&l_mg->free_lock);
-}
-
-static void pblk_line_meta_free(struct pblk *pblk)
-{
- struct pblk_line_mgmt *l_mg = &pblk->l_mg;
- int i;
-
- kfree(l_mg->bb_template);
- kfree(l_mg->bb_aux);
- kfree(l_mg->vsc_list);
- for (i = 0; i < PBLK_DATA_LINES; i++) {
- kfree(l_mg->sline_meta[i]);
- pblk_mfree(l_mg->eline_meta[i]->buf, l_mg->emeta_alloc_type);
- kfree(l_mg->eline_meta[i]);
- }
+ pblk_line_mg_free(pblk);
+ kfree(pblk->luns);
kfree(pblk->lines);
}
-static int pblk_bb_discovery(struct nvm_tgt_dev *dev, struct pblk_lun *rlun)
+static int pblk_bb_get_tbl(struct nvm_tgt_dev *dev, struct pblk_lun *rlun,
+ u8 *blks, int nr_blks)
{
- struct nvm_geo *geo = &dev->geo;
struct ppa_addr ppa;
- u8 *blks;
- int nr_blks, ret;
-
- nr_blks = geo->nr_chks * geo->plane_mode;
- blks = kmalloc(nr_blks, GFP_KERNEL);
- if (!blks)
- return -ENOMEM;
+ int ret;
ppa.ppa = 0;
ppa.g.ch = rlun->bppa.g.ch;
@@ -423,69 +529,64 @@ static int pblk_bb_discovery(struct nvm_tgt_dev *dev, struct pblk_lun *rlun)
ret = nvm_get_tgt_bb_tbl(dev, ppa, blks);
if (ret)
- goto out;
+ return ret;
nr_blks = nvm_bb_tbl_fold(dev->parent, blks, nr_blks);
- if (nr_blks < 0) {
- ret = nr_blks;
- goto out;
- }
-
- rlun->bb_list = blks;
+ if (nr_blks < 0)
+ return -EIO;
return 0;
-out:
- kfree(blks);
- return ret;
}
-static int pblk_bb_line(struct pblk *pblk, struct pblk_line *line,
- int blk_per_line)
+static void *pblk_bb_get_meta(struct pblk *pblk)
{
struct nvm_tgt_dev *dev = pblk->dev;
struct nvm_geo *geo = &dev->geo;
- struct pblk_lun *rlun;
- int bb_cnt = 0;
- int i;
+ u8 *meta;
+ int i, nr_blks, blk_per_lun;
+ int ret;
- for (i = 0; i < blk_per_line; i++) {
- rlun = &pblk->luns[i];
- if (rlun->bb_list[line->id] == NVM_BLK_T_FREE)
- continue;
+ blk_per_lun = geo->num_chk * geo->pln_mode;
+ nr_blks = blk_per_lun * geo->all_luns;
+
+ meta = kmalloc(nr_blks, GFP_KERNEL);
+ if (!meta)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < geo->all_luns; i++) {
+ struct pblk_lun *rlun = &pblk->luns[i];
+ u8 *meta_pos = meta + i * blk_per_lun;
- set_bit(pblk_ppa_to_pos(geo, rlun->bppa), line->blk_bitmap);
- bb_cnt++;
+ ret = pblk_bb_get_tbl(dev, rlun, meta_pos, blk_per_lun);
+ if (ret) {
+ kfree(meta);
+ return ERR_PTR(-EIO);
+ }
}
- return bb_cnt;
+ return meta;
}
-static int pblk_alloc_line_bitmaps(struct pblk *pblk, struct pblk_line *line)
+static void *pblk_chunk_get_meta(struct pblk *pblk)
{
- struct pblk_line_meta *lm = &pblk->lm;
-
- line->blk_bitmap = kzalloc(lm->blk_bitmap_len, GFP_KERNEL);
- if (!line->blk_bitmap)
- return -ENOMEM;
-
- line->erase_bitmap = kzalloc(lm->blk_bitmap_len, GFP_KERNEL);
- if (!line->erase_bitmap) {
- kfree(line->blk_bitmap);
- return -ENOMEM;
- }
+ struct nvm_tgt_dev *dev = pblk->dev;
+ struct nvm_geo *geo = &dev->geo;
- return 0;
+ if (geo->version == NVM_OCSSD_SPEC_12)
+ return pblk_bb_get_meta(pblk);
+ else
+ return pblk_chunk_get_info(pblk);
}
-static int pblk_luns_init(struct pblk *pblk, struct ppa_addr *luns)
+static int pblk_luns_init(struct pblk *pblk)
{
struct nvm_tgt_dev *dev = pblk->dev;
struct nvm_geo *geo = &dev->geo;
struct pblk_lun *rlun;
- int i, ret;
+ int i;
/* TODO: Implement unbalanced LUN support */
- if (geo->nr_luns < 0) {
+ if (geo->num_lun < 0) {
pr_err("pblk: unbalanced LUN config.\n");
return -EINVAL;
}
@@ -497,58 +598,19 @@ static int pblk_luns_init(struct pblk *pblk, struct ppa_addr *luns)
for (i = 0; i < geo->all_luns; i++) {
/* Stripe across channels */
- int ch = i % geo->nr_chnls;
- int lun_raw = i / geo->nr_chnls;
- int lunid = lun_raw + ch * geo->nr_luns;
+ int ch = i % geo->num_ch;
+ int lun_raw = i / geo->num_ch;
+ int lunid = lun_raw + ch * geo->num_lun;
rlun = &pblk->luns[i];
- rlun->bppa = luns[lunid];
+ rlun->bppa = dev->luns[lunid];
sema_init(&rlun->wr_sem, 1);
-
- ret = pblk_bb_discovery(dev, rlun);
- if (ret) {
- while (--i >= 0)
- kfree(pblk->luns[i].bb_list);
- return ret;
- }
}
return 0;
}
-static int pblk_lines_configure(struct pblk *pblk, int flags)
-{
- struct pblk_line *line = NULL;
- int ret = 0;
-
- if (!(flags & NVM_TARGET_FACTORY)) {
- line = pblk_recov_l2p(pblk);
- if (IS_ERR(line)) {
- pr_err("pblk: could not recover l2p table\n");
- ret = -EFAULT;
- }
- }
-
-#ifdef CONFIG_NVM_DEBUG
- pr_info("pblk init: L2P CRC: %x\n", pblk_l2p_crc(pblk));
-#endif
-
- /* Free full lines directly as GC has not been started yet */
- pblk_gc_free_full_lines(pblk);
-
- if (!line) {
- /* Configure next line for user data */
- line = pblk_line_get_first_data(pblk);
- if (!line) {
- pr_err("pblk: line list corrupted\n");
- ret = -EFAULT;
- }
- }
-
- return ret;
-}
-
/* See comment over struct line_emeta definition */
static unsigned int calc_emeta_len(struct pblk *pblk)
{
@@ -559,19 +621,19 @@ static unsigned int calc_emeta_len(struct pblk *pblk)
/* Round to sector size so that lba_list starts on its own sector */
lm->emeta_sec[1] = DIV_ROUND_UP(
- sizeof(struct line_emeta) + lm->blk_bitmap_len,
- geo->sec_size);
- lm->emeta_len[1] = lm->emeta_sec[1] * geo->sec_size;
+ sizeof(struct line_emeta) + lm->blk_bitmap_len +
+ sizeof(struct wa_counters), geo->csecs);
+ lm->emeta_len[1] = lm->emeta_sec[1] * geo->csecs;
/* Round to sector size so that vsc_list starts on its own sector */
lm->dsec_per_line = lm->sec_per_line - lm->emeta_sec[0];
lm->emeta_sec[2] = DIV_ROUND_UP(lm->dsec_per_line * sizeof(u64),
- geo->sec_size);
- lm->emeta_len[2] = lm->emeta_sec[2] * geo->sec_size;
+ geo->csecs);
+ lm->emeta_len[2] = lm->emeta_sec[2] * geo->csecs;
lm->emeta_sec[3] = DIV_ROUND_UP(l_mg->nr_lines * sizeof(u32),
- geo->sec_size);
- lm->emeta_len[3] = lm->emeta_sec[3] * geo->sec_size;
+ geo->csecs);
+ lm->emeta_len[3] = lm->emeta_sec[3] * geo->csecs;
lm->vsc_list_len = l_mg->nr_lines * sizeof(u32);
@@ -602,23 +664,211 @@ static void pblk_set_provision(struct pblk *pblk, long nr_free_blks)
* on user capacity consider only provisioned blocks
*/
pblk->rl.total_blocks = nr_free_blks;
- pblk->rl.nr_secs = nr_free_blks * geo->sec_per_chk;
+ pblk->rl.nr_secs = nr_free_blks * geo->clba;
/* Consider sectors used for metadata */
sec_meta = (lm->smeta_sec + lm->emeta_sec[0]) * l_mg->nr_free_lines;
- blk_meta = DIV_ROUND_UP(sec_meta, geo->sec_per_chk);
+ blk_meta = DIV_ROUND_UP(sec_meta, geo->clba);
- pblk->capacity = (provisioned - blk_meta) * geo->sec_per_chk;
+ pblk->capacity = (provisioned - blk_meta) * geo->clba;
atomic_set(&pblk->rl.free_blocks, nr_free_blks);
atomic_set(&pblk->rl.free_user_blocks, nr_free_blks);
}
-static int pblk_lines_alloc_metadata(struct pblk *pblk)
+static int pblk_setup_line_meta_12(struct pblk *pblk, struct pblk_line *line,
+ void *chunk_meta)
+{
+ struct nvm_tgt_dev *dev = pblk->dev;
+ struct nvm_geo *geo = &dev->geo;
+ struct pblk_line_meta *lm = &pblk->lm;
+ int i, chk_per_lun, nr_bad_chks = 0;
+
+ chk_per_lun = geo->num_chk * geo->pln_mode;
+
+ for (i = 0; i < lm->blk_per_line; i++) {
+ struct pblk_lun *rlun = &pblk->luns[i];
+ struct nvm_chk_meta *chunk;
+ int pos = pblk_ppa_to_pos(geo, rlun->bppa);
+ u8 *lun_bb_meta = chunk_meta + pos * chk_per_lun;
+
+ chunk = &line->chks[pos];
+
+ /*
+ * In 1.2 spec. chunk state is not persisted by the device. Thus
+ * some of the values are reset each time pblk is instantiated.
+ */
+ if (lun_bb_meta[line->id] == NVM_BLK_T_FREE)
+ chunk->state = NVM_CHK_ST_FREE;
+ else
+ chunk->state = NVM_CHK_ST_OFFLINE;
+
+ chunk->type = NVM_CHK_TP_W_SEQ;
+ chunk->wi = 0;
+ chunk->slba = -1;
+ chunk->cnlb = geo->clba;
+ chunk->wp = 0;
+
+ if (!(chunk->state & NVM_CHK_ST_OFFLINE))
+ continue;
+
+ set_bit(pos, line->blk_bitmap);
+ nr_bad_chks++;
+ }
+
+ return nr_bad_chks;
+}
+
+static int pblk_setup_line_meta_20(struct pblk *pblk, struct pblk_line *line,
+ struct nvm_chk_meta *meta)
+{
+ struct nvm_tgt_dev *dev = pblk->dev;
+ struct nvm_geo *geo = &dev->geo;
+ struct pblk_line_meta *lm = &pblk->lm;
+ int i, nr_bad_chks = 0;
+
+ for (i = 0; i < lm->blk_per_line; i++) {
+ struct pblk_lun *rlun = &pblk->luns[i];
+ struct nvm_chk_meta *chunk;
+ struct nvm_chk_meta *chunk_meta;
+ struct ppa_addr ppa;
+ int pos;
+
+ ppa = rlun->bppa;
+ pos = pblk_ppa_to_pos(geo, ppa);
+ chunk = &line->chks[pos];
+
+ ppa.m.chk = line->id;
+ chunk_meta = pblk_chunk_get_off(pblk, meta, ppa);
+
+ chunk->state = chunk_meta->state;
+ chunk->type = chunk_meta->type;
+ chunk->wi = chunk_meta->wi;
+ chunk->slba = chunk_meta->slba;
+ chunk->cnlb = chunk_meta->cnlb;
+ chunk->wp = chunk_meta->wp;
+
+ if (!(chunk->state & NVM_CHK_ST_OFFLINE))
+ continue;
+
+ if (chunk->type & NVM_CHK_TP_SZ_SPEC) {
+ WARN_ONCE(1, "pblk: custom-sized chunks unsupported\n");
+ continue;
+ }
+
+ set_bit(pos, line->blk_bitmap);
+ nr_bad_chks++;
+ }
+
+ return nr_bad_chks;
+}
+
+static long pblk_setup_line_meta(struct pblk *pblk, struct pblk_line *line,
+ void *chunk_meta, int line_id)
{
+ struct nvm_tgt_dev *dev = pblk->dev;
+ struct nvm_geo *geo = &dev->geo;
struct pblk_line_mgmt *l_mg = &pblk->l_mg;
struct pblk_line_meta *lm = &pblk->lm;
- int i;
+ long nr_bad_chks, chk_in_line;
+
+ line->pblk = pblk;
+ line->id = line_id;
+ line->type = PBLK_LINETYPE_FREE;
+ line->state = PBLK_LINESTATE_NEW;
+ line->gc_group = PBLK_LINEGC_NONE;
+ line->vsc = &l_mg->vsc_list[line_id];
+ spin_lock_init(&line->lock);
+
+ if (geo->version == NVM_OCSSD_SPEC_12)
+ nr_bad_chks = pblk_setup_line_meta_12(pblk, line, chunk_meta);
+ else
+ nr_bad_chks = pblk_setup_line_meta_20(pblk, line, chunk_meta);
+
+ chk_in_line = lm->blk_per_line - nr_bad_chks;
+ if (nr_bad_chks < 0 || nr_bad_chks > lm->blk_per_line ||
+ chk_in_line < lm->min_blk_line) {
+ line->state = PBLK_LINESTATE_BAD;
+ list_add_tail(&line->list, &l_mg->bad_list);
+ return 0;
+ }
+
+ atomic_set(&line->blk_in_line, chk_in_line);
+ list_add_tail(&line->list, &l_mg->free_list);
+ l_mg->nr_free_lines++;
+
+ return chk_in_line;
+}
+
+static int pblk_alloc_line_meta(struct pblk *pblk, struct pblk_line *line)
+{
+ struct pblk_line_meta *lm = &pblk->lm;
+
+ line->blk_bitmap = kzalloc(lm->blk_bitmap_len, GFP_KERNEL);
+ if (!line->blk_bitmap)
+ return -ENOMEM;
+
+ line->erase_bitmap = kzalloc(lm->blk_bitmap_len, GFP_KERNEL);
+ if (!line->erase_bitmap) {
+ kfree(line->blk_bitmap);
+ return -ENOMEM;
+ }
+
+ line->chks = kmalloc(lm->blk_per_line * sizeof(struct nvm_chk_meta),
+ GFP_KERNEL);
+ if (!line->chks) {
+ kfree(line->erase_bitmap);
+ kfree(line->blk_bitmap);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int pblk_line_mg_init(struct pblk *pblk)
+{
+ struct nvm_tgt_dev *dev = pblk->dev;
+ struct nvm_geo *geo = &dev->geo;
+ struct pblk_line_mgmt *l_mg = &pblk->l_mg;
+ struct pblk_line_meta *lm = &pblk->lm;
+ int i, bb_distance;
+
+ l_mg->nr_lines = geo->num_chk;
+ l_mg->log_line = l_mg->data_line = NULL;
+ l_mg->l_seq_nr = l_mg->d_seq_nr = 0;
+ l_mg->nr_free_lines = 0;
+ bitmap_zero(&l_mg->meta_bitmap, PBLK_DATA_LINES);
+
+ INIT_LIST_HEAD(&l_mg->free_list);
+ INIT_LIST_HEAD(&l_mg->corrupt_list);
+ INIT_LIST_HEAD(&l_mg->bad_list);
+ INIT_LIST_HEAD(&l_mg->gc_full_list);
+ INIT_LIST_HEAD(&l_mg->gc_high_list);
+ INIT_LIST_HEAD(&l_mg->gc_mid_list);
+ INIT_LIST_HEAD(&l_mg->gc_low_list);
+ INIT_LIST_HEAD(&l_mg->gc_empty_list);
+
+ INIT_LIST_HEAD(&l_mg->emeta_list);
+
+ l_mg->gc_lists[0] = &l_mg->gc_high_list;
+ l_mg->gc_lists[1] = &l_mg->gc_mid_list;
+ l_mg->gc_lists[2] = &l_mg->gc_low_list;
+
+ spin_lock_init(&l_mg->free_lock);
+ spin_lock_init(&l_mg->close_lock);
+ spin_lock_init(&l_mg->gc_lock);
+
+ l_mg->vsc_list = kcalloc(l_mg->nr_lines, sizeof(__le32), GFP_KERNEL);
+ if (!l_mg->vsc_list)
+ goto fail;
+
+ l_mg->bb_template = kzalloc(lm->sec_bitmap_len, GFP_KERNEL);
+ if (!l_mg->bb_template)
+ goto fail_free_vsc_list;
+
+ l_mg->bb_aux = kzalloc(lm->sec_bitmap_len, GFP_KERNEL);
+ if (!l_mg->bb_aux)
+ goto fail_free_bb_template;
/* smeta is always small enough to fit on a kmalloc memory allocation,
* emeta depends on the number of LUNs allocated to the pblk instance
@@ -664,13 +914,13 @@ static int pblk_lines_alloc_metadata(struct pblk *pblk)
}
}
- l_mg->vsc_list = kcalloc(l_mg->nr_lines, sizeof(__le32), GFP_KERNEL);
- if (!l_mg->vsc_list)
- goto fail_free_emeta;
-
for (i = 0; i < l_mg->nr_lines; i++)
l_mg->vsc_list[i] = cpu_to_le32(EMPTY_ENTRY);
+ bb_distance = (geo->all_luns) * geo->ws_opt;
+ for (i = 0; i < lm->sec_per_line; i += bb_distance)
+ bitmap_set(l_mg->bb_template, i, geo->ws_opt);
+
return 0;
fail_free_emeta:
@@ -681,50 +931,27 @@ fail_free_emeta:
kfree(l_mg->eline_meta[i]->buf);
kfree(l_mg->eline_meta[i]);
}
-
fail_free_smeta:
for (i = 0; i < PBLK_DATA_LINES; i++)
kfree(l_mg->sline_meta[i]);
-
+ kfree(l_mg->bb_aux);
+fail_free_bb_template:
+ kfree(l_mg->bb_template);
+fail_free_vsc_list:
+ kfree(l_mg->vsc_list);
+fail:
return -ENOMEM;
}
-static int pblk_lines_init(struct pblk *pblk)
+static int pblk_line_meta_init(struct pblk *pblk)
{
struct nvm_tgt_dev *dev = pblk->dev;
struct nvm_geo *geo = &dev->geo;
- struct pblk_line_mgmt *l_mg = &pblk->l_mg;
struct pblk_line_meta *lm = &pblk->lm;
- struct pblk_line *line;
unsigned int smeta_len, emeta_len;
- long nr_bad_blks, nr_free_blks;
- int bb_distance, max_write_ppas, mod;
- int i, ret;
-
- pblk->min_write_pgs = geo->sec_per_pl * (geo->sec_size / PAGE_SIZE);
- max_write_ppas = pblk->min_write_pgs * geo->all_luns;
- pblk->max_write_pgs = (max_write_ppas < nvm_max_phys_sects(dev)) ?
- max_write_ppas : nvm_max_phys_sects(dev);
- pblk_set_sec_per_write(pblk, pblk->min_write_pgs);
-
- if (pblk->max_write_pgs > PBLK_MAX_REQ_ADDRS) {
- pr_err("pblk: cannot support device max_phys_sect\n");
- return -EINVAL;
- }
-
- div_u64_rem(geo->sec_per_chk, pblk->min_write_pgs, &mod);
- if (mod) {
- pr_err("pblk: bad configuration of sectors/pages\n");
- return -EINVAL;
- }
-
- l_mg->nr_lines = geo->nr_chks;
- l_mg->log_line = l_mg->data_line = NULL;
- l_mg->l_seq_nr = l_mg->d_seq_nr = 0;
- l_mg->nr_free_lines = 0;
- bitmap_zero(&l_mg->meta_bitmap, PBLK_DATA_LINES);
+ int i;
- lm->sec_per_line = geo->sec_per_chk * geo->all_luns;
+ lm->sec_per_line = geo->clba * geo->all_luns;
lm->blk_per_line = geo->all_luns;
lm->blk_bitmap_len = BITS_TO_LONGS(geo->all_luns) * sizeof(long);
lm->sec_bitmap_len = BITS_TO_LONGS(lm->sec_per_line) * sizeof(long);
@@ -738,8 +965,8 @@ static int pblk_lines_init(struct pblk *pblk)
*/
i = 1;
add_smeta_page:
- lm->smeta_sec = i * geo->sec_per_pl;
- lm->smeta_len = lm->smeta_sec * geo->sec_size;
+ lm->smeta_sec = i * geo->ws_opt;
+ lm->smeta_len = lm->smeta_sec * geo->csecs;
smeta_len = sizeof(struct line_smeta) + lm->lun_bitmap_len;
if (smeta_len > lm->smeta_len) {
@@ -752,8 +979,8 @@ add_smeta_page:
*/
i = 1;
add_emeta_page:
- lm->emeta_sec[0] = i * geo->sec_per_pl;
- lm->emeta_len[0] = lm->emeta_sec[0] * geo->sec_size;
+ lm->emeta_sec[0] = i * geo->ws_opt;
+ lm->emeta_len[0] = lm->emeta_sec[0] * geo->csecs;
emeta_len = calc_emeta_len(pblk);
if (emeta_len > lm->emeta_len[0]) {
@@ -766,119 +993,75 @@ add_emeta_page:
lm->min_blk_line = 1;
if (geo->all_luns > 1)
lm->min_blk_line += DIV_ROUND_UP(lm->smeta_sec +
- lm->emeta_sec[0], geo->sec_per_chk);
+ lm->emeta_sec[0], geo->clba);
if (lm->min_blk_line > lm->blk_per_line) {
pr_err("pblk: config. not supported. Min. LUN in line:%d\n",
lm->blk_per_line);
- ret = -EINVAL;
- goto fail;
- }
-
- ret = pblk_lines_alloc_metadata(pblk);
- if (ret)
- goto fail;
-
- l_mg->bb_template = kzalloc(lm->sec_bitmap_len, GFP_KERNEL);
- if (!l_mg->bb_template) {
- ret = -ENOMEM;
- goto fail_free_meta;
+ return -EINVAL;
}
- l_mg->bb_aux = kzalloc(lm->sec_bitmap_len, GFP_KERNEL);
- if (!l_mg->bb_aux) {
- ret = -ENOMEM;
- goto fail_free_bb_template;
- }
+ return 0;
+}
- bb_distance = (geo->all_luns) * geo->sec_per_pl;
- for (i = 0; i < lm->sec_per_line; i += bb_distance)
- bitmap_set(l_mg->bb_template, i, geo->sec_per_pl);
+static int pblk_lines_init(struct pblk *pblk)
+{
+ struct pblk_line_mgmt *l_mg = &pblk->l_mg;
+ struct pblk_line *line;
+ void *chunk_meta;
+ long nr_free_chks = 0;
+ int i, ret;
- INIT_LIST_HEAD(&l_mg->free_list);
- INIT_LIST_HEAD(&l_mg->corrupt_list);
- INIT_LIST_HEAD(&l_mg->bad_list);
- INIT_LIST_HEAD(&l_mg->gc_full_list);
- INIT_LIST_HEAD(&l_mg->gc_high_list);
- INIT_LIST_HEAD(&l_mg->gc_mid_list);
- INIT_LIST_HEAD(&l_mg->gc_low_list);
- INIT_LIST_HEAD(&l_mg->gc_empty_list);
+ ret = pblk_line_meta_init(pblk);
+ if (ret)
+ return ret;
- INIT_LIST_HEAD(&l_mg->emeta_list);
+ ret = pblk_line_mg_init(pblk);
+ if (ret)
+ return ret;
- l_mg->gc_lists[0] = &l_mg->gc_high_list;
- l_mg->gc_lists[1] = &l_mg->gc_mid_list;
- l_mg->gc_lists[2] = &l_mg->gc_low_list;
+ ret = pblk_luns_init(pblk);
+ if (ret)
+ goto fail_free_meta;
- spin_lock_init(&l_mg->free_lock);
- spin_lock_init(&l_mg->close_lock);
- spin_lock_init(&l_mg->gc_lock);
+ chunk_meta = pblk_chunk_get_meta(pblk);
+ if (IS_ERR(chunk_meta)) {
+ ret = PTR_ERR(chunk_meta);
+ goto fail_free_luns;
+ }
pblk->lines = kcalloc(l_mg->nr_lines, sizeof(struct pblk_line),
GFP_KERNEL);
if (!pblk->lines) {
ret = -ENOMEM;
- goto fail_free_bb_aux;
+ goto fail_free_chunk_meta;
}
- nr_free_blks = 0;
for (i = 0; i < l_mg->nr_lines; i++) {
- int blk_in_line;
-
line = &pblk->lines[i];
- line->pblk = pblk;
- line->id = i;
- line->type = PBLK_LINETYPE_FREE;
- line->state = PBLK_LINESTATE_FREE;
- line->gc_group = PBLK_LINEGC_NONE;
- line->vsc = &l_mg->vsc_list[i];
- spin_lock_init(&line->lock);
-
- ret = pblk_alloc_line_bitmaps(pblk, line);
+ ret = pblk_alloc_line_meta(pblk, line);
if (ret)
goto fail_free_lines;
- nr_bad_blks = pblk_bb_line(pblk, line, lm->blk_per_line);
- if (nr_bad_blks < 0 || nr_bad_blks > lm->blk_per_line) {
- pblk_free_line_bitmaps(line);
- ret = -EINVAL;
- goto fail_free_lines;
- }
-
- blk_in_line = lm->blk_per_line - nr_bad_blks;
- if (blk_in_line < lm->min_blk_line) {
- line->state = PBLK_LINESTATE_BAD;
- list_add_tail(&line->list, &l_mg->bad_list);
- continue;
- }
-
- nr_free_blks += blk_in_line;
- atomic_set(&line->blk_in_line, blk_in_line);
-
- l_mg->nr_free_lines++;
- list_add_tail(&line->list, &l_mg->free_list);
+ nr_free_chks += pblk_setup_line_meta(pblk, line, chunk_meta, i);
}
- pblk_set_provision(pblk, nr_free_blks);
-
- /* Cleanup per-LUN bad block lists - managed within lines on run-time */
- for (i = 0; i < geo->all_luns; i++)
- kfree(pblk->luns[i].bb_list);
+ pblk_set_provision(pblk, nr_free_chks);
+ kfree(chunk_meta);
return 0;
+
fail_free_lines:
while (--i >= 0)
- pblk_free_line_bitmaps(&pblk->lines[i]);
-fail_free_bb_aux:
- kfree(l_mg->bb_aux);
-fail_free_bb_template:
- kfree(l_mg->bb_template);
+ pblk_line_meta_free(&pblk->lines[i]);
+ kfree(pblk->lines);
+fail_free_chunk_meta:
+ kfree(chunk_meta);
+fail_free_luns:
+ kfree(pblk->luns);
fail_free_meta:
- pblk_line_meta_free(pblk);
-fail:
- for (i = 0; i < geo->all_luns; i++)
- kfree(pblk->luns[i].bb_list);
+ pblk_line_mg_free(pblk);
return ret;
}
@@ -912,18 +1095,17 @@ static void pblk_writer_stop(struct pblk *pblk)
WARN(pblk_rb_sync_count(&pblk->rwb),
"Stopping not fully synced write buffer\n");
+ del_timer_sync(&pblk->wtimer);
if (pblk->writer_ts)
kthread_stop(pblk->writer_ts);
- del_timer(&pblk->wtimer);
}
static void pblk_free(struct pblk *pblk)
{
- pblk_luns_free(pblk);
pblk_lines_free(pblk);
- pblk_line_meta_free(pblk);
- pblk_core_free(pblk);
pblk_l2p_free(pblk);
+ pblk_rwb_free(pblk);
+ pblk_core_free(pblk);
kfree(pblk);
}
@@ -970,9 +1152,17 @@ static void *pblk_init(struct nvm_tgt_dev *dev, struct gendisk *tdisk,
struct pblk *pblk;
int ret;
- if (dev->identity.dom & NVM_RSP_L2P) {
+ /* pblk supports 1.2 and 2.0 versions */
+ if (!(geo->version == NVM_OCSSD_SPEC_12 ||
+ geo->version == NVM_OCSSD_SPEC_20)) {
+ pr_err("pblk: OCSSD version not supported (%u)\n",
+ geo->version);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (geo->version == NVM_OCSSD_SPEC_12 && geo->dom & NVM_RSP_L2P) {
pr_err("pblk: host-side L2P table not supported. (%x)\n",
- dev->identity.dom);
+ geo->dom);
return ERR_PTR(-EINVAL);
}
@@ -988,14 +1178,10 @@ static void *pblk_init(struct nvm_tgt_dev *dev, struct gendisk *tdisk,
spin_lock_init(&pblk->trans_lock);
spin_lock_init(&pblk->lock);
- if (flags & NVM_TARGET_FACTORY)
- pblk_setup_uuid(pblk);
-
#ifdef CONFIG_NVM_DEBUG
atomic_long_set(&pblk->inflight_writes, 0);
atomic_long_set(&pblk->padded_writes, 0);
atomic_long_set(&pblk->padded_wb, 0);
- atomic_long_set(&pblk->nr_flush, 0);
atomic_long_set(&pblk->req_writes, 0);
atomic_long_set(&pblk->sub_writes, 0);
atomic_long_set(&pblk->sync_writes, 0);
@@ -1015,41 +1201,35 @@ static void *pblk_init(struct nvm_tgt_dev *dev, struct gendisk *tdisk,
atomic_long_set(&pblk->write_failed, 0);
atomic_long_set(&pblk->erase_failed, 0);
- ret = pblk_luns_init(pblk, dev->luns);
+ ret = pblk_core_init(pblk);
if (ret) {
- pr_err("pblk: could not initialize luns\n");
+ pr_err("pblk: could not initialize core\n");
goto fail;
}
ret = pblk_lines_init(pblk);
if (ret) {
pr_err("pblk: could not initialize lines\n");
- goto fail_free_luns;
+ goto fail_free_core;
}
- ret = pblk_core_init(pblk);
+ ret = pblk_rwb_init(pblk);
if (ret) {
- pr_err("pblk: could not initialize core\n");
- goto fail_free_line_meta;
+ pr_err("pblk: could not initialize write buffer\n");
+ goto fail_free_lines;
}
- ret = pblk_l2p_init(pblk);
+ ret = pblk_l2p_init(pblk, flags & NVM_TARGET_FACTORY);
if (ret) {
pr_err("pblk: could not initialize maps\n");
- goto fail_free_core;
- }
-
- ret = pblk_lines_configure(pblk, flags);
- if (ret) {
- pr_err("pblk: could not configure lines\n");
- goto fail_free_l2p;
+ goto fail_free_rwb;
}
ret = pblk_writer_init(pblk);
if (ret) {
if (ret != -EINTR)
pr_err("pblk: could not initialize write thread\n");
- goto fail_free_lines;
+ goto fail_free_l2p;
}
ret = pblk_gc_init(pblk);
@@ -1064,10 +1244,10 @@ static void *pblk_init(struct nvm_tgt_dev *dev, struct gendisk *tdisk,
blk_queue_write_cache(tqueue, true, false);
- tqueue->limits.discard_granularity = geo->sec_per_chk * geo->sec_size;
+ tqueue->limits.discard_granularity = geo->clba * geo->csecs;
tqueue->limits.discard_alignment = 0;
blk_queue_max_discard_sectors(tqueue, UINT_MAX >> 9);
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, tqueue);
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, tqueue);
pr_info("pblk(%s): luns:%u, lines:%d, secs:%llu, buf entries:%u\n",
tdisk->disk_name,
@@ -1084,16 +1264,14 @@ static void *pblk_init(struct nvm_tgt_dev *dev, struct gendisk *tdisk,
fail_stop_writer:
pblk_writer_stop(pblk);
-fail_free_lines:
- pblk_lines_free(pblk);
fail_free_l2p:
pblk_l2p_free(pblk);
+fail_free_rwb:
+ pblk_rwb_free(pblk);
+fail_free_lines:
+ pblk_lines_free(pblk);
fail_free_core:
pblk_core_free(pblk);
-fail_free_line_meta:
- pblk_line_meta_free(pblk);
-fail_free_luns:
- pblk_luns_free(pblk);
fail:
kfree(pblk);
return ERR_PTR(ret);
diff --git a/drivers/lightnvm/pblk-map.c b/drivers/lightnvm/pblk-map.c
index 7445e6430c52..20dbaa89c9df 100644
--- a/drivers/lightnvm/pblk-map.c
+++ b/drivers/lightnvm/pblk-map.c
@@ -65,6 +65,8 @@ static void pblk_map_page_data(struct pblk *pblk, unsigned int sentry,
lba_list[paddr] = cpu_to_le64(w_ctx->lba);
if (lba_list[paddr] != addr_empty)
line->nr_valid_lbas++;
+ else
+ atomic64_inc(&pblk->pad_wa);
} else {
lba_list[paddr] = meta_list[i].lba = addr_empty;
__pblk_map_invalidate(pblk, line, paddr);
@@ -125,7 +127,7 @@ void pblk_map_erase_rq(struct pblk *pblk, struct nvm_rq *rqd,
atomic_dec(&e_line->left_eblks);
*erase_ppa = rqd->ppa_list[i];
- erase_ppa->g.blk = e_line->id;
+ erase_ppa->a.blk = e_line->id;
spin_unlock(&e_line->lock);
@@ -166,6 +168,6 @@ retry:
set_bit(bit, e_line->erase_bitmap);
atomic_dec(&e_line->left_eblks);
*erase_ppa = pblk->luns[bit].bppa; /* set ch and lun */
- erase_ppa->g.blk = e_line->id;
+ erase_ppa->a.blk = e_line->id;
}
}
diff --git a/drivers/lightnvm/pblk-rb.c b/drivers/lightnvm/pblk-rb.c
index ec8fc314646b..52fdd85dbc97 100644
--- a/drivers/lightnvm/pblk-rb.c
+++ b/drivers/lightnvm/pblk-rb.c
@@ -355,10 +355,13 @@ static int pblk_rb_flush_point_set(struct pblk_rb *rb, struct bio *bio,
struct pblk_rb_entry *entry;
unsigned int sync, flush_point;
+ pblk_rb_sync_init(rb, NULL);
sync = READ_ONCE(rb->sync);
- if (pos == sync)
+ if (pos == sync) {
+ pblk_rb_sync_end(rb, NULL);
return 0;
+ }
#ifdef CONFIG_NVM_DEBUG
atomic_inc(&rb->inflight_flush_point);
@@ -367,8 +370,6 @@ static int pblk_rb_flush_point_set(struct pblk_rb *rb, struct bio *bio,
flush_point = (pos == 0) ? (rb->nr_entries - 1) : (pos - 1);
entry = &rb->entries[flush_point];
- pblk_rb_sync_init(rb, NULL);
-
/* Protect flush points */
smp_store_release(&rb->flush_point, flush_point);
@@ -437,9 +438,7 @@ static int pblk_rb_may_write_flush(struct pblk_rb *rb, unsigned int nr_entries,
if (bio->bi_opf & REQ_PREFLUSH) {
struct pblk *pblk = container_of(rb, struct pblk, rwb);
-#ifdef CONFIG_NVM_DEBUG
- atomic_long_inc(&pblk->nr_flush);
-#endif
+ atomic64_inc(&pblk->nr_flush);
if (pblk_rb_flush_point_set(&pblk->rwb, bio, mem))
*io_ret = NVM_IO_OK;
}
@@ -620,11 +619,17 @@ try:
pr_err("pblk: could not pad page in write bio\n");
return NVM_IO_ERR;
}
+
+ if (pad < pblk->min_write_pgs)
+ atomic64_inc(&pblk->pad_dist[pad - 1]);
+ else
+ pr_warn("pblk: padding more than min. sectors\n");
+
+ atomic64_add(pad, &pblk->pad_wa);
}
#ifdef CONFIG_NVM_DEBUG
- atomic_long_add(pad, &((struct pblk *)
- (container_of(rb, struct pblk, rwb)))->padded_writes);
+ atomic_long_add(pad, &pblk->padded_writes);
#endif
return NVM_IO_OK;
diff --git a/drivers/lightnvm/pblk-read.c b/drivers/lightnvm/pblk-read.c
index 2f761283f43e..9eee10f69df0 100644
--- a/drivers/lightnvm/pblk-read.c
+++ b/drivers/lightnvm/pblk-read.c
@@ -563,7 +563,7 @@ int pblk_submit_read_gc(struct pblk *pblk, struct pblk_gc_rq *gc_rq)
if (!(gc_rq->secs_to_gc))
goto out;
- data_len = (gc_rq->secs_to_gc) * geo->sec_size;
+ data_len = (gc_rq->secs_to_gc) * geo->csecs;
bio = pblk_bio_map_addr(pblk, gc_rq->data, gc_rq->secs_to_gc, data_len,
PBLK_VMALLOC_META, GFP_KERNEL);
if (IS_ERR(bio)) {
diff --git a/drivers/lightnvm/pblk-recovery.c b/drivers/lightnvm/pblk-recovery.c
index 1d5e961bf5e0..3e079c2afa6e 100644
--- a/drivers/lightnvm/pblk-recovery.c
+++ b/drivers/lightnvm/pblk-recovery.c
@@ -21,17 +21,15 @@ void pblk_submit_rec(struct work_struct *work)
struct pblk_rec_ctx *recovery =
container_of(work, struct pblk_rec_ctx, ws_rec);
struct pblk *pblk = recovery->pblk;
- struct nvm_tgt_dev *dev = pblk->dev;
struct nvm_rq *rqd = recovery->rqd;
struct pblk_c_ctx *c_ctx = nvm_rq_to_pdu(rqd);
- int max_secs = nvm_max_phys_sects(dev);
struct bio *bio;
unsigned int nr_rec_secs;
unsigned int pgs_read;
int ret;
nr_rec_secs = bitmap_weight((unsigned long int *)&rqd->ppa_status,
- max_secs);
+ NVM_MAX_VLBA);
bio = bio_alloc(GFP_KERNEL, nr_rec_secs);
@@ -74,8 +72,6 @@ int pblk_recov_setup_rq(struct pblk *pblk, struct pblk_c_ctx *c_ctx,
struct pblk_rec_ctx *recovery, u64 *comp_bits,
unsigned int comp)
{
- struct nvm_tgt_dev *dev = pblk->dev;
- int max_secs = nvm_max_phys_sects(dev);
struct nvm_rq *rec_rqd;
struct pblk_c_ctx *rec_ctx;
int nr_entries = c_ctx->nr_valid + c_ctx->nr_padded;
@@ -86,7 +82,7 @@ int pblk_recov_setup_rq(struct pblk *pblk, struct pblk_c_ctx *c_ctx,
/* Copy completion bitmap, but exclude the first X completed entries */
bitmap_shift_right((unsigned long int *)&rec_rqd->ppa_status,
(unsigned long int *)comp_bits,
- comp, max_secs);
+ comp, NVM_MAX_VLBA);
/* Save the context for the entries that need to be re-written and
* update current context with the completed entries.
@@ -188,7 +184,7 @@ static int pblk_calc_sec_in_line(struct pblk *pblk, struct pblk_line *line)
int nr_bb = bitmap_weight(line->blk_bitmap, lm->blk_per_line);
return lm->sec_per_line - lm->smeta_sec - lm->emeta_sec[0] -
- nr_bb * geo->sec_per_chk;
+ nr_bb * geo->clba;
}
struct pblk_recov_alloc {
@@ -236,7 +232,7 @@ next_read_rq:
rq_ppas = pblk_calc_secs(pblk, left_ppas, 0);
if (!rq_ppas)
rq_ppas = pblk->min_write_pgs;
- rq_len = rq_ppas * geo->sec_size;
+ rq_len = rq_ppas * geo->csecs;
bio = bio_map_kern(dev->q, data, rq_len, GFP_KERNEL);
if (IS_ERR(bio))
@@ -355,7 +351,7 @@ static int pblk_recov_pad_oob(struct pblk *pblk, struct pblk_line *line,
if (!pad_rq)
return -ENOMEM;
- data = vzalloc(pblk->max_write_pgs * geo->sec_size);
+ data = vzalloc(pblk->max_write_pgs * geo->csecs);
if (!data) {
ret = -ENOMEM;
goto free_rq;
@@ -372,7 +368,7 @@ next_pad_rq:
goto fail_free_pad;
}
- rq_len = rq_ppas * geo->sec_size;
+ rq_len = rq_ppas * geo->csecs;
meta_list = nvm_dev_dma_alloc(dev->parent, GFP_KERNEL, &dma_meta_list);
if (!meta_list) {
@@ -513,7 +509,7 @@ next_rq:
rq_ppas = pblk_calc_secs(pblk, left_ppas, 0);
if (!rq_ppas)
rq_ppas = pblk->min_write_pgs;
- rq_len = rq_ppas * geo->sec_size;
+ rq_len = rq_ppas * geo->csecs;
bio = bio_map_kern(dev->q, data, rq_len, GFP_KERNEL);
if (IS_ERR(bio))
@@ -644,7 +640,7 @@ next_rq:
rq_ppas = pblk_calc_secs(pblk, left_ppas, 0);
if (!rq_ppas)
rq_ppas = pblk->min_write_pgs;
- rq_len = rq_ppas * geo->sec_size;
+ rq_len = rq_ppas * geo->csecs;
bio = bio_map_kern(dev->q, data, rq_len, GFP_KERNEL);
if (IS_ERR(bio))
@@ -749,7 +745,7 @@ static int pblk_recov_l2p_from_oob(struct pblk *pblk, struct pblk_line *line)
ppa_list = (void *)(meta_list) + pblk_dma_meta_size;
dma_ppa_list = dma_meta_list + pblk_dma_meta_size;
- data = kcalloc(pblk->max_write_pgs, geo->sec_size, GFP_KERNEL);
+ data = kcalloc(pblk->max_write_pgs, geo->csecs, GFP_KERNEL);
if (!data) {
ret = -ENOMEM;
goto free_meta_list;
@@ -826,6 +822,63 @@ static u64 pblk_line_emeta_start(struct pblk *pblk, struct pblk_line *line)
return emeta_start;
}
+static int pblk_recov_check_line_version(struct pblk *pblk,
+ struct line_emeta *emeta)
+{
+ struct line_header *header = &emeta->header;
+
+ if (header->version_major != EMETA_VERSION_MAJOR) {
+ pr_err("pblk: line major version mismatch: %d, expected: %d\n",
+ header->version_major, EMETA_VERSION_MAJOR);
+ return 1;
+ }
+
+#ifdef NVM_DEBUG
+ if (header->version_minor > EMETA_VERSION_MINOR)
+ pr_info("pblk: newer line minor version found: %d\n", line_v);
+#endif
+
+ return 0;
+}
+
+static void pblk_recov_wa_counters(struct pblk *pblk,
+ struct line_emeta *emeta)
+{
+ struct pblk_line_meta *lm = &pblk->lm;
+ struct line_header *header = &emeta->header;
+ struct wa_counters *wa = emeta_to_wa(lm, emeta);
+
+ /* WA counters were introduced in emeta version 0.2 */
+ if (header->version_major > 0 || header->version_minor >= 2) {
+ u64 user = le64_to_cpu(wa->user);
+ u64 pad = le64_to_cpu(wa->pad);
+ u64 gc = le64_to_cpu(wa->gc);
+
+ atomic64_set(&pblk->user_wa, user);
+ atomic64_set(&pblk->pad_wa, pad);
+ atomic64_set(&pblk->gc_wa, gc);
+
+ pblk->user_rst_wa = user;
+ pblk->pad_rst_wa = pad;
+ pblk->gc_rst_wa = gc;
+ }
+}
+
+static int pblk_line_was_written(struct pblk_line *line,
+ struct pblk_line_meta *lm)
+{
+
+ int i;
+ int state_mask = NVM_CHK_ST_OFFLINE | NVM_CHK_ST_FREE;
+
+ for (i = 0; i < lm->blk_per_line; i++) {
+ if (!(line->chks[i].state & state_mask))
+ return 1;
+ }
+
+ return 0;
+}
+
struct pblk_line *pblk_recov_l2p(struct pblk *pblk)
{
struct pblk_line_meta *lm = &pblk->lm;
@@ -862,6 +915,9 @@ struct pblk_line *pblk_recov_l2p(struct pblk *pblk)
line->lun_bitmap = ((void *)(smeta_buf)) +
sizeof(struct line_smeta);
+ if (!pblk_line_was_written(line, lm))
+ continue;
+
/* Lines that cannot be read are assumed as not written here */
if (pblk_line_read_smeta(pblk, line))
continue;
@@ -873,9 +929,9 @@ struct pblk_line *pblk_recov_l2p(struct pblk *pblk)
if (le32_to_cpu(smeta_buf->header.identifier) != PBLK_MAGIC)
continue;
- if (smeta_buf->header.version != SMETA_VERSION) {
+ if (smeta_buf->header.version_major != SMETA_VERSION_MAJOR) {
pr_err("pblk: found incompatible line version %u\n",
- le16_to_cpu(smeta_buf->header.version));
+ smeta_buf->header.version_major);
return ERR_PTR(-EINVAL);
}
@@ -943,6 +999,11 @@ struct pblk_line *pblk_recov_l2p(struct pblk *pblk)
goto next;
}
+ if (pblk_recov_check_line_version(pblk, line->emeta->buf))
+ return ERR_PTR(-EINVAL);
+
+ pblk_recov_wa_counters(pblk, line->emeta->buf);
+
if (pblk_recov_l2p_from_emeta(pblk, line))
pblk_recov_l2p_from_oob(pblk, line);
diff --git a/drivers/lightnvm/pblk-rl.c b/drivers/lightnvm/pblk-rl.c
index 0d457b162f23..883a7113b19d 100644
--- a/drivers/lightnvm/pblk-rl.c
+++ b/drivers/lightnvm/pblk-rl.c
@@ -200,7 +200,7 @@ void pblk_rl_init(struct pblk_rl *rl, int budget)
/* Consider sectors used for metadata */
sec_meta = (lm->smeta_sec + lm->emeta_sec[0]) * l_mg->nr_free_lines;
- blk_meta = DIV_ROUND_UP(sec_meta, geo->sec_per_chk);
+ blk_meta = DIV_ROUND_UP(sec_meta, geo->clba);
rl->high = pblk->op_blks - blk_meta - lm->blk_per_line;
rl->high_pw = get_count_order(rl->high);
diff --git a/drivers/lightnvm/pblk-sysfs.c b/drivers/lightnvm/pblk-sysfs.c
index 620bab853579..e61909af23a5 100644
--- a/drivers/lightnvm/pblk-sysfs.c
+++ b/drivers/lightnvm/pblk-sysfs.c
@@ -39,8 +39,8 @@ static ssize_t pblk_sysfs_luns_show(struct pblk *pblk, char *page)
sz += snprintf(page + sz, PAGE_SIZE - sz,
"pblk: pos:%d, ch:%d, lun:%d - %d\n",
i,
- rlun->bppa.g.ch,
- rlun->bppa.g.lun,
+ rlun->bppa.a.ch,
+ rlun->bppa.a.lun,
active);
}
@@ -115,24 +115,47 @@ static ssize_t pblk_sysfs_ppaf(struct pblk *pblk, char *page)
struct nvm_geo *geo = &dev->geo;
ssize_t sz = 0;
- sz = snprintf(page, PAGE_SIZE - sz,
- "g:(b:%d)blk:%d/%d,pg:%d/%d,lun:%d/%d,ch:%d/%d,pl:%d/%d,sec:%d/%d\n",
- pblk->ppaf_bitsize,
- pblk->ppaf.blk_offset, geo->ppaf.blk_len,
- pblk->ppaf.pg_offset, geo->ppaf.pg_len,
- pblk->ppaf.lun_offset, geo->ppaf.lun_len,
- pblk->ppaf.ch_offset, geo->ppaf.ch_len,
- pblk->ppaf.pln_offset, geo->ppaf.pln_len,
- pblk->ppaf.sec_offset, geo->ppaf.sect_len);
+ if (geo->version == NVM_OCSSD_SPEC_12) {
+ struct nvm_addrf_12 *ppaf = (struct nvm_addrf_12 *)&pblk->addrf;
+ struct nvm_addrf_12 *gppaf = (struct nvm_addrf_12 *)&geo->addrf;
- sz += snprintf(page + sz, PAGE_SIZE - sz,
- "d:blk:%d/%d,pg:%d/%d,lun:%d/%d,ch:%d/%d,pl:%d/%d,sec:%d/%d\n",
- geo->ppaf.blk_offset, geo->ppaf.blk_len,
- geo->ppaf.pg_offset, geo->ppaf.pg_len,
- geo->ppaf.lun_offset, geo->ppaf.lun_len,
- geo->ppaf.ch_offset, geo->ppaf.ch_len,
- geo->ppaf.pln_offset, geo->ppaf.pln_len,
- geo->ppaf.sect_offset, geo->ppaf.sect_len);
+ sz = snprintf(page, PAGE_SIZE,
+ "g:(b:%d)blk:%d/%d,pg:%d/%d,lun:%d/%d,ch:%d/%d,pl:%d/%d,sec:%d/%d\n",
+ pblk->addrf_len,
+ ppaf->blk_offset, ppaf->blk_len,
+ ppaf->pg_offset, ppaf->pg_len,
+ ppaf->lun_offset, ppaf->lun_len,
+ ppaf->ch_offset, ppaf->ch_len,
+ ppaf->pln_offset, ppaf->pln_len,
+ ppaf->sec_offset, ppaf->sec_len);
+
+ sz += snprintf(page + sz, PAGE_SIZE - sz,
+ "d:blk:%d/%d,pg:%d/%d,lun:%d/%d,ch:%d/%d,pl:%d/%d,sec:%d/%d\n",
+ gppaf->blk_offset, gppaf->blk_len,
+ gppaf->pg_offset, gppaf->pg_len,
+ gppaf->lun_offset, gppaf->lun_len,
+ gppaf->ch_offset, gppaf->ch_len,
+ gppaf->pln_offset, gppaf->pln_len,
+ gppaf->sec_offset, gppaf->sec_len);
+ } else {
+ struct nvm_addrf *ppaf = &pblk->addrf;
+ struct nvm_addrf *gppaf = &geo->addrf;
+
+ sz = snprintf(page, PAGE_SIZE,
+ "pblk:(s:%d)ch:%d/%d,lun:%d/%d,chk:%d/%d/sec:%d/%d\n",
+ pblk->addrf_len,
+ ppaf->ch_offset, ppaf->ch_len,
+ ppaf->lun_offset, ppaf->lun_len,
+ ppaf->chk_offset, ppaf->chk_len,
+ ppaf->sec_offset, ppaf->sec_len);
+
+ sz += snprintf(page + sz, PAGE_SIZE - sz,
+ "device:ch:%d/%d,lun:%d/%d,chk:%d/%d,sec:%d/%d\n",
+ gppaf->ch_offset, gppaf->ch_len,
+ gppaf->lun_offset, gppaf->lun_len,
+ gppaf->chk_offset, gppaf->chk_len,
+ gppaf->sec_offset, gppaf->sec_len);
+ }
return sz;
}
@@ -288,7 +311,7 @@ static ssize_t pblk_sysfs_lines_info(struct pblk *pblk, char *page)
"blk_line:%d, sec_line:%d, sec_blk:%d\n",
lm->blk_per_line,
lm->sec_per_line,
- geo->sec_per_chk);
+ geo->clba);
return sz;
}
@@ -298,15 +321,104 @@ static ssize_t pblk_sysfs_get_sec_per_write(struct pblk *pblk, char *page)
return snprintf(page, PAGE_SIZE, "%d\n", pblk->sec_per_write);
}
+static ssize_t pblk_get_write_amp(u64 user, u64 gc, u64 pad,
+ char *page)
+{
+ int sz;
+
+
+ sz = snprintf(page, PAGE_SIZE,
+ "user:%lld gc:%lld pad:%lld WA:",
+ user, gc, pad);
+
+ if (!user) {
+ sz += snprintf(page + sz, PAGE_SIZE - sz, "NaN\n");
+ } else {
+ u64 wa_int;
+ u32 wa_frac;
+
+ wa_int = (user + gc + pad) * 100000;
+ wa_int = div_u64(wa_int, user);
+ wa_int = div_u64_rem(wa_int, 100000, &wa_frac);
+
+ sz += snprintf(page + sz, PAGE_SIZE - sz, "%llu.%05u\n",
+ wa_int, wa_frac);
+ }
+
+ return sz;
+}
+
+static ssize_t pblk_sysfs_get_write_amp_mileage(struct pblk *pblk, char *page)
+{
+ return pblk_get_write_amp(atomic64_read(&pblk->user_wa),
+ atomic64_read(&pblk->gc_wa), atomic64_read(&pblk->pad_wa),
+ page);
+}
+
+static ssize_t pblk_sysfs_get_write_amp_trip(struct pblk *pblk, char *page)
+{
+ return pblk_get_write_amp(
+ atomic64_read(&pblk->user_wa) - pblk->user_rst_wa,
+ atomic64_read(&pblk->gc_wa) - pblk->gc_rst_wa,
+ atomic64_read(&pblk->pad_wa) - pblk->pad_rst_wa, page);
+}
+
+static long long bucket_percentage(unsigned long long bucket,
+ unsigned long long total)
+{
+ int p = bucket * 100;
+
+ p = div_u64(p, total);
+
+ return p;
+}
+
+static ssize_t pblk_sysfs_get_padding_dist(struct pblk *pblk, char *page)
+{
+ int sz = 0;
+ unsigned long long total;
+ unsigned long long total_buckets = 0;
+ int buckets = pblk->min_write_pgs - 1;
+ int i;
+
+ total = atomic64_read(&pblk->nr_flush) - pblk->nr_flush_rst;
+ if (!total) {
+ for (i = 0; i < (buckets + 1); i++)
+ sz += snprintf(page + sz, PAGE_SIZE - sz,
+ "%d:0 ", i);
+ sz += snprintf(page + sz, PAGE_SIZE - sz, "\n");
+
+ return sz;
+ }
+
+ for (i = 0; i < buckets; i++)
+ total_buckets += atomic64_read(&pblk->pad_dist[i]);
+
+ sz += snprintf(page + sz, PAGE_SIZE - sz, "0:%lld%% ",
+ bucket_percentage(total - total_buckets, total));
+
+ for (i = 0; i < buckets; i++) {
+ unsigned long long p;
+
+ p = bucket_percentage(atomic64_read(&pblk->pad_dist[i]),
+ total);
+ sz += snprintf(page + sz, PAGE_SIZE - sz, "%d:%lld%% ",
+ i + 1, p);
+ }
+ sz += snprintf(page + sz, PAGE_SIZE - sz, "\n");
+
+ return sz;
+}
+
#ifdef CONFIG_NVM_DEBUG
static ssize_t pblk_sysfs_stats_debug(struct pblk *pblk, char *page)
{
return snprintf(page, PAGE_SIZE,
- "%lu\t%lu\t%lu\t%lu\t%lu\t%lu\t%lu\t%lu\t%lu\t%lu\t%lu\t%lu\t%lu\n",
+ "%lu\t%lu\t%ld\t%llu\t%ld\t%lu\t%lu\t%lu\t%lu\t%lu\t%lu\t%lu\t%lu\n",
atomic_long_read(&pblk->inflight_writes),
atomic_long_read(&pblk->inflight_reads),
atomic_long_read(&pblk->req_writes),
- atomic_long_read(&pblk->nr_flush),
+ (u64)atomic64_read(&pblk->nr_flush),
atomic_long_read(&pblk->padded_writes),
atomic_long_read(&pblk->padded_wb),
atomic_long_read(&pblk->sub_writes),
@@ -360,6 +472,56 @@ static ssize_t pblk_sysfs_set_sec_per_write(struct pblk *pblk,
return len;
}
+static ssize_t pblk_sysfs_set_write_amp_trip(struct pblk *pblk,
+ const char *page, size_t len)
+{
+ size_t c_len;
+ int reset_value;
+
+ c_len = strcspn(page, "\n");
+ if (c_len >= len)
+ return -EINVAL;
+
+ if (kstrtouint(page, 0, &reset_value))
+ return -EINVAL;
+
+ if (reset_value != 0)
+ return -EINVAL;
+
+ pblk->user_rst_wa = atomic64_read(&pblk->user_wa);
+ pblk->pad_rst_wa = atomic64_read(&pblk->pad_wa);
+ pblk->gc_rst_wa = atomic64_read(&pblk->gc_wa);
+
+ return len;
+}
+
+
+static ssize_t pblk_sysfs_set_padding_dist(struct pblk *pblk,
+ const char *page, size_t len)
+{
+ size_t c_len;
+ int reset_value;
+ int buckets = pblk->min_write_pgs - 1;
+ int i;
+
+ c_len = strcspn(page, "\n");
+ if (c_len >= len)
+ return -EINVAL;
+
+ if (kstrtouint(page, 0, &reset_value))
+ return -EINVAL;
+
+ if (reset_value != 0)
+ return -EINVAL;
+
+ for (i = 0; i < buckets; i++)
+ atomic64_set(&pblk->pad_dist[i], 0);
+
+ pblk->nr_flush_rst = atomic64_read(&pblk->nr_flush);
+
+ return len;
+}
+
static struct attribute sys_write_luns = {
.name = "write_luns",
.mode = 0444,
@@ -410,6 +572,21 @@ static struct attribute sys_max_sec_per_write = {
.mode = 0644,
};
+static struct attribute sys_write_amp_mileage = {
+ .name = "write_amp_mileage",
+ .mode = 0444,
+};
+
+static struct attribute sys_write_amp_trip = {
+ .name = "write_amp_trip",
+ .mode = 0644,
+};
+
+static struct attribute sys_padding_dist = {
+ .name = "padding_dist",
+ .mode = 0644,
+};
+
#ifdef CONFIG_NVM_DEBUG
static struct attribute sys_stats_debug_attr = {
.name = "stats",
@@ -428,6 +605,9 @@ static struct attribute *pblk_attrs[] = {
&sys_stats_ppaf_attr,
&sys_lines_attr,
&sys_lines_info_attr,
+ &sys_write_amp_mileage,
+ &sys_write_amp_trip,
+ &sys_padding_dist,
#ifdef CONFIG_NVM_DEBUG
&sys_stats_debug_attr,
#endif
@@ -457,6 +637,12 @@ static ssize_t pblk_sysfs_show(struct kobject *kobj, struct attribute *attr,
return pblk_sysfs_lines_info(pblk, buf);
else if (strcmp(attr->name, "max_sec_per_write") == 0)
return pblk_sysfs_get_sec_per_write(pblk, buf);
+ else if (strcmp(attr->name, "write_amp_mileage") == 0)
+ return pblk_sysfs_get_write_amp_mileage(pblk, buf);
+ else if (strcmp(attr->name, "write_amp_trip") == 0)
+ return pblk_sysfs_get_write_amp_trip(pblk, buf);
+ else if (strcmp(attr->name, "padding_dist") == 0)
+ return pblk_sysfs_get_padding_dist(pblk, buf);
#ifdef CONFIG_NVM_DEBUG
else if (strcmp(attr->name, "stats") == 0)
return pblk_sysfs_stats_debug(pblk, buf);
@@ -473,7 +659,10 @@ static ssize_t pblk_sysfs_store(struct kobject *kobj, struct attribute *attr,
return pblk_sysfs_gc_force(pblk, buf, len);
else if (strcmp(attr->name, "max_sec_per_write") == 0)
return pblk_sysfs_set_sec_per_write(pblk, buf, len);
-
+ else if (strcmp(attr->name, "write_amp_trip") == 0)
+ return pblk_sysfs_set_write_amp_trip(pblk, buf, len);
+ else if (strcmp(attr->name, "padding_dist") == 0)
+ return pblk_sysfs_set_padding_dist(pblk, buf, len);
return 0;
}
diff --git a/drivers/lightnvm/pblk-write.c b/drivers/lightnvm/pblk-write.c
index aae86ed60b98..3e6f1ebd743a 100644
--- a/drivers/lightnvm/pblk-write.c
+++ b/drivers/lightnvm/pblk-write.c
@@ -333,7 +333,7 @@ int pblk_submit_meta_io(struct pblk *pblk, struct pblk_line *meta_line)
m_ctx = nvm_rq_to_pdu(rqd);
m_ctx->private = meta_line;
- rq_len = rq_ppas * geo->sec_size;
+ rq_len = rq_ppas * geo->csecs;
data = ((void *)emeta->buf) + emeta->mem;
bio = pblk_bio_map_addr(pblk, data, rq_ppas, rq_len,
diff --git a/drivers/lightnvm/pblk.h b/drivers/lightnvm/pblk.h
index 8c357fb6538e..9c682acfc5d1 100644
--- a/drivers/lightnvm/pblk.h
+++ b/drivers/lightnvm/pblk.h
@@ -201,12 +201,6 @@ struct pblk_rb {
struct pblk_lun {
struct ppa_addr bppa;
-
- u8 *bb_list; /* Bad block list for LUN. Only used on
- * bring up. Bad blocks are managed
- * within lines on run-time.
- */
-
struct semaphore wr_sem;
};
@@ -303,6 +297,7 @@ enum {
PBLK_LINETYPE_DATA = 2,
/* Line state */
+ PBLK_LINESTATE_NEW = 9,
PBLK_LINESTATE_FREE = 10,
PBLK_LINESTATE_OPEN = 11,
PBLK_LINESTATE_CLOSED = 12,
@@ -320,14 +315,26 @@ enum {
};
#define PBLK_MAGIC 0x70626c6b /*pblk*/
-#define SMETA_VERSION cpu_to_le16(1)
+
+/* emeta/smeta persistent storage format versions:
+ * Changes in major version requires offline migration.
+ * Changes in minor version are handled automatically during
+ * recovery.
+ */
+
+#define SMETA_VERSION_MAJOR (0)
+#define SMETA_VERSION_MINOR (1)
+
+#define EMETA_VERSION_MAJOR (0)
+#define EMETA_VERSION_MINOR (2)
struct line_header {
__le32 crc;
__le32 identifier; /* pblk identifier */
__u8 uuid[16]; /* instance uuid */
__le16 type; /* line type */
- __le16 version; /* type version */
+ __u8 version_major; /* version major */
+ __u8 version_minor; /* version minor */
__le32 id; /* line id for current line */
};
@@ -349,11 +356,13 @@ struct line_smeta {
__le64 lun_bitmap[];
};
+
/*
* Metadata layout in media:
* First sector:
* 1. struct line_emeta
* 2. bad block bitmap (u64 * window_wr_lun)
+ * 3. write amplification counters
* Mid sectors (start at lbas_sector):
* 3. nr_lbas (u64) forming lba list
* Last sectors (start at vsc_sector):
@@ -377,7 +386,15 @@ struct line_emeta {
__le32 next_id; /* Line id for next line */
__le64 nr_lbas; /* Number of lbas mapped in line */
__le64 nr_valid_lbas; /* Number of valid lbas mapped in line */
- __le64 bb_bitmap[]; /* Updated bad block bitmap for line */
+ __le64 bb_bitmap[]; /* Updated bad block bitmap for line */
+};
+
+
+/* Write amplification counters stored on media */
+struct wa_counters {
+ __le64 user; /* Number of user written sectors */
+ __le64 gc; /* Number of sectors written by GC*/
+ __le64 pad; /* Number of padded sectors */
};
struct pblk_emeta {
@@ -410,6 +427,8 @@ struct pblk_line {
unsigned long *lun_bitmap; /* Bitmap for LUNs mapped in line */
+ struct nvm_chk_meta *chks; /* Chunks forming line */
+
struct pblk_smeta *smeta; /* Start metadata */
struct pblk_emeta *emeta; /* End medatada */
@@ -507,10 +526,11 @@ struct pblk_line_meta {
unsigned int smeta_sec; /* Sectors needed for smeta */
unsigned int emeta_len[4]; /* Lengths for emeta:
- * [0]: Total length
- * [1]: struct line_emeta length
- * [2]: L2P portion length
- * [3]: vsc list length
+ * [0]: Total
+ * [1]: struct line_emeta +
+ * bb_bitmap + struct wa_counters
+ * [2]: L2P portion
+ * [3]: vsc
*/
unsigned int emeta_sec[4]; /* Sectors needed for emeta. Same layout
* as emeta_len
@@ -534,21 +554,6 @@ struct pblk_line_meta {
unsigned int meta_distance; /* Distance between data and metadata */
};
-struct pblk_addr_format {
- u64 ch_mask;
- u64 lun_mask;
- u64 pln_mask;
- u64 blk_mask;
- u64 pg_mask;
- u64 sec_mask;
- u8 ch_offset;
- u8 lun_offset;
- u8 pln_offset;
- u8 blk_offset;
- u8 pg_offset;
- u8 sec_offset;
-};
-
enum {
PBLK_STATE_RUNNING = 0,
PBLK_STATE_STOPPING = 1,
@@ -556,6 +561,18 @@ enum {
PBLK_STATE_STOPPED = 3,
};
+/* Internal format to support not power-of-2 device formats */
+struct pblk_addrf {
+ /* gen to dev */
+ int sec_stripe;
+ int ch_stripe;
+ int lun_stripe;
+
+ /* dev to gen */
+ int sec_lun_stripe;
+ int sec_ws_stripe;
+};
+
struct pblk {
struct nvm_tgt_dev *dev;
struct gendisk *disk;
@@ -568,8 +585,9 @@ struct pblk {
struct pblk_line_mgmt l_mg; /* Line management */
struct pblk_line_meta lm; /* Line metadata */
- int ppaf_bitsize;
- struct pblk_addr_format ppaf;
+ struct nvm_addrf addrf; /* Aligned address format */
+ struct pblk_addrf uaddrf; /* Unaligned address format */
+ int addrf_len;
struct pblk_rb rwb;
@@ -592,12 +610,27 @@ struct pblk {
int sec_per_write;
unsigned char instance_uuid[16];
+
+ /* Persistent write amplification counters, 4kb sector I/Os */
+ atomic64_t user_wa; /* Sectors written by user */
+ atomic64_t gc_wa; /* Sectors written by GC */
+ atomic64_t pad_wa; /* Padded sectors written */
+
+ /* Reset values for delta write amplification measurements */
+ u64 user_rst_wa;
+ u64 gc_rst_wa;
+ u64 pad_rst_wa;
+
+ /* Counters used for calculating padding distribution */
+ atomic64_t *pad_dist; /* Padding distribution buckets */
+ u64 nr_flush_rst; /* Flushes reset value for pad dist.*/
+ atomic64_t nr_flush; /* Number of flush/fua I/O */
+
#ifdef CONFIG_NVM_DEBUG
- /* All debug counters apply to 4kb sector I/Os */
+ /* Non-persistent debug counters, 4kb sector I/Os */
atomic_long_t inflight_writes; /* Inflight writes (user and gc) */
atomic_long_t padded_writes; /* Sectors padded due to flush/fua */
atomic_long_t padded_wb; /* Sectors padded in write buffer */
- atomic_long_t nr_flush; /* Number of flush/fua I/O */
atomic_long_t req_writes; /* Sectors stored on write buffer */
atomic_long_t sub_writes; /* Sectors submitted from buffer */
atomic_long_t sync_writes; /* Sectors synced to media */
@@ -712,6 +745,10 @@ void pblk_set_sec_per_write(struct pblk *pblk, int sec_per_write);
int pblk_setup_w_rec_rq(struct pblk *pblk, struct nvm_rq *rqd,
struct pblk_c_ctx *c_ctx);
void pblk_discard(struct pblk *pblk, struct bio *bio);
+struct nvm_chk_meta *pblk_chunk_get_info(struct pblk *pblk);
+struct nvm_chk_meta *pblk_chunk_get_off(struct pblk *pblk,
+ struct nvm_chk_meta *lp,
+ struct ppa_addr ppa);
void pblk_log_write_err(struct pblk *pblk, struct nvm_rq *rqd);
void pblk_log_read_err(struct pblk *pblk, struct nvm_rq *rqd);
int pblk_submit_io(struct pblk *pblk, struct nvm_rq *rqd);
@@ -888,6 +925,12 @@ static inline void *emeta_to_bb(struct line_emeta *emeta)
return emeta->bb_bitmap;
}
+static inline void *emeta_to_wa(struct pblk_line_meta *lm,
+ struct line_emeta *emeta)
+{
+ return emeta->bb_bitmap + lm->blk_bitmap_len;
+}
+
static inline void *emeta_to_lbas(struct pblk *pblk, struct line_emeta *emeta)
{
return ((void *)emeta + pblk->lm.emeta_len[1]);
@@ -903,38 +946,60 @@ static inline int pblk_line_vsc(struct pblk_line *line)
return le32_to_cpu(*line->vsc);
}
-#define NVM_MEM_PAGE_WRITE (8)
-
static inline int pblk_pad_distance(struct pblk *pblk)
{
struct nvm_tgt_dev *dev = pblk->dev;
struct nvm_geo *geo = &dev->geo;
- return NVM_MEM_PAGE_WRITE * geo->all_luns * geo->sec_per_pl;
+ return geo->mw_cunits * geo->all_luns * geo->ws_opt;
}
static inline int pblk_ppa_to_line(struct ppa_addr p)
{
- return p.g.blk;
+ return p.a.blk;
}
static inline int pblk_ppa_to_pos(struct nvm_geo *geo, struct ppa_addr p)
{
- return p.g.lun * geo->nr_chnls + p.g.ch;
+ return p.a.lun * geo->num_ch + p.a.ch;
}
static inline struct ppa_addr addr_to_gen_ppa(struct pblk *pblk, u64 paddr,
u64 line_id)
{
+ struct nvm_tgt_dev *dev = pblk->dev;
+ struct nvm_geo *geo = &dev->geo;
struct ppa_addr ppa;
- ppa.ppa = 0;
- ppa.g.blk = line_id;
- ppa.g.pg = (paddr & pblk->ppaf.pg_mask) >> pblk->ppaf.pg_offset;
- ppa.g.lun = (paddr & pblk->ppaf.lun_mask) >> pblk->ppaf.lun_offset;
- ppa.g.ch = (paddr & pblk->ppaf.ch_mask) >> pblk->ppaf.ch_offset;
- ppa.g.pl = (paddr & pblk->ppaf.pln_mask) >> pblk->ppaf.pln_offset;
- ppa.g.sec = (paddr & pblk->ppaf.sec_mask) >> pblk->ppaf.sec_offset;
+ if (geo->version == NVM_OCSSD_SPEC_12) {
+ struct nvm_addrf_12 *ppaf = (struct nvm_addrf_12 *)&pblk->addrf;
+
+ ppa.ppa = 0;
+ ppa.g.blk = line_id;
+ ppa.g.pg = (paddr & ppaf->pg_mask) >> ppaf->pg_offset;
+ ppa.g.lun = (paddr & ppaf->lun_mask) >> ppaf->lun_offset;
+ ppa.g.ch = (paddr & ppaf->ch_mask) >> ppaf->ch_offset;
+ ppa.g.pl = (paddr & ppaf->pln_mask) >> ppaf->pln_offset;
+ ppa.g.sec = (paddr & ppaf->sec_mask) >> ppaf->sec_offset;
+ } else {
+ struct pblk_addrf *uaddrf = &pblk->uaddrf;
+ int secs, chnls, luns;
+
+ ppa.ppa = 0;
+
+ ppa.m.chk = line_id;
+
+ paddr = div_u64_rem(paddr, uaddrf->sec_stripe, &secs);
+ ppa.m.sec = secs;
+
+ paddr = div_u64_rem(paddr, uaddrf->ch_stripe, &chnls);
+ ppa.m.grp = chnls;
+
+ paddr = div_u64_rem(paddr, uaddrf->lun_stripe, &luns);
+ ppa.m.pu = luns;
+
+ ppa.m.sec += uaddrf->sec_stripe * paddr;
+ }
return ppa;
}
@@ -942,13 +1007,30 @@ static inline struct ppa_addr addr_to_gen_ppa(struct pblk *pblk, u64 paddr,
static inline u64 pblk_dev_ppa_to_line_addr(struct pblk *pblk,
struct ppa_addr p)
{
+ struct nvm_tgt_dev *dev = pblk->dev;
+ struct nvm_geo *geo = &dev->geo;
u64 paddr;
- paddr = (u64)p.g.pg << pblk->ppaf.pg_offset;
- paddr |= (u64)p.g.lun << pblk->ppaf.lun_offset;
- paddr |= (u64)p.g.ch << pblk->ppaf.ch_offset;
- paddr |= (u64)p.g.pl << pblk->ppaf.pln_offset;
- paddr |= (u64)p.g.sec << pblk->ppaf.sec_offset;
+ if (geo->version == NVM_OCSSD_SPEC_12) {
+ struct nvm_addrf_12 *ppaf = (struct nvm_addrf_12 *)&pblk->addrf;
+
+ paddr = (u64)p.g.ch << ppaf->ch_offset;
+ paddr |= (u64)p.g.lun << ppaf->lun_offset;
+ paddr |= (u64)p.g.pg << ppaf->pg_offset;
+ paddr |= (u64)p.g.pl << ppaf->pln_offset;
+ paddr |= (u64)p.g.sec << ppaf->sec_offset;
+ } else {
+ struct pblk_addrf *uaddrf = &pblk->uaddrf;
+ u64 secs = p.m.sec;
+ int sec_stripe;
+
+ paddr = (u64)p.m.grp * uaddrf->sec_stripe;
+ paddr += (u64)p.m.pu * uaddrf->sec_lun_stripe;
+
+ secs = div_u64_rem(secs, uaddrf->sec_stripe, &sec_stripe);
+ paddr += secs * uaddrf->sec_ws_stripe;
+ paddr += sec_stripe;
+ }
return paddr;
}
@@ -965,18 +1047,37 @@ static inline struct ppa_addr pblk_ppa32_to_ppa64(struct pblk *pblk, u32 ppa32)
ppa64.c.line = ppa32 & ((~0U) >> 1);
ppa64.c.is_cached = 1;
} else {
- ppa64.g.blk = (ppa32 & pblk->ppaf.blk_mask) >>
- pblk->ppaf.blk_offset;
- ppa64.g.pg = (ppa32 & pblk->ppaf.pg_mask) >>
- pblk->ppaf.pg_offset;
- ppa64.g.lun = (ppa32 & pblk->ppaf.lun_mask) >>
- pblk->ppaf.lun_offset;
- ppa64.g.ch = (ppa32 & pblk->ppaf.ch_mask) >>
- pblk->ppaf.ch_offset;
- ppa64.g.pl = (ppa32 & pblk->ppaf.pln_mask) >>
- pblk->ppaf.pln_offset;
- ppa64.g.sec = (ppa32 & pblk->ppaf.sec_mask) >>
- pblk->ppaf.sec_offset;
+ struct nvm_tgt_dev *dev = pblk->dev;
+ struct nvm_geo *geo = &dev->geo;
+
+ if (geo->version == NVM_OCSSD_SPEC_12) {
+ struct nvm_addrf_12 *ppaf =
+ (struct nvm_addrf_12 *)&pblk->addrf;
+
+ ppa64.g.ch = (ppa32 & ppaf->ch_mask) >>
+ ppaf->ch_offset;
+ ppa64.g.lun = (ppa32 & ppaf->lun_mask) >>
+ ppaf->lun_offset;
+ ppa64.g.blk = (ppa32 & ppaf->blk_mask) >>
+ ppaf->blk_offset;
+ ppa64.g.pg = (ppa32 & ppaf->pg_mask) >>
+ ppaf->pg_offset;
+ ppa64.g.pl = (ppa32 & ppaf->pln_mask) >>
+ ppaf->pln_offset;
+ ppa64.g.sec = (ppa32 & ppaf->sec_mask) >>
+ ppaf->sec_offset;
+ } else {
+ struct nvm_addrf *lbaf = &pblk->addrf;
+
+ ppa64.m.grp = (ppa32 & lbaf->ch_mask) >>
+ lbaf->ch_offset;
+ ppa64.m.pu = (ppa32 & lbaf->lun_mask) >>
+ lbaf->lun_offset;
+ ppa64.m.chk = (ppa32 & lbaf->chk_mask) >>
+ lbaf->chk_offset;
+ ppa64.m.sec = (ppa32 & lbaf->sec_mask) >>
+ lbaf->sec_offset;
+ }
}
return ppa64;
@@ -992,12 +1093,27 @@ static inline u32 pblk_ppa64_to_ppa32(struct pblk *pblk, struct ppa_addr ppa64)
ppa32 |= ppa64.c.line;
ppa32 |= 1U << 31;
} else {
- ppa32 |= ppa64.g.blk << pblk->ppaf.blk_offset;
- ppa32 |= ppa64.g.pg << pblk->ppaf.pg_offset;
- ppa32 |= ppa64.g.lun << pblk->ppaf.lun_offset;
- ppa32 |= ppa64.g.ch << pblk->ppaf.ch_offset;
- ppa32 |= ppa64.g.pl << pblk->ppaf.pln_offset;
- ppa32 |= ppa64.g.sec << pblk->ppaf.sec_offset;
+ struct nvm_tgt_dev *dev = pblk->dev;
+ struct nvm_geo *geo = &dev->geo;
+
+ if (geo->version == NVM_OCSSD_SPEC_12) {
+ struct nvm_addrf_12 *ppaf =
+ (struct nvm_addrf_12 *)&pblk->addrf;
+
+ ppa32 |= ppa64.g.ch << ppaf->ch_offset;
+ ppa32 |= ppa64.g.lun << ppaf->lun_offset;
+ ppa32 |= ppa64.g.blk << ppaf->blk_offset;
+ ppa32 |= ppa64.g.pg << ppaf->pg_offset;
+ ppa32 |= ppa64.g.pl << ppaf->pln_offset;
+ ppa32 |= ppa64.g.sec << ppaf->sec_offset;
+ } else {
+ struct nvm_addrf *lbaf = &pblk->addrf;
+
+ ppa32 |= ppa64.m.grp << lbaf->ch_offset;
+ ppa32 |= ppa64.m.pu << lbaf->lun_offset;
+ ppa32 |= ppa64.m.chk << lbaf->chk_offset;
+ ppa32 |= ppa64.m.sec << lbaf->sec_offset;
+ }
}
return ppa32;
@@ -1008,7 +1124,7 @@ static inline struct ppa_addr pblk_trans_map_get(struct pblk *pblk,
{
struct ppa_addr ppa;
- if (pblk->ppaf_bitsize < 32) {
+ if (pblk->addrf_len < 32) {
u32 *map = (u32 *)pblk->trans_map;
ppa = pblk_ppa32_to_ppa64(pblk, map[lba]);
@@ -1024,7 +1140,7 @@ static inline struct ppa_addr pblk_trans_map_get(struct pblk *pblk,
static inline void pblk_trans_map_set(struct pblk *pblk, sector_t lba,
struct ppa_addr ppa)
{
- if (pblk->ppaf_bitsize < 32) {
+ if (pblk->addrf_len < 32) {
u32 *map = (u32 *)pblk->trans_map;
map[lba] = pblk_ppa64_to_ppa32(pblk, ppa);
@@ -1115,7 +1231,10 @@ static inline int pblk_set_progr_mode(struct pblk *pblk, int type)
struct nvm_geo *geo = &dev->geo;
int flags;
- flags = geo->plane_mode >> 1;
+ if (geo->version == NVM_OCSSD_SPEC_20)
+ return 0;
+
+ flags = geo->pln_mode >> 1;
if (type == PBLK_WRITE)
flags |= NVM_IO_SCRAMBLE_ENABLE;
@@ -1134,9 +1253,12 @@ static inline int pblk_set_read_mode(struct pblk *pblk, int type)
struct nvm_geo *geo = &dev->geo;
int flags;
+ if (geo->version == NVM_OCSSD_SPEC_20)
+ return 0;
+
flags = NVM_IO_SUSPEND | NVM_IO_SCRAMBLE_ENABLE;
if (type == PBLK_READ_SEQUENTIAL)
- flags |= geo->plane_mode >> 1;
+ flags |= geo->pln_mode >> 1;
return flags;
}
@@ -1147,16 +1269,21 @@ static inline int pblk_io_aligned(struct pblk *pblk, int nr_secs)
}
#ifdef CONFIG_NVM_DEBUG
-static inline void print_ppa(struct ppa_addr *p, char *msg, int error)
+static inline void print_ppa(struct nvm_geo *geo, struct ppa_addr *p,
+ char *msg, int error)
{
if (p->c.is_cached) {
pr_err("ppa: (%s: %x) cache line: %llu\n",
msg, error, (u64)p->c.line);
- } else {
+ } else if (geo->version == NVM_OCSSD_SPEC_12) {
pr_err("ppa: (%s: %x):ch:%d,lun:%d,blk:%d,pg:%d,pl:%d,sec:%d\n",
msg, error,
p->g.ch, p->g.lun, p->g.blk,
p->g.pg, p->g.pl, p->g.sec);
+ } else {
+ pr_err("ppa: (%s: %x):ch:%d,lun:%d,chk:%d,sec:%d\n",
+ msg, error,
+ p->m.grp, p->m.pu, p->m.chk, p->m.sec);
}
}
@@ -1166,13 +1293,13 @@ static inline void pblk_print_failed_rqd(struct pblk *pblk, struct nvm_rq *rqd,
int bit = -1;
if (rqd->nr_ppas == 1) {
- print_ppa(&rqd->ppa_addr, "rqd", error);
+ print_ppa(&pblk->dev->geo, &rqd->ppa_addr, "rqd", error);
return;
}
while ((bit = find_next_bit((void *)&rqd->ppa_status, rqd->nr_ppas,
bit + 1)) < rqd->nr_ppas) {
- print_ppa(&rqd->ppa_list[bit], "rqd", error);
+ print_ppa(&pblk->dev->geo, &rqd->ppa_list[bit], "rqd", error);
}
pr_err("error:%d, ppa_status:%llx\n", error, rqd->ppa_status);
@@ -1188,16 +1315,25 @@ static inline int pblk_boundary_ppa_checks(struct nvm_tgt_dev *tgt_dev,
for (i = 0; i < nr_ppas; i++) {
ppa = &ppas[i];
- if (!ppa->c.is_cached &&
- ppa->g.ch < geo->nr_chnls &&
- ppa->g.lun < geo->nr_luns &&
- ppa->g.pl < geo->nr_planes &&
- ppa->g.blk < geo->nr_chks &&
- ppa->g.pg < geo->ws_per_chk &&
- ppa->g.sec < geo->sec_per_pg)
- continue;
+ if (geo->version == NVM_OCSSD_SPEC_12) {
+ if (!ppa->c.is_cached &&
+ ppa->g.ch < geo->num_ch &&
+ ppa->g.lun < geo->num_lun &&
+ ppa->g.pl < geo->num_pln &&
+ ppa->g.blk < geo->num_chk &&
+ ppa->g.pg < geo->num_pg &&
+ ppa->g.sec < geo->ws_min)
+ continue;
+ } else {
+ if (!ppa->c.is_cached &&
+ ppa->m.grp < geo->num_ch &&
+ ppa->m.pu < geo->num_lun &&
+ ppa->m.chk < geo->num_chk &&
+ ppa->m.sec < geo->clba)
+ continue;
+ }
- print_ppa(ppa, "boundary", i);
+ print_ppa(geo, ppa, "boundary", i);
return 1;
}
diff --git a/drivers/macintosh/adb-iop.c b/drivers/macintosh/adb-iop.c
index 15db69d8ba69..ca623e6446e4 100644
--- a/drivers/macintosh/adb-iop.c
+++ b/drivers/macintosh/adb-iop.c
@@ -53,13 +53,13 @@ static void adb_iop_poll(void);
static int adb_iop_reset_bus(void);
struct adb_driver adb_iop_driver = {
- "ISM IOP",
- adb_iop_probe,
- adb_iop_init,
- adb_iop_send_request,
- adb_iop_autopoll,
- adb_iop_poll,
- adb_iop_reset_bus
+ .name = "ISM IOP",
+ .probe = adb_iop_probe,
+ .init = adb_iop_init,
+ .send_request = adb_iop_send_request,
+ .autopoll = adb_iop_autopoll,
+ .poll = adb_iop_poll,
+ .reset_bus = adb_iop_reset_bus
};
static void adb_iop_end_req(struct adb_request *req, int state)
diff --git a/drivers/macintosh/ans-lcd.c b/drivers/macintosh/ans-lcd.c
index 1de81d922d8a..c8e078b911c7 100644
--- a/drivers/macintosh/ans-lcd.c
+++ b/drivers/macintosh/ans-lcd.c
@@ -201,3 +201,4 @@ anslcd_exit(void)
module_init(anslcd_init);
module_exit(anslcd_exit);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/macintosh/macio-adb.c b/drivers/macintosh/macio-adb.c
index 9a6223add30e..eb3adfb7f88d 100644
--- a/drivers/macintosh/macio-adb.c
+++ b/drivers/macintosh/macio-adb.c
@@ -70,14 +70,13 @@ static void macio_adb_poll(void);
static int macio_adb_reset_bus(void);
struct adb_driver macio_adb_driver = {
- "MACIO",
- macio_probe,
- macio_init,
- macio_send_request,
- /*macio_write,*/
- macio_adb_autopoll,
- macio_adb_poll,
- macio_adb_reset_bus
+ .name = "MACIO",
+ .probe = macio_probe,
+ .init = macio_init,
+ .send_request = macio_send_request,
+ .autopoll = macio_adb_autopoll,
+ .poll = macio_adb_poll,
+ .reset_bus = macio_adb_reset_bus,
};
int macio_probe(void)
diff --git a/drivers/macintosh/rack-meter.c b/drivers/macintosh/rack-meter.c
index 910b5b6f96b1..1f29d2413c74 100644
--- a/drivers/macintosh/rack-meter.c
+++ b/drivers/macintosh/rack-meter.c
@@ -154,8 +154,8 @@ static void rackmeter_do_pause(struct rackmeter *rm, int pause)
DBDMA_DO_STOP(rm->dma_regs);
return;
}
- memset(rdma->buf1, 0, ARRAY_SIZE(rdma->buf1));
- memset(rdma->buf2, 0, ARRAY_SIZE(rdma->buf2));
+ memset(rdma->buf1, 0, sizeof(rdma->buf1));
+ memset(rdma->buf2, 0, sizeof(rdma->buf2));
rm->dma_buf_v->mark = 0;
@@ -397,7 +397,7 @@ static int rackmeter_probe(struct macio_dev* mdev,
}
/* Create and initialize our instance data */
- rm = kzalloc(sizeof(struct rackmeter), GFP_KERNEL);
+ rm = kzalloc(sizeof(*rm), GFP_KERNEL);
if (rm == NULL) {
printk(KERN_ERR "rackmeter: failed to allocate memory !\n");
rc = -ENOMEM;
diff --git a/drivers/macintosh/via-macii.c b/drivers/macintosh/via-macii.c
index 4ba06a1695ea..cf6f7d52d6be 100644
--- a/drivers/macintosh/via-macii.c
+++ b/drivers/macintosh/via-macii.c
@@ -91,13 +91,13 @@ static void macii_poll(void);
static int macii_reset_bus(void);
struct adb_driver via_macii_driver = {
- "Mac II",
- macii_probe,
- macii_init,
- macii_send_request,
- macii_autopoll,
- macii_poll,
- macii_reset_bus
+ .name = "Mac II",
+ .probe = macii_probe,
+ .init = macii_init,
+ .send_request = macii_send_request,
+ .autopoll = macii_autopoll,
+ .poll = macii_poll,
+ .reset_bus = macii_reset_bus,
};
static enum macii_state {
diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c
index 94c0f3f7df69..433dbeddfcf9 100644
--- a/drivers/macintosh/via-pmu.c
+++ b/drivers/macintosh/via-pmu.c
@@ -198,14 +198,14 @@ static const struct file_operations pmu_battery_proc_fops;
static const struct file_operations pmu_options_proc_fops;
#ifdef CONFIG_ADB
-struct adb_driver via_pmu_driver = {
- "PMU",
- pmu_probe,
- pmu_init,
- pmu_send_request,
- pmu_adb_autopoll,
- pmu_poll_adb,
- pmu_adb_reset_bus
+const struct adb_driver via_pmu_driver = {
+ .name = "PMU",
+ .probe = pmu_probe,
+ .init = pmu_init,
+ .send_request = pmu_send_request,
+ .autopoll = pmu_adb_autopoll,
+ .poll = pmu_poll_adb,
+ .reset_bus = pmu_adb_reset_bus,
};
#endif /* CONFIG_ADB */
diff --git a/drivers/macintosh/via-pmu68k.c b/drivers/macintosh/via-pmu68k.c
index 25465fb91ec9..d545ed45e482 100644
--- a/drivers/macintosh/via-pmu68k.c
+++ b/drivers/macintosh/via-pmu68k.c
@@ -109,6 +109,7 @@ static int pmu_autopoll(int devs);
void pmu_poll(void);
static int pmu_reset_bus(void);
+static int init_pmu(void);
static void pmu_start(void);
static void send_byte(int x);
static void recv_byte(void);
@@ -119,13 +120,13 @@ static void pmu_enable_backlight(int on);
static void pmu_set_brightness(int level);
struct adb_driver via_pmu_driver = {
- "68K PMU",
- pmu_probe,
- pmu_init,
- pmu_send_request,
- pmu_autopoll,
- pmu_poll,
- pmu_reset_bus
+ .name = "68K PMU",
+ .probe = pmu_probe,
+ .init = pmu_init,
+ .send_request = pmu_send_request,
+ .autopoll = pmu_autopoll,
+ .poll = pmu_poll,
+ .reset_bus = pmu_reset_bus,
};
/*
@@ -171,23 +172,76 @@ static s8 pmu_data_len[256][2] = {
/*f8*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
};
-int pmu_probe(void)
+int __init find_via_pmu(void)
{
- if (macintosh_config->adb_type == MAC_ADB_PB1) {
+ switch (macintosh_config->adb_type) {
+ case MAC_ADB_PB1:
pmu_kind = PMU_68K_V1;
- } else if (macintosh_config->adb_type == MAC_ADB_PB2) {
+ break;
+ case MAC_ADB_PB2:
pmu_kind = PMU_68K_V2;
- } else {
+ break;
+ default:
+ pmu_kind = PMU_UNKNOWN;
return -ENODEV;
}
pmu_state = idle;
+ if (!init_pmu())
+ goto fail_init;
+
+ pr_info("adb: PMU 68K driver v0.5 for Unified ADB\n");
+
+ return 1;
+
+fail_init:
+ pmu_kind = PMU_UNKNOWN;
return 0;
}
-static int
-pmu_init(void)
+static int pmu_probe(void)
+{
+ if (pmu_kind == PMU_UNKNOWN)
+ return -ENODEV;
+ return 0;
+}
+
+static int pmu_init(void)
+{
+ if (pmu_kind == PMU_UNKNOWN)
+ return -ENODEV;
+ return 0;
+}
+
+static int __init via_pmu_start(void)
+{
+ if (pmu_kind == PMU_UNKNOWN)
+ return -ENODEV;
+
+ if (request_irq(IRQ_MAC_ADB_SR, pmu_interrupt, 0, "PMU_SR",
+ pmu_interrupt)) {
+ pr_err("%s: can't get SR irq\n", __func__);
+ return -ENODEV;
+ }
+ if (request_irq(IRQ_MAC_ADB_CL, pmu_interrupt, 0, "PMU_CL",
+ pmu_interrupt)) {
+ pr_err("%s: can't get CL irq\n", __func__);
+ free_irq(IRQ_MAC_ADB_SR, pmu_interrupt);
+ return -ENODEV;
+ }
+
+ pmu_fully_inited = 1;
+
+ /* Enable backlight */
+ pmu_enable_backlight(1);
+
+ return 0;
+}
+
+arch_initcall(via_pmu_start);
+
+static int __init init_pmu(void)
{
int timeout;
volatile struct adb_request req;
@@ -238,28 +292,7 @@ pmu_init(void)
bright_req_2.complete = 1;
bright_req_3.complete = 1;
- if (request_irq(IRQ_MAC_ADB_SR, pmu_interrupt, 0, "pmu-shift",
- pmu_interrupt)) {
- printk(KERN_ERR "pmu_init: can't get irq %d\n",
- IRQ_MAC_ADB_SR);
- return -EAGAIN;
- }
- if (request_irq(IRQ_MAC_ADB_CL, pmu_interrupt, 0, "pmu-clock",
- pmu_interrupt)) {
- printk(KERN_ERR "pmu_init: can't get irq %d\n",
- IRQ_MAC_ADB_CL);
- free_irq(IRQ_MAC_ADB_SR, pmu_interrupt);
- return -EAGAIN;
- }
-
- pmu_fully_inited = 1;
-
- /* Enable backlight */
- pmu_enable_backlight(1);
-
- printk("adb: PMU 68K driver v0.5 for Unified ADB.\n");
-
- return 0;
+ return 1;
}
int
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index ba2f1525f4ee..a2bb27446dce 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -108,6 +108,14 @@ config TI_MESSAGE_MANAGER
multiple processors within the SoC. Select this driver if your
platform has support for the hardware block.
+config HI3660_MBOX
+ tristate "Hi3660 Mailbox"
+ depends on ARCH_HISI && OF
+ help
+ An implementation of the hi3660 mailbox. It is used to send message
+ between application processors and other processors/MCU/DSP. Select
+ Y here if you want to use Hi3660 mailbox controller.
+
config HI6220_MBOX
tristate "Hi6220 Mailbox"
depends on ARCH_HISI
@@ -134,7 +142,7 @@ config QCOM_APCS_IPC
config TEGRA_HSP_MBOX
bool "Tegra HSP (Hardware Synchronization Primitives) Driver"
- depends on ARCH_TEGRA_186_SOC
+ depends on ARCH_TEGRA
help
The Tegra HSP driver is used for the interprocessor communication
between different remote processors and host processors on Tegra186
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index 4896f8dcae95..cc23c3a43fcd 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -27,6 +27,8 @@ obj-$(CONFIG_TI_MESSAGE_MANAGER) += ti-msgmgr.o
obj-$(CONFIG_XGENE_SLIMPRO_MBOX) += mailbox-xgene-slimpro.o
+obj-$(CONFIG_HI3660_MBOX) += hi3660-mailbox.o
+
obj-$(CONFIG_HI6220_MBOX) += hi6220-mailbox.o
obj-$(CONFIG_BCM_PDC_MBOX) += bcm-pdc-mailbox.o
diff --git a/drivers/mailbox/bcm-flexrm-mailbox.c b/drivers/mailbox/bcm-flexrm-mailbox.c
index a8cf4333a68f..8ab077ff58f4 100644
--- a/drivers/mailbox/bcm-flexrm-mailbox.c
+++ b/drivers/mailbox/bcm-flexrm-mailbox.c
@@ -1268,7 +1268,7 @@ static int flexrm_startup(struct mbox_chan *chan)
}
/* Allocate completion memory */
- ring->cmpl_base = dma_pool_alloc(ring->mbox->cmpl_pool,
+ ring->cmpl_base = dma_pool_zalloc(ring->mbox->cmpl_pool,
GFP_KERNEL, &ring->cmpl_dma_base);
if (!ring->cmpl_base) {
dev_err(ring->mbox->dev,
@@ -1277,7 +1277,6 @@ static int flexrm_startup(struct mbox_chan *chan)
ret = -ENOMEM;
goto fail_free_bd_memory;
}
- memset(ring->cmpl_base, 0, RING_CMPL_SIZE);
/* Request IRQ */
if (ring->irq == UINT_MAX) {
diff --git a/drivers/mailbox/hi3660-mailbox.c b/drivers/mailbox/hi3660-mailbox.c
new file mode 100644
index 000000000000..3eea6b642484
--- /dev/null
+++ b/drivers/mailbox/hi3660-mailbox.c
@@ -0,0 +1,312 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2017-2018 Hisilicon Limited.
+// Copyright (c) 2017-2018 Linaro Limited.
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/mailbox_controller.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "mailbox.h"
+
+#define MBOX_CHAN_MAX 32
+
+#define MBOX_RX 0x0
+#define MBOX_TX 0x1
+
+#define MBOX_BASE(mbox, ch) ((mbox)->base + ((ch) * 0x40))
+#define MBOX_SRC_REG 0x00
+#define MBOX_DST_REG 0x04
+#define MBOX_DCLR_REG 0x08
+#define MBOX_DSTAT_REG 0x0c
+#define MBOX_MODE_REG 0x10
+#define MBOX_IMASK_REG 0x14
+#define MBOX_ICLR_REG 0x18
+#define MBOX_SEND_REG 0x1c
+#define MBOX_DATA_REG 0x20
+
+#define MBOX_IPC_LOCK_REG 0xa00
+#define MBOX_IPC_UNLOCK 0x1acce551
+
+#define MBOX_AUTOMATIC_ACK 1
+
+#define MBOX_STATE_IDLE BIT(4)
+#define MBOX_STATE_ACK BIT(7)
+
+#define MBOX_MSG_LEN 8
+
+/**
+ * Hi3660 mailbox channel information
+ *
+ * A channel can be used for TX or RX, it can trigger remote
+ * processor interrupt to notify remote processor and can receive
+ * interrupt if has incoming message.
+ *
+ * @dst_irq: Interrupt vector for remote processor
+ * @ack_irq: Interrupt vector for local processor
+ */
+struct hi3660_chan_info {
+ unsigned int dst_irq;
+ unsigned int ack_irq;
+};
+
+/**
+ * Hi3660 mailbox controller data
+ *
+ * Mailbox controller includes 32 channels and can allocate
+ * channel for message transferring.
+ *
+ * @dev: Device to which it is attached
+ * @base: Base address of the register mapping region
+ * @chan: Representation of channels in mailbox controller
+ * @mchan: Representation of channel info
+ * @controller: Representation of a communication channel controller
+ */
+struct hi3660_mbox {
+ struct device *dev;
+ void __iomem *base;
+ struct mbox_chan chan[MBOX_CHAN_MAX];
+ struct hi3660_chan_info mchan[MBOX_CHAN_MAX];
+ struct mbox_controller controller;
+};
+
+static struct hi3660_mbox *to_hi3660_mbox(struct mbox_controller *mbox)
+{
+ return container_of(mbox, struct hi3660_mbox, controller);
+}
+
+static int hi3660_mbox_check_state(struct mbox_chan *chan)
+{
+ unsigned long ch = (unsigned long)chan->con_priv;
+ struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox);
+ struct hi3660_chan_info *mchan = &mbox->mchan[ch];
+ void __iomem *base = MBOX_BASE(mbox, ch);
+ unsigned long val;
+ unsigned int ret;
+
+ /* Mailbox is idle so directly bail out */
+ if (readl(base + MBOX_MODE_REG) & MBOX_STATE_IDLE)
+ return 0;
+
+ /* Wait for acknowledge from remote */
+ ret = readx_poll_timeout_atomic(readl, base + MBOX_MODE_REG,
+ val, (val & MBOX_STATE_ACK), 1000, 300000);
+ if (ret) {
+ dev_err(mbox->dev, "%s: timeout for receiving ack\n", __func__);
+ return ret;
+ }
+
+ /* Ensure channel is released */
+ writel(0xffffffff, base + MBOX_IMASK_REG);
+ writel(BIT(mchan->ack_irq), base + MBOX_SRC_REG);
+ return 0;
+}
+
+static int hi3660_mbox_unlock(struct mbox_chan *chan)
+{
+ struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox);
+ unsigned int val, retry = 3;
+
+ do {
+ writel(MBOX_IPC_UNLOCK, mbox->base + MBOX_IPC_LOCK_REG);
+
+ val = readl(mbox->base + MBOX_IPC_LOCK_REG);
+ if (!val)
+ break;
+
+ udelay(10);
+ } while (retry--);
+
+ if (val)
+ dev_err(mbox->dev, "%s: failed to unlock mailbox\n", __func__);
+
+ return (!val) ? 0 : -ETIMEDOUT;
+}
+
+static int hi3660_mbox_acquire_channel(struct mbox_chan *chan)
+{
+ unsigned long ch = (unsigned long)chan->con_priv;
+ struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox);
+ struct hi3660_chan_info *mchan = &mbox->mchan[ch];
+ void __iomem *base = MBOX_BASE(mbox, ch);
+ unsigned int val, retry;
+
+ for (retry = 10; retry; retry--) {
+ /* Check if channel is in idle state */
+ if (readl(base + MBOX_MODE_REG) & MBOX_STATE_IDLE) {
+ writel(BIT(mchan->ack_irq), base + MBOX_SRC_REG);
+
+ /* Check ack bit has been set successfully */
+ val = readl(base + MBOX_SRC_REG);
+ if (val & BIT(mchan->ack_irq))
+ break;
+ }
+ }
+
+ if (!retry)
+ dev_err(mbox->dev, "%s: failed to acquire channel\n", __func__);
+
+ return retry ? 0 : -ETIMEDOUT;
+}
+
+static int hi3660_mbox_startup(struct mbox_chan *chan)
+{
+ int ret;
+
+ ret = hi3660_mbox_check_state(chan);
+ if (ret)
+ return ret;
+
+ ret = hi3660_mbox_unlock(chan);
+ if (ret)
+ return ret;
+
+ ret = hi3660_mbox_acquire_channel(chan);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int hi3660_mbox_send_data(struct mbox_chan *chan, void *msg)
+{
+ unsigned long ch = (unsigned long)chan->con_priv;
+ struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox);
+ struct hi3660_chan_info *mchan = &mbox->mchan[ch];
+ void __iomem *base = MBOX_BASE(mbox, ch);
+ u32 *buf = msg;
+ unsigned int i;
+
+ /* Ensure channel is released */
+ writel_relaxed(0xffffffff, base + MBOX_IMASK_REG);
+ writel_relaxed(BIT(mchan->ack_irq), base + MBOX_SRC_REG);
+
+ /* Clear mask for destination interrupt */
+ writel_relaxed(~BIT(mchan->dst_irq), base + MBOX_IMASK_REG);
+
+ /* Config destination for interrupt vector */
+ writel_relaxed(BIT(mchan->dst_irq), base + MBOX_DST_REG);
+
+ /* Automatic acknowledge mode */
+ writel_relaxed(MBOX_AUTOMATIC_ACK, base + MBOX_MODE_REG);
+
+ /* Fill message data */
+ for (i = 0; i < MBOX_MSG_LEN; i++)
+ writel_relaxed(buf[i], base + MBOX_DATA_REG + i * 4);
+
+ /* Trigger data transferring */
+ writel(BIT(mchan->ack_irq), base + MBOX_SEND_REG);
+ return 0;
+}
+
+static struct mbox_chan_ops hi3660_mbox_ops = {
+ .startup = hi3660_mbox_startup,
+ .send_data = hi3660_mbox_send_data,
+};
+
+static struct mbox_chan *hi3660_mbox_xlate(struct mbox_controller *controller,
+ const struct of_phandle_args *spec)
+{
+ struct hi3660_mbox *mbox = to_hi3660_mbox(controller);
+ struct hi3660_chan_info *mchan;
+ unsigned int ch = spec->args[0];
+
+ if (ch >= MBOX_CHAN_MAX) {
+ dev_err(mbox->dev, "Invalid channel idx %d\n", ch);
+ return ERR_PTR(-EINVAL);
+ }
+
+ mchan = &mbox->mchan[ch];
+ mchan->dst_irq = spec->args[1];
+ mchan->ack_irq = spec->args[2];
+
+ return &mbox->chan[ch];
+}
+
+static const struct of_device_id hi3660_mbox_of_match[] = {
+ { .compatible = "hisilicon,hi3660-mbox", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, hi3660_mbox_of_match);
+
+static int hi3660_mbox_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct hi3660_mbox *mbox;
+ struct mbox_chan *chan;
+ struct resource *res;
+ unsigned long ch;
+ int err;
+
+ mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
+ if (!mbox)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ mbox->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(mbox->base))
+ return PTR_ERR(mbox->base);
+
+ mbox->dev = dev;
+ mbox->controller.dev = dev;
+ mbox->controller.chans = mbox->chan;
+ mbox->controller.num_chans = MBOX_CHAN_MAX;
+ mbox->controller.ops = &hi3660_mbox_ops;
+ mbox->controller.of_xlate = hi3660_mbox_xlate;
+
+ /* Initialize mailbox channel data */
+ chan = mbox->chan;
+ for (ch = 0; ch < MBOX_CHAN_MAX; ch++)
+ chan[ch].con_priv = (void *)ch;
+
+ err = mbox_controller_register(&mbox->controller);
+ if (err) {
+ dev_err(dev, "Failed to register mailbox %d\n", err);
+ return err;
+ }
+
+ platform_set_drvdata(pdev, mbox);
+ dev_info(dev, "Mailbox enabled\n");
+ return 0;
+}
+
+static int hi3660_mbox_remove(struct platform_device *pdev)
+{
+ struct hi3660_mbox *mbox = platform_get_drvdata(pdev);
+
+ mbox_controller_unregister(&mbox->controller);
+ return 0;
+}
+
+static struct platform_driver hi3660_mbox_driver = {
+ .probe = hi3660_mbox_probe,
+ .remove = hi3660_mbox_remove,
+ .driver = {
+ .name = "hi3660-mbox",
+ .of_match_table = hi3660_mbox_of_match,
+ },
+};
+
+static int __init hi3660_mbox_init(void)
+{
+ return platform_driver_register(&hi3660_mbox_driver);
+}
+core_initcall(hi3660_mbox_init);
+
+static void __exit hi3660_mbox_exit(void)
+{
+ platform_driver_unregister(&hi3660_mbox_driver);
+}
+module_exit(hi3660_mbox_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Hisilicon Hi3660 Mailbox Controller");
+MODULE_AUTHOR("Leo Yan <leo.yan@linaro.org>");
diff --git a/drivers/mcb/mcb-pci.c b/drivers/mcb/mcb-pci.c
index af4d2f26f1c6..c2d69e33bf2b 100644
--- a/drivers/mcb/mcb-pci.c
+++ b/drivers/mcb/mcb-pci.c
@@ -117,6 +117,7 @@ static void mcb_pci_remove(struct pci_dev *pdev)
static const struct pci_device_id mcb_pci_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_MEN, PCI_DEVICE_ID_MEN_CHAMELEON) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ALTERA, PCI_DEVICE_ID_MEN_CHAMELEON) },
{ 0 },
};
MODULE_DEVICE_TABLE(pci, mcb_pci_tbl);
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index 2c8ac3688815..edff083f7c4e 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -201,7 +201,7 @@ config BLK_DEV_DM_BUILTIN
config BLK_DEV_DM
tristate "Device mapper support"
select BLK_DEV_DM_BUILTIN
- select DAX
+ depends on DAX || DAX=n
---help---
Device-mapper is a low level volume manager. It works by allowing
people to specify mappings for ranges of logical sectors. Various
diff --git a/drivers/md/bcache/alloc.c b/drivers/md/bcache/alloc.c
index 458e1d38577d..004cc3cc6123 100644
--- a/drivers/md/bcache/alloc.c
+++ b/drivers/md/bcache/alloc.c
@@ -287,7 +287,8 @@ do { \
break; \
\
mutex_unlock(&(ca)->set->bucket_lock); \
- if (kthread_should_stop()) { \
+ if (kthread_should_stop() || \
+ test_bit(CACHE_SET_IO_DISABLE, &ca->set->flags)) { \
set_current_state(TASK_RUNNING); \
return 0; \
} \
diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h
index 12e5197f186c..d338b7086013 100644
--- a/drivers/md/bcache/bcache.h
+++ b/drivers/md/bcache/bcache.h
@@ -188,6 +188,7 @@
#include <linux/refcount.h>
#include <linux/types.h>
#include <linux/workqueue.h>
+#include <linux/kthread.h>
#include "bset.h"
#include "util.h"
@@ -258,10 +259,11 @@ struct bcache_device {
struct gendisk *disk;
unsigned long flags;
-#define BCACHE_DEV_CLOSING 0
-#define BCACHE_DEV_DETACHING 1
-#define BCACHE_DEV_UNLINK_DONE 2
-
+#define BCACHE_DEV_CLOSING 0
+#define BCACHE_DEV_DETACHING 1
+#define BCACHE_DEV_UNLINK_DONE 2
+#define BCACHE_DEV_WB_RUNNING 3
+#define BCACHE_DEV_RATE_DW_RUNNING 4
unsigned nr_stripes;
unsigned stripe_size;
atomic_t *stripe_sectors_dirty;
@@ -286,6 +288,12 @@ struct io {
sector_t last;
};
+enum stop_on_failure {
+ BCH_CACHED_DEV_STOP_AUTO = 0,
+ BCH_CACHED_DEV_STOP_ALWAYS,
+ BCH_CACHED_DEV_STOP_MODE_MAX,
+};
+
struct cached_dev {
struct list_head list;
struct bcache_device disk;
@@ -359,6 +367,7 @@ struct cached_dev {
unsigned sequential_cutoff;
unsigned readahead;
+ unsigned io_disable:1;
unsigned verify:1;
unsigned bypass_torture_test:1;
@@ -378,6 +387,11 @@ struct cached_dev {
unsigned writeback_rate_i_term_inverse;
unsigned writeback_rate_p_term_inverse;
unsigned writeback_rate_minimum;
+
+ enum stop_on_failure stop_when_cache_set_failed;
+#define DEFAULT_CACHED_DEV_ERROR_LIMIT 64
+ atomic_t io_errors;
+ unsigned error_limit;
};
enum alloc_reserve {
@@ -474,10 +488,15 @@ struct gc_stat {
*
* CACHE_SET_RUNNING means all cache devices have been registered and journal
* replay is complete.
+ *
+ * CACHE_SET_IO_DISABLE is set when bcache is stopping the whold cache set, all
+ * external and internal I/O should be denied when this flag is set.
+ *
*/
#define CACHE_SET_UNREGISTERING 0
#define CACHE_SET_STOPPING 1
#define CACHE_SET_RUNNING 2
+#define CACHE_SET_IO_DISABLE 3
struct cache_set {
struct closure cl;
@@ -867,8 +886,36 @@ static inline void wake_up_allocators(struct cache_set *c)
wake_up_process(ca->alloc_thread);
}
+static inline void closure_bio_submit(struct cache_set *c,
+ struct bio *bio,
+ struct closure *cl)
+{
+ closure_get(cl);
+ if (unlikely(test_bit(CACHE_SET_IO_DISABLE, &c->flags))) {
+ bio->bi_status = BLK_STS_IOERR;
+ bio_endio(bio);
+ return;
+ }
+ generic_make_request(bio);
+}
+
+/*
+ * Prevent the kthread exits directly, and make sure when kthread_stop()
+ * is called to stop a kthread, it is still alive. If a kthread might be
+ * stopped by CACHE_SET_IO_DISABLE bit set, wait_for_kthread_stop() is
+ * necessary before the kthread returns.
+ */
+static inline void wait_for_kthread_stop(void)
+{
+ while (!kthread_should_stop()) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule();
+ }
+}
+
/* Forward declarations */
+void bch_count_backing_io_errors(struct cached_dev *dc, struct bio *bio);
void bch_count_io_errors(struct cache *, blk_status_t, int, const char *);
void bch_bbio_count_io_errors(struct cache_set *, struct bio *,
blk_status_t, const char *);
@@ -896,6 +943,7 @@ int bch_bucket_alloc_set(struct cache_set *, unsigned,
struct bkey *, int, bool);
bool bch_alloc_sectors(struct cache_set *, struct bkey *, unsigned,
unsigned, unsigned, bool);
+bool bch_cached_dev_error(struct cached_dev *dc);
__printf(2, 3)
bool bch_cache_set_error(struct cache_set *, const char *, ...);
@@ -905,6 +953,7 @@ void bch_write_bdev_super(struct cached_dev *, struct closure *);
extern struct workqueue_struct *bcache_wq;
extern const char * const bch_cache_modes[];
+extern const char * const bch_stop_on_failure_modes[];
extern struct mutex bch_register_lock;
extern struct list_head bch_cache_sets;
diff --git a/drivers/md/bcache/bset.c b/drivers/md/bcache/bset.c
index e56d3ecdbfcb..579c696a5fe0 100644
--- a/drivers/md/bcache/bset.c
+++ b/drivers/md/bcache/bset.c
@@ -1072,7 +1072,7 @@ EXPORT_SYMBOL(bch_btree_iter_init);
static inline struct bkey *__bch_btree_iter_next(struct btree_iter *iter,
btree_iter_cmp_fn *cmp)
{
- struct btree_iter_set unused;
+ struct btree_iter_set b __maybe_unused;
struct bkey *ret = NULL;
if (!btree_iter_end(iter)) {
@@ -1087,7 +1087,7 @@ static inline struct bkey *__bch_btree_iter_next(struct btree_iter *iter,
}
if (iter->data->k == iter->data->end)
- heap_pop(iter, unused, cmp);
+ heap_pop(iter, b, cmp);
else
heap_sift(iter, 0, cmp);
}
diff --git a/drivers/md/bcache/bset.h b/drivers/md/bcache/bset.h
index fa506c1aa524..0c24280f3b98 100644
--- a/drivers/md/bcache/bset.h
+++ b/drivers/md/bcache/bset.h
@@ -531,14 +531,15 @@ int __bch_keylist_realloc(struct keylist *, unsigned);
#ifdef CONFIG_BCACHE_DEBUG
int __bch_count_data(struct btree_keys *);
-void __bch_check_keys(struct btree_keys *, const char *, ...);
+void __printf(2, 3) __bch_check_keys(struct btree_keys *, const char *, ...);
void bch_dump_bset(struct btree_keys *, struct bset *, unsigned);
void bch_dump_bucket(struct btree_keys *);
#else
static inline int __bch_count_data(struct btree_keys *b) { return -1; }
-static inline void __bch_check_keys(struct btree_keys *b, const char *fmt, ...) {}
+static inline void __printf(2, 3)
+ __bch_check_keys(struct btree_keys *b, const char *fmt, ...) {}
static inline void bch_dump_bucket(struct btree_keys *b) {}
void bch_dump_bset(struct btree_keys *, struct bset *, unsigned);
diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c
index fad9fe8817eb..17936b2dc7d6 100644
--- a/drivers/md/bcache/btree.c
+++ b/drivers/md/bcache/btree.c
@@ -665,6 +665,7 @@ static unsigned long bch_mca_scan(struct shrinker *shrink,
struct btree *b, *t;
unsigned long i, nr = sc->nr_to_scan;
unsigned long freed = 0;
+ unsigned int btree_cache_used;
if (c->shrinker_disabled)
return SHRINK_STOP;
@@ -689,9 +690,10 @@ static unsigned long bch_mca_scan(struct shrinker *shrink,
nr = min_t(unsigned long, nr, mca_can_free(c));
i = 0;
+ btree_cache_used = c->btree_cache_used;
list_for_each_entry_safe(b, t, &c->btree_cache_freeable, list) {
- if (freed >= nr)
- break;
+ if (nr <= 0)
+ goto out;
if (++i > 3 &&
!mca_reap(b, 0, false)) {
@@ -699,9 +701,10 @@ static unsigned long bch_mca_scan(struct shrinker *shrink,
rw_unlock(true, b);
freed++;
}
+ nr--;
}
- for (i = 0; (nr--) && i < c->btree_cache_used; i++) {
+ for (; (nr--) && i < btree_cache_used; i++) {
if (list_empty(&c->btree_cache))
goto out;
@@ -719,7 +722,7 @@ static unsigned long bch_mca_scan(struct shrinker *shrink,
}
out:
mutex_unlock(&c->bucket_lock);
- return freed;
+ return freed * c->btree_pages;
}
static unsigned long bch_mca_count(struct shrinker *shrink,
@@ -959,7 +962,7 @@ err:
return b;
}
-/**
+/*
* bch_btree_node_get - find a btree node in the cache and lock it, reading it
* in from disk if necessary.
*
@@ -1744,6 +1747,7 @@ static void bch_btree_gc(struct cache_set *c)
btree_gc_start(c);
+ /* if CACHE_SET_IO_DISABLE set, gc thread should stop too */
do {
ret = btree_root(gc_root, c, &op, &writes, &stats);
closure_sync(&writes);
@@ -1751,7 +1755,7 @@ static void bch_btree_gc(struct cache_set *c)
if (ret && ret != -EAGAIN)
pr_warn("gc failed!");
- } while (ret);
+ } while (ret && !test_bit(CACHE_SET_IO_DISABLE, &c->flags));
bch_btree_gc_finish(c);
wake_up_allocators(c);
@@ -1789,15 +1793,19 @@ static int bch_gc_thread(void *arg)
while (1) {
wait_event_interruptible(c->gc_wait,
- kthread_should_stop() || gc_should_run(c));
+ kthread_should_stop() ||
+ test_bit(CACHE_SET_IO_DISABLE, &c->flags) ||
+ gc_should_run(c));
- if (kthread_should_stop())
+ if (kthread_should_stop() ||
+ test_bit(CACHE_SET_IO_DISABLE, &c->flags))
break;
set_gc_sectors(c);
bch_btree_gc(c);
}
+ wait_for_kthread_stop();
return 0;
}
@@ -2170,7 +2178,7 @@ int bch_btree_insert_check_key(struct btree *b, struct btree_op *op,
if (b->key.ptr[0] != btree_ptr ||
b->seq != seq + 1) {
- op->lock = b->level;
+ op->lock = b->level;
goto out;
}
}
diff --git a/drivers/md/bcache/closure.c b/drivers/md/bcache/closure.c
index 7f12920c14f7..0e14969182c6 100644
--- a/drivers/md/bcache/closure.c
+++ b/drivers/md/bcache/closure.c
@@ -46,7 +46,7 @@ void closure_sub(struct closure *cl, int v)
}
EXPORT_SYMBOL(closure_sub);
-/**
+/*
* closure_put - decrement a closure's refcount
*/
void closure_put(struct closure *cl)
@@ -55,7 +55,7 @@ void closure_put(struct closure *cl)
}
EXPORT_SYMBOL(closure_put);
-/**
+/*
* closure_wake_up - wake up all closures on a wait list, without memory barrier
*/
void __closure_wake_up(struct closure_waitlist *wait_list)
@@ -79,9 +79,9 @@ EXPORT_SYMBOL(__closure_wake_up);
/**
* closure_wait - add a closure to a waitlist
- *
- * @waitlist will own a ref on @cl, which will be released when
+ * @waitlist: will own a ref on @cl, which will be released when
* closure_wake_up() is called on @waitlist.
+ * @cl: closure pointer.
*
*/
bool closure_wait(struct closure_waitlist *waitlist, struct closure *cl)
@@ -157,7 +157,7 @@ void closure_debug_destroy(struct closure *cl)
}
EXPORT_SYMBOL(closure_debug_destroy);
-static struct dentry *debug;
+static struct dentry *closure_debug;
static int debug_seq_show(struct seq_file *f, void *data)
{
@@ -199,11 +199,12 @@ static const struct file_operations debug_ops = {
.release = single_release
};
-void __init closure_debug_init(void)
+int __init closure_debug_init(void)
{
- debug = debugfs_create_file("closures", 0400, NULL, NULL, &debug_ops);
+ closure_debug = debugfs_create_file("closures",
+ 0400, bcache_debug, NULL, &debug_ops);
+ return IS_ERR_OR_NULL(closure_debug);
}
-
#endif
MODULE_AUTHOR("Kent Overstreet <koverstreet@google.com>");
diff --git a/drivers/md/bcache/closure.h b/drivers/md/bcache/closure.h
index 3b9dfc9962ad..71427eb5fdae 100644
--- a/drivers/md/bcache/closure.h
+++ b/drivers/md/bcache/closure.h
@@ -105,6 +105,7 @@
struct closure;
struct closure_syncer;
typedef void (closure_fn) (struct closure *);
+extern struct dentry *bcache_debug;
struct closure_waitlist {
struct llist_head list;
@@ -185,13 +186,13 @@ static inline void closure_sync(struct closure *cl)
#ifdef CONFIG_BCACHE_CLOSURES_DEBUG
-void closure_debug_init(void);
+int closure_debug_init(void);
void closure_debug_create(struct closure *cl);
void closure_debug_destroy(struct closure *cl);
#else
-static inline void closure_debug_init(void) {}
+static inline int closure_debug_init(void) { return 0; }
static inline void closure_debug_create(struct closure *cl) {}
static inline void closure_debug_destroy(struct closure *cl) {}
diff --git a/drivers/md/bcache/debug.c b/drivers/md/bcache/debug.c
index af89408befe8..028f7b386e01 100644
--- a/drivers/md/bcache/debug.c
+++ b/drivers/md/bcache/debug.c
@@ -17,7 +17,7 @@
#include <linux/random.h>
#include <linux/seq_file.h>
-static struct dentry *debug;
+struct dentry *bcache_debug;
#ifdef CONFIG_BCACHE_DEBUG
@@ -232,11 +232,11 @@ static const struct file_operations cache_set_debug_ops = {
void bch_debug_init_cache_set(struct cache_set *c)
{
- if (!IS_ERR_OR_NULL(debug)) {
+ if (!IS_ERR_OR_NULL(bcache_debug)) {
char name[50];
snprintf(name, 50, "bcache-%pU", c->sb.set_uuid);
- c->debug = debugfs_create_file(name, 0400, debug, c,
+ c->debug = debugfs_create_file(name, 0400, bcache_debug, c,
&cache_set_debug_ops);
}
}
@@ -245,13 +245,13 @@ void bch_debug_init_cache_set(struct cache_set *c)
void bch_debug_exit(void)
{
- if (!IS_ERR_OR_NULL(debug))
- debugfs_remove_recursive(debug);
+ if (!IS_ERR_OR_NULL(bcache_debug))
+ debugfs_remove_recursive(bcache_debug);
}
int __init bch_debug_init(struct kobject *kobj)
{
- debug = debugfs_create_dir("bcache", NULL);
+ bcache_debug = debugfs_create_dir("bcache", NULL);
- return IS_ERR_OR_NULL(debug);
+ return IS_ERR_OR_NULL(bcache_debug);
}
diff --git a/drivers/md/bcache/extents.c b/drivers/md/bcache/extents.c
index f9d391711595..c334e6666461 100644
--- a/drivers/md/bcache/extents.c
+++ b/drivers/md/bcache/extents.c
@@ -534,7 +534,6 @@ err:
static bool bch_extent_bad(struct btree_keys *bk, const struct bkey *k)
{
struct btree *b = container_of(bk, struct btree, keys);
- struct bucket *g;
unsigned i, stale;
if (!KEY_PTRS(k) ||
@@ -549,7 +548,6 @@ static bool bch_extent_bad(struct btree_keys *bk, const struct bkey *k)
return false;
for (i = 0; i < KEY_PTRS(k); i++) {
- g = PTR_BUCKET(b->c, k, i);
stale = ptr_stale(b->c, k, i);
btree_bug_on(stale > 96, b,
diff --git a/drivers/md/bcache/io.c b/drivers/md/bcache/io.c
index a783c5a41ff1..7fac97ae036e 100644
--- a/drivers/md/bcache/io.c
+++ b/drivers/md/bcache/io.c
@@ -38,7 +38,7 @@ void __bch_submit_bbio(struct bio *bio, struct cache_set *c)
bio_set_dev(bio, PTR_CACHE(c, &b->key, 0)->bdev);
b->submit_time_us = local_clock_us();
- closure_bio_submit(bio, bio->bi_private);
+ closure_bio_submit(c, bio, bio->bi_private);
}
void bch_submit_bbio(struct bio *bio, struct cache_set *c,
@@ -50,6 +50,20 @@ void bch_submit_bbio(struct bio *bio, struct cache_set *c,
}
/* IO errors */
+void bch_count_backing_io_errors(struct cached_dev *dc, struct bio *bio)
+{
+ char buf[BDEVNAME_SIZE];
+ unsigned errors;
+
+ WARN_ONCE(!dc, "NULL pointer of struct cached_dev");
+
+ errors = atomic_add_return(1, &dc->io_errors);
+ if (errors < dc->error_limit)
+ pr_err("%s: IO error on backing device, unrecoverable",
+ bio_devname(bio, buf));
+ else
+ bch_cached_dev_error(dc);
+}
void bch_count_io_errors(struct cache *ca,
blk_status_t error,
diff --git a/drivers/md/bcache/journal.c b/drivers/md/bcache/journal.c
index 1b736b860739..18f1b5239620 100644
--- a/drivers/md/bcache/journal.c
+++ b/drivers/md/bcache/journal.c
@@ -62,7 +62,7 @@ reread: left = ca->sb.bucket_size - offset;
bio_set_op_attrs(bio, REQ_OP_READ, 0);
bch_bio_map(bio, data);
- closure_bio_submit(bio, &cl);
+ closure_bio_submit(ca->set, bio, &cl);
closure_sync(&cl);
/* This function could be simpler now since we no longer write
@@ -493,7 +493,7 @@ static void journal_reclaim(struct cache_set *c)
struct cache *ca;
uint64_t last_seq;
unsigned iter, n = 0;
- atomic_t p;
+ atomic_t p __maybe_unused;
atomic_long_inc(&c->reclaim);
@@ -594,6 +594,7 @@ static void journal_write_done(struct closure *cl)
}
static void journal_write_unlock(struct closure *cl)
+ __releases(&c->journal.lock)
{
struct cache_set *c = container_of(cl, struct cache_set, journal.io);
@@ -674,7 +675,7 @@ static void journal_write_unlocked(struct closure *cl)
spin_unlock(&c->journal.lock);
while ((bio = bio_list_pop(&list)))
- closure_bio_submit(bio, cl);
+ closure_bio_submit(c, bio, cl);
continue_at(cl, journal_write_done, NULL);
}
@@ -705,6 +706,7 @@ static void journal_try_write(struct cache_set *c)
static struct journal_write *journal_wait_for_write(struct cache_set *c,
unsigned nkeys)
+ __acquires(&c->journal.lock)
{
size_t sectors;
struct closure cl;
diff --git a/drivers/md/bcache/request.c b/drivers/md/bcache/request.c
index 6422846b546e..a65e3365eeb9 100644
--- a/drivers/md/bcache/request.c
+++ b/drivers/md/bcache/request.c
@@ -139,6 +139,7 @@ static void bch_data_invalidate(struct closure *cl)
}
op->insert_data_done = true;
+ /* get in bch_data_insert() */
bio_put(bio);
out:
continue_at(cl, bch_data_insert_keys, op->wq);
@@ -295,6 +296,7 @@ err:
/**
* bch_data_insert - stick some data in the cache
+ * @cl: closure pointer.
*
* This is the starting point for any data to end up in a cache device; it could
* be from a normal write, or a writeback write, or a write to a flash only
@@ -630,6 +632,41 @@ static void request_endio(struct bio *bio)
closure_put(cl);
}
+static void backing_request_endio(struct bio *bio)
+{
+ struct closure *cl = bio->bi_private;
+
+ if (bio->bi_status) {
+ struct search *s = container_of(cl, struct search, cl);
+ struct cached_dev *dc = container_of(s->d,
+ struct cached_dev, disk);
+ /*
+ * If a bio has REQ_PREFLUSH for writeback mode, it is
+ * speically assembled in cached_dev_write() for a non-zero
+ * write request which has REQ_PREFLUSH. we don't set
+ * s->iop.status by this failure, the status will be decided
+ * by result of bch_data_insert() operation.
+ */
+ if (unlikely(s->iop.writeback &&
+ bio->bi_opf & REQ_PREFLUSH)) {
+ char buf[BDEVNAME_SIZE];
+
+ bio_devname(bio, buf);
+ pr_err("Can't flush %s: returned bi_status %i",
+ buf, bio->bi_status);
+ } else {
+ /* set to orig_bio->bi_status in bio_complete() */
+ s->iop.status = bio->bi_status;
+ }
+ s->recoverable = false;
+ /* should count I/O error for backing device here */
+ bch_count_backing_io_errors(dc, bio);
+ }
+
+ bio_put(bio);
+ closure_put(cl);
+}
+
static void bio_complete(struct search *s)
{
if (s->orig_bio) {
@@ -644,13 +681,21 @@ static void bio_complete(struct search *s)
}
}
-static void do_bio_hook(struct search *s, struct bio *orig_bio)
+static void do_bio_hook(struct search *s,
+ struct bio *orig_bio,
+ bio_end_io_t *end_io_fn)
{
struct bio *bio = &s->bio.bio;
bio_init(bio, NULL, 0);
__bio_clone_fast(bio, orig_bio);
- bio->bi_end_io = request_endio;
+ /*
+ * bi_end_io can be set separately somewhere else, e.g. the
+ * variants in,
+ * - cache_bio->bi_end_io from cached_dev_cache_miss()
+ * - n->bi_end_io from cache_lookup_fn()
+ */
+ bio->bi_end_io = end_io_fn;
bio->bi_private = &s->cl;
bio_cnt_set(bio, 3);
@@ -676,7 +721,7 @@ static inline struct search *search_alloc(struct bio *bio,
s = mempool_alloc(d->c->search, GFP_NOIO);
closure_init(&s->cl, NULL);
- do_bio_hook(s, bio);
+ do_bio_hook(s, bio, request_endio);
s->orig_bio = bio;
s->cache_miss = NULL;
@@ -743,11 +788,12 @@ static void cached_dev_read_error(struct closure *cl)
trace_bcache_read_retry(s->orig_bio);
s->iop.status = 0;
- do_bio_hook(s, s->orig_bio);
+ do_bio_hook(s, s->orig_bio, backing_request_endio);
/* XXX: invalidate cache */
- closure_bio_submit(bio, cl);
+ /* I/O request sent to backing device */
+ closure_bio_submit(s->iop.c, bio, cl);
}
continue_at(cl, cached_dev_cache_miss_done, NULL);
@@ -859,7 +905,7 @@ static int cached_dev_cache_miss(struct btree *b, struct search *s,
bio_copy_dev(cache_bio, miss);
cache_bio->bi_iter.bi_size = s->insert_bio_sectors << 9;
- cache_bio->bi_end_io = request_endio;
+ cache_bio->bi_end_io = backing_request_endio;
cache_bio->bi_private = &s->cl;
bch_bio_map(cache_bio, NULL);
@@ -872,15 +918,17 @@ static int cached_dev_cache_miss(struct btree *b, struct search *s,
s->cache_miss = miss;
s->iop.bio = cache_bio;
bio_get(cache_bio);
- closure_bio_submit(cache_bio, &s->cl);
+ /* I/O request sent to backing device */
+ closure_bio_submit(s->iop.c, cache_bio, &s->cl);
return ret;
out_put:
bio_put(cache_bio);
out_submit:
- miss->bi_end_io = request_endio;
+ miss->bi_end_io = backing_request_endio;
miss->bi_private = &s->cl;
- closure_bio_submit(miss, &s->cl);
+ /* I/O request sent to backing device */
+ closure_bio_submit(s->iop.c, miss, &s->cl);
return ret;
}
@@ -943,31 +991,46 @@ static void cached_dev_write(struct cached_dev *dc, struct search *s)
s->iop.bio = s->orig_bio;
bio_get(s->iop.bio);
- if ((bio_op(bio) != REQ_OP_DISCARD) ||
- blk_queue_discard(bdev_get_queue(dc->bdev)))
- closure_bio_submit(bio, cl);
+ if (bio_op(bio) == REQ_OP_DISCARD &&
+ !blk_queue_discard(bdev_get_queue(dc->bdev)))
+ goto insert_data;
+
+ /* I/O request sent to backing device */
+ bio->bi_end_io = backing_request_endio;
+ closure_bio_submit(s->iop.c, bio, cl);
+
} else if (s->iop.writeback) {
bch_writeback_add(dc);
s->iop.bio = bio;
if (bio->bi_opf & REQ_PREFLUSH) {
- /* Also need to send a flush to the backing device */
- struct bio *flush = bio_alloc_bioset(GFP_NOIO, 0,
- dc->disk.bio_split);
-
+ /*
+ * Also need to send a flush to the backing
+ * device.
+ */
+ struct bio *flush;
+
+ flush = bio_alloc_bioset(GFP_NOIO, 0,
+ dc->disk.bio_split);
+ if (!flush) {
+ s->iop.status = BLK_STS_RESOURCE;
+ goto insert_data;
+ }
bio_copy_dev(flush, bio);
- flush->bi_end_io = request_endio;
+ flush->bi_end_io = backing_request_endio;
flush->bi_private = cl;
flush->bi_opf = REQ_OP_WRITE | REQ_PREFLUSH;
-
- closure_bio_submit(flush, cl);
+ /* I/O request sent to backing device */
+ closure_bio_submit(s->iop.c, flush, cl);
}
} else {
s->iop.bio = bio_clone_fast(bio, GFP_NOIO, dc->disk.bio_split);
-
- closure_bio_submit(bio, cl);
+ /* I/O request sent to backing device */
+ bio->bi_end_io = backing_request_endio;
+ closure_bio_submit(s->iop.c, bio, cl);
}
+insert_data:
closure_call(&s->iop.cl, bch_data_insert, NULL, cl);
continue_at(cl, cached_dev_write_complete, NULL);
}
@@ -981,11 +1044,67 @@ static void cached_dev_nodata(struct closure *cl)
bch_journal_meta(s->iop.c, cl);
/* If it's a flush, we send the flush to the backing device too */
- closure_bio_submit(bio, cl);
+ bio->bi_end_io = backing_request_endio;
+ closure_bio_submit(s->iop.c, bio, cl);
continue_at(cl, cached_dev_bio_complete, NULL);
}
+struct detached_dev_io_private {
+ struct bcache_device *d;
+ unsigned long start_time;
+ bio_end_io_t *bi_end_io;
+ void *bi_private;
+};
+
+static void detached_dev_end_io(struct bio *bio)
+{
+ struct detached_dev_io_private *ddip;
+
+ ddip = bio->bi_private;
+ bio->bi_end_io = ddip->bi_end_io;
+ bio->bi_private = ddip->bi_private;
+
+ generic_end_io_acct(ddip->d->disk->queue,
+ bio_data_dir(bio),
+ &ddip->d->disk->part0, ddip->start_time);
+
+ if (bio->bi_status) {
+ struct cached_dev *dc = container_of(ddip->d,
+ struct cached_dev, disk);
+ /* should count I/O error for backing device here */
+ bch_count_backing_io_errors(dc, bio);
+ }
+
+ kfree(ddip);
+ bio->bi_end_io(bio);
+}
+
+static void detached_dev_do_request(struct bcache_device *d, struct bio *bio)
+{
+ struct detached_dev_io_private *ddip;
+ struct cached_dev *dc = container_of(d, struct cached_dev, disk);
+
+ /*
+ * no need to call closure_get(&dc->disk.cl),
+ * because upper layer had already opened bcache device,
+ * which would call closure_get(&dc->disk.cl)
+ */
+ ddip = kzalloc(sizeof(struct detached_dev_io_private), GFP_NOIO);
+ ddip->d = d;
+ ddip->start_time = jiffies;
+ ddip->bi_end_io = bio->bi_end_io;
+ ddip->bi_private = bio->bi_private;
+ bio->bi_end_io = detached_dev_end_io;
+ bio->bi_private = ddip;
+
+ if ((bio_op(bio) == REQ_OP_DISCARD) &&
+ !blk_queue_discard(bdev_get_queue(dc->bdev)))
+ bio->bi_end_io(bio);
+ else
+ generic_make_request(bio);
+}
+
/* Cached devices - read & write stuff */
static blk_qc_t cached_dev_make_request(struct request_queue *q,
@@ -996,6 +1115,13 @@ static blk_qc_t cached_dev_make_request(struct request_queue *q,
struct cached_dev *dc = container_of(d, struct cached_dev, disk);
int rw = bio_data_dir(bio);
+ if (unlikely((d->c && test_bit(CACHE_SET_IO_DISABLE, &d->c->flags)) ||
+ dc->io_disable)) {
+ bio->bi_status = BLK_STS_IOERR;
+ bio_endio(bio);
+ return BLK_QC_T_NONE;
+ }
+
atomic_set(&dc->backing_idle, 0);
generic_start_io_acct(q, rw, bio_sectors(bio), &d->disk->part0);
@@ -1022,13 +1148,9 @@ static blk_qc_t cached_dev_make_request(struct request_queue *q,
else
cached_dev_read(dc, s);
}
- } else {
- if ((bio_op(bio) == REQ_OP_DISCARD) &&
- !blk_queue_discard(bdev_get_queue(dc->bdev)))
- bio_endio(bio);
- else
- generic_make_request(bio);
- }
+ } else
+ /* I/O request sent to backing device */
+ detached_dev_do_request(d, bio);
return BLK_QC_T_NONE;
}
@@ -1112,6 +1234,12 @@ static blk_qc_t flash_dev_make_request(struct request_queue *q,
struct bcache_device *d = bio->bi_disk->private_data;
int rw = bio_data_dir(bio);
+ if (unlikely(d->c && test_bit(CACHE_SET_IO_DISABLE, &d->c->flags))) {
+ bio->bi_status = BLK_STS_IOERR;
+ bio_endio(bio);
+ return BLK_QC_T_NONE;
+ }
+
generic_start_io_acct(q, rw, bio_sectors(bio), &d->disk->part0);
s = search_alloc(bio, d);
diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
index f2273143b3cb..d90d9e59ca00 100644
--- a/drivers/md/bcache/super.c
+++ b/drivers/md/bcache/super.c
@@ -47,6 +47,14 @@ const char * const bch_cache_modes[] = {
NULL
};
+/* Default is -1; we skip past it for stop_when_cache_set_failed */
+const char * const bch_stop_on_failure_modes[] = {
+ "default",
+ "auto",
+ "always",
+ NULL
+};
+
static struct kobject *bcache_kobj;
struct mutex bch_register_lock;
LIST_HEAD(bch_cache_sets);
@@ -265,6 +273,7 @@ void bch_write_bdev_super(struct cached_dev *dc, struct closure *parent)
bio->bi_private = dc;
closure_get(cl);
+ /* I/O request sent to backing device */
__write_super(&dc->sb, bio);
closure_return_with_destructor(cl, bch_write_bdev_super_unlock);
@@ -521,7 +530,7 @@ static void prio_io(struct cache *ca, uint64_t bucket, int op,
bio_set_op_attrs(bio, op, REQ_SYNC|REQ_META|op_flags);
bch_bio_map(bio, ca->disk_buckets);
- closure_bio_submit(bio, &ca->prio);
+ closure_bio_submit(ca->set, bio, &ca->prio);
closure_sync(cl);
}
@@ -769,6 +778,8 @@ static int bcache_device_init(struct bcache_device *d, unsigned block_size,
sector_t sectors)
{
struct request_queue *q;
+ const size_t max_stripes = min_t(size_t, INT_MAX,
+ SIZE_MAX / sizeof(atomic_t));
size_t n;
int idx;
@@ -777,9 +788,7 @@ static int bcache_device_init(struct bcache_device *d, unsigned block_size,
d->nr_stripes = DIV_ROUND_UP_ULL(sectors, d->stripe_size);
- if (!d->nr_stripes ||
- d->nr_stripes > INT_MAX ||
- d->nr_stripes > SIZE_MAX / sizeof(atomic_t)) {
+ if (!d->nr_stripes || d->nr_stripes > max_stripes) {
pr_err("nr_stripes too large or invalid: %u (start sector beyond end of disk?)",
(unsigned)d->nr_stripes);
return -ENOMEM;
@@ -833,9 +842,9 @@ static int bcache_device_init(struct bcache_device *d, unsigned block_size,
q->limits.io_min = block_size;
q->limits.logical_block_size = block_size;
q->limits.physical_block_size = block_size;
- set_bit(QUEUE_FLAG_NONROT, &d->disk->queue->queue_flags);
- clear_bit(QUEUE_FLAG_ADD_RANDOM, &d->disk->queue->queue_flags);
- set_bit(QUEUE_FLAG_DISCARD, &d->disk->queue->queue_flags);
+ blk_queue_flag_set(QUEUE_FLAG_NONROT, d->disk->queue);
+ blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, d->disk->queue);
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, d->disk->queue);
blk_queue_write_cache(q, true, true);
@@ -899,6 +908,31 @@ void bch_cached_dev_run(struct cached_dev *dc)
pr_debug("error creating sysfs link");
}
+/*
+ * If BCACHE_DEV_RATE_DW_RUNNING is set, it means routine of the delayed
+ * work dc->writeback_rate_update is running. Wait until the routine
+ * quits (BCACHE_DEV_RATE_DW_RUNNING is clear), then continue to
+ * cancel it. If BCACHE_DEV_RATE_DW_RUNNING is not clear after time_out
+ * seconds, give up waiting here and continue to cancel it too.
+ */
+static void cancel_writeback_rate_update_dwork(struct cached_dev *dc)
+{
+ int time_out = WRITEBACK_RATE_UPDATE_SECS_MAX * HZ;
+
+ do {
+ if (!test_bit(BCACHE_DEV_RATE_DW_RUNNING,
+ &dc->disk.flags))
+ break;
+ time_out--;
+ schedule_timeout_interruptible(1);
+ } while (time_out > 0);
+
+ if (time_out == 0)
+ pr_warn("give up waiting for dc->writeback_write_update to quit");
+
+ cancel_delayed_work_sync(&dc->writeback_rate_update);
+}
+
static void cached_dev_detach_finish(struct work_struct *w)
{
struct cached_dev *dc = container_of(w, struct cached_dev, detach);
@@ -911,7 +945,9 @@ static void cached_dev_detach_finish(struct work_struct *w)
mutex_lock(&bch_register_lock);
- cancel_delayed_work_sync(&dc->writeback_rate_update);
+ if (test_and_clear_bit(BCACHE_DEV_WB_RUNNING, &dc->disk.flags))
+ cancel_writeback_rate_update_dwork(dc);
+
if (!IS_ERR_OR_NULL(dc->writeback_thread)) {
kthread_stop(dc->writeback_thread);
dc->writeback_thread = NULL;
@@ -954,6 +990,7 @@ void bch_cached_dev_detach(struct cached_dev *dc)
closure_get(&dc->disk.cl);
bch_writeback_queue(dc);
+
cached_dev_put(dc);
}
@@ -1065,7 +1102,6 @@ int bch_cached_dev_attach(struct cached_dev *dc, struct cache_set *c,
if (BDEV_STATE(&dc->sb) == BDEV_STATE_DIRTY) {
bch_sectors_dirty_init(&dc->disk);
atomic_set(&dc->has_dirty, 1);
- refcount_inc(&dc->count);
bch_writeback_queue(dc);
}
@@ -1093,14 +1129,16 @@ static void cached_dev_free(struct closure *cl)
{
struct cached_dev *dc = container_of(cl, struct cached_dev, disk.cl);
- cancel_delayed_work_sync(&dc->writeback_rate_update);
+ mutex_lock(&bch_register_lock);
+
+ if (test_and_clear_bit(BCACHE_DEV_WB_RUNNING, &dc->disk.flags))
+ cancel_writeback_rate_update_dwork(dc);
+
if (!IS_ERR_OR_NULL(dc->writeback_thread))
kthread_stop(dc->writeback_thread);
if (dc->writeback_write_wq)
destroy_workqueue(dc->writeback_write_wq);
- mutex_lock(&bch_register_lock);
-
if (atomic_read(&dc->running))
bd_unlink_disk_holder(dc->bdev, dc->disk.disk);
bcache_device_free(&dc->disk);
@@ -1170,6 +1208,12 @@ static int cached_dev_init(struct cached_dev *dc, unsigned block_size)
max(dc->disk.disk->queue->backing_dev_info->ra_pages,
q->backing_dev_info->ra_pages);
+ atomic_set(&dc->io_errors, 0);
+ dc->io_disable = false;
+ dc->error_limit = DEFAULT_CACHED_DEV_ERROR_LIMIT;
+ /* default to auto */
+ dc->stop_when_cache_set_failed = BCH_CACHED_DEV_STOP_AUTO;
+
bch_cached_dev_request_init(dc);
bch_cached_dev_writeback_init(dc);
return 0;
@@ -1321,6 +1365,24 @@ int bch_flash_dev_create(struct cache_set *c, uint64_t size)
return flash_dev_run(c, u);
}
+bool bch_cached_dev_error(struct cached_dev *dc)
+{
+ char name[BDEVNAME_SIZE];
+
+ if (!dc || test_bit(BCACHE_DEV_CLOSING, &dc->disk.flags))
+ return false;
+
+ dc->io_disable = true;
+ /* make others know io_disable is true earlier */
+ smp_mb();
+
+ pr_err("stop %s: too many IO errors on backing device %s\n",
+ dc->disk.disk->disk_name, bdevname(dc->bdev, name));
+
+ bcache_device_stop(&dc->disk);
+ return true;
+}
+
/* Cache set */
__printf(2, 3)
@@ -1332,6 +1394,9 @@ bool bch_cache_set_error(struct cache_set *c, const char *fmt, ...)
test_bit(CACHE_SET_STOPPING, &c->flags))
return false;
+ if (test_and_set_bit(CACHE_SET_IO_DISABLE, &c->flags))
+ pr_warn("CACHE_SET_IO_DISABLE already set");
+
/* XXX: we can be called from atomic context
acquire_console_sem();
*/
@@ -1443,25 +1508,72 @@ static void cache_set_flush(struct closure *cl)
closure_return(cl);
}
+/*
+ * This function is only called when CACHE_SET_IO_DISABLE is set, which means
+ * cache set is unregistering due to too many I/O errors. In this condition,
+ * the bcache device might be stopped, it depends on stop_when_cache_set_failed
+ * value and whether the broken cache has dirty data:
+ *
+ * dc->stop_when_cache_set_failed dc->has_dirty stop bcache device
+ * BCH_CACHED_STOP_AUTO 0 NO
+ * BCH_CACHED_STOP_AUTO 1 YES
+ * BCH_CACHED_DEV_STOP_ALWAYS 0 YES
+ * BCH_CACHED_DEV_STOP_ALWAYS 1 YES
+ *
+ * The expected behavior is, if stop_when_cache_set_failed is configured to
+ * "auto" via sysfs interface, the bcache device will not be stopped if the
+ * backing device is clean on the broken cache device.
+ */
+static void conditional_stop_bcache_device(struct cache_set *c,
+ struct bcache_device *d,
+ struct cached_dev *dc)
+{
+ if (dc->stop_when_cache_set_failed == BCH_CACHED_DEV_STOP_ALWAYS) {
+ pr_warn("stop_when_cache_set_failed of %s is \"always\", stop it for failed cache set %pU.",
+ d->disk->disk_name, c->sb.set_uuid);
+ bcache_device_stop(d);
+ } else if (atomic_read(&dc->has_dirty)) {
+ /*
+ * dc->stop_when_cache_set_failed == BCH_CACHED_STOP_AUTO
+ * and dc->has_dirty == 1
+ */
+ pr_warn("stop_when_cache_set_failed of %s is \"auto\" and cache is dirty, stop it to avoid potential data corruption.",
+ d->disk->disk_name);
+ bcache_device_stop(d);
+ } else {
+ /*
+ * dc->stop_when_cache_set_failed == BCH_CACHED_STOP_AUTO
+ * and dc->has_dirty == 0
+ */
+ pr_warn("stop_when_cache_set_failed of %s is \"auto\" and cache is clean, keep it alive.",
+ d->disk->disk_name);
+ }
+}
+
static void __cache_set_unregister(struct closure *cl)
{
struct cache_set *c = container_of(cl, struct cache_set, caching);
struct cached_dev *dc;
+ struct bcache_device *d;
size_t i;
mutex_lock(&bch_register_lock);
- for (i = 0; i < c->devices_max_used; i++)
- if (c->devices[i]) {
- if (!UUID_FLASH_ONLY(&c->uuids[i]) &&
- test_bit(CACHE_SET_UNREGISTERING, &c->flags)) {
- dc = container_of(c->devices[i],
- struct cached_dev, disk);
- bch_cached_dev_detach(dc);
- } else {
- bcache_device_stop(c->devices[i]);
- }
+ for (i = 0; i < c->devices_max_used; i++) {
+ d = c->devices[i];
+ if (!d)
+ continue;
+
+ if (!UUID_FLASH_ONLY(&c->uuids[i]) &&
+ test_bit(CACHE_SET_UNREGISTERING, &c->flags)) {
+ dc = container_of(d, struct cached_dev, disk);
+ bch_cached_dev_detach(dc);
+ if (test_bit(CACHE_SET_IO_DISABLE, &c->flags))
+ conditional_stop_bcache_device(c, d, dc);
+ } else {
+ bcache_device_stop(d);
}
+ }
mutex_unlock(&bch_register_lock);
@@ -1567,6 +1679,7 @@ struct cache_set *bch_cache_set_alloc(struct cache_sb *sb)
c->congested_read_threshold_us = 2000;
c->congested_write_threshold_us = 20000;
c->error_limit = DEFAULT_IO_ERROR_LIMIT;
+ WARN_ON(test_and_clear_bit(CACHE_SET_IO_DISABLE, &c->flags));
return c;
err:
@@ -2148,7 +2261,6 @@ static int __init bcache_init(void)
mutex_init(&bch_register_lock);
init_waitqueue_head(&unregister_wait);
register_reboot_notifier(&reboot);
- closure_debug_init();
bcache_major = register_blkdev(0, "bcache");
if (bcache_major < 0) {
@@ -2160,7 +2272,7 @@ static int __init bcache_init(void)
if (!(bcache_wq = alloc_workqueue("bcache", WQ_MEM_RECLAIM, 0)) ||
!(bcache_kobj = kobject_create_and_add("bcache", fs_kobj)) ||
bch_request_init() ||
- bch_debug_init(bcache_kobj) ||
+ bch_debug_init(bcache_kobj) || closure_debug_init() ||
sysfs_create_files(bcache_kobj, files))
goto err;
diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c
index 78cd7bd50fdd..dfeef583ee50 100644
--- a/drivers/md/bcache/sysfs.c
+++ b/drivers/md/bcache/sysfs.c
@@ -78,6 +78,7 @@ rw_attribute(congested_write_threshold_us);
rw_attribute(sequential_cutoff);
rw_attribute(data_csum);
rw_attribute(cache_mode);
+rw_attribute(stop_when_cache_set_failed);
rw_attribute(writeback_metadata);
rw_attribute(writeback_running);
rw_attribute(writeback_percent);
@@ -95,6 +96,7 @@ read_attribute(partial_stripes_expensive);
rw_attribute(synchronous);
rw_attribute(journal_delay_ms);
+rw_attribute(io_disable);
rw_attribute(discard);
rw_attribute(running);
rw_attribute(label);
@@ -125,6 +127,12 @@ SHOW(__bch_cached_dev)
bch_cache_modes + 1,
BDEV_CACHE_MODE(&dc->sb));
+ if (attr == &sysfs_stop_when_cache_set_failed)
+ return bch_snprint_string_list(buf, PAGE_SIZE,
+ bch_stop_on_failure_modes + 1,
+ dc->stop_when_cache_set_failed);
+
+
sysfs_printf(data_csum, "%i", dc->disk.data_csum);
var_printf(verify, "%i");
var_printf(bypass_torture_test, "%i");
@@ -133,7 +141,9 @@ SHOW(__bch_cached_dev)
var_print(writeback_delay);
var_print(writeback_percent);
sysfs_hprint(writeback_rate, dc->writeback_rate.rate << 9);
-
+ sysfs_hprint(io_errors, atomic_read(&dc->io_errors));
+ sysfs_printf(io_error_limit, "%i", dc->error_limit);
+ sysfs_printf(io_disable, "%i", dc->io_disable);
var_print(writeback_rate_update_seconds);
var_print(writeback_rate_i_term_inverse);
var_print(writeback_rate_p_term_inverse);
@@ -173,7 +183,7 @@ SHOW(__bch_cached_dev)
sysfs_hprint(dirty_data,
bcache_dev_sectors_dirty(&dc->disk) << 9);
- sysfs_hprint(stripe_size, dc->disk.stripe_size << 9);
+ sysfs_hprint(stripe_size, ((uint64_t)dc->disk.stripe_size) << 9);
var_printf(partial_stripes_expensive, "%u");
var_hprint(sequential_cutoff);
@@ -224,6 +234,14 @@ STORE(__cached_dev)
d_strtoul(writeback_rate_i_term_inverse);
d_strtoul_nonzero(writeback_rate_p_term_inverse);
+ sysfs_strtoul_clamp(io_error_limit, dc->error_limit, 0, INT_MAX);
+
+ if (attr == &sysfs_io_disable) {
+ int v = strtoul_or_return(buf);
+
+ dc->io_disable = v ? 1 : 0;
+ }
+
d_strtoi_h(sequential_cutoff);
d_strtoi_h(readahead);
@@ -246,6 +264,15 @@ STORE(__cached_dev)
}
}
+ if (attr == &sysfs_stop_when_cache_set_failed) {
+ v = bch_read_string_list(buf, bch_stop_on_failure_modes + 1);
+
+ if (v < 0)
+ return v;
+
+ dc->stop_when_cache_set_failed = v;
+ }
+
if (attr == &sysfs_label) {
if (size > SB_LABEL_SIZE)
return -EINVAL;
@@ -309,7 +336,8 @@ STORE(bch_cached_dev)
bch_writeback_queue(dc);
if (attr == &sysfs_writeback_percent)
- schedule_delayed_work(&dc->writeback_rate_update,
+ if (!test_and_set_bit(BCACHE_DEV_WB_RUNNING, &dc->disk.flags))
+ schedule_delayed_work(&dc->writeback_rate_update,
dc->writeback_rate_update_seconds * HZ);
mutex_unlock(&bch_register_lock);
@@ -324,6 +352,7 @@ static struct attribute *bch_cached_dev_files[] = {
&sysfs_data_csum,
#endif
&sysfs_cache_mode,
+ &sysfs_stop_when_cache_set_failed,
&sysfs_writeback_metadata,
&sysfs_writeback_running,
&sysfs_writeback_delay,
@@ -333,6 +362,9 @@ static struct attribute *bch_cached_dev_files[] = {
&sysfs_writeback_rate_i_term_inverse,
&sysfs_writeback_rate_p_term_inverse,
&sysfs_writeback_rate_debug,
+ &sysfs_errors,
+ &sysfs_io_error_limit,
+ &sysfs_io_disable,
&sysfs_dirty_data,
&sysfs_stripe_size,
&sysfs_partial_stripes_expensive,
@@ -590,6 +622,8 @@ SHOW(__bch_cache_set)
sysfs_printf(gc_always_rewrite, "%i", c->gc_always_rewrite);
sysfs_printf(btree_shrinker_disabled, "%i", c->shrinker_disabled);
sysfs_printf(copy_gc_enabled, "%i", c->copy_gc_enabled);
+ sysfs_printf(io_disable, "%i",
+ test_bit(CACHE_SET_IO_DISABLE, &c->flags));
if (attr == &sysfs_bset_tree_stats)
return bch_bset_print_stats(c, buf);
@@ -679,6 +713,20 @@ STORE(__bch_cache_set)
if (attr == &sysfs_io_error_halflife)
c->error_decay = strtoul_or_return(buf) / 88;
+ if (attr == &sysfs_io_disable) {
+ int v = strtoul_or_return(buf);
+
+ if (v) {
+ if (test_and_set_bit(CACHE_SET_IO_DISABLE,
+ &c->flags))
+ pr_warn("CACHE_SET_IO_DISABLE already set");
+ } else {
+ if (!test_and_clear_bit(CACHE_SET_IO_DISABLE,
+ &c->flags))
+ pr_warn("CACHE_SET_IO_DISABLE already cleared");
+ }
+ }
+
sysfs_strtoul(journal_delay_ms, c->journal_delay_ms);
sysfs_strtoul(verify, c->verify);
sysfs_strtoul(key_merging_disabled, c->key_merging_disabled);
@@ -764,6 +812,7 @@ static struct attribute *bch_cache_set_internal_files[] = {
&sysfs_gc_always_rewrite,
&sysfs_btree_shrinker_disabled,
&sysfs_copy_gc_enabled,
+ &sysfs_io_disable,
NULL
};
KTYPE(bch_cache_set_internal);
diff --git a/drivers/md/bcache/util.c b/drivers/md/bcache/util.c
index a23cd6a14b74..74febd5230df 100644
--- a/drivers/md/bcache/util.c
+++ b/drivers/md/bcache/util.c
@@ -32,20 +32,27 @@ int bch_ ## name ## _h(const char *cp, type *res) \
case 'y': \
case 'z': \
u++; \
+ /* fall through */ \
case 'e': \
u++; \
+ /* fall through */ \
case 'p': \
u++; \
+ /* fall through */ \
case 't': \
u++; \
+ /* fall through */ \
case 'g': \
u++; \
+ /* fall through */ \
case 'm': \
u++; \
+ /* fall through */ \
case 'k': \
u++; \
if (e++ == cp) \
return -EINVAL; \
+ /* fall through */ \
case '\n': \
case '\0': \
if (*e == '\n') \
@@ -75,10 +82,9 @@ STRTO_H(strtoll, long long)
STRTO_H(strtoull, unsigned long long)
/**
- * bch_hprint() - formats @v to human readable string for sysfs.
- *
- * @v - signed 64 bit integer
- * @buf - the (at least 8 byte) buffer to format the result into.
+ * bch_hprint - formats @v to human readable string for sysfs.
+ * @buf: the (at least 8 byte) buffer to format the result into.
+ * @v: signed 64 bit integer
*
* Returns the number of bytes used by format.
*/
@@ -218,13 +224,12 @@ void bch_time_stats_update(struct time_stats *stats, uint64_t start_time)
}
/**
- * bch_next_delay() - increment @d by the amount of work done, and return how
- * long to delay until the next time to do some work.
- *
- * @d - the struct bch_ratelimit to update
- * @done - the amount of work done, in arbitrary units
+ * bch_next_delay() - update ratelimiting statistics and calculate next delay
+ * @d: the struct bch_ratelimit to update
+ * @done: the amount of work done, in arbitrary units
*
- * Returns the amount of time to delay by, in jiffies
+ * Increment @d by the amount of work done, and return how long to delay in
+ * jiffies until the next time to do some work.
*/
uint64_t bch_next_delay(struct bch_ratelimit *d, uint64_t done)
{
diff --git a/drivers/md/bcache/util.h b/drivers/md/bcache/util.h
index a6763db7f061..268024529edd 100644
--- a/drivers/md/bcache/util.h
+++ b/drivers/md/bcache/util.h
@@ -567,12 +567,6 @@ static inline sector_t bdev_sectors(struct block_device *bdev)
return bdev->bd_inode->i_size >> 9;
}
-#define closure_bio_submit(bio, cl) \
-do { \
- closure_get(cl); \
- generic_make_request(bio); \
-} while (0)
-
uint64_t bch_crc64_update(uint64_t, const void *, size_t);
uint64_t bch_crc64(const void *, size_t);
diff --git a/drivers/md/bcache/writeback.c b/drivers/md/bcache/writeback.c
index f1d2fc15abcc..4a9547cdcdc5 100644
--- a/drivers/md/bcache/writeback.c
+++ b/drivers/md/bcache/writeback.c
@@ -114,6 +114,27 @@ static void update_writeback_rate(struct work_struct *work)
struct cached_dev *dc = container_of(to_delayed_work(work),
struct cached_dev,
writeback_rate_update);
+ struct cache_set *c = dc->disk.c;
+
+ /*
+ * should check BCACHE_DEV_RATE_DW_RUNNING before calling
+ * cancel_delayed_work_sync().
+ */
+ set_bit(BCACHE_DEV_RATE_DW_RUNNING, &dc->disk.flags);
+ /* paired with where BCACHE_DEV_RATE_DW_RUNNING is tested */
+ smp_mb();
+
+ /*
+ * CACHE_SET_IO_DISABLE might be set via sysfs interface,
+ * check it here too.
+ */
+ if (!test_bit(BCACHE_DEV_WB_RUNNING, &dc->disk.flags) ||
+ test_bit(CACHE_SET_IO_DISABLE, &c->flags)) {
+ clear_bit(BCACHE_DEV_RATE_DW_RUNNING, &dc->disk.flags);
+ /* paired with where BCACHE_DEV_RATE_DW_RUNNING is tested */
+ smp_mb();
+ return;
+ }
down_read(&dc->writeback_lock);
@@ -123,8 +144,23 @@ static void update_writeback_rate(struct work_struct *work)
up_read(&dc->writeback_lock);
- schedule_delayed_work(&dc->writeback_rate_update,
+ /*
+ * CACHE_SET_IO_DISABLE might be set via sysfs interface,
+ * check it here too.
+ */
+ if (test_bit(BCACHE_DEV_WB_RUNNING, &dc->disk.flags) &&
+ !test_bit(CACHE_SET_IO_DISABLE, &c->flags)) {
+ schedule_delayed_work(&dc->writeback_rate_update,
dc->writeback_rate_update_seconds * HZ);
+ }
+
+ /*
+ * should check BCACHE_DEV_RATE_DW_RUNNING before calling
+ * cancel_delayed_work_sync().
+ */
+ clear_bit(BCACHE_DEV_RATE_DW_RUNNING, &dc->disk.flags);
+ /* paired with where BCACHE_DEV_RATE_DW_RUNNING is tested */
+ smp_mb();
}
static unsigned writeback_delay(struct cached_dev *dc, unsigned sectors)
@@ -253,7 +289,8 @@ static void write_dirty(struct closure *cl)
bio_set_dev(&io->bio, io->dc->bdev);
io->bio.bi_end_io = dirty_endio;
- closure_bio_submit(&io->bio, cl);
+ /* I/O request sent to backing device */
+ closure_bio_submit(io->dc->disk.c, &io->bio, cl);
}
atomic_set(&dc->writeback_sequence_next, next_sequence);
@@ -279,7 +316,7 @@ static void read_dirty_submit(struct closure *cl)
{
struct dirty_io *io = container_of(cl, struct dirty_io, cl);
- closure_bio_submit(&io->bio, cl);
+ closure_bio_submit(io->dc->disk.c, &io->bio, cl);
continue_at(cl, write_dirty, io->dc->writeback_write_wq);
}
@@ -305,7 +342,9 @@ static void read_dirty(struct cached_dev *dc)
next = bch_keybuf_next(&dc->writeback_keys);
- while (!kthread_should_stop() && next) {
+ while (!kthread_should_stop() &&
+ !test_bit(CACHE_SET_IO_DISABLE, &dc->disk.c->flags) &&
+ next) {
size = 0;
nk = 0;
@@ -402,7 +441,9 @@ static void read_dirty(struct cached_dev *dc)
}
}
- while (!kthread_should_stop() && delay) {
+ while (!kthread_should_stop() &&
+ !test_bit(CACHE_SET_IO_DISABLE, &dc->disk.c->flags) &&
+ delay) {
schedule_timeout_interruptible(delay);
delay = writeback_delay(dc, 0);
}
@@ -558,21 +599,30 @@ static bool refill_dirty(struct cached_dev *dc)
static int bch_writeback_thread(void *arg)
{
struct cached_dev *dc = arg;
+ struct cache_set *c = dc->disk.c;
bool searched_full_index;
bch_ratelimit_reset(&dc->writeback_rate);
- while (!kthread_should_stop()) {
+ while (!kthread_should_stop() &&
+ !test_bit(CACHE_SET_IO_DISABLE, &c->flags)) {
down_write(&dc->writeback_lock);
set_current_state(TASK_INTERRUPTIBLE);
- if (!atomic_read(&dc->has_dirty) ||
- (!test_bit(BCACHE_DEV_DETACHING, &dc->disk.flags) &&
- !dc->writeback_running)) {
+ /*
+ * If the bache device is detaching, skip here and continue
+ * to perform writeback. Otherwise, if no dirty data on cache,
+ * or there is dirty data on cache but writeback is disabled,
+ * the writeback thread should sleep here and wait for others
+ * to wake up it.
+ */
+ if (!test_bit(BCACHE_DEV_DETACHING, &dc->disk.flags) &&
+ (!atomic_read(&dc->has_dirty) || !dc->writeback_running)) {
up_write(&dc->writeback_lock);
- if (kthread_should_stop()) {
+ if (kthread_should_stop() ||
+ test_bit(CACHE_SET_IO_DISABLE, &c->flags)) {
set_current_state(TASK_RUNNING);
- return 0;
+ break;
}
schedule();
@@ -585,9 +635,16 @@ static int bch_writeback_thread(void *arg)
if (searched_full_index &&
RB_EMPTY_ROOT(&dc->writeback_keys.keys)) {
atomic_set(&dc->has_dirty, 0);
- cached_dev_put(dc);
SET_BDEV_STATE(&dc->sb, BDEV_STATE_CLEAN);
bch_write_bdev_super(dc, NULL);
+ /*
+ * If bcache device is detaching via sysfs interface,
+ * writeback thread should stop after there is no dirty
+ * data on cache. BCACHE_DEV_DETACHING flag is set in
+ * bch_cached_dev_detach().
+ */
+ if (test_bit(BCACHE_DEV_DETACHING, &dc->disk.flags))
+ break;
}
up_write(&dc->writeback_lock);
@@ -599,6 +656,7 @@ static int bch_writeback_thread(void *arg)
while (delay &&
!kthread_should_stop() &&
+ !test_bit(CACHE_SET_IO_DISABLE, &c->flags) &&
!test_bit(BCACHE_DEV_DETACHING, &dc->disk.flags))
delay = schedule_timeout_interruptible(delay);
@@ -606,6 +664,9 @@ static int bch_writeback_thread(void *arg)
}
}
+ cached_dev_put(dc);
+ wait_for_kthread_stop();
+
return 0;
}
@@ -659,6 +720,7 @@ void bch_cached_dev_writeback_init(struct cached_dev *dc)
dc->writeback_rate_p_term_inverse = 40;
dc->writeback_rate_i_term_inverse = 10000;
+ WARN_ON(test_and_clear_bit(BCACHE_DEV_WB_RUNNING, &dc->disk.flags));
INIT_DELAYED_WORK(&dc->writeback_rate_update, update_writeback_rate);
}
@@ -669,11 +731,15 @@ int bch_cached_dev_writeback_start(struct cached_dev *dc)
if (!dc->writeback_write_wq)
return -ENOMEM;
+ cached_dev_get(dc);
dc->writeback_thread = kthread_create(bch_writeback_thread, dc,
"bcache_writeback");
- if (IS_ERR(dc->writeback_thread))
+ if (IS_ERR(dc->writeback_thread)) {
+ cached_dev_put(dc);
return PTR_ERR(dc->writeback_thread);
+ }
+ WARN_ON(test_and_set_bit(BCACHE_DEV_WB_RUNNING, &dc->disk.flags));
schedule_delayed_work(&dc->writeback_rate_update,
dc->writeback_rate_update_seconds * HZ);
diff --git a/drivers/md/bcache/writeback.h b/drivers/md/bcache/writeback.h
index 587b25599856..610fb01de629 100644
--- a/drivers/md/bcache/writeback.h
+++ b/drivers/md/bcache/writeback.h
@@ -39,7 +39,7 @@ static inline uint64_t bcache_flash_devs_sectors_dirty(struct cache_set *c)
if (!d || !UUID_FLASH_ONLY(&c->uuids[i]))
continue;
- ret += bcache_dev_sectors_dirty(d);
+ ret += bcache_dev_sectors_dirty(d);
}
mutex_unlock(&bch_register_lock);
@@ -105,8 +105,6 @@ static inline void bch_writeback_add(struct cached_dev *dc)
{
if (!atomic_read(&dc->has_dirty) &&
!atomic_xchg(&dc->has_dirty, 1)) {
- refcount_inc(&dc->count);
-
if (BDEV_STATE(&dc->sb) != BDEV_STATE_DIRTY) {
SET_BDEV_STATE(&dc->sb, BDEV_STATE_DIRTY);
/* XXX: should do this synchronously */
diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c
index aa2032fa80d4..12aa9ca21d8c 100644
--- a/drivers/md/dm-bufio.c
+++ b/drivers/md/dm-bufio.c
@@ -6,7 +6,7 @@
* This file is released under the GPL.
*/
-#include "dm-bufio.h"
+#include <linux/dm-bufio.h>
#include <linux/device-mapper.h>
#include <linux/dm-io.h>
@@ -51,19 +51,6 @@
#define DM_BUFIO_DEFAULT_RETAIN_BYTES (256 * 1024)
/*
- * The number of bvec entries that are embedded directly in the buffer.
- * If the chunk size is larger, dm-io is used to do the io.
- */
-#define DM_BUFIO_INLINE_VECS 16
-
-/*
- * Don't try to use kmem_cache_alloc for blocks larger than this.
- * For explanation, see alloc_buffer_data below.
- */
-#define DM_BUFIO_BLOCK_SIZE_SLAB_LIMIT (PAGE_SIZE >> 1)
-#define DM_BUFIO_BLOCK_SIZE_GFP_LIMIT (PAGE_SIZE << (MAX_ORDER - 1))
-
-/*
* Align buffer writes to this boundary.
* Tests show that SSDs have the highest IOPS when using 4k writes.
*/
@@ -99,13 +86,12 @@ struct dm_bufio_client {
struct block_device *bdev;
unsigned block_size;
- unsigned char sectors_per_block_bits;
- unsigned char pages_per_block_bits;
- unsigned char blocks_per_page_bits;
- unsigned aux_size;
+ s8 sectors_per_block_bits;
void (*alloc_callback)(struct dm_buffer *);
void (*write_callback)(struct dm_buffer *);
+ struct kmem_cache *slab_buffer;
+ struct kmem_cache *slab_cache;
struct dm_io_client *dm_io;
struct list_head reserved_buffers;
@@ -148,11 +134,11 @@ struct dm_buffer {
struct list_head lru_list;
sector_t block;
void *data;
- enum data_mode data_mode;
+ unsigned char data_mode; /* DATA_MODE_* */
unsigned char list_mode; /* LIST_* */
- unsigned hold_count;
blk_status_t read_error;
blk_status_t write_error;
+ unsigned hold_count;
unsigned long state;
unsigned long last_accessed;
unsigned dirty_start;
@@ -161,8 +147,7 @@ struct dm_buffer {
unsigned write_end;
struct dm_bufio_client *c;
struct list_head write_list;
- struct bio bio;
- struct bio_vec bio_vec[DM_BUFIO_INLINE_VECS];
+ void (*end_io)(struct dm_buffer *, blk_status_t);
#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
#define MAX_STACK 10
struct stack_trace stack_trace;
@@ -172,21 +157,6 @@ struct dm_buffer {
/*----------------------------------------------------------------*/
-static struct kmem_cache *dm_bufio_caches[PAGE_SHIFT - SECTOR_SHIFT];
-static char *dm_bufio_cache_names[PAGE_SHIFT - SECTOR_SHIFT];
-
-static inline int dm_bufio_cache_index(struct dm_bufio_client *c)
-{
- unsigned ret = c->blocks_per_page_bits - 1;
-
- BUG_ON(ret >= ARRAY_SIZE(dm_bufio_caches));
-
- return ret;
-}
-
-#define DM_BUFIO_CACHE(c) (dm_bufio_caches[dm_bufio_cache_index(c)])
-#define DM_BUFIO_CACHE_NAME(c) (dm_bufio_cache_names[dm_bufio_cache_index(c)])
-
#define dm_bufio_in_request() (!!current->bio_list)
static void dm_bufio_lock(struct dm_bufio_client *c)
@@ -319,7 +289,7 @@ static void __remove(struct dm_bufio_client *c, struct dm_buffer *b)
/*----------------------------------------------------------------*/
-static void adjust_total_allocated(enum data_mode data_mode, long diff)
+static void adjust_total_allocated(unsigned char data_mode, long diff)
{
static unsigned long * const class_ptr[DATA_MODE_LIMIT] = {
&dm_bufio_allocated_kmem_cache,
@@ -384,18 +354,18 @@ static void __cache_size_refresh(void)
* space.
*/
static void *alloc_buffer_data(struct dm_bufio_client *c, gfp_t gfp_mask,
- enum data_mode *data_mode)
+ unsigned char *data_mode)
{
- if (c->block_size <= DM_BUFIO_BLOCK_SIZE_SLAB_LIMIT) {
+ if (unlikely(c->slab_cache != NULL)) {
*data_mode = DATA_MODE_SLAB;
- return kmem_cache_alloc(DM_BUFIO_CACHE(c), gfp_mask);
+ return kmem_cache_alloc(c->slab_cache, gfp_mask);
}
- if (c->block_size <= DM_BUFIO_BLOCK_SIZE_GFP_LIMIT &&
+ if (c->block_size <= KMALLOC_MAX_SIZE &&
gfp_mask & __GFP_NORETRY) {
*data_mode = DATA_MODE_GET_FREE_PAGES;
return (void *)__get_free_pages(gfp_mask,
- c->pages_per_block_bits);
+ c->sectors_per_block_bits - (PAGE_SHIFT - SECTOR_SHIFT));
}
*data_mode = DATA_MODE_VMALLOC;
@@ -424,15 +394,16 @@ static void *alloc_buffer_data(struct dm_bufio_client *c, gfp_t gfp_mask,
* Free buffer's data.
*/
static void free_buffer_data(struct dm_bufio_client *c,
- void *data, enum data_mode data_mode)
+ void *data, unsigned char data_mode)
{
switch (data_mode) {
case DATA_MODE_SLAB:
- kmem_cache_free(DM_BUFIO_CACHE(c), data);
+ kmem_cache_free(c->slab_cache, data);
break;
case DATA_MODE_GET_FREE_PAGES:
- free_pages((unsigned long)data, c->pages_per_block_bits);
+ free_pages((unsigned long)data,
+ c->sectors_per_block_bits - (PAGE_SHIFT - SECTOR_SHIFT));
break;
case DATA_MODE_VMALLOC:
@@ -451,8 +422,7 @@ static void free_buffer_data(struct dm_bufio_client *c,
*/
static struct dm_buffer *alloc_buffer(struct dm_bufio_client *c, gfp_t gfp_mask)
{
- struct dm_buffer *b = kmalloc(sizeof(struct dm_buffer) + c->aux_size,
- gfp_mask);
+ struct dm_buffer *b = kmem_cache_alloc(c->slab_buffer, gfp_mask);
if (!b)
return NULL;
@@ -461,7 +431,7 @@ static struct dm_buffer *alloc_buffer(struct dm_bufio_client *c, gfp_t gfp_mask)
b->data = alloc_buffer_data(c, gfp_mask, &b->data_mode);
if (!b->data) {
- kfree(b);
+ kmem_cache_free(c->slab_buffer, b);
return NULL;
}
@@ -483,7 +453,7 @@ static void free_buffer(struct dm_buffer *b)
adjust_total_allocated(b->data_mode, -(long)c->block_size);
free_buffer_data(c, b->data, b->data_mode);
- kfree(b);
+ kmem_cache_free(c->slab_buffer, b);
}
/*
@@ -540,10 +510,6 @@ static void __relink_lru(struct dm_buffer *b, int dirty)
*
* the memory must be direct-mapped, not vmalloced;
*
- * the I/O driver can reject requests spuriously if it thinks that
- * the requests are too big for the device or if they cross a
- * controller-defined memory boundary.
- *
* If the buffer is small enough (up to DM_BUFIO_INLINE_VECS pages) and
* it is not vmalloced, try using the bio interface.
*
@@ -561,12 +527,11 @@ static void dmio_complete(unsigned long error, void *context)
{
struct dm_buffer *b = context;
- b->bio.bi_status = error ? BLK_STS_IOERR : 0;
- b->bio.bi_end_io(&b->bio);
+ b->end_io(b, unlikely(error != 0) ? BLK_STS_IOERR : 0);
}
static void use_dmio(struct dm_buffer *b, int rw, sector_t sector,
- unsigned n_sectors, unsigned offset, bio_end_io_t *end_io)
+ unsigned n_sectors, unsigned offset)
{
int r;
struct dm_io_request io_req = {
@@ -590,76 +555,77 @@ static void use_dmio(struct dm_buffer *b, int rw, sector_t sector,
io_req.mem.ptr.vma = (char *)b->data + offset;
}
- b->bio.bi_end_io = end_io;
-
r = dm_io(&io_req, 1, &region, NULL);
- if (r) {
- b->bio.bi_status = errno_to_blk_status(r);
- end_io(&b->bio);
- }
+ if (unlikely(r))
+ b->end_io(b, errno_to_blk_status(r));
}
-static void inline_endio(struct bio *bio)
+static void bio_complete(struct bio *bio)
{
- bio_end_io_t *end_fn = bio->bi_private;
+ struct dm_buffer *b = bio->bi_private;
blk_status_t status = bio->bi_status;
-
- /*
- * Reset the bio to free any attached resources
- * (e.g. bio integrity profiles).
- */
- bio_reset(bio);
-
- bio->bi_status = status;
- end_fn(bio);
+ bio_put(bio);
+ b->end_io(b, status);
}
-static void use_inline_bio(struct dm_buffer *b, int rw, sector_t sector,
- unsigned n_sectors, unsigned offset, bio_end_io_t *end_io)
+static void use_bio(struct dm_buffer *b, int rw, sector_t sector,
+ unsigned n_sectors, unsigned offset)
{
+ struct bio *bio;
char *ptr;
- unsigned len;
+ unsigned vec_size, len;
- bio_init(&b->bio, b->bio_vec, DM_BUFIO_INLINE_VECS);
- b->bio.bi_iter.bi_sector = sector;
- bio_set_dev(&b->bio, b->c->bdev);
- b->bio.bi_end_io = inline_endio;
- /*
- * Use of .bi_private isn't a problem here because
- * the dm_buffer's inline bio is local to bufio.
- */
- b->bio.bi_private = end_io;
- bio_set_op_attrs(&b->bio, rw, 0);
+ vec_size = b->c->block_size >> PAGE_SHIFT;
+ if (unlikely(b->c->sectors_per_block_bits < PAGE_SHIFT - SECTOR_SHIFT))
+ vec_size += 2;
+
+ bio = bio_kmalloc(GFP_NOWAIT | __GFP_NORETRY | __GFP_NOWARN, vec_size);
+ if (!bio) {
+dmio:
+ use_dmio(b, rw, sector, n_sectors, offset);
+ return;
+ }
+
+ bio->bi_iter.bi_sector = sector;
+ bio_set_dev(bio, b->c->bdev);
+ bio_set_op_attrs(bio, rw, 0);
+ bio->bi_end_io = bio_complete;
+ bio->bi_private = b;
ptr = (char *)b->data + offset;
len = n_sectors << SECTOR_SHIFT;
do {
unsigned this_step = min((unsigned)(PAGE_SIZE - offset_in_page(ptr)), len);
- if (!bio_add_page(&b->bio, virt_to_page(ptr), this_step,
+ if (!bio_add_page(bio, virt_to_page(ptr), this_step,
offset_in_page(ptr))) {
- BUG_ON(b->c->block_size <= PAGE_SIZE);
- use_dmio(b, rw, sector, n_sectors, offset, end_io);
- return;
+ bio_put(bio);
+ goto dmio;
}
len -= this_step;
ptr += this_step;
} while (len > 0);
- submit_bio(&b->bio);
+ submit_bio(bio);
}
-static void submit_io(struct dm_buffer *b, int rw, bio_end_io_t *end_io)
+static void submit_io(struct dm_buffer *b, int rw, void (*end_io)(struct dm_buffer *, blk_status_t))
{
unsigned n_sectors;
sector_t sector;
unsigned offset, end;
- sector = (b->block << b->c->sectors_per_block_bits) + b->c->start;
+ b->end_io = end_io;
+
+ if (likely(b->c->sectors_per_block_bits >= 0))
+ sector = b->block << b->c->sectors_per_block_bits;
+ else
+ sector = b->block * (b->c->block_size >> SECTOR_SHIFT);
+ sector += b->c->start;
if (rw != REQ_OP_WRITE) {
- n_sectors = 1 << b->c->sectors_per_block_bits;
+ n_sectors = b->c->block_size >> SECTOR_SHIFT;
offset = 0;
} else {
if (b->c->write_callback)
@@ -676,11 +642,10 @@ static void submit_io(struct dm_buffer *b, int rw, bio_end_io_t *end_io)
n_sectors = (end - offset) >> SECTOR_SHIFT;
}
- if (n_sectors <= ((DM_BUFIO_INLINE_VECS * PAGE_SIZE) >> SECTOR_SHIFT) &&
- b->data_mode != DATA_MODE_VMALLOC)
- use_inline_bio(b, rw, sector, n_sectors, offset, end_io);
+ if (b->data_mode != DATA_MODE_VMALLOC)
+ use_bio(b, rw, sector, n_sectors, offset);
else
- use_dmio(b, rw, sector, n_sectors, offset, end_io);
+ use_dmio(b, rw, sector, n_sectors, offset);
}
/*----------------------------------------------------------------
@@ -693,16 +658,14 @@ static void submit_io(struct dm_buffer *b, int rw, bio_end_io_t *end_io)
* Set the error, clear B_WRITING bit and wake anyone who was waiting on
* it.
*/
-static void write_endio(struct bio *bio)
+static void write_endio(struct dm_buffer *b, blk_status_t status)
{
- struct dm_buffer *b = container_of(bio, struct dm_buffer, bio);
-
- b->write_error = bio->bi_status;
- if (unlikely(bio->bi_status)) {
+ b->write_error = status;
+ if (unlikely(status)) {
struct dm_bufio_client *c = b->c;
(void)cmpxchg(&c->async_write_error, 0,
- blk_status_to_errno(bio->bi_status));
+ blk_status_to_errno(status));
}
BUG_ON(!test_bit(B_WRITING, &b->state));
@@ -963,8 +926,11 @@ static void __get_memory_limit(struct dm_bufio_client *c,
}
}
- buffers = dm_bufio_cache_size_per_client >>
- (c->sectors_per_block_bits + SECTOR_SHIFT);
+ buffers = dm_bufio_cache_size_per_client;
+ if (likely(c->sectors_per_block_bits >= 0))
+ buffers >>= c->sectors_per_block_bits + SECTOR_SHIFT;
+ else
+ buffers /= c->block_size;
if (buffers < c->minimum_buffers)
buffers = c->minimum_buffers;
@@ -1076,11 +1042,9 @@ found_buffer:
* The endio routine for reading: set the error, clear the bit and wake up
* anyone waiting on the buffer.
*/
-static void read_endio(struct bio *bio)
+static void read_endio(struct dm_buffer *b, blk_status_t status)
{
- struct dm_buffer *b = container_of(bio, struct dm_buffer, bio);
-
- b->read_error = bio->bi_status;
+ b->read_error = status;
BUG_ON(!test_bit(B_READING, &b->state));
@@ -1482,13 +1446,13 @@ void dm_bufio_forget(struct dm_bufio_client *c, sector_t block)
dm_bufio_unlock(c);
}
-EXPORT_SYMBOL(dm_bufio_forget);
+EXPORT_SYMBOL_GPL(dm_bufio_forget);
void dm_bufio_set_minimum_buffers(struct dm_bufio_client *c, unsigned n)
{
c->minimum_buffers = n;
}
-EXPORT_SYMBOL(dm_bufio_set_minimum_buffers);
+EXPORT_SYMBOL_GPL(dm_bufio_set_minimum_buffers);
unsigned dm_bufio_get_block_size(struct dm_bufio_client *c)
{
@@ -1498,8 +1462,12 @@ EXPORT_SYMBOL_GPL(dm_bufio_get_block_size);
sector_t dm_bufio_get_device_size(struct dm_bufio_client *c)
{
- return i_size_read(c->bdev->bd_inode) >>
- (SECTOR_SHIFT + c->sectors_per_block_bits);
+ sector_t s = i_size_read(c->bdev->bd_inode) >> SECTOR_SHIFT;
+ if (likely(c->sectors_per_block_bits >= 0))
+ s >>= c->sectors_per_block_bits;
+ else
+ sector_div(s, c->block_size >> SECTOR_SHIFT);
+ return s;
}
EXPORT_SYMBOL_GPL(dm_bufio_get_device_size);
@@ -1597,8 +1565,12 @@ static bool __try_evict_buffer(struct dm_buffer *b, gfp_t gfp)
static unsigned long get_retain_buffers(struct dm_bufio_client *c)
{
- unsigned long retain_bytes = READ_ONCE(dm_bufio_retain_bytes);
- return retain_bytes >> (c->sectors_per_block_bits + SECTOR_SHIFT);
+ unsigned long retain_bytes = READ_ONCE(dm_bufio_retain_bytes);
+ if (likely(c->sectors_per_block_bits >= 0))
+ retain_bytes >>= c->sectors_per_block_bits + SECTOR_SHIFT;
+ else
+ retain_bytes /= c->block_size;
+ return retain_bytes;
}
static unsigned long __scan(struct dm_bufio_client *c, unsigned long nr_to_scan,
@@ -1662,9 +1634,13 @@ struct dm_bufio_client *dm_bufio_client_create(struct block_device *bdev, unsign
int r;
struct dm_bufio_client *c;
unsigned i;
+ char slab_name[27];
- BUG_ON(block_size < 1 << SECTOR_SHIFT ||
- (block_size & (block_size - 1)));
+ if (!block_size || block_size & ((1 << SECTOR_SHIFT) - 1)) {
+ DMERR("%s: block size not specified or is not multiple of 512b", __func__);
+ r = -EINVAL;
+ goto bad_client;
+ }
c = kzalloc(sizeof(*c), GFP_KERNEL);
if (!c) {
@@ -1675,13 +1651,11 @@ struct dm_bufio_client *dm_bufio_client_create(struct block_device *bdev, unsign
c->bdev = bdev;
c->block_size = block_size;
- c->sectors_per_block_bits = __ffs(block_size) - SECTOR_SHIFT;
- c->pages_per_block_bits = (__ffs(block_size) >= PAGE_SHIFT) ?
- __ffs(block_size) - PAGE_SHIFT : 0;
- c->blocks_per_page_bits = (__ffs(block_size) < PAGE_SHIFT ?
- PAGE_SHIFT - __ffs(block_size) : 0);
+ if (is_power_of_2(block_size))
+ c->sectors_per_block_bits = __ffs(block_size) - SECTOR_SHIFT;
+ else
+ c->sectors_per_block_bits = -1;
- c->aux_size = aux_size;
c->alloc_callback = alloc_callback;
c->write_callback = write_callback;
@@ -1694,7 +1668,7 @@ struct dm_bufio_client *dm_bufio_client_create(struct block_device *bdev, unsign
INIT_LIST_HEAD(&c->reserved_buffers);
c->need_reserved_buffers = reserved_buffers;
- c->minimum_buffers = DM_BUFIO_MIN_BUFFERS;
+ dm_bufio_set_minimum_buffers(c, DM_BUFIO_MIN_BUFFERS);
init_waitqueue_head(&c->free_buffer_wait);
c->async_write_error = 0;
@@ -1705,29 +1679,26 @@ struct dm_bufio_client *dm_bufio_client_create(struct block_device *bdev, unsign
goto bad_dm_io;
}
- mutex_lock(&dm_bufio_clients_lock);
- if (c->blocks_per_page_bits) {
- if (!DM_BUFIO_CACHE_NAME(c)) {
- DM_BUFIO_CACHE_NAME(c) = kasprintf(GFP_KERNEL, "dm_bufio_cache-%u", c->block_size);
- if (!DM_BUFIO_CACHE_NAME(c)) {
- r = -ENOMEM;
- mutex_unlock(&dm_bufio_clients_lock);
- goto bad;
- }
- }
-
- if (!DM_BUFIO_CACHE(c)) {
- DM_BUFIO_CACHE(c) = kmem_cache_create(DM_BUFIO_CACHE_NAME(c),
- c->block_size,
- c->block_size, 0, NULL);
- if (!DM_BUFIO_CACHE(c)) {
- r = -ENOMEM;
- mutex_unlock(&dm_bufio_clients_lock);
- goto bad;
- }
+ if (block_size <= KMALLOC_MAX_SIZE &&
+ (block_size < PAGE_SIZE || !is_power_of_2(block_size))) {
+ snprintf(slab_name, sizeof slab_name, "dm_bufio_cache-%u", c->block_size);
+ c->slab_cache = kmem_cache_create(slab_name, c->block_size, ARCH_KMALLOC_MINALIGN,
+ SLAB_RECLAIM_ACCOUNT, NULL);
+ if (!c->slab_cache) {
+ r = -ENOMEM;
+ goto bad;
}
}
- mutex_unlock(&dm_bufio_clients_lock);
+ if (aux_size)
+ snprintf(slab_name, sizeof slab_name, "dm_bufio_buffer-%u", aux_size);
+ else
+ snprintf(slab_name, sizeof slab_name, "dm_bufio_buffer");
+ c->slab_buffer = kmem_cache_create(slab_name, sizeof(struct dm_buffer) + aux_size,
+ 0, SLAB_RECLAIM_ACCOUNT, NULL);
+ if (!c->slab_buffer) {
+ r = -ENOMEM;
+ goto bad;
+ }
while (c->need_reserved_buffers) {
struct dm_buffer *b = alloc_buffer(c, GFP_KERNEL);
@@ -1762,6 +1733,8 @@ bad:
list_del(&b->lru_list);
free_buffer(b);
}
+ kmem_cache_destroy(c->slab_cache);
+ kmem_cache_destroy(c->slab_buffer);
dm_io_client_destroy(c->dm_io);
bad_dm_io:
mutex_destroy(&c->lock);
@@ -1808,6 +1781,8 @@ void dm_bufio_client_destroy(struct dm_bufio_client *c)
for (i = 0; i < LIST_SIZE; i++)
BUG_ON(c->n_buffers[i]);
+ kmem_cache_destroy(c->slab_cache);
+ kmem_cache_destroy(c->slab_buffer);
dm_io_client_destroy(c->dm_io);
mutex_destroy(&c->lock);
kfree(c);
@@ -1911,9 +1886,6 @@ static int __init dm_bufio_init(void)
dm_bufio_allocated_vmalloc = 0;
dm_bufio_current_allocated = 0;
- memset(&dm_bufio_caches, 0, sizeof dm_bufio_caches);
- memset(&dm_bufio_cache_names, 0, sizeof dm_bufio_cache_names);
-
mem = (__u64)mult_frac(totalram_pages - totalhigh_pages,
DM_BUFIO_MEMORY_PERCENT, 100) << PAGE_SHIFT;
@@ -1948,17 +1920,10 @@ static int __init dm_bufio_init(void)
static void __exit dm_bufio_exit(void)
{
int bug = 0;
- int i;
cancel_delayed_work_sync(&dm_bufio_work);
destroy_workqueue(dm_bufio_wq);
- for (i = 0; i < ARRAY_SIZE(dm_bufio_caches); i++)
- kmem_cache_destroy(dm_bufio_caches[i]);
-
- for (i = 0; i < ARRAY_SIZE(dm_bufio_cache_names); i++)
- kfree(dm_bufio_cache_names[i]);
-
if (dm_bufio_client_count) {
DMCRIT("%s: dm_bufio_client_count leaked: %d",
__func__, dm_bufio_client_count);
diff --git a/drivers/md/dm-bufio.h b/drivers/md/dm-bufio.h
deleted file mode 100644
index be732d3f8611..000000000000
--- a/drivers/md/dm-bufio.h
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2009-2011 Red Hat, Inc.
- *
- * Author: Mikulas Patocka <mpatocka@redhat.com>
- *
- * This file is released under the GPL.
- */
-
-#ifndef DM_BUFIO_H
-#define DM_BUFIO_H
-
-#include <linux/blkdev.h>
-#include <linux/types.h>
-
-/*----------------------------------------------------------------*/
-
-struct dm_bufio_client;
-struct dm_buffer;
-
-/*
- * Create a buffered IO cache on a given device
- */
-struct dm_bufio_client *
-dm_bufio_client_create(struct block_device *bdev, unsigned block_size,
- unsigned reserved_buffers, unsigned aux_size,
- void (*alloc_callback)(struct dm_buffer *),
- void (*write_callback)(struct dm_buffer *));
-
-/*
- * Release a buffered IO cache.
- */
-void dm_bufio_client_destroy(struct dm_bufio_client *c);
-
-/*
- * Set the sector range.
- * When this function is called, there must be no I/O in progress on the bufio
- * client.
- */
-void dm_bufio_set_sector_offset(struct dm_bufio_client *c, sector_t start);
-
-/*
- * WARNING: to avoid deadlocks, these conditions are observed:
- *
- * - At most one thread can hold at most "reserved_buffers" simultaneously.
- * - Each other threads can hold at most one buffer.
- * - Threads which call only dm_bufio_get can hold unlimited number of
- * buffers.
- */
-
-/*
- * Read a given block from disk. Returns pointer to data. Returns a
- * pointer to dm_buffer that can be used to release the buffer or to make
- * it dirty.
- */
-void *dm_bufio_read(struct dm_bufio_client *c, sector_t block,
- struct dm_buffer **bp);
-
-/*
- * Like dm_bufio_read, but return buffer from cache, don't read
- * it. If the buffer is not in the cache, return NULL.
- */
-void *dm_bufio_get(struct dm_bufio_client *c, sector_t block,
- struct dm_buffer **bp);
-
-/*
- * Like dm_bufio_read, but don't read anything from the disk. It is
- * expected that the caller initializes the buffer and marks it dirty.
- */
-void *dm_bufio_new(struct dm_bufio_client *c, sector_t block,
- struct dm_buffer **bp);
-
-/*
- * Prefetch the specified blocks to the cache.
- * The function starts to read the blocks and returns without waiting for
- * I/O to finish.
- */
-void dm_bufio_prefetch(struct dm_bufio_client *c,
- sector_t block, unsigned n_blocks);
-
-/*
- * Release a reference obtained with dm_bufio_{read,get,new}. The data
- * pointer and dm_buffer pointer is no longer valid after this call.
- */
-void dm_bufio_release(struct dm_buffer *b);
-
-/*
- * Mark a buffer dirty. It should be called after the buffer is modified.
- *
- * In case of memory pressure, the buffer may be written after
- * dm_bufio_mark_buffer_dirty, but before dm_bufio_write_dirty_buffers. So
- * dm_bufio_write_dirty_buffers guarantees that the buffer is on-disk but
- * the actual writing may occur earlier.
- */
-void dm_bufio_mark_buffer_dirty(struct dm_buffer *b);
-
-/*
- * Mark a part of the buffer dirty.
- *
- * The specified part of the buffer is scheduled to be written. dm-bufio may
- * write the specified part of the buffer or it may write a larger superset.
- */
-void dm_bufio_mark_partial_buffer_dirty(struct dm_buffer *b,
- unsigned start, unsigned end);
-
-/*
- * Initiate writing of dirty buffers, without waiting for completion.
- */
-void dm_bufio_write_dirty_buffers_async(struct dm_bufio_client *c);
-
-/*
- * Write all dirty buffers. Guarantees that all dirty buffers created prior
- * to this call are on disk when this call exits.
- */
-int dm_bufio_write_dirty_buffers(struct dm_bufio_client *c);
-
-/*
- * Send an empty write barrier to the device to flush hardware disk cache.
- */
-int dm_bufio_issue_flush(struct dm_bufio_client *c);
-
-/*
- * Like dm_bufio_release but also move the buffer to the new
- * block. dm_bufio_write_dirty_buffers is needed to commit the new block.
- */
-void dm_bufio_release_move(struct dm_buffer *b, sector_t new_block);
-
-/*
- * Free the given buffer.
- * This is just a hint, if the buffer is in use or dirty, this function
- * does nothing.
- */
-void dm_bufio_forget(struct dm_bufio_client *c, sector_t block);
-
-/*
- * Set the minimum number of buffers before cleanup happens.
- */
-void dm_bufio_set_minimum_buffers(struct dm_bufio_client *c, unsigned n);
-
-unsigned dm_bufio_get_block_size(struct dm_bufio_client *c);
-sector_t dm_bufio_get_device_size(struct dm_bufio_client *c);
-sector_t dm_bufio_get_block_number(struct dm_buffer *b);
-void *dm_bufio_get_block_data(struct dm_buffer *b);
-void *dm_bufio_get_aux_data(struct dm_buffer *b);
-struct dm_bufio_client *dm_bufio_get_client(struct dm_buffer *b);
-
-/*----------------------------------------------------------------*/
-
-#endif
diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c
index 47407e43b96a..da208638fba4 100644
--- a/drivers/md/dm-cache-target.c
+++ b/drivers/md/dm-cache-target.c
@@ -3387,7 +3387,8 @@ static int process_invalidate_cblocks_message(struct cache *cache, unsigned coun
*
* The key migration_threshold is supported by the cache target core.
*/
-static int cache_message(struct dm_target *ti, unsigned argc, char **argv)
+static int cache_message(struct dm_target *ti, unsigned argc, char **argv,
+ char *result, unsigned maxlen)
{
struct cache *cache = ti->private;
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index 8168f737590e..44ff473dab3e 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -148,6 +148,8 @@ struct crypt_config {
mempool_t *tag_pool;
unsigned tag_pool_max_sectors;
+ struct percpu_counter n_allocated_pages;
+
struct bio_set *bs;
struct mutex bio_alloc_lock;
@@ -219,6 +221,12 @@ struct crypt_config {
#define MAX_TAG_SIZE 480
#define POOL_ENTRY_SIZE 512
+static DEFINE_SPINLOCK(dm_crypt_clients_lock);
+static unsigned dm_crypt_clients_n = 0;
+static volatile unsigned long dm_crypt_pages_per_client;
+#define DM_CRYPT_MEMORY_PERCENT 2
+#define DM_CRYPT_MIN_PAGES_PER_CLIENT (BIO_MAX_PAGES * 16)
+
static void clone_init(struct dm_crypt_io *, struct bio *);
static void kcryptd_queue_crypt(struct dm_crypt_io *io);
static struct scatterlist *crypt_get_sg_data(struct crypt_config *cc,
@@ -2155,6 +2163,43 @@ static int crypt_wipe_key(struct crypt_config *cc)
return r;
}
+static void crypt_calculate_pages_per_client(void)
+{
+ unsigned long pages = (totalram_pages - totalhigh_pages) * DM_CRYPT_MEMORY_PERCENT / 100;
+
+ if (!dm_crypt_clients_n)
+ return;
+
+ pages /= dm_crypt_clients_n;
+ if (pages < DM_CRYPT_MIN_PAGES_PER_CLIENT)
+ pages = DM_CRYPT_MIN_PAGES_PER_CLIENT;
+ dm_crypt_pages_per_client = pages;
+}
+
+static void *crypt_page_alloc(gfp_t gfp_mask, void *pool_data)
+{
+ struct crypt_config *cc = pool_data;
+ struct page *page;
+
+ if (unlikely(percpu_counter_compare(&cc->n_allocated_pages, dm_crypt_pages_per_client) >= 0) &&
+ likely(gfp_mask & __GFP_NORETRY))
+ return NULL;
+
+ page = alloc_page(gfp_mask);
+ if (likely(page != NULL))
+ percpu_counter_add(&cc->n_allocated_pages, 1);
+
+ return page;
+}
+
+static void crypt_page_free(void *page, void *pool_data)
+{
+ struct crypt_config *cc = pool_data;
+
+ __free_page(page);
+ percpu_counter_sub(&cc->n_allocated_pages, 1);
+}
+
static void crypt_dtr(struct dm_target *ti)
{
struct crypt_config *cc = ti->private;
@@ -2181,6 +2226,10 @@ static void crypt_dtr(struct dm_target *ti)
mempool_destroy(cc->req_pool);
mempool_destroy(cc->tag_pool);
+ if (cc->page_pool)
+ WARN_ON(percpu_counter_sum(&cc->n_allocated_pages) != 0);
+ percpu_counter_destroy(&cc->n_allocated_pages);
+
if (cc->iv_gen_ops && cc->iv_gen_ops->dtr)
cc->iv_gen_ops->dtr(cc);
@@ -2197,6 +2246,12 @@ static void crypt_dtr(struct dm_target *ti)
/* Must zero key material before freeing */
kzfree(cc);
+
+ spin_lock(&dm_crypt_clients_lock);
+ WARN_ON(!dm_crypt_clients_n);
+ dm_crypt_clients_n--;
+ crypt_calculate_pages_per_client();
+ spin_unlock(&dm_crypt_clients_lock);
}
static int crypt_ctr_ivmode(struct dm_target *ti, const char *ivmode)
@@ -2644,6 +2699,15 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
ti->private = cc;
+ spin_lock(&dm_crypt_clients_lock);
+ dm_crypt_clients_n++;
+ crypt_calculate_pages_per_client();
+ spin_unlock(&dm_crypt_clients_lock);
+
+ ret = percpu_counter_init(&cc->n_allocated_pages, 0, GFP_KERNEL);
+ if (ret < 0)
+ goto bad;
+
/* Optional parameters need to be read before cipher constructor */
if (argc > 5) {
ret = crypt_ctr_optional(ti, argc - 5, &argv[5]);
@@ -2698,7 +2762,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
ALIGN(sizeof(struct dm_crypt_io) + cc->dmreq_start + additional_req_size,
ARCH_KMALLOC_MINALIGN);
- cc->page_pool = mempool_create_page_pool(BIO_MAX_PAGES, 0);
+ cc->page_pool = mempool_create(BIO_MAX_PAGES, crypt_page_alloc, crypt_page_free, cc);
if (!cc->page_pool) {
ti->error = "Cannot allocate page mempool";
goto bad;
@@ -2942,7 +3006,8 @@ static void crypt_resume(struct dm_target *ti)
* key set <key>
* key wipe
*/
-static int crypt_message(struct dm_target *ti, unsigned argc, char **argv)
+static int crypt_message(struct dm_target *ti, unsigned argc, char **argv,
+ char *result, unsigned maxlen)
{
struct crypt_config *cc = ti->private;
int key_size, ret = -EINVAL;
diff --git a/drivers/md/dm-era-target.c b/drivers/md/dm-era-target.c
index 73a5c198113a..8e48920a3ffa 100644
--- a/drivers/md/dm-era-target.c
+++ b/drivers/md/dm-era-target.c
@@ -1635,7 +1635,8 @@ err:
DMEMIT("Error");
}
-static int era_message(struct dm_target *ti, unsigned argc, char **argv)
+static int era_message(struct dm_target *ti, unsigned argc, char **argv,
+ char *result, unsigned maxlen)
{
struct era *era = ti->private;
diff --git a/drivers/md/dm-flakey.c b/drivers/md/dm-flakey.c
index 1b907b15f5c3..21d126a5078c 100644
--- a/drivers/md/dm-flakey.c
+++ b/drivers/md/dm-flakey.c
@@ -442,8 +442,7 @@ static void flakey_status(struct dm_target *ti, status_type_t type,
}
}
-static int flakey_prepare_ioctl(struct dm_target *ti,
- struct block_device **bdev, fmode_t *mode)
+static int flakey_prepare_ioctl(struct dm_target *ti, struct block_device **bdev)
{
struct flakey_c *fc = ti->private;
diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c
index 46d7c8749222..77d9fe58dae2 100644
--- a/drivers/md/dm-integrity.c
+++ b/drivers/md/dm-integrity.c
@@ -18,7 +18,7 @@
#include <crypto/hash.h>
#include <crypto/skcipher.h>
#include <linux/async_tx.h>
-#include "dm-bufio.h"
+#include <linux/dm-bufio.h>
#define DM_MSG_PREFIX "integrity"
@@ -2548,6 +2548,9 @@ static int get_mac(struct crypto_shash **hash, struct alg_spec *a, char **error,
*error = error_key;
return r;
}
+ } else if (crypto_shash_get_flags(*hash) & CRYPTO_TFM_NEED_KEY) {
+ *error = error_key;
+ return -ENOKEY;
}
}
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index a89fd8f44453..5acf77de5945 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -1595,7 +1595,7 @@ static int target_message(struct file *filp, struct dm_ioctl *param, size_t para
DMWARN("Target message sector outside device.");
r = -EINVAL;
} else if (ti->type->message)
- r = ti->type->message(ti, argc, argv);
+ r = ti->type->message(ti, argc, argv, result, maxlen);
else {
DMWARN("Target type does not support messages");
r = -EINVAL;
diff --git a/drivers/md/dm-linear.c b/drivers/md/dm-linear.c
index d5f8eff7c11d..775c06d953b7 100644
--- a/drivers/md/dm-linear.c
+++ b/drivers/md/dm-linear.c
@@ -59,6 +59,7 @@ static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv)
ti->num_flush_bios = 1;
ti->num_discard_bios = 1;
+ ti->num_secure_erase_bios = 1;
ti->num_write_same_bios = 1;
ti->num_write_zeroes_bios = 1;
ti->private = lc;
@@ -129,8 +130,7 @@ static void linear_status(struct dm_target *ti, status_type_t type,
}
}
-static int linear_prepare_ioctl(struct dm_target *ti,
- struct block_device **bdev, fmode_t *mode)
+static int linear_prepare_ioctl(struct dm_target *ti, struct block_device **bdev)
{
struct linear_c *lc = (struct linear_c *) ti->private;
struct dm_dev *dev = lc->dev;
@@ -154,6 +154,7 @@ static int linear_iterate_devices(struct dm_target *ti,
return fn(ti, lc->dev, lc->start, ti->len, data);
}
+#if IS_ENABLED(CONFIG_DAX_DRIVER)
static long linear_dax_direct_access(struct dm_target *ti, pgoff_t pgoff,
long nr_pages, void **kaddr, pfn_t *pfn)
{
@@ -184,6 +185,11 @@ static size_t linear_dax_copy_from_iter(struct dm_target *ti, pgoff_t pgoff,
return dax_copy_from_iter(dax_dev, pgoff, addr, bytes, i);
}
+#else
+#define linear_dax_direct_access NULL
+#define linear_dax_copy_from_iter NULL
+#endif
+
static struct target_type linear_target = {
.name = "linear",
.version = {1, 4, 0},
diff --git a/drivers/md/dm-log-writes.c b/drivers/md/dm-log-writes.c
index 3362d866793b..c90c7c08a77f 100644
--- a/drivers/md/dm-log-writes.c
+++ b/drivers/md/dm-log-writes.c
@@ -52,10 +52,11 @@
* in fact we want to do the data and the discard in the order that they
* completed.
*/
-#define LOG_FLUSH_FLAG (1 << 0)
-#define LOG_FUA_FLAG (1 << 1)
-#define LOG_DISCARD_FLAG (1 << 2)
-#define LOG_MARK_FLAG (1 << 3)
+#define LOG_FLUSH_FLAG (1 << 0)
+#define LOG_FUA_FLAG (1 << 1)
+#define LOG_DISCARD_FLAG (1 << 2)
+#define LOG_MARK_FLAG (1 << 3)
+#define LOG_METADATA_FLAG (1 << 4)
#define WRITE_LOG_VERSION 1ULL
#define WRITE_LOG_MAGIC 0x6a736677736872ULL
@@ -610,51 +611,6 @@ static int log_mark(struct log_writes_c *lc, char *data)
return 0;
}
-static int log_dax(struct log_writes_c *lc, sector_t sector, size_t bytes,
- struct iov_iter *i)
-{
- struct pending_block *block;
-
- if (!bytes)
- return 0;
-
- block = kzalloc(sizeof(struct pending_block), GFP_KERNEL);
- if (!block) {
- DMERR("Error allocating dax pending block");
- return -ENOMEM;
- }
-
- block->data = kzalloc(bytes, GFP_KERNEL);
- if (!block->data) {
- DMERR("Error allocating dax data space");
- kfree(block);
- return -ENOMEM;
- }
-
- /* write data provided via the iterator */
- if (!copy_from_iter(block->data, bytes, i)) {
- DMERR("Error copying dax data");
- kfree(block->data);
- kfree(block);
- return -EIO;
- }
-
- /* rewind the iterator so that the block driver can use it */
- iov_iter_revert(i, bytes);
-
- block->datalen = bytes;
- block->sector = bio_to_dev_sectors(lc, sector);
- block->nr_sectors = ALIGN(bytes, lc->sectorsize) >> lc->sectorshift;
-
- atomic_inc(&lc->pending_blocks);
- spin_lock_irq(&lc->blocks_lock);
- list_add_tail(&block->list, &lc->unflushed_blocks);
- spin_unlock_irq(&lc->blocks_lock);
- wake_up_process(lc->log_kthread);
-
- return 0;
-}
-
static void log_writes_dtr(struct dm_target *ti)
{
struct log_writes_c *lc = ti->private;
@@ -699,6 +655,7 @@ static int log_writes_map(struct dm_target *ti, struct bio *bio)
bool flush_bio = (bio->bi_opf & REQ_PREFLUSH);
bool fua_bio = (bio->bi_opf & REQ_FUA);
bool discard_bio = (bio_op(bio) == REQ_OP_DISCARD);
+ bool meta_bio = (bio->bi_opf & REQ_META);
pb->block = NULL;
@@ -743,6 +700,8 @@ static int log_writes_map(struct dm_target *ti, struct bio *bio)
block->flags |= LOG_FUA_FLAG;
if (discard_bio)
block->flags |= LOG_DISCARD_FLAG;
+ if (meta_bio)
+ block->flags |= LOG_METADATA_FLAG;
block->sector = bio_to_dev_sectors(lc, bio->bi_iter.bi_sector);
block->nr_sectors = bio_to_dev_sectors(lc, bio_sectors(bio));
@@ -860,7 +819,7 @@ static void log_writes_status(struct dm_target *ti, status_type_t type,
}
static int log_writes_prepare_ioctl(struct dm_target *ti,
- struct block_device **bdev, fmode_t *mode)
+ struct block_device **bdev)
{
struct log_writes_c *lc = ti->private;
struct dm_dev *dev = lc->dev;
@@ -887,7 +846,8 @@ static int log_writes_iterate_devices(struct dm_target *ti,
* Messages supported:
* mark <mark data> - specify the marked data.
*/
-static int log_writes_message(struct dm_target *ti, unsigned argc, char **argv)
+static int log_writes_message(struct dm_target *ti, unsigned argc, char **argv,
+ char *result, unsigned maxlen)
{
int r = -EINVAL;
struct log_writes_c *lc = ti->private;
@@ -920,6 +880,52 @@ static void log_writes_io_hints(struct dm_target *ti, struct queue_limits *limit
limits->io_min = limits->physical_block_size;
}
+#if IS_ENABLED(CONFIG_DAX_DRIVER)
+static int log_dax(struct log_writes_c *lc, sector_t sector, size_t bytes,
+ struct iov_iter *i)
+{
+ struct pending_block *block;
+
+ if (!bytes)
+ return 0;
+
+ block = kzalloc(sizeof(struct pending_block), GFP_KERNEL);
+ if (!block) {
+ DMERR("Error allocating dax pending block");
+ return -ENOMEM;
+ }
+
+ block->data = kzalloc(bytes, GFP_KERNEL);
+ if (!block->data) {
+ DMERR("Error allocating dax data space");
+ kfree(block);
+ return -ENOMEM;
+ }
+
+ /* write data provided via the iterator */
+ if (!copy_from_iter(block->data, bytes, i)) {
+ DMERR("Error copying dax data");
+ kfree(block->data);
+ kfree(block);
+ return -EIO;
+ }
+
+ /* rewind the iterator so that the block driver can use it */
+ iov_iter_revert(i, bytes);
+
+ block->datalen = bytes;
+ block->sector = bio_to_dev_sectors(lc, sector);
+ block->nr_sectors = ALIGN(bytes, lc->sectorsize) >> lc->sectorshift;
+
+ atomic_inc(&lc->pending_blocks);
+ spin_lock_irq(&lc->blocks_lock);
+ list_add_tail(&block->list, &lc->unflushed_blocks);
+ spin_unlock_irq(&lc->blocks_lock);
+ wake_up_process(lc->log_kthread);
+
+ return 0;
+}
+
static long log_writes_dax_direct_access(struct dm_target *ti, pgoff_t pgoff,
long nr_pages, void **kaddr, pfn_t *pfn)
{
@@ -956,6 +962,10 @@ static size_t log_writes_dax_copy_from_iter(struct dm_target *ti,
dax_copy:
return dax_copy_from_iter(lc->dev->dax_dev, pgoff, addr, bytes, i);
}
+#else
+#define log_writes_dax_direct_access NULL
+#define log_writes_dax_copy_from_iter NULL
+#endif
static struct target_type log_writes_target = {
.name = "log-writes",
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index a05a560d3cba..203a0419d2b0 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -714,7 +714,7 @@ static void process_queued_bios(struct work_struct *work)
case DM_MAPIO_REMAPPED:
generic_make_request(bio);
break;
- case 0:
+ case DM_MAPIO_SUBMITTED:
break;
default:
WARN_ONCE(true, "__multipath_map_bio() returned %d\n", r);
@@ -887,7 +887,7 @@ static struct pgpath *parse_path(struct dm_arg_set *as, struct path_selector *ps
q = bdev_get_queue(p->path.dev->bdev);
attached_handler_name = scsi_dh_attached_handler_name(q, GFP_KERNEL);
- if (attached_handler_name) {
+ if (attached_handler_name || m->hw_handler_name) {
INIT_DELAYED_WORK(&p->activate_path, activate_path_work);
r = setup_scsi_dh(p->path.dev->bdev, m, attached_handler_name, &ti->error);
if (r) {
@@ -1811,7 +1811,8 @@ static void multipath_status(struct dm_target *ti, status_type_t type,
spin_unlock_irqrestore(&m->lock, flags);
}
-static int multipath_message(struct dm_target *ti, unsigned argc, char **argv)
+static int multipath_message(struct dm_target *ti, unsigned argc, char **argv,
+ char *result, unsigned maxlen)
{
int r = -EINVAL;
struct dm_dev *dev;
@@ -1875,7 +1876,7 @@ out:
}
static int multipath_prepare_ioctl(struct dm_target *ti,
- struct block_device **bdev, fmode_t *mode)
+ struct block_device **bdev)
{
struct multipath *m = ti->private;
struct pgpath *current_pgpath;
@@ -1888,7 +1889,6 @@ static int multipath_prepare_ioctl(struct dm_target *ti,
if (current_pgpath) {
if (!test_bit(MPATHF_QUEUE_IO, &m->flags)) {
*bdev = current_pgpath->path.dev->bdev;
- *mode = current_pgpath->path.dev->mode;
r = 0;
} else {
/* pg_init has not started or completed */
diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c
index c1d1034ff7b7..6f823f44b4aa 100644
--- a/drivers/md/dm-raid.c
+++ b/drivers/md/dm-raid.c
@@ -1370,19 +1370,18 @@ static int parse_raid_params(struct raid_set *rs, struct dm_arg_set *as,
* In device-mapper, we specify things in sectors, but
* MD records this value in kB
*/
- value /= 2;
- if (value > COUNTER_MAX) {
+ if (value < 0 || value / 2 > COUNTER_MAX) {
rs->ti->error = "Max write-behind limit out of range";
return -EINVAL;
}
- rs->md.bitmap_info.max_write_behind = value;
+ rs->md.bitmap_info.max_write_behind = value / 2;
} else if (!strcasecmp(key, dm_raid_arg_name_by_flag(CTR_FLAG_DAEMON_SLEEP))) {
if (test_and_set_bit(__CTR_FLAG_DAEMON_SLEEP, &rs->ctr_flags)) {
rs->ti->error = "Only one daemon_sleep argument pair allowed";
return -EINVAL;
}
- if (!value || (value > MAX_SCHEDULE_TIMEOUT)) {
+ if (value < 0) {
rs->ti->error = "daemon sleep period out of range";
return -EINVAL;
}
@@ -1424,27 +1423,33 @@ static int parse_raid_params(struct raid_set *rs, struct dm_arg_set *as,
return -EINVAL;
}
+ if (value < 0) {
+ rs->ti->error = "Bogus stripe cache entries value";
+ return -EINVAL;
+ }
rs->stripe_cache_entries = value;
} else if (!strcasecmp(key, dm_raid_arg_name_by_flag(CTR_FLAG_MIN_RECOVERY_RATE))) {
if (test_and_set_bit(__CTR_FLAG_MIN_RECOVERY_RATE, &rs->ctr_flags)) {
rs->ti->error = "Only one min_recovery_rate argument pair allowed";
return -EINVAL;
}
- if (value > INT_MAX) {
+
+ if (value < 0) {
rs->ti->error = "min_recovery_rate out of range";
return -EINVAL;
}
- rs->md.sync_speed_min = (int)value;
+ rs->md.sync_speed_min = value;
} else if (!strcasecmp(key, dm_raid_arg_name_by_flag(CTR_FLAG_MAX_RECOVERY_RATE))) {
if (test_and_set_bit(__CTR_FLAG_MAX_RECOVERY_RATE, &rs->ctr_flags)) {
rs->ti->error = "Only one max_recovery_rate argument pair allowed";
return -EINVAL;
}
- if (value > INT_MAX) {
+
+ if (value < 0) {
rs->ti->error = "max_recovery_rate out of range";
return -EINVAL;
}
- rs->md.sync_speed_max = (int)value;
+ rs->md.sync_speed_max = value;
} else if (!strcasecmp(key, dm_raid_arg_name_by_flag(CTR_FLAG_REGION_SIZE))) {
if (test_and_set_bit(__CTR_FLAG_REGION_SIZE, &rs->ctr_flags)) {
rs->ti->error = "Only one region_size argument pair allowed";
@@ -1490,6 +1495,12 @@ static int parse_raid_params(struct raid_set *rs, struct dm_arg_set *as,
return -EINVAL;
}
+ if (rs->md.sync_speed_max &&
+ rs->md.sync_speed_min > rs->md.sync_speed_max) {
+ rs->ti->error = "Bogus recovery rates";
+ return -EINVAL;
+ }
+
if (validate_region_size(rs, region_size))
return -EINVAL;
@@ -3408,7 +3419,8 @@ static sector_t rs_get_progress(struct raid_set *rs, unsigned long recovery,
set_bit(RT_FLAG_RS_IN_SYNC, &rs->runtime_flags);
} else {
- if (!test_bit(MD_RECOVERY_INTR, &recovery) &&
+ if (!test_bit(__CTR_FLAG_NOSYNC, &rs->ctr_flags) &&
+ !test_bit(MD_RECOVERY_INTR, &recovery) &&
(test_bit(MD_RECOVERY_NEEDED, &recovery) ||
test_bit(MD_RECOVERY_RESHAPE, &recovery) ||
test_bit(MD_RECOVERY_RUNNING, &recovery)))
@@ -3663,7 +3675,8 @@ static void raid_status(struct dm_target *ti, status_type_t type,
}
}
-static int raid_message(struct dm_target *ti, unsigned int argc, char **argv)
+static int raid_message(struct dm_target *ti, unsigned int argc, char **argv,
+ char *result, unsigned maxlen)
{
struct raid_set *rs = ti->private;
struct mddev *mddev = &rs->md;
diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c
index c5534d294773..3c50c4e4da8f 100644
--- a/drivers/md/dm-snap-persistent.c
+++ b/drivers/md/dm-snap-persistent.c
@@ -14,7 +14,7 @@
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/dm-io.h>
-#include "dm-bufio.h"
+#include <linux/dm-bufio.h>
#define DM_MSG_PREFIX "persistent snapshot"
#define DM_CHUNK_SIZE_DEFAULT_SECTORS 32 /* 16KB */
diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c
index b5e892149c54..fe7fb9b1aec3 100644
--- a/drivers/md/dm-stripe.c
+++ b/drivers/md/dm-stripe.c
@@ -169,6 +169,7 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
ti->num_flush_bios = stripes;
ti->num_discard_bios = stripes;
+ ti->num_secure_erase_bios = stripes;
ti->num_write_same_bios = stripes;
ti->num_write_zeroes_bios = stripes;
@@ -295,6 +296,7 @@ static int stripe_map(struct dm_target *ti, struct bio *bio)
return DM_MAPIO_REMAPPED;
}
if (unlikely(bio_op(bio) == REQ_OP_DISCARD) ||
+ unlikely(bio_op(bio) == REQ_OP_SECURE_ERASE) ||
unlikely(bio_op(bio) == REQ_OP_WRITE_ZEROES) ||
unlikely(bio_op(bio) == REQ_OP_WRITE_SAME)) {
target_bio_nr = dm_bio_get_target_bio_nr(bio);
@@ -311,6 +313,7 @@ static int stripe_map(struct dm_target *ti, struct bio *bio)
return DM_MAPIO_REMAPPED;
}
+#if IS_ENABLED(CONFIG_DAX_DRIVER)
static long stripe_dax_direct_access(struct dm_target *ti, pgoff_t pgoff,
long nr_pages, void **kaddr, pfn_t *pfn)
{
@@ -351,6 +354,11 @@ static size_t stripe_dax_copy_from_iter(struct dm_target *ti, pgoff_t pgoff,
return dax_copy_from_iter(dax_dev, pgoff, addr, bytes, i);
}
+#else
+#define stripe_dax_direct_access NULL
+#define stripe_dax_copy_from_iter NULL
+#endif
+
/*
* Stripe status:
*
@@ -368,7 +376,6 @@ static void stripe_status(struct dm_target *ti, status_type_t type,
unsigned status_flags, char *result, unsigned maxlen)
{
struct stripe_c *sc = (struct stripe_c *) ti->private;
- char buffer[sc->stripes + 1];
unsigned int sz = 0;
unsigned int i;
@@ -377,11 +384,12 @@ static void stripe_status(struct dm_target *ti, status_type_t type,
DMEMIT("%d ", sc->stripes);
for (i = 0; i < sc->stripes; i++) {
DMEMIT("%s ", sc->stripe[i].dev->name);
- buffer[i] = atomic_read(&(sc->stripe[i].error_count)) ?
- 'D' : 'A';
}
- buffer[i] = '\0';
- DMEMIT("1 %s", buffer);
+ DMEMIT("1 ");
+ for (i = 0; i < sc->stripes; i++) {
+ DMEMIT("%c", atomic_read(&(sc->stripe[i].error_count)) ?
+ 'D' : 'A');
+ }
break;
case STATUSTYPE_TABLE:
diff --git a/drivers/md/dm-switch.c b/drivers/md/dm-switch.c
index 8d0ba879777e..7924a6a33ddc 100644
--- a/drivers/md/dm-switch.c
+++ b/drivers/md/dm-switch.c
@@ -466,7 +466,8 @@ static int process_set_region_mappings(struct switch_ctx *sctx,
*
* Only set_region_mappings is supported.
*/
-static int switch_message(struct dm_target *ti, unsigned argc, char **argv)
+static int switch_message(struct dm_target *ti, unsigned argc, char **argv,
+ char *result, unsigned maxlen)
{
static DEFINE_MUTEX(message_mutex);
@@ -511,8 +512,7 @@ static void switch_status(struct dm_target *ti, status_type_t type,
*
* Passthrough all ioctls to the path for sector 0
*/
-static int switch_prepare_ioctl(struct dm_target *ti,
- struct block_device **bdev, fmode_t *mode)
+static int switch_prepare_ioctl(struct dm_target *ti, struct block_device **bdev)
{
struct switch_ctx *sctx = ti->private;
unsigned path_nr;
@@ -520,7 +520,6 @@ static int switch_prepare_ioctl(struct dm_target *ti,
path_nr = switch_get_path_nr(sctx, 0);
*bdev = sctx->path_list[path_nr].dmdev->bdev;
- *mode = sctx->path_list[path_nr].dmdev->mode;
/*
* Only pass ioctls through if the device sizes match exactly.
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index 7eb3e2a3c07d..0589a4da12bb 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -1846,6 +1846,34 @@ static bool dm_table_supports_discards(struct dm_table *t)
return true;
}
+static int device_not_secure_erase_capable(struct dm_target *ti,
+ struct dm_dev *dev, sector_t start,
+ sector_t len, void *data)
+{
+ struct request_queue *q = bdev_get_queue(dev->bdev);
+
+ return q && !blk_queue_secure_erase(q);
+}
+
+static bool dm_table_supports_secure_erase(struct dm_table *t)
+{
+ struct dm_target *ti;
+ unsigned int i;
+
+ for (i = 0; i < dm_table_get_num_targets(t); i++) {
+ ti = dm_table_get_target(t, i);
+
+ if (!ti->num_secure_erase_bios)
+ return false;
+
+ if (!ti->type->iterate_devices ||
+ ti->type->iterate_devices(ti, device_not_secure_erase_capable, NULL))
+ return false;
+ }
+
+ return true;
+}
+
void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
struct queue_limits *limits)
{
@@ -1857,7 +1885,7 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
q->limits = *limits;
if (!dm_table_supports_discards(t)) {
- queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, q);
+ blk_queue_flag_clear(QUEUE_FLAG_DISCARD, q);
/* Must also clear discard limits... */
q->limits.max_discard_sectors = 0;
q->limits.max_hw_discard_sectors = 0;
@@ -1865,7 +1893,10 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
q->limits.discard_alignment = 0;
q->limits.discard_misaligned = 0;
} else
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q);
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, q);
+
+ if (dm_table_supports_secure_erase(t))
+ blk_queue_flag_set(QUEUE_FLAG_SECERASE, q);
if (dm_table_supports_flush(t, (1UL << QUEUE_FLAG_WC))) {
wc = true;
@@ -1875,15 +1906,15 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
blk_queue_write_cache(q, wc, fua);
if (dm_table_supports_dax(t))
- queue_flag_set_unlocked(QUEUE_FLAG_DAX, q);
+ blk_queue_flag_set(QUEUE_FLAG_DAX, q);
if (dm_table_supports_dax_write_cache(t))
dax_write_cache(t->md->dax_dev, true);
/* Ensure that all underlying devices are non-rotational. */
if (dm_table_all_devices_attribute(t, device_is_nonrot))
- queue_flag_set_unlocked(QUEUE_FLAG_NONROT, q);
+ blk_queue_flag_set(QUEUE_FLAG_NONROT, q);
else
- queue_flag_clear_unlocked(QUEUE_FLAG_NONROT, q);
+ blk_queue_flag_clear(QUEUE_FLAG_NONROT, q);
if (!dm_table_supports_write_same(t))
q->limits.max_write_same_sectors = 0;
@@ -1891,9 +1922,9 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
q->limits.max_write_zeroes_sectors = 0;
if (dm_table_all_devices_attribute(t, queue_supports_sg_merge))
- queue_flag_clear_unlocked(QUEUE_FLAG_NO_SG_MERGE, q);
+ blk_queue_flag_clear(QUEUE_FLAG_NO_SG_MERGE, q);
else
- queue_flag_set_unlocked(QUEUE_FLAG_NO_SG_MERGE, q);
+ blk_queue_flag_set(QUEUE_FLAG_NO_SG_MERGE, q);
dm_table_verify_integrity(t);
@@ -1904,7 +1935,7 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
* have it set.
*/
if (blk_queue_add_random(q) && dm_table_all_devices_attribute(t, device_is_not_random))
- queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, q);
+ blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, q);
}
unsigned int dm_table_get_num_targets(struct dm_table *t)
diff --git a/drivers/md/dm-target.c b/drivers/md/dm-target.c
index c0d7e60820c4..314d17ca6466 100644
--- a/drivers/md/dm-target.c
+++ b/drivers/md/dm-target.c
@@ -16,8 +16,6 @@
static LIST_HEAD(_targets);
static DECLARE_RWSEM(_lock);
-#define DM_MOD_NAME_SIZE 32
-
static inline struct target_type *__find_target_type(const char *name)
{
struct target_type *tt;
diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c
index 629c555890c1..b11107497d2e 100644
--- a/drivers/md/dm-thin.c
+++ b/drivers/md/dm-thin.c
@@ -3705,7 +3705,8 @@ static int process_release_metadata_snap_mesg(unsigned argc, char **argv, struct
* reserve_metadata_snap
* release_metadata_snap
*/
-static int pool_message(struct dm_target *ti, unsigned argc, char **argv)
+static int pool_message(struct dm_target *ti, unsigned argc, char **argv,
+ char *result, unsigned maxlen)
{
int r = -EINVAL;
struct pool_c *pt = ti->private;
diff --git a/drivers/md/dm-unstripe.c b/drivers/md/dm-unstripe.c
index 65f838fa2e99..954b7ab4e684 100644
--- a/drivers/md/dm-unstripe.c
+++ b/drivers/md/dm-unstripe.c
@@ -7,12 +7,6 @@
#include "dm.h"
#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/blkdev.h>
-#include <linux/bio.h>
-#include <linux/slab.h>
-#include <linux/bitops.h>
-#include <linux/device-mapper.h>
struct unstripe_c {
struct dm_dev *dev;
@@ -69,12 +63,6 @@ static int unstripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
goto err;
}
- // FIXME: must support non power of 2 chunk_size, dm-stripe.c does
- if (!is_power_of_2(uc->chunk_size)) {
- ti->error = "Non power of 2 chunk_size is not supported yet";
- goto err;
- }
-
if (kstrtouint(argv[2], 10, &uc->unstripe)) {
ti->error = "Invalid stripe number";
goto err;
@@ -98,7 +86,7 @@ static int unstripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
uc->unstripe_offset = uc->unstripe * uc->chunk_size;
uc->unstripe_width = (uc->stripes - 1) * uc->chunk_size;
- uc->chunk_shift = fls(uc->chunk_size) - 1;
+ uc->chunk_shift = is_power_of_2(uc->chunk_size) ? fls(uc->chunk_size) - 1 : 0;
tmp_len = ti->len;
if (sector_div(tmp_len, uc->chunk_size)) {
@@ -129,14 +117,18 @@ static sector_t map_to_core(struct dm_target *ti, struct bio *bio)
{
struct unstripe_c *uc = ti->private;
sector_t sector = bio->bi_iter.bi_sector;
+ sector_t tmp_sector = sector;
/* Shift us up to the right "row" on the stripe */
- sector += uc->unstripe_width * (sector >> uc->chunk_shift);
+ if (uc->chunk_shift)
+ tmp_sector >>= uc->chunk_shift;
+ else
+ sector_div(tmp_sector, uc->chunk_size);
- /* Account for what stripe we're operating on */
- sector += uc->unstripe_offset;
+ sector += uc->unstripe_width * tmp_sector;
- return sector;
+ /* Account for what stripe we're operating on */
+ return sector + uc->unstripe_offset;
}
static int unstripe_map(struct dm_target *ti, struct bio *bio)
@@ -185,7 +177,7 @@ static void unstripe_io_hints(struct dm_target *ti,
static struct target_type unstripe_target = {
.name = "unstriped",
- .version = {1, 0, 0},
+ .version = {1, 1, 0},
.module = THIS_MODULE,
.ctr = unstripe_ctr,
.dtr = unstripe_dtr,
@@ -197,13 +189,7 @@ static struct target_type unstripe_target = {
static int __init dm_unstripe_init(void)
{
- int r;
-
- r = dm_register_target(&unstripe_target);
- if (r < 0)
- DMERR("target registration failed");
-
- return r;
+ return dm_register_target(&unstripe_target);
}
static void __exit dm_unstripe_exit(void)
@@ -215,5 +201,6 @@ module_init(dm_unstripe_init);
module_exit(dm_unstripe_exit);
MODULE_DESCRIPTION(DM_NAME " unstriped target");
+MODULE_ALIAS("dm-unstriped");
MODULE_AUTHOR("Scott Bauer <scott.bauer@intel.com>");
MODULE_LICENSE("GPL");
diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c
index aedb8222836b..fc893f636a98 100644
--- a/drivers/md/dm-verity-target.c
+++ b/drivers/md/dm-verity-target.c
@@ -32,6 +32,7 @@
#define DM_VERITY_OPT_LOGGING "ignore_corruption"
#define DM_VERITY_OPT_RESTART "restart_on_corruption"
#define DM_VERITY_OPT_IGN_ZEROES "ignore_zero_blocks"
+#define DM_VERITY_OPT_AT_MOST_ONCE "check_at_most_once"
#define DM_VERITY_OPTS_MAX (2 + DM_VERITY_OPTS_FEC)
@@ -347,8 +348,8 @@ out:
/*
* Calculates the digest for the given bio
*/
-int verity_for_io_block(struct dm_verity *v, struct dm_verity_io *io,
- struct bvec_iter *iter, struct crypto_wait *wait)
+static int verity_for_io_block(struct dm_verity *v, struct dm_verity_io *io,
+ struct bvec_iter *iter, struct crypto_wait *wait)
{
unsigned int todo = 1 << v->data_dev_block_bits;
struct bio *bio = dm_bio_from_per_bio_data(io, v->ti->per_io_data_size);
@@ -433,6 +434,18 @@ static int verity_bv_zero(struct dm_verity *v, struct dm_verity_io *io,
}
/*
+ * Moves the bio iter one data block forward.
+ */
+static inline void verity_bv_skip_block(struct dm_verity *v,
+ struct dm_verity_io *io,
+ struct bvec_iter *iter)
+{
+ struct bio *bio = dm_bio_from_per_bio_data(io, v->ti->per_io_data_size);
+
+ bio_advance_iter(bio, iter, 1 << v->data_dev_block_bits);
+}
+
+/*
* Verify one "dm_verity_io" structure.
*/
static int verity_verify_io(struct dm_verity_io *io)
@@ -445,9 +458,16 @@ static int verity_verify_io(struct dm_verity_io *io)
for (b = 0; b < io->n_blocks; b++) {
int r;
+ sector_t cur_block = io->block + b;
struct ahash_request *req = verity_io_hash_req(v, io);
- r = verity_hash_for_block(v, io, io->block + b,
+ if (v->validated_blocks &&
+ likely(test_bit(cur_block, v->validated_blocks))) {
+ verity_bv_skip_block(v, io, &io->iter);
+ continue;
+ }
+
+ r = verity_hash_for_block(v, io, cur_block,
verity_io_want_digest(v, io),
&is_zero);
if (unlikely(r < 0))
@@ -481,13 +501,16 @@ static int verity_verify_io(struct dm_verity_io *io)
return r;
if (likely(memcmp(verity_io_real_digest(v, io),
- verity_io_want_digest(v, io), v->digest_size) == 0))
+ verity_io_want_digest(v, io), v->digest_size) == 0)) {
+ if (v->validated_blocks)
+ set_bit(cur_block, v->validated_blocks);
continue;
+ }
else if (verity_fec_decode(v, io, DM_VERITY_BLOCK_TYPE_DATA,
- io->block + b, NULL, &start) == 0)
+ cur_block, NULL, &start) == 0)
continue;
else if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_DATA,
- io->block + b))
+ cur_block))
return -EIO;
}
@@ -673,6 +696,8 @@ static void verity_status(struct dm_target *ti, status_type_t type,
args += DM_VERITY_OPTS_FEC;
if (v->zero_digest)
args++;
+ if (v->validated_blocks)
+ args++;
if (!args)
return;
DMEMIT(" %u", args);
@@ -691,13 +716,14 @@ static void verity_status(struct dm_target *ti, status_type_t type,
}
if (v->zero_digest)
DMEMIT(" " DM_VERITY_OPT_IGN_ZEROES);
+ if (v->validated_blocks)
+ DMEMIT(" " DM_VERITY_OPT_AT_MOST_ONCE);
sz = verity_fec_status_table(v, sz, result, maxlen);
break;
}
}
-static int verity_prepare_ioctl(struct dm_target *ti,
- struct block_device **bdev, fmode_t *mode)
+static int verity_prepare_ioctl(struct dm_target *ti, struct block_device **bdev)
{
struct dm_verity *v = ti->private;
@@ -740,6 +766,7 @@ static void verity_dtr(struct dm_target *ti)
if (v->bufio)
dm_bufio_client_destroy(v->bufio);
+ kvfree(v->validated_blocks);
kfree(v->salt);
kfree(v->root_digest);
kfree(v->zero_digest);
@@ -760,6 +787,26 @@ static void verity_dtr(struct dm_target *ti)
kfree(v);
}
+static int verity_alloc_most_once(struct dm_verity *v)
+{
+ struct dm_target *ti = v->ti;
+
+ /* the bitset can only handle INT_MAX blocks */
+ if (v->data_blocks > INT_MAX) {
+ ti->error = "device too large to use check_at_most_once";
+ return -E2BIG;
+ }
+
+ v->validated_blocks = kvzalloc(BITS_TO_LONGS(v->data_blocks) *
+ sizeof(unsigned long), GFP_KERNEL);
+ if (!v->validated_blocks) {
+ ti->error = "failed to allocate bitset for check_at_most_once";
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
static int verity_alloc_zero_digest(struct dm_verity *v)
{
int r = -ENOMEM;
@@ -829,6 +876,12 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v)
}
continue;
+ } else if (!strcasecmp(arg_name, DM_VERITY_OPT_AT_MOST_ONCE)) {
+ r = verity_alloc_most_once(v);
+ if (r)
+ return r;
+ continue;
+
} else if (verity_is_fec_opt_arg(arg_name)) {
r = verity_fec_parse_opt_args(as, v, &argc, arg_name);
if (r)
@@ -1096,7 +1149,7 @@ bad:
static struct target_type verity_target = {
.name = "verity",
- .version = {1, 3, 0},
+ .version = {1, 4, 0},
.module = THIS_MODULE,
.ctr = verity_ctr,
.dtr = verity_dtr,
diff --git a/drivers/md/dm-verity.h b/drivers/md/dm-verity.h
index b675bc015512..3441c10b840c 100644
--- a/drivers/md/dm-verity.h
+++ b/drivers/md/dm-verity.h
@@ -12,7 +12,7 @@
#ifndef DM_VERITY_H
#define DM_VERITY_H
-#include "dm-bufio.h"
+#include <linux/dm-bufio.h>
#include <linux/device-mapper.h>
#include <crypto/hash.h>
@@ -63,6 +63,7 @@ struct dm_verity {
sector_t hash_level_block[DM_VERITY_MAX_LEVELS];
struct dm_verity_fec *fec; /* forward error correction */
+ unsigned long *validated_blocks; /* bitset blocks validated */
};
struct dm_verity_io {
diff --git a/drivers/md/dm-zoned-target.c b/drivers/md/dm-zoned-target.c
index caff02caf083..e73b0776683c 100644
--- a/drivers/md/dm-zoned-target.c
+++ b/drivers/md/dm-zoned-target.c
@@ -898,8 +898,7 @@ static void dmz_io_hints(struct dm_target *ti, struct queue_limits *limits)
/*
* Pass on ioctl to the backend device.
*/
-static int dmz_prepare_ioctl(struct dm_target *ti,
- struct block_device **bdev, fmode_t *mode)
+static int dmz_prepare_ioctl(struct dm_target *ti, struct block_device **bdev)
{
struct dmz_target *dmz = ti->private;
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 45328d8b2859..4ea404dbcf0b 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -458,65 +458,56 @@ static int dm_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
return dm_get_geometry(md, geo);
}
-static char *_dm_claim_ptr = "I belong to device-mapper";
-
-static int dm_get_bdev_for_ioctl(struct mapped_device *md,
- struct block_device **bdev,
- fmode_t *mode)
+static int dm_prepare_ioctl(struct mapped_device *md, int *srcu_idx,
+ struct block_device **bdev)
+ __acquires(md->io_barrier)
{
struct dm_target *tgt;
struct dm_table *map;
- int srcu_idx, r;
+ int r;
retry:
r = -ENOTTY;
- map = dm_get_live_table(md, &srcu_idx);
+ map = dm_get_live_table(md, srcu_idx);
if (!map || !dm_table_get_size(map))
- goto out;
+ return r;
/* We only support devices that have a single target */
if (dm_table_get_num_targets(map) != 1)
- goto out;
+ return r;
tgt = dm_table_get_target(map, 0);
if (!tgt->type->prepare_ioctl)
- goto out;
-
- if (dm_suspended_md(md)) {
- r = -EAGAIN;
- goto out;
- }
-
- r = tgt->type->prepare_ioctl(tgt, bdev, mode);
- if (r < 0)
- goto out;
-
- bdgrab(*bdev);
- r = blkdev_get(*bdev, *mode, _dm_claim_ptr);
- if (r < 0)
- goto out;
+ return r;
- dm_put_live_table(md, srcu_idx);
- return r;
+ if (dm_suspended_md(md))
+ return -EAGAIN;
-out:
- dm_put_live_table(md, srcu_idx);
+ r = tgt->type->prepare_ioctl(tgt, bdev);
if (r == -ENOTCONN && !fatal_signal_pending(current)) {
+ dm_put_live_table(md, *srcu_idx);
msleep(10);
goto retry;
}
+
return r;
}
+static void dm_unprepare_ioctl(struct mapped_device *md, int srcu_idx)
+ __releases(md->io_barrier)
+{
+ dm_put_live_table(md, srcu_idx);
+}
+
static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long arg)
{
struct mapped_device *md = bdev->bd_disk->private_data;
- int r;
+ int r, srcu_idx;
- r = dm_get_bdev_for_ioctl(md, &bdev, &mode);
+ r = dm_prepare_ioctl(md, &srcu_idx, &bdev);
if (r < 0)
- return r;
+ goto out;
if (r > 0) {
/*
@@ -534,7 +525,7 @@ static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode,
r = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
out:
- blkdev_put(bdev, mode);
+ dm_unprepare_ioctl(md, srcu_idx);
return r;
}
@@ -708,6 +699,8 @@ static void dm_put_live_table_fast(struct mapped_device *md) __releases(RCU)
rcu_read_unlock();
}
+static char *_dm_claim_ptr = "I belong to device-mapper";
+
/*
* Open a table device so we can use it as a map destination.
*/
@@ -1412,6 +1405,11 @@ static unsigned get_num_discard_bios(struct dm_target *ti)
return ti->num_discard_bios;
}
+static unsigned get_num_secure_erase_bios(struct dm_target *ti)
+{
+ return ti->num_secure_erase_bios;
+}
+
static unsigned get_num_write_same_bios(struct dm_target *ti)
{
return ti->num_write_same_bios;
@@ -1465,6 +1463,11 @@ static int __send_discard(struct clone_info *ci, struct dm_target *ti)
is_split_required_for_discard);
}
+static int __send_secure_erase(struct clone_info *ci, struct dm_target *ti)
+{
+ return __send_changing_extent_only(ci, ti, get_num_secure_erase_bios, NULL);
+}
+
static int __send_write_same(struct clone_info *ci, struct dm_target *ti)
{
return __send_changing_extent_only(ci, ti, get_num_write_same_bios, NULL);
@@ -1475,6 +1478,25 @@ static int __send_write_zeroes(struct clone_info *ci, struct dm_target *ti)
return __send_changing_extent_only(ci, ti, get_num_write_zeroes_bios, NULL);
}
+static bool __process_abnormal_io(struct clone_info *ci, struct dm_target *ti,
+ int *result)
+{
+ struct bio *bio = ci->bio;
+
+ if (bio_op(bio) == REQ_OP_DISCARD)
+ *result = __send_discard(ci, ti);
+ else if (bio_op(bio) == REQ_OP_SECURE_ERASE)
+ *result = __send_secure_erase(ci, ti);
+ else if (bio_op(bio) == REQ_OP_WRITE_SAME)
+ *result = __send_write_same(ci, ti);
+ else if (bio_op(bio) == REQ_OP_WRITE_ZEROES)
+ *result = __send_write_zeroes(ci, ti);
+ else
+ return false;
+
+ return true;
+}
+
/*
* Select the correct strategy for processing a non-flush bio.
*/
@@ -1489,12 +1511,8 @@ static int __split_and_process_non_flush(struct clone_info *ci)
if (!dm_target_is_valid(ti))
return -EIO;
- if (unlikely(bio_op(bio) == REQ_OP_DISCARD))
- return __send_discard(ci, ti);
- else if (unlikely(bio_op(bio) == REQ_OP_WRITE_SAME))
- return __send_write_same(ci, ti);
- else if (unlikely(bio_op(bio) == REQ_OP_WRITE_ZEROES))
- return __send_write_zeroes(ci, ti);
+ if (unlikely(__process_abnormal_io(ci, ti, &r)))
+ return r;
if (bio_op(bio) == REQ_OP_ZONE_REPORT)
len = ci->sector_count;
@@ -1615,9 +1633,12 @@ static blk_qc_t __process_bio(struct mapped_device *md,
goto out;
}
- tio = alloc_tio(&ci, ti, 0, GFP_NOIO);
ci.bio = bio;
ci.sector_count = bio_sectors(bio);
+ if (unlikely(__process_abnormal_io(&ci, ti, &error)))
+ goto out;
+
+ tio = alloc_tio(&ci, ti, 0, GFP_NOIO);
ret = __clone_and_map_simple_bio(&ci, tio, NULL);
}
out:
@@ -1805,7 +1826,7 @@ static void cleanup_mapped_device(struct mapped_device *md)
static struct mapped_device *alloc_dev(int minor)
{
int r, numa_node_id = dm_get_numa_node();
- struct dax_device *dax_dev;
+ struct dax_device *dax_dev = NULL;
struct mapped_device *md;
void *old_md;
@@ -1846,7 +1867,7 @@ static struct mapped_device *alloc_dev(int minor)
INIT_LIST_HEAD(&md->table_devices);
spin_lock_init(&md->uevent_lock);
- md->queue = blk_alloc_queue_node(GFP_KERNEL, numa_node_id);
+ md->queue = blk_alloc_queue_node(GFP_KERNEL, numa_node_id, NULL);
if (!md->queue)
goto bad;
md->queue->queuedata = md;
@@ -1871,9 +1892,11 @@ static struct mapped_device *alloc_dev(int minor)
md->disk->private_data = md;
sprintf(md->disk->disk_name, "dm-%d", minor);
- dax_dev = alloc_dax(md, md->disk->disk_name, &dm_dax_ops);
- if (!dax_dev)
- goto bad;
+ if (IS_ENABLED(CONFIG_DAX_DRIVER)) {
+ dax_dev = alloc_dax(md, md->disk->disk_name, &dm_dax_ops);
+ if (!dax_dev)
+ goto bad;
+ }
md->dax_dev = dax_dev;
add_disk_no_queue_reg(md->disk);
@@ -3013,20 +3036,19 @@ static int dm_pr_reserve(struct block_device *bdev, u64 key, enum pr_type type,
{
struct mapped_device *md = bdev->bd_disk->private_data;
const struct pr_ops *ops;
- fmode_t mode;
- int r;
+ int r, srcu_idx;
- r = dm_get_bdev_for_ioctl(md, &bdev, &mode);
+ r = dm_prepare_ioctl(md, &srcu_idx, &bdev);
if (r < 0)
- return r;
+ goto out;
ops = bdev->bd_disk->fops->pr_ops;
if (ops && ops->pr_reserve)
r = ops->pr_reserve(bdev, key, type, flags);
else
r = -EOPNOTSUPP;
-
- blkdev_put(bdev, mode);
+out:
+ dm_unprepare_ioctl(md, srcu_idx);
return r;
}
@@ -3034,20 +3056,19 @@ static int dm_pr_release(struct block_device *bdev, u64 key, enum pr_type type)
{
struct mapped_device *md = bdev->bd_disk->private_data;
const struct pr_ops *ops;
- fmode_t mode;
- int r;
+ int r, srcu_idx;
- r = dm_get_bdev_for_ioctl(md, &bdev, &mode);
+ r = dm_prepare_ioctl(md, &srcu_idx, &bdev);
if (r < 0)
- return r;
+ goto out;
ops = bdev->bd_disk->fops->pr_ops;
if (ops && ops->pr_release)
r = ops->pr_release(bdev, key, type);
else
r = -EOPNOTSUPP;
-
- blkdev_put(bdev, mode);
+out:
+ dm_unprepare_ioctl(md, srcu_idx);
return r;
}
@@ -3056,20 +3077,19 @@ static int dm_pr_preempt(struct block_device *bdev, u64 old_key, u64 new_key,
{
struct mapped_device *md = bdev->bd_disk->private_data;
const struct pr_ops *ops;
- fmode_t mode;
- int r;
+ int r, srcu_idx;
- r = dm_get_bdev_for_ioctl(md, &bdev, &mode);
+ r = dm_prepare_ioctl(md, &srcu_idx, &bdev);
if (r < 0)
- return r;
+ goto out;
ops = bdev->bd_disk->fops->pr_ops;
if (ops && ops->pr_preempt)
r = ops->pr_preempt(bdev, old_key, new_key, type, abort);
else
r = -EOPNOTSUPP;
-
- blkdev_put(bdev, mode);
+out:
+ dm_unprepare_ioctl(md, srcu_idx);
return r;
}
@@ -3077,20 +3097,19 @@ static int dm_pr_clear(struct block_device *bdev, u64 key)
{
struct mapped_device *md = bdev->bd_disk->private_data;
const struct pr_ops *ops;
- fmode_t mode;
- int r;
+ int r, srcu_idx;
- r = dm_get_bdev_for_ioctl(md, &bdev, &mode);
+ r = dm_prepare_ioctl(md, &srcu_idx, &bdev);
if (r < 0)
- return r;
+ goto out;
ops = bdev->bd_disk->fops->pr_ops;
if (ops && ops->pr_clear)
r = ops->pr_clear(bdev, key);
else
r = -EOPNOTSUPP;
-
- blkdev_put(bdev, mode);
+out:
+ dm_unprepare_ioctl(md, srcu_idx);
return r;
}
diff --git a/drivers/md/md-linear.c b/drivers/md/md-linear.c
index 773fc70dced7..4964323d936b 100644
--- a/drivers/md/md-linear.c
+++ b/drivers/md/md-linear.c
@@ -138,9 +138,9 @@ static struct linear_conf *linear_conf(struct mddev *mddev, int raid_disks)
}
if (!discard_supported)
- queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, mddev->queue);
+ blk_queue_flag_clear(QUEUE_FLAG_DISCARD, mddev->queue);
else
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, mddev->queue);
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, mddev->queue);
/*
* Here we calculate the device offsets.
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 254e44e44668..c208c01f63a5 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -5206,12 +5206,12 @@ static void md_free(struct kobject *ko)
if (mddev->sysfs_state)
sysfs_put(mddev->sysfs_state);
+ if (mddev->gendisk)
+ del_gendisk(mddev->gendisk);
if (mddev->queue)
blk_cleanup_queue(mddev->queue);
- if (mddev->gendisk) {
- del_gendisk(mddev->gendisk);
+ if (mddev->gendisk)
put_disk(mddev->gendisk);
- }
percpu_ref_exit(&mddev->writes_pending);
kfree(mddev);
@@ -5619,9 +5619,9 @@ int md_run(struct mddev *mddev)
if (mddev->degraded)
nonrot = false;
if (nonrot)
- queue_flag_set_unlocked(QUEUE_FLAG_NONROT, mddev->queue);
+ blk_queue_flag_set(QUEUE_FLAG_NONROT, mddev->queue);
else
- queue_flag_clear_unlocked(QUEUE_FLAG_NONROT, mddev->queue);
+ blk_queue_flag_clear(QUEUE_FLAG_NONROT, mddev->queue);
mddev->queue->backing_dev_info->congested_data = mddev;
mddev->queue->backing_dev_info->congested_fn = md_congested;
}
@@ -9256,8 +9256,10 @@ void md_reload_sb(struct mddev *mddev, int nr)
check_sb_changes(mddev, rdev);
/* Read all rdev's to update recovery_offset */
- rdev_for_each_rcu(rdev, mddev)
- read_rdev(mddev, rdev);
+ rdev_for_each_rcu(rdev, mddev) {
+ if (!test_bit(Faulty, &rdev->flags))
+ read_rdev(mddev, rdev);
+ }
}
EXPORT_SYMBOL(md_reload_sb);
diff --git a/drivers/md/persistent-data/dm-block-manager.c b/drivers/md/persistent-data/dm-block-manager.c
index ea15d220ced7..492a3f8ac119 100644
--- a/drivers/md/persistent-data/dm-block-manager.c
+++ b/drivers/md/persistent-data/dm-block-manager.c
@@ -5,8 +5,8 @@
*/
#include "dm-block-manager.h"
#include "dm-persistent-data-internal.h"
-#include "../dm-bufio.h"
+#include <linux/dm-bufio.h>
#include <linux/crc32c.h>
#include <linux/module.h>
#include <linux/slab.h>
diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c
index 5ecba9eef441..584c10347267 100644
--- a/drivers/md/raid0.c
+++ b/drivers/md/raid0.c
@@ -399,9 +399,9 @@ static int raid0_run(struct mddev *mddev)
discard_supported = true;
}
if (!discard_supported)
- queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, mddev->queue);
+ blk_queue_flag_clear(QUEUE_FLAG_DISCARD, mddev->queue);
else
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, mddev->queue);
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, mddev->queue);
}
/* calculate array device size */
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index fe872dc6712e..e9e3308cb0a7 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -854,7 +854,7 @@ static void flush_pending_writes(struct r1conf *conf)
* there is no normal IO happeing. It must arrange to call
* lower_barrier when the particular background IO completes.
*/
-static void raise_barrier(struct r1conf *conf, sector_t sector_nr)
+static sector_t raise_barrier(struct r1conf *conf, sector_t sector_nr)
{
int idx = sector_to_idx(sector_nr);
@@ -885,13 +885,23 @@ static void raise_barrier(struct r1conf *conf, sector_t sector_nr)
* max resync count which allowed on current I/O barrier bucket.
*/
wait_event_lock_irq(conf->wait_barrier,
- !conf->array_frozen &&
+ (!conf->array_frozen &&
!atomic_read(&conf->nr_pending[idx]) &&
- atomic_read(&conf->barrier[idx]) < RESYNC_DEPTH,
+ atomic_read(&conf->barrier[idx]) < RESYNC_DEPTH) ||
+ test_bit(MD_RECOVERY_INTR, &conf->mddev->recovery),
conf->resync_lock);
+ if (test_bit(MD_RECOVERY_INTR, &conf->mddev->recovery)) {
+ atomic_dec(&conf->barrier[idx]);
+ spin_unlock_irq(&conf->resync_lock);
+ wake_up(&conf->wait_barrier);
+ return -EINTR;
+ }
+
atomic_inc(&conf->nr_sync_pending);
spin_unlock_irq(&conf->resync_lock);
+
+ return 0;
}
static void lower_barrier(struct r1conf *conf, sector_t sector_nr)
@@ -1092,6 +1102,8 @@ static void alloc_behind_master_bio(struct r1bio *r1_bio,
goto skip_copy;
}
+ behind_bio->bi_write_hint = bio->bi_write_hint;
+
while (i < vcnt && size) {
struct page *page;
int len = min_t(int, PAGE_SIZE, size);
@@ -1760,7 +1772,7 @@ static int raid1_add_disk(struct mddev *mddev, struct md_rdev *rdev)
}
}
if (mddev->queue && blk_queue_discard(bdev_get_queue(rdev->bdev)))
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, mddev->queue);
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, mddev->queue);
print_conf(conf);
return err;
}
@@ -2662,9 +2674,12 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr,
bitmap_cond_end_sync(mddev->bitmap, sector_nr,
mddev_is_clustered(mddev) && (sector_nr + 2 * RESYNC_SECTORS > conf->cluster_sync_high));
- r1_bio = raid1_alloc_init_r1buf(conf);
- raise_barrier(conf, sector_nr);
+
+ if (raise_barrier(conf, sector_nr))
+ return 0;
+
+ r1_bio = raid1_alloc_init_r1buf(conf);
rcu_read_lock();
/*
@@ -3110,10 +3125,10 @@ static int raid1_run(struct mddev *mddev)
if (mddev->queue) {
if (discard_supported)
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD,
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD,
mddev->queue);
else
- queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD,
+ blk_queue_flag_clear(QUEUE_FLAG_DISCARD,
mddev->queue);
}
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index c5e6c60fc0d4..3c60774c8430 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -1845,7 +1845,7 @@ static int raid10_add_disk(struct mddev *mddev, struct md_rdev *rdev)
break;
}
if (mddev->queue && blk_queue_discard(bdev_get_queue(rdev->bdev)))
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, mddev->queue);
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, mddev->queue);
print_conf(conf);
return err;
@@ -3846,10 +3846,10 @@ static int raid10_run(struct mddev *mddev)
if (mddev->queue) {
if (discard_supported)
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD,
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD,
mddev->queue);
else
- queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD,
+ blk_queue_flag_clear(QUEUE_FLAG_DISCARD,
mddev->queue);
}
/* need to check that every block has at least one working mirror */
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index b5d2601483e3..be117d0a65a8 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -7443,10 +7443,10 @@ static int raid5_run(struct mddev *mddev)
if (devices_handle_discard_safely &&
mddev->queue->limits.max_discard_sectors >= (stripe >> 9) &&
mddev->queue->limits.discard_granularity >= stripe)
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD,
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD,
mddev->queue);
else
- queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD,
+ blk_queue_flag_clear(QUEUE_FLAG_DISCARD,
mddev->queue);
blk_queue_max_hw_sectors(mddev->queue, UINT_MAX);
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
index 86c1a190d946..37124c3b8c2a 100644
--- a/drivers/media/Kconfig
+++ b/drivers/media/Kconfig
@@ -141,6 +141,7 @@ config DVB_CORE
tristate
depends on MEDIA_SUPPORT
depends on MEDIA_DIGITAL_TV_SUPPORT
+ depends on (I2C || I2C=n)
default y
select CRC32
diff --git a/drivers/media/cec/Kconfig b/drivers/media/cec/Kconfig
index 43428cec3a01..9c2b108c613a 100644
--- a/drivers/media/cec/Kconfig
+++ b/drivers/media/cec/Kconfig
@@ -4,3 +4,9 @@ config MEDIA_CEC_RC
depends on CEC_CORE=m || RC_CORE=y
---help---
Pass on CEC remote control messages to the RC framework.
+
+config CEC_PIN_ERROR_INJ
+ bool "Enable CEC error injection support"
+ depends on CEC_PIN && DEBUG_FS
+ ---help---
+ This option enables CEC error injection using debugfs.
diff --git a/drivers/media/cec/Makefile b/drivers/media/cec/Makefile
index 41ee3325e1ea..29a2ab9e77c5 100644
--- a/drivers/media/cec/Makefile
+++ b/drivers/media/cec/Makefile
@@ -9,4 +9,8 @@ ifeq ($(CONFIG_CEC_PIN),y)
cec-objs += cec-pin.o
endif
+ifeq ($(CONFIG_CEC_PIN_ERROR_INJ),y)
+ cec-objs += cec-pin-error-inj.o
+endif
+
obj-$(CONFIG_CEC_CORE) += cec.o
diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c
index 2b1e540587d6..002ed4c90371 100644
--- a/drivers/media/cec/cec-adap.c
+++ b/drivers/media/cec/cec-adap.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cec-adap.c - HDMI Consumer Electronics Control framework - CEC adapter
*
* Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/errno.h>
@@ -85,8 +73,8 @@ static unsigned int cec_log_addr2dev(const struct cec_adapter *adap, u8 log_addr
void cec_queue_event_fh(struct cec_fh *fh,
const struct cec_event *new_ev, u64 ts)
{
- static const u8 max_events[CEC_NUM_EVENTS] = {
- 1, 1, 64, 64, 8, 8,
+ static const u16 max_events[CEC_NUM_EVENTS] = {
+ 1, 1, 800, 800, 8, 8,
};
struct cec_event_entry *entry;
unsigned int ev_idx = new_ev->event - 1;
@@ -154,11 +142,13 @@ static void cec_queue_event(struct cec_adapter *adap,
}
/* Notify userspace that the CEC pin changed state at the given time. */
-void cec_queue_pin_cec_event(struct cec_adapter *adap, bool is_high, ktime_t ts)
+void cec_queue_pin_cec_event(struct cec_adapter *adap, bool is_high,
+ bool dropped_events, ktime_t ts)
{
struct cec_event ev = {
.event = is_high ? CEC_EVENT_PIN_CEC_HIGH :
CEC_EVENT_PIN_CEC_LOW,
+ .flags = dropped_events ? CEC_EVENT_FL_DROPPED_EVENTS : 0,
};
struct cec_fh *fh;
@@ -711,16 +701,31 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg,
else
msg->flags = 0;
+ if (msg->len > 1 && msg->msg[1] == CEC_MSG_CDC_MESSAGE) {
+ msg->msg[2] = adap->phys_addr >> 8;
+ msg->msg[3] = adap->phys_addr & 0xff;
+ }
+
/* Sanity checks */
if (msg->len == 0 || msg->len > CEC_MAX_MSG_SIZE) {
dprintk(1, "%s: invalid length %d\n", __func__, msg->len);
return -EINVAL;
}
+
+ memset(msg->msg + msg->len, 0, sizeof(msg->msg) - msg->len);
+
+ if (msg->timeout)
+ dprintk(2, "%s: %*ph (wait for 0x%02x%s)\n",
+ __func__, msg->len, msg->msg, msg->reply,
+ !block ? ", nb" : "");
+ else
+ dprintk(2, "%s: %*ph%s\n",
+ __func__, msg->len, msg->msg, !block ? " (nb)" : "");
+
if (msg->timeout && msg->len == 1) {
- dprintk(1, "%s: can't reply for poll msg\n", __func__);
+ dprintk(1, "%s: can't reply to poll msg\n", __func__);
return -EINVAL;
}
- memset(msg->msg + msg->len, 0, sizeof(msg->msg) - msg->len);
if (msg->len == 1) {
if (cec_msg_destination(msg) == 0xf) {
dprintk(1, "%s: invalid poll message\n", __func__);
@@ -780,19 +785,6 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg,
if (!msg->sequence)
msg->sequence = ++adap->sequence;
- if (msg->len > 1 && msg->msg[1] == CEC_MSG_CDC_MESSAGE) {
- msg->msg[2] = adap->phys_addr >> 8;
- msg->msg[3] = adap->phys_addr & 0xff;
- }
-
- if (msg->timeout)
- dprintk(2, "%s: %*ph (wait for 0x%02x%s)\n",
- __func__, msg->len, msg->msg, msg->reply,
- !block ? ", nb" : "");
- else
- dprintk(2, "%s: %*ph%s\n",
- __func__, msg->len, msg->msg, !block ? " (nb)" : "");
-
data->msg = *msg;
data->fh = fh;
data->adap = adap;
diff --git a/drivers/media/cec/cec-api.c b/drivers/media/cec/cec-api.c
index 492db12b8c4d..10b67fc40318 100644
--- a/drivers/media/cec/cec-api.c
+++ b/drivers/media/cec/cec-api.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cec-api.c - HDMI Consumer Electronics Control framework - API
*
* Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/errno.h>
diff --git a/drivers/media/cec/cec-core.c b/drivers/media/cec/cec-core.c
index a9f9525db9ae..b0c87f9ea08f 100644
--- a/drivers/media/cec/cec-core.c
+++ b/drivers/media/cec/cec-core.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cec-core.c - HDMI Consumer Electronics Control framework - Core
*
* Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/errno.h>
@@ -207,6 +195,55 @@ void cec_register_cec_notifier(struct cec_adapter *adap,
EXPORT_SYMBOL_GPL(cec_register_cec_notifier);
#endif
+#ifdef CONFIG_DEBUG_FS
+static ssize_t cec_error_inj_write(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *ppos)
+{
+ struct seq_file *sf = file->private_data;
+ struct cec_adapter *adap = sf->private;
+ char *buf;
+ char *line;
+ char *p;
+
+ buf = memdup_user_nul(ubuf, min_t(size_t, PAGE_SIZE, count));
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+ p = buf;
+ while (p && *p) {
+ p = skip_spaces(p);
+ line = strsep(&p, "\n");
+ if (!*line || *line == '#')
+ continue;
+ if (!adap->ops->error_inj_parse_line(adap, line)) {
+ kfree(buf);
+ return -EINVAL;
+ }
+ }
+ kfree(buf);
+ return count;
+}
+
+static int cec_error_inj_show(struct seq_file *sf, void *unused)
+{
+ struct cec_adapter *adap = sf->private;
+
+ return adap->ops->error_inj_show(adap, sf);
+}
+
+static int cec_error_inj_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, cec_error_inj_show, inode->i_private);
+}
+
+static const struct file_operations cec_error_inj_fops = {
+ .open = cec_error_inj_open,
+ .write = cec_error_inj_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+#endif
+
struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
void *priv, const char *name, u32 caps,
u8 available_las)
@@ -346,7 +383,16 @@ int cec_register_adapter(struct cec_adapter *adap,
pr_warn("cec-%s: Failed to create status file\n", adap->name);
debugfs_remove_recursive(adap->cec_dir);
adap->cec_dir = NULL;
+ return 0;
}
+ if (!adap->ops->error_inj_show || !adap->ops->error_inj_parse_line)
+ return 0;
+ adap->error_inj_file = debugfs_create_file("error-inj", 0644,
+ adap->cec_dir, adap,
+ &cec_error_inj_fops);
+ if (IS_ERR_OR_NULL(adap->error_inj_file))
+ pr_warn("cec-%s: Failed to create error-inj file\n",
+ adap->name);
#endif
return 0;
}
diff --git a/drivers/media/cec/cec-edid.c b/drivers/media/cec/cec-edid.c
index 38e3fec6152b..ec72ac1c0b91 100644
--- a/drivers/media/cec/cec-edid.c
+++ b/drivers/media/cec/cec-edid.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cec-edid - HDMI Consumer Electronics Control EDID & CEC helper functions
*
* Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/module.h>
diff --git a/drivers/media/cec/cec-notifier.c b/drivers/media/cec/cec-notifier.c
index 08b619d0ea1e..16dffa06c913 100644
--- a/drivers/media/cec/cec-notifier.c
+++ b/drivers/media/cec/cec-notifier.c
@@ -1,21 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cec-notifier.c - notify CEC drivers of physical address changes
*
* Copyright 2016 Russell King <rmk+kernel@arm.linux.org.uk>
* Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/export.h>
diff --git a/drivers/media/cec/cec-pin-error-inj.c b/drivers/media/cec/cec-pin-error-inj.c
new file mode 100644
index 000000000000..aaa899a175ce
--- /dev/null
+++ b/drivers/media/cec/cec-pin-error-inj.c
@@ -0,0 +1,342 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ */
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/sched/types.h>
+
+#include <media/cec-pin.h>
+#include "cec-pin-priv.h"
+
+struct cec_error_inj_cmd {
+ unsigned int mode_offset;
+ int arg_idx;
+ const char *cmd;
+};
+
+static const struct cec_error_inj_cmd cec_error_inj_cmds[] = {
+ { CEC_ERROR_INJ_RX_NACK_OFFSET, -1, "rx-nack" },
+ { CEC_ERROR_INJ_RX_LOW_DRIVE_OFFSET,
+ CEC_ERROR_INJ_RX_LOW_DRIVE_ARG_IDX, "rx-low-drive" },
+ { CEC_ERROR_INJ_RX_ADD_BYTE_OFFSET, -1, "rx-add-byte" },
+ { CEC_ERROR_INJ_RX_REMOVE_BYTE_OFFSET, -1, "rx-remove-byte" },
+ { CEC_ERROR_INJ_RX_ARB_LOST_OFFSET,
+ CEC_ERROR_INJ_RX_ARB_LOST_ARG_IDX, "rx-arb-lost" },
+
+ { CEC_ERROR_INJ_TX_NO_EOM_OFFSET, -1, "tx-no-eom" },
+ { CEC_ERROR_INJ_TX_EARLY_EOM_OFFSET, -1, "tx-early-eom" },
+ { CEC_ERROR_INJ_TX_ADD_BYTES_OFFSET,
+ CEC_ERROR_INJ_TX_ADD_BYTES_ARG_IDX, "tx-add-bytes" },
+ { CEC_ERROR_INJ_TX_REMOVE_BYTE_OFFSET, -1, "tx-remove-byte" },
+ { CEC_ERROR_INJ_TX_SHORT_BIT_OFFSET,
+ CEC_ERROR_INJ_TX_SHORT_BIT_ARG_IDX, "tx-short-bit" },
+ { CEC_ERROR_INJ_TX_LONG_BIT_OFFSET,
+ CEC_ERROR_INJ_TX_LONG_BIT_ARG_IDX, "tx-long-bit" },
+ { CEC_ERROR_INJ_TX_CUSTOM_BIT_OFFSET,
+ CEC_ERROR_INJ_TX_CUSTOM_BIT_ARG_IDX, "tx-custom-bit" },
+ { CEC_ERROR_INJ_TX_SHORT_START_OFFSET, -1, "tx-short-start" },
+ { CEC_ERROR_INJ_TX_LONG_START_OFFSET, -1, "tx-long-start" },
+ { CEC_ERROR_INJ_TX_CUSTOM_START_OFFSET, -1, "tx-custom-start" },
+ { CEC_ERROR_INJ_TX_LAST_BIT_OFFSET,
+ CEC_ERROR_INJ_TX_LAST_BIT_ARG_IDX, "tx-last-bit" },
+ { CEC_ERROR_INJ_TX_LOW_DRIVE_OFFSET,
+ CEC_ERROR_INJ_TX_LOW_DRIVE_ARG_IDX, "tx-low-drive" },
+ { 0, -1, NULL }
+};
+
+u16 cec_pin_rx_error_inj(struct cec_pin *pin)
+{
+ u16 cmd = CEC_ERROR_INJ_OP_ANY;
+
+ /* Only when 18 bits have been received do we have a valid cmd */
+ if (!(pin->error_inj[cmd] & CEC_ERROR_INJ_RX_MASK) &&
+ pin->rx_bit >= 18)
+ cmd = pin->rx_msg.msg[1];
+ return (pin->error_inj[cmd] & CEC_ERROR_INJ_RX_MASK) ? cmd :
+ CEC_ERROR_INJ_OP_ANY;
+}
+
+u16 cec_pin_tx_error_inj(struct cec_pin *pin)
+{
+ u16 cmd = CEC_ERROR_INJ_OP_ANY;
+
+ if (!(pin->error_inj[cmd] & CEC_ERROR_INJ_TX_MASK) &&
+ pin->tx_msg.len > 1)
+ cmd = pin->tx_msg.msg[1];
+ return (pin->error_inj[cmd] & CEC_ERROR_INJ_TX_MASK) ? cmd :
+ CEC_ERROR_INJ_OP_ANY;
+}
+
+bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line)
+{
+ static const char *delims = " \t\r";
+ struct cec_pin *pin = adap->pin;
+ unsigned int i;
+ bool has_pos = false;
+ char *p = line;
+ char *token;
+ char *comma;
+ u64 *error;
+ u8 *args;
+ bool has_op;
+ u32 op;
+ u8 mode;
+ u8 pos;
+ u8 v;
+
+ p = skip_spaces(p);
+ token = strsep(&p, delims);
+ if (!strcmp(token, "clear")) {
+ memset(pin->error_inj, 0, sizeof(pin->error_inj));
+ pin->rx_toggle = pin->tx_toggle = false;
+ pin->tx_ignore_nack_until_eom = false;
+ pin->tx_custom_pulse = false;
+ pin->tx_custom_low_usecs = CEC_TIM_CUSTOM_DEFAULT;
+ pin->tx_custom_high_usecs = CEC_TIM_CUSTOM_DEFAULT;
+ return true;
+ }
+ if (!strcmp(token, "rx-clear")) {
+ for (i = 0; i <= CEC_ERROR_INJ_OP_ANY; i++)
+ pin->error_inj[i] &= ~CEC_ERROR_INJ_RX_MASK;
+ pin->rx_toggle = false;
+ return true;
+ }
+ if (!strcmp(token, "tx-clear")) {
+ for (i = 0; i <= CEC_ERROR_INJ_OP_ANY; i++)
+ pin->error_inj[i] &= ~CEC_ERROR_INJ_TX_MASK;
+ pin->tx_toggle = false;
+ pin->tx_ignore_nack_until_eom = false;
+ pin->tx_custom_pulse = false;
+ pin->tx_custom_low_usecs = CEC_TIM_CUSTOM_DEFAULT;
+ pin->tx_custom_high_usecs = CEC_TIM_CUSTOM_DEFAULT;
+ return true;
+ }
+ if (!strcmp(token, "tx-ignore-nack-until-eom")) {
+ pin->tx_ignore_nack_until_eom = true;
+ return true;
+ }
+ if (!strcmp(token, "tx-custom-pulse")) {
+ pin->tx_custom_pulse = true;
+ cec_pin_start_timer(pin);
+ return true;
+ }
+ if (!p)
+ return false;
+
+ p = skip_spaces(p);
+ if (!strcmp(token, "tx-custom-low-usecs")) {
+ u32 usecs;
+
+ if (kstrtou32(p, 0, &usecs) || usecs > 10000000)
+ return false;
+ pin->tx_custom_low_usecs = usecs;
+ return true;
+ }
+ if (!strcmp(token, "tx-custom-high-usecs")) {
+ u32 usecs;
+
+ if (kstrtou32(p, 0, &usecs) || usecs > 10000000)
+ return false;
+ pin->tx_custom_high_usecs = usecs;
+ return true;
+ }
+
+ comma = strchr(token, ',');
+ if (comma)
+ *comma++ = '\0';
+ if (!strcmp(token, "any"))
+ op = CEC_ERROR_INJ_OP_ANY;
+ else if (!kstrtou8(token, 0, &v))
+ op = v;
+ else
+ return false;
+ mode = CEC_ERROR_INJ_MODE_ONCE;
+ if (comma) {
+ if (!strcmp(comma, "off"))
+ mode = CEC_ERROR_INJ_MODE_OFF;
+ else if (!strcmp(comma, "once"))
+ mode = CEC_ERROR_INJ_MODE_ONCE;
+ else if (!strcmp(comma, "always"))
+ mode = CEC_ERROR_INJ_MODE_ALWAYS;
+ else if (!strcmp(comma, "toggle"))
+ mode = CEC_ERROR_INJ_MODE_TOGGLE;
+ else
+ return false;
+ }
+
+ error = pin->error_inj + op;
+ args = pin->error_inj_args[op];
+ has_op = op <= 0xff;
+
+ token = strsep(&p, delims);
+ if (p) {
+ p = skip_spaces(p);
+ has_pos = !kstrtou8(p, 0, &pos);
+ }
+
+ if (!strcmp(token, "clear")) {
+ *error = 0;
+ return true;
+ }
+ if (!strcmp(token, "rx-clear")) {
+ *error &= ~CEC_ERROR_INJ_RX_MASK;
+ return true;
+ }
+ if (!strcmp(token, "tx-clear")) {
+ *error &= ~CEC_ERROR_INJ_TX_MASK;
+ return true;
+ }
+
+ for (i = 0; cec_error_inj_cmds[i].cmd; i++) {
+ const char *cmd = cec_error_inj_cmds[i].cmd;
+ unsigned int mode_offset;
+ u64 mode_mask;
+ int arg_idx;
+ bool is_bit_pos = true;
+
+ if (strcmp(token, cmd))
+ continue;
+
+ mode_offset = cec_error_inj_cmds[i].mode_offset;
+ mode_mask = CEC_ERROR_INJ_MODE_MASK << mode_offset;
+ arg_idx = cec_error_inj_cmds[i].arg_idx;
+
+ if (mode_offset == CEC_ERROR_INJ_RX_ARB_LOST_OFFSET ||
+ mode_offset == CEC_ERROR_INJ_TX_ADD_BYTES_OFFSET)
+ is_bit_pos = false;
+
+ if (mode_offset == CEC_ERROR_INJ_RX_ARB_LOST_OFFSET) {
+ if (has_op)
+ return false;
+ if (!has_pos)
+ pos = 0x0f;
+ }
+ if (arg_idx >= 0 && is_bit_pos) {
+ if (!has_pos || pos >= 160)
+ return false;
+ if (has_op && pos < 10 + 8)
+ return false;
+ /* Invalid bit position may not be the Ack bit */
+ if ((mode_offset == CEC_ERROR_INJ_TX_SHORT_BIT_OFFSET ||
+ mode_offset == CEC_ERROR_INJ_TX_LONG_BIT_OFFSET ||
+ mode_offset == CEC_ERROR_INJ_TX_CUSTOM_BIT_OFFSET) &&
+ (pos % 10) == 9)
+ return false;
+ }
+ *error &= ~mode_mask;
+ *error |= (u64)mode << mode_offset;
+ if (arg_idx >= 0)
+ args[arg_idx] = pos;
+ return true;
+ }
+ return false;
+}
+
+static void cec_pin_show_cmd(struct seq_file *sf, u32 cmd, u8 mode)
+{
+ if (cmd == CEC_ERROR_INJ_OP_ANY)
+ seq_puts(sf, "any,");
+ else
+ seq_printf(sf, "0x%02x,", cmd);
+ switch (mode) {
+ case CEC_ERROR_INJ_MODE_ONCE:
+ seq_puts(sf, "once ");
+ break;
+ case CEC_ERROR_INJ_MODE_ALWAYS:
+ seq_puts(sf, "always ");
+ break;
+ case CEC_ERROR_INJ_MODE_TOGGLE:
+ seq_puts(sf, "toggle ");
+ break;
+ default:
+ seq_puts(sf, "off ");
+ break;
+ }
+}
+
+int cec_pin_error_inj_show(struct cec_adapter *adap, struct seq_file *sf)
+{
+ struct cec_pin *pin = adap->pin;
+ unsigned int i, j;
+
+ seq_puts(sf, "# Clear error injections:\n");
+ seq_puts(sf, "# clear clear all rx and tx error injections\n");
+ seq_puts(sf, "# rx-clear clear all rx error injections\n");
+ seq_puts(sf, "# tx-clear clear all tx error injections\n");
+ seq_puts(sf, "# <op> clear clear all rx and tx error injections for <op>\n");
+ seq_puts(sf, "# <op> rx-clear clear all rx error injections for <op>\n");
+ seq_puts(sf, "# <op> tx-clear clear all tx error injections for <op>\n");
+ seq_puts(sf, "#\n");
+ seq_puts(sf, "# RX error injection:\n");
+ seq_puts(sf, "# <op>[,<mode>] rx-nack NACK the message instead of sending an ACK\n");
+ seq_puts(sf, "# <op>[,<mode>] rx-low-drive <bit> force a low-drive condition at this bit position\n");
+ seq_puts(sf, "# <op>[,<mode>] rx-add-byte add a spurious byte to the received CEC message\n");
+ seq_puts(sf, "# <op>[,<mode>] rx-remove-byte remove the last byte from the received CEC message\n");
+ seq_puts(sf, "# <op>[,<mode>] rx-arb-lost <poll> generate a POLL message to trigger an arbitration lost\n");
+ seq_puts(sf, "#\n");
+ seq_puts(sf, "# TX error injection settings:\n");
+ seq_puts(sf, "# tx-ignore-nack-until-eom ignore early NACKs until EOM\n");
+ seq_puts(sf, "# tx-custom-low-usecs <usecs> define the 'low' time for the custom pulse\n");
+ seq_puts(sf, "# tx-custom-high-usecs <usecs> define the 'high' time for the custom pulse\n");
+ seq_puts(sf, "# tx-custom-pulse transmit the custom pulse once the bus is idle\n");
+ seq_puts(sf, "#\n");
+ seq_puts(sf, "# TX error injection:\n");
+ seq_puts(sf, "# <op>[,<mode>] tx-no-eom don't set the EOM bit\n");
+ seq_puts(sf, "# <op>[,<mode>] tx-early-eom set the EOM bit one byte too soon\n");
+ seq_puts(sf, "# <op>[,<mode>] tx-add-bytes <num> append <num> (1-255) spurious bytes to the message\n");
+ seq_puts(sf, "# <op>[,<mode>] tx-remove-byte drop the last byte from the message\n");
+ seq_puts(sf, "# <op>[,<mode>] tx-short-bit <bit> make this bit shorter than allowed\n");
+ seq_puts(sf, "# <op>[,<mode>] tx-long-bit <bit> make this bit longer than allowed\n");
+ seq_puts(sf, "# <op>[,<mode>] tx-custom-bit <bit> send the custom pulse instead of this bit\n");
+ seq_puts(sf, "# <op>[,<mode>] tx-short-start send a start pulse that's too short\n");
+ seq_puts(sf, "# <op>[,<mode>] tx-long-start send a start pulse that's too long\n");
+ seq_puts(sf, "# <op>[,<mode>] tx-custom-start send the custom pulse instead of the start pulse\n");
+ seq_puts(sf, "# <op>[,<mode>] tx-last-bit <bit> stop sending after this bit\n");
+ seq_puts(sf, "# <op>[,<mode>] tx-low-drive <bit> force a low-drive condition at this bit position\n");
+ seq_puts(sf, "#\n");
+ seq_puts(sf, "# <op> CEC message opcode (0-255) or 'any'\n");
+ seq_puts(sf, "# <mode> 'once' (default), 'always', 'toggle' or 'off'\n");
+ seq_puts(sf, "# <bit> CEC message bit (0-159)\n");
+ seq_puts(sf, "# 10 bits per 'byte': bits 0-7: data, bit 8: EOM, bit 9: ACK\n");
+ seq_puts(sf, "# <poll> CEC poll message used to test arbitration lost (0x00-0xff, default 0x0f)\n");
+ seq_puts(sf, "# <usecs> microseconds (0-10000000, default 1000)\n");
+
+ seq_puts(sf, "\nclear\n");
+
+ for (i = 0; i < ARRAY_SIZE(pin->error_inj); i++) {
+ u64 e = pin->error_inj[i];
+
+ for (j = 0; cec_error_inj_cmds[j].cmd; j++) {
+ const char *cmd = cec_error_inj_cmds[j].cmd;
+ unsigned int mode;
+ unsigned int mode_offset;
+ int arg_idx;
+
+ mode_offset = cec_error_inj_cmds[j].mode_offset;
+ arg_idx = cec_error_inj_cmds[j].arg_idx;
+ mode = (e >> mode_offset) & CEC_ERROR_INJ_MODE_MASK;
+ if (!mode)
+ continue;
+ cec_pin_show_cmd(sf, i, mode);
+ seq_puts(sf, cmd);
+ if (arg_idx >= 0)
+ seq_printf(sf, " %u",
+ pin->error_inj_args[i][arg_idx]);
+ seq_puts(sf, "\n");
+ }
+ }
+
+ if (pin->tx_ignore_nack_until_eom)
+ seq_puts(sf, "tx-ignore-nack-until-eom\n");
+ if (pin->tx_custom_pulse)
+ seq_puts(sf, "tx-custom-pulse\n");
+ if (pin->tx_custom_low_usecs != CEC_TIM_CUSTOM_DEFAULT)
+ seq_printf(sf, "tx-custom-low-usecs %u\n",
+ pin->tx_custom_low_usecs);
+ if (pin->tx_custom_high_usecs != CEC_TIM_CUSTOM_DEFAULT)
+ seq_printf(sf, "tx-custom-high-usecs %u\n",
+ pin->tx_custom_high_usecs);
+ return 0;
+}
diff --git a/drivers/media/cec/cec-pin-priv.h b/drivers/media/cec/cec-pin-priv.h
index 7d0def199762..f423db8855d9 100644
--- a/drivers/media/cec/cec-pin-priv.h
+++ b/drivers/media/cec/cec-pin-priv.h
@@ -1,20 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* cec-pin-priv.h - internal cec-pin header
*
* Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
#ifndef LINUX_CEC_PIN_PRIV_H
@@ -40,14 +28,30 @@ enum cec_pin_state {
CEC_ST_TX_START_BIT_LOW,
/* Drive CEC high for the start bit */
CEC_ST_TX_START_BIT_HIGH,
+ /* Generate a start bit period that is too short */
+ CEC_ST_TX_START_BIT_HIGH_SHORT,
+ /* Generate a start bit period that is too long */
+ CEC_ST_TX_START_BIT_HIGH_LONG,
+ /* Drive CEC low for the start bit using the custom timing */
+ CEC_ST_TX_START_BIT_LOW_CUSTOM,
+ /* Drive CEC high for the start bit using the custom timing */
+ CEC_ST_TX_START_BIT_HIGH_CUSTOM,
/* Drive CEC low for the 0 bit */
CEC_ST_TX_DATA_BIT_0_LOW,
/* Drive CEC high for the 0 bit */
CEC_ST_TX_DATA_BIT_0_HIGH,
+ /* Generate a bit period that is too short */
+ CEC_ST_TX_DATA_BIT_0_HIGH_SHORT,
+ /* Generate a bit period that is too long */
+ CEC_ST_TX_DATA_BIT_0_HIGH_LONG,
/* Drive CEC low for the 1 bit */
CEC_ST_TX_DATA_BIT_1_LOW,
/* Drive CEC high for the 1 bit */
CEC_ST_TX_DATA_BIT_1_HIGH,
+ /* Generate a bit period that is too short */
+ CEC_ST_TX_DATA_BIT_1_HIGH_SHORT,
+ /* Generate a bit period that is too long */
+ CEC_ST_TX_DATA_BIT_1_HIGH_LONG,
/*
* Wait for start of sample time to check for Ack bit or first
* four initiator bits to check for Arbitration Lost.
@@ -55,6 +59,20 @@ enum cec_pin_state {
CEC_ST_TX_DATA_BIT_1_HIGH_PRE_SAMPLE,
/* Wait for end of bit period after sampling */
CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE,
+ /* Generate a bit period that is too short */
+ CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_SHORT,
+ /* Generate a bit period that is too long */
+ CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_LONG,
+ /* Drive CEC low for a data bit using the custom timing */
+ CEC_ST_TX_DATA_BIT_LOW_CUSTOM,
+ /* Drive CEC high for a data bit using the custom timing */
+ CEC_ST_TX_DATA_BIT_HIGH_CUSTOM,
+ /* Drive CEC low for a standalone pulse using the custom timing */
+ CEC_ST_TX_PULSE_LOW_CUSTOM,
+ /* Drive CEC high for a standalone pulse using the custom timing */
+ CEC_ST_TX_PULSE_HIGH_CUSTOM,
+ /* Start low drive */
+ CEC_ST_TX_LOW_DRIVE,
/* Rx states */
@@ -66,8 +84,8 @@ enum cec_pin_state {
CEC_ST_RX_DATA_SAMPLE,
/* Wait for earliest end of bit period after sampling */
CEC_ST_RX_DATA_POST_SAMPLE,
- /* Wait for CEC to go high (i.e. end of bit period */
- CEC_ST_RX_DATA_HIGH,
+ /* Wait for CEC to go low (i.e. end of bit period) */
+ CEC_ST_RX_DATA_WAIT_FOR_LOW,
/* Drive CEC low to send 0 Ack bit */
CEC_ST_RX_ACK_LOW,
/* End of 0 Ack time, wait for earliest end of bit period */
@@ -76,9 +94,9 @@ enum cec_pin_state {
CEC_ST_RX_ACK_HIGH_POST,
/* Wait for earliest end of bit period and end of message */
CEC_ST_RX_ACK_FINISH,
-
/* Start low drive */
- CEC_ST_LOW_DRIVE,
+ CEC_ST_RX_LOW_DRIVE,
+
/* Monitor pin using interrupts */
CEC_ST_RX_IRQ,
@@ -86,7 +104,58 @@ enum cec_pin_state {
CEC_PIN_STATES
};
-#define CEC_NUM_PIN_EVENTS 128
+/* Error Injection */
+
+/* Error injection modes */
+#define CEC_ERROR_INJ_MODE_OFF 0
+#define CEC_ERROR_INJ_MODE_ONCE 1
+#define CEC_ERROR_INJ_MODE_ALWAYS 2
+#define CEC_ERROR_INJ_MODE_TOGGLE 3
+#define CEC_ERROR_INJ_MODE_MASK 3ULL
+
+/* Receive error injection options */
+#define CEC_ERROR_INJ_RX_NACK_OFFSET 0
+#define CEC_ERROR_INJ_RX_LOW_DRIVE_OFFSET 2
+#define CEC_ERROR_INJ_RX_ADD_BYTE_OFFSET 4
+#define CEC_ERROR_INJ_RX_REMOVE_BYTE_OFFSET 6
+#define CEC_ERROR_INJ_RX_ARB_LOST_OFFSET 8
+#define CEC_ERROR_INJ_RX_MASK 0xffffULL
+
+/* Transmit error injection options */
+#define CEC_ERROR_INJ_TX_NO_EOM_OFFSET 16
+#define CEC_ERROR_INJ_TX_EARLY_EOM_OFFSET 18
+#define CEC_ERROR_INJ_TX_SHORT_BIT_OFFSET 20
+#define CEC_ERROR_INJ_TX_LONG_BIT_OFFSET 22
+#define CEC_ERROR_INJ_TX_CUSTOM_BIT_OFFSET 24
+#define CEC_ERROR_INJ_TX_SHORT_START_OFFSET 26
+#define CEC_ERROR_INJ_TX_LONG_START_OFFSET 28
+#define CEC_ERROR_INJ_TX_CUSTOM_START_OFFSET 30
+#define CEC_ERROR_INJ_TX_LAST_BIT_OFFSET 32
+#define CEC_ERROR_INJ_TX_ADD_BYTES_OFFSET 34
+#define CEC_ERROR_INJ_TX_REMOVE_BYTE_OFFSET 36
+#define CEC_ERROR_INJ_TX_LOW_DRIVE_OFFSET 38
+#define CEC_ERROR_INJ_TX_MASK 0xffffffffffff0000ULL
+
+#define CEC_ERROR_INJ_RX_LOW_DRIVE_ARG_IDX 0
+#define CEC_ERROR_INJ_RX_ARB_LOST_ARG_IDX 1
+
+#define CEC_ERROR_INJ_TX_ADD_BYTES_ARG_IDX 2
+#define CEC_ERROR_INJ_TX_SHORT_BIT_ARG_IDX 3
+#define CEC_ERROR_INJ_TX_LONG_BIT_ARG_IDX 4
+#define CEC_ERROR_INJ_TX_CUSTOM_BIT_ARG_IDX 5
+#define CEC_ERROR_INJ_TX_LAST_BIT_ARG_IDX 6
+#define CEC_ERROR_INJ_TX_LOW_DRIVE_ARG_IDX 7
+#define CEC_ERROR_INJ_NUM_ARGS 8
+
+/* Special CEC op values */
+#define CEC_ERROR_INJ_OP_ANY 0x00000100
+
+/* The default for the low/high time of the custom pulse */
+#define CEC_TIM_CUSTOM_DEFAULT 1000
+
+#define CEC_NUM_PIN_EVENTS 128
+#define CEC_PIN_EVENT_FL_IS_HIGH (1 << 0)
+#define CEC_PIN_EVENT_FL_DROPPED (1 << 1)
#define CEC_PIN_IRQ_UNCHANGED 0
#define CEC_PIN_IRQ_DISABLE 1
@@ -110,24 +179,63 @@ struct cec_pin {
u32 tx_bit;
bool tx_nacked;
u32 tx_signal_free_time;
+ bool tx_toggle;
struct cec_msg rx_msg;
u32 rx_bit;
+ bool rx_toggle;
+ u32 rx_start_bit_low_too_short_cnt;
+ u64 rx_start_bit_low_too_short_ts;
+ u32 rx_start_bit_low_too_short_delta;
+ u32 rx_start_bit_too_short_cnt;
+ u64 rx_start_bit_too_short_ts;
+ u32 rx_start_bit_too_short_delta;
+ u32 rx_start_bit_too_long_cnt;
+ u32 rx_data_bit_too_short_cnt;
+ u64 rx_data_bit_too_short_ts;
+ u32 rx_data_bit_too_short_delta;
+ u32 rx_data_bit_too_long_cnt;
+ u32 rx_low_drive_cnt;
struct cec_msg work_rx_msg;
u8 work_tx_status;
ktime_t work_tx_ts;
atomic_t work_irq_change;
- atomic_t work_pin_events;
+ atomic_t work_pin_num_events;
unsigned int work_pin_events_wr;
unsigned int work_pin_events_rd;
ktime_t work_pin_ts[CEC_NUM_PIN_EVENTS];
- bool work_pin_is_high[CEC_NUM_PIN_EVENTS];
+ u8 work_pin_events[CEC_NUM_PIN_EVENTS];
+ bool work_pin_events_dropped;
+ u32 work_pin_events_dropped_cnt;
ktime_t timer_ts;
u32 timer_cnt;
u32 timer_100ms_overruns;
u32 timer_300ms_overruns;
u32 timer_max_overrun;
u32 timer_sum_overrun;
+
+ u32 tx_custom_low_usecs;
+ u32 tx_custom_high_usecs;
+ bool tx_ignore_nack_until_eom;
+ bool tx_custom_pulse;
+ bool tx_generated_poll;
+ bool tx_post_eom;
+ u8 tx_extra_bytes;
+ u32 tx_low_drive_cnt;
+#ifdef CONFIG_CEC_PIN_ERROR_INJ
+ u64 error_inj[CEC_ERROR_INJ_OP_ANY + 1];
+ u8 error_inj_args[CEC_ERROR_INJ_OP_ANY + 1][CEC_ERROR_INJ_NUM_ARGS];
+#endif
};
+void cec_pin_start_timer(struct cec_pin *pin);
+
+#ifdef CONFIG_CEC_PIN_ERROR_INJ
+bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line);
+int cec_pin_error_inj_show(struct cec_adapter *adap, struct seq_file *sf);
+
+u16 cec_pin_rx_error_inj(struct cec_pin *pin);
+u16 cec_pin_tx_error_inj(struct cec_pin *pin);
+#endif
+
#endif
diff --git a/drivers/media/cec/cec-pin.c b/drivers/media/cec/cec-pin.c
index b48dfe844118..2a5df99735fa 100644
--- a/drivers/media/cec/cec-pin.c
+++ b/drivers/media/cec/cec-pin.c
@@ -1,18 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/delay.h>
@@ -51,11 +39,29 @@
#define CEC_TIM_IDLE_SAMPLE 1000
/* when processing the start bit, sample twice per millisecond */
#define CEC_TIM_START_BIT_SAMPLE 500
-/* when polling for a state change, sample once every 50 micoseconds */
+/* when polling for a state change, sample once every 50 microseconds */
#define CEC_TIM_SAMPLE 50
#define CEC_TIM_LOW_DRIVE_ERROR (1.5 * CEC_TIM_DATA_BIT_TOTAL)
+/*
+ * Total data bit time that is too short/long for a valid bit,
+ * used for error injection.
+ */
+#define CEC_TIM_DATA_BIT_TOTAL_SHORT 1800
+#define CEC_TIM_DATA_BIT_TOTAL_LONG 2900
+
+/*
+ * Total start bit time that is too short/long for a valid bit,
+ * used for error injection.
+ */
+#define CEC_TIM_START_BIT_TOTAL_SHORT 4100
+#define CEC_TIM_START_BIT_TOTAL_LONG 5000
+
+/* Data bits are 0-7, EOM is bit 8 and ACK is bit 9 */
+#define EOM_BIT 8
+#define ACK_BIT 9
+
struct cec_state {
const char * const name;
unsigned int usecs;
@@ -68,17 +74,32 @@ static const struct cec_state states[CEC_PIN_STATES] = {
{ "Tx Wait for High", CEC_TIM_IDLE_SAMPLE },
{ "Tx Start Bit Low", CEC_TIM_START_BIT_LOW },
{ "Tx Start Bit High", CEC_TIM_START_BIT_TOTAL - CEC_TIM_START_BIT_LOW },
+ { "Tx Start Bit High Short", CEC_TIM_START_BIT_TOTAL_SHORT - CEC_TIM_START_BIT_LOW },
+ { "Tx Start Bit High Long", CEC_TIM_START_BIT_TOTAL_LONG - CEC_TIM_START_BIT_LOW },
+ { "Tx Start Bit Low Custom", 0 },
+ { "Tx Start Bit High Custom", 0 },
{ "Tx Data 0 Low", CEC_TIM_DATA_BIT_0_LOW },
{ "Tx Data 0 High", CEC_TIM_DATA_BIT_TOTAL - CEC_TIM_DATA_BIT_0_LOW },
+ { "Tx Data 0 High Short", CEC_TIM_DATA_BIT_TOTAL_SHORT - CEC_TIM_DATA_BIT_0_LOW },
+ { "Tx Data 0 High Long", CEC_TIM_DATA_BIT_TOTAL_LONG - CEC_TIM_DATA_BIT_0_LOW },
{ "Tx Data 1 Low", CEC_TIM_DATA_BIT_1_LOW },
{ "Tx Data 1 High", CEC_TIM_DATA_BIT_TOTAL - CEC_TIM_DATA_BIT_1_LOW },
- { "Tx Data 1 Pre Sample", CEC_TIM_DATA_BIT_SAMPLE - CEC_TIM_DATA_BIT_1_LOW },
- { "Tx Data 1 Post Sample", CEC_TIM_DATA_BIT_TOTAL - CEC_TIM_DATA_BIT_SAMPLE },
+ { "Tx Data 1 High Short", CEC_TIM_DATA_BIT_TOTAL_SHORT - CEC_TIM_DATA_BIT_1_LOW },
+ { "Tx Data 1 High Long", CEC_TIM_DATA_BIT_TOTAL_LONG - CEC_TIM_DATA_BIT_1_LOW },
+ { "Tx Data 1 High Pre Sample", CEC_TIM_DATA_BIT_SAMPLE - CEC_TIM_DATA_BIT_1_LOW },
+ { "Tx Data 1 High Post Sample", CEC_TIM_DATA_BIT_TOTAL - CEC_TIM_DATA_BIT_SAMPLE },
+ { "Tx Data 1 High Post Sample Short", CEC_TIM_DATA_BIT_TOTAL_SHORT - CEC_TIM_DATA_BIT_SAMPLE },
+ { "Tx Data 1 High Post Sample Long", CEC_TIM_DATA_BIT_TOTAL_LONG - CEC_TIM_DATA_BIT_SAMPLE },
+ { "Tx Data Bit Low Custom", 0 },
+ { "Tx Data Bit High Custom", 0 },
+ { "Tx Pulse Low Custom", 0 },
+ { "Tx Pulse High Custom", 0 },
+ { "Tx Low Drive", CEC_TIM_LOW_DRIVE_ERROR },
{ "Rx Start Bit Low", CEC_TIM_SAMPLE },
{ "Rx Start Bit High", CEC_TIM_SAMPLE },
{ "Rx Data Sample", CEC_TIM_DATA_BIT_SAMPLE },
{ "Rx Data Post Sample", CEC_TIM_DATA_BIT_HIGH - CEC_TIM_DATA_BIT_SAMPLE },
- { "Rx Data High", CEC_TIM_SAMPLE },
+ { "Rx Data Wait for Low", CEC_TIM_SAMPLE },
{ "Rx Ack Low", CEC_TIM_DATA_BIT_0_LOW },
{ "Rx Ack Low Post", CEC_TIM_DATA_BIT_HIGH - CEC_TIM_DATA_BIT_0_LOW },
{ "Rx Ack High Post", CEC_TIM_DATA_BIT_HIGH },
@@ -93,12 +114,21 @@ static void cec_pin_update(struct cec_pin *pin, bool v, bool force)
return;
pin->adap->cec_pin_is_high = v;
- if (atomic_read(&pin->work_pin_events) < CEC_NUM_PIN_EVENTS) {
- pin->work_pin_is_high[pin->work_pin_events_wr] = v;
+ if (atomic_read(&pin->work_pin_num_events) < CEC_NUM_PIN_EVENTS) {
+ u8 ev = v;
+
+ if (pin->work_pin_events_dropped) {
+ pin->work_pin_events_dropped = false;
+ v |= CEC_PIN_EVENT_FL_DROPPED;
+ }
+ pin->work_pin_events[pin->work_pin_events_wr] = ev;
pin->work_pin_ts[pin->work_pin_events_wr] = ktime_get();
pin->work_pin_events_wr =
(pin->work_pin_events_wr + 1) % CEC_NUM_PIN_EVENTS;
- atomic_inc(&pin->work_pin_events);
+ atomic_inc(&pin->work_pin_num_events);
+ } else {
+ pin->work_pin_events_dropped = true;
+ pin->work_pin_events_dropped_cnt++;
}
wake_up_interruptible(&pin->kthread_waitq);
}
@@ -123,6 +153,173 @@ static bool cec_pin_high(struct cec_pin *pin)
return cec_pin_read(pin);
}
+static bool rx_error_inj(struct cec_pin *pin, unsigned int mode_offset,
+ int arg_idx, u8 *arg)
+{
+#ifdef CONFIG_CEC_PIN_ERROR_INJ
+ u16 cmd = cec_pin_rx_error_inj(pin);
+ u64 e = pin->error_inj[cmd];
+ unsigned int mode = (e >> mode_offset) & CEC_ERROR_INJ_MODE_MASK;
+
+ if (arg_idx >= 0) {
+ u8 pos = pin->error_inj_args[cmd][arg_idx];
+
+ if (arg)
+ *arg = pos;
+ else if (pos != pin->rx_bit)
+ return false;
+ }
+
+ switch (mode) {
+ case CEC_ERROR_INJ_MODE_ONCE:
+ pin->error_inj[cmd] &=
+ ~(CEC_ERROR_INJ_MODE_MASK << mode_offset);
+ return true;
+ case CEC_ERROR_INJ_MODE_ALWAYS:
+ return true;
+ case CEC_ERROR_INJ_MODE_TOGGLE:
+ return pin->rx_toggle;
+ default:
+ return false;
+ }
+#else
+ return false;
+#endif
+}
+
+static bool rx_nack(struct cec_pin *pin)
+{
+ return rx_error_inj(pin, CEC_ERROR_INJ_RX_NACK_OFFSET, -1, NULL);
+}
+
+static bool rx_low_drive(struct cec_pin *pin)
+{
+ return rx_error_inj(pin, CEC_ERROR_INJ_RX_LOW_DRIVE_OFFSET,
+ CEC_ERROR_INJ_RX_LOW_DRIVE_ARG_IDX, NULL);
+}
+
+static bool rx_add_byte(struct cec_pin *pin)
+{
+ return rx_error_inj(pin, CEC_ERROR_INJ_RX_ADD_BYTE_OFFSET, -1, NULL);
+}
+
+static bool rx_remove_byte(struct cec_pin *pin)
+{
+ return rx_error_inj(pin, CEC_ERROR_INJ_RX_REMOVE_BYTE_OFFSET, -1, NULL);
+}
+
+static bool rx_arb_lost(struct cec_pin *pin, u8 *poll)
+{
+ return pin->tx_msg.len == 0 &&
+ rx_error_inj(pin, CEC_ERROR_INJ_RX_ARB_LOST_OFFSET,
+ CEC_ERROR_INJ_RX_ARB_LOST_ARG_IDX, poll);
+}
+
+static bool tx_error_inj(struct cec_pin *pin, unsigned int mode_offset,
+ int arg_idx, u8 *arg)
+{
+#ifdef CONFIG_CEC_PIN_ERROR_INJ
+ u16 cmd = cec_pin_tx_error_inj(pin);
+ u64 e = pin->error_inj[cmd];
+ unsigned int mode = (e >> mode_offset) & CEC_ERROR_INJ_MODE_MASK;
+
+ if (arg_idx >= 0) {
+ u8 pos = pin->error_inj_args[cmd][arg_idx];
+
+ if (arg)
+ *arg = pos;
+ else if (pos != pin->tx_bit)
+ return false;
+ }
+
+ switch (mode) {
+ case CEC_ERROR_INJ_MODE_ONCE:
+ pin->error_inj[cmd] &=
+ ~(CEC_ERROR_INJ_MODE_MASK << mode_offset);
+ return true;
+ case CEC_ERROR_INJ_MODE_ALWAYS:
+ return true;
+ case CEC_ERROR_INJ_MODE_TOGGLE:
+ return pin->tx_toggle;
+ default:
+ return false;
+ }
+#else
+ return false;
+#endif
+}
+
+static bool tx_no_eom(struct cec_pin *pin)
+{
+ return tx_error_inj(pin, CEC_ERROR_INJ_TX_NO_EOM_OFFSET, -1, NULL);
+}
+
+static bool tx_early_eom(struct cec_pin *pin)
+{
+ return tx_error_inj(pin, CEC_ERROR_INJ_TX_EARLY_EOM_OFFSET, -1, NULL);
+}
+
+static bool tx_short_bit(struct cec_pin *pin)
+{
+ return tx_error_inj(pin, CEC_ERROR_INJ_TX_SHORT_BIT_OFFSET,
+ CEC_ERROR_INJ_TX_SHORT_BIT_ARG_IDX, NULL);
+}
+
+static bool tx_long_bit(struct cec_pin *pin)
+{
+ return tx_error_inj(pin, CEC_ERROR_INJ_TX_LONG_BIT_OFFSET,
+ CEC_ERROR_INJ_TX_LONG_BIT_ARG_IDX, NULL);
+}
+
+static bool tx_custom_bit(struct cec_pin *pin)
+{
+ return tx_error_inj(pin, CEC_ERROR_INJ_TX_CUSTOM_BIT_OFFSET,
+ CEC_ERROR_INJ_TX_CUSTOM_BIT_ARG_IDX, NULL);
+}
+
+static bool tx_short_start(struct cec_pin *pin)
+{
+ return tx_error_inj(pin, CEC_ERROR_INJ_TX_SHORT_START_OFFSET, -1, NULL);
+}
+
+static bool tx_long_start(struct cec_pin *pin)
+{
+ return tx_error_inj(pin, CEC_ERROR_INJ_TX_LONG_START_OFFSET, -1, NULL);
+}
+
+static bool tx_custom_start(struct cec_pin *pin)
+{
+ return tx_error_inj(pin, CEC_ERROR_INJ_TX_CUSTOM_START_OFFSET,
+ -1, NULL);
+}
+
+static bool tx_last_bit(struct cec_pin *pin)
+{
+ return tx_error_inj(pin, CEC_ERROR_INJ_TX_LAST_BIT_OFFSET,
+ CEC_ERROR_INJ_TX_LAST_BIT_ARG_IDX, NULL);
+}
+
+static u8 tx_add_bytes(struct cec_pin *pin)
+{
+ u8 bytes;
+
+ if (tx_error_inj(pin, CEC_ERROR_INJ_TX_ADD_BYTES_OFFSET,
+ CEC_ERROR_INJ_TX_ADD_BYTES_ARG_IDX, &bytes))
+ return bytes;
+ return 0;
+}
+
+static bool tx_remove_byte(struct cec_pin *pin)
+{
+ return tx_error_inj(pin, CEC_ERROR_INJ_TX_REMOVE_BYTE_OFFSET, -1, NULL);
+}
+
+static bool tx_low_drive(struct cec_pin *pin)
+{
+ return tx_error_inj(pin, CEC_ERROR_INJ_TX_LOW_DRIVE_OFFSET,
+ CEC_ERROR_INJ_TX_LOW_DRIVE_ARG_IDX, NULL);
+}
+
static void cec_pin_to_idle(struct cec_pin *pin)
{
/*
@@ -132,8 +329,16 @@ static void cec_pin_to_idle(struct cec_pin *pin)
pin->rx_bit = pin->tx_bit = 0;
pin->rx_msg.len = 0;
memset(pin->rx_msg.msg, 0, sizeof(pin->rx_msg.msg));
- pin->state = CEC_ST_IDLE;
pin->ts = ns_to_ktime(0);
+ pin->tx_generated_poll = false;
+ pin->tx_post_eom = false;
+ if (pin->state >= CEC_ST_TX_WAIT &&
+ pin->state <= CEC_ST_TX_LOW_DRIVE)
+ pin->tx_toggle ^= 1;
+ if (pin->state >= CEC_ST_RX_START_BIT_LOW &&
+ pin->state <= CEC_ST_RX_LOW_DRIVE)
+ pin->rx_toggle ^= 1;
+ pin->state = CEC_ST_IDLE;
}
/*
@@ -174,42 +379,109 @@ static void cec_pin_tx_states(struct cec_pin *pin, ktime_t ts)
break;
case CEC_ST_TX_START_BIT_LOW:
- pin->state = CEC_ST_TX_START_BIT_HIGH;
+ if (tx_short_start(pin)) {
+ /*
+ * Error Injection: send an invalid (too short)
+ * start pulse.
+ */
+ pin->state = CEC_ST_TX_START_BIT_HIGH_SHORT;
+ } else if (tx_long_start(pin)) {
+ /*
+ * Error Injection: send an invalid (too long)
+ * start pulse.
+ */
+ pin->state = CEC_ST_TX_START_BIT_HIGH_LONG;
+ } else {
+ pin->state = CEC_ST_TX_START_BIT_HIGH;
+ }
+ /* Generate start bit */
+ cec_pin_high(pin);
+ break;
+
+ case CEC_ST_TX_START_BIT_LOW_CUSTOM:
+ pin->state = CEC_ST_TX_START_BIT_HIGH_CUSTOM;
/* Generate start bit */
cec_pin_high(pin);
break;
case CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE:
- /* If the read value is 1, then all is OK */
- if (!cec_pin_read(pin)) {
+ case CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_SHORT:
+ case CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_LONG:
+ if (pin->tx_nacked) {
+ cec_pin_to_idle(pin);
+ pin->tx_msg.len = 0;
+ if (pin->tx_generated_poll)
+ break;
+ pin->work_tx_ts = ts;
+ pin->work_tx_status = CEC_TX_STATUS_NACK;
+ wake_up_interruptible(&pin->kthread_waitq);
+ break;
+ }
+ /* fall through */
+ case CEC_ST_TX_DATA_BIT_0_HIGH:
+ case CEC_ST_TX_DATA_BIT_0_HIGH_SHORT:
+ case CEC_ST_TX_DATA_BIT_0_HIGH_LONG:
+ case CEC_ST_TX_DATA_BIT_1_HIGH:
+ case CEC_ST_TX_DATA_BIT_1_HIGH_SHORT:
+ case CEC_ST_TX_DATA_BIT_1_HIGH_LONG:
+ /*
+ * If the read value is 1, then all is OK, otherwise we have a
+ * low drive condition.
+ *
+ * Special case: when we generate a poll message due to an
+ * Arbitration Lost error injection, then ignore this since
+ * the pin can actually be low in that case.
+ */
+ if (!cec_pin_read(pin) && !pin->tx_generated_poll) {
/*
* It's 0, so someone detected an error and pulled the
* line low for 1.5 times the nominal bit period.
*/
pin->tx_msg.len = 0;
+ pin->state = CEC_ST_TX_WAIT_FOR_HIGH;
pin->work_tx_ts = ts;
pin->work_tx_status = CEC_TX_STATUS_LOW_DRIVE;
- pin->state = CEC_ST_TX_WAIT_FOR_HIGH;
+ pin->tx_low_drive_cnt++;
wake_up_interruptible(&pin->kthread_waitq);
break;
}
- if (pin->tx_nacked) {
+ /* fall through */
+ case CEC_ST_TX_DATA_BIT_HIGH_CUSTOM:
+ if (tx_last_bit(pin)) {
+ /* Error Injection: just stop sending after this bit */
cec_pin_to_idle(pin);
pin->tx_msg.len = 0;
+ if (pin->tx_generated_poll)
+ break;
pin->work_tx_ts = ts;
- pin->work_tx_status = CEC_TX_STATUS_NACK;
+ pin->work_tx_status = CEC_TX_STATUS_OK;
wake_up_interruptible(&pin->kthread_waitq);
break;
}
- /* fall through */
- case CEC_ST_TX_DATA_BIT_0_HIGH:
- case CEC_ST_TX_DATA_BIT_1_HIGH:
pin->tx_bit++;
/* fall through */
case CEC_ST_TX_START_BIT_HIGH:
- if (pin->tx_bit / 10 >= pin->tx_msg.len) {
+ case CEC_ST_TX_START_BIT_HIGH_SHORT:
+ case CEC_ST_TX_START_BIT_HIGH_LONG:
+ case CEC_ST_TX_START_BIT_HIGH_CUSTOM:
+ if (tx_low_drive(pin)) {
+ /* Error injection: go to low drive */
+ cec_pin_low(pin);
+ pin->state = CEC_ST_TX_LOW_DRIVE;
+ pin->tx_msg.len = 0;
+ if (pin->tx_generated_poll)
+ break;
+ pin->work_tx_ts = ts;
+ pin->work_tx_status = CEC_TX_STATUS_LOW_DRIVE;
+ pin->tx_low_drive_cnt++;
+ wake_up_interruptible(&pin->kthread_waitq);
+ break;
+ }
+ if (pin->tx_bit / 10 >= pin->tx_msg.len + pin->tx_extra_bytes) {
cec_pin_to_idle(pin);
pin->tx_msg.len = 0;
+ if (pin->tx_generated_poll)
+ break;
pin->work_tx_ts = ts;
pin->work_tx_status = CEC_TX_STATUS_OK;
wake_up_interruptible(&pin->kthread_waitq);
@@ -217,39 +489,82 @@ static void cec_pin_tx_states(struct cec_pin *pin, ktime_t ts)
}
switch (pin->tx_bit % 10) {
- default:
- v = pin->tx_msg.msg[pin->tx_bit / 10] &
- (1 << (7 - (pin->tx_bit % 10)));
+ default: {
+ /*
+ * In the CEC_ERROR_INJ_TX_ADD_BYTES case we transmit
+ * extra bytes, so pin->tx_bit / 10 can become >= 16.
+ * Generate bit values for those extra bytes instead
+ * of reading them from the transmit buffer.
+ */
+ unsigned int idx = (pin->tx_bit / 10);
+ u8 val = idx;
+
+ if (idx < pin->tx_msg.len)
+ val = pin->tx_msg.msg[idx];
+ v = val & (1 << (7 - (pin->tx_bit % 10)));
+
pin->state = v ? CEC_ST_TX_DATA_BIT_1_LOW :
- CEC_ST_TX_DATA_BIT_0_LOW;
+ CEC_ST_TX_DATA_BIT_0_LOW;
break;
- case 8:
- v = pin->tx_bit / 10 == pin->tx_msg.len - 1;
+ }
+ case EOM_BIT: {
+ unsigned int tot_len = pin->tx_msg.len +
+ pin->tx_extra_bytes;
+ unsigned int tx_byte_idx = pin->tx_bit / 10;
+
+ v = !pin->tx_post_eom && tx_byte_idx == tot_len - 1;
+ if (tot_len > 1 && tx_byte_idx == tot_len - 2 &&
+ tx_early_eom(pin)) {
+ /* Error injection: set EOM one byte early */
+ v = true;
+ pin->tx_post_eom = true;
+ } else if (v && tx_no_eom(pin)) {
+ /* Error injection: no EOM */
+ v = false;
+ }
pin->state = v ? CEC_ST_TX_DATA_BIT_1_LOW :
- CEC_ST_TX_DATA_BIT_0_LOW;
+ CEC_ST_TX_DATA_BIT_0_LOW;
break;
- case 9:
+ }
+ case ACK_BIT:
pin->state = CEC_ST_TX_DATA_BIT_1_LOW;
break;
}
+ if (tx_custom_bit(pin))
+ pin->state = CEC_ST_TX_DATA_BIT_LOW_CUSTOM;
cec_pin_low(pin);
break;
case CEC_ST_TX_DATA_BIT_0_LOW:
case CEC_ST_TX_DATA_BIT_1_LOW:
v = pin->state == CEC_ST_TX_DATA_BIT_1_LOW;
- pin->state = v ? CEC_ST_TX_DATA_BIT_1_HIGH :
- CEC_ST_TX_DATA_BIT_0_HIGH;
- is_ack_bit = pin->tx_bit % 10 == 9;
- if (v && (pin->tx_bit < 4 || is_ack_bit))
+ is_ack_bit = pin->tx_bit % 10 == ACK_BIT;
+ if (v && (pin->tx_bit < 4 || is_ack_bit)) {
pin->state = CEC_ST_TX_DATA_BIT_1_HIGH_PRE_SAMPLE;
+ } else if (!is_ack_bit && tx_short_bit(pin)) {
+ /* Error Injection: send an invalid (too short) bit */
+ pin->state = v ? CEC_ST_TX_DATA_BIT_1_HIGH_SHORT :
+ CEC_ST_TX_DATA_BIT_0_HIGH_SHORT;
+ } else if (!is_ack_bit && tx_long_bit(pin)) {
+ /* Error Injection: send an invalid (too long) bit */
+ pin->state = v ? CEC_ST_TX_DATA_BIT_1_HIGH_LONG :
+ CEC_ST_TX_DATA_BIT_0_HIGH_LONG;
+ } else {
+ pin->state = v ? CEC_ST_TX_DATA_BIT_1_HIGH :
+ CEC_ST_TX_DATA_BIT_0_HIGH;
+ }
+ cec_pin_high(pin);
+ break;
+
+ case CEC_ST_TX_DATA_BIT_LOW_CUSTOM:
+ pin->state = CEC_ST_TX_DATA_BIT_HIGH_CUSTOM;
cec_pin_high(pin);
break;
case CEC_ST_TX_DATA_BIT_1_HIGH_PRE_SAMPLE:
/* Read the CEC value at the sample time */
v = cec_pin_read(pin);
- is_ack_bit = pin->tx_bit % 10 == 9;
+ is_ack_bit = pin->tx_bit % 10 == ACK_BIT;
/*
* If v == 0 and we're within the first 4 bits
* of the initiator, then someone else started
@@ -258,7 +573,7 @@ static void cec_pin_tx_states(struct cec_pin *pin, ktime_t ts)
* transmitter has more leading 0 bits in the
* initiator).
*/
- if (!v && !is_ack_bit) {
+ if (!v && !is_ack_bit && !pin->tx_generated_poll) {
pin->tx_msg.len = 0;
pin->work_tx_ts = ts;
pin->work_tx_status = CEC_TX_STATUS_ARB_LOST;
@@ -267,18 +582,27 @@ static void cec_pin_tx_states(struct cec_pin *pin, ktime_t ts)
pin->tx_bit = 0;
memset(pin->rx_msg.msg, 0, sizeof(pin->rx_msg.msg));
pin->rx_msg.msg[0] = pin->tx_msg.msg[0];
- pin->rx_msg.msg[0] &= ~(1 << (7 - pin->rx_bit));
+ pin->rx_msg.msg[0] &= (0xff << (8 - pin->rx_bit));
pin->rx_msg.len = 0;
+ pin->ts = ktime_sub_us(ts, CEC_TIM_DATA_BIT_SAMPLE);
pin->state = CEC_ST_RX_DATA_POST_SAMPLE;
pin->rx_bit++;
break;
}
pin->state = CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE;
+ if (!is_ack_bit && tx_short_bit(pin)) {
+ /* Error Injection: send an invalid (too short) bit */
+ pin->state = CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_SHORT;
+ } else if (!is_ack_bit && tx_long_bit(pin)) {
+ /* Error Injection: send an invalid (too long) bit */
+ pin->state = CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_LONG;
+ }
if (!is_ack_bit)
break;
/* Was the message ACKed? */
ack = cec_msg_is_broadcast(&pin->tx_msg) ? v : !v;
- if (!ack) {
+ if (!ack && !pin->tx_ignore_nack_until_eom &&
+ pin->tx_bit / 10 < pin->tx_msg.len && !pin->tx_post_eom) {
/*
* Note: the CEC spec is ambiguous regarding
* what action to take when a NACK appears
@@ -295,6 +619,15 @@ static void cec_pin_tx_states(struct cec_pin *pin, ktime_t ts)
}
break;
+ case CEC_ST_TX_PULSE_LOW_CUSTOM:
+ cec_pin_high(pin);
+ pin->state = CEC_ST_TX_PULSE_HIGH_CUSTOM;
+ break;
+
+ case CEC_ST_TX_PULSE_HIGH_CUSTOM:
+ cec_pin_to_idle(pin);
+ break;
+
default:
break;
}
@@ -322,6 +655,7 @@ static void cec_pin_rx_states(struct cec_pin *pin, ktime_t ts)
bool ack;
bool bcast, for_us;
u8 dest;
+ u8 poll;
switch (pin->state) {
/* Receive states */
@@ -331,24 +665,54 @@ static void cec_pin_rx_states(struct cec_pin *pin, ktime_t ts)
break;
pin->state = CEC_ST_RX_START_BIT_HIGH;
delta = ktime_us_delta(ts, pin->ts);
- pin->ts = ts;
/* Start bit low is too short, go back to idle */
- if (delta < CEC_TIM_START_BIT_LOW_MIN -
- CEC_TIM_IDLE_SAMPLE) {
+ if (delta < CEC_TIM_START_BIT_LOW_MIN - CEC_TIM_IDLE_SAMPLE) {
+ if (!pin->rx_start_bit_low_too_short_cnt++) {
+ pin->rx_start_bit_low_too_short_ts = ktime_to_ns(pin->ts);
+ pin->rx_start_bit_low_too_short_delta = delta;
+ }
cec_pin_to_idle(pin);
+ break;
+ }
+ if (rx_arb_lost(pin, &poll)) {
+ cec_msg_init(&pin->tx_msg, poll >> 4, poll & 0xf);
+ pin->tx_generated_poll = true;
+ pin->tx_extra_bytes = 0;
+ pin->state = CEC_ST_TX_START_BIT_HIGH;
+ pin->ts = ts;
}
break;
case CEC_ST_RX_START_BIT_HIGH:
v = cec_pin_read(pin);
delta = ktime_us_delta(ts, pin->ts);
- if (v && delta > CEC_TIM_START_BIT_TOTAL_MAX -
- CEC_TIM_START_BIT_LOW_MIN) {
+ /*
+ * Unfortunately the spec does not specify when to give up
+ * and go to idle. We just pick TOTAL_LONG.
+ */
+ if (v && delta > CEC_TIM_START_BIT_TOTAL_LONG) {
+ pin->rx_start_bit_too_long_cnt++;
cec_pin_to_idle(pin);
break;
}
if (v)
break;
+ /* Start bit is too short, go back to idle */
+ if (delta < CEC_TIM_START_BIT_TOTAL_MIN - CEC_TIM_IDLE_SAMPLE) {
+ if (!pin->rx_start_bit_too_short_cnt++) {
+ pin->rx_start_bit_too_short_ts = ktime_to_ns(pin->ts);
+ pin->rx_start_bit_too_short_delta = delta;
+ }
+ cec_pin_to_idle(pin);
+ break;
+ }
+ if (rx_low_drive(pin)) {
+ /* Error injection: go to low drive */
+ cec_pin_low(pin);
+ pin->state = CEC_ST_RX_LOW_DRIVE;
+ pin->rx_low_drive_cnt++;
+ break;
+ }
pin->state = CEC_ST_RX_DATA_SAMPLE;
pin->ts = ts;
pin->rx_eom = false;
@@ -363,36 +727,55 @@ static void cec_pin_rx_states(struct cec_pin *pin, ktime_t ts)
pin->rx_msg.msg[pin->rx_bit / 10] |=
v << (7 - (pin->rx_bit % 10));
break;
- case 8:
+ case EOM_BIT:
pin->rx_eom = v;
pin->rx_msg.len = pin->rx_bit / 10 + 1;
break;
- case 9:
+ case ACK_BIT:
break;
}
pin->rx_bit++;
break;
case CEC_ST_RX_DATA_POST_SAMPLE:
- pin->state = CEC_ST_RX_DATA_HIGH;
+ pin->state = CEC_ST_RX_DATA_WAIT_FOR_LOW;
break;
- case CEC_ST_RX_DATA_HIGH:
+ case CEC_ST_RX_DATA_WAIT_FOR_LOW:
v = cec_pin_read(pin);
delta = ktime_us_delta(ts, pin->ts);
- if (v && delta > CEC_TIM_DATA_BIT_TOTAL_MAX) {
+ /*
+ * Unfortunately the spec does not specify when to give up
+ * and go to idle. We just pick TOTAL_LONG.
+ */
+ if (v && delta > CEC_TIM_DATA_BIT_TOTAL_LONG) {
+ pin->rx_data_bit_too_long_cnt++;
cec_pin_to_idle(pin);
break;
}
if (v)
break;
+
+ if (rx_low_drive(pin)) {
+ /* Error injection: go to low drive */
+ cec_pin_low(pin);
+ pin->state = CEC_ST_RX_LOW_DRIVE;
+ pin->rx_low_drive_cnt++;
+ break;
+ }
+
/*
* Go to low drive state when the total bit time is
* too short.
*/
if (delta < CEC_TIM_DATA_BIT_TOTAL_MIN) {
+ if (!pin->rx_data_bit_too_short_cnt++) {
+ pin->rx_data_bit_too_short_ts = ktime_to_ns(pin->ts);
+ pin->rx_data_bit_too_short_delta = delta;
+ }
cec_pin_low(pin);
- pin->state = CEC_ST_LOW_DRIVE;
+ pin->state = CEC_ST_RX_LOW_DRIVE;
+ pin->rx_low_drive_cnt++;
break;
}
pin->ts = ts;
@@ -408,6 +791,11 @@ static void cec_pin_rx_states(struct cec_pin *pin, ktime_t ts)
/* ACK bit value */
ack = bcast ? 1 : !for_us;
+ if (for_us && rx_nack(pin)) {
+ /* Error injection: toggle the ACK bit */
+ ack = !ack;
+ }
+
if (ack) {
/* No need to write to the bus, just wait */
pin->state = CEC_ST_RX_ACK_HIGH_POST;
@@ -434,7 +822,7 @@ static void cec_pin_rx_states(struct cec_pin *pin, ktime_t ts)
break;
}
pin->rx_bit++;
- pin->state = CEC_ST_RX_DATA_HIGH;
+ pin->state = CEC_ST_RX_DATA_WAIT_FOR_LOW;
break;
case CEC_ST_RX_ACK_FINISH:
@@ -456,6 +844,7 @@ static enum hrtimer_restart cec_pin_timer(struct hrtimer *timer)
struct cec_adapter *adap = pin->adap;
ktime_t ts;
s32 delta;
+ u32 usecs;
ts = ktime_get();
if (ktime_to_ns(pin->timer_ts)) {
@@ -503,13 +892,27 @@ static enum hrtimer_restart cec_pin_timer(struct hrtimer *timer)
/* Transmit states */
case CEC_ST_TX_WAIT_FOR_HIGH:
case CEC_ST_TX_START_BIT_LOW:
- case CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE:
- case CEC_ST_TX_DATA_BIT_0_HIGH:
- case CEC_ST_TX_DATA_BIT_1_HIGH:
case CEC_ST_TX_START_BIT_HIGH:
+ case CEC_ST_TX_START_BIT_HIGH_SHORT:
+ case CEC_ST_TX_START_BIT_HIGH_LONG:
+ case CEC_ST_TX_START_BIT_LOW_CUSTOM:
+ case CEC_ST_TX_START_BIT_HIGH_CUSTOM:
case CEC_ST_TX_DATA_BIT_0_LOW:
+ case CEC_ST_TX_DATA_BIT_0_HIGH:
+ case CEC_ST_TX_DATA_BIT_0_HIGH_SHORT:
+ case CEC_ST_TX_DATA_BIT_0_HIGH_LONG:
case CEC_ST_TX_DATA_BIT_1_LOW:
+ case CEC_ST_TX_DATA_BIT_1_HIGH:
+ case CEC_ST_TX_DATA_BIT_1_HIGH_SHORT:
+ case CEC_ST_TX_DATA_BIT_1_HIGH_LONG:
case CEC_ST_TX_DATA_BIT_1_HIGH_PRE_SAMPLE:
+ case CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE:
+ case CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_SHORT:
+ case CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE_LONG:
+ case CEC_ST_TX_DATA_BIT_LOW_CUSTOM:
+ case CEC_ST_TX_DATA_BIT_HIGH_CUSTOM:
+ case CEC_ST_TX_PULSE_LOW_CUSTOM:
+ case CEC_ST_TX_PULSE_HIGH_CUSTOM:
cec_pin_tx_states(pin, ts);
break;
@@ -518,7 +921,7 @@ static enum hrtimer_restart cec_pin_timer(struct hrtimer *timer)
case CEC_ST_RX_START_BIT_HIGH:
case CEC_ST_RX_DATA_SAMPLE:
case CEC_ST_RX_DATA_POST_SAMPLE:
- case CEC_ST_RX_DATA_HIGH:
+ case CEC_ST_RX_DATA_WAIT_FOR_LOW:
case CEC_ST_RX_ACK_LOW:
case CEC_ST_RX_ACK_LOW_POST:
case CEC_ST_RX_ACK_HIGH_POST:
@@ -545,7 +948,10 @@ static enum hrtimer_restart cec_pin_timer(struct hrtimer *timer)
if (delta / CEC_TIM_DATA_BIT_TOTAL >
pin->tx_signal_free_time) {
pin->tx_nacked = false;
- pin->state = CEC_ST_TX_START_BIT_LOW;
+ if (tx_custom_start(pin))
+ pin->state = CEC_ST_TX_START_BIT_LOW_CUSTOM;
+ else
+ pin->state = CEC_ST_TX_START_BIT_LOW;
/* Generate start bit */
cec_pin_low(pin);
break;
@@ -555,6 +961,13 @@ static enum hrtimer_restart cec_pin_timer(struct hrtimer *timer)
pin->state = CEC_ST_TX_WAIT;
break;
}
+ if (pin->tx_custom_pulse && pin->state == CEC_ST_IDLE) {
+ pin->tx_custom_pulse = false;
+ /* Generate custom pulse */
+ cec_pin_low(pin);
+ pin->state = CEC_ST_TX_PULSE_LOW_CUSTOM;
+ break;
+ }
if (pin->state != CEC_ST_IDLE || pin->ops->enable_irq == NULL ||
pin->enable_irq_failed || adap->is_configuring ||
adap->is_configured || adap->monitor_all_cnt)
@@ -565,21 +978,40 @@ static enum hrtimer_restart cec_pin_timer(struct hrtimer *timer)
wake_up_interruptible(&pin->kthread_waitq);
return HRTIMER_NORESTART;
- case CEC_ST_LOW_DRIVE:
+ case CEC_ST_TX_LOW_DRIVE:
+ case CEC_ST_RX_LOW_DRIVE:
+ cec_pin_high(pin);
cec_pin_to_idle(pin);
break;
default:
break;
}
- if (!adap->monitor_pin_cnt || states[pin->state].usecs <= 150) {
+
+ switch (pin->state) {
+ case CEC_ST_TX_START_BIT_LOW_CUSTOM:
+ case CEC_ST_TX_DATA_BIT_LOW_CUSTOM:
+ case CEC_ST_TX_PULSE_LOW_CUSTOM:
+ usecs = pin->tx_custom_low_usecs;
+ break;
+ case CEC_ST_TX_START_BIT_HIGH_CUSTOM:
+ case CEC_ST_TX_DATA_BIT_HIGH_CUSTOM:
+ case CEC_ST_TX_PULSE_HIGH_CUSTOM:
+ usecs = pin->tx_custom_high_usecs;
+ break;
+ default:
+ usecs = states[pin->state].usecs;
+ break;
+ }
+
+ if (!adap->monitor_pin_cnt || usecs <= 150) {
pin->wait_usecs = 0;
- pin->timer_ts = ktime_add_us(ts, states[pin->state].usecs);
+ pin->timer_ts = ktime_add_us(ts, usecs);
hrtimer_forward_now(timer,
- ns_to_ktime(states[pin->state].usecs * 1000));
+ ns_to_ktime(usecs * 1000));
return HRTIMER_RESTART;
}
- pin->wait_usecs = states[pin->state].usecs - 100;
+ pin->wait_usecs = usecs - 100;
pin->timer_ts = ktime_add_us(ts, 100);
hrtimer_forward_now(timer, ns_to_ktime(100000));
return HRTIMER_RESTART;
@@ -596,12 +1028,25 @@ static int cec_pin_thread_func(void *_adap)
pin->work_rx_msg.len ||
pin->work_tx_status ||
atomic_read(&pin->work_irq_change) ||
- atomic_read(&pin->work_pin_events));
+ atomic_read(&pin->work_pin_num_events));
if (pin->work_rx_msg.len) {
- cec_received_msg_ts(adap, &pin->work_rx_msg,
+ struct cec_msg *msg = &pin->work_rx_msg;
+
+ if (msg->len > 1 && msg->len < CEC_MAX_MSG_SIZE &&
+ rx_add_byte(pin)) {
+ /* Error injection: add byte to the message */
+ msg->msg[msg->len++] = 0x55;
+ }
+ if (msg->len > 2 && rx_remove_byte(pin)) {
+ /* Error injection: remove byte from message */
+ msg->len--;
+ }
+ if (msg->len > CEC_MAX_MSG_SIZE)
+ msg->len = CEC_MAX_MSG_SIZE;
+ cec_received_msg_ts(adap, msg,
ns_to_ktime(pin->work_rx_msg.rx_ts));
- pin->work_rx_msg.len = 0;
+ msg->len = 0;
}
if (pin->work_tx_status) {
unsigned int tx_status = pin->work_tx_status;
@@ -611,14 +1056,16 @@ static int cec_pin_thread_func(void *_adap)
pin->work_tx_ts);
}
- while (atomic_read(&pin->work_pin_events)) {
+ while (atomic_read(&pin->work_pin_num_events)) {
unsigned int idx = pin->work_pin_events_rd;
+ u8 v = pin->work_pin_events[idx];
cec_queue_pin_cec_event(adap,
- pin->work_pin_is_high[idx],
+ v & CEC_PIN_EVENT_FL_IS_HIGH,
+ v & CEC_PIN_EVENT_FL_DROPPED,
pin->work_pin_ts[idx]);
pin->work_pin_events_rd = (idx + 1) % CEC_NUM_PIN_EVENTS;
- atomic_dec(&pin->work_pin_events);
+ atomic_dec(&pin->work_pin_num_events);
}
switch (atomic_xchg(&pin->work_irq_change,
@@ -654,8 +1101,9 @@ static int cec_pin_adap_enable(struct cec_adapter *adap, bool enable)
pin->enabled = enable;
if (enable) {
- atomic_set(&pin->work_pin_events, 0);
+ atomic_set(&pin->work_pin_num_events, 0);
pin->work_pin_events_rd = pin->work_pin_events_wr = 0;
+ pin->work_pin_events_dropped = false;
cec_pin_read(pin);
cec_pin_to_idle(pin);
pin->tx_msg.len = 0;
@@ -692,23 +1140,37 @@ static int cec_pin_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
return 0;
}
+void cec_pin_start_timer(struct cec_pin *pin)
+{
+ if (pin->state != CEC_ST_RX_IRQ)
+ return;
+
+ atomic_set(&pin->work_irq_change, CEC_PIN_IRQ_UNCHANGED);
+ pin->ops->disable_irq(pin->adap);
+ cec_pin_high(pin);
+ cec_pin_to_idle(pin);
+ hrtimer_start(&pin->timer, ns_to_ktime(0), HRTIMER_MODE_REL);
+}
+
static int cec_pin_adap_transmit(struct cec_adapter *adap, u8 attempts,
u32 signal_free_time, struct cec_msg *msg)
{
struct cec_pin *pin = adap->pin;
pin->tx_signal_free_time = signal_free_time;
+ pin->tx_extra_bytes = 0;
pin->tx_msg = *msg;
+ if (msg->len > 1) {
+ /* Error injection: add byte to the message */
+ pin->tx_extra_bytes = tx_add_bytes(pin);
+ }
+ if (msg->len > 2 && tx_remove_byte(pin)) {
+ /* Error injection: remove byte from the message */
+ pin->tx_msg.len--;
+ }
pin->work_tx_status = 0;
pin->tx_bit = 0;
- if (pin->state == CEC_ST_RX_IRQ) {
- atomic_set(&pin->work_irq_change, CEC_PIN_IRQ_UNCHANGED);
- pin->ops->disable_irq(adap);
- cec_pin_high(pin);
- cec_pin_to_idle(pin);
- hrtimer_start(&pin->timer, ns_to_ktime(0),
- HRTIMER_MODE_REL);
- }
+ cec_pin_start_timer(pin);
return 0;
}
@@ -717,10 +1179,12 @@ static void cec_pin_adap_status(struct cec_adapter *adap,
{
struct cec_pin *pin = adap->pin;
- seq_printf(file, "state: %s\n", states[pin->state].name);
- seq_printf(file, "tx_bit: %d\n", pin->tx_bit);
- seq_printf(file, "rx_bit: %d\n", pin->rx_bit);
+ seq_printf(file, "state: %s\n", states[pin->state].name);
+ seq_printf(file, "tx_bit: %d\n", pin->tx_bit);
+ seq_printf(file, "rx_bit: %d\n", pin->rx_bit);
seq_printf(file, "cec pin: %d\n", pin->ops->read(adap));
+ seq_printf(file, "cec pin events dropped: %u\n",
+ pin->work_pin_events_dropped_cnt);
seq_printf(file, "irq failed: %d\n", pin->enable_irq_failed);
if (pin->timer_100ms_overruns) {
seq_printf(file, "timer overruns > 100ms: %u of %u\n",
@@ -732,11 +1196,45 @@ static void cec_pin_adap_status(struct cec_adapter *adap,
seq_printf(file, "avg timer overrun: %u usecs\n",
pin->timer_sum_overrun / pin->timer_100ms_overruns);
}
+ if (pin->rx_start_bit_low_too_short_cnt)
+ seq_printf(file,
+ "rx start bit low too short: %u (delta %u, ts %llu)\n",
+ pin->rx_start_bit_low_too_short_cnt,
+ pin->rx_start_bit_low_too_short_delta,
+ pin->rx_start_bit_low_too_short_ts);
+ if (pin->rx_start_bit_too_short_cnt)
+ seq_printf(file,
+ "rx start bit too short: %u (delta %u, ts %llu)\n",
+ pin->rx_start_bit_too_short_cnt,
+ pin->rx_start_bit_too_short_delta,
+ pin->rx_start_bit_too_short_ts);
+ if (pin->rx_start_bit_too_long_cnt)
+ seq_printf(file, "rx start bit too long: %u\n",
+ pin->rx_start_bit_too_long_cnt);
+ if (pin->rx_data_bit_too_short_cnt)
+ seq_printf(file,
+ "rx data bit too short: %u (delta %u, ts %llu)\n",
+ pin->rx_data_bit_too_short_cnt,
+ pin->rx_data_bit_too_short_delta,
+ pin->rx_data_bit_too_short_ts);
+ if (pin->rx_data_bit_too_long_cnt)
+ seq_printf(file, "rx data bit too long: %u\n",
+ pin->rx_data_bit_too_long_cnt);
+ seq_printf(file, "rx initiated low drive: %u\n", pin->rx_low_drive_cnt);
+ seq_printf(file, "tx detected low drive: %u\n", pin->tx_low_drive_cnt);
+ pin->work_pin_events_dropped_cnt = 0;
pin->timer_cnt = 0;
pin->timer_100ms_overruns = 0;
pin->timer_300ms_overruns = 0;
pin->timer_max_overrun = 0;
pin->timer_sum_overrun = 0;
+ pin->rx_start_bit_low_too_short_cnt = 0;
+ pin->rx_start_bit_too_short_cnt = 0;
+ pin->rx_start_bit_too_long_cnt = 0;
+ pin->rx_data_bit_too_short_cnt = 0;
+ pin->rx_data_bit_too_long_cnt = 0;
+ pin->rx_low_drive_cnt = 0;
+ pin->tx_low_drive_cnt = 0;
if (pin->ops->status)
pin->ops->status(adap, file);
}
@@ -778,6 +1276,10 @@ static const struct cec_adap_ops cec_pin_adap_ops = {
.adap_transmit = cec_pin_adap_transmit,
.adap_status = cec_pin_adap_status,
.adap_free = cec_pin_adap_free,
+#ifdef CONFIG_CEC_PIN_ERROR_INJ
+ .error_inj_parse_line = cec_pin_error_inj_parse_line,
+ .error_inj_show = cec_pin_error_inj_show,
+#endif
};
struct cec_adapter *cec_pin_allocate_adapter(const struct cec_pin_ops *pin_ops,
@@ -792,6 +1294,8 @@ struct cec_adapter *cec_pin_allocate_adapter(const struct cec_pin_ops *pin_ops,
hrtimer_init(&pin->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
pin->timer.function = cec_pin_timer;
init_waitqueue_head(&pin->kthread_waitq);
+ pin->tx_custom_low_usecs = CEC_TIM_CUSTOM_DEFAULT;
+ pin->tx_custom_high_usecs = CEC_TIM_CUSTOM_DEFAULT;
adap = cec_allocate_adapter(&cec_pin_adap_ops, priv, name,
caps | CEC_CAP_MONITOR_ALL | CEC_CAP_MONITOR_PIN,
diff --git a/drivers/media/cec/cec-priv.h b/drivers/media/cec/cec-priv.h
index daf597643af8..804e38f849c7 100644
--- a/drivers/media/cec/cec-priv.h
+++ b/drivers/media/cec/cec-priv.h
@@ -1,20 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* cec-priv.h - HDMI Consumer Electronics Control internal header
*
* Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
#ifndef _CEC_PRIV_H
diff --git a/drivers/media/common/siano/smscoreapi.c b/drivers/media/common/siano/smscoreapi.c
index c5c827e11b64..b5dcc6d1fe90 100644
--- a/drivers/media/common/siano/smscoreapi.c
+++ b/drivers/media/common/siano/smscoreapi.c
@@ -631,7 +631,8 @@ smscore_buffer_t *smscore_createbuffer(u8 *buffer, void *common_buffer,
cb->p = buffer;
cb->offset_in_common = buffer - (u8 *) common_buffer;
- cb->phys = common_buffer_phys + cb->offset_in_common;
+ if (common_buffer_phys)
+ cb->phys = common_buffer_phys + cb->offset_in_common;
return cb;
}
@@ -690,17 +691,21 @@ int smscore_register_device(struct smsdevice_params_t *params,
/* alloc common buffer */
dev->common_buffer_size = params->buffer_size * params->num_buffers;
- dev->common_buffer = dma_alloc_coherent(NULL, dev->common_buffer_size,
- &dev->common_buffer_phys,
- GFP_KERNEL | GFP_DMA);
- if (!dev->common_buffer) {
+ if (params->usb_device)
+ buffer = kzalloc(dev->common_buffer_size, GFP_KERNEL);
+ else
+ buffer = dma_alloc_coherent(params->device,
+ dev->common_buffer_size,
+ &dev->common_buffer_phys,
+ GFP_KERNEL | GFP_DMA);
+ if (!buffer) {
smscore_unregister_device(dev);
return -ENOMEM;
}
+ dev->common_buffer = buffer;
/* prepare dma buffers */
- for (buffer = dev->common_buffer;
- dev->num_buffers < params->num_buffers;
+ for (; dev->num_buffers < params->num_buffers;
dev->num_buffers++, buffer += params->buffer_size) {
struct smscore_buffer_t *cb;
@@ -720,6 +725,7 @@ int smscore_register_device(struct smsdevice_params_t *params,
dev->board_id = SMS_BOARD_UNKNOWN;
dev->context = params->context;
dev->device = params->device;
+ dev->usb_device = params->usb_device;
dev->setmode_handler = params->setmode_handler;
dev->detectmode_handler = params->detectmode_handler;
dev->sendrequest_handler = params->sendrequest_handler;
@@ -1231,10 +1237,15 @@ void smscore_unregister_device(struct smscore_device_t *coredev)
pr_debug("freed %d buffers\n", num_buffers);
- if (coredev->common_buffer)
- dma_free_coherent(NULL, coredev->common_buffer_size,
- coredev->common_buffer, coredev->common_buffer_phys);
-
+ if (coredev->common_buffer) {
+ if (coredev->usb_device)
+ kfree(coredev->common_buffer);
+ else
+ dma_free_coherent(coredev->device,
+ coredev->common_buffer_size,
+ coredev->common_buffer,
+ coredev->common_buffer_phys);
+ }
kfree(coredev->fw_buf);
list_del(&coredev->entry);
diff --git a/drivers/media/common/siano/smscoreapi.h b/drivers/media/common/siano/smscoreapi.h
index 4cc39e4a8318..134c69f7ea7b 100644
--- a/drivers/media/common/siano/smscoreapi.h
+++ b/drivers/media/common/siano/smscoreapi.h
@@ -134,6 +134,7 @@ struct smscore_buffer_t {
struct smsdevice_params_t {
struct device *device;
+ struct usb_device *usb_device;
int buffer_size;
int num_buffers;
@@ -176,6 +177,7 @@ struct smscore_device_t {
void *context;
struct device *device;
+ struct usb_device *usb_device;
char devpath[32];
unsigned long device_flags;
diff --git a/drivers/media/common/v4l2-tpg/v4l2-tpg-colors.c b/drivers/media/common/v4l2-tpg/v4l2-tpg-colors.c
index 43180204fab2..3a3dc23c560c 100644
--- a/drivers/media/common/v4l2-tpg/v4l2-tpg-colors.c
+++ b/drivers/media/common/v4l2-tpg/v4l2-tpg-colors.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* v4l2-tpg-colors.c - A table that converts colors to various colorspaces
*
@@ -20,19 +21,6 @@
* in order to preserve precision.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/videodev2.h>
diff --git a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c
index 2b3d4ac4dfd4..9b64f4f354bf 100644
--- a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c
+++ b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* v4l2-tpg-core.c - Test Pattern Generator
*
@@ -5,19 +6,6 @@
* vivi.c source for the copyright information of those functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/module.h>
@@ -1155,13 +1143,13 @@ static void gen_twopix(struct tpg_data *tpg,
case V4L2_PIX_FMT_NV24:
buf[0][offset] = r_y_h;
buf[1][2 * offset] = g_u_s;
- buf[1][2 * offset + 1] = b_v;
+ buf[1][(2 * offset + 1) % 8] = b_v;
break;
case V4L2_PIX_FMT_NV42:
buf[0][offset] = r_y_h;
buf[1][2 * offset] = b_v;
- buf[1][2 * offset + 1] = g_u_s;
+ buf[1][(2 * offset + 1) % 8] = g_u_s;
break;
case V4L2_PIX_FMT_YUYV:
diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c
index debe35fc66b4..d3f7bb33a54d 100644
--- a/drivers/media/common/videobuf2/videobuf2-core.c
+++ b/drivers/media/common/videobuf2/videobuf2-core.c
@@ -1696,6 +1696,15 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
for (i = 0; i < q->num_buffers; ++i) {
struct vb2_buffer *vb = q->bufs[i];
+ if (vb->state == VB2_BUF_STATE_PREPARED ||
+ vb->state == VB2_BUF_STATE_QUEUED) {
+ unsigned int plane;
+
+ for (plane = 0; plane < vb->num_planes; ++plane)
+ call_void_memop(vb, finish,
+ vb->planes[plane].mem_priv);
+ }
+
if (vb->state != VB2_BUF_STATE_DEQUEUED) {
vb->state = VB2_BUF_STATE_PREPARED;
call_void_vb_qop(vb, buf_finish, vb);
diff --git a/drivers/media/common/videobuf2/videobuf2-vmalloc.c b/drivers/media/common/videobuf2/videobuf2-vmalloc.c
index 3a7c80cd1a17..359fb9804d16 100644
--- a/drivers/media/common/videobuf2/videobuf2-vmalloc.c
+++ b/drivers/media/common/videobuf2/videobuf2-vmalloc.c
@@ -106,7 +106,7 @@ static void *vb2_vmalloc_get_userptr(struct device *dev, unsigned long vaddr,
if (nums[i-1] + 1 != nums[i])
goto fail_map;
buf->vaddr = (__force void *)
- ioremap_nocache(nums[0] << PAGE_SHIFT, size);
+ ioremap_nocache(__pfn_to_phys(nums[0]), size + offset);
} else {
buf->vaddr = vm_map_ram(frame_vector_pages(vec), n_pages, -1,
PAGE_KERNEL);
diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c
index 204d0f6c678d..97365a863519 100644
--- a/drivers/media/dvb-core/dvb_ca_en50221.c
+++ b/drivers/media/dvb-core/dvb_ca_en50221.c
@@ -1254,8 +1254,8 @@ static void dvb_ca_en50221_thread_state_machine(struct dvb_ca_private *ca,
ca->pub->slot_ts_enable(ca->pub, slot);
sl->slot_state = DVB_CA_SLOTSTATE_RUNNING;
dvb_ca_en50221_thread_update_delay(ca);
- pr_err("dvb_ca adapter %d: DVB CAM detected and initialised successfully\n",
- ca->dvbdev->adapter->num);
+ pr_info("dvb_ca adapter %d: DVB CAM detected and initialised successfully\n",
+ ca->dvbdev->adapter->num);
break;
case DVB_CA_SLOTSTATE_RUNNING:
diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c
index a7ed16e0841d..e33414975065 100644
--- a/drivers/media/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb-core/dvb_frontend.c
@@ -2089,7 +2089,7 @@ static int dvb_frontend_handle_compat_ioctl(struct file *file, unsigned int cmd,
}
for (i = 0; i < tvps->num; i++) {
err = dtv_property_process_get(
- fe, &getp, (struct dtv_property *)tvp + i, file);
+ fe, &getp, (struct dtv_property *)(tvp + i), file);
if (err < 0) {
kfree(tvp);
return err;
@@ -2294,7 +2294,7 @@ static int dvb_frontend_handle_ioctl(struct file *file,
if (!tvps->num || (tvps->num > DTV_IOCTL_MAX_MSGS))
return -EINVAL;
- tvp = memdup_user(tvps->props, tvps->num * sizeof(*tvp));
+ tvp = memdup_user((void __user *)tvps->props, tvps->num * sizeof(*tvp));
if (IS_ERR(tvp))
return PTR_ERR(tvp);
@@ -2328,7 +2328,7 @@ static int dvb_frontend_handle_ioctl(struct file *file,
if (!tvps->num || (tvps->num > DTV_IOCTL_MAX_MSGS))
return -EINVAL;
- tvp = memdup_user(tvps->props, tvps->num * sizeof(*tvp));
+ tvp = memdup_user((void __user *)tvps->props, tvps->num * sizeof(*tvp));
if (IS_ERR(tvp))
return PTR_ERR(tvp);
diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c
index 60e9c2ba26be..787fe06df217 100644
--- a/drivers/media/dvb-core/dvbdev.c
+++ b/drivers/media/dvb-core/dvbdev.c
@@ -24,6 +24,7 @@
#include <linux/string.h>
#include <linux/module.h>
#include <linux/kernel.h>
+#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/device.h>
@@ -941,6 +942,55 @@ out:
return err;
}
+#if IS_ENABLED(CONFIG_I2C)
+struct i2c_client *dvb_module_probe(const char *module_name,
+ const char *name,
+ struct i2c_adapter *adap,
+ unsigned char addr,
+ void *platform_data)
+{
+ struct i2c_client *client;
+ struct i2c_board_info *board_info;
+
+ board_info = kzalloc(sizeof(*board_info), GFP_KERNEL);
+ if (!board_info)
+ return NULL;
+
+ if (name)
+ strlcpy(board_info->type, name, I2C_NAME_SIZE);
+ else
+ strlcpy(board_info->type, module_name, I2C_NAME_SIZE);
+
+ board_info->addr = addr;
+ board_info->platform_data = platform_data;
+ request_module(module_name);
+ client = i2c_new_device(adap, board_info);
+ if (client == NULL || client->dev.driver == NULL) {
+ kfree(board_info);
+ return NULL;
+ }
+
+ if (!try_module_get(client->dev.driver->owner)) {
+ i2c_unregister_device(client);
+ client = NULL;
+ }
+
+ kfree(board_info);
+ return client;
+}
+EXPORT_SYMBOL_GPL(dvb_module_probe);
+
+void dvb_module_release(struct i2c_client *client)
+{
+ if (!client)
+ return;
+
+ module_put(client->dev.driver->owner);
+ i2c_unregister_device(client);
+}
+EXPORT_SYMBOL_GPL(dvb_module_release);
+#endif
+
static int dvb_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct dvb_device *dvbdev = dev_get_drvdata(dev);
diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
index d17722eb4456..0712069fd9fe 100644
--- a/drivers/media/dvb-frontends/Kconfig
+++ b/drivers/media/dvb-frontends/Kconfig
@@ -462,7 +462,7 @@ config DVB_TDA10048
config DVB_AF9013
tristate "Afatech AF9013 demodulator"
- depends on DVB_CORE && I2C
+ depends on DVB_CORE && I2C && I2C_MUX
select REGMAP
default m if !MEDIA_SUBDRV_AUTOSELECT
help
@@ -546,6 +546,8 @@ config DVB_GP8PSK_FE
depends on DVB_CORE
default DVB_USB_GP8PSK
+source "drivers/media/dvb-frontends/cxd2880/Kconfig"
+
comment "DVB-C (cable) frontends"
depends on DVB_CORE
@@ -822,13 +824,6 @@ config DVB_A8293
depends on DVB_CORE && I2C
default m if !MEDIA_SUBDRV_AUTOSELECT
-config DVB_SP2
- tristate "CIMaX SP2"
- depends on DVB_CORE && I2C
- default m if !MEDIA_SUBDRV_AUTOSELECT
- help
- CIMaX SP2/SP2HF Common Interface module.
-
config DVB_LGS8GL5
tristate "Silicon Legend LGS-8GL5 demodulator (OFDM)"
depends on DVB_CORE && I2C
@@ -904,6 +899,27 @@ config DVB_HELENE
help
Say Y when you want to support this frontend.
+comment "Common Interface (EN50221) controller drivers"
+ depends on DVB_CORE
+
+config DVB_CXD2099
+ tristate "CXD2099AR Common Interface driver"
+ depends on DVB_CORE && I2C
+ select REGMAP_I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A driver for the CI controller currently found mostly on
+ Digital Devices DuoFlex CI (single) addon modules.
+
+ Say Y when you want to support these devices.
+
+config DVB_SP2
+ tristate "CIMaX SP2"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ CIMaX SP2/SP2HF Common Interface module.
+
comment "Tools to develop new frontends"
config DVB_DUMMY_FE
diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile
index 4be59fed4536..67a783fd5ed0 100644
--- a/drivers/media/dvb-frontends/Makefile
+++ b/drivers/media/dvb-frontends/Makefile
@@ -129,3 +129,5 @@ obj-$(CONFIG_DVB_HORUS3A) += horus3a.o
obj-$(CONFIG_DVB_ASCOT2E) += ascot2e.o
obj-$(CONFIG_DVB_HELENE) += helene.o
obj-$(CONFIG_DVB_ZD1301_DEMOD) += zd1301_demod.o
+obj-$(CONFIG_DVB_CXD2099) += cxd2099.o
+obj-$(CONFIG_DVB_CXD2880) += cxd2880/
diff --git a/drivers/media/dvb-frontends/af9013.c b/drivers/media/dvb-frontends/af9013.c
index b8f3ebfc3e27..482bce49819a 100644
--- a/drivers/media/dvb-frontends/af9013.c
+++ b/drivers/media/dvb-frontends/af9013.c
@@ -23,6 +23,7 @@
struct af9013_state {
struct i2c_client *client;
struct regmap *regmap;
+ struct i2c_mux_core *muxc;
struct dvb_frontend fe;
u32 clk;
u8 tuner;
@@ -33,20 +34,20 @@ struct af9013_state {
u8 api_version[4];
u8 gpio[4];
- /* tuner/demod RF and IF AGC limits used for signal strength calc */
- u8 signal_strength_en, rf_50, rf_80, if_50, if_80;
- u16 signal_strength;
- u32 ber;
- u32 ucblocks;
- u16 snr;
u32 bandwidth_hz;
enum fe_status fe_status;
+ /* RF and IF AGC limits used for signal strength calc */
+ u8 strength_en, rf_agc_50, rf_agc_80, if_agc_50, if_agc_80;
unsigned long set_frontend_jiffies;
unsigned long read_status_jiffies;
+ unsigned long strength_jiffies;
+ unsigned long cnr_jiffies;
+ unsigned long ber_ucb_jiffies;
+ u16 dvbv3_snr;
+ u16 dvbv3_strength;
+ u32 dvbv3_ber;
+ u32 dvbv3_ucblocks;
bool first_tune;
- bool i2c_gate_state;
- unsigned int statistics_step:3;
- struct delayed_work statistics_work;
};
static int af9013_set_gpio(struct af9013_state *state, u8 gpio, u8 gpioval)
@@ -101,232 +102,6 @@ err:
return ret;
}
-static int af9013_statistics_ber_unc_start(struct dvb_frontend *fe)
-{
- struct af9013_state *state = fe->demodulator_priv;
- struct i2c_client *client = state->client;
- int ret;
-
- dev_dbg(&client->dev, "\n");
-
- /* reset and start BER counter */
- ret = regmap_update_bits(state->regmap, 0xd391, 0x10, 0x10);
- if (ret)
- goto err;
-
- return 0;
-err:
- dev_dbg(&client->dev, "failed %d\n", ret);
- return ret;
-}
-
-static int af9013_statistics_ber_unc_result(struct dvb_frontend *fe)
-{
- struct af9013_state *state = fe->demodulator_priv;
- struct i2c_client *client = state->client;
- int ret;
- unsigned int utmp;
- u8 buf[5];
-
- dev_dbg(&client->dev, "\n");
-
- /* check if error bit count is ready */
- ret = regmap_read(state->regmap, 0xd391, &utmp);
- if (ret)
- goto err;
-
- if (!((utmp >> 4) & 0x01)) {
- dev_dbg(&client->dev, "not ready\n");
- return 0;
- }
-
- ret = regmap_bulk_read(state->regmap, 0xd387, buf, 5);
- if (ret)
- goto err;
-
- state->ber = (buf[2] << 16) | (buf[1] << 8) | buf[0];
- state->ucblocks += (buf[4] << 8) | buf[3];
-
- return 0;
-err:
- dev_dbg(&client->dev, "failed %d\n", ret);
- return ret;
-}
-
-static int af9013_statistics_snr_start(struct dvb_frontend *fe)
-{
- struct af9013_state *state = fe->demodulator_priv;
- struct i2c_client *client = state->client;
- int ret;
-
- dev_dbg(&client->dev, "\n");
-
- /* start SNR meas */
- ret = regmap_update_bits(state->regmap, 0xd2e1, 0x08, 0x08);
- if (ret)
- goto err;
-
- return 0;
-err:
- dev_dbg(&client->dev, "failed %d\n", ret);
- return ret;
-}
-
-static int af9013_statistics_snr_result(struct dvb_frontend *fe)
-{
- struct af9013_state *state = fe->demodulator_priv;
- struct i2c_client *client = state->client;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- int ret, i, len;
- unsigned int utmp;
- u8 buf[3];
- u32 snr_val;
- const struct af9013_snr *uninitialized_var(snr_lut);
-
- dev_dbg(&client->dev, "\n");
-
- /* check if SNR ready */
- ret = regmap_read(state->regmap, 0xd2e1, &utmp);
- if (ret)
- goto err;
-
- if (!((utmp >> 3) & 0x01)) {
- dev_dbg(&client->dev, "not ready\n");
- return 0;
- }
-
- /* read value */
- ret = regmap_bulk_read(state->regmap, 0xd2e3, buf, 3);
- if (ret)
- goto err;
-
- snr_val = (buf[2] << 16) | (buf[1] << 8) | buf[0];
-
- /* read current modulation */
- ret = regmap_read(state->regmap, 0xd3c1, &utmp);
- if (ret)
- goto err;
-
- switch ((utmp >> 6) & 3) {
- case 0:
- len = ARRAY_SIZE(qpsk_snr_lut);
- snr_lut = qpsk_snr_lut;
- break;
- case 1:
- len = ARRAY_SIZE(qam16_snr_lut);
- snr_lut = qam16_snr_lut;
- break;
- case 2:
- len = ARRAY_SIZE(qam64_snr_lut);
- snr_lut = qam64_snr_lut;
- break;
- default:
- goto err;
- }
-
- for (i = 0; i < len; i++) {
- utmp = snr_lut[i].snr;
-
- if (snr_val < snr_lut[i].val)
- break;
- }
- state->snr = utmp * 10; /* dB/10 */
-
- c->cnr.stat[0].svalue = 1000 * utmp;
- c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
-
- return 0;
-err:
- dev_dbg(&client->dev, "failed %d\n", ret);
- return ret;
-}
-
-static int af9013_statistics_signal_strength(struct dvb_frontend *fe)
-{
- struct af9013_state *state = fe->demodulator_priv;
- struct i2c_client *client = state->client;
- int ret = 0;
- u8 buf[2], rf_gain, if_gain;
- int signal_strength;
-
- dev_dbg(&client->dev, "\n");
-
- if (!state->signal_strength_en)
- return 0;
-
- ret = regmap_bulk_read(state->regmap, 0xd07c, buf, 2);
- if (ret)
- goto err;
-
- rf_gain = buf[0];
- if_gain = buf[1];
-
- signal_strength = (0xffff / \
- (9 * (state->rf_50 + state->if_50) - \
- 11 * (state->rf_80 + state->if_80))) * \
- (10 * (rf_gain + if_gain) - \
- 11 * (state->rf_80 + state->if_80));
- if (signal_strength < 0)
- signal_strength = 0;
- else if (signal_strength > 0xffff)
- signal_strength = 0xffff;
-
- state->signal_strength = signal_strength;
-
- return 0;
-err:
- dev_dbg(&client->dev, "failed %d\n", ret);
- return ret;
-}
-
-static void af9013_statistics_work(struct work_struct *work)
-{
- struct af9013_state *state = container_of(work,
- struct af9013_state, statistics_work.work);
- unsigned int next_msec;
-
- /* update only signal strength when demod is not locked */
- if (!(state->fe_status & FE_HAS_LOCK)) {
- state->statistics_step = 0;
- state->ber = 0;
- state->snr = 0;
- }
-
- switch (state->statistics_step) {
- default:
- state->statistics_step = 0;
- /* fall-through */
- case 0:
- af9013_statistics_signal_strength(&state->fe);
- state->statistics_step++;
- next_msec = 300;
- break;
- case 1:
- af9013_statistics_snr_start(&state->fe);
- state->statistics_step++;
- next_msec = 200;
- break;
- case 2:
- af9013_statistics_ber_unc_start(&state->fe);
- state->statistics_step++;
- next_msec = 1000;
- break;
- case 3:
- af9013_statistics_snr_result(&state->fe);
- state->statistics_step++;
- next_msec = 400;
- break;
- case 4:
- af9013_statistics_ber_unc_result(&state->fe);
- state->statistics_step++;
- next_msec = 100;
- break;
- }
-
- schedule_delayed_work(&state->statistics_work,
- msecs_to_jiffies(next_msec));
-}
-
static int af9013_get_tune_settings(struct dvb_frontend *fe,
struct dvb_frontend_tune_settings *fesettings)
{
@@ -751,46 +526,273 @@ static int af9013_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
struct af9013_state *state = fe->demodulator_priv;
struct i2c_client *client = state->client;
- int ret;
- unsigned int utmp;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, stmp1;
+ unsigned int utmp, utmp1, utmp2, utmp3, utmp4;
+ u8 buf[7];
+
+ dev_dbg(&client->dev, "\n");
/*
* Return status from the cache if it is younger than 2000ms with the
* exception of last tune is done during 4000ms.
*/
- if (time_is_after_jiffies(
- state->read_status_jiffies + msecs_to_jiffies(2000)) &&
- time_is_before_jiffies(
- state->set_frontend_jiffies + msecs_to_jiffies(4000))
- ) {
- *status = state->fe_status;
- return 0;
+ if (time_is_after_jiffies(state->read_status_jiffies + msecs_to_jiffies(2000)) &&
+ time_is_before_jiffies(state->set_frontend_jiffies + msecs_to_jiffies(4000))) {
+ *status = state->fe_status;
} else {
- *status = 0;
+ /* MPEG2 lock */
+ ret = regmap_read(state->regmap, 0xd507, &utmp);
+ if (ret)
+ goto err;
+
+ if ((utmp >> 6) & 0x01) {
+ utmp1 = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ } else {
+ /* TPS lock */
+ ret = regmap_read(state->regmap, 0xd330, &utmp);
+ if (ret)
+ goto err;
+
+ if ((utmp >> 3) & 0x01)
+ utmp1 = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI;
+ else
+ utmp1 = 0;
+ }
+
+ dev_dbg(&client->dev, "fe_status %02x\n", utmp1);
+
+ state->read_status_jiffies = jiffies;
+
+ state->fe_status = utmp1;
+ *status = utmp1;
}
- /* MPEG2 lock */
- ret = regmap_read(state->regmap, 0xd507, &utmp);
- if (ret)
- goto err;
+ /* Signal strength */
+ switch (state->strength_en) {
+ case 0:
+ /* Check if we support signal strength */
+ ret = regmap_read(state->regmap, 0x9bee, &utmp);
+ if (ret)
+ goto err;
- if ((utmp >> 6) & 0x01)
- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
- FE_HAS_SYNC | FE_HAS_LOCK;
+ if ((utmp >> 0) & 0x01) {
+ /* Read agc values for signal strength estimation */
+ ret = regmap_read(state->regmap, 0x9bbd, &utmp1);
+ if (ret)
+ goto err;
+ ret = regmap_read(state->regmap, 0x9bd0, &utmp2);
+ if (ret)
+ goto err;
+ ret = regmap_read(state->regmap, 0x9be2, &utmp3);
+ if (ret)
+ goto err;
+ ret = regmap_read(state->regmap, 0x9be4, &utmp4);
+ if (ret)
+ goto err;
- if (!*status) {
- /* TPS lock */
- ret = regmap_read(state->regmap, 0xd330, &utmp);
+ state->rf_agc_50 = utmp1;
+ state->rf_agc_80 = utmp2;
+ state->if_agc_50 = utmp3;
+ state->if_agc_80 = utmp4;
+ dev_dbg(&client->dev,
+ "rf_agc_50 %u, rf_agc_80 %u, if_agc_50 %u, if_agc_80 %u\n",
+ utmp1, utmp2, utmp3, utmp4);
+
+ state->strength_en = 1;
+ } else {
+ /* Signal strength is not supported */
+ state->strength_en = 2;
+ break;
+ }
+ /* Fall through */
+ case 1:
+ if (time_is_after_jiffies(state->strength_jiffies + msecs_to_jiffies(2000)))
+ break;
+
+ /* Read value */
+ ret = regmap_bulk_read(state->regmap, 0xd07c, buf, 2);
if (ret)
goto err;
- if ((utmp >> 3) & 0x01)
- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
- FE_HAS_VITERBI;
+ /*
+ * Construct line equation from tuner dependent -80/-50 dBm agc
+ * limits and use it to map current agc value to dBm estimate
+ */
+ #define agc_gain (buf[0] + buf[1])
+ #define agc_gain_50dbm (state->rf_agc_50 + state->if_agc_50)
+ #define agc_gain_80dbm (state->rf_agc_80 + state->if_agc_80)
+ stmp1 = 30000 * (agc_gain - agc_gain_80dbm) /
+ (agc_gain_50dbm - agc_gain_80dbm) - 80000;
+
+ dev_dbg(&client->dev,
+ "strength %d, agc_gain %d, agc_gain_50dbm %d, agc_gain_80dbm %d\n",
+ stmp1, agc_gain, agc_gain_50dbm, agc_gain_80dbm);
+
+ state->strength_jiffies = jiffies;
+ /* Convert [-90, -30] dBm to [0x0000, 0xffff] for dvbv3 */
+ utmp1 = clamp(stmp1 + 90000, 0, 60000);
+ state->dvbv3_strength = div_u64((u64)utmp1 * 0xffff, 60000);
+
+ c->strength.stat[0].scale = FE_SCALE_DECIBEL;
+ c->strength.stat[0].svalue = stmp1;
+ break;
+ default:
+ c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ break;
}
- state->fe_status = *status;
- state->read_status_jiffies = jiffies;
+ /* CNR */
+ switch (state->fe_status & FE_HAS_VITERBI) {
+ case FE_HAS_VITERBI:
+ if (time_is_after_jiffies(state->cnr_jiffies + msecs_to_jiffies(2000)))
+ break;
+
+ /* Check if cnr ready */
+ ret = regmap_read(state->regmap, 0xd2e1, &utmp);
+ if (ret)
+ goto err;
+
+ if (!((utmp >> 3) & 0x01)) {
+ dev_dbg(&client->dev, "cnr not ready\n");
+ break;
+ }
+
+ /* Read value */
+ ret = regmap_bulk_read(state->regmap, 0xd2e3, buf, 3);
+ if (ret)
+ goto err;
+
+ utmp1 = buf[2] << 16 | buf[1] << 8 | buf[0] << 0;
+
+ /* Read current modulation */
+ ret = regmap_read(state->regmap, 0xd3c1, &utmp);
+ if (ret)
+ goto err;
+
+ switch ((utmp >> 6) & 3) {
+ case 0:
+ /*
+ * QPSK
+ * CNR[dB] 13 * -log10((1690000 - value) / value) + 2.6
+ * value [653799, 1689999], 2.6 / 13 = 3355443
+ */
+ utmp1 = clamp(utmp1, 653799U, 1689999U);
+ utmp1 = ((u64)(intlog10(utmp1)
+ - intlog10(1690000 - utmp1)
+ + 3355443) * 13 * 1000) >> 24;
+ break;
+ case 1:
+ /*
+ * QAM-16
+ * CNR[dB] 6 * log10((value - 370000) / (828000 - value)) + 15.7
+ * value [371105, 827999], 15.7 / 6 = 43900382
+ */
+ utmp1 = clamp(utmp1, 371105U, 827999U);
+ utmp1 = ((u64)(intlog10(utmp1 - 370000)
+ - intlog10(828000 - utmp1)
+ + 43900382) * 6 * 1000) >> 24;
+ break;
+ case 2:
+ /*
+ * QAM-64
+ * CNR[dB] 8 * log10((value - 193000) / (425000 - value)) + 23.8
+ * value [193246, 424999], 23.8 / 8 = 49912218
+ */
+ utmp1 = clamp(utmp1, 193246U, 424999U);
+ utmp1 = ((u64)(intlog10(utmp1 - 193000)
+ - intlog10(425000 - utmp1)
+ + 49912218) * 8 * 1000) >> 24;
+ break;
+ default:
+ dev_dbg(&client->dev, "invalid modulation %u\n",
+ (utmp >> 6) & 3);
+ utmp1 = 0;
+ break;
+ }
+
+ dev_dbg(&client->dev, "cnr %u\n", utmp1);
+
+ state->cnr_jiffies = jiffies;
+ state->dvbv3_snr = utmp1 / 100;
+
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ c->cnr.stat[0].svalue = utmp1;
+ break;
+ default:
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ break;
+ }
+
+ /* BER / PER */
+ switch (state->fe_status & FE_HAS_SYNC) {
+ case FE_HAS_SYNC:
+ if (time_is_after_jiffies(state->ber_ucb_jiffies + msecs_to_jiffies(2000)))
+ break;
+
+ /* Check if ber / ucb is ready */
+ ret = regmap_read(state->regmap, 0xd391, &utmp);
+ if (ret)
+ goto err;
+
+ if (!((utmp >> 4) & 0x01)) {
+ dev_dbg(&client->dev, "ber not ready\n");
+ break;
+ }
+
+ /* Read value */
+ ret = regmap_bulk_read(state->regmap, 0xd385, buf, 7);
+ if (ret)
+ goto err;
+
+ utmp1 = buf[4] << 16 | buf[3] << 8 | buf[2] << 0;
+ utmp2 = (buf[1] << 8 | buf[0] << 0) * 204 * 8;
+ utmp3 = buf[6] << 8 | buf[5] << 0;
+ utmp4 = buf[1] << 8 | buf[0] << 0;
+
+ /* Use 10000 TS packets for measure */
+ if (utmp4 != 10000) {
+ buf[0] = (10000 >> 0) & 0xff;
+ buf[1] = (10000 >> 8) & 0xff;
+ ret = regmap_bulk_write(state->regmap, 0xd385, buf, 2);
+ if (ret)
+ goto err;
+ }
+
+ /* Reset ber / ucb counter */
+ ret = regmap_update_bits(state->regmap, 0xd391, 0x20, 0x20);
+ if (ret)
+ goto err;
+
+ dev_dbg(&client->dev, "post_bit_error %u, post_bit_count %u\n",
+ utmp1, utmp2);
+ dev_dbg(&client->dev, "block_error %u, block_count %u\n",
+ utmp3, utmp4);
+
+ state->ber_ucb_jiffies = jiffies;
+ state->dvbv3_ber = utmp1;
+ state->dvbv3_ucblocks += utmp3;
+
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue += utmp1;
+ c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[0].uvalue += utmp2;
+
+ c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_error.stat[0].uvalue += utmp3;
+ c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_count.stat[0].uvalue += utmp4;
+ break;
+ default:
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ break;
+ }
return 0;
err:
@@ -801,28 +803,36 @@ err:
static int af9013_read_snr(struct dvb_frontend *fe, u16 *snr)
{
struct af9013_state *state = fe->demodulator_priv;
- *snr = state->snr;
+
+ *snr = state->dvbv3_snr;
+
return 0;
}
static int af9013_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
{
struct af9013_state *state = fe->demodulator_priv;
- *strength = state->signal_strength;
+
+ *strength = state->dvbv3_strength;
+
return 0;
}
static int af9013_read_ber(struct dvb_frontend *fe, u32 *ber)
{
struct af9013_state *state = fe->demodulator_priv;
- *ber = state->ber;
+
+ *ber = state->dvbv3_ber;
+
return 0;
}
static int af9013_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
{
struct af9013_state *state = fe->demodulator_priv;
- *ucblocks = state->ucblocks;
+
+ *ucblocks = state->dvbv3_ucblocks;
+
return 0;
}
@@ -833,7 +843,7 @@ static int af9013_init(struct dvb_frontend *fe)
int ret, i, len;
unsigned int utmp;
u8 buf[3];
- const struct af9013_reg_bit *init;
+ const struct af9013_reg_mask_val *tab;
dev_dbg(&client->dev, "\n");
@@ -888,72 +898,66 @@ static int af9013_init(struct dvb_frontend *fe)
if (ret)
goto err;
- /* load OFSM settings */
- dev_dbg(&client->dev, "load ofsm settings\n");
- len = ARRAY_SIZE(ofsm_init);
- init = ofsm_init;
+ /* Demod core settings */
+ dev_dbg(&client->dev, "load demod core settings\n");
+ len = ARRAY_SIZE(demod_init_tab);
+ tab = demod_init_tab;
for (i = 0; i < len; i++) {
- u16 reg = init[i].addr;
- u8 mask = GENMASK(init[i].pos + init[i].len - 1, init[i].pos);
- u8 val = init[i].val << init[i].pos;
-
- ret = regmap_update_bits(state->regmap, reg, mask, val);
+ ret = regmap_update_bits(state->regmap, tab[i].reg, tab[i].mask,
+ tab[i].val);
if (ret)
goto err;
}
- /* load tuner specific settings */
+ /* Demod tuner specific settings */
dev_dbg(&client->dev, "load tuner specific settings\n");
switch (state->tuner) {
case AF9013_TUNER_MXL5003D:
- len = ARRAY_SIZE(tuner_init_mxl5003d);
- init = tuner_init_mxl5003d;
+ len = ARRAY_SIZE(tuner_init_tab_mxl5003d);
+ tab = tuner_init_tab_mxl5003d;
break;
case AF9013_TUNER_MXL5005D:
case AF9013_TUNER_MXL5005R:
case AF9013_TUNER_MXL5007T:
- len = ARRAY_SIZE(tuner_init_mxl5005);
- init = tuner_init_mxl5005;
+ len = ARRAY_SIZE(tuner_init_tab_mxl5005);
+ tab = tuner_init_tab_mxl5005;
break;
case AF9013_TUNER_ENV77H11D5:
- len = ARRAY_SIZE(tuner_init_env77h11d5);
- init = tuner_init_env77h11d5;
+ len = ARRAY_SIZE(tuner_init_tab_env77h11d5);
+ tab = tuner_init_tab_env77h11d5;
break;
case AF9013_TUNER_MT2060:
- len = ARRAY_SIZE(tuner_init_mt2060);
- init = tuner_init_mt2060;
+ len = ARRAY_SIZE(tuner_init_tab_mt2060);
+ tab = tuner_init_tab_mt2060;
break;
case AF9013_TUNER_MC44S803:
- len = ARRAY_SIZE(tuner_init_mc44s803);
- init = tuner_init_mc44s803;
+ len = ARRAY_SIZE(tuner_init_tab_mc44s803);
+ tab = tuner_init_tab_mc44s803;
break;
case AF9013_TUNER_QT1010:
case AF9013_TUNER_QT1010A:
- len = ARRAY_SIZE(tuner_init_qt1010);
- init = tuner_init_qt1010;
+ len = ARRAY_SIZE(tuner_init_tab_qt1010);
+ tab = tuner_init_tab_qt1010;
break;
case AF9013_TUNER_MT2060_2:
- len = ARRAY_SIZE(tuner_init_mt2060_2);
- init = tuner_init_mt2060_2;
+ len = ARRAY_SIZE(tuner_init_tab_mt2060_2);
+ tab = tuner_init_tab_mt2060_2;
break;
case AF9013_TUNER_TDA18271:
case AF9013_TUNER_TDA18218:
- len = ARRAY_SIZE(tuner_init_tda18271);
- init = tuner_init_tda18271;
+ len = ARRAY_SIZE(tuner_init_tab_tda18271);
+ tab = tuner_init_tab_tda18271;
break;
case AF9013_TUNER_UNKNOWN:
default:
- len = ARRAY_SIZE(tuner_init_unknown);
- init = tuner_init_unknown;
+ len = ARRAY_SIZE(tuner_init_tab_unknown);
+ tab = tuner_init_tab_unknown;
break;
}
for (i = 0; i < len; i++) {
- u16 reg = init[i].addr;
- u8 mask = GENMASK(init[i].pos + init[i].len - 1, init[i].pos);
- u8 val = init[i].val << init[i].pos;
-
- ret = regmap_update_bits(state->regmap, reg, mask, val);
+ ret = regmap_update_bits(state->regmap, tab[i].reg, tab[i].mask,
+ tab[i].val);
if (ret)
goto err;
}
@@ -972,50 +976,7 @@ static int af9013_init(struct dvb_frontend *fe)
if (ret)
goto err;
- /* check if we support signal strength */
- if (!state->signal_strength_en) {
- ret = regmap_read(state->regmap, 0x9bee, &utmp);
- if (ret)
- goto err;
-
- state->signal_strength_en = (utmp >> 0) & 0x01;
- }
-
- /* read values needed for signal strength calculation */
- if (state->signal_strength_en && !state->rf_50) {
- ret = regmap_bulk_read(state->regmap, 0x9bbd, &state->rf_50, 1);
- if (ret)
- goto err;
- ret = regmap_bulk_read(state->regmap, 0x9bd0, &state->rf_80, 1);
- if (ret)
- goto err;
- ret = regmap_bulk_read(state->regmap, 0x9be2, &state->if_50, 1);
- if (ret)
- goto err;
- ret = regmap_bulk_read(state->regmap, 0x9be4, &state->if_80, 1);
- if (ret)
- goto err;
- }
-
- /* SNR */
- ret = regmap_write(state->regmap, 0xd2e2, 0x01);
- if (ret)
- goto err;
-
- /* BER / UCB */
- buf[0] = (10000 >> 0) & 0xff;
- buf[1] = (10000 >> 8) & 0xff;
- ret = regmap_bulk_write(state->regmap, 0xd385, buf, 2);
- if (ret)
- goto err;
-
- /* enable FEC monitor */
- ret = regmap_update_bits(state->regmap, 0xd392, 0x02, 0x02);
- if (ret)
- goto err;
-
state->first_tune = true;
- schedule_delayed_work(&state->statistics_work, msecs_to_jiffies(400));
return 0;
err:
@@ -1032,9 +993,6 @@ static int af9013_sleep(struct dvb_frontend *fe)
dev_dbg(&client->dev, "\n");
- /* stop statistics polling */
- cancel_delayed_work_sync(&state->statistics_work);
-
/* disable lock led */
ret = regmap_update_bits(state->regmap, 0xd730, 0x01, 0x00);
if (ret)
@@ -1072,45 +1030,6 @@ err:
return ret;
}
-static int af9013_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
-{
- int ret;
- struct af9013_state *state = fe->demodulator_priv;
- struct i2c_client *client = state->client;
-
- dev_dbg(&client->dev, "enable %d\n", enable);
-
- /* gate already open or close */
- if (state->i2c_gate_state == enable)
- return 0;
-
- if (state->ts_mode == AF9013_TS_MODE_USB)
- ret = regmap_update_bits(state->regmap, 0xd417, 0x08,
- enable << 3);
- else
- ret = regmap_update_bits(state->regmap, 0xd607, 0x04,
- enable << 2);
- if (ret)
- goto err;
-
- state->i2c_gate_state = enable;
-
- return 0;
-err:
- dev_dbg(&client->dev, "failed %d\n", ret);
- return ret;
-}
-
-static void af9013_release(struct dvb_frontend *fe)
-{
- struct af9013_state *state = fe->demodulator_priv;
- struct i2c_client *client = state->client;
-
- dev_dbg(&client->dev, "\n");
-
- i2c_unregister_device(client);
-}
-
static const struct dvb_frontend_ops af9013_ops;
static int af9013_download_firmware(struct af9013_state *state)
@@ -1213,40 +1132,6 @@ err:
return ret;
}
-/*
- * XXX: That is wrapper to af9013_probe() via driver core in order to provide
- * proper I2C client for legacy media attach binding.
- * New users must use I2C client binding directly!
- */
-struct dvb_frontend *af9013_attach(const struct af9013_config *config,
- struct i2c_adapter *i2c)
-{
- struct i2c_client *client;
- struct i2c_board_info board_info;
- struct af9013_platform_data pdata;
-
- pdata.clk = config->clock;
- pdata.tuner = config->tuner;
- pdata.if_frequency = config->if_frequency;
- pdata.ts_mode = config->ts_mode;
- pdata.ts_output_pin = 7;
- pdata.spec_inv = config->spec_inv;
- memcpy(&pdata.api_version, config->api_version, sizeof(pdata.api_version));
- memcpy(&pdata.gpio, config->gpio, sizeof(pdata.gpio));
- pdata.attach_in_use = true;
-
- memset(&board_info, 0, sizeof(board_info));
- strlcpy(board_info.type, "af9013", sizeof(board_info.type));
- board_info.addr = config->i2c_addr;
- board_info.platform_data = &pdata;
- client = i2c_new_device(i2c, &board_info);
- if (!client || !client->dev.driver)
- return NULL;
-
- return pdata.get_dvb_frontend(client);
-}
-EXPORT_SYMBOL(af9013_attach);
-
static const struct dvb_frontend_ops af9013_ops = {
.delsys = { SYS_DVBT },
.info = {
@@ -1272,8 +1157,6 @@ static const struct dvb_frontend_ops af9013_ops = {
FE_CAN_MUTE_TS
},
- .release = af9013_release,
-
.init = af9013_init,
.sleep = af9013_sleep,
@@ -1286,10 +1169,58 @@ static const struct dvb_frontend_ops af9013_ops = {
.read_signal_strength = af9013_read_signal_strength,
.read_ber = af9013_read_ber,
.read_ucblocks = af9013_read_ucblocks,
-
- .i2c_gate_ctrl = af9013_i2c_gate_ctrl,
};
+static int af9013_pid_filter_ctrl(struct dvb_frontend *fe, int onoff)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ struct i2c_client *client = state->client;
+ int ret;
+
+ dev_dbg(&client->dev, "onoff %d\n", onoff);
+
+ ret = regmap_update_bits(state->regmap, 0xd503, 0x01, onoff);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed %d\n", ret);
+ return ret;
+}
+
+static int af9013_pid_filter(struct dvb_frontend *fe, u8 index, u16 pid,
+ int onoff)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ struct i2c_client *client = state->client;
+ int ret;
+ u8 buf[2];
+
+ dev_dbg(&client->dev, "index %d, pid %04x, onoff %d\n",
+ index, pid, onoff);
+
+ if (pid > 0x1fff) {
+ /* 0x2000 is kernel virtual pid for whole ts (all pids) */
+ ret = 0;
+ goto err;
+ }
+
+ buf[0] = (pid >> 0) & 0xff;
+ buf[1] = (pid >> 8) & 0xff;
+ ret = regmap_bulk_write(state->regmap, 0xd505, buf, 2);
+ if (ret)
+ goto err;
+ ret = regmap_write(state->regmap, 0xd504, onoff << 5 | index << 0);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed %d\n", ret);
+ return ret;
+}
+
static struct dvb_frontend *af9013_get_dvb_frontend(struct i2c_client *client)
{
struct af9013_state *state = i2c_get_clientdata(client);
@@ -1299,9 +1230,65 @@ static struct dvb_frontend *af9013_get_dvb_frontend(struct i2c_client *client)
return &state->fe;
}
+static struct i2c_adapter *af9013_get_i2c_adapter(struct i2c_client *client)
+{
+ struct af9013_state *state = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "\n");
+
+ return state->muxc->adapter[0];
+}
+
+/*
+ * XXX: Hackish solution. We use virtual register, reg bit 16, to carry info
+ * about i2c adapter locking. Own locking is needed because i2c mux call has
+ * already locked i2c adapter.
+ */
+static int af9013_select(struct i2c_mux_core *muxc, u32 chan)
+{
+ struct af9013_state *state = i2c_mux_priv(muxc);
+ struct i2c_client *client = state->client;
+ int ret;
+
+ dev_dbg(&client->dev, "\n");
+
+ if (state->ts_mode == AF9013_TS_MODE_USB)
+ ret = regmap_update_bits(state->regmap, 0x1d417, 0x08, 0x08);
+ else
+ ret = regmap_update_bits(state->regmap, 0x1d607, 0x04, 0x04);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed %d\n", ret);
+ return ret;
+}
+
+static int af9013_deselect(struct i2c_mux_core *muxc, u32 chan)
+{
+ struct af9013_state *state = i2c_mux_priv(muxc);
+ struct i2c_client *client = state->client;
+ int ret;
+
+ dev_dbg(&client->dev, "\n");
+
+ if (state->ts_mode == AF9013_TS_MODE_USB)
+ ret = regmap_update_bits(state->regmap, 0x1d417, 0x08, 0x00);
+ else
+ ret = regmap_update_bits(state->regmap, 0x1d607, 0x04, 0x00);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed %d\n", ret);
+ return ret;
+}
+
/* Own I2C access routines needed for regmap as chip uses extra command byte */
static int af9013_wregs(struct i2c_client *client, u8 cmd, u16 reg,
- const u8 *val, int len)
+ const u8 *val, int len, u8 lock)
{
int ret;
u8 buf[21];
@@ -1323,7 +1310,12 @@ static int af9013_wregs(struct i2c_client *client, u8 cmd, u16 reg,
buf[1] = (reg >> 0) & 0xff;
buf[2] = cmd;
memcpy(&buf[3], val, len);
- ret = i2c_transfer(client->adapter, msg, 1);
+
+ if (lock)
+ i2c_lock_adapter(client->adapter);
+ ret = __i2c_transfer(client->adapter, msg, 1);
+ if (lock)
+ i2c_unlock_adapter(client->adapter);
if (ret < 0) {
goto err;
} else if (ret != 1) {
@@ -1338,7 +1330,7 @@ err:
}
static int af9013_rregs(struct i2c_client *client, u8 cmd, u16 reg,
- u8 *val, int len)
+ u8 *val, int len, u8 lock)
{
int ret;
u8 buf[3];
@@ -1359,7 +1351,12 @@ static int af9013_rregs(struct i2c_client *client, u8 cmd, u16 reg,
buf[0] = (reg >> 8) & 0xff;
buf[1] = (reg >> 0) & 0xff;
buf[2] = cmd;
- ret = i2c_transfer(client->adapter, msg, 2);
+
+ if (lock)
+ i2c_lock_adapter(client->adapter);
+ ret = __i2c_transfer(client->adapter, msg, 2);
+ if (lock)
+ i2c_unlock_adapter(client->adapter);
if (ret < 0) {
goto err;
} else if (ret != 2) {
@@ -1379,25 +1376,27 @@ static int af9013_regmap_write(void *context, const void *data, size_t count)
struct af9013_state *state = i2c_get_clientdata(client);
int ret, i;
u8 cmd;
- u16 reg = ((u8 *)data)[0] << 8|((u8 *)data)[1] << 0;
- u8 *val = &((u8 *)data)[2];
- const unsigned int len = count - 2;
+ u8 lock = !((u8 *)data)[0];
+ u16 reg = ((u8 *)data)[1] << 8 | ((u8 *)data)[2] << 0;
+ u8 *val = &((u8 *)data)[3];
+ const unsigned int len = count - 3;
if (state->ts_mode == AF9013_TS_MODE_USB && (reg & 0xff00) != 0xae00) {
cmd = 0 << 7|0 << 6|(len - 1) << 2|1 << 1|1 << 0;
- ret = af9013_wregs(client, cmd, reg, val, len);
+ ret = af9013_wregs(client, cmd, reg, val, len, lock);
if (ret)
goto err;
} else if (reg >= 0x5100 && reg < 0x8fff) {
/* Firmware download */
cmd = 1 << 7|1 << 6|(len - 1) << 2|1 << 1|1 << 0;
- ret = af9013_wregs(client, cmd, reg, val, len);
+ ret = af9013_wregs(client, cmd, reg, val, len, lock);
if (ret)
goto err;
} else {
cmd = 0 << 7|0 << 6|(1 - 1) << 2|1 << 1|1 << 0;
for (i = 0; i < len; i++) {
- ret = af9013_wregs(client, cmd, reg + i, val + i, 1);
+ ret = af9013_wregs(client, cmd, reg + i, val + i, 1,
+ lock);
if (ret)
goto err;
}
@@ -1416,19 +1415,21 @@ static int af9013_regmap_read(void *context, const void *reg_buf,
struct af9013_state *state = i2c_get_clientdata(client);
int ret, i;
u8 cmd;
- u16 reg = ((u8 *)reg_buf)[0] << 8|((u8 *)reg_buf)[1] << 0;
+ u8 lock = !((u8 *)reg_buf)[0];
+ u16 reg = ((u8 *)reg_buf)[1] << 8 | ((u8 *)reg_buf)[2] << 0;
u8 *val = &((u8 *)val_buf)[0];
const unsigned int len = val_size;
if (state->ts_mode == AF9013_TS_MODE_USB && (reg & 0xff00) != 0xae00) {
cmd = 0 << 7|0 << 6|(len - 1) << 2|1 << 1|0 << 0;
- ret = af9013_rregs(client, cmd, reg, val_buf, len);
+ ret = af9013_rregs(client, cmd, reg, val_buf, len, lock);
if (ret)
goto err;
} else {
cmd = 0 << 7|0 << 6|(1 - 1) << 2|1 << 1|0 << 0;
for (i = 0; i < len; i++) {
- ret = af9013_rregs(client, cmd, reg + i, val + i, 1);
+ ret = af9013_rregs(client, cmd, reg + i, val + i, 1,
+ lock);
if (ret)
goto err;
}
@@ -1453,8 +1454,9 @@ static int af9013_probe(struct i2c_client *client,
.write = af9013_regmap_write,
};
static const struct regmap_config regmap_config = {
- .reg_bits = 16,
- .val_bits = 8,
+ /* Actual reg is 16 bits, see i2c adapter lock */
+ .reg_bits = 24,
+ .val_bits = 8,
};
state = kzalloc(sizeof(*state), GFP_KERNEL);
@@ -1463,6 +1465,8 @@ static int af9013_probe(struct i2c_client *client,
goto err;
}
+ dev_dbg(&client->dev, "\n");
+
/* Setup the state */
state->client = client;
i2c_set_clientdata(client, state);
@@ -1474,52 +1478,70 @@ static int af9013_probe(struct i2c_client *client,
state->spec_inv = pdata->spec_inv;
memcpy(&state->api_version, pdata->api_version, sizeof(state->api_version));
memcpy(&state->gpio, pdata->gpio, sizeof(state->gpio));
- INIT_DELAYED_WORK(&state->statistics_work, af9013_statistics_work);
state->regmap = regmap_init(&client->dev, &regmap_bus, client,
&regmap_config);
if (IS_ERR(state->regmap)) {
ret = PTR_ERR(state->regmap);
goto err_kfree;
}
+ /* Create mux i2c adapter */
+ state->muxc = i2c_mux_alloc(client->adapter, &client->dev, 1, 0, 0,
+ af9013_select, af9013_deselect);
+ if (!state->muxc) {
+ ret = -ENOMEM;
+ goto err_regmap_exit;
+ }
+ state->muxc->priv = state;
+ ret = i2c_mux_add_adapter(state->muxc, 0, 0, 0);
+ if (ret)
+ goto err_regmap_exit;
/* Download firmware */
if (state->ts_mode != AF9013_TS_MODE_USB) {
ret = af9013_download_firmware(state);
if (ret)
- goto err_regmap_exit;
+ goto err_i2c_mux_del_adapters;
}
/* Firmware version */
ret = regmap_bulk_read(state->regmap, 0x5103, firmware_version,
sizeof(firmware_version));
if (ret)
- goto err_regmap_exit;
+ goto err_i2c_mux_del_adapters;
/* Set GPIOs */
for (i = 0; i < sizeof(state->gpio); i++) {
ret = af9013_set_gpio(state, i, state->gpio[i]);
if (ret)
- goto err_regmap_exit;
+ goto err_i2c_mux_del_adapters;
}
/* Create dvb frontend */
memcpy(&state->fe.ops, &af9013_ops, sizeof(state->fe.ops));
- if (!pdata->attach_in_use)
- state->fe.ops.release = NULL;
state->fe.demodulator_priv = state;
/* Setup callbacks */
pdata->get_dvb_frontend = af9013_get_dvb_frontend;
+ pdata->get_i2c_adapter = af9013_get_i2c_adapter;
+ pdata->pid_filter = af9013_pid_filter;
+ pdata->pid_filter_ctrl = af9013_pid_filter_ctrl;
/* Init stats to indicate which stats are supported */
c = &state->fe.dtv_property_cache;
+ c->strength.len = 1;
c->cnr.len = 1;
+ c->post_bit_error.len = 1;
+ c->post_bit_count.len = 1;
+ c->block_error.len = 1;
+ c->block_count.len = 1;
dev_info(&client->dev, "Afatech AF9013 successfully attached\n");
dev_info(&client->dev, "firmware version: %d.%d.%d.%d\n",
firmware_version[0], firmware_version[1],
firmware_version[2], firmware_version[3]);
return 0;
+err_i2c_mux_del_adapters:
+ i2c_mux_del_adapters(state->muxc);
err_regmap_exit:
regmap_exit(state->regmap);
err_kfree:
@@ -1535,8 +1557,7 @@ static int af9013_remove(struct i2c_client *client)
dev_dbg(&client->dev, "\n");
- /* Stop statistics polling */
- cancel_delayed_work_sync(&state->statistics_work);
+ i2c_mux_del_adapters(state->muxc);
regmap_exit(state->regmap);
diff --git a/drivers/media/dvb-frontends/af9013.h b/drivers/media/dvb-frontends/af9013.h
index a290722c04fd..165ae29ccac4 100644
--- a/drivers/media/dvb-frontends/af9013.h
+++ b/drivers/media/dvb-frontends/af9013.h
@@ -38,13 +38,9 @@
* @api_version: Firmware API version.
* @gpio: GPIOs.
* @get_dvb_frontend: Get DVB frontend callback.
- *
- * AF9013/5 GPIOs (mostly guessed):
- * * demod#1-gpio#0 - set demod#2 i2c-addr for dual devices
- * * demod#1-gpio#1 - xtal setting (?)
- * * demod#1-gpio#3 - tuner#1
- * * demod#2-gpio#0 - tuner#2
- * * demod#2-gpio#1 - xtal setting (?)
+ * @get_i2c_adapter: Get I2C adapter.
+ * @pid_filter_ctrl: Control PID filter.
+ * @pid_filter: Set PID to PID filter.
*/
struct af9013_platform_data {
/*
@@ -84,36 +80,18 @@ struct af9013_platform_data {
u8 gpio[4];
struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *);
-
-/* private: For legacy media attach wrapper. Do not set value. */
- bool attach_in_use;
- u8 i2c_addr;
- u32 clock;
+ struct i2c_adapter* (*get_i2c_adapter)(struct i2c_client *);
+ int (*pid_filter_ctrl)(struct dvb_frontend *, int);
+ int (*pid_filter)(struct dvb_frontend *, u8, u16, int);
};
-#define af9013_config af9013_platform_data
-#define AF9013_TS_USB AF9013_TS_MODE_USB
-#define AF9013_TS_PARALLEL AF9013_TS_MODE_PARALLEL
-#define AF9013_TS_SERIAL AF9013_TS_MODE_SERIAL
-
-#if IS_REACHABLE(CONFIG_DVB_AF9013)
-/**
- * Attach an af9013 demod
- *
- * @config: pointer to &struct af9013_config with demod configuration.
- * @i2c: i2c adapter to use.
- *
- * return: FE pointer on success, NULL on failure.
+/*
+ * AF9013/5 GPIOs (mostly guessed)
+ * demod#1-gpio#0 - set demod#2 i2c-addr for dual devices
+ * demod#1-gpio#1 - xtal setting (?)
+ * demod#1-gpio#3 - tuner#1
+ * demod#2-gpio#0 - tuner#2
+ * demod#2-gpio#1 - xtal setting (?)
*/
-extern struct dvb_frontend *af9013_attach(const struct af9013_config *config,
- struct i2c_adapter *i2c);
-#else
-static inline struct dvb_frontend *af9013_attach(
-const struct af9013_config *config, struct i2c_adapter *i2c)
-{
- pr_warn("%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif /* CONFIG_DVB_AF9013 */
#endif /* AF9013_H */
diff --git a/drivers/media/dvb-frontends/af9013_priv.h b/drivers/media/dvb-frontends/af9013_priv.h
index 688fc3472cf6..3e95de7dba51 100644
--- a/drivers/media/dvb-frontends/af9013_priv.h
+++ b/drivers/media/dvb-frontends/af9013_priv.h
@@ -22,25 +22,21 @@
#define AF9013_PRIV_H
#include <media/dvb_frontend.h>
+#include <media/dvb_math.h>
#include "af9013.h"
#include <linux/firmware.h>
+#include <linux/i2c-mux.h>
#include <linux/math64.h>
#include <linux/regmap.h>
#define AF9013_FIRMWARE "dvb-fe-af9013.fw"
-struct af9013_reg_bit {
- u16 addr;
- u8 pos:4;
- u8 len:4;
+struct af9013_reg_mask_val {
+ u16 reg;
+ u8 mask;
u8 val;
};
-struct af9013_snr {
- u32 val;
- u8 snr;
-};
-
struct af9013_coeff {
u32 clock;
u32 bandwidth_hz;
@@ -91,817 +87,775 @@ static const struct af9013_coeff coeff_lut[] = {
0x2d, 0x00, 0x8c, 0x6a, 0xca, 0x01, 0x18, 0xde, 0x17 } },
};
-/* QPSK SNR lookup table */
-static const struct af9013_snr qpsk_snr_lut[] = {
- { 0x000000, 0 },
- { 0x0b4771, 0 },
- { 0x0c1aed, 1 },
- { 0x0d0d27, 2 },
- { 0x0e4d19, 3 },
- { 0x0e5da8, 4 },
- { 0x107097, 5 },
- { 0x116975, 6 },
- { 0x1252d9, 7 },
- { 0x131fa4, 8 },
- { 0x13d5e1, 9 },
- { 0x148e53, 10 },
- { 0x15358b, 11 },
- { 0x15dd29, 12 },
- { 0x168112, 13 },
- { 0x170b61, 14 },
- { 0xffffff, 15 },
-};
-
-/* QAM16 SNR lookup table */
-static const struct af9013_snr qam16_snr_lut[] = {
- { 0x000000, 0 },
- { 0x05eb62, 5 },
- { 0x05fecf, 6 },
- { 0x060b80, 7 },
- { 0x062501, 8 },
- { 0x064865, 9 },
- { 0x069604, 10 },
- { 0x06f356, 11 },
- { 0x07706a, 12 },
- { 0x0804d3, 13 },
- { 0x089d1a, 14 },
- { 0x093e3d, 15 },
- { 0x09e35d, 16 },
- { 0x0a7c3c, 17 },
- { 0x0afaf8, 18 },
- { 0x0b719d, 19 },
- { 0xffffff, 20 },
-};
-
-/* QAM64 SNR lookup table */
-static const struct af9013_snr qam64_snr_lut[] = {
- { 0x000000, 0 },
- { 0x03109b, 12 },
- { 0x0310d4, 13 },
- { 0x031920, 14 },
- { 0x0322d0, 15 },
- { 0x0339fc, 16 },
- { 0x0364a1, 17 },
- { 0x038bcc, 18 },
- { 0x03c7d3, 19 },
- { 0x0408cc, 20 },
- { 0x043bed, 21 },
- { 0x048061, 22 },
- { 0x04be95, 23 },
- { 0x04fa7d, 24 },
- { 0x052405, 25 },
- { 0x05570d, 26 },
- { 0xffffff, 27 },
-};
-
-static const struct af9013_reg_bit ofsm_init[] = {
- { 0xd73a, 0, 8, 0xa1 },
- { 0xd73b, 0, 8, 0x1f },
- { 0xd73c, 4, 4, 0x0a },
- { 0xd732, 3, 1, 0x00 },
- { 0xd731, 4, 2, 0x03 },
- { 0xd73d, 7, 1, 0x01 },
- { 0xd740, 0, 1, 0x00 },
- { 0xd740, 1, 1, 0x00 },
- { 0xd740, 2, 1, 0x00 },
- { 0xd740, 3, 1, 0x01 },
- { 0xd3c1, 4, 1, 0x01 },
- { 0x9124, 0, 8, 0x58 },
- { 0x9125, 0, 2, 0x02 },
- { 0xd3a2, 0, 8, 0x00 },
- { 0xd3a3, 0, 8, 0x04 },
- { 0xd305, 0, 8, 0x32 },
- { 0xd306, 0, 8, 0x10 },
- { 0xd304, 0, 8, 0x04 },
- { 0x9112, 0, 1, 0x01 },
- { 0x911d, 0, 1, 0x01 },
- { 0x911a, 0, 1, 0x01 },
- { 0x911b, 0, 1, 0x01 },
- { 0x9bce, 0, 4, 0x02 },
- { 0x9116, 0, 1, 0x01 },
- { 0x9122, 0, 8, 0xd0 },
- { 0xd2e0, 0, 8, 0xd0 },
- { 0xd2e9, 0, 4, 0x0d },
- { 0xd38c, 0, 8, 0xfc },
- { 0xd38d, 0, 8, 0x00 },
- { 0xd38e, 0, 8, 0x7e },
- { 0xd38f, 0, 8, 0x00 },
- { 0xd390, 0, 8, 0x2f },
- { 0xd145, 4, 1, 0x01 },
- { 0xd1a9, 4, 1, 0x01 },
- { 0xd158, 5, 3, 0x01 },
- { 0xd159, 0, 6, 0x06 },
- { 0xd167, 0, 8, 0x00 },
- { 0xd168, 0, 4, 0x07 },
- { 0xd1c3, 5, 3, 0x00 },
- { 0xd1c4, 0, 6, 0x00 },
- { 0xd1c5, 0, 7, 0x10 },
- { 0xd1c6, 0, 3, 0x02 },
- { 0xd080, 2, 5, 0x03 },
- { 0xd081, 4, 4, 0x09 },
- { 0xd098, 4, 4, 0x0f },
- { 0xd098, 0, 4, 0x03 },
- { 0xdbc0, 4, 1, 0x01 },
- { 0xdbc7, 0, 8, 0x08 },
- { 0xdbc8, 4, 4, 0x00 },
- { 0xdbc9, 0, 5, 0x01 },
- { 0xd280, 0, 8, 0xe0 },
- { 0xd281, 0, 8, 0xff },
- { 0xd282, 0, 8, 0xff },
- { 0xd283, 0, 8, 0xc3 },
- { 0xd284, 0, 8, 0xff },
- { 0xd285, 0, 4, 0x01 },
- { 0xd0f0, 0, 7, 0x1a },
- { 0xd0f1, 4, 1, 0x01 },
- { 0xd0f2, 0, 8, 0x0c },
- { 0xd101, 5, 3, 0x06 },
- { 0xd103, 0, 4, 0x08 },
- { 0xd0f8, 0, 7, 0x20 },
- { 0xd111, 5, 1, 0x00 },
- { 0xd111, 6, 1, 0x00 },
- { 0x910b, 0, 8, 0x0a },
- { 0x9115, 0, 8, 0x02 },
- { 0x910c, 0, 8, 0x02 },
- { 0x910d, 0, 8, 0x08 },
- { 0x910e, 0, 8, 0x0a },
- { 0x9bf6, 0, 8, 0x06 },
- { 0x9bf8, 0, 8, 0x02 },
- { 0x9bf7, 0, 8, 0x05 },
- { 0x9bf9, 0, 8, 0x0f },
- { 0x9bfc, 0, 8, 0x13 },
- { 0x9bd3, 0, 8, 0xff },
- { 0x9bbe, 0, 1, 0x01 },
- { 0x9bcc, 0, 1, 0x01 },
+/*
+ * Afatech AF9013 demod init
+ */
+static const struct af9013_reg_mask_val demod_init_tab[] = {
+ {0xd73a, 0xff, 0xa1},
+ {0xd73b, 0xff, 0x1f},
+ {0xd73c, 0xf0, 0xa0},
+ {0xd732, 0x08, 0x00},
+ {0xd731, 0x30, 0x30},
+ {0xd73d, 0x80, 0x80},
+ {0xd740, 0x01, 0x00},
+ {0xd740, 0x02, 0x00},
+ {0xd740, 0x04, 0x00},
+ {0xd740, 0x08, 0x08},
+ {0xd3c1, 0x10, 0x10},
+ {0x9124, 0xff, 0x58},
+ {0x9125, 0x03, 0x02},
+ {0xd3a2, 0xff, 0x00},
+ {0xd3a3, 0xff, 0x04},
+ {0xd305, 0xff, 0x32},
+ {0xd306, 0xff, 0x10},
+ {0xd304, 0xff, 0x04},
+ {0x9112, 0x01, 0x01},
+ {0x911d, 0x01, 0x01},
+ {0x911a, 0x01, 0x01},
+ {0x911b, 0x01, 0x01},
+ {0x9bce, 0x0f, 0x02},
+ {0x9116, 0x01, 0x01},
+ {0x9122, 0xff, 0xd0},
+ {0xd2e0, 0xff, 0xd0},
+ {0xd2e9, 0x0f, 0x0d},
+ {0xd38c, 0xff, 0xfc},
+ {0xd38d, 0xff, 0x00},
+ {0xd38e, 0xff, 0x7e},
+ {0xd38f, 0xff, 0x00},
+ {0xd390, 0xff, 0x2f},
+ {0xd145, 0x10, 0x10},
+ {0xd1a9, 0x10, 0x10},
+ {0xd158, 0xe0, 0x20},
+ {0xd159, 0x3f, 0x06},
+ {0xd167, 0xff, 0x00},
+ {0xd168, 0x0f, 0x07},
+ {0xd1c3, 0xe0, 0x00},
+ {0xd1c4, 0x3f, 0x00},
+ {0xd1c5, 0x7f, 0x10},
+ {0xd1c6, 0x07, 0x02},
+ {0xd080, 0x7c, 0x0c},
+ {0xd081, 0xf0, 0x90},
+ {0xd098, 0xf0, 0xf0},
+ {0xd098, 0x0f, 0x03},
+ {0xdbc0, 0x10, 0x10},
+ {0xdbc7, 0xff, 0x08},
+ {0xdbc8, 0xf0, 0x00},
+ {0xdbc9, 0x1f, 0x01},
+ {0xd280, 0xff, 0xe0},
+ {0xd281, 0xff, 0xff},
+ {0xd282, 0xff, 0xff},
+ {0xd283, 0xff, 0xc3},
+ {0xd284, 0xff, 0xff},
+ {0xd285, 0x0f, 0x01},
+ {0xd0f0, 0x7f, 0x1a},
+ {0xd0f1, 0x10, 0x10},
+ {0xd0f2, 0xff, 0x0c},
+ {0xd101, 0xe0, 0xc0},
+ {0xd103, 0x0f, 0x08},
+ {0xd0f8, 0x7f, 0x20},
+ {0xd111, 0x20, 0x00},
+ {0xd111, 0x40, 0x00},
+ {0x910b, 0xff, 0x0a},
+ {0x9115, 0xff, 0x02},
+ {0x910c, 0xff, 0x02},
+ {0x910d, 0xff, 0x08},
+ {0x910e, 0xff, 0x0a},
+ {0x9bf6, 0xff, 0x06},
+ {0x9bf8, 0xff, 0x02},
+ {0x9bf7, 0xff, 0x05},
+ {0x9bf9, 0xff, 0x0f},
+ {0x9bfc, 0xff, 0x13},
+ {0x9bd3, 0xff, 0xff},
+ {0x9bbe, 0x01, 0x01},
+ {0x9bcc, 0x01, 0x01},
};
-/* Panasonic ENV77H11D5 tuner init
- AF9013_TUNER_ENV77H11D5 = 129 */
-static const struct af9013_reg_bit tuner_init_env77h11d5[] = {
- { 0x9bd5, 0, 8, 0x01 },
- { 0x9bd6, 0, 8, 0x03 },
- { 0x9bbe, 0, 8, 0x01 },
- { 0xd1a0, 1, 1, 0x01 },
- { 0xd000, 0, 1, 0x01 },
- { 0xd000, 1, 1, 0x00 },
- { 0xd001, 1, 1, 0x01 },
- { 0xd001, 0, 1, 0x00 },
- { 0xd001, 5, 1, 0x00 },
- { 0xd002, 0, 5, 0x19 },
- { 0xd003, 0, 5, 0x1a },
- { 0xd004, 0, 5, 0x19 },
- { 0xd005, 0, 5, 0x1a },
- { 0xd00e, 0, 5, 0x10 },
- { 0xd00f, 0, 3, 0x04 },
- { 0xd00f, 3, 3, 0x05 },
- { 0xd010, 0, 3, 0x04 },
- { 0xd010, 3, 3, 0x05 },
- { 0xd016, 4, 4, 0x03 },
- { 0xd01f, 0, 6, 0x0a },
- { 0xd020, 0, 6, 0x0a },
- { 0x9bda, 0, 8, 0x00 },
- { 0x9be3, 0, 8, 0x00 },
- { 0xd015, 0, 8, 0x50 },
- { 0xd016, 0, 1, 0x00 },
- { 0xd044, 0, 8, 0x46 },
- { 0xd045, 0, 1, 0x00 },
- { 0xd008, 0, 8, 0xdf },
- { 0xd009, 0, 2, 0x02 },
- { 0xd006, 0, 8, 0x44 },
- { 0xd007, 0, 2, 0x01 },
- { 0xd00c, 0, 8, 0xeb },
- { 0xd00d, 0, 2, 0x02 },
- { 0xd00a, 0, 8, 0xf4 },
- { 0xd00b, 0, 2, 0x01 },
- { 0x9bba, 0, 8, 0xf9 },
- { 0x9bc3, 0, 8, 0xdf },
- { 0x9bc4, 0, 8, 0x02 },
- { 0x9bc5, 0, 8, 0xeb },
- { 0x9bc6, 0, 8, 0x02 },
- { 0x9bc9, 0, 8, 0x52 },
- { 0xd011, 0, 8, 0x3c },
- { 0xd012, 0, 2, 0x01 },
- { 0xd013, 0, 8, 0xf7 },
- { 0xd014, 0, 2, 0x02 },
- { 0xd040, 0, 8, 0x0b },
- { 0xd041, 0, 2, 0x02 },
- { 0xd042, 0, 8, 0x4d },
- { 0xd043, 0, 2, 0x00 },
- { 0xd045, 1, 1, 0x00 },
- { 0x9bcf, 0, 1, 0x01 },
- { 0xd045, 2, 1, 0x01 },
- { 0xd04f, 0, 8, 0x9a },
- { 0xd050, 0, 1, 0x01 },
- { 0xd051, 0, 8, 0x5a },
- { 0xd052, 0, 1, 0x01 },
- { 0xd053, 0, 8, 0x50 },
- { 0xd054, 0, 8, 0x46 },
- { 0x9bd7, 0, 8, 0x0a },
- { 0x9bd8, 0, 8, 0x14 },
- { 0x9bd9, 0, 8, 0x08 },
+/*
+ * Panasonic ENV77H11D5 tuner init
+ * AF9013_TUNER_ENV77H11D5 0x81
+ */
+static const struct af9013_reg_mask_val tuner_init_tab_env77h11d5[] = {
+ {0x9bd5, 0xff, 0x01},
+ {0x9bd6, 0xff, 0x03},
+ {0x9bbe, 0xff, 0x01},
+ {0xd1a0, 0x02, 0x02},
+ {0xd000, 0x01, 0x01},
+ {0xd000, 0x02, 0x00},
+ {0xd001, 0x02, 0x02},
+ {0xd001, 0x01, 0x00},
+ {0xd001, 0x20, 0x00},
+ {0xd002, 0x1f, 0x19},
+ {0xd003, 0x1f, 0x1a},
+ {0xd004, 0x1f, 0x19},
+ {0xd005, 0x1f, 0x1a},
+ {0xd00e, 0x1f, 0x10},
+ {0xd00f, 0x07, 0x04},
+ {0xd00f, 0x38, 0x28},
+ {0xd010, 0x07, 0x04},
+ {0xd010, 0x38, 0x28},
+ {0xd016, 0xf0, 0x30},
+ {0xd01f, 0x3f, 0x0a},
+ {0xd020, 0x3f, 0x0a},
+ {0x9bda, 0xff, 0x00},
+ {0x9be3, 0xff, 0x00},
+ {0xd015, 0xff, 0x50},
+ {0xd016, 0x01, 0x00},
+ {0xd044, 0xff, 0x46},
+ {0xd045, 0x01, 0x00},
+ {0xd008, 0xff, 0xdf},
+ {0xd009, 0x03, 0x02},
+ {0xd006, 0xff, 0x44},
+ {0xd007, 0x03, 0x01},
+ {0xd00c, 0xff, 0xeb},
+ {0xd00d, 0x03, 0x02},
+ {0xd00a, 0xff, 0xf4},
+ {0xd00b, 0x03, 0x01},
+ {0x9bba, 0xff, 0xf9},
+ {0x9bc3, 0xff, 0xdf},
+ {0x9bc4, 0xff, 0x02},
+ {0x9bc5, 0xff, 0xeb},
+ {0x9bc6, 0xff, 0x02},
+ {0x9bc9, 0xff, 0x52},
+ {0xd011, 0xff, 0x3c},
+ {0xd012, 0x03, 0x01},
+ {0xd013, 0xff, 0xf7},
+ {0xd014, 0x03, 0x02},
+ {0xd040, 0xff, 0x0b},
+ {0xd041, 0x03, 0x02},
+ {0xd042, 0xff, 0x4d},
+ {0xd043, 0x03, 0x00},
+ {0xd045, 0x02, 0x00},
+ {0x9bcf, 0x01, 0x01},
+ {0xd045, 0x04, 0x04},
+ {0xd04f, 0xff, 0x9a},
+ {0xd050, 0x01, 0x01},
+ {0xd051, 0xff, 0x5a},
+ {0xd052, 0x01, 0x01},
+ {0xd053, 0xff, 0x50},
+ {0xd054, 0xff, 0x46},
+ {0x9bd7, 0xff, 0x0a},
+ {0x9bd8, 0xff, 0x14},
+ {0x9bd9, 0xff, 0x08},
};
-/* Microtune MT2060 tuner init
- AF9013_TUNER_MT2060 = 130 */
-static const struct af9013_reg_bit tuner_init_mt2060[] = {
- { 0x9bd5, 0, 8, 0x01 },
- { 0x9bd6, 0, 8, 0x07 },
- { 0xd1a0, 1, 1, 0x01 },
- { 0xd000, 0, 1, 0x01 },
- { 0xd000, 1, 1, 0x00 },
- { 0xd001, 1, 1, 0x01 },
- { 0xd001, 0, 1, 0x00 },
- { 0xd001, 5, 1, 0x00 },
- { 0xd002, 0, 5, 0x19 },
- { 0xd003, 0, 5, 0x1a },
- { 0xd004, 0, 5, 0x19 },
- { 0xd005, 0, 5, 0x1a },
- { 0xd00e, 0, 5, 0x10 },
- { 0xd00f, 0, 3, 0x04 },
- { 0xd00f, 3, 3, 0x05 },
- { 0xd010, 0, 3, 0x04 },
- { 0xd010, 3, 3, 0x05 },
- { 0xd016, 4, 4, 0x03 },
- { 0xd01f, 0, 6, 0x0a },
- { 0xd020, 0, 6, 0x0a },
- { 0x9bda, 0, 8, 0x00 },
- { 0x9be3, 0, 8, 0x00 },
- { 0x9bbe, 0, 1, 0x00 },
- { 0x9bcc, 0, 1, 0x00 },
- { 0x9bb9, 0, 8, 0x75 },
- { 0x9bcd, 0, 8, 0x24 },
- { 0x9bff, 0, 8, 0x30 },
- { 0xd015, 0, 8, 0x46 },
- { 0xd016, 0, 1, 0x00 },
- { 0xd044, 0, 8, 0x46 },
- { 0xd045, 0, 1, 0x00 },
- { 0xd008, 0, 8, 0x0f },
- { 0xd009, 0, 2, 0x02 },
- { 0xd006, 0, 8, 0x32 },
- { 0xd007, 0, 2, 0x01 },
- { 0xd00c, 0, 8, 0x36 },
- { 0xd00d, 0, 2, 0x03 },
- { 0xd00a, 0, 8, 0x35 },
- { 0xd00b, 0, 2, 0x01 },
- { 0x9bc7, 0, 8, 0x07 },
- { 0x9bc8, 0, 8, 0x90 },
- { 0x9bc3, 0, 8, 0x0f },
- { 0x9bc4, 0, 8, 0x02 },
- { 0x9bc5, 0, 8, 0x36 },
- { 0x9bc6, 0, 8, 0x03 },
- { 0x9bba, 0, 8, 0xc9 },
- { 0x9bc9, 0, 8, 0x79 },
- { 0xd011, 0, 8, 0x10 },
- { 0xd012, 0, 2, 0x01 },
- { 0xd013, 0, 8, 0x45 },
- { 0xd014, 0, 2, 0x03 },
- { 0xd040, 0, 8, 0x98 },
- { 0xd041, 0, 2, 0x00 },
- { 0xd042, 0, 8, 0xcf },
- { 0xd043, 0, 2, 0x03 },
- { 0xd045, 1, 1, 0x00 },
- { 0x9bcf, 0, 1, 0x01 },
- { 0xd045, 2, 1, 0x01 },
- { 0xd04f, 0, 8, 0x9a },
- { 0xd050, 0, 1, 0x01 },
- { 0xd051, 0, 8, 0x5a },
- { 0xd052, 0, 1, 0x01 },
- { 0xd053, 0, 8, 0x50 },
- { 0xd054, 0, 8, 0x46 },
- { 0x9bd7, 0, 8, 0x0a },
- { 0x9bd8, 0, 8, 0x14 },
- { 0x9bd9, 0, 8, 0x08 },
- { 0x9bd0, 0, 8, 0xcc },
- { 0x9be4, 0, 8, 0xa0 },
- { 0x9bbd, 0, 8, 0x8e },
- { 0x9be2, 0, 8, 0x4d },
- { 0x9bee, 0, 1, 0x01 },
+/*
+ * Microtune MT2060 tuner init
+ * AF9013_TUNER_MT2060 0x82
+ */
+static const struct af9013_reg_mask_val tuner_init_tab_mt2060[] = {
+ {0x9bd5, 0xff, 0x01},
+ {0x9bd6, 0xff, 0x07},
+ {0xd1a0, 0x02, 0x02},
+ {0xd000, 0x01, 0x01},
+ {0xd000, 0x02, 0x00},
+ {0xd001, 0x02, 0x02},
+ {0xd001, 0x01, 0x00},
+ {0xd001, 0x20, 0x00},
+ {0xd002, 0x1f, 0x19},
+ {0xd003, 0x1f, 0x1a},
+ {0xd004, 0x1f, 0x19},
+ {0xd005, 0x1f, 0x1a},
+ {0xd00e, 0x1f, 0x10},
+ {0xd00f, 0x07, 0x04},
+ {0xd00f, 0x38, 0x28},
+ {0xd010, 0x07, 0x04},
+ {0xd010, 0x38, 0x28},
+ {0xd016, 0xf0, 0x30},
+ {0xd01f, 0x3f, 0x0a},
+ {0xd020, 0x3f, 0x0a},
+ {0x9bda, 0xff, 0x00},
+ {0x9be3, 0xff, 0x00},
+ {0x9bbe, 0x01, 0x00},
+ {0x9bcc, 0x01, 0x00},
+ {0x9bb9, 0xff, 0x75},
+ {0x9bcd, 0xff, 0x24},
+ {0x9bff, 0xff, 0x30},
+ {0xd015, 0xff, 0x46},
+ {0xd016, 0x01, 0x00},
+ {0xd044, 0xff, 0x46},
+ {0xd045, 0x01, 0x00},
+ {0xd008, 0xff, 0x0f},
+ {0xd009, 0x03, 0x02},
+ {0xd006, 0xff, 0x32},
+ {0xd007, 0x03, 0x01},
+ {0xd00c, 0xff, 0x36},
+ {0xd00d, 0x03, 0x03},
+ {0xd00a, 0xff, 0x35},
+ {0xd00b, 0x03, 0x01},
+ {0x9bc7, 0xff, 0x07},
+ {0x9bc8, 0xff, 0x90},
+ {0x9bc3, 0xff, 0x0f},
+ {0x9bc4, 0xff, 0x02},
+ {0x9bc5, 0xff, 0x36},
+ {0x9bc6, 0xff, 0x03},
+ {0x9bba, 0xff, 0xc9},
+ {0x9bc9, 0xff, 0x79},
+ {0xd011, 0xff, 0x10},
+ {0xd012, 0x03, 0x01},
+ {0xd013, 0xff, 0x45},
+ {0xd014, 0x03, 0x03},
+ {0xd040, 0xff, 0x98},
+ {0xd041, 0x03, 0x00},
+ {0xd042, 0xff, 0xcf},
+ {0xd043, 0x03, 0x03},
+ {0xd045, 0x02, 0x00},
+ {0x9bcf, 0x01, 0x01},
+ {0xd045, 0x04, 0x04},
+ {0xd04f, 0xff, 0x9a},
+ {0xd050, 0x01, 0x01},
+ {0xd051, 0xff, 0x5a},
+ {0xd052, 0x01, 0x01},
+ {0xd053, 0xff, 0x50},
+ {0xd054, 0xff, 0x46},
+ {0x9bd7, 0xff, 0x0a},
+ {0x9bd8, 0xff, 0x14},
+ {0x9bd9, 0xff, 0x08},
+ {0x9bd0, 0xff, 0xcc},
+ {0x9be4, 0xff, 0xa0},
+ {0x9bbd, 0xff, 0x8e},
+ {0x9be2, 0xff, 0x4d},
+ {0x9bee, 0x01, 0x01},
};
-/* Microtune MT2060 tuner init
- AF9013_TUNER_MT2060_2 = 147 */
-static const struct af9013_reg_bit tuner_init_mt2060_2[] = {
- { 0x9bd5, 0, 8, 0x01 },
- { 0x9bd6, 0, 8, 0x06 },
- { 0x9bbe, 0, 8, 0x01 },
- { 0xd1a0, 1, 1, 0x01 },
- { 0xd000, 0, 1, 0x01 },
- { 0xd000, 1, 1, 0x00 },
- { 0xd001, 1, 1, 0x01 },
- { 0xd001, 0, 1, 0x00 },
- { 0xd001, 5, 1, 0x00 },
- { 0xd002, 0, 5, 0x19 },
- { 0xd003, 0, 5, 0x1a },
- { 0xd004, 0, 5, 0x19 },
- { 0xd005, 0, 5, 0x1a },
- { 0xd00e, 0, 5, 0x10 },
- { 0xd00f, 0, 3, 0x04 },
- { 0xd00f, 3, 3, 0x05 },
- { 0xd010, 0, 3, 0x04 },
- { 0xd010, 3, 3, 0x05 },
- { 0xd016, 4, 4, 0x03 },
- { 0xd01f, 0, 6, 0x0a },
- { 0xd020, 0, 6, 0x0a },
- { 0xd015, 0, 8, 0x46 },
- { 0xd016, 0, 1, 0x00 },
- { 0xd044, 0, 8, 0x46 },
- { 0xd045, 0, 1, 0x00 },
- { 0xd008, 0, 8, 0x0f },
- { 0xd009, 0, 2, 0x02 },
- { 0xd006, 0, 8, 0x32 },
- { 0xd007, 0, 2, 0x01 },
- { 0xd00c, 0, 8, 0x36 },
- { 0xd00d, 0, 2, 0x03 },
- { 0xd00a, 0, 8, 0x35 },
- { 0xd00b, 0, 2, 0x01 },
- { 0x9bc7, 0, 8, 0x07 },
- { 0x9bc8, 0, 8, 0x90 },
- { 0x9bc3, 0, 8, 0x0f },
- { 0x9bc4, 0, 8, 0x02 },
- { 0x9bc5, 0, 8, 0x36 },
- { 0x9bc6, 0, 8, 0x03 },
- { 0x9bba, 0, 8, 0xc9 },
- { 0x9bc9, 0, 8, 0x79 },
- { 0xd011, 0, 8, 0x10 },
- { 0xd012, 0, 2, 0x01 },
- { 0xd013, 0, 8, 0x45 },
- { 0xd014, 0, 2, 0x03 },
- { 0xd040, 0, 8, 0x98 },
- { 0xd041, 0, 2, 0x00 },
- { 0xd042, 0, 8, 0xcf },
- { 0xd043, 0, 2, 0x03 },
- { 0xd045, 1, 1, 0x00 },
- { 0x9bcf, 0, 8, 0x01 },
- { 0xd045, 2, 1, 0x01 },
- { 0xd04f, 0, 8, 0x9a },
- { 0xd050, 0, 1, 0x01 },
- { 0xd051, 0, 8, 0x5a },
- { 0xd052, 0, 1, 0x01 },
- { 0xd053, 0, 8, 0x96 },
- { 0xd054, 0, 8, 0x46 },
- { 0xd045, 7, 1, 0x00 },
- { 0x9bd7, 0, 8, 0x0a },
- { 0x9bd8, 0, 8, 0x14 },
- { 0x9bd9, 0, 8, 0x08 },
+/*
+ * Microtune MT2060 tuner init
+ * AF9013_TUNER_MT2060_2 0x93
+ */
+static const struct af9013_reg_mask_val tuner_init_tab_mt2060_2[] = {
+ {0x9bd5, 0xff, 0x01},
+ {0x9bd6, 0xff, 0x06},
+ {0x9bbe, 0xff, 0x01},
+ {0xd1a0, 0x02, 0x02},
+ {0xd000, 0x01, 0x01},
+ {0xd000, 0x02, 0x00},
+ {0xd001, 0x02, 0x02},
+ {0xd001, 0x01, 0x00},
+ {0xd001, 0x20, 0x00},
+ {0xd002, 0x1f, 0x19},
+ {0xd003, 0x1f, 0x1a},
+ {0xd004, 0x1f, 0x19},
+ {0xd005, 0x1f, 0x1a},
+ {0xd00e, 0x1f, 0x10},
+ {0xd00f, 0x07, 0x04},
+ {0xd00f, 0x38, 0x28},
+ {0xd010, 0x07, 0x04},
+ {0xd010, 0x38, 0x28},
+ {0xd016, 0xf0, 0x30},
+ {0xd01f, 0x3f, 0x0a},
+ {0xd020, 0x3f, 0x0a},
+ {0xd015, 0xff, 0x46},
+ {0xd016, 0x01, 0x00},
+ {0xd044, 0xff, 0x46},
+ {0xd045, 0x01, 0x00},
+ {0xd008, 0xff, 0x0f},
+ {0xd009, 0x03, 0x02},
+ {0xd006, 0xff, 0x32},
+ {0xd007, 0x03, 0x01},
+ {0xd00c, 0xff, 0x36},
+ {0xd00d, 0x03, 0x03},
+ {0xd00a, 0xff, 0x35},
+ {0xd00b, 0x03, 0x01},
+ {0x9bc7, 0xff, 0x07},
+ {0x9bc8, 0xff, 0x90},
+ {0x9bc3, 0xff, 0x0f},
+ {0x9bc4, 0xff, 0x02},
+ {0x9bc5, 0xff, 0x36},
+ {0x9bc6, 0xff, 0x03},
+ {0x9bba, 0xff, 0xc9},
+ {0x9bc9, 0xff, 0x79},
+ {0xd011, 0xff, 0x10},
+ {0xd012, 0x03, 0x01},
+ {0xd013, 0xff, 0x45},
+ {0xd014, 0x03, 0x03},
+ {0xd040, 0xff, 0x98},
+ {0xd041, 0x03, 0x00},
+ {0xd042, 0xff, 0xcf},
+ {0xd043, 0x03, 0x03},
+ {0xd045, 0x02, 0x00},
+ {0x9bcf, 0xff, 0x01},
+ {0xd045, 0x04, 0x04},
+ {0xd04f, 0xff, 0x9a},
+ {0xd050, 0x01, 0x01},
+ {0xd051, 0xff, 0x5a},
+ {0xd052, 0x01, 0x01},
+ {0xd053, 0xff, 0x96},
+ {0xd054, 0xff, 0x46},
+ {0xd045, 0x80, 0x00},
+ {0x9bd7, 0xff, 0x0a},
+ {0x9bd8, 0xff, 0x14},
+ {0x9bd9, 0xff, 0x08},
};
-/* MaxLinear MXL5003 tuner init
- AF9013_TUNER_MXL5003D = 3 */
-static const struct af9013_reg_bit tuner_init_mxl5003d[] = {
- { 0x9bd5, 0, 8, 0x01 },
- { 0x9bd6, 0, 8, 0x09 },
- { 0xd1a0, 1, 1, 0x01 },
- { 0xd000, 0, 1, 0x01 },
- { 0xd000, 1, 1, 0x00 },
- { 0xd001, 1, 1, 0x01 },
- { 0xd001, 0, 1, 0x00 },
- { 0xd001, 5, 1, 0x00 },
- { 0xd002, 0, 5, 0x19 },
- { 0xd003, 0, 5, 0x1a },
- { 0xd004, 0, 5, 0x19 },
- { 0xd005, 0, 5, 0x1a },
- { 0xd00e, 0, 5, 0x10 },
- { 0xd00f, 0, 3, 0x04 },
- { 0xd00f, 3, 3, 0x05 },
- { 0xd010, 0, 3, 0x04 },
- { 0xd010, 3, 3, 0x05 },
- { 0xd016, 4, 4, 0x03 },
- { 0xd01f, 0, 6, 0x0a },
- { 0xd020, 0, 6, 0x0a },
- { 0x9bda, 0, 8, 0x00 },
- { 0x9be3, 0, 8, 0x00 },
- { 0x9bfc, 0, 8, 0x0f },
- { 0x9bf6, 0, 8, 0x01 },
- { 0x9bbe, 0, 1, 0x01 },
- { 0xd015, 0, 8, 0x33 },
- { 0xd016, 0, 1, 0x00 },
- { 0xd044, 0, 8, 0x40 },
- { 0xd045, 0, 1, 0x00 },
- { 0xd008, 0, 8, 0x0f },
- { 0xd009, 0, 2, 0x02 },
- { 0xd006, 0, 8, 0x6c },
- { 0xd007, 0, 2, 0x00 },
- { 0xd00c, 0, 8, 0x3d },
- { 0xd00d, 0, 2, 0x00 },
- { 0xd00a, 0, 8, 0x45 },
- { 0xd00b, 0, 2, 0x01 },
- { 0x9bc7, 0, 8, 0x07 },
- { 0x9bc8, 0, 8, 0x52 },
- { 0x9bc3, 0, 8, 0x0f },
- { 0x9bc4, 0, 8, 0x02 },
- { 0x9bc5, 0, 8, 0x3d },
- { 0x9bc6, 0, 8, 0x00 },
- { 0x9bba, 0, 8, 0xa2 },
- { 0x9bc9, 0, 8, 0xa0 },
- { 0xd011, 0, 8, 0x56 },
- { 0xd012, 0, 2, 0x00 },
- { 0xd013, 0, 8, 0x50 },
- { 0xd014, 0, 2, 0x00 },
- { 0xd040, 0, 8, 0x56 },
- { 0xd041, 0, 2, 0x00 },
- { 0xd042, 0, 8, 0x50 },
- { 0xd043, 0, 2, 0x00 },
- { 0xd045, 1, 1, 0x00 },
- { 0x9bcf, 0, 8, 0x01 },
- { 0xd045, 2, 1, 0x01 },
- { 0xd04f, 0, 8, 0x9a },
- { 0xd050, 0, 1, 0x01 },
- { 0xd051, 0, 8, 0x5a },
- { 0xd052, 0, 1, 0x01 },
- { 0xd053, 0, 8, 0x50 },
- { 0xd054, 0, 8, 0x46 },
- { 0x9bd7, 0, 8, 0x0a },
- { 0x9bd8, 0, 8, 0x14 },
- { 0x9bd9, 0, 8, 0x08 },
+/*
+ * MaxLinear MXL5003 tuner init
+ * AF9013_TUNER_MXL5003D 0x03
+ */
+static const struct af9013_reg_mask_val tuner_init_tab_mxl5003d[] = {
+ {0x9bd5, 0xff, 0x01},
+ {0x9bd6, 0xff, 0x09},
+ {0xd1a0, 0x02, 0x02},
+ {0xd000, 0x01, 0x01},
+ {0xd000, 0x02, 0x00},
+ {0xd001, 0x02, 0x02},
+ {0xd001, 0x01, 0x00},
+ {0xd001, 0x20, 0x00},
+ {0xd002, 0x1f, 0x19},
+ {0xd003, 0x1f, 0x1a},
+ {0xd004, 0x1f, 0x19},
+ {0xd005, 0x1f, 0x1a},
+ {0xd00e, 0x1f, 0x10},
+ {0xd00f, 0x07, 0x04},
+ {0xd00f, 0x38, 0x28},
+ {0xd010, 0x07, 0x04},
+ {0xd010, 0x38, 0x28},
+ {0xd016, 0xf0, 0x30},
+ {0xd01f, 0x3f, 0x0a},
+ {0xd020, 0x3f, 0x0a},
+ {0x9bda, 0xff, 0x00},
+ {0x9be3, 0xff, 0x00},
+ {0x9bfc, 0xff, 0x0f},
+ {0x9bf6, 0xff, 0x01},
+ {0x9bbe, 0x01, 0x01},
+ {0xd015, 0xff, 0x33},
+ {0xd016, 0x01, 0x00},
+ {0xd044, 0xff, 0x40},
+ {0xd045, 0x01, 0x00},
+ {0xd008, 0xff, 0x0f},
+ {0xd009, 0x03, 0x02},
+ {0xd006, 0xff, 0x6c},
+ {0xd007, 0x03, 0x00},
+ {0xd00c, 0xff, 0x3d},
+ {0xd00d, 0x03, 0x00},
+ {0xd00a, 0xff, 0x45},
+ {0xd00b, 0x03, 0x01},
+ {0x9bc7, 0xff, 0x07},
+ {0x9bc8, 0xff, 0x52},
+ {0x9bc3, 0xff, 0x0f},
+ {0x9bc4, 0xff, 0x02},
+ {0x9bc5, 0xff, 0x3d},
+ {0x9bc6, 0xff, 0x00},
+ {0x9bba, 0xff, 0xa2},
+ {0x9bc9, 0xff, 0xa0},
+ {0xd011, 0xff, 0x56},
+ {0xd012, 0x03, 0x00},
+ {0xd013, 0xff, 0x50},
+ {0xd014, 0x03, 0x00},
+ {0xd040, 0xff, 0x56},
+ {0xd041, 0x03, 0x00},
+ {0xd042, 0xff, 0x50},
+ {0xd043, 0x03, 0x00},
+ {0xd045, 0x02, 0x00},
+ {0x9bcf, 0xff, 0x01},
+ {0xd045, 0x04, 0x04},
+ {0xd04f, 0xff, 0x9a},
+ {0xd050, 0x01, 0x01},
+ {0xd051, 0xff, 0x5a},
+ {0xd052, 0x01, 0x01},
+ {0xd053, 0xff, 0x50},
+ {0xd054, 0xff, 0x46},
+ {0x9bd7, 0xff, 0x0a},
+ {0x9bd8, 0xff, 0x14},
+ {0x9bd9, 0xff, 0x08},
};
-/* MaxLinear MXL5005S & MXL5007T tuner init
- AF9013_TUNER_MXL5005D = 13
- AF9013_TUNER_MXL5005R = 30
- AF9013_TUNER_MXL5007T = 177 */
-static const struct af9013_reg_bit tuner_init_mxl5005[] = {
- { 0x9bd5, 0, 8, 0x01 },
- { 0x9bd6, 0, 8, 0x07 },
- { 0xd1a0, 1, 1, 0x01 },
- { 0xd000, 0, 1, 0x01 },
- { 0xd000, 1, 1, 0x00 },
- { 0xd001, 1, 1, 0x01 },
- { 0xd001, 0, 1, 0x00 },
- { 0xd001, 5, 1, 0x00 },
- { 0xd002, 0, 5, 0x19 },
- { 0xd003, 0, 5, 0x1a },
- { 0xd004, 0, 5, 0x19 },
- { 0xd005, 0, 5, 0x1a },
- { 0xd00e, 0, 5, 0x10 },
- { 0xd00f, 0, 3, 0x04 },
- { 0xd00f, 3, 3, 0x05 },
- { 0xd010, 0, 3, 0x04 },
- { 0xd010, 3, 3, 0x05 },
- { 0xd016, 4, 4, 0x03 },
- { 0xd01f, 0, 6, 0x0a },
- { 0xd020, 0, 6, 0x0a },
- { 0x9bda, 0, 8, 0x01 },
- { 0x9be3, 0, 8, 0x01 },
- { 0x9bbe, 0, 1, 0x01 },
- { 0x9bcc, 0, 1, 0x01 },
- { 0x9bb9, 0, 8, 0x00 },
- { 0x9bcd, 0, 8, 0x28 },
- { 0x9bff, 0, 8, 0x24 },
- { 0xd015, 0, 8, 0x40 },
- { 0xd016, 0, 1, 0x00 },
- { 0xd044, 0, 8, 0x40 },
- { 0xd045, 0, 1, 0x00 },
- { 0xd008, 0, 8, 0x0f },
- { 0xd009, 0, 2, 0x02 },
- { 0xd006, 0, 8, 0x73 },
- { 0xd007, 0, 2, 0x01 },
- { 0xd00c, 0, 8, 0xfa },
- { 0xd00d, 0, 2, 0x01 },
- { 0xd00a, 0, 8, 0xff },
- { 0xd00b, 0, 2, 0x01 },
- { 0x9bc7, 0, 8, 0x23 },
- { 0x9bc8, 0, 8, 0x55 },
- { 0x9bc3, 0, 8, 0x01 },
- { 0x9bc4, 0, 8, 0x02 },
- { 0x9bc5, 0, 8, 0xfa },
- { 0x9bc6, 0, 8, 0x01 },
- { 0x9bba, 0, 8, 0xff },
- { 0x9bc9, 0, 8, 0xff },
- { 0x9bd3, 0, 8, 0x95 },
- { 0xd011, 0, 8, 0x70 },
- { 0xd012, 0, 2, 0x01 },
- { 0xd013, 0, 8, 0xfb },
- { 0xd014, 0, 2, 0x01 },
- { 0xd040, 0, 8, 0x70 },
- { 0xd041, 0, 2, 0x01 },
- { 0xd042, 0, 8, 0xfb },
- { 0xd043, 0, 2, 0x01 },
- { 0xd045, 1, 1, 0x00 },
- { 0x9bcf, 0, 1, 0x01 },
- { 0xd045, 2, 1, 0x01 },
- { 0xd04f, 0, 8, 0x9a },
- { 0xd050, 0, 1, 0x01 },
- { 0xd051, 0, 8, 0x5a },
- { 0xd052, 0, 1, 0x01 },
- { 0xd053, 0, 8, 0x50 },
- { 0xd054, 0, 8, 0x46 },
- { 0x9bd7, 0, 8, 0x0a },
- { 0x9bd8, 0, 8, 0x14 },
- { 0x9bd9, 0, 8, 0x08 },
- { 0x9bd0, 0, 8, 0x93 },
- { 0x9be4, 0, 8, 0xfe },
- { 0x9bbd, 0, 8, 0x63 },
- { 0x9be2, 0, 8, 0xfe },
- { 0x9bee, 0, 1, 0x01 },
+/*
+ * MaxLinear MXL5005S & MXL5007T tuner init
+ * AF9013_TUNER_MXL5005D 0x0d
+ * AF9013_TUNER_MXL5005R 0x1e
+ * AF9013_TUNER_MXL5007T 0xb1
+ */
+static const struct af9013_reg_mask_val tuner_init_tab_mxl5005[] = {
+ {0x9bd5, 0xff, 0x01},
+ {0x9bd6, 0xff, 0x07},
+ {0xd1a0, 0x02, 0x02},
+ {0xd000, 0x01, 0x01},
+ {0xd000, 0x02, 0x00},
+ {0xd001, 0x02, 0x02},
+ {0xd001, 0x01, 0x00},
+ {0xd001, 0x20, 0x00},
+ {0xd002, 0x1f, 0x19},
+ {0xd003, 0x1f, 0x1a},
+ {0xd004, 0x1f, 0x19},
+ {0xd005, 0x1f, 0x1a},
+ {0xd00e, 0x1f, 0x10},
+ {0xd00f, 0x07, 0x04},
+ {0xd00f, 0x38, 0x28},
+ {0xd010, 0x07, 0x04},
+ {0xd010, 0x38, 0x28},
+ {0xd016, 0xf0, 0x30},
+ {0xd01f, 0x3f, 0x0a},
+ {0xd020, 0x3f, 0x0a},
+ {0x9bda, 0xff, 0x01},
+ {0x9be3, 0xff, 0x01},
+ {0x9bbe, 0x01, 0x01},
+ {0x9bcc, 0x01, 0x01},
+ {0x9bb9, 0xff, 0x00},
+ {0x9bcd, 0xff, 0x28},
+ {0x9bff, 0xff, 0x24},
+ {0xd015, 0xff, 0x40},
+ {0xd016, 0x01, 0x00},
+ {0xd044, 0xff, 0x40},
+ {0xd045, 0x01, 0x00},
+ {0xd008, 0xff, 0x0f},
+ {0xd009, 0x03, 0x02},
+ {0xd006, 0xff, 0x73},
+ {0xd007, 0x03, 0x01},
+ {0xd00c, 0xff, 0xfa},
+ {0xd00d, 0x03, 0x01},
+ {0xd00a, 0xff, 0xff},
+ {0xd00b, 0x03, 0x01},
+ {0x9bc7, 0xff, 0x23},
+ {0x9bc8, 0xff, 0x55},
+ {0x9bc3, 0xff, 0x01},
+ {0x9bc4, 0xff, 0x02},
+ {0x9bc5, 0xff, 0xfa},
+ {0x9bc6, 0xff, 0x01},
+ {0x9bba, 0xff, 0xff},
+ {0x9bc9, 0xff, 0xff},
+ {0x9bd3, 0xff, 0x95},
+ {0xd011, 0xff, 0x70},
+ {0xd012, 0x03, 0x01},
+ {0xd013, 0xff, 0xfb},
+ {0xd014, 0x03, 0x01},
+ {0xd040, 0xff, 0x70},
+ {0xd041, 0x03, 0x01},
+ {0xd042, 0xff, 0xfb},
+ {0xd043, 0x03, 0x01},
+ {0xd045, 0x02, 0x00},
+ {0x9bcf, 0x01, 0x01},
+ {0xd045, 0x04, 0x04},
+ {0xd04f, 0xff, 0x9a},
+ {0xd050, 0x01, 0x01},
+ {0xd051, 0xff, 0x5a},
+ {0xd052, 0x01, 0x01},
+ {0xd053, 0xff, 0x50},
+ {0xd054, 0xff, 0x46},
+ {0x9bd7, 0xff, 0x0a},
+ {0x9bd8, 0xff, 0x14},
+ {0x9bd9, 0xff, 0x08},
+ {0x9bd0, 0xff, 0x93},
+ {0x9be4, 0xff, 0xfe},
+ {0x9bbd, 0xff, 0x63},
+ {0x9be2, 0xff, 0xfe},
+ {0x9bee, 0x01, 0x01},
};
-/* Quantek QT1010 tuner init
- AF9013_TUNER_QT1010 = 134
- AF9013_TUNER_QT1010A = 162 */
-static const struct af9013_reg_bit tuner_init_qt1010[] = {
- { 0x9bd5, 0, 8, 0x01 },
- { 0x9bd6, 0, 8, 0x09 },
- { 0xd1a0, 1, 1, 0x01 },
- { 0xd000, 0, 1, 0x01 },
- { 0xd000, 1, 1, 0x00 },
- { 0xd001, 1, 1, 0x01 },
- { 0xd001, 0, 1, 0x00 },
- { 0xd001, 5, 1, 0x00 },
- { 0xd002, 0, 5, 0x19 },
- { 0xd003, 0, 5, 0x1a },
- { 0xd004, 0, 5, 0x19 },
- { 0xd005, 0, 5, 0x1a },
- { 0xd00e, 0, 5, 0x10 },
- { 0xd00f, 0, 3, 0x04 },
- { 0xd00f, 3, 3, 0x05 },
- { 0xd010, 0, 3, 0x04 },
- { 0xd010, 3, 3, 0x05 },
- { 0xd016, 4, 4, 0x03 },
- { 0xd01f, 0, 6, 0x0a },
- { 0xd020, 0, 6, 0x0a },
- { 0x9bda, 0, 8, 0x01 },
- { 0x9be3, 0, 8, 0x01 },
- { 0xd015, 0, 8, 0x46 },
- { 0xd016, 0, 1, 0x00 },
- { 0xd044, 0, 8, 0x46 },
- { 0xd045, 0, 1, 0x00 },
- { 0x9bbe, 0, 1, 0x01 },
- { 0x9bcc, 0, 1, 0x01 },
- { 0x9bb9, 0, 8, 0x00 },
- { 0x9bcd, 0, 8, 0x28 },
- { 0x9bff, 0, 8, 0x20 },
- { 0xd008, 0, 8, 0x0f },
- { 0xd009, 0, 2, 0x02 },
- { 0xd006, 0, 8, 0x99 },
- { 0xd007, 0, 2, 0x01 },
- { 0xd00c, 0, 8, 0x0f },
- { 0xd00d, 0, 2, 0x02 },
- { 0xd00a, 0, 8, 0x50 },
- { 0xd00b, 0, 2, 0x01 },
- { 0x9bc7, 0, 8, 0x00 },
- { 0x9bc8, 0, 8, 0x00 },
- { 0x9bc3, 0, 8, 0x0f },
- { 0x9bc4, 0, 8, 0x02 },
- { 0x9bc5, 0, 8, 0x0f },
- { 0x9bc6, 0, 8, 0x02 },
- { 0x9bba, 0, 8, 0xc5 },
- { 0x9bc9, 0, 8, 0xff },
- { 0xd011, 0, 8, 0x58 },
- { 0xd012, 0, 2, 0x02 },
- { 0xd013, 0, 8, 0x89 },
- { 0xd014, 0, 2, 0x01 },
- { 0xd040, 0, 8, 0x58 },
- { 0xd041, 0, 2, 0x02 },
- { 0xd042, 0, 8, 0x89 },
- { 0xd043, 0, 2, 0x01 },
- { 0xd045, 1, 1, 0x00 },
- { 0x9bcf, 0, 1, 0x01 },
- { 0xd045, 2, 1, 0x01 },
- { 0xd04f, 0, 8, 0x9a },
- { 0xd050, 0, 1, 0x01 },
- { 0xd051, 0, 8, 0x5a },
- { 0xd052, 0, 1, 0x01 },
- { 0xd053, 0, 8, 0x50 },
- { 0xd054, 0, 8, 0x46 },
- { 0x9bd7, 0, 8, 0x0a },
- { 0x9bd8, 0, 8, 0x14 },
- { 0x9bd9, 0, 8, 0x08 },
- { 0x9bd0, 0, 8, 0xcd },
- { 0x9be4, 0, 8, 0xbb },
- { 0x9bbd, 0, 8, 0x93 },
- { 0x9be2, 0, 8, 0x80 },
- { 0x9bee, 0, 1, 0x01 },
+/*
+ * Quantek QT1010 tuner init
+ * AF9013_TUNER_QT1010 0x86
+ * AF9013_TUNER_QT1010A 0xa2
+ */
+static const struct af9013_reg_mask_val tuner_init_tab_qt1010[] = {
+ {0x9bd5, 0xff, 0x01},
+ {0x9bd6, 0xff, 0x09},
+ {0xd1a0, 0x02, 0x02},
+ {0xd000, 0x01, 0x01},
+ {0xd000, 0x02, 0x00},
+ {0xd001, 0x02, 0x02},
+ {0xd001, 0x01, 0x00},
+ {0xd001, 0x20, 0x00},
+ {0xd002, 0x1f, 0x19},
+ {0xd003, 0x1f, 0x1a},
+ {0xd004, 0x1f, 0x19},
+ {0xd005, 0x1f, 0x1a},
+ {0xd00e, 0x1f, 0x10},
+ {0xd00f, 0x07, 0x04},
+ {0xd00f, 0x38, 0x28},
+ {0xd010, 0x07, 0x04},
+ {0xd010, 0x38, 0x28},
+ {0xd016, 0xf0, 0x30},
+ {0xd01f, 0x3f, 0x0a},
+ {0xd020, 0x3f, 0x0a},
+ {0x9bda, 0xff, 0x01},
+ {0x9be3, 0xff, 0x01},
+ {0xd015, 0xff, 0x46},
+ {0xd016, 0x01, 0x00},
+ {0xd044, 0xff, 0x46},
+ {0xd045, 0x01, 0x00},
+ {0x9bbe, 0x01, 0x01},
+ {0x9bcc, 0x01, 0x01},
+ {0x9bb9, 0xff, 0x00},
+ {0x9bcd, 0xff, 0x28},
+ {0x9bff, 0xff, 0x20},
+ {0xd008, 0xff, 0x0f},
+ {0xd009, 0x03, 0x02},
+ {0xd006, 0xff, 0x99},
+ {0xd007, 0x03, 0x01},
+ {0xd00c, 0xff, 0x0f},
+ {0xd00d, 0x03, 0x02},
+ {0xd00a, 0xff, 0x50},
+ {0xd00b, 0x03, 0x01},
+ {0x9bc7, 0xff, 0x00},
+ {0x9bc8, 0xff, 0x00},
+ {0x9bc3, 0xff, 0x0f},
+ {0x9bc4, 0xff, 0x02},
+ {0x9bc5, 0xff, 0x0f},
+ {0x9bc6, 0xff, 0x02},
+ {0x9bba, 0xff, 0xc5},
+ {0x9bc9, 0xff, 0xff},
+ {0xd011, 0xff, 0x58},
+ {0xd012, 0x03, 0x02},
+ {0xd013, 0xff, 0x89},
+ {0xd014, 0x03, 0x01},
+ {0xd040, 0xff, 0x58},
+ {0xd041, 0x03, 0x02},
+ {0xd042, 0xff, 0x89},
+ {0xd043, 0x03, 0x01},
+ {0xd045, 0x02, 0x00},
+ {0x9bcf, 0x01, 0x01},
+ {0xd045, 0x04, 0x04},
+ {0xd04f, 0xff, 0x9a},
+ {0xd050, 0x01, 0x01},
+ {0xd051, 0xff, 0x5a},
+ {0xd052, 0x01, 0x01},
+ {0xd053, 0xff, 0x50},
+ {0xd054, 0xff, 0x46},
+ {0x9bd7, 0xff, 0x0a},
+ {0x9bd8, 0xff, 0x14},
+ {0x9bd9, 0xff, 0x08},
+ {0x9bd0, 0xff, 0xcd},
+ {0x9be4, 0xff, 0xbb},
+ {0x9bbd, 0xff, 0x93},
+ {0x9be2, 0xff, 0x80},
+ {0x9bee, 0x01, 0x01},
};
-/* Freescale MC44S803 tuner init
- AF9013_TUNER_MC44S803 = 133 */
-static const struct af9013_reg_bit tuner_init_mc44s803[] = {
- { 0x9bd5, 0, 8, 0x01 },
- { 0x9bd6, 0, 8, 0x06 },
- { 0xd1a0, 1, 1, 0x01 },
- { 0xd000, 0, 1, 0x01 },
- { 0xd000, 1, 1, 0x00 },
- { 0xd001, 1, 1, 0x01 },
- { 0xd001, 0, 1, 0x00 },
- { 0xd001, 5, 1, 0x00 },
- { 0xd002, 0, 5, 0x19 },
- { 0xd003, 0, 5, 0x1a },
- { 0xd004, 0, 5, 0x19 },
- { 0xd005, 0, 5, 0x1a },
- { 0xd00e, 0, 5, 0x10 },
- { 0xd00f, 0, 3, 0x04 },
- { 0xd00f, 3, 3, 0x05 },
- { 0xd010, 0, 3, 0x04 },
- { 0xd010, 3, 3, 0x05 },
- { 0xd016, 4, 4, 0x03 },
- { 0xd01f, 0, 6, 0x0a },
- { 0xd020, 0, 6, 0x0a },
- { 0x9bda, 0, 8, 0x00 },
- { 0x9be3, 0, 8, 0x00 },
- { 0x9bf6, 0, 8, 0x01 },
- { 0x9bf8, 0, 8, 0x02 },
- { 0x9bf9, 0, 8, 0x02 },
- { 0x9bfc, 0, 8, 0x1f },
- { 0x9bbe, 0, 1, 0x01 },
- { 0x9bcc, 0, 1, 0x01 },
- { 0x9bb9, 0, 8, 0x00 },
- { 0x9bcd, 0, 8, 0x24 },
- { 0x9bff, 0, 8, 0x24 },
- { 0xd015, 0, 8, 0x46 },
- { 0xd016, 0, 1, 0x00 },
- { 0xd044, 0, 8, 0x46 },
- { 0xd045, 0, 1, 0x00 },
- { 0xd008, 0, 8, 0x01 },
- { 0xd009, 0, 2, 0x02 },
- { 0xd006, 0, 8, 0x7b },
- { 0xd007, 0, 2, 0x00 },
- { 0xd00c, 0, 8, 0x7c },
- { 0xd00d, 0, 2, 0x02 },
- { 0xd00a, 0, 8, 0xfe },
- { 0xd00b, 0, 2, 0x01 },
- { 0x9bc7, 0, 8, 0x08 },
- { 0x9bc8, 0, 8, 0x9a },
- { 0x9bc3, 0, 8, 0x01 },
- { 0x9bc4, 0, 8, 0x02 },
- { 0x9bc5, 0, 8, 0x7c },
- { 0x9bc6, 0, 8, 0x02 },
- { 0x9bba, 0, 8, 0xfc },
- { 0x9bc9, 0, 8, 0xaa },
- { 0xd011, 0, 8, 0x6b },
- { 0xd012, 0, 2, 0x00 },
- { 0xd013, 0, 8, 0x88 },
- { 0xd014, 0, 2, 0x02 },
- { 0xd040, 0, 8, 0x6b },
- { 0xd041, 0, 2, 0x00 },
- { 0xd042, 0, 8, 0x7c },
- { 0xd043, 0, 2, 0x02 },
- { 0xd045, 1, 1, 0x00 },
- { 0x9bcf, 0, 1, 0x01 },
- { 0xd045, 2, 1, 0x01 },
- { 0xd04f, 0, 8, 0x9a },
- { 0xd050, 0, 1, 0x01 },
- { 0xd051, 0, 8, 0x5a },
- { 0xd052, 0, 1, 0x01 },
- { 0xd053, 0, 8, 0x50 },
- { 0xd054, 0, 8, 0x46 },
- { 0x9bd7, 0, 8, 0x0a },
- { 0x9bd8, 0, 8, 0x14 },
- { 0x9bd9, 0, 8, 0x08 },
- { 0x9bd0, 0, 8, 0x9e },
- { 0x9be4, 0, 8, 0xff },
- { 0x9bbd, 0, 8, 0x9e },
- { 0x9be2, 0, 8, 0x25 },
- { 0x9bee, 0, 1, 0x01 },
- { 0xd73b, 3, 1, 0x00 },
+/*
+ * Freescale MC44S803 tuner init
+ * AF9013_TUNER_MC44S803 0x85
+ */
+static const struct af9013_reg_mask_val tuner_init_tab_mc44s803[] = {
+ {0x9bd5, 0xff, 0x01},
+ {0x9bd6, 0xff, 0x06},
+ {0xd1a0, 0x02, 0x02},
+ {0xd000, 0x01, 0x01},
+ {0xd000, 0x02, 0x00},
+ {0xd001, 0x02, 0x02},
+ {0xd001, 0x01, 0x00},
+ {0xd001, 0x20, 0x00},
+ {0xd002, 0x1f, 0x19},
+ {0xd003, 0x1f, 0x1a},
+ {0xd004, 0x1f, 0x19},
+ {0xd005, 0x1f, 0x1a},
+ {0xd00e, 0x1f, 0x10},
+ {0xd00f, 0x07, 0x04},
+ {0xd00f, 0x38, 0x28},
+ {0xd010, 0x07, 0x04},
+ {0xd010, 0x38, 0x28},
+ {0xd016, 0xf0, 0x30},
+ {0xd01f, 0x3f, 0x0a},
+ {0xd020, 0x3f, 0x0a},
+ {0x9bda, 0xff, 0x00},
+ {0x9be3, 0xff, 0x00},
+ {0x9bf6, 0xff, 0x01},
+ {0x9bf8, 0xff, 0x02},
+ {0x9bf9, 0xff, 0x02},
+ {0x9bfc, 0xff, 0x1f},
+ {0x9bbe, 0x01, 0x01},
+ {0x9bcc, 0x01, 0x01},
+ {0x9bb9, 0xff, 0x00},
+ {0x9bcd, 0xff, 0x24},
+ {0x9bff, 0xff, 0x24},
+ {0xd015, 0xff, 0x46},
+ {0xd016, 0x01, 0x00},
+ {0xd044, 0xff, 0x46},
+ {0xd045, 0x01, 0x00},
+ {0xd008, 0xff, 0x01},
+ {0xd009, 0x03, 0x02},
+ {0xd006, 0xff, 0x7b},
+ {0xd007, 0x03, 0x00},
+ {0xd00c, 0xff, 0x7c},
+ {0xd00d, 0x03, 0x02},
+ {0xd00a, 0xff, 0xfe},
+ {0xd00b, 0x03, 0x01},
+ {0x9bc7, 0xff, 0x08},
+ {0x9bc8, 0xff, 0x9a},
+ {0x9bc3, 0xff, 0x01},
+ {0x9bc4, 0xff, 0x02},
+ {0x9bc5, 0xff, 0x7c},
+ {0x9bc6, 0xff, 0x02},
+ {0x9bba, 0xff, 0xfc},
+ {0x9bc9, 0xff, 0xaa},
+ {0xd011, 0xff, 0x6b},
+ {0xd012, 0x03, 0x00},
+ {0xd013, 0xff, 0x88},
+ {0xd014, 0x03, 0x02},
+ {0xd040, 0xff, 0x6b},
+ {0xd041, 0x03, 0x00},
+ {0xd042, 0xff, 0x7c},
+ {0xd043, 0x03, 0x02},
+ {0xd045, 0x02, 0x00},
+ {0x9bcf, 0x01, 0x01},
+ {0xd045, 0x04, 0x04},
+ {0xd04f, 0xff, 0x9a},
+ {0xd050, 0x01, 0x01},
+ {0xd051, 0xff, 0x5a},
+ {0xd052, 0x01, 0x01},
+ {0xd053, 0xff, 0x50},
+ {0xd054, 0xff, 0x46},
+ {0x9bd7, 0xff, 0x0a},
+ {0x9bd8, 0xff, 0x14},
+ {0x9bd9, 0xff, 0x08},
+ {0x9bd0, 0xff, 0x9e},
+ {0x9be4, 0xff, 0xff},
+ {0x9bbd, 0xff, 0x9e},
+ {0x9be2, 0xff, 0x25},
+ {0x9bee, 0x01, 0x01},
+ {0xd73b, 0x08, 0x00},
};
-/* unknown, probably for tin can tuner, tuner init
- AF9013_TUNER_UNKNOWN = 140 */
-static const struct af9013_reg_bit tuner_init_unknown[] = {
- { 0x9bd5, 0, 8, 0x01 },
- { 0x9bd6, 0, 8, 0x02 },
- { 0xd1a0, 1, 1, 0x01 },
- { 0xd000, 0, 1, 0x01 },
- { 0xd000, 1, 1, 0x00 },
- { 0xd001, 1, 1, 0x01 },
- { 0xd001, 0, 1, 0x00 },
- { 0xd001, 5, 1, 0x00 },
- { 0xd002, 0, 5, 0x19 },
- { 0xd003, 0, 5, 0x1a },
- { 0xd004, 0, 5, 0x19 },
- { 0xd005, 0, 5, 0x1a },
- { 0xd00e, 0, 5, 0x10 },
- { 0xd00f, 0, 3, 0x04 },
- { 0xd00f, 3, 3, 0x05 },
- { 0xd010, 0, 3, 0x04 },
- { 0xd010, 3, 3, 0x05 },
- { 0xd016, 4, 4, 0x03 },
- { 0xd01f, 0, 6, 0x0a },
- { 0xd020, 0, 6, 0x0a },
- { 0x9bda, 0, 8, 0x01 },
- { 0x9be3, 0, 8, 0x01 },
- { 0xd1a0, 1, 1, 0x00 },
- { 0x9bbe, 0, 1, 0x01 },
- { 0x9bcc, 0, 1, 0x01 },
- { 0x9bb9, 0, 8, 0x00 },
- { 0x9bcd, 0, 8, 0x18 },
- { 0x9bff, 0, 8, 0x2c },
- { 0xd015, 0, 8, 0x46 },
- { 0xd016, 0, 1, 0x00 },
- { 0xd044, 0, 8, 0x46 },
- { 0xd045, 0, 1, 0x00 },
- { 0xd008, 0, 8, 0xdf },
- { 0xd009, 0, 2, 0x02 },
- { 0xd006, 0, 8, 0x44 },
- { 0xd007, 0, 2, 0x01 },
- { 0xd00c, 0, 8, 0x00 },
- { 0xd00d, 0, 2, 0x02 },
- { 0xd00a, 0, 8, 0xf6 },
- { 0xd00b, 0, 2, 0x01 },
- { 0x9bba, 0, 8, 0xf9 },
- { 0x9bc8, 0, 8, 0xaa },
- { 0x9bc3, 0, 8, 0xdf },
- { 0x9bc4, 0, 8, 0x02 },
- { 0x9bc5, 0, 8, 0x00 },
- { 0x9bc6, 0, 8, 0x02 },
- { 0x9bc9, 0, 8, 0xf0 },
- { 0xd011, 0, 8, 0x3c },
- { 0xd012, 0, 2, 0x01 },
- { 0xd013, 0, 8, 0xf7 },
- { 0xd014, 0, 2, 0x02 },
- { 0xd040, 0, 8, 0x0b },
- { 0xd041, 0, 2, 0x02 },
- { 0xd042, 0, 8, 0x4d },
- { 0xd043, 0, 2, 0x00 },
- { 0xd045, 1, 1, 0x00 },
- { 0x9bcf, 0, 1, 0x01 },
- { 0xd045, 2, 1, 0x01 },
- { 0xd04f, 0, 8, 0x9a },
- { 0xd050, 0, 1, 0x01 },
- { 0xd051, 0, 8, 0x5a },
- { 0xd052, 0, 1, 0x01 },
- { 0xd053, 0, 8, 0x50 },
- { 0xd054, 0, 8, 0x46 },
- { 0x9bd7, 0, 8, 0x0a },
- { 0x9bd8, 0, 8, 0x14 },
- { 0x9bd9, 0, 8, 0x08 },
+/*
+ * Unknown, probably for tin can tuner, tuner init
+ * AF9013_TUNER_UNKNOWN 0x8c
+ */
+static const struct af9013_reg_mask_val tuner_init_tab_unknown[] = {
+ {0x9bd5, 0xff, 0x01},
+ {0x9bd6, 0xff, 0x02},
+ {0xd1a0, 0x02, 0x02},
+ {0xd000, 0x01, 0x01},
+ {0xd000, 0x02, 0x00},
+ {0xd001, 0x02, 0x02},
+ {0xd001, 0x01, 0x00},
+ {0xd001, 0x20, 0x00},
+ {0xd002, 0x1f, 0x19},
+ {0xd003, 0x1f, 0x1a},
+ {0xd004, 0x1f, 0x19},
+ {0xd005, 0x1f, 0x1a},
+ {0xd00e, 0x1f, 0x10},
+ {0xd00f, 0x07, 0x04},
+ {0xd00f, 0x38, 0x28},
+ {0xd010, 0x07, 0x04},
+ {0xd010, 0x38, 0x28},
+ {0xd016, 0xf0, 0x30},
+ {0xd01f, 0x3f, 0x0a},
+ {0xd020, 0x3f, 0x0a},
+ {0x9bda, 0xff, 0x01},
+ {0x9be3, 0xff, 0x01},
+ {0xd1a0, 0x02, 0x00},
+ {0x9bbe, 0x01, 0x01},
+ {0x9bcc, 0x01, 0x01},
+ {0x9bb9, 0xff, 0x00},
+ {0x9bcd, 0xff, 0x18},
+ {0x9bff, 0xff, 0x2c},
+ {0xd015, 0xff, 0x46},
+ {0xd016, 0x01, 0x00},
+ {0xd044, 0xff, 0x46},
+ {0xd045, 0x01, 0x00},
+ {0xd008, 0xff, 0xdf},
+ {0xd009, 0x03, 0x02},
+ {0xd006, 0xff, 0x44},
+ {0xd007, 0x03, 0x01},
+ {0xd00c, 0xff, 0x00},
+ {0xd00d, 0x03, 0x02},
+ {0xd00a, 0xff, 0xf6},
+ {0xd00b, 0x03, 0x01},
+ {0x9bba, 0xff, 0xf9},
+ {0x9bc8, 0xff, 0xaa},
+ {0x9bc3, 0xff, 0xdf},
+ {0x9bc4, 0xff, 0x02},
+ {0x9bc5, 0xff, 0x00},
+ {0x9bc6, 0xff, 0x02},
+ {0x9bc9, 0xff, 0xf0},
+ {0xd011, 0xff, 0x3c},
+ {0xd012, 0x03, 0x01},
+ {0xd013, 0xff, 0xf7},
+ {0xd014, 0x03, 0x02},
+ {0xd040, 0xff, 0x0b},
+ {0xd041, 0x03, 0x02},
+ {0xd042, 0xff, 0x4d},
+ {0xd043, 0x03, 0x00},
+ {0xd045, 0x02, 0x00},
+ {0x9bcf, 0x01, 0x01},
+ {0xd045, 0x04, 0x04},
+ {0xd04f, 0xff, 0x9a},
+ {0xd050, 0x01, 0x01},
+ {0xd051, 0xff, 0x5a},
+ {0xd052, 0x01, 0x01},
+ {0xd053, 0xff, 0x50},
+ {0xd054, 0xff, 0x46},
+ {0x9bd7, 0xff, 0x0a},
+ {0x9bd8, 0xff, 0x14},
+ {0x9bd9, 0xff, 0x08},
};
-/* NXP TDA18271 & TDA18218 tuner init
- AF9013_TUNER_TDA18271 = 156
- AF9013_TUNER_TDA18218 = 179 */
-static const struct af9013_reg_bit tuner_init_tda18271[] = {
- { 0x9bd5, 0, 8, 0x01 },
- { 0x9bd6, 0, 8, 0x04 },
- { 0xd1a0, 1, 1, 0x01 },
- { 0xd000, 0, 1, 0x01 },
- { 0xd000, 1, 1, 0x00 },
- { 0xd001, 1, 1, 0x01 },
- { 0xd001, 0, 1, 0x00 },
- { 0xd001, 5, 1, 0x00 },
- { 0xd002, 0, 5, 0x19 },
- { 0xd003, 0, 5, 0x1a },
- { 0xd004, 0, 5, 0x19 },
- { 0xd005, 0, 5, 0x1a },
- { 0xd00e, 0, 5, 0x10 },
- { 0xd00f, 0, 3, 0x04 },
- { 0xd00f, 3, 3, 0x05 },
- { 0xd010, 0, 3, 0x04 },
- { 0xd010, 3, 3, 0x05 },
- { 0xd016, 4, 4, 0x03 },
- { 0xd01f, 0, 6, 0x0a },
- { 0xd020, 0, 6, 0x0a },
- { 0x9bda, 0, 8, 0x01 },
- { 0x9be3, 0, 8, 0x01 },
- { 0xd1a0, 1, 1, 0x00 },
- { 0x9bbe, 0, 1, 0x01 },
- { 0x9bcc, 0, 1, 0x01 },
- { 0x9bb9, 0, 8, 0x00 },
- { 0x9bcd, 0, 8, 0x18 },
- { 0x9bff, 0, 8, 0x2c },
- { 0xd015, 0, 8, 0x46 },
- { 0xd016, 0, 1, 0x00 },
- { 0xd044, 0, 8, 0x46 },
- { 0xd045, 0, 1, 0x00 },
- { 0xd008, 0, 8, 0xdf },
- { 0xd009, 0, 2, 0x02 },
- { 0xd006, 0, 8, 0x44 },
- { 0xd007, 0, 2, 0x01 },
- { 0xd00c, 0, 8, 0x00 },
- { 0xd00d, 0, 2, 0x02 },
- { 0xd00a, 0, 8, 0xf6 },
- { 0xd00b, 0, 2, 0x01 },
- { 0x9bba, 0, 8, 0xf9 },
- { 0x9bc8, 0, 8, 0xaa },
- { 0x9bc3, 0, 8, 0xdf },
- { 0x9bc4, 0, 8, 0x02 },
- { 0x9bc5, 0, 8, 0x00 },
- { 0x9bc6, 0, 8, 0x02 },
- { 0x9bc9, 0, 8, 0xf0 },
- { 0xd011, 0, 8, 0x3c },
- { 0xd012, 0, 2, 0x01 },
- { 0xd013, 0, 8, 0xf7 },
- { 0xd014, 0, 2, 0x02 },
- { 0xd040, 0, 8, 0x0b },
- { 0xd041, 0, 2, 0x02 },
- { 0xd042, 0, 8, 0x4d },
- { 0xd043, 0, 2, 0x00 },
- { 0xd045, 1, 1, 0x00 },
- { 0x9bcf, 0, 1, 0x01 },
- { 0xd045, 2, 1, 0x01 },
- { 0xd04f, 0, 8, 0x9a },
- { 0xd050, 0, 1, 0x01 },
- { 0xd051, 0, 8, 0x5a },
- { 0xd052, 0, 1, 0x01 },
- { 0xd053, 0, 8, 0x50 },
- { 0xd054, 0, 8, 0x46 },
- { 0x9bd7, 0, 8, 0x0a },
- { 0x9bd8, 0, 8, 0x14 },
- { 0x9bd9, 0, 8, 0x08 },
- { 0x9bd0, 0, 8, 0xa8 },
- { 0x9be4, 0, 8, 0x7f },
- { 0x9bbd, 0, 8, 0xa8 },
- { 0x9be2, 0, 8, 0x20 },
- { 0x9bee, 0, 1, 0x01 },
+/*
+ * NXP TDA18271 & TDA18218 tuner init
+ * AF9013_TUNER_TDA18271 0x9c
+ * AF9013_TUNER_TDA18218 0xb3
+ */
+static const struct af9013_reg_mask_val tuner_init_tab_tda18271[] = {
+ {0x9bd5, 0xff, 0x01},
+ {0x9bd6, 0xff, 0x04},
+ {0xd1a0, 0x02, 0x02},
+ {0xd000, 0x01, 0x01},
+ {0xd000, 0x02, 0x00},
+ {0xd001, 0x02, 0x02},
+ {0xd001, 0x01, 0x00},
+ {0xd001, 0x20, 0x00},
+ {0xd002, 0x1f, 0x19},
+ {0xd003, 0x1f, 0x1a},
+ {0xd004, 0x1f, 0x19},
+ {0xd005, 0x1f, 0x1a},
+ {0xd00e, 0x1f, 0x10},
+ {0xd00f, 0x07, 0x04},
+ {0xd00f, 0x38, 0x28},
+ {0xd010, 0x07, 0x04},
+ {0xd010, 0x38, 0x28},
+ {0xd016, 0xf0, 0x30},
+ {0xd01f, 0x3f, 0x0a},
+ {0xd020, 0x3f, 0x0a},
+ {0x9bda, 0xff, 0x01},
+ {0x9be3, 0xff, 0x01},
+ {0xd1a0, 0x02, 0x00},
+ {0x9bbe, 0x01, 0x01},
+ {0x9bcc, 0x01, 0x01},
+ {0x9bb9, 0xff, 0x00},
+ {0x9bcd, 0xff, 0x18},
+ {0x9bff, 0xff, 0x2c},
+ {0xd015, 0xff, 0x46},
+ {0xd016, 0x01, 0x00},
+ {0xd044, 0xff, 0x46},
+ {0xd045, 0x01, 0x00},
+ {0xd008, 0xff, 0xdf},
+ {0xd009, 0x03, 0x02},
+ {0xd006, 0xff, 0x44},
+ {0xd007, 0x03, 0x01},
+ {0xd00c, 0xff, 0x00},
+ {0xd00d, 0x03, 0x02},
+ {0xd00a, 0xff, 0xf6},
+ {0xd00b, 0x03, 0x01},
+ {0x9bba, 0xff, 0xf9},
+ {0x9bc8, 0xff, 0xaa},
+ {0x9bc3, 0xff, 0xdf},
+ {0x9bc4, 0xff, 0x02},
+ {0x9bc5, 0xff, 0x00},
+ {0x9bc6, 0xff, 0x02},
+ {0x9bc9, 0xff, 0xf0},
+ {0xd011, 0xff, 0x3c},
+ {0xd012, 0x03, 0x01},
+ {0xd013, 0xff, 0xf7},
+ {0xd014, 0x03, 0x02},
+ {0xd040, 0xff, 0x0b},
+ {0xd041, 0x03, 0x02},
+ {0xd042, 0xff, 0x4d},
+ {0xd043, 0x03, 0x00},
+ {0xd045, 0x02, 0x00},
+ {0x9bcf, 0x01, 0x01},
+ {0xd045, 0x04, 0x04},
+ {0xd04f, 0xff, 0x9a},
+ {0xd050, 0x01, 0x01},
+ {0xd051, 0xff, 0x5a},
+ {0xd052, 0x01, 0x01},
+ {0xd053, 0xff, 0x50},
+ {0xd054, 0xff, 0x46},
+ {0x9bd7, 0xff, 0x0a},
+ {0x9bd8, 0xff, 0x14},
+ {0x9bd9, 0xff, 0x08},
+ {0x9bd0, 0xff, 0xa8},
+ {0x9be4, 0xff, 0x7f},
+ {0x9bbd, 0xff, 0xa8},
+ {0x9be2, 0xff, 0x20},
+ {0x9bee, 0x01, 0x01},
};
#endif /* AF9013_PRIV_H */
diff --git a/drivers/media/dvb-frontends/cxd2099.c b/drivers/media/dvb-frontends/cxd2099.c
new file mode 100644
index 000000000000..c0a5849b76bb
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2099.c
@@ -0,0 +1,704 @@
+/*
+ * cxd2099.c: Driver for the CXD2099AR Common Interface Controller
+ *
+ * Copyright (C) 2010-2013 Digital Devices GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+
+#include "cxd2099.h"
+
+static int buffermode;
+module_param(buffermode, int, 0444);
+MODULE_PARM_DESC(buffermode, "Enable CXD2099AR buffer mode (default: disabled)");
+
+static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount);
+
+struct cxd {
+ struct dvb_ca_en50221 en;
+
+ struct cxd2099_cfg cfg;
+ struct i2c_client *client;
+ struct regmap *regmap;
+
+ u8 regs[0x23];
+ u8 lastaddress;
+ u8 clk_reg_f;
+ u8 clk_reg_b;
+ int mode;
+ int ready;
+ int dr;
+ int write_busy;
+ int slot_stat;
+
+ u8 amem[1024];
+ int amem_read;
+
+ int cammode;
+ struct mutex lock; /* device access lock */
+
+ u8 rbuf[1028];
+ u8 wbuf[1028];
+};
+
+static int read_block(struct cxd *ci, u8 adr, u8 *data, u16 n)
+{
+ int status = 0;
+
+ if (ci->lastaddress != adr)
+ status = regmap_write(ci->regmap, 0, adr);
+ if (!status) {
+ ci->lastaddress = adr;
+
+ while (n) {
+ int len = n;
+
+ if (ci->cfg.max_i2c && len > ci->cfg.max_i2c)
+ len = ci->cfg.max_i2c;
+ status = regmap_raw_read(ci->regmap, 1, data, len);
+ if (status)
+ return status;
+ data += len;
+ n -= len;
+ }
+ }
+ return status;
+}
+
+static int read_reg(struct cxd *ci, u8 reg, u8 *val)
+{
+ return read_block(ci, reg, val, 1);
+}
+
+static int read_pccard(struct cxd *ci, u16 address, u8 *data, u8 n)
+{
+ int status;
+ u8 addr[2] = {address & 0xff, address >> 8};
+
+ status = regmap_raw_write(ci->regmap, 2, addr, 2);
+ if (!status)
+ status = regmap_raw_read(ci->regmap, 3, data, n);
+ return status;
+}
+
+static int write_pccard(struct cxd *ci, u16 address, u8 *data, u8 n)
+{
+ int status;
+ u8 addr[2] = {address & 0xff, address >> 8};
+
+ status = regmap_raw_write(ci->regmap, 2, addr, 2);
+ if (!status) {
+ u8 buf[256];
+
+ memcpy(buf, data, n);
+ status = regmap_raw_write(ci->regmap, 3, buf, n);
+ }
+ return status;
+}
+
+static int read_io(struct cxd *ci, u16 address, unsigned int *val)
+{
+ int status;
+ u8 addr[2] = {address & 0xff, address >> 8};
+
+ status = regmap_raw_write(ci->regmap, 2, addr, 2);
+ if (!status)
+ status = regmap_read(ci->regmap, 3, val);
+ return status;
+}
+
+static int write_io(struct cxd *ci, u16 address, u8 val)
+{
+ int status;
+ u8 addr[2] = {address & 0xff, address >> 8};
+
+ status = regmap_raw_write(ci->regmap, 2, addr, 2);
+ if (!status)
+ status = regmap_write(ci->regmap, 3, val);
+ return status;
+}
+
+static int write_regm(struct cxd *ci, u8 reg, u8 val, u8 mask)
+{
+ int status = 0;
+ unsigned int regval;
+
+ if (ci->lastaddress != reg)
+ status = regmap_write(ci->regmap, 0, reg);
+ if (!status && reg >= 6 && reg <= 8 && mask != 0xff) {
+ status = regmap_read(ci->regmap, 1, &regval);
+ ci->regs[reg] = regval;
+ }
+ ci->lastaddress = reg;
+ ci->regs[reg] = (ci->regs[reg] & (~mask)) | val;
+ if (!status)
+ status = regmap_write(ci->regmap, 1, ci->regs[reg]);
+ if (reg == 0x20)
+ ci->regs[reg] &= 0x7f;
+ return status;
+}
+
+static int write_reg(struct cxd *ci, u8 reg, u8 val)
+{
+ return write_regm(ci, reg, val, 0xff);
+}
+
+static int write_block(struct cxd *ci, u8 adr, u8 *data, u16 n)
+{
+ int status = 0;
+ u8 *buf = ci->wbuf;
+
+ if (ci->lastaddress != adr)
+ status = regmap_write(ci->regmap, 0, adr);
+ if (status)
+ return status;
+
+ ci->lastaddress = adr;
+ while (n) {
+ int len = n;
+
+ if (ci->cfg.max_i2c && (len + 1 > ci->cfg.max_i2c))
+ len = ci->cfg.max_i2c - 1;
+ memcpy(buf, data, len);
+ status = regmap_raw_write(ci->regmap, 1, buf, len);
+ if (status)
+ return status;
+ n -= len;
+ data += len;
+ }
+ return status;
+}
+
+static void set_mode(struct cxd *ci, int mode)
+{
+ if (mode == ci->mode)
+ return;
+
+ switch (mode) {
+ case 0x00: /* IO mem */
+ write_regm(ci, 0x06, 0x00, 0x07);
+ break;
+ case 0x01: /* ATT mem */
+ write_regm(ci, 0x06, 0x02, 0x07);
+ break;
+ default:
+ break;
+ }
+ ci->mode = mode;
+}
+
+static void cam_mode(struct cxd *ci, int mode)
+{
+ u8 dummy;
+
+ if (mode == ci->cammode)
+ return;
+
+ switch (mode) {
+ case 0x00:
+ write_regm(ci, 0x20, 0x80, 0x80);
+ break;
+ case 0x01:
+ if (!ci->en.read_data)
+ return;
+ ci->write_busy = 0;
+ dev_info(&ci->client->dev, "enable cam buffer mode\n");
+ write_reg(ci, 0x0d, 0x00);
+ write_reg(ci, 0x0e, 0x01);
+ write_regm(ci, 0x08, 0x40, 0x40);
+ read_reg(ci, 0x12, &dummy);
+ write_regm(ci, 0x08, 0x80, 0x80);
+ break;
+ default:
+ break;
+ }
+ ci->cammode = mode;
+}
+
+static int init(struct cxd *ci)
+{
+ int status;
+
+ mutex_lock(&ci->lock);
+ ci->mode = -1;
+ do {
+ status = write_reg(ci, 0x00, 0x00);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x01, 0x00);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x02, 0x10);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x03, 0x00);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x05, 0xFF);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x06, 0x1F);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x07, 0x1F);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x08, 0x28);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x14, 0x20);
+ if (status < 0)
+ break;
+
+ /* TOSTRT = 8, Mode B (gated clock), falling Edge,
+ * Serial, POL=HIGH, MSB
+ */
+ status = write_reg(ci, 0x0A, 0xA7);
+ if (status < 0)
+ break;
+
+ status = write_reg(ci, 0x0B, 0x33);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x0C, 0x33);
+ if (status < 0)
+ break;
+
+ status = write_regm(ci, 0x14, 0x00, 0x0F);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x15, ci->clk_reg_b);
+ if (status < 0)
+ break;
+ status = write_regm(ci, 0x16, 0x00, 0x0F);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x17, ci->clk_reg_f);
+ if (status < 0)
+ break;
+
+ if (ci->cfg.clock_mode == 2) {
+ /* bitrate*2^13/ 72000 */
+ u32 reg = ((ci->cfg.bitrate << 13) + 71999) / 72000;
+
+ if (ci->cfg.polarity) {
+ status = write_reg(ci, 0x09, 0x6f);
+ if (status < 0)
+ break;
+ } else {
+ status = write_reg(ci, 0x09, 0x6d);
+ if (status < 0)
+ break;
+ }
+ status = write_reg(ci, 0x20, 0x08);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x21, (reg >> 8) & 0xff);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x22, reg & 0xff);
+ if (status < 0)
+ break;
+ } else if (ci->cfg.clock_mode == 1) {
+ if (ci->cfg.polarity) {
+ status = write_reg(ci, 0x09, 0x6f); /* D */
+ if (status < 0)
+ break;
+ } else {
+ status = write_reg(ci, 0x09, 0x6d);
+ if (status < 0)
+ break;
+ }
+ status = write_reg(ci, 0x20, 0x68);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x21, 0x00);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x22, 0x02);
+ if (status < 0)
+ break;
+ } else {
+ if (ci->cfg.polarity) {
+ status = write_reg(ci, 0x09, 0x4f); /* C */
+ if (status < 0)
+ break;
+ } else {
+ status = write_reg(ci, 0x09, 0x4d);
+ if (status < 0)
+ break;
+ }
+ status = write_reg(ci, 0x20, 0x28);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x21, 0x00);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x22, 0x07);
+ if (status < 0)
+ break;
+ }
+
+ status = write_regm(ci, 0x20, 0x80, 0x80);
+ if (status < 0)
+ break;
+ status = write_regm(ci, 0x03, 0x02, 0x02);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x01, 0x04);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x00, 0x31);
+ if (status < 0)
+ break;
+
+ /* Put TS in bypass */
+ status = write_regm(ci, 0x09, 0x08, 0x08);
+ if (status < 0)
+ break;
+ ci->cammode = -1;
+ cam_mode(ci, 0);
+ } while (0);
+ mutex_unlock(&ci->lock);
+
+ return 0;
+}
+
+static int read_attribute_mem(struct dvb_ca_en50221 *ca,
+ int slot, int address)
+{
+ struct cxd *ci = ca->data;
+ u8 val;
+
+ mutex_lock(&ci->lock);
+ set_mode(ci, 1);
+ read_pccard(ci, address, &val, 1);
+ mutex_unlock(&ci->lock);
+ return val;
+}
+
+static int write_attribute_mem(struct dvb_ca_en50221 *ca, int slot,
+ int address, u8 value)
+{
+ struct cxd *ci = ca->data;
+
+ mutex_lock(&ci->lock);
+ set_mode(ci, 1);
+ write_pccard(ci, address, &value, 1);
+ mutex_unlock(&ci->lock);
+ return 0;
+}
+
+static int read_cam_control(struct dvb_ca_en50221 *ca,
+ int slot, u8 address)
+{
+ struct cxd *ci = ca->data;
+ unsigned int val;
+
+ mutex_lock(&ci->lock);
+ set_mode(ci, 0);
+ read_io(ci, address, &val);
+ mutex_unlock(&ci->lock);
+ return val;
+}
+
+static int write_cam_control(struct dvb_ca_en50221 *ca, int slot,
+ u8 address, u8 value)
+{
+ struct cxd *ci = ca->data;
+
+ mutex_lock(&ci->lock);
+ set_mode(ci, 0);
+ write_io(ci, address, value);
+ mutex_unlock(&ci->lock);
+ return 0;
+}
+
+static int slot_reset(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct cxd *ci = ca->data;
+
+ if (ci->cammode)
+ read_data(ca, slot, ci->rbuf, 0);
+
+ mutex_lock(&ci->lock);
+ cam_mode(ci, 0);
+ write_reg(ci, 0x00, 0x21);
+ write_reg(ci, 0x06, 0x1F);
+ write_reg(ci, 0x00, 0x31);
+ write_regm(ci, 0x20, 0x80, 0x80);
+ write_reg(ci, 0x03, 0x02);
+ ci->ready = 0;
+ ci->mode = -1;
+ {
+ int i;
+
+ for (i = 0; i < 100; i++) {
+ usleep_range(10000, 11000);
+ if (ci->ready)
+ break;
+ }
+ }
+ mutex_unlock(&ci->lock);
+ return 0;
+}
+
+static int slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct cxd *ci = ca->data;
+
+ dev_dbg(&ci->client->dev, "%s\n", __func__);
+ if (ci->cammode)
+ read_data(ca, slot, ci->rbuf, 0);
+ mutex_lock(&ci->lock);
+ write_reg(ci, 0x00, 0x21);
+ write_reg(ci, 0x06, 0x1F);
+ msleep(300);
+
+ write_regm(ci, 0x09, 0x08, 0x08);
+ write_regm(ci, 0x20, 0x80, 0x80); /* Reset CAM Mode */
+ write_regm(ci, 0x06, 0x07, 0x07); /* Clear IO Mode */
+
+ ci->mode = -1;
+ ci->write_busy = 0;
+ mutex_unlock(&ci->lock);
+ return 0;
+}
+
+static int slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct cxd *ci = ca->data;
+
+ mutex_lock(&ci->lock);
+ write_regm(ci, 0x09, 0x00, 0x08);
+ set_mode(ci, 0);
+ cam_mode(ci, 1);
+ mutex_unlock(&ci->lock);
+ return 0;
+}
+
+static int campoll(struct cxd *ci)
+{
+ u8 istat;
+
+ read_reg(ci, 0x04, &istat);
+ if (!istat)
+ return 0;
+ write_reg(ci, 0x05, istat);
+
+ if (istat & 0x40)
+ ci->dr = 1;
+ if (istat & 0x20)
+ ci->write_busy = 0;
+
+ if (istat & 2) {
+ u8 slotstat;
+
+ read_reg(ci, 0x01, &slotstat);
+ if (!(2 & slotstat)) {
+ if (!ci->slot_stat) {
+ ci->slot_stat |=
+ DVB_CA_EN50221_POLL_CAM_PRESENT;
+ write_regm(ci, 0x03, 0x08, 0x08);
+ }
+
+ } else {
+ if (ci->slot_stat) {
+ ci->slot_stat = 0;
+ write_regm(ci, 0x03, 0x00, 0x08);
+ dev_info(&ci->client->dev, "NO CAM\n");
+ ci->ready = 0;
+ }
+ }
+ if ((istat & 8) &&
+ ci->slot_stat == DVB_CA_EN50221_POLL_CAM_PRESENT) {
+ ci->ready = 1;
+ ci->slot_stat |= DVB_CA_EN50221_POLL_CAM_READY;
+ }
+ }
+ return 0;
+}
+
+static int poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open)
+{
+ struct cxd *ci = ca->data;
+ u8 slotstat;
+
+ mutex_lock(&ci->lock);
+ campoll(ci);
+ read_reg(ci, 0x01, &slotstat);
+ mutex_unlock(&ci->lock);
+
+ return ci->slot_stat;
+}
+
+static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount)
+{
+ struct cxd *ci = ca->data;
+ u8 msb, lsb;
+ u16 len;
+
+ mutex_lock(&ci->lock);
+ campoll(ci);
+ mutex_unlock(&ci->lock);
+
+ if (!ci->dr)
+ return 0;
+
+ mutex_lock(&ci->lock);
+ read_reg(ci, 0x0f, &msb);
+ read_reg(ci, 0x10, &lsb);
+ len = ((u16)msb << 8) | lsb;
+ if (len > ecount || len < 2) {
+ /* read it anyway or cxd may hang */
+ read_block(ci, 0x12, ci->rbuf, len);
+ mutex_unlock(&ci->lock);
+ return -EIO;
+ }
+ read_block(ci, 0x12, ebuf, len);
+ ci->dr = 0;
+ mutex_unlock(&ci->lock);
+ return len;
+}
+
+static int write_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount)
+{
+ struct cxd *ci = ca->data;
+
+ if (ci->write_busy)
+ return -EAGAIN;
+ mutex_lock(&ci->lock);
+ write_reg(ci, 0x0d, ecount >> 8);
+ write_reg(ci, 0x0e, ecount & 0xff);
+ write_block(ci, 0x11, ebuf, ecount);
+ ci->write_busy = 1;
+ mutex_unlock(&ci->lock);
+ return ecount;
+}
+
+static struct dvb_ca_en50221 en_templ = {
+ .read_attribute_mem = read_attribute_mem,
+ .write_attribute_mem = write_attribute_mem,
+ .read_cam_control = read_cam_control,
+ .write_cam_control = write_cam_control,
+ .slot_reset = slot_reset,
+ .slot_shutdown = slot_shutdown,
+ .slot_ts_enable = slot_ts_enable,
+ .poll_slot_status = poll_slot_status,
+ .read_data = read_data,
+ .write_data = write_data,
+};
+
+static int cxd2099_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct cxd *ci;
+ struct cxd2099_cfg *cfg = client->dev.platform_data;
+ static const struct regmap_config rm_cfg = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ };
+ unsigned int val;
+ int ret;
+
+ ci = kzalloc(sizeof(*ci), GFP_KERNEL);
+ if (!ci) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ci->client = client;
+ memcpy(&ci->cfg, cfg, sizeof(ci->cfg));
+
+ ci->regmap = regmap_init_i2c(client, &rm_cfg);
+ if (IS_ERR(ci->regmap)) {
+ ret = PTR_ERR(ci->regmap);
+ goto err_kfree;
+ }
+
+ ret = regmap_read(ci->regmap, 0x00, &val);
+ if (ret < 0) {
+ dev_info(&client->dev, "No CXD2099AR detected at 0x%02x\n",
+ client->addr);
+ goto err_rmexit;
+ }
+
+ mutex_init(&ci->lock);
+ ci->lastaddress = 0xff;
+ ci->clk_reg_b = 0x4a;
+ ci->clk_reg_f = 0x1b;
+
+ ci->en = en_templ;
+ ci->en.data = ci;
+ init(ci);
+ dev_info(&client->dev, "Attached CXD2099AR at 0x%02x\n", client->addr);
+
+ *cfg->en = &ci->en;
+
+ if (!buffermode) {
+ ci->en.read_data = NULL;
+ ci->en.write_data = NULL;
+ } else {
+ dev_info(&client->dev, "Using CXD2099AR buffer mode");
+ }
+
+ i2c_set_clientdata(client, ci);
+
+ return 0;
+
+err_rmexit:
+ regmap_exit(ci->regmap);
+err_kfree:
+ kfree(ci);
+err:
+
+ return ret;
+}
+
+static int cxd2099_remove(struct i2c_client *client)
+{
+ struct cxd *ci = i2c_get_clientdata(client);
+
+ regmap_exit(ci->regmap);
+ kfree(ci);
+
+ return 0;
+}
+
+static const struct i2c_device_id cxd2099_id[] = {
+ {"cxd2099", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cxd2099_id);
+
+static struct i2c_driver cxd2099_driver = {
+ .driver = {
+ .name = "cxd2099",
+ },
+ .probe = cxd2099_probe,
+ .remove = cxd2099_remove,
+ .id_table = cxd2099_id,
+};
+
+module_i2c_driver(cxd2099_driver);
+
+MODULE_DESCRIPTION("CXD2099AR Common Interface controller driver");
+MODULE_AUTHOR("Ralph Metzler");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/cxd2099.h b/drivers/media/dvb-frontends/cxd2099.h
new file mode 100644
index 000000000000..8fa45a4c615a
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2099.h
@@ -0,0 +1,32 @@
+/*
+ * cxd2099.h: Driver for the CXD2099AR Common Interface Controller
+ *
+ * Copyright (C) 2010-2011 Digital Devices GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#ifndef _CXD2099_H_
+#define _CXD2099_H_
+
+#include <media/dvb_ca_en50221.h>
+
+struct cxd2099_cfg {
+ u32 bitrate;
+ u8 polarity;
+ u8 clock_mode;
+
+ u32 max_i2c;
+
+ /* ptr to DVB CA struct */
+ struct dvb_ca_en50221 **en;
+};
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/Kconfig b/drivers/media/dvb-frontends/cxd2880/Kconfig
new file mode 100644
index 000000000000..9d989676e800
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/Kconfig
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config DVB_CXD2880
+ tristate "Sony CXD2880 DVB-T2/T tuner + demodulator"
+ depends on DVB_CORE && SPI
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y when you want to support this frontend. \ No newline at end of file
diff --git a/drivers/media/dvb-frontends/cxd2880/Makefile b/drivers/media/dvb-frontends/cxd2880/Makefile
new file mode 100644
index 000000000000..c6baa4caba19
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/Makefile
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0
+
+cxd2880-objs := cxd2880_common.o \
+ cxd2880_devio_spi.o \
+ cxd2880_integ.o \
+ cxd2880_io.o \
+ cxd2880_spi_device.o \
+ cxd2880_tnrdmd.o \
+ cxd2880_tnrdmd_dvbt2.o \
+ cxd2880_tnrdmd_dvbt2_mon.o \
+ cxd2880_tnrdmd_dvbt.o \
+ cxd2880_tnrdmd_dvbt_mon.o\
+ cxd2880_tnrdmd_mon.o\
+ cxd2880_top.o
+
+obj-$(CONFIG_DVB_CXD2880) += cxd2880.o
+
+ccflags-y += -Idrivers/media/dvb-frontends
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880.h b/drivers/media/dvb-frontends/cxd2880/cxd2880.h
new file mode 100644
index 000000000000..4ea3510aab66
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver public definitions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_H
+#define CXD2880_H
+
+struct cxd2880_config {
+ struct spi_device *spi;
+ struct mutex *spi_mutex; /* For SPI access exclusive control */
+};
+
+#if IS_REACHABLE(CONFIG_DVB_CXD2880)
+extern struct dvb_frontend *cxd2880_attach(struct dvb_frontend *fe,
+ struct cxd2880_config *cfg);
+#else
+static inline struct dvb_frontend *cxd2880_attach(struct dvb_frontend *fe,
+ struct cxd2880_config *cfg)
+{
+ pr_warn("%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_CXD2880 */
+
+#endif /* CXD2880_H */
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_common.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
new file mode 100644
index 000000000000..d6f5af6609c1
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_common.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * common functions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#include "cxd2880_common.h"
+
+int cxd2880_convert2s_complement(u32 value, u32 bitlen)
+{
+ if (!bitlen || bitlen >= 32)
+ return (int)value;
+
+ if (value & (u32)(1 << (bitlen - 1)))
+ return (int)(GENMASK(31, bitlen) | value);
+ else
+ return (int)(GENMASK(bitlen - 1, 0) & value);
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_common.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
new file mode 100644
index 000000000000..b05bce71ab35
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_common.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver common definitions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_COMMON_H
+#define CXD2880_COMMON_H
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+
+int cxd2880_convert2s_complement(u32 value, u32 bitlen);
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c
new file mode 100644
index 000000000000..aba59400859e
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_devio_spi.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * I/O interface via SPI
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#include "cxd2880_devio_spi.h"
+
+#define BURST_WRITE_MAX 128
+
+static int cxd2880_io_spi_read_reg(struct cxd2880_io *io,
+ enum cxd2880_io_tgt tgt,
+ u8 sub_address, u8 *data,
+ u32 size)
+{
+ int ret = 0;
+ struct cxd2880_spi *spi = NULL;
+ u8 send_data[6];
+ u8 *read_data_top = data;
+
+ if (!io || !io->if_object || !data)
+ return -EINVAL;
+
+ if (sub_address + size > 0x100)
+ return -EINVAL;
+
+ spi = io->if_object;
+
+ if (tgt == CXD2880_IO_TGT_SYS)
+ send_data[0] = 0x0b;
+ else
+ send_data[0] = 0x0a;
+
+ send_data[3] = 0;
+ send_data[4] = 0;
+ send_data[5] = 0;
+
+ while (size > 0) {
+ send_data[1] = sub_address;
+ if (size > 255)
+ send_data[2] = 255;
+ else
+ send_data[2] = size;
+
+ ret =
+ spi->write_read(spi, send_data, sizeof(send_data),
+ read_data_top, send_data[2]);
+ if (ret)
+ return ret;
+
+ sub_address += send_data[2];
+ read_data_top += send_data[2];
+ size -= send_data[2];
+ }
+
+ return ret;
+}
+
+static int cxd2880_io_spi_write_reg(struct cxd2880_io *io,
+ enum cxd2880_io_tgt tgt,
+ u8 sub_address,
+ const u8 *data, u32 size)
+{
+ int ret = 0;
+ struct cxd2880_spi *spi = NULL;
+ u8 send_data[BURST_WRITE_MAX + 4];
+ const u8 *write_data_top = data;
+
+ if (!io || !io->if_object || !data)
+ return -EINVAL;
+
+ if (size > BURST_WRITE_MAX)
+ return -EINVAL;
+
+ if (sub_address + size > 0x100)
+ return -EINVAL;
+
+ spi = io->if_object;
+
+ if (tgt == CXD2880_IO_TGT_SYS)
+ send_data[0] = 0x0f;
+ else
+ send_data[0] = 0x0e;
+
+ while (size > 0) {
+ send_data[1] = sub_address;
+ if (size > 255)
+ send_data[2] = 255;
+ else
+ send_data[2] = size;
+
+ memcpy(&send_data[3], write_data_top, send_data[2]);
+
+ if (tgt == CXD2880_IO_TGT_SYS) {
+ send_data[3 + send_data[2]] = 0x00;
+ ret = spi->write(spi, send_data, send_data[2] + 4);
+ } else {
+ ret = spi->write(spi, send_data, send_data[2] + 3);
+ }
+ if (ret)
+ return ret;
+
+ sub_address += send_data[2];
+ write_data_top += send_data[2];
+ size -= send_data[2];
+ }
+
+ return ret;
+}
+
+int cxd2880_io_spi_create(struct cxd2880_io *io,
+ struct cxd2880_spi *spi, u8 slave_select)
+{
+ if (!io || !spi)
+ return -EINVAL;
+
+ io->read_regs = cxd2880_io_spi_read_reg;
+ io->write_regs = cxd2880_io_spi_write_reg;
+ io->write_reg = cxd2880_io_common_write_one_reg;
+ io->if_object = spi;
+ io->i2c_address_sys = 0;
+ io->i2c_address_demod = 0;
+ io->slave_select = slave_select;
+
+ return 0;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h
new file mode 100644
index 000000000000..27f7cb12fad4
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_devio_spi.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * I/O interface via SPI
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_DEVIO_SPI_H
+#define CXD2880_DEVIO_SPI_H
+
+#include "cxd2880_common.h"
+#include "cxd2880_io.h"
+#include "cxd2880_spi.h"
+
+#include "cxd2880_tnrdmd.h"
+
+int cxd2880_io_spi_create(struct cxd2880_io *io,
+ struct cxd2880_spi *spi,
+ u8 slave_select);
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
new file mode 100644
index 000000000000..820f4757a520
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_dtv.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DTV related definitions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_DTV_H
+#define CXD2880_DTV_H
+
+enum cxd2880_dtv_sys {
+ CXD2880_DTV_SYS_UNKNOWN,
+ CXD2880_DTV_SYS_DVBT,
+ CXD2880_DTV_SYS_DVBT2,
+ CXD2880_DTV_SYS_ANY
+};
+
+enum cxd2880_dtv_bandwidth {
+ CXD2880_DTV_BW_UNKNOWN = 0,
+ CXD2880_DTV_BW_1_7_MHZ = 1,
+ CXD2880_DTV_BW_5_MHZ = 5,
+ CXD2880_DTV_BW_6_MHZ = 6,
+ CXD2880_DTV_BW_7_MHZ = 7,
+ CXD2880_DTV_BW_8_MHZ = 8
+};
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h
new file mode 100644
index 000000000000..76a1acc346ef
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h
@@ -0,0 +1,74 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_dvbt.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T related definitions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_DVBT_H
+#define CXD2880_DVBT_H
+
+#include "cxd2880_common.h"
+
+enum cxd2880_dvbt_constellation {
+ CXD2880_DVBT_CONSTELLATION_QPSK,
+ CXD2880_DVBT_CONSTELLATION_16QAM,
+ CXD2880_DVBT_CONSTELLATION_64QAM,
+ CXD2880_DVBT_CONSTELLATION_RESERVED_3
+};
+
+enum cxd2880_dvbt_hierarchy {
+ CXD2880_DVBT_HIERARCHY_NON,
+ CXD2880_DVBT_HIERARCHY_1,
+ CXD2880_DVBT_HIERARCHY_2,
+ CXD2880_DVBT_HIERARCHY_4
+};
+
+enum cxd2880_dvbt_coderate {
+ CXD2880_DVBT_CODERATE_1_2,
+ CXD2880_DVBT_CODERATE_2_3,
+ CXD2880_DVBT_CODERATE_3_4,
+ CXD2880_DVBT_CODERATE_5_6,
+ CXD2880_DVBT_CODERATE_7_8,
+ CXD2880_DVBT_CODERATE_RESERVED_5,
+ CXD2880_DVBT_CODERATE_RESERVED_6,
+ CXD2880_DVBT_CODERATE_RESERVED_7
+};
+
+enum cxd2880_dvbt_guard {
+ CXD2880_DVBT_GUARD_1_32,
+ CXD2880_DVBT_GUARD_1_16,
+ CXD2880_DVBT_GUARD_1_8,
+ CXD2880_DVBT_GUARD_1_4
+};
+
+enum cxd2880_dvbt_mode {
+ CXD2880_DVBT_MODE_2K,
+ CXD2880_DVBT_MODE_8K,
+ CXD2880_DVBT_MODE_RESERVED_2,
+ CXD2880_DVBT_MODE_RESERVED_3
+};
+
+enum cxd2880_dvbt_profile {
+ CXD2880_DVBT_PROFILE_HP = 0,
+ CXD2880_DVBT_PROFILE_LP
+};
+
+struct cxd2880_dvbt_tpsinfo {
+ enum cxd2880_dvbt_constellation constellation;
+ enum cxd2880_dvbt_hierarchy hierarchy;
+ enum cxd2880_dvbt_coderate rate_hp;
+ enum cxd2880_dvbt_coderate rate_lp;
+ enum cxd2880_dvbt_guard guard;
+ enum cxd2880_dvbt_mode mode;
+ u8 fnum;
+ u8 length_indicator;
+ u16 cell_id;
+ u8 cell_id_ok;
+ u8 reserved_even;
+ u8 reserved_odd;
+};
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h
new file mode 100644
index 000000000000..191047b158fe
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h
@@ -0,0 +1,385 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_dvbt2.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T2 related definitions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_DVBT2_H
+#define CXD2880_DVBT2_H
+
+#include "cxd2880_common.h"
+
+enum cxd2880_dvbt2_profile {
+ CXD2880_DVBT2_PROFILE_BASE,
+ CXD2880_DVBT2_PROFILE_LITE,
+ CXD2880_DVBT2_PROFILE_ANY
+};
+
+enum cxd2880_dvbt2_version {
+ CXD2880_DVBT2_V111,
+ CXD2880_DVBT2_V121,
+ CXD2880_DVBT2_V131
+};
+
+enum cxd2880_dvbt2_s1 {
+ CXD2880_DVBT2_S1_BASE_SISO = 0x00,
+ CXD2880_DVBT2_S1_BASE_MISO = 0x01,
+ CXD2880_DVBT2_S1_NON_DVBT2 = 0x02,
+ CXD2880_DVBT2_S1_LITE_SISO = 0x03,
+ CXD2880_DVBT2_S1_LITE_MISO = 0x04,
+ CXD2880_DVBT2_S1_RSVD3 = 0x05,
+ CXD2880_DVBT2_S1_RSVD4 = 0x06,
+ CXD2880_DVBT2_S1_RSVD5 = 0x07,
+ CXD2880_DVBT2_S1_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_base_s2 {
+ CXD2880_DVBT2_BASE_S2_M2K_G_ANY = 0x00,
+ CXD2880_DVBT2_BASE_S2_M8K_G_DVBT = 0x01,
+ CXD2880_DVBT2_BASE_S2_M4K_G_ANY = 0x02,
+ CXD2880_DVBT2_BASE_S2_M1K_G_ANY = 0x03,
+ CXD2880_DVBT2_BASE_S2_M16K_G_ANY = 0x04,
+ CXD2880_DVBT2_BASE_S2_M32K_G_DVBT = 0x05,
+ CXD2880_DVBT2_BASE_S2_M8K_G_DVBT2 = 0x06,
+ CXD2880_DVBT2_BASE_S2_M32K_G_DVBT2 = 0x07,
+ CXD2880_DVBT2_BASE_S2_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_lite_s2 {
+ CXD2880_DVBT2_LITE_S2_M2K_G_ANY = 0x00,
+ CXD2880_DVBT2_LITE_S2_M8K_G_DVBT = 0x01,
+ CXD2880_DVBT2_LITE_S2_M4K_G_ANY = 0x02,
+ CXD2880_DVBT2_LITE_S2_M16K_G_DVBT2 = 0x03,
+ CXD2880_DVBT2_LITE_S2_M16K_G_DVBT = 0x04,
+ CXD2880_DVBT2_LITE_S2_RSVD1 = 0x05,
+ CXD2880_DVBT2_LITE_S2_M8K_G_DVBT2 = 0x06,
+ CXD2880_DVBT2_LITE_S2_RSVD2 = 0x07,
+ CXD2880_DVBT2_LITE_S2_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_guard {
+ CXD2880_DVBT2_G1_32 = 0x00,
+ CXD2880_DVBT2_G1_16 = 0x01,
+ CXD2880_DVBT2_G1_8 = 0x02,
+ CXD2880_DVBT2_G1_4 = 0x03,
+ CXD2880_DVBT2_G1_128 = 0x04,
+ CXD2880_DVBT2_G19_128 = 0x05,
+ CXD2880_DVBT2_G19_256 = 0x06,
+ CXD2880_DVBT2_G_RSVD1 = 0x07,
+ CXD2880_DVBT2_G_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_mode {
+ CXD2880_DVBT2_M2K = 0x00,
+ CXD2880_DVBT2_M8K = 0x01,
+ CXD2880_DVBT2_M4K = 0x02,
+ CXD2880_DVBT2_M1K = 0x03,
+ CXD2880_DVBT2_M16K = 0x04,
+ CXD2880_DVBT2_M32K = 0x05,
+ CXD2880_DVBT2_M_RSVD1 = 0x06,
+ CXD2880_DVBT2_M_RSVD2 = 0x07
+};
+
+enum cxd2880_dvbt2_bw {
+ CXD2880_DVBT2_BW_8 = 0x00,
+ CXD2880_DVBT2_BW_7 = 0x01,
+ CXD2880_DVBT2_BW_6 = 0x02,
+ CXD2880_DVBT2_BW_5 = 0x03,
+ CXD2880_DVBT2_BW_10 = 0x04,
+ CXD2880_DVBT2_BW_1_7 = 0x05,
+ CXD2880_DVBT2_BW_RSVD1 = 0x06,
+ CXD2880_DVBT2_BW_RSVD2 = 0x07,
+ CXD2880_DVBT2_BW_RSVD3 = 0x08,
+ CXD2880_DVBT2_BW_RSVD4 = 0x09,
+ CXD2880_DVBT2_BW_RSVD5 = 0x0a,
+ CXD2880_DVBT2_BW_RSVD6 = 0x0b,
+ CXD2880_DVBT2_BW_RSVD7 = 0x0c,
+ CXD2880_DVBT2_BW_RSVD8 = 0x0d,
+ CXD2880_DVBT2_BW_RSVD9 = 0x0e,
+ CXD2880_DVBT2_BW_RSVD10 = 0x0f,
+ CXD2880_DVBT2_BW_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_l1pre_type {
+ CXD2880_DVBT2_L1PRE_TYPE_TS = 0x00,
+ CXD2880_DVBT2_L1PRE_TYPE_GS = 0x01,
+ CXD2880_DVBT2_L1PRE_TYPE_TS_GS = 0x02,
+ CXD2880_DVBT2_L1PRE_TYPE_RESERVED = 0x03,
+ CXD2880_DVBT2_L1PRE_TYPE_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_papr {
+ CXD2880_DVBT2_PAPR_0 = 0x00,
+ CXD2880_DVBT2_PAPR_1 = 0x01,
+ CXD2880_DVBT2_PAPR_2 = 0x02,
+ CXD2880_DVBT2_PAPR_3 = 0x03,
+ CXD2880_DVBT2_PAPR_RSVD1 = 0x04,
+ CXD2880_DVBT2_PAPR_RSVD2 = 0x05,
+ CXD2880_DVBT2_PAPR_RSVD3 = 0x06,
+ CXD2880_DVBT2_PAPR_RSVD4 = 0x07,
+ CXD2880_DVBT2_PAPR_RSVD5 = 0x08,
+ CXD2880_DVBT2_PAPR_RSVD6 = 0x09,
+ CXD2880_DVBT2_PAPR_RSVD7 = 0x0a,
+ CXD2880_DVBT2_PAPR_RSVD8 = 0x0b,
+ CXD2880_DVBT2_PAPR_RSVD9 = 0x0c,
+ CXD2880_DVBT2_PAPR_RSVD10 = 0x0d,
+ CXD2880_DVBT2_PAPR_RSVD11 = 0x0e,
+ CXD2880_DVBT2_PAPR_RSVD12 = 0x0f,
+ CXD2880_DVBT2_PAPR_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_l1post_constell {
+ CXD2880_DVBT2_L1POST_BPSK = 0x00,
+ CXD2880_DVBT2_L1POST_QPSK = 0x01,
+ CXD2880_DVBT2_L1POST_QAM16 = 0x02,
+ CXD2880_DVBT2_L1POST_QAM64 = 0x03,
+ CXD2880_DVBT2_L1POST_C_RSVD1 = 0x04,
+ CXD2880_DVBT2_L1POST_C_RSVD2 = 0x05,
+ CXD2880_DVBT2_L1POST_C_RSVD3 = 0x06,
+ CXD2880_DVBT2_L1POST_C_RSVD4 = 0x07,
+ CXD2880_DVBT2_L1POST_C_RSVD5 = 0x08,
+ CXD2880_DVBT2_L1POST_C_RSVD6 = 0x09,
+ CXD2880_DVBT2_L1POST_C_RSVD7 = 0x0a,
+ CXD2880_DVBT2_L1POST_C_RSVD8 = 0x0b,
+ CXD2880_DVBT2_L1POST_C_RSVD9 = 0x0c,
+ CXD2880_DVBT2_L1POST_C_RSVD10 = 0x0d,
+ CXD2880_DVBT2_L1POST_C_RSVD11 = 0x0e,
+ CXD2880_DVBT2_L1POST_C_RSVD12 = 0x0f,
+ CXD2880_DVBT2_L1POST_CONSTELL_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_l1post_cr {
+ CXD2880_DVBT2_L1POST_R1_2 = 0x00,
+ CXD2880_DVBT2_L1POST_R_RSVD1 = 0x01,
+ CXD2880_DVBT2_L1POST_R_RSVD2 = 0x02,
+ CXD2880_DVBT2_L1POST_R_RSVD3 = 0x03,
+ CXD2880_DVBT2_L1POST_R_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_l1post_fec_type {
+ CXD2880_DVBT2_L1POST_FEC_LDPC16K = 0x00,
+ CXD2880_DVBT2_L1POST_FEC_RSVD1 = 0x01,
+ CXD2880_DVBT2_L1POST_FEC_RSVD2 = 0x02,
+ CXD2880_DVBT2_L1POST_FEC_RSVD3 = 0x03,
+ CXD2880_DVBT2_L1POST_FEC_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_pp {
+ CXD2880_DVBT2_PP1 = 0x00,
+ CXD2880_DVBT2_PP2 = 0x01,
+ CXD2880_DVBT2_PP3 = 0x02,
+ CXD2880_DVBT2_PP4 = 0x03,
+ CXD2880_DVBT2_PP5 = 0x04,
+ CXD2880_DVBT2_PP6 = 0x05,
+ CXD2880_DVBT2_PP7 = 0x06,
+ CXD2880_DVBT2_PP8 = 0x07,
+ CXD2880_DVBT2_PP_RSVD1 = 0x08,
+ CXD2880_DVBT2_PP_RSVD2 = 0x09,
+ CXD2880_DVBT2_PP_RSVD3 = 0x0a,
+ CXD2880_DVBT2_PP_RSVD4 = 0x0b,
+ CXD2880_DVBT2_PP_RSVD5 = 0x0c,
+ CXD2880_DVBT2_PP_RSVD6 = 0x0d,
+ CXD2880_DVBT2_PP_RSVD7 = 0x0e,
+ CXD2880_DVBT2_PP_RSVD8 = 0x0f,
+ CXD2880_DVBT2_PP_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_plp_code_rate {
+ CXD2880_DVBT2_R1_2 = 0x00,
+ CXD2880_DVBT2_R3_5 = 0x01,
+ CXD2880_DVBT2_R2_3 = 0x02,
+ CXD2880_DVBT2_R3_4 = 0x03,
+ CXD2880_DVBT2_R4_5 = 0x04,
+ CXD2880_DVBT2_R5_6 = 0x05,
+ CXD2880_DVBT2_R1_3 = 0x06,
+ CXD2880_DVBT2_R2_5 = 0x07,
+ CXD2880_DVBT2_PLP_CR_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_plp_constell {
+ CXD2880_DVBT2_QPSK = 0x00,
+ CXD2880_DVBT2_QAM16 = 0x01,
+ CXD2880_DVBT2_QAM64 = 0x02,
+ CXD2880_DVBT2_QAM256 = 0x03,
+ CXD2880_DVBT2_CON_RSVD1 = 0x04,
+ CXD2880_DVBT2_CON_RSVD2 = 0x05,
+ CXD2880_DVBT2_CON_RSVD3 = 0x06,
+ CXD2880_DVBT2_CON_RSVD4 = 0x07,
+ CXD2880_DVBT2_CONSTELL_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_plp_type {
+ CXD2880_DVBT2_PLP_TYPE_COMMON = 0x00,
+ CXD2880_DVBT2_PLP_TYPE_DATA1 = 0x01,
+ CXD2880_DVBT2_PLP_TYPE_DATA2 = 0x02,
+ CXD2880_DVBT2_PLP_TYPE_RSVD1 = 0x03,
+ CXD2880_DVBT2_PLP_TYPE_RSVD2 = 0x04,
+ CXD2880_DVBT2_PLP_TYPE_RSVD3 = 0x05,
+ CXD2880_DVBT2_PLP_TYPE_RSVD4 = 0x06,
+ CXD2880_DVBT2_PLP_TYPE_RSVD5 = 0x07,
+ CXD2880_DVBT2_PLP_TYPE_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_plp_payload {
+ CXD2880_DVBT2_PLP_PAYLOAD_GFPS = 0x00,
+ CXD2880_DVBT2_PLP_PAYLOAD_GCS = 0x01,
+ CXD2880_DVBT2_PLP_PAYLOAD_GSE = 0x02,
+ CXD2880_DVBT2_PLP_PAYLOAD_TS = 0x03,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD1 = 0x04,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD2 = 0x05,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD3 = 0x06,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD4 = 0x07,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD5 = 0x08,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD6 = 0x09,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD7 = 0x0a,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD8 = 0x0b,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD9 = 0x0c,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD10 = 0x0d,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD11 = 0x0e,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD12 = 0x0f,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD13 = 0x10,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD14 = 0x11,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD15 = 0x12,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD16 = 0x13,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD17 = 0x14,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD18 = 0x15,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD19 = 0x16,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD20 = 0x17,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD21 = 0x18,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD22 = 0x19,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD23 = 0x1a,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD24 = 0x1b,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD25 = 0x1c,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD26 = 0x1d,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD27 = 0x1e,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD28 = 0x1f,
+ CXD2880_DVBT2_PLP_PAYLOAD_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_plp_fec {
+ CXD2880_DVBT2_FEC_LDPC_16K = 0x00,
+ CXD2880_DVBT2_FEC_LDPC_64K = 0x01,
+ CXD2880_DVBT2_FEC_RSVD1 = 0x02,
+ CXD2880_DVBT2_FEC_RSVD2 = 0x03,
+ CXD2880_DVBT2_FEC_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_plp_mode {
+ CXD2880_DVBT2_PLP_MODE_NOTSPECIFIED = 0x00,
+ CXD2880_DVBT2_PLP_MODE_NM = 0x01,
+ CXD2880_DVBT2_PLP_MODE_HEM = 0x02,
+ CXD2880_DVBT2_PLP_MODE_RESERVED = 0x03,
+ CXD2880_DVBT2_PLP_MODE_UNKNOWN = 0xff
+};
+
+enum cxd2880_dvbt2_plp_btype {
+ CXD2880_DVBT2_PLP_COMMON,
+ CXD2880_DVBT2_PLP_DATA
+};
+
+enum cxd2880_dvbt2_stream {
+ CXD2880_DVBT2_STREAM_GENERIC_PACKETIZED = 0x00,
+ CXD2880_DVBT2_STREAM_GENERIC_CONTINUOUS = 0x01,
+ CXD2880_DVBT2_STREAM_GENERIC_ENCAPSULATED = 0x02,
+ CXD2880_DVBT2_STREAM_TRANSPORT = 0x03,
+ CXD2880_DVBT2_STREAM_UNKNOWN = 0xff
+};
+
+struct cxd2880_dvbt2_l1pre {
+ enum cxd2880_dvbt2_l1pre_type type;
+ u8 bw_ext;
+ enum cxd2880_dvbt2_s1 s1;
+ u8 s2;
+ u8 mixed;
+ enum cxd2880_dvbt2_mode fft_mode;
+ u8 l1_rep;
+ enum cxd2880_dvbt2_guard gi;
+ enum cxd2880_dvbt2_papr papr;
+ enum cxd2880_dvbt2_l1post_constell mod;
+ enum cxd2880_dvbt2_l1post_cr cr;
+ enum cxd2880_dvbt2_l1post_fec_type fec;
+ u32 l1_post_size;
+ u32 l1_post_info_size;
+ enum cxd2880_dvbt2_pp pp;
+ u8 tx_id_availability;
+ u16 cell_id;
+ u16 network_id;
+ u16 sys_id;
+ u8 num_frames;
+ u16 num_symbols;
+ u8 regen;
+ u8 post_ext;
+ u8 num_rf_freqs;
+ u8 rf_idx;
+ enum cxd2880_dvbt2_version t2_version;
+ u8 l1_post_scrambled;
+ u8 t2_base_lite;
+ u32 crc32;
+};
+
+struct cxd2880_dvbt2_plp {
+ u8 id;
+ enum cxd2880_dvbt2_plp_type type;
+ enum cxd2880_dvbt2_plp_payload payload;
+ u8 ff;
+ u8 first_rf_idx;
+ u8 first_frm_idx;
+ u8 group_id;
+ enum cxd2880_dvbt2_plp_constell constell;
+ enum cxd2880_dvbt2_plp_code_rate plp_cr;
+ u8 rot;
+ enum cxd2880_dvbt2_plp_fec fec;
+ u16 num_blocks_max;
+ u8 frm_int;
+ u8 til_len;
+ u8 til_type;
+ u8 in_band_a_flag;
+ u8 in_band_b_flag;
+ u16 rsvd;
+ enum cxd2880_dvbt2_plp_mode plp_mode;
+ u8 static_flag;
+ u8 static_padding_flag;
+};
+
+struct cxd2880_dvbt2_l1post {
+ u16 sub_slices_per_frame;
+ u8 num_plps;
+ u8 num_aux;
+ u8 aux_cfg_rfu;
+ u8 rf_idx;
+ u32 freq;
+ u8 fef_type;
+ u32 fef_length;
+ u8 fef_intvl;
+};
+
+struct cxd2880_dvbt2_ofdm {
+ u8 mixed;
+ u8 is_miso;
+ enum cxd2880_dvbt2_mode mode;
+ enum cxd2880_dvbt2_guard gi;
+ enum cxd2880_dvbt2_pp pp;
+ u8 bw_ext;
+ enum cxd2880_dvbt2_papr papr;
+ u16 num_symbols;
+};
+
+struct cxd2880_dvbt2_bbheader {
+ enum cxd2880_dvbt2_stream stream_input;
+ u8 is_single_input_stream;
+ u8 is_constant_coding_modulation;
+ u8 issy_indicator;
+ u8 null_packet_deletion;
+ u8 ext;
+ u8 input_stream_identifier;
+ u16 user_packet_length;
+ u16 data_field_length;
+ u8 sync_byte;
+ u32 issy;
+ enum cxd2880_dvbt2_plp_mode plp_mode;
+};
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
new file mode 100644
index 000000000000..5302ab0964c1
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_integ.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * integration layer common functions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#include <linux/ktime.h>
+#include <linux/errno.h>
+
+#include "cxd2880_tnrdmd.h"
+#include "cxd2880_tnrdmd_mon.h"
+#include "cxd2880_integ.h"
+
+int cxd2880_integ_init(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ int ret;
+ ktime_t start;
+ u8 cpu_task_completed = 0;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = cxd2880_tnrdmd_init1(tnr_dmd);
+ if (ret)
+ return ret;
+
+ start = ktime_get();
+
+ while (1) {
+ ret =
+ cxd2880_tnrdmd_check_internal_cpu_status(tnr_dmd,
+ &cpu_task_completed);
+ if (ret)
+ return ret;
+
+ if (cpu_task_completed)
+ break;
+
+ if (ktime_to_ms(ktime_sub(ktime_get(), start)) >
+ CXD2880_TNRDMD_WAIT_INIT_TIMEOUT)
+ return -ETIMEDOUT;
+
+ usleep_range(CXD2880_TNRDMD_WAIT_INIT_INTVL,
+ CXD2880_TNRDMD_WAIT_INIT_INTVL + 1000);
+ }
+
+ return cxd2880_tnrdmd_init2(tnr_dmd);
+}
+
+int cxd2880_integ_cancel(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ atomic_set(&tnr_dmd->cancel, 1);
+
+ return 0;
+}
+
+int cxd2880_integ_check_cancellation(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (atomic_read(&tnr_dmd->cancel) != 0)
+ return -ECANCELED;
+
+ return 0;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
new file mode 100644
index 000000000000..7160225db8b9
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_integ.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * integration layer common interface
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_INTEG_H
+#define CXD2880_INTEG_H
+
+#include "cxd2880_tnrdmd.h"
+
+#define CXD2880_TNRDMD_WAIT_INIT_TIMEOUT 500
+#define CXD2880_TNRDMD_WAIT_INIT_INTVL 10
+
+#define CXD2880_TNRDMD_WAIT_AGC_STABLE 100
+
+int cxd2880_integ_init(struct cxd2880_tnrdmd *tnr_dmd);
+
+int cxd2880_integ_cancel(struct cxd2880_tnrdmd *tnr_dmd);
+
+int cxd2880_integ_check_cancellation(struct cxd2880_tnrdmd
+ *tnr_dmd);
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_io.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
new file mode 100644
index 000000000000..9d932bccfa6c
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_io.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * register I/O interface functions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#include "cxd2880_io.h"
+
+int cxd2880_io_common_write_one_reg(struct cxd2880_io *io,
+ enum cxd2880_io_tgt tgt,
+ u8 sub_address, u8 data)
+{
+ if (!io)
+ return -EINVAL;
+
+ return io->write_regs(io, tgt, sub_address, &data, 1);
+}
+
+int cxd2880_io_set_reg_bits(struct cxd2880_io *io,
+ enum cxd2880_io_tgt tgt,
+ u8 sub_address, u8 data, u8 mask)
+{
+ int ret;
+
+ if (!io)
+ return -EINVAL;
+
+ if (mask == 0x00)
+ return 0;
+
+ if (mask != 0xff) {
+ u8 rdata = 0x00;
+
+ ret = io->read_regs(io, tgt, sub_address, &rdata, 1);
+ if (ret)
+ return ret;
+
+ data = (data & mask) | (rdata & (mask ^ 0xff));
+ }
+
+ return io->write_reg(io, tgt, sub_address, data);
+}
+
+int cxd2880_io_write_multi_regs(struct cxd2880_io *io,
+ enum cxd2880_io_tgt tgt,
+ const struct cxd2880_reg_value reg_value[],
+ u8 size)
+{
+ int ret;
+ int i;
+
+ if (!io)
+ return -EINVAL;
+
+ for (i = 0; i < size ; i++) {
+ ret = io->write_reg(io, tgt, reg_value[i].addr,
+ reg_value[i].value);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_io.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
new file mode 100644
index 000000000000..ba550278881d
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_io.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * register I/O interface definitions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_IO_H
+#define CXD2880_IO_H
+
+#include "cxd2880_common.h"
+
+enum cxd2880_io_tgt {
+ CXD2880_IO_TGT_SYS,
+ CXD2880_IO_TGT_DMD
+};
+
+struct cxd2880_reg_value {
+ u8 addr;
+ u8 value;
+};
+
+struct cxd2880_io {
+ int (*read_regs)(struct cxd2880_io *io,
+ enum cxd2880_io_tgt tgt, u8 sub_address,
+ u8 *data, u32 size);
+ int (*write_regs)(struct cxd2880_io *io,
+ enum cxd2880_io_tgt tgt, u8 sub_address,
+ const u8 *data, u32 size);
+ int (*write_reg)(struct cxd2880_io *io,
+ enum cxd2880_io_tgt tgt, u8 sub_address,
+ u8 data);
+ void *if_object;
+ u8 i2c_address_sys;
+ u8 i2c_address_demod;
+ u8 slave_select;
+ void *user;
+};
+
+int cxd2880_io_common_write_one_reg(struct cxd2880_io *io,
+ enum cxd2880_io_tgt tgt,
+ u8 sub_address, u8 data);
+
+int cxd2880_io_set_reg_bits(struct cxd2880_io *io,
+ enum cxd2880_io_tgt tgt,
+ u8 sub_address, u8 data, u8 mask);
+
+int cxd2880_io_write_multi_regs(struct cxd2880_io *io,
+ enum cxd2880_io_tgt tgt,
+ const struct cxd2880_reg_value reg_value[],
+ u8 size);
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h
new file mode 100644
index 000000000000..2be207461847
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_spi.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * SPI access definitions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_SPI_H
+#define CXD2880_SPI_H
+
+#include "cxd2880_common.h"
+
+enum cxd2880_spi_mode {
+ CXD2880_SPI_MODE_0,
+ CXD2880_SPI_MODE_1,
+ CXD2880_SPI_MODE_2,
+ CXD2880_SPI_MODE_3
+};
+
+struct cxd2880_spi {
+ int (*read)(struct cxd2880_spi *spi, u8 *data,
+ u32 size);
+ int (*write)(struct cxd2880_spi *spi, const u8 *data,
+ u32 size);
+ int (*write_read)(struct cxd2880_spi *spi,
+ const u8 *tx_data, u32 tx_size,
+ u8 *rx_data, u32 rx_size);
+ u32 flags;
+ void *user;
+};
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c
new file mode 100644
index 000000000000..b8cbaa8d7aff
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_spi_device.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * SPI access functions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#include <linux/spi/spi.h>
+
+#include "cxd2880_spi_device.h"
+
+static int cxd2880_spi_device_write(struct cxd2880_spi *spi,
+ const u8 *data, u32 size)
+{
+ struct cxd2880_spi_device *spi_device = NULL;
+ struct spi_message msg;
+ struct spi_transfer tx;
+ int result = 0;
+
+ if (!spi || !spi->user || !data || size == 0)
+ return -EINVAL;
+
+ spi_device = spi->user;
+
+ memset(&tx, 0, sizeof(tx));
+ tx.tx_buf = data;
+ tx.len = size;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&tx, &msg);
+ result = spi_sync(spi_device->spi, &msg);
+
+ if (result < 0)
+ return -EIO;
+
+ return 0;
+}
+
+static int cxd2880_spi_device_write_read(struct cxd2880_spi *spi,
+ const u8 *tx_data,
+ u32 tx_size,
+ u8 *rx_data,
+ u32 rx_size)
+{
+ struct cxd2880_spi_device *spi_device = NULL;
+ int result = 0;
+
+ if (!spi || !spi->user || !tx_data ||
+ !tx_size || !rx_data || !rx_size)
+ return -EINVAL;
+
+ spi_device = spi->user;
+
+ result = spi_write_then_read(spi_device->spi, tx_data,
+ tx_size, rx_data, rx_size);
+ if (result < 0)
+ return -EIO;
+
+ return 0;
+}
+
+int
+cxd2880_spi_device_initialize(struct cxd2880_spi_device *spi_device,
+ enum cxd2880_spi_mode mode,
+ u32 speed_hz)
+{
+ int result = 0;
+ struct spi_device *spi = spi_device->spi;
+
+ switch (mode) {
+ case CXD2880_SPI_MODE_0:
+ spi->mode = SPI_MODE_0;
+ break;
+ case CXD2880_SPI_MODE_1:
+ spi->mode = SPI_MODE_1;
+ break;
+ case CXD2880_SPI_MODE_2:
+ spi->mode = SPI_MODE_2;
+ break;
+ case CXD2880_SPI_MODE_3:
+ spi->mode = SPI_MODE_3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ spi->max_speed_hz = speed_hz;
+ spi->bits_per_word = 8;
+ result = spi_setup(spi);
+ if (result != 0) {
+ pr_err("spi_setup failed %d\n", result);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int cxd2880_spi_device_create_spi(struct cxd2880_spi *spi,
+ struct cxd2880_spi_device *spi_device)
+{
+ if (!spi || !spi_device)
+ return -EINVAL;
+
+ spi->read = NULL;
+ spi->write = cxd2880_spi_device_write;
+ spi->write_read = cxd2880_spi_device_write_read;
+ spi->flags = 0;
+ spi->user = spi_device;
+
+ return 0;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h
new file mode 100644
index 000000000000..05e3a03de3a3
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_spi_device.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * SPI access interface
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_SPI_DEVICE_H
+#define CXD2880_SPI_DEVICE_H
+
+#include "cxd2880_spi.h"
+
+struct cxd2880_spi_device {
+ struct spi_device *spi;
+};
+
+int cxd2880_spi_device_initialize(struct cxd2880_spi_device *spi_device,
+ enum cxd2880_spi_mode mode,
+ u32 speedHz);
+
+int cxd2880_spi_device_create_spi(struct cxd2880_spi *spi,
+ struct cxd2880_spi_device *spi_device);
+
+#endif /* CXD2880_SPI_DEVICE_H */
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
new file mode 100644
index 000000000000..4cf2d7cfd3f5
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
@@ -0,0 +1,3519 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_tnrdmd.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * common control functions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#include <media/dvb_frontend.h>
+#include "cxd2880_common.h"
+#include "cxd2880_tnrdmd.h"
+#include "cxd2880_tnrdmd_mon.h"
+#include "cxd2880_tnrdmd_dvbt.h"
+#include "cxd2880_tnrdmd_dvbt2.h"
+
+static const struct cxd2880_reg_value p_init1_seq[] = {
+ {0x11, 0x16}, {0x00, 0x10},
+};
+
+static const struct cxd2880_reg_value rf_init1_seq1[] = {
+ {0x4f, 0x18}, {0x61, 0x00}, {0x71, 0x00}, {0x9d, 0x01},
+ {0x7d, 0x02}, {0x8f, 0x01}, {0x8b, 0xc6}, {0x9a, 0x03},
+ {0x1c, 0x00},
+};
+
+static const struct cxd2880_reg_value rf_init1_seq2[] = {
+ {0xb9, 0x07}, {0x33, 0x01}, {0xc1, 0x01}, {0xc4, 0x1e},
+};
+
+static const struct cxd2880_reg_value rf_init1_seq3[] = {
+ {0x00, 0x10}, {0x51, 0x01}, {0xc5, 0x07}, {0x00, 0x11},
+ {0x70, 0xe9}, {0x76, 0x0a}, {0x78, 0x32}, {0x7a, 0x46},
+ {0x7c, 0x86}, {0x7e, 0xa4}, {0x00, 0x10}, {0xe1, 0x01},
+};
+
+static const struct cxd2880_reg_value rf_init1_seq4[] = {
+ {0x15, 0x00}, {0x00, 0x16}
+};
+
+static const struct cxd2880_reg_value rf_init1_seq5[] = {
+ {0x00, 0x00}, {0x25, 0x00}
+};
+
+static const struct cxd2880_reg_value rf_init1_seq6[] = {
+ {0x02, 0x00}, {0x00, 0x00}, {0x21, 0x01}, {0x00, 0xe1},
+ {0x8f, 0x16}, {0x67, 0x60}, {0x6a, 0x0f}, {0x6c, 0x17}
+};
+
+static const struct cxd2880_reg_value rf_init1_seq7[] = {
+ {0x00, 0xe2}, {0x41, 0xa0}, {0x4b, 0x68}, {0x00, 0x00},
+ {0x21, 0x00}, {0x10, 0x01},
+};
+
+static const struct cxd2880_reg_value rf_init1_seq8[] = {
+ {0x00, 0x10}, {0x25, 0x01},
+};
+
+static const struct cxd2880_reg_value rf_init1_seq9[] = {
+ {0x00, 0x10}, {0x14, 0x01}, {0x00, 0x00}, {0x26, 0x00},
+};
+
+static const struct cxd2880_reg_value rf_init2_seq1[] = {
+ {0x00, 0x14}, {0x1b, 0x01},
+};
+
+static const struct cxd2880_reg_value rf_init2_seq2[] = {
+ {0x00, 0x00}, {0x21, 0x01}, {0x00, 0xe1}, {0xd3, 0x00},
+ {0x00, 0x00}, {0x21, 0x00},
+};
+
+static const struct cxd2880_reg_value x_tune1_seq1[] = {
+ {0x00, 0x00}, {0x10, 0x01},
+};
+
+static const struct cxd2880_reg_value x_tune1_seq2[] = {
+ {0x62, 0x00}, {0x00, 0x15},
+};
+
+static const struct cxd2880_reg_value x_tune2_seq1[] = {
+ {0x00, 0x1a}, {0x29, 0x01},
+};
+
+static const struct cxd2880_reg_value x_tune2_seq2[] = {
+ {0x62, 0x01}, {0x00, 0x11}, {0x2d, 0x00}, {0x2f, 0x00},
+};
+
+static const struct cxd2880_reg_value x_tune2_seq3[] = {
+ {0x00, 0x00}, {0x10, 0x00}, {0x21, 0x01},
+};
+
+static const struct cxd2880_reg_value x_tune2_seq4[] = {
+ {0x00, 0xe1}, {0x8a, 0x87},
+};
+
+static const struct cxd2880_reg_value x_tune2_seq5[] = {
+ {0x00, 0x00}, {0x21, 0x00},
+};
+
+static const struct cxd2880_reg_value x_tune3_seq[] = {
+ {0x00, 0x00}, {0x21, 0x01}, {0x00, 0xe2}, {0x41, 0xa0},
+ {0x00, 0x00}, {0x21, 0x00}, {0xfe, 0x01},
+};
+
+static const struct cxd2880_reg_value x_tune4_seq[] = {
+ {0x00, 0x00}, {0xfe, 0x01},
+};
+
+static const struct cxd2880_reg_value x_sleep1_seq[] = {
+ {0x00, 0x00}, {0x57, 0x03},
+};
+
+static const struct cxd2880_reg_value x_sleep2_seq1[] = {
+ {0x00, 0x2d}, {0xb1, 0x01},
+};
+
+static const struct cxd2880_reg_value x_sleep2_seq2[] = {
+ {0x00, 0x10}, {0xf4, 0x00}, {0xf3, 0x00}, {0xf2, 0x00},
+ {0xf1, 0x00}, {0xf0, 0x00}, {0xef, 0x00},
+};
+
+static const struct cxd2880_reg_value x_sleep3_seq[] = {
+ {0x00, 0x00}, {0xfd, 0x00},
+};
+
+static const struct cxd2880_reg_value x_sleep4_seq[] = {
+ {0x00, 0x00}, {0x21, 0x01}, {0x00, 0xe2}, {0x41, 0x00},
+ {0x00, 0x00}, {0x21, 0x00},
+};
+
+static const struct cxd2880_reg_value spll_reset_seq1[] = {
+ {0x00, 0x10}, {0x29, 0x01}, {0x28, 0x01}, {0x27, 0x01},
+ {0x26, 0x01},
+};
+
+static const struct cxd2880_reg_value spll_reset_seq2[] = {
+ {0x00, 0x00}, {0x10, 0x00},
+};
+
+static const struct cxd2880_reg_value spll_reset_seq3[] = {
+ {0x00, 0x00}, {0x27, 0x00}, {0x22, 0x01},
+};
+
+static const struct cxd2880_reg_value spll_reset_seq4[] = {
+ {0x00, 0x00}, {0x27, 0x01},
+};
+
+static const struct cxd2880_reg_value spll_reset_seq5[] = {
+ {0x00, 0x00}, {0x10, 0x01},
+};
+
+static const struct cxd2880_reg_value t_power_x_seq1[] = {
+ {0x00, 0x10}, {0x29, 0x01}, {0x28, 0x01}, {0x27, 0x01},
+};
+
+static const struct cxd2880_reg_value t_power_x_seq2[] = {
+ {0x00, 0x00}, {0x10, 0x00},
+};
+
+static const struct cxd2880_reg_value t_power_x_seq3[] = {
+ {0x00, 0x00}, {0x27, 0x00}, {0x25, 0x01},
+};
+
+static const struct cxd2880_reg_value t_power_x_seq4[] = {
+ {0x00, 0x00}, {0x2a, 0x00},
+};
+
+static const struct cxd2880_reg_value t_power_x_seq5[] = {
+ {0x00, 0x00}, {0x25, 0x00},
+};
+
+static const struct cxd2880_reg_value t_power_x_seq6[] = {
+ {0x00, 0x00}, {0x27, 0x01},
+};
+
+static const struct cxd2880_reg_value t_power_x_seq7[] = {
+ {0x00, 0x00}, {0x10, 0x01},
+};
+
+static const struct cxd2880_reg_value set_ts_pin_seq[] = {
+ {0x50, 0x3f}, {0x52, 0x1f},
+
+};
+
+static const struct cxd2880_reg_value set_ts_output_seq1[] = {
+ {0x00, 0x00}, {0x52, 0x00},
+};
+
+static const struct cxd2880_reg_value set_ts_output_seq2[] = {
+ {0x00, 0x00}, {0xc3, 0x00},
+
+};
+
+static const struct cxd2880_reg_value set_ts_output_seq3[] = {
+ {0x00, 0x00}, {0xc3, 0x01},
+
+};
+
+static const struct cxd2880_reg_value set_ts_output_seq4[] = {
+ {0x00, 0x00}, {0x52, 0x1f},
+
+};
+
+static int p_init1(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ u8 data = 0;
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE ||
+ tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ switch (tnr_dmd->create_param.ts_output_if) {
+ case CXD2880_TNRDMD_TSOUT_IF_TS:
+ data = 0x00;
+ break;
+ case CXD2880_TNRDMD_TSOUT_IF_SPI:
+ data = 0x01;
+ break;
+ case CXD2880_TNRDMD_TSOUT_IF_SDIO:
+ data = 0x02;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x10, data);
+ if (ret)
+ return ret;
+ }
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ p_init1_seq,
+ ARRAY_SIZE(p_init1_seq));
+ if (ret)
+ return ret;
+
+ switch (tnr_dmd->chip_id) {
+ case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X:
+ data = 0x1a;
+ break;
+ case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11:
+ data = 0x16;
+ break;
+ default:
+ return -ENOTTY;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x10, data);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->create_param.en_internal_ldo)
+ data = 0x01;
+ else
+ data = 0x00;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x11, data);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x13, data);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x12, data);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x10);
+ if (ret)
+ return ret;
+
+ switch (tnr_dmd->chip_id) {
+ case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X:
+ data = 0x01;
+ break;
+ case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11:
+ data = 0x00;
+ break;
+ default:
+ return -ENOTTY;
+ }
+
+ return tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x69, data);
+}
+
+static int p_init2(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ u8 data[6] = { 0 };
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+ data[0] = tnr_dmd->create_param.xosc_cap;
+ data[1] = tnr_dmd->create_param.xosc_i;
+ switch (tnr_dmd->create_param.xtal_share_type) {
+ case CXD2880_TNRDMD_XTAL_SHARE_NONE:
+ data[2] = 0x01;
+ data[3] = 0x00;
+ break;
+ case CXD2880_TNRDMD_XTAL_SHARE_EXTREF:
+ data[2] = 0x00;
+ data[3] = 0x00;
+ break;
+ case CXD2880_TNRDMD_XTAL_SHARE_MASTER:
+ data[2] = 0x01;
+ data[3] = 0x01;
+ break;
+ case CXD2880_TNRDMD_XTAL_SHARE_SLAVE:
+ data[2] = 0x00;
+ data[3] = 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+ data[4] = 0x06;
+ data[5] = 0x00;
+
+ return tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x13, data, 6);
+}
+
+static int p_init3(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ u8 data[2] = { 0 };
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+
+ switch (tnr_dmd->diver_mode) {
+ case CXD2880_TNRDMD_DIVERMODE_SINGLE:
+ data[0] = 0x00;
+ break;
+ case CXD2880_TNRDMD_DIVERMODE_MAIN:
+ data[0] = 0x03;
+ break;
+ case CXD2880_TNRDMD_DIVERMODE_SUB:
+ data[0] = 0x02;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ data[1] = 0x01;
+
+ return tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x1f, data, 2);
+}
+
+static int rf_init1(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ u8 data[8] = { 0 };
+ static const u8 rf_init1_cdata1[40] = {
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x04, 0x04, 0x04, 0x03, 0x03,
+ 0x03, 0x04, 0x04, 0x05, 0x05, 0x05, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x03, 0x02, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x03, 0x04, 0x04, 0x04
+ };
+
+ static const u8 rf_init1_cdata2[5] = {0xff, 0x00, 0x00, 0x00, 0x00};
+ static const u8 rf_init1_cdata3[80] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x02, 0x00, 0x63, 0x00, 0x00,
+ 0x00, 0x03, 0x00, 0x04, 0x00, 0x04, 0x00,
+ 0x06, 0x00, 0x06, 0x00, 0x08, 0x00, 0x09,
+ 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x0d, 0x00,
+ 0x0d, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f,
+ 0x00, 0x10, 0x00, 0x79, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01,
+ 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00,
+ 0x04, 0x00, 0x04, 0x00, 0x06, 0x00, 0x05,
+ 0x00, 0x07, 0x00, 0x07, 0x00, 0x08, 0x00,
+ 0x0a, 0x03, 0xe0
+ };
+
+ static const u8 rf_init1_cdata4[8] = {
+ 0x20, 0x20, 0x30, 0x41, 0x50, 0x5f, 0x6f, 0x80
+ };
+
+ static const u8 rf_init1_cdata5[50] = {
+ 0x00, 0x09, 0x00, 0x08, 0x00, 0x07, 0x00,
+ 0x06, 0x00, 0x05, 0x00, 0x03, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x08, 0x00, 0x08, 0x00, 0x0c,
+ 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0f, 0x00,
+ 0x0e, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x0f,
+ 0x00, 0x0e, 0x00, 0x10, 0x00, 0x0f, 0x00,
+ 0x0e
+ };
+
+ u8 addr = 0;
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+ data[0] = 0x01;
+ data[1] = 0x00;
+ data[2] = 0x01;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x21, data, 3);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x10);
+ if (ret)
+ return ret;
+ data[0] = 0x01;
+ data[1] = 0x01;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x17, data, 2);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->create_param.stationary_use) {
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x1a, 0x06);
+ if (ret)
+ return ret;
+ }
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ rf_init1_seq1,
+ ARRAY_SIZE(rf_init1_seq1));
+ if (ret)
+ return ret;
+
+ data[0] = 0x00;
+ if (tnr_dmd->create_param.is_cxd2881gg &&
+ tnr_dmd->create_param.xtal_share_type ==
+ CXD2880_TNRDMD_XTAL_SHARE_SLAVE)
+ data[1] = 0x00;
+ else
+ data[1] = 0x1f;
+ data[2] = 0x0a;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0xb5, data, 3);
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ rf_init1_seq2,
+ ARRAY_SIZE(rf_init1_seq2));
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->chip_id == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X) {
+ data[0] = 0x34;
+ data[1] = 0x2c;
+ } else {
+ data[0] = 0x2f;
+ data[1] = 0x25;
+ }
+ data[2] = 0x15;
+ data[3] = 0x19;
+ data[4] = 0x1b;
+ data[5] = 0x15;
+ data[6] = 0x19;
+ data[7] = 0x1b;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0xd9, data, 8);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x11);
+ if (ret)
+ return ret;
+ data[0] = 0x6c;
+ data[1] = 0x10;
+ data[2] = 0xa6;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x44, data, 3);
+ if (ret)
+ return ret;
+ data[0] = 0x16;
+ data[1] = 0xa8;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x50, data, 2);
+ if (ret)
+ return ret;
+ data[0] = 0x00;
+ data[1] = 0x22;
+ data[2] = 0x00;
+ data[3] = 0x88;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x62, data, 4);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x74, 0x75);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x7f, rf_init1_cdata1, 40);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x16);
+ if (ret)
+ return ret;
+ data[0] = 0x00;
+ data[1] = 0x71;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x10, data, 2);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x23, 0x89);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x27, rf_init1_cdata2, 5);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x3a, rf_init1_cdata3, 80);
+ if (ret)
+ return ret;
+
+ data[0] = 0x03;
+ data[1] = 0xe0;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0xbc, data, 2);
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ rf_init1_seq3,
+ ARRAY_SIZE(rf_init1_seq3));
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->create_param.stationary_use) {
+ data[0] = 0x06;
+ data[1] = 0x07;
+ data[2] = 0x1a;
+ } else {
+ data[0] = 0x00;
+ data[1] = 0x08;
+ data[2] = 0x19;
+ }
+ data[3] = 0x0e;
+ data[4] = 0x09;
+ data[5] = 0x0e;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x12);
+ if (ret)
+ return ret;
+ for (addr = 0x10; addr < 0x9f; addr += 6) {
+ if (tnr_dmd->lna_thrs_tbl_air) {
+ u8 idx = 0;
+
+ idx = (addr - 0x10) / 6;
+ data[0] =
+ tnr_dmd->lna_thrs_tbl_air->thrs[idx].off_on;
+ data[1] =
+ tnr_dmd->lna_thrs_tbl_air->thrs[idx].on_off;
+ }
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ addr, data, 6);
+ if (ret)
+ return ret;
+ }
+
+ data[0] = 0x00;
+ data[1] = 0x08;
+ if (tnr_dmd->create_param.stationary_use)
+ data[2] = 0x1a;
+ else
+ data[2] = 0x19;
+ data[3] = 0x0e;
+ data[4] = 0x09;
+ data[5] = 0x0e;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x13);
+ if (ret)
+ return ret;
+ for (addr = 0x10; addr < 0xcf; addr += 6) {
+ if (tnr_dmd->lna_thrs_tbl_cable) {
+ u8 idx = 0;
+
+ idx = (addr - 0x10) / 6;
+ data[0] =
+ tnr_dmd->lna_thrs_tbl_cable->thrs[idx].off_on;
+ data[1] =
+ tnr_dmd->lna_thrs_tbl_cable->thrs[idx].on_off;
+ }
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ addr, data, 6);
+ if (ret)
+ return ret;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x11);
+ if (ret)
+ return ret;
+ data[0] = 0x08;
+ data[1] = 0x09;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0xbd, data, 2);
+ if (ret)
+ return ret;
+ data[0] = 0x08;
+ data[1] = 0x09;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0xc4, data, 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0xc9, rf_init1_cdata4, 8);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x14);
+ if (ret)
+ return ret;
+ data[0] = 0x15;
+ data[1] = 0x18;
+ data[2] = 0x00;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x10, data, 3);
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ rf_init1_seq4,
+ ARRAY_SIZE(rf_init1_seq4));
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x12, rf_init1_cdata5, 50);
+ if (ret)
+ return ret;
+
+ usleep_range(1000, 2000);
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x0a);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x10, data, 1);
+ if (ret)
+ return ret;
+ if ((data[0] & 0x01) == 0x00)
+ return -EINVAL;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ rf_init1_seq5,
+ ARRAY_SIZE(rf_init1_seq5));
+ if (ret)
+ return ret;
+
+ usleep_range(1000, 2000);
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x0a);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x11, data, 1);
+ if (ret)
+ return ret;
+ if ((data[0] & 0x01) == 0x00)
+ return -EINVAL;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ rf_init1_seq6,
+ ARRAY_SIZE(rf_init1_seq6));
+ if (ret)
+ return ret;
+
+ data[0] = 0x00;
+ data[1] = 0xfe;
+ data[2] = 0xee;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x6e, data, 3);
+ if (ret)
+ return ret;
+ data[0] = 0xa1;
+ data[1] = 0x8b;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x8d, data, 2);
+ if (ret)
+ return ret;
+ data[0] = 0x08;
+ data[1] = 0x09;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x77, data, 2);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->create_param.stationary_use) {
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x80, 0xaa);
+ if (ret)
+ return ret;
+ }
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ rf_init1_seq7,
+ ARRAY_SIZE(rf_init1_seq7));
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ rf_init1_seq8,
+ ARRAY_SIZE(rf_init1_seq8));
+ if (ret)
+ return ret;
+
+ usleep_range(1000, 2000);
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x1a);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x10, data, 1);
+ if (ret)
+ return ret;
+ if ((data[0] & 0x01) == 0x00)
+ return -EINVAL;
+
+ return cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ rf_init1_seq9,
+ ARRAY_SIZE(rf_init1_seq9));
+}
+
+static int rf_init2(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ u8 data[5] = { 0 };
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x10);
+ if (ret)
+ return ret;
+ data[0] = 0x40;
+ data[1] = 0x40;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0xea, data, 2);
+ if (ret)
+ return ret;
+
+ usleep_range(1000, 2000);
+
+ data[0] = 0x00;
+ if (tnr_dmd->chip_id == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X)
+ data[1] = 0x00;
+ else
+ data[1] = 0x01;
+ data[2] = 0x01;
+ data[3] = 0x03;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x30, data, 4);
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ rf_init2_seq1,
+ ARRAY_SIZE(rf_init2_seq1));
+ if (ret)
+ return ret;
+
+ return cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ rf_init2_seq2,
+ ARRAY_SIZE(rf_init2_seq2));
+}
+
+static int x_tune1(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dtv_sys sys, u32 freq_khz,
+ enum cxd2880_dtv_bandwidth bandwidth,
+ u8 is_cable, int shift_frequency_khz)
+{
+ u8 data[11] = { 0 };
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ x_tune1_seq1,
+ ARRAY_SIZE(x_tune1_seq1));
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x10);
+ if (ret)
+ return ret;
+
+ data[2] = 0x0e;
+ data[4] = 0x03;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0xe7, data, 5);
+ if (ret)
+ return ret;
+
+ data[0] = 0x1f;
+ data[1] = 0x80;
+ data[2] = 0x18;
+ data[3] = 0x00;
+ data[4] = 0x07;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0xe7, data, 5);
+ if (ret)
+ return ret;
+
+ usleep_range(1000, 2000);
+
+ data[0] = 0x72;
+ data[1] = 0x81;
+ data[3] = 0x1d;
+ data[4] = 0x6f;
+ data[5] = 0x7e;
+ data[7] = 0x1c;
+ switch (sys) {
+ case CXD2880_DTV_SYS_DVBT:
+ data[2] = 0x94;
+ data[6] = 0x91;
+ break;
+ case CXD2880_DTV_SYS_DVBT2:
+ data[2] = 0x96;
+ data[6] = 0x93;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x44, data, 8);
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ x_tune1_seq2,
+ ARRAY_SIZE(x_tune1_seq2));
+ if (ret)
+ return ret;
+
+ data[0] = 0x03;
+ data[1] = 0xe2;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x1e, data, 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x10);
+ if (ret)
+ return ret;
+
+ data[0] = is_cable ? 0x01 : 0x00;
+ data[1] = 0x00;
+ data[2] = 0x6b;
+ data[3] = 0x4d;
+
+ switch (bandwidth) {
+ case CXD2880_DTV_BW_1_7_MHZ:
+ data[4] = 0x03;
+ break;
+ case CXD2880_DTV_BW_5_MHZ:
+ case CXD2880_DTV_BW_6_MHZ:
+ data[4] = 0x00;
+ break;
+ case CXD2880_DTV_BW_7_MHZ:
+ data[4] = 0x01;
+ break;
+ case CXD2880_DTV_BW_8_MHZ:
+ data[4] = 0x02;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ data[5] = 0x00;
+
+ freq_khz += shift_frequency_khz;
+
+ data[6] = (freq_khz >> 16) & 0x0f;
+ data[7] = (freq_khz >> 8) & 0xff;
+ data[8] = freq_khz & 0xff;
+ data[9] = 0xff;
+ data[10] = 0xfe;
+
+ return tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x52, data, 11);
+}
+
+static int x_tune2(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dtv_bandwidth bandwidth,
+ enum cxd2880_tnrdmd_clockmode clk_mode,
+ int shift_frequency_khz)
+{
+ u8 data[3] = { 0 };
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x11);
+ if (ret)
+ return ret;
+
+ data[0] = 0x01;
+ data[1] = 0x0e;
+ data[2] = 0x01;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x2d, data, 3);
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ x_tune2_seq1,
+ ARRAY_SIZE(x_tune2_seq1));
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x2c, data, 1);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x10);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x60, data[0]);
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ x_tune2_seq2,
+ ARRAY_SIZE(x_tune2_seq2));
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ x_tune2_seq3,
+ ARRAY_SIZE(x_tune2_seq3));
+ if (ret)
+ return ret;
+
+ if (shift_frequency_khz != 0) {
+ int shift_freq = 0;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0xe1);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x60, data, 2);
+ if (ret)
+ return ret;
+
+ shift_freq = shift_frequency_khz * 1000;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ default:
+ if (shift_freq >= 0)
+ shift_freq = (shift_freq + 183 / 2) / 183;
+ else
+ shift_freq = (shift_freq - 183 / 2) / 183;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ if (shift_freq >= 0)
+ shift_freq = (shift_freq + 178 / 2) / 178;
+ else
+ shift_freq = (shift_freq - 178 / 2) / 178;
+ break;
+ }
+
+ shift_freq +=
+ cxd2880_convert2s_complement((data[0] << 8) | data[1], 16);
+
+ if (shift_freq > 32767)
+ shift_freq = 32767;
+ else if (shift_freq < -32768)
+ shift_freq = -32768;
+
+ data[0] = (shift_freq >> 8) & 0xff;
+ data[1] = shift_freq & 0xff;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x60, data, 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x69, data, 1);
+ if (ret)
+ return ret;
+
+ shift_freq = -shift_frequency_khz;
+
+ if (bandwidth == CXD2880_DTV_BW_1_7_MHZ) {
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ default:
+ if (shift_freq >= 0)
+ shift_freq =
+ (shift_freq * 1000 +
+ 17578 / 2) / 17578;
+ else
+ shift_freq =
+ (shift_freq * 1000 -
+ 17578 / 2) / 17578;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ if (shift_freq >= 0)
+ shift_freq =
+ (shift_freq * 1000 +
+ 17090 / 2) / 17090;
+ else
+ shift_freq =
+ (shift_freq * 1000 -
+ 17090 / 2) / 17090;
+ break;
+ }
+ } else {
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ default:
+ if (shift_freq >= 0)
+ shift_freq =
+ (shift_freq * 1000 +
+ 35156 / 2) / 35156;
+ else
+ shift_freq =
+ (shift_freq * 1000 -
+ 35156 / 2) / 35156;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ if (shift_freq >= 0)
+ shift_freq =
+ (shift_freq * 1000 +
+ 34180 / 2) / 34180;
+ else
+ shift_freq =
+ (shift_freq * 1000 -
+ 34180 / 2) / 34180;
+ break;
+ }
+ }
+
+ shift_freq += cxd2880_convert2s_complement(data[0], 8);
+
+ if (shift_freq > 127)
+ shift_freq = 127;
+ else if (shift_freq < -128)
+ shift_freq = -128;
+
+ data[0] = shift_freq & 0xff;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x69, data[0]);
+ if (ret)
+ return ret;
+ }
+
+ if (tnr_dmd->create_param.stationary_use) {
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ x_tune2_seq4,
+ ARRAY_SIZE(x_tune2_seq4));
+ if (ret)
+ return ret;
+ }
+
+ return cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ x_tune2_seq5,
+ ARRAY_SIZE(x_tune2_seq5));
+}
+
+static int x_tune3(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dtv_sys sys,
+ u8 en_fef_intmtnt_ctrl)
+{
+ u8 data[6] = { 0 };
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ x_tune3_seq,
+ ARRAY_SIZE(x_tune3_seq));
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x10);
+ if (ret)
+ return ret;
+
+ if (sys == CXD2880_DTV_SYS_DVBT2 && en_fef_intmtnt_ctrl)
+ memset(data, 0x01, sizeof(data));
+ else
+ memset(data, 0x00, sizeof(data));
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0xef, data, 6);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x2d);
+ if (ret)
+ return ret;
+ if (sys == CXD2880_DTV_SYS_DVBT2 && en_fef_intmtnt_ctrl)
+ data[0] = 0x00;
+ else
+ data[0] = 0x01;
+
+ return tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xb1, data[0]);
+}
+
+static int x_tune4(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ u8 data[2] = { 0 };
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return -EINVAL;
+
+ ret = tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+ data[0] = 0x14;
+ data[1] = 0x00;
+ ret = tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io,
+ CXD2880_IO_TGT_SYS,
+ 0x55, data, 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+ data[0] = 0x0b;
+ data[1] = 0xff;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x53, data, 2);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x57, 0x01);
+ if (ret)
+ return ret;
+ data[0] = 0x0b;
+ data[1] = 0xff;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x55, data, 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+ data[0] = 0x14;
+ data[1] = 0x00;
+ ret = tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io,
+ CXD2880_IO_TGT_SYS,
+ 0x53, data, 2);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
+ CXD2880_IO_TGT_SYS,
+ 0x57, 0x02);
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ x_tune4_seq,
+ ARRAY_SIZE(x_tune4_seq));
+ if (ret)
+ return ret;
+
+ return cxd2880_io_write_multi_regs(tnr_dmd->diver_sub->io,
+ CXD2880_IO_TGT_DMD,
+ x_tune4_seq,
+ ARRAY_SIZE(x_tune4_seq));
+}
+
+static int x_sleep1(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ u8 data[3] = { 0 };
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return -EINVAL;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ x_sleep1_seq,
+ ARRAY_SIZE(x_sleep1_seq));
+ if (ret)
+ return ret;
+
+ data[0] = 0x00;
+ data[1] = 0x00;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x53, data, 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+ data[0] = 0x1f;
+ data[1] = 0xff;
+ data[2] = 0x03;
+ ret = tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io,
+ CXD2880_IO_TGT_SYS,
+ 0x55, data, 3);
+ if (ret)
+ return ret;
+ data[0] = 0x00;
+ data[1] = 0x00;
+ ret = tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io,
+ CXD2880_IO_TGT_SYS,
+ 0x53, data, 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+ data[0] = 0x1f;
+ data[1] = 0xff;
+
+ return tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x55, data, 2);
+}
+
+static int x_sleep2(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ u8 data = 0;
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ x_sleep2_seq1,
+ ARRAY_SIZE(x_sleep2_seq1));
+ if (ret)
+ return ret;
+
+ usleep_range(1000, 2000);
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xb2, &data, 1);
+ if (ret)
+ return ret;
+
+ if ((data & 0x01) == 0x00)
+ return -EINVAL;
+
+ return cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ x_sleep2_seq2,
+ ARRAY_SIZE(x_sleep2_seq2));
+}
+
+static int x_sleep3(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ return cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ x_sleep3_seq,
+ ARRAY_SIZE(x_sleep3_seq));
+}
+
+static int x_sleep4(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ return cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ x_sleep4_seq,
+ ARRAY_SIZE(x_sleep4_seq));
+}
+
+static int spll_reset(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_tnrdmd_clockmode clockmode)
+{
+ u8 data[4] = { 0 };
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ spll_reset_seq1,
+ ARRAY_SIZE(spll_reset_seq1));
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ spll_reset_seq2,
+ ARRAY_SIZE(spll_reset_seq2));
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ spll_reset_seq3,
+ ARRAY_SIZE(spll_reset_seq3));
+ if (ret)
+ return ret;
+
+ switch (clockmode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data[0] = 0x00;
+ break;
+
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data[0] = 0x01;
+ break;
+
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data[0] = 0x02;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x30, data[0]);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x22, 0x00);
+ if (ret)
+ return ret;
+
+ usleep_range(2000, 3000);
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x0a);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x10, data, 1);
+ if (ret)
+ return ret;
+ if ((data[0] & 0x01) == 0x00)
+ return -EINVAL;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ spll_reset_seq4,
+ ARRAY_SIZE(spll_reset_seq4));
+ if (ret)
+ return ret;
+
+ usleep_range(1000, 2000);
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ spll_reset_seq5,
+ ARRAY_SIZE(spll_reset_seq5));
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x10);
+ if (ret)
+ return ret;
+
+ memset(data, 0x00, sizeof(data));
+
+ return tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x26, data, 4);
+}
+
+static int t_power_x(struct cxd2880_tnrdmd *tnr_dmd, u8 on)
+{
+ u8 data[3] = { 0 };
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ t_power_x_seq1,
+ ARRAY_SIZE(t_power_x_seq1));
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ t_power_x_seq2,
+ ARRAY_SIZE(t_power_x_seq2));
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ t_power_x_seq3,
+ ARRAY_SIZE(t_power_x_seq3));
+ if (ret)
+ return ret;
+
+ if (on) {
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x2b, 0x01);
+ if (ret)
+ return ret;
+
+ usleep_range(1000, 2000);
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x0a);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x12, data, 1);
+ if (ret)
+ return ret;
+ if ((data[0] & 0x01) == 0)
+ return -EINVAL;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ t_power_x_seq4,
+ ARRAY_SIZE(t_power_x_seq4));
+ if (ret)
+ return ret;
+ } else {
+ data[0] = 0x03;
+ data[1] = 0x00;
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x2a, data, 2);
+ if (ret)
+ return ret;
+
+ usleep_range(1000, 2000);
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x0a);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x13, data, 1);
+ if (ret)
+ return ret;
+ if ((data[0] & 0x01) == 0)
+ return -EINVAL;
+ }
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ t_power_x_seq5,
+ ARRAY_SIZE(t_power_x_seq5));
+ if (ret)
+ return ret;
+
+ usleep_range(1000, 2000);
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x0a);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x11, data, 1);
+ if (ret)
+ return ret;
+ if ((data[0] & 0x01) == 0)
+ return -EINVAL;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ t_power_x_seq6,
+ ARRAY_SIZE(t_power_x_seq6));
+ if (ret)
+ return ret;
+
+ usleep_range(1000, 2000);
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ t_power_x_seq7,
+ ARRAY_SIZE(t_power_x_seq7));
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x10);
+ if (ret)
+ return ret;
+
+ memset(data, 0x00, sizeof(data));
+
+ return tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x27, data, 3);
+}
+
+struct cxd2880_tnrdmd_ts_clk_cfg {
+ u8 srl_clk_mode;
+ u8 srl_duty_mode;
+ u8 ts_clk_period;
+};
+
+static int set_ts_clk_mode_and_freq(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dtv_sys sys)
+{
+ int ret;
+ u8 backwards_compatible = 0;
+ struct cxd2880_tnrdmd_ts_clk_cfg ts_clk_cfg;
+ u8 ts_rate_ctrl_off = 0;
+ u8 ts_in_off = 0;
+ u8 ts_clk_manaul_on = 0;
+ u8 data = 0;
+
+ static const struct cxd2880_tnrdmd_ts_clk_cfg srl_ts_clk_stgs[2][2] = {
+ {
+ {3, 1, 8,},
+ {0, 2, 16,}
+ }, {
+ {1, 1, 8,},
+ {2, 2, 16,}
+ }
+ };
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->is_ts_backwards_compatible_mode) {
+ backwards_compatible = 1;
+ ts_rate_ctrl_off = 1;
+ ts_in_off = 1;
+ } else {
+ backwards_compatible = 0;
+ ts_rate_ctrl_off = 0;
+ ts_in_off = 0;
+ }
+
+ if (tnr_dmd->ts_byte_clk_manual_setting) {
+ ts_clk_manaul_on = 1;
+ ts_rate_ctrl_off = 0;
+ }
+
+ ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xd3, ts_rate_ctrl_off, 0x01);
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xde, ts_in_off, 0x01);
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xda, ts_clk_manaul_on, 0x01);
+ if (ret)
+ return ret;
+
+ ts_clk_cfg = srl_ts_clk_stgs[tnr_dmd->srl_ts_clk_mod_cnts]
+ [tnr_dmd->srl_ts_clk_frq];
+
+ if (tnr_dmd->ts_byte_clk_manual_setting)
+ ts_clk_cfg.ts_clk_period = tnr_dmd->ts_byte_clk_manual_setting;
+
+ ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xc4, ts_clk_cfg.srl_clk_mode, 0x03);
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xd1, ts_clk_cfg.srl_duty_mode, 0x03);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xd9,
+ ts_clk_cfg.ts_clk_period);
+ if (ret)
+ return ret;
+
+ data = backwards_compatible ? 0x00 : 0x01;
+
+ if (sys == CXD2880_DTV_SYS_DVBT) {
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x10);
+ if (ret)
+ return ret;
+
+ ret =
+ cxd2880_io_set_reg_bits(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x66, data, 0x01);
+ }
+
+ return ret;
+}
+
+static int pid_ftr_setting(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_tnrdmd_pid_ftr_cfg
+ *pid_ftr_cfg)
+{
+ int i;
+ int ret;
+ u8 data[65];
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+
+ if (!pid_ftr_cfg)
+ return tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x50, 0x02);
+
+ data[0] = pid_ftr_cfg->is_negative ? 0x01 : 0x00;
+
+ for (i = 0; i < 32; i++) {
+ if (pid_ftr_cfg->pid_cfg[i].is_en) {
+ data[1 + (i * 2)] = (pid_ftr_cfg->pid_cfg[i].pid >> 8) | 0x20;
+ data[2 + (i * 2)] = pid_ftr_cfg->pid_cfg[i].pid & 0xff;
+ } else {
+ data[1 + (i * 2)] = 0x00;
+ data[2 + (i * 2)] = 0x00;
+ }
+ }
+
+ return tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x50, data, 65);
+}
+
+static int load_cfg_mem(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ int ret;
+ u8 i;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ for (i = 0; i < tnr_dmd->cfg_mem_last_entry; i++) {
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ tnr_dmd->cfg_mem[i].tgt,
+ 0x00, tnr_dmd->cfg_mem[i].bank);
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
+ tnr_dmd->cfg_mem[i].tgt,
+ tnr_dmd->cfg_mem[i].address,
+ tnr_dmd->cfg_mem[i].value,
+ tnr_dmd->cfg_mem[i].bit_mask);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int set_cfg_mem(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_io_tgt tgt,
+ u8 bank, u8 address, u8 value, u8 bit_mask)
+{
+ u8 i;
+ u8 value_stored = 0;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ for (i = 0; i < tnr_dmd->cfg_mem_last_entry; i++) {
+ if (value_stored == 0 &&
+ tnr_dmd->cfg_mem[i].tgt == tgt &&
+ tnr_dmd->cfg_mem[i].bank == bank &&
+ tnr_dmd->cfg_mem[i].address == address) {
+ tnr_dmd->cfg_mem[i].value &= ~bit_mask;
+ tnr_dmd->cfg_mem[i].value |= (value & bit_mask);
+
+ tnr_dmd->cfg_mem[i].bit_mask |= bit_mask;
+
+ value_stored = 1;
+ }
+ }
+
+ if (value_stored)
+ return 0;
+
+ if (tnr_dmd->cfg_mem_last_entry < CXD2880_TNRDMD_MAX_CFG_MEM_COUNT) {
+ tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].tgt = tgt;
+ tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].bank = bank;
+ tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].address = address;
+ tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].value = (value & bit_mask);
+ tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].bit_mask = bit_mask;
+ tnr_dmd->cfg_mem_last_entry++;
+ } else {
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_create(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_io *io,
+ struct cxd2880_tnrdmd_create_param
+ *create_param)
+{
+ if (!tnr_dmd || !io || !create_param)
+ return -EINVAL;
+
+ memset(tnr_dmd, 0, sizeof(struct cxd2880_tnrdmd));
+
+ tnr_dmd->io = io;
+ tnr_dmd->create_param = *create_param;
+
+ tnr_dmd->diver_mode = CXD2880_TNRDMD_DIVERMODE_SINGLE;
+ tnr_dmd->diver_sub = NULL;
+
+ tnr_dmd->srl_ts_clk_mod_cnts = 1;
+ tnr_dmd->en_fef_intmtnt_base = 1;
+ tnr_dmd->en_fef_intmtnt_lite = 1;
+ tnr_dmd->rf_lvl_cmpstn = NULL;
+ tnr_dmd->lna_thrs_tbl_air = NULL;
+ tnr_dmd->lna_thrs_tbl_cable = NULL;
+ atomic_set(&tnr_dmd->cancel, 0);
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_diver_create(struct cxd2880_tnrdmd
+ *tnr_dmd_main,
+ struct cxd2880_io *io_main,
+ struct cxd2880_tnrdmd *tnr_dmd_sub,
+ struct cxd2880_io *io_sub,
+ struct
+ cxd2880_tnrdmd_diver_create_param
+ *create_param)
+{
+ struct cxd2880_tnrdmd_create_param *main_param, *sub_param;
+
+ if (!tnr_dmd_main || !io_main || !tnr_dmd_sub || !io_sub ||
+ !create_param)
+ return -EINVAL;
+
+ memset(tnr_dmd_main, 0, sizeof(struct cxd2880_tnrdmd));
+ memset(tnr_dmd_sub, 0, sizeof(struct cxd2880_tnrdmd));
+
+ main_param = &tnr_dmd_main->create_param;
+ sub_param = &tnr_dmd_sub->create_param;
+
+ tnr_dmd_main->io = io_main;
+ tnr_dmd_main->diver_mode = CXD2880_TNRDMD_DIVERMODE_MAIN;
+ tnr_dmd_main->diver_sub = tnr_dmd_sub;
+ tnr_dmd_main->create_param.en_internal_ldo =
+ create_param->en_internal_ldo;
+
+ main_param->ts_output_if = create_param->ts_output_if;
+ main_param->xtal_share_type = CXD2880_TNRDMD_XTAL_SHARE_MASTER;
+ main_param->xosc_cap = create_param->xosc_cap_main;
+ main_param->xosc_i = create_param->xosc_i_main;
+ main_param->is_cxd2881gg = create_param->is_cxd2881gg;
+ main_param->stationary_use = create_param->stationary_use;
+
+ tnr_dmd_sub->io = io_sub;
+ tnr_dmd_sub->diver_mode = CXD2880_TNRDMD_DIVERMODE_SUB;
+ tnr_dmd_sub->diver_sub = NULL;
+
+ sub_param->en_internal_ldo = create_param->en_internal_ldo;
+ sub_param->ts_output_if = create_param->ts_output_if;
+ sub_param->xtal_share_type = CXD2880_TNRDMD_XTAL_SHARE_SLAVE;
+ sub_param->xosc_cap = 0;
+ sub_param->xosc_i = create_param->xosc_i_sub;
+ sub_param->is_cxd2881gg = create_param->is_cxd2881gg;
+ sub_param->stationary_use = create_param->stationary_use;
+
+ tnr_dmd_main->srl_ts_clk_mod_cnts = 1;
+ tnr_dmd_main->en_fef_intmtnt_base = 1;
+ tnr_dmd_main->en_fef_intmtnt_lite = 1;
+ tnr_dmd_main->rf_lvl_cmpstn = NULL;
+ tnr_dmd_main->lna_thrs_tbl_air = NULL;
+ tnr_dmd_main->lna_thrs_tbl_cable = NULL;
+
+ tnr_dmd_sub->srl_ts_clk_mod_cnts = 1;
+ tnr_dmd_sub->en_fef_intmtnt_base = 1;
+ tnr_dmd_sub->en_fef_intmtnt_lite = 1;
+ tnr_dmd_sub->rf_lvl_cmpstn = NULL;
+ tnr_dmd_sub->lna_thrs_tbl_air = NULL;
+ tnr_dmd_sub->lna_thrs_tbl_cable = NULL;
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_init1(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ int ret;
+
+ if (!tnr_dmd || tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ tnr_dmd->chip_id = CXD2880_TNRDMD_CHIP_ID_UNKNOWN;
+ tnr_dmd->state = CXD2880_TNRDMD_STATE_UNKNOWN;
+ tnr_dmd->clk_mode = CXD2880_TNRDMD_CLOCKMODE_UNKNOWN;
+ tnr_dmd->frequency_khz = 0;
+ tnr_dmd->sys = CXD2880_DTV_SYS_UNKNOWN;
+ tnr_dmd->bandwidth = CXD2880_DTV_BW_UNKNOWN;
+ tnr_dmd->scan_mode = 0;
+ atomic_set(&tnr_dmd->cancel, 0);
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ tnr_dmd->diver_sub->chip_id = CXD2880_TNRDMD_CHIP_ID_UNKNOWN;
+ tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_UNKNOWN;
+ tnr_dmd->diver_sub->clk_mode = CXD2880_TNRDMD_CLOCKMODE_UNKNOWN;
+ tnr_dmd->diver_sub->frequency_khz = 0;
+ tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_UNKNOWN;
+ tnr_dmd->diver_sub->bandwidth = CXD2880_DTV_BW_UNKNOWN;
+ tnr_dmd->diver_sub->scan_mode = 0;
+ atomic_set(&tnr_dmd->diver_sub->cancel, 0);
+ }
+
+ ret = cxd2880_tnrdmd_chip_id(tnr_dmd, &tnr_dmd->chip_id);
+ if (ret)
+ return ret;
+
+ if (!CXD2880_TNRDMD_CHIP_ID_VALID(tnr_dmd->chip_id))
+ return -ENOTTY;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret =
+ cxd2880_tnrdmd_chip_id(tnr_dmd->diver_sub,
+ &tnr_dmd->diver_sub->chip_id);
+ if (ret)
+ return ret;
+
+ if (!CXD2880_TNRDMD_CHIP_ID_VALID(tnr_dmd->diver_sub->chip_id))
+ return -ENOTTY;
+ }
+
+ ret = p_init1(tnr_dmd);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = p_init1(tnr_dmd->diver_sub);
+ if (ret)
+ return ret;
+ }
+
+ usleep_range(1000, 2000);
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = p_init2(tnr_dmd->diver_sub);
+ if (ret)
+ return ret;
+ }
+
+ ret = p_init2(tnr_dmd);
+ if (ret)
+ return ret;
+
+ usleep_range(5000, 6000);
+
+ ret = p_init3(tnr_dmd);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = p_init3(tnr_dmd->diver_sub);
+ if (ret)
+ return ret;
+ }
+
+ ret = rf_init1(tnr_dmd);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+ ret = rf_init1(tnr_dmd->diver_sub);
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_init2(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ u8 cpu_task_completed;
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ ret = cxd2880_tnrdmd_check_internal_cpu_status(tnr_dmd,
+ &cpu_task_completed);
+ if (ret)
+ return ret;
+
+ if (!cpu_task_completed)
+ return -EINVAL;
+
+ ret = rf_init2(tnr_dmd);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = rf_init2(tnr_dmd->diver_sub);
+ if (ret)
+ return ret;
+ }
+
+ ret = load_cfg_mem(tnr_dmd);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = load_cfg_mem(tnr_dmd->diver_sub);
+ if (ret)
+ return ret;
+ }
+
+ tnr_dmd->state = CXD2880_TNRDMD_STATE_SLEEP;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+ tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_SLEEP;
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_check_internal_cpu_status(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ u8 *task_completed)
+{
+ u16 cpu_status = 0;
+ int ret;
+
+ if (!tnr_dmd || !task_completed)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ ret = cxd2880_tnrdmd_mon_internal_cpu_status(tnr_dmd, &cpu_status);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+ if (cpu_status == 0)
+ *task_completed = 1;
+ else
+ *task_completed = 0;
+
+ return ret;
+ }
+ if (cpu_status != 0) {
+ *task_completed = 0;
+ return ret;
+ }
+
+ ret = cxd2880_tnrdmd_mon_internal_cpu_status_sub(tnr_dmd, &cpu_status);
+ if (ret)
+ return ret;
+
+ if (cpu_status == 0)
+ *task_completed = 1;
+ else
+ *task_completed = 0;
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_common_tune_setting1(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dtv_sys sys,
+ u32 frequency_khz,
+ enum cxd2880_dtv_bandwidth
+ bandwidth, u8 one_seg_opt,
+ u8 one_seg_opt_shft_dir)
+{
+ u8 data;
+ enum cxd2880_tnrdmd_clockmode new_clk_mode =
+ CXD2880_TNRDMD_CLOCKMODE_A;
+ int shift_frequency_khz;
+ u8 cpu_task_completed;
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (frequency_khz < 4000)
+ return -EINVAL;
+
+ ret = cxd2880_tnrdmd_sleep(tnr_dmd);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00,
+ 0x00);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x2b,
+ &data,
+ 1);
+ if (ret)
+ return ret;
+
+ switch (sys) {
+ case CXD2880_DTV_SYS_DVBT:
+ if (data == 0x00) {
+ ret = t_power_x(tnr_dmd, 1);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode ==
+ CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = t_power_x(tnr_dmd->diver_sub, 1);
+ if (ret)
+ return ret;
+ }
+ }
+ break;
+
+ case CXD2880_DTV_SYS_DVBT2:
+ if (data == 0x01) {
+ ret = t_power_x(tnr_dmd, 0);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode ==
+ CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = t_power_x(tnr_dmd->diver_sub, 0);
+ if (ret)
+ return ret;
+ }
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ ret = spll_reset(tnr_dmd, new_clk_mode);
+ if (ret)
+ return ret;
+
+ tnr_dmd->clk_mode = new_clk_mode;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = spll_reset(tnr_dmd->diver_sub, new_clk_mode);
+ if (ret)
+ return ret;
+
+ tnr_dmd->diver_sub->clk_mode = new_clk_mode;
+ }
+
+ ret = load_cfg_mem(tnr_dmd);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = load_cfg_mem(tnr_dmd->diver_sub);
+ if (ret)
+ return ret;
+ }
+
+ if (one_seg_opt) {
+ if (tnr_dmd->diver_mode ==
+ CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ shift_frequency_khz = 350;
+ } else {
+ if (one_seg_opt_shft_dir)
+ shift_frequency_khz = 350;
+ else
+ shift_frequency_khz = -350;
+
+ if (tnr_dmd->create_param.xtal_share_type ==
+ CXD2880_TNRDMD_XTAL_SHARE_SLAVE)
+ shift_frequency_khz *= -1;
+ }
+ } else {
+ if (tnr_dmd->diver_mode ==
+ CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ shift_frequency_khz = 150;
+ } else {
+ switch (tnr_dmd->create_param.xtal_share_type) {
+ case CXD2880_TNRDMD_XTAL_SHARE_NONE:
+ case CXD2880_TNRDMD_XTAL_SHARE_EXTREF:
+ default:
+ shift_frequency_khz = 0;
+ break;
+ case CXD2880_TNRDMD_XTAL_SHARE_MASTER:
+ shift_frequency_khz = 150;
+ break;
+ case CXD2880_TNRDMD_XTAL_SHARE_SLAVE:
+ shift_frequency_khz = -150;
+ break;
+ }
+ }
+ }
+
+ ret =
+ x_tune1(tnr_dmd, sys, frequency_khz, bandwidth,
+ tnr_dmd->is_cable_input, shift_frequency_khz);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret =
+ x_tune1(tnr_dmd->diver_sub, sys, frequency_khz,
+ bandwidth, tnr_dmd->is_cable_input,
+ -shift_frequency_khz);
+ if (ret)
+ return ret;
+ }
+
+ usleep_range(10000, 11000);
+
+ ret =
+ cxd2880_tnrdmd_check_internal_cpu_status(tnr_dmd,
+ &cpu_task_completed);
+ if (ret)
+ return ret;
+
+ if (!cpu_task_completed)
+ return -EINVAL;
+
+ ret =
+ x_tune2(tnr_dmd, bandwidth, tnr_dmd->clk_mode,
+ shift_frequency_khz);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret =
+ x_tune2(tnr_dmd->diver_sub, bandwidth,
+ tnr_dmd->diver_sub->clk_mode,
+ -shift_frequency_khz);
+ if (ret)
+ return ret;
+ }
+
+ if (tnr_dmd->create_param.ts_output_if == CXD2880_TNRDMD_TSOUT_IF_TS) {
+ ret = set_ts_clk_mode_and_freq(tnr_dmd, sys);
+ } else {
+ struct cxd2880_tnrdmd_pid_ftr_cfg *pid_ftr_cfg;
+
+ if (tnr_dmd->pid_ftr_cfg_en)
+ pid_ftr_cfg = &tnr_dmd->pid_ftr_cfg;
+ else
+ pid_ftr_cfg = NULL;
+
+ ret = pid_ftr_setting(tnr_dmd, pid_ftr_cfg);
+ }
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_common_tune_setting2(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dtv_sys sys,
+ u8 en_fef_intmtnt_ctrl)
+{
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret = x_tune3(tnr_dmd, sys, en_fef_intmtnt_ctrl);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = x_tune3(tnr_dmd->diver_sub, sys, en_fef_intmtnt_ctrl);
+ if (ret)
+ return ret;
+ ret = x_tune4(tnr_dmd);
+ if (ret)
+ return ret;
+ }
+
+ return cxd2880_tnrdmd_set_ts_output(tnr_dmd, 1);
+}
+
+int cxd2880_tnrdmd_sleep(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state == CXD2880_TNRDMD_STATE_SLEEP)
+ return 0;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret = cxd2880_tnrdmd_set_ts_output(tnr_dmd, 0);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = x_sleep1(tnr_dmd);
+ if (ret)
+ return ret;
+ }
+
+ ret = x_sleep2(tnr_dmd);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = x_sleep2(tnr_dmd->diver_sub);
+ if (ret)
+ return ret;
+ }
+
+ switch (tnr_dmd->sys) {
+ case CXD2880_DTV_SYS_DVBT:
+ ret = cxd2880_tnrdmd_dvbt_sleep_setting(tnr_dmd);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_DTV_SYS_DVBT2:
+ ret = cxd2880_tnrdmd_dvbt2_sleep_setting(tnr_dmd);
+ if (ret)
+ return ret;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ ret = x_sleep3(tnr_dmd);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = x_sleep3(tnr_dmd->diver_sub);
+ if (ret)
+ return ret;
+ }
+
+ ret = x_sleep4(tnr_dmd);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = x_sleep4(tnr_dmd->diver_sub);
+ if (ret)
+ return ret;
+ }
+
+ tnr_dmd->state = CXD2880_TNRDMD_STATE_SLEEP;
+ tnr_dmd->frequency_khz = 0;
+ tnr_dmd->sys = CXD2880_DTV_SYS_UNKNOWN;
+ tnr_dmd->bandwidth = CXD2880_DTV_BW_UNKNOWN;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_SLEEP;
+ tnr_dmd->diver_sub->frequency_khz = 0;
+ tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_UNKNOWN;
+ tnr_dmd->diver_sub->bandwidth = CXD2880_DTV_BW_UNKNOWN;
+ }
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_set_cfg(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_tnrdmd_cfg_id id,
+ int value)
+{
+ int ret = 0;
+ u8 data[2] = { 0 };
+ u8 need_sub_setting = 0;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ switch (id) {
+ case CXD2880_TNRDMD_CFG_OUTPUT_SEL_MSB:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0xc4,
+ value ? 0x00 : 0x10,
+ 0x10);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSVALID_ACTIVE_HI:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0xc5,
+ value ? 0x00 : 0x02,
+ 0x02);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSSYNC_ACTIVE_HI:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0xc5,
+ value ? 0x00 : 0x04,
+ 0x04);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSERR_ACTIVE_HI:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0xcb,
+ value ? 0x00 : 0x01,
+ 0x01);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_LATCH_ON_POSEDGE:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0xc5,
+ value ? 0x01 : 0x00,
+ 0x01);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSCLK_CONT:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ tnr_dmd->srl_ts_clk_mod_cnts = value ? 0x01 : 0x00;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSCLK_MASK:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ if (value < 0 || value > 0x1f)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0xc6, value,
+ 0x1f);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSVALID_MASK:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ if (value < 0 || value > 0x1f)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0xc8, value,
+ 0x1f);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSERR_MASK:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ if (value < 0 || value > 0x1f)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0xc9, value,
+ 0x1f);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSERR_VALID_DIS:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x91,
+ value ? 0x01 : 0x00,
+ 0x01);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSPIN_CURRENT:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x51, value,
+ 0x3f);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSPIN_PULLUP_MANUAL:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x50,
+ value ? 0x80 : 0x00,
+ 0x80);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSPIN_PULLUP:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x50, value,
+ 0x3f);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSCLK_FREQ:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ if (value < 0 || value > 1)
+ return -EINVAL;
+
+ tnr_dmd->srl_ts_clk_frq =
+ (enum cxd2880_tnrdmd_serial_ts_clk)value;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSBYTECLK_MANUAL:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ if (value < 0 || value > 0xff)
+ return -EINVAL;
+
+ tnr_dmd->ts_byte_clk_manual_setting = value;
+
+ break;
+
+ case CXD2880_TNRDMD_CFG_TS_PACKET_GAP:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ if (value < 0 || value > 7)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0xd6, value,
+ 0x07);
+ if (ret)
+ return ret;
+
+ break;
+
+ case CXD2880_TNRDMD_CFG_TS_BACKWARDS_COMPATIBLE:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ tnr_dmd->is_ts_backwards_compatible_mode = value ? 1 : 0;
+
+ break;
+
+ case CXD2880_TNRDMD_CFG_PWM_VALUE:
+ if (value < 0 || value > 0x1000)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x22,
+ value ? 0x01 : 0x00,
+ 0x01);
+ if (ret)
+ return ret;
+
+ data[0] = (value >> 8) & 0x1f;
+ data[1] = value & 0xff;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x23,
+ data[0], 0x1f);
+ if (ret)
+ return ret;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x24,
+ data[1], 0xff);
+ if (ret)
+ return ret;
+
+ break;
+
+ case CXD2880_TNRDMD_CFG_INTERRUPT:
+ data[0] = (value >> 8) & 0xff;
+ data[1] = value & 0xff;
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x48, data[0],
+ 0xff);
+ if (ret)
+ return ret;
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x49, data[1],
+ 0xff);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_INTERRUPT_LOCK_SEL:
+ data[0] = value & 0x07;
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x4a, data[0],
+ 0x07);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_INTERRUPT_INV_LOCK_SEL:
+ data[0] = (value & 0x07) << 3;
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x4a, data[0],
+ 0x38);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_FIXED_CLOCKMODE:
+ if (value < CXD2880_TNRDMD_CLOCKMODE_UNKNOWN ||
+ value > CXD2880_TNRDMD_CLOCKMODE_C)
+ return -EINVAL;
+ tnr_dmd->fixed_clk_mode = (enum cxd2880_tnrdmd_clockmode)value;
+ break;
+
+ case CXD2880_TNRDMD_CFG_CABLE_INPUT:
+ tnr_dmd->is_cable_input = value ? 1 : 0;
+ break;
+
+ case CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_BASE:
+ tnr_dmd->en_fef_intmtnt_base = value ? 1 : 0;
+ break;
+
+ case CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_LITE:
+ tnr_dmd->en_fef_intmtnt_lite = value ? 1 : 0;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_EMPTY_THRS:
+ data[0] = (value >> 8) & 0x07;
+ data[1] = value & 0xff;
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x99, data[0],
+ 0x07);
+ if (ret)
+ return ret;
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x9a, data[1],
+ 0xff);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_FULL_THRS:
+ data[0] = (value >> 8) & 0x07;
+ data[1] = value & 0xff;
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x9b, data[0],
+ 0x07);
+ if (ret)
+ return ret;
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x9c, data[1],
+ 0xff);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TS_BUF_RRDY_THRS:
+ data[0] = (value >> 8) & 0x07;
+ data[1] = value & 0xff;
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x9d, data[0],
+ 0x07);
+ if (ret)
+ return ret;
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x9e, data[1],
+ 0xff);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_BLINDTUNE_DVBT2_FIRST:
+ tnr_dmd->blind_tune_dvbt2_first = value ? 1 : 0;
+ break;
+
+ case CXD2880_TNRDMD_CFG_DVBT_BERN_PERIOD:
+ if (value < 0 || value > 31)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x10, 0x60,
+ value & 0x1f, 0x1f);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_DVBT_VBER_PERIOD:
+ if (value < 0 || value > 7)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x10, 0x6f,
+ value & 0x07, 0x07);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_DVBT2_BBER_MES:
+ if (value < 0 || value > 15)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x20, 0x72,
+ value & 0x0f, 0x0f);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_DVBT2_LBER_MES:
+ if (value < 0 || value > 15)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x20, 0x6f,
+ value & 0x0f, 0x0f);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_DVBT_PER_MES:
+ if (value < 0 || value > 15)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x10, 0x5c,
+ value & 0x0f, 0x0f);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_DVBT2_PER_MES:
+ if (value < 0 || value > 15)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x24, 0xdc,
+ value & 0x0f, 0x0f);
+ if (ret)
+ return ret;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (need_sub_setting &&
+ tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+ ret = cxd2880_tnrdmd_set_cfg(tnr_dmd->diver_sub, id, value);
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_gpio_set_cfg(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 id,
+ u8 en,
+ enum cxd2880_tnrdmd_gpio_mode mode,
+ u8 open_drain, u8 invert)
+{
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (id > 2)
+ return -EINVAL;
+
+ if (mode > CXD2880_TNRDMD_GPIO_MODE_EEW)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS,
+ 0x00, 0x40 + id, mode,
+ 0x0f);
+ if (ret)
+ return ret;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS,
+ 0x00, 0x43,
+ open_drain ? (1 << id) : 0,
+ 1 << id);
+ if (ret)
+ return ret;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS,
+ 0x00, 0x44,
+ invert ? (1 << id) : 0,
+ 1 << id);
+ if (ret)
+ return ret;
+
+ return cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x45,
+ en ? 0 : (1 << id),
+ 1 << id);
+}
+
+int cxd2880_tnrdmd_gpio_set_cfg_sub(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 id,
+ u8 en,
+ enum cxd2880_tnrdmd_gpio_mode
+ mode, u8 open_drain, u8 invert)
+{
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return -EINVAL;
+
+ return cxd2880_tnrdmd_gpio_set_cfg(tnr_dmd->diver_sub, id, en, mode,
+ open_drain, invert);
+}
+
+int cxd2880_tnrdmd_gpio_read(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 id, u8 *value)
+{
+ u8 data = 0;
+ int ret;
+
+ if (!tnr_dmd || !value)
+ return -EINVAL;
+
+ if (id > 2)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x0a);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x20, &data, 1);
+ if (ret)
+ return ret;
+
+ *value = (data >> id) & 0x01;
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_gpio_read_sub(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 id, u8 *value)
+{
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return -EINVAL;
+
+ return cxd2880_tnrdmd_gpio_read(tnr_dmd->diver_sub, id, value);
+}
+
+int cxd2880_tnrdmd_gpio_write(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 id, u8 value)
+{
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (id > 2)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ return cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x46,
+ value ? (1 << id) : 0,
+ 1 << id);
+}
+
+int cxd2880_tnrdmd_gpio_write_sub(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 id, u8 value)
+{
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return -EINVAL;
+
+ return cxd2880_tnrdmd_gpio_write(tnr_dmd->diver_sub, id, value);
+}
+
+int cxd2880_tnrdmd_interrupt_read(struct cxd2880_tnrdmd *tnr_dmd,
+ u16 *value)
+{
+ int ret;
+ u8 data[2] = { 0 };
+
+ if (!tnr_dmd || !value)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x0a);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x15, data, 2);
+ if (ret)
+ return ret;
+
+ *value = (data[0] << 8) | data[1];
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_interrupt_clear(struct cxd2880_tnrdmd *tnr_dmd,
+ u16 value)
+{
+ int ret;
+ u8 data[2] = { 0 };
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+
+ data[0] = (value >> 8) & 0xff;
+ data[1] = value & 0xff;
+
+ return tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x3c, data, 2);
+}
+
+int cxd2880_tnrdmd_ts_buf_clear(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 clear_overflow_flag,
+ u8 clear_underflow_flag,
+ u8 clear_buf)
+{
+ int ret;
+ u8 data[2] = { 0 };
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+
+ data[0] = clear_overflow_flag ? 0x02 : 0x00;
+ data[0] |= clear_underflow_flag ? 0x01 : 0x00;
+ data[1] = clear_buf ? 0x01 : 0x00;
+
+ return tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x9f, data, 2);
+}
+
+int cxd2880_tnrdmd_chip_id(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_tnrdmd_chip_id *chip_id)
+{
+ int ret;
+ u8 data = 0;
+
+ if (!tnr_dmd || !chip_id)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0xfd, &data, 1);
+ if (ret)
+ return ret;
+
+ *chip_id = (enum cxd2880_tnrdmd_chip_id)data;
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_set_and_save_reg_bits(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_io_tgt tgt,
+ u8 bank, u8 address,
+ u8 value, u8 bit_mask)
+{
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io, tgt, 0x00, bank);
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
+ tgt, address, value, bit_mask);
+ if (ret)
+ return ret;
+
+ return set_cfg_mem(tnr_dmd, tgt, bank, address, value, bit_mask);
+}
+
+int cxd2880_tnrdmd_set_scan_mode(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dtv_sys sys,
+ u8 scan_mode_end)
+{
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ tnr_dmd->scan_mode = scan_mode_end;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return cxd2880_tnrdmd_set_scan_mode(tnr_dmd->diver_sub, sys,
+ scan_mode_end);
+ else
+ return 0;
+}
+
+int cxd2880_tnrdmd_set_pid_ftr(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_tnrdmd_pid_ftr_cfg
+ *pid_ftr_cfg)
+{
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->create_param.ts_output_if == CXD2880_TNRDMD_TSOUT_IF_TS)
+ return -ENOTTY;
+
+ if (pid_ftr_cfg) {
+ tnr_dmd->pid_ftr_cfg = *pid_ftr_cfg;
+ tnr_dmd->pid_ftr_cfg_en = 1;
+ } else {
+ tnr_dmd->pid_ftr_cfg_en = 0;
+ }
+
+ if (tnr_dmd->state == CXD2880_TNRDMD_STATE_ACTIVE)
+ return pid_ftr_setting(tnr_dmd, pid_ftr_cfg);
+ else
+ return 0;
+}
+
+int cxd2880_tnrdmd_set_rf_lvl_cmpstn(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ int (*rf_lvl_cmpstn)
+ (struct cxd2880_tnrdmd *,
+ int *))
+{
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ tnr_dmd->rf_lvl_cmpstn = rf_lvl_cmpstn;
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_set_rf_lvl_cmpstn_sub(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ int (*rf_lvl_cmpstn)
+ (struct cxd2880_tnrdmd *,
+ int *))
+{
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return -EINVAL;
+
+ return cxd2880_tnrdmd_set_rf_lvl_cmpstn(tnr_dmd->diver_sub,
+ rf_lvl_cmpstn);
+}
+
+int cxd2880_tnrdmd_set_lna_thrs(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_tnrdmd_lna_thrs_tbl_air
+ *tbl_air,
+ struct cxd2880_tnrdmd_lna_thrs_tbl_cable
+ *tbl_cable)
+{
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ tnr_dmd->lna_thrs_tbl_air = tbl_air;
+ tnr_dmd->lna_thrs_tbl_cable = tbl_cable;
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_set_lna_thrs_sub(struct cxd2880_tnrdmd *tnr_dmd,
+ struct
+ cxd2880_tnrdmd_lna_thrs_tbl_air
+ *tbl_air,
+ struct cxd2880_tnrdmd_lna_thrs_tbl_cable
+ *tbl_cable)
+{
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return -EINVAL;
+
+ return cxd2880_tnrdmd_set_lna_thrs(tnr_dmd->diver_sub,
+ tbl_air, tbl_cable);
+}
+
+int cxd2880_tnrdmd_set_ts_pin_high_low(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 en, u8 value)
+{
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return -EINVAL;
+
+ if (tnr_dmd->create_param.ts_output_if != CXD2880_TNRDMD_TSOUT_IF_TS)
+ return -ENOTTY;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+
+ if (en) {
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x50, ((value & 0x1f) | 0x80));
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x52, (value & 0x1f));
+ } else {
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ set_ts_pin_seq,
+ ARRAY_SIZE(set_ts_pin_seq));
+ if (ret)
+ return ret;
+
+ ret = load_cfg_mem(tnr_dmd);
+ }
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_set_ts_output(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 en)
+{
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ switch (tnr_dmd->create_param.ts_output_if) {
+ case CXD2880_TNRDMD_TSOUT_IF_TS:
+ if (en) {
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ set_ts_output_seq1,
+ ARRAY_SIZE(set_ts_output_seq1));
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ set_ts_output_seq2,
+ ARRAY_SIZE(set_ts_output_seq2));
+ if (ret)
+ return ret;
+ } else {
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ set_ts_output_seq3,
+ ARRAY_SIZE(set_ts_output_seq3));
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ set_ts_output_seq4,
+ ARRAY_SIZE(set_ts_output_seq4));
+ if (ret)
+ return ret;
+ }
+ break;
+
+ case CXD2880_TNRDMD_TSOUT_IF_SPI:
+ break;
+
+ case CXD2880_TNRDMD_TSOUT_IF_SDIO:
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int slvt_freeze_reg(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ u8 data;
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ switch (tnr_dmd->create_param.ts_output_if) {
+ case CXD2880_TNRDMD_TSOUT_IF_SPI:
+ case CXD2880_TNRDMD_TSOUT_IF_SDIO:
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, &data, 1);
+ if (ret)
+ return ret;
+
+ break;
+ case CXD2880_TNRDMD_TSOUT_IF_TS:
+ default:
+ break;
+ }
+
+ return tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x01, 0x01);
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
new file mode 100644
index 000000000000..9d809a251fc7
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
@@ -0,0 +1,365 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_tnrdmd.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * common control interface
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_TNRDMD_H
+#define CXD2880_TNRDMD_H
+
+#include <linux/atomic.h>
+
+#include "cxd2880_common.h"
+#include "cxd2880_io.h"
+#include "cxd2880_dtv.h"
+#include "cxd2880_dvbt.h"
+#include "cxd2880_dvbt2.h"
+
+#define CXD2880_TNRDMD_MAX_CFG_MEM_COUNT 100
+
+#define slvt_unfreeze_reg(tnr_dmd) ((void)((tnr_dmd)->io->write_reg\
+((tnr_dmd)->io, CXD2880_IO_TGT_DMD, 0x01, 0x00)))
+
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_UNDERFLOW 0x0001
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_OVERFLOW 0x0002
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_ALMOST_EMPTY 0x0004
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_ALMOST_FULL 0x0008
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_RRDY 0x0010
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_ILLEGAL_COMMAND 0x0020
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_ILLEGAL_ACCESS 0x0040
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_CPU_ERROR 0x0100
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_LOCK 0x0200
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_INV_LOCK 0x0400
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_NOOFDM 0x0800
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_EWS 0x1000
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_EEW 0x2000
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_FEC_FAIL 0x4000
+
+#define CXD2880_TNRDMD_INTERRUPT_LOCK_SEL_L1POST_OK 0x01
+#define CXD2880_TNRDMD_INTERRUPT_LOCK_SEL_DMD_LOCK 0x02
+#define CXD2880_TNRDMD_INTERRUPT_LOCK_SEL_TS_LOCK 0x04
+
+enum cxd2880_tnrdmd_chip_id {
+ CXD2880_TNRDMD_CHIP_ID_UNKNOWN = 0x00,
+ CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X = 0x62,
+ CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11 = 0x6a
+};
+
+#define CXD2880_TNRDMD_CHIP_ID_VALID(chip_id) \
+ (((chip_id) == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X) || \
+ ((chip_id) == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11))
+
+enum cxd2880_tnrdmd_state {
+ CXD2880_TNRDMD_STATE_UNKNOWN,
+ CXD2880_TNRDMD_STATE_SLEEP,
+ CXD2880_TNRDMD_STATE_ACTIVE,
+ CXD2880_TNRDMD_STATE_INVALID
+};
+
+enum cxd2880_tnrdmd_divermode {
+ CXD2880_TNRDMD_DIVERMODE_SINGLE,
+ CXD2880_TNRDMD_DIVERMODE_MAIN,
+ CXD2880_TNRDMD_DIVERMODE_SUB
+};
+
+enum cxd2880_tnrdmd_clockmode {
+ CXD2880_TNRDMD_CLOCKMODE_UNKNOWN,
+ CXD2880_TNRDMD_CLOCKMODE_A,
+ CXD2880_TNRDMD_CLOCKMODE_B,
+ CXD2880_TNRDMD_CLOCKMODE_C
+};
+
+enum cxd2880_tnrdmd_tsout_if {
+ CXD2880_TNRDMD_TSOUT_IF_TS,
+ CXD2880_TNRDMD_TSOUT_IF_SPI,
+ CXD2880_TNRDMD_TSOUT_IF_SDIO
+};
+
+enum cxd2880_tnrdmd_xtal_share {
+ CXD2880_TNRDMD_XTAL_SHARE_NONE,
+ CXD2880_TNRDMD_XTAL_SHARE_EXTREF,
+ CXD2880_TNRDMD_XTAL_SHARE_MASTER,
+ CXD2880_TNRDMD_XTAL_SHARE_SLAVE
+};
+
+enum cxd2880_tnrdmd_spectrum_sense {
+ CXD2880_TNRDMD_SPECTRUM_NORMAL,
+ CXD2880_TNRDMD_SPECTRUM_INV
+};
+
+enum cxd2880_tnrdmd_cfg_id {
+ CXD2880_TNRDMD_CFG_OUTPUT_SEL_MSB,
+ CXD2880_TNRDMD_CFG_TSVALID_ACTIVE_HI,
+ CXD2880_TNRDMD_CFG_TSSYNC_ACTIVE_HI,
+ CXD2880_TNRDMD_CFG_TSERR_ACTIVE_HI,
+ CXD2880_TNRDMD_CFG_LATCH_ON_POSEDGE,
+ CXD2880_TNRDMD_CFG_TSCLK_CONT,
+ CXD2880_TNRDMD_CFG_TSCLK_MASK,
+ CXD2880_TNRDMD_CFG_TSVALID_MASK,
+ CXD2880_TNRDMD_CFG_TSERR_MASK,
+ CXD2880_TNRDMD_CFG_TSERR_VALID_DIS,
+ CXD2880_TNRDMD_CFG_TSPIN_CURRENT,
+ CXD2880_TNRDMD_CFG_TSPIN_PULLUP_MANUAL,
+ CXD2880_TNRDMD_CFG_TSPIN_PULLUP,
+ CXD2880_TNRDMD_CFG_TSCLK_FREQ,
+ CXD2880_TNRDMD_CFG_TSBYTECLK_MANUAL,
+ CXD2880_TNRDMD_CFG_TS_PACKET_GAP,
+ CXD2880_TNRDMD_CFG_TS_BACKWARDS_COMPATIBLE,
+ CXD2880_TNRDMD_CFG_PWM_VALUE,
+ CXD2880_TNRDMD_CFG_INTERRUPT,
+ CXD2880_TNRDMD_CFG_INTERRUPT_LOCK_SEL,
+ CXD2880_TNRDMD_CFG_INTERRUPT_INV_LOCK_SEL,
+ CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_EMPTY_THRS,
+ CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_FULL_THRS,
+ CXD2880_TNRDMD_CFG_TS_BUF_RRDY_THRS,
+ CXD2880_TNRDMD_CFG_FIXED_CLOCKMODE,
+ CXD2880_TNRDMD_CFG_CABLE_INPUT,
+ CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_BASE,
+ CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_LITE,
+ CXD2880_TNRDMD_CFG_BLINDTUNE_DVBT2_FIRST,
+ CXD2880_TNRDMD_CFG_DVBT_BERN_PERIOD,
+ CXD2880_TNRDMD_CFG_DVBT_VBER_PERIOD,
+ CXD2880_TNRDMD_CFG_DVBT_PER_MES,
+ CXD2880_TNRDMD_CFG_DVBT2_BBER_MES,
+ CXD2880_TNRDMD_CFG_DVBT2_LBER_MES,
+ CXD2880_TNRDMD_CFG_DVBT2_PER_MES,
+};
+
+enum cxd2880_tnrdmd_lock_result {
+ CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT,
+ CXD2880_TNRDMD_LOCK_RESULT_LOCKED,
+ CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED
+};
+
+enum cxd2880_tnrdmd_gpio_mode {
+ CXD2880_TNRDMD_GPIO_MODE_OUTPUT = 0x00,
+ CXD2880_TNRDMD_GPIO_MODE_INPUT = 0x01,
+ CXD2880_TNRDMD_GPIO_MODE_INT = 0x02,
+ CXD2880_TNRDMD_GPIO_MODE_FEC_FAIL = 0x03,
+ CXD2880_TNRDMD_GPIO_MODE_PWM = 0x04,
+ CXD2880_TNRDMD_GPIO_MODE_EWS = 0x05,
+ CXD2880_TNRDMD_GPIO_MODE_EEW = 0x06
+};
+
+enum cxd2880_tnrdmd_serial_ts_clk {
+ CXD2880_TNRDMD_SERIAL_TS_CLK_FULL,
+ CXD2880_TNRDMD_SERIAL_TS_CLK_HALF
+};
+
+struct cxd2880_tnrdmd_cfg_mem {
+ enum cxd2880_io_tgt tgt;
+ u8 bank;
+ u8 address;
+ u8 value;
+ u8 bit_mask;
+};
+
+struct cxd2880_tnrdmd_pid_cfg {
+ u8 is_en;
+ u16 pid;
+};
+
+struct cxd2880_tnrdmd_pid_ftr_cfg {
+ u8 is_negative;
+ struct cxd2880_tnrdmd_pid_cfg pid_cfg[32];
+};
+
+struct cxd2880_tnrdmd_lna_thrs {
+ u8 off_on;
+ u8 on_off;
+};
+
+struct cxd2880_tnrdmd_lna_thrs_tbl_air {
+ struct cxd2880_tnrdmd_lna_thrs thrs[24];
+};
+
+struct cxd2880_tnrdmd_lna_thrs_tbl_cable {
+ struct cxd2880_tnrdmd_lna_thrs thrs[32];
+};
+
+struct cxd2880_tnrdmd_create_param {
+ enum cxd2880_tnrdmd_tsout_if ts_output_if;
+ u8 en_internal_ldo;
+ enum cxd2880_tnrdmd_xtal_share xtal_share_type;
+ u8 xosc_cap;
+ u8 xosc_i;
+ u8 is_cxd2881gg;
+ u8 stationary_use;
+};
+
+struct cxd2880_tnrdmd_diver_create_param {
+ enum cxd2880_tnrdmd_tsout_if ts_output_if;
+ u8 en_internal_ldo;
+ u8 xosc_cap_main;
+ u8 xosc_i_main;
+ u8 xosc_i_sub;
+ u8 is_cxd2881gg;
+ u8 stationary_use;
+};
+
+struct cxd2880_tnrdmd {
+ struct cxd2880_tnrdmd *diver_sub;
+ struct cxd2880_io *io;
+ struct cxd2880_tnrdmd_create_param create_param;
+ enum cxd2880_tnrdmd_divermode diver_mode;
+ enum cxd2880_tnrdmd_clockmode fixed_clk_mode;
+ u8 is_cable_input;
+ u8 en_fef_intmtnt_base;
+ u8 en_fef_intmtnt_lite;
+ u8 blind_tune_dvbt2_first;
+ int (*rf_lvl_cmpstn)(struct cxd2880_tnrdmd *tnr_dmd,
+ int *rf_lvl_db);
+ struct cxd2880_tnrdmd_lna_thrs_tbl_air *lna_thrs_tbl_air;
+ struct cxd2880_tnrdmd_lna_thrs_tbl_cable *lna_thrs_tbl_cable;
+ u8 srl_ts_clk_mod_cnts;
+ enum cxd2880_tnrdmd_serial_ts_clk srl_ts_clk_frq;
+ u8 ts_byte_clk_manual_setting;
+ u8 is_ts_backwards_compatible_mode;
+ struct cxd2880_tnrdmd_cfg_mem cfg_mem[CXD2880_TNRDMD_MAX_CFG_MEM_COUNT];
+ u8 cfg_mem_last_entry;
+ struct cxd2880_tnrdmd_pid_ftr_cfg pid_ftr_cfg;
+ u8 pid_ftr_cfg_en;
+ void *user;
+ enum cxd2880_tnrdmd_chip_id chip_id;
+ enum cxd2880_tnrdmd_state state;
+ enum cxd2880_tnrdmd_clockmode clk_mode;
+ u32 frequency_khz;
+ enum cxd2880_dtv_sys sys;
+ enum cxd2880_dtv_bandwidth bandwidth;
+ u8 scan_mode;
+ atomic_t cancel;
+};
+
+int cxd2880_tnrdmd_create(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_io *io,
+ struct cxd2880_tnrdmd_create_param
+ *create_param);
+
+int cxd2880_tnrdmd_diver_create(struct cxd2880_tnrdmd
+ *tnr_dmd_main,
+ struct cxd2880_io *io_main,
+ struct cxd2880_tnrdmd *tnr_dmd_sub,
+ struct cxd2880_io *io_sub,
+ struct
+ cxd2880_tnrdmd_diver_create_param
+ *create_param);
+
+int cxd2880_tnrdmd_init1(struct cxd2880_tnrdmd *tnr_dmd);
+
+int cxd2880_tnrdmd_init2(struct cxd2880_tnrdmd *tnr_dmd);
+
+int cxd2880_tnrdmd_check_internal_cpu_status(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ u8 *task_completed);
+
+int cxd2880_tnrdmd_common_tune_setting1(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dtv_sys sys,
+ u32 frequency_khz,
+ enum cxd2880_dtv_bandwidth
+ bandwidth, u8 one_seg_opt,
+ u8 one_seg_opt_shft_dir);
+
+int cxd2880_tnrdmd_common_tune_setting2(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dtv_sys sys,
+ u8 en_fef_intmtnt_ctrl);
+
+int cxd2880_tnrdmd_sleep(struct cxd2880_tnrdmd *tnr_dmd);
+
+int cxd2880_tnrdmd_set_cfg(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_tnrdmd_cfg_id id,
+ int value);
+
+int cxd2880_tnrdmd_gpio_set_cfg(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 id,
+ u8 en,
+ enum cxd2880_tnrdmd_gpio_mode mode,
+ u8 open_drain, u8 invert);
+
+int cxd2880_tnrdmd_gpio_set_cfg_sub(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 id,
+ u8 en,
+ enum cxd2880_tnrdmd_gpio_mode
+ mode, u8 open_drain,
+ u8 invert);
+
+int cxd2880_tnrdmd_gpio_read(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 id, u8 *value);
+
+int cxd2880_tnrdmd_gpio_read_sub(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 id, u8 *value);
+
+int cxd2880_tnrdmd_gpio_write(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 id, u8 value);
+
+int cxd2880_tnrdmd_gpio_write_sub(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 id, u8 value);
+
+int cxd2880_tnrdmd_interrupt_read(struct cxd2880_tnrdmd *tnr_dmd,
+ u16 *value);
+
+int cxd2880_tnrdmd_interrupt_clear(struct cxd2880_tnrdmd *tnr_dmd,
+ u16 value);
+
+int cxd2880_tnrdmd_ts_buf_clear(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 clear_overflow_flag,
+ u8 clear_underflow_flag,
+ u8 clear_buf);
+
+int cxd2880_tnrdmd_chip_id(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_tnrdmd_chip_id *chip_id);
+
+int cxd2880_tnrdmd_set_and_save_reg_bits(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_io_tgt tgt,
+ u8 bank, u8 address,
+ u8 value, u8 bit_mask);
+
+int cxd2880_tnrdmd_set_scan_mode(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dtv_sys sys,
+ u8 scan_mode_end);
+
+int cxd2880_tnrdmd_set_pid_ftr(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_tnrdmd_pid_ftr_cfg
+ *pid_ftr_cfg);
+
+int cxd2880_tnrdmd_set_rf_lvl_cmpstn(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ int (*rf_lvl_cmpstn)
+ (struct cxd2880_tnrdmd *,
+ int *));
+
+int cxd2880_tnrdmd_set_rf_lvl_cmpstn_sub(struct cxd2880_tnrdmd *tnr_dmd,
+ int (*rf_lvl_cmpstn)
+ (struct cxd2880_tnrdmd *,
+ int *));
+
+int cxd2880_tnrdmd_set_lna_thrs(struct cxd2880_tnrdmd *tnr_dmd,
+ struct
+ cxd2880_tnrdmd_lna_thrs_tbl_air
+ *tbl_air,
+ struct
+ cxd2880_tnrdmd_lna_thrs_tbl_cable
+ *tbl_cable);
+
+int cxd2880_tnrdmd_set_lna_thrs_sub(struct cxd2880_tnrdmd *tnr_dmd,
+ struct
+ cxd2880_tnrdmd_lna_thrs_tbl_air
+ *tbl_air,
+ struct
+ cxd2880_tnrdmd_lna_thrs_tbl_cable
+ *tbl_cable);
+
+int cxd2880_tnrdmd_set_ts_pin_high_low(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 en, u8 value);
+
+int cxd2880_tnrdmd_set_ts_output(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 en);
+
+int slvt_freeze_reg(struct cxd2880_tnrdmd *tnr_dmd);
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
new file mode 100644
index 000000000000..fab55038b37b
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_tnrdmd_driver_version.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * version information
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#define CXD2880_TNRDMD_DRIVER_VERSION "1.4.1 - 1.0.4"
+
+#define CXD2880_TNRDMD_DRIVER_RELEASE_DATE "2018-01-17"
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c
new file mode 100644
index 000000000000..fe3c6f8b1b3e
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c
@@ -0,0 +1,919 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_tnrdmd_dvbt.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * control functions for DVB-T
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#include <media/dvb_frontend.h>
+
+#include "cxd2880_tnrdmd_dvbt.h"
+#include "cxd2880_tnrdmd_dvbt_mon.h"
+
+static const struct cxd2880_reg_value tune_dmd_setting_seq1[] = {
+ {0x00, 0x00}, {0x31, 0x01},
+};
+
+static const struct cxd2880_reg_value tune_dmd_setting_seq2[] = {
+ {0x00, 0x04}, {0x5c, 0xfb}, {0x00, 0x10}, {0xa4, 0x03},
+ {0x00, 0x14}, {0xb0, 0x00}, {0x00, 0x25},
+};
+
+static const struct cxd2880_reg_value tune_dmd_setting_seq3[] = {
+ {0x00, 0x12}, {0x44, 0x00},
+};
+
+static const struct cxd2880_reg_value tune_dmd_setting_seq4[] = {
+ {0x00, 0x11}, {0x87, 0xd2},
+};
+
+static const struct cxd2880_reg_value tune_dmd_setting_seq5[] = {
+ {0x00, 0x00}, {0xfd, 0x01},
+};
+
+static const struct cxd2880_reg_value sleep_dmd_setting_seq1[] = {
+ {0x00, 0x04}, {0x5c, 0xd8}, {0x00, 0x10}, {0xa4, 0x00},
+};
+
+static const struct cxd2880_reg_value sleep_dmd_setting_seq2[] = {
+ {0x00, 0x11}, {0x87, 0x04},
+};
+
+static int x_tune_dvbt_demod_setting(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dtv_bandwidth
+ bandwidth,
+ enum cxd2880_tnrdmd_clockmode
+ clk_mode)
+{
+ static const u8 clk_mode_ckffrq_a[2] = { 0x52, 0x49 };
+ static const u8 clk_mode_ckffrq_b[2] = { 0x5d, 0x55 };
+ static const u8 clk_mode_ckffrq_c[2] = { 0x60, 0x00 };
+ static const u8 ratectl_margin[2] = { 0x01, 0xf0 };
+ static const u8 maxclkcnt_a[3] = { 0x73, 0xca, 0x49 };
+ static const u8 maxclkcnt_b[3] = { 0xc8, 0x13, 0xaa };
+ static const u8 maxclkcnt_c[3] = { 0xdc, 0x6c, 0x00 };
+
+ static const u8 bw8_nomi_ac[5] = { 0x15, 0x00, 0x00, 0x00, 0x00};
+ static const u8 bw8_nomi_b[5] = { 0x14, 0x6a, 0xaa, 0xaa, 0xaa};
+ static const u8 bw8_gtdofst_a[2] = { 0x01, 0x28 };
+ static const u8 bw8_gtdofst_b[2] = { 0x11, 0x44 };
+ static const u8 bw8_gtdofst_c[2] = { 0x15, 0x28 };
+ static const u8 bw8_mrc_a[5] = { 0x30, 0x00, 0x00, 0x90, 0x00 };
+ static const u8 bw8_mrc_b[5] = { 0x36, 0x71, 0x00, 0xa3, 0x55 };
+ static const u8 bw8_mrc_c[5] = { 0x38, 0x00, 0x00, 0xa8, 0x00 };
+ static const u8 bw8_notch[4] = { 0xb3, 0x00, 0x01, 0x02 };
+
+ static const u8 bw7_nomi_ac[5] = { 0x18, 0x00, 0x00, 0x00, 0x00};
+ static const u8 bw7_nomi_b[5] = { 0x17, 0x55, 0x55, 0x55, 0x55};
+ static const u8 bw7_gtdofst_a[2] = { 0x12, 0x4c };
+ static const u8 bw7_gtdofst_b[2] = { 0x1f, 0x15 };
+ static const u8 bw7_gtdofst_c[2] = { 0x1f, 0xf8 };
+ static const u8 bw7_mrc_a[5] = { 0x36, 0xdb, 0x00, 0xa4, 0x92 };
+ static const u8 bw7_mrc_b[5] = { 0x3e, 0x38, 0x00, 0xba, 0xaa };
+ static const u8 bw7_mrc_c[5] = { 0x40, 0x00, 0x00, 0xc0, 0x00 };
+ static const u8 bw7_notch[4] = { 0xb8, 0x00, 0x00, 0x03 };
+
+ static const u8 bw6_nomi_ac[5] = { 0x1c, 0x00, 0x00, 0x00, 0x00};
+ static const u8 bw6_nomi_b[5] = { 0x1b, 0x38, 0xe3, 0x8e, 0x38};
+ static const u8 bw6_gtdofst_a[2] = { 0x1f, 0xf8 };
+ static const u8 bw6_gtdofst_b[2] = { 0x24, 0x43 };
+ static const u8 bw6_gtdofst_c[2] = { 0x25, 0x4c };
+ static const u8 bw6_mrc_a[5] = { 0x40, 0x00, 0x00, 0xc0, 0x00 };
+ static const u8 bw6_mrc_b[5] = { 0x48, 0x97, 0x00, 0xd9, 0xc7 };
+ static const u8 bw6_mrc_c[5] = { 0x4a, 0xaa, 0x00, 0xdf, 0xff };
+ static const u8 bw6_notch[4] = { 0xbe, 0xab, 0x00, 0x03 };
+
+ static const u8 bw5_nomi_ac[5] = { 0x21, 0x99, 0x99, 0x99, 0x99};
+ static const u8 bw5_nomi_b[5] = { 0x20, 0xaa, 0xaa, 0xaa, 0xaa};
+ static const u8 bw5_gtdofst_a[2] = { 0x26, 0x5d };
+ static const u8 bw5_gtdofst_b[2] = { 0x2b, 0x84 };
+ static const u8 bw5_gtdofst_c[2] = { 0x2c, 0xc2 };
+ static const u8 bw5_mrc_a[5] = { 0x4c, 0xcc, 0x00, 0xe6, 0x66 };
+ static const u8 bw5_mrc_b[5] = { 0x57, 0x1c, 0x01, 0x05, 0x55 };
+ static const u8 bw5_mrc_c[5] = { 0x59, 0x99, 0x01, 0x0c, 0xcc };
+ static const u8 bw5_notch[4] = { 0xc8, 0x01, 0x00, 0x03 };
+ const u8 *data = NULL;
+ u8 sst_data;
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ tune_dmd_setting_seq1,
+ ARRAY_SIZE(tune_dmd_setting_seq1));
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x04);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = clk_mode_ckffrq_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = clk_mode_ckffrq_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = clk_mode_ckffrq_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x65, data, 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x5d, 0x07);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) {
+ u8 data[2] = { 0x01, 0x01 };
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xce, data, 2);
+ if (ret)
+ return ret;
+ }
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ tune_dmd_setting_seq2,
+ ARRAY_SIZE(tune_dmd_setting_seq2));
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xf0, ratectl_margin, 2);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN ||
+ tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) {
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ tune_dmd_setting_seq3,
+ ARRAY_SIZE(tune_dmd_setting_seq3));
+ if (ret)
+ return ret;
+ }
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) {
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ tune_dmd_setting_seq4,
+ ARRAY_SIZE(tune_dmd_setting_seq4));
+ if (ret)
+ return ret;
+ }
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) {
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x04);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = maxclkcnt_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = maxclkcnt_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = maxclkcnt_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x68, data, 3);
+ if (ret)
+ return ret;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x04);
+ if (ret)
+ return ret;
+
+ switch (bandwidth) {
+ case CXD2880_DTV_BW_8_MHZ:
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw8_nomi_ac;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw8_nomi_b;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x60, data, 5);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4a, 0x00);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw8_gtdofst_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw8_gtdofst_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw8_gtdofst_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x7d, data, 2);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ sst_data = 0x35;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ sst_data = 0x34;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x71, sst_data);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw8_mrc_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw8_mrc_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw8_mrc_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4b, &data[0], 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x51, &data[2], 3);
+ if (ret)
+ return ret;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x72, &bw8_notch[0], 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x6b, &bw8_notch[2], 2);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_DTV_BW_7_MHZ:
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw7_nomi_ac;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw7_nomi_b;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x60, data, 5);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4a, 0x02);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw7_gtdofst_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw7_gtdofst_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw7_gtdofst_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x7d, data, 2);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ sst_data = 0x2f;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ sst_data = 0x2e;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x71, sst_data);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw7_mrc_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw7_mrc_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw7_mrc_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4b, &data[0], 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x51, &data[2], 3);
+ if (ret)
+ return ret;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x72, &bw7_notch[0], 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x6b, &bw7_notch[2], 2);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_DTV_BW_6_MHZ:
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw6_nomi_ac;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw6_nomi_b;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x60, data, 5);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4a, 0x04);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw6_gtdofst_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw6_gtdofst_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw6_gtdofst_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x7d, data, 2);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ sst_data = 0x29;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ sst_data = 0x2a;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x71, sst_data);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw6_mrc_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw6_mrc_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw6_mrc_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4b, &data[0], 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x51, &data[2], 3);
+ if (ret)
+ return ret;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x72, &bw6_notch[0], 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x6b, &bw6_notch[2], 2);
+ if (ret)
+ return ret;
+ break;
+
+ case CXD2880_DTV_BW_5_MHZ:
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw5_nomi_ac;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw5_nomi_b;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x60, data, 5);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4a, 0x06);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw5_gtdofst_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw5_gtdofst_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw5_gtdofst_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x7d, data, 2);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ sst_data = 0x24;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ sst_data = 0x23;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x71, sst_data);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw5_mrc_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw5_mrc_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw5_mrc_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4b, &data[0], 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x51, &data[2], 3);
+ if (ret)
+ return ret;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x72, &bw5_notch[0], 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x6b, &bw5_notch[2], 2);
+ if (ret)
+ return ret;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ tune_dmd_setting_seq5,
+ ARRAY_SIZE(tune_dmd_setting_seq5));
+}
+
+static int x_sleep_dvbt_demod_setting(struct cxd2880_tnrdmd
+ *tnr_dmd)
+{
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ sleep_dmd_setting_seq1,
+ ARRAY_SIZE(sleep_dmd_setting_seq1));
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ sleep_dmd_setting_seq2,
+ ARRAY_SIZE(sleep_dmd_setting_seq2));
+
+ return ret;
+}
+
+static int dvbt_set_profile(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dvbt_profile profile)
+{
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x10);
+ if (ret)
+ return ret;
+
+ return tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x67,
+ (profile == CXD2880_DVBT_PROFILE_HP)
+ ? 0x00 : 0x01);
+}
+
+int cxd2880_tnrdmd_dvbt_tune1(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt_tune_param
+ *tune_param)
+{
+ int ret;
+
+ if (!tnr_dmd || !tune_param)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_common_tune_setting1(tnr_dmd, CXD2880_DTV_SYS_DVBT,
+ tune_param->center_freq_khz,
+ tune_param->bandwidth, 0, 0);
+ if (ret)
+ return ret;
+
+ ret =
+ x_tune_dvbt_demod_setting(tnr_dmd, tune_param->bandwidth,
+ tnr_dmd->clk_mode);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret =
+ x_tune_dvbt_demod_setting(tnr_dmd->diver_sub,
+ tune_param->bandwidth,
+ tnr_dmd->diver_sub->clk_mode);
+ if (ret)
+ return ret;
+ }
+
+ return dvbt_set_profile(tnr_dmd, tune_param->profile);
+}
+
+int cxd2880_tnrdmd_dvbt_tune2(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt_tune_param
+ *tune_param)
+{
+ int ret;
+
+ if (!tnr_dmd || !tune_param)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_common_tune_setting2(tnr_dmd, CXD2880_DTV_SYS_DVBT,
+ 0);
+ if (ret)
+ return ret;
+
+ tnr_dmd->state = CXD2880_TNRDMD_STATE_ACTIVE;
+ tnr_dmd->frequency_khz = tune_param->center_freq_khz;
+ tnr_dmd->sys = CXD2880_DTV_SYS_DVBT;
+ tnr_dmd->bandwidth = tune_param->bandwidth;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_ACTIVE;
+ tnr_dmd->diver_sub->frequency_khz = tune_param->center_freq_khz;
+ tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_DVBT;
+ tnr_dmd->diver_sub->bandwidth = tune_param->bandwidth;
+ }
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_dvbt_sleep_setting(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret = x_sleep_dvbt_demod_setting(tnr_dmd);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+ ret = x_sleep_dvbt_demod_setting(tnr_dmd->diver_sub);
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_dvbt_check_demod_lock(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_tnrdmd_lock_result
+ *lock)
+{
+ int ret;
+
+ u8 sync_stat = 0;
+ u8 ts_lock = 0;
+ u8 unlock_detected = 0;
+ u8 unlock_detected_sub = 0;
+
+ if (!tnr_dmd || !lock)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd, &sync_stat, &ts_lock,
+ &unlock_detected);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+ if (sync_stat == 6)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+ else if (unlock_detected)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+ else
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+ return ret;
+ }
+
+ if (sync_stat == 6) {
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+ return ret;
+ }
+
+ ret =
+ cxd2880_tnrdmd_dvbt_mon_sync_stat_sub(tnr_dmd, &sync_stat,
+ &unlock_detected_sub);
+ if (ret)
+ return ret;
+
+ if (sync_stat == 6)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+ else if (unlock_detected && unlock_detected_sub)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+ else
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_dvbt_check_ts_lock(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_tnrdmd_lock_result
+ *lock)
+{
+ int ret;
+
+ u8 sync_stat = 0;
+ u8 ts_lock = 0;
+ u8 unlock_detected = 0;
+ u8 unlock_detected_sub = 0;
+
+ if (!tnr_dmd || !lock)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd, &sync_stat, &ts_lock,
+ &unlock_detected);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+ if (ts_lock)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+ else if (unlock_detected)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+ else
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+ return ret;
+ }
+
+ if (ts_lock) {
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+ return ret;
+ } else if (!unlock_detected) {
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+ return ret;
+ }
+
+ ret =
+ cxd2880_tnrdmd_dvbt_mon_sync_stat_sub(tnr_dmd, &sync_stat,
+ &unlock_detected_sub);
+ if (ret)
+ return ret;
+
+ if (unlock_detected && unlock_detected_sub)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+ else
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+ return ret;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h
new file mode 100644
index 000000000000..35d81ccc732b
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_tnrdmd_dvbt.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * control interface for DVB-T
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_TNRDMD_DVBT_H
+#define CXD2880_TNRDMD_DVBT_H
+
+#include "cxd2880_common.h"
+#include "cxd2880_tnrdmd.h"
+
+struct cxd2880_dvbt_tune_param {
+ u32 center_freq_khz;
+ enum cxd2880_dtv_bandwidth bandwidth;
+ enum cxd2880_dvbt_profile profile;
+};
+
+int cxd2880_tnrdmd_dvbt_tune1(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt_tune_param
+ *tune_param);
+
+int cxd2880_tnrdmd_dvbt_tune2(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt_tune_param
+ *tune_param);
+
+int cxd2880_tnrdmd_dvbt_sleep_setting(struct cxd2880_tnrdmd
+ *tnr_dmd);
+
+int cxd2880_tnrdmd_dvbt_check_demod_lock(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_tnrdmd_lock_result
+ *lock);
+
+int cxd2880_tnrdmd_dvbt_check_ts_lock(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_tnrdmd_lock_result
+ *lock);
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c
new file mode 100644
index 000000000000..dd32004a12d8
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c
@@ -0,0 +1,1217 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_tnrdmd_dvbt2.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * control functions for DVB-T2
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#include <media/dvb_frontend.h>
+
+#include "cxd2880_tnrdmd_dvbt2.h"
+#include "cxd2880_tnrdmd_dvbt2_mon.h"
+
+static const struct cxd2880_reg_value tune_dmd_setting_seq1[] = {
+ {0x00, 0x00}, {0x31, 0x02},
+};
+
+static const struct cxd2880_reg_value tune_dmd_setting_seq2[] = {
+ {0x00, 0x04}, {0x5d, 0x0b},
+};
+
+static int x_tune_dvbt2_demod_setting(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dtv_bandwidth
+ bandwidth,
+ enum cxd2880_tnrdmd_clockmode
+ clk_mode)
+{
+ static const u8 tsif_settings[2] = { 0x01, 0x01 };
+ static const u8 init_settings[14] = {
+ 0x07, 0x06, 0x01, 0xf0, 0x00, 0x00, 0x04, 0xb0, 0x00, 0x00,
+ 0x09, 0x9c, 0x0e, 0x4c
+ };
+ static const u8 clk_mode_settings_a1[9] = {
+ 0x52, 0x49, 0x2c, 0x51, 0x51, 0x3d, 0x15, 0x29, 0x0c
+ };
+
+ static const u8 clk_mode_settings_b1[9] = {
+ 0x5d, 0x55, 0x32, 0x5c, 0x5c, 0x45, 0x17, 0x2e, 0x0d
+ };
+
+ static const u8 clk_mode_settings_c1[9] = {
+ 0x60, 0x00, 0x34, 0x5e, 0x5e, 0x47, 0x18, 0x2f, 0x0e
+ };
+
+ static const u8 clk_mode_settings_a2[13] = {
+ 0x04, 0xe7, 0x94, 0x92, 0x09, 0xcf, 0x7e, 0xd0, 0x49,
+ 0xcd, 0xcd, 0x1f, 0x5b
+ };
+
+ static const u8 clk_mode_settings_b2[13] = {
+ 0x05, 0x90, 0x27, 0x55, 0x0b, 0x20, 0x8f, 0xd6, 0xea,
+ 0xc8, 0xc8, 0x23, 0x91
+ };
+
+ static const u8 clk_mode_settings_c2[13] = {
+ 0x05, 0xb8, 0xd8, 0x00, 0x0b, 0x72, 0x93, 0xf3, 0x00,
+ 0xcd, 0xcd, 0x24, 0x95
+ };
+
+ static const u8 clk_mode_settings_a3[5] = {
+ 0x0b, 0x6a, 0xc9, 0x03, 0x33
+ };
+ static const u8 clk_mode_settings_b3[5] = {
+ 0x01, 0x02, 0xe4, 0x03, 0x39
+ };
+ static const u8 clk_mode_settings_c3[5] = {
+ 0x01, 0x02, 0xeb, 0x03, 0x3b
+ };
+
+ static const u8 gtdofst[2] = { 0x3f, 0xff };
+
+ static const u8 bw8_gtdofst_a[2] = { 0x19, 0xd2 };
+ static const u8 bw8_nomi_ac[6] = { 0x15, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const u8 bw8_nomi_b[6] = { 0x14, 0x6a, 0xaa, 0xaa, 0xab, 0x00 };
+ static const u8 bw8_sst_a[2] = { 0x06, 0x2a };
+ static const u8 bw8_sst_b[2] = { 0x06, 0x29 };
+ static const u8 bw8_sst_c[2] = { 0x06, 0x28 };
+ static const u8 bw8_mrc_a[9] = {
+ 0x28, 0x00, 0x50, 0x00, 0x60, 0x00, 0x00, 0x90, 0x00
+ };
+ static const u8 bw8_mrc_b[9] = {
+ 0x2d, 0x5e, 0x5a, 0xbd, 0x6c, 0xe3, 0x00, 0xa3, 0x55
+ };
+ static const u8 bw8_mrc_c[9] = {
+ 0x2e, 0xaa, 0x5d, 0x55, 0x70, 0x00, 0x00, 0xa8, 0x00
+ };
+
+ static const u8 bw7_nomi_ac[6] = { 0x18, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const u8 bw7_nomi_b[6] = { 0x17, 0x55, 0x55, 0x55, 0x55, 0x00 };
+ static const u8 bw7_sst_a[2] = { 0x06, 0x23 };
+ static const u8 bw7_sst_b[2] = { 0x06, 0x22 };
+ static const u8 bw7_sst_c[2] = { 0x06, 0x21 };
+ static const u8 bw7_mrc_a[9] = {
+ 0x2d, 0xb6, 0x5b, 0x6d, 0x6d, 0xb6, 0x00, 0xa4, 0x92
+ };
+ static const u8 bw7_mrc_b[9] = {
+ 0x33, 0xda, 0x67, 0xb4, 0x7c, 0x71, 0x00, 0xba, 0xaa
+ };
+ static const u8 bw7_mrc_c[9] = {
+ 0x35, 0x55, 0x6a, 0xaa, 0x80, 0x00, 0x00, 0xc0, 0x00
+ };
+
+ static const u8 bw6_nomi_ac[6] = { 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const u8 bw6_nomi_b[6] = { 0x1b, 0x38, 0xe3, 0x8e, 0x39, 0x00 };
+ static const u8 bw6_sst_a[2] = { 0x06, 0x1c };
+ static const u8 bw6_sst_b[2] = { 0x06, 0x1b };
+ static const u8 bw6_sst_c[2] = { 0x06, 0x1a };
+ static const u8 bw6_mrc_a[9] = {
+ 0x35, 0x55, 0x6a, 0xaa, 0x80, 0x00, 0x00, 0xc0, 0x00
+ };
+ static const u8 bw6_mrc_b[9] = {
+ 0x3c, 0x7e, 0x78, 0xfc, 0x91, 0x2f, 0x00, 0xd9, 0xc7
+ };
+ static const u8 bw6_mrc_c[9] = {
+ 0x3e, 0x38, 0x7c, 0x71, 0x95, 0x55, 0x00, 0xdf, 0xff
+ };
+
+ static const u8 bw5_nomi_ac[6] = { 0x21, 0x99, 0x99, 0x99, 0x9a, 0x00 };
+ static const u8 bw5_nomi_b[6] = { 0x20, 0xaa, 0xaa, 0xaa, 0xab, 0x00 };
+ static const u8 bw5_sst_a[2] = { 0x06, 0x15 };
+ static const u8 bw5_sst_b[2] = { 0x06, 0x15 };
+ static const u8 bw5_sst_c[2] = { 0x06, 0x14 };
+ static const u8 bw5_mrc_a[9] = {
+ 0x40, 0x00, 0x6a, 0xaa, 0x80, 0x00, 0x00, 0xe6, 0x66
+ };
+ static const u8 bw5_mrc_b[9] = {
+ 0x48, 0x97, 0x78, 0xfc, 0x91, 0x2f, 0x01, 0x05, 0x55
+ };
+ static const u8 bw5_mrc_c[9] = {
+ 0x4a, 0xaa, 0x7c, 0x71, 0x95, 0x55, 0x01, 0x0c, 0xcc
+ };
+
+ static const u8 bw1_7_nomi_a[6] = {
+ 0x68, 0x0f, 0xa2, 0x32, 0xcf, 0x03
+ };
+ static const u8 bw1_7_nomi_c[6] = {
+ 0x68, 0x0f, 0xa2, 0x32, 0xcf, 0x03
+ };
+ static const u8 bw1_7_nomi_b[6] = {
+ 0x65, 0x2b, 0xa4, 0xcd, 0xd8, 0x03
+ };
+ static const u8 bw1_7_sst_a[2] = { 0x06, 0x0c };
+ static const u8 bw1_7_sst_b[2] = { 0x06, 0x0c };
+ static const u8 bw1_7_sst_c[2] = { 0x06, 0x0b };
+ static const u8 bw1_7_mrc_a[9] = {
+ 0x40, 0x00, 0x6a, 0xaa, 0x80, 0x00, 0x02, 0xc9, 0x8f
+ };
+ static const u8 bw1_7_mrc_b[9] = {
+ 0x48, 0x97, 0x78, 0xfc, 0x91, 0x2f, 0x03, 0x29, 0x5d
+ };
+ static const u8 bw1_7_mrc_c[9] = {
+ 0x4a, 0xaa, 0x7c, 0x71, 0x95, 0x55, 0x03, 0x40, 0x7d
+ };
+
+ const u8 *data = NULL;
+ const u8 *data2 = NULL;
+ const u8 *data3 = NULL;
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ tune_dmd_setting_seq1,
+ ARRAY_SIZE(tune_dmd_setting_seq1));
+ if (ret)
+ return ret;
+
+ ret = cxd2880_io_write_multi_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ tune_dmd_setting_seq2,
+ ARRAY_SIZE(tune_dmd_setting_seq2));
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) {
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xce, tsif_settings, 2);
+ if (ret)
+ return ret;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x20);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x8a, init_settings[0]);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x90, init_settings[1]);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x25);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xf0, &init_settings[2], 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x2a);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xdc, init_settings[4]);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xde, init_settings[5]);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x2d);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x73, &init_settings[6], 4);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x8f, &init_settings[10], 4);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = clk_mode_settings_a1;
+ data2 = clk_mode_settings_a2;
+ data3 = clk_mode_settings_a3;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = clk_mode_settings_b1;
+ data2 = clk_mode_settings_b2;
+ data3 = clk_mode_settings_b3;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = clk_mode_settings_c1;
+ data2 = clk_mode_settings_c2;
+ data3 = clk_mode_settings_c3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x04);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x1d, &data[0], 3);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x22, data[3]);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x24, data[4]);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x26, data[5]);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x29, &data[6], 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x2d, data[8]);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) {
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x2e, &data2[0], 6);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x35, &data2[6], 7);
+ if (ret)
+ return ret;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x3c, &data3[0], 2);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x56, &data3[2], 3);
+ if (ret)
+ return ret;
+
+ switch (bandwidth) {
+ case CXD2880_DTV_BW_8_MHZ:
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw8_nomi_ac;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw8_nomi_b;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x10, data, 6);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4a, 0x00);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw8_gtdofst_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = gtdofst;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x19, data, 2);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw8_sst_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw8_sst_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw8_sst_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x1b, data, 2);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw8_mrc_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw8_mrc_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw8_mrc_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4b, data, 9);
+ if (ret)
+ return ret;
+ }
+ break;
+
+ case CXD2880_DTV_BW_7_MHZ:
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw7_nomi_ac;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw7_nomi_b;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x10, data, 6);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4a, 0x02);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x19, gtdofst, 2);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw7_sst_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw7_sst_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw7_sst_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x1b, data, 2);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw7_mrc_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw7_mrc_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw7_mrc_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4b, data, 9);
+ if (ret)
+ return ret;
+ }
+ break;
+
+ case CXD2880_DTV_BW_6_MHZ:
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw6_nomi_ac;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw6_nomi_b;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x10, data, 6);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4a, 0x04);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x19, gtdofst, 2);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw6_sst_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw6_sst_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw6_sst_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x1b, data, 2);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw6_mrc_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw6_mrc_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw6_mrc_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4b, data, 9);
+ if (ret)
+ return ret;
+ }
+ break;
+
+ case CXD2880_DTV_BW_5_MHZ:
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw5_nomi_ac;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw5_nomi_b;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x10, data, 6);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4a, 0x06);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x19, gtdofst, 2);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw5_sst_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw5_sst_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw5_sst_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x1b, data, 2);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw5_mrc_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw5_mrc_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw5_mrc_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4b, data, 9);
+ if (ret)
+ return ret;
+ }
+ break;
+
+ case CXD2880_DTV_BW_1_7_MHZ:
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw1_7_nomi_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw1_7_nomi_c;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw1_7_nomi_b;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x10, data, 6);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4a, 0x03);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x19, gtdofst, 2);
+ if (ret)
+ return ret;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw1_7_sst_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw1_7_sst_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw1_7_sst_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x1b, data, 2);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = bw1_7_mrc_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = bw1_7_mrc_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = bw1_7_mrc_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x4b, data, 9);
+ if (ret)
+ return ret;
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+
+ return tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xfd, 0x01);
+}
+
+static int x_sleep_dvbt2_demod_setting(struct cxd2880_tnrdmd
+ *tnr_dmd)
+{
+ static const u8 difint_clip[] = {
+ 0, 1, 0, 2, 0, 4, 0, 8, 0, 16, 0, 32
+ };
+ int ret = 0;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x1d);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x47, difint_clip, 12);
+ }
+
+ return ret;
+}
+
+static int dvbt2_set_profile(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dvbt2_profile profile)
+{
+ u8 t2_mode_tune_mode = 0;
+ u8 seq_not2_dtime = 0;
+ u8 dtime1 = 0;
+ u8 dtime2 = 0;
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ switch (tnr_dmd->clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ dtime1 = 0x27;
+ dtime2 = 0x0c;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ dtime1 = 0x2c;
+ dtime2 = 0x0d;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ dtime1 = 0x2e;
+ dtime2 = 0x0e;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (profile) {
+ case CXD2880_DVBT2_PROFILE_BASE:
+ t2_mode_tune_mode = 0x01;
+ seq_not2_dtime = dtime2;
+ break;
+
+ case CXD2880_DVBT2_PROFILE_LITE:
+ t2_mode_tune_mode = 0x05;
+ seq_not2_dtime = dtime1;
+ break;
+
+ case CXD2880_DVBT2_PROFILE_ANY:
+ t2_mode_tune_mode = 0x00;
+ seq_not2_dtime = dtime1;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x2e);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x10, t2_mode_tune_mode);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x04);
+ if (ret)
+ return ret;
+
+ return tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x2c, seq_not2_dtime);
+}
+
+int cxd2880_tnrdmd_dvbt2_tune1(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt2_tune_param
+ *tune_param)
+{
+ int ret;
+
+ if (!tnr_dmd || !tune_param)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN &&
+ tune_param->profile == CXD2880_DVBT2_PROFILE_ANY)
+ return -ENOTTY;
+
+ ret =
+ cxd2880_tnrdmd_common_tune_setting1(tnr_dmd, CXD2880_DTV_SYS_DVBT2,
+ tune_param->center_freq_khz,
+ tune_param->bandwidth, 0, 0);
+ if (ret)
+ return ret;
+
+ ret =
+ x_tune_dvbt2_demod_setting(tnr_dmd, tune_param->bandwidth,
+ tnr_dmd->clk_mode);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret =
+ x_tune_dvbt2_demod_setting(tnr_dmd->diver_sub,
+ tune_param->bandwidth,
+ tnr_dmd->diver_sub->clk_mode);
+ if (ret)
+ return ret;
+ }
+
+ ret = dvbt2_set_profile(tnr_dmd, tune_param->profile);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret =
+ dvbt2_set_profile(tnr_dmd->diver_sub, tune_param->profile);
+ if (ret)
+ return ret;
+ }
+
+ if (tune_param->data_plp_id == CXD2880_DVBT2_TUNE_PARAM_PLPID_AUTO)
+ ret = cxd2880_tnrdmd_dvbt2_set_plp_cfg(tnr_dmd, 1, 0);
+ else
+ ret =
+ cxd2880_tnrdmd_dvbt2_set_plp_cfg(tnr_dmd, 0,
+ (u8)(tune_param->data_plp_id));
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_tune2(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt2_tune_param
+ *tune_param)
+{
+ u8 en_fef_intmtnt_ctrl = 1;
+ int ret;
+
+ if (!tnr_dmd || !tune_param)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ switch (tune_param->profile) {
+ case CXD2880_DVBT2_PROFILE_BASE:
+ en_fef_intmtnt_ctrl = tnr_dmd->en_fef_intmtnt_base;
+ break;
+ case CXD2880_DVBT2_PROFILE_LITE:
+ en_fef_intmtnt_ctrl = tnr_dmd->en_fef_intmtnt_lite;
+ break;
+ case CXD2880_DVBT2_PROFILE_ANY:
+ if (tnr_dmd->en_fef_intmtnt_base &&
+ tnr_dmd->en_fef_intmtnt_lite)
+ en_fef_intmtnt_ctrl = 1;
+ else
+ en_fef_intmtnt_ctrl = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret =
+ cxd2880_tnrdmd_common_tune_setting2(tnr_dmd,
+ CXD2880_DTV_SYS_DVBT2,
+ en_fef_intmtnt_ctrl);
+ if (ret)
+ return ret;
+
+ tnr_dmd->state = CXD2880_TNRDMD_STATE_ACTIVE;
+ tnr_dmd->frequency_khz = tune_param->center_freq_khz;
+ tnr_dmd->sys = CXD2880_DTV_SYS_DVBT2;
+ tnr_dmd->bandwidth = tune_param->bandwidth;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_ACTIVE;
+ tnr_dmd->diver_sub->frequency_khz = tune_param->center_freq_khz;
+ tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_DVBT2;
+ tnr_dmd->diver_sub->bandwidth = tune_param->bandwidth;
+ }
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_dvbt2_sleep_setting(struct cxd2880_tnrdmd
+ *tnr_dmd)
+{
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret = x_sleep_dvbt2_demod_setting(tnr_dmd);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+ ret = x_sleep_dvbt2_demod_setting(tnr_dmd->diver_sub);
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_check_demod_lock(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_tnrdmd_lock_result
+ *lock)
+{
+ int ret;
+
+ u8 sync_stat = 0;
+ u8 ts_lock = 0;
+ u8 unlock_detected = 0;
+ u8 unlock_detected_sub = 0;
+
+ if (!tnr_dmd || !lock)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_stat, &ts_lock,
+ &unlock_detected);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+ if (sync_stat == 6)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+ else if (unlock_detected)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+ else
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+ return ret;
+ }
+
+ if (sync_stat == 6) {
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+ return ret;
+ }
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub(tnr_dmd, &sync_stat,
+ &unlock_detected_sub);
+ if (ret)
+ return ret;
+
+ if (sync_stat == 6)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+ else if (unlock_detected && unlock_detected_sub)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+ else
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_check_ts_lock(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_tnrdmd_lock_result
+ *lock)
+{
+ int ret;
+
+ u8 sync_stat = 0;
+ u8 ts_lock = 0;
+ u8 unlock_detected = 0;
+ u8 unlock_detected_sub = 0;
+
+ if (!tnr_dmd || !lock)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_stat, &ts_lock,
+ &unlock_detected);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+ if (ts_lock)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+ else if (unlock_detected)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+ else
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+ return ret;
+ }
+
+ if (ts_lock) {
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+ return ret;
+ } else if (!unlock_detected) {
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+ return ret;
+ }
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub(tnr_dmd, &sync_stat,
+ &unlock_detected_sub);
+ if (ret)
+ return ret;
+
+ if (unlock_detected && unlock_detected_sub)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+ else
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_set_plp_cfg(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 auto_plp,
+ u8 plp_id)
+{
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x23);
+ if (ret)
+ return ret;
+
+ if (!auto_plp) {
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xaf, plp_id);
+ if (ret)
+ return ret;
+ }
+
+ return tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xad, auto_plp ? 0x00 : 0x01);
+}
+
+int cxd2880_tnrdmd_dvbt2_diver_fef_setting(struct cxd2880_tnrdmd
+ *tnr_dmd)
+{
+ struct cxd2880_dvbt2_ofdm ofdm;
+ static const u8 data[] = { 0, 8, 0, 16, 0, 32, 0, 64, 0, 128, 1, 0};
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE)
+ return 0;
+
+ ret = cxd2880_tnrdmd_dvbt2_mon_ofdm(tnr_dmd, &ofdm);
+ if (ret)
+ return ret;
+
+ if (!ofdm.mixed)
+ return 0;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x1d);
+ if (ret)
+ return ret;
+
+ return tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x47, data, 12);
+}
+
+int cxd2880_tnrdmd_dvbt2_check_l1post_valid(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ u8 *l1_post_valid)
+{
+ int ret;
+
+ u8 data;
+
+ if (!tnr_dmd || !l1_post_valid)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0b);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x86, &data, 1);
+ if (ret)
+ return ret;
+
+ *l1_post_valid = data & 0x01;
+
+ return ret;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h
new file mode 100644
index 000000000000..7108e3540093
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_tnrdmd_dvbt2.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * control interface for DVB-T2
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_TNRDMD_DVBT2_H
+#define CXD2880_TNRDMD_DVBT2_H
+
+#include "cxd2880_common.h"
+#include "cxd2880_tnrdmd.h"
+
+enum cxd2880_tnrdmd_dvbt2_tune_info {
+ CXD2880_TNRDMD_DVBT2_TUNE_INFO_OK,
+ CXD2880_TNRDMD_DVBT2_TUNE_INFO_INVALID_PLP_ID
+};
+
+struct cxd2880_dvbt2_tune_param {
+ u32 center_freq_khz;
+ enum cxd2880_dtv_bandwidth bandwidth;
+ u16 data_plp_id;
+ enum cxd2880_dvbt2_profile profile;
+ enum cxd2880_tnrdmd_dvbt2_tune_info tune_info;
+};
+
+#define CXD2880_DVBT2_TUNE_PARAM_PLPID_AUTO 0xffff
+
+int cxd2880_tnrdmd_dvbt2_tune1(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt2_tune_param
+ *tune_param);
+
+int cxd2880_tnrdmd_dvbt2_tune2(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt2_tune_param
+ *tune_param);
+
+int cxd2880_tnrdmd_dvbt2_sleep_setting(struct cxd2880_tnrdmd
+ *tnr_dmd);
+
+int cxd2880_tnrdmd_dvbt2_check_demod_lock(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_tnrdmd_lock_result
+ *lock);
+
+int cxd2880_tnrdmd_dvbt2_check_ts_lock(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_tnrdmd_lock_result
+ *lock);
+
+int cxd2880_tnrdmd_dvbt2_set_plp_cfg(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 auto_plp,
+ u8 plp_id);
+
+int cxd2880_tnrdmd_dvbt2_diver_fef_setting(struct cxd2880_tnrdmd
+ *tnr_dmd);
+
+int cxd2880_tnrdmd_dvbt2_check_l1post_valid(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ u8 *l1_post_valid);
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c
new file mode 100644
index 000000000000..604580bf7cf7
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c
@@ -0,0 +1,1878 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_tnrdmd_dvbt2_mon.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T2 monitor functions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#include "cxd2880_tnrdmd_mon.h"
+#include "cxd2880_tnrdmd_dvbt2.h"
+#include "cxd2880_tnrdmd_dvbt2_mon.h"
+
+#include <media/dvb_math.h>
+
+static const int ref_dbm_1000[4][8] = {
+ {-96000, -95000, -94000, -93000, -92000, -92000, -98000, -97000},
+ {-91000, -89000, -88000, -87000, -86000, -86000, -93000, -92000},
+ {-86000, -85000, -83000, -82000, -81000, -80000, -89000, -88000},
+ {-82000, -80000, -78000, -76000, -75000, -74000, -86000, -84000},
+};
+
+int cxd2880_tnrdmd_dvbt2_mon_sync_stat(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 *sync_stat,
+ u8 *ts_lock_stat,
+ u8 *unlock_detected)
+{
+ u8 data;
+ int ret;
+
+ if (!tnr_dmd || !sync_stat || !ts_lock_stat || !unlock_detected)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0b);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x10, &data, sizeof(data));
+ if (ret)
+ return ret;
+
+ *sync_stat = data & 0x07;
+ *ts_lock_stat = ((data & 0x20) ? 1 : 0);
+ *unlock_detected = ((data & 0x10) ? 1 : 0);
+
+ if (*sync_stat == 0x07)
+ return -EAGAIN;
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ u8 *sync_stat,
+ u8 *unlock_detected)
+{
+ u8 ts_lock_stat = 0;
+
+ if (!tnr_dmd || !sync_stat || !unlock_detected)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return -EINVAL;
+
+ return cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd->diver_sub,
+ sync_stat,
+ &ts_lock_stat,
+ unlock_detected);
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_carrier_offset(struct cxd2880_tnrdmd
+ *tnr_dmd, int *offset)
+{
+ u8 data[4];
+ u32 ctl_val = 0;
+ u8 sync_state = 0;
+ u8 ts_lock = 0;
+ u8 unlock_detected = 0;
+ int ret;
+
+ if (!tnr_dmd || !offset)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return -EINVAL;
+
+ ret = slvt_freeze_reg(tnr_dmd);
+ if (ret)
+ return ret;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+ &ts_lock,
+ &unlock_detected);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (sync_state != 6) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return -EAGAIN;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0b);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x30, data, sizeof(data));
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ ctl_val =
+ ((data[0] & 0x0f) << 24) | (data[1] << 16) | (data[2] << 8)
+ | (data[3]);
+ *offset = cxd2880_convert2s_complement(ctl_val, 28);
+
+ switch (tnr_dmd->bandwidth) {
+ case CXD2880_DTV_BW_1_7_MHZ:
+ *offset = -1 * ((*offset) / 582);
+ break;
+ case CXD2880_DTV_BW_5_MHZ:
+ case CXD2880_DTV_BW_6_MHZ:
+ case CXD2880_DTV_BW_7_MHZ:
+ case CXD2880_DTV_BW_8_MHZ:
+ *offset = -1 * ((*offset) * tnr_dmd->bandwidth / 940);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_carrier_offset_sub(struct
+ cxd2880_tnrdmd
+ *tnr_dmd,
+ int *offset)
+{
+ if (!tnr_dmd || !offset)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return -EINVAL;
+
+ return cxd2880_tnrdmd_dvbt2_mon_carrier_offset(tnr_dmd->diver_sub,
+ offset);
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_l1_pre(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt2_l1pre
+ *l1_pre)
+{
+ u8 data[37];
+ u8 sync_state = 0;
+ u8 ts_lock = 0;
+ u8 unlock_detected = 0;
+ u8 version = 0;
+ enum cxd2880_dvbt2_profile profile;
+ int ret;
+
+ if (!tnr_dmd || !l1_pre)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return -EINVAL;
+
+ ret = slvt_freeze_reg(tnr_dmd);
+ if (ret)
+ return ret;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+ &ts_lock,
+ &unlock_detected);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (sync_state < 5) {
+ if (tnr_dmd->diver_mode ==
+ CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub
+ (tnr_dmd, &sync_state, &unlock_detected);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (sync_state < 5) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return -EAGAIN;
+ }
+ } else {
+ slvt_unfreeze_reg(tnr_dmd);
+ return -EAGAIN;
+ }
+ }
+
+ ret = cxd2880_tnrdmd_dvbt2_mon_profile(tnr_dmd, &profile);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0b);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x61, data, sizeof(data));
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+ slvt_unfreeze_reg(tnr_dmd);
+
+ l1_pre->type = (enum cxd2880_dvbt2_l1pre_type)data[0];
+ l1_pre->bw_ext = data[1] & 0x01;
+ l1_pre->s1 = (enum cxd2880_dvbt2_s1)(data[2] & 0x07);
+ l1_pre->s2 = data[3] & 0x0f;
+ l1_pre->l1_rep = data[4] & 0x01;
+ l1_pre->gi = (enum cxd2880_dvbt2_guard)(data[5] & 0x07);
+ l1_pre->papr = (enum cxd2880_dvbt2_papr)(data[6] & 0x0f);
+ l1_pre->mod =
+ (enum cxd2880_dvbt2_l1post_constell)(data[7] & 0x0f);
+ l1_pre->cr = (enum cxd2880_dvbt2_l1post_cr)(data[8] & 0x03);
+ l1_pre->fec =
+ (enum cxd2880_dvbt2_l1post_fec_type)(data[9] & 0x03);
+ l1_pre->l1_post_size = (data[10] & 0x03) << 16;
+ l1_pre->l1_post_size |= (data[11]) << 8;
+ l1_pre->l1_post_size |= (data[12]);
+ l1_pre->l1_post_info_size = (data[13] & 0x03) << 16;
+ l1_pre->l1_post_info_size |= (data[14]) << 8;
+ l1_pre->l1_post_info_size |= (data[15]);
+ l1_pre->pp = (enum cxd2880_dvbt2_pp)(data[16] & 0x0f);
+ l1_pre->tx_id_availability = data[17];
+ l1_pre->cell_id = (data[18] << 8);
+ l1_pre->cell_id |= (data[19]);
+ l1_pre->network_id = (data[20] << 8);
+ l1_pre->network_id |= (data[21]);
+ l1_pre->sys_id = (data[22] << 8);
+ l1_pre->sys_id |= (data[23]);
+ l1_pre->num_frames = data[24];
+ l1_pre->num_symbols = (data[25] & 0x0f) << 8;
+ l1_pre->num_symbols |= data[26];
+ l1_pre->regen = data[27] & 0x07;
+ l1_pre->post_ext = data[28] & 0x01;
+ l1_pre->num_rf_freqs = data[29] & 0x07;
+ l1_pre->rf_idx = data[30] & 0x07;
+ version = (data[31] & 0x03) << 2;
+ version |= (data[32] & 0xc0) >> 6;
+ l1_pre->t2_version = (enum cxd2880_dvbt2_version)version;
+ l1_pre->l1_post_scrambled = (data[32] & 0x20) >> 5;
+ l1_pre->t2_base_lite = (data[32] & 0x10) >> 4;
+ l1_pre->crc32 = (data[33] << 24);
+ l1_pre->crc32 |= (data[34] << 16);
+ l1_pre->crc32 |= (data[35] << 8);
+ l1_pre->crc32 |= data[36];
+
+ if (profile == CXD2880_DVBT2_PROFILE_BASE) {
+ switch ((l1_pre->s2 >> 1)) {
+ case CXD2880_DVBT2_BASE_S2_M1K_G_ANY:
+ l1_pre->fft_mode = CXD2880_DVBT2_M1K;
+ break;
+ case CXD2880_DVBT2_BASE_S2_M2K_G_ANY:
+ l1_pre->fft_mode = CXD2880_DVBT2_M2K;
+ break;
+ case CXD2880_DVBT2_BASE_S2_M4K_G_ANY:
+ l1_pre->fft_mode = CXD2880_DVBT2_M4K;
+ break;
+ case CXD2880_DVBT2_BASE_S2_M8K_G_DVBT:
+ case CXD2880_DVBT2_BASE_S2_M8K_G_DVBT2:
+ l1_pre->fft_mode = CXD2880_DVBT2_M8K;
+ break;
+ case CXD2880_DVBT2_BASE_S2_M16K_G_ANY:
+ l1_pre->fft_mode = CXD2880_DVBT2_M16K;
+ break;
+ case CXD2880_DVBT2_BASE_S2_M32K_G_DVBT:
+ case CXD2880_DVBT2_BASE_S2_M32K_G_DVBT2:
+ l1_pre->fft_mode = CXD2880_DVBT2_M32K;
+ break;
+ default:
+ return -EAGAIN;
+ }
+ } else if (profile == CXD2880_DVBT2_PROFILE_LITE) {
+ switch ((l1_pre->s2 >> 1)) {
+ case CXD2880_DVBT2_LITE_S2_M2K_G_ANY:
+ l1_pre->fft_mode = CXD2880_DVBT2_M2K;
+ break;
+ case CXD2880_DVBT2_LITE_S2_M4K_G_ANY:
+ l1_pre->fft_mode = CXD2880_DVBT2_M4K;
+ break;
+ case CXD2880_DVBT2_LITE_S2_M8K_G_DVBT:
+ case CXD2880_DVBT2_LITE_S2_M8K_G_DVBT2:
+ l1_pre->fft_mode = CXD2880_DVBT2_M8K;
+ break;
+ case CXD2880_DVBT2_LITE_S2_M16K_G_DVBT:
+ case CXD2880_DVBT2_LITE_S2_M16K_G_DVBT2:
+ l1_pre->fft_mode = CXD2880_DVBT2_M16K;
+ break;
+ default:
+ return -EAGAIN;
+ }
+ } else {
+ return -EAGAIN;
+ }
+
+ l1_pre->mixed = l1_pre->s2 & 0x01;
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_version(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dvbt2_version
+ *ver)
+{
+ u8 data[2];
+ u8 sync_state = 0;
+ u8 ts_lock = 0;
+ u8 unlock_detected = 0;
+ u8 version = 0;
+ int ret;
+
+ if (!tnr_dmd || !ver)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return -EINVAL;
+
+ ret = slvt_freeze_reg(tnr_dmd);
+ if (ret)
+ return ret;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+ &ts_lock,
+ &unlock_detected);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (sync_state < 5) {
+ if (tnr_dmd->diver_mode ==
+ CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub
+ (tnr_dmd, &sync_state, &unlock_detected);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (sync_state < 5) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return -EAGAIN;
+ }
+ } else {
+ slvt_unfreeze_reg(tnr_dmd);
+ return -EAGAIN;
+ }
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0b);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x80, data, sizeof(data));
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ version = ((data[0] & 0x03) << 2);
+ version |= ((data[1] & 0xc0) >> 6);
+ *ver = (enum cxd2880_dvbt2_version)version;
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_ofdm(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt2_ofdm *ofdm)
+{
+ u8 data[5];
+ u8 sync_state = 0;
+ u8 ts_lock = 0;
+ u8 unlock_detected = 0;
+ int ret;
+
+ if (!tnr_dmd || !ofdm)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return -EINVAL;
+
+ ret = slvt_freeze_reg(tnr_dmd);
+ if (ret)
+ return ret;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+ &ts_lock,
+ &unlock_detected);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (sync_state != 6) {
+ slvt_unfreeze_reg(tnr_dmd);
+
+ ret = -EAGAIN;
+
+ if (tnr_dmd->diver_mode ==
+ CXD2880_TNRDMD_DIVERMODE_MAIN)
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_ofdm(tnr_dmd->diver_sub,
+ ofdm);
+
+ return ret;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0b);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x1d, data, sizeof(data));
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ ofdm->mixed = ((data[0] & 0x20) ? 1 : 0);
+ ofdm->is_miso = ((data[0] & 0x10) >> 4);
+ ofdm->mode = (enum cxd2880_dvbt2_mode)(data[0] & 0x07);
+ ofdm->gi = (enum cxd2880_dvbt2_guard)((data[1] & 0x70) >> 4);
+ ofdm->pp = (enum cxd2880_dvbt2_pp)(data[1] & 0x07);
+ ofdm->bw_ext = (data[2] & 0x10) >> 4;
+ ofdm->papr = (enum cxd2880_dvbt2_papr)(data[2] & 0x0f);
+ ofdm->num_symbols = (data[3] << 8) | data[4];
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_data_plps(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 *plp_ids,
+ u8 *num_plps)
+{
+ u8 l1_post_ok = 0;
+ int ret;
+
+ if (!tnr_dmd || !num_plps)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0b);
+ if (ret)
+ return ret;
+
+ ret = slvt_freeze_reg(tnr_dmd);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x86, &l1_post_ok, 1);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (!(l1_post_ok & 0x01)) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return -EAGAIN;
+ }
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xc1, num_plps, 1);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (*num_plps == 0) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return -EINVAL;
+ }
+
+ if (!plp_ids) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return 0;
+ }
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xc2,
+ plp_ids,
+ ((*num_plps > 62) ?
+ 62 : *num_plps));
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (*num_plps > 62) {
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0c);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x10, plp_ids + 62,
+ *num_plps - 62);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_active_plp(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_dvbt2_plp_btype
+ type,
+ struct cxd2880_dvbt2_plp
+ *plp_info)
+{
+ u8 data[20];
+ u8 addr = 0;
+ u8 index = 0;
+ u8 l1_post_ok = 0;
+ int ret;
+
+ if (!tnr_dmd || !plp_info)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return -EINVAL;
+
+ ret = slvt_freeze_reg(tnr_dmd);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0b);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x86, &l1_post_ok, 1);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (!l1_post_ok) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return -EAGAIN;
+ }
+
+ if (type == CXD2880_DVBT2_PLP_COMMON)
+ addr = 0xa9;
+ else
+ addr = 0x96;
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ addr, data, sizeof(data));
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ if (type == CXD2880_DVBT2_PLP_COMMON && !data[13])
+ return -EAGAIN;
+
+ plp_info->id = data[index++];
+ plp_info->type =
+ (enum cxd2880_dvbt2_plp_type)(data[index++] & 0x07);
+ plp_info->payload =
+ (enum cxd2880_dvbt2_plp_payload)(data[index++] & 0x1f);
+ plp_info->ff = data[index++] & 0x01;
+ plp_info->first_rf_idx = data[index++] & 0x07;
+ plp_info->first_frm_idx = data[index++];
+ plp_info->group_id = data[index++];
+ plp_info->plp_cr =
+ (enum cxd2880_dvbt2_plp_code_rate)(data[index++] & 0x07);
+ plp_info->constell =
+ (enum cxd2880_dvbt2_plp_constell)(data[index++] & 0x07);
+ plp_info->rot = data[index++] & 0x01;
+ plp_info->fec =
+ (enum cxd2880_dvbt2_plp_fec)(data[index++] & 0x03);
+ plp_info->num_blocks_max = (data[index++] & 0x03) << 8;
+ plp_info->num_blocks_max |= data[index++];
+ plp_info->frm_int = data[index++];
+ plp_info->til_len = data[index++];
+ plp_info->til_type = data[index++] & 0x01;
+
+ plp_info->in_band_a_flag = data[index++] & 0x01;
+ plp_info->rsvd = data[index++] << 8;
+ plp_info->rsvd |= data[index++];
+
+ plp_info->in_band_b_flag =
+ (plp_info->rsvd & 0x8000) >> 15;
+ plp_info->plp_mode =
+ (enum cxd2880_dvbt2_plp_mode)((plp_info->rsvd & 0x000c) >> 2);
+ plp_info->static_flag = (plp_info->rsvd & 0x0002) >> 1;
+ plp_info->static_padding_flag = plp_info->rsvd & 0x0001;
+ plp_info->rsvd = (plp_info->rsvd & 0x7ff0) >> 4;
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_data_plp_error(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ u8 *plp_error)
+{
+ u8 data;
+ int ret;
+
+ if (!tnr_dmd || !plp_error)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return -EINVAL;
+
+ ret = slvt_freeze_reg(tnr_dmd);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0b);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x86, &data, 1);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if ((data & 0x01) == 0x00) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return -EAGAIN;
+ }
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xc0, &data, 1);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ *plp_error = data & 0x01;
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_l1_change(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 *l1_change)
+{
+ u8 data;
+ u8 sync_state = 0;
+ u8 ts_lock = 0;
+ u8 unlock_detected = 0;
+ int ret;
+
+ if (!tnr_dmd || !l1_change)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return -EINVAL;
+
+ ret = slvt_freeze_reg(tnr_dmd);
+ if (ret)
+ return ret;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+ &ts_lock,
+ &unlock_detected);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (sync_state < 5) {
+ if (tnr_dmd->diver_mode ==
+ CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub
+ (tnr_dmd, &sync_state, &unlock_detected);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (sync_state < 5) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return -EAGAIN;
+ }
+ } else {
+ slvt_unfreeze_reg(tnr_dmd);
+ return -EAGAIN;
+ }
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0b);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x5f, &data, sizeof(data));
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ *l1_change = data & 0x01;
+ if (*l1_change) {
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x22);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x16, 0x01);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+ }
+ slvt_unfreeze_reg(tnr_dmd);
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_l1_post(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ struct cxd2880_dvbt2_l1post
+ *l1_post)
+{
+ u8 data[16];
+ int ret;
+
+ if (!tnr_dmd || !l1_post)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0b);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x86, data, sizeof(data));
+ if (ret)
+ return ret;
+
+ if (!(data[0] & 0x01))
+ return -EAGAIN;
+
+ l1_post->sub_slices_per_frame = (data[1] & 0x7f) << 8;
+ l1_post->sub_slices_per_frame |= data[2];
+ l1_post->num_plps = data[3];
+ l1_post->num_aux = data[4] & 0x0f;
+ l1_post->aux_cfg_rfu = data[5];
+ l1_post->rf_idx = data[6] & 0x07;
+ l1_post->freq = data[7] << 24;
+ l1_post->freq |= data[8] << 16;
+ l1_post->freq |= data[9] << 8;
+ l1_post->freq |= data[10];
+ l1_post->fef_type = data[11] & 0x0f;
+ l1_post->fef_length = data[12] << 16;
+ l1_post->fef_length |= data[13] << 8;
+ l1_post->fef_length |= data[14];
+ l1_post->fef_intvl = data[15];
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_bbheader(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dvbt2_plp_btype
+ type,
+ struct cxd2880_dvbt2_bbheader
+ *bbheader)
+{
+ u8 sync_state = 0;
+ u8 ts_lock = 0;
+ u8 unlock_detected = 0;
+ u8 data[14];
+ u8 addr = 0;
+ int ret;
+
+ if (!tnr_dmd || !bbheader)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return -EINVAL;
+
+ ret = slvt_freeze_reg(tnr_dmd);
+ if (ret)
+ return ret;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+ &ts_lock,
+ &unlock_detected);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (!ts_lock) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return -EAGAIN;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0b);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (type == CXD2880_DVBT2_PLP_COMMON) {
+ u8 l1_post_ok;
+ u8 data;
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x86, &l1_post_ok, 1);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (!(l1_post_ok & 0x01)) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return -EAGAIN;
+ }
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xb6, &data, 1);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (data == 0) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return -EAGAIN;
+ }
+ }
+
+ if (type == CXD2880_DVBT2_PLP_COMMON)
+ addr = 0x51;
+ else
+ addr = 0x42;
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ addr, data, sizeof(data));
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ bbheader->stream_input =
+ (enum cxd2880_dvbt2_stream)((data[0] >> 6) & 0x03);
+ bbheader->is_single_input_stream = (data[0] >> 5) & 0x01;
+ bbheader->is_constant_coding_modulation =
+ (data[0] >> 4) & 0x01;
+ bbheader->issy_indicator = (data[0] >> 3) & 0x01;
+ bbheader->null_packet_deletion = (data[0] >> 2) & 0x01;
+ bbheader->ext = data[0] & 0x03;
+
+ bbheader->input_stream_identifier = data[1];
+ bbheader->plp_mode =
+ (data[3] & 0x01) ? CXD2880_DVBT2_PLP_MODE_HEM :
+ CXD2880_DVBT2_PLP_MODE_NM;
+ bbheader->data_field_length = (data[4] << 8) | data[5];
+
+ if (bbheader->plp_mode == CXD2880_DVBT2_PLP_MODE_NM) {
+ bbheader->user_packet_length =
+ (data[6] << 8) | data[7];
+ bbheader->sync_byte = data[8];
+ bbheader->issy = 0;
+ } else {
+ bbheader->user_packet_length = 0;
+ bbheader->sync_byte = 0;
+ bbheader->issy =
+ (data[11] << 16) | (data[12] << 8) | data[13];
+ }
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_in_bandb_ts_rate(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_dvbt2_plp_btype
+ type,
+ u32 *ts_rate_bps)
+{
+ u8 sync_state = 0;
+ u8 ts_lock = 0;
+ u8 unlock_detected = 0;
+ u8 l1_post_ok = 0;
+ u8 data[4];
+ u8 addr = 0;
+
+ int ret;
+
+ if (!tnr_dmd || !ts_rate_bps)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return -EINVAL;
+
+ ret = slvt_freeze_reg(tnr_dmd);
+ if (ret)
+ return ret;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+ &ts_lock,
+ &unlock_detected);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (!ts_lock) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return -EAGAIN;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0b);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x86, &l1_post_ok, 1);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (!(l1_post_ok & 0x01)) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return -EAGAIN;
+ }
+
+ if (type == CXD2880_DVBT2_PLP_COMMON)
+ addr = 0xba;
+ else
+ addr = 0xa7;
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ addr, &data[0], 1);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if ((data[0] & 0x80) == 0x00) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return -EAGAIN;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x25);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (type == CXD2880_DVBT2_PLP_COMMON)
+ addr = 0xa6;
+ else
+ addr = 0xaa;
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ addr, &data[0], 4);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ *ts_rate_bps = ((data[0] & 0x07) << 24) | (data[1] << 16) |
+ (data[2] << 8) | data[3];
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_spectrum_sense(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_tnrdmd_spectrum_sense
+ *sense)
+{
+ u8 sync_state = 0;
+ u8 ts_lock = 0;
+ u8 early_unlock = 0;
+ u8 data = 0;
+ int ret;
+
+ if (!tnr_dmd || !sense)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return -EINVAL;
+
+ ret = slvt_freeze_reg(tnr_dmd);
+ if (ret)
+ return ret;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state, &ts_lock,
+ &early_unlock);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (sync_state != 6) {
+ slvt_unfreeze_reg(tnr_dmd);
+
+ ret = -EAGAIN;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_spectrum_sense(tnr_dmd->diver_sub,
+ sense);
+
+ return ret;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0b);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x2f, &data, sizeof(data));
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ *sense =
+ (data & 0x01) ? CXD2880_TNRDMD_SPECTRUM_INV :
+ CXD2880_TNRDMD_SPECTRUM_NORMAL;
+
+ return 0;
+}
+
+static int dvbt2_read_snr_reg(struct cxd2880_tnrdmd *tnr_dmd,
+ u16 *reg_value)
+{
+ u8 sync_state = 0;
+ u8 ts_lock = 0;
+ u8 unlock_detected = 0;
+ u8 data[2];
+ int ret;
+
+ if (!tnr_dmd || !reg_value)
+ return -EINVAL;
+
+ ret = slvt_freeze_reg(tnr_dmd);
+ if (ret)
+ return ret;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+ &ts_lock,
+ &unlock_detected);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (sync_state != 6) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return -EAGAIN;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0b);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x13, data, sizeof(data));
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ *reg_value = (data[0] << 8) | data[1];
+
+ return ret;
+}
+
+static int dvbt2_calc_snr(struct cxd2880_tnrdmd *tnr_dmd,
+ u32 reg_value, int *snr)
+{
+ if (!tnr_dmd || !snr)
+ return -EINVAL;
+
+ if (reg_value == 0)
+ return -EAGAIN;
+
+ if (reg_value > 10876)
+ reg_value = 10876;
+
+ *snr = intlog10(reg_value) - intlog10(12600 - reg_value);
+ *snr = (*snr + 839) / 1678 + 32000;
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_snr(struct cxd2880_tnrdmd *tnr_dmd,
+ int *snr)
+{
+ u16 reg_value = 0;
+ int ret;
+
+ if (!tnr_dmd || !snr)
+ return -EINVAL;
+
+ *snr = -1000 * 1000;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+ ret = dvbt2_read_snr_reg(tnr_dmd, &reg_value);
+ if (ret)
+ return ret;
+
+ ret = dvbt2_calc_snr(tnr_dmd, reg_value, snr);
+ } else {
+ int snr_main = 0;
+ int snr_sub = 0;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_snr_diver(tnr_dmd, snr, &snr_main,
+ &snr_sub);
+ }
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_snr_diver(struct cxd2880_tnrdmd
+ *tnr_dmd, int *snr,
+ int *snr_main, int *snr_sub)
+{
+ u16 reg_value = 0;
+ u32 reg_value_sum = 0;
+ int ret;
+
+ if (!tnr_dmd || !snr || !snr_main || !snr_sub)
+ return -EINVAL;
+
+ *snr = -1000 * 1000;
+ *snr_main = -1000 * 1000;
+ *snr_sub = -1000 * 1000;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return -EINVAL;
+
+ ret = dvbt2_read_snr_reg(tnr_dmd, &reg_value);
+ if (!ret) {
+ ret = dvbt2_calc_snr(tnr_dmd, reg_value, snr_main);
+ if (ret)
+ reg_value = 0;
+ } else if (ret == -EAGAIN) {
+ reg_value = 0;
+ } else {
+ return ret;
+ }
+
+ reg_value_sum += reg_value;
+
+ ret = dvbt2_read_snr_reg(tnr_dmd->diver_sub, &reg_value);
+ if (!ret) {
+ ret = dvbt2_calc_snr(tnr_dmd->diver_sub, reg_value, snr_sub);
+ if (ret)
+ reg_value = 0;
+ } else if (ret == -EAGAIN) {
+ reg_value = 0;
+ } else {
+ return ret;
+ }
+
+ reg_value_sum += reg_value;
+
+ return dvbt2_calc_snr(tnr_dmd, reg_value_sum, snr);
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_packet_error_number(struct
+ cxd2880_tnrdmd
+ *tnr_dmd,
+ u32 *pen)
+{
+ int ret;
+ u8 data[3];
+
+ if (!tnr_dmd || !pen)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0b);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x39, data, sizeof(data));
+ if (ret)
+ return ret;
+
+ if (!(data[0] & 0x01))
+ return -EAGAIN;
+
+ *pen = ((data[1] << 8) | data[2]);
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_sampling_offset(struct cxd2880_tnrdmd
+ *tnr_dmd, int *ppm)
+{
+ u8 ctl_val_reg[5];
+ u8 nominal_rate_reg[5];
+ u32 trl_ctl_val = 0;
+ u32 trcg_nominal_rate = 0;
+ int num;
+ int den;
+ int ret;
+ u8 sync_state = 0;
+ u8 ts_lock = 0;
+ u8 unlock_detected = 0;
+ s8 diff_upper = 0;
+
+ if (!tnr_dmd || !ppm)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return -EINVAL;
+
+ ret = slvt_freeze_reg(tnr_dmd);
+ if (ret)
+ return ret;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+ &ts_lock,
+ &unlock_detected);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (sync_state != 6) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return -EAGAIN;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0b);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x34, ctl_val_reg,
+ sizeof(ctl_val_reg));
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x04);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x10, nominal_rate_reg,
+ sizeof(nominal_rate_reg));
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ diff_upper =
+ (ctl_val_reg[0] & 0x7f) - (nominal_rate_reg[0] & 0x7f);
+
+ if (diff_upper < -1 || diff_upper > 1)
+ return -EAGAIN;
+
+ trl_ctl_val = ctl_val_reg[1] << 24;
+ trl_ctl_val |= ctl_val_reg[2] << 16;
+ trl_ctl_val |= ctl_val_reg[3] << 8;
+ trl_ctl_val |= ctl_val_reg[4];
+
+ trcg_nominal_rate = nominal_rate_reg[1] << 24;
+ trcg_nominal_rate |= nominal_rate_reg[2] << 16;
+ trcg_nominal_rate |= nominal_rate_reg[3] << 8;
+ trcg_nominal_rate |= nominal_rate_reg[4];
+
+ trl_ctl_val >>= 1;
+ trcg_nominal_rate >>= 1;
+
+ if (diff_upper == 1)
+ num =
+ (int)((trl_ctl_val + 0x80000000u) -
+ trcg_nominal_rate);
+ else if (diff_upper == -1)
+ num =
+ -(int)((trcg_nominal_rate + 0x80000000u) -
+ trl_ctl_val);
+ else
+ num = (int)(trl_ctl_val - trcg_nominal_rate);
+
+ den = (nominal_rate_reg[0] & 0x7f) << 24;
+ den |= nominal_rate_reg[1] << 16;
+ den |= nominal_rate_reg[2] << 8;
+ den |= nominal_rate_reg[3];
+ den = (den + (390625 / 2)) / 390625;
+
+ den >>= 1;
+
+ if (num >= 0)
+ *ppm = (num + (den / 2)) / den;
+ else
+ *ppm = (num - (den / 2)) / den;
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_sampling_offset_sub(struct
+ cxd2880_tnrdmd
+ *tnr_dmd,
+ int *ppm)
+{
+ if (!tnr_dmd || !ppm)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return -EINVAL;
+
+ return cxd2880_tnrdmd_dvbt2_mon_sampling_offset(tnr_dmd->diver_sub,
+ ppm);
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_qam(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dvbt2_plp_btype type,
+ enum cxd2880_dvbt2_plp_constell *qam)
+{
+ u8 data;
+ u8 l1_post_ok = 0;
+ int ret;
+
+ if (!tnr_dmd || !qam)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return -EINVAL;
+
+ ret = slvt_freeze_reg(tnr_dmd);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0b);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x86, &l1_post_ok, 1);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (!(l1_post_ok & 0x01)) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return -EAGAIN;
+ }
+
+ if (type == CXD2880_DVBT2_PLP_COMMON) {
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xb6, &data, 1);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (data == 0) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return -EAGAIN;
+ }
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xb1, &data, 1);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+ } else {
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x9e, &data, 1);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ *qam = (enum cxd2880_dvbt2_plp_constell)(data & 0x07);
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_code_rate(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dvbt2_plp_btype
+ type,
+ enum
+ cxd2880_dvbt2_plp_code_rate
+ *code_rate)
+{
+ u8 data;
+ u8 l1_post_ok = 0;
+ int ret;
+
+ if (!tnr_dmd || !code_rate)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return -EINVAL;
+
+ ret = slvt_freeze_reg(tnr_dmd);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0b);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x86, &l1_post_ok, 1);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (!(l1_post_ok & 0x01)) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return -EAGAIN;
+ }
+
+ if (type == CXD2880_DVBT2_PLP_COMMON) {
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xb6, &data, 1);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (data == 0) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return -EAGAIN;
+ }
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xb0, &data, 1);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+ } else {
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x9d, &data, 1);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ *code_rate = (enum cxd2880_dvbt2_plp_code_rate)(data & 0x07);
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_profile(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dvbt2_profile
+ *profile)
+{
+ u8 data;
+ int ret;
+
+ if (!tnr_dmd || !profile)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0b);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x22, &data, sizeof(data));
+ if (ret)
+ return ret;
+
+ if (data & 0x02) {
+ if (data & 0x01)
+ *profile = CXD2880_DVBT2_PROFILE_LITE;
+ else
+ *profile = CXD2880_DVBT2_PROFILE_BASE;
+ } else {
+ ret = -EAGAIN;
+ if (tnr_dmd->diver_mode ==
+ CXD2880_TNRDMD_DIVERMODE_MAIN)
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_profile(tnr_dmd->diver_sub,
+ profile);
+
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dvbt2_calc_ssi(struct cxd2880_tnrdmd *tnr_dmd,
+ int rf_lvl, u8 *ssi)
+{
+ enum cxd2880_dvbt2_plp_constell qam;
+ enum cxd2880_dvbt2_plp_code_rate code_rate;
+ int prel;
+ int temp_ssi = 0;
+ int ret;
+
+ if (!tnr_dmd || !ssi)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_qam(tnr_dmd, CXD2880_DVBT2_PLP_DATA, &qam);
+ if (ret)
+ return ret;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_code_rate(tnr_dmd, CXD2880_DVBT2_PLP_DATA,
+ &code_rate);
+ if (ret)
+ return ret;
+
+ if (code_rate > CXD2880_DVBT2_R2_5 || qam > CXD2880_DVBT2_QAM256)
+ return -EINVAL;
+
+ prel = rf_lvl - ref_dbm_1000[qam][code_rate];
+
+ if (prel < -15000)
+ temp_ssi = 0;
+ else if (prel < 0)
+ temp_ssi = ((2 * (prel + 15000)) + 1500) / 3000;
+ else if (prel < 20000)
+ temp_ssi = (((4 * prel) + 500) / 1000) + 10;
+ else if (prel < 35000)
+ temp_ssi = (((2 * (prel - 20000)) + 1500) / 3000) + 90;
+ else
+ temp_ssi = 100;
+
+ *ssi = (temp_ssi > 100) ? 100 : (u8)temp_ssi;
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_ssi(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 *ssi)
+{
+ int rf_lvl = 0;
+ int ret;
+
+ if (!tnr_dmd || !ssi)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return -EINVAL;
+
+ ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd, &rf_lvl);
+ if (ret)
+ return ret;
+
+ return dvbt2_calc_ssi(tnr_dmd, rf_lvl, ssi);
+}
+
+int cxd2880_tnrdmd_dvbt2_mon_ssi_sub(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 *ssi)
+{
+ int rf_lvl = 0;
+ int ret;
+
+ if (!tnr_dmd || !ssi)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return -EINVAL;
+
+ ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd->diver_sub, &rf_lvl);
+ if (ret)
+ return ret;
+
+ return dvbt2_calc_ssi(tnr_dmd, rf_lvl, ssi);
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h
new file mode 100644
index 000000000000..5b7adaceff22
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h
@@ -0,0 +1,135 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_tnrdmd_dvbt2_mon.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T2 monitor interface
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_TNRDMD_DVBT2_MON_H
+#define CXD2880_TNRDMD_DVBT2_MON_H
+
+#include "cxd2880_tnrdmd.h"
+#include "cxd2880_dvbt2.h"
+
+int cxd2880_tnrdmd_dvbt2_mon_sync_stat(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 *sync_stat,
+ u8 *ts_lock_stat,
+ u8 *unlock_detected);
+
+int cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ u8 *sync_stat,
+ u8 *unlock_detected);
+
+int cxd2880_tnrdmd_dvbt2_mon_carrier_offset(struct cxd2880_tnrdmd
+ *tnr_dmd, int *offset);
+
+int cxd2880_tnrdmd_dvbt2_mon_carrier_offset_sub(struct
+ cxd2880_tnrdmd
+ *tnr_dmd,
+ int *offset);
+
+int cxd2880_tnrdmd_dvbt2_mon_l1_pre(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt2_l1pre
+ *l1_pre);
+
+int cxd2880_tnrdmd_dvbt2_mon_version(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dvbt2_version
+ *ver);
+
+int cxd2880_tnrdmd_dvbt2_mon_ofdm(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt2_ofdm *ofdm);
+
+int cxd2880_tnrdmd_dvbt2_mon_data_plps(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 *plp_ids,
+ u8 *num_plps);
+
+int cxd2880_tnrdmd_dvbt2_mon_active_plp(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_dvbt2_plp_btype
+ type,
+ struct cxd2880_dvbt2_plp
+ *plp_info);
+
+int cxd2880_tnrdmd_dvbt2_mon_data_plp_error(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ u8 *plp_error);
+
+int cxd2880_tnrdmd_dvbt2_mon_l1_change(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 *l1_change);
+
+int cxd2880_tnrdmd_dvbt2_mon_l1_post(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ struct cxd2880_dvbt2_l1post
+ *l1_post);
+
+int cxd2880_tnrdmd_dvbt2_mon_bbheader(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dvbt2_plp_btype
+ type,
+ struct cxd2880_dvbt2_bbheader
+ *bbheader);
+
+int cxd2880_tnrdmd_dvbt2_mon_in_bandb_ts_rate(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_dvbt2_plp_btype
+ type,
+ u32 *ts_rate_bps);
+
+int cxd2880_tnrdmd_dvbt2_mon_spectrum_sense(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_tnrdmd_spectrum_sense
+ *sense);
+
+int cxd2880_tnrdmd_dvbt2_mon_snr(struct cxd2880_tnrdmd *tnr_dmd,
+ int *snr);
+
+int cxd2880_tnrdmd_dvbt2_mon_snr_diver(struct cxd2880_tnrdmd
+ *tnr_dmd, int *snr,
+ int *snr_main,
+ int *snr_sub);
+
+int cxd2880_tnrdmd_dvbt2_mon_packet_error_number(struct
+ cxd2880_tnrdmd
+ *tnr_dmd,
+ u32 *pen);
+
+int cxd2880_tnrdmd_dvbt2_mon_sampling_offset(struct cxd2880_tnrdmd
+ *tnr_dmd, int *ppm);
+
+int cxd2880_tnrdmd_dvbt2_mon_sampling_offset_sub(struct
+ cxd2880_tnrdmd
+ *tnr_dmd,
+ int *ppm);
+
+int cxd2880_tnrdmd_dvbt2_mon_qam(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dvbt2_plp_btype type,
+ enum cxd2880_dvbt2_plp_constell
+ *qam);
+
+int cxd2880_tnrdmd_dvbt2_mon_code_rate(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dvbt2_plp_btype
+ type,
+ enum
+ cxd2880_dvbt2_plp_code_rate
+ *code_rate);
+
+int cxd2880_tnrdmd_dvbt2_mon_profile(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dvbt2_profile
+ *profile);
+
+int cxd2880_tnrdmd_dvbt2_mon_ssi(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 *ssi);
+
+int cxd2880_tnrdmd_dvbt2_mon_ssi_sub(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 *ssi);
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c
new file mode 100644
index 000000000000..fedc3b4a2fa0
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c
@@ -0,0 +1,775 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_tnrdmd_dvbt_mon.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T monitor functions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#include "cxd2880_tnrdmd_mon.h"
+#include "cxd2880_tnrdmd_dvbt.h"
+#include "cxd2880_tnrdmd_dvbt_mon.h"
+
+#include <media/dvb_math.h>
+
+static const int ref_dbm_1000[3][5] = {
+ {-93000, -91000, -90000, -89000, -88000},
+ {-87000, -85000, -84000, -83000, -82000},
+ {-82000, -80000, -78000, -77000, -76000},
+};
+
+static int is_tps_locked(struct cxd2880_tnrdmd *tnr_dmd);
+
+int cxd2880_tnrdmd_dvbt_mon_sync_stat(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 *sync_stat,
+ u8 *ts_lock_stat,
+ u8 *unlock_detected)
+{
+ u8 rdata = 0x00;
+ int ret;
+
+ if (!tnr_dmd || !sync_stat || !ts_lock_stat || !unlock_detected)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0d);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x10, &rdata, 1);
+ if (ret)
+ return ret;
+
+ *unlock_detected = (rdata & 0x10) ? 1 : 0;
+ *sync_stat = rdata & 0x07;
+ *ts_lock_stat = (rdata & 0x20) ? 1 : 0;
+
+ if (*sync_stat == 0x07)
+ return -EAGAIN;
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_dvbt_mon_sync_stat_sub(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 *sync_stat,
+ u8 *unlock_detected)
+{
+ u8 ts_lock_stat = 0;
+
+ if (!tnr_dmd || !sync_stat || !unlock_detected)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return -EINVAL;
+
+ return cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd->diver_sub,
+ sync_stat,
+ &ts_lock_stat,
+ unlock_detected);
+}
+
+int cxd2880_tnrdmd_dvbt_mon_mode_guard(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dvbt_mode
+ *mode,
+ enum cxd2880_dvbt_guard
+ *guard)
+{
+ u8 rdata = 0x00;
+ int ret;
+
+ if (!tnr_dmd || !mode || !guard)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+ return -EINVAL;
+
+ ret = slvt_freeze_reg(tnr_dmd);
+ if (ret)
+ return ret;
+
+ ret = is_tps_locked(tnr_dmd);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+ ret =
+ cxd2880_tnrdmd_dvbt_mon_mode_guard(tnr_dmd->diver_sub,
+ mode, guard);
+
+ return ret;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0d);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x1b, &rdata, 1);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ *mode = (enum cxd2880_dvbt_mode)((rdata >> 2) & 0x03);
+ *guard = (enum cxd2880_dvbt_guard)(rdata & 0x03);
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_dvbt_mon_carrier_offset(struct cxd2880_tnrdmd
+ *tnr_dmd, int *offset)
+{
+ u8 rdata[4];
+ u32 ctl_val = 0;
+ int ret;
+
+ if (!tnr_dmd || !offset)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+ return -EINVAL;
+
+ ret = slvt_freeze_reg(tnr_dmd);
+ if (ret)
+ return ret;
+
+ ret = is_tps_locked(tnr_dmd);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0d);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x1d, rdata, 4);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ ctl_val =
+ ((rdata[0] & 0x1f) << 24) | (rdata[1] << 16) | (rdata[2] << 8) |
+ (rdata[3]);
+ *offset = cxd2880_convert2s_complement(ctl_val, 29);
+ *offset = -1 * ((*offset) * tnr_dmd->bandwidth / 235);
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_dvbt_mon_carrier_offset_sub(struct
+ cxd2880_tnrdmd
+ *tnr_dmd,
+ int *offset)
+{
+ if (!tnr_dmd || !offset)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return -EINVAL;
+
+ return cxd2880_tnrdmd_dvbt_mon_carrier_offset(tnr_dmd->diver_sub,
+ offset);
+}
+
+int cxd2880_tnrdmd_dvbt_mon_tps_info(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ struct cxd2880_dvbt_tpsinfo
+ *info)
+{
+ u8 rdata[7];
+ u8 cell_id_ok = 0;
+ int ret;
+
+ if (!tnr_dmd || !info)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+ return -EINVAL;
+
+ ret = slvt_freeze_reg(tnr_dmd);
+ if (ret)
+ return ret;
+
+ ret = is_tps_locked(tnr_dmd);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+ ret =
+ cxd2880_tnrdmd_dvbt_mon_tps_info(tnr_dmd->diver_sub,
+ info);
+
+ return ret;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0d);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x29, rdata, 7);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x11);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xd5, &cell_id_ok, 1);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ info->constellation =
+ (enum cxd2880_dvbt_constellation)((rdata[0] >> 6) & 0x03);
+ info->hierarchy = (enum cxd2880_dvbt_hierarchy)((rdata[0] >> 3) & 0x07);
+ info->rate_hp = (enum cxd2880_dvbt_coderate)(rdata[0] & 0x07);
+ info->rate_lp = (enum cxd2880_dvbt_coderate)((rdata[1] >> 5) & 0x07);
+ info->guard = (enum cxd2880_dvbt_guard)((rdata[1] >> 3) & 0x03);
+ info->mode = (enum cxd2880_dvbt_mode)((rdata[1] >> 1) & 0x03);
+ info->fnum = (rdata[2] >> 6) & 0x03;
+ info->length_indicator = rdata[2] & 0x3f;
+ info->cell_id = (rdata[3] << 8) | rdata[4];
+ info->reserved_even = rdata[5] & 0x3f;
+ info->reserved_odd = rdata[6] & 0x3f;
+
+ info->cell_id_ok = cell_id_ok & 0x01;
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_dvbt_mon_packet_error_number(struct
+ cxd2880_tnrdmd
+ *tnr_dmd,
+ u32 *pen)
+{
+ u8 rdata[3];
+ int ret;
+
+ if (!tnr_dmd || !pen)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0d);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x26, rdata, 3);
+ if (ret)
+ return ret;
+
+ if (!(rdata[0] & 0x01))
+ return -EAGAIN;
+
+ *pen = (rdata[1] << 8) | rdata[2];
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_dvbt_mon_spectrum_sense(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_tnrdmd_spectrum_sense
+ *sense)
+{
+ u8 data = 0;
+ int ret;
+
+ if (!tnr_dmd || !sense)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+ return -EINVAL;
+
+ ret = slvt_freeze_reg(tnr_dmd);
+ if (ret)
+ return ret;
+
+ ret = is_tps_locked(tnr_dmd);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+ ret = cxd2880_tnrdmd_dvbt_mon_spectrum_sense(tnr_dmd->diver_sub,
+ sense);
+
+ return ret;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0d);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x1c, &data, sizeof(data));
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ *sense =
+ (data & 0x01) ? CXD2880_TNRDMD_SPECTRUM_INV :
+ CXD2880_TNRDMD_SPECTRUM_NORMAL;
+
+ return ret;
+}
+
+static int dvbt_read_snr_reg(struct cxd2880_tnrdmd *tnr_dmd,
+ u16 *reg_value)
+{
+ u8 rdata[2];
+ int ret;
+
+ if (!tnr_dmd || !reg_value)
+ return -EINVAL;
+
+ ret = slvt_freeze_reg(tnr_dmd);
+ if (ret)
+ return ret;
+
+ ret = is_tps_locked(tnr_dmd);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0d);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x13, rdata, 2);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ *reg_value = (rdata[0] << 8) | rdata[1];
+
+ return ret;
+}
+
+static int dvbt_calc_snr(struct cxd2880_tnrdmd *tnr_dmd,
+ u32 reg_value, int *snr)
+{
+ if (!tnr_dmd || !snr)
+ return -EINVAL;
+
+ if (reg_value == 0)
+ return -EAGAIN;
+
+ if (reg_value > 4996)
+ reg_value = 4996;
+
+ *snr = intlog10(reg_value) - intlog10(5350 - reg_value);
+ *snr = (*snr + 839) / 1678 + 28500;
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_dvbt_mon_snr(struct cxd2880_tnrdmd *tnr_dmd,
+ int *snr)
+{
+ u16 reg_value = 0;
+ int ret;
+
+ if (!tnr_dmd || !snr)
+ return -EINVAL;
+
+ *snr = -1000 * 1000;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+ ret = dvbt_read_snr_reg(tnr_dmd, &reg_value);
+ if (ret)
+ return ret;
+
+ ret = dvbt_calc_snr(tnr_dmd, reg_value, snr);
+ } else {
+ int snr_main = 0;
+ int snr_sub = 0;
+
+ ret =
+ cxd2880_tnrdmd_dvbt_mon_snr_diver(tnr_dmd, snr, &snr_main,
+ &snr_sub);
+ }
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_dvbt_mon_snr_diver(struct cxd2880_tnrdmd
+ *tnr_dmd, int *snr,
+ int *snr_main, int *snr_sub)
+{
+ u16 reg_value = 0;
+ u32 reg_value_sum = 0;
+ int ret;
+
+ if (!tnr_dmd || !snr || !snr_main || !snr_sub)
+ return -EINVAL;
+
+ *snr = -1000 * 1000;
+ *snr_main = -1000 * 1000;
+ *snr_sub = -1000 * 1000;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+ return -EINVAL;
+
+ ret = dvbt_read_snr_reg(tnr_dmd, &reg_value);
+ if (!ret) {
+ ret = dvbt_calc_snr(tnr_dmd, reg_value, snr_main);
+ if (ret)
+ reg_value = 0;
+ } else if (ret == -EAGAIN) {
+ reg_value = 0;
+ } else {
+ return ret;
+ }
+
+ reg_value_sum += reg_value;
+
+ ret = dvbt_read_snr_reg(tnr_dmd->diver_sub, &reg_value);
+ if (!ret) {
+ ret = dvbt_calc_snr(tnr_dmd->diver_sub, reg_value, snr_sub);
+ if (ret)
+ reg_value = 0;
+ } else if (ret == -EAGAIN) {
+ reg_value = 0;
+ } else {
+ return ret;
+ }
+
+ reg_value_sum += reg_value;
+
+ return dvbt_calc_snr(tnr_dmd, reg_value_sum, snr);
+}
+
+int cxd2880_tnrdmd_dvbt_mon_sampling_offset(struct cxd2880_tnrdmd
+ *tnr_dmd, int *ppm)
+{
+ u8 ctl_val_reg[5];
+ u8 nominal_rate_reg[5];
+ u32 trl_ctl_val = 0;
+ u32 trcg_nominal_rate = 0;
+ int num;
+ int den;
+ s8 diff_upper = 0;
+ int ret;
+
+ if (!tnr_dmd || !ppm)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+ return -EINVAL;
+
+ ret = slvt_freeze_reg(tnr_dmd);
+ if (ret)
+ return ret;
+
+ ret = is_tps_locked(tnr_dmd);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0d);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x21, ctl_val_reg,
+ sizeof(ctl_val_reg));
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x04);
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x60, nominal_rate_reg,
+ sizeof(nominal_rate_reg));
+ if (ret) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ diff_upper =
+ (ctl_val_reg[0] & 0x7f) - (nominal_rate_reg[0] & 0x7f);
+
+ if (diff_upper < -1 || diff_upper > 1)
+ return -EAGAIN;
+
+ trl_ctl_val = ctl_val_reg[1] << 24;
+ trl_ctl_val |= ctl_val_reg[2] << 16;
+ trl_ctl_val |= ctl_val_reg[3] << 8;
+ trl_ctl_val |= ctl_val_reg[4];
+
+ trcg_nominal_rate = nominal_rate_reg[1] << 24;
+ trcg_nominal_rate |= nominal_rate_reg[2] << 16;
+ trcg_nominal_rate |= nominal_rate_reg[3] << 8;
+ trcg_nominal_rate |= nominal_rate_reg[4];
+
+ trl_ctl_val >>= 1;
+ trcg_nominal_rate >>= 1;
+
+ if (diff_upper == 1)
+ num =
+ (int)((trl_ctl_val + 0x80000000u) -
+ trcg_nominal_rate);
+ else if (diff_upper == -1)
+ num =
+ -(int)((trcg_nominal_rate + 0x80000000u) -
+ trl_ctl_val);
+ else
+ num = (int)(trl_ctl_val - trcg_nominal_rate);
+
+ den = (nominal_rate_reg[0] & 0x7f) << 24;
+ den |= nominal_rate_reg[1] << 16;
+ den |= nominal_rate_reg[2] << 8;
+ den |= nominal_rate_reg[3];
+ den = (den + (390625 / 2)) / 390625;
+
+ den >>= 1;
+
+ if (num >= 0)
+ *ppm = (num + (den / 2)) / den;
+ else
+ *ppm = (num - (den / 2)) / den;
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_dvbt_mon_sampling_offset_sub(struct
+ cxd2880_tnrdmd
+ *tnr_dmd, int *ppm)
+{
+ if (!tnr_dmd || !ppm)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return -EINVAL;
+
+ return cxd2880_tnrdmd_dvbt_mon_sampling_offset(tnr_dmd->diver_sub, ppm);
+}
+
+static int dvbt_calc_ssi(struct cxd2880_tnrdmd *tnr_dmd,
+ int rf_lvl, u8 *ssi)
+{
+ struct cxd2880_dvbt_tpsinfo tps;
+ int prel;
+ int temp_ssi = 0;
+ int ret;
+
+ if (!tnr_dmd || !ssi)
+ return -EINVAL;
+
+ ret = cxd2880_tnrdmd_dvbt_mon_tps_info(tnr_dmd, &tps);
+ if (ret)
+ return ret;
+
+ if (tps.constellation >= CXD2880_DVBT_CONSTELLATION_RESERVED_3 ||
+ tps.rate_hp >= CXD2880_DVBT_CODERATE_RESERVED_5)
+ return -EINVAL;
+
+ prel = rf_lvl - ref_dbm_1000[tps.constellation][tps.rate_hp];
+
+ if (prel < -15000)
+ temp_ssi = 0;
+ else if (prel < 0)
+ temp_ssi = ((2 * (prel + 15000)) + 1500) / 3000;
+ else if (prel < 20000)
+ temp_ssi = (((4 * prel) + 500) / 1000) + 10;
+ else if (prel < 35000)
+ temp_ssi = (((2 * (prel - 20000)) + 1500) / 3000) + 90;
+ else
+ temp_ssi = 100;
+
+ *ssi = (temp_ssi > 100) ? 100 : (u8)temp_ssi;
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_dvbt_mon_ssi(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 *ssi)
+{
+ int rf_lvl = 0;
+ int ret;
+
+ if (!tnr_dmd || !ssi)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+ return -EINVAL;
+
+ ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd, &rf_lvl);
+ if (ret)
+ return ret;
+
+ return dvbt_calc_ssi(tnr_dmd, rf_lvl, ssi);
+}
+
+int cxd2880_tnrdmd_dvbt_mon_ssi_sub(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 *ssi)
+{
+ int rf_lvl = 0;
+ int ret;
+
+ if (!tnr_dmd || !ssi)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+ return -EINVAL;
+
+ ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd->diver_sub, &rf_lvl);
+ if (ret)
+ return ret;
+
+ return dvbt_calc_ssi(tnr_dmd, rf_lvl, ssi);
+}
+
+static int is_tps_locked(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ u8 sync = 0;
+ u8 tslock = 0;
+ u8 early_unlock = 0;
+ int ret;
+
+ if (!tnr_dmd)
+ return -EINVAL;
+
+ ret =
+ cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd, &sync, &tslock,
+ &early_unlock);
+ if (ret)
+ return ret;
+
+ if (sync != 6)
+ return -EAGAIN;
+
+ return 0;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h
new file mode 100644
index 000000000000..f4c31725fa48
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_tnrdmd_dvbt_mon.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T monitor interface
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_TNRDMD_DVBT_MON_H
+#define CXD2880_TNRDMD_DVBT_MON_H
+
+#include "cxd2880_tnrdmd.h"
+#include "cxd2880_dvbt.h"
+
+int cxd2880_tnrdmd_dvbt_mon_sync_stat(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 *sync_stat,
+ u8 *ts_lock_stat,
+ u8 *unlock_detected);
+
+int cxd2880_tnrdmd_dvbt_mon_sync_stat_sub(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 *sync_stat,
+ u8 *unlock_detected);
+
+int cxd2880_tnrdmd_dvbt_mon_mode_guard(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dvbt_mode
+ *mode,
+ enum cxd2880_dvbt_guard
+ *guard);
+
+int cxd2880_tnrdmd_dvbt_mon_carrier_offset(struct cxd2880_tnrdmd
+ *tnr_dmd, int *offset);
+
+int cxd2880_tnrdmd_dvbt_mon_carrier_offset_sub(struct
+ cxd2880_tnrdmd
+ *tnr_dmd,
+ int *offset);
+
+int cxd2880_tnrdmd_dvbt_mon_tps_info(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ struct cxd2880_dvbt_tpsinfo
+ *info);
+
+int cxd2880_tnrdmd_dvbt_mon_packet_error_number(struct
+ cxd2880_tnrdmd
+ *tnr_dmd,
+ u32 *pen);
+
+int cxd2880_tnrdmd_dvbt_mon_spectrum_sense(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_tnrdmd_spectrum_sense
+ *sense);
+
+int cxd2880_tnrdmd_dvbt_mon_snr(struct cxd2880_tnrdmd *tnr_dmd,
+ int *snr);
+
+int cxd2880_tnrdmd_dvbt_mon_snr_diver(struct cxd2880_tnrdmd
+ *tnr_dmd, int *snr,
+ int *snr_main, int *snr_sub);
+
+int cxd2880_tnrdmd_dvbt_mon_sampling_offset(struct cxd2880_tnrdmd
+ *tnr_dmd, int *ppm);
+
+int cxd2880_tnrdmd_dvbt_mon_sampling_offset_sub(struct
+ cxd2880_tnrdmd
+ *tnr_dmd,
+ int *ppm);
+
+int cxd2880_tnrdmd_dvbt_mon_ssi(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 *ssi);
+
+int cxd2880_tnrdmd_dvbt_mon_ssi_sub(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 *ssi);
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
new file mode 100644
index 000000000000..3d8012c18e3f
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_tnrdmd_mon.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * common monitor functions
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#include "cxd2880_common.h"
+#include "cxd2880_tnrdmd_mon.h"
+
+static const u8 rf_lvl_seq[2] = {
+ 0x80, 0x00,
+};
+
+int cxd2880_tnrdmd_mon_rf_lvl(struct cxd2880_tnrdmd *tnr_dmd,
+ int *rf_lvl_db)
+{
+ u8 rdata[2];
+ int ret;
+
+ if (!tnr_dmd || !rf_lvl_db)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x10, 0x01);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x10);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x5b, rf_lvl_seq, 2);
+ if (ret)
+ return ret;
+
+ usleep_range(2000, 3000);
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x1a);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x15, rdata, 2);
+ if (ret)
+ return ret;
+
+ if (rdata[0] || rdata[1])
+ return -EINVAL;
+
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x11, rdata, 2);
+ if (ret)
+ return ret;
+
+ *rf_lvl_db =
+ cxd2880_convert2s_complement((rdata[0] << 3) |
+ ((rdata[1] & 0xe0) >> 5), 11);
+
+ *rf_lvl_db *= 125;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x00);
+ if (ret)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x10, 0x00);
+ if (ret)
+ return ret;
+
+ if (tnr_dmd->rf_lvl_cmpstn)
+ ret = tnr_dmd->rf_lvl_cmpstn(tnr_dmd, rf_lvl_db);
+
+ return ret;
+}
+
+int cxd2880_tnrdmd_mon_rf_lvl_sub(struct cxd2880_tnrdmd *tnr_dmd,
+ int *rf_lvl_db)
+{
+ if (!tnr_dmd || !rf_lvl_db)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return -EINVAL;
+
+ return cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd->diver_sub, rf_lvl_db);
+}
+
+int cxd2880_tnrdmd_mon_internal_cpu_status(struct cxd2880_tnrdmd
+ *tnr_dmd, u16 *status)
+{
+ u8 data[2] = { 0 };
+ int ret;
+
+ if (!tnr_dmd || !status)
+ return -EINVAL;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x1a);
+ if (ret)
+ return ret;
+ ret = tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS,
+ 0x15, data, 2);
+ if (ret)
+ return ret;
+
+ *status = (data[0] << 8) | data[1];
+
+ return 0;
+}
+
+int cxd2880_tnrdmd_mon_internal_cpu_status_sub(struct
+ cxd2880_tnrdmd
+ *tnr_dmd,
+ u16 *status)
+{
+ if (!tnr_dmd || !status)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return -EINVAL;
+
+ return cxd2880_tnrdmd_mon_internal_cpu_status(tnr_dmd->diver_sub,
+ status);
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
new file mode 100644
index 000000000000..570360925f87
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cxd2880_tnrdmd_mon.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * common monitor interface
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#ifndef CXD2880_TNRDMD_MON_H
+#define CXD2880_TNRDMD_MON_H
+
+#include "cxd2880_common.h"
+#include "cxd2880_tnrdmd.h"
+
+int cxd2880_tnrdmd_mon_rf_lvl(struct cxd2880_tnrdmd *tnr_dmd,
+ int *rf_lvl_db);
+
+int cxd2880_tnrdmd_mon_rf_lvl_sub(struct cxd2880_tnrdmd *tnr_dmd,
+ int *rf_lvl_db);
+
+int cxd2880_tnrdmd_mon_internal_cpu_status(struct cxd2880_tnrdmd
+ *tnr_dmd, u16 *status);
+
+int cxd2880_tnrdmd_mon_internal_cpu_status_sub(struct
+ cxd2880_tnrdmd
+ *tnr_dmd,
+ u16 *status);
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
new file mode 100644
index 000000000000..d474dc1b05da
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
@@ -0,0 +1,1947 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880_top.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
+
+#include <linux/spi/spi.h>
+
+#include <media/dvb_frontend.h>
+#include <media/dvb_math.h>
+
+#include "cxd2880.h"
+#include "cxd2880_tnrdmd_mon.h"
+#include "cxd2880_tnrdmd_dvbt2_mon.h"
+#include "cxd2880_tnrdmd_dvbt_mon.h"
+#include "cxd2880_integ.h"
+#include "cxd2880_tnrdmd_dvbt2.h"
+#include "cxd2880_tnrdmd_dvbt.h"
+#include "cxd2880_devio_spi.h"
+#include "cxd2880_spi_device.h"
+#include "cxd2880_tnrdmd_driver_version.h"
+
+struct cxd2880_priv {
+ struct cxd2880_tnrdmd tnrdmd;
+ struct spi_device *spi;
+ struct cxd2880_io regio;
+ struct cxd2880_spi_device spi_device;
+ struct cxd2880_spi cxd2880_spi;
+ struct cxd2880_dvbt_tune_param dvbt_tune_param;
+ struct cxd2880_dvbt2_tune_param dvbt2_tune_param;
+ struct mutex *spi_mutex; /* For SPI access exclusive control */
+ unsigned long pre_ber_update;
+ unsigned long pre_ber_interval;
+ unsigned long post_ber_update;
+ unsigned long post_ber_interval;
+ unsigned long ucblock_update;
+ unsigned long ucblock_interval;
+ enum fe_status s;
+};
+
+static int cxd2880_pre_bit_err_t(struct cxd2880_tnrdmd *tnrdmd,
+ u32 *pre_bit_err, u32 *pre_bit_count)
+{
+ u8 rdata[2];
+ int ret;
+
+ if (!tnrdmd || !pre_bit_err || !pre_bit_count)
+ return -EINVAL;
+
+ if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
+ return -EINVAL;
+
+ ret = slvt_freeze_reg(tnrdmd);
+ if (ret)
+ return ret;
+
+ ret = tnrdmd->io->write_reg(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x10);
+ if (ret) {
+ slvt_unfreeze_reg(tnrdmd);
+ return ret;
+ }
+
+ ret = tnrdmd->io->read_regs(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x39, rdata, 1);
+ if (ret) {
+ slvt_unfreeze_reg(tnrdmd);
+ return ret;
+ }
+
+ if ((rdata[0] & 0x01) == 0) {
+ slvt_unfreeze_reg(tnrdmd);
+ return -EAGAIN;
+ }
+
+ ret = tnrdmd->io->read_regs(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x22, rdata, 2);
+ if (ret) {
+ slvt_unfreeze_reg(tnrdmd);
+ return ret;
+ }
+
+ *pre_bit_err = (rdata[0] << 8) | rdata[1];
+
+ ret = tnrdmd->io->read_regs(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x6f, rdata, 1);
+ if (ret) {
+ slvt_unfreeze_reg(tnrdmd);
+ return ret;
+ }
+
+ slvt_unfreeze_reg(tnrdmd);
+
+ *pre_bit_count = ((rdata[0] & 0x07) == 0) ?
+ 256 : (0x1000 << (rdata[0] & 0x07));
+
+ return 0;
+}
+
+static int cxd2880_pre_bit_err_t2(struct cxd2880_tnrdmd *tnrdmd,
+ u32 *pre_bit_err,
+ u32 *pre_bit_count)
+{
+ u32 period_exp = 0;
+ u32 n_ldpc = 0;
+ u8 data[5];
+ int ret;
+
+ if (!tnrdmd || !pre_bit_err || !pre_bit_count)
+ return -EINVAL;
+
+ if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return -EINVAL;
+
+ ret = slvt_freeze_reg(tnrdmd);
+ if (ret)
+ return ret;
+
+ ret = tnrdmd->io->write_reg(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0b);
+ if (ret) {
+ slvt_unfreeze_reg(tnrdmd);
+ return ret;
+ }
+
+ ret = tnrdmd->io->read_regs(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x3c, data, sizeof(data));
+ if (ret) {
+ slvt_unfreeze_reg(tnrdmd);
+ return ret;
+ }
+
+ if (!(data[0] & 0x01)) {
+ slvt_unfreeze_reg(tnrdmd);
+ return -EAGAIN;
+ }
+ *pre_bit_err =
+ ((data[1] & 0x0f) << 24) | (data[2] << 16) | (data[3] << 8) | data[4];
+
+ ret = tnrdmd->io->read_regs(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xa0, data, 1);
+ if (ret) {
+ slvt_unfreeze_reg(tnrdmd);
+ return ret;
+ }
+
+ if (((enum cxd2880_dvbt2_plp_fec)(data[0] & 0x03)) ==
+ CXD2880_DVBT2_FEC_LDPC_16K)
+ n_ldpc = 16200;
+ else
+ n_ldpc = 64800;
+ slvt_unfreeze_reg(tnrdmd);
+
+ ret = tnrdmd->io->write_reg(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x20);
+ if (ret)
+ return ret;
+
+ ret = tnrdmd->io->read_regs(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x6f, data, 1);
+ if (ret)
+ return ret;
+
+ period_exp = data[0] & 0x0f;
+
+ *pre_bit_count = (1U << period_exp) * n_ldpc;
+
+ return 0;
+}
+
+static int cxd2880_post_bit_err_t(struct cxd2880_tnrdmd *tnrdmd,
+ u32 *post_bit_err,
+ u32 *post_bit_count)
+{
+ u8 rdata[3];
+ u32 bit_error = 0;
+ u32 period_exp = 0;
+ int ret;
+
+ if (!tnrdmd || !post_bit_err || !post_bit_count)
+ return -EINVAL;
+
+ if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
+ return -EINVAL;
+
+ ret = tnrdmd->io->write_reg(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0d);
+ if (ret)
+ return ret;
+
+ ret = tnrdmd->io->read_regs(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x15, rdata, 3);
+ if (ret)
+ return ret;
+
+ if ((rdata[0] & 0x40) == 0)
+ return -EAGAIN;
+
+ *post_bit_err = ((rdata[0] & 0x3f) << 16) | (rdata[1] << 8) | rdata[2];
+
+ ret = tnrdmd->io->write_reg(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x10);
+ if (ret)
+ return ret;
+
+ ret = tnrdmd->io->read_regs(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x60, rdata, 1);
+ if (ret)
+ return ret;
+
+ period_exp = (rdata[0] & 0x1f);
+
+ if (period_exp <= 11 && (bit_error > (1U << period_exp) * 204 * 8))
+ return -EAGAIN;
+
+ *post_bit_count = (1U << period_exp) * 204 * 8;
+
+ return 0;
+}
+
+static int cxd2880_post_bit_err_t2(struct cxd2880_tnrdmd *tnrdmd,
+ u32 *post_bit_err,
+ u32 *post_bit_count)
+{
+ u32 period_exp = 0;
+ u32 n_bch = 0;
+ u8 data[3];
+ enum cxd2880_dvbt2_plp_fec plp_fec_type =
+ CXD2880_DVBT2_FEC_LDPC_16K;
+ enum cxd2880_dvbt2_plp_code_rate plp_code_rate =
+ CXD2880_DVBT2_R1_2;
+ int ret;
+ static const u16 n_bch_bits_lookup[2][8] = {
+ {7200, 9720, 10800, 11880, 12600, 13320, 5400, 6480},
+ {32400, 38880, 43200, 48600, 51840, 54000, 21600, 25920}
+ };
+
+ if (!tnrdmd || !post_bit_err || !post_bit_count)
+ return -EINVAL;
+
+ if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return -EINVAL;
+
+ ret = slvt_freeze_reg(tnrdmd);
+ if (ret)
+ return ret;
+
+ ret = tnrdmd->io->write_reg(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0b);
+ if (ret) {
+ slvt_unfreeze_reg(tnrdmd);
+ return ret;
+ }
+
+ ret = tnrdmd->io->read_regs(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x15, data, 3);
+ if (ret) {
+ slvt_unfreeze_reg(tnrdmd);
+ return ret;
+ }
+
+ if (!(data[0] & 0x40)) {
+ slvt_unfreeze_reg(tnrdmd);
+ return -EAGAIN;
+ }
+
+ *post_bit_err =
+ ((data[0] & 0x3f) << 16) | (data[1] << 8) | data[2];
+
+ ret = tnrdmd->io->read_regs(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x9d, data, 1);
+ if (ret) {
+ slvt_unfreeze_reg(tnrdmd);
+ return ret;
+ }
+
+ plp_code_rate =
+ (enum cxd2880_dvbt2_plp_code_rate)(data[0] & 0x07);
+
+ ret = tnrdmd->io->read_regs(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xa0, data, 1);
+ if (ret) {
+ slvt_unfreeze_reg(tnrdmd);
+ return ret;
+ }
+
+ plp_fec_type = (enum cxd2880_dvbt2_plp_fec)(data[0] & 0x03);
+
+ slvt_unfreeze_reg(tnrdmd);
+
+ ret = tnrdmd->io->write_reg(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x20);
+ if (ret)
+ return ret;
+
+ ret = tnrdmd->io->read_regs(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x72, data, 1);
+ if (ret)
+ return ret;
+
+ period_exp = data[0] & 0x0f;
+
+ if (plp_fec_type > CXD2880_DVBT2_FEC_LDPC_64K ||
+ plp_code_rate > CXD2880_DVBT2_R2_5)
+ return -EAGAIN;
+
+ n_bch = n_bch_bits_lookup[plp_fec_type][plp_code_rate];
+
+ if (*post_bit_err > ((1U << period_exp) * n_bch))
+ return -EAGAIN;
+
+ *post_bit_count = (1U << period_exp) * n_bch;
+
+ return 0;
+}
+
+static int cxd2880_read_block_err_t(struct cxd2880_tnrdmd *tnrdmd,
+ u32 *block_err,
+ u32 *block_count)
+{
+ u8 rdata[3];
+ int ret;
+
+ if (!tnrdmd || !block_err || !block_count)
+ return -EINVAL;
+
+ if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
+ return -EINVAL;
+
+ ret = tnrdmd->io->write_reg(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0d);
+ if (ret)
+ return ret;
+
+ ret = tnrdmd->io->read_regs(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x18, rdata, 3);
+ if (ret)
+ return ret;
+
+ if ((rdata[0] & 0x01) == 0)
+ return -EAGAIN;
+
+ *block_err = (rdata[1] << 8) | rdata[2];
+
+ ret = tnrdmd->io->write_reg(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x10);
+ if (ret)
+ return ret;
+
+ ret = tnrdmd->io->read_regs(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x5c, rdata, 1);
+ if (ret)
+ return ret;
+
+ *block_count = 1U << (rdata[0] & 0x0f);
+
+ if ((*block_count == 0) || (*block_err > *block_count))
+ return -EAGAIN;
+
+ return 0;
+}
+
+static int cxd2880_read_block_err_t2(struct cxd2880_tnrdmd *tnrdmd,
+ u32 *block_err,
+ u32 *block_count)
+{
+ u8 rdata[3];
+ int ret;
+
+ if (!tnrdmd || !block_err || !block_count)
+ return -EINVAL;
+
+ if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+ if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return -EINVAL;
+
+ ret = tnrdmd->io->write_reg(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x0b);
+ if (ret)
+ return ret;
+
+ ret = tnrdmd->io->read_regs(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x18, rdata, 3);
+ if (ret)
+ return ret;
+
+ if ((rdata[0] & 0x01) == 0)
+ return -EAGAIN;
+
+ *block_err = (rdata[1] << 8) | rdata[2];
+
+ ret = tnrdmd->io->write_reg(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x24);
+ if (ret)
+ return ret;
+
+ ret = tnrdmd->io->read_regs(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0xdc, rdata, 1);
+ if (ret)
+ return ret;
+
+ *block_count = 1U << (rdata[0] & 0x0f);
+
+ if ((*block_count == 0) || (*block_err > *block_count))
+ return -EAGAIN;
+
+ return 0;
+}
+
+static void cxd2880_release(struct dvb_frontend *fe)
+{
+ struct cxd2880_priv *priv = NULL;
+
+ if (!fe) {
+ pr_err("invalid arg.\n");
+ return;
+ }
+ priv = fe->demodulator_priv;
+ kfree(priv);
+}
+
+static int cxd2880_init(struct dvb_frontend *fe)
+{
+ int ret;
+ struct cxd2880_priv *priv = NULL;
+ struct cxd2880_tnrdmd_create_param create_param;
+
+ if (!fe) {
+ pr_err("invalid arg.\n");
+ return -EINVAL;
+ }
+
+ priv = fe->demodulator_priv;
+
+ create_param.ts_output_if = CXD2880_TNRDMD_TSOUT_IF_SPI;
+ create_param.xtal_share_type = CXD2880_TNRDMD_XTAL_SHARE_NONE;
+ create_param.en_internal_ldo = 1;
+ create_param.xosc_cap = 18;
+ create_param.xosc_i = 8;
+ create_param.stationary_use = 1;
+
+ mutex_lock(priv->spi_mutex);
+ if (priv->tnrdmd.io != &priv->regio) {
+ ret = cxd2880_tnrdmd_create(&priv->tnrdmd,
+ &priv->regio, &create_param);
+ if (ret) {
+ mutex_unlock(priv->spi_mutex);
+ pr_info("cxd2880 tnrdmd create failed %d\n", ret);
+ return ret;
+ }
+ }
+ ret = cxd2880_integ_init(&priv->tnrdmd);
+ if (ret) {
+ mutex_unlock(priv->spi_mutex);
+ pr_err("cxd2880 integ init failed %d\n", ret);
+ return ret;
+ }
+ mutex_unlock(priv->spi_mutex);
+
+ pr_debug("OK.\n");
+
+ return ret;
+}
+
+static int cxd2880_sleep(struct dvb_frontend *fe)
+{
+ int ret;
+ struct cxd2880_priv *priv = NULL;
+
+ if (!fe) {
+ pr_err("invalid arg\n");
+ return -EINVAL;
+ }
+
+ priv = fe->demodulator_priv;
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_tnrdmd_sleep(&priv->tnrdmd);
+ mutex_unlock(priv->spi_mutex);
+
+ pr_debug("tnrdmd_sleep ret %d\n", ret);
+
+ return ret;
+}
+
+static int cxd2880_read_signal_strength(struct dvb_frontend *fe,
+ u16 *strength)
+{
+ int ret;
+ struct cxd2880_priv *priv = NULL;
+ struct dtv_frontend_properties *c = NULL;
+ int level = 0;
+
+ if (!fe || !strength) {
+ pr_err("invalid arg\n");
+ return -EINVAL;
+ }
+
+ priv = fe->demodulator_priv;
+ c = &fe->dtv_property_cache;
+
+ mutex_lock(priv->spi_mutex);
+ if (c->delivery_system == SYS_DVBT ||
+ c->delivery_system == SYS_DVBT2) {
+ ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &level);
+ } else {
+ pr_debug("invalid system\n");
+ mutex_unlock(priv->spi_mutex);
+ return -EINVAL;
+ }
+ mutex_unlock(priv->spi_mutex);
+
+ level /= 125;
+ /*
+ * level should be between -105dBm and -30dBm.
+ * E.g. they should be between:
+ * -105000/125 = -840 and -30000/125 = -240
+ */
+ level = clamp(level, -840, -240);
+ /* scale value to 0x0000-0xffff */
+ *strength = ((level + 840) * 0xffff) / (-240 + 840);
+
+ if (ret)
+ pr_debug("ret = %d\n", ret);
+
+ return ret;
+}
+
+static int cxd2880_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ int ret;
+ int snrvalue = 0;
+ struct cxd2880_priv *priv = NULL;
+ struct dtv_frontend_properties *c = NULL;
+
+ if (!fe || !snr) {
+ pr_err("invalid arg\n");
+ return -EINVAL;
+ }
+
+ priv = fe->demodulator_priv;
+ c = &fe->dtv_property_cache;
+
+ mutex_lock(priv->spi_mutex);
+ if (c->delivery_system == SYS_DVBT) {
+ ret = cxd2880_tnrdmd_dvbt_mon_snr(&priv->tnrdmd,
+ &snrvalue);
+ } else if (c->delivery_system == SYS_DVBT2) {
+ ret = cxd2880_tnrdmd_dvbt2_mon_snr(&priv->tnrdmd,
+ &snrvalue);
+ } else {
+ pr_err("invalid system\n");
+ mutex_unlock(priv->spi_mutex);
+ return -EINVAL;
+ }
+ mutex_unlock(priv->spi_mutex);
+
+ if (snrvalue < 0)
+ snrvalue = 0;
+ *snr = snrvalue;
+
+ if (ret)
+ pr_debug("ret = %d\n", ret);
+
+ return ret;
+}
+
+static int cxd2880_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ int ret;
+ struct cxd2880_priv *priv = NULL;
+ struct dtv_frontend_properties *c = NULL;
+
+ if (!fe || !ucblocks) {
+ pr_err("invalid arg\n");
+ return -EINVAL;
+ }
+
+ priv = fe->demodulator_priv;
+ c = &fe->dtv_property_cache;
+
+ mutex_lock(priv->spi_mutex);
+ if (c->delivery_system == SYS_DVBT) {
+ ret = cxd2880_tnrdmd_dvbt_mon_packet_error_number(&priv->tnrdmd,
+ ucblocks);
+ } else if (c->delivery_system == SYS_DVBT2) {
+ ret = cxd2880_tnrdmd_dvbt2_mon_packet_error_number(&priv->tnrdmd,
+ ucblocks);
+ } else {
+ pr_err("invalid system\n");
+ mutex_unlock(priv->spi_mutex);
+ return -EINVAL;
+ }
+ mutex_unlock(priv->spi_mutex);
+
+ if (ret)
+ pr_debug("ret = %d\n", ret);
+
+ return ret;
+}
+
+static int cxd2880_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ *ber = 0;
+
+ return 0;
+}
+
+static int cxd2880_set_ber_per_period_t(struct dvb_frontend *fe)
+{
+ int ret;
+ struct cxd2880_priv *priv;
+ struct cxd2880_dvbt_tpsinfo info;
+ enum cxd2880_dtv_bandwidth bw = CXD2880_DTV_BW_1_7_MHZ;
+ u32 pre_ber_rate = 0;
+ u32 post_ber_rate = 0;
+ u32 ucblock_rate = 0;
+ u32 mes_exp = 0;
+ static const int cr_table[5] = {31500, 42000, 47250, 52500, 55125};
+ static const int denominator_tbl[4] = {125664, 129472, 137088, 152320};
+
+ if (!fe) {
+ pr_err("invalid arg\n");
+ return -EINVAL;
+ }
+
+ priv = fe->demodulator_priv;
+ bw = priv->dvbt_tune_param.bandwidth;
+
+ ret = cxd2880_tnrdmd_dvbt_mon_tps_info(&priv->tnrdmd,
+ &info);
+ if (ret) {
+ pr_err("tps monitor error ret = %d\n", ret);
+ info.hierarchy = CXD2880_DVBT_HIERARCHY_NON;
+ info.constellation = CXD2880_DVBT_CONSTELLATION_QPSK;
+ info.guard = CXD2880_DVBT_GUARD_1_4;
+ info.rate_hp = CXD2880_DVBT_CODERATE_1_2;
+ info.rate_lp = CXD2880_DVBT_CODERATE_1_2;
+ }
+
+ if (info.hierarchy == CXD2880_DVBT_HIERARCHY_NON) {
+ pre_ber_rate = 63000000 * bw * (info.constellation * 2 + 2) /
+ denominator_tbl[info.guard];
+
+ post_ber_rate = 1000 * cr_table[info.rate_hp] * bw *
+ (info.constellation * 2 + 2) /
+ denominator_tbl[info.guard];
+
+ ucblock_rate = 875 * cr_table[info.rate_hp] * bw *
+ (info.constellation * 2 + 2) /
+ denominator_tbl[info.guard];
+ } else {
+ u8 data = 0;
+ struct cxd2880_tnrdmd *tnrdmd = &priv->tnrdmd;
+
+ ret = tnrdmd->io->write_reg(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x10);
+ if (!ret) {
+ ret = tnrdmd->io->read_regs(tnrdmd->io,
+ CXD2880_IO_TGT_DMD,
+ 0x67, &data, 1);
+ if (ret)
+ data = 0x00;
+ } else {
+ data = 0x00;
+ }
+
+ if (data & 0x01) { /* Low priority */
+ pre_ber_rate =
+ 63000000 * bw * (info.constellation * 2 + 2) /
+ denominator_tbl[info.guard];
+
+ post_ber_rate = 1000 * cr_table[info.rate_lp] * bw *
+ (info.constellation * 2 + 2) /
+ denominator_tbl[info.guard];
+
+ ucblock_rate = (1000 * 7 / 8) * cr_table[info.rate_lp] *
+ bw * (info.constellation * 2 + 2) /
+ denominator_tbl[info.guard];
+ } else { /* High priority */
+ pre_ber_rate =
+ 63000000 * bw * 2 / denominator_tbl[info.guard];
+
+ post_ber_rate = 1000 * cr_table[info.rate_hp] * bw * 2 /
+ denominator_tbl[info.guard];
+
+ ucblock_rate = (1000 * 7 / 8) * cr_table[info.rate_hp] *
+ bw * 2 / denominator_tbl[info.guard];
+ }
+ }
+
+ mes_exp = pre_ber_rate < 8192 ? 8 : intlog2(pre_ber_rate) >> 24;
+ priv->pre_ber_interval =
+ ((1U << mes_exp) * 1000 + (pre_ber_rate / 2)) /
+ pre_ber_rate;
+ cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
+ CXD2880_TNRDMD_CFG_DVBT_VBER_PERIOD,
+ mes_exp == 8 ? 0 : mes_exp - 12);
+
+ mes_exp = intlog2(post_ber_rate) >> 24;
+ priv->post_ber_interval =
+ ((1U << mes_exp) * 1000 + (post_ber_rate / 2)) /
+ post_ber_rate;
+ cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
+ CXD2880_TNRDMD_CFG_DVBT_BERN_PERIOD,
+ mes_exp);
+
+ mes_exp = intlog2(ucblock_rate) >> 24;
+ priv->ucblock_interval =
+ ((1U << mes_exp) * 1000 + (ucblock_rate / 2)) /
+ ucblock_rate;
+ cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
+ CXD2880_TNRDMD_CFG_DVBT_PER_MES,
+ mes_exp);
+
+ return 0;
+}
+
+static int cxd2880_set_ber_per_period_t2(struct dvb_frontend *fe)
+{
+ int ret;
+ struct cxd2880_priv *priv;
+ struct cxd2880_dvbt2_l1pre l1pre;
+ struct cxd2880_dvbt2_l1post l1post;
+ struct cxd2880_dvbt2_plp plp;
+ struct cxd2880_dvbt2_bbheader bbheader;
+ enum cxd2880_dtv_bandwidth bw = CXD2880_DTV_BW_1_7_MHZ;
+ u32 pre_ber_rate = 0;
+ u32 post_ber_rate = 0;
+ u32 ucblock_rate = 0;
+ u32 mes_exp = 0;
+ u32 term_a = 0;
+ u32 term_b = 0;
+ u32 denominator = 0;
+ static const u32 gi_tbl[7] = {32, 64, 128, 256, 8, 152, 76};
+ static const u8 n_tbl[6] = {8, 2, 4, 16, 1, 1};
+ static const u8 mode_tbl[6] = {2, 8, 4, 1, 16, 32};
+ static const u32 kbch_tbl[2][8] = {
+ {6952, 9472, 10552, 11632, 12352, 13072, 5152, 6232},
+ {32128, 38608, 42960, 48328, 51568, 53760, 0, 0}
+ };
+
+ if (!fe) {
+ pr_err("invalid arg\n");
+ return -EINVAL;
+ }
+
+ priv = fe->demodulator_priv;
+ bw = priv->dvbt2_tune_param.bandwidth;
+
+ ret = cxd2880_tnrdmd_dvbt2_mon_l1_pre(&priv->tnrdmd, &l1pre);
+ if (ret) {
+ pr_info("l1 pre error\n");
+ goto error_ber_setting;
+ }
+
+ ret = cxd2880_tnrdmd_dvbt2_mon_active_plp(&priv->tnrdmd,
+ CXD2880_DVBT2_PLP_DATA, &plp);
+ if (ret) {
+ pr_info("plp info error\n");
+ goto error_ber_setting;
+ }
+
+ ret = cxd2880_tnrdmd_dvbt2_mon_l1_post(&priv->tnrdmd, &l1post);
+ if (ret) {
+ pr_info("l1 post error\n");
+ goto error_ber_setting;
+ }
+
+ term_a =
+ (mode_tbl[l1pre.fft_mode] * (1024 + gi_tbl[l1pre.gi])) *
+ (l1pre.num_symbols + n_tbl[l1pre.fft_mode]) + 2048;
+
+ if (l1pre.mixed && l1post.fef_intvl) {
+ term_b = (l1post.fef_length + (l1post.fef_intvl / 2)) /
+ l1post.fef_intvl;
+ } else {
+ term_b = 0;
+ }
+
+ switch (bw) {
+ case CXD2880_DTV_BW_1_7_MHZ:
+ denominator = ((term_a + term_b) * 71 + (131 / 2)) / 131;
+ break;
+ case CXD2880_DTV_BW_5_MHZ:
+ denominator = ((term_a + term_b) * 7 + 20) / 40;
+ break;
+ case CXD2880_DTV_BW_6_MHZ:
+ denominator = ((term_a + term_b) * 7 + 24) / 48;
+ break;
+ case CXD2880_DTV_BW_7_MHZ:
+ denominator = ((term_a + term_b) + 4) / 8;
+ break;
+ case CXD2880_DTV_BW_8_MHZ:
+ default:
+ denominator = ((term_a + term_b) * 7 + 32) / 64;
+ break;
+ }
+
+ if (plp.til_type && plp.til_len) {
+ pre_ber_rate =
+ (plp.num_blocks_max * 1000000 + (denominator / 2)) /
+ denominator;
+ pre_ber_rate = (pre_ber_rate + (plp.til_len / 2)) /
+ plp.til_len;
+ } else {
+ pre_ber_rate =
+ (plp.num_blocks_max * 1000000 + (denominator / 2)) /
+ denominator;
+ }
+
+ post_ber_rate = pre_ber_rate;
+
+ mes_exp = intlog2(pre_ber_rate) >> 24;
+ priv->pre_ber_interval =
+ ((1U << mes_exp) * 1000 + (pre_ber_rate / 2)) /
+ pre_ber_rate;
+ cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
+ CXD2880_TNRDMD_CFG_DVBT2_LBER_MES,
+ mes_exp);
+
+ mes_exp = intlog2(post_ber_rate) >> 24;
+ priv->post_ber_interval =
+ ((1U << mes_exp) * 1000 + (post_ber_rate / 2)) /
+ post_ber_rate;
+ cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
+ CXD2880_TNRDMD_CFG_DVBT2_BBER_MES,
+ mes_exp);
+
+ ret = cxd2880_tnrdmd_dvbt2_mon_bbheader(&priv->tnrdmd,
+ CXD2880_DVBT2_PLP_DATA,
+ &bbheader);
+ if (ret) {
+ pr_info("bb header error\n");
+ goto error_ucblock_setting;
+ }
+
+ if (bbheader.plp_mode == CXD2880_DVBT2_PLP_MODE_NM) {
+ if (!bbheader.issy_indicator) {
+ ucblock_rate =
+ (pre_ber_rate * kbch_tbl[plp.fec][plp.plp_cr] +
+ 752) / 1504;
+ } else {
+ ucblock_rate =
+ (pre_ber_rate * kbch_tbl[plp.fec][plp.plp_cr] +
+ 764) / 1528;
+ }
+ } else if (bbheader.plp_mode == CXD2880_DVBT2_PLP_MODE_HEM) {
+ ucblock_rate =
+ (pre_ber_rate * kbch_tbl[plp.fec][plp.plp_cr] + 748) /
+ 1496;
+ } else {
+ pr_info("plp mode is not Normal or HEM\n");
+ goto error_ucblock_setting;
+ }
+
+ mes_exp = intlog2(ucblock_rate) >> 24;
+ priv->ucblock_interval =
+ ((1U << mes_exp) * 1000 + (ucblock_rate / 2)) /
+ ucblock_rate;
+ cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
+ CXD2880_TNRDMD_CFG_DVBT2_PER_MES,
+ mes_exp);
+
+ return 0;
+
+error_ber_setting:
+ priv->pre_ber_interval = 1000;
+ cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
+ CXD2880_TNRDMD_CFG_DVBT2_LBER_MES, 0);
+
+ priv->post_ber_interval = 1000;
+ cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
+ CXD2880_TNRDMD_CFG_DVBT2_BBER_MES, 0);
+
+error_ucblock_setting:
+ priv->ucblock_interval = 1000;
+ cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
+ CXD2880_TNRDMD_CFG_DVBT2_PER_MES, 8);
+
+ return 0;
+}
+
+static int cxd2880_dvbt_tune(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt_tune_param
+ *tune_param)
+{
+ int ret;
+
+ if (!tnr_dmd || !tune_param)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ atomic_set(&tnr_dmd->cancel, 0);
+
+ if (tune_param->bandwidth != CXD2880_DTV_BW_5_MHZ &&
+ tune_param->bandwidth != CXD2880_DTV_BW_6_MHZ &&
+ tune_param->bandwidth != CXD2880_DTV_BW_7_MHZ &&
+ tune_param->bandwidth != CXD2880_DTV_BW_8_MHZ) {
+ return -ENOTTY;
+ }
+
+ ret = cxd2880_tnrdmd_dvbt_tune1(tnr_dmd, tune_param);
+ if (ret)
+ return ret;
+
+ usleep_range(CXD2880_TNRDMD_WAIT_AGC_STABLE * 10000,
+ CXD2880_TNRDMD_WAIT_AGC_STABLE * 10000 + 1000);
+
+ return cxd2880_tnrdmd_dvbt_tune2(tnr_dmd, tune_param);
+}
+
+static int cxd2880_dvbt2_tune(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt2_tune_param
+ *tune_param)
+{
+ int ret;
+
+ if (!tnr_dmd || !tune_param)
+ return -EINVAL;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return -EINVAL;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP &&
+ tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return -EINVAL;
+
+ atomic_set(&tnr_dmd->cancel, 0);
+
+ if (tune_param->bandwidth != CXD2880_DTV_BW_1_7_MHZ &&
+ tune_param->bandwidth != CXD2880_DTV_BW_5_MHZ &&
+ tune_param->bandwidth != CXD2880_DTV_BW_6_MHZ &&
+ tune_param->bandwidth != CXD2880_DTV_BW_7_MHZ &&
+ tune_param->bandwidth != CXD2880_DTV_BW_8_MHZ) {
+ return -ENOTTY;
+ }
+
+ if (tune_param->profile != CXD2880_DVBT2_PROFILE_BASE &&
+ tune_param->profile != CXD2880_DVBT2_PROFILE_LITE)
+ return -EINVAL;
+
+ ret = cxd2880_tnrdmd_dvbt2_tune1(tnr_dmd, tune_param);
+ if (ret)
+ return ret;
+
+ usleep_range(CXD2880_TNRDMD_WAIT_AGC_STABLE * 10000,
+ CXD2880_TNRDMD_WAIT_AGC_STABLE * 10000 + 1000);
+
+ return cxd2880_tnrdmd_dvbt2_tune2(tnr_dmd, tune_param);
+}
+
+static int cxd2880_set_frontend(struct dvb_frontend *fe)
+{
+ int ret;
+ struct dtv_frontend_properties *c;
+ struct cxd2880_priv *priv;
+ enum cxd2880_dtv_bandwidth bw = CXD2880_DTV_BW_1_7_MHZ;
+
+ if (!fe) {
+ pr_err("invalid arg\n");
+ return -EINVAL;
+ }
+
+ priv = fe->demodulator_priv;
+ c = &fe->dtv_property_cache;
+
+ c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->pre_bit_error.stat[0].uvalue = 0;
+ c->pre_bit_error.len = 1;
+ c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->pre_bit_count.stat[0].uvalue = 0;
+ c->pre_bit_count.len = 1;
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_error.stat[0].uvalue = 0;
+ c->post_bit_error.len = 1;
+ c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.stat[0].uvalue = 0;
+ c->post_bit_count.len = 1;
+ c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_error.stat[0].uvalue = 0;
+ c->block_error.len = 1;
+ c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_count.stat[0].uvalue = 0;
+ c->block_count.len = 1;
+
+ switch (c->bandwidth_hz) {
+ case 1712000:
+ bw = CXD2880_DTV_BW_1_7_MHZ;
+ break;
+ case 5000000:
+ bw = CXD2880_DTV_BW_5_MHZ;
+ break;
+ case 6000000:
+ bw = CXD2880_DTV_BW_6_MHZ;
+ break;
+ case 7000000:
+ bw = CXD2880_DTV_BW_7_MHZ;
+ break;
+ case 8000000:
+ bw = CXD2880_DTV_BW_8_MHZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ priv->s = 0;
+
+ pr_info("sys:%d freq:%d bw:%d\n",
+ c->delivery_system, c->frequency, bw);
+ mutex_lock(priv->spi_mutex);
+ if (c->delivery_system == SYS_DVBT) {
+ priv->tnrdmd.sys = CXD2880_DTV_SYS_DVBT;
+ priv->dvbt_tune_param.center_freq_khz = c->frequency / 1000;
+ priv->dvbt_tune_param.bandwidth = bw;
+ priv->dvbt_tune_param.profile = CXD2880_DVBT_PROFILE_HP;
+ ret = cxd2880_dvbt_tune(&priv->tnrdmd,
+ &priv->dvbt_tune_param);
+ } else if (c->delivery_system == SYS_DVBT2) {
+ priv->tnrdmd.sys = CXD2880_DTV_SYS_DVBT2;
+ priv->dvbt2_tune_param.center_freq_khz = c->frequency / 1000;
+ priv->dvbt2_tune_param.bandwidth = bw;
+ priv->dvbt2_tune_param.data_plp_id = (u16)c->stream_id;
+ priv->dvbt2_tune_param.profile = CXD2880_DVBT2_PROFILE_BASE;
+ ret = cxd2880_dvbt2_tune(&priv->tnrdmd,
+ &priv->dvbt2_tune_param);
+ } else {
+ pr_err("invalid system\n");
+ mutex_unlock(priv->spi_mutex);
+ return -EINVAL;
+ }
+ mutex_unlock(priv->spi_mutex);
+
+ pr_info("tune result %d\n", ret);
+
+ return ret;
+}
+
+static int cxd2880_get_stats(struct dvb_frontend *fe,
+ enum fe_status status)
+{
+ struct cxd2880_priv *priv = NULL;
+ struct dtv_frontend_properties *c = NULL;
+ u32 pre_bit_err = 0, pre_bit_count = 0;
+ u32 post_bit_err = 0, post_bit_count = 0;
+ u32 block_err = 0, block_count = 0;
+ int ret;
+
+ if (!fe) {
+ pr_err("invalid arg\n");
+ return -EINVAL;
+ }
+
+ priv = fe->demodulator_priv;
+ c = &fe->dtv_property_cache;
+
+ if (!(status & FE_HAS_LOCK)) {
+ c->pre_bit_error.len = 1;
+ c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->pre_bit_count.len = 1;
+ c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_error.len = 1;
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.len = 1;
+ c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_error.len = 1;
+ c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_count.len = 1;
+ c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ return 0;
+ }
+
+ if (time_after(jiffies, priv->pre_ber_update)) {
+ priv->pre_ber_update =
+ jiffies + msecs_to_jiffies(priv->pre_ber_interval);
+ if (c->delivery_system == SYS_DVBT) {
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_pre_bit_err_t(&priv->tnrdmd,
+ &pre_bit_err,
+ &pre_bit_count);
+ mutex_unlock(priv->spi_mutex);
+ } else if (c->delivery_system == SYS_DVBT2) {
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_pre_bit_err_t2(&priv->tnrdmd,
+ &pre_bit_err,
+ &pre_bit_count);
+ mutex_unlock(priv->spi_mutex);
+ } else {
+ return -EINVAL;
+ }
+
+ if (!ret) {
+ c->pre_bit_error.len = 1;
+ c->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->pre_bit_error.stat[0].uvalue += pre_bit_err;
+ c->pre_bit_count.len = 1;
+ c->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->pre_bit_count.stat[0].uvalue += pre_bit_count;
+ } else {
+ c->pre_bit_error.len = 1;
+ c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->pre_bit_count.len = 1;
+ c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ pr_debug("pre_bit_error_t failed %d\n", ret);
+ }
+ }
+
+ if (time_after(jiffies, priv->post_ber_update)) {
+ priv->post_ber_update =
+ jiffies + msecs_to_jiffies(priv->post_ber_interval);
+ if (c->delivery_system == SYS_DVBT) {
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_post_bit_err_t(&priv->tnrdmd,
+ &post_bit_err,
+ &post_bit_count);
+ mutex_unlock(priv->spi_mutex);
+ } else if (c->delivery_system == SYS_DVBT2) {
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_post_bit_err_t2(&priv->tnrdmd,
+ &post_bit_err,
+ &post_bit_count);
+ mutex_unlock(priv->spi_mutex);
+ } else {
+ return -EINVAL;
+ }
+
+ if (!ret) {
+ c->post_bit_error.len = 1;
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue += post_bit_err;
+ c->post_bit_count.len = 1;
+ c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[0].uvalue += post_bit_count;
+ } else {
+ c->post_bit_error.len = 1;
+ c->post_bit_error.stat[0].scale =
+ FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.len = 1;
+ c->post_bit_count.stat[0].scale =
+ FE_SCALE_NOT_AVAILABLE;
+ pr_debug("post_bit_err_t %d\n", ret);
+ }
+ }
+
+ if (time_after(jiffies, priv->ucblock_update)) {
+ priv->ucblock_update =
+ jiffies + msecs_to_jiffies(priv->ucblock_interval);
+ if (c->delivery_system == SYS_DVBT) {
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_read_block_err_t(&priv->tnrdmd,
+ &block_err,
+ &block_count);
+ mutex_unlock(priv->spi_mutex);
+ } else if (c->delivery_system == SYS_DVBT2) {
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_read_block_err_t2(&priv->tnrdmd,
+ &block_err,
+ &block_count);
+ mutex_unlock(priv->spi_mutex);
+ } else {
+ return -EINVAL;
+ }
+ if (!ret) {
+ c->block_error.len = 1;
+ c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_error.stat[0].uvalue += block_err;
+ c->block_count.len = 1;
+ c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_count.stat[0].uvalue += block_count;
+ } else {
+ c->block_error.len = 1;
+ c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_count.len = 1;
+ c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ pr_debug("read_block_err_t %d\n", ret);
+ }
+ }
+
+ return 0;
+}
+
+static int cxd2880_check_l1post_plp(struct dvb_frontend *fe)
+{
+ u8 valid = 0;
+ u8 plp_not_found;
+ int ret;
+ struct cxd2880_priv *priv = NULL;
+
+ if (!fe) {
+ pr_err("invalid arg\n");
+ return -EINVAL;
+ }
+
+ priv = fe->demodulator_priv;
+
+ ret = cxd2880_tnrdmd_dvbt2_check_l1post_valid(&priv->tnrdmd,
+ &valid);
+ if (ret)
+ return ret;
+
+ if (!valid)
+ return -EAGAIN;
+
+ ret = cxd2880_tnrdmd_dvbt2_mon_data_plp_error(&priv->tnrdmd,
+ &plp_not_found);
+ if (ret)
+ return ret;
+
+ if (plp_not_found) {
+ priv->dvbt2_tune_param.tune_info =
+ CXD2880_TNRDMD_DVBT2_TUNE_INFO_INVALID_PLP_ID;
+ } else {
+ priv->dvbt2_tune_param.tune_info =
+ CXD2880_TNRDMD_DVBT2_TUNE_INFO_OK;
+ }
+
+ return 0;
+}
+
+static int cxd2880_read_status(struct dvb_frontend *fe,
+ enum fe_status *status)
+{
+ int ret;
+ u8 sync = 0;
+ u8 lock = 0;
+ u8 unlock = 0;
+ struct cxd2880_priv *priv = NULL;
+ struct dtv_frontend_properties *c = NULL;
+
+ if (!fe || !status) {
+ pr_err("invalid arg\n");
+ return -EINVAL;
+ }
+
+ priv = fe->demodulator_priv;
+ c = &fe->dtv_property_cache;
+ *status = 0;
+
+ if (priv->tnrdmd.state == CXD2880_TNRDMD_STATE_ACTIVE) {
+ mutex_lock(priv->spi_mutex);
+ if (c->delivery_system == SYS_DVBT) {
+ ret = cxd2880_tnrdmd_dvbt_mon_sync_stat(&priv->tnrdmd,
+ &sync,
+ &lock,
+ &unlock);
+ } else if (c->delivery_system == SYS_DVBT2) {
+ ret = cxd2880_tnrdmd_dvbt2_mon_sync_stat(&priv->tnrdmd,
+ &sync,
+ &lock,
+ &unlock);
+ } else {
+ pr_err("invalid system");
+ mutex_unlock(priv->spi_mutex);
+ return -EINVAL;
+ }
+
+ mutex_unlock(priv->spi_mutex);
+ if (ret) {
+ pr_err("failed. sys = %d\n", priv->tnrdmd.sys);
+ return ret;
+ }
+
+ if (sync == 6) {
+ *status = FE_HAS_SIGNAL |
+ FE_HAS_CARRIER;
+ }
+ if (lock)
+ *status |= FE_HAS_VITERBI |
+ FE_HAS_SYNC |
+ FE_HAS_LOCK;
+ }
+
+ pr_debug("status %d\n", *status);
+
+ if (priv->s == 0 && (*status & FE_HAS_LOCK)) {
+ mutex_lock(priv->spi_mutex);
+ if (c->delivery_system == SYS_DVBT) {
+ ret = cxd2880_set_ber_per_period_t(fe);
+ priv->s = *status;
+ } else if (c->delivery_system == SYS_DVBT2) {
+ ret = cxd2880_check_l1post_plp(fe);
+ if (!ret) {
+ ret = cxd2880_set_ber_per_period_t2(fe);
+ priv->s = *status;
+ }
+ } else {
+ pr_err("invalid system\n");
+ mutex_unlock(priv->spi_mutex);
+ return -EINVAL;
+ }
+ mutex_unlock(priv->spi_mutex);
+ }
+
+ cxd2880_get_stats(fe, *status);
+ return 0;
+}
+
+static int cxd2880_tune(struct dvb_frontend *fe,
+ bool retune,
+ unsigned int mode_flags,
+ unsigned int *delay,
+ enum fe_status *status)
+{
+ int ret;
+
+ if (!fe || !delay || !status) {
+ pr_err("invalid arg.");
+ return -EINVAL;
+ }
+
+ if (retune) {
+ ret = cxd2880_set_frontend(fe);
+ if (ret) {
+ pr_err("cxd2880_set_frontend failed %d\n", ret);
+ return ret;
+ }
+ }
+
+ *delay = HZ / 5;
+
+ return cxd2880_read_status(fe, status);
+}
+
+static int cxd2880_get_frontend_t(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
+{
+ int ret;
+ struct cxd2880_priv *priv = NULL;
+ enum cxd2880_dvbt_mode mode = CXD2880_DVBT_MODE_2K;
+ enum cxd2880_dvbt_guard guard = CXD2880_DVBT_GUARD_1_32;
+ struct cxd2880_dvbt_tpsinfo tps;
+ enum cxd2880_tnrdmd_spectrum_sense sense;
+ u16 snr = 0;
+ int strength = 0;
+
+ if (!fe || !c) {
+ pr_err("invalid arg\n");
+ return -EINVAL;
+ }
+
+ priv = fe->demodulator_priv;
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_tnrdmd_dvbt_mon_mode_guard(&priv->tnrdmd,
+ &mode, &guard);
+ mutex_unlock(priv->spi_mutex);
+ if (!ret) {
+ switch (mode) {
+ case CXD2880_DVBT_MODE_2K:
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ break;
+ case CXD2880_DVBT_MODE_8K:
+ c->transmission_mode = TRANSMISSION_MODE_8K;
+ break;
+ default:
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ pr_debug("transmission mode is invalid %d\n", mode);
+ break;
+ }
+ switch (guard) {
+ case CXD2880_DVBT_GUARD_1_32:
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case CXD2880_DVBT_GUARD_1_16:
+ c->guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case CXD2880_DVBT_GUARD_1_8:
+ c->guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case CXD2880_DVBT_GUARD_1_4:
+ c->guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ default:
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ pr_debug("guard interval is invalid %d\n",
+ guard);
+ break;
+ }
+ } else {
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ pr_debug("ModeGuard err %d\n", ret);
+ }
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_tnrdmd_dvbt_mon_tps_info(&priv->tnrdmd, &tps);
+ mutex_unlock(priv->spi_mutex);
+ if (!ret) {
+ switch (tps.hierarchy) {
+ case CXD2880_DVBT_HIERARCHY_NON:
+ c->hierarchy = HIERARCHY_NONE;
+ break;
+ case CXD2880_DVBT_HIERARCHY_1:
+ c->hierarchy = HIERARCHY_1;
+ break;
+ case CXD2880_DVBT_HIERARCHY_2:
+ c->hierarchy = HIERARCHY_2;
+ break;
+ case CXD2880_DVBT_HIERARCHY_4:
+ c->hierarchy = HIERARCHY_4;
+ break;
+ default:
+ c->hierarchy = HIERARCHY_NONE;
+ pr_debug("TPSInfo hierarchy is invalid %d\n",
+ tps.hierarchy);
+ break;
+ }
+
+ switch (tps.rate_hp) {
+ case CXD2880_DVBT_CODERATE_1_2:
+ c->code_rate_HP = FEC_1_2;
+ break;
+ case CXD2880_DVBT_CODERATE_2_3:
+ c->code_rate_HP = FEC_2_3;
+ break;
+ case CXD2880_DVBT_CODERATE_3_4:
+ c->code_rate_HP = FEC_3_4;
+ break;
+ case CXD2880_DVBT_CODERATE_5_6:
+ c->code_rate_HP = FEC_5_6;
+ break;
+ case CXD2880_DVBT_CODERATE_7_8:
+ c->code_rate_HP = FEC_7_8;
+ break;
+ default:
+ c->code_rate_HP = FEC_NONE;
+ pr_debug("TPSInfo rateHP is invalid %d\n",
+ tps.rate_hp);
+ break;
+ }
+ switch (tps.rate_lp) {
+ case CXD2880_DVBT_CODERATE_1_2:
+ c->code_rate_LP = FEC_1_2;
+ break;
+ case CXD2880_DVBT_CODERATE_2_3:
+ c->code_rate_LP = FEC_2_3;
+ break;
+ case CXD2880_DVBT_CODERATE_3_4:
+ c->code_rate_LP = FEC_3_4;
+ break;
+ case CXD2880_DVBT_CODERATE_5_6:
+ c->code_rate_LP = FEC_5_6;
+ break;
+ case CXD2880_DVBT_CODERATE_7_8:
+ c->code_rate_LP = FEC_7_8;
+ break;
+ default:
+ c->code_rate_LP = FEC_NONE;
+ pr_debug("TPSInfo rateLP is invalid %d\n",
+ tps.rate_lp);
+ break;
+ }
+ switch (tps.constellation) {
+ case CXD2880_DVBT_CONSTELLATION_QPSK:
+ c->modulation = QPSK;
+ break;
+ case CXD2880_DVBT_CONSTELLATION_16QAM:
+ c->modulation = QAM_16;
+ break;
+ case CXD2880_DVBT_CONSTELLATION_64QAM:
+ c->modulation = QAM_64;
+ break;
+ default:
+ c->modulation = QPSK;
+ pr_debug("TPSInfo constellation is invalid %d\n",
+ tps.constellation);
+ break;
+ }
+ } else {
+ c->hierarchy = HIERARCHY_NONE;
+ c->code_rate_HP = FEC_NONE;
+ c->code_rate_LP = FEC_NONE;
+ c->modulation = QPSK;
+ pr_debug("TPS info err %d\n", ret);
+ }
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_tnrdmd_dvbt_mon_spectrum_sense(&priv->tnrdmd, &sense);
+ mutex_unlock(priv->spi_mutex);
+ if (!ret) {
+ switch (sense) {
+ case CXD2880_TNRDMD_SPECTRUM_NORMAL:
+ c->inversion = INVERSION_OFF;
+ break;
+ case CXD2880_TNRDMD_SPECTRUM_INV:
+ c->inversion = INVERSION_ON;
+ break;
+ default:
+ c->inversion = INVERSION_OFF;
+ pr_debug("spectrum sense is invalid %d\n", sense);
+ break;
+ }
+ } else {
+ c->inversion = INVERSION_OFF;
+ pr_debug("spectrum_sense %d\n", ret);
+ }
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &strength);
+ mutex_unlock(priv->spi_mutex);
+ if (!ret) {
+ c->strength.len = 1;
+ c->strength.stat[0].scale = FE_SCALE_DECIBEL;
+ c->strength.stat[0].svalue = strength;
+ } else {
+ c->strength.len = 1;
+ c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ pr_debug("mon_rf_lvl %d\n", ret);
+ }
+
+ ret = cxd2880_read_snr(fe, &snr);
+ if (!ret) {
+ c->cnr.len = 1;
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ c->cnr.stat[0].svalue = snr;
+ } else {
+ c->cnr.len = 1;
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ pr_debug("read_snr %d\n", ret);
+ }
+
+ return 0;
+}
+
+static int cxd2880_get_frontend_t2(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
+{
+ int ret;
+ struct cxd2880_priv *priv = NULL;
+ struct cxd2880_dvbt2_l1pre l1pre;
+ enum cxd2880_dvbt2_plp_code_rate coderate;
+ enum cxd2880_dvbt2_plp_constell qam;
+ enum cxd2880_tnrdmd_spectrum_sense sense;
+ u16 snr = 0;
+ int strength = 0;
+
+ if (!fe || !c) {
+ pr_err("invalid arg.\n");
+ return -EINVAL;
+ }
+
+ priv = fe->demodulator_priv;
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_tnrdmd_dvbt2_mon_l1_pre(&priv->tnrdmd, &l1pre);
+ mutex_unlock(priv->spi_mutex);
+ if (!ret) {
+ switch (l1pre.fft_mode) {
+ case CXD2880_DVBT2_M2K:
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ break;
+ case CXD2880_DVBT2_M8K:
+ c->transmission_mode = TRANSMISSION_MODE_8K;
+ break;
+ case CXD2880_DVBT2_M4K:
+ c->transmission_mode = TRANSMISSION_MODE_4K;
+ break;
+ case CXD2880_DVBT2_M1K:
+ c->transmission_mode = TRANSMISSION_MODE_1K;
+ break;
+ case CXD2880_DVBT2_M16K:
+ c->transmission_mode = TRANSMISSION_MODE_16K;
+ break;
+ case CXD2880_DVBT2_M32K:
+ c->transmission_mode = TRANSMISSION_MODE_32K;
+ break;
+ default:
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ pr_debug("L1Pre fft_mode is invalid %d\n",
+ l1pre.fft_mode);
+ break;
+ }
+ switch (l1pre.gi) {
+ case CXD2880_DVBT2_G1_32:
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case CXD2880_DVBT2_G1_16:
+ c->guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case CXD2880_DVBT2_G1_8:
+ c->guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case CXD2880_DVBT2_G1_4:
+ c->guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ case CXD2880_DVBT2_G1_128:
+ c->guard_interval = GUARD_INTERVAL_1_128;
+ break;
+ case CXD2880_DVBT2_G19_128:
+ c->guard_interval = GUARD_INTERVAL_19_128;
+ break;
+ case CXD2880_DVBT2_G19_256:
+ c->guard_interval = GUARD_INTERVAL_19_256;
+ break;
+ default:
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ pr_debug("L1Pre guard interval is invalid %d\n",
+ l1pre.gi);
+ break;
+ }
+ } else {
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ pr_debug("L1Pre err %d\n", ret);
+ }
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_tnrdmd_dvbt2_mon_code_rate(&priv->tnrdmd,
+ CXD2880_DVBT2_PLP_DATA,
+ &coderate);
+ mutex_unlock(priv->spi_mutex);
+ if (!ret) {
+ switch (coderate) {
+ case CXD2880_DVBT2_R1_2:
+ c->fec_inner = FEC_1_2;
+ break;
+ case CXD2880_DVBT2_R3_5:
+ c->fec_inner = FEC_3_5;
+ break;
+ case CXD2880_DVBT2_R2_3:
+ c->fec_inner = FEC_2_3;
+ break;
+ case CXD2880_DVBT2_R3_4:
+ c->fec_inner = FEC_3_4;
+ break;
+ case CXD2880_DVBT2_R4_5:
+ c->fec_inner = FEC_4_5;
+ break;
+ case CXD2880_DVBT2_R5_6:
+ c->fec_inner = FEC_5_6;
+ break;
+ default:
+ c->fec_inner = FEC_NONE;
+ pr_debug("CodeRate is invalid %d\n", coderate);
+ break;
+ }
+ } else {
+ c->fec_inner = FEC_NONE;
+ pr_debug("CodeRate %d\n", ret);
+ }
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_tnrdmd_dvbt2_mon_qam(&priv->tnrdmd,
+ CXD2880_DVBT2_PLP_DATA,
+ &qam);
+ mutex_unlock(priv->spi_mutex);
+ if (!ret) {
+ switch (qam) {
+ case CXD2880_DVBT2_QPSK:
+ c->modulation = QPSK;
+ break;
+ case CXD2880_DVBT2_QAM16:
+ c->modulation = QAM_16;
+ break;
+ case CXD2880_DVBT2_QAM64:
+ c->modulation = QAM_64;
+ break;
+ case CXD2880_DVBT2_QAM256:
+ c->modulation = QAM_256;
+ break;
+ default:
+ c->modulation = QPSK;
+ pr_debug("QAM is invalid %d\n", qam);
+ break;
+ }
+ } else {
+ c->modulation = QPSK;
+ pr_debug("QAM %d\n", ret);
+ }
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_tnrdmd_dvbt2_mon_spectrum_sense(&priv->tnrdmd, &sense);
+ mutex_unlock(priv->spi_mutex);
+ if (!ret) {
+ switch (sense) {
+ case CXD2880_TNRDMD_SPECTRUM_NORMAL:
+ c->inversion = INVERSION_OFF;
+ break;
+ case CXD2880_TNRDMD_SPECTRUM_INV:
+ c->inversion = INVERSION_ON;
+ break;
+ default:
+ c->inversion = INVERSION_OFF;
+ pr_debug("spectrum sense is invalid %d\n", sense);
+ break;
+ }
+ } else {
+ c->inversion = INVERSION_OFF;
+ pr_debug("SpectrumSense %d\n", ret);
+ }
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &strength);
+ mutex_unlock(priv->spi_mutex);
+ if (!ret) {
+ c->strength.len = 1;
+ c->strength.stat[0].scale = FE_SCALE_DECIBEL;
+ c->strength.stat[0].svalue = strength;
+ } else {
+ c->strength.len = 1;
+ c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ pr_debug("mon_rf_lvl %d\n", ret);
+ }
+
+ ret = cxd2880_read_snr(fe, &snr);
+ if (!ret) {
+ c->cnr.len = 1;
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ c->cnr.stat[0].svalue = snr;
+ } else {
+ c->cnr.len = 1;
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ pr_debug("read_snr %d\n", ret);
+ }
+
+ return 0;
+}
+
+static int cxd2880_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *props)
+{
+ int ret;
+
+ if (!fe || !props) {
+ pr_err("invalid arg.");
+ return -EINVAL;
+ }
+
+ pr_debug("system=%d\n", fe->dtv_property_cache.delivery_system);
+ switch (fe->dtv_property_cache.delivery_system) {
+ case SYS_DVBT:
+ ret = cxd2880_get_frontend_t(fe, props);
+ break;
+ case SYS_DVBT2:
+ ret = cxd2880_get_frontend_t2(fe, props);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static enum dvbfe_algo cxd2880_get_frontend_algo(struct dvb_frontend *fe)
+{
+ return DVBFE_ALGO_HW;
+}
+
+static struct dvb_frontend_ops cxd2880_dvbt_t2_ops = {
+ .info = {
+ .name = "Sony CXD2880",
+ .frequency_min = 174000000,
+ .frequency_max = 862000000,
+ .frequency_stepsize = 1000,
+ .caps = FE_CAN_INVERSION_AUTO |
+ FE_CAN_FEC_1_2 |
+ FE_CAN_FEC_2_3 |
+ FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_4_5 |
+ FE_CAN_FEC_5_6 |
+ FE_CAN_FEC_7_8 |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK |
+ FE_CAN_QAM_16 |
+ FE_CAN_QAM_32 |
+ FE_CAN_QAM_64 |
+ FE_CAN_QAM_128 |
+ FE_CAN_QAM_256 |
+ FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_2G_MODULATION |
+ FE_CAN_RECOVER |
+ FE_CAN_MUTE_TS,
+ },
+ .delsys = { SYS_DVBT, SYS_DVBT2 },
+
+ .release = cxd2880_release,
+ .init = cxd2880_init,
+ .sleep = cxd2880_sleep,
+ .tune = cxd2880_tune,
+ .set_frontend = cxd2880_set_frontend,
+ .get_frontend = cxd2880_get_frontend,
+ .read_status = cxd2880_read_status,
+ .read_ber = cxd2880_read_ber,
+ .read_signal_strength = cxd2880_read_signal_strength,
+ .read_snr = cxd2880_read_snr,
+ .read_ucblocks = cxd2880_read_ucblocks,
+ .get_frontend_algo = cxd2880_get_frontend_algo,
+};
+
+struct dvb_frontend *cxd2880_attach(struct dvb_frontend *fe,
+ struct cxd2880_config *cfg)
+{
+ int ret;
+ enum cxd2880_tnrdmd_chip_id chipid =
+ CXD2880_TNRDMD_CHIP_ID_UNKNOWN;
+ static struct cxd2880_priv *priv;
+ u8 data = 0;
+
+ if (!fe) {
+ pr_err("invalid arg.\n");
+ return NULL;
+ }
+
+ priv = kzalloc(sizeof(struct cxd2880_priv), GFP_KERNEL);
+ if (!priv)
+ return NULL;
+
+ priv->spi = cfg->spi;
+ priv->spi_mutex = cfg->spi_mutex;
+ priv->spi_device.spi = cfg->spi;
+
+ memcpy(&fe->ops, &cxd2880_dvbt_t2_ops,
+ sizeof(struct dvb_frontend_ops));
+
+ ret = cxd2880_spi_device_initialize(&priv->spi_device,
+ CXD2880_SPI_MODE_0,
+ 55000000);
+ if (ret) {
+ pr_err("spi_device_initialize failed. %d\n", ret);
+ kfree(priv);
+ return NULL;
+ }
+
+ ret = cxd2880_spi_device_create_spi(&priv->cxd2880_spi,
+ &priv->spi_device);
+ if (ret) {
+ pr_err("spi_device_create_spi failed. %d\n", ret);
+ kfree(priv);
+ return NULL;
+ }
+
+ ret = cxd2880_io_spi_create(&priv->regio, &priv->cxd2880_spi, 0);
+ if (ret) {
+ pr_err("io_spi_create failed. %d\n", ret);
+ kfree(priv);
+ return NULL;
+ }
+ ret = priv->regio.write_reg(&priv->regio,
+ CXD2880_IO_TGT_SYS, 0x00, 0x00);
+ if (ret) {
+ pr_err("set bank to 0x00 failed.\n");
+ kfree(priv);
+ return NULL;
+ }
+ ret = priv->regio.read_regs(&priv->regio,
+ CXD2880_IO_TGT_SYS, 0xfd, &data, 1);
+ if (ret) {
+ pr_err("read chip id failed.\n");
+ kfree(priv);
+ return NULL;
+ }
+
+ chipid = (enum cxd2880_tnrdmd_chip_id)data;
+ if (chipid != CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X &&
+ chipid != CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11) {
+ pr_err("chip id invalid.\n");
+ kfree(priv);
+ return NULL;
+ }
+
+ fe->demodulator_priv = priv;
+ pr_info("CXD2880 driver version: Ver %s\n",
+ CXD2880_TNRDMD_DRIVER_VERSION);
+
+ return fe;
+}
+EXPORT_SYMBOL(cxd2880_attach);
+
+MODULE_DESCRIPTION("Sony CXD2880 DVB-T2/T tuner + demod driver");
+MODULE_AUTHOR("Sony Semiconductor Solutions Corporation");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/dvb-frontends/dib0090.c b/drivers/media/dvb-frontends/dib0090.c
index 64f49c8eb1fb..ee7af34979ed 100644
--- a/drivers/media/dvb-frontends/dib0090.c
+++ b/drivers/media/dvb-frontends/dib0090.c
@@ -1285,7 +1285,7 @@ int dib0090_gain_control(struct dvb_frontend *fe)
#endif
if (*tune_state == CT_AGC_STEP_1) { /* quickly go to the correct range of the ADC power */
- if (ABS(adc_error) < 50 || state->agc_step++ > 5) {
+ if (abs(adc_error) < 50 || state->agc_step++ > 5) {
#ifdef CONFIG_STANDARD_DAB
if (state->fe->dtv_property_cache.delivery_system == STANDARD_DAB) {
@@ -1754,7 +1754,7 @@ static int dib0090_dc_offset_calibration(struct dib0090_state *state, enum front
*tune_state = CT_TUNER_STEP_1;
} else {
/* the minimum was what we have seen in the step before */
- if (ABS(state->adc_diff) > ABS(state->min_adc_diff)) {
+ if (abs(state->adc_diff) > abs(state->min_adc_diff)) {
dprintk("Since adc_diff N = %d > adc_diff step N-1 = %d, Come back one step\n", state->adc_diff, state->min_adc_diff);
state->step--;
}
diff --git a/drivers/media/dvb-frontends/dib7000p.c b/drivers/media/dvb-frontends/dib7000p.c
index 90ace707a80d..902af482448e 100644
--- a/drivers/media/dvb-frontends/dib7000p.c
+++ b/drivers/media/dvb-frontends/dib7000p.c
@@ -809,7 +809,7 @@ static int dib7000p_set_dds(struct dib7000p_state *state, s32 offset_khz)
{
u32 internal = dib7000p_get_internal_freq(state);
s32 unit_khz_dds_val;
- u32 abs_offset_khz = ABS(offset_khz);
+ u32 abs_offset_khz = abs(offset_khz);
u32 dds = state->cfg.bw->ifreq & 0x1ffffff;
u8 invert = !!(state->cfg.bw->ifreq & (1 << 25));
if (internal == 0) {
diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c
index e64014f338fb..6f35173d2968 100644
--- a/drivers/media/dvb-frontends/dib8000.c
+++ b/drivers/media/dvb-frontends/dib8000.c
@@ -2677,7 +2677,7 @@ static void dib8000_viterbi_state(struct dib8000_state *state, u8 onoff)
static void dib8000_set_dds(struct dib8000_state *state, s32 offset_khz)
{
s16 unit_khz_dds_val;
- u32 abs_offset_khz = ABS(offset_khz);
+ u32 abs_offset_khz = abs(offset_khz);
u32 dds = state->cfg.pll->ifreq & 0x1ffffff;
u8 invert = !!(state->cfg.pll->ifreq & (1 << 25));
u8 ratio;
diff --git a/drivers/media/dvb-frontends/dibx000_common.c b/drivers/media/dvb-frontends/dibx000_common.c
index d981233e458f..70119c79ac2b 100644
--- a/drivers/media/dvb-frontends/dibx000_common.c
+++ b/drivers/media/dvb-frontends/dibx000_common.c
@@ -424,7 +424,7 @@ static int i2c_adapter_init(struct i2c_adapter *i2c_adap,
struct i2c_algorithm *algo, const char *name,
struct dibx000_i2c_master *mst)
{
- strncpy(i2c_adap->name, name, sizeof(i2c_adap->name));
+ strlcpy(i2c_adap->name, name, sizeof(i2c_adap->name));
i2c_adap->algo = algo;
i2c_adap->algo_data = NULL;
i2c_set_adapdata(i2c_adap, mst);
diff --git a/drivers/media/dvb-frontends/dibx000_common.h b/drivers/media/dvb-frontends/dibx000_common.h
index 8784af962eba..12b58f5c677d 100644
--- a/drivers/media/dvb-frontends/dibx000_common.h
+++ b/drivers/media/dvb-frontends/dibx000_common.h
@@ -223,8 +223,6 @@ struct dvb_frontend_parametersContext {
#define FE_CALLBACK_TIME_NEVER 0xffffffff
-#define ABS(x) ((x < 0) ? (-x) : (x))
-
#define DATA_BUS_ACCESS_MODE_8BIT 0x01
#define DATA_BUS_ACCESS_MODE_16BIT 0x02
#define DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT 0x10
diff --git a/drivers/media/dvb-frontends/drx39xyj/bsp_i2c.h b/drivers/media/dvb-frontends/drx39xyj/bsp_i2c.h
deleted file mode 100644
index 2b3af247a1f1..000000000000
--- a/drivers/media/dvb-frontends/drx39xyj/bsp_i2c.h
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- I2C API, implementation depends on board specifics
-
- Copyright (c), 2004-2005,2007-2010 Trident Microsystems, Inc.
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- * Neither the name of Trident Microsystems nor Hauppauge Computer Works
- nor the names of its contributors may be used to endorse or promote
- products derived from this software without specific prior written
- permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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.
-
- This module encapsulates I2C access.In some applications several devices
- share one I2C bus. If these devices have the same I2C address some kind
- off "switch" must be implemented to ensure error free communication with
- one device. In case such a "switch" is used, the device ID can be used
- to implement control over this "switch".
-*/
-
-#ifndef __BSPI2C_H__
-#define __BSPI2C_H__
-
-#include "bsp_types.h"
-
-/*
- * This structure contains the I2C address, the device ID and a user_data pointer.
- * The user_data pointer can be used for application specific purposes.
- */
-struct i2c_device_addr {
- u16 i2c_addr; /* The I2C address of the device. */
- u16 i2c_dev_id; /* The device identifier. */
- void *user_data; /* User data pointer */
-};
-
-
-/*
-* \def IS_I2C_10BIT( addr )
-* \brief Determine if I2C address 'addr' is a 10 bits address or not.
-* \param addr The I2C address.
-* \return int.
-* \retval 0 if address is not a 10 bits I2C address.
-* \retval 1 if address is a 10 bits I2C address.
-*/
-#define IS_I2C_10BIT(addr) \
- (((addr) & 0xF8) == 0xF0)
-
-/*------------------------------------------------------------------------------
-Exported FUNCTIONS
-------------------------------------------------------------------------------*/
-
-/*
-* \fn drxbsp_i2c_init()
-* \brief Initialize I2C communication module.
-* \return drx_status_t Return status.
-* \retval 0 Initialization successful.
-* \retval -EIO Initialization failed.
-*/
- drx_status_t drxbsp_i2c_init(void);
-
-/*
-* \fn drxbsp_i2c_term()
-* \brief Terminate I2C communication module.
-* \return drx_status_t Return status.
-* \retval 0 Termination successful.
-* \retval -EIO Termination failed.
-*/
- drx_status_t drxbsp_i2c_term(void);
-
-/*
-* \fn drx_status_t drxbsp_i2c_write_read( struct i2c_device_addr *w_dev_addr,
-* u16 w_count,
-* u8 *wData,
-* struct i2c_device_addr *r_dev_addr,
-* u16 r_count,
-* u8 *r_data)
-* \brief Read and/or write count bytes from I2C bus, store them in data[].
-* \param w_dev_addr The device i2c address and the device ID to write to
-* \param w_count The number of bytes to write
-* \param wData The array to write the data to
-* \param r_dev_addr The device i2c address and the device ID to read from
-* \param r_count The number of bytes to read
-* \param r_data The array to read the data from
-* \return drx_status_t Return status.
-* \retval 0 Succes.
-* \retval -EIO Failure.
-* \retval -EINVAL Parameter 'wcount' is not zero but parameter
-* 'wdata' contains NULL.
-* Idem for 'rcount' and 'rdata'.
-* Both w_dev_addr and r_dev_addr are NULL.
-*
-* This function must implement an atomic write and/or read action on the I2C bus
-* No other process may use the I2C bus when this function is executing.
-* The critical section of this function runs from and including the I2C
-* write, up to and including the I2C read action.
-*
-* The device ID can be useful if several devices share an I2C address.
-* It can be used to control a "switch" on the I2C bus to the correct device.
-*/
- drx_status_t drxbsp_i2c_write_read(struct i2c_device_addr *w_dev_addr,
- u16 w_count,
- u8 *w_data,
- struct i2c_device_addr *r_dev_addr,
- u16 r_count, u8 *r_data);
-
-/*
-* \fn drxbsp_i2c_error_text()
-* \brief Returns a human readable error.
-* Counter part of numerical drx_i2c_error_g.
-*
-* \return char* Pointer to human readable error text.
-*/
- char *drxbsp_i2c_error_text(void);
-
-/*
-* \var drx_i2c_error_g;
-* \brief I2C specific error codes, platform dependent.
-*/
- extern int drx_i2c_error_g;
-
-#endif /* __BSPI2C_H__ */
diff --git a/drivers/media/dvb-frontends/lgdt3306a.c b/drivers/media/dvb-frontends/lgdt3306a.c
index 6356815cf3e1..7eb4e1469d20 100644
--- a/drivers/media/dvb-frontends/lgdt3306a.c
+++ b/drivers/media/dvb-frontends/lgdt3306a.c
@@ -30,6 +30,17 @@ static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "set debug level (info=1, reg=2 (or-able))");
+/*
+ * Older drivers treated QAM64 and QAM256 the same; that is the HW always
+ * used "Auto" mode during detection. Setting "forced_manual"=1 allows
+ * the user to treat these modes as separate. For backwards compatibility,
+ * it's off by default. QAM_AUTO can now be specified to achive that
+ * effect even if "forced_manual"=1
+ */
+static int forced_manual;
+module_param(forced_manual, int, 0644);
+MODULE_PARM_DESC(forced_manual, "if set, QAM64 and QAM256 will only lock to modulation specified");
+
#define DBG_INFO 1
#define DBG_REG 2
#define DBG_DUMP 4 /* FGR - comment out to remove dump code */
@@ -566,7 +577,12 @@ static int lgdt3306a_set_qam(struct lgdt3306a_state *state, int modulation)
/* 3. : 64QAM/256QAM detection(manual, auto) */
ret = lgdt3306a_read_reg(state, 0x0009, &val);
val &= 0xfc;
- val |= 0x02; /* STDOPDETCMODE[1:0]=1=Manual 2=Auto */
+ /* Check for forced Manual modulation modes; otherwise always "auto" */
+ if(forced_manual && (modulation != QAM_AUTO)){
+ val |= 0x01; /* STDOPDETCMODE[1:0]= 1=Manual */
+ } else {
+ val |= 0x02; /* STDOPDETCMODE[1:0]= 2=Auto */
+ }
ret = lgdt3306a_write_reg(state, 0x0009, val);
if (lg_chkerr(ret))
goto fail;
@@ -598,6 +614,28 @@ static int lgdt3306a_set_qam(struct lgdt3306a_state *state, int modulation)
if (lg_chkerr(ret))
goto fail;
+ /* 5.1 V0.36 SRDCHKALWAYS : For better QAM detection */
+ ret = lgdt3306a_read_reg(state, 0x000a, &val);
+ val &= 0xfd;
+ val |= 0x02;
+ ret = lgdt3306a_write_reg(state, 0x000a, val);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* 5.2 V0.36 Control of "no signal" detector function */
+ ret = lgdt3306a_read_reg(state, 0x2849, &val);
+ val &= 0xdf;
+ ret = lgdt3306a_write_reg(state, 0x2849, val);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* 5.3 Fix for Blonder Tongue HDE-2H-QAM and AQM modulators */
+ ret = lgdt3306a_read_reg(state, 0x302b, &val);
+ val &= 0x7f; /* SELFSYNCFINDEN_CQS=0; disable auto reset */
+ ret = lgdt3306a_write_reg(state, 0x302b, val);
+ if (lg_chkerr(ret))
+ goto fail;
+
/* 6. Reset */
ret = lgdt3306a_soft_reset(state);
if (lg_chkerr(ret))
@@ -620,10 +658,9 @@ static int lgdt3306a_set_modulation(struct lgdt3306a_state *state,
ret = lgdt3306a_set_vsb(state);
break;
case QAM_64:
- ret = lgdt3306a_set_qam(state, QAM_64);
- break;
case QAM_256:
- ret = lgdt3306a_set_qam(state, QAM_256);
+ case QAM_AUTO:
+ ret = lgdt3306a_set_qam(state, p->modulation);
break;
default:
return -EINVAL;
@@ -650,6 +687,7 @@ static int lgdt3306a_agc_setup(struct lgdt3306a_state *state,
break;
case QAM_64:
case QAM_256:
+ case QAM_AUTO:
break;
default:
return -EINVAL;
@@ -704,6 +742,7 @@ static int lgdt3306a_spectral_inversion(struct lgdt3306a_state *state,
break;
case QAM_64:
case QAM_256:
+ case QAM_AUTO:
/* Auto ok for QAM */
ret = lgdt3306a_set_inversion_auto(state, 1);
break;
@@ -727,6 +766,7 @@ static int lgdt3306a_set_if(struct lgdt3306a_state *state,
break;
case QAM_64:
case QAM_256:
+ case QAM_AUTO:
if_freq_khz = state->cfg->qam_if_khz;
break;
default:
@@ -1585,6 +1625,7 @@ static int lgdt3306a_read_status(struct dvb_frontend *fe,
switch (state->current_modulation) {
case QAM_256:
case QAM_64:
+ case QAM_AUTO:
if (lgdt3306a_qam_lock_poll(state) == LG3306_LOCK) {
*status |= FE_HAS_VITERBI;
*status |= FE_HAS_SYNC;
@@ -1628,6 +1669,7 @@ static int lgdt3306a_read_signal_strength(struct dvb_frontend *fe,
* Calculate some sort of "strength" from SNR
*/
struct lgdt3306a_state *state = fe->demodulator_priv;
+ u8 val;
u16 snr; /* snr_x10 */
int ret;
u32 ref_snr; /* snr*100 */
@@ -1640,11 +1682,15 @@ static int lgdt3306a_read_signal_strength(struct dvb_frontend *fe,
ref_snr = 1600; /* 16dB */
break;
case QAM_64:
- ref_snr = 2200; /* 22dB */
- break;
case QAM_256:
- ref_snr = 2800; /* 28dB */
- break;
+ case QAM_AUTO:
+ /* need to know actual modulation to set proper SNR baseline */
+ lgdt3306a_read_reg(state, 0x00a6, &val);
+ if(val & 0x04)
+ ref_snr = 2800; /* QAM-256 28dB */
+ else
+ ref_snr = 2200; /* QAM-64 22dB */
+ break;
default:
return -EINVAL;
}
@@ -2114,7 +2160,7 @@ static const struct dvb_frontend_ops lgdt3306a_ops = {
.frequency_min = 54000000,
.frequency_max = 858000000,
.frequency_stepsize = 62500,
- .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
+ .caps = FE_CAN_QAM_AUTO | FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
},
.i2c_gate_ctrl = lgdt3306a_i2c_gate_ctrl,
.init = lgdt3306a_init,
@@ -2177,6 +2223,7 @@ static int lgdt3306a_probe(struct i2c_client *client,
i2c_set_clientdata(client, fe->demodulator_priv);
state = fe->demodulator_priv;
+ state->frontend.ops.release = NULL;
/* create mux i2c adapter for tuner */
state->muxc = i2c_mux_alloc(client->adapter, &client->dev,
@@ -2196,6 +2243,8 @@ static int lgdt3306a_probe(struct i2c_client *client,
*config->i2c_adapter = state->muxc->adapter[0];
*config->fe = fe;
+ dev_info(&client->dev, "LG Electronics LGDT3306A successfully identified\n");
+
return 0;
err_kfree:
@@ -2203,7 +2252,7 @@ err_kfree:
err_fe:
kfree(config);
fail:
- dev_dbg(&client->dev, "failed=%d\n", ret);
+ dev_warn(&client->dev, "probe failed = %d\n", ret);
return ret;
}
diff --git a/drivers/media/dvb-frontends/mb86a16.c b/drivers/media/dvb-frontends/mb86a16.c
index 2969ba6ed9e1..377cd984b069 100644
--- a/drivers/media/dvb-frontends/mb86a16.c
+++ b/drivers/media/dvb-frontends/mb86a16.c
@@ -31,8 +31,6 @@
static unsigned int verbose = 5;
module_param(verbose, int, 0644);
-#define ABS(x) ((x) < 0 ? (-x) : (x))
-
struct mb86a16_state {
struct i2c_adapter *i2c_adap;
const struct mb86a16_config *config;
@@ -1202,12 +1200,12 @@ static int mb86a16_set_fe(struct mb86a16_state *state)
signal_dupl = 0;
for (j = 0; j < prev_freq_num; j++) {
- if ((ABS(prev_swp_freq[j] - swp_freq)) < (swp_ofs * 3 / 2)) {
+ if ((abs(prev_swp_freq[j] - swp_freq)) < (swp_ofs * 3 / 2)) {
signal_dupl = 1;
dprintk(verbose, MB86A16_INFO, 1, "Probably Duplicate Signal, j = %d", j);
}
}
- if ((signal_dupl == 0) && (swp_freq > 0) && (ABS(swp_freq - state->frequency * 1000) < fcp + state->srate / 6)) {
+ if ((signal_dupl == 0) && (swp_freq > 0) && (abs(swp_freq - state->frequency * 1000) < fcp + state->srate / 6)) {
dprintk(verbose, MB86A16_DEBUG, 1, "------ Signal detect ------ [swp_freq=[%07d, srate=%05d]]", swp_freq, state->srate);
prev_swp_freq[prev_freq_num] = swp_freq;
prev_freq_num++;
@@ -1381,7 +1379,7 @@ static int mb86a16_set_fe(struct mb86a16_state *state)
dprintk(verbose, MB86A16_INFO, 1, "SWEEP Frequency = %d", swp_freq);
swp_freq += delta_freq;
dprintk(verbose, MB86A16_INFO, 1, "Adjusting .., DELTA Freq = %d, SWEEP Freq=%d", delta_freq, swp_freq);
- if (ABS(state->frequency * 1000 - swp_freq) > 3800) {
+ if (abs(state->frequency * 1000 - swp_freq) > 3800) {
dprintk(verbose, MB86A16_INFO, 1, "NO -- SIGNAL !");
} else {
diff --git a/drivers/media/dvb-frontends/mxl5xx.c b/drivers/media/dvb-frontends/mxl5xx.c
index e899821018a0..483ee7d6198e 100644
--- a/drivers/media/dvb-frontends/mxl5xx.c
+++ b/drivers/media/dvb-frontends/mxl5xx.c
@@ -380,6 +380,38 @@ static int get_algo(struct dvb_frontend *fe)
return DVBFE_ALGO_HW;
}
+static u32 gold2root(u32 gold)
+{
+ u32 x, g, tmp = gold;
+
+ if (tmp >= 0x3ffff)
+ tmp = 0;
+ for (g = 0, x = 1; g < tmp; g++)
+ x = (((x ^ (x >> 7)) & 1) << 17) | (x >> 1);
+ return x;
+}
+
+static int cfg_scrambler(struct mxl *state, u32 gold)
+{
+ u32 root;
+ u8 buf[26] = {
+ MXL_HYDRA_PLID_CMD_WRITE, 24,
+ 0, MXL_HYDRA_DEMOD_SCRAMBLE_CODE_CMD, 0, 0,
+ state->demod, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0, 0, 0,
+ };
+
+ root = gold2root(gold);
+
+ buf[25] = (root >> 24) & 0xff;
+ buf[24] = (root >> 16) & 0xff;
+ buf[23] = (root >> 8) & 0xff;
+ buf[22] = root & 0xff;
+
+ return send_command(state, sizeof(buf), buf);
+}
+
static int cfg_demod_abort_tune(struct mxl *state)
{
struct MXL_HYDRA_DEMOD_ABORT_TUNE_T abort_tune_cmd;
@@ -437,7 +469,7 @@ static int set_parameters(struct dvb_frontend *fe)
demod_chan_cfg.roll_off = MXL_HYDRA_ROLLOFF_AUTO;
demod_chan_cfg.modulation_scheme = MXL_HYDRA_MOD_AUTO;
demod_chan_cfg.pilots = MXL_HYDRA_PILOTS_AUTO;
- /* cfg_scrambler(state); */
+ cfg_scrambler(state, p->scrambling_sequence_index);
break;
default:
return -EINVAL;
diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c
index 94bf5b7d6f3f..fa3b8169c1a5 100644
--- a/drivers/media/dvb-frontends/rtl2832.c
+++ b/drivers/media/dvb-frontends/rtl2832.c
@@ -498,7 +498,7 @@ static int rtl2832_set_frontend(struct dvb_frontend *fe)
* RSAMP_RATIO = floor(CrystalFreqHz * 7 * pow(2, 22)
* / ConstWithBandwidthMode)
*/
- num = dev->pdata->clk * 7;
+ num = dev->pdata->clk * 7ULL;
num *= 0x400000;
num = div_u64(num, bw_mode);
resamp_ratio = num & 0x3ffffff;
@@ -511,7 +511,7 @@ static int rtl2832_set_frontend(struct dvb_frontend *fe)
* / (CrystalFreqHz * 7))
*/
num = bw_mode << 20;
- num2 = dev->pdata->clk * 7;
+ num2 = dev->pdata->clk * 7ULL;
num = div_u64(num, num2);
num = -num;
cfreq_off_ratio = num & 0xfffff;
diff --git a/drivers/media/dvb-frontends/s5h1409.c b/drivers/media/dvb-frontends/s5h1409.c
index aced6a956ec5..a23ba8727218 100644
--- a/drivers/media/dvb-frontends/s5h1409.c
+++ b/drivers/media/dvb-frontends/s5h1409.c
@@ -682,17 +682,17 @@ static int s5h1409_set_mpeg_timing(struct dvb_frontend *fe, int mode)
val = s5h1409_readreg(state, 0xac) & 0xcfff;
switch (mode) {
- case S5H1409_MPEGTIMING_CONTINOUS_INVERTING_CLOCK:
+ case S5H1409_MPEGTIMING_CONTINUOUS_INVERTING_CLOCK:
val |= 0x0000;
break;
- case S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK:
+ case S5H1409_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK:
dprintk("%s(%d) Mode1 or Defaulting\n", __func__, mode);
val |= 0x1000;
break;
- case S5H1409_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK:
+ case S5H1409_MPEGTIMING_NONCONTINUOUS_INVERTING_CLOCK:
val |= 0x2000;
break;
- case S5H1409_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK:
+ case S5H1409_MPEGTIMING_NONCONTINUOUS_NONINVERTING_CLOCK:
val |= 0x3000;
break;
default:
diff --git a/drivers/media/dvb-frontends/s5h1409.h b/drivers/media/dvb-frontends/s5h1409.h
index b38557c451b9..87de58ffc822 100644
--- a/drivers/media/dvb-frontends/s5h1409.h
+++ b/drivers/media/dvb-frontends/s5h1409.h
@@ -52,10 +52,10 @@ struct s5h1409_config {
u8 status_mode;
/* MPEG signal timing */
-#define S5H1409_MPEGTIMING_CONTINOUS_INVERTING_CLOCK 0
-#define S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK 1
-#define S5H1409_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK 2
-#define S5H1409_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK 3
+#define S5H1409_MPEGTIMING_CONTINUOUS_INVERTING_CLOCK 0
+#define S5H1409_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK 1
+#define S5H1409_MPEGTIMING_NONCONTINUOUS_INVERTING_CLOCK 2
+#define S5H1409_MPEGTIMING_NONCONTINUOUS_NONINVERTING_CLOCK 3
u16 mpeg_timing;
/* HVR-1600 optimizations (to better work with MXL5005s)
diff --git a/drivers/media/dvb-frontends/s5h1411.c b/drivers/media/dvb-frontends/s5h1411.c
index c4b1e9725f3e..af5962807f2c 100644
--- a/drivers/media/dvb-frontends/s5h1411.c
+++ b/drivers/media/dvb-frontends/s5h1411.c
@@ -433,17 +433,17 @@ static int s5h1411_set_mpeg_timing(struct dvb_frontend *fe, int mode)
val = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xbe) & 0xcfff;
switch (mode) {
- case S5H1411_MPEGTIMING_CONTINOUS_INVERTING_CLOCK:
+ case S5H1411_MPEGTIMING_CONTINUOUS_INVERTING_CLOCK:
val |= 0x0000;
break;
- case S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK:
+ case S5H1411_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK:
dprintk("%s(%d) Mode1 or Defaulting\n", __func__, mode);
val |= 0x1000;
break;
- case S5H1411_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK:
+ case S5H1411_MPEGTIMING_NONCONTINUOUS_INVERTING_CLOCK:
val |= 0x2000;
break;
- case S5H1411_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK:
+ case S5H1411_MPEGTIMING_NONCONTINUOUS_NONINVERTING_CLOCK:
val |= 0x3000;
break;
default:
diff --git a/drivers/media/dvb-frontends/s5h1411.h b/drivers/media/dvb-frontends/s5h1411.h
index 791bab0e16e9..850ee713d64c 100644
--- a/drivers/media/dvb-frontends/s5h1411.h
+++ b/drivers/media/dvb-frontends/s5h1411.h
@@ -40,10 +40,10 @@ struct s5h1411_config {
u8 gpio;
/* MPEG signal timing */
-#define S5H1411_MPEGTIMING_CONTINOUS_INVERTING_CLOCK 0
-#define S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK 1
-#define S5H1411_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK 2
-#define S5H1411_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK 3
+#define S5H1411_MPEGTIMING_CONTINUOUS_INVERTING_CLOCK 0
+#define S5H1411_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK 1
+#define S5H1411_MPEGTIMING_NONCONTINUOUS_INVERTING_CLOCK 2
+#define S5H1411_MPEGTIMING_NONCONTINUOUS_NONINVERTING_CLOCK 3
u16 mpeg_timing;
/* IF Freq for QAM and VSB in KHz */
diff --git a/drivers/media/dvb-frontends/s5h1432.h b/drivers/media/dvb-frontends/s5h1432.h
index af3a157b5e77..646dda36262b 100644
--- a/drivers/media/dvb-frontends/s5h1432.h
+++ b/drivers/media/dvb-frontends/s5h1432.h
@@ -42,10 +42,10 @@ struct s5h1432_config {
u8 gpio;
/* MPEG signal timing */
-#define S5H1432_MPEGTIMING_CONTINOUS_INVERTING_CLOCK 0
-#define S5H1432_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK 1
-#define S5H1432_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK 2
-#define S5H1432_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK 3
+#define S5H1432_MPEGTIMING_CONTINUOUS_INVERTING_CLOCK 0
+#define S5H1432_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK 1
+#define S5H1432_MPEGTIMING_NONCONTINUOUS_INVERTING_CLOCK 2
+#define S5H1432_MPEGTIMING_NONCONTINUOUS_NONINVERTING_CLOCK 3
u16 mpeg_timing;
/* IF Freq for QAM and VSB in KHz */
diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c
index 539399dac551..324493e05f9f 100644
--- a/drivers/media/dvb-frontends/si2168.c
+++ b/drivers/media/dvb-frontends/si2168.c
@@ -82,6 +82,30 @@ err_mutex_unlock:
return ret;
}
+static int si2168_ts_bus_ctrl(struct dvb_frontend *fe, int acquire)
+{
+ struct i2c_client *client = fe->demodulator_priv;
+ struct si2168_dev *dev = i2c_get_clientdata(client);
+ struct si2168_cmd cmd;
+ int ret = 0;
+
+ dev_dbg(&client->dev, "%s acquire: %d\n", __func__, acquire);
+
+ /* set TS_MODE property */
+ memcpy(cmd.args, "\x14\x00\x01\x10\x10\x00", 6);
+ if (acquire)
+ cmd.args[4] |= dev->ts_mode;
+ else
+ cmd.args[4] |= SI2168_TS_TRISTATE;
+ if (dev->ts_clock_gapped)
+ cmd.args[4] |= 0x40;
+ cmd.wlen = 6;
+ cmd.rlen = 4;
+ ret = si2168_cmd_execute(client, &cmd);
+
+ return ret;
+}
+
static int si2168_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
struct i2c_client *client = fe->demodulator_priv;
@@ -339,6 +363,8 @@ static int si2168_set_frontend(struct dvb_frontend *fe)
memcpy(cmd.args, "\x14\x00\x0a\x10\x00\x00", 6);
cmd.args[4] = delivery_system | bandwidth;
+ if (dev->spectral_inversion)
+ cmd.args[5] |= 1;
cmd.wlen = 6;
cmd.rlen = 4;
ret = si2168_cmd_execute(client, &cmd);
@@ -403,6 +429,11 @@ static int si2168_set_frontend(struct dvb_frontend *fe)
dev->delivery_system = c->delivery_system;
+ /* enable ts bus */
+ ret = si2168_ts_bus_ctrl(fe, 1);
+ if (ret)
+ goto err;
+
return 0;
err:
dev_dbg(&client->dev, "failed=%d\n", ret);
@@ -541,13 +572,7 @@ static int si2168_init(struct dvb_frontend *fe)
dev->version >> 8 & 0xff, dev->version >> 0 & 0xff);
/* set ts mode */
- memcpy(cmd.args, "\x14\x00\x01\x10\x10\x00", 6);
- cmd.args[4] |= dev->ts_mode;
- if (dev->ts_clock_gapped)
- cmd.args[4] |= 0x40;
- cmd.wlen = 6;
- cmd.rlen = 4;
- ret = si2168_cmd_execute(client, &cmd);
+ ret = si2168_ts_bus_ctrl(fe, 1);
if (ret)
goto err;
@@ -584,7 +609,12 @@ static int si2168_sleep(struct dvb_frontend *fe)
dev->active = false;
- /* Firmware B 4.0-11 or later loses warm state during sleep */
+ /* tri-state data bus */
+ ret = si2168_ts_bus_ctrl(fe, 0);
+ if (ret)
+ goto err;
+
+ /* Firmware later than B 4.0-11 loses warm state during sleep */
if (dev->version > ('B' << 24 | 4 << 16 | 0 << 8 | 11 << 0))
dev->warm = false;
@@ -776,6 +806,7 @@ static int si2168_probe(struct i2c_client *client,
dev->ts_mode = config->ts_mode;
dev->ts_clock_inv = config->ts_clock_inv;
dev->ts_clock_gapped = config->ts_clock_gapped;
+ dev->spectral_inversion = config->spectral_inversion;
dev_info(&client->dev, "Silicon Labs Si2168-%c%d%d successfully identified\n",
dev->version >> 24 & 0xff, dev->version >> 16 & 0xff,
@@ -788,7 +819,7 @@ static int si2168_probe(struct i2c_client *client,
err_kfree:
kfree(dev);
err:
- dev_dbg(&client->dev, "failed=%d\n", ret);
+ dev_warn(&client->dev, "probe failed = %d\n", ret);
return ret;
}
diff --git a/drivers/media/dvb-frontends/si2168.h b/drivers/media/dvb-frontends/si2168.h
index 3225d0cc93c7..d519edd26c21 100644
--- a/drivers/media/dvb-frontends/si2168.h
+++ b/drivers/media/dvb-frontends/si2168.h
@@ -38,6 +38,7 @@ struct si2168_config {
/* TS mode */
#define SI2168_TS_PARALLEL 0x06
#define SI2168_TS_SERIAL 0x03
+#define SI2168_TS_TRISTATE 0x00
u8 ts_mode;
/* TS clock inverted */
@@ -45,6 +46,9 @@ struct si2168_config {
/* TS clock gapped */
bool ts_clock_gapped;
+
+ /* Inverted spectrum */
+ bool spectral_inversion;
};
#endif
diff --git a/drivers/media/dvb-frontends/si2168_priv.h b/drivers/media/dvb-frontends/si2168_priv.h
index 3c8746a20038..2d362e162ade 100644
--- a/drivers/media/dvb-frontends/si2168_priv.h
+++ b/drivers/media/dvb-frontends/si2168_priv.h
@@ -48,6 +48,7 @@ struct si2168_dev {
u8 ts_mode;
bool ts_clock_inv;
bool ts_clock_gapped;
+ bool spectral_inversion;
};
/* firmware command struct */
diff --git a/drivers/media/dvb-frontends/sp887x.c b/drivers/media/dvb-frontends/sp887x.c
index 572a297811fe..f39d566d7d1d 100644
--- a/drivers/media/dvb-frontends/sp887x.c
+++ b/drivers/media/dvb-frontends/sp887x.c
@@ -136,7 +136,7 @@ static void sp887x_setup_agc (struct sp887x_state* state)
static int sp887x_initial_setup (struct dvb_frontend* fe, const struct firmware *fw)
{
struct sp887x_state* state = fe->demodulator_priv;
- u8 buf [BLOCKSIZE+2];
+ u8 buf [BLOCKSIZE + 2];
int i;
int fw_size = fw->size;
const unsigned char *mem = fw->data;
@@ -144,7 +144,7 @@ static int sp887x_initial_setup (struct dvb_frontend* fe, const struct firmware
dprintk("%s\n", __func__);
/* ignore the first 10 bytes, then we expect 0x4000 bytes of firmware */
- if (fw_size < FW_SIZE+10)
+ if (fw_size < FW_SIZE + 10)
return -ENODEV;
mem = fw->data + 10;
@@ -167,7 +167,7 @@ static int sp887x_initial_setup (struct dvb_frontend* fe, const struct firmware
int c = BLOCKSIZE;
int err;
- if (i+c > FW_SIZE)
+ if (c > FW_SIZE - i)
c = FW_SIZE - i;
/* bit 0x8000 in address is set to enable 13bit mode */
diff --git a/drivers/media/dvb-frontends/stb0899_reg.h b/drivers/media/dvb-frontends/stb0899_reg.h
index ba1ed56304a0..f564269249a6 100644
--- a/drivers/media/dvb-frontends/stb0899_reg.h
+++ b/drivers/media/dvb-frontends/stb0899_reg.h
@@ -374,22 +374,22 @@
#define STB0899_OFF0_IF_AGC_GAIN 0xf30c
#define STB0899_BASE_IF_AGC_GAIN 0x00000000
-#define STB0899_IF_AGC_GAIN (0x3fff < 0)
+#define STB0899_IF_AGC_GAIN (0x3fff << 0)
#define STB0899_OFFST_IF_AGC_GAIN 0
#define STB0899_WIDTH_IF_AGC_GAIN 14
#define STB0899_OFF0_BB_AGC_GAIN 0xf310
#define STB0899_BASE_BB_AGC_GAIN 0x00000000
-#define STB0899_BB_AGC_GAIN (0x3fff < 0)
+#define STB0899_BB_AGC_GAIN (0x3fff << 0)
#define STB0899_OFFST_BB_AGC_GAIN 0
#define STB0899_WIDTH_BB_AGC_GAIN 14
#define STB0899_OFF0_DC_OFFSET 0xf314
#define STB0899_BASE_DC_OFFSET 0x00000000
-#define STB0899_I (0xff < 8)
+#define STB0899_I (0xff << 8)
#define STB0899_OFFST_I 8
#define STB0899_WIDTH_I 8
-#define STB0899_Q (0xff < 0)
+#define STB0899_Q (0xff << 0)
#define STB0899_OFFST_Q 8
#define STB0899_WIDTH_Q 8
diff --git a/drivers/media/dvb-frontends/stv0367_priv.h b/drivers/media/dvb-frontends/stv0367_priv.h
index 8abc451dd524..460066a391b7 100644
--- a/drivers/media/dvb-frontends/stv0367_priv.h
+++ b/drivers/media/dvb-frontends/stv0367_priv.h
@@ -35,7 +35,6 @@
#endif
/* MACRO definitions */
-#define ABS(X) ((X) < 0 ? (-1 * (X)) : (X))
#define MAX(X, Y) ((X) >= (Y) ? (X) : (Y))
#define MIN(X, Y) ((X) <= (Y) ? (X) : (Y))
#define INRANGE(X, Y, Z) \
diff --git a/drivers/media/dvb-frontends/stv0900_priv.h b/drivers/media/dvb-frontends/stv0900_priv.h
index d1fc06ff27d3..09a46477eae4 100644
--- a/drivers/media/dvb-frontends/stv0900_priv.h
+++ b/drivers/media/dvb-frontends/stv0900_priv.h
@@ -24,7 +24,6 @@
#include <linux/i2c.h>
-#define ABS(X) ((X) < 0 ? (-1 * (X)) : (X))
#define INRANGE(X, Y, Z) ((((X) <= (Y)) && ((Y) <= (Z))) \
|| (((Z) <= (Y)) && ((Y) <= (X))) ? 1 : 0)
diff --git a/drivers/media/dvb-frontends/stv0900_sw.c b/drivers/media/dvb-frontends/stv0900_sw.c
index c97a39120ea5..d406c83e4744 100644
--- a/drivers/media/dvb-frontends/stv0900_sw.c
+++ b/drivers/media/dvb-frontends/stv0900_sw.c
@@ -1255,14 +1255,14 @@ fe_stv0900_signal_type stv0900_get_signal_params(struct dvb_frontend *fe)
else
intp->freq[d] = stv0900_get_tuner_freq(fe);
- if (ABS(offsetFreq) <= ((intp->srch_range[d] / 2000) + 500))
+ if (abs(offsetFreq) <= ((intp->srch_range[d] / 2000) + 500))
range = STV0900_RANGEOK;
- else if (ABS(offsetFreq) <=
+ else if (abs(offsetFreq) <=
(stv0900_carrier_width(result->symbol_rate,
result->rolloff) / 2000))
range = STV0900_RANGEOK;
- } else if (ABS(offsetFreq) <= ((intp->srch_range[d] / 2000) + 500))
+ } else if (abs(offsetFreq) <= ((intp->srch_range[d] / 2000) + 500))
range = STV0900_RANGEOK;
dprintk("%s: range %d\n", __func__, range);
diff --git a/drivers/media/dvb-frontends/stv0910.c b/drivers/media/dvb-frontends/stv0910.c
index a2f7c0c1587f..52355c14fd64 100644
--- a/drivers/media/dvb-frontends/stv0910.c
+++ b/drivers/media/dvb-frontends/stv0910.c
@@ -1673,15 +1673,15 @@ static int send_master_cmd(struct dvb_frontend *fe,
struct dvb_diseqc_master_cmd *cmd)
{
struct stv *state = fe->demodulator_priv;
- u16 offs = state->nr ? 0x40 : 0;
int i;
- write_reg(state, RSTV0910_P1_DISTXCFG + offs, 0x3E);
+ SET_FIELD(DISEQC_MODE, 2);
+ SET_FIELD(DIS_PRECHARGE, 1);
for (i = 0; i < cmd->msg_len; i++) {
wait_dis(state, 0x40, 0x00);
- write_reg(state, RSTV0910_P1_DISTXFIFO + offs, cmd->msg[i]);
+ SET_REG(DISTXFIFO, cmd->msg[i]);
}
- write_reg(state, RSTV0910_P1_DISTXCFG + offs, 0x3A);
+ SET_FIELD(DIS_PRECHARGE, 0);
wait_dis(state, 0x20, 0x20);
return 0;
}
@@ -1689,19 +1689,20 @@ static int send_master_cmd(struct dvb_frontend *fe,
static int send_burst(struct dvb_frontend *fe, enum fe_sec_mini_cmd burst)
{
struct stv *state = fe->demodulator_priv;
- u16 offs = state->nr ? 0x40 : 0;
u8 value;
if (burst == SEC_MINI_A) {
- write_reg(state, RSTV0910_P1_DISTXCFG + offs, 0x3F);
+ SET_FIELD(DISEQC_MODE, 3);
value = 0x00;
} else {
- write_reg(state, RSTV0910_P1_DISTXCFG + offs, 0x3E);
+ SET_FIELD(DISEQC_MODE, 2);
value = 0xFF;
}
+
+ SET_FIELD(DIS_PRECHARGE, 1);
wait_dis(state, 0x40, 0x00);
- write_reg(state, RSTV0910_P1_DISTXFIFO + offs, value);
- write_reg(state, RSTV0910_P1_DISTXCFG + offs, 0x3A);
+ SET_REG(DISTXFIFO, value);
+ SET_FIELD(DIS_PRECHARGE, 0);
wait_dis(state, 0x20, 0x20);
return 0;
diff --git a/drivers/media/dvb-frontends/ves1820.c b/drivers/media/dvb-frontends/ves1820.c
index 1d8979289915..17600989f121 100644
--- a/drivers/media/dvb-frontends/ves1820.c
+++ b/drivers/media/dvb-frontends/ves1820.c
@@ -137,7 +137,7 @@ static int ves1820_set_symbolrate(struct ves1820_state *state, u32 symbolrate)
NDEC = 3;
/* yeuch! */
- fpxin = state->config->xin * 10;
+ fpxin = state->config->xin * 10ULL;
fptmp = fpxin; do_div(fptmp, 123);
if (symbolrate < fptmp)
SFIL = 1;
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 9f18cd296841..541f0d28afd8 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -56,6 +56,17 @@ config VIDEO_TDA9840
To compile this driver as a module, choose M here: the
module will be called tda9840.
+config VIDEO_TDA1997X
+ tristate "NXP TDA1997x HDMI receiver"
+ depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
+ depends on SND_SOC
+ select SND_PCM
+ ---help---
+ V4L2 subdevice driver for the NXP TDA1997x HDMI receivers.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tda1997x.
+
config VIDEO_TEA6415C
tristate "Philips TEA6415C audio processor"
depends on I2C
@@ -423,6 +434,15 @@ config VIDEO_TW9906
To compile this driver as a module, choose M here: the
module will be called tw9906.
+config VIDEO_TW9910
+ tristate "Techwell TW9910 video decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for Techwell TW9910 NTSC/PAL/SECAM video decoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tw9910.
+
config VIDEO_VPX3220
tristate "vpx3220a, vpx3216b & vpx3214c video decoders"
depends on VIDEO_V4L2 && I2C
@@ -586,6 +606,18 @@ config VIDEO_OV2659
To compile this driver as a module, choose M here: the
module will be called ov2659.
+config VIDEO_OV2685
+ tristate "OmniVision OV2685 sensor support"
+ depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER
+ depends on MEDIA_CAMERA_SUPPORT
+ select V4L2_FWNODE
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the OmniVision
+ OV2685 camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ov2685.
+
config VIDEO_OV5640
tristate "OmniVision OV5640 sensor support"
depends on OF
@@ -645,6 +677,28 @@ config VIDEO_OV5670
To compile this driver as a module, choose M here: the
module will be called ov5670.
+config VIDEO_OV5695
+ tristate "OmniVision OV5695 sensor support"
+ depends on I2C && VIDEO_V4L2
+ depends on MEDIA_CAMERA_SUPPORT
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the OmniVision
+ OV5695 camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ov5695.
+
+config VIDEO_OV772X
+ tristate "OmniVision OV772x sensor support"
+ depends on I2C && VIDEO_V4L2
+ depends on MEDIA_CAMERA_SUPPORT
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the OmniVision
+ OV772x camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ov772x.
+
config VIDEO_OV7640
tristate "OmniVision OV7640 sensor support"
depends on I2C && VIDEO_V4L2
@@ -660,6 +714,7 @@ config VIDEO_OV7670
tristate "OmniVision OV7670 sensor support"
depends on I2C && VIDEO_V4L2
depends on MEDIA_CAMERA_SUPPORT
+ select V4L2_FWNODE
---help---
This is a Video4Linux2 sensor-level driver for the OmniVision
OV7670 VGA camera. It currently only works with the M88ALP01
@@ -733,6 +788,17 @@ config VIDEO_MT9T001
This is a Video4Linux2 sensor-level driver for the Aptina
(Micron) mt0t001 3 Mpixel camera.
+config VIDEO_MT9T112
+ tristate "Aptina MT9T111/MT9T112 support"
+ depends on I2C && VIDEO_V4L2
+ depends on MEDIA_CAMERA_SUPPORT
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the Aptina
+ (Micron) MT9T111 and MT9T112 3 Mpixel camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mt9t112.
+
config VIDEO_MT9V011
tristate "Micron mt9v011 sensor support"
depends on I2C && VIDEO_V4L2
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index c0f94cd8d56d..ea34aee1a85a 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o
obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o
obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o
obj-$(CONFIG_VIDEO_TDA9840) += tda9840.o
+obj-$(CONFIG_VIDEO_TDA1997X) += tda1997x.o
obj-$(CONFIG_VIDEO_TEA6415C) += tea6415c.o
obj-$(CONFIG_VIDEO_TEA6420) += tea6420.o
obj-$(CONFIG_VIDEO_SAA7110) += saa7110.o
@@ -48,6 +49,7 @@ obj-$(CONFIG_VIDEO_TVP7002) += tvp7002.o
obj-$(CONFIG_VIDEO_TW2804) += tw2804.o
obj-$(CONFIG_VIDEO_TW9903) += tw9903.o
obj-$(CONFIG_VIDEO_TW9906) += tw9906.o
+obj-$(CONFIG_VIDEO_TW9910) += tw9910.o
obj-$(CONFIG_VIDEO_CS3308) += cs3308.o
obj-$(CONFIG_VIDEO_CS5345) += cs5345.o
obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o
@@ -61,13 +63,16 @@ obj-$(CONFIG_VIDEO_SONY_BTF_MPX) += sony-btf-mpx.o
obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o
obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
obj-$(CONFIG_VIDEO_OV2640) += ov2640.o
+obj-$(CONFIG_VIDEO_OV2685) += ov2685.o
obj-$(CONFIG_VIDEO_OV5640) += ov5640.o
obj-$(CONFIG_VIDEO_OV5645) += ov5645.o
obj-$(CONFIG_VIDEO_OV5647) += ov5647.o
obj-$(CONFIG_VIDEO_OV5670) += ov5670.o
+obj-$(CONFIG_VIDEO_OV5695) += ov5695.o
obj-$(CONFIG_VIDEO_OV6650) += ov6650.o
obj-$(CONFIG_VIDEO_OV7640) += ov7640.o
obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
+obj-$(CONFIG_VIDEO_OV772X) += ov772x.o
obj-$(CONFIG_VIDEO_OV7740) += ov7740.o
obj-$(CONFIG_VIDEO_OV9650) += ov9650.o
obj-$(CONFIG_VIDEO_OV13858) += ov13858.o
@@ -75,6 +80,7 @@ obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o
obj-$(CONFIG_VIDEO_MT9M111) += mt9m111.o
obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o
obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o
+obj-$(CONFIG_VIDEO_MT9T112) += mt9t112.o
obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o
obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o
diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c
index a056d6cdaaaa..91ff06088572 100644
--- a/drivers/media/i2c/ad9389b.c
+++ b/drivers/media/i2c/ad9389b.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Analog Devices AD9389B/AD9889B video encoder driver
*
* Copyright 2012 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
/*
diff --git a/drivers/media/i2c/adv748x/adv748x-afe.c b/drivers/media/i2c/adv748x/adv748x-afe.c
index 5188178588c9..61514bae7e5c 100644
--- a/drivers/media/i2c/adv748x/adv748x-afe.c
+++ b/drivers/media/i2c/adv748x/adv748x-afe.c
@@ -275,7 +275,8 @@ static int adv748x_afe_s_stream(struct v4l2_subdev *sd, int enable)
{
struct adv748x_afe *afe = adv748x_sd_to_afe(sd);
struct adv748x_state *state = adv748x_afe_to_state(afe);
- int ret, signal = V4L2_IN_ST_NO_SIGNAL;
+ u32 signal = V4L2_IN_ST_NO_SIGNAL;
+ int ret;
mutex_lock(&state->mutex);
diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c
index fd92c9e4b519..6ca88daa0ecd 100644
--- a/drivers/media/i2c/adv748x/adv748x-core.c
+++ b/drivers/media/i2c/adv748x/adv748x-core.c
@@ -35,96 +35,28 @@
* Register manipulation
*/
-static const struct regmap_config adv748x_regmap_cnf[] = {
- {
- .name = "io",
- .reg_bits = 8,
- .val_bits = 8,
-
- .max_register = 0xff,
- .cache_type = REGCACHE_NONE,
- },
- {
- .name = "dpll",
- .reg_bits = 8,
- .val_bits = 8,
-
- .max_register = 0xff,
- .cache_type = REGCACHE_NONE,
- },
- {
- .name = "cp",
- .reg_bits = 8,
- .val_bits = 8,
-
- .max_register = 0xff,
- .cache_type = REGCACHE_NONE,
- },
- {
- .name = "hdmi",
- .reg_bits = 8,
- .val_bits = 8,
-
- .max_register = 0xff,
- .cache_type = REGCACHE_NONE,
- },
- {
- .name = "edid",
- .reg_bits = 8,
- .val_bits = 8,
-
- .max_register = 0xff,
- .cache_type = REGCACHE_NONE,
- },
- {
- .name = "repeater",
- .reg_bits = 8,
- .val_bits = 8,
-
- .max_register = 0xff,
- .cache_type = REGCACHE_NONE,
- },
- {
- .name = "infoframe",
- .reg_bits = 8,
- .val_bits = 8,
-
- .max_register = 0xff,
- .cache_type = REGCACHE_NONE,
- },
- {
- .name = "cec",
- .reg_bits = 8,
- .val_bits = 8,
-
- .max_register = 0xff,
- .cache_type = REGCACHE_NONE,
- },
- {
- .name = "sdp",
- .reg_bits = 8,
- .val_bits = 8,
-
- .max_register = 0xff,
- .cache_type = REGCACHE_NONE,
- },
-
- {
- .name = "txb",
- .reg_bits = 8,
- .val_bits = 8,
-
- .max_register = 0xff,
- .cache_type = REGCACHE_NONE,
- },
- {
- .name = "txa",
- .reg_bits = 8,
- .val_bits = 8,
+#define ADV748X_REGMAP_CONF(n) \
+{ \
+ .name = n, \
+ .reg_bits = 8, \
+ .val_bits = 8, \
+ .max_register = 0xff, \
+ .cache_type = REGCACHE_NONE, \
+}
- .max_register = 0xff,
- .cache_type = REGCACHE_NONE,
- },
+static const struct regmap_config adv748x_regmap_cnf[] = {
+ ADV748X_REGMAP_CONF("io"),
+ ADV748X_REGMAP_CONF("dpll"),
+ ADV748X_REGMAP_CONF("cp"),
+ ADV748X_REGMAP_CONF("hdmi"),
+ ADV748X_REGMAP_CONF("edid"),
+ ADV748X_REGMAP_CONF("repeater"),
+ ADV748X_REGMAP_CONF("infoframe"),
+ ADV748X_REGMAP_CONF("cbus"),
+ ADV748X_REGMAP_CONF("cec"),
+ ADV748X_REGMAP_CONF("sdp"),
+ ADV748X_REGMAP_CONF("txa"),
+ ADV748X_REGMAP_CONF("txb"),
};
static int adv748x_configure_regmap(struct adv748x_state *state, int region)
@@ -148,20 +80,24 @@ static int adv748x_configure_regmap(struct adv748x_state *state, int region)
return 0;
}
+struct adv748x_register_map {
+ const char *name;
+ u8 default_addr;
+};
-/* Default addresses for the I2C pages */
-static int adv748x_i2c_addresses[ADV748X_PAGE_MAX] = {
- ADV748X_I2C_IO,
- ADV748X_I2C_DPLL,
- ADV748X_I2C_CP,
- ADV748X_I2C_HDMI,
- ADV748X_I2C_EDID,
- ADV748X_I2C_REPEATER,
- ADV748X_I2C_INFOFRAME,
- ADV748X_I2C_CEC,
- ADV748X_I2C_SDP,
- ADV748X_I2C_TXB,
- ADV748X_I2C_TXA,
+static const struct adv748x_register_map adv748x_default_addresses[] = {
+ [ADV748X_PAGE_IO] = { "main", 0x70 },
+ [ADV748X_PAGE_DPLL] = { "dpll", 0x26 },
+ [ADV748X_PAGE_CP] = { "cp", 0x22 },
+ [ADV748X_PAGE_HDMI] = { "hdmi", 0x34 },
+ [ADV748X_PAGE_EDID] = { "edid", 0x36 },
+ [ADV748X_PAGE_REPEATER] = { "repeater", 0x32 },
+ [ADV748X_PAGE_INFOFRAME] = { "infoframe", 0x31 },
+ [ADV748X_PAGE_CBUS] = { "cbus", 0x30 },
+ [ADV748X_PAGE_CEC] = { "cec", 0x41 },
+ [ADV748X_PAGE_SDP] = { "sdp", 0x79 },
+ [ADV748X_PAGE_TXB] = { "txb", 0x48 },
+ [ADV748X_PAGE_TXA] = { "txa", 0x4a },
};
static int adv748x_read_check(struct adv748x_state *state,
@@ -210,15 +146,20 @@ int adv748x_write_block(struct adv748x_state *state, int client_page,
return regmap_raw_write(regmap, init_reg, val, val_len);
}
-static struct i2c_client *adv748x_dummy_client(struct adv748x_state *state,
- u8 addr, u8 io_reg)
+static int adv748x_set_slave_addresses(struct adv748x_state *state)
{
- struct i2c_client *client = state->client;
+ struct i2c_client *client;
+ unsigned int i;
+ u8 io_reg;
- if (addr)
- io_write(state, io_reg, addr << 1);
+ for (i = ADV748X_PAGE_DPLL; i < ADV748X_PAGE_MAX; ++i) {
+ io_reg = ADV748X_IO_SLAVE_ADDR_BASE + i;
+ client = state->i2c_clients[i];
+
+ io_write(state, io_reg, client->addr << 1);
+ }
- return i2c_new_dummy(client->adapter, io_read(state, io_reg) >> 1);
+ return 0;
}
static void adv748x_unregister_clients(struct adv748x_state *state)
@@ -231,13 +172,15 @@ static void adv748x_unregister_clients(struct adv748x_state *state)
static int adv748x_initialise_clients(struct adv748x_state *state)
{
- int i;
+ unsigned int i;
int ret;
for (i = ADV748X_PAGE_DPLL; i < ADV748X_PAGE_MAX; ++i) {
- state->i2c_clients[i] =
- adv748x_dummy_client(state, adv748x_i2c_addresses[i],
- ADV748X_IO_SLAVE_ADDR_BASE + i);
+ state->i2c_clients[i] = i2c_new_secondary_device(
+ state->client,
+ adv748x_default_addresses[i].name,
+ adv748x_default_addresses[i].default_addr);
+
if (state->i2c_clients[i] == NULL) {
adv_err(state, "failed to create i2c client %u\n", i);
return -ENOMEM;
@@ -248,7 +191,7 @@ static int adv748x_initialise_clients(struct adv748x_state *state)
return ret;
}
- return 0;
+ return adv748x_set_slave_addresses(state);
}
/**
@@ -414,20 +357,6 @@ static const struct adv748x_reg_value adv748x_sw_reset[] = {
{ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */
};
-static const struct adv748x_reg_value adv748x_set_slave_address[] = {
- {ADV748X_PAGE_IO, 0xf3, ADV748X_I2C_DPLL << 1},
- {ADV748X_PAGE_IO, 0xf4, ADV748X_I2C_CP << 1},
- {ADV748X_PAGE_IO, 0xf5, ADV748X_I2C_HDMI << 1},
- {ADV748X_PAGE_IO, 0xf6, ADV748X_I2C_EDID << 1},
- {ADV748X_PAGE_IO, 0xf7, ADV748X_I2C_REPEATER << 1},
- {ADV748X_PAGE_IO, 0xf8, ADV748X_I2C_INFOFRAME << 1},
- {ADV748X_PAGE_IO, 0xfa, ADV748X_I2C_CEC << 1},
- {ADV748X_PAGE_IO, 0xfb, ADV748X_I2C_SDP << 1},
- {ADV748X_PAGE_IO, 0xfc, ADV748X_I2C_TXB << 1},
- {ADV748X_PAGE_IO, 0xfd, ADV748X_I2C_TXA << 1},
- {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */
-};
-
/* Supported Formats For Script Below */
/* - 01-29 HDMI to MIPI TxA CSI 4-Lane - RGB888: */
static const struct adv748x_reg_value adv748x_init_txa_4lane[] = {
@@ -558,7 +487,7 @@ static int adv748x_reset(struct adv748x_state *state)
if (ret < 0)
return ret;
- ret = adv748x_write_regs(state, adv748x_set_slave_address);
+ ret = adv748x_set_slave_addresses(state);
if (ret < 0)
return ret;
@@ -715,7 +644,7 @@ static int adv748x_probe(struct i2c_client *client,
ret = adv748x_identify_chip(state);
if (ret) {
adv_err(state, "Failed to identify chip");
- goto err_cleanup_clients;
+ goto err_cleanup_dt;
}
/* Configure remaining pages as I2C clients with regmap access */
diff --git a/drivers/media/i2c/adv748x/adv748x-hdmi.c b/drivers/media/i2c/adv748x/adv748x-hdmi.c
index 4da4253553fc..10d229a4f088 100644
--- a/drivers/media/i2c/adv748x/adv748x-hdmi.c
+++ b/drivers/media/i2c/adv748x/adv748x-hdmi.c
@@ -105,6 +105,9 @@ static void adv748x_hdmi_fill_format(struct adv748x_hdmi *hdmi,
fmt->width = hdmi->timings.bt.width;
fmt->height = hdmi->timings.bt.height;
+
+ if (fmt->field == V4L2_FIELD_ALTERNATE)
+ fmt->height /= 2;
}
static void adv748x_fill_optional_dv_timings(struct v4l2_dv_timings *timings)
diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h
index 6789e2f3bc8c..65f83741277e 100644
--- a/drivers/media/i2c/adv748x/adv748x.h
+++ b/drivers/media/i2c/adv748x/adv748x.h
@@ -27,19 +27,6 @@
#ifndef _ADV748X_H_
#define _ADV748X_H_
-/* I2C slave addresses */
-#define ADV748X_I2C_IO 0x70 /* IO Map */
-#define ADV748X_I2C_DPLL 0x26 /* DPLL Map */
-#define ADV748X_I2C_CP 0x22 /* CP Map */
-#define ADV748X_I2C_HDMI 0x34 /* HDMI Map */
-#define ADV748X_I2C_EDID 0x36 /* EDID Map */
-#define ADV748X_I2C_REPEATER 0x32 /* HDMI RX Repeater Map */
-#define ADV748X_I2C_INFOFRAME 0x31 /* HDMI RX InfoFrame Map */
-#define ADV748X_I2C_CEC 0x41 /* CEC Map */
-#define ADV748X_I2C_SDP 0x79 /* SDP Map */
-#define ADV748X_I2C_TXB 0x48 /* CSI-TXB Map */
-#define ADV748X_I2C_TXA 0x4a /* CSI-TXA Map */
-
enum adv748x_page {
ADV748X_PAGE_IO,
ADV748X_PAGE_DPLL,
@@ -48,6 +35,7 @@ enum adv748x_page {
ADV748X_PAGE_EDID,
ADV748X_PAGE_REPEATER,
ADV748X_PAGE_INFOFRAME,
+ ADV748X_PAGE_CBUS,
ADV748X_PAGE_CEC,
ADV748X_PAGE_SDP,
ADV748X_PAGE_TXB,
diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c
index 2817bafc67bf..d23505a411ee 100644
--- a/drivers/media/i2c/adv7511.c
+++ b/drivers/media/i2c/adv7511.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Analog Devices ADV7511 HDMI Transmitter Device Driver
*
* Copyright 2013 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index 1544920ec52d..cac2081e876e 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -1,21 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* adv7604 - Analog Devices ADV7604 video decoder driver
*
* Copyright 2012 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
*
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
- *
*/
/*
@@ -2734,6 +2722,27 @@ static const struct v4l2_ctrl_config adv76xx_ctrl_free_run_color = {
/* ----------------------------------------------------------------------- */
+struct adv76xx_register_map {
+ const char *name;
+ u8 default_addr;
+};
+
+static const struct adv76xx_register_map adv76xx_default_addresses[] = {
+ [ADV76XX_PAGE_IO] = { "main", 0x4c },
+ [ADV7604_PAGE_AVLINK] = { "avlink", 0x42 },
+ [ADV76XX_PAGE_CEC] = { "cec", 0x40 },
+ [ADV76XX_PAGE_INFOFRAME] = { "infoframe", 0x3e },
+ [ADV7604_PAGE_ESDP] = { "esdp", 0x38 },
+ [ADV7604_PAGE_DPP] = { "dpp", 0x3c },
+ [ADV76XX_PAGE_AFE] = { "afe", 0x26 },
+ [ADV76XX_PAGE_REP] = { "rep", 0x32 },
+ [ADV76XX_PAGE_EDID] = { "edid", 0x36 },
+ [ADV76XX_PAGE_HDMI] = { "hdmi", 0x34 },
+ [ADV76XX_PAGE_TEST] = { "test", 0x30 },
+ [ADV76XX_PAGE_CP] = { "cp", 0x22 },
+ [ADV7604_PAGE_VDP] = { "vdp", 0x24 },
+};
+
static int adv76xx_core_init(struct v4l2_subdev *sd)
{
struct adv76xx_state *state = to_state(sd);
@@ -2834,13 +2843,26 @@ static void adv76xx_unregister_clients(struct adv76xx_state *state)
}
static struct i2c_client *adv76xx_dummy_client(struct v4l2_subdev *sd,
- u8 addr, u8 io_reg)
+ unsigned int page)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct adv76xx_state *state = to_state(sd);
+ struct adv76xx_platform_data *pdata = &state->pdata;
+ unsigned int io_reg = 0xf2 + page;
+ struct i2c_client *new_client;
+
+ if (pdata && pdata->i2c_addresses[page])
+ new_client = i2c_new_dummy(client->adapter,
+ pdata->i2c_addresses[page]);
+ else
+ new_client = i2c_new_secondary_device(client,
+ adv76xx_default_addresses[page].name,
+ adv76xx_default_addresses[page].default_addr);
+
+ if (new_client)
+ io_write(sd, io_reg, new_client->addr << 1);
- if (addr)
- io_write(sd, io_reg, addr << 1);
- return i2c_new_dummy(client->adapter, io_read(sd, io_reg) >> 1);
+ return new_client;
}
static const struct adv76xx_reg_seq adv7604_recommended_settings_afe[] = {
@@ -3115,20 +3137,6 @@ static int adv76xx_parse_dt(struct adv76xx_state *state)
/* Disable the interrupt for now as no DT-based board uses it. */
state->pdata.int1_config = ADV76XX_INT1_CONFIG_DISABLED;
- /* Use the default I2C addresses. */
- state->pdata.i2c_addresses[ADV7604_PAGE_AVLINK] = 0x42;
- state->pdata.i2c_addresses[ADV76XX_PAGE_CEC] = 0x40;
- state->pdata.i2c_addresses[ADV76XX_PAGE_INFOFRAME] = 0x3e;
- state->pdata.i2c_addresses[ADV7604_PAGE_ESDP] = 0x38;
- state->pdata.i2c_addresses[ADV7604_PAGE_DPP] = 0x3c;
- state->pdata.i2c_addresses[ADV76XX_PAGE_AFE] = 0x26;
- state->pdata.i2c_addresses[ADV76XX_PAGE_REP] = 0x32;
- state->pdata.i2c_addresses[ADV76XX_PAGE_EDID] = 0x36;
- state->pdata.i2c_addresses[ADV76XX_PAGE_HDMI] = 0x34;
- state->pdata.i2c_addresses[ADV76XX_PAGE_TEST] = 0x30;
- state->pdata.i2c_addresses[ADV76XX_PAGE_CP] = 0x22;
- state->pdata.i2c_addresses[ADV7604_PAGE_VDP] = 0x24;
-
/* Hardcode the remaining platform data fields. */
state->pdata.disable_pwrdnb = 0;
state->pdata.disable_cable_det_rst = 0;
@@ -3478,11 +3486,9 @@ static int adv76xx_probe(struct i2c_client *client,
if (!(BIT(i) & state->info->page_mask))
continue;
- state->i2c_clients[i] =
- adv76xx_dummy_client(sd, state->pdata.i2c_addresses[i],
- 0xf2 + i);
+ state->i2c_clients[i] = adv76xx_dummy_client(sd, i);
if (!state->i2c_clients[i]) {
- err = -ENOMEM;
+ err = -EINVAL;
v4l2_err(sd, "failed to create i2c client %u\n", i);
goto err_i2c;
}
diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c
index 136aa80a834b..fddac32e5051 100644
--- a/drivers/media/i2c/adv7842.c
+++ b/drivers/media/i2c/adv7842.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* adv7842 - Analog Devices ADV7842 video decoder driver
*
* Copyright 2013 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
- *
*/
/*
diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c
index 98be63ae8590..b168bf3635b6 100644
--- a/drivers/media/i2c/cx25840/cx25840-core.c
+++ b/drivers/media/i2c/cx25840/cx25840-core.c
@@ -463,8 +463,13 @@ static void cx23885_initialize(struct i2c_client *client)
{
DEFINE_WAIT(wait);
struct cx25840_state *state = to_state(i2c_get_clientdata(client));
+ u32 clk_freq = 0;
struct workqueue_struct *q;
+ /* cx23885 sets hostdata to clk_freq pointer */
+ if (v4l2_get_subdev_hostdata(&state->sd))
+ clk_freq = *((u32 *)v4l2_get_subdev_hostdata(&state->sd));
+
/*
* Come out of digital power down
* The CX23888, at least, needs this, otherwise registers aside from
@@ -500,8 +505,13 @@ static void cx23885_initialize(struct i2c_client *client)
* 50.0 MHz * (0xb + 0xe8ba26/0x2000000)/4 = 5 * 28.636363 MHz
* 572.73 MHz before post divide
*/
- /* HVR1850 or 50MHz xtal */
- cx25840_write(client, 0x2, 0x71);
+ if (clk_freq == 25000000) {
+ /* 888/ImpactVCBe or 25Mhz xtal */
+ ; /* nothing to do */
+ } else {
+ /* HVR1850 or 50MHz xtal */
+ cx25840_write(client, 0x2, 0x71);
+ }
cx25840_write4(client, 0x11c, 0x01d1744c);
cx25840_write4(client, 0x118, 0x00000416);
cx25840_write4(client, 0x404, 0x0010253e);
@@ -544,9 +554,15 @@ static void cx23885_initialize(struct i2c_client *client)
/* HVR1850 */
switch (state->id) {
case CX23888_AV:
- /* 888/HVR1250 specific */
- cx25840_write4(client, 0x10c, 0x13333333);
- cx25840_write4(client, 0x108, 0x00000515);
+ if (clk_freq == 25000000) {
+ /* 888/ImpactVCBe or 25MHz xtal */
+ cx25840_write4(client, 0x10c, 0x01b6db7b);
+ cx25840_write4(client, 0x108, 0x00000512);
+ } else {
+ /* 888/HVR1250 or 50MHz xtal */
+ cx25840_write4(client, 0x10c, 0x13333333);
+ cx25840_write4(client, 0x108, 0x00000515);
+ }
break;
default:
cx25840_write4(client, 0x10c, 0x002be2c9);
@@ -576,7 +592,7 @@ static void cx23885_initialize(struct i2c_client *client)
* 368.64 MHz before post divide
* 122.88 MHz / 0xa = 12.288 MHz
*/
- /* HVR1850 or 50MHz xtal */
+ /* HVR1850 or 50MHz xtal or 25MHz xtal */
cx25840_write4(client, 0x114, 0x017dbf48);
cx25840_write4(client, 0x110, 0x000a030e);
break;
diff --git a/drivers/media/i2c/dw9714.c b/drivers/media/i2c/dw9714.c
index 8dbbf0f917df..91fae01d052b 100644
--- a/drivers/media/i2c/dw9714.c
+++ b/drivers/media/i2c/dw9714.c
@@ -1,15 +1,5 @@
-/*
- * Copyright (c) 2015--2017 Intel Corporation.
- *
- * 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.
- *
- * 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.
- */
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2015--2017 Intel Corporation.
#include <linux/delay.h>
#include <linux/i2c.h>
diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c
index 664e8acdf2a0..daec33f4196a 100644
--- a/drivers/media/i2c/imx274.c
+++ b/drivers/media/i2c/imx274.c
@@ -1426,7 +1426,7 @@ static int imx274_set_vflip(struct stimx274 *priv, int val)
err = imx274_write_reg(priv, IMX274_VFLIP_REG, val);
if (err) {
- dev_err(&priv->client->dev, "VFILP control error\n");
+ dev_err(&priv->client->dev, "VFLIP control error\n");
return err;
}
diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c
index 193020d64e51..a7e23bcf845c 100644
--- a/drivers/media/i2c/ir-kbd-i2c.c
+++ b/drivers/media/i2c/ir-kbd-i2c.c
@@ -168,11 +168,15 @@ static int get_key_haup_xvr(struct IR_i2c *ir, enum rc_proto *protocol,
static int get_key_pixelview(struct IR_i2c *ir, enum rc_proto *protocol,
u32 *scancode, u8 *toggle)
{
+ int rc;
unsigned char b;
/* poll IR chip */
- if (1 != i2c_master_recv(ir->c, &b, 1)) {
+ rc = i2c_master_recv(ir->c, &b, 1);
+ if (rc != 1) {
dev_dbg(&ir->rc->dev, "read error\n");
+ if (rc < 0)
+ return rc;
return -EIO;
}
@@ -185,11 +189,15 @@ static int get_key_pixelview(struct IR_i2c *ir, enum rc_proto *protocol,
static int get_key_fusionhdtv(struct IR_i2c *ir, enum rc_proto *protocol,
u32 *scancode, u8 *toggle)
{
+ int rc;
unsigned char buf[4];
/* poll IR chip */
- if (4 != i2c_master_recv(ir->c, buf, 4)) {
+ rc = i2c_master_recv(ir->c, buf, 4);
+ if (rc != 4) {
dev_dbg(&ir->rc->dev, "read error\n");
+ if (rc < 0)
+ return rc;
return -EIO;
}
@@ -209,11 +217,15 @@ static int get_key_fusionhdtv(struct IR_i2c *ir, enum rc_proto *protocol,
static int get_key_knc1(struct IR_i2c *ir, enum rc_proto *protocol,
u32 *scancode, u8 *toggle)
{
+ int rc;
unsigned char b;
/* poll IR chip */
- if (1 != i2c_master_recv(ir->c, &b, 1)) {
+ rc = i2c_master_recv(ir->c, &b, 1);
+ if (rc != 1) {
dev_dbg(&ir->rc->dev, "read error\n");
+ if (rc < 0)
+ return rc;
return -EIO;
}
@@ -571,7 +583,7 @@ static int zilog_ir_format(struct rc_dev *rcdev, unsigned int *txbuf,
/* first copy any leading non-repeating */
int leading = c - rep * 3;
- if (leading + rep >= ARRAY_SIZE(code_block->codes) - 3) {
+ if (leading >= ARRAY_SIZE(code_block->codes) - 3 - rep) {
dev_warn(&rcdev->dev, "IR too long, cannot transmit\n");
return -EINVAL;
}
diff --git a/drivers/media/i2c/max2175.c b/drivers/media/i2c/max2175.c
index 2f1966bdc473..87cba15b2977 100644
--- a/drivers/media/i2c/max2175.c
+++ b/drivers/media/i2c/max2175.c
@@ -643,7 +643,7 @@ static int max2175_set_nco_freq(struct max2175 *ctx, s32 nco_freq)
if (abs_nco_freq < clock_rate / 2) {
nco_val_desired = 2 * nco_freq;
} else {
- nco_val_desired = 2 * (clock_rate - abs_nco_freq);
+ nco_val_desired = 2LL * (clock_rate - abs_nco_freq);
if (nco_freq < 0)
nco_val_desired = -nco_val_desired;
}
diff --git a/drivers/media/i2c/msp3400-kthreads.c b/drivers/media/i2c/msp3400-kthreads.c
index 4dd01e9f553b..dc6cb8d475b3 100644
--- a/drivers/media/i2c/msp3400-kthreads.c
+++ b/drivers/media/i2c/msp3400-kthreads.c
@@ -885,7 +885,7 @@ static int msp34xxg_modus(struct i2c_client *client)
}
static void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in)
- {
+{
struct msp_state *state = to_state(i2c_get_clientdata(client));
int source, matrix;
diff --git a/drivers/media/i2c/mt9t112.c b/drivers/media/i2c/mt9t112.c
new file mode 100644
index 000000000000..af8cca984215
--- /dev/null
+++ b/drivers/media/i2c/mt9t112.c
@@ -0,0 +1,1140 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mt9t112 Camera Driver
+ *
+ * Copyright (C) 2018 Jacopo Mondi <jacopo+renesas@jmondi.org>
+ *
+ * Copyright (C) 2009 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on ov772x driver, mt9m111 driver,
+ *
+ * Copyright (C) 2008 Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ * Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr>
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
+ * Copyright (C) 2008 Magnus Damm
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * TODO: This driver lacks support for frame rate control due to missing
+ * register level documentation and suitable hardware for testing.
+ * v4l-utils compliance tools will report errors.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/videodev2.h>
+
+#include <media/i2c/mt9t112.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-image-sizes.h>
+#include <media/v4l2-subdev.h>
+
+/* you can check PLL/clock info */
+/* #define EXT_CLOCK 24000000 */
+
+/************************************************************************
+ * macro
+ ***********************************************************************/
+/*
+ * frame size
+ */
+#define MAX_WIDTH 2048
+#define MAX_HEIGHT 1536
+
+/*
+ * macro of read/write
+ */
+#define ECHECKER(ret, x) \
+ do { \
+ (ret) = (x); \
+ if ((ret) < 0) \
+ return (ret); \
+ } while (0)
+
+#define mt9t112_reg_write(ret, client, a, b) \
+ ECHECKER(ret, __mt9t112_reg_write(client, a, b))
+#define mt9t112_mcu_write(ret, client, a, b) \
+ ECHECKER(ret, __mt9t112_mcu_write(client, a, b))
+
+#define mt9t112_reg_mask_set(ret, client, a, b, c) \
+ ECHECKER(ret, __mt9t112_reg_mask_set(client, a, b, c))
+#define mt9t112_mcu_mask_set(ret, client, a, b, c) \
+ ECHECKER(ret, __mt9t112_mcu_mask_set(client, a, b, c))
+
+#define mt9t112_reg_read(ret, client, a) \
+ ECHECKER(ret, __mt9t112_reg_read(client, a))
+
+/*
+ * Logical address
+ */
+#define _VAR(id, offset, base) (base | (id & 0x1f) << 10 | (offset & 0x3ff))
+#define VAR(id, offset) _VAR(id, offset, 0x0000)
+#define VAR8(id, offset) _VAR(id, offset, 0x8000)
+
+/************************************************************************
+ * struct
+ ***********************************************************************/
+struct mt9t112_format {
+ u32 code;
+ enum v4l2_colorspace colorspace;
+ u16 fmt;
+ u16 order;
+};
+
+struct mt9t112_priv {
+ struct v4l2_subdev subdev;
+ struct mt9t112_platform_data *info;
+ struct i2c_client *client;
+ struct v4l2_rect frame;
+ struct clk *clk;
+ struct gpio_desc *standby_gpio;
+ const struct mt9t112_format *format;
+ int num_formats;
+ bool init_done;
+};
+
+/************************************************************************
+ * supported format
+ ***********************************************************************/
+
+static const struct mt9t112_format mt9t112_cfmts[] = {
+ {
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .fmt = 1,
+ .order = 0,
+ }, {
+ .code = MEDIA_BUS_FMT_VYUY8_2X8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .fmt = 1,
+ .order = 1,
+ }, {
+ .code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .fmt = 1,
+ .order = 2,
+ }, {
+ .code = MEDIA_BUS_FMT_YVYU8_2X8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .fmt = 1,
+ .order = 3,
+ }, {
+ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .fmt = 8,
+ .order = 2,
+ }, {
+ .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .fmt = 4,
+ .order = 2,
+ },
+};
+
+/************************************************************************
+ * general function
+ ***********************************************************************/
+static struct mt9t112_priv *to_mt9t112(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client),
+ struct mt9t112_priv,
+ subdev);
+}
+
+static int __mt9t112_reg_read(const struct i2c_client *client, u16 command)
+{
+ struct i2c_msg msg[2];
+ u8 buf[2];
+ int ret;
+
+ command = swab16(command);
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 2;
+ msg[0].buf = (u8 *)&command;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = 2;
+ msg[1].buf = buf;
+
+ /*
+ * If return value of this function is < 0, it means error, else,
+ * below 16bit is valid data.
+ */
+ ret = i2c_transfer(client->adapter, msg, 2);
+ if (ret < 0)
+ return ret;
+
+ memcpy(&ret, buf, 2);
+
+ return swab16(ret);
+}
+
+static int __mt9t112_reg_write(const struct i2c_client *client,
+ u16 command, u16 data)
+{
+ struct i2c_msg msg;
+ u8 buf[4];
+ int ret;
+
+ command = swab16(command);
+ data = swab16(data);
+
+ memcpy(buf + 0, &command, 2);
+ memcpy(buf + 2, &data, 2);
+
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.len = 4;
+ msg.buf = buf;
+
+ /*
+ * i2c_transfer return message length, but this function should
+ * return 0 if correct case.
+ */
+ ret = i2c_transfer(client->adapter, &msg, 1);
+
+ return ret >= 0 ? 0 : ret;
+}
+
+static int __mt9t112_reg_mask_set(const struct i2c_client *client,
+ u16 command, u16 mask, u16 set)
+{
+ int val = __mt9t112_reg_read(client, command);
+
+ if (val < 0)
+ return val;
+
+ val &= ~mask;
+ val |= set & mask;
+
+ return __mt9t112_reg_write(client, command, val);
+}
+
+/* mcu access */
+static int __mt9t112_mcu_read(const struct i2c_client *client, u16 command)
+{
+ int ret;
+
+ ret = __mt9t112_reg_write(client, 0x098E, command);
+ if (ret < 0)
+ return ret;
+
+ return __mt9t112_reg_read(client, 0x0990);
+}
+
+static int __mt9t112_mcu_write(const struct i2c_client *client,
+ u16 command, u16 data)
+{
+ int ret;
+
+ ret = __mt9t112_reg_write(client, 0x098E, command);
+ if (ret < 0)
+ return ret;
+
+ return __mt9t112_reg_write(client, 0x0990, data);
+}
+
+static int __mt9t112_mcu_mask_set(const struct i2c_client *client,
+ u16 command, u16 mask, u16 set)
+{
+ int val = __mt9t112_mcu_read(client, command);
+
+ if (val < 0)
+ return val;
+
+ val &= ~mask;
+ val |= set & mask;
+
+ return __mt9t112_mcu_write(client, command, val);
+}
+
+static int mt9t112_reset(const struct i2c_client *client)
+{
+ int ret;
+
+ mt9t112_reg_mask_set(ret, client, 0x001a, 0x0001, 0x0001);
+ usleep_range(1000, 5000);
+ mt9t112_reg_mask_set(ret, client, 0x001a, 0x0001, 0x0000);
+
+ return ret;
+}
+
+#ifndef EXT_CLOCK
+#define CLOCK_INFO(a, b)
+#else
+#define CLOCK_INFO(a, b) mt9t112_clock_info(a, b)
+static int mt9t112_clock_info(const struct i2c_client *client, u32 ext)
+{
+ int m, n, p1, p2, p3, p4, p5, p6, p7;
+ u32 vco, clk;
+ char *enable;
+
+ ext /= 1000; /* kbyte order */
+
+ mt9t112_reg_read(n, client, 0x0012);
+ p1 = n & 0x000f;
+ n = n >> 4;
+ p2 = n & 0x000f;
+ n = n >> 4;
+ p3 = n & 0x000f;
+
+ mt9t112_reg_read(n, client, 0x002a);
+ p4 = n & 0x000f;
+ n = n >> 4;
+ p5 = n & 0x000f;
+ n = n >> 4;
+ p6 = n & 0x000f;
+
+ mt9t112_reg_read(n, client, 0x002c);
+ p7 = n & 0x000f;
+
+ mt9t112_reg_read(n, client, 0x0010);
+ m = n & 0x00ff;
+ n = (n >> 8) & 0x003f;
+
+ enable = ((ext < 6000) || (ext > 54000)) ? "X" : "";
+ dev_dbg(&client->dev, "EXTCLK : %10u K %s\n", ext, enable);
+
+ vco = 2 * m * ext / (n + 1);
+ enable = ((vco < 384000) || (vco > 768000)) ? "X" : "";
+ dev_dbg(&client->dev, "VCO : %10u K %s\n", vco, enable);
+
+ clk = vco / (p1 + 1) / (p2 + 1);
+ enable = (clk > 96000) ? "X" : "";
+ dev_dbg(&client->dev, "PIXCLK : %10u K %s\n", clk, enable);
+
+ clk = vco / (p3 + 1);
+ enable = (clk > 768000) ? "X" : "";
+ dev_dbg(&client->dev, "MIPICLK : %10u K %s\n", clk, enable);
+
+ clk = vco / (p6 + 1);
+ enable = (clk > 96000) ? "X" : "";
+ dev_dbg(&client->dev, "MCU CLK : %10u K %s\n", clk, enable);
+
+ clk = vco / (p5 + 1);
+ enable = (clk > 54000) ? "X" : "";
+ dev_dbg(&client->dev, "SOC CLK : %10u K %s\n", clk, enable);
+
+ clk = vco / (p4 + 1);
+ enable = (clk > 70000) ? "X" : "";
+ dev_dbg(&client->dev, "Sensor CLK : %10u K %s\n", clk, enable);
+
+ clk = vco / (p7 + 1);
+ dev_dbg(&client->dev, "External sensor : %10u K\n", clk);
+
+ clk = ext / (n + 1);
+ enable = ((clk < 2000) || (clk > 24000)) ? "X" : "";
+ dev_dbg(&client->dev, "PFD : %10u K %s\n", clk, enable);
+
+ return 0;
+}
+#endif
+
+static int mt9t112_set_a_frame_size(const struct i2c_client *client,
+ u16 width, u16 height)
+{
+ int ret;
+ u16 wstart = (MAX_WIDTH - width) / 2;
+ u16 hstart = (MAX_HEIGHT - height) / 2;
+
+ /* (Context A) Image Width/Height. */
+ mt9t112_mcu_write(ret, client, VAR(26, 0), width);
+ mt9t112_mcu_write(ret, client, VAR(26, 2), height);
+
+ /* (Context A) Output Width/Height. */
+ mt9t112_mcu_write(ret, client, VAR(18, 43), 8 + width);
+ mt9t112_mcu_write(ret, client, VAR(18, 45), 8 + height);
+
+ /* (Context A) Start Row/Column. */
+ mt9t112_mcu_write(ret, client, VAR(18, 2), 4 + hstart);
+ mt9t112_mcu_write(ret, client, VAR(18, 4), 4 + wstart);
+
+ /* (Context A) End Row/Column. */
+ mt9t112_mcu_write(ret, client, VAR(18, 6), 11 + height + hstart);
+ mt9t112_mcu_write(ret, client, VAR(18, 8), 11 + width + wstart);
+
+ mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06);
+
+ return ret;
+}
+
+static int mt9t112_set_pll_dividers(const struct i2c_client *client,
+ u8 m, u8 n, u8 p1, u8 p2, u8 p3, u8 p4,
+ u8 p5, u8 p6, u8 p7)
+{
+ int ret;
+ u16 val;
+
+ /* N/M */
+ val = (n << 8) | (m << 0);
+ mt9t112_reg_mask_set(ret, client, 0x0010, 0x3fff, val);
+
+ /* P1/P2/P3 */
+ val = ((p3 & 0x0F) << 8) | ((p2 & 0x0F) << 4) | ((p1 & 0x0F) << 0);
+ mt9t112_reg_mask_set(ret, client, 0x0012, 0x0fff, val);
+
+ /* P4/P5/P6 */
+ val = (0x7 << 12) | ((p6 & 0x0F) << 8) | ((p5 & 0x0F) << 4) |
+ ((p4 & 0x0F) << 0);
+ mt9t112_reg_mask_set(ret, client, 0x002A, 0x7fff, val);
+
+ /* P7 */
+ val = (0x1 << 12) | ((p7 & 0x0F) << 0);
+ mt9t112_reg_mask_set(ret, client, 0x002C, 0x100f, val);
+
+ return ret;
+}
+
+static int mt9t112_init_pll(const struct i2c_client *client)
+{
+ struct mt9t112_priv *priv = to_mt9t112(client);
+ int data, i, ret;
+
+ mt9t112_reg_mask_set(ret, client, 0x0014, 0x003, 0x0001);
+
+ /* PLL control: BYPASS PLL = 8517. */
+ mt9t112_reg_write(ret, client, 0x0014, 0x2145);
+
+ /* Replace these registers when new timing parameters are generated. */
+ mt9t112_set_pll_dividers(client,
+ priv->info->divider.m, priv->info->divider.n,
+ priv->info->divider.p1, priv->info->divider.p2,
+ priv->info->divider.p3, priv->info->divider.p4,
+ priv->info->divider.p5, priv->info->divider.p6,
+ priv->info->divider.p7);
+
+ /*
+ * TEST_BYPASS on
+ * PLL_ENABLE on
+ * SEL_LOCK_DET on
+ * TEST_BYPASS off
+ */
+ mt9t112_reg_write(ret, client, 0x0014, 0x2525);
+ mt9t112_reg_write(ret, client, 0x0014, 0x2527);
+ mt9t112_reg_write(ret, client, 0x0014, 0x3427);
+ mt9t112_reg_write(ret, client, 0x0014, 0x3027);
+
+ mdelay(10);
+
+ /*
+ * PLL_BYPASS off
+ * Reference clock count
+ * I2C Master Clock Divider
+ */
+ mt9t112_reg_write(ret, client, 0x0014, 0x3046);
+ /* JPEG initialization workaround */
+ mt9t112_reg_write(ret, client, 0x0016, 0x0400);
+ mt9t112_reg_write(ret, client, 0x0022, 0x0190);
+ mt9t112_reg_write(ret, client, 0x3B84, 0x0212);
+
+ /* External sensor clock is PLL bypass. */
+ mt9t112_reg_write(ret, client, 0x002E, 0x0500);
+
+ mt9t112_reg_mask_set(ret, client, 0x0018, 0x0002, 0x0002);
+ mt9t112_reg_mask_set(ret, client, 0x3B82, 0x0004, 0x0004);
+
+ /* MCU disabled. */
+ mt9t112_reg_mask_set(ret, client, 0x0018, 0x0004, 0x0004);
+
+ /* Out of standby. */
+ mt9t112_reg_mask_set(ret, client, 0x0018, 0x0001, 0);
+
+ mdelay(50);
+
+ /*
+ * Standby Workaround
+ * Disable Secondary I2C Pads
+ */
+ mt9t112_reg_write(ret, client, 0x0614, 0x0001);
+ mdelay(1);
+ mt9t112_reg_write(ret, client, 0x0614, 0x0001);
+ mdelay(1);
+ mt9t112_reg_write(ret, client, 0x0614, 0x0001);
+ mdelay(1);
+ mt9t112_reg_write(ret, client, 0x0614, 0x0001);
+ mdelay(1);
+ mt9t112_reg_write(ret, client, 0x0614, 0x0001);
+ mdelay(1);
+ mt9t112_reg_write(ret, client, 0x0614, 0x0001);
+ mdelay(1);
+
+ /* Poll to verify out of standby. Must Poll this bit. */
+ for (i = 0; i < 100; i++) {
+ mt9t112_reg_read(data, client, 0x0018);
+ if (!(data & 0x4000))
+ break;
+
+ mdelay(10);
+ }
+
+ return ret;
+}
+
+static int mt9t112_init_setting(const struct i2c_client *client)
+{
+ int ret;
+
+ /* Adaptive Output Clock (A) */
+ mt9t112_mcu_mask_set(ret, client, VAR(26, 160), 0x0040, 0x0000);
+
+ /* Read Mode (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 12), 0x0024);
+
+ /* Fine Correction (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 15), 0x00CC);
+
+ /* Fine IT Min (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 17), 0x01f1);
+
+ /* Fine IT Max Margin (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 19), 0x00fF);
+
+ /* Base Frame Lines (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 29), 0x032D);
+
+ /* Min Line Length (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 31), 0x073a);
+
+ /* Line Length (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 37), 0x07d0);
+
+ /* Adaptive Output Clock (B) */
+ mt9t112_mcu_mask_set(ret, client, VAR(27, 160), 0x0040, 0x0000);
+
+ /* Row Start (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 74), 0x004);
+
+ /* Column Start (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 76), 0x004);
+
+ /* Row End (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 78), 0x60B);
+
+ /* Column End (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 80), 0x80B);
+
+ /* Fine Correction (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 87), 0x008C);
+
+ /* Fine IT Min (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 89), 0x01F1);
+
+ /* Fine IT Max Margin (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 91), 0x00FF);
+
+ /* Base Frame Lines (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 101), 0x0668);
+
+ /* Min Line Length (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 103), 0x0AF0);
+
+ /* Line Length (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 109), 0x0AF0);
+
+ /*
+ * Flicker Dectection registers.
+ * This section should be replaced whenever new timing file is
+ * generated. All the following registers need to be replaced.
+ * Following registers are generated from Register Wizard but user can
+ * modify them. For detail see auto flicker detection tuning.
+ */
+
+ /* FD_FDPERIOD_SELECT */
+ mt9t112_mcu_write(ret, client, VAR8(8, 5), 0x01);
+
+ /* PRI_B_CONFIG_FD_ALGO_RUN */
+ mt9t112_mcu_write(ret, client, VAR(27, 17), 0x0003);
+
+ /* PRI_A_CONFIG_FD_ALGO_RUN */
+ mt9t112_mcu_write(ret, client, VAR(26, 17), 0x0003);
+
+ /*
+ * AFD range detection tuning registers.
+ */
+
+ /* Search_f1_50 */
+ mt9t112_mcu_write(ret, client, VAR8(18, 165), 0x25);
+
+ /* Search_f2_50 */
+ mt9t112_mcu_write(ret, client, VAR8(18, 166), 0x28);
+
+ /* Search_f1_60 */
+ mt9t112_mcu_write(ret, client, VAR8(18, 167), 0x2C);
+
+ /* Search_f2_60 */
+ mt9t112_mcu_write(ret, client, VAR8(18, 168), 0x2F);
+
+ /* Period_50Hz (A) */
+ mt9t112_mcu_write(ret, client, VAR8(18, 68), 0xBA);
+
+ /* Secret register by Aptina. */
+ /* Period_50Hz (A MSB) */
+ mt9t112_mcu_write(ret, client, VAR8(18, 303), 0x00);
+
+ /* Period_60Hz (A) */
+ mt9t112_mcu_write(ret, client, VAR8(18, 69), 0x9B);
+
+ /* Secret register by Aptina. */
+ /* Period_60Hz (A MSB) */
+ mt9t112_mcu_write(ret, client, VAR8(18, 301), 0x00);
+
+ /* Period_50Hz (B) */
+ mt9t112_mcu_write(ret, client, VAR8(18, 140), 0x82);
+
+ /* Secret register by Aptina. */
+ /* Period_50Hz (B) MSB */
+ mt9t112_mcu_write(ret, client, VAR8(18, 304), 0x00);
+
+ /* Period_60Hz (B) */
+ mt9t112_mcu_write(ret, client, VAR8(18, 141), 0x6D);
+
+ /* Secret register by Aptina. */
+ /* Period_60Hz (B) MSB */
+ mt9t112_mcu_write(ret, client, VAR8(18, 302), 0x00);
+
+ /* FD Mode */
+ mt9t112_mcu_write(ret, client, VAR8(8, 2), 0x10);
+
+ /* Stat_min */
+ mt9t112_mcu_write(ret, client, VAR8(8, 9), 0x02);
+
+ /* Stat_max */
+ mt9t112_mcu_write(ret, client, VAR8(8, 10), 0x03);
+
+ /* Min_amplitude */
+ mt9t112_mcu_write(ret, client, VAR8(8, 12), 0x0A);
+
+ /* RX FIFO Watermark (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 70), 0x0014);
+
+ /* RX FIFO Watermark (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 142), 0x0014);
+
+ /* MCLK: 16MHz
+ * PCLK: 73MHz
+ * CorePixCLK: 36.5 MHz
+ */
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x0044), 133);
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x0045), 110);
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x008c), 130);
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x008d), 108);
+
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x00A5), 27);
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x00a6), 30);
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x00a7), 32);
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x00a8), 35);
+
+ return ret;
+}
+
+static int mt9t112_auto_focus_setting(const struct i2c_client *client)
+{
+ int ret;
+
+ mt9t112_mcu_write(ret, client, VAR(12, 13), 0x000F);
+ mt9t112_mcu_write(ret, client, VAR(12, 23), 0x0F0F);
+ mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06);
+
+ mt9t112_reg_write(ret, client, 0x0614, 0x0000);
+
+ mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x05);
+ mt9t112_mcu_write(ret, client, VAR8(12, 2), 0x02);
+ mt9t112_mcu_write(ret, client, VAR(12, 3), 0x0002);
+ mt9t112_mcu_write(ret, client, VAR(17, 3), 0x8001);
+ mt9t112_mcu_write(ret, client, VAR(17, 11), 0x0025);
+ mt9t112_mcu_write(ret, client, VAR(17, 13), 0x0193);
+ mt9t112_mcu_write(ret, client, VAR8(17, 33), 0x18);
+ mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x05);
+
+ return ret;
+}
+
+static int mt9t112_auto_focus_trigger(const struct i2c_client *client)
+{
+ int ret;
+
+ mt9t112_mcu_write(ret, client, VAR8(12, 25), 0x01);
+
+ return ret;
+}
+
+static int mt9t112_init_camera(const struct i2c_client *client)
+{
+ int ret;
+
+ ECHECKER(ret, mt9t112_reset(client));
+ ECHECKER(ret, mt9t112_init_pll(client));
+ ECHECKER(ret, mt9t112_init_setting(client));
+ ECHECKER(ret, mt9t112_auto_focus_setting(client));
+
+ mt9t112_reg_mask_set(ret, client, 0x0018, 0x0004, 0);
+
+ /* Analog setting B.*/
+ mt9t112_reg_write(ret, client, 0x3084, 0x2409);
+ mt9t112_reg_write(ret, client, 0x3092, 0x0A49);
+ mt9t112_reg_write(ret, client, 0x3094, 0x4949);
+ mt9t112_reg_write(ret, client, 0x3096, 0x4950);
+
+ /*
+ * Disable adaptive clock.
+ * PRI_A_CONFIG_JPEG_OB_TX_CONTROL_VAR
+ * PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR
+ */
+ mt9t112_mcu_write(ret, client, VAR(26, 160), 0x0A2E);
+ mt9t112_mcu_write(ret, client, VAR(27, 160), 0x0A2E);
+
+ /*
+ * Configure Status in Status_before_length Format and enable header.
+ * PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR
+ */
+ mt9t112_mcu_write(ret, client, VAR(27, 144), 0x0CB4);
+
+ /*
+ * Enable JPEG in context B.
+ * PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR
+ */
+ mt9t112_mcu_write(ret, client, VAR8(27, 142), 0x01);
+
+ /* Disable Dac_TXLO. */
+ mt9t112_reg_write(ret, client, 0x316C, 0x350F);
+
+ /* Set max slew rates. */
+ mt9t112_reg_write(ret, client, 0x1E, 0x777);
+
+ return ret;
+}
+
+/************************************************************************
+ * v4l2_subdev_core_ops
+ ***********************************************************************/
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int mt9t112_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ reg->size = 2;
+ mt9t112_reg_read(ret, client, reg->reg);
+
+ reg->val = (__u64)ret;
+
+ return 0;
+}
+
+static int mt9t112_s_register(struct v4l2_subdev *sd,
+ const struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ mt9t112_reg_write(ret, client, reg->reg, reg->val);
+
+ return ret;
+}
+#endif
+
+static int mt9t112_power_on(struct mt9t112_priv *priv)
+{
+ int ret;
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret)
+ return ret;
+
+ if (priv->standby_gpio) {
+ gpiod_set_value(priv->standby_gpio, 0);
+ msleep(100);
+ }
+
+ return 0;
+}
+
+static int mt9t112_power_off(struct mt9t112_priv *priv)
+{
+ clk_disable_unprepare(priv->clk);
+ if (priv->standby_gpio) {
+ gpiod_set_value(priv->standby_gpio, 1);
+ msleep(100);
+ }
+
+ return 0;
+}
+
+static int mt9t112_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t112_priv *priv = to_mt9t112(client);
+
+ return on ? mt9t112_power_on(priv) :
+ mt9t112_power_off(priv);
+}
+
+static const struct v4l2_subdev_core_ops mt9t112_subdev_core_ops = {
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = mt9t112_g_register,
+ .s_register = mt9t112_s_register,
+#endif
+ .s_power = mt9t112_s_power,
+};
+
+/************************************************************************
+ * v4l2_subdev_video_ops
+ **********************************************************************/
+static int mt9t112_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t112_priv *priv = to_mt9t112(client);
+ int ret = 0;
+
+ if (!enable) {
+ /* FIXME
+ *
+ * If user selected large output size, and used it long time,
+ * mt9t112 camera will be very warm.
+ *
+ * But current driver can not stop mt9t112 camera.
+ * So, set small size here to solve this problem.
+ */
+ mt9t112_set_a_frame_size(client, VGA_WIDTH, VGA_HEIGHT);
+ return ret;
+ }
+
+ if (!priv->init_done) {
+ u16 param = MT9T112_FLAG_PCLK_RISING_EDGE & priv->info->flags ?
+ 0x0001 : 0x0000;
+
+ ECHECKER(ret, mt9t112_init_camera(client));
+
+ /* Invert PCLK (Data sampled on falling edge of pixclk). */
+ mt9t112_reg_write(ret, client, 0x3C20, param);
+
+ mdelay(5);
+
+ priv->init_done = true;
+ }
+
+ mt9t112_mcu_write(ret, client, VAR(26, 7), priv->format->fmt);
+ mt9t112_mcu_write(ret, client, VAR(26, 9), priv->format->order);
+ mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06);
+
+ mt9t112_set_a_frame_size(client, priv->frame.width, priv->frame.height);
+
+ ECHECKER(ret, mt9t112_auto_focus_trigger(client));
+
+ dev_dbg(&client->dev, "format : %d\n", priv->format->code);
+ dev_dbg(&client->dev, "size : %d x %d\n",
+ priv->frame.width,
+ priv->frame.height);
+
+ CLOCK_INFO(client, EXT_CLOCK);
+
+ return ret;
+}
+
+static int mt9t112_set_params(struct mt9t112_priv *priv,
+ const struct v4l2_rect *rect,
+ u32 code)
+{
+ int i;
+
+ /*
+ * get color format
+ */
+ for (i = 0; i < priv->num_formats; i++)
+ if (mt9t112_cfmts[i].code == code)
+ break;
+
+ if (i == priv->num_formats)
+ return -EINVAL;
+
+ priv->frame = *rect;
+
+ /*
+ * frame size check
+ */
+ v4l_bound_align_image(&priv->frame.width, 0, MAX_WIDTH, 0,
+ &priv->frame.height, 0, MAX_HEIGHT, 0, 0);
+
+ priv->format = mt9t112_cfmts + i;
+
+ return 0;
+}
+
+static int mt9t112_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_selection *sel)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t112_priv *priv = to_mt9t112(client);
+
+ if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = MAX_WIDTH;
+ sel->r.height = MAX_HEIGHT;
+ return 0;
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = VGA_WIDTH;
+ sel->r.height = VGA_HEIGHT;
+ return 0;
+ case V4L2_SEL_TGT_CROP:
+ sel->r = priv->frame;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mt9t112_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_selection *sel)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t112_priv *priv = to_mt9t112(client);
+ const struct v4l2_rect *rect = &sel->r;
+
+ if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE ||
+ sel->target != V4L2_SEL_TGT_CROP)
+ return -EINVAL;
+
+ return mt9t112_set_params(priv, rect, priv->format->code);
+}
+
+static int mt9t112_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *mf = &format->format;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t112_priv *priv = to_mt9t112(client);
+
+ if (format->pad)
+ return -EINVAL;
+
+ mf->width = priv->frame.width;
+ mf->height = priv->frame.height;
+ mf->colorspace = priv->format->colorspace;
+ mf->code = priv->format->code;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int mt9t112_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t112_priv *priv = to_mt9t112(client);
+ struct v4l2_rect rect = {
+ .width = mf->width,
+ .height = mf->height,
+ .left = priv->frame.left,
+ .top = priv->frame.top,
+ };
+ int ret;
+
+ ret = mt9t112_set_params(priv, &rect, mf->code);
+
+ if (!ret)
+ mf->colorspace = priv->format->colorspace;
+
+ return ret;
+}
+
+static int mt9t112_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *mf = &format->format;
+ struct mt9t112_priv *priv = to_mt9t112(client);
+ int i;
+
+ if (format->pad)
+ return -EINVAL;
+
+ for (i = 0; i < priv->num_formats; i++)
+ if (mt9t112_cfmts[i].code == mf->code)
+ break;
+
+ if (i == priv->num_formats) {
+ mf->code = MEDIA_BUS_FMT_UYVY8_2X8;
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ } else {
+ mf->colorspace = mt9t112_cfmts[i].colorspace;
+ }
+
+ v4l_bound_align_image(&mf->width, 0, MAX_WIDTH, 0,
+ &mf->height, 0, MAX_HEIGHT, 0, 0);
+
+ mf->field = V4L2_FIELD_NONE;
+
+ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ return mt9t112_s_fmt(sd, mf);
+ cfg->try_fmt = *mf;
+
+ return 0;
+}
+
+static int mt9t112_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t112_priv *priv = to_mt9t112(client);
+
+ if (code->pad || code->index >= priv->num_formats)
+ return -EINVAL;
+
+ code->code = mt9t112_cfmts[code->index].code;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops mt9t112_subdev_video_ops = {
+ .s_stream = mt9t112_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops mt9t112_subdev_pad_ops = {
+ .enum_mbus_code = mt9t112_enum_mbus_code,
+ .get_selection = mt9t112_get_selection,
+ .set_selection = mt9t112_set_selection,
+ .get_fmt = mt9t112_get_fmt,
+ .set_fmt = mt9t112_set_fmt,
+};
+
+/************************************************************************
+ * i2c driver
+ ***********************************************************************/
+static const struct v4l2_subdev_ops mt9t112_subdev_ops = {
+ .core = &mt9t112_subdev_core_ops,
+ .video = &mt9t112_subdev_video_ops,
+ .pad = &mt9t112_subdev_pad_ops,
+};
+
+static int mt9t112_camera_probe(struct i2c_client *client)
+{
+ struct mt9t112_priv *priv = to_mt9t112(client);
+ const char *devname;
+ int chipid;
+ int ret;
+
+ ret = mt9t112_s_power(&priv->subdev, 1);
+ if (ret < 0)
+ return ret;
+
+ /* Check and show chip ID. */
+ mt9t112_reg_read(chipid, client, 0x0000);
+
+ switch (chipid) {
+ case 0x2680:
+ devname = "mt9t111";
+ priv->num_formats = 1;
+ break;
+ case 0x2682:
+ devname = "mt9t112";
+ priv->num_formats = ARRAY_SIZE(mt9t112_cfmts);
+ break;
+ default:
+ dev_err(&client->dev, "Product ID error %04x\n", chipid);
+ ret = -ENODEV;
+ goto done;
+ }
+
+ dev_info(&client->dev, "%s chip ID %04x\n", devname, chipid);
+
+done:
+ mt9t112_s_power(&priv->subdev, 0);
+
+ return ret;
+}
+
+static int mt9t112_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct mt9t112_priv *priv;
+ int ret;
+
+ if (!client->dev.platform_data) {
+ dev_err(&client->dev, "mt9t112: missing platform data!\n");
+ return -EINVAL;
+ }
+
+ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->info = client->dev.platform_data;
+ priv->init_done = false;
+
+ v4l2_i2c_subdev_init(&priv->subdev, client, &mt9t112_subdev_ops);
+
+ priv->clk = devm_clk_get(&client->dev, "extclk");
+ if (PTR_ERR(priv->clk) == -ENOENT) {
+ priv->clk = NULL;
+ } else if (IS_ERR(priv->clk)) {
+ dev_err(&client->dev, "Unable to get clock \"extclk\"\n");
+ return PTR_ERR(priv->clk);
+ }
+
+ priv->standby_gpio = devm_gpiod_get_optional(&client->dev, "standby",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->standby_gpio)) {
+ dev_err(&client->dev, "Unable to get gpio \"standby\"\n");
+ return PTR_ERR(priv->standby_gpio);
+ }
+
+ ret = mt9t112_camera_probe(client);
+ if (ret)
+ return ret;
+
+ return v4l2_async_register_subdev(&priv->subdev);
+}
+
+static int mt9t112_remove(struct i2c_client *client)
+{
+ struct mt9t112_priv *priv = to_mt9t112(client);
+
+ clk_disable_unprepare(priv->clk);
+ v4l2_async_unregister_subdev(&priv->subdev);
+
+ return 0;
+}
+
+static const struct i2c_device_id mt9t112_id[] = {
+ { "mt9t112", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mt9t112_id);
+
+static struct i2c_driver mt9t112_i2c_driver = {
+ .driver = {
+ .name = "mt9t112",
+ },
+ .probe = mt9t112_probe,
+ .remove = mt9t112_remove,
+ .id_table = mt9t112_id,
+};
+
+module_i2c_driver(mt9t112_i2c_driver);
+
+MODULE_DESCRIPTION("V4L2 driver for MT9T111/MT9T112 camera sensor");
+MODULE_AUTHOR("Kuninori Morimoto");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c
index 5e29064fae91..46ef74a2ca36 100644
--- a/drivers/media/i2c/mt9v011.c
+++ b/drivers/media/i2c/mt9v011.c
@@ -364,33 +364,22 @@ static int mt9v011_set_fmt(struct v4l2_subdev *sd,
return 0;
}
-static int mt9v011_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+static int mt9v011_g_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *ival)
{
- struct v4l2_captureparm *cp = &parms->parm.capture;
-
- if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- memset(cp, 0, sizeof(struct v4l2_captureparm));
- cp->capability = V4L2_CAP_TIMEPERFRAME;
calc_fps(sd,
- &cp->timeperframe.numerator,
- &cp->timeperframe.denominator);
+ &ival->interval.numerator,
+ &ival->interval.denominator);
return 0;
}
-static int mt9v011_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+static int mt9v011_s_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *ival)
{
- struct v4l2_captureparm *cp = &parms->parm.capture;
- struct v4l2_fract *tpf = &cp->timeperframe;
+ struct v4l2_fract *tpf = &ival->interval;
u16 speed;
- if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- if (cp->extendedmode != 0)
- return -EINVAL;
-
speed = calc_speed(sd, tpf->numerator, tpf->denominator);
mt9v011_write(sd, R0A_MT9V011_CLK_SPEED, speed);
@@ -469,8 +458,8 @@ static const struct v4l2_subdev_core_ops mt9v011_core_ops = {
};
static const struct v4l2_subdev_video_ops mt9v011_video_ops = {
- .g_parm = mt9v011_g_parm,
- .s_parm = mt9v011_s_parm,
+ .g_frame_interval = mt9v011_g_frame_interval,
+ .s_frame_interval = mt9v011_s_frame_interval,
};
static const struct v4l2_subdev_pad_ops mt9v011_pad_ops = {
diff --git a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c
index bf7d06f3f21a..3dbcae257164 100644
--- a/drivers/media/i2c/ov13858.c
+++ b/drivers/media/i2c/ov13858.c
@@ -1,16 +1,5 @@
-/*
- * Copyright (c) 2017 Intel Corporation.
- *
- * 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.
- *
- * 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.
- *
- */
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2017 Intel Corporation.
#include <linux/acpi.h>
#include <linux/i2c.h>
@@ -194,6 +183,7 @@ static const struct ov13858_reg mode_4224x3136_regs[] = {
{0x3624, 0x1c},
{0x3640, 0x10},
{0x3641, 0x70},
+ {0x3660, 0x04},
{0x3661, 0x80},
{0x3662, 0x12},
{0x3664, 0x73},
@@ -384,6 +374,7 @@ static const struct ov13858_reg mode_2112x1568_regs[] = {
{0x3624, 0x1c},
{0x3640, 0x10},
{0x3641, 0x70},
+ {0x3660, 0x04},
{0x3661, 0x80},
{0x3662, 0x10},
{0x3664, 0x73},
@@ -574,6 +565,7 @@ static const struct ov13858_reg mode_2112x1188_regs[] = {
{0x3624, 0x1c},
{0x3640, 0x10},
{0x3641, 0x70},
+ {0x3660, 0x04},
{0x3661, 0x80},
{0x3662, 0x10},
{0x3664, 0x73},
@@ -764,6 +756,7 @@ static const struct ov13858_reg mode_1056x784_regs[] = {
{0x3624, 0x1c},
{0x3640, 0x10},
{0x3641, 0x70},
+ {0x3660, 0x04},
{0x3661, 0x80},
{0x3662, 0x08},
{0x3664, 0x73},
@@ -1057,14 +1050,15 @@ struct ov13858 {
#define to_ov13858(_sd) container_of(_sd, struct ov13858, sd)
/* Read registers up to 4 at a time */
-static int ov13858_read_reg(struct ov13858 *ov13858, u16 reg, u32 len, u32 *val)
+static int ov13858_read_reg(struct ov13858 *ov13858, u16 reg, u32 len,
+ u32 *val)
{
struct i2c_client *client = v4l2_get_subdevdata(&ov13858->sd);
struct i2c_msg msgs[2];
u8 *data_be_p;
int ret;
- u32 data_be = 0;
- u16 reg_addr_be = cpu_to_be16(reg);
+ __be32 data_be = 0;
+ __be16 reg_addr_be = cpu_to_be16(reg);
if (len > 4)
return -EINVAL;
@@ -1092,11 +1086,13 @@ static int ov13858_read_reg(struct ov13858 *ov13858, u16 reg, u32 len, u32 *val)
}
/* Write registers up to 4 at a time */
-static int ov13858_write_reg(struct ov13858 *ov13858, u16 reg, u32 len, u32 val)
+static int ov13858_write_reg(struct ov13858 *ov13858, u16 reg, u32 len,
+ u32 __val)
{
struct i2c_client *client = v4l2_get_subdevdata(&ov13858->sd);
int buf_i, val_i;
u8 buf[6], *val_p;
+ __be32 val;
if (len > 4)
return -EINVAL;
@@ -1104,7 +1100,7 @@ static int ov13858_write_reg(struct ov13858 *ov13858, u16 reg, u32 len, u32 val)
buf[0] = reg >> 8;
buf[1] = reg & 0xff;
- val = cpu_to_be32(val);
+ val = cpu_to_be32(__val);
val_p = (u8 *)&val;
buf_i = 2;
val_i = 4 - len;
@@ -1348,39 +1344,6 @@ static int ov13858_get_pad_format(struct v4l2_subdev *sd,
return ret;
}
-/*
- * Calculate resolution distance
- */
-static int
-ov13858_get_resolution_dist(const struct ov13858_mode *mode,
- struct v4l2_mbus_framefmt *framefmt)
-{
- return abs(mode->width - framefmt->width) +
- abs(mode->height - framefmt->height);
-}
-
-/*
- * Find the closest supported resolution to the requested resolution
- */
-static const struct ov13858_mode *
-ov13858_find_best_fit(struct ov13858 *ov13858,
- struct v4l2_subdev_format *fmt)
-{
- int i, dist, cur_best_fit = 0, cur_best_fit_dist = -1;
- struct v4l2_mbus_framefmt *framefmt = &fmt->format;
-
- for (i = 0; i < ARRAY_SIZE(supported_modes); i++) {
- dist = ov13858_get_resolution_dist(&supported_modes[i],
- framefmt);
- if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) {
- cur_best_fit_dist = dist;
- cur_best_fit = i;
- }
- }
-
- return &supported_modes[cur_best_fit];
-}
-
static int
ov13858_set_pad_format(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
@@ -1401,7 +1364,10 @@ ov13858_set_pad_format(struct v4l2_subdev *sd,
if (fmt->format.code != MEDIA_BUS_FMT_SGRBG10_1X10)
fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
- mode = ov13858_find_best_fit(ov13858, fmt);
+ mode = v4l2_find_nearest_size(supported_modes,
+ ARRAY_SIZE(supported_modes),
+ width, height,
+ fmt->format.width, fmt->format.height);
ov13858_update_pad_format(mode, fmt);
if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
@@ -1565,7 +1531,7 @@ static int __maybe_unused ov13858_resume(struct device *dev)
error:
ov13858_stop_streaming(ov13858);
- ov13858->streaming = 0;
+ ov13858->streaming = false;
return ret;
}
diff --git a/drivers/media/i2c/ov2685.c b/drivers/media/i2c/ov2685.c
new file mode 100644
index 000000000000..385c1886a947
--- /dev/null
+++ b/drivers/media/i2c/ov2685.c
@@ -0,0 +1,845 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ov2685 driver
+ *
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/sysfs.h>
+#include <media/media-entity.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+#define CHIP_ID 0x2685
+#define OV2685_REG_CHIP_ID 0x300a
+
+#define OV2685_XVCLK_FREQ 24000000
+
+#define REG_SC_CTRL_MODE 0x0100
+#define SC_CTRL_MODE_STANDBY 0x0
+#define SC_CTRL_MODE_STREAMING BIT(0)
+
+#define OV2685_REG_EXPOSURE 0x3500
+#define OV2685_EXPOSURE_MIN 4
+#define OV2685_EXPOSURE_STEP 1
+
+#define OV2685_REG_VTS 0x380e
+#define OV2685_VTS_MAX 0x7fff
+
+#define OV2685_REG_GAIN 0x350a
+#define OV2685_GAIN_MIN 0
+#define OV2685_GAIN_MAX 0x07ff
+#define OV2685_GAIN_STEP 0x1
+#define OV2685_GAIN_DEFAULT 0x0036
+
+#define OV2685_REG_TEST_PATTERN 0x5080
+#define OV2685_TEST_PATTERN_DISABLED 0x00
+#define OV2685_TEST_PATTERN_COLOR_BAR 0x80
+#define OV2685_TEST_PATTERN_RANDOM 0x81
+#define OV2685_TEST_PATTERN_COLOR_BAR_FADE 0x88
+#define OV2685_TEST_PATTERN_BW_SQUARE 0x92
+#define OV2685_TEST_PATTERN_COLOR_SQUARE 0x82
+
+#define REG_NULL 0xFFFF
+
+#define OV2685_REG_VALUE_08BIT 1
+#define OV2685_REG_VALUE_16BIT 2
+#define OV2685_REG_VALUE_24BIT 3
+
+#define OV2685_LANES 1
+#define OV2685_BITS_PER_SAMPLE 10
+
+static const char * const ov2685_supply_names[] = {
+ "avdd", /* Analog power */
+ "dovdd", /* Digital I/O power */
+ "dvdd", /* Digital core power */
+};
+
+#define OV2685_NUM_SUPPLIES ARRAY_SIZE(ov2685_supply_names)
+
+struct regval {
+ u16 addr;
+ u8 val;
+};
+
+struct ov2685_mode {
+ u32 width;
+ u32 height;
+ u32 exp_def;
+ u32 hts_def;
+ u32 vts_def;
+ const struct regval *reg_list;
+};
+
+struct ov2685 {
+ struct i2c_client *client;
+ struct clk *xvclk;
+ struct gpio_desc *reset_gpio;
+ struct regulator_bulk_data supplies[OV2685_NUM_SUPPLIES];
+
+ bool streaming;
+ struct mutex mutex;
+ struct v4l2_subdev subdev;
+ struct media_pad pad;
+ struct v4l2_ctrl *anal_gain;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *test_pattern;
+ struct v4l2_ctrl_handler ctrl_handler;
+
+ const struct ov2685_mode *cur_mode;
+};
+
+#define to_ov2685(sd) container_of(sd, struct ov2685, subdev)
+
+/* PLL settings bases on 24M xvclk */
+static struct regval ov2685_1600x1200_regs[] = {
+ {0x0103, 0x01},
+ {0x0100, 0x00},
+ {0x3002, 0x00},
+ {0x3016, 0x1c},
+ {0x3018, 0x44},
+ {0x301d, 0xf0},
+ {0x3020, 0x00},
+ {0x3082, 0x37},
+ {0x3083, 0x03},
+ {0x3084, 0x09},
+ {0x3085, 0x04},
+ {0x3086, 0x00},
+ {0x3087, 0x00},
+ {0x3501, 0x4e},
+ {0x3502, 0xe0},
+ {0x3503, 0x27},
+ {0x350b, 0x36},
+ {0x3600, 0xb4},
+ {0x3603, 0x35},
+ {0x3604, 0x24},
+ {0x3605, 0x00},
+ {0x3620, 0x24},
+ {0x3621, 0x34},
+ {0x3622, 0x03},
+ {0x3628, 0x10},
+ {0x3705, 0x3c},
+ {0x370a, 0x21},
+ {0x370c, 0x50},
+ {0x370d, 0xc0},
+ {0x3717, 0x58},
+ {0x3718, 0x80},
+ {0x3720, 0x00},
+ {0x3721, 0x09},
+ {0x3722, 0x06},
+ {0x3723, 0x59},
+ {0x3738, 0x99},
+ {0x3781, 0x80},
+ {0x3784, 0x0c},
+ {0x3789, 0x60},
+ {0x3800, 0x00},
+ {0x3801, 0x00},
+ {0x3802, 0x00},
+ {0x3803, 0x00},
+ {0x3804, 0x06},
+ {0x3805, 0x4f},
+ {0x3806, 0x04},
+ {0x3807, 0xbf},
+ {0x3808, 0x06},
+ {0x3809, 0x40},
+ {0x380a, 0x04},
+ {0x380b, 0xb0},
+ {0x380c, 0x06},
+ {0x380d, 0xa4},
+ {0x380e, 0x05},
+ {0x380f, 0x0e},
+ {0x3810, 0x00},
+ {0x3811, 0x08},
+ {0x3812, 0x00},
+ {0x3813, 0x08},
+ {0x3814, 0x11},
+ {0x3815, 0x11},
+ {0x3819, 0x04},
+ {0x3820, 0xc0},
+ {0x3821, 0x00},
+ {0x3a06, 0x01},
+ {0x3a07, 0x84},
+ {0x3a08, 0x01},
+ {0x3a09, 0x43},
+ {0x3a0a, 0x24},
+ {0x3a0b, 0x60},
+ {0x3a0c, 0x28},
+ {0x3a0d, 0x60},
+ {0x3a0e, 0x04},
+ {0x3a0f, 0x8c},
+ {0x3a10, 0x05},
+ {0x3a11, 0x0c},
+ {0x4000, 0x81},
+ {0x4001, 0x40},
+ {0x4008, 0x02},
+ {0x4009, 0x09},
+ {0x4300, 0x00},
+ {0x430e, 0x00},
+ {0x4602, 0x02},
+ {0x481b, 0x40},
+ {0x481f, 0x40},
+ {0x4837, 0x18},
+ {0x5000, 0x1f},
+ {0x5001, 0x05},
+ {0x5002, 0x30},
+ {0x5003, 0x04},
+ {0x5004, 0x00},
+ {0x5005, 0x0c},
+ {0x5280, 0x15},
+ {0x5281, 0x06},
+ {0x5282, 0x06},
+ {0x5283, 0x08},
+ {0x5284, 0x1c},
+ {0x5285, 0x1c},
+ {0x5286, 0x20},
+ {0x5287, 0x10},
+ {REG_NULL, 0x00}
+};
+
+#define OV2685_LINK_FREQ_330MHZ 330000000
+static const s64 link_freq_menu_items[] = {
+ OV2685_LINK_FREQ_330MHZ
+};
+
+static const char * const ov2685_test_pattern_menu[] = {
+ "Disabled",
+ "Color Bar",
+ "Color Bar FADE",
+ "Random Data",
+ "Black White Square",
+ "Color Square"
+};
+
+static const int ov2685_test_pattern_val[] = {
+ OV2685_TEST_PATTERN_DISABLED,
+ OV2685_TEST_PATTERN_COLOR_BAR,
+ OV2685_TEST_PATTERN_COLOR_BAR_FADE,
+ OV2685_TEST_PATTERN_RANDOM,
+ OV2685_TEST_PATTERN_BW_SQUARE,
+ OV2685_TEST_PATTERN_COLOR_SQUARE,
+};
+
+static const struct ov2685_mode supported_modes[] = {
+ {
+ .width = 1600,
+ .height = 1200,
+ .exp_def = 0x04ee,
+ .hts_def = 0x06a4,
+ .vts_def = 0x050e,
+ .reg_list = ov2685_1600x1200_regs,
+ },
+};
+
+/* Write registers up to 4 at a time */
+static int ov2685_write_reg(struct i2c_client *client, u16 reg,
+ u32 len, u32 val)
+{
+ u32 val_i, buf_i;
+ u8 buf[6];
+ u8 *val_p;
+ __be32 val_be;
+
+ if (len > 4)
+ return -EINVAL;
+
+ buf[0] = reg >> 8;
+ buf[1] = reg & 0xff;
+
+ val_be = cpu_to_be32(val);
+ val_p = (u8 *)&val_be;
+ buf_i = 2;
+ val_i = 4 - len;
+
+ while (val_i < 4)
+ buf[buf_i++] = val_p[val_i++];
+
+ if (i2c_master_send(client, buf, len + 2) != len + 2)
+ return -EIO;
+
+ return 0;
+}
+
+static int ov2685_write_array(struct i2c_client *client,
+ const struct regval *regs)
+{
+ int ret = 0;
+ u32 i;
+
+ for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
+ ret = ov2685_write_reg(client, regs[i].addr,
+ OV2685_REG_VALUE_08BIT, regs[i].val);
+
+ return ret;
+}
+
+/* Read registers up to 4 at a time */
+static int ov2685_read_reg(struct i2c_client *client, u16 reg,
+ u32 len, u32 *val)
+{
+ struct i2c_msg msgs[2];
+ u8 *data_be_p;
+ __be32 data_be = 0;
+ __be16 reg_addr_be = cpu_to_be16(reg);
+ int ret;
+
+ if (len > 4)
+ return -EINVAL;
+
+ data_be_p = (u8 *)&data_be;
+ /* Write register address */
+ msgs[0].addr = client->addr;
+ msgs[0].flags = 0;
+ msgs[0].len = 2;
+ msgs[0].buf = (u8 *)&reg_addr_be;
+
+ /* Read data from register */
+ msgs[1].addr = client->addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = len;
+ msgs[1].buf = &data_be_p[4 - len];
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs))
+ return -EIO;
+
+ *val = be32_to_cpu(data_be);
+
+ return 0;
+}
+
+static void ov2685_fill_fmt(const struct ov2685_mode *mode,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ fmt->width = mode->width;
+ fmt->height = mode->height;
+ fmt->field = V4L2_FIELD_NONE;
+}
+
+static int ov2685_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct ov2685 *ov2685 = to_ov2685(sd);
+ struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
+
+ /* only one mode supported for now */
+ ov2685_fill_fmt(ov2685->cur_mode, mbus_fmt);
+
+ return 0;
+}
+
+static int ov2685_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct ov2685 *ov2685 = to_ov2685(sd);
+ struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
+
+ ov2685_fill_fmt(ov2685->cur_mode, mbus_fmt);
+
+ return 0;
+}
+
+static int ov2685_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index >= ARRAY_SIZE(supported_modes))
+ return -EINVAL;
+
+ code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+
+ return 0;
+}
+
+static int ov2685_enum_frame_sizes(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ int index = fse->index;
+
+ if (index >= ARRAY_SIZE(supported_modes))
+ return -EINVAL;
+
+ fse->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+
+ fse->min_width = supported_modes[index].width;
+ fse->max_width = supported_modes[index].width;
+ fse->max_height = supported_modes[index].height;
+ fse->min_height = supported_modes[index].height;
+
+ return 0;
+}
+
+/* Calculate the delay in us by clock rate and clock cycles */
+static inline u32 ov2685_cal_delay(u32 cycles)
+{
+ return DIV_ROUND_UP(cycles, OV2685_XVCLK_FREQ / 1000 / 1000);
+}
+
+static int __ov2685_power_on(struct ov2685 *ov2685)
+{
+ int ret;
+ u32 delay_us;
+ struct device *dev = &ov2685->client->dev;
+
+ ret = clk_prepare_enable(ov2685->xvclk);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable xvclk\n");
+ return ret;
+ }
+
+ gpiod_set_value_cansleep(ov2685->reset_gpio, 1);
+
+ ret = regulator_bulk_enable(OV2685_NUM_SUPPLIES, ov2685->supplies);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable regulators\n");
+ goto disable_clk;
+ }
+
+ /* The minimum delay between power supplies and reset rising can be 0 */
+ gpiod_set_value_cansleep(ov2685->reset_gpio, 0);
+ /* 8192 xvclk cycles prior to the first SCCB transaction */
+ delay_us = ov2685_cal_delay(8192);
+ usleep_range(delay_us, delay_us * 2);
+
+ /* HACK: ov2685 would output messy data after reset(R0103),
+ * writing register before .s_stream() as a workaround
+ */
+ ret = ov2685_write_array(ov2685->client, ov2685->cur_mode->reg_list);
+ if (ret)
+ goto disable_supplies;
+
+ return 0;
+
+disable_supplies:
+ regulator_bulk_disable(OV2685_NUM_SUPPLIES, ov2685->supplies);
+disable_clk:
+ clk_disable_unprepare(ov2685->xvclk);
+
+ return ret;
+}
+
+static void __ov2685_power_off(struct ov2685 *ov2685)
+{
+ /* 512 xvclk cycles after the last SCCB transaction or MIPI frame end */
+ u32 delay_us = ov2685_cal_delay(512);
+
+ usleep_range(delay_us, delay_us * 2);
+ clk_disable_unprepare(ov2685->xvclk);
+ gpiod_set_value_cansleep(ov2685->reset_gpio, 1);
+ regulator_bulk_disable(OV2685_NUM_SUPPLIES, ov2685->supplies);
+}
+
+static int ov2685_s_stream(struct v4l2_subdev *sd, int on)
+{
+ struct ov2685 *ov2685 = to_ov2685(sd);
+ struct i2c_client *client = ov2685->client;
+ int ret = 0;
+
+ mutex_lock(&ov2685->mutex);
+
+ on = !!on;
+ if (on == ov2685->streaming)
+ goto unlock_and_return;
+
+ if (on) {
+ ret = pm_runtime_get_sync(&ov2685->client->dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(&client->dev);
+ goto unlock_and_return;
+ }
+ ret = __v4l2_ctrl_handler_setup(&ov2685->ctrl_handler);
+ if (ret) {
+ pm_runtime_put(&client->dev);
+ goto unlock_and_return;
+ }
+ ret = ov2685_write_reg(client, REG_SC_CTRL_MODE,
+ OV2685_REG_VALUE_08BIT, SC_CTRL_MODE_STREAMING);
+ if (ret) {
+ pm_runtime_put(&client->dev);
+ goto unlock_and_return;
+ }
+ } else {
+ ov2685_write_reg(client, REG_SC_CTRL_MODE,
+ OV2685_REG_VALUE_08BIT, SC_CTRL_MODE_STANDBY);
+ pm_runtime_put(&ov2685->client->dev);
+ }
+
+ ov2685->streaming = on;
+
+unlock_and_return:
+ mutex_unlock(&ov2685->mutex);
+
+ return ret;
+}
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static int ov2685_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct ov2685 *ov2685 = to_ov2685(sd);
+ struct v4l2_mbus_framefmt *try_fmt;
+
+ mutex_lock(&ov2685->mutex);
+
+ try_fmt = v4l2_subdev_get_try_format(sd, fh->pad, 0);
+ /* Initialize try_fmt */
+ ov2685_fill_fmt(&supported_modes[0], try_fmt);
+
+ mutex_unlock(&ov2685->mutex);
+
+ return 0;
+}
+#endif
+
+static int __maybe_unused ov2685_runtime_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov2685 *ov2685 = to_ov2685(sd);
+
+ return __ov2685_power_on(ov2685);
+}
+
+static int __maybe_unused ov2685_runtime_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov2685 *ov2685 = to_ov2685(sd);
+
+ __ov2685_power_off(ov2685);
+
+ return 0;
+}
+
+static const struct dev_pm_ops ov2685_pm_ops = {
+ SET_RUNTIME_PM_OPS(ov2685_runtime_suspend,
+ ov2685_runtime_resume, NULL)
+};
+
+static int ov2685_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov2685 *ov2685 = container_of(ctrl->handler,
+ struct ov2685, ctrl_handler);
+ struct i2c_client *client = ov2685->client;
+ s64 max_expo;
+ int ret;
+
+ /* Propagate change of current control to all related controls */
+ switch (ctrl->id) {
+ case V4L2_CID_VBLANK:
+ /* Update max exposure while meeting expected vblanking */
+ max_expo = ov2685->cur_mode->height + ctrl->val - 4;
+ __v4l2_ctrl_modify_range(ov2685->exposure,
+ ov2685->exposure->minimum, max_expo,
+ ov2685->exposure->step,
+ ov2685->exposure->default_value);
+ break;
+ }
+
+ if (pm_runtime_get_if_in_use(&client->dev) <= 0)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ ret = ov2685_write_reg(ov2685->client, OV2685_REG_EXPOSURE,
+ OV2685_REG_VALUE_24BIT, ctrl->val << 4);
+ break;
+ case V4L2_CID_ANALOGUE_GAIN:
+ ret = ov2685_write_reg(ov2685->client, OV2685_REG_GAIN,
+ OV2685_REG_VALUE_16BIT, ctrl->val);
+ break;
+ case V4L2_CID_VBLANK:
+ ret = ov2685_write_reg(ov2685->client, OV2685_REG_VTS,
+ OV2685_REG_VALUE_16BIT,
+ ctrl->val + ov2685->cur_mode->height);
+ break;
+ case V4L2_CID_TEST_PATTERN:
+ ret = ov2685_write_reg(ov2685->client, OV2685_REG_TEST_PATTERN,
+ OV2685_REG_VALUE_08BIT,
+ ov2685_test_pattern_val[ctrl->val]);
+ break;
+ default:
+ dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
+ __func__, ctrl->id, ctrl->val);
+ ret = -EINVAL;
+ break;
+ };
+
+ pm_runtime_put(&client->dev);
+
+ return ret;
+}
+
+static const struct v4l2_subdev_video_ops ov2685_video_ops = {
+ .s_stream = ov2685_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops ov2685_pad_ops = {
+ .enum_mbus_code = ov2685_enum_mbus_code,
+ .enum_frame_size = ov2685_enum_frame_sizes,
+ .get_fmt = ov2685_get_fmt,
+ .set_fmt = ov2685_set_fmt,
+};
+
+static const struct v4l2_subdev_ops ov2685_subdev_ops = {
+ .video = &ov2685_video_ops,
+ .pad = &ov2685_pad_ops,
+};
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static const struct v4l2_subdev_internal_ops ov2685_internal_ops = {
+ .open = ov2685_open,
+};
+#endif
+
+static const struct v4l2_ctrl_ops ov2685_ctrl_ops = {
+ .s_ctrl = ov2685_set_ctrl,
+};
+
+static int ov2685_initialize_controls(struct ov2685 *ov2685)
+{
+ const struct ov2685_mode *mode;
+ struct v4l2_ctrl_handler *handler;
+ struct v4l2_ctrl *ctrl;
+ u64 exposure_max;
+ u32 pixel_rate, h_blank;
+ int ret;
+
+ handler = &ov2685->ctrl_handler;
+ mode = ov2685->cur_mode;
+ ret = v4l2_ctrl_handler_init(handler, 8);
+ if (ret)
+ return ret;
+ handler->lock = &ov2685->mutex;
+
+ ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ,
+ 0, 0, link_freq_menu_items);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ pixel_rate = (link_freq_menu_items[0] * 2 * OV2685_LANES) /
+ OV2685_BITS_PER_SAMPLE;
+ v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE,
+ 0, pixel_rate, 1, pixel_rate);
+
+ h_blank = mode->hts_def - mode->width;
+ ov2685->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
+ h_blank, h_blank, 1, h_blank);
+ if (ov2685->hblank)
+ ov2685->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ ov2685->vblank = v4l2_ctrl_new_std(handler, &ov2685_ctrl_ops,
+ V4L2_CID_VBLANK, mode->vts_def - mode->height,
+ OV2685_VTS_MAX - mode->height, 1,
+ mode->vts_def - mode->height);
+
+ exposure_max = mode->vts_def - 4;
+ ov2685->exposure = v4l2_ctrl_new_std(handler, &ov2685_ctrl_ops,
+ V4L2_CID_EXPOSURE, OV2685_EXPOSURE_MIN,
+ exposure_max, OV2685_EXPOSURE_STEP,
+ mode->exp_def);
+
+ ov2685->anal_gain = v4l2_ctrl_new_std(handler, &ov2685_ctrl_ops,
+ V4L2_CID_ANALOGUE_GAIN, OV2685_GAIN_MIN,
+ OV2685_GAIN_MAX, OV2685_GAIN_STEP,
+ OV2685_GAIN_DEFAULT);
+
+ ov2685->test_pattern = v4l2_ctrl_new_std_menu_items(handler,
+ &ov2685_ctrl_ops, V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(ov2685_test_pattern_menu) - 1,
+ 0, 0, ov2685_test_pattern_menu);
+
+ if (handler->error) {
+ ret = handler->error;
+ dev_err(&ov2685->client->dev,
+ "Failed to init controls(%d)\n", ret);
+ goto err_free_handler;
+ }
+
+ ov2685->subdev.ctrl_handler = handler;
+
+ return 0;
+
+err_free_handler:
+ v4l2_ctrl_handler_free(handler);
+
+ return ret;
+}
+
+static int ov2685_check_sensor_id(struct ov2685 *ov2685,
+ struct i2c_client *client)
+{
+ struct device *dev = &ov2685->client->dev;
+ int ret;
+ u32 id = 0;
+
+ ret = ov2685_read_reg(client, OV2685_REG_CHIP_ID,
+ OV2685_REG_VALUE_16BIT, &id);
+ if (id != CHIP_ID) {
+ dev_err(dev, "Unexpected sensor id(%04x), ret(%d)\n", id, ret);
+ return ret;
+ }
+
+ dev_info(dev, "Detected OV%04x sensor\n", CHIP_ID);
+
+ return 0;
+}
+
+static int ov2685_configure_regulators(struct ov2685 *ov2685)
+{
+ int i;
+
+ for (i = 0; i < OV2685_NUM_SUPPLIES; i++)
+ ov2685->supplies[i].supply = ov2685_supply_names[i];
+
+ return devm_regulator_bulk_get(&ov2685->client->dev,
+ OV2685_NUM_SUPPLIES,
+ ov2685->supplies);
+}
+
+static int ov2685_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct ov2685 *ov2685;
+ int ret;
+
+ ov2685 = devm_kzalloc(dev, sizeof(*ov2685), GFP_KERNEL);
+ if (!ov2685)
+ return -ENOMEM;
+
+ ov2685->client = client;
+ ov2685->cur_mode = &supported_modes[0];
+
+ ov2685->xvclk = devm_clk_get(dev, "xvclk");
+ if (IS_ERR(ov2685->xvclk)) {
+ dev_err(dev, "Failed to get xvclk\n");
+ return -EINVAL;
+ }
+ ret = clk_set_rate(ov2685->xvclk, OV2685_XVCLK_FREQ);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set xvclk rate (24MHz)\n");
+ return ret;
+ }
+ if (clk_get_rate(ov2685->xvclk) != OV2685_XVCLK_FREQ)
+ dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
+
+ ov2685->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(ov2685->reset_gpio)) {
+ dev_err(dev, "Failed to get reset-gpios\n");
+ return -EINVAL;
+ }
+
+ ret = ov2685_configure_regulators(ov2685);
+ if (ret) {
+ dev_err(dev, "Failed to get power regulators\n");
+ return ret;
+ }
+
+ mutex_init(&ov2685->mutex);
+ v4l2_i2c_subdev_init(&ov2685->subdev, client, &ov2685_subdev_ops);
+ ret = ov2685_initialize_controls(ov2685);
+ if (ret)
+ goto err_destroy_mutex;
+
+ ret = __ov2685_power_on(ov2685);
+ if (ret)
+ goto err_free_handler;
+
+ ret = ov2685_check_sensor_id(ov2685, client);
+ if (ret)
+ goto err_power_off;
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+ ov2685->subdev.internal_ops = &ov2685_internal_ops;
+ ov2685->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+#endif
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ ov2685->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ov2685->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ ret = media_entity_pads_init(&ov2685->subdev.entity, 1, &ov2685->pad);
+ if (ret < 0)
+ goto err_power_off;
+#endif
+
+ ret = v4l2_async_register_subdev(&ov2685->subdev);
+ if (ret) {
+ dev_err(dev, "v4l2 async register subdev failed\n");
+ goto err_clean_entity;
+ }
+
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_idle(dev);
+
+ return 0;
+
+err_clean_entity:
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ media_entity_cleanup(&ov2685->subdev.entity);
+#endif
+err_power_off:
+ __ov2685_power_off(ov2685);
+err_free_handler:
+ v4l2_ctrl_handler_free(&ov2685->ctrl_handler);
+err_destroy_mutex:
+ mutex_destroy(&ov2685->mutex);
+
+ return ret;
+}
+
+static int ov2685_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov2685 *ov2685 = to_ov2685(sd);
+
+ v4l2_async_unregister_subdev(sd);
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ media_entity_cleanup(&sd->entity);
+#endif
+ v4l2_ctrl_handler_free(&ov2685->ctrl_handler);
+ mutex_destroy(&ov2685->mutex);
+
+ pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev))
+ __ov2685_power_off(ov2685);
+ pm_runtime_set_suspended(&client->dev);
+
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id ov2685_of_match[] = {
+ { .compatible = "ovti,ov2685" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ov2685_of_match);
+#endif
+
+static struct i2c_driver ov2685_i2c_driver = {
+ .driver = {
+ .name = "ov2685",
+ .pm = &ov2685_pm_ops,
+ .of_match_table = of_match_ptr(ov2685_of_match),
+ },
+ .probe = &ov2685_probe,
+ .remove = &ov2685_remove,
+};
+
+module_i2c_driver(ov2685_i2c_driver);
+
+MODULE_DESCRIPTION("OmniVision ov2685 sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index e2dd352224c7..852026baa2e7 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -14,14 +14,14 @@
#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/device.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/types.h>
-#include <linux/gpio/consumer.h>
-#include <linux/regulator/consumer.h>
#include <media/v4l2-async.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
@@ -34,6 +34,8 @@
#define OV5640_DEFAULT_SLAVE_ID 0x3c
+#define OV5640_REG_SYS_RESET02 0x3002
+#define OV5640_REG_SYS_CLOCK_ENABLE02 0x3006
#define OV5640_REG_SYS_CTRL0 0x3008
#define OV5640_REG_CHIP_ID 0x300a
#define OV5640_REG_IO_MIPI_CTRL00 0x300e
@@ -114,6 +116,7 @@ struct ov5640_pixfmt {
};
static const struct ov5640_pixfmt ov5640_formats[] = {
+ { MEDIA_BUS_FMT_JPEG_1X8, V4L2_COLORSPACE_JPEG, },
{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_SRGB, },
{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_SRGB, },
{ MEDIA_BUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB, },
@@ -125,7 +128,7 @@ static const struct ov5640_pixfmt ov5640_formats[] = {
* to set the MIPI CSI-2 virtual channel.
*/
static unsigned int virtual_channel;
-module_param(virtual_channel, int, 0);
+module_param(virtual_channel, uint, 0444);
MODULE_PARM_DESC(virtual_channel,
"MIPI CSI-2 virtual channel (0..3), default 0");
@@ -136,7 +139,7 @@ static const int ov5640_framerates[] = {
/* regulator supplies */
static const char * const ov5640_supply_name[] = {
- "DOVDD", /* Digital I/O (1.8V) suppply */
+ "DOVDD", /* Digital I/O (1.8V) supply */
"DVDD", /* Digital Core (1.5V) supply */
"AVDD", /* Analog (2.8V) supply */
};
@@ -242,7 +245,6 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
*/
static const struct reg_value ov5640_init_setting_30fps_VGA[] = {
-
{0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0},
{0x3103, 0x03, 0, 0}, {0x3017, 0x00, 0, 0}, {0x3018, 0x00, 0, 0},
{0x3034, 0x18, 0, 0}, {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0},
@@ -331,7 +333,6 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = {
};
static const struct reg_value ov5640_setting_30fps_VGA_640_480[] = {
-
{0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
@@ -374,7 +375,6 @@ static const struct reg_value ov5640_setting_15fps_VGA_640_480[] = {
};
static const struct reg_value ov5640_setting_30fps_XGA_1024_768[] = {
-
{0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
@@ -481,6 +481,7 @@ static const struct reg_value ov5640_setting_30fps_QCIF_176_144[] = {
{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
};
+
static const struct reg_value ov5640_setting_15fps_QCIF_176_144[] = {
{0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
@@ -836,7 +837,7 @@ static int ov5640_write_reg(struct ov5640_dev *sensor, u16 reg, u8 val)
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret < 0) {
- v4l2_err(&sensor->sd, "%s: error: reg=%x, val=%x\n",
+ dev_err(&client->dev, "%s: error: reg=%x, val=%x\n",
__func__, reg, val);
return ret;
}
@@ -865,8 +866,11 @@ static int ov5640_read_reg(struct ov5640_dev *sensor, u16 reg, u8 *val)
msg[1].len = 1;
ret = i2c_transfer(client->adapter, msg, 2);
- if (ret < 0)
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: error: reg=%x\n",
+ __func__, reg);
return ret;
+ }
*val = buf[0];
return 0;
@@ -880,7 +884,7 @@ static int ov5640_read_reg16(struct ov5640_dev *sensor, u16 reg, u16 *val)
ret = ov5640_read_reg(sensor, reg, &hi);
if (ret)
return ret;
- ret = ov5640_read_reg(sensor, reg+1, &lo);
+ ret = ov5640_read_reg(sensor, reg + 1, &lo);
if (ret)
return ret;
@@ -941,7 +945,7 @@ static int ov5640_load_regs(struct ov5640_dev *sensor,
break;
if (delay_ms)
- usleep_range(1000*delay_ms, 1000*delay_ms+100);
+ usleep_range(1000 * delay_ms, 1000 * delay_ms + 100);
}
return ret;
@@ -1283,7 +1287,6 @@ static int ov5640_set_bandingfilter(struct ov5640_dev *sensor)
return ret;
prev_vts = ret;
-
/* calculate banding filter */
/* 60Hz */
band_step60 = sensor->prev_sysclk * 100 / sensor->prev_hts * 100 / 120;
@@ -1355,11 +1358,16 @@ static int ov5640_binning_on(struct ov5640_dev *sensor)
static int ov5640_set_virtual_channel(struct ov5640_dev *sensor)
{
+ struct i2c_client *client = sensor->i2c_client;
u8 temp, channel = virtual_channel;
int ret;
- if (channel > 3)
+ if (channel > 3) {
+ dev_err(&client->dev,
+ "%s: wrong virtual_channel parameter, expected (0..3), got %d\n",
+ __func__, channel);
return -EINVAL;
+ }
ret = ov5640_read_reg(sensor, OV5640_REG_DEBUG_MODE, &temp);
if (ret)
@@ -1399,8 +1407,8 @@ ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr,
* sensor changes between scaling and subsampling, go through
* exposure calculation
*/
-static int ov5640_set_mode_exposure_calc(
- struct ov5640_dev *sensor, const struct ov5640_mode_info *mode)
+static int ov5640_set_mode_exposure_calc(struct ov5640_dev *sensor,
+ const struct ov5640_mode_info *mode)
{
u32 prev_shutter, prev_gain16;
u32 cap_shutter, cap_gain16;
@@ -1410,7 +1418,7 @@ static int ov5640_set_mode_exposure_calc(
u8 average;
int ret;
- if (mode->reg_data == NULL)
+ if (!mode->reg_data)
return -EINVAL;
/* read preview shutter */
@@ -1564,7 +1572,7 @@ static int ov5640_set_mode_direct(struct ov5640_dev *sensor,
{
int ret;
- if (mode->reg_data == NULL)
+ if (!mode->reg_data)
return -EINVAL;
/* Write capture setting */
@@ -1633,6 +1641,9 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
return 0;
}
+static int ov5640_set_framefmt(struct ov5640_dev *sensor,
+ struct v4l2_mbus_framefmt *format);
+
/* restore the last set video mode after chip power-on */
static int ov5640_restore_mode(struct ov5640_dev *sensor)
{
@@ -1644,7 +1655,11 @@ static int ov5640_restore_mode(struct ov5640_dev *sensor)
return ret;
/* now restore the last capture mode */
- return ov5640_set_mode(sensor, &ov5640_mode_init_data);
+ ret = ov5640_set_mode(sensor, &ov5640_mode_init_data);
+ if (ret < 0)
+ return ret;
+
+ return ov5640_set_framefmt(sensor, &sensor->fmt);
}
static void ov5640_power(struct ov5640_dev *sensor, bool enable)
@@ -1866,7 +1881,13 @@ static int ov5640_try_fmt_internal(struct v4l2_subdev *sd,
if (ov5640_formats[i].code == fmt->code)
break;
if (i >= ARRAY_SIZE(ov5640_formats))
- fmt->code = ov5640_formats[0].code;
+ i = 0;
+
+ fmt->code = ov5640_formats[i].code;
+ fmt->colorspace = ov5640_formats[i].colorspace;
+ fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
+ fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
return 0;
}
@@ -1877,6 +1898,7 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd,
{
struct ov5640_dev *sensor = to_ov5640_dev(sd);
const struct ov5640_mode_info *new_mode;
+ struct v4l2_mbus_framefmt *mbus_fmt = &format->format;
int ret;
if (format->pad != 0)
@@ -1889,7 +1911,7 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd,
goto out;
}
- ret = ov5640_try_fmt_internal(sd, &format->format,
+ ret = ov5640_try_fmt_internal(sd, mbus_fmt,
sensor->current_fr, &new_mode);
if (ret)
goto out;
@@ -1898,12 +1920,12 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *fmt =
v4l2_subdev_get_try_format(sd, cfg, 0);
- *fmt = format->format;
+ *fmt = *mbus_fmt;
goto out;
}
sensor->current_mode = new_mode;
- sensor->fmt = format->format;
+ sensor->fmt = *mbus_fmt;
sensor->pending_mode_change = true;
out:
mutex_unlock(&sensor->lock);
@@ -1915,6 +1937,7 @@ static int ov5640_set_framefmt(struct ov5640_dev *sensor,
{
int ret = 0;
bool is_rgb = false;
+ bool is_jpeg = false;
u8 val;
switch (format->code) {
@@ -1936,6 +1959,11 @@ static int ov5640_set_framefmt(struct ov5640_dev *sensor,
val = 0x61;
is_rgb = true;
break;
+ case MEDIA_BUS_FMT_JPEG_1X8:
+ /* YUV422, YUYV */
+ val = 0x30;
+ is_jpeg = true;
+ break;
default:
return -EINVAL;
}
@@ -1946,8 +1974,40 @@ static int ov5640_set_framefmt(struct ov5640_dev *sensor,
return ret;
/* FORMAT MUX CONTROL: ISP YUV or RGB */
- return ov5640_write_reg(sensor, OV5640_REG_ISP_FORMAT_MUX_CTRL,
- is_rgb ? 0x01 : 0x00);
+ ret = ov5640_write_reg(sensor, OV5640_REG_ISP_FORMAT_MUX_CTRL,
+ is_rgb ? 0x01 : 0x00);
+ if (ret)
+ return ret;
+
+ /*
+ * TIMING TC REG21:
+ * - [5]: JPEG enable
+ */
+ ret = ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG21,
+ BIT(5), is_jpeg ? BIT(5) : 0);
+ if (ret)
+ return ret;
+
+ /*
+ * SYSTEM RESET02:
+ * - [4]: Reset JFIFO
+ * - [3]: Reset SFIFO
+ * - [2]: Reset JPEG
+ */
+ ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_RESET02,
+ BIT(4) | BIT(3) | BIT(2),
+ is_jpeg ? 0 : (BIT(4) | BIT(3) | BIT(2)));
+ if (ret)
+ return ret;
+
+ /*
+ * CLOCK ENABLE02:
+ * - [5]: Enable JPEG 2x clock
+ * - [3]: Enable JPEG clock
+ */
+ return ov5640_mod_reg(sensor, OV5640_REG_SYS_CLOCK_ENABLE02,
+ BIT(5) | BIT(3),
+ is_jpeg ? (BIT(5) | BIT(3)) : 0);
}
/*
@@ -2073,7 +2133,8 @@ static int ov5640_set_ctrl_gain(struct ov5640_dev *sensor, int auto_gain)
if (ctrls->auto_gain->is_new) {
ret = ov5640_mod_reg(sensor, OV5640_REG_AEC_PK_MANUAL,
- BIT(1), ctrls->auto_gain->val ? 0 : BIT(1));
+ BIT(1),
+ ctrls->auto_gain->val ? 0 : BIT(1));
if (ret)
return ret;
}
@@ -2253,10 +2314,12 @@ static int ov5640_enum_frame_size(struct v4l2_subdev *sd,
if (fse->index >= OV5640_NUM_MODES)
return -EINVAL;
- fse->min_width = fse->max_width =
+ fse->min_width =
ov5640_mode_data[0][fse->index].width;
- fse->min_height = fse->max_height =
+ fse->max_width = fse->min_width;
+ fse->min_height =
ov5640_mode_data[0][fse->index].height;
+ fse->max_height = fse->min_height;
return 0;
}
@@ -2325,6 +2388,8 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd,
sensor->current_fr = frame_rate;
sensor->frame_interval = fi->interval;
+ sensor->current_mode = ov5640_find_mode(sensor, frame_rate, mode->width,
+ mode->height, true);
sensor->pending_mode_change = true;
out:
mutex_unlock(&sensor->lock);
@@ -2332,8 +2397,8 @@ out:
}
static int ov5640_enum_mbus_code(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_mbus_code_enum *code)
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
{
if (code->pad != 0)
return -EINVAL;
@@ -2445,6 +2510,7 @@ static int ov5640_probe(struct i2c_client *client,
struct device *dev = &client->dev;
struct fwnode_handle *endpoint;
struct ov5640_dev *sensor;
+ struct v4l2_mbus_framefmt *fmt;
int ret;
sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
@@ -2452,10 +2518,15 @@ static int ov5640_probe(struct i2c_client *client,
return -ENOMEM;
sensor->i2c_client = client;
- sensor->fmt.code = MEDIA_BUS_FMT_UYVY8_2X8;
- sensor->fmt.width = 640;
- sensor->fmt.height = 480;
- sensor->fmt.field = V4L2_FIELD_NONE;
+ fmt = &sensor->fmt;
+ fmt->code = ov5640_formats[0].code;
+ fmt->colorspace = ov5640_formats[0].colorspace;
+ fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
+ fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
+ fmt->width = 640;
+ fmt->height = 480;
+ fmt->field = V4L2_FIELD_NONE;
sensor->frame_interval.numerator = 1;
sensor->frame_interval.denominator = ov5640_framerates[OV5640_30_FPS];
sensor->current_fr = OV5640_30_FPS;
diff --git a/drivers/media/i2c/ov5645.c b/drivers/media/i2c/ov5645.c
index d28845f7356f..4e3142a7e5a7 100644
--- a/drivers/media/i2c/ov5645.c
+++ b/drivers/media/i2c/ov5645.c
@@ -959,23 +959,6 @@ __ov5645_get_pad_crop(struct ov5645 *ov5645, struct v4l2_subdev_pad_config *cfg,
}
}
-static const struct ov5645_mode_info *
-ov5645_find_nearest_mode(unsigned int width, unsigned int height)
-{
- int i;
-
- for (i = ARRAY_SIZE(ov5645_mode_info_data) - 1; i >= 0; i--) {
- if (ov5645_mode_info_data[i].width <= width &&
- ov5645_mode_info_data[i].height <= height)
- break;
- }
-
- if (i < 0)
- i = 0;
-
- return &ov5645_mode_info_data[i];
-}
-
static int ov5645_set_format(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *format)
@@ -989,8 +972,11 @@ static int ov5645_set_format(struct v4l2_subdev *sd,
__crop = __ov5645_get_pad_crop(ov5645, cfg, format->pad,
format->which);
- new_mode = ov5645_find_nearest_mode(format->format.width,
- format->format.height);
+ new_mode = v4l2_find_nearest_size(ov5645_mode_info_data,
+ ARRAY_SIZE(ov5645_mode_info_data),
+ width, height,
+ format->format.width, format->format.height);
+
__crop->width = new_mode->width;
__crop->height = new_mode->height;
@@ -1131,13 +1117,14 @@ static int ov5645_probe(struct i2c_client *client,
ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint),
&ov5645->ep);
+
+ of_node_put(endpoint);
+
if (ret < 0) {
dev_err(dev, "parsing endpoint node failed\n");
return ret;
}
- of_node_put(endpoint);
-
if (ov5645->ep.bus_type != V4L2_MBUS_CSI2) {
dev_err(dev, "invalid bus type, must be CSI2\n");
return -EINVAL;
diff --git a/drivers/media/i2c/ov5670.c b/drivers/media/i2c/ov5670.c
index 9f9196568eb8..7b7c74d77370 100644
--- a/drivers/media/i2c/ov5670.c
+++ b/drivers/media/i2c/ov5670.c
@@ -1,16 +1,5 @@
-/*
- * Copyright (c) 2017 Intel Corporation.
- *
- * 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.
- *
- * 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.
- *
- */
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2017 Intel Corporation.
#include <linux/acpi.h>
#include <linux/i2c.h>
@@ -1853,8 +1842,8 @@ static int ov5670_read_reg(struct ov5670 *ov5670, u16 reg, unsigned int len,
struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd);
struct i2c_msg msgs[2];
u8 *data_be_p;
- u32 data_be = 0;
- u16 reg_addr_be = cpu_to_be16(reg);
+ __be32 data_be = 0;
+ __be16 reg_addr_be = cpu_to_be16(reg);
int ret;
if (len > 4)
@@ -1891,6 +1880,7 @@ static int ov5670_write_reg(struct ov5670 *ov5670, u16 reg, unsigned int len,
int val_i;
u8 buf[6];
u8 *val_p;
+ __be32 tmp;
if (len > 4)
return -EINVAL;
@@ -1898,8 +1888,8 @@ static int ov5670_write_reg(struct ov5670 *ov5670, u16 reg, unsigned int len,
buf[0] = reg >> 8;
buf[1] = reg & 0xff;
- val = cpu_to_be32(val);
- val_p = (u8 *)&val;
+ tmp = cpu_to_be32(val);
+ val_p = (u8 *)&tmp;
buf_i = 2;
val_i = 4 - len;
@@ -2180,36 +2170,6 @@ static int ov5670_enum_frame_size(struct v4l2_subdev *sd,
return 0;
}
-/* Calculate resolution distance */
-static int ov5670_get_reso_dist(const struct ov5670_mode *mode,
- struct v4l2_mbus_framefmt *framefmt)
-{
- return abs(mode->width - framefmt->width) +
- abs(mode->height - framefmt->height);
-}
-
-/* Find the closest supported resolution to the requested resolution */
-static const struct ov5670_mode *ov5670_find_best_fit(
- struct ov5670 *ov5670,
- struct v4l2_subdev_format *fmt)
-{
- struct v4l2_mbus_framefmt *framefmt = &fmt->format;
- int dist;
- int cur_best_fit = 0;
- int cur_best_fit_dist = -1;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(supported_modes); i++) {
- dist = ov5670_get_reso_dist(&supported_modes[i], framefmt);
- if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) {
- cur_best_fit_dist = dist;
- cur_best_fit = i;
- }
- }
-
- return &supported_modes[cur_best_fit];
-}
-
static void ov5670_update_pad_format(const struct ov5670_mode *mode,
struct v4l2_subdev_format *fmt)
{
@@ -2259,7 +2219,10 @@ static int ov5670_set_pad_format(struct v4l2_subdev *sd,
fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
- mode = ov5670_find_best_fit(ov5670, fmt);
+ mode = v4l2_find_nearest_size(supported_modes,
+ ARRAY_SIZE(supported_modes),
+ width, height,
+ fmt->format.width, fmt->format.height);
ov5670_update_pad_format(mode, fmt);
if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
*v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format;
diff --git a/drivers/media/i2c/ov5695.c b/drivers/media/i2c/ov5695.c
new file mode 100644
index 000000000000..9be38a0a2046
--- /dev/null
+++ b/drivers/media/i2c/ov5695.c
@@ -0,0 +1,1399 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ov5695 driver
+ *
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/sysfs.h>
+#include <media/media-entity.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+#ifndef V4L2_CID_DIGITAL_GAIN
+#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN
+#endif
+
+/* 45Mhz * 4 Binning */
+#define OV5695_PIXEL_RATE (45 * 1000 * 1000 * 4)
+#define OV5695_XVCLK_FREQ 24000000
+
+#define CHIP_ID 0x005695
+#define OV5695_REG_CHIP_ID 0x300a
+
+#define OV5695_REG_CTRL_MODE 0x0100
+#define OV5695_MODE_SW_STANDBY 0x0
+#define OV5695_MODE_STREAMING BIT(0)
+
+#define OV5695_REG_EXPOSURE 0x3500
+#define OV5695_EXPOSURE_MIN 4
+#define OV5695_EXPOSURE_STEP 1
+#define OV5695_VTS_MAX 0x7fff
+
+#define OV5695_REG_ANALOG_GAIN 0x3509
+#define ANALOG_GAIN_MIN 0x10
+#define ANALOG_GAIN_MAX 0xf8
+#define ANALOG_GAIN_STEP 1
+#define ANALOG_GAIN_DEFAULT 0xf8
+
+#define OV5695_REG_DIGI_GAIN_H 0x350a
+#define OV5695_REG_DIGI_GAIN_L 0x350b
+#define OV5695_DIGI_GAIN_L_MASK 0x3f
+#define OV5695_DIGI_GAIN_H_SHIFT 6
+#define OV5695_DIGI_GAIN_MIN 0
+#define OV5695_DIGI_GAIN_MAX (0x4000 - 1)
+#define OV5695_DIGI_GAIN_STEP 1
+#define OV5695_DIGI_GAIN_DEFAULT 1024
+
+#define OV5695_REG_TEST_PATTERN 0x4503
+#define OV5695_TEST_PATTERN_ENABLE 0x80
+#define OV5695_TEST_PATTERN_DISABLE 0x0
+
+#define OV5695_REG_VTS 0x380e
+
+#define REG_NULL 0xFFFF
+
+#define OV5695_REG_VALUE_08BIT 1
+#define OV5695_REG_VALUE_16BIT 2
+#define OV5695_REG_VALUE_24BIT 3
+
+#define OV5695_LANES 2
+#define OV5695_BITS_PER_SAMPLE 10
+
+static const char * const ov5695_supply_names[] = {
+ "avdd", /* Analog power */
+ "dovdd", /* Digital I/O power */
+ "dvdd", /* Digital core power */
+};
+
+#define OV5695_NUM_SUPPLIES ARRAY_SIZE(ov5695_supply_names)
+
+struct regval {
+ u16 addr;
+ u8 val;
+};
+
+struct ov5695_mode {
+ u32 width;
+ u32 height;
+ u32 max_fps;
+ u32 hts_def;
+ u32 vts_def;
+ u32 exp_def;
+ const struct regval *reg_list;
+};
+
+struct ov5695 {
+ struct i2c_client *client;
+ struct clk *xvclk;
+ struct gpio_desc *reset_gpio;
+ struct regulator_bulk_data supplies[OV5695_NUM_SUPPLIES];
+
+ struct v4l2_subdev subdev;
+ struct media_pad pad;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *anal_gain;
+ struct v4l2_ctrl *digi_gain;
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *test_pattern;
+ struct mutex mutex;
+ bool streaming;
+ const struct ov5695_mode *cur_mode;
+};
+
+#define to_ov5695(sd) container_of(sd, struct ov5695, subdev)
+
+/*
+ * Xclk 24Mhz
+ * Pclk 45Mhz
+ * linelength 672(0x2a0)
+ * framelength 2232(0x8b8)
+ * grabwindow_width 1296
+ * grabwindow_height 972
+ * max_framerate 30fps
+ * mipi_datarate per lane 840Mbps
+ */
+static const struct regval ov5695_global_regs[] = {
+ {0x0103, 0x01},
+ {0x0100, 0x00},
+ {0x0300, 0x04},
+ {0x0301, 0x00},
+ {0x0302, 0x69},
+ {0x0303, 0x00},
+ {0x0304, 0x00},
+ {0x0305, 0x01},
+ {0x0307, 0x00},
+ {0x030b, 0x00},
+ {0x030c, 0x00},
+ {0x030d, 0x1e},
+ {0x030e, 0x04},
+ {0x030f, 0x03},
+ {0x0312, 0x01},
+ {0x3000, 0x00},
+ {0x3002, 0xa1},
+ {0x3008, 0x00},
+ {0x3010, 0x00},
+ {0x3022, 0x51},
+ {0x3106, 0x15},
+ {0x3107, 0x01},
+ {0x3108, 0x05},
+ {0x3500, 0x00},
+ {0x3501, 0x45},
+ {0x3502, 0x00},
+ {0x3503, 0x08},
+ {0x3504, 0x03},
+ {0x3505, 0x8c},
+ {0x3507, 0x03},
+ {0x3508, 0x00},
+ {0x3509, 0x10},
+ {0x350c, 0x00},
+ {0x350d, 0x80},
+ {0x3510, 0x00},
+ {0x3511, 0x02},
+ {0x3512, 0x00},
+ {0x3601, 0x55},
+ {0x3602, 0x58},
+ {0x3614, 0x30},
+ {0x3615, 0x77},
+ {0x3621, 0x08},
+ {0x3624, 0x40},
+ {0x3633, 0x0c},
+ {0x3634, 0x0c},
+ {0x3635, 0x0c},
+ {0x3636, 0x0c},
+ {0x3638, 0x00},
+ {0x3639, 0x00},
+ {0x363a, 0x00},
+ {0x363b, 0x00},
+ {0x363c, 0xff},
+ {0x363d, 0xfa},
+ {0x3650, 0x44},
+ {0x3651, 0x44},
+ {0x3652, 0x44},
+ {0x3653, 0x44},
+ {0x3654, 0x44},
+ {0x3655, 0x44},
+ {0x3656, 0x44},
+ {0x3657, 0x44},
+ {0x3660, 0x00},
+ {0x3661, 0x00},
+ {0x3662, 0x00},
+ {0x366a, 0x00},
+ {0x366e, 0x0c},
+ {0x3673, 0x04},
+ {0x3700, 0x14},
+ {0x3703, 0x0c},
+ {0x3715, 0x01},
+ {0x3733, 0x10},
+ {0x3734, 0x40},
+ {0x373f, 0xa0},
+ {0x3765, 0x20},
+ {0x37a1, 0x1d},
+ {0x37a8, 0x26},
+ {0x37ab, 0x14},
+ {0x37c2, 0x04},
+ {0x37cb, 0x09},
+ {0x37cc, 0x13},
+ {0x37cd, 0x1f},
+ {0x37ce, 0x1f},
+ {0x3800, 0x00},
+ {0x3801, 0x00},
+ {0x3802, 0x00},
+ {0x3803, 0x00},
+ {0x3804, 0x0a},
+ {0x3805, 0x3f},
+ {0x3806, 0x07},
+ {0x3807, 0xaf},
+ {0x3808, 0x05},
+ {0x3809, 0x10},
+ {0x380a, 0x03},
+ {0x380b, 0xcc},
+ {0x380c, 0x02},
+ {0x380d, 0xa0},
+ {0x380e, 0x08},
+ {0x380f, 0xb8},
+ {0x3810, 0x00},
+ {0x3811, 0x06},
+ {0x3812, 0x00},
+ {0x3813, 0x06},
+ {0x3814, 0x03},
+ {0x3815, 0x01},
+ {0x3816, 0x03},
+ {0x3817, 0x01},
+ {0x3818, 0x00},
+ {0x3819, 0x00},
+ {0x381a, 0x00},
+ {0x381b, 0x01},
+ {0x3820, 0x8b},
+ {0x3821, 0x01},
+ {0x3c80, 0x08},
+ {0x3c82, 0x00},
+ {0x3c83, 0x00},
+ {0x3c88, 0x00},
+ {0x3d85, 0x14},
+ {0x3f02, 0x08},
+ {0x3f03, 0x10},
+ {0x4008, 0x02},
+ {0x4009, 0x09},
+ {0x404e, 0x20},
+ {0x4501, 0x00},
+ {0x4502, 0x10},
+ {0x4800, 0x00},
+ {0x481f, 0x2a},
+ {0x4837, 0x13},
+ {0x5000, 0x17},
+ {0x5780, 0x3e},
+ {0x5781, 0x0f},
+ {0x5782, 0x44},
+ {0x5783, 0x02},
+ {0x5784, 0x01},
+ {0x5785, 0x01},
+ {0x5786, 0x00},
+ {0x5787, 0x04},
+ {0x5788, 0x02},
+ {0x5789, 0x0f},
+ {0x578a, 0xfd},
+ {0x578b, 0xf5},
+ {0x578c, 0xf5},
+ {0x578d, 0x03},
+ {0x578e, 0x08},
+ {0x578f, 0x0c},
+ {0x5790, 0x08},
+ {0x5791, 0x06},
+ {0x5792, 0x00},
+ {0x5793, 0x52},
+ {0x5794, 0xa3},
+ {0x5b00, 0x00},
+ {0x5b01, 0x1c},
+ {0x5b02, 0x00},
+ {0x5b03, 0x7f},
+ {0x5b05, 0x6c},
+ {0x5e10, 0xfc},
+ {0x4010, 0xf1},
+ {0x3503, 0x08},
+ {0x3505, 0x8c},
+ {0x3507, 0x03},
+ {0x3508, 0x00},
+ {0x3509, 0xf8},
+ {REG_NULL, 0x00},
+};
+
+/*
+ * Xclk 24Mhz
+ * Pclk 45Mhz
+ * linelength 740(0x2e4)
+ * framelength 2024(0x7e8)
+ * grabwindow_width 2592
+ * grabwindow_height 1944
+ * max_framerate 30fps
+ * mipi_datarate per lane 840Mbps
+ */
+static const struct regval ov5695_2592x1944_regs[] = {
+ {0x3501, 0x7e},
+ {0x366e, 0x18},
+ {0x3800, 0x00},
+ {0x3801, 0x00},
+ {0x3802, 0x00},
+ {0x3803, 0x04},
+ {0x3804, 0x0a},
+ {0x3805, 0x3f},
+ {0x3806, 0x07},
+ {0x3807, 0xab},
+ {0x3808, 0x0a},
+ {0x3809, 0x20},
+ {0x380a, 0x07},
+ {0x380b, 0x98},
+ {0x380c, 0x02},
+ {0x380d, 0xe4},
+ {0x380e, 0x07},
+ {0x380f, 0xe8},
+ {0x3811, 0x06},
+ {0x3813, 0x08},
+ {0x3814, 0x01},
+ {0x3816, 0x01},
+ {0x3817, 0x01},
+ {0x3820, 0x88},
+ {0x3821, 0x00},
+ {0x4501, 0x00},
+ {0x4008, 0x04},
+ {0x4009, 0x13},
+ {REG_NULL, 0x00},
+};
+
+/*
+ * Xclk 24Mhz
+ * Pclk 45Mhz
+ * linelength 672(0x2a0)
+ * framelength 2232(0x8b8)
+ * grabwindow_width 1920
+ * grabwindow_height 1080
+ * max_framerate 30fps
+ * mipi_datarate per lane 840Mbps
+ */
+static const struct regval ov5695_1920x1080_regs[] = {
+ {0x3501, 0x45},
+ {0x366e, 0x18},
+ {0x3800, 0x01},
+ {0x3801, 0x50},
+ {0x3802, 0x01},
+ {0x3803, 0xb8},
+ {0x3804, 0x08},
+ {0x3805, 0xef},
+ {0x3806, 0x05},
+ {0x3807, 0xf7},
+ {0x3808, 0x07},
+ {0x3809, 0x80},
+ {0x380a, 0x04},
+ {0x380b, 0x38},
+ {0x380c, 0x02},
+ {0x380d, 0xa0},
+ {0x380e, 0x08},
+ {0x380f, 0xb8},
+ {0x3811, 0x06},
+ {0x3813, 0x04},
+ {0x3814, 0x01},
+ {0x3816, 0x01},
+ {0x3817, 0x01},
+ {0x3820, 0x88},
+ {0x3821, 0x00},
+ {0x4501, 0x00},
+ {0x4008, 0x04},
+ {0x4009, 0x13},
+ {REG_NULL, 0x00}
+};
+
+/*
+ * Xclk 24Mhz
+ * Pclk 45Mhz
+ * linelength 740(0x02e4)
+ * framelength 1012(0x03f4)
+ * grabwindow_width 1296
+ * grabwindow_height 972
+ * max_framerate 60fps
+ * mipi_datarate per lane 840Mbps
+ */
+static const struct regval ov5695_1296x972_regs[] = {
+ {0x0103, 0x01},
+ {0x0100, 0x00},
+ {0x0300, 0x04},
+ {0x0301, 0x00},
+ {0x0302, 0x69},
+ {0x0303, 0x00},
+ {0x0304, 0x00},
+ {0x0305, 0x01},
+ {0x0307, 0x00},
+ {0x030b, 0x00},
+ {0x030c, 0x00},
+ {0x030d, 0x1e},
+ {0x030e, 0x04},
+ {0x030f, 0x03},
+ {0x0312, 0x01},
+ {0x3000, 0x00},
+ {0x3002, 0xa1},
+ {0x3008, 0x00},
+ {0x3010, 0x00},
+ {0x3016, 0x32},
+ {0x3022, 0x51},
+ {0x3106, 0x15},
+ {0x3107, 0x01},
+ {0x3108, 0x05},
+ {0x3500, 0x00},
+ {0x3501, 0x3e},
+ {0x3502, 0x00},
+ {0x3503, 0x08},
+ {0x3504, 0x03},
+ {0x3505, 0x8c},
+ {0x3507, 0x03},
+ {0x3508, 0x00},
+ {0x3509, 0x10},
+ {0x350c, 0x00},
+ {0x350d, 0x80},
+ {0x3510, 0x00},
+ {0x3511, 0x02},
+ {0x3512, 0x00},
+ {0x3601, 0x55},
+ {0x3602, 0x58},
+ {0x3611, 0x58},
+ {0x3614, 0x30},
+ {0x3615, 0x77},
+ {0x3621, 0x08},
+ {0x3624, 0x40},
+ {0x3633, 0x0c},
+ {0x3634, 0x0c},
+ {0x3635, 0x0c},
+ {0x3636, 0x0c},
+ {0x3638, 0x00},
+ {0x3639, 0x00},
+ {0x363a, 0x00},
+ {0x363b, 0x00},
+ {0x363c, 0xff},
+ {0x363d, 0xfa},
+ {0x3650, 0x44},
+ {0x3651, 0x44},
+ {0x3652, 0x44},
+ {0x3653, 0x44},
+ {0x3654, 0x44},
+ {0x3655, 0x44},
+ {0x3656, 0x44},
+ {0x3657, 0x44},
+ {0x3660, 0x00},
+ {0x3661, 0x00},
+ {0x3662, 0x00},
+ {0x366a, 0x00},
+ {0x366e, 0x0c},
+ {0x3673, 0x04},
+ {0x3700, 0x14},
+ {0x3703, 0x0c},
+ {0x3706, 0x24},
+ {0x3714, 0x27},
+ {0x3715, 0x01},
+ {0x3716, 0x00},
+ {0x3717, 0x02},
+ {0x3733, 0x10},
+ {0x3734, 0x40},
+ {0x373f, 0xa0},
+ {0x3765, 0x20},
+ {0x37a1, 0x1d},
+ {0x37a8, 0x26},
+ {0x37ab, 0x14},
+ {0x37c2, 0x04},
+ {0x37c3, 0xf0},
+ {0x37cb, 0x09},
+ {0x37cc, 0x13},
+ {0x37cd, 0x1f},
+ {0x37ce, 0x1f},
+ {0x3800, 0x00},
+ {0x3801, 0x00},
+ {0x3802, 0x00},
+ {0x3803, 0x00},
+ {0x3804, 0x0a},
+ {0x3805, 0x3f},
+ {0x3806, 0x07},
+ {0x3807, 0xaf},
+ {0x3808, 0x05},
+ {0x3809, 0x10},
+ {0x380a, 0x03},
+ {0x380b, 0xcc},
+ {0x380c, 0x02},
+ {0x380d, 0xe4},
+ {0x380e, 0x03},
+ {0x380f, 0xf4},
+ {0x3810, 0x00},
+ {0x3811, 0x00},
+ {0x3812, 0x00},
+ {0x3813, 0x06},
+ {0x3814, 0x03},
+ {0x3815, 0x01},
+ {0x3816, 0x03},
+ {0x3817, 0x01},
+ {0x3818, 0x00},
+ {0x3819, 0x00},
+ {0x381a, 0x00},
+ {0x381b, 0x01},
+ {0x3820, 0x8b},
+ {0x3821, 0x01},
+ {0x3c80, 0x08},
+ {0x3c82, 0x00},
+ {0x3c83, 0x00},
+ {0x3c88, 0x00},
+ {0x3d85, 0x14},
+ {0x3f02, 0x08},
+ {0x3f03, 0x10},
+ {0x4008, 0x02},
+ {0x4009, 0x09},
+ {0x404e, 0x20},
+ {0x4501, 0x00},
+ {0x4502, 0x10},
+ {0x4800, 0x00},
+ {0x481f, 0x2a},
+ {0x4837, 0x13},
+ {0x5000, 0x13},
+ {0x5780, 0x3e},
+ {0x5781, 0x0f},
+ {0x5782, 0x44},
+ {0x5783, 0x02},
+ {0x5784, 0x01},
+ {0x5785, 0x01},
+ {0x5786, 0x00},
+ {0x5787, 0x04},
+ {0x5788, 0x02},
+ {0x5789, 0x0f},
+ {0x578a, 0xfd},
+ {0x578b, 0xf5},
+ {0x578c, 0xf5},
+ {0x578d, 0x03},
+ {0x578e, 0x08},
+ {0x578f, 0x0c},
+ {0x5790, 0x08},
+ {0x5791, 0x06},
+ {0x5792, 0x00},
+ {0x5793, 0x52},
+ {0x5794, 0xa3},
+ {0x5b00, 0x00},
+ {0x5b01, 0x1c},
+ {0x5b02, 0x00},
+ {0x5b03, 0x7f},
+ {0x5b05, 0x6c},
+ {0x5e10, 0xfc},
+ {0x4010, 0xf1},
+ {0x3503, 0x08},
+ {0x3505, 0x8c},
+ {0x3507, 0x03},
+ {0x3508, 0x00},
+ {0x3509, 0xf8},
+ {0x0100, 0x01},
+ {REG_NULL, 0x00}
+};
+
+/*
+ * Xclk 24Mhz
+ * Pclk 45Mhz
+ * linelength 672(0x2a0)
+ * framelength 2232(0x8b8)
+ * grabwindow_width 1280
+ * grabwindow_height 720
+ * max_framerate 30fps
+ * mipi_datarate per lane 840Mbps
+ */
+static const struct regval ov5695_1280x720_regs[] = {
+ {0x3501, 0x45},
+ {0x366e, 0x0c},
+ {0x3800, 0x00},
+ {0x3801, 0x00},
+ {0x3802, 0x01},
+ {0x3803, 0x00},
+ {0x3804, 0x0a},
+ {0x3805, 0x3f},
+ {0x3806, 0x06},
+ {0x3807, 0xaf},
+ {0x3808, 0x05},
+ {0x3809, 0x00},
+ {0x380a, 0x02},
+ {0x380b, 0xd0},
+ {0x380c, 0x02},
+ {0x380d, 0xa0},
+ {0x380e, 0x08},
+ {0x380f, 0xb8},
+ {0x3811, 0x06},
+ {0x3813, 0x02},
+ {0x3814, 0x03},
+ {0x3816, 0x03},
+ {0x3817, 0x01},
+ {0x3820, 0x8b},
+ {0x3821, 0x01},
+ {0x4501, 0x00},
+ {0x4008, 0x02},
+ {0x4009, 0x09},
+ {REG_NULL, 0x00}
+};
+
+/*
+ * Xclk 24Mhz
+ * Pclk 45Mhz
+ * linelength 672(0x2a0)
+ * framelength 558(0x22e)
+ * grabwindow_width 640
+ * grabwindow_height 480
+ * max_framerate 120fps
+ * mipi_datarate per lane 840Mbps
+ */
+static const struct regval ov5695_640x480_regs[] = {
+ {0x3501, 0x22},
+ {0x366e, 0x0c},
+ {0x3800, 0x00},
+ {0x3801, 0x00},
+ {0x3802, 0x00},
+ {0x3803, 0x08},
+ {0x3804, 0x0a},
+ {0x3805, 0x3f},
+ {0x3806, 0x07},
+ {0x3807, 0xa7},
+ {0x3808, 0x02},
+ {0x3809, 0x80},
+ {0x380a, 0x01},
+ {0x380b, 0xe0},
+ {0x380c, 0x02},
+ {0x380d, 0xa0},
+ {0x380e, 0x02},
+ {0x380f, 0x2e},
+ {0x3811, 0x06},
+ {0x3813, 0x04},
+ {0x3814, 0x07},
+ {0x3816, 0x05},
+ {0x3817, 0x03},
+ {0x3820, 0x8d},
+ {0x3821, 0x01},
+ {0x4501, 0x00},
+ {0x4008, 0x02},
+ {0x4009, 0x09},
+ {REG_NULL, 0x00}
+};
+
+static const struct ov5695_mode supported_modes[] = {
+ {
+ .width = 2592,
+ .height = 1944,
+ .max_fps = 30,
+ .exp_def = 0x0450,
+ .hts_def = 0x02e4 * 4,
+ .vts_def = 0x07e8,
+ .reg_list = ov5695_2592x1944_regs,
+ },
+ {
+ .width = 1920,
+ .height = 1080,
+ .max_fps = 30,
+ .exp_def = 0x0450,
+ .hts_def = 0x02a0 * 4,
+ .vts_def = 0x08b8,
+ .reg_list = ov5695_1920x1080_regs,
+ },
+ {
+ .width = 1296,
+ .height = 972,
+ .max_fps = 60,
+ .exp_def = 0x03e0,
+ .hts_def = 0x02e4 * 4,
+ .vts_def = 0x03f4,
+ .reg_list = ov5695_1296x972_regs,
+ },
+ {
+ .width = 1280,
+ .height = 720,
+ .max_fps = 30,
+ .exp_def = 0x0450,
+ .hts_def = 0x02a0 * 4,
+ .vts_def = 0x08b8,
+ .reg_list = ov5695_1280x720_regs,
+ },
+ {
+ .width = 640,
+ .height = 480,
+ .max_fps = 120,
+ .exp_def = 0x0450,
+ .hts_def = 0x02a0 * 4,
+ .vts_def = 0x022e,
+ .reg_list = ov5695_640x480_regs,
+ },
+};
+
+#define OV5695_LINK_FREQ_420MHZ 420000000
+static const s64 link_freq_menu_items[] = {
+ OV5695_LINK_FREQ_420MHZ
+};
+
+static const char * const ov5695_test_pattern_menu[] = {
+ "Disabled",
+ "Vertical Color Bar Type 1",
+ "Vertical Color Bar Type 2",
+ "Vertical Color Bar Type 3",
+ "Vertical Color Bar Type 4"
+};
+
+/* Write registers up to 4 at a time */
+static int ov5695_write_reg(struct i2c_client *client, u16 reg,
+ u32 len, u32 val)
+{
+ u32 buf_i, val_i;
+ u8 buf[6];
+ u8 *val_p;
+ __be32 val_be;
+
+ if (len > 4)
+ return -EINVAL;
+
+ buf[0] = reg >> 8;
+ buf[1] = reg & 0xff;
+
+ val_be = cpu_to_be32(val);
+ val_p = (u8 *)&val_be;
+ buf_i = 2;
+ val_i = 4 - len;
+
+ while (val_i < 4)
+ buf[buf_i++] = val_p[val_i++];
+
+ if (i2c_master_send(client, buf, len + 2) != len + 2)
+ return -EIO;
+
+ return 0;
+}
+
+static int ov5695_write_array(struct i2c_client *client,
+ const struct regval *regs)
+{
+ u32 i;
+ int ret = 0;
+
+ for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
+ ret = ov5695_write_reg(client, regs[i].addr,
+ OV5695_REG_VALUE_08BIT, regs[i].val);
+
+ return ret;
+}
+
+/* Read registers up to 4 at a time */
+static int ov5695_read_reg(struct i2c_client *client, u16 reg, unsigned int len,
+ u32 *val)
+{
+ struct i2c_msg msgs[2];
+ u8 *data_be_p;
+ __be32 data_be = 0;
+ __be16 reg_addr_be = cpu_to_be16(reg);
+ int ret;
+
+ if (len > 4)
+ return -EINVAL;
+
+ data_be_p = (u8 *)&data_be;
+ /* Write register address */
+ msgs[0].addr = client->addr;
+ msgs[0].flags = 0;
+ msgs[0].len = 2;
+ msgs[0].buf = (u8 *)&reg_addr_be;
+
+ /* Read data from register */
+ msgs[1].addr = client->addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = len;
+ msgs[1].buf = &data_be_p[4 - len];
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs))
+ return -EIO;
+
+ *val = be32_to_cpu(data_be);
+
+ return 0;
+}
+
+static int ov5695_get_reso_dist(const struct ov5695_mode *mode,
+ struct v4l2_mbus_framefmt *framefmt)
+{
+ return abs(mode->width - framefmt->width) +
+ abs(mode->height - framefmt->height);
+}
+
+static const struct ov5695_mode *
+ov5695_find_best_fit(struct v4l2_subdev_format *fmt)
+{
+ struct v4l2_mbus_framefmt *framefmt = &fmt->format;
+ int dist;
+ int cur_best_fit = 0;
+ int cur_best_fit_dist = -1;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(supported_modes); i++) {
+ dist = ov5695_get_reso_dist(&supported_modes[i], framefmt);
+ if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) {
+ cur_best_fit_dist = dist;
+ cur_best_fit = i;
+ }
+ }
+
+ return &supported_modes[cur_best_fit];
+}
+
+static int ov5695_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct ov5695 *ov5695 = to_ov5695(sd);
+ const struct ov5695_mode *mode;
+ s64 h_blank, vblank_def;
+
+ mutex_lock(&ov5695->mutex);
+
+ mode = ov5695_find_best_fit(fmt);
+ fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ fmt->format.width = mode->width;
+ fmt->format.height = mode->height;
+ fmt->format.field = V4L2_FIELD_NONE;
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+ *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format;
+#else
+ mutex_unlock(&ov5695->mutex);
+ return -ENOTTY;
+#endif
+ } else {
+ ov5695->cur_mode = mode;
+ h_blank = mode->hts_def - mode->width;
+ __v4l2_ctrl_modify_range(ov5695->hblank, h_blank,
+ h_blank, 1, h_blank);
+ vblank_def = mode->vts_def - mode->height;
+ __v4l2_ctrl_modify_range(ov5695->vblank, vblank_def,
+ OV5695_VTS_MAX - mode->height,
+ 1, vblank_def);
+ }
+
+ mutex_unlock(&ov5695->mutex);
+
+ return 0;
+}
+
+static int ov5695_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct ov5695 *ov5695 = to_ov5695(sd);
+ const struct ov5695_mode *mode = ov5695->cur_mode;
+
+ mutex_lock(&ov5695->mutex);
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+ fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
+#else
+ mutex_unlock(&ov5695->mutex);
+ return -ENOTTY;
+#endif
+ } else {
+ fmt->format.width = mode->width;
+ fmt->format.height = mode->height;
+ fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ fmt->format.field = V4L2_FIELD_NONE;
+ }
+ mutex_unlock(&ov5695->mutex);
+
+ return 0;
+}
+
+static int ov5695_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index != 0)
+ return -EINVAL;
+ code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+
+ return 0;
+}
+
+static int ov5695_enum_frame_sizes(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ if (fse->index >= ARRAY_SIZE(supported_modes))
+ return -EINVAL;
+
+ if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10)
+ return -EINVAL;
+
+ fse->min_width = supported_modes[fse->index].width;
+ fse->max_width = supported_modes[fse->index].width;
+ fse->max_height = supported_modes[fse->index].height;
+ fse->min_height = supported_modes[fse->index].height;
+
+ return 0;
+}
+
+static int ov5695_enable_test_pattern(struct ov5695 *ov5695, u32 pattern)
+{
+ u32 val;
+
+ if (pattern)
+ val = (pattern - 1) | OV5695_TEST_PATTERN_ENABLE;
+ else
+ val = OV5695_TEST_PATTERN_DISABLE;
+
+ return ov5695_write_reg(ov5695->client, OV5695_REG_TEST_PATTERN,
+ OV5695_REG_VALUE_08BIT, val);
+}
+
+static int __ov5695_start_stream(struct ov5695 *ov5695)
+{
+ int ret;
+
+ ret = ov5695_write_array(ov5695->client, ov5695_global_regs);
+ if (ret)
+ return ret;
+ ret = ov5695_write_array(ov5695->client, ov5695->cur_mode->reg_list);
+ if (ret)
+ return ret;
+
+ /* In case these controls are set before streaming */
+ ret = __v4l2_ctrl_handler_setup(&ov5695->ctrl_handler);
+ if (ret)
+ return ret;
+
+ return ov5695_write_reg(ov5695->client, OV5695_REG_CTRL_MODE,
+ OV5695_REG_VALUE_08BIT, OV5695_MODE_STREAMING);
+}
+
+static int __ov5695_stop_stream(struct ov5695 *ov5695)
+{
+ return ov5695_write_reg(ov5695->client, OV5695_REG_CTRL_MODE,
+ OV5695_REG_VALUE_08BIT, OV5695_MODE_SW_STANDBY);
+}
+
+static int ov5695_s_stream(struct v4l2_subdev *sd, int on)
+{
+ struct ov5695 *ov5695 = to_ov5695(sd);
+ struct i2c_client *client = ov5695->client;
+ int ret = 0;
+
+ mutex_lock(&ov5695->mutex);
+ on = !!on;
+ if (on == ov5695->streaming)
+ goto unlock_and_return;
+
+ if (on) {
+ ret = pm_runtime_get_sync(&client->dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(&client->dev);
+ goto unlock_and_return;
+ }
+
+ ret = __ov5695_start_stream(ov5695);
+ if (ret) {
+ v4l2_err(sd, "start stream failed while write regs\n");
+ pm_runtime_put(&client->dev);
+ goto unlock_and_return;
+ }
+ } else {
+ __ov5695_stop_stream(ov5695);
+ pm_runtime_put(&client->dev);
+ }
+
+ ov5695->streaming = on;
+
+unlock_and_return:
+ mutex_unlock(&ov5695->mutex);
+
+ return ret;
+}
+
+/* Calculate the delay in us by clock rate and clock cycles */
+static inline u32 ov5695_cal_delay(u32 cycles)
+{
+ return DIV_ROUND_UP(cycles, OV5695_XVCLK_FREQ / 1000 / 1000);
+}
+
+static int __ov5695_power_on(struct ov5695 *ov5695)
+{
+ int ret;
+ u32 delay_us;
+ struct device *dev = &ov5695->client->dev;
+
+ ret = clk_prepare_enable(ov5695->xvclk);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable xvclk\n");
+ return ret;
+ }
+
+ gpiod_set_value_cansleep(ov5695->reset_gpio, 1);
+
+ ret = regulator_bulk_enable(OV5695_NUM_SUPPLIES, ov5695->supplies);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable regulators\n");
+ goto disable_clk;
+ }
+
+ gpiod_set_value_cansleep(ov5695->reset_gpio, 0);
+
+ /* 8192 cycles prior to first SCCB transaction */
+ delay_us = ov5695_cal_delay(8192);
+ usleep_range(delay_us, delay_us * 2);
+
+ return 0;
+
+disable_clk:
+ clk_disable_unprepare(ov5695->xvclk);
+
+ return ret;
+}
+
+static void __ov5695_power_off(struct ov5695 *ov5695)
+{
+ clk_disable_unprepare(ov5695->xvclk);
+ gpiod_set_value_cansleep(ov5695->reset_gpio, 1);
+ regulator_bulk_disable(OV5695_NUM_SUPPLIES, ov5695->supplies);
+}
+
+static int __maybe_unused ov5695_runtime_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov5695 *ov5695 = to_ov5695(sd);
+
+ return __ov5695_power_on(ov5695);
+}
+
+static int __maybe_unused ov5695_runtime_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov5695 *ov5695 = to_ov5695(sd);
+
+ __ov5695_power_off(ov5695);
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static int ov5695_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct ov5695 *ov5695 = to_ov5695(sd);
+ struct v4l2_mbus_framefmt *try_fmt =
+ v4l2_subdev_get_try_format(sd, fh->pad, 0);
+ const struct ov5695_mode *def_mode = &supported_modes[0];
+
+ mutex_lock(&ov5695->mutex);
+ /* Initialize try_fmt */
+ try_fmt->width = def_mode->width;
+ try_fmt->height = def_mode->height;
+ try_fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ try_fmt->field = V4L2_FIELD_NONE;
+
+ mutex_unlock(&ov5695->mutex);
+ /* No crop or compose */
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops ov5695_pm_ops = {
+ SET_RUNTIME_PM_OPS(ov5695_runtime_suspend,
+ ov5695_runtime_resume, NULL)
+};
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static const struct v4l2_subdev_internal_ops ov5695_internal_ops = {
+ .open = ov5695_open,
+};
+#endif
+
+static const struct v4l2_subdev_video_ops ov5695_video_ops = {
+ .s_stream = ov5695_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops ov5695_pad_ops = {
+ .enum_mbus_code = ov5695_enum_mbus_code,
+ .enum_frame_size = ov5695_enum_frame_sizes,
+ .get_fmt = ov5695_get_fmt,
+ .set_fmt = ov5695_set_fmt,
+};
+
+static const struct v4l2_subdev_ops ov5695_subdev_ops = {
+ .video = &ov5695_video_ops,
+ .pad = &ov5695_pad_ops,
+};
+
+static int ov5695_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov5695 *ov5695 = container_of(ctrl->handler,
+ struct ov5695, ctrl_handler);
+ struct i2c_client *client = ov5695->client;
+ s64 max;
+ int ret = 0;
+
+ /* Propagate change of current control to all related controls */
+ switch (ctrl->id) {
+ case V4L2_CID_VBLANK:
+ /* Update max exposure while meeting expected vblanking */
+ max = ov5695->cur_mode->height + ctrl->val - 4;
+ __v4l2_ctrl_modify_range(ov5695->exposure,
+ ov5695->exposure->minimum, max,
+ ov5695->exposure->step,
+ ov5695->exposure->default_value);
+ break;
+ }
+
+ if (pm_runtime_get_if_in_use(&client->dev) <= 0)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ /* 4 least significant bits of expsoure are fractional part */
+ ret = ov5695_write_reg(ov5695->client, OV5695_REG_EXPOSURE,
+ OV5695_REG_VALUE_24BIT, ctrl->val << 4);
+ break;
+ case V4L2_CID_ANALOGUE_GAIN:
+ ret = ov5695_write_reg(ov5695->client, OV5695_REG_ANALOG_GAIN,
+ OV5695_REG_VALUE_08BIT, ctrl->val);
+ break;
+ case V4L2_CID_DIGITAL_GAIN:
+ ret = ov5695_write_reg(ov5695->client, OV5695_REG_DIGI_GAIN_L,
+ OV5695_REG_VALUE_08BIT,
+ ctrl->val & OV5695_DIGI_GAIN_L_MASK);
+ ret = ov5695_write_reg(ov5695->client, OV5695_REG_DIGI_GAIN_H,
+ OV5695_REG_VALUE_08BIT,
+ ctrl->val >> OV5695_DIGI_GAIN_H_SHIFT);
+ break;
+ case V4L2_CID_VBLANK:
+ ret = ov5695_write_reg(ov5695->client, OV5695_REG_VTS,
+ OV5695_REG_VALUE_16BIT,
+ ctrl->val + ov5695->cur_mode->height);
+ break;
+ case V4L2_CID_TEST_PATTERN:
+ ret = ov5695_enable_test_pattern(ov5695, ctrl->val);
+ break;
+ default:
+ dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
+ __func__, ctrl->id, ctrl->val);
+ break;
+ };
+
+ pm_runtime_put(&client->dev);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops ov5695_ctrl_ops = {
+ .s_ctrl = ov5695_set_ctrl,
+};
+
+static int ov5695_initialize_controls(struct ov5695 *ov5695)
+{
+ const struct ov5695_mode *mode;
+ struct v4l2_ctrl_handler *handler;
+ struct v4l2_ctrl *ctrl;
+ s64 exposure_max, vblank_def;
+ u32 h_blank;
+ int ret;
+
+ handler = &ov5695->ctrl_handler;
+ mode = ov5695->cur_mode;
+ ret = v4l2_ctrl_handler_init(handler, 8);
+ if (ret)
+ return ret;
+ handler->lock = &ov5695->mutex;
+
+ ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ,
+ 0, 0, link_freq_menu_items);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE,
+ 0, OV5695_PIXEL_RATE, 1, OV5695_PIXEL_RATE);
+
+ h_blank = mode->hts_def - mode->width;
+ ov5695->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
+ h_blank, h_blank, 1, h_blank);
+ if (ov5695->hblank)
+ ov5695->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ vblank_def = mode->vts_def - mode->height;
+ ov5695->vblank = v4l2_ctrl_new_std(handler, &ov5695_ctrl_ops,
+ V4L2_CID_VBLANK, vblank_def,
+ OV5695_VTS_MAX - mode->height,
+ 1, vblank_def);
+
+ exposure_max = mode->vts_def - 4;
+ ov5695->exposure = v4l2_ctrl_new_std(handler, &ov5695_ctrl_ops,
+ V4L2_CID_EXPOSURE, OV5695_EXPOSURE_MIN,
+ exposure_max, OV5695_EXPOSURE_STEP,
+ mode->exp_def);
+
+ ov5695->anal_gain = v4l2_ctrl_new_std(handler, &ov5695_ctrl_ops,
+ V4L2_CID_ANALOGUE_GAIN, ANALOG_GAIN_MIN,
+ ANALOG_GAIN_MAX, ANALOG_GAIN_STEP,
+ ANALOG_GAIN_DEFAULT);
+
+ /* Digital gain */
+ ov5695->digi_gain = v4l2_ctrl_new_std(handler, &ov5695_ctrl_ops,
+ V4L2_CID_DIGITAL_GAIN, OV5695_DIGI_GAIN_MIN,
+ OV5695_DIGI_GAIN_MAX, OV5695_DIGI_GAIN_STEP,
+ OV5695_DIGI_GAIN_DEFAULT);
+
+ ov5695->test_pattern = v4l2_ctrl_new_std_menu_items(handler,
+ &ov5695_ctrl_ops, V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(ov5695_test_pattern_menu) - 1,
+ 0, 0, ov5695_test_pattern_menu);
+
+ if (handler->error) {
+ ret = handler->error;
+ dev_err(&ov5695->client->dev,
+ "Failed to init controls(%d)\n", ret);
+ goto err_free_handler;
+ }
+
+ ov5695->subdev.ctrl_handler = handler;
+
+ return 0;
+
+err_free_handler:
+ v4l2_ctrl_handler_free(handler);
+
+ return ret;
+}
+
+static int ov5695_check_sensor_id(struct ov5695 *ov5695,
+ struct i2c_client *client)
+{
+ struct device *dev = &ov5695->client->dev;
+ u32 id = 0;
+ int ret;
+
+ ret = ov5695_read_reg(client, OV5695_REG_CHIP_ID,
+ OV5695_REG_VALUE_24BIT, &id);
+ if (id != CHIP_ID) {
+ dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret);
+ return ret;
+ }
+
+ dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID);
+
+ return 0;
+}
+
+static int ov5695_configure_regulators(struct ov5695 *ov5695)
+{
+ int i;
+
+ for (i = 0; i < OV5695_NUM_SUPPLIES; i++)
+ ov5695->supplies[i].supply = ov5695_supply_names[i];
+
+ return devm_regulator_bulk_get(&ov5695->client->dev,
+ OV5695_NUM_SUPPLIES,
+ ov5695->supplies);
+}
+
+static int ov5695_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct ov5695 *ov5695;
+ struct v4l2_subdev *sd;
+ int ret;
+
+ ov5695 = devm_kzalloc(dev, sizeof(*ov5695), GFP_KERNEL);
+ if (!ov5695)
+ return -ENOMEM;
+
+ ov5695->client = client;
+ ov5695->cur_mode = &supported_modes[0];
+
+ ov5695->xvclk = devm_clk_get(dev, "xvclk");
+ if (IS_ERR(ov5695->xvclk)) {
+ dev_err(dev, "Failed to get xvclk\n");
+ return -EINVAL;
+ }
+ ret = clk_set_rate(ov5695->xvclk, OV5695_XVCLK_FREQ);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set xvclk rate (24MHz)\n");
+ return ret;
+ }
+ if (clk_get_rate(ov5695->xvclk) != OV5695_XVCLK_FREQ)
+ dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
+
+ ov5695->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(ov5695->reset_gpio)) {
+ dev_err(dev, "Failed to get reset-gpios\n");
+ return -EINVAL;
+ }
+
+ ret = ov5695_configure_regulators(ov5695);
+ if (ret) {
+ dev_err(dev, "Failed to get power regulators\n");
+ return ret;
+ }
+
+ mutex_init(&ov5695->mutex);
+
+ sd = &ov5695->subdev;
+ v4l2_i2c_subdev_init(sd, client, &ov5695_subdev_ops);
+ ret = ov5695_initialize_controls(ov5695);
+ if (ret)
+ goto err_destroy_mutex;
+
+ ret = __ov5695_power_on(ov5695);
+ if (ret)
+ goto err_free_handler;
+
+ ret = ov5695_check_sensor_id(ov5695, client);
+ if (ret)
+ goto err_power_off;
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+ sd->internal_ops = &ov5695_internal_ops;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+#endif
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ ov5695->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ ret = media_entity_pads_init(&sd->entity, 1, &ov5695->pad);
+ if (ret < 0)
+ goto err_power_off;
+#endif
+
+ ret = v4l2_async_register_subdev(sd);
+ if (ret) {
+ dev_err(dev, "v4l2 async register subdev failed\n");
+ goto err_clean_entity;
+ }
+
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_idle(dev);
+
+ return 0;
+
+err_clean_entity:
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ media_entity_cleanup(&sd->entity);
+#endif
+err_power_off:
+ __ov5695_power_off(ov5695);
+err_free_handler:
+ v4l2_ctrl_handler_free(&ov5695->ctrl_handler);
+err_destroy_mutex:
+ mutex_destroy(&ov5695->mutex);
+
+ return ret;
+}
+
+static int ov5695_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov5695 *ov5695 = to_ov5695(sd);
+
+ v4l2_async_unregister_subdev(sd);
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ media_entity_cleanup(&sd->entity);
+#endif
+ v4l2_ctrl_handler_free(&ov5695->ctrl_handler);
+ mutex_destroy(&ov5695->mutex);
+
+ pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev))
+ __ov5695_power_off(ov5695);
+ pm_runtime_set_suspended(&client->dev);
+
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id ov5695_of_match[] = {
+ { .compatible = "ovti,ov5695" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ov5695_of_match);
+#endif
+
+static struct i2c_driver ov5695_i2c_driver = {
+ .driver = {
+ .name = "ov5695",
+ .owner = THIS_MODULE,
+ .pm = &ov5695_pm_ops,
+ .of_match_table = of_match_ptr(ov5695_of_match),
+ },
+ .probe = &ov5695_probe,
+ .remove = &ov5695_remove,
+};
+
+module_i2c_driver(ov5695_i2c_driver);
+
+MODULE_DESCRIPTION("OmniVision ov5695 sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c
index 8975d16b2b24..17a34b4a819d 100644
--- a/drivers/media/i2c/ov6650.c
+++ b/drivers/media/i2c/ov6650.c
@@ -201,7 +201,7 @@ struct ov6650 {
struct v4l2_rect rect; /* sensor cropping window */
unsigned long pclk_limit; /* from host */
unsigned long pclk_max; /* from resolution and format */
- struct v4l2_fract tpf; /* as requested with s_parm */
+ struct v4l2_fract tpf; /* as requested with s_frame_interval */
u32 code;
enum v4l2_colorspace colorspace;
};
@@ -723,42 +723,31 @@ static int ov6650_enum_mbus_code(struct v4l2_subdev *sd,
return 0;
}
-static int ov6650_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+static int ov6650_g_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *ival)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov6650 *priv = to_ov6650(client);
- struct v4l2_captureparm *cp = &parms->parm.capture;
- if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- memset(cp, 0, sizeof(*cp));
- cp->capability = V4L2_CAP_TIMEPERFRAME;
- cp->timeperframe.numerator = GET_CLKRC_DIV(to_clkrc(&priv->tpf,
+ ival->interval.numerator = GET_CLKRC_DIV(to_clkrc(&priv->tpf,
priv->pclk_limit, priv->pclk_max));
- cp->timeperframe.denominator = FRAME_RATE_MAX;
+ ival->interval.denominator = FRAME_RATE_MAX;
dev_dbg(&client->dev, "Frame interval: %u/%u s\n",
- cp->timeperframe.numerator, cp->timeperframe.denominator);
+ ival->interval.numerator, ival->interval.denominator);
return 0;
}
-static int ov6650_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+static int ov6650_s_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *ival)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov6650 *priv = to_ov6650(client);
- struct v4l2_captureparm *cp = &parms->parm.capture;
- struct v4l2_fract *tpf = &cp->timeperframe;
+ struct v4l2_fract *tpf = &ival->interval;
int div, ret;
u8 clkrc;
- if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- if (cp->extendedmode != 0)
- return -EINVAL;
-
if (tpf->numerator == 0 || tpf->denominator == 0)
div = 1; /* Reset to full rate */
else
@@ -921,8 +910,8 @@ static int ov6650_s_mbus_config(struct v4l2_subdev *sd,
static const struct v4l2_subdev_video_ops ov6650_video_ops = {
.s_stream = ov6650_s_stream,
- .g_parm = ov6650_g_parm,
- .s_parm = ov6650_s_parm,
+ .g_frame_interval = ov6650_g_frame_interval,
+ .s_frame_interval = ov6650_s_frame_interval,
.g_mbus_config = ov6650_g_mbus_config,
.s_mbus_config = ov6650_s_mbus_config,
};
diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c
index 28571de1c2f6..3474ef832c1e 100644
--- a/drivers/media/i2c/ov7670.c
+++ b/drivers/media/i2c/ov7670.c
@@ -21,6 +21,7 @@
#include <linux/gpio/consumer.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
#include <media/v4l2-mediabus.h>
#include <media/v4l2-image-sizes.h>
#include <media/i2c/ov7670.h>
@@ -242,6 +243,7 @@ struct ov7670_info {
struct clk *clk;
struct gpio_desc *resetb_gpio;
struct gpio_desc *pwdn_gpio;
+ unsigned int mbus_config; /* Media bus configuration flags */
int min_width; /* Filter out smaller sizes */
int min_height; /* Filter out smaller sizes */
int clock_speed; /* External clock speed (MHz) */
@@ -1014,7 +1016,7 @@ static int ov7670_set_fmt(struct v4l2_subdev *sd,
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
struct v4l2_mbus_framefmt *mbus_fmt;
#endif
- unsigned char com7;
+ unsigned char com7, com10 = 0;
int ret;
if (format->pad)
@@ -1034,7 +1036,6 @@ static int ov7670_set_fmt(struct v4l2_subdev *sd,
}
ret = ov7670_try_fmt_internal(sd, &format->format, &ovfmt, &wsize);
-
if (ret)
return ret;
/*
@@ -1045,16 +1046,41 @@ static int ov7670_set_fmt(struct v4l2_subdev *sd,
*/
com7 = ovfmt->regs[0].value;
com7 |= wsize->com7_bit;
- ov7670_write(sd, REG_COM7, com7);
+ ret = ov7670_write(sd, REG_COM7, com7);
+ if (ret)
+ return ret;
+
+ /*
+ * Configure the media bus through COM10 register
+ */
+ if (info->mbus_config & V4L2_MBUS_VSYNC_ACTIVE_LOW)
+ com10 |= COM10_VS_NEG;
+ if (info->mbus_config & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ com10 |= COM10_HREF_REV;
+ if (info->pclk_hb_disable)
+ com10 |= COM10_PCLK_HB;
+ ret = ov7670_write(sd, REG_COM10, com10);
+ if (ret)
+ return ret;
+
/*
* Now write the rest of the array. Also store start/stops
*/
- ov7670_write_array(sd, ovfmt->regs + 1);
- ov7670_set_hw(sd, wsize->hstart, wsize->hstop, wsize->vstart,
- wsize->vstop);
- ret = 0;
- if (wsize->regs)
+ ret = ov7670_write_array(sd, ovfmt->regs + 1);
+ if (ret)
+ return ret;
+
+ ret = ov7670_set_hw(sd, wsize->hstart, wsize->hstop, wsize->vstart,
+ wsize->vstop);
+ if (ret)
+ return ret;
+
+ if (wsize->regs) {
ret = ov7670_write_array(sd, wsize->regs);
+ if (ret)
+ return ret;
+ }
+
info->fmt = ovfmt;
/*
@@ -1067,8 +1093,10 @@ static int ov7670_set_fmt(struct v4l2_subdev *sd,
* to write it unconditionally, and that will make the frame
* rate persistent too.
*/
- if (ret == 0)
- ret = ov7670_write(sd, REG_CLKRC, info->clkrc);
+ ret = ov7670_write(sd, REG_CLKRC, info->clkrc);
+ if (ret)
+ return ret;
+
return 0;
}
@@ -1100,30 +1128,24 @@ static int ov7670_get_fmt(struct v4l2_subdev *sd,
* Implement G/S_PARM. There is a "high quality" mode we could try
* to do someday; for now, we just do the frame rate tweak.
*/
-static int ov7670_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+static int ov7670_g_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *ival)
{
- struct v4l2_captureparm *cp = &parms->parm.capture;
struct ov7670_info *info = to_state(sd);
- if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- cp->capability = V4L2_CAP_TIMEPERFRAME;
- info->devtype->get_framerate(sd, &cp->timeperframe);
+ info->devtype->get_framerate(sd, &ival->interval);
return 0;
}
-static int ov7670_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+static int ov7670_s_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *ival)
{
- struct v4l2_captureparm *cp = &parms->parm.capture;
- struct v4l2_fract *tpf = &cp->timeperframe;
+ struct v4l2_fract *tpf = &ival->interval;
struct ov7670_info *info = to_state(sd);
- if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- cp->capability = V4L2_CAP_TIMEPERFRAME;
return info->devtype->set_framerate(sd, tpf);
}
@@ -1636,8 +1658,8 @@ static const struct v4l2_subdev_core_ops ov7670_core_ops = {
};
static const struct v4l2_subdev_video_ops ov7670_video_ops = {
- .s_parm = ov7670_s_parm,
- .g_parm = ov7670_g_parm,
+ .s_frame_interval = ov7670_s_frame_interval,
+ .g_frame_interval = ov7670_g_frame_interval,
};
static const struct v4l2_subdev_pad_ops ov7670_pad_ops = {
@@ -1698,6 +1720,45 @@ static int ov7670_init_gpio(struct i2c_client *client, struct ov7670_info *info)
return 0;
}
+/*
+ * ov7670_parse_dt() - Parse device tree to collect mbus configuration
+ * properties
+ */
+static int ov7670_parse_dt(struct device *dev,
+ struct ov7670_info *info)
+{
+ struct fwnode_handle *fwnode = dev_fwnode(dev);
+ struct v4l2_fwnode_endpoint bus_cfg;
+ struct fwnode_handle *ep;
+ int ret;
+
+ if (!fwnode)
+ return -EINVAL;
+
+ info->pclk_hb_disable = false;
+ if (fwnode_property_present(fwnode, "ov7670,pclk-hb-disable"))
+ info->pclk_hb_disable = true;
+
+ ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
+ if (!ep)
+ return -EINVAL;
+
+ ret = v4l2_fwnode_endpoint_parse(ep, &bus_cfg);
+ if (ret) {
+ fwnode_handle_put(ep);
+ return ret;
+ }
+
+ if (bus_cfg.bus_type != V4L2_MBUS_PARALLEL) {
+ dev_err(dev, "Unsupported media bus type\n");
+ fwnode_handle_put(ep);
+ return ret;
+ }
+ info->mbus_config = bus_cfg.bus.parallel.flags;
+
+ return 0;
+}
+
static int ov7670_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -1718,7 +1779,13 @@ static int ov7670_probe(struct i2c_client *client,
#endif
info->clock_speed = 30; /* default: a guess */
- if (client->dev.platform_data) {
+
+ if (dev_fwnode(&client->dev)) {
+ ret = ov7670_parse_dt(&client->dev, info);
+ if (ret)
+ return ret;
+
+ } else if (client->dev.platform_data) {
struct ov7670_config *config = client->dev.platform_data;
/*
@@ -1785,9 +1852,6 @@ static int ov7670_probe(struct i2c_client *client,
tpf.denominator = 30;
info->devtype->set_framerate(sd, &tpf);
- if (info->pclk_hb_disable)
- ov7670_write(sd, REG_COM10, COM10_PCLK_HB);
-
v4l2_ctrl_handler_init(&info->hdl, 10);
v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops,
V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
new file mode 100644
index 000000000000..b62860c89439
--- /dev/null
+++ b/drivers/media/i2c/ov772x.c
@@ -0,0 +1,1356 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ov772x Camera Driver
+ *
+ * Copyright (C) 2017 Jacopo Mondi <jacopo+renesas@jmondi.org>
+ *
+ * Copyright (C) 2008 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on ov7670 and soc_camera_platform driver,
+ *
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
+ * Copyright (C) 2008 Magnus Damm
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/videodev2.h>
+
+#include <media/i2c/ov772x.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-image-sizes.h>
+#include <media/v4l2-subdev.h>
+
+/*
+ * register offset
+ */
+#define GAIN 0x00 /* AGC - Gain control gain setting */
+#define BLUE 0x01 /* AWB - Blue channel gain setting */
+#define RED 0x02 /* AWB - Red channel gain setting */
+#define GREEN 0x03 /* AWB - Green channel gain setting */
+#define COM1 0x04 /* Common control 1 */
+#define BAVG 0x05 /* U/B Average Level */
+#define GAVG 0x06 /* Y/Gb Average Level */
+#define RAVG 0x07 /* V/R Average Level */
+#define AECH 0x08 /* Exposure Value - AEC MSBs */
+#define COM2 0x09 /* Common control 2 */
+#define PID 0x0A /* Product ID Number MSB */
+#define VER 0x0B /* Product ID Number LSB */
+#define COM3 0x0C /* Common control 3 */
+#define COM4 0x0D /* Common control 4 */
+#define COM5 0x0E /* Common control 5 */
+#define COM6 0x0F /* Common control 6 */
+#define AEC 0x10 /* Exposure Value */
+#define CLKRC 0x11 /* Internal clock */
+#define COM7 0x12 /* Common control 7 */
+#define COM8 0x13 /* Common control 8 */
+#define COM9 0x14 /* Common control 9 */
+#define COM10 0x15 /* Common control 10 */
+#define REG16 0x16 /* Register 16 */
+#define HSTART 0x17 /* Horizontal sensor size */
+#define HSIZE 0x18 /* Horizontal frame (HREF column) end high 8-bit */
+#define VSTART 0x19 /* Vertical frame (row) start high 8-bit */
+#define VSIZE 0x1A /* Vertical sensor size */
+#define PSHFT 0x1B /* Data format - pixel delay select */
+#define MIDH 0x1C /* Manufacturer ID byte - high */
+#define MIDL 0x1D /* Manufacturer ID byte - low */
+#define LAEC 0x1F /* Fine AEC value */
+#define COM11 0x20 /* Common control 11 */
+#define BDBASE 0x22 /* Banding filter Minimum AEC value */
+#define DBSTEP 0x23 /* Banding filter Maximum Setp */
+#define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */
+#define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */
+#define VPT 0x26 /* AGC/AEC Fast mode operating region */
+#define REG28 0x28 /* Register 28 */
+#define HOUTSIZE 0x29 /* Horizontal data output size MSBs */
+#define EXHCH 0x2A /* Dummy pixel insert MSB */
+#define EXHCL 0x2B /* Dummy pixel insert LSB */
+#define VOUTSIZE 0x2C /* Vertical data output size MSBs */
+#define ADVFL 0x2D /* LSB of insert dummy lines in Vertical direction */
+#define ADVFH 0x2E /* MSG of insert dummy lines in Vertical direction */
+#define YAVE 0x2F /* Y/G Channel Average value */
+#define LUMHTH 0x30 /* Histogram AEC/AGC Luminance high level threshold */
+#define LUMLTH 0x31 /* Histogram AEC/AGC Luminance low level threshold */
+#define HREF 0x32 /* Image start and size control */
+#define DM_LNL 0x33 /* Dummy line low 8 bits */
+#define DM_LNH 0x34 /* Dummy line high 8 bits */
+#define ADOFF_B 0x35 /* AD offset compensation value for B channel */
+#define ADOFF_R 0x36 /* AD offset compensation value for R channel */
+#define ADOFF_GB 0x37 /* AD offset compensation value for Gb channel */
+#define ADOFF_GR 0x38 /* AD offset compensation value for Gr channel */
+#define OFF_B 0x39 /* Analog process B channel offset value */
+#define OFF_R 0x3A /* Analog process R channel offset value */
+#define OFF_GB 0x3B /* Analog process Gb channel offset value */
+#define OFF_GR 0x3C /* Analog process Gr channel offset value */
+#define COM12 0x3D /* Common control 12 */
+#define COM13 0x3E /* Common control 13 */
+#define COM14 0x3F /* Common control 14 */
+#define COM15 0x40 /* Common control 15*/
+#define COM16 0x41 /* Common control 16 */
+#define TGT_B 0x42 /* BLC blue channel target value */
+#define TGT_R 0x43 /* BLC red channel target value */
+#define TGT_GB 0x44 /* BLC Gb channel target value */
+#define TGT_GR 0x45 /* BLC Gr channel target value */
+/* for ov7720 */
+#define LCC0 0x46 /* Lens correction control 0 */
+#define LCC1 0x47 /* Lens correction option 1 - X coordinate */
+#define LCC2 0x48 /* Lens correction option 2 - Y coordinate */
+#define LCC3 0x49 /* Lens correction option 3 */
+#define LCC4 0x4A /* Lens correction option 4 - radius of the circular */
+#define LCC5 0x4B /* Lens correction option 5 */
+#define LCC6 0x4C /* Lens correction option 6 */
+/* for ov7725 */
+#define LC_CTR 0x46 /* Lens correction control */
+#define LC_XC 0x47 /* X coordinate of lens correction center relative */
+#define LC_YC 0x48 /* Y coordinate of lens correction center relative */
+#define LC_COEF 0x49 /* Lens correction coefficient */
+#define LC_RADI 0x4A /* Lens correction radius */
+#define LC_COEFB 0x4B /* Lens B channel compensation coefficient */
+#define LC_COEFR 0x4C /* Lens R channel compensation coefficient */
+
+#define FIXGAIN 0x4D /* Analog fix gain amplifer */
+#define AREF0 0x4E /* Sensor reference control */
+#define AREF1 0x4F /* Sensor reference current control */
+#define AREF2 0x50 /* Analog reference control */
+#define AREF3 0x51 /* ADC reference control */
+#define AREF4 0x52 /* ADC reference control */
+#define AREF5 0x53 /* ADC reference control */
+#define AREF6 0x54 /* Analog reference control */
+#define AREF7 0x55 /* Analog reference control */
+#define UFIX 0x60 /* U channel fixed value output */
+#define VFIX 0x61 /* V channel fixed value output */
+#define AWBB_BLK 0x62 /* AWB option for advanced AWB */
+#define AWB_CTRL0 0x63 /* AWB control byte 0 */
+#define DSP_CTRL1 0x64 /* DSP control byte 1 */
+#define DSP_CTRL2 0x65 /* DSP control byte 2 */
+#define DSP_CTRL3 0x66 /* DSP control byte 3 */
+#define DSP_CTRL4 0x67 /* DSP control byte 4 */
+#define AWB_BIAS 0x68 /* AWB BLC level clip */
+#define AWB_CTRL1 0x69 /* AWB control 1 */
+#define AWB_CTRL2 0x6A /* AWB control 2 */
+#define AWB_CTRL3 0x6B /* AWB control 3 */
+#define AWB_CTRL4 0x6C /* AWB control 4 */
+#define AWB_CTRL5 0x6D /* AWB control 5 */
+#define AWB_CTRL6 0x6E /* AWB control 6 */
+#define AWB_CTRL7 0x6F /* AWB control 7 */
+#define AWB_CTRL8 0x70 /* AWB control 8 */
+#define AWB_CTRL9 0x71 /* AWB control 9 */
+#define AWB_CTRL10 0x72 /* AWB control 10 */
+#define AWB_CTRL11 0x73 /* AWB control 11 */
+#define AWB_CTRL12 0x74 /* AWB control 12 */
+#define AWB_CTRL13 0x75 /* AWB control 13 */
+#define AWB_CTRL14 0x76 /* AWB control 14 */
+#define AWB_CTRL15 0x77 /* AWB control 15 */
+#define AWB_CTRL16 0x78 /* AWB control 16 */
+#define AWB_CTRL17 0x79 /* AWB control 17 */
+#define AWB_CTRL18 0x7A /* AWB control 18 */
+#define AWB_CTRL19 0x7B /* AWB control 19 */
+#define AWB_CTRL20 0x7C /* AWB control 20 */
+#define AWB_CTRL21 0x7D /* AWB control 21 */
+#define GAM1 0x7E /* Gamma Curve 1st segment input end point */
+#define GAM2 0x7F /* Gamma Curve 2nd segment input end point */
+#define GAM3 0x80 /* Gamma Curve 3rd segment input end point */
+#define GAM4 0x81 /* Gamma Curve 4th segment input end point */
+#define GAM5 0x82 /* Gamma Curve 5th segment input end point */
+#define GAM6 0x83 /* Gamma Curve 6th segment input end point */
+#define GAM7 0x84 /* Gamma Curve 7th segment input end point */
+#define GAM8 0x85 /* Gamma Curve 8th segment input end point */
+#define GAM9 0x86 /* Gamma Curve 9th segment input end point */
+#define GAM10 0x87 /* Gamma Curve 10th segment input end point */
+#define GAM11 0x88 /* Gamma Curve 11th segment input end point */
+#define GAM12 0x89 /* Gamma Curve 12th segment input end point */
+#define GAM13 0x8A /* Gamma Curve 13th segment input end point */
+#define GAM14 0x8B /* Gamma Curve 14th segment input end point */
+#define GAM15 0x8C /* Gamma Curve 15th segment input end point */
+#define SLOP 0x8D /* Gamma curve highest segment slope */
+#define DNSTH 0x8E /* De-noise threshold */
+#define EDGE_STRNGT 0x8F /* Edge strength control when manual mode */
+#define EDGE_TRSHLD 0x90 /* Edge threshold control when manual mode */
+#define DNSOFF 0x91 /* Auto De-noise threshold control */
+#define EDGE_UPPER 0x92 /* Edge strength upper limit when Auto mode */
+#define EDGE_LOWER 0x93 /* Edge strength lower limit when Auto mode */
+#define MTX1 0x94 /* Matrix coefficient 1 */
+#define MTX2 0x95 /* Matrix coefficient 2 */
+#define MTX3 0x96 /* Matrix coefficient 3 */
+#define MTX4 0x97 /* Matrix coefficient 4 */
+#define MTX5 0x98 /* Matrix coefficient 5 */
+#define MTX6 0x99 /* Matrix coefficient 6 */
+#define MTX_CTRL 0x9A /* Matrix control */
+#define BRIGHT 0x9B /* Brightness control */
+#define CNTRST 0x9C /* Contrast contrast */
+#define CNTRST_CTRL 0x9D /* Contrast contrast center */
+#define UVAD_J0 0x9E /* Auto UV adjust contrast 0 */
+#define UVAD_J1 0x9F /* Auto UV adjust contrast 1 */
+#define SCAL0 0xA0 /* Scaling control 0 */
+#define SCAL1 0xA1 /* Scaling control 1 */
+#define SCAL2 0xA2 /* Scaling control 2 */
+#define FIFODLYM 0xA3 /* FIFO manual mode delay control */
+#define FIFODLYA 0xA4 /* FIFO auto mode delay control */
+#define SDE 0xA6 /* Special digital effect control */
+#define USAT 0xA7 /* U component saturation control */
+#define VSAT 0xA8 /* V component saturation control */
+/* for ov7720 */
+#define HUE0 0xA9 /* Hue control 0 */
+#define HUE1 0xAA /* Hue control 1 */
+/* for ov7725 */
+#define HUECOS 0xA9 /* Cosine value */
+#define HUESIN 0xAA /* Sine value */
+
+#define SIGN 0xAB /* Sign bit for Hue and contrast */
+#define DSPAUTO 0xAC /* DSP auto function ON/OFF control */
+
+/*
+ * register detail
+ */
+
+/* COM2 */
+#define SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */
+ /* Output drive capability */
+#define OCAP_1x 0x00 /* 1x */
+#define OCAP_2x 0x01 /* 2x */
+#define OCAP_3x 0x02 /* 3x */
+#define OCAP_4x 0x03 /* 4x */
+
+/* COM3 */
+#define SWAP_MASK (SWAP_RGB | SWAP_YUV | SWAP_ML)
+#define IMG_MASK (VFLIP_IMG | HFLIP_IMG)
+
+#define VFLIP_IMG 0x80 /* Vertical flip image ON/OFF selection */
+#define HFLIP_IMG 0x40 /* Horizontal mirror image ON/OFF selection */
+#define SWAP_RGB 0x20 /* Swap B/R output sequence in RGB mode */
+#define SWAP_YUV 0x10 /* Swap Y/UV output sequence in YUV mode */
+#define SWAP_ML 0x08 /* Swap output MSB/LSB */
+ /* Tri-state option for output clock */
+#define NOTRI_CLOCK 0x04 /* 0: Tri-state at this period */
+ /* 1: No tri-state at this period */
+ /* Tri-state option for output data */
+#define NOTRI_DATA 0x02 /* 0: Tri-state at this period */
+ /* 1: No tri-state at this period */
+#define SCOLOR_TEST 0x01 /* Sensor color bar test pattern */
+
+/* COM4 */
+ /* PLL frequency control */
+#define PLL_BYPASS 0x00 /* 00: Bypass PLL */
+#define PLL_4x 0x40 /* 01: PLL 4x */
+#define PLL_6x 0x80 /* 10: PLL 6x */
+#define PLL_8x 0xc0 /* 11: PLL 8x */
+ /* AEC evaluate window */
+#define AEC_FULL 0x00 /* 00: Full window */
+#define AEC_1p2 0x10 /* 01: 1/2 window */
+#define AEC_1p4 0x20 /* 10: 1/4 window */
+#define AEC_2p3 0x30 /* 11: Low 2/3 window */
+#define COM4_RESERVED 0x01 /* Reserved bit */
+
+/* COM5 */
+#define AFR_ON_OFF 0x80 /* Auto frame rate control ON/OFF selection */
+#define AFR_SPPED 0x40 /* Auto frame rate control speed selection */
+ /* Auto frame rate max rate control */
+#define AFR_NO_RATE 0x00 /* No reduction of frame rate */
+#define AFR_1p2 0x10 /* Max reduction to 1/2 frame rate */
+#define AFR_1p4 0x20 /* Max reduction to 1/4 frame rate */
+#define AFR_1p8 0x30 /* Max reduction to 1/8 frame rate */
+ /* Auto frame rate active point control */
+#define AF_2x 0x00 /* Add frame when AGC reaches 2x gain */
+#define AF_4x 0x04 /* Add frame when AGC reaches 4x gain */
+#define AF_8x 0x08 /* Add frame when AGC reaches 8x gain */
+#define AF_16x 0x0c /* Add frame when AGC reaches 16x gain */
+ /* AEC max step control */
+#define AEC_NO_LIMIT 0x01 /* 0 : AEC incease step has limit */
+ /* 1 : No limit to AEC increase step */
+/* CLKRC */
+ /* Input clock divider register */
+#define CLKRC_RESERVED 0x80 /* Reserved bit */
+#define CLKRC_DIV(n) ((n) - 1)
+
+/* COM7 */
+ /* SCCB Register Reset */
+#define SCCB_RESET 0x80 /* 0 : No change */
+ /* 1 : Resets all registers to default */
+ /* Resolution selection */
+#define SLCT_MASK 0x40 /* Mask of VGA or QVGA */
+#define SLCT_VGA 0x00 /* 0 : VGA */
+#define SLCT_QVGA 0x40 /* 1 : QVGA */
+#define ITU656_ON_OFF 0x20 /* ITU656 protocol ON/OFF selection */
+#define SENSOR_RAW 0x10 /* Sensor RAW */
+ /* RGB output format control */
+#define FMT_MASK 0x0c /* Mask of color format */
+#define FMT_GBR422 0x00 /* 00 : GBR 4:2:2 */
+#define FMT_RGB565 0x04 /* 01 : RGB 565 */
+#define FMT_RGB555 0x08 /* 10 : RGB 555 */
+#define FMT_RGB444 0x0c /* 11 : RGB 444 */
+ /* Output format control */
+#define OFMT_MASK 0x03 /* Mask of output format */
+#define OFMT_YUV 0x00 /* 00 : YUV */
+#define OFMT_P_BRAW 0x01 /* 01 : Processed Bayer RAW */
+#define OFMT_RGB 0x02 /* 10 : RGB */
+#define OFMT_BRAW 0x03 /* 11 : Bayer RAW */
+
+/* COM8 */
+#define FAST_ALGO 0x80 /* Enable fast AGC/AEC algorithm */
+ /* AEC Setp size limit */
+#define UNLMT_STEP 0x40 /* 0 : Step size is limited */
+ /* 1 : Unlimited step size */
+#define BNDF_ON_OFF 0x20 /* Banding filter ON/OFF */
+#define AEC_BND 0x10 /* Enable AEC below banding value */
+#define AEC_ON_OFF 0x08 /* Fine AEC ON/OFF control */
+#define AGC_ON 0x04 /* AGC Enable */
+#define AWB_ON 0x02 /* AWB Enable */
+#define AEC_ON 0x01 /* AEC Enable */
+
+/* COM9 */
+#define BASE_AECAGC 0x80 /* Histogram or average based AEC/AGC */
+ /* Automatic gain ceiling - maximum AGC value */
+#define GAIN_2x 0x00 /* 000 : 2x */
+#define GAIN_4x 0x10 /* 001 : 4x */
+#define GAIN_8x 0x20 /* 010 : 8x */
+#define GAIN_16x 0x30 /* 011 : 16x */
+#define GAIN_32x 0x40 /* 100 : 32x */
+#define GAIN_64x 0x50 /* 101 : 64x */
+#define GAIN_128x 0x60 /* 110 : 128x */
+#define DROP_VSYNC 0x04 /* Drop VSYNC output of corrupt frame */
+#define DROP_HREF 0x02 /* Drop HREF output of corrupt frame */
+
+/* COM11 */
+#define SGLF_ON_OFF 0x02 /* Single frame ON/OFF selection */
+#define SGLF_TRIG 0x01 /* Single frame transfer trigger */
+
+/* HREF */
+#define HREF_VSTART_SHIFT 6 /* VSTART LSB */
+#define HREF_HSTART_SHIFT 4 /* HSTART 2 LSBs */
+#define HREF_VSIZE_SHIFT 2 /* VSIZE LSB */
+#define HREF_HSIZE_SHIFT 0 /* HSIZE 2 LSBs */
+
+/* EXHCH */
+#define EXHCH_VSIZE_SHIFT 2 /* VOUTSIZE LSB */
+#define EXHCH_HSIZE_SHIFT 0 /* HOUTSIZE 2 LSBs */
+
+/* DSP_CTRL1 */
+#define FIFO_ON 0x80 /* FIFO enable/disable selection */
+#define UV_ON_OFF 0x40 /* UV adjust function ON/OFF selection */
+#define YUV444_2_422 0x20 /* YUV444 to 422 UV channel option selection */
+#define CLR_MTRX_ON_OFF 0x10 /* Color matrix ON/OFF selection */
+#define INTPLT_ON_OFF 0x08 /* Interpolation ON/OFF selection */
+#define GMM_ON_OFF 0x04 /* Gamma function ON/OFF selection */
+#define AUTO_BLK_ON_OFF 0x02 /* Black defect auto correction ON/OFF */
+#define AUTO_WHT_ON_OFF 0x01 /* White define auto correction ON/OFF */
+
+/* DSP_CTRL3 */
+#define UV_MASK 0x80 /* UV output sequence option */
+#define UV_ON 0x80 /* ON */
+#define UV_OFF 0x00 /* OFF */
+#define CBAR_MASK 0x20 /* DSP Color bar mask */
+#define CBAR_ON 0x20 /* ON */
+#define CBAR_OFF 0x00 /* OFF */
+
+/* DSP_CTRL4 */
+#define DSP_OFMT_YUV 0x00
+#define DSP_OFMT_RGB 0x00
+#define DSP_OFMT_RAW8 0x02
+#define DSP_OFMT_RAW10 0x03
+
+/* DSPAUTO (DSP Auto Function ON/OFF Control) */
+#define AWB_ACTRL 0x80 /* AWB auto threshold control */
+#define DENOISE_ACTRL 0x40 /* De-noise auto threshold control */
+#define EDGE_ACTRL 0x20 /* Edge enhancement auto strength control */
+#define UV_ACTRL 0x10 /* UV adjust auto slope control */
+#define SCAL0_ACTRL 0x08 /* Auto scaling factor control */
+#define SCAL1_2_ACTRL 0x04 /* Auto scaling factor control */
+
+#define OV772X_MAX_WIDTH VGA_WIDTH
+#define OV772X_MAX_HEIGHT VGA_HEIGHT
+
+/*
+ * ID
+ */
+#define OV7720 0x7720
+#define OV7725 0x7721
+#define VERSION(pid, ver) ((pid << 8) | (ver & 0xFF))
+
+/*
+ * PLL multipliers
+ */
+static struct {
+ unsigned int mult;
+ u8 com4;
+} ov772x_pll[] = {
+ { 1, PLL_BYPASS, },
+ { 4, PLL_4x, },
+ { 6, PLL_6x, },
+ { 8, PLL_8x, },
+};
+
+/*
+ * struct
+ */
+
+struct ov772x_color_format {
+ u32 code;
+ enum v4l2_colorspace colorspace;
+ u8 dsp3;
+ u8 dsp4;
+ u8 com3;
+ u8 com7;
+};
+
+struct ov772x_win_size {
+ char *name;
+ unsigned char com7_bit;
+ unsigned int sizeimage;
+ struct v4l2_rect rect;
+};
+
+struct ov772x_priv {
+ struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
+ struct clk *clk;
+ struct ov772x_camera_info *info;
+ struct gpio_desc *pwdn_gpio;
+ struct gpio_desc *rstb_gpio;
+ const struct ov772x_color_format *cfmt;
+ const struct ov772x_win_size *win;
+ unsigned short flag_vflip:1;
+ unsigned short flag_hflip:1;
+ /* band_filter = COM8[5] ? 256 - BDBASE : 0 */
+ unsigned short band_filter;
+ unsigned int fps;
+};
+
+/*
+ * supported color format list
+ */
+static const struct ov772x_color_format ov772x_cfmts[] = {
+ {
+ .code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .dsp3 = 0x0,
+ .dsp4 = DSP_OFMT_YUV,
+ .com3 = SWAP_YUV,
+ .com7 = OFMT_YUV,
+ },
+ {
+ .code = MEDIA_BUS_FMT_YVYU8_2X8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .dsp3 = UV_ON,
+ .dsp4 = DSP_OFMT_YUV,
+ .com3 = SWAP_YUV,
+ .com7 = OFMT_YUV,
+ },
+ {
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .dsp3 = 0x0,
+ .dsp4 = DSP_OFMT_YUV,
+ .com3 = 0x0,
+ .com7 = OFMT_YUV,
+ },
+ {
+ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .dsp3 = 0x0,
+ .dsp4 = DSP_OFMT_YUV,
+ .com3 = SWAP_RGB,
+ .com7 = FMT_RGB555 | OFMT_RGB,
+ },
+ {
+ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .dsp3 = 0x0,
+ .dsp4 = DSP_OFMT_YUV,
+ .com3 = 0x0,
+ .com7 = FMT_RGB555 | OFMT_RGB,
+ },
+ {
+ .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .dsp3 = 0x0,
+ .dsp4 = DSP_OFMT_YUV,
+ .com3 = SWAP_RGB,
+ .com7 = FMT_RGB565 | OFMT_RGB,
+ },
+ {
+ .code = MEDIA_BUS_FMT_RGB565_2X8_BE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .dsp3 = 0x0,
+ .dsp4 = DSP_OFMT_YUV,
+ .com3 = 0x0,
+ .com7 = FMT_RGB565 | OFMT_RGB,
+ },
+ {
+ /* Setting DSP4 to DSP_OFMT_RAW8 still gives 10-bit output,
+ * regardless of the COM7 value. We can thus only support 10-bit
+ * Bayer until someone figures it out.
+ */
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .dsp3 = 0x0,
+ .dsp4 = DSP_OFMT_RAW10,
+ .com3 = 0x0,
+ .com7 = SENSOR_RAW | OFMT_BRAW,
+ },
+};
+
+/*
+ * window size list
+ */
+
+static const struct ov772x_win_size ov772x_win_sizes[] = {
+ {
+ .name = "VGA",
+ .com7_bit = SLCT_VGA,
+ .sizeimage = 510 * 748,
+ .rect = {
+ .left = 140,
+ .top = 14,
+ .width = VGA_WIDTH,
+ .height = VGA_HEIGHT,
+ },
+ }, {
+ .name = "QVGA",
+ .com7_bit = SLCT_QVGA,
+ .sizeimage = 278 * 576,
+ .rect = {
+ .left = 252,
+ .top = 6,
+ .width = QVGA_WIDTH,
+ .height = QVGA_HEIGHT,
+ },
+ },
+};
+
+/*
+ * frame rate settings lists
+ */
+static const unsigned int ov772x_frame_intervals[] = { 5, 10, 15, 20, 30, 60 };
+
+/*
+ * general function
+ */
+
+static struct ov772x_priv *to_ov772x(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct ov772x_priv, subdev);
+}
+
+static inline int ov772x_read(struct i2c_client *client, u8 addr)
+{
+ return i2c_smbus_read_byte_data(client, addr);
+}
+
+static inline int ov772x_write(struct i2c_client *client, u8 addr, u8 value)
+{
+ return i2c_smbus_write_byte_data(client, addr, value);
+}
+
+static int ov772x_mask_set(struct i2c_client *client, u8 command, u8 mask,
+ u8 set)
+{
+ s32 val = ov772x_read(client, command);
+
+ if (val < 0)
+ return val;
+
+ val &= ~mask;
+ val |= set & mask;
+
+ return ov772x_write(client, command, val);
+}
+
+static int ov772x_reset(struct i2c_client *client)
+{
+ int ret;
+
+ ret = ov772x_write(client, COM7, SCCB_RESET);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(1000, 5000);
+
+ return ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE);
+}
+
+/*
+ * subdev ops
+ */
+
+static int ov772x_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov772x_priv *priv = to_ov772x(sd);
+
+ if (!enable) {
+ ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE);
+ return 0;
+ }
+
+ ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, 0);
+
+ dev_dbg(&client->dev, "format %d, win %s\n",
+ priv->cfmt->code, priv->win->name);
+
+ return 0;
+}
+
+static int ov772x_set_frame_rate(struct ov772x_priv *priv,
+ struct v4l2_fract *tpf,
+ const struct ov772x_color_format *cfmt,
+ const struct ov772x_win_size *win)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev);
+ unsigned long fin = clk_get_rate(priv->clk);
+ unsigned int fps = tpf->numerator ?
+ tpf->denominator / tpf->numerator :
+ tpf->denominator;
+ unsigned int best_diff;
+ unsigned int fsize;
+ unsigned int pclk;
+ unsigned int diff;
+ unsigned int idx;
+ unsigned int i;
+ u8 clkrc = 0;
+ u8 com4 = 0;
+ int ret;
+
+ /* Approximate to the closest supported frame interval. */
+ best_diff = ~0L;
+ for (i = 0, idx = 0; i < ARRAY_SIZE(ov772x_frame_intervals); i++) {
+ diff = abs(fps - ov772x_frame_intervals[i]);
+ if (diff < best_diff) {
+ idx = i;
+ best_diff = diff;
+ }
+ }
+ fps = ov772x_frame_intervals[idx];
+
+ /* Use image size (with blankings) to calculate desired pixel clock. */
+ switch (cfmt->com7 & OFMT_MASK) {
+ case OFMT_BRAW:
+ fsize = win->sizeimage;
+ break;
+ case OFMT_RGB:
+ case OFMT_YUV:
+ default:
+ fsize = win->sizeimage * 2;
+ break;
+ }
+
+ pclk = fps * fsize;
+
+ /*
+ * Pixel clock generation circuit is pretty simple:
+ *
+ * Fin -> [ / CLKRC_div] -> [ * PLL_mult] -> pclk
+ *
+ * Try to approximate the desired pixel clock testing all available
+ * PLL multipliers (1x, 4x, 6x, 8x) and calculate corresponding
+ * divisor with:
+ *
+ * div = PLL_mult * Fin / pclk
+ *
+ * and re-calculate the pixel clock using it:
+ *
+ * pclk = Fin * PLL_mult / CLKRC_div
+ *
+ * Choose the PLL_mult and CLKRC_div pair that gives a pixel clock
+ * closer to the desired one.
+ *
+ * The desired pixel clock is calculated using a known frame size
+ * (blanking included) and FPS.
+ */
+ best_diff = ~0L;
+ for (i = 0; i < ARRAY_SIZE(ov772x_pll); i++) {
+ unsigned int pll_mult = ov772x_pll[i].mult;
+ unsigned int pll_out = pll_mult * fin;
+ unsigned int t_pclk;
+ unsigned int div;
+
+ if (pll_out < pclk)
+ continue;
+
+ div = DIV_ROUND_CLOSEST(pll_out, pclk);
+ t_pclk = DIV_ROUND_CLOSEST(fin * pll_mult, div);
+ diff = abs(pclk - t_pclk);
+ if (diff < best_diff) {
+ best_diff = diff;
+ clkrc = CLKRC_DIV(div);
+ com4 = ov772x_pll[i].com4;
+ }
+ }
+
+ ret = ov772x_write(client, COM4, com4 | COM4_RESERVED);
+ if (ret < 0)
+ return ret;
+
+ ret = ov772x_write(client, CLKRC, clkrc | CLKRC_RESERVED);
+ if (ret < 0)
+ return ret;
+
+ tpf->numerator = 1;
+ tpf->denominator = fps;
+ priv->fps = tpf->denominator;
+
+ return 0;
+}
+
+static int ov772x_g_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *ival)
+{
+ struct ov772x_priv *priv = to_ov772x(sd);
+ struct v4l2_fract *tpf = &ival->interval;
+
+ tpf->numerator = 1;
+ tpf->denominator = priv->fps;
+
+ return 0;
+}
+
+static int ov772x_s_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *ival)
+{
+ struct ov772x_priv *priv = to_ov772x(sd);
+ struct v4l2_fract *tpf = &ival->interval;
+
+ return ov772x_set_frame_rate(priv, tpf, priv->cfmt, priv->win);
+}
+
+static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov772x_priv *priv = container_of(ctrl->handler,
+ struct ov772x_priv, hdl);
+ struct v4l2_subdev *sd = &priv->subdev;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret = 0;
+ u8 val;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ val = ctrl->val ? VFLIP_IMG : 0x00;
+ priv->flag_vflip = ctrl->val;
+ if (priv->info->flags & OV772X_FLAG_VFLIP)
+ val ^= VFLIP_IMG;
+ return ov772x_mask_set(client, COM3, VFLIP_IMG, val);
+ case V4L2_CID_HFLIP:
+ val = ctrl->val ? HFLIP_IMG : 0x00;
+ priv->flag_hflip = ctrl->val;
+ if (priv->info->flags & OV772X_FLAG_HFLIP)
+ val ^= HFLIP_IMG;
+ return ov772x_mask_set(client, COM3, HFLIP_IMG, val);
+ case V4L2_CID_BAND_STOP_FILTER:
+ if (!ctrl->val) {
+ /* Switch the filter off, it is on now */
+ ret = ov772x_mask_set(client, BDBASE, 0xff, 0xff);
+ if (!ret)
+ ret = ov772x_mask_set(client, COM8,
+ BNDF_ON_OFF, 0);
+ } else {
+ /* Switch the filter on, set AEC low limit */
+ val = 256 - ctrl->val;
+ ret = ov772x_mask_set(client, COM8,
+ BNDF_ON_OFF, BNDF_ON_OFF);
+ if (!ret)
+ ret = ov772x_mask_set(client, BDBASE,
+ 0xff, val);
+ }
+ if (!ret)
+ priv->band_filter = ctrl->val;
+ return ret;
+ }
+
+ return -EINVAL;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov772x_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ reg->size = 1;
+ if (reg->reg > 0xff)
+ return -EINVAL;
+
+ ret = ov772x_read(client, reg->reg);
+ if (ret < 0)
+ return ret;
+
+ reg->val = (__u64)ret;
+
+ return 0;
+}
+
+static int ov772x_s_register(struct v4l2_subdev *sd,
+ const struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->reg > 0xff ||
+ reg->val > 0xff)
+ return -EINVAL;
+
+ return ov772x_write(client, reg->reg, reg->val);
+}
+#endif
+
+static int ov772x_power_on(struct ov772x_priv *priv)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev);
+ int ret;
+
+ if (priv->clk) {
+ ret = clk_prepare_enable(priv->clk);
+ if (ret)
+ return ret;
+ }
+
+ if (priv->pwdn_gpio) {
+ gpiod_set_value(priv->pwdn_gpio, 1);
+ usleep_range(500, 1000);
+ }
+
+ /*
+ * FIXME: The reset signal is connected to a shared GPIO on some
+ * platforms (namely the SuperH Migo-R). Until a framework becomes
+ * available to handle this cleanly, request the GPIO temporarily
+ * to avoid conflicts.
+ */
+ priv->rstb_gpio = gpiod_get_optional(&client->dev, "rstb",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(priv->rstb_gpio)) {
+ dev_info(&client->dev, "Unable to get GPIO \"rstb\"");
+ return PTR_ERR(priv->rstb_gpio);
+ }
+
+ if (priv->rstb_gpio) {
+ gpiod_set_value(priv->rstb_gpio, 1);
+ usleep_range(500, 1000);
+ gpiod_set_value(priv->rstb_gpio, 0);
+ usleep_range(500, 1000);
+
+ gpiod_put(priv->rstb_gpio);
+ }
+
+ return 0;
+}
+
+static int ov772x_power_off(struct ov772x_priv *priv)
+{
+ clk_disable_unprepare(priv->clk);
+
+ if (priv->pwdn_gpio) {
+ gpiod_set_value(priv->pwdn_gpio, 0);
+ usleep_range(500, 1000);
+ }
+
+ return 0;
+}
+
+static int ov772x_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct ov772x_priv *priv = to_ov772x(sd);
+
+ return on ? ov772x_power_on(priv) :
+ ov772x_power_off(priv);
+}
+
+static const struct ov772x_win_size *ov772x_select_win(u32 width, u32 height)
+{
+ const struct ov772x_win_size *win = &ov772x_win_sizes[0];
+ u32 best_diff = UINT_MAX;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(ov772x_win_sizes); ++i) {
+ u32 diff = abs(width - ov772x_win_sizes[i].rect.width)
+ + abs(height - ov772x_win_sizes[i].rect.height);
+ if (diff < best_diff) {
+ best_diff = diff;
+ win = &ov772x_win_sizes[i];
+ }
+ }
+
+ return win;
+}
+
+static void ov772x_select_params(const struct v4l2_mbus_framefmt *mf,
+ const struct ov772x_color_format **cfmt,
+ const struct ov772x_win_size **win)
+{
+ unsigned int i;
+
+ /* Select a format. */
+ *cfmt = &ov772x_cfmts[0];
+
+ for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++) {
+ if (mf->code == ov772x_cfmts[i].code) {
+ *cfmt = &ov772x_cfmts[i];
+ break;
+ }
+ }
+
+ /* Select a window size. */
+ *win = ov772x_select_win(mf->width, mf->height);
+}
+
+static int ov772x_set_params(struct ov772x_priv *priv,
+ const struct ov772x_color_format *cfmt,
+ const struct ov772x_win_size *win)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev);
+ struct v4l2_fract tpf;
+ int ret;
+ u8 val;
+
+ /* Reset hardware. */
+ ov772x_reset(client);
+
+ /* Edge Ctrl. */
+ if (priv->info->edgectrl.strength & OV772X_MANUAL_EDGE_CTRL) {
+ /*
+ * Manual Edge Control Mode.
+ *
+ * Edge auto strength bit is set by default.
+ * Remove it when manual mode.
+ */
+
+ ret = ov772x_mask_set(client, DSPAUTO, EDGE_ACTRL, 0x00);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+
+ ret = ov772x_mask_set(client,
+ EDGE_TRSHLD, OV772X_EDGE_THRESHOLD_MASK,
+ priv->info->edgectrl.threshold);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+
+ ret = ov772x_mask_set(client,
+ EDGE_STRNGT, OV772X_EDGE_STRENGTH_MASK,
+ priv->info->edgectrl.strength);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+
+ } else if (priv->info->edgectrl.upper > priv->info->edgectrl.lower) {
+ /*
+ * Auto Edge Control Mode.
+ *
+ * Set upper and lower limit.
+ */
+ ret = ov772x_mask_set(client,
+ EDGE_UPPER, OV772X_EDGE_UPPER_MASK,
+ priv->info->edgectrl.upper);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+
+ ret = ov772x_mask_set(client,
+ EDGE_LOWER, OV772X_EDGE_LOWER_MASK,
+ priv->info->edgectrl.lower);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+ }
+
+ /* Format and window size. */
+ ret = ov772x_write(client, HSTART, win->rect.left >> 2);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+ ret = ov772x_write(client, HSIZE, win->rect.width >> 2);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+ ret = ov772x_write(client, VSTART, win->rect.top >> 1);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+ ret = ov772x_write(client, VSIZE, win->rect.height >> 1);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+ ret = ov772x_write(client, HOUTSIZE, win->rect.width >> 2);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+ ret = ov772x_write(client, VOUTSIZE, win->rect.height >> 1);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+ ret = ov772x_write(client, HREF,
+ ((win->rect.top & 1) << HREF_VSTART_SHIFT) |
+ ((win->rect.left & 3) << HREF_HSTART_SHIFT) |
+ ((win->rect.height & 1) << HREF_VSIZE_SHIFT) |
+ ((win->rect.width & 3) << HREF_HSIZE_SHIFT));
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+ ret = ov772x_write(client, EXHCH,
+ ((win->rect.height & 1) << EXHCH_VSIZE_SHIFT) |
+ ((win->rect.width & 3) << EXHCH_HSIZE_SHIFT));
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+
+ /* Set DSP_CTRL3. */
+ val = cfmt->dsp3;
+ if (val) {
+ ret = ov772x_mask_set(client,
+ DSP_CTRL3, UV_MASK, val);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+ }
+
+ /* DSP_CTRL4: AEC reference point and DSP output format. */
+ if (cfmt->dsp4) {
+ ret = ov772x_write(client, DSP_CTRL4, cfmt->dsp4);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+ }
+
+ /* Set COM3. */
+ val = cfmt->com3;
+ if (priv->info->flags & OV772X_FLAG_VFLIP)
+ val |= VFLIP_IMG;
+ if (priv->info->flags & OV772X_FLAG_HFLIP)
+ val |= HFLIP_IMG;
+ if (priv->flag_vflip)
+ val ^= VFLIP_IMG;
+ if (priv->flag_hflip)
+ val ^= HFLIP_IMG;
+
+ ret = ov772x_mask_set(client,
+ COM3, SWAP_MASK | IMG_MASK, val);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+
+ /* COM7: Sensor resolution and output format control. */
+ ret = ov772x_write(client, COM7, win->com7_bit | cfmt->com7);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+
+ /* COM4, CLKRC: Set pixel clock and framerate. */
+ tpf.numerator = 1;
+ tpf.denominator = priv->fps;
+ ret = ov772x_set_frame_rate(priv, &tpf, cfmt, win);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+
+ /* Set COM8. */
+ if (priv->band_filter) {
+ ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, 1);
+ if (!ret)
+ ret = ov772x_mask_set(client, BDBASE,
+ 0xff, 256 - priv->band_filter);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+ }
+
+ return ret;
+
+ov772x_set_fmt_error:
+
+ ov772x_reset(client);
+
+ return ret;
+}
+
+static int ov772x_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_selection *sel)
+{
+ struct ov772x_priv *priv = to_ov772x(sd);
+
+ if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
+ sel->r.left = 0;
+ sel->r.top = 0;
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP:
+ sel->r.width = priv->win->rect.width;
+ sel->r.height = priv->win->rect.height;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ov772x_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *mf = &format->format;
+ struct ov772x_priv *priv = to_ov772x(sd);
+
+ if (format->pad)
+ return -EINVAL;
+
+ mf->width = priv->win->rect.width;
+ mf->height = priv->win->rect.height;
+ mf->code = priv->cfmt->code;
+ mf->colorspace = priv->cfmt->colorspace;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int ov772x_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct ov772x_priv *priv = to_ov772x(sd);
+ struct v4l2_mbus_framefmt *mf = &format->format;
+ const struct ov772x_color_format *cfmt;
+ const struct ov772x_win_size *win;
+ int ret;
+
+ if (format->pad)
+ return -EINVAL;
+
+ ov772x_select_params(mf, &cfmt, &win);
+
+ mf->code = cfmt->code;
+ mf->width = win->rect.width;
+ mf->height = win->rect.height;
+ mf->field = V4L2_FIELD_NONE;
+ mf->colorspace = cfmt->colorspace;
+ mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ mf->quantization = V4L2_QUANTIZATION_DEFAULT;
+ mf->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+ cfg->try_fmt = *mf;
+ return 0;
+ }
+
+ ret = ov772x_set_params(priv, cfmt, win);
+ if (ret < 0)
+ return ret;
+
+ priv->win = win;
+ priv->cfmt = cfmt;
+
+ return 0;
+}
+
+static int ov772x_video_probe(struct ov772x_priv *priv)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev);
+ u8 pid, ver;
+ const char *devname;
+ int ret;
+
+ ret = ov772x_s_power(&priv->subdev, 1);
+ if (ret < 0)
+ return ret;
+
+ /* Check and show product ID and manufacturer ID. */
+ pid = ov772x_read(client, PID);
+ ver = ov772x_read(client, VER);
+
+ switch (VERSION(pid, ver)) {
+ case OV7720:
+ devname = "ov7720";
+ break;
+ case OV7725:
+ devname = "ov7725";
+ break;
+ default:
+ dev_err(&client->dev,
+ "Product ID error %x:%x\n", pid, ver);
+ ret = -ENODEV;
+ goto done;
+ }
+
+ dev_info(&client->dev,
+ "%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
+ devname,
+ pid,
+ ver,
+ ov772x_read(client, MIDH),
+ ov772x_read(client, MIDL));
+ ret = v4l2_ctrl_handler_setup(&priv->hdl);
+
+done:
+ ov772x_s_power(&priv->subdev, 0);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops ov772x_ctrl_ops = {
+ .s_ctrl = ov772x_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops ov772x_subdev_core_ops = {
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = ov772x_g_register,
+ .s_register = ov772x_s_register,
+#endif
+ .s_power = ov772x_s_power,
+};
+
+static int ov772x_enum_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_interval_enum *fie)
+{
+ if (fie->pad || fie->index >= ARRAY_SIZE(ov772x_frame_intervals))
+ return -EINVAL;
+
+ if (fie->width != VGA_WIDTH && fie->width != QVGA_WIDTH)
+ return -EINVAL;
+ if (fie->height != VGA_HEIGHT && fie->height != QVGA_HEIGHT)
+ return -EINVAL;
+
+ fie->interval.numerator = 1;
+ fie->interval.denominator = ov772x_frame_intervals[fie->index];
+
+ return 0;
+}
+
+static int ov772x_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->pad || code->index >= ARRAY_SIZE(ov772x_cfmts))
+ return -EINVAL;
+
+ code->code = ov772x_cfmts[code->index].code;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops ov772x_subdev_video_ops = {
+ .s_stream = ov772x_s_stream,
+ .s_frame_interval = ov772x_s_frame_interval,
+ .g_frame_interval = ov772x_g_frame_interval,
+};
+
+static const struct v4l2_subdev_pad_ops ov772x_subdev_pad_ops = {
+ .enum_frame_interval = ov772x_enum_frame_interval,
+ .enum_mbus_code = ov772x_enum_mbus_code,
+ .get_selection = ov772x_get_selection,
+ .get_fmt = ov772x_get_fmt,
+ .set_fmt = ov772x_set_fmt,
+};
+
+static const struct v4l2_subdev_ops ov772x_subdev_ops = {
+ .core = &ov772x_subdev_core_ops,
+ .video = &ov772x_subdev_video_ops,
+ .pad = &ov772x_subdev_pad_ops,
+};
+
+/*
+ * i2c_driver function
+ */
+
+static int ov772x_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct ov772x_priv *priv;
+ struct i2c_adapter *adapter = client->adapter;
+ int ret;
+
+ if (!client->dev.platform_data) {
+ dev_err(&client->dev, "Missing ov772x platform data\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_PROTOCOL_MANGLING)) {
+ dev_err(&adapter->dev,
+ "I2C-Adapter doesn't support SMBUS_BYTE_DATA or PROTOCOL_MANGLING\n");
+ return -EIO;
+ }
+ client->flags |= I2C_CLIENT_SCCB;
+
+ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->info = client->dev.platform_data;
+
+ v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops);
+ v4l2_ctrl_handler_init(&priv->hdl, 3);
+ v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops,
+ V4L2_CID_BAND_STOP_FILTER, 0, 256, 1, 0);
+ priv->subdev.ctrl_handler = &priv->hdl;
+ if (priv->hdl.error)
+ return priv->hdl.error;
+
+ priv->clk = clk_get(&client->dev, "xclk");
+ if (IS_ERR(priv->clk)) {
+ dev_err(&client->dev, "Unable to get xclk clock\n");
+ ret = PTR_ERR(priv->clk);
+ goto error_ctrl_free;
+ }
+
+ priv->pwdn_gpio = gpiod_get_optional(&client->dev, "pwdn",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(priv->pwdn_gpio)) {
+ dev_info(&client->dev, "Unable to get GPIO \"pwdn\"");
+ ret = PTR_ERR(priv->pwdn_gpio);
+ goto error_clk_put;
+ }
+
+ ret = ov772x_video_probe(priv);
+ if (ret < 0)
+ goto error_gpio_put;
+
+ priv->cfmt = &ov772x_cfmts[0];
+ priv->win = &ov772x_win_sizes[0];
+ priv->fps = 15;
+
+ ret = v4l2_async_register_subdev(&priv->subdev);
+ if (ret)
+ goto error_gpio_put;
+
+ return 0;
+
+error_gpio_put:
+ if (priv->pwdn_gpio)
+ gpiod_put(priv->pwdn_gpio);
+error_clk_put:
+ clk_put(priv->clk);
+error_ctrl_free:
+ v4l2_ctrl_handler_free(&priv->hdl);
+
+ return ret;
+}
+
+static int ov772x_remove(struct i2c_client *client)
+{
+ struct ov772x_priv *priv = to_ov772x(i2c_get_clientdata(client));
+
+ clk_put(priv->clk);
+ if (priv->pwdn_gpio)
+ gpiod_put(priv->pwdn_gpio);
+ v4l2_async_unregister_subdev(&priv->subdev);
+ v4l2_ctrl_handler_free(&priv->hdl);
+
+ return 0;
+}
+
+static const struct i2c_device_id ov772x_id[] = {
+ { "ov772x", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ov772x_id);
+
+static struct i2c_driver ov772x_i2c_driver = {
+ .driver = {
+ .name = "ov772x",
+ },
+ .probe = ov772x_probe,
+ .remove = ov772x_remove,
+ .id_table = ov772x_id,
+};
+
+module_i2c_driver(ov772x_i2c_driver);
+
+MODULE_DESCRIPTION("V4L2 driver for OV772x image sensor");
+MODULE_AUTHOR("Kuninori Morimoto");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c
index fc9dbbcae56e..01f578785e79 100644
--- a/drivers/media/i2c/ov7740.c
+++ b/drivers/media/i2c/ov7740.c
@@ -279,7 +279,7 @@ static int ov7740_get_register(struct v4l2_subdev *sd,
reg->val = val;
reg->size = 1;
- return 0;
+ return ret;
}
static int ov7740_set_register(struct v4l2_subdev *sd,
@@ -624,17 +624,11 @@ err_unlock:
return ret;
}
-static int ov7740_get_parm(struct v4l2_subdev *sd,
- struct v4l2_streamparm *parms)
+static int ov7740_g_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *ival)
{
- struct v4l2_captureparm *cp = &parms->parm.capture;
- struct v4l2_fract *tpf = &cp->timeperframe;
-
- if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
+ struct v4l2_fract *tpf = &ival->interval;
- memset(cp, 0, sizeof(struct v4l2_captureparm));
- cp->capability = V4L2_CAP_TIMEPERFRAME;
tpf->numerator = 1;
tpf->denominator = 60;
@@ -642,18 +636,11 @@ static int ov7740_get_parm(struct v4l2_subdev *sd,
return 0;
}
-static int ov7740_set_parm(struct v4l2_subdev *sd,
- struct v4l2_streamparm *parms)
+static int ov7740_s_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *ival)
{
- struct v4l2_captureparm *cp = &parms->parm.capture;
- struct v4l2_fract *tpf = &cp->timeperframe;
-
- if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- if (cp->extendedmode != 0)
- return -EINVAL;
+ struct v4l2_fract *tpf = &ival->interval;
- cp->capability = V4L2_CAP_TIMEPERFRAME;
tpf->numerator = 1;
tpf->denominator = 60;
@@ -663,8 +650,8 @@ static int ov7740_set_parm(struct v4l2_subdev *sd,
static struct v4l2_subdev_video_ops ov7740_subdev_video_ops = {
.s_stream = ov7740_set_stream,
- .s_parm = ov7740_set_parm,
- .g_parm = ov7740_get_parm,
+ .s_frame_interval = ov7740_s_frame_interval,
+ .g_frame_interval = ov7740_g_frame_interval,
};
static const struct reg_sequence ov7740_format_yuyv[] = {
diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c
index e519f278d5f9..5bea31cd41aa 100644
--- a/drivers/media/i2c/ov9650.c
+++ b/drivers/media/i2c/ov9650.c
@@ -11,8 +11,10 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/media.h>
@@ -249,9 +251,10 @@ struct ov965x {
struct v4l2_subdev sd;
struct media_pad pad;
enum v4l2_mbus_type bus_type;
- int gpios[NUM_GPIOS];
+ struct gpio_desc *gpios[NUM_GPIOS];
/* External master clock frequency */
unsigned long mclk_frequency;
+ struct clk *clk;
/* Protects the struct fields below */
struct mutex lock;
@@ -513,24 +516,27 @@ static int ov965x_set_color_matrix(struct ov965x *ov965x)
return 0;
}
-static void ov965x_gpio_set(int gpio, int val)
-{
- if (gpio_is_valid(gpio))
- gpio_set_value(gpio, val);
-}
-
-static void __ov965x_set_power(struct ov965x *ov965x, int on)
+static int __ov965x_set_power(struct ov965x *ov965x, int on)
{
if (on) {
- ov965x_gpio_set(ov965x->gpios[GPIO_PWDN], 0);
- ov965x_gpio_set(ov965x->gpios[GPIO_RST], 0);
+ int ret = clk_prepare_enable(ov965x->clk);
+
+ if (ret)
+ return ret;
+
+ gpiod_set_value_cansleep(ov965x->gpios[GPIO_PWDN], 0);
+ gpiod_set_value_cansleep(ov965x->gpios[GPIO_RST], 0);
msleep(25);
} else {
- ov965x_gpio_set(ov965x->gpios[GPIO_RST], 1);
- ov965x_gpio_set(ov965x->gpios[GPIO_PWDN], 1);
+ gpiod_set_value_cansleep(ov965x->gpios[GPIO_RST], 1);
+ gpiod_set_value_cansleep(ov965x->gpios[GPIO_PWDN], 1);
+
+ clk_disable_unprepare(ov965x->clk);
}
ov965x->streaming = 0;
+
+ return 0;
}
static int ov965x_s_power(struct v4l2_subdev *sd, int on)
@@ -543,8 +549,8 @@ static int ov965x_s_power(struct v4l2_subdev *sd, int on)
mutex_lock(&ov965x->lock);
if (ov965x->power == !on) {
- __ov965x_set_power(ov965x, on);
- if (on) {
+ ret = __ov965x_set_power(ov965x, on);
+ if (!ret && on) {
ret = ov965x_write_array(client,
ov965x_init_regs);
ov965x->apply_frame_fmt = 1;
@@ -1130,8 +1136,8 @@ static int __ov965x_set_frame_interval(struct ov965x *ov965x,
if (fi->interval.denominator == 0)
return -EINVAL;
- req_int = (u64)(fi->interval.numerator * 10000) /
- fi->interval.denominator;
+ req_int = (u64)fi->interval.numerator * 10000;
+ do_div(req_int, fi->interval.denominator);
for (i = 0; i < ARRAY_SIZE(ov965x_intervals); i++) {
const struct ov965x_interval *iv = &ov965x_intervals[i];
@@ -1410,16 +1416,17 @@ static const struct v4l2_subdev_ops ov965x_subdev_ops = {
/*
* Reset and power down GPIOs configuration
*/
-static int ov965x_configure_gpios(struct ov965x *ov965x,
- const struct ov9650_platform_data *pdata)
+static int ov965x_configure_gpios_pdata(struct ov965x *ov965x,
+ const struct ov9650_platform_data *pdata)
{
int ret, i;
+ int gpios[NUM_GPIOS];
- ov965x->gpios[GPIO_PWDN] = pdata->gpio_pwdn;
- ov965x->gpios[GPIO_RST] = pdata->gpio_reset;
+ gpios[GPIO_PWDN] = pdata->gpio_pwdn;
+ gpios[GPIO_RST] = pdata->gpio_reset;
for (i = 0; i < ARRAY_SIZE(ov965x->gpios); i++) {
- int gpio = ov965x->gpios[i];
+ int gpio = gpios[i];
if (!gpio_is_valid(gpio))
continue;
@@ -1429,9 +1436,30 @@ static int ov965x_configure_gpios(struct ov965x *ov965x,
return ret;
v4l2_dbg(1, debug, &ov965x->sd, "set gpio %d to 1\n", gpio);
- gpio_set_value(gpio, 1);
+ gpio_set_value_cansleep(gpio, 1);
gpio_export(gpio, 0);
- ov965x->gpios[i] = gpio;
+ ov965x->gpios[i] = gpio_to_desc(gpio);
+ }
+
+ return 0;
+}
+
+static int ov965x_configure_gpios(struct ov965x *ov965x)
+{
+ struct device *dev = &ov965x->client->dev;
+
+ ov965x->gpios[GPIO_PWDN] = devm_gpiod_get_optional(dev, "powerdown",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(ov965x->gpios[GPIO_PWDN])) {
+ dev_info(dev, "can't get %s GPIO\n", "powerdown");
+ return PTR_ERR(ov965x->gpios[GPIO_PWDN]);
+ }
+
+ ov965x->gpios[GPIO_RST] = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(ov965x->gpios[GPIO_RST])) {
+ dev_info(dev, "can't get %s GPIO\n", "reset");
+ return PTR_ERR(ov965x->gpios[GPIO_RST]);
}
return 0;
@@ -1445,7 +1473,10 @@ static int ov965x_detect_sensor(struct v4l2_subdev *sd)
int ret;
mutex_lock(&ov965x->lock);
- __ov965x_set_power(ov965x, 1);
+ ret = __ov965x_set_power(ov965x, 1);
+ if (ret)
+ goto out;
+
msleep(25);
/* Check sensor revision */
@@ -1465,6 +1496,7 @@ static int ov965x_detect_sensor(struct v4l2_subdev *sd)
ret = -ENODEV;
}
}
+out:
mutex_unlock(&ov965x->lock);
return ret;
@@ -1478,23 +1510,39 @@ static int ov965x_probe(struct i2c_client *client,
struct ov965x *ov965x;
int ret;
- if (!pdata) {
- dev_err(&client->dev, "platform data not specified\n");
- return -EINVAL;
- }
-
- if (pdata->mclk_frequency == 0) {
- dev_err(&client->dev, "MCLK frequency not specified\n");
- return -EINVAL;
- }
-
ov965x = devm_kzalloc(&client->dev, sizeof(*ov965x), GFP_KERNEL);
if (!ov965x)
return -ENOMEM;
- mutex_init(&ov965x->lock);
ov965x->client = client;
- ov965x->mclk_frequency = pdata->mclk_frequency;
+
+ if (pdata) {
+ if (pdata->mclk_frequency == 0) {
+ dev_err(&client->dev, "MCLK frequency not specified\n");
+ return -EINVAL;
+ }
+ ov965x->mclk_frequency = pdata->mclk_frequency;
+
+ ret = ov965x_configure_gpios_pdata(ov965x, pdata);
+ if (ret < 0)
+ return ret;
+ } else if (dev_fwnode(&client->dev)) {
+ ov965x->clk = devm_clk_get(&ov965x->client->dev, NULL);
+ if (IS_ERR(ov965x->clk))
+ return PTR_ERR(ov965x->clk);
+ ov965x->mclk_frequency = clk_get_rate(ov965x->clk);
+
+ ret = ov965x_configure_gpios(ov965x);
+ if (ret < 0)
+ return ret;
+ } else {
+ dev_err(&client->dev,
+ "Neither platform data nor device property specified\n");
+
+ return -EINVAL;
+ }
+
+ mutex_init(&ov965x->lock);
sd = &ov965x->sd;
v4l2_i2c_subdev_init(sd, client, &ov965x_subdev_ops);
@@ -1504,10 +1552,6 @@ static int ov965x_probe(struct i2c_client *client,
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
V4L2_SUBDEV_FL_HAS_EVENTS;
- ret = ov965x_configure_gpios(ov965x, pdata);
- if (ret < 0)
- goto err_mutex;
-
ov965x->pad.flags = MEDIA_PAD_FL_SOURCE;
sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
ret = media_entity_pads_init(&sd->entity, 1, &ov965x->pad);
@@ -1563,9 +1607,19 @@ static const struct i2c_device_id ov965x_id[] = {
};
MODULE_DEVICE_TABLE(i2c, ov965x_id);
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id ov965x_of_match[] = {
+ { .compatible = "ovti,ov9650", },
+ { .compatible = "ovti,ov9652", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ov965x_of_match);
+#endif
+
static struct i2c_driver ov965x_i2c_driver = {
.driver = {
.name = DRIVER_NAME,
+ .of_match_table = of_match_ptr(ov965x_of_match),
},
.probe = ov965x_probe,
.remove = ov965x_remove,
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
index cdc4f2392ef9..ce196b60f917 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c
+++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
@@ -248,17 +248,17 @@ static int s5c73m3_check_status(struct s5c73m3 *state, unsigned int value)
{
unsigned long start = jiffies;
unsigned long end = start + msecs_to_jiffies(2000);
- int ret = 0;
+ int ret;
u16 status;
int count = 0;
- while (time_is_after_jiffies(end)) {
+ do {
ret = s5c73m3_read(state, REG_STATUS, &status);
if (ret < 0 || status == value)
break;
usleep_range(500, 1000);
++count;
- }
+ } while (time_is_after_jiffies(end));
if (count > 0)
v4l2_dbg(1, s5c73m3_dbg, &state->sensor_sd,
diff --git a/drivers/media/i2c/saa6588.c b/drivers/media/i2c/saa6588.c
index c3089bd34df2..33d2987f9555 100644
--- a/drivers/media/i2c/saa6588.c
+++ b/drivers/media/i2c/saa6588.c
@@ -411,9 +411,9 @@ static long saa6588_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
break;
/* --- poll() for /dev/radio --- */
case SAA6588_CMD_POLL:
- a->result = 0;
+ a->poll_mask = 0;
if (s->data_available_for_read)
- a->result |= EPOLLIN | EPOLLRDNORM;
+ a->poll_mask |= EPOLLIN | EPOLLRDNORM;
poll_wait(a->instance, &s->read_queue, a->event_list);
break;
diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/media/i2c/soc_camera/Kconfig
index 72b369895b37..7c2aabc8a3f6 100644
--- a/drivers/media/i2c/soc_camera/Kconfig
+++ b/drivers/media/i2c/soc_camera/Kconfig
@@ -1,11 +1,5 @@
comment "soc_camera sensor drivers"
-config SOC_CAMERA_IMX074
- tristate "imx074 support"
- depends on SOC_CAMERA && I2C
- help
- This driver supports IMX074 cameras from Sony
-
config SOC_CAMERA_MT9M001
tristate "mt9m001 support"
depends on SOC_CAMERA && I2C
@@ -23,12 +17,6 @@ config SOC_CAMERA_MT9M111
This is the legacy configuration which shouldn't be used anymore,
while VIDEO_MT9M111 should be used instead.
-config SOC_CAMERA_MT9T031
- tristate "mt9t031 support"
- depends on SOC_CAMERA && I2C
- help
- This driver supports MT9T031 cameras from Micron.
-
config SOC_CAMERA_MT9T112
tristate "mt9t112 support"
depends on SOC_CAMERA && I2C
diff --git a/drivers/media/i2c/soc_camera/Makefile b/drivers/media/i2c/soc_camera/Makefile
index faa2df8901d2..8c7770f62997 100644
--- a/drivers/media/i2c/soc_camera/Makefile
+++ b/drivers/media/i2c/soc_camera/Makefile
@@ -1,7 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
-obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o
obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o
-obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o
obj-$(CONFIG_SOC_CAMERA_MT9T112) += mt9t112.o
obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o
obj-$(CONFIG_SOC_CAMERA_OV5642) += ov5642.o
diff --git a/drivers/media/i2c/soc_camera/mt9t112.c b/drivers/media/i2c/soc_camera/mt9t112.c
index 297d22ebcb18..b53c36dfa469 100644
--- a/drivers/media/i2c/soc_camera/mt9t112.c
+++ b/drivers/media/i2c/soc_camera/mt9t112.c
@@ -85,7 +85,7 @@ struct mt9t112_format {
struct mt9t112_priv {
struct v4l2_subdev subdev;
- struct mt9t112_camera_info *info;
+ struct mt9t112_platform_data *info;
struct i2c_client *client;
struct v4l2_rect frame;
struct v4l2_clk *clk;
diff --git a/drivers/media/i2c/sr030pc30.c b/drivers/media/i2c/sr030pc30.c
index 0bf031b7e4fa..2a4882cddc51 100644
--- a/drivers/media/i2c/sr030pc30.c
+++ b/drivers/media/i2c/sr030pc30.c
@@ -511,13 +511,16 @@ static int sr030pc30_get_fmt(struct v4l2_subdev *sd,
static const struct sr030pc30_format *try_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
- int i = ARRAY_SIZE(sr030pc30_formats);
+ int i;
sr030pc30_try_frame_size(mf);
- while (i--)
+ for (i = 0; i < ARRAY_SIZE(sr030pc30_formats); i++) {
if (mf->code == sr030pc30_formats[i].code)
break;
+ }
+ if (i == ARRAY_SIZE(sr030pc30_formats))
+ i = 0;
mf->code = sr030pc30_formats[i].code;
diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c
index 2b8181469b93..393bbbbbaad7 100644
--- a/drivers/media/i2c/tc358743.c
+++ b/drivers/media/i2c/tc358743.c
@@ -1,22 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* tc358743 - Toshiba HDMI to CSI-2 bridge
*
* Copyright 2015 Cisco Systems, Inc. and/or its affiliates. All rights
* reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
- *
*/
/*
diff --git a/drivers/media/i2c/tc358743_regs.h b/drivers/media/i2c/tc358743_regs.h
index 227b46471793..2495878dc358 100644
--- a/drivers/media/i2c/tc358743_regs.h
+++ b/drivers/media/i2c/tc358743_regs.h
@@ -1,22 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* tc358743 - Toshiba HDMI to CSI-2 bridge - register names and bit masks
*
* Copyright 2015 Cisco Systems, Inc. and/or its affiliates. All rights
* reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
- *
*/
/*
diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c
new file mode 100644
index 000000000000..3021913c28fa
--- /dev/null
+++ b/drivers/media/i2c/tda1997x.c
@@ -0,0 +1,2820 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Gateworks Corporation
+ */
+#include <linux/delay.h>
+#include <linux/hdmi.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+#include <linux/v4l2-dv-timings.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dv-timings.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fwnode.h>
+#include <media/i2c/tda1997x.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <dt-bindings/media/tda1997x.h>
+
+#include "tda1997x_regs.h"
+
+#define TDA1997X_MBUS_CODES 5
+
+/* debug level */
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "debug level (0-2)");
+
+/* Audio formats */
+static const char * const audtype_names[] = {
+ "PCM", /* PCM Samples */
+ "HBR", /* High Bit Rate Audio */
+ "OBA", /* One-Bit Audio */
+ "DST" /* Direct Stream Transfer */
+};
+
+/* Audio output port formats */
+enum audfmt_types {
+ AUDFMT_TYPE_DISABLED = 0,
+ AUDFMT_TYPE_I2S,
+ AUDFMT_TYPE_SPDIF,
+};
+static const char * const audfmt_names[] = {
+ "Disabled",
+ "I2S",
+ "SPDIF",
+};
+
+/* Video input formats */
+static const char * const hdmi_colorspace_names[] = {
+ "RGB", "YUV422", "YUV444", "YUV420", "", "", "", "",
+};
+static const char * const hdmi_colorimetry_names[] = {
+ "", "ITU601", "ITU709", "Extended",
+};
+static const char * const v4l2_quantization_names[] = {
+ "Default",
+ "Full Range (0-255)",
+ "Limited Range (16-235)",
+};
+
+/* Video output port formats */
+static const char * const vidfmt_names[] = {
+ "RGB444/YUV444", /* RGB/YUV444 16bit data bus, 8bpp */
+ "YUV422 semi-planar", /* YUV422 16bit data base, 8bpp */
+ "YUV422 CCIR656", /* BT656 (YUV 8bpp 2 clock per pixel) */
+ "Invalid",
+};
+
+/*
+ * Colorspace conversion matrices
+ */
+struct color_matrix_coefs {
+ const char *name;
+ /* Input offsets */
+ s16 offint1;
+ s16 offint2;
+ s16 offint3;
+ /* Coeficients */
+ s16 p11coef;
+ s16 p12coef;
+ s16 p13coef;
+ s16 p21coef;
+ s16 p22coef;
+ s16 p23coef;
+ s16 p31coef;
+ s16 p32coef;
+ s16 p33coef;
+ /* Output offsets */
+ s16 offout1;
+ s16 offout2;
+ s16 offout3;
+};
+
+enum {
+ ITU709_RGBFULL,
+ ITU601_RGBFULL,
+ RGBLIMITED_RGBFULL,
+ RGBLIMITED_ITU601,
+ RGBLIMITED_ITU709,
+ RGBFULL_ITU601,
+ RGBFULL_ITU709,
+};
+
+/* NB: 4096 is 1.0 using fixed point numbers */
+static const struct color_matrix_coefs conv_matrix[] = {
+ {
+ "YUV709 -> RGB full",
+ -256, -2048, -2048,
+ 4769, -2183, -873,
+ 4769, 7343, 0,
+ 4769, 0, 8652,
+ 0, 0, 0,
+ },
+ {
+ "YUV601 -> RGB full",
+ -256, -2048, -2048,
+ 4769, -3330, -1602,
+ 4769, 6538, 0,
+ 4769, 0, 8264,
+ 256, 256, 256,
+ },
+ {
+ "RGB limited -> RGB full",
+ -256, -256, -256,
+ 0, 4769, 0,
+ 0, 0, 4769,
+ 4769, 0, 0,
+ 0, 0, 0,
+ },
+ {
+ "RGB limited -> ITU601",
+ -256, -256, -256,
+ 2404, 1225, 467,
+ -1754, 2095, -341,
+ -1388, -707, 2095,
+ 256, 2048, 2048,
+ },
+ {
+ "RGB limited -> ITU709",
+ -256, -256, -256,
+ 2918, 867, 295,
+ -1894, 2087, -190,
+ -1607, -477, 2087,
+ 256, 2048, 2048,
+ },
+ {
+ "RGB full -> ITU601",
+ 0, 0, 0,
+ 2065, 1052, 401,
+ -1506, 1799, -293,
+ -1192, -607, 1799,
+ 256, 2048, 2048,
+ },
+ {
+ "RGB full -> ITU709",
+ 0, 0, 0,
+ 2506, 745, 253,
+ -1627, 1792, -163,
+ -1380, -410, 1792,
+ 256, 2048, 2048,
+ },
+};
+
+static const struct v4l2_dv_timings_cap tda1997x_dv_timings_cap = {
+ .type = V4L2_DV_BT_656_1120,
+ /* keep this initialization for compatibility with GCC < 4.4.6 */
+ .reserved = { 0 },
+
+ V4L2_INIT_BT_TIMINGS(
+ 640, 1920, /* min/max width */
+ 350, 1200, /* min/max height */
+ 13000000, 165000000, /* min/max pixelclock */
+ /* standards */
+ V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
+ V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT,
+ /* capabilities */
+ V4L2_DV_BT_CAP_INTERLACED | V4L2_DV_BT_CAP_PROGRESSIVE |
+ V4L2_DV_BT_CAP_REDUCED_BLANKING |
+ V4L2_DV_BT_CAP_CUSTOM
+ )
+};
+
+/* regulator supplies */
+static const char * const tda1997x_supply_name[] = {
+ "DOVDD", /* Digital I/O supply */
+ "DVDD", /* Digital Core supply */
+ "AVDD", /* Analog supply */
+};
+
+#define TDA1997X_NUM_SUPPLIES ARRAY_SIZE(tda1997x_supply_name)
+
+enum tda1997x_type {
+ TDA19971,
+ TDA19973,
+};
+
+enum tda1997x_hdmi_pads {
+ TDA1997X_PAD_SOURCE,
+ TDA1997X_NUM_PADS,
+};
+
+struct tda1997x_chip_info {
+ enum tda1997x_type type;
+ const char *name;
+};
+
+struct tda1997x_state {
+ const struct tda1997x_chip_info *info;
+ struct tda1997x_platform_data pdata;
+ struct i2c_client *client;
+ struct i2c_client *client_cec;
+ struct v4l2_subdev sd;
+ struct regulator_bulk_data supplies[TDA1997X_NUM_SUPPLIES];
+ struct media_pad pads[TDA1997X_NUM_PADS];
+ struct mutex lock;
+ struct mutex page_lock;
+ char page;
+
+ /* detected info from chip */
+ int chip_revision;
+ char port_30bit;
+ char output_2p5;
+ char tmdsb_clk;
+ char tmdsb_soc;
+
+ /* status info */
+ char hdmi_status;
+ char mptrw_in_progress;
+ char activity_status;
+ char input_detect[2];
+
+ /* video */
+ struct hdmi_avi_infoframe avi_infoframe;
+ struct v4l2_hdmi_colorimetry colorimetry;
+ u32 rgb_quantization_range;
+ struct v4l2_dv_timings timings;
+ int fps;
+ const struct color_matrix_coefs *conv;
+ u32 mbus_codes[TDA1997X_MBUS_CODES]; /* available modes */
+ u32 mbus_code; /* current mode */
+ u8 vid_fmt;
+
+ /* controls */
+ struct v4l2_ctrl_handler hdl;
+ struct v4l2_ctrl *detect_tx_5v_ctrl;
+ struct v4l2_ctrl *rgb_quantization_range_ctrl;
+
+ /* audio */
+ u8 audio_ch_alloc;
+ int audio_samplerate;
+ int audio_channels;
+ int audio_samplesize;
+ int audio_type;
+ struct mutex audio_lock;
+ struct snd_pcm_substream *audio_stream;
+
+ /* EDID */
+ struct {
+ u8 edid[256];
+ u32 present;
+ unsigned int blocks;
+ } edid;
+ struct delayed_work delayed_work_enable_hpd;
+};
+
+static const struct v4l2_event tda1997x_ev_fmt = {
+ .type = V4L2_EVENT_SOURCE_CHANGE,
+ .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
+};
+
+static const struct tda1997x_chip_info tda1997x_chip_info[] = {
+ [TDA19971] = {
+ .type = TDA19971,
+ .name = "tda19971",
+ },
+ [TDA19973] = {
+ .type = TDA19973,
+ .name = "tda19973",
+ },
+};
+
+static inline struct tda1997x_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct tda1997x_state, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct tda1997x_state, hdl)->sd;
+}
+
+static int tda1997x_cec_read(struct v4l2_subdev *sd, u8 reg)
+{
+ struct tda1997x_state *state = to_state(sd);
+ int val;
+
+ val = i2c_smbus_read_byte_data(state->client_cec, reg);
+ if (val < 0) {
+ v4l_err(state->client, "read reg error: reg=%2x\n", reg);
+ val = -1;
+ }
+
+ return val;
+}
+
+static int tda1997x_cec_write(struct v4l2_subdev *sd, u8 reg, u8 val)
+{
+ struct tda1997x_state *state = to_state(sd);
+ int ret = 0;
+
+ ret = i2c_smbus_write_byte_data(state->client_cec, reg, val);
+ if (ret < 0) {
+ v4l_err(state->client, "write reg error:reg=%2x,val=%2x\n",
+ reg, val);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * I2C transfer
+ */
+
+static int tda1997x_setpage(struct v4l2_subdev *sd, u8 page)
+{
+ struct tda1997x_state *state = to_state(sd);
+ int ret;
+
+ if (state->page != page) {
+ ret = i2c_smbus_write_byte_data(state->client,
+ REG_CURPAGE_00H, page);
+ if (ret < 0) {
+ v4l_err(state->client,
+ "write reg error:reg=%2x,val=%2x\n",
+ REG_CURPAGE_00H, page);
+ return ret;
+ }
+ state->page = page;
+ }
+ return 0;
+}
+
+static inline int io_read(struct v4l2_subdev *sd, u16 reg)
+{
+ struct tda1997x_state *state = to_state(sd);
+ int val;
+
+ mutex_lock(&state->page_lock);
+ if (tda1997x_setpage(sd, reg >> 8)) {
+ val = -1;
+ goto out;
+ }
+
+ val = i2c_smbus_read_byte_data(state->client, reg&0xff);
+ if (val < 0) {
+ v4l_err(state->client, "read reg error: reg=%2x\n", reg & 0xff);
+ val = -1;
+ goto out;
+ }
+
+out:
+ mutex_unlock(&state->page_lock);
+ return val;
+}
+
+static inline long io_read16(struct v4l2_subdev *sd, u16 reg)
+{
+ int val;
+ long lval = 0;
+
+ val = io_read(sd, reg);
+ if (val < 0)
+ return val;
+ lval |= (val << 8);
+ val = io_read(sd, reg + 1);
+ if (val < 0)
+ return val;
+ lval |= val;
+
+ return lval;
+}
+
+static inline long io_read24(struct v4l2_subdev *sd, u16 reg)
+{
+ int val;
+ long lval = 0;
+
+ val = io_read(sd, reg);
+ if (val < 0)
+ return val;
+ lval |= (val << 16);
+ val = io_read(sd, reg + 1);
+ if (val < 0)
+ return val;
+ lval |= (val << 8);
+ val = io_read(sd, reg + 2);
+ if (val < 0)
+ return val;
+ lval |= val;
+
+ return lval;
+}
+
+static unsigned int io_readn(struct v4l2_subdev *sd, u16 reg, u8 len, u8 *data)
+{
+ int i;
+ int sz = 0;
+ int val;
+
+ for (i = 0; i < len; i++) {
+ val = io_read(sd, reg + i);
+ if (val < 0)
+ break;
+ data[i] = val;
+ sz++;
+ }
+
+ return sz;
+}
+
+static int io_write(struct v4l2_subdev *sd, u16 reg, u8 val)
+{
+ struct tda1997x_state *state = to_state(sd);
+ s32 ret = 0;
+
+ mutex_lock(&state->page_lock);
+ if (tda1997x_setpage(sd, reg >> 8)) {
+ ret = -1;
+ goto out;
+ }
+
+ ret = i2c_smbus_write_byte_data(state->client, reg & 0xff, val);
+ if (ret < 0) {
+ v4l_err(state->client, "write reg error:reg=%2x,val=%2x\n",
+ reg&0xff, val);
+ ret = -1;
+ goto out;
+ }
+
+out:
+ mutex_unlock(&state->page_lock);
+ return ret;
+}
+
+static int io_write16(struct v4l2_subdev *sd, u16 reg, u16 val)
+{
+ int ret;
+
+ ret = io_write(sd, reg, (val >> 8) & 0xff);
+ if (ret < 0)
+ return ret;
+ ret = io_write(sd, reg + 1, val & 0xff);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int io_write24(struct v4l2_subdev *sd, u16 reg, u32 val)
+{
+ int ret;
+
+ ret = io_write(sd, reg, (val >> 16) & 0xff);
+ if (ret < 0)
+ return ret;
+ ret = io_write(sd, reg + 1, (val >> 8) & 0xff);
+ if (ret < 0)
+ return ret;
+ ret = io_write(sd, reg + 2, val & 0xff);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Hotplug
+ */
+
+enum hpd_mode {
+ HPD_LOW_BP, /* HPD low and pulse of at least 100ms */
+ HPD_LOW_OTHER, /* HPD low and pulse of at least 100ms */
+ HPD_HIGH_BP, /* HIGH */
+ HPD_HIGH_OTHER,
+ HPD_PULSE, /* HPD low pulse */
+};
+
+/* manual HPD (Hot Plug Detect) control */
+static int tda1997x_manual_hpd(struct v4l2_subdev *sd, enum hpd_mode mode)
+{
+ u8 hpd_auto, hpd_pwr, hpd_man;
+
+ hpd_auto = io_read(sd, REG_HPD_AUTO_CTRL);
+ hpd_pwr = io_read(sd, REG_HPD_POWER);
+ hpd_man = io_read(sd, REG_HPD_MAN_CTRL);
+
+ /* mask out unused bits */
+ hpd_man &= (HPD_MAN_CTRL_HPD_PULSE |
+ HPD_MAN_CTRL_5VEN |
+ HPD_MAN_CTRL_HPD_B |
+ HPD_MAN_CTRL_HPD_A);
+
+ switch (mode) {
+ /* HPD low and pulse of at least 100ms */
+ case HPD_LOW_BP:
+ /* hpd_bp=0 */
+ hpd_pwr &= ~HPD_POWER_BP_MASK;
+ /* disable HPD_A and HPD_B */
+ hpd_man &= ~(HPD_MAN_CTRL_HPD_A | HPD_MAN_CTRL_HPD_B);
+ io_write(sd, REG_HPD_POWER, hpd_pwr);
+ io_write(sd, REG_HPD_MAN_CTRL, hpd_man);
+ break;
+ /* HPD high */
+ case HPD_HIGH_BP:
+ /* hpd_bp=1 */
+ hpd_pwr &= ~HPD_POWER_BP_MASK;
+ hpd_pwr |= 1 << HPD_POWER_BP_SHIFT;
+ io_write(sd, REG_HPD_POWER, hpd_pwr);
+ break;
+ /* HPD low and pulse of at least 100ms */
+ case HPD_LOW_OTHER:
+ /* disable HPD_A and HPD_B */
+ hpd_man &= ~(HPD_MAN_CTRL_HPD_A | HPD_MAN_CTRL_HPD_B);
+ /* hp_other=0 */
+ hpd_auto &= ~HPD_AUTO_HP_OTHER;
+ io_write(sd, REG_HPD_AUTO_CTRL, hpd_auto);
+ io_write(sd, REG_HPD_MAN_CTRL, hpd_man);
+ break;
+ /* HPD high */
+ case HPD_HIGH_OTHER:
+ hpd_auto |= HPD_AUTO_HP_OTHER;
+ io_write(sd, REG_HPD_AUTO_CTRL, hpd_auto);
+ break;
+ /* HPD low pulse */
+ case HPD_PULSE:
+ /* disable HPD_A and HPD_B */
+ hpd_man &= ~(HPD_MAN_CTRL_HPD_A | HPD_MAN_CTRL_HPD_B);
+ io_write(sd, REG_HPD_MAN_CTRL, hpd_man);
+ break;
+ }
+
+ return 0;
+}
+
+static void tda1997x_delayed_work_enable_hpd(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct tda1997x_state *state = container_of(dwork,
+ struct tda1997x_state,
+ delayed_work_enable_hpd);
+ struct v4l2_subdev *sd = &state->sd;
+
+ v4l2_dbg(2, debug, sd, "%s:\n", __func__);
+
+ /* Set HPD high */
+ tda1997x_manual_hpd(sd, HPD_HIGH_OTHER);
+ tda1997x_manual_hpd(sd, HPD_HIGH_BP);
+
+ state->edid.present = 1;
+}
+
+static void tda1997x_disable_edid(struct v4l2_subdev *sd)
+{
+ struct tda1997x_state *state = to_state(sd);
+
+ v4l2_dbg(1, debug, sd, "%s\n", __func__);
+ cancel_delayed_work_sync(&state->delayed_work_enable_hpd);
+
+ /* Set HPD low */
+ tda1997x_manual_hpd(sd, HPD_LOW_BP);
+}
+
+static void tda1997x_enable_edid(struct v4l2_subdev *sd)
+{
+ struct tda1997x_state *state = to_state(sd);
+
+ v4l2_dbg(1, debug, sd, "%s\n", __func__);
+
+ /* Enable hotplug after 100ms */
+ schedule_delayed_work(&state->delayed_work_enable_hpd, HZ / 10);
+}
+
+/* -----------------------------------------------------------------------------
+ * Signal Control
+ */
+
+/*
+ * configure vid_fmt based on mbus_code
+ */
+static int
+tda1997x_setup_format(struct tda1997x_state *state, u32 code)
+{
+ v4l_dbg(1, debug, state->client, "%s code=0x%x\n", __func__, code);
+ switch (code) {
+ case MEDIA_BUS_FMT_RGB121212_1X36:
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ case MEDIA_BUS_FMT_YUV12_1X36:
+ case MEDIA_BUS_FMT_YUV8_1X24:
+ state->vid_fmt = OF_FMT_444;
+ break;
+ case MEDIA_BUS_FMT_UYVY12_1X24:
+ case MEDIA_BUS_FMT_UYVY10_1X20:
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ state->vid_fmt = OF_FMT_422_SMPT;
+ break;
+ case MEDIA_BUS_FMT_UYVY12_2X12:
+ case MEDIA_BUS_FMT_UYVY10_2X10:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ state->vid_fmt = OF_FMT_422_CCIR;
+ break;
+ default:
+ v4l_err(state->client, "incompatible format (0x%x)\n", code);
+ return -EINVAL;
+ }
+ v4l_dbg(1, debug, state->client, "%s code=0x%x fmt=%s\n", __func__,
+ code, vidfmt_names[state->vid_fmt]);
+ state->mbus_code = code;
+
+ return 0;
+}
+
+/*
+ * The color conversion matrix will convert between the colorimetry of the
+ * HDMI input to the desired output format RGB|YUV. RGB output is to be
+ * full-range and YUV is to be limited range.
+ *
+ * RGB full-range uses values from 0 to 255 which is recommended on a monitor
+ * and RGB Limited uses values from 16 to 236 (16=black, 235=white) which is
+ * typically recommended on a TV.
+ */
+static void
+tda1997x_configure_csc(struct v4l2_subdev *sd)
+{
+ struct tda1997x_state *state = to_state(sd);
+ struct hdmi_avi_infoframe *avi = &state->avi_infoframe;
+ struct v4l2_hdmi_colorimetry *c = &state->colorimetry;
+ /* Blanking code values depend on output colorspace (RGB or YUV) */
+ struct blanking_codes {
+ s16 code_gy;
+ s16 code_bu;
+ s16 code_rv;
+ };
+ static const struct blanking_codes rgb_blanking = { 64, 64, 64 };
+ static const struct blanking_codes yuv_blanking = { 64, 512, 512 };
+ const struct blanking_codes *blanking_codes = NULL;
+ u8 reg;
+
+ v4l_dbg(1, debug, state->client, "input:%s quant:%s output:%s\n",
+ hdmi_colorspace_names[avi->colorspace],
+ v4l2_quantization_names[c->quantization],
+ vidfmt_names[state->vid_fmt]);
+ state->conv = NULL;
+ switch (state->vid_fmt) {
+ /* RGB output */
+ case OF_FMT_444:
+ blanking_codes = &rgb_blanking;
+ if (c->colorspace == V4L2_COLORSPACE_SRGB) {
+ if (c->quantization == V4L2_QUANTIZATION_LIM_RANGE)
+ state->conv = &conv_matrix[RGBLIMITED_RGBFULL];
+ } else {
+ if (c->colorspace == V4L2_COLORSPACE_REC709)
+ state->conv = &conv_matrix[ITU709_RGBFULL];
+ else if (c->colorspace == V4L2_COLORSPACE_SMPTE170M)
+ state->conv = &conv_matrix[ITU601_RGBFULL];
+ }
+ break;
+
+ /* YUV output */
+ case OF_FMT_422_SMPT: /* semi-planar */
+ case OF_FMT_422_CCIR: /* CCIR656 */
+ blanking_codes = &yuv_blanking;
+ if ((c->colorspace == V4L2_COLORSPACE_SRGB) &&
+ (c->quantization == V4L2_QUANTIZATION_FULL_RANGE)) {
+ if (state->timings.bt.height <= 576)
+ state->conv = &conv_matrix[RGBFULL_ITU601];
+ else
+ state->conv = &conv_matrix[RGBFULL_ITU709];
+ } else if ((c->colorspace == V4L2_COLORSPACE_SRGB) &&
+ (c->quantization == V4L2_QUANTIZATION_LIM_RANGE)) {
+ if (state->timings.bt.height <= 576)
+ state->conv = &conv_matrix[RGBLIMITED_ITU601];
+ else
+ state->conv = &conv_matrix[RGBLIMITED_ITU709];
+ }
+ break;
+ }
+
+ if (state->conv) {
+ v4l_dbg(1, debug, state->client, "%s\n",
+ state->conv->name);
+ /* enable matrix conversion */
+ reg = io_read(sd, REG_VDP_CTRL);
+ reg &= ~VDP_CTRL_MATRIX_BP;
+ io_write(sd, REG_VDP_CTRL, reg);
+ /* offset inputs */
+ io_write16(sd, REG_VDP_MATRIX + 0, state->conv->offint1);
+ io_write16(sd, REG_VDP_MATRIX + 2, state->conv->offint2);
+ io_write16(sd, REG_VDP_MATRIX + 4, state->conv->offint3);
+ /* coefficients */
+ io_write16(sd, REG_VDP_MATRIX + 6, state->conv->p11coef);
+ io_write16(sd, REG_VDP_MATRIX + 8, state->conv->p12coef);
+ io_write16(sd, REG_VDP_MATRIX + 10, state->conv->p13coef);
+ io_write16(sd, REG_VDP_MATRIX + 12, state->conv->p21coef);
+ io_write16(sd, REG_VDP_MATRIX + 14, state->conv->p22coef);
+ io_write16(sd, REG_VDP_MATRIX + 16, state->conv->p23coef);
+ io_write16(sd, REG_VDP_MATRIX + 18, state->conv->p31coef);
+ io_write16(sd, REG_VDP_MATRIX + 20, state->conv->p32coef);
+ io_write16(sd, REG_VDP_MATRIX + 22, state->conv->p33coef);
+ /* offset outputs */
+ io_write16(sd, REG_VDP_MATRIX + 24, state->conv->offout1);
+ io_write16(sd, REG_VDP_MATRIX + 26, state->conv->offout2);
+ io_write16(sd, REG_VDP_MATRIX + 28, state->conv->offout3);
+ } else {
+ /* disable matrix conversion */
+ reg = io_read(sd, REG_VDP_CTRL);
+ reg |= VDP_CTRL_MATRIX_BP;
+ io_write(sd, REG_VDP_CTRL, reg);
+ }
+
+ /* SetBlankingCodes */
+ if (blanking_codes) {
+ io_write16(sd, REG_BLK_GY, blanking_codes->code_gy);
+ io_write16(sd, REG_BLK_BU, blanking_codes->code_bu);
+ io_write16(sd, REG_BLK_RV, blanking_codes->code_rv);
+ }
+}
+
+/* Configure frame detection window and VHREF timing generator */
+static void
+tda1997x_configure_vhref(struct v4l2_subdev *sd)
+{
+ struct tda1997x_state *state = to_state(sd);
+ const struct v4l2_bt_timings *bt = &state->timings.bt;
+ int width, lines;
+ u16 href_start, href_end;
+ u16 vref_f1_start, vref_f2_start;
+ u8 vref_f1_width, vref_f2_width;
+ u8 field_polarity;
+ u16 fieldref_f1_start, fieldref_f2_start;
+ u8 reg;
+
+ href_start = bt->hbackporch + bt->hsync + 1;
+ href_end = href_start + bt->width;
+ vref_f1_start = bt->height + bt->vbackporch + bt->vsync +
+ bt->il_vbackporch + bt->il_vsync +
+ bt->il_vfrontporch;
+ vref_f1_width = bt->vbackporch + bt->vsync + bt->vfrontporch;
+ vref_f2_start = 0;
+ vref_f2_width = 0;
+ fieldref_f1_start = 0;
+ fieldref_f2_start = 0;
+ if (bt->interlaced) {
+ vref_f2_start = (bt->height / 2) +
+ (bt->il_vbackporch + bt->il_vsync - 1);
+ vref_f2_width = bt->il_vbackporch + bt->il_vsync +
+ bt->il_vfrontporch;
+ fieldref_f2_start = vref_f2_start + bt->il_vfrontporch +
+ fieldref_f1_start;
+ }
+ field_polarity = 0;
+
+ width = V4L2_DV_BT_FRAME_WIDTH(bt);
+ lines = V4L2_DV_BT_FRAME_HEIGHT(bt);
+
+ /*
+ * Configure Frame Detection Window:
+ * horiz area where the VHREF module consider a VSYNC a new frame
+ */
+ io_write16(sd, REG_FDW_S, 0x2ef); /* start position */
+ io_write16(sd, REG_FDW_E, 0x141); /* end position */
+
+ /* Set Pixel And Line Counters */
+ if (state->chip_revision == 0)
+ io_write16(sd, REG_PXCNT_PR, 4);
+ else
+ io_write16(sd, REG_PXCNT_PR, 1);
+ io_write16(sd, REG_PXCNT_NPIX, width & MASK_VHREF);
+ io_write16(sd, REG_LCNT_PR, 1);
+ io_write16(sd, REG_LCNT_NLIN, lines & MASK_VHREF);
+
+ /*
+ * Configure the VHRef timing generator responsible for rebuilding all
+ * horiz and vert synch and ref signals from its input allowing auto
+ * detection algorithms and forcing predefined modes (480i & 576i)
+ */
+ reg = VHREF_STD_DET_OFF << VHREF_STD_DET_SHIFT;
+ io_write(sd, REG_VHREF_CTRL, reg);
+
+ /*
+ * Configure the VHRef timing values. In case the VHREF generator has
+ * been configured in manual mode, this will allow to manually set all
+ * horiz and vert ref values (non-active pixel areas) of the generator
+ * and allows setting the frame reference params.
+ */
+ /* horizontal reference start/end */
+ io_write16(sd, REG_HREF_S, href_start & MASK_VHREF);
+ io_write16(sd, REG_HREF_E, href_end & MASK_VHREF);
+ /* vertical reference f1 start/end */
+ io_write16(sd, REG_VREF_F1_S, vref_f1_start & MASK_VHREF);
+ io_write(sd, REG_VREF_F1_WIDTH, vref_f1_width);
+ /* vertical reference f2 start/end */
+ io_write16(sd, REG_VREF_F2_S, vref_f2_start & MASK_VHREF);
+ io_write(sd, REG_VREF_F2_WIDTH, vref_f2_width);
+
+ /* F1/F2 FREF, field polarity */
+ reg = fieldref_f1_start & MASK_VHREF;
+ reg |= field_polarity << 8;
+ io_write16(sd, REG_FREF_F1_S, reg);
+ reg = fieldref_f2_start & MASK_VHREF;
+ io_write16(sd, REG_FREF_F2_S, reg);
+}
+
+/* Configure Video Output port signals */
+static int
+tda1997x_configure_vidout(struct tda1997x_state *state)
+{
+ struct v4l2_subdev *sd = &state->sd;
+ struct tda1997x_platform_data *pdata = &state->pdata;
+ u8 prefilter;
+ u8 reg;
+
+ /* Configure pixel clock generator: delay, polarity, rate */
+ reg = (state->vid_fmt == OF_FMT_422_CCIR) ?
+ PCLK_SEL_X2 : PCLK_SEL_X1;
+ reg |= pdata->vidout_delay_pclk << PCLK_DELAY_SHIFT;
+ reg |= pdata->vidout_inv_pclk << PCLK_INV_SHIFT;
+ io_write(sd, REG_PCLK, reg);
+
+ /* Configure pre-filter */
+ prefilter = 0; /* filters off */
+ /* YUV422 mode requires conversion */
+ if ((state->vid_fmt == OF_FMT_422_SMPT) ||
+ (state->vid_fmt == OF_FMT_422_CCIR)) {
+ /* 2/7 taps for Rv and Bu */
+ prefilter = FILTERS_CTRL_2_7TAP << FILTERS_CTRL_BU_SHIFT |
+ FILTERS_CTRL_2_7TAP << FILTERS_CTRL_RV_SHIFT;
+ }
+ io_write(sd, REG_FILTERS_CTRL, prefilter);
+
+ /* Configure video port */
+ reg = state->vid_fmt & OF_FMT_MASK;
+ if (state->vid_fmt == OF_FMT_422_CCIR)
+ reg |= (OF_BLK | OF_TRC);
+ reg |= OF_VP_ENABLE;
+ io_write(sd, REG_OF, reg);
+
+ /* Configure formatter and conversions */
+ reg = io_read(sd, REG_VDP_CTRL);
+ /* pre-filter is needed unless (REG_FILTERS_CTRL == 0) */
+ if (!prefilter)
+ reg |= VDP_CTRL_PREFILTER_BP;
+ else
+ reg &= ~VDP_CTRL_PREFILTER_BP;
+ /* formatter is needed for YUV422 and for trc/blc codes */
+ if (state->vid_fmt == OF_FMT_444)
+ reg |= VDP_CTRL_FORMATTER_BP;
+ /* formatter and compdel needed for timing/blanking codes */
+ else
+ reg &= ~(VDP_CTRL_FORMATTER_BP | VDP_CTRL_COMPDEL_BP);
+ /* activate compdel for small sync delays */
+ if ((pdata->vidout_delay_vs < 4) || (pdata->vidout_delay_hs < 4))
+ reg &= ~VDP_CTRL_COMPDEL_BP;
+ io_write(sd, REG_VDP_CTRL, reg);
+
+ /* Configure DE output signal: delay, polarity, and source */
+ reg = pdata->vidout_delay_de << DE_FREF_DELAY_SHIFT |
+ pdata->vidout_inv_de << DE_FREF_INV_SHIFT |
+ pdata->vidout_sel_de << DE_FREF_SEL_SHIFT;
+ io_write(sd, REG_DE_FREF, reg);
+
+ /* Configure HS/HREF output signal: delay, polarity, and source */
+ if (state->vid_fmt != OF_FMT_422_CCIR) {
+ reg = pdata->vidout_delay_hs << HS_HREF_DELAY_SHIFT |
+ pdata->vidout_inv_hs << HS_HREF_INV_SHIFT |
+ pdata->vidout_sel_hs << HS_HREF_SEL_SHIFT;
+ } else
+ reg = HS_HREF_SEL_NONE << HS_HREF_SEL_SHIFT;
+ io_write(sd, REG_HS_HREF, reg);
+
+ /* Configure VS/VREF output signal: delay, polarity, and source */
+ if (state->vid_fmt != OF_FMT_422_CCIR) {
+ reg = pdata->vidout_delay_vs << VS_VREF_DELAY_SHIFT |
+ pdata->vidout_inv_vs << VS_VREF_INV_SHIFT |
+ pdata->vidout_sel_vs << VS_VREF_SEL_SHIFT;
+ } else
+ reg = VS_VREF_SEL_NONE << VS_VREF_SEL_SHIFT;
+ io_write(sd, REG_VS_VREF, reg);
+
+ return 0;
+}
+
+/* Configure Audio output port signals */
+static int
+tda1997x_configure_audout(struct v4l2_subdev *sd, u8 channel_assignment)
+{
+ struct tda1997x_state *state = to_state(sd);
+ struct tda1997x_platform_data *pdata = &state->pdata;
+ bool sp_used_by_fifo = 1;
+ u8 reg;
+
+ if (!pdata->audout_format)
+ return 0;
+
+ /* channel assignment (CEA-861-D Table 20) */
+ io_write(sd, REG_AUDIO_PATH, channel_assignment);
+
+ /* Audio output configuration */
+ reg = 0;
+ switch (pdata->audout_format) {
+ case AUDFMT_TYPE_I2S:
+ reg |= AUDCFG_BUS_I2S << AUDCFG_BUS_SHIFT;
+ break;
+ case AUDFMT_TYPE_SPDIF:
+ reg |= AUDCFG_BUS_SPDIF << AUDCFG_BUS_SHIFT;
+ break;
+ }
+ switch (state->audio_type) {
+ case AUDCFG_TYPE_PCM:
+ reg |= AUDCFG_TYPE_PCM << AUDCFG_TYPE_SHIFT;
+ break;
+ case AUDCFG_TYPE_OBA:
+ reg |= AUDCFG_TYPE_OBA << AUDCFG_TYPE_SHIFT;
+ break;
+ case AUDCFG_TYPE_DST:
+ reg |= AUDCFG_TYPE_DST << AUDCFG_TYPE_SHIFT;
+ sp_used_by_fifo = 0;
+ break;
+ case AUDCFG_TYPE_HBR:
+ reg |= AUDCFG_TYPE_HBR << AUDCFG_TYPE_SHIFT;
+ if (pdata->audout_layout == 1) {
+ /* demuxed via AP0:AP3 */
+ reg |= AUDCFG_HBR_DEMUX << AUDCFG_HBR_SHIFT;
+ if (pdata->audout_format == AUDFMT_TYPE_SPDIF)
+ sp_used_by_fifo = 0;
+ } else {
+ /* straight via AP0 */
+ reg |= AUDCFG_HBR_STRAIGHT << AUDCFG_HBR_SHIFT;
+ }
+ break;
+ }
+ if (pdata->audout_width == 32)
+ reg |= AUDCFG_I2SW_32 << AUDCFG_I2SW_SHIFT;
+ else
+ reg |= AUDCFG_I2SW_16 << AUDCFG_I2SW_SHIFT;
+
+ /* automatic hardware mute */
+ if (pdata->audio_auto_mute)
+ reg |= AUDCFG_AUTO_MUTE_EN;
+ /* clock polarity */
+ if (pdata->audout_invert_clk)
+ reg |= AUDCFG_CLK_INVERT;
+ io_write(sd, REG_AUDCFG, reg);
+
+ /* audio layout */
+ reg = (pdata->audout_layout) ? AUDIO_LAYOUT_LAYOUT1 : 0;
+ if (!pdata->audout_layoutauto)
+ reg |= AUDIO_LAYOUT_MANUAL;
+ if (sp_used_by_fifo)
+ reg |= AUDIO_LAYOUT_SP_FLAG;
+ io_write(sd, REG_AUDIO_LAYOUT, reg);
+
+ /* FIFO Latency value */
+ io_write(sd, REG_FIFO_LATENCY_VAL, 0x80);
+
+ /* Audio output port config */
+ if (sp_used_by_fifo) {
+ reg = AUDIO_OUT_ENABLE_AP0;
+ if (channel_assignment >= 0x01)
+ reg |= AUDIO_OUT_ENABLE_AP1;
+ if (channel_assignment >= 0x04)
+ reg |= AUDIO_OUT_ENABLE_AP2;
+ if (channel_assignment >= 0x0c)
+ reg |= AUDIO_OUT_ENABLE_AP3;
+ /* specific cases where AP1 is not used */
+ if ((channel_assignment == 0x04)
+ || (channel_assignment == 0x08)
+ || (channel_assignment == 0x0c)
+ || (channel_assignment == 0x10)
+ || (channel_assignment == 0x14)
+ || (channel_assignment == 0x18)
+ || (channel_assignment == 0x1c))
+ reg &= ~AUDIO_OUT_ENABLE_AP1;
+ /* specific cases where AP2 is not used */
+ if ((channel_assignment >= 0x14)
+ && (channel_assignment <= 0x17))
+ reg &= ~AUDIO_OUT_ENABLE_AP2;
+ } else {
+ reg = AUDIO_OUT_ENABLE_AP3 |
+ AUDIO_OUT_ENABLE_AP2 |
+ AUDIO_OUT_ENABLE_AP1 |
+ AUDIO_OUT_ENABLE_AP0;
+ }
+ if (pdata->audout_format == AUDFMT_TYPE_I2S)
+ reg |= (AUDIO_OUT_ENABLE_ACLK | AUDIO_OUT_ENABLE_WS);
+ io_write(sd, REG_AUDIO_OUT_ENABLE, reg);
+
+ /* reset test mode to normal audio freq auto selection */
+ io_write(sd, REG_TEST_MODE, 0x00);
+
+ return 0;
+}
+
+/* Soft Reset of specific hdmi info */
+static int
+tda1997x_hdmi_info_reset(struct v4l2_subdev *sd, u8 info_rst, bool reset_sus)
+{
+ u8 reg;
+
+ /* reset infoframe engine packets */
+ reg = io_read(sd, REG_HDMI_INFO_RST);
+ io_write(sd, REG_HDMI_INFO_RST, info_rst);
+
+ /* if infoframe engine has been reset clear INT_FLG_MODE */
+ if (reg & RESET_IF) {
+ reg = io_read(sd, REG_INT_FLG_CLR_MODE);
+ io_write(sd, REG_INT_FLG_CLR_MODE, reg);
+ }
+
+ /* Disable REFTIM to restart start-up-sequencer (SUS) */
+ reg = io_read(sd, REG_RATE_CTRL);
+ reg &= ~RATE_REFTIM_ENABLE;
+ if (!reset_sus)
+ reg |= RATE_REFTIM_ENABLE;
+ reg = io_write(sd, REG_RATE_CTRL, reg);
+
+ return 0;
+}
+
+static void
+tda1997x_power_mode(struct tda1997x_state *state, bool enable)
+{
+ struct v4l2_subdev *sd = &state->sd;
+ u8 reg;
+
+ if (enable) {
+ /* Automatic control of TMDS */
+ io_write(sd, REG_PON_OVR_EN, PON_DIS);
+ /* Enable current bias unit */
+ io_write(sd, REG_CFG1, PON_EN);
+ /* Enable deep color PLL */
+ io_write(sd, REG_DEEP_PLL7_BYP, PON_DIS);
+ /* Output buffers active */
+ reg = io_read(sd, REG_OF);
+ reg &= ~OF_VP_ENABLE;
+ io_write(sd, REG_OF, reg);
+ } else {
+ /* Power down EDID mode sequence */
+ /* Output buffers in HiZ */
+ reg = io_read(sd, REG_OF);
+ reg |= OF_VP_ENABLE;
+ io_write(sd, REG_OF, reg);
+ /* Disable deep color PLL */
+ io_write(sd, REG_DEEP_PLL7_BYP, PON_EN);
+ /* Disable current bias unit */
+ io_write(sd, REG_CFG1, PON_DIS);
+ /* Manual control of TMDS */
+ io_write(sd, REG_PON_OVR_EN, PON_EN);
+ }
+}
+
+static bool
+tda1997x_detect_tx_5v(struct v4l2_subdev *sd)
+{
+ u8 reg = io_read(sd, REG_DETECT_5V);
+
+ return ((reg & DETECT_5V_SEL) ? 1 : 0);
+}
+
+static bool
+tda1997x_detect_tx_hpd(struct v4l2_subdev *sd)
+{
+ u8 reg = io_read(sd, REG_DETECT_5V);
+
+ return ((reg & DETECT_HPD) ? 1 : 0);
+}
+
+static int
+tda1997x_detect_std(struct tda1997x_state *state,
+ struct v4l2_dv_timings *timings)
+{
+ struct v4l2_subdev *sd = &state->sd;
+ u32 vper;
+ u16 hper;
+ u16 hsper;
+ int i;
+
+ /*
+ * Read the FMT registers
+ * REG_V_PER: Period of a frame (or two fields) in MCLK(27MHz) cycles
+ * REG_H_PER: Period of a line in MCLK(27MHz) cycles
+ * REG_HS_WIDTH: Period of horiz sync pulse in MCLK(27MHz) cycles
+ */
+ vper = io_read24(sd, REG_V_PER) & MASK_VPER;
+ hper = io_read16(sd, REG_H_PER) & MASK_HPER;
+ hsper = io_read16(sd, REG_HS_WIDTH) & MASK_HSWIDTH;
+ v4l2_dbg(1, debug, sd, "Signal Timings: %u/%u/%u\n", vper, hper, hsper);
+ if (!vper || !hper || !hsper)
+ return -ENOLINK;
+
+ for (i = 0; v4l2_dv_timings_presets[i].bt.width; i++) {
+ const struct v4l2_bt_timings *bt;
+ u32 lines, width, _hper, _hsper;
+ u32 vmin, vmax, hmin, hmax, hsmin, hsmax;
+ bool vmatch, hmatch, hsmatch;
+
+ bt = &v4l2_dv_timings_presets[i].bt;
+ width = V4L2_DV_BT_FRAME_WIDTH(bt);
+ lines = V4L2_DV_BT_FRAME_HEIGHT(bt);
+ _hper = (u32)bt->pixelclock / width;
+ if (bt->interlaced)
+ lines /= 2;
+ /* vper +/- 0.7% */
+ vmin = ((27000000 / 1000) * 993) / _hper * lines;
+ vmax = ((27000000 / 1000) * 1007) / _hper * lines;
+ /* hper +/- 1.0% */
+ hmin = ((27000000 / 100) * 99) / _hper;
+ hmax = ((27000000 / 100) * 101) / _hper;
+ /* hsper +/- 2 (take care to avoid 32bit overflow) */
+ _hsper = 27000 * bt->hsync / ((u32)bt->pixelclock/1000);
+ hsmin = _hsper - 2;
+ hsmax = _hsper + 2;
+
+ /* vmatch matches the framerate */
+ vmatch = ((vper <= vmax) && (vper >= vmin)) ? 1 : 0;
+ /* hmatch matches the width */
+ hmatch = ((hper <= hmax) && (hper >= hmin)) ? 1 : 0;
+ /* hsmatch matches the hswidth */
+ hsmatch = ((hsper <= hsmax) && (hsper >= hsmin)) ? 1 : 0;
+ if (hmatch && vmatch && hsmatch) {
+ v4l2_print_dv_timings(sd->name, "Detected format: ",
+ &v4l2_dv_timings_presets[i],
+ false);
+ if (timings)
+ *timings = v4l2_dv_timings_presets[i];
+ return 0;
+ }
+ }
+
+ v4l_err(state->client, "no resolution match for timings: %d/%d/%d\n",
+ vper, hper, hsper);
+ return -ERANGE;
+}
+
+/* some sort of errata workaround for chip revision 0 (N1) */
+static void tda1997x_reset_n1(struct tda1997x_state *state)
+{
+ struct v4l2_subdev *sd = &state->sd;
+ u8 reg;
+
+ /* clear HDMI mode flag in BCAPS */
+ io_write(sd, REG_CLK_CFG, CLK_CFG_SEL_ACLK_EN | CLK_CFG_SEL_ACLK);
+ io_write(sd, REG_PON_OVR_EN, PON_EN);
+ io_write(sd, REG_PON_CBIAS, PON_EN);
+ io_write(sd, REG_PON_PLL, PON_EN);
+
+ reg = io_read(sd, REG_MODE_REC_CFG1);
+ reg &= ~0x06;
+ reg |= 0x02;
+ io_write(sd, REG_MODE_REC_CFG1, reg);
+ io_write(sd, REG_CLK_CFG, CLK_CFG_DIS);
+ io_write(sd, REG_PON_OVR_EN, PON_DIS);
+ reg = io_read(sd, REG_MODE_REC_CFG1);
+ reg &= ~0x06;
+ io_write(sd, REG_MODE_REC_CFG1, reg);
+}
+
+/*
+ * Activity detection must only be notified when stable_clk_x AND active_x
+ * bits are set to 1. If only stable_clk_x bit is set to 1 but not
+ * active_x, it means that the TMDS clock is not in the defined range
+ * and activity detection must not be notified.
+ */
+static u8
+tda1997x_read_activity_status_regs(struct v4l2_subdev *sd)
+{
+ u8 reg, status = 0;
+
+ /* Read CLK_A_STATUS register */
+ reg = io_read(sd, REG_CLK_A_STATUS);
+ /* ignore if not active */
+ if ((reg & MASK_CLK_STABLE) && !(reg & MASK_CLK_ACTIVE))
+ reg &= ~MASK_CLK_STABLE;
+ status |= ((reg & MASK_CLK_STABLE) >> 2);
+
+ /* Read CLK_B_STATUS register */
+ reg = io_read(sd, REG_CLK_B_STATUS);
+ /* ignore if not active */
+ if ((reg & MASK_CLK_STABLE) && !(reg & MASK_CLK_ACTIVE))
+ reg &= ~MASK_CLK_STABLE;
+ status |= ((reg & MASK_CLK_STABLE) >> 1);
+
+ /* Read the SUS_STATUS register */
+ reg = io_read(sd, REG_SUS_STATUS);
+
+ /* If state = 5 => TMDS is locked */
+ if ((reg & MASK_SUS_STATUS) == LAST_STATE_REACHED)
+ status |= MASK_SUS_STATE;
+ else
+ status &= ~MASK_SUS_STATE;
+
+ return status;
+}
+
+static void
+set_rgb_quantization_range(struct tda1997x_state *state)
+{
+ struct v4l2_hdmi_colorimetry *c = &state->colorimetry;
+
+ state->colorimetry = v4l2_hdmi_rx_colorimetry(&state->avi_infoframe,
+ NULL,
+ state->timings.bt.height);
+ /* If ycbcr_enc is V4L2_YCBCR_ENC_DEFAULT, we receive RGB */
+ if (c->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) {
+ switch (state->rgb_quantization_range) {
+ case V4L2_DV_RGB_RANGE_LIMITED:
+ c->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ break;
+ case V4L2_DV_RGB_RANGE_FULL:
+ c->quantization = V4L2_QUANTIZATION_LIM_RANGE;
+ break;
+ }
+ }
+ v4l_dbg(1, debug, state->client,
+ "colorspace=%d/%d colorimetry=%d range=%s content=%d\n",
+ state->avi_infoframe.colorspace, c->colorspace,
+ state->avi_infoframe.colorimetry,
+ v4l2_quantization_names[c->quantization],
+ state->avi_infoframe.content_type);
+}
+
+/* parse an infoframe and do some sanity checks on it */
+static unsigned int
+tda1997x_parse_infoframe(struct tda1997x_state *state, u16 addr)
+{
+ struct v4l2_subdev *sd = &state->sd;
+ union hdmi_infoframe frame;
+ u8 buffer[40];
+ u8 reg;
+ int len, err;
+
+ /* read data */
+ len = io_readn(sd, addr, sizeof(buffer), buffer);
+ err = hdmi_infoframe_unpack(&frame, buffer);
+ if (err) {
+ v4l_err(state->client,
+ "failed parsing %d byte infoframe: 0x%04x/0x%02x\n",
+ len, addr, buffer[0]);
+ return err;
+ }
+ hdmi_infoframe_log(KERN_INFO, &state->client->dev, &frame);
+ switch (frame.any.type) {
+ /* Audio InfoFrame: see HDMI spec 8.2.2 */
+ case HDMI_INFOFRAME_TYPE_AUDIO:
+ /* sample rate */
+ switch (frame.audio.sample_frequency) {
+ case HDMI_AUDIO_SAMPLE_FREQUENCY_32000:
+ state->audio_samplerate = 32000;
+ break;
+ case HDMI_AUDIO_SAMPLE_FREQUENCY_44100:
+ state->audio_samplerate = 44100;
+ break;
+ case HDMI_AUDIO_SAMPLE_FREQUENCY_48000:
+ state->audio_samplerate = 48000;
+ break;
+ case HDMI_AUDIO_SAMPLE_FREQUENCY_88200:
+ state->audio_samplerate = 88200;
+ break;
+ case HDMI_AUDIO_SAMPLE_FREQUENCY_96000:
+ state->audio_samplerate = 96000;
+ break;
+ case HDMI_AUDIO_SAMPLE_FREQUENCY_176400:
+ state->audio_samplerate = 176400;
+ break;
+ case HDMI_AUDIO_SAMPLE_FREQUENCY_192000:
+ state->audio_samplerate = 192000;
+ break;
+ default:
+ case HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM:
+ break;
+ }
+
+ /* sample size */
+ switch (frame.audio.sample_size) {
+ case HDMI_AUDIO_SAMPLE_SIZE_16:
+ state->audio_samplesize = 16;
+ break;
+ case HDMI_AUDIO_SAMPLE_SIZE_20:
+ state->audio_samplesize = 20;
+ break;
+ case HDMI_AUDIO_SAMPLE_SIZE_24:
+ state->audio_samplesize = 24;
+ break;
+ case HDMI_AUDIO_SAMPLE_SIZE_STREAM:
+ default:
+ break;
+ }
+
+ /* Channel Count */
+ state->audio_channels = frame.audio.channels;
+ if (frame.audio.channel_allocation &&
+ frame.audio.channel_allocation != state->audio_ch_alloc) {
+ /* use the channel assignment from the infoframe */
+ state->audio_ch_alloc = frame.audio.channel_allocation;
+ tda1997x_configure_audout(sd, state->audio_ch_alloc);
+ /* reset the audio FIFO */
+ tda1997x_hdmi_info_reset(sd, RESET_AUDIO, false);
+ }
+ break;
+
+ /* Auxiliary Video information (AVI) InfoFrame: see HDMI spec 8.2.1 */
+ case HDMI_INFOFRAME_TYPE_AVI:
+ state->avi_infoframe = frame.avi;
+ set_rgb_quantization_range(state);
+
+ /* configure upsampler: 0=bypass 1=repeatchroma 2=interpolate */
+ reg = io_read(sd, REG_PIX_REPEAT);
+ reg &= ~PIX_REPEAT_MASK_UP_SEL;
+ if (frame.avi.colorspace == HDMI_COLORSPACE_YUV422)
+ reg |= (PIX_REPEAT_CHROMA << PIX_REPEAT_SHIFT);
+ io_write(sd, REG_PIX_REPEAT, reg);
+
+ /* ConfigurePixelRepeater: repeat n-times each pixel */
+ reg = io_read(sd, REG_PIX_REPEAT);
+ reg &= ~PIX_REPEAT_MASK_REP;
+ reg |= frame.avi.pixel_repeat;
+ io_write(sd, REG_PIX_REPEAT, reg);
+
+ /* configure the receiver with the new colorspace */
+ tda1997x_configure_csc(sd);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static void tda1997x_irq_sus(struct tda1997x_state *state, u8 *flags)
+{
+ struct v4l2_subdev *sd = &state->sd;
+ u8 reg, source;
+
+ source = io_read(sd, REG_INT_FLG_CLR_SUS);
+ io_write(sd, REG_INT_FLG_CLR_SUS, source);
+
+ if (source & MASK_MPT) {
+ /* reset MTP in use flag if set */
+ if (state->mptrw_in_progress)
+ state->mptrw_in_progress = 0;
+ }
+
+ if (source & MASK_SUS_END) {
+ /* reset audio FIFO */
+ reg = io_read(sd, REG_HDMI_INFO_RST);
+ reg |= MASK_SR_FIFO_FIFO_CTRL;
+ io_write(sd, REG_HDMI_INFO_RST, reg);
+ reg &= ~MASK_SR_FIFO_FIFO_CTRL;
+ io_write(sd, REG_HDMI_INFO_RST, reg);
+
+ /* reset HDMI flags */
+ state->hdmi_status = 0;
+ }
+
+ /* filter FMT interrupt based on SUS state */
+ reg = io_read(sd, REG_SUS_STATUS);
+ if (((reg & MASK_SUS_STATUS) != LAST_STATE_REACHED)
+ || (source & MASK_MPT)) {
+ source &= ~MASK_FMT;
+ }
+
+ if (source & (MASK_FMT | MASK_SUS_END)) {
+ reg = io_read(sd, REG_SUS_STATUS);
+ if ((reg & MASK_SUS_STATUS) != LAST_STATE_REACHED) {
+ v4l_err(state->client, "BAD SUS STATUS\n");
+ return;
+ }
+ if (debug)
+ tda1997x_detect_std(state, NULL);
+ /* notify user of change in resolution */
+ v4l2_subdev_notify_event(&state->sd, &tda1997x_ev_fmt);
+ }
+}
+
+static void tda1997x_irq_ddc(struct tda1997x_state *state, u8 *flags)
+{
+ struct v4l2_subdev *sd = &state->sd;
+ u8 source;
+
+ source = io_read(sd, REG_INT_FLG_CLR_DDC);
+ io_write(sd, REG_INT_FLG_CLR_DDC, source);
+ if (source & MASK_EDID_MTP) {
+ /* reset MTP in use flag if set */
+ if (state->mptrw_in_progress)
+ state->mptrw_in_progress = 0;
+ }
+
+ /* Detection of +5V */
+ if (source & MASK_DET_5V) {
+ v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl,
+ tda1997x_detect_tx_5v(sd));
+ }
+}
+
+static void tda1997x_irq_rate(struct tda1997x_state *state, u8 *flags)
+{
+ struct v4l2_subdev *sd = &state->sd;
+ u8 reg, source;
+
+ u8 irq_status;
+
+ source = io_read(sd, REG_INT_FLG_CLR_RATE);
+ io_write(sd, REG_INT_FLG_CLR_RATE, source);
+
+ /* read status regs */
+ irq_status = tda1997x_read_activity_status_regs(sd);
+
+ /*
+ * read clock status reg until INT_FLG_CLR_RATE is still 0
+ * after the read to make sure its the last one
+ */
+ reg = source;
+ while (reg != 0) {
+ irq_status = tda1997x_read_activity_status_regs(sd);
+ reg = io_read(sd, REG_INT_FLG_CLR_RATE);
+ io_write(sd, REG_INT_FLG_CLR_RATE, reg);
+ source |= reg;
+ }
+
+ /* we only pay attention to stability change events */
+ if (source & (MASK_RATE_A_ST | MASK_RATE_B_ST)) {
+ int input = (source & MASK_RATE_A_ST)?0:1;
+ u8 mask = 1<<input;
+
+ /* state change */
+ if ((irq_status & mask) != (state->activity_status & mask)) {
+ /* activity lost */
+ if ((irq_status & mask) == 0) {
+ v4l_info(state->client,
+ "HDMI-%c: Digital Activity Lost\n",
+ input+'A');
+
+ /* bypass up/down sampler and pixel repeater */
+ reg = io_read(sd, REG_PIX_REPEAT);
+ reg &= ~PIX_REPEAT_MASK_UP_SEL;
+ reg &= ~PIX_REPEAT_MASK_REP;
+ io_write(sd, REG_PIX_REPEAT, reg);
+
+ if (state->chip_revision == 0)
+ tda1997x_reset_n1(state);
+
+ state->input_detect[input] = 0;
+ v4l2_subdev_notify_event(sd, &tda1997x_ev_fmt);
+ }
+
+ /* activity detected */
+ else {
+ v4l_info(state->client,
+ "HDMI-%c: Digital Activity Detected\n",
+ input+'A');
+ state->input_detect[input] = 1;
+ }
+
+ /* hold onto current state */
+ state->activity_status = (irq_status & mask);
+ }
+ }
+}
+
+static void tda1997x_irq_info(struct tda1997x_state *state, u8 *flags)
+{
+ struct v4l2_subdev *sd = &state->sd;
+ u8 source;
+
+ source = io_read(sd, REG_INT_FLG_CLR_INFO);
+ io_write(sd, REG_INT_FLG_CLR_INFO, source);
+
+ /* Audio infoframe */
+ if (source & MASK_AUD_IF) {
+ tda1997x_parse_infoframe(state, AUD_IF);
+ source &= ~MASK_AUD_IF;
+ }
+
+ /* Source Product Descriptor infoframe change */
+ if (source & MASK_SPD_IF) {
+ tda1997x_parse_infoframe(state, SPD_IF);
+ source &= ~MASK_SPD_IF;
+ }
+
+ /* Auxiliary Video Information infoframe */
+ if (source & MASK_AVI_IF) {
+ tda1997x_parse_infoframe(state, AVI_IF);
+ source &= ~MASK_AVI_IF;
+ }
+}
+
+static void tda1997x_irq_audio(struct tda1997x_state *state, u8 *flags)
+{
+ struct v4l2_subdev *sd = &state->sd;
+ u8 reg, source;
+
+ source = io_read(sd, REG_INT_FLG_CLR_AUDIO);
+ io_write(sd, REG_INT_FLG_CLR_AUDIO, source);
+
+ /* reset audio FIFO on FIFO pointer error or audio mute */
+ if (source & MASK_ERROR_FIFO_PT ||
+ source & MASK_MUTE_FLG) {
+ /* audio reset audio FIFO */
+ reg = io_read(sd, REG_SUS_STATUS);
+ if ((reg & MASK_SUS_STATUS) == LAST_STATE_REACHED) {
+ reg = io_read(sd, REG_HDMI_INFO_RST);
+ reg |= MASK_SR_FIFO_FIFO_CTRL;
+ io_write(sd, REG_HDMI_INFO_RST, reg);
+ reg &= ~MASK_SR_FIFO_FIFO_CTRL;
+ io_write(sd, REG_HDMI_INFO_RST, reg);
+ /* reset channel status IT if present */
+ source &= ~(MASK_CH_STATE);
+ }
+ }
+ if (source & MASK_AUDIO_FREQ_FLG) {
+ static const int freq[] = {
+ 0, 32000, 44100, 48000, 88200, 96000, 176400, 192000
+ };
+
+ reg = io_read(sd, REG_AUDIO_FREQ);
+ state->audio_samplerate = freq[reg & 7];
+ v4l_info(state->client, "Audio Frequency Change: %dHz\n",
+ state->audio_samplerate);
+ }
+ if (source & MASK_AUDIO_FLG) {
+ reg = io_read(sd, REG_AUDIO_FLAGS);
+ if (reg & BIT(AUDCFG_TYPE_DST))
+ state->audio_type = AUDCFG_TYPE_DST;
+ if (reg & BIT(AUDCFG_TYPE_OBA))
+ state->audio_type = AUDCFG_TYPE_OBA;
+ if (reg & BIT(AUDCFG_TYPE_HBR))
+ state->audio_type = AUDCFG_TYPE_HBR;
+ if (reg & BIT(AUDCFG_TYPE_PCM))
+ state->audio_type = AUDCFG_TYPE_PCM;
+ v4l_info(state->client, "Audio Type: %s\n",
+ audtype_names[state->audio_type]);
+ }
+}
+
+static void tda1997x_irq_hdcp(struct tda1997x_state *state, u8 *flags)
+{
+ struct v4l2_subdev *sd = &state->sd;
+ u8 reg, source;
+
+ source = io_read(sd, REG_INT_FLG_CLR_HDCP);
+ io_write(sd, REG_INT_FLG_CLR_HDCP, source);
+
+ /* reset MTP in use flag if set */
+ if (source & MASK_HDCP_MTP)
+ state->mptrw_in_progress = 0;
+ if (source & MASK_STATE_C5) {
+ /* REPEATER: mask AUDIO and IF irqs to avoid IF during auth */
+ reg = io_read(sd, REG_INT_MASK_TOP);
+ reg &= ~(INTERRUPT_AUDIO | INTERRUPT_INFO);
+ io_write(sd, REG_INT_MASK_TOP, reg);
+ *flags &= (INTERRUPT_AUDIO | INTERRUPT_INFO);
+ }
+}
+
+static irqreturn_t tda1997x_isr_thread(int irq, void *d)
+{
+ struct tda1997x_state *state = d;
+ struct v4l2_subdev *sd = &state->sd;
+ u8 flags;
+
+ mutex_lock(&state->lock);
+ do {
+ /* read interrupt flags */
+ flags = io_read(sd, REG_INT_FLG_CLR_TOP);
+ if (flags == 0)
+ break;
+
+ /* SUS interrupt source (Input activity events) */
+ if (flags & INTERRUPT_SUS)
+ tda1997x_irq_sus(state, &flags);
+ /* DDC interrupt source (Display Data Channel) */
+ else if (flags & INTERRUPT_DDC)
+ tda1997x_irq_ddc(state, &flags);
+ /* RATE interrupt source (Digital Input activity) */
+ else if (flags & INTERRUPT_RATE)
+ tda1997x_irq_rate(state, &flags);
+ /* Infoframe change interrupt */
+ else if (flags & INTERRUPT_INFO)
+ tda1997x_irq_info(state, &flags);
+ /* Audio interrupt source:
+ * freq change, DST,OBA,HBR,ASP flags, mute, FIFO err
+ */
+ else if (flags & INTERRUPT_AUDIO)
+ tda1997x_irq_audio(state, &flags);
+ /* HDCP interrupt source (content protection) */
+ if (flags & INTERRUPT_HDCP)
+ tda1997x_irq_hdcp(state, &flags);
+ } while (flags != 0);
+ mutex_unlock(&state->lock);
+
+ return IRQ_HANDLED;
+}
+
+/* -----------------------------------------------------------------------------
+ * v4l2_subdev_video_ops
+ */
+
+static int
+tda1997x_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+ struct tda1997x_state *state = to_state(sd);
+ u32 vper;
+ u16 hper;
+ u16 hsper;
+
+ mutex_lock(&state->lock);
+ vper = io_read24(sd, REG_V_PER) & MASK_VPER;
+ hper = io_read16(sd, REG_H_PER) & MASK_HPER;
+ hsper = io_read16(sd, REG_HS_WIDTH) & MASK_HSWIDTH;
+ /*
+ * The tda1997x supports A/B inputs but only a single output.
+ * The irq handler monitors for timing changes on both inputs and
+ * sets the input_detect array to 0|1 depending on signal presence.
+ * I believe selection of A vs B is automatic.
+ *
+ * The vper/hper/hsper registers provide the frame period, line period
+ * and horiz sync period (units of MCLK clock cycles (27MHz)) and
+ * testing shows these values to be random if no signal is present
+ * or locked.
+ */
+ v4l2_dbg(1, debug, sd, "inputs:%d/%d timings:%d/%d/%d\n",
+ state->input_detect[0], state->input_detect[1],
+ vper, hper, hsper);
+ if (!state->input_detect[0] && !state->input_detect[1])
+ *status = V4L2_IN_ST_NO_SIGNAL;
+ else if (!vper || !hper || !hsper)
+ *status = V4L2_IN_ST_NO_SYNC;
+ else
+ *status = 0;
+ mutex_unlock(&state->lock);
+
+ return 0;
+};
+
+static int tda1997x_s_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings *timings)
+{
+ struct tda1997x_state *state = to_state(sd);
+
+ v4l_dbg(1, debug, state->client, "%s\n", __func__);
+
+ if (v4l2_match_dv_timings(&state->timings, timings, 0, false))
+ return 0; /* no changes */
+
+ if (!v4l2_valid_dv_timings(timings, &tda1997x_dv_timings_cap,
+ NULL, NULL))
+ return -ERANGE;
+
+ mutex_lock(&state->lock);
+ state->timings = *timings;
+ /* setup frame detection window and VHREF timing generator */
+ tda1997x_configure_vhref(sd);
+ /* configure colorspace conversion */
+ tda1997x_configure_csc(sd);
+ mutex_unlock(&state->lock);
+
+ return 0;
+}
+
+static int tda1997x_g_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings *timings)
+{
+ struct tda1997x_state *state = to_state(sd);
+
+ v4l_dbg(1, debug, state->client, "%s\n", __func__);
+ mutex_lock(&state->lock);
+ *timings = state->timings;
+ mutex_unlock(&state->lock);
+
+ return 0;
+}
+
+static int tda1997x_query_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings *timings)
+{
+ struct tda1997x_state *state = to_state(sd);
+
+ v4l_dbg(1, debug, state->client, "%s\n", __func__);
+ memset(timings, 0, sizeof(struct v4l2_dv_timings));
+ mutex_lock(&state->lock);
+ tda1997x_detect_std(state, timings);
+ mutex_unlock(&state->lock);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops tda1997x_video_ops = {
+ .g_input_status = tda1997x_g_input_status,
+ .s_dv_timings = tda1997x_s_dv_timings,
+ .g_dv_timings = tda1997x_g_dv_timings,
+ .query_dv_timings = tda1997x_query_dv_timings,
+};
+
+
+/* -----------------------------------------------------------------------------
+ * v4l2_subdev_pad_ops
+ */
+
+static int tda1997x_init_cfg(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg)
+{
+ struct tda1997x_state *state = to_state(sd);
+ struct v4l2_mbus_framefmt *mf;
+
+ mf = v4l2_subdev_get_try_format(sd, cfg, 0);
+ mf->code = state->mbus_codes[0];
+
+ return 0;
+}
+
+static int tda1997x_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct tda1997x_state *state = to_state(sd);
+
+ v4l_dbg(1, debug, state->client, "%s %d\n", __func__, code->index);
+ if (code->index >= ARRAY_SIZE(state->mbus_codes))
+ return -EINVAL;
+
+ if (!state->mbus_codes[code->index])
+ return -EINVAL;
+
+ code->code = state->mbus_codes[code->index];
+
+ return 0;
+}
+
+static void tda1997x_fill_format(struct tda1997x_state *state,
+ struct v4l2_mbus_framefmt *format)
+{
+ const struct v4l2_bt_timings *bt;
+
+ memset(format, 0, sizeof(*format));
+ bt = &state->timings.bt;
+ format->width = bt->width;
+ format->height = bt->height;
+ format->colorspace = state->colorimetry.colorspace;
+ format->field = (bt->interlaced) ?
+ V4L2_FIELD_SEQ_TB : V4L2_FIELD_NONE;
+}
+
+static int tda1997x_get_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct tda1997x_state *state = to_state(sd);
+
+ v4l_dbg(1, debug, state->client, "%s pad=%d which=%d\n",
+ __func__, format->pad, format->which);
+
+ tda1997x_fill_format(state, &format->format);
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+ struct v4l2_mbus_framefmt *fmt;
+
+ fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad);
+ format->format.code = fmt->code;
+ } else
+ format->format.code = state->mbus_code;
+
+ return 0;
+}
+
+static int tda1997x_set_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct tda1997x_state *state = to_state(sd);
+ u32 code = 0;
+ int i;
+
+ v4l_dbg(1, debug, state->client, "%s pad=%d which=%d fmt=0x%x\n",
+ __func__, format->pad, format->which, format->format.code);
+
+ for (i = 0; i < ARRAY_SIZE(state->mbus_codes); i++) {
+ if (format->format.code == state->mbus_codes[i]) {
+ code = state->mbus_codes[i];
+ break;
+ }
+ }
+ if (!code)
+ code = state->mbus_codes[0];
+
+ tda1997x_fill_format(state, &format->format);
+ format->format.code = code;
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+ struct v4l2_mbus_framefmt *fmt;
+
+ fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad);
+ *fmt = format->format;
+ } else {
+ int ret = tda1997x_setup_format(state, format->format.code);
+
+ if (ret)
+ return ret;
+ /* mbus_code has changed - re-configure csc/vidout */
+ tda1997x_configure_csc(sd);
+ tda1997x_configure_vidout(state);
+ }
+
+ return 0;
+}
+
+static int tda1997x_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
+{
+ struct tda1997x_state *state = to_state(sd);
+
+ v4l_dbg(1, debug, state->client, "%s pad=%d\n", __func__, edid->pad);
+ memset(edid->reserved, 0, sizeof(edid->reserved));
+
+ if (edid->start_block == 0 && edid->blocks == 0) {
+ edid->blocks = state->edid.blocks;
+ return 0;
+ }
+
+ if (!state->edid.present)
+ return -ENODATA;
+
+ if (edid->start_block >= state->edid.blocks)
+ return -EINVAL;
+
+ if (edid->start_block + edid->blocks > state->edid.blocks)
+ edid->blocks = state->edid.blocks - edid->start_block;
+
+ memcpy(edid->edid, state->edid.edid + edid->start_block * 128,
+ edid->blocks * 128);
+
+ return 0;
+}
+
+static int tda1997x_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
+{
+ struct tda1997x_state *state = to_state(sd);
+ int i;
+
+ v4l_dbg(1, debug, state->client, "%s pad=%d\n", __func__, edid->pad);
+ memset(edid->reserved, 0, sizeof(edid->reserved));
+
+ if (edid->start_block != 0)
+ return -EINVAL;
+
+ if (edid->blocks == 0) {
+ state->edid.blocks = 0;
+ state->edid.present = 0;
+ tda1997x_disable_edid(sd);
+ return 0;
+ }
+
+ if (edid->blocks > 2) {
+ edid->blocks = 2;
+ return -E2BIG;
+ }
+
+ tda1997x_disable_edid(sd);
+
+ /* write base EDID */
+ for (i = 0; i < 128; i++)
+ io_write(sd, REG_EDID_IN_BYTE0 + i, edid->edid[i]);
+
+ /* write CEA Extension */
+ for (i = 0; i < 128; i++)
+ io_write(sd, REG_EDID_IN_BYTE128 + i, edid->edid[i+128]);
+
+ tda1997x_enable_edid(sd);
+
+ return 0;
+}
+
+static int tda1997x_get_dv_timings_cap(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings_cap *cap)
+{
+ *cap = tda1997x_dv_timings_cap;
+ return 0;
+}
+
+static int tda1997x_enum_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_enum_dv_timings *timings)
+{
+ return v4l2_enum_dv_timings_cap(timings, &tda1997x_dv_timings_cap,
+ NULL, NULL);
+}
+
+static const struct v4l2_subdev_pad_ops tda1997x_pad_ops = {
+ .init_cfg = tda1997x_init_cfg,
+ .enum_mbus_code = tda1997x_enum_mbus_code,
+ .get_fmt = tda1997x_get_format,
+ .set_fmt = tda1997x_set_format,
+ .get_edid = tda1997x_get_edid,
+ .set_edid = tda1997x_set_edid,
+ .dv_timings_cap = tda1997x_get_dv_timings_cap,
+ .enum_dv_timings = tda1997x_enum_dv_timings,
+};
+
+/* -----------------------------------------------------------------------------
+ * v4l2_subdev_core_ops
+ */
+
+static int tda1997x_log_infoframe(struct v4l2_subdev *sd, int addr)
+{
+ struct tda1997x_state *state = to_state(sd);
+ union hdmi_infoframe frame;
+ u8 buffer[40];
+ int len, err;
+
+ /* read data */
+ len = io_readn(sd, addr, sizeof(buffer), buffer);
+ v4l2_dbg(1, debug, sd, "infoframe: addr=%d len=%d\n", addr, len);
+ err = hdmi_infoframe_unpack(&frame, buffer);
+ if (err) {
+ v4l_err(state->client,
+ "failed parsing %d byte infoframe: 0x%04x/0x%02x\n",
+ len, addr, buffer[0]);
+ return err;
+ }
+ hdmi_infoframe_log(KERN_INFO, &state->client->dev, &frame);
+
+ return 0;
+}
+
+static int tda1997x_log_status(struct v4l2_subdev *sd)
+{
+ struct tda1997x_state *state = to_state(sd);
+ struct v4l2_dv_timings timings;
+ struct hdmi_avi_infoframe *avi = &state->avi_infoframe;
+
+ v4l2_info(sd, "-----Chip status-----\n");
+ v4l2_info(sd, "Chip: %s N%d\n", state->info->name,
+ state->chip_revision + 1);
+ v4l2_info(sd, "EDID Enabled: %s\n", state->edid.present ? "yes" : "no");
+
+ v4l2_info(sd, "-----Signal status-----\n");
+ v4l2_info(sd, "Cable detected (+5V power): %s\n",
+ tda1997x_detect_tx_5v(sd) ? "yes" : "no");
+ v4l2_info(sd, "HPD detected: %s\n",
+ tda1997x_detect_tx_hpd(sd) ? "yes" : "no");
+
+ v4l2_info(sd, "-----Video Timings-----\n");
+ switch (tda1997x_detect_std(state, &timings)) {
+ case -ENOLINK:
+ v4l2_info(sd, "No video detected\n");
+ break;
+ case -ERANGE:
+ v4l2_info(sd, "Invalid signal detected\n");
+ break;
+ }
+ v4l2_print_dv_timings(sd->name, "Configured format: ",
+ &state->timings, true);
+
+ v4l2_info(sd, "-----Color space-----\n");
+ v4l2_info(sd, "Input color space: %s %s %s",
+ hdmi_colorspace_names[avi->colorspace],
+ (avi->colorspace == HDMI_COLORSPACE_RGB) ? "" :
+ hdmi_colorimetry_names[avi->colorimetry],
+ v4l2_quantization_names[state->colorimetry.quantization]);
+ v4l2_info(sd, "Output color space: %s",
+ vidfmt_names[state->vid_fmt]);
+ v4l2_info(sd, "Color space conversion: %s", state->conv ?
+ state->conv->name : "None");
+
+ v4l2_info(sd, "-----Audio-----\n");
+ if (state->audio_channels) {
+ v4l2_info(sd, "audio: %dch %dHz\n", state->audio_channels,
+ state->audio_samplerate);
+ } else {
+ v4l2_info(sd, "audio: none\n");
+ }
+
+ v4l2_info(sd, "-----Infoframes-----\n");
+ tda1997x_log_infoframe(sd, AUD_IF);
+ tda1997x_log_infoframe(sd, SPD_IF);
+ tda1997x_log_infoframe(sd, AVI_IF);
+
+ return 0;
+}
+
+static int tda1997x_subscribe_event(struct v4l2_subdev *sd,
+ struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ switch (sub->type) {
+ case V4L2_EVENT_SOURCE_CHANGE:
+ return v4l2_src_change_event_subdev_subscribe(sd, fh, sub);
+ case V4L2_EVENT_CTRL:
+ return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct v4l2_subdev_core_ops tda1997x_core_ops = {
+ .log_status = tda1997x_log_status,
+ .subscribe_event = tda1997x_subscribe_event,
+ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+/* -----------------------------------------------------------------------------
+ * v4l2_subdev_ops
+ */
+
+static const struct v4l2_subdev_ops tda1997x_subdev_ops = {
+ .core = &tda1997x_core_ops,
+ .video = &tda1997x_video_ops,
+ .pad = &tda1997x_pad_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * v4l2_controls
+ */
+
+static int tda1997x_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ struct tda1997x_state *state = to_state(sd);
+
+ switch (ctrl->id) {
+ /* allow overriding the default RGB quantization range */
+ case V4L2_CID_DV_RX_RGB_RANGE:
+ state->rgb_quantization_range = ctrl->val;
+ set_rgb_quantization_range(state);
+ tda1997x_configure_csc(sd);
+ return 0;
+ }
+
+ return -EINVAL;
+};
+
+static int tda1997x_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ struct tda1997x_state *state = to_state(sd);
+
+ if (ctrl->id == V4L2_CID_DV_RX_IT_CONTENT_TYPE) {
+ ctrl->val = state->avi_infoframe.content_type;
+ return 0;
+ }
+ return -EINVAL;
+};
+
+static const struct v4l2_ctrl_ops tda1997x_ctrl_ops = {
+ .s_ctrl = tda1997x_s_ctrl,
+ .g_volatile_ctrl = tda1997x_g_volatile_ctrl,
+};
+
+static int tda1997x_core_init(struct v4l2_subdev *sd)
+{
+ struct tda1997x_state *state = to_state(sd);
+ struct tda1997x_platform_data *pdata = &state->pdata;
+ u8 reg;
+ int i;
+
+ /* disable HPD */
+ io_write(sd, REG_HPD_AUTO_CTRL, HPD_AUTO_HPD_UNSEL);
+ if (state->chip_revision == 0) {
+ io_write(sd, REG_MAN_SUS_HDMI_SEL, MAN_DIS_HDCP | MAN_RST_HDCP);
+ io_write(sd, REG_CGU_DBG_SEL, 1 << CGU_DBG_CLK_SEL_SHIFT);
+ }
+
+ /* reset infoframe at end of start-up-sequencer */
+ io_write(sd, REG_SUS_SET_RGB2, 0x06);
+ io_write(sd, REG_SUS_SET_RGB3, 0x06);
+
+ /* Enable TMDS pull-ups */
+ io_write(sd, REG_RT_MAN_CTRL, RT_MAN_CTRL_RT |
+ RT_MAN_CTRL_RT_B | RT_MAN_CTRL_RT_A);
+
+ /* enable sync measurement timing */
+ tda1997x_cec_write(sd, REG_PWR_CONTROL & 0xff, 0x04);
+ /* adjust CEC clock divider */
+ tda1997x_cec_write(sd, REG_OSC_DIVIDER & 0xff, 0x03);
+ tda1997x_cec_write(sd, REG_EN_OSC_PERIOD_LSB & 0xff, 0xa0);
+ io_write(sd, REG_TIMER_D, 0x54);
+ /* enable power switch */
+ reg = tda1997x_cec_read(sd, REG_CONTROL & 0xff);
+ reg |= 0x20;
+ tda1997x_cec_write(sd, REG_CONTROL & 0xff, reg);
+ mdelay(50);
+
+ /* read the chip version */
+ reg = io_read(sd, REG_VERSION);
+ /* get the chip configuration */
+ reg = io_read(sd, REG_CMTP_REG10);
+
+ /* enable interrupts we care about */
+ io_write(sd, REG_INT_MASK_TOP,
+ INTERRUPT_HDCP | INTERRUPT_AUDIO | INTERRUPT_INFO |
+ INTERRUPT_RATE | INTERRUPT_SUS);
+ /* config_mtp,fmt,sus_end,sus_st */
+ io_write(sd, REG_INT_MASK_SUS, MASK_MPT | MASK_FMT | MASK_SUS_END);
+ /* rate stability change for inputs A/B */
+ io_write(sd, REG_INT_MASK_RATE, MASK_RATE_B_ST | MASK_RATE_A_ST);
+ /* aud,spd,avi*/
+ io_write(sd, REG_INT_MASK_INFO,
+ MASK_AUD_IF | MASK_SPD_IF | MASK_AVI_IF);
+ /* audio_freq,audio_flg,mute_flg,fifo_err */
+ io_write(sd, REG_INT_MASK_AUDIO,
+ MASK_AUDIO_FREQ_FLG | MASK_AUDIO_FLG | MASK_MUTE_FLG |
+ MASK_ERROR_FIFO_PT);
+ /* HDCP C5 state reached */
+ io_write(sd, REG_INT_MASK_HDCP, MASK_STATE_C5);
+ /* 5V detect and HDP pulse end */
+ io_write(sd, REG_INT_MASK_DDC, MASK_DET_5V);
+ /* don't care about AFE/MODE */
+ io_write(sd, REG_INT_MASK_AFE, 0);
+ io_write(sd, REG_INT_MASK_MODE, 0);
+
+ /* clear all interrupts */
+ io_write(sd, REG_INT_FLG_CLR_TOP, 0xff);
+ io_write(sd, REG_INT_FLG_CLR_SUS, 0xff);
+ io_write(sd, REG_INT_FLG_CLR_DDC, 0xff);
+ io_write(sd, REG_INT_FLG_CLR_RATE, 0xff);
+ io_write(sd, REG_INT_FLG_CLR_MODE, 0xff);
+ io_write(sd, REG_INT_FLG_CLR_INFO, 0xff);
+ io_write(sd, REG_INT_FLG_CLR_AUDIO, 0xff);
+ io_write(sd, REG_INT_FLG_CLR_HDCP, 0xff);
+ io_write(sd, REG_INT_FLG_CLR_AFE, 0xff);
+
+ /* init TMDS equalizer */
+ if (state->chip_revision == 0)
+ io_write(sd, REG_CGU_DBG_SEL, 1 << CGU_DBG_CLK_SEL_SHIFT);
+ io_write24(sd, REG_CLK_MIN_RATE, CLK_MIN_RATE);
+ io_write24(sd, REG_CLK_MAX_RATE, CLK_MAX_RATE);
+ if (state->chip_revision == 0)
+ io_write(sd, REG_WDL_CFG, WDL_CFG_VAL);
+ /* DC filter */
+ io_write(sd, REG_DEEP_COLOR_CTRL, DC_FILTER_VAL);
+ /* disable test pattern */
+ io_write(sd, REG_SVC_MODE, 0x00);
+ /* update HDMI INFO CTRL */
+ io_write(sd, REG_INFO_CTRL, 0xff);
+ /* write HDMI INFO EXCEED value */
+ io_write(sd, REG_INFO_EXCEED, 3);
+
+ if (state->chip_revision == 0)
+ tda1997x_reset_n1(state);
+
+ /*
+ * No HDCP acknowledge when HDCP is disabled
+ * and reset SUS to force format detection
+ */
+ tda1997x_hdmi_info_reset(sd, NACK_HDCP, true);
+
+ /* Set HPD low */
+ tda1997x_manual_hpd(sd, HPD_LOW_BP);
+
+ /* Configure receiver capabilities */
+ io_write(sd, REG_HDCP_BCAPS, HDCP_HDMI | HDCP_FAST_REAUTH);
+
+ /* Configure HDMI: Auto HDCP mode, packet controlled mute */
+ reg = HDMI_CTRL_MUTE_AUTO << HDMI_CTRL_MUTE_SHIFT;
+ reg |= HDMI_CTRL_HDCP_AUTO << HDMI_CTRL_HDCP_SHIFT;
+ io_write(sd, REG_HDMI_CTRL, reg);
+
+ /* reset start-up-sequencer to force format detection */
+ tda1997x_hdmi_info_reset(sd, 0, true);
+
+ /* disable matrix conversion */
+ reg = io_read(sd, REG_VDP_CTRL);
+ reg |= VDP_CTRL_MATRIX_BP;
+ io_write(sd, REG_VDP_CTRL, reg);
+
+ /* set video output mode */
+ tda1997x_configure_vidout(state);
+
+ /* configure video output port */
+ for (i = 0; i < 9; i++) {
+ v4l_dbg(1, debug, state->client, "vidout_cfg[%d]=0x%02x\n", i,
+ pdata->vidout_port_cfg[i]);
+ io_write(sd, REG_VP35_32_CTRL + i, pdata->vidout_port_cfg[i]);
+ }
+
+ /* configure audio output port */
+ tda1997x_configure_audout(sd, 0);
+
+ /* configure audio clock freq */
+ switch (pdata->audout_mclk_fs) {
+ case 512:
+ reg = AUDIO_CLOCK_SEL_512FS;
+ break;
+ case 256:
+ reg = AUDIO_CLOCK_SEL_256FS;
+ break;
+ case 128:
+ reg = AUDIO_CLOCK_SEL_128FS;
+ break;
+ case 64:
+ reg = AUDIO_CLOCK_SEL_64FS;
+ break;
+ case 32:
+ reg = AUDIO_CLOCK_SEL_32FS;
+ break;
+ default:
+ reg = AUDIO_CLOCK_SEL_16FS;
+ break;
+ }
+ io_write(sd, REG_AUDIO_CLOCK, reg);
+
+ /* reset advanced infoframes (ISRC1/ISRC2/ACP) */
+ tda1997x_hdmi_info_reset(sd, RESET_AI, false);
+ /* reset infoframe */
+ tda1997x_hdmi_info_reset(sd, RESET_IF, false);
+ /* reset audio infoframes */
+ tda1997x_hdmi_info_reset(sd, RESET_AUDIO, false);
+ /* reset gamut */
+ tda1997x_hdmi_info_reset(sd, RESET_GAMUT, false);
+
+ /* get initial HDMI status */
+ state->hdmi_status = io_read(sd, REG_HDMI_FLAGS);
+
+ return 0;
+}
+
+static int tda1997x_set_power(struct tda1997x_state *state, bool on)
+{
+ int ret = 0;
+
+ if (on) {
+ ret = regulator_bulk_enable(TDA1997X_NUM_SUPPLIES,
+ state->supplies);
+ msleep(300);
+ } else {
+ ret = regulator_bulk_disable(TDA1997X_NUM_SUPPLIES,
+ state->supplies);
+ }
+
+ return ret;
+}
+
+static const struct i2c_device_id tda1997x_i2c_id[] = {
+ {"tda19971", (kernel_ulong_t)&tda1997x_chip_info[TDA19971]},
+ {"tda19973", (kernel_ulong_t)&tda1997x_chip_info[TDA19973]},
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, tda1997x_i2c_id);
+
+static const struct of_device_id tda1997x_of_id[] __maybe_unused = {
+ { .compatible = "nxp,tda19971", .data = &tda1997x_chip_info[TDA19971] },
+ { .compatible = "nxp,tda19973", .data = &tda1997x_chip_info[TDA19973] },
+ { },
+};
+MODULE_DEVICE_TABLE(of, tda1997x_of_id);
+
+static int tda1997x_parse_dt(struct tda1997x_state *state)
+{
+ struct tda1997x_platform_data *pdata = &state->pdata;
+ struct v4l2_fwnode_endpoint bus_cfg;
+ struct device_node *ep;
+ struct device_node *np;
+ unsigned int flags;
+ const char *str;
+ int ret;
+ u32 v;
+
+ /*
+ * setup default values:
+ * - HREF: active high from start to end of row
+ * - VS: Vertical Sync active high at beginning of frame
+ * - DE: Active high when data valid
+ * - A_CLK: 128*Fs
+ */
+ pdata->vidout_sel_hs = HS_HREF_SEL_HREF_VHREF;
+ pdata->vidout_sel_vs = VS_VREF_SEL_VREF_HDMI;
+ pdata->vidout_sel_de = DE_FREF_SEL_DE_VHREF;
+
+ np = state->client->dev.of_node;
+ ep = of_graph_get_next_endpoint(np, NULL);
+ if (!ep)
+ return -EINVAL;
+
+ ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &bus_cfg);
+ if (ret) {
+ of_node_put(ep);
+ return ret;
+ }
+ of_node_put(ep);
+ pdata->vidout_bus_type = bus_cfg.bus_type;
+
+ /* polarity of HS/VS/DE */
+ flags = bus_cfg.bus.parallel.flags;
+ if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ pdata->vidout_inv_hs = 1;
+ if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
+ pdata->vidout_inv_vs = 1;
+ if (flags & V4L2_MBUS_DATA_ACTIVE_LOW)
+ pdata->vidout_inv_de = 1;
+ pdata->vidout_bus_width = bus_cfg.bus.parallel.bus_width;
+
+ /* video output port config */
+ ret = of_property_count_u32_elems(np, "nxp,vidout-portcfg");
+ if (ret > 0) {
+ u32 reg, val, i;
+
+ for (i = 0; i < ret / 2 && i < 9; i++) {
+ of_property_read_u32_index(np, "nxp,vidout-portcfg",
+ i * 2, &reg);
+ of_property_read_u32_index(np, "nxp,vidout-portcfg",
+ i * 2 + 1, &val);
+ if (reg < 9)
+ pdata->vidout_port_cfg[reg] = val;
+ }
+ } else {
+ v4l_err(state->client, "nxp,vidout-portcfg missing\n");
+ return -EINVAL;
+ }
+
+ /* default to channel layout dictated by packet header */
+ pdata->audout_layoutauto = true;
+
+ pdata->audout_format = AUDFMT_TYPE_DISABLED;
+ if (!of_property_read_string(np, "nxp,audout-format", &str)) {
+ if (strcmp(str, "i2s") == 0)
+ pdata->audout_format = AUDFMT_TYPE_I2S;
+ else if (strcmp(str, "spdif") == 0)
+ pdata->audout_format = AUDFMT_TYPE_SPDIF;
+ else {
+ v4l_err(state->client, "nxp,audout-format invalid\n");
+ return -EINVAL;
+ }
+ if (!of_property_read_u32(np, "nxp,audout-layout", &v)) {
+ switch (v) {
+ case 0:
+ case 1:
+ break;
+ default:
+ v4l_err(state->client,
+ "nxp,audout-layout invalid\n");
+ return -EINVAL;
+ }
+ pdata->audout_layout = v;
+ }
+ if (!of_property_read_u32(np, "nxp,audout-width", &v)) {
+ switch (v) {
+ case 16:
+ case 32:
+ break;
+ default:
+ v4l_err(state->client,
+ "nxp,audout-width invalid\n");
+ return -EINVAL;
+ }
+ pdata->audout_width = v;
+ }
+ if (!of_property_read_u32(np, "nxp,audout-mclk-fs", &v)) {
+ switch (v) {
+ case 512:
+ case 256:
+ case 128:
+ case 64:
+ case 32:
+ case 16:
+ break;
+ default:
+ v4l_err(state->client,
+ "nxp,audout-mclk-fs invalid\n");
+ return -EINVAL;
+ }
+ pdata->audout_mclk_fs = v;
+ }
+ }
+
+ return 0;
+}
+
+static int tda1997x_get_regulators(struct tda1997x_state *state)
+{
+ int i;
+
+ for (i = 0; i < TDA1997X_NUM_SUPPLIES; i++)
+ state->supplies[i].supply = tda1997x_supply_name[i];
+
+ return devm_regulator_bulk_get(&state->client->dev,
+ TDA1997X_NUM_SUPPLIES,
+ state->supplies);
+}
+
+static int tda1997x_identify_module(struct tda1997x_state *state)
+{
+ struct v4l2_subdev *sd = &state->sd;
+ enum tda1997x_type type;
+ u8 reg;
+
+ /* Read chip configuration*/
+ reg = io_read(sd, REG_CMTP_REG10);
+ state->tmdsb_clk = (reg >> 6) & 0x01; /* use tmds clock B_inv for B */
+ state->tmdsb_soc = (reg >> 5) & 0x01; /* tmds of input B */
+ state->port_30bit = (reg >> 2) & 0x03; /* 30bit vs 24bit */
+ state->output_2p5 = (reg >> 1) & 0x01; /* output supply 2.5v */
+ switch ((reg >> 4) & 0x03) {
+ case 0x00:
+ type = TDA19971;
+ break;
+ case 0x02:
+ case 0x03:
+ type = TDA19973;
+ break;
+ default:
+ dev_err(&state->client->dev, "unsupported chip ID\n");
+ return -EIO;
+ }
+ if (state->info->type != type) {
+ dev_err(&state->client->dev, "chip id mismatch\n");
+ return -EIO;
+ }
+
+ /* read chip revision */
+ state->chip_revision = io_read(sd, REG_CMTP_REG11);
+
+ return 0;
+}
+
+static const struct media_entity_operations tda1997x_media_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+
+/* -----------------------------------------------------------------------------
+ * HDMI Audio Codec
+ */
+
+/* refine sample-rate based on HDMI source */
+static int tda1997x_pcm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct tda1997x_state *state = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_codec *codec = dai->codec;
+ struct snd_pcm_runtime *rtd = substream->runtime;
+ int rate, err;
+
+ rate = state->audio_samplerate;
+ err = snd_pcm_hw_constraint_minmax(rtd, SNDRV_PCM_HW_PARAM_RATE,
+ rate, rate);
+ if (err < 0) {
+ dev_err(codec->dev, "failed to constrain samplerate to %dHz\n",
+ rate);
+ return err;
+ }
+ dev_info(codec->dev, "set samplerate constraint to %dHz\n", rate);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops tda1997x_dai_ops = {
+ .startup = tda1997x_pcm_startup,
+};
+
+static struct snd_soc_dai_driver tda1997x_audio_dai = {
+ .name = "tda1997x",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
+ SNDRV_PCM_RATE_192000,
+ },
+ .ops = &tda1997x_dai_ops,
+};
+
+static int tda1997x_codec_probe(struct snd_soc_codec *codec)
+{
+ return 0;
+}
+
+static int tda1997x_codec_remove(struct snd_soc_codec *codec)
+{
+ return 0;
+}
+
+static struct snd_soc_codec_driver tda1997x_codec_driver = {
+ .probe = tda1997x_codec_probe,
+ .remove = tda1997x_codec_remove,
+ .reg_word_size = sizeof(u16),
+};
+
+static int tda1997x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct tda1997x_state *state;
+ struct tda1997x_platform_data *pdata;
+ struct v4l2_subdev *sd;
+ struct v4l2_ctrl_handler *hdl;
+ struct v4l2_ctrl *ctrl;
+ static const struct v4l2_dv_timings cea1920x1080 =
+ V4L2_DV_BT_CEA_1920X1080P60;
+ u32 *mbus_codes;
+ int i, ret;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ state = kzalloc(sizeof(struct tda1997x_state), GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ state->client = client;
+ pdata = &state->pdata;
+ if (IS_ENABLED(CONFIG_OF) && client->dev.of_node) {
+ const struct of_device_id *oid;
+
+ oid = of_match_node(tda1997x_of_id, client->dev.of_node);
+ state->info = oid->data;
+
+ ret = tda1997x_parse_dt(state);
+ if (ret < 0) {
+ v4l_err(client, "DT parsing error\n");
+ goto err_free_state;
+ }
+ } else if (client->dev.platform_data) {
+ struct tda1997x_platform_data *pdata =
+ client->dev.platform_data;
+ state->info =
+ (const struct tda1997x_chip_info *)id->driver_data;
+ state->pdata = *pdata;
+ } else {
+ v4l_err(client, "No platform data\n");
+ ret = -ENODEV;
+ goto err_free_state;
+ }
+
+ ret = tda1997x_get_regulators(state);
+ if (ret)
+ goto err_free_state;
+
+ ret = tda1997x_set_power(state, 1);
+ if (ret)
+ goto err_free_state;
+
+ mutex_init(&state->page_lock);
+ mutex_init(&state->lock);
+ state->page = 0xff;
+
+ INIT_DELAYED_WORK(&state->delayed_work_enable_hpd,
+ tda1997x_delayed_work_enable_hpd);
+
+ /* set video format based on chip and bus width */
+ ret = tda1997x_identify_module(state);
+ if (ret)
+ goto err_free_mutex;
+
+ /* initialize subdev */
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &tda1997x_subdev_ops);
+ snprintf(sd->name, sizeof(sd->name), "%s %d-%04x",
+ id->name, i2c_adapter_id(client->adapter),
+ client->addr);
+ sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
+ sd->entity.function = MEDIA_ENT_F_DTV_DECODER;
+ sd->entity.ops = &tda1997x_media_ops;
+
+ /* set allowed mbus modes based on chip, bus-type, and bus-width */
+ i = 0;
+ mbus_codes = state->mbus_codes;
+ switch (state->info->type) {
+ case TDA19973:
+ switch (pdata->vidout_bus_type) {
+ case V4L2_MBUS_PARALLEL:
+ switch (pdata->vidout_bus_width) {
+ case 36:
+ mbus_codes[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
+ mbus_codes[i++] = MEDIA_BUS_FMT_YUV12_1X36;
+ /* fall-through */
+ case 24:
+ mbus_codes[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
+ break;
+ }
+ break;
+ case V4L2_MBUS_BT656:
+ switch (pdata->vidout_bus_width) {
+ case 36:
+ case 24:
+ case 12:
+ mbus_codes[i++] = MEDIA_BUS_FMT_UYVY12_2X12;
+ mbus_codes[i++] = MEDIA_BUS_FMT_UYVY10_2X10;
+ mbus_codes[i++] = MEDIA_BUS_FMT_UYVY8_2X8;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case TDA19971:
+ switch (pdata->vidout_bus_type) {
+ case V4L2_MBUS_PARALLEL:
+ switch (pdata->vidout_bus_width) {
+ case 24:
+ mbus_codes[i++] = MEDIA_BUS_FMT_RGB888_1X24;
+ mbus_codes[i++] = MEDIA_BUS_FMT_YUV8_1X24;
+ mbus_codes[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
+ /* fall through */
+ case 20:
+ mbus_codes[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
+ /* fall through */
+ case 16:
+ mbus_codes[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
+ break;
+ }
+ break;
+ case V4L2_MBUS_BT656:
+ switch (pdata->vidout_bus_width) {
+ case 24:
+ case 20:
+ case 16:
+ case 12:
+ mbus_codes[i++] = MEDIA_BUS_FMT_UYVY12_2X12;
+ /* fall through */
+ case 10:
+ mbus_codes[i++] = MEDIA_BUS_FMT_UYVY10_2X10;
+ /* fall through */
+ case 8:
+ mbus_codes[i++] = MEDIA_BUS_FMT_UYVY8_2X8;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ if (WARN_ON(i > ARRAY_SIZE(state->mbus_codes))) {
+ ret = -EINVAL;
+ goto err_free_mutex;
+ }
+
+ /* default format */
+ tda1997x_setup_format(state, state->mbus_codes[0]);
+ state->timings = cea1920x1080;
+
+ /*
+ * default to SRGB full range quantization
+ * (in case we don't get an infoframe such as DVI signal
+ */
+ state->colorimetry.colorspace = V4L2_COLORSPACE_SRGB;
+ state->colorimetry.quantization = V4L2_QUANTIZATION_FULL_RANGE;
+
+ /* disable/reset HDCP to get correct I2C access to Rx HDMI */
+ io_write(sd, REG_MAN_SUS_HDMI_SEL, MAN_RST_HDCP | MAN_DIS_HDCP);
+
+ /*
+ * if N2 version, reset compdel_bp as it may generate some small pixel
+ * shifts in case of embedded sync/or delay lower than 4
+ */
+ if (state->chip_revision != 0) {
+ io_write(sd, REG_MAN_SUS_HDMI_SEL, 0x00);
+ io_write(sd, REG_VDP_CTRL, 0x1f);
+ }
+
+ v4l_info(client, "NXP %s N%d detected\n", state->info->name,
+ state->chip_revision + 1);
+ v4l_info(client, "video: %dbit %s %d formats available\n",
+ pdata->vidout_bus_width,
+ (pdata->vidout_bus_type == V4L2_MBUS_PARALLEL) ?
+ "parallel" : "BT656",
+ i);
+ if (pdata->audout_format) {
+ v4l_info(client, "audio: %dch %s layout%d sysclk=%d*fs\n",
+ pdata->audout_layout ? 2 : 8,
+ audfmt_names[pdata->audout_format],
+ pdata->audout_layout,
+ pdata->audout_mclk_fs);
+ }
+
+ ret = 0x34 + ((io_read(sd, REG_SLAVE_ADDR)>>4) & 0x03);
+ state->client_cec = i2c_new_dummy(client->adapter, ret);
+ v4l_info(client, "CEC slave address 0x%02x\n", ret);
+
+ ret = tda1997x_core_init(sd);
+ if (ret)
+ goto err_free_mutex;
+
+ /* control handlers */
+ hdl = &state->hdl;
+ v4l2_ctrl_handler_init(hdl, 3);
+ ctrl = v4l2_ctrl_new_std_menu(hdl, &tda1997x_ctrl_ops,
+ V4L2_CID_DV_RX_IT_CONTENT_TYPE,
+ V4L2_DV_IT_CONTENT_TYPE_NO_ITC, 0,
+ V4L2_DV_IT_CONTENT_TYPE_NO_ITC);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+ /* custom controls */
+ state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL,
+ V4L2_CID_DV_RX_POWER_PRESENT, 0, 1, 0, 0);
+ state->rgb_quantization_range_ctrl = v4l2_ctrl_new_std_menu(hdl,
+ &tda1997x_ctrl_ops,
+ V4L2_CID_DV_RX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL, 0,
+ V4L2_DV_RGB_RANGE_AUTO);
+ state->sd.ctrl_handler = hdl;
+ if (hdl->error) {
+ ret = hdl->error;
+ goto err_free_handler;
+ }
+ v4l2_ctrl_handler_setup(hdl);
+
+ /* initialize source pads */
+ state->pads[TDA1997X_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&sd->entity, TDA1997X_NUM_PADS,
+ state->pads);
+ if (ret) {
+ v4l_err(client, "failed entity_init: %d", ret);
+ goto err_free_mutex;
+ }
+
+ ret = v4l2_async_register_subdev(sd);
+ if (ret)
+ goto err_free_media;
+
+ /* register audio DAI */
+ if (pdata->audout_format) {
+ u64 formats;
+
+ if (pdata->audout_width == 32)
+ formats = SNDRV_PCM_FMTBIT_S32_LE;
+ else
+ formats = SNDRV_PCM_FMTBIT_S16_LE;
+ tda1997x_audio_dai.capture.formats = formats;
+ ret = snd_soc_register_codec(&state->client->dev,
+ &tda1997x_codec_driver,
+ &tda1997x_audio_dai, 1);
+ if (ret) {
+ dev_err(&client->dev, "register audio codec failed\n");
+ goto err_free_media;
+ }
+ dev_set_drvdata(&state->client->dev, state);
+ v4l_info(state->client, "registered audio codec\n");
+ }
+
+ /* request irq */
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, tda1997x_isr_thread,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ KBUILD_MODNAME, state);
+ if (ret) {
+ v4l_err(client, "irq%d reg failed: %d\n", client->irq, ret);
+ goto err_free_media;
+ }
+
+ return 0;
+
+err_free_media:
+ media_entity_cleanup(&sd->entity);
+err_free_handler:
+ v4l2_ctrl_handler_free(&state->hdl);
+err_free_mutex:
+ cancel_delayed_work(&state->delayed_work_enable_hpd);
+ mutex_destroy(&state->page_lock);
+ mutex_destroy(&state->lock);
+err_free_state:
+ kfree(state);
+ dev_err(&client->dev, "%s failed: %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int tda1997x_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct tda1997x_state *state = to_state(sd);
+ struct tda1997x_platform_data *pdata = &state->pdata;
+
+ if (pdata->audout_format) {
+ snd_soc_unregister_codec(&client->dev);
+ mutex_destroy(&state->audio_lock);
+ }
+
+ disable_irq(state->client->irq);
+ tda1997x_power_mode(state, 0);
+
+ v4l2_async_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+ v4l2_ctrl_handler_free(&state->hdl);
+ regulator_bulk_disable(TDA1997X_NUM_SUPPLIES, state->supplies);
+ i2c_unregister_device(state->client_cec);
+ cancel_delayed_work(&state->delayed_work_enable_hpd);
+ mutex_destroy(&state->page_lock);
+ mutex_destroy(&state->lock);
+
+ kfree(state);
+
+ return 0;
+}
+
+static struct i2c_driver tda1997x_i2c_driver = {
+ .driver = {
+ .name = "tda1997x",
+ .of_match_table = of_match_ptr(tda1997x_of_id),
+ },
+ .probe = tda1997x_probe,
+ .remove = tda1997x_remove,
+ .id_table = tda1997x_i2c_id,
+};
+
+module_i2c_driver(tda1997x_i2c_driver);
+
+MODULE_AUTHOR("Tim Harvey <tharvey@gateworks.com>");
+MODULE_DESCRIPTION("TDA1997X HDMI Receiver driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/tda1997x_regs.h b/drivers/media/i2c/tda1997x_regs.h
new file mode 100644
index 000000000000..f55dfc423a86
--- /dev/null
+++ b/drivers/media/i2c/tda1997x_regs.h
@@ -0,0 +1,641 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Gateworks Corporation
+ */
+
+/* Page 0x00 - General Control */
+#define REG_VERSION 0x0000
+#define REG_INPUT_SEL 0x0001
+#define REG_SVC_MODE 0x0002
+#define REG_HPD_MAN_CTRL 0x0003
+#define REG_RT_MAN_CTRL 0x0004
+#define REG_STANDBY_SOFT_RST 0x000A
+#define REG_HDMI_SOFT_RST 0x000B
+#define REG_HDMI_INFO_RST 0x000C
+#define REG_INT_FLG_CLR_TOP 0x000E
+#define REG_INT_FLG_CLR_SUS 0x000F
+#define REG_INT_FLG_CLR_DDC 0x0010
+#define REG_INT_FLG_CLR_RATE 0x0011
+#define REG_INT_FLG_CLR_MODE 0x0012
+#define REG_INT_FLG_CLR_INFO 0x0013
+#define REG_INT_FLG_CLR_AUDIO 0x0014
+#define REG_INT_FLG_CLR_HDCP 0x0015
+#define REG_INT_FLG_CLR_AFE 0x0016
+#define REG_INT_MASK_TOP 0x0017
+#define REG_INT_MASK_SUS 0x0018
+#define REG_INT_MASK_DDC 0x0019
+#define REG_INT_MASK_RATE 0x001A
+#define REG_INT_MASK_MODE 0x001B
+#define REG_INT_MASK_INFO 0x001C
+#define REG_INT_MASK_AUDIO 0x001D
+#define REG_INT_MASK_HDCP 0x001E
+#define REG_INT_MASK_AFE 0x001F
+#define REG_DETECT_5V 0x0020
+#define REG_SUS_STATUS 0x0021
+#define REG_V_PER 0x0022
+#define REG_H_PER 0x0025
+#define REG_HS_WIDTH 0x0027
+#define REG_FMT_H_TOT 0x0029
+#define REG_FMT_H_ACT 0x002b
+#define REG_FMT_H_FRONT 0x002d
+#define REG_FMT_H_SYNC 0x002f
+#define REG_FMT_H_BACK 0x0031
+#define REG_FMT_V_TOT 0x0033
+#define REG_FMT_V_ACT 0x0035
+#define REG_FMT_V_FRONT_F1 0x0037
+#define REG_FMT_V_FRONT_F2 0x0038
+#define REG_FMT_V_SYNC 0x0039
+#define REG_FMT_V_BACK_F1 0x003a
+#define REG_FMT_V_BACK_F2 0x003b
+#define REG_FMT_DE_ACT 0x003c
+#define REG_RATE_CTRL 0x0040
+#define REG_CLK_MIN_RATE 0x0043
+#define REG_CLK_MAX_RATE 0x0046
+#define REG_CLK_A_STATUS 0x0049
+#define REG_CLK_A_RATE 0x004A
+#define REG_DRIFT_CLK_A_REG 0x004D
+#define REG_CLK_B_STATUS 0x004E
+#define REG_CLK_B_RATE 0x004F
+#define REG_DRIFT_CLK_B_REG 0x0052
+#define REG_HDCP_CTRL 0x0060
+#define REG_HDCP_KDS 0x0061
+#define REG_HDCP_BCAPS 0x0063
+#define REG_HDCP_KEY_CTRL 0x0064
+#define REG_INFO_CTRL 0x0076
+#define REG_INFO_EXCEED 0x0077
+#define REG_PIX_REPEAT 0x007B
+#define REG_AUDIO_PATH 0x007C
+#define REG_AUDCFG 0x007D
+#define REG_AUDIO_OUT_ENABLE 0x007E
+#define REG_AUDIO_OUT_HIZ 0x007F
+#define REG_VDP_CTRL 0x0080
+#define REG_VDP_MATRIX 0x0081
+#define REG_VHREF_CTRL 0x00A0
+#define REG_PXCNT_PR 0x00A2
+#define REG_PXCNT_NPIX 0x00A4
+#define REG_LCNT_PR 0x00A6
+#define REG_LCNT_NLIN 0x00A8
+#define REG_HREF_S 0x00AA
+#define REG_HREF_E 0x00AC
+#define REG_HS_S 0x00AE
+#define REG_HS_E 0x00B0
+#define REG_VREF_F1_S 0x00B2
+#define REG_VREF_F1_WIDTH 0x00B4
+#define REG_VREF_F2_S 0x00B5
+#define REG_VREF_F2_WIDTH 0x00B7
+#define REG_VS_F1_LINE_S 0x00B8
+#define REG_VS_F1_LINE_WIDTH 0x00BA
+#define REG_VS_F2_LINE_S 0x00BB
+#define REG_VS_F2_LINE_WIDTH 0x00BD
+#define REG_VS_F1_PIX_S 0x00BE
+#define REG_VS_F1_PIX_E 0x00C0
+#define REG_VS_F2_PIX_S 0x00C2
+#define REG_VS_F2_PIX_E 0x00C4
+#define REG_FREF_F1_S 0x00C6
+#define REG_FREF_F2_S 0x00C8
+#define REG_FDW_S 0x00ca
+#define REG_FDW_E 0x00cc
+#define REG_BLK_GY 0x00da
+#define REG_BLK_BU 0x00dc
+#define REG_BLK_RV 0x00de
+#define REG_FILTERS_CTRL 0x00e0
+#define REG_DITHERING_CTRL 0x00E9
+#define REG_OF 0x00EA
+#define REG_PCLK 0x00EB
+#define REG_HS_HREF 0x00EC
+#define REG_VS_VREF 0x00ED
+#define REG_DE_FREF 0x00EE
+#define REG_VP35_32_CTRL 0x00EF
+#define REG_VP31_28_CTRL 0x00F0
+#define REG_VP27_24_CTRL 0x00F1
+#define REG_VP23_20_CTRL 0x00F2
+#define REG_VP19_16_CTRL 0x00F3
+#define REG_VP15_12_CTRL 0x00F4
+#define REG_VP11_08_CTRL 0x00F5
+#define REG_VP07_04_CTRL 0x00F6
+#define REG_VP03_00_CTRL 0x00F7
+#define REG_CURPAGE_00H 0xFF
+
+#define MASK_VPER 0x3fffff
+#define MASK_VHREF 0x3fff
+#define MASK_HPER 0x0fff
+#define MASK_HSWIDTH 0x03ff
+
+/* HPD Detection */
+#define DETECT_UTIL BIT(7) /* utility of HDMI level */
+#define DETECT_HPD BIT(6) /* HPD of HDMI level */
+#define DETECT_5V_SEL BIT(2) /* 5V present on selected input */
+#define DETECT_5V_B BIT(1) /* 5V present on input B */
+#define DETECT_5V_A BIT(0) /* 5V present on input A */
+
+/* Input Select */
+#define INPUT_SEL_RST_FMT BIT(7) /* 1=reset format measurement */
+#define INPUT_SEL_RST_VDP BIT(2) /* 1=reset video data path */
+#define INPUT_SEL_OUT_MODE BIT(1) /* 0=loop 1=bypass */
+#define INPUT_SEL_B BIT(0) /* 0=inputA 1=inputB */
+
+/* Service Mode */
+#define SVC_MODE_CLK2_MASK 0xc0
+#define SVC_MODE_CLK2_SHIFT 6
+#define SVC_MODE_CLK2_XTL 0L
+#define SVC_MODE_CLK2_XTLDIV2 1L
+#define SVC_MODE_CLK2_HDMIX2 3L
+#define SVC_MODE_CLK1_MASK 0x30
+#define SVC_MODE_CLK1_SHIFT 4
+#define SVC_MODE_CLK1_XTAL 0L
+#define SVC_MODE_CLK1_XTLDIV2 1L
+#define SVC_MODE_CLK1_HDMI 3L
+#define SVC_MODE_RAMP BIT(3) /* 0=colorbar 1=ramp */
+#define SVC_MODE_PAL BIT(2) /* 0=NTSC(480i/p) 1=PAL(576i/p) */
+#define SVC_MODE_INT_PROG BIT(1) /* 0=interlaced 1=progressive */
+#define SVC_MODE_SM_ON BIT(0) /* Enable color bars and tone gen */
+
+/* HDP Manual Control */
+#define HPD_MAN_CTRL_HPD_PULSE BIT(7) /* HPD Pulse low 110ms */
+#define HPD_MAN_CTRL_5VEN BIT(2) /* Output 5V */
+#define HPD_MAN_CTRL_HPD_B BIT(1) /* Assert HPD High for Input A */
+#define HPD_MAN_CTRL_HPD_A BIT(0) /* Assert HPD High for Input A */
+
+/* RT_MAN_CTRL */
+#define RT_MAN_CTRL_RT_AUTO BIT(7)
+#define RT_MAN_CTRL_RT BIT(6)
+#define RT_MAN_CTRL_RT_B BIT(1) /* enable TMDS pull-up on Input B */
+#define RT_MAN_CTRL_RT_A BIT(0) /* enable TMDS pull-up on Input A */
+
+/* VDP_CTRL */
+#define VDP_CTRL_COMPDEL_BP BIT(5) /* bypass compdel */
+#define VDP_CTRL_FORMATTER_BP BIT(4) /* bypass formatter */
+#define VDP_CTRL_PREFILTER_BP BIT(1) /* bypass prefilter */
+#define VDP_CTRL_MATRIX_BP BIT(0) /* bypass matrix conversion */
+
+/* REG_VHREF_CTRL */
+#define VHREF_INT_DET BIT(7) /* interlace detect: 1=alt 0=frame */
+#define VHREF_VSYNC_MASK 0x60
+#define VHREF_VSYNC_SHIFT 6
+#define VHREF_VSYNC_AUTO 0L
+#define VHREF_VSYNC_FDW 1L
+#define VHREF_VSYNC_EVEN 2L
+#define VHREF_VSYNC_ODD 3L
+#define VHREF_STD_DET_MASK 0x18
+#define VHREF_STD_DET_SHIFT 3
+#define VHREF_STD_DET_PAL 0L
+#define VHREF_STD_DET_NTSC 1L
+#define VHREF_STD_DET_AUTO 2L
+#define VHREF_STD_DET_OFF 3L
+#define VHREF_VREF_SRC_STD BIT(2) /* 1=from standard 0=manual */
+#define VHREF_HREF_SRC_STD BIT(1) /* 1=from standard 0=manual */
+#define VHREF_HSYNC_SEL_HS BIT(0) /* 1=HS 0=VS */
+
+/* AUDIO_OUT_ENABLE */
+#define AUDIO_OUT_ENABLE_ACLK BIT(5)
+#define AUDIO_OUT_ENABLE_WS BIT(4)
+#define AUDIO_OUT_ENABLE_AP3 BIT(3)
+#define AUDIO_OUT_ENABLE_AP2 BIT(2)
+#define AUDIO_OUT_ENABLE_AP1 BIT(1)
+#define AUDIO_OUT_ENABLE_AP0 BIT(0)
+
+/* Prefilter Control */
+#define FILTERS_CTRL_BU_MASK 0x0c
+#define FILTERS_CTRL_BU_SHIFT 2
+#define FILTERS_CTRL_RV_MASK 0x03
+#define FILTERS_CTRL_RV_SHIFT 0
+#define FILTERS_CTRL_OFF 0L /* off */
+#define FILTERS_CTRL_2TAP 1L /* 2 Taps */
+#define FILTERS_CTRL_7TAP 2L /* 7 Taps */
+#define FILTERS_CTRL_2_7TAP 3L /* 2/7 Taps */
+
+/* PCLK Configuration */
+#define PCLK_DELAY_MASK 0x70
+#define PCLK_DELAY_SHIFT 4 /* Pixel delay (-8..+7) */
+#define PCLK_INV_SHIFT 2
+#define PCLK_SEL_MASK 0x03 /* clock scaler */
+#define PCLK_SEL_SHIFT 0
+#define PCLK_SEL_X1 0L
+#define PCLK_SEL_X2 1L
+#define PCLK_SEL_DIV2 2L
+#define PCLK_SEL_DIV4 3L
+
+/* Pixel Repeater */
+#define PIX_REPEAT_MASK_UP_SEL 0x30
+#define PIX_REPEAT_MASK_REP 0x0f
+#define PIX_REPEAT_SHIFT 4
+#define PIX_REPEAT_CHROMA 1
+
+/* Page 0x01 - HDMI info and packets */
+#define REG_HDMI_FLAGS 0x0100
+#define REG_DEEP_COLOR_MODE 0x0101
+#define REG_AUDIO_FLAGS 0x0108
+#define REG_AUDIO_FREQ 0x0109
+#define REG_ACP_PACKET_TYPE 0x0141
+#define REG_ISRC1_PACKET_TYPE 0x0161
+#define REG_ISRC2_PACKET_TYPE 0x0181
+#define REG_GBD_PACKET_TYPE 0x01a1
+
+/* HDMI_FLAGS */
+#define HDMI_FLAGS_AUDIO BIT(7) /* Audio packet in last videoframe */
+#define HDMI_FLAGS_HDMI BIT(6) /* HDMI detected */
+#define HDMI_FLAGS_EESS BIT(5) /* EESS detected */
+#define HDMI_FLAGS_HDCP BIT(4) /* HDCP detected */
+#define HDMI_FLAGS_AVMUTE BIT(3) /* AVMUTE */
+#define HDMI_FLAGS_AUD_LAYOUT BIT(2) /* Layout status Audio sample packet */
+#define HDMI_FLAGS_AUD_FIFO_OF BIT(1) /* FIFO read/write pointers crossed */
+#define HDMI_FLAGS_AUD_FIFO_LOW BIT(0) /* FIFO read ptr within 2 of write */
+
+/* Page 0x12 - HDMI Extra control and debug */
+#define REG_CLK_CFG 0x1200
+#define REG_CLK_OUT_CFG 0x1201
+#define REG_CFG1 0x1202
+#define REG_CFG2 0x1203
+#define REG_WDL_CFG 0x1210
+#define REG_DELOCK_DELAY 0x1212
+#define REG_PON_OVR_EN 0x12A0
+#define REG_PON_CBIAS 0x12A1
+#define REG_PON_RESCAL 0x12A2
+#define REG_PON_RES 0x12A3
+#define REG_PON_CLK 0x12A4
+#define REG_PON_PLL 0x12A5
+#define REG_PON_EQ 0x12A6
+#define REG_PON_DES 0x12A7
+#define REG_PON_OUT 0x12A8
+#define REG_PON_MUX 0x12A9
+#define REG_MODE_REC_CFG1 0x12F8
+#define REG_MODE_REC_CFG2 0x12F9
+#define REG_MODE_REC_STS 0x12FA
+#define REG_AUDIO_LAYOUT 0x12D0
+
+#define PON_EN 1
+#define PON_DIS 0
+
+/* CLK CFG */
+#define CLK_CFG_INV_OUT_CLK BIT(7)
+#define CLK_CFG_INV_BUS_CLK BIT(6)
+#define CLK_CFG_SEL_ACLK_EN BIT(1)
+#define CLK_CFG_SEL_ACLK BIT(0)
+#define CLK_CFG_DIS 0
+
+/* Page 0x13 - HDMI Extra control and debug */
+#define REG_DEEP_COLOR_CTRL 0x1300
+#define REG_CGU_DBG_SEL 0x1305
+#define REG_HDCP_DDC_ADDR 0x1310
+#define REG_HDCP_KIDX 0x1316
+#define REG_DEEP_PLL7_BYP 0x1347
+#define REG_HDCP_DE_CTRL 0x1370
+#define REG_HDCP_EP_FILT_CTRL 0x1371
+#define REG_HDMI_CTRL 0x1377
+#define REG_HMTP_CTRL 0x137a
+#define REG_TIMER_D 0x13CF
+#define REG_SUS_SET_RGB0 0x13E1
+#define REG_SUS_SET_RGB1 0x13E2
+#define REG_SUS_SET_RGB2 0x13E3
+#define REG_SUS_SET_RGB3 0x13E4
+#define REG_SUS_SET_RGB4 0x13E5
+#define REG_MAN_SUS_HDMI_SEL 0x13E8
+#define REG_MAN_HDMI_SET 0x13E9
+#define REG_SUS_CLOCK_GOOD 0x13EF
+
+/* HDCP DE Control */
+#define HDCP_DE_MODE_MASK 0xc0 /* DE Measurement mode */
+#define HDCP_DE_MODE_SHIFT 6
+#define HDCP_DE_REGEN_EN BIT(5) /* enable regen mode */
+#define HDCP_DE_FILTER_MASK 0x18 /* DE filter sensitivity */
+#define HDCP_DE_FILTER_SHIFT 3
+#define HDCP_DE_COMP_MASK 0x07 /* DE Composition mode */
+#define HDCP_DE_COMP_MIXED 6L
+#define HDCP_DE_COMP_OR 5L
+#define HDCP_DE_COMP_AND 4L
+#define HDCP_DE_COMP_CH3 3L
+#define HDCP_DE_COMP_CH2 2L
+#define HDCP_DE_COMP_CH1 1L
+#define HDCP_DE_COMP_CH0 0L
+
+/* HDCP EP Filter Control */
+#define HDCP_EP_FIL_CTL_MASK 0x30
+#define HDCP_EP_FIL_CTL_SHIFT 4
+#define HDCP_EP_FIL_VS_MASK 0x0c
+#define HDCP_EP_FIL_VS_SHIFT 2
+#define HDCP_EP_FIL_HS_MASK 0x03
+#define HDCP_EP_FIL_HS_SHIFT 0
+
+/* HDMI_CTRL */
+#define HDMI_CTRL_MUTE_MASK 0x0c
+#define HDMI_CTRL_MUTE_SHIFT 2
+#define HDMI_CTRL_MUTE_AUTO 0L
+#define HDMI_CTRL_MUTE_OFF 1L
+#define HDMI_CTRL_MUTE_ON 2L
+#define HDMI_CTRL_HDCP_MASK 0x03
+#define HDMI_CTRL_HDCP_SHIFT 0
+#define HDMI_CTRL_HDCP_EESS 2L
+#define HDMI_CTRL_HDCP_OESS 1L
+#define HDMI_CTRL_HDCP_AUTO 0L
+
+/* CGU_DBG_SEL bits */
+#define CGU_DBG_CLK_SEL_MASK 0x18
+#define CGU_DBG_CLK_SEL_SHIFT 3
+#define CGU_DBG_XO_FRO_SEL BIT(2)
+#define CGU_DBG_VDP_CLK_SEL BIT(1)
+#define CGU_DBG_PIX_CLK_SEL BIT(0)
+
+/* REG_MAN_SUS_HDMI_SEL / REG_MAN_HDMI_SET bits */
+#define MAN_DIS_OUT_BUF BIT(7)
+#define MAN_DIS_ANA_PATH BIT(6)
+#define MAN_DIS_HDCP BIT(5)
+#define MAN_DIS_TMDS_ENC BIT(4)
+#define MAN_DIS_TMDS_FLOW BIT(3)
+#define MAN_RST_HDCP BIT(2)
+#define MAN_RST_TMDS_ENC BIT(1)
+#define MAN_RST_TMDS_FLOW BIT(0)
+
+/* Page 0x14 - Audio Extra control and debug */
+#define REG_FIFO_LATENCY_VAL 0x1403
+#define REG_AUDIO_CLOCK 0x1411
+#define REG_TEST_NCTS_CTRL 0x1415
+#define REG_TEST_AUDIO_FREQ 0x1426
+#define REG_TEST_MODE 0x1437
+
+/* Audio Clock Configuration */
+#define AUDIO_CLOCK_PLL_PD BIT(7) /* powerdown PLL */
+#define AUDIO_CLOCK_SEL_MASK 0x7f
+#define AUDIO_CLOCK_SEL_16FS 0L /* 16*fs */
+#define AUDIO_CLOCK_SEL_32FS 1L /* 32*fs */
+#define AUDIO_CLOCK_SEL_64FS 2L /* 64*fs */
+#define AUDIO_CLOCK_SEL_128FS 3L /* 128*fs */
+#define AUDIO_CLOCK_SEL_256FS 4L /* 256*fs */
+#define AUDIO_CLOCK_SEL_512FS 5L /* 512*fs */
+
+/* Page 0x20: EDID and Hotplug Detect */
+#define REG_EDID_IN_BYTE0 0x2000 /* EDID base */
+#define REG_EDID_IN_VERSION 0x2080
+#define REG_EDID_ENABLE 0x2081
+#define REG_HPD_POWER 0x2084
+#define REG_HPD_AUTO_CTRL 0x2085
+#define REG_HPD_DURATION 0x2086
+#define REG_RX_HPD_HEAC 0x2087
+
+/* EDID_ENABLE */
+#define EDID_ENABLE_NACK_OFF BIT(7)
+#define EDID_ENABLE_EDID_ONLY BIT(6)
+#define EDID_ENABLE_B_EN BIT(1)
+#define EDID_ENABLE_A_EN BIT(0)
+
+/* HPD Power */
+#define HPD_POWER_BP_MASK 0x0c
+#define HPD_POWER_BP_SHIFT 2
+#define HPD_POWER_BP_LOW 0L
+#define HPD_POWER_BP_HIGH 1L
+#define HPD_POWER_EDID_ONLY BIT(1)
+
+/* HPD Auto control */
+#define HPD_AUTO_READ_EDID BIT(7)
+#define HPD_AUTO_HPD_F3TECH BIT(5)
+#define HPD_AUTO_HP_OTHER BIT(4)
+#define HPD_AUTO_HPD_UNSEL BIT(3)
+#define HPD_AUTO_HPD_ALL_CH BIT(2)
+#define HPD_AUTO_HPD_PRV_CH BIT(1)
+#define HPD_AUTO_HPD_NEW_CH BIT(0)
+
+/* Page 0x21 - EDID content */
+#define REG_EDID_IN_BYTE128 0x2100 /* CEA Extension block */
+#define REG_EDID_IN_SPA_SUB 0x2180
+#define REG_EDID_IN_SPA_AB_A 0x2181
+#define REG_EDID_IN_SPA_CD_A 0x2182
+#define REG_EDID_IN_CKSUM_A 0x2183
+#define REG_EDID_IN_SPA_AB_B 0x2184
+#define REG_EDID_IN_SPA_CD_B 0x2185
+#define REG_EDID_IN_CKSUM_B 0x2186
+
+/* Page 0x30 - NV Configuration */
+#define REG_RT_AUTO_CTRL 0x3000
+#define REG_EQ_MAN_CTRL0 0x3001
+#define REG_EQ_MAN_CTRL1 0x3002
+#define REG_OUTPUT_CFG 0x3003
+#define REG_MUTE_CTRL 0x3004
+#define REG_SLAVE_ADDR 0x3005
+#define REG_CMTP_REG6 0x3006
+#define REG_CMTP_REG7 0x3007
+#define REG_CMTP_REG8 0x3008
+#define REG_CMTP_REG9 0x3009
+#define REG_CMTP_REGA 0x300A
+#define REG_CMTP_REGB 0x300B
+#define REG_CMTP_REGC 0x300C
+#define REG_CMTP_REGD 0x300D
+#define REG_CMTP_REGE 0x300E
+#define REG_CMTP_REGF 0x300F
+#define REG_CMTP_REG10 0x3010
+#define REG_CMTP_REG11 0x3011
+
+/* Page 0x80 - CEC */
+#define REG_PWR_CONTROL 0x80F4
+#define REG_OSC_DIVIDER 0x80F5
+#define REG_EN_OSC_PERIOD_LSB 0x80F8
+#define REG_CONTROL 0x80FF
+
+/* global interrupt flags (INT_FLG_CRL_TOP) */
+#define INTERRUPT_AFE BIT(7) /* AFE module */
+#define INTERRUPT_HDCP BIT(6) /* HDCP module */
+#define INTERRUPT_AUDIO BIT(5) /* Audio module */
+#define INTERRUPT_INFO BIT(4) /* Infoframe module */
+#define INTERRUPT_MODE BIT(3) /* HDMI mode module */
+#define INTERRUPT_RATE BIT(2) /* rate module */
+#define INTERRUPT_DDC BIT(1) /* DDC module */
+#define INTERRUPT_SUS BIT(0) /* SUS module */
+
+/* INT_FLG_CLR_HDCP bits */
+#define MASK_HDCP_MTP BIT(7) /* HDCP MTP busy */
+#define MASK_HDCP_DLMTP BIT(4) /* HDCP end download MTP to SRAM */
+#define MASK_HDCP_DLRAM BIT(3) /* HDCP end download keys from SRAM */
+#define MASK_HDCP_ENC BIT(2) /* HDCP ENC */
+#define MASK_STATE_C5 BIT(1) /* HDCP State C5 reached */
+#define MASK_AKSV BIT(0) /* AKSV received (start of auth) */
+
+/* INT_FLG_CLR_RATE bits */
+#define MASK_RATE_B_DRIFT BIT(7) /* Rate measurement drifted */
+#define MASK_RATE_B_ST BIT(6) /* Rate measurement stability change */
+#define MASK_RATE_B_ACT BIT(5) /* Rate measurement activity change */
+#define MASK_RATE_B_PST BIT(4) /* Rate measreument presence change */
+#define MASK_RATE_A_DRIFT BIT(3) /* Rate measurement drifted */
+#define MASK_RATE_A_ST BIT(2) /* Rate measurement stability change */
+#define MASK_RATE_A_ACT BIT(1) /* Rate measurement presence change */
+#define MASK_RATE_A_PST BIT(0) /* Rate measreument presence change */
+
+/* INT_FLG_CLR_SUS (Start Up Sequencer) bits */
+#define MASK_MPT BIT(7) /* Config MTP end of process */
+#define MASK_FMT BIT(5) /* Video format changed */
+#define MASK_RT_PULSE BIT(4) /* End of termination resistance pulse */
+#define MASK_SUS_END BIT(3) /* SUS last state reached */
+#define MASK_SUS_ACT BIT(2) /* Activity of selected input changed */
+#define MASK_SUS_CH BIT(1) /* Selected input changed */
+#define MASK_SUS_ST BIT(0) /* SUS state changed */
+
+/* INT_FLG_CLR_DDC bits */
+#define MASK_EDID_MTP BIT(7) /* EDID MTP end of process */
+#define MASK_DDC_ERR BIT(6) /* master DDC error */
+#define MASK_DDC_CMD_DONE BIT(5) /* master DDC cmd send correct */
+#define MASK_READ_DONE BIT(4) /* End of down EDID read */
+#define MASK_RX_DDC_SW BIT(3) /* Output DDC switching finished */
+#define MASK_HDCP_DDC_SW BIT(2) /* HDCP DDC switching finished */
+#define MASK_HDP_PULSE_END BIT(1) /* End of Hot Plug Detect pulse */
+#define MASK_DET_5V BIT(0) /* Detection of +5V */
+
+/* INT_FLG_CLR_MODE bits */
+#define MASK_HDMI_FLG BIT(7) /* HDMI mode/avmute/encrypt/FIFO fail */
+#define MASK_GAMUT BIT(6) /* Gamut packet */
+#define MASK_ISRC2 BIT(5) /* ISRC2 packet */
+#define MASK_ISRC1 BIT(4) /* ISRC1 packet */
+#define MASK_ACP BIT(3) /* Audio Content Protection packet */
+#define MASK_DC_NO_GCP BIT(2) /* GCP not received in 5 frames */
+#define MASK_DC_PHASE BIT(1) /* deepcolor pixel phase needs update */
+#define MASK_DC_MODE BIT(0) /* deepcolor color depth changed */
+
+/* INT_FLG_CLR_INFO bits (Infoframe Change Status) */
+#define MASK_MPS_IF BIT(6) /* MPEG Source Product */
+#define MASK_AUD_IF BIT(5) /* Audio */
+#define MASK_SPD_IF BIT(4) /* Source Product Descriptor */
+#define MASK_AVI_IF BIT(3) /* Auxiliary Video IF */
+#define MASK_VS_IF_OTHER_BK2 BIT(2) /* Vendor Specific (bank2) */
+#define MASK_VS_IF_OTHER_BK1 BIT(1) /* Vendor Specific (bank1) */
+#define MASK_VS_IF_HDMI BIT(0) /* Vendor Specific (w/ HDMI LLC code) */
+
+/* INT_FLG_CLR_AUDIO bits */
+#define MASK_AUDIO_FREQ_FLG BIT(5) /* Audio freq change */
+#define MASK_AUDIO_FLG BIT(4) /* DST, OBA, HBR, ASP change */
+#define MASK_MUTE_FLG BIT(3) /* Audio Mute */
+#define MASK_CH_STATE BIT(2) /* Channel status */
+#define MASK_UNMUTE_FIFO BIT(1) /* Audio Unmute */
+#define MASK_ERROR_FIFO_PT BIT(0) /* Audio FIFO pointer error */
+
+/* INT_FLG_CLR_AFE bits */
+#define MASK_AFE_WDL_UNLOCKED BIT(7) /* Wordlocker was unlocked */
+#define MASK_AFE_GAIN_DONE BIT(6) /* Gain calibration done */
+#define MASK_AFE_OFFSET_DONE BIT(5) /* Offset calibration done */
+#define MASK_AFE_ACTIVITY_DET BIT(4) /* Activity detected on data */
+#define MASK_AFE_PLL_LOCK BIT(3) /* TMDS PLL is locked */
+#define MASK_AFE_TRMCAL_DONE BIT(2) /* Termination calibration done */
+#define MASK_AFE_ASU_STATE BIT(1) /* ASU state is reached */
+#define MASK_AFE_ASU_READY BIT(0) /* AFE calibration done: TMDS ready */
+
+/* Audio Output */
+#define AUDCFG_CLK_INVERT BIT(7) /* invert A_CLK polarity */
+#define AUDCFG_TEST_TONE BIT(6) /* enable test tone generator */
+#define AUDCFG_BUS_SHIFT 5
+#define AUDCFG_BUS_I2S 0L
+#define AUDCFG_BUS_SPDIF 1L
+#define AUDCFG_I2SW_SHIFT 4
+#define AUDCFG_I2SW_16 0L
+#define AUDCFG_I2SW_32 1L
+#define AUDCFG_AUTO_MUTE_EN BIT(3) /* Enable Automatic audio mute */
+#define AUDCFG_HBR_SHIFT 2
+#define AUDCFG_HBR_STRAIGHT 0L /* straight via AP0 */
+#define AUDCFG_HBR_DEMUX 1L /* demuxed via AP0:AP3 */
+#define AUDCFG_TYPE_MASK 0x03
+#define AUDCFG_TYPE_SHIFT 0
+#define AUDCFG_TYPE_DST 3L /* Direct Stream Transfer (DST) */
+#define AUDCFG_TYPE_OBA 2L /* One Bit Audio (OBA) */
+#define AUDCFG_TYPE_HBR 1L /* High Bit Rate (HBR) */
+#define AUDCFG_TYPE_PCM 0L /* Audio samples */
+
+/* Video Formatter */
+#define OF_VP_ENABLE BIT(7) /* VP[35:0]/HS/VS/DE/CLK */
+#define OF_BLK BIT(4) /* blanking codes */
+#define OF_TRC BIT(3) /* timing codes (SAV/EAV) */
+#define OF_FMT_MASK 0x3
+#define OF_FMT_444 0L /* RGB444/YUV444 */
+#define OF_FMT_422_SMPT 1L /* YUV422 semi-planar */
+#define OF_FMT_422_CCIR 2L /* YUV422 CCIR656 */
+
+/* HS/HREF output control */
+#define HS_HREF_DELAY_MASK 0xf0
+#define HS_HREF_DELAY_SHIFT 4 /* Pixel delay (-8..+7) */
+#define HS_HREF_PXQ_SHIFT 3 /* Timing codes from HREF */
+#define HS_HREF_INV_SHIFT 2 /* polarity (1=invert) */
+#define HS_HREF_SEL_MASK 0x03
+#define HS_HREF_SEL_SHIFT 0
+#define HS_HREF_SEL_HS_VHREF 0L /* HS from VHREF */
+#define HS_HREF_SEL_HREF_VHREF 1L /* HREF from VHREF */
+#define HS_HREF_SEL_HREF_HDMI 2L /* HREF from HDMI */
+#define HS_HREF_SEL_NONE 3L /* not generated */
+
+/* VS output control */
+#define VS_VREF_DELAY_MASK 0xf0
+#define VS_VREF_DELAY_SHIFT 4 /* Pixel delay (-8..+7) */
+#define VS_VREF_INV_SHIFT 2 /* polarity (1=invert) */
+#define VS_VREF_SEL_MASK 0x03
+#define VS_VREF_SEL_SHIFT 0
+#define VS_VREF_SEL_VS_VHREF 0L /* VS from VHREF */
+#define VS_VREF_SEL_VREF_VHREF 1L /* VREF from VHREF */
+#define VS_VREF_SEL_VREF_HDMI 2L /* VREF from HDMI */
+#define VS_VREF_SEL_NONE 3L /* not generated */
+
+/* DE/FREF output control */
+#define DE_FREF_DELAY_MASK 0xf0
+#define DE_FREF_DELAY_SHIFT 4 /* Pixel delay (-8..+7) */
+#define DE_FREF_DE_PXQ_SHIFT 3 /* Timing codes from DE */
+#define DE_FREF_INV_SHIFT 2 /* polarity (1=invert) */
+#define DE_FREF_SEL_MASK 0x03
+#define DE_FREF_SEL_SHIFT 0
+#define DE_FREF_SEL_DE_VHREF 0L /* DE from VHREF (HREF and not(VREF) */
+#define DE_FREF_SEL_FREF_VHREF 1L /* FREF from VHREF */
+#define DE_FREF_SEL_FREF_HDMI 2L /* FREF from HDMI */
+#define DE_FREF_SEL_NONE 3L /* not generated */
+
+/* HDMI_SOFT_RST bits */
+#define RESET_DC BIT(7) /* Reset deep color module */
+#define RESET_HDCP BIT(6) /* Reset HDCP module */
+#define RESET_KSV BIT(5) /* Reset KSV-FIFO */
+#define RESET_SCFG BIT(4) /* Reset HDCP and repeater function */
+#define RESET_HCFG BIT(3) /* Reset HDCP DDC part */
+#define RESET_PA BIT(2) /* Reset polarity adjust */
+#define RESET_EP BIT(1) /* Reset Error protection */
+#define RESET_TMDS BIT(0) /* Reset TMDS (calib, encoding, flow) */
+
+/* HDMI_INFO_RST bits */
+#define NACK_HDCP BIT(7) /* No ACK on HDCP request */
+#define RESET_FIFO BIT(4) /* Reset Audio FIFO control */
+#define RESET_GAMUT BIT(3) /* Clear Gamut packet */
+#define RESET_AI BIT(2) /* Clear ACP and ISRC packets */
+#define RESET_IF BIT(1) /* Clear all Audio infoframe packets */
+#define RESET_AUDIO BIT(0) /* Reset Audio FIFO control */
+
+/* HDCP_BCAPS bits */
+#define HDCP_HDMI BIT(7) /* HDCP suports HDMI (vs DVI only) */
+#define HDCP_REPEATER BIT(6) /* HDCP supports repeater function */
+#define HDCP_READY BIT(5) /* set by repeater function */
+#define HDCP_FAST BIT(4) /* Up to 400kHz */
+#define HDCP_11 BIT(1) /* HDCP 1.1 supported */
+#define HDCP_FAST_REAUTH BIT(0) /* fast reauthentication supported */
+
+/* Audio output formatter */
+#define AUDIO_LAYOUT_SP_FLAG BIT(2) /* sp flag used by FIFO */
+#define AUDIO_LAYOUT_MANUAL BIT(1) /* manual layout (vs per pkt) */
+#define AUDIO_LAYOUT_LAYOUT1 BIT(0) /* Layout1: AP0-3 vs Layout0:AP0 */
+
+/* masks for interrupt status registers */
+#define MASK_SUS_STATUS 0x1F
+#define LAST_STATE_REACHED 0x1B
+#define MASK_CLK_STABLE 0x04
+#define MASK_CLK_ACTIVE 0x02
+#define MASK_SUS_STATE 0x10
+#define MASK_SR_FIFO_FIFO_CTRL 0x30
+#define MASK_AUDIO_FLAG 0x10
+
+/* Rate measurement */
+#define RATE_REFTIM_ENABLE 0x01
+#define CLK_MIN_RATE 0x0057e4
+#define CLK_MAX_RATE 0x0395f8
+#define WDL_CFG_VAL 0x82
+#define DC_FILTER_VAL 0x31
+
+/* Infoframe */
+#define VS_HDMI_IF_UPDATE 0x0200
+#define VS_HDMI_IF 0x0201
+#define VS_BK1_IF_UPDATE 0x0220
+#define VS_BK1_IF 0x0221
+#define VS_BK2_IF_UPDATE 0x0240
+#define VS_BK2_IF 0x0241
+#define AVI_IF_UPDATE 0x0260
+#define AVI_IF 0x0261
+#define SPD_IF_UPDATE 0x0280
+#define SPD_IF 0x0281
+#define AUD_IF_UPDATE 0x02a0
+#define AUD_IF 0x02a1
+#define MPS_IF_UPDATE 0x02c0
+#define MPS_IF 0x02c1
diff --git a/drivers/media/i2c/tda9840.c b/drivers/media/i2c/tda9840.c
index f31e659588ac..0dd6ff3e6201 100644
--- a/drivers/media/i2c/tda9840.c
+++ b/drivers/media/i2c/tda9840.c
@@ -68,11 +68,15 @@ static void tda9840_write(struct v4l2_subdev *sd, u8 reg, u8 val)
static int tda9840_status(struct v4l2_subdev *sd)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int rc;
u8 byte;
- if (1 != i2c_master_recv(client, &byte, 1)) {
+ rc = i2c_master_recv(client, &byte, 1);
+ if (rc != 1) {
v4l2_dbg(1, debug, sd,
"i2c_master_recv() failed\n");
+ if (rc < 0)
+ return rc;
return -EIO;
}
diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c
index 772164b848ef..5919214a56bf 100644
--- a/drivers/media/i2c/tvaudio.c
+++ b/drivers/media/i2c/tvaudio.c
@@ -156,14 +156,18 @@ static int chip_write(struct CHIPSTATE *chip, int subaddr, int val)
struct v4l2_subdev *sd = &chip->sd;
struct i2c_client *c = v4l2_get_subdevdata(sd);
unsigned char buffer[2];
+ int rc;
if (subaddr < 0) {
v4l2_dbg(1, debug, sd, "chip_write: 0x%x\n", val);
chip->shadow.bytes[1] = val;
buffer[0] = val;
- if (1 != i2c_master_send(c, buffer, 1)) {
+ rc = i2c_master_send(c, buffer, 1);
+ if (rc != 1) {
v4l2_warn(sd, "I/O error (write 0x%x)\n", val);
- return -1;
+ if (rc < 0)
+ return rc;
+ return -EIO;
}
} else {
if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) {
@@ -178,10 +182,13 @@ static int chip_write(struct CHIPSTATE *chip, int subaddr, int val)
chip->shadow.bytes[subaddr+1] = val;
buffer[0] = subaddr;
buffer[1] = val;
- if (2 != i2c_master_send(c, buffer, 2)) {
+ rc = i2c_master_send(c, buffer, 2);
+ if (rc != 2) {
v4l2_warn(sd, "I/O error (write reg%d=0x%x)\n",
subaddr, val);
- return -1;
+ if (rc < 0)
+ return rc;
+ return -EIO;
}
}
return 0;
@@ -214,10 +221,14 @@ static int chip_read(struct CHIPSTATE *chip)
struct v4l2_subdev *sd = &chip->sd;
struct i2c_client *c = v4l2_get_subdevdata(sd);
unsigned char buffer;
+ int rc;
- if (1 != i2c_master_recv(c, &buffer, 1)) {
+ rc = i2c_master_recv(c, &buffer, 1);
+ if (rc != 1) {
v4l2_warn(sd, "I/O error (read)\n");
- return -1;
+ if (rc < 0)
+ return rc;
+ return -EIO;
}
v4l2_dbg(1, debug, sd, "chip_read: 0x%x\n", buffer);
return buffer;
@@ -227,6 +238,7 @@ static int chip_read2(struct CHIPSTATE *chip, int subaddr)
{
struct v4l2_subdev *sd = &chip->sd;
struct i2c_client *c = v4l2_get_subdevdata(sd);
+ int rc;
unsigned char write[1];
unsigned char read[1];
struct i2c_msg msgs[2] = {
@@ -245,9 +257,12 @@ static int chip_read2(struct CHIPSTATE *chip, int subaddr)
write[0] = subaddr;
- if (2 != i2c_transfer(c->adapter, msgs, 2)) {
+ rc = i2c_transfer(c->adapter, msgs, 2);
+ if (rc != 2) {
v4l2_warn(sd, "I/O error (read2)\n");
- return -1;
+ if (rc < 0)
+ return rc;
+ return -EIO;
}
v4l2_dbg(1, debug, sd, "chip_read2: reg%d=0x%x\n",
subaddr, read[0]);
@@ -258,7 +273,7 @@ static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd)
{
struct v4l2_subdev *sd = &chip->sd;
struct i2c_client *c = v4l2_get_subdevdata(sd);
- int i;
+ int i, rc;
if (0 == cmd->count)
return 0;
@@ -284,9 +299,12 @@ static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd)
printk(KERN_CONT "\n");
/* send data to the chip */
- if (cmd->count != i2c_master_send(c, cmd->bytes, cmd->count)) {
+ rc = i2c_master_send(c, cmd->bytes, cmd->count);
+ if (rc != cmd->count) {
v4l2_warn(sd, "I/O error (%s)\n", name);
- return -1;
+ if (rc < 0)
+ return rc;
+ return -EIO;
}
return 0;
}
@@ -400,8 +418,12 @@ static int tda9840_getrxsubchans(struct CHIPSTATE *chip)
struct v4l2_subdev *sd = &chip->sd;
int val, mode;
- val = chip_read(chip);
mode = V4L2_TUNER_SUB_MONO;
+
+ val = chip_read(chip);
+ if (val < 0)
+ return mode;
+
if (val & TDA9840_DS_DUAL)
mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
if (val & TDA9840_ST_STEREO)
@@ -445,7 +467,12 @@ static void tda9840_setaudmode(struct CHIPSTATE *chip, int mode)
static int tda9840_checkit(struct CHIPSTATE *chip)
{
int rc;
+
rc = chip_read(chip);
+ if (rc < 0)
+ return 0;
+
+
/* lower 5 bits should be 0 */
return ((rc & 0x1f) == 0) ? 1 : 0;
}
@@ -563,6 +590,9 @@ static int tda985x_getrxsubchans(struct CHIPSTATE *chip)
/* Allows forced mono */
mode = V4L2_TUNER_SUB_MONO;
val = chip_read(chip);
+ if (val < 0)
+ return mode;
+
if (val & TDA985x_STP)
mode = V4L2_TUNER_SUB_STEREO;
if (val & TDA985x_SAPP)
@@ -720,8 +750,12 @@ static int tda9873_getrxsubchans(struct CHIPSTATE *chip)
struct v4l2_subdev *sd = &chip->sd;
int val,mode;
- val = chip_read(chip);
mode = V4L2_TUNER_SUB_MONO;
+
+ val = chip_read(chip);
+ if (val < 0)
+ return mode;
+
if (val & TDA9873_STEREO)
mode = V4L2_TUNER_SUB_STEREO;
if (val & TDA9873_DUAL)
@@ -780,7 +814,8 @@ static int tda9873_checkit(struct CHIPSTATE *chip)
{
int rc;
- if (-1 == (rc = chip_read2(chip,254)))
+ rc = chip_read2(chip, 254);
+ if (rc < 0)
return 0;
return (rc & ~0x1f) == 0x80;
}
@@ -926,11 +961,14 @@ static int tda9874a_getrxsubchans(struct CHIPSTATE *chip)
mode = V4L2_TUNER_SUB_MONO;
- if(-1 == (dsr = chip_read2(chip,TDA9874A_DSR)))
+ dsr = chip_read2(chip, TDA9874A_DSR);
+ if (dsr < 0)
return mode;
- if(-1 == (nsr = chip_read2(chip,TDA9874A_NSR)))
+ nsr = chip_read2(chip, TDA9874A_NSR);
+ if (nsr < 0)
return mode;
- if(-1 == (necr = chip_read2(chip,TDA9874A_NECR)))
+ necr = chip_read2(chip, TDA9874A_NECR);
+ if (necr < 0)
return mode;
/* need to store dsr/nsr somewhere */
@@ -1059,9 +1097,11 @@ static int tda9874a_checkit(struct CHIPSTATE *chip)
struct v4l2_subdev *sd = &chip->sd;
int dic,sic; /* device id. and software id. codes */
- if(-1 == (dic = chip_read2(chip,TDA9874A_DIC)))
+ dic = chip_read2(chip, TDA9874A_DIC);
+ if (dic < 0)
return 0;
- if(-1 == (sic = chip_read2(chip,TDA9874A_SIC)))
+ sic = chip_read2(chip, TDA9874A_SIC);
+ if (sic < 0)
return 0;
v4l2_dbg(1, debug, sd, "tda9874a_checkit(): DIC=0x%X, SIC=0x%X.\n", dic, sic);
@@ -1201,7 +1241,11 @@ static int tda9875_checkit(struct CHIPSTATE *chip)
int dic, rev;
dic = chip_read2(chip, 254);
+ if (dic < 0)
+ return 0;
rev = chip_read2(chip, 255);
+ if (rev < 0)
+ return 0;
if (dic == 0 || dic == 2) { /* tda9875 and tda9875A */
v4l2_info(sd, "found tda9875%s rev. %d.\n",
@@ -1377,8 +1421,12 @@ static int ta8874z_getrxsubchans(struct CHIPSTATE *chip)
{
int val, mode;
- val = chip_read(chip);
mode = V4L2_TUNER_SUB_MONO;
+
+ val = chip_read(chip);
+ if (val < 0)
+ return mode;
+
if (val & TA8874Z_B1){
mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
}else if (!(val & TA8874Z_B0)){
@@ -1431,7 +1479,11 @@ static void ta8874z_setaudmode(struct CHIPSTATE *chip, int mode)
static int ta8874z_checkit(struct CHIPSTATE *chip)
{
int rc;
+
rc = chip_read(chip);
+ if (rc < 0)
+ return rc;
+
return ((rc & 0x1f) == 0x1f) ? 1 : 0;
}
diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c
index 8b0aa9297bde..6a9890531d01 100644
--- a/drivers/media/i2c/tvp514x.c
+++ b/drivers/media/i2c/tvp514x.c
@@ -747,60 +747,47 @@ static int tvp514x_s_ctrl(struct v4l2_ctrl *ctrl)
}
/**
- * tvp514x_g_parm() - V4L2 decoder interface handler for g_parm
+ * tvp514x_g_frame_interval() - V4L2 decoder interface handler
* @sd: pointer to standard V4L2 sub-device structure
- * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
+ * @ival: pointer to a v4l2_subdev_frame_interval structure
*
* Returns the decoder's video CAPTURE parameters.
*/
static int
-tvp514x_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+tvp514x_g_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *ival)
{
struct tvp514x_decoder *decoder = to_decoder(sd);
- struct v4l2_captureparm *cparm;
enum tvp514x_std current_std;
- if (a == NULL)
- return -EINVAL;
-
- if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- /* only capture is supported */
- return -EINVAL;
/* get the current standard */
current_std = decoder->current_std;
- cparm = &a->parm.capture;
- cparm->capability = V4L2_CAP_TIMEPERFRAME;
- cparm->timeperframe =
+ ival->interval =
decoder->std_list[current_std].standard.frameperiod;
return 0;
}
/**
- * tvp514x_s_parm() - V4L2 decoder interface handler for s_parm
+ * tvp514x_s_frame_interval() - V4L2 decoder interface handler
* @sd: pointer to standard V4L2 sub-device structure
- * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure
+ * @ival: pointer to a v4l2_subdev_frame_interval structure
*
* Configures the decoder to use the input parameters, if possible. If
* not possible, returns the appropriate error code.
*/
static int
-tvp514x_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+tvp514x_s_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *ival)
{
struct tvp514x_decoder *decoder = to_decoder(sd);
struct v4l2_fract *timeperframe;
enum tvp514x_std current_std;
- if (a == NULL)
- return -EINVAL;
-
- if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- /* only capture is supported */
- return -EINVAL;
- timeperframe = &a->parm.capture.timeperframe;
+ timeperframe = &ival->interval;
/* get the current standard */
current_std = decoder->current_std;
@@ -961,8 +948,8 @@ static const struct v4l2_subdev_video_ops tvp514x_video_ops = {
.s_std = tvp514x_s_std,
.s_routing = tvp514x_s_routing,
.querystd = tvp514x_querystd,
- .g_parm = tvp514x_g_parm,
- .s_parm = tvp514x_s_parm,
+ .g_frame_interval = tvp514x_g_frame_interval,
+ .s_frame_interval = tvp514x_s_frame_interval,
.s_stream = tvp514x_s_stream,
};
diff --git a/drivers/media/i2c/tw9910.c b/drivers/media/i2c/tw9910.c
new file mode 100644
index 000000000000..a54548cc4285
--- /dev/null
+++ b/drivers/media/i2c/tw9910.c
@@ -0,0 +1,1027 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * tw9910 Video Driver
+ *
+ * Copyright (C) 2017 Jacopo Mondi <jacopo+renesas@jmondi.org>
+ *
+ * Copyright (C) 2008 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on ov772x driver,
+ *
+ * Copyright (C) 2008 Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
+ * Copyright (C) 2008 Magnus Damm
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/videodev2.h>
+
+#include <media/i2c/tw9910.h>
+#include <media/v4l2-subdev.h>
+
+#define GET_ID(val) ((val & 0xF8) >> 3)
+#define GET_REV(val) (val & 0x07)
+
+/*
+ * register offset
+ */
+#define ID 0x00 /* Product ID Code Register */
+#define STATUS1 0x01 /* Chip Status Register I */
+#define INFORM 0x02 /* Input Format */
+#define OPFORM 0x03 /* Output Format Control Register */
+#define DLYCTR 0x04 /* Hysteresis and HSYNC Delay Control */
+#define OUTCTR1 0x05 /* Output Control I */
+#define ACNTL1 0x06 /* Analog Control Register 1 */
+#define CROP_HI 0x07 /* Cropping Register, High */
+#define VDELAY_LO 0x08 /* Vertical Delay Register, Low */
+#define VACTIVE_LO 0x09 /* Vertical Active Register, Low */
+#define HDELAY_LO 0x0A /* Horizontal Delay Register, Low */
+#define HACTIVE_LO 0x0B /* Horizontal Active Register, Low */
+#define CNTRL1 0x0C /* Control Register I */
+#define VSCALE_LO 0x0D /* Vertical Scaling Register, Low */
+#define SCALE_HI 0x0E /* Scaling Register, High */
+#define HSCALE_LO 0x0F /* Horizontal Scaling Register, Low */
+#define BRIGHT 0x10 /* BRIGHTNESS Control Register */
+#define CONTRAST 0x11 /* CONTRAST Control Register */
+#define SHARPNESS 0x12 /* SHARPNESS Control Register I */
+#define SAT_U 0x13 /* Chroma (U) Gain Register */
+#define SAT_V 0x14 /* Chroma (V) Gain Register */
+#define HUE 0x15 /* Hue Control Register */
+#define CORING1 0x17
+#define CORING2 0x18 /* Coring and IF compensation */
+#define VBICNTL 0x19 /* VBI Control Register */
+#define ACNTL2 0x1A /* Analog Control 2 */
+#define OUTCTR2 0x1B /* Output Control 2 */
+#define SDT 0x1C /* Standard Selection */
+#define SDTR 0x1D /* Standard Recognition */
+#define TEST 0x1F /* Test Control Register */
+#define CLMPG 0x20 /* Clamping Gain */
+#define IAGC 0x21 /* Individual AGC Gain */
+#define AGCGAIN 0x22 /* AGC Gain */
+#define PEAKWT 0x23 /* White Peak Threshold */
+#define CLMPL 0x24 /* Clamp level */
+#define SYNCT 0x25 /* Sync Amplitude */
+#define MISSCNT 0x26 /* Sync Miss Count Register */
+#define PCLAMP 0x27 /* Clamp Position Register */
+#define VCNTL1 0x28 /* Vertical Control I */
+#define VCNTL2 0x29 /* Vertical Control II */
+#define CKILL 0x2A /* Color Killer Level Control */
+#define COMB 0x2B /* Comb Filter Control */
+#define LDLY 0x2C /* Luma Delay and H Filter Control */
+#define MISC1 0x2D /* Miscellaneous Control I */
+#define LOOP 0x2E /* LOOP Control Register */
+#define MISC2 0x2F /* Miscellaneous Control II */
+#define MVSN 0x30 /* Macrovision Detection */
+#define STATUS2 0x31 /* Chip STATUS II */
+#define HFREF 0x32 /* H monitor */
+#define CLMD 0x33 /* CLAMP MODE */
+#define IDCNTL 0x34 /* ID Detection Control */
+#define CLCNTL1 0x35 /* Clamp Control I */
+#define ANAPLLCTL 0x4C
+#define VBIMIN 0x4D
+#define HSLOWCTL 0x4E
+#define WSS3 0x4F
+#define FILLDATA 0x50
+#define SDID 0x51
+#define DID 0x52
+#define WSS1 0x53
+#define WSS2 0x54
+#define VVBI 0x55
+#define LCTL6 0x56
+#define LCTL7 0x57
+#define LCTL8 0x58
+#define LCTL9 0x59
+#define LCTL10 0x5A
+#define LCTL11 0x5B
+#define LCTL12 0x5C
+#define LCTL13 0x5D
+#define LCTL14 0x5E
+#define LCTL15 0x5F
+#define LCTL16 0x60
+#define LCTL17 0x61
+#define LCTL18 0x62
+#define LCTL19 0x63
+#define LCTL20 0x64
+#define LCTL21 0x65
+#define LCTL22 0x66
+#define LCTL23 0x67
+#define LCTL24 0x68
+#define LCTL25 0x69
+#define LCTL26 0x6A
+#define HSBEGIN 0x6B
+#define HSEND 0x6C
+#define OVSDLY 0x6D
+#define OVSEND 0x6E
+#define VBIDELAY 0x6F
+
+/*
+ * register detail
+ */
+
+/* INFORM */
+#define FC27_ON 0x40 /* 1 : Input crystal clock frequency is 27MHz */
+#define FC27_FF 0x00 /* 0 : Square pixel mode. */
+ /* Must use 24.54MHz for 60Hz field rate */
+ /* source or 29.5MHz for 50Hz field rate */
+#define IFSEL_S 0x10 /* 01 : S-video decoding */
+#define IFSEL_C 0x00 /* 00 : Composite video decoding */
+ /* Y input video selection */
+#define YSEL_M0 0x00 /* 00 : Mux0 selected */
+#define YSEL_M1 0x04 /* 01 : Mux1 selected */
+#define YSEL_M2 0x08 /* 10 : Mux2 selected */
+#define YSEL_M3 0x10 /* 11 : Mux3 selected */
+
+/* OPFORM */
+#define MODE 0x80 /* 0 : CCIR601 compatible YCrCb 4:2:2 format */
+ /* 1 : ITU-R-656 compatible data sequence format */
+#define LEN 0x40 /* 0 : 8-bit YCrCb 4:2:2 output format */
+ /* 1 : 16-bit YCrCb 4:2:2 output format.*/
+#define LLCMODE 0x20 /* 1 : LLC output mode. */
+ /* 0 : free-run output mode */
+#define AINC 0x10 /* Serial interface auto-indexing control */
+ /* 0 : auto-increment */
+ /* 1 : non-auto */
+#define VSCTL 0x08 /* 1 : Vertical out ctrl by DVALID */
+ /* 0 : Vertical out ctrl by HACTIVE and DVALID */
+#define OEN_TRI_SEL_MASK 0x07
+#define OEN_TRI_SEL_ALL_ON 0x00 /* Enable output for Rev0/Rev1 */
+#define OEN_TRI_SEL_ALL_OFF_r0 0x06 /* All tri-stated for Rev0 */
+#define OEN_TRI_SEL_ALL_OFF_r1 0x07 /* All tri-stated for Rev1 */
+
+/* OUTCTR1 */
+#define VSP_LO 0x00 /* 0 : VS pin output polarity is active low */
+#define VSP_HI 0x80 /* 1 : VS pin output polarity is active high. */
+ /* VS pin output control */
+#define VSSL_VSYNC 0x00 /* 0 : VSYNC */
+#define VSSL_VACT 0x10 /* 1 : VACT */
+#define VSSL_FIELD 0x20 /* 2 : FIELD */
+#define VSSL_VVALID 0x30 /* 3 : VVALID */
+#define VSSL_ZERO 0x70 /* 7 : 0 */
+#define HSP_LOW 0x00 /* 0 : HS pin output polarity is active low */
+#define HSP_HI 0x08 /* 1 : HS pin output polarity is active high.*/
+ /* HS pin output control */
+#define HSSL_HACT 0x00 /* 0 : HACT */
+#define HSSL_HSYNC 0x01 /* 1 : HSYNC */
+#define HSSL_DVALID 0x02 /* 2 : DVALID */
+#define HSSL_HLOCK 0x03 /* 3 : HLOCK */
+#define HSSL_ASYNCW 0x04 /* 4 : ASYNCW */
+#define HSSL_ZERO 0x07 /* 7 : 0 */
+
+/* ACNTL1 */
+#define SRESET 0x80 /* resets the device to its default state
+ * but all register content remain unchanged.
+ * This bit is self-resetting.
+ */
+#define ACNTL1_PDN_MASK 0x0e
+#define CLK_PDN 0x08 /* system clock power down */
+#define Y_PDN 0x04 /* Luma ADC power down */
+#define C_PDN 0x02 /* Chroma ADC power down */
+
+/* ACNTL2 */
+#define ACNTL2_PDN_MASK 0x40
+#define PLL_PDN 0x40 /* PLL power down */
+
+/* VBICNTL */
+
+/* RTSEL : control the real time signal output from the MPOUT pin */
+#define RTSEL_MASK 0x07
+#define RTSEL_VLOSS 0x00 /* 0000 = Video loss */
+#define RTSEL_HLOCK 0x01 /* 0001 = H-lock */
+#define RTSEL_SLOCK 0x02 /* 0010 = S-lock */
+#define RTSEL_VLOCK 0x03 /* 0011 = V-lock */
+#define RTSEL_MONO 0x04 /* 0100 = MONO */
+#define RTSEL_DET50 0x05 /* 0101 = DET50 */
+#define RTSEL_FIELD 0x06 /* 0110 = FIELD */
+#define RTSEL_RTCO 0x07 /* 0111 = RTCO ( Real Time Control ) */
+
+/* HSYNC start and end are constant for now */
+#define HSYNC_START 0x0260
+#define HSYNC_END 0x0300
+
+/*
+ * structure
+ */
+
+struct regval_list {
+ unsigned char reg_num;
+ unsigned char value;
+};
+
+struct tw9910_scale_ctrl {
+ char *name;
+ unsigned short width;
+ unsigned short height;
+ u16 hscale;
+ u16 vscale;
+};
+
+struct tw9910_priv {
+ struct v4l2_subdev subdev;
+ struct clk *clk;
+ struct tw9910_video_info *info;
+ struct gpio_desc *pdn_gpio;
+ struct gpio_desc *rstb_gpio;
+ const struct tw9910_scale_ctrl *scale;
+ v4l2_std_id norm;
+ u32 revision;
+};
+
+static const struct tw9910_scale_ctrl tw9910_ntsc_scales[] = {
+ {
+ .name = "NTSC SQ",
+ .width = 640,
+ .height = 480,
+ .hscale = 0x0100,
+ .vscale = 0x0100,
+ },
+ {
+ .name = "NTSC CCIR601",
+ .width = 720,
+ .height = 480,
+ .hscale = 0x0100,
+ .vscale = 0x0100,
+ },
+ {
+ .name = "NTSC SQ (CIF)",
+ .width = 320,
+ .height = 240,
+ .hscale = 0x0200,
+ .vscale = 0x0200,
+ },
+ {
+ .name = "NTSC CCIR601 (CIF)",
+ .width = 360,
+ .height = 240,
+ .hscale = 0x0200,
+ .vscale = 0x0200,
+ },
+ {
+ .name = "NTSC SQ (QCIF)",
+ .width = 160,
+ .height = 120,
+ .hscale = 0x0400,
+ .vscale = 0x0400,
+ },
+ {
+ .name = "NTSC CCIR601 (QCIF)",
+ .width = 180,
+ .height = 120,
+ .hscale = 0x0400,
+ .vscale = 0x0400,
+ },
+};
+
+static const struct tw9910_scale_ctrl tw9910_pal_scales[] = {
+ {
+ .name = "PAL SQ",
+ .width = 768,
+ .height = 576,
+ .hscale = 0x0100,
+ .vscale = 0x0100,
+ },
+ {
+ .name = "PAL CCIR601",
+ .width = 720,
+ .height = 576,
+ .hscale = 0x0100,
+ .vscale = 0x0100,
+ },
+ {
+ .name = "PAL SQ (CIF)",
+ .width = 384,
+ .height = 288,
+ .hscale = 0x0200,
+ .vscale = 0x0200,
+ },
+ {
+ .name = "PAL CCIR601 (CIF)",
+ .width = 360,
+ .height = 288,
+ .hscale = 0x0200,
+ .vscale = 0x0200,
+ },
+ {
+ .name = "PAL SQ (QCIF)",
+ .width = 192,
+ .height = 144,
+ .hscale = 0x0400,
+ .vscale = 0x0400,
+ },
+ {
+ .name = "PAL CCIR601 (QCIF)",
+ .width = 180,
+ .height = 144,
+ .hscale = 0x0400,
+ .vscale = 0x0400,
+ },
+};
+
+/*
+ * general function
+ */
+static struct tw9910_priv *to_tw9910(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct tw9910_priv,
+ subdev);
+}
+
+static int tw9910_mask_set(struct i2c_client *client, u8 command,
+ u8 mask, u8 set)
+{
+ s32 val = i2c_smbus_read_byte_data(client, command);
+
+ if (val < 0)
+ return val;
+
+ val &= ~mask;
+ val |= set & mask;
+
+ return i2c_smbus_write_byte_data(client, command, val);
+}
+
+static int tw9910_set_scale(struct i2c_client *client,
+ const struct tw9910_scale_ctrl *scale)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, SCALE_HI,
+ (scale->vscale & 0x0F00) >> 4 |
+ (scale->hscale & 0x0F00) >> 8);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, HSCALE_LO,
+ scale->hscale & 0x00FF);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, VSCALE_LO,
+ scale->vscale & 0x00FF);
+
+ return ret;
+}
+
+static int tw9910_set_hsync(struct i2c_client *client)
+{
+ struct tw9910_priv *priv = to_tw9910(client);
+ int ret;
+
+ /* bit 10 - 3 */
+ ret = i2c_smbus_write_byte_data(client, HSBEGIN,
+ (HSYNC_START & 0x07F8) >> 3);
+ if (ret < 0)
+ return ret;
+
+ /* bit 10 - 3 */
+ ret = i2c_smbus_write_byte_data(client, HSEND,
+ (HSYNC_END & 0x07F8) >> 3);
+ if (ret < 0)
+ return ret;
+
+ /* So far only revisions 0 and 1 have been seen. */
+ /* bit 2 - 0 */
+ if (priv->revision == 1)
+ ret = tw9910_mask_set(client, HSLOWCTL, 0x77,
+ (HSYNC_START & 0x0007) << 4 |
+ (HSYNC_END & 0x0007));
+
+ return ret;
+}
+
+static void tw9910_reset(struct i2c_client *client)
+{
+ tw9910_mask_set(client, ACNTL1, SRESET, SRESET);
+ usleep_range(1000, 5000);
+}
+
+static int tw9910_power(struct i2c_client *client, int enable)
+{
+ int ret;
+ u8 acntl1;
+ u8 acntl2;
+
+ if (enable) {
+ acntl1 = 0;
+ acntl2 = 0;
+ } else {
+ acntl1 = CLK_PDN | Y_PDN | C_PDN;
+ acntl2 = PLL_PDN;
+ }
+
+ ret = tw9910_mask_set(client, ACNTL1, ACNTL1_PDN_MASK, acntl1);
+ if (ret < 0)
+ return ret;
+
+ return tw9910_mask_set(client, ACNTL2, ACNTL2_PDN_MASK, acntl2);
+}
+
+static const struct tw9910_scale_ctrl *tw9910_select_norm(v4l2_std_id norm,
+ u32 width, u32 height)
+{
+ const struct tw9910_scale_ctrl *scale;
+ const struct tw9910_scale_ctrl *ret = NULL;
+ __u32 diff = 0xffffffff, tmp;
+ int size, i;
+
+ if (norm & V4L2_STD_NTSC) {
+ scale = tw9910_ntsc_scales;
+ size = ARRAY_SIZE(tw9910_ntsc_scales);
+ } else if (norm & V4L2_STD_PAL) {
+ scale = tw9910_pal_scales;
+ size = ARRAY_SIZE(tw9910_pal_scales);
+ } else {
+ return NULL;
+ }
+
+ for (i = 0; i < size; i++) {
+ tmp = abs(width - scale[i].width) +
+ abs(height - scale[i].height);
+ if (tmp < diff) {
+ diff = tmp;
+ ret = scale + i;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * subdevice operations
+ */
+static int tw9910_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tw9910_priv *priv = to_tw9910(client);
+ u8 val;
+ int ret;
+
+ if (!enable) {
+ switch (priv->revision) {
+ case 0:
+ val = OEN_TRI_SEL_ALL_OFF_r0;
+ break;
+ case 1:
+ val = OEN_TRI_SEL_ALL_OFF_r1;
+ break;
+ default:
+ dev_err(&client->dev, "un-supported revision\n");
+ return -EINVAL;
+ }
+ } else {
+ val = OEN_TRI_SEL_ALL_ON;
+
+ if (!priv->scale) {
+ dev_err(&client->dev, "norm select error\n");
+ return -EPERM;
+ }
+
+ dev_dbg(&client->dev, "%s %dx%d\n",
+ priv->scale->name,
+ priv->scale->width,
+ priv->scale->height);
+ }
+
+ ret = tw9910_mask_set(client, OPFORM, OEN_TRI_SEL_MASK, val);
+ if (ret < 0)
+ return ret;
+
+ return tw9910_power(client, enable);
+}
+
+static int tw9910_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tw9910_priv *priv = to_tw9910(client);
+
+ *norm = priv->norm;
+
+ return 0;
+}
+
+static int tw9910_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tw9910_priv *priv = to_tw9910(client);
+ const unsigned int hact = 720;
+ const unsigned int hdelay = 15;
+ unsigned int vact;
+ unsigned int vdelay;
+ int ret;
+
+ if (!(norm & (V4L2_STD_NTSC | V4L2_STD_PAL)))
+ return -EINVAL;
+
+ priv->norm = norm;
+ if (norm & V4L2_STD_525_60) {
+ vact = 240;
+ vdelay = 18;
+ ret = tw9910_mask_set(client, VVBI, 0x10, 0x10);
+ } else {
+ vact = 288;
+ vdelay = 24;
+ ret = tw9910_mask_set(client, VVBI, 0x10, 0x00);
+ }
+ if (!ret)
+ ret = i2c_smbus_write_byte_data(client, CROP_HI,
+ ((vdelay >> 2) & 0xc0) |
+ ((vact >> 4) & 0x30) |
+ ((hdelay >> 6) & 0x0c) |
+ ((hact >> 8) & 0x03));
+ if (!ret)
+ ret = i2c_smbus_write_byte_data(client, VDELAY_LO,
+ vdelay & 0xff);
+ if (!ret)
+ ret = i2c_smbus_write_byte_data(client, VACTIVE_LO,
+ vact & 0xff);
+
+ return ret;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int tw9910_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ if (reg->reg > 0xff)
+ return -EINVAL;
+
+ reg->size = 1;
+ ret = i2c_smbus_read_byte_data(client, reg->reg);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * ret = int
+ * reg->val = __u64
+ */
+ reg->val = (__u64)ret;
+
+ return 0;
+}
+
+static int tw9910_s_register(struct v4l2_subdev *sd,
+ const struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->reg > 0xff ||
+ reg->val > 0xff)
+ return -EINVAL;
+
+ return i2c_smbus_write_byte_data(client, reg->reg, reg->val);
+}
+#endif
+
+static int tw9910_power_on(struct tw9910_priv *priv)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev);
+ int ret;
+
+ if (priv->clk) {
+ ret = clk_prepare_enable(priv->clk);
+ if (ret)
+ return ret;
+ }
+
+ if (priv->pdn_gpio) {
+ gpiod_set_value(priv->pdn_gpio, 0);
+ usleep_range(500, 1000);
+ }
+
+ /*
+ * FIXME: The reset signal is connected to a shared GPIO on some
+ * platforms (namely the SuperH Migo-R). Until a framework becomes
+ * available to handle this cleanly, request the GPIO temporarily
+ * to avoid conflicts.
+ */
+ priv->rstb_gpio = gpiod_get_optional(&client->dev, "rstb",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(priv->rstb_gpio)) {
+ dev_info(&client->dev, "Unable to get GPIO \"rstb\"");
+ return PTR_ERR(priv->rstb_gpio);
+ }
+
+ if (priv->rstb_gpio) {
+ gpiod_set_value(priv->rstb_gpio, 1);
+ usleep_range(500, 1000);
+ gpiod_set_value(priv->rstb_gpio, 0);
+ usleep_range(500, 1000);
+
+ gpiod_put(priv->rstb_gpio);
+ }
+
+ return 0;
+}
+
+static int tw9910_power_off(struct tw9910_priv *priv)
+{
+ clk_disable_unprepare(priv->clk);
+
+ if (priv->pdn_gpio) {
+ gpiod_set_value(priv->pdn_gpio, 1);
+ usleep_range(500, 1000);
+ }
+
+ return 0;
+}
+
+static int tw9910_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tw9910_priv *priv = to_tw9910(client);
+
+ return on ? tw9910_power_on(priv) : tw9910_power_off(priv);
+}
+
+static int tw9910_set_frame(struct v4l2_subdev *sd, u32 *width, u32 *height)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tw9910_priv *priv = to_tw9910(client);
+ int ret = -EINVAL;
+ u8 val;
+
+ /* Select suitable norm. */
+ priv->scale = tw9910_select_norm(priv->norm, *width, *height);
+ if (!priv->scale)
+ goto tw9910_set_fmt_error;
+
+ /* Reset hardware. */
+ tw9910_reset(client);
+
+ /* Set bus width. */
+ val = 0x00;
+ if (priv->info->buswidth == 16)
+ val = LEN;
+
+ ret = tw9910_mask_set(client, OPFORM, LEN, val);
+ if (ret < 0)
+ goto tw9910_set_fmt_error;
+
+ /* Select MPOUT behavior. */
+ switch (priv->info->mpout) {
+ case TW9910_MPO_VLOSS:
+ val = RTSEL_VLOSS; break;
+ case TW9910_MPO_HLOCK:
+ val = RTSEL_HLOCK; break;
+ case TW9910_MPO_SLOCK:
+ val = RTSEL_SLOCK; break;
+ case TW9910_MPO_VLOCK:
+ val = RTSEL_VLOCK; break;
+ case TW9910_MPO_MONO:
+ val = RTSEL_MONO; break;
+ case TW9910_MPO_DET50:
+ val = RTSEL_DET50; break;
+ case TW9910_MPO_FIELD:
+ val = RTSEL_FIELD; break;
+ case TW9910_MPO_RTCO:
+ val = RTSEL_RTCO; break;
+ default:
+ val = 0;
+ }
+
+ ret = tw9910_mask_set(client, VBICNTL, RTSEL_MASK, val);
+ if (ret < 0)
+ goto tw9910_set_fmt_error;
+
+ /* Set scale. */
+ ret = tw9910_set_scale(client, priv->scale);
+ if (ret < 0)
+ goto tw9910_set_fmt_error;
+
+ /* Set hsync. */
+ ret = tw9910_set_hsync(client);
+ if (ret < 0)
+ goto tw9910_set_fmt_error;
+
+ *width = priv->scale->width;
+ *height = priv->scale->height;
+
+ return ret;
+
+tw9910_set_fmt_error:
+
+ tw9910_reset(client);
+ priv->scale = NULL;
+
+ return ret;
+}
+
+static int tw9910_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_selection *sel)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tw9910_priv *priv = to_tw9910(client);
+
+ if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+ /* Only CROP, CROP_DEFAULT and CROP_BOUNDS are supported. */
+ if (sel->target > V4L2_SEL_TGT_CROP_BOUNDS)
+ return -EINVAL;
+
+ sel->r.left = 0;
+ sel->r.top = 0;
+ if (priv->norm & V4L2_STD_NTSC) {
+ sel->r.width = 640;
+ sel->r.height = 480;
+ } else {
+ sel->r.width = 768;
+ sel->r.height = 576;
+ }
+
+ return 0;
+}
+
+static int tw9910_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *mf = &format->format;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tw9910_priv *priv = to_tw9910(client);
+
+ if (format->pad)
+ return -EINVAL;
+
+ if (!priv->scale) {
+ priv->scale = tw9910_select_norm(priv->norm, 640, 480);
+ if (!priv->scale)
+ return -EINVAL;
+ }
+
+ mf->width = priv->scale->width;
+ mf->height = priv->scale->height;
+ mf->code = MEDIA_BUS_FMT_UYVY8_2X8;
+ mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ mf->field = V4L2_FIELD_INTERLACED_BT;
+
+ return 0;
+}
+
+static int tw9910_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ u32 width = mf->width, height = mf->height;
+ int ret;
+
+ WARN_ON(mf->field != V4L2_FIELD_ANY &&
+ mf->field != V4L2_FIELD_INTERLACED_BT);
+
+ /* Check color format. */
+ if (mf->code != MEDIA_BUS_FMT_UYVY8_2X8)
+ return -EINVAL;
+
+ mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+ ret = tw9910_set_frame(sd, &width, &height);
+ if (ret)
+ return ret;
+
+ mf->width = width;
+ mf->height = height;
+
+ return 0;
+}
+
+static int tw9910_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *mf = &format->format;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tw9910_priv *priv = to_tw9910(client);
+ const struct tw9910_scale_ctrl *scale;
+
+ if (format->pad)
+ return -EINVAL;
+
+ if (mf->field == V4L2_FIELD_ANY) {
+ mf->field = V4L2_FIELD_INTERLACED_BT;
+ } else if (mf->field != V4L2_FIELD_INTERLACED_BT) {
+ dev_err(&client->dev, "Field type %d invalid\n", mf->field);
+ return -EINVAL;
+ }
+
+ mf->code = MEDIA_BUS_FMT_UYVY8_2X8;
+ mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+ /* Select suitable norm. */
+ scale = tw9910_select_norm(priv->norm, mf->width, mf->height);
+ if (!scale)
+ return -EINVAL;
+
+ mf->width = scale->width;
+ mf->height = scale->height;
+
+ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ return tw9910_s_fmt(sd, mf);
+
+ cfg->try_fmt = *mf;
+
+ return 0;
+}
+
+static int tw9910_video_probe(struct i2c_client *client)
+{
+ struct tw9910_priv *priv = to_tw9910(client);
+ s32 id;
+ int ret;
+
+ /* TW9910 only use 8 or 16 bit bus width. */
+ if (priv->info->buswidth != 16 && priv->info->buswidth != 8) {
+ dev_err(&client->dev, "bus width error\n");
+ return -ENODEV;
+ }
+
+ ret = tw9910_s_power(&priv->subdev, 1);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Check and show Product ID.
+ * So far only revisions 0 and 1 have been seen.
+ */
+ id = i2c_smbus_read_byte_data(client, ID);
+ priv->revision = GET_REV(id);
+ id = GET_ID(id);
+
+ if (id != 0x0b || priv->revision > 0x01) {
+ dev_err(&client->dev, "Product ID error %x:%x\n",
+ id, priv->revision);
+ ret = -ENODEV;
+ goto done;
+ }
+
+ dev_info(&client->dev, "tw9910 Product ID %0x:%0x\n",
+ id, priv->revision);
+
+ priv->norm = V4L2_STD_NTSC;
+ priv->scale = &tw9910_ntsc_scales[0];
+
+done:
+ tw9910_s_power(&priv->subdev, 0);
+
+ return ret;
+}
+
+static const struct v4l2_subdev_core_ops tw9910_subdev_core_ops = {
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = tw9910_g_register,
+ .s_register = tw9910_s_register,
+#endif
+ .s_power = tw9910_s_power,
+};
+
+static int tw9910_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->pad || code->index)
+ return -EINVAL;
+
+ code->code = MEDIA_BUS_FMT_UYVY8_2X8;
+
+ return 0;
+}
+
+static int tw9910_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm)
+{
+ *norm = V4L2_STD_NTSC | V4L2_STD_PAL;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops tw9910_subdev_video_ops = {
+ .s_std = tw9910_s_std,
+ .g_std = tw9910_g_std,
+ .s_stream = tw9910_s_stream,
+ .g_tvnorms = tw9910_g_tvnorms,
+};
+
+static const struct v4l2_subdev_pad_ops tw9910_subdev_pad_ops = {
+ .enum_mbus_code = tw9910_enum_mbus_code,
+ .get_selection = tw9910_get_selection,
+ .get_fmt = tw9910_get_fmt,
+ .set_fmt = tw9910_set_fmt,
+};
+
+static const struct v4l2_subdev_ops tw9910_subdev_ops = {
+ .core = &tw9910_subdev_core_ops,
+ .video = &tw9910_subdev_video_ops,
+ .pad = &tw9910_subdev_pad_ops,
+};
+
+/*
+ * i2c_driver function
+ */
+
+static int tw9910_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+
+{
+ struct tw9910_priv *priv;
+ struct tw9910_video_info *info;
+ struct i2c_adapter *adapter =
+ to_i2c_adapter(client->dev.parent);
+ int ret;
+
+ if (!client->dev.platform_data) {
+ dev_err(&client->dev, "TW9910: missing platform data!\n");
+ return -EINVAL;
+ }
+
+ info = client->dev.platform_data;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&client->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE_DATA\n");
+ return -EIO;
+ }
+
+ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->info = info;
+
+ v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops);
+
+ priv->clk = clk_get(&client->dev, "xti");
+ if (PTR_ERR(priv->clk) == -ENOENT) {
+ priv->clk = NULL;
+ } else if (IS_ERR(priv->clk)) {
+ dev_err(&client->dev, "Unable to get xti clock\n");
+ return PTR_ERR(priv->clk);
+ }
+
+ priv->pdn_gpio = gpiod_get_optional(&client->dev, "pdn",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->pdn_gpio)) {
+ dev_info(&client->dev, "Unable to get GPIO \"pdn\"");
+ ret = PTR_ERR(priv->pdn_gpio);
+ goto error_clk_put;
+ }
+
+ ret = tw9910_video_probe(client);
+ if (ret < 0)
+ goto error_gpio_put;
+
+ ret = v4l2_async_register_subdev(&priv->subdev);
+ if (ret)
+ goto error_gpio_put;
+
+ return ret;
+
+error_gpio_put:
+ if (priv->pdn_gpio)
+ gpiod_put(priv->pdn_gpio);
+error_clk_put:
+ clk_put(priv->clk);
+
+ return ret;
+}
+
+static int tw9910_remove(struct i2c_client *client)
+{
+ struct tw9910_priv *priv = to_tw9910(client);
+
+ if (priv->pdn_gpio)
+ gpiod_put(priv->pdn_gpio);
+ clk_put(priv->clk);
+ v4l2_device_unregister_subdev(&priv->subdev);
+
+ return 0;
+}
+
+static const struct i2c_device_id tw9910_id[] = {
+ { "tw9910", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tw9910_id);
+
+static struct i2c_driver tw9910_i2c_driver = {
+ .driver = {
+ .name = "tw9910",
+ },
+ .probe = tw9910_probe,
+ .remove = tw9910_remove,
+ .id_table = tw9910_id,
+};
+
+module_i2c_driver(tw9910_i2c_driver);
+
+MODULE_DESCRIPTION("V4L2 driver for TW9910 video decoder");
+MODULE_AUTHOR("Kuninori Morimoto");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/vs6624.c b/drivers/media/i2c/vs6624.c
index 560738213c00..1658816a9844 100644
--- a/drivers/media/i2c/vs6624.c
+++ b/drivers/media/i2c/vs6624.c
@@ -657,31 +657,22 @@ static int vs6624_get_fmt(struct v4l2_subdev *sd,
return 0;
}
-static int vs6624_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+static int vs6624_g_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *ival)
{
struct vs6624 *sensor = to_vs6624(sd);
- struct v4l2_captureparm *cp = &parms->parm.capture;
- if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- memset(cp, 0, sizeof(*cp));
- cp->capability = V4L2_CAP_TIMEPERFRAME;
- cp->timeperframe.numerator = sensor->frame_rate.denominator;
- cp->timeperframe.denominator = sensor->frame_rate.numerator;
+ ival->interval.numerator = sensor->frame_rate.denominator;
+ ival->interval.denominator = sensor->frame_rate.numerator;
return 0;
}
-static int vs6624_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+static int vs6624_s_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *ival)
{
struct vs6624 *sensor = to_vs6624(sd);
- struct v4l2_captureparm *cp = &parms->parm.capture;
- struct v4l2_fract *tpf = &cp->timeperframe;
+ struct v4l2_fract *tpf = &ival->interval;
- if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- if (cp->extendedmode != 0)
- return -EINVAL;
if (tpf->numerator == 0 || tpf->denominator == 0
|| (tpf->denominator > tpf->numerator * MAX_FRAME_RATE)) {
@@ -738,8 +729,8 @@ static const struct v4l2_subdev_core_ops vs6624_core_ops = {
};
static const struct v4l2_subdev_video_ops vs6624_video_ops = {
- .s_parm = vs6624_s_parm,
- .g_parm = vs6624_g_parm,
+ .s_frame_interval = vs6624_s_frame_interval,
+ .g_frame_interval = vs6624_g_frame_interval,
.s_stream = vs6624_s_stream,
};
diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c
index e79f72b8b858..35e81f7c0d2f 100644
--- a/drivers/media/media-device.c
+++ b/drivers/media/media-device.c
@@ -190,6 +190,7 @@ static long media_device_enum_links(struct media_device *mdev,
ulink_desc++;
}
}
+ memset(links->reserved, 0, sizeof(links->reserved));
return 0;
}
@@ -218,6 +219,8 @@ static long media_device_setup_link(struct media_device *mdev,
if (link == NULL)
return -EINVAL;
+ memset(linkd->reserved, 0, sizeof(linkd->reserved));
+
/* Setup the link on both entities. */
return __media_entity_setup_link(link, linkd->flags);
}
@@ -255,7 +258,7 @@ static long media_device_get_topology(struct media_device *mdev,
memset(&kentity, 0, sizeof(kentity));
kentity.id = entity->graph_obj.id;
kentity.function = entity->function;
- strncpy(kentity.name, entity->name,
+ strlcpy(kentity.name, entity->name,
sizeof(kentity.name));
if (copy_to_user(uentity, &kentity, sizeof(kentity)))
@@ -263,6 +266,7 @@ static long media_device_get_topology(struct media_device *mdev,
uentity++;
}
topo->num_entities = i;
+ topo->reserved1 = 0;
/* Get interfaces and number of interfaces */
i = 0;
@@ -298,6 +302,7 @@ static long media_device_get_topology(struct media_device *mdev,
uintf++;
}
topo->num_interfaces = i;
+ topo->reserved2 = 0;
/* Get pads and number of pads */
i = 0;
@@ -324,6 +329,7 @@ static long media_device_get_topology(struct media_device *mdev,
upad++;
}
topo->num_pads = i;
+ topo->reserved3 = 0;
/* Get links and number of links */
i = 0;
@@ -355,6 +361,7 @@ static long media_device_get_topology(struct media_device *mdev,
ulink++;
}
topo->num_links = i;
+ topo->reserved4 = 0;
return ret;
}
diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c
index f7c6d64e6031..3498551e618e 100644
--- a/drivers/media/media-entity.c
+++ b/drivers/media/media-entity.c
@@ -64,22 +64,6 @@ static inline const char *intf_type(struct media_interface *intf)
return "v4l-swradio";
case MEDIA_INTF_T_V4L_TOUCH:
return "v4l-touch";
- case MEDIA_INTF_T_ALSA_PCM_CAPTURE:
- return "alsa-pcm-capture";
- case MEDIA_INTF_T_ALSA_PCM_PLAYBACK:
- return "alsa-pcm-playback";
- case MEDIA_INTF_T_ALSA_CONTROL:
- return "alsa-control";
- case MEDIA_INTF_T_ALSA_COMPRESS:
- return "alsa-compress";
- case MEDIA_INTF_T_ALSA_RAWMIDI:
- return "alsa-rawmidi";
- case MEDIA_INTF_T_ALSA_HWDEP:
- return "alsa-hwdep";
- case MEDIA_INTF_T_ALSA_SEQUENCER:
- return "alsa-sequencer";
- case MEDIA_INTF_T_ALSA_TIMER:
- return "alsa-timer";
default:
return "unknown-intf";
}
diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c
index f697698fe38d..707f57a9f940 100644
--- a/drivers/media/pci/bt8xx/bttv-driver.c
+++ b/drivers/media/pci/bt8xx/bttv-driver.c
@@ -3344,10 +3344,10 @@ static __poll_t radio_poll(struct file *file, poll_table *wait)
radio_enable(btv);
cmd.instance = file;
cmd.event_list = wait;
- cmd.result = res;
+ cmd.poll_mask = res;
bttv_call_all(btv, core, ioctl, SAA6588_CMD_POLL, &cmd);
- return cmd.result;
+ return cmd.poll_mask;
}
static const struct v4l2_file_operations radio_fops =
diff --git a/drivers/media/pci/bt8xx/bttv-input.c b/drivers/media/pci/bt8xx/bttv-input.c
index da49c5567db5..08266b23826e 100644
--- a/drivers/media/pci/bt8xx/bttv-input.c
+++ b/drivers/media/pci/bt8xx/bttv-input.c
@@ -332,11 +332,15 @@ static void bttv_ir_stop(struct bttv *btv)
static int get_key_pv951(struct IR_i2c *ir, enum rc_proto *protocol,
u32 *scancode, u8 *toggle)
{
+ int rc;
unsigned char b;
/* poll IR chip */
- if (1 != i2c_master_recv(ir->c, &b, 1)) {
+ rc = i2c_master_recv(ir->c, &b, 1);
+ if (rc != 1) {
dprintk("read error\n");
+ if (rc < 0)
+ return rc;
return -EIO;
}
diff --git a/drivers/media/pci/cobalt/Makefile b/drivers/media/pci/cobalt/Makefile
index b328955abbd2..29eddff2f35f 100644
--- a/drivers/media/pci/cobalt/Makefile
+++ b/drivers/media/pci/cobalt/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
cobalt-objs := cobalt-driver.o cobalt-irq.o cobalt-v4l2.o \
cobalt-i2c.o cobalt-omnitek.o cobalt-flash.o cobalt-cpld.o \
cobalt-alsa-main.o cobalt-alsa-pcm.o
diff --git a/drivers/media/pci/cobalt/cobalt-alsa-main.c b/drivers/media/pci/cobalt/cobalt-alsa-main.c
index 720e3ad93a9e..e5022b620856 100644
--- a/drivers/media/pci/cobalt/cobalt-alsa-main.c
+++ b/drivers/media/pci/cobalt/cobalt-alsa-main.c
@@ -1,21 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA interface to cobalt PCM capture streams
*
* Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates.
* All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/init.h>
diff --git a/drivers/media/pci/cobalt/cobalt-alsa-pcm.c b/drivers/media/pci/cobalt/cobalt-alsa-pcm.c
index b69b258d39b9..f6a7df13cd04 100644
--- a/drivers/media/pci/cobalt/cobalt-alsa-pcm.c
+++ b/drivers/media/pci/cobalt/cobalt-alsa-pcm.c
@@ -1,22 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA PCM device for the
* ALSA interface to cobalt PCM capture streams
*
* Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates.
* All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/init.h>
diff --git a/drivers/media/pci/cobalt/cobalt-alsa-pcm.h b/drivers/media/pci/cobalt/cobalt-alsa-pcm.h
index 513fb1f71794..0e2e9c63a23e 100644
--- a/drivers/media/pci/cobalt/cobalt-alsa-pcm.h
+++ b/drivers/media/pci/cobalt/cobalt-alsa-pcm.h
@@ -1,22 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ALSA PCM device for the
* ALSA interface to cobalt PCM capture streams
*
* Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates.
* All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
int snd_cobalt_pcm_create(struct snd_cobalt_card *cobsc);
diff --git a/drivers/media/pci/cobalt/cobalt-alsa.h b/drivers/media/pci/cobalt/cobalt-alsa.h
index 08db699ced37..bb7f156ad3e7 100644
--- a/drivers/media/pci/cobalt/cobalt-alsa.h
+++ b/drivers/media/pci/cobalt/cobalt-alsa.h
@@ -1,21 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ALSA interface to cobalt PCM capture streams
*
* Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates.
* All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
struct snd_card;
diff --git a/drivers/media/pci/cobalt/cobalt-cpld.c b/drivers/media/pci/cobalt/cobalt-cpld.c
index bfcecef659e3..3d8026483ac3 100644
--- a/drivers/media/pci/cobalt/cobalt-cpld.c
+++ b/drivers/media/pci/cobalt/cobalt-cpld.c
@@ -1,21 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Cobalt CPLD functions
*
* Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates.
* All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/delay.h>
diff --git a/drivers/media/pci/cobalt/cobalt-cpld.h b/drivers/media/pci/cobalt/cobalt-cpld.h
index 0fc88fd5fa7b..8c880ed14cda 100644
--- a/drivers/media/pci/cobalt/cobalt-cpld.h
+++ b/drivers/media/pci/cobalt/cobalt-cpld.h
@@ -1,21 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Cobalt CPLD functions
*
* Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates.
* All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
#ifndef COBALT_CPLD_H
diff --git a/drivers/media/pci/cobalt/cobalt-driver.c b/drivers/media/pci/cobalt/cobalt-driver.c
index 3f16cf3f6d74..c8b1a6206c65 100644
--- a/drivers/media/pci/cobalt/cobalt-driver.c
+++ b/drivers/media/pci/cobalt/cobalt-driver.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cobalt driver initialization and card probing
*
@@ -5,19 +6,6 @@
*
* Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates.
* All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/delay.h>
diff --git a/drivers/media/pci/cobalt/cobalt-driver.h b/drivers/media/pci/cobalt/cobalt-driver.h
index 00f773ec359a..429bee4ef79c 100644
--- a/drivers/media/pci/cobalt/cobalt-driver.h
+++ b/drivers/media/pci/cobalt/cobalt-driver.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* cobalt driver internal defines and structures
*
@@ -5,19 +6,6 @@
*
* Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates.
* All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
#ifndef COBALT_DRIVER_H
diff --git a/drivers/media/pci/cobalt/cobalt-flash.c b/drivers/media/pci/cobalt/cobalt-flash.c
index 04dcaf9198d2..ef96e0f956d2 100644
--- a/drivers/media/pci/cobalt/cobalt-flash.c
+++ b/drivers/media/pci/cobalt/cobalt-flash.c
@@ -1,21 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Cobalt NOR flash functions
*
* Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates.
* All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/mtd/mtd.h>
diff --git a/drivers/media/pci/cobalt/cobalt-flash.h b/drivers/media/pci/cobalt/cobalt-flash.h
index 8077daea51cd..605ce3d37ca3 100644
--- a/drivers/media/pci/cobalt/cobalt-flash.h
+++ b/drivers/media/pci/cobalt/cobalt-flash.h
@@ -1,21 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Cobalt NOR flash functions
*
* Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates.
* All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
#ifndef COBALT_FLASH_H
diff --git a/drivers/media/pci/cobalt/cobalt-i2c.c b/drivers/media/pci/cobalt/cobalt-i2c.c
index 1a5c55673ea8..c374dae78bf7 100644
--- a/drivers/media/pci/cobalt/cobalt-i2c.c
+++ b/drivers/media/pci/cobalt/cobalt-i2c.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cobalt I2C functions
*
@@ -5,19 +6,6 @@
*
* Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates.
* All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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 "cobalt-driver.h"
diff --git a/drivers/media/pci/cobalt/cobalt-i2c.h b/drivers/media/pci/cobalt/cobalt-i2c.h
index a4c1cfaacf95..7a9057c8bff6 100644
--- a/drivers/media/pci/cobalt/cobalt-i2c.h
+++ b/drivers/media/pci/cobalt/cobalt-i2c.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* cobalt I2C functions
*
@@ -5,19 +6,6 @@
*
* Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates.
* All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
/* init + register i2c algo-bit adapter */
diff --git a/drivers/media/pci/cobalt/cobalt-irq.c b/drivers/media/pci/cobalt/cobalt-irq.c
index b190d4f81c6e..04783e78cc12 100644
--- a/drivers/media/pci/cobalt/cobalt-irq.c
+++ b/drivers/media/pci/cobalt/cobalt-irq.c
@@ -1,21 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cobalt interrupt handling
*
* Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates.
* All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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 <media/i2c/adv7604.h>
diff --git a/drivers/media/pci/cobalt/cobalt-irq.h b/drivers/media/pci/cobalt/cobalt-irq.h
index 5119484a24d9..0b4078ce6555 100644
--- a/drivers/media/pci/cobalt/cobalt-irq.h
+++ b/drivers/media/pci/cobalt/cobalt-irq.h
@@ -1,21 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* cobalt interrupt handling
*
* Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates.
* All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/interrupt.h>
diff --git a/drivers/media/pci/cobalt/cobalt-omnitek.c b/drivers/media/pci/cobalt/cobalt-omnitek.c
index a28a8482c1d4..4c137453e679 100644
--- a/drivers/media/pci/cobalt/cobalt-omnitek.c
+++ b/drivers/media/pci/cobalt/cobalt-omnitek.c
@@ -1,21 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Omnitek Scatter-Gather DMA Controller
*
* Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates.
* All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/string.h>
diff --git a/drivers/media/pci/cobalt/cobalt-omnitek.h b/drivers/media/pci/cobalt/cobalt-omnitek.h
index e5c6d032c6f2..129c5fccbe39 100644
--- a/drivers/media/pci/cobalt/cobalt-omnitek.h
+++ b/drivers/media/pci/cobalt/cobalt-omnitek.h
@@ -1,21 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Omnitek Scatter-Gather DMA Controller
*
* Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates.
* All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
#ifndef COBALT_OMNITEK_H
diff --git a/drivers/media/pci/cobalt/cobalt-v4l2.c b/drivers/media/pci/cobalt/cobalt-v4l2.c
index def4a3b37084..e2a4c705d353 100644
--- a/drivers/media/pci/cobalt/cobalt-v4l2.c
+++ b/drivers/media/pci/cobalt/cobalt-v4l2.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cobalt V4L2 API
*
@@ -5,19 +6,6 @@
*
* Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates.
* All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/dma-mapping.h>
diff --git a/drivers/media/pci/cobalt/cobalt-v4l2.h b/drivers/media/pci/cobalt/cobalt-v4l2.h
index 62be553cd8e2..dc43974b2d86 100644
--- a/drivers/media/pci/cobalt/cobalt-v4l2.h
+++ b/drivers/media/pci/cobalt/cobalt-v4l2.h
@@ -1,21 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* cobalt V4L2 API
*
* Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates.
* All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
int cobalt_nodes_register(struct cobalt *cobalt);
diff --git a/drivers/media/pci/cobalt/m00233_video_measure_memmap_package.h b/drivers/media/pci/cobalt/m00233_video_measure_memmap_package.h
index 9bc9ef1fd3a8..4c6ad1cee87e 100644
--- a/drivers/media/pci/cobalt/m00233_video_measure_memmap_package.h
+++ b/drivers/media/pci/cobalt/m00233_video_measure_memmap_package.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates.
* All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
#ifndef M00233_VIDEO_MEASURE_MEMMAP_PACKAGE_H
diff --git a/drivers/media/pci/cobalt/m00235_fdma_packer_memmap_package.h b/drivers/media/pci/cobalt/m00235_fdma_packer_memmap_package.h
index a480529f561e..6cc1ad7d98c9 100644
--- a/drivers/media/pci/cobalt/m00235_fdma_packer_memmap_package.h
+++ b/drivers/media/pci/cobalt/m00235_fdma_packer_memmap_package.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates.
* All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
#ifndef M00235_FDMA_PACKER_MEMMAP_PACKAGE_H
diff --git a/drivers/media/pci/cobalt/m00389_cvi_memmap_package.h b/drivers/media/pci/cobalt/m00389_cvi_memmap_package.h
index 602419e589d3..f0c6fe304247 100644
--- a/drivers/media/pci/cobalt/m00389_cvi_memmap_package.h
+++ b/drivers/media/pci/cobalt/m00389_cvi_memmap_package.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates.
* All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
#ifndef M00389_CVI_MEMMAP_PACKAGE_H
diff --git a/drivers/media/pci/cobalt/m00460_evcnt_memmap_package.h b/drivers/media/pci/cobalt/m00460_evcnt_memmap_package.h
index 95471c995067..27f05aca632f 100644
--- a/drivers/media/pci/cobalt/m00460_evcnt_memmap_package.h
+++ b/drivers/media/pci/cobalt/m00460_evcnt_memmap_package.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates.
* All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
#ifndef M00460_EVCNT_MEMMAP_PACKAGE_H
diff --git a/drivers/media/pci/cobalt/m00473_freewheel_memmap_package.h b/drivers/media/pci/cobalt/m00473_freewheel_memmap_package.h
index 384a3e156301..8a5bf008750a 100644
--- a/drivers/media/pci/cobalt/m00473_freewheel_memmap_package.h
+++ b/drivers/media/pci/cobalt/m00473_freewheel_memmap_package.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates.
* All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
#ifndef M00473_FREEWHEEL_MEMMAP_PACKAGE_H
diff --git a/drivers/media/pci/cobalt/m00479_clk_loss_detector_memmap_package.h b/drivers/media/pci/cobalt/m00479_clk_loss_detector_memmap_package.h
index 2a029026bf82..18bd4fcd2db8 100644
--- a/drivers/media/pci/cobalt/m00479_clk_loss_detector_memmap_package.h
+++ b/drivers/media/pci/cobalt/m00479_clk_loss_detector_memmap_package.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates.
* All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
#ifndef M00479_CLK_LOSS_DETECTOR_MEMMAP_PACKAGE_H
diff --git a/drivers/media/pci/cobalt/m00514_syncgen_flow_evcnt_memmap_package.h b/drivers/media/pci/cobalt/m00514_syncgen_flow_evcnt_memmap_package.h
index bdef2df5d689..86da49033cd8 100644
--- a/drivers/media/pci/cobalt/m00514_syncgen_flow_evcnt_memmap_package.h
+++ b/drivers/media/pci/cobalt/m00514_syncgen_flow_evcnt_memmap_package.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates.
* All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
#ifndef M00514_SYNCGEN_FLOW_EVCNT_MEMMAP_PACKAGE_H
diff --git a/drivers/media/pci/cx18/cx18-alsa-main.c b/drivers/media/pci/cx18/cx18-alsa-main.c
index 2531e4b81b60..93443d1457c5 100644
--- a/drivers/media/pci/cx18/cx18-alsa-main.c
+++ b/drivers/media/pci/cx18/cx18-alsa-main.c
@@ -32,7 +32,6 @@
#include "cx18-driver.h"
#include "cx18-version.h"
#include "cx18-alsa.h"
-#include "cx18-alsa-mixer.h"
#include "cx18-alsa-pcm.h"
int cx18_alsa_debug;
diff --git a/drivers/media/pci/cx18/cx18-alsa-mixer.c b/drivers/media/pci/cx18/cx18-alsa-mixer.c
deleted file mode 100644
index cb04c3d820e2..000000000000
--- a/drivers/media/pci/cx18/cx18-alsa-mixer.c
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * ALSA mixer controls for the
- * ALSA interface to cx18 PCM capture streams
- *
- * Copyright (C) 2009 Andy Walls <awalls@md.metrocast.net>
- *
- * 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.
- */
-
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/device.h>
-#include <linux/spinlock.h>
-#include <linux/videodev2.h>
-
-#include <media/v4l2-device.h>
-
-#include <sound/core.h>
-#include <sound/control.h>
-#include <sound/tlv.h>
-
-#include "cx18-alsa.h"
-#include "cx18-driver.h"
-
-/*
- * Note the cx18-av-core volume scale is funny, due to the alignment of the
- * scale with another chip's range:
- *
- * v4l2_control value /512 indicated dB actual dB reg 0x8d4
- * 0x0000 - 0x01ff 0 -119 -96 228
- * 0x0200 - 0x02ff 1 -118 -96 228
- * ...
- * 0x2c00 - 0x2dff 22 -97 -96 228
- * 0x2e00 - 0x2fff 23 -96 -96 228
- * 0x3000 - 0x31ff 24 -95 -95 226
- * ...
- * 0xee00 - 0xefff 119 0 0 36
- * ...
- * 0xfe00 - 0xffff 127 +8 +8 20
- */
-static inline int dB_to_cx18_av_vol(int dB)
-{
- if (dB < -96)
- dB = -96;
- else if (dB > 8)
- dB = 8;
- return (dB + 119) << 9;
-}
-
-static inline int cx18_av_vol_to_dB(int v)
-{
- if (v < (23 << 9))
- v = (23 << 9);
- else if (v > (127 << 9))
- v = (127 << 9);
- return (v >> 9) - 119;
-}
-
-static int snd_cx18_mixer_tv_vol_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 1;
- /* We're already translating values, just keep this control in dB */
- uinfo->value.integer.min = -96;
- uinfo->value.integer.max = 8;
- uinfo->value.integer.step = 1;
- return 0;
-}
-
-static int snd_cx18_mixer_tv_vol_get(struct snd_kcontrol *kctl,
- struct snd_ctl_elem_value *uctl)
-{
- struct snd_cx18_card *cxsc = snd_kcontrol_chip(kctl);
- struct cx18 *cx = to_cx18(cxsc->v4l2_dev);
- struct v4l2_control vctrl;
- int ret;
-
- vctrl.id = V4L2_CID_AUDIO_VOLUME;
- vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]);
-
- snd_cx18_lock(cxsc);
- ret = v4l2_g_ctrl(cx->sd_av->ctrl_handler, &vctrl);
- snd_cx18_unlock(cxsc);
-
- if (!ret)
- uctl->value.integer.value[0] = cx18_av_vol_to_dB(vctrl.value);
- return ret;
-}
-
-static int snd_cx18_mixer_tv_vol_put(struct snd_kcontrol *kctl,
- struct snd_ctl_elem_value *uctl)
-{
- struct snd_cx18_card *cxsc = snd_kcontrol_chip(kctl);
- struct cx18 *cx = to_cx18(cxsc->v4l2_dev);
- struct v4l2_control vctrl;
- int ret;
-
- vctrl.id = V4L2_CID_AUDIO_VOLUME;
- vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]);
-
- snd_cx18_lock(cxsc);
-
- /* Fetch current state */
- ret = v4l2_g_ctrl(cx->sd_av->ctrl_handler, &vctrl);
-
- if (ret ||
- (cx18_av_vol_to_dB(vctrl.value) != uctl->value.integer.value[0])) {
-
- /* Set, if needed */
- vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]);
- ret = v4l2_s_ctrl(cx->sd_av->ctrl_handler, &vctrl);
- if (!ret)
- ret = 1; /* Indicate control was changed w/o error */
- }
- snd_cx18_unlock(cxsc);
-
- return ret;
-}
-
-
-/* This is a bit of overkill, the slider is already in dB internally */
-static DECLARE_TLV_DB_SCALE(snd_cx18_mixer_tv_vol_db_scale, -9600, 100, 0);
-
-static struct snd_kcontrol_new snd_cx18_mixer_tv_vol __initdata = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Analog TV Capture Volume",
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
- SNDRV_CTL_ELEM_ACCESS_TLV_READ,
- .info = snd_cx18_mixer_tv_volume_info,
- .get = snd_cx18_mixer_tv_volume_get,
- .put = snd_cx18_mixer_tv_volume_put,
- .tlv.p = snd_cx18_mixer_tv_vol_db_scale
-};
-
-/* FIXME - add mute switch and balance, bass, treble sliders:
- V4L2_CID_AUDIO_MUTE
-
- V4L2_CID_AUDIO_BALANCE
-
- V4L2_CID_AUDIO_BASS
- V4L2_CID_AUDIO_TREBLE
-*/
-
-/* FIXME - add stereo, lang1, lang2, mono menu */
-/* FIXME - add CS5345 I2S volume for HVR-1600 */
-
-int __init snd_cx18_mixer_create(struct snd_cx18_card *cxsc)
-{
- struct v4l2_device *v4l2_dev = cxsc->v4l2_dev;
- struct snd_card *sc = cxsc->sc;
- int ret;
-
- strlcpy(sc->mixername, "CX23418 Mixer", sizeof(sc->mixername));
-
- ret = snd_ctl_add(sc, snd_ctl_new1(&snd_cx18_mixer_tv_vol, cxsc));
- if (ret) {
- CX18_ALSA_WARN("%s: failed to add %s control, err %d\n",
- __func__, snd_cx18_mixer_tv_vol.name, ret);
- }
- return ret;
-}
diff --git a/drivers/media/pci/cx18/cx18-alsa-mixer.h b/drivers/media/pci/cx18/cx18-alsa-mixer.h
deleted file mode 100644
index 3aed123955dd..000000000000
--- a/drivers/media/pci/cx18/cx18-alsa-mixer.h
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * ALSA mixer controls for the
- * ALSA interface to cx18 PCM capture streams
- *
- * Copyright (C) 2009 Andy Walls <awalls@md.metrocast.net>
- *
- * 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.
- */
-
-int __init snd_cx18_mixer_create(struct snd_cx18_card *cxsc);
diff --git a/drivers/media/pci/cx18/cx18-dvb.c b/drivers/media/pci/cx18/cx18-dvb.c
index 53f4d6bf81fb..010f39eafce1 100644
--- a/drivers/media/pci/cx18/cx18-dvb.c
+++ b/drivers/media/pci/cx18/cx18-dvb.c
@@ -72,7 +72,7 @@ static struct s5h1409_config hauppauge_hvr1600_config = {
.qam_if = 44000,
.inversion = S5H1409_INVERSION_OFF,
.status_mode = S5H1409_DEMODLOCKING,
- .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK,
.hvr1600_opt = S5H1409_HVR1600_OPTIMIZE
};
@@ -86,7 +86,7 @@ static struct s5h1411_config hcw_s5h1411_config = {
.qam_if = S5H1411_IF_4000,
.inversion = S5H1411_INVERSION_ON,
.status_mode = S5H1411_DEMODLOCKING,
- .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+ .mpeg_timing = S5H1411_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK,
};
static struct tda18271_std_map hauppauge_tda18271_std_map = {
diff --git a/drivers/media/pci/cx23885/cx23885-alsa.c b/drivers/media/pci/cx23885/cx23885-alsa.c
index d8c3637e492e..20b3cb17f97f 100644
--- a/drivers/media/pci/cx23885/cx23885-alsa.c
+++ b/drivers/media/pci/cx23885/cx23885-alsa.c
@@ -89,9 +89,8 @@ static int cx23885_alsa_dma_init(struct cx23885_audio_dev *chip, int nr_pages)
return -ENOMEM;
}
- dprintk(1, "vmalloc is at addr 0x%08lx, size=%d\n",
- (unsigned long)buf->vaddr,
- nr_pages << PAGE_SHIFT);
+ dprintk(1, "vmalloc is at addr %p, size=%d\n",
+ buf->vaddr, nr_pages << PAGE_SHIFT);
memset(buf->vaddr, 0, nr_pages << PAGE_SHIFT);
buf->nr_pages = nr_pages;
diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c
index 3622521431f5..3a1c55187b2a 100644
--- a/drivers/media/pci/cx23885/cx23885-cards.c
+++ b/drivers/media/pci/cx23885/cx23885-cards.c
@@ -771,11 +771,46 @@ struct cx23885_board cx23885_boards[] = {
.portb = CX23885_MPEG_DVB,
.portc = CX23885_MPEG_DVB,
},
+ [CX23885_BOARD_HAUPPAUGE_QUADHD_DVB_885] = {
+ .name = "Hauppauge WinTV-QuadHD-DVB(885)",
+ .portb = CX23885_MPEG_DVB,
+ .portc = CX23885_MPEG_DVB,
+ },
[CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC] = {
.name = "Hauppauge WinTV-QuadHD-ATSC",
.portb = CX23885_MPEG_DVB,
.portc = CX23885_MPEG_DVB,
},
+ [CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC_885] = {
+ .name = "Hauppauge WinTV-QuadHD-ATSC(885)",
+ .portb = CX23885_MPEG_DVB,
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1265_K4] = {
+ .name = "Hauppauge WinTV-HVR-1265(161111)",
+ .porta = CX23885_ANALOG_VIDEO,
+ .portc = CX23885_MPEG_DVB,
+ .tuner_type = TUNER_ABSENT,
+ .force_bff = 1,
+ .input = {{
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN4_CH2 |
+ CX25840_VIN6_CH1,
+ .amux = CX25840_AUDIO7,
+ }, {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN4_CH2 |
+ CX25840_VIN8_CH1 |
+ CX25840_SVIDEO_ON,
+ .amux = CX25840_AUDIO7,
+ } },
+ },
+ [CX23885_BOARD_HAUPPAUGE_STARBURST2] = {
+ .name = "Hauppauge WinTV-Starburst2",
+ .portb = CX23885_MPEG_DVB,
+ },
};
const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
@@ -1028,6 +1063,10 @@ struct cx23885_subid cx23885_subids[] = {
.subdevice = 0x7133,
.card = CX23885_BOARD_HAUPPAUGE_IMPACTVCBE,
}, {
+ .subvendor = 0x0070,
+ .subdevice = 0x7137,
+ .card = CX23885_BOARD_HAUPPAUGE_IMPACTVCBE,
+ }, {
.subvendor = 0x18ac,
.subdevice = 0xdb98,
.card = CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP2,
@@ -1087,6 +1126,14 @@ struct cx23885_subid cx23885_subids[] = {
.subvendor = 0x0070,
.subdevice = 0x6b18,
.card = CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC, /* Tuner Pair 2 */
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x2a18,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1265_K4, /* Hauppauge WinTV HVR-1265 (Model 161xx1, Hybrid ATSC/QAM-B) */
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0xf02a,
+ .card = CX23885_BOARD_HAUPPAUGE_STARBURST2,
},
};
const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids);
@@ -1287,25 +1334,28 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data)
case 150329:
/* WinTV-HVR5525 (PCIe, DVB-S/S2, DVB-T/T2/C) */
break;
- case 166100:
+ case 161111:
+ /* WinTV-HVR-1265 K4 (PCIe, Analog/ATSC/QAM-B) */
+ break;
+ case 166100: /* 888 version, hybrid */
+ case 166200: /* 885 version, DVB only */
/* WinTV-QuadHD (DVB) Tuner Pair 1 (PCIe, IR, half height,
DVB-T/T2/C, DVB-T/T2/C */
break;
- case 166101:
+ case 166101: /* 888 version, hybrid */
+ case 166201: /* 885 version, DVB only */
/* WinTV-QuadHD (DVB) Tuner Pair 2 (PCIe, IR, half height,
DVB-T/T2/C, DVB-T/T2/C */
break;
- case 165100:
- /*
- * WinTV-QuadHD (ATSC) Tuner Pair 1 (PCIe, IR, half height,
- * ATSC, ATSC
- */
+ case 165100: /* 888 version, hybrid */
+ case 165200: /* 885 version, digital only */
+ /* WinTV-QuadHD (ATSC) Tuner Pair 1 (PCIe, IR, half height,
+ * ATSC/QAM-B, ATSC/QAM-B */
break;
- case 165101:
- /*
- * WinTV-QuadHD (DVB) Tuner Pair 2 (PCIe, IR, half height,
- * ATSC, ATSC
- */
+ case 165101: /* 888 version, hybrid */
+ case 165201: /* 885 version, digital only */
+ /* WinTV-QuadHD (ATSC) Tuner Pair 2 (PCIe, IR, half height,
+ * ATSC/QAM-B, ATSC/QAM-B */
break;
default:
pr_warn("%s: warning: unknown hauppauge model #%d\n",
@@ -1778,8 +1828,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
cx23885_gpio_set(dev, GPIO_2);
break;
case CX23885_BOARD_HAUPPAUGE_HVR5525:
- case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB:
- case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC:
+ case CX23885_BOARD_HAUPPAUGE_STARBURST2:
/*
* HVR5525 GPIO Details:
* GPIO-00 IR_WIDE
@@ -1809,6 +1858,22 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
* card does not have any GPIO's connected to subcomponents.
*/
break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1265_K4:
+ case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB:
+ case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB_885:
+ case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC:
+ case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC_885:
+ /*
+ * GPIO-08 TER1_RESN
+ * GPIO-09 TER2_RESN
+ */
+ /* Put the parts into reset and back */
+ cx23885_gpio_enable(dev, GPIO_8 | GPIO_9, 1);
+ cx23885_gpio_clear(dev, GPIO_8 | GPIO_9);
+ msleep(100);
+ cx23885_gpio_set(dev, GPIO_8 | GPIO_9);
+ msleep(100);
+ break;
}
}
@@ -2054,8 +2119,12 @@ void cx23885_card_setup(struct cx23885_dev *dev)
case CX23885_BOARD_HAUPPAUGE_STARBURST:
case CX23885_BOARD_HAUPPAUGE_IMPACTVCBE:
case CX23885_BOARD_HAUPPAUGE_HVR5525:
+ case CX23885_BOARD_HAUPPAUGE_HVR1265_K4:
+ case CX23885_BOARD_HAUPPAUGE_STARBURST2:
case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB:
+ case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB_885:
case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC:
+ case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC_885:
if (dev->i2c_bus[0].i2c_rc == 0)
hauppauge_eeprom(dev, eeprom+0xc0);
break;
@@ -2194,6 +2263,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
break;
case CX23885_BOARD_HAUPPAUGE_HVR5525:
+ case CX23885_BOARD_HAUPPAUGE_STARBURST2:
ts1->gen_ctrl_val = 0x5; /* Parallel */
ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
@@ -2201,8 +2271,11 @@ void cx23885_card_setup(struct cx23885_dev *dev)
ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1265_K4:
case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB:
+ case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB_885:
case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC:
+ case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC_885:
ts1->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
@@ -2259,6 +2332,9 @@ void cx23885_card_setup(struct cx23885_dev *dev)
case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
case CX23885_BOARD_HAUPPAUGE_HVR1255:
case CX23885_BOARD_HAUPPAUGE_HVR1255_22111:
+ case CX23885_BOARD_HAUPPAUGE_HVR1265_K4:
+ case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB:
+ case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC:
case CX23885_BOARD_HAUPPAUGE_HVR1270:
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_MYGICA_X8506:
@@ -2286,6 +2362,10 @@ void cx23885_card_setup(struct cx23885_dev *dev)
&dev->i2c_bus[2].i2c_adap,
"cx25840", 0x88 >> 1, NULL);
if (dev->sd_cx25840) {
+ /* set host data for clk_freq configuration */
+ v4l2_set_subdev_hostdata(dev->sd_cx25840,
+ &dev->clk_freq);
+
dev->sd_cx25840->grp_id = CX23885_HW_AV_CORE;
v4l2_subdev_call(dev->sd_cx25840, core, load_fw);
}
diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c
index 8f63df1cb418..019fac49db5b 100644
--- a/drivers/media/pci/cx23885/cx23885-core.c
+++ b/drivers/media/pci/cx23885/cx23885-core.c
@@ -839,10 +839,10 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
/* Configure the internal memory */
if (dev->pci->device == 0x8880) {
- /* Could be 887 or 888, assume a default */
- dev->bridge = CX23885_BRIDGE_887;
+ /* Could be 887 or 888, assume an 888 default */
+ dev->bridge = CX23885_BRIDGE_888;
/* Apply a sensible clock frequency for the PCIe bridge */
- dev->clk_freq = 25000000;
+ dev->clk_freq = 50000000;
dev->sram_channels = cx23887_sram_channels;
} else
if (dev->pci->device == 0x8852) {
@@ -869,10 +869,28 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
cx23885_card_list(dev);
}
+ if (dev->pci->device == 0x8852) {
+ /* no DIF on cx23885, so no analog tuner support possible */
+ if (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC)
+ dev->board = CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC_885;
+ else if (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_DVB)
+ dev->board = CX23885_BOARD_HAUPPAUGE_QUADHD_DVB_885;
+ }
+
/* If the user specific a clk freq override, apply it */
if (cx23885_boards[dev->board].clk_freq > 0)
dev->clk_freq = cx23885_boards[dev->board].clk_freq;
+ if (dev->board == CX23885_BOARD_HAUPPAUGE_IMPACTVCBE &&
+ dev->pci->subsystem_device == 0x7137) {
+ /* Hauppauge ImpactVCBe device ID 0x7137 is populated
+ * with an 888, and a 25Mhz crystal, instead of the
+ * usual third overtone 50Mhz. The default clock rate must
+ * be overridden so the cx25840 is properly configured
+ */
+ dev->clk_freq = 25000000;
+ }
+
dev->pci_bus = dev->pci->bus->number;
dev->pci_slot = PCI_SLOT(dev->pci->devfn);
cx23885_irq_add(dev, 0x001f00);
@@ -965,7 +983,7 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
cx23885_i2c_register(&dev->i2c_bus[1]);
cx23885_i2c_register(&dev->i2c_bus[2]);
cx23885_card_setup(dev);
- call_all(dev, core, s_power, 0);
+ call_all(dev, tuner, standby);
cx23885_ir_init(dev);
if (dev->board == CX23885_BOARD_VIEWCAST_460E) {
diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c
index 700422b538c0..114d9bcbe4f4 100644
--- a/drivers/media/pci/cx23885/cx23885-dvb.c
+++ b/drivers/media/pci/cx23885/cx23885-dvb.c
@@ -193,7 +193,7 @@ static struct s5h1409_config hauppauge_generic_config = {
.qam_if = 44000,
.inversion = S5H1409_INVERSION_OFF,
.status_mode = S5H1409_DEMODLOCKING,
- .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK,
};
static struct tda10048_config hauppauge_hvr1200_config = {
@@ -225,7 +225,7 @@ static struct s5h1409_config hauppauge_ezqam_config = {
.qam_if = 4000,
.inversion = S5H1409_INVERSION_ON,
.status_mode = S5H1409_DEMODLOCKING,
- .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK,
};
static struct s5h1409_config hauppauge_hvr1800lp_config = {
@@ -235,7 +235,7 @@ static struct s5h1409_config hauppauge_hvr1800lp_config = {
.qam_if = 44000,
.inversion = S5H1409_INVERSION_OFF,
.status_mode = S5H1409_DEMODLOCKING,
- .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK,
};
static struct s5h1409_config hauppauge_hvr1500_config = {
@@ -244,7 +244,7 @@ static struct s5h1409_config hauppauge_hvr1500_config = {
.gpio = S5H1409_GPIO_OFF,
.inversion = S5H1409_INVERSION_OFF,
.status_mode = S5H1409_DEMODLOCKING,
- .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK,
};
static struct mt2131_config hauppauge_generic_tunerconfig = {
@@ -264,7 +264,7 @@ static struct s5h1409_config hauppauge_hvr1500q_config = {
.qam_if = 44000,
.inversion = S5H1409_INVERSION_OFF,
.status_mode = S5H1409_DEMODLOCKING,
- .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK,
};
static struct s5h1409_config dvico_s5h1409_config = {
@@ -274,7 +274,7 @@ static struct s5h1409_config dvico_s5h1409_config = {
.qam_if = 44000,
.inversion = S5H1409_INVERSION_OFF,
.status_mode = S5H1409_DEMODLOCKING,
- .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK,
};
static struct s5h1411_config dvico_s5h1411_config = {
@@ -284,7 +284,7 @@ static struct s5h1411_config dvico_s5h1411_config = {
.vsb_if = S5H1411_IF_44000,
.inversion = S5H1411_INVERSION_OFF,
.status_mode = S5H1411_DEMODLOCKING,
- .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+ .mpeg_timing = S5H1411_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK,
};
static struct s5h1411_config hcw_s5h1411_config = {
@@ -294,7 +294,7 @@ static struct s5h1411_config hcw_s5h1411_config = {
.qam_if = S5H1411_IF_4000,
.inversion = S5H1411_INVERSION_ON,
.status_mode = S5H1411_DEMODLOCKING,
- .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+ .mpeg_timing = S5H1411_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK,
};
static struct xc5000_config hauppauge_hvr1500q_tunerconfig = {
@@ -930,6 +930,18 @@ static const struct m88ds3103_config hauppauge_hvr5525_m88ds3103_config = {
.agc = 0x99,
};
+static struct lgdt3306a_config hauppauge_hvr1265k4_config = {
+ .i2c_addr = 0x59,
+ .qam_if_khz = 4000,
+ .vsb_if_khz = 3250,
+ .deny_i2c_rptr = 1, /* Disabled */
+ .spectral_inversion = 0, /* Disabled */
+ .mpeg_mode = LGDT3306A_MPEG_SERIAL,
+ .tpclk_edge = LGDT3306A_TPCLK_RISING_EDGE,
+ .tpvalid_polarity = LGDT3306A_TP_VALID_HIGH,
+ .xtalMHz = 25, /* 24 or 25 */
+};
+
static int netup_altera_fpga_rw(void *device, int flag, int data, int read)
{
struct cx23885_dev *dev = (struct cx23885_dev *)device;
@@ -1194,6 +1206,8 @@ static int dvb_register(struct cx23885_tsport *port)
struct si2157_config si2157_config;
struct ts2020_config ts2020_config;
struct m88ds3103_platform_data m88ds3103_pdata;
+ struct m88rs6000t_config m88rs6000t_config = {};
+ struct a8293_platform_data a8293_pdata = {};
struct i2c_board_info info;
struct i2c_adapter *adapter;
struct i2c_client *client_demod = NULL, *client_tuner = NULL;
@@ -2217,9 +2231,10 @@ static int dvb_register(struct cx23885_tsport *port)
}
port->i2c_client_tuner = client_tuner;
break;
- case CX23885_BOARD_HAUPPAUGE_HVR5525: {
- struct m88rs6000t_config m88rs6000t_config;
- struct a8293_platform_data a8293_pdata = {};
+ case CX23885_BOARD_HAUPPAUGE_STARBURST2:
+ case CX23885_BOARD_HAUPPAUGE_HVR5525:
+ i2c_bus = &dev->i2c_bus[0];
+ i2c_bus2 = &dev->i2c_bus[1];
switch (port->nr) {
@@ -2228,7 +2243,7 @@ static int dvb_register(struct cx23885_tsport *port)
/* attach frontend */
fe0->dvb.frontend = dvb_attach(m88ds3103_attach,
&hauppauge_hvr5525_m88ds3103_config,
- &dev->i2c_bus[0].i2c_adap, &adapter);
+ &i2c_bus->i2c_adap, &adapter);
if (fe0->dvb.frontend == NULL)
break;
@@ -2239,7 +2254,7 @@ static int dvb_register(struct cx23885_tsport *port)
info.addr = 0x0b;
info.platform_data = &a8293_pdata;
request_module("a8293");
- client_sec = i2c_new_device(&dev->i2c_bus[0].i2c_adap, &info);
+ client_sec = i2c_new_device(&i2c_bus->i2c_adap, &info);
if (!client_sec || !client_sec->dev.driver)
goto frontend_detach;
if (!try_module_get(client_sec->dev.driver->owner)) {
@@ -2281,7 +2296,7 @@ static int dvb_register(struct cx23885_tsport *port)
info.addr = 0x64;
info.platform_data = &si2168_config;
request_module("%s", info.type);
- client_demod = i2c_new_device(&dev->i2c_bus[0].i2c_adap, &info);
+ client_demod = i2c_new_device(&i2c_bus->i2c_adap, &info);
if (!client_demod || !client_demod->dev.driver)
goto frontend_detach;
if (!try_module_get(client_demod->dev.driver->owner)) {
@@ -2299,7 +2314,7 @@ static int dvb_register(struct cx23885_tsport *port)
info.addr = 0x60;
info.platform_data = &si2157_config;
request_module("%s", info.type);
- client_tuner = i2c_new_device(&dev->i2c_bus[1].i2c_adap, &info);
+ client_tuner = i2c_new_device(&i2c_bus2->i2c_adap, &info);
if (!client_tuner || !client_tuner->dev.driver) {
module_put(client_demod->dev.driver->owner);
i2c_unregister_device(client_demod);
@@ -2317,8 +2332,10 @@ static int dvb_register(struct cx23885_tsport *port)
break;
}
break;
- }
case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB:
+ case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB_885:
+ pr_info("%s(): board=%d port=%d\n", __func__,
+ dev->board, port->nr);
switch (port->nr) {
/* port b - Terrestrial/cable */
case 1:
@@ -2416,6 +2433,9 @@ static int dvb_register(struct cx23885_tsport *port)
}
break;
case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC:
+ case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC_885:
+ pr_info("%s(): board=%d port=%d\n", __func__,
+ dev->board, port->nr);
switch (port->nr) {
/* port b - Terrestrial/cable */
case 1:
@@ -2490,7 +2510,41 @@ static int dvb_register(struct cx23885_tsport *port)
break;
}
break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1265_K4:
+ switch (port->nr) {
+ /* port c - Terrestrial/cable */
+ case 2:
+ /* attach frontend */
+ i2c_bus = &dev->i2c_bus[0];
+ fe0->dvb.frontend = dvb_attach(lgdt3306a_attach,
+ &hauppauge_hvr1265k4_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend == NULL)
+ break;
+
+ /* attach tuner */
+ memset(&si2157_config, 0, sizeof(si2157_config));
+ si2157_config.fe = fe0->dvb.frontend;
+ si2157_config.if_port = 1;
+ si2157_config.inversion = 1;
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "si2157", I2C_NAME_SIZE);
+ info.addr = 0x60;
+ info.platform_data = &si2157_config;
+ request_module("%s", info.type);
+ client_tuner = i2c_new_device(&dev->i2c_bus[1].i2c_adap, &info);
+ if (!client_tuner || !client_tuner->dev.driver)
+ goto frontend_detach;
+ if (!try_module_get(client_tuner->dev.driver->owner)) {
+ i2c_unregister_device(client_tuner);
+ client_tuner = NULL;
+ goto frontend_detach;
+ }
+ port->i2c_client_tuner = client_tuner;
+ break;
+ }
+ break;
default:
pr_info("%s: The frontend of your DVB/ATSC card isn't supported yet\n",
dev->name);
@@ -2514,8 +2568,8 @@ static int dvb_register(struct cx23885_tsport *port)
fe1->dvb.frontend->ops.ts_bus_ctrl = cx23885_dvb_bus_ctrl;
#endif
- /* Put the analog decoder in standby to keep it quiet */
- call_all(dev, core, s_power, 0);
+ /* Put the tuner in standby to keep it quiet */
+ call_all(dev, tuner, standby);
if (fe0->dvb.frontend->ops.analog_ops.standby)
fe0->dvb.frontend->ops.analog_ops.standby(fe0->dvb.frontend);
diff --git a/drivers/media/pci/cx23885/cx23885-input.c b/drivers/media/pci/cx23885/cx23885-input.c
index 0f4e54294bb7..be49589a61d2 100644
--- a/drivers/media/pci/cx23885/cx23885-input.c
+++ b/drivers/media/pci/cx23885/cx23885-input.c
@@ -94,6 +94,7 @@ void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events)
case CX23885_BOARD_DVBSKY_S950:
case CX23885_BOARD_DVBSKY_S952:
case CX23885_BOARD_DVBSKY_T982:
+ case CX23885_BOARD_HAUPPAUGE_HVR1265_K4:
/*
* The only boards we handle right now. However other boards
* using the CX2388x integrated IR controller should be similar
@@ -153,6 +154,7 @@ static int cx23885_input_ir_start(struct cx23885_dev *dev)
case CX23885_BOARD_DVBSKY_S950:
case CX23885_BOARD_DVBSKY_S952:
case CX23885_BOARD_DVBSKY_T982:
+ case CX23885_BOARD_HAUPPAUGE_HVR1265_K4:
/*
* The IR controller on this board only returns pulse widths.
* Any other mode setting will fail to set up the device.
@@ -283,6 +285,7 @@ int cx23885_input_init(struct cx23885_dev *dev)
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ case CX23885_BOARD_HAUPPAUGE_HVR1265_K4:
/* Integrated CX2388[58] IR controller */
allowed_protos = RC_PROTO_BIT_ALL_IR_DECODER;
/* The grey Hauppauge RC-5 remote */
diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c
index a03dcb662953..f8a3deadc77a 100644
--- a/drivers/media/pci/cx23885/cx23885-video.c
+++ b/drivers/media/pci/cx23885/cx23885-video.c
@@ -263,6 +263,7 @@ static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input)
(dev->board == CX23885_BOARD_HAUPPAUGE_IMPACTVCBE) ||
(dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255) ||
(dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255_22111) ||
+ (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1265_K4) ||
(dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850) ||
(dev->board == CX23885_BOARD_MYGICA_X8507) ||
(dev->board == CX23885_BOARD_AVERMEDIA_HC81R) ||
@@ -993,7 +994,8 @@ static int cx23885_set_freq_via_ops(struct cx23885_dev *dev,
if ((dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850) ||
(dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255) ||
- (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255_22111))
+ (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255_22111) ||
+ (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1265_K4))
fe = &dev->ts1.analog_fe;
if (fe && fe->ops.tuner_ops.set_analog_params) {
@@ -1022,6 +1024,7 @@ int cx23885_set_frequency(struct file *file, void *priv,
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1255:
case CX23885_BOARD_HAUPPAUGE_HVR1255_22111:
+ case CX23885_BOARD_HAUPPAUGE_HVR1265_K4:
case CX23885_BOARD_HAUPPAUGE_HVR1850:
ret = cx23885_set_freq_via_ops(dev, f);
break;
diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h
index 2a17209eb4f6..d54c7ee1ab21 100644
--- a/drivers/media/pci/cx23885/cx23885.h
+++ b/drivers/media/pci/cx23885/cx23885.h
@@ -107,6 +107,10 @@
#define CX23885_BOARD_VIEWCAST_460E 55
#define CX23885_BOARD_HAUPPAUGE_QUADHD_DVB 56
#define CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC 57
+#define CX23885_BOARD_HAUPPAUGE_HVR1265_K4 58
+#define CX23885_BOARD_HAUPPAUGE_STARBURST2 59
+#define CX23885_BOARD_HAUPPAUGE_QUADHD_DVB_885 60
+#define CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC_885 61
#define GPIO_0 0x00000001
#define GPIO_1 0x00000002
diff --git a/drivers/media/pci/cx25821/cx25821-core.c b/drivers/media/pci/cx25821/cx25821-core.c
index 04aa4a68a0ae..040c6c251d3a 100644
--- a/drivers/media/pci/cx25821/cx25821-core.c
+++ b/drivers/media/pci/cx25821/cx25821-core.c
@@ -867,6 +867,10 @@ static int cx25821_dev_setup(struct cx25821_dev *dev)
dev->nr = ++cx25821_devcount;
sprintf(dev->name, "cx25821[%d]", dev->nr);
+ if (dev->nr >= ARRAY_SIZE(card)) {
+ CX25821_INFO("dev->nr >= %zd", ARRAY_SIZE(card));
+ return -ENODEV;
+ }
if (dev->pci->device != 0x8210) {
pr_info("%s(): Exiting. Incorrect Hardware device = 0x%02x\n",
__func__, dev->pci->device);
@@ -882,9 +886,6 @@ static int cx25821_dev_setup(struct cx25821_dev *dev)
dev->channels[i].sram_channels = &cx25821_sram_channels[i];
}
- if (dev->nr > 1)
- CX25821_INFO("dev->nr > 1!");
-
/* board config */
dev->board = 1; /* card[dev->nr]; */
dev->_max_num_decoders = MAX_DECODERS;
diff --git a/drivers/media/pci/cx88/cx88-alsa.c b/drivers/media/pci/cx88/cx88-alsa.c
index 9740326bc93f..ab09bb55cf45 100644
--- a/drivers/media/pci/cx88/cx88-alsa.c
+++ b/drivers/media/pci/cx88/cx88-alsa.c
@@ -292,8 +292,8 @@ static int cx88_alsa_dma_init(struct cx88_audio_dev *chip, int nr_pages)
return -ENOMEM;
}
- dprintk(1, "vmalloc is at addr 0x%08lx, size=%d\n",
- (unsigned long)buf->vaddr, nr_pages << PAGE_SHIFT);
+ dprintk(1, "vmalloc is at addr %p, size=%d\n",
+ buf->vaddr, nr_pages << PAGE_SHIFT);
memset(buf->vaddr, 0, nr_pages << PAGE_SHIFT);
buf->nr_pages = nr_pages;
@@ -656,8 +656,8 @@ static void snd_cx88_wm8775_volume_put(struct snd_kcontrol *kcontrol,
{
struct cx88_audio_dev *chip = snd_kcontrol_chip(kcontrol);
struct cx88_core *core = chip->core;
- int left = value->value.integer.value[0];
- int right = value->value.integer.value[1];
+ u16 left = value->value.integer.value[0];
+ u16 right = value->value.integer.value[1];
int v, b;
/* Pass volume & balance onto any WM8775 */
diff --git a/drivers/media/pci/cx88/cx88-cards.c b/drivers/media/pci/cx88/cx88-cards.c
index 6df21b29ea17..4c92d2388c26 100644
--- a/drivers/media/pci/cx88/cx88-cards.c
+++ b/drivers/media/pci/cx88/cx88-cards.c
@@ -3592,7 +3592,7 @@ static void cx88_card_setup(struct cx88_core *core)
ctl.fname);
call_all(core, tuner, s_config, &xc2028_cfg);
}
- call_all(core, core, s_power, 0);
+ call_all(core, tuner, standby);
}
/* ------------------------------------------------------------------ */
diff --git a/drivers/media/pci/cx88/cx88-dvb.c b/drivers/media/pci/cx88/cx88-dvb.c
index 49a335f4603e..2f886140dd2e 100644
--- a/drivers/media/pci/cx88/cx88-dvb.c
+++ b/drivers/media/pci/cx88/cx88-dvb.c
@@ -558,7 +558,7 @@ static const struct s5h1409_config pinnacle_pctv_hd_800i_config = {
.qam_if = 44000,
.inversion = S5H1409_INVERSION_OFF,
.status_mode = S5H1409_DEMODLOCKING,
- .mpeg_timing = S5H1409_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK,
+ .mpeg_timing = S5H1409_MPEGTIMING_NONCONTINUOUS_NONINVERTING_CLOCK,
};
static const struct s5h1409_config dvico_hdtv5_pci_nano_config = {
@@ -567,7 +567,7 @@ static const struct s5h1409_config dvico_hdtv5_pci_nano_config = {
.gpio = S5H1409_GPIO_OFF,
.inversion = S5H1409_INVERSION_OFF,
.status_mode = S5H1409_DEMODLOCKING,
- .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK,
};
static const struct s5h1409_config kworld_atsc_120_config = {
@@ -576,7 +576,7 @@ static const struct s5h1409_config kworld_atsc_120_config = {
.gpio = S5H1409_GPIO_OFF,
.inversion = S5H1409_INVERSION_OFF,
.status_mode = S5H1409_DEMODLOCKING,
- .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK,
};
static const struct xc5000_config pinnacle_pctv_hd_800i_tuner_config = {
@@ -599,7 +599,7 @@ static const struct zl10353_config cx88_geniatech_x8000_mt = {
static const struct s5h1411_config dvico_fusionhdtv7_config = {
.output_mode = S5H1411_SERIAL_OUTPUT,
.gpio = S5H1411_GPIO_ON,
- .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+ .mpeg_timing = S5H1411_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK,
.qam_if = S5H1411_IF_44000,
.vsb_if = S5H1411_IF_44000,
.inversion = S5H1411_INVERSION_OFF,
@@ -1631,8 +1631,8 @@ static int dvb_register(struct cx8802_dev *dev)
if (fe1)
fe1->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl;
- /* Put the analog decoder in standby to keep it quiet */
- call_all(core, core, s_power, 0);
+ /* Put the tuner in standby to keep it quiet */
+ call_all(core, tuner, standby);
/* register everything */
res = vb2_dvb_register_bus(&dev->frontends, THIS_MODULE, dev,
diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c
index 4e9953e61a12..6f4e6923a91a 100644
--- a/drivers/media/pci/cx88/cx88-input.c
+++ b/drivers/media/pci/cx88/cx88-input.c
@@ -180,7 +180,7 @@ static enum hrtimer_restart cx88_ir_work(struct hrtimer *timer)
struct cx88_IR *ir = container_of(timer, struct cx88_IR, timer);
cx88_ir_handle_key(ir);
- missed = hrtimer_forward_now(&ir->timer, ir->polling * 1000000);
+ missed = hrtimer_forward_now(&ir->timer, ir->polling * 1000000LL);
if (missed > 1)
ir_dprintk("Missed ticks %ld\n", missed - 1);
@@ -200,7 +200,7 @@ static int __cx88_ir_start(void *priv)
if (ir->polling) {
hrtimer_init(&ir->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
ir->timer.function = cx88_ir_work;
- hrtimer_start(&ir->timer, ir->polling * 1000000,
+ hrtimer_start(&ir->timer, ir->polling * 1000000LL,
HRTIMER_MODE_REL);
}
if (ir->sampling) {
diff --git a/drivers/media/pci/ddbridge/Kconfig b/drivers/media/pci/ddbridge/Kconfig
index f43d0b83fc0c..a422dde2f34a 100644
--- a/drivers/media/pci/ddbridge/Kconfig
+++ b/drivers/media/pci/ddbridge/Kconfig
@@ -13,6 +13,7 @@ config DVB_DDBRIDGE
select DVB_LNBH25 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_TDA18212 if MEDIA_SUBDRV_AUTOSELECT
select DVB_MXL5XX if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_CXD2099 if MEDIA_SUBDRV_AUTOSELECT
---help---
Support for cards with the Digital Devices PCI express bridge:
- Octopus PCIe Bridge
diff --git a/drivers/media/pci/ddbridge/Makefile b/drivers/media/pci/ddbridge/Makefile
index f58fdec50eab..745b37d07558 100644
--- a/drivers/media/pci/ddbridge/Makefile
+++ b/drivers/media/pci/ddbridge/Makefile
@@ -10,6 +10,3 @@ obj-$(CONFIG_DVB_DDBRIDGE) += ddbridge.o
ccflags-y += -Idrivers/media/dvb-frontends/
ccflags-y += -Idrivers/media/tuners/
-
-# For the staging CI driver cxd2099
-ccflags-y += -Idrivers/staging/media/cxd2099/
diff --git a/drivers/media/pci/ddbridge/ddbridge-ci.c b/drivers/media/pci/ddbridge/ddbridge-ci.c
index 5828111487b0..a9dbc4ebf94f 100644
--- a/drivers/media/pci/ddbridge/ddbridge-ci.c
+++ b/drivers/media/pci/ddbridge/ddbridge-ci.c
@@ -172,6 +172,7 @@ static void ci_attach(struct ddb_port *port)
memcpy(&ci->en, &en_templ, sizeof(en_templ));
ci->en.data = ci;
port->en = &ci->en;
+ port->en_freedata = 1;
ci->port = port;
ci->nr = port->nr - 2;
}
@@ -304,6 +305,7 @@ static void ci_xo2_attach(struct ddb_port *port)
memcpy(&ci->en, &en_xo2_templ, sizeof(en_xo2_templ));
ci->en.data = ci;
port->en = &ci->en;
+ port->en_freedata = 1;
ci->port = port;
ci->nr = port->nr - 2;
ci->port->creg = 0;
@@ -311,38 +313,58 @@ static void ci_xo2_attach(struct ddb_port *port)
write_creg(ci, 0x08, 0x08);
}
-static struct cxd2099_cfg cxd_cfg = {
+static const struct cxd2099_cfg cxd_cfgtmpl = {
.bitrate = 72000,
- .adr = 0x40,
.polarity = 1,
.clock_mode = 1,
.max_i2c = 512,
};
+static int ci_cxd2099_attach(struct ddb_port *port, u32 bitrate)
+{
+ struct cxd2099_cfg cxd_cfg = cxd_cfgtmpl;
+ struct i2c_client *client;
+
+ cxd_cfg.bitrate = bitrate;
+ cxd_cfg.en = &port->en;
+
+ client = dvb_module_probe("cxd2099", NULL, &port->i2c->adap,
+ 0x40, &cxd_cfg);
+ if (!client)
+ goto err;
+
+ port->dvb[0].i2c_client[0] = client;
+ port->en_freedata = 0;
+ return 0;
+
+err:
+ dev_err(port->dev->dev, "CXD2099AR attach failed\n");
+ return -ENODEV;
+}
+
int ddb_ci_attach(struct ddb_port *port, u32 bitrate)
{
+ int ret;
+
switch (port->type) {
case DDB_CI_EXTERNAL_SONY:
- cxd_cfg.bitrate = bitrate;
- port->en = cxd2099_attach(&cxd_cfg, port, &port->i2c->adap);
- if (!port->en)
+ ret = ci_cxd2099_attach(port, bitrate);
+ if (ret)
return -ENODEV;
break;
-
case DDB_CI_EXTERNAL_XO2:
case DDB_CI_EXTERNAL_XO2_B:
ci_xo2_attach(port);
- if (!port->en)
- return -ENODEV;
break;
-
case DDB_CI_INTERNAL:
ci_attach(port);
- if (!port->en)
- return -ENODEV;
break;
+ default:
+ return -ENODEV;
}
+ if (!port->en)
+ return -ENODEV;
dvb_ca_en50221_init(port->dvb[0].adap, port->en, 0, 1);
return 0;
}
@@ -353,7 +375,14 @@ void ddb_ci_detach(struct ddb_port *port)
dvb_unregister_device(port->dvb[0].dev);
if (port->en) {
dvb_ca_en50221_release(port->en);
- kfree(port->en->data);
+
+ dvb_module_release(port->dvb[0].i2c_client[0]);
+ port->dvb[0].i2c_client[0] = NULL;
+
+ /* free alloc'ed memory if needed */
+ if (port->en_freedata)
+ kfree(port->en->data);
+
port->en = NULL;
}
}
diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c
index f9bee36f1cad..90687eff5909 100644
--- a/drivers/media/pci/ddbridge/ddbridge-core.c
+++ b/drivers/media/pci/ddbridge/ddbridge-core.c
@@ -999,37 +999,21 @@ static int tuner_attach_tda18212(struct ddb_input *input, u32 porttype)
.if_dvbt2_8 = 4000,
.if_dvbc = 5000,
};
- struct i2c_board_info board_info = {
- .type = "tda18212",
- .platform_data = &config,
- };
-
- if (input->nr & 1)
- board_info.addr = 0x63;
- else
- board_info.addr = 0x60;
+ u8 addr = (input->nr & 1) ? 0x63 : 0x60;
/* due to a hardware quirk with the I2C gate on the stv0367+tda18212
* combo, the tda18212 must be probed by reading it's id _twice_ when
* cold started, or it very likely will fail.
*/
if (porttype == DDB_TUNER_DVBCT_ST)
- tuner_tda18212_ping(input, board_info.addr);
-
- request_module(board_info.type);
-
- /* perform tuner init/attach */
- client = i2c_new_device(adapter, &board_info);
- if (!client || !client->dev.driver)
- goto err;
+ tuner_tda18212_ping(input, addr);
- if (!try_module_get(client->dev.driver->owner)) {
- i2c_unregister_device(client);
+ /* perform tuner probe/init/attach */
+ client = dvb_module_probe("tda18212", NULL, adapter, addr, &config);
+ if (!client)
goto err;
- }
dvb->i2c_client[0] = client;
-
return 0;
err:
dev_err(dev, "TDA18212 tuner not found. Device is not fully operational.\n");
@@ -1253,7 +1237,6 @@ static void dvb_input_detach(struct ddb_input *input)
{
struct ddb_dvb *dvb = &input->port->dvb[input->nr & 1];
struct dvb_demux *dvbdemux = &dvb->demux;
- struct i2c_client *client;
switch (dvb->attached) {
case 0x31:
@@ -1263,13 +1246,8 @@ static void dvb_input_detach(struct ddb_input *input)
dvb_unregister_frontend(dvb->fe);
/* fallthrough */
case 0x30:
- client = dvb->i2c_client[0];
- if (client) {
- module_put(client->dev.driver->owner);
- i2c_unregister_device(client);
- dvb->i2c_client[0] = NULL;
- client = NULL;
- }
+ dvb_module_release(dvb->i2c_client[0]);
+ dvb->i2c_client[0] = NULL;
if (dvb->fe2)
dvb_frontend_detach(dvb->fe2);
diff --git a/drivers/media/pci/ddbridge/ddbridge.h b/drivers/media/pci/ddbridge/ddbridge.h
index 095457737bc1..f223dc6c9963 100644
--- a/drivers/media/pci/ddbridge/ddbridge.h
+++ b/drivers/media/pci/ddbridge/ddbridge.h
@@ -276,6 +276,7 @@ struct ddb_port {
struct ddb_input *input[2];
struct ddb_output *output;
struct dvb_ca_en50221 *en;
+ u8 en_freedata;
struct ddb_dvb dvb[2];
u32 gap;
u32 obr;
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
index 6c4444b31f4b..7d768ec0f824 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
- * Copyright (c) 2017 Intel Corporation.
- *
- * 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.
- *
- * 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.
+ * Copyright (C) 2017 Intel Corporation
*
* Based partially on Intel IPU4 driver written by
* Sakari Ailus <sakari.ailus@linux.intel.com>
@@ -526,6 +518,8 @@ static void cio2_hw_exit(struct cio2_device *cio2, struct cio2_queue *q)
unsigned int i, maxloops = 1000;
/* Disable CSI receiver and MIPI backend devices */
+ writel(0, q->csi_rx_base + CIO2_REG_IRQCTRL_MASK);
+ writel(0, q->csi_rx_base + CIO2_REG_IRQCTRL_ENABLE);
writel(0, q->csi_rx_base + CIO2_REG_CSIRX_ENABLE);
writel(0, q->csi_rx_base + CIO2_REG_MIPIBE_ENABLE);
@@ -1035,6 +1029,7 @@ static void cio2_vb2_stop_streaming(struct vb2_queue *vq)
"failed to stop sensor streaming\n");
cio2_hw_exit(cio2, q);
+ synchronize_irq(cio2->pci_dev->irq);
cio2_vb2_return_all_buffers(q, VB2_BUF_STATE_ERROR);
media_pipeline_stop(&q->vdev.entity);
pm_runtime_put(&cio2->pci_dev->dev);
@@ -1976,6 +1971,7 @@ static int __maybe_unused cio2_suspend(struct device *dev)
/* Stop stream */
cio2_hw_exit(cio2, q);
+ synchronize_irq(pci_dev->irq);
pm_runtime_force_suspend(dev);
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.h b/drivers/media/pci/intel/ipu3/ipu3-cio2.h
index 78a5799f08e7..240635be7a31 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2.h
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.h
@@ -1,15 +1,5 @@
-/*
- * Copyright (c) 2017 Intel Corporation.
- *
- * 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.
- *
- * 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.
- */
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2017 Intel Corporation */
#ifndef __IPU3_CIO2_H
#define __IPU3_CIO2_H
diff --git a/drivers/media/pci/ivtv/ivtv-alsa-main.c b/drivers/media/pci/ivtv/ivtv-alsa-main.c
index 029f52733f70..c1856f609d2c 100644
--- a/drivers/media/pci/ivtv/ivtv-alsa-main.c
+++ b/drivers/media/pci/ivtv/ivtv-alsa-main.c
@@ -20,7 +20,6 @@
#include "ivtv-driver.h"
#include "ivtv-version.h"
#include "ivtv-alsa.h"
-#include "ivtv-alsa-mixer.h"
#include "ivtv-alsa-pcm.h"
#include <sound/core.h>
@@ -160,15 +159,7 @@ static int snd_ivtv_init(struct v4l2_device *v4l2_dev)
/* (4) Set the driver ID and name strings */
snd_ivtv_card_set_names(itvsc);
- /* (5) Create other components: mixer, PCM, & proc files */
-#if 0
- ret = snd_ivtv_mixer_create(itvsc);
- if (ret) {
- IVTV_ALSA_WARN("%s: snd_ivtv_mixer_create() failed with err %d: proceeding anyway\n",
- __func__, ret);
- }
-#endif
-
+ /* (5) Create other components: PCM, & proc files */
ret = snd_ivtv_pcm_create(itvsc);
if (ret) {
IVTV_ALSA_ERR("%s: snd_ivtv_pcm_create() failed with err %d\n",
diff --git a/drivers/media/pci/ivtv/ivtv-alsa-mixer.c b/drivers/media/pci/ivtv/ivtv-alsa-mixer.c
deleted file mode 100644
index aee453fcff37..000000000000
--- a/drivers/media/pci/ivtv/ivtv-alsa-mixer.c
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * ALSA mixer controls for the
- * ALSA interface to ivtv PCM capture streams
- *
- * Copyright (C) 2009,2012 Andy Walls <awalls@md.metrocast.net>
- *
- * 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.
- */
-
-#include "ivtv-alsa.h"
-#include "ivtv-alsa-mixer.h"
-#include "ivtv-driver.h"
-
-#include <linux/videodev2.h>
-
-#include <sound/core.h>
-#include <sound/control.h>
-#include <sound/tlv.h>
-
-/*
- * Note the cx25840-core volume scale is funny, due to the alignment of the
- * scale with another chip's range:
- *
- * v4l2_control value /512 indicated dB actual dB reg 0x8d4
- * 0x0000 - 0x01ff 0 -119 -96 228
- * 0x0200 - 0x02ff 1 -118 -96 228
- * ...
- * 0x2c00 - 0x2dff 22 -97 -96 228
- * 0x2e00 - 0x2fff 23 -96 -96 228
- * 0x3000 - 0x31ff 24 -95 -95 226
- * ...
- * 0xee00 - 0xefff 119 0 0 36
- * ...
- * 0xfe00 - 0xffff 127 +8 +8 20
- */
-static inline int dB_to_cx25840_vol(int dB)
-{
- if (dB < -96)
- dB = -96;
- else if (dB > 8)
- dB = 8;
- return (dB + 119) << 9;
-}
-
-static inline int cx25840_vol_to_dB(int v)
-{
- if (v < (23 << 9))
- v = (23 << 9);
- else if (v > (127 << 9))
- v = (127 << 9);
- return (v >> 9) - 119;
-}
-
-static int snd_ivtv_mixer_tv_vol_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 1;
- /* We're already translating values, just keep this control in dB */
- uinfo->value.integer.min = -96;
- uinfo->value.integer.max = 8;
- uinfo->value.integer.step = 1;
- return 0;
-}
-
-static int snd_ivtv_mixer_tv_vol_get(struct snd_kcontrol *kctl,
- struct snd_ctl_elem_value *uctl)
-{
- struct snd_ivtv_card *itvsc = snd_kcontrol_chip(kctl);
- struct ivtv *itv = to_ivtv(itvsc->v4l2_dev);
- struct v4l2_control vctrl;
- int ret;
-
- vctrl.id = V4L2_CID_AUDIO_VOLUME;
- vctrl.value = dB_to_cx25840_vol(uctl->value.integer.value[0]);
-
- snd_ivtv_lock(itvsc);
- ret = v4l2_g_ctrl(itv->sd_audio->ctrl_handler, &vctrl);
- snd_ivtv_unlock(itvsc);
-
- if (!ret)
- uctl->value.integer.value[0] = cx25840_vol_to_dB(vctrl.value);
- return ret;
-}
-
-static int snd_ivtv_mixer_tv_vol_put(struct snd_kcontrol *kctl,
- struct snd_ctl_elem_value *uctl)
-{
- struct snd_ivtv_card *itvsc = snd_kcontrol_chip(kctl);
- struct ivtv *itv = to_ivtv(itvsc->v4l2_dev);
- struct v4l2_control vctrl;
- int ret;
-
- vctrl.id = V4L2_CID_AUDIO_VOLUME;
- vctrl.value = dB_to_cx25840_vol(uctl->value.integer.value[0]);
-
- snd_ivtv_lock(itvsc);
-
- /* Fetch current state */
- ret = v4l2_g_ctrl(itv->sd_audio->ctrl_handler, &vctrl);
-
- if (ret ||
- (cx25840_vol_to_dB(vctrl.value) != uctl->value.integer.value[0])) {
-
- /* Set, if needed */
- vctrl.value = dB_to_cx25840_vol(uctl->value.integer.value[0]);
- ret = v4l2_s_ctrl(itv->sd_audio->ctrl_handler, &vctrl);
- if (!ret)
- ret = 1; /* Indicate control was changed w/o error */
- }
- snd_ivtv_unlock(itvsc);
-
- return ret;
-}
-
-
-/* This is a bit of overkill, the slider is already in dB internally */
-static DECLARE_TLV_DB_SCALE(snd_ivtv_mixer_tv_vol_db_scale, -9600, 100, 0);
-
-static struct snd_kcontrol_new snd_ivtv_mixer_tv_vol __initdata = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Analog TV Capture Volume",
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
- SNDRV_CTL_ELEM_ACCESS_TLV_READ,
- .info = snd_ivtv_mixer_tv_volume_info,
- .get = snd_ivtv_mixer_tv_volume_get,
- .put = snd_ivtv_mixer_tv_volume_put,
- .tlv.p = snd_ivtv_mixer_tv_vol_db_scale
-};
-
-/* FIXME - add mute switch and balance, bass, treble sliders:
- V4L2_CID_AUDIO_MUTE
-
- V4L2_CID_AUDIO_BALANCE
-
- V4L2_CID_AUDIO_BASS
- V4L2_CID_AUDIO_TREBLE
-*/
-
-/* FIXME - add stereo, lang1, lang2, mono menu */
-/* FIXME - add I2S volume */
-
-int __init snd_ivtv_mixer_create(struct snd_ivtv_card *itvsc)
-{
- struct v4l2_device *v4l2_dev = itvsc->v4l2_dev;
- struct snd_card *sc = itvsc->sc;
- int ret;
-
- strlcpy(sc->mixername, "CX2341[56] Mixer", sizeof(sc->mixername));
-
- ret = snd_ctl_add(sc, snd_ctl_new1(&snd_ivtv_mixer_tv_vol, itvsc));
- if (ret) {
- IVTV_ALSA_WARN("%s: failed to add %s control, err %d\n",
- __func__, snd_ivtv_mixer_tv_vol.name, ret);
- }
- return ret;
-}
diff --git a/drivers/media/pci/ivtv/ivtv-alsa-mixer.h b/drivers/media/pci/ivtv/ivtv-alsa-mixer.h
deleted file mode 100644
index 382bc36bc529..000000000000
--- a/drivers/media/pci/ivtv/ivtv-alsa-mixer.h
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * ALSA mixer controls for the
- * ALSA interface to ivtv PCM capture streams
- *
- * Copyright (C) 2009,2012 Andy Walls <awalls@md.metrocast.net>
- *
- * 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.
- */
-
-int __init snd_ivtv_mixer_create(struct snd_ivtv_card *itvsc);
diff --git a/drivers/media/pci/ivtv/ivtvfb.c b/drivers/media/pci/ivtv/ivtvfb.c
index 621b2f613d81..8e62b8be6529 100644
--- a/drivers/media/pci/ivtv/ivtvfb.c
+++ b/drivers/media/pci/ivtv/ivtvfb.c
@@ -346,8 +346,8 @@ static int ivtvfb_prep_frame(struct ivtv *itv, int cmd, void __user *source,
/* Not fatal, but will have undesirable results */
if ((unsigned long)source & 3)
- IVTVFB_WARN("ivtvfb_prep_frame: Source address not 32 bit aligned (0x%08lx)\n",
- (unsigned long)source);
+ IVTVFB_WARN("ivtvfb_prep_frame: Source address not 32 bit aligned (%p)\n",
+ source);
if (dest_offset & 3)
IVTVFB_WARN("ivtvfb_prep_frame: Dest offset not 32 bit aligned (%ld)\n", dest_offset);
@@ -357,12 +357,10 @@ static int ivtvfb_prep_frame(struct ivtv *itv, int cmd, void __user *source,
/* Check Source */
if (!access_ok(VERIFY_READ, source + dest_offset, count)) {
- IVTVFB_WARN("Invalid userspace pointer 0x%08lx\n",
- (unsigned long)source);
+ IVTVFB_WARN("Invalid userspace pointer %p\n", source);
- IVTVFB_DEBUG_WARN("access_ok() failed for offset 0x%08lx source 0x%08lx count %d\n",
- dest_offset, (unsigned long)source,
- count);
+ IVTVFB_DEBUG_WARN("access_ok() failed for offset 0x%08lx source %p count %d\n",
+ dest_offset, source, count);
return -EINVAL;
}
diff --git a/drivers/media/pci/mantis/mantis_vp3028.c b/drivers/media/pci/mantis/mantis_vp3028.c
deleted file mode 100644
index 4155c838a18a..000000000000
--- a/drivers/media/pci/mantis/mantis_vp3028.c
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- Mantis VP-3028 driver
-
- Copyright (C) Manu Abraham (abraham.manu@gmail.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 "mantis_common.h"
-#include "mantis_vp3028.h"
-
-struct zl10353_config mantis_vp3028_config = {
- .demod_address = 0x0f,
-};
-
-#define MANTIS_MODEL_NAME "VP-3028"
-#define MANTIS_DEV_TYPE "DVB-T"
-
-struct mantis_hwconfig vp3028_mantis_config = {
- .model_name = MANTIS_MODEL_NAME,
- .dev_type = MANTIS_DEV_TYPE,
- .ts_size = MANTIS_TS_188,
- .baud_rate = MANTIS_BAUD_9600,
- .parity = MANTIS_PARITY_NONE,
- .bytes = 0,
-};
diff --git a/drivers/media/pci/mantis/mantis_vp3028.h b/drivers/media/pci/mantis/mantis_vp3028.h
deleted file mode 100644
index 34130d29e0aa..000000000000
--- a/drivers/media/pci/mantis/mantis_vp3028.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- Mantis VP-3028 driver
-
- Copyright (C) Manu Abraham (abraham.manu@gmail.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.
-*/
-
-#ifndef __MANTIS_VP3028_H
-#define __MANTIS_VP3028_H
-
-#include <media/dvb_frontend.h>
-#include "mantis_common.h"
-#include "zl10353.h"
-
-#define MANTIS_VP_3028_DVB_T 0x0028
-
-extern struct zl10353_config mantis_vp3028_config;
-extern struct mantis_hwconfig vp3028_mantis_config;
-
-#endif /* __MANTIS_VP3028_H */
diff --git a/drivers/media/pci/ngene/Kconfig b/drivers/media/pci/ngene/Kconfig
index 637d506b23c5..e06d019996f3 100644
--- a/drivers/media/pci/ngene/Kconfig
+++ b/drivers/media/pci/ngene/Kconfig
@@ -8,6 +8,13 @@ config DVB_NGENE
select DVB_DRXK if MEDIA_SUBDRV_AUTOSELECT
select DVB_TDA18271C2DD if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_MT2131 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0367 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_CXD2841ER if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_TDA18212 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0910 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV6111 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_LNBH25 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_CXD2099 if MEDIA_SUBDRV_AUTOSELECT
---help---
Support for Micronas PCI express cards with nGene bridge.
diff --git a/drivers/media/pci/ngene/Makefile b/drivers/media/pci/ngene/Makefile
index e4208f5ed215..ec450ad19281 100644
--- a/drivers/media/pci/ngene/Makefile
+++ b/drivers/media/pci/ngene/Makefile
@@ -9,6 +9,3 @@ obj-$(CONFIG_DVB_NGENE) += ngene.o
ccflags-y += -Idrivers/media/dvb-frontends/
ccflags-y += -Idrivers/media/tuners/
-
-# For the staging CI driver cxd2099
-ccflags-y += -Idrivers/staging/media/cxd2099/
diff --git a/drivers/media/pci/ngene/ngene-cards.c b/drivers/media/pci/ngene/ngene-cards.c
index bb49620540c5..65fc8f23ad86 100644
--- a/drivers/media/pci/ngene/ngene-cards.c
+++ b/drivers/media/pci/ngene/ngene-cards.c
@@ -23,6 +23,8 @@
* http://www.gnu.org/copyleft/gpl.html
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
@@ -40,22 +42,101 @@
#include "drxk.h"
#include "drxd.h"
#include "dvb-pll.h"
+#include "stv0367.h"
+#include "stv0367_priv.h"
+#include "tda18212.h"
+#include "cxd2841er.h"
+#include "stv0910.h"
+#include "stv6111.h"
+#include "lnbh25.h"
+
+/****************************************************************************/
+/* I2C transfer functions used for demod/tuner probing***********************/
+/****************************************************************************/
+
+static int i2c_io(struct i2c_adapter *adapter, u8 adr,
+ u8 *wbuf, u32 wlen, u8 *rbuf, u32 rlen)
+{
+ struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
+ .buf = wbuf, .len = wlen },
+ {.addr = adr, .flags = I2C_M_RD,
+ .buf = rbuf, .len = rlen } };
+ return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
+}
+
+static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len)
+{
+ struct i2c_msg msg = {.addr = adr, .flags = 0,
+ .buf = data, .len = len};
+
+ return (i2c_transfer(adap, &msg, 1) == 1) ? 0 : -1;
+}
+
+static int i2c_write_reg(struct i2c_adapter *adap, u8 adr,
+ u8 reg, u8 val)
+{
+ u8 msg[2] = {reg, val};
+
+ return i2c_write(adap, adr, msg, 2);
+}
+
+static int i2c_read(struct i2c_adapter *adapter, u8 adr, u8 *val)
+{
+ struct i2c_msg msgs[1] = {{.addr = adr, .flags = I2C_M_RD,
+ .buf = val, .len = 1 } };
+ return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1;
+}
+
+static int i2c_read_reg16(struct i2c_adapter *adapter, u8 adr,
+ u16 reg, u8 *val)
+{
+ u8 msg[2] = {reg >> 8, reg & 0xff};
+ struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
+ .buf = msg, .len = 2},
+ {.addr = adr, .flags = I2C_M_RD,
+ .buf = val, .len = 1} };
+ return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
+}
+static int i2c_read_regs(struct i2c_adapter *adapter,
+ u8 adr, u8 reg, u8 *val, u8 len)
+{
+ struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
+ .buf = &reg, .len = 1},
+ {.addr = adr, .flags = I2C_M_RD,
+ .buf = val, .len = len} };
+
+ return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
+}
+
+static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr, u8 reg, u8 *val)
+{
+ return i2c_read_regs(adapter, adr, reg, val, 1);
+}
/****************************************************************************/
/* Demod/tuner attachment ***************************************************/
/****************************************************************************/
+static struct i2c_adapter *i2c_adapter_from_chan(struct ngene_channel *chan)
+{
+ /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
+ if (chan->number < 2)
+ return &chan->dev->channel[0].i2c_adapter;
+
+ return &chan->dev->channel[1].i2c_adapter;
+}
+
static int tuner_attach_stv6110(struct ngene_channel *chan)
{
- struct i2c_adapter *i2c;
+ struct device *pdev = &chan->dev->pci_dev->dev;
+ struct i2c_adapter *i2c = i2c_adapter_from_chan(chan);
struct stv090x_config *feconf = (struct stv090x_config *)
chan->dev->card_info->fe_config[chan->number];
struct stv6110x_config *tunerconf = (struct stv6110x_config *)
chan->dev->card_info->tuner_config[chan->number];
const struct stv6110x_devctl *ctl;
- /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
if (chan->number < 2)
i2c = &chan->dev->channel[0].i2c_adapter;
else
@@ -63,7 +144,7 @@ static int tuner_attach_stv6110(struct ngene_channel *chan)
ctl = dvb_attach(stv6110x_attach, chan->fe, tunerconf, i2c);
if (ctl == NULL) {
- printk(KERN_ERR DEVICE_NAME ": No STV6110X found!\n");
+ dev_err(pdev, "No STV6110X found!\n");
return -ENODEV;
}
@@ -82,6 +163,23 @@ static int tuner_attach_stv6110(struct ngene_channel *chan)
return 0;
}
+static int tuner_attach_stv6111(struct ngene_channel *chan)
+{
+ struct device *pdev = &chan->dev->pci_dev->dev;
+ struct i2c_adapter *i2c = i2c_adapter_from_chan(chan);
+ struct dvb_frontend *fe;
+ u8 adr = 4 + ((chan->number & 1) ? 0x63 : 0x60);
+
+ fe = dvb_attach(stv6111_attach, chan->fe, i2c, adr);
+ if (!fe) {
+ fe = dvb_attach(stv6111_attach, chan->fe, i2c, adr & ~4);
+ if (!fe) {
+ dev_err(pdev, "stv6111_attach() failed!\n");
+ return -ENODEV;
+ }
+ }
+ return 0;
+}
static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
{
@@ -100,35 +198,109 @@ static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
static int tuner_attach_tda18271(struct ngene_channel *chan)
{
- struct i2c_adapter *i2c;
+ struct device *pdev = &chan->dev->pci_dev->dev;
+ struct i2c_adapter *i2c = i2c_adapter_from_chan(chan);
struct dvb_frontend *fe;
- i2c = &chan->dev->channel[0].i2c_adapter;
if (chan->fe->ops.i2c_gate_ctrl)
chan->fe->ops.i2c_gate_ctrl(chan->fe, 1);
fe = dvb_attach(tda18271c2dd_attach, chan->fe, i2c, 0x60);
if (chan->fe->ops.i2c_gate_ctrl)
chan->fe->ops.i2c_gate_ctrl(chan->fe, 0);
if (!fe) {
- printk(KERN_ERR "No TDA18271 found!\n");
+ dev_err(pdev, "No TDA18271 found!\n");
return -ENODEV;
}
return 0;
}
+static int tuner_tda18212_ping(struct ngene_channel *chan,
+ struct i2c_adapter *i2c,
+ unsigned short adr)
+{
+ struct device *pdev = &chan->dev->pci_dev->dev;
+ u8 tda_id[2];
+ u8 subaddr = 0x00;
+
+ dev_dbg(pdev, "stv0367-tda18212 tuner ping\n");
+ if (chan->fe->ops.i2c_gate_ctrl)
+ chan->fe->ops.i2c_gate_ctrl(chan->fe, 1);
+
+ if (i2c_read_regs(i2c, adr, subaddr, tda_id, sizeof(tda_id)) < 0)
+ dev_dbg(pdev, "tda18212 ping 1 fail\n");
+ if (i2c_read_regs(i2c, adr, subaddr, tda_id, sizeof(tda_id)) < 0)
+ dev_warn(pdev, "tda18212 ping failed, expect problems\n");
+
+ if (chan->fe->ops.i2c_gate_ctrl)
+ chan->fe->ops.i2c_gate_ctrl(chan->fe, 0);
+
+ return 0;
+}
+
+static int tuner_attach_tda18212(struct ngene_channel *chan, u32 dmdtype)
+{
+ struct device *pdev = &chan->dev->pci_dev->dev;
+ struct i2c_adapter *i2c = i2c_adapter_from_chan(chan);
+ struct i2c_client *client;
+ struct tda18212_config config = {
+ .fe = chan->fe,
+ .if_dvbt_6 = 3550,
+ .if_dvbt_7 = 3700,
+ .if_dvbt_8 = 4150,
+ .if_dvbt2_6 = 3250,
+ .if_dvbt2_7 = 4000,
+ .if_dvbt2_8 = 4000,
+ .if_dvbc = 5000,
+ };
+ u8 addr = (chan->number & 1) ? 0x63 : 0x60;
+
+ /*
+ * due to a hardware quirk with the I2C gate on the stv0367+tda18212
+ * combo, the tda18212 must be probed by reading it's id _twice_ when
+ * cold started, or it very likely will fail.
+ */
+ if (dmdtype == DEMOD_TYPE_STV0367)
+ tuner_tda18212_ping(chan, i2c, addr);
+
+ /* perform tuner probe/init/attach */
+ client = dvb_module_probe("tda18212", NULL, i2c, addr, &config);
+ if (!client)
+ goto err;
+
+ chan->i2c_client[0] = client;
+ chan->i2c_client_fe = 1;
+
+ return 0;
+err:
+ dev_err(pdev, "TDA18212 tuner not found. Device is not fully operational.\n");
+ return -ENODEV;
+}
+
static int tuner_attach_probe(struct ngene_channel *chan)
{
- if (chan->demod_type == 0)
+ switch (chan->demod_type) {
+ case DEMOD_TYPE_STV090X:
return tuner_attach_stv6110(chan);
- if (chan->demod_type == 1)
+ case DEMOD_TYPE_DRXK:
return tuner_attach_tda18271(chan);
+ case DEMOD_TYPE_STV0367:
+ case DEMOD_TYPE_SONY_CT2:
+ case DEMOD_TYPE_SONY_ISDBT:
+ case DEMOD_TYPE_SONY_C2T2:
+ case DEMOD_TYPE_SONY_C2T2I:
+ return tuner_attach_tda18212(chan, chan->demod_type);
+ case DEMOD_TYPE_STV0910:
+ return tuner_attach_stv6111(chan);
+ }
+
return -EINVAL;
}
static int demod_attach_stv0900(struct ngene_channel *chan)
{
- struct i2c_adapter *i2c;
+ struct device *pdev = &chan->dev->pci_dev->dev;
+ struct i2c_adapter *i2c = i2c_adapter_from_chan(chan);
struct stv090x_config *feconf = (struct stv090x_config *)
chan->dev->card_info->fe_config[chan->number];
@@ -144,7 +316,7 @@ static int demod_attach_stv0900(struct ngene_channel *chan)
(chan->number & 1) == 0 ? STV090x_DEMODULATOR_0
: STV090x_DEMODULATOR_1);
if (chan->fe == NULL) {
- printk(KERN_ERR DEVICE_NAME ": No STV0900 found!\n");
+ dev_err(pdev, "No STV0900 found!\n");
return -ENODEV;
}
@@ -154,7 +326,7 @@ static int demod_attach_stv0900(struct ngene_channel *chan)
if (!dvb_attach(lnbh24_attach, chan->fe, i2c, 0,
0, chan->dev->card_info->lnb[chan->number])) {
- printk(KERN_ERR DEVICE_NAME ": No LNBH24 found!\n");
+ dev_err(pdev, "No LNBH24 found!\n");
dvb_frontend_detach(chan->fe);
chan->fe = NULL;
return -ENODEV;
@@ -163,6 +335,119 @@ static int demod_attach_stv0900(struct ngene_channel *chan)
return 0;
}
+static struct stv0910_cfg stv0910_p = {
+ .adr = 0x68,
+ .parallel = 1,
+ .rptlvl = 4,
+ .clk = 30000000,
+};
+
+static struct lnbh25_config lnbh25_cfg = {
+ .i2c_address = 0x0c << 1,
+ .data2_config = LNBH25_TEN
+};
+
+static int demod_attach_stv0910(struct ngene_channel *chan,
+ struct i2c_adapter *i2c)
+{
+ struct device *pdev = &chan->dev->pci_dev->dev;
+ struct stv0910_cfg cfg = stv0910_p;
+ struct lnbh25_config lnbcfg = lnbh25_cfg;
+
+ chan->fe = dvb_attach(stv0910_attach, i2c, &cfg, (chan->number & 1));
+ if (!chan->fe) {
+ cfg.adr = 0x6c;
+ chan->fe = dvb_attach(stv0910_attach, i2c,
+ &cfg, (chan->number & 1));
+ }
+ if (!chan->fe) {
+ dev_err(pdev, "stv0910_attach() failed!\n");
+ return -ENODEV;
+ }
+
+ /*
+ * attach lnbh25 - leftshift by one as the lnbh25 driver expects 8bit
+ * i2c addresses
+ */
+ lnbcfg.i2c_address = (((chan->number & 1) ? 0x0d : 0x0c) << 1);
+ if (!dvb_attach(lnbh25_attach, chan->fe, &lnbcfg, i2c)) {
+ lnbcfg.i2c_address = (((chan->number & 1) ? 0x09 : 0x08) << 1);
+ if (!dvb_attach(lnbh25_attach, chan->fe, &lnbcfg, i2c)) {
+ dev_err(pdev, "lnbh25_attach() failed!\n");
+ dvb_frontend_detach(chan->fe);
+ chan->fe = NULL;
+ return -ENODEV;
+ }
+ }
+
+ return 0;
+}
+
+static struct stv0367_config ddb_stv0367_config[] = {
+ {
+ .demod_address = 0x1f,
+ .xtal = 27000000,
+ .if_khz = 0,
+ .if_iq_mode = FE_TER_NORMAL_IF_TUNER,
+ .ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
+ .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
+ }, {
+ .demod_address = 0x1e,
+ .xtal = 27000000,
+ .if_khz = 0,
+ .if_iq_mode = FE_TER_NORMAL_IF_TUNER,
+ .ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
+ .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
+ },
+};
+
+static int demod_attach_stv0367(struct ngene_channel *chan,
+ struct i2c_adapter *i2c)
+{
+ struct device *pdev = &chan->dev->pci_dev->dev;
+
+ chan->fe = dvb_attach(stv0367ddb_attach,
+ &ddb_stv0367_config[(chan->number & 1)], i2c);
+
+ if (!chan->fe) {
+ dev_err(pdev, "stv0367ddb_attach() failed!\n");
+ return -ENODEV;
+ }
+
+ chan->fe->sec_priv = chan;
+ chan->gate_ctrl = chan->fe->ops.i2c_gate_ctrl;
+ chan->fe->ops.i2c_gate_ctrl = drxk_gate_ctrl;
+ return 0;
+}
+
+static int demod_attach_cxd28xx(struct ngene_channel *chan,
+ struct i2c_adapter *i2c, int osc24)
+{
+ struct device *pdev = &chan->dev->pci_dev->dev;
+ struct cxd2841er_config cfg;
+
+ /* the cxd2841er driver expects 8bit/shifted I2C addresses */
+ cfg.i2c_addr = ((chan->number & 1) ? 0x6d : 0x6c) << 1;
+
+ cfg.xtal = osc24 ? SONY_XTAL_24000 : SONY_XTAL_20500;
+ cfg.flags = CXD2841ER_AUTO_IFHZ | CXD2841ER_EARLY_TUNE |
+ CXD2841ER_NO_WAIT_LOCK | CXD2841ER_NO_AGCNEG |
+ CXD2841ER_TSBITS | CXD2841ER_TS_SERIAL;
+
+ /* attach frontend */
+ chan->fe = dvb_attach(cxd2841er_attach_t_c, &cfg, i2c);
+
+ if (!chan->fe) {
+ dev_err(pdev, "CXD28XX attach failed!\n");
+ return -ENODEV;
+ }
+
+ chan->fe->sec_priv = chan;
+ chan->gate_ctrl = chan->fe->ops.i2c_gate_ctrl;
+ chan->fe->ops.i2c_gate_ctrl = drxk_gate_ctrl;
+ return 0;
+}
+
static void cineS2_tuner_i2c_lock(struct dvb_frontend *fe, int lock)
{
struct ngene_channel *chan = fe->analog_demod_priv;
@@ -173,44 +458,61 @@ static void cineS2_tuner_i2c_lock(struct dvb_frontend *fe, int lock)
up(&chan->dev->pll_mutex);
}
-static int i2c_read(struct i2c_adapter *adapter, u8 adr, u8 *val)
+static int port_has_stv0900(struct i2c_adapter *i2c, int port)
{
- struct i2c_msg msgs[1] = {{.addr = adr, .flags = I2C_M_RD,
- .buf = val, .len = 1 } };
- return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1;
+ u8 val;
+ if (i2c_read_reg16(i2c, 0x68+port/2, 0xf100, &val) < 0)
+ return 0;
+ return 1;
}
-static int i2c_read_reg16(struct i2c_adapter *adapter, u8 adr,
- u16 reg, u8 *val)
+static int port_has_drxk(struct i2c_adapter *i2c, int port)
{
- u8 msg[2] = {reg>>8, reg&0xff};
- struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
- .buf = msg, .len = 2},
- {.addr = adr, .flags = I2C_M_RD,
- .buf = val, .len = 1} };
- return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
+ u8 val;
+
+ if (i2c_read(i2c, 0x29+port, &val) < 0)
+ return 0;
+ return 1;
}
-static int port_has_stv0900(struct i2c_adapter *i2c, int port)
+static int port_has_stv0367(struct i2c_adapter *i2c)
{
u8 val;
- if (i2c_read_reg16(i2c, 0x68+port/2, 0xf100, &val) < 0)
+
+ if (i2c_read_reg16(i2c, 0x1e, 0xf000, &val) < 0)
+ return 0;
+ if (val != 0x60)
+ return 0;
+ if (i2c_read_reg16(i2c, 0x1f, 0xf000, &val) < 0)
+ return 0;
+ if (val != 0x60)
return 0;
return 1;
}
-static int port_has_drxk(struct i2c_adapter *i2c, int port)
+int ngene_port_has_cxd2099(struct i2c_adapter *i2c, u8 *type)
{
u8 val;
-
- if (i2c_read(i2c, 0x29+port, &val) < 0)
+ u8 probe[4] = { 0xe0, 0x00, 0x00, 0x00 }, data[4];
+ struct i2c_msg msgs[2] = {{ .addr = 0x40, .flags = 0,
+ .buf = probe, .len = 4 },
+ { .addr = 0x40, .flags = I2C_M_RD,
+ .buf = data, .len = 4 } };
+ val = i2c_transfer(i2c, msgs, 2);
+ if (val != 2)
return 0;
+
+ if (data[0] == 0x02 && data[1] == 0x2b && data[3] == 0x43)
+ *type = 2;
+ else
+ *type = 1;
return 1;
}
static int demod_attach_drxk(struct ngene_channel *chan,
struct i2c_adapter *i2c)
{
+ struct device *pdev = &chan->dev->pci_dev->dev;
struct drxk_config config;
memset(&config, 0, sizeof(config));
@@ -220,7 +522,7 @@ static int demod_attach_drxk(struct ngene_channel *chan,
chan->fe = dvb_attach(drxk_attach, &config, i2c);
if (!chan->fe) {
- printk(KERN_ERR "No DRXK found!\n");
+ dev_err(pdev, "No DRXK found!\n");
return -ENODEV;
}
chan->fe->sec_priv = chan;
@@ -229,22 +531,153 @@ static int demod_attach_drxk(struct ngene_channel *chan,
return 0;
}
+/****************************************************************************/
+/* XO2 related lists and functions ******************************************/
+/****************************************************************************/
+
+static char *xo2names[] = {
+ "DUAL DVB-S2",
+ "DUAL DVB-C/T/T2",
+ "DUAL DVB-ISDBT",
+ "DUAL DVB-C/C2/T/T2",
+ "DUAL ATSC",
+ "DUAL DVB-C/C2/T/T2/I",
+};
+
+static int init_xo2(struct ngene_channel *chan, struct i2c_adapter *i2c)
+{
+ struct device *pdev = &chan->dev->pci_dev->dev;
+ u8 addr = 0x10;
+ u8 val, data[2];
+ int res;
+
+ res = i2c_read_regs(i2c, addr, 0x04, data, 2);
+ if (res < 0)
+ return res;
+
+ if (data[0] != 0x01) {
+ dev_info(pdev, "Invalid XO2 on channel %d\n", chan->number);
+ return -1;
+ }
+
+ i2c_read_reg(i2c, addr, 0x08, &val);
+ if (val != 0) {
+ i2c_write_reg(i2c, addr, 0x08, 0x00);
+ msleep(100);
+ }
+ /* Enable tuner power, disable pll, reset demods */
+ i2c_write_reg(i2c, addr, 0x08, 0x04);
+ usleep_range(2000, 3000);
+ /* Release demod resets */
+ i2c_write_reg(i2c, addr, 0x08, 0x07);
+
+ /*
+ * speed: 0=55,1=75,2=90,3=104 MBit/s
+ * Note: The ngene hardware must be run at 75 MBit/s compared
+ * to more modern ddbridge hardware which runs at 90 MBit/s,
+ * else there will be issues with the data transport and non-
+ * working secondary/slave demods/tuners.
+ */
+ i2c_write_reg(i2c, addr, 0x09, 1);
+
+ i2c_write_reg(i2c, addr, 0x0a, 0x01);
+ i2c_write_reg(i2c, addr, 0x0b, 0x01);
+
+ usleep_range(2000, 3000);
+ /* Start XO2 PLL */
+ i2c_write_reg(i2c, addr, 0x08, 0x87);
+
+ return 0;
+}
+
+static int port_has_xo2(struct i2c_adapter *i2c, u8 *type, u8 *id)
+{
+ u8 probe[1] = { 0x00 }, data[4];
+ u8 addr = 0x10;
+
+ *type = NGENE_XO2_TYPE_NONE;
+
+ if (i2c_io(i2c, addr, probe, 1, data, 4))
+ return 0;
+ if (data[0] == 'D' && data[1] == 'F') {
+ *id = data[2];
+ *type = NGENE_XO2_TYPE_DUOFLEX;
+ return 1;
+ }
+ if (data[0] == 'C' && data[1] == 'I') {
+ *id = data[2];
+ *type = NGENE_XO2_TYPE_CI;
+ return 1;
+ }
+ return 0;
+}
+
+/****************************************************************************/
+/* Probing and port/channel handling ****************************************/
+/****************************************************************************/
+
static int cineS2_probe(struct ngene_channel *chan)
{
- struct i2c_adapter *i2c;
+ struct device *pdev = &chan->dev->pci_dev->dev;
+ struct i2c_adapter *i2c = i2c_adapter_from_chan(chan);
struct stv090x_config *fe_conf;
u8 buf[3];
+ u8 xo2_type, xo2_id, xo2_demodtype;
+ u8 sony_osc24 = 0;
struct i2c_msg i2c_msg = { .flags = 0, .buf = buf };
int rc;
- /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
- if (chan->number < 2)
- i2c = &chan->dev->channel[0].i2c_adapter;
- else
- i2c = &chan->dev->channel[1].i2c_adapter;
-
- if (port_has_stv0900(i2c, chan->number)) {
- chan->demod_type = 0;
+ if (port_has_xo2(i2c, &xo2_type, &xo2_id)) {
+ xo2_id >>= 2;
+ dev_dbg(pdev, "XO2 on channel %d (type %d, id %d)\n",
+ chan->number, xo2_type, xo2_id);
+
+ switch (xo2_type) {
+ case NGENE_XO2_TYPE_DUOFLEX:
+ if (chan->number & 1)
+ dev_dbg(pdev,
+ "skipping XO2 init on odd channel %d",
+ chan->number);
+ else
+ init_xo2(chan, i2c);
+
+ xo2_demodtype = DEMOD_TYPE_XO2 + xo2_id;
+
+ switch (xo2_demodtype) {
+ case DEMOD_TYPE_SONY_CT2:
+ case DEMOD_TYPE_SONY_ISDBT:
+ case DEMOD_TYPE_SONY_C2T2:
+ case DEMOD_TYPE_SONY_C2T2I:
+ dev_info(pdev, "%s (XO2) on channel %d\n",
+ xo2names[xo2_id], chan->number);
+ chan->demod_type = xo2_demodtype;
+ if (xo2_demodtype == DEMOD_TYPE_SONY_C2T2I)
+ sony_osc24 = 1;
+
+ demod_attach_cxd28xx(chan, i2c, sony_osc24);
+ break;
+ case DEMOD_TYPE_STV0910:
+ dev_info(pdev, "%s (XO2) on channel %d\n",
+ xo2names[xo2_id], chan->number);
+ chan->demod_type = xo2_demodtype;
+ demod_attach_stv0910(chan, i2c);
+ break;
+ default:
+ dev_warn(pdev,
+ "Unsupported XO2 module on channel %d\n",
+ chan->number);
+ return -ENODEV;
+ }
+ break;
+ case NGENE_XO2_TYPE_CI:
+ dev_info(pdev, "DuoFlex CI modules not supported\n");
+ return -ENODEV;
+ default:
+ dev_info(pdev, "Unsupported XO2 module type\n");
+ return -ENODEV;
+ }
+ } else if (port_has_stv0900(i2c, chan->number)) {
+ chan->demod_type = DEMOD_TYPE_STV090X;
fe_conf = chan->dev->card_info->fe_config[chan->number];
/* demod found, attach it */
rc = demod_attach_stv0900(chan);
@@ -269,14 +702,18 @@ static int cineS2_probe(struct ngene_channel *chan)
}
rc = i2c_transfer(i2c, &i2c_msg, 1);
if (rc != 1) {
- printk(KERN_ERR DEVICE_NAME ": could not setup DPNx\n");
+ dev_err(pdev, "Could not setup DPNx\n");
return -EIO;
}
} else if (port_has_drxk(i2c, chan->number^2)) {
- chan->demod_type = 1;
+ chan->demod_type = DEMOD_TYPE_DRXK;
demod_attach_drxk(chan, i2c);
+ } else if (port_has_stv0367(i2c)) {
+ chan->demod_type = DEMOD_TYPE_STV0367;
+ dev_info(pdev, "STV0367 on channel %d\n", chan->number);
+ demod_attach_stv0367(chan, i2c);
} else {
- printk(KERN_ERR "No demod found on chan %d\n", chan->number);
+ dev_info(pdev, "No demod found on chan %d\n", chan->number);
return -ENODEV;
}
return 0;
@@ -299,9 +736,11 @@ static struct mt2131_config m780_tunerconfig = {
*/
static int demod_attach_lg330x(struct ngene_channel *chan)
{
+ struct device *pdev = &chan->dev->pci_dev->dev;
+
chan->fe = dvb_attach(lgdt330x_attach, &aver_m780, &chan->i2c_adapter);
if (chan->fe == NULL) {
- printk(KERN_ERR DEVICE_NAME ": No LGDT330x found!\n");
+ dev_err(pdev, "No LGDT330x found!\n");
return -ENODEV;
}
@@ -313,6 +752,7 @@ static int demod_attach_lg330x(struct ngene_channel *chan)
static int demod_attach_drxd(struct ngene_channel *chan)
{
+ struct device *pdev = &chan->dev->pci_dev->dev;
struct drxd_config *feconf;
feconf = chan->dev->card_info->fe_config[chan->number];
@@ -320,7 +760,7 @@ static int demod_attach_drxd(struct ngene_channel *chan)
chan->fe = dvb_attach(drxd_attach, feconf, chan,
&chan->i2c_adapter, &chan->dev->pci_dev->dev);
if (!chan->fe) {
- pr_err("No DRXD found!\n");
+ dev_err(pdev, "No DRXD found!\n");
return -ENODEV;
}
return 0;
@@ -328,6 +768,7 @@ static int demod_attach_drxd(struct ngene_channel *chan)
static int tuner_attach_dtt7520x(struct ngene_channel *chan)
{
+ struct device *pdev = &chan->dev->pci_dev->dev;
struct drxd_config *feconf;
feconf = chan->dev->card_info->fe_config[chan->number];
@@ -335,7 +776,7 @@ static int tuner_attach_dtt7520x(struct ngene_channel *chan)
if (!dvb_attach(dvb_pll_attach, chan->fe, feconf->pll_address,
&chan->i2c_adapter,
feconf->pll_type)) {
- pr_err("No pll(%d) found!\n", feconf->pll_type);
+ dev_err(pdev, "No pll(%d) found!\n", feconf->pll_type);
return -ENODEV;
}
return 0;
@@ -371,12 +812,13 @@ static int tuner_attach_dtt7520x(struct ngene_channel *chan)
static int i2c_write_eeprom(struct i2c_adapter *adapter,
u8 adr, u16 reg, u8 data)
{
+ struct device *pdev = adapter->dev.parent;
u8 m[3] = {(reg >> 8), (reg & 0xff), data};
struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m,
.len = sizeof(m)};
if (i2c_transfer(adapter, &msg, 1) != 1) {
- pr_err(DEVICE_NAME ": Error writing EEPROM!\n");
+ dev_err(pdev, "Error writing EEPROM!\n");
return -EIO;
}
return 0;
@@ -385,6 +827,7 @@ static int i2c_write_eeprom(struct i2c_adapter *adapter,
static int i2c_read_eeprom(struct i2c_adapter *adapter,
u8 adr, u16 reg, u8 *data, int len)
{
+ struct device *pdev = adapter->dev.parent;
u8 msg[2] = {(reg >> 8), (reg & 0xff)};
struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
.buf = msg, .len = 2 },
@@ -392,7 +835,7 @@ static int i2c_read_eeprom(struct i2c_adapter *adapter,
.buf = data, .len = len} };
if (i2c_transfer(adapter, msgs, 2) != 2) {
- pr_err(DEVICE_NAME ": Error reading EEPROM\n");
+ dev_err(pdev, "Error reading EEPROM\n");
return -EIO;
}
return 0;
@@ -401,6 +844,7 @@ static int i2c_read_eeprom(struct i2c_adapter *adapter,
static int ReadEEProm(struct i2c_adapter *adapter,
u16 Tag, u32 MaxLen, u8 *data, u32 *pLength)
{
+ struct device *pdev = adapter->dev.parent;
int status = 0;
u16 Addr = MICNG_EE_START, Length, tag = 0;
u8 EETag[3];
@@ -416,9 +860,8 @@ static int ReadEEProm(struct i2c_adapter *adapter,
Addr += sizeof(u16) + 1 + EETag[2];
}
if (Addr + sizeof(u16) + 1 + EETag[2] > MICNG_EE_END) {
- pr_err(DEVICE_NAME
- ": Reached EOEE @ Tag = %04x Length = %3d\n",
- tag, EETag[2]);
+ dev_err(pdev, "Reached EOEE @ Tag = %04x Length = %3d\n",
+ tag, EETag[2]);
return -1;
}
Length = EETag[2];
@@ -441,6 +884,7 @@ static int ReadEEProm(struct i2c_adapter *adapter,
static int WriteEEProm(struct i2c_adapter *adapter,
u16 Tag, u32 Length, u8 *data)
{
+ struct device *pdev = adapter->dev.parent;
int status = 0;
u16 Addr = MICNG_EE_START;
u8 EETag[3];
@@ -458,9 +902,8 @@ static int WriteEEProm(struct i2c_adapter *adapter,
Addr += sizeof(u16) + 1 + EETag[2];
}
if (Addr + sizeof(u16) + 1 + EETag[2] > MICNG_EE_END) {
- pr_err(DEVICE_NAME
- ": Reached EOEE @ Tag = %04x Length = %3d\n",
- tag, EETag[2]);
+ dev_err(pdev, "Reached EOEE @ Tag = %04x Length = %3d\n",
+ tag, EETag[2]);
return -1;
}
@@ -487,13 +930,11 @@ static int WriteEEProm(struct i2c_adapter *adapter,
if (status)
break;
if (Tmp != data[i])
- pr_err(DEVICE_NAME
- "eeprom write error\n");
+ dev_err(pdev, "eeprom write error\n");
retry -= 1;
}
if (status) {
- pr_err(DEVICE_NAME
- ": Timeout polling eeprom\n");
+ dev_err(pdev, "Timeout polling eeprom\n");
break;
}
}
@@ -532,19 +973,20 @@ static int eeprom_write_ushort(struct i2c_adapter *adapter, u16 tag, u16 data)
static s16 osc_deviation(void *priv, s16 deviation, int flag)
{
struct ngene_channel *chan = priv;
+ struct device *pdev = &chan->dev->pci_dev->dev;
struct i2c_adapter *adap = &chan->i2c_adapter;
u16 data = 0;
if (flag) {
data = (u16) deviation;
- pr_info(DEVICE_NAME ": write deviation %d\n",
- deviation);
+ dev_info(pdev, "write deviation %d\n",
+ deviation);
eeprom_write_ushort(adap, 0x1000 + chan->number, data);
} else {
if (eeprom_read_ushort(adap, 0x1000 + chan->number, &data))
data = 0;
- pr_info(DEVICE_NAME ": read deviation %d\n",
- (s16) data);
+ dev_info(pdev, "read deviation %d\n",
+ (s16)data);
}
return (s16) data;
@@ -749,6 +1191,8 @@ static const struct ngene_info ngene_info_terratec = {
/****************************************************************************/
static const struct pci_device_id ngene_id_tbl[] = {
+ NGENE_ID(0x18c3, 0xab04, ngene_info_cineS2),
+ NGENE_ID(0x18c3, 0xab05, ngene_info_cineS2v5),
NGENE_ID(0x18c3, 0xabc3, ngene_info_cineS2),
NGENE_ID(0x18c3, 0xabc4, ngene_info_cineS2),
NGENE_ID(0x18c3, 0xdb01, ngene_info_satixS2),
@@ -769,7 +1213,7 @@ MODULE_DEVICE_TABLE(pci, ngene_id_tbl);
static pci_ers_result_t ngene_error_detected(struct pci_dev *dev,
enum pci_channel_state state)
{
- printk(KERN_ERR DEVICE_NAME ": PCI error\n");
+ dev_err(&dev->dev, "PCI error\n");
if (state == pci_channel_io_perm_failure)
return PCI_ERS_RESULT_DISCONNECT;
if (state == pci_channel_io_frozen)
@@ -779,13 +1223,13 @@ static pci_ers_result_t ngene_error_detected(struct pci_dev *dev,
static pci_ers_result_t ngene_slot_reset(struct pci_dev *dev)
{
- printk(KERN_INFO DEVICE_NAME ": slot reset\n");
+ dev_info(&dev->dev, "slot reset\n");
return 0;
}
static void ngene_resume(struct pci_dev *dev)
{
- printk(KERN_INFO DEVICE_NAME ": resume\n");
+ dev_info(&dev->dev, "resume\n");
}
static const struct pci_error_handlers ngene_errors = {
@@ -805,8 +1249,9 @@ static struct pci_driver ngene_pci_driver = {
static __init int module_init_ngene(void)
{
- printk(KERN_INFO
- "nGene PCIE bridge driver, Copyright (C) 2005-2007 Micronas\n");
+ /* pr_*() since we don't have a device to use with dev_*() yet */
+ pr_info("nGene PCIE bridge driver, Copyright (C) 2005-2007 Micronas\n");
+
return pci_register_driver(&ngene_pci_driver);
}
diff --git a/drivers/media/pci/ngene/ngene-core.c b/drivers/media/pci/ngene/ngene-core.c
index 8c92cb7f7e72..25f16833a475 100644
--- a/drivers/media/pci/ngene/ngene-core.c
+++ b/drivers/media/pci/ngene/ngene-core.c
@@ -51,8 +51,6 @@ MODULE_PARM_DESC(debug, "Print debugging information.");
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-#define dprintk if (debug) printk
-
#define ngwriteb(dat, adr) writeb((dat), dev->iomem + (adr))
#define ngwritel(dat, adr) writel((dat), dev->iomem + (adr))
#define ngwriteb(dat, adr) writeb((dat), dev->iomem + (adr))
@@ -86,6 +84,7 @@ static void event_tasklet(unsigned long data)
static void demux_tasklet(unsigned long data)
{
struct ngene_channel *chan = (struct ngene_channel *)data;
+ struct device *pdev = &chan->dev->pci_dev->dev;
struct SBufferHeader *Cur = chan->nextBuffer;
spin_lock_irq(&chan->state_lock);
@@ -124,16 +123,15 @@ static void demux_tasklet(unsigned long data)
chan->HWState = HWSTATE_RUN;
}
} else {
- printk(KERN_ERR DEVICE_NAME ": OOPS\n");
+ dev_err(pdev, "OOPS\n");
if (chan->HWState == HWSTATE_RUN) {
Cur->ngeneBuffer.SR.Flags &= ~0x40;
break; /* Stop processing stream */
}
}
if (chan->AudioDTOUpdated) {
- printk(KERN_INFO DEVICE_NAME
- ": Update AudioDTO = %d\n",
- chan->AudioDTOValue);
+ dev_info(pdev, "Update AudioDTO = %d\n",
+ chan->AudioDTOValue);
Cur->ngeneBuffer.SR.DTOUpdate =
chan->AudioDTOValue;
chan->AudioDTOUpdated = 0;
@@ -173,6 +171,7 @@ static void demux_tasklet(unsigned long data)
static irqreturn_t irq_handler(int irq, void *dev_id)
{
struct ngene *dev = (struct ngene *)dev_id;
+ struct device *pdev = &dev->pci_dev->dev;
u32 icounts = 0;
irqreturn_t rc = IRQ_NONE;
u32 i = MAX_STREAM;
@@ -213,7 +212,7 @@ static irqreturn_t irq_handler(int irq, void *dev_id)
*(dev->EventBuffer);
dev->EventQueueWriteIndex = nextWriteIndex;
} else {
- printk(KERN_ERR DEVICE_NAME ": event overflow\n");
+ dev_err(pdev, "event overflow\n");
dev->EventQueueOverflowCount += 1;
dev->EventQueueOverflowFlag = 1;
}
@@ -249,23 +248,25 @@ static irqreturn_t irq_handler(int irq, void *dev_id)
static void dump_command_io(struct ngene *dev)
{
+ struct device *pdev = &dev->pci_dev->dev;
u8 buf[8], *b;
ngcpyfrom(buf, HOST_TO_NGENE, 8);
- printk(KERN_ERR "host_to_ngene (%04x): %*ph\n", HOST_TO_NGENE, 8, buf);
+ dev_err(pdev, "host_to_ngene (%04x): %*ph\n", HOST_TO_NGENE, 8, buf);
ngcpyfrom(buf, NGENE_TO_HOST, 8);
- printk(KERN_ERR "ngene_to_host (%04x): %*ph\n", NGENE_TO_HOST, 8, buf);
+ dev_err(pdev, "ngene_to_host (%04x): %*ph\n", NGENE_TO_HOST, 8, buf);
b = dev->hosttongene;
- printk(KERN_ERR "dev->hosttongene (%p): %*ph\n", b, 8, b);
+ dev_err(pdev, "dev->hosttongene (%p): %*ph\n", b, 8, b);
b = dev->ngenetohost;
- printk(KERN_ERR "dev->ngenetohost (%p): %*ph\n", b, 8, b);
+ dev_err(pdev, "dev->ngenetohost (%p): %*ph\n", b, 8, b);
}
static int ngene_command_mutex(struct ngene *dev, struct ngene_command *com)
{
+ struct device *pdev = &dev->pci_dev->dev;
int ret;
u8 *tmpCmdDoneByte;
@@ -313,9 +314,8 @@ static int ngene_command_mutex(struct ngene *dev, struct ngene_command *com)
if (!ret) {
/*ngwritel(0, FORCE_NMI);*/
- printk(KERN_ERR DEVICE_NAME
- ": Command timeout cmd=%02x prev=%02x\n",
- com->cmd.hdr.Opcode, dev->prev_cmd);
+ dev_err(pdev, "Command timeout cmd=%02x prev=%02x\n",
+ com->cmd.hdr.Opcode, dev->prev_cmd);
dump_command_io(dev);
return -1;
}
@@ -553,6 +553,7 @@ static void clear_buffers(struct ngene_channel *chan)
static int ngene_command_stream_control(struct ngene *dev, u8 stream,
u8 control, u8 mode, u8 flags)
{
+ struct device *pdev = &dev->pci_dev->dev;
struct ngene_channel *chan = &dev->channel[stream];
struct ngene_command com;
u16 BsUVI = ((stream & 1) ? 0x9400 : 0x9300);
@@ -572,8 +573,7 @@ static int ngene_command_stream_control(struct ngene *dev, u8 stream,
com.in_len = sizeof(struct FW_STREAM_CONTROL);
com.out_len = 0;
- dprintk(KERN_INFO DEVICE_NAME
- ": Stream=%02x, Control=%02x, Mode=%02x\n",
+ dev_dbg(pdev, "Stream=%02x, Control=%02x, Mode=%02x\n",
com.cmd.StreamControl.Stream, com.cmd.StreamControl.Control,
com.cmd.StreamControl.Mode);
@@ -695,23 +695,24 @@ static int ngene_command_stream_control(struct ngene *dev, u8 stream,
void set_transfer(struct ngene_channel *chan, int state)
{
+ struct device *pdev = &chan->dev->pci_dev->dev;
u8 control = 0, mode = 0, flags = 0;
struct ngene *dev = chan->dev;
int ret;
/*
- printk(KERN_INFO DEVICE_NAME ": st %d\n", state);
+ dev_info(pdev, "st %d\n", state);
msleep(100);
*/
if (state) {
if (chan->running) {
- printk(KERN_INFO DEVICE_NAME ": already running\n");
+ dev_info(pdev, "already running\n");
return;
}
} else {
if (!chan->running) {
- printk(KERN_INFO DEVICE_NAME ": already stopped\n");
+ dev_info(pdev, "already stopped\n");
return;
}
}
@@ -722,7 +723,7 @@ void set_transfer(struct ngene_channel *chan, int state)
if (state) {
spin_lock_irq(&chan->state_lock);
- /* printk(KERN_INFO DEVICE_NAME ": lock=%08x\n",
+ /* dev_info(pdev, "lock=%08x\n",
ngreadl(0x9310)); */
dvb_ringbuffer_flush(&dev->tsout_rbuf);
control = 0x80;
@@ -740,7 +741,7 @@ void set_transfer(struct ngene_channel *chan, int state)
chan->pBufferExchange = tsin_exchange;
spin_unlock_irq(&chan->state_lock);
}
- /* else printk(KERN_INFO DEVICE_NAME ": lock=%08x\n",
+ /* else dev_info(pdev, "lock=%08x\n",
ngreadl(0x9310)); */
mutex_lock(&dev->stream_mutex);
@@ -751,8 +752,7 @@ void set_transfer(struct ngene_channel *chan, int state)
if (!ret)
chan->running = state;
else
- printk(KERN_ERR DEVICE_NAME ": set_transfer %d failed\n",
- state);
+ dev_err(pdev, "%s %d failed\n", __func__, state);
if (!state) {
spin_lock_irq(&chan->state_lock);
chan->pBufferExchange = NULL;
@@ -1195,6 +1195,7 @@ static int ngene_get_buffers(struct ngene *dev)
static void ngene_init(struct ngene *dev)
{
+ struct device *pdev = &dev->pci_dev->dev;
int i;
tasklet_init(&dev->event_tasklet, event_tasklet, (unsigned long)dev);
@@ -1214,12 +1215,12 @@ static void ngene_init(struct ngene *dev)
dev->icounts = ngreadl(NGENE_INT_COUNTS);
dev->device_version = ngreadl(DEV_VER) & 0x0f;
- printk(KERN_INFO DEVICE_NAME ": Device version %d\n",
- dev->device_version);
+ dev_info(pdev, "Device version %d\n", dev->device_version);
}
static int ngene_load_firm(struct ngene *dev)
{
+ struct device *pdev = &dev->pci_dev->dev;
u32 size;
const struct firmware *fw = NULL;
u8 *ngene_fw;
@@ -1253,21 +1254,18 @@ static int ngene_load_firm(struct ngene *dev)
}
if (request_firmware(&fw, fw_name, &dev->pci_dev->dev) < 0) {
- printk(KERN_ERR DEVICE_NAME
- ": Could not load firmware file %s.\n", fw_name);
- printk(KERN_INFO DEVICE_NAME
- ": Copy %s to your hotplug directory!\n", fw_name);
+ dev_err(pdev, "Could not load firmware file %s.\n", fw_name);
+ dev_info(pdev, "Copy %s to your hotplug directory!\n",
+ fw_name);
return -1;
}
if (size == 0)
size = fw->size;
if (size != fw->size) {
- printk(KERN_ERR DEVICE_NAME
- ": Firmware %s has invalid size!", fw_name);
+ dev_err(pdev, "Firmware %s has invalid size!", fw_name);
err = -1;
} else {
- printk(KERN_INFO DEVICE_NAME
- ": Loading firmware file %s.\n", fw_name);
+ dev_info(pdev, "Loading firmware file %s.\n", fw_name);
ngene_fw = (u8 *) fw->data;
err = ngene_command_load_firmware(dev, ngene_fw, size);
}
@@ -1360,14 +1358,14 @@ static int ngene_start(struct ngene *dev)
#ifdef CONFIG_PCI_MSI
/* enable MSI if kernel and card support it */
if (pci_msi_enabled() && dev->card_info->msi_supported) {
+ struct device *pdev = &dev->pci_dev->dev;
unsigned long flags;
ngwritel(0, NGENE_INT_ENABLE);
free_irq(dev->pci_dev->irq, dev);
stat = pci_enable_msi(dev->pci_dev);
if (stat) {
- printk(KERN_INFO DEVICE_NAME
- ": MSI not available\n");
+ dev_info(pdev, "MSI not available\n");
flags = IRQF_SHARED;
} else {
flags = 0;
@@ -1426,6 +1424,13 @@ static void release_channel(struct ngene_channel *chan)
if (chan->fe) {
dvb_unregister_frontend(chan->fe);
+
+ /* release I2C client (tuner) if needed */
+ if (chan->i2c_client_fe) {
+ dvb_module_release(chan->i2c_client[0]);
+ chan->i2c_client[0] = NULL;
+ }
+
dvb_frontend_detach(chan->fe);
chan->fe = NULL;
}
@@ -1461,6 +1466,7 @@ static int init_channel(struct ngene_channel *chan)
chan->users = 0;
chan->type = io;
chan->mode = chan->type; /* for now only one mode */
+ chan->i2c_client_fe = 0; /* be sure this is set to zero */
if (io & NGENE_IO_TSIN) {
chan->fe = NULL;
@@ -1562,19 +1568,46 @@ static int init_channels(struct ngene *dev)
return 0;
}
-static struct cxd2099_cfg cxd_cfg = {
+static const struct cxd2099_cfg cxd_cfgtmpl = {
.bitrate = 62000,
- .adr = 0x40,
.polarity = 0,
.clock_mode = 0,
};
static void cxd_attach(struct ngene *dev)
{
+ struct device *pdev = &dev->pci_dev->dev;
struct ngene_ci *ci = &dev->ci;
+ struct cxd2099_cfg cxd_cfg = cxd_cfgtmpl;
+ struct i2c_client *client;
+ int ret;
+ u8 type;
+
+ /* check for CXD2099AR presence before attaching */
+ ret = ngene_port_has_cxd2099(&dev->channel[0].i2c_adapter, &type);
+ if (!ret) {
+ dev_dbg(pdev, "No CXD2099AR found\n");
+ return;
+ }
+
+ if (type != 1) {
+ dev_warn(pdev, "CXD2099AR is uninitialized!\n");
+ return;
+ }
+
+ cxd_cfg.en = &ci->en;
+ client = dvb_module_probe("cxd2099", NULL,
+ &dev->channel[0].i2c_adapter,
+ 0x40, &cxd_cfg);
+ if (!client)
+ goto err;
- ci->en = cxd2099_attach(&cxd_cfg, dev, &dev->channel[0].i2c_adapter);
ci->dev = dev;
+ dev->channel[0].i2c_client[0] = client;
+ return;
+
+err:
+ dev_err(pdev, "CXD2099AR attach failed\n");
return;
}
@@ -1583,7 +1616,9 @@ static void cxd_detach(struct ngene *dev)
struct ngene_ci *ci = &dev->ci;
dvb_ca_en50221_release(ci->en);
- kfree(ci->en);
+
+ dvb_module_release(dev->channel[0].i2c_client[0]);
+ dev->channel[0].i2c_client[0] = NULL;
ci->en = NULL;
}
@@ -1615,7 +1650,7 @@ void ngene_shutdown(struct pci_dev *pdev)
if (!dev || !shutdown_workaround)
return;
- printk(KERN_INFO DEVICE_NAME ": shutdown workaround...\n");
+ dev_info(&pdev->dev, "shutdown workaround...\n");
ngene_unlink(dev);
pci_disable_device(pdev);
}
@@ -1655,7 +1690,7 @@ int ngene_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
dev->pci_dev = pci_dev;
dev->card_info = (struct ngene_info *)id->driver_data;
- printk(KERN_INFO DEVICE_NAME ": Found %s\n", dev->card_info->name);
+ dev_info(&pci_dev->dev, "Found %s\n", dev->card_info->name);
pci_set_drvdata(pci_dev, dev);
diff --git a/drivers/media/pci/ngene/ngene-dvb.c b/drivers/media/pci/ngene/ngene-dvb.c
index 03fc218a45e9..fee89b9ed9c1 100644
--- a/drivers/media/pci/ngene/ngene-dvb.c
+++ b/drivers/media/pci/ngene/ngene-dvb.c
@@ -38,6 +38,9 @@
#include "ngene.h"
+static int ci_tsfix = 1;
+module_param(ci_tsfix, int, 0444);
+MODULE_PARM_DESC(ci_tsfix, "Detect and fix TS buffer offset shifs in conjunction with CI expansions (default: 1/enabled)");
/****************************************************************************/
/* COMMAND API interface ****************************************************/
@@ -84,18 +87,41 @@ static ssize_t ts_read(struct file *file, char __user *buf,
return count;
}
+static __poll_t ts_poll(struct file *file, poll_table *wait)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct ngene_channel *chan = dvbdev->priv;
+ struct ngene *dev = chan->dev;
+ struct dvb_ringbuffer *rbuf = &dev->tsin_rbuf;
+ struct dvb_ringbuffer *wbuf = &dev->tsout_rbuf;
+ __poll_t mask = 0;
+
+ poll_wait(file, &rbuf->queue, wait);
+ poll_wait(file, &wbuf->queue, wait);
+
+ if (!dvb_ringbuffer_empty(rbuf))
+ mask |= EPOLLIN | EPOLLRDNORM;
+ if (dvb_ringbuffer_free(wbuf) >= 188)
+ mask |= EPOLLOUT | EPOLLWRNORM;
+
+ return mask;
+}
+
static const struct file_operations ci_fops = {
.owner = THIS_MODULE,
.read = ts_read,
.write = ts_write,
.open = dvb_generic_open,
.release = dvb_generic_release,
+ .poll = ts_poll,
+ .mmap = NULL,
};
struct dvb_device ngene_dvbdev_ci = {
- .readers = -1,
- .writers = -1,
- .users = -1,
+ .priv = NULL,
+ .readers = 1,
+ .writers = 1,
+ .users = 2,
.fops = &ci_fops,
};
@@ -116,47 +142,118 @@ static void swap_buffer(u32 *p, u32 len)
/* start of filler packet */
static u8 fill_ts[] = { 0x47, 0x1f, 0xff, 0x10, TS_FILLER };
-/* #define DEBUG_CI_XFER */
-#ifdef DEBUG_CI_XFER
-static u32 ok;
-static u32 overflow;
-static u32 stripped;
-#endif
+static int tsin_find_offset(void *buf, u32 len)
+{
+ int i, l;
+
+ l = len - sizeof(fill_ts);
+ if (l <= 0)
+ return -1;
+
+ for (i = 0; i < l; i++) {
+ if (((char *)buf)[i] == 0x47) {
+ if (!memcmp(buf + i, fill_ts, sizeof(fill_ts)))
+ return i % 188;
+ }
+ }
+
+ return -1;
+}
+
+static inline void tsin_copy_stripped(struct ngene *dev, void *buf)
+{
+ if (memcmp(buf, fill_ts, sizeof(fill_ts)) != 0) {
+ if (dvb_ringbuffer_free(&dev->tsin_rbuf) >= 188) {
+ dvb_ringbuffer_write(&dev->tsin_rbuf, buf, 188);
+ wake_up(&dev->tsin_rbuf.queue);
+ }
+ }
+}
void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags)
{
struct ngene_channel *chan = priv;
struct ngene *dev = chan->dev;
-
+ int tsoff;
if (flags & DF_SWAP32)
swap_buffer(buf, len);
if (dev->ci.en && chan->number == 2) {
+ /* blindly copy buffers if ci_tsfix is disabled */
+ if (!ci_tsfix) {
+ while (len >= 188) {
+ tsin_copy_stripped(dev, buf);
+
+ buf += 188;
+ len -= 188;
+ }
+ return NULL;
+ }
+
+ /* ci_tsfix = 1 */
+
+ /*
+ * since the remainder of the TS packet which got cut off
+ * in the previous tsin_exchange() run is at the beginning
+ * of the new TS buffer, append this to the temp buffer and
+ * send it to the DVB ringbuffer afterwards.
+ */
+ if (chan->tsin_offset) {
+ memcpy(&chan->tsin_buffer[(188 - chan->tsin_offset)],
+ buf, chan->tsin_offset);
+ tsin_copy_stripped(dev, &chan->tsin_buffer);
+
+ buf += chan->tsin_offset;
+ len -= chan->tsin_offset;
+ }
+
+ /*
+ * copy TS packets to the DVB ringbuffer and detect new offset
+ * shifts by checking for a valid TS SYNC byte
+ */
while (len >= 188) {
- if (memcmp(buf, fill_ts, sizeof fill_ts) != 0) {
- if (dvb_ringbuffer_free(&dev->tsin_rbuf) >= 188) {
- dvb_ringbuffer_write(&dev->tsin_rbuf, buf, 188);
- wake_up(&dev->tsin_rbuf.queue);
-#ifdef DEBUG_CI_XFER
- ok++;
-#endif
+ if (*((char *)buf) != 0x47) {
+ /*
+ * no SYNC header, find new offset shift
+ * (max. 188 bytes, tsoff will be mod 188)
+ */
+ tsoff = tsin_find_offset(buf, len);
+ if (tsoff > 0) {
+ chan->tsin_offset += tsoff;
+ chan->tsin_offset %= 188;
+
+ buf += tsoff;
+ len -= tsoff;
+
+ dev_info(&dev->pci_dev->dev,
+ "%s(): tsin_offset shift by %d on channel %d\n",
+ __func__, tsoff,
+ chan->number);
+
+ /*
+ * offset corrected. re-check remaining
+ * len for a full TS frame, break and
+ * skip to fragment handling if < 188.
+ */
+ if (len < 188)
+ break;
}
-#ifdef DEBUG_CI_XFER
- else
- overflow++;
-#endif
}
-#ifdef DEBUG_CI_XFER
- else
- stripped++;
- if (ok % 100 == 0 && overflow)
- printk(KERN_WARNING "%s: ok %u overflow %u dropped %u\n", __func__, ok, overflow, stripped);
-#endif
+ tsin_copy_stripped(dev, buf);
+
buf += 188;
len -= 188;
}
+
+ /*
+ * if a fragment is left, copy to temp buffer. The remainder
+ * will be appended in the next tsin_exchange() iteration.
+ */
+ if (len > 0 && len < 188)
+ memcpy(&chan->tsin_buffer, buf, len);
+
return NULL;
}
diff --git a/drivers/media/pci/ngene/ngene-i2c.c b/drivers/media/pci/ngene/ngene-i2c.c
index 3004947f300b..092d46c2a3a9 100644
--- a/drivers/media/pci/ngene/ngene-i2c.c
+++ b/drivers/media/pci/ngene/ngene-i2c.c
@@ -147,7 +147,7 @@ done:
static u32 ngene_i2c_functionality(struct i2c_adapter *adap)
{
- return I2C_FUNC_SMBUS_EMUL;
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
static const struct i2c_algorithm ngene_i2c_algo = {
diff --git a/drivers/media/pci/ngene/ngene.h b/drivers/media/pci/ngene/ngene.h
index 02dbd18f92d0..01d9f1b58fcb 100644
--- a/drivers/media/pci/ngene/ngene.h
+++ b/drivers/media/pci/ngene/ngene.h
@@ -51,6 +51,22 @@
#define VIDEO_CAP_MPEG4 512
#endif
+#define DEMOD_TYPE_STV090X 0
+#define DEMOD_TYPE_DRXK 1
+#define DEMOD_TYPE_STV0367 2
+
+#define DEMOD_TYPE_XO2 32
+#define DEMOD_TYPE_STV0910 (DEMOD_TYPE_XO2 + 0)
+#define DEMOD_TYPE_SONY_CT2 (DEMOD_TYPE_XO2 + 1)
+#define DEMOD_TYPE_SONY_ISDBT (DEMOD_TYPE_XO2 + 2)
+#define DEMOD_TYPE_SONY_C2T2 (DEMOD_TYPE_XO2 + 3)
+#define DEMOD_TYPE_ST_ATSC (DEMOD_TYPE_XO2 + 4)
+#define DEMOD_TYPE_SONY_C2T2I (DEMOD_TYPE_XO2 + 5)
+
+#define NGENE_XO2_TYPE_NONE 0
+#define NGENE_XO2_TYPE_DUOFLEX 1
+#define NGENE_XO2_TYPE_CI 2
+
enum STREAM {
STREAM_VIDEOIN1 = 0, /* ITU656 or TS Input */
STREAM_VIDEOIN2,
@@ -630,6 +646,8 @@ struct ngene_vopen {
struct ngene_channel {
struct device device;
struct i2c_adapter i2c_adapter;
+ struct i2c_client *i2c_client[1];
+ int i2c_client_fe;
struct ngene *dev;
int number;
@@ -714,6 +732,9 @@ struct ngene_channel {
#endif
int running;
+
+ int tsin_offset;
+ u8 tsin_buffer[188];
};
@@ -891,6 +912,9 @@ int ngene_command_gpio_set(struct ngene *dev, u8 select, u8 level);
void set_transfer(struct ngene_channel *chan, int state);
void FillTSBuffer(void *Buffer, int Length, u32 Flags);
+/* Provided by ngene-cards.c */
+int ngene_port_has_cxd2099(struct i2c_adapter *i2c, u8 *type);
+
/* Provided by ngene-i2c.c */
int ngene_i2c_init(struct ngene *dev, int dev_nr);
diff --git a/drivers/media/pci/saa7134/saa7134-alsa.c b/drivers/media/pci/saa7134/saa7134-alsa.c
index c59b69f1af9d..72311445d13d 100644
--- a/drivers/media/pci/saa7134/saa7134-alsa.c
+++ b/drivers/media/pci/saa7134/saa7134-alsa.c
@@ -273,9 +273,8 @@ static int saa7134_alsa_dma_init(struct saa7134_dev *dev, int nr_pages)
return -ENOMEM;
}
- pr_debug("vmalloc is at addr 0x%08lx, size=%d\n",
- (unsigned long)dma->vaddr,
- nr_pages << PAGE_SHIFT);
+ pr_debug("vmalloc is at addr %p, size=%d\n",
+ dma->vaddr, nr_pages << PAGE_SHIFT);
memset(dma->vaddr, 0, nr_pages << PAGE_SHIFT);
dma->nr_pages = nr_pages;
diff --git a/drivers/media/pci/saa7134/saa7134-dvb.c b/drivers/media/pci/saa7134/saa7134-dvb.c
index a7a63d608dde..3025d38ddb2b 100644
--- a/drivers/media/pci/saa7134/saa7134-dvb.c
+++ b/drivers/media/pci/saa7134/saa7134-dvb.c
@@ -1195,7 +1195,7 @@ static struct s5h1411_config kworld_s5h1411_config = {
.inversion = S5H1411_INVERSION_ON,
.status_mode = S5H1411_DEMODLOCKING,
.mpeg_timing =
- S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+ S5H1411_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK,
};
diff --git a/drivers/media/pci/saa7134/saa7134-input.c b/drivers/media/pci/saa7134/saa7134-input.c
index 33ee8322895e..0e28c5021ac4 100644
--- a/drivers/media/pci/saa7134/saa7134-input.c
+++ b/drivers/media/pci/saa7134/saa7134-input.c
@@ -115,7 +115,7 @@ static int build_key(struct saa7134_dev *dev)
static int get_key_flydvb_trio(struct IR_i2c *ir, enum rc_proto *protocol,
u32 *scancode, u8 *toggle)
{
- int gpio;
+ int gpio, rc;
int attempt = 0;
unsigned char b;
@@ -153,8 +153,11 @@ static int get_key_flydvb_trio(struct IR_i2c *ir, enum rc_proto *protocol,
attempt);
return -EIO;
}
- if (1 != i2c_master_recv(ir->c, &b, 1)) {
+ rc = i2c_master_recv(ir->c, &b, 1);
+ if (rc != 1) {
ir_dbg(ir, "read error\n");
+ if (rc < 0)
+ return rc;
return -EIO;
}
@@ -169,7 +172,7 @@ static int get_key_msi_tvanywhere_plus(struct IR_i2c *ir,
u32 *scancode, u8 *toggle)
{
unsigned char b;
- int gpio;
+ int gpio, rc;
/* <dev> is needed to access GPIO. Used by the saa_readl macro. */
struct saa7134_dev *dev = ir->c->adapter->algo_data;
@@ -193,8 +196,11 @@ static int get_key_msi_tvanywhere_plus(struct IR_i2c *ir,
/* GPIO says there is a button press. Get it. */
- if (1 != i2c_master_recv(ir->c, &b, 1)) {
+ rc = i2c_master_recv(ir->c, &b, 1);
+ if (rc != 1) {
ir_dbg(ir, "read error\n");
+ if (rc < 0)
+ return rc;
return -EIO;
}
@@ -218,6 +224,7 @@ static int get_key_kworld_pc150u(struct IR_i2c *ir, enum rc_proto *protocol,
{
unsigned char b;
unsigned int gpio;
+ int rc;
/* <dev> is needed to access GPIO. Used by the saa_readl macro. */
struct saa7134_dev *dev = ir->c->adapter->algo_data;
@@ -241,8 +248,11 @@ static int get_key_kworld_pc150u(struct IR_i2c *ir, enum rc_proto *protocol,
/* GPIO says there is a button press. Get it. */
- if (1 != i2c_master_recv(ir->c, &b, 1)) {
+ rc = i2c_master_recv(ir->c, &b, 1);
+ if (rc != 1) {
ir_dbg(ir, "read error\n");
+ if (rc < 0)
+ return rc;
return -EIO;
}
@@ -263,11 +273,15 @@ static int get_key_kworld_pc150u(struct IR_i2c *ir, enum rc_proto *protocol,
static int get_key_purpletv(struct IR_i2c *ir, enum rc_proto *protocol,
u32 *scancode, u8 *toggle)
{
+ int rc;
unsigned char b;
/* poll IR chip */
- if (1 != i2c_master_recv(ir->c, &b, 1)) {
+ rc = i2c_master_recv(ir->c, &b, 1);
+ if (rc != 1) {
ir_dbg(ir, "read error\n");
+ if (rc < 0)
+ return rc;
return -EIO;
}
@@ -288,11 +302,17 @@ static int get_key_purpletv(struct IR_i2c *ir, enum rc_proto *protocol,
static int get_key_hvr1110(struct IR_i2c *ir, enum rc_proto *protocol,
u32 *scancode, u8 *toggle)
{
+ int rc;
unsigned char buf[5];
/* poll IR chip */
- if (5 != i2c_master_recv(ir->c, buf, 5))
+ rc = i2c_master_recv(ir->c, buf, 5);
+ if (rc != 5) {
+ ir_dbg(ir, "read error\n");
+ if (rc < 0)
+ return rc;
return -EIO;
+ }
/* Check if some key were pressed */
if (!(buf[0] & 0x80))
@@ -319,6 +339,7 @@ static int get_key_hvr1110(struct IR_i2c *ir, enum rc_proto *protocol,
static int get_key_beholdm6xx(struct IR_i2c *ir, enum rc_proto *protocol,
u32 *scancode, u8 *toggle)
{
+ int rc;
unsigned char data[12];
u32 gpio;
@@ -335,8 +356,11 @@ static int get_key_beholdm6xx(struct IR_i2c *ir, enum rc_proto *protocol,
ir->c->addr = 0x5a >> 1;
- if (12 != i2c_master_recv(ir->c, data, 12)) {
+ rc = i2c_master_recv(ir->c, data, 12);
+ if (rc != 12) {
ir_dbg(ir, "read error\n");
+ if (rc < 0)
+ return rc;
return -EIO;
}
@@ -356,12 +380,16 @@ static int get_key_pinnacle(struct IR_i2c *ir, enum rc_proto *protocol,
u32 *scancode, u8 *toggle, int parity_offset,
int marker, int code_modulo)
{
+ int rc;
unsigned char b[4];
unsigned int start = 0,parity = 0,code = 0;
/* poll IR chip */
- if (4 != i2c_master_recv(ir->c, b, 4)) {
+ rc = i2c_master_recv(ir->c, b, 4);
+ if (rc != 4) {
ir_dbg(ir, "read error\n");
+ if (rc < 0)
+ return rc;
return -EIO;
}
diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c
index 1ca6a32ad10e..1a50ec9d084f 100644
--- a/drivers/media/pci/saa7134/saa7134-video.c
+++ b/drivers/media/pci/saa7134/saa7134-video.c
@@ -1200,7 +1200,7 @@ static int video_release(struct file *file)
saa_andorb(SAA7134_OFMT_DATA_A, 0x1f, 0);
saa_andorb(SAA7134_OFMT_DATA_B, 0x1f, 0);
- saa_call_all(dev, core, s_power, 0);
+ saa_call_all(dev, tuner, standby);
if (vdev->vfl_type == VFL_TYPE_RADIO)
saa_call_all(dev, core, ioctl, SAA6588_CMD_CLOSE, &cmd);
mutex_unlock(&dev->lock);
@@ -1235,12 +1235,12 @@ static __poll_t radio_poll(struct file *file, poll_table *wait)
cmd.instance = file;
cmd.event_list = wait;
- cmd.result = 0;
+ cmd.poll_mask = 0;
mutex_lock(&dev->lock);
saa_call_all(dev, core, ioctl, SAA6588_CMD_POLL, &cmd);
mutex_unlock(&dev->lock);
- return rc | cmd.result;
+ return rc | cmd.poll_mask;
}
/* ------------------------------------------------------------------ */
diff --git a/drivers/media/pci/saa7164/saa7164-dvb.c b/drivers/media/pci/saa7164/saa7164-dvb.c
index e76d3bafe2ce..4f9f03c3b252 100644
--- a/drivers/media/pci/saa7164/saa7164-dvb.c
+++ b/drivers/media/pci/saa7164/saa7164-dvb.c
@@ -78,7 +78,7 @@ static struct s5h1411_config hauppauge_s5h1411_config = {
.vsb_if = S5H1411_IF_3250,
.inversion = S5H1411_INVERSION_ON,
.status_mode = S5H1411_DEMODLOCKING,
- .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+ .mpeg_timing = S5H1411_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK,
};
static struct lgdt3306a_config hauppauge_hvr2255a_config = {
diff --git a/drivers/media/pci/solo6x10/solo6x10-g723.c b/drivers/media/pci/solo6x10/solo6x10-g723.c
index 81be1b8df758..2ac33b5cc454 100644
--- a/drivers/media/pci/solo6x10/solo6x10-g723.c
+++ b/drivers/media/pci/solo6x10/solo6x10-g723.c
@@ -223,9 +223,9 @@ static snd_pcm_uframes_t snd_solo_pcm_pointer(struct snd_pcm_substream *ss)
return idx * G723_FRAMES_PER_PAGE;
}
-static int __snd_solo_pcm_copy(struct snd_pcm_substream *ss,
- unsigned long pos, void *dst,
- unsigned long count, bool in_kernel)
+static int snd_solo_pcm_copy_user(struct snd_pcm_substream *ss, int channel,
+ unsigned long pos, void __user *dst,
+ unsigned long count)
{
struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss);
struct solo_dev *solo_dev = solo_pcm->solo_dev;
@@ -242,10 +242,7 @@ static int __snd_solo_pcm_copy(struct snd_pcm_substream *ss,
if (err)
return err;
- if (in_kernel)
- memcpy(dst, solo_pcm->g723_buf, G723_PERIOD_BYTES);
- else if (copy_to_user((void __user *)dst,
- solo_pcm->g723_buf, G723_PERIOD_BYTES))
+ if (copy_to_user(dst, solo_pcm->g723_buf, G723_PERIOD_BYTES))
return -EFAULT;
dst += G723_PERIOD_BYTES;
}
@@ -253,18 +250,30 @@ static int __snd_solo_pcm_copy(struct snd_pcm_substream *ss,
return 0;
}
-static int snd_solo_pcm_copy_user(struct snd_pcm_substream *ss, int channel,
- unsigned long pos, void __user *dst,
- unsigned long count)
-{
- return __snd_solo_pcm_copy(ss, pos, (void *)dst, count, false);
-}
-
static int snd_solo_pcm_copy_kernel(struct snd_pcm_substream *ss, int channel,
unsigned long pos, void *dst,
unsigned long count)
{
- return __snd_solo_pcm_copy(ss, pos, dst, count, true);
+ struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss);
+ struct solo_dev *solo_dev = solo_pcm->solo_dev;
+ int err, i;
+
+ for (i = 0; i < (count / G723_FRAMES_PER_PAGE); i++) {
+ int page = (pos / G723_FRAMES_PER_PAGE) + i;
+
+ err = solo_p2m_dma_t(solo_dev, 0, solo_pcm->g723_dma,
+ SOLO_G723_EXT_ADDR(solo_dev) +
+ (page * G723_PERIOD_BLOCK) +
+ (ss->number * G723_PERIOD_BYTES),
+ G723_PERIOD_BYTES, 0, 0);
+ if (err)
+ return err;
+
+ memcpy(dst, solo_pcm->g723_buf, G723_PERIOD_BYTES);
+ dst += G723_PERIOD_BYTES;
+ }
+
+ return 0;
}
static const struct snd_pcm_ops snd_solo_pcm_ops = {
diff --git a/drivers/media/pci/solo6x10/solo6x10-p2m.c b/drivers/media/pci/solo6x10/solo6x10-p2m.c
index 8c8484674d2f..46c30430e30b 100644
--- a/drivers/media/pci/solo6x10/solo6x10-p2m.c
+++ b/drivers/media/pci/solo6x10/solo6x10-p2m.c
@@ -69,14 +69,11 @@ int solo_p2m_dma_desc(struct solo_dev *solo_dev,
unsigned int timeout;
unsigned int config = 0;
int ret = 0;
- int p2m_id = 0;
+ unsigned int p2m_id = 0;
/* Get next ID. According to Softlogic, 6110 has problems on !=0 P2M */
- if (solo_dev->type != SOLO_DEV_6110 && multi_p2m) {
+ if (solo_dev->type != SOLO_DEV_6110 && multi_p2m)
p2m_id = atomic_inc_return(&solo_dev->p2m_count) % SOLO_NR_P2M;
- if (p2m_id < 0)
- p2m_id = -p2m_id;
- }
p2m_dev = &solo_dev->p2m_dev[p2m_id];
diff --git a/drivers/media/pci/ttpci/ttpci-eeprom.c b/drivers/media/pci/ttpci/ttpci-eeprom.c
index 9534f29c1ffd..78c7a6589be5 100644
--- a/drivers/media/pci/ttpci/ttpci-eeprom.c
+++ b/drivers/media/pci/ttpci/ttpci-eeprom.c
@@ -138,7 +138,7 @@ static int ttpci_eeprom_read_encodedMAC(struct i2c_adapter *adapter, u8 * encode
int ttpci_eeprom_parse_mac(struct i2c_adapter *adapter, u8 *proposed_mac)
{
- int ret, i;
+ int ret;
u8 encodedMAC[20];
u8 decodedMAC[6];
@@ -153,11 +153,8 @@ int ttpci_eeprom_parse_mac(struct i2c_adapter *adapter, u8 *proposed_mac)
ret = getmac_tt(decodedMAC, encodedMAC);
if( ret != 0 ) {
dprintk("adapter failed MAC signature check\n");
- dprintk("encoded MAC from EEPROM was " );
- for(i=0; i<19; i++) {
- dprintk( "%.2x:", encodedMAC[i]);
- }
- dprintk("%.2x\n", encodedMAC[19]);
+ dprintk("encoded MAC from EEPROM was %*phC",
+ (int)sizeof(encodedMAC), &encodedMAC);
eth_zero_addr(proposed_mac);
return ret;
}
diff --git a/drivers/media/pci/zoran/zoran_driver.c b/drivers/media/pci/zoran/zoran_driver.c
index 8d4e7d930a66..14f9c0e26a1c 100644
--- a/drivers/media/pci/zoran/zoran_driver.c
+++ b/drivers/media/pci/zoran/zoran_driver.c
@@ -241,8 +241,8 @@ static int v4l_fbuffer_alloc(struct zoran_fh *fh)
SetPageReserved(virt_to_page(mem + off));
dprintk(4,
KERN_INFO
- "%s: %s - V4L frame %d mem 0x%lx (bus: 0x%llx)\n",
- ZR_DEVNAME(zr), __func__, i, (unsigned long) mem,
+ "%s: %s - V4L frame %d mem %p (bus: 0x%llx)\n",
+ ZR_DEVNAME(zr), __func__, i, mem,
(unsigned long long)virt_to_bus(mem));
}
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 614fbef08ddc..c7a1cf8a1b01 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -31,8 +31,6 @@ source "drivers/media/platform/davinci/Kconfig"
source "drivers/media/platform/omap/Kconfig"
-source "drivers/media/platform/blackfin/Kconfig"
-
config VIDEO_SH_VOU
tristate "SuperH VOU video output driver"
depends on MEDIA_CAMERA_SUPPORT
@@ -54,26 +52,6 @@ config VIDEO_VIU
Say Y here if you want to enable VIU device on MPC5121e Rev2+.
In doubt, say N.
-config VIDEO_M32R_AR
- tristate "AR devices"
- depends on VIDEO_V4L2
- depends on M32R || COMPILE_TEST
- ---help---
- This is a video4linux driver for the Renesas AR (Artificial Retina)
- camera module.
-
-config VIDEO_M32R_AR_M64278
- tristate "AR device with color module M64278(VGA)"
- depends on PLAT_M32700UT
- select VIDEO_M32R_AR
- ---help---
- This is a video4linux driver for the Renesas AR (Artificial
- Retina) with M64278E-800 camera module.
- This module supports VGA(640x480 pixels) resolutions.
-
- To compile this driver as a module, choose M here: the
- module will be called arv.
-
config VIDEO_MUX
tristate "Video Multiplexer"
select MULTIPLEXER
@@ -144,6 +122,15 @@ config VIDEO_STM32_DCMI
To compile this driver as a module, choose M here: the module
will be called stm32-dcmi.
+config VIDEO_RENESAS_CEU
+ tristate "Renesas Capture Engine Unit (CEU) driver"
+ depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA
+ depends on ARCH_SHMOBILE || ARCH_R7S72100 || COMPILE_TEST
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_FWNODE
+ ---help---
+ This is a v4l2 driver for the Renesas CEU Interface
+
source "drivers/media/platform/soc_camera/Kconfig"
source "drivers/media/platform/exynos4-is/Kconfig"
source "drivers/media/platform/am437x/Kconfig"
@@ -582,7 +569,7 @@ config CEC_GPIO
config VIDEO_SAMSUNG_S5P_CEC
tristate "Samsung S5P CEC driver"
- depends on PLAT_S5P || ARCH_EXYNOS || COMPILE_TEST
+ depends on ARCH_EXYNOS || COMPILE_TEST
select CEC_CORE
select CEC_NOTIFIER
---help---
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 7f3080437be6..932515df4477 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -3,8 +3,6 @@
# Makefile for the video capture/playback device drivers.
#
-obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o
-
obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o
obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/
obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/
@@ -53,8 +51,6 @@ obj-$(CONFIG_VIDEO_TEGRA_HDMI_CEC) += tegra-cec/
obj-y += stm32/
-obj-y += blackfin/
-
obj-y += davinci/
obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o
@@ -62,6 +58,7 @@ obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o
obj-$(CONFIG_SOC_CAMERA) += soc_camera/
obj-$(CONFIG_VIDEO_RCAR_DRIF) += rcar_drif.o
+obj-$(CONFIG_VIDEO_RENESAS_CEU) += renesas-ceu.o
obj-$(CONFIG_VIDEO_RENESAS_FCP) += rcar-fcp.o
obj-$(CONFIG_VIDEO_RENESAS_FDP1) += rcar_fdp1.o
obj-$(CONFIG_VIDEO_RENESAS_JPU) += rcar_jpu.o
diff --git a/drivers/media/platform/arv.c b/drivers/media/platform/arv.c
deleted file mode 100644
index 1e865fea803c..000000000000
--- a/drivers/media/platform/arv.c
+++ /dev/null
@@ -1,884 +0,0 @@
-/*
- * Colour AR M64278(VGA) driver for Video4Linux
- *
- * Copyright (C) 2003 Takeo Takahashi <takahashi.takeo@renesas.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.
- *
- * Some code is taken from AR driver sample program for M3T-M32700UT.
- *
- * AR driver sample (M32R SDK):
- * Copyright (c) 2003 RENESAS TECHNOROGY CORPORATION
- * AND RENESAS SOLUTIONS CORPORATION
- * All Rights Reserved.
- *
- * 2003-09-01: Support w3cam by Takeo Takahashi
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/sched.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-fh.h>
-#include <linux/mutex.h>
-
-#include <linux/uaccess.h>
-#include <asm/m32r.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <asm/byteorder.h>
-
-#if 0
-#define DEBUG(n, args...) printk(KERN_INFO args)
-#define CHECK_LOST 1
-#else
-#define DEBUG(n, args...)
-#define CHECK_LOST 0
-#endif
-
-/*
- * USE_INT is always 0, interrupt mode is not available
- * on linux due to lack of speed
- */
-#define USE_INT 0 /* Don't modify */
-
-#define VERSION "0.0.5"
-
-#define ar_inl(addr) inl((unsigned long)(addr))
-#define ar_outl(val, addr) outl((unsigned long)(val), (unsigned long)(addr))
-
-extern struct cpuinfo_m32r boot_cpu_data;
-
-/*
- * CCD pixel size
- * Note that M32700UT does not support CIF mode, but QVGA is
- * supported by M32700UT hardware using VGA mode of AR LSI.
- *
- * Supported: VGA (Normal mode, Interlace mode)
- * QVGA (Always Interlace mode of VGA)
- *
- */
-#define AR_WIDTH_VGA 640
-#define AR_HEIGHT_VGA 480
-#define AR_WIDTH_QVGA 320
-#define AR_HEIGHT_QVGA 240
-#define MIN_AR_WIDTH AR_WIDTH_QVGA
-#define MIN_AR_HEIGHT AR_HEIGHT_QVGA
-#define MAX_AR_WIDTH AR_WIDTH_VGA
-#define MAX_AR_HEIGHT AR_HEIGHT_VGA
-
-/* bits & bytes per pixel */
-#define AR_BITS_PER_PIXEL 16
-#define AR_BYTES_PER_PIXEL (AR_BITS_PER_PIXEL / 8)
-
-/* line buffer size */
-#define AR_LINE_BYTES_VGA (AR_WIDTH_VGA * AR_BYTES_PER_PIXEL)
-#define AR_LINE_BYTES_QVGA (AR_WIDTH_QVGA * AR_BYTES_PER_PIXEL)
-#define MAX_AR_LINE_BYTES AR_LINE_BYTES_VGA
-
-/* frame size & type */
-#define AR_FRAME_BYTES_VGA \
- (AR_WIDTH_VGA * AR_HEIGHT_VGA * AR_BYTES_PER_PIXEL)
-#define AR_FRAME_BYTES_QVGA \
- (AR_WIDTH_QVGA * AR_HEIGHT_QVGA * AR_BYTES_PER_PIXEL)
-#define MAX_AR_FRAME_BYTES \
- (MAX_AR_WIDTH * MAX_AR_HEIGHT * AR_BYTES_PER_PIXEL)
-
-#define AR_MAX_FRAME 15
-
-/* capture size */
-#define AR_SIZE_VGA 0
-#define AR_SIZE_QVGA 1
-
-/* capture mode */
-#define AR_MODE_INTERLACE 0
-#define AR_MODE_NORMAL 1
-
-struct ar {
- struct v4l2_device v4l2_dev;
- struct video_device vdev;
- int start_capture; /* duaring capture in INT. mode. */
-#if USE_INT
- unsigned char *line_buff; /* DMA line buffer */
-#endif
- unsigned char *frame[MAX_AR_HEIGHT]; /* frame data */
- short size; /* capture size */
- short mode; /* capture mode */
- int width, height;
- int frame_bytes, line_bytes;
- wait_queue_head_t wait;
- struct mutex lock;
-};
-
-static struct ar ardev;
-
-static int video_nr = -1; /* video device number (first free) */
-static unsigned char yuv[MAX_AR_FRAME_BYTES];
-
-/* module parameters */
-/* default frequency */
-#define DEFAULT_FREQ 50 /* 50 or 75 (MHz) is available as BCLK */
-static int freq = DEFAULT_FREQ; /* BCLK: available 50 or 70 (MHz) */
-static int vga; /* default mode(0:QVGA mode, other:VGA mode) */
-static int vga_interlace; /* 0 is normal mode for, else interlace mode */
-module_param(freq, int, 0);
-module_param(vga, int, 0);
-module_param(vga_interlace, int, 0);
-
-static void wait_for_vsync(void)
-{
- while (ar_inl(ARVCR0) & ARVCR0_VDS) /* wait for VSYNC */
- cpu_relax();
- while (!(ar_inl(ARVCR0) & ARVCR0_VDS)) /* wait for VSYNC */
- cpu_relax();
-}
-
-static void wait_acknowledge(void)
-{
- int i;
-
- for (i = 0; i < 1000; i++)
- cpu_relax();
- while (ar_inl(PLDI2CSTS) & PLDI2CSTS_NOACK)
- cpu_relax();
-}
-
-/*******************************************************************
- * I2C functions
- *******************************************************************/
-static void iic(int n, unsigned long addr, unsigned long data1, unsigned long data2,
- unsigned long data3)
-{
- int i;
-
- /* Slave Address */
- ar_outl(addr, PLDI2CDATA);
- wait_for_vsync();
-
- /* Start */
- ar_outl(1, PLDI2CCND);
- wait_acknowledge();
-
- /* Transfer data 1 */
- ar_outl(data1, PLDI2CDATA);
- wait_for_vsync();
- ar_outl(PLDI2CSTEN_STEN, PLDI2CSTEN);
- wait_acknowledge();
-
- /* Transfer data 2 */
- ar_outl(data2, PLDI2CDATA);
- wait_for_vsync();
- ar_outl(PLDI2CSTEN_STEN, PLDI2CSTEN);
- wait_acknowledge();
-
- if (n == 3) {
- /* Transfer data 3 */
- ar_outl(data3, PLDI2CDATA);
- wait_for_vsync();
- ar_outl(PLDI2CSTEN_STEN, PLDI2CSTEN);
- wait_acknowledge();
- }
-
- /* Stop */
- for (i = 0; i < 100; i++)
- cpu_relax();
- ar_outl(2, PLDI2CCND);
- ar_outl(2, PLDI2CCND);
-
- while (ar_inl(PLDI2CSTS) & PLDI2CSTS_BB)
- cpu_relax();
-}
-
-
-static void init_iic(void)
-{
- DEBUG(1, "init_iic:\n");
-
- /*
- * ICU Setting (iic)
- */
- /* I2C Setting */
- ar_outl(0x0, PLDI2CCR); /* I2CCR Disable */
- ar_outl(0x0300, PLDI2CMOD); /* I2CMOD ACK/8b-data/7b-addr/auto */
- ar_outl(0x1, PLDI2CACK); /* I2CACK ACK */
-
- /* I2C CLK */
- /* 50MH-100k */
- if (freq == 75)
- ar_outl(369, PLDI2CFREQ); /* BCLK = 75MHz */
- else if (freq == 50)
- ar_outl(244, PLDI2CFREQ); /* BCLK = 50MHz */
- else
- ar_outl(244, PLDI2CFREQ); /* default: BCLK = 50MHz */
- ar_outl(0x1, PLDI2CCR); /* I2CCR Enable */
-}
-
-/**************************************************************************
- *
- * Video4Linux Interface functions
- *
- **************************************************************************/
-
-static inline void disable_dma(void)
-{
- ar_outl(0x8000, M32R_DMAEN_PORTL); /* disable DMA0 */
-}
-
-static inline void enable_dma(void)
-{
- ar_outl(0x8080, M32R_DMAEN_PORTL); /* enable DMA0 */
-}
-
-static inline void clear_dma_status(void)
-{
- ar_outl(0x8000, M32R_DMAEDET_PORTL); /* clear status */
-}
-
-static void wait_for_vertical_sync(struct ar *ar, int exp_line)
-{
-#if CHECK_LOST
- int tmout = 10000; /* FIXME */
- int l;
-
- /*
- * check HCOUNT because we cannot check vertical sync.
- */
- for (; tmout >= 0; tmout--) {
- l = ar_inl(ARVHCOUNT);
- if (l == exp_line)
- break;
- }
- if (tmout < 0)
- v4l2_err(&ar->v4l2_dev, "lost %d -> %d\n", exp_line, l);
-#else
- while (ar_inl(ARVHCOUNT) != exp_line)
- cpu_relax();
-#endif
-}
-
-static ssize_t ar_read(struct file *file, char *buf, size_t count, loff_t *ppos)
-{
- struct ar *ar = video_drvdata(file);
- long ret = ar->frame_bytes; /* return read bytes */
- unsigned long arvcr1 = 0;
- unsigned long flags;
- unsigned char *p;
- int h, w;
- unsigned char *py, *pu, *pv;
-#if !USE_INT
- int l;
-#endif
-
- DEBUG(1, "ar_read()\n");
-
- if (ar->size == AR_SIZE_QVGA)
- arvcr1 |= ARVCR1_QVGA;
- if (ar->mode == AR_MODE_NORMAL)
- arvcr1 |= ARVCR1_NORMAL;
-
- mutex_lock(&ar->lock);
-
-#if USE_INT
- local_irq_save(flags);
- disable_dma();
- ar_outl(0xa1871300, M32R_DMA0CR0_PORTL);
- ar_outl(0x01000000, M32R_DMA0CR1_PORTL);
-
- /* set AR FIFO address as source(BSEL5) */
- ar_outl(ARDATA32, M32R_DMA0CSA_PORTL);
- ar_outl(ARDATA32, M32R_DMA0RSA_PORTL);
- ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL); /* destination addr. */
- ar_outl(ar->line_buff, M32R_DMA0RDA_PORTL); /* reload address */
- ar_outl(ar->line_bytes, M32R_DMA0CBCUT_PORTL); /* byte count (bytes) */
- ar_outl(ar->line_bytes, M32R_DMA0RBCUT_PORTL); /* reload count (bytes) */
-
- /*
- * Okay, kick AR LSI to invoke an interrupt
- */
- ar->start_capture = -1;
- ar_outl(arvcr1 | ARVCR1_HIEN, ARVCR1);
- local_irq_restore(flags);
- /* .... AR interrupts .... */
- wait_event_interruptible(ar->wait, ar->start_capture == 0);
- if (signal_pending(current)) {
- printk(KERN_ERR "arv: interrupted while get frame data.\n");
- ret = -EINTR;
- goto out_up;
- }
-#else /* ! USE_INT */
- /* polling */
- ar_outl(arvcr1, ARVCR1);
- disable_dma();
- ar_outl(0x8000, M32R_DMAEDET_PORTL);
- ar_outl(0xa0861300, M32R_DMA0CR0_PORTL);
- ar_outl(0x01000000, M32R_DMA0CR1_PORTL);
- ar_outl(ARDATA32, M32R_DMA0CSA_PORTL);
- ar_outl(ARDATA32, M32R_DMA0RSA_PORTL);
- ar_outl(ar->line_bytes, M32R_DMA0CBCUT_PORTL);
- ar_outl(ar->line_bytes, M32R_DMA0RBCUT_PORTL);
-
- local_irq_save(flags);
- while (ar_inl(ARVHCOUNT) != 0) /* wait for 0 */
- cpu_relax();
- if (ar->mode == AR_MODE_INTERLACE && ar->size == AR_SIZE_VGA) {
- for (h = 0; h < ar->height; h++) {
- wait_for_vertical_sync(ar, h);
- if (h < (AR_HEIGHT_VGA/2))
- l = h << 1;
- else
- l = (((h - (AR_HEIGHT_VGA/2)) << 1) + 1);
- ar_outl(virt_to_phys(ar->frame[l]), M32R_DMA0CDA_PORTL);
- enable_dma();
- while (!(ar_inl(M32R_DMAEDET_PORTL) & 0x8000))
- cpu_relax();
- disable_dma();
- clear_dma_status();
- ar_outl(0xa0861300, M32R_DMA0CR0_PORTL);
- }
- } else {
- for (h = 0; h < ar->height; h++) {
- wait_for_vertical_sync(ar, h);
- ar_outl(virt_to_phys(ar->frame[h]), M32R_DMA0CDA_PORTL);
- enable_dma();
- while (!(ar_inl(M32R_DMAEDET_PORTL) & 0x8000))
- cpu_relax();
- disable_dma();
- clear_dma_status();
- ar_outl(0xa0861300, M32R_DMA0CR0_PORTL);
- }
- }
- local_irq_restore(flags);
-#endif /* ! USE_INT */
-
- /*
- * convert YUV422 to YUV422P
- * +--------------------+
- * | Y0,Y1,... |
- * | ..............Yn |
- * +--------------------+
- * | U0,U1,........Un |
- * +--------------------+
- * | V0,V1,........Vn |
- * +--------------------+
- */
- py = yuv;
- pu = py + (ar->frame_bytes / 2);
- pv = pu + (ar->frame_bytes / 4);
- for (h = 0; h < ar->height; h++) {
- p = ar->frame[h];
- for (w = 0; w < ar->line_bytes; w += 4) {
- *py++ = *p++;
- *pu++ = *p++;
- *py++ = *p++;
- *pv++ = *p++;
- }
- }
- if (copy_to_user(buf, yuv, ar->frame_bytes)) {
- v4l2_err(&ar->v4l2_dev, "failed while copy_to_user yuv.\n");
- ret = -EFAULT;
- goto out_up;
- }
- DEBUG(1, "ret = %d\n", ret);
-out_up:
- mutex_unlock(&ar->lock);
- return ret;
-}
-
-static int ar_querycap(struct file *file, void *priv,
- struct v4l2_capability *vcap)
-{
- struct ar *ar = video_drvdata(file);
-
- strlcpy(vcap->driver, ar->vdev.name, sizeof(vcap->driver));
- strlcpy(vcap->card, "Colour AR VGA", sizeof(vcap->card));
- strlcpy(vcap->bus_info, "Platform", sizeof(vcap->bus_info));
- vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
- vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS;
- return 0;
-}
-
-static int ar_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
-{
- if (vin->index > 0)
- return -EINVAL;
- strlcpy(vin->name, "Camera", sizeof(vin->name));
- vin->type = V4L2_INPUT_TYPE_CAMERA;
- vin->audioset = 0;
- vin->tuner = 0;
- vin->std = V4L2_STD_ALL;
- vin->status = 0;
- return 0;
-}
-
-static int ar_g_input(struct file *file, void *fh, unsigned int *inp)
-{
- *inp = 0;
- return 0;
-}
-
-static int ar_s_input(struct file *file, void *fh, unsigned int inp)
-{
- return inp ? -EINVAL : 0;
-}
-
-static int ar_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct ar *ar = video_drvdata(file);
- struct v4l2_pix_format *pix = &fmt->fmt.pix;
-
- pix->width = ar->width;
- pix->height = ar->height;
- pix->pixelformat = V4L2_PIX_FMT_YUV422P;
- pix->field = (ar->mode == AR_MODE_NORMAL) ? V4L2_FIELD_NONE : V4L2_FIELD_INTERLACED;
- pix->bytesperline = ar->width;
- pix->sizeimage = 2 * ar->width * ar->height;
- /* Just a guess */
- pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
- return 0;
-}
-
-static int ar_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct ar *ar = video_drvdata(file);
- struct v4l2_pix_format *pix = &fmt->fmt.pix;
-
- if (pix->height <= AR_HEIGHT_QVGA || pix->width <= AR_WIDTH_QVGA) {
- pix->height = AR_HEIGHT_QVGA;
- pix->width = AR_WIDTH_QVGA;
- pix->field = V4L2_FIELD_INTERLACED;
- } else {
- pix->height = AR_HEIGHT_VGA;
- pix->width = AR_WIDTH_VGA;
- pix->field = vga_interlace ? V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE;
- }
- pix->pixelformat = V4L2_PIX_FMT_YUV422P;
- pix->bytesperline = ar->width;
- pix->sizeimage = 2 * ar->width * ar->height;
- /* Just a guess */
- pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
- return 0;
-}
-
-static int ar_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct ar *ar = video_drvdata(file);
- struct v4l2_pix_format *pix = &fmt->fmt.pix;
- int ret = ar_try_fmt_vid_cap(file, fh, fmt);
-
- if (ret)
- return ret;
- mutex_lock(&ar->lock);
- ar->width = pix->width;
- ar->height = pix->height;
- if (ar->width == AR_WIDTH_VGA) {
- ar->size = AR_SIZE_VGA;
- ar->frame_bytes = AR_FRAME_BYTES_VGA;
- ar->line_bytes = AR_LINE_BYTES_VGA;
- if (vga_interlace)
- ar->mode = AR_MODE_INTERLACE;
- else
- ar->mode = AR_MODE_NORMAL;
- } else {
- ar->size = AR_SIZE_QVGA;
- ar->frame_bytes = AR_FRAME_BYTES_QVGA;
- ar->line_bytes = AR_LINE_BYTES_QVGA;
- ar->mode = AR_MODE_INTERLACE;
- }
- /* Ok we figured out what to use from our wide choice */
- mutex_unlock(&ar->lock);
- return 0;
-}
-
-static int ar_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
-{
- static struct v4l2_fmtdesc formats[] = {
- { 0, 0, 0,
- "YUV 4:2:2 Planar", V4L2_PIX_FMT_YUV422P,
- { 0, 0, 0, 0 }
- },
- };
- enum v4l2_buf_type type = fmt->type;
-
- if (fmt->index > 0)
- return -EINVAL;
-
- *fmt = formats[fmt->index];
- fmt->type = type;
- return 0;
-}
-
-#if USE_INT
-/*
- * Interrupt handler
- */
-static void ar_interrupt(int irq, void *dev)
-{
- struct ar *ar = dev;
- unsigned int line_count;
- unsigned int line_number;
- unsigned int arvcr1;
-
- line_count = ar_inl(ARVHCOUNT); /* line number */
- if (ar->mode == AR_MODE_INTERLACE && ar->size == AR_SIZE_VGA) {
- /* operations for interlace mode */
- if (line_count < (AR_HEIGHT_VGA / 2)) /* even line */
- line_number = (line_count << 1);
- else /* odd line */
- line_number =
- (((line_count - (AR_HEIGHT_VGA / 2)) << 1) + 1);
- } else {
- line_number = line_count;
- }
-
- if (line_number == 0) {
- /*
- * It is an interrupt for line 0.
- * we have to start capture.
- */
- disable_dma();
-#if 0
- ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL); /* needless? */
-#endif
- memcpy(ar->frame[0], ar->line_buff, ar->line_bytes);
-#if 0
- ar_outl(0xa1861300, M32R_DMA0CR0_PORTL);
-#endif
- enable_dma();
- ar->start_capture = 1; /* during capture */
- return;
- }
-
- if (ar->start_capture == 1 && line_number <= (ar->height - 1)) {
- disable_dma();
- memcpy(ar->frame[line_number], ar->line_buff, ar->line_bytes);
-
- /*
- * if captured all line of a frame, disable AR interrupt
- * and wake a process up.
- */
- if (line_number == (ar->height - 1)) { /* end of line */
-
- ar->start_capture = 0;
-
- /* disable AR interrupt request */
- arvcr1 = ar_inl(ARVCR1);
- arvcr1 &= ~ARVCR1_HIEN; /* clear int. flag */
- ar_outl(arvcr1, ARVCR1); /* disable */
- wake_up_interruptible(&ar->wait);
- } else {
-#if 0
- ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL);
- ar_outl(0xa1861300, M32R_DMA0CR0_PORTL);
-#endif
- enable_dma();
- }
- }
-}
-#endif
-
-/*
- * ar_initialize()
- * ar_initialize() is called by video_register_device() and
- * initializes AR LSI and peripherals.
- *
- * -1 is returned in all failures.
- * 0 is returned in success.
- *
- */
-static int ar_initialize(struct ar *ar)
-{
- unsigned long cr = 0;
- int i, found = 0;
-
- DEBUG(1, "ar_initialize:\n");
-
- /*
- * initialize AR LSI
- */
- ar_outl(0, ARVCR0); /* assert reset of AR LSI */
- for (i = 0; i < 0x18; i++) /* wait for over 10 cycles @ 27MHz */
- cpu_relax();
- ar_outl(ARVCR0_RST, ARVCR0); /* negate reset of AR LSI (enable) */
- for (i = 0; i < 0x40d; i++) /* wait for over 420 cycles @ 27MHz */
- cpu_relax();
-
- /* AR uses INT3 of CPU as interrupt pin. */
- ar_outl(ARINTSEL_INT3, ARINTSEL);
-
- if (ar->size == AR_SIZE_QVGA)
- cr |= ARVCR1_QVGA;
- if (ar->mode == AR_MODE_NORMAL)
- cr |= ARVCR1_NORMAL;
- ar_outl(cr, ARVCR1);
-
- /*
- * Initialize IIC so that CPU can communicate with AR LSI,
- * and send boot commands to AR LSI.
- */
- init_iic();
-
- for (i = 0; i < 0x100000; i++) { /* > 0xa1d10, 56ms */
- if ((ar_inl(ARVCR0) & ARVCR0_VDS)) { /* VSYNC */
- found = 1;
- break;
- }
- }
-
- if (found == 0)
- return -ENODEV;
-
- v4l2_info(&ar->v4l2_dev, "Initializing ");
-
- iic(2, 0x78, 0x11, 0x01, 0x00); /* start */
- iic(3, 0x78, 0x12, 0x00, 0x06);
- iic(3, 0x78, 0x12, 0x12, 0x30);
- iic(3, 0x78, 0x12, 0x15, 0x58);
- iic(3, 0x78, 0x12, 0x17, 0x30);
- printk(KERN_CONT ".");
- iic(3, 0x78, 0x12, 0x1a, 0x97);
- iic(3, 0x78, 0x12, 0x1b, 0xff);
- iic(3, 0x78, 0x12, 0x1c, 0xff);
- iic(3, 0x78, 0x12, 0x26, 0x10);
- iic(3, 0x78, 0x12, 0x27, 0x00);
- printk(KERN_CONT ".");
- iic(2, 0x78, 0x34, 0x02, 0x00);
- iic(2, 0x78, 0x7a, 0x10, 0x00);
- iic(2, 0x78, 0x80, 0x39, 0x00);
- iic(2, 0x78, 0x81, 0xe6, 0x00);
- iic(2, 0x78, 0x8d, 0x00, 0x00);
- printk(KERN_CONT ".");
- iic(2, 0x78, 0x8e, 0x0c, 0x00);
- iic(2, 0x78, 0x8f, 0x00, 0x00);
-#if 0
- iic(2, 0x78, 0x90, 0x00, 0x00); /* AWB on=1 off=0 */
-#endif
- iic(2, 0x78, 0x93, 0x01, 0x00);
- iic(2, 0x78, 0x94, 0xcd, 0x00);
- iic(2, 0x78, 0x95, 0x00, 0x00);
- printk(KERN_CONT ".");
- iic(2, 0x78, 0x96, 0xa0, 0x00);
- iic(2, 0x78, 0x97, 0x00, 0x00);
- iic(2, 0x78, 0x98, 0x60, 0x00);
- iic(2, 0x78, 0x99, 0x01, 0x00);
- iic(2, 0x78, 0x9a, 0x19, 0x00);
- printk(KERN_CONT ".");
- iic(2, 0x78, 0x9b, 0x02, 0x00);
- iic(2, 0x78, 0x9c, 0xe8, 0x00);
- iic(2, 0x78, 0x9d, 0x02, 0x00);
- iic(2, 0x78, 0x9e, 0x2e, 0x00);
- iic(2, 0x78, 0xb8, 0x78, 0x00);
- iic(2, 0x78, 0xba, 0x05, 0x00);
-#if 0
- iic(2, 0x78, 0x83, 0x8c, 0x00); /* brightness */
-#endif
- printk(KERN_CONT ".");
-
- /* color correction */
- iic(3, 0x78, 0x49, 0x00, 0x95); /* a */
- iic(3, 0x78, 0x49, 0x01, 0x96); /* b */
- iic(3, 0x78, 0x49, 0x03, 0x85); /* c */
- iic(3, 0x78, 0x49, 0x04, 0x97); /* d */
- iic(3, 0x78, 0x49, 0x02, 0x7e); /* e(Lo) */
- iic(3, 0x78, 0x49, 0x05, 0xa4); /* f(Lo) */
- iic(3, 0x78, 0x49, 0x06, 0x04); /* e(Hi) */
- iic(3, 0x78, 0x49, 0x07, 0x04); /* e(Hi) */
- iic(2, 0x78, 0x48, 0x01, 0x00); /* on=1 off=0 */
-
- printk(KERN_CONT ".");
- iic(2, 0x78, 0x11, 0x00, 0x00); /* end */
- printk(KERN_CONT " done\n");
- return 0;
-}
-
-
-/****************************************************************************
- *
- * Video4Linux Module functions
- *
- ****************************************************************************/
-
-static const struct v4l2_file_operations ar_fops = {
- .owner = THIS_MODULE,
- .open = v4l2_fh_open,
- .release = v4l2_fh_release,
- .read = ar_read,
- .unlocked_ioctl = video_ioctl2,
-};
-
-static const struct v4l2_ioctl_ops ar_ioctl_ops = {
- .vidioc_querycap = ar_querycap,
- .vidioc_g_input = ar_g_input,
- .vidioc_s_input = ar_s_input,
- .vidioc_enum_input = ar_enum_input,
- .vidioc_enum_fmt_vid_cap = ar_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = ar_g_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = ar_s_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = ar_try_fmt_vid_cap,
-};
-
-#define ALIGN4(x) ((((int)(x)) & 0x3) == 0)
-
-static int __init ar_init(void)
-{
- struct ar *ar;
- struct v4l2_device *v4l2_dev;
- int ret;
- int i;
-
- ar = &ardev;
- v4l2_dev = &ar->v4l2_dev;
- strlcpy(v4l2_dev->name, "arv", sizeof(v4l2_dev->name));
- v4l2_info(v4l2_dev, "Colour AR VGA driver %s\n", VERSION);
-
- ret = v4l2_device_register(NULL, v4l2_dev);
- if (ret < 0) {
- v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
- return ret;
- }
- ret = -EIO;
-
-#if USE_INT
- /* allocate a DMA buffer for 1 line. */
- ar->line_buff = kmalloc(MAX_AR_LINE_BYTES, GFP_KERNEL | GFP_DMA);
- if (ar->line_buff == NULL || !ALIGN4(ar->line_buff)) {
- v4l2_err(v4l2_dev, "buffer allocation failed for DMA.\n");
- ret = -ENOMEM;
- goto out_end;
- }
-#endif
- /* allocate buffers for a frame */
- for (i = 0; i < MAX_AR_HEIGHT; i++) {
- ar->frame[i] = kmalloc(MAX_AR_LINE_BYTES, GFP_KERNEL);
- if (ar->frame[i] == NULL || !ALIGN4(ar->frame[i])) {
- v4l2_err(v4l2_dev, "buffer allocation failed for frame.\n");
- ret = -ENOMEM;
- goto out_line_buff;
- }
- }
-
- strlcpy(ar->vdev.name, "Colour AR VGA", sizeof(ar->vdev.name));
- ar->vdev.v4l2_dev = v4l2_dev;
- ar->vdev.fops = &ar_fops;
- ar->vdev.ioctl_ops = &ar_ioctl_ops;
- ar->vdev.release = video_device_release_empty;
- video_set_drvdata(&ar->vdev, ar);
-
- if (vga) {
- ar->width = AR_WIDTH_VGA;
- ar->height = AR_HEIGHT_VGA;
- ar->size = AR_SIZE_VGA;
- ar->frame_bytes = AR_FRAME_BYTES_VGA;
- ar->line_bytes = AR_LINE_BYTES_VGA;
- if (vga_interlace)
- ar->mode = AR_MODE_INTERLACE;
- else
- ar->mode = AR_MODE_NORMAL;
- } else {
- ar->width = AR_WIDTH_QVGA;
- ar->height = AR_HEIGHT_QVGA;
- ar->size = AR_SIZE_QVGA;
- ar->frame_bytes = AR_FRAME_BYTES_QVGA;
- ar->line_bytes = AR_LINE_BYTES_QVGA;
- ar->mode = AR_MODE_INTERLACE;
- }
- mutex_init(&ar->lock);
- init_waitqueue_head(&ar->wait);
-
-#if USE_INT
- if (request_irq(M32R_IRQ_INT3, ar_interrupt, 0, "arv", ar)) {
- v4l2_err("request_irq(%d) failed.\n", M32R_IRQ_INT3);
- ret = -EIO;
- goto out_irq;
- }
-#endif
-
- if (ar_initialize(ar) != 0) {
- v4l2_err(v4l2_dev, "M64278 not found.\n");
- ret = -ENODEV;
- goto out_dev;
- }
-
- /*
- * ok, we can initialize h/w according to parameters,
- * so register video device as a frame grabber type.
- * device is named "video[0-64]".
- * video_register_device() initializes h/w using ar_initialize().
- */
- if (video_register_device(&ar->vdev, VFL_TYPE_GRABBER, video_nr) != 0) {
- /* return -1, -ENFILE(full) or others */
- v4l2_err(v4l2_dev, "register video (Colour AR) failed.\n");
- ret = -ENODEV;
- goto out_dev;
- }
-
- v4l2_info(v4l2_dev, "%s: Found M64278 VGA (IRQ %d, Freq %dMHz).\n",
- video_device_node_name(&ar->vdev), M32R_IRQ_INT3, freq);
-
- return 0;
-
-out_dev:
-#if USE_INT
- free_irq(M32R_IRQ_INT3, ar);
-
-out_irq:
-#endif
- for (i = 0; i < MAX_AR_HEIGHT; i++)
- kfree(ar->frame[i]);
-
-out_line_buff:
-#if USE_INT
- kfree(ar->line_buff);
-
-out_end:
-#endif
- v4l2_device_unregister(&ar->v4l2_dev);
- return ret;
-}
-
-
-static int __init ar_init_module(void)
-{
- freq = (boot_cpu_data.bus_clock / 1000000);
- printk(KERN_INFO "arv: Bus clock %d\n", freq);
- if (freq != 50 && freq != 75)
- freq = DEFAULT_FREQ;
- return ar_init();
-}
-
-static void __exit ar_cleanup_module(void)
-{
- struct ar *ar;
- int i;
-
- ar = &ardev;
- video_unregister_device(&ar->vdev);
-#if USE_INT
- free_irq(M32R_IRQ_INT3, ar);
-#endif
- for (i = 0; i < MAX_AR_HEIGHT; i++)
- kfree(ar->frame[i]);
-#if USE_INT
- kfree(ar->line_buff);
-#endif
- v4l2_device_unregister(&ar->v4l2_dev);
-}
-
-module_init(ar_init_module);
-module_exit(ar_cleanup_module);
-
-MODULE_AUTHOR("Takeo Takahashi <takahashi.takeo@renesas.com>");
-MODULE_DESCRIPTION("Colour AR M64278(VGA) for Video4Linux");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(VERSION);
diff --git a/drivers/media/platform/atmel/atmel-isc.c b/drivers/media/platform/atmel/atmel-isc.c
index 34676409ca08..d89e14524d42 100644
--- a/drivers/media/platform/atmel/atmel-isc.c
+++ b/drivers/media/platform/atmel/atmel-isc.c
@@ -335,7 +335,7 @@ static struct isc_format formats_list[] = {
},
};
-struct fmt_config fmt_configs_list[] = {
+static struct fmt_config fmt_configs_list[] = {
{
.fourcc = V4L2_PIX_FMT_SBGGR8,
.pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
@@ -1417,20 +1417,14 @@ static int isc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
{
struct isc_device *isc = video_drvdata(file);
- if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- return v4l2_subdev_call(isc->current_subdev->sd, video, g_parm, a);
+ return v4l2_g_parm_cap(video_devdata(file), isc->current_subdev->sd, a);
}
static int isc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
{
struct isc_device *isc = video_drvdata(file);
- if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- return v4l2_subdev_call(isc->current_subdev->sd, video, s_parm, a);
+ return v4l2_s_parm_cap(video_devdata(file), isc->current_subdev->sd, a);
}
static int isc_enum_framesizes(struct file *file, void *fh,
diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c
index 9958918e2449..e5be21a31640 100644
--- a/drivers/media/platform/atmel/atmel-isi.c
+++ b/drivers/media/platform/atmel/atmel-isi.c
@@ -689,22 +689,14 @@ static int isi_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
{
struct atmel_isi *isi = video_drvdata(file);
- if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- a->parm.capture.readbuffers = 2;
- return v4l2_subdev_call(isi->entity.subdev, video, g_parm, a);
+ return v4l2_g_parm_cap(video_devdata(file), isi->entity.subdev, a);
}
static int isi_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
{
struct atmel_isi *isi = video_drvdata(file);
- if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- a->parm.capture.readbuffers = 2;
- return v4l2_subdev_call(isi->entity.subdev, video, s_parm, a);
+ return v4l2_s_parm_cap(video_devdata(file), isi->entity.subdev, a);
}
static int isi_enum_framesizes(struct file *file, void *fh,
diff --git a/drivers/media/platform/blackfin/Kconfig b/drivers/media/platform/blackfin/Kconfig
deleted file mode 100644
index 68fa90151b8f..000000000000
--- a/drivers/media/platform/blackfin/Kconfig
+++ /dev/null
@@ -1,16 +0,0 @@
-config VIDEO_BLACKFIN_CAPTURE
- tristate "Blackfin Video Capture Driver"
- depends on VIDEO_V4L2 && BLACKFIN && I2C
- depends on HAS_DMA
- select VIDEOBUF2_DMA_CONTIG
- help
- V4L2 bridge driver for Blackfin video capture device.
- Choose PPI or EPPI as its interface.
-
- To compile this driver as a module, choose M here: the
- module will be called bfin_capture.
-
-config VIDEO_BLACKFIN_PPI
- tristate
- depends on VIDEO_BLACKFIN_CAPTURE
- default VIDEO_BLACKFIN_CAPTURE
diff --git a/drivers/media/platform/blackfin/Makefile b/drivers/media/platform/blackfin/Makefile
deleted file mode 100644
index 30421bc23080..000000000000
--- a/drivers/media/platform/blackfin/Makefile
+++ /dev/null
@@ -1,2 +0,0 @@
-obj-$(CONFIG_VIDEO_BLACKFIN_CAPTURE) += bfin_capture.o
-obj-$(CONFIG_VIDEO_BLACKFIN_PPI) += ppi.o
diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c
deleted file mode 100644
index 41f179117fb0..000000000000
--- a/drivers/media/platform/blackfin/bfin_capture.c
+++ /dev/null
@@ -1,989 +0,0 @@
-/*
- * Analog Devices video capture driver
- *
- * Copyright (c) 2011 Analog Devices Inc.
- *
- * 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.
- *
- * 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.
- */
-
-#include <linux/completion.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/i2c.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/mm.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/time.h>
-#include <linux/types.h>
-
-#include <media/v4l2-common.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <media/videobuf2-dma-contig.h>
-
-#include <asm/dma.h>
-
-#include <media/blackfin/bfin_capture.h>
-#include <media/blackfin/ppi.h>
-
-#define CAPTURE_DRV_NAME "bfin_capture"
-
-struct bcap_format {
- char *desc;
- u32 pixelformat;
- u32 mbus_code;
- int bpp; /* bits per pixel */
- int dlen; /* data length for ppi in bits */
-};
-
-struct bcap_buffer {
- struct vb2_v4l2_buffer vb;
- struct list_head list;
-};
-
-struct bcap_device {
- /* capture device instance */
- struct v4l2_device v4l2_dev;
- /* v4l2 control handler */
- struct v4l2_ctrl_handler ctrl_handler;
- /* device node data */
- struct video_device video_dev;
- /* sub device instance */
- struct v4l2_subdev *sd;
- /* capture config */
- struct bfin_capture_config *cfg;
- /* ppi interface */
- struct ppi_if *ppi;
- /* current input */
- unsigned int cur_input;
- /* current selected standard */
- v4l2_std_id std;
- /* current selected dv_timings */
- struct v4l2_dv_timings dv_timings;
- /* used to store pixel format */
- struct v4l2_pix_format fmt;
- /* bits per pixel*/
- int bpp;
- /* data length for ppi in bits */
- int dlen;
- /* used to store sensor supported format */
- struct bcap_format *sensor_formats;
- /* number of sensor formats array */
- int num_sensor_formats;
- /* pointing to current video buffer */
- struct bcap_buffer *cur_frm;
- /* buffer queue used in videobuf2 */
- struct vb2_queue buffer_queue;
- /* queue of filled frames */
- struct list_head dma_queue;
- /* used in videobuf2 callback */
- spinlock_t lock;
- /* used to access capture device */
- struct mutex mutex;
- /* used to wait ppi to complete one transfer */
- struct completion comp;
- /* prepare to stop */
- bool stop;
- /* vb2 buffer sequence counter */
- unsigned sequence;
-};
-
-static const struct bcap_format bcap_formats[] = {
- {
- .desc = "YCbCr 4:2:2 Interleaved UYVY",
- .pixelformat = V4L2_PIX_FMT_UYVY,
- .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
- .bpp = 16,
- .dlen = 8,
- },
- {
- .desc = "YCbCr 4:2:2 Interleaved YUYV",
- .pixelformat = V4L2_PIX_FMT_YUYV,
- .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
- .bpp = 16,
- .dlen = 8,
- },
- {
- .desc = "YCbCr 4:2:2 Interleaved UYVY",
- .pixelformat = V4L2_PIX_FMT_UYVY,
- .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16,
- .bpp = 16,
- .dlen = 16,
- },
- {
- .desc = "RGB 565",
- .pixelformat = V4L2_PIX_FMT_RGB565,
- .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE,
- .bpp = 16,
- .dlen = 8,
- },
- {
- .desc = "RGB 444",
- .pixelformat = V4L2_PIX_FMT_RGB444,
- .mbus_code = MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE,
- .bpp = 16,
- .dlen = 8,
- },
-
-};
-#define BCAP_MAX_FMTS ARRAY_SIZE(bcap_formats)
-
-static irqreturn_t bcap_isr(int irq, void *dev_id);
-
-static struct bcap_buffer *to_bcap_vb(struct vb2_v4l2_buffer *vb)
-{
- return container_of(vb, struct bcap_buffer, vb);
-}
-
-static int bcap_init_sensor_formats(struct bcap_device *bcap_dev)
-{
- struct v4l2_subdev_mbus_code_enum code = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- };
- struct bcap_format *sf;
- unsigned int num_formats = 0;
- int i, j;
-
- while (!v4l2_subdev_call(bcap_dev->sd, pad,
- enum_mbus_code, NULL, &code)) {
- num_formats++;
- code.index++;
- }
- if (!num_formats)
- return -ENXIO;
-
- sf = kcalloc(num_formats, sizeof(*sf), GFP_KERNEL);
- if (!sf)
- return -ENOMEM;
-
- for (i = 0; i < num_formats; i++) {
- code.index = i;
- v4l2_subdev_call(bcap_dev->sd, pad,
- enum_mbus_code, NULL, &code);
- for (j = 0; j < BCAP_MAX_FMTS; j++)
- if (code.code == bcap_formats[j].mbus_code)
- break;
- if (j == BCAP_MAX_FMTS) {
- /* we don't allow this sensor working with our bridge */
- kfree(sf);
- return -EINVAL;
- }
- sf[i] = bcap_formats[j];
- }
- bcap_dev->sensor_formats = sf;
- bcap_dev->num_sensor_formats = num_formats;
- return 0;
-}
-
-static void bcap_free_sensor_formats(struct bcap_device *bcap_dev)
-{
- bcap_dev->num_sensor_formats = 0;
- kfree(bcap_dev->sensor_formats);
- bcap_dev->sensor_formats = NULL;
-}
-
-static int bcap_queue_setup(struct vb2_queue *vq,
- unsigned int *nbuffers, unsigned int *nplanes,
- unsigned int sizes[], struct device *alloc_devs[])
-{
- struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
-
- if (vq->num_buffers + *nbuffers < 2)
- *nbuffers = 2;
-
- if (*nplanes)
- return sizes[0] < bcap_dev->fmt.sizeimage ? -EINVAL : 0;
-
- *nplanes = 1;
- sizes[0] = bcap_dev->fmt.sizeimage;
-
- return 0;
-}
-
-static int bcap_buffer_prepare(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue);
- unsigned long size = bcap_dev->fmt.sizeimage;
-
- if (vb2_plane_size(vb, 0) < size) {
- v4l2_err(&bcap_dev->v4l2_dev, "buffer too small (%lu < %lu)\n",
- vb2_plane_size(vb, 0), size);
- return -EINVAL;
- }
- vb2_set_plane_payload(vb, 0, size);
-
- vbuf->field = bcap_dev->fmt.field;
-
- return 0;
-}
-
-static void bcap_buffer_queue(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue);
- struct bcap_buffer *buf = to_bcap_vb(vbuf);
- unsigned long flags;
-
- spin_lock_irqsave(&bcap_dev->lock, flags);
- list_add_tail(&buf->list, &bcap_dev->dma_queue);
- spin_unlock_irqrestore(&bcap_dev->lock, flags);
-}
-
-static void bcap_buffer_cleanup(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue);
- struct bcap_buffer *buf = to_bcap_vb(vbuf);
- unsigned long flags;
-
- spin_lock_irqsave(&bcap_dev->lock, flags);
- list_del_init(&buf->list);
- spin_unlock_irqrestore(&bcap_dev->lock, flags);
-}
-
-static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count)
-{
- struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
- struct ppi_if *ppi = bcap_dev->ppi;
- struct bcap_buffer *buf, *tmp;
- struct ppi_params params;
- dma_addr_t addr;
- int ret;
-
- /* enable streamon on the sub device */
- ret = v4l2_subdev_call(bcap_dev->sd, video, s_stream, 1);
- if (ret && (ret != -ENOIOCTLCMD)) {
- v4l2_err(&bcap_dev->v4l2_dev, "stream on failed in subdev\n");
- goto err;
- }
-
- /* set ppi params */
- params.width = bcap_dev->fmt.width;
- params.height = bcap_dev->fmt.height;
- params.bpp = bcap_dev->bpp;
- params.dlen = bcap_dev->dlen;
- params.ppi_control = bcap_dev->cfg->ppi_control;
- params.int_mask = bcap_dev->cfg->int_mask;
- if (bcap_dev->cfg->inputs[bcap_dev->cur_input].capabilities
- & V4L2_IN_CAP_DV_TIMINGS) {
- struct v4l2_bt_timings *bt = &bcap_dev->dv_timings.bt;
-
- params.hdelay = bt->hsync + bt->hbackporch;
- params.vdelay = bt->vsync + bt->vbackporch;
- params.line = V4L2_DV_BT_FRAME_WIDTH(bt);
- params.frame = V4L2_DV_BT_FRAME_HEIGHT(bt);
- } else if (bcap_dev->cfg->inputs[bcap_dev->cur_input].capabilities
- & V4L2_IN_CAP_STD) {
- params.hdelay = 0;
- params.vdelay = 0;
- if (bcap_dev->std & V4L2_STD_525_60) {
- params.line = 858;
- params.frame = 525;
- } else {
- params.line = 864;
- params.frame = 625;
- }
- } else {
- params.hdelay = 0;
- params.vdelay = 0;
- params.line = params.width + bcap_dev->cfg->blank_pixels;
- params.frame = params.height;
- }
- ret = ppi->ops->set_params(ppi, &params);
- if (ret < 0) {
- v4l2_err(&bcap_dev->v4l2_dev,
- "Error in setting ppi params\n");
- goto err;
- }
-
- /* attach ppi DMA irq handler */
- ret = ppi->ops->attach_irq(ppi, bcap_isr);
- if (ret < 0) {
- v4l2_err(&bcap_dev->v4l2_dev,
- "Error in attaching interrupt handler\n");
- goto err;
- }
-
- bcap_dev->sequence = 0;
-
- reinit_completion(&bcap_dev->comp);
- bcap_dev->stop = false;
-
- /* get the next frame from the dma queue */
- bcap_dev->cur_frm = list_entry(bcap_dev->dma_queue.next,
- struct bcap_buffer, list);
- /* remove buffer from the dma queue */
- list_del_init(&bcap_dev->cur_frm->list);
- addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->cur_frm->vb.vb2_buf,
- 0);
- /* update DMA address */
- ppi->ops->update_addr(ppi, (unsigned long)addr);
- /* enable ppi */
- ppi->ops->start(ppi);
-
- return 0;
-
-err:
- list_for_each_entry_safe(buf, tmp, &bcap_dev->dma_queue, list) {
- list_del(&buf->list);
- vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
- }
-
- return ret;
-}
-
-static void bcap_stop_streaming(struct vb2_queue *vq)
-{
- struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
- struct ppi_if *ppi = bcap_dev->ppi;
- int ret;
-
- bcap_dev->stop = true;
- wait_for_completion(&bcap_dev->comp);
- ppi->ops->stop(ppi);
- ppi->ops->detach_irq(ppi);
- ret = v4l2_subdev_call(bcap_dev->sd, video, s_stream, 0);
- if (ret && (ret != -ENOIOCTLCMD))
- v4l2_err(&bcap_dev->v4l2_dev,
- "stream off failed in subdev\n");
-
- /* release all active buffers */
- if (bcap_dev->cur_frm)
- vb2_buffer_done(&bcap_dev->cur_frm->vb.vb2_buf,
- VB2_BUF_STATE_ERROR);
-
- while (!list_empty(&bcap_dev->dma_queue)) {
- bcap_dev->cur_frm = list_entry(bcap_dev->dma_queue.next,
- struct bcap_buffer, list);
- list_del_init(&bcap_dev->cur_frm->list);
- vb2_buffer_done(&bcap_dev->cur_frm->vb.vb2_buf,
- VB2_BUF_STATE_ERROR);
- }
-}
-
-static const struct vb2_ops bcap_video_qops = {
- .queue_setup = bcap_queue_setup,
- .buf_prepare = bcap_buffer_prepare,
- .buf_cleanup = bcap_buffer_cleanup,
- .buf_queue = bcap_buffer_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
- .start_streaming = bcap_start_streaming,
- .stop_streaming = bcap_stop_streaming,
-};
-
-static irqreturn_t bcap_isr(int irq, void *dev_id)
-{
- struct ppi_if *ppi = dev_id;
- struct bcap_device *bcap_dev = ppi->priv;
- struct vb2_v4l2_buffer *vbuf = &bcap_dev->cur_frm->vb;
- struct vb2_buffer *vb = &vbuf->vb2_buf;
- dma_addr_t addr;
-
- spin_lock(&bcap_dev->lock);
-
- if (!list_empty(&bcap_dev->dma_queue)) {
- vb->timestamp = ktime_get_ns();
- if (ppi->err) {
- vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
- ppi->err = false;
- } else {
- vbuf->sequence = bcap_dev->sequence++;
- vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
- }
- bcap_dev->cur_frm = list_entry(bcap_dev->dma_queue.next,
- struct bcap_buffer, list);
- list_del_init(&bcap_dev->cur_frm->list);
- } else {
- /* clear error flag, we will get a new frame */
- if (ppi->err)
- ppi->err = false;
- }
-
- ppi->ops->stop(ppi);
-
- if (bcap_dev->stop) {
- complete(&bcap_dev->comp);
- } else {
- addr = vb2_dma_contig_plane_dma_addr(
- &bcap_dev->cur_frm->vb.vb2_buf, 0);
- ppi->ops->update_addr(ppi, (unsigned long)addr);
- ppi->ops->start(ppi);
- }
-
- spin_unlock(&bcap_dev->lock);
-
- return IRQ_HANDLED;
-}
-
-static int bcap_querystd(struct file *file, void *priv, v4l2_std_id *std)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
- struct v4l2_input input;
-
- input = bcap_dev->cfg->inputs[bcap_dev->cur_input];
- if (!(input.capabilities & V4L2_IN_CAP_STD))
- return -ENODATA;
-
- return v4l2_subdev_call(bcap_dev->sd, video, querystd, std);
-}
-
-static int bcap_g_std(struct file *file, void *priv, v4l2_std_id *std)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
- struct v4l2_input input;
-
- input = bcap_dev->cfg->inputs[bcap_dev->cur_input];
- if (!(input.capabilities & V4L2_IN_CAP_STD))
- return -ENODATA;
-
- *std = bcap_dev->std;
- return 0;
-}
-
-static int bcap_s_std(struct file *file, void *priv, v4l2_std_id std)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
- struct v4l2_input input;
- int ret;
-
- input = bcap_dev->cfg->inputs[bcap_dev->cur_input];
- if (!(input.capabilities & V4L2_IN_CAP_STD))
- return -ENODATA;
-
- if (vb2_is_busy(&bcap_dev->buffer_queue))
- return -EBUSY;
-
- ret = v4l2_subdev_call(bcap_dev->sd, video, s_std, std);
- if (ret < 0)
- return ret;
-
- bcap_dev->std = std;
- return 0;
-}
-
-static int bcap_enum_dv_timings(struct file *file, void *priv,
- struct v4l2_enum_dv_timings *timings)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
- struct v4l2_input input;
-
- input = bcap_dev->cfg->inputs[bcap_dev->cur_input];
- if (!(input.capabilities & V4L2_IN_CAP_DV_TIMINGS))
- return -ENODATA;
-
- timings->pad = 0;
-
- return v4l2_subdev_call(bcap_dev->sd, pad,
- enum_dv_timings, timings);
-}
-
-static int bcap_query_dv_timings(struct file *file, void *priv,
- struct v4l2_dv_timings *timings)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
- struct v4l2_input input;
-
- input = bcap_dev->cfg->inputs[bcap_dev->cur_input];
- if (!(input.capabilities & V4L2_IN_CAP_DV_TIMINGS))
- return -ENODATA;
-
- return v4l2_subdev_call(bcap_dev->sd, video,
- query_dv_timings, timings);
-}
-
-static int bcap_g_dv_timings(struct file *file, void *priv,
- struct v4l2_dv_timings *timings)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
- struct v4l2_input input;
-
- input = bcap_dev->cfg->inputs[bcap_dev->cur_input];
- if (!(input.capabilities & V4L2_IN_CAP_DV_TIMINGS))
- return -ENODATA;
-
- *timings = bcap_dev->dv_timings;
- return 0;
-}
-
-static int bcap_s_dv_timings(struct file *file, void *priv,
- struct v4l2_dv_timings *timings)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
- struct v4l2_input input;
- int ret;
-
- input = bcap_dev->cfg->inputs[bcap_dev->cur_input];
- if (!(input.capabilities & V4L2_IN_CAP_DV_TIMINGS))
- return -ENODATA;
-
- if (vb2_is_busy(&bcap_dev->buffer_queue))
- return -EBUSY;
-
- ret = v4l2_subdev_call(bcap_dev->sd, video, s_dv_timings, timings);
- if (ret < 0)
- return ret;
-
- bcap_dev->dv_timings = *timings;
- return 0;
-}
-
-static int bcap_enum_input(struct file *file, void *priv,
- struct v4l2_input *input)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
- struct bfin_capture_config *config = bcap_dev->cfg;
- int ret;
- u32 status;
-
- if (input->index >= config->num_inputs)
- return -EINVAL;
-
- *input = config->inputs[input->index];
- /* get input status */
- ret = v4l2_subdev_call(bcap_dev->sd, video, g_input_status, &status);
- if (!ret)
- input->status = status;
- return 0;
-}
-
-static int bcap_g_input(struct file *file, void *priv, unsigned int *index)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
-
- *index = bcap_dev->cur_input;
- return 0;
-}
-
-static int bcap_s_input(struct file *file, void *priv, unsigned int index)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
- struct bfin_capture_config *config = bcap_dev->cfg;
- struct bcap_route *route;
- int ret;
-
- if (vb2_is_busy(&bcap_dev->buffer_queue))
- return -EBUSY;
-
- if (index >= config->num_inputs)
- return -EINVAL;
-
- route = &config->routes[index];
- ret = v4l2_subdev_call(bcap_dev->sd, video, s_routing,
- route->input, route->output, 0);
- if ((ret < 0) && (ret != -ENOIOCTLCMD)) {
- v4l2_err(&bcap_dev->v4l2_dev, "Failed to set input\n");
- return ret;
- }
- bcap_dev->cur_input = index;
- /* if this route has specific config, update ppi control */
- if (route->ppi_control)
- config->ppi_control = route->ppi_control;
- return 0;
-}
-
-static int bcap_try_format(struct bcap_device *bcap,
- struct v4l2_pix_format *pixfmt,
- struct bcap_format *bcap_fmt)
-{
- struct bcap_format *sf = bcap->sensor_formats;
- struct bcap_format *fmt = NULL;
- struct v4l2_subdev_pad_config pad_cfg;
- struct v4l2_subdev_format format = {
- .which = V4L2_SUBDEV_FORMAT_TRY,
- };
- int ret, i;
-
- for (i = 0; i < bcap->num_sensor_formats; i++) {
- fmt = &sf[i];
- if (pixfmt->pixelformat == fmt->pixelformat)
- break;
- }
- if (i == bcap->num_sensor_formats)
- fmt = &sf[0];
-
- v4l2_fill_mbus_format(&format.format, pixfmt, fmt->mbus_code);
- ret = v4l2_subdev_call(bcap->sd, pad, set_fmt, &pad_cfg,
- &format);
- if (ret < 0)
- return ret;
- v4l2_fill_pix_format(pixfmt, &format.format);
- if (bcap_fmt) {
- for (i = 0; i < bcap->num_sensor_formats; i++) {
- fmt = &sf[i];
- if (format.format.code == fmt->mbus_code)
- break;
- }
- *bcap_fmt = *fmt;
- }
- pixfmt->bytesperline = pixfmt->width * fmt->bpp / 8;
- pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
- return 0;
-}
-
-static int bcap_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *fmt)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
- struct bcap_format *sf = bcap_dev->sensor_formats;
-
- if (fmt->index >= bcap_dev->num_sensor_formats)
- return -EINVAL;
-
- fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- strlcpy(fmt->description,
- sf[fmt->index].desc,
- sizeof(fmt->description));
- fmt->pixelformat = sf[fmt->index].pixelformat;
- return 0;
-}
-
-static int bcap_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *fmt)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
- struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
-
- return bcap_try_format(bcap_dev, pixfmt, NULL);
-}
-
-static int bcap_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *fmt)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
-
- fmt->fmt.pix = bcap_dev->fmt;
- return 0;
-}
-
-static int bcap_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *fmt)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
- struct v4l2_subdev_format format = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- };
- struct bcap_format bcap_fmt;
- struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
- int ret;
-
- if (vb2_is_busy(&bcap_dev->buffer_queue))
- return -EBUSY;
-
- /* see if format works */
- ret = bcap_try_format(bcap_dev, pixfmt, &bcap_fmt);
- if (ret < 0)
- return ret;
-
- v4l2_fill_mbus_format(&format.format, pixfmt, bcap_fmt.mbus_code);
- ret = v4l2_subdev_call(bcap_dev->sd, pad, set_fmt, NULL, &format);
- if (ret < 0)
- return ret;
- bcap_dev->fmt = *pixfmt;
- bcap_dev->bpp = bcap_fmt.bpp;
- bcap_dev->dlen = bcap_fmt.dlen;
- return 0;
-}
-
-static int bcap_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
-
- cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
- cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
- strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver));
- strlcpy(cap->bus_info, "Blackfin Platform", sizeof(cap->bus_info));
- strlcpy(cap->card, bcap_dev->cfg->card_name, sizeof(cap->card));
- return 0;
-}
-
-static int bcap_g_parm(struct file *file, void *fh,
- struct v4l2_streamparm *a)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
-
- if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- return v4l2_subdev_call(bcap_dev->sd, video, g_parm, a);
-}
-
-static int bcap_s_parm(struct file *file, void *fh,
- struct v4l2_streamparm *a)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
-
- if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- return v4l2_subdev_call(bcap_dev->sd, video, s_parm, a);
-}
-
-static int bcap_log_status(struct file *file, void *priv)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
- /* status for sub devices */
- v4l2_device_call_all(&bcap_dev->v4l2_dev, 0, core, log_status);
- return 0;
-}
-
-static const struct v4l2_ioctl_ops bcap_ioctl_ops = {
- .vidioc_querycap = bcap_querycap,
- .vidioc_g_fmt_vid_cap = bcap_g_fmt_vid_cap,
- .vidioc_enum_fmt_vid_cap = bcap_enum_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = bcap_s_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = bcap_try_fmt_vid_cap,
- .vidioc_enum_input = bcap_enum_input,
- .vidioc_g_input = bcap_g_input,
- .vidioc_s_input = bcap_s_input,
- .vidioc_querystd = bcap_querystd,
- .vidioc_s_std = bcap_s_std,
- .vidioc_g_std = bcap_g_std,
- .vidioc_s_dv_timings = bcap_s_dv_timings,
- .vidioc_g_dv_timings = bcap_g_dv_timings,
- .vidioc_query_dv_timings = bcap_query_dv_timings,
- .vidioc_enum_dv_timings = bcap_enum_dv_timings,
- .vidioc_reqbufs = vb2_ioctl_reqbufs,
- .vidioc_create_bufs = vb2_ioctl_create_bufs,
- .vidioc_querybuf = vb2_ioctl_querybuf,
- .vidioc_qbuf = vb2_ioctl_qbuf,
- .vidioc_dqbuf = vb2_ioctl_dqbuf,
- .vidioc_expbuf = vb2_ioctl_expbuf,
- .vidioc_streamon = vb2_ioctl_streamon,
- .vidioc_streamoff = vb2_ioctl_streamoff,
- .vidioc_g_parm = bcap_g_parm,
- .vidioc_s_parm = bcap_s_parm,
- .vidioc_log_status = bcap_log_status,
-};
-
-static const struct v4l2_file_operations bcap_fops = {
- .owner = THIS_MODULE,
- .open = v4l2_fh_open,
- .release = vb2_fop_release,
- .unlocked_ioctl = video_ioctl2,
- .mmap = vb2_fop_mmap,
-#ifndef CONFIG_MMU
- .get_unmapped_area = vb2_fop_get_unmapped_area,
-#endif
- .poll = vb2_fop_poll
-};
-
-static int bcap_probe(struct platform_device *pdev)
-{
- struct bcap_device *bcap_dev;
- struct video_device *vfd;
- struct i2c_adapter *i2c_adap;
- struct bfin_capture_config *config;
- struct vb2_queue *q;
- struct bcap_route *route;
- int ret;
-
- config = pdev->dev.platform_data;
- if (!config || !config->num_inputs) {
- v4l2_err(pdev->dev.driver, "Unable to get board config\n");
- return -ENODEV;
- }
-
- bcap_dev = kzalloc(sizeof(*bcap_dev), GFP_KERNEL);
- if (!bcap_dev)
- return -ENOMEM;
-
- bcap_dev->cfg = config;
-
- bcap_dev->ppi = ppi_create_instance(pdev, config->ppi_info);
- if (!bcap_dev->ppi) {
- v4l2_err(pdev->dev.driver, "Unable to create ppi\n");
- ret = -ENODEV;
- goto err_free_dev;
- }
- bcap_dev->ppi->priv = bcap_dev;
-
- vfd = &bcap_dev->video_dev;
- /* initialize field of video device */
- vfd->release = video_device_release_empty;
- vfd->fops = &bcap_fops;
- vfd->ioctl_ops = &bcap_ioctl_ops;
- vfd->tvnorms = 0;
- vfd->v4l2_dev = &bcap_dev->v4l2_dev;
- strncpy(vfd->name, CAPTURE_DRV_NAME, sizeof(vfd->name));
-
- ret = v4l2_device_register(&pdev->dev, &bcap_dev->v4l2_dev);
- if (ret) {
- v4l2_err(pdev->dev.driver,
- "Unable to register v4l2 device\n");
- goto err_free_ppi;
- }
- v4l2_info(&bcap_dev->v4l2_dev, "v4l2 device registered\n");
-
- bcap_dev->v4l2_dev.ctrl_handler = &bcap_dev->ctrl_handler;
- ret = v4l2_ctrl_handler_init(&bcap_dev->ctrl_handler, 0);
- if (ret) {
- v4l2_err(&bcap_dev->v4l2_dev,
- "Unable to init control handler\n");
- goto err_unreg_v4l2;
- }
-
- spin_lock_init(&bcap_dev->lock);
- /* initialize queue */
- q = &bcap_dev->buffer_queue;
- q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- q->io_modes = VB2_MMAP | VB2_DMABUF;
- q->drv_priv = bcap_dev;
- q->buf_struct_size = sizeof(struct bcap_buffer);
- q->ops = &bcap_video_qops;
- q->mem_ops = &vb2_dma_contig_memops;
- q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- q->lock = &bcap_dev->mutex;
- q->min_buffers_needed = 1;
- q->dev = &pdev->dev;
-
- ret = vb2_queue_init(q);
- if (ret)
- goto err_free_handler;
-
- mutex_init(&bcap_dev->mutex);
- init_completion(&bcap_dev->comp);
-
- /* init video dma queues */
- INIT_LIST_HEAD(&bcap_dev->dma_queue);
-
- vfd->lock = &bcap_dev->mutex;
- vfd->queue = q;
-
- /* register video device */
- ret = video_register_device(&bcap_dev->video_dev, VFL_TYPE_GRABBER, -1);
- if (ret) {
- v4l2_err(&bcap_dev->v4l2_dev,
- "Unable to register video device\n");
- goto err_free_handler;
- }
- video_set_drvdata(&bcap_dev->video_dev, bcap_dev);
- v4l2_info(&bcap_dev->v4l2_dev, "video device registered as: %s\n",
- video_device_node_name(vfd));
-
- /* load up the subdevice */
- i2c_adap = i2c_get_adapter(config->i2c_adapter_id);
- if (!i2c_adap) {
- v4l2_err(&bcap_dev->v4l2_dev,
- "Unable to find i2c adapter\n");
- ret = -ENODEV;
- goto err_unreg_vdev;
-
- }
- bcap_dev->sd = v4l2_i2c_new_subdev_board(&bcap_dev->v4l2_dev,
- i2c_adap,
- &config->board_info,
- NULL);
- if (bcap_dev->sd) {
- int i;
-
- /* update tvnorms from the sub devices */
- for (i = 0; i < config->num_inputs; i++)
- vfd->tvnorms |= config->inputs[i].std;
- } else {
- v4l2_err(&bcap_dev->v4l2_dev,
- "Unable to register sub device\n");
- ret = -ENODEV;
- goto err_unreg_vdev;
- }
-
- v4l2_info(&bcap_dev->v4l2_dev, "v4l2 sub device registered\n");
-
- /*
- * explicitly set input, otherwise some boards
- * may not work at the state as we expected
- */
- route = &config->routes[0];
- ret = v4l2_subdev_call(bcap_dev->sd, video, s_routing,
- route->input, route->output, 0);
- if ((ret < 0) && (ret != -ENOIOCTLCMD)) {
- v4l2_err(&bcap_dev->v4l2_dev, "Failed to set input\n");
- goto err_unreg_vdev;
- }
- bcap_dev->cur_input = 0;
- /* if this route has specific config, update ppi control */
- if (route->ppi_control)
- config->ppi_control = route->ppi_control;
-
- /* now we can probe the default state */
- if (config->inputs[0].capabilities & V4L2_IN_CAP_STD) {
- v4l2_std_id std;
- ret = v4l2_subdev_call(bcap_dev->sd, video, g_std, &std);
- if (ret) {
- v4l2_err(&bcap_dev->v4l2_dev,
- "Unable to get std\n");
- goto err_unreg_vdev;
- }
- bcap_dev->std = std;
- }
- if (config->inputs[0].capabilities & V4L2_IN_CAP_DV_TIMINGS) {
- struct v4l2_dv_timings dv_timings;
- ret = v4l2_subdev_call(bcap_dev->sd, video,
- g_dv_timings, &dv_timings);
- if (ret) {
- v4l2_err(&bcap_dev->v4l2_dev,
- "Unable to get dv timings\n");
- goto err_unreg_vdev;
- }
- bcap_dev->dv_timings = dv_timings;
- }
- ret = bcap_init_sensor_formats(bcap_dev);
- if (ret) {
- v4l2_err(&bcap_dev->v4l2_dev,
- "Unable to create sensor formats table\n");
- goto err_unreg_vdev;
- }
- return 0;
-err_unreg_vdev:
- video_unregister_device(&bcap_dev->video_dev);
-err_free_handler:
- v4l2_ctrl_handler_free(&bcap_dev->ctrl_handler);
-err_unreg_v4l2:
- v4l2_device_unregister(&bcap_dev->v4l2_dev);
-err_free_ppi:
- ppi_delete_instance(bcap_dev->ppi);
-err_free_dev:
- kfree(bcap_dev);
- return ret;
-}
-
-static int bcap_remove(struct platform_device *pdev)
-{
- struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
- struct bcap_device *bcap_dev = container_of(v4l2_dev,
- struct bcap_device, v4l2_dev);
-
- bcap_free_sensor_formats(bcap_dev);
- video_unregister_device(&bcap_dev->video_dev);
- v4l2_ctrl_handler_free(&bcap_dev->ctrl_handler);
- v4l2_device_unregister(v4l2_dev);
- ppi_delete_instance(bcap_dev->ppi);
- kfree(bcap_dev);
- return 0;
-}
-
-static struct platform_driver bcap_driver = {
- .driver = {
- .name = CAPTURE_DRV_NAME,
- },
- .probe = bcap_probe,
- .remove = bcap_remove,
-};
-module_platform_driver(bcap_driver);
-
-MODULE_DESCRIPTION("Analog Devices blackfin video capture driver");
-MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/blackfin/ppi.c b/drivers/media/platform/blackfin/ppi.c
deleted file mode 100644
index d3dc765c1609..000000000000
--- a/drivers/media/platform/blackfin/ppi.c
+++ /dev/null
@@ -1,361 +0,0 @@
-/*
- * ppi.c Analog Devices Parallel Peripheral Interface driver
- *
- * Copyright (c) 2011 Analog Devices Inc.
- *
- * 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.
- *
- * 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.
- */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/platform_device.h>
-
-#include <asm/bfin_ppi.h>
-#include <asm/blackfin.h>
-#include <asm/cacheflush.h>
-#include <asm/dma.h>
-#include <asm/portmux.h>
-
-#include <media/blackfin/ppi.h>
-
-static int ppi_attach_irq(struct ppi_if *ppi, irq_handler_t handler);
-static void ppi_detach_irq(struct ppi_if *ppi);
-static int ppi_start(struct ppi_if *ppi);
-static int ppi_stop(struct ppi_if *ppi);
-static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params);
-static void ppi_update_addr(struct ppi_if *ppi, unsigned long addr);
-
-static const struct ppi_ops ppi_ops = {
- .attach_irq = ppi_attach_irq,
- .detach_irq = ppi_detach_irq,
- .start = ppi_start,
- .stop = ppi_stop,
- .set_params = ppi_set_params,
- .update_addr = ppi_update_addr,
-};
-
-static irqreturn_t ppi_irq_err(int irq, void *dev_id)
-{
- struct ppi_if *ppi = dev_id;
- const struct ppi_info *info = ppi->info;
-
- switch (info->type) {
- case PPI_TYPE_PPI:
- {
- struct bfin_ppi_regs *reg = info->base;
- unsigned short status;
-
- /* register on bf561 is cleared when read
- * others are W1C
- */
- status = bfin_read16(&reg->status);
- if (status & 0x3000)
- ppi->err = true;
- bfin_write16(&reg->status, 0xff00);
- break;
- }
- case PPI_TYPE_EPPI:
- {
- struct bfin_eppi_regs *reg = info->base;
- unsigned short status;
-
- status = bfin_read16(&reg->status);
- if (status & 0x2)
- ppi->err = true;
- bfin_write16(&reg->status, 0xffff);
- break;
- }
- case PPI_TYPE_EPPI3:
- {
- struct bfin_eppi3_regs *reg = info->base;
- unsigned long stat;
-
- stat = bfin_read32(&reg->stat);
- if (stat & 0x2)
- ppi->err = true;
- bfin_write32(&reg->stat, 0xc0ff);
- break;
- }
- default:
- break;
- }
-
- return IRQ_HANDLED;
-}
-
-static int ppi_attach_irq(struct ppi_if *ppi, irq_handler_t handler)
-{
- const struct ppi_info *info = ppi->info;
- int ret;
-
- ret = request_dma(info->dma_ch, "PPI_DMA");
-
- if (ret) {
- pr_err("Unable to allocate DMA channel for PPI\n");
- return ret;
- }
- set_dma_callback(info->dma_ch, handler, ppi);
-
- if (ppi->err_int) {
- ret = request_irq(info->irq_err, ppi_irq_err, 0, "PPI ERROR", ppi);
- if (ret) {
- pr_err("Unable to allocate IRQ for PPI\n");
- free_dma(info->dma_ch);
- }
- }
- return ret;
-}
-
-static void ppi_detach_irq(struct ppi_if *ppi)
-{
- const struct ppi_info *info = ppi->info;
-
- if (ppi->err_int)
- free_irq(info->irq_err, ppi);
- free_dma(info->dma_ch);
-}
-
-static int ppi_start(struct ppi_if *ppi)
-{
- const struct ppi_info *info = ppi->info;
-
- /* enable DMA */
- enable_dma(info->dma_ch);
-
- /* enable PPI */
- ppi->ppi_control |= PORT_EN;
- switch (info->type) {
- case PPI_TYPE_PPI:
- {
- struct bfin_ppi_regs *reg = info->base;
- bfin_write16(&reg->control, ppi->ppi_control);
- break;
- }
- case PPI_TYPE_EPPI:
- {
- struct bfin_eppi_regs *reg = info->base;
- bfin_write32(&reg->control, ppi->ppi_control);
- break;
- }
- case PPI_TYPE_EPPI3:
- {
- struct bfin_eppi3_regs *reg = info->base;
- bfin_write32(&reg->ctl, ppi->ppi_control);
- break;
- }
- default:
- return -EINVAL;
- }
-
- SSYNC();
- return 0;
-}
-
-static int ppi_stop(struct ppi_if *ppi)
-{
- const struct ppi_info *info = ppi->info;
-
- /* disable PPI */
- ppi->ppi_control &= ~PORT_EN;
- switch (info->type) {
- case PPI_TYPE_PPI:
- {
- struct bfin_ppi_regs *reg = info->base;
- bfin_write16(&reg->control, ppi->ppi_control);
- break;
- }
- case PPI_TYPE_EPPI:
- {
- struct bfin_eppi_regs *reg = info->base;
- bfin_write32(&reg->control, ppi->ppi_control);
- break;
- }
- case PPI_TYPE_EPPI3:
- {
- struct bfin_eppi3_regs *reg = info->base;
- bfin_write32(&reg->ctl, ppi->ppi_control);
- break;
- }
- default:
- return -EINVAL;
- }
-
- /* disable DMA */
- clear_dma_irqstat(info->dma_ch);
- disable_dma(info->dma_ch);
-
- SSYNC();
- return 0;
-}
-
-static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params)
-{
- const struct ppi_info *info = ppi->info;
- int dma32 = 0;
- int dma_config, bytes_per_line;
- int hcount, hdelay, samples_per_line;
-
-#ifdef CONFIG_PINCTRL
- static const char * const pin_state[] = {"8bit", "16bit", "24bit"};
- struct pinctrl *pctrl;
- struct pinctrl_state *pstate;
-
- if (params->dlen > 24 || params->dlen <= 0)
- return -EINVAL;
- pctrl = devm_pinctrl_get(ppi->dev);
- if (IS_ERR(pctrl))
- return PTR_ERR(pctrl);
- pstate = pinctrl_lookup_state(pctrl,
- pin_state[(params->dlen + 7) / 8 - 1]);
- if (pinctrl_select_state(pctrl, pstate))
- return -EINVAL;
-#endif
-
- bytes_per_line = params->width * params->bpp / 8;
- /* convert parameters unit from pixels to samples */
- hcount = params->width * params->bpp / params->dlen;
- hdelay = params->hdelay * params->bpp / params->dlen;
- samples_per_line = params->line * params->bpp / params->dlen;
- if (params->int_mask == 0xFFFFFFFF)
- ppi->err_int = false;
- else
- ppi->err_int = true;
-
- dma_config = (DMA_FLOW_STOP | RESTART | DMA2D | DI_EN_Y);
- ppi->ppi_control = params->ppi_control & ~PORT_EN;
- if (!(ppi->ppi_control & PORT_DIR))
- dma_config |= WNR;
- switch (info->type) {
- case PPI_TYPE_PPI:
- {
- struct bfin_ppi_regs *reg = info->base;
-
- if (params->ppi_control & DMA32)
- dma32 = 1;
-
- bfin_write16(&reg->control, ppi->ppi_control);
- bfin_write16(&reg->count, samples_per_line - 1);
- bfin_write16(&reg->frame, params->frame);
- break;
- }
- case PPI_TYPE_EPPI:
- {
- struct bfin_eppi_regs *reg = info->base;
-
- if ((params->ppi_control & PACK_EN)
- || (params->ppi_control & 0x38000) > DLEN_16)
- dma32 = 1;
-
- bfin_write32(&reg->control, ppi->ppi_control);
- bfin_write16(&reg->line, samples_per_line);
- bfin_write16(&reg->frame, params->frame);
- bfin_write16(&reg->hdelay, hdelay);
- bfin_write16(&reg->vdelay, params->vdelay);
- bfin_write16(&reg->hcount, hcount);
- bfin_write16(&reg->vcount, params->height);
- break;
- }
- case PPI_TYPE_EPPI3:
- {
- struct bfin_eppi3_regs *reg = info->base;
-
- if ((params->ppi_control & PACK_EN)
- || (params->ppi_control & 0x70000) > DLEN_16)
- dma32 = 1;
-
- bfin_write32(&reg->ctl, ppi->ppi_control);
- bfin_write32(&reg->line, samples_per_line);
- bfin_write32(&reg->frame, params->frame);
- bfin_write32(&reg->hdly, hdelay);
- bfin_write32(&reg->vdly, params->vdelay);
- bfin_write32(&reg->hcnt, hcount);
- bfin_write32(&reg->vcnt, params->height);
- if (params->int_mask)
- bfin_write32(&reg->imsk, params->int_mask & 0xFF);
- if (ppi->ppi_control & PORT_DIR) {
- u32 hsync_width, vsync_width, vsync_period;
-
- hsync_width = params->hsync
- * params->bpp / params->dlen;
- vsync_width = params->vsync * samples_per_line;
- vsync_period = samples_per_line * params->frame;
- bfin_write32(&reg->fs1_wlhb, hsync_width);
- bfin_write32(&reg->fs1_paspl, samples_per_line);
- bfin_write32(&reg->fs2_wlvb, vsync_width);
- bfin_write32(&reg->fs2_palpf, vsync_period);
- }
- break;
- }
- default:
- return -EINVAL;
- }
-
- if (dma32) {
- dma_config |= WDSIZE_32 | PSIZE_32;
- set_dma_x_count(info->dma_ch, bytes_per_line >> 2);
- set_dma_x_modify(info->dma_ch, 4);
- set_dma_y_modify(info->dma_ch, 4);
- } else {
- dma_config |= WDSIZE_16 | PSIZE_16;
- set_dma_x_count(info->dma_ch, bytes_per_line >> 1);
- set_dma_x_modify(info->dma_ch, 2);
- set_dma_y_modify(info->dma_ch, 2);
- }
- set_dma_y_count(info->dma_ch, params->height);
- set_dma_config(info->dma_ch, dma_config);
-
- SSYNC();
- return 0;
-}
-
-static void ppi_update_addr(struct ppi_if *ppi, unsigned long addr)
-{
- set_dma_start_addr(ppi->info->dma_ch, addr);
-}
-
-struct ppi_if *ppi_create_instance(struct platform_device *pdev,
- const struct ppi_info *info)
-{
- struct ppi_if *ppi;
-
- if (!info || !info->pin_req)
- return NULL;
-
-#ifndef CONFIG_PINCTRL
- if (peripheral_request_list(info->pin_req, KBUILD_MODNAME)) {
- dev_err(&pdev->dev, "request peripheral failed\n");
- return NULL;
- }
-#endif
-
- ppi = kzalloc(sizeof(*ppi), GFP_KERNEL);
- if (!ppi) {
- peripheral_free_list(info->pin_req);
- return NULL;
- }
- ppi->ops = &ppi_ops;
- ppi->info = info;
- ppi->dev = &pdev->dev;
-
- pr_info("ppi probe success\n");
- return ppi;
-}
-EXPORT_SYMBOL(ppi_create_instance);
-
-void ppi_delete_instance(struct ppi_if *ppi)
-{
- peripheral_free_list(ppi->info->pin_req);
- kfree(ppi);
-}
-EXPORT_SYMBOL(ppi_delete_instance);
-
-MODULE_DESCRIPTION("Analog Devices PPI driver");
-MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/cec-gpio/cec-gpio.c b/drivers/media/platform/cec-gpio/cec-gpio.c
index 5debdf08fbe7..f1f28cf5c751 100644
--- a/drivers/media/platform/cec-gpio/cec-gpio.c
+++ b/drivers/media/platform/cec-gpio/cec-gpio.c
@@ -1,18 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/module.h>
diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c
index 9fe113cb901f..68ed2a564ad1 100644
--- a/drivers/media/platform/coda/coda-bit.c
+++ b/drivers/media/platform/coda/coda-bit.c
@@ -68,8 +68,9 @@ static void coda_command_async(struct coda_ctx *ctx, int cmd)
{
struct coda_dev *dev = ctx->dev;
- if (dev->devtype->product == CODA_960 ||
- dev->devtype->product == CODA_7541) {
+ if (dev->devtype->product == CODA_HX4 ||
+ dev->devtype->product == CODA_7541 ||
+ dev->devtype->product == CODA_960) {
/* Restore context related registers to CODA */
coda_write(dev, ctx->bit_stream_param,
CODA_REG_BIT_BIT_STREAM_PARAM);
@@ -506,7 +507,8 @@ static int coda_alloc_context_buffers(struct coda_ctx *ctx,
goto err;
}
- if (!ctx->psbuf.vaddr && dev->devtype->product == CODA_7541) {
+ if (!ctx->psbuf.vaddr && (dev->devtype->product == CODA_HX4 ||
+ dev->devtype->product == CODA_7541)) {
ret = coda_alloc_context_buf(ctx, &ctx->psbuf,
CODA7_PS_BUF_SIZE, "psbuf");
if (ret < 0)
@@ -594,6 +596,7 @@ static void coda_setup_iram(struct coda_ctx *ctx)
int dbk_bits;
int bit_bits;
int ip_bits;
+ int me_bits;
memset(iram_info, 0, sizeof(*iram_info));
iram_info->next_paddr = dev->iram.paddr;
@@ -603,15 +606,23 @@ static void coda_setup_iram(struct coda_ctx *ctx)
return;
switch (dev->devtype->product) {
+ case CODA_HX4:
+ dbk_bits = CODA7_USE_HOST_DBK_ENABLE;
+ bit_bits = CODA7_USE_HOST_BIT_ENABLE;
+ ip_bits = CODA7_USE_HOST_IP_ENABLE;
+ me_bits = CODA7_USE_HOST_ME_ENABLE;
+ break;
case CODA_7541:
dbk_bits = CODA7_USE_HOST_DBK_ENABLE | CODA7_USE_DBK_ENABLE;
bit_bits = CODA7_USE_HOST_BIT_ENABLE | CODA7_USE_BIT_ENABLE;
ip_bits = CODA7_USE_HOST_IP_ENABLE | CODA7_USE_IP_ENABLE;
+ me_bits = CODA7_USE_HOST_ME_ENABLE | CODA7_USE_ME_ENABLE;
break;
case CODA_960:
dbk_bits = CODA9_USE_HOST_DBK_ENABLE | CODA9_USE_DBK_ENABLE;
bit_bits = CODA9_USE_HOST_BIT_ENABLE | CODA7_USE_BIT_ENABLE;
ip_bits = CODA9_USE_HOST_IP_ENABLE | CODA7_USE_IP_ENABLE;
+ me_bits = 0;
break;
default: /* CODA_DX6 */
return;
@@ -626,7 +637,8 @@ static void coda_setup_iram(struct coda_ctx *ctx)
w64 = mb_width * 64;
/* Prioritize in case IRAM is too small for everything */
- if (dev->devtype->product == CODA_7541) {
+ if (dev->devtype->product == CODA_HX4 ||
+ dev->devtype->product == CODA_7541) {
iram_info->search_ram_size = round_up(mb_width * 16 *
36 + 2048, 1024);
iram_info->search_ram_paddr = coda_iram_alloc(iram_info,
@@ -635,8 +647,7 @@ static void coda_setup_iram(struct coda_ctx *ctx)
pr_err("IRAM is smaller than the search ram size\n");
goto out;
}
- iram_info->axi_sram_use |= CODA7_USE_HOST_ME_ENABLE |
- CODA7_USE_ME_ENABLE;
+ iram_info->axi_sram_use |= me_bits;
}
/* Only H.264BP and H.263P3 are considered */
@@ -688,7 +699,8 @@ out:
v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
"IRAM smaller than needed\n");
- if (dev->devtype->product == CODA_7541) {
+ if (dev->devtype->product == CODA_HX4 ||
+ dev->devtype->product == CODA_7541) {
/* TODO - Enabling these causes picture errors on CODA7541 */
if (ctx->inst_type == CODA_INST_DECODER) {
/* fw 1.4.50 */
@@ -706,6 +718,7 @@ out:
static u32 coda_supported_firmwares[] = {
CODA_FIRMWARE_VERNUM(CODA_DX6, 2, 2, 5),
+ CODA_FIRMWARE_VERNUM(CODA_HX4, 1, 4, 50),
CODA_FIRMWARE_VERNUM(CODA_7541, 1, 4, 50),
CODA_FIRMWARE_VERNUM(CODA_960, 2, 1, 5),
CODA_FIRMWARE_VERNUM(CODA_960, 2, 3, 10),
@@ -890,6 +903,7 @@ static int coda_start_encoding(struct coda_ctx *ctx)
case CODA_960:
coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN);
/* fallthrough */
+ case CODA_HX4:
case CODA_7541:
coda_write(dev, CODA7_STREAM_BUF_DYNALLOC_EN |
CODA7_STREAM_BUF_PIC_RESET, CODA_REG_BIT_STREAM_CTRL);
@@ -919,6 +933,7 @@ static int coda_start_encoding(struct coda_ctx *ctx)
value |= (q_data_src->height & CODADX6_PICHEIGHT_MASK)
<< CODA_PICHEIGHT_OFFSET;
break;
+ case CODA_HX4:
case CODA_7541:
if (dst_fourcc == V4L2_PIX_FMT_H264) {
value = (round_up(q_data_src->width, 16) &
@@ -1086,6 +1101,7 @@ static int coda_start_encoding(struct coda_ctx *ctx)
value = FMO_SLICE_SAVE_BUF_SIZE << 7;
coda_write(dev, value, CODADX6_CMD_ENC_SEQ_FMO);
break;
+ case CODA_HX4:
case CODA_7541:
coda_write(dev, ctx->iram_info.search_ram_paddr,
CODA7_CMD_ENC_SEQ_SEARCH_BASE);
@@ -1131,7 +1147,8 @@ static int coda_start_encoding(struct coda_ctx *ctx)
coda_write(dev, num_fb, CODA_CMD_SET_FRAME_BUF_NUM);
coda_write(dev, stride, CODA_CMD_SET_FRAME_BUF_STRIDE);
- if (dev->devtype->product == CODA_7541) {
+ if (dev->devtype->product == CODA_HX4 ||
+ dev->devtype->product == CODA_7541) {
coda_write(dev, q_data_src->bytesperline,
CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE);
}
@@ -1570,7 +1587,8 @@ static bool coda_reorder_enable(struct coda_ctx *ctx)
struct coda_dev *dev = ctx->dev;
int profile, level;
- if (dev->devtype->product != CODA_7541 &&
+ if (dev->devtype->product != CODA_HX4 &&
+ dev->devtype->product != CODA_7541 &&
dev->devtype->product != CODA_960)
return false;
@@ -1664,7 +1682,8 @@ static int __coda_start_decoding(struct coda_ctx *ctx)
CODA_CMD_DEC_SEQ_MP4_ASP_CLASS);
}
if (src_fourcc == V4L2_PIX_FMT_H264) {
- if (dev->devtype->product == CODA_7541) {
+ if (dev->devtype->product == CODA_HX4 ||
+ dev->devtype->product == CODA_7541) {
coda_write(dev, ctx->psbuf.paddr,
CODA_CMD_DEC_SEQ_PS_BB_START);
coda_write(dev, (CODA7_PS_BUF_SIZE / 1024),
@@ -1791,7 +1810,8 @@ static int __coda_start_decoding(struct coda_ctx *ctx)
CODA_CMD_SET_FRAME_SLICE_BB_SIZE);
}
- if (dev->devtype->product == CODA_7541) {
+ if (dev->devtype->product == CODA_HX4 ||
+ dev->devtype->product == CODA_7541) {
int max_mb_x = 1920 / 16;
int max_mb_y = 1088 / 16;
int max_mb_num = max_mb_x * max_mb_y;
@@ -1909,6 +1929,7 @@ static int coda_prepare_decode(struct coda_ctx *ctx)
switch (dev->devtype->product) {
case CODA_DX6:
/* TBD */
+ case CODA_HX4:
case CODA_7541:
coda_write(dev, CODA_PRE_SCAN_EN, CODA_CMD_DEC_PIC_OPTION);
break;
@@ -2049,7 +2070,8 @@ static void coda_finish_decode(struct coda_ctx *ctx)
v4l2_err(&dev->v4l2_dev,
"errors in %d macroblocks\n", err_mb);
- if (dev->devtype->product == CODA_7541) {
+ if (dev->devtype->product == CODA_HX4 ||
+ dev->devtype->product == CODA_7541) {
val = coda_read(dev, CODA_RET_DEC_PIC_OPTION);
if (val == 0) {
/* not enough bitstream data */
diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c
index e8a7554a61d2..04e35d70ce2e 100644
--- a/drivers/media/platform/coda/coda-common.c
+++ b/drivers/media/platform/coda/coda-common.c
@@ -128,7 +128,8 @@ void coda_write_base(struct coda_ctx *ctx, struct coda_q_data *q_data,
/*
* Arrays of codecs supported by each given version of Coda:
* i.MX27 -> codadx6
- * i.MX5x -> coda7
+ * i.MX51 -> codahx4
+ * i.MX53 -> coda7
* i.MX6 -> coda960
* Use V4L2_PIX_FMT_YUV420 as placeholder for all supported YUV 4:2:0 variants
*/
@@ -137,6 +138,13 @@ static const struct coda_codec codadx6_codecs[] = {
CODA_CODEC(CODADX6_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 720, 576),
};
+static const struct coda_codec codahx4_codecs[] = {
+ CODA_CODEC(CODA7_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 720, 576),
+ CODA_CODEC(CODA7_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1088),
+ CODA_CODEC(CODA7_MODE_DECODE_MP2, V4L2_PIX_FMT_MPEG2, V4L2_PIX_FMT_YUV420, 1920, 1088),
+ CODA_CODEC(CODA7_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1280, 720),
+};
+
static const struct coda_codec coda7_codecs[] = {
CODA_CODEC(CODA7_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 1280, 720),
CODA_CODEC(CODA7_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 1280, 720),
@@ -234,6 +242,11 @@ static const struct coda_video_device *codadx6_video_devices[] = {
&coda_bit_encoder,
};
+static const struct coda_video_device *codahx4_video_devices[] = {
+ &coda_bit_encoder,
+ &coda_bit_decoder,
+};
+
static const struct coda_video_device *coda7_video_devices[] = {
&coda_bit_jpeg_encoder,
&coda_bit_jpeg_decoder,
@@ -332,6 +345,8 @@ const char *coda_product_name(int product)
switch (product) {
case CODA_DX6:
return "CodaDx6";
+ case CODA_HX4:
+ return "CodaHx4";
case CODA_7541:
return "CODA7541";
case CODA_960:
@@ -1775,7 +1790,8 @@ static void coda_encode_ctrls(struct coda_ctx *ctx)
V4L2_CID_MPEG_VIDEO_H264_PROFILE,
V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, 0x0,
V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE);
- if (ctx->dev->devtype->product == CODA_7541) {
+ if (ctx->dev->devtype->product == CODA_HX4 ||
+ ctx->dev->devtype->product == CODA_7541) {
v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops,
V4L2_CID_MPEG_VIDEO_H264_LEVEL,
V4L2_MPEG_VIDEO_H264_LEVEL_3_1,
@@ -1803,7 +1819,8 @@ static void coda_encode_ctrls(struct coda_ctx *ctx)
V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE,
V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE, 0x0,
V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE);
- if (ctx->dev->devtype->product == CODA_7541 ||
+ if (ctx->dev->devtype->product == CODA_HX4 ||
+ ctx->dev->devtype->product == CODA_7541 ||
ctx->dev->devtype->product == CODA_960) {
v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops,
V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL,
@@ -2004,6 +2021,7 @@ static int coda_open(struct file *file)
if (enable_bwb || ctx->inst_type == CODA_INST_ENCODER)
ctx->frame_mem_ctrl = CODA9_FRAME_ENABLE_BWB;
/* fallthrough */
+ case CODA_HX4:
case CODA_7541:
ctx->reg_idx = 0;
break;
@@ -2182,7 +2200,8 @@ static int coda_hw_init(struct coda_dev *dev)
/* Tell the BIT where to find everything it needs */
if (dev->devtype->product == CODA_960 ||
- dev->devtype->product == CODA_7541) {
+ dev->devtype->product == CODA_7541 ||
+ dev->devtype->product == CODA_HX4) {
coda_write(dev, dev->tempbuf.paddr,
CODA_REG_BIT_TEMP_BUF_ADDR);
coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM);
@@ -2387,6 +2406,7 @@ put_pm:
enum coda_platform {
CODA_IMX27,
+ CODA_IMX51,
CODA_IMX53,
CODA_IMX6Q,
CODA_IMX6DL,
@@ -2407,6 +2427,21 @@ static const struct coda_devtype coda_devdata[] = {
.workbuf_size = 288 * 1024 + FMO_SLICE_SAVE_BUF_SIZE * 8 * 1024,
.iram_size = 0xb000,
},
+ [CODA_IMX51] = {
+ .firmware = {
+ "vpu_fw_imx51.bin",
+ "vpu/vpu_fw_imx51.bin",
+ "v4l-codahx4-imx51.bin"
+ },
+ .product = CODA_HX4,
+ .codecs = codahx4_codecs,
+ .num_codecs = ARRAY_SIZE(codahx4_codecs),
+ .vdevs = codahx4_video_devices,
+ .num_vdevs = ARRAY_SIZE(codahx4_video_devices),
+ .workbuf_size = 128 * 1024,
+ .tempbuf_size = 304 * 1024,
+ .iram_size = 0x14000,
+ },
[CODA_IMX53] = {
.firmware = {
"vpu_fw_imx53.bin",
@@ -2463,6 +2498,7 @@ MODULE_DEVICE_TABLE(platform, coda_platform_ids);
#ifdef CONFIG_OF
static const struct of_device_id coda_dt_ids[] = {
{ .compatible = "fsl,imx27-vpu", .data = &coda_devdata[CODA_IMX27] },
+ { .compatible = "fsl,imx51-vpu", .data = &coda_devdata[CODA_IMX51] },
{ .compatible = "fsl,imx53-vpu", .data = &coda_devdata[CODA_IMX53] },
{ .compatible = "fsl,imx6q-vpu", .data = &coda_devdata[CODA_IMX6Q] },
{ .compatible = "fsl,imx6dl-vpu", .data = &coda_devdata[CODA_IMX6DL] },
diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h
index c5f504d8cf67..c70cfab2433f 100644
--- a/drivers/media/platform/coda/coda.h
+++ b/drivers/media/platform/coda/coda.h
@@ -28,7 +28,7 @@
#include "coda_regs.h"
-#define CODA_MAX_FRAMEBUFFERS 17
+#define CODA_MAX_FRAMEBUFFERS 19
#define FMO_SLICE_SAVE_BUF_SIZE (32)
enum {
@@ -43,6 +43,7 @@ enum coda_inst_type {
enum coda_product {
CODA_DX6 = 0xf001,
+ CODA_HX4 = 0xf00a,
CODA_7541 = 0xf012,
CODA_960 = 0xf020,
};
diff --git a/drivers/media/platform/davinci/vpss.c b/drivers/media/platform/davinci/vpss.c
index b73886519f4f..19cf6853411e 100644
--- a/drivers/media/platform/davinci/vpss.c
+++ b/drivers/media/platform/davinci/vpss.c
@@ -116,7 +116,7 @@ struct vpss_hw_ops {
struct vpss_oper_config {
__iomem void *vpss_regs_base0;
__iomem void *vpss_regs_base1;
- resource_size_t *vpss_regs_base2;
+ __iomem void *vpss_regs_base2;
enum vpss_platform_type platform;
spinlock_t vpss_lock;
struct vpss_hw_ops hw_ops;
diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c
index ed9302caa004..a3cdac188190 100644
--- a/drivers/media/platform/exynos4-is/fimc-capture.c
+++ b/drivers/media/platform/exynos4-is/fimc-capture.c
@@ -670,10 +670,13 @@ static void fimc_capture_try_selection(struct fimc_ctx *ctx,
return;
}
if (target == V4L2_SEL_TGT_COMPOSE) {
+ u32 tmp_min_h = ffs(sink->width) - 3;
+ u32 tmp_min_v = ffs(sink->height) - 1;
+
if (ctx->rotation != 90 && ctx->rotation != 270)
align_h = 1;
- max_sc_h = min(SCALER_MAX_HRATIO, 1 << (ffs(sink->width) - 3));
- max_sc_v = min(SCALER_MAX_VRATIO, 1 << (ffs(sink->height) - 1));
+ max_sc_h = min(SCALER_MAX_HRATIO, 1 << tmp_min_h);
+ max_sc_v = min(SCALER_MAX_VRATIO, 1 << tmp_min_v);
min_sz = var->min_out_pixsize;
} else {
u32 depth = fimc_get_format_depth(sink->fmt);
diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.c b/drivers/media/platform/exynos4-is/fimc-is-regs.c
index cfe4406a83ff..e0e291066037 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-regs.c
+++ b/drivers/media/platform/exynos4-is/fimc-is-regs.c
@@ -159,7 +159,7 @@ void fimc_is_hw_load_setfile(struct fimc_is *is)
int fimc_is_hw_change_mode(struct fimc_is *is)
{
- const u8 cmd[] = {
+ static const u8 cmd[] = {
HIC_PREVIEW_STILL, HIC_PREVIEW_VIDEO,
HIC_CAPTURE_STILL, HIC_CAPTURE_VIDEO,
};
diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c
index 7b7250b1cff8..80670eeee142 100644
--- a/drivers/media/platform/marvell-ccic/mcam-core.c
+++ b/drivers/media/platform/marvell-ccic/mcam-core.c
@@ -1443,24 +1443,24 @@ static int mcam_vidioc_s_input(struct file *filp, void *priv, unsigned int i)
* the level which controls the number of read buffers.
*/
static int mcam_vidioc_g_parm(struct file *filp, void *priv,
- struct v4l2_streamparm *parms)
+ struct v4l2_streamparm *a)
{
struct mcam_camera *cam = video_drvdata(filp);
int ret;
- ret = sensor_call(cam, video, g_parm, parms);
- parms->parm.capture.readbuffers = n_dma_bufs;
+ ret = v4l2_g_parm_cap(video_devdata(filp), cam->sensor, a);
+ a->parm.capture.readbuffers = n_dma_bufs;
return ret;
}
static int mcam_vidioc_s_parm(struct file *filp, void *priv,
- struct v4l2_streamparm *parms)
+ struct v4l2_streamparm *a)
{
struct mcam_camera *cam = video_drvdata(filp);
int ret;
- ret = sensor_call(cam, video, s_parm, parms);
- parms->parm.capture.readbuffers = n_dma_bufs;
+ ret = v4l2_s_parm_cap(video_devdata(filp), cam->sensor, a);
+ a->parm.capture.readbuffers = n_dma_bufs;
return ret;
}
diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c
index 226f90886484..af17aaa21f58 100644
--- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c
+++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c
@@ -1081,11 +1081,11 @@ static int mtk_jpeg_clk_init(struct mtk_jpeg_dev *jpeg)
jpeg->clk_jdec = devm_clk_get(jpeg->dev, "jpgdec");
if (IS_ERR(jpeg->clk_jdec))
- return -EINVAL;
+ return PTR_ERR(jpeg->clk_jdec);
jpeg->clk_jdec_smi = devm_clk_get(jpeg->dev, "jpgdec-smi");
if (IS_ERR(jpeg->clk_jdec_smi))
- return -EINVAL;
+ return PTR_ERR(jpeg->clk_jdec_smi);
return 0;
}
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c
index 843510979ad8..86f0a7134365 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c
@@ -1224,6 +1224,8 @@ static void vb2ops_vdec_buf_queue(struct vb2_buffer *vb)
ctx->dpb_size = dpbsize;
ctx->state = MTK_STATE_HEADER;
mtk_v4l2_debug(1, "[%d] dpbsize=%d", ctx->id, ctx->dpb_size);
+
+ mtk_vdec_queue_res_chg_event(ctx);
}
static void vb2ops_vdec_buf_finish(struct vb2_buffer *vb)
diff --git a/drivers/media/platform/omap/omap_vout_vrfb.c b/drivers/media/platform/omap/omap_vout_vrfb.c
index 123c2b26a933..1d8508237220 100644
--- a/drivers/media/platform/omap/omap_vout_vrfb.c
+++ b/drivers/media/platform/omap/omap_vout_vrfb.c
@@ -236,7 +236,6 @@ int omap_vout_prepare_vrfb(struct omap_vout_device *vout,
struct dma_async_tx_descriptor *tx;
enum dma_ctrl_flags flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK;
struct dma_chan *chan = vout->vrfb_dma_tx.chan;
- struct dma_device *dmadev = chan->device;
struct dma_interleaved_template *xt = vout->vrfb_dma_tx.xt;
dma_cookie_t cookie;
enum dma_status status;
@@ -271,7 +270,7 @@ int omap_vout_prepare_vrfb(struct omap_vout_device *vout,
xt->dst_sgl = true;
xt->dst_inc = true;
- tx = dmadev->device_prep_interleaved_dma(chan, xt, flags);
+ tx = dmaengine_prep_interleaved_dma(chan, xt, flags);
if (tx == NULL) {
pr_err("%s: DMA interleaved prep error\n", __func__);
return -EINVAL;
diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c
index 521d4b36c090..c4a577848dd7 100644
--- a/drivers/media/platform/qcom/venus/firmware.c
+++ b/drivers/media/platform/qcom/venus/firmware.c
@@ -76,7 +76,7 @@ int venus_boot(struct device *dev, const char *fwname)
}
ret = qcom_mdt_load(dev, mdt, fwname, VENUS_PAS_ID, mem_va, mem_phys,
- mem_size);
+ mem_size, NULL);
release_firmware(mdt);
diff --git a/drivers/media/platform/qcom/venus/hfi.c b/drivers/media/platform/qcom/venus/hfi.c
index 1baf78d3c02d..bca894a00c07 100644
--- a/drivers/media/platform/qcom/venus/hfi.c
+++ b/drivers/media/platform/qcom/venus/hfi.c
@@ -106,8 +106,8 @@ int hfi_core_deinit(struct venus_core *core, bool blocking)
if (!empty) {
mutex_unlock(&core->lock);
- wait_on_atomic_t(&core->insts_count, atomic_t_wait,
- TASK_UNINTERRUPTIBLE);
+ wait_var_event(&core->insts_count,
+ !atomic_read(&core->insts_count));
mutex_lock(&core->lock);
}
@@ -229,8 +229,8 @@ void hfi_session_destroy(struct venus_inst *inst)
mutex_lock(&core->lock);
list_del_init(&inst->list);
- atomic_dec(&core->insts_count);
- wake_up_atomic_t(&core->insts_count);
+ if (atomic_dec_and_test(&core->insts_count))
+ wake_up_var(&core->insts_count);
mutex_unlock(&core->lock);
}
EXPORT_SYMBOL_GPL(hfi_session_destroy);
diff --git a/drivers/media/platform/qcom/venus/hfi_msgs.c b/drivers/media/platform/qcom/venus/hfi_msgs.c
index a681ae5381d6..90c93d9603dc 100644
--- a/drivers/media/platform/qcom/venus/hfi_msgs.c
+++ b/drivers/media/platform/qcom/venus/hfi_msgs.c
@@ -659,10 +659,10 @@ static u32 init_done_read_prop(struct venus_core *core, struct venus_inst *inst,
prop->buffer_type == HFI_BUFFER_OUTPUT2) {
switch (prop->data[i]) {
case HFI_BUFFER_MODE_STATIC:
- inst->cap_bufs_mode_static = 1;
+ inst->cap_bufs_mode_static = true;
break;
case HFI_BUFFER_MODE_DYNAMIC:
- inst->cap_bufs_mode_dynamic = 1;
+ inst->cap_bufs_mode_dynamic = true;
break;
default:
break;
diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c
index c9e9576bb08a..49bbd1861d3a 100644
--- a/drivers/media/platform/qcom/venus/vdec.c
+++ b/drivers/media/platform/qcom/venus/vdec.c
@@ -135,20 +135,21 @@ find_format_by_index(struct venus_inst *inst, unsigned int index, u32 type)
return NULL;
for (i = 0; i < size; i++) {
+ bool valid;
+
if (fmt[i].type != type)
continue;
- if (k == index)
+ valid = type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ||
+ venus_helper_check_codec(inst, fmt[i].pixfmt);
+ if (k == index && valid)
break;
- k++;
+ if (valid)
+ k++;
}
if (i == size)
return NULL;
- if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
- !venus_helper_check_codec(inst, fmt[i].pixfmt))
- return NULL;
-
return &fmt[i];
}
diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c
index e3a10a852cad..6b2ce479584e 100644
--- a/drivers/media/platform/qcom/venus/venc.c
+++ b/drivers/media/platform/qcom/venus/venc.c
@@ -120,20 +120,21 @@ find_format_by_index(struct venus_inst *inst, unsigned int index, u32 type)
return NULL;
for (i = 0; i < size; i++) {
+ bool valid;
+
if (fmt[i].type != type)
continue;
- if (k == index)
+ valid = type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
+ venus_helper_check_codec(inst, fmt[i].pixfmt);
+ if (k == index && valid)
break;
- k++;
+ if (valid)
+ k++;
}
if (i == size)
return NULL;
- if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
- !venus_helper_check_codec(inst, fmt[i].pixfmt))
- return NULL;
-
return &fmt[i];
}
diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c
index 23fdff7a7370..4a40e6ad1be7 100644
--- a/drivers/media/platform/rcar-vin/rcar-dma.c
+++ b/drivers/media/platform/rcar-vin/rcar-dma.c
@@ -168,12 +168,8 @@ static int rvin_setup(struct rvin_dev *vin)
break;
case V4L2_FIELD_ALTERNATE:
case V4L2_FIELD_NONE:
- if (vin->continuous) {
- vnmc = VNMC_IM_ODD_EVEN;
- progressive = true;
- } else {
- vnmc = VNMC_IM_ODD;
- }
+ vnmc = VNMC_IM_ODD_EVEN;
+ progressive = true;
break;
default:
vnmc = VNMC_IM_ODD;
@@ -298,14 +294,6 @@ static bool rvin_capture_active(struct rvin_dev *vin)
return rvin_read(vin, VNMS_REG) & VNMS_CA;
}
-static int rvin_get_active_slot(struct rvin_dev *vin, u32 vnms)
-{
- if (vin->continuous)
- return (vnms & VNMS_FBS_MASK) >> VNMS_FBS_SHIFT;
-
- return 0;
-}
-
static enum v4l2_field rvin_get_active_field(struct rvin_dev *vin, u32 vnms)
{
if (vin->format.field == V4L2_FIELD_ALTERNATE) {
@@ -344,76 +332,47 @@ static void rvin_set_slot_addr(struct rvin_dev *vin, int slot, dma_addr_t addr)
rvin_write(vin, offset, VNMB_REG(slot));
}
-/* Moves a buffer from the queue to the HW slots */
-static bool rvin_fill_hw_slot(struct rvin_dev *vin, int slot)
+/*
+ * Moves a buffer from the queue to the HW slot. If no buffer is
+ * available use the scratch buffer. The scratch buffer is never
+ * returned to userspace, its only function is to enable the capture
+ * loop to keep running.
+ */
+static void rvin_fill_hw_slot(struct rvin_dev *vin, int slot)
{
struct rvin_buffer *buf;
struct vb2_v4l2_buffer *vbuf;
- dma_addr_t phys_addr_top;
-
- if (vin->queue_buf[slot] != NULL)
- return true;
+ dma_addr_t phys_addr;
- if (list_empty(&vin->buf_list))
- return false;
+ /* A already populated slot shall never be overwritten. */
+ if (WARN_ON(vin->queue_buf[slot] != NULL))
+ return;
vin_dbg(vin, "Filling HW slot: %d\n", slot);
- /* Keep track of buffer we give to HW */
- buf = list_entry(vin->buf_list.next, struct rvin_buffer, list);
- vbuf = &buf->vb;
- list_del_init(to_buf_list(vbuf));
- vin->queue_buf[slot] = vbuf;
-
- /* Setup DMA */
- phys_addr_top = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0);
- rvin_set_slot_addr(vin, slot, phys_addr_top);
-
- return true;
-}
-
-static bool rvin_fill_hw(struct rvin_dev *vin)
-{
- int slot, limit;
-
- limit = vin->continuous ? HW_BUFFER_NUM : 1;
-
- for (slot = 0; slot < limit; slot++)
- if (!rvin_fill_hw_slot(vin, slot))
- return false;
- return true;
-}
-
-static void rvin_capture_on(struct rvin_dev *vin)
-{
- vin_dbg(vin, "Capture on in %s mode\n",
- vin->continuous ? "continuous" : "single");
+ if (list_empty(&vin->buf_list)) {
+ vin->queue_buf[slot] = NULL;
+ phys_addr = vin->scratch_phys;
+ } else {
+ /* Keep track of buffer we give to HW */
+ buf = list_entry(vin->buf_list.next, struct rvin_buffer, list);
+ vbuf = &buf->vb;
+ list_del_init(to_buf_list(vbuf));
+ vin->queue_buf[slot] = vbuf;
+
+ /* Setup DMA */
+ phys_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0);
+ }
- if (vin->continuous)
- /* Continuous Frame Capture Mode */
- rvin_write(vin, VNFC_C_FRAME, VNFC_REG);
- else
- /* Single Frame Capture Mode */
- rvin_write(vin, VNFC_S_FRAME, VNFC_REG);
+ rvin_set_slot_addr(vin, slot, phys_addr);
}
static int rvin_capture_start(struct rvin_dev *vin)
{
- struct rvin_buffer *buf, *node;
- int bufs, ret;
-
- /* Count number of free buffers */
- bufs = 0;
- list_for_each_entry_safe(buf, node, &vin->buf_list, list)
- bufs++;
+ int slot, ret;
- /* Continuous capture requires more buffers then there are HW slots */
- vin->continuous = bufs > HW_BUFFER_NUM;
-
- if (!rvin_fill_hw(vin)) {
- vin_err(vin, "HW not ready to start, not enough buffers available\n");
- return -EINVAL;
- }
+ for (slot = 0; slot < HW_BUFFER_NUM; slot++)
+ rvin_fill_hw_slot(vin, slot);
rvin_crop_scale_comp(vin);
@@ -421,7 +380,10 @@ static int rvin_capture_start(struct rvin_dev *vin)
if (ret)
return ret;
- rvin_capture_on(vin);
+ vin_dbg(vin, "Starting to capture\n");
+
+ /* Continuous Frame Capture Mode */
+ rvin_write(vin, VNFC_C_FRAME, VNFC_REG);
vin->state = RUNNING;
@@ -904,7 +866,7 @@ static irqreturn_t rvin_irq(int irq, void *data)
struct rvin_dev *vin = data;
u32 int_status, vnms;
int slot;
- unsigned int i, sequence, handled = 0;
+ unsigned int handled = 0;
unsigned long flags;
spin_lock_irqsave(&vin->qlock, flags);
@@ -930,65 +892,25 @@ static irqreturn_t rvin_irq(int irq, void *data)
/* Prepare for capture and update state */
vnms = rvin_read(vin, VNMS_REG);
- slot = rvin_get_active_slot(vin, vnms);
- sequence = vin->sequence++;
-
- vin_dbg(vin, "IRQ %02d: %d\tbuf0: %c buf1: %c buf2: %c\tmore: %d\n",
- sequence, slot,
- slot == 0 ? 'x' : vin->queue_buf[0] != NULL ? '1' : '0',
- slot == 1 ? 'x' : vin->queue_buf[1] != NULL ? '1' : '0',
- slot == 2 ? 'x' : vin->queue_buf[2] != NULL ? '1' : '0',
- !list_empty(&vin->buf_list));
-
- /* HW have written to a slot that is not prepared we are in trouble */
- if (WARN_ON((vin->queue_buf[slot] == NULL)))
- goto done;
+ slot = (vnms & VNMS_FBS_MASK) >> VNMS_FBS_SHIFT;
/* Capture frame */
- vin->queue_buf[slot]->field = rvin_get_active_field(vin, vnms);
- vin->queue_buf[slot]->sequence = sequence;
- vin->queue_buf[slot]->vb2_buf.timestamp = ktime_get_ns();
- vb2_buffer_done(&vin->queue_buf[slot]->vb2_buf, VB2_BUF_STATE_DONE);
- vin->queue_buf[slot] = NULL;
-
- /* Prepare for next frame */
- if (!rvin_fill_hw(vin)) {
-
- /*
- * Can't supply HW with new buffers fast enough. Halt
- * capture until more buffers are available.
- */
- vin->state = STALLED;
-
- /*
- * The continuous capturing requires an explicit stop
- * operation when there is no buffer to be set into
- * the VnMBm registers.
- */
- if (vin->continuous) {
- rvin_capture_stop(vin);
- vin_dbg(vin, "IRQ %02d: hw not ready stop\n", sequence);
-
- /* Maybe we can continue in single capture mode */
- for (i = 0; i < HW_BUFFER_NUM; i++) {
- if (vin->queue_buf[i]) {
- list_add(to_buf_list(vin->queue_buf[i]),
- &vin->buf_list);
- vin->queue_buf[i] = NULL;
- }
- }
-
- if (!list_empty(&vin->buf_list))
- rvin_capture_start(vin);
- }
+ if (vin->queue_buf[slot]) {
+ vin->queue_buf[slot]->field = rvin_get_active_field(vin, vnms);
+ vin->queue_buf[slot]->sequence = vin->sequence;
+ vin->queue_buf[slot]->vb2_buf.timestamp = ktime_get_ns();
+ vb2_buffer_done(&vin->queue_buf[slot]->vb2_buf,
+ VB2_BUF_STATE_DONE);
+ vin->queue_buf[slot] = NULL;
} else {
- /*
- * The single capturing requires an explicit capture
- * operation to fetch the next frame.
- */
- if (!vin->continuous)
- rvin_capture_on(vin);
+ /* Scratch buffer was used, dropping frame. */
+ vin_dbg(vin, "Dropping frame %u\n", vin->sequence);
}
+
+ vin->sequence++;
+
+ /* Prepare for next frame */
+ rvin_fill_hw_slot(vin, slot);
done:
spin_unlock_irqrestore(&vin->qlock, flags);
@@ -1059,13 +981,6 @@ static void rvin_buffer_queue(struct vb2_buffer *vb)
list_add_tail(to_buf_list(vbuf), &vin->buf_list);
- /*
- * If capture is stalled add buffer to HW and restart
- * capturing if HW is ready to continue.
- */
- if (vin->state == STALLED)
- rvin_capture_start(vin);
-
spin_unlock_irqrestore(&vin->qlock, flags);
}
@@ -1076,6 +991,17 @@ static int rvin_start_streaming(struct vb2_queue *vq, unsigned int count)
unsigned long flags;
int ret;
+ /* Allocate scratch buffer. */
+ vin->scratch = dma_alloc_coherent(vin->dev, vin->format.sizeimage,
+ &vin->scratch_phys, GFP_KERNEL);
+ if (!vin->scratch) {
+ spin_lock_irqsave(&vin->qlock, flags);
+ return_all_buffers(vin, VB2_BUF_STATE_QUEUED);
+ spin_unlock_irqrestore(&vin->qlock, flags);
+ vin_err(vin, "Failed to allocate scratch buffer\n");
+ return -ENOMEM;
+ }
+
sd = vin_to_source(vin);
v4l2_subdev_call(sd, video, s_stream, 1);
@@ -1091,6 +1017,10 @@ static int rvin_start_streaming(struct vb2_queue *vq, unsigned int count)
spin_unlock_irqrestore(&vin->qlock, flags);
+ if (ret)
+ dma_free_coherent(vin->dev, vin->format.sizeimage, vin->scratch,
+ vin->scratch_phys);
+
return ret;
}
@@ -1141,6 +1071,10 @@ static void rvin_stop_streaming(struct vb2_queue *vq)
/* disable interrupts */
rvin_disable_interrupts(vin);
+
+ /* Free scratch buffer. */
+ dma_free_coherent(vin->dev, vin->format.sizeimage, vin->scratch,
+ vin->scratch_phys);
}
static const struct vb2_ops rvin_qops = {
@@ -1189,7 +1123,7 @@ int rvin_dma_probe(struct rvin_dev *vin, int irq)
q->ops = &rvin_qops;
q->mem_ops = &vb2_dma_contig_memops;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- q->min_buffers_needed = 1;
+ q->min_buffers_needed = 4;
q->dev = vin->dev;
ret = vb2_queue_init(q);
diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h
index 5382078143fb..95897127cc41 100644
--- a/drivers/media/platform/rcar-vin/rcar-vin.h
+++ b/drivers/media/platform/rcar-vin/rcar-vin.h
@@ -38,13 +38,11 @@ enum chip_id {
/**
* STOPPED - No operation in progress
* RUNNING - Operation in progress have buffers
- * STALLED - No operation in progress have no buffers
* STOPPING - Stopping operation
*/
enum rvin_dma_state {
STOPPED = 0,
RUNNING,
- STALLED,
STOPPING,
};
@@ -102,12 +100,13 @@ struct rvin_graph_entity {
*
* @lock: protects @queue
* @queue: vb2 buffers queue
+ * @scratch: cpu address for scratch buffer
+ * @scratch_phys: physical address of the scratch buffer
*
- * @qlock: protects @queue_buf, @buf_list, @continuous, @sequence
+ * @qlock: protects @queue_buf, @buf_list, @sequence
* @state
* @queue_buf: Keeps track of buffers given to HW slot
* @buf_list: list of queued buffers
- * @continuous: tracks if active operation is continuous or single mode
* @sequence: V4L2 buffers sequence number
* @state: keeps track of operation state
*
@@ -130,11 +129,12 @@ struct rvin_dev {
struct mutex lock;
struct vb2_queue queue;
+ void *scratch;
+ dma_addr_t scratch_phys;
spinlock_t qlock;
struct vb2_v4l2_buffer *queue_buf[HW_BUFFER_NUM];
struct list_head buf_list;
- bool continuous;
unsigned int sequence;
enum rvin_dma_state state;
diff --git a/drivers/media/platform/rcar_drif.c b/drivers/media/platform/rcar_drif.c
index b2e080ef5391..dc7e280c91b4 100644
--- a/drivers/media/platform/rcar_drif.c
+++ b/drivers/media/platform/rcar_drif.c
@@ -274,7 +274,7 @@ static int rcar_drif_alloc_dmachannels(struct rcar_drif_sdr *sdr)
{
struct dma_slave_config dma_cfg;
unsigned int i;
- int ret = -ENODEV;
+ int ret;
for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
struct rcar_drif *ch = sdr->ch[i];
@@ -282,6 +282,7 @@ static int rcar_drif_alloc_dmachannels(struct rcar_drif_sdr *sdr)
ch->dmach = dma_request_slave_channel(&ch->pdev->dev, "rx");
if (!ch->dmach) {
rdrif_err(sdr, "ch%u: dma channel req failed\n", i);
+ ret = -ENODEV;
goto dmach_error;
}
diff --git a/drivers/media/platform/renesas-ceu.c b/drivers/media/platform/renesas-ceu.c
new file mode 100644
index 000000000000..6599dba5ab84
--- /dev/null
+++ b/drivers/media/platform/renesas-ceu.c
@@ -0,0 +1,1677 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * V4L2 Driver for Renesas Capture Engine Unit (CEU) interface
+ * Copyright (C) 2017-2018 Jacopo Mondi <jacopo+renesas@jmondi.org>
+ *
+ * Based on soc-camera driver "soc_camera/sh_mobile_ceu_camera.c"
+ * Copyright (C) 2008 Magnus Damm
+ *
+ * Based on V4L2 Driver for PXA camera host - "pxa_camera.c",
+ * Copyright (C) 2006, Sascha Hauer, Pengutronix
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-async.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-image-sizes.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mediabus.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include <media/drv-intf/renesas-ceu.h>
+
+#define DRIVER_NAME "renesas-ceu"
+
+/* CEU registers offsets and masks. */
+#define CEU_CAPSR 0x00 /* Capture start register */
+#define CEU_CAPCR 0x04 /* Capture control register */
+#define CEU_CAMCR 0x08 /* Capture interface control register */
+#define CEU_CAMOR 0x10 /* Capture interface offset register */
+#define CEU_CAPWR 0x14 /* Capture interface width register */
+#define CEU_CAIFR 0x18 /* Capture interface input format register */
+#define CEU_CRCNTR 0x28 /* CEU register control register */
+#define CEU_CRCMPR 0x2c /* CEU register forcible control register */
+#define CEU_CFLCR 0x30 /* Capture filter control register */
+#define CEU_CFSZR 0x34 /* Capture filter size clip register */
+#define CEU_CDWDR 0x38 /* Capture destination width register */
+#define CEU_CDAYR 0x3c /* Capture data address Y register */
+#define CEU_CDACR 0x40 /* Capture data address C register */
+#define CEU_CFWCR 0x5c /* Firewall operation control register */
+#define CEU_CDOCR 0x64 /* Capture data output control register */
+#define CEU_CEIER 0x70 /* Capture event interrupt enable register */
+#define CEU_CETCR 0x74 /* Capture event flag clear register */
+#define CEU_CSTSR 0x7c /* Capture status register */
+#define CEU_CSRTR 0x80 /* Capture software reset register */
+
+/* Data synchronous fetch mode. */
+#define CEU_CAMCR_JPEG BIT(4)
+
+/* Input components ordering: CEU_CAMCR.DTARY field. */
+#define CEU_CAMCR_DTARY_8_UYVY (0x00 << 8)
+#define CEU_CAMCR_DTARY_8_VYUY (0x01 << 8)
+#define CEU_CAMCR_DTARY_8_YUYV (0x02 << 8)
+#define CEU_CAMCR_DTARY_8_YVYU (0x03 << 8)
+/* TODO: input components ordering for 16 bits input. */
+
+/* Bus transfer MTU. */
+#define CEU_CAPCR_BUS_WIDTH256 (0x3 << 20)
+
+/* Bus width configuration. */
+#define CEU_CAMCR_DTIF_16BITS BIT(12)
+
+/* No downsampling to planar YUV420 in image fetch mode. */
+#define CEU_CDOCR_NO_DOWSAMPLE BIT(4)
+
+/* Swap all input data in 8-bit, 16-bits and 32-bits units (Figure 46.45). */
+#define CEU_CDOCR_SWAP_ENDIANNESS (7)
+
+/* Capture reset and enable bits. */
+#define CEU_CAPSR_CPKIL BIT(16)
+#define CEU_CAPSR_CE BIT(0)
+
+/* CEU operating flag bit. */
+#define CEU_CAPCR_CTNCP BIT(16)
+#define CEU_CSTRST_CPTON BIT(0)
+
+/* Platform specific IRQ source flags. */
+#define CEU_CETCR_ALL_IRQS_RZ 0x397f313
+#define CEU_CETCR_ALL_IRQS_SH4 0x3d7f313
+
+/* Prohibited register access interrupt bit. */
+#define CEU_CETCR_IGRW BIT(4)
+/* One-frame capture end interrupt. */
+#define CEU_CEIER_CPE BIT(0)
+/* VBP error. */
+#define CEU_CEIER_VBP BIT(20)
+#define CEU_CEIER_MASK (CEU_CEIER_CPE | CEU_CEIER_VBP)
+
+#define CEU_MAX_WIDTH 2560
+#define CEU_MAX_HEIGHT 1920
+#define CEU_MAX_BPL 8188
+#define CEU_W_MAX(w) ((w) < CEU_MAX_WIDTH ? (w) : CEU_MAX_WIDTH)
+#define CEU_H_MAX(h) ((h) < CEU_MAX_HEIGHT ? (h) : CEU_MAX_HEIGHT)
+
+/*
+ * ceu_bus_fmt - describe a 8-bits yuyv format the sensor can produce
+ *
+ * @mbus_code: bus format code
+ * @fmt_order: CEU_CAMCR.DTARY ordering of input components (Y, Cb, Cr)
+ * @fmt_order_swap: swapped CEU_CAMCR.DTARY ordering of input components
+ * (Y, Cr, Cb)
+ * @swapped: does Cr appear before Cb?
+ * @bps: number of bits sent over bus for each sample
+ * @bpp: number of bits per pixels unit
+ */
+struct ceu_mbus_fmt {
+ u32 mbus_code;
+ u32 fmt_order;
+ u32 fmt_order_swap;
+ bool swapped;
+ u8 bps;
+ u8 bpp;
+};
+
+/*
+ * ceu_buffer - Link vb2 buffer to the list of available buffers.
+ */
+struct ceu_buffer {
+ struct vb2_v4l2_buffer vb;
+ struct list_head queue;
+};
+
+static inline struct ceu_buffer *vb2_to_ceu(struct vb2_v4l2_buffer *vbuf)
+{
+ return container_of(vbuf, struct ceu_buffer, vb);
+}
+
+/*
+ * ceu_subdev - Wraps v4l2 sub-device and provides async subdevice.
+ */
+struct ceu_subdev {
+ struct v4l2_subdev *v4l2_sd;
+ struct v4l2_async_subdev asd;
+
+ /* per-subdevice mbus configuration options */
+ unsigned int mbus_flags;
+ struct ceu_mbus_fmt mbus_fmt;
+};
+
+static struct ceu_subdev *to_ceu_subdev(struct v4l2_async_subdev *asd)
+{
+ return container_of(asd, struct ceu_subdev, asd);
+}
+
+/*
+ * ceu_device - CEU device instance
+ */
+struct ceu_device {
+ struct device *dev;
+ struct video_device vdev;
+ struct v4l2_device v4l2_dev;
+
+ /* subdevices descriptors */
+ struct ceu_subdev *subdevs;
+ /* the subdevice currently in use */
+ struct ceu_subdev *sd;
+ unsigned int sd_index;
+ unsigned int num_sd;
+
+ /* platform specific mask with all IRQ sources flagged */
+ u32 irq_mask;
+
+ /* currently configured field and pixel format */
+ enum v4l2_field field;
+ struct v4l2_pix_format_mplane v4l2_pix;
+
+ /* async subdev notification helpers */
+ struct v4l2_async_notifier notifier;
+ /* pointers to "struct ceu_subdevice -> asd" */
+ struct v4l2_async_subdev **asds;
+
+ /* vb2 queue, capture buffer list and active buffer pointer */
+ struct vb2_queue vb2_vq;
+ struct list_head capture;
+ struct vb2_v4l2_buffer *active;
+ unsigned int sequence;
+
+ /* mlock - lock access to interface reset and vb2 queue */
+ struct mutex mlock;
+
+ /* lock - lock access to capture buffer queue and active buffer */
+ spinlock_t lock;
+
+ /* base - CEU memory base address */
+ void __iomem *base;
+};
+
+static inline struct ceu_device *v4l2_to_ceu(struct v4l2_device *v4l2_dev)
+{
+ return container_of(v4l2_dev, struct ceu_device, v4l2_dev);
+}
+
+/* --- CEU memory output formats --- */
+
+/*
+ * ceu_fmt - describe a memory output format supported by CEU interface.
+ *
+ * @fourcc: memory layout fourcc format code
+ * @bpp: number of bits for each pixel stored in memory
+ */
+struct ceu_fmt {
+ u32 fourcc;
+ u32 bpp;
+};
+
+/*
+ * ceu_format_list - List of supported memory output formats
+ *
+ * If sensor provides any YUYV bus format, all the following planar memory
+ * formats are available thanks to CEU re-ordering and sub-sampling
+ * capabilities.
+ */
+static const struct ceu_fmt ceu_fmt_list[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .bpp = 16,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV61,
+ .bpp = 16,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .bpp = 12,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .bpp = 12,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .bpp = 16,
+ },
+};
+
+static const struct ceu_fmt *get_ceu_fmt_from_fourcc(unsigned int fourcc)
+{
+ const struct ceu_fmt *fmt = &ceu_fmt_list[0];
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(ceu_fmt_list); i++, fmt++)
+ if (fmt->fourcc == fourcc)
+ return fmt;
+
+ return NULL;
+}
+
+static bool ceu_fmt_mplane(struct v4l2_pix_format_mplane *pix)
+{
+ switch (pix->pixelformat) {
+ case V4L2_PIX_FMT_YUYV:
+ return false;
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* --- CEU HW operations --- */
+
+static void ceu_write(struct ceu_device *priv, unsigned int reg_offs, u32 data)
+{
+ iowrite32(data, priv->base + reg_offs);
+}
+
+static u32 ceu_read(struct ceu_device *priv, unsigned int reg_offs)
+{
+ return ioread32(priv->base + reg_offs);
+}
+
+/*
+ * ceu_soft_reset() - Software reset the CEU interface.
+ * @ceu_device: CEU device.
+ *
+ * Returns 0 for success, -EIO for error.
+ */
+static int ceu_soft_reset(struct ceu_device *ceudev)
+{
+ unsigned int i;
+
+ ceu_write(ceudev, CEU_CAPSR, CEU_CAPSR_CPKIL);
+
+ for (i = 0; i < 100; i++) {
+ if (!(ceu_read(ceudev, CEU_CSTSR) & CEU_CSTRST_CPTON))
+ break;
+ udelay(1);
+ }
+
+ if (i == 100) {
+ dev_err(ceudev->dev, "soft reset time out\n");
+ return -EIO;
+ }
+
+ for (i = 0; i < 100; i++) {
+ if (!(ceu_read(ceudev, CEU_CAPSR) & CEU_CAPSR_CPKIL))
+ return 0;
+ udelay(1);
+ }
+
+ /* If we get here, CEU has not reset properly. */
+ return -EIO;
+}
+
+/* --- CEU Capture Operations --- */
+
+/*
+ * ceu_hw_config() - Configure CEU interface registers.
+ */
+static int ceu_hw_config(struct ceu_device *ceudev)
+{
+ u32 camcr, cdocr, cfzsr, cdwdr, capwr;
+ struct v4l2_pix_format_mplane *pix = &ceudev->v4l2_pix;
+ struct ceu_subdev *ceu_sd = ceudev->sd;
+ struct ceu_mbus_fmt *mbus_fmt = &ceu_sd->mbus_fmt;
+ unsigned int mbus_flags = ceu_sd->mbus_flags;
+
+ /* Start configuring CEU registers */
+ ceu_write(ceudev, CEU_CAIFR, 0);
+ ceu_write(ceudev, CEU_CFWCR, 0);
+ ceu_write(ceudev, CEU_CRCNTR, 0);
+ ceu_write(ceudev, CEU_CRCMPR, 0);
+
+ /* Set the frame capture period for both image capture and data sync. */
+ capwr = (pix->height << 16) | pix->width * mbus_fmt->bpp / 8;
+
+ /*
+ * Swap input data endianness by default.
+ * In data fetch mode bytes are received in chunks of 8 bytes.
+ * D0, D1, D2, D3, D4, D5, D6, D7 (D0 received first)
+ * The data is however by default written to memory in reverse order:
+ * D7, D6, D5, D4, D3, D2, D1, D0 (D7 written to lowest byte)
+ *
+ * Use CEU_CDOCR[2:0] to swap data ordering.
+ */
+ cdocr = CEU_CDOCR_SWAP_ENDIANNESS;
+
+ /*
+ * Configure CAMCR and CDOCR:
+ * match input components ordering with memory output format and
+ * handle downsampling to YUV420.
+ *
+ * If the memory output planar format is 'swapped' (Cr before Cb) and
+ * input format is not, use the swapped version of CAMCR.DTARY.
+ *
+ * If the memory output planar format is not 'swapped' (Cb before Cr)
+ * and input format is, use the swapped version of CAMCR.DTARY.
+ *
+ * CEU by default downsample to planar YUV420 (CDCOR[4] = 0).
+ * If output is planar YUV422 set CDOCR[4] = 1
+ *
+ * No downsample for data fetch sync mode.
+ */
+ switch (pix->pixelformat) {
+ /* Data fetch sync mode */
+ case V4L2_PIX_FMT_YUYV:
+ /* TODO: handle YUYV permutations through DTARY bits. */
+ camcr = CEU_CAMCR_JPEG;
+ cdocr |= CEU_CDOCR_NO_DOWSAMPLE;
+ cfzsr = (pix->height << 16) | pix->width;
+ cdwdr = pix->plane_fmt[0].bytesperline;
+ break;
+
+ /* Non-swapped planar image capture mode. */
+ case V4L2_PIX_FMT_NV16:
+ cdocr |= CEU_CDOCR_NO_DOWSAMPLE;
+ /* fall-through */
+ case V4L2_PIX_FMT_NV12:
+ if (mbus_fmt->swapped)
+ camcr = mbus_fmt->fmt_order_swap;
+ else
+ camcr = mbus_fmt->fmt_order;
+
+ cfzsr = (pix->height << 16) | pix->width;
+ cdwdr = pix->width;
+ break;
+
+ /* Swapped planar image capture mode. */
+ case V4L2_PIX_FMT_NV61:
+ cdocr |= CEU_CDOCR_NO_DOWSAMPLE;
+ /* fall-through */
+ case V4L2_PIX_FMT_NV21:
+ if (mbus_fmt->swapped)
+ camcr = mbus_fmt->fmt_order;
+ else
+ camcr = mbus_fmt->fmt_order_swap;
+
+ cfzsr = (pix->height << 16) | pix->width;
+ cdwdr = pix->width;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ camcr |= mbus_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
+ camcr |= mbus_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ? 1 << 0 : 0;
+
+ /* TODO: handle 16 bit bus width with DTIF bit in CAMCR */
+ ceu_write(ceudev, CEU_CAMCR, camcr);
+ ceu_write(ceudev, CEU_CDOCR, cdocr);
+ ceu_write(ceudev, CEU_CAPCR, CEU_CAPCR_BUS_WIDTH256);
+
+ /*
+ * TODO: make CAMOR offsets configurable.
+ * CAMOR wants to know the number of blanks between a VS/HS signal
+ * and valid data. This value should actually come from the sensor...
+ */
+ ceu_write(ceudev, CEU_CAMOR, 0);
+
+ /* TODO: 16 bit bus width require re-calculation of cdwdr and cfzsr */
+ ceu_write(ceudev, CEU_CAPWR, capwr);
+ ceu_write(ceudev, CEU_CFSZR, cfzsr);
+ ceu_write(ceudev, CEU_CDWDR, cdwdr);
+
+ return 0;
+}
+
+/*
+ * ceu_capture() - Trigger start of a capture sequence.
+ *
+ * Program the CEU DMA registers with addresses where to transfer image data.
+ */
+static int ceu_capture(struct ceu_device *ceudev)
+{
+ struct v4l2_pix_format_mplane *pix = &ceudev->v4l2_pix;
+ dma_addr_t phys_addr_top;
+
+ phys_addr_top =
+ vb2_dma_contig_plane_dma_addr(&ceudev->active->vb2_buf, 0);
+ ceu_write(ceudev, CEU_CDAYR, phys_addr_top);
+
+ /* Ignore CbCr plane for non multi-planar image formats. */
+ if (ceu_fmt_mplane(pix)) {
+ phys_addr_top =
+ vb2_dma_contig_plane_dma_addr(&ceudev->active->vb2_buf,
+ 1);
+ ceu_write(ceudev, CEU_CDACR, phys_addr_top);
+ }
+
+ /*
+ * Trigger new capture start: once for each frame, as we work in
+ * one-frame capture mode.
+ */
+ ceu_write(ceudev, CEU_CAPSR, CEU_CAPSR_CE);
+
+ return 0;
+}
+
+static irqreturn_t ceu_irq(int irq, void *data)
+{
+ struct ceu_device *ceudev = data;
+ struct vb2_v4l2_buffer *vbuf;
+ struct ceu_buffer *buf;
+ u32 status;
+
+ /* Clean interrupt status. */
+ status = ceu_read(ceudev, CEU_CETCR);
+ ceu_write(ceudev, CEU_CETCR, ~ceudev->irq_mask);
+
+ /* Unexpected interrupt. */
+ if (!(status & CEU_CEIER_MASK))
+ return IRQ_NONE;
+
+ spin_lock(&ceudev->lock);
+
+ /* Stale interrupt from a released buffer, ignore it. */
+ vbuf = ceudev->active;
+ if (!vbuf) {
+ spin_unlock(&ceudev->lock);
+ return IRQ_HANDLED;
+ }
+
+ /*
+ * When a VBP interrupt occurs, no capture end interrupt will occur
+ * and the image of that frame is not captured correctly.
+ */
+ if (status & CEU_CEIER_VBP) {
+ dev_err(ceudev->dev, "VBP interrupt: abort capture\n");
+ goto error_irq_out;
+ }
+
+ /* Prepare to return the 'previous' buffer. */
+ vbuf->vb2_buf.timestamp = ktime_get_ns();
+ vbuf->sequence = ceudev->sequence++;
+ vbuf->field = ceudev->field;
+
+ /* Prepare a new 'active' buffer and trigger a new capture. */
+ if (!list_empty(&ceudev->capture)) {
+ buf = list_first_entry(&ceudev->capture, struct ceu_buffer,
+ queue);
+ list_del(&buf->queue);
+ ceudev->active = &buf->vb;
+
+ ceu_capture(ceudev);
+ }
+
+ /* Return the 'previous' buffer. */
+ vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE);
+
+ spin_unlock(&ceudev->lock);
+
+ return IRQ_HANDLED;
+
+error_irq_out:
+ /* Return the 'previous' buffer and all queued ones. */
+ vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_ERROR);
+
+ list_for_each_entry(buf, &ceudev->capture, queue)
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+
+ spin_unlock(&ceudev->lock);
+
+ return IRQ_HANDLED;
+}
+
+/* --- CEU Videobuf2 operations --- */
+
+static void ceu_update_plane_sizes(struct v4l2_plane_pix_format *plane,
+ unsigned int bpl, unsigned int szimage)
+{
+ memset(plane, 0, sizeof(*plane));
+
+ plane->sizeimage = szimage;
+ if (plane->bytesperline < bpl || plane->bytesperline > CEU_MAX_BPL)
+ plane->bytesperline = bpl;
+}
+
+/*
+ * ceu_calc_plane_sizes() - Fill per-plane 'struct v4l2_plane_pix_format'
+ * information according to the currently configured
+ * pixel format.
+ * @ceu_device: CEU device.
+ * @ceu_fmt: Active image format.
+ * @pix: Pixel format information (store line width and image sizes)
+ */
+static void ceu_calc_plane_sizes(struct ceu_device *ceudev,
+ const struct ceu_fmt *ceu_fmt,
+ struct v4l2_pix_format_mplane *pix)
+{
+ unsigned int bpl, szimage;
+
+ switch (pix->pixelformat) {
+ case V4L2_PIX_FMT_YUYV:
+ pix->num_planes = 1;
+ bpl = pix->width * ceu_fmt->bpp / 8;
+ szimage = pix->height * bpl;
+ ceu_update_plane_sizes(&pix->plane_fmt[0], bpl, szimage);
+ break;
+
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ pix->num_planes = 2;
+ bpl = pix->width;
+ szimage = pix->height * pix->width;
+ ceu_update_plane_sizes(&pix->plane_fmt[0], bpl, szimage);
+ ceu_update_plane_sizes(&pix->plane_fmt[1], bpl, szimage / 2);
+ break;
+
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ default:
+ pix->num_planes = 2;
+ bpl = pix->width;
+ szimage = pix->height * pix->width;
+ ceu_update_plane_sizes(&pix->plane_fmt[0], bpl, szimage);
+ ceu_update_plane_sizes(&pix->plane_fmt[1], bpl, szimage);
+ break;
+ }
+}
+
+/*
+ * ceu_vb2_setup() - is called to check whether the driver can accept the
+ * requested number of buffers and to fill in plane sizes
+ * for the current frame format, if required.
+ */
+static int ceu_vb2_setup(struct vb2_queue *vq, unsigned int *count,
+ unsigned int *num_planes, unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct ceu_device *ceudev = vb2_get_drv_priv(vq);
+ struct v4l2_pix_format_mplane *pix = &ceudev->v4l2_pix;
+ unsigned int i;
+
+ /* num_planes is set: just check plane sizes. */
+ if (*num_planes) {
+ for (i = 0; i < pix->num_planes; i++)
+ if (sizes[i] < pix->plane_fmt[i].sizeimage)
+ return -EINVAL;
+
+ return 0;
+ }
+
+ /* num_planes not set: called from REQBUFS, just set plane sizes. */
+ *num_planes = pix->num_planes;
+ for (i = 0; i < pix->num_planes; i++)
+ sizes[i] = pix->plane_fmt[i].sizeimage;
+
+ return 0;
+}
+
+static void ceu_vb2_queue(struct vb2_buffer *vb)
+{
+ struct ceu_device *ceudev = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct ceu_buffer *buf = vb2_to_ceu(vbuf);
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&ceudev->lock, irqflags);
+ list_add_tail(&buf->queue, &ceudev->capture);
+ spin_unlock_irqrestore(&ceudev->lock, irqflags);
+}
+
+static int ceu_vb2_prepare(struct vb2_buffer *vb)
+{
+ struct ceu_device *ceudev = vb2_get_drv_priv(vb->vb2_queue);
+ struct v4l2_pix_format_mplane *pix = &ceudev->v4l2_pix;
+ unsigned int i;
+
+ for (i = 0; i < pix->num_planes; i++) {
+ if (vb2_plane_size(vb, i) < pix->plane_fmt[i].sizeimage) {
+ dev_err(ceudev->dev,
+ "Plane size too small (%lu < %u)\n",
+ vb2_plane_size(vb, i),
+ pix->plane_fmt[i].sizeimage);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, i, pix->plane_fmt[i].sizeimage);
+ }
+
+ return 0;
+}
+
+static int ceu_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct ceu_device *ceudev = vb2_get_drv_priv(vq);
+ struct v4l2_subdev *v4l2_sd = ceudev->sd->v4l2_sd;
+ struct ceu_buffer *buf;
+ unsigned long irqflags;
+ int ret;
+
+ /* Program the CEU interface according to the CEU image format. */
+ ret = ceu_hw_config(ceudev);
+ if (ret)
+ goto error_return_bufs;
+
+ ret = v4l2_subdev_call(v4l2_sd, video, s_stream, 1);
+ if (ret && ret != -ENOIOCTLCMD) {
+ dev_dbg(ceudev->dev,
+ "Subdevice failed to start streaming: %d\n", ret);
+ goto error_return_bufs;
+ }
+
+ spin_lock_irqsave(&ceudev->lock, irqflags);
+ ceudev->sequence = 0;
+
+ /* Grab the first available buffer and trigger the first capture. */
+ buf = list_first_entry(&ceudev->capture, struct ceu_buffer,
+ queue);
+ if (!buf) {
+ spin_unlock_irqrestore(&ceudev->lock, irqflags);
+ dev_dbg(ceudev->dev,
+ "No buffer available for capture.\n");
+ goto error_stop_sensor;
+ }
+
+ list_del(&buf->queue);
+ ceudev->active = &buf->vb;
+
+ /* Clean and program interrupts for first capture. */
+ ceu_write(ceudev, CEU_CETCR, ~ceudev->irq_mask);
+ ceu_write(ceudev, CEU_CEIER, CEU_CEIER_MASK);
+
+ ceu_capture(ceudev);
+
+ spin_unlock_irqrestore(&ceudev->lock, irqflags);
+
+ return 0;
+
+error_stop_sensor:
+ v4l2_subdev_call(v4l2_sd, video, s_stream, 0);
+
+error_return_bufs:
+ spin_lock_irqsave(&ceudev->lock, irqflags);
+ list_for_each_entry(buf, &ceudev->capture, queue)
+ vb2_buffer_done(&ceudev->active->vb2_buf,
+ VB2_BUF_STATE_QUEUED);
+ ceudev->active = NULL;
+ spin_unlock_irqrestore(&ceudev->lock, irqflags);
+
+ return ret;
+}
+
+static void ceu_stop_streaming(struct vb2_queue *vq)
+{
+ struct ceu_device *ceudev = vb2_get_drv_priv(vq);
+ struct v4l2_subdev *v4l2_sd = ceudev->sd->v4l2_sd;
+ struct ceu_buffer *buf;
+ unsigned long irqflags;
+
+ /* Clean and disable interrupt sources. */
+ ceu_write(ceudev, CEU_CETCR,
+ ceu_read(ceudev, CEU_CETCR) & ceudev->irq_mask);
+ ceu_write(ceudev, CEU_CEIER, CEU_CEIER_MASK);
+
+ v4l2_subdev_call(v4l2_sd, video, s_stream, 0);
+
+ spin_lock_irqsave(&ceudev->lock, irqflags);
+ if (ceudev->active) {
+ vb2_buffer_done(&ceudev->active->vb2_buf,
+ VB2_BUF_STATE_ERROR);
+ ceudev->active = NULL;
+ }
+
+ /* Release all queued buffers. */
+ list_for_each_entry(buf, &ceudev->capture, queue)
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ INIT_LIST_HEAD(&ceudev->capture);
+
+ spin_unlock_irqrestore(&ceudev->lock, irqflags);
+
+ ceu_soft_reset(ceudev);
+}
+
+static const struct vb2_ops ceu_vb2_ops = {
+ .queue_setup = ceu_vb2_setup,
+ .buf_queue = ceu_vb2_queue,
+ .buf_prepare = ceu_vb2_prepare,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .start_streaming = ceu_start_streaming,
+ .stop_streaming = ceu_stop_streaming,
+};
+
+/* --- CEU image formats handling --- */
+
+/*
+ * ceu_try_fmt() - test format on CEU and sensor
+ * @ceudev: The CEU device.
+ * @v4l2_fmt: format to test.
+ *
+ * Returns 0 for success, < 0 for errors.
+ */
+static int ceu_try_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt)
+{
+ struct ceu_subdev *ceu_sd = ceudev->sd;
+ struct v4l2_pix_format_mplane *pix = &v4l2_fmt->fmt.pix_mp;
+ struct v4l2_subdev *v4l2_sd = ceu_sd->v4l2_sd;
+ struct v4l2_subdev_pad_config pad_cfg;
+ const struct ceu_fmt *ceu_fmt;
+ int ret;
+
+ struct v4l2_subdev_format sd_format = {
+ .which = V4L2_SUBDEV_FORMAT_TRY,
+ };
+
+ switch (pix->pixelformat) {
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ break;
+
+ default:
+ pix->pixelformat = V4L2_PIX_FMT_NV16;
+ break;
+ }
+
+ ceu_fmt = get_ceu_fmt_from_fourcc(pix->pixelformat);
+
+ /* CFSZR requires height and width to be 4-pixel aligned. */
+ v4l_bound_align_image(&pix->width, 2, CEU_MAX_WIDTH, 4,
+ &pix->height, 4, CEU_MAX_HEIGHT, 4, 0);
+
+ /*
+ * Set format on sensor sub device: bus format used to produce memory
+ * format is selected at initialization time.
+ */
+ v4l2_fill_mbus_format_mplane(&sd_format.format, pix);
+ ret = v4l2_subdev_call(v4l2_sd, pad, set_fmt, &pad_cfg, &sd_format);
+ if (ret)
+ return ret;
+
+ /* Apply size returned by sensor as the CEU can't scale. */
+ v4l2_fill_pix_format_mplane(pix, &sd_format.format);
+
+ /* Calculate per-plane sizes based on image format. */
+ ceu_calc_plane_sizes(ceudev, ceu_fmt, pix);
+
+ return 0;
+}
+
+/*
+ * ceu_set_fmt() - Apply the supplied format to both sensor and CEU
+ */
+static int ceu_set_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt)
+{
+ struct ceu_subdev *ceu_sd = ceudev->sd;
+ struct v4l2_subdev *v4l2_sd = ceu_sd->v4l2_sd;
+ int ret;
+
+ struct v4l2_subdev_format format = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+
+ ret = ceu_try_fmt(ceudev, v4l2_fmt);
+ if (ret)
+ return ret;
+
+ v4l2_fill_mbus_format_mplane(&format.format, &v4l2_fmt->fmt.pix_mp);
+ ret = v4l2_subdev_call(v4l2_sd, pad, set_fmt, NULL, &format);
+ if (ret)
+ return ret;
+
+ ceudev->v4l2_pix = v4l2_fmt->fmt.pix_mp;
+ ceudev->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+/*
+ * ceu_set_default_fmt() - Apply default NV16 memory output format with VGA
+ * sizes.
+ */
+static int ceu_set_default_fmt(struct ceu_device *ceudev)
+{
+ int ret;
+
+ struct v4l2_format v4l2_fmt = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ .fmt.pix_mp = {
+ .width = VGA_WIDTH,
+ .height = VGA_HEIGHT,
+ .field = V4L2_FIELD_NONE,
+ .pixelformat = V4L2_PIX_FMT_NV16,
+ .num_planes = 2,
+ .plane_fmt = {
+ [0] = {
+ .sizeimage = VGA_WIDTH * VGA_HEIGHT * 2,
+ .bytesperline = VGA_WIDTH * 2,
+ },
+ [1] = {
+ .sizeimage = VGA_WIDTH * VGA_HEIGHT * 2,
+ .bytesperline = VGA_WIDTH * 2,
+ },
+ },
+ },
+ };
+
+ ret = ceu_try_fmt(ceudev, &v4l2_fmt);
+ if (ret)
+ return ret;
+
+ ceudev->v4l2_pix = v4l2_fmt.fmt.pix_mp;
+ ceudev->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+/*
+ * ceu_init_mbus_fmt() - Query sensor for supported formats and initialize
+ * CEU media bus format used to produce memory formats.
+ *
+ * Find out if sensor can produce a permutation of 8-bits YUYV bus format.
+ * From a single 8-bits YUYV bus format the CEU can produce several memory
+ * output formats:
+ * - NV[12|21|16|61] through image fetch mode;
+ * - YUYV422 if sensor provides YUYV422
+ *
+ * TODO: Other YUYV422 permutations through data fetch sync mode and DTARY
+ * TODO: Binary data (eg. JPEG) and raw formats through data fetch sync mode
+ */
+static int ceu_init_mbus_fmt(struct ceu_device *ceudev)
+{
+ struct ceu_subdev *ceu_sd = ceudev->sd;
+ struct ceu_mbus_fmt *mbus_fmt = &ceu_sd->mbus_fmt;
+ struct v4l2_subdev *v4l2_sd = ceu_sd->v4l2_sd;
+ bool yuyv_bus_fmt = false;
+
+ struct v4l2_subdev_mbus_code_enum sd_mbus_fmt = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ .index = 0,
+ };
+
+ /* Find out if sensor can produce any permutation of 8-bits YUYV422. */
+ while (!yuyv_bus_fmt &&
+ !v4l2_subdev_call(v4l2_sd, pad, enum_mbus_code,
+ NULL, &sd_mbus_fmt)) {
+ switch (sd_mbus_fmt.code) {
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_YVYU8_2X8:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_VYUY8_2X8:
+ yuyv_bus_fmt = true;
+ break;
+ default:
+ /*
+ * Only support 8-bits YUYV bus formats at the moment;
+ *
+ * TODO: add support for binary formats (data sync
+ * fetch mode).
+ */
+ break;
+ }
+
+ sd_mbus_fmt.index++;
+ }
+
+ if (!yuyv_bus_fmt)
+ return -ENXIO;
+
+ /*
+ * Save the first encountered YUYV format as "mbus_fmt" and use it
+ * to output all planar YUV422 and YUV420 (NV*) formats to memory as
+ * well as for data synch fetch mode (YUYV - YVYU etc. ).
+ */
+ mbus_fmt->mbus_code = sd_mbus_fmt.code;
+ mbus_fmt->bps = 8;
+
+ /* Annotate the selected bus format components ordering. */
+ switch (sd_mbus_fmt.code) {
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ mbus_fmt->fmt_order = CEU_CAMCR_DTARY_8_YUYV;
+ mbus_fmt->fmt_order_swap = CEU_CAMCR_DTARY_8_YVYU;
+ mbus_fmt->swapped = false;
+ mbus_fmt->bpp = 16;
+ break;
+
+ case MEDIA_BUS_FMT_YVYU8_2X8:
+ mbus_fmt->fmt_order = CEU_CAMCR_DTARY_8_YVYU;
+ mbus_fmt->fmt_order_swap = CEU_CAMCR_DTARY_8_YUYV;
+ mbus_fmt->swapped = true;
+ mbus_fmt->bpp = 16;
+ break;
+
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ mbus_fmt->fmt_order = CEU_CAMCR_DTARY_8_UYVY;
+ mbus_fmt->fmt_order_swap = CEU_CAMCR_DTARY_8_VYUY;
+ mbus_fmt->swapped = false;
+ mbus_fmt->bpp = 16;
+ break;
+
+ case MEDIA_BUS_FMT_VYUY8_2X8:
+ mbus_fmt->fmt_order = CEU_CAMCR_DTARY_8_VYUY;
+ mbus_fmt->fmt_order_swap = CEU_CAMCR_DTARY_8_UYVY;
+ mbus_fmt->swapped = true;
+ mbus_fmt->bpp = 16;
+ break;
+ }
+
+ return 0;
+}
+
+/* --- Runtime PM Handlers --- */
+
+/*
+ * ceu_runtime_resume() - soft-reset the interface and turn sensor power on.
+ */
+static int __maybe_unused ceu_runtime_resume(struct device *dev)
+{
+ struct ceu_device *ceudev = dev_get_drvdata(dev);
+ struct v4l2_subdev *v4l2_sd = ceudev->sd->v4l2_sd;
+
+ v4l2_subdev_call(v4l2_sd, core, s_power, 1);
+
+ ceu_soft_reset(ceudev);
+
+ return 0;
+}
+
+/*
+ * ceu_runtime_suspend() - disable capture and interrupts and soft-reset.
+ * Turn sensor power off.
+ */
+static int __maybe_unused ceu_runtime_suspend(struct device *dev)
+{
+ struct ceu_device *ceudev = dev_get_drvdata(dev);
+ struct v4l2_subdev *v4l2_sd = ceudev->sd->v4l2_sd;
+
+ v4l2_subdev_call(v4l2_sd, core, s_power, 0);
+
+ ceu_write(ceudev, CEU_CEIER, 0);
+ ceu_soft_reset(ceudev);
+
+ return 0;
+}
+
+/* --- File Operations --- */
+
+static int ceu_open(struct file *file)
+{
+ struct ceu_device *ceudev = video_drvdata(file);
+ int ret;
+
+ ret = v4l2_fh_open(file);
+ if (ret)
+ return ret;
+
+ mutex_lock(&ceudev->mlock);
+ /* Causes soft-reset and sensor power on on first open */
+ pm_runtime_get_sync(ceudev->dev);
+ mutex_unlock(&ceudev->mlock);
+
+ return 0;
+}
+
+static int ceu_release(struct file *file)
+{
+ struct ceu_device *ceudev = video_drvdata(file);
+
+ vb2_fop_release(file);
+
+ mutex_lock(&ceudev->mlock);
+ /* Causes soft-reset and sensor power down on last close */
+ pm_runtime_put(ceudev->dev);
+ mutex_unlock(&ceudev->mlock);
+
+ return 0;
+}
+
+static const struct v4l2_file_operations ceu_fops = {
+ .owner = THIS_MODULE,
+ .open = ceu_open,
+ .release = ceu_release,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vb2_fop_mmap,
+ .poll = vb2_fop_poll,
+};
+
+/* --- Video Device IOCTLs --- */
+
+static int ceu_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct ceu_device *ceudev = video_drvdata(file);
+
+ strlcpy(cap->card, "Renesas CEU", sizeof(cap->card));
+ strlcpy(cap->driver, DRIVER_NAME, sizeof(cap->driver));
+ snprintf(cap->bus_info, sizeof(cap->bus_info),
+ "platform:renesas-ceu-%s", dev_name(ceudev->dev));
+
+ return 0;
+}
+
+static int ceu_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ const struct ceu_fmt *fmt;
+
+ if (f->index >= ARRAY_SIZE(ceu_fmt_list))
+ return -EINVAL;
+
+ fmt = &ceu_fmt_list[f->index];
+ f->pixelformat = fmt->fourcc;
+
+ return 0;
+}
+
+static int ceu_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct ceu_device *ceudev = video_drvdata(file);
+
+ return ceu_try_fmt(ceudev, f);
+}
+
+static int ceu_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct ceu_device *ceudev = video_drvdata(file);
+
+ if (vb2_is_streaming(&ceudev->vb2_vq))
+ return -EBUSY;
+
+ return ceu_set_fmt(ceudev, f);
+}
+
+static int ceu_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct ceu_device *ceudev = video_drvdata(file);
+
+ f->fmt.pix_mp = ceudev->v4l2_pix;
+
+ return 0;
+}
+
+static int ceu_enum_input(struct file *file, void *priv,
+ struct v4l2_input *inp)
+{
+ struct ceu_device *ceudev = video_drvdata(file);
+ struct ceu_subdev *ceusd;
+
+ if (inp->index >= ceudev->num_sd)
+ return -EINVAL;
+
+ ceusd = &ceudev->subdevs[inp->index];
+
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+ inp->std = 0;
+ snprintf(inp->name, sizeof(inp->name), "Camera%u: %s",
+ inp->index, ceusd->v4l2_sd->name);
+
+ return 0;
+}
+
+static int ceu_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct ceu_device *ceudev = video_drvdata(file);
+
+ *i = ceudev->sd_index;
+
+ return 0;
+}
+
+static int ceu_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct ceu_device *ceudev = video_drvdata(file);
+ struct ceu_subdev *ceu_sd_old;
+ int ret;
+
+ if (i >= ceudev->num_sd)
+ return -EINVAL;
+
+ if (vb2_is_streaming(&ceudev->vb2_vq))
+ return -EBUSY;
+
+ if (i == ceudev->sd_index)
+ return 0;
+
+ ceu_sd_old = ceudev->sd;
+ ceudev->sd = &ceudev->subdevs[i];
+
+ /*
+ * Make sure we can generate output image formats and apply
+ * default one.
+ */
+ ret = ceu_init_mbus_fmt(ceudev);
+ if (ret) {
+ ceudev->sd = ceu_sd_old;
+ return -EINVAL;
+ }
+
+ ret = ceu_set_default_fmt(ceudev);
+ if (ret) {
+ ceudev->sd = ceu_sd_old;
+ return -EINVAL;
+ }
+
+ /* Now that we're sure we can use the sensor, power off the old one. */
+ v4l2_subdev_call(ceu_sd_old->v4l2_sd, core, s_power, 0);
+ v4l2_subdev_call(ceudev->sd->v4l2_sd, core, s_power, 1);
+
+ ceudev->sd_index = i;
+
+ return 0;
+}
+
+static int ceu_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+ struct ceu_device *ceudev = video_drvdata(file);
+
+ return v4l2_g_parm_cap(video_devdata(file), ceudev->sd->v4l2_sd, a);
+}
+
+static int ceu_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+ struct ceu_device *ceudev = video_drvdata(file);
+
+ return v4l2_s_parm_cap(video_devdata(file), ceudev->sd->v4l2_sd, a);
+}
+
+static int ceu_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct ceu_device *ceudev = video_drvdata(file);
+ struct ceu_subdev *ceu_sd = ceudev->sd;
+ const struct ceu_fmt *ceu_fmt;
+ struct v4l2_subdev *v4l2_sd = ceu_sd->v4l2_sd;
+ int ret;
+
+ struct v4l2_subdev_frame_size_enum fse = {
+ .code = ceu_sd->mbus_fmt.mbus_code,
+ .index = fsize->index,
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+
+ /* Just check if user supplied pixel format is supported. */
+ ceu_fmt = get_ceu_fmt_from_fourcc(fsize->pixel_format);
+ if (!ceu_fmt)
+ return -EINVAL;
+
+ ret = v4l2_subdev_call(v4l2_sd, pad, enum_frame_size,
+ NULL, &fse);
+ if (ret)
+ return ret;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = CEU_W_MAX(fse.max_width);
+ fsize->discrete.height = CEU_H_MAX(fse.max_height);
+
+ return 0;
+}
+
+static int ceu_enum_frameintervals(struct file *file, void *fh,
+ struct v4l2_frmivalenum *fival)
+{
+ struct ceu_device *ceudev = video_drvdata(file);
+ struct ceu_subdev *ceu_sd = ceudev->sd;
+ const struct ceu_fmt *ceu_fmt;
+ struct v4l2_subdev *v4l2_sd = ceu_sd->v4l2_sd;
+ int ret;
+
+ struct v4l2_subdev_frame_interval_enum fie = {
+ .code = ceu_sd->mbus_fmt.mbus_code,
+ .index = fival->index,
+ .width = fival->width,
+ .height = fival->height,
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+
+ /* Just check if user supplied pixel format is supported. */
+ ceu_fmt = get_ceu_fmt_from_fourcc(fival->pixel_format);
+ if (!ceu_fmt)
+ return -EINVAL;
+
+ ret = v4l2_subdev_call(v4l2_sd, pad, enum_frame_interval, NULL,
+ &fie);
+ if (ret)
+ return ret;
+
+ fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ fival->discrete = fie.interval;
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops ceu_ioctl_ops = {
+ .vidioc_querycap = ceu_querycap,
+
+ .vidioc_enum_fmt_vid_cap_mplane = ceu_enum_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap_mplane = ceu_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap_mplane = ceu_s_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap_mplane = ceu_g_fmt_vid_cap,
+
+ .vidioc_enum_input = ceu_enum_input,
+ .vidioc_g_input = ceu_g_input,
+ .vidioc_s_input = ceu_s_input,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+
+ .vidioc_g_parm = ceu_g_parm,
+ .vidioc_s_parm = ceu_s_parm,
+ .vidioc_enum_framesizes = ceu_enum_framesizes,
+ .vidioc_enum_frameintervals = ceu_enum_frameintervals,
+
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+/*
+ * ceu_vdev_release() - release CEU video device memory when last reference
+ * to this driver is closed
+ */
+static void ceu_vdev_release(struct video_device *vdev)
+{
+ struct ceu_device *ceudev = video_get_drvdata(vdev);
+
+ kfree(ceudev);
+}
+
+static int ceu_notify_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *v4l2_sd,
+ struct v4l2_async_subdev *asd)
+{
+ struct v4l2_device *v4l2_dev = notifier->v4l2_dev;
+ struct ceu_device *ceudev = v4l2_to_ceu(v4l2_dev);
+ struct ceu_subdev *ceu_sd = to_ceu_subdev(asd);
+
+ ceu_sd->v4l2_sd = v4l2_sd;
+ ceudev->num_sd++;
+
+ return 0;
+}
+
+static int ceu_notify_complete(struct v4l2_async_notifier *notifier)
+{
+ struct v4l2_device *v4l2_dev = notifier->v4l2_dev;
+ struct ceu_device *ceudev = v4l2_to_ceu(v4l2_dev);
+ struct video_device *vdev = &ceudev->vdev;
+ struct vb2_queue *q = &ceudev->vb2_vq;
+ struct v4l2_subdev *v4l2_sd;
+ int ret;
+
+ /* Initialize vb2 queue. */
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ q->io_modes = VB2_MMAP | VB2_DMABUF;
+ q->drv_priv = ceudev;
+ q->ops = &ceu_vb2_ops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct ceu_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->min_buffers_needed = 2;
+ q->lock = &ceudev->mlock;
+ q->dev = ceudev->v4l2_dev.dev;
+
+ ret = vb2_queue_init(q);
+ if (ret)
+ return ret;
+
+ /*
+ * Make sure at least one sensor is primary and use it to initialize
+ * ceu formats.
+ */
+ if (!ceudev->sd) {
+ ceudev->sd = &ceudev->subdevs[0];
+ ceudev->sd_index = 0;
+ }
+
+ v4l2_sd = ceudev->sd->v4l2_sd;
+
+ ret = ceu_init_mbus_fmt(ceudev);
+ if (ret)
+ return ret;
+
+ ret = ceu_set_default_fmt(ceudev);
+ if (ret)
+ return ret;
+
+ /* Register the video device. */
+ strncpy(vdev->name, DRIVER_NAME, strlen(DRIVER_NAME));
+ vdev->v4l2_dev = v4l2_dev;
+ vdev->lock = &ceudev->mlock;
+ vdev->queue = &ceudev->vb2_vq;
+ vdev->ctrl_handler = v4l2_sd->ctrl_handler;
+ vdev->fops = &ceu_fops;
+ vdev->ioctl_ops = &ceu_ioctl_ops;
+ vdev->release = ceu_vdev_release;
+ vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
+ V4L2_CAP_STREAMING;
+ video_set_drvdata(vdev, ceudev);
+
+ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ v4l2_err(vdev->v4l2_dev,
+ "video_register_device failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_async_notifier_operations ceu_notify_ops = {
+ .bound = ceu_notify_bound,
+ .complete = ceu_notify_complete,
+};
+
+/*
+ * ceu_init_async_subdevs() - Initialize CEU subdevices and async_subdevs in
+ * ceu device. Both DT and platform data parsing use
+ * this routine.
+ *
+ * Returns 0 for success, -ENOMEM for failure.
+ */
+static int ceu_init_async_subdevs(struct ceu_device *ceudev, unsigned int n_sd)
+{
+ /* Reserve memory for 'n_sd' ceu_subdev descriptors. */
+ ceudev->subdevs = devm_kcalloc(ceudev->dev, n_sd,
+ sizeof(*ceudev->subdevs), GFP_KERNEL);
+ if (!ceudev->subdevs)
+ return -ENOMEM;
+
+ /*
+ * Reserve memory for 'n_sd' pointers to async_subdevices.
+ * ceudev->asds members will point to &ceu_subdev.asd
+ */
+ ceudev->asds = devm_kcalloc(ceudev->dev, n_sd,
+ sizeof(*ceudev->asds), GFP_KERNEL);
+ if (!ceudev->asds)
+ return -ENOMEM;
+
+ ceudev->sd = NULL;
+ ceudev->sd_index = 0;
+ ceudev->num_sd = 0;
+
+ return 0;
+}
+
+/*
+ * ceu_parse_platform_data() - Initialize async_subdevices using platform
+ * device provided data.
+ */
+static int ceu_parse_platform_data(struct ceu_device *ceudev,
+ const struct ceu_platform_data *pdata)
+{
+ const struct ceu_async_subdev *async_sd;
+ struct ceu_subdev *ceu_sd;
+ unsigned int i;
+ int ret;
+
+ if (pdata->num_subdevs == 0)
+ return -ENODEV;
+
+ ret = ceu_init_async_subdevs(ceudev, pdata->num_subdevs);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < pdata->num_subdevs; i++) {
+ /* Setup the ceu subdevice and the async subdevice. */
+ async_sd = &pdata->subdevs[i];
+ ceu_sd = &ceudev->subdevs[i];
+
+ INIT_LIST_HEAD(&ceu_sd->asd.list);
+
+ ceu_sd->mbus_flags = async_sd->flags;
+ ceu_sd->asd.match_type = V4L2_ASYNC_MATCH_I2C;
+ ceu_sd->asd.match.i2c.adapter_id = async_sd->i2c_adapter_id;
+ ceu_sd->asd.match.i2c.address = async_sd->i2c_address;
+
+ ceudev->asds[i] = &ceu_sd->asd;
+ }
+
+ return pdata->num_subdevs;
+}
+
+/*
+ * ceu_parse_dt() - Initialize async_subdevs parsing device tree graph.
+ */
+static int ceu_parse_dt(struct ceu_device *ceudev)
+{
+ struct device_node *of = ceudev->dev->of_node;
+ struct v4l2_fwnode_endpoint fw_ep;
+ struct ceu_subdev *ceu_sd;
+ struct device_node *ep;
+ unsigned int i;
+ int num_ep;
+ int ret;
+
+ num_ep = of_graph_get_endpoint_count(of);
+ if (!num_ep)
+ return -ENODEV;
+
+ ret = ceu_init_async_subdevs(ceudev, num_ep);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < num_ep; i++) {
+ ep = of_graph_get_endpoint_by_regs(of, 0, i);
+ if (!ep) {
+ dev_err(ceudev->dev,
+ "No subdevice connected on endpoint %u.\n", i);
+ ret = -ENODEV;
+ goto error_put_node;
+ }
+
+ ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &fw_ep);
+ if (ret) {
+ dev_err(ceudev->dev,
+ "Unable to parse endpoint #%u.\n", i);
+ goto error_put_node;
+ }
+
+ if (fw_ep.bus_type != V4L2_MBUS_PARALLEL) {
+ dev_err(ceudev->dev,
+ "Only parallel input supported.\n");
+ ret = -EINVAL;
+ goto error_put_node;
+ }
+
+ /* Setup the ceu subdevice and the async subdevice. */
+ ceu_sd = &ceudev->subdevs[i];
+ INIT_LIST_HEAD(&ceu_sd->asd.list);
+
+ ceu_sd->mbus_flags = fw_ep.bus.parallel.flags;
+ ceu_sd->asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
+ ceu_sd->asd.match.fwnode =
+ fwnode_graph_get_remote_port_parent(
+ of_fwnode_handle(ep));
+
+ ceudev->asds[i] = &ceu_sd->asd;
+ of_node_put(ep);
+ }
+
+ return num_ep;
+
+error_put_node:
+ of_node_put(ep);
+ return ret;
+}
+
+/*
+ * struct ceu_data - Platform specific CEU data
+ * @irq_mask: CETCR mask with all interrupt sources enabled. The mask differs
+ * between SH4 and RZ platforms.
+ */
+struct ceu_data {
+ u32 irq_mask;
+};
+
+static const struct ceu_data ceu_data_rz = {
+ .irq_mask = CEU_CETCR_ALL_IRQS_RZ,
+};
+
+static const struct ceu_data ceu_data_sh4 = {
+ .irq_mask = CEU_CETCR_ALL_IRQS_SH4,
+};
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id ceu_of_match[] = {
+ { .compatible = "renesas,r7s72100-ceu", .data = &ceu_data_rz },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ceu_of_match);
+#endif
+
+static int ceu_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct ceu_data *ceu_data;
+ struct ceu_device *ceudev;
+ struct resource *res;
+ unsigned int irq;
+ int num_subdevs;
+ int ret;
+
+ ceudev = kzalloc(sizeof(*ceudev), GFP_KERNEL);
+ if (!ceudev)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, ceudev);
+ ceudev->dev = dev;
+
+ INIT_LIST_HEAD(&ceudev->capture);
+ spin_lock_init(&ceudev->lock);
+ mutex_init(&ceudev->mlock);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ceudev->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ceudev->base)) {
+ ret = PTR_ERR(ceudev->base);
+ goto error_free_ceudev;
+ }
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0) {
+ dev_err(dev, "Failed to get irq: %d\n", ret);
+ goto error_free_ceudev;
+ }
+ irq = ret;
+
+ ret = devm_request_irq(dev, irq, ceu_irq,
+ 0, dev_name(dev), ceudev);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to request CEU interrupt.\n");
+ goto error_free_ceudev;
+ }
+
+ pm_runtime_enable(dev);
+
+ ret = v4l2_device_register(dev, &ceudev->v4l2_dev);
+ if (ret)
+ goto error_pm_disable;
+
+ if (IS_ENABLED(CONFIG_OF) && dev->of_node) {
+ ceu_data = of_match_device(ceu_of_match, dev)->data;
+ num_subdevs = ceu_parse_dt(ceudev);
+ } else if (dev->platform_data) {
+ /* Assume SH4 if booting with platform data. */
+ ceu_data = &ceu_data_sh4;
+ num_subdevs = ceu_parse_platform_data(ceudev,
+ dev->platform_data);
+ } else {
+ num_subdevs = -EINVAL;
+ }
+
+ if (num_subdevs < 0) {
+ ret = num_subdevs;
+ goto error_v4l2_unregister;
+ }
+ ceudev->irq_mask = ceu_data->irq_mask;
+
+ ceudev->notifier.v4l2_dev = &ceudev->v4l2_dev;
+ ceudev->notifier.subdevs = ceudev->asds;
+ ceudev->notifier.num_subdevs = num_subdevs;
+ ceudev->notifier.ops = &ceu_notify_ops;
+ ret = v4l2_async_notifier_register(&ceudev->v4l2_dev,
+ &ceudev->notifier);
+ if (ret)
+ goto error_v4l2_unregister;
+
+ dev_info(dev, "Renesas Capture Engine Unit %s\n", dev_name(dev));
+
+ return 0;
+
+error_v4l2_unregister:
+ v4l2_device_unregister(&ceudev->v4l2_dev);
+error_pm_disable:
+ pm_runtime_disable(dev);
+error_free_ceudev:
+ kfree(ceudev);
+
+ return ret;
+}
+
+static int ceu_remove(struct platform_device *pdev)
+{
+ struct ceu_device *ceudev = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(ceudev->dev);
+
+ v4l2_async_notifier_unregister(&ceudev->notifier);
+
+ v4l2_device_unregister(&ceudev->v4l2_dev);
+
+ video_unregister_device(&ceudev->vdev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops ceu_pm_ops = {
+ SET_RUNTIME_PM_OPS(ceu_runtime_suspend,
+ ceu_runtime_resume,
+ NULL)
+};
+
+static struct platform_driver ceu_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .pm = &ceu_pm_ops,
+ .of_match_table = of_match_ptr(ceu_of_match),
+ },
+ .probe = ceu_probe,
+ .remove = ceu_remove,
+};
+
+module_platform_driver(ceu_driver);
+
+MODULE_DESCRIPTION("Renesas CEU camera driver");
+MODULE_AUTHOR("Jacopo Mondi <jacopo+renesas@jmondi.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c
index 49cacc7a48d1..fa1ba98c96dc 100644
--- a/drivers/media/platform/rockchip/rga/rga-buf.c
+++ b/drivers/media/platform/rockchip/rga/rga-buf.c
@@ -140,7 +140,8 @@ void rga_buf_map(struct vb2_buffer *vb)
address = sg_phys(sgl);
for (p = 0; p < len; p++) {
- dma_addr_t phys = address + (p << PAGE_SHIFT);
+ dma_addr_t phys = address +
+ ((dma_addr_t)p << PAGE_SHIFT);
pages[mapped_size + p] = phys;
}
diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
index 89296de9cf4a..d508a8ba6f89 100644
--- a/drivers/media/platform/rockchip/rga/rga.c
+++ b/drivers/media/platform/rockchip/rga/rga.c
@@ -207,7 +207,7 @@ static int rga_setup_ctrls(struct rga_ctx *ctx)
return 0;
}
-struct rga_fmt formats[] = {
+static struct rga_fmt formats[] = {
{
.fourcc = V4L2_PIX_FMT_ARGB32,
.color_swap = RGA_COLOR_RB_SWAP,
diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c
index 437395a61065..9ab8e7ee2e1e 100644
--- a/drivers/media/platform/s3c-camif/camif-capture.c
+++ b/drivers/media/platform/s3c-camif/camif-capture.c
@@ -1256,16 +1256,17 @@ static void __camif_subdev_try_format(struct camif_dev *camif,
{
const struct s3c_camif_variant *variant = camif->variant;
const struct vp_pix_limits *pix_lim;
- int i = ARRAY_SIZE(camif_mbus_formats);
+ unsigned int i;
/* FIXME: constraints against codec or preview path ? */
pix_lim = &variant->vp_pix_limits[VP_CODEC];
- while (i-- >= 0)
+ for (i = 0; i < ARRAY_SIZE(camif_mbus_formats); i++)
if (camif_mbus_formats[i] == mf->code)
break;
- mf->code = camif_mbus_formats[i];
+ if (i == ARRAY_SIZE(camif_mbus_formats))
+ mf->code = camif_mbus_formats[0];
if (pad == CAMIF_SD_PAD_SINK) {
v4l_bound_align_image(&mf->width, 8, CAMIF_MAX_PIX_WIDTH,
diff --git a/drivers/media/platform/s5p-mfc/regs-mfc-v10.h b/drivers/media/platform/s5p-mfc/regs-mfc-v10.h
new file mode 100644
index 000000000000..fadd9139b489
--- /dev/null
+++ b/drivers/media/platform/s5p-mfc/regs-mfc-v10.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Register definition file for Samsung MFC V10.x Interface (FIMV) driver
+ *
+ */
+
+#ifndef _REGS_MFC_V10_H
+#define _REGS_MFC_V10_H
+
+#include <linux/sizes.h>
+#include "regs-mfc-v8.h"
+
+/* MFCv10 register definitions*/
+#define S5P_FIMV_MFC_CLOCK_OFF_V10 0x7120
+#define S5P_FIMV_MFC_STATE_V10 0x7124
+#define S5P_FIMV_D_STATIC_BUFFER_ADDR_V10 0xF570
+#define S5P_FIMV_D_STATIC_BUFFER_SIZE_V10 0xF574
+#define S5P_FIMV_E_NUM_T_LAYER_V10 0xFBAC
+#define S5P_FIMV_E_HIERARCHICAL_QP_LAYER0_V10 0xFBB0
+#define S5P_FIMV_E_HIERARCHICAL_QP_LAYER1_V10 0xFBB4
+#define S5P_FIMV_E_HIERARCHICAL_QP_LAYER2_V10 0xFBB8
+#define S5P_FIMV_E_HIERARCHICAL_QP_LAYER3_V10 0xFBBC
+#define S5P_FIMV_E_HIERARCHICAL_QP_LAYER4_V10 0xFBC0
+#define S5P_FIMV_E_HIERARCHICAL_QP_LAYER5_V10 0xFBC4
+#define S5P_FIMV_E_HIERARCHICAL_QP_LAYER6_V10 0xFBC8
+#define S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER0_V10 0xFD18
+#define S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER1_V10 0xFD1C
+#define S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER2_V10 0xFD20
+#define S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER3_V10 0xFD24
+#define S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER4_V10 0xFD28
+#define S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER5_V10 0xFD2C
+#define S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER6_V10 0xFD30
+#define S5P_FIMV_E_HEVC_OPTIONS_V10 0xFDD4
+#define S5P_FIMV_E_HEVC_REFRESH_PERIOD_V10 0xFDD8
+#define S5P_FIMV_E_HEVC_CHROMA_QP_OFFSET_V10 0xFDDC
+#define S5P_FIMV_E_HEVC_LF_BETA_OFFSET_DIV2_V10 0xFDE0
+#define S5P_FIMV_E_HEVC_LF_TC_OFFSET_DIV2_V10 0xFDE4
+#define S5P_FIMV_E_HEVC_NAL_CONTROL_V10 0xFDE8
+
+/* MFCv10 Context buffer sizes */
+#define MFC_CTX_BUF_SIZE_V10 (30 * SZ_1K)
+#define MFC_H264_DEC_CTX_BUF_SIZE_V10 (2 * SZ_1M)
+#define MFC_OTHER_DEC_CTX_BUF_SIZE_V10 (20 * SZ_1K)
+#define MFC_H264_ENC_CTX_BUF_SIZE_V10 (100 * SZ_1K)
+#define MFC_HEVC_ENC_CTX_BUF_SIZE_V10 (30 * SZ_1K)
+#define MFC_OTHER_ENC_CTX_BUF_SIZE_V10 (15 * SZ_1K)
+
+/* MFCv10 variant defines */
+#define MAX_FW_SIZE_V10 (SZ_1M)
+#define MAX_CPB_SIZE_V10 (3 * SZ_1M)
+#define MFC_VERSION_V10 0xA0
+#define MFC_NUM_PORTS_V10 1
+
+/* MFCv10 codec defines*/
+#define S5P_FIMV_CODEC_HEVC_DEC 17
+#define S5P_FIMV_CODEC_VP9_DEC 18
+#define S5P_FIMV_CODEC_HEVC_ENC 26
+
+/* Decoder buffer size for MFC v10 */
+#define DEC_VP9_STATIC_BUFFER_SIZE 20480
+
+/* Encoder buffer size for MFC v10.0 */
+#define ENC_V100_BASE_SIZE(x, y) \
+ (((x + 3) * (y + 3) * 8) \
+ + ((y * 64) + 1280) * DIV_ROUND_UP(x, 8))
+
+#define ENC_V100_H264_ME_SIZE(x, y) \
+ (ENC_V100_BASE_SIZE(x, y) \
+ + (DIV_ROUND_UP(x * y, 64) * 32))
+
+#define ENC_V100_MPEG4_ME_SIZE(x, y) \
+ (ENC_V100_BASE_SIZE(x, y) \
+ + (DIV_ROUND_UP(x * y, 128) * 16))
+
+#define ENC_V100_VP8_ME_SIZE(x, y) \
+ ENC_V100_BASE_SIZE(x, y)
+
+#define ENC_V100_HEVC_ME_SIZE(x, y) \
+ (((x + 3) * (y + 3) * 32) \
+ + ((y * 128) + 1280) * DIV_ROUND_UP(x, 4))
+
+#endif /*_REGS_MFC_V10_H*/
+
diff --git a/drivers/media/platform/s5p-mfc/regs-mfc-v8.h b/drivers/media/platform/s5p-mfc/regs-mfc-v8.h
index 75f5f7511d72..bd639ae71023 100644
--- a/drivers/media/platform/s5p-mfc/regs-mfc-v8.h
+++ b/drivers/media/platform/s5p-mfc/regs-mfc-v8.h
@@ -17,6 +17,7 @@
/* Additional registers for v8 */
#define S5P_FIMV_D_MVC_NUM_VIEWS_V8 0xf104
+#define S5P_FIMV_D_MIN_SCRATCH_BUFFER_SIZE_V8 0xf108
#define S5P_FIMV_D_FIRST_PLANE_DPB_SIZE_V8 0xf144
#define S5P_FIMV_D_SECOND_PLANE_DPB_SIZE_V8 0xf148
#define S5P_FIMV_D_MV_BUFFER_SIZE_V8 0xf150
@@ -84,6 +85,7 @@
#define S5P_FIMV_E_VBV_BUFFER_SIZE_V8 0xf78c
#define S5P_FIMV_E_VBV_INIT_DELAY_V8 0xf790
+#define S5P_FIMV_E_MIN_SCRATCH_BUFFER_SIZE_V8 0xf894
#define S5P_FIMV_E_ASPECT_RATIO_V8 0xfb4c
#define S5P_FIMV_E_EXTENDED_SAR_V8 0xfb50
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c
index d5b94fc0040e..a80251ed3143 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c
@@ -526,6 +526,8 @@ static void s5p_mfc_handle_seq_done(struct s5p_mfc_ctx *ctx,
dev);
ctx->mv_count = s5p_mfc_hw_call(dev->mfc_ops, get_mv_count,
dev);
+ ctx->scratch_buf_size = s5p_mfc_hw_call(dev->mfc_ops,
+ get_min_scratch_buf_size, dev);
if (ctx->img_width == 0 || ctx->img_height == 0)
ctx->state = MFCINST_ERROR;
else
@@ -1607,6 +1609,29 @@ static struct s5p_mfc_variant mfc_drvdata_v8_5433 = {
.num_clocks = 3,
};
+static struct s5p_mfc_buf_size_v6 mfc_buf_size_v10 = {
+ .dev_ctx = MFC_CTX_BUF_SIZE_V10,
+ .h264_dec_ctx = MFC_H264_DEC_CTX_BUF_SIZE_V10,
+ .other_dec_ctx = MFC_OTHER_DEC_CTX_BUF_SIZE_V10,
+ .h264_enc_ctx = MFC_H264_ENC_CTX_BUF_SIZE_V10,
+ .hevc_enc_ctx = MFC_HEVC_ENC_CTX_BUF_SIZE_V10,
+ .other_enc_ctx = MFC_OTHER_ENC_CTX_BUF_SIZE_V10,
+};
+
+static struct s5p_mfc_buf_size buf_size_v10 = {
+ .fw = MAX_FW_SIZE_V10,
+ .cpb = MAX_CPB_SIZE_V10,
+ .priv = &mfc_buf_size_v10,
+};
+
+static struct s5p_mfc_variant mfc_drvdata_v10 = {
+ .version = MFC_VERSION_V10,
+ .version_bit = MFC_V10_BIT,
+ .port_num = MFC_NUM_PORTS_V10,
+ .buf_size = &buf_size_v10,
+ .fw_name[0] = "s5p-mfc-v10.fw",
+};
+
static const struct of_device_id exynos_mfc_match[] = {
{
.compatible = "samsung,mfc-v5",
@@ -1623,6 +1648,9 @@ static const struct of_device_id exynos_mfc_match[] = {
}, {
.compatible = "samsung,exynos5433-mfc",
.data = &mfc_drvdata_v8_5433,
+ }, {
+ .compatible = "samsung,mfc-v10",
+ .data = &mfc_drvdata_v10,
},
{},
};
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c
index b1b149151d2d..7521fceb68f1 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c
@@ -101,6 +101,12 @@ static int s5p_mfc_open_inst_cmd_v6(struct s5p_mfc_ctx *ctx)
case S5P_MFC_CODEC_VP8_DEC:
codec_type = S5P_FIMV_CODEC_VP8_DEC_V6;
break;
+ case S5P_MFC_CODEC_HEVC_DEC:
+ codec_type = S5P_FIMV_CODEC_HEVC_DEC;
+ break;
+ case S5P_MFC_CODEC_VP9_DEC:
+ codec_type = S5P_FIMV_CODEC_VP9_DEC;
+ break;
case S5P_MFC_CODEC_H264_ENC:
codec_type = S5P_FIMV_CODEC_H264_ENC_V6;
break;
@@ -116,6 +122,9 @@ static int s5p_mfc_open_inst_cmd_v6(struct s5p_mfc_ctx *ctx)
case S5P_MFC_CODEC_VP8_ENC:
codec_type = S5P_FIMV_CODEC_VP8_ENC_V7;
break;
+ case S5P_MFC_CODEC_HEVC_ENC:
+ codec_type = S5P_FIMV_CODEC_HEVC_ENC;
+ break;
default:
codec_type = S5P_FIMV_CODEC_NONE_V6;
}
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
index 76119a8cc477..20442a9b9f7a 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
@@ -23,7 +23,7 @@
#include <media/v4l2-ioctl.h>
#include <media/videobuf2-v4l2.h>
#include "regs-mfc.h"
-#include "regs-mfc-v8.h"
+#include "regs-mfc-v10.h"
#define S5P_MFC_NAME "s5p-mfc"
@@ -61,7 +61,7 @@
#define MFC_ENC_CAP_PLANE_COUNT 1
#define MFC_ENC_OUT_PLANE_COUNT 2
#define STUFF_BYTE 4
-#define MFC_MAX_CTRLS 77
+#define MFC_MAX_CTRLS 128
#define S5P_MFC_CODEC_NONE -1
#define S5P_MFC_CODEC_H264_DEC 0
@@ -72,12 +72,15 @@
#define S5P_MFC_CODEC_H263_DEC 5
#define S5P_MFC_CODEC_VC1RCV_DEC 6
#define S5P_MFC_CODEC_VP8_DEC 7
+#define S5P_MFC_CODEC_HEVC_DEC 17
+#define S5P_MFC_CODEC_VP9_DEC 18
#define S5P_MFC_CODEC_H264_ENC 20
#define S5P_MFC_CODEC_H264_MVC_ENC 21
#define S5P_MFC_CODEC_MPEG4_ENC 22
#define S5P_MFC_CODEC_H263_ENC 23
#define S5P_MFC_CODEC_VP8_ENC 24
+#define S5P_MFC_CODEC_HEVC_ENC 26
#define S5P_MFC_R2H_CMD_EMPTY 0
#define S5P_MFC_R2H_CMD_SYS_INIT_RET 1
@@ -213,6 +216,7 @@ struct s5p_mfc_buf_size_v6 {
unsigned int h264_dec_ctx;
unsigned int other_dec_ctx;
unsigned int h264_enc_ctx;
+ unsigned int hevc_enc_ctx;
unsigned int other_enc_ctx;
};
@@ -430,6 +434,55 @@ struct s5p_mfc_vp8_enc_params {
u8 profile;
};
+struct s5p_mfc_hevc_enc_params {
+ enum v4l2_mpeg_video_hevc_profile profile;
+ int level;
+ enum v4l2_mpeg_video_h264_level level_v4l2;
+ u8 tier;
+ u32 rc_framerate;
+ u8 rc_min_qp;
+ u8 rc_max_qp;
+ u8 rc_lcu_dark;
+ u8 rc_lcu_smooth;
+ u8 rc_lcu_static;
+ u8 rc_lcu_activity;
+ u8 rc_frame_qp;
+ u8 rc_p_frame_qp;
+ u8 rc_b_frame_qp;
+ u8 max_partition_depth;
+ u8 num_refs_for_p;
+ u8 refreshtype;
+ u16 refreshperiod;
+ s32 lf_beta_offset_div2;
+ s32 lf_tc_offset_div2;
+ u8 loopfilter;
+ u8 loopfilter_disable;
+ u8 loopfilter_across;
+ u8 nal_control_length_filed;
+ u8 nal_control_user_ref;
+ u8 nal_control_store_ref;
+ u8 const_intra_period_enable;
+ u8 lossless_cu_enable;
+ u8 wavefront_enable;
+ u8 enable_ltr;
+ u8 hier_qp_enable;
+ enum v4l2_mpeg_video_hevc_hier_coding_type hier_qp_type;
+ u8 num_hier_layer;
+ u8 hier_qp_layer[7];
+ u32 hier_bit_layer[7];
+ u8 sign_data_hiding;
+ u8 general_pb_enable;
+ u8 temporal_id_enable;
+ u8 strong_intra_smooth;
+ u8 intra_pu_split_disable;
+ u8 tmv_prediction_disable;
+ u8 max_num_merge_mv;
+ u8 eco_mode_enable;
+ u8 encoding_nostartcode_enable;
+ u8 size_of_length_field;
+ u8 prepend_sps_pps_to_idr;
+};
+
/**
* struct s5p_mfc_enc_params - general encoding parameters
*/
@@ -467,6 +520,7 @@ struct s5p_mfc_enc_params {
struct s5p_mfc_h264_enc_params h264;
struct s5p_mfc_mpeg4_enc_params mpeg4;
struct s5p_mfc_vp8_enc_params vp8;
+ struct s5p_mfc_hevc_enc_params hevc;
} codec;
};
@@ -714,12 +768,20 @@ void s5p_mfc_cleanup_queue(struct list_head *lh, struct vb2_queue *vq);
#define IS_TWOPORT(dev) (dev->variant->port_num == 2 ? 1 : 0)
#define IS_MFCV6_PLUS(dev) (dev->variant->version >= 0x60 ? 1 : 0)
#define IS_MFCV7_PLUS(dev) (dev->variant->version >= 0x70 ? 1 : 0)
-#define IS_MFCV8(dev) (dev->variant->version >= 0x80 ? 1 : 0)
+#define IS_MFCV8_PLUS(dev) (dev->variant->version >= 0x80 ? 1 : 0)
+#define IS_MFCV10(dev) (dev->variant->version >= 0xA0 ? 1 : 0)
+#define FW_HAS_E_MIN_SCRATCH_BUF(dev) (IS_MFCV10(dev))
#define MFC_V5_BIT BIT(0)
#define MFC_V6_BIT BIT(1)
#define MFC_V7_BIT BIT(2)
#define MFC_V8_BIT BIT(3)
+#define MFC_V10_BIT BIT(5)
+#define MFC_V5PLUS_BITS (MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | \
+ MFC_V8_BIT | MFC_V10_BIT)
+#define MFC_V6PLUS_BITS (MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT | \
+ MFC_V10_BIT)
+#define MFC_V7PLUS_BITS (MFC_V7_BIT | MFC_V8_BIT | MFC_V10_BIT)
#endif /* S5P_MFC_COMMON_H_ */
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
index f95cd76af537..ee7b15b335e0 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
@@ -62,7 +62,7 @@ int s5p_mfc_load_firmware(struct s5p_mfc_dev *dev)
if (!dev->variant->fw_name[i])
continue;
err = request_firmware((const struct firmware **)&fw_blob,
- dev->variant->fw_name[i], dev->v4l2_dev.dev);
+ dev->variant->fw_name[i], &dev->plat_dev->dev);
if (!err) {
dev->fw_ver = (enum s5p_mfc_fw_ver) i;
break;
@@ -239,6 +239,10 @@ int s5p_mfc_init_hw(struct s5p_mfc_dev *dev)
}
else
mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET);
+
+ if (IS_MFCV10(dev))
+ mfc_write(dev, 0x0, S5P_FIMV_MFC_CLOCK_OFF_V10);
+
mfc_debug(2, "Will now wait for completion of firmware transfer\n");
if (s5p_mfc_wait_for_done_dev(dev, S5P_MFC_R2H_CMD_FW_STATUS_RET)) {
mfc_err("Failed to load firmware\n");
@@ -399,7 +403,7 @@ int s5p_mfc_wakeup(struct s5p_mfc_dev *dev)
s5p_mfc_clear_cmds(dev);
s5p_mfc_clean_dev_int_flags(dev);
/* 3. Send MFC wakeup command and wait for completion*/
- if (IS_MFCV8(dev))
+ if (IS_MFCV8_PLUS(dev))
ret = s5p_mfc_v8_wait_wakeup(dev);
else
ret = s5p_mfc_wait_wakeup(dev);
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
index 8937b0af7cb3..5cf4d9921264 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
@@ -54,7 +54,7 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_NONE,
.type = MFC_FMT_RAW,
.num_planes = 2,
- .versions = MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT,
+ .versions = MFC_V6PLUS_BITS,
},
{
.name = "4:2:0 2 Planes Y/CrCb",
@@ -62,7 +62,7 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_NONE,
.type = MFC_FMT_RAW,
.num_planes = 2,
- .versions = MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT,
+ .versions = MFC_V6PLUS_BITS,
},
{
.name = "H264 Encoded Stream",
@@ -70,8 +70,7 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_H264_DEC,
.type = MFC_FMT_DEC,
.num_planes = 1,
- .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
- MFC_V8_BIT,
+ .versions = MFC_V5PLUS_BITS,
},
{
.name = "H264/MVC Encoded Stream",
@@ -79,7 +78,7 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_H264_MVC_DEC,
.type = MFC_FMT_DEC,
.num_planes = 1,
- .versions = MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT,
+ .versions = MFC_V6PLUS_BITS,
},
{
.name = "H263 Encoded Stream",
@@ -87,8 +86,7 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_H263_DEC,
.type = MFC_FMT_DEC,
.num_planes = 1,
- .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
- MFC_V8_BIT,
+ .versions = MFC_V5PLUS_BITS,
},
{
.name = "MPEG1 Encoded Stream",
@@ -96,8 +94,7 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_MPEG2_DEC,
.type = MFC_FMT_DEC,
.num_planes = 1,
- .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
- MFC_V8_BIT,
+ .versions = MFC_V5PLUS_BITS,
},
{
.name = "MPEG2 Encoded Stream",
@@ -105,8 +102,7 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_MPEG2_DEC,
.type = MFC_FMT_DEC,
.num_planes = 1,
- .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
- MFC_V8_BIT,
+ .versions = MFC_V5PLUS_BITS,
},
{
.name = "MPEG4 Encoded Stream",
@@ -114,8 +110,7 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_MPEG4_DEC,
.type = MFC_FMT_DEC,
.num_planes = 1,
- .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
- MFC_V8_BIT,
+ .versions = MFC_V5PLUS_BITS,
},
{
.name = "XviD Encoded Stream",
@@ -123,8 +118,7 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_MPEG4_DEC,
.type = MFC_FMT_DEC,
.num_planes = 1,
- .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
- MFC_V8_BIT,
+ .versions = MFC_V5PLUS_BITS,
},
{
.name = "VC1 Encoded Stream",
@@ -132,8 +126,7 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_VC1_DEC,
.type = MFC_FMT_DEC,
.num_planes = 1,
- .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
- MFC_V8_BIT,
+ .versions = MFC_V5PLUS_BITS,
},
{
.name = "VC1 RCV Encoded Stream",
@@ -141,8 +134,7 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_VC1RCV_DEC,
.type = MFC_FMT_DEC,
.num_planes = 1,
- .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
- MFC_V8_BIT,
+ .versions = MFC_V5PLUS_BITS,
},
{
.name = "VP8 Encoded Stream",
@@ -150,7 +142,21 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_VP8_DEC,
.type = MFC_FMT_DEC,
.num_planes = 1,
- .versions = MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT,
+ .versions = MFC_V6PLUS_BITS,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_HEVC,
+ .codec_mode = S5P_FIMV_CODEC_HEVC_DEC,
+ .type = MFC_FMT_DEC,
+ .num_planes = 1,
+ .versions = MFC_V10_BIT,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_VP9,
+ .codec_mode = S5P_FIMV_CODEC_VP9_DEC,
+ .type = MFC_FMT_DEC,
+ .num_planes = 1,
+ .versions = MFC_V10_BIT,
},
};
@@ -1177,7 +1183,7 @@ void s5p_mfc_dec_init(struct s5p_mfc_ctx *ctx)
struct v4l2_format f;
f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_H264;
ctx->src_fmt = find_format(&f, MFC_FMT_DEC);
- if (IS_MFCV8(ctx->dev))
+ if (IS_MFCV8_PLUS(ctx->dev))
f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12M;
else if (IS_MFCV6_PLUS(ctx->dev))
f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12MT_16X16;
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
index 0d5d465561be..5c0462ca9993 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
@@ -57,8 +57,7 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_NONE,
.type = MFC_FMT_RAW,
.num_planes = 2,
- .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
- MFC_V8_BIT,
+ .versions = MFC_V5PLUS_BITS,
},
{
.name = "4:2:0 2 Planes Y/CrCb",
@@ -66,7 +65,7 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_NONE,
.type = MFC_FMT_RAW,
.num_planes = 2,
- .versions = MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT,
+ .versions = MFC_V6PLUS_BITS,
},
{
.name = "H264 Encoded Stream",
@@ -74,8 +73,7 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_H264_ENC,
.type = MFC_FMT_ENC,
.num_planes = 1,
- .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
- MFC_V8_BIT,
+ .versions = MFC_V5PLUS_BITS,
},
{
.name = "MPEG4 Encoded Stream",
@@ -83,8 +81,7 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_MPEG4_ENC,
.type = MFC_FMT_ENC,
.num_planes = 1,
- .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
- MFC_V8_BIT,
+ .versions = MFC_V5PLUS_BITS,
},
{
.name = "H263 Encoded Stream",
@@ -92,8 +89,7 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_H263_ENC,
.type = MFC_FMT_ENC,
.num_planes = 1,
- .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
- MFC_V8_BIT,
+ .versions = MFC_V5PLUS_BITS,
},
{
.name = "VP8 Encoded Stream",
@@ -101,7 +97,14 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_VP8_ENC,
.type = MFC_FMT_ENC,
.num_planes = 1,
- .versions = MFC_V7_BIT | MFC_V8_BIT,
+ .versions = MFC_V7PLUS_BITS,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_HEVC,
+ .codec_mode = S5P_FIMV_CODEC_HEVC_ENC,
+ .type = MFC_FMT_ENC,
+ .num_planes = 1,
+ .versions = MFC_V10_BIT,
},
};
@@ -697,6 +700,368 @@ static struct mfc_control controls[] = {
.default_value = 0,
},
{
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "HEVC I Frame QP Value",
+ .minimum = 0,
+ .maximum = 51,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "HEVC P Frame QP Value",
+ .minimum = 0,
+ .maximum = 51,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 51,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 51,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 51,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_PROFILE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
+ .maximum = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE,
+ .step = 1,
+ .default_value = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_LEVEL,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_HEVC_LEVEL_1,
+ .maximum = V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2,
+ .step = 1,
+ .default_value = V4L2_MPEG_VIDEO_HEVC_LEVEL_1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_TIER,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_HEVC_TIER_MAIN,
+ .maximum = V4L2_MPEG_VIDEO_HEVC_TIER_HIGH,
+ .step = 1,
+ .default_value = V4L2_MPEG_VIDEO_HEVC_TIER_MAIN,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_FRAME_RATE_RESOLUTION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_MAX_PARTITION_DEPTH,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_REF_NUMBER_FOR_PFRAMES,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = 2,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_HEVC_REFRESH_NONE,
+ .maximum = V4L2_MPEG_VIDEO_HEVC_REFRESH_IDR,
+ .step = 1,
+ .default_value = V4L2_MPEG_VIDEO_HEVC_REFRESH_NONE,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_CONST_INTRA_PRED,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_LOSSLESS_CU,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_WAVEFRONT,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED,
+ .maximum = V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY,
+ .step = 1,
+ .default_value = V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_QP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B,
+ .maximum = V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P,
+ .step = 1,
+ .default_value = V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_LAYER,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 6,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 51,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 51,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 51,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 51,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 51,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 51,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 51,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_BR,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = INT_MIN,
+ .maximum = INT_MAX,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_BR,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = INT_MIN,
+ .maximum = INT_MAX,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_BR,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = INT_MIN,
+ .maximum = INT_MAX,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_BR,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = INT_MIN,
+ .maximum = INT_MAX,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_BR,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = INT_MIN,
+ .maximum = INT_MAX,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_BR,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = INT_MIN,
+ .maximum = INT_MAX,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_BR,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = INT_MIN,
+ .maximum = INT_MAX,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_GENERAL_PB,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_TEMPORAL_ID,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_STRONG_SMOOTHING,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_INTRA_PU_SPLIT,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_TMV_PREDICTION,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 4,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_WITHOUT_STARTCODE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_LF_BETA_OFFSET_DIV2,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = -6,
+ .maximum = 6,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_LF_TC_OFFSET_DIV2,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = -6,
+ .maximum = 6,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_HEVC_SIZE_0,
+ .maximum = V4L2_MPEG_VIDEO_HEVC_SIZE_4,
+ .step = 1,
+ .default_value = V4L2_MPEG_VIDEO_HEVC_SIZE_0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
.id = V4L2_CID_MIN_BUFFERS_FOR_OUTPUT,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Minimum number of output bufs",
@@ -817,6 +1182,11 @@ static int enc_post_seq_start(struct s5p_mfc_ctx *ctx)
get_enc_dpb_count, dev);
if (ctx->pb_count < enc_pb_count)
ctx->pb_count = enc_pb_count;
+ if (FW_HAS_E_MIN_SCRATCH_BUF(dev)) {
+ ctx->scratch_buf_size = s5p_mfc_hw_call(dev->mfc_ops,
+ get_e_min_scratch_buf_size, dev);
+ ctx->bank1.size += ctx->scratch_buf_size;
+ }
ctx->state = MFCINST_HEAD_PRODUCED;
}
@@ -850,7 +1220,7 @@ static int enc_post_frame_start(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
struct s5p_mfc_buf *mb_entry;
- unsigned long enc_y_addr, enc_c_addr;
+ unsigned long enc_y_addr = 0, enc_c_addr = 0;
unsigned long mb_y_addr, mb_c_addr;
int slice_type;
unsigned int strm_size;
@@ -1358,6 +1728,26 @@ static inline int mpeg4_level(enum v4l2_mpeg_video_mpeg4_level lvl)
return t[lvl];
}
+static inline int hevc_level(enum v4l2_mpeg_video_hevc_level lvl)
+{
+ static unsigned int t[] = {
+ /* V4L2_MPEG_VIDEO_HEVC_LEVEL_1 */ 10,
+ /* V4L2_MPEG_VIDEO_HEVC_LEVEL_2 */ 20,
+ /* V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1 */ 21,
+ /* V4L2_MPEG_VIDEO_HEVC_LEVEL_3 */ 30,
+ /* V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1 */ 31,
+ /* V4L2_MPEG_VIDEO_HEVC_LEVEL_4 */ 40,
+ /* V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1 */ 41,
+ /* V4L2_MPEG_VIDEO_HEVC_LEVEL_5 */ 50,
+ /* V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1 */ 51,
+ /* V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2 */ 52,
+ /* V4L2_MPEG_VIDEO_HEVC_LEVEL_6 */ 60,
+ /* V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1 */ 61,
+ /* V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2 */ 62,
+ };
+ return t[lvl];
+}
+
static inline int vui_sar_idc(enum v4l2_mpeg_video_h264_vui_sar_idc sar)
{
static unsigned int t[V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED + 1] = {
@@ -1383,6 +1773,42 @@ static inline int vui_sar_idc(enum v4l2_mpeg_video_h264_vui_sar_idc sar)
return t[sar];
}
+/*
+ * Update range of all HEVC quantization parameter controls that depend on the
+ * V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP controls.
+ */
+static void __enc_update_hevc_qp_ctrls_range(struct s5p_mfc_ctx *ctx,
+ int min, int max)
+{
+ static const int __hevc_qp_ctrls[] = {
+ V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP,
+ V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP,
+ V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP,
+ V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_QP,
+ V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_QP,
+ V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_QP,
+ V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_QP,
+ V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_QP,
+ V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_QP,
+ V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_QP,
+ };
+ struct v4l2_ctrl *ctrl = NULL;
+ int i, j;
+
+ for (i = 0; i < ARRAY_SIZE(__hevc_qp_ctrls); i++) {
+ for (j = 0; j < ARRAY_SIZE(ctx->ctrls); j++) {
+ if (ctx->ctrls[j]->id == __hevc_qp_ctrls[i]) {
+ ctrl = ctx->ctrls[j];
+ break;
+ }
+ }
+ if (WARN_ON(!ctrl))
+ break;
+
+ __v4l2_ctrl_modify_range(ctrl, min, max, ctrl->step, min);
+ }
+}
+
static int s5p_mfc_enc_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct s5p_mfc_ctx *ctx = ctrl_to_ctx(ctrl);
@@ -1634,6 +2060,157 @@ static int s5p_mfc_enc_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_MPEG_VIDEO_VPX_PROFILE:
p->codec.vp8.profile = ctrl->val;
break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP:
+ p->codec.hevc.rc_frame_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP:
+ p->codec.hevc.rc_p_frame_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP:
+ p->codec.hevc.rc_b_frame_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_FRAME_RATE_RESOLUTION:
+ p->codec.hevc.rc_framerate = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP:
+ p->codec.hevc.rc_min_qp = ctrl->val;
+ __enc_update_hevc_qp_ctrls_range(ctx, ctrl->val,
+ p->codec.hevc.rc_max_qp);
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP:
+ p->codec.hevc.rc_max_qp = ctrl->val;
+ __enc_update_hevc_qp_ctrls_range(ctx, p->codec.hevc.rc_min_qp,
+ ctrl->val);
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL:
+ p->codec.hevc.level_v4l2 = ctrl->val;
+ p->codec.hevc.level = hevc_level(ctrl->val);
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE:
+ switch (ctrl->val) {
+ case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN:
+ p->codec.hevc.profile =
+ V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN;
+ break;
+ case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE:
+ p->codec.hevc.profile =
+ V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_TIER:
+ p->codec.hevc.tier = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_MAX_PARTITION_DEPTH:
+ p->codec.hevc.max_partition_depth = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_REF_NUMBER_FOR_PFRAMES:
+ p->codec.hevc.num_refs_for_p = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE:
+ p->codec.hevc.refreshtype = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_CONST_INTRA_PRED:
+ p->codec.hevc.const_intra_period_enable = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_LOSSLESS_CU:
+ p->codec.hevc.lossless_cu_enable = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_WAVEFRONT:
+ p->codec.hevc.wavefront_enable = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE:
+ p->codec.hevc.loopfilter = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_QP:
+ p->codec.hevc.hier_qp_enable = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE:
+ p->codec.hevc.hier_qp_type = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_LAYER:
+ p->codec.hevc.num_hier_layer = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_QP:
+ p->codec.hevc.hier_qp_layer[0] = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_QP:
+ p->codec.hevc.hier_qp_layer[1] = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_QP:
+ p->codec.hevc.hier_qp_layer[2] = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_QP:
+ p->codec.hevc.hier_qp_layer[3] = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_QP:
+ p->codec.hevc.hier_qp_layer[4] = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_QP:
+ p->codec.hevc.hier_qp_layer[5] = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_QP:
+ p->codec.hevc.hier_qp_layer[6] = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_BR:
+ p->codec.hevc.hier_bit_layer[0] = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_BR:
+ p->codec.hevc.hier_bit_layer[1] = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_BR:
+ p->codec.hevc.hier_bit_layer[2] = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_BR:
+ p->codec.hevc.hier_bit_layer[3] = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_BR:
+ p->codec.hevc.hier_bit_layer[4] = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_BR:
+ p->codec.hevc.hier_bit_layer[5] = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_BR:
+ p->codec.hevc.hier_bit_layer[6] = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_GENERAL_PB:
+ p->codec.hevc.general_pb_enable = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_TEMPORAL_ID:
+ p->codec.hevc.temporal_id_enable = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_STRONG_SMOOTHING:
+ p->codec.hevc.strong_intra_smooth = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_INTRA_PU_SPLIT:
+ p->codec.hevc.intra_pu_split_disable = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_TMV_PREDICTION:
+ p->codec.hevc.tmv_prediction_disable = !ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1:
+ p->codec.hevc.max_num_merge_mv = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_WITHOUT_STARTCODE:
+ p->codec.hevc.encoding_nostartcode_enable = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD:
+ p->codec.hevc.refreshperiod = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_LF_BETA_OFFSET_DIV2:
+ p->codec.hevc.lf_beta_offset_div2 = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_LF_TC_OFFSET_DIV2:
+ p->codec.hevc.lf_tc_offset_div2 = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD:
+ p->codec.hevc.size_of_length_field = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR:
+ p->codec.hevc.prepend_sps_pps_to_idr = ctrl->val;
+ break;
default:
v4l2_err(&dev->v4l2_dev, "Invalid control, id=%d, val=%d\n",
ctrl->id, ctrl->val);
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h
index 16d553fcff08..8c295f0f9740 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h
@@ -169,6 +169,9 @@ struct s5p_mfc_regs {
void __iomem *d_decoded_third_addr;/* only v7 */
void __iomem *d_used_dpb_flag_upper;/* v7 and v8 */
void __iomem *d_used_dpb_flag_lower;/* v7 and v8 */
+ void __iomem *d_min_scratch_buffer_size; /* v10 */
+ void __iomem *d_static_buffer_addr; /* v10 */
+ void __iomem *d_static_buffer_size; /* v10 */
/* encoder registers */
void __iomem *e_frame_width;
@@ -268,6 +271,15 @@ struct s5p_mfc_regs {
void __iomem *e_vp8_hierarchical_qp_layer0;/* v7 and v8 */
void __iomem *e_vp8_hierarchical_qp_layer1;/* v7 and v8 */
void __iomem *e_vp8_hierarchical_qp_layer2;/* v7 and v8 */
+ void __iomem *e_min_scratch_buffer_size; /* v10 */
+ void __iomem *e_num_t_layer; /* v10 */
+ void __iomem *e_hier_qp_layer0; /* v10 */
+ void __iomem *e_hier_bit_rate_layer0; /* v10 */
+ void __iomem *e_hevc_options; /* v10 */
+ void __iomem *e_hevc_refresh_period; /* v10 */
+ void __iomem *e_hevc_lf_beta_offset_div2; /* v10 */
+ void __iomem *e_hevc_lf_tc_offset_div2; /* v10 */
+ void __iomem *e_hevc_nal_control; /* v10 */
};
struct s5p_mfc_hw_ops {
@@ -311,6 +323,8 @@ struct s5p_mfc_hw_ops {
unsigned int (*get_pic_type_bot)(struct s5p_mfc_ctx *ctx);
unsigned int (*get_crop_info_h)(struct s5p_mfc_ctx *ctx);
unsigned int (*get_crop_info_v)(struct s5p_mfc_ctx *ctx);
+ int (*get_min_scratch_buf_size)(struct s5p_mfc_dev *dev);
+ int (*get_e_min_scratch_buf_size)(struct s5p_mfc_dev *dev);
};
void s5p_mfc_init_hw_ops(struct s5p_mfc_dev *dev);
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
index 88dbb9c341ec..7c629be43205 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
@@ -64,6 +64,7 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
unsigned int mb_width, mb_height;
+ unsigned int lcu_width = 0, lcu_height = 0;
int ret;
mb_width = MB_WIDTH(ctx->img_width);
@@ -74,7 +75,9 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
ctx->luma_size, ctx->chroma_size, ctx->mv_size);
mfc_debug(2, "Totals bufs: %d\n", ctx->total_dpb_count);
} else if (ctx->type == MFCINST_ENCODER) {
- if (IS_MFCV8(dev))
+ if (IS_MFCV10(dev)) {
+ ctx->tmv_buffer_size = 0;
+ } else if (IS_MFCV8_PLUS(dev))
ctx->tmv_buffer_size = S5P_FIMV_NUM_TMV_BUFFERS_V6 *
ALIGN(S5P_FIMV_TMV_BUFFER_SIZE_V8(mb_width, mb_height),
S5P_FIMV_TMV_BUFFER_ALIGN_V6);
@@ -82,14 +85,37 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
ctx->tmv_buffer_size = S5P_FIMV_NUM_TMV_BUFFERS_V6 *
ALIGN(S5P_FIMV_TMV_BUFFER_SIZE_V6(mb_width, mb_height),
S5P_FIMV_TMV_BUFFER_ALIGN_V6);
-
- ctx->luma_dpb_size = ALIGN((mb_width * mb_height) *
- S5P_FIMV_LUMA_MB_TO_PIXEL_V6,
- S5P_FIMV_LUMA_DPB_BUFFER_ALIGN_V6);
- ctx->chroma_dpb_size = ALIGN((mb_width * mb_height) *
- S5P_FIMV_CHROMA_MB_TO_PIXEL_V6,
- S5P_FIMV_CHROMA_DPB_BUFFER_ALIGN_V6);
- if (IS_MFCV8(dev))
+ if (IS_MFCV10(dev)) {
+ lcu_width = S5P_MFC_LCU_WIDTH(ctx->img_width);
+ lcu_height = S5P_MFC_LCU_HEIGHT(ctx->img_height);
+ if (ctx->codec_mode != S5P_FIMV_CODEC_HEVC_ENC) {
+ ctx->luma_dpb_size =
+ ALIGN((mb_width * 16), 64)
+ * ALIGN((mb_height * 16), 32)
+ + 64;
+ ctx->chroma_dpb_size =
+ ALIGN((mb_width * 16), 64)
+ * (mb_height * 8)
+ + 64;
+ } else {
+ ctx->luma_dpb_size =
+ ALIGN((lcu_width * 32), 64)
+ * ALIGN((lcu_height * 32), 32)
+ + 64;
+ ctx->chroma_dpb_size =
+ ALIGN((lcu_width * 32), 64)
+ * (lcu_height * 16)
+ + 64;
+ }
+ } else {
+ ctx->luma_dpb_size = ALIGN((mb_width * mb_height) *
+ S5P_FIMV_LUMA_MB_TO_PIXEL_V6,
+ S5P_FIMV_LUMA_DPB_BUFFER_ALIGN_V6);
+ ctx->chroma_dpb_size = ALIGN((mb_width * mb_height) *
+ S5P_FIMV_CHROMA_MB_TO_PIXEL_V6,
+ S5P_FIMV_CHROMA_DPB_BUFFER_ALIGN_V6);
+ }
+ if (IS_MFCV8_PLUS(dev))
ctx->me_buffer_size = ALIGN(S5P_FIMV_ME_BUFFER_SIZE_V8(
ctx->img_width, ctx->img_height,
mb_width, mb_height),
@@ -110,7 +136,9 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
switch (ctx->codec_mode) {
case S5P_MFC_CODEC_H264_DEC:
case S5P_MFC_CODEC_H264_MVC_DEC:
- if (IS_MFCV8(dev))
+ if (IS_MFCV10(dev))
+ mfc_debug(2, "Use min scratch buffer size\n");
+ else if (IS_MFCV8_PLUS(dev))
ctx->scratch_buf_size =
S5P_FIMV_SCRATCH_BUF_SIZE_H264_DEC_V8(
mb_width,
@@ -127,7 +155,9 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
(ctx->mv_count * ctx->mv_size);
break;
case S5P_MFC_CODEC_MPEG4_DEC:
- if (IS_MFCV7_PLUS(dev)) {
+ if (IS_MFCV10(dev))
+ mfc_debug(2, "Use min scratch buffer size\n");
+ else if (IS_MFCV7_PLUS(dev)) {
ctx->scratch_buf_size =
S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_DEC_V7(
mb_width,
@@ -145,10 +175,14 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
break;
case S5P_MFC_CODEC_VC1RCV_DEC:
case S5P_MFC_CODEC_VC1_DEC:
- ctx->scratch_buf_size =
- S5P_FIMV_SCRATCH_BUF_SIZE_VC1_DEC_V6(
- mb_width,
- mb_height);
+ if (IS_MFCV10(dev))
+ mfc_debug(2, "Use min scratch buffer size\n");
+ else
+ ctx->scratch_buf_size =
+ S5P_FIMV_SCRATCH_BUF_SIZE_VC1_DEC_V6(
+ mb_width,
+ mb_height);
+
ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size,
S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6);
ctx->bank1.size = ctx->scratch_buf_size;
@@ -158,16 +192,21 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
ctx->bank2.size = 0;
break;
case S5P_MFC_CODEC_H263_DEC:
- ctx->scratch_buf_size =
- S5P_FIMV_SCRATCH_BUF_SIZE_H263_DEC_V6(
- mb_width,
- mb_height);
+ if (IS_MFCV10(dev))
+ mfc_debug(2, "Use min scratch buffer size\n");
+ else
+ ctx->scratch_buf_size =
+ S5P_FIMV_SCRATCH_BUF_SIZE_H263_DEC_V6(
+ mb_width,
+ mb_height);
ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size,
S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6);
ctx->bank1.size = ctx->scratch_buf_size;
break;
case S5P_MFC_CODEC_VP8_DEC:
- if (IS_MFCV8(dev))
+ if (IS_MFCV10(dev))
+ mfc_debug(2, "Use min scratch buffer size\n");
+ else if (IS_MFCV8_PLUS(dev))
ctx->scratch_buf_size =
S5P_FIMV_SCRATCH_BUF_SIZE_VP8_DEC_V8(
mb_width,
@@ -181,8 +220,24 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6);
ctx->bank1.size = ctx->scratch_buf_size;
break;
+ case S5P_MFC_CODEC_HEVC_DEC:
+ mfc_debug(2, "Use min scratch buffer size\n");
+ ctx->bank1.size =
+ ctx->scratch_buf_size +
+ (ctx->mv_count * ctx->mv_size);
+ break;
+ case S5P_MFC_CODEC_VP9_DEC:
+ mfc_debug(2, "Use min scratch buffer size\n");
+ ctx->bank1.size =
+ ctx->scratch_buf_size +
+ DEC_VP9_STATIC_BUFFER_SIZE;
+ break;
case S5P_MFC_CODEC_H264_ENC:
- if (IS_MFCV8(dev))
+ if (IS_MFCV10(dev)) {
+ mfc_debug(2, "Use min scratch buffer size\n");
+ ctx->me_buffer_size =
+ ALIGN(ENC_V100_H264_ME_SIZE(mb_width, mb_height), 16);
+ } else if (IS_MFCV8_PLUS(dev))
ctx->scratch_buf_size =
S5P_FIMV_SCRATCH_BUF_SIZE_H264_ENC_V8(
mb_width,
@@ -202,10 +257,16 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
break;
case S5P_MFC_CODEC_MPEG4_ENC:
case S5P_MFC_CODEC_H263_ENC:
- ctx->scratch_buf_size =
- S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_ENC_V6(
- mb_width,
- mb_height);
+ if (IS_MFCV10(dev)) {
+ mfc_debug(2, "Use min scratch buffer size\n");
+ ctx->me_buffer_size =
+ ALIGN(ENC_V100_MPEG4_ME_SIZE(mb_width,
+ mb_height), 16);
+ } else
+ ctx->scratch_buf_size =
+ S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_ENC_V6(
+ mb_width,
+ mb_height);
ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size,
S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6);
ctx->bank1.size =
@@ -215,7 +276,12 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
ctx->bank2.size = 0;
break;
case S5P_MFC_CODEC_VP8_ENC:
- if (IS_MFCV8(dev))
+ if (IS_MFCV10(dev)) {
+ mfc_debug(2, "Use min scratch buffer size\n");
+ ctx->me_buffer_size =
+ ALIGN(ENC_V100_VP8_ME_SIZE(mb_width, mb_height),
+ 16);
+ } else if (IS_MFCV8_PLUS(dev))
ctx->scratch_buf_size =
S5P_FIMV_SCRATCH_BUF_SIZE_VP8_ENC_V8(
mb_width,
@@ -233,6 +299,17 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
ctx->chroma_dpb_size + ctx->me_buffer_size));
ctx->bank2.size = 0;
break;
+ case S5P_MFC_CODEC_HEVC_ENC:
+ mfc_debug(2, "Use min scratch buffer size\n");
+ ctx->me_buffer_size =
+ ALIGN(ENC_V100_HEVC_ME_SIZE(lcu_width, lcu_height), 16);
+ ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, 256);
+ ctx->bank1.size =
+ ctx->scratch_buf_size + ctx->tmv_buffer_size +
+ (ctx->pb_count * (ctx->luma_dpb_size +
+ ctx->chroma_dpb_size + ctx->me_buffer_size));
+ ctx->bank2.size = 0;
+ break;
default:
break;
}
@@ -267,6 +344,7 @@ static int s5p_mfc_alloc_instance_buffer_v6(struct s5p_mfc_ctx *ctx)
switch (ctx->codec_mode) {
case S5P_MFC_CODEC_H264_DEC:
case S5P_MFC_CODEC_H264_MVC_DEC:
+ case S5P_MFC_CODEC_HEVC_DEC:
ctx->ctx.size = buf_size->h264_dec_ctx;
break;
case S5P_MFC_CODEC_MPEG4_DEC:
@@ -275,11 +353,15 @@ static int s5p_mfc_alloc_instance_buffer_v6(struct s5p_mfc_ctx *ctx)
case S5P_MFC_CODEC_VC1_DEC:
case S5P_MFC_CODEC_MPEG2_DEC:
case S5P_MFC_CODEC_VP8_DEC:
+ case S5P_MFC_CODEC_VP9_DEC:
ctx->ctx.size = buf_size->other_dec_ctx;
break;
case S5P_MFC_CODEC_H264_ENC:
ctx->ctx.size = buf_size->h264_enc_ctx;
break;
+ case S5P_MFC_CODEC_HEVC_ENC:
+ ctx->ctx.size = buf_size->hevc_enc_ctx;
+ break;
case S5P_MFC_CODEC_MPEG4_ENC:
case S5P_MFC_CODEC_H263_ENC:
case S5P_MFC_CODEC_VP8_ENC:
@@ -356,6 +438,7 @@ static int calc_plane(int width, int height)
static void s5p_mfc_dec_calc_dpb_size_v6(struct s5p_mfc_ctx *ctx)
{
+ struct s5p_mfc_dev *dev = ctx->dev;
ctx->buf_width = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN_V6);
ctx->buf_height = ALIGN(ctx->img_height, S5P_FIMV_NV12MT_VALIGN_V6);
mfc_debug(2, "SEQ Done: Movie dimensions %dx%d,\n"
@@ -364,7 +447,7 @@ static void s5p_mfc_dec_calc_dpb_size_v6(struct s5p_mfc_ctx *ctx)
ctx->luma_size = calc_plane(ctx->img_width, ctx->img_height);
ctx->chroma_size = calc_plane(ctx->img_width, (ctx->img_height >> 1));
- if (IS_MFCV8(ctx->dev)) {
+ if (IS_MFCV8_PLUS(ctx->dev)) {
/* MFCv8 needs additional 64 bytes for luma,chroma dpb*/
ctx->luma_size += S5P_FIMV_D_ALIGN_PLANE_SIZE_V8;
ctx->chroma_size += S5P_FIMV_D_ALIGN_PLANE_SIZE_V8;
@@ -372,9 +455,17 @@ static void s5p_mfc_dec_calc_dpb_size_v6(struct s5p_mfc_ctx *ctx)
if (ctx->codec_mode == S5P_MFC_CODEC_H264_DEC ||
ctx->codec_mode == S5P_MFC_CODEC_H264_MVC_DEC) {
- ctx->mv_size = S5P_MFC_DEC_MV_SIZE_V6(ctx->img_width,
+ if (IS_MFCV10(dev)) {
+ ctx->mv_size = S5P_MFC_DEC_MV_SIZE_V10(ctx->img_width,
+ ctx->img_height);
+ } else {
+ ctx->mv_size = S5P_MFC_DEC_MV_SIZE_V6(ctx->img_width,
+ ctx->img_height);
+ }
+ } else if (ctx->codec_mode == S5P_MFC_CODEC_HEVC_DEC) {
+ ctx->mv_size = s5p_mfc_dec_hevc_mv_size(ctx->img_width,
ctx->img_height);
- ctx->mv_size = ALIGN(ctx->mv_size, 16);
+ ctx->mv_size = ALIGN(ctx->mv_size, 32);
} else {
ctx->mv_size = 0;
}
@@ -445,7 +536,7 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx)
writel(buf_addr1, mfc_regs->d_scratch_buffer_addr);
writel(ctx->scratch_buf_size, mfc_regs->d_scratch_buffer_size);
- if (IS_MFCV8(dev)) {
+ if (IS_MFCV8_PLUS(dev)) {
writel(ctx->img_width,
mfc_regs->d_first_plane_dpb_stride_size);
writel(ctx->img_width,
@@ -456,7 +547,8 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx)
buf_size1 -= ctx->scratch_buf_size;
if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC ||
- ctx->codec_mode == S5P_FIMV_CODEC_H264_MVC_DEC){
+ ctx->codec_mode == S5P_FIMV_CODEC_H264_MVC_DEC ||
+ ctx->codec_mode == S5P_FIMV_CODEC_HEVC_DEC) {
writel(ctx->mv_size, mfc_regs->d_mv_buffer_size);
writel(ctx->mv_count, mfc_regs->d_num_mv);
}
@@ -479,7 +571,8 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx)
mfc_regs->d_second_plane_dpb + i * 4);
}
if (ctx->codec_mode == S5P_MFC_CODEC_H264_DEC ||
- ctx->codec_mode == S5P_MFC_CODEC_H264_MVC_DEC) {
+ ctx->codec_mode == S5P_MFC_CODEC_H264_MVC_DEC ||
+ ctx->codec_mode == S5P_MFC_CODEC_HEVC_DEC) {
for (i = 0; i < ctx->mv_count; i++) {
/* To test alignment */
align_gap = buf_addr1;
@@ -494,6 +587,13 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx)
buf_size1 -= frame_size_mv;
}
}
+ if (ctx->codec_mode == S5P_FIMV_CODEC_VP9_DEC) {
+ writel(buf_addr1, mfc_regs->d_static_buffer_addr);
+ writel(DEC_VP9_STATIC_BUFFER_SIZE,
+ mfc_regs->d_static_buffer_size);
+ buf_addr1 += DEC_VP9_STATIC_BUFFER_SIZE;
+ buf_size1 -= DEC_VP9_STATIC_BUFFER_SIZE;
+ }
mfc_debug(2, "Buf1: %zx, buf_size1: %d (frames %d)\n",
buf_addr1, buf_size1, ctx->total_dpb_count);
@@ -571,15 +671,34 @@ static int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx)
mfc_debug(2, "Buf1: %p (%d)\n", (void *)buf_addr1, buf_size1);
- for (i = 0; i < ctx->pb_count; i++) {
- writel(buf_addr1, mfc_regs->e_luma_dpb + (4 * i));
- buf_addr1 += ctx->luma_dpb_size;
- writel(buf_addr1, mfc_regs->e_chroma_dpb + (4 * i));
- buf_addr1 += ctx->chroma_dpb_size;
- writel(buf_addr1, mfc_regs->e_me_buffer + (4 * i));
- buf_addr1 += ctx->me_buffer_size;
- buf_size1 -= (ctx->luma_dpb_size + ctx->chroma_dpb_size +
- ctx->me_buffer_size);
+ if (IS_MFCV10(dev)) {
+ /* start address of per buffer is aligned */
+ for (i = 0; i < ctx->pb_count; i++) {
+ writel(buf_addr1, mfc_regs->e_luma_dpb + (4 * i));
+ buf_addr1 += ctx->luma_dpb_size;
+ buf_size1 -= ctx->luma_dpb_size;
+ }
+ for (i = 0; i < ctx->pb_count; i++) {
+ writel(buf_addr1, mfc_regs->e_chroma_dpb + (4 * i));
+ buf_addr1 += ctx->chroma_dpb_size;
+ buf_size1 -= ctx->chroma_dpb_size;
+ }
+ for (i = 0; i < ctx->pb_count; i++) {
+ writel(buf_addr1, mfc_regs->e_me_buffer + (4 * i));
+ buf_addr1 += ctx->me_buffer_size;
+ buf_size1 -= ctx->me_buffer_size;
+ }
+ } else {
+ for (i = 0; i < ctx->pb_count; i++) {
+ writel(buf_addr1, mfc_regs->e_luma_dpb + (4 * i));
+ buf_addr1 += ctx->luma_dpb_size;
+ writel(buf_addr1, mfc_regs->e_chroma_dpb + (4 * i));
+ buf_addr1 += ctx->chroma_dpb_size;
+ writel(buf_addr1, mfc_regs->e_me_buffer + (4 * i));
+ buf_addr1 += ctx->me_buffer_size;
+ buf_size1 -= (ctx->luma_dpb_size + ctx->chroma_dpb_size
+ + ctx->me_buffer_size);
+ }
}
writel(buf_addr1, mfc_regs->e_scratch_buffer_addr);
@@ -1321,6 +1440,162 @@ static int s5p_mfc_set_enc_params_vp8(struct s5p_mfc_ctx *ctx)
return 0;
}
+static int s5p_mfc_set_enc_params_hevc(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
+ struct s5p_mfc_enc_params *p = &ctx->enc_params;
+ struct s5p_mfc_hevc_enc_params *p_hevc = &p->codec.hevc;
+ unsigned int reg = 0;
+ int i;
+
+ mfc_debug_enter();
+
+ s5p_mfc_set_enc_params(ctx);
+
+ /* pictype : number of B */
+ reg = readl(mfc_regs->e_gop_config);
+ /* num_b_frame - 0 ~ 2 */
+ reg &= ~(0x3 << 16);
+ reg |= (p->num_b_frame << 16);
+ writel(reg, mfc_regs->e_gop_config);
+
+ /* UHD encoding case */
+ if ((ctx->img_width == 3840) && (ctx->img_height == 2160)) {
+ p_hevc->level = 51;
+ p_hevc->tier = 0;
+ /* this tier can be changed */
+ }
+
+ /* tier & level */
+ reg = 0;
+ /* profile */
+ reg |= p_hevc->profile & 0x3;
+ /* level */
+ reg &= ~(0xFF << 8);
+ reg |= (p_hevc->level << 8);
+ /* tier - 0 ~ 1 */
+ reg |= (p_hevc->tier << 16);
+ writel(reg, mfc_regs->e_picture_profile);
+
+ switch (p_hevc->loopfilter) {
+ case V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED:
+ p_hevc->loopfilter_disable = 1;
+ break;
+ case V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_ENABLED:
+ p_hevc->loopfilter_disable = 0;
+ p_hevc->loopfilter_across = 1;
+ break;
+ case V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY:
+ p_hevc->loopfilter_disable = 0;
+ p_hevc->loopfilter_across = 0;
+ break;
+ }
+
+ /* max partition depth */
+ reg = 0;
+ reg |= (p_hevc->max_partition_depth & 0x1);
+ reg |= (p_hevc->num_refs_for_p-1) << 2;
+ reg |= (p_hevc->refreshtype & 0x3) << 3;
+ reg |= (p_hevc->const_intra_period_enable & 0x1) << 5;
+ reg |= (p_hevc->lossless_cu_enable & 0x1) << 6;
+ reg |= (p_hevc->wavefront_enable & 0x1) << 7;
+ reg |= (p_hevc->loopfilter_disable & 0x1) << 8;
+ reg |= (p_hevc->loopfilter_across & 0x1) << 9;
+ reg |= (p_hevc->enable_ltr & 0x1) << 10;
+ reg |= (p_hevc->hier_qp_enable & 0x1) << 11;
+ reg |= (p_hevc->general_pb_enable & 0x1) << 13;
+ reg |= (p_hevc->temporal_id_enable & 0x1) << 14;
+ reg |= (p_hevc->strong_intra_smooth & 0x1) << 15;
+ reg |= (p_hevc->intra_pu_split_disable & 0x1) << 16;
+ reg |= (p_hevc->tmv_prediction_disable & 0x1) << 17;
+ reg |= (p_hevc->max_num_merge_mv & 0x7) << 18;
+ reg |= (p_hevc->encoding_nostartcode_enable & 0x1) << 23;
+ reg |= (p_hevc->prepend_sps_pps_to_idr << 26);
+
+ writel(reg, mfc_regs->e_hevc_options);
+ /* refresh period */
+ if (p_hevc->refreshtype) {
+ reg = 0;
+ reg |= (p_hevc->refreshperiod & 0xFFFF);
+ writel(reg, mfc_regs->e_hevc_refresh_period);
+ }
+ /* loop filter setting */
+ if (!(p_hevc->loopfilter_disable & 0x1)) {
+ reg = 0;
+ reg |= (p_hevc->lf_beta_offset_div2);
+ writel(reg, mfc_regs->e_hevc_lf_beta_offset_div2);
+ reg = 0;
+ reg |= (p_hevc->lf_tc_offset_div2);
+ writel(reg, mfc_regs->e_hevc_lf_tc_offset_div2);
+ }
+ /* hier qp enable */
+ if (p_hevc->num_hier_layer) {
+ reg = 0;
+ reg |= (p_hevc->hier_qp_type & 0x1) << 0x3;
+ reg |= p_hevc->num_hier_layer & 0x7;
+ writel(reg, mfc_regs->e_num_t_layer);
+ /* QP value for each layer */
+ if (p_hevc->hier_qp_enable) {
+ for (i = 0; i < 7; i++)
+ writel(p_hevc->hier_qp_layer[i],
+ mfc_regs->e_hier_qp_layer0 + i * 4);
+ }
+ if (p->rc_frame) {
+ for (i = 0; i < 7; i++)
+ writel(p_hevc->hier_bit_layer[i],
+ mfc_regs->e_hier_bit_rate_layer0
+ + i * 4);
+ }
+ }
+
+ /* rate control config. */
+ reg = readl(mfc_regs->e_rc_config);
+ /* macroblock level rate control */
+ reg &= ~(0x1 << 8);
+ reg |= (p->rc_mb << 8);
+ writel(reg, mfc_regs->e_rc_config);
+ /* frame QP */
+ reg &= ~(0xFF);
+ reg |= p_hevc->rc_frame_qp;
+ writel(reg, mfc_regs->e_rc_config);
+
+ /* frame rate */
+ if (p->rc_frame) {
+ reg = 0;
+ reg &= ~(0xFFFF << 16);
+ reg |= ((p_hevc->rc_framerate) << 16);
+ reg &= ~(0xFFFF);
+ reg |= FRAME_DELTA_DEFAULT;
+ writel(reg, mfc_regs->e_rc_frame_rate);
+ }
+
+ /* max & min value of QP */
+ reg = 0;
+ /* max QP */
+ reg &= ~(0xFF << 8);
+ reg |= (p_hevc->rc_max_qp << 8);
+ /* min QP */
+ reg &= ~(0xFF);
+ reg |= p_hevc->rc_min_qp;
+ writel(reg, mfc_regs->e_rc_qp_bound);
+
+ writel(0x0, mfc_regs->e_fixed_picture_qp);
+ if (!p->rc_frame && !p->rc_mb) {
+ reg = 0;
+ reg &= ~(0xFF << 16);
+ reg |= (p_hevc->rc_b_frame_qp << 16);
+ reg &= ~(0xFF << 8);
+ reg |= (p_hevc->rc_p_frame_qp << 8);
+ reg &= ~(0xFF);
+ reg |= p_hevc->rc_frame_qp;
+ writel(reg, mfc_regs->e_fixed_picture_qp);
+ }
+ mfc_debug_leave();
+
+ return 0;
+}
+
/* Initialize decoding */
static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)
{
@@ -1440,6 +1715,8 @@ static int s5p_mfc_init_encode_v6(struct s5p_mfc_ctx *ctx)
s5p_mfc_set_enc_params_h263(ctx);
else if (ctx->codec_mode == S5P_MFC_CODEC_VP8_ENC)
s5p_mfc_set_enc_params_vp8(ctx);
+ else if (ctx->codec_mode == S5P_FIMV_CODEC_HEVC_ENC)
+ s5p_mfc_set_enc_params_hevc(ctx);
else {
mfc_err("Unknown codec for encoding (%x).\n",
ctx->codec_mode);
@@ -1895,6 +2172,16 @@ static int s5p_mfc_get_mv_count_v6(struct s5p_mfc_dev *dev)
return readl(dev->mfc_regs->d_min_num_mv);
}
+static int s5p_mfc_get_min_scratch_buf_size(struct s5p_mfc_dev *dev)
+{
+ return readl(dev->mfc_regs->d_min_scratch_buffer_size);
+}
+
+static int s5p_mfc_get_e_min_scratch_buf_size(struct s5p_mfc_dev *dev)
+{
+ return readl(dev->mfc_regs->e_min_scratch_buffer_size);
+}
+
static int s5p_mfc_get_inst_no_v6(struct s5p_mfc_dev *dev)
{
return readl(dev->mfc_regs->ret_instance_id);
@@ -2109,7 +2396,7 @@ const struct s5p_mfc_regs *s5p_mfc_init_regs_v6_plus(struct s5p_mfc_dev *dev)
S5P_FIMV_E_ENCODED_SOURCE_SECOND_ADDR_V7);
R(e_vp8_options, S5P_FIMV_E_VP8_OPTIONS_V7);
- if (!IS_MFCV8(dev))
+ if (!IS_MFCV8_PLUS(dev))
goto done;
/* Initialize registers used in MFC v8 only.
@@ -2153,6 +2440,7 @@ const struct s5p_mfc_regs *s5p_mfc_init_regs_v6_plus(struct s5p_mfc_dev *dev)
R(d_ret_picture_tag_bot, S5P_FIMV_D_RET_PICTURE_TAG_BOT_V8);
R(d_display_crop_info1, S5P_FIMV_D_DISPLAY_CROP_INFO1_V8);
R(d_display_crop_info2, S5P_FIMV_D_DISPLAY_CROP_INFO2_V8);
+ R(d_min_scratch_buffer_size, S5P_FIMV_D_MIN_SCRATCH_BUFFER_SIZE_V8);
/* encoder registers */
R(e_padding_ctrl, S5P_FIMV_E_PADDING_CTRL_V8);
@@ -2168,6 +2456,29 @@ const struct s5p_mfc_regs *s5p_mfc_init_regs_v6_plus(struct s5p_mfc_dev *dev)
R(e_aspect_ratio, S5P_FIMV_E_ASPECT_RATIO_V8);
R(e_extended_sar, S5P_FIMV_E_EXTENDED_SAR_V8);
R(e_h264_options, S5P_FIMV_E_H264_OPTIONS_V8);
+ R(e_min_scratch_buffer_size, S5P_FIMV_E_MIN_SCRATCH_BUFFER_SIZE_V8);
+
+ if (!IS_MFCV10(dev))
+ goto done;
+
+ /* Initialize registers used in MFC v10 only.
+ * Also, over-write the registers which have
+ * a different offset for MFC v10.
+ */
+
+ /* decoder registers */
+ R(d_static_buffer_addr, S5P_FIMV_D_STATIC_BUFFER_ADDR_V10);
+ R(d_static_buffer_size, S5P_FIMV_D_STATIC_BUFFER_SIZE_V10);
+
+ /* encoder registers */
+ R(e_num_t_layer, S5P_FIMV_E_NUM_T_LAYER_V10);
+ R(e_hier_qp_layer0, S5P_FIMV_E_HIERARCHICAL_QP_LAYER0_V10);
+ R(e_hier_bit_rate_layer0, S5P_FIMV_E_HIERARCHICAL_BIT_RATE_LAYER0_V10);
+ R(e_hevc_options, S5P_FIMV_E_HEVC_OPTIONS_V10);
+ R(e_hevc_refresh_period, S5P_FIMV_E_HEVC_REFRESH_PERIOD_V10);
+ R(e_hevc_lf_beta_offset_div2, S5P_FIMV_E_HEVC_LF_BETA_OFFSET_DIV2_V10);
+ R(e_hevc_lf_tc_offset_div2, S5P_FIMV_E_HEVC_LF_TC_OFFSET_DIV2_V10);
+ R(e_hevc_nal_control, S5P_FIMV_E_HEVC_NAL_CONTROL_V10);
done:
return &mfc_regs;
@@ -2216,6 +2527,8 @@ static struct s5p_mfc_hw_ops s5p_mfc_ops_v6 = {
.get_pic_type_bot = s5p_mfc_get_pic_type_bot_v6,
.get_crop_info_h = s5p_mfc_get_crop_info_h_v6,
.get_crop_info_v = s5p_mfc_get_crop_info_v_v6,
+ .get_min_scratch_buf_size = s5p_mfc_get_min_scratch_buf_size,
+ .get_e_min_scratch_buf_size = s5p_mfc_get_e_min_scratch_buf_size,
};
struct s5p_mfc_hw_ops *s5p_mfc_init_hw_ops_v6(void)
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.h b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.h
index 80558484bb40..f013b291ae5b 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.h
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.h
@@ -24,6 +24,13 @@
#define MB_HEIGHT(y_size) DIV_ROUND_UP(y_size, 16)
#define S5P_MFC_DEC_MV_SIZE_V6(x, y) (MB_WIDTH(x) * \
(((MB_HEIGHT(y)+1)/2)*2) * 64 + 128)
+#define S5P_MFC_DEC_MV_SIZE_V10(x, y) (MB_WIDTH(x) * \
+ (((MB_HEIGHT(y)+1)/2)*2) * 64 + 512)
+#define S5P_MFC_LCU_WIDTH(x_size) DIV_ROUND_UP(x_size, 32)
+#define S5P_MFC_LCU_HEIGHT(y_size) DIV_ROUND_UP(y_size, 32)
+
+#define s5p_mfc_dec_hevc_mv_size(x, y) \
+ (DIV_ROUND_UP(x, 64) * DIV_ROUND_UP(y, 64) * 256 + 512)
/* Definition */
#define ENC_MULTI_SLICE_MB_MAX ((1 << 30) - 1)
@@ -39,6 +46,14 @@
#define ENC_MPEG4_VOP_TIME_RES_MAX ((1 << 16) - 1)
#define FRAME_DELTA_H264_H263 1
#define TIGHT_CBR_MAX 10
+#define ENC_HEVC_RC_FRAME_RATE_MAX ((1 << 16) - 1)
+#define ENC_HEVC_QP_INDEX_MIN -12
+#define ENC_HEVC_QP_INDEX_MAX 12
+#define ENC_HEVC_LOOP_FILTER_MIN -12
+#define ENC_HEVC_LOOP_FILTER_MAX 12
+#define ENC_HEVC_LEVEL_MAX 62
+
+#define FRAME_DELTA_DEFAULT 1
struct s5p_mfc_hw_ops *s5p_mfc_init_hw_ops_v6(void);
const struct s5p_mfc_regs *s5p_mfc_init_regs_v6_plus(struct s5p_mfc_dev *dev);
diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c
index 976ea0bb5b6c..1a0cde017fdf 100644
--- a/drivers/media/platform/sh_veu.c
+++ b/drivers/media/platform/sh_veu.c
@@ -520,8 +520,8 @@ static void sh_veu_colour_offset(struct sh_veu_dev *veu, struct sh_veu_vfmt *vfm
/* dst_left and dst_top validity will be verified in CROP / COMPOSE */
unsigned int left = vfmt->frame.left & ~0x03;
unsigned int top = vfmt->frame.top;
- dma_addr_t offset = ((left * veu->vfmt_out.fmt->depth) >> 3) +
- top * veu->vfmt_out.bytesperline;
+ dma_addr_t offset = (dma_addr_t)top * veu->vfmt_out.bytesperline +
+ (((dma_addr_t)left * veu->vfmt_out.fmt->depth) >> 3);
unsigned int y_line;
vfmt->offset_y = offset;
diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
index c86dd2fdab84..69f0d8e80bd8 100644
--- a/drivers/media/platform/soc_camera/soc_camera.c
+++ b/drivers/media/platform/soc_camera/soc_camera.c
@@ -787,7 +787,7 @@ static int soc_camera_mmap(struct file *file, struct vm_area_struct *vma)
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
int err;
- dev_dbg(icd->pdev, "mmap called, vma=0x%08lx\n", (unsigned long)vma);
+ dev_dbg(icd->pdev, "mmap called, vma=%p\n", vma);
if (icd->streamer != file)
return -EBUSY;
@@ -1788,17 +1788,19 @@ static int default_s_selection(struct soc_camera_device *icd,
}
static int default_g_parm(struct soc_camera_device *icd,
- struct v4l2_streamparm *parm)
+ struct v4l2_streamparm *a)
{
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- return v4l2_subdev_call(sd, video, g_parm, parm);
+
+ return v4l2_g_parm_cap(icd->vdev, sd, a);
}
static int default_s_parm(struct soc_camera_device *icd,
- struct v4l2_streamparm *parm)
+ struct v4l2_streamparm *a)
{
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- return v4l2_subdev_call(sd, video, s_parm, parm);
+
+ return v4l2_s_parm_cap(icd->vdev, sd, a);
}
static int default_enum_framesizes(struct soc_camera_device *icd,
diff --git a/drivers/media/platform/stm32/stm32-cec.c b/drivers/media/platform/stm32/stm32-cec.c
index 0e5aa17bdd40..7c496bc1cf38 100644
--- a/drivers/media/platform/stm32/stm32-cec.c
+++ b/drivers/media/platform/stm32/stm32-cec.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* STM32 CEC driver
* Copyright (C) STMicroelectronics SA 2017
*
- * 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/clk.h>
diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c
index 9460b3080dca..2e1933d872ee 100644
--- a/drivers/media/platform/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/stm32/stm32-dcmi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Driver for STM32 Digital Camera Memory Interface
*
@@ -5,7 +6,6 @@
* Authors: Yannick Fertre <yannick.fertre@st.com>
* Hugues Fruchet <hugues.fruchet@st.com>
* for STMicroelectronics.
- * License terms: GNU General Public License (GPL), version 2
*
* This driver is based on atmel_isi.c
*
@@ -93,6 +93,11 @@ enum state {
#define MIN_HEIGHT 16U
#define MAX_HEIGHT 2048U
+#define MIN_JPEG_WIDTH 16U
+#define MAX_JPEG_WIDTH 2592U
+#define MIN_JPEG_HEIGHT 16U
+#define MAX_JPEG_HEIGHT 2592U
+
#define TIMEOUT_MS 1000
struct dcmi_graph_entity {
@@ -160,6 +165,7 @@ struct stm32_dcmi {
dma_cookie_t dma_cookie;
u32 misr;
int errors_count;
+ int overrun_count;
int buffers_count;
};
@@ -190,14 +196,67 @@ static inline void reg_clear(void __iomem *base, u32 reg, u32 mask)
static int dcmi_start_capture(struct stm32_dcmi *dcmi);
+static void dcmi_buffer_done(struct stm32_dcmi *dcmi,
+ struct dcmi_buf *buf,
+ size_t bytesused,
+ int err)
+{
+ struct vb2_v4l2_buffer *vbuf;
+
+ if (!buf)
+ return;
+
+ vbuf = &buf->vb;
+
+ vbuf->sequence = dcmi->sequence++;
+ vbuf->field = V4L2_FIELD_NONE;
+ vbuf->vb2_buf.timestamp = ktime_get_ns();
+ vb2_set_plane_payload(&vbuf->vb2_buf, 0, bytesused);
+ vb2_buffer_done(&vbuf->vb2_buf,
+ err ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+ dev_dbg(dcmi->dev, "buffer[%d] done seq=%d, bytesused=%zu\n",
+ vbuf->vb2_buf.index, vbuf->sequence, bytesused);
+
+ dcmi->buffers_count++;
+ dcmi->active = NULL;
+}
+
+static int dcmi_restart_capture(struct stm32_dcmi *dcmi)
+{
+ spin_lock_irq(&dcmi->irqlock);
+
+ if (dcmi->state != RUNNING) {
+ spin_unlock_irq(&dcmi->irqlock);
+ return -EINVAL;
+ }
+
+ /* Restart a new DMA transfer with next buffer */
+ if (list_empty(&dcmi->buffers)) {
+ dev_err(dcmi->dev, "%s: No more buffer queued, cannot capture buffer\n",
+ __func__);
+ dcmi->errors_count++;
+ dcmi->active = NULL;
+
+ spin_unlock_irq(&dcmi->irqlock);
+ return -EINVAL;
+ }
+
+ dcmi->active = list_entry(dcmi->buffers.next,
+ struct dcmi_buf, list);
+ list_del_init(&dcmi->active->list);
+
+ spin_unlock_irq(&dcmi->irqlock);
+
+ return dcmi_start_capture(dcmi);
+}
+
static void dcmi_dma_callback(void *param)
{
struct stm32_dcmi *dcmi = (struct stm32_dcmi *)param;
struct dma_chan *chan = dcmi->dma_chan;
struct dma_tx_state state;
enum dma_status status;
-
- spin_lock(&dcmi->irqlock);
+ struct dcmi_buf *buf = dcmi->active;
/* Check DMA status */
status = dmaengine_tx_status(chan, dcmi->dma_cookie, &state);
@@ -215,58 +274,18 @@ static void dcmi_dma_callback(void *param)
case DMA_COMPLETE:
dev_dbg(dcmi->dev, "%s: Received DMA_COMPLETE\n", __func__);
- if (dcmi->active) {
- struct dcmi_buf *buf = dcmi->active;
- struct vb2_v4l2_buffer *vbuf = &dcmi->active->vb;
-
- vbuf->sequence = dcmi->sequence++;
- vbuf->field = V4L2_FIELD_NONE;
- vbuf->vb2_buf.timestamp = ktime_get_ns();
- vb2_set_plane_payload(&vbuf->vb2_buf, 0, buf->size);
- vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE);
- dev_dbg(dcmi->dev, "buffer[%d] done seq=%d\n",
- vbuf->vb2_buf.index, vbuf->sequence);
-
- dcmi->buffers_count++;
- dcmi->active = NULL;
- }
-
- /* Restart a new DMA transfer with next buffer */
- if (dcmi->state == RUNNING) {
- if (list_empty(&dcmi->buffers)) {
- dev_err(dcmi->dev, "%s: No more buffer queued, cannot capture buffer",
- __func__);
- dcmi->errors_count++;
- dcmi->active = NULL;
-
- spin_unlock(&dcmi->irqlock);
- return;
- }
-
- dcmi->active = list_entry(dcmi->buffers.next,
- struct dcmi_buf, list);
-
- list_del_init(&dcmi->active->list);
-
- if (dcmi_start_capture(dcmi)) {
- dev_err(dcmi->dev, "%s: Cannot restart capture on DMA complete",
- __func__);
-
- spin_unlock(&dcmi->irqlock);
- return;
- }
-
- /* Enable capture */
- reg_set(dcmi->regs, DCMI_CR, CR_CAPTURE);
- }
+ /* Return buffer to V4L2 */
+ dcmi_buffer_done(dcmi, buf, buf->size, 0);
+ /* Restart capture */
+ if (dcmi_restart_capture(dcmi))
+ dev_err(dcmi->dev, "%s: Cannot restart capture on DMA complete\n",
+ __func__);
break;
default:
dev_err(dcmi->dev, "%s: Received unknown status\n", __func__);
break;
}
-
- spin_unlock(&dcmi->irqlock);
}
static int dcmi_start_dma(struct stm32_dcmi *dcmi,
@@ -359,11 +378,57 @@ static void dcmi_set_crop(struct stm32_dcmi *dcmi)
reg_set(dcmi->regs, DCMI_CR, CR_CROP);
}
+static void dcmi_process_jpeg(struct stm32_dcmi *dcmi)
+{
+ struct dma_tx_state state;
+ enum dma_status status;
+ struct dma_chan *chan = dcmi->dma_chan;
+ struct dcmi_buf *buf = dcmi->active;
+
+ if (!buf)
+ return;
+
+ /*
+ * Because of variable JPEG buffer size sent by sensor,
+ * DMA transfer never completes due to transfer size
+ * never reached.
+ * In order to ensure that all the JPEG data are transferred
+ * in active buffer memory, DMA is drained.
+ * Then DMA tx status gives the amount of data transferred
+ * to memory, which is then returned to V4L2 through the active
+ * buffer payload.
+ */
+
+ /* Drain DMA */
+ dmaengine_synchronize(chan);
+
+ /* Get DMA residue to get JPEG size */
+ status = dmaengine_tx_status(chan, dcmi->dma_cookie, &state);
+ if (status != DMA_ERROR && state.residue < buf->size) {
+ /* Return JPEG buffer to V4L2 with received JPEG buffer size */
+ dcmi_buffer_done(dcmi, buf, buf->size - state.residue, 0);
+ } else {
+ dcmi->errors_count++;
+ dev_err(dcmi->dev, "%s: Cannot get JPEG size from DMA\n",
+ __func__);
+ /* Return JPEG buffer to V4L2 in ERROR state */
+ dcmi_buffer_done(dcmi, buf, 0, -EIO);
+ }
+
+ /* Abort DMA operation */
+ dmaengine_terminate_all(dcmi->dma_chan);
+
+ /* Restart capture */
+ if (dcmi_restart_capture(dcmi))
+ dev_err(dcmi->dev, "%s: Cannot restart capture on JPEG received\n",
+ __func__);
+}
+
static irqreturn_t dcmi_irq_thread(int irq, void *arg)
{
struct stm32_dcmi *dcmi = arg;
- spin_lock(&dcmi->irqlock);
+ spin_lock_irq(&dcmi->irqlock);
/* Stop capture is required */
if (dcmi->state == STOPPING) {
@@ -373,50 +438,41 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg)
complete(&dcmi->complete);
- spin_unlock(&dcmi->irqlock);
+ spin_unlock_irq(&dcmi->irqlock);
return IRQ_HANDLED;
}
if ((dcmi->misr & IT_OVR) || (dcmi->misr & IT_ERR)) {
- /*
- * An overflow or an error has been detected,
- * stop current DMA transfert & restart it
- */
- dev_warn(dcmi->dev, "%s: Overflow or error detected\n",
- __func__);
-
dcmi->errors_count++;
- dmaengine_terminate_all(dcmi->dma_chan);
-
- reg_set(dcmi->regs, DCMI_ICR, IT_FRAME | IT_OVR | IT_ERR);
-
- dev_dbg(dcmi->dev, "Restarting capture after DCMI error\n");
-
- if (dcmi_start_capture(dcmi)) {
- dev_err(dcmi->dev, "%s: Cannot restart capture on overflow or error\n",
- __func__);
+ if (dcmi->misr & IT_OVR)
+ dcmi->overrun_count++;
+ }
- spin_unlock(&dcmi->irqlock);
- return IRQ_HANDLED;
- }
+ if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG &&
+ dcmi->misr & IT_FRAME) {
+ /* JPEG received */
+ spin_unlock_irq(&dcmi->irqlock);
+ dcmi_process_jpeg(dcmi);
+ return IRQ_HANDLED;
}
- spin_unlock(&dcmi->irqlock);
+ spin_unlock_irq(&dcmi->irqlock);
return IRQ_HANDLED;
}
static irqreturn_t dcmi_irq_callback(int irq, void *arg)
{
struct stm32_dcmi *dcmi = arg;
+ unsigned long flags;
- spin_lock(&dcmi->irqlock);
+ spin_lock_irqsave(&dcmi->irqlock, flags);
dcmi->misr = reg_read(dcmi->regs, DCMI_MIS);
/* Clear interrupt */
reg_set(dcmi->regs, DCMI_ICR, IT_FRAME | IT_OVR | IT_ERR);
- spin_unlock(&dcmi->irqlock);
+ spin_unlock_irqrestore(&dcmi->irqlock, flags);
return IRQ_WAKE_THREAD;
}
@@ -483,7 +539,7 @@ static int dcmi_buf_prepare(struct vb2_buffer *vb)
vb2_set_plane_payload(&buf->vb.vb2_buf, 0, buf->size);
- dev_dbg(dcmi->dev, "buffer[%d] phy=0x%pad size=%zu\n",
+ dev_dbg(dcmi->dev, "buffer[%d] phy=%pad size=%zu\n",
vb->index, &buf->paddr, buf->size);
}
@@ -495,29 +551,24 @@ static void dcmi_buf_queue(struct vb2_buffer *vb)
struct stm32_dcmi *dcmi = vb2_get_drv_priv(vb->vb2_queue);
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct dcmi_buf *buf = container_of(vbuf, struct dcmi_buf, vb);
- unsigned long flags = 0;
- spin_lock_irqsave(&dcmi->irqlock, flags);
+ spin_lock_irq(&dcmi->irqlock);
- if ((dcmi->state == RUNNING) && (!dcmi->active)) {
+ if (dcmi->state == RUNNING && !dcmi->active) {
dcmi->active = buf;
dev_dbg(dcmi->dev, "Starting capture on buffer[%d] queued\n",
buf->vb.vb2_buf.index);
- if (dcmi_start_capture(dcmi)) {
+ spin_unlock_irq(&dcmi->irqlock);
+ if (dcmi_start_capture(dcmi))
dev_err(dcmi->dev, "%s: Cannot restart capture on overflow or error\n",
__func__);
-
- spin_unlock_irqrestore(&dcmi->irqlock, flags);
- return;
- }
} else {
/* Enqueue to video buffers list */
list_add_tail(&buf->list, &dcmi->buffers);
+ spin_unlock_irq(&dcmi->irqlock);
}
-
- spin_unlock_irqrestore(&dcmi->irqlock, flags);
}
static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count)
@@ -529,7 +580,7 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count)
ret = clk_enable(dcmi->mclk);
if (ret) {
- dev_err(dcmi->dev, "%s: Failed to start streaming, cannot enable clock",
+ dev_err(dcmi->dev, "%s: Failed to start streaming, cannot enable clock\n",
__func__);
goto err_release_buffers;
}
@@ -578,6 +629,10 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count)
if (dcmi->do_crop)
dcmi_set_crop(dcmi);
+ /* Enable jpeg capture */
+ if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG)
+ reg_set(dcmi->regs, DCMI_CR, CR_CM);/* Snapshot mode */
+
/* Enable dcmi */
reg_set(dcmi->regs, DCMI_CR, CR_ENABLE);
@@ -585,6 +640,7 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count)
dcmi->sequence = 0;
dcmi->errors_count = 0;
+ dcmi->overrun_count = 0;
dcmi->buffers_count = 0;
dcmi->active = NULL;
@@ -603,20 +659,17 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count)
dev_dbg(dcmi->dev, "Start streaming, starting capture\n");
+ spin_unlock_irq(&dcmi->irqlock);
ret = dcmi_start_capture(dcmi);
if (ret) {
- dev_err(dcmi->dev, "%s: Start streaming failed, cannot start capture",
+ dev_err(dcmi->dev, "%s: Start streaming failed, cannot start capture\n",
__func__);
-
- spin_unlock_irq(&dcmi->irqlock);
goto err_subdev_streamoff;
}
/* Enable interruptions */
reg_set(dcmi->regs, DCMI_IER, IT_FRAME | IT_OVR | IT_ERR);
- spin_unlock_irq(&dcmi->irqlock);
-
return 0;
err_subdev_streamoff:
@@ -656,9 +709,12 @@ static void dcmi_stop_streaming(struct vb2_queue *vq)
/* Disable stream on the sub device */
ret = v4l2_subdev_call(dcmi->entity.subdev, video, s_stream, 0);
if (ret && ret != -ENOIOCTLCMD)
- dev_err(dcmi->dev, "stream off failed in subdev\n");
+ dev_err(dcmi->dev, "%s: Failed to stop streaming, subdev streamoff error (%d)\n",
+ __func__, ret);
+ spin_lock_irq(&dcmi->irqlock);
dcmi->state = STOPPING;
+ spin_unlock_irq(&dcmi->irqlock);
timeout = wait_for_completion_interruptible_timeout(&dcmi->complete,
time_ms);
@@ -672,7 +728,8 @@ static void dcmi_stop_streaming(struct vb2_queue *vq)
reg_clear(dcmi->regs, DCMI_CR, CR_ENABLE);
if (!timeout) {
- dev_err(dcmi->dev, "Timeout during stop streaming\n");
+ dev_err(dcmi->dev, "%s: Timeout during stop streaming\n",
+ __func__);
dcmi->state = STOPPED;
}
@@ -694,8 +751,13 @@ static void dcmi_stop_streaming(struct vb2_queue *vq)
clk_disable(dcmi->mclk);
- dev_dbg(dcmi->dev, "Stop streaming, errors=%d buffers=%d\n",
- dcmi->errors_count, dcmi->buffers_count);
+ if (dcmi->errors_count)
+ dev_warn(dcmi->dev, "Some errors found while streaming: errors=%d (overrun=%d), buffers=%d\n",
+ dcmi->errors_count, dcmi->overrun_count,
+ dcmi->buffers_count);
+ dev_dbg(dcmi->dev, "Stop streaming, errors=%d (overrun=%d), buffers=%d\n",
+ dcmi->errors_count, dcmi->overrun_count,
+ dcmi->buffers_count);
}
static const struct vb2_ops dcmi_video_qops = {
@@ -749,7 +811,7 @@ static void __find_outer_frame_size(struct stm32_dcmi *dcmi,
int h_err = (fsize->height - pix->height);
int err = w_err + h_err;
- if ((w_err >= 0) && (h_err >= 0) && (err < min_err)) {
+ if (w_err >= 0 && h_err >= 0 && err < min_err) {
min_err = err;
match = fsize;
}
@@ -771,6 +833,7 @@ static int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f,
struct v4l2_subdev_format format = {
.which = V4L2_SUBDEV_FORMAT_TRY,
};
+ bool do_crop;
int ret;
sd_fmt = find_format_by_fourcc(dcmi, pix->pixelformat);
@@ -780,10 +843,19 @@ static int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f,
}
/* Limit to hardware capabilities */
- pix->width = clamp(pix->width, MIN_WIDTH, MAX_WIDTH);
- pix->height = clamp(pix->height, MIN_HEIGHT, MAX_HEIGHT);
+ if (pix->pixelformat == V4L2_PIX_FMT_JPEG) {
+ pix->width = clamp(pix->width, MIN_JPEG_WIDTH, MAX_JPEG_WIDTH);
+ pix->height =
+ clamp(pix->height, MIN_JPEG_HEIGHT, MAX_JPEG_HEIGHT);
+ } else {
+ pix->width = clamp(pix->width, MIN_WIDTH, MAX_WIDTH);
+ pix->height = clamp(pix->height, MIN_HEIGHT, MAX_HEIGHT);
+ }
- if (dcmi->do_crop && dcmi->num_of_sd_framesizes) {
+ /* No crop if JPEG is requested */
+ do_crop = dcmi->do_crop && (pix->pixelformat != V4L2_PIX_FMT_JPEG);
+
+ if (do_crop && dcmi->num_of_sd_framesizes) {
struct dcmi_framesize outer_sd_fsize;
/*
* If crop is requested and sensor have discrete frame sizes,
@@ -807,7 +879,7 @@ static int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f,
sd_fsize.width = pix->width;
sd_fsize.height = pix->height;
- if (dcmi->do_crop) {
+ if (do_crop) {
struct v4l2_rect c = dcmi->crop;
struct v4l2_rect max_rect;
@@ -862,6 +934,10 @@ static int dcmi_set_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f)
if (ret)
return ret;
+ /* Disable crop if JPEG is requested */
+ if (pix->pixelformat == V4L2_PIX_FMT_JPEG)
+ dcmi->do_crop = false;
+
/* pix to mbus format */
v4l2_fill_mbus_format(mf, pix,
sd_format->mbus_code);
@@ -1084,10 +1160,10 @@ static int dcmi_s_selection(struct file *file, void *priv,
r.top = clamp_t(s32, r.top, 0, pix.height - r.height);
r.left = clamp_t(s32, r.left, 0, pix.width - r.width);
- if (!((r.top == dcmi->sd_bounds.top) &&
- (r.left == dcmi->sd_bounds.left) &&
- (r.width == dcmi->sd_bounds.width) &&
- (r.height == dcmi->sd_bounds.height))) {
+ if (!(r.top == dcmi->sd_bounds.top &&
+ r.left == dcmi->sd_bounds.left &&
+ r.width == dcmi->sd_bounds.width &&
+ r.height == dcmi->sd_bounds.height)) {
/* Crop if request is different than sensor resolution */
dcmi->do_crop = true;
dcmi->crop = r;
@@ -1167,6 +1243,22 @@ static int dcmi_enum_framesizes(struct file *file, void *fh,
return 0;
}
+static int dcmi_g_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *p)
+{
+ struct stm32_dcmi *dcmi = video_drvdata(file);
+
+ return v4l2_g_parm_cap(video_devdata(file), dcmi->entity.subdev, p);
+}
+
+static int dcmi_s_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *p)
+{
+ struct stm32_dcmi *dcmi = video_drvdata(file);
+
+ return v4l2_s_parm_cap(video_devdata(file), dcmi->entity.subdev, p);
+}
+
static int dcmi_enum_frameintervals(struct file *file, void *fh,
struct v4l2_frmivalenum *fival)
{
@@ -1269,6 +1361,9 @@ static const struct v4l2_ioctl_ops dcmi_ioctl_ops = {
.vidioc_g_input = dcmi_g_input,
.vidioc_s_input = dcmi_s_input,
+ .vidioc_g_parm = dcmi_g_parm,
+ .vidioc_s_parm = dcmi_s_parm,
+
.vidioc_enum_framesizes = dcmi_enum_framesizes,
.vidioc_enum_frameintervals = dcmi_enum_frameintervals,
@@ -1334,6 +1429,10 @@ static const struct dcmi_format dcmi_formats[] = {
.fourcc = V4L2_PIX_FMT_UYVY,
.mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
.bpp = 2,
+ }, {
+ .fourcc = V4L2_PIX_FMT_JPEG,
+ .mbus_code = MEDIA_BUS_FMT_JPEG_1X8,
+ .bpp = 1,
},
};
diff --git a/drivers/media/platform/via-camera.c b/drivers/media/platform/via-camera.c
index f77be9302120..e9a02639554b 100644
--- a/drivers/media/platform/via-camera.c
+++ b/drivers/media/platform/via-camera.c
@@ -1112,7 +1112,7 @@ static int viacam_g_parm(struct file *filp, void *priv,
int ret;
mutex_lock(&cam->lock);
- ret = sensor_call(cam, video, g_parm, parm);
+ ret = v4l2_g_parm_cap(video_devdata(filp), cam->sensor, parm);
mutex_unlock(&cam->lock);
parm->parm.capture.readbuffers = cam->n_cap_bufs;
return ret;
@@ -1125,7 +1125,7 @@ static int viacam_s_parm(struct file *filp, void *priv,
int ret;
mutex_lock(&cam->lock);
- ret = sensor_call(cam, video, s_parm, parm);
+ ret = v4l2_s_parm_cap(video_devdata(filp), cam->sensor, parm);
mutex_unlock(&cam->lock);
parm->parm.capture.readbuffers = cam->n_cap_bufs;
return ret;
diff --git a/drivers/media/platform/vimc/vimc-common.c b/drivers/media/platform/vimc/vimc-common.c
index 9d63c84a9876..617415c224fe 100644
--- a/drivers/media/platform/vimc/vimc-common.c
+++ b/drivers/media/platform/vimc/vimc-common.c
@@ -434,7 +434,9 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved,
v4l2_set_subdevdata(sd, ved);
/* Expose this subdev to user space */
- sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ if (sd->ctrl_handler)
+ sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS;
/* Initialize the media entity */
ret = media_entity_pads_init(&sd->entity, num_pads, ved->pads);
diff --git a/drivers/media/platform/vimc/vimc-debayer.c b/drivers/media/platform/vimc/vimc-debayer.c
index 4d663e89d33f..6e10b63ba9ec 100644
--- a/drivers/media/platform/vimc/vimc-debayer.c
+++ b/drivers/media/platform/vimc/vimc-debayer.c
@@ -533,7 +533,7 @@ static int vimc_deb_comp_bind(struct device *comp, struct device *master,
/* Initialize ved and sd */
ret = vimc_ent_sd_register(&vdeb->ved, &vdeb->sd, v4l2_dev,
pdata->entity_name,
- MEDIA_ENT_F_ATV_DECODER, 2,
+ MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV, 2,
(const unsigned long[2]) {MEDIA_PAD_FL_SINK,
MEDIA_PAD_FL_SOURCE},
&vimc_deb_ops);
diff --git a/drivers/media/platform/vimc/vimc-scaler.c b/drivers/media/platform/vimc/vimc-scaler.c
index e1602e0bc230..e583ec7a91da 100644
--- a/drivers/media/platform/vimc/vimc-scaler.c
+++ b/drivers/media/platform/vimc/vimc-scaler.c
@@ -395,7 +395,7 @@ static int vimc_sca_comp_bind(struct device *comp, struct device *master,
/* Initialize ved and sd */
ret = vimc_ent_sd_register(&vsca->ved, &vsca->sd, v4l2_dev,
pdata->entity_name,
- MEDIA_ENT_F_ATV_DECODER, 2,
+ MEDIA_ENT_F_PROC_VIDEO_SCALER, 2,
(const unsigned long[2]) {MEDIA_PAD_FL_SINK,
MEDIA_PAD_FL_SOURCE},
&vimc_sca_ops);
diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c
index 457e211514c6..605e2a2d5dd5 100644
--- a/drivers/media/platform/vimc/vimc-sensor.c
+++ b/drivers/media/platform/vimc/vimc-sensor.c
@@ -23,6 +23,7 @@
#include <linux/v4l2-mediabus.h>
#include <linux/vmalloc.h>
#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
#include <media/v4l2-subdev.h>
#include <media/tpg/v4l2-tpg.h>
@@ -284,11 +285,18 @@ static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable)
return 0;
}
+static struct v4l2_subdev_core_ops vimc_sen_core_ops = {
+ .log_status = v4l2_ctrl_subdev_log_status,
+ .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
+ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
static const struct v4l2_subdev_video_ops vimc_sen_video_ops = {
.s_stream = vimc_sen_s_stream,
};
static const struct v4l2_subdev_ops vimc_sen_ops = {
+ .core = &vimc_sen_core_ops,
.pad = &vimc_sen_pad_ops,
.video = &vimc_sen_video_ops,
};
@@ -378,7 +386,7 @@ static int vimc_sen_comp_bind(struct device *comp, struct device *master,
/* Initialize ved and sd */
ret = vimc_ent_sd_register(&vsen->ved, &vsen->sd, v4l2_dev,
pdata->entity_name,
- MEDIA_ENT_F_ATV_DECODER, 1,
+ MEDIA_ENT_F_CAM_SENSOR, 1,
(const unsigned long[1]) {MEDIA_PAD_FL_SOURCE},
&vimc_sen_ops);
if (ret)
diff --git a/drivers/media/platform/vivid/vivid-cec.c b/drivers/media/platform/vivid/vivid-cec.c
index b55d278d38a7..71105fa4c5f9 100644
--- a/drivers/media/platform/vivid/vivid-cec.c
+++ b/drivers/media/platform/vivid/vivid-cec.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* vivid-cec.c - A Virtual Video Test Driver, cec emulation
*
* Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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 <media/cec.h>
@@ -82,11 +70,18 @@ static void vivid_cec_pin_adap_events(struct cec_adapter *adap, ktime_t ts,
if (adap == NULL)
return;
- ts = ktime_sub_us(ts, (CEC_TIM_START_BIT_TOTAL +
- len * 10 * CEC_TIM_DATA_BIT_TOTAL));
- cec_queue_pin_cec_event(adap, false, ts);
+
+ /*
+ * Suffix ULL on constant 10 makes the expression
+ * CEC_TIM_START_BIT_TOTAL + 10ULL * len * CEC_TIM_DATA_BIT_TOTAL
+ * to be evaluated using 64-bit unsigned arithmetic (u64), which
+ * is what ktime_sub_us expects as second argument.
+ */
+ ts = ktime_sub_us(ts, CEC_TIM_START_BIT_TOTAL +
+ 10ULL * len * CEC_TIM_DATA_BIT_TOTAL);
+ cec_queue_pin_cec_event(adap, false, false, ts);
ts = ktime_add_us(ts, CEC_TIM_START_BIT_LOW);
- cec_queue_pin_cec_event(adap, true, ts);
+ cec_queue_pin_cec_event(adap, true, false, ts);
ts = ktime_add_us(ts, CEC_TIM_START_BIT_HIGH);
for (i = 0; i < 10 * len; i++) {
@@ -101,12 +96,12 @@ static void vivid_cec_pin_adap_events(struct cec_adapter *adap, ktime_t ts,
bit = cec_msg_is_broadcast(msg) ^ nacked;
break;
}
- cec_queue_pin_cec_event(adap, false, ts);
+ cec_queue_pin_cec_event(adap, false, false, ts);
if (bit)
ts = ktime_add_us(ts, CEC_TIM_DATA_BIT_1_LOW);
else
ts = ktime_add_us(ts, CEC_TIM_DATA_BIT_0_LOW);
- cec_queue_pin_cec_event(adap, true, ts);
+ cec_queue_pin_cec_event(adap, true, false, ts);
if (bit)
ts = ktime_add_us(ts, CEC_TIM_DATA_BIT_1_HIGH);
else
diff --git a/drivers/media/platform/vivid/vivid-cec.h b/drivers/media/platform/vivid/vivid-cec.h
index 3926b1422777..7524ed48a914 100644
--- a/drivers/media/platform/vivid/vivid-cec.h
+++ b/drivers/media/platform/vivid/vivid-cec.h
@@ -1,20 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* vivid-cec.h - A Virtual Video Test Driver, cec emulation
*
* Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
#ifdef CONFIG_VIDEO_VIVID_CEC
diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c
index 9f036c5f51b0..82ec216f2ad8 100644
--- a/drivers/media/platform/vivid/vivid-core.c
+++ b/drivers/media/platform/vivid/vivid-core.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* vivid-core.c - A Virtual Video Test Driver, core initialization
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/module.h>
diff --git a/drivers/media/platform/vivid/vivid-core.h b/drivers/media/platform/vivid/vivid-core.h
index c90e4a0ab94e..477c80a4d44c 100644
--- a/drivers/media/platform/vivid/vivid-core.h
+++ b/drivers/media/platform/vivid/vivid-core.h
@@ -1,20 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* vivid-core.h - core datastructures
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
#ifndef _VIVID_CORE_H_
diff --git a/drivers/media/platform/vivid/vivid-ctrls.c b/drivers/media/platform/vivid/vivid-ctrls.c
index 3f9d354827af..6b0bfa091592 100644
--- a/drivers/media/platform/vivid/vivid-ctrls.c
+++ b/drivers/media/platform/vivid/vivid-ctrls.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* vivid-ctrls.c - control support functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/errno.h>
@@ -1208,6 +1196,7 @@ static int vivid_radio_rx_s_ctrl(struct v4l2_ctrl *ctrl)
v4l2_ctrl_activate(dev->radio_rx_rds_ta, dev->radio_rx_rds_controls);
v4l2_ctrl_activate(dev->radio_rx_rds_tp, dev->radio_rx_rds_controls);
v4l2_ctrl_activate(dev->radio_rx_rds_ms, dev->radio_rx_rds_controls);
+ dev->radio_rx_dev.device_caps = dev->radio_rx_caps;
break;
case V4L2_CID_RDS_RECEPTION:
dev->radio_rx_rds_enabled = ctrl->val;
@@ -1282,6 +1271,7 @@ static int vivid_radio_tx_s_ctrl(struct v4l2_ctrl *ctrl)
dev->radio_tx_caps &= ~V4L2_CAP_READWRITE;
if (!dev->radio_tx_rds_controls)
dev->radio_tx_caps |= V4L2_CAP_READWRITE;
+ dev->radio_tx_dev.device_caps = dev->radio_tx_caps;
break;
case V4L2_CID_RDS_TX_PTY:
if (dev->radio_rx_rds_controls)
diff --git a/drivers/media/platform/vivid/vivid-ctrls.h b/drivers/media/platform/vivid/vivid-ctrls.h
index 9bcca9d56359..6fad5f5d0054 100644
--- a/drivers/media/platform/vivid/vivid-ctrls.h
+++ b/drivers/media/platform/vivid/vivid-ctrls.h
@@ -1,20 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* vivid-ctrls.h - control support functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
#ifndef _VIVID_CTRLS_H_
diff --git a/drivers/media/platform/vivid/vivid-kthread-cap.c b/drivers/media/platform/vivid/vivid-kthread-cap.c
index 6ca71aabb576..3fdb280c36ca 100644
--- a/drivers/media/platform/vivid/vivid-kthread-cap.c
+++ b/drivers/media/platform/vivid/vivid-kthread-cap.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* vivid-kthread-cap.h - video/vbi capture thread support functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/module.h>
diff --git a/drivers/media/platform/vivid/vivid-kthread-cap.h b/drivers/media/platform/vivid/vivid-kthread-cap.h
index 5b92fc9a0d04..0f43015306d6 100644
--- a/drivers/media/platform/vivid/vivid-kthread-cap.h
+++ b/drivers/media/platform/vivid/vivid-kthread-cap.h
@@ -1,20 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* vivid-kthread-cap.h - video/vbi capture thread support functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
#ifndef _VIVID_KTHREAD_CAP_H_
diff --git a/drivers/media/platform/vivid/vivid-kthread-out.c b/drivers/media/platform/vivid/vivid-kthread-out.c
index 98eed5889bc1..9981e7548019 100644
--- a/drivers/media/platform/vivid/vivid-kthread-out.c
+++ b/drivers/media/platform/vivid/vivid-kthread-out.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* vivid-kthread-out.h - video/vbi output thread support functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/module.h>
diff --git a/drivers/media/platform/vivid/vivid-kthread-out.h b/drivers/media/platform/vivid/vivid-kthread-out.h
index 2bf04a17b05d..d5bcf44bbaca 100644
--- a/drivers/media/platform/vivid/vivid-kthread-out.h
+++ b/drivers/media/platform/vivid/vivid-kthread-out.h
@@ -1,20 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* vivid-kthread-out.h - video/vbi output thread support functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
#ifndef _VIVID_KTHREAD_OUT_H_
diff --git a/drivers/media/platform/vivid/vivid-osd.c b/drivers/media/platform/vivid/vivid-osd.c
index bdc380b14e0c..bbbc1b6938a5 100644
--- a/drivers/media/platform/vivid/vivid-osd.c
+++ b/drivers/media/platform/vivid/vivid-osd.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* vivid-osd.c - osd support for testing overlays.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/module.h>
diff --git a/drivers/media/platform/vivid/vivid-osd.h b/drivers/media/platform/vivid/vivid-osd.h
index 57c9daa5940a..f9ac1af25dd3 100644
--- a/drivers/media/platform/vivid/vivid-osd.h
+++ b/drivers/media/platform/vivid/vivid-osd.h
@@ -1,20 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* vivid-osd.h - output overlay support functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
#ifndef _VIVID_OSD_H_
diff --git a/drivers/media/platform/vivid/vivid-radio-common.c b/drivers/media/platform/vivid/vivid-radio-common.c
index 78c1e920670a..7c8efe38ff5b 100644
--- a/drivers/media/platform/vivid/vivid-radio-common.c
+++ b/drivers/media/platform/vivid/vivid-radio-common.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* vivid-radio-common.c - common radio rx/tx support functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/errno.h>
diff --git a/drivers/media/platform/vivid/vivid-radio-common.h b/drivers/media/platform/vivid/vivid-radio-common.h
index 92fe589141b7..30a9900e5b2b 100644
--- a/drivers/media/platform/vivid/vivid-radio-common.h
+++ b/drivers/media/platform/vivid/vivid-radio-common.h
@@ -1,20 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* vivid-radio-common.h - common radio rx/tx support functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
#ifndef _VIVID_RADIO_COMMON_H_
diff --git a/drivers/media/platform/vivid/vivid-radio-rx.c b/drivers/media/platform/vivid/vivid-radio-rx.c
index f834f7df8cf9..1f86d7d4f72f 100644
--- a/drivers/media/platform/vivid/vivid-radio-rx.c
+++ b/drivers/media/platform/vivid/vivid-radio-rx.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* vivid-radio-rx.c - radio receiver support functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/errno.h>
@@ -259,7 +247,7 @@ int vivid_radio_rx_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
vt->rangehigh = FM_FREQ_RANGE_HIGH;
sig_qual = dev->radio_rx_sig_qual;
vt->signal = abs(sig_qual) > delta ? 0 :
- 0xffff - (abs(sig_qual) * 0xffff) / delta;
+ 0xffff - ((unsigned)abs(sig_qual) * 0xffff) / delta;
vt->afc = sig_qual > delta ? 0 : sig_qual;
if (abs(sig_qual) > delta)
vt->rxsubchans = 0;
diff --git a/drivers/media/platform/vivid/vivid-radio-rx.h b/drivers/media/platform/vivid/vivid-radio-rx.h
index 2b33edb60942..c9c7849f6f99 100644
--- a/drivers/media/platform/vivid/vivid-radio-rx.h
+++ b/drivers/media/platform/vivid/vivid-radio-rx.h
@@ -1,20 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* vivid-radio-rx.h - radio receiver support functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
#ifndef _VIVID_RADIO_RX_H_
diff --git a/drivers/media/platform/vivid/vivid-radio-tx.c b/drivers/media/platform/vivid/vivid-radio-tx.c
index 308b13f85dc0..1a3749ba5e7e 100644
--- a/drivers/media/platform/vivid/vivid-radio-tx.c
+++ b/drivers/media/platform/vivid/vivid-radio-tx.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* vivid-radio-tx.c - radio transmitter support functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/errno.h>
diff --git a/drivers/media/platform/vivid/vivid-radio-tx.h b/drivers/media/platform/vivid/vivid-radio-tx.h
index 3c3343d70cbc..c2bf1e7e634a 100644
--- a/drivers/media/platform/vivid/vivid-radio-tx.h
+++ b/drivers/media/platform/vivid/vivid-radio-tx.h
@@ -1,20 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* vivid-radio-tx.h - radio transmitter support functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
#ifndef _VIVID_RADIO_TX_H_
diff --git a/drivers/media/platform/vivid/vivid-rds-gen.c b/drivers/media/platform/vivid/vivid-rds-gen.c
index 996e35e28d37..39ca9a56448c 100644
--- a/drivers/media/platform/vivid/vivid-rds-gen.c
+++ b/drivers/media/platform/vivid/vivid-rds-gen.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* vivid-rds-gen.c - rds (radio data system) generator support functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/kernel.h>
diff --git a/drivers/media/platform/vivid/vivid-rds-gen.h b/drivers/media/platform/vivid/vivid-rds-gen.h
index e55e3b22b7ca..35ac5742302b 100644
--- a/drivers/media/platform/vivid/vivid-rds-gen.h
+++ b/drivers/media/platform/vivid/vivid-rds-gen.h
@@ -1,20 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* vivid-rds-gen.h - rds (radio data system) generator support functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
#ifndef _VIVID_RDS_GEN_H_
diff --git a/drivers/media/platform/vivid/vivid-sdr-cap.c b/drivers/media/platform/vivid/vivid-sdr-cap.c
index ebd7b9c4dd83..cfb7cb4d37a8 100644
--- a/drivers/media/platform/vivid/vivid-sdr-cap.c
+++ b/drivers/media/platform/vivid/vivid-sdr-cap.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* vivid-sdr-cap.c - software defined radio support functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/errno.h>
diff --git a/drivers/media/platform/vivid/vivid-sdr-cap.h b/drivers/media/platform/vivid/vivid-sdr-cap.h
index 43014b2733db..813c9248e5a7 100644
--- a/drivers/media/platform/vivid/vivid-sdr-cap.h
+++ b/drivers/media/platform/vivid/vivid-sdr-cap.h
@@ -1,20 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* vivid-sdr-cap.h - software defined radio support functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
#ifndef _VIVID_SDR_CAP_H_
diff --git a/drivers/media/platform/vivid/vivid-vbi-cap.c b/drivers/media/platform/vivid/vivid-vbi-cap.c
index d66ef95dd2b5..92a852955173 100644
--- a/drivers/media/platform/vivid/vivid-vbi-cap.c
+++ b/drivers/media/platform/vivid/vivid-vbi-cap.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* vivid-vbi-cap.c - vbi capture support functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/errno.h>
diff --git a/drivers/media/platform/vivid/vivid-vbi-cap.h b/drivers/media/platform/vivid/vivid-vbi-cap.h
index 2d8ea0bac743..91d2de01381c 100644
--- a/drivers/media/platform/vivid/vivid-vbi-cap.h
+++ b/drivers/media/platform/vivid/vivid-vbi-cap.h
@@ -1,20 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* vivid-vbi-cap.h - vbi capture support functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
#ifndef _VIVID_VBI_CAP_H_
diff --git a/drivers/media/platform/vivid/vivid-vbi-gen.c b/drivers/media/platform/vivid/vivid-vbi-gen.c
index 02c79d7cedab..acc98445a1fa 100644
--- a/drivers/media/platform/vivid/vivid-vbi-gen.c
+++ b/drivers/media/platform/vivid/vivid-vbi-gen.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* vivid-vbi-gen.c - vbi generator support functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/errno.h>
diff --git a/drivers/media/platform/vivid/vivid-vbi-gen.h b/drivers/media/platform/vivid/vivid-vbi-gen.h
index 8444abe905ea..2657a7f5571c 100644
--- a/drivers/media/platform/vivid/vivid-vbi-gen.h
+++ b/drivers/media/platform/vivid/vivid-vbi-gen.h
@@ -1,20 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* vivid-vbi-gen.h - vbi generator support functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
#ifndef _VIVID_VBI_GEN_H_
diff --git a/drivers/media/platform/vivid/vivid-vbi-out.c b/drivers/media/platform/vivid/vivid-vbi-out.c
index d2989195cf03..69486c130a7e 100644
--- a/drivers/media/platform/vivid/vivid-vbi-out.c
+++ b/drivers/media/platform/vivid/vivid-vbi-out.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* vivid-vbi-out.c - vbi output support functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/errno.h>
diff --git a/drivers/media/platform/vivid/vivid-vbi-out.h b/drivers/media/platform/vivid/vivid-vbi-out.h
index 6555ba9d2860..76584940cdaf 100644
--- a/drivers/media/platform/vivid/vivid-vbi-out.h
+++ b/drivers/media/platform/vivid/vivid-vbi-out.h
@@ -1,20 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* vivid-vbi-out.h - vbi output support functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
#ifndef _VIVID_VBI_OUT_H_
diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c b/drivers/media/platform/vivid/vivid-vid-cap.c
index 0fbbcde19f0d..1599159f2574 100644
--- a/drivers/media/platform/vivid/vivid-vid-cap.c
+++ b/drivers/media/platform/vivid/vivid-vid-cap.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* vivid-vid-cap.c - video capture support functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/errno.h>
@@ -573,9 +561,9 @@ int vivid_try_fmt_vid_cap(struct file *file, void *priv,
mp->field = vivid_field_cap(dev, mp->field);
if (vivid_is_webcam(dev)) {
const struct v4l2_frmsize_discrete *sz =
- v4l2_find_nearest_format(webcam_sizes,
- VIVID_WEBCAM_SIZES,
- mp->width, mp->height);
+ v4l2_find_nearest_size(webcam_sizes,
+ VIVID_WEBCAM_SIZES, width,
+ height, mp->width, mp->height);
w = sz->width;
h = sz->height;
diff --git a/drivers/media/platform/vivid/vivid-vid-cap.h b/drivers/media/platform/vivid/vivid-vid-cap.h
index 94079815dbc2..47d8b48820df 100644
--- a/drivers/media/platform/vivid/vivid-vid-cap.h
+++ b/drivers/media/platform/vivid/vivid-vid-cap.h
@@ -1,20 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* vivid-vid-cap.h - video capture support functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
#ifndef _VIVID_VID_CAP_H_
diff --git a/drivers/media/platform/vivid/vivid-vid-common.c b/drivers/media/platform/vivid/vivid-vid-common.c
index a651527d80db..e5914be0e12d 100644
--- a/drivers/media/platform/vivid/vivid-vid-common.c
+++ b/drivers/media/platform/vivid/vivid-vid-common.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* vivid-vid-common.c - common video support functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/errno.h>
@@ -874,7 +862,8 @@ int vidioc_g_edid(struct file *file, void *_fh,
return -EINVAL;
if (edid->start_block + edid->blocks > dev->edid_blocks)
edid->blocks = dev->edid_blocks - edid->start_block;
- cec_set_edid_phys_addr(dev->edid, dev->edid_blocks * 128, adap->phys_addr);
+ if (adap)
+ cec_set_edid_phys_addr(dev->edid, dev->edid_blocks * 128, adap->phys_addr);
memcpy(edid->edid, dev->edid + edid->start_block * 128, edid->blocks * 128);
return 0;
}
diff --git a/drivers/media/platform/vivid/vivid-vid-common.h b/drivers/media/platform/vivid/vivid-vid-common.h
index 4b6175eab8a2..29b6c0b40a1b 100644
--- a/drivers/media/platform/vivid/vivid-vid-common.h
+++ b/drivers/media/platform/vivid/vivid-vid-common.h
@@ -1,20 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* vivid-vid-common.h - common video support functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
#ifndef _VIVID_VID_COMMON_H_
diff --git a/drivers/media/platform/vivid/vivid-vid-out.c b/drivers/media/platform/vivid/vivid-vid-out.c
index 0b1b6218ede8..51fec66d8d45 100644
--- a/drivers/media/platform/vivid/vivid-vid-out.c
+++ b/drivers/media/platform/vivid/vivid-vid-out.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* vivid-vid-out.c - video output support functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/errno.h>
diff --git a/drivers/media/platform/vivid/vivid-vid-out.h b/drivers/media/platform/vivid/vivid-vid-out.h
index dfa84db184ed..e87aacf843c5 100644
--- a/drivers/media/platform/vivid/vivid-vid-out.h
+++ b/drivers/media/platform/vivid/vivid-vid-out.h
@@ -1,20 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* vivid-vid-out.h - video output support functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
#ifndef _VIVID_VID_OUT_H_
diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c
index 4257451f1bd8..0b86ed01e85d 100644
--- a/drivers/media/platform/vsp1/vsp1_dl.c
+++ b/drivers/media/platform/vsp1/vsp1_dl.c
@@ -509,7 +509,8 @@ static bool vsp1_dl_list_hw_update_pending(struct vsp1_dl_manager *dlm)
return !!(vsp1_read(vsp1, VI6_DL_BODY_SIZE)
& VI6_DL_BODY_SIZE_UPD);
else
- return !!(vsp1_read(vsp1, VI6_CMD(dlm->index) & VI6_CMD_UPDHDR));
+ return !!(vsp1_read(vsp1, VI6_CMD(dlm->index))
+ & VI6_CMD_UPDHDR);
}
static void vsp1_dl_list_hw_enqueue(struct vsp1_dl_list *dl)
diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c
index 7ce69f23f50a..b8fee1834253 100644
--- a/drivers/media/platform/vsp1/vsp1_drm.c
+++ b/drivers/media/platform/vsp1/vsp1_drm.c
@@ -27,6 +27,7 @@
#include "vsp1_pipe.h"
#include "vsp1_rwpf.h"
+#define BRU_NAME(e) (e)->type == VSP1_ENTITY_BRU ? "BRU" : "BRS"
/* -----------------------------------------------------------------------------
* Interrupt Handling
@@ -88,7 +89,6 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index,
struct vsp1_entity *next;
struct vsp1_dl_list *dl;
struct v4l2_subdev_format format;
- const char *bru_name;
unsigned long flags;
unsigned int i;
int ret;
@@ -99,7 +99,6 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index,
drm_pipe = &vsp1->drm->pipe[pipe_index];
pipe = &drm_pipe->pipe;
bru = to_bru(&pipe->bru->subdev);
- bru_name = pipe->bru->type == VSP1_ENTITY_BRU ? "BRU" : "BRS";
if (!cfg) {
/*
@@ -165,7 +164,7 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index,
dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on %s pad %u\n",
__func__, format.format.width, format.format.height,
- format.format.code, bru_name, i);
+ format.format.code, BRU_NAME(pipe->bru), i);
}
format.pad = pipe->bru->source_pad;
@@ -181,7 +180,7 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index,
dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on %s pad %u\n",
__func__, format.format.width, format.format.height,
- format.format.code, bru_name, i);
+ format.format.code, BRU_NAME(pipe->bru), i);
format.pad = RWPF_PAD_SINK;
ret = v4l2_subdev_call(&pipe->output->entity.subdev, pad, set_fmt, NULL,
@@ -473,9 +472,9 @@ static int vsp1_du_setup_rpf_pipe(struct vsp1_device *vsp1,
if (ret < 0)
return ret;
- dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on BRU pad %u\n",
+ dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on %s pad %u\n",
__func__, format.format.width, format.format.height,
- format.format.code, format.pad);
+ format.format.code, BRU_NAME(pipe->bru), format.pad);
sel.pad = bru_input;
sel.target = V4L2_SEL_TGT_COMPOSE;
@@ -486,10 +485,9 @@ static int vsp1_du_setup_rpf_pipe(struct vsp1_device *vsp1,
if (ret < 0)
return ret;
- dev_dbg(vsp1->dev,
- "%s: set selection (%u,%u)/%ux%u on BRU pad %u\n",
+ dev_dbg(vsp1->dev, "%s: set selection (%u,%u)/%ux%u on %s pad %u\n",
__func__, sel.r.left, sel.r.top, sel.r.width, sel.r.height,
- sel.pad);
+ BRU_NAME(pipe->bru), sel.pad);
return 0;
}
@@ -514,12 +512,9 @@ void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index)
struct vsp1_entity *entity;
struct vsp1_entity *next;
struct vsp1_dl_list *dl;
- const char *bru_name;
unsigned int i;
int ret;
- bru_name = pipe->bru->type == VSP1_ENTITY_BRU ? "BRU" : "BRS";
-
/* Prepare the display list. */
dl = vsp1_dl_list_get(pipe->output->dlm);
@@ -530,6 +525,15 @@ void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index)
struct vsp1_rwpf *rpf = vsp1->rpf[i];
unsigned int j;
+ /*
+ * Make sure we don't accept more inputs than the hardware can
+ * handle. This is a temporary fix to avoid display stall, we
+ * need to instead allocate the BRU or BRS to display pipelines
+ * dynamically based on the number of planes they each use.
+ */
+ if (pipe->num_inputs >= pipe->bru->source_pad)
+ pipe->inputs[i] = NULL;
+
if (!pipe->inputs[i])
continue;
@@ -561,7 +565,7 @@ void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index)
rpf->entity.sink_pad = i;
dev_dbg(vsp1->dev, "%s: connecting RPF.%u to %s:%u\n",
- __func__, rpf->entity.index, bru_name, i);
+ __func__, rpf->entity.index, BRU_NAME(pipe->bru), i);
ret = vsp1_du_setup_rpf_pipe(vsp1, pipe, rpf, i);
if (ret < 0)
diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c
index e6fa16d7fda8..704920753998 100644
--- a/drivers/media/platform/vsp1/vsp1_lif.c
+++ b/drivers/media/platform/vsp1/vsp1_lif.c
@@ -155,6 +155,18 @@ static void lif_configure(struct vsp1_entity *entity,
(obth << VI6_LIF_CTRL_OBTH_SHIFT) |
(format->code == 0 ? VI6_LIF_CTRL_CFMT : 0) |
VI6_LIF_CTRL_REQSEL | VI6_LIF_CTRL_LIF_EN);
+
+ /*
+ * On R-Car V3M the LIF0 buffer attribute register has to be set to a
+ * non-default value to guarantee proper operation (otherwise artifacts
+ * may appear on the output). The value required by the manual is not
+ * explained but is likely a buffer size or threshold.
+ */
+ if ((entity->vsp1->version & VI6_IP_VERSION_MASK) ==
+ (VI6_IP_VERSION_MODEL_VSPD_V3 | VI6_IP_VERSION_SOC_V3M))
+ vsp1_lif_write(lif, dl, VI6_LIF_LBA,
+ VI6_LIF_LBA_LBA0 |
+ (1536 << VI6_LIF_LBA_LBA1_SHIFT));
}
static const struct vsp1_entity_operations lif_entity_ops = {
diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h
index 26c4ffad2f46..dae0c1901297 100644
--- a/drivers/media/platform/vsp1/vsp1_regs.h
+++ b/drivers/media/platform/vsp1/vsp1_regs.h
@@ -225,7 +225,7 @@
#define VI6_RPF_MULT_ALPHA_P_MMD_RATIO (1 << 8)
#define VI6_RPF_MULT_ALPHA_P_MMD_IMAGE (2 << 8)
#define VI6_RPF_MULT_ALPHA_P_MMD_BOTH (3 << 8)
-#define VI6_RPF_MULT_ALPHA_RATIO_MASK (0xff < 0)
+#define VI6_RPF_MULT_ALPHA_RATIO_MASK (0xff << 0)
#define VI6_RPF_MULT_ALPHA_RATIO_SHIFT 0
/* -----------------------------------------------------------------------------
@@ -693,6 +693,11 @@
#define VI6_LIF_CSBTH_LBTH_MASK (0x7ff << 0)
#define VI6_LIF_CSBTH_LBTH_SHIFT 0
+#define VI6_LIF_LBA 0x3b0c
+#define VI6_LIF_LBA_LBA0 (1 << 31)
+#define VI6_LIF_LBA_LBA1_MASK (0xfff << 16)
+#define VI6_LIF_LBA_LBA1_SHIFT 16
+
/* -----------------------------------------------------------------------------
* Security Control Registers
*/
@@ -705,6 +710,7 @@
*/
#define VI6_IP_VERSION 0x3f00
+#define VI6_IP_VERSION_MASK (0xffff << 0)
#define VI6_IP_VERSION_MODEL_MASK (0xff << 8)
#define VI6_IP_VERSION_MODEL_VSPS_H2 (0x09 << 8)
#define VI6_IP_VERSION_MODEL_VSPR_H2 (0x0a << 8)
diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c
index f7f3b4b2c2de..8bd6b2f1af15 100644
--- a/drivers/media/platform/vsp1/vsp1_wpf.c
+++ b/drivers/media/platform/vsp1/vsp1_wpf.c
@@ -452,7 +452,7 @@ static void wpf_configure(struct vsp1_entity *entity,
: VI6_WPF_SRCRPF_RPF_ACT_SUB(input->entity.index);
}
- if (pipe->bru || pipe->num_inputs > 1)
+ if (pipe->bru)
srcrpf |= pipe->bru->type == VSP1_ENTITY_BRU
? VI6_WPF_SRCRPF_VIRACT_MST
: VI6_WPF_SRCRPF_VIRACT2_MST;
diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c
index dc6c4f985911..0f292c6ba338 100644
--- a/drivers/media/radio/radio-mr800.c
+++ b/drivers/media/radio/radio-mr800.c
@@ -511,7 +511,7 @@ static int usb_amradio_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct amradio_device *radio;
- int retval = 0;
+ int retval;
radio = kzalloc(sizeof(struct amradio_device), GFP_KERNEL);
diff --git a/drivers/media/radio/radio-raremono.c b/drivers/media/radio/radio-raremono.c
index 70a2c86774ce..9a5079d64c4a 100644
--- a/drivers/media/radio/radio-raremono.c
+++ b/drivers/media/radio/radio-raremono.c
@@ -1,18 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2013 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/kernel.h>
diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c
index 58e944591602..8f9f8dfc3497 100644
--- a/drivers/media/radio/radio-wl1273.c
+++ b/drivers/media/radio/radio-wl1273.c
@@ -671,7 +671,7 @@ fail:
static int wl1273_fm_suspend(struct wl1273_device *radio)
{
struct wl1273_core *core = radio->core;
- int r = 0;
+ int r;
/* Cannot go from OFF to SUSPENDED */
if (core->mode == WL1273_MODE_RX)
diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c
index e0054e0f410d..b94d66e53d4e 100644
--- a/drivers/media/radio/si470x/radio-si470x-common.c
+++ b/drivers/media/radio/si470x/radio-si470x-common.c
@@ -207,6 +207,15 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
unsigned long time_left;
bool timed_out = false;
+ retval = si470x_get_register(radio, POWERCFG);
+ if (retval)
+ return retval;
+
+ if ((radio->registers[POWERCFG] & (POWERCFG_ENABLE|POWERCFG_DMUTE))
+ != (POWERCFG_ENABLE|POWERCFG_DMUTE)) {
+ return 0;
+ }
+
/* start tuning */
radio->registers[CHANNEL] &= ~CHANNEL_CHAN;
radio->registers[CHANNEL] |= CHANNEL_TUNE | chan;
@@ -377,8 +386,12 @@ int si470x_start(struct si470x_device *radio)
goto done;
/* sysconfig 1 */
- radio->registers[SYSCONFIG1] =
- (de << 11) & SYSCONFIG1_DE; /* DE*/
+ radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDSIEN | SYSCONFIG1_STCIEN |
+ SYSCONFIG1_RDS;
+ radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_GPIO2;
+ radio->registers[SYSCONFIG1] |= SYSCONFIG1_GPIO2_INT;
+ if (de)
+ radio->registers[SYSCONFIG1] |= SYSCONFIG1_DE;
retval = si470x_set_register(radio, SYSCONFIG1);
if (retval < 0)
goto done;
diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c
index b3034f80163f..41709b24b28f 100644
--- a/drivers/media/radio/si470x/radio-si470x-i2c.c
+++ b/drivers/media/radio/si470x/radio-si470x-i2c.c
@@ -43,7 +43,6 @@ static const struct i2c_device_id si470x_i2c_id[] = {
MODULE_DEVICE_TABLE(i2c, si470x_i2c_id);
-
/**************************************************************************
* Module Parameters
**************************************************************************/
@@ -362,22 +361,43 @@ static int si470x_i2c_probe(struct i2c_client *client,
mutex_init(&radio->lock);
init_completion(&radio->completion);
+ retval = v4l2_device_register(&client->dev, &radio->v4l2_dev);
+ if (retval < 0) {
+ dev_err(&client->dev, "couldn't register v4l2_device\n");
+ goto err_radio;
+ }
+
+ v4l2_ctrl_handler_init(&radio->hdl, 2);
+ v4l2_ctrl_new_std(&radio->hdl, &si470x_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+ v4l2_ctrl_new_std(&radio->hdl, &si470x_ctrl_ops,
+ V4L2_CID_AUDIO_VOLUME, 0, 15, 1, 15);
+ if (radio->hdl.error) {
+ retval = radio->hdl.error;
+ dev_err(&client->dev, "couldn't register control\n");
+ goto err_dev;
+ }
+
/* video device initialization */
radio->videodev = si470x_viddev_template;
+ radio->videodev.ctrl_handler = &radio->hdl;
+ radio->videodev.lock = &radio->lock;
+ radio->videodev.v4l2_dev = &radio->v4l2_dev;
+ radio->videodev.release = video_device_release_empty;
video_set_drvdata(&radio->videodev, radio);
/* power up : need 110ms */
radio->registers[POWERCFG] = POWERCFG_ENABLE;
if (si470x_set_register(radio, POWERCFG) < 0) {
retval = -EIO;
- goto err_radio;
+ goto err_ctrl;
}
msleep(110);
/* get device and chip versions */
if (si470x_get_all_registers(radio) < 0) {
retval = -EIO;
- goto err_radio;
+ goto err_ctrl;
}
dev_info(&client->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n",
radio->registers[DEVICEID], radio->registers[SI_CHIPID]);
@@ -407,7 +427,7 @@ static int si470x_i2c_probe(struct i2c_client *client,
radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL);
if (!radio->buffer) {
retval = -EIO;
- goto err_radio;
+ goto err_ctrl;
}
/* rds buffer configuration */
@@ -437,6 +457,10 @@ err_all:
free_irq(client->irq, radio);
err_rds:
kfree(radio->buffer);
+err_ctrl:
+ v4l2_ctrl_handler_free(&radio->hdl);
+err_dev:
+ v4l2_device_unregister(&radio->v4l2_dev);
err_radio:
kfree(radio);
err_initial:
diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c
index c311f9951d80..2277e850bb5e 100644
--- a/drivers/media/radio/si470x/radio-si470x-usb.c
+++ b/drivers/media/radio/si470x/radio-si470x-usb.c
@@ -578,7 +578,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
struct si470x_device *radio;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
- int i, int_end_size, retval = 0;
+ int i, int_end_size, retval;
unsigned char version_warning = 0;
/* private data allocation and initialization */
diff --git a/drivers/media/radio/si470x/radio-si470x.h b/drivers/media/radio/si470x/radio-si470x.h
index eb7b834a0ae5..0202f8eb90c4 100644
--- a/drivers/media/radio/si470x/radio-si470x.h
+++ b/drivers/media/radio/si470x/radio-si470x.h
@@ -79,6 +79,8 @@
#define SYSCONFIG1_BLNDADJ 0x00c0 /* bits 07..06: Stereo/Mono Blend Level Adjustment */
#define SYSCONFIG1_GPIO3 0x0030 /* bits 05..04: General Purpose I/O 3 */
#define SYSCONFIG1_GPIO2 0x000c /* bits 03..02: General Purpose I/O 2 */
+#define SYSCONFIG1_GPIO2_DIS 0x0000 /* Disable GPIO 2 interrupt */
+#define SYSCONFIG1_GPIO2_INT 0x0004 /* Enable STC/RDS interrupt */
#define SYSCONFIG1_GPIO1 0x0003 /* bits 01..00: General Purpose I/O 1 */
#define SYSCONFIG2 5 /* System Configuration 2 */
diff --git a/drivers/media/radio/si4713/radio-usb-si4713.c b/drivers/media/radio/si4713/radio-usb-si4713.c
index a115db24667b..05c66701a899 100644
--- a/drivers/media/radio/si4713/radio-usb-si4713.c
+++ b/drivers/media/radio/si4713/radio-usb-si4713.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2013 Cisco Systems, Inc. and/or its affiliates.
* All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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.
*/
/* kernel includes */
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index f14ead5954e0..eb2c3b6eca7f 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -28,14 +28,12 @@ config LIRC
menuconfig RC_DECODERS
bool "Remote controller decoders"
depends on RC_CORE
- default y
if RC_DECODERS
config IR_NEC_DECODER
tristate "Enable IR raw decoder for the NEC protocol"
depends on RC_CORE
select BITREVERSE
- default y
---help---
Enable this option if you have IR with NEC protocol, and
@@ -45,7 +43,6 @@ config IR_RC5_DECODER
tristate "Enable IR raw decoder for the RC-5 protocol"
depends on RC_CORE
select BITREVERSE
- default y
---help---
Enable this option if you have IR with RC-5 protocol, and
@@ -55,7 +52,6 @@ config IR_RC6_DECODER
tristate "Enable IR raw decoder for the RC6 protocol"
depends on RC_CORE
select BITREVERSE
- default y
---help---
Enable this option if you have an infrared remote control which
@@ -65,7 +61,6 @@ config IR_JVC_DECODER
tristate "Enable IR raw decoder for the JVC protocol"
depends on RC_CORE
select BITREVERSE
- default y
---help---
Enable this option if you have an infrared remote control which
@@ -75,7 +70,6 @@ config IR_SONY_DECODER
tristate "Enable IR raw decoder for the Sony protocol"
depends on RC_CORE
select BITREVERSE
- default y
---help---
Enable this option if you have an infrared remote control which
@@ -84,7 +78,6 @@ config IR_SONY_DECODER
config IR_SANYO_DECODER
tristate "Enable IR raw decoder for the Sanyo protocol"
depends on RC_CORE
- default y
---help---
Enable this option if you have an infrared remote control which
@@ -94,7 +87,6 @@ config IR_SANYO_DECODER
config IR_SHARP_DECODER
tristate "Enable IR raw decoder for the Sharp protocol"
depends on RC_CORE
- default y
---help---
Enable this option if you have an infrared remote control which
@@ -105,7 +97,6 @@ config IR_MCE_KBD_DECODER
tristate "Enable IR raw decoder for the MCE keyboard/mouse protocol"
depends on RC_CORE
select BITREVERSE
- default y
---help---
Enable this option if you have a Microsoft Remote Keyboard for
@@ -116,11 +107,19 @@ config IR_XMP_DECODER
tristate "Enable IR raw decoder for the XMP protocol"
depends on RC_CORE
select BITREVERSE
- default y
---help---
Enable this option if you have IR with XMP protocol, and
if the IR is decoded in software
+
+config IR_IMON_DECODER
+ tristate "Enable IR raw decoder for the iMON protocol"
+ depends on RC_CORE
+ ---help---
+ Enable this option if you have iMON PAD or Antec Veris infrared
+ remote control and you would like to use it with a raw IR
+ receiver, or if you wish to use an encoder to transmit this IR.
+
endif #RC_DECODERS
menuconfig RC_DEVICES
@@ -185,6 +184,18 @@ config IR_IMON
To compile this driver as a module, choose M here: the
module will be called imon.
+config IR_IMON_RAW
+ tristate "SoundGraph iMON Receiver (early raw IR models)"
+ depends on USB_ARCH_HAS_HCD
+ depends on RC_CORE
+ select USB
+ ---help---
+ Say Y here if you want to use a SoundGraph iMON IR Receiver,
+ early raw models.
+
+ To compile this driver as a module, choose M here: the
+ module will be called imon_raw.
+
config IR_MCEUSB
tristate "Windows Media Center Ed. eHome Infrared Transceiver"
depends on USB_ARCH_HAS_HCD
@@ -446,7 +457,6 @@ config IR_SERIAL
config IR_SERIAL_TRANSMITTER
bool "Serial Port Transmitter"
- default y
depends on IR_SERIAL
---help---
Serial Port Transmitter support
diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
index 0e857816ac2d..2e1c87066f6c 100644
--- a/drivers/media/rc/Makefile
+++ b/drivers/media/rc/Makefile
@@ -14,11 +14,13 @@ obj-$(CONFIG_IR_SANYO_DECODER) += ir-sanyo-decoder.o
obj-$(CONFIG_IR_SHARP_DECODER) += ir-sharp-decoder.o
obj-$(CONFIG_IR_MCE_KBD_DECODER) += ir-mce_kbd-decoder.o
obj-$(CONFIG_IR_XMP_DECODER) += ir-xmp-decoder.o
+obj-$(CONFIG_IR_IMON_DECODER) += ir-imon-decoder.o
# stand-alone IR receivers/transmitters
obj-$(CONFIG_RC_ATI_REMOTE) += ati_remote.o
obj-$(CONFIG_IR_HIX5HD2) += ir-hix5hd2.o
obj-$(CONFIG_IR_IMON) += imon.o
+obj-$(CONFIG_IR_IMON_RAW) += imon_raw.o
obj-$(CONFIG_IR_ITE_CIR) += ite-cir.o
obj-$(CONFIG_IR_MCEUSB) += mceusb.o
obj-$(CONFIG_IR_FINTEK) += fintek-cir.o
diff --git a/drivers/media/rc/img-ir/Kconfig b/drivers/media/rc/img-ir/Kconfig
index a896d3c83a1c..d2c6617d468e 100644
--- a/drivers/media/rc/img-ir/Kconfig
+++ b/drivers/media/rc/img-ir/Kconfig
@@ -1,7 +1,7 @@
config IR_IMG
tristate "ImgTec IR Decoder"
depends on RC_CORE
- depends on METAG || MIPS || COMPILE_TEST
+ depends on MIPS || COMPILE_TEST
select IR_IMG_HW if !IR_IMG_RAW
help
Say Y or M here if you want to use the ImgTec infrared decoder
diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c
index 950d068ba806..1041c056854d 100644
--- a/drivers/media/rc/imon.c
+++ b/drivers/media/rc/imon.c
@@ -92,7 +92,6 @@ struct imon_usb_dev_descr {
__u16 flags;
#define IMON_NO_FLAGS 0
#define IMON_NEED_20MS_PKT_DELAY 1
-#define IMON_IR_RAW 2
struct imon_panel_key_table key_table[];
};
@@ -123,12 +122,6 @@ struct imon_context {
unsigned char usb_tx_buf[8];
unsigned int send_packet_delay;
- struct rx_data {
- int count; /* length of 0 or 1 sequence */
- int prev_bit; /* logic level of sequence */
- int initial_space; /* initial space flag */
- } rx;
-
struct tx_t {
unsigned char data_buf[35]; /* user data buffer */
struct completion finished; /* wait for write to finish */
@@ -331,10 +324,6 @@ static const struct imon_usb_dev_descr imon_DH102 = {
}
};
-static const struct imon_usb_dev_descr imon_ir_raw = {
- .flags = IMON_IR_RAW,
-};
-
/*
* USB Device ID for iMON USB Control Boards
*
@@ -418,18 +407,6 @@ static const struct usb_device_id imon_usb_id_table[] = {
/* device specifics unknown */
{ USB_DEVICE(0x15c2, 0x0046),
.driver_info = (unsigned long)&imon_default_table},
- /* TriGem iMON (IR only) -- TG_iMON.inf */
- { USB_DEVICE(0x0aa8, 0x8001),
- .driver_info = (unsigned long)&imon_ir_raw},
- /* SoundGraph iMON (IR only) -- sg_imon.inf */
- { USB_DEVICE(0x04e8, 0xff30),
- .driver_info = (unsigned long)&imon_ir_raw},
- /* SoundGraph iMON VFD (IR & VFD) -- iMON_VFD.inf */
- { USB_DEVICE(0x0aa8, 0xffda),
- .driver_info = (unsigned long)&imon_ir_raw},
- /* SoundGraph iMON SS (IR & VFD) -- iMON_SS.inf */
- { USB_DEVICE(0x15c2, 0xffda),
- .driver_info = (unsigned long)&imon_ir_raw},
{}
};
@@ -1133,18 +1110,18 @@ static int imon_ir_change_protocol(struct rc_dev *rc, u64 *rc_proto)
dev_dbg(dev, "Configuring IR receiver for MCE protocol\n");
ir_proto_packet[0] = 0x01;
*rc_proto = RC_PROTO_BIT_RC6_MCE;
- } else if (*rc_proto & RC_PROTO_BIT_OTHER) {
+ } else if (*rc_proto & RC_PROTO_BIT_IMON) {
dev_dbg(dev, "Configuring IR receiver for iMON protocol\n");
if (!pad_stabilize)
dev_dbg(dev, "PAD stabilize functionality disabled\n");
/* ir_proto_packet[0] = 0x00; // already the default */
- *rc_proto = RC_PROTO_BIT_OTHER;
+ *rc_proto = RC_PROTO_BIT_IMON;
} else {
dev_warn(dev, "Unsupported IR protocol specified, overriding to iMON IR protocol\n");
if (!pad_stabilize)
dev_dbg(dev, "PAD stabilize functionality disabled\n");
/* ir_proto_packet[0] = 0x00; // already the default */
- *rc_proto = RC_PROTO_BIT_OTHER;
+ *rc_proto = RC_PROTO_BIT_IMON;
}
memcpy(ictx->usb_tx_buf, &ir_proto_packet, sizeof(ir_proto_packet));
@@ -1411,7 +1388,7 @@ static void imon_pad_to_keys(struct imon_context *ictx, unsigned char *buf)
rel_x = buf[2];
rel_y = buf[3];
- if (ictx->rc_proto == RC_PROTO_BIT_OTHER && pad_stabilize) {
+ if (ictx->rc_proto == RC_PROTO_BIT_IMON && pad_stabilize) {
if ((buf[1] == 0) && ((rel_x != 0) || (rel_y != 0))) {
dir = stabilize((int)rel_x, (int)rel_y,
timeout, threshold);
@@ -1478,7 +1455,7 @@ static void imon_pad_to_keys(struct imon_context *ictx, unsigned char *buf)
buf[0] = 0x01;
buf[1] = buf[4] = buf[5] = buf[6] = buf[7] = 0;
- if (ictx->rc_proto == RC_PROTO_BIT_OTHER && pad_stabilize) {
+ if (ictx->rc_proto == RC_PROTO_BIT_IMON && pad_stabilize) {
dir = stabilize((int)rel_x, (int)rel_y,
timeout, threshold);
if (!dir) {
@@ -1572,94 +1549,11 @@ static int imon_parse_press_type(struct imon_context *ictx,
/*
* Process the incoming packet
*/
-/*
- * Convert bit count to time duration (in us) and submit
- * the value to lirc_dev.
- */
-static void submit_data(struct imon_context *context)
-{
- DEFINE_IR_RAW_EVENT(ev);
-
- ev.pulse = context->rx.prev_bit;
- ev.duration = US_TO_NS(context->rx.count * BIT_DURATION);
- ir_raw_event_store_with_filter(context->rdev, &ev);
-}
-
-/*
- * Process the incoming packet
- */
-static void imon_incoming_ir_raw(struct imon_context *context,
+static void imon_incoming_packet(struct imon_context *ictx,
struct urb *urb, int intf)
{
int len = urb->actual_length;
unsigned char *buf = urb->transfer_buffer;
- struct device *dev = context->dev;
- int octet, bit;
- unsigned char mask;
-
- if (len != 8) {
- dev_warn(dev, "imon %s: invalid incoming packet size (len = %d, intf%d)\n",
- __func__, len, intf);
- return;
- }
-
- if (debug)
- dev_info(dev, "raw packet: %*ph\n", len, buf);
- /*
- * Translate received data to pulse and space lengths.
- * Received data is active low, i.e. pulses are 0 and
- * spaces are 1.
- *
- * My original algorithm was essentially similar to
- * Changwoo Ryu's with the exception that he switched
- * the incoming bits to active high and also fed an
- * initial space to LIRC at the start of a new sequence
- * if the previous bit was a pulse.
- *
- * I've decided to adopt his algorithm.
- */
-
- if (buf[7] == 1 && context->rx.initial_space) {
- /* LIRC requires a leading space */
- context->rx.prev_bit = 0;
- context->rx.count = 4;
- submit_data(context);
- context->rx.count = 0;
- }
-
- for (octet = 0; octet < 5; ++octet) {
- mask = 0x80;
- for (bit = 0; bit < 8; ++bit) {
- int curr_bit = !(buf[octet] & mask);
-
- if (curr_bit != context->rx.prev_bit) {
- if (context->rx.count) {
- submit_data(context);
- context->rx.count = 0;
- }
- context->rx.prev_bit = curr_bit;
- }
- ++context->rx.count;
- mask >>= 1;
- }
- }
-
- if (buf[7] == 10) {
- if (context->rx.count) {
- submit_data(context);
- context->rx.count = 0;
- }
- context->rx.initial_space = context->rx.prev_bit;
- }
-
- ir_raw_event_handle(context->rdev);
-}
-
-static void imon_incoming_scancode(struct imon_context *ictx,
- struct urb *urb, int intf)
-{
- int len = urb->actual_length;
- unsigned char *buf = urb->transfer_buffer;
struct device *dev = ictx->dev;
unsigned long flags;
u32 kc;
@@ -1745,11 +1639,18 @@ static void imon_incoming_scancode(struct imon_context *ictx,
if (press_type == 0)
rc_keyup(ictx->rdev);
else {
- if (ictx->rc_proto == RC_PROTO_BIT_RC6_MCE ||
- ictx->rc_proto == RC_PROTO_BIT_OTHER)
- rc_keydown(ictx->rdev,
- ictx->rc_proto == RC_PROTO_BIT_RC6_MCE ? RC_PROTO_RC6_MCE : RC_PROTO_OTHER,
- ictx->rc_scancode, ictx->rc_toggle);
+ enum rc_proto proto;
+
+ if (ictx->rc_proto == RC_PROTO_BIT_RC6_MCE)
+ proto = RC_PROTO_RC6_MCE;
+ else if (ictx->rc_proto == RC_PROTO_BIT_IMON)
+ proto = RC_PROTO_IMON;
+ else
+ return;
+
+ rc_keydown(ictx->rdev, proto, ictx->rc_scancode,
+ ictx->rc_toggle);
+
spin_lock_irqsave(&ictx->kc_lock, flags);
ictx->last_keycode = ictx->kc;
spin_unlock_irqrestore(&ictx->kc_lock, flags);
@@ -1839,10 +1740,7 @@ static void usb_rx_callback_intf0(struct urb *urb)
break;
case 0:
- if (ictx->rdev->driver_type == RC_DRIVER_IR_RAW)
- imon_incoming_ir_raw(ictx, urb, intfnum);
- else
- imon_incoming_scancode(ictx, urb, intfnum);
+ imon_incoming_packet(ictx, urb, intfnum);
break;
default:
@@ -1883,10 +1781,7 @@ static void usb_rx_callback_intf1(struct urb *urb)
break;
case 0:
- if (ictx->rdev->driver_type == RC_DRIVER_IR_RAW)
- imon_incoming_ir_raw(ictx, urb, intfnum);
- else
- imon_incoming_scancode(ictx, urb, intfnum);
+ imon_incoming_packet(ictx, urb, intfnum);
break;
default:
@@ -1912,7 +1807,7 @@ static void imon_get_ffdc_type(struct imon_context *ictx)
{
u8 ffdc_cfg_byte = ictx->usb_rx_buf[6];
u8 detected_display_type = IMON_DISPLAY_TYPE_NONE;
- u64 allowed_protos = RC_PROTO_BIT_OTHER;
+ u64 allowed_protos = RC_PROTO_BIT_IMON;
switch (ffdc_cfg_byte) {
/* iMON Knob, no display, iMON IR + vol knob */
@@ -1960,8 +1855,10 @@ static void imon_get_ffdc_type(struct imon_context *ictx)
default:
dev_info(ictx->dev, "Unknown 0xffdc device, defaulting to VFD and iMON IR");
detected_display_type = IMON_DISPLAY_TYPE_VFD;
- /* We don't know which one it is, allow user to set the
- * RC6 one from userspace if OTHER wasn't correct. */
+ /*
+ * We don't know which one it is, allow user to set the
+ * RC6 one from userspace if IMON wasn't correct.
+ */
allowed_protos |= RC_PROTO_BIT_RC6_MCE;
break;
}
@@ -2000,14 +1897,11 @@ static void imon_set_display_type(struct imon_context *ictx)
case 0x0041:
case 0x0042:
case 0x0043:
- case 0x8001:
- case 0xff30:
configured_display_type = IMON_DISPLAY_TYPE_NONE;
ictx->display_supported = false;
break;
case 0x0036:
case 0x0044:
- case 0xffda:
default:
configured_display_type = IMON_DISPLAY_TYPE_VFD;
break;
@@ -2032,8 +1926,7 @@ static struct rc_dev *imon_init_rdev(struct imon_context *ictx)
static const unsigned char fp_packet[] = {
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88 };
- rdev = rc_allocate_device(ictx->dev_descr->flags & IMON_IR_RAW ?
- RC_DRIVER_IR_RAW : RC_DRIVER_SCANCODE);
+ rdev = rc_allocate_device(RC_DRIVER_SCANCODE);
if (!rdev) {
dev_err(ictx->dev, "remote control dev allocation failed\n");
goto out;
@@ -2051,12 +1944,8 @@ static struct rc_dev *imon_init_rdev(struct imon_context *ictx)
rdev->dev.parent = ictx->dev;
rdev->priv = ictx;
- if (ictx->dev_descr->flags & IMON_IR_RAW)
- rdev->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
- else
- /* iMON PAD or MCE */
- rdev->allowed_protocols = RC_PROTO_BIT_OTHER |
- RC_PROTO_BIT_RC6_MCE;
+ /* iMON PAD or MCE */
+ rdev->allowed_protocols = RC_PROTO_BIT_IMON | RC_PROTO_BIT_RC6_MCE;
rdev->change_protocol = imon_ir_change_protocol;
rdev->driver_name = MOD_NAME;
@@ -2074,8 +1963,7 @@ static struct rc_dev *imon_init_rdev(struct imon_context *ictx)
imon_set_display_type(ictx);
- if (ictx->rc_proto == RC_PROTO_BIT_RC6_MCE ||
- ictx->dev_descr->flags & IMON_IR_RAW)
+ if (ictx->rc_proto == RC_PROTO_BIT_RC6_MCE)
rdev->map_name = RC_MAP_IMON_MCE;
else
rdev->map_name = RC_MAP_IMON_PAD;
diff --git a/drivers/media/rc/imon_raw.c b/drivers/media/rc/imon_raw.c
new file mode 100644
index 000000000000..32709f96de14
--- /dev/null
+++ b/drivers/media/rc/imon_raw.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Copyright (C) 2018 Sean Young <sean@mess.org>
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <media/rc-core.h>
+
+/* Each bit is 250us */
+#define BIT_DURATION 250000
+
+struct imon {
+ struct device *dev;
+ struct urb *ir_urb;
+ struct rc_dev *rcdev;
+ u8 ir_buf[8];
+ char phys[64];
+};
+
+/*
+ * ffs/find_next_bit() searches in the wrong direction, so open-code our own.
+ */
+static inline int is_bit_set(const u8 *buf, int bit)
+{
+ return buf[bit / 8] & (0x80 >> (bit & 7));
+}
+
+static void imon_ir_data(struct imon *imon)
+{
+ DEFINE_IR_RAW_EVENT(rawir);
+ int offset = 0, size = 5 * 8;
+ int bit;
+
+ dev_dbg(imon->dev, "data: %*ph", 8, imon->ir_buf);
+
+ while (offset < size) {
+ bit = offset;
+ while (!is_bit_set(imon->ir_buf, bit) && bit < size)
+ bit++;
+ dev_dbg(imon->dev, "pulse: %d bits", bit - offset);
+ if (bit > offset) {
+ rawir.pulse = true;
+ rawir.duration = (bit - offset) * BIT_DURATION;
+ ir_raw_event_store_with_filter(imon->rcdev, &rawir);
+ }
+
+ if (bit >= size)
+ break;
+
+ offset = bit;
+ while (is_bit_set(imon->ir_buf, bit) && bit < size)
+ bit++;
+ dev_dbg(imon->dev, "space: %d bits", bit - offset);
+
+ rawir.pulse = false;
+ rawir.duration = (bit - offset) * BIT_DURATION;
+ ir_raw_event_store_with_filter(imon->rcdev, &rawir);
+
+ offset = bit;
+ }
+
+ if (imon->ir_buf[7] == 0x0a) {
+ ir_raw_event_set_idle(imon->rcdev, true);
+ ir_raw_event_handle(imon->rcdev);
+ }
+}
+
+static void imon_ir_rx(struct urb *urb)
+{
+ struct imon *imon = urb->context;
+ int ret;
+
+ switch (urb->status) {
+ case 0:
+ if (imon->ir_buf[7] != 0xff)
+ imon_ir_data(imon);
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ usb_unlink_urb(urb);
+ return;
+ case -EPIPE:
+ default:
+ dev_dbg(imon->dev, "error: urb status = %d", urb->status);
+ break;
+ }
+
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret && ret != -ENODEV)
+ dev_warn(imon->dev, "failed to resubmit urb: %d", ret);
+}
+
+static int imon_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_endpoint_descriptor *ir_ep = NULL;
+ struct usb_host_interface *idesc;
+ struct usb_device *udev;
+ struct rc_dev *rcdev;
+ struct imon *imon;
+ int i, ret;
+
+ udev = interface_to_usbdev(intf);
+ idesc = intf->cur_altsetting;
+
+ for (i = 0; i < idesc->desc.bNumEndpoints; i++) {
+ struct usb_endpoint_descriptor *ep = &idesc->endpoint[i].desc;
+
+ if (usb_endpoint_is_int_in(ep)) {
+ ir_ep = ep;
+ break;
+ }
+ }
+
+ if (!ir_ep) {
+ dev_err(&intf->dev, "IR endpoint missing");
+ return -ENODEV;
+ }
+
+ imon = devm_kmalloc(&intf->dev, sizeof(*imon), GFP_KERNEL);
+ if (!imon)
+ return -ENOMEM;
+
+ imon->ir_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!imon->ir_urb)
+ return -ENOMEM;
+
+ imon->dev = &intf->dev;
+ usb_fill_int_urb(imon->ir_urb, udev,
+ usb_rcvintpipe(udev, ir_ep->bEndpointAddress),
+ imon->ir_buf, sizeof(imon->ir_buf),
+ imon_ir_rx, imon, ir_ep->bInterval);
+
+ rcdev = devm_rc_allocate_device(&intf->dev, RC_DRIVER_IR_RAW);
+ if (!rcdev) {
+ ret = -ENOMEM;
+ goto free_urb;
+ }
+
+ usb_make_path(udev, imon->phys, sizeof(imon->phys));
+
+ rcdev->device_name = "iMON Station";
+ rcdev->driver_name = KBUILD_MODNAME;
+ rcdev->input_phys = imon->phys;
+ usb_to_input_id(udev, &rcdev->input_id);
+ rcdev->dev.parent = &intf->dev;
+ rcdev->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
+ rcdev->map_name = RC_MAP_IMON_RSC;
+ rcdev->rx_resolution = BIT_DURATION;
+ rcdev->priv = imon;
+
+ ret = devm_rc_register_device(&intf->dev, rcdev);
+ if (ret)
+ goto free_urb;
+
+ imon->rcdev = rcdev;
+
+ ret = usb_submit_urb(imon->ir_urb, GFP_KERNEL);
+ if (ret)
+ goto free_urb;
+
+ usb_set_intfdata(intf, imon);
+
+ return 0;
+
+free_urb:
+ usb_free_urb(imon->ir_urb);
+ return ret;
+}
+
+static void imon_disconnect(struct usb_interface *intf)
+{
+ struct imon *imon = usb_get_intfdata(intf);
+
+ usb_kill_urb(imon->ir_urb);
+ usb_free_urb(imon->ir_urb);
+}
+
+static const struct usb_device_id imon_table[] = {
+ /* SoundGraph iMON (IR only) -- sg_imon.inf */
+ { USB_DEVICE(0x04e8, 0xff30) },
+ {}
+};
+
+static struct usb_driver imon_driver = {
+ .name = KBUILD_MODNAME,
+ .probe = imon_probe,
+ .disconnect = imon_disconnect,
+ .id_table = imon_table
+};
+
+module_usb_driver(imon_driver);
+
+MODULE_DESCRIPTION("Early raw iMON IR devices");
+MODULE_AUTHOR("Sean Young <sean@mess.org>");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, imon_table);
diff --git a/drivers/media/rc/ir-hix5hd2.c b/drivers/media/rc/ir-hix5hd2.c
index 0ce11c41dfae..700ab4c563d0 100644
--- a/drivers/media/rc/ir-hix5hd2.c
+++ b/drivers/media/rc/ir-hix5hd2.c
@@ -71,9 +71,10 @@ struct hix5hd2_ir_priv {
unsigned long rate;
};
-static void hix5hd2_ir_enable(struct hix5hd2_ir_priv *dev, bool on)
+static int hix5hd2_ir_enable(struct hix5hd2_ir_priv *dev, bool on)
{
u32 val;
+ int ret = 0;
if (dev->regmap) {
regmap_read(dev->regmap, IR_CLK, &val);
@@ -87,10 +88,11 @@ static void hix5hd2_ir_enable(struct hix5hd2_ir_priv *dev, bool on)
regmap_write(dev->regmap, IR_CLK, val);
} else {
if (on)
- clk_prepare_enable(dev->clock);
+ ret = clk_prepare_enable(dev->clock);
else
clk_disable_unprepare(dev->clock);
}
+ return ret;
}
static int hix5hd2_ir_config(struct hix5hd2_ir_priv *priv)
@@ -127,9 +129,18 @@ static int hix5hd2_ir_config(struct hix5hd2_ir_priv *priv)
static int hix5hd2_ir_open(struct rc_dev *rdev)
{
struct hix5hd2_ir_priv *priv = rdev->priv;
+ int ret;
+
+ ret = hix5hd2_ir_enable(priv, true);
+ if (ret)
+ return ret;
- hix5hd2_ir_enable(priv, true);
- return hix5hd2_ir_config(priv);
+ ret = hix5hd2_ir_config(priv);
+ if (ret) {
+ hix5hd2_ir_enable(priv, false);
+ return ret;
+ }
+ return 0;
}
static void hix5hd2_ir_close(struct rc_dev *rdev)
@@ -239,7 +250,9 @@ static int hix5hd2_ir_probe(struct platform_device *pdev)
ret = PTR_ERR(priv->clock);
goto err;
}
- clk_prepare_enable(priv->clock);
+ ret = clk_prepare_enable(priv->clock);
+ if (ret)
+ goto err;
priv->rate = clk_get_rate(priv->clock);
rdev->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
@@ -309,9 +322,17 @@ static int hix5hd2_ir_suspend(struct device *dev)
static int hix5hd2_ir_resume(struct device *dev)
{
struct hix5hd2_ir_priv *priv = dev_get_drvdata(dev);
+ int ret;
- hix5hd2_ir_enable(priv, true);
- clk_prepare_enable(priv->clock);
+ ret = hix5hd2_ir_enable(priv, true);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(priv->clock);
+ if (ret) {
+ hix5hd2_ir_enable(priv, false);
+ return ret;
+ }
writel_relaxed(0x01, priv->base + IR_ENABLE);
writel_relaxed(0x00, priv->base + IR_INTM);
diff --git a/drivers/media/rc/ir-imon-decoder.c b/drivers/media/rc/ir-imon-decoder.c
new file mode 100644
index 000000000000..a1ff06a26542
--- /dev/null
+++ b/drivers/media/rc/ir-imon-decoder.c
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: GPL-2.0+
+// ir-imon-decoder.c - handle iMon protocol
+//
+// Copyright (C) 2018 by Sean Young <sean@mess.org>
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include "rc-core-priv.h"
+
+#define IMON_UNIT 415662 /* ns */
+#define IMON_BITS 30
+#define IMON_CHKBITS (BIT(30) | BIT(25) | BIT(24) | BIT(22) | \
+ BIT(21) | BIT(20) | BIT(19) | BIT(18) | \
+ BIT(17) | BIT(16) | BIT(14) | BIT(13) | \
+ BIT(12) | BIT(11) | BIT(10) | BIT(9))
+
+/*
+ * This protocol has 30 bits. The format is one IMON_UNIT header pulse,
+ * followed by 30 bits. Each bit is one IMON_UNIT check field, and then
+ * one IMON_UNIT field with the actual bit (1=space, 0=pulse).
+ * The check field is always space for some bits, for others it is pulse if
+ * both the preceding and current bit are zero, else space. IMON_CHKBITS
+ * defines which bits are of type check.
+ *
+ * There is no way to distinguish an incomplete message from one where
+ * the lower bits are all set, iow. the last pulse is for the lowest
+ * bit which is 0.
+ */
+enum imon_state {
+ STATE_INACTIVE,
+ STATE_BIT_CHK,
+ STATE_BIT_START,
+ STATE_FINISHED
+};
+
+/**
+ * ir_imon_decode() - Decode one iMON pulse or space
+ * @dev: the struct rc_dev descriptor of the device
+ * @ev: the struct ir_raw_event descriptor of the pulse/space
+ *
+ * This function returns -EINVAL if the pulse violates the state machine
+ */
+static int ir_imon_decode(struct rc_dev *dev, struct ir_raw_event ev)
+{
+ struct imon_dec *data = &dev->raw->imon;
+
+ if (!is_timing_event(ev)) {
+ if (ev.reset)
+ data->state = STATE_INACTIVE;
+ return 0;
+ }
+
+ dev_dbg(&dev->dev,
+ "iMON decode started at state %d bitno %d (%uus %s)\n",
+ data->state, data->count, TO_US(ev.duration),
+ TO_STR(ev.pulse));
+
+ for (;;) {
+ if (!geq_margin(ev.duration, IMON_UNIT, IMON_UNIT / 2))
+ return 0;
+
+ decrease_duration(&ev, IMON_UNIT);
+
+ switch (data->state) {
+ case STATE_INACTIVE:
+ if (ev.pulse) {
+ data->state = STATE_BIT_CHK;
+ data->bits = 0;
+ data->count = IMON_BITS;
+ }
+ break;
+ case STATE_BIT_CHK:
+ if (IMON_CHKBITS & BIT(data->count))
+ data->last_chk = ev.pulse;
+ else if (ev.pulse)
+ goto err_out;
+ data->state = STATE_BIT_START;
+ break;
+ case STATE_BIT_START:
+ data->bits <<= 1;
+ if (!ev.pulse)
+ data->bits |= 1;
+
+ if (IMON_CHKBITS & BIT(data->count)) {
+ if (data->last_chk != !(data->bits & 3))
+ goto err_out;
+ }
+
+ if (!data->count--)
+ data->state = STATE_FINISHED;
+ else
+ data->state = STATE_BIT_CHK;
+ break;
+ case STATE_FINISHED:
+ if (ev.pulse)
+ goto err_out;
+ rc_keydown(dev, RC_PROTO_IMON, data->bits, 0);
+ data->state = STATE_INACTIVE;
+ break;
+ }
+ }
+
+err_out:
+ dev_dbg(&dev->dev,
+ "iMON decode failed at state %d bitno %d (%uus %s)\n",
+ data->state, data->count, TO_US(ev.duration),
+ TO_STR(ev.pulse));
+
+ data->state = STATE_INACTIVE;
+
+ return -EINVAL;
+}
+
+/**
+ * ir_imon_encode() - Encode a scancode as a stream of raw events
+ *
+ * @protocol: protocol to encode
+ * @scancode: scancode to encode
+ * @events: array of raw ir events to write into
+ * @max: maximum size of @events
+ *
+ * Returns: The number of events written.
+ * -ENOBUFS if there isn't enough space in the array to fit the
+ * encoding. In this case all @max events will have been written.
+ */
+static int ir_imon_encode(enum rc_proto protocol, u32 scancode,
+ struct ir_raw_event *events, unsigned int max)
+{
+ struct ir_raw_event *e = events;
+ int i, pulse;
+
+ if (!max--)
+ return -ENOBUFS;
+ init_ir_raw_event_duration(e, 1, IMON_UNIT);
+
+ for (i = IMON_BITS; i >= 0; i--) {
+ if (BIT(i) & IMON_CHKBITS)
+ pulse = !(scancode & (BIT(i) | BIT(i + 1)));
+ else
+ pulse = 0;
+
+ if (pulse == e->pulse) {
+ e->duration += IMON_UNIT;
+ } else {
+ if (!max--)
+ return -ENOBUFS;
+ init_ir_raw_event_duration(++e, pulse, IMON_UNIT);
+ }
+
+ pulse = !(scancode & BIT(i));
+
+ if (pulse == e->pulse) {
+ e->duration += IMON_UNIT;
+ } else {
+ if (!max--)
+ return -ENOBUFS;
+ init_ir_raw_event_duration(++e, pulse, IMON_UNIT);
+ }
+ }
+
+ if (e->pulse)
+ e++;
+
+ return e - events;
+}
+
+static struct ir_raw_handler imon_handler = {
+ .protocols = RC_PROTO_BIT_IMON,
+ .decode = ir_imon_decode,
+ .encode = ir_imon_encode,
+ .carrier = 38000,
+};
+
+static int __init ir_imon_decode_init(void)
+{
+ ir_raw_handler_register(&imon_handler);
+
+ pr_info("IR iMON protocol handler initialized\n");
+ return 0;
+}
+
+static void __exit ir_imon_decode_exit(void)
+{
+ ir_raw_handler_unregister(&imon_handler);
+}
+
+module_init(ir_imon_decode_init);
+module_exit(ir_imon_decode_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sean Young <sean@mess.org>");
+MODULE_DESCRIPTION("iMON IR protocol decoder");
diff --git a/drivers/media/rc/ir-jvc-decoder.c b/drivers/media/rc/ir-jvc-decoder.c
index c03c776cfa54..8cb68ae43282 100644
--- a/drivers/media/rc/ir-jvc-decoder.c
+++ b/drivers/media/rc/ir-jvc-decoder.c
@@ -56,8 +56,8 @@ static int ir_jvc_decode(struct rc_dev *dev, struct ir_raw_event ev)
if (!geq_margin(ev.duration, JVC_UNIT, JVC_UNIT / 2))
goto out;
- IR_dprintk(2, "JVC decode started at state %d (%uus %s)\n",
- data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+ dev_dbg(&dev->dev, "JVC decode started at state %d (%uus %s)\n",
+ data->state, TO_US(ev.duration), TO_STR(ev.pulse));
again:
switch (data->state) {
@@ -136,15 +136,15 @@ again:
u32 scancode;
scancode = (bitrev8((data->bits >> 8) & 0xff) << 8) |
(bitrev8((data->bits >> 0) & 0xff) << 0);
- IR_dprintk(1, "JVC scancode 0x%04x\n", scancode);
+ dev_dbg(&dev->dev, "JVC scancode 0x%04x\n", scancode);
rc_keydown(dev, RC_PROTO_JVC, scancode, data->toggle);
data->first = false;
data->old_bits = data->bits;
} else if (data->bits == data->old_bits) {
- IR_dprintk(1, "JVC repeat\n");
+ dev_dbg(&dev->dev, "JVC repeat\n");
rc_repeat(dev);
} else {
- IR_dprintk(1, "JVC invalid repeat msg\n");
+ dev_dbg(&dev->dev, "JVC invalid repeat msg\n");
break;
}
@@ -164,8 +164,8 @@ again:
}
out:
- IR_dprintk(1, "JVC decode failed at state %d (%uus %s)\n",
- data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+ dev_dbg(&dev->dev, "JVC decode failed at state %d (%uus %s)\n",
+ data->state, TO_US(ev.duration), TO_STR(ev.pulse));
data->state = STATE_INACTIVE;
return -EINVAL;
}
diff --git a/drivers/media/rc/ir-mce_kbd-decoder.c b/drivers/media/rc/ir-mce_kbd-decoder.c
index 2c3df02e05ff..c110984ca671 100644
--- a/drivers/media/rc/ir-mce_kbd-decoder.c
+++ b/drivers/media/rc/ir-mce_kbd-decoder.c
@@ -117,19 +117,19 @@ static unsigned char kbd_keycodes[256] = {
static void mce_kbd_rx_timeout(struct timer_list *t)
{
- struct mce_kbd_dec *mce_kbd = from_timer(mce_kbd, t, rx_timeout);
- int i;
+ struct ir_raw_event_ctrl *raw = from_timer(raw, t, mce_kbd.rx_timeout);
unsigned char maskcode;
+ int i;
- IR_dprintk(2, "timer callback clearing all keys\n");
+ dev_dbg(&raw->dev->dev, "timer callback clearing all keys\n");
for (i = 0; i < 7; i++) {
maskcode = kbd_keycodes[MCIR2_MASK_KEYS_START + i];
- input_report_key(mce_kbd->idev, maskcode, 0);
+ input_report_key(raw->mce_kbd.idev, maskcode, 0);
}
for (i = 0; i < MCIR2_MASK_KEYS_START; i++)
- input_report_key(mce_kbd->idev, kbd_keycodes[i], 0);
+ input_report_key(raw->mce_kbd.idev, kbd_keycodes[i], 0);
}
static enum mce_kbd_mode mce_kbd_mode(struct mce_kbd_dec *data)
@@ -144,16 +144,16 @@ static enum mce_kbd_mode mce_kbd_mode(struct mce_kbd_dec *data)
}
}
-static void ir_mce_kbd_process_keyboard_data(struct input_dev *idev,
- u32 scancode)
+static void ir_mce_kbd_process_keyboard_data(struct rc_dev *dev, u32 scancode)
{
+ struct mce_kbd_dec *data = &dev->raw->mce_kbd;
u8 keydata = (scancode >> 8) & 0xff;
u8 shiftmask = scancode & 0xff;
unsigned char keycode, maskcode;
int i, keystate;
- IR_dprintk(1, "keyboard: keydata = 0x%02x, shiftmask = 0x%02x\n",
- keydata, shiftmask);
+ dev_dbg(&dev->dev, "keyboard: keydata = 0x%02x, shiftmask = 0x%02x\n",
+ keydata, shiftmask);
for (i = 0; i < 7; i++) {
maskcode = kbd_keycodes[MCIR2_MASK_KEYS_START + i];
@@ -161,20 +161,21 @@ static void ir_mce_kbd_process_keyboard_data(struct input_dev *idev,
keystate = 1;
else
keystate = 0;
- input_report_key(idev, maskcode, keystate);
+ input_report_key(data->idev, maskcode, keystate);
}
if (keydata) {
keycode = kbd_keycodes[keydata];
- input_report_key(idev, keycode, 1);
+ input_report_key(data->idev, keycode, 1);
} else {
for (i = 0; i < MCIR2_MASK_KEYS_START; i++)
- input_report_key(idev, kbd_keycodes[i], 0);
+ input_report_key(data->idev, kbd_keycodes[i], 0);
}
}
-static void ir_mce_kbd_process_mouse_data(struct input_dev *idev, u32 scancode)
+static void ir_mce_kbd_process_mouse_data(struct rc_dev *dev, u32 scancode)
{
+ struct mce_kbd_dec *data = &dev->raw->mce_kbd;
/* raw mouse coordinates */
u8 xdata = (scancode >> 7) & 0x7f;
u8 ydata = (scancode >> 14) & 0x7f;
@@ -193,14 +194,14 @@ static void ir_mce_kbd_process_mouse_data(struct input_dev *idev, u32 scancode)
else
y = ydata;
- IR_dprintk(1, "mouse: x = %d, y = %d, btns = %s%s\n",
- x, y, left ? "L" : "", right ? "R" : "");
+ dev_dbg(&dev->dev, "mouse: x = %d, y = %d, btns = %s%s\n",
+ x, y, left ? "L" : "", right ? "R" : "");
- input_report_rel(idev, REL_X, x);
- input_report_rel(idev, REL_Y, y);
+ input_report_rel(data->idev, REL_X, x);
+ input_report_rel(data->idev, REL_Y, y);
- input_report_key(idev, BTN_LEFT, left);
- input_report_key(idev, BTN_RIGHT, right);
+ input_report_key(data->idev, BTN_LEFT, left);
+ input_report_key(data->idev, BTN_RIGHT, right);
}
/**
@@ -227,8 +228,8 @@ static int ir_mce_kbd_decode(struct rc_dev *dev, struct ir_raw_event ev)
goto out;
again:
- IR_dprintk(2, "started at state %i (%uus %s)\n",
- data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+ dev_dbg(&dev->dev, "started at state %i (%uus %s)\n",
+ data->state, TO_US(ev.duration), TO_STR(ev.pulse));
if (!geq_margin(ev.duration, MCIR2_UNIT, MCIR2_UNIT / 2))
return 0;
@@ -280,7 +281,7 @@ again:
data->wanted_bits = MCIR2_MOUSE_NBITS;
break;
default:
- IR_dprintk(1, "not keyboard or mouse data\n");
+ dev_dbg(&dev->dev, "not keyboard or mouse data\n");
goto out;
}
@@ -319,25 +320,26 @@ again:
switch (data->wanted_bits) {
case MCIR2_KEYBOARD_NBITS:
scancode = data->body & 0xffff;
- IR_dprintk(1, "keyboard data 0x%08x\n", data->body);
+ dev_dbg(&dev->dev, "keyboard data 0x%08x\n",
+ data->body);
if (dev->timeout)
delay = usecs_to_jiffies(dev->timeout / 1000);
else
delay = msecs_to_jiffies(100);
mod_timer(&data->rx_timeout, jiffies + delay);
/* Pass data to keyboard buffer parser */
- ir_mce_kbd_process_keyboard_data(data->idev, scancode);
+ ir_mce_kbd_process_keyboard_data(dev, scancode);
lsc.rc_proto = RC_PROTO_MCIR2_KBD;
break;
case MCIR2_MOUSE_NBITS:
scancode = data->body & 0x1fffff;
- IR_dprintk(1, "mouse data 0x%06x\n", scancode);
+ dev_dbg(&dev->dev, "mouse data 0x%06x\n", scancode);
/* Pass data to mouse buffer parser */
- ir_mce_kbd_process_mouse_data(data->idev, scancode);
+ ir_mce_kbd_process_mouse_data(dev, scancode);
lsc.rc_proto = RC_PROTO_MCIR2_MSE;
break;
default:
- IR_dprintk(1, "not keyboard or mouse data\n");
+ dev_dbg(&dev->dev, "not keyboard or mouse data\n");
goto out;
}
@@ -350,8 +352,8 @@ again:
}
out:
- IR_dprintk(1, "failed at state %i (%uus %s)\n",
- data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+ dev_dbg(&dev->dev, "failed at state %i (%uus %s)\n",
+ data->state, TO_US(ev.duration), TO_STR(ev.pulse));
data->state = STATE_INACTIVE;
input_sync(data->idev);
return -EINVAL;
diff --git a/drivers/media/rc/ir-nec-decoder.c b/drivers/media/rc/ir-nec-decoder.c
index 31d7bafe7bda..21647b809e6f 100644
--- a/drivers/media/rc/ir-nec-decoder.c
+++ b/drivers/media/rc/ir-nec-decoder.c
@@ -49,8 +49,8 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev)
return 0;
}
- IR_dprintk(2, "NEC decode started at state %d (%uus %s)\n",
- data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+ dev_dbg(&dev->dev, "NEC decode started at state %d (%uus %s)\n",
+ data->state, TO_US(ev.duration), TO_STR(ev.pulse));
switch (data->state) {
@@ -99,13 +99,11 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev)
break;
if (data->necx_repeat && data->count == NECX_REPEAT_BITS &&
- geq_margin(ev.duration,
- NEC_TRAILER_SPACE, NEC_UNIT / 2)) {
- IR_dprintk(1, "Repeat last key\n");
- rc_repeat(dev);
- data->state = STATE_INACTIVE;
- return 0;
-
+ geq_margin(ev.duration, NEC_TRAILER_SPACE, NEC_UNIT / 2)) {
+ dev_dbg(&dev->dev, "Repeat last key\n");
+ rc_repeat(dev);
+ data->state = STATE_INACTIVE;
+ return 0;
} else if (data->count > NECX_REPEAT_BITS)
data->necx_repeat = false;
@@ -164,8 +162,8 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev)
return 0;
}
- IR_dprintk(1, "NEC decode failed at count %d state %d (%uus %s)\n",
- data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+ dev_dbg(&dev->dev, "NEC decode failed at count %d state %d (%uus %s)\n",
+ data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse));
data->state = STATE_INACTIVE;
return -EINVAL;
}
diff --git a/drivers/media/rc/ir-rc5-decoder.c b/drivers/media/rc/ir-rc5-decoder.c
index 11a28f8772da..74d3b859c3a2 100644
--- a/drivers/media/rc/ir-rc5-decoder.c
+++ b/drivers/media/rc/ir-rc5-decoder.c
@@ -54,8 +54,8 @@ static int ir_rc5_decode(struct rc_dev *dev, struct ir_raw_event ev)
goto out;
again:
- IR_dprintk(2, "RC5(x/sz) decode started at state %i (%uus %s)\n",
- data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+ dev_dbg(&dev->dev, "RC5(x/sz) decode started at state %i (%uus %s)\n",
+ data->state, TO_US(ev.duration), TO_STR(ev.pulse));
if (!geq_margin(ev.duration, RC5_UNIT, RC5_UNIT / 2))
return 0;
@@ -157,8 +157,8 @@ again:
} else
break;
- IR_dprintk(1, "RC5(x/sz) scancode 0x%06x (p: %u, t: %u)\n",
- scancode, protocol, toggle);
+ dev_dbg(&dev->dev, "RC5(x/sz) scancode 0x%06x (p: %u, t: %u)\n",
+ scancode, protocol, toggle);
rc_keydown(dev, protocol, scancode, toggle);
data->state = STATE_INACTIVE;
@@ -166,8 +166,8 @@ again:
}
out:
- IR_dprintk(1, "RC5(x/sz) decode failed at state %i count %d (%uus %s)\n",
- data->state, data->count, TO_US(ev.duration), TO_STR(ev.pulse));
+ dev_dbg(&dev->dev, "RC5(x/sz) decode failed at state %i count %d (%uus %s)\n",
+ data->state, data->count, TO_US(ev.duration), TO_STR(ev.pulse));
data->state = STATE_INACTIVE;
return -EINVAL;
}
diff --git a/drivers/media/rc/ir-rc6-decoder.c b/drivers/media/rc/ir-rc6-decoder.c
index 55bb19bbd4e9..8314da32453f 100644
--- a/drivers/media/rc/ir-rc6-decoder.c
+++ b/drivers/media/rc/ir-rc6-decoder.c
@@ -100,8 +100,8 @@ static int ir_rc6_decode(struct rc_dev *dev, struct ir_raw_event ev)
goto out;
again:
- IR_dprintk(2, "RC6 decode started at state %i (%uus %s)\n",
- data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+ dev_dbg(&dev->dev, "RC6 decode started at state %i (%uus %s)\n",
+ data->state, TO_US(ev.duration), TO_STR(ev.pulse));
if (!geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2))
return 0;
@@ -170,7 +170,7 @@ again:
break;
if (!(data->header & RC6_STARTBIT_MASK)) {
- IR_dprintk(1, "RC6 invalid start bit\n");
+ dev_dbg(&dev->dev, "RC6 invalid start bit\n");
break;
}
@@ -187,7 +187,7 @@ again:
data->wanted_bits = RC6_6A_NBITS;
break;
default:
- IR_dprintk(1, "RC6 unknown mode\n");
+ dev_dbg(&dev->dev, "RC6 unknown mode\n");
goto out;
}
goto again;
@@ -230,13 +230,13 @@ again:
scancode = data->body;
toggle = data->toggle;
protocol = RC_PROTO_RC6_0;
- IR_dprintk(1, "RC6(0) scancode 0x%04x (toggle: %u)\n",
- scancode, toggle);
+ dev_dbg(&dev->dev, "RC6(0) scancode 0x%04x (toggle: %u)\n",
+ scancode, toggle);
break;
case RC6_MODE_6A:
if (data->count > CHAR_BIT * sizeof data->body) {
- IR_dprintk(1, "RC6 too many (%u) data bits\n",
+ dev_dbg(&dev->dev, "RC6 too many (%u) data bits\n",
data->count);
goto out;
}
@@ -262,15 +262,15 @@ again:
}
break;
default:
- IR_dprintk(1, "RC6(6A) unsupported length\n");
+ dev_dbg(&dev->dev, "RC6(6A) unsupported length\n");
goto out;
}
- IR_dprintk(1, "RC6(6A) proto 0x%04x, scancode 0x%08x (toggle: %u)\n",
- protocol, scancode, toggle);
+ dev_dbg(&dev->dev, "RC6(6A) proto 0x%04x, scancode 0x%08x (toggle: %u)\n",
+ protocol, scancode, toggle);
break;
default:
- IR_dprintk(1, "RC6 unknown mode\n");
+ dev_dbg(&dev->dev, "RC6 unknown mode\n");
goto out;
}
@@ -280,8 +280,8 @@ again:
}
out:
- IR_dprintk(1, "RC6 decode failed at state %i (%uus %s)\n",
- data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+ dev_dbg(&dev->dev, "RC6 decode failed at state %i (%uus %s)\n",
+ data->state, TO_US(ev.duration), TO_STR(ev.pulse));
data->state = STATE_INACTIVE;
return -EINVAL;
}
diff --git a/drivers/media/rc/ir-sanyo-decoder.c b/drivers/media/rc/ir-sanyo-decoder.c
index ded39cdfc6ef..4efe6db5376a 100644
--- a/drivers/media/rc/ir-sanyo-decoder.c
+++ b/drivers/media/rc/ir-sanyo-decoder.c
@@ -52,14 +52,14 @@ static int ir_sanyo_decode(struct rc_dev *dev, struct ir_raw_event ev)
if (!is_timing_event(ev)) {
if (ev.reset) {
- IR_dprintk(1, "SANYO event reset received. reset to state 0\n");
+ dev_dbg(&dev->dev, "SANYO event reset received. reset to state 0\n");
data->state = STATE_INACTIVE;
}
return 0;
}
- IR_dprintk(2, "SANYO decode started at state %d (%uus %s)\n",
- data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+ dev_dbg(&dev->dev, "SANYO decode started at state %d (%uus %s)\n",
+ data->state, TO_US(ev.duration), TO_STR(ev.pulse));
switch (data->state) {
@@ -102,7 +102,7 @@ static int ir_sanyo_decode(struct rc_dev *dev, struct ir_raw_event ev)
if (!data->count && geq_margin(ev.duration, SANYO_REPEAT_SPACE, SANYO_UNIT / 2)) {
rc_repeat(dev);
- IR_dprintk(1, "SANYO repeat last key\n");
+ dev_dbg(&dev->dev, "SANYO repeat last key\n");
data->state = STATE_INACTIVE;
return 0;
}
@@ -144,21 +144,21 @@ static int ir_sanyo_decode(struct rc_dev *dev, struct ir_raw_event ev)
not_command = bitrev8((data->bits >> 0) & 0xff);
if ((command ^ not_command) != 0xff) {
- IR_dprintk(1, "SANYO checksum error: received 0x%08Lx\n",
- data->bits);
+ dev_dbg(&dev->dev, "SANYO checksum error: received 0x%08llx\n",
+ data->bits);
data->state = STATE_INACTIVE;
return 0;
}
scancode = address << 8 | command;
- IR_dprintk(1, "SANYO scancode: 0x%06x\n", scancode);
+ dev_dbg(&dev->dev, "SANYO scancode: 0x%06x\n", scancode);
rc_keydown(dev, RC_PROTO_SANYO, scancode, 0);
data->state = STATE_INACTIVE;
return 0;
}
- IR_dprintk(1, "SANYO decode failed at count %d state %d (%uus %s)\n",
- data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+ dev_dbg(&dev->dev, "SANYO decode failed at count %d state %d (%uus %s)\n",
+ data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse));
data->state = STATE_INACTIVE;
return -EINVAL;
}
diff --git a/drivers/media/rc/ir-sharp-decoder.c b/drivers/media/rc/ir-sharp-decoder.c
index df296991906c..6a38c50566a4 100644
--- a/drivers/media/rc/ir-sharp-decoder.c
+++ b/drivers/media/rc/ir-sharp-decoder.c
@@ -54,8 +54,8 @@ static int ir_sharp_decode(struct rc_dev *dev, struct ir_raw_event ev)
return 0;
}
- IR_dprintk(2, "Sharp decode started at state %d (%uus %s)\n",
- data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+ dev_dbg(&dev->dev, "Sharp decode started at state %d (%uus %s)\n",
+ data->state, TO_US(ev.duration), TO_STR(ev.pulse));
switch (data->state) {
@@ -149,9 +149,9 @@ static int ir_sharp_decode(struct rc_dev *dev, struct ir_raw_event ev)
msg = (data->bits >> 15) & 0x7fff;
echo = data->bits & 0x7fff;
if ((msg ^ echo) != 0x3ff) {
- IR_dprintk(1,
- "Sharp checksum error: received 0x%04x, 0x%04x\n",
- msg, echo);
+ dev_dbg(&dev->dev,
+ "Sharp checksum error: received 0x%04x, 0x%04x\n",
+ msg, echo);
break;
}
@@ -159,16 +159,15 @@ static int ir_sharp_decode(struct rc_dev *dev, struct ir_raw_event ev)
command = bitrev8((msg >> 2) & 0xff);
scancode = address << 8 | command;
- IR_dprintk(1, "Sharp scancode 0x%04x\n", scancode);
+ dev_dbg(&dev->dev, "Sharp scancode 0x%04x\n", scancode);
rc_keydown(dev, RC_PROTO_SHARP, scancode, 0);
data->state = STATE_INACTIVE;
return 0;
}
- IR_dprintk(1, "Sharp decode failed at count %d state %d (%uus %s)\n",
- data->count, data->state, TO_US(ev.duration),
- TO_STR(ev.pulse));
+ dev_dbg(&dev->dev, "Sharp decode failed at count %d state %d (%uus %s)\n",
+ data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse));
data->state = STATE_INACTIVE;
return -EINVAL;
}
diff --git a/drivers/media/rc/ir-sony-decoder.c b/drivers/media/rc/ir-sony-decoder.c
index e4bcff21c025..6764ec9de646 100644
--- a/drivers/media/rc/ir-sony-decoder.c
+++ b/drivers/media/rc/ir-sony-decoder.c
@@ -55,8 +55,8 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev)
if (!geq_margin(ev.duration, SONY_UNIT, SONY_UNIT / 2))
goto out;
- IR_dprintk(2, "Sony decode started at state %d (%uus %s)\n",
- data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+ dev_dbg(&dev->dev, "Sony decode started at state %d (%uus %s)\n",
+ data->state, TO_US(ev.duration), TO_STR(ev.pulse));
switch (data->state) {
@@ -148,19 +148,21 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev)
protocol = RC_PROTO_SONY20;
break;
default:
- IR_dprintk(1, "Sony invalid bitcount %u\n", data->count);
+ dev_dbg(&dev->dev, "Sony invalid bitcount %u\n",
+ data->count);
goto out;
}
scancode = device << 16 | subdevice << 8 | function;
- IR_dprintk(1, "Sony(%u) scancode 0x%05x\n", data->count, scancode);
+ dev_dbg(&dev->dev, "Sony(%u) scancode 0x%05x\n", data->count,
+ scancode);
rc_keydown(dev, protocol, scancode, 0);
goto finish_state_machine;
}
out:
- IR_dprintk(1, "Sony decode failed at state %d (%uus %s)\n",
- data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+ dev_dbg(&dev->dev, "Sony decode failed at state %d (%uus %s)\n",
+ data->state, TO_US(ev.duration), TO_STR(ev.pulse));
data->state = STATE_INACTIVE;
return -EINVAL;
diff --git a/drivers/media/rc/ir-spi.c b/drivers/media/rc/ir-spi.c
index a32a84ae2d0b..7163d5ce2e64 100644
--- a/drivers/media/rc/ir-spi.c
+++ b/drivers/media/rc/ir-spi.c
@@ -15,21 +15,11 @@
#define IR_SPI_DRIVER_NAME "ir-spi"
-/* pulse value for different duty cycles */
-#define IR_SPI_PULSE_DC_50 0xff00
-#define IR_SPI_PULSE_DC_60 0xfc00
-#define IR_SPI_PULSE_DC_70 0xf800
-#define IR_SPI_PULSE_DC_75 0xf000
-#define IR_SPI_PULSE_DC_80 0xc000
-#define IR_SPI_PULSE_DC_90 0x8000
-
#define IR_SPI_DEFAULT_FREQUENCY 38000
-#define IR_SPI_BIT_PER_WORD 8
#define IR_SPI_MAX_BUFSIZE 4096
struct ir_spi_data {
u32 freq;
- u8 duty_cycle;
bool negated;
u16 tx_buf[IR_SPI_MAX_BUFSIZE];
@@ -105,19 +95,9 @@ static int ir_spi_set_tx_carrier(struct rc_dev *dev, u32 carrier)
static int ir_spi_set_duty_cycle(struct rc_dev *dev, u32 duty_cycle)
{
struct ir_spi_data *idata = dev->priv;
+ int bits = (duty_cycle * 15) / 100;
- if (duty_cycle >= 90)
- idata->pulse = IR_SPI_PULSE_DC_90;
- else if (duty_cycle >= 80)
- idata->pulse = IR_SPI_PULSE_DC_80;
- else if (duty_cycle >= 75)
- idata->pulse = IR_SPI_PULSE_DC_75;
- else if (duty_cycle >= 70)
- idata->pulse = IR_SPI_PULSE_DC_70;
- else if (duty_cycle >= 60)
- idata->pulse = IR_SPI_PULSE_DC_60;
- else
- idata->pulse = IR_SPI_PULSE_DC_50;
+ idata->pulse = GENMASK(bits, 0);
if (idata->negated) {
idata->pulse = ~idata->pulse;
diff --git a/drivers/media/rc/ir-xmp-decoder.c b/drivers/media/rc/ir-xmp-decoder.c
index 712bc6d76e92..58b47af1a763 100644
--- a/drivers/media/rc/ir-xmp-decoder.c
+++ b/drivers/media/rc/ir-xmp-decoder.c
@@ -49,8 +49,8 @@ static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev)
return 0;
}
- IR_dprintk(2, "XMP decode started at state %d %d (%uus %s)\n",
- data->state, data->count, TO_US(ev.duration), TO_STR(ev.pulse));
+ dev_dbg(&dev->dev, "XMP decode started at state %d %d (%uus %s)\n",
+ data->state, data->count, TO_US(ev.duration), TO_STR(ev.pulse));
switch (data->state) {
@@ -85,7 +85,7 @@ static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev)
u32 scancode;
if (data->count != 16) {
- IR_dprintk(2, "received TRAILER period at index %d: %u\n",
+ dev_dbg(&dev->dev, "received TRAILER period at index %d: %u\n",
data->count, ev.duration);
data->state = STATE_INACTIVE;
return -EINVAL;
@@ -99,7 +99,8 @@ static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev)
*/
divider = (n[3] - XMP_NIBBLE_PREFIX) / 15 - 2000;
if (divider < 50) {
- IR_dprintk(2, "divider to small %d.\n", divider);
+ dev_dbg(&dev->dev, "divider to small %d.\n",
+ divider);
data->state = STATE_INACTIVE;
return -EINVAL;
}
@@ -113,7 +114,7 @@ static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev)
n[12] + n[13] + n[14] + n[15]) % 16;
if (sum1 != 15 || sum2 != 15) {
- IR_dprintk(2, "checksum errors sum1=0x%X sum2=0x%X\n",
+ dev_dbg(&dev->dev, "checksum errors sum1=0x%X sum2=0x%X\n",
sum1, sum2);
data->state = STATE_INACTIVE;
return -EINVAL;
@@ -127,24 +128,24 @@ static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev)
obc1 = n[12] << 4 | n[13];
obc2 = n[14] << 4 | n[15];
if (subaddr != subaddr2) {
- IR_dprintk(2, "subaddress nibbles mismatch 0x%02X != 0x%02X\n",
+ dev_dbg(&dev->dev, "subaddress nibbles mismatch 0x%02X != 0x%02X\n",
subaddr, subaddr2);
data->state = STATE_INACTIVE;
return -EINVAL;
}
if (oem != 0x44)
- IR_dprintk(1, "Warning: OEM nibbles 0x%02X. Expected 0x44\n",
+ dev_dbg(&dev->dev, "Warning: OEM nibbles 0x%02X. Expected 0x44\n",
oem);
scancode = addr << 24 | subaddr << 16 |
obc1 << 8 | obc2;
- IR_dprintk(1, "XMP scancode 0x%06x\n", scancode);
+ dev_dbg(&dev->dev, "XMP scancode 0x%06x\n", scancode);
if (toggle == 0) {
rc_keydown(dev, RC_PROTO_XMP, scancode, 0);
} else {
rc_repeat(dev);
- IR_dprintk(1, "Repeat last key\n");
+ dev_dbg(&dev->dev, "Repeat last key\n");
}
data->state = STATE_INACTIVE;
@@ -153,7 +154,7 @@ static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev)
} else if (geq_margin(ev.duration, XMP_HALFFRAME_SPACE, XMP_NIBBLE_PREFIX)) {
/* Expect 8 or 16 nibble pulses. 16 in case of 'final' frame */
if (data->count == 16) {
- IR_dprintk(2, "received half frame pulse at index %d. Probably a final frame key-up event: %u\n",
+ dev_dbg(&dev->dev, "received half frame pulse at index %d. Probably a final frame key-up event: %u\n",
data->count, ev.duration);
/*
* TODO: for now go back to half frame position
@@ -164,7 +165,7 @@ static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev)
}
else if (data->count != 8)
- IR_dprintk(2, "received half frame pulse at index %d: %u\n",
+ dev_dbg(&dev->dev, "received half frame pulse at index %d: %u\n",
data->count, ev.duration);
data->state = STATE_LEADER_PULSE;
@@ -173,7 +174,7 @@ static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev)
} else if (geq_margin(ev.duration, XMP_NIBBLE_PREFIX, XMP_UNIT)) {
/* store nibble raw data, decode after trailer */
if (data->count == 16) {
- IR_dprintk(2, "to many pulses (%d) ignoring: %u\n",
+ dev_dbg(&dev->dev, "to many pulses (%d) ignoring: %u\n",
data->count, ev.duration);
data->state = STATE_INACTIVE;
return -EINVAL;
@@ -189,8 +190,8 @@ static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev)
break;
}
- IR_dprintk(1, "XMP decode failed at count %d state %d (%uus %s)\n",
- data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+ dev_dbg(&dev->dev, "XMP decode failed at count %d state %d (%uus %s)\n",
+ data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse));
data->state = STATE_INACTIVE;
return -EINVAL;
}
diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile
index 50b319355edf..d6b913a3032d 100644
--- a/drivers/media/rc/keymaps/Makefile
+++ b/drivers/media/rc/keymaps/Makefile
@@ -53,6 +53,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
rc-hisi-tv-demo.o \
rc-imon-mce.o \
rc-imon-pad.o \
+ rc-imon-rsc.o \
rc-iodata-bctv7e.o \
rc-it913x-v1.o \
rc-it913x-v2.o \
diff --git a/drivers/media/rc/keymaps/rc-imon-pad.c b/drivers/media/rc/keymaps/rc-imon-pad.c
index a7296ffbf218..8501cf0a3253 100644
--- a/drivers/media/rc/keymaps/rc-imon-pad.c
+++ b/drivers/media/rc/keymaps/rc-imon-pad.c
@@ -134,8 +134,7 @@ static struct rc_map_list imon_pad_map = {
.map = {
.scan = imon_pad,
.size = ARRAY_SIZE(imon_pad),
- /* actual protocol details unknown, hardware decoder */
- .rc_proto = RC_PROTO_OTHER,
+ .rc_proto = RC_PROTO_IMON,
.name = RC_MAP_IMON_PAD,
}
};
diff --git a/drivers/media/rc/keymaps/rc-imon-rsc.c b/drivers/media/rc/keymaps/rc-imon-rsc.c
new file mode 100644
index 000000000000..83e4564aaa22
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-imon-rsc.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Copyright (C) 2018 Sean Young <sean@mess.org>
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+//
+// Note that this remote has a stick which its own IR protocol,
+// with 16 directions. This is not supported yet.
+//
+static struct rc_map_table imon_rsc[] = {
+ { 0x801010, KEY_EXIT },
+ { 0x80102f, KEY_POWER },
+ { 0x80104a, KEY_SCREENSAVER }, /* Screensaver */
+ { 0x801049, KEY_TIME }, /* Timer */
+ { 0x801054, KEY_NUMERIC_1 },
+ { 0x801055, KEY_NUMERIC_2 },
+ { 0x801056, KEY_NUMERIC_3 },
+ { 0x801057, KEY_NUMERIC_4 },
+ { 0x801058, KEY_NUMERIC_5 },
+ { 0x801059, KEY_NUMERIC_6 },
+ { 0x80105a, KEY_NUMERIC_7 },
+ { 0x80105b, KEY_NUMERIC_8 },
+ { 0x80105c, KEY_NUMERIC_9 },
+ { 0x801081, KEY_SCREEN }, /* Desktop */
+ { 0x80105d, KEY_NUMERIC_0 },
+ { 0x801082, KEY_MAX },
+ { 0x801048, KEY_ESC },
+ { 0x80104b, KEY_MEDIA }, /* Windows key */
+ { 0x801083, KEY_MENU },
+ { 0x801045, KEY_APPSELECT }, /* app launcher */
+ { 0x801084, KEY_STOP },
+ { 0x801046, KEY_CYCLEWINDOWS },
+ { 0x801085, KEY_BACKSPACE },
+ { 0x801086, KEY_KEYBOARD },
+ { 0x801087, KEY_SPACE },
+ { 0x80101e, KEY_RESERVED }, /* shift tab */
+ { 0x801098, BTN_0 },
+ { 0x80101f, KEY_TAB },
+ { 0x80101b, BTN_LEFT },
+ { 0x80101d, BTN_RIGHT },
+ { 0x801016, BTN_MIDDLE }, /* drag and drop */
+ { 0x801088, KEY_MUTE },
+ { 0x80105e, KEY_VOLUMEDOWN },
+ { 0x80105f, KEY_VOLUMEUP },
+ { 0x80104c, KEY_PLAY },
+ { 0x80104d, KEY_PAUSE },
+ { 0x80104f, KEY_EJECTCD },
+ { 0x801050, KEY_PREVIOUS },
+ { 0x801051, KEY_NEXT },
+ { 0x80104e, KEY_STOP },
+ { 0x801052, KEY_REWIND },
+ { 0x801053, KEY_FASTFORWARD },
+ { 0x801089, KEY_ZOOM } /* full screen */
+};
+
+static struct rc_map_list imon_rsc_map = {
+ .map = {
+ .scan = imon_rsc,
+ .size = ARRAY_SIZE(imon_rsc),
+ .rc_proto = RC_PROTO_NEC,
+ .name = RC_MAP_IMON_RSC,
+ }
+};
+
+static int __init init_rc_map_imon_rsc(void)
+{
+ return rc_map_register(&imon_rsc_map);
+}
+
+static void __exit exit_rc_map_imon_rsc(void)
+{
+ rc_map_unregister(&imon_rsc_map);
+}
+
+module_init(init_rc_map_imon_rsc)
+module_exit(exit_rc_map_imon_rsc)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sean Young <sean@mess.org>");
diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c
index cc863044c880..24e9fbb80e81 100644
--- a/drivers/media/rc/lirc_dev.c
+++ b/drivers/media/rc/lirc_dev.c
@@ -60,12 +60,12 @@ void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev)
* space with the maximum time value.
*/
sample = LIRC_SPACE(LIRC_VALUE_MASK);
- IR_dprintk(2, "delivering reset sync space to lirc_dev\n");
+ dev_dbg(&dev->dev, "delivering reset sync space to lirc_dev\n");
/* Carrier reports */
} else if (ev.carrier_report) {
sample = LIRC_FREQUENCY(ev.carrier);
- IR_dprintk(2, "carrier report (freq: %d)\n", sample);
+ dev_dbg(&dev->dev, "carrier report (freq: %d)\n", sample);
/* Packet end */
} else if (ev.timeout) {
@@ -77,7 +77,7 @@ void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev)
dev->gap_duration = ev.duration;
sample = LIRC_TIMEOUT(ev.duration / 1000);
- IR_dprintk(2, "timeout report (duration: %d)\n", sample);
+ dev_dbg(&dev->dev, "timeout report (duration: %d)\n", sample);
/* Normal sample */
} else {
@@ -100,8 +100,8 @@ void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev)
sample = ev.pulse ? LIRC_PULSE(ev.duration / 1000) :
LIRC_SPACE(ev.duration / 1000);
- IR_dprintk(2, "delivering %uus %s to lirc_dev\n",
- TO_US(ev.duration), TO_STR(ev.pulse));
+ dev_dbg(&dev->dev, "delivering %uus %s to lirc_dev\n",
+ TO_US(ev.duration), TO_STR(ev.pulse));
}
spin_lock_irqsave(&dev->lirc_fh_lock, flags);
@@ -249,8 +249,6 @@ static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf,
goto out_unlock;
}
- start = ktime_get();
-
if (!dev->tx_ir) {
ret = -EINVAL;
goto out_unlock;
@@ -343,6 +341,8 @@ static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf,
duration += txbuf[i];
}
+ start = ktime_get();
+
ret = dev->tx_ir(dev, txbuf, count);
if (ret < 0)
goto out_kfree;
@@ -570,7 +570,7 @@ static long ir_lirc_ioctl(struct file *file, unsigned int cmd,
ret = -EINVAL;
else if (dev->s_timeout)
ret = dev->s_timeout(dev, tmp);
- else if (!ret)
+ else
dev->timeout = tmp;
}
break;
@@ -804,8 +804,8 @@ int __init lirc_dev_init(void)
return retval;
}
- pr_info("IR Remote Control driver registered, major %d\n",
- MAJOR(lirc_base_dev));
+ pr_debug("IR Remote Control driver registered, major %d\n",
+ MAJOR(lirc_base_dev));
return 0;
}
diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c
index a9187b0b46a1..69ba57372c05 100644
--- a/drivers/media/rc/mceusb.c
+++ b/drivers/media/rc/mceusb.c
@@ -42,7 +42,7 @@
#include <linux/pm_wakeup.h>
#include <media/rc-core.h>
-#define DRIVER_VERSION "1.93"
+#define DRIVER_VERSION "1.94"
#define DRIVER_AUTHOR "Jarod Wilson <jarod@redhat.com>"
#define DRIVER_DESC "Windows Media Center Ed. eHome Infrared Transceiver " \
"device driver"
@@ -182,6 +182,7 @@ enum mceusb_model_type {
MCE_GEN1,
MCE_GEN3,
MCE_GEN2_TX_INV,
+ MCE_GEN2_TX_INV_RX_GOOD,
POLARIS_EVK,
CX_HYBRID_TV,
MULTIFUNCTION,
@@ -198,6 +199,13 @@ struct mceusb_model {
u32 mce_gen3:1;
u32 tx_mask_normal:1;
u32 no_tx:1;
+ /*
+ * 2nd IR receiver (short-range, wideband) for learning mode:
+ * 0, absent 2nd receiver (rx2)
+ * 1, rx2 present
+ * 2, rx2 which under counts IR carrier cycles
+ */
+ u32 rx2;
int ir_intfnum;
@@ -209,9 +217,11 @@ static const struct mceusb_model mceusb_model[] = {
[MCE_GEN1] = {
.mce_gen1 = 1,
.tx_mask_normal = 1,
+ .rx2 = 2,
},
[MCE_GEN2] = {
.mce_gen2 = 1,
+ .rx2 = 2,
},
[MCE_GEN2_NO_TX] = {
.mce_gen2 = 1,
@@ -220,10 +230,17 @@ static const struct mceusb_model mceusb_model[] = {
[MCE_GEN2_TX_INV] = {
.mce_gen2 = 1,
.tx_mask_normal = 1,
+ .rx2 = 1,
+ },
+ [MCE_GEN2_TX_INV_RX_GOOD] = {
+ .mce_gen2 = 1,
+ .tx_mask_normal = 1,
+ .rx2 = 2,
},
[MCE_GEN3] = {
.mce_gen3 = 1,
.tx_mask_normal = 1,
+ .rx2 = 2,
},
[POLARIS_EVK] = {
/*
@@ -232,6 +249,7 @@ static const struct mceusb_model mceusb_model[] = {
* to allow testing it
*/
.name = "Conexant Hybrid TV (cx231xx) MCE IR",
+ .rx2 = 2,
},
[CX_HYBRID_TV] = {
.no_tx = 1, /* tx isn't wired up at all */
@@ -244,10 +262,12 @@ static const struct mceusb_model mceusb_model[] = {
[MULTIFUNCTION] = {
.mce_gen2 = 1,
.ir_intfnum = 2,
+ .rx2 = 2,
},
[TIVO_KIT] = {
.mce_gen2 = 1,
.rc_map = RC_MAP_TIVO,
+ .rx2 = 2,
},
[EVROMEDIA_FULL_HYBRID_FULLHD] = {
.name = "Evromedia USB Full Hybrid Full HD",
@@ -290,7 +310,7 @@ static const struct usb_device_id mceusb_dev_table[] = {
.driver_info = MULTIFUNCTION },
/* SMK/Toshiba G83C0004D410 */
{ USB_DEVICE(VENDOR_SMK, 0x031d),
- .driver_info = MCE_GEN2_TX_INV },
+ .driver_info = MCE_GEN2_TX_INV_RX_GOOD },
/* SMK eHome Infrared Transceiver (Sony VAIO) */
{ USB_DEVICE(VENDOR_SMK, 0x0322),
.driver_info = MCE_GEN2_TX_INV },
@@ -427,7 +447,8 @@ struct mceusb_dev {
struct rc_dev *rc;
/* optional features we can enable */
- bool learning_enabled;
+ bool carrier_report_enabled;
+ bool wideband_rx_enabled; /* aka learning mode, short-range rx */
/* core device bits */
struct device *dev;
@@ -458,6 +479,7 @@ struct mceusb_dev {
u32 tx_mask_normal:1;
u32 microsoft_gen1:1;
u32 no_tx:1;
+ u32 rx2;
} flags;
/* transmit support */
@@ -474,6 +496,11 @@ struct mceusb_dev {
u8 num_rxports; /* number of receive sensors */
u8 txports_cabled; /* bitmask of transmitters with cable */
u8 rxports_active; /* bitmask of active receive sensors */
+ bool learning_active; /* wideband rx is active */
+
+ /* receiver carrier frequency detection support */
+ u32 pulse_tunit; /* IR pulse "on" cumulative time units */
+ u32 pulse_count; /* pulse "on" count in measurement interval */
/*
* support for async error handler mceusb_deferred_kevent()
@@ -684,8 +711,8 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, u8 *buf, int buf_len,
/* aka MCE_RSP_EQIRRXCFCNT */
if (out)
dev_dbg(dev, "Get receive sensor");
- else if (ir->learning_enabled)
- dev_dbg(dev, "RX pulse count: %d",
+ else
+ dev_dbg(dev, "RX carrier cycle count: %d",
((data[0] << 8) | data[1]));
break;
case MCE_RSP_EQIRNUMPORTS:
@@ -956,14 +983,78 @@ static int mceusb_set_tx_carrier(struct rc_dev *dev, u32 carrier)
}
/*
+ * Select or deselect the 2nd receiver port.
+ * Second receiver is learning mode, wide-band, short-range receiver.
+ * Only one receiver (long or short range) may be active at a time.
+ */
+static int mceusb_set_rx_wideband(struct rc_dev *dev, int enable)
+{
+ struct mceusb_dev *ir = dev->priv;
+ unsigned char cmdbuf[3] = { MCE_CMD_PORT_IR,
+ MCE_CMD_SETIRRXPORTEN, 0x00 };
+
+ dev_dbg(ir->dev, "select %s-range receive sensor",
+ enable ? "short" : "long");
+ if (enable) {
+ ir->wideband_rx_enabled = true;
+ cmdbuf[2] = 2; /* port 2 is short range receiver */
+ } else {
+ ir->wideband_rx_enabled = false;
+ cmdbuf[2] = 1; /* port 1 is long range receiver */
+ }
+ mce_async_out(ir, cmdbuf, sizeof(cmdbuf));
+ /* response from device sets ir->learning_active */
+
+ return 0;
+}
+
+/*
+ * Enable/disable receiver carrier frequency pass through reporting.
+ * Only the short-range receiver has carrier frequency measuring capability.
+ * Implicitly select this receiver when enabling carrier frequency reporting.
+ */
+static int mceusb_set_rx_carrier_report(struct rc_dev *dev, int enable)
+{
+ struct mceusb_dev *ir = dev->priv;
+ unsigned char cmdbuf[3] = { MCE_CMD_PORT_IR,
+ MCE_CMD_SETIRRXPORTEN, 0x00 };
+
+ dev_dbg(ir->dev, "%s short-range receiver carrier reporting",
+ enable ? "enable" : "disable");
+ if (enable) {
+ ir->carrier_report_enabled = true;
+ if (!ir->learning_active) {
+ cmdbuf[2] = 2; /* port 2 is short range receiver */
+ mce_async_out(ir, cmdbuf, sizeof(cmdbuf));
+ }
+ } else {
+ ir->carrier_report_enabled = false;
+ /*
+ * Revert to normal (long-range) receiver only if the
+ * wideband (short-range) receiver wasn't explicitly
+ * enabled.
+ */
+ if (ir->learning_active && !ir->wideband_rx_enabled) {
+ cmdbuf[2] = 1; /* port 1 is long range receiver */
+ mce_async_out(ir, cmdbuf, sizeof(cmdbuf));
+ }
+ }
+
+ return 0;
+}
+
+/*
* We don't do anything but print debug spew for many of the command bits
* we receive from the hardware, but some of them are useful information
* we want to store so that we can use them.
*/
static void mceusb_handle_command(struct mceusb_dev *ir, int index)
{
+ DEFINE_IR_RAW_EVENT(rawir);
u8 hi = ir->buf_in[index + 1] & 0xff;
u8 lo = ir->buf_in[index + 2] & 0xff;
+ u32 carrier_cycles;
+ u32 cycles_fix;
switch (ir->buf_in[index]) {
/* the one and only 5-byte return value command */
@@ -980,6 +1071,33 @@ static void mceusb_handle_command(struct mceusb_dev *ir, int index)
ir->num_txports = hi;
ir->num_rxports = lo;
break;
+ case MCE_RSP_EQIRRXCFCNT:
+ /*
+ * The carrier cycle counter can overflow and wrap around
+ * without notice from the device. So frequency measurement
+ * will be inaccurate with long duration IR.
+ *
+ * The long-range (non learning) receiver always reports
+ * zero count so we always ignore its report.
+ */
+ if (ir->carrier_report_enabled && ir->learning_active &&
+ ir->pulse_tunit > 0) {
+ carrier_cycles = (hi << 8 | lo);
+ /*
+ * Adjust carrier cycle count by adding
+ * 1 missed count per pulse "on"
+ */
+ cycles_fix = ir->flags.rx2 == 2 ? ir->pulse_count : 0;
+ rawir.carrier_report = 1;
+ rawir.carrier = (1000000u / MCE_TIME_UNIT) *
+ (carrier_cycles + cycles_fix) /
+ ir->pulse_tunit;
+ dev_dbg(ir->dev, "RX carrier frequency %u Hz (pulse count = %u, cycles = %u, duration = %u, rx2 = %u)",
+ rawir.carrier, ir->pulse_count, carrier_cycles,
+ ir->pulse_tunit, ir->flags.rx2);
+ ir_raw_event_store(ir->rc, &rawir);
+ }
+ break;
/* 1-byte return value commands */
case MCE_RSP_EQEMVER:
@@ -989,8 +1107,12 @@ static void mceusb_handle_command(struct mceusb_dev *ir, int index)
ir->tx_mask = hi;
break;
case MCE_RSP_EQIRRXPORTEN:
- ir->learning_enabled = ((hi & 0x02) == 0x02);
- ir->rxports_active = hi;
+ ir->learning_active = ((hi & 0x02) == 0x02);
+ if (ir->rxports_active != hi) {
+ dev_info(ir->dev, "%s-range (0x%x) receiver active",
+ ir->learning_active ? "short" : "long", hi);
+ ir->rxports_active = hi;
+ }
break;
case MCE_RSP_CMD_ILLEGAL:
ir->need_reset = true;
@@ -1027,12 +1149,16 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
ir->rem--;
init_ir_raw_event(&rawir);
rawir.pulse = ((ir->buf_in[i] & MCE_PULSE_BIT) != 0);
- rawir.duration = (ir->buf_in[i] & MCE_PULSE_MASK)
- * US_TO_NS(MCE_TIME_UNIT);
+ rawir.duration = (ir->buf_in[i] & MCE_PULSE_MASK);
+ if (rawir.pulse) {
+ ir->pulse_tunit += rawir.duration;
+ ir->pulse_count++;
+ }
+ rawir.duration *= US_TO_NS(MCE_TIME_UNIT);
- dev_dbg(ir->dev, "Storing %s with duration %u",
+ dev_dbg(ir->dev, "Storing %s %u ns (%02x)",
rawir.pulse ? "pulse" : "space",
- rawir.duration);
+ rawir.duration, ir->buf_in[i]);
if (ir_raw_event_store_with_filter(ir->rc, &rawir))
event = true;
@@ -1053,10 +1179,13 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
ir->rem = (ir->cmd & MCE_PACKET_LENGTH_MASK);
mceusb_dev_printdata(ir, ir->buf_in, buf_len,
i, ir->rem + 1, false);
- if (ir->rem)
+ if (ir->rem) {
ir->parser_state = PARSE_IRDATA;
- else
+ } else {
ir_raw_event_reset(ir->rc);
+ ir->pulse_tunit = 0;
+ ir->pulse_count = 0;
+ }
break;
}
@@ -1292,6 +1421,10 @@ static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir)
rc->s_tx_carrier = mceusb_set_tx_carrier;
rc->tx_ir = mceusb_tx_ir;
}
+ if (ir->flags.rx2 > 0) {
+ rc->s_learning_mode = mceusb_set_rx_wideband;
+ rc->s_carrier_report = mceusb_set_rx_carrier_report;
+ }
rc->driver_name = DRIVER_NAME;
switch (le16_to_cpu(udev->descriptor.idVendor)) {
@@ -1406,6 +1539,7 @@ static int mceusb_dev_probe(struct usb_interface *intf,
ir->flags.microsoft_gen1 = is_microsoft_gen1;
ir->flags.tx_mask_normal = tx_mask_normal;
ir->flags.no_tx = mceusb_model[model].no_tx;
+ ir->flags.rx2 = mceusb_model[model].rx2;
ir->model = model;
/* Saving usb interface data for use by the transmitter routine */
diff --git a/drivers/media/rc/meson-ir.c b/drivers/media/rc/meson-ir.c
index f2204eb77e2a..f449b35d25e7 100644
--- a/drivers/media/rc/meson-ir.c
+++ b/drivers/media/rc/meson-ir.c
@@ -97,8 +97,7 @@ static irqreturn_t meson_ir_irq(int irqno, void *dev_id)
status = readl_relaxed(ir->reg + IR_DEC_STATUS);
rawir.pulse = !!(status & STATUS_IR_DEC_IN);
- ir_raw_event_store(ir->rc, &rawir);
- ir_raw_event_handle(ir->rc);
+ ir_raw_event_store_with_timeout(ir->rc, &rawir);
spin_unlock(&ir->lock);
@@ -145,7 +144,9 @@ static int meson_ir_probe(struct platform_device *pdev)
ir->rc->map_name = map_name ? map_name : RC_MAP_EMPTY;
ir->rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
ir->rc->rx_resolution = US_TO_NS(MESON_TRATE);
- ir->rc->timeout = MS_TO_NS(200);
+ ir->rc->min_timeout = 1;
+ ir->rc->timeout = IR_DEFAULT_TIMEOUT;
+ ir->rc->max_timeout = 10 * IR_DEFAULT_TIMEOUT;
ir->rc->driver_name = DRIVER_NAME;
spin_lock_init(&ir->lock);
diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h
index 458e9eb2d6a9..e0e6a17460f6 100644
--- a/drivers/media/rc/rc-core-priv.h
+++ b/drivers/media/rc/rc-core-priv.h
@@ -50,8 +50,9 @@ struct ir_raw_event_ctrl {
DECLARE_KFIFO(kfifo, struct ir_raw_event, MAX_IR_EVENT_SIZE);
ktime_t last_event; /* when last event occurred */
struct rc_dev *dev; /* pointer to the parent rc_dev */
- /* edge driver */
- struct timer_list edge_handle;
+ /* handle delayed ir_raw_event_store_edge processing */
+ spinlock_t edge_spinlock;
+ struct timer_list edge_handle;
/* raw decoder state follows */
struct ir_raw_event prev_ev;
@@ -117,6 +118,12 @@ struct ir_raw_event_ctrl {
unsigned count;
u32 durations[16];
} xmp;
+ struct imon_dec {
+ int state;
+ int count;
+ int last_chk;
+ unsigned int bits;
+ } imon;
};
/* macros for IR decoders */
@@ -292,11 +299,4 @@ static inline int ir_lirc_register(struct rc_dev *dev) { return 0; }
static inline void ir_lirc_unregister(struct rc_dev *dev) { }
#endif
-/*
- * Decoder initialization code
- *
- * Those load logic are called during ir-core init, and automatically
- * loads the compiled decoders for their usage with IR raw events
- */
-
#endif /* _RC_CORE_PRIV */
diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c
index 18504870b9f0..374f83105a23 100644
--- a/drivers/media/rc/rc-ir-raw.c
+++ b/drivers/media/rc/rc-ir-raw.c
@@ -65,8 +65,8 @@ int ir_raw_event_store(struct rc_dev *dev, struct ir_raw_event *ev)
if (!dev->raw)
return -EINVAL;
- IR_dprintk(2, "sample: (%05dus %s)\n",
- TO_US(ev->duration), TO_STR(ev->pulse));
+ dev_dbg(&dev->dev, "sample: (%05dus %s)\n",
+ TO_US(ev->duration), TO_STR(ev->pulse));
if (!kfifo_put(&dev->raw->kfifo, *ev)) {
dev_err(&dev->dev, "IR event FIFO is full!\n");
@@ -92,7 +92,6 @@ int ir_raw_event_store_edge(struct rc_dev *dev, bool pulse)
{
ktime_t now;
DEFINE_IR_RAW_EVENT(ev);
- int rc = 0;
if (!dev->raw)
return -EINVAL;
@@ -101,7 +100,33 @@ int ir_raw_event_store_edge(struct rc_dev *dev, bool pulse)
ev.duration = ktime_to_ns(ktime_sub(now, dev->raw->last_event));
ev.pulse = !pulse;
- rc = ir_raw_event_store(dev, &ev);
+ return ir_raw_event_store_with_timeout(dev, &ev);
+}
+EXPORT_SYMBOL_GPL(ir_raw_event_store_edge);
+
+/*
+ * ir_raw_event_store_with_timeout() - pass a pulse/space duration to the raw
+ * ir decoders, schedule decoding and
+ * timeout
+ * @dev: the struct rc_dev device descriptor
+ * @ev: the struct ir_raw_event descriptor of the pulse/space
+ *
+ * This routine (which may be called from an interrupt context) stores a
+ * pulse/space duration for the raw ir decoding state machines, schedules
+ * decoding and generates a timeout.
+ */
+int ir_raw_event_store_with_timeout(struct rc_dev *dev, struct ir_raw_event *ev)
+{
+ ktime_t now;
+ int rc = 0;
+
+ if (!dev->raw)
+ return -EINVAL;
+
+ now = ktime_get();
+
+ spin_lock(&dev->raw->edge_spinlock);
+ rc = ir_raw_event_store(dev, ev);
dev->raw->last_event = now;
@@ -112,10 +137,11 @@ int ir_raw_event_store_edge(struct rc_dev *dev, bool pulse)
mod_timer(&dev->raw->edge_handle,
jiffies + msecs_to_jiffies(15));
}
+ spin_unlock(&dev->raw->edge_spinlock);
return rc;
}
-EXPORT_SYMBOL_GPL(ir_raw_event_store_edge);
+EXPORT_SYMBOL_GPL(ir_raw_event_store_with_timeout);
/**
* ir_raw_event_store_with_filter() - pass next pulse/space to decoders with some processing
@@ -168,7 +194,7 @@ void ir_raw_event_set_idle(struct rc_dev *dev, bool idle)
if (!dev->raw)
return;
- IR_dprintk(2, "%s idle mode\n", idle ? "enter" : "leave");
+ dev_dbg(&dev->dev, "%s idle mode\n", idle ? "enter" : "leave");
if (idle) {
dev->raw->this_ev.timeout = true;
@@ -462,12 +488,26 @@ int ir_raw_encode_scancode(enum rc_proto protocol, u32 scancode,
}
EXPORT_SYMBOL(ir_raw_encode_scancode);
-static void edge_handle(struct timer_list *t)
+/**
+ * ir_raw_edge_handle() - Handle ir_raw_event_store_edge() processing
+ *
+ * @t: timer_list
+ *
+ * This callback is armed by ir_raw_event_store_edge(). It does two things:
+ * first of all, rather than calling ir_raw_event_handle() for each
+ * edge and waking up the rc thread, 15 ms after the first edge
+ * ir_raw_event_handle() is called. Secondly, generate a timeout event
+ * no more IR is received after the rc_dev timeout.
+ */
+static void ir_raw_edge_handle(struct timer_list *t)
{
struct ir_raw_event_ctrl *raw = from_timer(raw, t, edge_handle);
struct rc_dev *dev = raw->dev;
- ktime_t interval = ktime_sub(ktime_get(), dev->raw->last_event);
+ unsigned long flags;
+ ktime_t interval;
+ spin_lock_irqsave(&dev->raw->edge_spinlock, flags);
+ interval = ktime_sub(ktime_get(), dev->raw->last_event);
if (ktime_to_ns(interval) >= dev->timeout) {
DEFINE_IR_RAW_EVENT(ev);
@@ -480,6 +520,7 @@ static void edge_handle(struct timer_list *t)
jiffies + nsecs_to_jiffies(dev->timeout -
ktime_to_ns(interval)));
}
+ spin_unlock_irqrestore(&dev->raw->edge_spinlock, flags);
ir_raw_event_handle(dev);
}
@@ -528,7 +569,8 @@ int ir_raw_event_prepare(struct rc_dev *dev)
dev->raw->dev = dev;
dev->change_protocol = change_protocol;
- timer_setup(&dev->raw->edge_handle, edge_handle, 0);
+ spin_lock_init(&dev->raw->edge_spinlock);
+ timer_setup(&dev->raw->edge_handle, ir_raw_edge_handle, 0);
INIT_KFIFO(dev->raw->kfifo);
return 0;
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
index 1db8d38fed7c..b67be33bd62f 100644
--- a/drivers/media/rc/rc-main.c
+++ b/drivers/media/rc/rc-main.c
@@ -68,6 +68,8 @@ static const struct {
.scancode_bits = 0x1fff, .repeat_period = 250 },
[RC_PROTO_XMP] = { .name = "xmp", .repeat_period = 250 },
[RC_PROTO_CEC] = { .name = "cec", .repeat_period = 550 },
+ [RC_PROTO_IMON] = { .name = "imon",
+ .scancode_bits = 0x7fffffff, .repeat_period = 250 },
};
/* Used to keep track of known keymaps */
@@ -156,6 +158,7 @@ static struct rc_map_list empty_map = {
/**
* ir_create_table() - initializes a scancode table
+ * @dev: the rc_dev device
* @rc_map: the rc_map to initialize
* @name: name to assign to the table
* @rc_proto: ir type to assign to the new table
@@ -166,7 +169,7 @@ static struct rc_map_list empty_map = {
*
* return: zero on success or a negative error code
*/
-static int ir_create_table(struct rc_map *rc_map,
+static int ir_create_table(struct rc_dev *dev, struct rc_map *rc_map,
const char *name, u64 rc_proto, size_t size)
{
rc_map->name = kstrdup(name, GFP_KERNEL);
@@ -182,8 +185,8 @@ static int ir_create_table(struct rc_map *rc_map,
return -ENOMEM;
}
- IR_dprintk(1, "Allocated space for %u keycode entries (%u bytes)\n",
- rc_map->size, rc_map->alloc);
+ dev_dbg(&dev->dev, "Allocated space for %u keycode entries (%u bytes)\n",
+ rc_map->size, rc_map->alloc);
return 0;
}
@@ -205,6 +208,7 @@ static void ir_free_table(struct rc_map *rc_map)
/**
* ir_resize_table() - resizes a scancode table if necessary
+ * @dev: the rc_dev device
* @rc_map: the rc_map to resize
* @gfp_flags: gfp flags to use when allocating memory
*
@@ -213,7 +217,8 @@ static void ir_free_table(struct rc_map *rc_map)
*
* return: zero on success or a negative error code
*/
-static int ir_resize_table(struct rc_map *rc_map, gfp_t gfp_flags)
+static int ir_resize_table(struct rc_dev *dev, struct rc_map *rc_map,
+ gfp_t gfp_flags)
{
unsigned int oldalloc = rc_map->alloc;
unsigned int newalloc = oldalloc;
@@ -226,23 +231,21 @@ static int ir_resize_table(struct rc_map *rc_map, gfp_t gfp_flags)
return -ENOMEM;
newalloc *= 2;
- IR_dprintk(1, "Growing table to %u bytes\n", newalloc);
+ dev_dbg(&dev->dev, "Growing table to %u bytes\n", newalloc);
}
if ((rc_map->len * 3 < rc_map->size) && (oldalloc > IR_TAB_MIN_SIZE)) {
/* Less than 1/3 of entries in use -> shrink keytable */
newalloc /= 2;
- IR_dprintk(1, "Shrinking table to %u bytes\n", newalloc);
+ dev_dbg(&dev->dev, "Shrinking table to %u bytes\n", newalloc);
}
if (newalloc == oldalloc)
return 0;
newscan = kmalloc(newalloc, gfp_flags);
- if (!newscan) {
- IR_dprintk(1, "Failed to kmalloc %u bytes\n", newalloc);
+ if (!newscan)
return -ENOMEM;
- }
memcpy(newscan, rc_map->scan, rc_map->len * sizeof(struct rc_map_table));
rc_map->scan = newscan;
@@ -275,16 +278,16 @@ static unsigned int ir_update_mapping(struct rc_dev *dev,
/* Did the user wish to remove the mapping? */
if (new_keycode == KEY_RESERVED || new_keycode == KEY_UNKNOWN) {
- IR_dprintk(1, "#%d: Deleting scan 0x%04x\n",
- index, rc_map->scan[index].scancode);
+ dev_dbg(&dev->dev, "#%d: Deleting scan 0x%04x\n",
+ index, rc_map->scan[index].scancode);
rc_map->len--;
memmove(&rc_map->scan[index], &rc_map->scan[index+ 1],
(rc_map->len - index) * sizeof(struct rc_map_table));
} else {
- IR_dprintk(1, "#%d: %s scan 0x%04x with key 0x%04x\n",
- index,
- old_keycode == KEY_RESERVED ? "New" : "Replacing",
- rc_map->scan[index].scancode, new_keycode);
+ dev_dbg(&dev->dev, "#%d: %s scan 0x%04x with key 0x%04x\n",
+ index,
+ old_keycode == KEY_RESERVED ? "New" : "Replacing",
+ rc_map->scan[index].scancode, new_keycode);
rc_map->scan[index].keycode = new_keycode;
__set_bit(new_keycode, dev->input_dev->keybit);
}
@@ -301,7 +304,7 @@ static unsigned int ir_update_mapping(struct rc_dev *dev,
}
/* Possibly shrink the keytable, failure is not a problem */
- ir_resize_table(rc_map, GFP_ATOMIC);
+ ir_resize_table(dev, rc_map, GFP_ATOMIC);
}
return old_keycode;
@@ -352,7 +355,7 @@ static unsigned int ir_establish_scancode(struct rc_dev *dev,
/* No previous mapping found, we might need to grow the table */
if (rc_map->size == rc_map->len) {
- if (!resize || ir_resize_table(rc_map, GFP_ATOMIC))
+ if (!resize || ir_resize_table(dev, rc_map, GFP_ATOMIC))
return -1U;
}
@@ -431,8 +434,8 @@ static int ir_setkeytable(struct rc_dev *dev,
unsigned int i, index;
int rc;
- rc = ir_create_table(rc_map, from->name,
- from->rc_proto, from->size);
+ rc = ir_create_table(dev, rc_map, from->name, from->rc_proto,
+ from->size);
if (rc)
return rc;
@@ -576,8 +579,8 @@ u32 rc_g_keycode_from_table(struct rc_dev *dev, u32 scancode)
spin_unlock_irqrestore(&rc_map->lock, flags);
if (keycode != KEY_RESERVED)
- IR_dprintk(1, "%s: scancode 0x%04x keycode 0x%02x\n",
- dev->device_name, scancode, keycode);
+ dev_dbg(&dev->dev, "%s: scancode 0x%04x keycode 0x%02x\n",
+ dev->device_name, scancode, keycode);
return keycode;
}
@@ -596,7 +599,7 @@ static void ir_do_keyup(struct rc_dev *dev, bool sync)
if (!dev->keypressed)
return;
- IR_dprintk(1, "keyup key 0x%04x\n", dev->last_keycode);
+ dev_dbg(&dev->dev, "keyup key 0x%04x\n", dev->last_keycode);
del_timer(&dev->timer_repeat);
input_report_key(dev->input_dev, dev->last_keycode, 0);
led_trigger_event(led_feedback, LED_OFF);
@@ -751,8 +754,8 @@ static void ir_do_keydown(struct rc_dev *dev, enum rc_proto protocol,
/* Register a keypress */
dev->keypressed = true;
- IR_dprintk(1, "%s: key down event, key 0x%04x, protocol 0x%04x, scancode 0x%08x\n",
- dev->device_name, keycode, protocol, scancode);
+ dev_dbg(&dev->dev, "%s: key down event, key 0x%04x, protocol 0x%04x, scancode 0x%08x\n",
+ dev->device_name, keycode, protocol, scancode);
input_report_key(dev->input_dev, keycode, 1);
led_trigger_event(led_feedback, LED_FULL);
@@ -1003,6 +1006,7 @@ static const struct {
RC_PROTO_BIT_MCIR2_MSE, "mce_kbd", "ir-mce_kbd-decoder" },
{ RC_PROTO_BIT_XMP, "xmp", "ir-xmp-decoder" },
{ RC_PROTO_BIT_CEC, "cec", NULL },
+ { RC_PROTO_BIT_IMON, "imon", "ir-imon-decoder" },
};
/**
@@ -1056,8 +1060,8 @@ static ssize_t show_protocols(struct device *device,
mutex_unlock(&dev->lock);
- IR_dprintk(1, "%s: allowed - 0x%llx, enabled - 0x%llx\n",
- __func__, (long long)allowed, (long long)enabled);
+ dev_dbg(&dev->dev, "%s: allowed - 0x%llx, enabled - 0x%llx\n",
+ __func__, (long long)allowed, (long long)enabled);
for (i = 0; i < ARRAY_SIZE(proto_names); i++) {
if (allowed & enabled & proto_names[i].type)
@@ -1083,6 +1087,7 @@ static ssize_t show_protocols(struct device *device,
/**
* parse_protocol_change() - parses a protocol change request
+ * @dev: rc_dev device
* @protocols: pointer to the bitmask of current protocols
* @buf: pointer to the buffer with a list of changes
*
@@ -1092,7 +1097,8 @@ static ssize_t show_protocols(struct device *device,
* Writing "none" will disable all protocols.
* Returns the number of changes performed or a negative error code.
*/
-static int parse_protocol_change(u64 *protocols, const char *buf)
+static int parse_protocol_change(struct rc_dev *dev, u64 *protocols,
+ const char *buf)
{
const char *tmp;
unsigned count = 0;
@@ -1128,7 +1134,8 @@ static int parse_protocol_change(u64 *protocols, const char *buf)
if (!strcasecmp(tmp, "lirc"))
mask = 0;
else {
- IR_dprintk(1, "Unknown protocol: '%s'\n", tmp);
+ dev_dbg(&dev->dev, "Unknown protocol: '%s'\n",
+ tmp);
return -EINVAL;
}
}
@@ -1144,7 +1151,7 @@ static int parse_protocol_change(u64 *protocols, const char *buf)
}
if (!count) {
- IR_dprintk(1, "Protocol not specified\n");
+ dev_dbg(&dev->dev, "Protocol not specified\n");
return -EINVAL;
}
@@ -1217,12 +1224,12 @@ static ssize_t store_protocols(struct device *device,
u64 old_protocols, new_protocols;
ssize_t rc;
- IR_dprintk(1, "Normal protocol change requested\n");
+ dev_dbg(&dev->dev, "Normal protocol change requested\n");
current_protocols = &dev->enabled_protocols;
filter = &dev->scancode_filter;
if (!dev->change_protocol) {
- IR_dprintk(1, "Protocol switching not supported\n");
+ dev_dbg(&dev->dev, "Protocol switching not supported\n");
return -EINVAL;
}
@@ -1230,14 +1237,14 @@ static ssize_t store_protocols(struct device *device,
old_protocols = *current_protocols;
new_protocols = old_protocols;
- rc = parse_protocol_change(&new_protocols, buf);
+ rc = parse_protocol_change(dev, &new_protocols, buf);
if (rc < 0)
goto out;
rc = dev->change_protocol(dev, &new_protocols);
if (rc < 0) {
- IR_dprintk(1, "Error setting protocols to 0x%llx\n",
- (long long)new_protocols);
+ dev_dbg(&dev->dev, "Error setting protocols to 0x%llx\n",
+ (long long)new_protocols);
goto out;
}
@@ -1246,8 +1253,8 @@ static ssize_t store_protocols(struct device *device,
if (new_protocols != old_protocols) {
*current_protocols = new_protocols;
- IR_dprintk(1, "Protocols changed to 0x%llx\n",
- (long long)new_protocols);
+ dev_dbg(&dev->dev, "Protocols changed to 0x%llx\n",
+ (long long)new_protocols);
}
/*
@@ -1435,8 +1442,8 @@ static ssize_t show_wakeup_protocols(struct device *device,
mutex_unlock(&dev->lock);
- IR_dprintk(1, "%s: allowed - 0x%llx, enabled - %d\n",
- __func__, (long long)allowed, enabled);
+ dev_dbg(&dev->dev, "%s: allowed - 0x%llx, enabled - %d\n",
+ __func__, (long long)allowed, enabled);
for (i = 0; i < ARRAY_SIZE(protocols); i++) {
if (allowed & (1ULL << i)) {
@@ -1511,7 +1518,7 @@ static ssize_t store_wakeup_protocols(struct device *device,
if (dev->wakeup_protocol != protocol) {
dev->wakeup_protocol = protocol;
- IR_dprintk(1, "Wakeup protocol changed to %d\n", protocol);
+ dev_dbg(&dev->dev, "Wakeup protocol changed to %d\n", protocol);
if (protocol == RC_PROTO_RC6_MCE)
dev->scancode_wakeup_filter.data = 0x800f0000;
@@ -1874,9 +1881,8 @@ int rc_register_device(struct rc_dev *dev)
dev->registered = true;
- IR_dprintk(1, "Registered rc%u (driver: %s)\n",
- dev->minor,
- dev->driver_name ? dev->driver_name : "unknown");
+ dev_dbg(&dev->dev, "Registered rc%u (driver: %s)\n", dev->minor,
+ dev->driver_name ? dev->driver_name : "unknown");
return 0;
@@ -1929,12 +1935,12 @@ void rc_unregister_device(struct rc_dev *dev)
if (!dev)
return;
- del_timer_sync(&dev->timer_keyup);
- del_timer_sync(&dev->timer_repeat);
-
if (dev->driver_type == RC_DRIVER_IR_RAW)
ir_raw_event_unregister(dev);
+ del_timer_sync(&dev->timer_keyup);
+ del_timer_sync(&dev->timer_repeat);
+
rc_free_rx_device(dev);
mutex_lock(&dev->lock);
@@ -1994,9 +2000,5 @@ static void __exit rc_core_exit(void)
subsys_initcall(rc_core_init);
module_exit(rc_core_exit);
-int rc_core_debug; /* ir_debug level (0,1,2) */
-EXPORT_SYMBOL_GPL(rc_core_debug);
-module_param_named(debug, rc_core_debug, int, 0644);
-
MODULE_AUTHOR("Mauro Carvalho Chehab");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c
index 97f367b446c4..f500cea228a9 100644
--- a/drivers/media/rc/sunxi-cir.c
+++ b/drivers/media/rc/sunxi-cir.c
@@ -72,12 +72,8 @@
/* CIR_REG register idle threshold */
#define REG_CIR_ITHR(val) (((val) << 8) & (GENMASK(15, 8)))
-/* Required frequency for IR0 or IR1 clock in CIR mode */
+/* Required frequency for IR0 or IR1 clock in CIR mode (default) */
#define SUNXI_IR_BASE_CLK 8000000
-/* Frequency after IR internal divider */
-#define SUNXI_IR_CLK (SUNXI_IR_BASE_CLK / 64)
-/* Sample period in ns */
-#define SUNXI_IR_SAMPLE (1000000000ul / SUNXI_IR_CLK)
/* Noise threshold in samples */
#define SUNXI_IR_RXNOISE 1
/* Idle Threshold in samples */
@@ -122,7 +118,8 @@ static irqreturn_t sunxi_ir_irq(int irqno, void *dev_id)
/* for each bit in fifo */
dt = readb(ir->base + SUNXI_IR_RXFIFO_REG);
rawir.pulse = (dt & 0x80) != 0;
- rawir.duration = ((dt & 0x7f) + 1) * SUNXI_IR_SAMPLE;
+ rawir.duration = ((dt & 0x7f) + 1) *
+ ir->rc->rx_resolution;
ir_raw_event_store_with_filter(ir->rc, &rawir);
}
}
@@ -148,6 +145,7 @@ static int sunxi_ir_probe(struct platform_device *pdev)
struct device_node *dn = dev->of_node;
struct resource *res;
struct sunxi_ir *ir;
+ u32 b_clk_freq = SUNXI_IR_BASE_CLK;
ir = devm_kzalloc(dev, sizeof(struct sunxi_ir), GFP_KERNEL);
if (!ir)
@@ -172,6 +170,9 @@ static int sunxi_ir_probe(struct platform_device *pdev)
return PTR_ERR(ir->clk);
}
+ /* Base clock frequency (optional) */
+ of_property_read_u32(dn, "clock-frequency", &b_clk_freq);
+
/* Reset (optional) */
ir->rst = devm_reset_control_get_optional_exclusive(dev, NULL);
if (IS_ERR(ir->rst))
@@ -180,11 +181,12 @@ static int sunxi_ir_probe(struct platform_device *pdev)
if (ret)
return ret;
- ret = clk_set_rate(ir->clk, SUNXI_IR_BASE_CLK);
+ ret = clk_set_rate(ir->clk, b_clk_freq);
if (ret) {
dev_err(dev, "set ir base clock failed!\n");
goto exit_reset_assert;
}
+ dev_dbg(dev, "set base clock frequency to %d Hz.\n", b_clk_freq);
if (clk_prepare_enable(ir->apb_clk)) {
dev_err(dev, "try to enable apb_ir_clk failed\n");
@@ -225,7 +227,8 @@ static int sunxi_ir_probe(struct platform_device *pdev)
ir->rc->map_name = ir->map_name ?: RC_MAP_EMPTY;
ir->rc->dev.parent = dev;
ir->rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
- ir->rc->rx_resolution = SUNXI_IR_SAMPLE;
+ /* Frequency after IR internal divider with sample period in ns */
+ ir->rc->rx_resolution = (1000000000ul / (b_clk_freq / 64));
ir->rc->timeout = MS_TO_NS(SUNXI_IR_TIMEOUT);
ir->rc->driver_name = SUNXI_IR_DEV;
diff --git a/drivers/media/spi/Kconfig b/drivers/media/spi/Kconfig
index a21f5a39a440..b07ac86fc53c 100644
--- a/drivers/media/spi/Kconfig
+++ b/drivers/media/spi/Kconfig
@@ -12,3 +12,17 @@ config VIDEO_GS1662
endmenu
endif
+
+if SPI
+menu "Media SPI Adapters"
+
+config CXD2880_SPI_DRV
+ tristate "Sony CXD2880 SPI support"
+ depends on DVB_CORE && SPI
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Choose if you would like to have SPI interface support for Sony CXD2880.
+
+endmenu
+
+endif
diff --git a/drivers/media/spi/Makefile b/drivers/media/spi/Makefile
index ea64013d16cc..9e536777a330 100644
--- a/drivers/media/spi/Makefile
+++ b/drivers/media/spi/Makefile
@@ -1 +1,6 @@
obj-$(CONFIG_VIDEO_GS1662) += gs1662.o
+obj-$(CONFIG_CXD2880_SPI_DRV) += cxd2880-spi.o
+
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
+ccflags-y += -Idrivers/media/dvb-frontends/cxd2880
diff --git a/drivers/media/spi/cxd2880-spi.c b/drivers/media/spi/cxd2880-spi.c
new file mode 100644
index 000000000000..4df3bd312f48
--- /dev/null
+++ b/drivers/media/spi/cxd2880-spi.c
@@ -0,0 +1,670 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cxd2880-spi.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * SPI adapter
+ *
+ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
+
+#include <linux/spi/spi.h>
+#include <linux/ktime.h>
+
+#include <media/dvb_demux.h>
+#include <media/dmxdev.h>
+#include <media/dvb_frontend.h>
+#include "cxd2880.h"
+
+#define CXD2880_MAX_FILTER_SIZE 32
+#define BURST_WRITE_MAX 128
+#define MAX_TRANS_PKT 300
+
+struct cxd2880_ts_buf_info {
+ u8 read_ready:1;
+ u8 almost_full:1;
+ u8 almost_empty:1;
+ u8 overflow:1;
+ u8 underflow:1;
+ u16 pkt_num;
+};
+
+struct cxd2880_pid_config {
+ u8 is_enable;
+ u16 pid;
+};
+
+struct cxd2880_pid_filter_config {
+ u8 is_negative;
+ struct cxd2880_pid_config pid_config[CXD2880_MAX_FILTER_SIZE];
+};
+
+struct cxd2880_dvb_spi {
+ struct dvb_frontend dvb_fe;
+ struct dvb_adapter adapter;
+ struct dvb_demux demux;
+ struct dmxdev dmxdev;
+ struct dmx_frontend dmx_fe;
+ struct task_struct *cxd2880_ts_read_thread;
+ struct spi_device *spi;
+ struct mutex spi_mutex; /* For SPI access exclusive control */
+ int feed_count;
+ int all_pid_feed_count;
+ u8 *ts_buf;
+ struct cxd2880_pid_filter_config filter_config;
+};
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static int cxd2880_write_spi(struct spi_device *spi, u8 *data, u32 size)
+{
+ struct spi_message msg;
+ struct spi_transfer tx;
+
+ if (!spi || !data) {
+ pr_err("invalid arg\n");
+ return -EINVAL;
+ }
+
+ memset(&tx, 0, sizeof(tx));
+ tx.tx_buf = data;
+ tx.len = size;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&tx, &msg);
+
+ return spi_sync(spi, &msg);
+}
+
+static int cxd2880_write_reg(struct spi_device *spi,
+ u8 sub_address, const u8 *data, u32 size)
+{
+ u8 send_data[BURST_WRITE_MAX + 4];
+ const u8 *write_data_top = NULL;
+ int ret = 0;
+
+ if (!spi || !data) {
+ pr_err("invalid arg\n");
+ return -EINVAL;
+ }
+ if (size > BURST_WRITE_MAX) {
+ pr_err("data size > WRITE_MAX\n");
+ return -EINVAL;
+ }
+
+ if (sub_address + size > 0x100) {
+ pr_err("out of range\n");
+ return -EINVAL;
+ }
+
+ send_data[0] = 0x0e;
+ write_data_top = data;
+
+ while (size > 0) {
+ send_data[1] = sub_address;
+ if (size > 255)
+ send_data[2] = 255;
+ else
+ send_data[2] = (u8)size;
+
+ memcpy(&send_data[3], write_data_top, send_data[2]);
+
+ ret = cxd2880_write_spi(spi, send_data, send_data[2] + 3);
+ if (ret) {
+ pr_err("write spi failed %d\n", ret);
+ break;
+ }
+ sub_address += send_data[2];
+ write_data_top += send_data[2];
+ size -= send_data[2];
+ }
+
+ return ret;
+}
+
+static int cxd2880_spi_read_ts(struct spi_device *spi,
+ u8 *read_data,
+ u32 packet_num)
+{
+ int ret;
+ u8 data[3];
+ struct spi_message message;
+ struct spi_transfer transfer[2];
+
+ if (!spi || !read_data || !packet_num) {
+ pr_err("invalid arg\n");
+ return -EINVAL;
+ }
+ if (packet_num > 0xffff) {
+ pr_err("packet num > 0xffff\n");
+ return -EINVAL;
+ }
+
+ data[0] = 0x10;
+ data[1] = packet_num >> 8;
+ data[2] = packet_num;
+
+ spi_message_init(&message);
+ memset(transfer, 0, sizeof(transfer));
+
+ transfer[0].len = 3;
+ transfer[0].tx_buf = data;
+ spi_message_add_tail(&transfer[0], &message);
+ transfer[1].len = packet_num * 188;
+ transfer[1].rx_buf = read_data;
+ spi_message_add_tail(&transfer[1], &message);
+
+ ret = spi_sync(spi, &message);
+ if (ret)
+ pr_err("spi_write_then_read failed\n");
+
+ return ret;
+}
+
+static int cxd2880_spi_read_ts_buffer_info(struct spi_device *spi,
+ struct cxd2880_ts_buf_info *info)
+{
+ u8 send_data = 0x20;
+ u8 recv_data[2];
+ int ret;
+
+ if (!spi || !info) {
+ pr_err("invalid arg\n");
+ return -EINVAL;
+ }
+
+ ret = spi_write_then_read(spi, &send_data, 1,
+ recv_data, sizeof(recv_data));
+ if (ret)
+ pr_err("spi_write_then_read failed\n");
+
+ info->read_ready = (recv_data[0] & 0x80) ? 1 : 0;
+ info->almost_full = (recv_data[0] & 0x40) ? 1 : 0;
+ info->almost_empty = (recv_data[0] & 0x20) ? 1 : 0;
+ info->overflow = (recv_data[0] & 0x10) ? 1 : 0;
+ info->underflow = (recv_data[0] & 0x08) ? 1 : 0;
+ info->pkt_num = ((recv_data[0] & 0x07) << 8) | recv_data[1];
+
+ return ret;
+}
+
+static int cxd2880_spi_clear_ts_buffer(struct spi_device *spi)
+{
+ u8 data = 0x03;
+ int ret;
+
+ ret = cxd2880_write_spi(spi, &data, 1);
+
+ if (ret)
+ pr_err("write spi failed\n");
+
+ return ret;
+}
+
+static int cxd2880_set_pid_filter(struct spi_device *spi,
+ struct cxd2880_pid_filter_config *cfg)
+{
+ u8 data[65];
+ int i;
+ u16 pid = 0;
+ int ret;
+
+ if (!spi) {
+ pr_err("invalid arg\n");
+ return -EINVAL;
+ }
+
+ data[0] = 0x00;
+ ret = cxd2880_write_reg(spi, 0x00, &data[0], 1);
+ if (ret)
+ return ret;
+ if (!cfg) {
+ data[0] = 0x02;
+ ret = cxd2880_write_reg(spi, 0x50, &data[0], 1);
+ } else {
+ data[0] = cfg->is_negative ? 0x01 : 0x00;
+
+ for (i = 0; i < CXD2880_MAX_FILTER_SIZE; i++) {
+ pid = cfg->pid_config[i].pid;
+ if (cfg->pid_config[i].is_enable) {
+ data[1 + (i * 2)] = (pid >> 8) | 0x20;
+ data[2 + (i * 2)] = pid & 0xff;
+ } else {
+ data[1 + (i * 2)] = 0x00;
+ data[2 + (i * 2)] = 0x00;
+ }
+ }
+ ret = cxd2880_write_reg(spi, 0x50, data, 65);
+ }
+
+ return ret;
+}
+
+static int cxd2880_update_pid_filter(struct cxd2880_dvb_spi *dvb_spi,
+ struct cxd2880_pid_filter_config *cfg,
+ bool is_all_pid_filter)
+{
+ int ret;
+
+ if (!dvb_spi || !cfg) {
+ pr_err("invalid arg.\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&dvb_spi->spi_mutex);
+ if (is_all_pid_filter) {
+ struct cxd2880_pid_filter_config tmpcfg;
+
+ memset(&tmpcfg, 0, sizeof(tmpcfg));
+ tmpcfg.is_negative = 1;
+ tmpcfg.pid_config[0].is_enable = 1;
+ tmpcfg.pid_config[0].pid = 0x1fff;
+
+ ret = cxd2880_set_pid_filter(dvb_spi->spi, &tmpcfg);
+ } else {
+ ret = cxd2880_set_pid_filter(dvb_spi->spi, cfg);
+ }
+ mutex_unlock(&dvb_spi->spi_mutex);
+
+ if (ret)
+ pr_err("set_pid_filter failed\n");
+
+ return ret;
+}
+
+static int cxd2880_ts_read(void *arg)
+{
+ struct cxd2880_dvb_spi *dvb_spi = NULL;
+ struct cxd2880_ts_buf_info info;
+ ktime_t start;
+ u32 i;
+ int ret;
+
+ dvb_spi = arg;
+ if (!dvb_spi) {
+ pr_err("invalid arg\n");
+ return -EINVAL;
+ }
+
+ ret = cxd2880_spi_clear_ts_buffer(dvb_spi->spi);
+ if (ret) {
+ pr_err("set_clear_ts_buffer failed\n");
+ return ret;
+ }
+
+ start = ktime_get();
+ while (!kthread_should_stop()) {
+ ret = cxd2880_spi_read_ts_buffer_info(dvb_spi->spi,
+ &info);
+ if (ret) {
+ pr_err("spi_read_ts_buffer_info error\n");
+ return ret;
+ }
+
+ if (info.pkt_num > MAX_TRANS_PKT) {
+ for (i = 0; i < info.pkt_num / MAX_TRANS_PKT; i++) {
+ cxd2880_spi_read_ts(dvb_spi->spi,
+ dvb_spi->ts_buf,
+ MAX_TRANS_PKT);
+ dvb_dmx_swfilter(&dvb_spi->demux,
+ dvb_spi->ts_buf,
+ MAX_TRANS_PKT * 188);
+ }
+ start = ktime_get();
+ } else if ((info.pkt_num > 0) &&
+ (ktime_to_ms(ktime_sub(ktime_get(), start)) >= 500)) {
+ cxd2880_spi_read_ts(dvb_spi->spi,
+ dvb_spi->ts_buf,
+ info.pkt_num);
+ dvb_dmx_swfilter(&dvb_spi->demux,
+ dvb_spi->ts_buf,
+ info.pkt_num * 188);
+ start = ktime_get();
+ } else {
+ usleep_range(10000, 11000);
+ }
+ }
+
+ return 0;
+}
+
+static int cxd2880_start_feed(struct dvb_demux_feed *feed)
+{
+ int ret = 0;
+ int i = 0;
+ struct dvb_demux *demux = NULL;
+ struct cxd2880_dvb_spi *dvb_spi = NULL;
+
+ if (!feed) {
+ pr_err("invalid arg\n");
+ return -EINVAL;
+ }
+
+ demux = feed->demux;
+ if (!demux) {
+ pr_err("feed->demux is NULL\n");
+ return -EINVAL;
+ }
+ dvb_spi = demux->priv;
+
+ if (dvb_spi->feed_count == CXD2880_MAX_FILTER_SIZE) {
+ pr_err("Exceeded maximum PID count (32).");
+ pr_err("Selected PID cannot be enabled.\n");
+ return -EINVAL;
+ }
+
+ if (feed->pid == 0x2000) {
+ if (dvb_spi->all_pid_feed_count == 0) {
+ ret = cxd2880_update_pid_filter(dvb_spi,
+ &dvb_spi->filter_config,
+ true);
+ if (ret) {
+ pr_err("update pid filter failed\n");
+ return ret;
+ }
+ }
+ dvb_spi->all_pid_feed_count++;
+
+ pr_debug("all PID feed (count = %d)\n",
+ dvb_spi->all_pid_feed_count);
+ } else {
+ struct cxd2880_pid_filter_config cfgtmp;
+
+ cfgtmp = dvb_spi->filter_config;
+
+ for (i = 0; i < CXD2880_MAX_FILTER_SIZE; i++) {
+ if (cfgtmp.pid_config[i].is_enable == 0) {
+ cfgtmp.pid_config[i].is_enable = 1;
+ cfgtmp.pid_config[i].pid = feed->pid;
+ pr_debug("store PID %d to #%d\n",
+ feed->pid, i);
+ break;
+ }
+ }
+ if (i == CXD2880_MAX_FILTER_SIZE) {
+ pr_err("PID filter is full. Assumed bug.\n");
+ return -EINVAL;
+ }
+ if (!dvb_spi->all_pid_feed_count)
+ ret = cxd2880_update_pid_filter(dvb_spi,
+ &cfgtmp,
+ false);
+ if (ret)
+ return ret;
+
+ dvb_spi->filter_config = cfgtmp;
+ }
+
+ if (dvb_spi->feed_count == 0) {
+ dvb_spi->ts_buf =
+ kmalloc(MAX_TRANS_PKT * 188,
+ GFP_KERNEL | GFP_DMA);
+ if (!dvb_spi->ts_buf) {
+ pr_err("ts buffer allocate failed\n");
+ memset(&dvb_spi->filter_config, 0,
+ sizeof(dvb_spi->filter_config));
+ dvb_spi->all_pid_feed_count = 0;
+ return -ENOMEM;
+ }
+ dvb_spi->cxd2880_ts_read_thread = kthread_run(cxd2880_ts_read,
+ dvb_spi,
+ "cxd2880_ts_read");
+ if (IS_ERR(dvb_spi->cxd2880_ts_read_thread)) {
+ pr_err("kthread_run failed/\n");
+ kfree(dvb_spi->ts_buf);
+ dvb_spi->ts_buf = NULL;
+ memset(&dvb_spi->filter_config, 0,
+ sizeof(dvb_spi->filter_config));
+ dvb_spi->all_pid_feed_count = 0;
+ return PTR_ERR(dvb_spi->cxd2880_ts_read_thread);
+ }
+ }
+
+ dvb_spi->feed_count++;
+
+ pr_debug("start feed (count %d)\n", dvb_spi->feed_count);
+ return 0;
+}
+
+static int cxd2880_stop_feed(struct dvb_demux_feed *feed)
+{
+ int i = 0;
+ int ret;
+ struct dvb_demux *demux = NULL;
+ struct cxd2880_dvb_spi *dvb_spi = NULL;
+
+ if (!feed) {
+ pr_err("invalid arg\n");
+ return -EINVAL;
+ }
+
+ demux = feed->demux;
+ if (!demux) {
+ pr_err("feed->demux is NULL\n");
+ return -EINVAL;
+ }
+ dvb_spi = demux->priv;
+
+ if (!dvb_spi->feed_count) {
+ pr_err("no feed is started\n");
+ return -EINVAL;
+ }
+
+ if (feed->pid == 0x2000) {
+ /*
+ * Special PID case.
+ * Number of 0x2000 feed request was stored
+ * in dvb_spi->all_pid_feed_count.
+ */
+ if (dvb_spi->all_pid_feed_count <= 0) {
+ pr_err("PID %d not found.\n", feed->pid);
+ return -EINVAL;
+ }
+ dvb_spi->all_pid_feed_count--;
+ } else {
+ struct cxd2880_pid_filter_config cfgtmp;
+
+ cfgtmp = dvb_spi->filter_config;
+
+ for (i = 0; i < CXD2880_MAX_FILTER_SIZE; i++) {
+ if (feed->pid == cfgtmp.pid_config[i].pid &&
+ cfgtmp.pid_config[i].is_enable != 0) {
+ cfgtmp.pid_config[i].is_enable = 0;
+ cfgtmp.pid_config[i].pid = 0;
+ pr_debug("removed PID %d from #%d\n",
+ feed->pid, i);
+ break;
+ }
+ }
+ dvb_spi->filter_config = cfgtmp;
+
+ if (i == CXD2880_MAX_FILTER_SIZE) {
+ pr_err("PID %d not found\n", feed->pid);
+ return -EINVAL;
+ }
+ }
+
+ ret = cxd2880_update_pid_filter(dvb_spi,
+ &dvb_spi->filter_config,
+ dvb_spi->all_pid_feed_count > 0);
+ dvb_spi->feed_count--;
+
+ if (dvb_spi->feed_count == 0) {
+ int ret_stop = 0;
+
+ ret_stop = kthread_stop(dvb_spi->cxd2880_ts_read_thread);
+ if (ret_stop) {
+ pr_err("'kthread_stop failed. (%d)\n", ret_stop);
+ ret = ret_stop;
+ }
+ kfree(dvb_spi->ts_buf);
+ dvb_spi->ts_buf = NULL;
+ }
+
+ pr_debug("stop feed ok.(count %d)\n", dvb_spi->feed_count);
+
+ return ret;
+}
+
+static const struct of_device_id cxd2880_spi_of_match[] = {
+ { .compatible = "sony,cxd2880" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, cxd2880_spi_of_match);
+
+static int
+cxd2880_spi_probe(struct spi_device *spi)
+{
+ int ret;
+ struct cxd2880_dvb_spi *dvb_spi = NULL;
+ struct cxd2880_config config;
+
+ if (!spi) {
+ pr_err("invalid arg.\n");
+ return -EINVAL;
+ }
+
+ dvb_spi = kzalloc(sizeof(struct cxd2880_dvb_spi), GFP_KERNEL);
+ if (!dvb_spi)
+ return -ENOMEM;
+
+ dvb_spi->spi = spi;
+ mutex_init(&dvb_spi->spi_mutex);
+ dev_set_drvdata(&spi->dev, dvb_spi);
+ config.spi = spi;
+ config.spi_mutex = &dvb_spi->spi_mutex;
+
+ ret = dvb_register_adapter(&dvb_spi->adapter,
+ "CXD2880",
+ THIS_MODULE,
+ &spi->dev,
+ adapter_nr);
+ if (ret < 0) {
+ pr_err("dvb_register_adapter() failed\n");
+ goto fail_adapter;
+ }
+
+ if (!dvb_attach(cxd2880_attach, &dvb_spi->dvb_fe, &config)) {
+ pr_err("cxd2880_attach failed\n");
+ goto fail_attach;
+ }
+
+ ret = dvb_register_frontend(&dvb_spi->adapter,
+ &dvb_spi->dvb_fe);
+ if (ret < 0) {
+ pr_err("dvb_register_frontend() failed\n");
+ goto fail_frontend;
+ }
+
+ dvb_spi->demux.dmx.capabilities = DMX_TS_FILTERING;
+ dvb_spi->demux.priv = dvb_spi;
+ dvb_spi->demux.filternum = CXD2880_MAX_FILTER_SIZE;
+ dvb_spi->demux.feednum = CXD2880_MAX_FILTER_SIZE;
+ dvb_spi->demux.start_feed = cxd2880_start_feed;
+ dvb_spi->demux.stop_feed = cxd2880_stop_feed;
+
+ ret = dvb_dmx_init(&dvb_spi->demux);
+ if (ret < 0) {
+ pr_err("dvb_dmx_init() failed\n");
+ goto fail_dmx;
+ }
+
+ dvb_spi->dmxdev.filternum = CXD2880_MAX_FILTER_SIZE;
+ dvb_spi->dmxdev.demux = &dvb_spi->demux.dmx;
+ dvb_spi->dmxdev.capabilities = 0;
+ ret = dvb_dmxdev_init(&dvb_spi->dmxdev,
+ &dvb_spi->adapter);
+ if (ret < 0) {
+ pr_err("dvb_dmxdev_init() failed\n");
+ goto fail_dmxdev;
+ }
+
+ dvb_spi->dmx_fe.source = DMX_FRONTEND_0;
+ ret = dvb_spi->demux.dmx.add_frontend(&dvb_spi->demux.dmx,
+ &dvb_spi->dmx_fe);
+ if (ret < 0) {
+ pr_err("add_frontend() failed\n");
+ goto fail_dmx_fe;
+ }
+
+ ret = dvb_spi->demux.dmx.connect_frontend(&dvb_spi->demux.dmx,
+ &dvb_spi->dmx_fe);
+ if (ret < 0) {
+ pr_err("dvb_register_frontend() failed\n");
+ goto fail_fe_conn;
+ }
+
+ pr_info("Sony CXD2880 has successfully attached.\n");
+
+ return 0;
+
+fail_fe_conn:
+ dvb_spi->demux.dmx.remove_frontend(&dvb_spi->demux.dmx,
+ &dvb_spi->dmx_fe);
+fail_dmx_fe:
+ dvb_dmxdev_release(&dvb_spi->dmxdev);
+fail_dmxdev:
+ dvb_dmx_release(&dvb_spi->demux);
+fail_dmx:
+ dvb_unregister_frontend(&dvb_spi->dvb_fe);
+fail_frontend:
+ dvb_frontend_detach(&dvb_spi->dvb_fe);
+fail_attach:
+ dvb_unregister_adapter(&dvb_spi->adapter);
+fail_adapter:
+ kfree(dvb_spi);
+ return ret;
+}
+
+static int
+cxd2880_spi_remove(struct spi_device *spi)
+{
+ struct cxd2880_dvb_spi *dvb_spi;
+
+ if (!spi) {
+ pr_err("invalid arg\n");
+ return -EINVAL;
+ }
+
+ dvb_spi = dev_get_drvdata(&spi->dev);
+
+ if (!dvb_spi) {
+ pr_err("failed\n");
+ return -EINVAL;
+ }
+ dvb_spi->demux.dmx.remove_frontend(&dvb_spi->demux.dmx,
+ &dvb_spi->dmx_fe);
+ dvb_dmxdev_release(&dvb_spi->dmxdev);
+ dvb_dmx_release(&dvb_spi->demux);
+ dvb_unregister_frontend(&dvb_spi->dvb_fe);
+ dvb_frontend_detach(&dvb_spi->dvb_fe);
+ dvb_unregister_adapter(&dvb_spi->adapter);
+
+ kfree(dvb_spi);
+ pr_info("cxd2880_spi remove ok.\n");
+
+ return 0;
+}
+
+static const struct spi_device_id cxd2880_spi_id[] = {
+ { "cxd2880", 0 },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(spi, cxd2880_spi_id);
+
+static struct spi_driver cxd2880_spi_driver = {
+ .driver = {
+ .name = "cxd2880",
+ .of_match_table = cxd2880_spi_of_match,
+ },
+ .id_table = cxd2880_spi_id,
+ .probe = cxd2880_spi_probe,
+ .remove = cxd2880_spi_remove,
+};
+module_spi_driver(cxd2880_spi_driver);
+
+MODULE_DESCRIPTION("Sony CXD2880 DVB-T2/T tuner + demod driver SPI adapter");
+MODULE_AUTHOR("Sony Semiconductor Solutions Corporation");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c
index 564a000f503e..b5b9d87ba75c 100644
--- a/drivers/media/tuners/e4000.c
+++ b/drivers/media/tuners/e4000.c
@@ -293,28 +293,18 @@ static inline struct e4000_dev *e4000_subdev_to_dev(struct v4l2_subdev *sd)
return container_of(sd, struct e4000_dev, sd);
}
-static int e4000_s_power(struct v4l2_subdev *sd, int on)
+static int e4000_standby(struct v4l2_subdev *sd)
{
struct e4000_dev *dev = e4000_subdev_to_dev(sd);
- struct i2c_client *client = dev->client;
int ret;
- dev_dbg(&client->dev, "on=%d\n", on);
-
- if (on)
- ret = e4000_init(dev);
- else
- ret = e4000_sleep(dev);
+ ret = e4000_sleep(dev);
if (ret)
return ret;
return e4000_set_params(dev);
}
-static const struct v4l2_subdev_core_ops e4000_subdev_core_ops = {
- .s_power = e4000_s_power,
-};
-
static int e4000_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v)
{
struct e4000_dev *dev = e4000_subdev_to_dev(sd);
@@ -382,6 +372,7 @@ static int e4000_enum_freq_bands(struct v4l2_subdev *sd,
}
static const struct v4l2_subdev_tuner_ops e4000_subdev_tuner_ops = {
+ .standby = e4000_standby,
.g_tuner = e4000_g_tuner,
.s_tuner = e4000_s_tuner,
.g_frequency = e4000_g_frequency,
@@ -390,7 +381,6 @@ static const struct v4l2_subdev_tuner_ops e4000_subdev_tuner_ops = {
};
static const struct v4l2_subdev_ops e4000_subdev_ops = {
- .core = &e4000_subdev_core_ops,
.tuner = &e4000_subdev_tuner_ops,
};
diff --git a/drivers/media/tuners/fc2580.c b/drivers/media/tuners/fc2580.c
index f4d4665de168..743184ae0d26 100644
--- a/drivers/media/tuners/fc2580.c
+++ b/drivers/media/tuners/fc2580.c
@@ -386,28 +386,18 @@ static inline struct fc2580_dev *fc2580_subdev_to_dev(struct v4l2_subdev *sd)
return container_of(sd, struct fc2580_dev, subdev);
}
-static int fc2580_s_power(struct v4l2_subdev *sd, int on)
+static int fc2580_standby(struct v4l2_subdev *sd)
{
struct fc2580_dev *dev = fc2580_subdev_to_dev(sd);
- struct i2c_client *client = dev->client;
int ret;
- dev_dbg(&client->dev, "on=%d\n", on);
-
- if (on)
- ret = fc2580_init(dev);
- else
- ret = fc2580_sleep(dev);
+ ret = fc2580_sleep(dev);
if (ret)
return ret;
return fc2580_set_params(dev);
}
-static const struct v4l2_subdev_core_ops fc2580_subdev_core_ops = {
- .s_power = fc2580_s_power,
-};
-
static int fc2580_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v)
{
struct fc2580_dev *dev = fc2580_subdev_to_dev(sd);
@@ -475,6 +465,7 @@ static int fc2580_enum_freq_bands(struct v4l2_subdev *sd,
}
static const struct v4l2_subdev_tuner_ops fc2580_subdev_tuner_ops = {
+ .standby = fc2580_standby,
.g_tuner = fc2580_g_tuner,
.s_tuner = fc2580_s_tuner,
.g_frequency = fc2580_g_frequency,
@@ -483,7 +474,6 @@ static const struct v4l2_subdev_tuner_ops fc2580_subdev_tuner_ops = {
};
static const struct v4l2_subdev_ops fc2580_subdev_ops = {
- .core = &fc2580_subdev_core_ops,
.tuner = &fc2580_subdev_tuner_ops,
};
diff --git a/drivers/media/tuners/msi001.c b/drivers/media/tuners/msi001.c
index 3a12ef35682b..5de6ed728708 100644
--- a/drivers/media/tuners/msi001.c
+++ b/drivers/media/tuners/msi001.c
@@ -291,26 +291,13 @@ err:
return ret;
}
-static int msi001_s_power(struct v4l2_subdev *sd, int on)
+static int msi001_standby(struct v4l2_subdev *sd)
{
struct msi001_dev *dev = sd_to_msi001_dev(sd);
- struct spi_device *spi = dev->spi;
- int ret;
-
- dev_dbg(&spi->dev, "on=%d\n", on);
-
- if (on)
- ret = 0;
- else
- ret = msi001_wreg(dev, 0x000000);
- return ret;
+ return msi001_wreg(dev, 0x000000);
}
-static const struct v4l2_subdev_core_ops msi001_core_ops = {
- .s_power = msi001_s_power,
-};
-
static int msi001_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v)
{
struct msi001_dev *dev = sd_to_msi001_dev(sd);
@@ -386,6 +373,7 @@ static int msi001_enum_freq_bands(struct v4l2_subdev *sd,
}
static const struct v4l2_subdev_tuner_ops msi001_tuner_ops = {
+ .standby = msi001_standby,
.g_tuner = msi001_g_tuner,
.s_tuner = msi001_s_tuner,
.g_frequency = msi001_g_frequency,
@@ -394,7 +382,6 @@ static const struct v4l2_subdev_tuner_ops msi001_tuner_ops = {
};
static const struct v4l2_subdev_ops msi001_ops = {
- .core = &msi001_core_ops,
.tuner = &msi001_tuner_ops,
};
diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c
index bc9299059f48..3e14b9e2e763 100644
--- a/drivers/media/tuners/r820t.c
+++ b/drivers/media/tuners/r820t.c
@@ -20,6 +20,8 @@
//
// RF Gain set/get is not implemented.
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/videodev2.h>
#include <linux/mutex.h>
#include <linux/slab.h>
@@ -2371,7 +2373,7 @@ err:
err_no_gate:
mutex_unlock(&r820t_list_mutex);
- tuner_info("%s: failed=%d\n", __func__, rc);
+ pr_info("%s: failed=%d\n", __func__, rc);
r820t_release(fe);
return NULL;
}
diff --git a/drivers/media/usb/au0828/Kconfig b/drivers/media/usb/au0828/Kconfig
index bfaa806633df..65fc067eb864 100644
--- a/drivers/media/usb/au0828/Kconfig
+++ b/drivers/media/usb/au0828/Kconfig
@@ -4,7 +4,7 @@ config VIDEO_AU0828
depends on I2C && INPUT && DVB_CORE && USB && VIDEO_V4L2
select I2C_ALGOBIT
select VIDEO_TVEEPROM
- select VIDEOBUF2_VMALLOC
+ select VIDEOBUF2_VMALLOC if VIDEO_V4L2
select DVB_AU8522_DTV if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT
@@ -18,7 +18,8 @@ config VIDEO_AU0828
config VIDEO_AU0828_V4L2
bool "Auvitek AU0828 v4l2 analog video support"
- depends on VIDEO_AU0828 && VIDEO_V4L2
+ depends on VIDEO_AU0828
+ depends on VIDEO_V4L2=y || VIDEO_V4L2=VIDEO_AU0828
select DVB_AU8522_V4L if MEDIA_SUBDRV_AUTOSELECT
select VIDEO_TUNER
default y
diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c
index c765d546114d..964cd7bcdd2c 100644
--- a/drivers/media/usb/au0828/au0828-video.c
+++ b/drivers/media/usb/au0828/au0828-video.c
@@ -1091,8 +1091,8 @@ static int au0828_v4l2_close(struct file *filp)
*/
ret = v4l_enable_media_source(vdev);
if (ret == 0)
- v4l2_device_call_all(&dev->v4l2_dev, 0, core,
- s_power, 0);
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner,
+ standby);
dev->std_set_in_tuner_core = 0;
/* When close the device, set the usb intf0 into alt0 to free
diff --git a/drivers/media/usb/cpia2/cpia2_usb.c b/drivers/media/usb/cpia2/cpia2_usb.c
index f3a1e5b1e57c..b51fc372ca25 100644
--- a/drivers/media/usb/cpia2/cpia2_usb.c
+++ b/drivers/media/usb/cpia2/cpia2_usb.c
@@ -910,9 +910,6 @@ static void cpia2_usb_disconnect(struct usb_interface *intf)
wake_up_interruptible(&cam->wq_stream);
}
- DBG("Releasing interface\n");
- usb_driver_release_interface(&cpia2_driver, intf);
-
LOG("CPiA2 camera disconnected.\n");
}
diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c
index f9ec7fedcd5b..c76b2101193c 100644
--- a/drivers/media/usb/cx231xx/cx231xx-cards.c
+++ b/drivers/media/usb/cx231xx/cx231xx-cards.c
@@ -922,6 +922,85 @@ struct cx231xx_board cx231xx_boards[] = {
.gpio = NULL,
} },
},
+ [CX231XX_BOARD_HAUPPAUGE_935C] = {
+ .name = "Hauppauge WinTV-HVR-935C",
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = 0x60,
+ .tuner_gpio = RDE250_XCV_TUNER,
+ .tuner_sif_gpio = 0x05,
+ .tuner_scl_gpio = 0x1a,
+ .tuner_sda_gpio = 0x1b,
+ .decoder = CX231XX_AVDECODER,
+ .output_mode = OUT_MODE_VIP11,
+ .demod_xfer_mode = 0,
+ .ctl_pin_status_mask = 0xFFFFFFC4,
+ .agc_analog_digital_select_gpio = 0x0c,
+ .gpio_pin_status_mask = 0x4001000,
+ .tuner_i2c_master = I2C_1_MUX_3,
+ .demod_i2c_master = I2C_1_MUX_3,
+ .has_dvb = 1,
+ .demod_addr = 0x64, /* 0xc8 >> 1 */
+ .norm = V4L2_STD_PAL,
+
+ .input = {{
+ .type = CX231XX_VMUX_TELEVISION,
+ .vmux = CX231XX_VIN_3_1,
+ .amux = CX231XX_AMUX_VIDEO,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_COMPOSITE1,
+ .vmux = CX231XX_VIN_2_1,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_SVIDEO,
+ .vmux = CX231XX_VIN_1_1 |
+ (CX231XX_VIN_1_2 << 8) |
+ CX25840_SVIDEO_ON,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ } },
+ },
+ [CX231XX_BOARD_HAUPPAUGE_975] = {
+ .name = "Hauppauge WinTV-HVR-975",
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = 0x60,
+ .tuner_gpio = RDE250_XCV_TUNER,
+ .tuner_sif_gpio = 0x05,
+ .tuner_scl_gpio = 0x1a,
+ .tuner_sda_gpio = 0x1b,
+ .decoder = CX231XX_AVDECODER,
+ .output_mode = OUT_MODE_VIP11,
+ .demod_xfer_mode = 0,
+ .ctl_pin_status_mask = 0xFFFFFFC4,
+ .agc_analog_digital_select_gpio = 0x0c,
+ .gpio_pin_status_mask = 0x4001000,
+ .tuner_i2c_master = I2C_1_MUX_3,
+ .demod_i2c_master = I2C_1_MUX_3,
+ .has_dvb = 1,
+ .demod_addr = 0x59, /* 0xb2 >> 1 */
+ .demod_addr2 = 0x64, /* 0xc8 >> 1 */
+ .norm = V4L2_STD_ALL,
+
+ .input = {{
+ .type = CX231XX_VMUX_TELEVISION,
+ .vmux = CX231XX_VIN_3_1,
+ .amux = CX231XX_AMUX_VIDEO,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_COMPOSITE1,
+ .vmux = CX231XX_VIN_2_1,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_SVIDEO,
+ .vmux = CX231XX_VIN_1_1 |
+ (CX231XX_VIN_1_2 << 8) |
+ CX25840_SVIDEO_ON,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ } },
+ },
};
const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards);
@@ -953,6 +1032,10 @@ struct usb_device_id cx231xx_id_table[] = {
.driver_info = CX231XX_BOARD_HAUPPAUGE_EXETER},
{USB_DEVICE(0x2040, 0xb123),
.driver_info = CX231XX_BOARD_HAUPPAUGE_955Q},
+ {USB_DEVICE(0x2040, 0xb151),
+ .driver_info = CX231XX_BOARD_HAUPPAUGE_935C},
+ {USB_DEVICE(0x2040, 0xb150),
+ .driver_info = CX231XX_BOARD_HAUPPAUGE_975},
{USB_DEVICE(0x2040, 0xb130),
.driver_info = CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx},
{USB_DEVICE(0x2040, 0xb131),
@@ -1135,7 +1218,7 @@ static void cx231xx_config_tuner(struct cx231xx *dev)
static int read_eeprom(struct cx231xx *dev, struct i2c_client *client,
u8 *eedata, int len)
{
- int ret = 0;
+ int ret;
u8 start_offset = 0;
int len_todo = len;
u8 *eedata_cur = eedata;
@@ -1211,6 +1294,8 @@ void cx231xx_card_setup(struct cx231xx *dev)
case CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx:
case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx:
case CX231XX_BOARD_HAUPPAUGE_955Q:
+ case CX231XX_BOARD_HAUPPAUGE_935C:
+ case CX231XX_BOARD_HAUPPAUGE_975:
{
struct eeprom {
struct tveeprom tvee;
diff --git a/drivers/media/usb/cx231xx/cx231xx-dvb.c b/drivers/media/usb/cx231xx/cx231xx-dvb.c
index fb5654062b1a..67ed66712d05 100644
--- a/drivers/media/usb/cx231xx/cx231xx-dvb.c
+++ b/drivers/media/usb/cx231xx/cx231xx-dvb.c
@@ -53,9 +53,10 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
#define CX231XX_DVB_NUM_BUFS 5
#define CX231XX_DVB_MAX_PACKETSIZE 564
#define CX231XX_DVB_MAX_PACKETS 64
+#define CX231XX_DVB_MAX_FRONTENDS 2
struct cx231xx_dvb {
- struct dvb_frontend *frontend;
+ struct dvb_frontend *frontend[CX231XX_DVB_MAX_FRONTENDS];
/* feed count management */
struct mutex lock;
@@ -68,7 +69,7 @@ struct cx231xx_dvb {
struct dmx_frontend fe_hw;
struct dmx_frontend fe_mem;
struct dvb_net net;
- struct i2c_client *i2c_client_demod;
+ struct i2c_client *i2c_client_demod[2];
struct i2c_client *i2c_client_tuner;
};
@@ -79,7 +80,7 @@ static struct s5h1432_config dvico_s5h1432_config = {
.vsb_if = S5H1432_IF_4000,
.inversion = S5H1432_INVERSION_OFF,
.status_mode = S5H1432_DEMODLOCKING,
- .mpeg_timing = S5H1432_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+ .mpeg_timing = S5H1432_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK,
};
static struct tda18271_std_map cnxt_rde253s_tda18271_std_map = {
@@ -108,7 +109,7 @@ static struct s5h1411_config tda18271_s5h1411_config = {
.qam_if = S5H1411_IF_4000,
.inversion = S5H1411_INVERSION_ON,
.status_mode = S5H1411_DEMODLOCKING,
- .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+ .mpeg_timing = S5H1411_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK,
};
static struct s5h1411_config xc5000_s5h1411_config = {
.output_mode = S5H1411_SERIAL_OUTPUT,
@@ -117,7 +118,7 @@ static struct s5h1411_config xc5000_s5h1411_config = {
.qam_if = S5H1411_IF_3250,
.inversion = S5H1411_INVERSION_OFF,
.status_mode = S5H1411_DEMODLOCKING,
- .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+ .mpeg_timing = S5H1411_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK,
};
static struct lgdt3305_config hcw_lgdt3305_config = {
@@ -275,7 +276,7 @@ static int start_streaming(struct cx231xx_dvb *dvb)
if (dev->USE_ISO) {
dev_dbg(dev->dev, "DVB transfer mode is ISO.\n");
- cx231xx_set_alt_setting(dev, INDEX_TS1, 4);
+ cx231xx_set_alt_setting(dev, INDEX_TS1, 5);
rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
if (rc < 0)
return rc;
@@ -386,17 +387,17 @@ static int attach_xc5000(u8 addr, struct cx231xx *dev)
cfg.i2c_adap = cx231xx_get_i2c_adap(dev, dev->board.tuner_i2c_master);
cfg.i2c_addr = addr;
- if (!dev->dvb->frontend) {
+ if (!dev->dvb->frontend[0]) {
dev_err(dev->dev, "%s/2: dvb frontend not attached. Can't attach xc5000\n",
dev->name);
return -EINVAL;
}
- fe = dvb_attach(xc5000_attach, dev->dvb->frontend, &cfg);
+ fe = dvb_attach(xc5000_attach, dev->dvb->frontend[0], &cfg);
if (!fe) {
dev_err(dev->dev, "%s/2: xc5000 attach failed\n", dev->name);
- dvb_frontend_detach(dev->dvb->frontend);
- dev->dvb->frontend = NULL;
+ dvb_frontend_detach(dev->dvb->frontend[0]);
+ dev->dvb->frontend[0] = NULL;
return -EINVAL;
}
@@ -408,9 +409,9 @@ static int attach_xc5000(u8 addr, struct cx231xx *dev)
int cx231xx_set_analog_freq(struct cx231xx *dev, u32 freq)
{
- if ((dev->dvb != NULL) && (dev->dvb->frontend != NULL)) {
+ if (dev->dvb && dev->dvb->frontend[0]) {
- struct dvb_tuner_ops *dops = &dev->dvb->frontend->ops.tuner_ops;
+ struct dvb_tuner_ops *dops = &dev->dvb->frontend[0]->ops.tuner_ops;
if (dops->set_analog_params != NULL) {
struct analog_parameters params;
@@ -421,7 +422,7 @@ int cx231xx_set_analog_freq(struct cx231xx *dev, u32 freq)
/*params.audmode = ; */
/* Set the analog parameters to set the frequency */
- dops->set_analog_params(dev->dvb->frontend, &params);
+ dops->set_analog_params(dev->dvb->frontend[0], &params);
}
}
@@ -433,15 +434,15 @@ int cx231xx_reset_analog_tuner(struct cx231xx *dev)
{
int status = 0;
- if ((dev->dvb != NULL) && (dev->dvb->frontend != NULL)) {
+ if (dev->dvb && dev->dvb->frontend[0]) {
- struct dvb_tuner_ops *dops = &dev->dvb->frontend->ops.tuner_ops;
+ struct dvb_tuner_ops *dops = &dev->dvb->frontend[0]->ops.tuner_ops;
if (dops->init != NULL && !dev->xc_fw_load_done) {
dev_dbg(dev->dev,
"Reloading firmware for XC5000\n");
- status = dops->init(dev->dvb->frontend);
+ status = dops->init(dev->dvb->frontend[0]);
if (status == 0) {
dev->xc_fw_load_done = 1;
dev_dbg(dev->dev,
@@ -481,17 +482,32 @@ static int register_dvb(struct cx231xx_dvb *dvb,
dvb_register_media_controller(&dvb->adapter, dev->media_dev);
/* Ensure all frontends negotiate bus access */
- dvb->frontend->ops.ts_bus_ctrl = cx231xx_dvb_bus_ctrl;
+ dvb->frontend[0]->ops.ts_bus_ctrl = cx231xx_dvb_bus_ctrl;
+ if (dvb->frontend[1])
+ dvb->frontend[1]->ops.ts_bus_ctrl = cx231xx_dvb_bus_ctrl;
dvb->adapter.priv = dev;
/* register frontend */
- result = dvb_register_frontend(&dvb->adapter, dvb->frontend);
+ result = dvb_register_frontend(&dvb->adapter, dvb->frontend[0]);
if (result < 0) {
dev_warn(dev->dev,
"%s: dvb_register_frontend failed (errno = %d)\n",
dev->name, result);
- goto fail_frontend;
+ goto fail_frontend0;
+ }
+
+ if (dvb->frontend[1]) {
+ result = dvb_register_frontend(&dvb->adapter, dvb->frontend[1]);
+ if (result < 0) {
+ dev_warn(dev->dev,
+ "%s: 2nd dvb_register_frontend failed (errno = %d)\n",
+ dev->name, result);
+ goto fail_frontend1;
+ }
+
+ /* MFE lock */
+ dvb->adapter.mfe_shared = 1;
}
/* register demux stuff */
@@ -569,9 +585,14 @@ fail_fe_hw:
fail_dmxdev:
dvb_dmx_release(&dvb->demux);
fail_dmx:
- dvb_unregister_frontend(dvb->frontend);
-fail_frontend:
- dvb_frontend_detach(dvb->frontend);
+ if (dvb->frontend[1])
+ dvb_unregister_frontend(dvb->frontend[1]);
+ dvb_unregister_frontend(dvb->frontend[0]);
+fail_frontend1:
+ if (dvb->frontend[1])
+ dvb_frontend_detach(dvb->frontend[1]);
+fail_frontend0:
+ dvb_frontend_detach(dvb->frontend[0]);
dvb_unregister_adapter(&dvb->adapter);
fail_adapter:
return result;
@@ -585,8 +606,12 @@ static void unregister_dvb(struct cx231xx_dvb *dvb)
dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
dvb_dmxdev_release(&dvb->dmxdev);
dvb_dmx_release(&dvb->demux);
- dvb_unregister_frontend(dvb->frontend);
- dvb_frontend_detach(dvb->frontend);
+ if (dvb->frontend[1])
+ dvb_unregister_frontend(dvb->frontend[1]);
+ dvb_unregister_frontend(dvb->frontend[0]);
+ if (dvb->frontend[1])
+ dvb_frontend_detach(dvb->frontend[1]);
+ dvb_frontend_detach(dvb->frontend[0]);
dvb_unregister_adapter(&dvb->adapter);
/* remove I2C tuner */
client = dvb->i2c_client_tuner;
@@ -595,7 +620,12 @@ static void unregister_dvb(struct cx231xx_dvb *dvb)
i2c_unregister_device(client);
}
/* remove I2C demod */
- client = dvb->i2c_client_demod;
+ client = dvb->i2c_client_demod[1];
+ if (client) {
+ module_put(client->dev.driver->owner);
+ i2c_unregister_device(client);
+ }
+ client = dvb->i2c_client_demod[0];
if (client) {
module_put(client->dev.driver->owner);
i2c_unregister_device(client);
@@ -604,7 +634,7 @@ static void unregister_dvb(struct cx231xx_dvb *dvb)
static int dvb_init(struct cx231xx *dev)
{
- int result = 0;
+ int result;
struct cx231xx_dvb *dvb;
struct i2c_adapter *tuner_i2c;
struct i2c_adapter *demod_i2c;
@@ -635,11 +665,11 @@ static int dvb_init(struct cx231xx *dev)
case CX231XX_BOARD_CNXT_CARRAERA:
case CX231XX_BOARD_CNXT_RDE_250:
- dev->dvb->frontend = dvb_attach(s5h1432_attach,
+ dev->dvb->frontend[0] = dvb_attach(s5h1432_attach,
&dvico_s5h1432_config,
demod_i2c);
- if (dev->dvb->frontend == NULL) {
+ if (!dev->dvb->frontend[0]) {
dev_err(dev->dev,
"Failed to attach s5h1432 front end\n");
result = -EINVAL;
@@ -647,9 +677,9 @@ static int dvb_init(struct cx231xx *dev)
}
/* define general-purpose callback pointer */
- dvb->frontend->callback = cx231xx_tuner_callback;
+ dvb->frontend[0]->callback = cx231xx_tuner_callback;
- if (!dvb_attach(xc5000_attach, dev->dvb->frontend,
+ if (!dvb_attach(xc5000_attach, dev->dvb->frontend[0],
tuner_i2c,
&cnxt_rde250_tunerconfig)) {
result = -EINVAL;
@@ -660,11 +690,11 @@ static int dvb_init(struct cx231xx *dev)
case CX231XX_BOARD_CNXT_SHELBY:
case CX231XX_BOARD_CNXT_RDU_250:
- dev->dvb->frontend = dvb_attach(s5h1411_attach,
+ dev->dvb->frontend[0] = dvb_attach(s5h1411_attach,
&xc5000_s5h1411_config,
demod_i2c);
- if (dev->dvb->frontend == NULL) {
+ if (!dev->dvb->frontend[0]) {
dev_err(dev->dev,
"Failed to attach s5h1411 front end\n");
result = -EINVAL;
@@ -672,9 +702,9 @@ static int dvb_init(struct cx231xx *dev)
}
/* define general-purpose callback pointer */
- dvb->frontend->callback = cx231xx_tuner_callback;
+ dvb->frontend[0]->callback = cx231xx_tuner_callback;
- if (!dvb_attach(xc5000_attach, dev->dvb->frontend,
+ if (!dvb_attach(xc5000_attach, dev->dvb->frontend[0],
tuner_i2c,
&cnxt_rdu250_tunerconfig)) {
result = -EINVAL;
@@ -683,11 +713,11 @@ static int dvb_init(struct cx231xx *dev)
break;
case CX231XX_BOARD_CNXT_RDE_253S:
- dev->dvb->frontend = dvb_attach(s5h1432_attach,
+ dev->dvb->frontend[0] = dvb_attach(s5h1432_attach,
&dvico_s5h1432_config,
demod_i2c);
- if (dev->dvb->frontend == NULL) {
+ if (!dev->dvb->frontend[0]) {
dev_err(dev->dev,
"Failed to attach s5h1432 front end\n");
result = -EINVAL;
@@ -695,9 +725,9 @@ static int dvb_init(struct cx231xx *dev)
}
/* define general-purpose callback pointer */
- dvb->frontend->callback = cx231xx_tuner_callback;
+ dvb->frontend[0]->callback = cx231xx_tuner_callback;
- if (!dvb_attach(tda18271_attach, dev->dvb->frontend,
+ if (!dvb_attach(tda18271_attach, dev->dvb->frontend[0],
0x60, tuner_i2c,
&cnxt_rde253s_tunerconfig)) {
result = -EINVAL;
@@ -707,11 +737,11 @@ static int dvb_init(struct cx231xx *dev)
case CX231XX_BOARD_CNXT_RDU_253S:
case CX231XX_BOARD_KWORLD_UB445_USB_HYBRID:
- dev->dvb->frontend = dvb_attach(s5h1411_attach,
+ dev->dvb->frontend[0] = dvb_attach(s5h1411_attach,
&tda18271_s5h1411_config,
demod_i2c);
- if (dev->dvb->frontend == NULL) {
+ if (!dev->dvb->frontend[0]) {
dev_err(dev->dev,
"Failed to attach s5h1411 front end\n");
result = -EINVAL;
@@ -719,9 +749,9 @@ static int dvb_init(struct cx231xx *dev)
}
/* define general-purpose callback pointer */
- dvb->frontend->callback = cx231xx_tuner_callback;
+ dvb->frontend[0]->callback = cx231xx_tuner_callback;
- if (!dvb_attach(tda18271_attach, dev->dvb->frontend,
+ if (!dvb_attach(tda18271_attach, dev->dvb->frontend[0],
0x60, tuner_i2c,
&cnxt_rde253s_tunerconfig)) {
result = -EINVAL;
@@ -734,11 +764,11 @@ static int dvb_init(struct cx231xx *dev)
"%s: looking for tuner / demod on i2c bus: %d\n",
__func__, i2c_adapter_id(tuner_i2c));
- dev->dvb->frontend = dvb_attach(lgdt3305_attach,
+ dev->dvb->frontend[0] = dvb_attach(lgdt3305_attach,
&hcw_lgdt3305_config,
demod_i2c);
- if (dev->dvb->frontend == NULL) {
+ if (!dev->dvb->frontend[0]) {
dev_err(dev->dev,
"Failed to attach LG3305 front end\n");
result = -EINVAL;
@@ -746,9 +776,9 @@ static int dvb_init(struct cx231xx *dev)
}
/* define general-purpose callback pointer */
- dvb->frontend->callback = cx231xx_tuner_callback;
+ dvb->frontend[0]->callback = cx231xx_tuner_callback;
- dvb_attach(tda18271_attach, dev->dvb->frontend,
+ dvb_attach(tda18271_attach, dev->dvb->frontend[0],
0x60, tuner_i2c,
&hcw_tda18271_config);
break;
@@ -761,7 +791,7 @@ static int dvb_init(struct cx231xx *dev)
/* attach demod */
memset(&si2165_pdata, 0, sizeof(si2165_pdata));
- si2165_pdata.fe = &dev->dvb->frontend;
+ si2165_pdata.fe = &dev->dvb->frontend[0];
si2165_pdata.chip_mode = SI2165_MODE_PLL_XTAL;
si2165_pdata.ref_freq_hz = 16000000;
@@ -771,7 +801,7 @@ static int dvb_init(struct cx231xx *dev)
info.platform_data = &si2165_pdata;
request_module(info.type);
client = i2c_new_device(demod_i2c, &info);
- if (client == NULL || client->dev.driver == NULL || dev->dvb->frontend == NULL) {
+ if (!client || !client->dev.driver || !dev->dvb->frontend[0]) {
dev_err(dev->dev,
"Failed to attach SI2165 front end\n");
result = -EINVAL;
@@ -784,14 +814,14 @@ static int dvb_init(struct cx231xx *dev)
goto out_free;
}
- dvb->i2c_client_demod = client;
+ dvb->i2c_client_demod[0] = client;
- dev->dvb->frontend->ops.i2c_gate_ctrl = NULL;
+ dev->dvb->frontend[0]->ops.i2c_gate_ctrl = NULL;
/* define general-purpose callback pointer */
- dvb->frontend->callback = cx231xx_tuner_callback;
+ dvb->frontend[0]->callback = cx231xx_tuner_callback;
- dvb_attach(tda18271_attach, dev->dvb->frontend,
+ dvb_attach(tda18271_attach, dev->dvb->frontend[0],
0x60,
tuner_i2c,
&hcw_tda18271_config);
@@ -808,7 +838,7 @@ static int dvb_init(struct cx231xx *dev)
/* attach demod */
memset(&si2165_pdata, 0, sizeof(si2165_pdata));
- si2165_pdata.fe = &dev->dvb->frontend;
+ si2165_pdata.fe = &dev->dvb->frontend[0];
si2165_pdata.chip_mode = SI2165_MODE_PLL_EXT;
si2165_pdata.ref_freq_hz = 24000000;
@@ -818,7 +848,7 @@ static int dvb_init(struct cx231xx *dev)
info.platform_data = &si2165_pdata;
request_module(info.type);
client = i2c_new_device(demod_i2c, &info);
- if (client == NULL || client->dev.driver == NULL || dev->dvb->frontend == NULL) {
+ if (!client || !client->dev.driver || !dev->dvb->frontend[0]) {
dev_err(dev->dev,
"Failed to attach SI2165 front end\n");
result = -EINVAL;
@@ -831,18 +861,18 @@ static int dvb_init(struct cx231xx *dev)
goto out_free;
}
- dvb->i2c_client_demod = client;
+ dvb->i2c_client_demod[0] = client;
memset(&info, 0, sizeof(struct i2c_board_info));
- dev->dvb->frontend->ops.i2c_gate_ctrl = NULL;
+ dev->dvb->frontend[0]->ops.i2c_gate_ctrl = NULL;
/* define general-purpose callback pointer */
- dvb->frontend->callback = cx231xx_tuner_callback;
+ dvb->frontend[0]->callback = cx231xx_tuner_callback;
/* attach tuner */
memset(&si2157_config, 0, sizeof(si2157_config));
- si2157_config.fe = dev->dvb->frontend;
+ si2157_config.fe = dev->dvb->frontend[0];
#ifdef CONFIG_MEDIA_CONTROLLER_DVB
si2157_config.mdev = dev->media_dev;
#endif
@@ -857,14 +887,14 @@ static int dvb_init(struct cx231xx *dev)
tuner_i2c,
&info);
if (client == NULL || client->dev.driver == NULL) {
- dvb_frontend_detach(dev->dvb->frontend);
+ dvb_frontend_detach(dev->dvb->frontend[0]);
result = -ENODEV;
goto out_free;
}
if (!try_module_get(client->dev.driver->owner)) {
i2c_unregister_device(client);
- dvb_frontend_detach(dev->dvb->frontend);
+ dvb_frontend_detach(dev->dvb->frontend[0]);
result = -ENODEV;
goto out_free;
}
@@ -882,26 +912,26 @@ static int dvb_init(struct cx231xx *dev)
memset(&info, 0, sizeof(struct i2c_board_info));
- dev->dvb->frontend = dvb_attach(lgdt3306a_attach,
+ dev->dvb->frontend[0] = dvb_attach(lgdt3306a_attach,
&hauppauge_955q_lgdt3306a_config,
demod_i2c
);
- if (dev->dvb->frontend == NULL) {
+ if (!dev->dvb->frontend[0]) {
dev_err(dev->dev,
"Failed to attach LGDT3306A frontend.\n");
result = -EINVAL;
goto out_free;
}
- dev->dvb->frontend->ops.i2c_gate_ctrl = NULL;
+ dev->dvb->frontend[0]->ops.i2c_gate_ctrl = NULL;
/* define general-purpose callback pointer */
- dvb->frontend->callback = cx231xx_tuner_callback;
+ dvb->frontend[0]->callback = cx231xx_tuner_callback;
/* attach tuner */
memset(&si2157_config, 0, sizeof(si2157_config));
- si2157_config.fe = dev->dvb->frontend;
+ si2157_config.fe = dev->dvb->frontend[0];
#ifdef CONFIG_MEDIA_CONTROLLER_DVB
si2157_config.mdev = dev->media_dev;
#endif
@@ -916,14 +946,14 @@ static int dvb_init(struct cx231xx *dev)
tuner_i2c,
&info);
if (client == NULL || client->dev.driver == NULL) {
- dvb_frontend_detach(dev->dvb->frontend);
+ dvb_frontend_detach(dev->dvb->frontend[0]);
result = -ENODEV;
goto out_free;
}
if (!try_module_get(client->dev.driver->owner)) {
i2c_unregister_device(client);
- dvb_frontend_detach(dev->dvb->frontend);
+ dvb_frontend_detach(dev->dvb->frontend[0]);
result = -ENODEV;
goto out_free;
}
@@ -940,11 +970,11 @@ static int dvb_init(struct cx231xx *dev)
"%s: looking for demod on i2c bus: %d\n",
__func__, i2c_adapter_id(tuner_i2c));
- dev->dvb->frontend = dvb_attach(mb86a20s_attach,
+ dev->dvb->frontend[0] = dvb_attach(mb86a20s_attach,
&pv_mb86a20s_config,
demod_i2c);
- if (dev->dvb->frontend == NULL) {
+ if (!dev->dvb->frontend[0]) {
dev_err(dev->dev,
"Failed to attach mb86a20s demod\n");
result = -EINVAL;
@@ -952,9 +982,9 @@ static int dvb_init(struct cx231xx *dev)
}
/* define general-purpose callback pointer */
- dvb->frontend->callback = cx231xx_tuner_callback;
+ dvb->frontend[0]->callback = cx231xx_tuner_callback;
- dvb_attach(tda18271_attach, dev->dvb->frontend,
+ dvb_attach(tda18271_attach, dev->dvb->frontend[0],
0x60, tuner_i2c,
&pv_tda18271_config);
break;
@@ -969,7 +999,7 @@ static int dvb_init(struct cx231xx *dev)
/* attach demodulator chip */
si2168_config.ts_mode = SI2168_TS_SERIAL; /* from *.inf file */
- si2168_config.fe = &dev->dvb->frontend;
+ si2168_config.fe = &dev->dvb->frontend[0];
si2168_config.i2c_adapter = &adapter;
si2168_config.ts_clock_inv = true;
@@ -991,10 +1021,10 @@ static int dvb_init(struct cx231xx *dev)
goto out_free;
}
- dvb->i2c_client_demod = client;
+ dvb->i2c_client_demod[0] = client;
/* attach tuner chip */
- si2157_config.fe = dev->dvb->frontend;
+ si2157_config.fe = dev->dvb->frontend[0];
#ifdef CONFIG_MEDIA_CONTROLLER_DVB
si2157_config.mdev = dev->media_dev;
#endif
@@ -1010,16 +1040,16 @@ static int dvb_init(struct cx231xx *dev)
client = i2c_new_device(tuner_i2c, &info);
if (client == NULL || client->dev.driver == NULL) {
- module_put(dvb->i2c_client_demod->dev.driver->owner);
- i2c_unregister_device(dvb->i2c_client_demod);
+ module_put(dvb->i2c_client_demod[0]->dev.driver->owner);
+ i2c_unregister_device(dvb->i2c_client_demod[0]);
result = -ENODEV;
goto out_free;
}
if (!try_module_get(client->dev.driver->owner)) {
i2c_unregister_device(client);
- module_put(dvb->i2c_client_demod->dev.driver->owner);
- i2c_unregister_device(dvb->i2c_client_demod);
+ module_put(dvb->i2c_client_demod[0]->dev.driver->owner);
+ i2c_unregister_device(dvb->i2c_client_demod[0]);
result = -ENODEV;
goto out_free;
}
@@ -1037,7 +1067,7 @@ static int dvb_init(struct cx231xx *dev)
/* attach demodulator chip */
mn88473_config.i2c_wr_max = 16;
mn88473_config.xtal = 25000000;
- mn88473_config.fe = &dev->dvb->frontend;
+ mn88473_config.fe = &dev->dvb->frontend[0];
strlcpy(info.type, "mn88473", sizeof(info.type));
info.addr = dev->board.demod_addr;
@@ -1057,24 +1087,219 @@ static int dvb_init(struct cx231xx *dev)
goto out_free;
}
- dvb->i2c_client_demod = client;
+ dvb->i2c_client_demod[0] = client;
/* define general-purpose callback pointer */
- dvb->frontend->callback = cx231xx_tuner_callback;
+ dvb->frontend[0]->callback = cx231xx_tuner_callback;
/* attach tuner chip */
- dvb_attach(r820t_attach, dev->dvb->frontend,
+ dvb_attach(r820t_attach, dev->dvb->frontend[0],
tuner_i2c,
&astrometa_t2hybrid_r820t_config);
break;
}
+ case CX231XX_BOARD_HAUPPAUGE_935C:
+ {
+ struct i2c_client *client;
+ struct i2c_adapter *adapter;
+ struct i2c_board_info info = {};
+ struct si2157_config si2157_config = {};
+ struct si2168_config si2168_config = {};
+
+ /* attach demodulator chip */
+ si2168_config.ts_mode = SI2168_TS_SERIAL;
+ si2168_config.fe = &dev->dvb->frontend[0];
+ si2168_config.i2c_adapter = &adapter;
+ si2168_config.ts_clock_inv = true;
+
+ strlcpy(info.type, "si2168", sizeof(info.type));
+ info.addr = dev->board.demod_addr;
+ info.platform_data = &si2168_config;
+
+ request_module(info.type);
+ client = i2c_new_device(demod_i2c, &info);
+ if (client == NULL || client->dev.driver == NULL) {
+ result = -ENODEV;
+ goto out_free;
+ }
+
+ if (!try_module_get(client->dev.driver->owner)) {
+ dev_err(dev->dev,
+ "Failed to attach %s frontend.\n", info.type);
+ i2c_unregister_device(client);
+ result = -ENODEV;
+ goto out_free;
+ }
+
+ dvb->i2c_client_demod[0] = client;
+ dev->dvb->frontend[0]->ops.i2c_gate_ctrl = NULL;
+
+ /* define general-purpose callback pointer */
+ dvb->frontend[0]->callback = cx231xx_tuner_callback;
+
+ /* attach tuner */
+ si2157_config.fe = dev->dvb->frontend[0];
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ si2157_config.mdev = dev->media_dev;
+#endif
+ si2157_config.if_port = 1;
+ si2157_config.inversion = true;
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "si2157", I2C_NAME_SIZE);
+ info.addr = dev->board.tuner_addr;
+ info.platform_data = &si2157_config;
+ request_module("si2157");
+
+ client = i2c_new_device(adapter, &info);
+ if (client == NULL || client->dev.driver == NULL) {
+ module_put(dvb->i2c_client_demod[0]->dev.driver->owner);
+ i2c_unregister_device(dvb->i2c_client_demod[0]);
+ result = -ENODEV;
+ goto out_free;
+ }
+
+ if (!try_module_get(client->dev.driver->owner)) {
+ dev_err(dev->dev,
+ "Failed to obtain %s tuner.\n", info.type);
+ i2c_unregister_device(client);
+ module_put(dvb->i2c_client_demod[0]->dev.driver->owner);
+ i2c_unregister_device(dvb->i2c_client_demod[0]);
+ result = -ENODEV;
+ goto out_free;
+ }
+
+ dev->cx231xx_reset_analog_tuner = NULL;
+ dev->dvb->i2c_client_tuner = client;
+ break;
+ }
+ case CX231XX_BOARD_HAUPPAUGE_975:
+ {
+ struct i2c_client *client;
+ struct i2c_adapter *adapter;
+ struct i2c_adapter *adapter2;
+ struct i2c_board_info info = {};
+ struct si2157_config si2157_config = {};
+ struct lgdt3306a_config lgdt3306a_config = {};
+ struct si2168_config si2168_config = {};
+
+ /* attach first demodulator chip */
+ lgdt3306a_config = hauppauge_955q_lgdt3306a_config;
+ lgdt3306a_config.fe = &dev->dvb->frontend[0];
+ lgdt3306a_config.i2c_adapter = &adapter;
+ lgdt3306a_config.deny_i2c_rptr = 0;
+
+ strlcpy(info.type, "lgdt3306a", sizeof(info.type));
+ info.addr = dev->board.demod_addr;
+ info.platform_data = &lgdt3306a_config;
+
+ request_module(info.type);
+ client = i2c_new_device(demod_i2c, &info);
+ if (client == NULL || client->dev.driver == NULL) {
+ result = -ENODEV;
+ goto out_free;
+ }
+
+ if (!try_module_get(client->dev.driver->owner)) {
+ dev_err(dev->dev,
+ "Failed to attach %s frontend.\n", info.type);
+ i2c_unregister_device(client);
+ result = -ENODEV;
+ goto out_free;
+ }
+
+ dvb->i2c_client_demod[0] = client;
+
+ /* attach second demodulator chip */
+ si2168_config.ts_mode = SI2168_TS_SERIAL;
+ si2168_config.fe = &dev->dvb->frontend[1];
+ si2168_config.i2c_adapter = &adapter2;
+ si2168_config.ts_clock_inv = true;
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "si2168", sizeof(info.type));
+ info.addr = dev->board.demod_addr2;
+ info.platform_data = &si2168_config;
+
+ request_module(info.type);
+ client = i2c_new_device(adapter, &info);
+ if (client == NULL || client->dev.driver == NULL) {
+ dev_err(dev->dev,
+ "Failed to attach %s frontend.\n", info.type);
+ module_put(dvb->i2c_client_demod[0]->dev.driver->owner);
+ i2c_unregister_device(dvb->i2c_client_demod[0]);
+ result = -ENODEV;
+ goto out_free;
+ }
+
+ if (!try_module_get(client->dev.driver->owner)) {
+ i2c_unregister_device(client);
+ module_put(dvb->i2c_client_demod[0]->dev.driver->owner);
+ i2c_unregister_device(dvb->i2c_client_demod[0]);
+ result = -ENODEV;
+ goto out_free;
+ }
+
+ dvb->i2c_client_demod[1] = client;
+ dvb->frontend[1]->id = 1;
+
+ /* define general-purpose callback pointer */
+ dvb->frontend[0]->callback = cx231xx_tuner_callback;
+ dvb->frontend[1]->callback = cx231xx_tuner_callback;
+
+ /* attach tuner */
+ si2157_config.fe = dev->dvb->frontend[0];
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ si2157_config.mdev = dev->media_dev;
+#endif
+ si2157_config.if_port = 1;
+ si2157_config.inversion = true;
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "si2157", I2C_NAME_SIZE);
+ info.addr = dev->board.tuner_addr;
+ info.platform_data = &si2157_config;
+ request_module("si2157");
+
+ client = i2c_new_device(adapter, &info);
+ if (client == NULL || client->dev.driver == NULL) {
+ module_put(dvb->i2c_client_demod[1]->dev.driver->owner);
+ i2c_unregister_device(dvb->i2c_client_demod[1]);
+ module_put(dvb->i2c_client_demod[0]->dev.driver->owner);
+ i2c_unregister_device(dvb->i2c_client_demod[0]);
+ result = -ENODEV;
+ goto out_free;
+ }
+
+ if (!try_module_get(client->dev.driver->owner)) {
+ dev_err(dev->dev,
+ "Failed to obtain %s tuner.\n", info.type);
+ i2c_unregister_device(client);
+ module_put(dvb->i2c_client_demod[1]->dev.driver->owner);
+ i2c_unregister_device(dvb->i2c_client_demod[1]);
+ module_put(dvb->i2c_client_demod[0]->dev.driver->owner);
+ i2c_unregister_device(dvb->i2c_client_demod[0]);
+ result = -ENODEV;
+ goto out_free;
+ }
+
+ dev->cx231xx_reset_analog_tuner = NULL;
+ dvb->i2c_client_tuner = client;
+
+ dvb->frontend[1]->tuner_priv = dvb->frontend[0]->tuner_priv;
+
+ memcpy(&dvb->frontend[1]->ops.tuner_ops,
+ &dvb->frontend[0]->ops.tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+ break;
+ }
default:
dev_err(dev->dev,
"%s/2: The frontend of your DVB/ATSC card isn't supported yet\n",
dev->name);
break;
}
- if (NULL == dvb->frontend) {
+ if (!dvb->frontend[0]) {
dev_err(dev->dev,
"%s/2: frontend initialization failed\n", dev->name);
result = -EINVAL;
diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c
index 5b321b8ada3a..f7fcd733a2ca 100644
--- a/drivers/media/usb/cx231xx/cx231xx-video.c
+++ b/drivers/media/usb/cx231xx/cx231xx-video.c
@@ -1941,7 +1941,7 @@ static int cx231xx_close(struct file *filp)
}
/* Save some power by putting tuner to sleep */
- call_all(dev, core, s_power, 0);
+ call_all(dev, tuner, standby);
/* do this before setting alternate! */
if (dev->USE_ISO)
diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h
index 65b039cf80be..6ffa4bd96484 100644
--- a/drivers/media/usb/cx231xx/cx231xx.h
+++ b/drivers/media/usb/cx231xx/cx231xx.h
@@ -81,6 +81,8 @@
#define CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD 23
#define CX231XX_BOARD_ASTROMETA_T2HYBRID 24
#define CX231XX_BOARD_THE_IMAGING_SOURCE_DFG_USB2_PRO 25
+#define CX231XX_BOARD_HAUPPAUGE_935C 26
+#define CX231XX_BOARD_HAUPPAUGE_975 27
/* Limits minimum and default number of buffers */
#define CX231XX_MIN_BUF 4
@@ -343,6 +345,7 @@ struct cx231xx_board {
/* demod related */
int demod_addr;
+ int demod_addr2;
u8 demod_xfer_mode; /* 0 - Serial; 1 - parallel */
/* GPIO Pins */
diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig
index 0e4944b2b0f4..37053477b84d 100644
--- a/drivers/media/usb/dvb-usb-v2/Kconfig
+++ b/drivers/media/usb/dvb-usb-v2/Kconfig
@@ -15,7 +15,8 @@ config DVB_USB_V2
config DVB_USB_AF9015
tristate "Afatech AF9015 DVB-T USB2.0 support"
- depends on DVB_USB_V2
+ depends on DVB_USB_V2 && I2C_MUX
+ select REGMAP
select DVB_AF9013
select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT
diff --git a/drivers/media/usb/dvb-usb-v2/af9015.c b/drivers/media/usb/dvb-usb-v2/af9015.c
index 8013659c41b1..39f9ffce3caa 100644
--- a/drivers/media/usb/dvb-usb-v2/af9015.c
+++ b/drivers/media/usb/dvb-usb-v2/af9015.c
@@ -29,6 +29,7 @@ static int af9015_ctrl_msg(struct dvb_usb_device *d, struct req_t *req)
#define REQ_HDR_LEN 8 /* send header size */
#define ACK_HDR_LEN 2 /* rece header size */
struct af9015_state *state = d_to_priv(d);
+ struct usb_interface *intf = d->intf;
int ret, wlen, rlen;
u8 write = 1;
@@ -66,23 +67,24 @@ static int af9015_ctrl_msg(struct dvb_usb_device *d, struct req_t *req)
case BOOT:
break;
default:
- dev_err(&d->udev->dev, "%s: unknown command=%d\n",
- KBUILD_MODNAME, req->cmd);
+ dev_err(&intf->dev, "unknown cmd %d\n", req->cmd);
ret = -EIO;
goto error;
}
- /* buffer overflow check */
+ /* Buffer overflow check */
if ((write && (req->data_len > BUF_LEN - REQ_HDR_LEN)) ||
- (!write && (req->data_len > BUF_LEN - ACK_HDR_LEN))) {
- dev_err(&d->udev->dev, "%s: too much data; cmd=%d len=%d\n",
- KBUILD_MODNAME, req->cmd, req->data_len);
+ (!write && (req->data_len > BUF_LEN - ACK_HDR_LEN))) {
+ dev_err(&intf->dev, "too much data, cmd %u, len %u\n",
+ req->cmd, req->data_len);
ret = -EINVAL;
goto error;
}
- /* write receives seq + status = 2 bytes
- read receives seq + status + data = 2 + N bytes */
+ /*
+ * Write receives seq + status = 2 bytes
+ * Read receives seq + status + data = 2 + N bytes
+ */
wlen = REQ_HDR_LEN;
rlen = ACK_HDR_LEN;
if (write) {
@@ -96,15 +98,14 @@ static int af9015_ctrl_msg(struct dvb_usb_device *d, struct req_t *req)
if (req->cmd == DOWNLOAD_FIRMWARE || req->cmd == RECONNECT_USB)
rlen = 0;
- ret = dvb_usbv2_generic_rw_locked(d,
- state->buf, wlen, state->buf, rlen);
+ ret = dvb_usbv2_generic_rw_locked(d, state->buf, wlen,
+ state->buf, rlen);
if (ret)
goto error;
/* check status */
if (rlen && state->buf[1]) {
- dev_err(&d->udev->dev, "%s: command failed=%d\n",
- KBUILD_MODNAME, state->buf[1]);
+ dev_err(&intf->dev, "cmd failed %u\n", state->buf[1]);
ret = -EIO;
goto error;
}
@@ -118,121 +119,66 @@ error:
return ret;
}
-static int af9015_write_regs(struct dvb_usb_device *d, u16 addr, u8 *val,
- u8 len)
-{
- struct req_t req = {WRITE_MEMORY, AF9015_I2C_DEMOD, addr, 0, 0, len,
- val};
- return af9015_ctrl_msg(d, &req);
-}
-
-static int af9015_read_regs(struct dvb_usb_device *d, u16 addr, u8 *val, u8 len)
-{
- struct req_t req = {READ_MEMORY, AF9015_I2C_DEMOD, addr, 0, 0, len,
- val};
- return af9015_ctrl_msg(d, &req);
-}
-
-static int af9015_write_reg(struct dvb_usb_device *d, u16 addr, u8 val)
-{
- return af9015_write_regs(d, addr, &val, 1);
-}
-
-static int af9015_read_reg(struct dvb_usb_device *d, u16 addr, u8 *val)
-{
- return af9015_read_regs(d, addr, val, 1);
-}
-
static int af9015_write_reg_i2c(struct dvb_usb_device *d, u8 addr, u16 reg,
- u8 val)
+ u8 val)
{
struct af9015_state *state = d_to_priv(d);
struct req_t req = {WRITE_I2C, addr, reg, 1, 1, 1, &val};
- if (addr == state->af9013_config[0].i2c_addr ||
- addr == state->af9013_config[1].i2c_addr)
+ if (addr == state->af9013_i2c_addr[0] ||
+ addr == state->af9013_i2c_addr[1])
req.addr_len = 3;
return af9015_ctrl_msg(d, &req);
}
static int af9015_read_reg_i2c(struct dvb_usb_device *d, u8 addr, u16 reg,
- u8 *val)
+ u8 *val)
{
struct af9015_state *state = d_to_priv(d);
struct req_t req = {READ_I2C, addr, reg, 0, 1, 1, val};
- if (addr == state->af9013_config[0].i2c_addr ||
- addr == state->af9013_config[1].i2c_addr)
+ if (addr == state->af9013_i2c_addr[0] ||
+ addr == state->af9013_i2c_addr[1])
req.addr_len = 3;
return af9015_ctrl_msg(d, &req);
}
-static int af9015_do_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit, u8 op)
-{
- int ret;
- u8 val, mask = 0x01;
-
- ret = af9015_read_reg(d, addr, &val);
- if (ret)
- return ret;
-
- mask <<= bit;
- if (op) {
- /* set bit */
- val |= mask;
- } else {
- /* clear bit */
- mask ^= 0xff;
- val &= mask;
- }
-
- return af9015_write_reg(d, addr, val);
-}
-
-static int af9015_set_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit)
-{
- return af9015_do_reg_bit(d, addr, bit, 1);
-}
-
-static int af9015_clear_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit)
-{
- return af9015_do_reg_bit(d, addr, bit, 0);
-}
-
static int af9015_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
- int num)
+ int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
struct af9015_state *state = d_to_priv(d);
+ struct usb_interface *intf = d->intf;
int ret;
u16 addr;
u8 mbox, addr_len;
struct req_t req;
-/*
-The bus lock is needed because there is two tuners both using same I2C-address.
-Due to that the only way to select correct tuner is use demodulator I2C-gate.
-
-................................................
-. AF9015 includes integrated AF9013 demodulator.
-. ____________ ____________ . ____________
-.| uC | | demod | . | tuner |
-.|------------| |------------| . |------------|
-.| AF9015 | | AF9013/5 | . | MXL5003 |
-.| |--+----I2C-------|-----/ -----|-.-----I2C-------| |
-.| | | | addr 0x38 | . | addr 0xc6 |
-.|____________| | |____________| . |____________|
-.................|..............................
- | ____________ ____________
- | | demod | | tuner |
- | |------------| |------------|
- | | AF9013 | | MXL5003 |
- +----I2C-------|-----/ -----|-------I2C-------| |
- | addr 0x3a | | addr 0xc6 |
- |____________| |____________|
-*/
+ /*
+ * I2C multiplexing:
+ * There could be two tuners, both using same I2C address. Demodulator
+ * I2C-gate is only possibility to select correct tuner.
+ *
+ * ...........................................
+ * . AF9015 integrates AF9013 demodulator .
+ * . ____________ ____________ . ____________
+ * .| USB IF | | demod |. | tuner |
+ * .|------------| |------------|. |------------|
+ * .| AF9015 | | AF9013 |. | MXL5003 |
+ * .| |--+--I2C-----|-----/ -----|.----I2C-----| |
+ * .| | | | addr 0x1c |. | addr 0x63 |
+ * .|____________| | |____________|. |____________|
+ * .................|.........................
+ * | ____________ ____________
+ * | | demod | | tuner |
+ * | |------------| |------------|
+ * | | AF9013 | | MXL5003 |
+ * +--I2C-----|-----/ -----|-----I2C-----| |
+ * | addr 0x1d | | addr 0x63 |
+ * |____________| |____________|
+ */
if (msg[0].len == 0 || msg[0].flags & I2C_M_RD) {
addr = 0x0000;
@@ -243,11 +189,11 @@ Due to that the only way to select correct tuner is use demodulator I2C-gate.
mbox = 0;
addr_len = 1;
} else if (msg[0].len == 2) {
- addr = msg[0].buf[0] << 8|msg[0].buf[1] << 0;
+ addr = msg[0].buf[0] << 8 | msg[0].buf[1] << 0;
mbox = 0;
addr_len = 2;
} else {
- addr = msg[0].buf[0] << 8|msg[0].buf[1] << 0;
+ addr = msg[0].buf[0] << 8 | msg[0].buf[1] << 0;
mbox = msg[0].buf[2];
addr_len = 3;
}
@@ -258,7 +204,7 @@ Due to that the only way to select correct tuner is use demodulator I2C-gate.
ret = -EOPNOTSUPP;
goto err;
}
- if (msg[0].addr == state->af9013_config[0].i2c_addr)
+ if (msg[0].addr == state->af9013_i2c_addr[0])
req.cmd = WRITE_MEMORY;
else
req.cmd = WRITE_I2C;
@@ -266,7 +212,7 @@ Due to that the only way to select correct tuner is use demodulator I2C-gate.
req.addr = addr;
req.mbox = mbox;
req.addr_len = addr_len;
- req.data_len = msg[0].len-addr_len;
+ req.data_len = msg[0].len - addr_len;
req.data = &msg[0].buf[addr_len];
ret = af9015_ctrl_msg(d, &req);
} else if (num == 2 && !(msg[0].flags & I2C_M_RD) &&
@@ -276,7 +222,7 @@ Due to that the only way to select correct tuner is use demodulator I2C-gate.
ret = -EOPNOTSUPP;
goto err;
}
- if (msg[0].addr == state->af9013_config[0].i2c_addr)
+ if (msg[0].addr == state->af9013_i2c_addr[0])
req.cmd = READ_MEMORY;
else
req.cmd = READ_I2C;
@@ -293,7 +239,7 @@ Due to that the only way to select correct tuner is use demodulator I2C-gate.
ret = -EOPNOTSUPP;
goto err;
}
- if (msg[0].addr == state->af9013_config[0].i2c_addr) {
+ if (msg[0].addr == state->af9013_i2c_addr[0]) {
ret = -EINVAL;
goto err;
}
@@ -307,15 +253,14 @@ Due to that the only way to select correct tuner is use demodulator I2C-gate.
ret = af9015_ctrl_msg(d, &req);
} else {
ret = -EOPNOTSUPP;
- dev_dbg(&d->udev->dev, "%s: unknown msg, num %u\n",
- __func__, num);
+ dev_dbg(&intf->dev, "unknown msg, num %u\n", num);
}
if (ret)
goto err;
return num;
err:
- dev_dbg(&d->udev->dev, "%s: failed %d\n", __func__, ret);
+ dev_dbg(&intf->dev, "failed %d\n", ret);
return ret;
}
@@ -331,6 +276,7 @@ static struct i2c_algorithm af9015_i2c_algo = {
static int af9015_identify_state(struct dvb_usb_device *d, const char **name)
{
+ struct usb_interface *intf = d->intf;
int ret;
u8 reply;
struct req_t req = {GET_CONFIG, 0, 0, 0, 0, 1, &reply};
@@ -339,7 +285,7 @@ static int af9015_identify_state(struct dvb_usb_device *d, const char **name)
if (ret)
return ret;
- dev_dbg(&d->udev->dev, "%s: reply=%02x\n", __func__, reply);
+ dev_dbg(&intf->dev, "reply %02x\n", reply);
if (reply == 0x02)
ret = WARM;
@@ -350,52 +296,47 @@ static int af9015_identify_state(struct dvb_usb_device *d, const char **name)
}
static int af9015_download_firmware(struct dvb_usb_device *d,
- const struct firmware *fw)
+ const struct firmware *firmware)
{
struct af9015_state *state = d_to_priv(d);
- int i, len, remaining, ret;
+ struct usb_interface *intf = d->intf;
+ int ret, i, rem;
struct req_t req = {DOWNLOAD_FIRMWARE, 0, 0, 0, 0, 0, NULL};
- u16 checksum = 0;
- dev_dbg(&d->udev->dev, "%s:\n", __func__);
+ u16 checksum;
- /* calc checksum */
- for (i = 0; i < fw->size; i++)
- checksum += fw->data[i];
+ dev_dbg(&intf->dev, "\n");
- state->firmware_size = fw->size;
- state->firmware_checksum = checksum;
+ /* Calc checksum, we need it when copy firmware to slave demod */
+ for (i = 0, checksum = 0; i < firmware->size; i++)
+ checksum += firmware->data[i];
- #define FW_ADDR 0x5100 /* firmware start address */
- #define LEN_MAX 55 /* max packet size */
- for (remaining = fw->size; remaining > 0; remaining -= LEN_MAX) {
- len = remaining;
- if (len > LEN_MAX)
- len = LEN_MAX;
-
- req.data_len = len;
- req.data = (u8 *) &fw->data[fw->size - remaining];
- req.addr = FW_ADDR + fw->size - remaining;
+ state->firmware_size = firmware->size;
+ state->firmware_checksum = checksum;
+ #define LEN_MAX (BUF_LEN - REQ_HDR_LEN) /* Max payload size */
+ for (rem = firmware->size; rem > 0; rem -= LEN_MAX) {
+ req.data_len = min(LEN_MAX, rem);
+ req.data = (u8 *)&firmware->data[firmware->size - rem];
+ req.addr = 0x5100 + firmware->size - rem;
ret = af9015_ctrl_msg(d, &req);
if (ret) {
- dev_err(&d->udev->dev,
- "%s: firmware download failed=%d\n",
- KBUILD_MODNAME, ret);
- goto error;
+ dev_err(&intf->dev, "firmware download failed %d\n",
+ ret);
+ goto err;
}
}
- /* firmware loaded, request boot */
req.cmd = BOOT;
req.data_len = 0;
ret = af9015_ctrl_msg(d, &req);
if (ret) {
- dev_err(&d->udev->dev, "%s: firmware boot failed=%d\n",
- KBUILD_MODNAME, ret);
- goto error;
+ dev_err(&intf->dev, "firmware boot failed %d\n", ret);
+ goto err;
}
-error:
+ return 0;
+err:
+ dev_dbg(&intf->dev, "failed %d\n", ret);
return ret;
}
@@ -407,6 +348,7 @@ error:
static int af9015_eeprom_hash(struct dvb_usb_device *d)
{
struct af9015_state *state = d_to_priv(d);
+ struct usb_interface *intf = d->intf;
int ret, i;
u8 buf[AF9015_EEPROM_SIZE];
struct req_t req = {READ_I2C, AF9015_I2C_EEPROM, 0, 0, 1, 1, NULL};
@@ -427,24 +369,24 @@ static int af9015_eeprom_hash(struct dvb_usb_device *d)
}
for (i = 0; i < AF9015_EEPROM_SIZE; i += 16)
- dev_dbg(&d->udev->dev, "%s: %*ph\n", __func__, 16, buf + i);
+ dev_dbg(&intf->dev, "%*ph\n", 16, buf + i);
- dev_dbg(&d->udev->dev, "%s: eeprom sum=%.8x\n",
- __func__, state->eeprom_sum);
+ dev_dbg(&intf->dev, "eeprom sum %.8x\n", state->eeprom_sum);
return 0;
err:
- dev_err(&d->udev->dev, "%s: eeprom failed=%d\n", KBUILD_MODNAME, ret);
+ dev_dbg(&intf->dev, "failed %d\n", ret);
return ret;
}
static int af9015_read_config(struct dvb_usb_device *d)
{
struct af9015_state *state = d_to_priv(d);
+ struct usb_interface *intf = d->intf;
int ret;
u8 val, i, offset = 0;
struct req_t req = {READ_I2C, AF9015_I2C_EEPROM, 0, 0, 1, 1, &val};
- dev_dbg(&d->udev->dev, "%s:\n", __func__);
+ dev_dbg(&intf->dev, "\n");
/* IR remote controller */
req.addr = AF9015_EEPROM_IR_MODE;
@@ -462,7 +404,7 @@ static int af9015_read_config(struct dvb_usb_device *d)
goto error;
state->ir_mode = val;
- dev_dbg(&d->udev->dev, "%s: IR mode=%d\n", __func__, val);
+ dev_dbg(&intf->dev, "ir mode %02x\n", val);
/* TS mode - one or two receivers */
req.addr = AF9015_EEPROM_TS_MODE;
@@ -471,13 +413,9 @@ static int af9015_read_config(struct dvb_usb_device *d)
goto error;
state->dual_mode = val;
- dev_dbg(&d->udev->dev, "%s: TS mode=%d\n", __func__, state->dual_mode);
+ dev_dbg(&intf->dev, "ts mode %02x\n", state->dual_mode);
- /* disable 2nd adapter because we don't have PID-filters */
- if (d->udev->speed == USB_SPEED_FULL)
- state->dual_mode = 0;
-
- state->af9013_config[0].i2c_addr = AF9015_I2C_DEMOD;
+ state->af9013_i2c_addr[0] = AF9015_I2C_DEMOD;
if (state->dual_mode) {
/* read 2nd demodulator I2C address */
@@ -486,7 +424,7 @@ static int af9015_read_config(struct dvb_usb_device *d)
if (ret)
goto error;
- state->af9013_config[1].i2c_addr = val >> 1;
+ state->af9013_i2c_addr[1] = val >> 1;
}
for (i = 0; i < state->dual_mode + 1; i++) {
@@ -499,21 +437,20 @@ static int af9015_read_config(struct dvb_usb_device *d)
goto error;
switch (val) {
case 0:
- state->af9013_config[i].clock = 28800000;
+ state->af9013_pdata[i].clk = 28800000;
break;
case 1:
- state->af9013_config[i].clock = 20480000;
+ state->af9013_pdata[i].clk = 20480000;
break;
case 2:
- state->af9013_config[i].clock = 28000000;
+ state->af9013_pdata[i].clk = 28000000;
break;
case 3:
- state->af9013_config[i].clock = 25000000;
+ state->af9013_pdata[i].clk = 25000000;
break;
}
- dev_dbg(&d->udev->dev, "%s: [%d] xtal=%d set clock=%d\n",
- __func__, i, val,
- state->af9013_config[i].clock);
+ dev_dbg(&intf->dev, "[%d] xtal %02x, clk %u\n",
+ i, val, state->af9013_pdata[i].clk);
/* IF frequency */
req.addr = AF9015_EEPROM_IF1H + offset;
@@ -521,17 +458,17 @@ static int af9015_read_config(struct dvb_usb_device *d)
if (ret)
goto error;
- state->af9013_config[i].if_frequency = val << 8;
+ state->af9013_pdata[i].if_frequency = val << 8;
req.addr = AF9015_EEPROM_IF1L + offset;
ret = af9015_ctrl_msg(d, &req);
if (ret)
goto error;
- state->af9013_config[i].if_frequency += val;
- state->af9013_config[i].if_frequency *= 1000;
- dev_dbg(&d->udev->dev, "%s: [%d] IF frequency=%d\n", __func__,
- i, state->af9013_config[i].if_frequency);
+ state->af9013_pdata[i].if_frequency += val;
+ state->af9013_pdata[i].if_frequency *= 1000;
+ dev_dbg(&intf->dev, "[%d] if frequency %u\n",
+ i, state->af9013_pdata[i].if_frequency);
/* MT2060 IF1 */
req.addr = AF9015_EEPROM_MT2060_IF1H + offset;
@@ -544,8 +481,8 @@ static int af9015_read_config(struct dvb_usb_device *d)
if (ret)
goto error;
state->mt2060_if1[i] += val;
- dev_dbg(&d->udev->dev, "%s: [%d] MT2060 IF1=%d\n", __func__, i,
- state->mt2060_if1[i]);
+ dev_dbg(&intf->dev, "[%d] MT2060 IF1 %u\n",
+ i, state->mt2060_if1[i]);
/* tuner */
req.addr = AF9015_EEPROM_TUNER_ID1 + offset;
@@ -561,71 +498,177 @@ static int af9015_read_config(struct dvb_usb_device *d)
case AF9013_TUNER_TDA18271:
case AF9013_TUNER_QT1010A:
case AF9013_TUNER_TDA18218:
- state->af9013_config[i].spec_inv = 1;
+ state->af9013_pdata[i].spec_inv = 1;
break;
case AF9013_TUNER_MXL5003D:
case AF9013_TUNER_MXL5005D:
case AF9013_TUNER_MXL5005R:
case AF9013_TUNER_MXL5007T:
- state->af9013_config[i].spec_inv = 0;
+ state->af9013_pdata[i].spec_inv = 0;
break;
case AF9013_TUNER_MC44S803:
- state->af9013_config[i].gpio[1] = AF9013_GPIO_LO;
- state->af9013_config[i].spec_inv = 1;
+ state->af9013_pdata[i].gpio[1] = AF9013_GPIO_LO;
+ state->af9013_pdata[i].spec_inv = 1;
break;
default:
- dev_err(&d->udev->dev, "%s: tuner id=%d not " \
- "supported, please report!\n",
- KBUILD_MODNAME, val);
+ dev_err(&intf->dev,
+ "tuner id %02x not supported, please report!\n",
+ val);
return -ENODEV;
}
- state->af9013_config[i].tuner = val;
- dev_dbg(&d->udev->dev, "%s: [%d] tuner id=%d\n",
- __func__, i, val);
+ state->af9013_pdata[i].tuner = val;
+ dev_dbg(&intf->dev, "[%d] tuner id %02x\n", i, val);
}
error:
if (ret)
- dev_err(&d->udev->dev, "%s: eeprom read failed=%d\n",
- KBUILD_MODNAME, ret);
+ dev_err(&intf->dev, "eeprom read failed %d\n", ret);
- /* AverMedia AVerTV Volar Black HD (A850) device have bad EEPROM
- content :-( Override some wrong values here. Ditto for the
- AVerTV Red HD+ (A850T) device. */
+ /*
+ * AverMedia AVerTV Volar Black HD (A850) device have bad EEPROM
+ * content :-( Override some wrong values here. Ditto for the
+ * AVerTV Red HD+ (A850T) device.
+ */
if (le16_to_cpu(d->udev->descriptor.idVendor) == USB_VID_AVERMEDIA &&
- ((le16_to_cpu(d->udev->descriptor.idProduct) ==
- USB_PID_AVERMEDIA_A850) ||
- (le16_to_cpu(d->udev->descriptor.idProduct) ==
- USB_PID_AVERMEDIA_A850T))) {
- dev_dbg(&d->udev->dev,
- "%s: AverMedia A850: overriding config\n",
- __func__);
+ ((le16_to_cpu(d->udev->descriptor.idProduct) == USB_PID_AVERMEDIA_A850) ||
+ (le16_to_cpu(d->udev->descriptor.idProduct) == USB_PID_AVERMEDIA_A850T))) {
+ dev_dbg(&intf->dev, "AverMedia A850: overriding config\n");
/* disable dual mode */
state->dual_mode = 0;
/* set correct IF */
- state->af9013_config[0].if_frequency = 4570000;
+ state->af9013_pdata[0].if_frequency = 4570000;
}
return ret;
}
static int af9015_get_stream_config(struct dvb_frontend *fe, u8 *ts_type,
- struct usb_data_stream_properties *stream)
+ struct usb_data_stream_properties *stream)
{
struct dvb_usb_device *d = fe_to_d(fe);
- dev_dbg(&d->udev->dev, "%s: adap=%d\n", __func__, fe_to_adap(fe)->id);
+ struct usb_interface *intf = d->intf;
+
+ dev_dbg(&intf->dev, "adap %u\n", fe_to_adap(fe)->id);
if (d->udev->speed == USB_SPEED_FULL)
- stream->u.bulk.buffersize = TS_USB11_FRAME_SIZE;
+ stream->u.bulk.buffersize = 5 * 188;
+
+ return 0;
+}
+
+static int af9015_streaming_ctrl(struct dvb_frontend *fe, int onoff)
+{
+ struct dvb_usb_device *d = fe_to_d(fe);
+ struct af9015_state *state = d_to_priv(d);
+ struct usb_interface *intf = d->intf;
+ int ret;
+ unsigned int utmp1, utmp2, reg1, reg2;
+ u8 buf[2];
+ const unsigned int adap_id = fe_to_adap(fe)->id;
+
+ dev_dbg(&intf->dev, "adap id %d, onoff %d\n", adap_id, onoff);
+
+ if (!state->usb_ts_if_configured[adap_id]) {
+ dev_dbg(&intf->dev, "set usb and ts interface\n");
+
+ /* USB IF stream settings */
+ utmp1 = (d->udev->speed == USB_SPEED_FULL ? 5 : 87) * 188 / 4;
+ utmp2 = (d->udev->speed == USB_SPEED_FULL ? 64 : 512) / 4;
+
+ buf[0] = (utmp1 >> 0) & 0xff;
+ buf[1] = (utmp1 >> 8) & 0xff;
+ if (adap_id == 0) {
+ /* 1st USB IF (EP4) stream settings */
+ reg1 = 0xdd88;
+ reg2 = 0xdd0c;
+ } else {
+ /* 2nd USB IF (EP5) stream settings */
+ reg1 = 0xdd8a;
+ reg2 = 0xdd0d;
+ }
+ ret = regmap_bulk_write(state->regmap, reg1, buf, 2);
+ if (ret)
+ goto err;
+ ret = regmap_write(state->regmap, reg2, utmp2);
+ if (ret)
+ goto err;
+
+ /* TS IF settings */
+ if (state->dual_mode) {
+ utmp1 = 0x01;
+ utmp2 = 0x10;
+ } else {
+ utmp1 = 0x00;
+ utmp2 = 0x00;
+ }
+ ret = regmap_update_bits(state->regmap, 0xd50b, 0x01, utmp1);
+ if (ret)
+ goto err;
+ ret = regmap_update_bits(state->regmap, 0xd520, 0x10, utmp2);
+ if (ret)
+ goto err;
+
+ state->usb_ts_if_configured[adap_id] = true;
+ }
+
+ if (adap_id == 0 && onoff) {
+ /* Adapter 0 stream on. EP4: clear NAK, enable, clear reset */
+ ret = regmap_update_bits(state->regmap, 0xdd13, 0x20, 0x00);
+ if (ret)
+ goto err;
+ ret = regmap_update_bits(state->regmap, 0xdd11, 0x20, 0x20);
+ if (ret)
+ goto err;
+ ret = regmap_update_bits(state->regmap, 0xd507, 0x04, 0x00);
+ if (ret)
+ goto err;
+ } else if (adap_id == 1 && onoff) {
+ /* Adapter 1 stream on. EP5: clear NAK, enable, clear reset */
+ ret = regmap_update_bits(state->regmap, 0xdd13, 0x40, 0x00);
+ if (ret)
+ goto err;
+ ret = regmap_update_bits(state->regmap, 0xdd11, 0x40, 0x40);
+ if (ret)
+ goto err;
+ ret = regmap_update_bits(state->regmap, 0xd50b, 0x02, 0x00);
+ if (ret)
+ goto err;
+ } else if (adap_id == 0 && !onoff) {
+ /* Adapter 0 stream off. EP4: set reset, disable, set NAK */
+ ret = regmap_update_bits(state->regmap, 0xd507, 0x04, 0x04);
+ if (ret)
+ goto err;
+ ret = regmap_update_bits(state->regmap, 0xdd11, 0x20, 0x00);
+ if (ret)
+ goto err;
+ ret = regmap_update_bits(state->regmap, 0xdd13, 0x20, 0x20);
+ if (ret)
+ goto err;
+ } else if (adap_id == 1 && !onoff) {
+ /* Adapter 1 stream off. EP5: set reset, disable, set NAK */
+ ret = regmap_update_bits(state->regmap, 0xd50b, 0x02, 0x02);
+ if (ret)
+ goto err;
+ ret = regmap_update_bits(state->regmap, 0xdd11, 0x40, 0x00);
+ if (ret)
+ goto err;
+ ret = regmap_update_bits(state->regmap, 0xdd13, 0x40, 0x40);
+ if (ret)
+ goto err;
+ }
return 0;
+err:
+ dev_dbg(&intf->dev, "failed %d\n", ret);
+ return ret;
}
static int af9015_get_adapter_count(struct dvb_usb_device *d)
{
struct af9015_state *state = d_to_priv(d);
+
return state->dual_mode + 1;
}
@@ -647,7 +690,7 @@ static int af9015_af9013_set_frontend(struct dvb_frontend *fe)
/* override demod callbacks for resource locking */
static int af9015_af9013_read_status(struct dvb_frontend *fe,
- enum fe_status *status)
+ enum fe_status *status)
{
int ret;
struct af9015_state *state = fe_to_priv(fe);
@@ -729,102 +772,105 @@ static int af9015_tuner_sleep(struct dvb_frontend *fe)
static int af9015_copy_firmware(struct dvb_usb_device *d)
{
struct af9015_state *state = d_to_priv(d);
+ struct usb_interface *intf = d->intf;
int ret;
- u8 fw_params[4];
- u8 val, i;
- struct req_t req = {COPY_FIRMWARE, 0, 0x5100, 0, 0, sizeof(fw_params),
- fw_params };
- dev_dbg(&d->udev->dev, "%s:\n", __func__);
-
- fw_params[0] = state->firmware_size >> 8;
- fw_params[1] = state->firmware_size & 0xff;
- fw_params[2] = state->firmware_checksum >> 8;
- fw_params[3] = state->firmware_checksum & 0xff;
-
- ret = af9015_read_reg_i2c(d, state->af9013_config[1].i2c_addr,
- 0x98be, &val);
- if (ret)
- goto error;
- else
- dev_dbg(&d->udev->dev, "%s: firmware status=%02x\n",
- __func__, val);
+ unsigned long timeout;
+ u8 val, firmware_info[4];
+ struct req_t req = {COPY_FIRMWARE, 0, 0x5100, 0, 0, 4, firmware_info};
- if (val == 0x0c) /* fw is running, no need for download */
- goto exit;
+ dev_dbg(&intf->dev, "\n");
- /* set I2C master clock to fast (to speed up firmware copy) */
- ret = af9015_write_reg(d, 0xd416, 0x04); /* 0x04 * 400ns */
+ firmware_info[0] = (state->firmware_size >> 8) & 0xff;
+ firmware_info[1] = (state->firmware_size >> 0) & 0xff;
+ firmware_info[2] = (state->firmware_checksum >> 8) & 0xff;
+ firmware_info[3] = (state->firmware_checksum >> 0) & 0xff;
+
+ /* Check whether firmware is already running */
+ ret = af9015_read_reg_i2c(d, state->af9013_i2c_addr[1], 0x98be, &val);
if (ret)
- goto error;
+ goto err;
- msleep(50);
+ dev_dbg(&intf->dev, "firmware status %02x\n", val);
- /* copy firmware */
- ret = af9015_ctrl_msg(d, &req);
+ if (val == 0x0c)
+ return 0;
+
+ /* Set i2c clock to 625kHz to speed up firmware copy */
+ ret = regmap_write(state->regmap, 0xd416, 0x04);
if (ret)
- dev_err(&d->udev->dev, "%s: firmware copy cmd failed=%d\n",
- KBUILD_MODNAME, ret);
+ goto err;
- dev_dbg(&d->udev->dev, "%s: firmware copy done\n", __func__);
+ /* Copy firmware from master demod to slave demod */
+ ret = af9015_ctrl_msg(d, &req);
+ if (ret) {
+ dev_err(&intf->dev, "firmware copy cmd failed %d\n", ret);
+ goto err;
+ }
- /* set I2C master clock back to normal */
- ret = af9015_write_reg(d, 0xd416, 0x14); /* 0x14 * 400ns */
+ /* Set i2c clock to 125kHz */
+ ret = regmap_write(state->regmap, 0xd416, 0x14);
if (ret)
- goto error;
+ goto err;
- /* request boot firmware */
- ret = af9015_write_reg_i2c(d, state->af9013_config[1].i2c_addr,
- 0xe205, 1);
- dev_dbg(&d->udev->dev, "%s: firmware boot cmd status=%d\n",
- __func__, ret);
+ /* Boot firmware */
+ ret = af9015_write_reg_i2c(d, state->af9013_i2c_addr[1], 0xe205, 0x01);
if (ret)
- goto error;
+ goto err;
- for (i = 0; i < 15; i++) {
- msleep(100);
+ /* Poll firmware ready */
+ for (val = 0x00, timeout = jiffies + msecs_to_jiffies(1000);
+ !time_after(jiffies, timeout) && val != 0x0c && val != 0x04;) {
+ msleep(20);
- /* check firmware status */
- ret = af9015_read_reg_i2c(d, state->af9013_config[1].i2c_addr,
- 0x98be, &val);
- dev_dbg(&d->udev->dev, "%s: firmware status cmd status=%d " \
- "firmware status=%02x\n", __func__, ret, val);
+ /* Check firmware status. 0c=OK, 04=fail */
+ ret = af9015_read_reg_i2c(d, state->af9013_i2c_addr[1],
+ 0x98be, &val);
if (ret)
- goto error;
+ goto err;
- if (val == 0x0c || val == 0x04) /* success or fail */
- break;
+ dev_dbg(&intf->dev, "firmware status %02x\n", val);
}
+ dev_dbg(&intf->dev, "firmware boot took %u ms\n",
+ jiffies_to_msecs(jiffies) - (jiffies_to_msecs(timeout) - 1000));
+
if (val == 0x04) {
- dev_err(&d->udev->dev, "%s: firmware did not run\n",
- KBUILD_MODNAME);
- ret = -ETIMEDOUT;
+ ret = -ENODEV;
+ dev_err(&intf->dev, "firmware did not run\n");
+ goto err;
} else if (val != 0x0c) {
- dev_err(&d->udev->dev, "%s: firmware boot timeout\n",
- KBUILD_MODNAME);
ret = -ETIMEDOUT;
+ dev_err(&intf->dev, "firmware boot timeout\n");
+ goto err;
}
-error:
-exit:
+ return 0;
+err:
+ dev_dbg(&intf->dev, "failed %d\n", ret);
return ret;
}
static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap)
{
- int ret;
struct af9015_state *state = adap_to_priv(adap);
+ struct dvb_usb_device *d = adap_to_d(adap);
+ struct usb_interface *intf = d->intf;
+ struct i2c_client *client;
+ int ret;
+
+ dev_dbg(&intf->dev, "adap id %u\n", adap->id);
if (adap->id == 0) {
- state->af9013_config[0].ts_mode = AF9013_TS_USB;
- memcpy(state->af9013_config[0].api_version, "\x0\x1\x9\x0", 4);
- state->af9013_config[0].gpio[0] = AF9013_GPIO_HI;
- state->af9013_config[0].gpio[3] = AF9013_GPIO_TUNER_ON;
+ state->af9013_pdata[0].ts_mode = AF9013_TS_MODE_USB;
+ memcpy(state->af9013_pdata[0].api_version, "\x0\x1\x9\x0", 4);
+ state->af9013_pdata[0].gpio[0] = AF9013_GPIO_HI;
+ state->af9013_pdata[0].gpio[3] = AF9013_GPIO_TUNER_ON;
} else if (adap->id == 1) {
- state->af9013_config[1].ts_mode = AF9013_TS_SERIAL;
- memcpy(state->af9013_config[1].api_version, "\x0\x1\x9\x0", 4);
- state->af9013_config[1].gpio[0] = AF9013_GPIO_TUNER_ON;
- state->af9013_config[1].gpio[1] = AF9013_GPIO_LO;
+ state->af9013_pdata[1].ts_mode = AF9013_TS_MODE_SERIAL;
+ state->af9013_pdata[1].ts_output_pin = 7;
+ memcpy(state->af9013_pdata[1].api_version, "\x0\x1\x9\x0", 4);
+ state->af9013_pdata[1].gpio[0] = AF9013_GPIO_TUNER_ON;
+ state->af9013_pdata[1].gpio[1] = AF9013_GPIO_LO;
/* copy firmware to 2nd demodulator */
if (state->dual_mode) {
@@ -833,21 +879,27 @@ static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap)
ret = af9015_copy_firmware(adap_to_d(adap));
if (ret) {
- dev_err(&adap_to_d(adap)->udev->dev,
- "%s: firmware copy to 2nd " \
- "frontend failed, will " \
- "disable it\n", KBUILD_MODNAME);
+ dev_err(&intf->dev,
+ "firmware copy to 2nd frontend failed, will disable it\n");
state->dual_mode = 0;
- return -ENODEV;
+ goto err;
}
} else {
- return -ENODEV;
+ ret = -ENODEV;
+ goto err;
}
}
- /* attach demodulator */
- adap->fe[0] = dvb_attach(af9013_attach,
- &state->af9013_config[adap->id], &adap_to_d(adap)->i2c_adap);
+ /* Add I2C demod */
+ client = dvb_module_probe("af9013", NULL, &d->i2c_adap,
+ state->af9013_i2c_addr[adap->id],
+ &state->af9013_pdata[adap->id]);
+ if (!client) {
+ ret = -ENODEV;
+ goto err;
+ }
+ adap->fe[0] = state->af9013_pdata[adap->id].get_dvb_frontend(client);
+ state->demod_i2c_client[adap->id] = client;
/*
* AF9015 firmware does not like if it gets interrupted by I2C adapter
@@ -857,24 +909,36 @@ static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap)
* those "critical" paths to keep AF9015 happy.
*/
if (adap->fe[0]) {
- state->set_frontend[adap->id] =
- adap->fe[0]->ops.set_frontend;
- adap->fe[0]->ops.set_frontend =
- af9015_af9013_set_frontend;
-
- state->read_status[adap->id] =
- adap->fe[0]->ops.read_status;
- adap->fe[0]->ops.read_status =
- af9015_af9013_read_status;
-
+ state->set_frontend[adap->id] = adap->fe[0]->ops.set_frontend;
+ adap->fe[0]->ops.set_frontend = af9015_af9013_set_frontend;
+ state->read_status[adap->id] = adap->fe[0]->ops.read_status;
+ adap->fe[0]->ops.read_status = af9015_af9013_read_status;
state->init[adap->id] = adap->fe[0]->ops.init;
adap->fe[0]->ops.init = af9015_af9013_init;
-
state->sleep[adap->id] = adap->fe[0]->ops.sleep;
adap->fe[0]->ops.sleep = af9015_af9013_sleep;
}
- return adap->fe[0] == NULL ? -ENODEV : 0;
+ return 0;
+err:
+ dev_dbg(&intf->dev, "failed %d\n", ret);
+ return ret;
+}
+
+static int af9015_frontend_detach(struct dvb_usb_adapter *adap)
+{
+ struct af9015_state *state = adap_to_priv(adap);
+ struct dvb_usb_device *d = adap_to_d(adap);
+ struct usb_interface *intf = d->intf;
+ struct i2c_client *client;
+
+ dev_dbg(&intf->dev, "adap id %u\n", adap->id);
+
+ /* Remove I2C demod */
+ client = state->demod_i2c_client[adap->id];
+ dvb_module_release(client);
+
+ return 0;
}
static struct mt2060_config af9015_mt2060_config = {
@@ -944,64 +1008,61 @@ static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dvb_usb_device *d = adap_to_d(adap);
struct af9015_state *state = d_to_priv(d);
+ struct usb_interface *intf = d->intf;
+ struct i2c_client *client;
+ struct i2c_adapter *adapter;
int ret;
- dev_dbg(&d->udev->dev, "%s:\n", __func__);
- switch (state->af9013_config[adap->id].tuner) {
+ dev_dbg(&intf->dev, "adap id %u\n", adap->id);
+
+ client = state->demod_i2c_client[adap->id];
+ adapter = state->af9013_pdata[adap->id].get_i2c_adapter(client);
+
+ switch (state->af9013_pdata[adap->id].tuner) {
case AF9013_TUNER_MT2060:
case AF9013_TUNER_MT2060_2:
- ret = dvb_attach(mt2060_attach, adap->fe[0],
- &adap_to_d(adap)->i2c_adap, &af9015_mt2060_config,
- state->mt2060_if1[adap->id])
- == NULL ? -ENODEV : 0;
+ ret = dvb_attach(mt2060_attach, adap->fe[0], adapter,
+ &af9015_mt2060_config,
+ state->mt2060_if1[adap->id]) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_QT1010:
case AF9013_TUNER_QT1010A:
- ret = dvb_attach(qt1010_attach, adap->fe[0],
- &adap_to_d(adap)->i2c_adap,
- &af9015_qt1010_config) == NULL ? -ENODEV : 0;
+ ret = dvb_attach(qt1010_attach, adap->fe[0], adapter,
+ &af9015_qt1010_config) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_TDA18271:
- ret = dvb_attach(tda18271_attach, adap->fe[0], 0x60,
- &adap_to_d(adap)->i2c_adap,
- &af9015_tda18271_config) == NULL ? -ENODEV : 0;
+ ret = dvb_attach(tda18271_attach, adap->fe[0], 0x60, adapter,
+ &af9015_tda18271_config) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_TDA18218:
- ret = dvb_attach(tda18218_attach, adap->fe[0],
- &adap_to_d(adap)->i2c_adap,
- &af9015_tda18218_config) == NULL ? -ENODEV : 0;
+ ret = dvb_attach(tda18218_attach, adap->fe[0], adapter,
+ &af9015_tda18218_config) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_MXL5003D:
- ret = dvb_attach(mxl5005s_attach, adap->fe[0],
- &adap_to_d(adap)->i2c_adap,
- &af9015_mxl5003_config) == NULL ? -ENODEV : 0;
+ ret = dvb_attach(mxl5005s_attach, adap->fe[0], adapter,
+ &af9015_mxl5003_config) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_MXL5005D:
case AF9013_TUNER_MXL5005R:
- ret = dvb_attach(mxl5005s_attach, adap->fe[0],
- &adap_to_d(adap)->i2c_adap,
- &af9015_mxl5005_config) == NULL ? -ENODEV : 0;
+ ret = dvb_attach(mxl5005s_attach, adap->fe[0], adapter,
+ &af9015_mxl5005_config) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_ENV77H11D5:
- ret = dvb_attach(dvb_pll_attach, adap->fe[0], 0x60,
- &adap_to_d(adap)->i2c_adap,
- DVB_PLL_TDA665X) == NULL ? -ENODEV : 0;
+ ret = dvb_attach(dvb_pll_attach, adap->fe[0], 0x60, adapter,
+ DVB_PLL_TDA665X) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_MC44S803:
- ret = dvb_attach(mc44s803_attach, adap->fe[0],
- &adap_to_d(adap)->i2c_adap,
- &af9015_mc44s803_config) == NULL ? -ENODEV : 0;
+ ret = dvb_attach(mc44s803_attach, adap->fe[0], adapter,
+ &af9015_mc44s803_config) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_MXL5007T:
- ret = dvb_attach(mxl5007t_attach, adap->fe[0],
- &adap_to_d(adap)->i2c_adap,
- 0x60, &af9015_mxl5007t_config) == NULL ? -ENODEV : 0;
+ ret = dvb_attach(mxl5007t_attach, adap->fe[0], adapter,
+ 0x60, &af9015_mxl5007t_config) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_UNKNOWN:
default:
- dev_err(&d->udev->dev, "%s: unknown tuner id=%d\n",
- KBUILD_MODNAME,
- state->af9013_config[adap->id].tuner);
+ dev_err(&intf->dev, "unknown tuner, tuner id %02x\n",
+ state->af9013_pdata[adap->id].tuner);
ret = -ENODEV;
}
@@ -1022,136 +1083,27 @@ static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
static int af9015_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
- struct dvb_usb_device *d = adap_to_d(adap);
- int ret;
- dev_dbg(&d->udev->dev, "%s: onoff=%d\n", __func__, onoff);
-
- if (onoff)
- ret = af9015_set_reg_bit(d, 0xd503, 0);
- else
- ret = af9015_clear_reg_bit(d, 0xd503, 0);
-
- return ret;
-}
-
-static int af9015_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid,
- int onoff)
-{
- struct dvb_usb_device *d = adap_to_d(adap);
+ struct af9015_state *state = adap_to_priv(adap);
+ struct af9013_platform_data *pdata = &state->af9013_pdata[adap->id];
int ret;
- u8 idx;
- dev_dbg(&d->udev->dev, "%s: index=%d pid=%04x onoff=%d\n",
- __func__, index, pid, onoff);
-
- ret = af9015_write_reg(d, 0xd505, (pid & 0xff));
- if (ret)
- goto error;
- ret = af9015_write_reg(d, 0xd506, (pid >> 8));
- if (ret)
- goto error;
-
- idx = ((index & 0x1f) | (1 << 5));
- ret = af9015_write_reg(d, 0xd504, idx);
+ mutex_lock(&state->fe_mutex);
+ ret = pdata->pid_filter_ctrl(adap->fe[0], onoff);
+ mutex_unlock(&state->fe_mutex);
-error:
return ret;
}
-static int af9015_init_endpoint(struct dvb_usb_device *d)
+static int af9015_pid_filter(struct dvb_usb_adapter *adap, int index,
+ u16 pid, int onoff)
{
- struct af9015_state *state = d_to_priv(d);
+ struct af9015_state *state = adap_to_priv(adap);
+ struct af9013_platform_data *pdata = &state->af9013_pdata[adap->id];
int ret;
- u16 frame_size;
- u8 packet_size;
- dev_dbg(&d->udev->dev, "%s: USB speed=%d\n", __func__, d->udev->speed);
-
- if (d->udev->speed == USB_SPEED_FULL) {
- frame_size = TS_USB11_FRAME_SIZE/4;
- packet_size = TS_USB11_MAX_PACKET_SIZE/4;
- } else {
- frame_size = TS_USB20_FRAME_SIZE/4;
- packet_size = TS_USB20_MAX_PACKET_SIZE/4;
- }
-
- ret = af9015_set_reg_bit(d, 0xd507, 2); /* assert EP4 reset */
- if (ret)
- goto error;
- ret = af9015_set_reg_bit(d, 0xd50b, 1); /* assert EP5 reset */
- if (ret)
- goto error;
- ret = af9015_clear_reg_bit(d, 0xdd11, 5); /* disable EP4 */
- if (ret)
- goto error;
- ret = af9015_clear_reg_bit(d, 0xdd11, 6); /* disable EP5 */
- if (ret)
- goto error;
- ret = af9015_set_reg_bit(d, 0xdd11, 5); /* enable EP4 */
- if (ret)
- goto error;
- if (state->dual_mode) {
- ret = af9015_set_reg_bit(d, 0xdd11, 6); /* enable EP5 */
- if (ret)
- goto error;
- }
- ret = af9015_clear_reg_bit(d, 0xdd13, 5); /* disable EP4 NAK */
- if (ret)
- goto error;
- if (state->dual_mode) {
- ret = af9015_clear_reg_bit(d, 0xdd13, 6); /* disable EP5 NAK */
- if (ret)
- goto error;
- }
- /* EP4 xfer length */
- ret = af9015_write_reg(d, 0xdd88, frame_size & 0xff);
- if (ret)
- goto error;
- ret = af9015_write_reg(d, 0xdd89, frame_size >> 8);
- if (ret)
- goto error;
- /* EP5 xfer length */
- ret = af9015_write_reg(d, 0xdd8a, frame_size & 0xff);
- if (ret)
- goto error;
- ret = af9015_write_reg(d, 0xdd8b, frame_size >> 8);
- if (ret)
- goto error;
- ret = af9015_write_reg(d, 0xdd0c, packet_size); /* EP4 packet size */
- if (ret)
- goto error;
- ret = af9015_write_reg(d, 0xdd0d, packet_size); /* EP5 packet size */
- if (ret)
- goto error;
- ret = af9015_clear_reg_bit(d, 0xd507, 2); /* negate EP4 reset */
- if (ret)
- goto error;
- if (state->dual_mode) {
- ret = af9015_clear_reg_bit(d, 0xd50b, 1); /* negate EP5 reset */
- if (ret)
- goto error;
- }
- /* enable / disable mp2if2 */
- if (state->dual_mode) {
- ret = af9015_set_reg_bit(d, 0xd50b, 0);
- if (ret)
- goto error;
- ret = af9015_set_reg_bit(d, 0xd520, 4);
- if (ret)
- goto error;
- } else {
- ret = af9015_clear_reg_bit(d, 0xd50b, 0);
- if (ret)
- goto error;
- ret = af9015_clear_reg_bit(d, 0xd520, 4);
- if (ret)
- goto error;
- }
-
-error:
- if (ret)
- dev_err(&d->udev->dev, "%s: endpoint init failed=%d\n",
- KBUILD_MODNAME, ret);
+ mutex_lock(&state->fe_mutex);
+ ret = pdata->pid_filter(adap->fe[0], index, pid, onoff);
+ mutex_unlock(&state->fe_mutex);
return ret;
}
@@ -1159,17 +1111,15 @@ error:
static int af9015_init(struct dvb_usb_device *d)
{
struct af9015_state *state = d_to_priv(d);
+ struct usb_interface *intf = d->intf;
int ret;
- dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ dev_dbg(&intf->dev, "\n");
mutex_init(&state->fe_mutex);
/* init RC canary */
- ret = af9015_write_reg(d, 0x98e9, 0xff);
- if (ret)
- goto error;
-
- ret = af9015_init_endpoint(d);
+ ret = regmap_write(state->regmap, 0x98e9, 0xff);
if (ret)
goto error;
@@ -1184,7 +1134,7 @@ struct af9015_rc_setup {
};
static char *af9015_rc_setup_match(unsigned int id,
- const struct af9015_rc_setup *table)
+ const struct af9015_rc_setup *table)
{
for (; table->rc_codes; table++)
if (table->id == id)
@@ -1212,24 +1162,25 @@ static const struct af9015_rc_setup af9015_rc_setup_hashes[] = {
static int af9015_rc_query(struct dvb_usb_device *d)
{
struct af9015_state *state = d_to_priv(d);
+ struct usb_interface *intf = d->intf;
int ret;
u8 buf[17];
/* read registers needed to detect remote controller code */
- ret = af9015_read_regs(d, 0x98d9, buf, sizeof(buf));
+ ret = regmap_bulk_read(state->regmap, 0x98d9, buf, sizeof(buf));
if (ret)
goto error;
/* If any of these are non-zero, assume invalid data */
if (buf[1] || buf[2] || buf[3]) {
- dev_dbg(&d->udev->dev, "%s: invalid data\n", __func__);
+ dev_dbg(&intf->dev, "invalid data\n");
return ret;
}
/* Check for repeat of previous code */
if ((state->rc_repeat != buf[6] || buf[0]) &&
- !memcmp(&buf[12], state->rc_last, 4)) {
- dev_dbg(&d->udev->dev, "%s: key repeated\n", __func__);
+ !memcmp(&buf[12], state->rc_last, 4)) {
+ dev_dbg(&intf->dev, "key repeated\n");
rc_repeat(d->rc_dev);
state->rc_repeat = buf[6];
return ret;
@@ -1238,18 +1189,18 @@ static int af9015_rc_query(struct dvb_usb_device *d)
/* Only process key if canary killed */
if (buf[16] != 0xff && buf[0] != 0x01) {
enum rc_proto proto;
- dev_dbg(&d->udev->dev, "%s: key pressed %*ph\n",
- __func__, 4, buf + 12);
+
+ dev_dbg(&intf->dev, "key pressed %*ph\n", 4, buf + 12);
/* Reset the canary */
- ret = af9015_write_reg(d, 0x98e9, 0xff);
+ ret = regmap_write(state->regmap, 0x98e9, 0xff);
if (ret)
goto error;
/* Remember this key */
memcpy(state->rc_last, &buf[12], 4);
- if (buf[14] == (u8) ~buf[15]) {
- if (buf[12] == (u8) ~buf[13]) {
+ if (buf[14] == (u8)~buf[15]) {
+ if (buf[12] == (u8)~buf[13]) {
/* NEC */
state->rc_keycode = RC_SCANCODE_NEC(buf[12],
buf[14]);
@@ -1271,7 +1222,7 @@ static int af9015_rc_query(struct dvb_usb_device *d)
}
rc_keydown(d->rc_dev, proto, state->rc_keycode, 0);
} else {
- dev_dbg(&d->udev->dev, "%s: no key press\n", __func__);
+ dev_dbg(&intf->dev, "no key press\n");
/* Invalidate last keypress */
/* Not really needed, but helps with debug */
state->rc_last[2] = state->rc_last[3];
@@ -1282,8 +1233,7 @@ static int af9015_rc_query(struct dvb_usb_device *d)
error:
if (ret) {
- dev_warn(&d->udev->dev, "%s: rc query failed=%d\n",
- KBUILD_MODNAME, ret);
+ dev_warn(&intf->dev, "rc query failed %d\n", ret);
/* allow random errors as dvb-usb will stop polling on error */
if (!state->rc_failed)
@@ -1306,29 +1256,33 @@ static int af9015_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc)
/* try to load remote based module param */
if (!rc->map_name)
rc->map_name = af9015_rc_setup_match(dvb_usb_af9015_remote,
- af9015_rc_setup_modparam);
+ af9015_rc_setup_modparam);
/* try to load remote based eeprom hash */
if (!rc->map_name)
rc->map_name = af9015_rc_setup_match(state->eeprom_sum,
- af9015_rc_setup_hashes);
+ af9015_rc_setup_hashes);
/* try to load remote based USB iManufacturer string */
if (!rc->map_name && vid == USB_VID_AFATECH) {
- /* Check USB manufacturer and product strings and try
- to determine correct remote in case of chip vendor
- reference IDs are used.
- DO NOT ADD ANYTHING NEW HERE. Use hashes instead. */
+ /*
+ * Check USB manufacturer and product strings and try
+ * to determine correct remote in case of chip vendor
+ * reference IDs are used.
+ * DO NOT ADD ANYTHING NEW HERE. Use hashes instead.
+ */
char manufacturer[10];
+
memset(manufacturer, 0, sizeof(manufacturer));
usb_string(d->udev, d->udev->descriptor.iManufacturer,
- manufacturer, sizeof(manufacturer));
+ manufacturer, sizeof(manufacturer));
if (!strcmp("MSI", manufacturer)) {
- /* iManufacturer 1 MSI
- iProduct 2 MSI K-VOX */
- rc->map_name = af9015_rc_setup_match(
- AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3,
- af9015_rc_setup_modparam);
+ /*
+ * iManufacturer 1 MSI
+ * iProduct 2 MSI K-VOX
+ */
+ rc->map_name = af9015_rc_setup_match(AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3,
+ af9015_rc_setup_modparam);
}
}
@@ -1347,15 +1301,68 @@ static int af9015_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc)
#define af9015_get_rc_config NULL
#endif
-static int af9015_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
+static int af9015_regmap_write(void *context, const void *data, size_t count)
{
+ struct dvb_usb_device *d = context;
+ struct usb_interface *intf = d->intf;
+ int ret;
+ u16 reg = ((u8 *)data)[0] << 8 | ((u8 *)data)[1] << 0;
+ u8 *val = &((u8 *)data)[2];
+ const unsigned int len = count - 2;
+ struct req_t req = {WRITE_MEMORY, 0, reg, 0, 0, len, val};
+
+ ret = af9015_ctrl_msg(d, &req);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&intf->dev, "failed %d\n", ret);
+ return ret;
+}
+
+static int af9015_regmap_read(void *context, const void *reg_buf,
+ size_t reg_size, void *val_buf, size_t val_size)
+{
+ struct dvb_usb_device *d = context;
+ struct usb_interface *intf = d->intf;
+ int ret;
+ u16 reg = ((u8 *)reg_buf)[0] << 8 | ((u8 *)reg_buf)[1] << 0;
+ u8 *val = &((u8 *)val_buf)[0];
+ const unsigned int len = val_size;
+ struct req_t req = {READ_MEMORY, 0, reg, 0, 0, len, val};
+
+ ret = af9015_ctrl_msg(d, &req);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&intf->dev, "failed %d\n", ret);
+ return ret;
+}
+
+static int af9015_probe(struct dvb_usb_device *d)
+{
+ struct af9015_state *state = d_to_priv(d);
+ struct usb_interface *intf = d->intf;
struct usb_device *udev = interface_to_usbdev(intf);
+ int ret;
char manufacturer[sizeof("ITE Technologies, Inc.")];
+ static const struct regmap_config regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ };
+ static const struct regmap_bus regmap_bus = {
+ .read = af9015_regmap_read,
+ .write = af9015_regmap_write,
+ };
+
+ dev_dbg(&intf->dev, "\n");
memset(manufacturer, 0, sizeof(manufacturer));
usb_string(udev, udev->descriptor.iManufacturer,
- manufacturer, sizeof(manufacturer));
+ manufacturer, sizeof(manufacturer));
/*
* There is two devices having same ID but different chipset. One uses
* AF9015 and the other IT9135 chipset. Only difference seen on lsusb
@@ -1374,19 +1381,41 @@ static int af9015_probe(struct usb_interface *intf,
* iProduct 2 DVB-T TV Stick
*/
if ((le16_to_cpu(udev->descriptor.idVendor) == USB_VID_TERRATEC) &&
- (le16_to_cpu(udev->descriptor.idProduct) == 0x0099)) {
+ (le16_to_cpu(udev->descriptor.idProduct) == 0x0099)) {
if (!strcmp("ITE Technologies, Inc.", manufacturer)) {
- dev_dbg(&udev->dev, "%s: rejecting device\n", __func__);
- return -ENODEV;
+ ret = -ENODEV;
+ dev_dbg(&intf->dev, "rejecting device\n");
+ goto err;
}
}
- return dvb_usbv2_probe(intf, id);
+ state->regmap = regmap_init(&intf->dev, &regmap_bus, d, &regmap_config);
+ if (IS_ERR(state->regmap)) {
+ ret = PTR_ERR(state->regmap);
+ goto err;
+ }
+
+ return 0;
+err:
+ dev_dbg(&intf->dev, "failed %d\n", ret);
+ return ret;
}
-/* interface 0 is used by DVB-T receiver and
- interface 1 is for remote controller (HID) */
-static struct dvb_usb_device_properties af9015_props = {
+static void af9015_disconnect(struct dvb_usb_device *d)
+{
+ struct af9015_state *state = d_to_priv(d);
+ struct usb_interface *intf = d->intf;
+
+ dev_dbg(&intf->dev, "\n");
+
+ regmap_exit(state->regmap);
+}
+
+/*
+ * Interface 0 is used by DVB-T receiver and
+ * interface 1 is for remote controller (HID)
+ */
+static const struct dvb_usb_device_properties af9015_props = {
.driver_name = KBUILD_MODNAME,
.owner = THIS_MODULE,
.adapter_nr = adapter_nr,
@@ -1395,6 +1424,8 @@ static struct dvb_usb_device_properties af9015_props = {
.generic_bulk_ctrl_endpoint = 0x02,
.generic_bulk_ctrl_endpoint_response = 0x81,
+ .probe = af9015_probe,
+ .disconnect = af9015_disconnect,
.identify_state = af9015_identify_state,
.firmware = AF9015_FIRMWARE,
.download_firmware = af9015_download_firmware,
@@ -1402,10 +1433,12 @@ static struct dvb_usb_device_properties af9015_props = {
.i2c_algo = &af9015_i2c_algo,
.read_config = af9015_read_config,
.frontend_attach = af9015_af9013_frontend_attach,
+ .frontend_detach = af9015_frontend_detach,
.tuner_attach = af9015_tuner_attach,
.init = af9015_init,
.get_rc_config = af9015_get_rc_config,
.get_stream_config = af9015_get_stream_config,
+ .streaming_ctrl = af9015_streaming_ctrl,
.get_adapter_count = af9015_get_adapter_count,
.adapter = {
@@ -1416,9 +1449,15 @@ static struct dvb_usb_device_properties af9015_props = {
.pid_filter = af9015_pid_filter,
.pid_filter_ctrl = af9015_pid_filter_ctrl,
- .stream = DVB_USB_STREAM_BULK(0x84, 8, TS_USB20_FRAME_SIZE),
+ .stream = DVB_USB_STREAM_BULK(0x84, 6, 87 * 188),
}, {
- .stream = DVB_USB_STREAM_BULK(0x85, 8, TS_USB20_FRAME_SIZE),
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER |
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .pid_filter_count = 32,
+ .pid_filter = af9015_pid_filter,
+ .pid_filter_ctrl = af9015_pid_filter_ctrl,
+
+ .stream = DVB_USB_STREAM_BULK(0x85, 6, 87 * 188),
},
},
};
@@ -1509,7 +1548,7 @@ MODULE_DEVICE_TABLE(usb, af9015_id_table);
static struct usb_driver af9015_usb_driver = {
.name = KBUILD_MODNAME,
.id_table = af9015_id_table,
- .probe = af9015_probe,
+ .probe = dvb_usbv2_probe,
.disconnect = dvb_usbv2_disconnect,
.suspend = dvb_usbv2_suspend,
.resume = dvb_usbv2_resume,
diff --git a/drivers/media/usb/dvb-usb-v2/af9015.h b/drivers/media/usb/dvb-usb-v2/af9015.h
index 3a9d9815ab7a..ad2b045cc39c 100644
--- a/drivers/media/usb/dvb-usb-v2/af9015.h
+++ b/drivers/media/usb/dvb-usb-v2/af9015.h
@@ -21,6 +21,7 @@
#define AF9015_H
#include <linux/hash.h>
+#include <linux/regmap.h>
#include "dvb_usb.h"
#include "af9013.h"
#include "dvb-pll.h"
@@ -34,19 +35,6 @@
#define AF9015_FIRMWARE "dvb-usb-af9015.fw"
-/* Windows driver uses packet count 21 for USB1.1 and 348 for USB2.0.
- We use smaller - about 1/4 from the original, 5 and 87. */
-#define TS_PACKET_SIZE 188
-
-#define TS_USB20_PACKET_COUNT 87
-#define TS_USB20_FRAME_SIZE (TS_PACKET_SIZE*TS_USB20_PACKET_COUNT)
-
-#define TS_USB11_PACKET_COUNT 5
-#define TS_USB11_FRAME_SIZE (TS_PACKET_SIZE*TS_USB11_PACKET_COUNT)
-
-#define TS_USB20_MAX_PACKET_SIZE 512
-#define TS_USB11_MAX_PACKET_SIZE 64
-
#define AF9015_I2C_EEPROM 0x50
#define AF9015_I2C_DEMOD 0x1c
#define AF9015_USB_TIMEOUT 2000
@@ -113,6 +101,7 @@ enum af9015_ir_mode {
#define BUF_LEN 63
struct af9015_state {
+ struct regmap *regmap;
u8 buf[BUF_LEN]; /* bulk USB control message */
u8 ir_mode;
u8 rc_repeat;
@@ -125,7 +114,10 @@ struct af9015_state {
u16 firmware_size;
u16 firmware_checksum;
u32 eeprom_sum;
- struct af9013_config af9013_config[2];
+ struct af9013_platform_data af9013_pdata[2];
+ struct i2c_client *demod_i2c_client[2];
+ u8 af9013_i2c_addr[2];
+ bool usb_ts_if_configured[2];
/* for demod callback override */
int (*set_frontend[2]) (struct dvb_frontend *fe);
diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb.h b/drivers/media/usb/dvb-usb-v2/dvb_usb.h
index d2e80537b2f7..3fd6cc0d6340 100644
--- a/drivers/media/usb/dvb-usb-v2/dvb_usb.h
+++ b/drivers/media/usb/dvb-usb-v2/dvb_usb.h
@@ -203,6 +203,8 @@ struct dvb_usb_adapter_properties {
* @generic_bulk_ctrl_endpoint_response: bulk control endpoint number for
* receive
* @generic_bulk_ctrl_delay: delay between bulk control sent and receive message
+ * @probe: like probe on driver model
+ * @disconnect: like disconnect on driver model
* @identify_state: called to determine the firmware state (cold or warm) and
* return possible firmware file name to be loaded
* @firmware: name of the firmware file to be loaded
@@ -239,6 +241,8 @@ struct dvb_usb_device_properties {
u8 generic_bulk_ctrl_endpoint_response;
unsigned int generic_bulk_ctrl_delay;
+ int (*probe)(struct dvb_usb_device *);
+ void (*disconnect)(struct dvb_usb_device *);
#define WARM 0
#define COLD 1
int (*identify_state) (struct dvb_usb_device *, const char **);
diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
index 2bf3bd81280a..afdcdbf005e9 100644
--- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
+++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
@@ -854,8 +854,6 @@ static int dvb_usbv2_exit(struct dvb_usb_device *d)
dvb_usbv2_remote_exit(d);
dvb_usbv2_adapter_exit(d);
dvb_usbv2_i2c_exit(d);
- kfree(d->priv);
- kfree(d);
return 0;
}
@@ -934,7 +932,7 @@ int dvb_usbv2_probe(struct usb_interface *intf,
if (intf->cur_altsetting->desc.bInterfaceNumber !=
d->props->bInterfaceNumber) {
ret = -ENODEV;
- goto err_free_all;
+ goto err_kfree_d;
}
mutex_init(&d->usb_mutex);
@@ -946,10 +944,16 @@ int dvb_usbv2_probe(struct usb_interface *intf,
dev_err(&d->udev->dev, "%s: kzalloc() failed\n",
KBUILD_MODNAME);
ret = -ENOMEM;
- goto err_free_all;
+ goto err_kfree_d;
}
}
+ if (d->props->probe) {
+ ret = d->props->probe(d);
+ if (ret)
+ goto err_kfree_priv;
+ }
+
if (d->props->identify_state) {
const char *name = NULL;
ret = d->props->identify_state(d, &name);
@@ -1001,6 +1005,12 @@ exit:
return 0;
err_free_all:
dvb_usbv2_exit(d);
+ if (d->props->disconnect)
+ d->props->disconnect(d);
+err_kfree_priv:
+ kfree(d->priv);
+err_kfree_d:
+ kfree(d);
err:
dev_dbg(&udev->dev, "%s: failed=%d\n", __func__, ret);
return ret;
@@ -1021,6 +1031,12 @@ void dvb_usbv2_disconnect(struct usb_interface *intf)
dvb_usbv2_exit(d);
+ if (d->props->disconnect)
+ d->props->disconnect(d);
+
+ kfree(d->priv);
+ kfree(d);
+
pr_info("%s: '%s:%s' successfully deinitialized and disconnected\n",
KBUILD_MODNAME, drvname, devname);
kfree(devname);
diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c
index 2abd15c6df81..387a074ea6ec 100644
--- a/drivers/media/usb/dvb-usb/cxusb.c
+++ b/drivers/media/usb/dvb-usb/cxusb.c
@@ -1243,82 +1243,6 @@ static int cxusb_mygica_t230_frontend_attach(struct dvb_usb_adapter *adap)
return 0;
}
-static int cxusb_mygica_t230c_frontend_attach(struct dvb_usb_adapter *adap)
-{
- struct dvb_usb_device *d = adap->dev;
- struct cxusb_state *st = d->priv;
- struct i2c_adapter *adapter;
- struct i2c_client *client_demod;
- struct i2c_client *client_tuner;
- struct i2c_board_info info;
- struct si2168_config si2168_config;
- struct si2157_config si2157_config;
-
- /* Select required USB configuration */
- if (usb_set_interface(d->udev, 0, 0) < 0)
- err("set interface failed");
-
- /* Unblock all USB pipes */
- usb_clear_halt(d->udev,
- usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint));
- usb_clear_halt(d->udev,
- usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint));
- usb_clear_halt(d->udev,
- usb_rcvbulkpipe(d->udev, d->props.adapter[0].fe[0].stream.endpoint));
-
- /* attach frontend */
- memset(&si2168_config, 0, sizeof(si2168_config));
- si2168_config.i2c_adapter = &adapter;
- si2168_config.fe = &adap->fe_adap[0].fe;
- si2168_config.ts_mode = SI2168_TS_PARALLEL;
- si2168_config.ts_clock_inv = 1;
- memset(&info, 0, sizeof(struct i2c_board_info));
- strlcpy(info.type, "si2168", I2C_NAME_SIZE);
- info.addr = 0x64;
- info.platform_data = &si2168_config;
- request_module(info.type);
- client_demod = i2c_new_device(&d->i2c_adap, &info);
- if (client_demod == NULL || client_demod->dev.driver == NULL)
- return -ENODEV;
-
- if (!try_module_get(client_demod->dev.driver->owner)) {
- i2c_unregister_device(client_demod);
- return -ENODEV;
- }
-
- /* attach tuner */
- memset(&si2157_config, 0, sizeof(si2157_config));
- si2157_config.fe = adap->fe_adap[0].fe;
- memset(&info, 0, sizeof(struct i2c_board_info));
- strlcpy(info.type, "si2141", I2C_NAME_SIZE);
- info.addr = 0x60;
- info.platform_data = &si2157_config;
- request_module("si2157");
- client_tuner = i2c_new_device(adapter, &info);
- if (client_tuner == NULL || client_tuner->dev.driver == NULL) {
- module_put(client_demod->dev.driver->owner);
- i2c_unregister_device(client_demod);
- return -ENODEV;
- }
- if (!try_module_get(client_tuner->dev.driver->owner)) {
- i2c_unregister_device(client_tuner);
- module_put(client_demod->dev.driver->owner);
- i2c_unregister_device(client_demod);
- return -ENODEV;
- }
-
- st->i2c_client_demod = client_demod;
- st->i2c_client_tuner = client_tuner;
-
- /* hook fe: need to resync the slave fifo when signal locks. */
- mutex_init(&st->stream_mutex);
- st->last_lock = 0;
- st->fe_read_status = adap->fe_adap[0].fe->ops.read_status;
- adap->fe_adap[0].fe->ops.read_status = cxusb_read_status;
-
- return 0;
-}
-
/*
* DViCO has shipped two devices with the same USB ID, but only one of them
* needs a firmware download. Check the device class details to see if they
@@ -1401,7 +1325,6 @@ static struct dvb_usb_device_properties cxusb_aver_a868r_properties;
static struct dvb_usb_device_properties cxusb_d680_dmb_properties;
static struct dvb_usb_device_properties cxusb_mygica_d689_properties;
static struct dvb_usb_device_properties cxusb_mygica_t230_properties;
-static struct dvb_usb_device_properties cxusb_mygica_t230c_properties;
static int cxusb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
@@ -1434,8 +1357,6 @@ static int cxusb_probe(struct usb_interface *intf,
THIS_MODULE, NULL, adapter_nr) ||
0 == dvb_usb_device_init(intf, &cxusb_mygica_t230_properties,
THIS_MODULE, NULL, adapter_nr) ||
- 0 == dvb_usb_device_init(intf, &cxusb_mygica_t230c_properties,
- THIS_MODULE, NULL, adapter_nr) ||
0)
return 0;
@@ -1487,7 +1408,6 @@ enum cxusb_table_index {
CONEXANT_D680_DMB,
MYGICA_D689,
MYGICA_T230,
- MYGICA_T230C,
NR__cxusb_table_index
};
@@ -1555,9 +1475,6 @@ static struct usb_device_id cxusb_table[NR__cxusb_table_index + 1] = {
[MYGICA_T230] = {
USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230)
},
- [MYGICA_T230C] = {
- USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230+1)
- },
{} /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, cxusb_table);
@@ -2143,7 +2060,7 @@ static struct dvb_usb_device_properties cxusb_d680_dmb_properties = {
.rc.core = {
.rc_interval = 100,
- .rc_codes = RC_MAP_D680_DMB,
+ .rc_codes = RC_MAP_TOTAL_MEDIA_IN_HAND_02,
.module_name = KBUILD_MODNAME,
.rc_query = cxusb_d680_dmb_rc_query,
.allowed_protos = RC_PROTO_BIT_UNKNOWN,
@@ -2252,7 +2169,7 @@ static struct dvb_usb_device_properties cxusb_mygica_t230_properties = {
.rc.core = {
.rc_interval = 100,
- .rc_codes = RC_MAP_TOTAL_MEDIA_IN_HAND_02,
+ .rc_codes = RC_MAP_D680_DMB,
.module_name = KBUILD_MODNAME,
.rc_query = cxusb_d680_dmb_rc_query,
.allowed_protos = RC_PROTO_BIT_UNKNOWN,
@@ -2268,60 +2185,6 @@ static struct dvb_usb_device_properties cxusb_mygica_t230_properties = {
}
};
-static struct dvb_usb_device_properties cxusb_mygica_t230c_properties = {
- .caps = DVB_USB_IS_AN_I2C_ADAPTER,
-
- .usb_ctrl = CYPRESS_FX2,
-
- .size_of_priv = sizeof(struct cxusb_state),
-
- .num_adapters = 1,
- .adapter = {
- {
- .num_frontends = 1,
- .fe = {{
- .streaming_ctrl = cxusb_streaming_ctrl,
- .frontend_attach = cxusb_mygica_t230c_frontend_attach,
-
- /* parameter for the MPEG2-data transfer */
- .stream = {
- .type = USB_BULK,
- .count = 5,
- .endpoint = 0x02,
- .u = {
- .bulk = {
- .buffersize = 8192,
- }
- }
- },
- } },
- },
- },
-
- .power_ctrl = cxusb_d680_dmb_power_ctrl,
-
- .i2c_algo = &cxusb_i2c_algo,
-
- .generic_bulk_ctrl_endpoint = 0x01,
-
- .rc.core = {
- .rc_interval = 100,
- .rc_codes = RC_MAP_TOTAL_MEDIA_IN_HAND_02,
- .module_name = KBUILD_MODNAME,
- .rc_query = cxusb_d680_dmb_rc_query,
- .allowed_protos = RC_PROTO_BIT_UNKNOWN,
- },
-
- .num_device_descs = 1,
- .devices = {
- {
- "Mygica T230C DVB-T/T2/C",
- { NULL },
- { &cxusb_table[MYGICA_T230C], NULL },
- },
- }
-};
-
static struct usb_driver cxusb_driver = {
.name = "dvb_usb_cxusb",
.probe = cxusb_probe,
diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c
index 3d99e141d566..c53a969bc6be 100644
--- a/drivers/media/usb/dvb-usb/dib0700_devices.c
+++ b/drivers/media/usb/dvb-usb/dib0700_devices.c
@@ -3412,7 +3412,7 @@ static int novatd_frontend_attach(struct dvb_usb_adapter *adap)
static struct s5h1411_config pinnacle_801e_config = {
.output_mode = S5H1411_PARALLEL_OUTPUT,
.gpio = S5H1411_GPIO_OFF,
- .mpeg_timing = S5H1411_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK,
+ .mpeg_timing = S5H1411_MPEGTIMING_NONCONTINUOUS_NONINVERTING_CLOCK,
.qam_if = S5H1411_IF_44000,
.vsb_if = S5H1411_IF_44000,
.inversion = S5H1411_INVERSION_OFF,
diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c
index 4628d73f46f2..8e799ae1df69 100644
--- a/drivers/media/usb/em28xx/em28xx-audio.c
+++ b/drivers/media/usb/em28xx/em28xx-audio.c
@@ -1,25 +1,25 @@
-/*
- * Empiatech em28x1 audio extension
- *
- * Copyright (C) 2006 Markus Rechberger <mrechberger@gmail.com>
- *
- * Copyright (C) 2007-2016 Mauro Carvalho Chehab
- * - Port to work with the in-kernel driver
- * - Cleanups, fixes, alsa-controls, etc.
- *
- * This driver is based on my previous au600 usb pstn audio driver
- * and inherits all the copyrights
- *
- * 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.
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Empiatech em28x1 audio extension
+//
+// Copyright (C) 2006 Markus Rechberger <mrechberger@gmail.com>
+//
+// Copyright (C) 2007-2016 Mauro Carvalho Chehab
+// - Port to work with the in-kernel driver
+// - Cleanups, fixes, alsa-controls, etc.
+//
+// This driver is based on my previous au600 usb pstn audio driver
+// and inherits all the copyrights
+//
+// 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.
#include "em28xx.h"
@@ -103,7 +103,7 @@ static void em28xx_audio_isocirq(struct urb *urb)
case -ESHUTDOWN:
return;
default: /* error */
- dprintk("urb completition error %d.\n", urb->status);
+ dprintk("urb completion error %d.\n", urb->status);
break;
}
@@ -165,12 +165,11 @@ static void em28xx_audio_isocirq(struct urb *urb)
dev_err(&dev->intf->dev,
"resubmit of audio urb failed (error=%i)\n",
status);
- return;
}
static int em28xx_init_audio_isoc(struct em28xx *dev)
{
- int i, errCode;
+ int i, err;
dprintk("Starting isoc transfers\n");
@@ -179,16 +178,15 @@ static int em28xx_init_audio_isoc(struct em28xx *dev)
memset(dev->adev.transfer_buffer[i], 0x80,
dev->adev.urb[i]->transfer_buffer_length);
- errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC);
- if (errCode) {
+ err = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC);
+ if (err) {
dev_err(&dev->intf->dev,
"submit of audio urb failed (error=%i)\n",
- errCode);
+ err);
em28xx_deinit_isoc_audio(dev);
atomic_set(&dev->adev.stream_started, 0);
- return errCode;
+ return err;
}
-
}
return 0;
@@ -268,14 +266,17 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream)
if (nonblock) {
if (!mutex_trylock(&dev->lock))
return -EAGAIN;
- } else
+ } else {
mutex_lock(&dev->lock);
+ }
runtime->hw = snd_em28xx_hw_capture;
if (dev->adev.users == 0) {
- if (dev->alt == 0 || dev->is_audio_only) {
- struct usb_device *udev = interface_to_usbdev(dev->intf);
+ if (!dev->alt || dev->is_audio_only) {
+ struct usb_device *udev;
+
+ udev = interface_to_usbdev(dev->intf);
if (dev->is_audio_only)
/* audio is on a separate interface */
@@ -367,9 +368,11 @@ static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream,
if (ret < 0)
return ret;
#if 0
- /* TODO: set up em28xx audio chip to deliver the correct audio format,
- current default is 48000hz multiplexed => 96000hz mono
- which shouldn't matter since analogue TV only supports mono */
+ /*
+ * TODO: set up em28xx audio chip to deliver the correct audio format,
+ * current default is 48000hz multiplexed => 96000hz mono
+ * which shouldn't matter since analogue TV only supports mono
+ */
unsigned int channels, rate, format;
format = params_format(hw_params);
@@ -513,8 +516,9 @@ static int em28xx_vol_put(struct snd_kcontrol *kcontrol,
if (nonblock) {
if (!mutex_trylock(&dev->lock))
return -EAGAIN;
- } else
+ } else {
mutex_lock(&dev->lock);
+ }
rc = em28xx_read_ac97(dev, kcontrol->private_value);
if (rc < 0)
goto err;
@@ -551,8 +555,9 @@ static int em28xx_vol_get(struct snd_kcontrol *kcontrol,
if (nonblock) {
if (!mutex_trylock(&dev->lock))
return -EAGAIN;
- } else
+ } else {
mutex_lock(&dev->lock);
+ }
val = em28xx_read_ac97(dev, kcontrol->private_value);
mutex_unlock(&dev->lock);
if (val < 0)
@@ -586,8 +591,9 @@ static int em28xx_vol_put_mute(struct snd_kcontrol *kcontrol,
if (nonblock) {
if (!mutex_trylock(&dev->lock))
return -EAGAIN;
- } else
+ } else {
mutex_lock(&dev->lock);
+ }
rc = em28xx_read_ac97(dev, kcontrol->private_value);
if (rc < 0)
goto err;
@@ -627,8 +633,9 @@ static int em28xx_vol_get_mute(struct snd_kcontrol *kcontrol,
if (nonblock) {
if (!mutex_trylock(&dev->lock))
return -EAGAIN;
- } else
+ } else {
mutex_lock(&dev->lock);
+ }
val = em28xx_read_ac97(dev, kcontrol->private_value);
mutex_unlock(&dev->lock);
if (val < 0)
@@ -762,7 +769,7 @@ static int em28xx_audio_urb_init(struct em28xx *dev)
if (intf->num_altsetting <= alt) {
dev_err(&dev->intf->dev, "alt %d doesn't exist on interface %d\n",
- dev->ifnum, alt);
+ dev->ifnum, alt);
return -ENODEV;
}
@@ -836,9 +843,8 @@ static int em28xx_audio_urb_init(struct em28xx *dev)
dev->adev.transfer_buffer = kcalloc(num_urb,
sizeof(*dev->adev.transfer_buffer),
GFP_ATOMIC);
- if (!dev->adev.transfer_buffer) {
+ if (!dev->adev.transfer_buffer)
return -ENOMEM;
- }
dev->adev.urb = kcalloc(num_urb, sizeof(*dev->adev.urb), GFP_ATOMIC);
if (!dev->adev.urb) {
@@ -899,9 +905,11 @@ static int em28xx_audio_init(struct em28xx *dev)
int err;
if (dev->usb_audio_type != EM28XX_USB_AUDIO_VENDOR) {
- /* This device does not support the extension (in this case
- the device is expecting the snd-usb-audio module or
- doesn't have analog audio support at all) */
+ /*
+ * This device does not support the extension (in this case
+ * the device is expecting the snd-usb-audio module or
+ * doesn't have analog audio support at all)
+ */
return 0;
}
@@ -977,13 +985,15 @@ card_free:
static int em28xx_audio_fini(struct em28xx *dev)
{
- if (dev == NULL)
+ if (!dev)
return 0;
if (dev->usb_audio_type != EM28XX_USB_AUDIO_VENDOR) {
- /* This device does not support the extension (in this case
- the device is expecting the snd-usb-audio module or
- doesn't have analog audio support at all) */
+ /*
+ * This device does not support the extension (in this case
+ * the device is expecting the snd-usb-audio module or
+ * doesn't have analog audio support at all)
+ */
return 0;
}
@@ -1005,7 +1015,7 @@ static int em28xx_audio_fini(struct em28xx *dev)
static int em28xx_audio_suspend(struct em28xx *dev)
{
- if (dev == NULL)
+ if (!dev)
return 0;
if (dev->usb_audio_type != EM28XX_USB_AUDIO_VENDOR)
@@ -1019,7 +1029,7 @@ static int em28xx_audio_suspend(struct em28xx *dev)
static int em28xx_audio_resume(struct em28xx *dev)
{
- if (dev == NULL)
+ if (!dev)
return 0;
if (dev->usb_audio_type != EM28XX_USB_AUDIO_VENDOR)
@@ -1050,7 +1060,7 @@ static void __exit em28xx_alsa_unregister(void)
em28xx_unregister_extension(&audio_ops);
}
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Markus Rechberger <mrechberger@gmail.com>");
MODULE_AUTHOR("Mauro Carvalho Chehab");
MODULE_DESCRIPTION(DRIVER_DESC " - audio interface");
diff --git a/drivers/media/usb/em28xx/em28xx-camera.c b/drivers/media/usb/em28xx/em28xx-camera.c
index ae87dd3e671f..3c2694a16ed1 100644
--- a/drivers/media/usb/em28xx/em28xx-camera.c
+++ b/drivers/media/usb/em28xx/em28xx-camera.c
@@ -1,23 +1,19 @@
-/*
- em28xx-camera.c - driver for Empia EM25xx/27xx/28xx USB video capture devices
-
- Copyright (C) 2009 Mauro Carvalho Chehab <mchehab@infradead.org>
- Copyright (C) 2013 Frank Schäfer <fschaefer.oss@googlemail.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.
-*/
+// SPDX-License-Identifier: GPL-2.0+
+//
+// em28xx-camera.c - driver for Empia EM25xx/27xx/28xx USB video capture devices
+//
+// Copyright (C) 2009 Mauro Carvalho Chehab <mchehab@infradead.org>
+// Copyright (C) 2013 Frank Schäfer <fschaefer.oss@googlemail.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.
#include "em28xx.h"
@@ -49,7 +45,7 @@ static int em28xx_initialize_mt9m111(struct em28xx *dev)
{ 0x0d, 0x00, 0x01, }, /* reset and use defaults */
{ 0x0d, 0x00, 0x00, },
{ 0x0a, 0x00, 0x21, },
- { 0x21, 0x04, 0x00, }, /* full readout speed, no row/col skipping */
+ { 0x21, 0x04, 0x00, }, /* full readout spd, no row/col skip */
};
for (i = 0; i < ARRAY_SIZE(regs); i++)
@@ -157,7 +153,8 @@ static int em28xx_probe_sensor_micron(struct em28xx *dev)
break;
default:
dev_info(&dev->intf->dev,
- "unknown Micron sensor detected: 0x%04x\n", id);
+ "unknown Micron sensor detected: 0x%04x\n",
+ id);
return 0;
}
@@ -186,8 +183,10 @@ static int em28xx_probe_sensor_omnivision(struct em28xx *dev)
struct i2c_client *client = &dev->i2c_client[dev->def_i2c_bus];
dev->em28xx_sensor = EM28XX_NOSENSOR;
- /* NOTE: these devices have the register auto incrementation disabled
- * by default, so we have to use single byte reads ! */
+ /*
+ * NOTE: these devices have the register auto incrementation disabled
+ * by default, so we have to use single byte reads !
+ */
for (i = 0; omnivision_sensor_addrs[i] != I2C_CLIENT_END; i++) {
client->addr = omnivision_sensor_addrs[i];
/* Read manufacturer ID from registers 0x1c-0x1d (BE) */
@@ -397,7 +396,7 @@ int em28xx_init_camera(struct em28xx *dev)
subdev =
v4l2_i2c_new_subdev_board(&v4l2->v4l2_dev, adap,
&ov2640_info, NULL);
- if (subdev == NULL)
+ if (!subdev)
return -ENODEV;
format.format.code = MEDIA_BUS_FMT_YUYV8_2X8;
diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c
index 34e16f6ab4ac..6e0e67d23876 100644
--- a/drivers/media/usb/em28xx/em28xx-cards.c
+++ b/drivers/media/usb/em28xx/em28xx-cards.c
@@ -1,27 +1,23 @@
-/*
- em28xx-cards.c - driver for Empia EM2800/EM2820/2840 USB
- video capture devices
-
- Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
- Markus Rechberger <mrechberger@gmail.com>
- Mauro Carvalho Chehab <mchehab@infradead.org>
- Sascha Sommer <saschasommer@freenet.de>
- Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.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.
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// em28xx-cards.c - driver for Empia EM2800/EM2820/2840 USB
+// video capture devices
+//
+// Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
+// Markus Rechberger <mrechberger@gmail.com>
+// Mauro Carvalho Chehab <mchehab@infradead.org>
+// Sascha Sommer <saschasommer@freenet.de>
+// Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.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.
#include "em28xx.h"
@@ -40,7 +36,6 @@
#include <media/v4l2-common.h>
#include <sound/ac97_codec.h>
-
#define DRIVER_NAME "em28xx"
static int tuner = -1;
@@ -81,26 +76,26 @@ static void em28xx_pre_card_setup(struct em28xx *dev);
*/
/* Reset for the most [analog] boards */
-static struct em28xx_reg_seq default_analog[] = {
+static const struct em28xx_reg_seq default_analog[] = {
{EM2820_R08_GPIO_CTRL, 0x6d, ~EM_GPIO_4, 10},
{ -1, -1, -1, -1},
};
/* Reset for the most [digital] boards */
-static struct em28xx_reg_seq default_digital[] = {
+static const struct em28xx_reg_seq default_digital[] = {
{EM2820_R08_GPIO_CTRL, 0x6e, ~EM_GPIO_4, 10},
{ -1, -1, -1, -1},
};
/* Board Hauppauge WinTV HVR 900 analog */
-static struct em28xx_reg_seq hauppauge_wintv_hvr_900_analog[] = {
+static const struct em28xx_reg_seq hauppauge_wintv_hvr_900_analog[] = {
{EM2820_R08_GPIO_CTRL, 0x2d, ~EM_GPIO_4, 10},
{ 0x05, 0xff, 0x10, 10},
{ -1, -1, -1, -1},
};
/* Board Hauppauge WinTV HVR 900 digital */
-static struct em28xx_reg_seq hauppauge_wintv_hvr_900_digital[] = {
+static const struct em28xx_reg_seq hauppauge_wintv_hvr_900_digital[] = {
{EM2820_R08_GPIO_CTRL, 0x2e, ~EM_GPIO_4, 10},
{EM2880_R04_GPO, 0x04, 0x0f, 10},
{EM2880_R04_GPO, 0x0c, 0x0f, 10},
@@ -108,25 +103,20 @@ static struct em28xx_reg_seq hauppauge_wintv_hvr_900_digital[] = {
};
/* Board Hauppauge WinTV HVR 900 (R2) digital */
-static struct em28xx_reg_seq hauppauge_wintv_hvr_900R2_digital[] = {
+static const struct em28xx_reg_seq hauppauge_wintv_hvr_900R2_digital[] = {
{EM2820_R08_GPIO_CTRL, 0x2e, ~EM_GPIO_4, 10},
{EM2880_R04_GPO, 0x0c, 0x0f, 10},
{ -1, -1, -1, -1},
};
/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */
-static struct em28xx_reg_seq em2880_msi_digivox_ad_analog[] = {
+static const struct em28xx_reg_seq em2880_msi_digivox_ad_analog[] = {
{EM2820_R08_GPIO_CTRL, 0x69, ~EM_GPIO_4, 10},
{ -1, -1, -1, -1},
};
-/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */
-
-/* Board - EM2870 Kworld 355u
- Analog - No input analog */
-
/* Board - EM2882 Kworld 315U digital */
-static struct em28xx_reg_seq em2882_kworld_315u_digital[] = {
+static const struct em28xx_reg_seq em2882_kworld_315u_digital[] = {
{EM2820_R08_GPIO_CTRL, 0xff, 0xff, 10},
{EM2820_R08_GPIO_CTRL, 0xfe, 0xff, 10},
{EM2880_R04_GPO, 0x04, 0xff, 10},
@@ -135,7 +125,7 @@ static struct em28xx_reg_seq em2882_kworld_315u_digital[] = {
{ -1, -1, -1, -1},
};
-static struct em28xx_reg_seq em2882_kworld_315u_tuner_gpio[] = {
+static const struct em28xx_reg_seq em2882_kworld_315u_tuner_gpio[] = {
{EM2880_R04_GPO, 0x08, 0xff, 10},
{EM2880_R04_GPO, 0x0c, 0xff, 10},
{EM2880_R04_GPO, 0x08, 0xff, 10},
@@ -143,30 +133,31 @@ static struct em28xx_reg_seq em2882_kworld_315u_tuner_gpio[] = {
{ -1, -1, -1, -1},
};
-static struct em28xx_reg_seq kworld_330u_analog[] = {
+static const struct em28xx_reg_seq kworld_330u_analog[] = {
{EM2820_R08_GPIO_CTRL, 0x6d, ~EM_GPIO_4, 10},
{EM2880_R04_GPO, 0x00, 0xff, 10},
{ -1, -1, -1, -1},
};
-static struct em28xx_reg_seq kworld_330u_digital[] = {
+static const struct em28xx_reg_seq kworld_330u_digital[] = {
{EM2820_R08_GPIO_CTRL, 0x6e, ~EM_GPIO_4, 10},
{EM2880_R04_GPO, 0x08, 0xff, 10},
{ -1, -1, -1, -1},
};
-/* Evga inDtube
- GPIO0 - Enable digital power (s5h1409) - low to enable
- GPIO1 - Enable analog power (tvp5150/emp202) - low to enable
- GPIO4 - xc3028 reset
- GOP3 - s5h1409 reset
+/*
+ * Evga inDtube
+ * GPIO0 - Enable digital power (s5h1409) - low to enable
+ * GPIO1 - Enable analog power (tvp5150/emp202) - low to enable
+ * GPIO4 - xc3028 reset
+ * GOP3 - s5h1409 reset
*/
-static struct em28xx_reg_seq evga_indtube_analog[] = {
+static const struct em28xx_reg_seq evga_indtube_analog[] = {
{EM2820_R08_GPIO_CTRL, 0x79, 0xff, 60},
{ -1, -1, -1, -1},
};
-static struct em28xx_reg_seq evga_indtube_digital[] = {
+static const struct em28xx_reg_seq evga_indtube_digital[] = {
{EM2820_R08_GPIO_CTRL, 0x7a, 0xff, 1},
{EM2880_R04_GPO, 0x04, 0xff, 10},
{EM2880_R04_GPO, 0x0c, 0xff, 1},
@@ -184,12 +175,12 @@ static struct em28xx_reg_seq evga_indtube_digital[] = {
* EM_GPIO_6 - currently unknown
* EM_GPIO_7 - currently unknown
*/
-static struct em28xx_reg_seq kworld_a340_digital[] = {
+static const struct em28xx_reg_seq kworld_a340_digital[] = {
{EM2820_R08_GPIO_CTRL, 0x6d, ~EM_GPIO_4, 10},
{ -1, -1, -1, -1},
};
-static struct em28xx_reg_seq kworld_ub435q_v3_digital[] = {
+static const struct em28xx_reg_seq kworld_ub435q_v3_digital[] = {
{EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 100},
{EM2874_R80_GPIO_P0_CTRL, 0xfe, 0xff, 100},
{EM2874_R80_GPIO_P0_CTRL, 0xbe, 0xff, 100},
@@ -198,45 +189,49 @@ static struct em28xx_reg_seq kworld_ub435q_v3_digital[] = {
};
/* Pinnacle Hybrid Pro eb1a:2881 */
-static struct em28xx_reg_seq pinnacle_hybrid_pro_analog[] = {
+static const struct em28xx_reg_seq pinnacle_hybrid_pro_analog[] = {
{EM2820_R08_GPIO_CTRL, 0xfd, ~EM_GPIO_4, 10},
{ -1, -1, -1, -1},
};
-static struct em28xx_reg_seq pinnacle_hybrid_pro_digital[] = {
+static const struct em28xx_reg_seq pinnacle_hybrid_pro_digital[] = {
{EM2820_R08_GPIO_CTRL, 0x6e, ~EM_GPIO_4, 10},
{EM2880_R04_GPO, 0x04, 0xff, 100},/* zl10353 reset */
{EM2880_R04_GPO, 0x0c, 0xff, 1},
{ -1, -1, -1, -1},
};
-static struct em28xx_reg_seq terratec_cinergy_USB_XS_FR_analog[] = {
+static const struct em28xx_reg_seq terratec_cinergy_USB_XS_FR_analog[] = {
{EM2820_R08_GPIO_CTRL, 0x6d, ~EM_GPIO_4, 10},
{EM2880_R04_GPO, 0x00, 0xff, 10},
{ -1, -1, -1, -1},
};
-static struct em28xx_reg_seq terratec_cinergy_USB_XS_FR_digital[] = {
+static const struct em28xx_reg_seq terratec_cinergy_USB_XS_FR_digital[] = {
{EM2820_R08_GPIO_CTRL, 0x6e, ~EM_GPIO_4, 10},
{EM2880_R04_GPO, 0x08, 0xff, 10},
{ -1, -1, -1, -1},
};
-/* PCTV HD Mini (80e) GPIOs
- 0-5: not used
- 6: demod reset, active low
- 7: LED on, active high */
-static struct em28xx_reg_seq em2874_pctv_80e_digital[] = {
+/*
+ * PCTV HD Mini (80e) GPIOs
+ * 0-5: not used
+ * 6: demod reset, active low
+ * 7: LED on, active high
+ */
+static const struct em28xx_reg_seq em2874_pctv_80e_digital[] = {
{EM28XX_R06_I2C_CLK, 0x45, 0xff, 10}, /*400 KHz*/
{EM2874_R80_GPIO_P0_CTRL, 0x00, 0xff, 100},/*Demod reset*/
{EM2874_R80_GPIO_P0_CTRL, 0x40, 0xff, 10},
{ -1, -1, -1, -1},
};
-/* eb1a:2868 Reddo DVB-C USB TV Box
- GPIO4 - CU1216L NIM
- Other GPIOs seems to be don't care. */
-static struct em28xx_reg_seq reddo_dvb_c_usb_box[] = {
+/*
+ * eb1a:2868 Reddo DVB-C USB TV Box
+ * GPIO4 - CU1216L NIM
+ * Other GPIOs seems to be don't care.
+ */
+static const struct em28xx_reg_seq reddo_dvb_c_usb_box[] = {
{EM2820_R08_GPIO_CTRL, 0xfe, 0xff, 10},
{EM2820_R08_GPIO_CTRL, 0xde, 0xff, 10},
{EM2820_R08_GPIO_CTRL, 0xfe, 0xff, 10},
@@ -248,7 +243,7 @@ static struct em28xx_reg_seq reddo_dvb_c_usb_box[] = {
};
/* Callback for the most boards */
-static struct em28xx_reg_seq default_tuner_gpio[] = {
+static const struct em28xx_reg_seq default_tuner_gpio[] = {
{EM2820_R08_GPIO_CTRL, EM_GPIO_4, EM_GPIO_4, 10},
{EM2820_R08_GPIO_CTRL, 0, EM_GPIO_4, 10},
{EM2820_R08_GPIO_CTRL, EM_GPIO_4, EM_GPIO_4, 10},
@@ -256,69 +251,70 @@ static struct em28xx_reg_seq default_tuner_gpio[] = {
};
/* Mute/unmute */
-static struct em28xx_reg_seq compro_unmute_tv_gpio[] = {
+static const struct em28xx_reg_seq compro_unmute_tv_gpio[] = {
{EM2820_R08_GPIO_CTRL, 5, 7, 10},
{ -1, -1, -1, -1},
};
-static struct em28xx_reg_seq compro_unmute_svid_gpio[] = {
+static const struct em28xx_reg_seq compro_unmute_svid_gpio[] = {
{EM2820_R08_GPIO_CTRL, 4, 7, 10},
{ -1, -1, -1, -1},
};
-static struct em28xx_reg_seq compro_mute_gpio[] = {
+static const struct em28xx_reg_seq compro_mute_gpio[] = {
{EM2820_R08_GPIO_CTRL, 6, 7, 10},
{ -1, -1, -1, -1},
};
/* Terratec AV350 */
-static struct em28xx_reg_seq terratec_av350_mute_gpio[] = {
+static const struct em28xx_reg_seq terratec_av350_mute_gpio[] = {
{EM2820_R08_GPIO_CTRL, 0xff, 0x7f, 10},
{ -1, -1, -1, -1},
};
-static struct em28xx_reg_seq terratec_av350_unmute_gpio[] = {
+static const struct em28xx_reg_seq terratec_av350_unmute_gpio[] = {
{EM2820_R08_GPIO_CTRL, 0xff, 0xff, 10},
{ -1, -1, -1, -1},
};
-static struct em28xx_reg_seq silvercrest_reg_seq[] = {
+static const struct em28xx_reg_seq silvercrest_reg_seq[] = {
{EM2820_R08_GPIO_CTRL, 0xff, 0xff, 10},
{EM2820_R08_GPIO_CTRL, 0x01, 0xf7, 10},
{ -1, -1, -1, -1},
};
-static struct em28xx_reg_seq vc211a_enable[] = {
+static const struct em28xx_reg_seq vc211a_enable[] = {
{EM2820_R08_GPIO_CTRL, 0xff, 0x07, 10},
{EM2820_R08_GPIO_CTRL, 0xff, 0x0f, 10},
{EM2820_R08_GPIO_CTRL, 0xff, 0x0b, 10},
{ -1, -1, -1, -1},
};
-static struct em28xx_reg_seq dikom_dk300_digital[] = {
+static const struct em28xx_reg_seq dikom_dk300_digital[] = {
{EM2820_R08_GPIO_CTRL, 0x6e, ~EM_GPIO_4, 10},
{EM2880_R04_GPO, 0x08, 0xff, 10},
{ -1, -1, -1, -1},
};
/* Reset for the most [digital] boards */
-static struct em28xx_reg_seq leadership_digital[] = {
+static const struct em28xx_reg_seq leadership_digital[] = {
{EM2874_R80_GPIO_P0_CTRL, 0x70, 0xff, 10},
{ -1, -1, -1, -1},
};
-static struct em28xx_reg_seq leadership_reset[] = {
+static const struct em28xx_reg_seq leadership_reset[] = {
{EM2874_R80_GPIO_P0_CTRL, 0xf0, 0xff, 10},
{EM2874_R80_GPIO_P0_CTRL, 0xb0, 0xff, 10},
{EM2874_R80_GPIO_P0_CTRL, 0xf0, 0xff, 10},
{ -1, -1, -1, -1},
};
-/* 2013:024f PCTV nanoStick T2 290e
+/*
+ * 2013:024f PCTV nanoStick T2 290e
* GPIO_6 - demod reset
* GPIO_7 - LED
*/
-static struct em28xx_reg_seq pctv_290e[] = {
+static const struct em28xx_reg_seq pctv_290e[] = {
{EM2874_R80_GPIO_P0_CTRL, 0x00, 0xff, 80},
{EM2874_R80_GPIO_P0_CTRL, 0x40, 0xff, 80}, /* GPIO_6 = 1 */
{EM2874_R80_GPIO_P0_CTRL, 0xc0, 0xff, 80}, /* GPIO_7 = 1 */
@@ -326,7 +322,7 @@ static struct em28xx_reg_seq pctv_290e[] = {
};
#if 0
-static struct em28xx_reg_seq terratec_h5_gpio[] = {
+static const struct em28xx_reg_seq terratec_h5_gpio[] = {
{EM2820_R08_GPIO_CTRL, 0xff, 0xff, 10},
{EM2874_R80_GPIO_P0_CTRL, 0xf6, 0xff, 100},
{EM2874_R80_GPIO_P0_CTRL, 0xf2, 0xff, 50},
@@ -334,7 +330,7 @@ static struct em28xx_reg_seq terratec_h5_gpio[] = {
{ -1, -1, -1, -1},
};
-static struct em28xx_reg_seq terratec_h5_digital[] = {
+static const struct em28xx_reg_seq terratec_h5_digital[] = {
{EM2874_R80_GPIO_P0_CTRL, 0xf6, 0xff, 10},
{EM2874_R80_GPIO_P0_CTRL, 0xe6, 0xff, 100},
{EM2874_R80_GPIO_P0_CTRL, 0xa6, 0xff, 10},
@@ -342,7 +338,8 @@ static struct em28xx_reg_seq terratec_h5_digital[] = {
};
#endif
-/* 2013:024f PCTV DVB-S2 Stick 460e
+/*
+ * 2013:024f PCTV DVB-S2 Stick 460e
* GPIO_0 - POWER_ON
* GPIO_1 - BOOST
* GPIO_2 - VUV_LNB (red LED)
@@ -352,7 +349,7 @@ static struct em28xx_reg_seq terratec_h5_digital[] = {
* GPIO_6 - RESET_DEM
* GPIO_7 - LED (green LED)
*/
-static struct em28xx_reg_seq pctv_460e[] = {
+static const struct em28xx_reg_seq pctv_460e[] = {
{EM2874_R80_GPIO_P0_CTRL, 0x01, 0xff, 50},
{ 0x0d, 0xff, 0xff, 50},
{EM2874_R80_GPIO_P0_CTRL, 0x41, 0xff, 50}, /* GPIO_6=1 */
@@ -361,7 +358,7 @@ static struct em28xx_reg_seq pctv_460e[] = {
{ -1, -1, -1, -1},
};
-static struct em28xx_reg_seq c3tech_digital_duo_digital[] = {
+static const struct em28xx_reg_seq c3tech_digital_duo_digital[] = {
{EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 10},
{EM2874_R80_GPIO_P0_CTRL, 0xfd, 0xff, 10}, /* xc5000 reset */
{EM2874_R80_GPIO_P0_CTRL, 0xf9, 0xff, 35},
@@ -384,7 +381,7 @@ static struct em28xx_reg_seq c3tech_digital_duo_digital[] = {
* GPIO 6 = #RESET_DEM
* GPIO 7 = P07_LED (green LED)
*/
-static struct em28xx_reg_seq pctv_461e[] = {
+static const struct em28xx_reg_seq pctv_461e[] = {
{EM2874_R80_GPIO_P0_CTRL, 0x7f, 0xff, 0},
{0x0d, 0xff, 0xff, 0},
{EM2874_R80_GPIO_P0_CTRL, 0x3f, 0xff, 100}, /* reset demod */
@@ -396,7 +393,7 @@ static struct em28xx_reg_seq pctv_461e[] = {
};
#if 0
-static struct em28xx_reg_seq hauppauge_930c_gpio[] = {
+static const struct em28xx_reg_seq hauppauge_930c_gpio[] = {
{EM2874_R80_GPIO_P0_CTRL, 0x6f, 0xff, 10},
{EM2874_R80_GPIO_P0_CTRL, 0x4f, 0xff, 10}, /* xc5000 reset */
{EM2874_R80_GPIO_P0_CTRL, 0x6f, 0xff, 10},
@@ -404,7 +401,7 @@ static struct em28xx_reg_seq hauppauge_930c_gpio[] = {
{ -1, -1, -1, -1},
};
-static struct em28xx_reg_seq hauppauge_930c_digital[] = {
+static const struct em28xx_reg_seq hauppauge_930c_digital[] = {
{EM2874_R80_GPIO_P0_CTRL, 0xf6, 0xff, 10},
{EM2874_R80_GPIO_P0_CTRL, 0xe6, 0xff, 100},
{EM2874_R80_GPIO_P0_CTRL, 0xa6, 0xff, 10},
@@ -412,38 +409,41 @@ static struct em28xx_reg_seq hauppauge_930c_digital[] = {
};
#endif
-/* 1b80:e425 MaxMedia UB425-TC
+/*
+ * 1b80:e425 MaxMedia UB425-TC
* 1b80:e1cc Delock 61959
* GPIO_6 - demod reset, 0=active
* GPIO_7 - LED, 0=active
*/
-static struct em28xx_reg_seq maxmedia_ub425_tc[] = {
+static const struct em28xx_reg_seq maxmedia_ub425_tc[] = {
{EM2874_R80_GPIO_P0_CTRL, 0x83, 0xff, 100},
{EM2874_R80_GPIO_P0_CTRL, 0xc3, 0xff, 100}, /* GPIO_6 = 1 */
{EM2874_R80_GPIO_P0_CTRL, 0x43, 0xff, 000}, /* GPIO_7 = 0 */
{ -1, -1, -1, -1},
};
-/* 2304:0242 PCTV QuatroStick (510e)
+/*
+ * 2304:0242 PCTV QuatroStick (510e)
* GPIO_2: decoder reset, 0=active
* GPIO_4: decoder suspend, 0=active
* GPIO_6: demod reset, 0=active
* GPIO_7: LED, 1=active
*/
-static struct em28xx_reg_seq pctv_510e[] = {
+static const struct em28xx_reg_seq pctv_510e[] = {
{EM2874_R80_GPIO_P0_CTRL, 0x10, 0xff, 100},
{EM2874_R80_GPIO_P0_CTRL, 0x14, 0xff, 100}, /* GPIO_2 = 1 */
{EM2874_R80_GPIO_P0_CTRL, 0x54, 0xff, 050}, /* GPIO_6 = 1 */
{ -1, -1, -1, -1},
};
-/* 2013:0251 PCTV QuatroStick nano (520e)
+/*
+ * 2013:0251 PCTV QuatroStick nano (520e)
* GPIO_2: decoder reset, 0=active
* GPIO_4: decoder suspend, 0=active
* GPIO_6: demod reset, 0=active
* GPIO_7: LED, 1=active
*/
-static struct em28xx_reg_seq pctv_520e[] = {
+static const struct em28xx_reg_seq pctv_520e[] = {
{EM2874_R80_GPIO_P0_CTRL, 0x10, 0xff, 100},
{EM2874_R80_GPIO_P0_CTRL, 0x14, 0xff, 100}, /* GPIO_2 = 1 */
{EM2874_R80_GPIO_P0_CTRL, 0x54, 0xff, 050}, /* GPIO_6 = 1 */
@@ -451,7 +451,8 @@ static struct em28xx_reg_seq pctv_520e[] = {
{ -1, -1, -1, -1},
};
-/* 1ae7:9003/9004 SpeedLink Vicious And Devine Laplace webcam
+/*
+ * 1ae7:9003/9004 SpeedLink Vicious And Devine Laplace webcam
* reg 0x80/0x84:
* GPIO_0: capturing LED, 0=on, 1=off
* GPIO_2: AV mute button, 0=pressed, 1=unpressed
@@ -460,13 +461,13 @@ static struct em28xx_reg_seq pctv_520e[] = {
* reg 0x81/0x85:
* GPIO_7: snapshot button, 0=pressed, 1=unpressed
*/
-static struct em28xx_reg_seq speedlink_vad_laplace_reg_seq[] = {
+static const struct em28xx_reg_seq speedlink_vad_laplace_reg_seq[] = {
{EM2820_R08_GPIO_CTRL, 0xf7, 0xff, 10},
{EM2874_R80_GPIO_P0_CTRL, 0xff, 0xb2, 10},
{ -1, -1, -1, -1},
};
-static struct em28xx_reg_seq pctv_292e[] = {
+static const struct em28xx_reg_seq pctv_292e[] = {
{EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 0},
{0x0d, 0xff, 0xff, 950},
{EM2874_R80_GPIO_P0_CTRL, 0xbd, 0xff, 100},
@@ -478,7 +479,7 @@ static struct em28xx_reg_seq pctv_292e[] = {
{-1, -1, -1, -1},
};
-static struct em28xx_reg_seq terratec_t2_stick_hd[] = {
+static const struct em28xx_reg_seq terratec_t2_stick_hd[] = {
{EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 0},
{0x0d, 0xff, 0xff, 600},
{EM2874_R80_GPIO_P0_CTRL, 0xfc, 0xff, 10},
@@ -492,7 +493,7 @@ static struct em28xx_reg_seq terratec_t2_stick_hd[] = {
{-1, -1, -1, -1},
};
-static struct em28xx_reg_seq plex_px_bcud[] = {
+static const struct em28xx_reg_seq plex_px_bcud[] = {
{EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 0},
{0x0d, 0xff, 0xff, 0},
{EM2874_R50_IR_CONFIG, 0x01, 0xff, 0},
@@ -507,8 +508,10 @@ static struct em28xx_reg_seq plex_px_bcud[] = {
};
/*
- * 2040:0265 Hauppauge WinTV-dualHD DVB
- * 2040:026d Hauppauge WinTV-dualHD ATSC/QAM
+ * 2040:0265 Hauppauge WinTV-dualHD DVB Isoc
+ * 2040:8265 Hauppauge WinTV-dualHD DVB Bulk
+ * 2040:026d Hauppauge WinTV-dualHD ATSC/QAM Isoc
+ * 2040:826d Hauppauge WinTV-dualHD ATSC/QAM Bulk
* reg 0x80/0x84:
* GPIO_0: Yellow LED tuner 1, 0=on, 1=off
* GPIO_1: Green LED tuner 1, 0=on, 1=off
@@ -517,7 +520,7 @@ static struct em28xx_reg_seq plex_px_bcud[] = {
* GPIO_5: Reset #2, 0=active
* GPIO_6: Reset #1, 0=active
*/
-static struct em28xx_reg_seq hauppauge_dualhd_dvb[] = {
+static const struct em28xx_reg_seq hauppauge_dualhd_dvb[] = {
{EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 0},
{0x0d, 0xff, 0xff, 200},
{0x50, 0x04, 0xff, 300},
@@ -534,7 +537,7 @@ static struct em28xx_reg_seq hauppauge_dualhd_dvb[] = {
/*
* Button definitions
*/
-static struct em28xx_button std_snapshot_button[] = {
+static const struct em28xx_button std_snapshot_button[] = {
{
.role = EM28XX_BUTTON_SNAPSHOT,
.reg_r = EM28XX_R0C_USBSUSP,
@@ -545,7 +548,7 @@ static struct em28xx_button std_snapshot_button[] = {
{-1, 0, 0, 0, 0},
};
-static struct em28xx_button speedlink_vad_laplace_buttons[] = {
+static const struct em28xx_button speedlink_vad_laplace_buttons[] = {
{
.role = EM28XX_BUTTON_SNAPSHOT,
.reg_r = EM2874_R85_GPIO_P1_STATE,
@@ -629,7 +632,7 @@ static struct em28xx_led hauppauge_dualhd_leds[] = {
/*
* Board definitions
*/
-struct em28xx_board em28xx_boards[] = {
+const struct em28xx_board em28xx_boards[] = {
[EM2750_BOARD_UNKNOWN] = {
.name = "EM2710/EM2750/EM2751 webcam grabber",
.xclk = EM28XX_XCLK_FREQUENCY_20MHZ,
@@ -1436,9 +1439,11 @@ struct em28xx_board em28xx_boards[] = {
.gpio = default_analog,
} },
},
- /* maybe there's a reason behind it why Terratec sells the Hybrid XS
- as Prodigy XS with a different PID, let's keep it separated for now
- maybe we'll need it lateron */
+ /*
+ * maybe there's a reason behind it why Terratec sells the Hybrid XS
+ * as Prodigy XS with a different PID, let's keep it separated for now
+ * maybe we'll need it later on
+ */
[EM2880_BOARD_TERRATEC_PRODIGY_XS] = {
.name = "Terratec Prodigy XS",
.tuner_type = TUNER_XC2028,
@@ -1782,8 +1787,9 @@ struct em28xx_board em28xx_boards[] = {
.ir_codes = RC_MAP_KWORLD_315U,
.xclk = EM28XX_XCLK_FREQUENCY_12MHZ,
.i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE,
- /* Analog mode - still not ready */
- /*.input = { {
+#if 0
+ /* FIXME: Analog mode - still not ready */
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
.amux = EM28XX_AMUX_VIDEO,
@@ -1801,7 +1807,8 @@ struct em28xx_board em28xx_boards[] = {
.amux = EM28XX_AMUX_LINE_IN,
.gpio = em2882_kworld_315u_analog1,
.aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO,
- } }, */
+ } },
+#endif
},
[EM2880_BOARD_EMPIRE_DUAL_TV] = {
.name = "Empire dual TV",
@@ -2162,17 +2169,21 @@ struct em28xx_board em28xx_boards[] = {
.gpio = evga_indtube_analog,
} },
},
- /* eb1a:2868 Empia EM2870 + Philips CU1216L NIM (Philips TDA10023 +
- Infineon TUA6034) */
+ /*
+ * eb1a:2868 Empia EM2870 + Philips CU1216L NIM
+ * (Philips TDA10023 + Infineon TUA6034)
+ */
[EM2870_BOARD_REDDO_DVB_C_USB_BOX] = {
.name = "Reddo DVB-C USB TV Box",
.tuner_type = TUNER_ABSENT,
.tuner_gpio = reddo_dvb_c_usb_box,
.has_dvb = 1,
},
- /* 1b80:a340 - Empia EM2870, NXP TDA18271HD and LG DT3304, sold
+ /*
+ * 1b80:a340 - Empia EM2870, NXP TDA18271HD and LG DT3304, sold
* initially as the KWorld PlusTV 340U, then as the UB435-Q.
- * Early variants have a TDA18271HD/C1, later ones a TDA18271HD/C2 */
+ * Early variants have a TDA18271HD/C1, later ones a TDA18271HD/C2
+ */
[EM2870_BOARD_KWORLD_A340] = {
.name = "KWorld PlusTV 340U or UB435-Q (ATSC)",
.tuner_type = TUNER_ABSENT, /* Digital-only TDA18271HD */
@@ -2180,30 +2191,38 @@ struct em28xx_board em28xx_boards[] = {
.dvb_gpio = kworld_a340_digital,
.tuner_gpio = default_tuner_gpio,
},
- /* 2013:024f PCTV nanoStick T2 290e.
- * Empia EM28174, Sony CXD2820R and NXP TDA18271HD/C2 */
+ /*
+ * 2013:024f PCTV nanoStick T2 290e.
+ * Empia EM28174, Sony CXD2820R and NXP TDA18271HD/C2
+ */
[EM28174_BOARD_PCTV_290E] = {
.name = "PCTV nanoStick T2 290e",
.def_i2c_bus = 1,
- .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ,
+ .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_100_KHZ,
.tuner_type = TUNER_ABSENT,
.tuner_gpio = pctv_290e,
.has_dvb = 1,
.ir_codes = RC_MAP_PINNACLE_PCTV_HD,
},
- /* 2013:024f PCTV DVB-S2 Stick 460e
- * Empia EM28174, NXP TDA10071, Conexant CX24118A and Allegro A8293 */
+ /*
+ * 2013:024f PCTV DVB-S2 Stick 460e
+ * Empia EM28174, NXP TDA10071, Conexant CX24118A and Allegro A8293
+ */
[EM28174_BOARD_PCTV_460E] = {
.def_i2c_bus = 1,
- .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ,
+ .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_400_KHZ,
.name = "PCTV DVB-S2 Stick (460e)",
.tuner_type = TUNER_ABSENT,
.tuner_gpio = pctv_460e,
.has_dvb = 1,
.ir_codes = RC_MAP_PINNACLE_PCTV_HD,
},
- /* eb1a:5006 Honestech VIDBOX NW03
- * Empia EM2860, Philips SAA7113, Empia EMP202, No Tuner */
+ /*
+ * eb1a:5006 Honestech VIDBOX NW03
+ * Empia EM2860, Philips SAA7113, Empia EMP202, No Tuner
+ */
[EM2860_BOARD_HT_VIDBOX_NW03] = {
.name = "Honestech Vidbox NW03",
.tuner_type = TUNER_ABSENT,
@@ -2214,12 +2233,14 @@ struct em28xx_board em28xx_boards[] = {
.amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3, /* S-VIDEO needs confirming */
+ .vmux = SAA7115_SVIDEO3, /* S-VIDEO needs check */
.amux = EM28XX_AMUX_LINE_IN,
} },
},
- /* 1b80:e425 MaxMedia UB425-TC
- * Empia EM2874B + Micronas DRX 3913KA2 + NXP TDA18271HDC2 */
+ /*
+ * 1b80:e425 MaxMedia UB425-TC
+ * Empia EM2874B + Micronas DRX 3913KA2 + NXP TDA18271HDC2
+ */
[EM2874_BOARD_MAXMEDIA_UB425_TC] = {
.name = "MaxMedia UB425-TC",
.tuner_type = TUNER_ABSENT,
@@ -2230,8 +2251,10 @@ struct em28xx_board em28xx_boards[] = {
.i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
EM28XX_I2C_FREQ_400_KHZ,
},
- /* 2304:0242 PCTV QuatroStick (510e)
- * Empia EM2884 + Micronas DRX 3926K + NXP TDA18271HDC2 */
+ /*
+ * 2304:0242 PCTV QuatroStick (510e)
+ * Empia EM2884 + Micronas DRX 3926K + NXP TDA18271HDC2
+ */
[EM2884_BOARD_PCTV_510E] = {
.name = "PCTV QuatroStick (510e)",
.tuner_type = TUNER_ABSENT,
@@ -2242,8 +2265,10 @@ struct em28xx_board em28xx_boards[] = {
.i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
EM28XX_I2C_FREQ_400_KHZ,
},
- /* 2013:0251 PCTV QuatroStick nano (520e)
- * Empia EM2884 + Micronas DRX 3926K + NXP TDA18271HDC2 */
+ /*
+ * 2013:0251 PCTV QuatroStick nano (520e)
+ * Empia EM2884 + Micronas DRX 3926K + NXP TDA18271HDC2
+ */
[EM2884_BOARD_PCTV_520E] = {
.name = "PCTV QuatroStick nano (520e)",
.tuner_type = TUNER_ABSENT,
@@ -2263,9 +2288,11 @@ struct em28xx_board em28xx_boards[] = {
.i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
EM28XX_I2C_FREQ_400_KHZ,
},
- /* 1b80:e1cc Delock 61959
+ /*
+ * 1b80:e1cc Delock 61959
* Empia EM2874B + Micronas DRX 3913KA2 + NXP TDA18271HDC2
- * mostly the same as MaxMedia UB-425-TC but different remote */
+ * mostly the same as MaxMedia UB-425-TC but different remote
+ */
[EM2874_BOARD_DELOCK_61959] = {
.name = "Delock 61959",
.tuner_type = TUNER_ABSENT,
@@ -2311,8 +2338,10 @@ struct em28xx_board em28xx_boards[] = {
.ir_codes = RC_MAP_PINNACLE_PCTV_HD,
.leds = pctv_80e_leds,
},
- /* 1ae7:9003/9004 SpeedLink Vicious And Devine Laplace webcam
- * Empia EM2765 + OmniVision OV2640 */
+ /*
+ * 1ae7:9003/9004 SpeedLink Vicious And Devine Laplace webcam
+ * Empia EM2765 + OmniVision OV2640
+ */
[EM2765_BOARD_SPEEDLINK_VAD_LAPLACE] = {
.name = "SpeedLink Vicious And Devine Laplace webcam",
.xclk = EM28XX_XCLK_FREQUENCY_24MHZ,
@@ -2329,23 +2358,29 @@ struct em28xx_board em28xx_boards[] = {
.buttons = speedlink_vad_laplace_buttons,
.leds = speedlink_vad_laplace_leds,
},
- /* 2013:0258 PCTV DVB-S2 Stick (461e)
- * Empia EM28178, Montage M88DS3103, Montage M88TS2022, Allegro A8293 */
+ /*
+ * 2013:0258 PCTV DVB-S2 Stick (461e)
+ * Empia EM28178, Montage M88DS3103, Montage M88TS2022, Allegro A8293
+ */
[EM28178_BOARD_PCTV_461E] = {
.def_i2c_bus = 1,
- .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ,
+ .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_400_KHZ,
.name = "PCTV DVB-S2 Stick (461e)",
.tuner_type = TUNER_ABSENT,
.tuner_gpio = pctv_461e,
.has_dvb = 1,
.ir_codes = RC_MAP_PINNACLE_PCTV_HD,
},
- /* 2013:025f PCTV tripleStick (292e).
- * Empia EM28178, Silicon Labs Si2168, Silicon Labs Si2157 */
+ /*
+ * 2013:025f PCTV tripleStick (292e).
+ * Empia EM28178, Silicon Labs Si2168, Silicon Labs Si2157
+ */
[EM28178_BOARD_PCTV_292E] = {
.name = "PCTV tripleStick (292e)",
.def_i2c_bus = 1,
- .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ,
+ .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_400_KHZ,
.tuner_type = TUNER_ABSENT,
.tuner_gpio = pctv_292e,
.has_dvb = 1,
@@ -2365,12 +2400,15 @@ struct em28xx_board em28xx_boards[] = {
.amux = EM28XX_AMUX_LINE_IN,
} },
},
- /* eb1a:8179 Terratec Cinergy T2 Stick HD.
- * Empia EM28178, Silicon Labs Si2168, Silicon Labs Si2146 */
+ /*
+ * eb1a:8179 Terratec Cinergy T2 Stick HD.
+ * Empia EM28178, Silicon Labs Si2168, Silicon Labs Si2146
+ */
[EM28178_BOARD_TERRATEC_T2_STICK_HD] = {
.name = "Terratec Cinergy T2 Stick HD",
.def_i2c_bus = 1,
- .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ,
+ .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_400_KHZ,
.tuner_type = TUNER_ABSENT,
.tuner_gpio = terratec_t2_stick_hd,
.has_dvb = 1,
@@ -2391,7 +2429,8 @@ struct em28xx_board em28xx_boards[] = {
.has_dvb = 1,
},
/*
- * 2040:0265 Hauppauge WinTV-dualHD (DVB version).
+ * 2040:0265 Hauppauge WinTV-dualHD (DVB version) Isoc.
+ * 2040:8265 Hauppauge WinTV-dualHD (DVB version) Bulk.
* Empia EM28274, 2x Silicon Labs Si2168, 2x Silicon Labs Si2157
*/
[EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB] = {
@@ -2402,11 +2441,13 @@ struct em28xx_board em28xx_boards[] = {
.tuner_type = TUNER_ABSENT,
.tuner_gpio = hauppauge_dualhd_dvb,
.has_dvb = 1,
+ .has_dual_ts = 1,
.ir_codes = RC_MAP_HAUPPAUGE,
.leds = hauppauge_dualhd_leds,
},
/*
- * 2040:026d Hauppauge WinTV-dualHD (model 01595 - ATSC/QAM).
+ * 2040:026d Hauppauge WinTV-dualHD (model 01595 - ATSC/QAM) Isoc.
+ * 2040:826d Hauppauge WinTV-dualHD (model 01595 - ATSC/QAM) Bulk.
* Empia EM28274, 2x LG LGDT3306A, 2x Silicon Labs Si2157
*/
[EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595] = {
@@ -2417,6 +2458,7 @@ struct em28xx_board em28xx_boards[] = {
.tuner_type = TUNER_ABSENT,
.tuner_gpio = hauppauge_dualhd_dvb,
.has_dvb = 1,
+ .has_dual_ts = 1,
.ir_codes = RC_MAP_HAUPPAUGE,
.leds = hauppauge_dualhd_leds,
},
@@ -2485,10 +2527,10 @@ struct usb_device_id em28xx_id_table[] = {
.driver_info = EM2870_BOARD_KWORLD_355U },
{ USB_DEVICE(0xeb1a, 0xe359),
.driver_info = EM2870_BOARD_KWORLD_355U },
- { USB_DEVICE(0x1b80, 0xe302),
- .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, /* Kaiser Baas Video to DVD maker */
- { USB_DEVICE(0x1b80, 0xe304),
- .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, /* Kworld DVD Maker 2 */
+ { USB_DEVICE(0x1b80, 0xe302), /* Kaiser Baas Video to DVD maker */
+ .driver_info = EM2820_BOARD_PINNACLE_DVC_90 },
+ { USB_DEVICE(0x1b80, 0xe304), /* Kworld DVD Maker 2 */
+ .driver_info = EM2820_BOARD_PINNACLE_DVC_90 },
{ USB_DEVICE(0x0ccd, 0x0036),
.driver_info = EM2820_BOARD_TERRATEC_CINERGY_250 },
{ USB_DEVICE(0x0ccd, 0x004c),
@@ -2547,8 +2589,12 @@ struct usb_device_id em28xx_id_table[] = {
.driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 },
{ USB_DEVICE(0x2040, 0x0265),
.driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB },
+ { USB_DEVICE(0x2040, 0x8265),
+ .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB },
{ USB_DEVICE(0x2040, 0x026d),
.driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 },
+ { USB_DEVICE(0x2040, 0x826d),
+ .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 },
{ USB_DEVICE(0x0438, 0xb002),
.driver_info = EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 },
{ USB_DEVICE(0x2001, 0xf112),
@@ -2609,7 +2655,13 @@ struct usb_device_id em28xx_id_table[] = {
.driver_info = EM28178_BOARD_PCTV_461E },
{ USB_DEVICE(0x2013, 0x025f),
.driver_info = EM28178_BOARD_PCTV_292E },
- { USB_DEVICE(0x2040, 0x0264), /* Hauppauge WinTV-soloHD */
+ { USB_DEVICE(0x2013, 0x0264), /* Hauppauge WinTV-soloHD 292e SE */
+ .driver_info = EM28178_BOARD_PCTV_292E },
+ { USB_DEVICE(0x2040, 0x0264), /* Hauppauge WinTV-soloHD Isoc */
+ .driver_info = EM28178_BOARD_PCTV_292E },
+ { USB_DEVICE(0x2040, 0x8264), /* Hauppauge OEM Generic WinTV-soloHD Bulk */
+ .driver_info = EM28178_BOARD_PCTV_292E },
+ { USB_DEVICE(0x2040, 0x8268), /* Hauppauge Retail WinTV-soloHD Bulk */
.driver_info = EM28178_BOARD_PCTV_292E },
{ USB_DEVICE(0x0413, 0x6f07),
.driver_info = EM2861_BOARD_LEADTEK_VC100 },
@@ -2626,7 +2678,7 @@ MODULE_DEVICE_TABLE(usb, em28xx_id_table);
/*
* EEPROM hash table for devices with generic USB IDs
*/
-static struct em28xx_hash_table em28xx_eeprom_hash[] = {
+static const struct em28xx_hash_table em28xx_eeprom_hash[] = {
/* P/N: SA 60002070465 Tuner: TVF7533-MF */
{0x6ce05a8f, EM2820_BOARD_PROLINK_PLAYTV_USB2, TUNER_YMEC_TVF_5533MF},
{0x72cc5a8b, EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2, TUNER_YMEC_TVF_5533MF},
@@ -2639,7 +2691,7 @@ static struct em28xx_hash_table em28xx_eeprom_hash[] = {
};
/* I2C devicelist hash table for devices with generic USB IDs */
-static struct em28xx_hash_table em28xx_i2c_hash[] = {
+static const struct em28xx_hash_table em28xx_i2c_hash[] = {
{0xb06a32c3, EM2800_BOARD_TERRATEC_CINERGY_200, TUNER_LG_PAL_NEW_TAPC},
{0xf51200e3, EM2800_BOARD_VGEAR_POCKETTV, TUNER_LG_PAL_NEW_TAPC},
{0x1ba50080, EM2860_BOARD_SAA711X_REFERENCE_DESIGN, TUNER_ABSENT},
@@ -2669,26 +2721,46 @@ int em28xx_tuner_callback(void *ptr, int component, int command, int arg)
}
EXPORT_SYMBOL_GPL(em28xx_tuner_callback);
-static inline void em28xx_set_model(struct em28xx *dev)
+static inline void em28xx_set_xclk_i2c_speed(struct em28xx *dev)
{
- dev->board = em28xx_boards[dev->model];
+ const struct em28xx_board *board = &em28xx_boards[dev->model];
+ u8 xclk = board->xclk, i2c_speed = board->i2c_speed;
- /* Those are the default values for the majority of boards
- Use those values if not specified otherwise at boards entry
+ /*
+ * Those are the default values for the majority of boards
+ * Use those values if not specified otherwise at boards entry
*/
- if (!dev->board.xclk)
- dev->board.xclk = EM28XX_XCLK_IR_RC5_MODE |
- EM28XX_XCLK_FREQUENCY_12MHZ;
+ if (!xclk)
+ xclk = EM28XX_XCLK_IR_RC5_MODE |
+ EM28XX_XCLK_FREQUENCY_12MHZ;
+
+ em28xx_write_reg(dev, EM28XX_R0F_XCLK, xclk);
- if (!dev->board.i2c_speed)
- dev->board.i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
- EM28XX_I2C_FREQ_100_KHZ;
+ if (!i2c_speed)
+ i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_100_KHZ;
+
+ dev->i2c_speed = i2c_speed & 0x03;
+
+ if (!dev->board.is_em2800)
+ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, i2c_speed);
+ msleep(50);
+}
+
+static inline void em28xx_set_model(struct em28xx *dev)
+{
+ dev->board = em28xx_boards[dev->model];
+ dev->has_msp34xx = dev->board.has_msp34xx;
+ dev->is_webcam = dev->board.is_webcam;
+
+ em28xx_set_xclk_i2c_speed(dev);
/* Should be initialized early, for I2C to work */
dev->def_i2c_bus = dev->board.def_i2c_bus;
}
-/* Wait until AC97_RESET reports the expected value reliably before proceeding.
+/*
+ * Wait until AC97_RESET reports the expected value reliably before proceeding.
* We also check that two unrelated registers accesses don't return the same
* value to avoid premature return.
* This procedure helps ensuring AC97 register accesses are reliable.
@@ -2718,17 +2790,17 @@ static int em28xx_wait_until_ac97_features_equals(struct em28xx *dev,
return -ETIMEDOUT;
}
-/* Since em28xx_pre_card_setup() requires a proper dev->model,
+/*
+ * Since em28xx_pre_card_setup() requires a proper dev->model,
* this won't work for boards with generic PCI IDs
*/
static void em28xx_pre_card_setup(struct em28xx *dev)
{
- /* Set the initial XCLK and I2C clock values based on the board
- definition */
- em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk & 0x7f);
- if (!dev->board.is_em2800)
- em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed);
- msleep(50);
+ /*
+ * Set the initial XCLK and I2C clock values based on the board
+ * definition
+ */
+ em28xx_set_xclk_i2c_speed(dev);
/* request some modules */
switch (dev->model) {
@@ -2739,17 +2811,19 @@ static void em28xx_pre_card_setup(struct em28xx *dev)
case EM2861_BOARD_KWORLD_PVRTV_300U:
case EM2880_BOARD_KWORLD_DVB_305U:
em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0x6d);
- msleep(10);
+ usleep_range(10000, 11000);
em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0x7d);
- msleep(10);
+ usleep_range(10000, 11000);
break;
case EM2870_BOARD_COMPRO_VIDEOMATE:
- /* TODO: someone can do some cleanup here...
- not everything's needed */
+ /*
+ * TODO: someone can do some cleanup here...
+ * not everything's needed
+ */
em28xx_write_reg(dev, EM2880_R04_GPO, 0x00);
- msleep(10);
+ usleep_range(10000, 11000);
em28xx_write_reg(dev, EM2880_R04_GPO, 0x01);
- msleep(10);
+ usleep_range(10000, 11000);
em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfd);
mdelay(70);
em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfc);
@@ -2760,8 +2834,10 @@ static void em28xx_pre_card_setup(struct em28xx *dev)
mdelay(70);
break;
case EM2870_BOARD_TERRATEC_XS_MT2060:
- /* this device needs some gpio writes to get the DVB-T
- demod work */
+ /*
+ * this device needs some gpio writes to get the DVB-T
+ * demod work
+ */
em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfe);
mdelay(70);
em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xde);
@@ -2770,8 +2846,10 @@ static void em28xx_pre_card_setup(struct em28xx *dev)
mdelay(70);
break;
case EM2870_BOARD_PINNACLE_PCTV_DVB:
- /* this device needs some gpio writes to get the
- DVB-T demod work */
+ /*
+ * this device needs some gpio writes to get the
+ * DVB-T demod work
+ */
em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfe);
mdelay(70);
em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xde);
@@ -2787,13 +2865,13 @@ static void em28xx_pre_card_setup(struct em28xx *dev)
case EM2882_BOARD_KWORLD_ATSC_315U:
em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xff);
- msleep(10);
+ usleep_range(10000, 11000);
em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfe);
- msleep(10);
+ usleep_range(10000, 11000);
em28xx_write_reg(dev, EM2880_R04_GPO, 0x00);
- msleep(10);
+ usleep_range(10000, 11000);
em28xx_write_reg(dev, EM2880_R04_GPO, 0x08);
- msleep(10);
+ usleep_range(10000, 11000);
break;
case EM2860_BOARD_KAIOMY_TVNPC_U2:
@@ -2801,11 +2879,11 @@ static void em28xx_pre_card_setup(struct em28xx *dev)
em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
em28xx_write_regs(dev, 0x0d, "\x42", 1);
em28xx_write_regs(dev, 0x08, "\xfd", 1);
- msleep(10);
+ usleep_range(10000, 11000);
em28xx_write_regs(dev, 0x08, "\xff", 1);
- msleep(10);
+ usleep_range(10000, 11000);
em28xx_write_regs(dev, 0x08, "\x7f", 1);
- msleep(10);
+ usleep_range(10000, 11000);
em28xx_write_regs(dev, 0x08, "\x6b", 1);
break;
@@ -2817,7 +2895,7 @@ static void em28xx_pre_card_setup(struct em28xx *dev)
em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xff);
msleep(70);
em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xf7);
- msleep(10);
+ usleep_range(10000, 11000);
em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfe);
msleep(70);
em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfd);
@@ -2825,7 +2903,8 @@ static void em28xx_pre_card_setup(struct em28xx *dev)
break;
case EM2860_BOARD_TERRATEC_GRABBY:
- /* HACK?: Ensure AC97 register reading is reliable before
+ /*
+ * HACK?: Ensure AC97 register reading is reliable before
* proceeding. In practice, this will wait about 1.6 seconds.
*/
em28xx_wait_until_ac97_features_equals(dev, 0x6a90);
@@ -2843,7 +2922,7 @@ static int em28xx_hint_board(struct em28xx *dev)
{
int i;
- if (dev->board.is_webcam) {
+ if (dev->is_webcam) {
if (dev->em28xx_sensor == EM28XX_MT9V011) {
dev->model = EM2820_BOARD_SILVERCREST_WEBCAM;
} else if (dev->em28xx_sensor == EM28XX_MT9M001 ||
@@ -2855,7 +2934,8 @@ static int em28xx_hint_board(struct em28xx *dev)
return 0;
}
- /* HINT method: EEPROM
+ /*
+ * HINT method: EEPROM
*
* This method works only for boards with eeprom.
* Uses a hash of all eeprom bytes. The hash should be
@@ -2881,7 +2961,8 @@ static int em28xx_hint_board(struct em28xx *dev)
}
}
- /* HINT method: I2C attached devices
+ /*
+ * HINT method: I2C attached devices
*
* This method works for all boards.
* Uses a hash of i2c scanned devices.
@@ -2935,11 +3016,11 @@ static void em28xx_card_setup(struct em28xx *dev)
* If the device can be a webcam, seek for a sensor.
* If sensor is not found, then it isn't a webcam.
*/
- if (dev->board.is_webcam) {
+ if (dev->is_webcam) {
em28xx_detect_sensor(dev);
if (dev->em28xx_sensor == EM28XX_NOSENSOR)
/* NOTE: error/unknown sensor/no sensor */
- dev->board.is_webcam = 0;
+ dev->is_webcam = 0;
}
switch (dev->model) {
@@ -2959,9 +3040,9 @@ static void em28xx_card_setup(struct em28xx *dev)
* This solution is only valid if they do not share eeprom
* hash identities which has not been determined as yet.
*/
- if (em28xx_hint_board(dev) < 0)
+ if (em28xx_hint_board(dev) < 0) {
dev_err(&dev->intf->dev, "Board not discovered\n");
- else {
+ } else {
em28xx_set_model(dev);
em28xx_pre_card_setup(dev);
}
@@ -2971,7 +3052,7 @@ static void em28xx_card_setup(struct em28xx *dev)
}
dev_info(&dev->intf->dev, "Identified as %s (card=%d)\n",
- dev->board.name, dev->model);
+ dev->board.name, dev->model);
dev->tuner_type = em28xx_boards[dev->model].tuner_type;
@@ -2988,7 +3069,7 @@ static void em28xx_card_setup(struct em28xx *dev)
{
struct tveeprom tv;
- if (dev->eedata == NULL)
+ if (!dev->eedata)
break;
#if defined(CONFIG_MODULES) && defined(MODULE)
request_module("tveeprom");
@@ -3001,15 +3082,15 @@ static void em28xx_card_setup(struct em28xx *dev)
if (tv.audio_processor == TVEEPROM_AUDPROC_MSP) {
dev->i2s_speed = 2048000;
- dev->board.has_msp34xx = 1;
+ dev->has_msp34xx = 1;
}
break;
}
case EM2882_BOARD_KWORLD_ATSC_315U:
em28xx_write_reg(dev, 0x0d, 0x42);
- msleep(10);
+ usleep_range(10000, 11000);
em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfd);
- msleep(10);
+ usleep_range(10000, 11000);
break;
case EM2820_BOARD_KWORLD_PVRTV2800RF:
/* GPIO enables sound on KWORLD PVR TV 2800RF */
@@ -3034,10 +3115,12 @@ static void em28xx_card_setup(struct em28xx *dev)
if (!em28xx_hint_board(dev))
em28xx_set_model(dev);
- /* In cases where we had to use a board hint, the call to
- em28xx_set_mode() in em28xx_pre_card_setup() was a no-op,
- so make the call now so the analog GPIOs are set properly
- before probing the i2c bus. */
+ /*
+ * In cases where we had to use a board hint, the call to
+ * em28xx_set_mode() in em28xx_pre_card_setup() was a no-op,
+ * so make the call now so the analog GPIOs are set properly
+ * before probing the i2c bus.
+ */
em28xx_gpio_set(dev, dev->board.tuner_gpio);
em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
break;
@@ -3059,10 +3142,12 @@ static void em28xx_card_setup(struct em28xx *dev)
if (!em28xx_hint_board(dev))
em28xx_set_model(dev);
- /* In cases where we had to use a board hint, the call to
- em28xx_set_mode() in em28xx_pre_card_setup() was a no-op,
- so make the call now so the analog GPIOs are set properly
- before probing the i2c bus. */
+ /*
+ * In cases where we had to use a board hint, the call to
+ * em28xx_set_mode() in em28xx_pre_card_setup() was a no-op,
+ * so make the call now so the analog GPIOs are set properly
+ * before probing the i2c bus.
+ */
em28xx_gpio_set(dev, dev->board.tuner_gpio);
em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
break;
@@ -3147,8 +3232,8 @@ static void request_module_async(struct work_struct *work)
*/
/*
- * Devicdes with an audio-only interface also have a V4L/DVB/RC
- * interface. Don't register extensions twice on those devices.
+ * Devices with an audio-only intf also have a V4L/DVB/RC
+ * intf. Don't register extensions twice on those devices.
*/
if (dev->is_audio_only) {
#if defined(CONFIG_MODULES) && defined(MODULE)
@@ -3209,7 +3294,6 @@ static int em28xx_media_device_init(struct em28xx *dev,
static void em28xx_unregister_media_device(struct em28xx *dev)
{
-
#ifdef CONFIG_MEDIA_CONTROLLER
if (dev->media_dev) {
media_device_unregister(dev->media_dev);
@@ -3224,7 +3308,7 @@ static void em28xx_unregister_media_device(struct em28xx *dev)
* em28xx_release_resources()
* unregisters the v4l2,i2c and usb devices
* called when the device gets disconnected or at module unload
-*/
+ */
static void em28xx_release_resources(struct em28xx *dev)
{
struct usb_device *udev = interface_to_usbdev(dev->intf);
@@ -3239,7 +3323,8 @@ static void em28xx_release_resources(struct em28xx *dev)
em28xx_i2c_unregister(dev, 1);
em28xx_i2c_unregister(dev, 0);
- usb_put_dev(udev);
+ if (dev->ts == PRIMARY_TS)
+ usb_put_dev(udev);
/* Mark device as unused */
clear_bit(dev->devno, em28xx_devused);
@@ -3273,13 +3358,13 @@ EXPORT_SYMBOL_GPL(em28xx_free_device);
* allocates and inits the device structs, registers i2c bus and v4l device
*/
static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev,
- struct usb_interface *interface,
+ struct usb_interface *intf,
int minor)
{
int retval;
const char *chip_name = NULL;
- dev->intf = interface;
+ dev->intf = intf;
mutex_init(&dev->ctrl_urb_lock);
spin_lock_init(&dev->slock);
@@ -3382,17 +3467,6 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev,
em28xx_pre_card_setup(dev);
- if (!dev->board.is_em2800) {
- /* Resets I2C speed */
- retval = em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed);
- if (retval < 0) {
- dev_err(&dev->intf->dev,
- "%s: em28xx_write_reg failed! retval [%d]\n",
- __func__, retval);
- return retval;
- }
- }
-
rt_mutex_init(&dev->i2c_bus_lock);
/* register i2c bus 0 */
@@ -3417,8 +3491,8 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev,
EM28XX_I2C_ALGO_EM28XX);
if (retval < 0) {
dev_err(&dev->intf->dev,
- "%s: em28xx_i2c_register bus 1 - error [%d]!\n",
- __func__, retval);
+ "%s: em28xx_i2c_register bus 1 - error [%d]!\n",
+ __func__, retval);
em28xx_i2c_unregister(dev, 0);
@@ -3432,14 +3506,147 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev,
return 0;
}
+static int em28xx_duplicate_dev(struct em28xx *dev)
+{
+ int nr;
+ struct em28xx *sec_dev = kzalloc(sizeof(*sec_dev), GFP_KERNEL);
+
+ if (!sec_dev) {
+ dev->dev_next = NULL;
+ return -ENOMEM;
+ }
+ memcpy(sec_dev, dev, sizeof(*sec_dev));
+ /* Check to see next free device and mark as used */
+ do {
+ nr = find_first_zero_bit(em28xx_devused, EM28XX_MAXBOARDS);
+ if (nr >= EM28XX_MAXBOARDS) {
+ /* No free device slots */
+ dev_warn(&dev->intf->dev, ": Supports only %i em28xx boards.\n",
+ EM28XX_MAXBOARDS);
+ kfree(sec_dev);
+ dev->dev_next = NULL;
+ return -ENOMEM;
+ }
+ } while (test_and_set_bit(nr, em28xx_devused));
+ sec_dev->devno = nr;
+ snprintf(sec_dev->name, 28, "em28xx #%d", nr);
+ sec_dev->dev_next = NULL;
+ dev->dev_next = sec_dev;
+ return 0;
+}
+
/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */
#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
+static void em28xx_check_usb_descriptor(struct em28xx *dev,
+ struct usb_device *udev,
+ struct usb_interface *intf,
+ int alt, int ep,
+ bool *has_vendor_audio,
+ bool *has_video,
+ bool *has_dvb)
+{
+ const struct usb_endpoint_descriptor *e;
+ int sizedescr, size;
+
+ /*
+ * NOTE:
+ *
+ * Old logic with support for isoc transfers only was:
+ * 0x82 isoc => analog
+ * 0x83 isoc => audio
+ * 0x84 isoc => digital
+ *
+ * New logic with support for bulk transfers
+ * 0x82 isoc => analog
+ * 0x82 bulk => analog
+ * 0x83 isoc* => audio
+ * 0x84 isoc => digital
+ * 0x84 bulk => analog or digital**
+ * 0x85 isoc => digital TS2
+ * 0x85 bulk => digital TS2
+ * (*: audio should always be isoc)
+ * (**: analog, if ep 0x82 is isoc, otherwise digital)
+ *
+ * The new logic preserves backwards compatibility and
+ * reflects the endpoint configurations we have seen
+ * so far. But there might be devices for which this
+ * logic is not sufficient...
+ */
+
+ e = &intf->altsetting[alt].endpoint[ep].desc;
+
+ if (!usb_endpoint_dir_in(e))
+ return;
+
+ sizedescr = le16_to_cpu(e->wMaxPacketSize);
+ size = sizedescr & 0x7ff;
+
+ if (udev->speed == USB_SPEED_HIGH)
+ size = size * hb_mult(sizedescr);
+
+ /* Only inspect input endpoints */
+
+ switch (e->bEndpointAddress) {
+ case 0x82:
+ *has_video = true;
+ if (usb_endpoint_xfer_isoc(e)) {
+ dev->analog_ep_isoc = e->bEndpointAddress;
+ dev->alt_max_pkt_size_isoc[alt] = size;
+ } else if (usb_endpoint_xfer_bulk(e)) {
+ dev->analog_ep_bulk = e->bEndpointAddress;
+ }
+ return;
+ case 0x83:
+ if (usb_endpoint_xfer_isoc(e))
+ *has_vendor_audio = true;
+ else
+ dev_err(&intf->dev,
+ "error: skipping audio endpoint 0x83, because it uses bulk transfers !\n");
+ return;
+ case 0x84:
+ if (*has_video && (usb_endpoint_xfer_bulk(e))) {
+ dev->analog_ep_bulk = e->bEndpointAddress;
+ } else {
+ if (usb_endpoint_xfer_isoc(e)) {
+ if (size > dev->dvb_max_pkt_size_isoc) {
+ /*
+ * 2) some manufacturers (e.g. Terratec)
+ * disable endpoints by setting
+ * wMaxPacketSize to 0 bytes for all
+ * alt settings. So far, we've seen
+ * this for DVB isoc endpoints only.
+ */
+ *has_dvb = true;
+ dev->dvb_ep_isoc = e->bEndpointAddress;
+ dev->dvb_max_pkt_size_isoc = size;
+ dev->dvb_alt_isoc = alt;
+ }
+ } else {
+ *has_dvb = true;
+ dev->dvb_ep_bulk = e->bEndpointAddress;
+ }
+ }
+ return;
+ case 0x85:
+ if (usb_endpoint_xfer_isoc(e)) {
+ if (size > dev->dvb_max_pkt_size_isoc_ts2) {
+ dev->dvb_ep_isoc_ts2 = e->bEndpointAddress;
+ dev->dvb_max_pkt_size_isoc_ts2 = size;
+ dev->dvb_alt_isoc = alt;
+ }
+ } else {
+ dev->dvb_ep_bulk_ts2 = e->bEndpointAddress;
+ }
+ return;
+ }
+}
+
/*
* em28xx_usb_probe()
* checks for supported devices
*/
-static int em28xx_usb_probe(struct usb_interface *interface,
+static int em28xx_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *udev;
@@ -3447,17 +3654,17 @@ static int em28xx_usb_probe(struct usb_interface *interface,
int retval;
bool has_vendor_audio = false, has_video = false, has_dvb = false;
int i, nr, try_bulk;
- const int ifnum = interface->altsetting[0].desc.bInterfaceNumber;
+ const int ifnum = intf->altsetting[0].desc.bInterfaceNumber;
char *speed;
- udev = usb_get_dev(interface_to_usbdev(interface));
+ udev = usb_get_dev(interface_to_usbdev(intf));
/* Check to see next free device and mark as used */
do {
nr = find_first_zero_bit(em28xx_devused, EM28XX_MAXBOARDS);
if (nr >= EM28XX_MAXBOARDS) {
/* No free device slots */
- dev_err(&interface->dev,
+ dev_err(&intf->dev,
"Driver supports up to %i em28xx boards.\n",
EM28XX_MAXBOARDS);
retval = -ENOMEM;
@@ -3466,13 +3673,13 @@ static int em28xx_usb_probe(struct usb_interface *interface,
} while (test_and_set_bit(nr, em28xx_devused));
/* Don't register audio interfaces */
- if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
- dev_err(&interface->dev,
+ if (intf->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
+ dev_err(&intf->dev,
"audio device (%04x:%04x): interface %i, class %i\n",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct),
ifnum,
- interface->altsetting[0].desc.bInterfaceClass);
+ intf->altsetting[0].desc.bInterfaceClass);
retval = -ENODEV;
goto err;
@@ -3480,106 +3687,33 @@ static int em28xx_usb_probe(struct usb_interface *interface,
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (dev == NULL) {
+ if (!dev) {
retval = -ENOMEM;
goto err;
}
/* compute alternate max packet sizes */
- dev->alt_max_pkt_size_isoc =
- kmalloc(sizeof(dev->alt_max_pkt_size_isoc[0]) *
- interface->num_altsetting, GFP_KERNEL);
- if (dev->alt_max_pkt_size_isoc == NULL) {
+ dev->alt_max_pkt_size_isoc = kcalloc(intf->num_altsetting,
+ sizeof(dev->alt_max_pkt_size_isoc[0]),
+ GFP_KERNEL);
+ if (!dev->alt_max_pkt_size_isoc) {
kfree(dev);
retval = -ENOMEM;
goto err;
}
/* Get endpoints */
- for (i = 0; i < interface->num_altsetting; i++) {
+ for (i = 0; i < intf->num_altsetting; i++) {
int ep;
- for (ep = 0; ep < interface->altsetting[i].desc.bNumEndpoints; ep++) {
- const struct usb_endpoint_descriptor *e;
- int sizedescr, size;
-
- e = &interface->altsetting[i].endpoint[ep].desc;
-
- sizedescr = le16_to_cpu(e->wMaxPacketSize);
- size = sizedescr & 0x7ff;
-
- if (udev->speed == USB_SPEED_HIGH)
- size = size * hb_mult(sizedescr);
-
- if (usb_endpoint_dir_in(e)) {
- switch (e->bEndpointAddress) {
- case 0x82:
- has_video = true;
- if (usb_endpoint_xfer_isoc(e)) {
- dev->analog_ep_isoc =
- e->bEndpointAddress;
- dev->alt_max_pkt_size_isoc[i] = size;
- } else if (usb_endpoint_xfer_bulk(e)) {
- dev->analog_ep_bulk =
- e->bEndpointAddress;
- }
- break;
- case 0x83:
- if (usb_endpoint_xfer_isoc(e)) {
- has_vendor_audio = true;
- } else {
- dev_err(&interface->dev,
- "error: skipping audio endpoint 0x83, because it uses bulk transfers !\n");
- }
- break;
- case 0x84:
- if (has_video &&
- (usb_endpoint_xfer_bulk(e))) {
- dev->analog_ep_bulk =
- e->bEndpointAddress;
- } else {
- if (usb_endpoint_xfer_isoc(e)) {
- if (size > dev->dvb_max_pkt_size_isoc) {
- has_dvb = true; /* see NOTE (~) */
- dev->dvb_ep_isoc = e->bEndpointAddress;
- dev->dvb_max_pkt_size_isoc = size;
- dev->dvb_alt_isoc = i;
- }
- } else {
- has_dvb = true;
- dev->dvb_ep_bulk = e->bEndpointAddress;
- }
- }
- break;
- }
- }
- /* NOTE:
- * Old logic with support for isoc transfers only was:
- * 0x82 isoc => analog
- * 0x83 isoc => audio
- * 0x84 isoc => digital
- *
- * New logic with support for bulk transfers
- * 0x82 isoc => analog
- * 0x82 bulk => analog
- * 0x83 isoc* => audio
- * 0x84 isoc => digital
- * 0x84 bulk => analog or digital**
- * (*: audio should always be isoc)
- * (**: analog, if ep 0x82 is isoc, otherwise digital)
- *
- * The new logic preserves backwards compatibility and
- * reflects the endpoint configurations we have seen
- * so far. But there might be devices for which this
- * logic is not sufficient...
- */
- /*
- * NOTE (~): some manufacturers (e.g. Terratec) disable
- * endpoints by setting wMaxPacketSize to 0 bytes for
- * all alt settings. So far, we've seen this for
- * DVB isoc endpoints only.
- */
- }
+ for (ep = 0;
+ ep < intf->altsetting[i].desc.bNumEndpoints;
+ ep++)
+ em28xx_check_usb_descriptor(dev, udev, intf,
+ i, ep,
+ &has_vendor_audio,
+ &has_video,
+ &has_dvb);
}
if (!(has_vendor_audio || has_video || has_dvb)) {
@@ -3602,7 +3736,7 @@ static int em28xx_usb_probe(struct usb_interface *interface,
speed = "unknown";
}
- dev_err(&interface->dev,
+ dev_err(&intf->dev,
"New device %s %s @ %s Mbps (%04x:%04x, interface %d, class %d)\n",
udev->manufacturer ? udev->manufacturer : "",
udev->product ? udev->product : "",
@@ -3610,7 +3744,7 @@ static int em28xx_usb_probe(struct usb_interface *interface,
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct),
ifnum,
- interface->altsetting->desc.bInterfaceNumber);
+ intf->altsetting->desc.bInterfaceNumber);
/*
* Make sure we have 480 Mbps of bandwidth, otherwise things like
@@ -3618,8 +3752,8 @@ static int em28xx_usb_probe(struct usb_interface *interface,
* not enough even for most Digital TV streams.
*/
if (udev->speed != USB_SPEED_HIGH && disable_usb_speed_check == 0) {
- dev_err(&interface->dev, "Device initialization failed.\n");
- dev_err(&interface->dev,
+ dev_err(&intf->dev, "Device initialization failed.\n");
+ dev_err(&intf->dev,
"Device must be connected to a high-speed USB 2.0 port.\n");
retval = -ENODEV;
goto err_free;
@@ -3632,53 +3766,56 @@ static int em28xx_usb_probe(struct usb_interface *interface,
dev->has_video = has_video;
dev->ifnum = ifnum;
+ dev->ts = PRIMARY_TS;
+ snprintf(dev->name, 28, "em28xx");
+ dev->dev_next = NULL;
+
if (has_vendor_audio) {
- dev_err(&interface->dev,
+ dev_err(&intf->dev,
"Audio interface %i found (Vendor Class)\n", ifnum);
dev->usb_audio_type = EM28XX_USB_AUDIO_VENDOR;
}
- /* Checks if audio is provided by a USB Audio Class interface */
+ /* Checks if audio is provided by a USB Audio Class intf */
for (i = 0; i < udev->config->desc.bNumInterfaces; i++) {
struct usb_interface *uif = udev->config->interface[i];
if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
if (has_vendor_audio)
- dev_err(&interface->dev,
+ dev_err(&intf->dev,
"em28xx: device seems to have vendor AND usb audio class interfaces !\n"
- "\t\tThe vendor interface will be ignored. Please contact the developers <linux-media@vger.kernel.org>\n");
+ "\t\tThe vendor interface will be ignored. Please contact the developers <linux-media@vger.kernel.org>\n");
dev->usb_audio_type = EM28XX_USB_AUDIO_CLASS;
break;
}
}
if (has_video)
- dev_err(&interface->dev, "Video interface %i found:%s%s\n",
+ dev_err(&intf->dev, "Video interface %i found:%s%s\n",
ifnum,
dev->analog_ep_bulk ? " bulk" : "",
dev->analog_ep_isoc ? " isoc" : "");
if (has_dvb)
- dev_err(&interface->dev, "DVB interface %i found:%s%s\n",
+ dev_err(&intf->dev, "DVB interface %i found:%s%s\n",
ifnum,
dev->dvb_ep_bulk ? " bulk" : "",
dev->dvb_ep_isoc ? " isoc" : "");
- dev->num_alt = interface->num_altsetting;
+ dev->num_alt = intf->num_altsetting;
- if ((unsigned)card[nr] < em28xx_bcount)
+ if ((unsigned int)card[nr] < em28xx_bcount)
dev->model = card[nr];
- /* save our data pointer in this interface device */
- usb_set_intfdata(interface, dev);
+ /* save our data pointer in this intf device */
+ usb_set_intfdata(intf, dev);
/* allocate device struct and check if the device is a webcam */
mutex_init(&dev->lock);
- retval = em28xx_init_dev(dev, udev, interface, nr);
- if (retval) {
+ retval = em28xx_init_dev(dev, udev, intf, nr);
+ if (retval)
goto err_free;
- }
if (usb_xfer_mode < 0) {
- if (dev->board.is_webcam)
+ if (dev->is_webcam)
try_bulk = 1;
else
try_bulk = 0;
@@ -3690,8 +3827,7 @@ static int em28xx_usb_probe(struct usb_interface *interface,
if (has_video &&
dev->board.decoder == EM28XX_NODECODER &&
dev->em28xx_sensor == EM28XX_NOSENSOR) {
-
- dev_err(&interface->dev,
+ dev_err(&intf->dev,
"Currently, V4L2 is not supported on this model\n");
has_video = false;
dev->has_video = false;
@@ -3701,16 +3837,75 @@ static int em28xx_usb_probe(struct usb_interface *interface,
if (has_video) {
if (!dev->analog_ep_isoc || (try_bulk && dev->analog_ep_bulk))
dev->analog_xfer_bulk = 1;
- dev_err(&interface->dev, "analog set to %s mode.\n",
+ dev_err(&intf->dev, "analog set to %s mode.\n",
dev->analog_xfer_bulk ? "bulk" : "isoc");
}
if (has_dvb) {
if (!dev->dvb_ep_isoc || (try_bulk && dev->dvb_ep_bulk))
dev->dvb_xfer_bulk = 1;
- dev_err(&interface->dev, "dvb set to %s mode.\n",
+ dev_err(&intf->dev, "dvb set to %s mode.\n",
dev->dvb_xfer_bulk ? "bulk" : "isoc");
}
+ if (dev->board.has_dual_ts && em28xx_duplicate_dev(dev) == 0) {
+ dev->dev_next->ts = SECONDARY_TS;
+ dev->dev_next->alt = -1;
+ dev->dev_next->is_audio_only = has_vendor_audio &&
+ !(has_video || has_dvb);
+ dev->dev_next->has_video = false;
+ dev->dev_next->ifnum = ifnum;
+ dev->dev_next->model = id->driver_info;
+
+ mutex_init(&dev->dev_next->lock);
+ retval = em28xx_init_dev(dev->dev_next, udev, intf,
+ dev->dev_next->devno);
+ if (retval)
+ goto err_free;
+
+ dev->dev_next->board.ir_codes = NULL; /* No IR for 2nd tuner */
+ dev->dev_next->board.has_ir_i2c = 0; /* No IR for 2nd tuner */
+
+ if (usb_xfer_mode < 0) {
+ if (dev->dev_next->is_webcam)
+ try_bulk = 1;
+ else
+ try_bulk = 0;
+ } else {
+ try_bulk = usb_xfer_mode > 0;
+ }
+
+ /* Select USB transfer types to use */
+ if (has_dvb) {
+ if (!dev->dvb_ep_isoc_ts2 ||
+ (try_bulk && dev->dvb_ep_bulk_ts2))
+ dev->dev_next->dvb_xfer_bulk = 1;
+ dev_info(&dev->intf->dev, "dvb ts2 set to %s mode.\n",
+ dev->dev_next->dvb_xfer_bulk ? "bulk" : "isoc");
+ }
+
+ dev->dev_next->dvb_ep_isoc = dev->dvb_ep_isoc_ts2;
+ dev->dev_next->dvb_ep_bulk = dev->dvb_ep_bulk_ts2;
+ dev->dev_next->dvb_max_pkt_size_isoc = dev->dvb_max_pkt_size_isoc_ts2;
+ dev->dev_next->dvb_alt_isoc = dev->dvb_alt_isoc;
+
+ /* Configuare hardware to support TS2*/
+ if (dev->dvb_xfer_bulk) {
+ /* The ep4 and ep5 are configuared for BULK */
+ em28xx_write_reg(dev, 0x0b, 0x96);
+ mdelay(100);
+ em28xx_write_reg(dev, 0x0b, 0x80);
+ mdelay(100);
+ } else {
+ /* The ep4 and ep5 are configuared for ISO */
+ em28xx_write_reg(dev, 0x0b, 0x96);
+ mdelay(100);
+ em28xx_write_reg(dev, 0x0b, 0x82);
+ mdelay(100);
+ }
+
+ kref_init(&dev->dev_next->ref);
+ }
+
kref_init(&dev->ref);
request_modules(dev);
@@ -3743,45 +3938,59 @@ err_no_slot:
* called when the device gets disconnected
* video device will be unregistered on v4l2_close in case it is still open
*/
-static void em28xx_usb_disconnect(struct usb_interface *interface)
+static void em28xx_usb_disconnect(struct usb_interface *intf)
{
struct em28xx *dev;
- dev = usb_get_intfdata(interface);
- usb_set_intfdata(interface, NULL);
+ dev = usb_get_intfdata(intf);
+ usb_set_intfdata(intf, NULL);
if (!dev)
return;
+ if (dev->dev_next) {
+ dev->dev_next->disconnected = 1;
+ dev_info(&dev->intf->dev, "Disconnecting %s\n",
+ dev->dev_next->name);
+ flush_request_modules(dev->dev_next);
+ }
+
dev->disconnected = 1;
- dev_err(&dev->intf->dev, "Disconnecting\n");
+ dev_err(&dev->intf->dev, "Disconnecting %s\n", dev->name);
flush_request_modules(dev);
em28xx_close_extension(dev);
+ if (dev->dev_next)
+ em28xx_release_resources(dev->dev_next);
em28xx_release_resources(dev);
+
+ if (dev->dev_next) {
+ kref_put(&dev->dev_next->ref, em28xx_free_device);
+ dev->dev_next = NULL;
+ }
kref_put(&dev->ref, em28xx_free_device);
}
-static int em28xx_usb_suspend(struct usb_interface *interface,
+static int em28xx_usb_suspend(struct usb_interface *intf,
pm_message_t message)
{
struct em28xx *dev;
- dev = usb_get_intfdata(interface);
+ dev = usb_get_intfdata(intf);
if (!dev)
return 0;
em28xx_suspend_extension(dev);
return 0;
}
-static int em28xx_usb_resume(struct usb_interface *interface)
+static int em28xx_usb_resume(struct usb_interface *intf)
{
struct em28xx *dev;
- dev = usb_get_intfdata(interface);
+ dev = usb_get_intfdata(intf);
if (!dev)
return 0;
em28xx_resume_extension(dev);
diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c
index 1d0d8cc06103..36d341fb65dd 100644
--- a/drivers/media/usb/em28xx/em28xx-core.c
+++ b/drivers/media/usb/em28xx/em28xx-core.c
@@ -1,26 +1,22 @@
-/*
- em28xx-core.c - driver for Empia EM2800/EM2820/2840 USB video capture devices
-
- Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
- Markus Rechberger <mrechberger@gmail.com>
- Mauro Carvalho Chehab <mchehab@infradead.org>
- Sascha Sommer <saschasommer@freenet.de>
- Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.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.
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// em28xx-core.c - driver for Empia EM2800/EM2820/2840 USB video capture devices
+//
+// Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
+// Markus Rechberger <mrechberger@gmail.com>
+// Mauro Carvalho Chehab <mchehab@infradead.org>
+// Sascha Sommer <saschasommer@freenet.de>
+// Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.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.
#include "em28xx.h"
@@ -41,7 +37,7 @@
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_VERSION(EM28XX_VERSION);
/* #define ENABLE_DEBUG_ISOC_FRAMES */
@@ -60,7 +56,6 @@ static unsigned int reg_debug;
module_param(reg_debug, int, 0644);
MODULE_PARM_DESC(reg_debug, "enable debug messages [URB reg]");
-
#define em28xx_regdbg(fmt, arg...) do { \
if (reg_debug) \
dev_printk(KERN_DEBUG, &dev->intf->dev, \
@@ -97,10 +92,11 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg,
0x0000, reg, dev->urb_buf, len, HZ);
if (ret < 0) {
em28xx_regdbg("(pipe 0x%08x): IN: %02x %02x %02x %02x %02x %02x %02x %02x failed with error %i\n",
- pipe, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- req, 0, 0,
- reg & 0xff, reg >> 8,
- len & 0xff, len >> 8, ret);
+ pipe,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ req, 0, 0,
+ reg & 0xff, reg >> 8,
+ len & 0xff, len >> 8, ret);
mutex_unlock(&dev->ctrl_urb_lock);
return usb_translate_errors(ret);
}
@@ -111,10 +107,10 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg,
mutex_unlock(&dev->ctrl_urb_lock);
em28xx_regdbg("(pipe 0x%08x): IN: %02x %02x %02x %02x %02x %02x %02x %02x <<< %*ph\n",
- pipe, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- req, 0, 0,
- reg & 0xff, reg >> 8,
- len & 0xff, len >> 8, len, buf);
+ pipe, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ req, 0, 0,
+ reg & 0xff, reg >> 8,
+ len & 0xff, len >> 8, len, buf);
return ret;
}
@@ -155,7 +151,7 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf,
if (dev->disconnected)
return -ENODEV;
- if ((len < 1) || (len > URB_MAX_CTRL_SIZE))
+ if (len < 1 || len > URB_MAX_CTRL_SIZE)
return -EINVAL;
mutex_lock(&dev->ctrl_urb_lock);
@@ -341,8 +337,9 @@ static int set_ac97_input(struct em28xx *dev)
int ret, i;
enum em28xx_amux amux = dev->ctl_ainput;
- /* EM28XX_AMUX_VIDEO2 is a special case used to indicate that
- em28xx should point to LINE IN, while AC97 should use VIDEO
+ /*
+ * EM28XX_AMUX_VIDEO2 is a special case used to indicate that
+ * em28xx should point to LINE IN, while AC97 should use VIDEO
*/
if (amux == EM28XX_AMUX_VIDEO2)
amux = EM28XX_AMUX_VIDEO;
@@ -378,9 +375,9 @@ static int em28xx_set_audio_source(struct em28xx *dev)
return ret;
}
- if (dev->board.has_msp34xx)
+ if (dev->has_msp34xx) {
input = EM28XX_AUDIO_SRC_TUNER;
- else {
+ } else {
switch (dev->ctl_ainput) {
case EM28XX_AMUX_VIDEO:
input = EM28XX_AUDIO_SRC_TUNER;
@@ -399,7 +396,7 @@ static int em28xx_set_audio_source(struct em28xx *dev)
ret = em28xx_write_reg_bits(dev, EM28XX_R0E_AUDIOSRC, input, 0xc0);
if (ret < 0)
return ret;
- msleep(5);
+ usleep_range(10000, 11000);
switch (dev->audio_mode.ac97) {
case EM28XX_NO_AC97:
@@ -432,8 +429,9 @@ int em28xx_audio_analog_set(struct em28xx *dev)
if (dev->int_audio_type == EM28XX_INT_AUDIO_NONE)
return 0;
- /* It is assumed that all devices use master volume for output.
- It would be possible to use also line output.
+ /*
+ * It is assumed that all devices use master volume for output.
+ * It would be possible to use also line output.
*/
if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
/* Mute all outputs */
@@ -453,7 +451,7 @@ int em28xx_audio_analog_set(struct em28xx *dev)
ret = em28xx_write_reg(dev, EM28XX_R0F_XCLK, xclk);
if (ret < 0)
return ret;
- msleep(10);
+ usleep_range(10000, 11000);
/* Selects the proper audio input */
ret = em28xx_set_audio_source(dev);
@@ -487,8 +485,10 @@ int em28xx_audio_analog_set(struct em28xx *dev)
if (dev->ctl_aoutput & EM28XX_AOUT_PCM_IN) {
int sel = ac97_return_record_select(dev->ctl_aoutput);
- /* Use the same input for both left and right
- channels */
+ /*
+ * Use the same input for both left and right
+ * channels
+ */
sel |= (sel << 8);
em28xx_write_ac97(dev, AC97_REC_SEL, sel);
@@ -539,7 +539,7 @@ int em28xx_audio_setup(struct em28xx *dev)
else
i2s_samplerates = 3;
dev_info(&dev->intf->dev, "I2S Audio (%d sample rate(s))\n",
- i2s_samplerates);
+ i2s_samplerates);
/* Skip the code that does AC97 vendor detection */
dev->audio_mode.ac97 = EM28XX_NO_AC97;
goto init_audio;
@@ -579,7 +579,7 @@ int em28xx_audio_setup(struct em28xx *dev)
dev_warn(&dev->intf->dev, "AC97 features = 0x%04x\n", feat);
/* Try to identify what audio processor we have */
- if (((vid == 0xffffffff) || (vid == 0x83847650)) && (feat == 0x6a90))
+ if ((vid == 0xffffffff || vid == 0x83847650) && feat == 0x6a90)
dev->audio_mode.ac97 = EM28XX_AC97_EM202;
else if ((vid >> 8) == 0x838476)
dev->audio_mode.ac97 = EM28XX_AC97_SIGMATEL;
@@ -638,10 +638,29 @@ int em28xx_capture_start(struct em28xx *dev, int start)
dev->chip_id == CHIP_ID_EM28174 ||
dev->chip_id == CHIP_ID_EM28178) {
/* The Transport Stream Enable Register moved in em2874 */
- rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE,
- start ?
- EM2874_TS1_CAPTURE_ENABLE : 0x00,
- EM2874_TS1_CAPTURE_ENABLE);
+ if (dev->dvb_xfer_bulk) {
+ /* Max Tx Size = 188 * 256 = 48128 - LCM(188,512) * 2 */
+ em28xx_write_reg(dev, (dev->ts == PRIMARY_TS) ?
+ EM2874_R5D_TS1_PKT_SIZE :
+ EM2874_R5E_TS2_PKT_SIZE,
+ 0xff);
+ } else {
+ /* ISOC Maximum Transfer Size = 188 * 5 */
+ em28xx_write_reg(dev, (dev->ts == PRIMARY_TS) ?
+ EM2874_R5D_TS1_PKT_SIZE :
+ EM2874_R5E_TS2_PKT_SIZE,
+ dev->dvb_max_pkt_size_isoc / 188);
+ }
+ if (dev->ts == PRIMARY_TS)
+ rc = em28xx_write_reg_bits(dev,
+ EM2874_R5F_TS_ENABLE,
+ start ? EM2874_TS1_CAPTURE_ENABLE : 0x00,
+ EM2874_TS1_CAPTURE_ENABLE);
+ else
+ rc = em28xx_write_reg_bits(dev,
+ EM2874_R5F_TS_ENABLE,
+ start ? EM2874_TS2_CAPTURE_ENABLE : 0x00,
+ EM2874_TS2_CAPTURE_ENABLE);
} else {
/* FIXME: which is the best order? */
/* video registers are sampled by VREF */
@@ -651,7 +670,7 @@ int em28xx_capture_start(struct em28xx *dev, int start)
return rc;
if (start) {
- if (dev->board.is_webcam)
+ if (dev->is_webcam)
rc = em28xx_write_reg(dev, 0x13, 0x0c);
/* Enable video capture */
@@ -670,7 +689,7 @@ int em28xx_capture_start(struct em28xx *dev, int start)
if (rc < 0)
return rc;
- msleep(6);
+ usleep_range(10000, 11000);
} else {
/* disable video capture */
rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x27);
@@ -691,7 +710,7 @@ int em28xx_capture_start(struct em28xx *dev, int start)
return rc;
}
-int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio)
+int em28xx_gpio_set(struct em28xx *dev, const struct em28xx_reg_seq *gpio)
{
int rc = 0;
@@ -704,7 +723,7 @@ int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio)
em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x67);
else
em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x37);
- msleep(6);
+ usleep_range(10000, 11000);
}
/* Send GPIO reset sequences specified at board entry */
@@ -748,9 +767,9 @@ int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode)
}
EXPORT_SYMBOL_GPL(em28xx_set_mode);
-/* ------------------------------------------------------------------
- URB control
- ------------------------------------------------------------------*/
+/*
+ *URB control
+ */
/*
* URB completion handler for isoc/bulk transfers
@@ -769,7 +788,7 @@ static void em28xx_irq_callback(struct urb *urb)
case -ESHUTDOWN:
return;
default: /* error */
- em28xx_isocdbg("urb completition error %d.\n", urb->status);
+ em28xx_isocdbg("urb completion error %d.\n", urb->status);
break;
}
@@ -800,11 +819,9 @@ void em28xx_uninit_usb_xfer(struct em28xx *dev, enum em28xx_mode mode)
{
struct urb *urb;
struct em28xx_usb_bufs *usb_bufs;
- struct usb_device *udev = interface_to_usbdev(dev->intf);
int i;
- em28xx_isocdbg("em28xx: called em28xx_uninit_usb_xfer in mode %d\n",
- mode);
+ em28xx_isocdbg("called %s in mode %d\n", __func__, mode);
if (mode == EM28XX_DIGITAL_MODE)
usb_bufs = &dev->usb_ctl.digital_bufs;
@@ -819,23 +836,16 @@ void em28xx_uninit_usb_xfer(struct em28xx *dev, enum em28xx_mode mode)
else
usb_unlink_urb(urb);
- if (usb_bufs->transfer_buffer[i]) {
- usb_free_coherent(udev,
- urb->transfer_buffer_length,
- usb_bufs->transfer_buffer[i],
- urb->transfer_dma);
- }
usb_free_urb(urb);
usb_bufs->urb[i] = NULL;
}
- usb_bufs->transfer_buffer[i] = NULL;
}
kfree(usb_bufs->urb);
- kfree(usb_bufs->transfer_buffer);
+ kfree(usb_bufs->buf);
usb_bufs->urb = NULL;
- usb_bufs->transfer_buffer = NULL;
+ usb_bufs->buf = NULL;
usb_bufs->num_bufs = 0;
em28xx_capture_start(dev, 0);
@@ -851,7 +861,7 @@ void em28xx_stop_urbs(struct em28xx *dev)
struct urb *urb;
struct em28xx_usb_bufs *isoc_bufs = &dev->usb_ctl.digital_bufs;
- em28xx_isocdbg("em28xx: called em28xx_stop_urbs\n");
+ em28xx_isocdbg("called %s\n", __func__);
for (i = 0; i < isoc_bufs->num_bufs; i++) {
urb = isoc_bufs->urb[i];
@@ -880,10 +890,12 @@ int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk,
int sb_size, pipe;
int j, k;
- em28xx_isocdbg("em28xx: called em28xx_alloc_isoc in mode %d\n", mode);
+ em28xx_isocdbg("em28xx: called %s in mode %d\n", __func__, mode);
- /* Check mode and if we have an endpoint for the selected
- transfer type, select buffer */
+ /*
+ * Check mode and if we have an endpoint for the selected
+ * transfer type, select buffer
+ */
if (mode == EM28XX_DIGITAL_MODE) {
if ((xfer_bulk && !dev->dvb_ep_bulk) ||
(!xfer_bulk && !dev->dvb_ep_isoc)) {
@@ -912,14 +924,13 @@ int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk,
usb_bufs->num_bufs = num_bufs;
- usb_bufs->urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL);
+ usb_bufs->urb = kcalloc(num_bufs, sizeof(void *), GFP_KERNEL);
if (!usb_bufs->urb)
return -ENOMEM;
- usb_bufs->transfer_buffer = kzalloc(sizeof(void *)*num_bufs,
- GFP_KERNEL);
- if (!usb_bufs->transfer_buffer) {
- kfree(usb_bufs->urb);
+ usb_bufs->buf = kcalloc(num_bufs, sizeof(void *), GFP_KERNEL);
+ if (!usb_bufs->buf) {
+ kfree(usb_bufs->buf);
return -ENOMEM;
}
@@ -942,37 +953,36 @@ int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk,
}
usb_bufs->urb[i] = urb;
- usb_bufs->transfer_buffer[i] = usb_alloc_coherent(udev,
- sb_size, GFP_KERNEL, &urb->transfer_dma);
- if (!usb_bufs->transfer_buffer[i]) {
- dev_err(&dev->intf->dev,
- "unable to allocate %i bytes for transfer buffer %i%s\n",
- sb_size, i,
- in_interrupt() ? " while in int" : "");
+ usb_bufs->buf[i] = kzalloc(sb_size, GFP_KERNEL);
+ if (!usb_bufs->buf[i]) {
em28xx_uninit_usb_xfer(dev, mode);
+
+ for (i--; i >= 0; i--)
+ kfree(usb_bufs->buf[i]);
+
+ kfree(usb_bufs->buf);
+ usb_bufs->buf = NULL;
+
return -ENOMEM;
}
- memset(usb_bufs->transfer_buffer[i], 0, sb_size);
+
+ urb->transfer_flags = URB_FREE_BUFFER;
if (xfer_bulk) { /* bulk */
pipe = usb_rcvbulkpipe(udev,
mode == EM28XX_ANALOG_MODE ?
dev->analog_ep_bulk :
dev->dvb_ep_bulk);
- usb_fill_bulk_urb(urb, udev, pipe,
- usb_bufs->transfer_buffer[i], sb_size,
- em28xx_irq_callback, dev);
- urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+ usb_fill_bulk_urb(urb, udev, pipe, usb_bufs->buf[i],
+ sb_size, em28xx_irq_callback, dev);
} else { /* isoc */
pipe = usb_rcvisocpipe(udev,
mode == EM28XX_ANALOG_MODE ?
dev->analog_ep_isoc :
dev->dvb_ep_isoc);
- usb_fill_int_urb(urb, udev, pipe,
- usb_bufs->transfer_buffer[i], sb_size,
- em28xx_irq_callback, dev, 1);
- urb->transfer_flags = URB_ISO_ASAP |
- URB_NO_TRANSFER_DMA_MAP;
+ usb_fill_int_urb(urb, udev, pipe, usb_bufs->buf[i],
+ sb_size, em28xx_irq_callback, dev, 1);
+ urb->transfer_flags |= URB_ISO_ASAP;
k = 0;
for (j = 0; j < usb_bufs->num_packets; j++) {
urb->iso_frame_desc[j].offset = k;
@@ -1005,8 +1015,7 @@ int em28xx_init_usb_xfer(struct em28xx *dev, enum em28xx_mode mode,
int rc;
int alloc;
- em28xx_isocdbg("em28xx: called em28xx_init_usb_xfer in mode %d\n",
- mode);
+ em28xx_isocdbg("em28xx: called %s in mode %d\n", __func__, mode);
dev->usb_ctl.urb_data_copy = urb_data_copy;
@@ -1077,7 +1086,11 @@ int em28xx_register_extension(struct em28xx_ops *ops)
mutex_lock(&em28xx_devlist_mutex);
list_add_tail(&ops->next, &em28xx_extension_devlist);
list_for_each_entry(dev, &em28xx_devlist, devlist) {
- ops->init(dev);
+ if (ops->init) {
+ ops->init(dev);
+ if (dev->dev_next)
+ ops->init(dev->dev_next);
+ }
}
mutex_unlock(&em28xx_devlist_mutex);
pr_info("em28xx: Registered (%s) extension\n", ops->name);
@@ -1091,7 +1104,11 @@ void em28xx_unregister_extension(struct em28xx_ops *ops)
mutex_lock(&em28xx_devlist_mutex);
list_for_each_entry(dev, &em28xx_devlist, devlist) {
- ops->fini(dev);
+ if (ops->fini) {
+ if (dev->dev_next)
+ ops->fini(dev->dev_next);
+ ops->fini(dev);
+ }
}
list_del(&ops->next);
mutex_unlock(&em28xx_devlist_mutex);
@@ -1106,8 +1123,11 @@ void em28xx_init_extension(struct em28xx *dev)
mutex_lock(&em28xx_devlist_mutex);
list_add_tail(&dev->devlist, &em28xx_devlist);
list_for_each_entry(ops, &em28xx_extension_devlist, next) {
- if (ops->init)
+ if (ops->init) {
ops->init(dev);
+ if (dev->dev_next)
+ ops->init(dev->dev_next);
+ }
}
mutex_unlock(&em28xx_devlist_mutex);
}
@@ -1118,8 +1138,11 @@ void em28xx_close_extension(struct em28xx *dev)
mutex_lock(&em28xx_devlist_mutex);
list_for_each_entry(ops, &em28xx_extension_devlist, next) {
- if (ops->fini)
+ if (ops->fini) {
+ if (dev->dev_next)
+ ops->fini(dev->dev_next);
ops->fini(dev);
+ }
}
list_del(&dev->devlist);
mutex_unlock(&em28xx_devlist_mutex);
@@ -1134,6 +1157,8 @@ int em28xx_suspend_extension(struct em28xx *dev)
list_for_each_entry(ops, &em28xx_extension_devlist, next) {
if (ops->suspend)
ops->suspend(dev);
+ if (dev->dev_next)
+ ops->suspend(dev->dev_next);
}
mutex_unlock(&em28xx_devlist_mutex);
return 0;
@@ -1148,6 +1173,8 @@ int em28xx_resume_extension(struct em28xx *dev)
list_for_each_entry(ops, &em28xx_extension_devlist, next) {
if (ops->resume)
ops->resume(dev);
+ if (dev->dev_next)
+ ops->resume(dev->dev_next);
}
mutex_unlock(&em28xx_devlist_mutex);
return 0;
diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c
index 8a81c94a8a27..a54cb8dc52c9 100644
--- a/drivers/media/usb/em28xx/em28xx-dvb.c
+++ b/drivers/media/usb/em28xx/em28xx-dvb.c
@@ -1,25 +1,25 @@
-/*
- DVB device driver for em28xx
-
- (c) 2008-2011 Mauro Carvalho Chehab <mchehab@infradead.org>
-
- (c) 2008 Devin Heitmueller <devin.heitmueller@gmail.com>
- - Fixes for the driver to properly work with HVR-950
- - Fixes for the driver to properly work with Pinnacle PCTV HD Pro Stick
- - Fixes for the driver to properly work with AMD ATI TV Wonder HD 600
-
- (c) 2008 Aidan Thornton <makosoft@googlemail.com>
-
- (c) 2012 Frank Schäfer <fschaefer.oss@googlemail.com>
-
- Based on cx88-dvb, saa7134-dvb and videobuf-dvb originally written by:
- (c) 2004, 2005 Chris Pascoe <c.pascoe@itee.uq.edu.au>
- (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
-
- 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.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// DVB device driver for em28xx
+//
+// (c) 2008-2011 Mauro Carvalho Chehab <mchehab@infradead.org>
+//
+// (c) 2008 Devin Heitmueller <devin.heitmueller@gmail.com>
+// - Fixes for the driver to properly work with HVR-950
+// - Fixes for the driver to properly work with Pinnacle PCTV HD Pro Stick
+// - Fixes for the driver to properly work with AMD ATI TV Wonder HD 600
+//
+// (c) 2008 Aidan Thornton <makosoft@googlemail.com>
+//
+// (c) 2012 Frank Schäfer <fschaefer.oss@googlemail.com>
+//
+// Based on cx88-dvb, saa7134-dvb and videobuf-dvb originally written by:
+// (c) 2004, 2005 Chris Pascoe <c.pascoe@itee.uq.edu.au>
+// (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+//
+// 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 of the License.
#include "em28xx.h"
@@ -64,7 +64,7 @@
#include "qm1d1c0042.h"
MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION(DRIVER_DESC " - digital TV interface");
MODULE_VERSION(EM28XX_VERSION);
@@ -96,7 +96,7 @@ struct em28xx_dvb {
struct dvb_net net;
/* Due to DRX-K - probably need changes */
- int (*gate_ctrl)(struct dvb_frontend *, int);
+ int (*gate_ctrl)(struct dvb_frontend *fe, int gate);
struct semaphore pll_mutex;
bool dont_attach_fe1;
int lna_gpio;
@@ -199,7 +199,6 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb)
int rc;
struct em28xx_i2c_bus *i2c_bus = dvb->adapter.priv;
struct em28xx *dev = i2c_bus->dev;
- struct usb_device *udev = interface_to_usbdev(dev->intf);
int dvb_max_packet_size, packet_multiplier, dvb_alt;
if (dev->dvb_xfer_bulk) {
@@ -218,7 +217,6 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb)
dvb_alt = dev->dvb_alt_isoc;
}
- usb_set_interface(udev, dev->ifnum, dvb_alt);
rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);
if (rc < 0)
return rc;
@@ -278,14 +276,13 @@ static int em28xx_stop_feed(struct dvb_demux_feed *feed)
mutex_lock(&dvb->lock);
dvb->nfeeds--;
- if (0 == dvb->nfeeds)
+ if (!dvb->nfeeds)
err = em28xx_stop_streaming(dvb);
mutex_unlock(&dvb->lock);
return err;
}
-
/* ------------------------------------------------------------------ */
static int em28xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire)
{
@@ -358,7 +355,7 @@ static struct s5h1409_config em28xx_s5h1409_with_xc3028 = {
.gpio = S5H1409_GPIO_OFF,
.inversion = S5H1409_INVERSION_OFF,
.status_mode = S5H1409_DEMODLOCKING,
- .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK
};
static struct tda18271_std_map kworld_a340_std_map = {
@@ -514,14 +511,15 @@ static void hauppauge_hvr930c_init(struct em28xx *dev)
em28xx_gpio_set(dev, hauppauge_hvr930c_init);
em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40);
- msleep(10);
+ usleep_range(10000, 11000);
em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44);
- msleep(10);
+ usleep_range(10000, 11000);
dev->i2c_client[dev->def_i2c_bus].addr = 0x82 >> 1;
for (i = 0; i < ARRAY_SIZE(regs); i++)
- i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], regs[i].r, regs[i].len);
+ i2c_master_send(&dev->i2c_client[dev->def_i2c_bus],
+ regs[i].r, regs[i].len);
em28xx_gpio_set(dev, hauppauge_hvr930c_end);
msleep(100);
@@ -530,8 +528,7 @@ static void hauppauge_hvr930c_init(struct em28xx *dev)
msleep(30);
em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x45);
- msleep(10);
-
+ usleep_range(10000, 11000);
}
static void terratec_h5_init(struct em28xx *dev)
@@ -571,14 +568,15 @@ static void terratec_h5_init(struct em28xx *dev)
em28xx_gpio_set(dev, terratec_h5_init);
em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40);
- msleep(10);
+ usleep_range(10000, 11000);
em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x45);
- msleep(10);
+ usleep_range(10000, 11000);
dev->i2c_client[dev->def_i2c_bus].addr = 0x82 >> 1;
for (i = 0; i < ARRAY_SIZE(regs); i++)
- i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], regs[i].r, regs[i].len);
+ i2c_master_send(&dev->i2c_client[dev->def_i2c_bus],
+ regs[i].r, regs[i].len);
em28xx_gpio_set(dev, terratec_h5_end);
};
@@ -624,14 +622,15 @@ static void terratec_htc_stick_init(struct em28xx *dev)
em28xx_gpio_set(dev, terratec_htc_stick_init);
em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40);
- msleep(10);
+ usleep_range(10000, 11000);
em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44);
- msleep(10);
+ usleep_range(10000, 11000);
dev->i2c_client[dev->def_i2c_bus].addr = 0x82 >> 1;
for (i = 0; i < ARRAY_SIZE(regs); i++)
- i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], regs[i].r, regs[i].len);
+ i2c_master_send(&dev->i2c_client[dev->def_i2c_bus],
+ regs[i].r, regs[i].len);
em28xx_gpio_set(dev, terratec_htc_stick_end);
};
@@ -682,14 +681,15 @@ static void terratec_htc_usb_xs_init(struct em28xx *dev)
em28xx_gpio_set(dev, terratec_htc_usb_xs_init);
em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40);
- msleep(10);
+ usleep_range(10000, 11000);
em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44);
- msleep(10);
+ usleep_range(10000, 11000);
dev->i2c_client[dev->def_i2c_bus].addr = 0x82 >> 1;
for (i = 0; i < ARRAY_SIZE(regs); i++)
- i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], regs[i].r, regs[i].len);
+ i2c_master_send(&dev->i2c_client[dev->def_i2c_bus],
+ regs[i].r, regs[i].len);
em28xx_gpio_set(dev, terratec_htc_usb_xs_end);
};
@@ -718,7 +718,8 @@ static void pctv_520e_init(struct em28xx *dev)
dev->i2c_client[dev->def_i2c_bus].addr = 0x82 >> 1; /* 0x41 */
for (i = 0; i < ARRAY_SIZE(regs); i++)
- i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], regs[i].r, regs[i].len);
+ i2c_master_send(&dev->i2c_client[dev->def_i2c_bus],
+ regs[i].r, regs[i].len);
};
static int em28xx_pctv_290e_set_lna(struct dvb_frontend *fe)
@@ -780,7 +781,7 @@ static int em28xx_mt352_terratec_xs_init(struct dvb_frontend *fe)
static u8 tuner_go[] = { TUNER_GO, 0x01};
mt352_write(fe, clock_config, sizeof(clock_config));
- udelay(200);
+ usleep_range(200, 250);
mt352_write(fe, reset, sizeof(reset));
mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
mt352_write(fe, agc_cfg, sizeof(agc_cfg));
@@ -840,8 +841,8 @@ static void px_bcud_init(struct em28xx *dev)
/* sleeping ISDB-T */
dev->dvb->i2c_client_demod->addr = 0x14;
for (i = 0; i < ARRAY_SIZE(regs1); i++)
- i2c_master_send(dev->dvb->i2c_client_demod, regs1[i].r,
- regs1[i].len);
+ i2c_master_send(dev->dvb->i2c_client_demod,
+ regs1[i].r, regs1[i].len);
/* sleeping ISDB-S */
dev->dvb->i2c_client_demod->addr = 0x15;
for (i = 0; i < ARRAY_SIZE(regs2); i++)
@@ -934,7 +935,7 @@ static struct lgdt3306a_config hauppauge_01595_lgdt3306a_config = {
/* ------------------------------------------------------------------ */
-static int em28xx_attach_xc3028(u8 addr, struct em28xx *dev)
+static noinline_for_stack int em28xx_attach_xc3028(u8 addr, struct em28xx *dev)
{
struct dvb_frontend *fe;
struct xc2028_config cfg;
@@ -1077,7 +1078,7 @@ static int em28xx_register_dvb(struct em28xx_dvb *dvb, struct module *module,
dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx);
/* If the analog part won't create RF connectors, DVB will do it */
- if (!dev->has_video || (dev->tuner_type == TUNER_ABSENT))
+ if (!dev->has_video || dev->tuner_type == TUNER_ABSENT)
create_rf_connector = true;
result = dvb_create_media_graph(&dvb->adapter, create_rf_connector);
@@ -1126,10 +1127,285 @@ static void em28xx_unregister_dvb(struct em28xx_dvb *dvb)
dvb_unregister_adapter(&dvb->adapter);
}
+static int em28174_dvb_init_pctv_460e(struct em28xx *dev)
+{
+ struct em28xx_dvb *dvb = dev->dvb;
+ struct tda10071_platform_data tda10071_pdata = {};
+ struct a8293_platform_data a8293_pdata = {};
+
+ /* attach demod + tuner combo */
+ tda10071_pdata.clk = 40444000; /* 40.444 MHz */
+ tda10071_pdata.i2c_wr_max = 64;
+ tda10071_pdata.ts_mode = TDA10071_TS_SERIAL;
+ tda10071_pdata.pll_multiplier = 20;
+ tda10071_pdata.tuner_i2c_addr = 0x14;
+
+ dvb->i2c_client_demod = dvb_module_probe("tda10071", "tda10071_cx24118",
+ &dev->i2c_adap[dev->def_i2c_bus],
+ 0x55, &tda10071_pdata);
+ if (!dvb->i2c_client_demod)
+ return -ENODEV;
+
+ dvb->fe[0] = tda10071_pdata.get_dvb_frontend(dvb->i2c_client_demod);
+
+ /* attach SEC */
+ a8293_pdata.dvb_frontend = dvb->fe[0];
+
+ dvb->i2c_client_sec = dvb_module_probe("a8293", NULL,
+ &dev->i2c_adap[dev->def_i2c_bus],
+ 0x08, &a8293_pdata);
+ if (!dvb->i2c_client_sec) {
+ dvb_module_release(dvb->i2c_client_demod);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int em28178_dvb_init_pctv_461e(struct em28xx *dev)
+{
+ struct em28xx_dvb *dvb = dev->dvb;
+ struct i2c_adapter *i2c_adapter;
+ struct m88ds3103_platform_data m88ds3103_pdata = {};
+ struct ts2020_config ts2020_config = {};
+ struct a8293_platform_data a8293_pdata = {};
+
+ /* attach demod */
+ m88ds3103_pdata.clk = 27000000;
+ m88ds3103_pdata.i2c_wr_max = 33;
+ m88ds3103_pdata.ts_mode = M88DS3103_TS_PARALLEL;
+ m88ds3103_pdata.ts_clk = 16000;
+ m88ds3103_pdata.ts_clk_pol = 1;
+ m88ds3103_pdata.agc = 0x99;
+
+ dvb->i2c_client_demod = dvb_module_probe("m88ds3103", NULL,
+ &dev->i2c_adap[dev->def_i2c_bus],
+ 0x68, &m88ds3103_pdata);
+ if (!dvb->i2c_client_demod)
+ return -ENODEV;
+
+ dvb->fe[0] = m88ds3103_pdata.get_dvb_frontend(dvb->i2c_client_demod);
+ i2c_adapter = m88ds3103_pdata.get_i2c_adapter(dvb->i2c_client_demod);
+
+ /* attach tuner */
+ ts2020_config.fe = dvb->fe[0];
+
+ dvb->i2c_client_tuner = dvb_module_probe("ts2020", "ts2022",
+ i2c_adapter,
+ 0x60, &ts2020_config);
+ if (!dvb->i2c_client_tuner) {
+ dvb_module_release(dvb->i2c_client_demod);
+ return -ENODEV;
+ }
+
+ /* delegate signal strength measurement to tuner */
+ dvb->fe[0]->ops.read_signal_strength =
+ dvb->fe[0]->ops.tuner_ops.get_rf_strength;
+
+ /* attach SEC */
+ a8293_pdata.dvb_frontend = dvb->fe[0];
+ dvb->i2c_client_sec = dvb_module_probe("a8293", NULL,
+ &dev->i2c_adap[dev->def_i2c_bus],
+ 0x08, &a8293_pdata);
+ if (!dvb->i2c_client_sec) {
+ dvb_module_release(dvb->i2c_client_tuner);
+ dvb_module_release(dvb->i2c_client_demod);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int em28178_dvb_init_pctv_292e(struct em28xx *dev)
+{
+ struct em28xx_dvb *dvb = dev->dvb;
+ struct i2c_adapter *adapter;
+ struct si2168_config si2168_config = {};
+ struct si2157_config si2157_config = {};
+
+ /* attach demod */
+ si2168_config.i2c_adapter = &adapter;
+ si2168_config.fe = &dvb->fe[0];
+ si2168_config.ts_mode = SI2168_TS_PARALLEL;
+ si2168_config.spectral_inversion = true;
+
+ dvb->i2c_client_demod = dvb_module_probe("si2168", NULL,
+ &dev->i2c_adap[dev->def_i2c_bus],
+ 0x64, &si2168_config);
+ if (!dvb->i2c_client_demod)
+ return -ENODEV;
+
+ /* attach tuner */
+ si2157_config.fe = dvb->fe[0];
+ si2157_config.if_port = 1;
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ si2157_config.mdev = dev->media_dev;
+#endif
+ dvb->i2c_client_tuner = dvb_module_probe("si2157", NULL,
+ adapter,
+ 0x60, &si2157_config);
+ if (!dvb->i2c_client_tuner) {
+ dvb_module_release(dvb->i2c_client_demod);
+ return -ENODEV;
+ }
+ dvb->fe[0]->ops.set_lna = em28xx_pctv_292e_set_lna;
+
+ return 0;
+}
+
+static int em28178_dvb_init_terratec_t2_stick_hd(struct em28xx *dev)
+{
+ struct em28xx_dvb *dvb = dev->dvb;
+ struct i2c_adapter *adapter;
+ struct si2168_config si2168_config = {};
+ struct si2157_config si2157_config = {};
+
+ /* attach demod */
+ si2168_config.i2c_adapter = &adapter;
+ si2168_config.fe = &dvb->fe[0];
+ si2168_config.ts_mode = SI2168_TS_PARALLEL;
+
+ dvb->i2c_client_demod = dvb_module_probe("si2168", NULL,
+ &dev->i2c_adap[dev->def_i2c_bus],
+ 0x64, &si2168_config);
+ if (!dvb->i2c_client_demod)
+ return -ENODEV;
+
+ /* attach tuner */
+ memset(&si2157_config, 0, sizeof(si2157_config));
+ si2157_config.fe = dvb->fe[0];
+ si2157_config.if_port = 0;
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ si2157_config.mdev = dev->media_dev;
+#endif
+ dvb->i2c_client_tuner = dvb_module_probe("si2157", "si2146",
+ adapter,
+ 0x60, &si2157_config);
+ if (!dvb->i2c_client_tuner) {
+ dvb_module_release(dvb->i2c_client_demod);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int em28178_dvb_init_plex_px_bcud(struct em28xx *dev)
+{
+ struct em28xx_dvb *dvb = dev->dvb;
+ struct tc90522_config tc90522_config = {};
+ struct qm1d1c0042_config qm1d1c0042_config = {};
+
+ /* attach demod */
+ dvb->i2c_client_demod = dvb_module_probe("tc90522", "tc90522sat",
+ &dev->i2c_adap[dev->def_i2c_bus],
+ 0x15, &tc90522_config);
+ if (!dvb->i2c_client_demod)
+ return -ENODEV;
+
+ /* attach tuner */
+ qm1d1c0042_config.fe = tc90522_config.fe;
+ qm1d1c0042_config.lpf = 1;
+
+ dvb->i2c_client_tuner = dvb_module_probe("qm1d1c0042", NULL,
+ tc90522_config.tuner_i2c,
+ 0x61, &qm1d1c0042_config);
+ if (!dvb->i2c_client_tuner) {
+ dvb_module_release(dvb->i2c_client_demod);
+ return -ENODEV;
+ }
+
+ dvb->fe[0] = tc90522_config.fe;
+ px_bcud_init(dev);
+
+ return 0;
+}
+
+static int em28174_dvb_init_hauppauge_wintv_dualhd_dvb(struct em28xx *dev)
+{
+ struct em28xx_dvb *dvb = dev->dvb;
+ struct i2c_adapter *adapter;
+ struct si2168_config si2168_config = {};
+ struct si2157_config si2157_config = {};
+ unsigned char addr;
+
+ /* attach demod */
+ si2168_config.i2c_adapter = &adapter;
+ si2168_config.fe = &dvb->fe[0];
+ si2168_config.ts_mode = SI2168_TS_SERIAL;
+ si2168_config.spectral_inversion = true;
+ addr = (dev->ts == PRIMARY_TS) ? 0x64 : 0x67;
+
+ dvb->i2c_client_demod = dvb_module_probe("si2168", NULL,
+ &dev->i2c_adap[dev->def_i2c_bus],
+ addr, &si2168_config);
+ if (!dvb->i2c_client_demod)
+ return -ENODEV;
+
+ /* attach tuner */
+ memset(&si2157_config, 0, sizeof(si2157_config));
+ si2157_config.fe = dvb->fe[0];
+ si2157_config.if_port = 1;
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ si2157_config.mdev = dev->media_dev;
+#endif
+ addr = (dev->ts == PRIMARY_TS) ? 0x60 : 0x63;
+
+ dvb->i2c_client_tuner = dvb_module_probe("si2157", NULL,
+ adapter,
+ addr, &si2157_config);
+ if (!dvb->i2c_client_tuner) {
+ dvb_module_release(dvb->i2c_client_demod);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int em28174_dvb_init_hauppauge_wintv_dualhd_01595(struct em28xx *dev)
+{
+ struct em28xx_dvb *dvb = dev->dvb;
+ struct i2c_adapter *adapter;
+ struct lgdt3306a_config lgdt3306a_config = {};
+ struct si2157_config si2157_config = {};
+ unsigned char addr;
+
+ /* attach demod */
+ lgdt3306a_config = hauppauge_01595_lgdt3306a_config;
+ lgdt3306a_config.fe = &dvb->fe[0];
+ lgdt3306a_config.i2c_adapter = &adapter;
+ addr = (dev->ts == PRIMARY_TS) ? 0x59 : 0x0e;
+
+ dvb->i2c_client_demod = dvb_module_probe("lgdt3306a", NULL,
+ &dev->i2c_adap[dev->def_i2c_bus],
+ addr, &lgdt3306a_config);
+ if (!dvb->i2c_client_demod)
+ return -ENODEV;
+
+ /* attach tuner */
+ si2157_config.fe = dvb->fe[0];
+ si2157_config.if_port = 1;
+ si2157_config.inversion = 1;
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ si2157_config.mdev = dev->media_dev;
+#endif
+ addr = (dev->ts == PRIMARY_TS) ? 0x60 : 0x62;
+
+ dvb->i2c_client_tuner = dvb_module_probe("si2157", NULL,
+ adapter,
+ 0x60, &si2157_config);
+ if (!dvb->i2c_client_tuner) {
+ dvb_module_release(dvb->i2c_client_demod);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int em28xx_dvb_init(struct em28xx *dev)
{
- int result = 0;
+ int result = 0, dvb_alt = 0;
struct em28xx_dvb *dvb;
+ struct usb_device *udev;
if (dev->is_audio_only) {
/* Shouldn't initialize IR for this interface */
@@ -1143,12 +1419,13 @@ static int em28xx_dvb_init(struct em28xx *dev)
dev_info(&dev->intf->dev, "Binding DVB extension\n");
- dvb = kzalloc(sizeof(struct em28xx_dvb), GFP_KERNEL);
+ dvb = kzalloc(sizeof(*dvb), GFP_KERNEL);
if (!dvb)
return -ENOMEM;
dev->dvb = dvb;
- dvb->fe[0] = dvb->fe[1] = NULL;
+ dvb->fe[0] = NULL;
+ dvb->fe[1] = NULL;
/* pre-allocate DVB usb transfer buffers */
if (dev->dvb_xfer_bulk) {
@@ -1178,7 +1455,8 @@ static int em28xx_dvb_init(struct em28xx *dev)
switch (dev->model) {
case EM2874_BOARD_LEADERSHIP_ISDBT:
dvb->fe[0] = dvb_attach(s921_attach,
- &sharp_isdbt, &dev->i2c_adap[dev->def_i2c_bus]);
+ &sharp_isdbt,
+ &dev->i2c_adap[dev->def_i2c_bus]);
if (!dvb->fe[0]) {
result = -EINVAL;
@@ -1191,8 +1469,8 @@ static int em28xx_dvb_init(struct em28xx *dev)
case EM2880_BOARD_PINNACLE_PCTV_HD_PRO:
case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600:
dvb->fe[0] = dvb_attach(lgdt330x_attach,
- &em2880_lgdt3303_dev,
- &dev->i2c_adap[dev->def_i2c_bus]);
+ &em2880_lgdt3303_dev,
+ &dev->i2c_adap[dev->def_i2c_bus]);
if (em28xx_attach_xc3028(0x61, dev) < 0) {
result = -EINVAL;
goto out_free;
@@ -1200,8 +1478,8 @@ static int em28xx_dvb_init(struct em28xx *dev)
break;
case EM2880_BOARD_KWORLD_DVB_310U:
dvb->fe[0] = dvb_attach(zl10353_attach,
- &em28xx_zl10353_with_xc3028,
- &dev->i2c_adap[dev->def_i2c_bus]);
+ &em28xx_zl10353_with_xc3028,
+ &dev->i2c_adap[dev->def_i2c_bus]);
if (em28xx_attach_xc3028(0x61, dev) < 0) {
result = -EINVAL;
goto out_free;
@@ -1211,8 +1489,8 @@ static int em28xx_dvb_init(struct em28xx *dev)
case EM2882_BOARD_TERRATEC_HYBRID_XS:
case EM2880_BOARD_EMPIRE_DUAL_TV:
dvb->fe[0] = dvb_attach(zl10353_attach,
- &em28xx_zl10353_xc3028_no_i2c_gate,
- &dev->i2c_adap[dev->def_i2c_bus]);
+ &em28xx_zl10353_xc3028_no_i2c_gate,
+ &dev->i2c_adap[dev->def_i2c_bus]);
if (em28xx_attach_xc3028(0x61, dev) < 0) {
result = -EINVAL;
goto out_free;
@@ -1223,16 +1501,17 @@ static int em28xx_dvb_init(struct em28xx *dev)
case EM2881_BOARD_PINNACLE_HYBRID_PRO:
case EM2882_BOARD_DIKOM_DK300:
case EM2882_BOARD_KWORLD_VS_DVBT:
+ /*
+ * Those boards could have either a zl10353 or a mt352.
+ * If the chip id isn't for zl10353, try mt352.
+ */
dvb->fe[0] = dvb_attach(zl10353_attach,
- &em28xx_zl10353_xc3028_no_i2c_gate,
- &dev->i2c_adap[dev->def_i2c_bus]);
- if (dvb->fe[0] == NULL) {
- /* This board could have either a zl10353 or a mt352.
- If the chip id isn't for zl10353, try mt352 */
+ &em28xx_zl10353_xc3028_no_i2c_gate,
+ &dev->i2c_adap[dev->def_i2c_bus]);
+ if (!dvb->fe[0])
dvb->fe[0] = dvb_attach(mt352_attach,
- &terratec_xs_mt352_cfg,
- &dev->i2c_adap[dev->def_i2c_bus]);
- }
+ &terratec_xs_mt352_cfg,
+ &dev->i2c_adap[dev->def_i2c_bus]);
if (em28xx_attach_xc3028(0x61, dev) < 0) {
result = -EINVAL;
@@ -1241,27 +1520,28 @@ static int em28xx_dvb_init(struct em28xx *dev)
break;
case EM2870_BOARD_TERRATEC_XS_MT2060:
dvb->fe[0] = dvb_attach(zl10353_attach,
- &em28xx_zl10353_no_i2c_gate_dev,
- &dev->i2c_adap[dev->def_i2c_bus]);
- if (dvb->fe[0] != NULL) {
+ &em28xx_zl10353_no_i2c_gate_dev,
+ &dev->i2c_adap[dev->def_i2c_bus]);
+ if (dvb->fe[0]) {
dvb_attach(mt2060_attach, dvb->fe[0],
- &dev->i2c_adap[dev->def_i2c_bus],
- &em28xx_mt2060_config, 1220);
+ &dev->i2c_adap[dev->def_i2c_bus],
+ &em28xx_mt2060_config, 1220);
}
break;
case EM2870_BOARD_KWORLD_355U:
dvb->fe[0] = dvb_attach(zl10353_attach,
- &em28xx_zl10353_no_i2c_gate_dev,
- &dev->i2c_adap[dev->def_i2c_bus]);
- if (dvb->fe[0] != NULL)
+ &em28xx_zl10353_no_i2c_gate_dev,
+ &dev->i2c_adap[dev->def_i2c_bus]);
+ if (dvb->fe[0])
dvb_attach(qt1010_attach, dvb->fe[0],
- &dev->i2c_adap[dev->def_i2c_bus], &em28xx_qt1010_config);
+ &dev->i2c_adap[dev->def_i2c_bus],
+ &em28xx_qt1010_config);
break;
case EM2883_BOARD_KWORLD_HYBRID_330U:
case EM2882_BOARD_EVGA_INDTUBE:
dvb->fe[0] = dvb_attach(s5h1409_attach,
- &em28xx_s5h1409_with_xc3028,
- &dev->i2c_adap[dev->def_i2c_bus]);
+ &em28xx_s5h1409_with_xc3028,
+ &dev->i2c_adap[dev->def_i2c_bus]);
if (em28xx_attach_xc3028(0x61, dev) < 0) {
result = -EINVAL;
goto out_free;
@@ -1269,9 +1549,9 @@ static int em28xx_dvb_init(struct em28xx *dev)
break;
case EM2882_BOARD_KWORLD_ATSC_315U:
dvb->fe[0] = dvb_attach(lgdt330x_attach,
- &em2880_lgdt3303_dev,
- &dev->i2c_adap[dev->def_i2c_bus]);
- if (dvb->fe[0] != NULL) {
+ &em2880_lgdt3303_dev,
+ &dev->i2c_adap[dev->def_i2c_bus]);
+ if (dvb->fe[0]) {
if (!dvb_attach(simple_tuner_attach, dvb->fe[0],
&dev->i2c_adap[dev->def_i2c_bus],
0x61, TUNER_THOMSON_DTT761X)) {
@@ -1293,8 +1573,9 @@ static int em28xx_dvb_init(struct em28xx *dev)
case EM2870_BOARD_REDDO_DVB_C_USB_BOX:
/* Philips CU1216L NIM (Philips TDA10023 + Infineon TUA6034) */
dvb->fe[0] = dvb_attach(tda10023_attach,
- &em28xx_tda10023_config,
- &dev->i2c_adap[dev->def_i2c_bus], 0x48);
+ &em28xx_tda10023_config,
+ &dev->i2c_adap[dev->def_i2c_bus],
+ 0x48);
if (dvb->fe[0]) {
if (!dvb_attach(simple_tuner_attach, dvb->fe[0],
&dev->i2c_adap[dev->def_i2c_bus],
@@ -1306,18 +1587,18 @@ static int em28xx_dvb_init(struct em28xx *dev)
break;
case EM2870_BOARD_KWORLD_A340:
dvb->fe[0] = dvb_attach(lgdt3305_attach,
- &em2870_lgdt3304_dev,
- &dev->i2c_adap[dev->def_i2c_bus]);
+ &em2870_lgdt3304_dev,
+ &dev->i2c_adap[dev->def_i2c_bus]);
if (!dvb->fe[0]) {
result = -EINVAL;
goto out_free;
}
if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
&dev->i2c_adap[dev->def_i2c_bus],
- &kworld_a340_config)) {
- dvb_frontend_detach(dvb->fe[0]);
- result = -EINVAL;
- goto out_free;
+ &kworld_a340_config)) {
+ dvb_frontend_detach(dvb->fe[0]);
+ result = -EINVAL;
+ goto out_free;
}
break;
case EM28174_BOARD_PCTV_290E:
@@ -1335,7 +1616,6 @@ static int em28xx_dvb_init(struct em28xx *dev)
0x60,
&dev->i2c_adap[dev->def_i2c_bus],
&em28xx_cxd2820r_tda18271_config)) {
-
dvb_frontend_detach(dvb->fe[0]);
result = -EINVAL;
goto out_free;
@@ -1360,12 +1640,13 @@ static int em28xx_dvb_init(struct em28xx *dev)
break;
case EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C:
{
- struct xc5000_config cfg;
+ struct xc5000_config cfg = {};
hauppauge_hvr930c_init(dev);
dvb->fe[0] = dvb_attach(drxk_attach,
- &hauppauge_930c_drxk, &dev->i2c_adap[dev->def_i2c_bus]);
+ &hauppauge_930c_drxk,
+ &dev->i2c_adap[dev->def_i2c_bus]);
if (!dvb->fe[0]) {
result = -EINVAL;
goto out_free;
@@ -1377,14 +1658,13 @@ static int em28xx_dvb_init(struct em28xx *dev)
dvb->fe[0]->ops.i2c_gate_ctrl = drxk_gate_ctrl;
/* Attach xc5000 */
- memset(&cfg, 0, sizeof(cfg));
cfg.i2c_address = 0x61;
cfg.if_khz = 4000;
if (dvb->fe[0]->ops.i2c_gate_ctrl)
dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 1);
- if (!dvb_attach(xc5000_attach, dvb->fe[0], &dev->i2c_adap[dev->def_i2c_bus],
- &cfg)) {
+ if (!dvb_attach(xc5000_attach, dvb->fe[0],
+ &dev->i2c_adap[dev->def_i2c_bus], &cfg)) {
result = -EINVAL;
goto out_free;
}
@@ -1396,7 +1676,8 @@ static int em28xx_dvb_init(struct em28xx *dev)
case EM2884_BOARD_TERRATEC_H5:
terratec_h5_init(dev);
- dvb->fe[0] = dvb_attach(drxk_attach, &terratec_h5_drxk, &dev->i2c_adap[dev->def_i2c_bus]);
+ dvb->fe[0] = dvb_attach(drxk_attach, &terratec_h5_drxk,
+ &dev->i2c_adap[dev->def_i2c_bus]);
if (!dvb->fe[0]) {
result = -EINVAL;
goto out_free;
@@ -1410,7 +1691,8 @@ static int em28xx_dvb_init(struct em28xx *dev)
/* Attach tda18271 to DVB-C frontend */
if (dvb->fe[0]->ops.i2c_gate_ctrl)
dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 1);
- if (!dvb_attach(tda18271c2dd_attach, dvb->fe[0], &dev->i2c_adap[dev->def_i2c_bus], 0x60)) {
+ if (!dvb_attach(tda18271c2dd_attach, dvb->fe[0],
+ &dev->i2c_adap[dev->def_i2c_bus], 0x60)) {
result = -EINVAL;
goto out_free;
}
@@ -1420,72 +1702,23 @@ static int em28xx_dvb_init(struct em28xx *dev)
break;
case EM2884_BOARD_C3TECH_DIGITAL_DUO:
dvb->fe[0] = dvb_attach(mb86a20s_attach,
- &c3tech_duo_mb86a20s_config,
- &dev->i2c_adap[dev->def_i2c_bus]);
- if (dvb->fe[0] != NULL)
+ &c3tech_duo_mb86a20s_config,
+ &dev->i2c_adap[dev->def_i2c_bus]);
+ if (dvb->fe[0])
dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
&dev->i2c_adap[dev->def_i2c_bus],
&c3tech_duo_tda18271_config);
break;
- case EM28174_BOARD_PCTV_460E: {
- struct i2c_client *client;
- struct i2c_board_info board_info;
- struct tda10071_platform_data tda10071_pdata = {};
- struct a8293_platform_data a8293_pdata = {};
-
- /* attach demod + tuner combo */
- tda10071_pdata.clk = 40444000, /* 40.444 MHz */
- tda10071_pdata.i2c_wr_max = 64,
- tda10071_pdata.ts_mode = TDA10071_TS_SERIAL,
- tda10071_pdata.pll_multiplier = 20,
- tda10071_pdata.tuner_i2c_addr = 0x14,
- memset(&board_info, 0, sizeof(board_info));
- strlcpy(board_info.type, "tda10071_cx24118", I2C_NAME_SIZE);
- board_info.addr = 0x55;
- board_info.platform_data = &tda10071_pdata;
- request_module("tda10071");
- client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &board_info);
- if (client == NULL || client->dev.driver == NULL) {
- result = -ENODEV;
+ case EM28174_BOARD_PCTV_460E:
+ result = em28174_dvb_init_pctv_460e(dev);
+ if (result)
goto out_free;
- }
- if (!try_module_get(client->dev.driver->owner)) {
- i2c_unregister_device(client);
- result = -ENODEV;
- goto out_free;
- }
- dvb->fe[0] = tda10071_pdata.get_dvb_frontend(client);
- dvb->i2c_client_demod = client;
-
- /* attach SEC */
- a8293_pdata.dvb_frontend = dvb->fe[0];
- memset(&board_info, 0, sizeof(board_info));
- strlcpy(board_info.type, "a8293", I2C_NAME_SIZE);
- board_info.addr = 0x08;
- board_info.platform_data = &a8293_pdata;
- request_module("a8293");
- client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &board_info);
- if (client == NULL || client->dev.driver == NULL) {
- module_put(dvb->i2c_client_demod->dev.driver->owner);
- i2c_unregister_device(dvb->i2c_client_demod);
- result = -ENODEV;
- goto out_free;
- }
- if (!try_module_get(client->dev.driver->owner)) {
- i2c_unregister_device(client);
- module_put(dvb->i2c_client_demod->dev.driver->owner);
- i2c_unregister_device(dvb->i2c_client_demod);
- result = -ENODEV;
- goto out_free;
- }
- dvb->i2c_client_sec = client;
break;
- }
case EM2874_BOARD_DELOCK_61959:
case EM2874_BOARD_MAXMEDIA_UB425_TC:
/* attach demodulator */
dvb->fe[0] = dvb_attach(drxk_attach, &maxmedia_ub425_tc_drxk,
- &dev->i2c_adap[dev->def_i2c_bus]);
+ &dev->i2c_adap[dev->def_i2c_bus]);
if (dvb->fe[0]) {
/* disable I2C-gate */
@@ -1507,7 +1740,7 @@ static int em28xx_dvb_init(struct em28xx *dev)
/* attach demodulator */
dvb->fe[0] = dvb_attach(drxk_attach, &pctv_520e_drxk,
- &dev->i2c_adap[dev->def_i2c_bus]);
+ &dev->i2c_adap[dev->def_i2c_bus]);
if (dvb->fe[0]) {
/* attach tuner */
@@ -1579,13 +1812,7 @@ static int em28xx_dvb_init(struct em28xx *dev)
break;
case EM2874_BOARD_KWORLD_UB435Q_V3:
{
- struct i2c_client *client;
struct i2c_adapter *adapter = &dev->i2c_adap[dev->def_i2c_bus];
- struct i2c_board_info board_info = {
- .type = "tda18212",
- .addr = 0x60,
- .platform_data = &kworld_ub435q_v3_config,
- };
dvb->fe[0] = dvb_attach(lgdt3305_attach,
&em2874_lgdt3305_nogate_dev,
@@ -1597,28 +1824,23 @@ static int em28xx_dvb_init(struct em28xx *dev)
/* attach tuner */
kworld_ub435q_v3_config.fe = dvb->fe[0];
- request_module("tda18212");
- client = i2c_new_device(adapter, &board_info);
- if (client == NULL || client->dev.driver == NULL) {
- dvb_frontend_detach(dvb->fe[0]);
- result = -ENODEV;
- goto out_free;
- }
- if (!try_module_get(client->dev.driver->owner)) {
- i2c_unregister_device(client);
+ dvb->i2c_client_tuner = dvb_module_probe("tda18212", NULL,
+ adapter, 0x60,
+ &kworld_ub435q_v3_config);
+ if (!dvb->i2c_client_tuner) {
dvb_frontend_detach(dvb->fe[0]);
result = -ENODEV;
goto out_free;
}
-
- dvb->i2c_client_tuner = client;
break;
}
case EM2874_BOARD_PCTV_HD_MINI_80E:
- dvb->fe[0] = dvb_attach(drx39xxj_attach, &dev->i2c_adap[dev->def_i2c_bus]);
- if (dvb->fe[0] != NULL) {
- dvb->fe[0] = dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
+ dvb->fe[0] = dvb_attach(drx39xxj_attach,
+ &dev->i2c_adap[dev->def_i2c_bus]);
+ if (dvb->fe[0]) {
+ dvb->fe[0] = dvb_attach(tda18271_attach, dvb->fe[0],
+ 0x60,
&dev->i2c_adap[dev->def_i2c_bus],
&pinnacle_80e_dvb_config);
if (!dvb->fe[0]) {
@@ -1627,410 +1849,42 @@ static int em28xx_dvb_init(struct em28xx *dev)
}
}
break;
- case EM28178_BOARD_PCTV_461E: {
- struct i2c_client *client;
- struct i2c_adapter *i2c_adapter;
- struct i2c_board_info board_info;
- struct m88ds3103_platform_data m88ds3103_pdata = {};
- struct ts2020_config ts2020_config = {};
- struct a8293_platform_data a8293_pdata = {};
-
- /* attach demod */
- m88ds3103_pdata.clk = 27000000;
- m88ds3103_pdata.i2c_wr_max = 33;
- m88ds3103_pdata.ts_mode = M88DS3103_TS_PARALLEL;
- m88ds3103_pdata.ts_clk = 16000;
- m88ds3103_pdata.ts_clk_pol = 1;
- m88ds3103_pdata.agc = 0x99;
- memset(&board_info, 0, sizeof(board_info));
- strlcpy(board_info.type, "m88ds3103", I2C_NAME_SIZE);
- board_info.addr = 0x68;
- board_info.platform_data = &m88ds3103_pdata;
- request_module("m88ds3103");
- client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &board_info);
- if (client == NULL || client->dev.driver == NULL) {
- result = -ENODEV;
- goto out_free;
- }
- if (!try_module_get(client->dev.driver->owner)) {
- i2c_unregister_device(client);
- result = -ENODEV;
- goto out_free;
- }
- dvb->fe[0] = m88ds3103_pdata.get_dvb_frontend(client);
- i2c_adapter = m88ds3103_pdata.get_i2c_adapter(client);
- dvb->i2c_client_demod = client;
-
- /* attach tuner */
- ts2020_config.fe = dvb->fe[0];
- memset(&board_info, 0, sizeof(board_info));
- strlcpy(board_info.type, "ts2022", I2C_NAME_SIZE);
- board_info.addr = 0x60;
- board_info.platform_data = &ts2020_config;
- request_module("ts2020");
- client = i2c_new_device(i2c_adapter, &board_info);
- if (client == NULL || client->dev.driver == NULL) {
- module_put(dvb->i2c_client_demod->dev.driver->owner);
- i2c_unregister_device(dvb->i2c_client_demod);
- result = -ENODEV;
- goto out_free;
- }
- if (!try_module_get(client->dev.driver->owner)) {
- i2c_unregister_device(client);
- module_put(dvb->i2c_client_demod->dev.driver->owner);
- i2c_unregister_device(dvb->i2c_client_demod);
- result = -ENODEV;
- goto out_free;
- }
- dvb->i2c_client_tuner = client;
- /* delegate signal strength measurement to tuner */
- dvb->fe[0]->ops.read_signal_strength =
- dvb->fe[0]->ops.tuner_ops.get_rf_strength;
-
- /* attach SEC */
- a8293_pdata.dvb_frontend = dvb->fe[0];
- memset(&board_info, 0, sizeof(board_info));
- strlcpy(board_info.type, "a8293", I2C_NAME_SIZE);
- board_info.addr = 0x08;
- board_info.platform_data = &a8293_pdata;
- request_module("a8293");
- client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &board_info);
- if (client == NULL || client->dev.driver == NULL) {
- module_put(dvb->i2c_client_tuner->dev.driver->owner);
- i2c_unregister_device(dvb->i2c_client_tuner);
- module_put(dvb->i2c_client_demod->dev.driver->owner);
- i2c_unregister_device(dvb->i2c_client_demod);
- result = -ENODEV;
- goto out_free;
- }
- if (!try_module_get(client->dev.driver->owner)) {
- i2c_unregister_device(client);
- module_put(dvb->i2c_client_tuner->dev.driver->owner);
- i2c_unregister_device(dvb->i2c_client_tuner);
- module_put(dvb->i2c_client_demod->dev.driver->owner);
- i2c_unregister_device(dvb->i2c_client_demod);
- result = -ENODEV;
+ case EM28178_BOARD_PCTV_461E:
+ result = em28178_dvb_init_pctv_461e(dev);
+ if (result)
goto out_free;
- }
- dvb->i2c_client_sec = client;
break;
- }
case EM28178_BOARD_PCTV_292E:
- {
- struct i2c_adapter *adapter;
- struct i2c_client *client;
- struct i2c_board_info info;
- struct si2168_config si2168_config;
- struct si2157_config si2157_config;
-
- /* attach demod */
- memset(&si2168_config, 0, sizeof(si2168_config));
- si2168_config.i2c_adapter = &adapter;
- si2168_config.fe = &dvb->fe[0];
- si2168_config.ts_mode = SI2168_TS_PARALLEL;
- memset(&info, 0, sizeof(struct i2c_board_info));
- strlcpy(info.type, "si2168", I2C_NAME_SIZE);
- info.addr = 0x64;
- info.platform_data = &si2168_config;
- request_module(info.type);
- client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &info);
- if (client == NULL || client->dev.driver == NULL) {
- result = -ENODEV;
- goto out_free;
- }
-
- if (!try_module_get(client->dev.driver->owner)) {
- i2c_unregister_device(client);
- result = -ENODEV;
- goto out_free;
- }
-
- dvb->i2c_client_demod = client;
-
- /* attach tuner */
- memset(&si2157_config, 0, sizeof(si2157_config));
- si2157_config.fe = dvb->fe[0];
- si2157_config.if_port = 1;
-#ifdef CONFIG_MEDIA_CONTROLLER_DVB
- si2157_config.mdev = dev->media_dev;
-#endif
- memset(&info, 0, sizeof(struct i2c_board_info));
- strlcpy(info.type, "si2157", I2C_NAME_SIZE);
- info.addr = 0x60;
- info.platform_data = &si2157_config;
- request_module(info.type);
- client = i2c_new_device(adapter, &info);
- if (client == NULL || client->dev.driver == NULL) {
- module_put(dvb->i2c_client_demod->dev.driver->owner);
- i2c_unregister_device(dvb->i2c_client_demod);
- result = -ENODEV;
- goto out_free;
- }
-
- if (!try_module_get(client->dev.driver->owner)) {
- i2c_unregister_device(client);
- module_put(dvb->i2c_client_demod->dev.driver->owner);
- i2c_unregister_device(dvb->i2c_client_demod);
- result = -ENODEV;
- goto out_free;
- }
-
- dvb->i2c_client_tuner = client;
- dvb->fe[0]->ops.set_lna = em28xx_pctv_292e_set_lna;
- }
+ result = em28178_dvb_init_pctv_292e(dev);
+ if (result)
+ goto out_free;
break;
case EM28178_BOARD_TERRATEC_T2_STICK_HD:
- {
- struct i2c_adapter *adapter;
- struct i2c_client *client;
- struct i2c_board_info info;
- struct si2168_config si2168_config;
- struct si2157_config si2157_config;
-
- /* attach demod */
- memset(&si2168_config, 0, sizeof(si2168_config));
- si2168_config.i2c_adapter = &adapter;
- si2168_config.fe = &dvb->fe[0];
- si2168_config.ts_mode = SI2168_TS_PARALLEL;
- memset(&info, 0, sizeof(struct i2c_board_info));
- strlcpy(info.type, "si2168", I2C_NAME_SIZE);
- info.addr = 0x64;
- info.platform_data = &si2168_config;
- request_module(info.type);
- client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &info);
- if (client == NULL || client->dev.driver == NULL) {
- result = -ENODEV;
- goto out_free;
- }
-
- if (!try_module_get(client->dev.driver->owner)) {
- i2c_unregister_device(client);
- result = -ENODEV;
- goto out_free;
- }
-
- dvb->i2c_client_demod = client;
-
- /* attach tuner */
- memset(&si2157_config, 0, sizeof(si2157_config));
- si2157_config.fe = dvb->fe[0];
- si2157_config.if_port = 0;
-#ifdef CONFIG_MEDIA_CONTROLLER_DVB
- si2157_config.mdev = dev->media_dev;
-#endif
- memset(&info, 0, sizeof(struct i2c_board_info));
- strlcpy(info.type, "si2146", I2C_NAME_SIZE);
- info.addr = 0x60;
- info.platform_data = &si2157_config;
- request_module("si2157");
- client = i2c_new_device(adapter, &info);
- if (client == NULL || client->dev.driver == NULL) {
- module_put(dvb->i2c_client_demod->dev.driver->owner);
- i2c_unregister_device(dvb->i2c_client_demod);
- result = -ENODEV;
- goto out_free;
- }
-
- if (!try_module_get(client->dev.driver->owner)) {
- i2c_unregister_device(client);
- module_put(dvb->i2c_client_demod->dev.driver->owner);
- i2c_unregister_device(dvb->i2c_client_demod);
- result = -ENODEV;
- goto out_free;
- }
-
- dvb->i2c_client_tuner = client;
- }
+ result = em28178_dvb_init_terratec_t2_stick_hd(dev);
+ if (result)
+ goto out_free;
break;
-
case EM28178_BOARD_PLEX_PX_BCUD:
- {
- struct i2c_client *client;
- struct i2c_board_info info;
- struct tc90522_config tc90522_config;
- struct qm1d1c0042_config qm1d1c0042_config;
-
- /* attach demod */
- memset(&tc90522_config, 0, sizeof(tc90522_config));
- memset(&info, 0, sizeof(struct i2c_board_info));
- strlcpy(info.type, "tc90522sat", I2C_NAME_SIZE);
- info.addr = 0x15;
- info.platform_data = &tc90522_config;
- request_module("tc90522");
- client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &info);
- if (client == NULL || client->dev.driver == NULL) {
- result = -ENODEV;
- goto out_free;
- }
- dvb->i2c_client_demod = client;
- if (!try_module_get(client->dev.driver->owner)) {
- i2c_unregister_device(client);
- result = -ENODEV;
- goto out_free;
- }
-
- /* attach tuner */
- memset(&qm1d1c0042_config, 0,
- sizeof(qm1d1c0042_config));
- qm1d1c0042_config.fe = tc90522_config.fe;
- qm1d1c0042_config.lpf = 1;
- memset(&info, 0, sizeof(struct i2c_board_info));
- strlcpy(info.type, "qm1d1c0042", I2C_NAME_SIZE);
- info.addr = 0x61;
- info.platform_data = &qm1d1c0042_config;
- request_module(info.type);
- client = i2c_new_device(tc90522_config.tuner_i2c,
- &info);
- if (client == NULL || client->dev.driver == NULL) {
- module_put(dvb->i2c_client_demod->dev.driver->owner);
- i2c_unregister_device(dvb->i2c_client_demod);
- result = -ENODEV;
- goto out_free;
- }
- dvb->i2c_client_tuner = client;
- if (!try_module_get(client->dev.driver->owner)) {
- i2c_unregister_device(client);
- module_put(dvb->i2c_client_demod->dev.driver->owner);
- i2c_unregister_device(dvb->i2c_client_demod);
- result = -ENODEV;
- goto out_free;
- }
- dvb->fe[0] = tc90522_config.fe;
- px_bcud_init(dev);
- }
+ result = em28178_dvb_init_plex_px_bcud(dev);
+ if (result)
+ goto out_free;
break;
case EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB:
- {
- struct i2c_adapter *adapter;
- struct i2c_client *client;
- struct i2c_board_info info;
- struct si2168_config si2168_config;
- struct si2157_config si2157_config;
-
- /* attach demod */
- memset(&si2168_config, 0, sizeof(si2168_config));
- si2168_config.i2c_adapter = &adapter;
- si2168_config.fe = &dvb->fe[0];
- si2168_config.ts_mode = SI2168_TS_SERIAL;
- memset(&info, 0, sizeof(struct i2c_board_info));
- strlcpy(info.type, "si2168", I2C_NAME_SIZE);
- info.addr = 0x64;
- info.platform_data = &si2168_config;
- request_module(info.type);
- client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &info);
- if (client == NULL || client->dev.driver == NULL) {
- result = -ENODEV;
- goto out_free;
- }
-
- if (!try_module_get(client->dev.driver->owner)) {
- i2c_unregister_device(client);
- result = -ENODEV;
- goto out_free;
- }
-
- dvb->i2c_client_demod = client;
-
- /* attach tuner */
- memset(&si2157_config, 0, sizeof(si2157_config));
- si2157_config.fe = dvb->fe[0];
- si2157_config.if_port = 1;
-#ifdef CONFIG_MEDIA_CONTROLLER_DVB
- si2157_config.mdev = dev->media_dev;
-#endif
- memset(&info, 0, sizeof(struct i2c_board_info));
- strlcpy(info.type, "si2157", I2C_NAME_SIZE);
- info.addr = 0x60;
- info.platform_data = &si2157_config;
- request_module(info.type);
- client = i2c_new_device(adapter, &info);
- if (client == NULL || client->dev.driver == NULL) {
- module_put(dvb->i2c_client_demod->dev.driver->owner);
- i2c_unregister_device(dvb->i2c_client_demod);
- result = -ENODEV;
- goto out_free;
- }
-
- if (!try_module_get(client->dev.driver->owner)) {
- i2c_unregister_device(client);
- module_put(dvb->i2c_client_demod->dev.driver->owner);
- i2c_unregister_device(dvb->i2c_client_demod);
- result = -ENODEV;
- goto out_free;
- }
-
- dvb->i2c_client_tuner = client;
-
- }
+ result = em28174_dvb_init_hauppauge_wintv_dualhd_dvb(dev);
+ if (result)
+ goto out_free;
break;
case EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595:
- {
- struct i2c_adapter *adapter;
- struct i2c_client *client;
- struct i2c_board_info info = {};
- struct lgdt3306a_config lgdt3306a_config;
- struct si2157_config si2157_config = {};
-
- /* attach demod */
- lgdt3306a_config = hauppauge_01595_lgdt3306a_config;
- lgdt3306a_config.fe = &dvb->fe[0];
- lgdt3306a_config.i2c_adapter = &adapter;
- strlcpy(info.type, "lgdt3306a", sizeof(info.type));
- info.addr = 0x59;
- info.platform_data = &lgdt3306a_config;
- request_module(info.type);
- client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus],
- &info);
- if (client == NULL || client->dev.driver == NULL) {
- result = -ENODEV;
- goto out_free;
- }
-
- if (!try_module_get(client->dev.driver->owner)) {
- i2c_unregister_device(client);
- result = -ENODEV;
- goto out_free;
- }
-
- dvb->i2c_client_demod = client;
-
- /* attach tuner */
- si2157_config.fe = dvb->fe[0];
- si2157_config.if_port = 1;
- si2157_config.inversion = 1;
-#ifdef CONFIG_MEDIA_CONTROLLER_DVB
- si2157_config.mdev = dev->media_dev;
-#endif
- memset(&info, 0, sizeof(struct i2c_board_info));
- strlcpy(info.type, "si2157", sizeof(info.type));
- info.addr = 0x60;
- info.platform_data = &si2157_config;
- request_module(info.type);
-
- client = i2c_new_device(adapter, &info);
- if (client == NULL || client->dev.driver == NULL) {
- module_put(dvb->i2c_client_demod->dev.driver->owner);
- i2c_unregister_device(dvb->i2c_client_demod);
- result = -ENODEV;
- goto out_free;
- }
- if (!try_module_get(client->dev.driver->owner)) {
- i2c_unregister_device(client);
- module_put(dvb->i2c_client_demod->dev.driver->owner);
- i2c_unregister_device(dvb->i2c_client_demod);
- result = -ENODEV;
- goto out_free;
- }
-
- dvb->i2c_client_tuner = client;
- }
+ result = em28174_dvb_init_hauppauge_wintv_dualhd_01595(dev);
+ if (result)
+ goto out_free;
break;
default:
dev_err(&dev->intf->dev,
"The frontend of your DVB/ATSC card isn't supported yet\n");
break;
}
- if (NULL == dvb->fe[0]) {
+ if (!dvb->fe[0]) {
dev_err(&dev->intf->dev, "frontend initialization failed\n");
result = -EINVAL;
goto out_free;
@@ -2046,6 +1900,14 @@ static int em28xx_dvb_init(struct em28xx *dev)
if (result < 0)
goto out_free;
+ if (dev->dvb_xfer_bulk) {
+ dvb_alt = 0;
+ } else { /* isoc */
+ dvb_alt = dev->dvb_alt_isoc;
+ }
+
+ udev = interface_to_usbdev(dev->intf);
+ usb_set_interface(udev, dev->ifnum, dvb_alt);
dev_info(&dev->intf->dev, "DVB extension successfully initialized\n");
kref_get(&dev->ref);
@@ -2071,7 +1933,6 @@ static inline void prevent_sleep(struct dvb_frontend_ops *ops)
static int em28xx_dvb_fini(struct em28xx *dev)
{
struct em28xx_dvb *dvb;
- struct i2c_client *client;
if (dev->is_audio_only) {
/* Shouldn't initialize IR for this interface */
@@ -2093,8 +1954,10 @@ static int em28xx_dvb_fini(struct em28xx *dev)
em28xx_uninit_usb_xfer(dev, EM28XX_DIGITAL_MODE);
if (dev->disconnected) {
- /* We cannot tell the device to sleep
- * once it has been unplugged. */
+ /*
+ * We cannot tell the device to sleep
+ * once it has been unplugged.
+ */
if (dvb->fe[0]) {
prevent_sleep(&dvb->fe[0]->ops);
dvb->fe[0]->exit = DVB_FE_DEVICE_REMOVED;
@@ -2107,26 +1970,10 @@ static int em28xx_dvb_fini(struct em28xx *dev)
em28xx_unregister_dvb(dvb);
- /* remove I2C SEC */
- client = dvb->i2c_client_sec;
- if (client) {
- module_put(client->dev.driver->owner);
- i2c_unregister_device(client);
- }
-
- /* remove I2C tuner */
- client = dvb->i2c_client_tuner;
- if (client) {
- module_put(client->dev.driver->owner);
- i2c_unregister_device(client);
- }
-
- /* remove I2C demod */
- client = dvb->i2c_client_demod;
- if (client) {
- module_put(client->dev.driver->owner);
- i2c_unregister_device(client);
- }
+ /* release I2C module bindings */
+ dvb_module_release(dvb->i2c_client_sec);
+ dvb_module_release(dvb->i2c_client_tuner);
+ dvb_module_release(dvb->i2c_client_demod);
kfree(dvb);
dev->dvb = NULL;
diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c
index 9bf49d666e5a..9151bccd859a 100644
--- a/drivers/media/usb/em28xx/em28xx-i2c.c
+++ b/drivers/media/usb/em28xx/em28xx-i2c.c
@@ -1,26 +1,22 @@
-/*
- em28xx-i2c.c - driver for Empia EM2800/EM2820/2840 USB video capture devices
-
- Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
- Markus Rechberger <mrechberger@gmail.com>
- Mauro Carvalho Chehab <mchehab@infradead.org>
- Sascha Sommer <saschasommer@freenet.de>
- Copyright (C) 2013 Frank Schäfer <fschaefer.oss@googlemail.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.
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// em28xx-i2c.c - driver for Empia EM2800/EM2820/2840 USB video capture devices
+//
+// Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
+// Markus Rechberger <mrechberger@gmail.com>
+// Mauro Carvalho Chehab <mchehab@infradead.org>
+// Sascha Sommer <saschasommer@freenet.de>
+// Copyright (C) 2013 Frank Schäfer <fschaefer.oss@googlemail.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.
#include "em28xx.h"
@@ -50,6 +46,35 @@ MODULE_PARM_DESC(i2c_debug, "i2c debug message level (1: normal debug, 2: show I
"i2c: %s: " fmt, __func__, ## arg); \
} while (0)
+/*
+ * Time in msecs to wait for i2c xfers to finish.
+ * 35ms is the maximum time a SMBUS device could wait when
+ * clock stretching is used. As the transfer itself will take
+ * some time to happen, set it to 35 ms.
+ *
+ * Ok, I2C doesn't specify any limit. So, eventually, we may need
+ * to increase this timeout.
+ */
+#define EM28XX_I2C_XFER_TIMEOUT 35 /* ms */
+
+static int em28xx_i2c_timeout(struct em28xx *dev)
+{
+ int time = EM28XX_I2C_XFER_TIMEOUT;
+
+ switch (dev->i2c_speed & 0x03) {
+ case EM28XX_I2C_FREQ_25_KHZ:
+ time += 4; /* Assume 4 ms for transfers */
+ break;
+ case EM28XX_I2C_FREQ_100_KHZ:
+ case EM28XX_I2C_FREQ_400_KHZ:
+ time += 1; /* Assume 1 ms for transfers */
+ break;
+ default: /* EM28XX_I2C_FREQ_1_5_MHZ */
+ break;
+ }
+
+ return msecs_to_jiffies(time);
+}
/*
* em2800_i2c_send_bytes()
@@ -57,14 +82,13 @@ MODULE_PARM_DESC(i2c_debug, "i2c debug message level (1: normal debug, 2: show I
*/
static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len)
{
- unsigned long timeout = jiffies + msecs_to_jiffies(EM28XX_I2C_XFER_TIMEOUT);
+ unsigned long timeout = jiffies + em28xx_i2c_timeout(dev);
int ret;
u8 b2[6];
if (len < 1 || len > 4)
return -EOPNOTSUPP;
- BUG_ON(len < 1 || len > 4);
b2[5] = 0x80 + len - 1;
b2[4] = addr;
b2[3] = buf[0];
@@ -98,7 +122,7 @@ static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len)
ret);
return ret;
}
- msleep(5);
+ usleep_range(5000, 6000);
}
dprintk(0, "write to i2c device at 0x%x timed out\n", addr);
return -ETIMEDOUT;
@@ -110,7 +134,7 @@ static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len)
*/
static int em2800_i2c_recv_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len)
{
- unsigned long timeout = jiffies + msecs_to_jiffies(EM28XX_I2C_XFER_TIMEOUT);
+ unsigned long timeout = jiffies + em28xx_i2c_timeout(dev);
u8 buf2[4];
int ret;
int i;
@@ -145,14 +169,13 @@ static int em2800_i2c_recv_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len)
ret);
return ret;
}
- msleep(5);
+ usleep_range(5000, 6000);
}
- if (ret != 0x84 + len - 1) {
+ if (ret != 0x84 + len - 1)
dprintk(0, "read from i2c device at 0x%x timed out\n", addr);
- }
/* get the received message */
- ret = dev->em28xx_read_reg_req_len(dev, 0x00, 4-len, buf2, len);
+ ret = dev->em28xx_read_reg_req_len(dev, 0x00, 4 - len, buf2, len);
if (ret != len) {
dev_warn(&dev->intf->dev,
"reading from i2c device at 0x%x failed: couldn't get the received message from the bridge (error=%i)\n",
@@ -186,7 +209,7 @@ static int em2800_i2c_check_for_device(struct em28xx *dev, u8 addr)
static int em28xx_i2c_send_bytes(struct em28xx *dev, u16 addr, u8 *buf,
u16 len, int stop)
{
- unsigned long timeout = jiffies + msecs_to_jiffies(EM28XX_I2C_XFER_TIMEOUT);
+ unsigned long timeout = jiffies + em28xx_i2c_timeout(dev);
int ret;
if (len < 1 || len > 64)
@@ -204,12 +227,11 @@ static int em28xx_i2c_send_bytes(struct em28xx *dev, u16 addr, u8 *buf,
"writing to i2c device at 0x%x failed (error=%i)\n",
addr, ret);
return ret;
- } else {
- dev_warn(&dev->intf->dev,
- "%i bytes write to i2c device at 0x%x requested, but %i bytes written\n",
- len, addr, ret);
- return -EIO;
}
+ dev_warn(&dev->intf->dev,
+ "%i bytes write to i2c device at 0x%x requested, but %i bytes written\n",
+ len, addr, ret);
+ return -EIO;
}
/* wait for completion */
@@ -228,7 +250,7 @@ static int em28xx_i2c_send_bytes(struct em28xx *dev, u16 addr, u8 *buf,
ret);
return ret;
}
- msleep(5);
+ usleep_range(5000, 6000);
/*
* NOTE: do we really have to wait for success ?
* Never seen anything else than 0x00 or 0x10
@@ -351,12 +373,12 @@ static int em25xx_bus_B_send_bytes(struct em28xx *dev, u16 addr, u8 *buf,
"writing to i2c device at 0x%x failed (error=%i)\n",
addr, ret);
return ret;
- } else {
- dev_warn(&dev->intf->dev,
- "%i bytes write to i2c device at 0x%x requested, but %i bytes written\n",
- len, addr, ret);
- return -EIO;
}
+
+ dev_warn(&dev->intf->dev,
+ "%i bytes write to i2c device at 0x%x requested, but %i bytes written\n",
+ len, addr, ret);
+ return -EIO;
}
/* Check success */
ret = dev->em28xx_read_reg_req(dev, 0x08, 0x0000);
@@ -366,7 +388,8 @@ static int em25xx_bus_B_send_bytes(struct em28xx *dev, u16 addr, u8 *buf,
*/
if (!ret)
return len;
- else if (ret > 0) {
+
+ if (ret > 0) {
dprintk(1, "Bus B R08 returned 0x%02x: I2C ACK error\n", ret);
return -ENXIO;
}
@@ -420,7 +443,8 @@ static int em25xx_bus_B_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf,
*/
if (!ret)
return len;
- else if (ret > 0) {
+
+ if (ret > 0) {
dprintk(1, "Bus B R08 returned 0x%02x: I2C ACK error\n", ret);
return -ENXIO;
}
@@ -508,13 +532,15 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap,
{
struct em28xx_i2c_bus *i2c_bus = i2c_adap->algo_data;
struct em28xx *dev = i2c_bus->dev;
- unsigned bus = i2c_bus->bus;
+ unsigned int bus = i2c_bus->bus;
int addr, rc, i;
u8 reg;
- /* prevent i2c xfer attempts after device is disconnected
- some fe's try to do i2c writes/reads from their release
- interfaces when called in disconnect path */
+ /*
+ * prevent i2c xfer attempts after device is disconnected
+ * some fe's try to do i2c writes/reads from their release
+ * interfaces when called in disconnect path
+ */
if (dev->disconnected)
return -ENODEV;
@@ -597,12 +623,13 @@ static inline unsigned long em28xx_hash_mem(char *buf, int length, int bits)
if (len == length) {
c = (char)len;
len = -1;
- } else
+ } else {
c = *buf++;
+ }
l = (l << 8) | c;
len++;
if ((len & (32 / 8 - 1)) == 0)
- hash = ((hash^l) * 0x9e370001UL);
+ hash = ((hash ^ l) * 0x9e370001UL);
} while (len);
return (hash >> (32 - bits)) & 0xffffffffUL;
@@ -612,7 +639,7 @@ static inline unsigned long em28xx_hash_mem(char *buf, int length, int bits)
* Helper function to read data blocks from i2c clients with 8 or 16 bit
* address width, 8 bit register width and auto incrementation been activated
*/
-static int em28xx_i2c_read_block(struct em28xx *dev, unsigned bus, u16 addr,
+static int em28xx_i2c_read_block(struct em28xx *dev, unsigned int bus, u16 addr,
bool addr_w16, u16 len, u8 *data)
{
int remain = len, rsize, rsize_max, ret;
@@ -624,7 +651,8 @@ static int em28xx_i2c_read_block(struct em28xx *dev, unsigned bus, u16 addr,
/* Select address */
buf[0] = addr >> 8;
buf[1] = addr & 0xff;
- ret = i2c_master_send(&dev->i2c_client[bus], buf + !addr_w16, 1 + addr_w16);
+ ret = i2c_master_send(&dev->i2c_client[bus],
+ buf + !addr_w16, 1 + addr_w16);
if (ret < 0)
return ret;
/* Read data */
@@ -649,7 +677,7 @@ static int em28xx_i2c_read_block(struct em28xx *dev, unsigned bus, u16 addr,
return len;
}
-static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned bus,
+static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned int bus,
u8 **eedata, u16 *eedata_len)
{
const u16 len = 256;
@@ -677,7 +705,7 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned bus,
}
data = kzalloc(len, GFP_KERNEL);
- if (data == NULL)
+ if (!data)
return -ENOMEM;
/* Read EEPROM content */
@@ -710,8 +738,8 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned bus,
mc_start = (data[1] << 8) + 4; /* usually 0x0004 */
dev_info(&dev->intf->dev,
- "EEPROM ID = %02x %02x %02x %02x, EEPROM hash = 0x%08lx\n",
- data[0], data[1], data[2], data[3], dev->hash);
+ "EEPROM ID = %4ph, EEPROM hash = 0x%08lx\n",
+ data, dev->hash);
dev_info(&dev->intf->dev,
"EEPROM info:\n");
dev_info(&dev->intf->dev,
@@ -769,15 +797,18 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned bus,
return 0;
}
- /* TODO: decrypt eeprom data for camera bridges (em25xx, em276x+) */
+ /*
+ * TODO: decrypt eeprom data for camera bridges
+ * (em25xx, em276x+)
+ */
} else if (!dev->eeprom_addrwidth_16bit &&
data[0] == 0x1a && data[1] == 0xeb &&
data[2] == 0x67 && data[3] == 0x95) {
dev->hash = em28xx_hash_mem(data, len, 32);
dev_info(&dev->intf->dev,
- "EEPROM ID = %02x %02x %02x %02x, EEPROM hash = 0x%08lx\n",
- data[0], data[1], data[2], data[3], dev->hash);
+ "EEPROM ID = %4ph, EEPROM hash = 0x%08lx\n",
+ data, dev->hash);
dev_info(&dev->intf->dev,
"EEPROM info:\n");
} else {
@@ -859,8 +890,8 @@ static u32 functionality(struct i2c_adapter *i2c_adap)
{
struct em28xx_i2c_bus *i2c_bus = i2c_adap->algo_data;
- if ((i2c_bus->algo_type == EM28XX_I2C_ALGO_EM28XX) ||
- (i2c_bus->algo_type == EM28XX_I2C_ALGO_EM25XX_BUS_B)) {
+ if (i2c_bus->algo_type == EM28XX_I2C_ALGO_EM28XX ||
+ i2c_bus->algo_type == EM28XX_I2C_ALGO_EM25XX_BUS_B) {
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
} else if (i2c_bus->algo_type == EM28XX_I2C_ALGO_EM2800) {
return (I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL) &
@@ -893,7 +924,7 @@ static const struct i2c_client em28xx_client_template = {
* incomplete list of known devices
*/
static char *i2c_devs[128] = {
- [0x1c >> 1] = "lgdt330x",
+ [0x1c >> 1] = "lgdt330x",
[0x3e >> 1] = "remote IR sensor",
[0x4a >> 1] = "saa7113h",
[0x52 >> 1] = "drxk",
@@ -916,7 +947,7 @@ static char *i2c_devs[128] = {
* do_i2c_scan()
* check i2c address range for devices
*/
-void em28xx_do_i2c_scan(struct em28xx *dev, unsigned bus)
+void em28xx_do_i2c_scan(struct em28xx *dev, unsigned int bus)
{
u8 i2c_devicelist[128];
unsigned char buf;
@@ -944,13 +975,14 @@ void em28xx_do_i2c_scan(struct em28xx *dev, unsigned bus)
* em28xx_i2c_register()
* register i2c bus
*/
-int em28xx_i2c_register(struct em28xx *dev, unsigned bus,
+int em28xx_i2c_register(struct em28xx *dev, unsigned int bus,
enum em28xx_i2c_algo_type algo_type)
{
int retval;
- BUG_ON(!dev->em28xx_write_regs || !dev->em28xx_read_reg);
- BUG_ON(!dev->em28xx_write_regs_req || !dev->em28xx_read_reg_req);
+ if (WARN_ON(!dev->em28xx_write_regs || !dev->em28xx_read_reg ||
+ !dev->em28xx_write_regs_req || !dev->em28xx_read_reg_req))
+ return -ENODEV;
if (bus >= NUM_I2C_BUSES)
return -ENODEV;
@@ -977,8 +1009,9 @@ int em28xx_i2c_register(struct em28xx *dev, unsigned bus,
/* Up to now, all eeproms are at bus 0 */
if (!bus) {
- retval = em28xx_i2c_eeprom(dev, bus, &dev->eedata, &dev->eedata_len);
- if ((retval < 0) && (retval != -ENODEV)) {
+ retval = em28xx_i2c_eeprom(dev, bus,
+ &dev->eedata, &dev->eedata_len);
+ if (retval < 0 && retval != -ENODEV) {
dev_err(&dev->intf->dev,
"%s: em28xx_i2_eeprom failed! retval [%d]\n",
__func__, retval);
@@ -995,7 +1028,7 @@ int em28xx_i2c_register(struct em28xx *dev, unsigned bus,
* em28xx_i2c_unregister()
* unregister i2c_bus
*/
-int em28xx_i2c_unregister(struct em28xx *dev, unsigned bus)
+int em28xx_i2c_unregister(struct em28xx *dev, unsigned int bus)
{
if (bus >= NUM_I2C_BUSES)
return -ENODEV;
diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c
index 046223de1e91..2dc1be00b8b8 100644
--- a/drivers/media/usb/em28xx/em28xx-input.c
+++ b/drivers/media/usb/em28xx/em28xx-input.c
@@ -1,25 +1,21 @@
-/*
- handle em28xx IR remotes via linux kernel input layer.
-
- Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
- Markus Rechberger <mrechberger@gmail.com>
- Mauro Carvalho Chehab <mchehab@infradead.org>
- Sascha Sommer <saschasommer@freenet.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 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
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// handle em28xx IR remotes via linux kernel input layer.
+//
+// Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
+// Markus Rechberger <mrechberger@gmail.com>
+// Mauro Carvalho Chehab <mchehab@infradead.org>
+// Sascha Sommer <saschasommer@freenet.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 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.
#include "em28xx.h"
@@ -41,15 +37,15 @@ MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]");
#define MODULE_NAME "em28xx"
-#define dprintk( fmt, arg...) do { \
+#define dprintk(fmt, arg...) do { \
if (ir_debug) \
dev_printk(KERN_DEBUG, &ir->dev->intf->dev, \
"input: %s: " fmt, __func__, ## arg); \
} while (0)
-/**********************************************************
- Polling structure used by em28xx IR's
- **********************************************************/
+/*
+ * Polling structure used by em28xx IR's
+ */
struct em28xx_ir_poll_result {
unsigned int toggle_bit:1;
@@ -76,24 +72,31 @@ struct em28xx_IR {
int (*get_key_i2c)(struct i2c_client *ir, enum rc_proto *protocol,
u32 *scancode);
- int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *);
+ int (*get_key)(struct em28xx_IR *ir, struct em28xx_ir_poll_result *r);
};
-/**********************************************************
- I2C IR based get keycodes - should be used with ir-kbd-i2c
- **********************************************************/
+/*
+ * I2C IR based get keycodes - should be used with ir-kbd-i2c
+ */
static int em28xx_get_key_terratec(struct i2c_client *i2c_dev,
enum rc_proto *protocol, u32 *scancode)
{
+ int rc;
unsigned char b;
/* poll IR chip */
- if (1 != i2c_master_recv(i2c_dev, &b, 1))
+ rc = i2c_master_recv(i2c_dev, &b, 1);
+ if (rc != 1) {
+ if (rc < 0)
+ return rc;
return -EIO;
+ }
- /* it seems that 0xFE indicates that a button is still hold
- down, while 0xff indicates that no button is hold down. */
+ /*
+ * it seems that 0xFE indicates that a button is still hold
+ * down, while 0xff indicates that no button is hold down.
+ */
if (b == 0xff)
return 0;
@@ -145,7 +148,7 @@ static int em28xx_get_key_pinnacle_usb_grey(struct i2c_client *i2c_dev,
/* poll IR chip */
- if (3 != i2c_master_recv(i2c_dev, buf, 3))
+ if (i2c_master_recv(i2c_dev, buf, 3) != 3)
return -EIO;
if (buf[0] != 0x00)
@@ -162,18 +165,28 @@ static int em28xx_get_key_winfast_usbii_deluxe(struct i2c_client *i2c_dev,
{
unsigned char subaddr, keydetect, key;
- struct i2c_msg msg[] = { { .addr = i2c_dev->addr, .flags = 0, .buf = &subaddr, .len = 1},
- { .addr = i2c_dev->addr, .flags = I2C_M_RD, .buf = &keydetect, .len = 1} };
+ struct i2c_msg msg[] = {
+ {
+ .addr = i2c_dev->addr,
+ .flags = 0,
+ .buf = &subaddr, .len = 1
+ }, {
+ .addr = i2c_dev->addr,
+ .flags = I2C_M_RD,
+ .buf = &keydetect,
+ .len = 1
+ }
+ };
subaddr = 0x10;
- if (2 != i2c_transfer(i2c_dev->adapter, msg, 2))
+ if (i2c_transfer(i2c_dev->adapter, msg, 2) != 2)
return -EIO;
if (keydetect == 0x00)
return 0;
subaddr = 0x00;
msg[1].buf = &key;
- if (2 != i2c_transfer(i2c_dev->adapter, msg, 2))
+ if (i2c_transfer(i2c_dev->adapter, msg, 2) != 2)
return -EIO;
if (key == 0x00)
return 0;
@@ -183,9 +196,9 @@ static int em28xx_get_key_winfast_usbii_deluxe(struct i2c_client *i2c_dev,
return 1;
}
-/**********************************************************
- Poll based get keycode functions
- **********************************************************/
+/*
+ * Poll based get keycode functions
+ */
/* This is for the em2860/em2880 */
static int default_polling_getkey(struct em28xx_IR *ir,
@@ -195,8 +208,9 @@ static int default_polling_getkey(struct em28xx_IR *ir,
int rc;
u8 msg[3] = { 0, 0, 0 };
- /* Read key toggle, brand, and key code
- on registers 0x45, 0x46 and 0x47
+ /*
+ * Read key toggle, brand, and key code
+ * on registers 0x45, 0x46 and 0x47
*/
rc = dev->em28xx_read_reg_req_len(dev, 0, EM28XX_R45_IR,
msg, sizeof(msg));
@@ -237,8 +251,9 @@ static int em2874_polling_getkey(struct em28xx_IR *ir,
int rc;
u8 msg[5] = { 0, 0, 0, 0, 0 };
- /* Read key toggle, brand, and key code
- on registers 0x51-55
+ /*
+ * Read key toggle, brand, and key code
+ * on registers 0x51-55
*/
rc = dev->em28xx_read_reg_req_len(dev, 0, EM2874_R51_IR,
msg, sizeof(msg));
@@ -294,9 +309,9 @@ static int em2874_polling_getkey(struct em28xx_IR *ir,
return 0;
}
-/**********************************************************
- Polling code for em28xx
- **********************************************************/
+/*
+ * Polling code for em28xx
+ */
static int em28xx_i2c_ir_handle_key(struct em28xx_IR *ir)
{
@@ -347,11 +362,14 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir)
if (ir->dev->chip_id == CHIP_ID_EM2874 ||
ir->dev->chip_id == CHIP_ID_EM2884)
- /* The em2874 clears the readcount field every time the
- register is read. The em2860/2880 datasheet says that it
- is supposed to clear the readcount, but it doesn't. So with
- the em2874, we are looking for a non-zero read count as
- opposed to a readcount that is incrementing */
+ /*
+ * The em2874 clears the readcount field every time the
+ * register is read. The em2860/2880 datasheet says
+ * that it is supposed to clear the readcount, but it
+ * doesn't. So with the em2874, we are looking for a
+ * non-zero read count as opposed to a readcount
+ * that is incrementing
+ */
ir->last_readcount = 0;
else
ir->last_readcount = poll_result.read_count;
@@ -476,15 +494,18 @@ static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_proto)
static int em28xx_probe_i2c_ir(struct em28xx *dev)
{
int i = 0;
- /* Leadtek winfast tv USBII deluxe can find a non working IR-device */
- /* at address 0x18, so if that address is needed for another board in */
- /* the future, please put it after 0x1f. */
+ /*
+ * Leadtek winfast tv USBII deluxe can find a non working IR-device
+ * at address 0x18, so if that address is needed for another board in
+ * the future, please put it after 0x1f.
+ */
const unsigned short addr_list[] = {
0x1f, 0x30, 0x47, I2C_CLIENT_END
};
while (addr_list[i] != I2C_CLIENT_END) {
- if (i2c_probe_func_quick_read(&dev->i2c_adap[dev->def_i2c_bus], addr_list[i]) == 1)
+ if (i2c_probe_func_quick_read(&dev->i2c_adap[dev->def_i2c_bus],
+ addr_list[i]) == 1)
return addr_list[i];
i++;
}
@@ -492,9 +513,9 @@ static int em28xx_probe_i2c_ir(struct em28xx *dev)
return -ENODEV;
}
-/**********************************************************
- Handle buttons
- **********************************************************/
+/*
+ * Handle buttons
+ */
static void em28xx_query_buttons(struct work_struct *work)
{
@@ -515,7 +536,10 @@ static void em28xx_query_buttons(struct work_struct *work)
j = 0;
while (dev->board.buttons[j].role >= 0 &&
dev->board.buttons[j].role < EM28XX_NUM_BUTTON_ROLES) {
- struct em28xx_button *button = &dev->board.buttons[j];
+ const struct em28xx_button *button;
+
+ button = &dev->board.buttons[j];
+
/* Check if button uses the current address */
if (button->reg_r != dev->button_polling_addresses[i]) {
j++;
@@ -618,7 +642,8 @@ static void em28xx_init_buttons(struct em28xx *dev)
dev->button_polling_interval = EM28XX_BUTTONS_DEBOUNCED_QUERY_INTERVAL;
while (dev->board.buttons[i].role >= 0 &&
dev->board.buttons[i].role < EM28XX_NUM_BUTTON_ROLES) {
- struct em28xx_button *button = &dev->board.buttons[i];
+ const struct em28xx_button *button = &dev->board.buttons[i];
+
/* Check if polling address is already on the list */
addr_new = true;
for (j = 0; j < dev->num_button_polling_addresses; j++) {
@@ -649,6 +674,7 @@ static void em28xx_init_buttons(struct em28xx *dev)
/* Add read address to list of polling addresses */
if (addr_new) {
unsigned int index = dev->num_button_polling_addresses;
+
dev->button_polling_addresses[index] = button->reg_r;
dev->num_button_polling_addresses++;
}
@@ -677,7 +703,7 @@ static void em28xx_shutdown_buttons(struct em28xx *dev)
/* Clear polling addresses list */
dev->num_button_polling_addresses = 0;
/* Deregister input devices */
- if (dev->sbutton_input_dev != NULL) {
+ if (dev->sbutton_input_dev) {
dev_info(&dev->intf->dev, "Deregistering snapshot button\n");
input_unregister_device(dev->sbutton_input_dev);
dev->sbutton_input_dev = NULL;
@@ -714,7 +740,7 @@ static int em28xx_ir_init(struct em28xx *dev)
}
}
- if (dev->board.ir_codes == NULL && !dev->board.has_ir_i2c) {
+ if (!dev->board.ir_codes && !dev->board.has_ir_i2c) {
/* No remote control support */
dev_warn(&dev->intf->dev,
"Remote control support is not available for this card.\n");
@@ -764,7 +790,7 @@ static int em28xx_ir_init(struct em28xx *dev)
goto error;
}
- ir->i2c_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
+ ir->i2c_client = kzalloc(sizeof(*ir->i2c_client), GFP_KERNEL);
if (!ir->i2c_client)
goto error;
ir->i2c_client->adapter = &ir->dev->i2c_adap[dev->def_i2c_bus];
@@ -881,9 +907,11 @@ static int em28xx_ir_suspend(struct em28xx *dev)
if (ir)
cancel_delayed_work_sync(&ir->work);
cancel_delayed_work_sync(&dev->buttons_query_work);
- /* is canceling delayed work sufficient or does the rc event
- kthread needs stopping? kthread is stopped in
- ir_raw_event_unregister() */
+ /*
+ * is canceling delayed work sufficient or does the rc event
+ * kthread needs stopping? kthread is stopped in
+ * ir_raw_event_unregister()
+ */
return 0;
}
@@ -895,8 +923,10 @@ static int em28xx_ir_resume(struct em28xx *dev)
return 0;
dev_info(&dev->intf->dev, "Resuming input extension\n");
- /* if suspend calls ir_raw_event_unregister(), the should call
- ir_raw_event_register() */
+ /*
+ * if suspend calls ir_raw_event_unregister(), the should call
+ * ir_raw_event_register()
+ */
if (ir)
schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling));
if (dev->num_button_polling_addresses)
@@ -924,7 +954,7 @@ static void __exit em28xx_rc_unregister(void)
em28xx_unregister_extension(&rc_ops);
}
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Mauro Carvalho Chehab");
MODULE_DESCRIPTION(DRIVER_DESC " - input interface");
MODULE_VERSION(EM28XX_VERSION);
diff --git a/drivers/media/usb/em28xx/em28xx-reg.h b/drivers/media/usb/em28xx/em28xx-reg.h
index 9e5cdfb25a73..f53afe18e92d 100644
--- a/drivers/media/usb/em28xx/em28xx-reg.h
+++ b/drivers/media/usb/em28xx/em28xx-reg.h
@@ -1,17 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0 */
-#define EM_GPIO_0 (1 << 0)
-#define EM_GPIO_1 (1 << 1)
-#define EM_GPIO_2 (1 << 2)
-#define EM_GPIO_3 (1 << 3)
-#define EM_GPIO_4 (1 << 4)
-#define EM_GPIO_5 (1 << 5)
-#define EM_GPIO_6 (1 << 6)
-#define EM_GPIO_7 (1 << 7)
-
-#define EM_GPO_0 (1 << 0)
-#define EM_GPO_1 (1 << 1)
-#define EM_GPO_2 (1 << 2)
-#define EM_GPO_3 (1 << 3)
+
+/*
+ * em28xx-reg.h - Register definitions for em28xx driver
+ */
+
+#define EM_GPIO_0 ((unsigned char)BIT(0))
+#define EM_GPIO_1 ((unsigned char)BIT(1))
+#define EM_GPIO_2 ((unsigned char)BIT(2))
+#define EM_GPIO_3 ((unsigned char)BIT(3))
+#define EM_GPIO_4 ((unsigned char)BIT(4))
+#define EM_GPIO_5 ((unsigned char)BIT(5))
+#define EM_GPIO_6 ((unsigned char)BIT(6))
+#define EM_GPIO_7 ((unsigned char)BIT(7))
+
+#define EM_GPO_0 ((unsigned char)BIT(0))
+#define EM_GPO_1 ((unsigned char)BIT(1))
+#define EM_GPO_2 ((unsigned char)BIT(2))
+#define EM_GPO_3 ((unsigned char)BIT(3))
/* em28xx endpoints */
/* 0x82: (always ?) analog */
@@ -203,10 +208,11 @@
#define EM28XX_R43_AC97BUSY 0x43
#define EM28XX_R45_IR 0x45
- /* 0x45 bit 7 - parity bit
- bits 6-0 - count
- 0x46 IR brand
- 0x47 IR data
+ /*
+ * 0x45 bit 7 - parity bit
+ * bits 6-0 - count
+ * 0x46 IR brand
+ * 0x47 IR data
*/
/* em2874 registers */
@@ -249,12 +255,12 @@
#define EM2874_IR_RC6_MODE_6A 0x0b
/* em2874 Transport Stream Enable Register (0x5f) */
-#define EM2874_TS1_CAPTURE_ENABLE (1 << 0)
-#define EM2874_TS1_FILTER_ENABLE (1 << 1)
-#define EM2874_TS1_NULL_DISCARD (1 << 2)
-#define EM2874_TS2_CAPTURE_ENABLE (1 << 4)
-#define EM2874_TS2_FILTER_ENABLE (1 << 5)
-#define EM2874_TS2_NULL_DISCARD (1 << 6)
+#define EM2874_TS1_CAPTURE_ENABLE ((unsigned char)BIT(0))
+#define EM2874_TS1_FILTER_ENABLE ((unsigned char)BIT(1))
+#define EM2874_TS1_NULL_DISCARD ((unsigned char)BIT(2))
+#define EM2874_TS2_CAPTURE_ENABLE ((unsigned char)BIT(4))
+#define EM2874_TS2_FILTER_ENABLE ((unsigned char)BIT(5))
+#define EM2874_TS2_NULL_DISCARD ((unsigned char)BIT(6))
/* register settings */
#define EM2800_AUDIO_SRC_TUNER 0x0d
diff --git a/drivers/media/usb/em28xx/em28xx-v4l.h b/drivers/media/usb/em28xx/em28xx-v4l.h
index 9c411aac3878..1788dbf9024a 100644
--- a/drivers/media/usb/em28xx/em28xx-v4l.h
+++ b/drivers/media/usb/em28xx/em28xx-v4l.h
@@ -1,17 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
- em28xx-video.c - driver for Empia EM2800/EM2820/2840 USB
- video capture devices
-
- Copyright (C) 2013-2014 Mauro Carvalho Chehab <m.chehab@samsung.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 of the License.
-
- 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.
+ * em28xx-video.c - driver for Empia EM2800/EM2820/2840 USB
+ * video capture devices
+ *
+ * Copyright (C) 2013-2014 Mauro Carvalho Chehab <m.chehab@samsung.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 of the License.
+ *
+ * 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.
*/
int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count);
diff --git a/drivers/media/usb/em28xx/em28xx-vbi.c b/drivers/media/usb/em28xx/em28xx-vbi.c
index f5123651ef30..63c48361d3f2 100644
--- a/drivers/media/usb/em28xx/em28xx-vbi.c
+++ b/drivers/media/usb/em28xx/em28xx-vbi.c
@@ -1,25 +1,20 @@
-/*
- em28xx-vbi.c - VBI driver for em28xx
-
- Copyright (C) 2009 Devin Heitmueller <dheitmueller@kernellabs.com>
-
- This work was sponsored by EyeMagnet Limited.
-
- 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.
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// em28xx-vbi.c - VBI driver for em28xx
+//
+// Copyright (C) 2009 Devin Heitmueller <dheitmueller@kernellabs.com>
+//
+// This work was sponsored by EyeMagnet Limited.
+//
+// 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.
#include "em28xx.h"
diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c
index a2ba2d905952..d70ee13cc52e 100644
--- a/drivers/media/usb/em28xx/em28xx-video.c
+++ b/drivers/media/usb/em28xx/em28xx-video.c
@@ -1,30 +1,26 @@
-/*
- em28xx-video.c - driver for Empia EM2800/EM2820/2840 USB
- video capture devices
-
- Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
- Markus Rechberger <mrechberger@gmail.com>
- Mauro Carvalho Chehab <mchehab@infradead.org>
- Sascha Sommer <saschasommer@freenet.de>
- Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.com>
-
- Some parts based on SN9C10x PC Camera Controllers GPL driver made
- by Luca Risolia <luca.risolia@studio.unibo.it>
-
- 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.
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// em28xx-video.c - driver for Empia EM2800/EM2820/2840 USB
+// video capture devices
+//
+// Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
+// Markus Rechberger <mrechberger@gmail.com>
+// Mauro Carvalho Chehab <mchehab@infradead.org>
+// Sascha Sommer <saschasommer@freenet.de>
+// Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.com>
+//
+// Some parts based on SN9C10x PC Camera Controllers GPL driver made
+// by Luca Risolia <luca.risolia@studio.unibo.it>
+//
+// 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.
#include "em28xx.h"
@@ -77,7 +73,7 @@ MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint");
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC " - v4l2 interface");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_VERSION(EM28XX_VERSION);
#define EM25XX_FRMDATAHDR_BYTE1 0x02
@@ -148,7 +144,7 @@ static inline unsigned int norm_maxw(struct em28xx *dev)
{
struct em28xx_v4l2 *v4l2 = dev->v4l2;
- if (dev->board.is_webcam)
+ if (dev->is_webcam)
return v4l2->sensor_xres;
if (dev->board.max_range_640_480)
@@ -161,7 +157,7 @@ static inline unsigned int norm_maxh(struct em28xx *dev)
{
struct em28xx_v4l2 *v4l2 = dev->v4l2;
- if (dev->board.is_webcam)
+ if (dev->is_webcam)
return v4l2->sensor_yres;
if (dev->board.max_range_640_480)
@@ -176,7 +172,7 @@ static int em28xx_vbi_supported(struct em28xx *dev)
if (disable_vbi == 1)
return 0;
- if (dev->board.is_webcam)
+ if (dev->is_webcam)
return 0;
/* FIXME: check subdevices for VBI support */
@@ -250,7 +246,8 @@ static int em28xx_set_outfmt(struct em28xx *dev)
if (em28xx_vbi_supported(dev) == 1) {
vinctrl |= EM28XX_VINCTRL_VBI_RAW;
em28xx_write_reg(dev, EM28XX_R34_VBI_START_H, 0x00);
- em28xx_write_reg(dev, EM28XX_R36_VBI_WIDTH, v4l2->vbi_width/4);
+ em28xx_write_reg(dev, EM28XX_R36_VBI_WIDTH,
+ v4l2->vbi_width / 4);
em28xx_write_reg(dev, EM28XX_R37_VBI_HEIGHT, v4l2->vbi_height);
if (v4l2->norm & V4L2_STD_525_60) {
/* NTSC */
@@ -320,8 +317,10 @@ static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v)
buf[0] = v;
buf[1] = v >> 8;
em28xx_write_regs(dev, EM28XX_R32_VSCALELOW, (char *)buf, 2);
- /* it seems that both H and V scalers must be active
- to work correctly */
+ /*
+ * it seems that both H and V scalers must be active
+ * to work correctly
+ */
mode = (h || v) ? 0x30 : 0x00;
}
return em28xx_write_reg(dev, EM28XX_R26_COMPR, mode);
@@ -345,13 +344,15 @@ static int em28xx_resolution_set(struct em28xx *dev)
em28xx_accumulator_set(dev, 1, (width - 4) >> 2, 1, (height - 4) >> 2);
- /* If we don't set the start position to 2 in VBI mode, we end up
- with line 20/21 being YUYV encoded instead of being in 8-bit
- greyscale. The core of the issue is that line 21 (and line 23 for
- PAL WSS) are inside of active video region, and as a result they
- get the pixelformatting associated with that area. So by cropping
- it out, we end up with the same format as the rest of the VBI
- region */
+ /*
+ * If we don't set the start position to 2 in VBI mode, we end up
+ * with line 20/21 being YUYV encoded instead of being in 8-bit
+ * greyscale. The core of the issue is that line 21 (and line 23 for
+ * PAL WSS) are inside of active video region, and as a result they
+ * get the pixelformatting associated with that area. So by cropping
+ * it out, we end up with the same format as the rest of the VBI
+ * region
+ */
if (em28xx_vbi_supported(dev) == 1)
em28xx_capture_area_set(dev, 0, 2, width, height);
else
@@ -365,14 +366,16 @@ static int em28xx_set_alternate(struct em28xx *dev)
{
struct em28xx_v4l2 *v4l2 = dev->v4l2;
struct usb_device *udev = interface_to_usbdev(dev->intf);
- int errCode;
+ int err;
int i;
unsigned int min_pkt_size = v4l2->width * 2 + 4;
- /* NOTE: for isoc transfers, only alt settings > 0 are allowed
- bulk transfers seem to work only with alt=0 ! */
+ /*
+ * NOTE: for isoc transfers, only alt settings > 0 are allowed
+ * bulk transfers seem to work only with alt=0 !
+ */
dev->alt = 0;
- if ((alt > 0) && (alt < dev->num_alt)) {
+ if (alt > 0 && alt < dev->num_alt) {
em28xx_videodbg("alternate forced to %d\n", dev->alt);
dev->alt = alt;
goto set_alt;
@@ -380,9 +383,10 @@ static int em28xx_set_alternate(struct em28xx *dev)
if (dev->analog_xfer_bulk)
goto set_alt;
- /* When image size is bigger than a certain value,
- the frame size should be increased, otherwise, only
- green screen will be received.
+ /*
+ * When image size is bigger than a certain value,
+ * the frame size should be increased, otherwise, only
+ * green screen will be received.
*/
if (v4l2->width * 2 * v4l2->height > 720 * 240 * 2)
min_pkt_size *= 2;
@@ -392,18 +396,22 @@ static int em28xx_set_alternate(struct em28xx *dev)
if (dev->alt_max_pkt_size_isoc[i] >= min_pkt_size) {
dev->alt = i;
break;
- /* otherwise make sure that we end up with the maximum bandwidth
- because the min_pkt_size equation might be wrong...
- */
+ /*
+ * otherwise make sure that we end up with the maximum
+ * bandwidth because the min_pkt_size equation might be wrong.
+ *
+ */
} else if (dev->alt_max_pkt_size_isoc[i] >
dev->alt_max_pkt_size_isoc[dev->alt])
dev->alt = i;
}
set_alt:
- /* NOTE: for bulk transfers, we need to call usb_set_interface()
+ /*
+ * NOTE: for bulk transfers, we need to call usb_set_interface()
* even if the previous settings were the same. Otherwise streaming
- * fails with all urbs having status = -EOVERFLOW ! */
+ * fails with all urbs having status = -EOVERFLOW !
+ */
if (dev->analog_xfer_bulk) {
dev->max_pkt_size = 512; /* USB 2.0 spec */
dev->packet_multiplier = EM28XX_BULK_PACKET_MULTIPLIER;
@@ -416,19 +424,19 @@ set_alt:
}
em28xx_videodbg("setting alternate %d with wMaxPacketSize=%u\n",
dev->alt, dev->max_pkt_size);
- errCode = usb_set_interface(udev, dev->ifnum, dev->alt);
- if (errCode < 0) {
+ err = usb_set_interface(udev, dev->ifnum, dev->alt);
+ if (err < 0) {
dev_err(&dev->intf->dev,
"cannot change alternate number to %d (error=%i)\n",
- dev->alt, errCode);
- return errCode;
+ dev->alt, err);
+ return err;
}
return 0;
}
-/* ------------------------------------------------------------------
- DMA and thread functions
- ------------------------------------------------------------------*/
+/*
+ * DMA and thread functions
+ */
/*
* Finish the current buffer
@@ -514,8 +522,9 @@ static void em28xx_copy_video(struct em28xx *dev,
em28xx_isocdbg("Overflow of %zu bytes past buffer end(2)\n",
((char *)startwrite + lencopy) -
((char *)buf->vb_buf + buf->length));
- lencopy = remain = (char *)buf->vb_buf + buf->length -
- (char *)startwrite;
+ remain = (char *)buf->vb_buf + buf->length -
+ (char *)startwrite;
+ lencopy = remain;
}
if (lencopy <= 0)
break;
@@ -623,11 +632,11 @@ finish_field_prepare_next(struct em28xx *dev,
struct em28xx_v4l2 *v4l2 = dev->v4l2;
if (v4l2->progressive || v4l2->top_field) { /* Brand new frame */
- if (buf != NULL)
+ if (buf)
finish_buffer(dev, buf);
buf = get_next_buf(dev, dma_q);
}
- if (buf != NULL) {
+ if (buf) {
buf->top_field = v4l2->top_field;
buf->pos = 0;
}
@@ -648,13 +657,17 @@ static inline void process_frame_data_em28xx(struct em28xx *dev,
struct em28xx_dmaqueue *dma_q = &dev->vidq;
struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq;
- /* capture type 0 = vbi start
- capture type 1 = vbi in progress
- capture type 2 = video start
- capture type 3 = video in progress */
+ /*
+ * capture type 0 = vbi start
+ * capture type 1 = vbi in progress
+ * capture type 2 = video start
+ * capture type 3 = video in progress
+ */
if (data_len >= 4) {
- /* NOTE: Headers are always 4 bytes and
- * never split across packets */
+ /*
+ * NOTE: Headers are always 4 bytes and
+ * never split across packets
+ */
if (data_pkt[0] == 0x88 && data_pkt[1] == 0x88 &&
data_pkt[2] == 0x88 && data_pkt[3] == 0x88) {
/* Continuation */
@@ -677,8 +690,10 @@ static inline void process_frame_data_em28xx(struct em28xx *dev,
data_len -= 4;
}
}
- /* NOTE: With bulk transfers, intermediate data packets
- * have no continuation header */
+ /*
+ * NOTE: With bulk transfers, intermediate data packets
+ * have no continuation header
+ */
if (v4l2->capture_type == 0) {
vbi_buf = finish_field_prepare_next(dev, vbi_buf, vbi_dma_q);
@@ -692,7 +707,7 @@ static inline void process_frame_data_em28xx(struct em28xx *dev,
(vbi_size - v4l2->vbi_read) : data_len;
/* Copy VBI data */
- if (vbi_buf != NULL)
+ if (vbi_buf)
em28xx_copy_vbi(dev, vbi_buf, data_pkt, vbi_data_len);
v4l2->vbi_read += vbi_data_len;
@@ -710,7 +725,7 @@ static inline void process_frame_data_em28xx(struct em28xx *dev,
v4l2->capture_type = 3;
}
- if (v4l2->capture_type == 3 && buf != NULL && data_len > 0)
+ if (v4l2->capture_type == 3 && buf && data_len > 0)
em28xx_copy_video(dev, buf, data_pkt, data_len);
}
@@ -727,8 +742,10 @@ static inline void process_frame_data_em25xx(struct em28xx *dev,
bool frame_end = false;
/* Check for header */
- /* NOTE: at least with bulk transfers, only the first packet
- * has a header and has always set the FRAME_END bit */
+ /*
+ * NOTE: at least with bulk transfers, only the first packet
+ * has a header and has always set the FRAME_END bit
+ */
if (data_len >= 2) { /* em25xx header is only 2 bytes long */
if ((data_pkt[0] == EM25XX_FRMDATAHDR_BYTE1) &&
((data_pkt[1] & ~EM25XX_FRMDATAHDR_BYTE2_MASK) == 0x00)) {
@@ -745,14 +762,15 @@ static inline void process_frame_data_em25xx(struct em28xx *dev,
buf = finish_field_prepare_next(dev, buf, dmaq);
dev->usb_ctl.vid_buf = buf;
}
- /* NOTE: in ISOC mode when a new frame starts and buf==NULL,
+ /*
+ * NOTE: in ISOC mode when a new frame starts and buf==NULL,
* we COULD already prepare a buffer here to avoid skipping the
* first frame.
*/
}
/* Copy data */
- if (buf != NULL && data_len > 0)
+ if (buf && data_len > 0)
em28xx_copy_video(dev, buf, data_pkt, data_len);
/* Finish frame (ISOC only) => avoids lag of 1 frame */
@@ -761,14 +779,17 @@ static inline void process_frame_data_em25xx(struct em28xx *dev,
dev->usb_ctl.vid_buf = buf;
}
- /* NOTE: Tested with USB bulk transfers only !
+ /*
+ * NOTES:
+ *
+ * 1) Tested with USB bulk transfers only !
* The wording in the datasheet suggests that isoc might work different.
* The current code assumes that with isoc transfers each packet has a
* header like with the other em28xx devices.
+ *
+ * 2) Support for interlaced mode is pure theory. It has not been
+ * tested and it is unknown if these devices actually support it.
*/
- /* NOTE: Support for interlaced mode is pure theory. It has not been
- * tested and it is unknown if these devices actually support it. */
- /* NOTE: No VBI support yet (these chips likely do not support VBI). */
}
/* Processes and copies the URB data content (video and VBI data) */
@@ -829,12 +850,11 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb)
else
process_frame_data_em28xx(dev,
usb_data_pkt, usb_data_len);
-
}
return 1;
}
-static int get_ressource(enum v4l2_buf_type f_type)
+static int get_resource(enum v4l2_buf_type f_type)
{
switch (f_type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
@@ -842,14 +862,15 @@ static int get_ressource(enum v4l2_buf_type f_type)
case V4L2_BUF_TYPE_VBI_CAPTURE:
return EM28XX_RESOURCE_VBI;
default:
- BUG();
+ WARN_ON(1);
+ return -1; /* Indicate that device is busy */
}
}
/* Usage lock check functions */
static int res_get(struct em28xx *dev, enum v4l2_buf_type f_type)
{
- int res_type = get_ressource(f_type);
+ int res_type = get_resource(f_type);
/* is it free? */
if (dev->resources & res_type) {
@@ -865,7 +886,7 @@ static int res_get(struct em28xx *dev, enum v4l2_buf_type f_type)
static void res_free(struct em28xx *dev, enum v4l2_buf_type f_type)
{
- int res_type = get_ressource(f_type);
+ int res_type = get_resource(f_type);
dev->resources &= ~res_type;
em28xx_videodbg("res: put %d\n", res_type);
@@ -937,10 +958,11 @@ static int em28xx_enable_analog_tuner(struct em28xx *dev)
flags ? "enabled" : "disabled",
ret);
return ret;
- } else
- em28xx_videodbg("link %s->%s was %s\n",
- source->name, sink->name,
- flags ? "ENABLED" : "disabled");
+ }
+
+ em28xx_videodbg("link %s->%s was %s\n",
+ source->name, sink->name,
+ flags ? "ENABLED" : "disabled");
}
#endif
return 0;
@@ -976,7 +998,7 @@ static void em28xx_v4l2_create_entities(struct em28xx *dev)
}
/* Webcams don't have input connectors */
- if (dev->board.is_webcam)
+ if (dev->is_webcam)
return;
/* Create entities for each input connector */
@@ -1016,10 +1038,9 @@ static void em28xx_v4l2_create_entities(struct em28xx *dev)
#endif
}
-
-/* ------------------------------------------------------------------
- Videobuf2 operations
- ------------------------------------------------------------------*/
+/*
+ * Videobuf2 operations
+ */
static int queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
@@ -1072,8 +1093,10 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count)
em28xx_videodbg("%s\n", __func__);
- /* Make sure streaming is not already in progress for this type
- of filehandle (e.g. video, vbi) */
+ /*
+ * Make sure streaming is not already in progress for this type
+ * of filehandle (e.g. video, vbi)
+ */
rc = res_get(dev, vq->type);
if (rc)
return rc;
@@ -1084,9 +1107,10 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count)
/* Allocate the USB bandwidth */
em28xx_set_alternate(dev);
- /* Needed, since GPIO might have disabled power of
- some i2c device
- */
+ /*
+ * Needed, since GPIO might have disabled power of
+ * some i2c device
+ */
em28xx_wake_i2c(dev);
v4l2->capture_type = -1;
@@ -1145,7 +1169,7 @@ static void em28xx_stop_streaming(struct vb2_queue *vq)
}
spin_lock_irqsave(&dev->slock, flags);
- if (dev->usb_ctl.vid_buf != NULL) {
+ if (dev->usb_ctl.vid_buf) {
vb2_buffer_done(&dev->usb_ctl.vid_buf->vb.vb2_buf,
VB2_BUF_STATE_ERROR);
dev->usb_ctl.vid_buf = NULL;
@@ -1180,7 +1204,7 @@ void em28xx_stop_vbi_streaming(struct vb2_queue *vq)
}
spin_lock_irqsave(&dev->slock, flags);
- if (dev->usb_ctl.vbi_buf != NULL) {
+ if (dev->usb_ctl.vbi_buf) {
vb2_buffer_done(&dev->usb_ctl.vbi_buf->vb.vb2_buf,
VB2_BUF_STATE_ERROR);
dev->usb_ctl.vbi_buf = NULL;
@@ -1261,7 +1285,9 @@ static int em28xx_vb2_setup(struct em28xx *dev)
return 0;
}
-/********************* v4l2 interface **************************************/
+/*
+ * v4l2 interface
+ */
static void video_mux(struct em28xx *dev, int index)
{
@@ -1277,7 +1303,7 @@ static void video_mux(struct em28xx *dev, int index)
v4l2_device_call_all(v4l2_dev, 0, video, s_routing,
INPUT(index)->vmux, 0, 0);
- if (dev->board.has_msp34xx) {
+ if (dev->has_msp34xx) {
if (dev->i2s_speed) {
v4l2_device_call_all(v4l2_dev, 0, audio,
s_i2s_clock_freq, dev->i2s_speed);
@@ -1394,9 +1420,9 @@ static void scale_to_size(struct em28xx *dev,
*height = 1;
}
-/* ------------------------------------------------------------------
- IOCTL vidioc handling
- ------------------------------------------------------------------*/
+/*
+ * IOCTL vidioc handling
+ */
static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
@@ -1462,8 +1488,10 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
if (width == maxw && height == maxh)
width /= 2;
} else {
- /* width must even because of the YUYV format
- height must be even because of interlacing */
+ /*
+ * width must even because of the YUYV format
+ * height must be even because of interlacing
+ */
v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh,
1, 0);
}
@@ -1493,7 +1521,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
}
static int em28xx_set_video_format(struct em28xx *dev, unsigned int fourcc,
- unsigned width, unsigned height)
+ unsigned int width, unsigned int height)
{
struct em28xx_fmt *fmt;
struct em28xx_v4l2 *v4l2 = dev->v4l2;
@@ -1582,17 +1610,26 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm)
static int vidioc_g_parm(struct file *file, void *priv,
struct v4l2_streamparm *p)
{
+ struct v4l2_subdev_frame_interval ival = { 0 };
struct em28xx *dev = video_drvdata(file);
struct em28xx_v4l2 *v4l2 = dev->v4l2;
int rc = 0;
+ if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+
p->parm.capture.readbuffers = EM28XX_MIN_BUF;
- if (dev->board.is_webcam)
+ p->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ if (dev->is_webcam) {
rc = v4l2_device_call_until_err(&v4l2->v4l2_dev, 0,
- video, g_parm, p);
- else
+ video, g_frame_interval, &ival);
+ if (!rc)
+ p->parm.capture.timeperframe = ival.interval;
+ } else {
v4l2_video_std_frame_period(v4l2->norm,
&p->parm.capture.timeperframe);
+ }
return rc;
}
@@ -1601,10 +1638,27 @@ static int vidioc_s_parm(struct file *file, void *priv,
struct v4l2_streamparm *p)
{
struct em28xx *dev = video_drvdata(file);
+ struct v4l2_subdev_frame_interval ival = {
+ 0,
+ p->parm.capture.timeperframe
+ };
+ int rc = 0;
+
+ if (!dev->is_webcam)
+ return -ENOTTY;
+
+ if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+ memset(&p->parm, 0, sizeof(p->parm));
p->parm.capture.readbuffers = EM28XX_MIN_BUF;
- return v4l2_device_call_until_err(&dev->v4l2->v4l2_dev,
- 0, video, s_parm, p);
+ p->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ rc = v4l2_device_call_until_err(&dev->v4l2->v4l2_dev, 0,
+ video, s_frame_interval, &ival);
+ if (!rc)
+ p->parm.capture.timeperframe = ival.interval;
+ return rc;
}
static int vidioc_enum_input(struct file *file, void *priv,
@@ -1616,20 +1670,19 @@ static int vidioc_enum_input(struct file *file, void *priv,
n = i->index;
if (n >= MAX_EM28XX_INPUT)
return -EINVAL;
- if (0 == INPUT(n)->type)
+ if (!INPUT(n)->type)
return -EINVAL;
- i->index = n;
i->type = V4L2_INPUT_TYPE_CAMERA;
strcpy(i->name, iname[INPUT(n)->type]);
- if ((EM28XX_VMUX_TELEVISION == INPUT(n)->type))
+ if (INPUT(n)->type == EM28XX_VMUX_TELEVISION)
i->type = V4L2_INPUT_TYPE_TUNER;
i->std = dev->v4l2->vdev.tvnorms;
/* webcams do not have the STD API */
- if (dev->board.is_webcam)
+ if (dev->is_webcam)
i->capabilities = 0;
return 0;
@@ -1650,7 +1703,7 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
if (i >= MAX_EM28XX_INPUT)
return -EINVAL;
- if (0 == INPUT(i)->type)
+ if (!INPUT(i)->type)
return -EINVAL;
video_mux(dev, i);
@@ -1696,13 +1749,14 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
return 0;
}
-static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio *a)
+static int vidioc_s_audio(struct file *file, void *priv,
+ const struct v4l2_audio *a)
{
struct em28xx *dev = video_drvdata(file);
if (a->index >= MAX_EM28XX_INPUT)
return -EINVAL;
- if (0 == INPUT(a->index)->type)
+ if (!INPUT(a->index)->type)
return -EINVAL;
dev->ctl_ainput = INPUT(a->index)->amux;
@@ -1719,7 +1773,7 @@ static int vidioc_g_tuner(struct file *file, void *priv,
{
struct em28xx *dev = video_drvdata(file);
- if (0 != t->index)
+ if (t->index != 0)
return -EINVAL;
strcpy(t->name, "Tuner");
@@ -1733,7 +1787,7 @@ static int vidioc_s_tuner(struct file *file, void *priv,
{
struct em28xx *dev = video_drvdata(file);
- if (0 != t->index)
+ if (t->index != 0)
return -EINVAL;
v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, s_tuner, t);
@@ -1746,7 +1800,7 @@ static int vidioc_g_frequency(struct file *file, void *priv,
struct em28xx *dev = video_drvdata(file);
struct em28xx_v4l2 *v4l2 = dev->v4l2;
- if (0 != f->tuner)
+ if (f->tuner != 0)
return -EINVAL;
f->frequency = v4l2->frequency;
@@ -1760,7 +1814,7 @@ static int vidioc_s_frequency(struct file *file, void *priv,
struct em28xx *dev = video_drvdata(file);
struct em28xx_v4l2 *v4l2 = dev->v4l2;
- if (0 != f->tuner)
+ if (f->tuner != 0)
return -EINVAL;
v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, s_frequency, f);
@@ -1884,8 +1938,9 @@ static int vidioc_querycap(struct file *file, void *priv,
if (dev->tuner_type != TUNER_ABSENT)
cap->device_caps |= V4L2_CAP_TUNER;
- cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS |
- V4L2_CAP_READWRITE | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps |
+ V4L2_CAP_DEVICE_CAPS | V4L2_CAP_READWRITE |
+ V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
if (video_is_registered(&v4l2->vbi_dev))
cap->capabilities |= V4L2_CAP_VBI_CAPTURE;
if (video_is_registered(&v4l2->radio_dev))
@@ -1978,9 +2033,9 @@ static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
return 0;
}
-/* ----------------------------------------------------------- */
-/* RADIO ESPECIFIC IOCTLS */
-/* ----------------------------------------------------------- */
+/*
+ * RADIO ESPECIFIC IOCTLS
+ */
static int radio_g_tuner(struct file *file, void *priv,
struct v4l2_tuner *t)
@@ -2002,7 +2057,7 @@ static int radio_s_tuner(struct file *file, void *priv,
{
struct em28xx *dev = video_drvdata(file);
- if (0 != t->index)
+ if (t->index != 0)
return -EINVAL;
v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, s_tuner, t);
@@ -2097,7 +2152,7 @@ static int em28xx_v4l2_open(struct file *filp)
* em28xx_v4l2_fini()
* unregisters the v4l2,i2c and usb devices
* called when the device gets disconected or at module unload
-*/
+ */
static int em28xx_v4l2_fini(struct em28xx *dev)
{
struct em28xx_v4l2 *v4l2 = dev->v4l2;
@@ -2112,7 +2167,7 @@ static int em28xx_v4l2_fini(struct em28xx *dev)
return 0;
}
- if (v4l2 == NULL)
+ if (!v4l2)
return 0;
dev_info(&dev->intf->dev, "Closing video extension\n");
@@ -2127,17 +2182,17 @@ static int em28xx_v4l2_fini(struct em28xx *dev)
if (video_is_registered(&v4l2->radio_dev)) {
dev_info(&dev->intf->dev, "V4L2 device %s deregistered\n",
- video_device_node_name(&v4l2->radio_dev));
+ video_device_node_name(&v4l2->radio_dev));
video_unregister_device(&v4l2->radio_dev);
}
if (video_is_registered(&v4l2->vbi_dev)) {
dev_info(&dev->intf->dev, "V4L2 device %s deregistered\n",
- video_device_node_name(&v4l2->vbi_dev));
+ video_device_node_name(&v4l2->vbi_dev));
video_unregister_device(&v4l2->vbi_dev);
}
if (video_is_registered(&v4l2->vdev)) {
dev_info(&dev->intf->dev, "V4L2 device %s deregistered\n",
- video_device_node_name(&v4l2->vdev));
+ video_device_node_name(&v4l2->vdev));
video_unregister_device(&v4l2->vdev);
}
@@ -2189,7 +2244,7 @@ static int em28xx_v4l2_close(struct file *filp)
struct em28xx *dev = video_drvdata(filp);
struct em28xx_v4l2 *v4l2 = dev->v4l2;
struct usb_device *udev = interface_to_usbdev(dev->intf);
- int errCode;
+ int err;
em28xx_videodbg("users=%d\n", v4l2->users);
@@ -2202,7 +2257,7 @@ static int em28xx_v4l2_close(struct file *filp)
goto exit;
/* Save some power by putting tuner to sleep */
- v4l2_device_call_all(&v4l2->v4l2_dev, 0, core, s_power, 0);
+ v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, standby);
/* do this before setting alternate! */
em28xx_set_mode(dev, EM28XX_SUSPEND);
@@ -2210,11 +2265,11 @@ static int em28xx_v4l2_close(struct file *filp)
/* set alternate 0 */
dev->alt = 0;
em28xx_videodbg("setting alternate 0\n");
- errCode = usb_set_interface(udev, 0, 0);
- if (errCode < 0) {
+ err = usb_set_interface(udev, 0, 0);
+ if (err < 0) {
dev_err(&dev->intf->dev,
"cannot change alternate number to 0 (error=%i)\n",
- errCode);
+ err);
}
}
@@ -2343,7 +2398,7 @@ static void em28xx_vdev_init(struct em28xx *dev,
*vfd = *template;
vfd->v4l2_dev = &dev->v4l2->v4l2_dev;
vfd->lock = &dev->lock;
- if (dev->board.is_webcam)
+ if (dev->is_webcam)
vfd->tvnorms = 0;
snprintf(vfd->name, sizeof(vfd->name), "%s %s",
@@ -2372,7 +2427,7 @@ static void em28xx_tuner_setup(struct em28xx *dev, unsigned short tuner_addr)
0, tuner, s_type_addr, &tun_setup);
}
- if ((dev->tuner_type != TUNER_ABSENT) && (dev->tuner_type)) {
+ if (dev->tuner_type != TUNER_ABSENT && dev->tuner_type) {
tun_setup.type = dev->tuner_type;
tun_setup.addr = tuner_addr;
@@ -2435,7 +2490,7 @@ static int em28xx_v4l2_init(struct em28xx *dev)
mutex_lock(&dev->lock);
- v4l2 = kzalloc(sizeof(struct em28xx_v4l2), GFP_KERNEL);
+ v4l2 = kzalloc(sizeof(*v4l2), GFP_KERNEL);
if (!v4l2) {
mutex_unlock(&dev->lock);
return -ENOMEM;
@@ -2458,7 +2513,7 @@ static int em28xx_v4l2_init(struct em28xx *dev)
v4l2_ctrl_handler_init(hdl, 8);
v4l2->v4l2_dev.ctrl_handler = hdl;
- if (dev->board.is_webcam)
+ if (dev->is_webcam)
v4l2->progressive = true;
/*
@@ -2470,7 +2525,7 @@ static int em28xx_v4l2_init(struct em28xx *dev)
/* request some modules */
- if (dev->board.has_msp34xx)
+ if (dev->has_msp34xx)
v4l2_i2c_new_subdev(&v4l2->v4l2_dev,
&dev->i2c_adap[dev->def_i2c_bus],
"msp3400", 0, msp3400_addrs);
@@ -2559,7 +2614,7 @@ static int em28xx_v4l2_init(struct em28xx *dev)
INIT_LIST_HEAD(&dev->vidq.active);
INIT_LIST_HEAD(&dev->vbiq.active);
- if (dev->board.has_msp34xx) {
+ if (dev->has_msp34xx) {
/* Send a reset to other chips via gpio */
ret = em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xf7);
if (ret < 0) {
@@ -2568,7 +2623,7 @@ static int em28xx_v4l2_init(struct em28xx *dev)
__func__, ret);
goto unregister_dev;
}
- msleep(3);
+ usleep_range(10000, 11000);
ret = em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xff);
if (ret < 0) {
@@ -2577,7 +2632,7 @@ static int em28xx_v4l2_init(struct em28xx *dev)
__func__, ret);
goto unregister_dev;
}
- msleep(3);
+ usleep_range(10000, 11000);
}
/* set default norm */
@@ -2589,8 +2644,10 @@ static int em28xx_v4l2_init(struct em28xx *dev)
v4l2->format = &format[0];
maxw = norm_maxw(dev);
- /* MaxPacketSize for em2800 is too small to capture at full resolution
- * use half of maxw as the scaler can only scale to 50% */
+ /*
+ * MaxPacketSize for em2800 is too small to capture at full resolution
+ * use half of maxw as the scaler can only scale to 50%
+ */
if (dev->board.is_em2800)
maxw /= 2;
@@ -2611,29 +2668,33 @@ static int em28xx_v4l2_init(struct em28xx *dev)
em28xx_set_outfmt(dev);
/* Add image controls */
- /* NOTE: at this point, the subdevices are already registered, so bridge
- * controls are only added/enabled when no subdevice provides them */
- if (NULL == v4l2_ctrl_find(hdl, V4L2_CID_CONTRAST))
+
+ /*
+ * NOTE: at this point, the subdevices are already registered, so
+ * bridge controls are only added/enabled when no subdevice provides
+ * them
+ */
+ if (!v4l2_ctrl_find(hdl, V4L2_CID_CONTRAST))
v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops,
V4L2_CID_CONTRAST,
0, 0x1f, 1, CONTRAST_DEFAULT);
- if (NULL == v4l2_ctrl_find(hdl, V4L2_CID_BRIGHTNESS))
+ if (!v4l2_ctrl_find(hdl, V4L2_CID_BRIGHTNESS))
v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops,
V4L2_CID_BRIGHTNESS,
-0x80, 0x7f, 1, BRIGHTNESS_DEFAULT);
- if (NULL == v4l2_ctrl_find(hdl, V4L2_CID_SATURATION))
+ if (!v4l2_ctrl_find(hdl, V4L2_CID_SATURATION))
v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops,
V4L2_CID_SATURATION,
0, 0x1f, 1, SATURATION_DEFAULT);
- if (NULL == v4l2_ctrl_find(hdl, V4L2_CID_BLUE_BALANCE))
+ if (!v4l2_ctrl_find(hdl, V4L2_CID_BLUE_BALANCE))
v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops,
V4L2_CID_BLUE_BALANCE,
-0x30, 0x30, 1, BLUE_BALANCE_DEFAULT);
- if (NULL == v4l2_ctrl_find(hdl, V4L2_CID_RED_BALANCE))
+ if (!v4l2_ctrl_find(hdl, V4L2_CID_RED_BALANCE))
v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops,
V4L2_CID_RED_BALANCE,
-0x30, 0x30, 1, RED_BALANCE_DEFAULT);
- if (NULL == v4l2_ctrl_find(hdl, V4L2_CID_SHARPNESS))
+ if (!v4l2_ctrl_find(hdl, V4L2_CID_SHARPNESS))
v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops,
V4L2_CID_SHARPNESS,
0, 0x0f, 1, SHARPNESS_DEFAULT);
@@ -2653,7 +2714,7 @@ static int em28xx_v4l2_init(struct em28xx *dev)
v4l2->vdev.queue->lock = &v4l2->vb_queue_lock;
/* disable inapplicable ioctls */
- if (dev->board.is_webcam) {
+ if (dev->is_webcam) {
v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_QUERYSTD);
v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_G_STD);
v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_STD);
@@ -2683,7 +2744,7 @@ static int em28xx_v4l2_init(struct em28xx *dev)
/* Allocate and fill vbi video_device struct */
if (em28xx_vbi_supported(dev) == 1) {
em28xx_vdev_init(dev, &v4l2->vbi_dev, &em28xx_video_template,
- "vbi");
+ "vbi");
v4l2->vbi_dev.queue = &v4l2->vb_vbiq;
v4l2->vbi_dev.queue->lock = &v4l2->vb_vbi_queue_lock;
@@ -2713,7 +2774,7 @@ static int em28xx_v4l2_init(struct em28xx *dev)
if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) {
em28xx_vdev_init(dev, &v4l2->radio_dev, &em28xx_radio_template,
- "radio");
+ "radio");
ret = video_register_device(&v4l2->radio_dev, VFL_TYPE_RADIO,
radio_nr[dev->devno]);
if (ret < 0) {
@@ -2749,7 +2810,7 @@ static int em28xx_v4l2_init(struct em28xx *dev)
video_device_node_name(&v4l2->vbi_dev));
/* Save some power by putting tuner to sleep */
- v4l2_device_call_all(&v4l2->v4l2_dev, 0, core, s_power, 0);
+ v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, standby);
/* initialize videobuf2 stuff */
em28xx_vb2_setup(dev);
diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h
index 88084f24f033..63c7c6124707 100644
--- a/drivers/media/usb/em28xx/em28xx.h
+++ b/drivers/media/usb/em28xx/em28xx.h
@@ -1,31 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
- em28xx.h - driver for Empia EM2800/EM2820/2840 USB video capture devices
-
- Copyright (C) 2005 Markus Rechberger <mrechberger@gmail.com>
- Ludovico Cavedon <cavedon@sssup.it>
- Mauro Carvalho Chehab <mchehab@infradead.org>
- Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.com>
-
- Based on the em2800 driver from Sascha Sommer <saschasommer@freenet.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 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.
+ * em28xx.h - driver for Empia EM2800/EM2820/2840 USB video capture devices
+ *
+ * Copyright (C) 2005 Markus Rechberger <mrechberger@gmail.com>
+ * Ludovico Cavedon <cavedon@sssup.it>
+ * Mauro Carvalho Chehab <mchehab@infradead.org>
+ * Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.com>
+ *
+ * Based on the em2800 driver from Sascha Sommer <saschasommer@freenet.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 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.
*/
#ifndef _EM28XX_H
#define _EM28XX_H
+#include <linux/bitfield.h>
+
#define EM28XX_VERSION "0.2.2"
#define DRIVER_DESC "Empia em28xx device driver"
@@ -166,7 +165,7 @@
#define EM28XX_STOP_AUDIO 0
/* maximum number of em28xx boards */
-#define EM28XX_MAXBOARDS 4 /*FIXME: should be bigger */
+#define EM28XX_MAXBOARDS DVB_MAX_ADAPTERS /* All adapters could be em28xx */
/* maximum number of frames that can be queued */
#define EM28XX_NUM_FRAMES 5
@@ -180,43 +179,32 @@
/* max number of I2C buses on em28xx devices */
#define NUM_I2C_BUSES 2
-/* isoc transfers: number of packets for each buffer
- windows requests only 64 packets .. so we better do the same
- this is what I found out for all alternate numbers there!
+/*
+ * isoc transfers: number of packets for each buffer
+ * windows requests only 64 packets .. so we better do the same
+ * this is what I found out for all alternate numbers there!
*/
#define EM28XX_NUM_ISOC_PACKETS 64
#define EM28XX_DVB_NUM_ISOC_PACKETS 64
-/* bulk transfers: transfer buffer size = packet size * packet multiplier
- USB 2.0 spec says bulk packet size is always 512 bytes
+/*
+ * bulk transfers: transfer buffer size = packet size * packet multiplier
+ * USB 2.0 spec says bulk packet size is always 512 bytes
*/
#define EM28XX_BULK_PACKET_MULTIPLIER 384
-#define EM28XX_DVB_BULK_PACKET_MULTIPLIER 384
+#define EM28XX_DVB_BULK_PACKET_MULTIPLIER 94
#define EM28XX_INTERLACED_DEFAULT 1
-/*
- * Time in msecs to wait for i2c xfers to finish.
- * 35ms is the maximum time a SMBUS device could wait when
- * clock stretching is used. As the transfer itself will take
- * some time to happen, set it to 35 ms.
- *
- * Ok, I2C doesn't specify any limit. So, eventually, we may need
- * to increase this timeout.
- *
- * FIXME: this assumes that an I2C message is not longer than 1ms.
- * This is actually dependent on the I2C bus speed, although most
- * devices use a 100kHz clock. So, this assumtion is true most of
- * the time.
- */
-#define EM28XX_I2C_XFER_TIMEOUT 36
-
/* time in msecs to wait for AC97 xfers to finish */
#define EM28XX_AC97_XFER_TIMEOUT 100
/* max. number of button state polling addresses */
#define EM28XX_NUM_BUTTON_ADDRESSES_MAX 5
+#define PRIMARY_TS 0
+#define SECONDARY_TS 1
+
enum em28xx_mode {
EM28XX_SUSPEND,
EM28XX_ANALOG_MODE,
@@ -225,64 +213,83 @@ enum em28xx_mode {
struct em28xx;
+/**
+ * struct em28xx_usb_bufs - Contains URB-related buffer data
+ *
+ * @max_pkt_size: max packet size of isoc transaction
+ * @num_packets: number of packets in each buffer
+ * @num_bufs: number of allocated urb
+ * @urb: urb for isoc/bulk transfers
+ * @buf: transfer buffers for isoc/bulk transfer
+ */
struct em28xx_usb_bufs {
- /* max packet size of isoc transaction */
int max_pkt_size;
-
- /* number of packets in each buffer */
int num_packets;
-
- /* number of allocated urbs */
int num_bufs;
-
- /* urb for isoc/bulk transfers */
struct urb **urb;
-
- /* transfer buffers for isoc/bulk transfer */
- char **transfer_buffer;
+ char **buf;
};
+/**
+ * struct em28xx_usb_ctl - Contains URB-related buffer data
+ *
+ * @analog_bufs: isoc/bulk transfer buffers for analog mode
+ * @digital_bufs: isoc/bulk transfer buffers for digital mode
+ * @vid_buf: Stores already requested video buffers
+ * @vbi_buf: Stores already requested VBI buffers
+ * @urb_data_copy: copy data from URB
+ */
struct em28xx_usb_ctl {
- /* isoc/bulk transfer buffers for analog mode */
struct em28xx_usb_bufs analog_bufs;
-
- /* isoc/bulk transfer buffers for digital mode */
struct em28xx_usb_bufs digital_bufs;
-
- /* Stores already requested buffers */
struct em28xx_buffer *vid_buf;
struct em28xx_buffer *vbi_buf;
-
- /* copy data from URB */
int (*urb_data_copy)(struct em28xx *dev, struct urb *urb);
-
};
-/* Struct to enumberate video formats */
+/**
+ * struct em28xx_fmt - Struct to enumberate video formats
+ *
+ * @name: Name for the video standard
+ * @fourcc: v4l2 format id
+ * @depth: mean number of bits to represent a pixel
+ * @reg: em28xx register value to set it
+ */
struct em28xx_fmt {
- char *name;
- u32 fourcc; /* v4l2 format id */
- int depth;
- int reg;
+ char *name;
+ u32 fourcc;
+ int depth;
+ int reg;
};
-/* buffer for one video frame */
+/**
+ * struct em28xx_buffer- buffer for storing one video frame
+ *
+ * @vb: common v4l buffer stuff
+ * @list: List to associate it with the other buffers
+ * @mem: pointer to the buffer, as returned by vb2_plane_vaddr()
+ * @length: length of the buffer, as returned by vb2_plane_size()
+ * @top_field: If non-zero, indicate that the buffer is the top field
+ * @pos: Indicate the next position of the buffer to be filled.
+ * @vb_buf: pointer to vmalloc memory address in vb
+ *
+ * .. note::
+ *
+ * in interlaced mode, @pos is reset to zero at the start of each new
+ * field (not frame !)
+ */
struct em28xx_buffer {
- /* common v4l buffer stuff -- must be first */
- struct vb2_v4l2_buffer vb;
- struct list_head list;
+ struct vb2_v4l2_buffer vb; /* must be first */
- void *mem;
- unsigned int length;
- int top_field;
+ struct list_head list;
- /* counter to control buffer fill */
- unsigned int pos;
- /* NOTE; in interlaced mode, this value is reset to zero at
- * the start of each new field (not frame !) */
+ void *mem;
+ unsigned int length;
+ int top_field;
- /* pointer to vmalloc memory address in vb */
- char *vb_buf;
+ unsigned int pos;
+
+ char *vb_buf;
};
struct em28xx_dmaqueue {
@@ -324,20 +331,48 @@ enum em28xx_usb_audio_type {
EM28XX_USB_AUDIO_VENDOR,
};
-/* em28xx has two audio inputs: tuner and line in.
- However, on most devices, an auxiliary AC97 codec device is used.
- The AC97 device may have several different inputs and outputs,
- depending on their model. So, it is possible to use AC97 mixer to
- address more than two different entries.
+/**
+ * em28xx_amux - describes the type of audio input used by em28xx
+ *
+ * @EM28XX_AMUX_VIDEO:
+ * On devices without AC97, this is the only value that it is currently
+ * allowed.
+ * On devices with AC97, it corresponds to the AC97 mixer "Video" control.
+ * @EM28XX_AMUX_LINE_IN:
+ * Only for devices with AC97. Corresponds to AC97 mixer "Line In".
+ * @EM28XX_AMUX_VIDEO2:
+ * Only for devices with AC97. It means that em28xx should use "Line In"
+ * And AC97 should use the "Video" mixer control.
+ * @EM28XX_AMUX_PHONE:
+ * Only for devices with AC97. Corresponds to AC97 mixer "Phone".
+ * @EM28XX_AMUX_MIC:
+ * Only for devices with AC97. Corresponds to AC97 mixer "Mic".
+ * @EM28XX_AMUX_CD:
+ * Only for devices with AC97. Corresponds to AC97 mixer "CD".
+ * @EM28XX_AMUX_AUX:
+ * Only for devices with AC97. Corresponds to AC97 mixer "Aux".
+ * @EM28XX_AMUX_PCM_OUT:
+ * Only for devices with AC97. Corresponds to AC97 mixer "PCM out".
+ *
+ * The em28xx chip itself has only two audio inputs: tuner and line in.
+ * On almost all devices, only the tuner input is used.
+ *
+ * However, on most devices, an auxiliary AC97 codec device is used,
+ * usually connected to the em28xx tuner input (except for
+ * @EM28XX_AMUX_LINE_IN).
+ *
+ * The AC97 device typically have several different inputs and outputs.
+ * The exact number and description depends on their model.
+ *
+ * It is possible to AC97 to mixer more than one different entries at the
+ * same time, via the alsa mux.
*/
enum em28xx_amux {
- /* This is the only entry for em28xx tuner input */
- EM28XX_AMUX_VIDEO, /* em28xx tuner, AC97 mixer Video */
-
- EM28XX_AMUX_LINE_IN, /* AC97 mixer Line In */
+ EM28XX_AMUX_VIDEO,
+ EM28XX_AMUX_LINE_IN,
/* Some less-common mixer setups */
- EM28XX_AMUX_VIDEO2, /* em28xx Line in, AC97 mixer Video */
+ EM28XX_AMUX_VIDEO2,
EM28XX_AMUX_PHONE,
EM28XX_AMUX_MIC,
EM28XX_AMUX_CD,
@@ -347,14 +382,14 @@ enum em28xx_amux {
enum em28xx_aout {
/* AC97 outputs */
- EM28XX_AOUT_MASTER = 1 << 0,
- EM28XX_AOUT_LINE = 1 << 1,
- EM28XX_AOUT_MONO = 1 << 2,
- EM28XX_AOUT_LFE = 1 << 3,
- EM28XX_AOUT_SURR = 1 << 4,
+ EM28XX_AOUT_MASTER = BIT(0),
+ EM28XX_AOUT_LINE = BIT(1),
+ EM28XX_AOUT_MONO = BIT(2),
+ EM28XX_AOUT_LFE = BIT(3),
+ EM28XX_AOUT_SURR = BIT(4),
/* PCM IN Mixer - used by AC97_RECORD_SELECT register */
- EM28XX_AOUT_PCM_IN = 1 << 7,
+ EM28XX_AOUT_PCM_IN = BIT(7),
/* Bits 10-8 are used to indicate the PCM IN record select */
EM28XX_AOUT_PCM_MIC_PCM = 0 << 8,
@@ -383,7 +418,7 @@ struct em28xx_input {
unsigned int vmux;
enum em28xx_amux amux;
enum em28xx_aout aout;
- struct em28xx_reg_seq *gpio;
+ const struct em28xx_reg_seq *gpio;
};
#define INPUT(nr) (&em28xx_boards[dev->model].input[nr])
@@ -441,22 +476,23 @@ struct em28xx_board {
int vchannels;
int tuner_type;
int tuner_addr;
- unsigned def_i2c_bus; /* Default I2C bus */
+ unsigned int def_i2c_bus; /* Default I2C bus */
/* i2c flags */
unsigned int tda9887_conf;
/* GPIO sequences */
- struct em28xx_reg_seq *dvb_gpio;
- struct em28xx_reg_seq *suspend_gpio;
- struct em28xx_reg_seq *tuner_gpio;
- struct em28xx_reg_seq *mute_gpio;
+ const struct em28xx_reg_seq *dvb_gpio;
+ const struct em28xx_reg_seq *suspend_gpio;
+ const struct em28xx_reg_seq *tuner_gpio;
+ const struct em28xx_reg_seq *mute_gpio;
unsigned int is_em2800:1;
unsigned int has_msp34xx:1;
unsigned int mts_firmware:1;
unsigned int max_range_640_480:1;
unsigned int has_dvb:1;
+ unsigned int has_dual_ts:1;
unsigned int is_webcam:1;
unsigned int valid:1;
unsigned int has_ir_i2c:1;
@@ -476,7 +512,7 @@ struct em28xx_board {
struct em28xx_led *leds;
/* Buttons */
- struct em28xx_button *buttons;
+ const struct em28xx_button *buttons;
};
struct em28xx_eeprom {
@@ -519,8 +555,8 @@ struct em28xx_v4l2 {
/* Videobuf2 */
struct vb2_queue vb_vidq;
struct vb2_queue vb_vbiq;
- struct mutex vb_queue_lock;
- struct mutex vb_vbi_queue_lock;
+ struct mutex vb_queue_lock; /* Protects vb_vidq */
+ struct mutex vb_vbi_queue_lock; /* Protects vb_vbiq */
u8 vinmode;
u8 vinctl;
@@ -546,8 +582,8 @@ struct em28xx_v4l2 {
/* Frame properties */
int width; /* current frame width */
int height; /* current frame height */
- unsigned hscale; /* horizontal scale factor (see datasheet) */
- unsigned vscale; /* vertical scale factor (see datasheet) */
+ unsigned int hscale; /* horizontal scale factor (see datasheet) */
+ unsigned int vscale; /* vertical scale factor (see datasheet) */
unsigned int vbi_width;
unsigned int vbi_height; /* lines per field */
@@ -565,7 +601,7 @@ struct em28xx_v4l2 {
struct em28xx_audio {
char name[50];
- unsigned num_urb;
+ unsigned int num_urb;
char **transfer_buffer;
struct urb **urb;
struct usb_device *udev;
@@ -578,7 +614,7 @@ struct em28xx_audio {
size_t period;
int users;
- spinlock_t slock;
+ spinlock_t slock; /* Protects struct em28xx_audio */
/* Controls streaming */
struct work_struct wq_trigger; /* trigger to start/stop audio */
@@ -596,7 +632,7 @@ enum em28xx_i2c_algo_type {
struct em28xx_i2c_bus {
struct em28xx *dev;
- unsigned bus;
+ unsigned int bus;
enum em28xx_i2c_algo_type algo_type;
};
@@ -604,102 +640,111 @@ struct em28xx_i2c_bus {
struct em28xx {
struct kref ref;
- /* Sub-module data */
+ // Sub-module data
struct em28xx_v4l2 *v4l2;
struct em28xx_dvb *dvb;
struct em28xx_audio adev;
struct em28xx_IR *ir;
- /* generic device properties */
- int model; /* index in the device_data struct */
- int devno; /* marks the number of this device */
+ // generic device properties
+ int model; // index in the device_data struct
+ int devno; // marks the number of this device
enum em28xx_chip_id chip_id;
- unsigned int is_em25xx:1; /* em25xx/em276x/7x/8x family bridge */
- unsigned char disconnected:1; /* device has been diconnected */
+ unsigned int is_em25xx:1; // em25xx/em276x/7x/8x family bridge
+ unsigned int disconnected:1; // device has been diconnected
unsigned int has_video:1;
unsigned int is_audio_only:1;
+ unsigned int is_webcam:1;
+ unsigned int has_msp34xx:1;
+ unsigned int i2c_speed:2;
enum em28xx_int_audio_type int_audio_type;
enum em28xx_usb_audio_type usb_audio_type;
+ unsigned char name[32];
struct em28xx_board board;
- enum em28xx_sensor em28xx_sensor; /* camera specific */
+ enum em28xx_sensor em28xx_sensor; // camera specific
- /* Some older em28xx chips needs a waiting time after writing */
+ // Some older em28xx chips needs a waiting time after writing
unsigned int wait_after_write;
struct list_head devlist;
- u32 i2s_speed; /* I2S speed for audio digital stream */
+ u32 i2s_speed; // I2S speed for audio digital stream
struct em28xx_audio_mode audio_mode;
- int tuner_type; /* type of the tuner */
+ int tuner_type; // type of the tuner
- /* i2c i/o */
+ // i2c i/o
struct i2c_adapter i2c_adap[NUM_I2C_BUSES];
struct i2c_client i2c_client[NUM_I2C_BUSES];
struct em28xx_i2c_bus i2c_bus[NUM_I2C_BUSES];
unsigned char eeprom_addrwidth_16bit:1;
- unsigned def_i2c_bus; /* Default I2C bus */
- unsigned cur_i2c_bus; /* Current I2C bus */
+ unsigned int def_i2c_bus; // Default I2C bus
+ unsigned int cur_i2c_bus; // Current I2C bus
struct rt_mutex i2c_bus_lock;
- /* video for linux */
- unsigned int ctl_input; /* selected input */
- unsigned int ctl_ainput;/* selected audio input */
- unsigned int ctl_aoutput;/* selected audio output */
+ // video for linux
+ unsigned int ctl_input; // selected input
+ unsigned int ctl_ainput;// selected audio input
+ unsigned int ctl_aoutput;// selected audio output
int mute;
int volume;
- unsigned long hash; /* eeprom hash - for boards with generic ID */
- unsigned long i2c_hash; /* i2c devicelist hash -
- for boards with generic ID */
+ unsigned long hash; // eeprom hash - for boards with generic ID
+ unsigned long i2c_hash; // i2c devicelist hash -
+ // for boards with generic ID
struct work_struct request_module_wk;
- /* locks */
- struct mutex lock;
+ // locks
+ struct mutex lock; /* protects em28xx struct */
struct mutex ctrl_urb_lock; /* protects urb_buf */
- /* resources in use */
+ // resources in use
unsigned int resources;
- /* eeprom content */
+ // eeprom content
u8 *eedata;
u16 eedata_len;
- /* Isoc control struct */
+ // Isoc control struct
struct em28xx_dmaqueue vidq;
struct em28xx_dmaqueue vbiq;
struct em28xx_usb_ctl usb_ctl;
- spinlock_t slock;
-
- /* usb transfer */
- struct usb_interface *intf; /* the usb interface */
- u8 ifnum; /* number of the assigned usb interface */
- u8 analog_ep_isoc; /* address of isoc endpoint for analog */
- u8 analog_ep_bulk; /* address of bulk endpoint for analog */
- u8 dvb_ep_isoc; /* address of isoc endpoint for DVB */
- u8 dvb_ep_bulk; /* address of bulk endpoint for DVB */
- int alt; /* alternate setting */
- int max_pkt_size; /* max packet size of the selected ep at alt */
- int packet_multiplier; /* multiplier for wMaxPacketSize, used for
- URB buffer size definition */
- int num_alt; /* number of alternative settings */
- unsigned int *alt_max_pkt_size_isoc; /* array of isoc wMaxPacketSize */
- unsigned int analog_xfer_bulk:1; /* use bulk instead of isoc
- transfers for analog */
- int dvb_alt_isoc; /* alternate setting for DVB isoc transfers */
- unsigned int dvb_max_pkt_size_isoc; /* isoc max packet size of the
- selected DVB ep at dvb_alt */
- unsigned int dvb_xfer_bulk:1; /* use bulk instead of isoc
- transfers for DVB */
- char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */
-
- /* helper funcs that call usb_control_msg */
+
+ spinlock_t slock; /* Protects em28xx video/vbi/dvb IRQ stream data */
+
+ // usb transfer
+ struct usb_interface *intf; // the usb interface
+ u8 ifnum; // number of the assigned usb interface
+ u8 analog_ep_isoc; // address of isoc endpoint for analog
+ u8 analog_ep_bulk; // address of bulk endpoint for analog
+ u8 dvb_ep_isoc_ts2; // address of isoc endpoint for DVB TS2
+ u8 dvb_ep_bulk_ts2; // address of bulk endpoint for DVB TS2
+ u8 dvb_ep_isoc; // address of isoc endpoint for DVB
+ u8 dvb_ep_bulk; // address of bulk endpoint for DVB
+ int alt; // alternate setting
+ int max_pkt_size; // max packet size of the selected ep at alt
+ int packet_multiplier; // multiplier for wMaxPacketSize, used for
+ // URB buffer size definition
+ int num_alt; // number of alternative settings
+ unsigned int *alt_max_pkt_size_isoc; // array of isoc wMaxPacketSize
+ unsigned int analog_xfer_bulk:1; // use bulk instead of isoc
+ // transfers for analog
+ int dvb_alt_isoc; // alternate setting for DVB isoc transfers
+ unsigned int dvb_max_pkt_size_isoc; // isoc max packet size of the
+ // selected DVB ep at dvb_alt
+ unsigned int dvb_max_pkt_size_isoc_ts2; // isoc max packet size of the
+ // selected DVB ep at dvb_alt
+ unsigned int dvb_xfer_bulk:1; // use bulk instead of isoc
+ // transfers for DVB
+ char urb_buf[URB_MAX_CTRL_SIZE]; // urb control msg buffer
+
+ // helper funcs that call usb_control_msg
int (*em28xx_write_regs)(struct em28xx *dev, u16 reg,
char *buf, int len);
int (*em28xx_read_reg)(struct em28xx *dev, u16 reg);
@@ -711,14 +756,14 @@ struct em28xx {
enum em28xx_mode mode;
- /* Button state polling */
+ // Button state polling
struct delayed_work buttons_query_work;
u8 button_polling_addresses[EM28XX_NUM_BUTTON_ADDRESSES_MAX];
u8 button_polling_last_values[EM28XX_NUM_BUTTON_ADDRESSES_MAX];
u8 num_button_polling_addresses;
- u16 button_polling_interval; /* [ms] */
- /* Snapshot button input device */
- char snapshot_button_path[30]; /* path of the input dev */
+ u16 button_polling_interval; // [ms]
+ // Snapshot button input device
+ char snapshot_button_path[30]; // path of the input dev
struct input_dev *sbutton_input_dev;
#ifdef CONFIG_MEDIA_CONTROLLER
@@ -726,6 +771,9 @@ struct em28xx {
struct media_entity input_ent[MAX_EM28XX_INPUT];
struct media_pad input_pad[MAX_EM28XX_INPUT];
#endif
+
+ struct em28xx *dev_next;
+ int ts;
};
#define kref_to_dev(d) container_of(d, struct em28xx, ref)
@@ -734,17 +782,17 @@ struct em28xx_ops {
struct list_head next;
char *name;
int id;
- int (*init)(struct em28xx *);
- int (*fini)(struct em28xx *);
- int (*suspend)(struct em28xx *);
- int (*resume)(struct em28xx *);
+ int (*init)(struct em28xx *dev);
+ int (*fini)(struct em28xx *dev);
+ int (*suspend)(struct em28xx *dev);
+ int (*resume)(struct em28xx *dev);
};
/* Provided by em28xx-i2c.c */
-void em28xx_do_i2c_scan(struct em28xx *dev, unsigned bus);
-int em28xx_i2c_register(struct em28xx *dev, unsigned bus,
+void em28xx_do_i2c_scan(struct em28xx *dev, unsigned int bus);
+int em28xx_i2c_register(struct em28xx *dev, unsigned int bus,
enum em28xx_i2c_algo_type algo_type);
-int em28xx_i2c_unregister(struct em28xx *dev, unsigned bus);
+int em28xx_i2c_unregister(struct em28xx *dev, unsigned int bus);
/* Provided by em28xx-core.c */
int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg,
@@ -778,7 +826,7 @@ int em28xx_init_usb_xfer(struct em28xx *dev, enum em28xx_mode mode,
void em28xx_uninit_usb_xfer(struct em28xx *dev, enum em28xx_mode mode);
void em28xx_stop_urbs(struct em28xx *dev);
int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode);
-int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio);
+int em28xx_gpio_set(struct em28xx *dev, const struct em28xx_reg_seq *gpio);
int em28xx_register_extension(struct em28xx_ops *dev);
void em28xx_unregister_extension(struct em28xx_ops *dev);
void em28xx_init_extension(struct em28xx *dev);
@@ -787,7 +835,7 @@ int em28xx_suspend_extension(struct em28xx *dev);
int em28xx_resume_extension(struct em28xx *dev);
/* Provided by em28xx-cards.c */
-extern struct em28xx_board em28xx_boards[];
+extern const struct em28xx_board em28xx_boards[];
extern struct usb_device_id em28xx_id_table[];
int em28xx_tuner_callback(void *ptr, int component, int command, int arg);
void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl);
diff --git a/drivers/media/usb/go7007/snd-go7007.c b/drivers/media/usb/go7007/snd-go7007.c
index c618764480c6..f84a2130f033 100644
--- a/drivers/media/usb/go7007/snd-go7007.c
+++ b/drivers/media/usb/go7007/snd-go7007.c
@@ -227,7 +227,7 @@ int go7007_snd_init(struct go7007 *go)
{
static int dev;
struct go7007_snd *gosnd;
- int ret = 0;
+ int ret;
if (dev >= SNDRV_CARDS)
return -ENODEV;
diff --git a/drivers/media/usb/gspca/Kconfig b/drivers/media/usb/gspca/Kconfig
index d214a21acff7..bc9a439745aa 100644
--- a/drivers/media/usb/gspca/Kconfig
+++ b/drivers/media/usb/gspca/Kconfig
@@ -7,7 +7,7 @@ menuconfig USB_GSPCA
Say Y here if you want to enable selecting webcams based
on the GSPCA framework.
- See <file:Documentation/video4linux/gspca.txt> for more info.
+ See <file:Documentation/media/v4l-drivers/gspca-cardlist.rst> for more info.
This driver uses the Video For Linux API. You must say Y or M to
"Video For Linux" to use this driver.
diff --git a/drivers/media/usb/gspca/dtcs033.c b/drivers/media/usb/gspca/dtcs033.c
index cdf27cf0112a..7654c8c08eda 100644
--- a/drivers/media/usb/gspca/dtcs033.c
+++ b/drivers/media/usb/gspca/dtcs033.c
@@ -76,12 +76,10 @@ static int reg_reqs(struct gspca_dev *gspca_dev,
} else if (preq->bRequestType & USB_DIR_IN) {
gspca_dbg(gspca_dev, D_STREAM,
- "USB IN (%d) returned[%d] %02X %02X %02X %s\n",
+ "USB IN (%d) returned[%d] %3ph %s",
i,
preq->wLength,
- gspca_dev->usb_buf[0],
- gspca_dev->usb_buf[1],
- gspca_dev->usb_buf[2],
+ gspca_dev->usb_buf,
preq->wLength > 3 ? "...\n" : "\n");
}
diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c
index 8c2a86d71e8a..82927eb334c4 100644
--- a/drivers/media/usb/s2255/s2255drv.c
+++ b/drivers/media/usb/s2255/s2255drv.c
@@ -648,8 +648,8 @@ static void s2255_fillbuff(struct s2255_vc *vc,
pr_err("s2255: =======no frame\n");
return;
}
- dprintk(dev, 2, "s2255fill at : Buffer 0x%08lx size= %d\n",
- (unsigned long)vbuf, pos);
+ dprintk(dev, 2, "s2255fill at : Buffer %p size= %d\n",
+ vbuf, pos);
}
@@ -803,10 +803,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
}
if (f->fmt.pix.width >= LINE_SZ_4CIFS_NTSC)
f->fmt.pix.width = LINE_SZ_4CIFS_NTSC;
- else if (f->fmt.pix.width >= LINE_SZ_2CIFS_NTSC)
- f->fmt.pix.width = LINE_SZ_2CIFS_NTSC;
- else if (f->fmt.pix.width >= LINE_SZ_1CIFS_NTSC)
- f->fmt.pix.width = LINE_SZ_1CIFS_NTSC;
else
f->fmt.pix.width = LINE_SZ_1CIFS_NTSC;
} else {
@@ -820,10 +816,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
}
if (f->fmt.pix.width >= LINE_SZ_4CIFS_PAL)
f->fmt.pix.width = LINE_SZ_4CIFS_PAL;
- else if (f->fmt.pix.width >= LINE_SZ_2CIFS_PAL)
- f->fmt.pix.width = LINE_SZ_2CIFS_PAL;
- else if (f->fmt.pix.width >= LINE_SZ_1CIFS_PAL)
- f->fmt.pix.width = LINE_SZ_1CIFS_PAL;
else
f->fmt.pix.width = LINE_SZ_1CIFS_PAL;
}
diff --git a/drivers/media/usb/siano/smsusb.c b/drivers/media/usb/siano/smsusb.c
index f13e4b01b5a5..6d436e9e454f 100644
--- a/drivers/media/usb/siano/smsusb.c
+++ b/drivers/media/usb/siano/smsusb.c
@@ -179,8 +179,7 @@ static int smsusb_submit_urb(struct smsusb_device_t *dev,
smsusb_onresponse,
surb
);
- surb->urb.transfer_dma = surb->cb->phys;
- surb->urb.transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ surb->urb.transfer_flags |= URB_FREE_BUFFER;
return usb_submit_urb(&surb->urb, GFP_ATOMIC);
}
@@ -446,6 +445,7 @@ static int smsusb_init_device(struct usb_interface *intf, int board_id)
dev->in_ep, dev->out_ep);
params.device = &dev->udev->dev;
+ params.usb_device = dev->udev;
params.buffer_size = dev->buffer_size;
params.num_buffers = MAX_BUFFERS;
params.sendrequest_handler = smsusb_sendrequest;
diff --git a/drivers/media/usb/tm6000/tm6000-cards.c b/drivers/media/usb/tm6000/tm6000-cards.c
index 4d5f4cc4887e..70939e96b856 100644
--- a/drivers/media/usb/tm6000/tm6000-cards.c
+++ b/drivers/media/usb/tm6000/tm6000-cards.c
@@ -1174,7 +1174,7 @@ static int tm6000_usb_probe(struct usb_interface *interface,
{
struct usb_device *usbdev;
struct tm6000_core *dev;
- int i, rc = 0;
+ int i, rc;
int nr = 0;
char *speed;
diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c
index 8314d3fa9241..b2399d4266da 100644
--- a/drivers/media/usb/tm6000/tm6000-video.c
+++ b/drivers/media/usb/tm6000/tm6000-video.c
@@ -1346,9 +1346,8 @@ static int __tm6000_open(struct file *file)
fh->width = dev->width;
fh->height = dev->height;
- dprintk(dev, V4L2_DEBUG_OPEN, "Open: fh=0x%08lx, dev=0x%08lx, dev->vidq=0x%08lx\n",
- (unsigned long)fh, (unsigned long)dev,
- (unsigned long)&dev->vidq);
+ dprintk(dev, V4L2_DEBUG_OPEN, "Open: fh=%p, dev=%p, dev->vidq=%p\n",
+ fh, dev, &dev->vidq);
dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty queued=%d\n",
list_empty(&dev->vidq.queued));
dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty active=%d\n",
diff --git a/drivers/media/usb/usbtv/usbtv-core.c b/drivers/media/usb/usbtv/usbtv-core.c
index 127f8a0c098b..5095c380b2c1 100644
--- a/drivers/media/usb/usbtv/usbtv-core.c
+++ b/drivers/media/usb/usbtv/usbtv-core.c
@@ -112,6 +112,8 @@ static int usbtv_probe(struct usb_interface *intf,
return 0;
usbtv_audio_fail:
+ /* we must not free at this point */
+ usb_get_dev(usbtv->udev);
usbtv_video_free(usbtv);
usbtv_video_fail:
@@ -145,6 +147,7 @@ static void usbtv_disconnect(struct usb_interface *intf)
static const struct usb_device_id usbtv_id_table[] = {
{ USB_DEVICE(0x1b71, 0x3002) },
{ USB_DEVICE(0x1f71, 0x3301) },
+ { USB_DEVICE(0x1f71, 0x3306) },
{}
};
MODULE_DEVICE_TABLE(usb, usbtv_id_table);
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 20397aba6849..102594ec3e97 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -366,10 +366,10 @@ static struct uvc_menu_info exposure_auto_controls[] = {
{ 8, "Aperture Priority Mode" },
};
-static __s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping,
- __u8 query, const __u8 *data)
+static s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping,
+ u8 query, const u8 *data)
{
- __s8 zoom = (__s8)data[0];
+ s8 zoom = (s8)data[0];
switch (query) {
case UVC_GET_CUR:
@@ -385,17 +385,17 @@ static __s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping,
}
static void uvc_ctrl_set_zoom(struct uvc_control_mapping *mapping,
- __s32 value, __u8 *data)
+ s32 value, u8 *data)
{
data[0] = value == 0 ? 0 : (value > 0) ? 1 : 0xff;
data[2] = min((int)abs(value), 0xff);
}
-static __s32 uvc_ctrl_get_rel_speed(struct uvc_control_mapping *mapping,
- __u8 query, const __u8 *data)
+static s32 uvc_ctrl_get_rel_speed(struct uvc_control_mapping *mapping,
+ u8 query, const u8 *data)
{
unsigned int first = mapping->offset / 8;
- __s8 rel = (__s8)data[first];
+ s8 rel = (s8)data[first];
switch (query) {
case UVC_GET_CUR:
@@ -412,7 +412,7 @@ static __s32 uvc_ctrl_get_rel_speed(struct uvc_control_mapping *mapping,
}
static void uvc_ctrl_set_rel_speed(struct uvc_control_mapping *mapping,
- __s32 value, __u8 *data)
+ s32 value, u8 *data)
{
unsigned int first = mapping->offset / 8;
@@ -745,17 +745,17 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = {
* Utility functions
*/
-static inline __u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id)
+static inline u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id)
{
return ctrl->uvc_data + id * ctrl->info.size;
}
-static inline int uvc_test_bit(const __u8 *data, int bit)
+static inline int uvc_test_bit(const u8 *data, int bit)
{
return (data[bit >> 3] >> (bit & 7)) & 1;
}
-static inline void uvc_clear_bit(__u8 *data, int bit)
+static inline void uvc_clear_bit(u8 *data, int bit)
{
data[bit >> 3] &= ~(1 << (bit & 7));
}
@@ -765,20 +765,20 @@ static inline void uvc_clear_bit(__u8 *data, int bit)
* a signed 32bit integer. Sign extension will be performed if the mapping
* references a signed data type.
*/
-static __s32 uvc_get_le_value(struct uvc_control_mapping *mapping,
- __u8 query, const __u8 *data)
+static s32 uvc_get_le_value(struct uvc_control_mapping *mapping,
+ u8 query, const u8 *data)
{
int bits = mapping->size;
int offset = mapping->offset;
- __s32 value = 0;
- __u8 mask;
+ s32 value = 0;
+ u8 mask;
data += offset / 8;
offset &= 7;
mask = ((1LL << bits) - 1) << offset;
for (; bits > 0; data++) {
- __u8 byte = *data & mask;
+ u8 byte = *data & mask;
value |= offset > 0 ? (byte >> offset) : (byte << (-offset));
bits -= 8 - (offset > 0 ? offset : 0);
offset -= 8;
@@ -796,11 +796,11 @@ static __s32 uvc_get_le_value(struct uvc_control_mapping *mapping,
* in the little-endian data stored at 'data' to the value 'value'.
*/
static void uvc_set_le_value(struct uvc_control_mapping *mapping,
- __s32 value, __u8 *data)
+ s32 value, u8 *data)
{
int bits = mapping->size;
int offset = mapping->offset;
- __u8 mask;
+ u8 mask;
/* According to the v4l2 spec, writing any value to a button control
* should result in the action belonging to the button control being
@@ -826,13 +826,13 @@ static void uvc_set_le_value(struct uvc_control_mapping *mapping,
* Terminal and unit management
*/
-static const __u8 uvc_processing_guid[16] = UVC_GUID_UVC_PROCESSING;
-static const __u8 uvc_camera_guid[16] = UVC_GUID_UVC_CAMERA;
-static const __u8 uvc_media_transport_input_guid[16] =
+static const u8 uvc_processing_guid[16] = UVC_GUID_UVC_PROCESSING;
+static const u8 uvc_camera_guid[16] = UVC_GUID_UVC_CAMERA;
+static const u8 uvc_media_transport_input_guid[16] =
UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT;
static int uvc_entity_match_guid(const struct uvc_entity *entity,
- const __u8 guid[16])
+ const u8 guid[16])
{
switch (UVC_ENTITY_TYPE(entity)) {
case UVC_ITT_CAMERA:
@@ -857,7 +857,7 @@ static int uvc_entity_match_guid(const struct uvc_entity *entity,
* UVC Controls
*/
-static void __uvc_find_control(struct uvc_entity *entity, __u32 v4l2_id,
+static void __uvc_find_control(struct uvc_entity *entity, u32 v4l2_id,
struct uvc_control_mapping **mapping, struct uvc_control **control,
int next)
{
@@ -890,7 +890,7 @@ static void __uvc_find_control(struct uvc_entity *entity, __u32 v4l2_id,
}
static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
- __u32 v4l2_id, struct uvc_control_mapping **mapping)
+ u32 v4l2_id, struct uvc_control_mapping **mapping)
{
struct uvc_control *ctrl = NULL;
struct uvc_entity *entity;
@@ -1019,10 +1019,10 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
struct uvc_menu_info *menu;
unsigned int i;
- memset(v4l2_ctrl, 0, sizeof *v4l2_ctrl);
+ memset(v4l2_ctrl, 0, sizeof(*v4l2_ctrl));
v4l2_ctrl->id = mapping->id;
v4l2_ctrl->type = mapping->v4l2_type;
- strlcpy(v4l2_ctrl->name, mapping->name, sizeof v4l2_ctrl->name);
+ strlcpy(v4l2_ctrl->name, mapping->name, sizeof(v4l2_ctrl->name));
v4l2_ctrl->flags = 0;
if (!(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR))
@@ -1182,7 +1182,7 @@ int uvc_query_v4l2_menu(struct uvc_video_chain *chain,
}
}
- strlcpy(query_menu->name, menu_info->name, sizeof query_menu->name);
+ strlcpy(query_menu->name, menu_info->name, sizeof(query_menu->name));
done:
mutex_unlock(&chain->ctrl_mutex);
@@ -1590,6 +1590,36 @@ int uvc_ctrl_set(struct uvc_video_chain *chain,
* Dynamic controls
*/
+/*
+ * Retrieve flags for a given control
+ */
+static int uvc_ctrl_get_flags(struct uvc_device *dev,
+ const struct uvc_control *ctrl,
+ struct uvc_control_info *info)
+{
+ u8 *data;
+ int ret;
+
+ data = kmalloc(1, GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, dev->intfnum,
+ info->selector, data, 1);
+ if (!ret)
+ info->flags = UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX
+ | UVC_CTRL_FLAG_GET_RES | UVC_CTRL_FLAG_GET_DEF
+ | (data[0] & UVC_CONTROL_CAP_GET ?
+ UVC_CTRL_FLAG_GET_CUR : 0)
+ | (data[0] & UVC_CONTROL_CAP_SET ?
+ UVC_CTRL_FLAG_SET_CUR : 0)
+ | (data[0] & UVC_CONTROL_CAP_AUTOUPDATE ?
+ UVC_CTRL_FLAG_AUTO_UPDATE : 0);
+
+ kfree(data);
+ return ret;
+}
+
static void uvc_ctrl_fixup_xu_info(struct uvc_device *dev,
const struct uvc_control *ctrl, struct uvc_control_info *info)
{
@@ -1659,25 +1689,14 @@ static int uvc_ctrl_fill_xu_info(struct uvc_device *dev,
info->size = le16_to_cpup((__le16 *)data);
- /* Query the control information (GET_INFO) */
- ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, dev->intfnum,
- info->selector, data, 1);
+ ret = uvc_ctrl_get_flags(dev, ctrl, info);
if (ret < 0) {
uvc_trace(UVC_TRACE_CONTROL,
- "GET_INFO failed on control %pUl/%u (%d).\n",
+ "Failed to get flags for control %pUl/%u (%d).\n",
info->entity, info->selector, ret);
goto done;
}
- info->flags = UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX
- | UVC_CTRL_FLAG_GET_RES | UVC_CTRL_FLAG_GET_DEF
- | (data[0] & UVC_CONTROL_CAP_GET ?
- UVC_CTRL_FLAG_GET_CUR : 0)
- | (data[0] & UVC_CONTROL_CAP_SET ?
- UVC_CTRL_FLAG_SET_CUR : 0)
- | (data[0] & UVC_CONTROL_CAP_AUTOUPDATE ?
- UVC_CTRL_FLAG_AUTO_UPDATE : 0);
-
uvc_ctrl_fixup_xu_info(dev, ctrl, info);
uvc_trace(UVC_TRACE_CONTROL, "XU control %pUl/%u queried: len %u, "
@@ -1723,9 +1742,9 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
struct uvc_entity *entity;
struct uvc_control *ctrl;
unsigned int i, found = 0;
- __u32 reqflags;
- __u16 size;
- __u8 *data = NULL;
+ u32 reqflags;
+ u16 size;
+ u8 *data = NULL;
int ret;
/* Find the extension unit. */
@@ -1902,6 +1921,13 @@ static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl,
goto done;
}
+ /*
+ * Retrieve control flags from the device. Ignore errors and work with
+ * default flag values from the uvc_ctrl array when the device doesn't
+ * properly implement GET_INFO on standard controls.
+ */
+ uvc_ctrl_get_flags(dev, ctrl, &ctrl->info);
+
ctrl->initialized = 1;
uvc_trace(UVC_TRACE_CONTROL, "Added control %pUl/%u to device %s "
@@ -2150,7 +2176,7 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
list_for_each_entry(entity, &dev->entities, list) {
struct uvc_control *ctrl;
unsigned int bControlSize = 0, ncontrols;
- __u8 *bmControls = NULL;
+ u8 *bmControls = NULL;
if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT) {
bmControls = entity->extension.bmControls;
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index fd387bf3f02d..2469b49b2b30 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -216,7 +216,7 @@ static struct uvc_format_desc uvc_fmts[] = {
*/
struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,
- __u8 epaddr)
+ u8 epaddr)
{
struct usb_host_endpoint *ep;
unsigned int i;
@@ -230,7 +230,7 @@ struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,
return NULL;
}
-static struct uvc_format_desc *uvc_format_by_guid(const __u8 guid[16])
+static struct uvc_format_desc *uvc_format_by_guid(const u8 guid[16])
{
unsigned int len = ARRAY_SIZE(uvc_fmts);
unsigned int i;
@@ -243,9 +243,9 @@ static struct uvc_format_desc *uvc_format_by_guid(const __u8 guid[16])
return NULL;
}
-static __u32 uvc_colorspace(const __u8 primaries)
+static u32 uvc_colorspace(const u8 primaries)
{
- static const __u8 colorprimaries[] = {
+ static const u8 colorprimaries[] = {
0,
V4L2_COLORSPACE_SRGB,
V4L2_COLORSPACE_470_SYSTEM_M,
@@ -267,14 +267,14 @@ static __u32 uvc_colorspace(const __u8 primaries)
* continued fraction decomposition. Using 8 and 333 for n_terms and threshold
* respectively seems to give nice results.
*/
-void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator,
+void uvc_simplify_fraction(u32 *numerator, u32 *denominator,
unsigned int n_terms, unsigned int threshold)
{
- uint32_t *an;
- uint32_t x, y, r;
+ u32 *an;
+ u32 x, y, r;
unsigned int i, n;
- an = kmalloc(n_terms * sizeof *an, GFP_KERNEL);
+ an = kmalloc_array(n_terms, sizeof(*an), GFP_KERNEL);
if (an == NULL)
return;
@@ -318,21 +318,21 @@ void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator,
* to compute numerator / denominator * 10000000 using 32 bit fixed point
* arithmetic only.
*/
-uint32_t uvc_fraction_to_interval(uint32_t numerator, uint32_t denominator)
+u32 uvc_fraction_to_interval(u32 numerator, u32 denominator)
{
- uint32_t multiplier;
+ u32 multiplier;
/* Saturate the result if the operation would overflow. */
if (denominator == 0 ||
- numerator/denominator >= ((uint32_t)-1)/10000000)
- return (uint32_t)-1;
+ numerator/denominator >= ((u32)-1)/10000000)
+ return (u32)-1;
/* Divide both the denominator and the multiplier by two until
* numerator * multiplier doesn't overflow. If anyone knows a better
* algorithm please let me know.
*/
multiplier = 10000000;
- while (numerator > ((uint32_t)-1)/multiplier) {
+ while (numerator > ((u32)-1)/multiplier) {
multiplier /= 2;
denominator /= 2;
}
@@ -391,7 +391,7 @@ static struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id)
static int uvc_parse_format(struct uvc_device *dev,
struct uvc_streaming *streaming, struct uvc_format *format,
- __u32 **intervals, unsigned char *buffer, int buflen)
+ u32 **intervals, unsigned char *buffer, int buflen)
{
struct usb_interface *intf = streaming->intf;
struct usb_host_interface *alts = intf->cur_altsetting;
@@ -401,7 +401,7 @@ static int uvc_parse_format(struct uvc_device *dev,
unsigned int width_multiplier = 1;
unsigned int interval;
unsigned int i, n;
- __u8 ftype;
+ u8 ftype;
format->type = buffer[2];
format->index = buffer[3];
@@ -423,7 +423,7 @@ static int uvc_parse_format(struct uvc_device *dev,
if (fmtdesc != NULL) {
strlcpy(format->name, fmtdesc->name,
- sizeof format->name);
+ sizeof(format->name));
format->fcc = fmtdesc->fcc;
} else {
uvc_printk(KERN_INFO, "Unknown video format %pUl\n",
@@ -466,7 +466,7 @@ static int uvc_parse_format(struct uvc_device *dev,
return -EINVAL;
}
- strlcpy(format->name, "MJPEG", sizeof format->name);
+ strlcpy(format->name, "MJPEG", sizeof(format->name));
format->fcc = V4L2_PIX_FMT_MJPEG;
format->flags = UVC_FMT_FLAG_COMPRESSED;
format->bpp = 0;
@@ -484,13 +484,13 @@ static int uvc_parse_format(struct uvc_device *dev,
switch (buffer[8] & 0x7f) {
case 0:
- strlcpy(format->name, "SD-DV", sizeof format->name);
+ strlcpy(format->name, "SD-DV", sizeof(format->name));
break;
case 1:
- strlcpy(format->name, "SDL-DV", sizeof format->name);
+ strlcpy(format->name, "SDL-DV", sizeof(format->name));
break;
case 2:
- strlcpy(format->name, "HD-DV", sizeof format->name);
+ strlcpy(format->name, "HD-DV", sizeof(format->name));
break;
default:
uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
@@ -501,7 +501,7 @@ static int uvc_parse_format(struct uvc_device *dev,
}
strlcat(format->name, buffer[8] & (1 << 7) ? " 60Hz" : " 50Hz",
- sizeof format->name);
+ sizeof(format->name));
format->fcc = V4L2_PIX_FMT_DV;
format->flags = UVC_FMT_FLAG_COMPRESSED | UVC_FMT_FLAG_STREAM;
@@ -510,7 +510,7 @@ static int uvc_parse_format(struct uvc_device *dev,
/* Create a dummy frame descriptor. */
frame = &format->frame[0];
- memset(&format->frame[0], 0, sizeof format->frame[0]);
+ memset(&format->frame[0], 0, sizeof(format->frame[0]));
frame->bFrameIntervalType = 1;
frame->dwDefaultFrameInterval = 1;
frame->dwFrameInterval = *intervals;
@@ -658,8 +658,8 @@ static int uvc_parse_streaming(struct uvc_device *dev,
int _buflen, buflen = alts->extralen;
unsigned int nformats = 0, nframes = 0, nintervals = 0;
unsigned int size, i, n, p;
- __u32 *interval;
- __u16 psize;
+ u32 *interval;
+ u16 psize;
int ret = -EINVAL;
if (intf->cur_altsetting->desc.bInterfaceSubClass
@@ -677,7 +677,7 @@ static int uvc_parse_streaming(struct uvc_device *dev,
return -EINVAL;
}
- streaming = kzalloc(sizeof *streaming, GFP_KERNEL);
+ streaming = kzalloc(sizeof(*streaming), GFP_KERNEL);
if (streaming == NULL) {
usb_driver_release_interface(&uvc_driver.driver, intf);
return -EINVAL;
@@ -827,8 +827,8 @@ static int uvc_parse_streaming(struct uvc_device *dev,
goto error;
}
- size = nformats * sizeof *format + nframes * sizeof *frame
- + nintervals * sizeof *interval;
+ size = nformats * sizeof(*format) + nframes * sizeof(*frame)
+ + nintervals * sizeof(*interval);
format = kzalloc(size, GFP_KERNEL);
if (format == NULL) {
ret = -ENOMEM;
@@ -836,7 +836,7 @@ static int uvc_parse_streaming(struct uvc_device *dev,
}
frame = (struct uvc_frame *)&format[nformats];
- interval = (__u32 *)&frame[nframes];
+ interval = (u32 *)&frame[nframes];
streaming->format = format;
streaming->nformats = nformats;
@@ -930,7 +930,7 @@ static struct uvc_entity *uvc_alloc_entity(u16 type, u8 id,
entity->pads[num_pads-1].flags = MEDIA_PAD_FL_SOURCE;
entity->bNrInPins = num_inputs;
- entity->baSourceID = (__u8 *)(&entity->pads[num_pads]);
+ entity->baSourceID = (u8 *)(&entity->pads[num_pads]);
return entity;
}
@@ -995,14 +995,14 @@ static int uvc_parse_vendor_control(struct uvc_device *dev,
unit->extension.bNumControls = buffer[20];
memcpy(unit->baSourceID, &buffer[22], p);
unit->extension.bControlSize = buffer[22+p];
- unit->extension.bmControls = (__u8 *)unit + sizeof(*unit);
- unit->extension.bmControlsType = (__u8 *)unit + sizeof(*unit)
+ unit->extension.bmControls = (u8 *)unit + sizeof(*unit);
+ unit->extension.bmControlsType = (u8 *)unit + sizeof(*unit)
+ n;
memcpy(unit->extension.bmControls, &buffer[23+p], 2*n);
if (buffer[24+p+2*n] != 0)
usb_string(udev, buffer[24+p+2*n], unit->name,
- sizeof unit->name);
+ sizeof(unit->name));
else
sprintf(unit->name, "Extension %u", buffer[3]);
@@ -1022,7 +1022,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
struct usb_interface *intf;
struct usb_host_interface *alts = dev->intf->cur_altsetting;
unsigned int i, n, p, len;
- __u16 type;
+ u16 type;
switch (buffer[2]) {
case UVC_VC_HEADER:
@@ -1101,7 +1101,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) {
term->camera.bControlSize = n;
- term->camera.bmControls = (__u8 *)term + sizeof *term;
+ term->camera.bmControls = (u8 *)term + sizeof(*term);
term->camera.wObjectiveFocalLengthMin =
get_unaligned_le16(&buffer[8]);
term->camera.wObjectiveFocalLengthMax =
@@ -1112,17 +1112,17 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
} else if (UVC_ENTITY_TYPE(term) ==
UVC_ITT_MEDIA_TRANSPORT_INPUT) {
term->media.bControlSize = n;
- term->media.bmControls = (__u8 *)term + sizeof *term;
+ term->media.bmControls = (u8 *)term + sizeof(*term);
term->media.bTransportModeSize = p;
- term->media.bmTransportModes = (__u8 *)term
- + sizeof *term + n;
+ term->media.bmTransportModes = (u8 *)term
+ + sizeof(*term) + n;
memcpy(term->media.bmControls, &buffer[9], n);
memcpy(term->media.bmTransportModes, &buffer[10+n], p);
}
if (buffer[7] != 0)
usb_string(udev, buffer[7], term->name,
- sizeof term->name);
+ sizeof(term->name));
else if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA)
sprintf(term->name, "Camera %u", buffer[3]);
else if (UVC_ENTITY_TYPE(term) == UVC_ITT_MEDIA_TRANSPORT_INPUT)
@@ -1162,7 +1162,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
if (buffer[8] != 0)
usb_string(udev, buffer[8], term->name,
- sizeof term->name);
+ sizeof(term->name));
else
sprintf(term->name, "Output %u", buffer[3]);
@@ -1187,7 +1187,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
if (buffer[5+p] != 0)
usb_string(udev, buffer[5+p], unit->name,
- sizeof unit->name);
+ sizeof(unit->name));
else
sprintf(unit->name, "Selector %u", buffer[3]);
@@ -1213,14 +1213,14 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
unit->processing.wMaxMultiplier =
get_unaligned_le16(&buffer[5]);
unit->processing.bControlSize = buffer[7];
- unit->processing.bmControls = (__u8 *)unit + sizeof *unit;
+ unit->processing.bmControls = (u8 *)unit + sizeof(*unit);
memcpy(unit->processing.bmControls, &buffer[8], n);
if (dev->uvc_version >= 0x0110)
unit->processing.bmVideoStandards = buffer[9+n];
if (buffer[8+n] != 0)
usb_string(udev, buffer[8+n], unit->name,
- sizeof unit->name);
+ sizeof(unit->name));
else
sprintf(unit->name, "Processing %u", buffer[3]);
@@ -1246,12 +1246,12 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
unit->extension.bNumControls = buffer[20];
memcpy(unit->baSourceID, &buffer[22], p);
unit->extension.bControlSize = buffer[22+p];
- unit->extension.bmControls = (__u8 *)unit + sizeof *unit;
+ unit->extension.bmControls = (u8 *)unit + sizeof(*unit);
memcpy(unit->extension.bmControls, &buffer[23+p], n);
if (buffer[23+p+n] != 0)
usb_string(udev, buffer[23+p+n], unit->name,
- sizeof unit->name);
+ sizeof(unit->name));
else
sprintf(unit->name, "Extension %u", buffer[3]);
@@ -1936,7 +1936,7 @@ int uvc_register_video_device(struct uvc_device *dev,
break;
}
- strlcpy(vdev->name, dev->name, sizeof vdev->name);
+ strlcpy(vdev->name, dev->name, sizeof(vdev->name));
/*
* Set the driver data before calling video_register_device, otherwise
@@ -2070,7 +2070,8 @@ static int uvc_probe(struct usb_interface *intf,
udev->devpath);
/* Allocate memory for the device and initialize it. */
- if ((dev = kzalloc(sizeof *dev, GFP_KERNEL)) == NULL)
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL)
return -ENOMEM;
INIT_LIST_HEAD(&dev->entities);
@@ -2089,9 +2090,9 @@ static int uvc_probe(struct usb_interface *intf,
dev->meta_format = info->meta_format;
if (udev->product != NULL)
- strlcpy(dev->name, udev->product, sizeof dev->name);
+ strlcpy(dev->name, udev->product, sizeof(dev->name));
else
- snprintf(dev->name, sizeof dev->name,
+ snprintf(dev->name, sizeof(dev->name),
"UVC Camera (%04x:%04x)",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct));
diff --git a/drivers/media/usb/uvc/uvc_isight.c b/drivers/media/usb/uvc/uvc_isight.c
index 5059fbf41020..81e6f2187bfb 100644
--- a/drivers/media/usb/uvc/uvc_isight.c
+++ b/drivers/media/usb/uvc/uvc_isight.c
@@ -37,16 +37,16 @@
*/
static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf,
- const __u8 *data, unsigned int len)
+ const u8 *data, unsigned int len)
{
- static const __u8 hdr[] = {
+ static const u8 hdr[] = {
0x11, 0x22, 0x33, 0x44,
0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xfa, 0xce
};
unsigned int maxlen, nbytes;
- __u8 *mem;
+ u8 *mem;
int is_header = 0;
if (buf == NULL)
diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c
index 1ef20e74b7ac..7b710410584a 100644
--- a/drivers/media/usb/uvc/uvc_status.c
+++ b/drivers/media/usb/uvc/uvc_status.c
@@ -78,7 +78,7 @@ static void uvc_input_report_key(struct uvc_device *dev, unsigned int code,
/* --------------------------------------------------------------------------
* Status interrupt endpoint
*/
-static void uvc_event_streaming(struct uvc_device *dev, __u8 *data, int len)
+static void uvc_event_streaming(struct uvc_device *dev, u8 *data, int len)
{
if (len < 3) {
uvc_trace(UVC_TRACE_STATUS, "Invalid streaming status event "
@@ -99,7 +99,7 @@ static void uvc_event_streaming(struct uvc_device *dev, __u8 *data, int len)
}
}
-static void uvc_event_control(struct uvc_device *dev, __u8 *data, int len)
+static void uvc_event_control(struct uvc_device *dev, u8 *data, int len)
{
char *attrs[3] = { "value", "info", "failure" };
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index a13ad4e178be..bd32914259ae 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -40,13 +40,13 @@ static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain,
unsigned int size;
int ret;
- map = kzalloc(sizeof *map, GFP_KERNEL);
+ map = kzalloc(sizeof(*map), GFP_KERNEL);
if (map == NULL)
return -ENOMEM;
map->id = xmap->id;
- memcpy(map->name, xmap->name, sizeof map->name);
- memcpy(map->entity, xmap->entity, sizeof map->entity);
+ memcpy(map->name, xmap->name, sizeof(map->name));
+ memcpy(map->entity, xmap->entity, sizeof(map->entity));
map->selector = xmap->selector;
map->size = xmap->size;
map->offset = xmap->offset;
@@ -105,12 +105,12 @@ free_map:
* the Video Probe and Commit negotiation, but some hardware don't implement
* that feature.
*/
-static __u32 uvc_try_frame_interval(struct uvc_frame *frame, __u32 interval)
+static u32 uvc_try_frame_interval(struct uvc_frame *frame, u32 interval)
{
unsigned int i;
if (frame->bFrameIntervalType) {
- __u32 best = -1, dist;
+ u32 best = -1, dist;
for (i = 0; i < frame->bFrameIntervalType; ++i) {
dist = interval > frame->dwFrameInterval[i]
@@ -125,9 +125,9 @@ static __u32 uvc_try_frame_interval(struct uvc_frame *frame, __u32 interval)
interval = frame->dwFrameInterval[i-1];
} else {
- const __u32 min = frame->dwFrameInterval[0];
- const __u32 max = frame->dwFrameInterval[1];
- const __u32 step = frame->dwFrameInterval[2];
+ const u32 min = frame->dwFrameInterval[0];
+ const u32 max = frame->dwFrameInterval[1];
+ const u32 step = frame->dwFrameInterval[2];
interval = min + (interval - min + step/2) / step * step;
if (interval > max)
@@ -137,7 +137,7 @@ static __u32 uvc_try_frame_interval(struct uvc_frame *frame, __u32 interval)
return interval;
}
-static __u32 uvc_v4l2_get_bytesperline(const struct uvc_format *format,
+static u32 uvc_v4l2_get_bytesperline(const struct uvc_format *format,
const struct uvc_frame *frame)
{
switch (format->fcc) {
@@ -158,17 +158,17 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream,
{
struct uvc_format *format = NULL;
struct uvc_frame *frame = NULL;
- __u16 rw, rh;
+ u16 rw, rh;
unsigned int d, maxd;
unsigned int i;
- __u32 interval;
+ u32 interval;
int ret = 0;
- __u8 *fcc;
+ u8 *fcc;
if (fmt->type != stream->type)
return -EINVAL;
- fcc = (__u8 *)&fmt->fmt.pix.pixelformat;
+ fcc = (u8 *)&fmt->fmt.pix.pixelformat;
uvc_trace(UVC_TRACE_FORMAT, "Trying format 0x%08x (%c%c%c%c): %ux%u.\n",
fmt->fmt.pix.pixelformat,
fcc[0], fcc[1], fcc[2], fcc[3],
@@ -197,8 +197,8 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream,
maxd = (unsigned int)-1;
for (i = 0; i < format->nframes; ++i) {
- __u16 w = format->frame[i].wWidth;
- __u16 h = format->frame[i].wHeight;
+ u16 w = format->frame[i].wWidth;
+ u16 h = format->frame[i].wHeight;
d = min(w, rw) * min(h, rh);
d = w*h + rw*rh - 2*d;
@@ -224,7 +224,7 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream,
(100000000/interval)%10);
/* Set the format index, frame index and frame interval. */
- memset(probe, 0, sizeof *probe);
+ memset(probe, 0, sizeof(*probe));
probe->bmHint = 1; /* dwFrameInterval */
probe->bFormatIndex = format->index;
probe->bFrameIndex = frame->bFrameIndex;
@@ -336,7 +336,7 @@ done:
static int uvc_v4l2_get_streamparm(struct uvc_streaming *stream,
struct v4l2_streamparm *parm)
{
- uint32_t numerator, denominator;
+ u32 numerator, denominator;
if (parm->type != stream->type)
return -EINVAL;
@@ -348,7 +348,7 @@ static int uvc_v4l2_get_streamparm(struct uvc_streaming *stream,
denominator = 10000000;
uvc_simplify_fraction(&numerator, &denominator, 8, 333);
- memset(parm, 0, sizeof *parm);
+ memset(parm, 0, sizeof(*parm));
parm->type = stream->type;
if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
@@ -373,7 +373,10 @@ static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream,
{
struct uvc_streaming_control probe;
struct v4l2_fract timeperframe;
- uint32_t interval;
+ struct uvc_format *format;
+ struct uvc_frame *frame;
+ u32 interval, maxd;
+ unsigned int i;
int ret;
if (parm->type != stream->type)
@@ -396,9 +399,33 @@ static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream,
return -EBUSY;
}
+ format = stream->cur_format;
+ frame = stream->cur_frame;
probe = stream->ctrl;
- probe.dwFrameInterval =
- uvc_try_frame_interval(stream->cur_frame, interval);
+ probe.dwFrameInterval = uvc_try_frame_interval(frame, interval);
+ maxd = abs((s32)probe.dwFrameInterval - interval);
+
+ /* Try frames with matching size to find the best frame interval. */
+ for (i = 0; i < format->nframes && maxd != 0; i++) {
+ u32 d, ival;
+
+ if (&format->frame[i] == stream->cur_frame)
+ continue;
+
+ if (format->frame[i].wWidth != stream->cur_frame->wWidth ||
+ format->frame[i].wHeight != stream->cur_frame->wHeight)
+ continue;
+
+ ival = uvc_try_frame_interval(&format->frame[i], interval);
+ d = abs((s32)ival - interval);
+ if (d >= maxd)
+ continue;
+
+ frame = &format->frame[i];
+ probe.bFrameIndex = frame->bFrameIndex;
+ probe.dwFrameInterval = ival;
+ maxd = d;
+ }
/* Probe the device with the new settings. */
ret = uvc_probe_video(stream, &probe);
@@ -408,6 +435,7 @@ static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream,
}
stream->ctrl = probe;
+ stream->cur_frame = frame;
mutex_unlock(&stream->mutex);
/* Return the actual frame period. */
@@ -498,7 +526,7 @@ static int uvc_v4l2_open(struct file *file)
return ret;
/* Create the device handle. */
- handle = kzalloc(sizeof *handle, GFP_KERNEL);
+ handle = kzalloc(sizeof(*handle), GFP_KERNEL);
if (handle == NULL) {
usb_autopm_put_interface(stream->dev->intf);
return -ENOMEM;
@@ -577,7 +605,7 @@ static int uvc_ioctl_enum_fmt(struct uvc_streaming *stream,
{
struct uvc_format *format;
enum v4l2_buf_type type = fmt->type;
- __u32 index = fmt->index;
+ u32 index = fmt->index;
if (fmt->type != stream->type || fmt->index >= stream->nformats)
return -EINVAL;
@@ -1145,8 +1173,9 @@ static int uvc_ioctl_enum_framesizes(struct file *file, void *fh,
struct uvc_fh *handle = fh;
struct uvc_streaming *stream = handle->stream;
struct uvc_format *format = NULL;
- struct uvc_frame *frame;
- int i;
+ struct uvc_frame *frame = NULL;
+ unsigned int index;
+ unsigned int i;
/* Look for the given pixel format */
for (i = 0; i < stream->nformats; i++) {
@@ -1158,10 +1187,20 @@ static int uvc_ioctl_enum_framesizes(struct file *file, void *fh,
if (format == NULL)
return -EINVAL;
- if (fsize->index >= format->nframes)
+ /* Skip duplicate frame sizes */
+ for (i = 0, index = 0; i < format->nframes; i++) {
+ if (frame && frame->wWidth == format->frame[i].wWidth &&
+ frame->wHeight == format->frame[i].wHeight)
+ continue;
+ frame = &format->frame[i];
+ if (index == fsize->index)
+ break;
+ index++;
+ }
+
+ if (i == format->nframes)
return -EINVAL;
- frame = &format->frame[fsize->index];
fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
fsize->discrete.width = frame->wWidth;
fsize->discrete.height = frame->wHeight;
@@ -1175,7 +1214,9 @@ static int uvc_ioctl_enum_frameintervals(struct file *file, void *fh,
struct uvc_streaming *stream = handle->stream;
struct uvc_format *format = NULL;
struct uvc_frame *frame = NULL;
- int i;
+ unsigned int nintervals;
+ unsigned int index;
+ unsigned int i;
/* Look for the given pixel format and frame size */
for (i = 0; i < stream->nformats; i++) {
@@ -1187,30 +1228,28 @@ static int uvc_ioctl_enum_frameintervals(struct file *file, void *fh,
if (format == NULL)
return -EINVAL;
+ index = fival->index;
for (i = 0; i < format->nframes; i++) {
if (format->frame[i].wWidth == fival->width &&
format->frame[i].wHeight == fival->height) {
frame = &format->frame[i];
- break;
+ nintervals = frame->bFrameIntervalType ?: 1;
+ if (index < nintervals)
+ break;
+ index -= nintervals;
}
}
- if (frame == NULL)
+ if (i == format->nframes)
return -EINVAL;
if (frame->bFrameIntervalType) {
- if (fival->index >= frame->bFrameIntervalType)
- return -EINVAL;
-
fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
fival->discrete.numerator =
- frame->dwFrameInterval[fival->index];
+ frame->dwFrameInterval[index];
fival->discrete.denominator = 10000000;
uvc_simplify_fraction(&fival->discrete.numerator,
&fival->discrete.denominator, 8, 333);
} else {
- if (fival->index)
- return -EINVAL;
-
fival->type = V4L2_FRMIVAL_TYPE_STEPWISE;
fival->stepwise.min.numerator = frame->dwFrameInterval[0];
fival->stepwise.min.denominator = 10000000;
@@ -1261,20 +1300,20 @@ static long uvc_ioctl_default(struct file *file, void *fh, bool valid_prio,
#ifdef CONFIG_COMPAT
struct uvc_xu_control_mapping32 {
- __u32 id;
- __u8 name[32];
- __u8 entity[16];
- __u8 selector;
+ u32 id;
+ u8 name[32];
+ u8 entity[16];
+ u8 selector;
- __u8 size;
- __u8 offset;
- __u32 v4l2_type;
- __u32 data_type;
+ u8 size;
+ u8 offset;
+ u32 v4l2_type;
+ u32 data_type;
compat_caddr_t menu_info;
- __u32 menu_count;
+ u32 menu_count;
- __u32 reserved[4];
+ u32 reserved[4];
};
static int uvc_v4l2_get_xu_mapping(struct uvc_xu_control_mapping *kp,
@@ -1310,10 +1349,10 @@ static int uvc_v4l2_put_xu_mapping(const struct uvc_xu_control_mapping *kp,
}
struct uvc_xu_control_query32 {
- __u8 unit;
- __u8 selector;
- __u8 query;
- __u16 size;
+ u8 unit;
+ u8 selector;
+ u8 query;
+ u16 size;
compat_caddr_t data;
};
diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index 5441553f74e1..aa0082fe5833 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -30,11 +30,11 @@
* UVC Controls
*/
-static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
- __u8 intfnum, __u8 cs, void *data, __u16 size,
+static int __uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit,
+ u8 intfnum, u8 cs, void *data, u16 size,
int timeout)
{
- __u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+ u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
unsigned int pipe;
pipe = (query & 0x80) ? usb_rcvctrlpipe(dev->udev, 0)
@@ -45,7 +45,7 @@ static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
unit << 8 | intfnum, data, size, timeout);
}
-static const char *uvc_query_name(__u8 query)
+static const char *uvc_query_name(u8 query)
{
switch (query) {
case UVC_SET_CUR:
@@ -69,8 +69,8 @@ static const char *uvc_query_name(__u8 query)
}
}
-int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
- __u8 intfnum, __u8 cs, void *data, __u16 size)
+int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit,
+ u8 intfnum, u8 cs, void *data, u16 size)
{
int ret;
@@ -164,10 +164,10 @@ static void uvc_fixup_video_ctrl(struct uvc_streaming *stream,
}
static int uvc_get_video_ctrl(struct uvc_streaming *stream,
- struct uvc_streaming_control *ctrl, int probe, __u8 query)
+ struct uvc_streaming_control *ctrl, int probe, u8 query)
{
- __u8 *data;
- __u16 size;
+ u8 *data;
+ u16 size;
int ret;
size = stream->dev->uvc_version >= 0x0110 ? 34 : 26;
@@ -191,7 +191,7 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream,
uvc_warn_once(stream->dev, UVC_WARN_MINMAX, "UVC non "
"compliance - GET_MIN/MAX(PROBE) incorrectly "
"supported. Enabling workaround.\n");
- memset(ctrl, 0, sizeof *ctrl);
+ memset(ctrl, 0, sizeof(*ctrl));
ctrl->wCompQuality = le16_to_cpup((__le16 *)data);
ret = 0;
goto out;
@@ -254,8 +254,8 @@ out:
static int uvc_set_video_ctrl(struct uvc_streaming *stream,
struct uvc_streaming_control *ctrl, int probe)
{
- __u8 *data;
- __u16 size;
+ u8 *data;
+ u16 size;
int ret;
size = stream->dev->uvc_version >= 0x0110 ? 34 : 26;
@@ -301,7 +301,7 @@ int uvc_probe_video(struct uvc_streaming *stream,
struct uvc_streaming_control *probe)
{
struct uvc_streaming_control probe_min, probe_max;
- __u16 bandwidth;
+ u16 bandwidth;
unsigned int i;
int ret;
@@ -379,7 +379,7 @@ static inline ktime_t uvc_video_get_time(void)
static void
uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf,
- const __u8 *data, int len)
+ const u8 *data, int len)
{
struct uvc_clock_sample *sample;
unsigned int header_size;
@@ -705,7 +705,7 @@ done:
*/
static void uvc_video_stats_decode(struct uvc_streaming *stream,
- const __u8 *data, int len)
+ const u8 *data, int len)
{
unsigned int header_size;
bool has_pts = false;
@@ -946,9 +946,9 @@ static void uvc_video_stats_stop(struct uvc_streaming *stream)
* uvc_video_decode_end will never be called with a NULL buffer.
*/
static int uvc_video_decode_start(struct uvc_streaming *stream,
- struct uvc_buffer *buf, const __u8 *data, int len)
+ struct uvc_buffer *buf, const u8 *data, int len)
{
- __u8 fid;
+ u8 fid;
/* Sanity checks:
* - packet must be at least 2 bytes long
@@ -1009,7 +1009,7 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
buf->buf.field = V4L2_FIELD_NONE;
buf->buf.sequence = stream->sequence;
- buf->buf.vb2_buf.timestamp = uvc_video_get_time();
+ buf->buf.vb2_buf.timestamp = ktime_to_ns(uvc_video_get_time());
/* TODO: Handle PTS and SCR. */
buf->state = UVC_BUF_STATE_ACTIVE;
@@ -1043,7 +1043,7 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
}
static void uvc_video_decode_data(struct uvc_streaming *stream,
- struct uvc_buffer *buf, const __u8 *data, int len)
+ struct uvc_buffer *buf, const u8 *data, int len)
{
unsigned int maxlen, nbytes;
void *mem;
@@ -1067,7 +1067,7 @@ static void uvc_video_decode_data(struct uvc_streaming *stream,
}
static void uvc_video_decode_end(struct uvc_streaming *stream,
- struct uvc_buffer *buf, const __u8 *data, int len)
+ struct uvc_buffer *buf, const u8 *data, int len)
{
/* Mark the buffer as done if the EOF marker is set. */
if (data[1] & UVC_STREAM_EOF && buf->bytesused != 0) {
@@ -1092,7 +1092,7 @@ static void uvc_video_decode_end(struct uvc_streaming *stream,
* video buffer to the transfer buffer.
*/
static int uvc_video_encode_header(struct uvc_streaming *stream,
- struct uvc_buffer *buf, __u8 *data, int len)
+ struct uvc_buffer *buf, u8 *data, int len)
{
data[0] = 2; /* Header length */
data[1] = UVC_STREAM_EOH | UVC_STREAM_EOF
@@ -1101,7 +1101,7 @@ static int uvc_video_encode_header(struct uvc_streaming *stream,
}
static int uvc_video_encode_data(struct uvc_streaming *stream,
- struct uvc_buffer *buf, __u8 *data, int len)
+ struct uvc_buffer *buf, u8 *data, int len)
{
struct uvc_video_queue *queue = &stream->queue;
unsigned int nbytes;
@@ -1191,7 +1191,8 @@ static void uvc_video_decode_meta(struct uvc_streaming *stream,
uvc_trace(UVC_TRACE_FRAME,
"%s(): t-sys %lluns, SOF %u, len %u, flags 0x%x, PTS %u, STC %u frame SOF %u\n",
- __func__, time, meta->sof, meta->length, meta->flags,
+ __func__, ktime_to_ns(time), meta->sof, meta->length,
+ meta->flags,
has_pts ? *(u32 *)meta->buf : 0,
has_scr ? *(u32 *)scr : 0,
has_scr ? *(u32 *)(scr + 4) & 0x7ff : 0);
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index d9e7c70788d0..be5cf179228b 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -208,60 +208,60 @@ struct uvc_device;
struct uvc_control_info {
struct list_head mappings;
- __u8 entity[16];
- __u8 index; /* Bit index in bmControls */
- __u8 selector;
+ u8 entity[16];
+ u8 index; /* Bit index in bmControls */
+ u8 selector;
- __u16 size;
- __u32 flags;
+ u16 size;
+ u32 flags;
};
struct uvc_control_mapping {
struct list_head list;
struct list_head ev_subs;
- __u32 id;
- __u8 name[32];
- __u8 entity[16];
- __u8 selector;
+ u32 id;
+ u8 name[32];
+ u8 entity[16];
+ u8 selector;
- __u8 size;
- __u8 offset;
+ u8 size;
+ u8 offset;
enum v4l2_ctrl_type v4l2_type;
- __u32 data_type;
+ u32 data_type;
struct uvc_menu_info *menu_info;
- __u32 menu_count;
+ u32 menu_count;
- __u32 master_id;
- __s32 master_manual;
- __u32 slave_ids[2];
+ u32 master_id;
+ s32 master_manual;
+ u32 slave_ids[2];
- __s32 (*get) (struct uvc_control_mapping *mapping, __u8 query,
- const __u8 *data);
- void (*set) (struct uvc_control_mapping *mapping, __s32 value,
- __u8 *data);
+ s32 (*get)(struct uvc_control_mapping *mapping, u8 query,
+ const u8 *data);
+ void (*set)(struct uvc_control_mapping *mapping, s32 value,
+ u8 *data);
};
struct uvc_control {
struct uvc_entity *entity;
struct uvc_control_info info;
- __u8 index; /* Used to match the uvc_control entry with a
+ u8 index; /* Used to match the uvc_control entry with a
uvc_control_info. */
- __u8 dirty:1,
- loaded:1,
- modified:1,
- cached:1,
- initialized:1;
+ u8 dirty:1,
+ loaded:1,
+ modified:1,
+ cached:1,
+ initialized:1;
- __u8 *uvc_data;
+ u8 *uvc_data;
};
struct uvc_format_desc {
char *name;
- __u8 guid[16];
- __u32 fcc;
+ u8 guid[16];
+ u32 fcc;
};
/* The term 'entity' refers to both UVC units and UVC terminals.
@@ -287,8 +287,8 @@ struct uvc_entity {
* chain. */
unsigned int flags;
- __u8 id;
- __u16 type;
+ u8 id;
+ u16 type;
char name[64];
/* Media controller-related fields. */
@@ -300,69 +300,69 @@ struct uvc_entity {
union {
struct {
- __u16 wObjectiveFocalLengthMin;
- __u16 wObjectiveFocalLengthMax;
- __u16 wOcularFocalLength;
- __u8 bControlSize;
- __u8 *bmControls;
+ u16 wObjectiveFocalLengthMin;
+ u16 wObjectiveFocalLengthMax;
+ u16 wOcularFocalLength;
+ u8 bControlSize;
+ u8 *bmControls;
} camera;
struct {
- __u8 bControlSize;
- __u8 *bmControls;
- __u8 bTransportModeSize;
- __u8 *bmTransportModes;
+ u8 bControlSize;
+ u8 *bmControls;
+ u8 bTransportModeSize;
+ u8 *bmTransportModes;
} media;
struct {
} output;
struct {
- __u16 wMaxMultiplier;
- __u8 bControlSize;
- __u8 *bmControls;
- __u8 bmVideoStandards;
+ u16 wMaxMultiplier;
+ u8 bControlSize;
+ u8 *bmControls;
+ u8 bmVideoStandards;
} processing;
struct {
} selector;
struct {
- __u8 guidExtensionCode[16];
- __u8 bNumControls;
- __u8 bControlSize;
- __u8 *bmControls;
- __u8 *bmControlsType;
+ u8 guidExtensionCode[16];
+ u8 bNumControls;
+ u8 bControlSize;
+ u8 *bmControls;
+ u8 *bmControlsType;
} extension;
};
- __u8 bNrInPins;
- __u8 *baSourceID;
+ u8 bNrInPins;
+ u8 *baSourceID;
unsigned int ncontrols;
struct uvc_control *controls;
};
struct uvc_frame {
- __u8 bFrameIndex;
- __u8 bmCapabilities;
- __u16 wWidth;
- __u16 wHeight;
- __u32 dwMinBitRate;
- __u32 dwMaxBitRate;
- __u32 dwMaxVideoFrameBufferSize;
- __u8 bFrameIntervalType;
- __u32 dwDefaultFrameInterval;
- __u32 *dwFrameInterval;
+ u8 bFrameIndex;
+ u8 bmCapabilities;
+ u16 wWidth;
+ u16 wHeight;
+ u32 dwMinBitRate;
+ u32 dwMaxBitRate;
+ u32 dwMaxVideoFrameBufferSize;
+ u8 bFrameIntervalType;
+ u32 dwDefaultFrameInterval;
+ u32 *dwFrameInterval;
};
struct uvc_format {
- __u8 type;
- __u8 index;
- __u8 bpp;
- __u8 colorspace;
- __u32 fcc;
- __u32 flags;
+ u8 type;
+ u8 index;
+ u8 bpp;
+ u8 colorspace;
+ u32 fcc;
+ u32 flags;
char name[32];
@@ -371,16 +371,16 @@ struct uvc_format {
};
struct uvc_streaming_header {
- __u8 bNumFormats;
- __u8 bEndpointAddress;
- __u8 bTerminalLink;
- __u8 bControlSize;
- __u8 *bmaControls;
+ u8 bNumFormats;
+ u8 bEndpointAddress;
+ u8 bTerminalLink;
+ u8 bControlSize;
+ u8 *bmaControls;
/* The following fields are used by input headers only. */
- __u8 bmInfo;
- __u8 bStillCaptureMethod;
- __u8 bTriggerSupport;
- __u8 bTriggerUsage;
+ u8 bmInfo;
+ u8 bStillCaptureMethod;
+ u8 bTriggerSupport;
+ u8 bTriggerUsage;
};
enum uvc_buffer_state {
@@ -490,7 +490,7 @@ struct uvc_streaming {
struct usb_interface *intf;
int intfnum;
- __u16 maxpsize;
+ u16 maxpsize;
struct uvc_streaming_header header;
enum v4l2_buf_type type;
@@ -517,16 +517,16 @@ struct uvc_streaming {
struct {
struct video_device vdev;
struct uvc_video_queue queue;
- __u32 format;
+ u32 format;
} meta;
/* Context data used by the bulk completion handler. */
struct {
- __u8 header[256];
+ u8 header[256];
unsigned int header_size;
int skip_payload;
- __u32 payload_size;
- __u32 max_payload_size;
+ u32 payload_size;
+ u32 max_payload_size;
} bulk;
struct urb *urb[UVC_URBS];
@@ -534,8 +534,8 @@ struct uvc_streaming {
dma_addr_t urb_dma[UVC_URBS];
unsigned int urb_size;
- __u32 sequence;
- __u8 last_fid;
+ u32 sequence;
+ u8 last_fid;
/* debugfs */
struct dentry *debugfs_dir;
@@ -570,8 +570,8 @@ struct uvc_device {
struct usb_device *udev;
struct usb_interface *intf;
unsigned long warnings;
- __u32 quirks;
- __u32 meta_format;
+ u32 quirks;
+ u32 meta_format;
int intfnum;
char name[32];
@@ -584,8 +584,8 @@ struct uvc_device {
struct media_device mdev;
#endif
struct v4l2_device vdev;
- __u16 uvc_version;
- __u32 clock_frequency;
+ u16 uvc_version;
+ u32 clock_frequency;
struct list_head entities;
struct list_head chains;
@@ -597,7 +597,7 @@ struct uvc_device {
/* Status Interrupt Endpoint */
struct usb_host_endpoint *int_ep;
struct urb *int_urb;
- __u8 *status;
+ u8 *status;
struct input_dev *input;
char input_phys[64];
};
@@ -667,40 +667,38 @@ extern unsigned int uvc_hw_timestamps_param;
/* Core driver */
extern struct uvc_driver uvc_driver;
-extern struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id);
+struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id);
/* Video buffers queue management. */
-extern int uvc_queue_init(struct uvc_video_queue *queue,
- enum v4l2_buf_type type, int drop_corrupted);
-extern void uvc_queue_release(struct uvc_video_queue *queue);
-extern int uvc_request_buffers(struct uvc_video_queue *queue,
- struct v4l2_requestbuffers *rb);
-extern int uvc_query_buffer(struct uvc_video_queue *queue,
- struct v4l2_buffer *v4l2_buf);
-extern int uvc_create_buffers(struct uvc_video_queue *queue,
- struct v4l2_create_buffers *v4l2_cb);
-extern int uvc_queue_buffer(struct uvc_video_queue *queue,
- struct v4l2_buffer *v4l2_buf);
-extern int uvc_export_buffer(struct uvc_video_queue *queue,
- struct v4l2_exportbuffer *exp);
-extern int uvc_dequeue_buffer(struct uvc_video_queue *queue,
- struct v4l2_buffer *v4l2_buf, int nonblocking);
-extern int uvc_queue_streamon(struct uvc_video_queue *queue,
- enum v4l2_buf_type type);
-extern int uvc_queue_streamoff(struct uvc_video_queue *queue,
- enum v4l2_buf_type type);
-extern void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect);
-extern struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
- struct uvc_buffer *buf);
-extern int uvc_queue_mmap(struct uvc_video_queue *queue,
- struct vm_area_struct *vma);
-extern __poll_t uvc_queue_poll(struct uvc_video_queue *queue,
- struct file *file, poll_table *wait);
+int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type,
+ int drop_corrupted);
+void uvc_queue_release(struct uvc_video_queue *queue);
+int uvc_request_buffers(struct uvc_video_queue *queue,
+ struct v4l2_requestbuffers *rb);
+int uvc_query_buffer(struct uvc_video_queue *queue,
+ struct v4l2_buffer *v4l2_buf);
+int uvc_create_buffers(struct uvc_video_queue *queue,
+ struct v4l2_create_buffers *v4l2_cb);
+int uvc_queue_buffer(struct uvc_video_queue *queue,
+ struct v4l2_buffer *v4l2_buf);
+int uvc_export_buffer(struct uvc_video_queue *queue,
+ struct v4l2_exportbuffer *exp);
+int uvc_dequeue_buffer(struct uvc_video_queue *queue,
+ struct v4l2_buffer *v4l2_buf, int nonblocking);
+int uvc_queue_streamon(struct uvc_video_queue *queue, enum v4l2_buf_type type);
+int uvc_queue_streamoff(struct uvc_video_queue *queue, enum v4l2_buf_type type);
+void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect);
+struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
+ struct uvc_buffer *buf);
+int uvc_queue_mmap(struct uvc_video_queue *queue,
+ struct vm_area_struct *vma);
+__poll_t uvc_queue_poll(struct uvc_video_queue *queue, struct file *file,
+ poll_table *wait);
#ifndef CONFIG_MMU
-extern unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue,
- unsigned long pgoff);
+unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue,
+ unsigned long pgoff);
#endif
-extern int uvc_queue_allocated(struct uvc_video_queue *queue);
+int uvc_queue_allocated(struct uvc_video_queue *queue);
static inline int uvc_queue_streaming(struct uvc_video_queue *queue)
{
return vb2_is_streaming(&queue->queue);
@@ -711,18 +709,18 @@ extern const struct v4l2_ioctl_ops uvc_ioctl_ops;
extern const struct v4l2_file_operations uvc_fops;
/* Media controller */
-extern int uvc_mc_register_entities(struct uvc_video_chain *chain);
-extern void uvc_mc_cleanup_entity(struct uvc_entity *entity);
+int uvc_mc_register_entities(struct uvc_video_chain *chain);
+void uvc_mc_cleanup_entity(struct uvc_entity *entity);
/* Video */
-extern int uvc_video_init(struct uvc_streaming *stream);
-extern int uvc_video_suspend(struct uvc_streaming *stream);
-extern int uvc_video_resume(struct uvc_streaming *stream, int reset);
-extern int uvc_video_enable(struct uvc_streaming *stream, int enable);
-extern int uvc_probe_video(struct uvc_streaming *stream,
- struct uvc_streaming_control *probe);
-extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
- __u8 intfnum, __u8 cs, void *data, __u16 size);
+int uvc_video_init(struct uvc_streaming *stream);
+int uvc_video_suspend(struct uvc_streaming *stream);
+int uvc_video_resume(struct uvc_streaming *stream, int reset);
+int uvc_video_enable(struct uvc_streaming *stream, int enable);
+int uvc_probe_video(struct uvc_streaming *stream,
+ struct uvc_streaming_control *probe);
+int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit,
+ u8 intfnum, u8 cs, void *data, u16 size);
void uvc_video_clock_update(struct uvc_streaming *stream,
struct vb2_v4l2_buffer *vbuf,
struct uvc_buffer *buf);
@@ -737,32 +735,32 @@ int uvc_register_video_device(struct uvc_device *dev,
const struct v4l2_ioctl_ops *ioctl_ops);
/* Status */
-extern int uvc_status_init(struct uvc_device *dev);
-extern void uvc_status_cleanup(struct uvc_device *dev);
-extern int uvc_status_start(struct uvc_device *dev, gfp_t flags);
-extern void uvc_status_stop(struct uvc_device *dev);
+int uvc_status_init(struct uvc_device *dev);
+void uvc_status_cleanup(struct uvc_device *dev);
+int uvc_status_start(struct uvc_device *dev, gfp_t flags);
+void uvc_status_stop(struct uvc_device *dev);
/* Controls */
extern const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops;
-extern int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
- struct v4l2_queryctrl *v4l2_ctrl);
-extern int uvc_query_v4l2_menu(struct uvc_video_chain *chain,
- struct v4l2_querymenu *query_menu);
-
-extern int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
- const struct uvc_control_mapping *mapping);
-extern int uvc_ctrl_init_device(struct uvc_device *dev);
-extern void uvc_ctrl_cleanup_device(struct uvc_device *dev);
-extern int uvc_ctrl_restore_values(struct uvc_device *dev);
-
-extern int uvc_ctrl_begin(struct uvc_video_chain *chain);
-extern int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback,
- const struct v4l2_ext_control *xctrls,
- unsigned int xctrls_count);
+int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
+ struct v4l2_queryctrl *v4l2_ctrl);
+int uvc_query_v4l2_menu(struct uvc_video_chain *chain,
+ struct v4l2_querymenu *query_menu);
+
+int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
+ const struct uvc_control_mapping *mapping);
+int uvc_ctrl_init_device(struct uvc_device *dev);
+void uvc_ctrl_cleanup_device(struct uvc_device *dev);
+int uvc_ctrl_restore_values(struct uvc_device *dev);
+
+int uvc_ctrl_begin(struct uvc_video_chain *chain);
+int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback,
+ const struct v4l2_ext_control *xctrls,
+ unsigned int xctrls_count);
static inline int uvc_ctrl_commit(struct uvc_fh *handle,
- const struct v4l2_ext_control *xctrls,
- unsigned int xctrls_count)
+ const struct v4l2_ext_control *xctrls,
+ unsigned int xctrls_count)
{
return __uvc_ctrl_commit(handle, 0, xctrls, xctrls_count);
}
@@ -771,25 +769,23 @@ static inline int uvc_ctrl_rollback(struct uvc_fh *handle)
return __uvc_ctrl_commit(handle, 1, NULL, 0);
}
-extern int uvc_ctrl_get(struct uvc_video_chain *chain,
- struct v4l2_ext_control *xctrl);
-extern int uvc_ctrl_set(struct uvc_video_chain *chain,
- struct v4l2_ext_control *xctrl);
+int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl);
+int uvc_ctrl_set(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl);
-extern int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
- struct uvc_xu_control_query *xqry);
+int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
+ struct uvc_xu_control_query *xqry);
/* Utility functions */
-extern void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator,
- unsigned int n_terms, unsigned int threshold);
-extern uint32_t uvc_fraction_to_interval(uint32_t numerator,
- uint32_t denominator);
-extern struct usb_host_endpoint *uvc_find_endpoint(
- struct usb_host_interface *alts, __u8 epaddr);
+void uvc_simplify_fraction(u32 *numerator, u32 *denominator,
+ unsigned int n_terms, unsigned int threshold);
+u32 uvc_fraction_to_interval(u32 numerator, u32 denominator);
+struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,
+ u8 epaddr);
/* Quirks support */
void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream,
- struct uvc_buffer *buf, struct uvc_buffer *meta_buf);
+ struct uvc_buffer *buf,
+ struct uvc_buffer *meta_buf);
/* debugfs and statistics */
void uvc_debugfs_init(void);
diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c
index 8b7c19943d46..b8886102c5ed 100644
--- a/drivers/media/usb/zr364xx/zr364xx.c
+++ b/drivers/media/usb/zr364xx/zr364xx.c
@@ -517,8 +517,7 @@ static void zr364xx_fillbuff(struct zr364xx_camera *cam,
printk(KERN_ERR KBUILD_MODNAME ": =======no frame\n");
return;
}
- DBG("%s: Buffer 0x%08lx size= %d\n", __func__,
- (unsigned long)vbuf, pos);
+ DBG("%s: Buffer %p size= %d\n", __func__, vbuf, pos);
/* tell v4l buffer was filled */
buf->vb.field_count = cam->frame_count * 2;
@@ -1277,7 +1276,7 @@ static int zr364xx_mmap(struct file *file, struct vm_area_struct *vma)
DBG("%s: cam == NULL\n", __func__);
return -ENODEV;
}
- DBG("mmap called, vma=0x%08lx\n", (unsigned long)vma);
+ DBG("mmap called, vma=%p\n", vma);
ret = videobuf_mmap_mapper(&cam->vb_vidq, vma);
diff --git a/drivers/media/v4l2-core/tuner-core.c b/drivers/media/v4l2-core/tuner-core.c
index 82852f23a3b6..7f858c39753c 100644
--- a/drivers/media/v4l2-core/tuner-core.c
+++ b/drivers/media/v4l2-core/tuner-core.c
@@ -1099,23 +1099,14 @@ static int tuner_s_radio(struct v4l2_subdev *sd)
*/
/**
- * tuner_s_power - controls the power state of the tuner
+ * tuner_standby - places the tuner in standby mode
* @sd: pointer to struct v4l2_subdev
- * @on: a zero value puts the tuner to sleep, non-zero wakes it up
*/
-static int tuner_s_power(struct v4l2_subdev *sd, int on)
+static int tuner_standby(struct v4l2_subdev *sd)
{
struct tuner *t = to_tuner(sd);
struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
- if (on) {
- if (t->standby && set_mode(t, t->mode) == 0) {
- dprintk("Waking up tuner\n");
- set_freq(t, 0);
- }
- return 0;
- }
-
dprintk("Putting tuner to sleep\n");
t->standby = true;
if (analog_ops->standby)
@@ -1328,10 +1319,10 @@ static int tuner_command(struct i2c_client *client, unsigned cmd, void *arg)
static const struct v4l2_subdev_core_ops tuner_core_ops = {
.log_status = tuner_log_status,
- .s_power = tuner_s_power,
};
static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = {
+ .standby = tuner_standby,
.s_radio = tuner_s_radio,
.g_tuner = tuner_g_tuner,
.s_tuner = tuner_s_tuner,
diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index 8650ad92b64d..b518b92d6d96 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -357,31 +357,35 @@ void v4l_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax,
}
EXPORT_SYMBOL_GPL(v4l_bound_align_image);
-const struct v4l2_frmsize_discrete *
-v4l2_find_nearest_format(const struct v4l2_frmsize_discrete *sizes,
- size_t num_sizes,
- s32 width, s32 height)
+const void *
+__v4l2_find_nearest_size(const void *array, size_t array_size,
+ size_t entry_size, size_t width_offset,
+ size_t height_offset, s32 width, s32 height)
{
- int i;
- u32 error, min_error = UINT_MAX;
- const struct v4l2_frmsize_discrete *size, *best = NULL;
+ u32 error, min_error = U32_MAX;
+ const void *best = NULL;
+ unsigned int i;
- if (!sizes)
+ if (!array)
return NULL;
- for (i = 0, size = sizes; i < num_sizes; i++, size++) {
- error = abs(size->width - width) + abs(size->height - height);
- if (error < min_error) {
- min_error = error;
- best = size;
- }
+ for (i = 0; i < array_size; i++, array += entry_size) {
+ const u32 *entry_width = array + width_offset;
+ const u32 *entry_height = array + height_offset;
+
+ error = abs(*entry_width - width) + abs(*entry_height - height);
+ if (error > min_error)
+ continue;
+
+ min_error = error;
+ best = array;
if (!error)
break;
}
return best;
}
-EXPORT_SYMBOL_GPL(v4l2_find_nearest_format);
+EXPORT_SYMBOL_GPL(__v4l2_find_nearest_size);
void v4l2_get_timestamp(struct timeval *tv)
{
@@ -392,3 +396,51 @@ void v4l2_get_timestamp(struct timeval *tv)
tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
}
EXPORT_SYMBOL_GPL(v4l2_get_timestamp);
+
+int v4l2_g_parm_cap(struct video_device *vdev,
+ struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+{
+ struct v4l2_subdev_frame_interval ival = { 0 };
+ int ret;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+
+ if (vdev->device_caps & V4L2_CAP_READWRITE)
+ a->parm.capture.readbuffers = 2;
+ if (v4l2_subdev_has_op(sd, video, g_frame_interval))
+ a->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ ret = v4l2_subdev_call(sd, video, g_frame_interval, &ival);
+ if (!ret)
+ a->parm.capture.timeperframe = ival.interval;
+ return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_g_parm_cap);
+
+int v4l2_s_parm_cap(struct video_device *vdev,
+ struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+{
+ struct v4l2_subdev_frame_interval ival = {
+ .interval = a->parm.capture.timeperframe
+ };
+ int ret;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+
+ memset(&a->parm, 0, sizeof(a->parm));
+ if (vdev->device_caps & V4L2_CAP_READWRITE)
+ a->parm.capture.readbuffers = 2;
+ else
+ a->parm.capture.readbuffers = 0;
+
+ if (v4l2_subdev_has_op(sd, video, g_frame_interval))
+ a->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ ret = v4l2_subdev_call(sd, video, s_frame_interval, &ival);
+ if (!ret)
+ a->parm.capture.timeperframe = ival.interval;
+ return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_s_parm_cap);
diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
index 5198c9eeb348..4312935f1dfc 100644
--- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
+++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
@@ -101,7 +101,7 @@ static int get_v4l2_window32(struct v4l2_window __user *kp,
static int put_v4l2_window32(struct v4l2_window __user *kp,
struct v4l2_window32 __user *up)
{
- struct v4l2_clip __user *kclips = kp->clips;
+ struct v4l2_clip __user *kclips;
struct v4l2_clip32 __user *uclips;
compat_caddr_t p;
u32 clipcount;
@@ -116,6 +116,8 @@ static int put_v4l2_window32(struct v4l2_window __user *kp,
if (!clipcount)
return 0;
+ if (get_user(kclips, &kp->clips))
+ return -EFAULT;
if (get_user(p, &up->clips))
return -EFAULT;
uclips = compat_ptr(p);
diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
index ce08b50b8290..d29e45516eb7 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -480,6 +480,57 @@ const char * const *v4l2_ctrl_get_menu(u32 id)
NULL,
};
+ static const char * const hevc_profile[] = {
+ "Main",
+ "Main Still Picture",
+ "Main 10",
+ NULL,
+ };
+ static const char * const hevc_level[] = {
+ "1",
+ "2",
+ "2.1",
+ "3",
+ "3.1",
+ "4",
+ "4.1",
+ "5",
+ "5.1",
+ "5.2",
+ "6",
+ "6.1",
+ "6.2",
+ NULL,
+ };
+ static const char * const hevc_hierarchial_coding_type[] = {
+ "B",
+ "P",
+ NULL,
+ };
+ static const char * const hevc_refresh_type[] = {
+ "None",
+ "CRA",
+ "IDR",
+ NULL,
+ };
+ static const char * const hevc_size_of_length_field[] = {
+ "0",
+ "1",
+ "2",
+ "4",
+ NULL,
+ };
+ static const char * const hevc_tier[] = {
+ "Main",
+ "High",
+ NULL,
+ };
+ static const char * const hevc_loop_filter_mode[] = {
+ "Disabled",
+ "Enabled",
+ "Disabled at slice boundary",
+ "NULL",
+ };
switch (id) {
case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
@@ -575,6 +626,20 @@ const char * const *v4l2_ctrl_get_menu(u32 id)
return dv_it_content_type;
case V4L2_CID_DETECT_MD_MODE:
return detect_md_mode;
+ case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE:
+ return hevc_profile;
+ case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL:
+ return hevc_level;
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE:
+ return hevc_hierarchial_coding_type;
+ case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE:
+ return hevc_refresh_type;
+ case V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD:
+ return hevc_size_of_length_field;
+ case V4L2_CID_MPEG_VIDEO_HEVC_TIER:
+ return hevc_tier;
+ case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE:
+ return hevc_loop_filter_mode;
default:
return NULL;
@@ -776,6 +841,53 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP: return "VPX P-Frame QP Value";
case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: return "VPX Profile";
+ /* HEVC controls */
+ case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP: return "HEVC I-Frame QP Value";
+ case V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP: return "HEVC P-Frame QP Value";
+ case V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP: return "HEVC B-Frame QP Value";
+ case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP: return "HEVC Minimum QP Value";
+ case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP: return "HEVC Maximum QP Value";
+ case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: return "HEVC Profile";
+ case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL: return "HEVC Level";
+ case V4L2_CID_MPEG_VIDEO_HEVC_TIER: return "HEVC Tier";
+ case V4L2_CID_MPEG_VIDEO_HEVC_FRAME_RATE_RESOLUTION: return "HEVC Frame Rate Resolution";
+ case V4L2_CID_MPEG_VIDEO_HEVC_MAX_PARTITION_DEPTH: return "HEVC Maximum Coding Unit Depth";
+ case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE: return "HEVC Refresh Type";
+ case V4L2_CID_MPEG_VIDEO_HEVC_CONST_INTRA_PRED: return "HEVC Constant Intra Prediction";
+ case V4L2_CID_MPEG_VIDEO_HEVC_LOSSLESS_CU: return "HEVC Lossless Encoding";
+ case V4L2_CID_MPEG_VIDEO_HEVC_WAVEFRONT: return "HEVC Wavefront";
+ case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE: return "HEVC Loop Filter";
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_QP: return "HEVC QP Values";
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE: return "HEVC Hierarchical Coding Type";
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_LAYER: return "HEVC Hierarchical Coding Layer";
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_QP: return "HEVC Hierarchical Layer 0 QP";
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_QP: return "HEVC Hierarchical Layer 1 QP";
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_QP: return "HEVC Hierarchical Layer 2 QP";
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_QP: return "HEVC Hierarchical Layer 3 QP";
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_QP: return "HEVC Hierarchical Layer 4 QP";
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_QP: return "HEVC Hierarchical Layer 5 QP";
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_QP: return "HEVC Hierarchical Layer 6 QP";
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_BR: return "HEVC Hierarchical Lay 0 BitRate";
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_BR: return "HEVC Hierarchical Lay 1 BitRate";
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_BR: return "HEVC Hierarchical Lay 2 BitRate";
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_BR: return "HEVC Hierarchical Lay 3 BitRate";
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_BR: return "HEVC Hierarchical Lay 4 BitRate";
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_BR: return "HEVC Hierarchical Lay 5 BitRate";
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_BR: return "HEVC Hierarchical Lay 6 BitRate";
+ case V4L2_CID_MPEG_VIDEO_HEVC_GENERAL_PB: return "HEVC General PB";
+ case V4L2_CID_MPEG_VIDEO_HEVC_TEMPORAL_ID: return "HEVC Temporal ID";
+ case V4L2_CID_MPEG_VIDEO_HEVC_STRONG_SMOOTHING: return "HEVC Strong Intra Smoothing";
+ case V4L2_CID_MPEG_VIDEO_HEVC_INTRA_PU_SPLIT: return "HEVC Intra PU Split";
+ case V4L2_CID_MPEG_VIDEO_HEVC_TMV_PREDICTION: return "HEVC TMV Prediction";
+ case V4L2_CID_MPEG_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1: return "HEVC Max Num of Candidate MVs";
+ case V4L2_CID_MPEG_VIDEO_HEVC_WITHOUT_STARTCODE: return "HEVC ENC Without Startcode";
+ case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD: return "HEVC Num of I-Frame b/w 2 IDR";
+ case V4L2_CID_MPEG_VIDEO_HEVC_LF_BETA_OFFSET_DIV2: return "HEVC Loop Filter Beta Offset";
+ case V4L2_CID_MPEG_VIDEO_HEVC_LF_TC_OFFSET_DIV2: return "HEVC Loop Filter TC Offset";
+ case V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD: return "HEVC Size of Length Field";
+ case V4L2_CID_MPEG_VIDEO_REF_NUMBER_FOR_PFRAMES: return "Reference Frames for a P-Frame";
+ case V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR: return "Prepend SPS and PPS to IDR";
+
/* CAMERA controls */
/* Keep the order of the 'case's the same as in v4l2-controls.h! */
case V4L2_CID_CAMERA_CLASS: return "Camera Controls";
@@ -1069,6 +1181,13 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
case V4L2_CID_TUNE_DEEMPHASIS:
case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL:
case V4L2_CID_DETECT_MD_MODE:
+ case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE:
+ case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL:
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE:
+ case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE:
+ case V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD:
+ case V4L2_CID_MPEG_VIDEO_HEVC_TIER:
+ case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE:
*type = V4L2_CTRL_TYPE_MENU;
break;
case V4L2_CID_LINK_FREQ:
diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
index 0301fe426a43..1d0b2208e8fb 100644
--- a/drivers/media/v4l2-core/v4l2-dev.c
+++ b/drivers/media/v4l2-core/v4l2-dev.c
@@ -939,10 +939,14 @@ int __video_register_device(struct video_device *vdev,
#endif
vdev->minor = i + minor_offset;
vdev->num = nr;
- devnode_set(vdev);
/* Should not happen since we thought this minor was free */
- WARN_ON(video_device[vdev->minor] != NULL);
+ if (WARN_ON(video_device[vdev->minor])) {
+ mutex_unlock(&videodev_lock);
+ printk(KERN_ERR "video_device not empty!\n");
+ return -ENFILE;
+ }
+ devnode_set(vdev);
vdev->index = get_index(vdev);
video_device[vdev->minor] = vdev;
mutex_unlock(&videodev_lock);
diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c
index e2ee5f00c445..c81faea96fba 100644
--- a/drivers/media/v4l2-core/v4l2-dv-timings.c
+++ b/drivers/media/v4l2-core/v4l2-dv-timings.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* v4l2-dv-timings - dv-timings helper functions
*
* Copyright 2013 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * 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
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * 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/module.h>
@@ -27,6 +14,7 @@
#include <linux/v4l2-dv-timings.h>
#include <media/v4l2-dv-timings.h>
#include <linux/math64.h>
+#include <linux/hdmi.h>
MODULE_AUTHOR("Hans Verkuil");
MODULE_DESCRIPTION("V4L2 DV Timings Helper Functions");
@@ -814,3 +802,143 @@ struct v4l2_fract v4l2_calc_aspect_ratio(u8 hor_landscape, u8 vert_portrait)
return aspect;
}
EXPORT_SYMBOL_GPL(v4l2_calc_aspect_ratio);
+
+/** v4l2_hdmi_rx_colorimetry - determine HDMI colorimetry information
+ * based on various InfoFrames.
+ * @avi: the AVI InfoFrame
+ * @hdmi: the HDMI Vendor InfoFrame, may be NULL
+ * @height: the frame height
+ *
+ * Determines the HDMI colorimetry information, i.e. how the HDMI
+ * pixel color data should be interpreted.
+ *
+ * Note that some of the newer features (DCI-P3, HDR) are not yet
+ * implemented: the hdmi.h header needs to be updated to the HDMI 2.0
+ * and CTA-861-G standards.
+ */
+struct v4l2_hdmi_colorimetry
+v4l2_hdmi_rx_colorimetry(const struct hdmi_avi_infoframe *avi,
+ const struct hdmi_vendor_infoframe *hdmi,
+ unsigned int height)
+{
+ struct v4l2_hdmi_colorimetry c = {
+ V4L2_COLORSPACE_SRGB,
+ V4L2_YCBCR_ENC_DEFAULT,
+ V4L2_QUANTIZATION_FULL_RANGE,
+ V4L2_XFER_FUNC_SRGB
+ };
+ bool is_ce = avi->video_code || (hdmi && hdmi->vic);
+ bool is_sdtv = height <= 576;
+ bool default_is_lim_range_rgb = avi->video_code > 1;
+
+ switch (avi->colorspace) {
+ case HDMI_COLORSPACE_RGB:
+ /* RGB pixel encoding */
+ switch (avi->colorimetry) {
+ case HDMI_COLORIMETRY_EXTENDED:
+ switch (avi->extended_colorimetry) {
+ case HDMI_EXTENDED_COLORIMETRY_ADOBE_RGB:
+ c.colorspace = V4L2_COLORSPACE_ADOBERGB;
+ c.xfer_func = V4L2_XFER_FUNC_ADOBERGB;
+ break;
+ case HDMI_EXTENDED_COLORIMETRY_BT2020:
+ c.colorspace = V4L2_COLORSPACE_BT2020;
+ c.xfer_func = V4L2_XFER_FUNC_709;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ switch (avi->quantization_range) {
+ case HDMI_QUANTIZATION_RANGE_LIMITED:
+ c.quantization = V4L2_QUANTIZATION_LIM_RANGE;
+ break;
+ case HDMI_QUANTIZATION_RANGE_FULL:
+ break;
+ default:
+ if (default_is_lim_range_rgb)
+ c.quantization = V4L2_QUANTIZATION_LIM_RANGE;
+ break;
+ }
+ break;
+
+ default:
+ /* YCbCr pixel encoding */
+ c.quantization = V4L2_QUANTIZATION_LIM_RANGE;
+ switch (avi->colorimetry) {
+ case HDMI_COLORIMETRY_NONE:
+ if (!is_ce)
+ break;
+ if (is_sdtv) {
+ c.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ c.ycbcr_enc = V4L2_YCBCR_ENC_601;
+ } else {
+ c.colorspace = V4L2_COLORSPACE_REC709;
+ c.ycbcr_enc = V4L2_YCBCR_ENC_709;
+ }
+ c.xfer_func = V4L2_XFER_FUNC_709;
+ break;
+ case HDMI_COLORIMETRY_ITU_601:
+ c.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ c.ycbcr_enc = V4L2_YCBCR_ENC_601;
+ c.xfer_func = V4L2_XFER_FUNC_709;
+ break;
+ case HDMI_COLORIMETRY_ITU_709:
+ c.colorspace = V4L2_COLORSPACE_REC709;
+ c.ycbcr_enc = V4L2_YCBCR_ENC_709;
+ c.xfer_func = V4L2_XFER_FUNC_709;
+ break;
+ case HDMI_COLORIMETRY_EXTENDED:
+ switch (avi->extended_colorimetry) {
+ case HDMI_EXTENDED_COLORIMETRY_XV_YCC_601:
+ c.colorspace = V4L2_COLORSPACE_REC709;
+ c.ycbcr_enc = V4L2_YCBCR_ENC_XV709;
+ c.xfer_func = V4L2_XFER_FUNC_709;
+ break;
+ case HDMI_EXTENDED_COLORIMETRY_XV_YCC_709:
+ c.colorspace = V4L2_COLORSPACE_REC709;
+ c.ycbcr_enc = V4L2_YCBCR_ENC_XV601;
+ c.xfer_func = V4L2_XFER_FUNC_709;
+ break;
+ case HDMI_EXTENDED_COLORIMETRY_S_YCC_601:
+ c.colorspace = V4L2_COLORSPACE_SRGB;
+ c.ycbcr_enc = V4L2_YCBCR_ENC_601;
+ c.xfer_func = V4L2_XFER_FUNC_SRGB;
+ break;
+ case HDMI_EXTENDED_COLORIMETRY_ADOBE_YCC_601:
+ c.colorspace = V4L2_COLORSPACE_ADOBERGB;
+ c.ycbcr_enc = V4L2_YCBCR_ENC_601;
+ c.xfer_func = V4L2_XFER_FUNC_ADOBERGB;
+ break;
+ case HDMI_EXTENDED_COLORIMETRY_BT2020:
+ c.colorspace = V4L2_COLORSPACE_BT2020;
+ c.ycbcr_enc = V4L2_YCBCR_ENC_BT2020;
+ c.xfer_func = V4L2_XFER_FUNC_709;
+ break;
+ case HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM:
+ c.colorspace = V4L2_COLORSPACE_BT2020;
+ c.ycbcr_enc = V4L2_YCBCR_ENC_BT2020_CONST_LUM;
+ c.xfer_func = V4L2_XFER_FUNC_709;
+ break;
+ default: /* fall back to ITU_709 */
+ c.colorspace = V4L2_COLORSPACE_REC709;
+ c.ycbcr_enc = V4L2_YCBCR_ENC_709;
+ c.xfer_func = V4L2_XFER_FUNC_709;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ /*
+ * YCC Quantization Range signaling is more-or-less broken,
+ * let's just ignore this.
+ */
+ break;
+ }
+ return c;
+}
+EXPORT_SYMBOL_GPL(v4l2_hdmi_rx_colorimetry);
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 260288ca4f55..f48c505550e0 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1273,6 +1273,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_PIX_FMT_VC1_ANNEX_L: descr = "VC-1 (SMPTE 412M Annex L)"; break;
case V4L2_PIX_FMT_VP8: descr = "VP8"; break;
case V4L2_PIX_FMT_VP9: descr = "VP9"; break;
+ case V4L2_PIX_FMT_HEVC: descr = "HEVC"; break; /* aka H.265 */
case V4L2_PIX_FMT_CPIA1: descr = "GSPCA CPiA YUV"; break;
case V4L2_PIX_FMT_WNVA: descr = "WNVA"; break;
case V4L2_PIX_FMT_SN9C10X: descr = "GSPCA SN9C10X"; break;
@@ -2611,7 +2612,7 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = {
IOCTL_INFO_FNC(VIDIOC_PREPARE_BUF, v4l_prepare_buf, v4l_print_buffer, INFO_FL_QUEUE),
IOCTL_INFO_STD(VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings, v4l_print_enum_dv_timings, INFO_FL_CLEAR(v4l2_enum_dv_timings, pad)),
IOCTL_INFO_STD(VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings, v4l_print_dv_timings, INFO_FL_ALWAYS_COPY),
- IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, type)),
+ IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, pad)),
IOCTL_INFO_FNC(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0),
IOCTL_INFO_FNC(VIDIOC_DBG_G_CHIP_INFO, v4l_dbg_g_chip_info, v4l_print_dbg_chip_info, INFO_FL_CLEAR(v4l2_dbg_chip_info, match)),
IOCTL_INFO_FNC(VIDIOC_QUERY_EXT_CTRL, v4l_query_ext_ctrl, v4l_print_query_ext_ctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_query_ext_ctrl, id)),
@@ -2832,14 +2833,15 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
size_t array_size = 0;
void __user *user_ptr = NULL;
void **kernel_ptr = NULL;
+ const size_t ioc_size = _IOC_SIZE(cmd);
/* Copy arguments into temp kernel buffer */
if (_IOC_DIR(cmd) != _IOC_NONE) {
- if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
+ if (ioc_size <= sizeof(sbuf)) {
parg = sbuf;
} else {
/* too big to allocate from stack */
- mbuf = kvmalloc(_IOC_SIZE(cmd), GFP_KERNEL);
+ mbuf = kvmalloc(ioc_size, GFP_KERNEL);
if (NULL == mbuf)
return -ENOMEM;
parg = mbuf;
@@ -2847,7 +2849,7 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
err = -EFAULT;
if (_IOC_DIR(cmd) & _IOC_WRITE) {
- unsigned int n = _IOC_SIZE(cmd);
+ unsigned int n = ioc_size;
/*
* In some cases, only a few fields are used as input,
@@ -2868,11 +2870,11 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
goto out;
/* zero out anything we don't copy from userspace */
- if (n < _IOC_SIZE(cmd))
- memset((u8 *)parg + n, 0, _IOC_SIZE(cmd) - n);
+ if (n < ioc_size)
+ memset((u8 *)parg + n, 0, ioc_size - n);
} else {
/* read-only ioctl */
- memset(parg, 0, _IOC_SIZE(cmd));
+ memset(parg, 0, ioc_size);
}
}
@@ -2930,7 +2932,7 @@ out_array_args:
switch (_IOC_DIR(cmd)) {
case _IOC_READ:
case (_IOC_WRITE | _IOC_READ):
- if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
+ if (copy_to_user((void __user *)arg, parg, ioc_size))
err = -EFAULT;
break;
}
diff --git a/drivers/media/v4l2-core/v4l2-mc.c b/drivers/media/v4l2-core/v4l2-mc.c
index 1d550afeda13..0fc185a2ce90 100644
--- a/drivers/media/v4l2-core/v4l2-mc.c
+++ b/drivers/media/v4l2-core/v4l2-mc.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
/*
* Media Controller ancillary functions
*
@@ -5,16 +7,6 @@
* Copyright (C) 2016 Shuah Khan <shuahkh@osg.samsung.com>
* Copyright (C) 2006-2010 Nokia Corporation
* Copyright (c) 2016 Intel Corporation.
- *
- * 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.
*/
#include <linux/module.h>
diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
index c5639817db34..f9eed938d348 100644
--- a/drivers/media/v4l2-core/v4l2-subdev.c
+++ b/drivers/media/v4l2-core/v4l2-subdev.c
@@ -187,27 +187,51 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
switch (cmd) {
case VIDIOC_QUERYCTRL:
+ /*
+ * TODO: this really should be folded into v4l2_queryctrl (this
+ * currently returns -EINVAL for NULL control handlers).
+ * However, v4l2_queryctrl() is still called directly by
+ * drivers as well and until that has been addressed I believe
+ * it is safer to do the check here. The same is true for the
+ * other control ioctls below.
+ */
+ if (!vfh->ctrl_handler)
+ return -ENOTTY;
return v4l2_queryctrl(vfh->ctrl_handler, arg);
case VIDIOC_QUERY_EXT_CTRL:
+ if (!vfh->ctrl_handler)
+ return -ENOTTY;
return v4l2_query_ext_ctrl(vfh->ctrl_handler, arg);
case VIDIOC_QUERYMENU:
+ if (!vfh->ctrl_handler)
+ return -ENOTTY;
return v4l2_querymenu(vfh->ctrl_handler, arg);
case VIDIOC_G_CTRL:
+ if (!vfh->ctrl_handler)
+ return -ENOTTY;
return v4l2_g_ctrl(vfh->ctrl_handler, arg);
case VIDIOC_S_CTRL:
+ if (!vfh->ctrl_handler)
+ return -ENOTTY;
return v4l2_s_ctrl(vfh, vfh->ctrl_handler, arg);
case VIDIOC_G_EXT_CTRLS:
+ if (!vfh->ctrl_handler)
+ return -ENOTTY;
return v4l2_g_ext_ctrls(vfh->ctrl_handler, arg);
case VIDIOC_S_EXT_CTRLS:
+ if (!vfh->ctrl_handler)
+ return -ENOTTY;
return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, arg);
case VIDIOC_TRY_EXT_CTRLS:
+ if (!vfh->ctrl_handler)
+ return -ENOTTY;
return v4l2_try_ext_ctrls(vfh->ctrl_handler, arg);
case VIDIOC_DQEVENT:
@@ -239,6 +263,19 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
return -EPERM;
return v4l2_subdev_call(sd, core, s_register, p);
}
+ case VIDIOC_DBG_G_CHIP_INFO:
+ {
+ struct v4l2_dbg_chip_info *p = arg;
+
+ if (p->match.type != V4L2_CHIP_MATCH_SUBDEV || p->match.addr)
+ return -EINVAL;
+ if (sd->ops->core && sd->ops->core->s_register)
+ p->flags |= V4L2_CHIP_FL_WRITABLE;
+ if (sd->ops->core && sd->ops->core->g_register)
+ p->flags |= V4L2_CHIP_FL_READABLE;
+ strlcpy(p->name, sd->name, sizeof(p->name));
+ return 0;
+ }
#endif
case VIDIOC_LOG_STATUS: {
@@ -260,6 +297,8 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (rval)
return rval;
+ memset(format->reserved, 0, sizeof(format->reserved));
+ memset(format->format.reserved, 0, sizeof(format->format.reserved));
return v4l2_subdev_call(sd, pad, get_fmt, subdev_fh->pad, format);
}
@@ -270,6 +309,8 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (rval)
return rval;
+ memset(format->reserved, 0, sizeof(format->reserved));
+ memset(format->format.reserved, 0, sizeof(format->format.reserved));
return v4l2_subdev_call(sd, pad, set_fmt, subdev_fh->pad, format);
}
@@ -281,6 +322,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (rval)
return rval;
+ memset(crop->reserved, 0, sizeof(crop->reserved));
memset(&sel, 0, sizeof(sel));
sel.which = crop->which;
sel.pad = crop->pad;
@@ -298,6 +340,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
struct v4l2_subdev_crop *crop = arg;
struct v4l2_subdev_selection sel;
+ memset(crop->reserved, 0, sizeof(crop->reserved));
rval = check_crop(sd, crop);
if (rval)
return rval;
@@ -326,6 +369,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (code->pad >= sd->entity.num_pads)
return -EINVAL;
+ memset(code->reserved, 0, sizeof(code->reserved));
return v4l2_subdev_call(sd, pad, enum_mbus_code, subdev_fh->pad,
code);
}
@@ -340,6 +384,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (fse->pad >= sd->entity.num_pads)
return -EINVAL;
+ memset(fse->reserved, 0, sizeof(fse->reserved));
return v4l2_subdev_call(sd, pad, enum_frame_size, subdev_fh->pad,
fse);
}
@@ -350,6 +395,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (fi->pad >= sd->entity.num_pads)
return -EINVAL;
+ memset(fi->reserved, 0, sizeof(fi->reserved));
return v4l2_subdev_call(sd, video, g_frame_interval, arg);
}
@@ -359,6 +405,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (fi->pad >= sd->entity.num_pads)
return -EINVAL;
+ memset(fi->reserved, 0, sizeof(fi->reserved));
return v4l2_subdev_call(sd, video, s_frame_interval, arg);
}
@@ -372,6 +419,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (fie->pad >= sd->entity.num_pads)
return -EINVAL;
+ memset(fie->reserved, 0, sizeof(fie->reserved));
return v4l2_subdev_call(sd, pad, enum_frame_interval, subdev_fh->pad,
fie);
}
@@ -383,6 +431,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (rval)
return rval;
+ memset(sel->reserved, 0, sizeof(sel->reserved));
return v4l2_subdev_call(
sd, pad, get_selection, subdev_fh->pad, sel);
}
@@ -394,6 +443,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (rval)
return rval;
+ memset(sel->reserved, 0, sizeof(sel->reserved));
return v4l2_subdev_call(
sd, pad, set_selection, subdev_fh->pad, sel);
}
diff --git a/drivers/media/v4l2-core/videobuf-dma-sg.c b/drivers/media/v4l2-core/videobuf-dma-sg.c
index f412429cf5ba..add2edb23eac 100644
--- a/drivers/media/v4l2-core/videobuf-dma-sg.c
+++ b/drivers/media/v4l2-core/videobuf-dma-sg.c
@@ -244,9 +244,8 @@ static int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction,
goto out_free_pages;
}
- dprintk(1, "vmalloc is at addr 0x%08lx, size=%d\n",
- (unsigned long)dma->vaddr,
- nr_pages << PAGE_SHIFT);
+ dprintk(1, "vmalloc is at addr %p, size=%d\n",
+ dma->vaddr, nr_pages << PAGE_SHIFT);
memset(dma->vaddr, 0, nr_pages << PAGE_SHIFT);
dma->nr_pages = nr_pages;
diff --git a/drivers/memory/emif-asm-offsets.c b/drivers/memory/emif-asm-offsets.c
index 71a89d5d3efd..db8043019ec6 100644
--- a/drivers/memory/emif-asm-offsets.c
+++ b/drivers/memory/emif-asm-offsets.c
@@ -16,77 +16,7 @@
int main(void)
{
- DEFINE(EMIF_SDCFG_VAL_OFFSET,
- offsetof(struct emif_regs_amx3, emif_sdcfg_val));
- DEFINE(EMIF_TIMING1_VAL_OFFSET,
- offsetof(struct emif_regs_amx3, emif_timing1_val));
- DEFINE(EMIF_TIMING2_VAL_OFFSET,
- offsetof(struct emif_regs_amx3, emif_timing2_val));
- DEFINE(EMIF_TIMING3_VAL_OFFSET,
- offsetof(struct emif_regs_amx3, emif_timing3_val));
- DEFINE(EMIF_REF_CTRL_VAL_OFFSET,
- offsetof(struct emif_regs_amx3, emif_ref_ctrl_val));
- DEFINE(EMIF_ZQCFG_VAL_OFFSET,
- offsetof(struct emif_regs_amx3, emif_zqcfg_val));
- DEFINE(EMIF_PMCR_VAL_OFFSET,
- offsetof(struct emif_regs_amx3, emif_pmcr_val));
- DEFINE(EMIF_PMCR_SHDW_VAL_OFFSET,
- offsetof(struct emif_regs_amx3, emif_pmcr_shdw_val));
- DEFINE(EMIF_RD_WR_LEVEL_RAMP_CTRL_OFFSET,
- offsetof(struct emif_regs_amx3, emif_rd_wr_level_ramp_ctrl));
- DEFINE(EMIF_RD_WR_EXEC_THRESH_OFFSET,
- offsetof(struct emif_regs_amx3, emif_rd_wr_exec_thresh));
- DEFINE(EMIF_COS_CONFIG_OFFSET,
- offsetof(struct emif_regs_amx3, emif_cos_config));
- DEFINE(EMIF_PRIORITY_TO_COS_MAPPING_OFFSET,
- offsetof(struct emif_regs_amx3, emif_priority_to_cos_mapping));
- DEFINE(EMIF_CONNECT_ID_SERV_1_MAP_OFFSET,
- offsetof(struct emif_regs_amx3, emif_connect_id_serv_1_map));
- DEFINE(EMIF_CONNECT_ID_SERV_2_MAP_OFFSET,
- offsetof(struct emif_regs_amx3, emif_connect_id_serv_2_map));
- DEFINE(EMIF_OCP_CONFIG_VAL_OFFSET,
- offsetof(struct emif_regs_amx3, emif_ocp_config_val));
- DEFINE(EMIF_LPDDR2_NVM_TIM_OFFSET,
- offsetof(struct emif_regs_amx3, emif_lpddr2_nvm_tim));
- DEFINE(EMIF_LPDDR2_NVM_TIM_SHDW_OFFSET,
- offsetof(struct emif_regs_amx3, emif_lpddr2_nvm_tim_shdw));
- DEFINE(EMIF_DLL_CALIB_CTRL_VAL_OFFSET,
- offsetof(struct emif_regs_amx3, emif_dll_calib_ctrl_val));
- DEFINE(EMIF_DLL_CALIB_CTRL_VAL_SHDW_OFFSET,
- offsetof(struct emif_regs_amx3, emif_dll_calib_ctrl_val_shdw));
- DEFINE(EMIF_DDR_PHY_CTLR_1_OFFSET,
- offsetof(struct emif_regs_amx3, emif_ddr_phy_ctlr_1));
- DEFINE(EMIF_EXT_PHY_CTRL_VALS_OFFSET,
- offsetof(struct emif_regs_amx3, emif_ext_phy_ctrl_vals));
- DEFINE(EMIF_REGS_AMX3_SIZE, sizeof(struct emif_regs_amx3));
-
- BLANK();
-
- DEFINE(EMIF_PM_BASE_ADDR_VIRT_OFFSET,
- offsetof(struct ti_emif_pm_data, ti_emif_base_addr_virt));
- DEFINE(EMIF_PM_BASE_ADDR_PHYS_OFFSET,
- offsetof(struct ti_emif_pm_data, ti_emif_base_addr_phys));
- DEFINE(EMIF_PM_CONFIG_OFFSET,
- offsetof(struct ti_emif_pm_data, ti_emif_sram_config));
- DEFINE(EMIF_PM_REGS_VIRT_OFFSET,
- offsetof(struct ti_emif_pm_data, regs_virt));
- DEFINE(EMIF_PM_REGS_PHYS_OFFSET,
- offsetof(struct ti_emif_pm_data, regs_phys));
- DEFINE(EMIF_PM_DATA_SIZE, sizeof(struct ti_emif_pm_data));
-
- BLANK();
-
- DEFINE(EMIF_PM_SAVE_CONTEXT_OFFSET,
- offsetof(struct ti_emif_pm_functions, save_context));
- DEFINE(EMIF_PM_RESTORE_CONTEXT_OFFSET,
- offsetof(struct ti_emif_pm_functions, restore_context));
- DEFINE(EMIF_PM_ENTER_SR_OFFSET,
- offsetof(struct ti_emif_pm_functions, enter_sr));
- DEFINE(EMIF_PM_EXIT_SR_OFFSET,
- offsetof(struct ti_emif_pm_functions, exit_sr));
- DEFINE(EMIF_PM_ABORT_SR_OFFSET,
- offsetof(struct ti_emif_pm_functions, abort_sr));
- DEFINE(EMIF_PM_FUNCTIONS_SIZE, sizeof(struct ti_emif_pm_functions));
+ ti_emif_asm_offsets();
return 0;
}
diff --git a/drivers/memory/emif.c b/drivers/memory/emif.c
index 04644e7b42b1..2f214440008c 100644
--- a/drivers/memory/emif.c
+++ b/drivers/memory/emif.c
@@ -127,7 +127,7 @@ static int emif_regdump_show(struct seq_file *s, void *unused)
for (i = 0; i < EMIF_MAX_NUM_FREQUENCIES && regs_cache[i]; i++) {
do_emif_regdump_show(s, emif, regs_cache[i]);
- seq_printf(s, "\n");
+ seq_putc(s, '\n');
}
return 0;
diff --git a/drivers/memory/samsung/Kconfig b/drivers/memory/samsung/Kconfig
index 9de12222061c..79ce7ea58903 100644
--- a/drivers/memory/samsung/Kconfig
+++ b/drivers/memory/samsung/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
config SAMSUNG_MC
bool "Samsung Exynos Memory Controller support" if COMPILE_TEST
help
diff --git a/drivers/memory/samsung/Makefile b/drivers/memory/samsung/Makefile
index 9c554d5522ad..00587be66211 100644
--- a/drivers/memory/samsung/Makefile
+++ b/drivers/memory/samsung/Makefile
@@ -1 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_EXYNOS_SROM) += exynos-srom.o
diff --git a/drivers/memory/samsung/exynos-srom.c b/drivers/memory/samsung/exynos-srom.c
index bf827a666694..7edd7fb540f2 100644
--- a/drivers/memory/samsung/exynos-srom.c
+++ b/drivers/memory/samsung/exynos-srom.c
@@ -1,14 +1,10 @@
-/*
- * Copyright (c) 2015 Samsung Electronics Co., Ltd.
- * http://www.samsung.com/
- *
- * EXYNOS - SROM Controller support
- * Author: Pankaj Dubey <pankaj.dubey@samsung.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.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2015 Samsung Electronics Co., Ltd.
+// http://www.samsung.com/
+//
+// EXYNOS - SROM Controller support
+// Author: Pankaj Dubey <pankaj.dubey@samsung.com>
#include <linux/io.h>
#include <linux/init.h>
diff --git a/drivers/memory/samsung/exynos-srom.h b/drivers/memory/samsung/exynos-srom.h
index 34660c6a57a9..da612797f522 100644
--- a/drivers/memory/samsung/exynos-srom.h
+++ b/drivers/memory/samsung/exynos-srom.h
@@ -1,13 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Exynos SROMC register definitions
- *
- * 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.
-*/
+ */
#ifndef __EXYNOS_SROM_H
#define __EXYNOS_SROM_H __FILE__
diff --git a/drivers/memory/ti-emif-pm.c b/drivers/memory/ti-emif-pm.c
index 62a86c4bcd0b..632651f4b6e8 100644
--- a/drivers/memory/ti-emif-pm.c
+++ b/drivers/memory/ti-emif-pm.c
@@ -271,7 +271,6 @@ static int ti_emif_probe(struct platform_device *pdev)
emif_data->pm_data.ti_emif_base_addr_virt = devm_ioremap_resource(dev,
res);
if (IS_ERR(emif_data->pm_data.ti_emif_base_addr_virt)) {
- dev_err(dev, "could not ioremap emif mem\n");
ret = PTR_ERR(emif_data->pm_data.ti_emif_base_addr_virt);
return ret;
}
diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c
index 439ee9c5f535..86503f60468f 100644
--- a/drivers/message/fusion/mptsas.c
+++ b/drivers/message/fusion/mptsas.c
@@ -1994,6 +1994,7 @@ static struct scsi_host_template mptsas_driver_template = {
.cmd_per_lun = 7,
.use_clustering = ENABLE_CLUSTERING,
.shost_attrs = mptscsih_host_attrs,
+ .no_write_same = 1,
};
static int mptsas_get_linkerrors(struct sas_phy *phy)
@@ -2967,7 +2968,7 @@ out_unlock:
mutex_unlock(&ioc->sas_mgmt.mutex);
out:
return ret;
- }
+}
static void
mptsas_parse_device_info(struct sas_identify *identify,
diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c
index e4fafdd96e5e..eafd06f62a3a 100644
--- a/drivers/mfd/cros_ec_dev.c
+++ b/drivers/mfd/cros_ec_dev.c
@@ -305,8 +305,8 @@ static void cros_ec_sensors_register(struct cros_ec_dev *ec)
resp = (struct ec_response_motion_sense *)msg->data;
sensor_num = resp->dump.sensor_count;
- /* Allocate 2 extra sensors in case lid angle or FIFO are needed */
- sensor_cells = kzalloc(sizeof(struct mfd_cell) * (sensor_num + 2),
+ /* Allocate 1 extra sensors in FIFO are needed */
+ sensor_cells = kzalloc(sizeof(struct mfd_cell) * (sensor_num + 1),
GFP_KERNEL);
if (sensor_cells == NULL)
goto error;
@@ -362,16 +362,10 @@ static void cros_ec_sensors_register(struct cros_ec_dev *ec)
sensor_type[resp->info.type]++;
id++;
}
- if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2) {
- sensor_platforms[id].sensor_num = sensor_num;
- sensor_cells[id].name = "cros-ec-angle";
- sensor_cells[id].id = 0;
- sensor_cells[id].platform_data = &sensor_platforms[id];
- sensor_cells[id].pdata_size =
- sizeof(struct cros_ec_sensor_platform);
- id++;
- }
+ if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2)
+ ec->has_kb_wake_angle = true;
+
if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
sensor_cells[id].name = "cros-ec-ring";
id++;
@@ -424,6 +418,14 @@ static int ec_device_probe(struct platform_device *pdev)
goto failed;
}
+ /* check whether this EC is a sensor hub. */
+ if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE))
+ cros_ec_sensors_register(ec);
+
+ /* Take control of the lightbar from the EC. */
+ lb_manual_suspend_ctrl(ec, 1);
+
+ /* We can now add the sysfs class, we know which parameter to show */
retval = cdev_device_add(&ec->cdev, &ec->class_dev);
if (retval) {
dev_err(dev, "cdev_device_add failed => %d\n", retval);
@@ -433,13 +435,6 @@ static int ec_device_probe(struct platform_device *pdev)
if (cros_ec_debugfs_init(ec))
dev_warn(dev, "failed to create debugfs directory\n");
- /* check whether this EC is a sensor hub. */
- if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE))
- cros_ec_sensors_register(ec);
-
- /* Take control of the lightbar from the EC. */
- lb_manual_suspend_ctrl(ec, 1);
-
return 0;
failed:
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 03605f8fc0dc..5d713008749b 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -75,7 +75,6 @@ config ATMEL_TCB_CLKSRC
config ATMEL_TCB_CLKSRC_BLOCK
int
depends on ATMEL_TCB_CLKSRC
- prompt "TC Block" if CPU_AT32AP700X
default 0
range 0 1
help
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index c3c8624f4d95..20be70c3f118 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -12,7 +12,7 @@ obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o
obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o
obj-$(CONFIG_DUMMY_IRQ) += dummy-irq.o
obj-$(CONFIG_ICS932S401) += ics932s401.o
-obj-$(CONFIG_LKDTM) += lkdtm.o
+obj-$(CONFIG_LKDTM) += lkdtm/
obj-$(CONFIG_TIFM_CORE) += tifm_core.o
obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o
obj-$(CONFIG_PHANTOM) += phantom.o
@@ -57,21 +57,3 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o
obj-$(CONFIG_OCXL) += ocxl/
obj-$(CONFIG_MISC_RTSX) += cardreader/
-
-lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o
-lkdtm-$(CONFIG_LKDTM) += lkdtm_bugs.o
-lkdtm-$(CONFIG_LKDTM) += lkdtm_heap.o
-lkdtm-$(CONFIG_LKDTM) += lkdtm_perms.o
-lkdtm-$(CONFIG_LKDTM) += lkdtm_refcount.o
-lkdtm-$(CONFIG_LKDTM) += lkdtm_rodata_objcopy.o
-lkdtm-$(CONFIG_LKDTM) += lkdtm_usercopy.o
-
-KCOV_INSTRUMENT_lkdtm_rodata.o := n
-
-OBJCOPYFLAGS :=
-OBJCOPYFLAGS_lkdtm_rodata_objcopy.o := \
- --set-section-flags .text=alloc,readonly \
- --rename-section .text=.rodata
-targets += lkdtm_rodata.o lkdtm_rodata_objcopy.o
-$(obj)/lkdtm_rodata_objcopy.o: $(obj)/lkdtm_rodata.o FORCE
- $(call if_changed,objcopy)
diff --git a/drivers/misc/aspeed-lpc-ctrl.c b/drivers/misc/aspeed-lpc-ctrl.c
index b5439643f54b..a024f8042259 100644
--- a/drivers/misc/aspeed-lpc-ctrl.c
+++ b/drivers/misc/aspeed-lpc-ctrl.c
@@ -7,6 +7,7 @@
* 2 of the License, or (at your option) any later version.
*/
+#include <linux/clk.h>
#include <linux/mfd/syscon.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
@@ -20,12 +21,17 @@
#define DEVICE_NAME "aspeed-lpc-ctrl"
+#define HICR5 0x0
+#define HICR5_ENL2H BIT(8)
+#define HICR5_ENFWH BIT(10)
+
#define HICR7 0x8
#define HICR8 0xc
struct aspeed_lpc_ctrl {
struct miscdevice miscdev;
struct regmap *regmap;
+ struct clk *clk;
phys_addr_t mem_base;
resource_size_t mem_size;
u32 pnor_size;
@@ -153,8 +159,18 @@ static long aspeed_lpc_ctrl_ioctl(struct file *file, unsigned int cmd,
if (rc)
return rc;
- return regmap_write(lpc_ctrl->regmap, HICR8,
- (~(map.size - 1)) | ((map.size >> 16) - 1));
+ rc = regmap_write(lpc_ctrl->regmap, HICR8,
+ (~(map.size - 1)) | ((map.size >> 16) - 1));
+ if (rc)
+ return rc;
+
+ /*
+ * Enable LPC FHW cycles. This is required for the host to
+ * access the regions specified.
+ */
+ return regmap_update_bits(lpc_ctrl->regmap, HICR5,
+ HICR5_ENFWH | HICR5_ENL2H,
+ HICR5_ENFWH | HICR5_ENL2H);
}
return -EINVAL;
@@ -221,16 +237,33 @@ static int aspeed_lpc_ctrl_probe(struct platform_device *pdev)
return -ENODEV;
}
+ lpc_ctrl->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(lpc_ctrl->clk)) {
+ dev_err(dev, "couldn't get clock\n");
+ return PTR_ERR(lpc_ctrl->clk);
+ }
+ rc = clk_prepare_enable(lpc_ctrl->clk);
+ if (rc) {
+ dev_err(dev, "couldn't enable clock\n");
+ return rc;
+ }
+
lpc_ctrl->miscdev.minor = MISC_DYNAMIC_MINOR;
lpc_ctrl->miscdev.name = DEVICE_NAME;
lpc_ctrl->miscdev.fops = &aspeed_lpc_ctrl_fops;
lpc_ctrl->miscdev.parent = dev;
rc = misc_register(&lpc_ctrl->miscdev);
- if (rc)
+ if (rc) {
dev_err(dev, "Unable to register device\n");
- else
- dev_info(dev, "Loaded at %pr\n", &resm);
+ goto err;
+ }
+
+ dev_info(dev, "Loaded at %pr\n", &resm);
+
+ return 0;
+err:
+ clk_disable_unprepare(lpc_ctrl->clk);
return rc;
}
@@ -239,6 +272,7 @@ static int aspeed_lpc_ctrl_remove(struct platform_device *pdev)
struct aspeed_lpc_ctrl *lpc_ctrl = dev_get_drvdata(&pdev->dev);
misc_deregister(&lpc_ctrl->miscdev);
+ clk_disable_unprepare(lpc_ctrl->clk);
return 0;
}
diff --git a/drivers/misc/cardreader/rts5260.c b/drivers/misc/cardreader/rts5260.c
index 07cb93abf685..a493b01c5bc6 100644
--- a/drivers/misc/cardreader/rts5260.c
+++ b/drivers/misc/cardreader/rts5260.c
@@ -388,17 +388,17 @@ static void rts5260_disable_ocp(struct rtsx_pcr *pcr)
OC_POWER_DOWN);
}
-int rts5260_get_ocpstat(struct rtsx_pcr *pcr, u8 *val)
+static int rts5260_get_ocpstat(struct rtsx_pcr *pcr, u8 *val)
{
return rtsx_pci_read_register(pcr, REG_OCPSTAT, val);
}
-int rts5260_get_ocpstat2(struct rtsx_pcr *pcr, u8 *val)
+static int rts5260_get_ocpstat2(struct rtsx_pcr *pcr, u8 *val)
{
return rtsx_pci_read_register(pcr, REG_DV3318_OCPSTAT, val);
}
-void rts5260_clear_ocpstat(struct rtsx_pcr *pcr)
+static void rts5260_clear_ocpstat(struct rtsx_pcr *pcr)
{
u8 mask = 0;
u8 val = 0;
@@ -418,7 +418,7 @@ void rts5260_clear_ocpstat(struct rtsx_pcr *pcr)
DV3318_OCP_INT_CLR | DV3318_OCP_CLR, 0);
}
-void rts5260_process_ocp(struct rtsx_pcr *pcr)
+static void rts5260_process_ocp(struct rtsx_pcr *pcr)
{
if (!pcr->option.ocp_en)
return;
@@ -449,7 +449,7 @@ void rts5260_process_ocp(struct rtsx_pcr *pcr)
}
}
-int rts5260_init_hw(struct rtsx_pcr *pcr)
+static int rts5260_init_hw(struct rtsx_pcr *pcr)
{
int err;
@@ -620,7 +620,7 @@ static int rts5260_extra_init_hw(struct rtsx_pcr *pcr)
return 0;
}
-void rts5260_set_aspm(struct rtsx_pcr *pcr, bool enable)
+static void rts5260_set_aspm(struct rtsx_pcr *pcr, bool enable)
{
struct rtsx_cr_option *option = &pcr->option;
u8 val = 0;
diff --git a/drivers/misc/cardreader/rtsx_pcr.c b/drivers/misc/cardreader/rtsx_pcr.c
index fd09b0960097..e8f1d4bb806a 100644
--- a/drivers/misc/cardreader/rtsx_pcr.c
+++ b/drivers/misc/cardreader/rtsx_pcr.c
@@ -444,12 +444,12 @@ static void rtsx_pci_add_sg_tbl(struct rtsx_pcr *pcr,
{
u64 *ptr = (u64 *)(pcr->host_sg_tbl_ptr) + pcr->sgi;
u64 val;
- u8 option = SG_VALID | SG_TRANS_DATA;
+ u8 option = RTSX_SG_VALID | RTSX_SG_TRANS_DATA;
pcr_dbg(pcr, "DMA addr: 0x%x, Len: 0x%x\n", (unsigned int)addr, len);
if (end)
- option |= SG_END;
+ option |= RTSX_SG_END;
val = ((u64)addr << 32) | ((u64)len << 12) | option;
put_unaligned_le64(val, ptr);
diff --git a/drivers/misc/cxl/cxl.h b/drivers/misc/cxl/cxl.h
index 4f015da78f28..a4c9c8297a6d 100644
--- a/drivers/misc/cxl/cxl.h
+++ b/drivers/misc/cxl/cxl.h
@@ -369,6 +369,9 @@ static const cxl_p2n_reg_t CXL_PSL_WED_An = {0x0A0};
#define CXL_PSL_TFC_An_AE (1ull << (63-30)) /* Restart PSL with address error */
#define CXL_PSL_TFC_An_R (1ull << (63-31)) /* Restart PSL transaction */
+/****** CXL_PSL_DEBUG *****************************************************/
+#define CXL_PSL_DEBUG_CDC (1ull << (63-27)) /* Coherent Data cache support */
+
/****** CXL_XSL9_IERAT_ERAT - CAIA 2 **********************************/
#define CXL_XSL9_IERAT_MLPID (1ull << (63-0)) /* Match LPID */
#define CXL_XSL9_IERAT_MPID (1ull << (63-1)) /* Match PID */
@@ -669,6 +672,7 @@ struct cxl_native {
irq_hw_number_t err_hwirq;
unsigned int err_virq;
u64 ps_off;
+ bool no_data_cache; /* set if no data cache on the card */
const struct cxl_service_layer_ops *sl_ops;
};
@@ -1065,7 +1069,7 @@ int cxl_psl_purge(struct cxl_afu *afu);
int cxl_calc_capp_routing(struct pci_dev *dev, u64 *chipid,
u32 *phb_index, u64 *capp_unit_id);
int cxl_slot_is_switched(struct pci_dev *dev);
-int cxl_get_xsl9_dsnctl(u64 capp_unit_id, u64 *reg);
+int cxl_get_xsl9_dsnctl(struct pci_dev *dev, u64 capp_unit_id, u64 *reg);
u64 cxl_calculate_sr(bool master, bool kernel, bool real_mode, bool p9);
void cxl_native_irq_dump_regs_psl9(struct cxl_context *ctx);
diff --git a/drivers/misc/cxl/cxllib.c b/drivers/misc/cxl/cxllib.c
index 30ccba436b3b..0bc7c31cf739 100644
--- a/drivers/misc/cxl/cxllib.c
+++ b/drivers/misc/cxl/cxllib.c
@@ -99,7 +99,7 @@ int cxllib_get_xsl_config(struct pci_dev *dev, struct cxllib_xsl_config *cfg)
if (rc)
return rc;
- rc = cxl_get_xsl9_dsnctl(capp_unit_id, &cfg->dsnctl);
+ rc = cxl_get_xsl9_dsnctl(dev, capp_unit_id, &cfg->dsnctl);
if (rc)
return rc;
if (cpu_has_feature(CPU_FTR_POWER9_DD1)) {
@@ -208,49 +208,74 @@ int cxllib_get_PE_attributes(struct task_struct *task,
}
EXPORT_SYMBOL_GPL(cxllib_get_PE_attributes);
-int cxllib_handle_fault(struct mm_struct *mm, u64 addr, u64 size, u64 flags)
+static int get_vma_info(struct mm_struct *mm, u64 addr,
+ u64 *vma_start, u64 *vma_end,
+ unsigned long *page_size)
{
- int rc;
- u64 dar;
struct vm_area_struct *vma = NULL;
- unsigned long page_size;
-
- if (mm == NULL)
- return -EFAULT;
+ int rc = 0;
down_read(&mm->mmap_sem);
vma = find_vma(mm, addr);
if (!vma) {
- pr_err("Can't find vma for addr %016llx\n", addr);
rc = -EFAULT;
goto out;
}
- /* get the size of the pages allocated */
- page_size = vma_kernel_pagesize(vma);
-
- for (dar = (addr & ~(page_size - 1)); dar < (addr + size); dar += page_size) {
- if (dar < vma->vm_start || dar >= vma->vm_end) {
- vma = find_vma(mm, addr);
- if (!vma) {
- pr_err("Can't find vma for addr %016llx\n", addr);
- rc = -EFAULT;
- goto out;
- }
- /* get the size of the pages allocated */
- page_size = vma_kernel_pagesize(vma);
+ *page_size = vma_kernel_pagesize(vma);
+ *vma_start = vma->vm_start;
+ *vma_end = vma->vm_end;
+out:
+ up_read(&mm->mmap_sem);
+ return rc;
+}
+
+int cxllib_handle_fault(struct mm_struct *mm, u64 addr, u64 size, u64 flags)
+{
+ int rc;
+ u64 dar, vma_start, vma_end;
+ unsigned long page_size;
+
+ if (mm == NULL)
+ return -EFAULT;
+
+ /*
+ * The buffer we have to process can extend over several pages
+ * and may also cover several VMAs.
+ * We iterate over all the pages. The page size could vary
+ * between VMAs.
+ */
+ rc = get_vma_info(mm, addr, &vma_start, &vma_end, &page_size);
+ if (rc)
+ return rc;
+
+ for (dar = (addr & ~(page_size - 1)); dar < (addr + size);
+ dar += page_size) {
+ if (dar < vma_start || dar >= vma_end) {
+ /*
+ * We don't hold the mm->mmap_sem semaphore
+ * while iterating, since the semaphore is
+ * required by one of the lower-level page
+ * fault processing functions and it could
+ * create a deadlock.
+ *
+ * It means the VMAs can be altered between 2
+ * loop iterations and we could theoretically
+ * miss a page (however unlikely). But that's
+ * not really a problem, as the driver will
+ * retry access, get another page fault on the
+ * missing page and call us again.
+ */
+ rc = get_vma_info(mm, dar, &vma_start, &vma_end,
+ &page_size);
+ if (rc)
+ return rc;
}
rc = cxl_handle_mm_fault(mm, flags, dar);
- if (rc) {
- pr_err("cxl_handle_mm_fault failed %d", rc);
- rc = -EFAULT;
- goto out;
- }
+ if (rc)
+ return -EFAULT;
}
- rc = 0;
-out:
- up_read(&mm->mmap_sem);
- return rc;
+ return 0;
}
EXPORT_SYMBOL_GPL(cxllib_handle_fault);
diff --git a/drivers/misc/cxl/native.c b/drivers/misc/cxl/native.c
index 1b3d7c65ea3f..98f867fcef24 100644
--- a/drivers/misc/cxl/native.c
+++ b/drivers/misc/cxl/native.c
@@ -353,8 +353,17 @@ int cxl_data_cache_flush(struct cxl *adapter)
u64 reg;
unsigned long timeout = jiffies + (HZ * CXL_TIMEOUT);
- pr_devel("Flushing data cache\n");
+ /*
+ * Do a datacache flush only if datacache is available.
+ * In case of PSL9D datacache absent hence flush operation.
+ * would timeout.
+ */
+ if (adapter->native->no_data_cache) {
+ pr_devel("No PSL data cache. Ignoring cache flush req.\n");
+ return 0;
+ }
+ pr_devel("Flushing data cache\n");
reg = cxl_p1_read(adapter, CXL_PSL_Control);
reg |= CXL_PSL_Control_Fr;
cxl_p1_write(adapter, CXL_PSL_Control, reg);
diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c
index 758842f65a1b..83f1d08058fc 100644
--- a/drivers/misc/cxl/pci.c
+++ b/drivers/misc/cxl/pci.c
@@ -407,21 +407,59 @@ int cxl_calc_capp_routing(struct pci_dev *dev, u64 *chipid,
return 0;
}
-int cxl_get_xsl9_dsnctl(u64 capp_unit_id, u64 *reg)
+static DEFINE_MUTEX(indications_mutex);
+
+static int get_phb_indications(struct pci_dev *dev, u64 *capiind, u64 *asnind,
+ u64 *nbwind)
+{
+ static u64 nbw, asn, capi = 0;
+ struct device_node *np;
+ const __be32 *prop;
+
+ mutex_lock(&indications_mutex);
+ if (!capi) {
+ if (!(np = pnv_pci_get_phb_node(dev))) {
+ mutex_unlock(&indications_mutex);
+ return -ENODEV;
+ }
+
+ prop = of_get_property(np, "ibm,phb-indications", NULL);
+ if (!prop) {
+ nbw = 0x0300UL; /* legacy values */
+ asn = 0x0400UL;
+ capi = 0x0200UL;
+ } else {
+ nbw = (u64)be32_to_cpu(prop[2]);
+ asn = (u64)be32_to_cpu(prop[1]);
+ capi = (u64)be32_to_cpu(prop[0]);
+ }
+ of_node_put(np);
+ }
+ *capiind = capi;
+ *asnind = asn;
+ *nbwind = nbw;
+ mutex_unlock(&indications_mutex);
+ return 0;
+}
+
+int cxl_get_xsl9_dsnctl(struct pci_dev *dev, u64 capp_unit_id, u64 *reg)
{
u64 xsl_dsnctl;
+ u64 capiind, asnind, nbwind;
/*
* CAPI Identifier bits [0:7]
* bit 61:60 MSI bits --> 0
* bit 59 TVT selector --> 0
*/
+ if (get_phb_indications(dev, &capiind, &asnind, &nbwind))
+ return -ENODEV;
/*
* Tell XSL where to route data to.
* The field chipid should match the PHB CAPI_CMPM register
*/
- xsl_dsnctl = ((u64)0x2 << (63-7)); /* Bit 57 */
+ xsl_dsnctl = (capiind << (63-15)); /* Bit 57 */
xsl_dsnctl |= (capp_unit_id << (63-15));
/* nMMU_ID Defaults to: b’000001001’*/
@@ -435,14 +473,14 @@ int cxl_get_xsl9_dsnctl(u64 capp_unit_id, u64 *reg)
* nbwind=0x03, bits [57:58], must include capi indicator.
* Not supported on P9 DD1.
*/
- xsl_dsnctl |= ((u64)0x03 << (63-47));
+ xsl_dsnctl |= (nbwind << (63-55));
/*
* Upper 16b address bits of ASB_Notify messages sent to the
* system. Need to match the PHB’s ASN Compare/Mask Register.
* Not supported on P9 DD1.
*/
- xsl_dsnctl |= ((u64)0x04 << (63-55));
+ xsl_dsnctl |= asnind;
}
*reg = xsl_dsnctl;
@@ -456,13 +494,14 @@ static int init_implementation_adapter_regs_psl9(struct cxl *adapter,
u64 chipid;
u32 phb_index;
u64 capp_unit_id;
+ u64 psl_debug;
int rc;
rc = cxl_calc_capp_routing(dev, &chipid, &phb_index, &capp_unit_id);
if (rc)
return rc;
- rc = cxl_get_xsl9_dsnctl(capp_unit_id, &xsl_dsnctl);
+ rc = cxl_get_xsl9_dsnctl(dev, capp_unit_id, &xsl_dsnctl);
if (rc)
return rc;
@@ -503,8 +542,22 @@ static int init_implementation_adapter_regs_psl9(struct cxl *adapter,
if (cxl_is_power9_dd1()) {
/* Disabling deadlock counter CAR */
cxl_p1_write(adapter, CXL_PSL9_GP_CT, 0x0020000000000001ULL);
- } else
- cxl_p1_write(adapter, CXL_PSL9_DEBUG, 0x4000000000000000ULL);
+ /* Enable NORST */
+ cxl_p1_write(adapter, CXL_PSL9_DEBUG, 0x8000000000000000ULL);
+ } else {
+ /* Enable NORST and DD2 features */
+ cxl_p1_write(adapter, CXL_PSL9_DEBUG, 0xC000000000000000ULL);
+ }
+
+ /*
+ * Check if PSL has data-cache. We need to flush adapter datacache
+ * when as its about to be removed.
+ */
+ psl_debug = cxl_p1_read(adapter, CXL_PSL9_DEBUG);
+ if (psl_debug & CXL_PSL_DEBUG_CDC) {
+ dev_dbg(&dev->dev, "No data-cache present\n");
+ adapter->native->no_data_cache = true;
+ }
return 0;
}
@@ -568,12 +621,6 @@ static int init_implementation_adapter_regs_xsl(struct cxl *adapter, struct pci_
/* For the PSL this is a multiple for 0 < n <= 7: */
#define PSL_2048_250MHZ_CYCLES 1
-static void write_timebase_ctrl_psl9(struct cxl *adapter)
-{
- cxl_p1_write(adapter, CXL_PSL9_TB_CTLSTAT,
- TBSYNC_CNT(2 * PSL_2048_250MHZ_CYCLES));
-}
-
static void write_timebase_ctrl_psl8(struct cxl *adapter)
{
cxl_p1_write(adapter, CXL_PSL_TB_CTLSTAT,
@@ -612,9 +659,6 @@ static u64 timebase_read_xsl(struct cxl *adapter)
static void cxl_setup_psl_timebase(struct cxl *adapter, struct pci_dev *dev)
{
- u64 psl_tb;
- int delta;
- unsigned int retry = 0;
struct device_node *np;
adapter->psl_timebase_synced = false;
@@ -635,26 +679,13 @@ static void cxl_setup_psl_timebase(struct cxl *adapter, struct pci_dev *dev)
* Setup PSL Timebase Control and Status register
* with the recommended Timebase Sync Count value
*/
- adapter->native->sl_ops->write_timebase_ctrl(adapter);
+ if (adapter->native->sl_ops->write_timebase_ctrl)
+ adapter->native->sl_ops->write_timebase_ctrl(adapter);
/* Enable PSL Timebase */
cxl_p1_write(adapter, CXL_PSL_Control, 0x0000000000000000);
cxl_p1_write(adapter, CXL_PSL_Control, CXL_PSL_Control_tb);
- /* Wait until CORE TB and PSL TB difference <= 16usecs */
- do {
- msleep(1);
- if (retry++ > 5) {
- dev_info(&dev->dev, "PSL timebase can't synchronize\n");
- return;
- }
- psl_tb = adapter->native->sl_ops->timebase_read(adapter);
- delta = mftb() - psl_tb;
- if (delta < 0)
- delta = -delta;
- } while (tb_to_ns(delta) > 16000);
-
- adapter->psl_timebase_synced = true;
return;
}
@@ -1449,10 +1480,8 @@ int cxl_pci_reset(struct cxl *adapter)
/*
* The adapter is about to be reset, so ignore errors.
- * Not supported on P9 DD1
*/
- if ((cxl_is_power8()) || (!(cxl_is_power9_dd1())))
- cxl_data_cache_flush(adapter);
+ cxl_data_cache_flush(adapter);
/* pcie_warm_reset requests a fundamental pci reset which includes a
* PERST assert/deassert. PERST triggers a loading of the image
@@ -1801,7 +1830,6 @@ static const struct cxl_service_layer_ops psl9_ops = {
.psl_irq_dump_registers = cxl_native_irq_dump_regs_psl9,
.err_irq_dump_registers = cxl_native_err_irq_dump_regs_psl9,
.debugfs_stop_trace = cxl_stop_trace_psl9,
- .write_timebase_ctrl = write_timebase_ctrl_psl9,
.timebase_read = timebase_read_psl9,
.capi_mode = OPAL_PHB_CAPI_MODE_CAPI,
.needs_reset_before_disable = true,
@@ -1936,10 +1964,8 @@ static void cxl_pci_remove_adapter(struct cxl *adapter)
/*
* Flush adapter datacache as its about to be removed.
- * Not supported on P9 DD1.
*/
- if ((cxl_is_power8()) || (!(cxl_is_power9_dd1())))
- cxl_data_cache_flush(adapter);
+ cxl_data_cache_flush(adapter);
cxl_deconfigure_adapter(adapter);
diff --git a/drivers/misc/cxl/sysfs.c b/drivers/misc/cxl/sysfs.c
index a8b6d6a635e9..95285b7f636f 100644
--- a/drivers/misc/cxl/sysfs.c
+++ b/drivers/misc/cxl/sysfs.c
@@ -62,7 +62,19 @@ static ssize_t psl_timebase_synced_show(struct device *device,
char *buf)
{
struct cxl *adapter = to_cxl_adapter(device);
+ u64 psl_tb, delta;
+ /* Recompute the status only in native mode */
+ if (cpu_has_feature(CPU_FTR_HVMODE)) {
+ psl_tb = adapter->native->sl_ops->timebase_read(adapter);
+ delta = abs(mftb() - psl_tb);
+
+ /* CORE TB and PSL TB difference <= 16usecs ? */
+ adapter->psl_timebase_synced = (tb_to_ns(delta) < 16000) ? true : false;
+ pr_devel("PSL timebase %s - delta: 0x%016llx\n",
+ (tb_to_ns(delta) < 16000) ? "synchronized" :
+ "not synchronized", tb_to_ns(delta));
+ }
return scnprintf(buf, PAGE_SIZE, "%i\n", adapter->psl_timebase_synced);
}
diff --git a/drivers/misc/echo/echo.c b/drivers/misc/echo/echo.c
index 9597e9523cac..8a5adc0d2e88 100644
--- a/drivers/misc/echo/echo.c
+++ b/drivers/misc/echo/echo.c
@@ -115,78 +115,6 @@
/* adapting coeffs using the traditional stochastic descent (N)LMS algorithm */
-#ifdef __bfin__
-static inline void lms_adapt_bg(struct oslec_state *ec, int clean, int shift)
-{
- int i;
- int offset1;
- int offset2;
- int factor;
- int exp;
- int16_t *phist;
- int n;
-
- if (shift > 0)
- factor = clean << shift;
- else
- factor = clean >> -shift;
-
- /* Update the FIR taps */
-
- offset2 = ec->curr_pos;
- offset1 = ec->taps - offset2;
- phist = &ec->fir_state_bg.history[offset2];
-
- /* st: and en: help us locate the assembler in echo.s */
-
- /* asm("st:"); */
- n = ec->taps;
- for (i = 0; i < n; i++) {
- exp = *phist++ * factor;
- ec->fir_taps16[1][i] += (int16_t) ((exp + (1 << 14)) >> 15);
- }
- /* asm("en:"); */
-
- /* Note the asm for the inner loop above generated by Blackfin gcc
- 4.1.1 is pretty good (note even parallel instructions used):
-
- R0 = W [P0++] (X);
- R0 *= R2;
- R0 = R0 + R3 (NS) ||
- R1 = W [P1] (X) ||
- nop;
- R0 >>>= 15;
- R0 = R0 + R1;
- W [P1++] = R0;
-
- A block based update algorithm would be much faster but the
- above can't be improved on much. Every instruction saved in
- the loop above is 2 MIPs/ch! The for loop above is where the
- Blackfin spends most of it's time - about 17 MIPs/ch measured
- with speedtest.c with 256 taps (32ms). Write-back and
- Write-through cache gave about the same performance.
- */
-}
-
-/*
- IDEAS for further optimisation of lms_adapt_bg():
-
- 1/ The rounding is quite costly. Could we keep as 32 bit coeffs
- then make filter pluck the MS 16-bits of the coeffs when filtering?
- However this would lower potential optimisation of filter, as I
- think the dual-MAC architecture requires packed 16 bit coeffs.
-
- 2/ Block based update would be more efficient, as per comments above,
- could use dual MAC architecture.
-
- 3/ Look for same sample Blackfin LMS code, see if we can get dual-MAC
- packing.
-
- 4/ Execute the whole e/c in a block of say 20ms rather than sample
- by sample. Processing a few samples every ms is inefficient.
-*/
-
-#else
static inline void lms_adapt_bg(struct oslec_state *ec, int clean, int shift)
{
int i;
@@ -215,7 +143,6 @@ static inline void lms_adapt_bg(struct oslec_state *ec, int clean, int shift)
ec->fir_taps16[1][i] += (int16_t) ((exp + (1 << 14)) >> 15);
}
}
-#endif
static inline int top_bit(unsigned int bits)
{
diff --git a/drivers/misc/echo/fir.h b/drivers/misc/echo/fir.h
index 7b9fabf1fea5..4e0f365f0577 100644
--- a/drivers/misc/echo/fir.h
+++ b/drivers/misc/echo/fir.h
@@ -27,14 +27,6 @@
#define _FIR_H_
/*
- Blackfin NOTES & IDEAS:
-
- A simple dot product function is used to implement the filter. This performs
- just one MAC/cycle which is inefficient but was easy to implement as a first
- pass. The current Blackfin code also uses an unrolled form of the filter
- history to avoid 0 length hardware loop issues. This is wasteful of
- memory.
-
Ideas for improvement:
1/ Rewrite filter for dual MAC inner loop. The issue here is handling
@@ -94,21 +86,13 @@ static inline const int16_t *fir16_create(struct fir16_state_t *fir,
fir->taps = taps;
fir->curr_pos = taps - 1;
fir->coeffs = coeffs;
-#if defined(__bfin__)
- fir->history = kcalloc(2 * taps, sizeof(int16_t), GFP_KERNEL);
-#else
fir->history = kcalloc(taps, sizeof(int16_t), GFP_KERNEL);
-#endif
return fir->history;
}
static inline void fir16_flush(struct fir16_state_t *fir)
{
-#if defined(__bfin__)
- memset(fir->history, 0, 2 * fir->taps * sizeof(int16_t));
-#else
memset(fir->history, 0, fir->taps * sizeof(int16_t));
-#endif
}
static inline void fir16_free(struct fir16_state_t *fir)
@@ -116,42 +100,9 @@ static inline void fir16_free(struct fir16_state_t *fir)
kfree(fir->history);
}
-#ifdef __bfin__
-static inline int32_t dot_asm(short *x, short *y, int len)
-{
- int dot;
-
- len--;
-
- __asm__("I0 = %1;\n\t"
- "I1 = %2;\n\t"
- "A0 = 0;\n\t"
- "R0.L = W[I0++] || R1.L = W[I1++];\n\t"
- "LOOP dot%= LC0 = %3;\n\t"
- "LOOP_BEGIN dot%=;\n\t"
- "A0 += R0.L * R1.L (IS) || R0.L = W[I0++] || R1.L = W[I1++];\n\t"
- "LOOP_END dot%=;\n\t"
- "A0 += R0.L*R1.L (IS);\n\t"
- "R0 = A0;\n\t"
- "%0 = R0;\n\t"
- : "=&d"(dot)
- : "a"(x), "a"(y), "a"(len)
- : "I0", "I1", "A1", "A0", "R0", "R1"
- );
-
- return dot;
-}
-#endif
-
static inline int16_t fir16(struct fir16_state_t *fir, int16_t sample)
{
int32_t y;
-#if defined(__bfin__)
- fir->history[fir->curr_pos] = sample;
- fir->history[fir->curr_pos + fir->taps] = sample;
- y = dot_asm((int16_t *) fir->coeffs, &fir->history[fir->curr_pos],
- fir->taps);
-#else
int i;
int offset1;
int offset2;
@@ -165,7 +116,6 @@ static inline int16_t fir16(struct fir16_state_t *fir, int16_t sample)
y += fir->coeffs[i] * fir->history[i - offset1];
for (; i >= 0; i--)
y += fir->coeffs[i] * fir->history[i + offset2];
-#endif
if (fir->curr_pos <= 0)
fir->curr_pos = fir->taps;
fir->curr_pos--;
diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c
index 01f9c4921c50..0c125f207aea 100644
--- a/drivers/misc/eeprom/at24.c
+++ b/drivers/misc/eeprom/at24.c
@@ -1,14 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* at24.c - handle most I2C EEPROMs
*
* Copyright (C) 2005-2007 David Brownell
* Copyright (C) 2008 Wolfram Sang, Pengutronix
- *
- * 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/init.h>
#include <linux/module.h>
@@ -63,8 +60,6 @@ struct at24_client {
};
struct at24_data {
- struct at24_platform_data chip;
-
/*
* Lock protects against activities from other Linux tasks,
* but not from changes by other I2C masters.
@@ -75,7 +70,10 @@ struct at24_data {
unsigned int num_addresses;
unsigned int offset_adj;
- struct nvmem_config nvmem_config;
+ u32 byte_len;
+ u16 page_size;
+ u8 flags;
+
struct nvmem_device *nvmem;
struct gpio_desc *wp_gpio;
@@ -239,8 +237,6 @@ static const struct acpi_device_id at24_acpi_ids[] = {
};
MODULE_DEVICE_TABLE(acpi, at24_acpi_ids);
-/*-------------------------------------------------------------------------*/
-
/*
* This routine supports chips which consume multiple I2C addresses. It
* computes the addressing information to be used for a given r/w request.
@@ -255,7 +251,7 @@ static struct at24_client *at24_translate_offset(struct at24_data *at24,
{
unsigned int i;
- if (at24->chip.flags & AT24_FLAG_ADDR16) {
+ if (at24->flags & AT24_FLAG_ADDR16) {
i = *offset >> 16;
*offset &= 0xffff;
} else {
@@ -266,6 +262,11 @@ static struct at24_client *at24_translate_offset(struct at24_data *at24,
return &at24->client[i];
}
+static struct device *at24_base_client_dev(struct at24_data *at24)
+{
+ return &at24->client[0].client->dev;
+}
+
static size_t at24_adjust_read_count(struct at24_data *at24,
unsigned int offset, size_t count)
{
@@ -277,8 +278,8 @@ static size_t at24_adjust_read_count(struct at24_data *at24,
* the next slave address: truncate the count to the slave boundary,
* so that the read never straddles slaves.
*/
- if (at24->chip.flags & AT24_FLAG_NO_RDROL) {
- bits = (at24->chip.flags & AT24_FLAG_ADDR16) ? 16 : 8;
+ if (at24->flags & AT24_FLAG_NO_RDROL) {
+ bits = (at24->flags & AT24_FLAG_ADDR16) ? 16 : 8;
remainder = BIT(bits) - offset;
if (count > remainder)
count = remainder;
@@ -337,7 +338,7 @@ static size_t at24_adjust_write_count(struct at24_data *at24,
count = at24->write_max;
/* Never roll over backwards, to the start of this page */
- next_page = roundup(offset + 1, at24->chip.page_size);
+ next_page = roundup(offset + 1, at24->page_size);
if (offset + count > next_page)
count = next_page - offset;
@@ -371,15 +372,18 @@ static ssize_t at24_regmap_write(struct at24_data *at24, const char *buf,
static int at24_read(void *priv, unsigned int off, void *val, size_t count)
{
- struct at24_data *at24 = priv;
- struct device *dev = &at24->client[0].client->dev;
+ struct at24_data *at24;
+ struct device *dev;
char *buf = val;
int ret;
+ at24 = priv;
+ dev = at24_base_client_dev(at24);
+
if (unlikely(!count))
return count;
- if (off + count > at24->chip.byte_len)
+ if (off + count > at24->byte_len)
return -EINVAL;
ret = pm_runtime_get_sync(dev);
@@ -395,17 +399,15 @@ static int at24_read(void *priv, unsigned int off, void *val, size_t count)
mutex_lock(&at24->lock);
while (count) {
- int status;
-
- status = at24_regmap_read(at24, buf, off, count);
- if (status < 0) {
+ ret = at24_regmap_read(at24, buf, off, count);
+ if (ret < 0) {
mutex_unlock(&at24->lock);
pm_runtime_put(dev);
- return status;
+ return ret;
}
- buf += status;
- off += status;
- count -= status;
+ buf += ret;
+ off += ret;
+ count -= ret;
}
mutex_unlock(&at24->lock);
@@ -417,15 +419,18 @@ static int at24_read(void *priv, unsigned int off, void *val, size_t count)
static int at24_write(void *priv, unsigned int off, void *val, size_t count)
{
- struct at24_data *at24 = priv;
- struct device *dev = &at24->client[0].client->dev;
+ struct at24_data *at24;
+ struct device *dev;
char *buf = val;
int ret;
+ at24 = priv;
+ dev = at24_base_client_dev(at24);
+
if (unlikely(!count))
return -EINVAL;
- if (off + count > at24->chip.byte_len)
+ if (off + count > at24->byte_len)
return -EINVAL;
ret = pm_runtime_get_sync(dev);
@@ -442,18 +447,16 @@ static int at24_write(void *priv, unsigned int off, void *val, size_t count)
gpiod_set_value_cansleep(at24->wp_gpio, 0);
while (count) {
- int status;
-
- status = at24_regmap_write(at24, buf, off, count);
- if (status < 0) {
+ ret = at24_regmap_write(at24, buf, off, count);
+ if (ret < 0) {
gpiod_set_value_cansleep(at24->wp_gpio, 1);
mutex_unlock(&at24->lock);
pm_runtime_put(dev);
- return status;
+ return ret;
}
- buf += status;
- off += status;
- count -= status;
+ buf += ret;
+ off += ret;
+ count -= ret;
}
gpiod_set_value_cansleep(at24->wp_gpio, 1);
@@ -464,7 +467,8 @@ static int at24_write(void *priv, unsigned int off, void *val, size_t count)
return 0;
}
-static void at24_get_pdata(struct device *dev, struct at24_platform_data *chip)
+static void at24_properties_to_pdata(struct device *dev,
+ struct at24_platform_data *chip)
{
int err;
u32 val;
@@ -491,6 +495,43 @@ static void at24_get_pdata(struct device *dev, struct at24_platform_data *chip)
}
}
+static int at24_get_pdata(struct device *dev, struct at24_platform_data *pdata)
+{
+ struct device_node *of_node = dev->of_node;
+ const struct at24_chip_data *cdata;
+ const struct i2c_device_id *id;
+ struct at24_platform_data *pd;
+
+ pd = dev_get_platdata(dev);
+ if (pd) {
+ memcpy(pdata, pd, sizeof(*pdata));
+ return 0;
+ }
+
+ id = i2c_match_id(at24_ids, to_i2c_client(dev));
+
+ /*
+ * The I2C core allows OF nodes compatibles to match against the
+ * I2C device ID table as a fallback, so check not only if an OF
+ * node is present but also if it matches an OF device ID entry.
+ */
+ if (of_node && of_match_device(at24_of_match, dev))
+ cdata = of_device_get_match_data(dev);
+ else if (id)
+ cdata = (void *)&id->driver_data;
+ else
+ cdata = acpi_device_get_match_data(dev);
+
+ if (!cdata)
+ return -ENODEV;
+
+ pdata->byte_len = cdata->byte_len;
+ pdata->flags = cdata->flags;
+ at24_properties_to_pdata(dev, pdata);
+
+ return 0;
+}
+
static unsigned int at24_get_offset_adj(u8 flags, unsigned int byte_len)
{
if (flags & AT24_FLAG_MAC) {
@@ -514,102 +555,83 @@ static unsigned int at24_get_offset_adj(u8 flags, unsigned int byte_len)
}
}
-static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
+static int at24_probe(struct i2c_client *client)
{
- struct at24_platform_data chip = { 0 };
- const struct at24_chip_data *cd = NULL;
- bool writable;
- struct at24_data *at24;
- int err;
- unsigned int i, num_addresses;
struct regmap_config regmap_config = { };
+ struct nvmem_config nvmem_config = { };
+ struct at24_platform_data pdata = { };
+ struct device *dev = &client->dev;
+ bool i2c_fn_i2c, i2c_fn_block;
+ unsigned int i, num_addresses;
+ struct at24_data *at24;
+ struct regmap *regmap;
+ size_t at24_size;
+ bool writable;
u8 test_byte;
+ int err;
- if (client->dev.platform_data) {
- chip = *(struct at24_platform_data *)client->dev.platform_data;
- } else {
- /*
- * The I2C core allows OF nodes compatibles to match against the
- * I2C device ID table as a fallback, so check not only if an OF
- * node is present but also if it matches an OF device ID entry.
- */
- if (client->dev.of_node &&
- of_match_device(at24_of_match, &client->dev)) {
- cd = of_device_get_match_data(&client->dev);
- } else if (id) {
- cd = (void *)id->driver_data;
- } else {
- const struct acpi_device_id *aid;
-
- aid = acpi_match_device(at24_acpi_ids, &client->dev);
- if (aid)
- cd = (void *)aid->driver_data;
- }
- if (!cd)
- return -ENODEV;
+ i2c_fn_i2c = i2c_check_functionality(client->adapter, I2C_FUNC_I2C);
+ i2c_fn_block = i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WRITE_I2C_BLOCK);
- chip.byte_len = cd->byte_len;
- chip.flags = cd->flags;
- at24_get_pdata(&client->dev, &chip);
- }
+ err = at24_get_pdata(dev, &pdata);
+ if (err)
+ return err;
+
+ if (!i2c_fn_i2c && !i2c_fn_block)
+ pdata.page_size = 1;
- if (!is_power_of_2(chip.byte_len))
- dev_warn(&client->dev,
- "byte_len looks suspicious (no power of 2)!\n");
- if (!chip.page_size) {
- dev_err(&client->dev, "page_size must not be 0!\n");
+ if (!pdata.page_size) {
+ dev_err(dev, "page_size must not be 0!\n");
return -EINVAL;
}
- if (!is_power_of_2(chip.page_size))
- dev_warn(&client->dev,
- "page_size looks suspicious (no power of 2)!\n");
- if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C) &&
- !i2c_check_functionality(client->adapter,
- I2C_FUNC_SMBUS_WRITE_I2C_BLOCK))
- chip.page_size = 1;
+ if (!is_power_of_2(pdata.page_size))
+ dev_warn(dev, "page_size looks suspicious (no power of 2)!\n");
- if (chip.flags & AT24_FLAG_TAKE8ADDR)
+ if (pdata.flags & AT24_FLAG_TAKE8ADDR)
num_addresses = 8;
else
- num_addresses = DIV_ROUND_UP(chip.byte_len,
- (chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256);
+ num_addresses = DIV_ROUND_UP(pdata.byte_len,
+ (pdata.flags & AT24_FLAG_ADDR16) ? 65536 : 256);
+
+ if ((pdata.flags & AT24_FLAG_SERIAL) && (pdata.flags & AT24_FLAG_MAC)) {
+ dev_err(dev,
+ "invalid device data - cannot have both AT24_FLAG_SERIAL & AT24_FLAG_MAC.");
+ return -EINVAL;
+ }
regmap_config.val_bits = 8;
- regmap_config.reg_bits = (chip.flags & AT24_FLAG_ADDR16) ? 16 : 8;
+ regmap_config.reg_bits = (pdata.flags & AT24_FLAG_ADDR16) ? 16 : 8;
+ regmap_config.disable_locking = true;
- at24 = devm_kzalloc(&client->dev, sizeof(struct at24_data) +
- num_addresses * sizeof(struct at24_client), GFP_KERNEL);
+ regmap = devm_regmap_init_i2c(client, &regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ at24_size = sizeof(*at24) + num_addresses * sizeof(struct at24_client);
+ at24 = devm_kzalloc(dev, at24_size, GFP_KERNEL);
if (!at24)
return -ENOMEM;
mutex_init(&at24->lock);
- at24->chip = chip;
+ at24->byte_len = pdata.byte_len;
+ at24->page_size = pdata.page_size;
+ at24->flags = pdata.flags;
at24->num_addresses = num_addresses;
- at24->offset_adj = at24_get_offset_adj(chip.flags, chip.byte_len);
+ at24->offset_adj = at24_get_offset_adj(pdata.flags, pdata.byte_len);
+ at24->client[0].client = client;
+ at24->client[0].regmap = regmap;
- at24->wp_gpio = devm_gpiod_get_optional(&client->dev,
- "wp", GPIOD_OUT_HIGH);
+ at24->wp_gpio = devm_gpiod_get_optional(dev, "wp", GPIOD_OUT_HIGH);
if (IS_ERR(at24->wp_gpio))
return PTR_ERR(at24->wp_gpio);
- at24->client[0].client = client;
- at24->client[0].regmap = devm_regmap_init_i2c(client, &regmap_config);
- if (IS_ERR(at24->client[0].regmap))
- return PTR_ERR(at24->client[0].regmap);
-
- if ((chip.flags & AT24_FLAG_SERIAL) && (chip.flags & AT24_FLAG_MAC)) {
- dev_err(&client->dev,
- "invalid device data - cannot have both AT24_FLAG_SERIAL & AT24_FLAG_MAC.");
- return -EINVAL;
- }
-
- writable = !(chip.flags & AT24_FLAG_READONLY);
+ writable = !(pdata.flags & AT24_FLAG_READONLY);
if (writable) {
at24->write_max = min_t(unsigned int,
- chip.page_size, at24_io_limit);
- if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C) &&
- at24->write_max > I2C_SMBUS_BLOCK_MAX)
+ pdata.page_size, at24_io_limit);
+ if (!i2c_fn_i2c && at24->write_max > I2C_SMBUS_BLOCK_MAX)
at24->write_max = I2C_SMBUS_BLOCK_MAX;
}
@@ -618,8 +640,8 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
at24->client[i].client = i2c_new_dummy(client->adapter,
client->addr + i);
if (!at24->client[i].client) {
- dev_err(&client->dev, "address 0x%02x unavailable\n",
- client->addr + i);
+ dev_err(dev, "address 0x%02x unavailable\n",
+ client->addr + i);
err = -EADDRINUSE;
goto err_clients;
}
@@ -635,48 +657,47 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
i2c_set_clientdata(client, at24);
/* enable runtime pm */
- pm_runtime_set_active(&client->dev);
- pm_runtime_enable(&client->dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
/*
* Perform a one-byte test read to verify that the
* chip is functional.
*/
err = at24_read(at24, 0, &test_byte, 1);
- pm_runtime_idle(&client->dev);
+ pm_runtime_idle(dev);
if (err) {
err = -ENODEV;
goto err_clients;
}
- at24->nvmem_config.name = dev_name(&client->dev);
- at24->nvmem_config.dev = &client->dev;
- at24->nvmem_config.read_only = !writable;
- at24->nvmem_config.root_only = true;
- at24->nvmem_config.owner = THIS_MODULE;
- at24->nvmem_config.compat = true;
- at24->nvmem_config.base_dev = &client->dev;
- at24->nvmem_config.reg_read = at24_read;
- at24->nvmem_config.reg_write = at24_write;
- at24->nvmem_config.priv = at24;
- at24->nvmem_config.stride = 1;
- at24->nvmem_config.word_size = 1;
- at24->nvmem_config.size = chip.byte_len;
-
- at24->nvmem = nvmem_register(&at24->nvmem_config);
-
+ nvmem_config.name = dev_name(dev);
+ nvmem_config.dev = dev;
+ nvmem_config.read_only = !writable;
+ nvmem_config.root_only = true;
+ nvmem_config.owner = THIS_MODULE;
+ nvmem_config.compat = true;
+ nvmem_config.base_dev = dev;
+ nvmem_config.reg_read = at24_read;
+ nvmem_config.reg_write = at24_write;
+ nvmem_config.priv = at24;
+ nvmem_config.stride = 1;
+ nvmem_config.word_size = 1;
+ nvmem_config.size = pdata.byte_len;
+
+ at24->nvmem = nvmem_register(&nvmem_config);
if (IS_ERR(at24->nvmem)) {
err = PTR_ERR(at24->nvmem);
goto err_clients;
}
- dev_info(&client->dev, "%u byte %s EEPROM, %s, %u bytes/write\n",
- chip.byte_len, client->name,
- writable ? "writable" : "read-only", at24->write_max);
+ dev_info(dev, "%u byte %s EEPROM, %s, %u bytes/write\n",
+ pdata.byte_len, client->name,
+ writable ? "writable" : "read-only", at24->write_max);
/* export data to kernel code */
- if (chip.setup)
- chip.setup(at24->nvmem, chip.context);
+ if (pdata.setup)
+ pdata.setup(at24->nvmem, pdata.context);
return 0;
@@ -685,7 +706,7 @@ err_clients:
if (at24->client[i].client)
i2c_unregister_device(at24->client[i].client);
- pm_runtime_disable(&client->dev);
+ pm_runtime_disable(dev);
return err;
}
@@ -708,15 +729,13 @@ static int at24_remove(struct i2c_client *client)
return 0;
}
-/*-------------------------------------------------------------------------*/
-
static struct i2c_driver at24_driver = {
.driver = {
.name = "at24",
.of_match_table = at24_of_match,
.acpi_match_table = ACPI_PTR(at24_acpi_ids),
},
- .probe = at24_probe,
+ .probe_new = at24_probe,
.remove = at24_remove,
.id_table = at24_ids,
};
diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c
index 9282ffd607ff..6a7d4a2ad514 100644
--- a/drivers/misc/eeprom/at25.c
+++ b/drivers/misc/eeprom/at25.c
@@ -102,7 +102,7 @@ static int at25_ee_read(void *priv, unsigned int offset,
}
spi_message_init(&m);
- memset(t, 0, sizeof t);
+ memset(t, 0, sizeof(t));
t[0].tx_buf = command;
t[0].len = at25->addrlen + 1;
diff --git a/drivers/misc/eeprom/digsy_mtc_eeprom.c b/drivers/misc/eeprom/digsy_mtc_eeprom.c
index 66d9e1baeae5..fbde2516c04f 100644
--- a/drivers/misc/eeprom/digsy_mtc_eeprom.c
+++ b/drivers/misc/eeprom/digsy_mtc_eeprom.c
@@ -7,9 +7,18 @@
* 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.
+ *
+ * FIXME: this driver is used on a device-tree probed platform: it
+ * should be defined as a bit-banged SPI device and probed from the device
+ * tree and not like this with static grabbing of a few numbered GPIO
+ * lines at random.
+ *
+ * Add proper SPI and EEPROM in arch/powerpc/boot/dts/digsy_mtc.dts
+ * and delete this driver.
*/
#include <linux/gpio.h>
+#include <linux/gpio/machine.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
@@ -42,9 +51,6 @@ struct eeprom_93xx46_platform_data digsy_mtc_eeprom_data = {
};
static struct spi_gpio_platform_data eeprom_spi_gpio_data = {
- .sck = GPIO_EEPROM_CLK,
- .mosi = GPIO_EEPROM_DI,
- .miso = GPIO_EEPROM_DO,
.num_chipselect = 1,
};
@@ -56,6 +62,21 @@ static struct platform_device digsy_mtc_eeprom = {
},
};
+static struct gpiod_lookup_table eeprom_spi_gpiod_table = {
+ .dev_id = "spi_gpio",
+ .table = {
+ GPIO_LOOKUP("gpio@b00", GPIO_EEPROM_CLK,
+ "sck", GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP("gpio@b00", GPIO_EEPROM_DI,
+ "mosi", GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP("gpio@b00", GPIO_EEPROM_DO,
+ "miso", GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP("gpio@b00", GPIO_EEPROM_CS,
+ "cs", GPIO_ACTIVE_HIGH),
+ { },
+ },
+};
+
static struct spi_board_info digsy_mtc_eeprom_info[] __initdata = {
{
.modalias = "93xx46",
@@ -63,7 +84,6 @@ static struct spi_board_info digsy_mtc_eeprom_info[] __initdata = {
.bus_num = EE_SPI_BUS_NUM,
.chip_select = 0,
.mode = SPI_MODE_0,
- .controller_data = (void *)GPIO_EEPROM_CS,
.platform_data = &digsy_mtc_eeprom_data,
},
};
@@ -78,6 +98,7 @@ static int __init digsy_mtc_eeprom_devices_init(void)
pr_err("can't request gpio %d\n", GPIO_EEPROM_OE);
return ret;
}
+ gpiod_add_lookup_table(&eeprom_spi_gpiod_table);
spi_register_board_info(digsy_mtc_eeprom_info,
ARRAY_SIZE(digsy_mtc_eeprom_info));
return platform_device_register(&digsy_mtc_eeprom);
diff --git a/drivers/misc/genwqe/card_utils.c b/drivers/misc/genwqe/card_utils.c
index 8f2e6442d88b..8679e0bd8ec2 100644
--- a/drivers/misc/genwqe/card_utils.c
+++ b/drivers/misc/genwqe/card_utils.c
@@ -453,7 +453,7 @@ int genwqe_setup_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl,
s += 8; /* continue 8 elements further */
}
fixup:
- if (j == 1) { /* combining happend on last entry! */
+ if (j == 1) { /* combining happened on last entry! */
s -= 8; /* full shift needed on previous sgl block */
j = 7; /* shift all elements */
}
@@ -471,7 +471,7 @@ int genwqe_setup_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl,
* genwqe_free_sync_sgl() - Free memory for sgl and overlapping pages
*
* After the DMA transfer has been completed we free the memory for
- * the sgl and the cached pages. Data is being transfered from cached
+ * the sgl and the cached pages. Data is being transferred from cached
* pages into user-space buffers.
*/
int genwqe_free_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl)
diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c
index 24108bfad889..6193270e7b3d 100644
--- a/drivers/misc/kgdbts.c
+++ b/drivers/misc/kgdbts.c
@@ -400,10 +400,14 @@ static void skip_back_repeat_test(char *arg)
int go_back = simple_strtol(arg, NULL, 10);
repeat_test--;
- if (repeat_test <= 0)
+ if (repeat_test <= 0) {
ts.idx++;
- else
+ } else {
+ if (repeat_test % 100 == 0)
+ v1printk("kgdbts:RUN ... %d remaining\n", repeat_test);
+
ts.idx -= go_back;
+ }
fill_get_buf(ts.tst[ts.idx].get);
}
diff --git a/drivers/misc/lkdtm/Makefile b/drivers/misc/lkdtm/Makefile
new file mode 100644
index 000000000000..3370a4138e94
--- /dev/null
+++ b/drivers/misc/lkdtm/Makefile
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_LKDTM) += lkdtm.o
+
+lkdtm-$(CONFIG_LKDTM) += core.o
+lkdtm-$(CONFIG_LKDTM) += bugs.o
+lkdtm-$(CONFIG_LKDTM) += heap.o
+lkdtm-$(CONFIG_LKDTM) += perms.o
+lkdtm-$(CONFIG_LKDTM) += refcount.o
+lkdtm-$(CONFIG_LKDTM) += rodata_objcopy.o
+lkdtm-$(CONFIG_LKDTM) += usercopy.o
+
+KCOV_INSTRUMENT_rodata.o := n
+
+OBJCOPYFLAGS :=
+OBJCOPYFLAGS_rodata_objcopy.o := \
+ --set-section-flags .text=alloc,readonly \
+ --rename-section .text=.rodata
+targets += rodata.o rodata_objcopy.o
+$(obj)/rodata_objcopy.o: $(obj)/rodata.o FORCE
+ $(call if_changed,objcopy)
diff --git a/drivers/misc/lkdtm_bugs.c b/drivers/misc/lkdtm/bugs.c
index 7eebbdfbcacd..7eebbdfbcacd 100644
--- a/drivers/misc/lkdtm_bugs.c
+++ b/drivers/misc/lkdtm/bugs.c
diff --git a/drivers/misc/lkdtm_core.c b/drivers/misc/lkdtm/core.c
index 2154d1bfd18b..2154d1bfd18b 100644
--- a/drivers/misc/lkdtm_core.c
+++ b/drivers/misc/lkdtm/core.c
diff --git a/drivers/misc/lkdtm_heap.c b/drivers/misc/lkdtm/heap.c
index 65026d7de130..65026d7de130 100644
--- a/drivers/misc/lkdtm_heap.c
+++ b/drivers/misc/lkdtm/heap.c
diff --git a/drivers/misc/lkdtm.h b/drivers/misc/lkdtm/lkdtm.h
index 9e513dcfd809..9e513dcfd809 100644
--- a/drivers/misc/lkdtm.h
+++ b/drivers/misc/lkdtm/lkdtm.h
diff --git a/drivers/misc/lkdtm_perms.c b/drivers/misc/lkdtm/perms.c
index 53b85c9d16b8..53b85c9d16b8 100644
--- a/drivers/misc/lkdtm_perms.c
+++ b/drivers/misc/lkdtm/perms.c
diff --git a/drivers/misc/lkdtm/refcount.c b/drivers/misc/lkdtm/refcount.c
new file mode 100644
index 000000000000..0a146b32da13
--- /dev/null
+++ b/drivers/misc/lkdtm/refcount.c
@@ -0,0 +1,401 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This is for all the tests related to refcount bugs (e.g. overflow,
+ * underflow, reaching zero untested, etc).
+ */
+#include "lkdtm.h"
+#include <linux/refcount.h>
+
+#ifdef CONFIG_REFCOUNT_FULL
+#define REFCOUNT_MAX (UINT_MAX - 1)
+#define REFCOUNT_SATURATED UINT_MAX
+#else
+#define REFCOUNT_MAX INT_MAX
+#define REFCOUNT_SATURATED (INT_MIN / 2)
+#endif
+
+static void overflow_check(refcount_t *ref)
+{
+ switch (refcount_read(ref)) {
+ case REFCOUNT_SATURATED:
+ pr_info("Overflow detected: saturated\n");
+ break;
+ case REFCOUNT_MAX:
+ pr_warn("Overflow detected: unsafely reset to max\n");
+ break;
+ default:
+ pr_err("Fail: refcount wrapped to %d\n", refcount_read(ref));
+ }
+}
+
+/*
+ * A refcount_inc() above the maximum value of the refcount implementation,
+ * should at least saturate, and at most also WARN.
+ */
+void lkdtm_REFCOUNT_INC_OVERFLOW(void)
+{
+ refcount_t over = REFCOUNT_INIT(REFCOUNT_MAX - 1);
+
+ pr_info("attempting good refcount_inc() without overflow\n");
+ refcount_dec(&over);
+ refcount_inc(&over);
+
+ pr_info("attempting bad refcount_inc() overflow\n");
+ refcount_inc(&over);
+ refcount_inc(&over);
+
+ overflow_check(&over);
+}
+
+/* refcount_add() should behave just like refcount_inc() above. */
+void lkdtm_REFCOUNT_ADD_OVERFLOW(void)
+{
+ refcount_t over = REFCOUNT_INIT(REFCOUNT_MAX - 1);
+
+ pr_info("attempting good refcount_add() without overflow\n");
+ refcount_dec(&over);
+ refcount_dec(&over);
+ refcount_dec(&over);
+ refcount_dec(&over);
+ refcount_add(4, &over);
+
+ pr_info("attempting bad refcount_add() overflow\n");
+ refcount_add(4, &over);
+
+ overflow_check(&over);
+}
+
+/* refcount_inc_not_zero() should behave just like refcount_inc() above. */
+void lkdtm_REFCOUNT_INC_NOT_ZERO_OVERFLOW(void)
+{
+ refcount_t over = REFCOUNT_INIT(REFCOUNT_MAX);
+
+ pr_info("attempting bad refcount_inc_not_zero() overflow\n");
+ if (!refcount_inc_not_zero(&over))
+ pr_warn("Weird: refcount_inc_not_zero() reported zero\n");
+
+ overflow_check(&over);
+}
+
+/* refcount_add_not_zero() should behave just like refcount_inc() above. */
+void lkdtm_REFCOUNT_ADD_NOT_ZERO_OVERFLOW(void)
+{
+ refcount_t over = REFCOUNT_INIT(REFCOUNT_MAX);
+
+ pr_info("attempting bad refcount_add_not_zero() overflow\n");
+ if (!refcount_add_not_zero(6, &over))
+ pr_warn("Weird: refcount_add_not_zero() reported zero\n");
+
+ overflow_check(&over);
+}
+
+static void check_zero(refcount_t *ref)
+{
+ switch (refcount_read(ref)) {
+ case REFCOUNT_SATURATED:
+ pr_info("Zero detected: saturated\n");
+ break;
+ case REFCOUNT_MAX:
+ pr_warn("Zero detected: unsafely reset to max\n");
+ break;
+ case 0:
+ pr_warn("Still at zero: refcount_inc/add() must not inc-from-0\n");
+ break;
+ default:
+ pr_err("Fail: refcount went crazy: %d\n", refcount_read(ref));
+ }
+}
+
+/*
+ * A refcount_dec(), as opposed to a refcount_dec_and_test(), when it hits
+ * zero it should either saturate (when inc-from-zero isn't protected)
+ * or stay at zero (when inc-from-zero is protected) and should WARN for both.
+ */
+void lkdtm_REFCOUNT_DEC_ZERO(void)
+{
+ refcount_t zero = REFCOUNT_INIT(2);
+
+ pr_info("attempting good refcount_dec()\n");
+ refcount_dec(&zero);
+
+ pr_info("attempting bad refcount_dec() to zero\n");
+ refcount_dec(&zero);
+
+ check_zero(&zero);
+}
+
+static void check_negative(refcount_t *ref, int start)
+{
+ /*
+ * CONFIG_REFCOUNT_FULL refuses to move a refcount at all on an
+ * over-sub, so we have to track our starting position instead of
+ * looking only at zero-pinning.
+ */
+ if (refcount_read(ref) == start) {
+ pr_warn("Still at %d: refcount_inc/add() must not inc-from-0\n",
+ start);
+ return;
+ }
+
+ switch (refcount_read(ref)) {
+ case REFCOUNT_SATURATED:
+ pr_info("Negative detected: saturated\n");
+ break;
+ case REFCOUNT_MAX:
+ pr_warn("Negative detected: unsafely reset to max\n");
+ break;
+ default:
+ pr_err("Fail: refcount went crazy: %d\n", refcount_read(ref));
+ }
+}
+
+/* A refcount_dec() going negative should saturate and may WARN. */
+void lkdtm_REFCOUNT_DEC_NEGATIVE(void)
+{
+ refcount_t neg = REFCOUNT_INIT(0);
+
+ pr_info("attempting bad refcount_dec() below zero\n");
+ refcount_dec(&neg);
+
+ check_negative(&neg, 0);
+}
+
+/*
+ * A refcount_dec_and_test() should act like refcount_dec() above when
+ * going negative.
+ */
+void lkdtm_REFCOUNT_DEC_AND_TEST_NEGATIVE(void)
+{
+ refcount_t neg = REFCOUNT_INIT(0);
+
+ pr_info("attempting bad refcount_dec_and_test() below zero\n");
+ if (refcount_dec_and_test(&neg))
+ pr_warn("Weird: refcount_dec_and_test() reported zero\n");
+
+ check_negative(&neg, 0);
+}
+
+/*
+ * A refcount_sub_and_test() should act like refcount_dec_and_test()
+ * above when going negative.
+ */
+void lkdtm_REFCOUNT_SUB_AND_TEST_NEGATIVE(void)
+{
+ refcount_t neg = REFCOUNT_INIT(3);
+
+ pr_info("attempting bad refcount_sub_and_test() below zero\n");
+ if (refcount_sub_and_test(5, &neg))
+ pr_warn("Weird: refcount_sub_and_test() reported zero\n");
+
+ check_negative(&neg, 3);
+}
+
+static void check_from_zero(refcount_t *ref)
+{
+ switch (refcount_read(ref)) {
+ case 0:
+ pr_info("Zero detected: stayed at zero\n");
+ break;
+ case REFCOUNT_SATURATED:
+ pr_info("Zero detected: saturated\n");
+ break;
+ case REFCOUNT_MAX:
+ pr_warn("Zero detected: unsafely reset to max\n");
+ break;
+ default:
+ pr_info("Fail: zero not detected, incremented to %d\n",
+ refcount_read(ref));
+ }
+}
+
+/*
+ * A refcount_inc() from zero should pin to zero or saturate and may WARN.
+ * Only CONFIG_REFCOUNT_FULL provides this protection currently.
+ */
+void lkdtm_REFCOUNT_INC_ZERO(void)
+{
+ refcount_t zero = REFCOUNT_INIT(0);
+
+ pr_info("attempting safe refcount_inc_not_zero() from zero\n");
+ if (!refcount_inc_not_zero(&zero)) {
+ pr_info("Good: zero detected\n");
+ if (refcount_read(&zero) == 0)
+ pr_info("Correctly stayed at zero\n");
+ else
+ pr_err("Fail: refcount went past zero!\n");
+ } else {
+ pr_err("Fail: Zero not detected!?\n");
+ }
+
+ pr_info("attempting bad refcount_inc() from zero\n");
+ refcount_inc(&zero);
+
+ check_from_zero(&zero);
+}
+
+/*
+ * A refcount_add() should act like refcount_inc() above when starting
+ * at zero.
+ */
+void lkdtm_REFCOUNT_ADD_ZERO(void)
+{
+ refcount_t zero = REFCOUNT_INIT(0);
+
+ pr_info("attempting safe refcount_add_not_zero() from zero\n");
+ if (!refcount_add_not_zero(3, &zero)) {
+ pr_info("Good: zero detected\n");
+ if (refcount_read(&zero) == 0)
+ pr_info("Correctly stayed at zero\n");
+ else
+ pr_err("Fail: refcount went past zero\n");
+ } else {
+ pr_err("Fail: Zero not detected!?\n");
+ }
+
+ pr_info("attempting bad refcount_add() from zero\n");
+ refcount_add(3, &zero);
+
+ check_from_zero(&zero);
+}
+
+static void check_saturated(refcount_t *ref)
+{
+ switch (refcount_read(ref)) {
+ case REFCOUNT_SATURATED:
+ pr_info("Saturation detected: still saturated\n");
+ break;
+ case REFCOUNT_MAX:
+ pr_warn("Saturation detected: unsafely reset to max\n");
+ break;
+ default:
+ pr_err("Fail: refcount went crazy: %d\n", refcount_read(ref));
+ }
+}
+
+/*
+ * A refcount_inc() from a saturated value should at most warn about
+ * being saturated already.
+ */
+void lkdtm_REFCOUNT_INC_SATURATED(void)
+{
+ refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED);
+
+ pr_info("attempting bad refcount_inc() from saturated\n");
+ refcount_inc(&sat);
+
+ check_saturated(&sat);
+}
+
+/* Should act like refcount_inc() above from saturated. */
+void lkdtm_REFCOUNT_DEC_SATURATED(void)
+{
+ refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED);
+
+ pr_info("attempting bad refcount_dec() from saturated\n");
+ refcount_dec(&sat);
+
+ check_saturated(&sat);
+}
+
+/* Should act like refcount_inc() above from saturated. */
+void lkdtm_REFCOUNT_ADD_SATURATED(void)
+{
+ refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED);
+
+ pr_info("attempting bad refcount_dec() from saturated\n");
+ refcount_add(8, &sat);
+
+ check_saturated(&sat);
+}
+
+/* Should act like refcount_inc() above from saturated. */
+void lkdtm_REFCOUNT_INC_NOT_ZERO_SATURATED(void)
+{
+ refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED);
+
+ pr_info("attempting bad refcount_inc_not_zero() from saturated\n");
+ if (!refcount_inc_not_zero(&sat))
+ pr_warn("Weird: refcount_inc_not_zero() reported zero\n");
+
+ check_saturated(&sat);
+}
+
+/* Should act like refcount_inc() above from saturated. */
+void lkdtm_REFCOUNT_ADD_NOT_ZERO_SATURATED(void)
+{
+ refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED);
+
+ pr_info("attempting bad refcount_add_not_zero() from saturated\n");
+ if (!refcount_add_not_zero(7, &sat))
+ pr_warn("Weird: refcount_add_not_zero() reported zero\n");
+
+ check_saturated(&sat);
+}
+
+/* Should act like refcount_inc() above from saturated. */
+void lkdtm_REFCOUNT_DEC_AND_TEST_SATURATED(void)
+{
+ refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED);
+
+ pr_info("attempting bad refcount_dec_and_test() from saturated\n");
+ if (refcount_dec_and_test(&sat))
+ pr_warn("Weird: refcount_dec_and_test() reported zero\n");
+
+ check_saturated(&sat);
+}
+
+/* Should act like refcount_inc() above from saturated. */
+void lkdtm_REFCOUNT_SUB_AND_TEST_SATURATED(void)
+{
+ refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED);
+
+ pr_info("attempting bad refcount_sub_and_test() from saturated\n");
+ if (refcount_sub_and_test(8, &sat))
+ pr_warn("Weird: refcount_sub_and_test() reported zero\n");
+
+ check_saturated(&sat);
+}
+
+/* Used to time the existing atomic_t when used for reference counting */
+void lkdtm_ATOMIC_TIMING(void)
+{
+ unsigned int i;
+ atomic_t count = ATOMIC_INIT(1);
+
+ for (i = 0; i < INT_MAX - 1; i++)
+ atomic_inc(&count);
+
+ for (i = INT_MAX; i > 0; i--)
+ if (atomic_dec_and_test(&count))
+ break;
+
+ if (i != 1)
+ pr_err("atomic timing: out of sync up/down cycle: %u\n", i - 1);
+ else
+ pr_info("atomic timing: done\n");
+}
+
+/*
+ * This can be compared to ATOMIC_TIMING when implementing fast refcount
+ * protections. Looking at the number of CPU cycles tells the real story
+ * about performance. For example:
+ * cd /sys/kernel/debug/provoke-crash
+ * perf stat -B -- cat <(echo REFCOUNT_TIMING) > DIRECT
+ */
+void lkdtm_REFCOUNT_TIMING(void)
+{
+ unsigned int i;
+ refcount_t count = REFCOUNT_INIT(1);
+
+ for (i = 0; i < INT_MAX - 1; i++)
+ refcount_inc(&count);
+
+ for (i = INT_MAX; i > 0; i--)
+ if (refcount_dec_and_test(&count))
+ break;
+
+ if (i != 1)
+ pr_err("refcount: out of sync up/down cycle: %u\n", i - 1);
+ else
+ pr_info("refcount timing: done\n");
+}
diff --git a/drivers/misc/lkdtm_rodata.c b/drivers/misc/lkdtm/rodata.c
index 58d180af72cf..58d180af72cf 100644
--- a/drivers/misc/lkdtm_rodata.c
+++ b/drivers/misc/lkdtm/rodata.c
diff --git a/drivers/misc/lkdtm_usercopy.c b/drivers/misc/lkdtm/usercopy.c
index 9725aed305bb..9725aed305bb 100644
--- a/drivers/misc/lkdtm_usercopy.c
+++ b/drivers/misc/lkdtm/usercopy.c
diff --git a/drivers/misc/lkdtm_refcount.c b/drivers/misc/lkdtm_refcount.c
deleted file mode 100644
index 2b99d448e7fd..000000000000
--- a/drivers/misc/lkdtm_refcount.c
+++ /dev/null
@@ -1,400 +0,0 @@
-/*
- * This is for all the tests related to refcount bugs (e.g. overflow,
- * underflow, reaching zero untested, etc).
- */
-#include "lkdtm.h"
-#include <linux/refcount.h>
-
-#ifdef CONFIG_REFCOUNT_FULL
-#define REFCOUNT_MAX (UINT_MAX - 1)
-#define REFCOUNT_SATURATED UINT_MAX
-#else
-#define REFCOUNT_MAX INT_MAX
-#define REFCOUNT_SATURATED (INT_MIN / 2)
-#endif
-
-static void overflow_check(refcount_t *ref)
-{
- switch (refcount_read(ref)) {
- case REFCOUNT_SATURATED:
- pr_info("Overflow detected: saturated\n");
- break;
- case REFCOUNT_MAX:
- pr_warn("Overflow detected: unsafely reset to max\n");
- break;
- default:
- pr_err("Fail: refcount wrapped to %d\n", refcount_read(ref));
- }
-}
-
-/*
- * A refcount_inc() above the maximum value of the refcount implementation,
- * should at least saturate, and at most also WARN.
- */
-void lkdtm_REFCOUNT_INC_OVERFLOW(void)
-{
- refcount_t over = REFCOUNT_INIT(REFCOUNT_MAX - 1);
-
- pr_info("attempting good refcount_inc() without overflow\n");
- refcount_dec(&over);
- refcount_inc(&over);
-
- pr_info("attempting bad refcount_inc() overflow\n");
- refcount_inc(&over);
- refcount_inc(&over);
-
- overflow_check(&over);
-}
-
-/* refcount_add() should behave just like refcount_inc() above. */
-void lkdtm_REFCOUNT_ADD_OVERFLOW(void)
-{
- refcount_t over = REFCOUNT_INIT(REFCOUNT_MAX - 1);
-
- pr_info("attempting good refcount_add() without overflow\n");
- refcount_dec(&over);
- refcount_dec(&over);
- refcount_dec(&over);
- refcount_dec(&over);
- refcount_add(4, &over);
-
- pr_info("attempting bad refcount_add() overflow\n");
- refcount_add(4, &over);
-
- overflow_check(&over);
-}
-
-/* refcount_inc_not_zero() should behave just like refcount_inc() above. */
-void lkdtm_REFCOUNT_INC_NOT_ZERO_OVERFLOW(void)
-{
- refcount_t over = REFCOUNT_INIT(REFCOUNT_MAX);
-
- pr_info("attempting bad refcount_inc_not_zero() overflow\n");
- if (!refcount_inc_not_zero(&over))
- pr_warn("Weird: refcount_inc_not_zero() reported zero\n");
-
- overflow_check(&over);
-}
-
-/* refcount_add_not_zero() should behave just like refcount_inc() above. */
-void lkdtm_REFCOUNT_ADD_NOT_ZERO_OVERFLOW(void)
-{
- refcount_t over = REFCOUNT_INIT(REFCOUNT_MAX);
-
- pr_info("attempting bad refcount_add_not_zero() overflow\n");
- if (!refcount_add_not_zero(6, &over))
- pr_warn("Weird: refcount_add_not_zero() reported zero\n");
-
- overflow_check(&over);
-}
-
-static void check_zero(refcount_t *ref)
-{
- switch (refcount_read(ref)) {
- case REFCOUNT_SATURATED:
- pr_info("Zero detected: saturated\n");
- break;
- case REFCOUNT_MAX:
- pr_warn("Zero detected: unsafely reset to max\n");
- break;
- case 0:
- pr_warn("Still at zero: refcount_inc/add() must not inc-from-0\n");
- break;
- default:
- pr_err("Fail: refcount went crazy: %d\n", refcount_read(ref));
- }
-}
-
-/*
- * A refcount_dec(), as opposed to a refcount_dec_and_test(), when it hits
- * zero it should either saturate (when inc-from-zero isn't protected)
- * or stay at zero (when inc-from-zero is protected) and should WARN for both.
- */
-void lkdtm_REFCOUNT_DEC_ZERO(void)
-{
- refcount_t zero = REFCOUNT_INIT(2);
-
- pr_info("attempting good refcount_dec()\n");
- refcount_dec(&zero);
-
- pr_info("attempting bad refcount_dec() to zero\n");
- refcount_dec(&zero);
-
- check_zero(&zero);
-}
-
-static void check_negative(refcount_t *ref, int start)
-{
- /*
- * CONFIG_REFCOUNT_FULL refuses to move a refcount at all on an
- * over-sub, so we have to track our starting position instead of
- * looking only at zero-pinning.
- */
- if (refcount_read(ref) == start) {
- pr_warn("Still at %d: refcount_inc/add() must not inc-from-0\n",
- start);
- return;
- }
-
- switch (refcount_read(ref)) {
- case REFCOUNT_SATURATED:
- pr_info("Negative detected: saturated\n");
- break;
- case REFCOUNT_MAX:
- pr_warn("Negative detected: unsafely reset to max\n");
- break;
- default:
- pr_err("Fail: refcount went crazy: %d\n", refcount_read(ref));
- }
-}
-
-/* A refcount_dec() going negative should saturate and may WARN. */
-void lkdtm_REFCOUNT_DEC_NEGATIVE(void)
-{
- refcount_t neg = REFCOUNT_INIT(0);
-
- pr_info("attempting bad refcount_dec() below zero\n");
- refcount_dec(&neg);
-
- check_negative(&neg, 0);
-}
-
-/*
- * A refcount_dec_and_test() should act like refcount_dec() above when
- * going negative.
- */
-void lkdtm_REFCOUNT_DEC_AND_TEST_NEGATIVE(void)
-{
- refcount_t neg = REFCOUNT_INIT(0);
-
- pr_info("attempting bad refcount_dec_and_test() below zero\n");
- if (refcount_dec_and_test(&neg))
- pr_warn("Weird: refcount_dec_and_test() reported zero\n");
-
- check_negative(&neg, 0);
-}
-
-/*
- * A refcount_sub_and_test() should act like refcount_dec_and_test()
- * above when going negative.
- */
-void lkdtm_REFCOUNT_SUB_AND_TEST_NEGATIVE(void)
-{
- refcount_t neg = REFCOUNT_INIT(3);
-
- pr_info("attempting bad refcount_sub_and_test() below zero\n");
- if (refcount_sub_and_test(5, &neg))
- pr_warn("Weird: refcount_sub_and_test() reported zero\n");
-
- check_negative(&neg, 3);
-}
-
-static void check_from_zero(refcount_t *ref)
-{
- switch (refcount_read(ref)) {
- case 0:
- pr_info("Zero detected: stayed at zero\n");
- break;
- case REFCOUNT_SATURATED:
- pr_info("Zero detected: saturated\n");
- break;
- case REFCOUNT_MAX:
- pr_warn("Zero detected: unsafely reset to max\n");
- break;
- default:
- pr_info("Fail: zero not detected, incremented to %d\n",
- refcount_read(ref));
- }
-}
-
-/*
- * A refcount_inc() from zero should pin to zero or saturate and may WARN.
- * Only CONFIG_REFCOUNT_FULL provides this protection currently.
- */
-void lkdtm_REFCOUNT_INC_ZERO(void)
-{
- refcount_t zero = REFCOUNT_INIT(0);
-
- pr_info("attempting safe refcount_inc_not_zero() from zero\n");
- if (!refcount_inc_not_zero(&zero)) {
- pr_info("Good: zero detected\n");
- if (refcount_read(&zero) == 0)
- pr_info("Correctly stayed at zero\n");
- else
- pr_err("Fail: refcount went past zero!\n");
- } else {
- pr_err("Fail: Zero not detected!?\n");
- }
-
- pr_info("attempting bad refcount_inc() from zero\n");
- refcount_inc(&zero);
-
- check_from_zero(&zero);
-}
-
-/*
- * A refcount_add() should act like refcount_inc() above when starting
- * at zero.
- */
-void lkdtm_REFCOUNT_ADD_ZERO(void)
-{
- refcount_t zero = REFCOUNT_INIT(0);
-
- pr_info("attempting safe refcount_add_not_zero() from zero\n");
- if (!refcount_add_not_zero(3, &zero)) {
- pr_info("Good: zero detected\n");
- if (refcount_read(&zero) == 0)
- pr_info("Correctly stayed at zero\n");
- else
- pr_err("Fail: refcount went past zero\n");
- } else {
- pr_err("Fail: Zero not detected!?\n");
- }
-
- pr_info("attempting bad refcount_add() from zero\n");
- refcount_add(3, &zero);
-
- check_from_zero(&zero);
-}
-
-static void check_saturated(refcount_t *ref)
-{
- switch (refcount_read(ref)) {
- case REFCOUNT_SATURATED:
- pr_info("Saturation detected: still saturated\n");
- break;
- case REFCOUNT_MAX:
- pr_warn("Saturation detected: unsafely reset to max\n");
- break;
- default:
- pr_err("Fail: refcount went crazy: %d\n", refcount_read(ref));
- }
-}
-
-/*
- * A refcount_inc() from a saturated value should at most warn about
- * being saturated already.
- */
-void lkdtm_REFCOUNT_INC_SATURATED(void)
-{
- refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED);
-
- pr_info("attempting bad refcount_inc() from saturated\n");
- refcount_inc(&sat);
-
- check_saturated(&sat);
-}
-
-/* Should act like refcount_inc() above from saturated. */
-void lkdtm_REFCOUNT_DEC_SATURATED(void)
-{
- refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED);
-
- pr_info("attempting bad refcount_dec() from saturated\n");
- refcount_dec(&sat);
-
- check_saturated(&sat);
-}
-
-/* Should act like refcount_inc() above from saturated. */
-void lkdtm_REFCOUNT_ADD_SATURATED(void)
-{
- refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED);
-
- pr_info("attempting bad refcount_dec() from saturated\n");
- refcount_add(8, &sat);
-
- check_saturated(&sat);
-}
-
-/* Should act like refcount_inc() above from saturated. */
-void lkdtm_REFCOUNT_INC_NOT_ZERO_SATURATED(void)
-{
- refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED);
-
- pr_info("attempting bad refcount_inc_not_zero() from saturated\n");
- if (!refcount_inc_not_zero(&sat))
- pr_warn("Weird: refcount_inc_not_zero() reported zero\n");
-
- check_saturated(&sat);
-}
-
-/* Should act like refcount_inc() above from saturated. */
-void lkdtm_REFCOUNT_ADD_NOT_ZERO_SATURATED(void)
-{
- refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED);
-
- pr_info("attempting bad refcount_add_not_zero() from saturated\n");
- if (!refcount_add_not_zero(7, &sat))
- pr_warn("Weird: refcount_add_not_zero() reported zero\n");
-
- check_saturated(&sat);
-}
-
-/* Should act like refcount_inc() above from saturated. */
-void lkdtm_REFCOUNT_DEC_AND_TEST_SATURATED(void)
-{
- refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED);
-
- pr_info("attempting bad refcount_dec_and_test() from saturated\n");
- if (refcount_dec_and_test(&sat))
- pr_warn("Weird: refcount_dec_and_test() reported zero\n");
-
- check_saturated(&sat);
-}
-
-/* Should act like refcount_inc() above from saturated. */
-void lkdtm_REFCOUNT_SUB_AND_TEST_SATURATED(void)
-{
- refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED);
-
- pr_info("attempting bad refcount_sub_and_test() from saturated\n");
- if (refcount_sub_and_test(8, &sat))
- pr_warn("Weird: refcount_sub_and_test() reported zero\n");
-
- check_saturated(&sat);
-}
-
-/* Used to time the existing atomic_t when used for reference counting */
-void lkdtm_ATOMIC_TIMING(void)
-{
- unsigned int i;
- atomic_t count = ATOMIC_INIT(1);
-
- for (i = 0; i < INT_MAX - 1; i++)
- atomic_inc(&count);
-
- for (i = INT_MAX; i > 0; i--)
- if (atomic_dec_and_test(&count))
- break;
-
- if (i != 1)
- pr_err("atomic timing: out of sync up/down cycle: %u\n", i - 1);
- else
- pr_info("atomic timing: done\n");
-}
-
-/*
- * This can be compared to ATOMIC_TIMING when implementing fast refcount
- * protections. Looking at the number of CPU cycles tells the real story
- * about performance. For example:
- * cd /sys/kernel/debug/provoke-crash
- * perf stat -B -- cat <(echo REFCOUNT_TIMING) > DIRECT
- */
-void lkdtm_REFCOUNT_TIMING(void)
-{
- unsigned int i;
- refcount_t count = REFCOUNT_INIT(1);
-
- for (i = 0; i < INT_MAX - 1; i++)
- refcount_inc(&count);
-
- for (i = INT_MAX; i > 0; i--)
- if (refcount_dec_and_test(&count))
- break;
-
- if (i != 1)
- pr_err("refcount: out of sync up/down cycle: %u\n", i - 1);
- else
- pr_info("refcount timing: done\n");
-}
diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c
index 772d02922529..b1133739fb4b 100644
--- a/drivers/misc/mei/bus.c
+++ b/drivers/misc/mei/bus.c
@@ -74,6 +74,23 @@ ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
goto out;
}
+ while (cl->tx_cb_queued >= bus->tx_queue_limit) {
+ mutex_unlock(&bus->device_lock);
+ rets = wait_event_interruptible(cl->tx_wait,
+ cl->writing_state == MEI_WRITE_COMPLETE ||
+ (!mei_cl_is_connected(cl)));
+ mutex_lock(&bus->device_lock);
+ if (rets) {
+ if (signal_pending(current))
+ rets = -EINTR;
+ goto out;
+ }
+ if (!mei_cl_is_connected(cl)) {
+ rets = -ENODEV;
+ goto out;
+ }
+ }
+
cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, NULL);
if (!cb) {
rets = -ENOMEM;
@@ -450,6 +467,29 @@ bool mei_cldev_enabled(struct mei_cl_device *cldev)
EXPORT_SYMBOL_GPL(mei_cldev_enabled);
/**
+ * mei_cl_bus_module_get - acquire module of the underlying
+ * hw driver.
+ *
+ * @cldev: mei client device
+ *
+ * Return: true on success; false if the module was removed.
+ */
+static bool mei_cl_bus_module_get(struct mei_cl_device *cldev)
+{
+ return try_module_get(cldev->bus->dev->driver->owner);
+}
+
+/**
+ * mei_cl_bus_module_put - release the underlying hw module.
+ *
+ * @cldev: mei client device
+ */
+static void mei_cl_bus_module_put(struct mei_cl_device *cldev)
+{
+ module_put(cldev->bus->dev->driver->owner);
+}
+
+/**
* mei_cldev_enable - enable me client device
* create connection with me client
*
@@ -487,9 +527,17 @@ int mei_cldev_enable(struct mei_cl_device *cldev)
goto out;
}
+ if (!mei_cl_bus_module_get(cldev)) {
+ dev_err(&cldev->dev, "get hw module failed");
+ ret = -ENODEV;
+ goto out;
+ }
+
ret = mei_cl_connect(cl, cldev->me_cl, NULL);
- if (ret < 0)
+ if (ret < 0) {
dev_err(&cldev->dev, "cannot connect\n");
+ mei_cl_bus_module_put(cldev);
+ }
out:
mutex_unlock(&bus->device_lock);
@@ -553,6 +601,8 @@ int mei_cldev_disable(struct mei_cl_device *cldev)
dev_err(bus->dev, "Could not disconnect from the ME client\n");
out:
+ mei_cl_bus_module_put(cldev);
+
/* Flush queues and remove any pending read */
mei_cl_flush_queues(cl, NULL);
mei_cl_unlink(cl);
@@ -563,37 +613,6 @@ out:
EXPORT_SYMBOL_GPL(mei_cldev_disable);
/**
- * mei_cl_bus_module_get - acquire module of the underlying
- * hw module.
- *
- * @cl: host client
- *
- * Return: true on success; false if the module was removed.
- */
-bool mei_cl_bus_module_get(struct mei_cl *cl)
-{
- struct mei_cl_device *cldev = cl->cldev;
-
- if (!cldev)
- return true;
-
- return try_module_get(cldev->bus->dev->driver->owner);
-}
-
-/**
- * mei_cl_bus_module_put - release the underlying hw module.
- *
- * @cl: host client
- */
-void mei_cl_bus_module_put(struct mei_cl *cl)
-{
- struct mei_cl_device *cldev = cl->cldev;
-
- if (cldev)
- module_put(cldev->bus->dev->driver->owner);
-}
-
-/**
* mei_cl_device_find - find matching entry in the driver id table
*
* @cldev: me client device
diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c
index 7e60c1817c31..8d6197a88b54 100644
--- a/drivers/misc/mei/client.c
+++ b/drivers/misc/mei/client.c
@@ -350,6 +350,36 @@ void mei_io_cb_free(struct mei_cl_cb *cb)
}
/**
+ * mei_tx_cb_queue - queue tx callback
+ *
+ * Locking: called under "dev->device_lock" lock
+ *
+ * @cb: mei callback struct
+ * @head: an instance of list to queue on
+ */
+static inline void mei_tx_cb_enqueue(struct mei_cl_cb *cb,
+ struct list_head *head)
+{
+ list_add_tail(&cb->list, head);
+ cb->cl->tx_cb_queued++;
+}
+
+/**
+ * mei_tx_cb_dequeue - dequeue tx callback
+ *
+ * Locking: called under "dev->device_lock" lock
+ *
+ * @cb: mei callback struct to dequeue and free
+ */
+static inline void mei_tx_cb_dequeue(struct mei_cl_cb *cb)
+{
+ if (!WARN_ON(cb->cl->tx_cb_queued == 0))
+ cb->cl->tx_cb_queued--;
+
+ mei_io_cb_free(cb);
+}
+
+/**
* mei_io_cb_init - allocate and initialize io callback
*
* @cl: mei client
@@ -377,49 +407,37 @@ static struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl,
}
/**
- * __mei_io_list_flush_cl - removes and frees cbs belonging to cl.
+ * mei_io_list_flush_cl - removes cbs belonging to the cl.
*
* @head: an instance of our list structure
- * @cl: host client, can be NULL for flushing the whole list
- * @free: whether to free the cbs
+ * @cl: host client
*/
-static void __mei_io_list_flush_cl(struct list_head *head,
- const struct mei_cl *cl, bool free)
+static void mei_io_list_flush_cl(struct list_head *head,
+ const struct mei_cl *cl)
{
struct mei_cl_cb *cb, *next;
- /* enable removing everything if no cl is specified */
list_for_each_entry_safe(cb, next, head, list) {
- if (!cl || mei_cl_cmp_id(cl, cb->cl)) {
+ if (mei_cl_cmp_id(cl, cb->cl))
list_del_init(&cb->list);
- if (free)
- mei_io_cb_free(cb);
- }
}
}
/**
- * mei_io_list_flush_cl - removes list entry belonging to cl.
+ * mei_io_tx_list_free_cl - removes cb belonging to the cl and free them
*
* @head: An instance of our list structure
* @cl: host client
*/
-static inline void mei_io_list_flush_cl(struct list_head *head,
- const struct mei_cl *cl)
+static void mei_io_tx_list_free_cl(struct list_head *head,
+ const struct mei_cl *cl)
{
- __mei_io_list_flush_cl(head, cl, false);
-}
+ struct mei_cl_cb *cb, *next;
-/**
- * mei_io_list_free_cl - removes cb belonging to cl and free them
- *
- * @head: An instance of our list structure
- * @cl: host client
- */
-static inline void mei_io_list_free_cl(struct list_head *head,
- const struct mei_cl *cl)
-{
- __mei_io_list_flush_cl(head, cl, true);
+ list_for_each_entry_safe(cb, next, head, list) {
+ if (mei_cl_cmp_id(cl, cb->cl))
+ mei_tx_cb_dequeue(cb);
+ }
}
/**
@@ -538,8 +556,8 @@ int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp)
dev = cl->dev;
cl_dbg(dev, cl, "remove list entry belonging to cl\n");
- mei_io_list_free_cl(&cl->dev->write_list, cl);
- mei_io_list_free_cl(&cl->dev->write_waiting_list, cl);
+ mei_io_tx_list_free_cl(&cl->dev->write_list, cl);
+ mei_io_tx_list_free_cl(&cl->dev->write_waiting_list, cl);
mei_io_list_flush_cl(&cl->dev->ctrl_wr_list, cl);
mei_io_list_flush_cl(&cl->dev->ctrl_rd_list, cl);
mei_io_list_free_fp(&cl->rd_pending, fp);
@@ -756,8 +774,8 @@ static void mei_cl_set_disconnected(struct mei_cl *cl)
return;
cl->state = MEI_FILE_DISCONNECTED;
- mei_io_list_free_cl(&dev->write_list, cl);
- mei_io_list_free_cl(&dev->write_waiting_list, cl);
+ mei_io_tx_list_free_cl(&dev->write_list, cl);
+ mei_io_tx_list_free_cl(&dev->write_waiting_list, cl);
mei_io_list_flush_cl(&dev->ctrl_rd_list, cl);
mei_io_list_flush_cl(&dev->ctrl_wr_list, cl);
mei_cl_wake_all(cl);
@@ -765,8 +783,6 @@ static void mei_cl_set_disconnected(struct mei_cl *cl)
cl->tx_flow_ctrl_creds = 0;
cl->timer_count = 0;
- mei_cl_bus_module_put(cl);
-
if (!cl->me_cl)
return;
@@ -1076,9 +1092,6 @@ int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl,
dev = cl->dev;
- if (!mei_cl_bus_module_get(cl))
- return -ENODEV;
-
rets = mei_cl_set_connecting(cl, me_cl);
if (rets)
goto nortpm;
@@ -1698,9 +1711,9 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb)
out:
if (mei_hdr.msg_complete)
- list_add_tail(&cb->list, &dev->write_waiting_list);
+ mei_tx_cb_enqueue(cb, &dev->write_waiting_list);
else
- list_add_tail(&cb->list, &dev->write_list);
+ mei_tx_cb_enqueue(cb, &dev->write_list);
cb = NULL;
if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) {
@@ -1746,7 +1759,7 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
switch (cb->fop_type) {
case MEI_FOP_WRITE:
- mei_io_cb_free(cb);
+ mei_tx_cb_dequeue(cb);
cl->writing_state = MEI_WRITE_COMPLETE;
if (waitqueue_active(&cl->tx_wait)) {
wake_up_interruptible(&cl->tx_wait);
diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c
index a617aa5a3ad8..c815da91089c 100644
--- a/drivers/misc/mei/debugfs.c
+++ b/drivers/misc/mei/debugfs.c
@@ -97,7 +97,7 @@ static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf,
int pos = 0;
int ret;
-#define HDR " |me|host|state|rd|wr|\n"
+#define HDR " |me|host|state|rd|wr|wrq\n"
if (!dev)
return -ENODEV;
@@ -130,9 +130,10 @@ static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf,
list_for_each_entry(cl, &dev->file_list, link) {
pos += scnprintf(buf + pos, bufsz - pos,
- "%3d|%2d|%4d|%5d|%2d|%2d|\n",
+ "%3d|%2d|%4d|%5d|%2d|%2d|%3u\n",
i, mei_cl_me_id(cl), cl->host_client_id, cl->state,
- !list_empty(&cl->rd_completed), cl->writing_state);
+ !list_empty(&cl->rd_completed), cl->writing_state,
+ cl->tx_cb_queued);
i++;
}
out:
diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c
index c46f6e99a55e..4888ebc076b7 100644
--- a/drivers/misc/mei/init.c
+++ b/drivers/misc/mei/init.c
@@ -383,6 +383,7 @@ void mei_device_init(struct mei_device *dev,
INIT_LIST_HEAD(&dev->write_waiting_list);
INIT_LIST_HEAD(&dev->ctrl_wr_list);
INIT_LIST_HEAD(&dev->ctrl_rd_list);
+ dev->tx_queue_limit = MEI_TX_QUEUE_LIMIT_DEFAULT;
INIT_DELAYED_WORK(&dev->timer_work, mei_timer);
INIT_WORK(&dev->reset_work, mei_reset_work);
diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c
index 758dc73602d5..7465f17e1559 100644
--- a/drivers/misc/mei/main.c
+++ b/drivers/misc/mei/main.c
@@ -291,6 +291,27 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
goto out;
}
+ while (cl->tx_cb_queued >= dev->tx_queue_limit) {
+ if (file->f_flags & O_NONBLOCK) {
+ rets = -EAGAIN;
+ goto out;
+ }
+ mutex_unlock(&dev->device_lock);
+ rets = wait_event_interruptible(cl->tx_wait,
+ cl->writing_state == MEI_WRITE_COMPLETE ||
+ (!mei_cl_is_connected(cl)));
+ mutex_lock(&dev->device_lock);
+ if (rets) {
+ if (signal_pending(current))
+ rets = -EINTR;
+ goto out;
+ }
+ if (!mei_cl_is_connected(cl)) {
+ rets = -ENODEV;
+ goto out;
+ }
+ }
+
*offset = 0;
cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, file);
if (!cb) {
@@ -507,7 +528,6 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data)
break;
default:
- dev_err(dev->dev, ": unsupported ioctl %d.\n", cmd);
rets = -ENOIOCTLCMD;
}
@@ -580,6 +600,12 @@ static __poll_t mei_poll(struct file *file, poll_table *wait)
mei_cl_read_start(cl, mei_cl_mtu(cl), file);
}
+ if (req_events & (POLLOUT | POLLWRNORM)) {
+ poll_wait(file, &cl->tx_wait, wait);
+ if (cl->tx_cb_queued < dev->tx_queue_limit)
+ mask |= POLLOUT | POLLWRNORM;
+ }
+
out:
mutex_unlock(&dev->device_lock);
return mask;
@@ -749,10 +775,48 @@ static ssize_t hbm_ver_drv_show(struct device *device,
}
static DEVICE_ATTR_RO(hbm_ver_drv);
+static ssize_t tx_queue_limit_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct mei_device *dev = dev_get_drvdata(device);
+ u8 size = 0;
+
+ mutex_lock(&dev->device_lock);
+ size = dev->tx_queue_limit;
+ mutex_unlock(&dev->device_lock);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", size);
+}
+
+static ssize_t tx_queue_limit_store(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mei_device *dev = dev_get_drvdata(device);
+ u8 limit;
+ unsigned int inp;
+ int err;
+
+ err = kstrtouint(buf, 10, &inp);
+ if (err)
+ return err;
+ if (inp > MEI_TX_QUEUE_LIMIT_MAX || inp < MEI_TX_QUEUE_LIMIT_MIN)
+ return -EINVAL;
+ limit = inp;
+
+ mutex_lock(&dev->device_lock);
+ dev->tx_queue_limit = limit;
+ mutex_unlock(&dev->device_lock);
+
+ return count;
+}
+static DEVICE_ATTR_RW(tx_queue_limit);
+
static struct attribute *mei_attrs[] = {
&dev_attr_fw_status.attr,
&dev_attr_hbm_ver.attr,
&dev_attr_hbm_ver_drv.attr,
+ &dev_attr_tx_queue_limit.attr,
NULL
};
ATTRIBUTE_GROUPS(mei);
diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h
index ebcd5132e447..be9c48415da9 100644
--- a/drivers/misc/mei/mei_dev.h
+++ b/drivers/misc/mei/mei_dev.h
@@ -210,6 +210,7 @@ struct mei_cl_cb {
* @timer_count: watchdog timer for operation completion
* @notify_en: notification - enabled/disabled
* @notify_ev: pending notification event
+ * @tx_cb_queued: number of tx callbacks in queue
* @writing_state: state of the tx
* @rd_pending: pending read credits
* @rd_completed: completed read
@@ -234,6 +235,7 @@ struct mei_cl {
u8 timer_count;
u8 notify_en;
u8 notify_ev;
+ u8 tx_cb_queued;
enum mei_file_transaction_states writing_state;
struct list_head rd_pending;
struct list_head rd_completed;
@@ -241,6 +243,10 @@ struct mei_cl {
struct mei_cl_device *cldev;
};
+#define MEI_TX_QUEUE_LIMIT_DEFAULT 50
+#define MEI_TX_QUEUE_LIMIT_MAX 255
+#define MEI_TX_QUEUE_LIMIT_MIN 30
+
/**
* struct mei_hw_ops - hw specific ops
*
@@ -315,8 +321,6 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length,
bool mei_cl_bus_rx_event(struct mei_cl *cl);
bool mei_cl_bus_notify_event(struct mei_cl *cl);
void mei_cl_bus_remove_devices(struct mei_device *bus);
-bool mei_cl_bus_module_get(struct mei_cl *cl);
-void mei_cl_bus_module_put(struct mei_cl *cl);
int mei_cl_bus_init(void);
void mei_cl_bus_exit(void);
@@ -361,6 +365,7 @@ const char *mei_pg_state_str(enum mei_pg_state state);
* @write_waiting_list : write completion list
* @ctrl_wr_list : pending control write list
* @ctrl_rd_list : pending control read list
+ * @tx_queue_limit: tx queues per client linit
*
* @file_list : list of opened handles
* @open_handle_count: number of opened handles
@@ -425,6 +430,7 @@ struct mei_device {
struct list_head write_waiting_list;
struct list_head ctrl_wr_list;
struct list_head ctrl_rd_list;
+ u8 tx_queue_limit;
struct list_head file_list;
long open_handle_count;
diff --git a/drivers/misc/mic/bus/vop_bus.c b/drivers/misc/mic/bus/vop_bus.c
index fd7f2a6049f8..e5bb9c749b5d 100644
--- a/drivers/misc/mic/bus/vop_bus.c
+++ b/drivers/misc/mic/bus/vop_bus.c
@@ -135,7 +135,9 @@ EXPORT_SYMBOL_GPL(vop_unregister_driver);
static void vop_release_dev(struct device *d)
{
- put_device(d);
+ struct vop_device *dev = dev_to_vop(d);
+
+ kfree(dev);
}
struct vop_device *
@@ -174,7 +176,7 @@ vop_register_device(struct device *pdev, int id,
goto free_vdev;
return vdev;
free_vdev:
- kfree(vdev);
+ put_device(&vdev->dev);
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(vop_register_device);
diff --git a/drivers/misc/ocxl/pci.c b/drivers/misc/ocxl/pci.c
index 0051d9ec76cc..21f425472a82 100644
--- a/drivers/misc/ocxl/pci.c
+++ b/drivers/misc/ocxl/pci.c
@@ -519,7 +519,7 @@ static struct ocxl_fn *init_function(struct pci_dev *dev)
rc = device_register(&fn->dev);
if (rc) {
deconfigure_function(fn);
- device_unregister(&fn->dev);
+ put_device(&fn->dev);
return ERR_PTR(rc);
}
return fn;
diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c
index 320276f42653..fe8897e64635 100644
--- a/drivers/misc/pci_endpoint_test.c
+++ b/drivers/misc/pci_endpoint_test.c
@@ -534,12 +534,14 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev,
}
for (bar = BAR_0; bar <= BAR_5; bar++) {
- base = pci_ioremap_bar(pdev, bar);
- if (!base) {
- dev_err(dev, "failed to read BAR%d\n", bar);
- WARN_ON(bar == test_reg_bar);
+ if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) {
+ base = pci_ioremap_bar(pdev, bar);
+ if (!base) {
+ dev_err(dev, "failed to read BAR%d\n", bar);
+ WARN_ON(bar == test_reg_bar);
+ }
+ test->bar[bar] = base;
}
- test->bar[bar] = base;
}
test->base = test->bar[test_reg_bar];
diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
index 2cfb963d9f37..9e923cd1d80e 100644
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
@@ -376,22 +376,15 @@ static struct mmc_blk_ioc_data *mmc_blk_ioctl_copy_from_user(
return idata;
}
- idata->buf = kmalloc(idata->buf_bytes, GFP_KERNEL);
- if (!idata->buf) {
- err = -ENOMEM;
+ idata->buf = memdup_user((void __user *)(unsigned long)
+ idata->ic.data_ptr, idata->buf_bytes);
+ if (IS_ERR(idata->buf)) {
+ err = PTR_ERR(idata->buf);
goto idata_err;
}
- if (copy_from_user(idata->buf, (void __user *)(unsigned long)
- idata->ic.data_ptr, idata->buf_bytes)) {
- err = -EFAULT;
- goto copy_err;
- }
-
return idata;
-copy_err:
- kfree(idata->buf);
idata_err:
kfree(idata);
out:
@@ -2666,7 +2659,6 @@ static void mmc_blk_remove_req(struct mmc_blk_data *md)
* from being accepted.
*/
card = md->queue.card;
- mmc_cleanup_queue(&md->queue);
if (md->disk->flags & GENHD_FL_UP) {
device_remove_file(disk_to_dev(md->disk), &md->force_ro);
if ((md->area_type & MMC_BLK_DATA_AREA_BOOT) &&
@@ -2676,6 +2668,7 @@ static void mmc_blk_remove_req(struct mmc_blk_data *md)
del_gendisk(md->disk);
}
+ mmc_cleanup_queue(&md->queue);
mmc_blk_put(md);
}
}
@@ -3087,6 +3080,7 @@ static void __exit mmc_blk_exit(void)
mmc_unregister_driver(&mmc_driver);
unregister_blkdev(MMC_BLOCK_MAJOR, "mmc");
unregister_chrdev_region(mmc_rpmb_devt, MAX_DEVICES);
+ bus_unregister(&mmc_rpmb_bus_type);
}
module_init(mmc_blk_init);
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index c0ba6d8823b7..121ce50b6d5e 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -2369,7 +2369,7 @@ unsigned int mmc_calc_max_discard(struct mmc_card *card)
return card->pref_erase;
max_discard = mmc_do_calc_max_discard(card, MMC_ERASE_ARG);
- if (mmc_can_trim(card)) {
+ if (max_discard && mmc_can_trim(card)) {
max_trim = mmc_do_calc_max_discard(card, MMC_TRIM_ARG);
if (max_trim < max_discard)
max_discard = max_trim;
@@ -2655,8 +2655,7 @@ void mmc_start_host(struct mmc_host *host)
void mmc_stop_host(struct mmc_host *host)
{
if (host->slot.cd_irq >= 0) {
- if (host->slot.cd_wake_enabled)
- disable_irq_wake(host->slot.cd_irq);
+ mmc_gpio_set_cd_wake(host, false);
disable_irq(host->slot.cd_irq);
}
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
index 0f4a7d7b2626..d2275c5a2311 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -196,18 +196,7 @@ static int mmc_ios_show(struct seq_file *s, void *data)
return 0;
}
-
-static int mmc_ios_open(struct inode *inode, struct file *file)
-{
- return single_open(file, mmc_ios_show, inode->i_private);
-}
-
-static const struct file_operations mmc_ios_fops = {
- .open = mmc_ios_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(mmc_ios);
static int mmc_clock_opt_get(void *data, u64 *val)
{
@@ -254,6 +243,12 @@ void mmc_add_host_debugfs(struct mmc_host *host)
if (!debugfs_create_file("ios", S_IRUSR, root, host, &mmc_ios_fops))
goto err_node;
+ if (!debugfs_create_x32("caps", S_IRUSR, root, &host->caps))
+ goto err_node;
+
+ if (!debugfs_create_x32("caps2", S_IRUSR, root, &host->caps2))
+ goto err_node;
+
if (!debugfs_create_file("clock", S_IRUSR | S_IWUSR, root, host,
&mmc_clock_fops))
goto err_node;
diff --git a/drivers/mmc/core/host.h b/drivers/mmc/core/host.h
index 06ec19b5bf9f..4805438c02ff 100644
--- a/drivers/mmc/core/host.h
+++ b/drivers/mmc/core/host.h
@@ -56,7 +56,8 @@ static inline int mmc_host_uhs(struct mmc_host *host)
return host->caps &
(MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
- MMC_CAP_UHS_DDR50);
+ MMC_CAP_UHS_DDR50) &&
+ host->caps & MMC_CAP_4_BIT_DATA;
}
static inline bool mmc_card_hs200(struct mmc_card *card)
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 208a762b87ef..6f8ebd6caa4c 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -792,6 +792,7 @@ MMC_DEV_ATTR(enhanced_area_size, "%u\n", card->ext_csd.enhanced_area_size);
MMC_DEV_ATTR(raw_rpmb_size_mult, "%#x\n", card->ext_csd.raw_rpmb_size_mult);
MMC_DEV_ATTR(rel_sectors, "%#x\n", card->ext_csd.rel_sectors);
MMC_DEV_ATTR(ocr, "0x%08x\n", card->ocr);
+MMC_DEV_ATTR(rca, "0x%04x\n", card->rca);
MMC_DEV_ATTR(cmdq_en, "%d\n", card->ext_csd.cmdq_en);
static ssize_t mmc_fwrev_show(struct device *dev,
@@ -848,6 +849,7 @@ static struct attribute *mmc_std_attrs[] = {
&dev_attr_raw_rpmb_size_mult.attr,
&dev_attr_rel_sectors.attr,
&dev_attr_ocr.attr,
+ &dev_attr_rca.attr,
&dev_attr_dsr.attr,
&dev_attr_cmdq_en.attr,
NULL,
diff --git a/drivers/mmc/core/queue.c b/drivers/mmc/core/queue.c
index 421fab7250ac..56e9a803db21 100644
--- a/drivers/mmc/core/queue.c
+++ b/drivers/mmc/core/queue.c
@@ -185,14 +185,14 @@ static void mmc_queue_setup_discard(struct request_queue *q,
if (!max_discard)
return;
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q);
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, q);
blk_queue_max_discard_sectors(q, max_discard);
q->limits.discard_granularity = card->pref_erase << 9;
/* granularity must not be greater than max. discard */
if (card->pref_erase > max_discard)
q->limits.discard_granularity = 0;
if (mmc_can_secure_erase_trim(card))
- queue_flag_set_unlocked(QUEUE_FLAG_SECERASE, q);
+ blk_queue_flag_set(QUEUE_FLAG_SECERASE, q);
}
/**
@@ -356,8 +356,8 @@ static void mmc_setup_queue(struct mmc_queue *mq, struct mmc_card *card)
if (mmc_dev(host)->dma_mask && *mmc_dev(host)->dma_mask)
limit = (u64)dma_max_pfn(mmc_dev(host)) << PAGE_SHIFT;
- queue_flag_set_unlocked(QUEUE_FLAG_NONROT, mq->queue);
- queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, mq->queue);
+ blk_queue_flag_set(QUEUE_FLAG_NONROT, mq->queue);
+ blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, mq->queue);
if (mmc_can_erase(card))
mmc_queue_setup_discard(mq->queue, card);
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 62b84dd8f9fe..baf3d5da4ccb 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -291,8 +291,6 @@ static int mmc_read_switch(struct mmc_card *card)
return 0;
}
- err = -EIO;
-
status = kmalloc(64, GFP_KERNEL);
if (!status)
return -ENOMEM;
@@ -582,9 +580,6 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
int err;
u8 *status;
- if (!card->scr.sda_spec3)
- return 0;
-
if (!(card->csd.cmdclass & CCC_SWITCH))
return 0;
@@ -593,14 +588,11 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
return -ENOMEM;
/* Set 4-bit bus width */
- if ((card->host->caps & MMC_CAP_4_BIT_DATA) &&
- (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
- err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
- if (err)
- goto out;
+ err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
+ if (err)
+ goto out;
- mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
- }
+ mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
/*
* Select the bus speed mode depending on host
@@ -676,6 +668,7 @@ MMC_DEV_ATTR(name, "%s\n", card->cid.prod_name);
MMC_DEV_ATTR(oemid, "0x%04x\n", card->cid.oemid);
MMC_DEV_ATTR(serial, "0x%08x\n", card->cid.serial);
MMC_DEV_ATTR(ocr, "0x%08x\n", card->ocr);
+MMC_DEV_ATTR(rca, "0x%04x\n", card->rca);
static ssize_t mmc_dsr_show(struct device *dev,
@@ -709,6 +702,7 @@ static struct attribute *sd_std_attrs[] = {
&dev_attr_oemid.attr,
&dev_attr_serial.attr,
&dev_attr_ocr.attr,
+ &dev_attr_rca.attr,
&dev_attr_dsr.attr,
NULL,
};
@@ -1033,7 +1027,7 @@ retry:
}
/* Initialization sequence for UHS-I cards */
- if (rocr & SD_ROCR_S18A) {
+ if (rocr & SD_ROCR_S18A && mmc_host_uhs(host)) {
err = mmc_sd_init_uhs_card(card);
if (err)
goto free_card;
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index cc43687ca241..c599a628a387 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -518,11 +518,10 @@ static int mmc_sdio_init_uhs_card(struct mmc_card *card)
if (!card->scr.sda_spec3)
return 0;
- /*
- * Switch to wider bus (if supported).
- */
- if (card->host->caps & MMC_CAP_4_BIT_DATA)
- err = sdio_enable_4bit_bus(card);
+ /* Switch to wider bus */
+ err = sdio_enable_4bit_bus(card);
+ if (err)
+ goto out;
/* Set the driver strength for the card */
sdio_select_driver_type(card);
diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c
index 7a2eaf8410a3..7ca7b99413f0 100644
--- a/drivers/mmc/core/sdio_irq.c
+++ b/drivers/mmc/core/sdio_irq.c
@@ -277,8 +277,8 @@ static void sdio_single_irq_set(struct mmc_card *card)
*
* Claim and activate the IRQ for the given SDIO function. The provided
* handler will be called when that IRQ is asserted. The host is always
- * claimed already when the handler is called so the handler must not
- * call sdio_claim_host() nor sdio_release_host().
+ * claimed already when the handler is called so the handler should not
+ * call sdio_claim_host() or sdio_release_host().
*/
int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler)
{
diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c
index 3698b0576009..31f7dbb15668 100644
--- a/drivers/mmc/core/slot-gpio.c
+++ b/drivers/mmc/core/slot-gpio.c
@@ -149,11 +149,30 @@ void mmc_gpiod_request_cd_irq(struct mmc_host *host)
if (irq < 0)
host->caps |= MMC_CAP_NEEDS_POLL;
- else if ((host->caps & MMC_CAP_CD_WAKE) && !enable_irq_wake(irq))
- host->slot.cd_wake_enabled = true;
}
EXPORT_SYMBOL(mmc_gpiod_request_cd_irq);
+int mmc_gpio_set_cd_wake(struct mmc_host *host, bool on)
+{
+ int ret = 0;
+
+ if (!(host->caps & MMC_CAP_CD_WAKE) ||
+ host->slot.cd_irq < 0 ||
+ on == host->slot.cd_wake_enabled)
+ return 0;
+
+ if (on) {
+ ret = enable_irq_wake(host->slot.cd_irq);
+ host->slot.cd_wake_enabled = !ret;
+ } else {
+ disable_irq_wake(host->slot.cd_irq);
+ host->slot.cd_wake_enabled = false;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(mmc_gpio_set_cd_wake);
+
/* Register an alternate interrupt service routine for
* the card-detect GPIO.
*/
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 620c2d90a646..9589f9c9046f 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -646,25 +646,6 @@ config MMC_VIA_SDMMC
If unsure, say N.
-config SDH_BFIN
- tristate "Blackfin Secure Digital Host support"
- depends on (BF54x && !BF544) || (BF51x && !BF512)
- help
- If you say yes here you will get support for the Blackfin on-chip
- Secure Digital Host interface. This includes support for MMC and
- SD cards.
-
- To compile this driver as a module, choose M here: the
- module will be called bfin_sdh.
-
- If unsure, say N.
-
-config SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND
- bool "Blackfin EZkit Missing SDH_CMD Pull Up Resistor Workaround"
- depends on SDH_BFIN
- help
- If you say yes here SD-Cards may work on the EZkit.
-
config MMC_CAVIUM_OCTEON
tristate "Cavium OCTEON SD/MMC Card Interface support"
depends on CAVIUM_OCTEON_SOC
@@ -718,6 +699,15 @@ config MMC_DW_EXYNOS
Synopsys DesignWare Memory Card Interface driver. Select this option
for platforms based on Exynos4 and Exynos5 SoC's.
+config MMC_DW_HI3798CV200
+ tristate "Hi3798CV200 specific extensions for Synopsys DW Memory Card Interface"
+ depends on MMC_DW
+ select MMC_DW_PLTFM
+ help
+ This selects support for HiSilicon Hi3798CV200 SoC specific extensions to the
+ Synopsys DesignWare Memory Card Interface driver. Select this option
+ for platforms based on HiSilicon Hi3798CV200 SoC.
+
config MMC_DW_K3
tristate "K3 specific extensions for Synopsys DW Memory Card Interface"
depends on MMC_DW
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 84cd1388abc3..6aead24879b4 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -43,7 +43,6 @@ obj-$(CONFIG_MMC_SDHI_SYS_DMAC) += renesas_sdhi_sys_dmac.o
obj-$(CONFIG_MMC_SDHI_INTERNAL_DMAC) += renesas_sdhi_internal_dmac.o
obj-$(CONFIG_MMC_CB710) += cb710-mmc.o
obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o
-obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o
octeon-mmc-objs := cavium.o cavium-octeon.o
obj-$(CONFIG_MMC_CAVIUM_OCTEON) += octeon-mmc.o
thunderx-mmc-objs := cavium.o cavium-thunderx.o
@@ -51,6 +50,7 @@ obj-$(CONFIG_MMC_CAVIUM_THUNDERX) += thunderx-mmc.o
obj-$(CONFIG_MMC_DW) += dw_mmc.o
obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o
obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o
+obj-$(CONFIG_MMC_DW_HI3798CV200) += dw_mmc-hi3798cv200.o
obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o
obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
obj-$(CONFIG_MMC_DW_ROCKCHIP) += dw_mmc-rockchip.o
diff --git a/drivers/mmc/host/bfin_sdh.c b/drivers/mmc/host/bfin_sdh.c
deleted file mode 100644
index 526231e38583..000000000000
--- a/drivers/mmc/host/bfin_sdh.c
+++ /dev/null
@@ -1,679 +0,0 @@
-/*
- * bfin_sdh.c - Analog Devices Blackfin SDH Controller
- *
- * Copyright (C) 2007-2009 Analog Device Inc.
- *
- * Licensed under the GPL-2 or later.
- */
-
-#define DRIVER_NAME "bfin-sdh"
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/ioport.h>
-#include <linux/platform_device.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/dma-mapping.h>
-#include <linux/mmc/host.h>
-#include <linux/proc_fs.h>
-#include <linux/gfp.h>
-
-#include <asm/cacheflush.h>
-#include <asm/dma.h>
-#include <asm/portmux.h>
-#include <asm/bfin_sdh.h>
-
-#if defined(CONFIG_BF51x) || defined(__ADSPBF60x__)
-#define bfin_read_SDH_CLK_CTL bfin_read_RSI_CLK_CTL
-#define bfin_write_SDH_CLK_CTL bfin_write_RSI_CLK_CTL
-#define bfin_write_SDH_ARGUMENT bfin_write_RSI_ARGUMENT
-#define bfin_write_SDH_COMMAND bfin_write_RSI_COMMAND
-#define bfin_write_SDH_DATA_TIMER bfin_write_RSI_DATA_TIMER
-#define bfin_read_SDH_RESPONSE0 bfin_read_RSI_RESPONSE0
-#define bfin_read_SDH_RESPONSE1 bfin_read_RSI_RESPONSE1
-#define bfin_read_SDH_RESPONSE2 bfin_read_RSI_RESPONSE2
-#define bfin_read_SDH_RESPONSE3 bfin_read_RSI_RESPONSE3
-#define bfin_write_SDH_DATA_LGTH bfin_write_RSI_DATA_LGTH
-#define bfin_read_SDH_DATA_CTL bfin_read_RSI_DATA_CTL
-#define bfin_write_SDH_DATA_CTL bfin_write_RSI_DATA_CTL
-#define bfin_read_SDH_DATA_CNT bfin_read_RSI_DATA_CNT
-#define bfin_write_SDH_STATUS_CLR bfin_write_RSI_STATUS_CLR
-#define bfin_read_SDH_E_STATUS bfin_read_RSI_E_STATUS
-#define bfin_write_SDH_E_STATUS bfin_write_RSI_E_STATUS
-#define bfin_read_SDH_STATUS bfin_read_RSI_STATUS
-#define bfin_write_SDH_MASK0 bfin_write_RSI_MASK0
-#define bfin_write_SDH_E_MASK bfin_write_RSI_E_MASK
-#define bfin_read_SDH_CFG bfin_read_RSI_CFG
-#define bfin_write_SDH_CFG bfin_write_RSI_CFG
-# if defined(__ADSPBF60x__)
-# define bfin_read_SDH_BLK_SIZE bfin_read_RSI_BLKSZ
-# define bfin_write_SDH_BLK_SIZE bfin_write_RSI_BLKSZ
-# else
-# define bfin_read_SDH_PWR_CTL bfin_read_RSI_PWR_CTL
-# define bfin_write_SDH_PWR_CTL bfin_write_RSI_PWR_CTL
-# endif
-#endif
-
-struct sdh_host {
- struct mmc_host *mmc;
- spinlock_t lock;
- struct resource *res;
- void __iomem *base;
- int irq;
- int stat_irq;
- int dma_ch;
- int dma_dir;
- struct dma_desc_array *sg_cpu;
- dma_addr_t sg_dma;
- int dma_len;
-
- unsigned long sclk;
- unsigned int imask;
- unsigned int power_mode;
- unsigned int clk_div;
-
- struct mmc_request *mrq;
- struct mmc_command *cmd;
- struct mmc_data *data;
-};
-
-static struct bfin_sd_host *get_sdh_data(struct platform_device *pdev)
-{
- return pdev->dev.platform_data;
-}
-
-static void sdh_stop_clock(struct sdh_host *host)
-{
- bfin_write_SDH_CLK_CTL(bfin_read_SDH_CLK_CTL() & ~CLK_E);
- SSYNC();
-}
-
-static void sdh_enable_stat_irq(struct sdh_host *host, unsigned int mask)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&host->lock, flags);
- host->imask |= mask;
- bfin_write_SDH_MASK0(mask);
- SSYNC();
- spin_unlock_irqrestore(&host->lock, flags);
-}
-
-static void sdh_disable_stat_irq(struct sdh_host *host, unsigned int mask)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&host->lock, flags);
- host->imask &= ~mask;
- bfin_write_SDH_MASK0(host->imask);
- SSYNC();
- spin_unlock_irqrestore(&host->lock, flags);
-}
-
-static int sdh_setup_data(struct sdh_host *host, struct mmc_data *data)
-{
- unsigned int length;
- unsigned int data_ctl;
- unsigned int dma_cfg;
- unsigned int cycle_ns, timeout;
-
- dev_dbg(mmc_dev(host->mmc), "%s enter flags: 0x%x\n", __func__, data->flags);
- host->data = data;
- data_ctl = 0;
- dma_cfg = 0;
-
- length = data->blksz * data->blocks;
- bfin_write_SDH_DATA_LGTH(length);
-
- if (data->flags & MMC_DATA_READ)
- data_ctl |= DTX_DIR;
- /* Only supports power-of-2 block size */
- if (data->blksz & (data->blksz - 1))
- return -EINVAL;
-#ifndef RSI_BLKSZ
- data_ctl |= ((ffs(data->blksz) - 1) << 4);
-#else
- bfin_write_SDH_BLK_SIZE(data->blksz);
-#endif
-
- bfin_write_SDH_DATA_CTL(data_ctl);
- /* the time of a host clock period in ns */
- cycle_ns = 1000000000 / (host->sclk / (2 * (host->clk_div + 1)));
- timeout = data->timeout_ns / cycle_ns;
- timeout += data->timeout_clks;
- bfin_write_SDH_DATA_TIMER(timeout);
- SSYNC();
-
- if (data->flags & MMC_DATA_READ) {
- host->dma_dir = DMA_FROM_DEVICE;
- dma_cfg |= WNR;
- } else
- host->dma_dir = DMA_TO_DEVICE;
-
- sdh_enable_stat_irq(host, (DAT_CRC_FAIL | DAT_TIME_OUT | DAT_END));
- host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, host->dma_dir);
-#if defined(CONFIG_BF54x) || defined(CONFIG_BF60x)
- dma_cfg |= DMAFLOW_ARRAY | RESTART | WDSIZE_32 | DMAEN;
-# ifdef RSI_BLKSZ
- dma_cfg |= PSIZE_32 | NDSIZE_3;
-# else
- dma_cfg |= NDSIZE_5;
-# endif
- {
- struct scatterlist *sg;
- int i;
- for_each_sg(data->sg, sg, host->dma_len, i) {
- host->sg_cpu[i].start_addr = sg_dma_address(sg);
- host->sg_cpu[i].cfg = dma_cfg;
- host->sg_cpu[i].x_count = sg_dma_len(sg) / 4;
- host->sg_cpu[i].x_modify = 4;
- dev_dbg(mmc_dev(host->mmc), "%d: start_addr:0x%lx, "
- "cfg:0x%lx, x_count:0x%lx, x_modify:0x%lx\n",
- i, host->sg_cpu[i].start_addr,
- host->sg_cpu[i].cfg, host->sg_cpu[i].x_count,
- host->sg_cpu[i].x_modify);
- }
- }
- flush_dcache_range((unsigned int)host->sg_cpu,
- (unsigned int)host->sg_cpu +
- host->dma_len * sizeof(struct dma_desc_array));
- /* Set the last descriptor to stop mode */
- host->sg_cpu[host->dma_len - 1].cfg &= ~(DMAFLOW | NDSIZE);
- host->sg_cpu[host->dma_len - 1].cfg |= DI_EN;
-
- set_dma_curr_desc_addr(host->dma_ch, (unsigned long *)host->sg_dma);
- set_dma_x_count(host->dma_ch, 0);
- set_dma_x_modify(host->dma_ch, 0);
- SSYNC();
- set_dma_config(host->dma_ch, dma_cfg);
-#elif defined(CONFIG_BF51x)
- /* RSI DMA doesn't work in array mode */
- dma_cfg |= WDSIZE_32 | DMAEN;
- set_dma_start_addr(host->dma_ch, sg_dma_address(&data->sg[0]));
- set_dma_x_count(host->dma_ch, length / 4);
- set_dma_x_modify(host->dma_ch, 4);
- SSYNC();
- set_dma_config(host->dma_ch, dma_cfg);
-#endif
- bfin_write_SDH_DATA_CTL(bfin_read_SDH_DATA_CTL() | DTX_DMA_E | DTX_E);
-
- SSYNC();
-
- dev_dbg(mmc_dev(host->mmc), "%s exit\n", __func__);
- return 0;
-}
-
-static void sdh_start_cmd(struct sdh_host *host, struct mmc_command *cmd)
-{
- unsigned int sdh_cmd;
- unsigned int stat_mask;
-
- dev_dbg(mmc_dev(host->mmc), "%s enter cmd: 0x%p\n", __func__, cmd);
- WARN_ON(host->cmd != NULL);
- host->cmd = cmd;
-
- sdh_cmd = 0;
- stat_mask = 0;
-
- sdh_cmd |= cmd->opcode;
-
- if (cmd->flags & MMC_RSP_PRESENT) {
- sdh_cmd |= CMD_RSP;
- stat_mask |= CMD_RESP_END;
- } else {
- stat_mask |= CMD_SENT;
- }
-
- if (cmd->flags & MMC_RSP_136)
- sdh_cmd |= CMD_L_RSP;
-
- stat_mask |= CMD_CRC_FAIL | CMD_TIME_OUT;
-
- sdh_enable_stat_irq(host, stat_mask);
-
- bfin_write_SDH_ARGUMENT(cmd->arg);
- bfin_write_SDH_COMMAND(sdh_cmd | CMD_E);
- bfin_write_SDH_CLK_CTL(bfin_read_SDH_CLK_CTL() | CLK_E);
- SSYNC();
-}
-
-static void sdh_finish_request(struct sdh_host *host, struct mmc_request *mrq)
-{
- dev_dbg(mmc_dev(host->mmc), "%s enter\n", __func__);
- host->mrq = NULL;
- host->cmd = NULL;
- host->data = NULL;
- mmc_request_done(host->mmc, mrq);
-}
-
-static int sdh_cmd_done(struct sdh_host *host, unsigned int stat)
-{
- struct mmc_command *cmd = host->cmd;
- int ret = 0;
-
- dev_dbg(mmc_dev(host->mmc), "%s enter cmd: %p\n", __func__, cmd);
- if (!cmd)
- return 0;
-
- host->cmd = NULL;
-
- if (cmd->flags & MMC_RSP_PRESENT) {
- cmd->resp[0] = bfin_read_SDH_RESPONSE0();
- if (cmd->flags & MMC_RSP_136) {
- cmd->resp[1] = bfin_read_SDH_RESPONSE1();
- cmd->resp[2] = bfin_read_SDH_RESPONSE2();
- cmd->resp[3] = bfin_read_SDH_RESPONSE3();
- }
- }
- if (stat & CMD_TIME_OUT)
- cmd->error = -ETIMEDOUT;
- else if (stat & CMD_CRC_FAIL && cmd->flags & MMC_RSP_CRC)
- cmd->error = -EILSEQ;
-
- sdh_disable_stat_irq(host, (CMD_SENT | CMD_RESP_END | CMD_TIME_OUT | CMD_CRC_FAIL));
-
- if (host->data && !cmd->error) {
- if (host->data->flags & MMC_DATA_WRITE) {
- ret = sdh_setup_data(host, host->data);
- if (ret)
- return 0;
- }
-
- sdh_enable_stat_irq(host, DAT_END | RX_OVERRUN | TX_UNDERRUN | DAT_TIME_OUT);
- } else
- sdh_finish_request(host, host->mrq);
-
- return 1;
-}
-
-static int sdh_data_done(struct sdh_host *host, unsigned int stat)
-{
- struct mmc_data *data = host->data;
-
- dev_dbg(mmc_dev(host->mmc), "%s enter stat: 0x%x\n", __func__, stat);
- if (!data)
- return 0;
-
- disable_dma(host->dma_ch);
- dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
- host->dma_dir);
-
- if (stat & DAT_TIME_OUT)
- data->error = -ETIMEDOUT;
- else if (stat & DAT_CRC_FAIL)
- data->error = -EILSEQ;
- else if (stat & (RX_OVERRUN | TX_UNDERRUN))
- data->error = -EIO;
-
- if (!data->error)
- data->bytes_xfered = data->blocks * data->blksz;
- else
- data->bytes_xfered = 0;
-
- bfin_write_SDH_STATUS_CLR(DAT_END_STAT | DAT_TIMEOUT_STAT | \
- DAT_CRC_FAIL_STAT | DAT_BLK_END_STAT | RX_OVERRUN | TX_UNDERRUN);
- bfin_write_SDH_DATA_CTL(0);
- SSYNC();
-
- host->data = NULL;
- if (host->mrq->stop) {
- sdh_stop_clock(host);
- sdh_start_cmd(host, host->mrq->stop);
- } else {
- sdh_finish_request(host, host->mrq);
- }
-
- return 1;
-}
-
-static void sdh_request(struct mmc_host *mmc, struct mmc_request *mrq)
-{
- struct sdh_host *host = mmc_priv(mmc);
- int ret = 0;
-
- dev_dbg(mmc_dev(host->mmc), "%s enter, mrp:%p, cmd:%p\n", __func__, mrq, mrq->cmd);
- WARN_ON(host->mrq != NULL);
-
- spin_lock(&host->lock);
- host->mrq = mrq;
- host->data = mrq->data;
-
- if (mrq->data && mrq->data->flags & MMC_DATA_READ) {
- ret = sdh_setup_data(host, mrq->data);
- if (ret)
- goto data_err;
- }
-
- sdh_start_cmd(host, mrq->cmd);
-data_err:
- spin_unlock(&host->lock);
-}
-
-static void sdh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
-{
- struct sdh_host *host;
- u16 clk_ctl = 0;
-#ifndef RSI_BLKSZ
- u16 pwr_ctl = 0;
-#endif
- u16 cfg;
- host = mmc_priv(mmc);
-
- spin_lock(&host->lock);
-
- cfg = bfin_read_SDH_CFG();
- cfg |= MWE;
- switch (ios->bus_width) {
- case MMC_BUS_WIDTH_4:
-#ifndef RSI_BLKSZ
- cfg &= ~PD_SDDAT3;
-#endif
- cfg |= PUP_SDDAT3;
- /* Enable 4 bit SDIO */
- cfg |= SD4E;
- clk_ctl |= WIDE_BUS_4;
- break;
- case MMC_BUS_WIDTH_8:
-#ifndef RSI_BLKSZ
- cfg &= ~PD_SDDAT3;
-#endif
- cfg |= PUP_SDDAT3;
- /* Disable 4 bit SDIO */
- cfg &= ~SD4E;
- clk_ctl |= BYTE_BUS_8;
- break;
- default:
- cfg &= ~PUP_SDDAT3;
- /* Disable 4 bit SDIO */
- cfg &= ~SD4E;
- }
- bfin_write_SDH_CFG(cfg);
-
- host->power_mode = ios->power_mode;
-#ifndef RSI_BLKSZ
- if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) {
- pwr_ctl |= ROD_CTL;
-# ifndef CONFIG_SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND
- pwr_ctl |= SD_CMD_OD;
-# endif
- }
-
- if (ios->power_mode != MMC_POWER_OFF)
- pwr_ctl |= PWR_ON;
- else
- pwr_ctl &= ~PWR_ON;
-
- bfin_write_SDH_PWR_CTL(pwr_ctl);
-#else
-# ifndef CONFIG_SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND
- if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
- cfg |= SD_CMD_OD;
- else
- cfg &= ~SD_CMD_OD;
-# endif
-
- if (ios->power_mode != MMC_POWER_OFF)
- cfg |= PWR_ON;
- else
- cfg &= ~PWR_ON;
-
- bfin_write_SDH_CFG(cfg);
-#endif
- SSYNC();
-
- if (ios->power_mode == MMC_POWER_ON && ios->clock) {
- unsigned char clk_div;
- clk_div = (get_sclk() / ios->clock - 1) / 2;
- clk_div = min_t(unsigned char, clk_div, 0xFF);
- clk_ctl |= clk_div;
- clk_ctl |= CLK_E;
- host->clk_div = clk_div;
- bfin_write_SDH_CLK_CTL(clk_ctl);
- } else
- sdh_stop_clock(host);
-
- /* set up sdh interrupt mask*/
- if (ios->power_mode == MMC_POWER_ON)
- bfin_write_SDH_MASK0(DAT_END | DAT_TIME_OUT | DAT_CRC_FAIL |
- RX_OVERRUN | TX_UNDERRUN | CMD_SENT | CMD_RESP_END |
- CMD_TIME_OUT | CMD_CRC_FAIL);
- else
- bfin_write_SDH_MASK0(0);
- SSYNC();
-
- spin_unlock(&host->lock);
-
- dev_dbg(mmc_dev(host->mmc), "SDH: clk_div = 0x%x actual clock:%ld expected clock:%d\n",
- host->clk_div,
- host->clk_div ? get_sclk() / (2 * (host->clk_div + 1)) : 0,
- ios->clock);
-}
-
-static const struct mmc_host_ops sdh_ops = {
- .request = sdh_request,
- .set_ios = sdh_set_ios,
-};
-
-static irqreturn_t sdh_dma_irq(int irq, void *devid)
-{
- struct sdh_host *host = devid;
-
- dev_dbg(mmc_dev(host->mmc), "%s enter, irq_stat: 0x%04lx\n", __func__,
- get_dma_curr_irqstat(host->dma_ch));
- clear_dma_irqstat(host->dma_ch);
- SSYNC();
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t sdh_stat_irq(int irq, void *devid)
-{
- struct sdh_host *host = devid;
- unsigned int status;
- int handled = 0;
-
- dev_dbg(mmc_dev(host->mmc), "%s enter\n", __func__);
-
- spin_lock(&host->lock);
-
- status = bfin_read_SDH_E_STATUS();
- if (status & SD_CARD_DET) {
- mmc_detect_change(host->mmc, 0);
- bfin_write_SDH_E_STATUS(SD_CARD_DET);
- }
- status = bfin_read_SDH_STATUS();
- if (status & (CMD_SENT | CMD_RESP_END | CMD_TIME_OUT | CMD_CRC_FAIL)) {
- handled |= sdh_cmd_done(host, status);
- bfin_write_SDH_STATUS_CLR(CMD_SENT_STAT | CMD_RESP_END_STAT | \
- CMD_TIMEOUT_STAT | CMD_CRC_FAIL_STAT);
- SSYNC();
- }
-
- status = bfin_read_SDH_STATUS();
- if (status & (DAT_END | DAT_TIME_OUT | DAT_CRC_FAIL | RX_OVERRUN | TX_UNDERRUN))
- handled |= sdh_data_done(host, status);
-
- spin_unlock(&host->lock);
-
- dev_dbg(mmc_dev(host->mmc), "%s exit\n\n", __func__);
-
- return IRQ_RETVAL(handled);
-}
-
-static void sdh_reset(void)
-{
-#if defined(CONFIG_BF54x)
- /* Secure Digital Host shares DMA with Nand controller */
- bfin_write_DMAC1_PERIMUX(bfin_read_DMAC1_PERIMUX() | 0x1);
-#endif
-
- bfin_write_SDH_CFG(bfin_read_SDH_CFG() | CLKS_EN);
- SSYNC();
-
- /* Disable card inserting detection pin. set MMC_CAP_NEEDS_POLL, and
- * mmc stack will do the detection.
- */
- bfin_write_SDH_CFG((bfin_read_SDH_CFG() & 0x1F) | (PUP_SDDAT | PUP_SDDAT3));
- SSYNC();
-}
-
-static int sdh_probe(struct platform_device *pdev)
-{
- struct mmc_host *mmc;
- struct sdh_host *host;
- struct bfin_sd_host *drv_data = get_sdh_data(pdev);
- int ret;
-
- if (!drv_data) {
- dev_err(&pdev->dev, "missing platform driver data\n");
- ret = -EINVAL;
- goto out;
- }
-
- mmc = mmc_alloc_host(sizeof(struct sdh_host), &pdev->dev);
- if (!mmc) {
- ret = -ENOMEM;
- goto out;
- }
-
- mmc->ops = &sdh_ops;
-#if defined(CONFIG_BF51x)
- mmc->max_segs = 1;
-#else
- mmc->max_segs = PAGE_SIZE / sizeof(struct dma_desc_array);
-#endif
-#ifdef RSI_BLKSZ
- mmc->max_seg_size = -1;
-#else
- mmc->max_seg_size = 1 << 16;
-#endif
- mmc->max_blk_size = 1 << 11;
- mmc->max_blk_count = 1 << 11;
- mmc->max_req_size = PAGE_SIZE;
- mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
- mmc->f_max = get_sclk();
- mmc->f_min = mmc->f_max >> 9;
- mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_NEEDS_POLL;
- host = mmc_priv(mmc);
- host->mmc = mmc;
- host->sclk = get_sclk();
-
- spin_lock_init(&host->lock);
- host->irq = drv_data->irq_int0;
- host->dma_ch = drv_data->dma_chan;
-
- ret = request_dma(host->dma_ch, DRIVER_NAME "DMA");
- if (ret) {
- dev_err(&pdev->dev, "unable to request DMA channel\n");
- goto out1;
- }
-
- ret = set_dma_callback(host->dma_ch, sdh_dma_irq, host);
- if (ret) {
- dev_err(&pdev->dev, "unable to request DMA irq\n");
- goto out2;
- }
-
- host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL);
- if (host->sg_cpu == NULL) {
- ret = -ENOMEM;
- goto out2;
- }
-
- platform_set_drvdata(pdev, mmc);
-
- ret = request_irq(host->irq, sdh_stat_irq, 0, "SDH Status IRQ", host);
- if (ret) {
- dev_err(&pdev->dev, "unable to request status irq\n");
- goto out3;
- }
-
- ret = peripheral_request_list(drv_data->pin_req, DRIVER_NAME);
- if (ret) {
- dev_err(&pdev->dev, "unable to request peripheral pins\n");
- goto out4;
- }
-
- sdh_reset();
-
- mmc_add_host(mmc);
- return 0;
-
-out4:
- free_irq(host->irq, host);
-out3:
- mmc_remove_host(mmc);
- dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
-out2:
- free_dma(host->dma_ch);
-out1:
- mmc_free_host(mmc);
- out:
- return ret;
-}
-
-static int sdh_remove(struct platform_device *pdev)
-{
- struct mmc_host *mmc = platform_get_drvdata(pdev);
-
- if (mmc) {
- struct sdh_host *host = mmc_priv(mmc);
-
- mmc_remove_host(mmc);
-
- sdh_stop_clock(host);
- free_irq(host->irq, host);
- free_dma(host->dma_ch);
- dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
-
- mmc_free_host(mmc);
- }
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int sdh_suspend(struct platform_device *dev, pm_message_t state)
-{
- struct bfin_sd_host *drv_data = get_sdh_data(dev);
-
- peripheral_free_list(drv_data->pin_req);
-
- return 0;
-}
-
-static int sdh_resume(struct platform_device *dev)
-{
- struct bfin_sd_host *drv_data = get_sdh_data(dev);
- int ret = 0;
-
- ret = peripheral_request_list(drv_data->pin_req, DRIVER_NAME);
- if (ret) {
- dev_err(&dev->dev, "unable to request peripheral pins\n");
- return ret;
- }
-
- sdh_reset();
- return ret;
-}
-#else
-# define sdh_suspend NULL
-# define sdh_resume NULL
-#endif
-
-static struct platform_driver sdh_driver = {
- .probe = sdh_probe,
- .remove = sdh_remove,
- .suspend = sdh_suspend,
- .resume = sdh_resume,
- .driver = {
- .name = DRIVER_NAME,
- },
-};
-
-module_platform_driver(sdh_driver);
-
-MODULE_DESCRIPTION("Blackfin Secure Digital Host Driver");
-MODULE_AUTHOR("Cliff Cai, Roy Huang");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/dw_mmc-hi3798cv200.c b/drivers/mmc/host/dw_mmc-hi3798cv200.c
new file mode 100644
index 000000000000..f9b333ff259e
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc-hi3798cv200.c
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 HiSilicon Technologies Co., Ltd.
+ */
+
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mmc/host.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#include "dw_mmc.h"
+#include "dw_mmc-pltfm.h"
+
+#define ALL_INT_CLR 0x1ffff
+
+struct hi3798cv200_priv {
+ struct clk *sample_clk;
+ struct clk *drive_clk;
+};
+
+static void dw_mci_hi3798cv200_set_ios(struct dw_mci *host, struct mmc_ios *ios)
+{
+ struct hi3798cv200_priv *priv = host->priv;
+ u32 val;
+
+ val = mci_readl(host, UHS_REG);
+ if (ios->timing == MMC_TIMING_MMC_DDR52 ||
+ ios->timing == MMC_TIMING_UHS_DDR50)
+ val |= SDMMC_UHS_DDR;
+ else
+ val &= ~SDMMC_UHS_DDR;
+ mci_writel(host, UHS_REG, val);
+
+ val = mci_readl(host, ENABLE_SHIFT);
+ if (ios->timing == MMC_TIMING_MMC_DDR52)
+ val |= SDMMC_ENABLE_PHASE;
+ else
+ val &= ~SDMMC_ENABLE_PHASE;
+ mci_writel(host, ENABLE_SHIFT, val);
+
+ val = mci_readl(host, DDR_REG);
+ if (ios->timing == MMC_TIMING_MMC_HS400)
+ val |= SDMMC_DDR_HS400;
+ else
+ val &= ~SDMMC_DDR_HS400;
+ mci_writel(host, DDR_REG, val);
+
+ if (ios->timing == MMC_TIMING_MMC_HS ||
+ ios->timing == MMC_TIMING_LEGACY)
+ clk_set_phase(priv->drive_clk, 180);
+ else if (ios->timing == MMC_TIMING_MMC_HS200)
+ clk_set_phase(priv->drive_clk, 135);
+}
+
+static int dw_mci_hi3798cv200_execute_tuning(struct dw_mci_slot *slot,
+ u32 opcode)
+{
+ int degrees[] = { 0, 45, 90, 135, 180, 225, 270, 315 };
+ struct dw_mci *host = slot->host;
+ struct hi3798cv200_priv *priv = host->priv;
+ int raise_point = -1, fall_point = -1;
+ int err, prev_err = -1;
+ int found = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(degrees); i++) {
+ clk_set_phase(priv->sample_clk, degrees[i]);
+ mci_writel(host, RINTSTS, ALL_INT_CLR);
+
+ err = mmc_send_tuning(slot->mmc, opcode, NULL);
+ if (!err)
+ found = 1;
+
+ if (i > 0) {
+ if (err && !prev_err)
+ fall_point = i - 1;
+ if (!err && prev_err)
+ raise_point = i;
+ }
+
+ if (raise_point != -1 && fall_point != -1)
+ goto tuning_out;
+
+ prev_err = err;
+ err = 0;
+ }
+
+tuning_out:
+ if (found) {
+ if (raise_point == -1)
+ raise_point = 0;
+ if (fall_point == -1)
+ fall_point = ARRAY_SIZE(degrees) - 1;
+ if (fall_point < raise_point) {
+ if ((raise_point + fall_point) >
+ (ARRAY_SIZE(degrees) - 1))
+ i = fall_point / 2;
+ else
+ i = (raise_point + ARRAY_SIZE(degrees) - 1) / 2;
+ } else {
+ i = (raise_point + fall_point) / 2;
+ }
+
+ clk_set_phase(priv->sample_clk, degrees[i]);
+ dev_dbg(host->dev, "Tuning clk_sample[%d, %d], set[%d]\n",
+ raise_point, fall_point, degrees[i]);
+ } else {
+ dev_err(host->dev, "No valid clk_sample shift! use default\n");
+ err = -EINVAL;
+ }
+
+ mci_writel(host, RINTSTS, ALL_INT_CLR);
+ return err;
+}
+
+static int dw_mci_hi3798cv200_init(struct dw_mci *host)
+{
+ struct hi3798cv200_priv *priv;
+ int ret;
+
+ priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->sample_clk = devm_clk_get(host->dev, "ciu-sample");
+ if (IS_ERR(priv->sample_clk)) {
+ dev_err(host->dev, "failed to get ciu-sample clock\n");
+ return PTR_ERR(priv->sample_clk);
+ }
+
+ priv->drive_clk = devm_clk_get(host->dev, "ciu-drive");
+ if (IS_ERR(priv->drive_clk)) {
+ dev_err(host->dev, "failed to get ciu-drive clock\n");
+ return PTR_ERR(priv->drive_clk);
+ }
+
+ ret = clk_prepare_enable(priv->sample_clk);
+ if (ret) {
+ dev_err(host->dev, "failed to enable ciu-sample clock\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(priv->drive_clk);
+ if (ret) {
+ dev_err(host->dev, "failed to enable ciu-drive clock\n");
+ goto disable_sample_clk;
+ }
+
+ host->priv = priv;
+ return 0;
+
+disable_sample_clk:
+ clk_disable_unprepare(priv->sample_clk);
+ return ret;
+}
+
+static const struct dw_mci_drv_data hi3798cv200_data = {
+ .init = dw_mci_hi3798cv200_init,
+ .set_ios = dw_mci_hi3798cv200_set_ios,
+ .execute_tuning = dw_mci_hi3798cv200_execute_tuning,
+};
+
+static int dw_mci_hi3798cv200_probe(struct platform_device *pdev)
+{
+ return dw_mci_pltfm_register(pdev, &hi3798cv200_data);
+}
+
+static int dw_mci_hi3798cv200_remove(struct platform_device *pdev)
+{
+ struct dw_mci *host = platform_get_drvdata(pdev);
+ struct hi3798cv200_priv *priv = host->priv;
+
+ clk_disable_unprepare(priv->drive_clk);
+ clk_disable_unprepare(priv->sample_clk);
+
+ return dw_mci_pltfm_remove(pdev);
+}
+
+static const struct of_device_id dw_mci_hi3798cv200_match[] = {
+ { .compatible = "hisilicon,hi3798cv200-dw-mshc", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, dw_mci_hi3798cv200_match);
+static struct platform_driver dw_mci_hi3798cv200_driver = {
+ .probe = dw_mci_hi3798cv200_probe,
+ .remove = dw_mci_hi3798cv200_remove,
+ .driver = {
+ .name = "dwmmc_hi3798cv200",
+ .of_match_table = dw_mci_hi3798cv200_match,
+ },
+};
+module_platform_driver(dw_mci_hi3798cv200_driver);
+
+MODULE_DESCRIPTION("HiSilicon Hi3798CV200 Specific DW-MSHC Driver Extension");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:dwmmc_hi3798cv200");
diff --git a/drivers/mmc/host/dw_mmc-pci.c b/drivers/mmc/host/dw_mmc-pci.c
index ab8713297edb..3ad07d7b2c97 100644
--- a/drivers/mmc/host/dw_mmc-pci.c
+++ b/drivers/mmc/host/dw_mmc-pci.c
@@ -29,7 +29,6 @@
MMC_CAP_SDIO_IRQ)
static struct dw_mci_board pci_board_data = {
- .num_slots = 1,
.caps = DW_MCI_CAPABILITIES,
.bus_hz = 33 * 1000 * 1000,
.detect_delay_ms = 200,
diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c
index 339295212935..40d7de2eea12 100644
--- a/drivers/mmc/host/dw_mmc-rockchip.c
+++ b/drivers/mmc/host/dw_mmc-rockchip.c
@@ -282,11 +282,11 @@ static int dw_mci_rk3288_parse_dt(struct dw_mci *host)
priv->drv_clk = devm_clk_get(host->dev, "ciu-drive");
if (IS_ERR(priv->drv_clk))
- dev_dbg(host->dev, "ciu_drv not available\n");
+ dev_dbg(host->dev, "ciu-drive not available\n");
priv->sample_clk = devm_clk_get(host->dev, "ciu-sample");
if (IS_ERR(priv->sample_clk))
- dev_dbg(host->dev, "ciu_sample not available\n");
+ dev_dbg(host->dev, "ciu-sample not available\n");
host->priv = priv;
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 06d47414d0c1..29a1afa81f66 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -147,19 +147,7 @@ static int dw_mci_req_show(struct seq_file *s, void *v)
return 0;
}
-
-static int dw_mci_req_open(struct inode *inode, struct file *file)
-{
- return single_open(file, dw_mci_req_show, inode->i_private);
-}
-
-static const struct file_operations dw_mci_req_fops = {
- .owner = THIS_MODULE,
- .open = dw_mci_req_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(dw_mci_req);
static int dw_mci_regs_show(struct seq_file *s, void *v)
{
@@ -178,19 +166,7 @@ static int dw_mci_regs_show(struct seq_file *s, void *v)
return 0;
}
-
-static int dw_mci_regs_open(struct inode *inode, struct file *file)
-{
- return single_open(file, dw_mci_regs_show, inode->i_private);
-}
-
-static const struct file_operations dw_mci_regs_fops = {
- .owner = THIS_MODULE,
- .open = dw_mci_regs_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(dw_mci_regs);
static void dw_mci_init_debugfs(struct dw_mci_slot *slot)
{
@@ -2030,7 +2006,6 @@ static void dw_mci_tasklet_func(unsigned long priv)
set_bit(EVENT_CMD_COMPLETE, &host->completed_events);
err = dw_mci_command_complete(host, cmd);
if (cmd == mrq->sbc && !err) {
- prev_state = state = STATE_SENDING_CMD;
__dw_mci_start_request(host, host->slot,
mrq->cmd);
goto unlock;
@@ -2826,6 +2801,10 @@ static int dw_mci_init_slot_caps(struct dw_mci_slot *slot)
if (host->pdata->caps2)
mmc->caps2 = host->pdata->caps2;
+ mmc->f_min = DW_MCI_FREQ_MIN;
+ if (!mmc->f_max)
+ mmc->f_max = DW_MCI_FREQ_MAX;
+
/* Process SDIO IRQs through the sdio_irq_work. */
if (mmc->caps & MMC_CAP_SDIO_IRQ)
mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
@@ -2838,7 +2817,6 @@ static int dw_mci_init_slot(struct dw_mci *host)
struct mmc_host *mmc;
struct dw_mci_slot *slot;
int ret;
- u32 freq[2];
mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);
if (!mmc)
@@ -2852,16 +2830,6 @@ static int dw_mci_init_slot(struct dw_mci *host)
host->slot = slot;
mmc->ops = &dw_mci_ops;
- if (device_property_read_u32_array(host->dev, "clock-freq-min-max",
- freq, 2)) {
- mmc->f_min = DW_MCI_FREQ_MIN;
- mmc->f_max = DW_MCI_FREQ_MAX;
- } else {
- dev_info(host->dev,
- "'clock-freq-min-max' property was deprecated.\n");
- mmc->f_min = freq[0];
- mmc->f_max = freq[1];
- }
/*if there are external regulators, get them*/
ret = mmc_regulator_get_supply(mmc);
@@ -3160,10 +3128,6 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
return ERR_PTR(-EPROBE_DEFER);
}
- /* find out number of slots supported */
- if (!device_property_read_u32(dev, "num-slots", &pdata->num_slots))
- dev_info(dev, "'num-slots' was deprecated.\n");
-
if (device_property_read_u32(dev, "fifo-depth", &pdata->fifo_depth))
dev_info(dev,
"fifo-depth property not found, using value of FIFOTH register as default\n");
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index 1424bd490dd1..46e9f8ec5398 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -65,8 +65,7 @@ struct dw_mci_dma_slave {
* @fifo_reg: Pointer to MMIO registers for data FIFO
* @sg: Scatterlist entry currently being processed by PIO code, if any.
* @sg_miter: PIO mapping scatterlist iterator.
- * @cur_slot: The slot which is currently using the controller.
- * @mrq: The request currently being processed on @cur_slot,
+ * @mrq: The request currently being processed on @slot,
* or NULL if the controller is idle.
* @cmd: The command currently being sent to the card, or NULL.
* @data: The data currently being transferred, or NULL if no data
@@ -102,7 +101,6 @@ struct dw_mci_dma_slave {
* @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus
* rate and timeout calculations.
* @current_speed: Configured rate of the controller.
- * @num_slots: Number of slots available.
* @fifoth_val: The value of FIFOTH register.
* @verid: Denote Version ID.
* @dev: Device associated with the MMC controller.
@@ -134,17 +132,17 @@ struct dw_mci_dma_slave {
* =======
*
* @lock is a softirq-safe spinlock protecting @queue as well as
+ * @slot, @mrq and @state. These must always be updated
* at the same time while holding @lock.
+ * The @mrq field of struct dw_mci_slot is also protected by @lock,
+ * and must always be written at the same time as the slot is added to
+ * @queue.
*
* @irq_lock is an irq-safe spinlock protecting the INTMASK register
* to allow the interrupt handler to modify it directly. Held for only long
* enough to read-modify-write INTMASK and no other locks are grabbed when
* holding this one.
*
- * The @mrq field of struct dw_mci_slot is also protected by @lock,
- * and must always be written at the same time as the slot is added to
- * @queue.
- *
* @pending_events and @completed_events are accessed using atomic bit
* operations, so they don't need any locking.
*
@@ -253,8 +251,6 @@ struct dma_pdata;
/* Board platform data */
struct dw_mci_board {
- u32 num_slots;
-
unsigned int bus_hz; /* Clock speed at the cclk_in pad */
u32 caps; /* Capabilities */
@@ -318,11 +314,12 @@ struct dw_mci_board {
#define SDMMC_BUFADDR 0x098
#define SDMMC_CDTHRCTL 0x100
#define SDMMC_UHS_REG_EXT 0x108
+#define SDMMC_DDR_REG 0x10c
#define SDMMC_ENABLE_SHIFT 0x110
#define SDMMC_DATA(x) (x)
/*
-* Registers to support idmac 64-bit address mode
-*/
+ * Registers to support idmac 64-bit address mode
+ */
#define SDMMC_DBADDRL 0x088
#define SDMMC_DBADDRU 0x08c
#define SDMMC_IDSTS64 0x090
@@ -443,13 +440,19 @@ struct dw_mci_board {
#define SDMMC_CARD_WR_THR_EN BIT(2)
#define SDMMC_CARD_RD_THR_EN BIT(0)
/* UHS-1 register defines */
+#define SDMMC_UHS_DDR BIT(16)
#define SDMMC_UHS_18V BIT(0)
+/* DDR register defines */
+#define SDMMC_DDR_HS400 BIT(31)
+/* Enable shift register defines */
+#define SDMMC_ENABLE_PHASE BIT(0)
/* All ctrl reset bits */
#define SDMMC_CTRL_ALL_RESET_FLAGS \
(SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET)
/* FIFO register access macros. These should not change the data endian-ness
- * as they are written to memory to be dealt with by the upper layers */
+ * as they are written to memory to be dealt with by the upper layers
+ */
#define mci_fifo_readw(__reg) __raw_readw(__reg)
#define mci_fifo_readl(__reg) __raw_readl(__reg)
#define mci_fifo_readq(__reg) __raw_readq(__reg)
diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c
index 712e08d9a45e..a0168e9e4fce 100644
--- a/drivers/mmc/host/jz4740_mmc.c
+++ b/drivers/mmc/host/jz4740_mmc.c
@@ -362,9 +362,9 @@ static void jz4740_mmc_set_irq_enabled(struct jz4740_mmc_host *host,
host->irq_mask &= ~irq;
else
host->irq_mask |= irq;
- spin_unlock_irqrestore(&host->lock, flags);
writew(host->irq_mask, host->base + JZ_REG_MMC_IMASK);
+ spin_unlock_irqrestore(&host->lock, flags);
}
static void jz4740_mmc_clock_enable(struct jz4740_mmc_host *host,
diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
index 6457a7d8880f..cb274e822293 100644
--- a/drivers/mmc/host/mtk-sd.c
+++ b/drivers/mmc/host/mtk-sd.c
@@ -438,11 +438,23 @@ static const struct mtk_mmc_compatible mt2712_compat = {
.enhance_rx = true,
};
+static const struct mtk_mmc_compatible mt7622_compat = {
+ .clk_div_bits = 12,
+ .hs400_tune = false,
+ .pad_tune_reg = MSDC_PAD_TUNE0,
+ .async_fifo = true,
+ .data_tune = true,
+ .busy_check = true,
+ .stop_clk_fix = true,
+ .enhance_rx = true,
+};
+
static const struct of_device_id msdc_of_ids[] = {
{ .compatible = "mediatek,mt8135-mmc", .data = &mt8135_compat},
{ .compatible = "mediatek,mt8173-mmc", .data = &mt8173_compat},
{ .compatible = "mediatek,mt2701-mmc", .data = &mt2701_compat},
{ .compatible = "mediatek,mt2712-mmc", .data = &mt2712_compat},
+ { .compatible = "mediatek,mt7622-mmc", .data = &mt7622_compat},
{}
};
MODULE_DEVICE_TABLE(of, msdc_of_ids);
diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c
index 80943fa07db6..51e01f03fb99 100644
--- a/drivers/mmc/host/renesas_sdhi_core.c
+++ b/drivers/mmc/host/renesas_sdhi_core.c
@@ -38,7 +38,7 @@
#include "renesas_sdhi.h"
#include "tmio_mmc.h"
-#define EXT_ACC 0xe4
+#define HOST_MODE 0xe4
#define SDHI_VER_GEN2_SDR50 0x490c
#define SDHI_VER_RZ_A1 0x820b
@@ -76,7 +76,7 @@ static void renesas_sdhi_sdbuf_width(struct tmio_mmc_host *host, int width)
return;
}
- sd_ctrl_write16(host, EXT_ACC, val);
+ sd_ctrl_write16(host, HOST_MODE, val);
}
static int renesas_sdhi_clk_enable(struct tmio_mmc_host *host)
@@ -417,7 +417,7 @@ static int renesas_sdhi_write16_hook(struct tmio_mmc_host *host, int addr)
case CTL_SD_MEM_CARD_OPT:
case CTL_TRANSACTION_CTL:
case CTL_DMA_ENABLE:
- case EXT_ACC:
+ case HOST_MODE:
if (host->pdata->flags & TMIO_MMC_HAVE_CBSY)
bit = TMIO_STAT_CMD_BUSY;
/* fallthrough */
diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
index 7c03cfead6f9..6af946d16d24 100644
--- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c
+++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
@@ -9,6 +9,7 @@
* published by the Free Software Foundation.
*/
+#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/io-64-nonatomic-hi-lo.h>
@@ -62,6 +63,17 @@
* need a custom accessor.
*/
+static unsigned long global_flags;
+/*
+ * Workaround for avoiding to use RX DMAC by multiple channels.
+ * On R-Car H3 ES1.* and M3-W ES1.0, when multiple SDHI channels use
+ * RX DMAC simultaneously, sometimes hundreds of bytes data are not
+ * stored into the system memory even if the DMAC interrupt happened.
+ * So, this driver then uses one RX DMAC channel only.
+ */
+#define SDHI_INTERNAL_DMAC_ONE_RX_ONLY 0
+#define SDHI_INTERNAL_DMAC_RX_IN_USE 1
+
/* Definitions for sampling clocks */
static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = {
{
@@ -71,9 +83,8 @@ static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = {
};
static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = {
- .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
- TMIO_MMC_CLK_ACTUAL | TMIO_MMC_HAVE_CBSY |
- TMIO_MMC_MIN_RCAR2,
+ .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
+ TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
MMC_CAP_CMD23,
.bus_shift = 2,
@@ -127,6 +138,9 @@ renesas_sdhi_internal_dmac_abort_dma(struct tmio_mmc_host *host) {
renesas_sdhi_internal_dmac_dm_write(host, DM_CM_RST,
RST_RESERVED_BITS | val);
+ if (host->data && host->data->flags & MMC_DATA_READ)
+ clear_bit(SDHI_INTERNAL_DMAC_RX_IN_USE, &global_flags);
+
renesas_sdhi_internal_dmac_enable_dma(host, true);
}
@@ -145,7 +159,6 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
u32 dtran_mode = DTRAN_MODE_BUS_WID_TH | DTRAN_MODE_ADDR_MODE;
enum dma_data_direction dir;
int ret;
- u32 irq_mask;
/* This DMAC cannot handle if sg_len is not 1 */
WARN_ON(host->sg_len > 1);
@@ -157,11 +170,12 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
if (data->flags & MMC_DATA_READ) {
dtran_mode |= DTRAN_MODE_CH_NUM_CH1;
dir = DMA_FROM_DEVICE;
- irq_mask = TMIO_STAT_RXRDY;
+ if (test_bit(SDHI_INTERNAL_DMAC_ONE_RX_ONLY, &global_flags) &&
+ test_and_set_bit(SDHI_INTERNAL_DMAC_RX_IN_USE, &global_flags))
+ goto force_pio;
} else {
dtran_mode |= DTRAN_MODE_CH_NUM_CH0;
dir = DMA_TO_DEVICE;
- irq_mask = TMIO_STAT_TXRQ;
}
ret = dma_map_sg(&host->pdev->dev, sg, host->sg_len, dir);
@@ -170,9 +184,6 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
renesas_sdhi_internal_dmac_enable_dma(host, true);
- /* disable PIO irqs to avoid "PIO IRQ in DMA mode!" */
- tmio_mmc_disable_mmc_irqs(host, irq_mask);
-
/* set dma parameters */
renesas_sdhi_internal_dmac_dm_write(host, DM_CM_DTRAN_MODE,
dtran_mode);
@@ -215,6 +226,9 @@ static void renesas_sdhi_internal_dmac_complete_tasklet_fn(unsigned long arg)
renesas_sdhi_internal_dmac_enable_dma(host, false);
dma_unmap_sg(&host->pdev->dev, host->sg_ptr, host->sg_len, dir);
+ if (dir == DMA_FROM_DEVICE)
+ clear_bit(SDHI_INTERNAL_DMAC_RX_IN_USE, &global_flags);
+
tmio_mmc_do_data_irq(host);
out:
spin_unlock_irq(&host->lock);
@@ -258,18 +272,24 @@ static const struct tmio_mmc_dma_ops renesas_sdhi_internal_dmac_dma_ops = {
* implementation as others may use a different implementation.
*/
static const struct soc_device_attribute gen3_soc_whitelist[] = {
- { .soc_id = "r8a7795", .revision = "ES1.*" },
- { .soc_id = "r8a7795", .revision = "ES2.0" },
- { .soc_id = "r8a7796", .revision = "ES1.0" },
- { .soc_id = "r8a77995", .revision = "ES1.0" },
- { /* sentinel */ }
+ { .soc_id = "r8a7795", .revision = "ES1.*",
+ .data = (void *)BIT(SDHI_INTERNAL_DMAC_ONE_RX_ONLY) },
+ { .soc_id = "r8a7795", .revision = "ES2.0" },
+ { .soc_id = "r8a7796", .revision = "ES1.0",
+ .data = (void *)BIT(SDHI_INTERNAL_DMAC_ONE_RX_ONLY) },
+ { .soc_id = "r8a77995", .revision = "ES1.0" },
+ { /* sentinel */ }
};
static int renesas_sdhi_internal_dmac_probe(struct platform_device *pdev)
{
- if (!soc_device_match(gen3_soc_whitelist))
+ const struct soc_device_attribute *soc = soc_device_match(gen3_soc_whitelist);
+
+ if (!soc)
return -ENODEV;
+ global_flags |= (unsigned long)soc->data;
+
return renesas_sdhi_probe(pdev, &renesas_sdhi_internal_dmac_dma_ops);
}
diff --git a/drivers/mmc/host/renesas_sdhi_sys_dmac.c b/drivers/mmc/host/renesas_sdhi_sys_dmac.c
index 82d757c480b2..848e50c1638a 100644
--- a/drivers/mmc/host/renesas_sdhi_sys_dmac.c
+++ b/drivers/mmc/host/renesas_sdhi_sys_dmac.c
@@ -40,8 +40,7 @@ static const struct renesas_sdhi_of_data of_rz_compatible = {
};
static const struct renesas_sdhi_of_data of_rcar_gen1_compatible = {
- .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
- TMIO_MMC_CLK_ACTUAL,
+ .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
};
@@ -58,9 +57,8 @@ static struct renesas_sdhi_scc rcar_gen2_scc_taps[] = {
};
static const struct renesas_sdhi_of_data of_rcar_gen2_compatible = {
- .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
- TMIO_MMC_CLK_ACTUAL | TMIO_MMC_HAVE_CBSY |
- TMIO_MMC_MIN_RCAR2,
+ .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
+ TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
MMC_CAP_CMD23,
.dma_buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES,
@@ -79,9 +77,8 @@ static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = {
};
static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = {
- .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
- TMIO_MMC_CLK_ACTUAL | TMIO_MMC_HAVE_CBSY |
- TMIO_MMC_MIN_RCAR2,
+ .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
+ TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
MMC_CAP_CMD23,
.bus_shift = 2,
@@ -205,8 +202,6 @@ static void renesas_sdhi_sys_dmac_start_dma_rx(struct tmio_mmc_host *host)
return;
}
- tmio_mmc_disable_mmc_irqs(host, TMIO_STAT_RXRDY);
-
/* The only sg element can be unaligned, use our bounce buffer then */
if (!aligned) {
sg_init_one(&host->bounce_sg, host->bounce_buf, sg->length);
@@ -280,8 +275,6 @@ static void renesas_sdhi_sys_dmac_start_dma_tx(struct tmio_mmc_host *host)
return;
}
- tmio_mmc_disable_mmc_irqs(host, TMIO_STAT_TXRQ);
-
/* The only sg element can be unaligned, use our bounce buffer then */
if (!aligned) {
unsigned long flags;
diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c
index 61666d269771..0ef741bc515d 100644
--- a/drivers/mmc/host/sdhci-iproc.c
+++ b/drivers/mmc/host/sdhci-iproc.c
@@ -214,6 +214,7 @@ static const struct sdhci_pltfm_data sdhci_bcm2835_pltfm_data = {
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
SDHCI_QUIRK_MISSING_CAPS |
SDHCI_QUIRK_NO_HISPD_BIT,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
.ops = &sdhci_iproc_32only_ops,
};
diff --git a/drivers/mmc/host/sdhci-omap.c b/drivers/mmc/host/sdhci-omap.c
index 628bfe9a3d17..1456abd5eeb9 100644
--- a/drivers/mmc/host/sdhci-omap.c
+++ b/drivers/mmc/host/sdhci-omap.c
@@ -25,17 +25,32 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
+#include <linux/pinctrl/consumer.h>
#include "sdhci-pltfm.h"
#define SDHCI_OMAP_CON 0x12c
#define CON_DW8 BIT(5)
#define CON_DMA_MASTER BIT(20)
+#define CON_DDR BIT(19)
+#define CON_CLKEXTFREE BIT(16)
+#define CON_PADEN BIT(15)
#define CON_INIT BIT(1)
#define CON_OD BIT(0)
+#define SDHCI_OMAP_DLL 0x0134
+#define DLL_SWT BIT(20)
+#define DLL_FORCE_SR_C_SHIFT 13
+#define DLL_FORCE_SR_C_MASK (0x7f << DLL_FORCE_SR_C_SHIFT)
+#define DLL_FORCE_VALUE BIT(12)
+#define DLL_CALIB BIT(1)
+
#define SDHCI_OMAP_CMD 0x20c
+#define SDHCI_OMAP_PSTATE 0x0224
+#define PSTATE_DLEV_DAT0 BIT(20)
+#define PSTATE_DATI BIT(1)
+
#define SDHCI_OMAP_HCTL 0x228
#define HCTL_SDBP BIT(8)
#define HCTL_SDVS_SHIFT 9
@@ -56,12 +71,16 @@
#define SDHCI_OMAP_AC12 0x23c
#define AC12_V1V8_SIGEN BIT(19)
+#define AC12_SCLK_SEL BIT(23)
#define SDHCI_OMAP_CAPA 0x240
#define CAPA_VS33 BIT(24)
#define CAPA_VS30 BIT(25)
#define CAPA_VS18 BIT(26)
+#define SDHCI_OMAP_CAPA2 0x0244
+#define CAPA2_TSDR50 BIT(13)
+
#define SDHCI_OMAP_TIMEOUT 1 /* 1 msec */
#define SYSCTL_CLKD_MAX 0x3FF
@@ -70,8 +89,14 @@
#define IOV_3V0 3000000 /* 300000 uV */
#define IOV_3V3 3300000 /* 330000 uV */
+#define MAX_PHASE_DELAY 0x7C
+
+/* sdhci-omap controller flags */
+#define SDHCI_OMAP_REQUIRE_IODELAY BIT(0)
+
struct sdhci_omap_data {
u32 offset;
+ u8 flags;
};
struct sdhci_omap_host {
@@ -82,8 +107,16 @@ struct sdhci_omap_host {
struct sdhci_host *host;
u8 bus_mode;
u8 power_mode;
+ u8 timing;
+ u8 flags;
+
+ struct pinctrl *pinctrl;
+ struct pinctrl_state **pinctrl_state;
};
+static void sdhci_omap_start_clock(struct sdhci_omap_host *omap_host);
+static void sdhci_omap_stop_clock(struct sdhci_omap_host *omap_host);
+
static inline u32 sdhci_omap_readl(struct sdhci_omap_host *host,
unsigned int offset)
{
@@ -191,6 +224,178 @@ static void sdhci_omap_conf_bus_power(struct sdhci_omap_host *omap_host,
}
}
+static inline void sdhci_omap_set_dll(struct sdhci_omap_host *omap_host,
+ int count)
+{
+ int i;
+ u32 reg;
+
+ reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL);
+ reg |= DLL_FORCE_VALUE;
+ reg &= ~DLL_FORCE_SR_C_MASK;
+ reg |= (count << DLL_FORCE_SR_C_SHIFT);
+ sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
+
+ reg |= DLL_CALIB;
+ sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
+ for (i = 0; i < 1000; i++) {
+ reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL);
+ if (reg & DLL_CALIB)
+ break;
+ }
+ reg &= ~DLL_CALIB;
+ sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
+}
+
+static void sdhci_omap_disable_tuning(struct sdhci_omap_host *omap_host)
+{
+ u32 reg;
+
+ reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
+ reg &= ~AC12_SCLK_SEL;
+ sdhci_omap_writel(omap_host, SDHCI_OMAP_AC12, reg);
+
+ reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL);
+ reg &= ~(DLL_FORCE_VALUE | DLL_SWT);
+ sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
+}
+
+static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
+ struct device *dev = omap_host->dev;
+ struct mmc_ios *ios = &mmc->ios;
+ u32 start_window = 0, max_window = 0;
+ u8 cur_match, prev_match = 0;
+ u32 length = 0, max_len = 0;
+ u32 ier = host->ier;
+ u32 phase_delay = 0;
+ int ret = 0;
+ u32 reg;
+
+ pltfm_host = sdhci_priv(host);
+ omap_host = sdhci_pltfm_priv(pltfm_host);
+ dev = omap_host->dev;
+
+ /* clock tuning is not needed for upto 52MHz */
+ if (ios->clock <= 52000000)
+ return 0;
+
+ reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA2);
+ if (ios->timing == MMC_TIMING_UHS_SDR50 && !(reg & CAPA2_TSDR50))
+ return 0;
+
+ reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL);
+ reg |= DLL_SWT;
+ sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
+
+ /*
+ * OMAP5/DRA74X/DRA72x Errata i802:
+ * DCRC error interrupts (MMCHS_STAT[21] DCRC=0x1) can occur
+ * during the tuning procedure. So disable it during the
+ * tuning procedure.
+ */
+ ier &= ~SDHCI_INT_DATA_CRC;
+ sdhci_writel(host, ier, SDHCI_INT_ENABLE);
+ sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE);
+
+ while (phase_delay <= MAX_PHASE_DELAY) {
+ sdhci_omap_set_dll(omap_host, phase_delay);
+
+ cur_match = !mmc_send_tuning(mmc, opcode, NULL);
+ if (cur_match) {
+ if (prev_match) {
+ length++;
+ } else {
+ start_window = phase_delay;
+ length = 1;
+ }
+ }
+
+ if (length > max_len) {
+ max_window = start_window;
+ max_len = length;
+ }
+
+ prev_match = cur_match;
+ phase_delay += 4;
+ }
+
+ if (!max_len) {
+ dev_err(dev, "Unable to find match\n");
+ ret = -EIO;
+ goto tuning_error;
+ }
+
+ reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
+ if (!(reg & AC12_SCLK_SEL)) {
+ ret = -EIO;
+ goto tuning_error;
+ }
+
+ phase_delay = max_window + 4 * (max_len >> 1);
+ sdhci_omap_set_dll(omap_host, phase_delay);
+
+ goto ret;
+
+tuning_error:
+ dev_err(dev, "Tuning failed\n");
+ sdhci_omap_disable_tuning(omap_host);
+
+ret:
+ sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+ return ret;
+}
+
+static int sdhci_omap_card_busy(struct mmc_host *mmc)
+{
+ u32 reg, ac12;
+ int ret = false;
+ struct sdhci_host *host = mmc_priv(mmc);
+ struct sdhci_pltfm_host *pltfm_host;
+ struct sdhci_omap_host *omap_host;
+ u32 ier = host->ier;
+
+ pltfm_host = sdhci_priv(host);
+ omap_host = sdhci_pltfm_priv(pltfm_host);
+
+ reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
+ ac12 = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
+ reg &= ~CON_CLKEXTFREE;
+ if (ac12 & AC12_V1V8_SIGEN)
+ reg |= CON_CLKEXTFREE;
+ reg |= CON_PADEN;
+ sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
+
+ disable_irq(host->irq);
+ ier |= SDHCI_INT_CARD_INT;
+ sdhci_writel(host, ier, SDHCI_INT_ENABLE);
+ sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE);
+
+ /*
+ * Delay is required for PSTATE to correctly reflect
+ * DLEV/CLEV values after PADEN is set.
+ */
+ usleep_range(50, 100);
+ reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_PSTATE);
+ if ((reg & PSTATE_DATI) || !(reg & PSTATE_DLEV_DAT0))
+ ret = true;
+
+ reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
+ reg &= ~(CON_CLKEXTFREE | CON_PADEN);
+ sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
+
+ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+ enable_irq(host->irq);
+
+ return ret;
+}
+
static int sdhci_omap_start_signal_voltage_switch(struct mmc_host *mmc,
struct mmc_ios *ios)
{
@@ -244,6 +449,39 @@ static int sdhci_omap_start_signal_voltage_switch(struct mmc_host *mmc,
return 0;
}
+static void sdhci_omap_set_timing(struct sdhci_omap_host *omap_host, u8 timing)
+{
+ int ret;
+ struct pinctrl_state *pinctrl_state;
+ struct device *dev = omap_host->dev;
+
+ if (!(omap_host->flags & SDHCI_OMAP_REQUIRE_IODELAY))
+ return;
+
+ if (omap_host->timing == timing)
+ return;
+
+ sdhci_omap_stop_clock(omap_host);
+
+ pinctrl_state = omap_host->pinctrl_state[timing];
+ ret = pinctrl_select_state(omap_host->pinctrl, pinctrl_state);
+ if (ret) {
+ dev_err(dev, "failed to select pinctrl state\n");
+ return;
+ }
+
+ sdhci_omap_start_clock(omap_host);
+ omap_host->timing = timing;
+}
+
+static void sdhci_omap_set_power_mode(struct sdhci_omap_host *omap_host,
+ u8 power_mode)
+{
+ if (omap_host->bus_mode == MMC_POWER_OFF)
+ sdhci_omap_disable_tuning(omap_host);
+ omap_host->power_mode = power_mode;
+}
+
static void sdhci_omap_set_bus_mode(struct sdhci_omap_host *omap_host,
unsigned int mode)
{
@@ -272,7 +510,9 @@ static void sdhci_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
omap_host = sdhci_pltfm_priv(pltfm_host);
sdhci_omap_set_bus_mode(omap_host, ios->bus_mode);
+ sdhci_omap_set_timing(omap_host, ios->timing);
sdhci_set_ios(mmc, ios);
+ sdhci_omap_set_power_mode(omap_host, ios->power_mode);
}
static u16 sdhci_omap_calc_divisor(struct sdhci_pltfm_host *host,
@@ -401,8 +641,26 @@ static void sdhci_omap_init_74_clocks(struct sdhci_host *host, u8 power_mode)
sdhci_omap_writel(omap_host, SDHCI_OMAP_STAT, INT_CC_EN);
enable_irq(host->irq);
+}
- omap_host->power_mode = power_mode;
+static void sdhci_omap_set_uhs_signaling(struct sdhci_host *host,
+ unsigned int timing)
+{
+ u32 reg;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
+
+ sdhci_omap_stop_clock(omap_host);
+
+ reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
+ if (timing == MMC_TIMING_UHS_DDR50 || timing == MMC_TIMING_MMC_DDR52)
+ reg |= CON_DDR;
+ else
+ reg &= ~CON_DDR;
+ sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
+
+ sdhci_set_uhs_signaling(host, timing);
+ sdhci_omap_start_clock(omap_host);
}
static struct sdhci_ops sdhci_omap_ops = {
@@ -414,7 +672,7 @@ static struct sdhci_ops sdhci_omap_ops = {
.set_bus_width = sdhci_omap_set_bus_width,
.platform_send_init_74_clocks = sdhci_omap_init_74_clocks,
.reset = sdhci_reset,
- .set_uhs_signaling = sdhci_set_uhs_signaling,
+ .set_uhs_signaling = sdhci_omap_set_uhs_signaling,
};
static int sdhci_omap_set_capabilities(struct sdhci_omap_host *omap_host)
@@ -453,14 +711,15 @@ static const struct sdhci_pltfm_data sdhci_omap_pdata = {
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
SDHCI_QUIRK_NO_HISPD_BIT |
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
- .quirks2 = SDHCI_QUIRK2_NO_1_8_V |
- SDHCI_QUIRK2_ACMD23_BROKEN |
+ .quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN |
+ SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
SDHCI_QUIRK2_RSP_136_HAS_CRC,
.ops = &sdhci_omap_ops,
};
static const struct sdhci_omap_data dra7_data = {
.offset = 0x200,
+ .flags = SDHCI_OMAP_REQUIRE_IODELAY,
};
static const struct of_device_id omap_sdhci_match[] = {
@@ -469,6 +728,108 @@ static const struct of_device_id omap_sdhci_match[] = {
};
MODULE_DEVICE_TABLE(of, omap_sdhci_match);
+static struct pinctrl_state
+*sdhci_omap_iodelay_pinctrl_state(struct sdhci_omap_host *omap_host, char *mode,
+ u32 *caps, u32 capmask)
+{
+ struct device *dev = omap_host->dev;
+ struct pinctrl_state *pinctrl_state = ERR_PTR(-ENODEV);
+
+ if (!(*caps & capmask))
+ goto ret;
+
+ pinctrl_state = pinctrl_lookup_state(omap_host->pinctrl, mode);
+ if (IS_ERR(pinctrl_state)) {
+ dev_err(dev, "no pinctrl state for %s mode", mode);
+ *caps &= ~capmask;
+ }
+
+ret:
+ return pinctrl_state;
+}
+
+static int sdhci_omap_config_iodelay_pinctrl_state(struct sdhci_omap_host
+ *omap_host)
+{
+ struct device *dev = omap_host->dev;
+ struct sdhci_host *host = omap_host->host;
+ struct mmc_host *mmc = host->mmc;
+ u32 *caps = &mmc->caps;
+ u32 *caps2 = &mmc->caps2;
+ struct pinctrl_state *state;
+ struct pinctrl_state **pinctrl_state;
+
+ if (!(omap_host->flags & SDHCI_OMAP_REQUIRE_IODELAY))
+ return 0;
+
+ pinctrl_state = devm_kzalloc(dev, sizeof(*pinctrl_state) *
+ (MMC_TIMING_MMC_HS200 + 1), GFP_KERNEL);
+ if (!pinctrl_state)
+ return -ENOMEM;
+
+ omap_host->pinctrl = devm_pinctrl_get(omap_host->dev);
+ if (IS_ERR(omap_host->pinctrl)) {
+ dev_err(dev, "Cannot get pinctrl\n");
+ return PTR_ERR(omap_host->pinctrl);
+ }
+
+ state = pinctrl_lookup_state(omap_host->pinctrl, "default");
+ if (IS_ERR(state)) {
+ dev_err(dev, "no pinctrl state for default mode\n");
+ return PTR_ERR(state);
+ }
+ pinctrl_state[MMC_TIMING_LEGACY] = state;
+
+ state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr104", caps,
+ MMC_CAP_UHS_SDR104);
+ if (!IS_ERR(state))
+ pinctrl_state[MMC_TIMING_UHS_SDR104] = state;
+
+ state = sdhci_omap_iodelay_pinctrl_state(omap_host, "ddr50", caps,
+ MMC_CAP_UHS_DDR50);
+ if (!IS_ERR(state))
+ pinctrl_state[MMC_TIMING_UHS_DDR50] = state;
+
+ state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr50", caps,
+ MMC_CAP_UHS_SDR50);
+ if (!IS_ERR(state))
+ pinctrl_state[MMC_TIMING_UHS_SDR50] = state;
+
+ state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr25", caps,
+ MMC_CAP_UHS_SDR25);
+ if (!IS_ERR(state))
+ pinctrl_state[MMC_TIMING_UHS_SDR25] = state;
+
+ state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr12", caps,
+ MMC_CAP_UHS_SDR12);
+ if (!IS_ERR(state))
+ pinctrl_state[MMC_TIMING_UHS_SDR12] = state;
+
+ state = sdhci_omap_iodelay_pinctrl_state(omap_host, "ddr_1_8v", caps,
+ MMC_CAP_1_8V_DDR);
+ if (!IS_ERR(state))
+ pinctrl_state[MMC_TIMING_MMC_DDR52] = state;
+
+ state = sdhci_omap_iodelay_pinctrl_state(omap_host, "hs", caps,
+ MMC_CAP_SD_HIGHSPEED);
+ if (!IS_ERR(state))
+ pinctrl_state[MMC_TIMING_SD_HS] = state;
+
+ state = sdhci_omap_iodelay_pinctrl_state(omap_host, "hs", caps,
+ MMC_CAP_MMC_HIGHSPEED);
+ if (!IS_ERR(state))
+ pinctrl_state[MMC_TIMING_MMC_HS] = state;
+
+ state = sdhci_omap_iodelay_pinctrl_state(omap_host, "hs200_1_8v", caps2,
+ MMC_CAP2_HS200_1_8V_SDR);
+ if (!IS_ERR(state))
+ pinctrl_state[MMC_TIMING_MMC_HS200] = state;
+
+ omap_host->pinctrl_state = pinctrl_state;
+
+ return 0;
+}
+
static int sdhci_omap_probe(struct platform_device *pdev)
{
int ret;
@@ -504,6 +865,9 @@ static int sdhci_omap_probe(struct platform_device *pdev)
omap_host->host = host;
omap_host->base = host->ioaddr;
omap_host->dev = dev;
+ omap_host->power_mode = MMC_POWER_UNDEFINED;
+ omap_host->timing = MMC_TIMING_LEGACY;
+ omap_host->flags = data->flags;
host->ioaddr += offset;
mmc = host->mmc;
@@ -552,10 +916,16 @@ static int sdhci_omap_probe(struct platform_device *pdev)
goto err_put_sync;
}
+ ret = sdhci_omap_config_iodelay_pinctrl_state(omap_host);
+ if (ret)
+ goto err_put_sync;
+
host->mmc_host_ops.get_ro = mmc_gpio_get_ro;
host->mmc_host_ops.start_signal_voltage_switch =
sdhci_omap_start_signal_voltage_switch;
host->mmc_host_ops.set_ios = sdhci_omap_set_ios;
+ host->mmc_host_ops.card_busy = sdhci_omap_card_busy;
+ host->mmc_host_ops.execute_tuning = sdhci_omap_execute_tuning;
sdhci_read_caps(host);
host->caps |= SDHCI_CAN_DO_ADMA2;
diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
index 82c4f05f91d8..78c25ad35fd2 100644
--- a/drivers/mmc/host/sdhci-pci-core.c
+++ b/drivers/mmc/host/sdhci-pci-core.c
@@ -41,18 +41,25 @@ static void sdhci_pci_hw_reset(struct sdhci_host *host);
static int sdhci_pci_init_wakeup(struct sdhci_pci_chip *chip)
{
mmc_pm_flag_t pm_flags = 0;
+ bool cap_cd_wake = false;
int i;
for (i = 0; i < chip->num_slots; i++) {
struct sdhci_pci_slot *slot = chip->slots[i];
- if (slot)
+ if (slot) {
pm_flags |= slot->host->mmc->pm_flags;
+ if (slot->host->mmc->caps & MMC_CAP_CD_WAKE)
+ cap_cd_wake = true;
+ }
}
- return device_set_wakeup_enable(&chip->pdev->dev,
- (pm_flags & MMC_PM_KEEP_POWER) &&
- (pm_flags & MMC_PM_WAKE_SDIO_IRQ));
+ if ((pm_flags & MMC_PM_KEEP_POWER) && (pm_flags & MMC_PM_WAKE_SDIO_IRQ))
+ return device_wakeup_enable(&chip->pdev->dev);
+ else if (!cap_cd_wake)
+ return device_wakeup_disable(&chip->pdev->dev);
+
+ return 0;
}
static int sdhci_pci_suspend_host(struct sdhci_pci_chip *chip)
@@ -76,6 +83,9 @@ static int sdhci_pci_suspend_host(struct sdhci_pci_chip *chip)
ret = sdhci_suspend_host(host);
if (ret)
goto err_pci_suspend;
+
+ if (device_may_wakeup(&chip->pdev->dev))
+ mmc_gpio_set_cd_wake(host->mmc, true);
}
return 0;
@@ -99,6 +109,8 @@ int sdhci_pci_resume_host(struct sdhci_pci_chip *chip)
ret = sdhci_resume_host(slot->host);
if (ret)
return ret;
+
+ mmc_gpio_set_cd_wake(slot->host->mmc, false);
}
return 0;
@@ -712,26 +724,8 @@ static int glk_emmc_probe_slot(struct sdhci_pci_slot *slot)
return ret;
}
-static void glk_cqe_enable(struct mmc_host *mmc)
-{
- struct sdhci_host *host = mmc_priv(mmc);
- u32 reg;
-
- /*
- * CQE gets stuck if it sees Buffer Read Enable bit set, which can be
- * the case after tuning, so ensure the buffer is drained.
- */
- reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
- while (reg & SDHCI_DATA_AVAILABLE) {
- sdhci_readl(host, SDHCI_BUFFER);
- reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
- }
-
- sdhci_cqe_enable(mmc);
-}
-
static const struct cqhci_host_ops glk_cqhci_ops = {
- .enable = glk_cqe_enable,
+ .enable = sdhci_cqe_enable,
.disable = sdhci_cqe_disable,
.dumpregs = sdhci_pci_dumpregs,
};
@@ -1318,7 +1312,7 @@ static void amd_enable_manual_tuning(struct pci_dev *pdev)
pci_write_config_dword(pdev, AMD_SD_MISC_CONTROL, val);
}
-static int amd_execute_tuning(struct sdhci_host *host, u32 opcode)
+static int amd_execute_tuning_hs200(struct sdhci_host *host, u32 opcode)
{
struct sdhci_pci_slot *slot = sdhci_priv(host);
struct pci_dev *pdev = slot->chip->pdev;
@@ -1357,6 +1351,27 @@ static int amd_execute_tuning(struct sdhci_host *host, u32 opcode)
return 0;
}
+static int amd_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ /* AMD requires custom HS200 tuning */
+ if (host->timing == MMC_TIMING_MMC_HS200)
+ return amd_execute_tuning_hs200(host, opcode);
+
+ /* Otherwise perform standard SDHCI tuning */
+ return sdhci_execute_tuning(mmc, opcode);
+}
+
+static int amd_probe_slot(struct sdhci_pci_slot *slot)
+{
+ struct mmc_host_ops *ops = &slot->host->mmc_host_ops;
+
+ ops->execute_tuning = amd_execute_tuning;
+
+ return 0;
+}
+
static int amd_probe(struct sdhci_pci_chip *chip)
{
struct pci_dev *smbus_dev;
@@ -1391,12 +1406,12 @@ static const struct sdhci_ops amd_sdhci_pci_ops = {
.set_bus_width = sdhci_set_bus_width,
.reset = sdhci_reset,
.set_uhs_signaling = sdhci_set_uhs_signaling,
- .platform_execute_tuning = amd_execute_tuning,
};
static const struct sdhci_pci_fixes sdhci_amd = {
.probe = amd_probe,
.ops = &amd_sdhci_pci_ops,
+ .probe_slot = amd_probe_slot,
};
static const struct pci_device_id pci_ids[] = {
@@ -1716,6 +1731,9 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
if (device_can_wakeup(&pdev->dev))
host->mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
+ if (host->mmc->caps & MMC_CAP_CD_WAKE)
+ device_init_wakeup(&pdev->dev, true);
+
if (slot->cd_idx >= 0) {
ret = mmc_gpiod_request_cd(host->mmc, NULL, slot->cd_idx,
slot->cd_override_level, 0, NULL);
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 2020e57ffa7e..2ededa7f43df 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2899,6 +2899,14 @@ static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
\*****************************************************************************/
#ifdef CONFIG_PM
+
+static bool sdhci_cd_irq_can_wakeup(struct sdhci_host *host)
+{
+ return mmc_card_is_removable(host->mmc) &&
+ !(host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
+ !mmc_can_gpio_cd(host->mmc);
+}
+
/*
* To enable wakeup events, the corresponding events have to be enabled in
* the Interrupt Status Enable register too. See 'Table 1-6: Wakeup Signal
@@ -2915,13 +2923,18 @@ static bool sdhci_enable_irq_wakeups(struct sdhci_host *host)
u8 wake_val = 0;
u8 val;
- if (!(host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)) {
+ if (sdhci_cd_irq_can_wakeup(host)) {
wake_val |= SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE;
irq_val |= SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE;
}
- wake_val |= SDHCI_WAKE_ON_INT;
- irq_val |= SDHCI_INT_CARD_INT;
+ if (mmc_card_wake_sdio_irq(host->mmc)) {
+ wake_val |= SDHCI_WAKE_ON_INT;
+ irq_val |= SDHCI_INT_CARD_INT;
+ }
+
+ if (!irq_val)
+ return false;
val = sdhci_readb(host, SDHCI_WAKE_UP_CONTROL);
val &= ~mask;
diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c
index 7bb00c68a756..4c2a1f8ddbf3 100644
--- a/drivers/mmc/host/sh_mmcif.c
+++ b/drivers/mmc/host/sh_mmcif.c
@@ -7,13 +7,6 @@
* 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.
- *
- *
- * TODO
- * 1. DMA
- * 2. Power management
- * 3. Handle MMC errors better
- *
*/
/*
@@ -67,7 +60,6 @@
#include <linux/module.h>
#define DRIVER_NAME "sh_mmcif"
-#define DRIVER_VERSION "2010-04-28"
/* CE_CMD_SET */
#define CMD_MASK 0x3f000000
diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c
index bad612d6f879..20cfb20418f3 100644
--- a/drivers/mmc/host/sunxi-mmc.c
+++ b/drivers/mmc/host/sunxi-mmc.c
@@ -268,6 +268,7 @@ struct sunxi_mmc_cfg {
};
struct sunxi_mmc_host {
+ struct device *dev;
struct mmc_host *mmc;
struct reset_control *reset;
const struct sunxi_mmc_cfg *cfg;
@@ -1165,6 +1166,80 @@ static const struct of_device_id sunxi_mmc_of_match[] = {
};
MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match);
+static int sunxi_mmc_enable(struct sunxi_mmc_host *host)
+{
+ int ret;
+
+ if (!IS_ERR(host->reset)) {
+ ret = reset_control_reset(host->reset);
+ if (ret) {
+ dev_err(host->dev, "Couldn't reset the MMC controller (%d)\n",
+ ret);
+ return ret;
+ }
+ }
+
+ ret = clk_prepare_enable(host->clk_ahb);
+ if (ret) {
+ dev_err(host->dev, "Couldn't enable the bus clocks (%d)\n", ret);
+ goto error_assert_reset;
+ }
+
+ ret = clk_prepare_enable(host->clk_mmc);
+ if (ret) {
+ dev_err(host->dev, "Enable mmc clk err %d\n", ret);
+ goto error_disable_clk_ahb;
+ }
+
+ ret = clk_prepare_enable(host->clk_output);
+ if (ret) {
+ dev_err(host->dev, "Enable output clk err %d\n", ret);
+ goto error_disable_clk_mmc;
+ }
+
+ ret = clk_prepare_enable(host->clk_sample);
+ if (ret) {
+ dev_err(host->dev, "Enable sample clk err %d\n", ret);
+ goto error_disable_clk_output;
+ }
+
+ /*
+ * Sometimes the controller asserts the irq on boot for some reason,
+ * make sure the controller is in a sane state before enabling irqs.
+ */
+ ret = sunxi_mmc_reset_host(host);
+ if (ret)
+ goto error_disable_clk_sample;
+
+ return 0;
+
+error_disable_clk_sample:
+ clk_disable_unprepare(host->clk_sample);
+error_disable_clk_output:
+ clk_disable_unprepare(host->clk_output);
+error_disable_clk_mmc:
+ clk_disable_unprepare(host->clk_mmc);
+error_disable_clk_ahb:
+ clk_disable_unprepare(host->clk_ahb);
+error_assert_reset:
+ if (!IS_ERR(host->reset))
+ reset_control_assert(host->reset);
+ return ret;
+}
+
+static void sunxi_mmc_disable(struct sunxi_mmc_host *host)
+{
+ sunxi_mmc_reset_host(host);
+
+ clk_disable_unprepare(host->clk_sample);
+ clk_disable_unprepare(host->clk_output);
+ clk_disable_unprepare(host->clk_mmc);
+ clk_disable_unprepare(host->clk_ahb);
+
+ if (!IS_ERR(host->reset))
+ reset_control_assert(host->reset);
+}
+
static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host,
struct platform_device *pdev)
{
@@ -1214,66 +1289,21 @@ static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host,
if (PTR_ERR(host->reset) == -EPROBE_DEFER)
return PTR_ERR(host->reset);
- ret = clk_prepare_enable(host->clk_ahb);
- if (ret) {
- dev_err(&pdev->dev, "Enable ahb clk err %d\n", ret);
- return ret;
- }
-
- ret = clk_prepare_enable(host->clk_mmc);
- if (ret) {
- dev_err(&pdev->dev, "Enable mmc clk err %d\n", ret);
- goto error_disable_clk_ahb;
- }
-
- ret = clk_prepare_enable(host->clk_output);
- if (ret) {
- dev_err(&pdev->dev, "Enable output clk err %d\n", ret);
- goto error_disable_clk_mmc;
- }
-
- ret = clk_prepare_enable(host->clk_sample);
- if (ret) {
- dev_err(&pdev->dev, "Enable sample clk err %d\n", ret);
- goto error_disable_clk_output;
- }
-
- if (!IS_ERR(host->reset)) {
- ret = reset_control_reset(host->reset);
- if (ret) {
- dev_err(&pdev->dev, "reset err %d\n", ret);
- goto error_disable_clk_sample;
- }
- }
-
- /*
- * Sometimes the controller asserts the irq on boot for some reason,
- * make sure the controller is in a sane state before enabling irqs.
- */
- ret = sunxi_mmc_reset_host(host);
+ ret = sunxi_mmc_enable(host);
if (ret)
- goto error_assert_reset;
+ return ret;
host->irq = platform_get_irq(pdev, 0);
if (host->irq <= 0) {
ret = -EINVAL;
- goto error_assert_reset;
+ goto error_disable_mmc;
}
return devm_request_threaded_irq(&pdev->dev, host->irq, sunxi_mmc_irq,
sunxi_mmc_handle_manual_stop, 0, "sunxi-mmc", host);
-error_assert_reset:
- if (!IS_ERR(host->reset))
- reset_control_assert(host->reset);
-error_disable_clk_sample:
- clk_disable_unprepare(host->clk_sample);
-error_disable_clk_output:
- clk_disable_unprepare(host->clk_output);
-error_disable_clk_mmc:
- clk_disable_unprepare(host->clk_mmc);
-error_disable_clk_ahb:
- clk_disable_unprepare(host->clk_ahb);
+error_disable_mmc:
+ sunxi_mmc_disable(host);
return ret;
}
@@ -1288,8 +1318,10 @@ static int sunxi_mmc_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "mmc alloc host failed\n");
return -ENOMEM;
}
+ platform_set_drvdata(pdev, mmc);
host = mmc_priv(mmc);
+ host->dev = &pdev->dev;
host->mmc = mmc;
spin_lock_init(&host->lock);
@@ -1353,7 +1385,6 @@ static int sunxi_mmc_probe(struct platform_device *pdev)
goto error_free_dma;
dev_info(&pdev->dev, "base:0x%p irq:%u\n", host->reg_base, host->irq);
- platform_set_drvdata(pdev, mmc);
return 0;
error_free_dma:
@@ -1370,16 +1401,7 @@ static int sunxi_mmc_remove(struct platform_device *pdev)
mmc_remove_host(mmc);
disable_irq(host->irq);
- sunxi_mmc_reset_host(host);
-
- if (!IS_ERR(host->reset))
- reset_control_assert(host->reset);
-
- clk_disable_unprepare(host->clk_sample);
- clk_disable_unprepare(host->clk_output);
- clk_disable_unprepare(host->clk_mmc);
- clk_disable_unprepare(host->clk_ahb);
-
+ sunxi_mmc_disable(host);
dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
mmc_free_host(mmc);
diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c
index 33494241245a..308029930304 100644
--- a/drivers/mmc/host/tmio_mmc_core.c
+++ b/drivers/mmc/host/tmio_mmc_core.c
@@ -278,7 +278,6 @@ static void tmio_mmc_reset_work(struct work_struct *work)
host->cmd = NULL;
host->data = NULL;
- host->force_pio = false;
spin_unlock_irqrestore(&host->lock, flags);
@@ -350,8 +349,6 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host,
c |= TRANSFER_READ;
}
- if (!host->native_hotplug)
- irq_mask &= ~(TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT);
tmio_mmc_enable_mmc_irqs(host, irq_mask);
/* Fire off the command */
@@ -623,15 +620,21 @@ static void tmio_mmc_cmd_irq(struct tmio_mmc_host *host, unsigned int stat)
*/
if (host->data && (!cmd->error || cmd->error == -EILSEQ)) {
if (host->data->flags & MMC_DATA_READ) {
- if (host->force_pio || !host->chan_rx)
+ if (host->force_pio || !host->chan_rx) {
tmio_mmc_enable_mmc_irqs(host, TMIO_MASK_READOP);
- else
+ } else {
+ tmio_mmc_disable_mmc_irqs(host,
+ TMIO_MASK_READOP);
tasklet_schedule(&host->dma_issue);
+ }
} else {
- if (host->force_pio || !host->chan_tx)
+ if (host->force_pio || !host->chan_tx) {
tmio_mmc_enable_mmc_irqs(host, TMIO_MASK_WRITEOP);
- else
+ } else {
+ tmio_mmc_disable_mmc_irqs(host,
+ TMIO_MASK_WRITEOP);
tasklet_schedule(&host->dma_issue);
+ }
}
} else {
schedule_work(&host->done);
@@ -755,6 +758,7 @@ static int tmio_mmc_start_data(struct tmio_mmc_host *host,
tmio_mmc_init_sg(host, data);
host->data = data;
+ host->force_pio = false;
/* Set transfer length / blocksize */
sd_ctrl_write16(host, CTL_SD_XFER_LEN, data->blksz);
@@ -846,7 +850,6 @@ static void tmio_process_mrq(struct tmio_mmc_host *host,
return;
fail:
- host->force_pio = false;
host->mrq = NULL;
mrq->cmd->error = ret;
mmc_request_done(host->mmc, mrq);
@@ -896,7 +899,6 @@ static void tmio_mmc_finish_request(struct tmio_mmc_host *host)
if (host->cmd != mrq->sbc) {
host->cmd = NULL;
host->data = NULL;
- host->force_pio = false;
host->mrq = NULL;
}
@@ -911,7 +913,7 @@ static void tmio_mmc_finish_request(struct tmio_mmc_host *host)
host->check_scc_error(host);
/* If SET_BLOCK_COUNT, continue with main command */
- if (host->mrq) {
+ if (host->mrq && !mrq->cmd->error) {
tmio_process_mrq(host, mrq);
return;
}
@@ -1061,10 +1063,17 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
static int tmio_mmc_get_ro(struct mmc_host *mmc)
{
struct tmio_mmc_host *host = mmc_priv(mmc);
- struct tmio_mmc_data *pdata = host->pdata;
- return !((pdata->flags & TMIO_MMC_WRPROTECT_DISABLE) ||
- (sd_ctrl_read16_and_16_as_32(host, CTL_STATUS) & TMIO_STAT_WRPROTECT));
+ return !(sd_ctrl_read16_and_16_as_32(host, CTL_STATUS) &
+ TMIO_STAT_WRPROTECT);
+}
+
+static int tmio_mmc_get_cd(struct mmc_host *mmc)
+{
+ struct tmio_mmc_host *host = mmc_priv(mmc);
+
+ return !!(sd_ctrl_read16_and_16_as_32(host, CTL_STATUS) &
+ TMIO_STAT_SIGSTATE);
}
static int tmio_multi_io_quirk(struct mmc_card *card,
@@ -1082,7 +1091,7 @@ static const struct mmc_host_ops tmio_mmc_ops = {
.request = tmio_mmc_request,
.set_ios = tmio_mmc_set_ios,
.get_ro = tmio_mmc_get_ro,
- .get_cd = mmc_gpio_get_cd,
+ .get_cd = tmio_mmc_get_cd,
.enable_sdio_irq = tmio_mmc_enable_sdio_irq,
.multi_io_quirk = tmio_multi_io_quirk,
.hw_reset = tmio_mmc_hw_reset,
@@ -1114,15 +1123,20 @@ static int tmio_mmc_init_ocr(struct tmio_mmc_host *host)
}
static void tmio_mmc_of_parse(struct platform_device *pdev,
- struct tmio_mmc_data *pdata)
+ struct mmc_host *mmc)
{
const struct device_node *np = pdev->dev.of_node;
if (!np)
return;
+ /*
+ * DEPRECATED:
+ * For new platforms, please use "disable-wp" instead of
+ * "toshiba,mmc-wrprotect-disable"
+ */
if (of_get_property(np, "toshiba,mmc-wrprotect-disable", NULL))
- pdata->flags |= TMIO_MMC_WRPROTECT_DISABLE;
+ mmc->caps2 |= MMC_CAP2_NO_WRITE_PROTECT;
}
struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev,
@@ -1157,7 +1171,7 @@ struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev,
goto free;
}
- tmio_mmc_of_parse(pdev, pdata);
+ tmio_mmc_of_parse(pdev, mmc);
platform_set_drvdata(pdev, host);
@@ -1181,7 +1195,6 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
struct tmio_mmc_data *pdata = _host->pdata;
struct mmc_host *mmc = _host->mmc;
int ret;
- u32 irq_mask = TMIO_MASK_CMD;
/*
* Check the sanity of mmc->f_min to prevent tmio_mmc_set_clock() from
@@ -1230,6 +1243,9 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
if (mmc_can_gpio_ro(mmc))
_host->ops.get_ro = mmc_gpio_get_ro;
+ if (mmc_can_gpio_cd(mmc))
+ _host->ops.get_cd = mmc_gpio_get_cd;
+
_host->native_hotplug = !(mmc_can_gpio_cd(mmc) ||
mmc->caps & MMC_CAP_NEEDS_POLL ||
!mmc_card_is_removable(mmc));
@@ -1260,15 +1276,9 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
_host->sdcard_irq_mask = sd_ctrl_read16_and_16_as_32(_host, CTL_IRQ_MASK);
tmio_mmc_disable_mmc_irqs(_host, TMIO_MASK_ALL);
- /* Unmask the IRQs we want to know about */
- if (!_host->chan_rx)
- irq_mask |= TMIO_MASK_READOP;
- if (!_host->chan_tx)
- irq_mask |= TMIO_MASK_WRITEOP;
- if (!_host->native_hotplug)
- irq_mask &= ~(TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT);
-
- _host->sdcard_irq_mask &= ~irq_mask;
+ if (_host->native_hotplug)
+ tmio_mmc_enable_mmc_irqs(_host,
+ TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT);
spin_lock_init(&_host->lock);
mutex_init(&_host->ios_lock);
@@ -1367,6 +1377,10 @@ int tmio_mmc_host_runtime_resume(struct device *dev)
if (host->clk_cache)
tmio_mmc_set_clock(host, host->clk_cache);
+ if (host->native_hotplug)
+ tmio_mmc_enable_mmc_irqs(host,
+ TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT);
+
tmio_mmc_enable_dma(host, true);
if (tmio_mmc_can_retune(host) && host->select_tuning(host))
diff --git a/drivers/mmc/host/ushc.c b/drivers/mmc/host/ushc.c
index 1d843357422e..81dac17064d7 100644
--- a/drivers/mmc/host/ushc.c
+++ b/drivers/mmc/host/ushc.c
@@ -309,8 +309,6 @@ static void ushc_request(struct mmc_host *mmc, struct mmc_request *req)
/* Submit CSW. */
ret = usb_submit_urb(ushc->csw_urb, GFP_ATOMIC);
- if (ret < 0)
- goto out;
out:
spin_unlock_irqrestore(&ushc->lock, flags);
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index 2a8ac6829d42..46ab7feec6b6 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -333,8 +333,6 @@ source "drivers/mtd/devices/Kconfig"
source "drivers/mtd/nand/Kconfig"
-source "drivers/mtd/onenand/Kconfig"
-
source "drivers/mtd/lpddr/Kconfig"
source "drivers/mtd/spi-nor/Kconfig"
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
index d6f8f625e1ff..93473d215a38 100644
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -32,7 +32,7 @@ obj-$(CONFIG_MTD_SWAP) += mtdswap.o
nftl-objs := nftlcore.o nftlmount.o
inftl-objs := inftlcore.o inftlmount.o
-obj-y += chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/
+obj-y += chips/ lpddr/ maps/ devices/ nand/ tests/
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/
obj-$(CONFIG_MTD_UBI) += ubi/
diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c
index 5e1b68cbcd0a..f5695be14499 100644
--- a/drivers/mtd/chips/cfi_cmdset_0001.c
+++ b/drivers/mtd/chips/cfi_cmdset_0001.c
@@ -45,6 +45,7 @@
#define I82802AB 0x00ad
#define I82802AC 0x00ac
#define PF38F4476 0x881c
+#define M28F00AP30 0x8963
/* STMicroelectronics chips */
#define M50LPW080 0x002F
#define M50FLW080A 0x0080
@@ -375,6 +376,17 @@ static void cfi_fixup_major_minor(struct cfi_private *cfi,
extp->MinorVersion = '1';
}
+static int cfi_is_micron_28F00AP30(struct cfi_private *cfi, struct flchip *chip)
+{
+ /*
+ * Micron(was Numonyx) 1Gbit bottom boot are buggy w.r.t
+ * Erase Supend for their small Erase Blocks(0x8000)
+ */
+ if (cfi->mfr == CFI_MFR_INTEL && cfi->id == M28F00AP30)
+ return 1;
+ return 0;
+}
+
static inline struct cfi_pri_intelext *
read_pri_intelext(struct map_info *map, __u16 adr)
{
@@ -831,21 +843,30 @@ static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long
(mode == FL_WRITING && (cfip->SuspendCmdSupport & 1))))
goto sleep;
+ /* Do not allow suspend iff read/write to EB address */
+ if ((adr & chip->in_progress_block_mask) ==
+ chip->in_progress_block_addr)
+ goto sleep;
+
+ /* do not suspend small EBs, buggy Micron Chips */
+ if (cfi_is_micron_28F00AP30(cfi, chip) &&
+ (chip->in_progress_block_mask == ~(0x8000-1)))
+ goto sleep;
/* Erase suspend */
- map_write(map, CMD(0xB0), adr);
+ map_write(map, CMD(0xB0), chip->in_progress_block_addr);
/* If the flash has finished erasing, then 'erase suspend'
* appears to make some (28F320) flash devices switch to
* 'read' mode. Make sure that we switch to 'read status'
* mode so we get the right data. --rmk
*/
- map_write(map, CMD(0x70), adr);
+ map_write(map, CMD(0x70), chip->in_progress_block_addr);
chip->oldstate = FL_ERASING;
chip->state = FL_ERASE_SUSPENDING;
chip->erase_suspended = 1;
for (;;) {
- status = map_read(map, adr);
+ status = map_read(map, chip->in_progress_block_addr);
if (map_word_andequal(map, status, status_OK, status_OK))
break;
@@ -1041,8 +1062,8 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
sending the 0x70 (Read Status) command to an erasing
chip and expecting it to be ignored, that's what we
do. */
- map_write(map, CMD(0xd0), adr);
- map_write(map, CMD(0x70), adr);
+ map_write(map, CMD(0xd0), chip->in_progress_block_addr);
+ map_write(map, CMD(0x70), chip->in_progress_block_addr);
chip->oldstate = FL_READY;
chip->state = FL_ERASING;
break;
@@ -1933,6 +1954,8 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
map_write(map, CMD(0xD0), adr);
chip->state = FL_ERASING;
chip->erase_suspended = 0;
+ chip->in_progress_block_addr = adr;
+ chip->in_progress_block_mask = ~(len - 1);
ret = INVAL_CACHE_AND_WAIT(map, chip, adr,
adr, len,
@@ -1993,20 +2016,8 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
static int cfi_intelext_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
{
- unsigned long ofs, len;
- int ret;
-
- ofs = instr->addr;
- len = instr->len;
-
- ret = cfi_varsize_frob(mtd, do_erase_oneblock, ofs, len, NULL);
- if (ret)
- return ret;
-
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
-
- return 0;
+ return cfi_varsize_frob(mtd, do_erase_oneblock, instr->addr,
+ instr->len, NULL);
}
static void cfi_intelext_sync (struct mtd_info *mtd)
diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c
index 56aa6b75213d..692902df2598 100644
--- a/drivers/mtd/chips/cfi_cmdset_0002.c
+++ b/drivers/mtd/chips/cfi_cmdset_0002.c
@@ -816,9 +816,10 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
(mode == FL_WRITING && (cfip->EraseSuspend & 0x2))))
goto sleep;
- /* We could check to see if we're trying to access the sector
- * that is currently being erased. However, no user will try
- * anything like that so we just wait for the timeout. */
+ /* Do not allow suspend iff read/write to EB address */
+ if ((adr & chip->in_progress_block_mask) ==
+ chip->in_progress_block_addr)
+ goto sleep;
/* Erase suspend */
/* It's harmless to issue the Erase-Suspend and Erase-Resume
@@ -2267,6 +2268,7 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip)
chip->state = FL_ERASING;
chip->erase_suspended = 0;
chip->in_progress_block_addr = adr;
+ chip->in_progress_block_mask = ~(map->size - 1);
INVALIDATE_CACHE_UDELAY(map, chip,
adr, map->size,
@@ -2356,6 +2358,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
chip->state = FL_ERASING;
chip->erase_suspended = 0;
chip->in_progress_block_addr = adr;
+ chip->in_progress_block_mask = ~(len - 1);
INVALIDATE_CACHE_UDELAY(map, chip,
adr, len,
@@ -2415,20 +2418,8 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
static int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
{
- unsigned long ofs, len;
- int ret;
-
- ofs = instr->addr;
- len = instr->len;
-
- ret = cfi_varsize_frob(mtd, do_erase_oneblock, ofs, len, NULL);
- if (ret)
- return ret;
-
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
-
- return 0;
+ return cfi_varsize_frob(mtd, do_erase_oneblock, instr->addr,
+ instr->len, NULL);
}
@@ -2436,7 +2427,6 @@ static int cfi_amdstd_erase_chip(struct mtd_info *mtd, struct erase_info *instr)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
- int ret = 0;
if (instr->addr != 0)
return -EINVAL;
@@ -2444,14 +2434,7 @@ static int cfi_amdstd_erase_chip(struct mtd_info *mtd, struct erase_info *instr)
if (instr->len != mtd->size)
return -EINVAL;
- ret = do_erase_chip(map, &cfi->chips[0]);
- if (ret)
- return ret;
-
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
-
- return 0;
+ return do_erase_chip(map, &cfi->chips[0]);
}
static int do_atmel_lock(struct map_info *map, struct flchip *chip,
diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c
index 7d342965f392..7b7658a05036 100644
--- a/drivers/mtd/chips/cfi_cmdset_0020.c
+++ b/drivers/mtd/chips/cfi_cmdset_0020.c
@@ -965,9 +965,6 @@ static int cfi_staa_erase_varsize(struct mtd_info *mtd,
}
}
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
-
return 0;
}
diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c
index 7c0b27d132b1..6f7e7e1b3fe5 100644
--- a/drivers/mtd/chips/jedec_probe.c
+++ b/drivers/mtd/chips/jedec_probe.c
@@ -53,6 +53,8 @@
#define AT49BV32XT 0x00C9
/* Eon */
+#define EN29LV400AT 0x22B9
+#define EN29LV400AB 0x22BA
#define EN29SL800BB 0x226B
#define EN29SL800BT 0x22EA
@@ -643,6 +645,36 @@ static const struct amd_flash_info jedec_table[] = {
}
}, {
.mfr_id = CFI_MFR_EON,
+ .dev_id = EN29LV400AT,
+ .name = "Eon EN29LV400AT",
+ .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8,
+ .uaddr = MTD_UADDR_0x0AAA_0x0555,
+ .dev_size = SIZE_512KiB,
+ .cmd_set = P_ID_AMD_STD,
+ .nr_regions = 4,
+ .regions = {
+ ERASEINFO(0x10000,7),
+ ERASEINFO(0x08000,1),
+ ERASEINFO(0x02000,2),
+ ERASEINFO(0x04000,1),
+ }
+ }, {
+ .mfr_id = CFI_MFR_EON,
+ .dev_id = EN29LV400AB,
+ .name = "Eon EN29LV400AB",
+ .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8,
+ .uaddr = MTD_UADDR_0x0AAA_0x0555,
+ .dev_size = SIZE_512KiB,
+ .cmd_set = P_ID_AMD_STD,
+ .nr_regions = 4,
+ .regions = {
+ ERASEINFO(0x04000,1),
+ ERASEINFO(0x02000,2),
+ ERASEINFO(0x08000,1),
+ ERASEINFO(0x10000,7),
+ }
+ }, {
+ .mfr_id = CFI_MFR_EON,
.dev_id = EN29SL800BT,
.name = "Eon EN29SL800BT",
.devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8,
@@ -1889,6 +1921,8 @@ static inline u32 jedec_read_mfr(struct map_info *map, uint32_t base,
do {
uint32_t ofs = cfi_build_cmd_addr(0 + (bank << 8), map, cfi);
mask = (1 << (cfi->device_type * 8)) - 1;
+ if (ofs >= map->size)
+ return 0;
result = map_read(map, base + ofs);
bank++;
} while ((result.x[0] & mask) == CFI_MFR_CONTINUATION);
diff --git a/drivers/mtd/chips/map_ram.c b/drivers/mtd/chips/map_ram.c
index 1cd0fff0e940..c37fce926864 100644
--- a/drivers/mtd/chips/map_ram.c
+++ b/drivers/mtd/chips/map_ram.c
@@ -131,8 +131,6 @@ static int mapram_erase (struct mtd_info *mtd, struct erase_info *instr)
allff = map_word_ff(map);
for (i=0; i<instr->len; i += map_bankwidth(map))
map_write(map, allff, instr->addr + i);
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
return 0;
}
diff --git a/drivers/mtd/devices/bcm47xxsflash.c b/drivers/mtd/devices/bcm47xxsflash.c
index e2bd81817df4..9baa81b8780c 100644
--- a/drivers/mtd/devices/bcm47xxsflash.c
+++ b/drivers/mtd/devices/bcm47xxsflash.c
@@ -68,7 +68,6 @@ static int bcm47xxsflash_poll(struct bcm47xxsflash *b47s, int timeout)
static int bcm47xxsflash_erase(struct mtd_info *mtd, struct erase_info *erase)
{
struct bcm47xxsflash *b47s = mtd->priv;
- int err;
switch (b47s->type) {
case BCM47XXSFLASH_TYPE_ST:
@@ -89,16 +88,7 @@ static int bcm47xxsflash_erase(struct mtd_info *mtd, struct erase_info *erase)
break;
}
- err = bcm47xxsflash_poll(b47s, HZ);
- if (err)
- erase->state = MTD_ERASE_FAILED;
- else
- erase->state = MTD_ERASE_DONE;
-
- if (erase->callback)
- erase->callback(erase);
-
- return err;
+ return bcm47xxsflash_poll(b47s, HZ);
}
static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len,
diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c
index 62fd6905c648..c9e424993e37 100644
--- a/drivers/mtd/devices/block2mtd.c
+++ b/drivers/mtd/devices/block2mtd.c
@@ -88,17 +88,12 @@ static int block2mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
size_t len = instr->len;
int err;
- instr->state = MTD_ERASING;
mutex_lock(&dev->write_mutex);
err = _block2mtd_erase(dev, from, len);
mutex_unlock(&dev->write_mutex);
- if (err) {
+ if (err)
pr_err("erase failed err = %d\n", err);
- instr->state = MTD_ERASE_FAILED;
- } else
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
return err;
}
@@ -225,7 +220,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size,
int i;
#endif
const fmode_t mode = FMODE_READ | FMODE_WRITE | FMODE_EXCL;
- struct block_device *bdev = ERR_PTR(-ENODEV);
+ struct block_device *bdev;
struct block2mtd_dev *dev;
char *name;
diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c
index a85af236b44d..c594fe5eac08 100644
--- a/drivers/mtd/devices/docg3.c
+++ b/drivers/mtd/devices/docg3.c
@@ -1191,39 +1191,27 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *info)
{
struct docg3 *docg3 = mtd->priv;
uint64_t len;
- int block0, block1, page, ret, ofs = 0;
+ int block0, block1, page, ret = 0, ofs = 0;
doc_dbg("doc_erase(from=%lld, len=%lld\n", info->addr, info->len);
- info->state = MTD_ERASE_PENDING;
calc_block_sector(info->addr + info->len, &block0, &block1, &page,
&ofs, docg3->reliable);
- ret = -EINVAL;
if (info->addr + info->len > mtd->size || page || ofs)
- goto reset_err;
+ return -EINVAL;
- ret = 0;
calc_block_sector(info->addr, &block0, &block1, &page, &ofs,
docg3->reliable);
mutex_lock(&docg3->cascade->lock);
doc_set_device_id(docg3, docg3->device_id);
doc_set_reliable_mode(docg3);
for (len = info->len; !ret && len > 0; len -= mtd->erasesize) {
- info->state = MTD_ERASING;
ret = doc_erase_block(docg3, block0, block1);
block0 += 2;
block1 += 2;
}
mutex_unlock(&docg3->cascade->lock);
- if (ret)
- goto reset_err;
-
- info->state = MTD_ERASE_DONE;
- return 0;
-
-reset_err:
- info->state = MTD_ERASE_FAILED;
return ret;
}
diff --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c
index 555b94406e0b..f67b653c17d7 100644
--- a/drivers/mtd/devices/lart.c
+++ b/drivers/mtd/devices/lart.c
@@ -414,10 +414,7 @@ static int flash_erase (struct mtd_info *mtd,struct erase_info *instr)
while (len)
{
if (!erase_block (addr))
- {
- instr->state = MTD_ERASE_FAILED;
return (-EIO);
- }
addr += mtd->eraseregions[i].erasesize;
len -= mtd->eraseregions[i].erasesize;
@@ -425,9 +422,6 @@ static int flash_erase (struct mtd_info *mtd,struct erase_info *instr)
if (addr == mtd->eraseregions[i].offset + (mtd->eraseregions[i].erasesize * mtd->eraseregions[i].numblocks)) i++;
}
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
-
return (0);
}
diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c
index 5dc8bd042cc5..aaaeaae01e1d 100644
--- a/drivers/mtd/devices/mtd_dataflash.c
+++ b/drivers/mtd/devices/mtd_dataflash.c
@@ -220,10 +220,6 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
}
mutex_unlock(&priv->lock);
- /* Inform MTD subsystem that erase is complete */
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
-
return 0;
}
diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c
index 0bf4aeaf0cb8..46238796145f 100644
--- a/drivers/mtd/devices/mtdram.c
+++ b/drivers/mtd/devices/mtdram.c
@@ -60,8 +60,7 @@ static int ram_erase(struct mtd_info *mtd, struct erase_info *instr)
if (check_offs_len(mtd, instr->addr, instr->len))
return -EINVAL;
memset((char *)mtd->priv + instr->addr, 0xff, instr->len);
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
+
return 0;
}
diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c
index 7287696a21f9..9ee04b5f9311 100644
--- a/drivers/mtd/devices/phram.c
+++ b/drivers/mtd/devices/phram.c
@@ -39,13 +39,6 @@ static int phram_erase(struct mtd_info *mtd, struct erase_info *instr)
memset(start + instr->addr, 0xff, instr->len);
- /*
- * This'll catch a few races. Free the thing before returning :)
- * I don't feel at all ashamed. This kind of thing is possible anyway
- * with flash, but unlikely.
- */
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
return 0;
}
diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c
index cadea0620cd0..5d842cbca3de 100644
--- a/drivers/mtd/devices/pmc551.c
+++ b/drivers/mtd/devices/pmc551.c
@@ -184,12 +184,10 @@ static int pmc551_erase(struct mtd_info *mtd, struct erase_info *instr)
}
out:
- instr->state = MTD_ERASE_DONE;
#ifdef CONFIG_MTD_PMC551_DEBUG
printk(KERN_DEBUG "pmc551_erase() done\n");
#endif
- mtd_erase_callback(instr);
return 0;
}
diff --git a/drivers/mtd/devices/powernv_flash.c b/drivers/mtd/devices/powernv_flash.c
index 26f9feaa5d17..c1312b141ae0 100644
--- a/drivers/mtd/devices/powernv_flash.c
+++ b/drivers/mtd/devices/powernv_flash.c
@@ -175,19 +175,11 @@ static int powernv_flash_erase(struct mtd_info *mtd, struct erase_info *erase)
{
int rc;
- erase->state = MTD_ERASING;
-
- /* todo: register our own notifier to do a true async implementation */
rc = powernv_flash_async_op(mtd, FLASH_OP_ERASE, erase->addr,
erase->len, NULL, NULL);
-
- if (rc) {
+ if (rc)
erase->fail_addr = erase->addr;
- erase->state = MTD_ERASE_FAILED;
- } else {
- erase->state = MTD_ERASE_DONE;
- }
- mtd_erase_callback(erase);
+
return rc;
}
diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c
index 0ec85f316d24..10183ee4e12b 100644
--- a/drivers/mtd/devices/slram.c
+++ b/drivers/mtd/devices/slram.c
@@ -84,12 +84,7 @@ static int slram_erase(struct mtd_info *mtd, struct erase_info *instr)
slram_priv_t *priv = mtd->priv;
memset(priv->start + instr->addr, 0xff, instr->len);
- /* This'll catch a few races. Free the thing before returning :)
- * I don't feel at all ashamed. This kind of thing is possible anyway
- * with flash, but unlikely.
- */
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
+
return(0);
}
diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c
index ddf478976013..986f81d2f93e 100644
--- a/drivers/mtd/devices/spear_smi.c
+++ b/drivers/mtd/devices/spear_smi.c
@@ -518,7 +518,6 @@ static int spear_mtd_erase(struct mtd_info *mtd, struct erase_info *e_info)
/* preparing the command for flash */
ret = spear_smi_erase_sector(dev, bank, command, 4);
if (ret) {
- e_info->state = MTD_ERASE_FAILED;
mutex_unlock(&flash->lock);
return ret;
}
@@ -527,8 +526,6 @@ static int spear_mtd_erase(struct mtd_info *mtd, struct erase_info *e_info)
}
mutex_unlock(&flash->lock);
- e_info->state = MTD_ERASE_DONE;
- mtd_erase_callback(e_info);
return 0;
}
diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c
index 5b84d71efb36..1897f33fe3e7 100644
--- a/drivers/mtd/devices/sst25l.c
+++ b/drivers/mtd/devices/sst25l.c
@@ -195,7 +195,6 @@ static int sst25l_erase(struct mtd_info *mtd, struct erase_info *instr)
err = sst25l_erase_sector(flash, addr);
if (err) {
mutex_unlock(&flash->lock);
- instr->state = MTD_ERASE_FAILED;
dev_err(&flash->spi->dev, "Erase failed\n");
return err;
}
@@ -205,8 +204,6 @@ static int sst25l_erase(struct mtd_info *mtd, struct erase_info *instr)
mutex_unlock(&flash->lock);
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
return 0;
}
diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c
index 7bc29d725200..55d4a77f3b7f 100644
--- a/drivers/mtd/devices/st_spi_fsm.c
+++ b/drivers/mtd/devices/st_spi_fsm.c
@@ -1825,13 +1825,9 @@ static int stfsm_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
mutex_unlock(&fsm->lock);
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
-
return 0;
out1:
- instr->state = MTD_ERASE_FAILED;
mutex_unlock(&fsm->lock);
return ret;
@@ -1868,8 +1864,7 @@ static struct flash_info *stfsm_jedec_probe(struct stfsm *fsm)
*/
ext_jedec = id[3] << 8 | id[4];
- dev_dbg(fsm->dev, "JEDEC = 0x%08x [%02x %02x %02x %02x %02x]\n",
- jedec, id[0], id[1], id[2], id[3], id[4]);
+ dev_dbg(fsm->dev, "JEDEC = 0x%08x [%5ph]\n", jedec, id);
for (info = flash_types; info->name; info++) {
if (info->jedec_id == jedec) {
diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c
index 664d206a4cbe..ef6ad2551d57 100644
--- a/drivers/mtd/ftl.c
+++ b/drivers/mtd/ftl.c
@@ -140,12 +140,6 @@ typedef struct partition_t {
#define XFER_PREPARED 0x03
#define XFER_FAILED 0x04
-/*====================================================================*/
-
-
-static void ftl_erase_callback(struct erase_info *done);
-
-
/*======================================================================
Scan_header() checks to see if a memory region contains an FTL
@@ -348,18 +342,19 @@ static int erase_xfer(partition_t *part,
if (!erase)
return -ENOMEM;
- erase->mtd = part->mbd.mtd;
- erase->callback = ftl_erase_callback;
erase->addr = xfer->Offset;
erase->len = 1 << part->header.EraseUnitSize;
- erase->priv = (u_long)part;
ret = mtd_erase(part->mbd.mtd, erase);
+ if (!ret) {
+ xfer->state = XFER_ERASED;
+ xfer->EraseCount++;
+ } else {
+ xfer->state = XFER_FAILED;
+ pr_notice("ftl_cs: erase failed: err = %d\n", ret);
+ }
- if (!ret)
- xfer->EraseCount++;
- else
- kfree(erase);
+ kfree(erase);
return ret;
} /* erase_xfer */
@@ -371,37 +366,6 @@ static int erase_xfer(partition_t *part,
======================================================================*/
-static void ftl_erase_callback(struct erase_info *erase)
-{
- partition_t *part;
- struct xfer_info_t *xfer;
- int i;
-
- /* Look up the transfer unit */
- part = (partition_t *)(erase->priv);
-
- for (i = 0; i < part->header.NumTransferUnits; i++)
- if (part->XferInfo[i].Offset == erase->addr) break;
-
- if (i == part->header.NumTransferUnits) {
- printk(KERN_NOTICE "ftl_cs: internal error: "
- "erase lookup failed!\n");
- return;
- }
-
- xfer = &part->XferInfo[i];
- if (erase->state == MTD_ERASE_DONE)
- xfer->state = XFER_ERASED;
- else {
- xfer->state = XFER_FAILED;
- printk(KERN_NOTICE "ftl_cs: erase failed: state = %d\n",
- erase->state);
- }
-
- kfree(erase);
-
-} /* ftl_erase_callback */
-
static int prepare_xfer(partition_t *part, int i)
{
erase_unit_header_t header;
@@ -429,8 +393,8 @@ static int prepare_xfer(partition_t *part, int i)
}
/* Write the BAM stub */
- nbam = (part->BlocksPerUnit * sizeof(uint32_t) +
- le32_to_cpu(part->header.BAMOffset) + SECTOR_SIZE - 1) / SECTOR_SIZE;
+ nbam = DIV_ROUND_UP(part->BlocksPerUnit * sizeof(uint32_t) +
+ le32_to_cpu(part->header.BAMOffset), SECTOR_SIZE);
offset = xfer->Offset + le32_to_cpu(part->header.BAMOffset);
ctl = cpu_to_le32(BLOCK_CONTROL);
diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c
index 8d6bb189ea8e..aab4f68bd36f 100644
--- a/drivers/mtd/inftlmount.c
+++ b/drivers/mtd/inftlmount.c
@@ -208,8 +208,6 @@ static int find_boot_record(struct INFTLrecord *inftl)
if (ip->Reserved0 != ip->firstUnit) {
struct erase_info *instr = &inftl->instr;
- instr->mtd = inftl->mbd.mtd;
-
/*
* Most likely this is using the
* undocumented qiuck mount feature.
@@ -385,7 +383,6 @@ int INFTL_formatblock(struct INFTLrecord *inftl, int block)
_first_? */
/* Use async erase interface, test return code */
- instr->mtd = inftl->mbd.mtd;
instr->addr = block * inftl->EraseSize;
instr->len = inftl->mbd.mtd->erasesize;
/* Erase one physical eraseblock at a time, even though the NAND api
@@ -393,9 +390,10 @@ int INFTL_formatblock(struct INFTLrecord *inftl, int block)
mark only the failed block in the bbt. */
for (physblock = 0; physblock < inftl->EraseSize;
physblock += instr->len, instr->addr += instr->len) {
- mtd_erase(inftl->mbd.mtd, instr);
+ int ret;
- if (instr->state == MTD_ERASE_FAILED) {
+ ret = mtd_erase(inftl->mbd.mtd, instr);
+ if (ret) {
printk(KERN_WARNING "INFTL: error while formatting block %d\n",
block);
goto fail;
diff --git a/drivers/mtd/lpddr/lpddr2_nvm.c b/drivers/mtd/lpddr/lpddr2_nvm.c
index 2342277c9bcb..5d73db2a496d 100644
--- a/drivers/mtd/lpddr/lpddr2_nvm.c
+++ b/drivers/mtd/lpddr/lpddr2_nvm.c
@@ -380,14 +380,8 @@ out:
*/
static int lpddr2_nvm_erase(struct mtd_info *mtd, struct erase_info *instr)
{
- int ret = lpddr2_nvm_do_block_op(mtd, instr->addr, instr->len,
- LPDDR2_NVM_ERASE);
- if (!ret) {
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
- }
-
- return ret;
+ return lpddr2_nvm_do_block_op(mtd, instr->addr, instr->len,
+ LPDDR2_NVM_ERASE);
}
/*
diff --git a/drivers/mtd/lpddr/lpddr_cmds.c b/drivers/mtd/lpddr/lpddr_cmds.c
index 018c75faadb3..5c5ba3c7c79d 100644
--- a/drivers/mtd/lpddr/lpddr_cmds.c
+++ b/drivers/mtd/lpddr/lpddr_cmds.c
@@ -693,8 +693,6 @@ static int lpddr_erase(struct mtd_info *mtd, struct erase_info *instr)
ofs += size;
len -= size;
}
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
return 0;
}
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index 542fdf8e81fa..bdc1283f30fb 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -334,16 +334,6 @@ config MTD_PCMCIA_ANONYMOUS
If unsure, say N.
-config MTD_BFIN_ASYNC
- tristate "Blackfin BF533-STAMP Flash Chip Support"
- depends on BFIN533_STAMP && MTD_CFI && MTD_COMPLEX_MAPPINGS
- default y
- help
- Map driver which allows for simultaneous utilization of
- ethernet and CFI parallel flash.
-
- If compiled as a module, it will be called bfin-async-flash.
-
config MTD_GPIO_ADDR
tristate "GPIO-assisted Flash Chip Support"
depends on GPIOLIB || COMPILE_TEST
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
index b849aaf85c34..51acf1fec19b 100644
--- a/drivers/mtd/maps/Makefile
+++ b/drivers/mtd/maps/Makefile
@@ -42,7 +42,6 @@ obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o
obj-$(CONFIG_MTD_IXP4XX) += ixp4xx.o
obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o
obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o
-obj-$(CONFIG_MTD_BFIN_ASYNC) += bfin-async-flash.o
obj-$(CONFIG_MTD_RBTX4939) += rbtx4939-flash.o
obj-$(CONFIG_MTD_VMU) += vmu-flash.o
obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr-flash.o
diff --git a/drivers/mtd/maps/bfin-async-flash.c b/drivers/mtd/maps/bfin-async-flash.c
deleted file mode 100644
index 41730feeace8..000000000000
--- a/drivers/mtd/maps/bfin-async-flash.c
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * drivers/mtd/maps/bfin-async-flash.c
- *
- * Handle the case where flash memory and ethernet mac/phy are
- * mapped onto the same async bank. The BF533-STAMP does this
- * for example. All board-specific configuration goes in your
- * board resources file.
- *
- * Copyright 2000 Nicolas Pitre <nico@fluxnic.net>
- * Copyright 2005-2008 Analog Devices Inc.
- *
- * Enter bugs at http://blackfin.uclinux.org/
- *
- * Licensed under the GPL-2 or later.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/partitions.h>
-#include <linux/mtd/physmap.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/types.h>
-
-#include <asm/blackfin.h>
-#include <linux/gpio.h>
-#include <linux/io.h>
-#include <asm/unaligned.h>
-
-#define pr_devinit(fmt, args...) \
- ({ static const char __fmt[] = fmt; printk(__fmt, ## args); })
-
-#define DRIVER_NAME "bfin-async-flash"
-
-struct async_state {
- struct mtd_info *mtd;
- struct map_info map;
- int enet_flash_pin;
- uint32_t flash_ambctl0, flash_ambctl1;
- uint32_t save_ambctl0, save_ambctl1;
- unsigned long irq_flags;
-};
-
-static void switch_to_flash(struct async_state *state)
-{
- local_irq_save(state->irq_flags);
-
- gpio_set_value(state->enet_flash_pin, 0);
-
- state->save_ambctl0 = bfin_read_EBIU_AMBCTL0();
- state->save_ambctl1 = bfin_read_EBIU_AMBCTL1();
- bfin_write_EBIU_AMBCTL0(state->flash_ambctl0);
- bfin_write_EBIU_AMBCTL1(state->flash_ambctl1);
- SSYNC();
-}
-
-static void switch_back(struct async_state *state)
-{
- bfin_write_EBIU_AMBCTL0(state->save_ambctl0);
- bfin_write_EBIU_AMBCTL1(state->save_ambctl1);
- SSYNC();
-
- gpio_set_value(state->enet_flash_pin, 1);
-
- local_irq_restore(state->irq_flags);
-}
-
-static map_word bfin_flash_read(struct map_info *map, unsigned long ofs)
-{
- struct async_state *state = (struct async_state *)map->map_priv_1;
- uint16_t word;
- map_word test;
-
- switch_to_flash(state);
-
- word = readw(map->virt + ofs);
-
- switch_back(state);
-
- test.x[0] = word;
- return test;
-}
-
-static void bfin_flash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
- struct async_state *state = (struct async_state *)map->map_priv_1;
-
- switch_to_flash(state);
-
- memcpy(to, map->virt + from, len);
-
- switch_back(state);
-}
-
-static void bfin_flash_write(struct map_info *map, map_word d1, unsigned long ofs)
-{
- struct async_state *state = (struct async_state *)map->map_priv_1;
- uint16_t d;
-
- d = d1.x[0];
-
- switch_to_flash(state);
-
- writew(d, map->virt + ofs);
- SSYNC();
-
- switch_back(state);
-}
-
-static void bfin_flash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
- struct async_state *state = (struct async_state *)map->map_priv_1;
-
- switch_to_flash(state);
-
- memcpy(map->virt + to, from, len);
- SSYNC();
-
- switch_back(state);
-}
-
-static const char * const part_probe_types[] = {
- "cmdlinepart", "RedBoot", NULL };
-
-static int bfin_flash_probe(struct platform_device *pdev)
-{
- struct physmap_flash_data *pdata = dev_get_platdata(&pdev->dev);
- struct resource *memory = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- struct resource *flash_ambctl = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- struct async_state *state;
-
- state = kzalloc(sizeof(*state), GFP_KERNEL);
- if (!state)
- return -ENOMEM;
-
- state->map.name = DRIVER_NAME;
- state->map.read = bfin_flash_read;
- state->map.copy_from = bfin_flash_copy_from;
- state->map.write = bfin_flash_write;
- state->map.copy_to = bfin_flash_copy_to;
- state->map.bankwidth = pdata->width;
- state->map.size = resource_size(memory);
- state->map.virt = (void __iomem *)memory->start;
- state->map.phys = memory->start;
- state->map.map_priv_1 = (unsigned long)state;
- state->enet_flash_pin = platform_get_irq(pdev, 0);
- state->flash_ambctl0 = flash_ambctl->start;
- state->flash_ambctl1 = flash_ambctl->end;
-
- if (gpio_request(state->enet_flash_pin, DRIVER_NAME)) {
- pr_devinit(KERN_ERR DRIVER_NAME ": Failed to request gpio %d\n", state->enet_flash_pin);
- kfree(state);
- return -EBUSY;
- }
- gpio_direction_output(state->enet_flash_pin, 1);
-
- pr_devinit(KERN_NOTICE DRIVER_NAME ": probing %d-bit flash bus\n", state->map.bankwidth * 8);
- state->mtd = do_map_probe(memory->name, &state->map);
- if (!state->mtd) {
- gpio_free(state->enet_flash_pin);
- kfree(state);
- return -ENXIO;
- }
-
- mtd_device_parse_register(state->mtd, part_probe_types, NULL,
- pdata->parts, pdata->nr_parts);
-
- platform_set_drvdata(pdev, state);
-
- return 0;
-}
-
-static int bfin_flash_remove(struct platform_device *pdev)
-{
- struct async_state *state = platform_get_drvdata(pdev);
- gpio_free(state->enet_flash_pin);
- mtd_device_unregister(state->mtd);
- map_destroy(state->mtd);
- kfree(state);
- return 0;
-}
-
-static struct platform_driver bfin_flash_driver = {
- .probe = bfin_flash_probe,
- .remove = bfin_flash_remove,
- .driver = {
- .name = DRIVER_NAME,
- },
-};
-
-module_platform_driver(bfin_flash_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("MTD map driver for Blackfins with flash/ethernet on same async bank");
diff --git a/drivers/mtd/maps/physmap_of_core.c b/drivers/mtd/maps/physmap_of_core.c
index b1bd4faecfb2..527b1682381f 100644
--- a/drivers/mtd/maps/physmap_of_core.c
+++ b/drivers/mtd/maps/physmap_of_core.c
@@ -20,6 +20,7 @@
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/concat.h>
+#include <linux/mtd/cfi_endian.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
@@ -233,6 +234,11 @@ static int of_flash_probe(struct platform_device *dev)
info->list[i].map.bankwidth = be32_to_cpup(width);
info->list[i].map.device_node = dp;
+ if (of_property_read_bool(dp, "big-endian"))
+ info->list[i].map.swap = CFI_BIG_ENDIAN;
+ else if (of_property_read_bool(dp, "little-endian"))
+ info->list[i].map.swap = CFI_LITTLE_ENDIAN;
+
err = of_flash_probe_gemini(dev, dp, &info->list[i].map);
if (err)
goto err_out;
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index 9ec8f033ac5f..16ae4ae8e8f9 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -419,11 +419,11 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
blk_queue_logical_block_size(new->rq, tr->blksize);
blk_queue_bounce_limit(new->rq, BLK_BOUNCE_HIGH);
- queue_flag_set_unlocked(QUEUE_FLAG_NONROT, new->rq);
- queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, new->rq);
+ blk_queue_flag_set(QUEUE_FLAG_NONROT, new->rq);
+ blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, new->rq);
if (tr->discard) {
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, new->rq);
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, new->rq);
blk_queue_max_discard_sectors(new->rq, UINT_MAX);
}
diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c
index bb4c14f83c75..a5b1933c0490 100644
--- a/drivers/mtd/mtdblock.c
+++ b/drivers/mtd/mtdblock.c
@@ -55,48 +55,27 @@ struct mtdblk_dev {
* being written to until a different sector is required.
*/
-static void erase_callback(struct erase_info *done)
-{
- wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv;
- wake_up(wait_q);
-}
-
static int erase_write (struct mtd_info *mtd, unsigned long pos,
int len, const char *buf)
{
struct erase_info erase;
- DECLARE_WAITQUEUE(wait, current);
- wait_queue_head_t wait_q;
size_t retlen;
int ret;
/*
* First, let's erase the flash block.
*/
-
- init_waitqueue_head(&wait_q);
- erase.mtd = mtd;
- erase.callback = erase_callback;
erase.addr = pos;
erase.len = len;
- erase.priv = (u_long)&wait_q;
-
- set_current_state(TASK_INTERRUPTIBLE);
- add_wait_queue(&wait_q, &wait);
ret = mtd_erase(mtd, &erase);
if (ret) {
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&wait_q, &wait);
printk (KERN_WARNING "mtdblock: erase of region [0x%lx, 0x%x] "
"on \"%s\" failed\n",
pos, len, mtd->name);
return ret;
}
- schedule(); /* Wait for erase to finish. */
- remove_wait_queue(&wait_q, &wait);
-
/*
* Next, write the data to flash.
*/
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index 7d80a8bb96fe..cd67c85cc87d 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -324,10 +324,6 @@ static ssize_t mtdchar_write(struct file *file, const char __user *buf, size_t c
IOCTL calls for getting device parameters.
======================================================================*/
-static void mtdchar_erase_callback (struct erase_info *instr)
-{
- wake_up((wait_queue_head_t *)instr->priv);
-}
static int otp_select_filemode(struct mtd_file_info *mfi, int mode)
{
@@ -709,11 +705,6 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
if (!erase)
ret = -ENOMEM;
else {
- wait_queue_head_t waitq;
- DECLARE_WAITQUEUE(wait, current);
-
- init_waitqueue_head(&waitq);
-
if (cmd == MEMERASE64) {
struct erase_info_user64 einfo64;
@@ -735,31 +726,8 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
erase->addr = einfo32.start;
erase->len = einfo32.length;
}
- erase->mtd = mtd;
- erase->callback = mtdchar_erase_callback;
- erase->priv = (unsigned long)&waitq;
-
- /*
- FIXME: Allow INTERRUPTIBLE. Which means
- not having the wait_queue head on the stack.
-
- If the wq_head is on the stack, and we
- leave because we got interrupted, then the
- wq_head is no longer there when the
- callback routine tries to wake us up.
- */
+
ret = mtd_erase(mtd, erase);
- if (!ret) {
- set_current_state(TASK_UNINTERRUPTIBLE);
- add_wait_queue(&waitq, &wait);
- if (erase->state != MTD_ERASE_DONE &&
- erase->state != MTD_ERASE_FAILED)
- schedule();
- remove_wait_queue(&waitq, &wait);
- set_current_state(TASK_RUNNING);
-
- ret = (erase->state == MTD_ERASE_FAILED)?-EIO:0;
- }
kfree(erase);
}
break;
diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c
index 60bf53df5454..6b86d1a73cf2 100644
--- a/drivers/mtd/mtdconcat.c
+++ b/drivers/mtd/mtdconcat.c
@@ -333,45 +333,6 @@ concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
return -EINVAL;
}
-static void concat_erase_callback(struct erase_info *instr)
-{
- wake_up((wait_queue_head_t *) instr->priv);
-}
-
-static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase)
-{
- int err;
- wait_queue_head_t waitq;
- DECLARE_WAITQUEUE(wait, current);
-
- /*
- * This code was stol^H^H^H^Hinspired by mtdchar.c
- */
- init_waitqueue_head(&waitq);
-
- erase->mtd = mtd;
- erase->callback = concat_erase_callback;
- erase->priv = (unsigned long) &waitq;
-
- /*
- * FIXME: Allow INTERRUPTIBLE. Which means
- * not having the wait_queue head on the stack.
- */
- err = mtd_erase(mtd, erase);
- if (!err) {
- set_current_state(TASK_UNINTERRUPTIBLE);
- add_wait_queue(&waitq, &wait);
- if (erase->state != MTD_ERASE_DONE
- && erase->state != MTD_ERASE_FAILED)
- schedule();
- remove_wait_queue(&waitq, &wait);
- set_current_state(TASK_RUNNING);
-
- err = (erase->state == MTD_ERASE_FAILED) ? -EIO : 0;
- }
- return err;
-}
-
static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
{
struct mtd_concat *concat = CONCAT(mtd);
@@ -466,7 +427,7 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
erase->len = length;
length -= erase->len;
- if ((err = concat_dev_erase(subdev, erase))) {
+ if ((err = mtd_erase(subdev, erase))) {
/* sanity check: should never happen since
* block alignment has been checked above */
BUG_ON(err == -EINVAL);
@@ -485,14 +446,9 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
erase->addr = 0;
offset += subdev->size;
}
- instr->state = erase->state;
kfree(erase);
- if (err)
- return err;
- if (instr->callback)
- instr->callback(instr);
- return 0;
+ return err;
}
static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index 28553c840d32..807d17d863b3 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -419,7 +419,7 @@ int mtd_wunit_to_pairing_info(struct mtd_info *mtd, int wunit,
EXPORT_SYMBOL_GPL(mtd_wunit_to_pairing_info);
/**
- * mtd_wunit_to_pairing_info - get wunit from pairing information
+ * mtd_pairing_info_to_wunit - get wunit from pairing information
* @mtd: pointer to new MTD device info structure
* @info: pairing information struct
*
@@ -641,29 +641,6 @@ out_error:
return ret;
}
-static int mtd_add_device_partitions(struct mtd_info *mtd,
- struct mtd_partitions *parts)
-{
- const struct mtd_partition *real_parts = parts->parts;
- int nbparts = parts->nr_parts;
- int ret;
-
- if (nbparts == 0 || IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) {
- ret = add_mtd_device(mtd);
- if (ret)
- return ret;
- }
-
- if (nbparts > 0) {
- ret = add_mtd_partitions(mtd, real_parts, nbparts);
- if (ret && IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER))
- del_mtd_device(mtd);
- return ret;
- }
-
- return 0;
-}
-
/*
* Set a few defaults based on the parent devices, if not provided by the
* driver
@@ -696,14 +673,13 @@ static void mtd_set_dev_defaults(struct mtd_info *mtd)
* 'parse_mtd_partitions()') and MTD device and partitions registering. It
* basically follows the most common pattern found in many MTD drivers:
*
- * * It first tries to probe partitions on MTD device @mtd using parsers
+ * * If the MTD_PARTITIONED_MASTER option is set, then the device as a whole is
+ * registered first.
+ * * Then It tries to probe partitions on MTD device @mtd using parsers
* specified in @types (if @types is %NULL, then the default list of parsers
* is used, see 'parse_mtd_partitions()' for more information). If none are
* found this functions tries to fallback to information specified in
* @parts/@nr_parts.
- * * If any partitioning info was found, this function registers the found
- * partitions. If the MTD_PARTITIONED_MASTER option is set, then the device
- * as a whole is registered first.
* * If no partitions were found this function just registers the MTD device
* @mtd and exits.
*
@@ -714,29 +690,31 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
const struct mtd_partition *parts,
int nr_parts)
{
- struct mtd_partitions parsed;
+ struct mtd_partitions parsed = { };
int ret;
mtd_set_dev_defaults(mtd);
- memset(&parsed, 0, sizeof(parsed));
+ if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) {
+ ret = add_mtd_device(mtd);
+ if (ret)
+ return ret;
+ }
+ /* Prefer parsed partitions over driver-provided fallback */
ret = parse_mtd_partitions(mtd, types, &parsed, parser_data);
- if ((ret < 0 || parsed.nr_parts == 0) && parts && nr_parts) {
- /* Fall back to driver-provided partitions */
- parsed = (struct mtd_partitions){
- .parts = parts,
- .nr_parts = nr_parts,
- };
- } else if (ret < 0) {
- /* Didn't come up with parsed OR fallback partitions */
- pr_info("mtd: failed to find partitions; one or more parsers reports errors (%d)\n",
- ret);
- /* Don't abort on errors; we can still use unpartitioned MTD */
- memset(&parsed, 0, sizeof(parsed));
+ if (!ret && parsed.nr_parts) {
+ parts = parsed.parts;
+ nr_parts = parsed.nr_parts;
}
- ret = mtd_add_device_partitions(mtd, &parsed);
+ if (nr_parts)
+ ret = add_mtd_partitions(mtd, parts, nr_parts);
+ else if (!device_is_registered(&mtd->dev))
+ ret = add_mtd_device(mtd);
+ else
+ ret = 0;
+
if (ret)
goto out;
@@ -758,6 +736,9 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
out:
/* Cleanup any parsed partitions */
mtd_part_parser_cleanup(&parsed);
+ if (ret && device_is_registered(&mtd->dev))
+ del_mtd_device(mtd);
+
return ret;
}
EXPORT_SYMBOL_GPL(mtd_device_parse_register);
@@ -963,24 +944,25 @@ void __put_mtd_device(struct mtd_info *mtd)
EXPORT_SYMBOL_GPL(__put_mtd_device);
/*
- * Erase is an asynchronous operation. Device drivers are supposed
- * to call instr->callback() whenever the operation completes, even
- * if it completes with a failure.
- * Callers are supposed to pass a callback function and wait for it
- * to be called before writing to the block.
+ * Erase is an synchronous operation. Device drivers are epected to return a
+ * negative error code if the operation failed and update instr->fail_addr
+ * to point the portion that was not properly erased.
*/
int mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
{
+ instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
+
+ if (!mtd->erasesize || !mtd->_erase)
+ return -ENOTSUPP;
+
if (instr->addr >= mtd->size || instr->len > mtd->size - instr->addr)
return -EINVAL;
if (!(mtd->flags & MTD_WRITEABLE))
return -EROFS;
- instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
- if (!instr->len) {
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
+
+ if (!instr->len)
return 0;
- }
+
ledtrig_mtd_activity();
return mtd->_erase(mtd, instr);
}
@@ -1525,9 +1507,9 @@ int mtd_ooblayout_get_databytes(struct mtd_info *mtd, u8 *databuf,
EXPORT_SYMBOL_GPL(mtd_ooblayout_get_databytes);
/**
- * mtd_ooblayout_get_eccbytes - set data bytes into the oob buffer
+ * mtd_ooblayout_set_databytes - set data bytes into the oob buffer
* @mtd: mtd info structure
- * @eccbuf: source buffer to get data bytes from
+ * @databuf: source buffer to get data bytes from
* @oobbuf: OOB buffer
* @start: first ECC byte to set
* @nbytes: number of ECC bytes to set
@@ -1559,7 +1541,7 @@ int mtd_ooblayout_count_freebytes(struct mtd_info *mtd)
EXPORT_SYMBOL_GPL(mtd_ooblayout_count_freebytes);
/**
- * mtd_ooblayout_count_freebytes - count the number of ECC bytes in OOB
+ * mtd_ooblayout_count_eccbytes - count the number of ECC bytes in OOB
* @mtd: mtd info structure
*
* Works like mtd_ooblayout_count_bytes(), except it count ECC bytes.
diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c
index 97bb8f6304d4..9f25111fd559 100644
--- a/drivers/mtd/mtdoops.c
+++ b/drivers/mtd/mtdoops.c
@@ -84,12 +84,6 @@ static int page_is_used(struct mtdoops_context *cxt, int page)
return test_bit(page, cxt->oops_page_used);
}
-static void mtdoops_erase_callback(struct erase_info *done)
-{
- wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv;
- wake_up(wait_q);
-}
-
static int mtdoops_erase_block(struct mtdoops_context *cxt, int offset)
{
struct mtd_info *mtd = cxt->mtd;
@@ -97,34 +91,20 @@ static int mtdoops_erase_block(struct mtdoops_context *cxt, int offset)
u32 start_page = start_page_offset / record_size;
u32 erase_pages = mtd->erasesize / record_size;
struct erase_info erase;
- DECLARE_WAITQUEUE(wait, current);
- wait_queue_head_t wait_q;
int ret;
int page;
- init_waitqueue_head(&wait_q);
- erase.mtd = mtd;
- erase.callback = mtdoops_erase_callback;
erase.addr = offset;
erase.len = mtd->erasesize;
- erase.priv = (u_long)&wait_q;
-
- set_current_state(TASK_INTERRUPTIBLE);
- add_wait_queue(&wait_q, &wait);
ret = mtd_erase(mtd, &erase);
if (ret) {
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&wait_q, &wait);
printk(KERN_WARNING "mtdoops: erase of region [0x%llx, 0x%llx] on \"%s\" failed\n",
(unsigned long long)erase.addr,
(unsigned long long)erase.len, mtddev);
return ret;
}
- schedule(); /* Wait for erase to finish. */
- remove_wait_queue(&wait_q, &wait);
-
/* Mark pages as unused */
for (page = start_page; page < start_page + erase_pages; page++)
mark_page_unused(cxt, page);
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index 76cd21d1171b..023516a63276 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -30,6 +30,7 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/err.h>
+#include <linux/of.h>
#include "mtdcore.h"
@@ -205,27 +206,12 @@ static int part_erase(struct mtd_info *mtd, struct erase_info *instr)
instr->addr += part->offset;
ret = part->parent->_erase(part->parent, instr);
- if (ret) {
- if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
- instr->fail_addr -= part->offset;
- instr->addr -= part->offset;
- }
- return ret;
-}
-
-void mtd_erase_callback(struct erase_info *instr)
-{
- if (instr->mtd->_erase == part_erase) {
- struct mtd_part *part = mtd_to_part(instr->mtd);
+ if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
+ instr->fail_addr -= part->offset;
+ instr->addr -= part->offset;
- if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
- instr->fail_addr -= part->offset;
- instr->addr -= part->offset;
- }
- if (instr->callback)
- instr->callback(instr);
+ return ret;
}
-EXPORT_SYMBOL_GPL(mtd_erase_callback);
static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
@@ -861,6 +847,92 @@ static int mtd_part_do_parse(struct mtd_part_parser *parser,
}
/**
+ * mtd_part_get_compatible_parser - find MTD parser by a compatible string
+ *
+ * @compat: compatible string describing partitions in a device tree
+ *
+ * MTD parsers can specify supported partitions by providing a table of
+ * compatibility strings. This function finds a parser that advertises support
+ * for a passed value of "compatible".
+ */
+static struct mtd_part_parser *mtd_part_get_compatible_parser(const char *compat)
+{
+ struct mtd_part_parser *p, *ret = NULL;
+
+ spin_lock(&part_parser_lock);
+
+ list_for_each_entry(p, &part_parsers, list) {
+ const struct of_device_id *matches;
+
+ matches = p->of_match_table;
+ if (!matches)
+ continue;
+
+ for (; matches->compatible[0]; matches++) {
+ if (!strcmp(matches->compatible, compat) &&
+ try_module_get(p->owner)) {
+ ret = p;
+ break;
+ }
+ }
+
+ if (ret)
+ break;
+ }
+
+ spin_unlock(&part_parser_lock);
+
+ return ret;
+}
+
+static int mtd_part_of_parse(struct mtd_info *master,
+ struct mtd_partitions *pparts)
+{
+ struct mtd_part_parser *parser;
+ struct device_node *np;
+ struct property *prop;
+ const char *compat;
+ const char *fixed = "fixed-partitions";
+ int ret, err = 0;
+
+ np = of_get_child_by_name(mtd_get_of_node(master), "partitions");
+ of_property_for_each_string(np, "compatible", prop, compat) {
+ parser = mtd_part_get_compatible_parser(compat);
+ if (!parser)
+ continue;
+ ret = mtd_part_do_parse(parser, master, pparts, NULL);
+ if (ret > 0) {
+ of_node_put(np);
+ return ret;
+ }
+ mtd_part_parser_put(parser);
+ if (ret < 0 && !err)
+ err = ret;
+ }
+ of_node_put(np);
+
+ /*
+ * For backward compatibility we have to try the "fixed-partitions"
+ * parser. It supports old DT format with partitions specified as a
+ * direct subnodes of a flash device DT node without any compatibility
+ * specified we could match.
+ */
+ parser = mtd_part_parser_get(fixed);
+ if (!parser && !request_module("%s", fixed))
+ parser = mtd_part_parser_get(fixed);
+ if (parser) {
+ ret = mtd_part_do_parse(parser, master, pparts, NULL);
+ if (ret > 0)
+ return ret;
+ mtd_part_parser_put(parser);
+ if (ret < 0 && !err)
+ err = ret;
+ }
+
+ return err;
+}
+
+/**
* parse_mtd_partitions - parse MTD partitions
* @master: the master partition (describes whole MTD device)
* @types: names of partition parsers to try or %NULL
@@ -892,19 +964,30 @@ int parse_mtd_partitions(struct mtd_info *master, const char *const *types,
types = default_mtd_part_types;
for ( ; *types; types++) {
- pr_debug("%s: parsing partitions %s\n", master->name, *types);
- parser = mtd_part_parser_get(*types);
- if (!parser && !request_module("%s", *types))
+ /*
+ * ofpart is a special type that means OF partitioning info
+ * should be used. It requires a bit different logic so it is
+ * handled in a separated function.
+ */
+ if (!strcmp(*types, "ofpart")) {
+ ret = mtd_part_of_parse(master, pparts);
+ } else {
+ pr_debug("%s: parsing partitions %s\n", master->name,
+ *types);
parser = mtd_part_parser_get(*types);
- pr_debug("%s: got parser %s\n", master->name,
- parser ? parser->name : NULL);
- if (!parser)
- continue;
- ret = mtd_part_do_parse(parser, master, pparts, data);
+ if (!parser && !request_module("%s", *types))
+ parser = mtd_part_parser_get(*types);
+ pr_debug("%s: got parser %s\n", master->name,
+ parser ? parser->name : NULL);
+ if (!parser)
+ continue;
+ ret = mtd_part_do_parse(parser, master, pparts, data);
+ if (ret <= 0)
+ mtd_part_parser_put(parser);
+ }
/* Found partitions! */
if (ret > 0)
return 0;
- mtd_part_parser_put(parser);
/*
* Stash the first error we see; only report it if no parser
* succeeds
diff --git a/drivers/mtd/mtdswap.c b/drivers/mtd/mtdswap.c
index 7eb0e1f4f980..7161f8a17f62 100644
--- a/drivers/mtd/mtdswap.c
+++ b/drivers/mtd/mtdswap.c
@@ -536,18 +536,10 @@ static void mtdswap_store_eb(struct mtdswap_dev *d, struct swap_eb *eb)
mtdswap_rb_add(d, eb, MTDSWAP_HIFRAG);
}
-
-static void mtdswap_erase_callback(struct erase_info *done)
-{
- wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv;
- wake_up(wait_q);
-}
-
static int mtdswap_erase_block(struct mtdswap_dev *d, struct swap_eb *eb)
{
struct mtd_info *mtd = d->mtd;
struct erase_info erase;
- wait_queue_head_t wq;
unsigned int retries = 0;
int ret;
@@ -556,14 +548,9 @@ static int mtdswap_erase_block(struct mtdswap_dev *d, struct swap_eb *eb)
d->max_erase_count = eb->erase_count;
retry:
- init_waitqueue_head(&wq);
memset(&erase, 0, sizeof(struct erase_info));
-
- erase.mtd = mtd;
- erase.callback = mtdswap_erase_callback;
erase.addr = mtdswap_eb_offset(d, eb);
erase.len = mtd->erasesize;
- erase.priv = (u_long)&wq;
ret = mtd_erase(mtd, &erase);
if (ret) {
@@ -582,27 +569,6 @@ retry:
return -EIO;
}
- ret = wait_event_interruptible(wq, erase.state == MTD_ERASE_DONE ||
- erase.state == MTD_ERASE_FAILED);
- if (ret) {
- dev_err(d->dev, "Interrupted erase block %#llx erasure on %s\n",
- erase.addr, mtd->name);
- return -EINTR;
- }
-
- if (erase.state == MTD_ERASE_FAILED) {
- if (retries++ < MTDSWAP_ERASE_RETRIES) {
- dev_warn(d->dev,
- "erase of erase block %#llx on %s failed",
- erase.addr, mtd->name);
- yield();
- goto retry;
- }
-
- mtdswap_handle_badblock(d, eb);
- return -EIO;
- }
-
return 0;
}
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 736ac887303c..88c7d3b4ff8b 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -1,580 +1,6 @@
-config MTD_NAND_ECC
+config MTD_NAND_CORE
tristate
-config MTD_NAND_ECC_SMC
- bool "NAND ECC Smart Media byte order"
- depends on MTD_NAND_ECC
- default n
- help
- Software ECC according to the Smart Media Specification.
- The original Linux implementation had byte 0 and 1 swapped.
+source "drivers/mtd/nand/onenand/Kconfig"
-
-menuconfig MTD_NAND
- tristate "NAND Device Support"
- depends on MTD
- select MTD_NAND_ECC
- help
- This enables support for accessing all type of NAND flash
- devices. For further information see
- <http://www.linux-mtd.infradead.org/doc/nand.html>.
-
-if MTD_NAND
-
-config MTD_NAND_BCH
- tristate
- select BCH
- depends on MTD_NAND_ECC_BCH
- default MTD_NAND
-
-config MTD_NAND_ECC_BCH
- bool "Support software BCH ECC"
- default n
- help
- This enables support for software BCH error correction. Binary BCH
- codes are more powerful and cpu intensive than traditional Hamming
- ECC codes. They are used with NAND devices requiring more than 1 bit
- of error correction.
-
-config MTD_SM_COMMON
- tristate
- default n
-
-config MTD_NAND_DENALI
- tristate
-
-config MTD_NAND_DENALI_PCI
- tristate "Support Denali NAND controller on Intel Moorestown"
- select MTD_NAND_DENALI
- depends on HAS_DMA && PCI
- help
- Enable the driver for NAND flash on Intel Moorestown, using the
- Denali NAND controller core.
-
-config MTD_NAND_DENALI_DT
- tristate "Support Denali NAND controller as a DT device"
- select MTD_NAND_DENALI
- depends on HAS_DMA && HAVE_CLK && OF
- help
- Enable the driver for NAND flash on platforms using a Denali NAND
- controller as a DT device.
-
-config MTD_NAND_GPIO
- tristate "GPIO assisted NAND Flash driver"
- depends on GPIOLIB || COMPILE_TEST
- depends on HAS_IOMEM
- help
- This enables a NAND flash driver where control signals are
- connected to GPIO pins, and commands and data are communicated
- via a memory mapped interface.
-
-config MTD_NAND_AMS_DELTA
- tristate "NAND Flash device on Amstrad E3"
- depends on MACH_AMS_DELTA
- default y
- help
- Support for NAND flash on Amstrad E3 (Delta).
-
-config MTD_NAND_OMAP2
- tristate "NAND Flash device on OMAP2, OMAP3, OMAP4 and Keystone"
- depends on (ARCH_OMAP2PLUS || ARCH_KEYSTONE)
- help
- Support for NAND flash on Texas Instruments OMAP2, OMAP3, OMAP4
- and Keystone platforms.
-
-config MTD_NAND_OMAP_BCH
- depends on MTD_NAND_OMAP2
- bool "Support hardware based BCH error correction"
- default n
- select BCH
- help
- This config enables the ELM hardware engine, which can be used to
- locate and correct errors when using BCH ECC scheme. This offloads
- the cpu from doing ECC error searching and correction. However some
- legacy OMAP families like OMAP2xxx, OMAP3xxx do not have ELM engine
- so this is optional for them.
-
-config MTD_NAND_OMAP_BCH_BUILD
- def_tristate MTD_NAND_OMAP2 && MTD_NAND_OMAP_BCH
-
-config MTD_NAND_RICOH
- tristate "Ricoh xD card reader"
- default n
- depends on PCI
- select MTD_SM_COMMON
- help
- Enable support for Ricoh R5C852 xD card reader
- You also need to enable ether
- NAND SSFDC (SmartMedia) read only translation layer' or new
- expermental, readwrite
- 'SmartMedia/xD new translation layer'
-
-config MTD_NAND_AU1550
- tristate "Au1550/1200 NAND support"
- depends on MIPS_ALCHEMY
- help
- This enables the driver for the NAND flash controller on the
- AMD/Alchemy 1550 SOC.
-
-config MTD_NAND_BF5XX
- tristate "Blackfin on-chip NAND Flash Controller driver"
- depends on BF54x || BF52x
- help
- This enables the Blackfin on-chip NAND flash controller
-
- No board specific support is done by this driver, each board
- must advertise a platform_device for the driver to attach.
-
- This driver can also be built as a module. If so, the module
- will be called bf5xx-nand.
-
-config MTD_NAND_BF5XX_HWECC
- bool "BF5XX NAND Hardware ECC"
- default y
- depends on MTD_NAND_BF5XX
- help
- Enable the use of the BF5XX's internal ECC generator when
- using NAND.
-
-config MTD_NAND_BF5XX_BOOTROM_ECC
- bool "Use Blackfin BootROM ECC Layout"
- default n
- depends on MTD_NAND_BF5XX_HWECC
- help
- If you wish to modify NAND pages and allow the Blackfin on-chip
- BootROM to boot from them, say Y here. This is only necessary
- if you are booting U-Boot out of NAND and you wish to update
- U-Boot from Linux' userspace. Otherwise, you should say N here.
-
- If unsure, say N.
-
-config MTD_NAND_S3C2410
- tristate "NAND Flash support for Samsung S3C SoCs"
- depends on ARCH_S3C24XX || ARCH_S3C64XX
- help
- This enables the NAND flash controller on the S3C24xx and S3C64xx
- SoCs
-
- No board specific support is done by this driver, each board
- must advertise a platform_device for the driver to attach.
-
-config MTD_NAND_S3C2410_DEBUG
- bool "Samsung S3C NAND driver debug"
- depends on MTD_NAND_S3C2410
- help
- Enable debugging of the S3C NAND driver
-
-config MTD_NAND_NDFC
- tristate "NDFC NanD Flash Controller"
- depends on 4xx
- select MTD_NAND_ECC_SMC
- help
- NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs
-
-config MTD_NAND_S3C2410_CLKSTOP
- bool "Samsung S3C NAND IDLE clock stop"
- depends on MTD_NAND_S3C2410
- default n
- help
- Stop the clock to the NAND controller when there is no chip
- selected to save power. This will mean there is a small delay
- when the is NAND chip selected or released, but will save
- approximately 5mA of power when there is nothing happening.
-
-config MTD_NAND_TANGO
- tristate "NAND Flash support for Tango chips"
- depends on ARCH_TANGO || COMPILE_TEST
- depends on HAS_DMA
- help
- Enables the NAND Flash controller on Tango chips.
-
-config MTD_NAND_DISKONCHIP
- tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation)"
- depends on HAS_IOMEM
- select REED_SOLOMON
- select REED_SOLOMON_DEC16
- help
- This is a reimplementation of M-Systems DiskOnChip 2000,
- Millennium and Millennium Plus as a standard NAND device driver,
- as opposed to the earlier self-contained MTD device drivers.
- This should enable, among other things, proper JFFS2 operation on
- these devices.
-
-config MTD_NAND_DISKONCHIP_PROBE_ADVANCED
- bool "Advanced detection options for DiskOnChip"
- depends on MTD_NAND_DISKONCHIP
- help
- This option allows you to specify nonstandard address at which to
- probe for a DiskOnChip, or to change the detection options. You
- are unlikely to need any of this unless you are using LinuxBIOS.
- Say 'N'.
-
-config MTD_NAND_DISKONCHIP_PROBE_ADDRESS
- hex "Physical address of DiskOnChip" if MTD_NAND_DISKONCHIP_PROBE_ADVANCED
- depends on MTD_NAND_DISKONCHIP
- default "0"
- ---help---
- By default, the probe for DiskOnChip devices will look for a
- DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000.
- This option allows you to specify a single address at which to probe
- for the device, which is useful if you have other devices in that
- range which get upset when they are probed.
-
- (Note that on PowerPC, the normal probe will only check at
- 0xE4000000.)
-
- Normally, you should leave this set to zero, to allow the probe at
- the normal addresses.
-
-config MTD_NAND_DISKONCHIP_PROBE_HIGH
- bool "Probe high addresses"
- depends on MTD_NAND_DISKONCHIP_PROBE_ADVANCED
- help
- By default, the probe for DiskOnChip devices will look for a
- DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000.
- This option changes to make it probe between 0xFFFC8000 and
- 0xFFFEE000. Unless you are using LinuxBIOS, this is unlikely to be
- useful to you. Say 'N'.
-
-config MTD_NAND_DISKONCHIP_BBTWRITE
- bool "Allow BBT writes on DiskOnChip Millennium and 2000TSOP"
- depends on MTD_NAND_DISKONCHIP
- help
- On DiskOnChip devices shipped with the INFTL filesystem (Millennium
- and 2000 TSOP/Alon), Linux reserves some space at the end of the
- device for the Bad Block Table (BBT). If you have existing INFTL
- data on your device (created by non-Linux tools such as M-Systems'
- DOS drivers), your data might overlap the area Linux wants to use for
- the BBT. If this is a concern for you, leave this option disabled and
- Linux will not write BBT data into this area.
- The downside of leaving this option disabled is that if bad blocks
- are detected by Linux, they will not be recorded in the BBT, which
- could cause future problems.
- Once you enable this option, new filesystems (INFTL or others, created
- in Linux or other operating systems) will not use the reserved area.
- The only reason not to enable this option is to prevent damage to
- preexisting filesystems.
- Even if you leave this disabled, you can enable BBT writes at module
- load time (assuming you build diskonchip as a module) with the module
- parameter "inftl_bbt_write=1".
-
-config MTD_NAND_DOCG4
- tristate "Support for DiskOnChip G4"
- depends on HAS_IOMEM
- select BCH
- select BITREVERSE
- help
- Support for diskonchip G4 nand flash, found in various smartphones and
- PDAs, among them the Palm Treo680, HTC Prophet and Wizard, Toshiba
- Portege G900, Asus P526, and O2 XDA Zinc.
-
- With this driver you will be able to use UBI and create a ubifs on the
- device, so you may wish to consider enabling UBI and UBIFS as well.
-
- These devices ship with the Mys/Sandisk SAFTL formatting, for which
- there is currently no mtd parser, so you may want to use command line
- partitioning to segregate write-protected blocks. On the Treo680, the
- first five erase blocks (256KiB each) are write-protected, followed
- by the block containing the saftl partition table. This is probably
- typical.
-
-config MTD_NAND_SHARPSL
- tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
- depends on ARCH_PXA
-
-config MTD_NAND_CAFE
- tristate "NAND support for OLPC CAFÉ chip"
- depends on PCI
- select REED_SOLOMON
- select REED_SOLOMON_DEC16
- help
- Use NAND flash attached to the CAFÉ chip designed for the OLPC
- laptop.
-
-config MTD_NAND_CS553X
- tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)"
- depends on X86_32
- depends on !UML && HAS_IOMEM
- help
- The CS553x companion chips for the AMD Geode processor
- include NAND flash controllers with built-in hardware ECC
- capabilities; enabling this option will allow you to use
- these. The driver will check the MSRs to verify that the
- controller is enabled for NAND, and currently requires that
- the controller be in MMIO mode.
-
- If you say "m", the module will be called cs553x_nand.
-
-config MTD_NAND_ATMEL
- tristate "Support for NAND Flash / SmartMedia on AT91"
- depends on ARCH_AT91
- select MFD_ATMEL_SMC
- help
- Enables support for NAND Flash / Smart Media Card interface
- on Atmel AT91 processors.
-
-config MTD_NAND_PXA3xx
- tristate "NAND support on PXA3xx and Armada 370/XP"
- depends on !MTD_NAND_MARVELL
- depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU
- help
-
- This enables the driver for the NAND flash device found on
- PXA3xx processors (NFCv1) and also on 32-bit Armada
- platforms (XP, 370, 375, 38x, 39x) and 64-bit Armada
- platforms (7K, 8K) (NFCv2).
-
-config MTD_NAND_MARVELL
- tristate "NAND controller support on Marvell boards"
- depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU || \
- COMPILE_TEST
- depends on HAS_IOMEM && HAS_DMA
- help
- This enables the NAND flash controller driver for Marvell boards,
- including:
- - PXA3xx processors (NFCv1)
- - 32-bit Armada platforms (XP, 37x, 38x, 39x) (NFCv2)
- - 64-bit Aramda platforms (7k, 8k) (NFCv2)
-
-config MTD_NAND_SLC_LPC32XX
- tristate "NXP LPC32xx SLC Controller"
- depends on ARCH_LPC32XX
- help
- Enables support for NXP's LPC32XX SLC (i.e. for Single Level Cell
- chips) NAND controller. This is the default for the PHYTEC 3250
- reference board which contains a NAND256R3A2CZA6 chip.
-
- Please check the actual NAND chip connected and its support
- by the SLC NAND controller.
-
-config MTD_NAND_MLC_LPC32XX
- tristate "NXP LPC32xx MLC Controller"
- depends on ARCH_LPC32XX
- help
- Uses the LPC32XX MLC (i.e. for Multi Level Cell chips) NAND
- controller. This is the default for the WORK92105 controller
- board.
-
- Please check the actual NAND chip connected and its support
- by the MLC NAND controller.
-
-config MTD_NAND_CM_X270
- tristate "Support for NAND Flash on CM-X270 modules"
- depends on MACH_ARMCORE
-
-config MTD_NAND_PASEMI
- tristate "NAND support for PA Semi PWRficient"
- depends on PPC_PASEMI
- help
- Enables support for NAND Flash interface on PA Semi PWRficient
- based boards
-
-config MTD_NAND_TMIO
- tristate "NAND Flash device on Toshiba Mobile IO Controller"
- depends on MFD_TMIO
- help
- Support for NAND flash connected to a Toshiba Mobile IO
- Controller in some PDAs, including the Sharp SL6000x.
-
-config MTD_NAND_NANDSIM
- tristate "Support for NAND Flash Simulator"
- help
- The simulator may simulate various NAND flash chips for the
- MTD nand layer.
-
-config MTD_NAND_GPMI_NAND
- tristate "GPMI NAND Flash Controller driver"
- depends on MTD_NAND && MXS_DMA
- help
- Enables NAND Flash support for IMX23, IMX28 or IMX6.
- The GPMI controller is very powerful, with the help of BCH
- module, it can do the hardware ECC. The GPMI supports several
- NAND flashs at the same time.
-
-config MTD_NAND_BRCMNAND
- tristate "Broadcom STB NAND controller"
- depends on ARM || ARM64 || MIPS
- help
- Enables the Broadcom NAND controller driver. The controller was
- originally designed for Set-Top Box but is used on various BCM7xxx,
- BCM3xxx, BCM63xxx, iProc/Cygnus and more.
-
-config MTD_NAND_BCM47XXNFLASH
- tristate "Support for NAND flash on BCM4706 BCMA bus"
- depends on BCMA_NFLASH
- help
- BCMA bus can have various flash memories attached, they are
- registered by bcma as platform devices. This enables driver for
- NAND flash memories. For now only BCM4706 is supported.
-
-config MTD_NAND_PLATFORM
- tristate "Support for generic platform NAND driver"
- depends on HAS_IOMEM
- help
- This implements a generic NAND driver for on-SOC platform
- devices. You will need to provide platform-specific functions
- via platform_data.
-
-config MTD_NAND_ORION
- tristate "NAND Flash support for Marvell Orion SoC"
- depends on PLAT_ORION
- help
- This enables the NAND flash controller on Orion machines.
-
- No board specific support is done by this driver, each board
- must advertise a platform_device for the driver to attach.
-
-config MTD_NAND_OXNAS
- tristate "NAND Flash support for Oxford Semiconductor SoC"
- depends on ARCH_OXNAS || COMPILE_TEST
- depends on HAS_IOMEM
- help
- This enables the NAND flash controller on Oxford Semiconductor SoCs.
-
-config MTD_NAND_FSL_ELBC
- tristate "NAND support for Freescale eLBC controllers"
- depends on FSL_SOC
- select FSL_LBC
- help
- Various Freescale chips, including the 8313, include a NAND Flash
- Controller Module with built-in hardware ECC capabilities.
- Enabling this option will enable you to use this to control
- external NAND devices.
-
-config MTD_NAND_FSL_IFC
- tristate "NAND support for Freescale IFC controller"
- depends on FSL_SOC || ARCH_LAYERSCAPE || SOC_LS1021A
- select FSL_IFC
- select MEMORY
- help
- Various Freescale chips e.g P1010, include a NAND Flash machine
- with built-in hardware ECC capabilities.
- Enabling this option will enable you to use this to control
- external NAND devices.
-
-config MTD_NAND_FSL_UPM
- tristate "Support for NAND on Freescale UPM"
- depends on PPC_83xx || PPC_85xx
- select FSL_LBC
- help
- Enables support for NAND Flash chips wired onto Freescale PowerPC
- processor localbus with User-Programmable Machine support.
-
-config MTD_NAND_MPC5121_NFC
- tristate "MPC5121 built-in NAND Flash Controller support"
- depends on PPC_MPC512x
- help
- This enables the driver for the NAND flash controller on the
- MPC5121 SoC.
-
-config MTD_NAND_VF610_NFC
- tristate "Support for Freescale NFC for VF610/MPC5125"
- depends on (SOC_VF610 || COMPILE_TEST)
- depends on HAS_IOMEM
- help
- Enables support for NAND Flash Controller on some Freescale
- processors like the VF610, MPC5125, MCF54418 or Kinetis K70.
- The driver supports a maximum 2k page size. With 2k pages and
- 64 bytes or more of OOB, hardware ECC with up to 32-bit error
- correction is supported. Hardware ECC is only enabled through
- device tree.
-
-config MTD_NAND_MXC
- tristate "MXC NAND support"
- depends on ARCH_MXC
- help
- This enables the driver for the NAND flash controller on the
- MXC processors.
-
-config MTD_NAND_SH_FLCTL
- tristate "Support for NAND on Renesas SuperH FLCTL"
- depends on SUPERH || COMPILE_TEST
- depends on HAS_IOMEM
- depends on HAS_DMA
- help
- Several Renesas SuperH CPU has FLCTL. This option enables support
- for NAND Flash using FLCTL.
-
-config MTD_NAND_DAVINCI
- tristate "Support NAND on DaVinci/Keystone SoC"
- depends on ARCH_DAVINCI || (ARCH_KEYSTONE && TI_AEMIF)
- help
- Enable the driver for NAND flash chips on Texas Instruments
- DaVinci/Keystone processors.
-
-config MTD_NAND_TXX9NDFMC
- tristate "NAND Flash support for TXx9 SoC"
- depends on SOC_TX4938 || SOC_TX4939
- help
- This enables the NAND flash controller on the TXx9 SoCs.
-
-config MTD_NAND_SOCRATES
- tristate "Support for NAND on Socrates board"
- depends on SOCRATES
- help
- Enables support for NAND Flash chips wired onto Socrates board.
-
-config MTD_NAND_NUC900
- tristate "Support for NAND on Nuvoton NUC9xx/w90p910 evaluation boards."
- depends on ARCH_W90X900
- help
- This enables the driver for the NAND Flash on evaluation board based
- on w90p910 / NUC9xx.
-
-config MTD_NAND_JZ4740
- tristate "Support for JZ4740 SoC NAND controller"
- depends on MACH_JZ4740
- help
- Enables support for NAND Flash on JZ4740 SoC based boards.
-
-config MTD_NAND_JZ4780
- tristate "Support for NAND on JZ4780 SoC"
- depends on MACH_JZ4780 && JZ4780_NEMC
- help
- Enables support for NAND Flash connected to the NEMC on JZ4780 SoC
- based boards, using the BCH controller for hardware error correction.
-
-config MTD_NAND_FSMC
- tristate "Support for NAND on ST Micros FSMC"
- depends on OF
- depends on PLAT_SPEAR || ARCH_NOMADIK || ARCH_U8500 || MACH_U300
- help
- Enables support for NAND Flash chips on the ST Microelectronics
- Flexible Static Memory Controller (FSMC)
-
-config MTD_NAND_XWAY
- bool "Support for NAND on Lantiq XWAY SoC"
- depends on LANTIQ && SOC_TYPE_XWAY
- help
- Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached
- to the External Bus Unit (EBU).
-
-config MTD_NAND_SUNXI
- tristate "Support for NAND on Allwinner SoCs"
- depends on ARCH_SUNXI
- help
- Enables support for NAND Flash chips on Allwinner SoCs.
-
-config MTD_NAND_HISI504
- tristate "Support for NAND controller on Hisilicon SoC Hip04"
- depends on ARCH_HISI || COMPILE_TEST
- depends on HAS_DMA
- help
- Enables support for NAND controller on Hisilicon SoC Hip04.
-
-config MTD_NAND_QCOM
- tristate "Support for NAND on QCOM SoCs"
- depends on ARCH_QCOM
- help
- Enables support for NAND flash chips on SoCs containing the EBI2 NAND
- controller. This controller is found on IPQ806x SoC.
-
-config MTD_NAND_MTK
- tristate "Support for NAND controller on MTK SoCs"
- depends on ARCH_MEDIATEK || COMPILE_TEST
- depends on HAS_DMA
- help
- Enables support for NAND controller on MTK SoCs.
- This controller is found on mt27xx, mt81xx, mt65xx SoCs.
-
-endif # MTD_NAND
+source "drivers/mtd/nand/raw/Kconfig"
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 921634ba400c..3f0cb87f1a57 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -1,71 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
-#
-# linux/drivers/nand/Makefile
-#
-obj-$(CONFIG_MTD_NAND) += nand.o
-obj-$(CONFIG_MTD_NAND_ECC) += nand_ecc.o
-obj-$(CONFIG_MTD_NAND_BCH) += nand_bch.o
-obj-$(CONFIG_MTD_SM_COMMON) += sm_common.o
+nandcore-objs := core.o bbt.o
+obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o
-obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o
-obj-$(CONFIG_MTD_NAND_AMS_DELTA) += ams-delta.o
-obj-$(CONFIG_MTD_NAND_DENALI) += denali.o
-obj-$(CONFIG_MTD_NAND_DENALI_PCI) += denali_pci.o
-obj-$(CONFIG_MTD_NAND_DENALI_DT) += denali_dt.o
-obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o
-obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o
-obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o
-obj-$(CONFIG_MTD_NAND_TANGO) += tango_nand.o
-obj-$(CONFIG_MTD_NAND_DAVINCI) += davinci_nand.o
-obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o
-obj-$(CONFIG_MTD_NAND_DOCG4) += docg4.o
-obj-$(CONFIG_MTD_NAND_FSMC) += fsmc_nand.o
-obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o
-obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o
-obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o
-obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o
-obj-$(CONFIG_MTD_NAND_ATMEL) += atmel/
-obj-$(CONFIG_MTD_NAND_GPIO) += gpio.o
-omap2_nand-objs := omap2.o
-obj-$(CONFIG_MTD_NAND_OMAP2) += omap2_nand.o
-obj-$(CONFIG_MTD_NAND_OMAP_BCH_BUILD) += omap_elm.o
-obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o
-obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o
-obj-$(CONFIG_MTD_NAND_MARVELL) += marvell_nand.o
-obj-$(CONFIG_MTD_NAND_TMIO) += tmio_nand.o
-obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o
-obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o
-obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o
-obj-$(CONFIG_MTD_NAND_OXNAS) += oxnas_nand.o
-obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o
-obj-$(CONFIG_MTD_NAND_FSL_IFC) += fsl_ifc_nand.o
-obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o
-obj-$(CONFIG_MTD_NAND_SLC_LPC32XX) += lpc32xx_slc.o
-obj-$(CONFIG_MTD_NAND_MLC_LPC32XX) += lpc32xx_mlc.o
-obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o
-obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o
-obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o
-obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o
-obj-$(CONFIG_MTD_NAND_NUC900) += nuc900_nand.o
-obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o
-obj-$(CONFIG_MTD_NAND_VF610_NFC) += vf610_nfc.o
-obj-$(CONFIG_MTD_NAND_RICOH) += r852.o
-obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o
-obj-$(CONFIG_MTD_NAND_JZ4780) += jz4780_nand.o jz4780_bch.o
-obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/
-obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o
-obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/
-obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o
-obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o
-obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/
-obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o
-obj-$(CONFIG_MTD_NAND_MTK) += mtk_ecc.o mtk_nand.o
-
-nand-objs := nand_base.o nand_bbt.o nand_timings.o nand_ids.o
-nand-objs += nand_amd.o
-nand-objs += nand_hynix.o
-nand-objs += nand_macronix.o
-nand-objs += nand_micron.o
-nand-objs += nand_samsung.o
-nand-objs += nand_toshiba.o
+obj-y += onenand/
+obj-y += raw/
diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c
deleted file mode 100644
index d60ada45c549..000000000000
--- a/drivers/mtd/nand/ams-delta.c
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
- * drivers/mtd/nand/ams-delta.c
- *
- * Copyright (C) 2006 Jonathan McDowell <noodles@earth.li>
- *
- * Derived from drivers/mtd/toto.c
- * Converted to platform driver by Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
- * Partially stolen from drivers/mtd/nand/plat_nand.c
- *
- * 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.
- *
- * Overview:
- * This is a device driver for the NAND flash device found on the
- * Amstrad E3 (Delta).
- */
-
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/partitions.h>
-#include <linux/gpio.h>
-#include <linux/platform_data/gpio-omap.h>
-
-#include <asm/io.h>
-#include <asm/sizes.h>
-
-#include <mach/board-ams-delta.h>
-
-#include <mach/hardware.h>
-
-/*
- * MTD structure for E3 (Delta)
- */
-static struct mtd_info *ams_delta_mtd = NULL;
-
-/*
- * Define partitions for flash devices
- */
-
-static const struct mtd_partition partition_info[] = {
- { .name = "Kernel",
- .offset = 0,
- .size = 3 * SZ_1M + SZ_512K },
- { .name = "u-boot",
- .offset = 3 * SZ_1M + SZ_512K,
- .size = SZ_256K },
- { .name = "u-boot params",
- .offset = 3 * SZ_1M + SZ_512K + SZ_256K,
- .size = SZ_256K },
- { .name = "Amstrad LDR",
- .offset = 4 * SZ_1M,
- .size = SZ_256K },
- { .name = "File system",
- .offset = 4 * SZ_1M + 1 * SZ_256K,
- .size = 27 * SZ_1M },
- { .name = "PBL reserved",
- .offset = 32 * SZ_1M - 3 * SZ_256K,
- .size = 3 * SZ_256K },
-};
-
-static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- void __iomem *io_base = (void __iomem *)nand_get_controller_data(this);
-
- writew(0, io_base + OMAP_MPUIO_IO_CNTL);
- writew(byte, this->IO_ADDR_W);
- gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NWE, 0);
- ndelay(40);
- gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NWE, 1);
-}
-
-static u_char ams_delta_read_byte(struct mtd_info *mtd)
-{
- u_char res;
- struct nand_chip *this = mtd_to_nand(mtd);
- void __iomem *io_base = (void __iomem *)nand_get_controller_data(this);
-
- gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NRE, 0);
- ndelay(40);
- writew(~0, io_base + OMAP_MPUIO_IO_CNTL);
- res = readw(this->IO_ADDR_R);
- gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NRE, 1);
-
- return res;
-}
-
-static void ams_delta_write_buf(struct mtd_info *mtd, const u_char *buf,
- int len)
-{
- int i;
-
- for (i=0; i<len; i++)
- ams_delta_write_byte(mtd, buf[i]);
-}
-
-static void ams_delta_read_buf(struct mtd_info *mtd, u_char *buf, int len)
-{
- int i;
-
- for (i=0; i<len; i++)
- buf[i] = ams_delta_read_byte(mtd);
-}
-
-/*
- * Command control function
- *
- * ctrl:
- * NAND_NCE: bit 0 -> bit 2
- * NAND_CLE: bit 1 -> bit 7
- * NAND_ALE: bit 2 -> bit 6
- */
-static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd,
- unsigned int ctrl)
-{
-
- if (ctrl & NAND_CTRL_CHANGE) {
- gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NCE,
- (ctrl & NAND_NCE) == 0);
- gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_CLE,
- (ctrl & NAND_CLE) != 0);
- gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_ALE,
- (ctrl & NAND_ALE) != 0);
- }
-
- if (cmd != NAND_CMD_NONE)
- ams_delta_write_byte(mtd, cmd);
-}
-
-static int ams_delta_nand_ready(struct mtd_info *mtd)
-{
- return gpio_get_value(AMS_DELTA_GPIO_PIN_NAND_RB);
-}
-
-static const struct gpio _mandatory_gpio[] = {
- {
- .gpio = AMS_DELTA_GPIO_PIN_NAND_NCE,
- .flags = GPIOF_OUT_INIT_HIGH,
- .label = "nand_nce",
- },
- {
- .gpio = AMS_DELTA_GPIO_PIN_NAND_NRE,
- .flags = GPIOF_OUT_INIT_HIGH,
- .label = "nand_nre",
- },
- {
- .gpio = AMS_DELTA_GPIO_PIN_NAND_NWP,
- .flags = GPIOF_OUT_INIT_HIGH,
- .label = "nand_nwp",
- },
- {
- .gpio = AMS_DELTA_GPIO_PIN_NAND_NWE,
- .flags = GPIOF_OUT_INIT_HIGH,
- .label = "nand_nwe",
- },
- {
- .gpio = AMS_DELTA_GPIO_PIN_NAND_ALE,
- .flags = GPIOF_OUT_INIT_LOW,
- .label = "nand_ale",
- },
- {
- .gpio = AMS_DELTA_GPIO_PIN_NAND_CLE,
- .flags = GPIOF_OUT_INIT_LOW,
- .label = "nand_cle",
- },
-};
-
-/*
- * Main initialization routine
- */
-static int ams_delta_init(struct platform_device *pdev)
-{
- struct nand_chip *this;
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- void __iomem *io_base;
- int err = 0;
-
- if (!res)
- return -ENXIO;
-
- /* Allocate memory for MTD device structure and private data */
- this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
- if (!this) {
- printk (KERN_WARNING "Unable to allocate E3 NAND MTD device structure.\n");
- err = -ENOMEM;
- goto out;
- }
-
- ams_delta_mtd = nand_to_mtd(this);
- ams_delta_mtd->owner = THIS_MODULE;
-
- /*
- * Don't try to request the memory region from here,
- * it should have been already requested from the
- * gpio-omap driver and requesting it again would fail.
- */
-
- io_base = ioremap(res->start, resource_size(res));
- if (io_base == NULL) {
- dev_err(&pdev->dev, "ioremap failed\n");
- err = -EIO;
- goto out_free;
- }
-
- nand_set_controller_data(this, (void *)io_base);
-
- /* Set address of NAND IO lines */
- this->IO_ADDR_R = io_base + OMAP_MPUIO_INPUT_LATCH;
- this->IO_ADDR_W = io_base + OMAP_MPUIO_OUTPUT;
- this->read_byte = ams_delta_read_byte;
- this->write_buf = ams_delta_write_buf;
- this->read_buf = ams_delta_read_buf;
- this->cmd_ctrl = ams_delta_hwcontrol;
- if (gpio_request(AMS_DELTA_GPIO_PIN_NAND_RB, "nand_rdy") == 0) {
- this->dev_ready = ams_delta_nand_ready;
- } else {
- this->dev_ready = NULL;
- printk(KERN_NOTICE "Couldn't request gpio for Delta NAND ready.\n");
- }
- /* 25 us command delay time */
- this->chip_delay = 30;
- this->ecc.mode = NAND_ECC_SOFT;
- this->ecc.algo = NAND_ECC_HAMMING;
-
- platform_set_drvdata(pdev, io_base);
-
- /* Set chip enabled, but */
- err = gpio_request_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio));
- if (err)
- goto out_gpio;
-
- /* Scan to find existence of the device */
- err = nand_scan(ams_delta_mtd, 1);
- if (err)
- goto out_mtd;
-
- /* Register the partitions */
- mtd_device_register(ams_delta_mtd, partition_info,
- ARRAY_SIZE(partition_info));
-
- goto out;
-
- out_mtd:
- gpio_free_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio));
-out_gpio:
- gpio_free(AMS_DELTA_GPIO_PIN_NAND_RB);
- iounmap(io_base);
-out_free:
- kfree(this);
- out:
- return err;
-}
-
-/*
- * Clean up routine
- */
-static int ams_delta_cleanup(struct platform_device *pdev)
-{
- void __iomem *io_base = platform_get_drvdata(pdev);
-
- /* Release resources, unregister device */
- nand_release(ams_delta_mtd);
-
- gpio_free_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio));
- gpio_free(AMS_DELTA_GPIO_PIN_NAND_RB);
- iounmap(io_base);
-
- /* Free the MTD device structure */
- kfree(mtd_to_nand(ams_delta_mtd));
-
- return 0;
-}
-
-static struct platform_driver ams_delta_nand_driver = {
- .probe = ams_delta_init,
- .remove = ams_delta_cleanup,
- .driver = {
- .name = "ams-delta-nand",
- },
-};
-
-module_platform_driver(ams_delta_nand_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Jonathan McDowell <noodles@earth.li>");
-MODULE_DESCRIPTION("Glue layer for NAND flash on Amstrad E3 (Delta)");
diff --git a/drivers/mtd/nand/atmel/nand-controller.c b/drivers/mtd/nand/atmel/nand-controller.c
deleted file mode 100644
index b2f00b398490..000000000000
--- a/drivers/mtd/nand/atmel/nand-controller.c
+++ /dev/null
@@ -1,2565 +0,0 @@
-/*
- * Copyright 2017 ATMEL
- * Copyright 2017 Free Electrons
- *
- * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
- *
- * Derived from the atmel_nand.c driver which contained the following
- * copyrights:
- *
- * Copyright 2003 Rick Bronson
- *
- * Derived from drivers/mtd/nand/autcpu12.c
- * Copyright 2001 Thomas Gleixner (gleixner@autronix.de)
- *
- * Derived from drivers/mtd/spia.c
- * Copyright 2000 Steven J. Hill (sjhill@cotw.com)
- *
- *
- * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263
- * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright 2007
- *
- * Derived from Das U-Boot source code
- * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c)
- * Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas
- *
- * Add Programmable Multibit ECC support for various AT91 SoC
- * Copyright 2012 ATMEL, Hong Xu
- *
- * Add Nand Flash Controller support for SAMA5 SoC
- * Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.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.
- *
- * A few words about the naming convention in this file. This convention
- * applies to structure and function names.
- *
- * Prefixes:
- *
- * - atmel_nand_: all generic structures/functions
- * - atmel_smc_nand_: all structures/functions specific to the SMC interface
- * (at91sam9 and avr32 SoCs)
- * - atmel_hsmc_nand_: all structures/functions specific to the HSMC interface
- * (sama5 SoCs and later)
- * - atmel_nfc_: all structures/functions used to manipulate the NFC sub-block
- * that is available in the HSMC block
- * - <soc>_nand_: all SoC specific structures/functions
- */
-
-#include <linux/clk.h>
-#include <linux/dma-mapping.h>
-#include <linux/dmaengine.h>
-#include <linux/genalloc.h>
-#include <linux/gpio.h>
-#include <linux/gpio/consumer.h>
-#include <linux/interrupt.h>
-#include <linux/mfd/syscon.h>
-#include <linux/mfd/syscon/atmel-matrix.h>
-#include <linux/mfd/syscon/atmel-smc.h>
-#include <linux/module.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/of_platform.h>
-#include <linux/iopoll.h>
-#include <linux/platform_device.h>
-#include <linux/regmap.h>
-
-#include "pmecc.h"
-
-#define ATMEL_HSMC_NFC_CFG 0x0
-#define ATMEL_HSMC_NFC_CFG_SPARESIZE(x) (((x) / 4) << 24)
-#define ATMEL_HSMC_NFC_CFG_SPARESIZE_MASK GENMASK(30, 24)
-#define ATMEL_HSMC_NFC_CFG_DTO(cyc, mul) (((cyc) << 16) | ((mul) << 20))
-#define ATMEL_HSMC_NFC_CFG_DTO_MAX GENMASK(22, 16)
-#define ATMEL_HSMC_NFC_CFG_RBEDGE BIT(13)
-#define ATMEL_HSMC_NFC_CFG_FALLING_EDGE BIT(12)
-#define ATMEL_HSMC_NFC_CFG_RSPARE BIT(9)
-#define ATMEL_HSMC_NFC_CFG_WSPARE BIT(8)
-#define ATMEL_HSMC_NFC_CFG_PAGESIZE_MASK GENMASK(2, 0)
-#define ATMEL_HSMC_NFC_CFG_PAGESIZE(x) (fls((x) / 512) - 1)
-
-#define ATMEL_HSMC_NFC_CTRL 0x4
-#define ATMEL_HSMC_NFC_CTRL_EN BIT(0)
-#define ATMEL_HSMC_NFC_CTRL_DIS BIT(1)
-
-#define ATMEL_HSMC_NFC_SR 0x8
-#define ATMEL_HSMC_NFC_IER 0xc
-#define ATMEL_HSMC_NFC_IDR 0x10
-#define ATMEL_HSMC_NFC_IMR 0x14
-#define ATMEL_HSMC_NFC_SR_ENABLED BIT(1)
-#define ATMEL_HSMC_NFC_SR_RB_RISE BIT(4)
-#define ATMEL_HSMC_NFC_SR_RB_FALL BIT(5)
-#define ATMEL_HSMC_NFC_SR_BUSY BIT(8)
-#define ATMEL_HSMC_NFC_SR_WR BIT(11)
-#define ATMEL_HSMC_NFC_SR_CSID GENMASK(14, 12)
-#define ATMEL_HSMC_NFC_SR_XFRDONE BIT(16)
-#define ATMEL_HSMC_NFC_SR_CMDDONE BIT(17)
-#define ATMEL_HSMC_NFC_SR_DTOE BIT(20)
-#define ATMEL_HSMC_NFC_SR_UNDEF BIT(21)
-#define ATMEL_HSMC_NFC_SR_AWB BIT(22)
-#define ATMEL_HSMC_NFC_SR_NFCASE BIT(23)
-#define ATMEL_HSMC_NFC_SR_ERRORS (ATMEL_HSMC_NFC_SR_DTOE | \
- ATMEL_HSMC_NFC_SR_UNDEF | \
- ATMEL_HSMC_NFC_SR_AWB | \
- ATMEL_HSMC_NFC_SR_NFCASE)
-#define ATMEL_HSMC_NFC_SR_RBEDGE(x) BIT((x) + 24)
-
-#define ATMEL_HSMC_NFC_ADDR 0x18
-#define ATMEL_HSMC_NFC_BANK 0x1c
-
-#define ATMEL_NFC_MAX_RB_ID 7
-
-#define ATMEL_NFC_SRAM_SIZE 0x2400
-
-#define ATMEL_NFC_CMD(pos, cmd) ((cmd) << (((pos) * 8) + 2))
-#define ATMEL_NFC_VCMD2 BIT(18)
-#define ATMEL_NFC_ACYCLE(naddrs) ((naddrs) << 19)
-#define ATMEL_NFC_CSID(cs) ((cs) << 22)
-#define ATMEL_NFC_DATAEN BIT(25)
-#define ATMEL_NFC_NFCWR BIT(26)
-
-#define ATMEL_NFC_MAX_ADDR_CYCLES 5
-
-#define ATMEL_NAND_ALE_OFFSET BIT(21)
-#define ATMEL_NAND_CLE_OFFSET BIT(22)
-
-#define DEFAULT_TIMEOUT_MS 1000
-#define MIN_DMA_LEN 128
-
-enum atmel_nand_rb_type {
- ATMEL_NAND_NO_RB,
- ATMEL_NAND_NATIVE_RB,
- ATMEL_NAND_GPIO_RB,
-};
-
-struct atmel_nand_rb {
- enum atmel_nand_rb_type type;
- union {
- struct gpio_desc *gpio;
- int id;
- };
-};
-
-struct atmel_nand_cs {
- int id;
- struct atmel_nand_rb rb;
- struct gpio_desc *csgpio;
- struct {
- void __iomem *virt;
- dma_addr_t dma;
- } io;
-
- struct atmel_smc_cs_conf smcconf;
-};
-
-struct atmel_nand {
- struct list_head node;
- struct device *dev;
- struct nand_chip base;
- struct atmel_nand_cs *activecs;
- struct atmel_pmecc_user *pmecc;
- struct gpio_desc *cdgpio;
- int numcs;
- struct atmel_nand_cs cs[];
-};
-
-static inline struct atmel_nand *to_atmel_nand(struct nand_chip *chip)
-{
- return container_of(chip, struct atmel_nand, base);
-}
-
-enum atmel_nfc_data_xfer {
- ATMEL_NFC_NO_DATA,
- ATMEL_NFC_READ_DATA,
- ATMEL_NFC_WRITE_DATA,
-};
-
-struct atmel_nfc_op {
- u8 cs;
- u8 ncmds;
- u8 cmds[2];
- u8 naddrs;
- u8 addrs[5];
- enum atmel_nfc_data_xfer data;
- u32 wait;
- u32 errors;
-};
-
-struct atmel_nand_controller;
-struct atmel_nand_controller_caps;
-
-struct atmel_nand_controller_ops {
- int (*probe)(struct platform_device *pdev,
- const struct atmel_nand_controller_caps *caps);
- int (*remove)(struct atmel_nand_controller *nc);
- void (*nand_init)(struct atmel_nand_controller *nc,
- struct atmel_nand *nand);
- int (*ecc_init)(struct atmel_nand *nand);
- int (*setup_data_interface)(struct atmel_nand *nand, int csline,
- const struct nand_data_interface *conf);
-};
-
-struct atmel_nand_controller_caps {
- bool has_dma;
- bool legacy_of_bindings;
- u32 ale_offs;
- u32 cle_offs;
- const struct atmel_nand_controller_ops *ops;
-};
-
-struct atmel_nand_controller {
- struct nand_hw_control base;
- const struct atmel_nand_controller_caps *caps;
- struct device *dev;
- struct regmap *smc;
- struct dma_chan *dmac;
- struct atmel_pmecc *pmecc;
- struct list_head chips;
- struct clk *mck;
-};
-
-static inline struct atmel_nand_controller *
-to_nand_controller(struct nand_hw_control *ctl)
-{
- return container_of(ctl, struct atmel_nand_controller, base);
-}
-
-struct atmel_smc_nand_controller {
- struct atmel_nand_controller base;
- struct regmap *matrix;
- unsigned int ebi_csa_offs;
-};
-
-static inline struct atmel_smc_nand_controller *
-to_smc_nand_controller(struct nand_hw_control *ctl)
-{
- return container_of(to_nand_controller(ctl),
- struct atmel_smc_nand_controller, base);
-}
-
-struct atmel_hsmc_nand_controller {
- struct atmel_nand_controller base;
- struct {
- struct gen_pool *pool;
- void __iomem *virt;
- dma_addr_t dma;
- } sram;
- const struct atmel_hsmc_reg_layout *hsmc_layout;
- struct regmap *io;
- struct atmel_nfc_op op;
- struct completion complete;
- int irq;
-
- /* Only used when instantiating from legacy DT bindings. */
- struct clk *clk;
-};
-
-static inline struct atmel_hsmc_nand_controller *
-to_hsmc_nand_controller(struct nand_hw_control *ctl)
-{
- return container_of(to_nand_controller(ctl),
- struct atmel_hsmc_nand_controller, base);
-}
-
-static bool atmel_nfc_op_done(struct atmel_nfc_op *op, u32 status)
-{
- op->errors |= status & ATMEL_HSMC_NFC_SR_ERRORS;
- op->wait ^= status & op->wait;
-
- return !op->wait || op->errors;
-}
-
-static irqreturn_t atmel_nfc_interrupt(int irq, void *data)
-{
- struct atmel_hsmc_nand_controller *nc = data;
- u32 sr, rcvd;
- bool done;
-
- regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &sr);
-
- rcvd = sr & (nc->op.wait | ATMEL_HSMC_NFC_SR_ERRORS);
- done = atmel_nfc_op_done(&nc->op, sr);
-
- if (rcvd)
- regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, rcvd);
-
- if (done)
- complete(&nc->complete);
-
- return rcvd ? IRQ_HANDLED : IRQ_NONE;
-}
-
-static int atmel_nfc_wait(struct atmel_hsmc_nand_controller *nc, bool poll,
- unsigned int timeout_ms)
-{
- int ret;
-
- if (!timeout_ms)
- timeout_ms = DEFAULT_TIMEOUT_MS;
-
- if (poll) {
- u32 status;
-
- ret = regmap_read_poll_timeout(nc->base.smc,
- ATMEL_HSMC_NFC_SR, status,
- atmel_nfc_op_done(&nc->op,
- status),
- 0, timeout_ms * 1000);
- } else {
- init_completion(&nc->complete);
- regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IER,
- nc->op.wait | ATMEL_HSMC_NFC_SR_ERRORS);
- ret = wait_for_completion_timeout(&nc->complete,
- msecs_to_jiffies(timeout_ms));
- if (!ret)
- ret = -ETIMEDOUT;
- else
- ret = 0;
-
- regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, 0xffffffff);
- }
-
- if (nc->op.errors & ATMEL_HSMC_NFC_SR_DTOE) {
- dev_err(nc->base.dev, "Waiting NAND R/B Timeout\n");
- ret = -ETIMEDOUT;
- }
-
- if (nc->op.errors & ATMEL_HSMC_NFC_SR_UNDEF) {
- dev_err(nc->base.dev, "Access to an undefined area\n");
- ret = -EIO;
- }
-
- if (nc->op.errors & ATMEL_HSMC_NFC_SR_AWB) {
- dev_err(nc->base.dev, "Access while busy\n");
- ret = -EIO;
- }
-
- if (nc->op.errors & ATMEL_HSMC_NFC_SR_NFCASE) {
- dev_err(nc->base.dev, "Wrong access size\n");
- ret = -EIO;
- }
-
- return ret;
-}
-
-static void atmel_nand_dma_transfer_finished(void *data)
-{
- struct completion *finished = data;
-
- complete(finished);
-}
-
-static int atmel_nand_dma_transfer(struct atmel_nand_controller *nc,
- void *buf, dma_addr_t dev_dma, size_t len,
- enum dma_data_direction dir)
-{
- DECLARE_COMPLETION_ONSTACK(finished);
- dma_addr_t src_dma, dst_dma, buf_dma;
- struct dma_async_tx_descriptor *tx;
- dma_cookie_t cookie;
-
- buf_dma = dma_map_single(nc->dev, buf, len, dir);
- if (dma_mapping_error(nc->dev, dev_dma)) {
- dev_err(nc->dev,
- "Failed to prepare a buffer for DMA access\n");
- goto err;
- }
-
- if (dir == DMA_FROM_DEVICE) {
- src_dma = dev_dma;
- dst_dma = buf_dma;
- } else {
- src_dma = buf_dma;
- dst_dma = dev_dma;
- }
-
- tx = dmaengine_prep_dma_memcpy(nc->dmac, dst_dma, src_dma, len,
- DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
- if (!tx) {
- dev_err(nc->dev, "Failed to prepare DMA memcpy\n");
- goto err_unmap;
- }
-
- tx->callback = atmel_nand_dma_transfer_finished;
- tx->callback_param = &finished;
-
- cookie = dmaengine_submit(tx);
- if (dma_submit_error(cookie)) {
- dev_err(nc->dev, "Failed to do DMA tx_submit\n");
- goto err_unmap;
- }
-
- dma_async_issue_pending(nc->dmac);
- wait_for_completion(&finished);
-
- return 0;
-
-err_unmap:
- dma_unmap_single(nc->dev, buf_dma, len, dir);
-
-err:
- dev_dbg(nc->dev, "Fall back to CPU I/O\n");
-
- return -EIO;
-}
-
-static u8 atmel_nand_read_byte(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct atmel_nand *nand = to_atmel_nand(chip);
-
- return ioread8(nand->activecs->io.virt);
-}
-
-static u16 atmel_nand_read_word(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct atmel_nand *nand = to_atmel_nand(chip);
-
- return ioread16(nand->activecs->io.virt);
-}
-
-static void atmel_nand_write_byte(struct mtd_info *mtd, u8 byte)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct atmel_nand *nand = to_atmel_nand(chip);
-
- if (chip->options & NAND_BUSWIDTH_16)
- iowrite16(byte | (byte << 8), nand->activecs->io.virt);
- else
- iowrite8(byte, nand->activecs->io.virt);
-}
-
-static void atmel_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct atmel_nand *nand = to_atmel_nand(chip);
- struct atmel_nand_controller *nc;
-
- nc = to_nand_controller(chip->controller);
-
- /*
- * If the controller supports DMA, the buffer address is DMA-able and
- * len is long enough to make DMA transfers profitable, let's trigger
- * a DMA transfer. If it fails, fallback to PIO mode.
- */
- if (nc->dmac && virt_addr_valid(buf) &&
- len >= MIN_DMA_LEN &&
- !atmel_nand_dma_transfer(nc, buf, nand->activecs->io.dma, len,
- DMA_FROM_DEVICE))
- return;
-
- if (chip->options & NAND_BUSWIDTH_16)
- ioread16_rep(nand->activecs->io.virt, buf, len / 2);
- else
- ioread8_rep(nand->activecs->io.virt, buf, len);
-}
-
-static void atmel_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct atmel_nand *nand = to_atmel_nand(chip);
- struct atmel_nand_controller *nc;
-
- nc = to_nand_controller(chip->controller);
-
- /*
- * If the controller supports DMA, the buffer address is DMA-able and
- * len is long enough to make DMA transfers profitable, let's trigger
- * a DMA transfer. If it fails, fallback to PIO mode.
- */
- if (nc->dmac && virt_addr_valid(buf) &&
- len >= MIN_DMA_LEN &&
- !atmel_nand_dma_transfer(nc, (void *)buf, nand->activecs->io.dma,
- len, DMA_TO_DEVICE))
- return;
-
- if (chip->options & NAND_BUSWIDTH_16)
- iowrite16_rep(nand->activecs->io.virt, buf, len / 2);
- else
- iowrite8_rep(nand->activecs->io.virt, buf, len);
-}
-
-static int atmel_nand_dev_ready(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct atmel_nand *nand = to_atmel_nand(chip);
-
- return gpiod_get_value(nand->activecs->rb.gpio);
-}
-
-static void atmel_nand_select_chip(struct mtd_info *mtd, int cs)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct atmel_nand *nand = to_atmel_nand(chip);
-
- if (cs < 0 || cs >= nand->numcs) {
- nand->activecs = NULL;
- chip->dev_ready = NULL;
- return;
- }
-
- nand->activecs = &nand->cs[cs];
-
- if (nand->activecs->rb.type == ATMEL_NAND_GPIO_RB)
- chip->dev_ready = atmel_nand_dev_ready;
-}
-
-static int atmel_hsmc_nand_dev_ready(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct atmel_nand *nand = to_atmel_nand(chip);
- struct atmel_hsmc_nand_controller *nc;
- u32 status;
-
- nc = to_hsmc_nand_controller(chip->controller);
-
- regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &status);
-
- return status & ATMEL_HSMC_NFC_SR_RBEDGE(nand->activecs->rb.id);
-}
-
-static void atmel_hsmc_nand_select_chip(struct mtd_info *mtd, int cs)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct atmel_nand *nand = to_atmel_nand(chip);
- struct atmel_hsmc_nand_controller *nc;
-
- nc = to_hsmc_nand_controller(chip->controller);
-
- atmel_nand_select_chip(mtd, cs);
-
- if (!nand->activecs) {
- regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CTRL,
- ATMEL_HSMC_NFC_CTRL_DIS);
- return;
- }
-
- if (nand->activecs->rb.type == ATMEL_NAND_NATIVE_RB)
- chip->dev_ready = atmel_hsmc_nand_dev_ready;
-
- regmap_update_bits(nc->base.smc, ATMEL_HSMC_NFC_CFG,
- ATMEL_HSMC_NFC_CFG_PAGESIZE_MASK |
- ATMEL_HSMC_NFC_CFG_SPARESIZE_MASK |
- ATMEL_HSMC_NFC_CFG_RSPARE |
- ATMEL_HSMC_NFC_CFG_WSPARE,
- ATMEL_HSMC_NFC_CFG_PAGESIZE(mtd->writesize) |
- ATMEL_HSMC_NFC_CFG_SPARESIZE(mtd->oobsize) |
- ATMEL_HSMC_NFC_CFG_RSPARE);
- regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CTRL,
- ATMEL_HSMC_NFC_CTRL_EN);
-}
-
-static int atmel_nfc_exec_op(struct atmel_hsmc_nand_controller *nc, bool poll)
-{
- u8 *addrs = nc->op.addrs;
- unsigned int op = 0;
- u32 addr, val;
- int i, ret;
-
- nc->op.wait = ATMEL_HSMC_NFC_SR_CMDDONE;
-
- for (i = 0; i < nc->op.ncmds; i++)
- op |= ATMEL_NFC_CMD(i, nc->op.cmds[i]);
-
- if (nc->op.naddrs == ATMEL_NFC_MAX_ADDR_CYCLES)
- regmap_write(nc->base.smc, ATMEL_HSMC_NFC_ADDR, *addrs++);
-
- op |= ATMEL_NFC_CSID(nc->op.cs) |
- ATMEL_NFC_ACYCLE(nc->op.naddrs);
-
- if (nc->op.ncmds > 1)
- op |= ATMEL_NFC_VCMD2;
-
- addr = addrs[0] | (addrs[1] << 8) | (addrs[2] << 16) |
- (addrs[3] << 24);
-
- if (nc->op.data != ATMEL_NFC_NO_DATA) {
- op |= ATMEL_NFC_DATAEN;
- nc->op.wait |= ATMEL_HSMC_NFC_SR_XFRDONE;
-
- if (nc->op.data == ATMEL_NFC_WRITE_DATA)
- op |= ATMEL_NFC_NFCWR;
- }
-
- /* Clear all flags. */
- regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &val);
-
- /* Send the command. */
- regmap_write(nc->io, op, addr);
-
- ret = atmel_nfc_wait(nc, poll, 0);
- if (ret)
- dev_err(nc->base.dev,
- "Failed to send NAND command (err = %d)!",
- ret);
-
- /* Reset the op state. */
- memset(&nc->op, 0, sizeof(nc->op));
-
- return ret;
-}
-
-static void atmel_hsmc_nand_cmd_ctrl(struct mtd_info *mtd, int dat,
- unsigned int ctrl)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct atmel_nand *nand = to_atmel_nand(chip);
- struct atmel_hsmc_nand_controller *nc;
-
- nc = to_hsmc_nand_controller(chip->controller);
-
- if (ctrl & NAND_ALE) {
- if (nc->op.naddrs == ATMEL_NFC_MAX_ADDR_CYCLES)
- return;
-
- nc->op.addrs[nc->op.naddrs++] = dat;
- } else if (ctrl & NAND_CLE) {
- if (nc->op.ncmds > 1)
- return;
-
- nc->op.cmds[nc->op.ncmds++] = dat;
- }
-
- if (dat == NAND_CMD_NONE) {
- nc->op.cs = nand->activecs->id;
- atmel_nfc_exec_op(nc, true);
- }
-}
-
-static void atmel_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
- unsigned int ctrl)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct atmel_nand *nand = to_atmel_nand(chip);
- struct atmel_nand_controller *nc;
-
- nc = to_nand_controller(chip->controller);
-
- if ((ctrl & NAND_CTRL_CHANGE) && nand->activecs->csgpio) {
- if (ctrl & NAND_NCE)
- gpiod_set_value(nand->activecs->csgpio, 0);
- else
- gpiod_set_value(nand->activecs->csgpio, 1);
- }
-
- if (ctrl & NAND_ALE)
- writeb(cmd, nand->activecs->io.virt + nc->caps->ale_offs);
- else if (ctrl & NAND_CLE)
- writeb(cmd, nand->activecs->io.virt + nc->caps->cle_offs);
-}
-
-static void atmel_nfc_copy_to_sram(struct nand_chip *chip, const u8 *buf,
- bool oob_required)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct atmel_hsmc_nand_controller *nc;
- int ret = -EIO;
-
- nc = to_hsmc_nand_controller(chip->controller);
-
- if (nc->base.dmac)
- ret = atmel_nand_dma_transfer(&nc->base, (void *)buf,
- nc->sram.dma, mtd->writesize,
- DMA_TO_DEVICE);
-
- /* Falling back to CPU copy. */
- if (ret)
- memcpy_toio(nc->sram.virt, buf, mtd->writesize);
-
- if (oob_required)
- memcpy_toio(nc->sram.virt + mtd->writesize, chip->oob_poi,
- mtd->oobsize);
-}
-
-static void atmel_nfc_copy_from_sram(struct nand_chip *chip, u8 *buf,
- bool oob_required)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct atmel_hsmc_nand_controller *nc;
- int ret = -EIO;
-
- nc = to_hsmc_nand_controller(chip->controller);
-
- if (nc->base.dmac)
- ret = atmel_nand_dma_transfer(&nc->base, buf, nc->sram.dma,
- mtd->writesize, DMA_FROM_DEVICE);
-
- /* Falling back to CPU copy. */
- if (ret)
- memcpy_fromio(buf, nc->sram.virt, mtd->writesize);
-
- if (oob_required)
- memcpy_fromio(chip->oob_poi, nc->sram.virt + mtd->writesize,
- mtd->oobsize);
-}
-
-static void atmel_nfc_set_op_addr(struct nand_chip *chip, int page, int column)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct atmel_hsmc_nand_controller *nc;
-
- nc = to_hsmc_nand_controller(chip->controller);
-
- if (column >= 0) {
- nc->op.addrs[nc->op.naddrs++] = column;
-
- /*
- * 2 address cycles for the column offset on large page NANDs.
- */
- if (mtd->writesize > 512)
- nc->op.addrs[nc->op.naddrs++] = column >> 8;
- }
-
- if (page >= 0) {
- nc->op.addrs[nc->op.naddrs++] = page;
- nc->op.addrs[nc->op.naddrs++] = page >> 8;
-
- if (chip->options & NAND_ROW_ADDR_3)
- nc->op.addrs[nc->op.naddrs++] = page >> 16;
- }
-}
-
-static int atmel_nand_pmecc_enable(struct nand_chip *chip, int op, bool raw)
-{
- struct atmel_nand *nand = to_atmel_nand(chip);
- struct atmel_nand_controller *nc;
- int ret;
-
- nc = to_nand_controller(chip->controller);
-
- if (raw)
- return 0;
-
- ret = atmel_pmecc_enable(nand->pmecc, op);
- if (ret)
- dev_err(nc->dev,
- "Failed to enable ECC engine (err = %d)\n", ret);
-
- return ret;
-}
-
-static void atmel_nand_pmecc_disable(struct nand_chip *chip, bool raw)
-{
- struct atmel_nand *nand = to_atmel_nand(chip);
-
- if (!raw)
- atmel_pmecc_disable(nand->pmecc);
-}
-
-static int atmel_nand_pmecc_generate_eccbytes(struct nand_chip *chip, bool raw)
-{
- struct atmel_nand *nand = to_atmel_nand(chip);
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct atmel_nand_controller *nc;
- struct mtd_oob_region oobregion;
- void *eccbuf;
- int ret, i;
-
- nc = to_nand_controller(chip->controller);
-
- if (raw)
- return 0;
-
- ret = atmel_pmecc_wait_rdy(nand->pmecc);
- if (ret) {
- dev_err(nc->dev,
- "Failed to transfer NAND page data (err = %d)\n",
- ret);
- return ret;
- }
-
- mtd_ooblayout_ecc(mtd, 0, &oobregion);
- eccbuf = chip->oob_poi + oobregion.offset;
-
- for (i = 0; i < chip->ecc.steps; i++) {
- atmel_pmecc_get_generated_eccbytes(nand->pmecc, i,
- eccbuf);
- eccbuf += chip->ecc.bytes;
- }
-
- return 0;
-}
-
-static int atmel_nand_pmecc_correct_data(struct nand_chip *chip, void *buf,
- bool raw)
-{
- struct atmel_nand *nand = to_atmel_nand(chip);
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct atmel_nand_controller *nc;
- struct mtd_oob_region oobregion;
- int ret, i, max_bitflips = 0;
- void *databuf, *eccbuf;
-
- nc = to_nand_controller(chip->controller);
-
- if (raw)
- return 0;
-
- ret = atmel_pmecc_wait_rdy(nand->pmecc);
- if (ret) {
- dev_err(nc->dev,
- "Failed to read NAND page data (err = %d)\n",
- ret);
- return ret;
- }
-
- mtd_ooblayout_ecc(mtd, 0, &oobregion);
- eccbuf = chip->oob_poi + oobregion.offset;
- databuf = buf;
-
- for (i = 0; i < chip->ecc.steps; i++) {
- ret = atmel_pmecc_correct_sector(nand->pmecc, i, databuf,
- eccbuf);
- if (ret < 0 && !atmel_pmecc_correct_erased_chunks(nand->pmecc))
- ret = nand_check_erased_ecc_chunk(databuf,
- chip->ecc.size,
- eccbuf,
- chip->ecc.bytes,
- NULL, 0,
- chip->ecc.strength);
-
- if (ret >= 0)
- max_bitflips = max(ret, max_bitflips);
- else
- mtd->ecc_stats.failed++;
-
- databuf += chip->ecc.size;
- eccbuf += chip->ecc.bytes;
- }
-
- return max_bitflips;
-}
-
-static int atmel_nand_pmecc_write_pg(struct nand_chip *chip, const u8 *buf,
- bool oob_required, int page, bool raw)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct atmel_nand *nand = to_atmel_nand(chip);
- int ret;
-
- nand_prog_page_begin_op(chip, page, 0, NULL, 0);
-
- ret = atmel_nand_pmecc_enable(chip, NAND_ECC_WRITE, raw);
- if (ret)
- return ret;
-
- atmel_nand_write_buf(mtd, buf, mtd->writesize);
-
- ret = atmel_nand_pmecc_generate_eccbytes(chip, raw);
- if (ret) {
- atmel_pmecc_disable(nand->pmecc);
- return ret;
- }
-
- atmel_nand_pmecc_disable(chip, raw);
-
- atmel_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize);
-
- return nand_prog_page_end_op(chip);
-}
-
-static int atmel_nand_pmecc_write_page(struct mtd_info *mtd,
- struct nand_chip *chip, const u8 *buf,
- int oob_required, int page)
-{
- return atmel_nand_pmecc_write_pg(chip, buf, oob_required, page, false);
-}
-
-static int atmel_nand_pmecc_write_page_raw(struct mtd_info *mtd,
- struct nand_chip *chip,
- const u8 *buf, int oob_required,
- int page)
-{
- return atmel_nand_pmecc_write_pg(chip, buf, oob_required, page, true);
-}
-
-static int atmel_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf,
- bool oob_required, int page, bool raw)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- int ret;
-
- nand_read_page_op(chip, page, 0, NULL, 0);
-
- ret = atmel_nand_pmecc_enable(chip, NAND_ECC_READ, raw);
- if (ret)
- return ret;
-
- atmel_nand_read_buf(mtd, buf, mtd->writesize);
- atmel_nand_read_buf(mtd, chip->oob_poi, mtd->oobsize);
-
- ret = atmel_nand_pmecc_correct_data(chip, buf, raw);
-
- atmel_nand_pmecc_disable(chip, raw);
-
- return ret;
-}
-
-static int atmel_nand_pmecc_read_page(struct mtd_info *mtd,
- struct nand_chip *chip, u8 *buf,
- int oob_required, int page)
-{
- return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, false);
-}
-
-static int atmel_nand_pmecc_read_page_raw(struct mtd_info *mtd,
- struct nand_chip *chip, u8 *buf,
- int oob_required, int page)
-{
- return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, true);
-}
-
-static int atmel_hsmc_nand_pmecc_write_pg(struct nand_chip *chip,
- const u8 *buf, bool oob_required,
- int page, bool raw)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct atmel_nand *nand = to_atmel_nand(chip);
- struct atmel_hsmc_nand_controller *nc;
- int ret, status;
-
- nc = to_hsmc_nand_controller(chip->controller);
-
- atmel_nfc_copy_to_sram(chip, buf, false);
-
- nc->op.cmds[0] = NAND_CMD_SEQIN;
- nc->op.ncmds = 1;
- atmel_nfc_set_op_addr(chip, page, 0x0);
- nc->op.cs = nand->activecs->id;
- nc->op.data = ATMEL_NFC_WRITE_DATA;
-
- ret = atmel_nand_pmecc_enable(chip, NAND_ECC_WRITE, raw);
- if (ret)
- return ret;
-
- ret = atmel_nfc_exec_op(nc, false);
- if (ret) {
- atmel_nand_pmecc_disable(chip, raw);
- dev_err(nc->base.dev,
- "Failed to transfer NAND page data (err = %d)\n",
- ret);
- return ret;
- }
-
- ret = atmel_nand_pmecc_generate_eccbytes(chip, raw);
-
- atmel_nand_pmecc_disable(chip, raw);
-
- if (ret)
- return ret;
-
- atmel_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize);
-
- nc->op.cmds[0] = NAND_CMD_PAGEPROG;
- nc->op.ncmds = 1;
- nc->op.cs = nand->activecs->id;
- ret = atmel_nfc_exec_op(nc, false);
- if (ret)
- dev_err(nc->base.dev, "Failed to program NAND page (err = %d)\n",
- ret);
-
- status = chip->waitfunc(mtd, chip);
- if (status & NAND_STATUS_FAIL)
- return -EIO;
-
- return ret;
-}
-
-static int atmel_hsmc_nand_pmecc_write_page(struct mtd_info *mtd,
- struct nand_chip *chip,
- const u8 *buf, int oob_required,
- int page)
-{
- return atmel_hsmc_nand_pmecc_write_pg(chip, buf, oob_required, page,
- false);
-}
-
-static int atmel_hsmc_nand_pmecc_write_page_raw(struct mtd_info *mtd,
- struct nand_chip *chip,
- const u8 *buf,
- int oob_required, int page)
-{
- return atmel_hsmc_nand_pmecc_write_pg(chip, buf, oob_required, page,
- true);
-}
-
-static int atmel_hsmc_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf,
- bool oob_required, int page,
- bool raw)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct atmel_nand *nand = to_atmel_nand(chip);
- struct atmel_hsmc_nand_controller *nc;
- int ret;
-
- nc = to_hsmc_nand_controller(chip->controller);
-
- /*
- * Optimized read page accessors only work when the NAND R/B pin is
- * connected to a native SoC R/B pin. If that's not the case, fallback
- * to the non-optimized one.
- */
- if (nand->activecs->rb.type != ATMEL_NAND_NATIVE_RB) {
- nand_read_page_op(chip, page, 0, NULL, 0);
-
- return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page,
- raw);
- }
-
- nc->op.cmds[nc->op.ncmds++] = NAND_CMD_READ0;
-
- if (mtd->writesize > 512)
- nc->op.cmds[nc->op.ncmds++] = NAND_CMD_READSTART;
-
- atmel_nfc_set_op_addr(chip, page, 0x0);
- nc->op.cs = nand->activecs->id;
- nc->op.data = ATMEL_NFC_READ_DATA;
-
- ret = atmel_nand_pmecc_enable(chip, NAND_ECC_READ, raw);
- if (ret)
- return ret;
-
- ret = atmel_nfc_exec_op(nc, false);
- if (ret) {
- atmel_nand_pmecc_disable(chip, raw);
- dev_err(nc->base.dev,
- "Failed to load NAND page data (err = %d)\n",
- ret);
- return ret;
- }
-
- atmel_nfc_copy_from_sram(chip, buf, true);
-
- ret = atmel_nand_pmecc_correct_data(chip, buf, raw);
-
- atmel_nand_pmecc_disable(chip, raw);
-
- return ret;
-}
-
-static int atmel_hsmc_nand_pmecc_read_page(struct mtd_info *mtd,
- struct nand_chip *chip, u8 *buf,
- int oob_required, int page)
-{
- return atmel_hsmc_nand_pmecc_read_pg(chip, buf, oob_required, page,
- false);
-}
-
-static int atmel_hsmc_nand_pmecc_read_page_raw(struct mtd_info *mtd,
- struct nand_chip *chip,
- u8 *buf, int oob_required,
- int page)
-{
- return atmel_hsmc_nand_pmecc_read_pg(chip, buf, oob_required, page,
- true);
-}
-
-static int atmel_nand_pmecc_init(struct nand_chip *chip)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct atmel_nand *nand = to_atmel_nand(chip);
- struct atmel_nand_controller *nc;
- struct atmel_pmecc_user_req req;
-
- nc = to_nand_controller(chip->controller);
-
- if (!nc->pmecc) {
- dev_err(nc->dev, "HW ECC not supported\n");
- return -ENOTSUPP;
- }
-
- if (nc->caps->legacy_of_bindings) {
- u32 val;
-
- if (!of_property_read_u32(nc->dev->of_node, "atmel,pmecc-cap",
- &val))
- chip->ecc.strength = val;
-
- if (!of_property_read_u32(nc->dev->of_node,
- "atmel,pmecc-sector-size",
- &val))
- chip->ecc.size = val;
- }
-
- if (chip->ecc.options & NAND_ECC_MAXIMIZE)
- req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH;
- else if (chip->ecc.strength)
- req.ecc.strength = chip->ecc.strength;
- else if (chip->ecc_strength_ds)
- req.ecc.strength = chip->ecc_strength_ds;
- else
- req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH;
-
- if (chip->ecc.size)
- req.ecc.sectorsize = chip->ecc.size;
- else if (chip->ecc_step_ds)
- req.ecc.sectorsize = chip->ecc_step_ds;
- else
- req.ecc.sectorsize = ATMEL_PMECC_SECTOR_SIZE_AUTO;
-
- req.pagesize = mtd->writesize;
- req.oobsize = mtd->oobsize;
-
- if (mtd->writesize <= 512) {
- req.ecc.bytes = 4;
- req.ecc.ooboffset = 0;
- } else {
- req.ecc.bytes = mtd->oobsize - 2;
- req.ecc.ooboffset = ATMEL_PMECC_OOBOFFSET_AUTO;
- }
-
- nand->pmecc = atmel_pmecc_create_user(nc->pmecc, &req);
- if (IS_ERR(nand->pmecc))
- return PTR_ERR(nand->pmecc);
-
- chip->ecc.algo = NAND_ECC_BCH;
- chip->ecc.size = req.ecc.sectorsize;
- chip->ecc.bytes = req.ecc.bytes / req.ecc.nsectors;
- chip->ecc.strength = req.ecc.strength;
-
- chip->options |= NAND_NO_SUBPAGE_WRITE;
-
- mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
-
- return 0;
-}
-
-static int atmel_nand_ecc_init(struct atmel_nand *nand)
-{
- struct nand_chip *chip = &nand->base;
- struct atmel_nand_controller *nc;
- int ret;
-
- nc = to_nand_controller(chip->controller);
-
- switch (chip->ecc.mode) {
- case NAND_ECC_NONE:
- case NAND_ECC_SOFT:
- /*
- * Nothing to do, the core will initialize everything for us.
- */
- break;
-
- case NAND_ECC_HW:
- ret = atmel_nand_pmecc_init(chip);
- if (ret)
- return ret;
-
- chip->ecc.read_page = atmel_nand_pmecc_read_page;
- chip->ecc.write_page = atmel_nand_pmecc_write_page;
- chip->ecc.read_page_raw = atmel_nand_pmecc_read_page_raw;
- chip->ecc.write_page_raw = atmel_nand_pmecc_write_page_raw;
- break;
-
- default:
- /* Other modes are not supported. */
- dev_err(nc->dev, "Unsupported ECC mode: %d\n",
- chip->ecc.mode);
- return -ENOTSUPP;
- }
-
- return 0;
-}
-
-static int atmel_hsmc_nand_ecc_init(struct atmel_nand *nand)
-{
- struct nand_chip *chip = &nand->base;
- int ret;
-
- ret = atmel_nand_ecc_init(nand);
- if (ret)
- return ret;
-
- if (chip->ecc.mode != NAND_ECC_HW)
- return 0;
-
- /* Adjust the ECC operations for the HSMC IP. */
- chip->ecc.read_page = atmel_hsmc_nand_pmecc_read_page;
- chip->ecc.write_page = atmel_hsmc_nand_pmecc_write_page;
- chip->ecc.read_page_raw = atmel_hsmc_nand_pmecc_read_page_raw;
- chip->ecc.write_page_raw = atmel_hsmc_nand_pmecc_write_page_raw;
-
- return 0;
-}
-
-static int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand,
- const struct nand_data_interface *conf,
- struct atmel_smc_cs_conf *smcconf)
-{
- u32 ncycles, totalcycles, timeps, mckperiodps;
- struct atmel_nand_controller *nc;
- int ret;
-
- nc = to_nand_controller(nand->base.controller);
-
- /* DDR interface not supported. */
- if (conf->type != NAND_SDR_IFACE)
- return -ENOTSUPP;
-
- /*
- * tRC < 30ns implies EDO mode. This controller does not support this
- * mode.
- */
- if (conf->timings.sdr.tRC_min < 30000)
- return -ENOTSUPP;
-
- atmel_smc_cs_conf_init(smcconf);
-
- mckperiodps = NSEC_PER_SEC / clk_get_rate(nc->mck);
- mckperiodps *= 1000;
-
- /*
- * Set write pulse timing. This one is easy to extract:
- *
- * NWE_PULSE = tWP
- */
- ncycles = DIV_ROUND_UP(conf->timings.sdr.tWP_min, mckperiodps);
- totalcycles = ncycles;
- ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NWE_SHIFT,
- ncycles);
- if (ret)
- return ret;
-
- /*
- * The write setup timing depends on the operation done on the NAND.
- * All operations goes through the same data bus, but the operation
- * type depends on the address we are writing to (ALE/CLE address
- * lines).
- * Since we have no way to differentiate the different operations at
- * the SMC level, we must consider the worst case (the biggest setup
- * time among all operation types):
- *
- * NWE_SETUP = max(tCLS, tCS, tALS, tDS) - NWE_PULSE
- */
- timeps = max3(conf->timings.sdr.tCLS_min, conf->timings.sdr.tCS_min,
- conf->timings.sdr.tALS_min);
- timeps = max(timeps, conf->timings.sdr.tDS_min);
- ncycles = DIV_ROUND_UP(timeps, mckperiodps);
- ncycles = ncycles > totalcycles ? ncycles - totalcycles : 0;
- totalcycles += ncycles;
- ret = atmel_smc_cs_conf_set_setup(smcconf, ATMEL_SMC_NWE_SHIFT,
- ncycles);
- if (ret)
- return ret;
-
- /*
- * As for the write setup timing, the write hold timing depends on the
- * operation done on the NAND:
- *
- * NWE_HOLD = max(tCLH, tCH, tALH, tDH, tWH)
- */
- timeps = max3(conf->timings.sdr.tCLH_min, conf->timings.sdr.tCH_min,
- conf->timings.sdr.tALH_min);
- timeps = max3(timeps, conf->timings.sdr.tDH_min,
- conf->timings.sdr.tWH_min);
- ncycles = DIV_ROUND_UP(timeps, mckperiodps);
- totalcycles += ncycles;
-
- /*
- * The write cycle timing is directly matching tWC, but is also
- * dependent on the other timings on the setup and hold timings we
- * calculated earlier, which gives:
- *
- * NWE_CYCLE = max(tWC, NWE_SETUP + NWE_PULSE + NWE_HOLD)
- */
- ncycles = DIV_ROUND_UP(conf->timings.sdr.tWC_min, mckperiodps);
- ncycles = max(totalcycles, ncycles);
- ret = atmel_smc_cs_conf_set_cycle(smcconf, ATMEL_SMC_NWE_SHIFT,
- ncycles);
- if (ret)
- return ret;
-
- /*
- * We don't want the CS line to be toggled between each byte/word
- * transfer to the NAND. The only way to guarantee that is to have the
- * NCS_{WR,RD}_{SETUP,HOLD} timings set to 0, which in turn means:
- *
- * NCS_WR_PULSE = NWE_CYCLE
- */
- ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NCS_WR_SHIFT,
- ncycles);
- if (ret)
- return ret;
-
- /*
- * As for the write setup timing, the read hold timing depends on the
- * operation done on the NAND:
- *
- * NRD_HOLD = max(tREH, tRHOH)
- */
- timeps = max(conf->timings.sdr.tREH_min, conf->timings.sdr.tRHOH_min);
- ncycles = DIV_ROUND_UP(timeps, mckperiodps);
- totalcycles = ncycles;
-
- /*
- * TDF = tRHZ - NRD_HOLD
- */
- ncycles = DIV_ROUND_UP(conf->timings.sdr.tRHZ_max, mckperiodps);
- ncycles -= totalcycles;
-
- /*
- * In ONFI 4.0 specs, tRHZ has been increased to support EDO NANDs and
- * we might end up with a config that does not fit in the TDF field.
- * Just take the max value in this case and hope that the NAND is more
- * tolerant than advertised.
- */
- if (ncycles > ATMEL_SMC_MODE_TDF_MAX)
- ncycles = ATMEL_SMC_MODE_TDF_MAX;
- else if (ncycles < ATMEL_SMC_MODE_TDF_MIN)
- ncycles = ATMEL_SMC_MODE_TDF_MIN;
-
- smcconf->mode |= ATMEL_SMC_MODE_TDF(ncycles) |
- ATMEL_SMC_MODE_TDFMODE_OPTIMIZED;
-
- /*
- * Read pulse timing directly matches tRP:
- *
- * NRD_PULSE = tRP
- */
- ncycles = DIV_ROUND_UP(conf->timings.sdr.tRP_min, mckperiodps);
- totalcycles += ncycles;
- ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NRD_SHIFT,
- ncycles);
- if (ret)
- return ret;
-
- /*
- * The write cycle timing is directly matching tWC, but is also
- * dependent on the setup and hold timings we calculated earlier,
- * which gives:
- *
- * NRD_CYCLE = max(tRC, NRD_PULSE + NRD_HOLD)
- *
- * NRD_SETUP is always 0.
- */
- ncycles = DIV_ROUND_UP(conf->timings.sdr.tRC_min, mckperiodps);
- ncycles = max(totalcycles, ncycles);
- ret = atmel_smc_cs_conf_set_cycle(smcconf, ATMEL_SMC_NRD_SHIFT,
- ncycles);
- if (ret)
- return ret;
-
- /*
- * We don't want the CS line to be toggled between each byte/word
- * transfer from the NAND. The only way to guarantee that is to have
- * the NCS_{WR,RD}_{SETUP,HOLD} timings set to 0, which in turn means:
- *
- * NCS_RD_PULSE = NRD_CYCLE
- */
- ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NCS_RD_SHIFT,
- ncycles);
- if (ret)
- return ret;
-
- /* Txxx timings are directly matching tXXX ones. */
- ncycles = DIV_ROUND_UP(conf->timings.sdr.tCLR_min, mckperiodps);
- ret = atmel_smc_cs_conf_set_timing(smcconf,
- ATMEL_HSMC_TIMINGS_TCLR_SHIFT,
- ncycles);
- if (ret)
- return ret;
-
- ncycles = DIV_ROUND_UP(conf->timings.sdr.tADL_min, mckperiodps);
- ret = atmel_smc_cs_conf_set_timing(smcconf,
- ATMEL_HSMC_TIMINGS_TADL_SHIFT,
- ncycles);
- /*
- * Version 4 of the ONFI spec mandates that tADL be at least 400
- * nanoseconds, but, depending on the master clock rate, 400 ns may not
- * fit in the tADL field of the SMC reg. We need to relax the check and
- * accept the -ERANGE return code.
- *
- * Note that previous versions of the ONFI spec had a lower tADL_min
- * (100 or 200 ns). It's not clear why this timing constraint got
- * increased but it seems most NANDs are fine with values lower than
- * 400ns, so we should be safe.
- */
- if (ret && ret != -ERANGE)
- return ret;
-
- ncycles = DIV_ROUND_UP(conf->timings.sdr.tAR_min, mckperiodps);
- ret = atmel_smc_cs_conf_set_timing(smcconf,
- ATMEL_HSMC_TIMINGS_TAR_SHIFT,
- ncycles);
- if (ret)
- return ret;
-
- ncycles = DIV_ROUND_UP(conf->timings.sdr.tRR_min, mckperiodps);
- ret = atmel_smc_cs_conf_set_timing(smcconf,
- ATMEL_HSMC_TIMINGS_TRR_SHIFT,
- ncycles);
- if (ret)
- return ret;
-
- ncycles = DIV_ROUND_UP(conf->timings.sdr.tWB_max, mckperiodps);
- ret = atmel_smc_cs_conf_set_timing(smcconf,
- ATMEL_HSMC_TIMINGS_TWB_SHIFT,
- ncycles);
- if (ret)
- return ret;
-
- /* Attach the CS line to the NFC logic. */
- smcconf->timings |= ATMEL_HSMC_TIMINGS_NFSEL;
-
- /* Set the appropriate data bus width. */
- if (nand->base.options & NAND_BUSWIDTH_16)
- smcconf->mode |= ATMEL_SMC_MODE_DBW_16;
-
- /* Operate in NRD/NWE READ/WRITEMODE. */
- smcconf->mode |= ATMEL_SMC_MODE_READMODE_NRD |
- ATMEL_SMC_MODE_WRITEMODE_NWE;
-
- return 0;
-}
-
-static int atmel_smc_nand_setup_data_interface(struct atmel_nand *nand,
- int csline,
- const struct nand_data_interface *conf)
-{
- struct atmel_nand_controller *nc;
- struct atmel_smc_cs_conf smcconf;
- struct atmel_nand_cs *cs;
- int ret;
-
- nc = to_nand_controller(nand->base.controller);
-
- ret = atmel_smc_nand_prepare_smcconf(nand, conf, &smcconf);
- if (ret)
- return ret;
-
- if (csline == NAND_DATA_IFACE_CHECK_ONLY)
- return 0;
-
- cs = &nand->cs[csline];
- cs->smcconf = smcconf;
- atmel_smc_cs_conf_apply(nc->smc, cs->id, &cs->smcconf);
-
- return 0;
-}
-
-static int atmel_hsmc_nand_setup_data_interface(struct atmel_nand *nand,
- int csline,
- const struct nand_data_interface *conf)
-{
- struct atmel_hsmc_nand_controller *nc;
- struct atmel_smc_cs_conf smcconf;
- struct atmel_nand_cs *cs;
- int ret;
-
- nc = to_hsmc_nand_controller(nand->base.controller);
-
- ret = atmel_smc_nand_prepare_smcconf(nand, conf, &smcconf);
- if (ret)
- return ret;
-
- if (csline == NAND_DATA_IFACE_CHECK_ONLY)
- return 0;
-
- cs = &nand->cs[csline];
- cs->smcconf = smcconf;
-
- if (cs->rb.type == ATMEL_NAND_NATIVE_RB)
- cs->smcconf.timings |= ATMEL_HSMC_TIMINGS_RBNSEL(cs->rb.id);
-
- atmel_hsmc_cs_conf_apply(nc->base.smc, nc->hsmc_layout, cs->id,
- &cs->smcconf);
-
- return 0;
-}
-
-static int atmel_nand_setup_data_interface(struct mtd_info *mtd, int csline,
- const struct nand_data_interface *conf)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct atmel_nand *nand = to_atmel_nand(chip);
- struct atmel_nand_controller *nc;
-
- nc = to_nand_controller(nand->base.controller);
-
- if (csline >= nand->numcs ||
- (csline < 0 && csline != NAND_DATA_IFACE_CHECK_ONLY))
- return -EINVAL;
-
- return nc->caps->ops->setup_data_interface(nand, csline, conf);
-}
-
-static void atmel_nand_init(struct atmel_nand_controller *nc,
- struct atmel_nand *nand)
-{
- struct nand_chip *chip = &nand->base;
- struct mtd_info *mtd = nand_to_mtd(chip);
-
- mtd->dev.parent = nc->dev;
- nand->base.controller = &nc->base;
-
- chip->cmd_ctrl = atmel_nand_cmd_ctrl;
- chip->read_byte = atmel_nand_read_byte;
- chip->read_word = atmel_nand_read_word;
- chip->write_byte = atmel_nand_write_byte;
- chip->read_buf = atmel_nand_read_buf;
- chip->write_buf = atmel_nand_write_buf;
- chip->select_chip = atmel_nand_select_chip;
-
- if (nc->mck && nc->caps->ops->setup_data_interface)
- chip->setup_data_interface = atmel_nand_setup_data_interface;
-
- /* Some NANDs require a longer delay than the default one (20us). */
- chip->chip_delay = 40;
-
- /*
- * Use a bounce buffer when the buffer passed by the MTD user is not
- * suitable for DMA.
- */
- if (nc->dmac)
- chip->options |= NAND_USE_BOUNCE_BUFFER;
-
- /* Default to HW ECC if pmecc is available. */
- if (nc->pmecc)
- chip->ecc.mode = NAND_ECC_HW;
-}
-
-static void atmel_smc_nand_init(struct atmel_nand_controller *nc,
- struct atmel_nand *nand)
-{
- struct nand_chip *chip = &nand->base;
- struct atmel_smc_nand_controller *smc_nc;
- int i;
-
- atmel_nand_init(nc, nand);
-
- smc_nc = to_smc_nand_controller(chip->controller);
- if (!smc_nc->matrix)
- return;
-
- /* Attach the CS to the NAND Flash logic. */
- for (i = 0; i < nand->numcs; i++)
- regmap_update_bits(smc_nc->matrix, smc_nc->ebi_csa_offs,
- BIT(nand->cs[i].id), BIT(nand->cs[i].id));
-}
-
-static void atmel_hsmc_nand_init(struct atmel_nand_controller *nc,
- struct atmel_nand *nand)
-{
- struct nand_chip *chip = &nand->base;
-
- atmel_nand_init(nc, nand);
-
- /* Overload some methods for the HSMC controller. */
- chip->cmd_ctrl = atmel_hsmc_nand_cmd_ctrl;
- chip->select_chip = atmel_hsmc_nand_select_chip;
-}
-
-static int atmel_nand_detect(struct atmel_nand *nand)
-{
- struct nand_chip *chip = &nand->base;
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct atmel_nand_controller *nc;
- int ret;
-
- nc = to_nand_controller(chip->controller);
-
- ret = nand_scan_ident(mtd, nand->numcs, NULL);
- if (ret)
- dev_err(nc->dev, "nand_scan_ident() failed: %d\n", ret);
-
- return ret;
-}
-
-static int atmel_nand_unregister(struct atmel_nand *nand)
-{
- struct nand_chip *chip = &nand->base;
- struct mtd_info *mtd = nand_to_mtd(chip);
- int ret;
-
- ret = mtd_device_unregister(mtd);
- if (ret)
- return ret;
-
- nand_cleanup(chip);
- list_del(&nand->node);
-
- return 0;
-}
-
-static int atmel_nand_register(struct atmel_nand *nand)
-{
- struct nand_chip *chip = &nand->base;
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct atmel_nand_controller *nc;
- int ret;
-
- nc = to_nand_controller(chip->controller);
-
- if (nc->caps->legacy_of_bindings || !nc->dev->of_node) {
- /*
- * We keep the MTD name unchanged to avoid breaking platforms
- * where the MTD cmdline parser is used and the bootloader
- * has not been updated to use the new naming scheme.
- */
- mtd->name = "atmel_nand";
- } else if (!mtd->name) {
- /*
- * If the new bindings are used and the bootloader has not been
- * updated to pass a new mtdparts parameter on the cmdline, you
- * should define the following property in your nand node:
- *
- * label = "atmel_nand";
- *
- * This way, mtd->name will be set by the core when
- * nand_set_flash_node() is called.
- */
- mtd->name = devm_kasprintf(nc->dev, GFP_KERNEL,
- "%s:nand.%d", dev_name(nc->dev),
- nand->cs[0].id);
- if (!mtd->name) {
- dev_err(nc->dev, "Failed to allocate mtd->name\n");
- return -ENOMEM;
- }
- }
-
- ret = nand_scan_tail(mtd);
- if (ret) {
- dev_err(nc->dev, "nand_scan_tail() failed: %d\n", ret);
- return ret;
- }
-
- ret = mtd_device_register(mtd, NULL, 0);
- if (ret) {
- dev_err(nc->dev, "Failed to register mtd device: %d\n", ret);
- nand_cleanup(chip);
- return ret;
- }
-
- list_add_tail(&nand->node, &nc->chips);
-
- return 0;
-}
-
-static struct atmel_nand *atmel_nand_create(struct atmel_nand_controller *nc,
- struct device_node *np,
- int reg_cells)
-{
- struct atmel_nand *nand;
- struct gpio_desc *gpio;
- int numcs, ret, i;
-
- numcs = of_property_count_elems_of_size(np, "reg",
- reg_cells * sizeof(u32));
- if (numcs < 1) {
- dev_err(nc->dev, "Missing or invalid reg property\n");
- return ERR_PTR(-EINVAL);
- }
-
- nand = devm_kzalloc(nc->dev,
- sizeof(*nand) + (numcs * sizeof(*nand->cs)),
- GFP_KERNEL);
- if (!nand) {
- dev_err(nc->dev, "Failed to allocate NAND object\n");
- return ERR_PTR(-ENOMEM);
- }
-
- nand->numcs = numcs;
-
- gpio = devm_fwnode_get_index_gpiod_from_child(nc->dev, "det", 0,
- &np->fwnode, GPIOD_IN,
- "nand-det");
- if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) {
- dev_err(nc->dev,
- "Failed to get detect gpio (err = %ld)\n",
- PTR_ERR(gpio));
- return ERR_CAST(gpio);
- }
-
- if (!IS_ERR(gpio))
- nand->cdgpio = gpio;
-
- for (i = 0; i < numcs; i++) {
- struct resource res;
- u32 val;
-
- ret = of_address_to_resource(np, 0, &res);
- if (ret) {
- dev_err(nc->dev, "Invalid reg property (err = %d)\n",
- ret);
- return ERR_PTR(ret);
- }
-
- ret = of_property_read_u32_index(np, "reg", i * reg_cells,
- &val);
- if (ret) {
- dev_err(nc->dev, "Invalid reg property (err = %d)\n",
- ret);
- return ERR_PTR(ret);
- }
-
- nand->cs[i].id = val;
-
- nand->cs[i].io.dma = res.start;
- nand->cs[i].io.virt = devm_ioremap_resource(nc->dev, &res);
- if (IS_ERR(nand->cs[i].io.virt))
- return ERR_CAST(nand->cs[i].io.virt);
-
- if (!of_property_read_u32(np, "atmel,rb", &val)) {
- if (val > ATMEL_NFC_MAX_RB_ID)
- return ERR_PTR(-EINVAL);
-
- nand->cs[i].rb.type = ATMEL_NAND_NATIVE_RB;
- nand->cs[i].rb.id = val;
- } else {
- gpio = devm_fwnode_get_index_gpiod_from_child(nc->dev,
- "rb", i, &np->fwnode,
- GPIOD_IN, "nand-rb");
- if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) {
- dev_err(nc->dev,
- "Failed to get R/B gpio (err = %ld)\n",
- PTR_ERR(gpio));
- return ERR_CAST(gpio);
- }
-
- if (!IS_ERR(gpio)) {
- nand->cs[i].rb.type = ATMEL_NAND_GPIO_RB;
- nand->cs[i].rb.gpio = gpio;
- }
- }
-
- gpio = devm_fwnode_get_index_gpiod_from_child(nc->dev, "cs",
- i, &np->fwnode,
- GPIOD_OUT_HIGH,
- "nand-cs");
- if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) {
- dev_err(nc->dev,
- "Failed to get CS gpio (err = %ld)\n",
- PTR_ERR(gpio));
- return ERR_CAST(gpio);
- }
-
- if (!IS_ERR(gpio))
- nand->cs[i].csgpio = gpio;
- }
-
- nand_set_flash_node(&nand->base, np);
-
- return nand;
-}
-
-static int
-atmel_nand_controller_add_nand(struct atmel_nand_controller *nc,
- struct atmel_nand *nand)
-{
- int ret;
-
- /* No card inserted, skip this NAND. */
- if (nand->cdgpio && gpiod_get_value(nand->cdgpio)) {
- dev_info(nc->dev, "No SmartMedia card inserted.\n");
- return 0;
- }
-
- nc->caps->ops->nand_init(nc, nand);
-
- ret = atmel_nand_detect(nand);
- if (ret)
- return ret;
-
- ret = nc->caps->ops->ecc_init(nand);
- if (ret)
- return ret;
-
- return atmel_nand_register(nand);
-}
-
-static int
-atmel_nand_controller_remove_nands(struct atmel_nand_controller *nc)
-{
- struct atmel_nand *nand, *tmp;
- int ret;
-
- list_for_each_entry_safe(nand, tmp, &nc->chips, node) {
- ret = atmel_nand_unregister(nand);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static int
-atmel_nand_controller_legacy_add_nands(struct atmel_nand_controller *nc)
-{
- struct device *dev = nc->dev;
- struct platform_device *pdev = to_platform_device(dev);
- struct atmel_nand *nand;
- struct gpio_desc *gpio;
- struct resource *res;
-
- /*
- * Legacy bindings only allow connecting a single NAND with a unique CS
- * line to the controller.
- */
- nand = devm_kzalloc(nc->dev, sizeof(*nand) + sizeof(*nand->cs),
- GFP_KERNEL);
- if (!nand)
- return -ENOMEM;
-
- nand->numcs = 1;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- nand->cs[0].io.virt = devm_ioremap_resource(dev, res);
- if (IS_ERR(nand->cs[0].io.virt))
- return PTR_ERR(nand->cs[0].io.virt);
-
- nand->cs[0].io.dma = res->start;
-
- /*
- * The old driver was hardcoding the CS id to 3 for all sama5
- * controllers. Since this id is only meaningful for the sama5
- * controller we can safely assign this id to 3 no matter the
- * controller.
- * If one wants to connect a NAND to a different CS line, he will
- * have to use the new bindings.
- */
- nand->cs[0].id = 3;
-
- /* R/B GPIO. */
- gpio = devm_gpiod_get_index_optional(dev, NULL, 0, GPIOD_IN);
- if (IS_ERR(gpio)) {
- dev_err(dev, "Failed to get R/B gpio (err = %ld)\n",
- PTR_ERR(gpio));
- return PTR_ERR(gpio);
- }
-
- if (gpio) {
- nand->cs[0].rb.type = ATMEL_NAND_GPIO_RB;
- nand->cs[0].rb.gpio = gpio;
- }
-
- /* CS GPIO. */
- gpio = devm_gpiod_get_index_optional(dev, NULL, 1, GPIOD_OUT_HIGH);
- if (IS_ERR(gpio)) {
- dev_err(dev, "Failed to get CS gpio (err = %ld)\n",
- PTR_ERR(gpio));
- return PTR_ERR(gpio);
- }
-
- nand->cs[0].csgpio = gpio;
-
- /* Card detect GPIO. */
- gpio = devm_gpiod_get_index_optional(nc->dev, NULL, 2, GPIOD_IN);
- if (IS_ERR(gpio)) {
- dev_err(dev,
- "Failed to get detect gpio (err = %ld)\n",
- PTR_ERR(gpio));
- return PTR_ERR(gpio);
- }
-
- nand->cdgpio = gpio;
-
- nand_set_flash_node(&nand->base, nc->dev->of_node);
-
- return atmel_nand_controller_add_nand(nc, nand);
-}
-
-static int atmel_nand_controller_add_nands(struct atmel_nand_controller *nc)
-{
- struct device_node *np, *nand_np;
- struct device *dev = nc->dev;
- int ret, reg_cells;
- u32 val;
-
- /* We do not retrieve the SMC syscon when parsing old DTs. */
- if (nc->caps->legacy_of_bindings)
- return atmel_nand_controller_legacy_add_nands(nc);
-
- np = dev->of_node;
-
- ret = of_property_read_u32(np, "#address-cells", &val);
- if (ret) {
- dev_err(dev, "missing #address-cells property\n");
- return ret;
- }
-
- reg_cells = val;
-
- ret = of_property_read_u32(np, "#size-cells", &val);
- if (ret) {
- dev_err(dev, "missing #address-cells property\n");
- return ret;
- }
-
- reg_cells += val;
-
- for_each_child_of_node(np, nand_np) {
- struct atmel_nand *nand;
-
- nand = atmel_nand_create(nc, nand_np, reg_cells);
- if (IS_ERR(nand)) {
- ret = PTR_ERR(nand);
- goto err;
- }
-
- ret = atmel_nand_controller_add_nand(nc, nand);
- if (ret)
- goto err;
- }
-
- return 0;
-
-err:
- atmel_nand_controller_remove_nands(nc);
-
- return ret;
-}
-
-static void atmel_nand_controller_cleanup(struct atmel_nand_controller *nc)
-{
- if (nc->dmac)
- dma_release_channel(nc->dmac);
-
- clk_put(nc->mck);
-}
-
-static const struct of_device_id atmel_matrix_of_ids[] = {
- {
- .compatible = "atmel,at91sam9260-matrix",
- .data = (void *)AT91SAM9260_MATRIX_EBICSA,
- },
- {
- .compatible = "atmel,at91sam9261-matrix",
- .data = (void *)AT91SAM9261_MATRIX_EBICSA,
- },
- {
- .compatible = "atmel,at91sam9263-matrix",
- .data = (void *)AT91SAM9263_MATRIX_EBI0CSA,
- },
- {
- .compatible = "atmel,at91sam9rl-matrix",
- .data = (void *)AT91SAM9RL_MATRIX_EBICSA,
- },
- {
- .compatible = "atmel,at91sam9g45-matrix",
- .data = (void *)AT91SAM9G45_MATRIX_EBICSA,
- },
- {
- .compatible = "atmel,at91sam9n12-matrix",
- .data = (void *)AT91SAM9N12_MATRIX_EBICSA,
- },
- {
- .compatible = "atmel,at91sam9x5-matrix",
- .data = (void *)AT91SAM9X5_MATRIX_EBICSA,
- },
- { /* sentinel */ },
-};
-
-static int atmel_nand_controller_init(struct atmel_nand_controller *nc,
- struct platform_device *pdev,
- const struct atmel_nand_controller_caps *caps)
-{
- struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;
- int ret;
-
- nand_hw_control_init(&nc->base);
- INIT_LIST_HEAD(&nc->chips);
- nc->dev = dev;
- nc->caps = caps;
-
- platform_set_drvdata(pdev, nc);
-
- nc->pmecc = devm_atmel_pmecc_get(dev);
- if (IS_ERR(nc->pmecc)) {
- ret = PTR_ERR(nc->pmecc);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "Could not get PMECC object (err = %d)\n",
- ret);
- return ret;
- }
-
- if (nc->caps->has_dma) {
- dma_cap_mask_t mask;
-
- dma_cap_zero(mask);
- dma_cap_set(DMA_MEMCPY, mask);
-
- nc->dmac = dma_request_channel(mask, NULL, NULL);
- if (!nc->dmac)
- dev_err(nc->dev, "Failed to request DMA channel\n");
- }
-
- /* We do not retrieve the SMC syscon when parsing old DTs. */
- if (nc->caps->legacy_of_bindings)
- return 0;
-
- nc->mck = of_clk_get(dev->parent->of_node, 0);
- if (IS_ERR(nc->mck)) {
- dev_err(dev, "Failed to retrieve MCK clk\n");
- return PTR_ERR(nc->mck);
- }
-
- np = of_parse_phandle(dev->parent->of_node, "atmel,smc", 0);
- if (!np) {
- dev_err(dev, "Missing or invalid atmel,smc property\n");
- return -EINVAL;
- }
-
- nc->smc = syscon_node_to_regmap(np);
- of_node_put(np);
- if (IS_ERR(nc->smc)) {
- ret = PTR_ERR(nc->smc);
- dev_err(dev, "Could not get SMC regmap (err = %d)\n", ret);
- return ret;
- }
-
- return 0;
-}
-
-static int
-atmel_smc_nand_controller_init(struct atmel_smc_nand_controller *nc)
-{
- struct device *dev = nc->base.dev;
- const struct of_device_id *match;
- struct device_node *np;
- int ret;
-
- /* We do not retrieve the matrix syscon when parsing old DTs. */
- if (nc->base.caps->legacy_of_bindings)
- return 0;
-
- np = of_parse_phandle(dev->parent->of_node, "atmel,matrix", 0);
- if (!np)
- return 0;
-
- match = of_match_node(atmel_matrix_of_ids, np);
- if (!match) {
- of_node_put(np);
- return 0;
- }
-
- nc->matrix = syscon_node_to_regmap(np);
- of_node_put(np);
- if (IS_ERR(nc->matrix)) {
- ret = PTR_ERR(nc->matrix);
- dev_err(dev, "Could not get Matrix regmap (err = %d)\n", ret);
- return ret;
- }
-
- nc->ebi_csa_offs = (unsigned int)match->data;
-
- /*
- * The at91sam9263 has 2 EBIs, if the NAND controller is under EBI1
- * add 4 to ->ebi_csa_offs.
- */
- if (of_device_is_compatible(dev->parent->of_node,
- "atmel,at91sam9263-ebi1"))
- nc->ebi_csa_offs += 4;
-
- return 0;
-}
-
-static int
-atmel_hsmc_nand_controller_legacy_init(struct atmel_hsmc_nand_controller *nc)
-{
- struct regmap_config regmap_conf = {
- .reg_bits = 32,
- .val_bits = 32,
- .reg_stride = 4,
- };
-
- struct device *dev = nc->base.dev;
- struct device_node *nand_np, *nfc_np;
- void __iomem *iomem;
- struct resource res;
- int ret;
-
- nand_np = dev->of_node;
- nfc_np = of_find_compatible_node(dev->of_node, NULL,
- "atmel,sama5d3-nfc");
-
- nc->clk = of_clk_get(nfc_np, 0);
- if (IS_ERR(nc->clk)) {
- ret = PTR_ERR(nc->clk);
- dev_err(dev, "Failed to retrieve HSMC clock (err = %d)\n",
- ret);
- goto out;
- }
-
- ret = clk_prepare_enable(nc->clk);
- if (ret) {
- dev_err(dev, "Failed to enable the HSMC clock (err = %d)\n",
- ret);
- goto out;
- }
-
- nc->irq = of_irq_get(nand_np, 0);
- if (nc->irq <= 0) {
- ret = nc->irq ?: -ENXIO;
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "Failed to get IRQ number (err = %d)\n",
- ret);
- goto out;
- }
-
- ret = of_address_to_resource(nfc_np, 0, &res);
- if (ret) {
- dev_err(dev, "Invalid or missing NFC IO resource (err = %d)\n",
- ret);
- goto out;
- }
-
- iomem = devm_ioremap_resource(dev, &res);
- if (IS_ERR(iomem)) {
- ret = PTR_ERR(iomem);
- goto out;
- }
-
- regmap_conf.name = "nfc-io";
- regmap_conf.max_register = resource_size(&res) - 4;
- nc->io = devm_regmap_init_mmio(dev, iomem, &regmap_conf);
- if (IS_ERR(nc->io)) {
- ret = PTR_ERR(nc->io);
- dev_err(dev, "Could not create NFC IO regmap (err = %d)\n",
- ret);
- goto out;
- }
-
- ret = of_address_to_resource(nfc_np, 1, &res);
- if (ret) {
- dev_err(dev, "Invalid or missing HSMC resource (err = %d)\n",
- ret);
- goto out;
- }
-
- iomem = devm_ioremap_resource(dev, &res);
- if (IS_ERR(iomem)) {
- ret = PTR_ERR(iomem);
- goto out;
- }
-
- regmap_conf.name = "smc";
- regmap_conf.max_register = resource_size(&res) - 4;
- nc->base.smc = devm_regmap_init_mmio(dev, iomem, &regmap_conf);
- if (IS_ERR(nc->base.smc)) {
- ret = PTR_ERR(nc->base.smc);
- dev_err(dev, "Could not create NFC IO regmap (err = %d)\n",
- ret);
- goto out;
- }
-
- ret = of_address_to_resource(nfc_np, 2, &res);
- if (ret) {
- dev_err(dev, "Invalid or missing SRAM resource (err = %d)\n",
- ret);
- goto out;
- }
-
- nc->sram.virt = devm_ioremap_resource(dev, &res);
- if (IS_ERR(nc->sram.virt)) {
- ret = PTR_ERR(nc->sram.virt);
- goto out;
- }
-
- nc->sram.dma = res.start;
-
-out:
- of_node_put(nfc_np);
-
- return ret;
-}
-
-static int
-atmel_hsmc_nand_controller_init(struct atmel_hsmc_nand_controller *nc)
-{
- struct device *dev = nc->base.dev;
- struct device_node *np;
- int ret;
-
- np = of_parse_phandle(dev->parent->of_node, "atmel,smc", 0);
- if (!np) {
- dev_err(dev, "Missing or invalid atmel,smc property\n");
- return -EINVAL;
- }
-
- nc->hsmc_layout = atmel_hsmc_get_reg_layout(np);
-
- nc->irq = of_irq_get(np, 0);
- of_node_put(np);
- if (nc->irq <= 0) {
- ret = nc->irq ?: -ENXIO;
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "Failed to get IRQ number (err = %d)\n",
- ret);
- return ret;
- }
-
- np = of_parse_phandle(dev->of_node, "atmel,nfc-io", 0);
- if (!np) {
- dev_err(dev, "Missing or invalid atmel,nfc-io property\n");
- return -EINVAL;
- }
-
- nc->io = syscon_node_to_regmap(np);
- of_node_put(np);
- if (IS_ERR(nc->io)) {
- ret = PTR_ERR(nc->io);
- dev_err(dev, "Could not get NFC IO regmap (err = %d)\n", ret);
- return ret;
- }
-
- nc->sram.pool = of_gen_pool_get(nc->base.dev->of_node,
- "atmel,nfc-sram", 0);
- if (!nc->sram.pool) {
- dev_err(nc->base.dev, "Missing SRAM\n");
- return -ENOMEM;
- }
-
- nc->sram.virt = gen_pool_dma_alloc(nc->sram.pool,
- ATMEL_NFC_SRAM_SIZE,
- &nc->sram.dma);
- if (!nc->sram.virt) {
- dev_err(nc->base.dev,
- "Could not allocate memory from the NFC SRAM pool\n");
- return -ENOMEM;
- }
-
- return 0;
-}
-
-static int
-atmel_hsmc_nand_controller_remove(struct atmel_nand_controller *nc)
-{
- struct atmel_hsmc_nand_controller *hsmc_nc;
- int ret;
-
- ret = atmel_nand_controller_remove_nands(nc);
- if (ret)
- return ret;
-
- hsmc_nc = container_of(nc, struct atmel_hsmc_nand_controller, base);
- if (hsmc_nc->sram.pool)
- gen_pool_free(hsmc_nc->sram.pool,
- (unsigned long)hsmc_nc->sram.virt,
- ATMEL_NFC_SRAM_SIZE);
-
- if (hsmc_nc->clk) {
- clk_disable_unprepare(hsmc_nc->clk);
- clk_put(hsmc_nc->clk);
- }
-
- atmel_nand_controller_cleanup(nc);
-
- return 0;
-}
-
-static int atmel_hsmc_nand_controller_probe(struct platform_device *pdev,
- const struct atmel_nand_controller_caps *caps)
-{
- struct device *dev = &pdev->dev;
- struct atmel_hsmc_nand_controller *nc;
- int ret;
-
- nc = devm_kzalloc(dev, sizeof(*nc), GFP_KERNEL);
- if (!nc)
- return -ENOMEM;
-
- ret = atmel_nand_controller_init(&nc->base, pdev, caps);
- if (ret)
- return ret;
-
- if (caps->legacy_of_bindings)
- ret = atmel_hsmc_nand_controller_legacy_init(nc);
- else
- ret = atmel_hsmc_nand_controller_init(nc);
-
- if (ret)
- return ret;
-
- /* Make sure all irqs are masked before registering our IRQ handler. */
- regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, 0xffffffff);
- ret = devm_request_irq(dev, nc->irq, atmel_nfc_interrupt,
- IRQF_SHARED, "nfc", nc);
- if (ret) {
- dev_err(dev,
- "Could not get register NFC interrupt handler (err = %d)\n",
- ret);
- goto err;
- }
-
- /* Initial NFC configuration. */
- regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CFG,
- ATMEL_HSMC_NFC_CFG_DTO_MAX);
-
- ret = atmel_nand_controller_add_nands(&nc->base);
- if (ret)
- goto err;
-
- return 0;
-
-err:
- atmel_hsmc_nand_controller_remove(&nc->base);
-
- return ret;
-}
-
-static const struct atmel_nand_controller_ops atmel_hsmc_nc_ops = {
- .probe = atmel_hsmc_nand_controller_probe,
- .remove = atmel_hsmc_nand_controller_remove,
- .ecc_init = atmel_hsmc_nand_ecc_init,
- .nand_init = atmel_hsmc_nand_init,
- .setup_data_interface = atmel_hsmc_nand_setup_data_interface,
-};
-
-static const struct atmel_nand_controller_caps atmel_sama5_nc_caps = {
- .has_dma = true,
- .ale_offs = BIT(21),
- .cle_offs = BIT(22),
- .ops = &atmel_hsmc_nc_ops,
-};
-
-/* Only used to parse old bindings. */
-static const struct atmel_nand_controller_caps atmel_sama5_nand_caps = {
- .has_dma = true,
- .ale_offs = BIT(21),
- .cle_offs = BIT(22),
- .ops = &atmel_hsmc_nc_ops,
- .legacy_of_bindings = true,
-};
-
-static int atmel_smc_nand_controller_probe(struct platform_device *pdev,
- const struct atmel_nand_controller_caps *caps)
-{
- struct device *dev = &pdev->dev;
- struct atmel_smc_nand_controller *nc;
- int ret;
-
- nc = devm_kzalloc(dev, sizeof(*nc), GFP_KERNEL);
- if (!nc)
- return -ENOMEM;
-
- ret = atmel_nand_controller_init(&nc->base, pdev, caps);
- if (ret)
- return ret;
-
- ret = atmel_smc_nand_controller_init(nc);
- if (ret)
- return ret;
-
- return atmel_nand_controller_add_nands(&nc->base);
-}
-
-static int
-atmel_smc_nand_controller_remove(struct atmel_nand_controller *nc)
-{
- int ret;
-
- ret = atmel_nand_controller_remove_nands(nc);
- if (ret)
- return ret;
-
- atmel_nand_controller_cleanup(nc);
-
- return 0;
-}
-
-/*
- * The SMC reg layout of at91rm9200 is completely different which prevents us
- * from re-using atmel_smc_nand_setup_data_interface() for the
- * ->setup_data_interface() hook.
- * At this point, there's no support for the at91rm9200 SMC IP, so we leave
- * ->setup_data_interface() unassigned.
- */
-static const struct atmel_nand_controller_ops at91rm9200_nc_ops = {
- .probe = atmel_smc_nand_controller_probe,
- .remove = atmel_smc_nand_controller_remove,
- .ecc_init = atmel_nand_ecc_init,
- .nand_init = atmel_smc_nand_init,
-};
-
-static const struct atmel_nand_controller_caps atmel_rm9200_nc_caps = {
- .ale_offs = BIT(21),
- .cle_offs = BIT(22),
- .ops = &at91rm9200_nc_ops,
-};
-
-static const struct atmel_nand_controller_ops atmel_smc_nc_ops = {
- .probe = atmel_smc_nand_controller_probe,
- .remove = atmel_smc_nand_controller_remove,
- .ecc_init = atmel_nand_ecc_init,
- .nand_init = atmel_smc_nand_init,
- .setup_data_interface = atmel_smc_nand_setup_data_interface,
-};
-
-static const struct atmel_nand_controller_caps atmel_sam9260_nc_caps = {
- .ale_offs = BIT(21),
- .cle_offs = BIT(22),
- .ops = &atmel_smc_nc_ops,
-};
-
-static const struct atmel_nand_controller_caps atmel_sam9261_nc_caps = {
- .ale_offs = BIT(22),
- .cle_offs = BIT(21),
- .ops = &atmel_smc_nc_ops,
-};
-
-static const struct atmel_nand_controller_caps atmel_sam9g45_nc_caps = {
- .has_dma = true,
- .ale_offs = BIT(21),
- .cle_offs = BIT(22),
- .ops = &atmel_smc_nc_ops,
-};
-
-/* Only used to parse old bindings. */
-static const struct atmel_nand_controller_caps atmel_rm9200_nand_caps = {
- .ale_offs = BIT(21),
- .cle_offs = BIT(22),
- .ops = &atmel_smc_nc_ops,
- .legacy_of_bindings = true,
-};
-
-static const struct atmel_nand_controller_caps atmel_sam9261_nand_caps = {
- .ale_offs = BIT(22),
- .cle_offs = BIT(21),
- .ops = &atmel_smc_nc_ops,
- .legacy_of_bindings = true,
-};
-
-static const struct atmel_nand_controller_caps atmel_sam9g45_nand_caps = {
- .has_dma = true,
- .ale_offs = BIT(21),
- .cle_offs = BIT(22),
- .ops = &atmel_smc_nc_ops,
- .legacy_of_bindings = true,
-};
-
-static const struct of_device_id atmel_nand_controller_of_ids[] = {
- {
- .compatible = "atmel,at91rm9200-nand-controller",
- .data = &atmel_rm9200_nc_caps,
- },
- {
- .compatible = "atmel,at91sam9260-nand-controller",
- .data = &atmel_sam9260_nc_caps,
- },
- {
- .compatible = "atmel,at91sam9261-nand-controller",
- .data = &atmel_sam9261_nc_caps,
- },
- {
- .compatible = "atmel,at91sam9g45-nand-controller",
- .data = &atmel_sam9g45_nc_caps,
- },
- {
- .compatible = "atmel,sama5d3-nand-controller",
- .data = &atmel_sama5_nc_caps,
- },
- /* Support for old/deprecated bindings: */
- {
- .compatible = "atmel,at91rm9200-nand",
- .data = &atmel_rm9200_nand_caps,
- },
- {
- .compatible = "atmel,sama5d4-nand",
- .data = &atmel_rm9200_nand_caps,
- },
- {
- .compatible = "atmel,sama5d2-nand",
- .data = &atmel_rm9200_nand_caps,
- },
- { /* sentinel */ },
-};
-MODULE_DEVICE_TABLE(of, atmel_nand_controller_of_ids);
-
-static int atmel_nand_controller_probe(struct platform_device *pdev)
-{
- const struct atmel_nand_controller_caps *caps;
-
- if (pdev->id_entry)
- caps = (void *)pdev->id_entry->driver_data;
- else
- caps = of_device_get_match_data(&pdev->dev);
-
- if (!caps) {
- dev_err(&pdev->dev, "Could not retrieve NFC caps\n");
- return -EINVAL;
- }
-
- if (caps->legacy_of_bindings) {
- u32 ale_offs = 21;
-
- /*
- * If we are parsing legacy DT props and the DT contains a
- * valid NFC node, forward the request to the sama5 logic.
- */
- if (of_find_compatible_node(pdev->dev.of_node, NULL,
- "atmel,sama5d3-nfc"))
- caps = &atmel_sama5_nand_caps;
-
- /*
- * Even if the compatible says we are dealing with an
- * at91rm9200 controller, the atmel,nand-has-dma specify that
- * this controller supports DMA, which means we are in fact
- * dealing with an at91sam9g45+ controller.
- */
- if (!caps->has_dma &&
- of_property_read_bool(pdev->dev.of_node,
- "atmel,nand-has-dma"))
- caps = &atmel_sam9g45_nand_caps;
-
- /*
- * All SoCs except the at91sam9261 are assigning ALE to A21 and
- * CLE to A22. If atmel,nand-addr-offset != 21 this means we're
- * actually dealing with an at91sam9261 controller.
- */
- of_property_read_u32(pdev->dev.of_node,
- "atmel,nand-addr-offset", &ale_offs);
- if (ale_offs != 21)
- caps = &atmel_sam9261_nand_caps;
- }
-
- return caps->ops->probe(pdev, caps);
-}
-
-static int atmel_nand_controller_remove(struct platform_device *pdev)
-{
- struct atmel_nand_controller *nc = platform_get_drvdata(pdev);
-
- return nc->caps->ops->remove(nc);
-}
-
-static __maybe_unused int atmel_nand_controller_resume(struct device *dev)
-{
- struct atmel_nand_controller *nc = dev_get_drvdata(dev);
- struct atmel_nand *nand;
-
- if (nc->pmecc)
- atmel_pmecc_reset(nc->pmecc);
-
- list_for_each_entry(nand, &nc->chips, node) {
- int i;
-
- for (i = 0; i < nand->numcs; i++)
- nand_reset(&nand->base, i);
- }
-
- return 0;
-}
-
-static SIMPLE_DEV_PM_OPS(atmel_nand_controller_pm_ops, NULL,
- atmel_nand_controller_resume);
-
-static struct platform_driver atmel_nand_controller_driver = {
- .driver = {
- .name = "atmel-nand-controller",
- .of_match_table = of_match_ptr(atmel_nand_controller_of_ids),
- .pm = &atmel_nand_controller_pm_ops,
- },
- .probe = atmel_nand_controller_probe,
- .remove = atmel_nand_controller_remove,
-};
-module_platform_driver(atmel_nand_controller_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
-MODULE_DESCRIPTION("NAND Flash Controller driver for Atmel SoCs");
-MODULE_ALIAS("platform:atmel-nand-controller");
diff --git a/drivers/mtd/nand/atmel/pmecc.c b/drivers/mtd/nand/atmel/pmecc.c
deleted file mode 100644
index fcbe4fd6e684..000000000000
--- a/drivers/mtd/nand/atmel/pmecc.c
+++ /dev/null
@@ -1,1012 +0,0 @@
-/*
- * Copyright 2017 ATMEL
- * Copyright 2017 Free Electrons
- *
- * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
- *
- * Derived from the atmel_nand.c driver which contained the following
- * copyrights:
- *
- * Copyright 2003 Rick Bronson
- *
- * Derived from drivers/mtd/nand/autcpu12.c
- * Copyright 2001 Thomas Gleixner (gleixner@autronix.de)
- *
- * Derived from drivers/mtd/spia.c
- * Copyright 2000 Steven J. Hill (sjhill@cotw.com)
- *
- * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263
- * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright 2007
- *
- * Derived from Das U-Boot source code
- * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c)
- * Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas
- *
- * Add Programmable Multibit ECC support for various AT91 SoC
- * Copyright 2012 ATMEL, Hong Xu
- *
- * Add Nand Flash Controller support for SAMA5 SoC
- * Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.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 PMECC is an hardware assisted BCH engine, which means part of the
- * ECC algorithm is left to the software. The hardware/software repartition
- * is explained in the "PMECC Controller Functional Description" chapter in
- * Atmel datasheets, and some of the functions in this file are directly
- * implementing the algorithms described in the "Software Implementation"
- * sub-section.
- *
- * TODO: it seems that the software BCH implementation in lib/bch.c is already
- * providing some of the logic we are implementing here. It would be smart
- * to expose the needed lib/bch.c helpers/functions and re-use them here.
- */
-
-#include <linux/genalloc.h>
-#include <linux/iopoll.h>
-#include <linux/module.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/of_irq.h>
-#include <linux/of_platform.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-
-#include "pmecc.h"
-
-/* Galois field dimension */
-#define PMECC_GF_DIMENSION_13 13
-#define PMECC_GF_DIMENSION_14 14
-
-/* Primitive Polynomial used by PMECC */
-#define PMECC_GF_13_PRIMITIVE_POLY 0x201b
-#define PMECC_GF_14_PRIMITIVE_POLY 0x4443
-
-#define PMECC_LOOKUP_TABLE_SIZE_512 0x2000
-#define PMECC_LOOKUP_TABLE_SIZE_1024 0x4000
-
-/* Time out value for reading PMECC status register */
-#define PMECC_MAX_TIMEOUT_MS 100
-
-/* PMECC Register Definitions */
-#define ATMEL_PMECC_CFG 0x0
-#define PMECC_CFG_BCH_STRENGTH(x) (x)
-#define PMECC_CFG_BCH_STRENGTH_MASK GENMASK(2, 0)
-#define PMECC_CFG_SECTOR512 (0 << 4)
-#define PMECC_CFG_SECTOR1024 (1 << 4)
-#define PMECC_CFG_NSECTORS(x) ((fls(x) - 1) << 8)
-#define PMECC_CFG_READ_OP (0 << 12)
-#define PMECC_CFG_WRITE_OP (1 << 12)
-#define PMECC_CFG_SPARE_ENABLE BIT(16)
-#define PMECC_CFG_AUTO_ENABLE BIT(20)
-
-#define ATMEL_PMECC_SAREA 0x4
-#define ATMEL_PMECC_SADDR 0x8
-#define ATMEL_PMECC_EADDR 0xc
-
-#define ATMEL_PMECC_CLK 0x10
-#define PMECC_CLK_133MHZ (2 << 0)
-
-#define ATMEL_PMECC_CTRL 0x14
-#define PMECC_CTRL_RST BIT(0)
-#define PMECC_CTRL_DATA BIT(1)
-#define PMECC_CTRL_USER BIT(2)
-#define PMECC_CTRL_ENABLE BIT(4)
-#define PMECC_CTRL_DISABLE BIT(5)
-
-#define ATMEL_PMECC_SR 0x18
-#define PMECC_SR_BUSY BIT(0)
-#define PMECC_SR_ENABLE BIT(4)
-
-#define ATMEL_PMECC_IER 0x1c
-#define ATMEL_PMECC_IDR 0x20
-#define ATMEL_PMECC_IMR 0x24
-#define ATMEL_PMECC_ISR 0x28
-#define PMECC_ERROR_INT BIT(0)
-
-#define ATMEL_PMECC_ECC(sector, n) \
- ((((sector) + 1) * 0x40) + (n))
-
-#define ATMEL_PMECC_REM(sector, n) \
- ((((sector) + 1) * 0x40) + ((n) * 4) + 0x200)
-
-/* PMERRLOC Register Definitions */
-#define ATMEL_PMERRLOC_ELCFG 0x0
-#define PMERRLOC_ELCFG_SECTOR_512 (0 << 0)
-#define PMERRLOC_ELCFG_SECTOR_1024 (1 << 0)
-#define PMERRLOC_ELCFG_NUM_ERRORS(n) ((n) << 16)
-
-#define ATMEL_PMERRLOC_ELPRIM 0x4
-#define ATMEL_PMERRLOC_ELEN 0x8
-#define ATMEL_PMERRLOC_ELDIS 0xc
-#define PMERRLOC_DISABLE BIT(0)
-
-#define ATMEL_PMERRLOC_ELSR 0x10
-#define PMERRLOC_ELSR_BUSY BIT(0)
-
-#define ATMEL_PMERRLOC_ELIER 0x14
-#define ATMEL_PMERRLOC_ELIDR 0x18
-#define ATMEL_PMERRLOC_ELIMR 0x1c
-#define ATMEL_PMERRLOC_ELISR 0x20
-#define PMERRLOC_ERR_NUM_MASK GENMASK(12, 8)
-#define PMERRLOC_CALC_DONE BIT(0)
-
-#define ATMEL_PMERRLOC_SIGMA(x) (((x) * 0x4) + 0x28)
-
-#define ATMEL_PMERRLOC_EL(offs, x) (((x) * 0x4) + (offs))
-
-struct atmel_pmecc_gf_tables {
- u16 *alpha_to;
- u16 *index_of;
-};
-
-struct atmel_pmecc_caps {
- const int *strengths;
- int nstrengths;
- int el_offset;
- bool correct_erased_chunks;
-};
-
-struct atmel_pmecc {
- struct device *dev;
- const struct atmel_pmecc_caps *caps;
-
- struct {
- void __iomem *base;
- void __iomem *errloc;
- } regs;
-
- struct mutex lock;
-};
-
-struct atmel_pmecc_user_conf_cache {
- u32 cfg;
- u32 sarea;
- u32 saddr;
- u32 eaddr;
-};
-
-struct atmel_pmecc_user {
- struct atmel_pmecc_user_conf_cache cache;
- struct atmel_pmecc *pmecc;
- const struct atmel_pmecc_gf_tables *gf_tables;
- int eccbytes;
- s16 *partial_syn;
- s16 *si;
- s16 *lmu;
- s16 *smu;
- s32 *mu;
- s32 *dmu;
- s32 *delta;
- u32 isr;
-};
-
-static DEFINE_MUTEX(pmecc_gf_tables_lock);
-static const struct atmel_pmecc_gf_tables *pmecc_gf_tables_512;
-static const struct atmel_pmecc_gf_tables *pmecc_gf_tables_1024;
-
-static inline int deg(unsigned int poly)
-{
- /* polynomial degree is the most-significant bit index */
- return fls(poly) - 1;
-}
-
-static int atmel_pmecc_build_gf_tables(int mm, unsigned int poly,
- struct atmel_pmecc_gf_tables *gf_tables)
-{
- unsigned int i, x = 1;
- const unsigned int k = BIT(deg(poly));
- unsigned int nn = BIT(mm) - 1;
-
- /* primitive polynomial must be of degree m */
- if (k != (1u << mm))
- return -EINVAL;
-
- for (i = 0; i < nn; i++) {
- gf_tables->alpha_to[i] = x;
- gf_tables->index_of[x] = i;
- if (i && (x == 1))
- /* polynomial is not primitive (a^i=1 with 0<i<2^m-1) */
- return -EINVAL;
- x <<= 1;
- if (x & k)
- x ^= poly;
- }
- gf_tables->alpha_to[nn] = 1;
- gf_tables->index_of[0] = 0;
-
- return 0;
-}
-
-static const struct atmel_pmecc_gf_tables *
-atmel_pmecc_create_gf_tables(const struct atmel_pmecc_user_req *req)
-{
- struct atmel_pmecc_gf_tables *gf_tables;
- unsigned int poly, degree, table_size;
- int ret;
-
- if (req->ecc.sectorsize == 512) {
- degree = PMECC_GF_DIMENSION_13;
- poly = PMECC_GF_13_PRIMITIVE_POLY;
- table_size = PMECC_LOOKUP_TABLE_SIZE_512;
- } else {
- degree = PMECC_GF_DIMENSION_14;
- poly = PMECC_GF_14_PRIMITIVE_POLY;
- table_size = PMECC_LOOKUP_TABLE_SIZE_1024;
- }
-
- gf_tables = kzalloc(sizeof(*gf_tables) +
- (2 * table_size * sizeof(u16)),
- GFP_KERNEL);
- if (!gf_tables)
- return ERR_PTR(-ENOMEM);
-
- gf_tables->alpha_to = (void *)(gf_tables + 1);
- gf_tables->index_of = gf_tables->alpha_to + table_size;
-
- ret = atmel_pmecc_build_gf_tables(degree, poly, gf_tables);
- if (ret) {
- kfree(gf_tables);
- return ERR_PTR(ret);
- }
-
- return gf_tables;
-}
-
-static const struct atmel_pmecc_gf_tables *
-atmel_pmecc_get_gf_tables(const struct atmel_pmecc_user_req *req)
-{
- const struct atmel_pmecc_gf_tables **gf_tables, *ret;
-
- mutex_lock(&pmecc_gf_tables_lock);
- if (req->ecc.sectorsize == 512)
- gf_tables = &pmecc_gf_tables_512;
- else
- gf_tables = &pmecc_gf_tables_1024;
-
- ret = *gf_tables;
-
- if (!ret) {
- ret = atmel_pmecc_create_gf_tables(req);
- if (!IS_ERR(ret))
- *gf_tables = ret;
- }
- mutex_unlock(&pmecc_gf_tables_lock);
-
- return ret;
-}
-
-static int atmel_pmecc_prepare_user_req(struct atmel_pmecc *pmecc,
- struct atmel_pmecc_user_req *req)
-{
- int i, max_eccbytes, eccbytes = 0, eccstrength = 0;
-
- if (req->pagesize <= 0 || req->oobsize <= 0 || req->ecc.bytes <= 0)
- return -EINVAL;
-
- if (req->ecc.ooboffset >= 0 &&
- req->ecc.ooboffset + req->ecc.bytes > req->oobsize)
- return -EINVAL;
-
- if (req->ecc.sectorsize == ATMEL_PMECC_SECTOR_SIZE_AUTO) {
- if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH)
- return -EINVAL;
-
- if (req->pagesize > 512)
- req->ecc.sectorsize = 1024;
- else
- req->ecc.sectorsize = 512;
- }
-
- if (req->ecc.sectorsize != 512 && req->ecc.sectorsize != 1024)
- return -EINVAL;
-
- if (req->pagesize % req->ecc.sectorsize)
- return -EINVAL;
-
- req->ecc.nsectors = req->pagesize / req->ecc.sectorsize;
-
- max_eccbytes = req->ecc.bytes;
-
- for (i = 0; i < pmecc->caps->nstrengths; i++) {
- int nbytes, strength = pmecc->caps->strengths[i];
-
- if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH &&
- strength < req->ecc.strength)
- continue;
-
- nbytes = DIV_ROUND_UP(strength * fls(8 * req->ecc.sectorsize),
- 8);
- nbytes *= req->ecc.nsectors;
-
- if (nbytes > max_eccbytes)
- break;
-
- eccstrength = strength;
- eccbytes = nbytes;
-
- if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH)
- break;
- }
-
- if (!eccstrength)
- return -EINVAL;
-
- req->ecc.bytes = eccbytes;
- req->ecc.strength = eccstrength;
-
- if (req->ecc.ooboffset < 0)
- req->ecc.ooboffset = req->oobsize - eccbytes;
-
- return 0;
-}
-
-struct atmel_pmecc_user *
-atmel_pmecc_create_user(struct atmel_pmecc *pmecc,
- struct atmel_pmecc_user_req *req)
-{
- struct atmel_pmecc_user *user;
- const struct atmel_pmecc_gf_tables *gf_tables;
- int strength, size, ret;
-
- ret = atmel_pmecc_prepare_user_req(pmecc, req);
- if (ret)
- return ERR_PTR(ret);
-
- size = sizeof(*user);
- size = ALIGN(size, sizeof(u16));
- /* Reserve space for partial_syn, si and smu */
- size += ((2 * req->ecc.strength) + 1) * sizeof(u16) *
- (2 + req->ecc.strength + 2);
- /* Reserve space for lmu. */
- size += (req->ecc.strength + 1) * sizeof(u16);
- /* Reserve space for mu, dmu and delta. */
- size = ALIGN(size, sizeof(s32));
- size += (req->ecc.strength + 1) * sizeof(s32) * 3;
-
- user = kzalloc(size, GFP_KERNEL);
- if (!user)
- return ERR_PTR(-ENOMEM);
-
- user->pmecc = pmecc;
-
- user->partial_syn = (s16 *)PTR_ALIGN(user + 1, sizeof(u16));
- user->si = user->partial_syn + ((2 * req->ecc.strength) + 1);
- user->lmu = user->si + ((2 * req->ecc.strength) + 1);
- user->smu = user->lmu + (req->ecc.strength + 1);
- user->mu = (s32 *)PTR_ALIGN(user->smu +
- (((2 * req->ecc.strength) + 1) *
- (req->ecc.strength + 2)),
- sizeof(s32));
- user->dmu = user->mu + req->ecc.strength + 1;
- user->delta = user->dmu + req->ecc.strength + 1;
-
- gf_tables = atmel_pmecc_get_gf_tables(req);
- if (IS_ERR(gf_tables)) {
- kfree(user);
- return ERR_CAST(gf_tables);
- }
-
- user->gf_tables = gf_tables;
-
- user->eccbytes = req->ecc.bytes / req->ecc.nsectors;
-
- for (strength = 0; strength < pmecc->caps->nstrengths; strength++) {
- if (pmecc->caps->strengths[strength] == req->ecc.strength)
- break;
- }
-
- user->cache.cfg = PMECC_CFG_BCH_STRENGTH(strength) |
- PMECC_CFG_NSECTORS(req->ecc.nsectors);
-
- if (req->ecc.sectorsize == 1024)
- user->cache.cfg |= PMECC_CFG_SECTOR1024;
-
- user->cache.sarea = req->oobsize - 1;
- user->cache.saddr = req->ecc.ooboffset;
- user->cache.eaddr = req->ecc.ooboffset + req->ecc.bytes - 1;
-
- return user;
-}
-EXPORT_SYMBOL_GPL(atmel_pmecc_create_user);
-
-void atmel_pmecc_destroy_user(struct atmel_pmecc_user *user)
-{
- kfree(user);
-}
-EXPORT_SYMBOL_GPL(atmel_pmecc_destroy_user);
-
-static int get_strength(struct atmel_pmecc_user *user)
-{
- const int *strengths = user->pmecc->caps->strengths;
-
- return strengths[user->cache.cfg & PMECC_CFG_BCH_STRENGTH_MASK];
-}
-
-static int get_sectorsize(struct atmel_pmecc_user *user)
-{
- return user->cache.cfg & PMECC_LOOKUP_TABLE_SIZE_1024 ? 1024 : 512;
-}
-
-static void atmel_pmecc_gen_syndrome(struct atmel_pmecc_user *user, int sector)
-{
- int strength = get_strength(user);
- u32 value;
- int i;
-
- /* Fill odd syndromes */
- for (i = 0; i < strength; i++) {
- value = readl_relaxed(user->pmecc->regs.base +
- ATMEL_PMECC_REM(sector, i / 2));
- if (i & 1)
- value >>= 16;
-
- user->partial_syn[(2 * i) + 1] = value;
- }
-}
-
-static void atmel_pmecc_substitute(struct atmel_pmecc_user *user)
-{
- int degree = get_sectorsize(user) == 512 ? 13 : 14;
- int cw_len = BIT(degree) - 1;
- int strength = get_strength(user);
- s16 *alpha_to = user->gf_tables->alpha_to;
- s16 *index_of = user->gf_tables->index_of;
- s16 *partial_syn = user->partial_syn;
- s16 *si;
- int i, j;
-
- /*
- * si[] is a table that holds the current syndrome value,
- * an element of that table belongs to the field
- */
- si = user->si;
-
- memset(&si[1], 0, sizeof(s16) * ((2 * strength) - 1));
-
- /* Computation 2t syndromes based on S(x) */
- /* Odd syndromes */
- for (i = 1; i < 2 * strength; i += 2) {
- for (j = 0; j < degree; j++) {
- if (partial_syn[i] & BIT(j))
- si[i] = alpha_to[i * j] ^ si[i];
- }
- }
- /* Even syndrome = (Odd syndrome) ** 2 */
- for (i = 2, j = 1; j <= strength; i = ++j << 1) {
- if (si[j] == 0) {
- si[i] = 0;
- } else {
- s16 tmp;
-
- tmp = index_of[si[j]];
- tmp = (tmp * 2) % cw_len;
- si[i] = alpha_to[tmp];
- }
- }
-}
-
-static void atmel_pmecc_get_sigma(struct atmel_pmecc_user *user)
-{
- s16 *lmu = user->lmu;
- s16 *si = user->si;
- s32 *mu = user->mu;
- s32 *dmu = user->dmu;
- s32 *delta = user->delta;
- int degree = get_sectorsize(user) == 512 ? 13 : 14;
- int cw_len = BIT(degree) - 1;
- int strength = get_strength(user);
- int num = 2 * strength + 1;
- s16 *index_of = user->gf_tables->index_of;
- s16 *alpha_to = user->gf_tables->alpha_to;
- int i, j, k;
- u32 dmu_0_count, tmp;
- s16 *smu = user->smu;
-
- /* index of largest delta */
- int ro;
- int largest;
- int diff;
-
- dmu_0_count = 0;
-
- /* First Row */
-
- /* Mu */
- mu[0] = -1;
-
- memset(smu, 0, sizeof(s16) * num);
- smu[0] = 1;
-
- /* discrepancy set to 1 */
- dmu[0] = 1;
- /* polynom order set to 0 */
- lmu[0] = 0;
- delta[0] = (mu[0] * 2 - lmu[0]) >> 1;
-
- /* Second Row */
-
- /* Mu */
- mu[1] = 0;
- /* Sigma(x) set to 1 */
- memset(&smu[num], 0, sizeof(s16) * num);
- smu[num] = 1;
-
- /* discrepancy set to S1 */
- dmu[1] = si[1];
-
- /* polynom order set to 0 */
- lmu[1] = 0;
-
- delta[1] = (mu[1] * 2 - lmu[1]) >> 1;
-
- /* Init the Sigma(x) last row */
- memset(&smu[(strength + 1) * num], 0, sizeof(s16) * num);
-
- for (i = 1; i <= strength; i++) {
- mu[i + 1] = i << 1;
- /* Begin Computing Sigma (Mu+1) and L(mu) */
- /* check if discrepancy is set to 0 */
- if (dmu[i] == 0) {
- dmu_0_count++;
-
- tmp = ((strength - (lmu[i] >> 1) - 1) / 2);
- if ((strength - (lmu[i] >> 1) - 1) & 0x1)
- tmp += 2;
- else
- tmp += 1;
-
- if (dmu_0_count == tmp) {
- for (j = 0; j <= (lmu[i] >> 1) + 1; j++)
- smu[(strength + 1) * num + j] =
- smu[i * num + j];
-
- lmu[strength + 1] = lmu[i];
- return;
- }
-
- /* copy polynom */
- for (j = 0; j <= lmu[i] >> 1; j++)
- smu[(i + 1) * num + j] = smu[i * num + j];
-
- /* copy previous polynom order to the next */
- lmu[i + 1] = lmu[i];
- } else {
- ro = 0;
- largest = -1;
- /* find largest delta with dmu != 0 */
- for (j = 0; j < i; j++) {
- if ((dmu[j]) && (delta[j] > largest)) {
- largest = delta[j];
- ro = j;
- }
- }
-
- /* compute difference */
- diff = (mu[i] - mu[ro]);
-
- /* Compute degree of the new smu polynomial */
- if ((lmu[i] >> 1) > ((lmu[ro] >> 1) + diff))
- lmu[i + 1] = lmu[i];
- else
- lmu[i + 1] = ((lmu[ro] >> 1) + diff) * 2;
-
- /* Init smu[i+1] with 0 */
- for (k = 0; k < num; k++)
- smu[(i + 1) * num + k] = 0;
-
- /* Compute smu[i+1] */
- for (k = 0; k <= lmu[ro] >> 1; k++) {
- s16 a, b, c;
-
- if (!(smu[ro * num + k] && dmu[i]))
- continue;
-
- a = index_of[dmu[i]];
- b = index_of[dmu[ro]];
- c = index_of[smu[ro * num + k]];
- tmp = a + (cw_len - b) + c;
- a = alpha_to[tmp % cw_len];
- smu[(i + 1) * num + (k + diff)] = a;
- }
-
- for (k = 0; k <= lmu[i] >> 1; k++)
- smu[(i + 1) * num + k] ^= smu[i * num + k];
- }
-
- /* End Computing Sigma (Mu+1) and L(mu) */
- /* In either case compute delta */
- delta[i + 1] = (mu[i + 1] * 2 - lmu[i + 1]) >> 1;
-
- /* Do not compute discrepancy for the last iteration */
- if (i >= strength)
- continue;
-
- for (k = 0; k <= (lmu[i + 1] >> 1); k++) {
- tmp = 2 * (i - 1);
- if (k == 0) {
- dmu[i + 1] = si[tmp + 3];
- } else if (smu[(i + 1) * num + k] && si[tmp + 3 - k]) {
- s16 a, b, c;
-
- a = index_of[smu[(i + 1) * num + k]];
- b = si[2 * (i - 1) + 3 - k];
- c = index_of[b];
- tmp = a + c;
- tmp %= cw_len;
- dmu[i + 1] = alpha_to[tmp] ^ dmu[i + 1];
- }
- }
- }
-}
-
-static int atmel_pmecc_err_location(struct atmel_pmecc_user *user)
-{
- int sector_size = get_sectorsize(user);
- int degree = sector_size == 512 ? 13 : 14;
- struct atmel_pmecc *pmecc = user->pmecc;
- int strength = get_strength(user);
- int ret, roots_nbr, i, err_nbr = 0;
- int num = (2 * strength) + 1;
- s16 *smu = user->smu;
- u32 val;
-
- writel(PMERRLOC_DISABLE, pmecc->regs.errloc + ATMEL_PMERRLOC_ELDIS);
-
- for (i = 0; i <= user->lmu[strength + 1] >> 1; i++) {
- writel_relaxed(smu[(strength + 1) * num + i],
- pmecc->regs.errloc + ATMEL_PMERRLOC_SIGMA(i));
- err_nbr++;
- }
-
- val = (err_nbr - 1) << 16;
- if (sector_size == 1024)
- val |= 1;
-
- writel(val, pmecc->regs.errloc + ATMEL_PMERRLOC_ELCFG);
- writel((sector_size * 8) + (degree * strength),
- pmecc->regs.errloc + ATMEL_PMERRLOC_ELEN);
-
- ret = readl_relaxed_poll_timeout(pmecc->regs.errloc +
- ATMEL_PMERRLOC_ELISR,
- val, val & PMERRLOC_CALC_DONE, 0,
- PMECC_MAX_TIMEOUT_MS * 1000);
- if (ret) {
- dev_err(pmecc->dev,
- "PMECC: Timeout to calculate error location.\n");
- return ret;
- }
-
- roots_nbr = (val & PMERRLOC_ERR_NUM_MASK) >> 8;
- /* Number of roots == degree of smu hence <= cap */
- if (roots_nbr == user->lmu[strength + 1] >> 1)
- return err_nbr - 1;
-
- /*
- * Number of roots does not match the degree of smu
- * unable to correct error.
- */
- return -EBADMSG;
-}
-
-int atmel_pmecc_correct_sector(struct atmel_pmecc_user *user, int sector,
- void *data, void *ecc)
-{
- struct atmel_pmecc *pmecc = user->pmecc;
- int sectorsize = get_sectorsize(user);
- int eccbytes = user->eccbytes;
- int i, nerrors;
-
- if (!(user->isr & BIT(sector)))
- return 0;
-
- atmel_pmecc_gen_syndrome(user, sector);
- atmel_pmecc_substitute(user);
- atmel_pmecc_get_sigma(user);
-
- nerrors = atmel_pmecc_err_location(user);
- if (nerrors < 0)
- return nerrors;
-
- for (i = 0; i < nerrors; i++) {
- const char *area;
- int byte, bit;
- u32 errpos;
- u8 *ptr;
-
- errpos = readl_relaxed(pmecc->regs.errloc +
- ATMEL_PMERRLOC_EL(pmecc->caps->el_offset, i));
- errpos--;
-
- byte = errpos / 8;
- bit = errpos % 8;
-
- if (byte < sectorsize) {
- ptr = data + byte;
- area = "data";
- } else if (byte < sectorsize + eccbytes) {
- ptr = ecc + byte - sectorsize;
- area = "ECC";
- } else {
- dev_dbg(pmecc->dev,
- "Invalid errpos value (%d, max is %d)\n",
- errpos, (sectorsize + eccbytes) * 8);
- return -EINVAL;
- }
-
- dev_dbg(pmecc->dev,
- "Bit flip in %s area, byte %d: 0x%02x -> 0x%02x\n",
- area, byte, *ptr, (unsigned int)(*ptr ^ BIT(bit)));
-
- *ptr ^= BIT(bit);
- }
-
- return nerrors;
-}
-EXPORT_SYMBOL_GPL(atmel_pmecc_correct_sector);
-
-bool atmel_pmecc_correct_erased_chunks(struct atmel_pmecc_user *user)
-{
- return user->pmecc->caps->correct_erased_chunks;
-}
-EXPORT_SYMBOL_GPL(atmel_pmecc_correct_erased_chunks);
-
-void atmel_pmecc_get_generated_eccbytes(struct atmel_pmecc_user *user,
- int sector, void *ecc)
-{
- struct atmel_pmecc *pmecc = user->pmecc;
- u8 *ptr = ecc;
- int i;
-
- for (i = 0; i < user->eccbytes; i++)
- ptr[i] = readb_relaxed(pmecc->regs.base +
- ATMEL_PMECC_ECC(sector, i));
-}
-EXPORT_SYMBOL_GPL(atmel_pmecc_get_generated_eccbytes);
-
-void atmel_pmecc_reset(struct atmel_pmecc *pmecc)
-{
- writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
- writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
-}
-EXPORT_SYMBOL_GPL(atmel_pmecc_reset);
-
-int atmel_pmecc_enable(struct atmel_pmecc_user *user, int op)
-{
- struct atmel_pmecc *pmecc = user->pmecc;
- u32 cfg;
-
- if (op != NAND_ECC_READ && op != NAND_ECC_WRITE) {
- dev_err(pmecc->dev, "Bad ECC operation!");
- return -EINVAL;
- }
-
- mutex_lock(&user->pmecc->lock);
-
- cfg = user->cache.cfg;
- if (op == NAND_ECC_WRITE)
- cfg |= PMECC_CFG_WRITE_OP;
- else
- cfg |= PMECC_CFG_AUTO_ENABLE;
-
- writel(cfg, pmecc->regs.base + ATMEL_PMECC_CFG);
- writel(user->cache.sarea, pmecc->regs.base + ATMEL_PMECC_SAREA);
- writel(user->cache.saddr, pmecc->regs.base + ATMEL_PMECC_SADDR);
- writel(user->cache.eaddr, pmecc->regs.base + ATMEL_PMECC_EADDR);
-
- writel(PMECC_CTRL_ENABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
- writel(PMECC_CTRL_DATA, pmecc->regs.base + ATMEL_PMECC_CTRL);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(atmel_pmecc_enable);
-
-void atmel_pmecc_disable(struct atmel_pmecc_user *user)
-{
- atmel_pmecc_reset(user->pmecc);
- mutex_unlock(&user->pmecc->lock);
-}
-EXPORT_SYMBOL_GPL(atmel_pmecc_disable);
-
-int atmel_pmecc_wait_rdy(struct atmel_pmecc_user *user)
-{
- struct atmel_pmecc *pmecc = user->pmecc;
- u32 status;
- int ret;
-
- ret = readl_relaxed_poll_timeout(pmecc->regs.base +
- ATMEL_PMECC_SR,
- status, !(status & PMECC_SR_BUSY), 0,
- PMECC_MAX_TIMEOUT_MS * 1000);
- if (ret) {
- dev_err(pmecc->dev,
- "Timeout while waiting for PMECC ready.\n");
- return ret;
- }
-
- user->isr = readl_relaxed(pmecc->regs.base + ATMEL_PMECC_ISR);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(atmel_pmecc_wait_rdy);
-
-static struct atmel_pmecc *atmel_pmecc_create(struct platform_device *pdev,
- const struct atmel_pmecc_caps *caps,
- int pmecc_res_idx, int errloc_res_idx)
-{
- struct device *dev = &pdev->dev;
- struct atmel_pmecc *pmecc;
- struct resource *res;
-
- pmecc = devm_kzalloc(dev, sizeof(*pmecc), GFP_KERNEL);
- if (!pmecc)
- return ERR_PTR(-ENOMEM);
-
- pmecc->caps = caps;
- pmecc->dev = dev;
- mutex_init(&pmecc->lock);
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, pmecc_res_idx);
- pmecc->regs.base = devm_ioremap_resource(dev, res);
- if (IS_ERR(pmecc->regs.base))
- return ERR_CAST(pmecc->regs.base);
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, errloc_res_idx);
- pmecc->regs.errloc = devm_ioremap_resource(dev, res);
- if (IS_ERR(pmecc->regs.errloc))
- return ERR_CAST(pmecc->regs.errloc);
-
- /* Disable all interrupts before registering the PMECC handler. */
- writel(0xffffffff, pmecc->regs.base + ATMEL_PMECC_IDR);
- atmel_pmecc_reset(pmecc);
-
- return pmecc;
-}
-
-static void devm_atmel_pmecc_put(struct device *dev, void *res)
-{
- struct atmel_pmecc **pmecc = res;
-
- put_device((*pmecc)->dev);
-}
-
-static struct atmel_pmecc *atmel_pmecc_get_by_node(struct device *userdev,
- struct device_node *np)
-{
- struct platform_device *pdev;
- struct atmel_pmecc *pmecc, **ptr;
-
- pdev = of_find_device_by_node(np);
- if (!pdev || !platform_get_drvdata(pdev))
- return ERR_PTR(-EPROBE_DEFER);
-
- ptr = devres_alloc(devm_atmel_pmecc_put, sizeof(*ptr), GFP_KERNEL);
- if (!ptr)
- return ERR_PTR(-ENOMEM);
-
- get_device(&pdev->dev);
- pmecc = platform_get_drvdata(pdev);
-
- *ptr = pmecc;
-
- devres_add(userdev, ptr);
-
- return pmecc;
-}
-
-static const int atmel_pmecc_strengths[] = { 2, 4, 8, 12, 24, 32 };
-
-static struct atmel_pmecc_caps at91sam9g45_caps = {
- .strengths = atmel_pmecc_strengths,
- .nstrengths = 5,
- .el_offset = 0x8c,
-};
-
-static struct atmel_pmecc_caps sama5d4_caps = {
- .strengths = atmel_pmecc_strengths,
- .nstrengths = 5,
- .el_offset = 0x8c,
- .correct_erased_chunks = true,
-};
-
-static struct atmel_pmecc_caps sama5d2_caps = {
- .strengths = atmel_pmecc_strengths,
- .nstrengths = 6,
- .el_offset = 0xac,
- .correct_erased_chunks = true,
-};
-
-static const struct of_device_id atmel_pmecc_legacy_match[] = {
- { .compatible = "atmel,sama5d4-nand", &sama5d4_caps },
- { .compatible = "atmel,sama5d2-nand", &sama5d2_caps },
- { /* sentinel */ }
-};
-
-struct atmel_pmecc *devm_atmel_pmecc_get(struct device *userdev)
-{
- struct atmel_pmecc *pmecc;
- struct device_node *np;
-
- if (!userdev)
- return ERR_PTR(-EINVAL);
-
- if (!userdev->of_node)
- return NULL;
-
- np = of_parse_phandle(userdev->of_node, "ecc-engine", 0);
- if (np) {
- pmecc = atmel_pmecc_get_by_node(userdev, np);
- of_node_put(np);
- } else {
- /*
- * Support old DT bindings: in this case the PMECC iomem
- * resources are directly defined in the user pdev at position
- * 1 and 2. Extract all relevant information from there.
- */
- struct platform_device *pdev = to_platform_device(userdev);
- const struct atmel_pmecc_caps *caps;
- const struct of_device_id *match;
-
- /* No PMECC engine available. */
- if (!of_property_read_bool(userdev->of_node,
- "atmel,has-pmecc"))
- return NULL;
-
- caps = &at91sam9g45_caps;
-
- /* Find the caps associated to the NAND dev node. */
- match = of_match_node(atmel_pmecc_legacy_match,
- userdev->of_node);
- if (match && match->data)
- caps = match->data;
-
- pmecc = atmel_pmecc_create(pdev, caps, 1, 2);
- }
-
- return pmecc;
-}
-EXPORT_SYMBOL(devm_atmel_pmecc_get);
-
-static const struct of_device_id atmel_pmecc_match[] = {
- { .compatible = "atmel,at91sam9g45-pmecc", &at91sam9g45_caps },
- { .compatible = "atmel,sama5d4-pmecc", &sama5d4_caps },
- { .compatible = "atmel,sama5d2-pmecc", &sama5d2_caps },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, atmel_pmecc_match);
-
-static int atmel_pmecc_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- const struct atmel_pmecc_caps *caps;
- struct atmel_pmecc *pmecc;
-
- caps = of_device_get_match_data(&pdev->dev);
- if (!caps) {
- dev_err(dev, "Invalid caps\n");
- return -EINVAL;
- }
-
- pmecc = atmel_pmecc_create(pdev, caps, 0, 1);
- if (IS_ERR(pmecc))
- return PTR_ERR(pmecc);
-
- platform_set_drvdata(pdev, pmecc);
-
- return 0;
-}
-
-static struct platform_driver atmel_pmecc_driver = {
- .driver = {
- .name = "atmel-pmecc",
- .of_match_table = of_match_ptr(atmel_pmecc_match),
- },
- .probe = atmel_pmecc_probe,
-};
-module_platform_driver(atmel_pmecc_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
-MODULE_DESCRIPTION("PMECC engine driver");
-MODULE_ALIAS("platform:atmel_pmecc");
diff --git a/drivers/mtd/nand/atmel/pmecc.h b/drivers/mtd/nand/atmel/pmecc.h
deleted file mode 100644
index 817e0dd9fd15..000000000000
--- a/drivers/mtd/nand/atmel/pmecc.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * © Copyright 2016 ATMEL
- * © Copyright 2016 Free Electrons
- *
- * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
- *
- * Derived from the atmel_nand.c driver which contained the following
- * copyrights:
- *
- * Copyright © 2003 Rick Bronson
- *
- * Derived from drivers/mtd/nand/autcpu12.c
- * Copyright © 2001 Thomas Gleixner (gleixner@autronix.de)
- *
- * Derived from drivers/mtd/spia.c
- * Copyright © 2000 Steven J. Hill (sjhill@cotw.com)
- *
- *
- * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263
- * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright © 2007
- *
- * Derived from Das U-Boot source code
- * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c)
- * © Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas
- *
- * Add Programmable Multibit ECC support for various AT91 SoC
- * © Copyright 2012 ATMEL, Hong Xu
- *
- * Add Nand Flash Controller support for SAMA5 SoC
- * © Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.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.
- *
- */
-
-#ifndef ATMEL_PMECC_H
-#define ATMEL_PMECC_H
-
-#define ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH 0
-#define ATMEL_PMECC_SECTOR_SIZE_AUTO 0
-#define ATMEL_PMECC_OOBOFFSET_AUTO -1
-
-struct atmel_pmecc_user_req {
- int pagesize;
- int oobsize;
- struct {
- int strength;
- int bytes;
- int sectorsize;
- int nsectors;
- int ooboffset;
- } ecc;
-};
-
-struct atmel_pmecc *devm_atmel_pmecc_get(struct device *dev);
-
-struct atmel_pmecc_user *
-atmel_pmecc_create_user(struct atmel_pmecc *pmecc,
- struct atmel_pmecc_user_req *req);
-void atmel_pmecc_destroy_user(struct atmel_pmecc_user *user);
-
-void atmel_pmecc_reset(struct atmel_pmecc *pmecc);
-int atmel_pmecc_enable(struct atmel_pmecc_user *user, int op);
-void atmel_pmecc_disable(struct atmel_pmecc_user *user);
-int atmel_pmecc_wait_rdy(struct atmel_pmecc_user *user);
-int atmel_pmecc_correct_sector(struct atmel_pmecc_user *user, int sector,
- void *data, void *ecc);
-bool atmel_pmecc_correct_erased_chunks(struct atmel_pmecc_user *user);
-void atmel_pmecc_get_generated_eccbytes(struct atmel_pmecc_user *user,
- int sector, void *ecc);
-
-#endif /* ATMEL_PMECC_H */
diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c
deleted file mode 100644
index 8ab827edf94e..000000000000
--- a/drivers/mtd/nand/au1550nd.c
+++ /dev/null
@@ -1,517 +0,0 @@
-/*
- * drivers/mtd/nand/au1550nd.c
- *
- * Copyright (C) 2004 Embedded Edge, LLC
- *
- * 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.
- *
- */
-
-#include <linux/slab.h>
-#include <linux/gpio.h>
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/partitions.h>
-#include <linux/platform_device.h>
-#include <asm/io.h>
-#include <asm/mach-au1x00/au1000.h>
-#include <asm/mach-au1x00/au1550nd.h>
-
-
-struct au1550nd_ctx {
- struct nand_chip chip;
-
- int cs;
- void __iomem *base;
- void (*write_byte)(struct mtd_info *, u_char);
-};
-
-/**
- * au_read_byte - read one byte from the chip
- * @mtd: MTD device structure
- *
- * read function for 8bit buswidth
- */
-static u_char au_read_byte(struct mtd_info *mtd)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- u_char ret = readb(this->IO_ADDR_R);
- wmb(); /* drain writebuffer */
- return ret;
-}
-
-/**
- * au_write_byte - write one byte to the chip
- * @mtd: MTD device structure
- * @byte: pointer to data byte to write
- *
- * write function for 8it buswidth
- */
-static void au_write_byte(struct mtd_info *mtd, u_char byte)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- writeb(byte, this->IO_ADDR_W);
- wmb(); /* drain writebuffer */
-}
-
-/**
- * au_read_byte16 - read one byte endianness aware from the chip
- * @mtd: MTD device structure
- *
- * read function for 16bit buswidth with endianness conversion
- */
-static u_char au_read_byte16(struct mtd_info *mtd)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- u_char ret = (u_char) cpu_to_le16(readw(this->IO_ADDR_R));
- wmb(); /* drain writebuffer */
- return ret;
-}
-
-/**
- * au_write_byte16 - write one byte endianness aware to the chip
- * @mtd: MTD device structure
- * @byte: pointer to data byte to write
- *
- * write function for 16bit buswidth with endianness conversion
- */
-static void au_write_byte16(struct mtd_info *mtd, u_char byte)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- writew(le16_to_cpu((u16) byte), this->IO_ADDR_W);
- wmb(); /* drain writebuffer */
-}
-
-/**
- * au_read_word - read one word from the chip
- * @mtd: MTD device structure
- *
- * read function for 16bit buswidth without endianness conversion
- */
-static u16 au_read_word(struct mtd_info *mtd)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- u16 ret = readw(this->IO_ADDR_R);
- wmb(); /* drain writebuffer */
- return ret;
-}
-
-/**
- * au_write_buf - write buffer to chip
- * @mtd: MTD device structure
- * @buf: data buffer
- * @len: number of bytes to write
- *
- * write function for 8bit buswidth
- */
-static void au_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
-{
- int i;
- struct nand_chip *this = mtd_to_nand(mtd);
-
- for (i = 0; i < len; i++) {
- writeb(buf[i], this->IO_ADDR_W);
- wmb(); /* drain writebuffer */
- }
-}
-
-/**
- * au_read_buf - read chip data into buffer
- * @mtd: MTD device structure
- * @buf: buffer to store date
- * @len: number of bytes to read
- *
- * read function for 8bit buswidth
- */
-static void au_read_buf(struct mtd_info *mtd, u_char *buf, int len)
-{
- int i;
- struct nand_chip *this = mtd_to_nand(mtd);
-
- for (i = 0; i < len; i++) {
- buf[i] = readb(this->IO_ADDR_R);
- wmb(); /* drain writebuffer */
- }
-}
-
-/**
- * au_write_buf16 - write buffer to chip
- * @mtd: MTD device structure
- * @buf: data buffer
- * @len: number of bytes to write
- *
- * write function for 16bit buswidth
- */
-static void au_write_buf16(struct mtd_info *mtd, const u_char *buf, int len)
-{
- int i;
- struct nand_chip *this = mtd_to_nand(mtd);
- u16 *p = (u16 *) buf;
- len >>= 1;
-
- for (i = 0; i < len; i++) {
- writew(p[i], this->IO_ADDR_W);
- wmb(); /* drain writebuffer */
- }
-
-}
-
-/**
- * au_read_buf16 - read chip data into buffer
- * @mtd: MTD device structure
- * @buf: buffer to store date
- * @len: number of bytes to read
- *
- * read function for 16bit buswidth
- */
-static void au_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
-{
- int i;
- struct nand_chip *this = mtd_to_nand(mtd);
- u16 *p = (u16 *) buf;
- len >>= 1;
-
- for (i = 0; i < len; i++) {
- p[i] = readw(this->IO_ADDR_R);
- wmb(); /* drain writebuffer */
- }
-}
-
-/* Select the chip by setting nCE to low */
-#define NAND_CTL_SETNCE 1
-/* Deselect the chip by setting nCE to high */
-#define NAND_CTL_CLRNCE 2
-/* Select the command latch by setting CLE to high */
-#define NAND_CTL_SETCLE 3
-/* Deselect the command latch by setting CLE to low */
-#define NAND_CTL_CLRCLE 4
-/* Select the address latch by setting ALE to high */
-#define NAND_CTL_SETALE 5
-/* Deselect the address latch by setting ALE to low */
-#define NAND_CTL_CLRALE 6
-
-static void au1550_hwcontrol(struct mtd_info *mtd, int cmd)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct au1550nd_ctx *ctx = container_of(this, struct au1550nd_ctx,
- chip);
-
- switch (cmd) {
-
- case NAND_CTL_SETCLE:
- this->IO_ADDR_W = ctx->base + MEM_STNAND_CMD;
- break;
-
- case NAND_CTL_CLRCLE:
- this->IO_ADDR_W = ctx->base + MEM_STNAND_DATA;
- break;
-
- case NAND_CTL_SETALE:
- this->IO_ADDR_W = ctx->base + MEM_STNAND_ADDR;
- break;
-
- case NAND_CTL_CLRALE:
- this->IO_ADDR_W = ctx->base + MEM_STNAND_DATA;
- /* FIXME: Nobody knows why this is necessary,
- * but it works only that way */
- udelay(1);
- break;
-
- case NAND_CTL_SETNCE:
- /* assert (force assert) chip enable */
- alchemy_wrsmem((1 << (4 + ctx->cs)), AU1000_MEM_STNDCTL);
- break;
-
- case NAND_CTL_CLRNCE:
- /* deassert chip enable */
- alchemy_wrsmem(0, AU1000_MEM_STNDCTL);
- break;
- }
-
- this->IO_ADDR_R = this->IO_ADDR_W;
-
- wmb(); /* Drain the writebuffer */
-}
-
-int au1550_device_ready(struct mtd_info *mtd)
-{
- return (alchemy_rdsmem(AU1000_MEM_STSTAT) & 0x1) ? 1 : 0;
-}
-
-/**
- * au1550_select_chip - control -CE line
- * Forbid driving -CE manually permitting the NAND controller to do this.
- * Keeping -CE asserted during the whole sector reads interferes with the
- * NOR flash and PCMCIA drivers as it causes contention on the static bus.
- * We only have to hold -CE low for the NAND read commands since the flash
- * chip needs it to be asserted during chip not ready time but the NAND
- * controller keeps it released.
- *
- * @mtd: MTD device structure
- * @chip: chipnumber to select, -1 for deselect
- */
-static void au1550_select_chip(struct mtd_info *mtd, int chip)
-{
-}
-
-/**
- * au1550_command - Send command to NAND device
- * @mtd: MTD device structure
- * @command: the command to be sent
- * @column: the column address for this command, -1 if none
- * @page_addr: the page address for this command, -1 if none
- */
-static void au1550_command(struct mtd_info *mtd, unsigned command, int column, int page_addr)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct au1550nd_ctx *ctx = container_of(this, struct au1550nd_ctx,
- chip);
- int ce_override = 0, i;
- unsigned long flags = 0;
-
- /* Begin command latch cycle */
- au1550_hwcontrol(mtd, NAND_CTL_SETCLE);
- /*
- * Write out the command to the device.
- */
- if (command == NAND_CMD_SEQIN) {
- int readcmd;
-
- if (column >= mtd->writesize) {
- /* OOB area */
- column -= mtd->writesize;
- readcmd = NAND_CMD_READOOB;
- } else if (column < 256) {
- /* First 256 bytes --> READ0 */
- readcmd = NAND_CMD_READ0;
- } else {
- column -= 256;
- readcmd = NAND_CMD_READ1;
- }
- ctx->write_byte(mtd, readcmd);
- }
- ctx->write_byte(mtd, command);
-
- /* Set ALE and clear CLE to start address cycle */
- au1550_hwcontrol(mtd, NAND_CTL_CLRCLE);
-
- if (column != -1 || page_addr != -1) {
- au1550_hwcontrol(mtd, NAND_CTL_SETALE);
-
- /* Serially input address */
- if (column != -1) {
- /* Adjust columns for 16 bit buswidth */
- if (this->options & NAND_BUSWIDTH_16 &&
- !nand_opcode_8bits(command))
- column >>= 1;
- ctx->write_byte(mtd, column);
- }
- if (page_addr != -1) {
- ctx->write_byte(mtd, (u8)(page_addr & 0xff));
-
- if (command == NAND_CMD_READ0 ||
- command == NAND_CMD_READ1 ||
- command == NAND_CMD_READOOB) {
- /*
- * NAND controller will release -CE after
- * the last address byte is written, so we'll
- * have to forcibly assert it. No interrupts
- * are allowed while we do this as we don't
- * want the NOR flash or PCMCIA drivers to
- * steal our precious bytes of data...
- */
- ce_override = 1;
- local_irq_save(flags);
- au1550_hwcontrol(mtd, NAND_CTL_SETNCE);
- }
-
- ctx->write_byte(mtd, (u8)(page_addr >> 8));
-
- if (this->options & NAND_ROW_ADDR_3)
- ctx->write_byte(mtd,
- ((page_addr >> 16) & 0x0f));
- }
- /* Latch in address */
- au1550_hwcontrol(mtd, NAND_CTL_CLRALE);
- }
-
- /*
- * Program and erase have their own busy handlers.
- * Status and sequential in need no delay.
- */
- switch (command) {
-
- case NAND_CMD_PAGEPROG:
- case NAND_CMD_ERASE1:
- case NAND_CMD_ERASE2:
- case NAND_CMD_SEQIN:
- case NAND_CMD_STATUS:
- return;
-
- case NAND_CMD_RESET:
- break;
-
- case NAND_CMD_READ0:
- case NAND_CMD_READ1:
- case NAND_CMD_READOOB:
- /* Check if we're really driving -CE low (just in case) */
- if (unlikely(!ce_override))
- break;
-
- /* Apply a short delay always to ensure that we do wait tWB. */
- ndelay(100);
- /* Wait for a chip to become ready... */
- for (i = this->chip_delay; !this->dev_ready(mtd) && i > 0; --i)
- udelay(1);
-
- /* Release -CE and re-enable interrupts. */
- au1550_hwcontrol(mtd, NAND_CTL_CLRNCE);
- local_irq_restore(flags);
- return;
- }
- /* Apply this short delay always to ensure that we do wait tWB. */
- ndelay(100);
-
- while(!this->dev_ready(mtd));
-}
-
-static int find_nand_cs(unsigned long nand_base)
-{
- void __iomem *base =
- (void __iomem *)KSEG1ADDR(AU1000_STATIC_MEM_PHYS_ADDR);
- unsigned long addr, staddr, start, mask, end;
- int i;
-
- for (i = 0; i < 4; i++) {
- addr = 0x1000 + (i * 0x10); /* CSx */
- staddr = __raw_readl(base + addr + 0x08); /* STADDRx */
- /* figure out the decoded range of this CS */
- start = (staddr << 4) & 0xfffc0000;
- mask = (staddr << 18) & 0xfffc0000;
- end = (start | (start - 1)) & ~(start ^ mask);
- if ((nand_base >= start) && (nand_base < end))
- return i;
- }
-
- return -ENODEV;
-}
-
-static int au1550nd_probe(struct platform_device *pdev)
-{
- struct au1550nd_platdata *pd;
- struct au1550nd_ctx *ctx;
- struct nand_chip *this;
- struct mtd_info *mtd;
- struct resource *r;
- int ret, cs;
-
- pd = dev_get_platdata(&pdev->dev);
- if (!pd) {
- dev_err(&pdev->dev, "missing platform data\n");
- return -ENODEV;
- }
-
- ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
- if (!ctx)
- return -ENOMEM;
-
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!r) {
- dev_err(&pdev->dev, "no NAND memory resource\n");
- ret = -ENODEV;
- goto out1;
- }
- if (request_mem_region(r->start, resource_size(r), "au1550-nand")) {
- dev_err(&pdev->dev, "cannot claim NAND memory area\n");
- ret = -ENOMEM;
- goto out1;
- }
-
- ctx->base = ioremap_nocache(r->start, 0x1000);
- if (!ctx->base) {
- dev_err(&pdev->dev, "cannot remap NAND memory area\n");
- ret = -ENODEV;
- goto out2;
- }
-
- this = &ctx->chip;
- mtd = nand_to_mtd(this);
- mtd->dev.parent = &pdev->dev;
-
- /* figure out which CS# r->start belongs to */
- cs = find_nand_cs(r->start);
- if (cs < 0) {
- dev_err(&pdev->dev, "cannot detect NAND chipselect\n");
- ret = -ENODEV;
- goto out3;
- }
- ctx->cs = cs;
-
- this->dev_ready = au1550_device_ready;
- this->select_chip = au1550_select_chip;
- this->cmdfunc = au1550_command;
-
- /* 30 us command delay time */
- this->chip_delay = 30;
- this->ecc.mode = NAND_ECC_SOFT;
- this->ecc.algo = NAND_ECC_HAMMING;
-
- if (pd->devwidth)
- this->options |= NAND_BUSWIDTH_16;
-
- this->read_byte = (pd->devwidth) ? au_read_byte16 : au_read_byte;
- ctx->write_byte = (pd->devwidth) ? au_write_byte16 : au_write_byte;
- this->read_word = au_read_word;
- this->write_buf = (pd->devwidth) ? au_write_buf16 : au_write_buf;
- this->read_buf = (pd->devwidth) ? au_read_buf16 : au_read_buf;
-
- ret = nand_scan(mtd, 1);
- if (ret) {
- dev_err(&pdev->dev, "NAND scan failed with %d\n", ret);
- goto out3;
- }
-
- mtd_device_register(mtd, pd->parts, pd->num_parts);
-
- platform_set_drvdata(pdev, ctx);
-
- return 0;
-
-out3:
- iounmap(ctx->base);
-out2:
- release_mem_region(r->start, resource_size(r));
-out1:
- kfree(ctx);
- return ret;
-}
-
-static int au1550nd_remove(struct platform_device *pdev)
-{
- struct au1550nd_ctx *ctx = platform_get_drvdata(pdev);
- struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
- nand_release(nand_to_mtd(&ctx->chip));
- iounmap(ctx->base);
- release_mem_region(r->start, 0x1000);
- kfree(ctx);
- return 0;
-}
-
-static struct platform_driver au1550nd_driver = {
- .driver = {
- .name = "au1550-nand",
- },
- .probe = au1550nd_probe,
- .remove = au1550nd_remove,
-};
-
-module_platform_driver(au1550nd_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Embedded Edge, LLC");
-MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on Pb1550 board");
diff --git a/drivers/mtd/nand/bbt.c b/drivers/mtd/nand/bbt.c
new file mode 100644
index 000000000000..56cde38b92c0
--- /dev/null
+++ b/drivers/mtd/nand/bbt.c
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2017 Free Electrons
+ *
+ * Authors:
+ * Boris Brezillon <boris.brezillon@free-electrons.com>
+ * Peter Pan <peterpandong@micron.com>
+ */
+
+#define pr_fmt(fmt) "nand-bbt: " fmt
+
+#include <linux/mtd/nand.h>
+#include <linux/slab.h>
+
+/**
+ * nanddev_bbt_init() - Initialize the BBT (Bad Block Table)
+ * @nand: NAND device
+ *
+ * Initialize the in-memory BBT.
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int nanddev_bbt_init(struct nand_device *nand)
+{
+ unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
+ unsigned int nblocks = nanddev_neraseblocks(nand);
+ unsigned int nwords = DIV_ROUND_UP(nblocks * bits_per_block,
+ BITS_PER_LONG);
+
+ nand->bbt.cache = kzalloc(nwords, GFP_KERNEL);
+ if (!nand->bbt.cache)
+ return -ENOMEM;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nanddev_bbt_init);
+
+/**
+ * nanddev_bbt_cleanup() - Cleanup the BBT (Bad Block Table)
+ * @nand: NAND device
+ *
+ * Undoes what has been done in nanddev_bbt_init()
+ */
+void nanddev_bbt_cleanup(struct nand_device *nand)
+{
+ kfree(nand->bbt.cache);
+}
+EXPORT_SYMBOL_GPL(nanddev_bbt_cleanup);
+
+/**
+ * nanddev_bbt_update() - Update a BBT
+ * @nand: nand device
+ *
+ * Update the BBT. Currently a NOP function since on-flash bbt is not yet
+ * supported.
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int nanddev_bbt_update(struct nand_device *nand)
+{
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nanddev_bbt_update);
+
+/**
+ * nanddev_bbt_get_block_status() - Return the status of an eraseblock
+ * @nand: nand device
+ * @entry: the BBT entry
+ *
+ * Return: a positive number nand_bbt_block_status status or -%ERANGE if @entry
+ * is bigger than the BBT size.
+ */
+int nanddev_bbt_get_block_status(const struct nand_device *nand,
+ unsigned int entry)
+{
+ unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
+ unsigned long *pos = nand->bbt.cache +
+ ((entry * bits_per_block) / BITS_PER_LONG);
+ unsigned int offs = (entry * bits_per_block) % BITS_PER_LONG;
+ unsigned long status;
+
+ if (entry >= nanddev_neraseblocks(nand))
+ return -ERANGE;
+
+ status = pos[0] >> offs;
+ if (bits_per_block + offs > BITS_PER_LONG)
+ status |= pos[1] << (BITS_PER_LONG - offs);
+
+ return status & GENMASK(bits_per_block - 1, 0);
+}
+EXPORT_SYMBOL_GPL(nanddev_bbt_get_block_status);
+
+/**
+ * nanddev_bbt_set_block_status() - Update the status of an eraseblock in the
+ * in-memory BBT
+ * @nand: nand device
+ * @entry: the BBT entry to update
+ * @status: the new status
+ *
+ * Update an entry of the in-memory BBT. If you want to push the updated BBT
+ * the NAND you should call nanddev_bbt_update().
+ *
+ * Return: 0 in case of success or -%ERANGE if @entry is bigger than the BBT
+ * size.
+ */
+int nanddev_bbt_set_block_status(struct nand_device *nand, unsigned int entry,
+ enum nand_bbt_block_status status)
+{
+ unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
+ unsigned long *pos = nand->bbt.cache +
+ ((entry * bits_per_block) / BITS_PER_LONG);
+ unsigned int offs = (entry * bits_per_block) % BITS_PER_LONG;
+ unsigned long val = status & GENMASK(bits_per_block - 1, 0);
+
+ if (entry >= nanddev_neraseblocks(nand))
+ return -ERANGE;
+
+ pos[0] &= ~GENMASK(offs + bits_per_block - 1, offs);
+ pos[0] |= val << offs;
+
+ if (bits_per_block + offs > BITS_PER_LONG) {
+ unsigned int rbits = bits_per_block + offs - BITS_PER_LONG;
+
+ pos[1] &= ~GENMASK(rbits - 1, 0);
+ pos[1] |= val >> rbits;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nanddev_bbt_set_block_status);
diff --git a/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c b/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c
deleted file mode 100644
index 54bac5b73f0a..000000000000
--- a/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c
+++ /dev/null
@@ -1,456 +0,0 @@
-/*
- * BCM47XX NAND flash driver
- *
- * Copyright (C) 2012 Rafał Miłecki <zajec5@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.
- *
- */
-
-#include "bcm47xxnflash.h"
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/bcma/bcma.h>
-
-/* Broadcom uses 1'000'000 but it seems to be too many. Tests on WNDR4500 has
- * shown ~1000 retries as maxiumum. */
-#define NFLASH_READY_RETRIES 10000
-
-#define NFLASH_SECTOR_SIZE 512
-
-#define NCTL_CMD0 0x00010000
-#define NCTL_COL 0x00020000 /* Update column with value from BCMA_CC_NFLASH_COL_ADDR */
-#define NCTL_ROW 0x00040000 /* Update row (page) with value from BCMA_CC_NFLASH_ROW_ADDR */
-#define NCTL_CMD1W 0x00080000
-#define NCTL_READ 0x00100000
-#define NCTL_WRITE 0x00200000
-#define NCTL_SPECADDR 0x01000000
-#define NCTL_READY 0x04000000
-#define NCTL_ERR 0x08000000
-#define NCTL_CSA 0x40000000
-#define NCTL_START 0x80000000
-
-/**************************************************
- * Various helpers
- **************************************************/
-
-static inline u8 bcm47xxnflash_ops_bcm4706_ns_to_cycle(u16 ns, u16 clock)
-{
- return ((ns * 1000 * clock) / 1000000) + 1;
-}
-
-static int bcm47xxnflash_ops_bcm4706_ctl_cmd(struct bcma_drv_cc *cc, u32 code)
-{
- int i = 0;
-
- bcma_cc_write32(cc, BCMA_CC_NFLASH_CTL, NCTL_START | code);
- for (i = 0; i < NFLASH_READY_RETRIES; i++) {
- if (!(bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) & NCTL_START)) {
- i = 0;
- break;
- }
- }
- if (i) {
- pr_err("NFLASH control command not ready!\n");
- return -EBUSY;
- }
- return 0;
-}
-
-static int bcm47xxnflash_ops_bcm4706_poll(struct bcma_drv_cc *cc)
-{
- int i;
-
- for (i = 0; i < NFLASH_READY_RETRIES; i++) {
- if (bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) & NCTL_READY) {
- if (bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) &
- BCMA_CC_NFLASH_CTL_ERR) {
- pr_err("Error on polling\n");
- return -EBUSY;
- } else {
- return 0;
- }
- }
- }
-
- pr_err("Polling timeout!\n");
- return -EBUSY;
-}
-
-/**************************************************
- * R/W
- **************************************************/
-
-static void bcm47xxnflash_ops_bcm4706_read(struct mtd_info *mtd, uint8_t *buf,
- int len)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
-
- u32 ctlcode;
- u32 *dest = (u32 *)buf;
- int i;
- int toread;
-
- BUG_ON(b47n->curr_page_addr & ~nand_chip->pagemask);
- /* Don't validate column using nand_chip->page_shift, it may be bigger
- * when accessing OOB */
-
- while (len) {
- /* We can read maximum of 0x200 bytes at once */
- toread = min(len, 0x200);
-
- /* Set page and column */
- bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_COL_ADDR,
- b47n->curr_column);
- bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_ROW_ADDR,
- b47n->curr_page_addr);
-
- /* Prepare to read */
- ctlcode = NCTL_CSA | NCTL_CMD1W | NCTL_ROW | NCTL_COL |
- NCTL_CMD0;
- ctlcode |= NAND_CMD_READSTART << 8;
- if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, ctlcode))
- return;
- if (bcm47xxnflash_ops_bcm4706_poll(b47n->cc))
- return;
-
- /* Eventually read some data :) */
- for (i = 0; i < toread; i += 4, dest++) {
- ctlcode = NCTL_CSA | 0x30000000 | NCTL_READ;
- if (i == toread - 4) /* Last read goes without that */
- ctlcode &= ~NCTL_CSA;
- if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc,
- ctlcode))
- return;
- *dest = bcma_cc_read32(b47n->cc, BCMA_CC_NFLASH_DATA);
- }
-
- b47n->curr_column += toread;
- len -= toread;
- }
-}
-
-static void bcm47xxnflash_ops_bcm4706_write(struct mtd_info *mtd,
- const uint8_t *buf, int len)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
- struct bcma_drv_cc *cc = b47n->cc;
-
- u32 ctlcode;
- const u32 *data = (u32 *)buf;
- int i;
-
- BUG_ON(b47n->curr_page_addr & ~nand_chip->pagemask);
- /* Don't validate column using nand_chip->page_shift, it may be bigger
- * when accessing OOB */
-
- for (i = 0; i < len; i += 4, data++) {
- bcma_cc_write32(cc, BCMA_CC_NFLASH_DATA, *data);
-
- ctlcode = NCTL_CSA | 0x30000000 | NCTL_WRITE;
- if (i == len - 4) /* Last read goes without that */
- ctlcode &= ~NCTL_CSA;
- if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode)) {
- pr_err("%s ctl_cmd didn't work!\n", __func__);
- return;
- }
- }
-
- b47n->curr_column += len;
-}
-
-/**************************************************
- * NAND chip ops
- **************************************************/
-
-static void bcm47xxnflash_ops_bcm4706_cmd_ctrl(struct mtd_info *mtd, int cmd,
- unsigned int ctrl)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
- u32 code = 0;
-
- if (cmd == NAND_CMD_NONE)
- return;
-
- if (cmd & NAND_CTRL_CLE)
- code = cmd | NCTL_CMD0;
-
- /* nCS is not needed for reset command */
- if (cmd != NAND_CMD_RESET)
- code |= NCTL_CSA;
-
- bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, code);
-}
-
-/* Default nand_select_chip calls cmd_ctrl, which is not used in BCM4706 */
-static void bcm47xxnflash_ops_bcm4706_select_chip(struct mtd_info *mtd,
- int chip)
-{
- return;
-}
-
-static int bcm47xxnflash_ops_bcm4706_dev_ready(struct mtd_info *mtd)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
-
- return !!(bcma_cc_read32(b47n->cc, BCMA_CC_NFLASH_CTL) & NCTL_READY);
-}
-
-/*
- * Default nand_command and nand_command_lp don't match BCM4706 hardware layout.
- * For example, reading chip id is performed in a non-standard way.
- * Setting column and page is also handled differently, we use a special
- * registers of ChipCommon core. Hacking cmd_ctrl to understand and convert
- * standard commands would be much more complicated.
- */
-static void bcm47xxnflash_ops_bcm4706_cmdfunc(struct mtd_info *mtd,
- unsigned command, int column,
- int page_addr)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
- struct bcma_drv_cc *cc = b47n->cc;
- u32 ctlcode;
- int i;
-
- if (column != -1)
- b47n->curr_column = column;
- if (page_addr != -1)
- b47n->curr_page_addr = page_addr;
-
- switch (command) {
- case NAND_CMD_RESET:
- nand_chip->cmd_ctrl(mtd, command, NAND_CTRL_CLE);
-
- ndelay(100);
- nand_wait_ready(mtd);
- break;
- case NAND_CMD_READID:
- ctlcode = NCTL_CSA | 0x01000000 | NCTL_CMD1W | NCTL_CMD0;
- ctlcode |= NAND_CMD_READID;
- if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, ctlcode)) {
- pr_err("READID error\n");
- break;
- }
-
- /*
- * Reading is specific, last one has to go without NCTL_CSA
- * bit. We don't know how many reads NAND subsystem is going
- * to perform, so cache everything.
- */
- for (i = 0; i < ARRAY_SIZE(b47n->id_data); i++) {
- ctlcode = NCTL_CSA | NCTL_READ;
- if (i == ARRAY_SIZE(b47n->id_data) - 1)
- ctlcode &= ~NCTL_CSA;
- if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc,
- ctlcode)) {
- pr_err("READID error\n");
- break;
- }
- b47n->id_data[i] =
- bcma_cc_read32(b47n->cc, BCMA_CC_NFLASH_DATA)
- & 0xFF;
- }
-
- break;
- case NAND_CMD_STATUS:
- ctlcode = NCTL_CSA | NCTL_CMD0 | NAND_CMD_STATUS;
- if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode))
- pr_err("STATUS command error\n");
- break;
- case NAND_CMD_READ0:
- break;
- case NAND_CMD_READOOB:
- if (page_addr != -1)
- b47n->curr_column += mtd->writesize;
- break;
- case NAND_CMD_ERASE1:
- bcma_cc_write32(cc, BCMA_CC_NFLASH_ROW_ADDR,
- b47n->curr_page_addr);
- ctlcode = NCTL_ROW | NCTL_CMD1W | NCTL_CMD0 |
- NAND_CMD_ERASE1 | (NAND_CMD_ERASE2 << 8);
- if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode))
- pr_err("ERASE1 failed\n");
- break;
- case NAND_CMD_ERASE2:
- break;
- case NAND_CMD_SEQIN:
- /* Set page and column */
- bcma_cc_write32(cc, BCMA_CC_NFLASH_COL_ADDR,
- b47n->curr_column);
- bcma_cc_write32(cc, BCMA_CC_NFLASH_ROW_ADDR,
- b47n->curr_page_addr);
-
- /* Prepare to write */
- ctlcode = 0x40000000 | NCTL_ROW | NCTL_COL | NCTL_CMD0;
- ctlcode |= NAND_CMD_SEQIN;
- if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode))
- pr_err("SEQIN failed\n");
- break;
- case NAND_CMD_PAGEPROG:
- if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, NCTL_CMD0 |
- NAND_CMD_PAGEPROG))
- pr_err("PAGEPROG failed\n");
- if (bcm47xxnflash_ops_bcm4706_poll(cc))
- pr_err("PAGEPROG not ready\n");
- break;
- default:
- pr_err("Command 0x%X unsupported\n", command);
- break;
- }
- b47n->curr_command = command;
-}
-
-static u8 bcm47xxnflash_ops_bcm4706_read_byte(struct mtd_info *mtd)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
- struct bcma_drv_cc *cc = b47n->cc;
- u32 tmp = 0;
-
- switch (b47n->curr_command) {
- case NAND_CMD_READID:
- if (b47n->curr_column >= ARRAY_SIZE(b47n->id_data)) {
- pr_err("Requested invalid id_data: %d\n",
- b47n->curr_column);
- return 0;
- }
- return b47n->id_data[b47n->curr_column++];
- case NAND_CMD_STATUS:
- if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, NCTL_READ))
- return 0;
- return bcma_cc_read32(cc, BCMA_CC_NFLASH_DATA) & 0xff;
- case NAND_CMD_READOOB:
- bcm47xxnflash_ops_bcm4706_read(mtd, (u8 *)&tmp, 4);
- return tmp & 0xFF;
- }
-
- pr_err("Invalid command for byte read: 0x%X\n", b47n->curr_command);
- return 0;
-}
-
-static void bcm47xxnflash_ops_bcm4706_read_buf(struct mtd_info *mtd,
- uint8_t *buf, int len)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
-
- switch (b47n->curr_command) {
- case NAND_CMD_READ0:
- case NAND_CMD_READOOB:
- bcm47xxnflash_ops_bcm4706_read(mtd, buf, len);
- return;
- }
-
- pr_err("Invalid command for buf read: 0x%X\n", b47n->curr_command);
-}
-
-static void bcm47xxnflash_ops_bcm4706_write_buf(struct mtd_info *mtd,
- const uint8_t *buf, int len)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
-
- switch (b47n->curr_command) {
- case NAND_CMD_SEQIN:
- bcm47xxnflash_ops_bcm4706_write(mtd, buf, len);
- return;
- }
-
- pr_err("Invalid command for buf write: 0x%X\n", b47n->curr_command);
-}
-
-/**************************************************
- * Init
- **************************************************/
-
-int bcm47xxnflash_ops_bcm4706_init(struct bcm47xxnflash *b47n)
-{
- struct nand_chip *nand_chip = (struct nand_chip *)&b47n->nand_chip;
- int err;
- u32 freq;
- u16 clock;
- u8 w0, w1, w2, w3, w4;
-
- unsigned long chipsize; /* MiB */
- u8 tbits, col_bits, col_size, row_bits, row_bsize;
- u32 val;
-
- b47n->nand_chip.select_chip = bcm47xxnflash_ops_bcm4706_select_chip;
- nand_chip->cmd_ctrl = bcm47xxnflash_ops_bcm4706_cmd_ctrl;
- nand_chip->dev_ready = bcm47xxnflash_ops_bcm4706_dev_ready;
- b47n->nand_chip.cmdfunc = bcm47xxnflash_ops_bcm4706_cmdfunc;
- b47n->nand_chip.read_byte = bcm47xxnflash_ops_bcm4706_read_byte;
- b47n->nand_chip.read_buf = bcm47xxnflash_ops_bcm4706_read_buf;
- b47n->nand_chip.write_buf = bcm47xxnflash_ops_bcm4706_write_buf;
- b47n->nand_chip.onfi_set_features = nand_onfi_get_set_features_notsupp;
- b47n->nand_chip.onfi_get_features = nand_onfi_get_set_features_notsupp;
-
- nand_chip->chip_delay = 50;
- b47n->nand_chip.bbt_options = NAND_BBT_USE_FLASH;
- b47n->nand_chip.ecc.mode = NAND_ECC_NONE; /* TODO: implement ECC */
-
- /* Enable NAND flash access */
- bcma_cc_set32(b47n->cc, BCMA_CC_4706_FLASHSCFG,
- BCMA_CC_4706_FLASHSCFG_NF1);
-
- /* Configure wait counters */
- if (b47n->cc->status & BCMA_CC_CHIPST_4706_PKG_OPTION) {
- /* 400 MHz */
- freq = 400000000 / 4;
- } else {
- freq = bcma_chipco_pll_read(b47n->cc, 4);
- freq = (freq & 0xFFF) >> 3;
- /* Fixed reference clock 25 MHz and m = 2 */
- freq = (freq * 25000000 / 2) / 4;
- }
- clock = freq / 1000000;
- w0 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(15, clock);
- w1 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(20, clock);
- w2 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(10, clock);
- w3 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(10, clock);
- w4 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(100, clock);
- bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_WAITCNT0,
- (w4 << 24 | w3 << 18 | w2 << 12 | w1 << 6 | w0));
-
- /* Scan NAND */
- err = nand_scan(nand_to_mtd(&b47n->nand_chip), 1);
- if (err) {
- pr_err("Could not scan NAND flash: %d\n", err);
- goto exit;
- }
-
- /* Configure FLASH */
- chipsize = b47n->nand_chip.chipsize >> 20;
- tbits = ffs(chipsize); /* find first bit set */
- if (!tbits || tbits != fls(chipsize)) {
- pr_err("Invalid flash size: 0x%lX\n", chipsize);
- err = -ENOTSUPP;
- goto exit;
- }
- tbits += 19; /* Broadcom increases *index* by 20, we increase *pos* */
-
- col_bits = b47n->nand_chip.page_shift + 1;
- col_size = (col_bits + 7) / 8;
-
- row_bits = tbits - col_bits + 1;
- row_bsize = (row_bits + 7) / 8;
-
- val = ((row_bsize - 1) << 6) | ((col_size - 1) << 4) | 2;
- bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_CONF, val);
-
-exit:
- if (err)
- bcma_cc_mask32(b47n->cc, BCMA_CC_4706_FLASHSCFG,
- ~BCMA_CC_4706_FLASHSCFG_NF1);
- return err;
-}
diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c
deleted file mode 100644
index 87bbd177b3e5..000000000000
--- a/drivers/mtd/nand/bf5xx_nand.c
+++ /dev/null
@@ -1,862 +0,0 @@
-/* linux/drivers/mtd/nand/bf5xx_nand.c
- *
- * Copyright 2006-2008 Analog Devices Inc.
- * http://blackfin.uclinux.org/
- * Bryan Wu <bryan.wu@analog.com>
- *
- * Blackfin BF5xx on-chip NAND flash controller driver
- *
- * Derived from drivers/mtd/nand/s3c2410.c
- * Copyright (c) 2007 Ben Dooks <ben@simtec.co.uk>
- *
- * Derived from drivers/mtd/nand/cafe.c
- * Copyright © 2006 Red Hat, Inc.
- * Copyright © 2006 David Woodhouse <dwmw2@infradead.org>
- *
- * Changelog:
- * 12-Jun-2007 Bryan Wu: Initial version
- * 18-Jul-2007 Bryan Wu:
- * - ECC_HW and ECC_SW supported
- * - DMA supported in ECC_HW
- * - YAFFS tested as rootfs in both ECC_HW and ECC_SW
- *
- * 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/types.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/ioport.h>
-#include <linux/platform_device.h>
-#include <linux/delay.h>
-#include <linux/dma-mapping.h>
-#include <linux/err.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-#include <linux/bitops.h>
-
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand_ecc.h>
-#include <linux/mtd/partitions.h>
-
-#include <asm/blackfin.h>
-#include <asm/dma.h>
-#include <asm/cacheflush.h>
-#include <asm/nand.h>
-#include <asm/portmux.h>
-
-#define DRV_NAME "bf5xx-nand"
-#define DRV_VERSION "1.2"
-#define DRV_AUTHOR "Bryan Wu <bryan.wu@analog.com>"
-#define DRV_DESC "BF5xx on-chip NAND FLash Controller Driver"
-
-/* NFC_STAT Masks */
-#define NBUSY 0x01 /* Not Busy */
-#define WB_FULL 0x02 /* Write Buffer Full */
-#define PG_WR_STAT 0x04 /* Page Write Pending */
-#define PG_RD_STAT 0x08 /* Page Read Pending */
-#define WB_EMPTY 0x10 /* Write Buffer Empty */
-
-/* NFC_IRQSTAT Masks */
-#define NBUSYIRQ 0x01 /* Not Busy IRQ */
-#define WB_OVF 0x02 /* Write Buffer Overflow */
-#define WB_EDGE 0x04 /* Write Buffer Edge Detect */
-#define RD_RDY 0x08 /* Read Data Ready */
-#define WR_DONE 0x10 /* Page Write Done */
-
-/* NFC_RST Masks */
-#define ECC_RST 0x01 /* ECC (and NFC counters) Reset */
-
-/* NFC_PGCTL Masks */
-#define PG_RD_START 0x01 /* Page Read Start */
-#define PG_WR_START 0x02 /* Page Write Start */
-
-#ifdef CONFIG_MTD_NAND_BF5XX_HWECC
-static int hardware_ecc = 1;
-#else
-static int hardware_ecc;
-#endif
-
-static const unsigned short bfin_nfc_pin_req[] =
- {P_NAND_CE,
- P_NAND_RB,
- P_NAND_D0,
- P_NAND_D1,
- P_NAND_D2,
- P_NAND_D3,
- P_NAND_D4,
- P_NAND_D5,
- P_NAND_D6,
- P_NAND_D7,
- P_NAND_WE,
- P_NAND_RE,
- P_NAND_CLE,
- P_NAND_ALE,
- 0};
-
-#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
-static int bootrom_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- if (section > 7)
- return -ERANGE;
-
- oobregion->offset = section * 8;
- oobregion->length = 3;
-
- return 0;
-}
-
-static int bootrom_ooblayout_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- if (section > 7)
- return -ERANGE;
-
- oobregion->offset = (section * 8) + 3;
- oobregion->length = 5;
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops bootrom_ooblayout_ops = {
- .ecc = bootrom_ooblayout_ecc,
- .free = bootrom_ooblayout_free,
-};
-#endif
-
-/*
- * Data structures for bf5xx nand flash controller driver
- */
-
-/* bf5xx nand info */
-struct bf5xx_nand_info {
- /* mtd info */
- struct nand_hw_control controller;
- struct nand_chip chip;
-
- /* platform info */
- struct bf5xx_nand_platform *platform;
-
- /* device info */
- struct device *device;
-
- /* DMA stuff */
- struct completion dma_completion;
-};
-
-/*
- * Conversion functions
- */
-static struct bf5xx_nand_info *mtd_to_nand_info(struct mtd_info *mtd)
-{
- return container_of(mtd_to_nand(mtd), struct bf5xx_nand_info,
- chip);
-}
-
-static struct bf5xx_nand_info *to_nand_info(struct platform_device *pdev)
-{
- return platform_get_drvdata(pdev);
-}
-
-static struct bf5xx_nand_platform *to_nand_plat(struct platform_device *pdev)
-{
- return dev_get_platdata(&pdev->dev);
-}
-
-/*
- * struct nand_chip interface function pointers
- */
-
-/*
- * bf5xx_nand_hwcontrol
- *
- * Issue command and address cycles to the chip
- */
-static void bf5xx_nand_hwcontrol(struct mtd_info *mtd, int cmd,
- unsigned int ctrl)
-{
- if (cmd == NAND_CMD_NONE)
- return;
-
- while (bfin_read_NFC_STAT() & WB_FULL)
- cpu_relax();
-
- if (ctrl & NAND_CLE)
- bfin_write_NFC_CMD(cmd);
- else if (ctrl & NAND_ALE)
- bfin_write_NFC_ADDR(cmd);
- SSYNC();
-}
-
-/*
- * bf5xx_nand_devready()
- *
- * returns 0 if the nand is busy, 1 if it is ready
- */
-static int bf5xx_nand_devready(struct mtd_info *mtd)
-{
- unsigned short val = bfin_read_NFC_STAT();
-
- if ((val & NBUSY) == NBUSY)
- return 1;
- else
- return 0;
-}
-
-/*
- * ECC functions
- * These allow the bf5xx to use the controller's ECC
- * generator block to ECC the data as it passes through
- */
-
-/*
- * ECC error correction function
- */
-static int bf5xx_nand_correct_data_256(struct mtd_info *mtd, u_char *dat,
- u_char *read_ecc, u_char *calc_ecc)
-{
- struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
- u32 syndrome[5];
- u32 calced, stored;
- int i;
- unsigned short failing_bit, failing_byte;
- u_char data;
-
- calced = calc_ecc[0] | (calc_ecc[1] << 8) | (calc_ecc[2] << 16);
- stored = read_ecc[0] | (read_ecc[1] << 8) | (read_ecc[2] << 16);
-
- syndrome[0] = (calced ^ stored);
-
- /*
- * syndrome 0: all zero
- * No error in data
- * No action
- */
- if (!syndrome[0] || !calced || !stored)
- return 0;
-
- /*
- * sysdrome 0: only one bit is one
- * ECC data was incorrect
- * No action
- */
- if (hweight32(syndrome[0]) == 1) {
- dev_err(info->device, "ECC data was incorrect!\n");
- return -EBADMSG;
- }
-
- syndrome[1] = (calced & 0x7FF) ^ (stored & 0x7FF);
- syndrome[2] = (calced & 0x7FF) ^ ((calced >> 11) & 0x7FF);
- syndrome[3] = (stored & 0x7FF) ^ ((stored >> 11) & 0x7FF);
- syndrome[4] = syndrome[2] ^ syndrome[3];
-
- for (i = 0; i < 5; i++)
- dev_info(info->device, "syndrome[%d] 0x%08x\n", i, syndrome[i]);
-
- dev_info(info->device,
- "calced[0x%08x], stored[0x%08x]\n",
- calced, stored);
-
- /*
- * sysdrome 0: exactly 11 bits are one, each parity
- * and parity' pair is 1 & 0 or 0 & 1.
- * 1-bit correctable error
- * Correct the error
- */
- if (hweight32(syndrome[0]) == 11 && syndrome[4] == 0x7FF) {
- dev_info(info->device,
- "1-bit correctable error, correct it.\n");
- dev_info(info->device,
- "syndrome[1] 0x%08x\n", syndrome[1]);
-
- failing_bit = syndrome[1] & 0x7;
- failing_byte = syndrome[1] >> 0x3;
- data = *(dat + failing_byte);
- data = data ^ (0x1 << failing_bit);
- *(dat + failing_byte) = data;
-
- return 1;
- }
-
- /*
- * sysdrome 0: random data
- * More than 1-bit error, non-correctable error
- * Discard data, mark bad block
- */
- dev_err(info->device,
- "More than 1-bit error, non-correctable error.\n");
- dev_err(info->device,
- "Please discard data, mark bad block\n");
-
- return -EBADMSG;
-}
-
-static int bf5xx_nand_correct_data(struct mtd_info *mtd, u_char *dat,
- u_char *read_ecc, u_char *calc_ecc)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- int ret, bitflips = 0;
-
- ret = bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc);
- if (ret < 0)
- return ret;
-
- bitflips = ret;
-
- /* If ecc size is 512, correct second 256 bytes */
- if (chip->ecc.size == 512) {
- dat += 256;
- read_ecc += 3;
- calc_ecc += 3;
- ret = bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc);
- if (ret < 0)
- return ret;
-
- bitflips += ret;
- }
-
- return bitflips;
-}
-
-static void bf5xx_nand_enable_hwecc(struct mtd_info *mtd, int mode)
-{
- return;
-}
-
-static int bf5xx_nand_calculate_ecc(struct mtd_info *mtd,
- const u_char *dat, u_char *ecc_code)
-{
- struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
- struct nand_chip *chip = mtd_to_nand(mtd);
- u16 ecc0, ecc1;
- u32 code[2];
- u8 *p;
-
- /* first 3 bytes ECC code for 256 page size */
- ecc0 = bfin_read_NFC_ECC0();
- ecc1 = bfin_read_NFC_ECC1();
-
- code[0] = (ecc0 & 0x7ff) | ((ecc1 & 0x7ff) << 11);
-
- dev_dbg(info->device, "returning ecc 0x%08x\n", code[0]);
-
- p = (u8 *) code;
- memcpy(ecc_code, p, 3);
-
- /* second 3 bytes ECC code for 512 ecc size */
- if (chip->ecc.size == 512) {
- ecc0 = bfin_read_NFC_ECC2();
- ecc1 = bfin_read_NFC_ECC3();
- code[1] = (ecc0 & 0x7ff) | ((ecc1 & 0x7ff) << 11);
-
- /* second 3 bytes in ecc_code for second 256
- * bytes of 512 page size
- */
- p = (u8 *) (code + 1);
- memcpy((ecc_code + 3), p, 3);
- dev_dbg(info->device, "returning ecc 0x%08x\n", code[1]);
- }
-
- return 0;
-}
-
-/*
- * PIO mode for buffer writing and reading
- */
-static void bf5xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
-{
- int i;
- unsigned short val;
-
- /*
- * Data reads are requested by first writing to NFC_DATA_RD
- * and then reading back from NFC_READ.
- */
- for (i = 0; i < len; i++) {
- while (bfin_read_NFC_STAT() & WB_FULL)
- cpu_relax();
-
- /* Contents do not matter */
- bfin_write_NFC_DATA_RD(0x0000);
- SSYNC();
-
- while ((bfin_read_NFC_IRQSTAT() & RD_RDY) != RD_RDY)
- cpu_relax();
-
- buf[i] = bfin_read_NFC_READ();
-
- val = bfin_read_NFC_IRQSTAT();
- val |= RD_RDY;
- bfin_write_NFC_IRQSTAT(val);
- SSYNC();
- }
-}
-
-static uint8_t bf5xx_nand_read_byte(struct mtd_info *mtd)
-{
- uint8_t val;
-
- bf5xx_nand_read_buf(mtd, &val, 1);
-
- return val;
-}
-
-static void bf5xx_nand_write_buf(struct mtd_info *mtd,
- const uint8_t *buf, int len)
-{
- int i;
-
- for (i = 0; i < len; i++) {
- while (bfin_read_NFC_STAT() & WB_FULL)
- cpu_relax();
-
- bfin_write_NFC_DATA_WR(buf[i]);
- SSYNC();
- }
-}
-
-static void bf5xx_nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
-{
- int i;
- u16 *p = (u16 *) buf;
- len >>= 1;
-
- /*
- * Data reads are requested by first writing to NFC_DATA_RD
- * and then reading back from NFC_READ.
- */
- bfin_write_NFC_DATA_RD(0x5555);
-
- SSYNC();
-
- for (i = 0; i < len; i++)
- p[i] = bfin_read_NFC_READ();
-}
-
-static void bf5xx_nand_write_buf16(struct mtd_info *mtd,
- const uint8_t *buf, int len)
-{
- int i;
- u16 *p = (u16 *) buf;
- len >>= 1;
-
- for (i = 0; i < len; i++)
- bfin_write_NFC_DATA_WR(p[i]);
-
- SSYNC();
-}
-
-/*
- * DMA functions for buffer writing and reading
- */
-static irqreturn_t bf5xx_nand_dma_irq(int irq, void *dev_id)
-{
- struct bf5xx_nand_info *info = dev_id;
-
- clear_dma_irqstat(CH_NFC);
- disable_dma(CH_NFC);
- complete(&info->dma_completion);
-
- return IRQ_HANDLED;
-}
-
-static void bf5xx_nand_dma_rw(struct mtd_info *mtd,
- uint8_t *buf, int is_read)
-{
- struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
- struct nand_chip *chip = mtd_to_nand(mtd);
- unsigned short val;
-
- dev_dbg(info->device, " mtd->%p, buf->%p, is_read %d\n",
- mtd, buf, is_read);
-
- /*
- * Before starting a dma transfer, be sure to invalidate/flush
- * the cache over the address range of your DMA buffer to
- * prevent cache coherency problems. Otherwise very subtle bugs
- * can be introduced to your driver.
- */
- if (is_read)
- invalidate_dcache_range((unsigned int)buf,
- (unsigned int)(buf + chip->ecc.size));
- else
- flush_dcache_range((unsigned int)buf,
- (unsigned int)(buf + chip->ecc.size));
-
- /*
- * This register must be written before each page is
- * transferred to generate the correct ECC register
- * values.
- */
- bfin_write_NFC_RST(ECC_RST);
- SSYNC();
- while (bfin_read_NFC_RST() & ECC_RST)
- cpu_relax();
-
- disable_dma(CH_NFC);
- clear_dma_irqstat(CH_NFC);
-
- /* setup DMA register with Blackfin DMA API */
- set_dma_config(CH_NFC, 0x0);
- set_dma_start_addr(CH_NFC, (unsigned long) buf);
-
- /* The DMAs have different size on BF52x and BF54x */
-#ifdef CONFIG_BF52x
- set_dma_x_count(CH_NFC, (chip->ecc.size >> 1));
- set_dma_x_modify(CH_NFC, 2);
- val = DI_EN | WDSIZE_16;
-#endif
-
-#ifdef CONFIG_BF54x
- set_dma_x_count(CH_NFC, (chip->ecc.size >> 2));
- set_dma_x_modify(CH_NFC, 4);
- val = DI_EN | WDSIZE_32;
-#endif
- /* setup write or read operation */
- if (is_read)
- val |= WNR;
- set_dma_config(CH_NFC, val);
- enable_dma(CH_NFC);
-
- /* Start PAGE read/write operation */
- if (is_read)
- bfin_write_NFC_PGCTL(PG_RD_START);
- else
- bfin_write_NFC_PGCTL(PG_WR_START);
- wait_for_completion(&info->dma_completion);
-}
-
-static void bf5xx_nand_dma_read_buf(struct mtd_info *mtd,
- uint8_t *buf, int len)
-{
- struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- dev_dbg(info->device, "mtd->%p, buf->%p, int %d\n", mtd, buf, len);
-
- if (len == chip->ecc.size)
- bf5xx_nand_dma_rw(mtd, buf, 1);
- else
- bf5xx_nand_read_buf(mtd, buf, len);
-}
-
-static void bf5xx_nand_dma_write_buf(struct mtd_info *mtd,
- const uint8_t *buf, int len)
-{
- struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- dev_dbg(info->device, "mtd->%p, buf->%p, len %d\n", mtd, buf, len);
-
- if (len == chip->ecc.size)
- bf5xx_nand_dma_rw(mtd, (uint8_t *)buf, 0);
- else
- bf5xx_nand_write_buf(mtd, buf, len);
-}
-
-static int bf5xx_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page)
-{
- nand_read_page_op(chip, page, 0, NULL, 0);
-
- bf5xx_nand_read_buf(mtd, buf, mtd->writesize);
- bf5xx_nand_read_buf(mtd, chip->oob_poi, mtd->oobsize);
-
- return 0;
-}
-
-static int bf5xx_nand_write_page_raw(struct mtd_info *mtd,
- struct nand_chip *chip, const uint8_t *buf, int oob_required,
- int page)
-{
- nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);
- bf5xx_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize);
-
- return nand_prog_page_end_op(chip);
-}
-
-/*
- * System initialization functions
- */
-static int bf5xx_nand_dma_init(struct bf5xx_nand_info *info)
-{
- int ret;
-
- /* Do not use dma */
- if (!hardware_ecc)
- return 0;
-
- init_completion(&info->dma_completion);
-
- /* Request NFC DMA channel */
- ret = request_dma(CH_NFC, "BF5XX NFC driver");
- if (ret < 0) {
- dev_err(info->device, " unable to get DMA channel\n");
- return ret;
- }
-
-#ifdef CONFIG_BF54x
- /* Setup DMAC1 channel mux for NFC which shared with SDH */
- bfin_write_DMAC1_PERIMUX(bfin_read_DMAC1_PERIMUX() & ~1);
- SSYNC();
-#endif
-
- set_dma_callback(CH_NFC, bf5xx_nand_dma_irq, info);
-
- /* Turn off the DMA channel first */
- disable_dma(CH_NFC);
- return 0;
-}
-
-static void bf5xx_nand_dma_remove(struct bf5xx_nand_info *info)
-{
- /* Free NFC DMA channel */
- if (hardware_ecc)
- free_dma(CH_NFC);
-}
-
-/*
- * BF5XX NFC hardware initialization
- * - pin mux setup
- * - clear interrupt status
- */
-static int bf5xx_nand_hw_init(struct bf5xx_nand_info *info)
-{
- int err = 0;
- unsigned short val;
- struct bf5xx_nand_platform *plat = info->platform;
-
- /* setup NFC_CTL register */
- dev_info(info->device,
- "data_width=%d, wr_dly=%d, rd_dly=%d\n",
- (plat->data_width ? 16 : 8),
- plat->wr_dly, plat->rd_dly);
-
- val = (1 << NFC_PG_SIZE_OFFSET) |
- (plat->data_width << NFC_NWIDTH_OFFSET) |
- (plat->rd_dly << NFC_RDDLY_OFFSET) |
- (plat->wr_dly << NFC_WRDLY_OFFSET);
- dev_dbg(info->device, "NFC_CTL is 0x%04x\n", val);
-
- bfin_write_NFC_CTL(val);
- SSYNC();
-
- /* clear interrupt status */
- bfin_write_NFC_IRQMASK(0x0);
- SSYNC();
- val = bfin_read_NFC_IRQSTAT();
- bfin_write_NFC_IRQSTAT(val);
- SSYNC();
-
- /* DMA initialization */
- if (bf5xx_nand_dma_init(info))
- err = -ENXIO;
-
- return err;
-}
-
-/*
- * Device management interface
- */
-static int bf5xx_nand_add_partition(struct bf5xx_nand_info *info)
-{
- struct mtd_info *mtd = nand_to_mtd(&info->chip);
- struct mtd_partition *parts = info->platform->partitions;
- int nr = info->platform->nr_partitions;
-
- return mtd_device_register(mtd, parts, nr);
-}
-
-static int bf5xx_nand_remove(struct platform_device *pdev)
-{
- struct bf5xx_nand_info *info = to_nand_info(pdev);
-
- /* first thing we need to do is release all our mtds
- * and their partitions, then go through freeing the
- * resources used
- */
- nand_release(nand_to_mtd(&info->chip));
-
- peripheral_free_list(bfin_nfc_pin_req);
- bf5xx_nand_dma_remove(info);
-
- return 0;
-}
-
-static int bf5xx_nand_scan(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- int ret;
-
- ret = nand_scan_ident(mtd, 1, NULL);
- if (ret)
- return ret;
-
- if (hardware_ecc) {
- /*
- * for nand with page size > 512B, think it as several sections with 512B
- */
- if (likely(mtd->writesize >= 512)) {
- chip->ecc.size = 512;
- chip->ecc.bytes = 6;
- chip->ecc.strength = 2;
- } else {
- chip->ecc.size = 256;
- chip->ecc.bytes = 3;
- chip->ecc.strength = 1;
- bfin_write_NFC_CTL(bfin_read_NFC_CTL() & ~(1 << NFC_PG_SIZE_OFFSET));
- SSYNC();
- }
- }
-
- return nand_scan_tail(mtd);
-}
-
-/*
- * bf5xx_nand_probe
- *
- * called by device layer when it finds a device matching
- * one our driver can handled. This code checks to see if
- * it can allocate all necessary resources then calls the
- * nand layer to look for devices
- */
-static int bf5xx_nand_probe(struct platform_device *pdev)
-{
- struct bf5xx_nand_platform *plat = to_nand_plat(pdev);
- struct bf5xx_nand_info *info = NULL;
- struct nand_chip *chip = NULL;
- struct mtd_info *mtd = NULL;
- int err = 0;
-
- dev_dbg(&pdev->dev, "(%p)\n", pdev);
-
- if (!plat) {
- dev_err(&pdev->dev, "no platform specific information\n");
- return -EINVAL;
- }
-
- if (peripheral_request_list(bfin_nfc_pin_req, DRV_NAME)) {
- dev_err(&pdev->dev, "requesting Peripherals failed\n");
- return -EFAULT;
- }
-
- info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
- if (info == NULL) {
- err = -ENOMEM;
- goto out_err;
- }
-
- platform_set_drvdata(pdev, info);
-
- nand_hw_control_init(&info->controller);
-
- info->device = &pdev->dev;
- info->platform = plat;
-
- /* initialise chip data struct */
- chip = &info->chip;
- mtd = nand_to_mtd(&info->chip);
-
- if (plat->data_width)
- chip->options |= NAND_BUSWIDTH_16;
-
- chip->options |= NAND_CACHEPRG | NAND_SKIP_BBTSCAN;
-
- chip->read_buf = (plat->data_width) ?
- bf5xx_nand_read_buf16 : bf5xx_nand_read_buf;
- chip->write_buf = (plat->data_width) ?
- bf5xx_nand_write_buf16 : bf5xx_nand_write_buf;
-
- chip->read_byte = bf5xx_nand_read_byte;
-
- chip->cmd_ctrl = bf5xx_nand_hwcontrol;
- chip->dev_ready = bf5xx_nand_devready;
-
- nand_set_controller_data(chip, mtd);
- chip->controller = &info->controller;
-
- chip->IO_ADDR_R = (void __iomem *) NFC_READ;
- chip->IO_ADDR_W = (void __iomem *) NFC_DATA_WR;
-
- chip->chip_delay = 0;
-
- /* initialise mtd info data struct */
- mtd->dev.parent = &pdev->dev;
-
- /* initialise the hardware */
- err = bf5xx_nand_hw_init(info);
- if (err)
- goto out_err;
-
- /* setup hardware ECC data struct */
- if (hardware_ecc) {
-#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
- mtd_set_ooblayout(mtd, &bootrom_ooblayout_ops);
-#endif
- chip->read_buf = bf5xx_nand_dma_read_buf;
- chip->write_buf = bf5xx_nand_dma_write_buf;
- chip->ecc.calculate = bf5xx_nand_calculate_ecc;
- chip->ecc.correct = bf5xx_nand_correct_data;
- chip->ecc.mode = NAND_ECC_HW;
- chip->ecc.hwctl = bf5xx_nand_enable_hwecc;
- chip->ecc.read_page_raw = bf5xx_nand_read_page_raw;
- chip->ecc.write_page_raw = bf5xx_nand_write_page_raw;
- } else {
- chip->ecc.mode = NAND_ECC_SOFT;
- chip->ecc.algo = NAND_ECC_HAMMING;
- }
-
- /* scan hardware nand chip and setup mtd info data struct */
- if (bf5xx_nand_scan(mtd)) {
- err = -ENXIO;
- goto out_err_nand_scan;
- }
-
-#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
- chip->badblockpos = 63;
-#endif
-
- /* add NAND partition */
- bf5xx_nand_add_partition(info);
-
- dev_dbg(&pdev->dev, "initialised ok\n");
- return 0;
-
-out_err_nand_scan:
- bf5xx_nand_dma_remove(info);
-out_err:
- peripheral_free_list(bfin_nfc_pin_req);
-
- return err;
-}
-
-/* driver device registration */
-static struct platform_driver bf5xx_nand_driver = {
- .probe = bf5xx_nand_probe,
- .remove = bf5xx_nand_remove,
- .driver = {
- .name = DRV_NAME,
- },
-};
-
-module_platform_driver(bf5xx_nand_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR(DRV_AUTHOR);
-MODULE_DESCRIPTION(DRV_DESC);
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c
deleted file mode 100644
index c28fd2bc1a84..000000000000
--- a/drivers/mtd/nand/brcmnand/brcmnand.c
+++ /dev/null
@@ -1,2620 +0,0 @@
-/*
- * Copyright © 2010-2015 Broadcom Corporation
- *
- * 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.
- *
- * 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.
- */
-
-#include <linux/clk.h>
-#include <linux/version.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/err.h>
-#include <linux/completion.h>
-#include <linux/interrupt.h>
-#include <linux/spinlock.h>
-#include <linux/dma-mapping.h>
-#include <linux/ioport.h>
-#include <linux/bug.h>
-#include <linux/kernel.h>
-#include <linux/bitops.h>
-#include <linux/mm.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/partitions.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/slab.h>
-#include <linux/list.h>
-#include <linux/log2.h>
-
-#include "brcmnand.h"
-
-/*
- * This flag controls if WP stays on between erase/write commands to mitigate
- * flash corruption due to power glitches. Values:
- * 0: NAND_WP is not used or not available
- * 1: NAND_WP is set by default, cleared for erase/write operations
- * 2: NAND_WP is always cleared
- */
-static int wp_on = 1;
-module_param(wp_on, int, 0444);
-
-/***********************************************************************
- * Definitions
- ***********************************************************************/
-
-#define DRV_NAME "brcmnand"
-
-#define CMD_NULL 0x00
-#define CMD_PAGE_READ 0x01
-#define CMD_SPARE_AREA_READ 0x02
-#define CMD_STATUS_READ 0x03
-#define CMD_PROGRAM_PAGE 0x04
-#define CMD_PROGRAM_SPARE_AREA 0x05
-#define CMD_COPY_BACK 0x06
-#define CMD_DEVICE_ID_READ 0x07
-#define CMD_BLOCK_ERASE 0x08
-#define CMD_FLASH_RESET 0x09
-#define CMD_BLOCKS_LOCK 0x0a
-#define CMD_BLOCKS_LOCK_DOWN 0x0b
-#define CMD_BLOCKS_UNLOCK 0x0c
-#define CMD_READ_BLOCKS_LOCK_STATUS 0x0d
-#define CMD_PARAMETER_READ 0x0e
-#define CMD_PARAMETER_CHANGE_COL 0x0f
-#define CMD_LOW_LEVEL_OP 0x10
-
-struct brcm_nand_dma_desc {
- u32 next_desc;
- u32 next_desc_ext;
- u32 cmd_irq;
- u32 dram_addr;
- u32 dram_addr_ext;
- u32 tfr_len;
- u32 total_len;
- u32 flash_addr;
- u32 flash_addr_ext;
- u32 cs;
- u32 pad2[5];
- u32 status_valid;
-} __packed;
-
-/* Bitfields for brcm_nand_dma_desc::status_valid */
-#define FLASH_DMA_ECC_ERROR (1 << 8)
-#define FLASH_DMA_CORR_ERROR (1 << 9)
-
-/* 512B flash cache in the NAND controller HW */
-#define FC_SHIFT 9U
-#define FC_BYTES 512U
-#define FC_WORDS (FC_BYTES >> 2)
-
-#define BRCMNAND_MIN_PAGESIZE 512
-#define BRCMNAND_MIN_BLOCKSIZE (8 * 1024)
-#define BRCMNAND_MIN_DEVSIZE (4ULL * 1024 * 1024)
-
-#define NAND_CTRL_RDY (INTFC_CTLR_READY | INTFC_FLASH_READY)
-#define NAND_POLL_STATUS_TIMEOUT_MS 100
-
-/* Controller feature flags */
-enum {
- BRCMNAND_HAS_1K_SECTORS = BIT(0),
- BRCMNAND_HAS_PREFETCH = BIT(1),
- BRCMNAND_HAS_CACHE_MODE = BIT(2),
- BRCMNAND_HAS_WP = BIT(3),
-};
-
-struct brcmnand_controller {
- struct device *dev;
- struct nand_hw_control controller;
- void __iomem *nand_base;
- void __iomem *nand_fc; /* flash cache */
- void __iomem *flash_dma_base;
- unsigned int irq;
- unsigned int dma_irq;
- int nand_version;
-
- /* Some SoCs provide custom interrupt status register(s) */
- struct brcmnand_soc *soc;
-
- /* Some SoCs have a gateable clock for the controller */
- struct clk *clk;
-
- int cmd_pending;
- bool dma_pending;
- struct completion done;
- struct completion dma_done;
-
- /* List of NAND hosts (one for each chip-select) */
- struct list_head host_list;
-
- struct brcm_nand_dma_desc *dma_desc;
- dma_addr_t dma_pa;
-
- /* in-memory cache of the FLASH_CACHE, used only for some commands */
- u8 flash_cache[FC_BYTES];
-
- /* Controller revision details */
- const u16 *reg_offsets;
- unsigned int reg_spacing; /* between CS1, CS2, ... regs */
- const u8 *cs_offsets; /* within each chip-select */
- const u8 *cs0_offsets; /* within CS0, if different */
- unsigned int max_block_size;
- const unsigned int *block_sizes;
- unsigned int max_page_size;
- const unsigned int *page_sizes;
- unsigned int max_oob;
- u32 features;
-
- /* for low-power standby/resume only */
- u32 nand_cs_nand_select;
- u32 nand_cs_nand_xor;
- u32 corr_stat_threshold;
- u32 flash_dma_mode;
-};
-
-struct brcmnand_cfg {
- u64 device_size;
- unsigned int block_size;
- unsigned int page_size;
- unsigned int spare_area_size;
- unsigned int device_width;
- unsigned int col_adr_bytes;
- unsigned int blk_adr_bytes;
- unsigned int ful_adr_bytes;
- unsigned int sector_size_1k;
- unsigned int ecc_level;
- /* use for low-power standby/resume only */
- u32 acc_control;
- u32 config;
- u32 config_ext;
- u32 timing_1;
- u32 timing_2;
-};
-
-struct brcmnand_host {
- struct list_head node;
-
- struct nand_chip chip;
- struct platform_device *pdev;
- int cs;
-
- unsigned int last_cmd;
- unsigned int last_byte;
- u64 last_addr;
- struct brcmnand_cfg hwcfg;
- struct brcmnand_controller *ctrl;
-};
-
-enum brcmnand_reg {
- BRCMNAND_CMD_START = 0,
- BRCMNAND_CMD_EXT_ADDRESS,
- BRCMNAND_CMD_ADDRESS,
- BRCMNAND_INTFC_STATUS,
- BRCMNAND_CS_SELECT,
- BRCMNAND_CS_XOR,
- BRCMNAND_LL_OP,
- BRCMNAND_CS0_BASE,
- BRCMNAND_CS1_BASE, /* CS1 regs, if non-contiguous */
- BRCMNAND_CORR_THRESHOLD,
- BRCMNAND_CORR_THRESHOLD_EXT,
- BRCMNAND_UNCORR_COUNT,
- BRCMNAND_CORR_COUNT,
- BRCMNAND_CORR_EXT_ADDR,
- BRCMNAND_CORR_ADDR,
- BRCMNAND_UNCORR_EXT_ADDR,
- BRCMNAND_UNCORR_ADDR,
- BRCMNAND_SEMAPHORE,
- BRCMNAND_ID,
- BRCMNAND_ID_EXT,
- BRCMNAND_LL_RDATA,
- BRCMNAND_OOB_READ_BASE,
- BRCMNAND_OOB_READ_10_BASE, /* offset 0x10, if non-contiguous */
- BRCMNAND_OOB_WRITE_BASE,
- BRCMNAND_OOB_WRITE_10_BASE, /* offset 0x10, if non-contiguous */
- BRCMNAND_FC_BASE,
-};
-
-/* BRCMNAND v4.0 */
-static const u16 brcmnand_regs_v40[] = {
- [BRCMNAND_CMD_START] = 0x04,
- [BRCMNAND_CMD_EXT_ADDRESS] = 0x08,
- [BRCMNAND_CMD_ADDRESS] = 0x0c,
- [BRCMNAND_INTFC_STATUS] = 0x6c,
- [BRCMNAND_CS_SELECT] = 0x14,
- [BRCMNAND_CS_XOR] = 0x18,
- [BRCMNAND_LL_OP] = 0x178,
- [BRCMNAND_CS0_BASE] = 0x40,
- [BRCMNAND_CS1_BASE] = 0xd0,
- [BRCMNAND_CORR_THRESHOLD] = 0x84,
- [BRCMNAND_CORR_THRESHOLD_EXT] = 0,
- [BRCMNAND_UNCORR_COUNT] = 0,
- [BRCMNAND_CORR_COUNT] = 0,
- [BRCMNAND_CORR_EXT_ADDR] = 0x70,
- [BRCMNAND_CORR_ADDR] = 0x74,
- [BRCMNAND_UNCORR_EXT_ADDR] = 0x78,
- [BRCMNAND_UNCORR_ADDR] = 0x7c,
- [BRCMNAND_SEMAPHORE] = 0x58,
- [BRCMNAND_ID] = 0x60,
- [BRCMNAND_ID_EXT] = 0x64,
- [BRCMNAND_LL_RDATA] = 0x17c,
- [BRCMNAND_OOB_READ_BASE] = 0x20,
- [BRCMNAND_OOB_READ_10_BASE] = 0x130,
- [BRCMNAND_OOB_WRITE_BASE] = 0x30,
- [BRCMNAND_OOB_WRITE_10_BASE] = 0,
- [BRCMNAND_FC_BASE] = 0x200,
-};
-
-/* BRCMNAND v5.0 */
-static const u16 brcmnand_regs_v50[] = {
- [BRCMNAND_CMD_START] = 0x04,
- [BRCMNAND_CMD_EXT_ADDRESS] = 0x08,
- [BRCMNAND_CMD_ADDRESS] = 0x0c,
- [BRCMNAND_INTFC_STATUS] = 0x6c,
- [BRCMNAND_CS_SELECT] = 0x14,
- [BRCMNAND_CS_XOR] = 0x18,
- [BRCMNAND_LL_OP] = 0x178,
- [BRCMNAND_CS0_BASE] = 0x40,
- [BRCMNAND_CS1_BASE] = 0xd0,
- [BRCMNAND_CORR_THRESHOLD] = 0x84,
- [BRCMNAND_CORR_THRESHOLD_EXT] = 0,
- [BRCMNAND_UNCORR_COUNT] = 0,
- [BRCMNAND_CORR_COUNT] = 0,
- [BRCMNAND_CORR_EXT_ADDR] = 0x70,
- [BRCMNAND_CORR_ADDR] = 0x74,
- [BRCMNAND_UNCORR_EXT_ADDR] = 0x78,
- [BRCMNAND_UNCORR_ADDR] = 0x7c,
- [BRCMNAND_SEMAPHORE] = 0x58,
- [BRCMNAND_ID] = 0x60,
- [BRCMNAND_ID_EXT] = 0x64,
- [BRCMNAND_LL_RDATA] = 0x17c,
- [BRCMNAND_OOB_READ_BASE] = 0x20,
- [BRCMNAND_OOB_READ_10_BASE] = 0x130,
- [BRCMNAND_OOB_WRITE_BASE] = 0x30,
- [BRCMNAND_OOB_WRITE_10_BASE] = 0x140,
- [BRCMNAND_FC_BASE] = 0x200,
-};
-
-/* BRCMNAND v6.0 - v7.1 */
-static const u16 brcmnand_regs_v60[] = {
- [BRCMNAND_CMD_START] = 0x04,
- [BRCMNAND_CMD_EXT_ADDRESS] = 0x08,
- [BRCMNAND_CMD_ADDRESS] = 0x0c,
- [BRCMNAND_INTFC_STATUS] = 0x14,
- [BRCMNAND_CS_SELECT] = 0x18,
- [BRCMNAND_CS_XOR] = 0x1c,
- [BRCMNAND_LL_OP] = 0x20,
- [BRCMNAND_CS0_BASE] = 0x50,
- [BRCMNAND_CS1_BASE] = 0,
- [BRCMNAND_CORR_THRESHOLD] = 0xc0,
- [BRCMNAND_CORR_THRESHOLD_EXT] = 0xc4,
- [BRCMNAND_UNCORR_COUNT] = 0xfc,
- [BRCMNAND_CORR_COUNT] = 0x100,
- [BRCMNAND_CORR_EXT_ADDR] = 0x10c,
- [BRCMNAND_CORR_ADDR] = 0x110,
- [BRCMNAND_UNCORR_EXT_ADDR] = 0x114,
- [BRCMNAND_UNCORR_ADDR] = 0x118,
- [BRCMNAND_SEMAPHORE] = 0x150,
- [BRCMNAND_ID] = 0x194,
- [BRCMNAND_ID_EXT] = 0x198,
- [BRCMNAND_LL_RDATA] = 0x19c,
- [BRCMNAND_OOB_READ_BASE] = 0x200,
- [BRCMNAND_OOB_READ_10_BASE] = 0,
- [BRCMNAND_OOB_WRITE_BASE] = 0x280,
- [BRCMNAND_OOB_WRITE_10_BASE] = 0,
- [BRCMNAND_FC_BASE] = 0x400,
-};
-
-/* BRCMNAND v7.1 */
-static const u16 brcmnand_regs_v71[] = {
- [BRCMNAND_CMD_START] = 0x04,
- [BRCMNAND_CMD_EXT_ADDRESS] = 0x08,
- [BRCMNAND_CMD_ADDRESS] = 0x0c,
- [BRCMNAND_INTFC_STATUS] = 0x14,
- [BRCMNAND_CS_SELECT] = 0x18,
- [BRCMNAND_CS_XOR] = 0x1c,
- [BRCMNAND_LL_OP] = 0x20,
- [BRCMNAND_CS0_BASE] = 0x50,
- [BRCMNAND_CS1_BASE] = 0,
- [BRCMNAND_CORR_THRESHOLD] = 0xdc,
- [BRCMNAND_CORR_THRESHOLD_EXT] = 0xe0,
- [BRCMNAND_UNCORR_COUNT] = 0xfc,
- [BRCMNAND_CORR_COUNT] = 0x100,
- [BRCMNAND_CORR_EXT_ADDR] = 0x10c,
- [BRCMNAND_CORR_ADDR] = 0x110,
- [BRCMNAND_UNCORR_EXT_ADDR] = 0x114,
- [BRCMNAND_UNCORR_ADDR] = 0x118,
- [BRCMNAND_SEMAPHORE] = 0x150,
- [BRCMNAND_ID] = 0x194,
- [BRCMNAND_ID_EXT] = 0x198,
- [BRCMNAND_LL_RDATA] = 0x19c,
- [BRCMNAND_OOB_READ_BASE] = 0x200,
- [BRCMNAND_OOB_READ_10_BASE] = 0,
- [BRCMNAND_OOB_WRITE_BASE] = 0x280,
- [BRCMNAND_OOB_WRITE_10_BASE] = 0,
- [BRCMNAND_FC_BASE] = 0x400,
-};
-
-/* BRCMNAND v7.2 */
-static const u16 brcmnand_regs_v72[] = {
- [BRCMNAND_CMD_START] = 0x04,
- [BRCMNAND_CMD_EXT_ADDRESS] = 0x08,
- [BRCMNAND_CMD_ADDRESS] = 0x0c,
- [BRCMNAND_INTFC_STATUS] = 0x14,
- [BRCMNAND_CS_SELECT] = 0x18,
- [BRCMNAND_CS_XOR] = 0x1c,
- [BRCMNAND_LL_OP] = 0x20,
- [BRCMNAND_CS0_BASE] = 0x50,
- [BRCMNAND_CS1_BASE] = 0,
- [BRCMNAND_CORR_THRESHOLD] = 0xdc,
- [BRCMNAND_CORR_THRESHOLD_EXT] = 0xe0,
- [BRCMNAND_UNCORR_COUNT] = 0xfc,
- [BRCMNAND_CORR_COUNT] = 0x100,
- [BRCMNAND_CORR_EXT_ADDR] = 0x10c,
- [BRCMNAND_CORR_ADDR] = 0x110,
- [BRCMNAND_UNCORR_EXT_ADDR] = 0x114,
- [BRCMNAND_UNCORR_ADDR] = 0x118,
- [BRCMNAND_SEMAPHORE] = 0x150,
- [BRCMNAND_ID] = 0x194,
- [BRCMNAND_ID_EXT] = 0x198,
- [BRCMNAND_LL_RDATA] = 0x19c,
- [BRCMNAND_OOB_READ_BASE] = 0x200,
- [BRCMNAND_OOB_READ_10_BASE] = 0,
- [BRCMNAND_OOB_WRITE_BASE] = 0x400,
- [BRCMNAND_OOB_WRITE_10_BASE] = 0,
- [BRCMNAND_FC_BASE] = 0x600,
-};
-
-enum brcmnand_cs_reg {
- BRCMNAND_CS_CFG_EXT = 0,
- BRCMNAND_CS_CFG,
- BRCMNAND_CS_ACC_CONTROL,
- BRCMNAND_CS_TIMING1,
- BRCMNAND_CS_TIMING2,
-};
-
-/* Per chip-select offsets for v7.1 */
-static const u8 brcmnand_cs_offsets_v71[] = {
- [BRCMNAND_CS_ACC_CONTROL] = 0x00,
- [BRCMNAND_CS_CFG_EXT] = 0x04,
- [BRCMNAND_CS_CFG] = 0x08,
- [BRCMNAND_CS_TIMING1] = 0x0c,
- [BRCMNAND_CS_TIMING2] = 0x10,
-};
-
-/* Per chip-select offsets for pre v7.1, except CS0 on <= v5.0 */
-static const u8 brcmnand_cs_offsets[] = {
- [BRCMNAND_CS_ACC_CONTROL] = 0x00,
- [BRCMNAND_CS_CFG_EXT] = 0x04,
- [BRCMNAND_CS_CFG] = 0x04,
- [BRCMNAND_CS_TIMING1] = 0x08,
- [BRCMNAND_CS_TIMING2] = 0x0c,
-};
-
-/* Per chip-select offset for <= v5.0 on CS0 only */
-static const u8 brcmnand_cs_offsets_cs0[] = {
- [BRCMNAND_CS_ACC_CONTROL] = 0x00,
- [BRCMNAND_CS_CFG_EXT] = 0x08,
- [BRCMNAND_CS_CFG] = 0x08,
- [BRCMNAND_CS_TIMING1] = 0x10,
- [BRCMNAND_CS_TIMING2] = 0x14,
-};
-
-/*
- * Bitfields for the CFG and CFG_EXT registers. Pre-v7.1 controllers only had
- * one config register, but once the bitfields overflowed, newer controllers
- * (v7.1 and newer) added a CFG_EXT register and shuffled a few fields around.
- */
-enum {
- CFG_BLK_ADR_BYTES_SHIFT = 8,
- CFG_COL_ADR_BYTES_SHIFT = 12,
- CFG_FUL_ADR_BYTES_SHIFT = 16,
- CFG_BUS_WIDTH_SHIFT = 23,
- CFG_BUS_WIDTH = BIT(CFG_BUS_WIDTH_SHIFT),
- CFG_DEVICE_SIZE_SHIFT = 24,
-
- /* Only for pre-v7.1 (with no CFG_EXT register) */
- CFG_PAGE_SIZE_SHIFT = 20,
- CFG_BLK_SIZE_SHIFT = 28,
-
- /* Only for v7.1+ (with CFG_EXT register) */
- CFG_EXT_PAGE_SIZE_SHIFT = 0,
- CFG_EXT_BLK_SIZE_SHIFT = 4,
-};
-
-/* BRCMNAND_INTFC_STATUS */
-enum {
- INTFC_FLASH_STATUS = GENMASK(7, 0),
-
- INTFC_ERASED = BIT(27),
- INTFC_OOB_VALID = BIT(28),
- INTFC_CACHE_VALID = BIT(29),
- INTFC_FLASH_READY = BIT(30),
- INTFC_CTLR_READY = BIT(31),
-};
-
-static inline u32 nand_readreg(struct brcmnand_controller *ctrl, u32 offs)
-{
- return brcmnand_readl(ctrl->nand_base + offs);
-}
-
-static inline void nand_writereg(struct brcmnand_controller *ctrl, u32 offs,
- u32 val)
-{
- brcmnand_writel(val, ctrl->nand_base + offs);
-}
-
-static int brcmnand_revision_init(struct brcmnand_controller *ctrl)
-{
- static const unsigned int block_sizes_v6[] = { 8, 16, 128, 256, 512, 1024, 2048, 0 };
- static const unsigned int block_sizes_v4[] = { 16, 128, 8, 512, 256, 1024, 2048, 0 };
- static const unsigned int page_sizes[] = { 512, 2048, 4096, 8192, 0 };
-
- ctrl->nand_version = nand_readreg(ctrl, 0) & 0xffff;
-
- /* Only support v4.0+? */
- if (ctrl->nand_version < 0x0400) {
- dev_err(ctrl->dev, "version %#x not supported\n",
- ctrl->nand_version);
- return -ENODEV;
- }
-
- /* Register offsets */
- if (ctrl->nand_version >= 0x0702)
- ctrl->reg_offsets = brcmnand_regs_v72;
- else if (ctrl->nand_version >= 0x0701)
- ctrl->reg_offsets = brcmnand_regs_v71;
- else if (ctrl->nand_version >= 0x0600)
- ctrl->reg_offsets = brcmnand_regs_v60;
- else if (ctrl->nand_version >= 0x0500)
- ctrl->reg_offsets = brcmnand_regs_v50;
- else if (ctrl->nand_version >= 0x0400)
- ctrl->reg_offsets = brcmnand_regs_v40;
-
- /* Chip-select stride */
- if (ctrl->nand_version >= 0x0701)
- ctrl->reg_spacing = 0x14;
- else
- ctrl->reg_spacing = 0x10;
-
- /* Per chip-select registers */
- if (ctrl->nand_version >= 0x0701) {
- ctrl->cs_offsets = brcmnand_cs_offsets_v71;
- } else {
- ctrl->cs_offsets = brcmnand_cs_offsets;
-
- /* v5.0 and earlier has a different CS0 offset layout */
- if (ctrl->nand_version <= 0x0500)
- ctrl->cs0_offsets = brcmnand_cs_offsets_cs0;
- }
-
- /* Page / block sizes */
- if (ctrl->nand_version >= 0x0701) {
- /* >= v7.1 use nice power-of-2 values! */
- ctrl->max_page_size = 16 * 1024;
- ctrl->max_block_size = 2 * 1024 * 1024;
- } else {
- ctrl->page_sizes = page_sizes;
- if (ctrl->nand_version >= 0x0600)
- ctrl->block_sizes = block_sizes_v6;
- else
- ctrl->block_sizes = block_sizes_v4;
-
- if (ctrl->nand_version < 0x0400) {
- ctrl->max_page_size = 4096;
- ctrl->max_block_size = 512 * 1024;
- }
- }
-
- /* Maximum spare area sector size (per 512B) */
- if (ctrl->nand_version >= 0x0702)
- ctrl->max_oob = 128;
- else if (ctrl->nand_version >= 0x0600)
- ctrl->max_oob = 64;
- else if (ctrl->nand_version >= 0x0500)
- ctrl->max_oob = 32;
- else
- ctrl->max_oob = 16;
-
- /* v6.0 and newer (except v6.1) have prefetch support */
- if (ctrl->nand_version >= 0x0600 && ctrl->nand_version != 0x0601)
- ctrl->features |= BRCMNAND_HAS_PREFETCH;
-
- /*
- * v6.x has cache mode, but it's implemented differently. Ignore it for
- * now.
- */
- if (ctrl->nand_version >= 0x0700)
- ctrl->features |= BRCMNAND_HAS_CACHE_MODE;
-
- if (ctrl->nand_version >= 0x0500)
- ctrl->features |= BRCMNAND_HAS_1K_SECTORS;
-
- if (ctrl->nand_version >= 0x0700)
- ctrl->features |= BRCMNAND_HAS_WP;
- else if (of_property_read_bool(ctrl->dev->of_node, "brcm,nand-has-wp"))
- ctrl->features |= BRCMNAND_HAS_WP;
-
- return 0;
-}
-
-static inline u32 brcmnand_read_reg(struct brcmnand_controller *ctrl,
- enum brcmnand_reg reg)
-{
- u16 offs = ctrl->reg_offsets[reg];
-
- if (offs)
- return nand_readreg(ctrl, offs);
- else
- return 0;
-}
-
-static inline void brcmnand_write_reg(struct brcmnand_controller *ctrl,
- enum brcmnand_reg reg, u32 val)
-{
- u16 offs = ctrl->reg_offsets[reg];
-
- if (offs)
- nand_writereg(ctrl, offs, val);
-}
-
-static inline void brcmnand_rmw_reg(struct brcmnand_controller *ctrl,
- enum brcmnand_reg reg, u32 mask, unsigned
- int shift, u32 val)
-{
- u32 tmp = brcmnand_read_reg(ctrl, reg);
-
- tmp &= ~mask;
- tmp |= val << shift;
- brcmnand_write_reg(ctrl, reg, tmp);
-}
-
-static inline u32 brcmnand_read_fc(struct brcmnand_controller *ctrl, int word)
-{
- return __raw_readl(ctrl->nand_fc + word * 4);
-}
-
-static inline void brcmnand_write_fc(struct brcmnand_controller *ctrl,
- int word, u32 val)
-{
- __raw_writel(val, ctrl->nand_fc + word * 4);
-}
-
-static inline u16 brcmnand_cs_offset(struct brcmnand_controller *ctrl, int cs,
- enum brcmnand_cs_reg reg)
-{
- u16 offs_cs0 = ctrl->reg_offsets[BRCMNAND_CS0_BASE];
- u16 offs_cs1 = ctrl->reg_offsets[BRCMNAND_CS1_BASE];
- u8 cs_offs;
-
- if (cs == 0 && ctrl->cs0_offsets)
- cs_offs = ctrl->cs0_offsets[reg];
- else
- cs_offs = ctrl->cs_offsets[reg];
-
- if (cs && offs_cs1)
- return offs_cs1 + (cs - 1) * ctrl->reg_spacing + cs_offs;
-
- return offs_cs0 + cs * ctrl->reg_spacing + cs_offs;
-}
-
-static inline u32 brcmnand_count_corrected(struct brcmnand_controller *ctrl)
-{
- if (ctrl->nand_version < 0x0600)
- return 1;
- return brcmnand_read_reg(ctrl, BRCMNAND_CORR_COUNT);
-}
-
-static void brcmnand_wr_corr_thresh(struct brcmnand_host *host, u8 val)
-{
- struct brcmnand_controller *ctrl = host->ctrl;
- unsigned int shift = 0, bits;
- enum brcmnand_reg reg = BRCMNAND_CORR_THRESHOLD;
- int cs = host->cs;
-
- if (ctrl->nand_version >= 0x0702)
- bits = 7;
- else if (ctrl->nand_version >= 0x0600)
- bits = 6;
- else if (ctrl->nand_version >= 0x0500)
- bits = 5;
- else
- bits = 4;
-
- if (ctrl->nand_version >= 0x0702) {
- if (cs >= 4)
- reg = BRCMNAND_CORR_THRESHOLD_EXT;
- shift = (cs % 4) * bits;
- } else if (ctrl->nand_version >= 0x0600) {
- if (cs >= 5)
- reg = BRCMNAND_CORR_THRESHOLD_EXT;
- shift = (cs % 5) * bits;
- }
- brcmnand_rmw_reg(ctrl, reg, (bits - 1) << shift, shift, val);
-}
-
-static inline int brcmnand_cmd_shift(struct brcmnand_controller *ctrl)
-{
- if (ctrl->nand_version < 0x0602)
- return 24;
- return 0;
-}
-
-/***********************************************************************
- * NAND ACC CONTROL bitfield
- *
- * Some bits have remained constant throughout hardware revision, while
- * others have shifted around.
- ***********************************************************************/
-
-/* Constant for all versions (where supported) */
-enum {
- /* See BRCMNAND_HAS_CACHE_MODE */
- ACC_CONTROL_CACHE_MODE = BIT(22),
-
- /* See BRCMNAND_HAS_PREFETCH */
- ACC_CONTROL_PREFETCH = BIT(23),
-
- ACC_CONTROL_PAGE_HIT = BIT(24),
- ACC_CONTROL_WR_PREEMPT = BIT(25),
- ACC_CONTROL_PARTIAL_PAGE = BIT(26),
- ACC_CONTROL_RD_ERASED = BIT(27),
- ACC_CONTROL_FAST_PGM_RDIN = BIT(28),
- ACC_CONTROL_WR_ECC = BIT(30),
- ACC_CONTROL_RD_ECC = BIT(31),
-};
-
-static inline u32 brcmnand_spare_area_mask(struct brcmnand_controller *ctrl)
-{
- if (ctrl->nand_version >= 0x0702)
- return GENMASK(7, 0);
- else if (ctrl->nand_version >= 0x0600)
- return GENMASK(6, 0);
- else
- return GENMASK(5, 0);
-}
-
-#define NAND_ACC_CONTROL_ECC_SHIFT 16
-#define NAND_ACC_CONTROL_ECC_EXT_SHIFT 13
-
-static inline u32 brcmnand_ecc_level_mask(struct brcmnand_controller *ctrl)
-{
- u32 mask = (ctrl->nand_version >= 0x0600) ? 0x1f : 0x0f;
-
- mask <<= NAND_ACC_CONTROL_ECC_SHIFT;
-
- /* v7.2 includes additional ECC levels */
- if (ctrl->nand_version >= 0x0702)
- mask |= 0x7 << NAND_ACC_CONTROL_ECC_EXT_SHIFT;
-
- return mask;
-}
-
-static void brcmnand_set_ecc_enabled(struct brcmnand_host *host, int en)
-{
- struct brcmnand_controller *ctrl = host->ctrl;
- u16 offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL);
- u32 acc_control = nand_readreg(ctrl, offs);
- u32 ecc_flags = ACC_CONTROL_WR_ECC | ACC_CONTROL_RD_ECC;
-
- if (en) {
- acc_control |= ecc_flags; /* enable RD/WR ECC */
- acc_control |= host->hwcfg.ecc_level
- << NAND_ACC_CONTROL_ECC_SHIFT;
- } else {
- acc_control &= ~ecc_flags; /* disable RD/WR ECC */
- acc_control &= ~brcmnand_ecc_level_mask(ctrl);
- }
-
- nand_writereg(ctrl, offs, acc_control);
-}
-
-static inline int brcmnand_sector_1k_shift(struct brcmnand_controller *ctrl)
-{
- if (ctrl->nand_version >= 0x0702)
- return 9;
- else if (ctrl->nand_version >= 0x0600)
- return 7;
- else if (ctrl->nand_version >= 0x0500)
- return 6;
- else
- return -1;
-}
-
-static int brcmnand_get_sector_size_1k(struct brcmnand_host *host)
-{
- struct brcmnand_controller *ctrl = host->ctrl;
- int shift = brcmnand_sector_1k_shift(ctrl);
- u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
- BRCMNAND_CS_ACC_CONTROL);
-
- if (shift < 0)
- return 0;
-
- return (nand_readreg(ctrl, acc_control_offs) >> shift) & 0x1;
-}
-
-static void brcmnand_set_sector_size_1k(struct brcmnand_host *host, int val)
-{
- struct brcmnand_controller *ctrl = host->ctrl;
- int shift = brcmnand_sector_1k_shift(ctrl);
- u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
- BRCMNAND_CS_ACC_CONTROL);
- u32 tmp;
-
- if (shift < 0)
- return;
-
- tmp = nand_readreg(ctrl, acc_control_offs);
- tmp &= ~(1 << shift);
- tmp |= (!!val) << shift;
- nand_writereg(ctrl, acc_control_offs, tmp);
-}
-
-/***********************************************************************
- * CS_NAND_SELECT
- ***********************************************************************/
-
-enum {
- CS_SELECT_NAND_WP = BIT(29),
- CS_SELECT_AUTO_DEVICE_ID_CFG = BIT(30),
-};
-
-static int bcmnand_ctrl_poll_status(struct brcmnand_controller *ctrl,
- u32 mask, u32 expected_val,
- unsigned long timeout_ms)
-{
- unsigned long limit;
- u32 val;
-
- if (!timeout_ms)
- timeout_ms = NAND_POLL_STATUS_TIMEOUT_MS;
-
- limit = jiffies + msecs_to_jiffies(timeout_ms);
- do {
- val = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS);
- if ((val & mask) == expected_val)
- return 0;
-
- cpu_relax();
- } while (time_after(limit, jiffies));
-
- dev_warn(ctrl->dev, "timeout on status poll (expected %x got %x)\n",
- expected_val, val & mask);
-
- return -ETIMEDOUT;
-}
-
-static inline void brcmnand_set_wp(struct brcmnand_controller *ctrl, bool en)
-{
- u32 val = en ? CS_SELECT_NAND_WP : 0;
-
- brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT, CS_SELECT_NAND_WP, 0, val);
-}
-
-/***********************************************************************
- * Flash DMA
- ***********************************************************************/
-
-enum flash_dma_reg {
- FLASH_DMA_REVISION = 0x00,
- FLASH_DMA_FIRST_DESC = 0x04,
- FLASH_DMA_FIRST_DESC_EXT = 0x08,
- FLASH_DMA_CTRL = 0x0c,
- FLASH_DMA_MODE = 0x10,
- FLASH_DMA_STATUS = 0x14,
- FLASH_DMA_INTERRUPT_DESC = 0x18,
- FLASH_DMA_INTERRUPT_DESC_EXT = 0x1c,
- FLASH_DMA_ERROR_STATUS = 0x20,
- FLASH_DMA_CURRENT_DESC = 0x24,
- FLASH_DMA_CURRENT_DESC_EXT = 0x28,
-};
-
-static inline bool has_flash_dma(struct brcmnand_controller *ctrl)
-{
- return ctrl->flash_dma_base;
-}
-
-static inline bool flash_dma_buf_ok(const void *buf)
-{
- return buf && !is_vmalloc_addr(buf) &&
- likely(IS_ALIGNED((uintptr_t)buf, 4));
-}
-
-static inline void flash_dma_writel(struct brcmnand_controller *ctrl, u8 offs,
- u32 val)
-{
- brcmnand_writel(val, ctrl->flash_dma_base + offs);
-}
-
-static inline u32 flash_dma_readl(struct brcmnand_controller *ctrl, u8 offs)
-{
- return brcmnand_readl(ctrl->flash_dma_base + offs);
-}
-
-/* Low-level operation types: command, address, write, or read */
-enum brcmnand_llop_type {
- LL_OP_CMD,
- LL_OP_ADDR,
- LL_OP_WR,
- LL_OP_RD,
-};
-
-/***********************************************************************
- * Internal support functions
- ***********************************************************************/
-
-static inline bool is_hamming_ecc(struct brcmnand_controller *ctrl,
- struct brcmnand_cfg *cfg)
-{
- if (ctrl->nand_version <= 0x0701)
- return cfg->sector_size_1k == 0 && cfg->spare_area_size == 16 &&
- cfg->ecc_level == 15;
- else
- return cfg->sector_size_1k == 0 && ((cfg->spare_area_size == 16 &&
- cfg->ecc_level == 15) ||
- (cfg->spare_area_size == 28 && cfg->ecc_level == 16));
-}
-
-/*
- * Set mtd->ooblayout to the appropriate mtd_ooblayout_ops given
- * the layout/configuration.
- * Returns -ERRCODE on failure.
- */
-static int brcmnand_hamming_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct brcmnand_host *host = nand_get_controller_data(chip);
- struct brcmnand_cfg *cfg = &host->hwcfg;
- int sas = cfg->spare_area_size << cfg->sector_size_1k;
- int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
-
- if (section >= sectors)
- return -ERANGE;
-
- oobregion->offset = (section * sas) + 6;
- oobregion->length = 3;
-
- return 0;
-}
-
-static int brcmnand_hamming_ooblayout_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct brcmnand_host *host = nand_get_controller_data(chip);
- struct brcmnand_cfg *cfg = &host->hwcfg;
- int sas = cfg->spare_area_size << cfg->sector_size_1k;
- int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
-
- if (section >= sectors * 2)
- return -ERANGE;
-
- oobregion->offset = (section / 2) * sas;
-
- if (section & 1) {
- oobregion->offset += 9;
- oobregion->length = 7;
- } else {
- oobregion->length = 6;
-
- /* First sector of each page may have BBI */
- if (!section) {
- /*
- * Small-page NAND use byte 6 for BBI while large-page
- * NAND use byte 0.
- */
- if (cfg->page_size > 512)
- oobregion->offset++;
- oobregion->length--;
- }
- }
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops brcmnand_hamming_ooblayout_ops = {
- .ecc = brcmnand_hamming_ooblayout_ecc,
- .free = brcmnand_hamming_ooblayout_free,
-};
-
-static int brcmnand_bch_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct brcmnand_host *host = nand_get_controller_data(chip);
- struct brcmnand_cfg *cfg = &host->hwcfg;
- int sas = cfg->spare_area_size << cfg->sector_size_1k;
- int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
-
- if (section >= sectors)
- return -ERANGE;
-
- oobregion->offset = (section * (sas + 1)) - chip->ecc.bytes;
- oobregion->length = chip->ecc.bytes;
-
- return 0;
-}
-
-static int brcmnand_bch_ooblayout_free_lp(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct brcmnand_host *host = nand_get_controller_data(chip);
- struct brcmnand_cfg *cfg = &host->hwcfg;
- int sas = cfg->spare_area_size << cfg->sector_size_1k;
- int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
-
- if (section >= sectors)
- return -ERANGE;
-
- if (sas <= chip->ecc.bytes)
- return 0;
-
- oobregion->offset = section * sas;
- oobregion->length = sas - chip->ecc.bytes;
-
- if (!section) {
- oobregion->offset++;
- oobregion->length--;
- }
-
- return 0;
-}
-
-static int brcmnand_bch_ooblayout_free_sp(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct brcmnand_host *host = nand_get_controller_data(chip);
- struct brcmnand_cfg *cfg = &host->hwcfg;
- int sas = cfg->spare_area_size << cfg->sector_size_1k;
-
- if (section > 1 || sas - chip->ecc.bytes < 6 ||
- (section && sas - chip->ecc.bytes == 6))
- return -ERANGE;
-
- if (!section) {
- oobregion->offset = 0;
- oobregion->length = 5;
- } else {
- oobregion->offset = 6;
- oobregion->length = sas - chip->ecc.bytes - 6;
- }
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops brcmnand_bch_lp_ooblayout_ops = {
- .ecc = brcmnand_bch_ooblayout_ecc,
- .free = brcmnand_bch_ooblayout_free_lp,
-};
-
-static const struct mtd_ooblayout_ops brcmnand_bch_sp_ooblayout_ops = {
- .ecc = brcmnand_bch_ooblayout_ecc,
- .free = brcmnand_bch_ooblayout_free_sp,
-};
-
-static int brcmstb_choose_ecc_layout(struct brcmnand_host *host)
-{
- struct brcmnand_cfg *p = &host->hwcfg;
- struct mtd_info *mtd = nand_to_mtd(&host->chip);
- struct nand_ecc_ctrl *ecc = &host->chip.ecc;
- unsigned int ecc_level = p->ecc_level;
- int sas = p->spare_area_size << p->sector_size_1k;
- int sectors = p->page_size / (512 << p->sector_size_1k);
-
- if (p->sector_size_1k)
- ecc_level <<= 1;
-
- if (is_hamming_ecc(host->ctrl, p)) {
- ecc->bytes = 3 * sectors;
- mtd_set_ooblayout(mtd, &brcmnand_hamming_ooblayout_ops);
- return 0;
- }
-
- /*
- * CONTROLLER_VERSION:
- * < v5.0: ECC_REQ = ceil(BCH_T * 13/8)
- * >= v5.0: ECC_REQ = ceil(BCH_T * 14/8)
- * But we will just be conservative.
- */
- ecc->bytes = DIV_ROUND_UP(ecc_level * 14, 8);
- if (p->page_size == 512)
- mtd_set_ooblayout(mtd, &brcmnand_bch_sp_ooblayout_ops);
- else
- mtd_set_ooblayout(mtd, &brcmnand_bch_lp_ooblayout_ops);
-
- if (ecc->bytes >= sas) {
- dev_err(&host->pdev->dev,
- "error: ECC too large for OOB (ECC bytes %d, spare sector %d)\n",
- ecc->bytes, sas);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static void brcmnand_wp(struct mtd_info *mtd, int wp)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct brcmnand_host *host = nand_get_controller_data(chip);
- struct brcmnand_controller *ctrl = host->ctrl;
-
- if ((ctrl->features & BRCMNAND_HAS_WP) && wp_on == 1) {
- static int old_wp = -1;
- int ret;
-
- if (old_wp != wp) {
- dev_dbg(ctrl->dev, "WP %s\n", wp ? "on" : "off");
- old_wp = wp;
- }
-
- /*
- * make sure ctrl/flash ready before and after
- * changing state of #WP pin
- */
- ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY |
- NAND_STATUS_READY,
- NAND_CTRL_RDY |
- NAND_STATUS_READY, 0);
- if (ret)
- return;
-
- brcmnand_set_wp(ctrl, wp);
- nand_status_op(chip, NULL);
- /* NAND_STATUS_WP 0x00 = protected, 0x80 = not protected */
- ret = bcmnand_ctrl_poll_status(ctrl,
- NAND_CTRL_RDY |
- NAND_STATUS_READY |
- NAND_STATUS_WP,
- NAND_CTRL_RDY |
- NAND_STATUS_READY |
- (wp ? 0 : NAND_STATUS_WP), 0);
-
- if (ret)
- dev_err_ratelimited(&host->pdev->dev,
- "nand #WP expected %s\n",
- wp ? "on" : "off");
- }
-}
-
-/* Helper functions for reading and writing OOB registers */
-static inline u8 oob_reg_read(struct brcmnand_controller *ctrl, u32 offs)
-{
- u16 offset0, offset10, reg_offs;
-
- offset0 = ctrl->reg_offsets[BRCMNAND_OOB_READ_BASE];
- offset10 = ctrl->reg_offsets[BRCMNAND_OOB_READ_10_BASE];
-
- if (offs >= ctrl->max_oob)
- return 0x77;
-
- if (offs >= 16 && offset10)
- reg_offs = offset10 + ((offs - 0x10) & ~0x03);
- else
- reg_offs = offset0 + (offs & ~0x03);
-
- return nand_readreg(ctrl, reg_offs) >> (24 - ((offs & 0x03) << 3));
-}
-
-static inline void oob_reg_write(struct brcmnand_controller *ctrl, u32 offs,
- u32 data)
-{
- u16 offset0, offset10, reg_offs;
-
- offset0 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_BASE];
- offset10 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_10_BASE];
-
- if (offs >= ctrl->max_oob)
- return;
-
- if (offs >= 16 && offset10)
- reg_offs = offset10 + ((offs - 0x10) & ~0x03);
- else
- reg_offs = offset0 + (offs & ~0x03);
-
- nand_writereg(ctrl, reg_offs, data);
-}
-
-/*
- * read_oob_from_regs - read data from OOB registers
- * @ctrl: NAND controller
- * @i: sub-page sector index
- * @oob: buffer to read to
- * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE)
- * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal
- */
-static int read_oob_from_regs(struct brcmnand_controller *ctrl, int i, u8 *oob,
- int sas, int sector_1k)
-{
- int tbytes = sas << sector_1k;
- int j;
-
- /* Adjust OOB values for 1K sector size */
- if (sector_1k && (i & 0x01))
- tbytes = max(0, tbytes - (int)ctrl->max_oob);
- tbytes = min_t(int, tbytes, ctrl->max_oob);
-
- for (j = 0; j < tbytes; j++)
- oob[j] = oob_reg_read(ctrl, j);
- return tbytes;
-}
-
-/*
- * write_oob_to_regs - write data to OOB registers
- * @i: sub-page sector index
- * @oob: buffer to write from
- * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE)
- * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal
- */
-static int write_oob_to_regs(struct brcmnand_controller *ctrl, int i,
- const u8 *oob, int sas, int sector_1k)
-{
- int tbytes = sas << sector_1k;
- int j;
-
- /* Adjust OOB values for 1K sector size */
- if (sector_1k && (i & 0x01))
- tbytes = max(0, tbytes - (int)ctrl->max_oob);
- tbytes = min_t(int, tbytes, ctrl->max_oob);
-
- for (j = 0; j < tbytes; j += 4)
- oob_reg_write(ctrl, j,
- (oob[j + 0] << 24) |
- (oob[j + 1] << 16) |
- (oob[j + 2] << 8) |
- (oob[j + 3] << 0));
- return tbytes;
-}
-
-static irqreturn_t brcmnand_ctlrdy_irq(int irq, void *data)
-{
- struct brcmnand_controller *ctrl = data;
-
- /* Discard all NAND_CTLRDY interrupts during DMA */
- if (ctrl->dma_pending)
- return IRQ_HANDLED;
-
- complete(&ctrl->done);
- return IRQ_HANDLED;
-}
-
-/* Handle SoC-specific interrupt hardware */
-static irqreturn_t brcmnand_irq(int irq, void *data)
-{
- struct brcmnand_controller *ctrl = data;
-
- if (ctrl->soc->ctlrdy_ack(ctrl->soc))
- return brcmnand_ctlrdy_irq(irq, data);
-
- return IRQ_NONE;
-}
-
-static irqreturn_t brcmnand_dma_irq(int irq, void *data)
-{
- struct brcmnand_controller *ctrl = data;
-
- complete(&ctrl->dma_done);
-
- return IRQ_HANDLED;
-}
-
-static void brcmnand_send_cmd(struct brcmnand_host *host, int cmd)
-{
- struct brcmnand_controller *ctrl = host->ctrl;
- int ret;
-
- dev_dbg(ctrl->dev, "send native cmd %d addr_lo 0x%x\n", cmd,
- brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS));
- BUG_ON(ctrl->cmd_pending != 0);
- ctrl->cmd_pending = cmd;
-
- ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY, NAND_CTRL_RDY, 0);
- WARN_ON(ret);
-
- mb(); /* flush previous writes */
- brcmnand_write_reg(ctrl, BRCMNAND_CMD_START,
- cmd << brcmnand_cmd_shift(ctrl));
-}
-
-/***********************************************************************
- * NAND MTD API: read/program/erase
- ***********************************************************************/
-
-static void brcmnand_cmd_ctrl(struct mtd_info *mtd, int dat,
- unsigned int ctrl)
-{
- /* intentionally left blank */
-}
-
-static int brcmnand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct brcmnand_host *host = nand_get_controller_data(chip);
- struct brcmnand_controller *ctrl = host->ctrl;
- unsigned long timeo = msecs_to_jiffies(100);
-
- dev_dbg(ctrl->dev, "wait on native cmd %d\n", ctrl->cmd_pending);
- if (ctrl->cmd_pending &&
- wait_for_completion_timeout(&ctrl->done, timeo) <= 0) {
- u32 cmd = brcmnand_read_reg(ctrl, BRCMNAND_CMD_START)
- >> brcmnand_cmd_shift(ctrl);
-
- dev_err_ratelimited(ctrl->dev,
- "timeout waiting for command %#02x\n", cmd);
- dev_err_ratelimited(ctrl->dev, "intfc status %08x\n",
- brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS));
- }
- ctrl->cmd_pending = 0;
- return brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) &
- INTFC_FLASH_STATUS;
-}
-
-enum {
- LLOP_RE = BIT(16),
- LLOP_WE = BIT(17),
- LLOP_ALE = BIT(18),
- LLOP_CLE = BIT(19),
- LLOP_RETURN_IDLE = BIT(31),
-
- LLOP_DATA_MASK = GENMASK(15, 0),
-};
-
-static int brcmnand_low_level_op(struct brcmnand_host *host,
- enum brcmnand_llop_type type, u32 data,
- bool last_op)
-{
- struct mtd_info *mtd = nand_to_mtd(&host->chip);
- struct nand_chip *chip = &host->chip;
- struct brcmnand_controller *ctrl = host->ctrl;
- u32 tmp;
-
- tmp = data & LLOP_DATA_MASK;
- switch (type) {
- case LL_OP_CMD:
- tmp |= LLOP_WE | LLOP_CLE;
- break;
- case LL_OP_ADDR:
- /* WE | ALE */
- tmp |= LLOP_WE | LLOP_ALE;
- break;
- case LL_OP_WR:
- /* WE */
- tmp |= LLOP_WE;
- break;
- case LL_OP_RD:
- /* RE */
- tmp |= LLOP_RE;
- break;
- }
- if (last_op)
- /* RETURN_IDLE */
- tmp |= LLOP_RETURN_IDLE;
-
- dev_dbg(ctrl->dev, "ll_op cmd %#x\n", tmp);
-
- brcmnand_write_reg(ctrl, BRCMNAND_LL_OP, tmp);
- (void)brcmnand_read_reg(ctrl, BRCMNAND_LL_OP);
-
- brcmnand_send_cmd(host, CMD_LOW_LEVEL_OP);
- return brcmnand_waitfunc(mtd, chip);
-}
-
-static void brcmnand_cmdfunc(struct mtd_info *mtd, unsigned command,
- int column, int page_addr)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct brcmnand_host *host = nand_get_controller_data(chip);
- struct brcmnand_controller *ctrl = host->ctrl;
- u64 addr = (u64)page_addr << chip->page_shift;
- int native_cmd = 0;
-
- if (command == NAND_CMD_READID || command == NAND_CMD_PARAM ||
- command == NAND_CMD_RNDOUT)
- addr = (u64)column;
- /* Avoid propagating a negative, don't-care address */
- else if (page_addr < 0)
- addr = 0;
-
- dev_dbg(ctrl->dev, "cmd 0x%x addr 0x%llx\n", command,
- (unsigned long long)addr);
-
- host->last_cmd = command;
- host->last_byte = 0;
- host->last_addr = addr;
-
- switch (command) {
- case NAND_CMD_RESET:
- native_cmd = CMD_FLASH_RESET;
- break;
- case NAND_CMD_STATUS:
- native_cmd = CMD_STATUS_READ;
- break;
- case NAND_CMD_READID:
- native_cmd = CMD_DEVICE_ID_READ;
- break;
- case NAND_CMD_READOOB:
- native_cmd = CMD_SPARE_AREA_READ;
- break;
- case NAND_CMD_ERASE1:
- native_cmd = CMD_BLOCK_ERASE;
- brcmnand_wp(mtd, 0);
- break;
- case NAND_CMD_PARAM:
- native_cmd = CMD_PARAMETER_READ;
- break;
- case NAND_CMD_SET_FEATURES:
- case NAND_CMD_GET_FEATURES:
- brcmnand_low_level_op(host, LL_OP_CMD, command, false);
- brcmnand_low_level_op(host, LL_OP_ADDR, column, false);
- break;
- case NAND_CMD_RNDOUT:
- native_cmd = CMD_PARAMETER_CHANGE_COL;
- addr &= ~((u64)(FC_BYTES - 1));
- /*
- * HW quirk: PARAMETER_CHANGE_COL requires SECTOR_SIZE_1K=0
- * NB: hwcfg.sector_size_1k may not be initialized yet
- */
- if (brcmnand_get_sector_size_1k(host)) {
- host->hwcfg.sector_size_1k =
- brcmnand_get_sector_size_1k(host);
- brcmnand_set_sector_size_1k(host, 0);
- }
- break;
- }
-
- if (!native_cmd)
- return;
-
- brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS,
- (host->cs << 16) | ((addr >> 32) & 0xffff));
- (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS);
- brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, lower_32_bits(addr));
- (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS);
-
- brcmnand_send_cmd(host, native_cmd);
- brcmnand_waitfunc(mtd, chip);
-
- if (native_cmd == CMD_PARAMETER_READ ||
- native_cmd == CMD_PARAMETER_CHANGE_COL) {
- /* Copy flash cache word-wise */
- u32 *flash_cache = (u32 *)ctrl->flash_cache;
- int i;
-
- brcmnand_soc_data_bus_prepare(ctrl->soc, true);
-
- /*
- * Must cache the FLASH_CACHE now, since changes in
- * SECTOR_SIZE_1K may invalidate it
- */
- for (i = 0; i < FC_WORDS; i++)
- /*
- * Flash cache is big endian for parameter pages, at
- * least on STB SoCs
- */
- flash_cache[i] = be32_to_cpu(brcmnand_read_fc(ctrl, i));
-
- brcmnand_soc_data_bus_unprepare(ctrl->soc, true);
-
- /* Cleanup from HW quirk: restore SECTOR_SIZE_1K */
- if (host->hwcfg.sector_size_1k)
- brcmnand_set_sector_size_1k(host,
- host->hwcfg.sector_size_1k);
- }
-
- /* Re-enable protection is necessary only after erase */
- if (command == NAND_CMD_ERASE1)
- brcmnand_wp(mtd, 1);
-}
-
-static uint8_t brcmnand_read_byte(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct brcmnand_host *host = nand_get_controller_data(chip);
- struct brcmnand_controller *ctrl = host->ctrl;
- uint8_t ret = 0;
- int addr, offs;
-
- switch (host->last_cmd) {
- case NAND_CMD_READID:
- if (host->last_byte < 4)
- ret = brcmnand_read_reg(ctrl, BRCMNAND_ID) >>
- (24 - (host->last_byte << 3));
- else if (host->last_byte < 8)
- ret = brcmnand_read_reg(ctrl, BRCMNAND_ID_EXT) >>
- (56 - (host->last_byte << 3));
- break;
-
- case NAND_CMD_READOOB:
- ret = oob_reg_read(ctrl, host->last_byte);
- break;
-
- case NAND_CMD_STATUS:
- ret = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) &
- INTFC_FLASH_STATUS;
- if (wp_on) /* hide WP status */
- ret |= NAND_STATUS_WP;
- break;
-
- case NAND_CMD_PARAM:
- case NAND_CMD_RNDOUT:
- addr = host->last_addr + host->last_byte;
- offs = addr & (FC_BYTES - 1);
-
- /* At FC_BYTES boundary, switch to next column */
- if (host->last_byte > 0 && offs == 0)
- nand_change_read_column_op(chip, addr, NULL, 0, false);
-
- ret = ctrl->flash_cache[offs];
- break;
- case NAND_CMD_GET_FEATURES:
- if (host->last_byte >= ONFI_SUBFEATURE_PARAM_LEN) {
- ret = 0;
- } else {
- bool last = host->last_byte ==
- ONFI_SUBFEATURE_PARAM_LEN - 1;
- brcmnand_low_level_op(host, LL_OP_RD, 0, last);
- ret = brcmnand_read_reg(ctrl, BRCMNAND_LL_RDATA) & 0xff;
- }
- }
-
- dev_dbg(ctrl->dev, "read byte = 0x%02x\n", ret);
- host->last_byte++;
-
- return ret;
-}
-
-static void brcmnand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
-{
- int i;
-
- for (i = 0; i < len; i++, buf++)
- *buf = brcmnand_read_byte(mtd);
-}
-
-static void brcmnand_write_buf(struct mtd_info *mtd, const uint8_t *buf,
- int len)
-{
- int i;
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct brcmnand_host *host = nand_get_controller_data(chip);
-
- switch (host->last_cmd) {
- case NAND_CMD_SET_FEATURES:
- for (i = 0; i < len; i++)
- brcmnand_low_level_op(host, LL_OP_WR, buf[i],
- (i + 1) == len);
- break;
- default:
- BUG();
- break;
- }
-}
-
-/**
- * Construct a FLASH_DMA descriptor as part of a linked list. You must know the
- * following ahead of time:
- * - Is this descriptor the beginning or end of a linked list?
- * - What is the (DMA) address of the next descriptor in the linked list?
- */
-static int brcmnand_fill_dma_desc(struct brcmnand_host *host,
- struct brcm_nand_dma_desc *desc, u64 addr,
- dma_addr_t buf, u32 len, u8 dma_cmd,
- bool begin, bool end,
- dma_addr_t next_desc)
-{
- memset(desc, 0, sizeof(*desc));
- /* Descriptors are written in native byte order (wordwise) */
- desc->next_desc = lower_32_bits(next_desc);
- desc->next_desc_ext = upper_32_bits(next_desc);
- desc->cmd_irq = (dma_cmd << 24) |
- (end ? (0x03 << 8) : 0) | /* IRQ | STOP */
- (!!begin) | ((!!end) << 1); /* head, tail */
-#ifdef CONFIG_CPU_BIG_ENDIAN
- desc->cmd_irq |= 0x01 << 12;
-#endif
- desc->dram_addr = lower_32_bits(buf);
- desc->dram_addr_ext = upper_32_bits(buf);
- desc->tfr_len = len;
- desc->total_len = len;
- desc->flash_addr = lower_32_bits(addr);
- desc->flash_addr_ext = upper_32_bits(addr);
- desc->cs = host->cs;
- desc->status_valid = 0x01;
- return 0;
-}
-
-/**
- * Kick the FLASH_DMA engine, with a given DMA descriptor
- */
-static void brcmnand_dma_run(struct brcmnand_host *host, dma_addr_t desc)
-{
- struct brcmnand_controller *ctrl = host->ctrl;
- unsigned long timeo = msecs_to_jiffies(100);
-
- flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC, lower_32_bits(desc));
- (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC);
- flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC_EXT, upper_32_bits(desc));
- (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC_EXT);
-
- /* Start FLASH_DMA engine */
- ctrl->dma_pending = true;
- mb(); /* flush previous writes */
- flash_dma_writel(ctrl, FLASH_DMA_CTRL, 0x03); /* wake | run */
-
- if (wait_for_completion_timeout(&ctrl->dma_done, timeo) <= 0) {
- dev_err(ctrl->dev,
- "timeout waiting for DMA; status %#x, error status %#x\n",
- flash_dma_readl(ctrl, FLASH_DMA_STATUS),
- flash_dma_readl(ctrl, FLASH_DMA_ERROR_STATUS));
- }
- ctrl->dma_pending = false;
- flash_dma_writel(ctrl, FLASH_DMA_CTRL, 0); /* force stop */
-}
-
-static int brcmnand_dma_trans(struct brcmnand_host *host, u64 addr, u32 *buf,
- u32 len, u8 dma_cmd)
-{
- struct brcmnand_controller *ctrl = host->ctrl;
- dma_addr_t buf_pa;
- int dir = dma_cmd == CMD_PAGE_READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
-
- buf_pa = dma_map_single(ctrl->dev, buf, len, dir);
- if (dma_mapping_error(ctrl->dev, buf_pa)) {
- dev_err(ctrl->dev, "unable to map buffer for DMA\n");
- return -ENOMEM;
- }
-
- brcmnand_fill_dma_desc(host, ctrl->dma_desc, addr, buf_pa, len,
- dma_cmd, true, true, 0);
-
- brcmnand_dma_run(host, ctrl->dma_pa);
-
- dma_unmap_single(ctrl->dev, buf_pa, len, dir);
-
- if (ctrl->dma_desc->status_valid & FLASH_DMA_ECC_ERROR)
- return -EBADMSG;
- else if (ctrl->dma_desc->status_valid & FLASH_DMA_CORR_ERROR)
- return -EUCLEAN;
-
- return 0;
-}
-
-/*
- * Assumes proper CS is already set
- */
-static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip,
- u64 addr, unsigned int trans, u32 *buf,
- u8 *oob, u64 *err_addr)
-{
- struct brcmnand_host *host = nand_get_controller_data(chip);
- struct brcmnand_controller *ctrl = host->ctrl;
- int i, j, ret = 0;
-
- /* Clear error addresses */
- brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_ADDR, 0);
- brcmnand_write_reg(ctrl, BRCMNAND_CORR_ADDR, 0);
- brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_EXT_ADDR, 0);
- brcmnand_write_reg(ctrl, BRCMNAND_CORR_EXT_ADDR, 0);
-
- brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS,
- (host->cs << 16) | ((addr >> 32) & 0xffff));
- (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS);
-
- for (i = 0; i < trans; i++, addr += FC_BYTES) {
- brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS,
- lower_32_bits(addr));
- (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS);
- /* SPARE_AREA_READ does not use ECC, so just use PAGE_READ */
- brcmnand_send_cmd(host, CMD_PAGE_READ);
- brcmnand_waitfunc(mtd, chip);
-
- if (likely(buf)) {
- brcmnand_soc_data_bus_prepare(ctrl->soc, false);
-
- for (j = 0; j < FC_WORDS; j++, buf++)
- *buf = brcmnand_read_fc(ctrl, j);
-
- brcmnand_soc_data_bus_unprepare(ctrl->soc, false);
- }
-
- if (oob)
- oob += read_oob_from_regs(ctrl, i, oob,
- mtd->oobsize / trans,
- host->hwcfg.sector_size_1k);
-
- if (!ret) {
- *err_addr = brcmnand_read_reg(ctrl,
- BRCMNAND_UNCORR_ADDR) |
- ((u64)(brcmnand_read_reg(ctrl,
- BRCMNAND_UNCORR_EXT_ADDR)
- & 0xffff) << 32);
- if (*err_addr)
- ret = -EBADMSG;
- }
-
- if (!ret) {
- *err_addr = brcmnand_read_reg(ctrl,
- BRCMNAND_CORR_ADDR) |
- ((u64)(brcmnand_read_reg(ctrl,
- BRCMNAND_CORR_EXT_ADDR)
- & 0xffff) << 32);
- if (*err_addr)
- ret = -EUCLEAN;
- }
- }
-
- return ret;
-}
-
-/*
- * Check a page to see if it is erased (w/ bitflips) after an uncorrectable ECC
- * error
- *
- * Because the HW ECC signals an ECC error if an erase paged has even a single
- * bitflip, we must check each ECC error to see if it is actually an erased
- * page with bitflips, not a truly corrupted page.
- *
- * On a real error, return a negative error code (-EBADMSG for ECC error), and
- * buf will contain raw data.
- * Otherwise, buf gets filled with 0xffs and return the maximum number of
- * bitflips-per-ECC-sector to the caller.
- *
- */
-static int brcmstb_nand_verify_erased_page(struct mtd_info *mtd,
- struct nand_chip *chip, void *buf, u64 addr)
-{
- int i, sas;
- void *oob = chip->oob_poi;
- int bitflips = 0;
- int page = addr >> chip->page_shift;
- int ret;
-
- if (!buf) {
- buf = chip->data_buf;
- /* Invalidate page cache */
- chip->pagebuf = -1;
- }
-
- sas = mtd->oobsize / chip->ecc.steps;
-
- /* read without ecc for verification */
- ret = chip->ecc.read_page_raw(mtd, chip, buf, true, page);
- if (ret)
- return ret;
-
- for (i = 0; i < chip->ecc.steps; i++, oob += sas) {
- ret = nand_check_erased_ecc_chunk(buf, chip->ecc.size,
- oob, sas, NULL, 0,
- chip->ecc.strength);
- if (ret < 0)
- return ret;
-
- bitflips = max(bitflips, ret);
- }
-
- return bitflips;
-}
-
-static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip,
- u64 addr, unsigned int trans, u32 *buf, u8 *oob)
-{
- struct brcmnand_host *host = nand_get_controller_data(chip);
- struct brcmnand_controller *ctrl = host->ctrl;
- u64 err_addr = 0;
- int err;
- bool retry = true;
-
- dev_dbg(ctrl->dev, "read %llx -> %p\n", (unsigned long long)addr, buf);
-
-try_dmaread:
- brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_COUNT, 0);
-
- if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) {
- err = brcmnand_dma_trans(host, addr, buf, trans * FC_BYTES,
- CMD_PAGE_READ);
- if (err) {
- if (mtd_is_bitflip_or_eccerr(err))
- err_addr = addr;
- else
- return -EIO;
- }
- } else {
- if (oob)
- memset(oob, 0x99, mtd->oobsize);
-
- err = brcmnand_read_by_pio(mtd, chip, addr, trans, buf,
- oob, &err_addr);
- }
-
- if (mtd_is_eccerr(err)) {
- /*
- * On controller version and 7.0, 7.1 , DMA read after a
- * prior PIO read that reported uncorrectable error,
- * the DMA engine captures this error following DMA read
- * cleared only on subsequent DMA read, so just retry once
- * to clear a possible false error reported for current DMA
- * read
- */
- if ((ctrl->nand_version == 0x0700) ||
- (ctrl->nand_version == 0x0701)) {
- if (retry) {
- retry = false;
- goto try_dmaread;
- }
- }
-
- /*
- * Controller version 7.2 has hw encoder to detect erased page
- * bitflips, apply sw verification for older controllers only
- */
- if (ctrl->nand_version < 0x0702) {
- err = brcmstb_nand_verify_erased_page(mtd, chip, buf,
- addr);
- /* erased page bitflips corrected */
- if (err >= 0)
- return err;
- }
-
- dev_dbg(ctrl->dev, "uncorrectable error at 0x%llx\n",
- (unsigned long long)err_addr);
- mtd->ecc_stats.failed++;
- /* NAND layer expects zero on ECC errors */
- return 0;
- }
-
- if (mtd_is_bitflip(err)) {
- unsigned int corrected = brcmnand_count_corrected(ctrl);
-
- dev_dbg(ctrl->dev, "corrected error at 0x%llx\n",
- (unsigned long long)err_addr);
- mtd->ecc_stats.corrected += corrected;
- /* Always exceed the software-imposed threshold */
- return max(mtd->bitflip_threshold, corrected);
- }
-
- return 0;
-}
-
-static int brcmnand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page)
-{
- struct brcmnand_host *host = nand_get_controller_data(chip);
- u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL;
-
- nand_read_page_op(chip, page, 0, NULL, 0);
-
- return brcmnand_read(mtd, chip, host->last_addr,
- mtd->writesize >> FC_SHIFT, (u32 *)buf, oob);
-}
-
-static int brcmnand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page)
-{
- struct brcmnand_host *host = nand_get_controller_data(chip);
- u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL;
- int ret;
-
- nand_read_page_op(chip, page, 0, NULL, 0);
-
- brcmnand_set_ecc_enabled(host, 0);
- ret = brcmnand_read(mtd, chip, host->last_addr,
- mtd->writesize >> FC_SHIFT, (u32 *)buf, oob);
- brcmnand_set_ecc_enabled(host, 1);
- return ret;
-}
-
-static int brcmnand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
- int page)
-{
- return brcmnand_read(mtd, chip, (u64)page << chip->page_shift,
- mtd->writesize >> FC_SHIFT,
- NULL, (u8 *)chip->oob_poi);
-}
-
-static int brcmnand_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
- int page)
-{
- struct brcmnand_host *host = nand_get_controller_data(chip);
-
- brcmnand_set_ecc_enabled(host, 0);
- brcmnand_read(mtd, chip, (u64)page << chip->page_shift,
- mtd->writesize >> FC_SHIFT,
- NULL, (u8 *)chip->oob_poi);
- brcmnand_set_ecc_enabled(host, 1);
- return 0;
-}
-
-static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip,
- u64 addr, const u32 *buf, u8 *oob)
-{
- struct brcmnand_host *host = nand_get_controller_data(chip);
- struct brcmnand_controller *ctrl = host->ctrl;
- unsigned int i, j, trans = mtd->writesize >> FC_SHIFT;
- int status, ret = 0;
-
- dev_dbg(ctrl->dev, "write %llx <- %p\n", (unsigned long long)addr, buf);
-
- if (unlikely((unsigned long)buf & 0x03)) {
- dev_warn(ctrl->dev, "unaligned buffer: %p\n", buf);
- buf = (u32 *)((unsigned long)buf & ~0x03);
- }
-
- brcmnand_wp(mtd, 0);
-
- for (i = 0; i < ctrl->max_oob; i += 4)
- oob_reg_write(ctrl, i, 0xffffffff);
-
- if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) {
- if (brcmnand_dma_trans(host, addr, (u32 *)buf,
- mtd->writesize, CMD_PROGRAM_PAGE))
- ret = -EIO;
- goto out;
- }
-
- brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS,
- (host->cs << 16) | ((addr >> 32) & 0xffff));
- (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS);
-
- for (i = 0; i < trans; i++, addr += FC_BYTES) {
- /* full address MUST be set before populating FC */
- brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS,
- lower_32_bits(addr));
- (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS);
-
- if (buf) {
- brcmnand_soc_data_bus_prepare(ctrl->soc, false);
-
- for (j = 0; j < FC_WORDS; j++, buf++)
- brcmnand_write_fc(ctrl, j, *buf);
-
- brcmnand_soc_data_bus_unprepare(ctrl->soc, false);
- } else if (oob) {
- for (j = 0; j < FC_WORDS; j++)
- brcmnand_write_fc(ctrl, j, 0xffffffff);
- }
-
- if (oob) {
- oob += write_oob_to_regs(ctrl, i, oob,
- mtd->oobsize / trans,
- host->hwcfg.sector_size_1k);
- }
-
- /* we cannot use SPARE_AREA_PROGRAM when PARTIAL_PAGE_EN=0 */
- brcmnand_send_cmd(host, CMD_PROGRAM_PAGE);
- status = brcmnand_waitfunc(mtd, chip);
-
- if (status & NAND_STATUS_FAIL) {
- dev_info(ctrl->dev, "program failed at %llx\n",
- (unsigned long long)addr);
- ret = -EIO;
- goto out;
- }
- }
-out:
- brcmnand_wp(mtd, 1);
- return ret;
-}
-
-static int brcmnand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf, int oob_required, int page)
-{
- struct brcmnand_host *host = nand_get_controller_data(chip);
- void *oob = oob_required ? chip->oob_poi : NULL;
-
- nand_prog_page_begin_op(chip, page, 0, NULL, 0);
- brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob);
-
- return nand_prog_page_end_op(chip);
-}
-
-static int brcmnand_write_page_raw(struct mtd_info *mtd,
- struct nand_chip *chip, const uint8_t *buf,
- int oob_required, int page)
-{
- struct brcmnand_host *host = nand_get_controller_data(chip);
- void *oob = oob_required ? chip->oob_poi : NULL;
-
- nand_prog_page_begin_op(chip, page, 0, NULL, 0);
- brcmnand_set_ecc_enabled(host, 0);
- brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob);
- brcmnand_set_ecc_enabled(host, 1);
-
- return nand_prog_page_end_op(chip);
-}
-
-static int brcmnand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
- int page)
-{
- return brcmnand_write(mtd, chip, (u64)page << chip->page_shift,
- NULL, chip->oob_poi);
-}
-
-static int brcmnand_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
- int page)
-{
- struct brcmnand_host *host = nand_get_controller_data(chip);
- int ret;
-
- brcmnand_set_ecc_enabled(host, 0);
- ret = brcmnand_write(mtd, chip, (u64)page << chip->page_shift, NULL,
- (u8 *)chip->oob_poi);
- brcmnand_set_ecc_enabled(host, 1);
-
- return ret;
-}
-
-/***********************************************************************
- * Per-CS setup (1 NAND device)
- ***********************************************************************/
-
-static int brcmnand_set_cfg(struct brcmnand_host *host,
- struct brcmnand_cfg *cfg)
-{
- struct brcmnand_controller *ctrl = host->ctrl;
- struct nand_chip *chip = &host->chip;
- u16 cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG);
- u16 cfg_ext_offs = brcmnand_cs_offset(ctrl, host->cs,
- BRCMNAND_CS_CFG_EXT);
- u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
- BRCMNAND_CS_ACC_CONTROL);
- u8 block_size = 0, page_size = 0, device_size = 0;
- u32 tmp;
-
- if (ctrl->block_sizes) {
- int i, found;
-
- for (i = 0, found = 0; ctrl->block_sizes[i]; i++)
- if (ctrl->block_sizes[i] * 1024 == cfg->block_size) {
- block_size = i;
- found = 1;
- }
- if (!found) {
- dev_warn(ctrl->dev, "invalid block size %u\n",
- cfg->block_size);
- return -EINVAL;
- }
- } else {
- block_size = ffs(cfg->block_size) - ffs(BRCMNAND_MIN_BLOCKSIZE);
- }
-
- if (cfg->block_size < BRCMNAND_MIN_BLOCKSIZE || (ctrl->max_block_size &&
- cfg->block_size > ctrl->max_block_size)) {
- dev_warn(ctrl->dev, "invalid block size %u\n",
- cfg->block_size);
- block_size = 0;
- }
-
- if (ctrl->page_sizes) {
- int i, found;
-
- for (i = 0, found = 0; ctrl->page_sizes[i]; i++)
- if (ctrl->page_sizes[i] == cfg->page_size) {
- page_size = i;
- found = 1;
- }
- if (!found) {
- dev_warn(ctrl->dev, "invalid page size %u\n",
- cfg->page_size);
- return -EINVAL;
- }
- } else {
- page_size = ffs(cfg->page_size) - ffs(BRCMNAND_MIN_PAGESIZE);
- }
-
- if (cfg->page_size < BRCMNAND_MIN_PAGESIZE || (ctrl->max_page_size &&
- cfg->page_size > ctrl->max_page_size)) {
- dev_warn(ctrl->dev, "invalid page size %u\n", cfg->page_size);
- return -EINVAL;
- }
-
- if (fls64(cfg->device_size) < fls64(BRCMNAND_MIN_DEVSIZE)) {
- dev_warn(ctrl->dev, "invalid device size 0x%llx\n",
- (unsigned long long)cfg->device_size);
- return -EINVAL;
- }
- device_size = fls64(cfg->device_size) - fls64(BRCMNAND_MIN_DEVSIZE);
-
- tmp = (cfg->blk_adr_bytes << CFG_BLK_ADR_BYTES_SHIFT) |
- (cfg->col_adr_bytes << CFG_COL_ADR_BYTES_SHIFT) |
- (cfg->ful_adr_bytes << CFG_FUL_ADR_BYTES_SHIFT) |
- (!!(cfg->device_width == 16) << CFG_BUS_WIDTH_SHIFT) |
- (device_size << CFG_DEVICE_SIZE_SHIFT);
- if (cfg_offs == cfg_ext_offs) {
- tmp |= (page_size << CFG_PAGE_SIZE_SHIFT) |
- (block_size << CFG_BLK_SIZE_SHIFT);
- nand_writereg(ctrl, cfg_offs, tmp);
- } else {
- nand_writereg(ctrl, cfg_offs, tmp);
- tmp = (page_size << CFG_EXT_PAGE_SIZE_SHIFT) |
- (block_size << CFG_EXT_BLK_SIZE_SHIFT);
- nand_writereg(ctrl, cfg_ext_offs, tmp);
- }
-
- tmp = nand_readreg(ctrl, acc_control_offs);
- tmp &= ~brcmnand_ecc_level_mask(ctrl);
- tmp |= cfg->ecc_level << NAND_ACC_CONTROL_ECC_SHIFT;
- tmp &= ~brcmnand_spare_area_mask(ctrl);
- tmp |= cfg->spare_area_size;
- nand_writereg(ctrl, acc_control_offs, tmp);
-
- brcmnand_set_sector_size_1k(host, cfg->sector_size_1k);
-
- /* threshold = ceil(BCH-level * 0.75) */
- brcmnand_wr_corr_thresh(host, DIV_ROUND_UP(chip->ecc.strength * 3, 4));
-
- return 0;
-}
-
-static void brcmnand_print_cfg(struct brcmnand_host *host,
- char *buf, struct brcmnand_cfg *cfg)
-{
- buf += sprintf(buf,
- "%lluMiB total, %uKiB blocks, %u%s pages, %uB OOB, %u-bit",
- (unsigned long long)cfg->device_size >> 20,
- cfg->block_size >> 10,
- cfg->page_size >= 1024 ? cfg->page_size >> 10 : cfg->page_size,
- cfg->page_size >= 1024 ? "KiB" : "B",
- cfg->spare_area_size, cfg->device_width);
-
- /* Account for Hamming ECC and for BCH 512B vs 1KiB sectors */
- if (is_hamming_ecc(host->ctrl, cfg))
- sprintf(buf, ", Hamming ECC");
- else if (cfg->sector_size_1k)
- sprintf(buf, ", BCH-%u (1KiB sector)", cfg->ecc_level << 1);
- else
- sprintf(buf, ", BCH-%u", cfg->ecc_level);
-}
-
-/*
- * Minimum number of bytes to address a page. Calculated as:
- * roundup(log2(size / page-size) / 8)
- *
- * NB: the following does not "round up" for non-power-of-2 'size'; but this is
- * OK because many other things will break if 'size' is irregular...
- */
-static inline int get_blk_adr_bytes(u64 size, u32 writesize)
-{
- return ALIGN(ilog2(size) - ilog2(writesize), 8) >> 3;
-}
-
-static int brcmnand_setup_dev(struct brcmnand_host *host)
-{
- struct mtd_info *mtd = nand_to_mtd(&host->chip);
- struct nand_chip *chip = &host->chip;
- struct brcmnand_controller *ctrl = host->ctrl;
- struct brcmnand_cfg *cfg = &host->hwcfg;
- char msg[128];
- u32 offs, tmp, oob_sector;
- int ret;
-
- memset(cfg, 0, sizeof(*cfg));
-
- ret = of_property_read_u32(nand_get_flash_node(chip),
- "brcm,nand-oob-sector-size",
- &oob_sector);
- if (ret) {
- /* Use detected size */
- cfg->spare_area_size = mtd->oobsize /
- (mtd->writesize >> FC_SHIFT);
- } else {
- cfg->spare_area_size = oob_sector;
- }
- if (cfg->spare_area_size > ctrl->max_oob)
- cfg->spare_area_size = ctrl->max_oob;
- /*
- * Set oobsize to be consistent with controller's spare_area_size, as
- * the rest is inaccessible.
- */
- mtd->oobsize = cfg->spare_area_size * (mtd->writesize >> FC_SHIFT);
-
- cfg->device_size = mtd->size;
- cfg->block_size = mtd->erasesize;
- cfg->page_size = mtd->writesize;
- cfg->device_width = (chip->options & NAND_BUSWIDTH_16) ? 16 : 8;
- cfg->col_adr_bytes = 2;
- cfg->blk_adr_bytes = get_blk_adr_bytes(mtd->size, mtd->writesize);
-
- if (chip->ecc.mode != NAND_ECC_HW) {
- dev_err(ctrl->dev, "only HW ECC supported; selected: %d\n",
- chip->ecc.mode);
- return -EINVAL;
- }
-
- if (chip->ecc.algo == NAND_ECC_UNKNOWN) {
- if (chip->ecc.strength == 1 && chip->ecc.size == 512)
- /* Default to Hamming for 1-bit ECC, if unspecified */
- chip->ecc.algo = NAND_ECC_HAMMING;
- else
- /* Otherwise, BCH */
- chip->ecc.algo = NAND_ECC_BCH;
- }
-
- if (chip->ecc.algo == NAND_ECC_HAMMING && (chip->ecc.strength != 1 ||
- chip->ecc.size != 512)) {
- dev_err(ctrl->dev, "invalid Hamming params: %d bits per %d bytes\n",
- chip->ecc.strength, chip->ecc.size);
- return -EINVAL;
- }
-
- switch (chip->ecc.size) {
- case 512:
- if (chip->ecc.algo == NAND_ECC_HAMMING)
- cfg->ecc_level = 15;
- else
- cfg->ecc_level = chip->ecc.strength;
- cfg->sector_size_1k = 0;
- break;
- case 1024:
- if (!(ctrl->features & BRCMNAND_HAS_1K_SECTORS)) {
- dev_err(ctrl->dev, "1KB sectors not supported\n");
- return -EINVAL;
- }
- if (chip->ecc.strength & 0x1) {
- dev_err(ctrl->dev,
- "odd ECC not supported with 1KB sectors\n");
- return -EINVAL;
- }
-
- cfg->ecc_level = chip->ecc.strength >> 1;
- cfg->sector_size_1k = 1;
- break;
- default:
- dev_err(ctrl->dev, "unsupported ECC size: %d\n",
- chip->ecc.size);
- return -EINVAL;
- }
-
- cfg->ful_adr_bytes = cfg->blk_adr_bytes;
- if (mtd->writesize > 512)
- cfg->ful_adr_bytes += cfg->col_adr_bytes;
- else
- cfg->ful_adr_bytes += 1;
-
- ret = brcmnand_set_cfg(host, cfg);
- if (ret)
- return ret;
-
- brcmnand_set_ecc_enabled(host, 1);
-
- brcmnand_print_cfg(host, msg, cfg);
- dev_info(ctrl->dev, "detected %s\n", msg);
-
- /* Configure ACC_CONTROL */
- offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL);
- tmp = nand_readreg(ctrl, offs);
- tmp &= ~ACC_CONTROL_PARTIAL_PAGE;
- tmp &= ~ACC_CONTROL_RD_ERASED;
-
- /* We need to turn on Read from erased paged protected by ECC */
- if (ctrl->nand_version >= 0x0702)
- tmp |= ACC_CONTROL_RD_ERASED;
- tmp &= ~ACC_CONTROL_FAST_PGM_RDIN;
- if (ctrl->features & BRCMNAND_HAS_PREFETCH)
- tmp &= ~ACC_CONTROL_PREFETCH;
-
- nand_writereg(ctrl, offs, tmp);
-
- return 0;
-}
-
-static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn)
-{
- struct brcmnand_controller *ctrl = host->ctrl;
- struct platform_device *pdev = host->pdev;
- struct mtd_info *mtd;
- struct nand_chip *chip;
- int ret;
- u16 cfg_offs;
-
- ret = of_property_read_u32(dn, "reg", &host->cs);
- if (ret) {
- dev_err(&pdev->dev, "can't get chip-select\n");
- return -ENXIO;
- }
-
- mtd = nand_to_mtd(&host->chip);
- chip = &host->chip;
-
- nand_set_flash_node(chip, dn);
- nand_set_controller_data(chip, host);
- mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "brcmnand.%d",
- host->cs);
- if (!mtd->name)
- return -ENOMEM;
-
- mtd->owner = THIS_MODULE;
- mtd->dev.parent = &pdev->dev;
-
- chip->IO_ADDR_R = (void __iomem *)0xdeadbeef;
- chip->IO_ADDR_W = (void __iomem *)0xdeadbeef;
-
- chip->cmd_ctrl = brcmnand_cmd_ctrl;
- chip->cmdfunc = brcmnand_cmdfunc;
- chip->waitfunc = brcmnand_waitfunc;
- chip->read_byte = brcmnand_read_byte;
- chip->read_buf = brcmnand_read_buf;
- chip->write_buf = brcmnand_write_buf;
-
- chip->ecc.mode = NAND_ECC_HW;
- chip->ecc.read_page = brcmnand_read_page;
- chip->ecc.write_page = brcmnand_write_page;
- chip->ecc.read_page_raw = brcmnand_read_page_raw;
- chip->ecc.write_page_raw = brcmnand_write_page_raw;
- chip->ecc.write_oob_raw = brcmnand_write_oob_raw;
- chip->ecc.read_oob_raw = brcmnand_read_oob_raw;
- chip->ecc.read_oob = brcmnand_read_oob;
- chip->ecc.write_oob = brcmnand_write_oob;
-
- chip->controller = &ctrl->controller;
-
- /*
- * The bootloader might have configured 16bit mode but
- * NAND READID command only works in 8bit mode. We force
- * 8bit mode here to ensure that NAND READID commands works.
- */
- cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG);
- nand_writereg(ctrl, cfg_offs,
- nand_readreg(ctrl, cfg_offs) & ~CFG_BUS_WIDTH);
-
- ret = nand_scan_ident(mtd, 1, NULL);
- if (ret)
- return ret;
-
- chip->options |= NAND_NO_SUBPAGE_WRITE;
- /*
- * Avoid (for instance) kmap()'d buffers from JFFS2, which we can't DMA
- * to/from, and have nand_base pass us a bounce buffer instead, as
- * needed.
- */
- chip->options |= NAND_USE_BOUNCE_BUFFER;
-
- if (chip->bbt_options & NAND_BBT_USE_FLASH)
- chip->bbt_options |= NAND_BBT_NO_OOB;
-
- if (brcmnand_setup_dev(host))
- return -ENXIO;
-
- chip->ecc.size = host->hwcfg.sector_size_1k ? 1024 : 512;
- /* only use our internal HW threshold */
- mtd->bitflip_threshold = 1;
-
- ret = brcmstb_choose_ecc_layout(host);
- if (ret)
- return ret;
-
- ret = nand_scan_tail(mtd);
- if (ret)
- return ret;
-
- return mtd_device_register(mtd, NULL, 0);
-}
-
-static void brcmnand_save_restore_cs_config(struct brcmnand_host *host,
- int restore)
-{
- struct brcmnand_controller *ctrl = host->ctrl;
- u16 cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG);
- u16 cfg_ext_offs = brcmnand_cs_offset(ctrl, host->cs,
- BRCMNAND_CS_CFG_EXT);
- u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
- BRCMNAND_CS_ACC_CONTROL);
- u16 t1_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING1);
- u16 t2_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING2);
-
- if (restore) {
- nand_writereg(ctrl, cfg_offs, host->hwcfg.config);
- if (cfg_offs != cfg_ext_offs)
- nand_writereg(ctrl, cfg_ext_offs,
- host->hwcfg.config_ext);
- nand_writereg(ctrl, acc_control_offs, host->hwcfg.acc_control);
- nand_writereg(ctrl, t1_offs, host->hwcfg.timing_1);
- nand_writereg(ctrl, t2_offs, host->hwcfg.timing_2);
- } else {
- host->hwcfg.config = nand_readreg(ctrl, cfg_offs);
- if (cfg_offs != cfg_ext_offs)
- host->hwcfg.config_ext =
- nand_readreg(ctrl, cfg_ext_offs);
- host->hwcfg.acc_control = nand_readreg(ctrl, acc_control_offs);
- host->hwcfg.timing_1 = nand_readreg(ctrl, t1_offs);
- host->hwcfg.timing_2 = nand_readreg(ctrl, t2_offs);
- }
-}
-
-static int brcmnand_suspend(struct device *dev)
-{
- struct brcmnand_controller *ctrl = dev_get_drvdata(dev);
- struct brcmnand_host *host;
-
- list_for_each_entry(host, &ctrl->host_list, node)
- brcmnand_save_restore_cs_config(host, 0);
-
- ctrl->nand_cs_nand_select = brcmnand_read_reg(ctrl, BRCMNAND_CS_SELECT);
- ctrl->nand_cs_nand_xor = brcmnand_read_reg(ctrl, BRCMNAND_CS_XOR);
- ctrl->corr_stat_threshold =
- brcmnand_read_reg(ctrl, BRCMNAND_CORR_THRESHOLD);
-
- if (has_flash_dma(ctrl))
- ctrl->flash_dma_mode = flash_dma_readl(ctrl, FLASH_DMA_MODE);
-
- return 0;
-}
-
-static int brcmnand_resume(struct device *dev)
-{
- struct brcmnand_controller *ctrl = dev_get_drvdata(dev);
- struct brcmnand_host *host;
-
- if (has_flash_dma(ctrl)) {
- flash_dma_writel(ctrl, FLASH_DMA_MODE, ctrl->flash_dma_mode);
- flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0);
- }
-
- brcmnand_write_reg(ctrl, BRCMNAND_CS_SELECT, ctrl->nand_cs_nand_select);
- brcmnand_write_reg(ctrl, BRCMNAND_CS_XOR, ctrl->nand_cs_nand_xor);
- brcmnand_write_reg(ctrl, BRCMNAND_CORR_THRESHOLD,
- ctrl->corr_stat_threshold);
- if (ctrl->soc) {
- /* Clear/re-enable interrupt */
- ctrl->soc->ctlrdy_ack(ctrl->soc);
- ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true);
- }
-
- list_for_each_entry(host, &ctrl->host_list, node) {
- struct nand_chip *chip = &host->chip;
-
- brcmnand_save_restore_cs_config(host, 1);
-
- /* Reset the chip, required by some chips after power-up */
- nand_reset_op(chip);
- }
-
- return 0;
-}
-
-const struct dev_pm_ops brcmnand_pm_ops = {
- .suspend = brcmnand_suspend,
- .resume = brcmnand_resume,
-};
-EXPORT_SYMBOL_GPL(brcmnand_pm_ops);
-
-static const struct of_device_id brcmnand_of_match[] = {
- { .compatible = "brcm,brcmnand-v4.0" },
- { .compatible = "brcm,brcmnand-v5.0" },
- { .compatible = "brcm,brcmnand-v6.0" },
- { .compatible = "brcm,brcmnand-v6.1" },
- { .compatible = "brcm,brcmnand-v6.2" },
- { .compatible = "brcm,brcmnand-v7.0" },
- { .compatible = "brcm,brcmnand-v7.1" },
- { .compatible = "brcm,brcmnand-v7.2" },
- {},
-};
-MODULE_DEVICE_TABLE(of, brcmnand_of_match);
-
-/***********************************************************************
- * Platform driver setup (per controller)
- ***********************************************************************/
-
-int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
-{
- struct device *dev = &pdev->dev;
- struct device_node *dn = dev->of_node, *child;
- struct brcmnand_controller *ctrl;
- struct resource *res;
- int ret;
-
- /* We only support device-tree instantiation */
- if (!dn)
- return -ENODEV;
-
- if (!of_match_node(brcmnand_of_match, dn))
- return -ENODEV;
-
- ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
- if (!ctrl)
- return -ENOMEM;
-
- dev_set_drvdata(dev, ctrl);
- ctrl->dev = dev;
-
- init_completion(&ctrl->done);
- init_completion(&ctrl->dma_done);
- nand_hw_control_init(&ctrl->controller);
- INIT_LIST_HEAD(&ctrl->host_list);
-
- /* NAND register range */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- ctrl->nand_base = devm_ioremap_resource(dev, res);
- if (IS_ERR(ctrl->nand_base))
- return PTR_ERR(ctrl->nand_base);
-
- /* Enable clock before using NAND registers */
- ctrl->clk = devm_clk_get(dev, "nand");
- if (!IS_ERR(ctrl->clk)) {
- ret = clk_prepare_enable(ctrl->clk);
- if (ret)
- return ret;
- } else {
- ret = PTR_ERR(ctrl->clk);
- if (ret == -EPROBE_DEFER)
- return ret;
-
- ctrl->clk = NULL;
- }
-
- /* Initialize NAND revision */
- ret = brcmnand_revision_init(ctrl);
- if (ret)
- goto err;
-
- /*
- * Most chips have this cache at a fixed offset within 'nand' block.
- * Some must specify this region separately.
- */
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand-cache");
- if (res) {
- ctrl->nand_fc = devm_ioremap_resource(dev, res);
- if (IS_ERR(ctrl->nand_fc)) {
- ret = PTR_ERR(ctrl->nand_fc);
- goto err;
- }
- } else {
- ctrl->nand_fc = ctrl->nand_base +
- ctrl->reg_offsets[BRCMNAND_FC_BASE];
- }
-
- /* FLASH_DMA */
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "flash-dma");
- if (res) {
- ctrl->flash_dma_base = devm_ioremap_resource(dev, res);
- if (IS_ERR(ctrl->flash_dma_base)) {
- ret = PTR_ERR(ctrl->flash_dma_base);
- goto err;
- }
-
- flash_dma_writel(ctrl, FLASH_DMA_MODE, 1); /* linked-list */
- flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0);
-
- /* Allocate descriptor(s) */
- ctrl->dma_desc = dmam_alloc_coherent(dev,
- sizeof(*ctrl->dma_desc),
- &ctrl->dma_pa, GFP_KERNEL);
- if (!ctrl->dma_desc) {
- ret = -ENOMEM;
- goto err;
- }
-
- ctrl->dma_irq = platform_get_irq(pdev, 1);
- if ((int)ctrl->dma_irq < 0) {
- dev_err(dev, "missing FLASH_DMA IRQ\n");
- ret = -ENODEV;
- goto err;
- }
-
- ret = devm_request_irq(dev, ctrl->dma_irq,
- brcmnand_dma_irq, 0, DRV_NAME,
- ctrl);
- if (ret < 0) {
- dev_err(dev, "can't allocate IRQ %d: error %d\n",
- ctrl->dma_irq, ret);
- goto err;
- }
-
- dev_info(dev, "enabling FLASH_DMA\n");
- }
-
- /* Disable automatic device ID config, direct addressing */
- brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT,
- CS_SELECT_AUTO_DEVICE_ID_CFG | 0xff, 0, 0);
- /* Disable XOR addressing */
- brcmnand_rmw_reg(ctrl, BRCMNAND_CS_XOR, 0xff, 0, 0);
-
- if (ctrl->features & BRCMNAND_HAS_WP) {
- /* Permanently disable write protection */
- if (wp_on == 2)
- brcmnand_set_wp(ctrl, false);
- } else {
- wp_on = 0;
- }
-
- /* IRQ */
- ctrl->irq = platform_get_irq(pdev, 0);
- if ((int)ctrl->irq < 0) {
- dev_err(dev, "no IRQ defined\n");
- ret = -ENODEV;
- goto err;
- }
-
- /*
- * Some SoCs integrate this controller (e.g., its interrupt bits) in
- * interesting ways
- */
- if (soc) {
- ctrl->soc = soc;
-
- ret = devm_request_irq(dev, ctrl->irq, brcmnand_irq, 0,
- DRV_NAME, ctrl);
-
- /* Enable interrupt */
- ctrl->soc->ctlrdy_ack(ctrl->soc);
- ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true);
- } else {
- /* Use standard interrupt infrastructure */
- ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0,
- DRV_NAME, ctrl);
- }
- if (ret < 0) {
- dev_err(dev, "can't allocate IRQ %d: error %d\n",
- ctrl->irq, ret);
- goto err;
- }
-
- for_each_available_child_of_node(dn, child) {
- if (of_device_is_compatible(child, "brcm,nandcs")) {
- struct brcmnand_host *host;
-
- host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
- if (!host) {
- of_node_put(child);
- ret = -ENOMEM;
- goto err;
- }
- host->pdev = pdev;
- host->ctrl = ctrl;
-
- ret = brcmnand_init_cs(host, child);
- if (ret) {
- devm_kfree(dev, host);
- continue; /* Try all chip-selects */
- }
-
- list_add_tail(&host->node, &ctrl->host_list);
- }
- }
-
- /* No chip-selects could initialize properly */
- if (list_empty(&ctrl->host_list)) {
- ret = -ENODEV;
- goto err;
- }
-
- return 0;
-
-err:
- clk_disable_unprepare(ctrl->clk);
- return ret;
-
-}
-EXPORT_SYMBOL_GPL(brcmnand_probe);
-
-int brcmnand_remove(struct platform_device *pdev)
-{
- struct brcmnand_controller *ctrl = dev_get_drvdata(&pdev->dev);
- struct brcmnand_host *host;
-
- list_for_each_entry(host, &ctrl->host_list, node)
- nand_release(nand_to_mtd(&host->chip));
-
- clk_disable_unprepare(ctrl->clk);
-
- dev_set_drvdata(&pdev->dev, NULL);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(brcmnand_remove);
-
-MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Kevin Cernekee");
-MODULE_AUTHOR("Brian Norris");
-MODULE_DESCRIPTION("NAND driver for Broadcom chips");
-MODULE_ALIAS("platform:brcmnand");
diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c
deleted file mode 100644
index 567ff972d5fc..000000000000
--- a/drivers/mtd/nand/cafe_nand.c
+++ /dev/null
@@ -1,871 +0,0 @@
-/*
- * Driver for One Laptop Per Child ‘CAFÉ’ controller, aka Marvell 88ALP01
- *
- * The data sheet for this device can be found at:
- * http://wiki.laptop.org/go/Datasheets
- *
- * Copyright © 2006 Red Hat, Inc.
- * Copyright © 2006 David Woodhouse <dwmw2@infradead.org>
- */
-
-#define DEBUG
-
-#include <linux/device.h>
-#undef DEBUG
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/partitions.h>
-#include <linux/rslib.h>
-#include <linux/pci.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/dma-mapping.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <asm/io.h>
-
-#define CAFE_NAND_CTRL1 0x00
-#define CAFE_NAND_CTRL2 0x04
-#define CAFE_NAND_CTRL3 0x08
-#define CAFE_NAND_STATUS 0x0c
-#define CAFE_NAND_IRQ 0x10
-#define CAFE_NAND_IRQ_MASK 0x14
-#define CAFE_NAND_DATA_LEN 0x18
-#define CAFE_NAND_ADDR1 0x1c
-#define CAFE_NAND_ADDR2 0x20
-#define CAFE_NAND_TIMING1 0x24
-#define CAFE_NAND_TIMING2 0x28
-#define CAFE_NAND_TIMING3 0x2c
-#define CAFE_NAND_NONMEM 0x30
-#define CAFE_NAND_ECC_RESULT 0x3C
-#define CAFE_NAND_DMA_CTRL 0x40
-#define CAFE_NAND_DMA_ADDR0 0x44
-#define CAFE_NAND_DMA_ADDR1 0x48
-#define CAFE_NAND_ECC_SYN01 0x50
-#define CAFE_NAND_ECC_SYN23 0x54
-#define CAFE_NAND_ECC_SYN45 0x58
-#define CAFE_NAND_ECC_SYN67 0x5c
-#define CAFE_NAND_READ_DATA 0x1000
-#define CAFE_NAND_WRITE_DATA 0x2000
-
-#define CAFE_GLOBAL_CTRL 0x3004
-#define CAFE_GLOBAL_IRQ 0x3008
-#define CAFE_GLOBAL_IRQ_MASK 0x300c
-#define CAFE_NAND_RESET 0x3034
-
-/* Missing from the datasheet: bit 19 of CTRL1 sets CE0 vs. CE1 */
-#define CTRL1_CHIPSELECT (1<<19)
-
-struct cafe_priv {
- struct nand_chip nand;
- struct pci_dev *pdev;
- void __iomem *mmio;
- struct rs_control *rs;
- uint32_t ctl1;
- uint32_t ctl2;
- int datalen;
- int nr_data;
- int data_pos;
- int page_addr;
- dma_addr_t dmaaddr;
- unsigned char *dmabuf;
-};
-
-static int usedma = 1;
-module_param(usedma, int, 0644);
-
-static int skipbbt = 0;
-module_param(skipbbt, int, 0644);
-
-static int debug = 0;
-module_param(debug, int, 0644);
-
-static int regdebug = 0;
-module_param(regdebug, int, 0644);
-
-static int checkecc = 1;
-module_param(checkecc, int, 0644);
-
-static unsigned int numtimings;
-static int timing[3];
-module_param_array(timing, int, &numtimings, 0644);
-
-static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL };
-
-/* Hrm. Why isn't this already conditional on something in the struct device? */
-#define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0)
-
-/* Make it easier to switch to PIO if we need to */
-#define cafe_readl(cafe, addr) readl((cafe)->mmio + CAFE_##addr)
-#define cafe_writel(cafe, datum, addr) writel(datum, (cafe)->mmio + CAFE_##addr)
-
-static int cafe_device_ready(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct cafe_priv *cafe = nand_get_controller_data(chip);
- int result = !!(cafe_readl(cafe, NAND_STATUS) & 0x40000000);
- uint32_t irqs = cafe_readl(cafe, NAND_IRQ);
-
- cafe_writel(cafe, irqs, NAND_IRQ);
-
- cafe_dev_dbg(&cafe->pdev->dev, "NAND device is%s ready, IRQ %x (%x) (%x,%x)\n",
- result?"":" not", irqs, cafe_readl(cafe, NAND_IRQ),
- cafe_readl(cafe, GLOBAL_IRQ), cafe_readl(cafe, GLOBAL_IRQ_MASK));
-
- return result;
-}
-
-
-static void cafe_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct cafe_priv *cafe = nand_get_controller_data(chip);
-
- if (usedma)
- memcpy(cafe->dmabuf + cafe->datalen, buf, len);
- else
- memcpy_toio(cafe->mmio + CAFE_NAND_WRITE_DATA + cafe->datalen, buf, len);
-
- cafe->datalen += len;
-
- cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes to write buffer. datalen 0x%x\n",
- len, cafe->datalen);
-}
-
-static void cafe_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct cafe_priv *cafe = nand_get_controller_data(chip);
-
- if (usedma)
- memcpy(buf, cafe->dmabuf + cafe->datalen, len);
- else
- memcpy_fromio(buf, cafe->mmio + CAFE_NAND_READ_DATA + cafe->datalen, len);
-
- cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes from position 0x%x in read buffer.\n",
- len, cafe->datalen);
- cafe->datalen += len;
-}
-
-static uint8_t cafe_read_byte(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct cafe_priv *cafe = nand_get_controller_data(chip);
- uint8_t d;
-
- cafe_read_buf(mtd, &d, 1);
- cafe_dev_dbg(&cafe->pdev->dev, "Read %02x\n", d);
-
- return d;
-}
-
-static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
- int column, int page_addr)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct cafe_priv *cafe = nand_get_controller_data(chip);
- int adrbytes = 0;
- uint32_t ctl1;
- uint32_t doneint = 0x80000000;
-
- cafe_dev_dbg(&cafe->pdev->dev, "cmdfunc %02x, 0x%x, 0x%x\n",
- command, column, page_addr);
-
- if (command == NAND_CMD_ERASE2 || command == NAND_CMD_PAGEPROG) {
- /* Second half of a command we already calculated */
- cafe_writel(cafe, cafe->ctl2 | 0x100 | command, NAND_CTRL2);
- ctl1 = cafe->ctl1;
- cafe->ctl2 &= ~(1<<30);
- cafe_dev_dbg(&cafe->pdev->dev, "Continue command, ctl1 %08x, #data %d\n",
- cafe->ctl1, cafe->nr_data);
- goto do_command;
- }
- /* Reset ECC engine */
- cafe_writel(cafe, 0, NAND_CTRL2);
-
- /* Emulate NAND_CMD_READOOB on large-page chips */
- if (mtd->writesize > 512 &&
- command == NAND_CMD_READOOB) {
- column += mtd->writesize;
- command = NAND_CMD_READ0;
- }
-
- /* FIXME: Do we need to send read command before sending data
- for small-page chips, to position the buffer correctly? */
-
- if (column != -1) {
- cafe_writel(cafe, column, NAND_ADDR1);
- adrbytes = 2;
- if (page_addr != -1)
- goto write_adr2;
- } else if (page_addr != -1) {
- cafe_writel(cafe, page_addr & 0xffff, NAND_ADDR1);
- page_addr >>= 16;
- write_adr2:
- cafe_writel(cafe, page_addr, NAND_ADDR2);
- adrbytes += 2;
- if (mtd->size > mtd->writesize << 16)
- adrbytes++;
- }
-
- cafe->data_pos = cafe->datalen = 0;
-
- /* Set command valid bit, mask in the chip select bit */
- ctl1 = 0x80000000 | command | (cafe->ctl1 & CTRL1_CHIPSELECT);
-
- /* Set RD or WR bits as appropriate */
- if (command == NAND_CMD_READID || command == NAND_CMD_STATUS) {
- ctl1 |= (1<<26); /* rd */
- /* Always 5 bytes, for now */
- cafe->datalen = 4;
- /* And one address cycle -- even for STATUS, since the controller doesn't work without */
- adrbytes = 1;
- } else if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 ||
- command == NAND_CMD_READOOB || command == NAND_CMD_RNDOUT) {
- ctl1 |= 1<<26; /* rd */
- /* For now, assume just read to end of page */
- cafe->datalen = mtd->writesize + mtd->oobsize - column;
- } else if (command == NAND_CMD_SEQIN)
- ctl1 |= 1<<25; /* wr */
-
- /* Set number of address bytes */
- if (adrbytes)
- ctl1 |= ((adrbytes-1)|8) << 27;
-
- if (command == NAND_CMD_SEQIN || command == NAND_CMD_ERASE1) {
- /* Ignore the first command of a pair; the hardware
- deals with them both at once, later */
- cafe->ctl1 = ctl1;
- cafe_dev_dbg(&cafe->pdev->dev, "Setup for delayed command, ctl1 %08x, dlen %x\n",
- cafe->ctl1, cafe->datalen);
- return;
- }
- /* RNDOUT and READ0 commands need a following byte */
- if (command == NAND_CMD_RNDOUT)
- cafe_writel(cafe, cafe->ctl2 | 0x100 | NAND_CMD_RNDOUTSTART, NAND_CTRL2);
- else if (command == NAND_CMD_READ0 && mtd->writesize > 512)
- cafe_writel(cafe, cafe->ctl2 | 0x100 | NAND_CMD_READSTART, NAND_CTRL2);
-
- do_command:
- cafe_dev_dbg(&cafe->pdev->dev, "dlen %x, ctl1 %x, ctl2 %x\n",
- cafe->datalen, ctl1, cafe_readl(cafe, NAND_CTRL2));
-
- /* NB: The datasheet lies -- we really should be subtracting 1 here */
- cafe_writel(cafe, cafe->datalen, NAND_DATA_LEN);
- cafe_writel(cafe, 0x90000000, NAND_IRQ);
- if (usedma && (ctl1 & (3<<25))) {
- uint32_t dmactl = 0xc0000000 + cafe->datalen;
- /* If WR or RD bits set, set up DMA */
- if (ctl1 & (1<<26)) {
- /* It's a read */
- dmactl |= (1<<29);
- /* ... so it's done when the DMA is done, not just
- the command. */
- doneint = 0x10000000;
- }
- cafe_writel(cafe, dmactl, NAND_DMA_CTRL);
- }
- cafe->datalen = 0;
-
- if (unlikely(regdebug)) {
- int i;
- printk("About to write command %08x to register 0\n", ctl1);
- for (i=4; i< 0x5c; i+=4)
- printk("Register %x: %08x\n", i, readl(cafe->mmio + i));
- }
-
- cafe_writel(cafe, ctl1, NAND_CTRL1);
- /* Apply this short delay always to ensure that we do wait tWB in
- * any case on any machine. */
- ndelay(100);
-
- if (1) {
- int c;
- uint32_t irqs;
-
- for (c = 500000; c != 0; c--) {
- irqs = cafe_readl(cafe, NAND_IRQ);
- if (irqs & doneint)
- break;
- udelay(1);
- if (!(c % 100000))
- cafe_dev_dbg(&cafe->pdev->dev, "Wait for ready, IRQ %x\n", irqs);
- cpu_relax();
- }
- cafe_writel(cafe, doneint, NAND_IRQ);
- cafe_dev_dbg(&cafe->pdev->dev, "Command %x completed after %d usec, irqs %x (%x)\n",
- command, 500000-c, irqs, cafe_readl(cafe, NAND_IRQ));
- }
-
- WARN_ON(cafe->ctl2 & (1<<30));
-
- switch (command) {
-
- case NAND_CMD_CACHEDPROG:
- case NAND_CMD_PAGEPROG:
- case NAND_CMD_ERASE1:
- case NAND_CMD_ERASE2:
- case NAND_CMD_SEQIN:
- case NAND_CMD_RNDIN:
- case NAND_CMD_STATUS:
- case NAND_CMD_RNDOUT:
- cafe_writel(cafe, cafe->ctl2, NAND_CTRL2);
- return;
- }
- nand_wait_ready(mtd);
- cafe_writel(cafe, cafe->ctl2, NAND_CTRL2);
-}
-
-static void cafe_select_chip(struct mtd_info *mtd, int chipnr)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct cafe_priv *cafe = nand_get_controller_data(chip);
-
- cafe_dev_dbg(&cafe->pdev->dev, "select_chip %d\n", chipnr);
-
- /* Mask the appropriate bit into the stored value of ctl1
- which will be used by cafe_nand_cmdfunc() */
- if (chipnr)
- cafe->ctl1 |= CTRL1_CHIPSELECT;
- else
- cafe->ctl1 &= ~CTRL1_CHIPSELECT;
-}
-
-static irqreturn_t cafe_nand_interrupt(int irq, void *id)
-{
- struct mtd_info *mtd = id;
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct cafe_priv *cafe = nand_get_controller_data(chip);
- uint32_t irqs = cafe_readl(cafe, NAND_IRQ);
- cafe_writel(cafe, irqs & ~0x90000000, NAND_IRQ);
- if (!irqs)
- return IRQ_NONE;
-
- cafe_dev_dbg(&cafe->pdev->dev, "irq, bits %x (%x)\n", irqs, cafe_readl(cafe, NAND_IRQ));
- return IRQ_HANDLED;
-}
-
-static void cafe_nand_bug(struct mtd_info *mtd)
-{
- BUG();
-}
-
-static int cafe_nand_write_oob(struct mtd_info *mtd,
- struct nand_chip *chip, int page)
-{
- return nand_prog_page_op(chip, page, mtd->writesize, chip->oob_poi,
- mtd->oobsize);
-}
-
-/* Don't use -- use nand_read_oob_std for now */
-static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
- int page)
-{
- return nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize);
-}
-/**
- * cafe_nand_read_page_syndrome - [REPLACEABLE] hardware ecc syndrome based page read
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- * @oob_required: caller expects OOB data read to chip->oob_poi
- *
- * The hw generator calculates the error syndrome automatically. Therefore
- * we need a special oob layout and handling.
- */
-static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page)
-{
- struct cafe_priv *cafe = nand_get_controller_data(chip);
- unsigned int max_bitflips = 0;
-
- cafe_dev_dbg(&cafe->pdev->dev, "ECC result %08x SYN1,2 %08x\n",
- cafe_readl(cafe, NAND_ECC_RESULT),
- cafe_readl(cafe, NAND_ECC_SYN01));
-
- nand_read_page_op(chip, page, 0, buf, mtd->writesize);
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
-
- if (checkecc && cafe_readl(cafe, NAND_ECC_RESULT) & (1<<18)) {
- unsigned short syn[8], pat[4];
- int pos[4];
- u8 *oob = chip->oob_poi;
- int i, n;
-
- for (i=0; i<8; i+=2) {
- uint32_t tmp = cafe_readl(cafe, NAND_ECC_SYN01 + (i*2));
- syn[i] = cafe->rs->index_of[tmp & 0xfff];
- syn[i+1] = cafe->rs->index_of[(tmp >> 16) & 0xfff];
- }
-
- n = decode_rs16(cafe->rs, NULL, NULL, 1367, syn, 0, pos, 0,
- pat);
-
- for (i = 0; i < n; i++) {
- int p = pos[i];
-
- /* The 12-bit symbols are mapped to bytes here */
-
- if (p > 1374) {
- /* out of range */
- n = -1374;
- } else if (p == 0) {
- /* high four bits do not correspond to data */
- if (pat[i] > 0xff)
- n = -2048;
- else
- buf[0] ^= pat[i];
- } else if (p == 1365) {
- buf[2047] ^= pat[i] >> 4;
- oob[0] ^= pat[i] << 4;
- } else if (p > 1365) {
- if ((p & 1) == 1) {
- oob[3*p/2 - 2048] ^= pat[i] >> 4;
- oob[3*p/2 - 2047] ^= pat[i] << 4;
- } else {
- oob[3*p/2 - 2049] ^= pat[i] >> 8;
- oob[3*p/2 - 2048] ^= pat[i];
- }
- } else if ((p & 1) == 1) {
- buf[3*p/2] ^= pat[i] >> 4;
- buf[3*p/2 + 1] ^= pat[i] << 4;
- } else {
- buf[3*p/2 - 1] ^= pat[i] >> 8;
- buf[3*p/2] ^= pat[i];
- }
- }
-
- if (n < 0) {
- dev_dbg(&cafe->pdev->dev, "Failed to correct ECC at %08x\n",
- cafe_readl(cafe, NAND_ADDR2) * 2048);
- for (i = 0; i < 0x5c; i += 4)
- printk("Register %x: %08x\n", i, readl(cafe->mmio + i));
- mtd->ecc_stats.failed++;
- } else {
- dev_dbg(&cafe->pdev->dev, "Corrected %d symbol errors\n", n);
- mtd->ecc_stats.corrected += n;
- max_bitflips = max_t(unsigned int, max_bitflips, n);
- }
- }
-
- return max_bitflips;
-}
-
-static int cafe_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- if (section)
- return -ERANGE;
-
- oobregion->offset = 0;
- oobregion->length = chip->ecc.total;
-
- return 0;
-}
-
-static int cafe_ooblayout_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- if (section)
- return -ERANGE;
-
- oobregion->offset = chip->ecc.total;
- oobregion->length = mtd->oobsize - chip->ecc.total;
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops cafe_ooblayout_ops = {
- .ecc = cafe_ooblayout_ecc,
- .free = cafe_ooblayout_free,
-};
-
-/* Ick. The BBT code really ought to be able to work this bit out
- for itself from the above, at least for the 2KiB case */
-static uint8_t cafe_bbt_pattern_2048[] = { 'B', 'b', 't', '0' };
-static uint8_t cafe_mirror_pattern_2048[] = { '1', 't', 'b', 'B' };
-
-static uint8_t cafe_bbt_pattern_512[] = { 0xBB };
-static uint8_t cafe_mirror_pattern_512[] = { 0xBC };
-
-
-static struct nand_bbt_descr cafe_bbt_main_descr_2048 = {
- .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
- | NAND_BBT_2BIT | NAND_BBT_VERSION,
- .offs = 14,
- .len = 4,
- .veroffs = 18,
- .maxblocks = 4,
- .pattern = cafe_bbt_pattern_2048
-};
-
-static struct nand_bbt_descr cafe_bbt_mirror_descr_2048 = {
- .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
- | NAND_BBT_2BIT | NAND_BBT_VERSION,
- .offs = 14,
- .len = 4,
- .veroffs = 18,
- .maxblocks = 4,
- .pattern = cafe_mirror_pattern_2048
-};
-
-static struct nand_bbt_descr cafe_bbt_main_descr_512 = {
- .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
- | NAND_BBT_2BIT | NAND_BBT_VERSION,
- .offs = 14,
- .len = 1,
- .veroffs = 15,
- .maxblocks = 4,
- .pattern = cafe_bbt_pattern_512
-};
-
-static struct nand_bbt_descr cafe_bbt_mirror_descr_512 = {
- .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
- | NAND_BBT_2BIT | NAND_BBT_VERSION,
- .offs = 14,
- .len = 1,
- .veroffs = 15,
- .maxblocks = 4,
- .pattern = cafe_mirror_pattern_512
-};
-
-
-static int cafe_nand_write_page_lowlevel(struct mtd_info *mtd,
- struct nand_chip *chip,
- const uint8_t *buf, int oob_required,
- int page)
-{
- struct cafe_priv *cafe = nand_get_controller_data(chip);
-
- nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);
- chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
-
- /* Set up ECC autogeneration */
- cafe->ctl2 |= (1<<30);
-
- return nand_prog_page_end_op(chip);
-}
-
-static int cafe_nand_block_bad(struct mtd_info *mtd, loff_t ofs)
-{
- return 0;
-}
-
-/* F_2[X]/(X**6+X+1) */
-static unsigned short gf64_mul(u8 a, u8 b)
-{
- u8 c;
- unsigned int i;
-
- c = 0;
- for (i = 0; i < 6; i++) {
- if (a & 1)
- c ^= b;
- a >>= 1;
- b <<= 1;
- if ((b & 0x40) != 0)
- b ^= 0x43;
- }
-
- return c;
-}
-
-/* F_64[X]/(X**2+X+A**-1) with A the generator of F_64[X] */
-static u16 gf4096_mul(u16 a, u16 b)
-{
- u8 ah, al, bh, bl, ch, cl;
-
- ah = a >> 6;
- al = a & 0x3f;
- bh = b >> 6;
- bl = b & 0x3f;
-
- ch = gf64_mul(ah ^ al, bh ^ bl) ^ gf64_mul(al, bl);
- cl = gf64_mul(gf64_mul(ah, bh), 0x21) ^ gf64_mul(al, bl);
-
- return (ch << 6) ^ cl;
-}
-
-static int cafe_mul(int x)
-{
- if (x == 0)
- return 1;
- return gf4096_mul(x, 0xe01);
-}
-
-static int cafe_nand_probe(struct pci_dev *pdev,
- const struct pci_device_id *ent)
-{
- struct mtd_info *mtd;
- struct cafe_priv *cafe;
- uint32_t ctrl;
- int err = 0;
- int old_dma;
-
- /* Very old versions shared the same PCI ident for all three
- functions on the chip. Verify the class too... */
- if ((pdev->class >> 8) != PCI_CLASS_MEMORY_FLASH)
- return -ENODEV;
-
- err = pci_enable_device(pdev);
- if (err)
- return err;
-
- pci_set_master(pdev);
-
- cafe = kzalloc(sizeof(*cafe), GFP_KERNEL);
- if (!cafe)
- return -ENOMEM;
-
- mtd = nand_to_mtd(&cafe->nand);
- mtd->dev.parent = &pdev->dev;
- nand_set_controller_data(&cafe->nand, cafe);
-
- cafe->pdev = pdev;
- cafe->mmio = pci_iomap(pdev, 0, 0);
- if (!cafe->mmio) {
- dev_warn(&pdev->dev, "failed to iomap\n");
- err = -ENOMEM;
- goto out_free_mtd;
- }
-
- cafe->rs = init_rs_non_canonical(12, &cafe_mul, 0, 1, 8);
- if (!cafe->rs) {
- err = -ENOMEM;
- goto out_ior;
- }
-
- cafe->nand.cmdfunc = cafe_nand_cmdfunc;
- cafe->nand.dev_ready = cafe_device_ready;
- cafe->nand.read_byte = cafe_read_byte;
- cafe->nand.read_buf = cafe_read_buf;
- cafe->nand.write_buf = cafe_write_buf;
- cafe->nand.select_chip = cafe_select_chip;
- cafe->nand.onfi_set_features = nand_onfi_get_set_features_notsupp;
- cafe->nand.onfi_get_features = nand_onfi_get_set_features_notsupp;
-
- cafe->nand.chip_delay = 0;
-
- /* Enable the following for a flash based bad block table */
- cafe->nand.bbt_options = NAND_BBT_USE_FLASH;
-
- if (skipbbt) {
- cafe->nand.options |= NAND_SKIP_BBTSCAN;
- cafe->nand.block_bad = cafe_nand_block_bad;
- }
-
- if (numtimings && numtimings != 3) {
- dev_warn(&cafe->pdev->dev, "%d timing register values ignored; precisely three are required\n", numtimings);
- }
-
- if (numtimings == 3) {
- cafe_dev_dbg(&cafe->pdev->dev, "Using provided timings (%08x %08x %08x)\n",
- timing[0], timing[1], timing[2]);
- } else {
- timing[0] = cafe_readl(cafe, NAND_TIMING1);
- timing[1] = cafe_readl(cafe, NAND_TIMING2);
- timing[2] = cafe_readl(cafe, NAND_TIMING3);
-
- if (timing[0] | timing[1] | timing[2]) {
- cafe_dev_dbg(&cafe->pdev->dev, "Timing registers already set (%08x %08x %08x)\n",
- timing[0], timing[1], timing[2]);
- } else {
- dev_warn(&cafe->pdev->dev, "Timing registers unset; using most conservative defaults\n");
- timing[0] = timing[1] = timing[2] = 0xffffffff;
- }
- }
-
- /* Start off by resetting the NAND controller completely */
- cafe_writel(cafe, 1, NAND_RESET);
- cafe_writel(cafe, 0, NAND_RESET);
-
- cafe_writel(cafe, timing[0], NAND_TIMING1);
- cafe_writel(cafe, timing[1], NAND_TIMING2);
- cafe_writel(cafe, timing[2], NAND_TIMING3);
-
- cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK);
- err = request_irq(pdev->irq, &cafe_nand_interrupt, IRQF_SHARED,
- "CAFE NAND", mtd);
- if (err) {
- dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq);
- goto out_ior;
- }
-
- /* Disable master reset, enable NAND clock */
- ctrl = cafe_readl(cafe, GLOBAL_CTRL);
- ctrl &= 0xffffeff0;
- ctrl |= 0x00007000;
- cafe_writel(cafe, ctrl | 0x05, GLOBAL_CTRL);
- cafe_writel(cafe, ctrl | 0x0a, GLOBAL_CTRL);
- cafe_writel(cafe, 0, NAND_DMA_CTRL);
-
- cafe_writel(cafe, 0x7006, GLOBAL_CTRL);
- cafe_writel(cafe, 0x700a, GLOBAL_CTRL);
-
- /* Enable NAND IRQ in global IRQ mask register */
- cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK);
- cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n",
- cafe_readl(cafe, GLOBAL_CTRL),
- cafe_readl(cafe, GLOBAL_IRQ_MASK));
-
- /* Do not use the DMA for the nand_scan_ident() */
- old_dma = usedma;
- usedma = 0;
-
- /* Scan to find existence of the device */
- err = nand_scan_ident(mtd, 2, NULL);
- if (err)
- goto out_irq;
-
- cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev, 2112,
- &cafe->dmaaddr, GFP_KERNEL);
- if (!cafe->dmabuf) {
- err = -ENOMEM;
- goto out_irq;
- }
-
- /* Set up DMA address */
- cafe_writel(cafe, lower_32_bits(cafe->dmaaddr), NAND_DMA_ADDR0);
- cafe_writel(cafe, upper_32_bits(cafe->dmaaddr), NAND_DMA_ADDR1);
-
- cafe_dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n",
- cafe_readl(cafe, NAND_DMA_ADDR0), cafe->dmabuf);
-
- /* Restore the DMA flag */
- usedma = old_dma;
-
- cafe->ctl2 = 1<<27; /* Reed-Solomon ECC */
- if (mtd->writesize == 2048)
- cafe->ctl2 |= 1<<29; /* 2KiB page size */
-
- /* Set up ECC according to the type of chip we found */
- mtd_set_ooblayout(mtd, &cafe_ooblayout_ops);
- if (mtd->writesize == 2048) {
- cafe->nand.bbt_td = &cafe_bbt_main_descr_2048;
- cafe->nand.bbt_md = &cafe_bbt_mirror_descr_2048;
- } else if (mtd->writesize == 512) {
- cafe->nand.bbt_td = &cafe_bbt_main_descr_512;
- cafe->nand.bbt_md = &cafe_bbt_mirror_descr_512;
- } else {
- printk(KERN_WARNING "Unexpected NAND flash writesize %d. Aborting\n",
- mtd->writesize);
- goto out_free_dma;
- }
- cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME;
- cafe->nand.ecc.size = mtd->writesize;
- cafe->nand.ecc.bytes = 14;
- cafe->nand.ecc.strength = 4;
- cafe->nand.ecc.hwctl = (void *)cafe_nand_bug;
- cafe->nand.ecc.calculate = (void *)cafe_nand_bug;
- cafe->nand.ecc.correct = (void *)cafe_nand_bug;
- cafe->nand.ecc.write_page = cafe_nand_write_page_lowlevel;
- cafe->nand.ecc.write_oob = cafe_nand_write_oob;
- cafe->nand.ecc.read_page = cafe_nand_read_page;
- cafe->nand.ecc.read_oob = cafe_nand_read_oob;
-
- err = nand_scan_tail(mtd);
- if (err)
- goto out_free_dma;
-
- pci_set_drvdata(pdev, mtd);
-
- mtd->name = "cafe_nand";
- mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0);
-
- goto out;
-
- out_free_dma:
- dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
- out_irq:
- /* Disable NAND IRQ in global IRQ mask register */
- cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK);
- free_irq(pdev->irq, mtd);
- out_ior:
- pci_iounmap(pdev, cafe->mmio);
- out_free_mtd:
- kfree(cafe);
- out:
- return err;
-}
-
-static void cafe_nand_remove(struct pci_dev *pdev)
-{
- struct mtd_info *mtd = pci_get_drvdata(pdev);
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct cafe_priv *cafe = nand_get_controller_data(chip);
-
- /* Disable NAND IRQ in global IRQ mask register */
- cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK);
- free_irq(pdev->irq, mtd);
- nand_release(mtd);
- free_rs(cafe->rs);
- pci_iounmap(pdev, cafe->mmio);
- dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
- kfree(cafe);
-}
-
-static const struct pci_device_id cafe_nand_tbl[] = {
- { PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_88ALP01_NAND,
- PCI_ANY_ID, PCI_ANY_ID },
- { }
-};
-
-MODULE_DEVICE_TABLE(pci, cafe_nand_tbl);
-
-static int cafe_nand_resume(struct pci_dev *pdev)
-{
- uint32_t ctrl;
- struct mtd_info *mtd = pci_get_drvdata(pdev);
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct cafe_priv *cafe = nand_get_controller_data(chip);
-
- /* Start off by resetting the NAND controller completely */
- cafe_writel(cafe, 1, NAND_RESET);
- cafe_writel(cafe, 0, NAND_RESET);
- cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK);
-
- /* Restore timing configuration */
- cafe_writel(cafe, timing[0], NAND_TIMING1);
- cafe_writel(cafe, timing[1], NAND_TIMING2);
- cafe_writel(cafe, timing[2], NAND_TIMING3);
-
- /* Disable master reset, enable NAND clock */
- ctrl = cafe_readl(cafe, GLOBAL_CTRL);
- ctrl &= 0xffffeff0;
- ctrl |= 0x00007000;
- cafe_writel(cafe, ctrl | 0x05, GLOBAL_CTRL);
- cafe_writel(cafe, ctrl | 0x0a, GLOBAL_CTRL);
- cafe_writel(cafe, 0, NAND_DMA_CTRL);
- cafe_writel(cafe, 0x7006, GLOBAL_CTRL);
- cafe_writel(cafe, 0x700a, GLOBAL_CTRL);
-
- /* Set up DMA address */
- cafe_writel(cafe, cafe->dmaaddr & 0xffffffff, NAND_DMA_ADDR0);
- if (sizeof(cafe->dmaaddr) > 4)
- /* Shift in two parts to shut the compiler up */
- cafe_writel(cafe, (cafe->dmaaddr >> 16) >> 16, NAND_DMA_ADDR1);
- else
- cafe_writel(cafe, 0, NAND_DMA_ADDR1);
-
- /* Enable NAND IRQ in global IRQ mask register */
- cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK);
- return 0;
-}
-
-static struct pci_driver cafe_nand_pci_driver = {
- .name = "CAFÉ NAND",
- .id_table = cafe_nand_tbl,
- .probe = cafe_nand_probe,
- .remove = cafe_nand_remove,
- .resume = cafe_nand_resume,
-};
-
-module_pci_driver(cafe_nand_pci_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
-MODULE_DESCRIPTION("NAND flash driver for OLPC CAFÉ chip");
diff --git a/drivers/mtd/nand/cmx270_nand.c b/drivers/mtd/nand/cmx270_nand.c
deleted file mode 100644
index b01c9804590e..000000000000
--- a/drivers/mtd/nand/cmx270_nand.c
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * linux/drivers/mtd/nand/cmx270-nand.c
- *
- * Copyright (C) 2006 Compulab, Ltd.
- * Mike Rapoport <mike@compulab.co.il>
- *
- * Derived from drivers/mtd/nand/h1910.c
- * Copyright (C) 2002 Marius Gröger (mag@sysgo.de)
- * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
- *
- *
- * 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.
- *
- * Overview:
- * This is a device driver for the NAND flash device found on the
- * CM-X270 board.
- */
-
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/partitions.h>
-#include <linux/slab.h>
-#include <linux/gpio.h>
-#include <linux/module.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/mach-types.h>
-
-#include <mach/pxa2xx-regs.h>
-
-#define GPIO_NAND_CS (11)
-#define GPIO_NAND_RB (89)
-
-/* MTD structure for CM-X270 board */
-static struct mtd_info *cmx270_nand_mtd;
-
-/* remaped IO address of the device */
-static void __iomem *cmx270_nand_io;
-
-/*
- * Define static partitions for flash device
- */
-static const struct mtd_partition partition_info[] = {
- [0] = {
- .name = "cmx270-0",
- .offset = 0,
- .size = MTDPART_SIZ_FULL
- }
-};
-#define NUM_PARTITIONS (ARRAY_SIZE(partition_info))
-
-static u_char cmx270_read_byte(struct mtd_info *mtd)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
-
- return (readl(this->IO_ADDR_R) >> 16);
-}
-
-static void cmx270_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
-{
- int i;
- struct nand_chip *this = mtd_to_nand(mtd);
-
- for (i=0; i<len; i++)
- writel((*buf++ << 16), this->IO_ADDR_W);
-}
-
-static void cmx270_read_buf(struct mtd_info *mtd, u_char *buf, int len)
-{
- int i;
- struct nand_chip *this = mtd_to_nand(mtd);
-
- for (i=0; i<len; i++)
- *buf++ = readl(this->IO_ADDR_R) >> 16;
-}
-
-static inline void nand_cs_on(void)
-{
- gpio_set_value(GPIO_NAND_CS, 0);
-}
-
-static void nand_cs_off(void)
-{
- dsb();
-
- gpio_set_value(GPIO_NAND_CS, 1);
-}
-
-/*
- * hardware specific access to control-lines
- */
-static void cmx270_hwcontrol(struct mtd_info *mtd, int dat,
- unsigned int ctrl)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- unsigned int nandaddr = (unsigned int)this->IO_ADDR_W;
-
- dsb();
-
- if (ctrl & NAND_CTRL_CHANGE) {
- if ( ctrl & NAND_ALE )
- nandaddr |= (1 << 3);
- else
- nandaddr &= ~(1 << 3);
- if ( ctrl & NAND_CLE )
- nandaddr |= (1 << 2);
- else
- nandaddr &= ~(1 << 2);
- if ( ctrl & NAND_NCE )
- nand_cs_on();
- else
- nand_cs_off();
- }
-
- dsb();
- this->IO_ADDR_W = (void __iomem*)nandaddr;
- if (dat != NAND_CMD_NONE)
- writel((dat << 16), this->IO_ADDR_W);
-
- dsb();
-}
-
-/*
- * read device ready pin
- */
-static int cmx270_device_ready(struct mtd_info *mtd)
-{
- dsb();
-
- return (gpio_get_value(GPIO_NAND_RB));
-}
-
-/*
- * Main initialization routine
- */
-static int __init cmx270_init(void)
-{
- struct nand_chip *this;
- int ret;
-
- if (!(machine_is_armcore() && cpu_is_pxa27x()))
- return -ENODEV;
-
- ret = gpio_request(GPIO_NAND_CS, "NAND CS");
- if (ret) {
- pr_warn("CM-X270: failed to request NAND CS gpio\n");
- return ret;
- }
-
- gpio_direction_output(GPIO_NAND_CS, 1);
-
- ret = gpio_request(GPIO_NAND_RB, "NAND R/B");
- if (ret) {
- pr_warn("CM-X270: failed to request NAND R/B gpio\n");
- goto err_gpio_request;
- }
-
- gpio_direction_input(GPIO_NAND_RB);
-
- /* Allocate memory for MTD device structure and private data */
- this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
- if (!this) {
- ret = -ENOMEM;
- goto err_kzalloc;
- }
-
- cmx270_nand_io = ioremap(PXA_CS1_PHYS, 12);
- if (!cmx270_nand_io) {
- pr_debug("Unable to ioremap NAND device\n");
- ret = -EINVAL;
- goto err_ioremap;
- }
-
- cmx270_nand_mtd = nand_to_mtd(this);
-
- /* Link the private data with the MTD structure */
- cmx270_nand_mtd->owner = THIS_MODULE;
-
- /* insert callbacks */
- this->IO_ADDR_R = cmx270_nand_io;
- this->IO_ADDR_W = cmx270_nand_io;
- this->cmd_ctrl = cmx270_hwcontrol;
- this->dev_ready = cmx270_device_ready;
-
- /* 15 us command delay time */
- this->chip_delay = 20;
- this->ecc.mode = NAND_ECC_SOFT;
- this->ecc.algo = NAND_ECC_HAMMING;
-
- /* read/write functions */
- this->read_byte = cmx270_read_byte;
- this->read_buf = cmx270_read_buf;
- this->write_buf = cmx270_write_buf;
-
- /* Scan to find existence of the device */
- ret = nand_scan(cmx270_nand_mtd, 1);
- if (ret) {
- pr_notice("No NAND device\n");
- goto err_scan;
- }
-
- /* Register the partitions */
- ret = mtd_device_parse_register(cmx270_nand_mtd, NULL, NULL,
- partition_info, NUM_PARTITIONS);
- if (ret)
- goto err_scan;
-
- /* Return happy */
- return 0;
-
-err_scan:
- iounmap(cmx270_nand_io);
-err_ioremap:
- kfree(this);
-err_kzalloc:
- gpio_free(GPIO_NAND_RB);
-err_gpio_request:
- gpio_free(GPIO_NAND_CS);
-
- return ret;
-
-}
-module_init(cmx270_init);
-
-/*
- * Clean up routine
- */
-static void __exit cmx270_cleanup(void)
-{
- /* Release resources, unregister device */
- nand_release(cmx270_nand_mtd);
-
- gpio_free(GPIO_NAND_RB);
- gpio_free(GPIO_NAND_CS);
-
- iounmap(cmx270_nand_io);
-
- kfree(mtd_to_nand(cmx270_nand_mtd));
-}
-module_exit(cmx270_cleanup);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
-MODULE_DESCRIPTION("NAND flash driver for Compulab CM-X270 Module");
diff --git a/drivers/mtd/nand/core.c b/drivers/mtd/nand/core.c
new file mode 100644
index 000000000000..9c9f8936b63b
--- /dev/null
+++ b/drivers/mtd/nand/core.c
@@ -0,0 +1,241 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2017 Free Electrons
+ *
+ * Authors:
+ * Boris Brezillon <boris.brezillon@free-electrons.com>
+ * Peter Pan <peterpandong@micron.com>
+ */
+
+#define pr_fmt(fmt) "nand: " fmt
+
+#include <linux/module.h>
+#include <linux/mtd/nand.h>
+
+/**
+ * nanddev_isbad() - Check if a block is bad
+ * @nand: NAND device
+ * @pos: position pointing to the block we want to check
+ *
+ * Return: true if the block is bad, false otherwise.
+ */
+bool nanddev_isbad(struct nand_device *nand, const struct nand_pos *pos)
+{
+ if (nanddev_bbt_is_initialized(nand)) {
+ unsigned int entry;
+ int status;
+
+ entry = nanddev_bbt_pos_to_entry(nand, pos);
+ status = nanddev_bbt_get_block_status(nand, entry);
+ /* Lazy block status retrieval */
+ if (status == NAND_BBT_BLOCK_STATUS_UNKNOWN) {
+ if (nand->ops->isbad(nand, pos))
+ status = NAND_BBT_BLOCK_FACTORY_BAD;
+ else
+ status = NAND_BBT_BLOCK_GOOD;
+
+ nanddev_bbt_set_block_status(nand, entry, status);
+ }
+
+ if (status == NAND_BBT_BLOCK_WORN ||
+ status == NAND_BBT_BLOCK_FACTORY_BAD)
+ return true;
+
+ return false;
+ }
+
+ return nand->ops->isbad(nand, pos);
+}
+EXPORT_SYMBOL_GPL(nanddev_isbad);
+
+/**
+ * nanddev_markbad() - Mark a block as bad
+ * @nand: NAND device
+ * @pos: position of the block to mark bad
+ *
+ * Mark a block bad. This function is updating the BBT if available and
+ * calls the low-level markbad hook (nand->ops->markbad()).
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int nanddev_markbad(struct nand_device *nand, const struct nand_pos *pos)
+{
+ struct mtd_info *mtd = nanddev_to_mtd(nand);
+ unsigned int entry;
+ int ret = 0;
+
+ if (nanddev_isbad(nand, pos))
+ return 0;
+
+ ret = nand->ops->markbad(nand, pos);
+ if (ret)
+ pr_warn("failed to write BBM to block @%llx (err = %d)\n",
+ nanddev_pos_to_offs(nand, pos), ret);
+
+ if (!nanddev_bbt_is_initialized(nand))
+ goto out;
+
+ entry = nanddev_bbt_pos_to_entry(nand, pos);
+ ret = nanddev_bbt_set_block_status(nand, entry, NAND_BBT_BLOCK_WORN);
+ if (ret)
+ goto out;
+
+ ret = nanddev_bbt_update(nand);
+
+out:
+ if (!ret)
+ mtd->ecc_stats.badblocks++;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(nanddev_markbad);
+
+/**
+ * nanddev_isreserved() - Check whether an eraseblock is reserved or not
+ * @nand: NAND device
+ * @pos: NAND position to test
+ *
+ * Checks whether the eraseblock pointed by @pos is reserved or not.
+ *
+ * Return: true if the eraseblock is reserved, false otherwise.
+ */
+bool nanddev_isreserved(struct nand_device *nand, const struct nand_pos *pos)
+{
+ unsigned int entry;
+ int status;
+
+ if (!nanddev_bbt_is_initialized(nand))
+ return false;
+
+ /* Return info from the table */
+ entry = nanddev_bbt_pos_to_entry(nand, pos);
+ status = nanddev_bbt_get_block_status(nand, entry);
+ return status == NAND_BBT_BLOCK_RESERVED;
+}
+EXPORT_SYMBOL_GPL(nanddev_isreserved);
+
+/**
+ * nanddev_erase() - Erase a NAND portion
+ * @nand: NAND device
+ * @pos: position of the block to erase
+ *
+ * Erases the block if it's not bad.
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos)
+{
+ if (nanddev_isbad(nand, pos) || nanddev_isreserved(nand, pos)) {
+ pr_warn("attempt to erase a bad/reserved block @%llx\n",
+ nanddev_pos_to_offs(nand, pos));
+ return -EIO;
+ }
+
+ return nand->ops->erase(nand, pos);
+}
+EXPORT_SYMBOL_GPL(nanddev_erase);
+
+/**
+ * nanddev_mtd_erase() - Generic mtd->_erase() implementation for NAND devices
+ * @mtd: MTD device
+ * @einfo: erase request
+ *
+ * This is a simple mtd->_erase() implementation iterating over all blocks
+ * concerned by @einfo and calling nand->ops->erase() on each of them.
+ *
+ * Note that mtd->_erase should not be directly assigned to this helper,
+ * because there's no locking here. NAND specialized layers should instead
+ * implement there own wrapper around nanddev_mtd_erase() taking the
+ * appropriate lock before calling nanddev_mtd_erase().
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int nanddev_mtd_erase(struct mtd_info *mtd, struct erase_info *einfo)
+{
+ struct nand_device *nand = mtd_to_nanddev(mtd);
+ struct nand_pos pos, last;
+ int ret;
+
+ nanddev_offs_to_pos(nand, einfo->addr, &pos);
+ nanddev_offs_to_pos(nand, einfo->addr + einfo->len - 1, &last);
+ while (nanddev_pos_cmp(&pos, &last) <= 0) {
+ ret = nanddev_erase(nand, &pos);
+ if (ret) {
+ einfo->fail_addr = nanddev_pos_to_offs(nand, &pos);
+
+ return ret;
+ }
+
+ nanddev_pos_next_eraseblock(nand, &pos);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nanddev_mtd_erase);
+
+/**
+ * nanddev_init() - Initialize a NAND device
+ * @nand: NAND device
+ * @ops: NAND device operations
+ * @owner: NAND device owner
+ *
+ * Initializes a NAND device object. Consistency checks are done on @ops and
+ * @nand->memorg. Also takes care of initializing the BBT.
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int nanddev_init(struct nand_device *nand, const struct nand_ops *ops,
+ struct module *owner)
+{
+ struct mtd_info *mtd = nanddev_to_mtd(nand);
+ struct nand_memory_organization *memorg = nanddev_get_memorg(nand);
+
+ if (!nand || !ops)
+ return -EINVAL;
+
+ if (!ops->erase || !ops->markbad || !ops->isbad)
+ return -EINVAL;
+
+ if (!memorg->bits_per_cell || !memorg->pagesize ||
+ !memorg->pages_per_eraseblock || !memorg->eraseblocks_per_lun ||
+ !memorg->planes_per_lun || !memorg->luns_per_target ||
+ !memorg->ntargets)
+ return -EINVAL;
+
+ nand->rowconv.eraseblock_addr_shift =
+ fls(memorg->pages_per_eraseblock - 1);
+ nand->rowconv.lun_addr_shift = fls(memorg->eraseblocks_per_lun - 1) +
+ nand->rowconv.eraseblock_addr_shift;
+
+ nand->ops = ops;
+
+ mtd->type = memorg->bits_per_cell == 1 ?
+ MTD_NANDFLASH : MTD_MLCNANDFLASH;
+ mtd->flags = MTD_CAP_NANDFLASH;
+ mtd->erasesize = memorg->pagesize * memorg->pages_per_eraseblock;
+ mtd->writesize = memorg->pagesize;
+ mtd->writebufsize = memorg->pagesize;
+ mtd->oobsize = memorg->oobsize;
+ mtd->size = nanddev_size(nand);
+ mtd->owner = owner;
+
+ return nanddev_bbt_init(nand);
+}
+EXPORT_SYMBOL_GPL(nanddev_init);
+
+/**
+ * nanddev_cleanup() - Release resources allocated in nanddev_init()
+ * @nand: NAND device
+ *
+ * Basically undoes what has been done in nanddev_init().
+ */
+void nanddev_cleanup(struct nand_device *nand)
+{
+ if (nanddev_bbt_is_initialized(nand))
+ nanddev_bbt_cleanup(nand);
+}
+EXPORT_SYMBOL_GPL(nanddev_cleanup);
+
+MODULE_DESCRIPTION("Generic NAND framework");
+MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c
deleted file mode 100644
index d48877540f14..000000000000
--- a/drivers/mtd/nand/cs553x_nand.c
+++ /dev/null
@@ -1,357 +0,0 @@
-/*
- * drivers/mtd/nand/cs553x_nand.c
- *
- * (C) 2005, 2006 Red Hat Inc.
- *
- * Author: David Woodhouse <dwmw2@infradead.org>
- * Tom Sylla <tom.sylla@amd.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.
- *
- * Overview:
- * This is a device driver for the NAND flash controller found on
- * the AMD CS5535/CS5536 companion chipsets for the Geode processor.
- * mtd-id for command line partitioning is cs553x_nand_cs[0-3]
- * where 0-3 reflects the chip select for NAND.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand_ecc.h>
-#include <linux/mtd/partitions.h>
-
-#include <asm/msr.h>
-#include <asm/io.h>
-
-#define NR_CS553X_CONTROLLERS 4
-
-#define MSR_DIVIL_GLD_CAP 0x51400000 /* DIVIL capabilitiies */
-#define CAP_CS5535 0x2df000ULL
-#define CAP_CS5536 0x5df500ULL
-
-/* NAND Timing MSRs */
-#define MSR_NANDF_DATA 0x5140001b /* NAND Flash Data Timing MSR */
-#define MSR_NANDF_CTL 0x5140001c /* NAND Flash Control Timing */
-#define MSR_NANDF_RSVD 0x5140001d /* Reserved */
-
-/* NAND BAR MSRs */
-#define MSR_DIVIL_LBAR_FLSH0 0x51400010 /* Flash Chip Select 0 */
-#define MSR_DIVIL_LBAR_FLSH1 0x51400011 /* Flash Chip Select 1 */
-#define MSR_DIVIL_LBAR_FLSH2 0x51400012 /* Flash Chip Select 2 */
-#define MSR_DIVIL_LBAR_FLSH3 0x51400013 /* Flash Chip Select 3 */
- /* Each made up of... */
-#define FLSH_LBAR_EN (1ULL<<32)
-#define FLSH_NOR_NAND (1ULL<<33) /* 1 for NAND */
-#define FLSH_MEM_IO (1ULL<<34) /* 1 for MMIO */
- /* I/O BARs have BASE_ADDR in bits 15:4, IO_MASK in 47:36 */
- /* MMIO BARs have BASE_ADDR in bits 31:12, MEM_MASK in 63:44 */
-
-/* Pin function selection MSR (IDE vs. flash on the IDE pins) */
-#define MSR_DIVIL_BALL_OPTS 0x51400015
-#define PIN_OPT_IDE (1<<0) /* 0 for flash, 1 for IDE */
-
-/* Registers within the NAND flash controller BAR -- memory mapped */
-#define MM_NAND_DATA 0x00 /* 0 to 0x7ff, in fact */
-#define MM_NAND_CTL 0x800 /* Any even address 0x800-0x80e */
-#define MM_NAND_IO 0x801 /* Any odd address 0x801-0x80f */
-#define MM_NAND_STS 0x810
-#define MM_NAND_ECC_LSB 0x811
-#define MM_NAND_ECC_MSB 0x812
-#define MM_NAND_ECC_COL 0x813
-#define MM_NAND_LAC 0x814
-#define MM_NAND_ECC_CTL 0x815
-
-/* Registers within the NAND flash controller BAR -- I/O mapped */
-#define IO_NAND_DATA 0x00 /* 0 to 3, in fact */
-#define IO_NAND_CTL 0x04
-#define IO_NAND_IO 0x05
-#define IO_NAND_STS 0x06
-#define IO_NAND_ECC_CTL 0x08
-#define IO_NAND_ECC_LSB 0x09
-#define IO_NAND_ECC_MSB 0x0a
-#define IO_NAND_ECC_COL 0x0b
-#define IO_NAND_LAC 0x0c
-
-#define CS_NAND_CTL_DIST_EN (1<<4) /* Enable NAND Distract interrupt */
-#define CS_NAND_CTL_RDY_INT_MASK (1<<3) /* Enable RDY/BUSY# interrupt */
-#define CS_NAND_CTL_ALE (1<<2)
-#define CS_NAND_CTL_CLE (1<<1)
-#define CS_NAND_CTL_CE (1<<0) /* Keep low; 1 to reset */
-
-#define CS_NAND_STS_FLASH_RDY (1<<3)
-#define CS_NAND_CTLR_BUSY (1<<2)
-#define CS_NAND_CMD_COMP (1<<1)
-#define CS_NAND_DIST_ST (1<<0)
-
-#define CS_NAND_ECC_PARITY (1<<2)
-#define CS_NAND_ECC_CLRECC (1<<1)
-#define CS_NAND_ECC_ENECC (1<<0)
-
-static void cs553x_read_buf(struct mtd_info *mtd, u_char *buf, int len)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
-
- while (unlikely(len > 0x800)) {
- memcpy_fromio(buf, this->IO_ADDR_R, 0x800);
- buf += 0x800;
- len -= 0x800;
- }
- memcpy_fromio(buf, this->IO_ADDR_R, len);
-}
-
-static void cs553x_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
-
- while (unlikely(len > 0x800)) {
- memcpy_toio(this->IO_ADDR_R, buf, 0x800);
- buf += 0x800;
- len -= 0x800;
- }
- memcpy_toio(this->IO_ADDR_R, buf, len);
-}
-
-static unsigned char cs553x_read_byte(struct mtd_info *mtd)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- return readb(this->IO_ADDR_R);
-}
-
-static void cs553x_write_byte(struct mtd_info *mtd, u_char byte)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- int i = 100000;
-
- while (i && readb(this->IO_ADDR_R + MM_NAND_STS) & CS_NAND_CTLR_BUSY) {
- udelay(1);
- i--;
- }
- writeb(byte, this->IO_ADDR_W + 0x801);
-}
-
-static void cs553x_hwcontrol(struct mtd_info *mtd, int cmd,
- unsigned int ctrl)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- void __iomem *mmio_base = this->IO_ADDR_R;
- if (ctrl & NAND_CTRL_CHANGE) {
- unsigned char ctl = (ctrl & ~NAND_CTRL_CHANGE ) ^ 0x01;
- writeb(ctl, mmio_base + MM_NAND_CTL);
- }
- if (cmd != NAND_CMD_NONE)
- cs553x_write_byte(mtd, cmd);
-}
-
-static int cs553x_device_ready(struct mtd_info *mtd)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- void __iomem *mmio_base = this->IO_ADDR_R;
- unsigned char foo = readb(mmio_base + MM_NAND_STS);
-
- return (foo & CS_NAND_STS_FLASH_RDY) && !(foo & CS_NAND_CTLR_BUSY);
-}
-
-static void cs_enable_hwecc(struct mtd_info *mtd, int mode)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- void __iomem *mmio_base = this->IO_ADDR_R;
-
- writeb(0x07, mmio_base + MM_NAND_ECC_CTL);
-}
-
-static int cs_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
-{
- uint32_t ecc;
- struct nand_chip *this = mtd_to_nand(mtd);
- void __iomem *mmio_base = this->IO_ADDR_R;
-
- ecc = readl(mmio_base + MM_NAND_STS);
-
- ecc_code[1] = ecc >> 8;
- ecc_code[0] = ecc >> 16;
- ecc_code[2] = ecc >> 24;
- return 0;
-}
-
-static struct mtd_info *cs553x_mtd[4];
-
-static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
-{
- int err = 0;
- struct nand_chip *this;
- struct mtd_info *new_mtd;
-
- printk(KERN_NOTICE "Probing CS553x NAND controller CS#%d at %sIO 0x%08lx\n", cs, mmio?"MM":"P", adr);
-
- if (!mmio) {
- printk(KERN_NOTICE "PIO mode not yet implemented for CS553X NAND controller\n");
- return -ENXIO;
- }
-
- /* Allocate memory for MTD device structure and private data */
- this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
- if (!this) {
- err = -ENOMEM;
- goto out;
- }
-
- new_mtd = nand_to_mtd(this);
-
- /* Link the private data with the MTD structure */
- new_mtd->owner = THIS_MODULE;
-
- /* map physical address */
- this->IO_ADDR_R = this->IO_ADDR_W = ioremap(adr, 4096);
- if (!this->IO_ADDR_R) {
- printk(KERN_WARNING "ioremap cs553x NAND @0x%08lx failed\n", adr);
- err = -EIO;
- goto out_mtd;
- }
-
- this->cmd_ctrl = cs553x_hwcontrol;
- this->dev_ready = cs553x_device_ready;
- this->read_byte = cs553x_read_byte;
- this->read_buf = cs553x_read_buf;
- this->write_buf = cs553x_write_buf;
-
- this->chip_delay = 0;
-
- this->ecc.mode = NAND_ECC_HW;
- this->ecc.size = 256;
- this->ecc.bytes = 3;
- this->ecc.hwctl = cs_enable_hwecc;
- this->ecc.calculate = cs_calculate_ecc;
- this->ecc.correct = nand_correct_data;
- this->ecc.strength = 1;
-
- /* Enable the following for a flash based bad block table */
- this->bbt_options = NAND_BBT_USE_FLASH;
-
- new_mtd->name = kasprintf(GFP_KERNEL, "cs553x_nand_cs%d", cs);
- if (!new_mtd->name) {
- err = -ENOMEM;
- goto out_ior;
- }
-
- /* Scan to find existence of the device */
- err = nand_scan(new_mtd, 1);
- if (err)
- goto out_free;
-
- cs553x_mtd[cs] = new_mtd;
- goto out;
-
-out_free:
- kfree(new_mtd->name);
-out_ior:
- iounmap(this->IO_ADDR_R);
-out_mtd:
- kfree(this);
-out:
- return err;
-}
-
-static int is_geode(void)
-{
- /* These are the CPUs which will have a CS553[56] companion chip */
- if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD &&
- boot_cpu_data.x86 == 5 &&
- boot_cpu_data.x86_model == 10)
- return 1; /* Geode LX */
-
- if ((boot_cpu_data.x86_vendor == X86_VENDOR_NSC ||
- boot_cpu_data.x86_vendor == X86_VENDOR_CYRIX) &&
- boot_cpu_data.x86 == 5 &&
- boot_cpu_data.x86_model == 5)
- return 1; /* Geode GX (née GX2) */
-
- return 0;
-}
-
-static int __init cs553x_init(void)
-{
- int err = -ENXIO;
- int i;
- uint64_t val;
-
- /* If the CPU isn't a Geode GX or LX, abort */
- if (!is_geode())
- return -ENXIO;
-
- /* If it doesn't have the CS553[56], abort */
- rdmsrl(MSR_DIVIL_GLD_CAP, val);
- val &= ~0xFFULL;
- if (val != CAP_CS5535 && val != CAP_CS5536)
- return -ENXIO;
-
- /* If it doesn't have the NAND controller enabled, abort */
- rdmsrl(MSR_DIVIL_BALL_OPTS, val);
- if (val & PIN_OPT_IDE) {
- printk(KERN_INFO "CS553x NAND controller: Flash I/O not enabled in MSR_DIVIL_BALL_OPTS.\n");
- return -ENXIO;
- }
-
- for (i = 0; i < NR_CS553X_CONTROLLERS; i++) {
- rdmsrl(MSR_DIVIL_LBAR_FLSH0 + i, val);
-
- if ((val & (FLSH_LBAR_EN|FLSH_NOR_NAND)) == (FLSH_LBAR_EN|FLSH_NOR_NAND))
- err = cs553x_init_one(i, !!(val & FLSH_MEM_IO), val & 0xFFFFFFFF);
- }
-
- /* Register all devices together here. This means we can easily hack it to
- do mtdconcat etc. if we want to. */
- for (i = 0; i < NR_CS553X_CONTROLLERS; i++) {
- if (cs553x_mtd[i]) {
- /* If any devices registered, return success. Else the last error. */
- mtd_device_parse_register(cs553x_mtd[i], NULL, NULL,
- NULL, 0);
- err = 0;
- }
- }
-
- return err;
-}
-
-module_init(cs553x_init);
-
-static void __exit cs553x_cleanup(void)
-{
- int i;
-
- for (i = 0; i < NR_CS553X_CONTROLLERS; i++) {
- struct mtd_info *mtd = cs553x_mtd[i];
- struct nand_chip *this;
- void __iomem *mmio_base;
-
- if (!mtd)
- continue;
-
- this = mtd_to_nand(mtd);
- mmio_base = this->IO_ADDR_R;
-
- /* Release resources, unregister device */
- nand_release(mtd);
- kfree(mtd->name);
- cs553x_mtd[i] = NULL;
-
- /* unmap physical address */
- iounmap(mmio_base);
-
- /* Free the MTD device structure */
- kfree(this);
- }
-}
-
-module_exit(cs553x_cleanup);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
-MODULE_DESCRIPTION("NAND controller driver for AMD CS5535/CS5536 companion chip");
diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c
deleted file mode 100644
index ccc8c43abcff..000000000000
--- a/drivers/mtd/nand/davinci_nand.c
+++ /dev/null
@@ -1,879 +0,0 @@
-/*
- * davinci_nand.c - NAND Flash Driver for DaVinci family chips
- *
- * Copyright © 2006 Texas Instruments.
- *
- * Port to 2.6.23 Copyright © 2008 by:
- * Sander Huijsen <Shuijsen@optelecom-nkf.com>
- * Troy Kisky <troy.kisky@boundarydevices.com>
- * Dirk Behme <Dirk.Behme@gmail.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/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/err.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/partitions.h>
-#include <linux/slab.h>
-#include <linux/of_device.h>
-#include <linux/of.h>
-
-#include <linux/platform_data/mtd-davinci.h>
-#include <linux/platform_data/mtd-davinci-aemif.h>
-
-/*
- * This is a device driver for the NAND flash controller found on the
- * various DaVinci family chips. It handles up to four SoC chipselects,
- * and some flavors of secondary chipselect (e.g. based on A12) as used
- * with multichip packages.
- *
- * The 1-bit ECC hardware is supported, as well as the newer 4-bit ECC
- * available on chips like the DM355 and OMAP-L137 and needed with the
- * more error-prone MLC NAND chips.
- *
- * This driver assumes EM_WAIT connects all the NAND devices' RDY/nBUSY
- * outputs in a "wire-AND" configuration, with no per-chip signals.
- */
-struct davinci_nand_info {
- struct nand_chip chip;
-
- struct device *dev;
- struct clk *clk;
-
- bool is_readmode;
-
- void __iomem *base;
- void __iomem *vaddr;
-
- uint32_t ioaddr;
- uint32_t current_cs;
-
- uint32_t mask_chipsel;
- uint32_t mask_ale;
- uint32_t mask_cle;
-
- uint32_t core_chipsel;
-
- struct davinci_aemif_timing *timing;
-};
-
-static DEFINE_SPINLOCK(davinci_nand_lock);
-static bool ecc4_busy;
-
-static inline struct davinci_nand_info *to_davinci_nand(struct mtd_info *mtd)
-{
- return container_of(mtd_to_nand(mtd), struct davinci_nand_info, chip);
-}
-
-static inline unsigned int davinci_nand_readl(struct davinci_nand_info *info,
- int offset)
-{
- return __raw_readl(info->base + offset);
-}
-
-static inline void davinci_nand_writel(struct davinci_nand_info *info,
- int offset, unsigned long value)
-{
- __raw_writel(value, info->base + offset);
-}
-
-/*----------------------------------------------------------------------*/
-
-/*
- * Access to hardware control lines: ALE, CLE, secondary chipselect.
- */
-
-static void nand_davinci_hwcontrol(struct mtd_info *mtd, int cmd,
- unsigned int ctrl)
-{
- struct davinci_nand_info *info = to_davinci_nand(mtd);
- uint32_t addr = info->current_cs;
- struct nand_chip *nand = mtd_to_nand(mtd);
-
- /* Did the control lines change? */
- if (ctrl & NAND_CTRL_CHANGE) {
- if ((ctrl & NAND_CTRL_CLE) == NAND_CTRL_CLE)
- addr |= info->mask_cle;
- else if ((ctrl & NAND_CTRL_ALE) == NAND_CTRL_ALE)
- addr |= info->mask_ale;
-
- nand->IO_ADDR_W = (void __iomem __force *)addr;
- }
-
- if (cmd != NAND_CMD_NONE)
- iowrite8(cmd, nand->IO_ADDR_W);
-}
-
-static void nand_davinci_select_chip(struct mtd_info *mtd, int chip)
-{
- struct davinci_nand_info *info = to_davinci_nand(mtd);
- uint32_t addr = info->ioaddr;
-
- /* maybe kick in a second chipselect */
- if (chip > 0)
- addr |= info->mask_chipsel;
- info->current_cs = addr;
-
- info->chip.IO_ADDR_W = (void __iomem __force *)addr;
- info->chip.IO_ADDR_R = info->chip.IO_ADDR_W;
-}
-
-/*----------------------------------------------------------------------*/
-
-/*
- * 1-bit hardware ECC ... context maintained for each core chipselect
- */
-
-static inline uint32_t nand_davinci_readecc_1bit(struct mtd_info *mtd)
-{
- struct davinci_nand_info *info = to_davinci_nand(mtd);
-
- return davinci_nand_readl(info, NANDF1ECC_OFFSET
- + 4 * info->core_chipsel);
-}
-
-static void nand_davinci_hwctl_1bit(struct mtd_info *mtd, int mode)
-{
- struct davinci_nand_info *info;
- uint32_t nandcfr;
- unsigned long flags;
-
- info = to_davinci_nand(mtd);
-
- /* Reset ECC hardware */
- nand_davinci_readecc_1bit(mtd);
-
- spin_lock_irqsave(&davinci_nand_lock, flags);
-
- /* Restart ECC hardware */
- nandcfr = davinci_nand_readl(info, NANDFCR_OFFSET);
- nandcfr |= BIT(8 + info->core_chipsel);
- davinci_nand_writel(info, NANDFCR_OFFSET, nandcfr);
-
- spin_unlock_irqrestore(&davinci_nand_lock, flags);
-}
-
-/*
- * Read hardware ECC value and pack into three bytes
- */
-static int nand_davinci_calculate_1bit(struct mtd_info *mtd,
- const u_char *dat, u_char *ecc_code)
-{
- unsigned int ecc_val = nand_davinci_readecc_1bit(mtd);
- unsigned int ecc24 = (ecc_val & 0x0fff) | ((ecc_val & 0x0fff0000) >> 4);
-
- /* invert so that erased block ecc is correct */
- ecc24 = ~ecc24;
- ecc_code[0] = (u_char)(ecc24);
- ecc_code[1] = (u_char)(ecc24 >> 8);
- ecc_code[2] = (u_char)(ecc24 >> 16);
-
- return 0;
-}
-
-static int nand_davinci_correct_1bit(struct mtd_info *mtd, u_char *dat,
- u_char *read_ecc, u_char *calc_ecc)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- uint32_t eccNand = read_ecc[0] | (read_ecc[1] << 8) |
- (read_ecc[2] << 16);
- uint32_t eccCalc = calc_ecc[0] | (calc_ecc[1] << 8) |
- (calc_ecc[2] << 16);
- uint32_t diff = eccCalc ^ eccNand;
-
- if (diff) {
- if ((((diff >> 12) ^ diff) & 0xfff) == 0xfff) {
- /* Correctable error */
- if ((diff >> (12 + 3)) < chip->ecc.size) {
- dat[diff >> (12 + 3)] ^= BIT((diff >> 12) & 7);
- return 1;
- } else {
- return -EBADMSG;
- }
- } else if (!(diff & (diff - 1))) {
- /* Single bit ECC error in the ECC itself,
- * nothing to fix */
- return 1;
- } else {
- /* Uncorrectable error */
- return -EBADMSG;
- }
-
- }
- return 0;
-}
-
-/*----------------------------------------------------------------------*/
-
-/*
- * 4-bit hardware ECC ... context maintained over entire AEMIF
- *
- * This is a syndrome engine, but we avoid NAND_ECC_HW_SYNDROME
- * since that forces use of a problematic "infix OOB" layout.
- * Among other things, it trashes manufacturer bad block markers.
- * Also, and specific to this hardware, it ECC-protects the "prepad"
- * in the OOB ... while having ECC protection for parts of OOB would
- * seem useful, the current MTD stack sometimes wants to update the
- * OOB without recomputing ECC.
- */
-
-static void nand_davinci_hwctl_4bit(struct mtd_info *mtd, int mode)
-{
- struct davinci_nand_info *info = to_davinci_nand(mtd);
- unsigned long flags;
- u32 val;
-
- /* Reset ECC hardware */
- davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET);
-
- spin_lock_irqsave(&davinci_nand_lock, flags);
-
- /* Start 4-bit ECC calculation for read/write */
- val = davinci_nand_readl(info, NANDFCR_OFFSET);
- val &= ~(0x03 << 4);
- val |= (info->core_chipsel << 4) | BIT(12);
- davinci_nand_writel(info, NANDFCR_OFFSET, val);
-
- info->is_readmode = (mode == NAND_ECC_READ);
-
- spin_unlock_irqrestore(&davinci_nand_lock, flags);
-}
-
-/* Read raw ECC code after writing to NAND. */
-static void
-nand_davinci_readecc_4bit(struct davinci_nand_info *info, u32 code[4])
-{
- const u32 mask = 0x03ff03ff;
-
- code[0] = davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET) & mask;
- code[1] = davinci_nand_readl(info, NAND_4BIT_ECC2_OFFSET) & mask;
- code[2] = davinci_nand_readl(info, NAND_4BIT_ECC3_OFFSET) & mask;
- code[3] = davinci_nand_readl(info, NAND_4BIT_ECC4_OFFSET) & mask;
-}
-
-/* Terminate read ECC; or return ECC (as bytes) of data written to NAND. */
-static int nand_davinci_calculate_4bit(struct mtd_info *mtd,
- const u_char *dat, u_char *ecc_code)
-{
- struct davinci_nand_info *info = to_davinci_nand(mtd);
- u32 raw_ecc[4], *p;
- unsigned i;
-
- /* After a read, terminate ECC calculation by a dummy read
- * of some 4-bit ECC register. ECC covers everything that
- * was read; correct() just uses the hardware state, so
- * ecc_code is not needed.
- */
- if (info->is_readmode) {
- davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET);
- return 0;
- }
-
- /* Pack eight raw 10-bit ecc values into ten bytes, making
- * two passes which each convert four values (in upper and
- * lower halves of two 32-bit words) into five bytes. The
- * ROM boot loader uses this same packing scheme.
- */
- nand_davinci_readecc_4bit(info, raw_ecc);
- for (i = 0, p = raw_ecc; i < 2; i++, p += 2) {
- *ecc_code++ = p[0] & 0xff;
- *ecc_code++ = ((p[0] >> 8) & 0x03) | ((p[0] >> 14) & 0xfc);
- *ecc_code++ = ((p[0] >> 22) & 0x0f) | ((p[1] << 4) & 0xf0);
- *ecc_code++ = ((p[1] >> 4) & 0x3f) | ((p[1] >> 10) & 0xc0);
- *ecc_code++ = (p[1] >> 18) & 0xff;
- }
-
- return 0;
-}
-
-/* Correct up to 4 bits in data we just read, using state left in the
- * hardware plus the ecc_code computed when it was first written.
- */
-static int nand_davinci_correct_4bit(struct mtd_info *mtd,
- u_char *data, u_char *ecc_code, u_char *null)
-{
- int i;
- struct davinci_nand_info *info = to_davinci_nand(mtd);
- unsigned short ecc10[8];
- unsigned short *ecc16;
- u32 syndrome[4];
- u32 ecc_state;
- unsigned num_errors, corrected;
- unsigned long timeo;
-
- /* Unpack ten bytes into eight 10 bit values. We know we're
- * little-endian, and use type punning for less shifting/masking.
- */
- if (WARN_ON(0x01 & (unsigned) ecc_code))
- return -EINVAL;
- ecc16 = (unsigned short *)ecc_code;
-
- ecc10[0] = (ecc16[0] >> 0) & 0x3ff;
- ecc10[1] = ((ecc16[0] >> 10) & 0x3f) | ((ecc16[1] << 6) & 0x3c0);
- ecc10[2] = (ecc16[1] >> 4) & 0x3ff;
- ecc10[3] = ((ecc16[1] >> 14) & 0x3) | ((ecc16[2] << 2) & 0x3fc);
- ecc10[4] = (ecc16[2] >> 8) | ((ecc16[3] << 8) & 0x300);
- ecc10[5] = (ecc16[3] >> 2) & 0x3ff;
- ecc10[6] = ((ecc16[3] >> 12) & 0xf) | ((ecc16[4] << 4) & 0x3f0);
- ecc10[7] = (ecc16[4] >> 6) & 0x3ff;
-
- /* Tell ECC controller about the expected ECC codes. */
- for (i = 7; i >= 0; i--)
- davinci_nand_writel(info, NAND_4BIT_ECC_LOAD_OFFSET, ecc10[i]);
-
- /* Allow time for syndrome calculation ... then read it.
- * A syndrome of all zeroes 0 means no detected errors.
- */
- davinci_nand_readl(info, NANDFSR_OFFSET);
- nand_davinci_readecc_4bit(info, syndrome);
- if (!(syndrome[0] | syndrome[1] | syndrome[2] | syndrome[3]))
- return 0;
-
- /*
- * Clear any previous address calculation by doing a dummy read of an
- * error address register.
- */
- davinci_nand_readl(info, NAND_ERR_ADD1_OFFSET);
-
- /* Start address calculation, and wait for it to complete.
- * We _could_ start reading more data while this is working,
- * to speed up the overall page read.
- */
- davinci_nand_writel(info, NANDFCR_OFFSET,
- davinci_nand_readl(info, NANDFCR_OFFSET) | BIT(13));
-
- /*
- * ECC_STATE field reads 0x3 (Error correction complete) immediately
- * after setting the 4BITECC_ADD_CALC_START bit. So if you immediately
- * begin trying to poll for the state, you may fall right out of your
- * loop without any of the correction calculations having taken place.
- * The recommendation from the hardware team is to initially delay as
- * long as ECC_STATE reads less than 4. After that, ECC HW has entered
- * correction state.
- */
- timeo = jiffies + usecs_to_jiffies(100);
- do {
- ecc_state = (davinci_nand_readl(info,
- NANDFSR_OFFSET) >> 8) & 0x0f;
- cpu_relax();
- } while ((ecc_state < 4) && time_before(jiffies, timeo));
-
- for (;;) {
- u32 fsr = davinci_nand_readl(info, NANDFSR_OFFSET);
-
- switch ((fsr >> 8) & 0x0f) {
- case 0: /* no error, should not happen */
- davinci_nand_readl(info, NAND_ERR_ERRVAL1_OFFSET);
- return 0;
- case 1: /* five or more errors detected */
- davinci_nand_readl(info, NAND_ERR_ERRVAL1_OFFSET);
- return -EBADMSG;
- case 2: /* error addresses computed */
- case 3:
- num_errors = 1 + ((fsr >> 16) & 0x03);
- goto correct;
- default: /* still working on it */
- cpu_relax();
- continue;
- }
- }
-
-correct:
- /* correct each error */
- for (i = 0, corrected = 0; i < num_errors; i++) {
- int error_address, error_value;
-
- if (i > 1) {
- error_address = davinci_nand_readl(info,
- NAND_ERR_ADD2_OFFSET);
- error_value = davinci_nand_readl(info,
- NAND_ERR_ERRVAL2_OFFSET);
- } else {
- error_address = davinci_nand_readl(info,
- NAND_ERR_ADD1_OFFSET);
- error_value = davinci_nand_readl(info,
- NAND_ERR_ERRVAL1_OFFSET);
- }
-
- if (i & 1) {
- error_address >>= 16;
- error_value >>= 16;
- }
- error_address &= 0x3ff;
- error_address = (512 + 7) - error_address;
-
- if (error_address < 512) {
- data[error_address] ^= error_value;
- corrected++;
- }
- }
-
- return corrected;
-}
-
-/*----------------------------------------------------------------------*/
-
-/*
- * NOTE: NAND boot requires ALE == EM_A[1], CLE == EM_A[2], so that's
- * how these chips are normally wired. This translates to both 8 and 16
- * bit busses using ALE == BIT(3) in byte addresses, and CLE == BIT(4).
- *
- * For now we assume that configuration, or any other one which ignores
- * the two LSBs for NAND access ... so we can issue 32-bit reads/writes
- * and have that transparently morphed into multiple NAND operations.
- */
-static void nand_davinci_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- if ((0x03 & ((unsigned)buf)) == 0 && (0x03 & len) == 0)
- ioread32_rep(chip->IO_ADDR_R, buf, len >> 2);
- else if ((0x01 & ((unsigned)buf)) == 0 && (0x01 & len) == 0)
- ioread16_rep(chip->IO_ADDR_R, buf, len >> 1);
- else
- ioread8_rep(chip->IO_ADDR_R, buf, len);
-}
-
-static void nand_davinci_write_buf(struct mtd_info *mtd,
- const uint8_t *buf, int len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- if ((0x03 & ((unsigned)buf)) == 0 && (0x03 & len) == 0)
- iowrite32_rep(chip->IO_ADDR_R, buf, len >> 2);
- else if ((0x01 & ((unsigned)buf)) == 0 && (0x01 & len) == 0)
- iowrite16_rep(chip->IO_ADDR_R, buf, len >> 1);
- else
- iowrite8_rep(chip->IO_ADDR_R, buf, len);
-}
-
-/*
- * Check hardware register for wait status. Returns 1 if device is ready,
- * 0 if it is still busy.
- */
-static int nand_davinci_dev_ready(struct mtd_info *mtd)
-{
- struct davinci_nand_info *info = to_davinci_nand(mtd);
-
- return davinci_nand_readl(info, NANDFSR_OFFSET) & BIT(0);
-}
-
-/*----------------------------------------------------------------------*/
-
-/* An ECC layout for using 4-bit ECC with small-page flash, storing
- * ten ECC bytes plus the manufacturer's bad block marker byte, and
- * and not overlapping the default BBT markers.
- */
-static int hwecc4_ooblayout_small_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- if (section > 2)
- return -ERANGE;
-
- if (!section) {
- oobregion->offset = 0;
- oobregion->length = 5;
- } else if (section == 1) {
- oobregion->offset = 6;
- oobregion->length = 2;
- } else {
- oobregion->offset = 13;
- oobregion->length = 3;
- }
-
- return 0;
-}
-
-static int hwecc4_ooblayout_small_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- if (section > 1)
- return -ERANGE;
-
- if (!section) {
- oobregion->offset = 8;
- oobregion->length = 5;
- } else {
- oobregion->offset = 16;
- oobregion->length = mtd->oobsize - 16;
- }
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops hwecc4_small_ooblayout_ops = {
- .ecc = hwecc4_ooblayout_small_ecc,
- .free = hwecc4_ooblayout_small_free,
-};
-
-#if defined(CONFIG_OF)
-static const struct of_device_id davinci_nand_of_match[] = {
- {.compatible = "ti,davinci-nand", },
- {.compatible = "ti,keystone-nand", },
- {},
-};
-MODULE_DEVICE_TABLE(of, davinci_nand_of_match);
-
-static struct davinci_nand_pdata
- *nand_davinci_get_pdata(struct platform_device *pdev)
-{
- if (!dev_get_platdata(&pdev->dev) && pdev->dev.of_node) {
- struct davinci_nand_pdata *pdata;
- const char *mode;
- u32 prop;
-
- pdata = devm_kzalloc(&pdev->dev,
- sizeof(struct davinci_nand_pdata),
- GFP_KERNEL);
- pdev->dev.platform_data = pdata;
- if (!pdata)
- return ERR_PTR(-ENOMEM);
- if (!of_property_read_u32(pdev->dev.of_node,
- "ti,davinci-chipselect", &prop))
- pdev->id = prop;
- else
- return ERR_PTR(-EINVAL);
-
- if (!of_property_read_u32(pdev->dev.of_node,
- "ti,davinci-mask-ale", &prop))
- pdata->mask_ale = prop;
- if (!of_property_read_u32(pdev->dev.of_node,
- "ti,davinci-mask-cle", &prop))
- pdata->mask_cle = prop;
- if (!of_property_read_u32(pdev->dev.of_node,
- "ti,davinci-mask-chipsel", &prop))
- pdata->mask_chipsel = prop;
- if (!of_property_read_string(pdev->dev.of_node,
- "ti,davinci-ecc-mode", &mode)) {
- if (!strncmp("none", mode, 4))
- pdata->ecc_mode = NAND_ECC_NONE;
- if (!strncmp("soft", mode, 4))
- pdata->ecc_mode = NAND_ECC_SOFT;
- if (!strncmp("hw", mode, 2))
- pdata->ecc_mode = NAND_ECC_HW;
- }
- if (!of_property_read_u32(pdev->dev.of_node,
- "ti,davinci-ecc-bits", &prop))
- pdata->ecc_bits = prop;
-
- if (!of_property_read_u32(pdev->dev.of_node,
- "ti,davinci-nand-buswidth", &prop) && prop == 16)
- pdata->options |= NAND_BUSWIDTH_16;
-
- if (of_property_read_bool(pdev->dev.of_node,
- "ti,davinci-nand-use-bbt"))
- pdata->bbt_options = NAND_BBT_USE_FLASH;
-
- /*
- * Since kernel v4.8, this driver has been fixed to enable
- * use of 4-bit hardware ECC with subpages and verified on
- * TI's keystone EVMs (K2L, K2HK and K2E).
- * However, in the interest of not breaking systems using
- * existing UBI partitions, sub-page writes are not being
- * (re)enabled. If you want to use subpage writes on Keystone
- * platforms (i.e. do not have any existing UBI partitions),
- * then use "ti,davinci-nand" as the compatible in your
- * device-tree file.
- */
- if (of_device_is_compatible(pdev->dev.of_node,
- "ti,keystone-nand")) {
- pdata->options |= NAND_NO_SUBPAGE_WRITE;
- }
- }
-
- return dev_get_platdata(&pdev->dev);
-}
-#else
-static struct davinci_nand_pdata
- *nand_davinci_get_pdata(struct platform_device *pdev)
-{
- return dev_get_platdata(&pdev->dev);
-}
-#endif
-
-static int nand_davinci_probe(struct platform_device *pdev)
-{
- struct davinci_nand_pdata *pdata;
- struct davinci_nand_info *info;
- struct resource *res1;
- struct resource *res2;
- void __iomem *vaddr;
- void __iomem *base;
- int ret;
- uint32_t val;
- struct mtd_info *mtd;
-
- pdata = nand_davinci_get_pdata(pdev);
- if (IS_ERR(pdata))
- return PTR_ERR(pdata);
-
- /* insist on board-specific configuration */
- if (!pdata)
- return -ENODEV;
-
- /* which external chipselect will we be managing? */
- if (pdev->id < 0 || pdev->id > 3)
- return -ENODEV;
-
- info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
- if (!info)
- return -ENOMEM;
-
- platform_set_drvdata(pdev, info);
-
- res1 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (!res1 || !res2) {
- dev_err(&pdev->dev, "resource missing\n");
- return -EINVAL;
- }
-
- vaddr = devm_ioremap_resource(&pdev->dev, res1);
- if (IS_ERR(vaddr))
- return PTR_ERR(vaddr);
-
- /*
- * This registers range is used to setup NAND settings. In case with
- * TI AEMIF driver, the same memory address range is requested already
- * by AEMIF, so we cannot request it twice, just ioremap.
- * The AEMIF and NAND drivers not use the same registers in this range.
- */
- base = devm_ioremap(&pdev->dev, res2->start, resource_size(res2));
- if (!base) {
- dev_err(&pdev->dev, "ioremap failed for resource %pR\n", res2);
- return -EADDRNOTAVAIL;
- }
-
- info->dev = &pdev->dev;
- info->base = base;
- info->vaddr = vaddr;
-
- mtd = nand_to_mtd(&info->chip);
- mtd->dev.parent = &pdev->dev;
- nand_set_flash_node(&info->chip, pdev->dev.of_node);
-
- info->chip.IO_ADDR_R = vaddr;
- info->chip.IO_ADDR_W = vaddr;
- info->chip.chip_delay = 0;
- info->chip.select_chip = nand_davinci_select_chip;
-
- /* options such as NAND_BBT_USE_FLASH */
- info->chip.bbt_options = pdata->bbt_options;
- /* options such as 16-bit widths */
- info->chip.options = pdata->options;
- info->chip.bbt_td = pdata->bbt_td;
- info->chip.bbt_md = pdata->bbt_md;
- info->timing = pdata->timing;
-
- info->ioaddr = (uint32_t __force) vaddr;
-
- info->current_cs = info->ioaddr;
- info->core_chipsel = pdev->id;
- info->mask_chipsel = pdata->mask_chipsel;
-
- /* use nandboot-capable ALE/CLE masks by default */
- info->mask_ale = pdata->mask_ale ? : MASK_ALE;
- info->mask_cle = pdata->mask_cle ? : MASK_CLE;
-
- /* Set address of hardware control function */
- info->chip.cmd_ctrl = nand_davinci_hwcontrol;
- info->chip.dev_ready = nand_davinci_dev_ready;
-
- /* Speed up buffer I/O */
- info->chip.read_buf = nand_davinci_read_buf;
- info->chip.write_buf = nand_davinci_write_buf;
-
- /* Use board-specific ECC config */
- info->chip.ecc.mode = pdata->ecc_mode;
-
- ret = -EINVAL;
-
- info->clk = devm_clk_get(&pdev->dev, "aemif");
- if (IS_ERR(info->clk)) {
- ret = PTR_ERR(info->clk);
- dev_dbg(&pdev->dev, "unable to get AEMIF clock, err %d\n", ret);
- return ret;
- }
-
- ret = clk_prepare_enable(info->clk);
- if (ret < 0) {
- dev_dbg(&pdev->dev, "unable to enable AEMIF clock, err %d\n",
- ret);
- goto err_clk_enable;
- }
-
- spin_lock_irq(&davinci_nand_lock);
-
- /* put CSxNAND into NAND mode */
- val = davinci_nand_readl(info, NANDFCR_OFFSET);
- val |= BIT(info->core_chipsel);
- davinci_nand_writel(info, NANDFCR_OFFSET, val);
-
- spin_unlock_irq(&davinci_nand_lock);
-
- /* Scan to find existence of the device(s) */
- ret = nand_scan_ident(mtd, pdata->mask_chipsel ? 2 : 1, NULL);
- if (ret < 0) {
- dev_dbg(&pdev->dev, "no NAND chip(s) found\n");
- goto err;
- }
-
- switch (info->chip.ecc.mode) {
- case NAND_ECC_NONE:
- pdata->ecc_bits = 0;
- break;
- case NAND_ECC_SOFT:
- pdata->ecc_bits = 0;
- /*
- * This driver expects Hamming based ECC when ecc_mode is set
- * to NAND_ECC_SOFT. Force ecc.algo to NAND_ECC_HAMMING to
- * avoid adding an extra ->ecc_algo field to
- * davinci_nand_pdata.
- */
- info->chip.ecc.algo = NAND_ECC_HAMMING;
- break;
- case NAND_ECC_HW:
- if (pdata->ecc_bits == 4) {
- /* No sanity checks: CPUs must support this,
- * and the chips may not use NAND_BUSWIDTH_16.
- */
-
- /* No sharing 4-bit hardware between chipselects yet */
- spin_lock_irq(&davinci_nand_lock);
- if (ecc4_busy)
- ret = -EBUSY;
- else
- ecc4_busy = true;
- spin_unlock_irq(&davinci_nand_lock);
-
- if (ret == -EBUSY)
- return ret;
-
- info->chip.ecc.calculate = nand_davinci_calculate_4bit;
- info->chip.ecc.correct = nand_davinci_correct_4bit;
- info->chip.ecc.hwctl = nand_davinci_hwctl_4bit;
- info->chip.ecc.bytes = 10;
- info->chip.ecc.options = NAND_ECC_GENERIC_ERASED_CHECK;
- info->chip.ecc.algo = NAND_ECC_BCH;
- } else {
- /* 1bit ecc hamming */
- info->chip.ecc.calculate = nand_davinci_calculate_1bit;
- info->chip.ecc.correct = nand_davinci_correct_1bit;
- info->chip.ecc.hwctl = nand_davinci_hwctl_1bit;
- info->chip.ecc.bytes = 3;
- info->chip.ecc.algo = NAND_ECC_HAMMING;
- }
- info->chip.ecc.size = 512;
- info->chip.ecc.strength = pdata->ecc_bits;
- break;
- default:
- return -EINVAL;
- }
-
- /* Update ECC layout if needed ... for 1-bit HW ECC, the default
- * is OK, but it allocates 6 bytes when only 3 are needed (for
- * each 512 bytes). For the 4-bit HW ECC, that default is not
- * usable: 10 bytes are needed, not 6.
- */
- if (pdata->ecc_bits == 4) {
- int chunks = mtd->writesize / 512;
-
- if (!chunks || mtd->oobsize < 16) {
- dev_dbg(&pdev->dev, "too small\n");
- ret = -EINVAL;
- goto err;
- }
-
- /* For small page chips, preserve the manufacturer's
- * badblock marking data ... and make sure a flash BBT
- * table marker fits in the free bytes.
- */
- if (chunks == 1) {
- mtd_set_ooblayout(mtd, &hwecc4_small_ooblayout_ops);
- } else if (chunks == 4 || chunks == 8) {
- mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
- info->chip.ecc.mode = NAND_ECC_HW_OOB_FIRST;
- } else {
- ret = -EIO;
- goto err;
- }
- }
-
- ret = nand_scan_tail(mtd);
- if (ret < 0)
- goto err;
-
- if (pdata->parts)
- ret = mtd_device_parse_register(mtd, NULL, NULL,
- pdata->parts, pdata->nr_parts);
- else
- ret = mtd_device_register(mtd, NULL, 0);
- if (ret < 0)
- goto err;
-
- val = davinci_nand_readl(info, NRCSR_OFFSET);
- dev_info(&pdev->dev, "controller rev. %d.%d\n",
- (val >> 8) & 0xff, val & 0xff);
-
- return 0;
-
-err:
- clk_disable_unprepare(info->clk);
-
-err_clk_enable:
- spin_lock_irq(&davinci_nand_lock);
- if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME)
- ecc4_busy = false;
- spin_unlock_irq(&davinci_nand_lock);
- return ret;
-}
-
-static int nand_davinci_remove(struct platform_device *pdev)
-{
- struct davinci_nand_info *info = platform_get_drvdata(pdev);
-
- spin_lock_irq(&davinci_nand_lock);
- if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME)
- ecc4_busy = false;
- spin_unlock_irq(&davinci_nand_lock);
-
- nand_release(nand_to_mtd(&info->chip));
-
- clk_disable_unprepare(info->clk);
-
- return 0;
-}
-
-static struct platform_driver nand_davinci_driver = {
- .probe = nand_davinci_probe,
- .remove = nand_davinci_remove,
- .driver = {
- .name = "davinci_nand",
- .of_match_table = of_match_ptr(davinci_nand_of_match),
- },
-};
-MODULE_ALIAS("platform:davinci_nand");
-
-module_platform_driver(nand_davinci_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Texas Instruments");
-MODULE_DESCRIPTION("Davinci NAND flash driver");
-
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
deleted file mode 100644
index 313c7f50621b..000000000000
--- a/drivers/mtd/nand/denali.c
+++ /dev/null
@@ -1,1408 +0,0 @@
-/*
- * NAND Flash Controller Device Driver
- * Copyright © 2009-2010, Intel Corporation and its suppliers.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#include <linux/bitfield.h>
-#include <linux/completion.h>
-#include <linux/dma-mapping.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-
-#include "denali.h"
-
-MODULE_LICENSE("GPL");
-
-#define DENALI_NAND_NAME "denali-nand"
-
-/* for Indexed Addressing */
-#define DENALI_INDEXED_CTRL 0x00
-#define DENALI_INDEXED_DATA 0x10
-
-#define DENALI_MAP00 (0 << 26) /* direct access to buffer */
-#define DENALI_MAP01 (1 << 26) /* read/write pages in PIO */
-#define DENALI_MAP10 (2 << 26) /* high-level control plane */
-#define DENALI_MAP11 (3 << 26) /* direct controller access */
-
-/* MAP11 access cycle type */
-#define DENALI_MAP11_CMD ((DENALI_MAP11) | 0) /* command cycle */
-#define DENALI_MAP11_ADDR ((DENALI_MAP11) | 1) /* address cycle */
-#define DENALI_MAP11_DATA ((DENALI_MAP11) | 2) /* data cycle */
-
-/* MAP10 commands */
-#define DENALI_ERASE 0x01
-
-#define DENALI_BANK(denali) ((denali)->active_bank << 24)
-
-#define DENALI_INVALID_BANK -1
-#define DENALI_NR_BANKS 4
-
-/*
- * The bus interface clock, clk_x, is phase aligned with the core clock. The
- * clk_x is an integral multiple N of the core clk. The value N is configured
- * at IP delivery time, and its available value is 4, 5, or 6. We need to align
- * to the largest value to make it work with any possible configuration.
- */
-#define DENALI_CLK_X_MULT 6
-
-static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd)
-{
- return container_of(mtd_to_nand(mtd), struct denali_nand_info, nand);
-}
-
-/*
- * Direct Addressing - the slave address forms the control information (command
- * type, bank, block, and page address). The slave data is the actual data to
- * be transferred. This mode requires 28 bits of address region allocated.
- */
-static u32 denali_direct_read(struct denali_nand_info *denali, u32 addr)
-{
- return ioread32(denali->host + addr);
-}
-
-static void denali_direct_write(struct denali_nand_info *denali, u32 addr,
- u32 data)
-{
- iowrite32(data, denali->host + addr);
-}
-
-/*
- * Indexed Addressing - address translation module intervenes in passing the
- * control information. This mode reduces the required address range. The
- * control information and transferred data are latched by the registers in
- * the translation module.
- */
-static u32 denali_indexed_read(struct denali_nand_info *denali, u32 addr)
-{
- iowrite32(addr, denali->host + DENALI_INDEXED_CTRL);
- return ioread32(denali->host + DENALI_INDEXED_DATA);
-}
-
-static void denali_indexed_write(struct denali_nand_info *denali, u32 addr,
- u32 data)
-{
- iowrite32(addr, denali->host + DENALI_INDEXED_CTRL);
- iowrite32(data, denali->host + DENALI_INDEXED_DATA);
-}
-
-/*
- * Use the configuration feature register to determine the maximum number of
- * banks that the hardware supports.
- */
-static void denali_detect_max_banks(struct denali_nand_info *denali)
-{
- uint32_t features = ioread32(denali->reg + FEATURES);
-
- denali->max_banks = 1 << FIELD_GET(FEATURES__N_BANKS, features);
-
- /* the encoding changed from rev 5.0 to 5.1 */
- if (denali->revision < 0x0501)
- denali->max_banks <<= 1;
-}
-
-static void denali_enable_irq(struct denali_nand_info *denali)
-{
- int i;
-
- for (i = 0; i < DENALI_NR_BANKS; i++)
- iowrite32(U32_MAX, denali->reg + INTR_EN(i));
- iowrite32(GLOBAL_INT_EN_FLAG, denali->reg + GLOBAL_INT_ENABLE);
-}
-
-static void denali_disable_irq(struct denali_nand_info *denali)
-{
- int i;
-
- for (i = 0; i < DENALI_NR_BANKS; i++)
- iowrite32(0, denali->reg + INTR_EN(i));
- iowrite32(0, denali->reg + GLOBAL_INT_ENABLE);
-}
-
-static void denali_clear_irq(struct denali_nand_info *denali,
- int bank, uint32_t irq_status)
-{
- /* write one to clear bits */
- iowrite32(irq_status, denali->reg + INTR_STATUS(bank));
-}
-
-static void denali_clear_irq_all(struct denali_nand_info *denali)
-{
- int i;
-
- for (i = 0; i < DENALI_NR_BANKS; i++)
- denali_clear_irq(denali, i, U32_MAX);
-}
-
-static irqreturn_t denali_isr(int irq, void *dev_id)
-{
- struct denali_nand_info *denali = dev_id;
- irqreturn_t ret = IRQ_NONE;
- uint32_t irq_status;
- int i;
-
- spin_lock(&denali->irq_lock);
-
- for (i = 0; i < DENALI_NR_BANKS; i++) {
- irq_status = ioread32(denali->reg + INTR_STATUS(i));
- if (irq_status)
- ret = IRQ_HANDLED;
-
- denali_clear_irq(denali, i, irq_status);
-
- if (i != denali->active_bank)
- continue;
-
- denali->irq_status |= irq_status;
-
- if (denali->irq_status & denali->irq_mask)
- complete(&denali->complete);
- }
-
- spin_unlock(&denali->irq_lock);
-
- return ret;
-}
-
-static void denali_reset_irq(struct denali_nand_info *denali)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&denali->irq_lock, flags);
- denali->irq_status = 0;
- denali->irq_mask = 0;
- spin_unlock_irqrestore(&denali->irq_lock, flags);
-}
-
-static uint32_t denali_wait_for_irq(struct denali_nand_info *denali,
- uint32_t irq_mask)
-{
- unsigned long time_left, flags;
- uint32_t irq_status;
-
- spin_lock_irqsave(&denali->irq_lock, flags);
-
- irq_status = denali->irq_status;
-
- if (irq_mask & irq_status) {
- /* return immediately if the IRQ has already happened. */
- spin_unlock_irqrestore(&denali->irq_lock, flags);
- return irq_status;
- }
-
- denali->irq_mask = irq_mask;
- reinit_completion(&denali->complete);
- spin_unlock_irqrestore(&denali->irq_lock, flags);
-
- time_left = wait_for_completion_timeout(&denali->complete,
- msecs_to_jiffies(1000));
- if (!time_left) {
- dev_err(denali->dev, "timeout while waiting for irq 0x%x\n",
- irq_mask);
- return 0;
- }
-
- return denali->irq_status;
-}
-
-static uint32_t denali_check_irq(struct denali_nand_info *denali)
-{
- unsigned long flags;
- uint32_t irq_status;
-
- spin_lock_irqsave(&denali->irq_lock, flags);
- irq_status = denali->irq_status;
- spin_unlock_irqrestore(&denali->irq_lock, flags);
-
- return irq_status;
-}
-
-static void denali_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
-{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
- u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali);
- int i;
-
- for (i = 0; i < len; i++)
- buf[i] = denali->host_read(denali, addr);
-}
-
-static void denali_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
-{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
- u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali);
- int i;
-
- for (i = 0; i < len; i++)
- denali->host_write(denali, addr, buf[i]);
-}
-
-static void denali_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
-{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
- u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali);
- uint16_t *buf16 = (uint16_t *)buf;
- int i;
-
- for (i = 0; i < len / 2; i++)
- buf16[i] = denali->host_read(denali, addr);
-}
-
-static void denali_write_buf16(struct mtd_info *mtd, const uint8_t *buf,
- int len)
-{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
- u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali);
- const uint16_t *buf16 = (const uint16_t *)buf;
- int i;
-
- for (i = 0; i < len / 2; i++)
- denali->host_write(denali, addr, buf16[i]);
-}
-
-static uint8_t denali_read_byte(struct mtd_info *mtd)
-{
- uint8_t byte;
-
- denali_read_buf(mtd, &byte, 1);
-
- return byte;
-}
-
-static void denali_write_byte(struct mtd_info *mtd, uint8_t byte)
-{
- denali_write_buf(mtd, &byte, 1);
-}
-
-static uint16_t denali_read_word(struct mtd_info *mtd)
-{
- uint16_t word;
-
- denali_read_buf16(mtd, (uint8_t *)&word, 2);
-
- return word;
-}
-
-static void denali_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
-{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
- uint32_t type;
-
- if (ctrl & NAND_CLE)
- type = DENALI_MAP11_CMD;
- else if (ctrl & NAND_ALE)
- type = DENALI_MAP11_ADDR;
- else
- return;
-
- /*
- * Some commands are followed by chip->dev_ready or chip->waitfunc.
- * irq_status must be cleared here to catch the R/B# interrupt later.
- */
- if (ctrl & NAND_CTRL_CHANGE)
- denali_reset_irq(denali);
-
- denali->host_write(denali, DENALI_BANK(denali) | type, dat);
-}
-
-static int denali_dev_ready(struct mtd_info *mtd)
-{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
-
- return !!(denali_check_irq(denali) & INTR__INT_ACT);
-}
-
-static int denali_check_erased_page(struct mtd_info *mtd,
- struct nand_chip *chip, uint8_t *buf,
- unsigned long uncor_ecc_flags,
- unsigned int max_bitflips)
-{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
- uint8_t *ecc_code = chip->oob_poi + denali->oob_skip_bytes;
- int ecc_steps = chip->ecc.steps;
- int ecc_size = chip->ecc.size;
- int ecc_bytes = chip->ecc.bytes;
- int i, stat;
-
- for (i = 0; i < ecc_steps; i++) {
- if (!(uncor_ecc_flags & BIT(i)))
- continue;
-
- stat = nand_check_erased_ecc_chunk(buf, ecc_size,
- ecc_code, ecc_bytes,
- NULL, 0,
- chip->ecc.strength);
- if (stat < 0) {
- mtd->ecc_stats.failed++;
- } else {
- mtd->ecc_stats.corrected += stat;
- max_bitflips = max_t(unsigned int, max_bitflips, stat);
- }
-
- buf += ecc_size;
- ecc_code += ecc_bytes;
- }
-
- return max_bitflips;
-}
-
-static int denali_hw_ecc_fixup(struct mtd_info *mtd,
- struct denali_nand_info *denali,
- unsigned long *uncor_ecc_flags)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- int bank = denali->active_bank;
- uint32_t ecc_cor;
- unsigned int max_bitflips;
-
- ecc_cor = ioread32(denali->reg + ECC_COR_INFO(bank));
- ecc_cor >>= ECC_COR_INFO__SHIFT(bank);
-
- if (ecc_cor & ECC_COR_INFO__UNCOR_ERR) {
- /*
- * This flag is set when uncorrectable error occurs at least in
- * one ECC sector. We can not know "how many sectors", or
- * "which sector(s)". We need erase-page check for all sectors.
- */
- *uncor_ecc_flags = GENMASK(chip->ecc.steps - 1, 0);
- return 0;
- }
-
- max_bitflips = FIELD_GET(ECC_COR_INFO__MAX_ERRORS, ecc_cor);
-
- /*
- * The register holds the maximum of per-sector corrected bitflips.
- * This is suitable for the return value of the ->read_page() callback.
- * Unfortunately, we can not know the total number of corrected bits in
- * the page. Increase the stats by max_bitflips. (compromised solution)
- */
- mtd->ecc_stats.corrected += max_bitflips;
-
- return max_bitflips;
-}
-
-static int denali_sw_ecc_fixup(struct mtd_info *mtd,
- struct denali_nand_info *denali,
- unsigned long *uncor_ecc_flags, uint8_t *buf)
-{
- unsigned int ecc_size = denali->nand.ecc.size;
- unsigned int bitflips = 0;
- unsigned int max_bitflips = 0;
- uint32_t err_addr, err_cor_info;
- unsigned int err_byte, err_sector, err_device;
- uint8_t err_cor_value;
- unsigned int prev_sector = 0;
- uint32_t irq_status;
-
- denali_reset_irq(denali);
-
- do {
- err_addr = ioread32(denali->reg + ECC_ERROR_ADDRESS);
- err_sector = FIELD_GET(ECC_ERROR_ADDRESS__SECTOR, err_addr);
- err_byte = FIELD_GET(ECC_ERROR_ADDRESS__OFFSET, err_addr);
-
- err_cor_info = ioread32(denali->reg + ERR_CORRECTION_INFO);
- err_cor_value = FIELD_GET(ERR_CORRECTION_INFO__BYTE,
- err_cor_info);
- err_device = FIELD_GET(ERR_CORRECTION_INFO__DEVICE,
- err_cor_info);
-
- /* reset the bitflip counter when crossing ECC sector */
- if (err_sector != prev_sector)
- bitflips = 0;
-
- if (err_cor_info & ERR_CORRECTION_INFO__UNCOR) {
- /*
- * Check later if this is a real ECC error, or
- * an erased sector.
- */
- *uncor_ecc_flags |= BIT(err_sector);
- } else if (err_byte < ecc_size) {
- /*
- * If err_byte is larger than ecc_size, means error
- * happened in OOB, so we ignore it. It's no need for
- * us to correct it err_device is represented the NAND
- * error bits are happened in if there are more than
- * one NAND connected.
- */
- int offset;
- unsigned int flips_in_byte;
-
- offset = (err_sector * ecc_size + err_byte) *
- denali->devs_per_cs + err_device;
-
- /* correct the ECC error */
- flips_in_byte = hweight8(buf[offset] ^ err_cor_value);
- buf[offset] ^= err_cor_value;
- mtd->ecc_stats.corrected += flips_in_byte;
- bitflips += flips_in_byte;
-
- max_bitflips = max(max_bitflips, bitflips);
- }
-
- prev_sector = err_sector;
- } while (!(err_cor_info & ERR_CORRECTION_INFO__LAST_ERR));
-
- /*
- * Once handle all ECC errors, controller will trigger an
- * ECC_TRANSACTION_DONE interrupt.
- */
- irq_status = denali_wait_for_irq(denali, INTR__ECC_TRANSACTION_DONE);
- if (!(irq_status & INTR__ECC_TRANSACTION_DONE))
- return -EIO;
-
- return max_bitflips;
-}
-
-static void denali_setup_dma64(struct denali_nand_info *denali,
- dma_addr_t dma_addr, int page, int write)
-{
- uint32_t mode;
- const int page_count = 1;
-
- mode = DENALI_MAP10 | DENALI_BANK(denali) | page;
-
- /* DMA is a three step process */
-
- /*
- * 1. setup transfer type, interrupt when complete,
- * burst len = 64 bytes, the number of pages
- */
- denali->host_write(denali, mode,
- 0x01002000 | (64 << 16) | (write << 8) | page_count);
-
- /* 2. set memory low address */
- denali->host_write(denali, mode, lower_32_bits(dma_addr));
-
- /* 3. set memory high address */
- denali->host_write(denali, mode, upper_32_bits(dma_addr));
-}
-
-static void denali_setup_dma32(struct denali_nand_info *denali,
- dma_addr_t dma_addr, int page, int write)
-{
- uint32_t mode;
- const int page_count = 1;
-
- mode = DENALI_MAP10 | DENALI_BANK(denali);
-
- /* DMA is a four step process */
-
- /* 1. setup transfer type and # of pages */
- denali->host_write(denali, mode | page,
- 0x2000 | (write << 8) | page_count);
-
- /* 2. set memory high address bits 23:8 */
- denali->host_write(denali, mode | ((dma_addr >> 16) << 8), 0x2200);
-
- /* 3. set memory low address bits 23:8 */
- denali->host_write(denali, mode | ((dma_addr & 0xffff) << 8), 0x2300);
-
- /* 4. interrupt when complete, burst len = 64 bytes */
- denali->host_write(denali, mode | 0x14000, 0x2400);
-}
-
-static int denali_pio_read(struct denali_nand_info *denali, void *buf,
- size_t size, int page, int raw)
-{
- u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page;
- uint32_t *buf32 = (uint32_t *)buf;
- uint32_t irq_status, ecc_err_mask;
- int i;
-
- if (denali->caps & DENALI_CAP_HW_ECC_FIXUP)
- ecc_err_mask = INTR__ECC_UNCOR_ERR;
- else
- ecc_err_mask = INTR__ECC_ERR;
-
- denali_reset_irq(denali);
-
- for (i = 0; i < size / 4; i++)
- *buf32++ = denali->host_read(denali, addr);
-
- irq_status = denali_wait_for_irq(denali, INTR__PAGE_XFER_INC);
- if (!(irq_status & INTR__PAGE_XFER_INC))
- return -EIO;
-
- if (irq_status & INTR__ERASED_PAGE)
- memset(buf, 0xff, size);
-
- return irq_status & ecc_err_mask ? -EBADMSG : 0;
-}
-
-static int denali_pio_write(struct denali_nand_info *denali,
- const void *buf, size_t size, int page, int raw)
-{
- u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page;
- const uint32_t *buf32 = (uint32_t *)buf;
- uint32_t irq_status;
- int i;
-
- denali_reset_irq(denali);
-
- for (i = 0; i < size / 4; i++)
- denali->host_write(denali, addr, *buf32++);
-
- irq_status = denali_wait_for_irq(denali,
- INTR__PROGRAM_COMP | INTR__PROGRAM_FAIL);
- if (!(irq_status & INTR__PROGRAM_COMP))
- return -EIO;
-
- return 0;
-}
-
-static int denali_pio_xfer(struct denali_nand_info *denali, void *buf,
- size_t size, int page, int raw, int write)
-{
- if (write)
- return denali_pio_write(denali, buf, size, page, raw);
- else
- return denali_pio_read(denali, buf, size, page, raw);
-}
-
-static int denali_dma_xfer(struct denali_nand_info *denali, void *buf,
- size_t size, int page, int raw, int write)
-{
- dma_addr_t dma_addr;
- uint32_t irq_mask, irq_status, ecc_err_mask;
- enum dma_data_direction dir = write ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
- int ret = 0;
-
- dma_addr = dma_map_single(denali->dev, buf, size, dir);
- if (dma_mapping_error(denali->dev, dma_addr)) {
- dev_dbg(denali->dev, "Failed to DMA-map buffer. Trying PIO.\n");
- return denali_pio_xfer(denali, buf, size, page, raw, write);
- }
-
- if (write) {
- /*
- * INTR__PROGRAM_COMP is never asserted for the DMA transfer.
- * We can use INTR__DMA_CMD_COMP instead. This flag is asserted
- * when the page program is completed.
- */
- irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL;
- ecc_err_mask = 0;
- } else if (denali->caps & DENALI_CAP_HW_ECC_FIXUP) {
- irq_mask = INTR__DMA_CMD_COMP;
- ecc_err_mask = INTR__ECC_UNCOR_ERR;
- } else {
- irq_mask = INTR__DMA_CMD_COMP;
- ecc_err_mask = INTR__ECC_ERR;
- }
-
- iowrite32(DMA_ENABLE__FLAG, denali->reg + DMA_ENABLE);
-
- denali_reset_irq(denali);
- denali->setup_dma(denali, dma_addr, page, write);
-
- irq_status = denali_wait_for_irq(denali, irq_mask);
- if (!(irq_status & INTR__DMA_CMD_COMP))
- ret = -EIO;
- else if (irq_status & ecc_err_mask)
- ret = -EBADMSG;
-
- iowrite32(0, denali->reg + DMA_ENABLE);
-
- dma_unmap_single(denali->dev, dma_addr, size, dir);
-
- if (irq_status & INTR__ERASED_PAGE)
- memset(buf, 0xff, size);
-
- return ret;
-}
-
-static int denali_data_xfer(struct denali_nand_info *denali, void *buf,
- size_t size, int page, int raw, int write)
-{
- iowrite32(raw ? 0 : ECC_ENABLE__FLAG, denali->reg + ECC_ENABLE);
- iowrite32(raw ? TRANSFER_SPARE_REG__FLAG : 0,
- denali->reg + TRANSFER_SPARE_REG);
-
- if (denali->dma_avail)
- return denali_dma_xfer(denali, buf, size, page, raw, write);
- else
- return denali_pio_xfer(denali, buf, size, page, raw, write);
-}
-
-static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip,
- int page, int write)
-{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
- int writesize = mtd->writesize;
- int oobsize = mtd->oobsize;
- uint8_t *bufpoi = chip->oob_poi;
- int ecc_steps = chip->ecc.steps;
- int ecc_size = chip->ecc.size;
- int ecc_bytes = chip->ecc.bytes;
- int oob_skip = denali->oob_skip_bytes;
- size_t size = writesize + oobsize;
- int i, pos, len;
-
- /* BBM at the beginning of the OOB area */
- if (write)
- nand_prog_page_begin_op(chip, page, writesize, bufpoi,
- oob_skip);
- else
- nand_read_page_op(chip, page, writesize, bufpoi, oob_skip);
- bufpoi += oob_skip;
-
- /* OOB ECC */
- for (i = 0; i < ecc_steps; i++) {
- pos = ecc_size + i * (ecc_size + ecc_bytes);
- len = ecc_bytes;
-
- if (pos >= writesize)
- pos += oob_skip;
- else if (pos + len > writesize)
- len = writesize - pos;
-
- if (write)
- nand_change_write_column_op(chip, pos, bufpoi, len,
- false);
- else
- nand_change_read_column_op(chip, pos, bufpoi, len,
- false);
- bufpoi += len;
- if (len < ecc_bytes) {
- len = ecc_bytes - len;
- if (write)
- nand_change_write_column_op(chip, writesize +
- oob_skip, bufpoi,
- len, false);
- else
- nand_change_read_column_op(chip, writesize +
- oob_skip, bufpoi,
- len, false);
- bufpoi += len;
- }
- }
-
- /* OOB free */
- len = oobsize - (bufpoi - chip->oob_poi);
- if (write)
- nand_change_write_column_op(chip, size - len, bufpoi, len,
- false);
- else
- nand_change_read_column_op(chip, size - len, bufpoi, len,
- false);
-}
-
-static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page)
-{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
- int writesize = mtd->writesize;
- int oobsize = mtd->oobsize;
- int ecc_steps = chip->ecc.steps;
- int ecc_size = chip->ecc.size;
- int ecc_bytes = chip->ecc.bytes;
- void *tmp_buf = denali->buf;
- int oob_skip = denali->oob_skip_bytes;
- size_t size = writesize + oobsize;
- int ret, i, pos, len;
-
- ret = denali_data_xfer(denali, tmp_buf, size, page, 1, 0);
- if (ret)
- return ret;
-
- /* Arrange the buffer for syndrome payload/ecc layout */
- if (buf) {
- for (i = 0; i < ecc_steps; i++) {
- pos = i * (ecc_size + ecc_bytes);
- len = ecc_size;
-
- if (pos >= writesize)
- pos += oob_skip;
- else if (pos + len > writesize)
- len = writesize - pos;
-
- memcpy(buf, tmp_buf + pos, len);
- buf += len;
- if (len < ecc_size) {
- len = ecc_size - len;
- memcpy(buf, tmp_buf + writesize + oob_skip,
- len);
- buf += len;
- }
- }
- }
-
- if (oob_required) {
- uint8_t *oob = chip->oob_poi;
-
- /* BBM at the beginning of the OOB area */
- memcpy(oob, tmp_buf + writesize, oob_skip);
- oob += oob_skip;
-
- /* OOB ECC */
- for (i = 0; i < ecc_steps; i++) {
- pos = ecc_size + i * (ecc_size + ecc_bytes);
- len = ecc_bytes;
-
- if (pos >= writesize)
- pos += oob_skip;
- else if (pos + len > writesize)
- len = writesize - pos;
-
- memcpy(oob, tmp_buf + pos, len);
- oob += len;
- if (len < ecc_bytes) {
- len = ecc_bytes - len;
- memcpy(oob, tmp_buf + writesize + oob_skip,
- len);
- oob += len;
- }
- }
-
- /* OOB free */
- len = oobsize - (oob - chip->oob_poi);
- memcpy(oob, tmp_buf + size - len, len);
- }
-
- return 0;
-}
-
-static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
- int page)
-{
- denali_oob_xfer(mtd, chip, page, 0);
-
- return 0;
-}
-
-static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
- int page)
-{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
-
- denali_reset_irq(denali);
-
- denali_oob_xfer(mtd, chip, page, 1);
-
- return nand_prog_page_end_op(chip);
-}
-
-static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page)
-{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
- unsigned long uncor_ecc_flags = 0;
- int stat = 0;
- int ret;
-
- ret = denali_data_xfer(denali, buf, mtd->writesize, page, 0, 0);
- if (ret && ret != -EBADMSG)
- return ret;
-
- if (denali->caps & DENALI_CAP_HW_ECC_FIXUP)
- stat = denali_hw_ecc_fixup(mtd, denali, &uncor_ecc_flags);
- else if (ret == -EBADMSG)
- stat = denali_sw_ecc_fixup(mtd, denali, &uncor_ecc_flags, buf);
-
- if (stat < 0)
- return stat;
-
- if (uncor_ecc_flags) {
- ret = denali_read_oob(mtd, chip, page);
- if (ret)
- return ret;
-
- stat = denali_check_erased_page(mtd, chip, buf,
- uncor_ecc_flags, stat);
- }
-
- return stat;
-}
-
-static int denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf, int oob_required, int page)
-{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
- int writesize = mtd->writesize;
- int oobsize = mtd->oobsize;
- int ecc_steps = chip->ecc.steps;
- int ecc_size = chip->ecc.size;
- int ecc_bytes = chip->ecc.bytes;
- void *tmp_buf = denali->buf;
- int oob_skip = denali->oob_skip_bytes;
- size_t size = writesize + oobsize;
- int i, pos, len;
-
- /*
- * Fill the buffer with 0xff first except the full page transfer.
- * This simplifies the logic.
- */
- if (!buf || !oob_required)
- memset(tmp_buf, 0xff, size);
-
- /* Arrange the buffer for syndrome payload/ecc layout */
- if (buf) {
- for (i = 0; i < ecc_steps; i++) {
- pos = i * (ecc_size + ecc_bytes);
- len = ecc_size;
-
- if (pos >= writesize)
- pos += oob_skip;
- else if (pos + len > writesize)
- len = writesize - pos;
-
- memcpy(tmp_buf + pos, buf, len);
- buf += len;
- if (len < ecc_size) {
- len = ecc_size - len;
- memcpy(tmp_buf + writesize + oob_skip, buf,
- len);
- buf += len;
- }
- }
- }
-
- if (oob_required) {
- const uint8_t *oob = chip->oob_poi;
-
- /* BBM at the beginning of the OOB area */
- memcpy(tmp_buf + writesize, oob, oob_skip);
- oob += oob_skip;
-
- /* OOB ECC */
- for (i = 0; i < ecc_steps; i++) {
- pos = ecc_size + i * (ecc_size + ecc_bytes);
- len = ecc_bytes;
-
- if (pos >= writesize)
- pos += oob_skip;
- else if (pos + len > writesize)
- len = writesize - pos;
-
- memcpy(tmp_buf + pos, oob, len);
- oob += len;
- if (len < ecc_bytes) {
- len = ecc_bytes - len;
- memcpy(tmp_buf + writesize + oob_skip, oob,
- len);
- oob += len;
- }
- }
-
- /* OOB free */
- len = oobsize - (oob - chip->oob_poi);
- memcpy(tmp_buf + size - len, oob, len);
- }
-
- return denali_data_xfer(denali, tmp_buf, size, page, 1, 1);
-}
-
-static int denali_write_page(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf, int oob_required, int page)
-{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
-
- return denali_data_xfer(denali, (void *)buf, mtd->writesize,
- page, 0, 1);
-}
-
-static void denali_select_chip(struct mtd_info *mtd, int chip)
-{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
-
- denali->active_bank = chip;
-}
-
-static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
-{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
- uint32_t irq_status;
-
- /* R/B# pin transitioned from low to high? */
- irq_status = denali_wait_for_irq(denali, INTR__INT_ACT);
-
- return irq_status & INTR__INT_ACT ? 0 : NAND_STATUS_FAIL;
-}
-
-static int denali_erase(struct mtd_info *mtd, int page)
-{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
- uint32_t irq_status;
-
- denali_reset_irq(denali);
-
- denali->host_write(denali, DENALI_MAP10 | DENALI_BANK(denali) | page,
- DENALI_ERASE);
-
- /* wait for erase to complete or failure to occur */
- irq_status = denali_wait_for_irq(denali,
- INTR__ERASE_COMP | INTR__ERASE_FAIL);
-
- return irq_status & INTR__ERASE_COMP ? 0 : -EIO;
-}
-
-static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,
- const struct nand_data_interface *conf)
-{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
- const struct nand_sdr_timings *timings;
- unsigned long t_clk;
- int acc_clks, re_2_we, re_2_re, we_2_re, addr_2_data;
- int rdwr_en_lo, rdwr_en_hi, rdwr_en_lo_hi, cs_setup;
- int addr_2_data_mask;
- uint32_t tmp;
-
- timings = nand_get_sdr_timings(conf);
- if (IS_ERR(timings))
- return PTR_ERR(timings);
-
- /* clk_x period in picoseconds */
- t_clk = DIV_ROUND_DOWN_ULL(1000000000000ULL, denali->clk_x_rate);
- if (!t_clk)
- return -EINVAL;
-
- if (chipnr == NAND_DATA_IFACE_CHECK_ONLY)
- return 0;
-
- /* tREA -> ACC_CLKS */
- acc_clks = DIV_ROUND_UP(timings->tREA_max, t_clk);
- acc_clks = min_t(int, acc_clks, ACC_CLKS__VALUE);
-
- tmp = ioread32(denali->reg + ACC_CLKS);
- tmp &= ~ACC_CLKS__VALUE;
- tmp |= FIELD_PREP(ACC_CLKS__VALUE, acc_clks);
- iowrite32(tmp, denali->reg + ACC_CLKS);
-
- /* tRWH -> RE_2_WE */
- re_2_we = DIV_ROUND_UP(timings->tRHW_min, t_clk);
- re_2_we = min_t(int, re_2_we, RE_2_WE__VALUE);
-
- tmp = ioread32(denali->reg + RE_2_WE);
- tmp &= ~RE_2_WE__VALUE;
- tmp |= FIELD_PREP(RE_2_WE__VALUE, re_2_we);
- iowrite32(tmp, denali->reg + RE_2_WE);
-
- /* tRHZ -> RE_2_RE */
- re_2_re = DIV_ROUND_UP(timings->tRHZ_max, t_clk);
- re_2_re = min_t(int, re_2_re, RE_2_RE__VALUE);
-
- tmp = ioread32(denali->reg + RE_2_RE);
- tmp &= ~RE_2_RE__VALUE;
- tmp |= FIELD_PREP(RE_2_RE__VALUE, re_2_re);
- iowrite32(tmp, denali->reg + RE_2_RE);
-
- /*
- * tCCS, tWHR -> WE_2_RE
- *
- * With WE_2_RE properly set, the Denali controller automatically takes
- * care of the delay; the driver need not set NAND_WAIT_TCCS.
- */
- we_2_re = DIV_ROUND_UP(max(timings->tCCS_min, timings->tWHR_min),
- t_clk);
- we_2_re = min_t(int, we_2_re, TWHR2_AND_WE_2_RE__WE_2_RE);
-
- tmp = ioread32(denali->reg + TWHR2_AND_WE_2_RE);
- tmp &= ~TWHR2_AND_WE_2_RE__WE_2_RE;
- tmp |= FIELD_PREP(TWHR2_AND_WE_2_RE__WE_2_RE, we_2_re);
- iowrite32(tmp, denali->reg + TWHR2_AND_WE_2_RE);
-
- /* tADL -> ADDR_2_DATA */
-
- /* for older versions, ADDR_2_DATA is only 6 bit wide */
- addr_2_data_mask = TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA;
- if (denali->revision < 0x0501)
- addr_2_data_mask >>= 1;
-
- addr_2_data = DIV_ROUND_UP(timings->tADL_min, t_clk);
- addr_2_data = min_t(int, addr_2_data, addr_2_data_mask);
-
- tmp = ioread32(denali->reg + TCWAW_AND_ADDR_2_DATA);
- tmp &= ~TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA;
- tmp |= FIELD_PREP(TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA, addr_2_data);
- iowrite32(tmp, denali->reg + TCWAW_AND_ADDR_2_DATA);
-
- /* tREH, tWH -> RDWR_EN_HI_CNT */
- rdwr_en_hi = DIV_ROUND_UP(max(timings->tREH_min, timings->tWH_min),
- t_clk);
- rdwr_en_hi = min_t(int, rdwr_en_hi, RDWR_EN_HI_CNT__VALUE);
-
- tmp = ioread32(denali->reg + RDWR_EN_HI_CNT);
- tmp &= ~RDWR_EN_HI_CNT__VALUE;
- tmp |= FIELD_PREP(RDWR_EN_HI_CNT__VALUE, rdwr_en_hi);
- iowrite32(tmp, denali->reg + RDWR_EN_HI_CNT);
-
- /* tRP, tWP -> RDWR_EN_LO_CNT */
- rdwr_en_lo = DIV_ROUND_UP(max(timings->tRP_min, timings->tWP_min),
- t_clk);
- rdwr_en_lo_hi = DIV_ROUND_UP(max(timings->tRC_min, timings->tWC_min),
- t_clk);
- rdwr_en_lo_hi = max(rdwr_en_lo_hi, DENALI_CLK_X_MULT);
- rdwr_en_lo = max(rdwr_en_lo, rdwr_en_lo_hi - rdwr_en_hi);
- rdwr_en_lo = min_t(int, rdwr_en_lo, RDWR_EN_LO_CNT__VALUE);
-
- tmp = ioread32(denali->reg + RDWR_EN_LO_CNT);
- tmp &= ~RDWR_EN_LO_CNT__VALUE;
- tmp |= FIELD_PREP(RDWR_EN_LO_CNT__VALUE, rdwr_en_lo);
- iowrite32(tmp, denali->reg + RDWR_EN_LO_CNT);
-
- /* tCS, tCEA -> CS_SETUP_CNT */
- cs_setup = max3((int)DIV_ROUND_UP(timings->tCS_min, t_clk) - rdwr_en_lo,
- (int)DIV_ROUND_UP(timings->tCEA_max, t_clk) - acc_clks,
- 0);
- cs_setup = min_t(int, cs_setup, CS_SETUP_CNT__VALUE);
-
- tmp = ioread32(denali->reg + CS_SETUP_CNT);
- tmp &= ~CS_SETUP_CNT__VALUE;
- tmp |= FIELD_PREP(CS_SETUP_CNT__VALUE, cs_setup);
- iowrite32(tmp, denali->reg + CS_SETUP_CNT);
-
- return 0;
-}
-
-static void denali_reset_banks(struct denali_nand_info *denali)
-{
- u32 irq_status;
- int i;
-
- for (i = 0; i < denali->max_banks; i++) {
- denali->active_bank = i;
-
- denali_reset_irq(denali);
-
- iowrite32(DEVICE_RESET__BANK(i),
- denali->reg + DEVICE_RESET);
-
- irq_status = denali_wait_for_irq(denali,
- INTR__RST_COMP | INTR__INT_ACT | INTR__TIME_OUT);
- if (!(irq_status & INTR__INT_ACT))
- break;
- }
-
- dev_dbg(denali->dev, "%d chips connected\n", i);
- denali->max_banks = i;
-}
-
-static void denali_hw_init(struct denali_nand_info *denali)
-{
- /*
- * The REVISION register may not be reliable. Platforms are allowed to
- * override it.
- */
- if (!denali->revision)
- denali->revision = swab16(ioread32(denali->reg + REVISION));
-
- /*
- * tell driver how many bit controller will skip before
- * writing ECC code in OOB, this register may be already
- * set by firmware. So we read this value out.
- * if this value is 0, just let it be.
- */
- denali->oob_skip_bytes = ioread32(denali->reg + SPARE_AREA_SKIP_BYTES);
- denali_detect_max_banks(denali);
- iowrite32(0x0F, denali->reg + RB_PIN_ENABLED);
- iowrite32(CHIP_EN_DONT_CARE__FLAG, denali->reg + CHIP_ENABLE_DONT_CARE);
-
- iowrite32(0xffff, denali->reg + SPARE_AREA_MARKER);
-}
-
-int denali_calc_ecc_bytes(int step_size, int strength)
-{
- /* BCH code. Denali requires ecc.bytes to be multiple of 2 */
- return DIV_ROUND_UP(strength * fls(step_size * 8), 16) * 2;
-}
-EXPORT_SYMBOL(denali_calc_ecc_bytes);
-
-static int denali_ecc_setup(struct mtd_info *mtd, struct nand_chip *chip,
- struct denali_nand_info *denali)
-{
- int oobavail = mtd->oobsize - denali->oob_skip_bytes;
- int ret;
-
- /*
- * If .size and .strength are already set (usually by DT),
- * check if they are supported by this controller.
- */
- if (chip->ecc.size && chip->ecc.strength)
- return nand_check_ecc_caps(chip, denali->ecc_caps, oobavail);
-
- /*
- * We want .size and .strength closest to the chip's requirement
- * unless NAND_ECC_MAXIMIZE is requested.
- */
- if (!(chip->ecc.options & NAND_ECC_MAXIMIZE)) {
- ret = nand_match_ecc_req(chip, denali->ecc_caps, oobavail);
- if (!ret)
- return 0;
- }
-
- /* Max ECC strength is the last thing we can do */
- return nand_maximize_ecc(chip, denali->ecc_caps, oobavail);
-}
-
-static int denali_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- if (section)
- return -ERANGE;
-
- oobregion->offset = denali->oob_skip_bytes;
- oobregion->length = chip->ecc.total;
-
- return 0;
-}
-
-static int denali_ooblayout_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- if (section)
- return -ERANGE;
-
- oobregion->offset = chip->ecc.total + denali->oob_skip_bytes;
- oobregion->length = mtd->oobsize - oobregion->offset;
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops denali_ooblayout_ops = {
- .ecc = denali_ooblayout_ecc,
- .free = denali_ooblayout_free,
-};
-
-static int denali_multidev_fixup(struct denali_nand_info *denali)
-{
- struct nand_chip *chip = &denali->nand;
- struct mtd_info *mtd = nand_to_mtd(chip);
-
- /*
- * Support for multi device:
- * When the IP configuration is x16 capable and two x8 chips are
- * connected in parallel, DEVICES_CONNECTED should be set to 2.
- * In this case, the core framework knows nothing about this fact,
- * so we should tell it the _logical_ pagesize and anything necessary.
- */
- denali->devs_per_cs = ioread32(denali->reg + DEVICES_CONNECTED);
-
- /*
- * On some SoCs, DEVICES_CONNECTED is not auto-detected.
- * For those, DEVICES_CONNECTED is left to 0. Set 1 if it is the case.
- */
- if (denali->devs_per_cs == 0) {
- denali->devs_per_cs = 1;
- iowrite32(1, denali->reg + DEVICES_CONNECTED);
- }
-
- if (denali->devs_per_cs == 1)
- return 0;
-
- if (denali->devs_per_cs != 2) {
- dev_err(denali->dev, "unsupported number of devices %d\n",
- denali->devs_per_cs);
- return -EINVAL;
- }
-
- /* 2 chips in parallel */
- mtd->size <<= 1;
- mtd->erasesize <<= 1;
- mtd->writesize <<= 1;
- mtd->oobsize <<= 1;
- chip->chipsize <<= 1;
- chip->page_shift += 1;
- chip->phys_erase_shift += 1;
- chip->bbt_erase_shift += 1;
- chip->chip_shift += 1;
- chip->pagemask <<= 1;
- chip->ecc.size <<= 1;
- chip->ecc.bytes <<= 1;
- chip->ecc.strength <<= 1;
- denali->oob_skip_bytes <<= 1;
-
- return 0;
-}
-
-int denali_init(struct denali_nand_info *denali)
-{
- struct nand_chip *chip = &denali->nand;
- struct mtd_info *mtd = nand_to_mtd(chip);
- u32 features = ioread32(denali->reg + FEATURES);
- int ret;
-
- mtd->dev.parent = denali->dev;
- denali_hw_init(denali);
-
- init_completion(&denali->complete);
- spin_lock_init(&denali->irq_lock);
-
- denali_clear_irq_all(denali);
-
- ret = devm_request_irq(denali->dev, denali->irq, denali_isr,
- IRQF_SHARED, DENALI_NAND_NAME, denali);
- if (ret) {
- dev_err(denali->dev, "Unable to request IRQ\n");
- return ret;
- }
-
- denali_enable_irq(denali);
- denali_reset_banks(denali);
-
- denali->active_bank = DENALI_INVALID_BANK;
-
- nand_set_flash_node(chip, denali->dev->of_node);
- /* Fallback to the default name if DT did not give "label" property */
- if (!mtd->name)
- mtd->name = "denali-nand";
-
- chip->select_chip = denali_select_chip;
- chip->read_byte = denali_read_byte;
- chip->write_byte = denali_write_byte;
- chip->read_word = denali_read_word;
- chip->cmd_ctrl = denali_cmd_ctrl;
- chip->dev_ready = denali_dev_ready;
- chip->waitfunc = denali_waitfunc;
-
- if (features & FEATURES__INDEX_ADDR) {
- denali->host_read = denali_indexed_read;
- denali->host_write = denali_indexed_write;
- } else {
- denali->host_read = denali_direct_read;
- denali->host_write = denali_direct_write;
- }
-
- /* clk rate info is needed for setup_data_interface */
- if (denali->clk_x_rate)
- chip->setup_data_interface = denali_setup_data_interface;
-
- ret = nand_scan_ident(mtd, denali->max_banks, NULL);
- if (ret)
- goto disable_irq;
-
- if (ioread32(denali->reg + FEATURES) & FEATURES__DMA)
- denali->dma_avail = 1;
-
- if (denali->dma_avail) {
- int dma_bit = denali->caps & DENALI_CAP_DMA_64BIT ? 64 : 32;
-
- ret = dma_set_mask(denali->dev, DMA_BIT_MASK(dma_bit));
- if (ret) {
- dev_info(denali->dev,
- "Failed to set DMA mask. Disabling DMA.\n");
- denali->dma_avail = 0;
- }
- }
-
- if (denali->dma_avail) {
- chip->options |= NAND_USE_BOUNCE_BUFFER;
- chip->buf_align = 16;
- if (denali->caps & DENALI_CAP_DMA_64BIT)
- denali->setup_dma = denali_setup_dma64;
- else
- denali->setup_dma = denali_setup_dma32;
- }
-
- chip->bbt_options |= NAND_BBT_USE_FLASH;
- chip->bbt_options |= NAND_BBT_NO_OOB;
- chip->ecc.mode = NAND_ECC_HW_SYNDROME;
- chip->options |= NAND_NO_SUBPAGE_WRITE;
-
- ret = denali_ecc_setup(mtd, chip, denali);
- if (ret) {
- dev_err(denali->dev, "Failed to setup ECC settings.\n");
- goto disable_irq;
- }
-
- dev_dbg(denali->dev,
- "chosen ECC settings: step=%d, strength=%d, bytes=%d\n",
- chip->ecc.size, chip->ecc.strength, chip->ecc.bytes);
-
- iowrite32(FIELD_PREP(ECC_CORRECTION__ERASE_THRESHOLD, 1) |
- FIELD_PREP(ECC_CORRECTION__VALUE, chip->ecc.strength),
- denali->reg + ECC_CORRECTION);
- iowrite32(mtd->erasesize / mtd->writesize,
- denali->reg + PAGES_PER_BLOCK);
- iowrite32(chip->options & NAND_BUSWIDTH_16 ? 1 : 0,
- denali->reg + DEVICE_WIDTH);
- iowrite32(chip->options & NAND_ROW_ADDR_3 ? 0 : TWO_ROW_ADDR_CYCLES__FLAG,
- denali->reg + TWO_ROW_ADDR_CYCLES);
- iowrite32(mtd->writesize, denali->reg + DEVICE_MAIN_AREA_SIZE);
- iowrite32(mtd->oobsize, denali->reg + DEVICE_SPARE_AREA_SIZE);
-
- iowrite32(chip->ecc.size, denali->reg + CFG_DATA_BLOCK_SIZE);
- iowrite32(chip->ecc.size, denali->reg + CFG_LAST_DATA_BLOCK_SIZE);
- /* chip->ecc.steps is set by nand_scan_tail(); not available here */
- iowrite32(mtd->writesize / chip->ecc.size,
- denali->reg + CFG_NUM_DATA_BLOCKS);
-
- mtd_set_ooblayout(mtd, &denali_ooblayout_ops);
-
- if (chip->options & NAND_BUSWIDTH_16) {
- chip->read_buf = denali_read_buf16;
- chip->write_buf = denali_write_buf16;
- } else {
- chip->read_buf = denali_read_buf;
- chip->write_buf = denali_write_buf;
- }
- chip->ecc.read_page = denali_read_page;
- chip->ecc.read_page_raw = denali_read_page_raw;
- chip->ecc.write_page = denali_write_page;
- chip->ecc.write_page_raw = denali_write_page_raw;
- chip->ecc.read_oob = denali_read_oob;
- chip->ecc.write_oob = denali_write_oob;
- chip->erase = denali_erase;
-
- ret = denali_multidev_fixup(denali);
- if (ret)
- goto disable_irq;
-
- /*
- * This buffer is DMA-mapped by denali_{read,write}_page_raw. Do not
- * use devm_kmalloc() because the memory allocated by devm_ does not
- * guarantee DMA-safe alignment.
- */
- denali->buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
- if (!denali->buf) {
- ret = -ENOMEM;
- goto disable_irq;
- }
-
- ret = nand_scan_tail(mtd);
- if (ret)
- goto free_buf;
-
- ret = mtd_device_register(mtd, NULL, 0);
- if (ret) {
- dev_err(denali->dev, "Failed to register MTD: %d\n", ret);
- goto free_buf;
- }
- return 0;
-
-free_buf:
- kfree(denali->buf);
-disable_irq:
- denali_disable_irq(denali);
-
- return ret;
-}
-EXPORT_SYMBOL(denali_init);
-
-void denali_remove(struct denali_nand_info *denali)
-{
- struct mtd_info *mtd = nand_to_mtd(&denali->nand);
-
- nand_release(mtd);
- kfree(denali->buf);
- denali_disable_irq(denali);
-}
-EXPORT_SYMBOL(denali_remove);
diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c
deleted file mode 100644
index 6bc93ea66f50..000000000000
--- a/drivers/mtd/nand/diskonchip.c
+++ /dev/null
@@ -1,1711 +0,0 @@
-/*
- * drivers/mtd/nand/diskonchip.c
- *
- * (C) 2003 Red Hat, Inc.
- * (C) 2004 Dan Brown <dan_brown@ieee.org>
- * (C) 2004 Kalev Lember <kalev@smartlink.ee>
- *
- * Author: David Woodhouse <dwmw2@infradead.org>
- * Additional Diskonchip 2000 and Millennium support by Dan Brown <dan_brown@ieee.org>
- * Diskonchip Millennium Plus support by Kalev Lember <kalev@smartlink.ee>
- *
- * Error correction code lifted from the old docecc code
- * Author: Fabrice Bellard (fabrice.bellard@netgem.com)
- * Copyright (C) 2000 Netgem S.A.
- * converted to the generic Reed-Solomon library by Thomas Gleixner <tglx@linutronix.de>
- *
- * Interface to generic NAND code for M-Systems DiskOnChip devices
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/delay.h>
-#include <linux/rslib.h>
-#include <linux/moduleparam.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/doc2000.h>
-#include <linux/mtd/partitions.h>
-#include <linux/mtd/inftl.h>
-#include <linux/module.h>
-
-/* Where to look for the devices? */
-#ifndef CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS
-#define CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS 0
-#endif
-
-static unsigned long doc_locations[] __initdata = {
-#if defined (__alpha__) || defined(__i386__) || defined(__x86_64__)
-#ifdef CONFIG_MTD_NAND_DISKONCHIP_PROBE_HIGH
- 0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000,
- 0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000,
- 0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000,
- 0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000,
- 0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000,
-#else
- 0xc8000, 0xca000, 0xcc000, 0xce000,
- 0xd0000, 0xd2000, 0xd4000, 0xd6000,
- 0xd8000, 0xda000, 0xdc000, 0xde000,
- 0xe0000, 0xe2000, 0xe4000, 0xe6000,
- 0xe8000, 0xea000, 0xec000, 0xee000,
-#endif
-#endif
- 0xffffffff };
-
-static struct mtd_info *doclist = NULL;
-
-struct doc_priv {
- void __iomem *virtadr;
- unsigned long physadr;
- u_char ChipID;
- u_char CDSNControl;
- int chips_per_floor; /* The number of chips detected on each floor */
- int curfloor;
- int curchip;
- int mh0_page;
- int mh1_page;
- struct mtd_info *nextdoc;
-
- /* Handle the last stage of initialization (BBT scan, partitioning) */
- int (*late_init)(struct mtd_info *mtd);
-};
-
-/* This is the ecc value computed by the HW ecc generator upon writing an empty
- page, one with all 0xff for data. */
-static u_char empty_write_ecc[6] = { 0x4b, 0x00, 0xe2, 0x0e, 0x93, 0xf7 };
-
-#define INFTL_BBT_RESERVED_BLOCKS 4
-
-#define DoC_is_MillenniumPlus(doc) ((doc)->ChipID == DOC_ChipID_DocMilPlus16 || (doc)->ChipID == DOC_ChipID_DocMilPlus32)
-#define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil)
-#define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k)
-
-static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd,
- unsigned int bitmask);
-static void doc200x_select_chip(struct mtd_info *mtd, int chip);
-
-static int debug = 0;
-module_param(debug, int, 0);
-
-static int try_dword = 1;
-module_param(try_dword, int, 0);
-
-static int no_ecc_failures = 0;
-module_param(no_ecc_failures, int, 0);
-
-static int no_autopart = 0;
-module_param(no_autopart, int, 0);
-
-static int show_firmware_partition = 0;
-module_param(show_firmware_partition, int, 0);
-
-#ifdef CONFIG_MTD_NAND_DISKONCHIP_BBTWRITE
-static int inftl_bbt_write = 1;
-#else
-static int inftl_bbt_write = 0;
-#endif
-module_param(inftl_bbt_write, int, 0);
-
-static unsigned long doc_config_location = CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS;
-module_param(doc_config_location, ulong, 0);
-MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip");
-
-/* Sector size for HW ECC */
-#define SECTOR_SIZE 512
-/* The sector bytes are packed into NB_DATA 10 bit words */
-#define NB_DATA (((SECTOR_SIZE + 1) * 8 + 6) / 10)
-/* Number of roots */
-#define NROOTS 4
-/* First consective root */
-#define FCR 510
-/* Number of symbols */
-#define NN 1023
-
-/* the Reed Solomon control structure */
-static struct rs_control *rs_decoder;
-
-/*
- * The HW decoder in the DoC ASIC's provides us a error syndrome,
- * which we must convert to a standard syndrome usable by the generic
- * Reed-Solomon library code.
- *
- * Fabrice Bellard figured this out in the old docecc code. I added
- * some comments, improved a minor bit and converted it to make use
- * of the generic Reed-Solomon library. tglx
- */
-static int doc_ecc_decode(struct rs_control *rs, uint8_t *data, uint8_t *ecc)
-{
- int i, j, nerr, errpos[8];
- uint8_t parity;
- uint16_t ds[4], s[5], tmp, errval[8], syn[4];
-
- memset(syn, 0, sizeof(syn));
- /* Convert the ecc bytes into words */
- ds[0] = ((ecc[4] & 0xff) >> 0) | ((ecc[5] & 0x03) << 8);
- ds[1] = ((ecc[5] & 0xfc) >> 2) | ((ecc[2] & 0x0f) << 6);
- ds[2] = ((ecc[2] & 0xf0) >> 4) | ((ecc[3] & 0x3f) << 4);
- ds[3] = ((ecc[3] & 0xc0) >> 6) | ((ecc[0] & 0xff) << 2);
- parity = ecc[1];
-
- /* Initialize the syndrome buffer */
- for (i = 0; i < NROOTS; i++)
- s[i] = ds[0];
- /*
- * Evaluate
- * s[i] = ds[3]x^3 + ds[2]x^2 + ds[1]x^1 + ds[0]
- * where x = alpha^(FCR + i)
- */
- for (j = 1; j < NROOTS; j++) {
- if (ds[j] == 0)
- continue;
- tmp = rs->index_of[ds[j]];
- for (i = 0; i < NROOTS; i++)
- s[i] ^= rs->alpha_to[rs_modnn(rs, tmp + (FCR + i) * j)];
- }
-
- /* Calc syn[i] = s[i] / alpha^(v + i) */
- for (i = 0; i < NROOTS; i++) {
- if (s[i])
- syn[i] = rs_modnn(rs, rs->index_of[s[i]] + (NN - FCR - i));
- }
- /* Call the decoder library */
- nerr = decode_rs16(rs, NULL, NULL, 1019, syn, 0, errpos, 0, errval);
-
- /* Incorrectable errors ? */
- if (nerr < 0)
- return nerr;
-
- /*
- * Correct the errors. The bitpositions are a bit of magic,
- * but they are given by the design of the de/encoder circuit
- * in the DoC ASIC's.
- */
- for (i = 0; i < nerr; i++) {
- int index, bitpos, pos = 1015 - errpos[i];
- uint8_t val;
- if (pos >= NB_DATA && pos < 1019)
- continue;
- if (pos < NB_DATA) {
- /* extract bit position (MSB first) */
- pos = 10 * (NB_DATA - 1 - pos) - 6;
- /* now correct the following 10 bits. At most two bytes
- can be modified since pos is even */
- index = (pos >> 3) ^ 1;
- bitpos = pos & 7;
- if ((index >= 0 && index < SECTOR_SIZE) || index == (SECTOR_SIZE + 1)) {
- val = (uint8_t) (errval[i] >> (2 + bitpos));
- parity ^= val;
- if (index < SECTOR_SIZE)
- data[index] ^= val;
- }
- index = ((pos >> 3) + 1) ^ 1;
- bitpos = (bitpos + 10) & 7;
- if (bitpos == 0)
- bitpos = 8;
- if ((index >= 0 && index < SECTOR_SIZE) || index == (SECTOR_SIZE + 1)) {
- val = (uint8_t) (errval[i] << (8 - bitpos));
- parity ^= val;
- if (index < SECTOR_SIZE)
- data[index] ^= val;
- }
- }
- }
- /* If the parity is wrong, no rescue possible */
- return parity ? -EBADMSG : nerr;
-}
-
-static void DoC_Delay(struct doc_priv *doc, unsigned short cycles)
-{
- volatile char dummy;
- int i;
-
- for (i = 0; i < cycles; i++) {
- if (DoC_is_Millennium(doc))
- dummy = ReadDOC(doc->virtadr, NOP);
- else if (DoC_is_MillenniumPlus(doc))
- dummy = ReadDOC(doc->virtadr, Mplus_NOP);
- else
- dummy = ReadDOC(doc->virtadr, DOCStatus);
- }
-
-}
-
-#define CDSN_CTRL_FR_B_MASK (CDSN_CTRL_FR_B0 | CDSN_CTRL_FR_B1)
-
-/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */
-static int _DoC_WaitReady(struct doc_priv *doc)
-{
- void __iomem *docptr = doc->virtadr;
- unsigned long timeo = jiffies + (HZ * 10);
-
- if (debug)
- printk("_DoC_WaitReady...\n");
- /* Out-of-line routine to wait for chip response */
- if (DoC_is_MillenniumPlus(doc)) {
- while ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) {
- if (time_after(jiffies, timeo)) {
- printk("_DoC_WaitReady timed out.\n");
- return -EIO;
- }
- udelay(1);
- cond_resched();
- }
- } else {
- while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) {
- if (time_after(jiffies, timeo)) {
- printk("_DoC_WaitReady timed out.\n");
- return -EIO;
- }
- udelay(1);
- cond_resched();
- }
- }
-
- return 0;
-}
-
-static inline int DoC_WaitReady(struct doc_priv *doc)
-{
- void __iomem *docptr = doc->virtadr;
- int ret = 0;
-
- if (DoC_is_MillenniumPlus(doc)) {
- DoC_Delay(doc, 4);
-
- if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK)
- /* Call the out-of-line routine to wait */
- ret = _DoC_WaitReady(doc);
- } else {
- DoC_Delay(doc, 4);
-
- if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B))
- /* Call the out-of-line routine to wait */
- ret = _DoC_WaitReady(doc);
- DoC_Delay(doc, 2);
- }
-
- if (debug)
- printk("DoC_WaitReady OK\n");
- return ret;
-}
-
-static void doc2000_write_byte(struct mtd_info *mtd, u_char datum)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
- void __iomem *docptr = doc->virtadr;
-
- if (debug)
- printk("write_byte %02x\n", datum);
- WriteDOC(datum, docptr, CDSNSlowIO);
- WriteDOC(datum, docptr, 2k_CDSN_IO);
-}
-
-static u_char doc2000_read_byte(struct mtd_info *mtd)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
- void __iomem *docptr = doc->virtadr;
- u_char ret;
-
- ReadDOC(docptr, CDSNSlowIO);
- DoC_Delay(doc, 2);
- ret = ReadDOC(docptr, 2k_CDSN_IO);
- if (debug)
- printk("read_byte returns %02x\n", ret);
- return ret;
-}
-
-static void doc2000_writebuf(struct mtd_info *mtd, const u_char *buf, int len)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
- void __iomem *docptr = doc->virtadr;
- int i;
- if (debug)
- printk("writebuf of %d bytes: ", len);
- for (i = 0; i < len; i++) {
- WriteDOC_(buf[i], docptr, DoC_2k_CDSN_IO + i);
- if (debug && i < 16)
- printk("%02x ", buf[i]);
- }
- if (debug)
- printk("\n");
-}
-
-static void doc2000_readbuf(struct mtd_info *mtd, u_char *buf, int len)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
- void __iomem *docptr = doc->virtadr;
- int i;
-
- if (debug)
- printk("readbuf of %d bytes: ", len);
-
- for (i = 0; i < len; i++) {
- buf[i] = ReadDOC(docptr, 2k_CDSN_IO + i);
- }
-}
-
-static void doc2000_readbuf_dword(struct mtd_info *mtd, u_char *buf, int len)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
- void __iomem *docptr = doc->virtadr;
- int i;
-
- if (debug)
- printk("readbuf_dword of %d bytes: ", len);
-
- if (unlikely((((unsigned long)buf) | len) & 3)) {
- for (i = 0; i < len; i++) {
- *(uint8_t *) (&buf[i]) = ReadDOC(docptr, 2k_CDSN_IO + i);
- }
- } else {
- for (i = 0; i < len; i += 4) {
- *(uint32_t *) (&buf[i]) = readl(docptr + DoC_2k_CDSN_IO + i);
- }
- }
-}
-
-static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
- uint16_t ret;
-
- doc200x_select_chip(mtd, nr);
- doc200x_hwcontrol(mtd, NAND_CMD_READID,
- NAND_CTRL_CLE | NAND_CTRL_CHANGE);
- doc200x_hwcontrol(mtd, 0, NAND_CTRL_ALE | NAND_CTRL_CHANGE);
- doc200x_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
-
- /* We can't use dev_ready here, but at least we wait for the
- * command to complete
- */
- udelay(50);
-
- ret = this->read_byte(mtd) << 8;
- ret |= this->read_byte(mtd);
-
- if (doc->ChipID == DOC_ChipID_Doc2k && try_dword && !nr) {
- /* First chip probe. See if we get same results by 32-bit access */
- union {
- uint32_t dword;
- uint8_t byte[4];
- } ident;
- void __iomem *docptr = doc->virtadr;
-
- doc200x_hwcontrol(mtd, NAND_CMD_READID,
- NAND_CTRL_CLE | NAND_CTRL_CHANGE);
- doc200x_hwcontrol(mtd, 0, NAND_CTRL_ALE | NAND_CTRL_CHANGE);
- doc200x_hwcontrol(mtd, NAND_CMD_NONE,
- NAND_NCE | NAND_CTRL_CHANGE);
-
- udelay(50);
-
- ident.dword = readl(docptr + DoC_2k_CDSN_IO);
- if (((ident.byte[0] << 8) | ident.byte[1]) == ret) {
- printk(KERN_INFO "DiskOnChip 2000 responds to DWORD access\n");
- this->read_buf = &doc2000_readbuf_dword;
- }
- }
-
- return ret;
-}
-
-static void __init doc2000_count_chips(struct mtd_info *mtd)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
- uint16_t mfrid;
- int i;
-
- /* Max 4 chips per floor on DiskOnChip 2000 */
- doc->chips_per_floor = 4;
-
- /* Find out what the first chip is */
- mfrid = doc200x_ident_chip(mtd, 0);
-
- /* Find how many chips in each floor. */
- for (i = 1; i < 4; i++) {
- if (doc200x_ident_chip(mtd, i) != mfrid)
- break;
- }
- doc->chips_per_floor = i;
- printk(KERN_DEBUG "Detected %d chips per floor.\n", i);
-}
-
-static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this)
-{
- struct doc_priv *doc = nand_get_controller_data(this);
-
- int status;
-
- DoC_WaitReady(doc);
- nand_status_op(this, NULL);
- DoC_WaitReady(doc);
- status = (int)this->read_byte(mtd);
-
- return status;
-}
-
-static void doc2001_write_byte(struct mtd_info *mtd, u_char datum)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
- void __iomem *docptr = doc->virtadr;
-
- WriteDOC(datum, docptr, CDSNSlowIO);
- WriteDOC(datum, docptr, Mil_CDSN_IO);
- WriteDOC(datum, docptr, WritePipeTerm);
-}
-
-static u_char doc2001_read_byte(struct mtd_info *mtd)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
- void __iomem *docptr = doc->virtadr;
-
- //ReadDOC(docptr, CDSNSlowIO);
- /* 11.4.5 -- delay twice to allow extended length cycle */
- DoC_Delay(doc, 2);
- ReadDOC(docptr, ReadPipeInit);
- //return ReadDOC(docptr, Mil_CDSN_IO);
- return ReadDOC(docptr, LastDataRead);
-}
-
-static void doc2001_writebuf(struct mtd_info *mtd, const u_char *buf, int len)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
- void __iomem *docptr = doc->virtadr;
- int i;
-
- for (i = 0; i < len; i++)
- WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i);
- /* Terminate write pipeline */
- WriteDOC(0x00, docptr, WritePipeTerm);
-}
-
-static void doc2001_readbuf(struct mtd_info *mtd, u_char *buf, int len)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
- void __iomem *docptr = doc->virtadr;
- int i;
-
- /* Start read pipeline */
- ReadDOC(docptr, ReadPipeInit);
-
- for (i = 0; i < len - 1; i++)
- buf[i] = ReadDOC(docptr, Mil_CDSN_IO + (i & 0xff));
-
- /* Terminate read pipeline */
- buf[i] = ReadDOC(docptr, LastDataRead);
-}
-
-static u_char doc2001plus_read_byte(struct mtd_info *mtd)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
- void __iomem *docptr = doc->virtadr;
- u_char ret;
-
- ReadDOC(docptr, Mplus_ReadPipeInit);
- ReadDOC(docptr, Mplus_ReadPipeInit);
- ret = ReadDOC(docptr, Mplus_LastDataRead);
- if (debug)
- printk("read_byte returns %02x\n", ret);
- return ret;
-}
-
-static void doc2001plus_writebuf(struct mtd_info *mtd, const u_char *buf, int len)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
- void __iomem *docptr = doc->virtadr;
- int i;
-
- if (debug)
- printk("writebuf of %d bytes: ", len);
- for (i = 0; i < len; i++) {
- WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i);
- if (debug && i < 16)
- printk("%02x ", buf[i]);
- }
- if (debug)
- printk("\n");
-}
-
-static void doc2001plus_readbuf(struct mtd_info *mtd, u_char *buf, int len)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
- void __iomem *docptr = doc->virtadr;
- int i;
-
- if (debug)
- printk("readbuf of %d bytes: ", len);
-
- /* Start read pipeline */
- ReadDOC(docptr, Mplus_ReadPipeInit);
- ReadDOC(docptr, Mplus_ReadPipeInit);
-
- for (i = 0; i < len - 2; i++) {
- buf[i] = ReadDOC(docptr, Mil_CDSN_IO);
- if (debug && i < 16)
- printk("%02x ", buf[i]);
- }
-
- /* Terminate read pipeline */
- buf[len - 2] = ReadDOC(docptr, Mplus_LastDataRead);
- if (debug && i < 16)
- printk("%02x ", buf[len - 2]);
- buf[len - 1] = ReadDOC(docptr, Mplus_LastDataRead);
- if (debug && i < 16)
- printk("%02x ", buf[len - 1]);
- if (debug)
- printk("\n");
-}
-
-static void doc2001plus_select_chip(struct mtd_info *mtd, int chip)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
- void __iomem *docptr = doc->virtadr;
- int floor = 0;
-
- if (debug)
- printk("select chip (%d)\n", chip);
-
- if (chip == -1) {
- /* Disable flash internally */
- WriteDOC(0, docptr, Mplus_FlashSelect);
- return;
- }
-
- floor = chip / doc->chips_per_floor;
- chip -= (floor * doc->chips_per_floor);
-
- /* Assert ChipEnable and deassert WriteProtect */
- WriteDOC((DOC_FLASH_CE), docptr, Mplus_FlashSelect);
- nand_reset_op(this);
-
- doc->curchip = chip;
- doc->curfloor = floor;
-}
-
-static void doc200x_select_chip(struct mtd_info *mtd, int chip)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
- void __iomem *docptr = doc->virtadr;
- int floor = 0;
-
- if (debug)
- printk("select chip (%d)\n", chip);
-
- if (chip == -1)
- return;
-
- floor = chip / doc->chips_per_floor;
- chip -= (floor * doc->chips_per_floor);
-
- /* 11.4.4 -- deassert CE before changing chip */
- doc200x_hwcontrol(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
-
- WriteDOC(floor, docptr, FloorSelect);
- WriteDOC(chip, docptr, CDSNDeviceSelect);
-
- doc200x_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
-
- doc->curchip = chip;
- doc->curfloor = floor;
-}
-
-#define CDSN_CTRL_MSK (CDSN_CTRL_CE | CDSN_CTRL_CLE | CDSN_CTRL_ALE)
-
-static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd,
- unsigned int ctrl)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
- void __iomem *docptr = doc->virtadr;
-
- if (ctrl & NAND_CTRL_CHANGE) {
- doc->CDSNControl &= ~CDSN_CTRL_MSK;
- doc->CDSNControl |= ctrl & CDSN_CTRL_MSK;
- if (debug)
- printk("hwcontrol(%d): %02x\n", cmd, doc->CDSNControl);
- WriteDOC(doc->CDSNControl, docptr, CDSNControl);
- /* 11.4.3 -- 4 NOPs after CSDNControl write */
- DoC_Delay(doc, 4);
- }
- if (cmd != NAND_CMD_NONE) {
- if (DoC_is_2000(doc))
- doc2000_write_byte(mtd, cmd);
- else
- doc2001_write_byte(mtd, cmd);
- }
-}
-
-static void doc2001plus_command(struct mtd_info *mtd, unsigned command, int column, int page_addr)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
- void __iomem *docptr = doc->virtadr;
-
- /*
- * Must terminate write pipeline before sending any commands
- * to the device.
- */
- if (command == NAND_CMD_PAGEPROG) {
- WriteDOC(0x00, docptr, Mplus_WritePipeTerm);
- WriteDOC(0x00, docptr, Mplus_WritePipeTerm);
- }
-
- /*
- * Write out the command to the device.
- */
- if (command == NAND_CMD_SEQIN) {
- int readcmd;
-
- if (column >= mtd->writesize) {
- /* OOB area */
- column -= mtd->writesize;
- readcmd = NAND_CMD_READOOB;
- } else if (column < 256) {
- /* First 256 bytes --> READ0 */
- readcmd = NAND_CMD_READ0;
- } else {
- column -= 256;
- readcmd = NAND_CMD_READ1;
- }
- WriteDOC(readcmd, docptr, Mplus_FlashCmd);
- }
- WriteDOC(command, docptr, Mplus_FlashCmd);
- WriteDOC(0, docptr, Mplus_WritePipeTerm);
- WriteDOC(0, docptr, Mplus_WritePipeTerm);
-
- if (column != -1 || page_addr != -1) {
- /* Serially input address */
- if (column != -1) {
- /* Adjust columns for 16 bit buswidth */
- if (this->options & NAND_BUSWIDTH_16 &&
- !nand_opcode_8bits(command))
- column >>= 1;
- WriteDOC(column, docptr, Mplus_FlashAddress);
- }
- if (page_addr != -1) {
- WriteDOC((unsigned char)(page_addr & 0xff), docptr, Mplus_FlashAddress);
- WriteDOC((unsigned char)((page_addr >> 8) & 0xff), docptr, Mplus_FlashAddress);
- if (this->options & NAND_ROW_ADDR_3) {
- WriteDOC((unsigned char)((page_addr >> 16) & 0x0f), docptr, Mplus_FlashAddress);
- printk("high density\n");
- }
- }
- WriteDOC(0, docptr, Mplus_WritePipeTerm);
- WriteDOC(0, docptr, Mplus_WritePipeTerm);
- /* deassert ALE */
- if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 ||
- command == NAND_CMD_READOOB || command == NAND_CMD_READID)
- WriteDOC(0, docptr, Mplus_FlashControl);
- }
-
- /*
- * program and erase have their own busy handlers
- * status and sequential in needs no delay
- */
- switch (command) {
-
- case NAND_CMD_PAGEPROG:
- case NAND_CMD_ERASE1:
- case NAND_CMD_ERASE2:
- case NAND_CMD_SEQIN:
- case NAND_CMD_STATUS:
- return;
-
- case NAND_CMD_RESET:
- if (this->dev_ready)
- break;
- udelay(this->chip_delay);
- WriteDOC(NAND_CMD_STATUS, docptr, Mplus_FlashCmd);
- WriteDOC(0, docptr, Mplus_WritePipeTerm);
- WriteDOC(0, docptr, Mplus_WritePipeTerm);
- while (!(this->read_byte(mtd) & 0x40)) ;
- return;
-
- /* This applies to read commands */
- default:
- /*
- * If we don't have access to the busy pin, we apply the given
- * command delay
- */
- if (!this->dev_ready) {
- udelay(this->chip_delay);
- return;
- }
- }
-
- /* Apply this short delay always to ensure that we do wait tWB in
- * any case on any machine. */
- ndelay(100);
- /* wait until command is processed */
- while (!this->dev_ready(mtd)) ;
-}
-
-static int doc200x_dev_ready(struct mtd_info *mtd)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
- void __iomem *docptr = doc->virtadr;
-
- if (DoC_is_MillenniumPlus(doc)) {
- /* 11.4.2 -- must NOP four times before checking FR/B# */
- DoC_Delay(doc, 4);
- if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) {
- if (debug)
- printk("not ready\n");
- return 0;
- }
- if (debug)
- printk("was ready\n");
- return 1;
- } else {
- /* 11.4.2 -- must NOP four times before checking FR/B# */
- DoC_Delay(doc, 4);
- if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) {
- if (debug)
- printk("not ready\n");
- return 0;
- }
- /* 11.4.2 -- Must NOP twice if it's ready */
- DoC_Delay(doc, 2);
- if (debug)
- printk("was ready\n");
- return 1;
- }
-}
-
-static int doc200x_block_bad(struct mtd_info *mtd, loff_t ofs)
-{
- /* This is our last resort if we couldn't find or create a BBT. Just
- pretend all blocks are good. */
- return 0;
-}
-
-static void doc200x_enable_hwecc(struct mtd_info *mtd, int mode)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
- void __iomem *docptr = doc->virtadr;
-
- /* Prime the ECC engine */
- switch (mode) {
- case NAND_ECC_READ:
- WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
- WriteDOC(DOC_ECC_EN, docptr, ECCConf);
- break;
- case NAND_ECC_WRITE:
- WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
- WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf);
- break;
- }
-}
-
-static void doc2001plus_enable_hwecc(struct mtd_info *mtd, int mode)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
- void __iomem *docptr = doc->virtadr;
-
- /* Prime the ECC engine */
- switch (mode) {
- case NAND_ECC_READ:
- WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);
- WriteDOC(DOC_ECC_EN, docptr, Mplus_ECCConf);
- break;
- case NAND_ECC_WRITE:
- WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);
- WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, Mplus_ECCConf);
- break;
- }
-}
-
-/* This code is only called on write */
-static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat, unsigned char *ecc_code)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
- void __iomem *docptr = doc->virtadr;
- int i;
- int emptymatch = 1;
-
- /* flush the pipeline */
- if (DoC_is_2000(doc)) {
- WriteDOC(doc->CDSNControl & ~CDSN_CTRL_FLASH_IO, docptr, CDSNControl);
- WriteDOC(0, docptr, 2k_CDSN_IO);
- WriteDOC(0, docptr, 2k_CDSN_IO);
- WriteDOC(0, docptr, 2k_CDSN_IO);
- WriteDOC(doc->CDSNControl, docptr, CDSNControl);
- } else if (DoC_is_MillenniumPlus(doc)) {
- WriteDOC(0, docptr, Mplus_NOP);
- WriteDOC(0, docptr, Mplus_NOP);
- WriteDOC(0, docptr, Mplus_NOP);
- } else {
- WriteDOC(0, docptr, NOP);
- WriteDOC(0, docptr, NOP);
- WriteDOC(0, docptr, NOP);
- }
-
- for (i = 0; i < 6; i++) {
- if (DoC_is_MillenniumPlus(doc))
- ecc_code[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i);
- else
- ecc_code[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i);
- if (ecc_code[i] != empty_write_ecc[i])
- emptymatch = 0;
- }
- if (DoC_is_MillenniumPlus(doc))
- WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf);
- else
- WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
-#if 0
- /* If emptymatch=1, we might have an all-0xff data buffer. Check. */
- if (emptymatch) {
- /* Note: this somewhat expensive test should not be triggered
- often. It could be optimized away by examining the data in
- the writebuf routine, and remembering the result. */
- for (i = 0; i < 512; i++) {
- if (dat[i] == 0xff)
- continue;
- emptymatch = 0;
- break;
- }
- }
- /* If emptymatch still =1, we do have an all-0xff data buffer.
- Return all-0xff ecc value instead of the computed one, so
- it'll look just like a freshly-erased page. */
- if (emptymatch)
- memset(ecc_code, 0xff, 6);
-#endif
- return 0;
-}
-
-static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat,
- u_char *read_ecc, u_char *isnull)
-{
- int i, ret = 0;
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
- void __iomem *docptr = doc->virtadr;
- uint8_t calc_ecc[6];
- volatile u_char dummy;
-
- /* flush the pipeline */
- if (DoC_is_2000(doc)) {
- dummy = ReadDOC(docptr, 2k_ECCStatus);
- dummy = ReadDOC(docptr, 2k_ECCStatus);
- dummy = ReadDOC(docptr, 2k_ECCStatus);
- } else if (DoC_is_MillenniumPlus(doc)) {
- dummy = ReadDOC(docptr, Mplus_ECCConf);
- dummy = ReadDOC(docptr, Mplus_ECCConf);
- dummy = ReadDOC(docptr, Mplus_ECCConf);
- } else {
- dummy = ReadDOC(docptr, ECCConf);
- dummy = ReadDOC(docptr, ECCConf);
- dummy = ReadDOC(docptr, ECCConf);
- }
-
- /* Error occurred ? */
- if (dummy & 0x80) {
- for (i = 0; i < 6; i++) {
- if (DoC_is_MillenniumPlus(doc))
- calc_ecc[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i);
- else
- calc_ecc[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i);
- }
-
- ret = doc_ecc_decode(rs_decoder, dat, calc_ecc);
- if (ret > 0)
- printk(KERN_ERR "doc200x_correct_data corrected %d errors\n", ret);
- }
- if (DoC_is_MillenniumPlus(doc))
- WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf);
- else
- WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
- if (no_ecc_failures && mtd_is_eccerr(ret)) {
- printk(KERN_ERR "suppressing ECC failure\n");
- ret = 0;
- }
- return ret;
-}
-
-//u_char mydatabuf[528];
-
-static int doc200x_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- if (section)
- return -ERANGE;
-
- oobregion->offset = 0;
- oobregion->length = 6;
-
- return 0;
-}
-
-static int doc200x_ooblayout_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- if (section > 1)
- return -ERANGE;
-
- /*
- * The strange out-of-order free bytes definition is a (possibly
- * unneeded) attempt to retain compatibility. It used to read:
- * .oobfree = { {8, 8} }
- * Since that leaves two bytes unusable, it was changed. But the
- * following scheme might affect existing jffs2 installs by moving the
- * cleanmarker:
- * .oobfree = { {6, 10} }
- * jffs2 seems to handle the above gracefully, but the current scheme
- * seems safer. The only problem with it is that any code retrieving
- * free bytes position must be able to handle out-of-order segments.
- */
- if (!section) {
- oobregion->offset = 8;
- oobregion->length = 8;
- } else {
- oobregion->offset = 6;
- oobregion->length = 2;
- }
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops doc200x_ooblayout_ops = {
- .ecc = doc200x_ooblayout_ecc,
- .free = doc200x_ooblayout_free,
-};
-
-/* Find the (I)NFTL Media Header, and optionally also the mirror media header.
- On successful return, buf will contain a copy of the media header for
- further processing. id is the string to scan for, and will presumably be
- either "ANAND" or "BNAND". If findmirror=1, also look for the mirror media
- header. The page #s of the found media headers are placed in mh0_page and
- mh1_page in the DOC private structure. */
-static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, const char *id, int findmirror)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
- unsigned offs;
- int ret;
- size_t retlen;
-
- for (offs = 0; offs < mtd->size; offs += mtd->erasesize) {
- ret = mtd_read(mtd, offs, mtd->writesize, &retlen, buf);
- if (retlen != mtd->writesize)
- continue;
- if (ret) {
- printk(KERN_WARNING "ECC error scanning DOC at 0x%x\n", offs);
- }
- if (memcmp(buf, id, 6))
- continue;
- printk(KERN_INFO "Found DiskOnChip %s Media Header at 0x%x\n", id, offs);
- if (doc->mh0_page == -1) {
- doc->mh0_page = offs >> this->page_shift;
- if (!findmirror)
- return 1;
- continue;
- }
- doc->mh1_page = offs >> this->page_shift;
- return 2;
- }
- if (doc->mh0_page == -1) {
- printk(KERN_WARNING "DiskOnChip %s Media Header not found.\n", id);
- return 0;
- }
- /* Only one mediaheader was found. We want buf to contain a
- mediaheader on return, so we'll have to re-read the one we found. */
- offs = doc->mh0_page << this->page_shift;
- ret = mtd_read(mtd, offs, mtd->writesize, &retlen, buf);
- if (retlen != mtd->writesize) {
- /* Insanity. Give up. */
- printk(KERN_ERR "Read DiskOnChip Media Header once, but can't reread it???\n");
- return 0;
- }
- return 1;
-}
-
-static inline int __init nftl_partscan(struct mtd_info *mtd, struct mtd_partition *parts)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
- int ret = 0;
- u_char *buf;
- struct NFTLMediaHeader *mh;
- const unsigned psize = 1 << this->page_shift;
- int numparts = 0;
- unsigned blocks, maxblocks;
- int offs, numheaders;
-
- buf = kmalloc(mtd->writesize, GFP_KERNEL);
- if (!buf) {
- return 0;
- }
- if (!(numheaders = find_media_headers(mtd, buf, "ANAND", 1)))
- goto out;
- mh = (struct NFTLMediaHeader *)buf;
-
- le16_to_cpus(&mh->NumEraseUnits);
- le16_to_cpus(&mh->FirstPhysicalEUN);
- le32_to_cpus(&mh->FormattedSize);
-
- printk(KERN_INFO " DataOrgID = %s\n"
- " NumEraseUnits = %d\n"
- " FirstPhysicalEUN = %d\n"
- " FormattedSize = %d\n"
- " UnitSizeFactor = %d\n",
- mh->DataOrgID, mh->NumEraseUnits,
- mh->FirstPhysicalEUN, mh->FormattedSize,
- mh->UnitSizeFactor);
-
- blocks = mtd->size >> this->phys_erase_shift;
- maxblocks = min(32768U, mtd->erasesize - psize);
-
- if (mh->UnitSizeFactor == 0x00) {
- /* Auto-determine UnitSizeFactor. The constraints are:
- - There can be at most 32768 virtual blocks.
- - There can be at most (virtual block size - page size)
- virtual blocks (because MediaHeader+BBT must fit in 1).
- */
- mh->UnitSizeFactor = 0xff;
- while (blocks > maxblocks) {
- blocks >>= 1;
- maxblocks = min(32768U, (maxblocks << 1) + psize);
- mh->UnitSizeFactor--;
- }
- printk(KERN_WARNING "UnitSizeFactor=0x00 detected. Correct value is assumed to be 0x%02x.\n", mh->UnitSizeFactor);
- }
-
- /* NOTE: The lines below modify internal variables of the NAND and MTD
- layers; variables with have already been configured by nand_scan.
- Unfortunately, we didn't know before this point what these values
- should be. Thus, this code is somewhat dependent on the exact
- implementation of the NAND layer. */
- if (mh->UnitSizeFactor != 0xff) {
- this->bbt_erase_shift += (0xff - mh->UnitSizeFactor);
- mtd->erasesize <<= (0xff - mh->UnitSizeFactor);
- printk(KERN_INFO "Setting virtual erase size to %d\n", mtd->erasesize);
- blocks = mtd->size >> this->bbt_erase_shift;
- maxblocks = min(32768U, mtd->erasesize - psize);
- }
-
- if (blocks > maxblocks) {
- printk(KERN_ERR "UnitSizeFactor of 0x%02x is inconsistent with device size. Aborting.\n", mh->UnitSizeFactor);
- goto out;
- }
-
- /* Skip past the media headers. */
- offs = max(doc->mh0_page, doc->mh1_page);
- offs <<= this->page_shift;
- offs += mtd->erasesize;
-
- if (show_firmware_partition == 1) {
- parts[0].name = " DiskOnChip Firmware / Media Header partition";
- parts[0].offset = 0;
- parts[0].size = offs;
- numparts = 1;
- }
-
- parts[numparts].name = " DiskOnChip BDTL partition";
- parts[numparts].offset = offs;
- parts[numparts].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift;
-
- offs += parts[numparts].size;
- numparts++;
-
- if (offs < mtd->size) {
- parts[numparts].name = " DiskOnChip Remainder partition";
- parts[numparts].offset = offs;
- parts[numparts].size = mtd->size - offs;
- numparts++;
- }
-
- ret = numparts;
- out:
- kfree(buf);
- return ret;
-}
-
-/* This is a stripped-down copy of the code in inftlmount.c */
-static inline int __init inftl_partscan(struct mtd_info *mtd, struct mtd_partition *parts)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
- int ret = 0;
- u_char *buf;
- struct INFTLMediaHeader *mh;
- struct INFTLPartition *ip;
- int numparts = 0;
- int blocks;
- int vshift, lastvunit = 0;
- int i;
- int end = mtd->size;
-
- if (inftl_bbt_write)
- end -= (INFTL_BBT_RESERVED_BLOCKS << this->phys_erase_shift);
-
- buf = kmalloc(mtd->writesize, GFP_KERNEL);
- if (!buf) {
- return 0;
- }
-
- if (!find_media_headers(mtd, buf, "BNAND", 0))
- goto out;
- doc->mh1_page = doc->mh0_page + (4096 >> this->page_shift);
- mh = (struct INFTLMediaHeader *)buf;
-
- le32_to_cpus(&mh->NoOfBootImageBlocks);
- le32_to_cpus(&mh->NoOfBinaryPartitions);
- le32_to_cpus(&mh->NoOfBDTLPartitions);
- le32_to_cpus(&mh->BlockMultiplierBits);
- le32_to_cpus(&mh->FormatFlags);
- le32_to_cpus(&mh->PercentUsed);
-
- printk(KERN_INFO " bootRecordID = %s\n"
- " NoOfBootImageBlocks = %d\n"
- " NoOfBinaryPartitions = %d\n"
- " NoOfBDTLPartitions = %d\n"
- " BlockMultiplerBits = %d\n"
- " FormatFlgs = %d\n"
- " OsakVersion = %d.%d.%d.%d\n"
- " PercentUsed = %d\n",
- mh->bootRecordID, mh->NoOfBootImageBlocks,
- mh->NoOfBinaryPartitions,
- mh->NoOfBDTLPartitions,
- mh->BlockMultiplierBits, mh->FormatFlags,
- ((unsigned char *) &mh->OsakVersion)[0] & 0xf,
- ((unsigned char *) &mh->OsakVersion)[1] & 0xf,
- ((unsigned char *) &mh->OsakVersion)[2] & 0xf,
- ((unsigned char *) &mh->OsakVersion)[3] & 0xf,
- mh->PercentUsed);
-
- vshift = this->phys_erase_shift + mh->BlockMultiplierBits;
-
- blocks = mtd->size >> vshift;
- if (blocks > 32768) {
- printk(KERN_ERR "BlockMultiplierBits=%d is inconsistent with device size. Aborting.\n", mh->BlockMultiplierBits);
- goto out;
- }
-
- blocks = doc->chips_per_floor << (this->chip_shift - this->phys_erase_shift);
- if (inftl_bbt_write && (blocks > mtd->erasesize)) {
- printk(KERN_ERR "Writeable BBTs spanning more than one erase block are not yet supported. FIX ME!\n");
- goto out;
- }
-
- /* Scan the partitions */
- for (i = 0; (i < 4); i++) {
- ip = &(mh->Partitions[i]);
- le32_to_cpus(&ip->virtualUnits);
- le32_to_cpus(&ip->firstUnit);
- le32_to_cpus(&ip->lastUnit);
- le32_to_cpus(&ip->flags);
- le32_to_cpus(&ip->spareUnits);
- le32_to_cpus(&ip->Reserved0);
-
- printk(KERN_INFO " PARTITION[%d] ->\n"
- " virtualUnits = %d\n"
- " firstUnit = %d\n"
- " lastUnit = %d\n"
- " flags = 0x%x\n"
- " spareUnits = %d\n",
- i, ip->virtualUnits, ip->firstUnit,
- ip->lastUnit, ip->flags,
- ip->spareUnits);
-
- if ((show_firmware_partition == 1) &&
- (i == 0) && (ip->firstUnit > 0)) {
- parts[0].name = " DiskOnChip IPL / Media Header partition";
- parts[0].offset = 0;
- parts[0].size = mtd->erasesize * ip->firstUnit;
- numparts = 1;
- }
-
- if (ip->flags & INFTL_BINARY)
- parts[numparts].name = " DiskOnChip BDK partition";
- else
- parts[numparts].name = " DiskOnChip BDTL partition";
- parts[numparts].offset = ip->firstUnit << vshift;
- parts[numparts].size = (1 + ip->lastUnit - ip->firstUnit) << vshift;
- numparts++;
- if (ip->lastUnit > lastvunit)
- lastvunit = ip->lastUnit;
- if (ip->flags & INFTL_LAST)
- break;
- }
- lastvunit++;
- if ((lastvunit << vshift) < end) {
- parts[numparts].name = " DiskOnChip Remainder partition";
- parts[numparts].offset = lastvunit << vshift;
- parts[numparts].size = end - parts[numparts].offset;
- numparts++;
- }
- ret = numparts;
- out:
- kfree(buf);
- return ret;
-}
-
-static int __init nftl_scan_bbt(struct mtd_info *mtd)
-{
- int ret, numparts;
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
- struct mtd_partition parts[2];
-
- memset((char *)parts, 0, sizeof(parts));
- /* On NFTL, we have to find the media headers before we can read the
- BBTs, since they're stored in the media header eraseblocks. */
- numparts = nftl_partscan(mtd, parts);
- if (!numparts)
- return -EIO;
- this->bbt_td->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT |
- NAND_BBT_SAVECONTENT | NAND_BBT_WRITE |
- NAND_BBT_VERSION;
- this->bbt_td->veroffs = 7;
- this->bbt_td->pages[0] = doc->mh0_page + 1;
- if (doc->mh1_page != -1) {
- this->bbt_md->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT |
- NAND_BBT_SAVECONTENT | NAND_BBT_WRITE |
- NAND_BBT_VERSION;
- this->bbt_md->veroffs = 7;
- this->bbt_md->pages[0] = doc->mh1_page + 1;
- } else {
- this->bbt_md = NULL;
- }
-
- ret = this->scan_bbt(mtd);
- if (ret)
- return ret;
-
- return mtd_device_register(mtd, parts, no_autopart ? 0 : numparts);
-}
-
-static int __init inftl_scan_bbt(struct mtd_info *mtd)
-{
- int ret, numparts;
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
- struct mtd_partition parts[5];
-
- if (this->numchips > doc->chips_per_floor) {
- printk(KERN_ERR "Multi-floor INFTL devices not yet supported.\n");
- return -EIO;
- }
-
- if (DoC_is_MillenniumPlus(doc)) {
- this->bbt_td->options = NAND_BBT_2BIT | NAND_BBT_ABSPAGE;
- if (inftl_bbt_write)
- this->bbt_td->options |= NAND_BBT_WRITE;
- this->bbt_td->pages[0] = 2;
- this->bbt_md = NULL;
- } else {
- this->bbt_td->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | NAND_BBT_VERSION;
- if (inftl_bbt_write)
- this->bbt_td->options |= NAND_BBT_WRITE;
- this->bbt_td->offs = 8;
- this->bbt_td->len = 8;
- this->bbt_td->veroffs = 7;
- this->bbt_td->maxblocks = INFTL_BBT_RESERVED_BLOCKS;
- this->bbt_td->reserved_block_code = 0x01;
- this->bbt_td->pattern = "MSYS_BBT";
-
- this->bbt_md->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | NAND_BBT_VERSION;
- if (inftl_bbt_write)
- this->bbt_md->options |= NAND_BBT_WRITE;
- this->bbt_md->offs = 8;
- this->bbt_md->len = 8;
- this->bbt_md->veroffs = 7;
- this->bbt_md->maxblocks = INFTL_BBT_RESERVED_BLOCKS;
- this->bbt_md->reserved_block_code = 0x01;
- this->bbt_md->pattern = "TBB_SYSM";
- }
-
- ret = this->scan_bbt(mtd);
- if (ret)
- return ret;
-
- memset((char *)parts, 0, sizeof(parts));
- numparts = inftl_partscan(mtd, parts);
- /* At least for now, require the INFTL Media Header. We could probably
- do without it for non-INFTL use, since all it gives us is
- autopartitioning, but I want to give it more thought. */
- if (!numparts)
- return -EIO;
- return mtd_device_register(mtd, parts, no_autopart ? 0 : numparts);
-}
-
-static inline int __init doc2000_init(struct mtd_info *mtd)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
-
- this->read_byte = doc2000_read_byte;
- this->write_buf = doc2000_writebuf;
- this->read_buf = doc2000_readbuf;
- doc->late_init = nftl_scan_bbt;
-
- doc->CDSNControl = CDSN_CTRL_FLASH_IO | CDSN_CTRL_ECC_IO;
- doc2000_count_chips(mtd);
- mtd->name = "DiskOnChip 2000 (NFTL Model)";
- return (4 * doc->chips_per_floor);
-}
-
-static inline int __init doc2001_init(struct mtd_info *mtd)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
-
- this->read_byte = doc2001_read_byte;
- this->write_buf = doc2001_writebuf;
- this->read_buf = doc2001_readbuf;
-
- ReadDOC(doc->virtadr, ChipID);
- ReadDOC(doc->virtadr, ChipID);
- ReadDOC(doc->virtadr, ChipID);
- if (ReadDOC(doc->virtadr, ChipID) != DOC_ChipID_DocMil) {
- /* It's not a Millennium; it's one of the newer
- DiskOnChip 2000 units with a similar ASIC.
- Treat it like a Millennium, except that it
- can have multiple chips. */
- doc2000_count_chips(mtd);
- mtd->name = "DiskOnChip 2000 (INFTL Model)";
- doc->late_init = inftl_scan_bbt;
- return (4 * doc->chips_per_floor);
- } else {
- /* Bog-standard Millennium */
- doc->chips_per_floor = 1;
- mtd->name = "DiskOnChip Millennium";
- doc->late_init = nftl_scan_bbt;
- return 1;
- }
-}
-
-static inline int __init doc2001plus_init(struct mtd_info *mtd)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct doc_priv *doc = nand_get_controller_data(this);
-
- this->read_byte = doc2001plus_read_byte;
- this->write_buf = doc2001plus_writebuf;
- this->read_buf = doc2001plus_readbuf;
- doc->late_init = inftl_scan_bbt;
- this->cmd_ctrl = NULL;
- this->select_chip = doc2001plus_select_chip;
- this->cmdfunc = doc2001plus_command;
- this->ecc.hwctl = doc2001plus_enable_hwecc;
-
- doc->chips_per_floor = 1;
- mtd->name = "DiskOnChip Millennium Plus";
-
- return 1;
-}
-
-static int __init doc_probe(unsigned long physadr)
-{
- unsigned char ChipID;
- struct mtd_info *mtd;
- struct nand_chip *nand;
- struct doc_priv *doc;
- void __iomem *virtadr;
- unsigned char save_control;
- unsigned char tmp, tmpb, tmpc;
- int reg, len, numchips;
- int ret = 0;
-
- if (!request_mem_region(physadr, DOC_IOREMAP_LEN, "DiskOnChip"))
- return -EBUSY;
- virtadr = ioremap(physadr, DOC_IOREMAP_LEN);
- if (!virtadr) {
- printk(KERN_ERR "Diskonchip ioremap failed: 0x%x bytes at 0x%lx\n", DOC_IOREMAP_LEN, physadr);
- ret = -EIO;
- goto error_ioremap;
- }
-
- /* It's not possible to cleanly detect the DiskOnChip - the
- * bootup procedure will put the device into reset mode, and
- * it's not possible to talk to it without actually writing
- * to the DOCControl register. So we store the current contents
- * of the DOCControl register's location, in case we later decide
- * that it's not a DiskOnChip, and want to put it back how we
- * found it.
- */
- save_control = ReadDOC(virtadr, DOCControl);
-
- /* Reset the DiskOnChip ASIC */
- WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, virtadr, DOCControl);
- WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, virtadr, DOCControl);
-
- /* Enable the DiskOnChip ASIC */
- WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, virtadr, DOCControl);
- WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, virtadr, DOCControl);
-
- ChipID = ReadDOC(virtadr, ChipID);
-
- switch (ChipID) {
- case DOC_ChipID_Doc2k:
- reg = DoC_2k_ECCStatus;
- break;
- case DOC_ChipID_DocMil:
- reg = DoC_ECCConf;
- break;
- case DOC_ChipID_DocMilPlus16:
- case DOC_ChipID_DocMilPlus32:
- case 0:
- /* Possible Millennium Plus, need to do more checks */
- /* Possibly release from power down mode */
- for (tmp = 0; (tmp < 4); tmp++)
- ReadDOC(virtadr, Mplus_Power);
-
- /* Reset the Millennium Plus ASIC */
- tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT;
- WriteDOC(tmp, virtadr, Mplus_DOCControl);
- WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm);
-
- mdelay(1);
- /* Enable the Millennium Plus ASIC */
- tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT;
- WriteDOC(tmp, virtadr, Mplus_DOCControl);
- WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm);
- mdelay(1);
-
- ChipID = ReadDOC(virtadr, ChipID);
-
- switch (ChipID) {
- case DOC_ChipID_DocMilPlus16:
- reg = DoC_Mplus_Toggle;
- break;
- case DOC_ChipID_DocMilPlus32:
- printk(KERN_ERR "DiskOnChip Millennium Plus 32MB is not supported, ignoring.\n");
- default:
- ret = -ENODEV;
- goto notfound;
- }
- break;
-
- default:
- ret = -ENODEV;
- goto notfound;
- }
- /* Check the TOGGLE bit in the ECC register */
- tmp = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
- tmpb = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
- tmpc = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
- if ((tmp == tmpb) || (tmp != tmpc)) {
- printk(KERN_WARNING "Possible DiskOnChip at 0x%lx failed TOGGLE test, dropping.\n", physadr);
- ret = -ENODEV;
- goto notfound;
- }
-
- for (mtd = doclist; mtd; mtd = doc->nextdoc) {
- unsigned char oldval;
- unsigned char newval;
- nand = mtd_to_nand(mtd);
- doc = nand_get_controller_data(nand);
- /* Use the alias resolution register to determine if this is
- in fact the same DOC aliased to a new address. If writes
- to one chip's alias resolution register change the value on
- the other chip, they're the same chip. */
- if (ChipID == DOC_ChipID_DocMilPlus16) {
- oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution);
- newval = ReadDOC(virtadr, Mplus_AliasResolution);
- } else {
- oldval = ReadDOC(doc->virtadr, AliasResolution);
- newval = ReadDOC(virtadr, AliasResolution);
- }
- if (oldval != newval)
- continue;
- if (ChipID == DOC_ChipID_DocMilPlus16) {
- WriteDOC(~newval, virtadr, Mplus_AliasResolution);
- oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution);
- WriteDOC(newval, virtadr, Mplus_AliasResolution); // restore it
- } else {
- WriteDOC(~newval, virtadr, AliasResolution);
- oldval = ReadDOC(doc->virtadr, AliasResolution);
- WriteDOC(newval, virtadr, AliasResolution); // restore it
- }
- newval = ~newval;
- if (oldval == newval) {
- printk(KERN_DEBUG "Found alias of DOC at 0x%lx to 0x%lx\n", doc->physadr, physadr);
- goto notfound;
- }
- }
-
- printk(KERN_NOTICE "DiskOnChip found at 0x%lx\n", physadr);
-
- len = sizeof(struct nand_chip) + sizeof(struct doc_priv) +
- (2 * sizeof(struct nand_bbt_descr));
- nand = kzalloc(len, GFP_KERNEL);
- if (!nand) {
- ret = -ENOMEM;
- goto fail;
- }
-
- mtd = nand_to_mtd(nand);
- doc = (struct doc_priv *) (nand + 1);
- nand->bbt_td = (struct nand_bbt_descr *) (doc + 1);
- nand->bbt_md = nand->bbt_td + 1;
-
- mtd->owner = THIS_MODULE;
- mtd_set_ooblayout(mtd, &doc200x_ooblayout_ops);
-
- nand_set_controller_data(nand, doc);
- nand->select_chip = doc200x_select_chip;
- nand->cmd_ctrl = doc200x_hwcontrol;
- nand->dev_ready = doc200x_dev_ready;
- nand->waitfunc = doc200x_wait;
- nand->block_bad = doc200x_block_bad;
- nand->ecc.hwctl = doc200x_enable_hwecc;
- nand->ecc.calculate = doc200x_calculate_ecc;
- nand->ecc.correct = doc200x_correct_data;
-
- nand->ecc.mode = NAND_ECC_HW_SYNDROME;
- nand->ecc.size = 512;
- nand->ecc.bytes = 6;
- nand->ecc.strength = 2;
- nand->ecc.options = NAND_ECC_GENERIC_ERASED_CHECK;
- nand->bbt_options = NAND_BBT_USE_FLASH;
- /* Skip the automatic BBT scan so we can run it manually */
- nand->options |= NAND_SKIP_BBTSCAN;
-
- doc->physadr = physadr;
- doc->virtadr = virtadr;
- doc->ChipID = ChipID;
- doc->curfloor = -1;
- doc->curchip = -1;
- doc->mh0_page = -1;
- doc->mh1_page = -1;
- doc->nextdoc = doclist;
-
- if (ChipID == DOC_ChipID_Doc2k)
- numchips = doc2000_init(mtd);
- else if (ChipID == DOC_ChipID_DocMilPlus16)
- numchips = doc2001plus_init(mtd);
- else
- numchips = doc2001_init(mtd);
-
- if ((ret = nand_scan(mtd, numchips)) || (ret = doc->late_init(mtd))) {
- /* DBB note: i believe nand_release is necessary here, as
- buffers may have been allocated in nand_base. Check with
- Thomas. FIX ME! */
- /* nand_release will call mtd_device_unregister, but we
- haven't yet added it. This is handled without incident by
- mtd_device_unregister, as far as I can tell. */
- nand_release(mtd);
- kfree(nand);
- goto fail;
- }
-
- /* Success! */
- doclist = mtd;
- return 0;
-
- notfound:
- /* Put back the contents of the DOCControl register, in case it's not
- actually a DiskOnChip. */
- WriteDOC(save_control, virtadr, DOCControl);
- fail:
- iounmap(virtadr);
-
-error_ioremap:
- release_mem_region(physadr, DOC_IOREMAP_LEN);
-
- return ret;
-}
-
-static void release_nanddoc(void)
-{
- struct mtd_info *mtd, *nextmtd;
- struct nand_chip *nand;
- struct doc_priv *doc;
-
- for (mtd = doclist; mtd; mtd = nextmtd) {
- nand = mtd_to_nand(mtd);
- doc = nand_get_controller_data(nand);
-
- nextmtd = doc->nextdoc;
- nand_release(mtd);
- iounmap(doc->virtadr);
- release_mem_region(doc->physadr, DOC_IOREMAP_LEN);
- kfree(nand);
- }
-}
-
-static int __init init_nanddoc(void)
-{
- int i, ret = 0;
-
- /* We could create the decoder on demand, if memory is a concern.
- * This way we have it handy, if an error happens
- *
- * Symbolsize is 10 (bits)
- * Primitve polynomial is x^10+x^3+1
- * first consecutive root is 510
- * primitve element to generate roots = 1
- * generator polinomial degree = 4
- */
- rs_decoder = init_rs(10, 0x409, FCR, 1, NROOTS);
- if (!rs_decoder) {
- printk(KERN_ERR "DiskOnChip: Could not create a RS decoder\n");
- return -ENOMEM;
- }
-
- if (doc_config_location) {
- printk(KERN_INFO "Using configured DiskOnChip probe address 0x%lx\n", doc_config_location);
- ret = doc_probe(doc_config_location);
- if (ret < 0)
- goto outerr;
- } else {
- for (i = 0; (doc_locations[i] != 0xffffffff); i++) {
- doc_probe(doc_locations[i]);
- }
- }
- /* No banner message any more. Print a message if no DiskOnChip
- found, so the user knows we at least tried. */
- if (!doclist) {
- printk(KERN_INFO "No valid DiskOnChip devices found\n");
- ret = -ENODEV;
- goto outerr;
- }
- return 0;
- outerr:
- free_rs(rs_decoder);
- return ret;
-}
-
-static void __exit cleanup_nanddoc(void)
-{
- /* Cleanup the nand/DoC resources */
- release_nanddoc();
-
- /* Free the reed solomon resources */
- if (rs_decoder) {
- free_rs(rs_decoder);
- }
-}
-
-module_init(init_nanddoc);
-module_exit(cleanup_nanddoc);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
-MODULE_DESCRIPTION("M-Systems DiskOnChip 2000, Millennium and Millennium Plus device driver");
diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c
deleted file mode 100644
index 72f1327c4430..000000000000
--- a/drivers/mtd/nand/docg4.c
+++ /dev/null
@@ -1,1421 +0,0 @@
-/*
- * Copyright © 2012 Mike Dunn <mikedunn@newsguy.com>
- *
- * mtd nand driver for M-Systems DiskOnChip G4
- *
- * 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.
- *
- * Tested on the Palm Treo 680. The G4 is also present on Toshiba Portege, Asus
- * P526, some HTC smartphones (Wizard, Prophet, ...), O2 XDA Zinc, maybe others.
- * Should work on these as well. Let me know!
- *
- * TODO:
- *
- * Mechanism for management of password-protected areas
- *
- * Hamming ecc when reading oob only
- *
- * According to the M-Sys documentation, this device is also available in a
- * "dual-die" configuration having a 256MB capacity, but no mechanism for
- * detecting this variant is documented. Currently this driver assumes 128MB
- * capacity.
- *
- * Support for multiple cascaded devices ("floors"). Not sure which gadgets
- * contain multiple G4s in a cascaded configuration, if any.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/string.h>
-#include <linux/sched.h>
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/export.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
-#include <linux/bitops.h>
-#include <linux/mtd/partitions.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/bch.h>
-#include <linux/bitrev.h>
-#include <linux/jiffies.h>
-
-/*
- * In "reliable mode" consecutive 2k pages are used in parallel (in some
- * fashion) to store the same data. The data can be read back from the
- * even-numbered pages in the normal manner; odd-numbered pages will appear to
- * contain junk. Systems that boot from the docg4 typically write the secondary
- * program loader (SPL) code in this mode. The SPL is loaded by the initial
- * program loader (IPL, stored in the docg4's 2k NOR-like region that is mapped
- * to the reset vector address). This module parameter enables you to use this
- * driver to write the SPL. When in this mode, no more than 2k of data can be
- * written at a time, because the addresses do not increment in the normal
- * manner, and the starting offset must be within an even-numbered 2k region;
- * i.e., invalid starting offsets are 0x800, 0xa00, 0xc00, 0xe00, 0x1800,
- * 0x1a00, ... Reliable mode is a special case and should not be used unless
- * you know what you're doing.
- */
-static bool reliable_mode;
-module_param(reliable_mode, bool, 0);
-MODULE_PARM_DESC(reliable_mode, "pages are programmed in reliable mode");
-
-/*
- * You'll want to ignore badblocks if you're reading a partition that contains
- * data written by the TrueFFS library (i.e., by PalmOS, Windows, etc), since
- * it does not use mtd nand's method for marking bad blocks (using oob area).
- * This will also skip the check of the "page written" flag.
- */
-static bool ignore_badblocks;
-module_param(ignore_badblocks, bool, 0);
-MODULE_PARM_DESC(ignore_badblocks, "no badblock checking performed");
-
-struct docg4_priv {
- struct mtd_info *mtd;
- struct device *dev;
- void __iomem *virtadr;
- int status;
- struct {
- unsigned int command;
- int column;
- int page;
- } last_command;
- uint8_t oob_buf[16];
- uint8_t ecc_buf[7];
- int oob_page;
- struct bch_control *bch;
-};
-
-/*
- * Defines prefixed with DOCG4 are unique to the diskonchip G4. All others are
- * shared with other diskonchip devices (P3, G3 at least).
- *
- * Functions with names prefixed with docg4_ are mtd / nand interface functions
- * (though they may also be called internally). All others are internal.
- */
-
-#define DOC_IOSPACE_DATA 0x0800
-
-/* register offsets */
-#define DOC_CHIPID 0x1000
-#define DOC_DEVICESELECT 0x100a
-#define DOC_ASICMODE 0x100c
-#define DOC_DATAEND 0x101e
-#define DOC_NOP 0x103e
-
-#define DOC_FLASHSEQUENCE 0x1032
-#define DOC_FLASHCOMMAND 0x1034
-#define DOC_FLASHADDRESS 0x1036
-#define DOC_FLASHCONTROL 0x1038
-#define DOC_ECCCONF0 0x1040
-#define DOC_ECCCONF1 0x1042
-#define DOC_HAMMINGPARITY 0x1046
-#define DOC_BCH_SYNDROM(idx) (0x1048 + idx)
-
-#define DOC_ASICMODECONFIRM 0x1072
-#define DOC_CHIPID_INV 0x1074
-#define DOC_POWERMODE 0x107c
-
-#define DOCG4_MYSTERY_REG 0x1050
-
-/* apparently used only to write oob bytes 6 and 7 */
-#define DOCG4_OOB_6_7 0x1052
-
-/* DOC_FLASHSEQUENCE register commands */
-#define DOC_SEQ_RESET 0x00
-#define DOCG4_SEQ_PAGE_READ 0x03
-#define DOCG4_SEQ_FLUSH 0x29
-#define DOCG4_SEQ_PAGEWRITE 0x16
-#define DOCG4_SEQ_PAGEPROG 0x1e
-#define DOCG4_SEQ_BLOCKERASE 0x24
-#define DOCG4_SEQ_SETMODE 0x45
-
-/* DOC_FLASHCOMMAND register commands */
-#define DOCG4_CMD_PAGE_READ 0x00
-#define DOC_CMD_ERASECYCLE2 0xd0
-#define DOCG4_CMD_FLUSH 0x70
-#define DOCG4_CMD_READ2 0x30
-#define DOC_CMD_PROG_BLOCK_ADDR 0x60
-#define DOCG4_CMD_PAGEWRITE 0x80
-#define DOC_CMD_PROG_CYCLE2 0x10
-#define DOCG4_CMD_FAST_MODE 0xa3 /* functionality guessed */
-#define DOC_CMD_RELIABLE_MODE 0x22
-#define DOC_CMD_RESET 0xff
-
-/* DOC_POWERMODE register bits */
-#define DOC_POWERDOWN_READY 0x80
-
-/* DOC_FLASHCONTROL register bits */
-#define DOC_CTRL_CE 0x10
-#define DOC_CTRL_UNKNOWN 0x40
-#define DOC_CTRL_FLASHREADY 0x01
-
-/* DOC_ECCCONF0 register bits */
-#define DOC_ECCCONF0_READ_MODE 0x8000
-#define DOC_ECCCONF0_UNKNOWN 0x2000
-#define DOC_ECCCONF0_ECC_ENABLE 0x1000
-#define DOC_ECCCONF0_DATA_BYTES_MASK 0x07ff
-
-/* DOC_ECCCONF1 register bits */
-#define DOC_ECCCONF1_BCH_SYNDROM_ERR 0x80
-#define DOC_ECCCONF1_ECC_ENABLE 0x07
-#define DOC_ECCCONF1_PAGE_IS_WRITTEN 0x20
-
-/* DOC_ASICMODE register bits */
-#define DOC_ASICMODE_RESET 0x00
-#define DOC_ASICMODE_NORMAL 0x01
-#define DOC_ASICMODE_POWERDOWN 0x02
-#define DOC_ASICMODE_MDWREN 0x04
-#define DOC_ASICMODE_BDETCT_RESET 0x08
-#define DOC_ASICMODE_RSTIN_RESET 0x10
-#define DOC_ASICMODE_RAM_WE 0x20
-
-/* good status values read after read/write/erase operations */
-#define DOCG4_PROGSTATUS_GOOD 0x51
-#define DOCG4_PROGSTATUS_GOOD_2 0xe0
-
-/*
- * On read operations (page and oob-only), the first byte read from I/O reg is a
- * status. On error, it reads 0x73; otherwise, it reads either 0x71 (first read
- * after reset only) or 0x51, so bit 1 is presumed to be an error indicator.
- */
-#define DOCG4_READ_ERROR 0x02 /* bit 1 indicates read error */
-
-/* anatomy of the device */
-#define DOCG4_CHIP_SIZE 0x8000000
-#define DOCG4_PAGE_SIZE 0x200
-#define DOCG4_PAGES_PER_BLOCK 0x200
-#define DOCG4_BLOCK_SIZE (DOCG4_PAGES_PER_BLOCK * DOCG4_PAGE_SIZE)
-#define DOCG4_NUMBLOCKS (DOCG4_CHIP_SIZE / DOCG4_BLOCK_SIZE)
-#define DOCG4_OOB_SIZE 0x10
-#define DOCG4_CHIP_SHIFT 27 /* log_2(DOCG4_CHIP_SIZE) */
-#define DOCG4_PAGE_SHIFT 9 /* log_2(DOCG4_PAGE_SIZE) */
-#define DOCG4_ERASE_SHIFT 18 /* log_2(DOCG4_BLOCK_SIZE) */
-
-/* all but the last byte is included in ecc calculation */
-#define DOCG4_BCH_SIZE (DOCG4_PAGE_SIZE + DOCG4_OOB_SIZE - 1)
-
-#define DOCG4_USERDATA_LEN 520 /* 512 byte page plus 8 oob avail to user */
-
-/* expected values from the ID registers */
-#define DOCG4_IDREG1_VALUE 0x0400
-#define DOCG4_IDREG2_VALUE 0xfbff
-
-/* primitive polynomial used to build the Galois field used by hw ecc gen */
-#define DOCG4_PRIMITIVE_POLY 0x4443
-
-#define DOCG4_M 14 /* Galois field is of order 2^14 */
-#define DOCG4_T 4 /* BCH alg corrects up to 4 bit errors */
-
-#define DOCG4_FACTORY_BBT_PAGE 16 /* page where read-only factory bbt lives */
-#define DOCG4_REDUNDANT_BBT_PAGE 24 /* page where redundant factory bbt lives */
-
-/*
- * Bytes 0, 1 are used as badblock marker.
- * Bytes 2 - 6 are available to the user.
- * Byte 7 is hamming ecc for first 7 oob bytes only.
- * Bytes 8 - 14 are hw-generated ecc covering entire page + oob bytes 0 - 14.
- * Byte 15 (the last) is used by the driver as a "page written" flag.
- */
-static int docg4_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- if (section)
- return -ERANGE;
-
- oobregion->offset = 7;
- oobregion->length = 9;
-
- return 0;
-}
-
-static int docg4_ooblayout_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- if (section)
- return -ERANGE;
-
- oobregion->offset = 2;
- oobregion->length = 5;
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops docg4_ooblayout_ops = {
- .ecc = docg4_ooblayout_ecc,
- .free = docg4_ooblayout_free,
-};
-
-/*
- * The device has a nop register which M-Sys claims is for the purpose of
- * inserting precise delays. But beware; at least some operations fail if the
- * nop writes are replaced with a generic delay!
- */
-static inline void write_nop(void __iomem *docptr)
-{
- writew(0, docptr + DOC_NOP);
-}
-
-static void docg4_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
-{
- int i;
- struct nand_chip *nand = mtd_to_nand(mtd);
- uint16_t *p = (uint16_t *) buf;
- len >>= 1;
-
- for (i = 0; i < len; i++)
- p[i] = readw(nand->IO_ADDR_R);
-}
-
-static void docg4_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
-{
- int i;
- struct nand_chip *nand = mtd_to_nand(mtd);
- uint16_t *p = (uint16_t *) buf;
- len >>= 1;
-
- for (i = 0; i < len; i++)
- writew(p[i], nand->IO_ADDR_W);
-}
-
-static int poll_status(struct docg4_priv *doc)
-{
- /*
- * Busy-wait for the FLASHREADY bit to be set in the FLASHCONTROL
- * register. Operations known to take a long time (e.g., block erase)
- * should sleep for a while before calling this.
- */
-
- uint16_t flash_status;
- unsigned long timeo;
- void __iomem *docptr = doc->virtadr;
-
- dev_dbg(doc->dev, "%s...\n", __func__);
-
- /* hardware quirk requires reading twice initially */
- flash_status = readw(docptr + DOC_FLASHCONTROL);
-
- timeo = jiffies + msecs_to_jiffies(200); /* generous timeout */
- do {
- cpu_relax();
- flash_status = readb(docptr + DOC_FLASHCONTROL);
- } while (!(flash_status & DOC_CTRL_FLASHREADY) &&
- time_before(jiffies, timeo));
-
- if (unlikely(!(flash_status & DOC_CTRL_FLASHREADY))) {
- dev_err(doc->dev, "%s: timed out!\n", __func__);
- return NAND_STATUS_FAIL;
- }
-
- return 0;
-}
-
-
-static int docg4_wait(struct mtd_info *mtd, struct nand_chip *nand)
-{
-
- struct docg4_priv *doc = nand_get_controller_data(nand);
- int status = NAND_STATUS_WP; /* inverse logic?? */
- dev_dbg(doc->dev, "%s...\n", __func__);
-
- /* report any previously unreported error */
- if (doc->status) {
- status |= doc->status;
- doc->status = 0;
- return status;
- }
-
- status |= poll_status(doc);
- return status;
-}
-
-static void docg4_select_chip(struct mtd_info *mtd, int chip)
-{
- /*
- * Select among multiple cascaded chips ("floors"). Multiple floors are
- * not yet supported, so the only valid non-negative value is 0.
- */
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct docg4_priv *doc = nand_get_controller_data(nand);
- void __iomem *docptr = doc->virtadr;
-
- dev_dbg(doc->dev, "%s: chip %d\n", __func__, chip);
-
- if (chip < 0)
- return; /* deselected */
-
- if (chip > 0)
- dev_warn(doc->dev, "multiple floors currently unsupported\n");
-
- writew(0, docptr + DOC_DEVICESELECT);
-}
-
-static void reset(struct mtd_info *mtd)
-{
- /* full device reset */
-
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct docg4_priv *doc = nand_get_controller_data(nand);
- void __iomem *docptr = doc->virtadr;
-
- writew(DOC_ASICMODE_RESET | DOC_ASICMODE_MDWREN,
- docptr + DOC_ASICMODE);
- writew(~(DOC_ASICMODE_RESET | DOC_ASICMODE_MDWREN),
- docptr + DOC_ASICMODECONFIRM);
- write_nop(docptr);
-
- writew(DOC_ASICMODE_NORMAL | DOC_ASICMODE_MDWREN,
- docptr + DOC_ASICMODE);
- writew(~(DOC_ASICMODE_NORMAL | DOC_ASICMODE_MDWREN),
- docptr + DOC_ASICMODECONFIRM);
-
- writew(DOC_ECCCONF1_ECC_ENABLE, docptr + DOC_ECCCONF1);
-
- poll_status(doc);
-}
-
-static void read_hw_ecc(void __iomem *docptr, uint8_t *ecc_buf)
-{
- /* read the 7 hw-generated ecc bytes */
-
- int i;
- for (i = 0; i < 7; i++) { /* hw quirk; read twice */
- ecc_buf[i] = readb(docptr + DOC_BCH_SYNDROM(i));
- ecc_buf[i] = readb(docptr + DOC_BCH_SYNDROM(i));
- }
-}
-
-static int correct_data(struct mtd_info *mtd, uint8_t *buf, int page)
-{
- /*
- * Called after a page read when hardware reports bitflips.
- * Up to four bitflips can be corrected.
- */
-
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct docg4_priv *doc = nand_get_controller_data(nand);
- void __iomem *docptr = doc->virtadr;
- int i, numerrs, errpos[4];
- const uint8_t blank_read_hwecc[8] = {
- 0xcf, 0x72, 0xfc, 0x1b, 0xa9, 0xc7, 0xb9, 0 };
-
- read_hw_ecc(docptr, doc->ecc_buf); /* read 7 hw-generated ecc bytes */
-
- /* check if read error is due to a blank page */
- if (!memcmp(doc->ecc_buf, blank_read_hwecc, 7))
- return 0; /* yes */
-
- /* skip additional check of "written flag" if ignore_badblocks */
- if (ignore_badblocks == false) {
-
- /*
- * If the hw ecc bytes are not those of a blank page, there's
- * still a chance that the page is blank, but was read with
- * errors. Check the "written flag" in last oob byte, which
- * is set to zero when a page is written. If more than half
- * the bits are set, assume a blank page. Unfortunately, the
- * bit flips(s) are not reported in stats.
- */
-
- if (nand->oob_poi[15]) {
- int bit, numsetbits = 0;
- unsigned long written_flag = nand->oob_poi[15];
- for_each_set_bit(bit, &written_flag, 8)
- numsetbits++;
- if (numsetbits > 4) { /* assume blank */
- dev_warn(doc->dev,
- "error(s) in blank page "
- "at offset %08x\n",
- page * DOCG4_PAGE_SIZE);
- return 0;
- }
- }
- }
-
- /*
- * The hardware ecc unit produces oob_ecc ^ calc_ecc. The kernel's bch
- * algorithm is used to decode this. However the hw operates on page
- * data in a bit order that is the reverse of that of the bch alg,
- * requiring that the bits be reversed on the result. Thanks to Ivan
- * Djelic for his analysis!
- */
- for (i = 0; i < 7; i++)
- doc->ecc_buf[i] = bitrev8(doc->ecc_buf[i]);
-
- numerrs = decode_bch(doc->bch, NULL, DOCG4_USERDATA_LEN, NULL,
- doc->ecc_buf, NULL, errpos);
-
- if (numerrs == -EBADMSG) {
- dev_warn(doc->dev, "uncorrectable errors at offset %08x\n",
- page * DOCG4_PAGE_SIZE);
- return -EBADMSG;
- }
-
- BUG_ON(numerrs < 0); /* -EINVAL, or anything other than -EBADMSG */
-
- /* undo last step in BCH alg (modulo mirroring not needed) */
- for (i = 0; i < numerrs; i++)
- errpos[i] = (errpos[i] & ~7)|(7-(errpos[i] & 7));
-
- /* fix the errors */
- for (i = 0; i < numerrs; i++) {
-
- /* ignore if error within oob ecc bytes */
- if (errpos[i] > DOCG4_USERDATA_LEN * 8)
- continue;
-
- /* if error within oob area preceeding ecc bytes... */
- if (errpos[i] > DOCG4_PAGE_SIZE * 8)
- change_bit(errpos[i] - DOCG4_PAGE_SIZE * 8,
- (unsigned long *)nand->oob_poi);
-
- else /* error in page data */
- change_bit(errpos[i], (unsigned long *)buf);
- }
-
- dev_notice(doc->dev, "%d error(s) corrected at offset %08x\n",
- numerrs, page * DOCG4_PAGE_SIZE);
-
- return numerrs;
-}
-
-static uint8_t docg4_read_byte(struct mtd_info *mtd)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct docg4_priv *doc = nand_get_controller_data(nand);
-
- dev_dbg(doc->dev, "%s\n", __func__);
-
- if (doc->last_command.command == NAND_CMD_STATUS) {
- int status;
-
- /*
- * Previous nand command was status request, so nand
- * infrastructure code expects to read the status here. If an
- * error occurred in a previous operation, report it.
- */
- doc->last_command.command = 0;
-
- if (doc->status) {
- status = doc->status;
- doc->status = 0;
- }
-
- /* why is NAND_STATUS_WP inverse logic?? */
- else
- status = NAND_STATUS_WP | NAND_STATUS_READY;
-
- return status;
- }
-
- dev_warn(doc->dev, "unexpected call to read_byte()\n");
-
- return 0;
-}
-
-static void write_addr(struct docg4_priv *doc, uint32_t docg4_addr)
-{
- /* write the four address bytes packed in docg4_addr to the device */
-
- void __iomem *docptr = doc->virtadr;
- writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS);
- docg4_addr >>= 8;
- writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS);
- docg4_addr >>= 8;
- writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS);
- docg4_addr >>= 8;
- writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS);
-}
-
-static int read_progstatus(struct docg4_priv *doc)
-{
- /*
- * This apparently checks the status of programming. Done after an
- * erasure, and after page data is written. On error, the status is
- * saved, to be later retrieved by the nand infrastructure code.
- */
- void __iomem *docptr = doc->virtadr;
-
- /* status is read from the I/O reg */
- uint16_t status1 = readw(docptr + DOC_IOSPACE_DATA);
- uint16_t status2 = readw(docptr + DOC_IOSPACE_DATA);
- uint16_t status3 = readw(docptr + DOCG4_MYSTERY_REG);
-
- dev_dbg(doc->dev, "docg4: %s: %02x %02x %02x\n",
- __func__, status1, status2, status3);
-
- if (status1 != DOCG4_PROGSTATUS_GOOD
- || status2 != DOCG4_PROGSTATUS_GOOD_2
- || status3 != DOCG4_PROGSTATUS_GOOD_2) {
- doc->status = NAND_STATUS_FAIL;
- dev_warn(doc->dev, "read_progstatus failed: "
- "%02x, %02x, %02x\n", status1, status2, status3);
- return -EIO;
- }
- return 0;
-}
-
-static int pageprog(struct mtd_info *mtd)
-{
- /*
- * Final step in writing a page. Writes the contents of its
- * internal buffer out to the flash array, or some such.
- */
-
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct docg4_priv *doc = nand_get_controller_data(nand);
- void __iomem *docptr = doc->virtadr;
- int retval = 0;
-
- dev_dbg(doc->dev, "docg4: %s\n", __func__);
-
- writew(DOCG4_SEQ_PAGEPROG, docptr + DOC_FLASHSEQUENCE);
- writew(DOC_CMD_PROG_CYCLE2, docptr + DOC_FLASHCOMMAND);
- write_nop(docptr);
- write_nop(docptr);
-
- /* Just busy-wait; usleep_range() slows things down noticeably. */
- poll_status(doc);
-
- writew(DOCG4_SEQ_FLUSH, docptr + DOC_FLASHSEQUENCE);
- writew(DOCG4_CMD_FLUSH, docptr + DOC_FLASHCOMMAND);
- writew(DOC_ECCCONF0_READ_MODE | 4, docptr + DOC_ECCCONF0);
- write_nop(docptr);
- write_nop(docptr);
- write_nop(docptr);
- write_nop(docptr);
- write_nop(docptr);
-
- retval = read_progstatus(doc);
- writew(0, docptr + DOC_DATAEND);
- write_nop(docptr);
- poll_status(doc);
- write_nop(docptr);
-
- return retval;
-}
-
-static void sequence_reset(struct mtd_info *mtd)
-{
- /* common starting sequence for all operations */
-
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct docg4_priv *doc = nand_get_controller_data(nand);
- void __iomem *docptr = doc->virtadr;
-
- writew(DOC_CTRL_UNKNOWN | DOC_CTRL_CE, docptr + DOC_FLASHCONTROL);
- writew(DOC_SEQ_RESET, docptr + DOC_FLASHSEQUENCE);
- writew(DOC_CMD_RESET, docptr + DOC_FLASHCOMMAND);
- write_nop(docptr);
- write_nop(docptr);
- poll_status(doc);
- write_nop(docptr);
-}
-
-static void read_page_prologue(struct mtd_info *mtd, uint32_t docg4_addr)
-{
- /* first step in reading a page */
-
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct docg4_priv *doc = nand_get_controller_data(nand);
- void __iomem *docptr = doc->virtadr;
-
- dev_dbg(doc->dev,
- "docg4: %s: g4 page %08x\n", __func__, docg4_addr);
-
- sequence_reset(mtd);
-
- writew(DOCG4_SEQ_PAGE_READ, docptr + DOC_FLASHSEQUENCE);
- writew(DOCG4_CMD_PAGE_READ, docptr + DOC_FLASHCOMMAND);
- write_nop(docptr);
-
- write_addr(doc, docg4_addr);
-
- write_nop(docptr);
- writew(DOCG4_CMD_READ2, docptr + DOC_FLASHCOMMAND);
- write_nop(docptr);
- write_nop(docptr);
-
- poll_status(doc);
-}
-
-static void write_page_prologue(struct mtd_info *mtd, uint32_t docg4_addr)
-{
- /* first step in writing a page */
-
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct docg4_priv *doc = nand_get_controller_data(nand);
- void __iomem *docptr = doc->virtadr;
-
- dev_dbg(doc->dev,
- "docg4: %s: g4 addr: %x\n", __func__, docg4_addr);
- sequence_reset(mtd);
-
- if (unlikely(reliable_mode)) {
- writew(DOCG4_SEQ_SETMODE, docptr + DOC_FLASHSEQUENCE);
- writew(DOCG4_CMD_FAST_MODE, docptr + DOC_FLASHCOMMAND);
- writew(DOC_CMD_RELIABLE_MODE, docptr + DOC_FLASHCOMMAND);
- write_nop(docptr);
- }
-
- writew(DOCG4_SEQ_PAGEWRITE, docptr + DOC_FLASHSEQUENCE);
- writew(DOCG4_CMD_PAGEWRITE, docptr + DOC_FLASHCOMMAND);
- write_nop(docptr);
- write_addr(doc, docg4_addr);
- write_nop(docptr);
- write_nop(docptr);
- poll_status(doc);
-}
-
-static uint32_t mtd_to_docg4_address(int page, int column)
-{
- /*
- * Convert mtd address to format used by the device, 32 bit packed.
- *
- * Some notes on G4 addressing... The M-Sys documentation on this device
- * claims that pages are 2K in length, and indeed, the format of the
- * address used by the device reflects that. But within each page are
- * four 512 byte "sub-pages", each with its own oob data that is
- * read/written immediately after the 512 bytes of page data. This oob
- * data contains the ecc bytes for the preceeding 512 bytes.
- *
- * Rather than tell the mtd nand infrastructure that page size is 2k,
- * with four sub-pages each, we engage in a little subterfuge and tell
- * the infrastructure code that pages are 512 bytes in size. This is
- * done because during the course of reverse-engineering the device, I
- * never observed an instance where an entire 2K "page" was read or
- * written as a unit. Each "sub-page" is always addressed individually,
- * its data read/written, and ecc handled before the next "sub-page" is
- * addressed.
- *
- * This requires us to convert addresses passed by the mtd nand
- * infrastructure code to those used by the device.
- *
- * The address that is written to the device consists of four bytes: the
- * first two are the 2k page number, and the second is the index into
- * the page. The index is in terms of 16-bit half-words and includes
- * the preceeding oob data, so e.g., the index into the second
- * "sub-page" is 0x108, and the full device address of the start of mtd
- * page 0x201 is 0x00800108.
- */
- int g4_page = page / 4; /* device's 2K page */
- int g4_index = (page % 4) * 0x108 + column/2; /* offset into page */
- return (g4_page << 16) | g4_index; /* pack */
-}
-
-static void docg4_command(struct mtd_info *mtd, unsigned command, int column,
- int page_addr)
-{
- /* handle standard nand commands */
-
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct docg4_priv *doc = nand_get_controller_data(nand);
- uint32_t g4_addr = mtd_to_docg4_address(page_addr, column);
-
- dev_dbg(doc->dev, "%s %x, page_addr=%x, column=%x\n",
- __func__, command, page_addr, column);
-
- /*
- * Save the command and its arguments. This enables emulation of
- * standard flash devices, and also some optimizations.
- */
- doc->last_command.command = command;
- doc->last_command.column = column;
- doc->last_command.page = page_addr;
-
- switch (command) {
-
- case NAND_CMD_RESET:
- reset(mtd);
- break;
-
- case NAND_CMD_READ0:
- read_page_prologue(mtd, g4_addr);
- break;
-
- case NAND_CMD_STATUS:
- /* next call to read_byte() will expect a status */
- break;
-
- case NAND_CMD_SEQIN:
- if (unlikely(reliable_mode)) {
- uint16_t g4_page = g4_addr >> 16;
-
- /* writes to odd-numbered 2k pages are invalid */
- if (g4_page & 0x01)
- dev_warn(doc->dev,
- "invalid reliable mode address\n");
- }
-
- write_page_prologue(mtd, g4_addr);
-
- /* hack for deferred write of oob bytes */
- if (doc->oob_page == page_addr)
- memcpy(nand->oob_poi, doc->oob_buf, 16);
- break;
-
- case NAND_CMD_PAGEPROG:
- pageprog(mtd);
- break;
-
- /* we don't expect these, based on review of nand_base.c */
- case NAND_CMD_READOOB:
- case NAND_CMD_READID:
- case NAND_CMD_ERASE1:
- case NAND_CMD_ERASE2:
- dev_warn(doc->dev, "docg4_command: "
- "unexpected nand command 0x%x\n", command);
- break;
-
- }
-}
-
-static int read_page(struct mtd_info *mtd, struct nand_chip *nand,
- uint8_t *buf, int page, bool use_ecc)
-{
- struct docg4_priv *doc = nand_get_controller_data(nand);
- void __iomem *docptr = doc->virtadr;
- uint16_t status, edc_err, *buf16;
- int bits_corrected = 0;
-
- dev_dbg(doc->dev, "%s: page %08x\n", __func__, page);
-
- nand_read_page_op(nand, page, 0, NULL, 0);
-
- writew(DOC_ECCCONF0_READ_MODE |
- DOC_ECCCONF0_ECC_ENABLE |
- DOC_ECCCONF0_UNKNOWN |
- DOCG4_BCH_SIZE,
- docptr + DOC_ECCCONF0);
- write_nop(docptr);
- write_nop(docptr);
- write_nop(docptr);
- write_nop(docptr);
- write_nop(docptr);
-
- /* the 1st byte from the I/O reg is a status; the rest is page data */
- status = readw(docptr + DOC_IOSPACE_DATA);
- if (status & DOCG4_READ_ERROR) {
- dev_err(doc->dev,
- "docg4_read_page: bad status: 0x%02x\n", status);
- writew(0, docptr + DOC_DATAEND);
- return -EIO;
- }
-
- dev_dbg(doc->dev, "%s: status = 0x%x\n", __func__, status);
-
- docg4_read_buf(mtd, buf, DOCG4_PAGE_SIZE); /* read the page data */
-
- /* this device always reads oob after page data */
- /* first 14 oob bytes read from I/O reg */
- docg4_read_buf(mtd, nand->oob_poi, 14);
-
- /* last 2 read from another reg */
- buf16 = (uint16_t *)(nand->oob_poi + 14);
- *buf16 = readw(docptr + DOCG4_MYSTERY_REG);
-
- write_nop(docptr);
-
- if (likely(use_ecc == true)) {
-
- /* read the register that tells us if bitflip(s) detected */
- edc_err = readw(docptr + DOC_ECCCONF1);
- edc_err = readw(docptr + DOC_ECCCONF1);
- dev_dbg(doc->dev, "%s: edc_err = 0x%02x\n", __func__, edc_err);
-
- /* If bitflips are reported, attempt to correct with ecc */
- if (edc_err & DOC_ECCCONF1_BCH_SYNDROM_ERR) {
- bits_corrected = correct_data(mtd, buf, page);
- if (bits_corrected == -EBADMSG)
- mtd->ecc_stats.failed++;
- else
- mtd->ecc_stats.corrected += bits_corrected;
- }
- }
-
- writew(0, docptr + DOC_DATAEND);
- if (bits_corrected == -EBADMSG) /* uncorrectable errors */
- return 0;
- return bits_corrected;
-}
-
-
-static int docg4_read_page_raw(struct mtd_info *mtd, struct nand_chip *nand,
- uint8_t *buf, int oob_required, int page)
-{
- return read_page(mtd, nand, buf, page, false);
-}
-
-static int docg4_read_page(struct mtd_info *mtd, struct nand_chip *nand,
- uint8_t *buf, int oob_required, int page)
-{
- return read_page(mtd, nand, buf, page, true);
-}
-
-static int docg4_read_oob(struct mtd_info *mtd, struct nand_chip *nand,
- int page)
-{
- struct docg4_priv *doc = nand_get_controller_data(nand);
- void __iomem *docptr = doc->virtadr;
- uint16_t status;
-
- dev_dbg(doc->dev, "%s: page %x\n", __func__, page);
-
- nand_read_page_op(nand, page, nand->ecc.size, NULL, 0);
-
- writew(DOC_ECCCONF0_READ_MODE | DOCG4_OOB_SIZE, docptr + DOC_ECCCONF0);
- write_nop(docptr);
- write_nop(docptr);
- write_nop(docptr);
- write_nop(docptr);
- write_nop(docptr);
-
- /* the 1st byte from the I/O reg is a status; the rest is oob data */
- status = readw(docptr + DOC_IOSPACE_DATA);
- if (status & DOCG4_READ_ERROR) {
- dev_warn(doc->dev,
- "docg4_read_oob failed: status = 0x%02x\n", status);
- return -EIO;
- }
-
- dev_dbg(doc->dev, "%s: status = 0x%x\n", __func__, status);
-
- docg4_read_buf(mtd, nand->oob_poi, 16);
-
- write_nop(docptr);
- write_nop(docptr);
- write_nop(docptr);
- writew(0, docptr + DOC_DATAEND);
- write_nop(docptr);
-
- return 0;
-}
-
-static int docg4_erase_block(struct mtd_info *mtd, int page)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct docg4_priv *doc = nand_get_controller_data(nand);
- void __iomem *docptr = doc->virtadr;
- uint16_t g4_page;
- int status;
-
- dev_dbg(doc->dev, "%s: page %04x\n", __func__, page);
-
- sequence_reset(mtd);
-
- writew(DOCG4_SEQ_BLOCKERASE, docptr + DOC_FLASHSEQUENCE);
- writew(DOC_CMD_PROG_BLOCK_ADDR, docptr + DOC_FLASHCOMMAND);
- write_nop(docptr);
-
- /* only 2 bytes of address are written to specify erase block */
- g4_page = (uint16_t)(page / 4); /* to g4's 2k page addressing */
- writeb(g4_page & 0xff, docptr + DOC_FLASHADDRESS);
- g4_page >>= 8;
- writeb(g4_page & 0xff, docptr + DOC_FLASHADDRESS);
- write_nop(docptr);
-
- /* start the erasure */
- writew(DOC_CMD_ERASECYCLE2, docptr + DOC_FLASHCOMMAND);
- write_nop(docptr);
- write_nop(docptr);
-
- usleep_range(500, 1000); /* erasure is long; take a snooze */
- poll_status(doc);
- writew(DOCG4_SEQ_FLUSH, docptr + DOC_FLASHSEQUENCE);
- writew(DOCG4_CMD_FLUSH, docptr + DOC_FLASHCOMMAND);
- writew(DOC_ECCCONF0_READ_MODE | 4, docptr + DOC_ECCCONF0);
- write_nop(docptr);
- write_nop(docptr);
- write_nop(docptr);
- write_nop(docptr);
- write_nop(docptr);
-
- read_progstatus(doc);
-
- writew(0, docptr + DOC_DATAEND);
- write_nop(docptr);
- poll_status(doc);
- write_nop(docptr);
-
- status = nand->waitfunc(mtd, nand);
- if (status < 0)
- return status;
-
- return status & NAND_STATUS_FAIL ? -EIO : 0;
-}
-
-static int write_page(struct mtd_info *mtd, struct nand_chip *nand,
- const uint8_t *buf, int page, bool use_ecc)
-{
- struct docg4_priv *doc = nand_get_controller_data(nand);
- void __iomem *docptr = doc->virtadr;
- uint8_t ecc_buf[8];
-
- dev_dbg(doc->dev, "%s...\n", __func__);
-
- nand_prog_page_begin_op(nand, page, 0, NULL, 0);
-
- writew(DOC_ECCCONF0_ECC_ENABLE |
- DOC_ECCCONF0_UNKNOWN |
- DOCG4_BCH_SIZE,
- docptr + DOC_ECCCONF0);
- write_nop(docptr);
-
- /* write the page data */
- docg4_write_buf16(mtd, buf, DOCG4_PAGE_SIZE);
-
- /* oob bytes 0 through 5 are written to I/O reg */
- docg4_write_buf16(mtd, nand->oob_poi, 6);
-
- /* oob byte 6 written to a separate reg */
- writew(nand->oob_poi[6], docptr + DOCG4_OOB_6_7);
-
- write_nop(docptr);
- write_nop(docptr);
-
- /* write hw-generated ecc bytes to oob */
- if (likely(use_ecc == true)) {
- /* oob byte 7 is hamming code */
- uint8_t hamming = readb(docptr + DOC_HAMMINGPARITY);
- hamming = readb(docptr + DOC_HAMMINGPARITY); /* 2nd read */
- writew(hamming, docptr + DOCG4_OOB_6_7);
- write_nop(docptr);
-
- /* read the 7 bch bytes from ecc regs */
- read_hw_ecc(docptr, ecc_buf);
- ecc_buf[7] = 0; /* clear the "page written" flag */
- }
-
- /* write user-supplied bytes to oob */
- else {
- writew(nand->oob_poi[7], docptr + DOCG4_OOB_6_7);
- write_nop(docptr);
- memcpy(ecc_buf, &nand->oob_poi[8], 8);
- }
-
- docg4_write_buf16(mtd, ecc_buf, 8);
- write_nop(docptr);
- write_nop(docptr);
- writew(0, docptr + DOC_DATAEND);
- write_nop(docptr);
-
- return nand_prog_page_end_op(nand);
-}
-
-static int docg4_write_page_raw(struct mtd_info *mtd, struct nand_chip *nand,
- const uint8_t *buf, int oob_required, int page)
-{
- return write_page(mtd, nand, buf, page, false);
-}
-
-static int docg4_write_page(struct mtd_info *mtd, struct nand_chip *nand,
- const uint8_t *buf, int oob_required, int page)
-{
- return write_page(mtd, nand, buf, page, true);
-}
-
-static int docg4_write_oob(struct mtd_info *mtd, struct nand_chip *nand,
- int page)
-{
- /*
- * Writing oob-only is not really supported, because MLC nand must write
- * oob bytes at the same time as page data. Nonetheless, we save the
- * oob buffer contents here, and then write it along with the page data
- * if the same page is subsequently written. This allows user space
- * utilities that write the oob data prior to the page data to work
- * (e.g., nandwrite). The disdvantage is that, if the intention was to
- * write oob only, the operation is quietly ignored. Also, oob can get
- * corrupted if two concurrent processes are running nandwrite.
- */
-
- /* note that bytes 7..14 are hw generated hamming/ecc and overwritten */
- struct docg4_priv *doc = nand_get_controller_data(nand);
- doc->oob_page = page;
- memcpy(doc->oob_buf, nand->oob_poi, 16);
- return 0;
-}
-
-static int __init read_factory_bbt(struct mtd_info *mtd)
-{
- /*
- * The device contains a read-only factory bad block table. Read it and
- * update the memory-based bbt accordingly.
- */
-
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct docg4_priv *doc = nand_get_controller_data(nand);
- uint32_t g4_addr = mtd_to_docg4_address(DOCG4_FACTORY_BBT_PAGE, 0);
- uint8_t *buf;
- int i, block;
- __u32 eccfailed_stats = mtd->ecc_stats.failed;
-
- buf = kzalloc(DOCG4_PAGE_SIZE, GFP_KERNEL);
- if (buf == NULL)
- return -ENOMEM;
-
- read_page_prologue(mtd, g4_addr);
- docg4_read_page(mtd, nand, buf, 0, DOCG4_FACTORY_BBT_PAGE);
-
- /*
- * If no memory-based bbt was created, exit. This will happen if module
- * parameter ignore_badblocks is set. Then why even call this function?
- * For an unknown reason, block erase always fails if it's the first
- * operation after device power-up. The above read ensures it never is.
- * Ugly, I know.
- */
- if (nand->bbt == NULL) /* no memory-based bbt */
- goto exit;
-
- if (mtd->ecc_stats.failed > eccfailed_stats) {
- /*
- * Whoops, an ecc failure ocurred reading the factory bbt.
- * It is stored redundantly, so we get another chance.
- */
- eccfailed_stats = mtd->ecc_stats.failed;
- docg4_read_page(mtd, nand, buf, 0, DOCG4_REDUNDANT_BBT_PAGE);
- if (mtd->ecc_stats.failed > eccfailed_stats) {
- dev_warn(doc->dev,
- "The factory bbt could not be read!\n");
- goto exit;
- }
- }
-
- /*
- * Parse factory bbt and update memory-based bbt. Factory bbt format is
- * simple: one bit per block, block numbers increase left to right (msb
- * to lsb). Bit clear means bad block.
- */
- for (i = block = 0; block < DOCG4_NUMBLOCKS; block += 8, i++) {
- int bitnum;
- unsigned long bits = ~buf[i];
- for_each_set_bit(bitnum, &bits, 8) {
- int badblock = block + 7 - bitnum;
- nand->bbt[badblock / 4] |=
- 0x03 << ((badblock % 4) * 2);
- mtd->ecc_stats.badblocks++;
- dev_notice(doc->dev, "factory-marked bad block: %d\n",
- badblock);
- }
- }
- exit:
- kfree(buf);
- return 0;
-}
-
-static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs)
-{
- /*
- * Mark a block as bad. Bad blocks are marked in the oob area of the
- * first page of the block. The default scan_bbt() in the nand
- * infrastructure code works fine for building the memory-based bbt
- * during initialization, as does the nand infrastructure function that
- * checks if a block is bad by reading the bbt. This function replaces
- * the nand default because writes to oob-only are not supported.
- */
-
- int ret, i;
- uint8_t *buf;
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct docg4_priv *doc = nand_get_controller_data(nand);
- struct nand_bbt_descr *bbtd = nand->badblock_pattern;
- int page = (int)(ofs >> nand->page_shift);
- uint32_t g4_addr = mtd_to_docg4_address(page, 0);
-
- dev_dbg(doc->dev, "%s: %08llx\n", __func__, ofs);
-
- if (unlikely(ofs & (DOCG4_BLOCK_SIZE - 1)))
- dev_warn(doc->dev, "%s: ofs %llx not start of block!\n",
- __func__, ofs);
-
- /* allocate blank buffer for page data */
- buf = kzalloc(DOCG4_PAGE_SIZE, GFP_KERNEL);
- if (buf == NULL)
- return -ENOMEM;
-
- /* write bit-wise negation of pattern to oob buffer */
- memset(nand->oob_poi, 0xff, mtd->oobsize);
- for (i = 0; i < bbtd->len; i++)
- nand->oob_poi[bbtd->offs + i] = ~bbtd->pattern[i];
-
- /* write first page of block */
- write_page_prologue(mtd, g4_addr);
- docg4_write_page(mtd, nand, buf, 1, page);
- ret = pageprog(mtd);
-
- kfree(buf);
-
- return ret;
-}
-
-static int docg4_block_neverbad(struct mtd_info *mtd, loff_t ofs)
-{
- /* only called when module_param ignore_badblocks is set */
- return 0;
-}
-
-static int docg4_suspend(struct platform_device *pdev, pm_message_t state)
-{
- /*
- * Put the device into "deep power-down" mode. Note that CE# must be
- * deasserted for this to take effect. The xscale, e.g., can be
- * configured to float this signal when the processor enters power-down,
- * and a suitable pull-up ensures its deassertion.
- */
-
- int i;
- uint8_t pwr_down;
- struct docg4_priv *doc = platform_get_drvdata(pdev);
- void __iomem *docptr = doc->virtadr;
-
- dev_dbg(doc->dev, "%s...\n", __func__);
-
- /* poll the register that tells us we're ready to go to sleep */
- for (i = 0; i < 10; i++) {
- pwr_down = readb(docptr + DOC_POWERMODE);
- if (pwr_down & DOC_POWERDOWN_READY)
- break;
- usleep_range(1000, 4000);
- }
-
- if (pwr_down & DOC_POWERDOWN_READY) {
- dev_err(doc->dev, "suspend failed; "
- "timeout polling DOC_POWERDOWN_READY\n");
- return -EIO;
- }
-
- writew(DOC_ASICMODE_POWERDOWN | DOC_ASICMODE_MDWREN,
- docptr + DOC_ASICMODE);
- writew(~(DOC_ASICMODE_POWERDOWN | DOC_ASICMODE_MDWREN),
- docptr + DOC_ASICMODECONFIRM);
-
- write_nop(docptr);
-
- return 0;
-}
-
-static int docg4_resume(struct platform_device *pdev)
-{
-
- /*
- * Exit power-down. Twelve consecutive reads of the address below
- * accomplishes this, assuming CE# has been asserted.
- */
-
- struct docg4_priv *doc = platform_get_drvdata(pdev);
- void __iomem *docptr = doc->virtadr;
- int i;
-
- dev_dbg(doc->dev, "%s...\n", __func__);
-
- for (i = 0; i < 12; i++)
- readb(docptr + 0x1fff);
-
- return 0;
-}
-
-static void __init init_mtd_structs(struct mtd_info *mtd)
-{
- /* initialize mtd and nand data structures */
-
- /*
- * Note that some of the following initializations are not usually
- * required within a nand driver because they are performed by the nand
- * infrastructure code as part of nand_scan(). In this case they need
- * to be initialized here because we skip call to nand_scan_ident() (the
- * first half of nand_scan()). The call to nand_scan_ident() is skipped
- * because for this device the chip id is not read in the manner of a
- * standard nand device. Unfortunately, nand_scan_ident() does other
- * things as well, such as call nand_set_defaults().
- */
-
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct docg4_priv *doc = nand_get_controller_data(nand);
-
- mtd->size = DOCG4_CHIP_SIZE;
- mtd->name = "Msys_Diskonchip_G4";
- mtd->writesize = DOCG4_PAGE_SIZE;
- mtd->erasesize = DOCG4_BLOCK_SIZE;
- mtd->oobsize = DOCG4_OOB_SIZE;
- mtd_set_ooblayout(mtd, &docg4_ooblayout_ops);
- nand->chipsize = DOCG4_CHIP_SIZE;
- nand->chip_shift = DOCG4_CHIP_SHIFT;
- nand->bbt_erase_shift = nand->phys_erase_shift = DOCG4_ERASE_SHIFT;
- nand->chip_delay = 20;
- nand->page_shift = DOCG4_PAGE_SHIFT;
- nand->pagemask = 0x3ffff;
- nand->badblockpos = NAND_LARGE_BADBLOCK_POS;
- nand->badblockbits = 8;
- nand->ecc.mode = NAND_ECC_HW_SYNDROME;
- nand->ecc.size = DOCG4_PAGE_SIZE;
- nand->ecc.prepad = 8;
- nand->ecc.bytes = 8;
- nand->ecc.strength = DOCG4_T;
- nand->options = NAND_BUSWIDTH_16 | NAND_NO_SUBPAGE_WRITE;
- nand->IO_ADDR_R = nand->IO_ADDR_W = doc->virtadr + DOC_IOSPACE_DATA;
- nand->controller = &nand->hwcontrol;
- nand_hw_control_init(nand->controller);
-
- /* methods */
- nand->cmdfunc = docg4_command;
- nand->waitfunc = docg4_wait;
- nand->select_chip = docg4_select_chip;
- nand->read_byte = docg4_read_byte;
- nand->block_markbad = docg4_block_markbad;
- nand->read_buf = docg4_read_buf;
- nand->write_buf = docg4_write_buf16;
- nand->erase = docg4_erase_block;
- nand->onfi_set_features = nand_onfi_get_set_features_notsupp;
- nand->onfi_get_features = nand_onfi_get_set_features_notsupp;
- nand->ecc.read_page = docg4_read_page;
- nand->ecc.write_page = docg4_write_page;
- nand->ecc.read_page_raw = docg4_read_page_raw;
- nand->ecc.write_page_raw = docg4_write_page_raw;
- nand->ecc.read_oob = docg4_read_oob;
- nand->ecc.write_oob = docg4_write_oob;
-
- /*
- * The way the nand infrastructure code is written, a memory-based bbt
- * is not created if NAND_SKIP_BBTSCAN is set. With no memory bbt,
- * nand->block_bad() is used. So when ignoring bad blocks, we skip the
- * scan and define a dummy block_bad() which always returns 0.
- */
- if (ignore_badblocks) {
- nand->options |= NAND_SKIP_BBTSCAN;
- nand->block_bad = docg4_block_neverbad;
- }
-
-}
-
-static int __init read_id_reg(struct mtd_info *mtd)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct docg4_priv *doc = nand_get_controller_data(nand);
- void __iomem *docptr = doc->virtadr;
- uint16_t id1, id2;
-
- /* check for presence of g4 chip by reading id registers */
- id1 = readw(docptr + DOC_CHIPID);
- id1 = readw(docptr + DOCG4_MYSTERY_REG);
- id2 = readw(docptr + DOC_CHIPID_INV);
- id2 = readw(docptr + DOCG4_MYSTERY_REG);
-
- if (id1 == DOCG4_IDREG1_VALUE && id2 == DOCG4_IDREG2_VALUE) {
- dev_info(doc->dev,
- "NAND device: 128MiB Diskonchip G4 detected\n");
- return 0;
- }
-
- return -ENODEV;
-}
-
-static char const *part_probes[] = { "cmdlinepart", "saftlpart", NULL };
-
-static int __init probe_docg4(struct platform_device *pdev)
-{
- struct mtd_info *mtd;
- struct nand_chip *nand;
- void __iomem *virtadr;
- struct docg4_priv *doc;
- int len, retval;
- struct resource *r;
- struct device *dev = &pdev->dev;
-
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (r == NULL) {
- dev_err(dev, "no io memory resource defined!\n");
- return -ENODEV;
- }
-
- virtadr = ioremap(r->start, resource_size(r));
- if (!virtadr) {
- dev_err(dev, "Diskonchip ioremap failed: %pR\n", r);
- return -EIO;
- }
-
- len = sizeof(struct nand_chip) + sizeof(struct docg4_priv);
- nand = kzalloc(len, GFP_KERNEL);
- if (nand == NULL) {
- retval = -ENOMEM;
- goto fail_unmap;
- }
-
- mtd = nand_to_mtd(nand);
- doc = (struct docg4_priv *) (nand + 1);
- nand_set_controller_data(nand, doc);
- mtd->dev.parent = &pdev->dev;
- doc->virtadr = virtadr;
- doc->dev = dev;
-
- init_mtd_structs(mtd);
-
- /* initialize kernel bch algorithm */
- doc->bch = init_bch(DOCG4_M, DOCG4_T, DOCG4_PRIMITIVE_POLY);
- if (doc->bch == NULL) {
- retval = -EINVAL;
- goto fail;
- }
-
- platform_set_drvdata(pdev, doc);
-
- reset(mtd);
- retval = read_id_reg(mtd);
- if (retval == -ENODEV) {
- dev_warn(dev, "No diskonchip G4 device found.\n");
- goto fail;
- }
-
- retval = nand_scan_tail(mtd);
- if (retval)
- goto fail;
-
- retval = read_factory_bbt(mtd);
- if (retval)
- goto fail;
-
- retval = mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0);
- if (retval)
- goto fail;
-
- doc->mtd = mtd;
- return 0;
-
-fail:
- nand_release(mtd); /* deletes partitions and mtd devices */
- free_bch(doc->bch);
- kfree(nand);
-
-fail_unmap:
- iounmap(virtadr);
-
- return retval;
-}
-
-static int __exit cleanup_docg4(struct platform_device *pdev)
-{
- struct docg4_priv *doc = platform_get_drvdata(pdev);
- nand_release(doc->mtd);
- free_bch(doc->bch);
- kfree(mtd_to_nand(doc->mtd));
- iounmap(doc->virtadr);
- return 0;
-}
-
-static struct platform_driver docg4_driver = {
- .driver = {
- .name = "docg4",
- },
- .suspend = docg4_suspend,
- .resume = docg4_resume,
- .remove = __exit_p(cleanup_docg4),
-};
-
-module_platform_driver_probe(docg4_driver, probe_docg4);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mike Dunn");
-MODULE_DESCRIPTION("M-Systems DiskOnChip G4 device driver");
diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c
deleted file mode 100644
index 8b6dcd739ecb..000000000000
--- a/drivers/mtd/nand/fsl_elbc_nand.c
+++ /dev/null
@@ -1,979 +0,0 @@
-/* Freescale Enhanced Local Bus Controller NAND driver
- *
- * Copyright © 2006-2007, 2010 Freescale Semiconductor
- *
- * Authors: Nick Spence <nick.spence@freescale.com>,
- * Scott Wood <scottwood@freescale.com>
- * Jack Lan <jack.lan@freescale.com>
- * Roy Zang <tie-fei.zang@freescale.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/ioport.h>
-#include <linux/of_address.h>
-#include <linux/of_platform.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand_ecc.h>
-#include <linux/mtd/partitions.h>
-
-#include <asm/io.h>
-#include <asm/fsl_lbc.h>
-
-#define MAX_BANKS 8
-#define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */
-#define FCM_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait for FCM */
-
-/* mtd information per set */
-
-struct fsl_elbc_mtd {
- struct nand_chip chip;
- struct fsl_lbc_ctrl *ctrl;
-
- struct device *dev;
- int bank; /* Chip select bank number */
- u8 __iomem *vbase; /* Chip select base virtual address */
- int page_size; /* NAND page size (0=512, 1=2048) */
- unsigned int fmr; /* FCM Flash Mode Register value */
-};
-
-/* Freescale eLBC FCM controller information */
-
-struct fsl_elbc_fcm_ctrl {
- struct nand_hw_control controller;
- struct fsl_elbc_mtd *chips[MAX_BANKS];
-
- u8 __iomem *addr; /* Address of assigned FCM buffer */
- unsigned int page; /* Last page written to / read from */
- unsigned int read_bytes; /* Number of bytes read during command */
- unsigned int column; /* Saved column from SEQIN */
- unsigned int index; /* Pointer to next byte to 'read' */
- unsigned int status; /* status read from LTESR after last op */
- unsigned int mdr; /* UPM/FCM Data Register value */
- unsigned int use_mdr; /* Non zero if the MDR is to be set */
- unsigned int oob; /* Non zero if operating on OOB data */
- unsigned int counter; /* counter for the initializations */
- unsigned int max_bitflips; /* Saved during READ0 cmd */
-};
-
-/* These map to the positions used by the FCM hardware ECC generator */
-
-static int fsl_elbc_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
-
- if (section >= chip->ecc.steps)
- return -ERANGE;
-
- oobregion->offset = (16 * section) + 6;
- if (priv->fmr & FMR_ECCM)
- oobregion->offset += 2;
-
- oobregion->length = chip->ecc.bytes;
-
- return 0;
-}
-
-static int fsl_elbc_ooblayout_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
-
- if (section > chip->ecc.steps)
- return -ERANGE;
-
- if (!section) {
- oobregion->offset = 0;
- if (mtd->writesize > 512)
- oobregion->offset++;
- oobregion->length = (priv->fmr & FMR_ECCM) ? 7 : 5;
- } else {
- oobregion->offset = (16 * section) -
- ((priv->fmr & FMR_ECCM) ? 5 : 7);
- if (section < chip->ecc.steps)
- oobregion->length = 13;
- else
- oobregion->length = mtd->oobsize - oobregion->offset;
- }
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops fsl_elbc_ooblayout_ops = {
- .ecc = fsl_elbc_ooblayout_ecc,
- .free = fsl_elbc_ooblayout_free,
-};
-
-/*
- * ELBC may use HW ECC, so that OOB offsets, that NAND core uses for bbt,
- * interfere with ECC positions, that's why we implement our own descriptors.
- * OOB {11, 5}, works for both SP and LP chips, with ECCM = 1 and ECCM = 0.
- */
-static u8 bbt_pattern[] = {'B', 'b', 't', '0' };
-static u8 mirror_pattern[] = {'1', 't', 'b', 'B' };
-
-static struct nand_bbt_descr bbt_main_descr = {
- .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
- NAND_BBT_2BIT | NAND_BBT_VERSION,
- .offs = 11,
- .len = 4,
- .veroffs = 15,
- .maxblocks = 4,
- .pattern = bbt_pattern,
-};
-
-static struct nand_bbt_descr bbt_mirror_descr = {
- .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
- NAND_BBT_2BIT | NAND_BBT_VERSION,
- .offs = 11,
- .len = 4,
- .veroffs = 15,
- .maxblocks = 4,
- .pattern = mirror_pattern,
-};
-
-/*=================================*/
-
-/*
- * Set up the FCM hardware block and page address fields, and the fcm
- * structure addr field to point to the correct FCM buffer in memory
- */
-static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
- struct fsl_lbc_ctrl *ctrl = priv->ctrl;
- struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
- struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
- int buf_num;
-
- elbc_fcm_ctrl->page = page_addr;
-
- if (priv->page_size) {
- /*
- * large page size chip : FPAR[PI] save the lowest 6 bits,
- * FBAR[BLK] save the other bits.
- */
- out_be32(&lbc->fbar, page_addr >> 6);
- out_be32(&lbc->fpar,
- ((page_addr << FPAR_LP_PI_SHIFT) & FPAR_LP_PI) |
- (oob ? FPAR_LP_MS : 0) | column);
- buf_num = (page_addr & 1) << 2;
- } else {
- /*
- * small page size chip : FPAR[PI] save the lowest 5 bits,
- * FBAR[BLK] save the other bits.
- */
- out_be32(&lbc->fbar, page_addr >> 5);
- out_be32(&lbc->fpar,
- ((page_addr << FPAR_SP_PI_SHIFT) & FPAR_SP_PI) |
- (oob ? FPAR_SP_MS : 0) | column);
- buf_num = page_addr & 7;
- }
-
- elbc_fcm_ctrl->addr = priv->vbase + buf_num * 1024;
- elbc_fcm_ctrl->index = column;
-
- /* for OOB data point to the second half of the buffer */
- if (oob)
- elbc_fcm_ctrl->index += priv->page_size ? 2048 : 512;
-
- dev_vdbg(priv->dev, "set_addr: bank=%d, "
- "elbc_fcm_ctrl->addr=0x%p (0x%p), "
- "index %x, pes %d ps %d\n",
- buf_num, elbc_fcm_ctrl->addr, priv->vbase,
- elbc_fcm_ctrl->index,
- chip->phys_erase_shift, chip->page_shift);
-}
-
-/*
- * execute FCM command and wait for it to complete
- */
-static int fsl_elbc_run_command(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
- struct fsl_lbc_ctrl *ctrl = priv->ctrl;
- struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
- struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
-
- /* Setup the FMR[OP] to execute without write protection */
- out_be32(&lbc->fmr, priv->fmr | 3);
- if (elbc_fcm_ctrl->use_mdr)
- out_be32(&lbc->mdr, elbc_fcm_ctrl->mdr);
-
- dev_vdbg(priv->dev,
- "fsl_elbc_run_command: fmr=%08x fir=%08x fcr=%08x\n",
- in_be32(&lbc->fmr), in_be32(&lbc->fir), in_be32(&lbc->fcr));
- dev_vdbg(priv->dev,
- "fsl_elbc_run_command: fbar=%08x fpar=%08x "
- "fbcr=%08x bank=%d\n",
- in_be32(&lbc->fbar), in_be32(&lbc->fpar),
- in_be32(&lbc->fbcr), priv->bank);
-
- ctrl->irq_status = 0;
- /* execute special operation */
- out_be32(&lbc->lsor, priv->bank);
-
- /* wait for FCM complete flag or timeout */
- wait_event_timeout(ctrl->irq_wait, ctrl->irq_status,
- FCM_TIMEOUT_MSECS * HZ/1000);
- elbc_fcm_ctrl->status = ctrl->irq_status;
- /* store mdr value in case it was needed */
- if (elbc_fcm_ctrl->use_mdr)
- elbc_fcm_ctrl->mdr = in_be32(&lbc->mdr);
-
- elbc_fcm_ctrl->use_mdr = 0;
-
- if (elbc_fcm_ctrl->status != LTESR_CC) {
- dev_info(priv->dev,
- "command failed: fir %x fcr %x status %x mdr %x\n",
- in_be32(&lbc->fir), in_be32(&lbc->fcr),
- elbc_fcm_ctrl->status, elbc_fcm_ctrl->mdr);
- return -EIO;
- }
-
- if (chip->ecc.mode != NAND_ECC_HW)
- return 0;
-
- elbc_fcm_ctrl->max_bitflips = 0;
-
- if (elbc_fcm_ctrl->read_bytes == mtd->writesize + mtd->oobsize) {
- uint32_t lteccr = in_be32(&lbc->lteccr);
- /*
- * if command was a full page read and the ELBC
- * has the LTECCR register, then bits 12-15 (ppc order) of
- * LTECCR indicates which 512 byte sub-pages had fixed errors.
- * bits 28-31 are uncorrectable errors, marked elsewhere.
- * for small page nand only 1 bit is used.
- * if the ELBC doesn't have the lteccr register it reads 0
- * FIXME: 4 bits can be corrected on NANDs with 2k pages, so
- * count the number of sub-pages with bitflips and update
- * ecc_stats.corrected accordingly.
- */
- if (lteccr & 0x000F000F)
- out_be32(&lbc->lteccr, 0x000F000F); /* clear lteccr */
- if (lteccr & 0x000F0000) {
- mtd->ecc_stats.corrected++;
- elbc_fcm_ctrl->max_bitflips = 1;
- }
- }
-
- return 0;
-}
-
-static void fsl_elbc_do_read(struct nand_chip *chip, int oob)
-{
- struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
- struct fsl_lbc_ctrl *ctrl = priv->ctrl;
- struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
-
- if (priv->page_size) {
- out_be32(&lbc->fir,
- (FIR_OP_CM0 << FIR_OP0_SHIFT) |
- (FIR_OP_CA << FIR_OP1_SHIFT) |
- (FIR_OP_PA << FIR_OP2_SHIFT) |
- (FIR_OP_CM1 << FIR_OP3_SHIFT) |
- (FIR_OP_RBW << FIR_OP4_SHIFT));
-
- out_be32(&lbc->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) |
- (NAND_CMD_READSTART << FCR_CMD1_SHIFT));
- } else {
- out_be32(&lbc->fir,
- (FIR_OP_CM0 << FIR_OP0_SHIFT) |
- (FIR_OP_CA << FIR_OP1_SHIFT) |
- (FIR_OP_PA << FIR_OP2_SHIFT) |
- (FIR_OP_RBW << FIR_OP3_SHIFT));
-
- if (oob)
- out_be32(&lbc->fcr, NAND_CMD_READOOB << FCR_CMD0_SHIFT);
- else
- out_be32(&lbc->fcr, NAND_CMD_READ0 << FCR_CMD0_SHIFT);
- }
-}
-
-/* cmdfunc send commands to the FCM */
-static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
- int column, int page_addr)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
- struct fsl_lbc_ctrl *ctrl = priv->ctrl;
- struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
- struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
-
- elbc_fcm_ctrl->use_mdr = 0;
-
- /* clear the read buffer */
- elbc_fcm_ctrl->read_bytes = 0;
- if (command != NAND_CMD_PAGEPROG)
- elbc_fcm_ctrl->index = 0;
-
- switch (command) {
- /* READ0 and READ1 read the entire buffer to use hardware ECC. */
- case NAND_CMD_READ1:
- column += 256;
-
- /* fall-through */
- case NAND_CMD_READ0:
- dev_dbg(priv->dev,
- "fsl_elbc_cmdfunc: NAND_CMD_READ0, page_addr:"
- " 0x%x, column: 0x%x.\n", page_addr, column);
-
-
- out_be32(&lbc->fbcr, 0); /* read entire page to enable ECC */
- set_addr(mtd, 0, page_addr, 0);
-
- elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize;
- elbc_fcm_ctrl->index += column;
-
- fsl_elbc_do_read(chip, 0);
- fsl_elbc_run_command(mtd);
- return;
-
- /* READOOB reads only the OOB because no ECC is performed. */
- case NAND_CMD_READOOB:
- dev_vdbg(priv->dev,
- "fsl_elbc_cmdfunc: NAND_CMD_READOOB, page_addr:"
- " 0x%x, column: 0x%x.\n", page_addr, column);
-
- out_be32(&lbc->fbcr, mtd->oobsize - column);
- set_addr(mtd, column, page_addr, 1);
-
- elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize;
-
- fsl_elbc_do_read(chip, 1);
- fsl_elbc_run_command(mtd);
- return;
-
- case NAND_CMD_READID:
- case NAND_CMD_PARAM:
- dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD %x\n", command);
-
- out_be32(&lbc->fir, (FIR_OP_CM0 << FIR_OP0_SHIFT) |
- (FIR_OP_UA << FIR_OP1_SHIFT) |
- (FIR_OP_RBW << FIR_OP2_SHIFT));
- out_be32(&lbc->fcr, command << FCR_CMD0_SHIFT);
- /*
- * although currently it's 8 bytes for READID, we always read
- * the maximum 256 bytes(for PARAM)
- */
- out_be32(&lbc->fbcr, 256);
- elbc_fcm_ctrl->read_bytes = 256;
- elbc_fcm_ctrl->use_mdr = 1;
- elbc_fcm_ctrl->mdr = column;
- set_addr(mtd, 0, 0, 0);
- fsl_elbc_run_command(mtd);
- return;
-
- /* ERASE1 stores the block and page address */
- case NAND_CMD_ERASE1:
- dev_vdbg(priv->dev,
- "fsl_elbc_cmdfunc: NAND_CMD_ERASE1, "
- "page_addr: 0x%x.\n", page_addr);
- set_addr(mtd, 0, page_addr, 0);
- return;
-
- /* ERASE2 uses the block and page address from ERASE1 */
- case NAND_CMD_ERASE2:
- dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n");
-
- out_be32(&lbc->fir,
- (FIR_OP_CM0 << FIR_OP0_SHIFT) |
- (FIR_OP_PA << FIR_OP1_SHIFT) |
- (FIR_OP_CM2 << FIR_OP2_SHIFT) |
- (FIR_OP_CW1 << FIR_OP3_SHIFT) |
- (FIR_OP_RS << FIR_OP4_SHIFT));
-
- out_be32(&lbc->fcr,
- (NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) |
- (NAND_CMD_STATUS << FCR_CMD1_SHIFT) |
- (NAND_CMD_ERASE2 << FCR_CMD2_SHIFT));
-
- out_be32(&lbc->fbcr, 0);
- elbc_fcm_ctrl->read_bytes = 0;
- elbc_fcm_ctrl->use_mdr = 1;
-
- fsl_elbc_run_command(mtd);
- return;
-
- /* SEQIN sets up the addr buffer and all registers except the length */
- case NAND_CMD_SEQIN: {
- __be32 fcr;
- dev_vdbg(priv->dev,
- "fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, "
- "page_addr: 0x%x, column: 0x%x.\n",
- page_addr, column);
-
- elbc_fcm_ctrl->column = column;
- elbc_fcm_ctrl->use_mdr = 1;
-
- if (column >= mtd->writesize) {
- /* OOB area */
- column -= mtd->writesize;
- elbc_fcm_ctrl->oob = 1;
- } else {
- WARN_ON(column != 0);
- elbc_fcm_ctrl->oob = 0;
- }
-
- fcr = (NAND_CMD_STATUS << FCR_CMD1_SHIFT) |
- (NAND_CMD_SEQIN << FCR_CMD2_SHIFT) |
- (NAND_CMD_PAGEPROG << FCR_CMD3_SHIFT);
-
- if (priv->page_size) {
- out_be32(&lbc->fir,
- (FIR_OP_CM2 << FIR_OP0_SHIFT) |
- (FIR_OP_CA << FIR_OP1_SHIFT) |
- (FIR_OP_PA << FIR_OP2_SHIFT) |
- (FIR_OP_WB << FIR_OP3_SHIFT) |
- (FIR_OP_CM3 << FIR_OP4_SHIFT) |
- (FIR_OP_CW1 << FIR_OP5_SHIFT) |
- (FIR_OP_RS << FIR_OP6_SHIFT));
- } else {
- out_be32(&lbc->fir,
- (FIR_OP_CM0 << FIR_OP0_SHIFT) |
- (FIR_OP_CM2 << FIR_OP1_SHIFT) |
- (FIR_OP_CA << FIR_OP2_SHIFT) |
- (FIR_OP_PA << FIR_OP3_SHIFT) |
- (FIR_OP_WB << FIR_OP4_SHIFT) |
- (FIR_OP_CM3 << FIR_OP5_SHIFT) |
- (FIR_OP_CW1 << FIR_OP6_SHIFT) |
- (FIR_OP_RS << FIR_OP7_SHIFT));
-
- if (elbc_fcm_ctrl->oob)
- /* OOB area --> READOOB */
- fcr |= NAND_CMD_READOOB << FCR_CMD0_SHIFT;
- else
- /* First 256 bytes --> READ0 */
- fcr |= NAND_CMD_READ0 << FCR_CMD0_SHIFT;
- }
-
- out_be32(&lbc->fcr, fcr);
- set_addr(mtd, column, page_addr, elbc_fcm_ctrl->oob);
- return;
- }
-
- /* PAGEPROG reuses all of the setup from SEQIN and adds the length */
- case NAND_CMD_PAGEPROG: {
- dev_vdbg(priv->dev,
- "fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG "
- "writing %d bytes.\n", elbc_fcm_ctrl->index);
-
- /* if the write did not start at 0 or is not a full page
- * then set the exact length, otherwise use a full page
- * write so the HW generates the ECC.
- */
- if (elbc_fcm_ctrl->oob || elbc_fcm_ctrl->column != 0 ||
- elbc_fcm_ctrl->index != mtd->writesize + mtd->oobsize)
- out_be32(&lbc->fbcr,
- elbc_fcm_ctrl->index - elbc_fcm_ctrl->column);
- else
- out_be32(&lbc->fbcr, 0);
-
- fsl_elbc_run_command(mtd);
- return;
- }
-
- /* CMD_STATUS must read the status byte while CEB is active */
- /* Note - it does not wait for the ready line */
- case NAND_CMD_STATUS:
- out_be32(&lbc->fir,
- (FIR_OP_CM0 << FIR_OP0_SHIFT) |
- (FIR_OP_RBW << FIR_OP1_SHIFT));
- out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT);
- out_be32(&lbc->fbcr, 1);
- set_addr(mtd, 0, 0, 0);
- elbc_fcm_ctrl->read_bytes = 1;
-
- fsl_elbc_run_command(mtd);
-
- /* The chip always seems to report that it is
- * write-protected, even when it is not.
- */
- setbits8(elbc_fcm_ctrl->addr, NAND_STATUS_WP);
- return;
-
- /* RESET without waiting for the ready line */
- case NAND_CMD_RESET:
- dev_dbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_RESET.\n");
- out_be32(&lbc->fir, FIR_OP_CM0 << FIR_OP0_SHIFT);
- out_be32(&lbc->fcr, NAND_CMD_RESET << FCR_CMD0_SHIFT);
- fsl_elbc_run_command(mtd);
- return;
-
- default:
- dev_err(priv->dev,
- "fsl_elbc_cmdfunc: error, unsupported command 0x%x.\n",
- command);
- }
-}
-
-static void fsl_elbc_select_chip(struct mtd_info *mtd, int chip)
-{
- /* The hardware does not seem to support multiple
- * chips per bank.
- */
-}
-
-/*
- * Write buf to the FCM Controller Data Buffer
- */
-static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
- struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
- unsigned int bufsize = mtd->writesize + mtd->oobsize;
-
- if (len <= 0) {
- dev_err(priv->dev, "write_buf of %d bytes", len);
- elbc_fcm_ctrl->status = 0;
- return;
- }
-
- if ((unsigned int)len > bufsize - elbc_fcm_ctrl->index) {
- dev_err(priv->dev,
- "write_buf beyond end of buffer "
- "(%d requested, %u available)\n",
- len, bufsize - elbc_fcm_ctrl->index);
- len = bufsize - elbc_fcm_ctrl->index;
- }
-
- memcpy_toio(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], buf, len);
- /*
- * This is workaround for the weird elbc hangs during nand write,
- * Scott Wood says: "...perhaps difference in how long it takes a
- * write to make it through the localbus compared to a write to IMMR
- * is causing problems, and sync isn't helping for some reason."
- * Reading back the last byte helps though.
- */
- in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index] + len - 1);
-
- elbc_fcm_ctrl->index += len;
-}
-
-/*
- * read a byte from either the FCM hardware buffer if it has any data left
- * otherwise issue a command to read a single byte.
- */
-static u8 fsl_elbc_read_byte(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
- struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
-
- /* If there are still bytes in the FCM, then use the next byte. */
- if (elbc_fcm_ctrl->index < elbc_fcm_ctrl->read_bytes)
- return in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index++]);
-
- dev_err(priv->dev, "read_byte beyond end of buffer\n");
- return ERR_BYTE;
-}
-
-/*
- * Read from the FCM Controller Data Buffer
- */
-static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
- struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
- int avail;
-
- if (len < 0)
- return;
-
- avail = min((unsigned int)len,
- elbc_fcm_ctrl->read_bytes - elbc_fcm_ctrl->index);
- memcpy_fromio(buf, &elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], avail);
- elbc_fcm_ctrl->index += avail;
-
- if (len > avail)
- dev_err(priv->dev,
- "read_buf beyond end of buffer "
- "(%d requested, %d available)\n",
- len, avail);
-}
-
-/* This function is called after Program and Erase Operations to
- * check for success or failure.
- */
-static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip)
-{
- struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
- struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
-
- if (elbc_fcm_ctrl->status != LTESR_CC)
- return NAND_STATUS_FAIL;
-
- /* The chip always seems to report that it is
- * write-protected, even when it is not.
- */
- return (elbc_fcm_ctrl->mdr & 0xff) | NAND_STATUS_WP;
-}
-
-static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
- struct fsl_lbc_ctrl *ctrl = priv->ctrl;
- struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
- unsigned int al;
-
- /* calculate FMR Address Length field */
- al = 0;
- if (chip->pagemask & 0xffff0000)
- al++;
- if (chip->pagemask & 0xff000000)
- al++;
-
- priv->fmr |= al << FMR_AL_SHIFT;
-
- dev_dbg(priv->dev, "fsl_elbc_init: nand->numchips = %d\n",
- chip->numchips);
- dev_dbg(priv->dev, "fsl_elbc_init: nand->chipsize = %lld\n",
- chip->chipsize);
- dev_dbg(priv->dev, "fsl_elbc_init: nand->pagemask = %8x\n",
- chip->pagemask);
- dev_dbg(priv->dev, "fsl_elbc_init: nand->chip_delay = %d\n",
- chip->chip_delay);
- dev_dbg(priv->dev, "fsl_elbc_init: nand->badblockpos = %d\n",
- chip->badblockpos);
- dev_dbg(priv->dev, "fsl_elbc_init: nand->chip_shift = %d\n",
- chip->chip_shift);
- dev_dbg(priv->dev, "fsl_elbc_init: nand->page_shift = %d\n",
- chip->page_shift);
- dev_dbg(priv->dev, "fsl_elbc_init: nand->phys_erase_shift = %d\n",
- chip->phys_erase_shift);
- dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.mode = %d\n",
- chip->ecc.mode);
- dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.steps = %d\n",
- chip->ecc.steps);
- dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.bytes = %d\n",
- chip->ecc.bytes);
- dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.total = %d\n",
- chip->ecc.total);
- dev_dbg(priv->dev, "fsl_elbc_init: mtd->ooblayout = %p\n",
- mtd->ooblayout);
- dev_dbg(priv->dev, "fsl_elbc_init: mtd->flags = %08x\n", mtd->flags);
- dev_dbg(priv->dev, "fsl_elbc_init: mtd->size = %lld\n", mtd->size);
- dev_dbg(priv->dev, "fsl_elbc_init: mtd->erasesize = %d\n",
- mtd->erasesize);
- dev_dbg(priv->dev, "fsl_elbc_init: mtd->writesize = %d\n",
- mtd->writesize);
- dev_dbg(priv->dev, "fsl_elbc_init: mtd->oobsize = %d\n",
- mtd->oobsize);
-
- /* adjust Option Register and ECC to match Flash page size */
- if (mtd->writesize == 512) {
- priv->page_size = 0;
- clrbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS);
- } else if (mtd->writesize == 2048) {
- priv->page_size = 1;
- setbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS);
- } else {
- dev_err(priv->dev,
- "fsl_elbc_init: page size %d is not supported\n",
- mtd->writesize);
- return -1;
- }
-
- return 0;
-}
-
-static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page)
-{
- struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
- struct fsl_lbc_ctrl *ctrl = priv->ctrl;
- struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
-
- nand_read_page_op(chip, page, 0, buf, mtd->writesize);
- if (oob_required)
- fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize);
-
- if (fsl_elbc_wait(mtd, chip) & NAND_STATUS_FAIL)
- mtd->ecc_stats.failed++;
-
- return elbc_fcm_ctrl->max_bitflips;
-}
-
-/* ECC will be calculated automatically, and errors will be detected in
- * waitfunc.
- */
-static int fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf, int oob_required, int page)
-{
- nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);
- fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
-
- return nand_prog_page_end_op(chip);
-}
-
-/* ECC will be calculated automatically, and errors will be detected in
- * waitfunc.
- */
-static int fsl_elbc_write_subpage(struct mtd_info *mtd, struct nand_chip *chip,
- uint32_t offset, uint32_t data_len,
- const uint8_t *buf, int oob_required, int page)
-{
- nand_prog_page_begin_op(chip, page, 0, NULL, 0);
- fsl_elbc_write_buf(mtd, buf, mtd->writesize);
- fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
- return nand_prog_page_end_op(chip);
-}
-
-static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
-{
- struct fsl_lbc_ctrl *ctrl = priv->ctrl;
- struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
- struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
- struct nand_chip *chip = &priv->chip;
- struct mtd_info *mtd = nand_to_mtd(chip);
-
- dev_dbg(priv->dev, "eLBC Set Information for bank %d\n", priv->bank);
-
- /* Fill in fsl_elbc_mtd structure */
- mtd->dev.parent = priv->dev;
- nand_set_flash_node(chip, priv->dev->of_node);
-
- /* set timeout to maximum */
- priv->fmr = 15 << FMR_CWTO_SHIFT;
- if (in_be32(&lbc->bank[priv->bank].or) & OR_FCM_PGS)
- priv->fmr |= FMR_ECCM;
-
- /* fill in nand_chip structure */
- /* set up function call table */
- chip->read_byte = fsl_elbc_read_byte;
- chip->write_buf = fsl_elbc_write_buf;
- chip->read_buf = fsl_elbc_read_buf;
- chip->select_chip = fsl_elbc_select_chip;
- chip->cmdfunc = fsl_elbc_cmdfunc;
- chip->waitfunc = fsl_elbc_wait;
- chip->onfi_set_features = nand_onfi_get_set_features_notsupp;
- chip->onfi_get_features = nand_onfi_get_set_features_notsupp;
-
- chip->bbt_td = &bbt_main_descr;
- chip->bbt_md = &bbt_mirror_descr;
-
- /* set up nand options */
- chip->bbt_options = NAND_BBT_USE_FLASH;
-
- chip->controller = &elbc_fcm_ctrl->controller;
- nand_set_controller_data(chip, priv);
-
- chip->ecc.read_page = fsl_elbc_read_page;
- chip->ecc.write_page = fsl_elbc_write_page;
- chip->ecc.write_subpage = fsl_elbc_write_subpage;
-
- /* If CS Base Register selects full hardware ECC then use it */
- if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) ==
- BR_DECC_CHK_GEN) {
- chip->ecc.mode = NAND_ECC_HW;
- mtd_set_ooblayout(mtd, &fsl_elbc_ooblayout_ops);
- chip->ecc.size = 512;
- chip->ecc.bytes = 3;
- chip->ecc.strength = 1;
- } else {
- /* otherwise fall back to default software ECC */
- chip->ecc.mode = NAND_ECC_SOFT;
- chip->ecc.algo = NAND_ECC_HAMMING;
- }
-
- return 0;
-}
-
-static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv)
-{
- struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
- struct mtd_info *mtd = nand_to_mtd(&priv->chip);
-
- nand_release(mtd);
-
- kfree(mtd->name);
-
- if (priv->vbase)
- iounmap(priv->vbase);
-
- elbc_fcm_ctrl->chips[priv->bank] = NULL;
- kfree(priv);
- return 0;
-}
-
-static DEFINE_MUTEX(fsl_elbc_nand_mutex);
-
-static int fsl_elbc_nand_probe(struct platform_device *pdev)
-{
- struct fsl_lbc_regs __iomem *lbc;
- struct fsl_elbc_mtd *priv;
- struct resource res;
- struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl;
- static const char *part_probe_types[]
- = { "cmdlinepart", "RedBoot", "ofpart", NULL };
- int ret;
- int bank;
- struct device *dev;
- struct device_node *node = pdev->dev.of_node;
- struct mtd_info *mtd;
-
- if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
- return -ENODEV;
- lbc = fsl_lbc_ctrl_dev->regs;
- dev = fsl_lbc_ctrl_dev->dev;
-
- /* get, allocate and map the memory resource */
- ret = of_address_to_resource(node, 0, &res);
- if (ret) {
- dev_err(dev, "failed to get resource\n");
- return ret;
- }
-
- /* find which chip select it is connected to */
- for (bank = 0; bank < MAX_BANKS; bank++)
- if ((in_be32(&lbc->bank[bank].br) & BR_V) &&
- (in_be32(&lbc->bank[bank].br) & BR_MSEL) == BR_MS_FCM &&
- (in_be32(&lbc->bank[bank].br) &
- in_be32(&lbc->bank[bank].or) & BR_BA)
- == fsl_lbc_addr(res.start))
- break;
-
- if (bank >= MAX_BANKS) {
- dev_err(dev, "address did not match any chip selects\n");
- return -ENODEV;
- }
-
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- mutex_lock(&fsl_elbc_nand_mutex);
- if (!fsl_lbc_ctrl_dev->nand) {
- elbc_fcm_ctrl = kzalloc(sizeof(*elbc_fcm_ctrl), GFP_KERNEL);
- if (!elbc_fcm_ctrl) {
- mutex_unlock(&fsl_elbc_nand_mutex);
- ret = -ENOMEM;
- goto err;
- }
- elbc_fcm_ctrl->counter++;
-
- nand_hw_control_init(&elbc_fcm_ctrl->controller);
- fsl_lbc_ctrl_dev->nand = elbc_fcm_ctrl;
- } else {
- elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand;
- }
- mutex_unlock(&fsl_elbc_nand_mutex);
-
- elbc_fcm_ctrl->chips[bank] = priv;
- priv->bank = bank;
- priv->ctrl = fsl_lbc_ctrl_dev;
- priv->dev = &pdev->dev;
- dev_set_drvdata(priv->dev, priv);
-
- priv->vbase = ioremap(res.start, resource_size(&res));
- if (!priv->vbase) {
- dev_err(dev, "failed to map chip region\n");
- ret = -ENOMEM;
- goto err;
- }
-
- mtd = nand_to_mtd(&priv->chip);
- mtd->name = kasprintf(GFP_KERNEL, "%llx.flash", (u64)res.start);
- if (!nand_to_mtd(&priv->chip)->name) {
- ret = -ENOMEM;
- goto err;
- }
-
- ret = fsl_elbc_chip_init(priv);
- if (ret)
- goto err;
-
- ret = nand_scan_ident(mtd, 1, NULL);
- if (ret)
- goto err;
-
- ret = fsl_elbc_chip_init_tail(mtd);
- if (ret)
- goto err;
-
- ret = nand_scan_tail(mtd);
- if (ret)
- goto err;
-
- /* First look for RedBoot table or partitions on the command
- * line, these take precedence over device tree information */
- mtd_device_parse_register(mtd, part_probe_types, NULL,
- NULL, 0);
-
- printk(KERN_INFO "eLBC NAND device at 0x%llx, bank %d\n",
- (unsigned long long)res.start, priv->bank);
- return 0;
-
-err:
- fsl_elbc_chip_remove(priv);
- return ret;
-}
-
-static int fsl_elbc_nand_remove(struct platform_device *pdev)
-{
- struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand;
- struct fsl_elbc_mtd *priv = dev_get_drvdata(&pdev->dev);
-
- fsl_elbc_chip_remove(priv);
-
- mutex_lock(&fsl_elbc_nand_mutex);
- elbc_fcm_ctrl->counter--;
- if (!elbc_fcm_ctrl->counter) {
- fsl_lbc_ctrl_dev->nand = NULL;
- kfree(elbc_fcm_ctrl);
- }
- mutex_unlock(&fsl_elbc_nand_mutex);
-
- return 0;
-
-}
-
-static const struct of_device_id fsl_elbc_nand_match[] = {
- { .compatible = "fsl,elbc-fcm-nand", },
- {}
-};
-MODULE_DEVICE_TABLE(of, fsl_elbc_nand_match);
-
-static struct platform_driver fsl_elbc_nand_driver = {
- .driver = {
- .name = "fsl,elbc-fcm-nand",
- .of_match_table = fsl_elbc_nand_match,
- },
- .probe = fsl_elbc_nand_probe,
- .remove = fsl_elbc_nand_remove,
-};
-
-module_platform_driver(fsl_elbc_nand_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Freescale");
-MODULE_DESCRIPTION("Freescale Enhanced Local Bus Controller MTD NAND driver");
diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c
deleted file mode 100644
index 5a9c2f0020c2..000000000000
--- a/drivers/mtd/nand/fsl_ifc_nand.c
+++ /dev/null
@@ -1,1111 +0,0 @@
-/*
- * Freescale Integrated Flash Controller NAND driver
- *
- * Copyright 2011-2012 Freescale Semiconductor, Inc
- *
- * Author: Dipen Dudhat <Dipen.Dudhat@freescale.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/of_address.h>
-#include <linux/slab.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/partitions.h>
-#include <linux/mtd/nand_ecc.h>
-#include <linux/fsl_ifc.h>
-
-#define ERR_BYTE 0xFF /* Value returned for read
- bytes when read failed */
-#define IFC_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait
- for IFC NAND Machine */
-
-struct fsl_ifc_ctrl;
-
-/* mtd information per set */
-struct fsl_ifc_mtd {
- struct nand_chip chip;
- struct fsl_ifc_ctrl *ctrl;
-
- struct device *dev;
- int bank; /* Chip select bank number */
- unsigned int bufnum_mask; /* bufnum = page & bufnum_mask */
- u8 __iomem *vbase; /* Chip select base virtual address */
-};
-
-/* overview of the fsl ifc controller */
-struct fsl_ifc_nand_ctrl {
- struct nand_hw_control controller;
- struct fsl_ifc_mtd *chips[FSL_IFC_BANK_COUNT];
-
- void __iomem *addr; /* Address of assigned IFC buffer */
- unsigned int page; /* Last page written to / read from */
- unsigned int read_bytes;/* Number of bytes read during command */
- unsigned int column; /* Saved column from SEQIN */
- unsigned int index; /* Pointer to next byte to 'read' */
- unsigned int oob; /* Non zero if operating on OOB data */
- unsigned int eccread; /* Non zero for a full-page ECC read */
- unsigned int counter; /* counter for the initializations */
- unsigned int max_bitflips; /* Saved during READ0 cmd */
-};
-
-static struct fsl_ifc_nand_ctrl *ifc_nand_ctrl;
-
-/*
- * Generic flash bbt descriptors
- */
-static u8 bbt_pattern[] = {'B', 'b', 't', '0' };
-static u8 mirror_pattern[] = {'1', 't', 'b', 'B' };
-
-static struct nand_bbt_descr bbt_main_descr = {
- .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
- NAND_BBT_2BIT | NAND_BBT_VERSION,
- .offs = 2, /* 0 on 8-bit small page */
- .len = 4,
- .veroffs = 6,
- .maxblocks = 4,
- .pattern = bbt_pattern,
-};
-
-static struct nand_bbt_descr bbt_mirror_descr = {
- .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
- NAND_BBT_2BIT | NAND_BBT_VERSION,
- .offs = 2, /* 0 on 8-bit small page */
- .len = 4,
- .veroffs = 6,
- .maxblocks = 4,
- .pattern = mirror_pattern,
-};
-
-static int fsl_ifc_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- if (section)
- return -ERANGE;
-
- oobregion->offset = 8;
- oobregion->length = chip->ecc.total;
-
- return 0;
-}
-
-static int fsl_ifc_ooblayout_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- if (section > 1)
- return -ERANGE;
-
- if (mtd->writesize == 512 &&
- !(chip->options & NAND_BUSWIDTH_16)) {
- if (!section) {
- oobregion->offset = 0;
- oobregion->length = 5;
- } else {
- oobregion->offset = 6;
- oobregion->length = 2;
- }
-
- return 0;
- }
-
- if (!section) {
- oobregion->offset = 2;
- oobregion->length = 6;
- } else {
- oobregion->offset = chip->ecc.total + 8;
- oobregion->length = mtd->oobsize - oobregion->offset;
- }
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops fsl_ifc_ooblayout_ops = {
- .ecc = fsl_ifc_ooblayout_ecc,
- .free = fsl_ifc_ooblayout_free,
-};
-
-/*
- * Set up the IFC hardware block and page address fields, and the ifc nand
- * structure addr field to point to the correct IFC buffer in memory
- */
-static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
- struct fsl_ifc_ctrl *ctrl = priv->ctrl;
- struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
- int buf_num;
-
- ifc_nand_ctrl->page = page_addr;
- /* Program ROW0/COL0 */
- ifc_out32(page_addr, &ifc->ifc_nand.row0);
- ifc_out32((oob ? IFC_NAND_COL_MS : 0) | column, &ifc->ifc_nand.col0);
-
- buf_num = page_addr & priv->bufnum_mask;
-
- ifc_nand_ctrl->addr = priv->vbase + buf_num * (mtd->writesize * 2);
- ifc_nand_ctrl->index = column;
-
- /* for OOB data point to the second half of the buffer */
- if (oob)
- ifc_nand_ctrl->index += mtd->writesize;
-}
-
-/* returns nonzero if entire page is blank */
-static int check_read_ecc(struct mtd_info *mtd, struct fsl_ifc_ctrl *ctrl,
- u32 eccstat, unsigned int bufnum)
-{
- return (eccstat >> ((3 - bufnum % 4) * 8)) & 15;
-}
-
-/*
- * execute IFC NAND command and wait for it to complete
- */
-static void fsl_ifc_run_command(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
- struct fsl_ifc_ctrl *ctrl = priv->ctrl;
- struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl;
- struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
- u32 eccstat;
- int i;
-
- /* set the chip select for NAND Transaction */
- ifc_out32(priv->bank << IFC_NAND_CSEL_SHIFT,
- &ifc->ifc_nand.nand_csel);
-
- dev_vdbg(priv->dev,
- "%s: fir0=%08x fcr0=%08x\n",
- __func__,
- ifc_in32(&ifc->ifc_nand.nand_fir0),
- ifc_in32(&ifc->ifc_nand.nand_fcr0));
-
- ctrl->nand_stat = 0;
-
- /* start read/write seq */
- ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt);
-
- /* wait for command complete flag or timeout */
- wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat,
- msecs_to_jiffies(IFC_TIMEOUT_MSECS));
-
- /* ctrl->nand_stat will be updated from IRQ context */
- if (!ctrl->nand_stat)
- dev_err(priv->dev, "Controller is not responding\n");
- if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_FTOER)
- dev_err(priv->dev, "NAND Flash Timeout Error\n");
- if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_WPER)
- dev_err(priv->dev, "NAND Flash Write Protect Error\n");
-
- nctrl->max_bitflips = 0;
-
- if (nctrl->eccread) {
- int errors;
- int bufnum = nctrl->page & priv->bufnum_mask;
- int sector_start = bufnum * chip->ecc.steps;
- int sector_end = sector_start + chip->ecc.steps - 1;
- __be32 *eccstat_regs;
-
- eccstat_regs = ifc->ifc_nand.nand_eccstat;
- eccstat = ifc_in32(&eccstat_regs[sector_start / 4]);
-
- for (i = sector_start; i <= sector_end; i++) {
- if (i != sector_start && !(i % 4))
- eccstat = ifc_in32(&eccstat_regs[i / 4]);
-
- errors = check_read_ecc(mtd, ctrl, eccstat, i);
-
- if (errors == 15) {
- /*
- * Uncorrectable error.
- * We'll check for blank pages later.
- *
- * We disable ECCER reporting due to...
- * erratum IFC-A002770 -- so report it now if we
- * see an uncorrectable error in ECCSTAT.
- */
- ctrl->nand_stat |= IFC_NAND_EVTER_STAT_ECCER;
- continue;
- }
-
- mtd->ecc_stats.corrected += errors;
- nctrl->max_bitflips = max_t(unsigned int,
- nctrl->max_bitflips,
- errors);
- }
-
- nctrl->eccread = 0;
- }
-}
-
-static void fsl_ifc_do_read(struct nand_chip *chip,
- int oob,
- struct mtd_info *mtd)
-{
- struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
- struct fsl_ifc_ctrl *ctrl = priv->ctrl;
- struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
-
- /* Program FIR/IFC_NAND_FCR0 for Small/Large page */
- if (mtd->writesize > 512) {
- ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
- (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) |
- (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) |
- (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) |
- (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP4_SHIFT),
- &ifc->ifc_nand.nand_fir0);
- ifc_out32(0x0, &ifc->ifc_nand.nand_fir1);
-
- ifc_out32((NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) |
- (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT),
- &ifc->ifc_nand.nand_fcr0);
- } else {
- ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
- (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) |
- (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) |
- (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP3_SHIFT),
- &ifc->ifc_nand.nand_fir0);
- ifc_out32(0x0, &ifc->ifc_nand.nand_fir1);
-
- if (oob)
- ifc_out32(NAND_CMD_READOOB <<
- IFC_NAND_FCR0_CMD0_SHIFT,
- &ifc->ifc_nand.nand_fcr0);
- else
- ifc_out32(NAND_CMD_READ0 <<
- IFC_NAND_FCR0_CMD0_SHIFT,
- &ifc->ifc_nand.nand_fcr0);
- }
-}
-
-/* cmdfunc send commands to the IFC NAND Machine */
-static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
- int column, int page_addr) {
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
- struct fsl_ifc_ctrl *ctrl = priv->ctrl;
- struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
-
- /* clear the read buffer */
- ifc_nand_ctrl->read_bytes = 0;
- if (command != NAND_CMD_PAGEPROG)
- ifc_nand_ctrl->index = 0;
-
- switch (command) {
- /* READ0 read the entire buffer to use hardware ECC. */
- case NAND_CMD_READ0:
- ifc_out32(0, &ifc->ifc_nand.nand_fbcr);
- set_addr(mtd, 0, page_addr, 0);
-
- ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize;
- ifc_nand_ctrl->index += column;
-
- if (chip->ecc.mode == NAND_ECC_HW)
- ifc_nand_ctrl->eccread = 1;
-
- fsl_ifc_do_read(chip, 0, mtd);
- fsl_ifc_run_command(mtd);
- return;
-
- /* READOOB reads only the OOB because no ECC is performed. */
- case NAND_CMD_READOOB:
- ifc_out32(mtd->oobsize - column, &ifc->ifc_nand.nand_fbcr);
- set_addr(mtd, column, page_addr, 1);
-
- ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize;
-
- fsl_ifc_do_read(chip, 1, mtd);
- fsl_ifc_run_command(mtd);
-
- return;
-
- case NAND_CMD_READID:
- case NAND_CMD_PARAM: {
- int timing = IFC_FIR_OP_RB;
- if (command == NAND_CMD_PARAM)
- timing = IFC_FIR_OP_RBCD;
-
- ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
- (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) |
- (timing << IFC_NAND_FIR0_OP2_SHIFT),
- &ifc->ifc_nand.nand_fir0);
- ifc_out32(command << IFC_NAND_FCR0_CMD0_SHIFT,
- &ifc->ifc_nand.nand_fcr0);
- ifc_out32(column, &ifc->ifc_nand.row3);
-
- /*
- * although currently it's 8 bytes for READID, we always read
- * the maximum 256 bytes(for PARAM)
- */
- ifc_out32(256, &ifc->ifc_nand.nand_fbcr);
- ifc_nand_ctrl->read_bytes = 256;
-
- set_addr(mtd, 0, 0, 0);
- fsl_ifc_run_command(mtd);
- return;
- }
-
- /* ERASE1 stores the block and page address */
- case NAND_CMD_ERASE1:
- set_addr(mtd, 0, page_addr, 0);
- return;
-
- /* ERASE2 uses the block and page address from ERASE1 */
- case NAND_CMD_ERASE2:
- ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
- (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP1_SHIFT) |
- (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP2_SHIFT),
- &ifc->ifc_nand.nand_fir0);
-
- ifc_out32((NAND_CMD_ERASE1 << IFC_NAND_FCR0_CMD0_SHIFT) |
- (NAND_CMD_ERASE2 << IFC_NAND_FCR0_CMD1_SHIFT),
- &ifc->ifc_nand.nand_fcr0);
-
- ifc_out32(0, &ifc->ifc_nand.nand_fbcr);
- ifc_nand_ctrl->read_bytes = 0;
- fsl_ifc_run_command(mtd);
- return;
-
- /* SEQIN sets up the addr buffer and all registers except the length */
- case NAND_CMD_SEQIN: {
- u32 nand_fcr0;
- ifc_nand_ctrl->column = column;
- ifc_nand_ctrl->oob = 0;
-
- if (mtd->writesize > 512) {
- nand_fcr0 =
- (NAND_CMD_SEQIN << IFC_NAND_FCR0_CMD0_SHIFT) |
- (NAND_CMD_STATUS << IFC_NAND_FCR0_CMD1_SHIFT) |
- (NAND_CMD_PAGEPROG << IFC_NAND_FCR0_CMD2_SHIFT);
-
- ifc_out32(
- (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
- (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) |
- (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) |
- (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP3_SHIFT) |
- (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP4_SHIFT),
- &ifc->ifc_nand.nand_fir0);
- ifc_out32(
- (IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT) |
- (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR1_OP6_SHIFT) |
- (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP7_SHIFT),
- &ifc->ifc_nand.nand_fir1);
- } else {
- nand_fcr0 = ((NAND_CMD_PAGEPROG <<
- IFC_NAND_FCR0_CMD1_SHIFT) |
- (NAND_CMD_SEQIN <<
- IFC_NAND_FCR0_CMD2_SHIFT) |
- (NAND_CMD_STATUS <<
- IFC_NAND_FCR0_CMD3_SHIFT));
-
- ifc_out32(
- (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
- (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP1_SHIFT) |
- (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP2_SHIFT) |
- (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP3_SHIFT) |
- (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP4_SHIFT),
- &ifc->ifc_nand.nand_fir0);
- ifc_out32(
- (IFC_FIR_OP_CMD1 << IFC_NAND_FIR1_OP5_SHIFT) |
- (IFC_FIR_OP_CW3 << IFC_NAND_FIR1_OP6_SHIFT) |
- (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR1_OP7_SHIFT) |
- (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP8_SHIFT),
- &ifc->ifc_nand.nand_fir1);
-
- if (column >= mtd->writesize)
- nand_fcr0 |=
- NAND_CMD_READOOB << IFC_NAND_FCR0_CMD0_SHIFT;
- else
- nand_fcr0 |=
- NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT;
- }
-
- if (column >= mtd->writesize) {
- /* OOB area --> READOOB */
- column -= mtd->writesize;
- ifc_nand_ctrl->oob = 1;
- }
- ifc_out32(nand_fcr0, &ifc->ifc_nand.nand_fcr0);
- set_addr(mtd, column, page_addr, ifc_nand_ctrl->oob);
- return;
- }
-
- /* PAGEPROG reuses all of the setup from SEQIN and adds the length */
- case NAND_CMD_PAGEPROG: {
- if (ifc_nand_ctrl->oob) {
- ifc_out32(ifc_nand_ctrl->index -
- ifc_nand_ctrl->column,
- &ifc->ifc_nand.nand_fbcr);
- } else {
- ifc_out32(0, &ifc->ifc_nand.nand_fbcr);
- }
-
- fsl_ifc_run_command(mtd);
- return;
- }
-
- case NAND_CMD_STATUS: {
- void __iomem *addr;
-
- ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
- (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP1_SHIFT),
- &ifc->ifc_nand.nand_fir0);
- ifc_out32(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT,
- &ifc->ifc_nand.nand_fcr0);
- ifc_out32(1, &ifc->ifc_nand.nand_fbcr);
- set_addr(mtd, 0, 0, 0);
- ifc_nand_ctrl->read_bytes = 1;
-
- fsl_ifc_run_command(mtd);
-
- /*
- * The chip always seems to report that it is
- * write-protected, even when it is not.
- */
- addr = ifc_nand_ctrl->addr;
- if (chip->options & NAND_BUSWIDTH_16)
- ifc_out16(ifc_in16(addr) | (NAND_STATUS_WP), addr);
- else
- ifc_out8(ifc_in8(addr) | (NAND_STATUS_WP), addr);
- return;
- }
-
- case NAND_CMD_RESET:
- ifc_out32(IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT,
- &ifc->ifc_nand.nand_fir0);
- ifc_out32(NAND_CMD_RESET << IFC_NAND_FCR0_CMD0_SHIFT,
- &ifc->ifc_nand.nand_fcr0);
- fsl_ifc_run_command(mtd);
- return;
-
- default:
- dev_err(priv->dev, "%s: error, unsupported command 0x%x.\n",
- __func__, command);
- }
-}
-
-static void fsl_ifc_select_chip(struct mtd_info *mtd, int chip)
-{
- /* The hardware does not seem to support multiple
- * chips per bank.
- */
-}
-
-/*
- * Write buf to the IFC NAND Controller Data Buffer
- */
-static void fsl_ifc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
- unsigned int bufsize = mtd->writesize + mtd->oobsize;
-
- if (len <= 0) {
- dev_err(priv->dev, "%s: len %d bytes", __func__, len);
- return;
- }
-
- if ((unsigned int)len > bufsize - ifc_nand_ctrl->index) {
- dev_err(priv->dev,
- "%s: beyond end of buffer (%d requested, %u available)\n",
- __func__, len, bufsize - ifc_nand_ctrl->index);
- len = bufsize - ifc_nand_ctrl->index;
- }
-
- memcpy_toio(ifc_nand_ctrl->addr + ifc_nand_ctrl->index, buf, len);
- ifc_nand_ctrl->index += len;
-}
-
-/*
- * Read a byte from either the IFC hardware buffer
- * read function for 8-bit buswidth
- */
-static uint8_t fsl_ifc_read_byte(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
- unsigned int offset;
-
- /*
- * If there are still bytes in the IFC buffer, then use the
- * next byte.
- */
- if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) {
- offset = ifc_nand_ctrl->index++;
- return ifc_in8(ifc_nand_ctrl->addr + offset);
- }
-
- dev_err(priv->dev, "%s: beyond end of buffer\n", __func__);
- return ERR_BYTE;
-}
-
-/*
- * Read two bytes from the IFC hardware buffer
- * read function for 16-bit buswith
- */
-static uint8_t fsl_ifc_read_byte16(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
- uint16_t data;
-
- /*
- * If there are still bytes in the IFC buffer, then use the
- * next byte.
- */
- if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) {
- data = ifc_in16(ifc_nand_ctrl->addr + ifc_nand_ctrl->index);
- ifc_nand_ctrl->index += 2;
- return (uint8_t) data;
- }
-
- dev_err(priv->dev, "%s: beyond end of buffer\n", __func__);
- return ERR_BYTE;
-}
-
-/*
- * Read from the IFC Controller Data Buffer
- */
-static void fsl_ifc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
- int avail;
-
- if (len < 0) {
- dev_err(priv->dev, "%s: len %d bytes", __func__, len);
- return;
- }
-
- avail = min((unsigned int)len,
- ifc_nand_ctrl->read_bytes - ifc_nand_ctrl->index);
- memcpy_fromio(buf, ifc_nand_ctrl->addr + ifc_nand_ctrl->index, avail);
- ifc_nand_ctrl->index += avail;
-
- if (len > avail)
- dev_err(priv->dev,
- "%s: beyond end of buffer (%d requested, %d available)\n",
- __func__, len, avail);
-}
-
-/*
- * This function is called after Program and Erase Operations to
- * check for success or failure.
- */
-static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip)
-{
- struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
- struct fsl_ifc_ctrl *ctrl = priv->ctrl;
- struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
- u32 nand_fsr;
- int status;
-
- /* Use READ_STATUS command, but wait for the device to be ready */
- ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
- (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR0_OP1_SHIFT),
- &ifc->ifc_nand.nand_fir0);
- ifc_out32(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT,
- &ifc->ifc_nand.nand_fcr0);
- ifc_out32(1, &ifc->ifc_nand.nand_fbcr);
- set_addr(mtd, 0, 0, 0);
- ifc_nand_ctrl->read_bytes = 1;
-
- fsl_ifc_run_command(mtd);
-
- nand_fsr = ifc_in32(&ifc->ifc_nand.nand_fsr);
- status = nand_fsr >> 24;
- /*
- * The chip always seems to report that it is
- * write-protected, even when it is not.
- */
- return status | NAND_STATUS_WP;
-}
-
-/*
- * The controller does not check for bitflips in erased pages,
- * therefore software must check instead.
- */
-static int check_erased_page(struct nand_chip *chip, u8 *buf)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- u8 *ecc = chip->oob_poi;
- const int ecc_size = chip->ecc.bytes;
- const int pkt_size = chip->ecc.size;
- int i, res, bitflips = 0;
- struct mtd_oob_region oobregion = { };
-
- mtd_ooblayout_ecc(mtd, 0, &oobregion);
- ecc += oobregion.offset;
-
- for (i = 0; i < chip->ecc.steps; ++i) {
- res = nand_check_erased_ecc_chunk(buf, pkt_size, ecc, ecc_size,
- NULL, 0,
- chip->ecc.strength);
- if (res < 0)
- mtd->ecc_stats.failed++;
- else
- mtd->ecc_stats.corrected += res;
-
- bitflips = max(res, bitflips);
- buf += pkt_size;
- ecc += ecc_size;
- }
-
- return bitflips;
-}
-
-static int fsl_ifc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page)
-{
- struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
- struct fsl_ifc_ctrl *ctrl = priv->ctrl;
- struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl;
-
- nand_read_page_op(chip, page, 0, buf, mtd->writesize);
- if (oob_required)
- fsl_ifc_read_buf(mtd, chip->oob_poi, mtd->oobsize);
-
- if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_ECCER) {
- if (!oob_required)
- fsl_ifc_read_buf(mtd, chip->oob_poi, mtd->oobsize);
-
- return check_erased_page(chip, buf);
- }
-
- if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC)
- mtd->ecc_stats.failed++;
-
- return nctrl->max_bitflips;
-}
-
-/* ECC will be calculated automatically, and errors will be detected in
- * waitfunc.
- */
-static int fsl_ifc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf, int oob_required, int page)
-{
- nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);
- fsl_ifc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
-
- return nand_prog_page_end_op(chip);
-}
-
-static int fsl_ifc_chip_init_tail(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
-
- dev_dbg(priv->dev, "%s: nand->numchips = %d\n", __func__,
- chip->numchips);
- dev_dbg(priv->dev, "%s: nand->chipsize = %lld\n", __func__,
- chip->chipsize);
- dev_dbg(priv->dev, "%s: nand->pagemask = %8x\n", __func__,
- chip->pagemask);
- dev_dbg(priv->dev, "%s: nand->chip_delay = %d\n", __func__,
- chip->chip_delay);
- dev_dbg(priv->dev, "%s: nand->badblockpos = %d\n", __func__,
- chip->badblockpos);
- dev_dbg(priv->dev, "%s: nand->chip_shift = %d\n", __func__,
- chip->chip_shift);
- dev_dbg(priv->dev, "%s: nand->page_shift = %d\n", __func__,
- chip->page_shift);
- dev_dbg(priv->dev, "%s: nand->phys_erase_shift = %d\n", __func__,
- chip->phys_erase_shift);
- dev_dbg(priv->dev, "%s: nand->ecc.mode = %d\n", __func__,
- chip->ecc.mode);
- dev_dbg(priv->dev, "%s: nand->ecc.steps = %d\n", __func__,
- chip->ecc.steps);
- dev_dbg(priv->dev, "%s: nand->ecc.bytes = %d\n", __func__,
- chip->ecc.bytes);
- dev_dbg(priv->dev, "%s: nand->ecc.total = %d\n", __func__,
- chip->ecc.total);
- dev_dbg(priv->dev, "%s: mtd->ooblayout = %p\n", __func__,
- mtd->ooblayout);
- dev_dbg(priv->dev, "%s: mtd->flags = %08x\n", __func__, mtd->flags);
- dev_dbg(priv->dev, "%s: mtd->size = %lld\n", __func__, mtd->size);
- dev_dbg(priv->dev, "%s: mtd->erasesize = %d\n", __func__,
- mtd->erasesize);
- dev_dbg(priv->dev, "%s: mtd->writesize = %d\n", __func__,
- mtd->writesize);
- dev_dbg(priv->dev, "%s: mtd->oobsize = %d\n", __func__,
- mtd->oobsize);
-
- return 0;
-}
-
-static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv)
-{
- struct fsl_ifc_ctrl *ctrl = priv->ctrl;
- struct fsl_ifc_runtime __iomem *ifc_runtime = ctrl->rregs;
- struct fsl_ifc_global __iomem *ifc_global = ctrl->gregs;
- uint32_t csor = 0, csor_8k = 0, csor_ext = 0;
- uint32_t cs = priv->bank;
-
- /* Save CSOR and CSOR_ext */
- csor = ifc_in32(&ifc_global->csor_cs[cs].csor);
- csor_ext = ifc_in32(&ifc_global->csor_cs[cs].csor_ext);
-
- /* chage PageSize 8K and SpareSize 1K*/
- csor_8k = (csor & ~(CSOR_NAND_PGS_MASK)) | 0x0018C000;
- ifc_out32(csor_8k, &ifc_global->csor_cs[cs].csor);
- ifc_out32(0x0000400, &ifc_global->csor_cs[cs].csor_ext);
-
- /* READID */
- ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
- (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) |
- (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT),
- &ifc_runtime->ifc_nand.nand_fir0);
- ifc_out32(NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT,
- &ifc_runtime->ifc_nand.nand_fcr0);
- ifc_out32(0x0, &ifc_runtime->ifc_nand.row3);
-
- ifc_out32(0x0, &ifc_runtime->ifc_nand.nand_fbcr);
-
- /* Program ROW0/COL0 */
- ifc_out32(0x0, &ifc_runtime->ifc_nand.row0);
- ifc_out32(0x0, &ifc_runtime->ifc_nand.col0);
-
- /* set the chip select for NAND Transaction */
- ifc_out32(cs << IFC_NAND_CSEL_SHIFT,
- &ifc_runtime->ifc_nand.nand_csel);
-
- /* start read seq */
- ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT,
- &ifc_runtime->ifc_nand.nandseq_strt);
-
- /* wait for command complete flag or timeout */
- wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat,
- msecs_to_jiffies(IFC_TIMEOUT_MSECS));
-
- if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC)
- printk(KERN_ERR "fsl-ifc: Failed to Initialise SRAM\n");
-
- /* Restore CSOR and CSOR_ext */
- ifc_out32(csor, &ifc_global->csor_cs[cs].csor);
- ifc_out32(csor_ext, &ifc_global->csor_cs[cs].csor_ext);
-}
-
-static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
-{
- struct fsl_ifc_ctrl *ctrl = priv->ctrl;
- struct fsl_ifc_global __iomem *ifc_global = ctrl->gregs;
- struct fsl_ifc_runtime __iomem *ifc_runtime = ctrl->rregs;
- struct nand_chip *chip = &priv->chip;
- struct mtd_info *mtd = nand_to_mtd(&priv->chip);
- u32 csor;
-
- /* Fill in fsl_ifc_mtd structure */
- mtd->dev.parent = priv->dev;
- nand_set_flash_node(chip, priv->dev->of_node);
-
- /* fill in nand_chip structure */
- /* set up function call table */
- if ((ifc_in32(&ifc_global->cspr_cs[priv->bank].cspr))
- & CSPR_PORT_SIZE_16)
- chip->read_byte = fsl_ifc_read_byte16;
- else
- chip->read_byte = fsl_ifc_read_byte;
-
- chip->write_buf = fsl_ifc_write_buf;
- chip->read_buf = fsl_ifc_read_buf;
- chip->select_chip = fsl_ifc_select_chip;
- chip->cmdfunc = fsl_ifc_cmdfunc;
- chip->waitfunc = fsl_ifc_wait;
- chip->onfi_set_features = nand_onfi_get_set_features_notsupp;
- chip->onfi_get_features = nand_onfi_get_set_features_notsupp;
-
- chip->bbt_td = &bbt_main_descr;
- chip->bbt_md = &bbt_mirror_descr;
-
- ifc_out32(0x0, &ifc_runtime->ifc_nand.ncfgr);
-
- /* set up nand options */
- chip->bbt_options = NAND_BBT_USE_FLASH;
- chip->options = NAND_NO_SUBPAGE_WRITE;
-
- if (ifc_in32(&ifc_global->cspr_cs[priv->bank].cspr)
- & CSPR_PORT_SIZE_16) {
- chip->read_byte = fsl_ifc_read_byte16;
- chip->options |= NAND_BUSWIDTH_16;
- } else {
- chip->read_byte = fsl_ifc_read_byte;
- }
-
- chip->controller = &ifc_nand_ctrl->controller;
- nand_set_controller_data(chip, priv);
-
- chip->ecc.read_page = fsl_ifc_read_page;
- chip->ecc.write_page = fsl_ifc_write_page;
-
- csor = ifc_in32(&ifc_global->csor_cs[priv->bank].csor);
-
- switch (csor & CSOR_NAND_PGS_MASK) {
- case CSOR_NAND_PGS_512:
- if (!(chip->options & NAND_BUSWIDTH_16)) {
- /* Avoid conflict with bad block marker */
- bbt_main_descr.offs = 0;
- bbt_mirror_descr.offs = 0;
- }
-
- priv->bufnum_mask = 15;
- break;
-
- case CSOR_NAND_PGS_2K:
- priv->bufnum_mask = 3;
- break;
-
- case CSOR_NAND_PGS_4K:
- priv->bufnum_mask = 1;
- break;
-
- case CSOR_NAND_PGS_8K:
- priv->bufnum_mask = 0;
- break;
-
- default:
- dev_err(priv->dev, "bad csor %#x: bad page size\n", csor);
- return -ENODEV;
- }
-
- /* Must also set CSOR_NAND_ECC_ENC_EN if DEC_EN set */
- if (csor & CSOR_NAND_ECC_DEC_EN) {
- chip->ecc.mode = NAND_ECC_HW;
- mtd_set_ooblayout(mtd, &fsl_ifc_ooblayout_ops);
-
- /* Hardware generates ECC per 512 Bytes */
- chip->ecc.size = 512;
- if ((csor & CSOR_NAND_ECC_MODE_MASK) == CSOR_NAND_ECC_MODE_4) {
- chip->ecc.bytes = 8;
- chip->ecc.strength = 4;
- } else {
- chip->ecc.bytes = 16;
- chip->ecc.strength = 8;
- }
- } else {
- chip->ecc.mode = NAND_ECC_SOFT;
- chip->ecc.algo = NAND_ECC_HAMMING;
- }
-
- if (ctrl->version >= FSL_IFC_VERSION_1_1_0)
- fsl_ifc_sram_init(priv);
-
- /*
- * As IFC version 2.0.0 has 16KB of internal SRAM as compared to older
- * versions which had 8KB. Hence bufnum mask needs to be updated.
- */
- if (ctrl->version >= FSL_IFC_VERSION_2_0_0)
- priv->bufnum_mask = (priv->bufnum_mask * 2) + 1;
-
- return 0;
-}
-
-static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv)
-{
- struct mtd_info *mtd = nand_to_mtd(&priv->chip);
-
- nand_release(mtd);
-
- kfree(mtd->name);
-
- if (priv->vbase)
- iounmap(priv->vbase);
-
- ifc_nand_ctrl->chips[priv->bank] = NULL;
-
- return 0;
-}
-
-static int match_bank(struct fsl_ifc_global __iomem *ifc_global, int bank,
- phys_addr_t addr)
-{
- u32 cspr = ifc_in32(&ifc_global->cspr_cs[bank].cspr);
-
- if (!(cspr & CSPR_V))
- return 0;
- if ((cspr & CSPR_MSEL) != CSPR_MSEL_NAND)
- return 0;
-
- return (cspr & CSPR_BA) == convert_ifc_address(addr);
-}
-
-static DEFINE_MUTEX(fsl_ifc_nand_mutex);
-
-static int fsl_ifc_nand_probe(struct platform_device *dev)
-{
- struct fsl_ifc_runtime __iomem *ifc;
- struct fsl_ifc_mtd *priv;
- struct resource res;
- static const char *part_probe_types[]
- = { "cmdlinepart", "RedBoot", "ofpart", NULL };
- int ret;
- int bank;
- struct device_node *node = dev->dev.of_node;
- struct mtd_info *mtd;
-
- if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->rregs)
- return -ENODEV;
- ifc = fsl_ifc_ctrl_dev->rregs;
-
- /* get, allocate and map the memory resource */
- ret = of_address_to_resource(node, 0, &res);
- if (ret) {
- dev_err(&dev->dev, "%s: failed to get resource\n", __func__);
- return ret;
- }
-
- /* find which chip select it is connected to */
- for (bank = 0; bank < fsl_ifc_ctrl_dev->banks; bank++) {
- if (match_bank(fsl_ifc_ctrl_dev->gregs, bank, res.start))
- break;
- }
-
- if (bank >= fsl_ifc_ctrl_dev->banks) {
- dev_err(&dev->dev, "%s: address did not match any chip selects\n",
- __func__);
- return -ENODEV;
- }
-
- priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- mutex_lock(&fsl_ifc_nand_mutex);
- if (!fsl_ifc_ctrl_dev->nand) {
- ifc_nand_ctrl = kzalloc(sizeof(*ifc_nand_ctrl), GFP_KERNEL);
- if (!ifc_nand_ctrl) {
- mutex_unlock(&fsl_ifc_nand_mutex);
- return -ENOMEM;
- }
-
- ifc_nand_ctrl->read_bytes = 0;
- ifc_nand_ctrl->index = 0;
- ifc_nand_ctrl->addr = NULL;
- fsl_ifc_ctrl_dev->nand = ifc_nand_ctrl;
-
- nand_hw_control_init(&ifc_nand_ctrl->controller);
- } else {
- ifc_nand_ctrl = fsl_ifc_ctrl_dev->nand;
- }
- mutex_unlock(&fsl_ifc_nand_mutex);
-
- ifc_nand_ctrl->chips[bank] = priv;
- priv->bank = bank;
- priv->ctrl = fsl_ifc_ctrl_dev;
- priv->dev = &dev->dev;
-
- priv->vbase = ioremap(res.start, resource_size(&res));
- if (!priv->vbase) {
- dev_err(priv->dev, "%s: failed to map chip region\n", __func__);
- ret = -ENOMEM;
- goto err;
- }
-
- dev_set_drvdata(priv->dev, priv);
-
- ifc_out32(IFC_NAND_EVTER_EN_OPC_EN |
- IFC_NAND_EVTER_EN_FTOER_EN |
- IFC_NAND_EVTER_EN_WPER_EN,
- &ifc->ifc_nand.nand_evter_en);
-
- /* enable NAND Machine Interrupts */
- ifc_out32(IFC_NAND_EVTER_INTR_OPCIR_EN |
- IFC_NAND_EVTER_INTR_FTOERIR_EN |
- IFC_NAND_EVTER_INTR_WPERIR_EN,
- &ifc->ifc_nand.nand_evter_intr_en);
-
- mtd = nand_to_mtd(&priv->chip);
- mtd->name = kasprintf(GFP_KERNEL, "%llx.flash", (u64)res.start);
- if (!mtd->name) {
- ret = -ENOMEM;
- goto err;
- }
-
- ret = fsl_ifc_chip_init(priv);
- if (ret)
- goto err;
-
- ret = nand_scan_ident(mtd, 1, NULL);
- if (ret)
- goto err;
-
- ret = fsl_ifc_chip_init_tail(mtd);
- if (ret)
- goto err;
-
- ret = nand_scan_tail(mtd);
- if (ret)
- goto err;
-
- /* First look for RedBoot table or partitions on the command
- * line, these take precedence over device tree information */
- mtd_device_parse_register(mtd, part_probe_types, NULL, NULL, 0);
-
- dev_info(priv->dev, "IFC NAND device at 0x%llx, bank %d\n",
- (unsigned long long)res.start, priv->bank);
- return 0;
-
-err:
- fsl_ifc_chip_remove(priv);
- return ret;
-}
-
-static int fsl_ifc_nand_remove(struct platform_device *dev)
-{
- struct fsl_ifc_mtd *priv = dev_get_drvdata(&dev->dev);
-
- fsl_ifc_chip_remove(priv);
-
- mutex_lock(&fsl_ifc_nand_mutex);
- ifc_nand_ctrl->counter--;
- if (!ifc_nand_ctrl->counter) {
- fsl_ifc_ctrl_dev->nand = NULL;
- kfree(ifc_nand_ctrl);
- }
- mutex_unlock(&fsl_ifc_nand_mutex);
-
- return 0;
-}
-
-static const struct of_device_id fsl_ifc_nand_match[] = {
- {
- .compatible = "fsl,ifc-nand",
- },
- {}
-};
-MODULE_DEVICE_TABLE(of, fsl_ifc_nand_match);
-
-static struct platform_driver fsl_ifc_nand_driver = {
- .driver = {
- .name = "fsl,ifc-nand",
- .of_match_table = fsl_ifc_nand_match,
- },
- .probe = fsl_ifc_nand_probe,
- .remove = fsl_ifc_nand_remove,
-};
-
-module_platform_driver(fsl_ifc_nand_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Freescale");
-MODULE_DESCRIPTION("Freescale Integrated Flash Controller MTD NAND driver");
diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c
deleted file mode 100644
index f49ed46fa770..000000000000
--- a/drivers/mtd/nand/fsmc_nand.c
+++ /dev/null
@@ -1,1175 +0,0 @@
-/*
- * drivers/mtd/nand/fsmc_nand.c
- *
- * ST Microelectronics
- * Flexible Static Memory Controller (FSMC)
- * Driver for NAND portions
- *
- * Copyright © 2010 ST Microelectronics
- * Vipin Kumar <vipin.kumar@st.com>
- * Ashish Priyadarshi
- *
- * Based on drivers/mtd/nand/nomadik_nand.c
- *
- * 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/completion.h>
-#include <linux/dmaengine.h>
-#include <linux/dma-direction.h>
-#include <linux/dma-mapping.h>
-#include <linux/err.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/resource.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand_ecc.h>
-#include <linux/platform_device.h>
-#include <linux/of.h>
-#include <linux/mtd/partitions.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-#include <linux/amba/bus.h>
-#include <mtd/mtd-abi.h>
-
-/* fsmc controller registers for NOR flash */
-#define CTRL 0x0
- /* ctrl register definitions */
- #define BANK_ENABLE (1 << 0)
- #define MUXED (1 << 1)
- #define NOR_DEV (2 << 2)
- #define WIDTH_8 (0 << 4)
- #define WIDTH_16 (1 << 4)
- #define RSTPWRDWN (1 << 6)
- #define WPROT (1 << 7)
- #define WRT_ENABLE (1 << 12)
- #define WAIT_ENB (1 << 13)
-
-#define CTRL_TIM 0x4
- /* ctrl_tim register definitions */
-
-#define FSMC_NOR_BANK_SZ 0x8
-#define FSMC_NOR_REG_SIZE 0x40
-
-#define FSMC_NOR_REG(base, bank, reg) (base + \
- FSMC_NOR_BANK_SZ * (bank) + \
- reg)
-
-/* fsmc controller registers for NAND flash */
-#define PC 0x00
- /* pc register definitions */
- #define FSMC_RESET (1 << 0)
- #define FSMC_WAITON (1 << 1)
- #define FSMC_ENABLE (1 << 2)
- #define FSMC_DEVTYPE_NAND (1 << 3)
- #define FSMC_DEVWID_8 (0 << 4)
- #define FSMC_DEVWID_16 (1 << 4)
- #define FSMC_ECCEN (1 << 6)
- #define FSMC_ECCPLEN_512 (0 << 7)
- #define FSMC_ECCPLEN_256 (1 << 7)
- #define FSMC_TCLR_1 (1)
- #define FSMC_TCLR_SHIFT (9)
- #define FSMC_TCLR_MASK (0xF)
- #define FSMC_TAR_1 (1)
- #define FSMC_TAR_SHIFT (13)
- #define FSMC_TAR_MASK (0xF)
-#define STS 0x04
- /* sts register definitions */
- #define FSMC_CODE_RDY (1 << 15)
-#define COMM 0x08
- /* comm register definitions */
- #define FSMC_TSET_0 0
- #define FSMC_TSET_SHIFT 0
- #define FSMC_TSET_MASK 0xFF
- #define FSMC_TWAIT_6 6
- #define FSMC_TWAIT_SHIFT 8
- #define FSMC_TWAIT_MASK 0xFF
- #define FSMC_THOLD_4 4
- #define FSMC_THOLD_SHIFT 16
- #define FSMC_THOLD_MASK 0xFF
- #define FSMC_THIZ_1 1
- #define FSMC_THIZ_SHIFT 24
- #define FSMC_THIZ_MASK 0xFF
-#define ATTRIB 0x0C
-#define IOATA 0x10
-#define ECC1 0x14
-#define ECC2 0x18
-#define ECC3 0x1C
-#define FSMC_NAND_BANK_SZ 0x20
-
-#define FSMC_NAND_REG(base, bank, reg) (base + FSMC_NOR_REG_SIZE + \
- (FSMC_NAND_BANK_SZ * (bank)) + \
- reg)
-
-#define FSMC_BUSY_WAIT_TIMEOUT (1 * HZ)
-
-struct fsmc_nand_timings {
- uint8_t tclr;
- uint8_t tar;
- uint8_t thiz;
- uint8_t thold;
- uint8_t twait;
- uint8_t tset;
-};
-
-enum access_mode {
- USE_DMA_ACCESS = 1,
- USE_WORD_ACCESS,
-};
-
-/**
- * struct fsmc_nand_data - structure for FSMC NAND device state
- *
- * @pid: Part ID on the AMBA PrimeCell format
- * @mtd: MTD info for a NAND flash.
- * @nand: Chip related info for a NAND flash.
- * @partitions: Partition info for a NAND Flash.
- * @nr_partitions: Total number of partition of a NAND flash.
- *
- * @bank: Bank number for probed device.
- * @clk: Clock structure for FSMC.
- *
- * @read_dma_chan: DMA channel for read access
- * @write_dma_chan: DMA channel for write access to NAND
- * @dma_access_complete: Completion structure
- *
- * @data_pa: NAND Physical port for Data.
- * @data_va: NAND port for Data.
- * @cmd_va: NAND port for Command.
- * @addr_va: NAND port for Address.
- * @regs_va: FSMC regs base address.
- */
-struct fsmc_nand_data {
- u32 pid;
- struct nand_chip nand;
-
- unsigned int bank;
- struct device *dev;
- enum access_mode mode;
- struct clk *clk;
-
- /* DMA related objects */
- struct dma_chan *read_dma_chan;
- struct dma_chan *write_dma_chan;
- struct completion dma_access_complete;
-
- struct fsmc_nand_timings *dev_timings;
-
- dma_addr_t data_pa;
- void __iomem *data_va;
- void __iomem *cmd_va;
- void __iomem *addr_va;
- void __iomem *regs_va;
-};
-
-static int fsmc_ecc1_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- if (section >= chip->ecc.steps)
- return -ERANGE;
-
- oobregion->offset = (section * 16) + 2;
- oobregion->length = 3;
-
- return 0;
-}
-
-static int fsmc_ecc1_ooblayout_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- if (section >= chip->ecc.steps)
- return -ERANGE;
-
- oobregion->offset = (section * 16) + 8;
-
- if (section < chip->ecc.steps - 1)
- oobregion->length = 8;
- else
- oobregion->length = mtd->oobsize - oobregion->offset;
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops fsmc_ecc1_ooblayout_ops = {
- .ecc = fsmc_ecc1_ooblayout_ecc,
- .free = fsmc_ecc1_ooblayout_free,
-};
-
-/*
- * ECC placement definitions in oobfree type format.
- * There are 13 bytes of ecc for every 512 byte block and it has to be read
- * consecutively and immediately after the 512 byte data block for hardware to
- * generate the error bit offsets in 512 byte data.
- */
-static int fsmc_ecc4_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- if (section >= chip->ecc.steps)
- return -ERANGE;
-
- oobregion->length = chip->ecc.bytes;
-
- if (!section && mtd->writesize <= 512)
- oobregion->offset = 0;
- else
- oobregion->offset = (section * 16) + 2;
-
- return 0;
-}
-
-static int fsmc_ecc4_ooblayout_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- if (section >= chip->ecc.steps)
- return -ERANGE;
-
- oobregion->offset = (section * 16) + 15;
-
- if (section < chip->ecc.steps - 1)
- oobregion->length = 3;
- else
- oobregion->length = mtd->oobsize - oobregion->offset;
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops fsmc_ecc4_ooblayout_ops = {
- .ecc = fsmc_ecc4_ooblayout_ecc,
- .free = fsmc_ecc4_ooblayout_free,
-};
-
-static inline struct fsmc_nand_data *mtd_to_fsmc(struct mtd_info *mtd)
-{
- return container_of(mtd_to_nand(mtd), struct fsmc_nand_data, nand);
-}
-
-/*
- * fsmc_cmd_ctrl - For facilitaing Hardware access
- * This routine allows hardware specific access to control-lines(ALE,CLE)
- */
-static void fsmc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
- void __iomem *regs = host->regs_va;
- unsigned int bank = host->bank;
-
- if (ctrl & NAND_CTRL_CHANGE) {
- u32 pc;
-
- if (ctrl & NAND_CLE) {
- this->IO_ADDR_R = host->cmd_va;
- this->IO_ADDR_W = host->cmd_va;
- } else if (ctrl & NAND_ALE) {
- this->IO_ADDR_R = host->addr_va;
- this->IO_ADDR_W = host->addr_va;
- } else {
- this->IO_ADDR_R = host->data_va;
- this->IO_ADDR_W = host->data_va;
- }
-
- pc = readl(FSMC_NAND_REG(regs, bank, PC));
- if (ctrl & NAND_NCE)
- pc |= FSMC_ENABLE;
- else
- pc &= ~FSMC_ENABLE;
- writel_relaxed(pc, FSMC_NAND_REG(regs, bank, PC));
- }
-
- mb();
-
- if (cmd != NAND_CMD_NONE)
- writeb_relaxed(cmd, this->IO_ADDR_W);
-}
-
-/*
- * fsmc_nand_setup - FSMC (Flexible Static Memory Controller) init routine
- *
- * This routine initializes timing parameters related to NAND memory access in
- * FSMC registers
- */
-static void fsmc_nand_setup(struct fsmc_nand_data *host,
- struct fsmc_nand_timings *tims)
-{
- uint32_t value = FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON;
- uint32_t tclr, tar, thiz, thold, twait, tset;
- unsigned int bank = host->bank;
- void __iomem *regs = host->regs_va;
-
- tclr = (tims->tclr & FSMC_TCLR_MASK) << FSMC_TCLR_SHIFT;
- tar = (tims->tar & FSMC_TAR_MASK) << FSMC_TAR_SHIFT;
- thiz = (tims->thiz & FSMC_THIZ_MASK) << FSMC_THIZ_SHIFT;
- thold = (tims->thold & FSMC_THOLD_MASK) << FSMC_THOLD_SHIFT;
- twait = (tims->twait & FSMC_TWAIT_MASK) << FSMC_TWAIT_SHIFT;
- tset = (tims->tset & FSMC_TSET_MASK) << FSMC_TSET_SHIFT;
-
- if (host->nand.options & NAND_BUSWIDTH_16)
- writel_relaxed(value | FSMC_DEVWID_16,
- FSMC_NAND_REG(regs, bank, PC));
- else
- writel_relaxed(value | FSMC_DEVWID_8,
- FSMC_NAND_REG(regs, bank, PC));
-
- writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) | tclr | tar,
- FSMC_NAND_REG(regs, bank, PC));
- writel_relaxed(thiz | thold | twait | tset,
- FSMC_NAND_REG(regs, bank, COMM));
- writel_relaxed(thiz | thold | twait | tset,
- FSMC_NAND_REG(regs, bank, ATTRIB));
-}
-
-static int fsmc_calc_timings(struct fsmc_nand_data *host,
- const struct nand_sdr_timings *sdrt,
- struct fsmc_nand_timings *tims)
-{
- unsigned long hclk = clk_get_rate(host->clk);
- unsigned long hclkn = NSEC_PER_SEC / hclk;
- uint32_t thiz, thold, twait, tset;
-
- if (sdrt->tRC_min < 30000)
- return -EOPNOTSUPP;
-
- tims->tar = DIV_ROUND_UP(sdrt->tAR_min / 1000, hclkn) - 1;
- if (tims->tar > FSMC_TAR_MASK)
- tims->tar = FSMC_TAR_MASK;
- tims->tclr = DIV_ROUND_UP(sdrt->tCLR_min / 1000, hclkn) - 1;
- if (tims->tclr > FSMC_TCLR_MASK)
- tims->tclr = FSMC_TCLR_MASK;
-
- thiz = sdrt->tCS_min - sdrt->tWP_min;
- tims->thiz = DIV_ROUND_UP(thiz / 1000, hclkn);
-
- thold = sdrt->tDH_min;
- if (thold < sdrt->tCH_min)
- thold = sdrt->tCH_min;
- if (thold < sdrt->tCLH_min)
- thold = sdrt->tCLH_min;
- if (thold < sdrt->tWH_min)
- thold = sdrt->tWH_min;
- if (thold < sdrt->tALH_min)
- thold = sdrt->tALH_min;
- if (thold < sdrt->tREH_min)
- thold = sdrt->tREH_min;
- tims->thold = DIV_ROUND_UP(thold / 1000, hclkn);
- if (tims->thold == 0)
- tims->thold = 1;
- else if (tims->thold > FSMC_THOLD_MASK)
- tims->thold = FSMC_THOLD_MASK;
-
- twait = max(sdrt->tRP_min, sdrt->tWP_min);
- tims->twait = DIV_ROUND_UP(twait / 1000, hclkn) - 1;
- if (tims->twait == 0)
- tims->twait = 1;
- else if (tims->twait > FSMC_TWAIT_MASK)
- tims->twait = FSMC_TWAIT_MASK;
-
- tset = max(sdrt->tCS_min - sdrt->tWP_min,
- sdrt->tCEA_max - sdrt->tREA_max);
- tims->tset = DIV_ROUND_UP(tset / 1000, hclkn) - 1;
- if (tims->tset == 0)
- tims->tset = 1;
- else if (tims->tset > FSMC_TSET_MASK)
- tims->tset = FSMC_TSET_MASK;
-
- return 0;
-}
-
-static int fsmc_setup_data_interface(struct mtd_info *mtd, int csline,
- const struct nand_data_interface *conf)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct fsmc_nand_data *host = nand_get_controller_data(nand);
- struct fsmc_nand_timings tims;
- const struct nand_sdr_timings *sdrt;
- int ret;
-
- sdrt = nand_get_sdr_timings(conf);
- if (IS_ERR(sdrt))
- return PTR_ERR(sdrt);
-
- ret = fsmc_calc_timings(host, sdrt, &tims);
- if (ret)
- return ret;
-
- if (csline == NAND_DATA_IFACE_CHECK_ONLY)
- return 0;
-
- fsmc_nand_setup(host, &tims);
-
- return 0;
-}
-
-/*
- * fsmc_enable_hwecc - Enables Hardware ECC through FSMC registers
- */
-static void fsmc_enable_hwecc(struct mtd_info *mtd, int mode)
-{
- struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
- void __iomem *regs = host->regs_va;
- uint32_t bank = host->bank;
-
- writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) & ~FSMC_ECCPLEN_256,
- FSMC_NAND_REG(regs, bank, PC));
- writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) & ~FSMC_ECCEN,
- FSMC_NAND_REG(regs, bank, PC));
- writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) | FSMC_ECCEN,
- FSMC_NAND_REG(regs, bank, PC));
-}
-
-/*
- * fsmc_read_hwecc_ecc4 - Hardware ECC calculator for ecc4 option supported by
- * FSMC. ECC is 13 bytes for 512 bytes of data (supports error correction up to
- * max of 8-bits)
- */
-static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data,
- uint8_t *ecc)
-{
- struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
- void __iomem *regs = host->regs_va;
- uint32_t bank = host->bank;
- uint32_t ecc_tmp;
- unsigned long deadline = jiffies + FSMC_BUSY_WAIT_TIMEOUT;
-
- do {
- if (readl_relaxed(FSMC_NAND_REG(regs, bank, STS)) & FSMC_CODE_RDY)
- break;
- else
- cond_resched();
- } while (!time_after_eq(jiffies, deadline));
-
- if (time_after_eq(jiffies, deadline)) {
- dev_err(host->dev, "calculate ecc timed out\n");
- return -ETIMEDOUT;
- }
-
- ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC1));
- ecc[0] = (uint8_t) (ecc_tmp >> 0);
- ecc[1] = (uint8_t) (ecc_tmp >> 8);
- ecc[2] = (uint8_t) (ecc_tmp >> 16);
- ecc[3] = (uint8_t) (ecc_tmp >> 24);
-
- ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC2));
- ecc[4] = (uint8_t) (ecc_tmp >> 0);
- ecc[5] = (uint8_t) (ecc_tmp >> 8);
- ecc[6] = (uint8_t) (ecc_tmp >> 16);
- ecc[7] = (uint8_t) (ecc_tmp >> 24);
-
- ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC3));
- ecc[8] = (uint8_t) (ecc_tmp >> 0);
- ecc[9] = (uint8_t) (ecc_tmp >> 8);
- ecc[10] = (uint8_t) (ecc_tmp >> 16);
- ecc[11] = (uint8_t) (ecc_tmp >> 24);
-
- ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, STS));
- ecc[12] = (uint8_t) (ecc_tmp >> 16);
-
- return 0;
-}
-
-/*
- * fsmc_read_hwecc_ecc1 - Hardware ECC calculator for ecc1 option supported by
- * FSMC. ECC is 3 bytes for 512 bytes of data (supports error correction up to
- * max of 1-bit)
- */
-static int fsmc_read_hwecc_ecc1(struct mtd_info *mtd, const uint8_t *data,
- uint8_t *ecc)
-{
- struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
- void __iomem *regs = host->regs_va;
- uint32_t bank = host->bank;
- uint32_t ecc_tmp;
-
- ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC1));
- ecc[0] = (uint8_t) (ecc_tmp >> 0);
- ecc[1] = (uint8_t) (ecc_tmp >> 8);
- ecc[2] = (uint8_t) (ecc_tmp >> 16);
-
- return 0;
-}
-
-/* Count the number of 0's in buff upto a max of max_bits */
-static int count_written_bits(uint8_t *buff, int size, int max_bits)
-{
- int k, written_bits = 0;
-
- for (k = 0; k < size; k++) {
- written_bits += hweight8(~buff[k]);
- if (written_bits > max_bits)
- break;
- }
-
- return written_bits;
-}
-
-static void dma_complete(void *param)
-{
- struct fsmc_nand_data *host = param;
-
- complete(&host->dma_access_complete);
-}
-
-static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len,
- enum dma_data_direction direction)
-{
- struct dma_chan *chan;
- struct dma_device *dma_dev;
- struct dma_async_tx_descriptor *tx;
- dma_addr_t dma_dst, dma_src, dma_addr;
- dma_cookie_t cookie;
- unsigned long flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
- int ret;
- unsigned long time_left;
-
- if (direction == DMA_TO_DEVICE)
- chan = host->write_dma_chan;
- else if (direction == DMA_FROM_DEVICE)
- chan = host->read_dma_chan;
- else
- return -EINVAL;
-
- dma_dev = chan->device;
- dma_addr = dma_map_single(dma_dev->dev, buffer, len, direction);
-
- if (direction == DMA_TO_DEVICE) {
- dma_src = dma_addr;
- dma_dst = host->data_pa;
- } else {
- dma_src = host->data_pa;
- dma_dst = dma_addr;
- }
-
- tx = dma_dev->device_prep_dma_memcpy(chan, dma_dst, dma_src,
- len, flags);
- if (!tx) {
- dev_err(host->dev, "device_prep_dma_memcpy error\n");
- ret = -EIO;
- goto unmap_dma;
- }
-
- tx->callback = dma_complete;
- tx->callback_param = host;
- cookie = tx->tx_submit(tx);
-
- ret = dma_submit_error(cookie);
- if (ret) {
- dev_err(host->dev, "dma_submit_error %d\n", cookie);
- goto unmap_dma;
- }
-
- dma_async_issue_pending(chan);
-
- time_left =
- wait_for_completion_timeout(&host->dma_access_complete,
- msecs_to_jiffies(3000));
- if (time_left == 0) {
- dmaengine_terminate_all(chan);
- dev_err(host->dev, "wait_for_completion_timeout\n");
- ret = -ETIMEDOUT;
- goto unmap_dma;
- }
-
- ret = 0;
-
-unmap_dma:
- dma_unmap_single(dma_dev->dev, dma_addr, len, direction);
-
- return ret;
-}
-
-/*
- * fsmc_write_buf - write buffer to chip
- * @mtd: MTD device structure
- * @buf: data buffer
- * @len: number of bytes to write
- */
-static void fsmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
-{
- int i;
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) &&
- IS_ALIGNED(len, sizeof(uint32_t))) {
- uint32_t *p = (uint32_t *)buf;
- len = len >> 2;
- for (i = 0; i < len; i++)
- writel_relaxed(p[i], chip->IO_ADDR_W);
- } else {
- for (i = 0; i < len; i++)
- writeb_relaxed(buf[i], chip->IO_ADDR_W);
- }
-}
-
-/*
- * fsmc_read_buf - read chip data into buffer
- * @mtd: MTD device structure
- * @buf: buffer to store date
- * @len: number of bytes to read
- */
-static void fsmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
-{
- int i;
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) &&
- IS_ALIGNED(len, sizeof(uint32_t))) {
- uint32_t *p = (uint32_t *)buf;
- len = len >> 2;
- for (i = 0; i < len; i++)
- p[i] = readl_relaxed(chip->IO_ADDR_R);
- } else {
- for (i = 0; i < len; i++)
- buf[i] = readb_relaxed(chip->IO_ADDR_R);
- }
-}
-
-/*
- * fsmc_read_buf_dma - read chip data into buffer
- * @mtd: MTD device structure
- * @buf: buffer to store date
- * @len: number of bytes to read
- */
-static void fsmc_read_buf_dma(struct mtd_info *mtd, uint8_t *buf, int len)
-{
- struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
-
- dma_xfer(host, buf, len, DMA_FROM_DEVICE);
-}
-
-/*
- * fsmc_write_buf_dma - write buffer to chip
- * @mtd: MTD device structure
- * @buf: data buffer
- * @len: number of bytes to write
- */
-static void fsmc_write_buf_dma(struct mtd_info *mtd, const uint8_t *buf,
- int len)
-{
- struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
-
- dma_xfer(host, (void *)buf, len, DMA_TO_DEVICE);
-}
-
-/*
- * fsmc_read_page_hwecc
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- * @oob_required: caller expects OOB data read to chip->oob_poi
- * @page: page number to read
- *
- * This routine is needed for fsmc version 8 as reading from NAND chip has to be
- * performed in a strict sequence as follows:
- * data(512 byte) -> ecc(13 byte)
- * After this read, fsmc hardware generates and reports error data bits(up to a
- * max of 8 bits)
- */
-static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page)
-{
- int i, j, s, stat, eccsize = chip->ecc.size;
- int eccbytes = chip->ecc.bytes;
- int eccsteps = chip->ecc.steps;
- uint8_t *p = buf;
- uint8_t *ecc_calc = chip->ecc.calc_buf;
- uint8_t *ecc_code = chip->ecc.code_buf;
- int off, len, group = 0;
- /*
- * ecc_oob is intentionally taken as uint16_t. In 16bit devices, we
- * end up reading 14 bytes (7 words) from oob. The local array is
- * to maintain word alignment
- */
- uint16_t ecc_oob[7];
- uint8_t *oob = (uint8_t *)&ecc_oob[0];
- unsigned int max_bitflips = 0;
-
- for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) {
- nand_read_page_op(chip, page, s * eccsize, NULL, 0);
- chip->ecc.hwctl(mtd, NAND_ECC_READ);
- chip->read_buf(mtd, p, eccsize);
-
- for (j = 0; j < eccbytes;) {
- struct mtd_oob_region oobregion;
- int ret;
-
- ret = mtd_ooblayout_ecc(mtd, group++, &oobregion);
- if (ret)
- return ret;
-
- off = oobregion.offset;
- len = oobregion.length;
-
- /*
- * length is intentionally kept a higher multiple of 2
- * to read at least 13 bytes even in case of 16 bit NAND
- * devices
- */
- if (chip->options & NAND_BUSWIDTH_16)
- len = roundup(len, 2);
-
- nand_read_oob_op(chip, page, off, oob + j, len);
- j += len;
- }
-
- memcpy(&ecc_code[i], oob, chip->ecc.bytes);
- chip->ecc.calculate(mtd, p, &ecc_calc[i]);
-
- stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
- if (stat < 0) {
- mtd->ecc_stats.failed++;
- } else {
- mtd->ecc_stats.corrected += stat;
- max_bitflips = max_t(unsigned int, max_bitflips, stat);
- }
- }
-
- return max_bitflips;
-}
-
-/*
- * fsmc_bch8_correct_data
- * @mtd: mtd info structure
- * @dat: buffer of read data
- * @read_ecc: ecc read from device spare area
- * @calc_ecc: ecc calculated from read data
- *
- * calc_ecc is a 104 bit information containing maximum of 8 error
- * offset informations of 13 bits each in 512 bytes of read data.
- */
-static int fsmc_bch8_correct_data(struct mtd_info *mtd, uint8_t *dat,
- uint8_t *read_ecc, uint8_t *calc_ecc)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
- void __iomem *regs = host->regs_va;
- unsigned int bank = host->bank;
- uint32_t err_idx[8];
- uint32_t num_err, i;
- uint32_t ecc1, ecc2, ecc3, ecc4;
-
- num_err = (readl_relaxed(FSMC_NAND_REG(regs, bank, STS)) >> 10) & 0xF;
-
- /* no bit flipping */
- if (likely(num_err == 0))
- return 0;
-
- /* too many errors */
- if (unlikely(num_err > 8)) {
- /*
- * This is a temporary erase check. A newly erased page read
- * would result in an ecc error because the oob data is also
- * erased to FF and the calculated ecc for an FF data is not
- * FF..FF.
- * This is a workaround to skip performing correction in case
- * data is FF..FF
- *
- * Logic:
- * For every page, each bit written as 0 is counted until these
- * number of bits are greater than 8 (the maximum correction
- * capability of FSMC for each 512 + 13 bytes)
- */
-
- int bits_ecc = count_written_bits(read_ecc, chip->ecc.bytes, 8);
- int bits_data = count_written_bits(dat, chip->ecc.size, 8);
-
- if ((bits_ecc + bits_data) <= 8) {
- if (bits_data)
- memset(dat, 0xff, chip->ecc.size);
- return bits_data;
- }
-
- return -EBADMSG;
- }
-
- /*
- * ------------------- calc_ecc[] bit wise -----------|--13 bits--|
- * |---idx[7]--|--.....-----|---idx[2]--||---idx[1]--||---idx[0]--|
- *
- * calc_ecc is a 104 bit information containing maximum of 8 error
- * offset informations of 13 bits each. calc_ecc is copied into a
- * uint64_t array and error offset indexes are populated in err_idx
- * array
- */
- ecc1 = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC1));
- ecc2 = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC2));
- ecc3 = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC3));
- ecc4 = readl_relaxed(FSMC_NAND_REG(regs, bank, STS));
-
- err_idx[0] = (ecc1 >> 0) & 0x1FFF;
- err_idx[1] = (ecc1 >> 13) & 0x1FFF;
- err_idx[2] = (((ecc2 >> 0) & 0x7F) << 6) | ((ecc1 >> 26) & 0x3F);
- err_idx[3] = (ecc2 >> 7) & 0x1FFF;
- err_idx[4] = (((ecc3 >> 0) & 0x1) << 12) | ((ecc2 >> 20) & 0xFFF);
- err_idx[5] = (ecc3 >> 1) & 0x1FFF;
- err_idx[6] = (ecc3 >> 14) & 0x1FFF;
- err_idx[7] = (((ecc4 >> 16) & 0xFF) << 5) | ((ecc3 >> 27) & 0x1F);
-
- i = 0;
- while (num_err--) {
- change_bit(0, (unsigned long *)&err_idx[i]);
- change_bit(1, (unsigned long *)&err_idx[i]);
-
- if (err_idx[i] < chip->ecc.size * 8) {
- change_bit(err_idx[i], (unsigned long *)dat);
- i++;
- }
- }
- return i;
-}
-
-static bool filter(struct dma_chan *chan, void *slave)
-{
- chan->private = slave;
- return true;
-}
-
-static int fsmc_nand_probe_config_dt(struct platform_device *pdev,
- struct fsmc_nand_data *host,
- struct nand_chip *nand)
-{
- struct device_node *np = pdev->dev.of_node;
- u32 val;
- int ret;
-
- nand->options = 0;
-
- if (!of_property_read_u32(np, "bank-width", &val)) {
- if (val == 2) {
- nand->options |= NAND_BUSWIDTH_16;
- } else if (val != 1) {
- dev_err(&pdev->dev, "invalid bank-width %u\n", val);
- return -EINVAL;
- }
- }
-
- if (of_get_property(np, "nand-skip-bbtscan", NULL))
- nand->options |= NAND_SKIP_BBTSCAN;
-
- host->dev_timings = devm_kzalloc(&pdev->dev,
- sizeof(*host->dev_timings), GFP_KERNEL);
- if (!host->dev_timings)
- return -ENOMEM;
- ret = of_property_read_u8_array(np, "timings", (u8 *)host->dev_timings,
- sizeof(*host->dev_timings));
- if (ret)
- host->dev_timings = NULL;
-
- /* Set default NAND bank to 0 */
- host->bank = 0;
- if (!of_property_read_u32(np, "bank", &val)) {
- if (val > 3) {
- dev_err(&pdev->dev, "invalid bank %u\n", val);
- return -EINVAL;
- }
- host->bank = val;
- }
- return 0;
-}
-
-/*
- * fsmc_nand_probe - Probe function
- * @pdev: platform device structure
- */
-static int __init fsmc_nand_probe(struct platform_device *pdev)
-{
- struct fsmc_nand_data *host;
- struct mtd_info *mtd;
- struct nand_chip *nand;
- struct resource *res;
- dma_cap_mask_t mask;
- int ret = 0;
- u32 pid;
- int i;
-
- /* Allocate memory for the device structure (and zero it) */
- host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
- if (!host)
- return -ENOMEM;
-
- nand = &host->nand;
-
- ret = fsmc_nand_probe_config_dt(pdev, host, nand);
- if (ret)
- return ret;
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data");
- host->data_va = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(host->data_va))
- return PTR_ERR(host->data_va);
-
- host->data_pa = (dma_addr_t)res->start;
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_addr");
- host->addr_va = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(host->addr_va))
- return PTR_ERR(host->addr_va);
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_cmd");
- host->cmd_va = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(host->cmd_va))
- return PTR_ERR(host->cmd_va);
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fsmc_regs");
- host->regs_va = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(host->regs_va))
- return PTR_ERR(host->regs_va);
-
- host->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(host->clk)) {
- dev_err(&pdev->dev, "failed to fetch block clock\n");
- return PTR_ERR(host->clk);
- }
-
- ret = clk_prepare_enable(host->clk);
- if (ret)
- return ret;
-
- /*
- * This device ID is actually a common AMBA ID as used on the
- * AMBA PrimeCell bus. However it is not a PrimeCell.
- */
- for (pid = 0, i = 0; i < 4; i++)
- pid |= (readl(host->regs_va + resource_size(res) - 0x20 + 4 * i) & 255) << (i * 8);
- host->pid = pid;
- dev_info(&pdev->dev, "FSMC device partno %03x, manufacturer %02x, "
- "revision %02x, config %02x\n",
- AMBA_PART_BITS(pid), AMBA_MANF_BITS(pid),
- AMBA_REV_BITS(pid), AMBA_CONFIG_BITS(pid));
-
- host->dev = &pdev->dev;
-
- if (host->mode == USE_DMA_ACCESS)
- init_completion(&host->dma_access_complete);
-
- /* Link all private pointers */
- mtd = nand_to_mtd(&host->nand);
- nand_set_controller_data(nand, host);
- nand_set_flash_node(nand, pdev->dev.of_node);
-
- mtd->dev.parent = &pdev->dev;
- nand->IO_ADDR_R = host->data_va;
- nand->IO_ADDR_W = host->data_va;
- nand->cmd_ctrl = fsmc_cmd_ctrl;
- nand->chip_delay = 30;
-
- /*
- * Setup default ECC mode. nand_dt_init() called from nand_scan_ident()
- * can overwrite this value if the DT provides a different value.
- */
- nand->ecc.mode = NAND_ECC_HW;
- nand->ecc.hwctl = fsmc_enable_hwecc;
- nand->ecc.size = 512;
- nand->badblockbits = 7;
-
- switch (host->mode) {
- case USE_DMA_ACCESS:
- dma_cap_zero(mask);
- dma_cap_set(DMA_MEMCPY, mask);
- host->read_dma_chan = dma_request_channel(mask, filter, NULL);
- if (!host->read_dma_chan) {
- dev_err(&pdev->dev, "Unable to get read dma channel\n");
- goto err_req_read_chnl;
- }
- host->write_dma_chan = dma_request_channel(mask, filter, NULL);
- if (!host->write_dma_chan) {
- dev_err(&pdev->dev, "Unable to get write dma channel\n");
- goto err_req_write_chnl;
- }
- nand->read_buf = fsmc_read_buf_dma;
- nand->write_buf = fsmc_write_buf_dma;
- break;
-
- default:
- case USE_WORD_ACCESS:
- nand->read_buf = fsmc_read_buf;
- nand->write_buf = fsmc_write_buf;
- break;
- }
-
- if (host->dev_timings)
- fsmc_nand_setup(host, host->dev_timings);
- else
- nand->setup_data_interface = fsmc_setup_data_interface;
-
- if (AMBA_REV_BITS(host->pid) >= 8) {
- nand->ecc.read_page = fsmc_read_page_hwecc;
- nand->ecc.calculate = fsmc_read_hwecc_ecc4;
- nand->ecc.correct = fsmc_bch8_correct_data;
- nand->ecc.bytes = 13;
- nand->ecc.strength = 8;
- }
-
- /*
- * Scan to find existence of the device
- */
- ret = nand_scan_ident(mtd, 1, NULL);
- if (ret) {
- dev_err(&pdev->dev, "No NAND Device found!\n");
- goto err_scan_ident;
- }
-
- if (AMBA_REV_BITS(host->pid) >= 8) {
- switch (mtd->oobsize) {
- case 16:
- case 64:
- case 128:
- case 224:
- case 256:
- break;
- default:
- dev_warn(&pdev->dev, "No oob scheme defined for oobsize %d\n",
- mtd->oobsize);
- ret = -EINVAL;
- goto err_probe;
- }
-
- mtd_set_ooblayout(mtd, &fsmc_ecc4_ooblayout_ops);
- } else {
- switch (nand->ecc.mode) {
- case NAND_ECC_HW:
- dev_info(&pdev->dev, "Using 1-bit HW ECC scheme\n");
- nand->ecc.calculate = fsmc_read_hwecc_ecc1;
- nand->ecc.correct = nand_correct_data;
- nand->ecc.bytes = 3;
- nand->ecc.strength = 1;
- break;
-
- case NAND_ECC_SOFT:
- if (nand->ecc.algo == NAND_ECC_BCH) {
- dev_info(&pdev->dev, "Using 4-bit SW BCH ECC scheme\n");
- break;
- }
-
- case NAND_ECC_ON_DIE:
- break;
-
- default:
- dev_err(&pdev->dev, "Unsupported ECC mode!\n");
- goto err_probe;
- }
-
- /*
- * Don't set layout for BCH4 SW ECC. This will be
- * generated later in nand_bch_init() later.
- */
- if (nand->ecc.mode == NAND_ECC_HW) {
- switch (mtd->oobsize) {
- case 16:
- case 64:
- case 128:
- mtd_set_ooblayout(mtd,
- &fsmc_ecc1_ooblayout_ops);
- break;
- default:
- dev_warn(&pdev->dev,
- "No oob scheme defined for oobsize %d\n",
- mtd->oobsize);
- ret = -EINVAL;
- goto err_probe;
- }
- }
- }
-
- /* Second stage of scan to fill MTD data-structures */
- ret = nand_scan_tail(mtd);
- if (ret)
- goto err_probe;
-
- mtd->name = "nand";
- ret = mtd_device_register(mtd, NULL, 0);
- if (ret)
- goto err_probe;
-
- platform_set_drvdata(pdev, host);
- dev_info(&pdev->dev, "FSMC NAND driver registration successful\n");
- return 0;
-
-err_probe:
-err_scan_ident:
- if (host->mode == USE_DMA_ACCESS)
- dma_release_channel(host->write_dma_chan);
-err_req_write_chnl:
- if (host->mode == USE_DMA_ACCESS)
- dma_release_channel(host->read_dma_chan);
-err_req_read_chnl:
- clk_disable_unprepare(host->clk);
- return ret;
-}
-
-/*
- * Clean up routine
- */
-static int fsmc_nand_remove(struct platform_device *pdev)
-{
- struct fsmc_nand_data *host = platform_get_drvdata(pdev);
-
- if (host) {
- nand_release(nand_to_mtd(&host->nand));
-
- if (host->mode == USE_DMA_ACCESS) {
- dma_release_channel(host->write_dma_chan);
- dma_release_channel(host->read_dma_chan);
- }
- clk_disable_unprepare(host->clk);
- }
-
- return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int fsmc_nand_suspend(struct device *dev)
-{
- struct fsmc_nand_data *host = dev_get_drvdata(dev);
- if (host)
- clk_disable_unprepare(host->clk);
- return 0;
-}
-
-static int fsmc_nand_resume(struct device *dev)
-{
- struct fsmc_nand_data *host = dev_get_drvdata(dev);
- if (host) {
- clk_prepare_enable(host->clk);
- if (host->dev_timings)
- fsmc_nand_setup(host, host->dev_timings);
- }
- return 0;
-}
-#endif
-
-static SIMPLE_DEV_PM_OPS(fsmc_nand_pm_ops, fsmc_nand_suspend, fsmc_nand_resume);
-
-static const struct of_device_id fsmc_nand_id_table[] = {
- { .compatible = "st,spear600-fsmc-nand" },
- { .compatible = "stericsson,fsmc-nand" },
- {}
-};
-MODULE_DEVICE_TABLE(of, fsmc_nand_id_table);
-
-static struct platform_driver fsmc_nand_driver = {
- .remove = fsmc_nand_remove,
- .driver = {
- .name = "fsmc-nand",
- .of_match_table = fsmc_nand_id_table,
- .pm = &fsmc_nand_pm_ops,
- },
-};
-
-module_platform_driver_probe(fsmc_nand_driver, fsmc_nand_probe);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Vipin Kumar <vipin.kumar@st.com>, Ashish Priyadarshi");
-MODULE_DESCRIPTION("NAND driver for SPEAr Platforms");
diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c
deleted file mode 100644
index a8bde6665c24..000000000000
--- a/drivers/mtd/nand/gpio.c
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
- * drivers/mtd/nand/gpio.c
- *
- * Updated, and converted to generic GPIO based driver by Russell King.
- *
- * Written by Ben Dooks <ben@simtec.co.uk>
- * Based on 2.4 version by Mark Whittaker
- *
- * © 2004 Simtec Electronics
- *
- * Device driver for NAND flash that uses a memory mapped interface to
- * read/write the NAND commands and data, and GPIO pins for control signals
- * (the DT binding refers to this as "GPIO assisted NAND flash")
- *
- * 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.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/err.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/gpio/consumer.h>
-#include <linux/io.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/partitions.h>
-#include <linux/mtd/nand-gpio.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-
-struct gpiomtd {
- void __iomem *io_sync;
- struct nand_chip nand_chip;
- struct gpio_nand_platdata plat;
- struct gpio_desc *nce; /* Optional chip enable */
- struct gpio_desc *cle;
- struct gpio_desc *ale;
- struct gpio_desc *rdy;
- struct gpio_desc *nwp; /* Optional write protection */
-};
-
-static inline struct gpiomtd *gpio_nand_getpriv(struct mtd_info *mtd)
-{
- return container_of(mtd_to_nand(mtd), struct gpiomtd, nand_chip);
-}
-
-
-#ifdef CONFIG_ARM
-/* gpio_nand_dosync()
- *
- * Make sure the GPIO state changes occur in-order with writes to NAND
- * memory region.
- * Needed on PXA due to bus-reordering within the SoC itself (see section on
- * I/O ordering in PXA manual (section 2.3, p35)
- */
-static void gpio_nand_dosync(struct gpiomtd *gpiomtd)
-{
- unsigned long tmp;
-
- if (gpiomtd->io_sync) {
- /*
- * Linux memory barriers don't cater for what's required here.
- * What's required is what's here - a read from a separate
- * region with a dependency on that read.
- */
- tmp = readl(gpiomtd->io_sync);
- asm volatile("mov %1, %0\n" : "=r" (tmp) : "r" (tmp));
- }
-}
-#else
-static inline void gpio_nand_dosync(struct gpiomtd *gpiomtd) {}
-#endif
-
-static void gpio_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
-{
- struct gpiomtd *gpiomtd = gpio_nand_getpriv(mtd);
-
- gpio_nand_dosync(gpiomtd);
-
- if (ctrl & NAND_CTRL_CHANGE) {
- if (gpiomtd->nce)
- gpiod_set_value(gpiomtd->nce, !(ctrl & NAND_NCE));
- gpiod_set_value(gpiomtd->cle, !!(ctrl & NAND_CLE));
- gpiod_set_value(gpiomtd->ale, !!(ctrl & NAND_ALE));
- gpio_nand_dosync(gpiomtd);
- }
- if (cmd == NAND_CMD_NONE)
- return;
-
- writeb(cmd, gpiomtd->nand_chip.IO_ADDR_W);
- gpio_nand_dosync(gpiomtd);
-}
-
-static int gpio_nand_devready(struct mtd_info *mtd)
-{
- struct gpiomtd *gpiomtd = gpio_nand_getpriv(mtd);
-
- return gpiod_get_value(gpiomtd->rdy);
-}
-
-#ifdef CONFIG_OF
-static const struct of_device_id gpio_nand_id_table[] = {
- { .compatible = "gpio-control-nand" },
- {}
-};
-MODULE_DEVICE_TABLE(of, gpio_nand_id_table);
-
-static int gpio_nand_get_config_of(const struct device *dev,
- struct gpio_nand_platdata *plat)
-{
- u32 val;
-
- if (!dev->of_node)
- return -ENODEV;
-
- if (!of_property_read_u32(dev->of_node, "bank-width", &val)) {
- if (val == 2) {
- plat->options |= NAND_BUSWIDTH_16;
- } else if (val != 1) {
- dev_err(dev, "invalid bank-width %u\n", val);
- return -EINVAL;
- }
- }
-
- if (!of_property_read_u32(dev->of_node, "chip-delay", &val))
- plat->chip_delay = val;
-
- return 0;
-}
-
-static struct resource *gpio_nand_get_io_sync_of(struct platform_device *pdev)
-{
- struct resource *r;
- u64 addr;
-
- if (of_property_read_u64(pdev->dev.of_node,
- "gpio-control-nand,io-sync-reg", &addr))
- return NULL;
-
- r = devm_kzalloc(&pdev->dev, sizeof(*r), GFP_KERNEL);
- if (!r)
- return NULL;
-
- r->start = addr;
- r->end = r->start + 0x3;
- r->flags = IORESOURCE_MEM;
-
- return r;
-}
-#else /* CONFIG_OF */
-static inline int gpio_nand_get_config_of(const struct device *dev,
- struct gpio_nand_platdata *plat)
-{
- return -ENOSYS;
-}
-
-static inline struct resource *
-gpio_nand_get_io_sync_of(struct platform_device *pdev)
-{
- return NULL;
-}
-#endif /* CONFIG_OF */
-
-static inline int gpio_nand_get_config(const struct device *dev,
- struct gpio_nand_platdata *plat)
-{
- int ret = gpio_nand_get_config_of(dev, plat);
-
- if (!ret)
- return ret;
-
- if (dev_get_platdata(dev)) {
- memcpy(plat, dev_get_platdata(dev), sizeof(*plat));
- return 0;
- }
-
- return -EINVAL;
-}
-
-static inline struct resource *
-gpio_nand_get_io_sync(struct platform_device *pdev)
-{
- struct resource *r = gpio_nand_get_io_sync_of(pdev);
-
- if (r)
- return r;
-
- return platform_get_resource(pdev, IORESOURCE_MEM, 1);
-}
-
-static int gpio_nand_remove(struct platform_device *pdev)
-{
- struct gpiomtd *gpiomtd = platform_get_drvdata(pdev);
-
- nand_release(nand_to_mtd(&gpiomtd->nand_chip));
-
- /* Enable write protection and disable the chip */
- if (gpiomtd->nwp && !IS_ERR(gpiomtd->nwp))
- gpiod_set_value(gpiomtd->nwp, 0);
- if (gpiomtd->nce && !IS_ERR(gpiomtd->nce))
- gpiod_set_value(gpiomtd->nce, 0);
-
- return 0;
-}
-
-static int gpio_nand_probe(struct platform_device *pdev)
-{
- struct gpiomtd *gpiomtd;
- struct nand_chip *chip;
- struct mtd_info *mtd;
- struct resource *res;
- struct device *dev = &pdev->dev;
- int ret = 0;
-
- if (!dev->of_node && !dev_get_platdata(dev))
- return -EINVAL;
-
- gpiomtd = devm_kzalloc(dev, sizeof(*gpiomtd), GFP_KERNEL);
- if (!gpiomtd)
- return -ENOMEM;
-
- chip = &gpiomtd->nand_chip;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- chip->IO_ADDR_R = devm_ioremap_resource(dev, res);
- if (IS_ERR(chip->IO_ADDR_R))
- return PTR_ERR(chip->IO_ADDR_R);
-
- res = gpio_nand_get_io_sync(pdev);
- if (res) {
- gpiomtd->io_sync = devm_ioremap_resource(dev, res);
- if (IS_ERR(gpiomtd->io_sync))
- return PTR_ERR(gpiomtd->io_sync);
- }
-
- ret = gpio_nand_get_config(dev, &gpiomtd->plat);
- if (ret)
- return ret;
-
- /* Just enable the chip */
- gpiomtd->nce = devm_gpiod_get_optional(dev, "nce", GPIOD_OUT_HIGH);
- if (IS_ERR(gpiomtd->nce))
- return PTR_ERR(gpiomtd->nce);
-
- /* We disable write protection once we know probe() will succeed */
- gpiomtd->nwp = devm_gpiod_get_optional(dev, "nwp", GPIOD_OUT_LOW);
- if (IS_ERR(gpiomtd->nwp)) {
- ret = PTR_ERR(gpiomtd->nwp);
- goto out_ce;
- }
-
- gpiomtd->ale = devm_gpiod_get(dev, "ale", GPIOD_OUT_LOW);
- if (IS_ERR(gpiomtd->ale)) {
- ret = PTR_ERR(gpiomtd->ale);
- goto out_ce;
- }
-
- gpiomtd->cle = devm_gpiod_get(dev, "cle", GPIOD_OUT_LOW);
- if (IS_ERR(gpiomtd->cle)) {
- ret = PTR_ERR(gpiomtd->cle);
- goto out_ce;
- }
-
- gpiomtd->rdy = devm_gpiod_get_optional(dev, "rdy", GPIOD_IN);
- if (IS_ERR(gpiomtd->rdy)) {
- ret = PTR_ERR(gpiomtd->rdy);
- goto out_ce;
- }
- /* Using RDY pin */
- if (gpiomtd->rdy)
- chip->dev_ready = gpio_nand_devready;
-
- nand_set_flash_node(chip, pdev->dev.of_node);
- chip->IO_ADDR_W = chip->IO_ADDR_R;
- chip->ecc.mode = NAND_ECC_SOFT;
- chip->ecc.algo = NAND_ECC_HAMMING;
- chip->options = gpiomtd->plat.options;
- chip->chip_delay = gpiomtd->plat.chip_delay;
- chip->cmd_ctrl = gpio_nand_cmd_ctrl;
-
- mtd = nand_to_mtd(chip);
- mtd->dev.parent = dev;
-
- platform_set_drvdata(pdev, gpiomtd);
-
- /* Disable write protection, if wired up */
- if (gpiomtd->nwp && !IS_ERR(gpiomtd->nwp))
- gpiod_direction_output(gpiomtd->nwp, 1);
-
- ret = nand_scan(mtd, 1);
- if (ret)
- goto err_wp;
-
- if (gpiomtd->plat.adjust_parts)
- gpiomtd->plat.adjust_parts(&gpiomtd->plat, mtd->size);
-
- ret = mtd_device_register(mtd, gpiomtd->plat.parts,
- gpiomtd->plat.num_parts);
- if (!ret)
- return 0;
-
-err_wp:
- if (gpiomtd->nwp && !IS_ERR(gpiomtd->nwp))
- gpiod_set_value(gpiomtd->nwp, 0);
-out_ce:
- if (gpiomtd->nce && !IS_ERR(gpiomtd->nce))
- gpiod_set_value(gpiomtd->nce, 0);
-
- return ret;
-}
-
-static struct platform_driver gpio_nand_driver = {
- .probe = gpio_nand_probe,
- .remove = gpio_nand_remove,
- .driver = {
- .name = "gpio-nand",
- .of_match_table = of_match_ptr(gpio_nand_id_table),
- },
-};
-
-module_platform_driver(gpio_nand_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
-MODULE_DESCRIPTION("GPIO NAND Driver");
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
deleted file mode 100644
index 97787246af41..000000000000
--- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
+++ /dev/null
@@ -1,1510 +0,0 @@
-/*
- * Freescale GPMI NAND Flash Driver
- *
- * Copyright (C) 2008-2011 Freescale Semiconductor, Inc.
- * Copyright (C) 2008 Embedded Alley Solutions, Inc.
- *
- * 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/delay.h>
-#include <linux/clk.h>
-#include <linux/slab.h>
-
-#include "gpmi-nand.h"
-#include "gpmi-regs.h"
-#include "bch-regs.h"
-
-static struct timing_threshold timing_default_threshold = {
- .max_data_setup_cycles = (BM_GPMI_TIMING0_DATA_SETUP >>
- BP_GPMI_TIMING0_DATA_SETUP),
- .internal_data_setup_in_ns = 0,
- .max_sample_delay_factor = (BM_GPMI_CTRL1_RDN_DELAY >>
- BP_GPMI_CTRL1_RDN_DELAY),
- .max_dll_clock_period_in_ns = 32,
- .max_dll_delay_in_ns = 16,
-};
-
-#define MXS_SET_ADDR 0x4
-#define MXS_CLR_ADDR 0x8
-/*
- * Clear the bit and poll it cleared. This is usually called with
- * a reset address and mask being either SFTRST(bit 31) or CLKGATE
- * (bit 30).
- */
-static int clear_poll_bit(void __iomem *addr, u32 mask)
-{
- int timeout = 0x400;
-
- /* clear the bit */
- writel(mask, addr + MXS_CLR_ADDR);
-
- /*
- * SFTRST needs 3 GPMI clocks to settle, the reference manual
- * recommends to wait 1us.
- */
- udelay(1);
-
- /* poll the bit becoming clear */
- while ((readl(addr) & mask) && --timeout)
- /* nothing */;
-
- return !timeout;
-}
-
-#define MODULE_CLKGATE (1 << 30)
-#define MODULE_SFTRST (1 << 31)
-/*
- * The current mxs_reset_block() will do two things:
- * [1] enable the module.
- * [2] reset the module.
- *
- * In most of the cases, it's ok.
- * But in MX23, there is a hardware bug in the BCH block (see erratum #2847).
- * If you try to soft reset the BCH block, it becomes unusable until
- * the next hard reset. This case occurs in the NAND boot mode. When the board
- * boots by NAND, the ROM of the chip will initialize the BCH blocks itself.
- * So If the driver tries to reset the BCH again, the BCH will not work anymore.
- * You will see a DMA timeout in this case. The bug has been fixed
- * in the following chips, such as MX28.
- *
- * To avoid this bug, just add a new parameter `just_enable` for
- * the mxs_reset_block(), and rewrite it here.
- */
-static int gpmi_reset_block(void __iomem *reset_addr, bool just_enable)
-{
- int ret;
- int timeout = 0x400;
-
- /* clear and poll SFTRST */
- ret = clear_poll_bit(reset_addr, MODULE_SFTRST);
- if (unlikely(ret))
- goto error;
-
- /* clear CLKGATE */
- writel(MODULE_CLKGATE, reset_addr + MXS_CLR_ADDR);
-
- if (!just_enable) {
- /* set SFTRST to reset the block */
- writel(MODULE_SFTRST, reset_addr + MXS_SET_ADDR);
- udelay(1);
-
- /* poll CLKGATE becoming set */
- while ((!(readl(reset_addr) & MODULE_CLKGATE)) && --timeout)
- /* nothing */;
- if (unlikely(!timeout))
- goto error;
- }
-
- /* clear and poll SFTRST */
- ret = clear_poll_bit(reset_addr, MODULE_SFTRST);
- if (unlikely(ret))
- goto error;
-
- /* clear and poll CLKGATE */
- ret = clear_poll_bit(reset_addr, MODULE_CLKGATE);
- if (unlikely(ret))
- goto error;
-
- return 0;
-
-error:
- pr_err("%s(%p): module reset timeout\n", __func__, reset_addr);
- return -ETIMEDOUT;
-}
-
-static int __gpmi_enable_clk(struct gpmi_nand_data *this, bool v)
-{
- struct clk *clk;
- int ret;
- int i;
-
- for (i = 0; i < GPMI_CLK_MAX; i++) {
- clk = this->resources.clock[i];
- if (!clk)
- break;
-
- if (v) {
- ret = clk_prepare_enable(clk);
- if (ret)
- goto err_clk;
- } else {
- clk_disable_unprepare(clk);
- }
- }
- return 0;
-
-err_clk:
- for (; i > 0; i--)
- clk_disable_unprepare(this->resources.clock[i - 1]);
- return ret;
-}
-
-#define gpmi_enable_clk(x) __gpmi_enable_clk(x, true)
-#define gpmi_disable_clk(x) __gpmi_enable_clk(x, false)
-
-int gpmi_init(struct gpmi_nand_data *this)
-{
- struct resources *r = &this->resources;
- int ret;
-
- ret = gpmi_enable_clk(this);
- if (ret)
- return ret;
- ret = gpmi_reset_block(r->gpmi_regs, false);
- if (ret)
- goto err_out;
-
- /*
- * Reset BCH here, too. We got failures otherwise :(
- * See later BCH reset for explanation of MX23 handling
- */
- ret = gpmi_reset_block(r->bch_regs, GPMI_IS_MX23(this));
- if (ret)
- goto err_out;
-
-
- /* Choose NAND mode. */
- writel(BM_GPMI_CTRL1_GPMI_MODE, r->gpmi_regs + HW_GPMI_CTRL1_CLR);
-
- /* Set the IRQ polarity. */
- writel(BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY,
- r->gpmi_regs + HW_GPMI_CTRL1_SET);
-
- /* Disable Write-Protection. */
- writel(BM_GPMI_CTRL1_DEV_RESET, r->gpmi_regs + HW_GPMI_CTRL1_SET);
-
- /* Select BCH ECC. */
- writel(BM_GPMI_CTRL1_BCH_MODE, r->gpmi_regs + HW_GPMI_CTRL1_SET);
-
- /*
- * Decouple the chip select from dma channel. We use dma0 for all
- * the chips.
- */
- writel(BM_GPMI_CTRL1_DECOUPLE_CS, r->gpmi_regs + HW_GPMI_CTRL1_SET);
-
- gpmi_disable_clk(this);
- return 0;
-err_out:
- gpmi_disable_clk(this);
- return ret;
-}
-
-/* This function is very useful. It is called only when the bug occur. */
-void gpmi_dump_info(struct gpmi_nand_data *this)
-{
- struct resources *r = &this->resources;
- struct bch_geometry *geo = &this->bch_geometry;
- u32 reg;
- int i;
-
- dev_err(this->dev, "Show GPMI registers :\n");
- for (i = 0; i <= HW_GPMI_DEBUG / 0x10 + 1; i++) {
- reg = readl(r->gpmi_regs + i * 0x10);
- dev_err(this->dev, "offset 0x%.3x : 0x%.8x\n", i * 0x10, reg);
- }
-
- /* start to print out the BCH info */
- dev_err(this->dev, "Show BCH registers :\n");
- for (i = 0; i <= HW_BCH_VERSION / 0x10 + 1; i++) {
- reg = readl(r->bch_regs + i * 0x10);
- dev_err(this->dev, "offset 0x%.3x : 0x%.8x\n", i * 0x10, reg);
- }
- dev_err(this->dev, "BCH Geometry :\n"
- "GF length : %u\n"
- "ECC Strength : %u\n"
- "Page Size in Bytes : %u\n"
- "Metadata Size in Bytes : %u\n"
- "ECC Chunk Size in Bytes: %u\n"
- "ECC Chunk Count : %u\n"
- "Payload Size in Bytes : %u\n"
- "Auxiliary Size in Bytes: %u\n"
- "Auxiliary Status Offset: %u\n"
- "Block Mark Byte Offset : %u\n"
- "Block Mark Bit Offset : %u\n",
- geo->gf_len,
- geo->ecc_strength,
- geo->page_size,
- geo->metadata_size,
- geo->ecc_chunk_size,
- geo->ecc_chunk_count,
- geo->payload_size,
- geo->auxiliary_size,
- geo->auxiliary_status_offset,
- geo->block_mark_byte_offset,
- geo->block_mark_bit_offset);
-}
-
-/* Configures the geometry for BCH. */
-int bch_set_geometry(struct gpmi_nand_data *this)
-{
- struct resources *r = &this->resources;
- struct bch_geometry *bch_geo = &this->bch_geometry;
- unsigned int block_count;
- unsigned int block_size;
- unsigned int metadata_size;
- unsigned int ecc_strength;
- unsigned int page_size;
- unsigned int gf_len;
- int ret;
-
- if (common_nfc_set_geometry(this))
- return !0;
-
- block_count = bch_geo->ecc_chunk_count - 1;
- block_size = bch_geo->ecc_chunk_size;
- metadata_size = bch_geo->metadata_size;
- ecc_strength = bch_geo->ecc_strength >> 1;
- page_size = bch_geo->page_size;
- gf_len = bch_geo->gf_len;
-
- ret = gpmi_enable_clk(this);
- if (ret)
- return ret;
-
- /*
- * Due to erratum #2847 of the MX23, the BCH cannot be soft reset on this
- * chip, otherwise it will lock up. So we skip resetting BCH on the MX23.
- * On the other hand, the MX28 needs the reset, because one case has been
- * seen where the BCH produced ECC errors constantly after 10000
- * consecutive reboots. The latter case has not been seen on the MX23
- * yet, still we don't know if it could happen there as well.
- */
- ret = gpmi_reset_block(r->bch_regs, GPMI_IS_MX23(this));
- if (ret)
- goto err_out;
-
- /* Configure layout 0. */
- writel(BF_BCH_FLASH0LAYOUT0_NBLOCKS(block_count)
- | BF_BCH_FLASH0LAYOUT0_META_SIZE(metadata_size)
- | BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this)
- | BF_BCH_FLASH0LAYOUT0_GF(gf_len, this)
- | BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size, this),
- r->bch_regs + HW_BCH_FLASH0LAYOUT0);
-
- writel(BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size)
- | BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this)
- | BF_BCH_FLASH0LAYOUT1_GF(gf_len, this)
- | BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size, this),
- r->bch_regs + HW_BCH_FLASH0LAYOUT1);
-
- /* Set *all* chip selects to use layout 0. */
- writel(0, r->bch_regs + HW_BCH_LAYOUTSELECT);
-
- /* Enable interrupts. */
- writel(BM_BCH_CTRL_COMPLETE_IRQ_EN,
- r->bch_regs + HW_BCH_CTRL_SET);
-
- gpmi_disable_clk(this);
- return 0;
-err_out:
- gpmi_disable_clk(this);
- return ret;
-}
-
-/* Converts time in nanoseconds to cycles. */
-static unsigned int ns_to_cycles(unsigned int time,
- unsigned int period, unsigned int min)
-{
- unsigned int k;
-
- k = (time + period - 1) / period;
- return max(k, min);
-}
-
-#define DEF_MIN_PROP_DELAY 5
-#define DEF_MAX_PROP_DELAY 9
-/* Apply timing to current hardware conditions. */
-static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this,
- struct gpmi_nfc_hardware_timing *hw)
-{
- struct timing_threshold *nfc = &timing_default_threshold;
- struct resources *r = &this->resources;
- struct nand_chip *nand = &this->nand;
- struct nand_timing target = this->timing;
- bool improved_timing_is_available;
- unsigned long clock_frequency_in_hz;
- unsigned int clock_period_in_ns;
- bool dll_use_half_periods;
- unsigned int dll_delay_shift;
- unsigned int max_sample_delay_in_ns;
- unsigned int address_setup_in_cycles;
- unsigned int data_setup_in_ns;
- unsigned int data_setup_in_cycles;
- unsigned int data_hold_in_cycles;
- int ideal_sample_delay_in_ns;
- unsigned int sample_delay_factor;
- int tEYE;
- unsigned int min_prop_delay_in_ns = DEF_MIN_PROP_DELAY;
- unsigned int max_prop_delay_in_ns = DEF_MAX_PROP_DELAY;
-
- /*
- * If there are multiple chips, we need to relax the timings to allow
- * for signal distortion due to higher capacitance.
- */
- if (nand->numchips > 2) {
- target.data_setup_in_ns += 10;
- target.data_hold_in_ns += 10;
- target.address_setup_in_ns += 10;
- } else if (nand->numchips > 1) {
- target.data_setup_in_ns += 5;
- target.data_hold_in_ns += 5;
- target.address_setup_in_ns += 5;
- }
-
- /* Check if improved timing information is available. */
- improved_timing_is_available =
- (target.tREA_in_ns >= 0) &&
- (target.tRLOH_in_ns >= 0) &&
- (target.tRHOH_in_ns >= 0);
-
- /* Inspect the clock. */
- nfc->clock_frequency_in_hz = clk_get_rate(r->clock[0]);
- clock_frequency_in_hz = nfc->clock_frequency_in_hz;
- clock_period_in_ns = NSEC_PER_SEC / clock_frequency_in_hz;
-
- /*
- * The NFC quantizes setup and hold parameters in terms of clock cycles.
- * Here, we quantize the setup and hold timing parameters to the
- * next-highest clock period to make sure we apply at least the
- * specified times.
- *
- * For data setup and data hold, the hardware interprets a value of zero
- * as the largest possible delay. This is not what's intended by a zero
- * in the input parameter, so we impose a minimum of one cycle.
- */
- data_setup_in_cycles = ns_to_cycles(target.data_setup_in_ns,
- clock_period_in_ns, 1);
- data_hold_in_cycles = ns_to_cycles(target.data_hold_in_ns,
- clock_period_in_ns, 1);
- address_setup_in_cycles = ns_to_cycles(target.address_setup_in_ns,
- clock_period_in_ns, 0);
-
- /*
- * The clock's period affects the sample delay in a number of ways:
- *
- * (1) The NFC HAL tells us the maximum clock period the sample delay
- * DLL can tolerate. If the clock period is greater than half that
- * maximum, we must configure the DLL to be driven by half periods.
- *
- * (2) We need to convert from an ideal sample delay, in ns, to a
- * "sample delay factor," which the NFC uses. This factor depends on
- * whether we're driving the DLL with full or half periods.
- * Paraphrasing the reference manual:
- *
- * AD = SDF x 0.125 x RP
- *
- * where:
- *
- * AD is the applied delay, in ns.
- * SDF is the sample delay factor, which is dimensionless.
- * RP is the reference period, in ns, which is a full clock period
- * if the DLL is being driven by full periods, or half that if
- * the DLL is being driven by half periods.
- *
- * Let's re-arrange this in a way that's more useful to us:
- *
- * 8
- * SDF = AD x ----
- * RP
- *
- * The reference period is either the clock period or half that, so this
- * is:
- *
- * 8 AD x DDF
- * SDF = AD x ----- = --------
- * f x P P
- *
- * where:
- *
- * f is 1 or 1/2, depending on how we're driving the DLL.
- * P is the clock period.
- * DDF is the DLL Delay Factor, a dimensionless value that
- * incorporates all the constants in the conversion.
- *
- * DDF will be either 8 or 16, both of which are powers of two. We can
- * reduce the cost of this conversion by using bit shifts instead of
- * multiplication or division. Thus:
- *
- * AD << DDS
- * SDF = ---------
- * P
- *
- * or
- *
- * AD = (SDF >> DDS) x P
- *
- * where:
- *
- * DDS is the DLL Delay Shift, the logarithm to base 2 of the DDF.
- */
- if (clock_period_in_ns > (nfc->max_dll_clock_period_in_ns >> 1)) {
- dll_use_half_periods = true;
- dll_delay_shift = 3 + 1;
- } else {
- dll_use_half_periods = false;
- dll_delay_shift = 3;
- }
-
- /*
- * Compute the maximum sample delay the NFC allows, under current
- * conditions. If the clock is running too slowly, no sample delay is
- * possible.
- */
- if (clock_period_in_ns > nfc->max_dll_clock_period_in_ns)
- max_sample_delay_in_ns = 0;
- else {
- /*
- * Compute the delay implied by the largest sample delay factor
- * the NFC allows.
- */
- max_sample_delay_in_ns =
- (nfc->max_sample_delay_factor * clock_period_in_ns) >>
- dll_delay_shift;
-
- /*
- * Check if the implied sample delay larger than the NFC
- * actually allows.
- */
- if (max_sample_delay_in_ns > nfc->max_dll_delay_in_ns)
- max_sample_delay_in_ns = nfc->max_dll_delay_in_ns;
- }
-
- /*
- * Check if improved timing information is available. If not, we have to
- * use a less-sophisticated algorithm.
- */
- if (!improved_timing_is_available) {
- /*
- * Fold the read setup time required by the NFC into the ideal
- * sample delay.
- */
- ideal_sample_delay_in_ns = target.gpmi_sample_delay_in_ns +
- nfc->internal_data_setup_in_ns;
-
- /*
- * The ideal sample delay may be greater than the maximum
- * allowed by the NFC. If so, we can trade off sample delay time
- * for more data setup time.
- *
- * In each iteration of the following loop, we add a cycle to
- * the data setup time and subtract a corresponding amount from
- * the sample delay until we've satisified the constraints or
- * can't do any better.
- */
- while ((ideal_sample_delay_in_ns > max_sample_delay_in_ns) &&
- (data_setup_in_cycles < nfc->max_data_setup_cycles)) {
-
- data_setup_in_cycles++;
- ideal_sample_delay_in_ns -= clock_period_in_ns;
-
- if (ideal_sample_delay_in_ns < 0)
- ideal_sample_delay_in_ns = 0;
-
- }
-
- /*
- * Compute the sample delay factor that corresponds most closely
- * to the ideal sample delay. If the result is too large for the
- * NFC, use the maximum value.
- *
- * Notice that we use the ns_to_cycles function to compute the
- * sample delay factor. We do this because the form of the
- * computation is the same as that for calculating cycles.
- */
- sample_delay_factor =
- ns_to_cycles(
- ideal_sample_delay_in_ns << dll_delay_shift,
- clock_period_in_ns, 0);
-
- if (sample_delay_factor > nfc->max_sample_delay_factor)
- sample_delay_factor = nfc->max_sample_delay_factor;
-
- /* Skip to the part where we return our results. */
- goto return_results;
- }
-
- /*
- * If control arrives here, we have more detailed timing information,
- * so we can use a better algorithm.
- */
-
- /*
- * Fold the read setup time required by the NFC into the maximum
- * propagation delay.
- */
- max_prop_delay_in_ns += nfc->internal_data_setup_in_ns;
-
- /*
- * Earlier, we computed the number of clock cycles required to satisfy
- * the data setup time. Now, we need to know the actual nanoseconds.
- */
- data_setup_in_ns = clock_period_in_ns * data_setup_in_cycles;
-
- /*
- * Compute tEYE, the width of the data eye when reading from the NAND
- * Flash. The eye width is fundamentally determined by the data setup
- * time, perturbed by propagation delays and some characteristics of the
- * NAND Flash device.
- *
- * start of the eye = max_prop_delay + tREA
- * end of the eye = min_prop_delay + tRHOH + data_setup
- */
- tEYE = (int)min_prop_delay_in_ns + (int)target.tRHOH_in_ns +
- (int)data_setup_in_ns;
-
- tEYE -= (int)max_prop_delay_in_ns + (int)target.tREA_in_ns;
-
- /*
- * The eye must be open. If it's not, we can try to open it by
- * increasing its main forcer, the data setup time.
- *
- * In each iteration of the following loop, we increase the data setup
- * time by a single clock cycle. We do this until either the eye is
- * open or we run into NFC limits.
- */
- while ((tEYE <= 0) &&
- (data_setup_in_cycles < nfc->max_data_setup_cycles)) {
- /* Give a cycle to data setup. */
- data_setup_in_cycles++;
- /* Synchronize the data setup time with the cycles. */
- data_setup_in_ns += clock_period_in_ns;
- /* Adjust tEYE accordingly. */
- tEYE += clock_period_in_ns;
- }
-
- /*
- * When control arrives here, the eye is open. The ideal time to sample
- * the data is in the center of the eye:
- *
- * end of the eye + start of the eye
- * --------------------------------- - data_setup
- * 2
- *
- * After some algebra, this simplifies to the code immediately below.
- */
- ideal_sample_delay_in_ns =
- ((int)max_prop_delay_in_ns +
- (int)target.tREA_in_ns +
- (int)min_prop_delay_in_ns +
- (int)target.tRHOH_in_ns -
- (int)data_setup_in_ns) >> 1;
-
- /*
- * The following figure illustrates some aspects of a NAND Flash read:
- *
- *
- * __ _____________________________________
- * RDN \_________________/
- *
- * <---- tEYE ----->
- * /-----------------\
- * Read Data ----------------------------< >---------
- * \-----------------/
- * ^ ^ ^ ^
- * | | | |
- * |<--Data Setup -->|<--Delay Time -->| |
- * | | | |
- * | | |
- * | |<-- Quantized Delay Time -->|
- * | | |
- *
- *
- * We have some issues we must now address:
- *
- * (1) The *ideal* sample delay time must not be negative. If it is, we
- * jam it to zero.
- *
- * (2) The *ideal* sample delay time must not be greater than that
- * allowed by the NFC. If it is, we can increase the data setup
- * time, which will reduce the delay between the end of the data
- * setup and the center of the eye. It will also make the eye
- * larger, which might help with the next issue...
- *
- * (3) The *quantized* sample delay time must not fall either before the
- * eye opens or after it closes (the latter is the problem
- * illustrated in the above figure).
- */
-
- /* Jam a negative ideal sample delay to zero. */
- if (ideal_sample_delay_in_ns < 0)
- ideal_sample_delay_in_ns = 0;
-
- /*
- * Extend the data setup as needed to reduce the ideal sample delay
- * below the maximum permitted by the NFC.
- */
- while ((ideal_sample_delay_in_ns > max_sample_delay_in_ns) &&
- (data_setup_in_cycles < nfc->max_data_setup_cycles)) {
-
- /* Give a cycle to data setup. */
- data_setup_in_cycles++;
- /* Synchronize the data setup time with the cycles. */
- data_setup_in_ns += clock_period_in_ns;
- /* Adjust tEYE accordingly. */
- tEYE += clock_period_in_ns;
-
- /*
- * Decrease the ideal sample delay by one half cycle, to keep it
- * in the middle of the eye.
- */
- ideal_sample_delay_in_ns -= (clock_period_in_ns >> 1);
-
- /* Jam a negative ideal sample delay to zero. */
- if (ideal_sample_delay_in_ns < 0)
- ideal_sample_delay_in_ns = 0;
- }
-
- /*
- * Compute the sample delay factor that corresponds to the ideal sample
- * delay. If the result is too large, then use the maximum allowed
- * value.
- *
- * Notice that we use the ns_to_cycles function to compute the sample
- * delay factor. We do this because the form of the computation is the
- * same as that for calculating cycles.
- */
- sample_delay_factor =
- ns_to_cycles(ideal_sample_delay_in_ns << dll_delay_shift,
- clock_period_in_ns, 0);
-
- if (sample_delay_factor > nfc->max_sample_delay_factor)
- sample_delay_factor = nfc->max_sample_delay_factor;
-
- /*
- * These macros conveniently encapsulate a computation we'll use to
- * continuously evaluate whether or not the data sample delay is inside
- * the eye.
- */
- #define IDEAL_DELAY ((int) ideal_sample_delay_in_ns)
-
- #define QUANTIZED_DELAY \
- ((int) ((sample_delay_factor * clock_period_in_ns) >> \
- dll_delay_shift))
-
- #define DELAY_ERROR (abs(QUANTIZED_DELAY - IDEAL_DELAY))
-
- #define SAMPLE_IS_NOT_WITHIN_THE_EYE (DELAY_ERROR > (tEYE >> 1))
-
- /*
- * While the quantized sample time falls outside the eye, reduce the
- * sample delay or extend the data setup to move the sampling point back
- * toward the eye. Do not allow the number of data setup cycles to
- * exceed the maximum allowed by the NFC.
- */
- while (SAMPLE_IS_NOT_WITHIN_THE_EYE &&
- (data_setup_in_cycles < nfc->max_data_setup_cycles)) {
- /*
- * If control arrives here, the quantized sample delay falls
- * outside the eye. Check if it's before the eye opens, or after
- * the eye closes.
- */
- if (QUANTIZED_DELAY > IDEAL_DELAY) {
- /*
- * If control arrives here, the quantized sample delay
- * falls after the eye closes. Decrease the quantized
- * delay time and then go back to re-evaluate.
- */
- if (sample_delay_factor != 0)
- sample_delay_factor--;
- continue;
- }
-
- /*
- * If control arrives here, the quantized sample delay falls
- * before the eye opens. Shift the sample point by increasing
- * data setup time. This will also make the eye larger.
- */
-
- /* Give a cycle to data setup. */
- data_setup_in_cycles++;
- /* Synchronize the data setup time with the cycles. */
- data_setup_in_ns += clock_period_in_ns;
- /* Adjust tEYE accordingly. */
- tEYE += clock_period_in_ns;
-
- /*
- * Decrease the ideal sample delay by one half cycle, to keep it
- * in the middle of the eye.
- */
- ideal_sample_delay_in_ns -= (clock_period_in_ns >> 1);
-
- /* ...and one less period for the delay time. */
- ideal_sample_delay_in_ns -= clock_period_in_ns;
-
- /* Jam a negative ideal sample delay to zero. */
- if (ideal_sample_delay_in_ns < 0)
- ideal_sample_delay_in_ns = 0;
-
- /*
- * We have a new ideal sample delay, so re-compute the quantized
- * delay.
- */
- sample_delay_factor =
- ns_to_cycles(
- ideal_sample_delay_in_ns << dll_delay_shift,
- clock_period_in_ns, 0);
-
- if (sample_delay_factor > nfc->max_sample_delay_factor)
- sample_delay_factor = nfc->max_sample_delay_factor;
- }
-
- /* Control arrives here when we're ready to return our results. */
-return_results:
- hw->data_setup_in_cycles = data_setup_in_cycles;
- hw->data_hold_in_cycles = data_hold_in_cycles;
- hw->address_setup_in_cycles = address_setup_in_cycles;
- hw->use_half_periods = dll_use_half_periods;
- hw->sample_delay_factor = sample_delay_factor;
- hw->device_busy_timeout = GPMI_DEFAULT_BUSY_TIMEOUT;
- hw->wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS;
-
- /* Return success. */
- return 0;
-}
-
-/*
- * <1> Firstly, we should know what's the GPMI-clock means.
- * The GPMI-clock is the internal clock in the gpmi nand controller.
- * If you set 100MHz to gpmi nand controller, the GPMI-clock's period
- * is 10ns. Mark the GPMI-clock's period as GPMI-clock-period.
- *
- * <2> Secondly, we should know what's the frequency on the nand chip pins.
- * The frequency on the nand chip pins is derived from the GPMI-clock.
- * We can get it from the following equation:
- *
- * F = G / (DS + DH)
- *
- * F : the frequency on the nand chip pins.
- * G : the GPMI clock, such as 100MHz.
- * DS : GPMI_HW_GPMI_TIMING0:DATA_SETUP
- * DH : GPMI_HW_GPMI_TIMING0:DATA_HOLD
- *
- * <3> Thirdly, when the frequency on the nand chip pins is above 33MHz,
- * the nand EDO(extended Data Out) timing could be applied.
- * The GPMI implements a feedback read strobe to sample the read data.
- * The feedback read strobe can be delayed to support the nand EDO timing
- * where the read strobe may deasserts before the read data is valid, and
- * read data is valid for some time after read strobe.
- *
- * The following figure illustrates some aspects of a NAND Flash read:
- *
- * |<---tREA---->|
- * | |
- * | | |
- * |<--tRP-->| |
- * | | |
- * __ ___|__________________________________
- * RDN \________/ |
- * |
- * /---------\
- * Read Data --------------< >---------
- * \---------/
- * | |
- * |<-D->|
- * FeedbackRDN ________ ____________
- * \___________/
- *
- * D stands for delay, set in the HW_GPMI_CTRL1:RDN_DELAY.
- *
- *
- * <4> Now, we begin to describe how to compute the right RDN_DELAY.
- *
- * 4.1) From the aspect of the nand chip pins:
- * Delay = (tREA + C - tRP) {1}
- *
- * tREA : the maximum read access time. From the ONFI nand standards,
- * we know that tREA is 16ns in mode 5, tREA is 20ns is mode 4.
- * Please check it in : www.onfi.org
- * C : a constant for adjust the delay. default is 4.
- * tRP : the read pulse width.
- * Specified by the HW_GPMI_TIMING0:DATA_SETUP:
- * tRP = (GPMI-clock-period) * DATA_SETUP
- *
- * 4.2) From the aspect of the GPMI nand controller:
- * Delay = RDN_DELAY * 0.125 * RP {2}
- *
- * RP : the DLL reference period.
- * if (GPMI-clock-period > DLL_THRETHOLD)
- * RP = GPMI-clock-period / 2;
- * else
- * RP = GPMI-clock-period;
- *
- * Set the HW_GPMI_CTRL1:HALF_PERIOD if GPMI-clock-period
- * is greater DLL_THRETHOLD. In other SOCs, the DLL_THRETHOLD
- * is 16ns, but in mx6q, we use 12ns.
- *
- * 4.3) since {1} equals {2}, we get:
- *
- * (tREA + 4 - tRP) * 8
- * RDN_DELAY = --------------------- {3}
- * RP
- *
- * 4.4) We only support the fastest asynchronous mode of ONFI nand.
- * For some ONFI nand, the mode 4 is the fastest mode;
- * while for some ONFI nand, the mode 5 is the fastest mode.
- * So we only support the mode 4 and mode 5. It is no need to
- * support other modes.
- */
-static void gpmi_compute_edo_timing(struct gpmi_nand_data *this,
- struct gpmi_nfc_hardware_timing *hw)
-{
- struct resources *r = &this->resources;
- unsigned long rate = clk_get_rate(r->clock[0]);
- int mode = this->timing_mode;
- int dll_threshold = this->devdata->max_chain_delay;
- unsigned long delay;
- unsigned long clk_period;
- int t_rea;
- int c = 4;
- int t_rp;
- int rp;
-
- /*
- * [1] for GPMI_HW_GPMI_TIMING0:
- * The async mode requires 40MHz for mode 4, 50MHz for mode 5.
- * The GPMI can support 100MHz at most. So if we want to
- * get the 40MHz or 50MHz, we have to set DS=1, DH=1.
- * Set the ADDRESS_SETUP to 0 in mode 4.
- */
- hw->data_setup_in_cycles = 1;
- hw->data_hold_in_cycles = 1;
- hw->address_setup_in_cycles = ((mode == 5) ? 1 : 0);
-
- /* [2] for GPMI_HW_GPMI_TIMING1 */
- hw->device_busy_timeout = 0x9000;
-
- /* [3] for GPMI_HW_GPMI_CTRL1 */
- hw->wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY;
-
- /*
- * Enlarge 10 times for the numerator and denominator in {3}.
- * This make us to get more accurate result.
- */
- clk_period = NSEC_PER_SEC / (rate / 10);
- dll_threshold *= 10;
- t_rea = ((mode == 5) ? 16 : 20) * 10;
- c *= 10;
-
- t_rp = clk_period * 1; /* DATA_SETUP is 1 */
-
- if (clk_period > dll_threshold) {
- hw->use_half_periods = 1;
- rp = clk_period / 2;
- } else {
- hw->use_half_periods = 0;
- rp = clk_period;
- }
-
- /*
- * Multiply the numerator with 10, we could do a round off:
- * 7.8 round up to 8; 7.4 round down to 7.
- */
- delay = (((t_rea + c - t_rp) * 8) * 10) / rp;
- delay = (delay + 5) / 10;
-
- hw->sample_delay_factor = delay;
-}
-
-static int enable_edo_mode(struct gpmi_nand_data *this, int mode)
-{
- struct resources *r = &this->resources;
- struct nand_chip *nand = &this->nand;
- struct mtd_info *mtd = nand_to_mtd(nand);
- uint8_t *feature;
- unsigned long rate;
- int ret;
-
- feature = kzalloc(ONFI_SUBFEATURE_PARAM_LEN, GFP_KERNEL);
- if (!feature)
- return -ENOMEM;
-
- nand->select_chip(mtd, 0);
-
- /* [1] send SET FEATURE command to NAND */
- feature[0] = mode;
- ret = nand->onfi_set_features(mtd, nand,
- ONFI_FEATURE_ADDR_TIMING_MODE, feature);
- if (ret)
- goto err_out;
-
- /* [2] send GET FEATURE command to double-check the timing mode */
- memset(feature, 0, ONFI_SUBFEATURE_PARAM_LEN);
- ret = nand->onfi_get_features(mtd, nand,
- ONFI_FEATURE_ADDR_TIMING_MODE, feature);
- if (ret || feature[0] != mode)
- goto err_out;
-
- nand->select_chip(mtd, -1);
-
- /* [3] set the main IO clock, 100MHz for mode 5, 80MHz for mode 4. */
- rate = (mode == 5) ? 100000000 : 80000000;
- clk_set_rate(r->clock[0], rate);
-
- /* Let the gpmi_begin() re-compute the timing again. */
- this->flags &= ~GPMI_TIMING_INIT_OK;
-
- this->flags |= GPMI_ASYNC_EDO_ENABLED;
- this->timing_mode = mode;
- kfree(feature);
- dev_info(this->dev, "enable the asynchronous EDO mode %d\n", mode);
- return 0;
-
-err_out:
- nand->select_chip(mtd, -1);
- kfree(feature);
- dev_err(this->dev, "mode:%d ,failed in set feature.\n", mode);
- return -EINVAL;
-}
-
-int gpmi_extra_init(struct gpmi_nand_data *this)
-{
- struct nand_chip *chip = &this->nand;
-
- /* Enable the asynchronous EDO feature. */
- if (GPMI_IS_MX6(this) && chip->onfi_version) {
- int mode = onfi_get_async_timing_mode(chip);
-
- /* We only support the timing mode 4 and mode 5. */
- if (mode & ONFI_TIMING_MODE_5)
- mode = 5;
- else if (mode & ONFI_TIMING_MODE_4)
- mode = 4;
- else
- return 0;
-
- return enable_edo_mode(this, mode);
- }
- return 0;
-}
-
-/* Begin the I/O */
-void gpmi_begin(struct gpmi_nand_data *this)
-{
- struct resources *r = &this->resources;
- void __iomem *gpmi_regs = r->gpmi_regs;
- unsigned int clock_period_in_ns;
- uint32_t reg;
- unsigned int dll_wait_time_in_us;
- struct gpmi_nfc_hardware_timing hw;
- int ret;
-
- /* Enable the clock. */
- ret = gpmi_enable_clk(this);
- if (ret) {
- dev_err(this->dev, "We failed in enable the clk\n");
- goto err_out;
- }
-
- /* Only initialize the timing once */
- if (this->flags & GPMI_TIMING_INIT_OK)
- return;
- this->flags |= GPMI_TIMING_INIT_OK;
-
- if (this->flags & GPMI_ASYNC_EDO_ENABLED)
- gpmi_compute_edo_timing(this, &hw);
- else
- gpmi_nfc_compute_hardware_timing(this, &hw);
-
- /* [1] Set HW_GPMI_TIMING0 */
- reg = BF_GPMI_TIMING0_ADDRESS_SETUP(hw.address_setup_in_cycles) |
- BF_GPMI_TIMING0_DATA_HOLD(hw.data_hold_in_cycles) |
- BF_GPMI_TIMING0_DATA_SETUP(hw.data_setup_in_cycles);
-
- writel(reg, gpmi_regs + HW_GPMI_TIMING0);
-
- /* [2] Set HW_GPMI_TIMING1 */
- writel(BF_GPMI_TIMING1_BUSY_TIMEOUT(hw.device_busy_timeout),
- gpmi_regs + HW_GPMI_TIMING1);
-
- /* [3] The following code is to set the HW_GPMI_CTRL1. */
-
- /* Set the WRN_DLY_SEL */
- writel(BM_GPMI_CTRL1_WRN_DLY_SEL, gpmi_regs + HW_GPMI_CTRL1_CLR);
- writel(BF_GPMI_CTRL1_WRN_DLY_SEL(hw.wrn_dly_sel),
- gpmi_regs + HW_GPMI_CTRL1_SET);
-
- /* DLL_ENABLE must be set to 0 when setting RDN_DELAY or HALF_PERIOD. */
- writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_CLR);
-
- /* Clear out the DLL control fields. */
- reg = BM_GPMI_CTRL1_RDN_DELAY | BM_GPMI_CTRL1_HALF_PERIOD;
- writel(reg, gpmi_regs + HW_GPMI_CTRL1_CLR);
-
- /* If no sample delay is called for, return immediately. */
- if (!hw.sample_delay_factor)
- return;
-
- /* Set RDN_DELAY or HALF_PERIOD. */
- reg = ((hw.use_half_periods) ? BM_GPMI_CTRL1_HALF_PERIOD : 0)
- | BF_GPMI_CTRL1_RDN_DELAY(hw.sample_delay_factor);
-
- writel(reg, gpmi_regs + HW_GPMI_CTRL1_SET);
-
- /* At last, we enable the DLL. */
- writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_SET);
-
- /*
- * After we enable the GPMI DLL, we have to wait 64 clock cycles before
- * we can use the GPMI. Calculate the amount of time we need to wait,
- * in microseconds.
- */
- clock_period_in_ns = NSEC_PER_SEC / clk_get_rate(r->clock[0]);
- dll_wait_time_in_us = (clock_period_in_ns * 64) / 1000;
-
- if (!dll_wait_time_in_us)
- dll_wait_time_in_us = 1;
-
- /* Wait for the DLL to settle. */
- udelay(dll_wait_time_in_us);
-
-err_out:
- return;
-}
-
-void gpmi_end(struct gpmi_nand_data *this)
-{
- gpmi_disable_clk(this);
-}
-
-/* Clears a BCH interrupt. */
-void gpmi_clear_bch(struct gpmi_nand_data *this)
-{
- struct resources *r = &this->resources;
- writel(BM_BCH_CTRL_COMPLETE_IRQ, r->bch_regs + HW_BCH_CTRL_CLR);
-}
-
-/* Returns the Ready/Busy status of the given chip. */
-int gpmi_is_ready(struct gpmi_nand_data *this, unsigned chip)
-{
- struct resources *r = &this->resources;
- uint32_t mask = 0;
- uint32_t reg = 0;
-
- if (GPMI_IS_MX23(this)) {
- mask = MX23_BM_GPMI_DEBUG_READY0 << chip;
- reg = readl(r->gpmi_regs + HW_GPMI_DEBUG);
- } else if (GPMI_IS_MX28(this) || GPMI_IS_MX6(this)) {
- /*
- * In the imx6, all the ready/busy pins are bound
- * together. So we only need to check chip 0.
- */
- if (GPMI_IS_MX6(this))
- chip = 0;
-
- /* MX28 shares the same R/B register as MX6Q. */
- mask = MX28_BF_GPMI_STAT_READY_BUSY(1 << chip);
- reg = readl(r->gpmi_regs + HW_GPMI_STAT);
- } else
- dev_err(this->dev, "unknown arch.\n");
- return reg & mask;
-}
-
-static inline void set_dma_type(struct gpmi_nand_data *this,
- enum dma_ops_type type)
-{
- this->last_dma_type = this->dma_type;
- this->dma_type = type;
-}
-
-int gpmi_send_command(struct gpmi_nand_data *this)
-{
- struct dma_chan *channel = get_dma_chan(this);
- struct dma_async_tx_descriptor *desc;
- struct scatterlist *sgl;
- int chip = this->current_chip;
- u32 pio[3];
-
- /* [1] send out the PIO words */
- pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__WRITE)
- | BM_GPMI_CTRL0_WORD_LENGTH
- | BF_GPMI_CTRL0_CS(chip, this)
- | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
- | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_CLE)
- | BM_GPMI_CTRL0_ADDRESS_INCREMENT
- | BF_GPMI_CTRL0_XFER_COUNT(this->command_length);
- pio[1] = pio[2] = 0;
- desc = dmaengine_prep_slave_sg(channel,
- (struct scatterlist *)pio,
- ARRAY_SIZE(pio), DMA_TRANS_NONE, 0);
- if (!desc)
- return -EINVAL;
-
- /* [2] send out the COMMAND + ADDRESS string stored in @buffer */
- sgl = &this->cmd_sgl;
-
- sg_init_one(sgl, this->cmd_buffer, this->command_length);
- dma_map_sg(this->dev, sgl, 1, DMA_TO_DEVICE);
- desc = dmaengine_prep_slave_sg(channel,
- sgl, 1, DMA_MEM_TO_DEV,
- DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
- if (!desc)
- return -EINVAL;
-
- /* [3] submit the DMA */
- set_dma_type(this, DMA_FOR_COMMAND);
- return start_dma_without_bch_irq(this, desc);
-}
-
-int gpmi_send_data(struct gpmi_nand_data *this)
-{
- struct dma_async_tx_descriptor *desc;
- struct dma_chan *channel = get_dma_chan(this);
- int chip = this->current_chip;
- uint32_t command_mode;
- uint32_t address;
- u32 pio[2];
-
- /* [1] PIO */
- command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE;
- address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
-
- pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode)
- | BM_GPMI_CTRL0_WORD_LENGTH
- | BF_GPMI_CTRL0_CS(chip, this)
- | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
- | BF_GPMI_CTRL0_ADDRESS(address)
- | BF_GPMI_CTRL0_XFER_COUNT(this->upper_len);
- pio[1] = 0;
- desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio,
- ARRAY_SIZE(pio), DMA_TRANS_NONE, 0);
- if (!desc)
- return -EINVAL;
-
- /* [2] send DMA request */
- prepare_data_dma(this, DMA_TO_DEVICE);
- desc = dmaengine_prep_slave_sg(channel, &this->data_sgl,
- 1, DMA_MEM_TO_DEV,
- DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
- if (!desc)
- return -EINVAL;
-
- /* [3] submit the DMA */
- set_dma_type(this, DMA_FOR_WRITE_DATA);
- return start_dma_without_bch_irq(this, desc);
-}
-
-int gpmi_read_data(struct gpmi_nand_data *this)
-{
- struct dma_async_tx_descriptor *desc;
- struct dma_chan *channel = get_dma_chan(this);
- int chip = this->current_chip;
- u32 pio[2];
-
- /* [1] : send PIO */
- pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__READ)
- | BM_GPMI_CTRL0_WORD_LENGTH
- | BF_GPMI_CTRL0_CS(chip, this)
- | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
- | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA)
- | BF_GPMI_CTRL0_XFER_COUNT(this->upper_len);
- pio[1] = 0;
- desc = dmaengine_prep_slave_sg(channel,
- (struct scatterlist *)pio,
- ARRAY_SIZE(pio), DMA_TRANS_NONE, 0);
- if (!desc)
- return -EINVAL;
-
- /* [2] : send DMA request */
- prepare_data_dma(this, DMA_FROM_DEVICE);
- desc = dmaengine_prep_slave_sg(channel, &this->data_sgl,
- 1, DMA_DEV_TO_MEM,
- DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
- if (!desc)
- return -EINVAL;
-
- /* [3] : submit the DMA */
- set_dma_type(this, DMA_FOR_READ_DATA);
- return start_dma_without_bch_irq(this, desc);
-}
-
-int gpmi_send_page(struct gpmi_nand_data *this,
- dma_addr_t payload, dma_addr_t auxiliary)
-{
- struct bch_geometry *geo = &this->bch_geometry;
- uint32_t command_mode;
- uint32_t address;
- uint32_t ecc_command;
- uint32_t buffer_mask;
- struct dma_async_tx_descriptor *desc;
- struct dma_chan *channel = get_dma_chan(this);
- int chip = this->current_chip;
- u32 pio[6];
-
- /* A DMA descriptor that does an ECC page read. */
- command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE;
- address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
- ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE;
- buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE |
- BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY;
-
- pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode)
- | BM_GPMI_CTRL0_WORD_LENGTH
- | BF_GPMI_CTRL0_CS(chip, this)
- | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
- | BF_GPMI_CTRL0_ADDRESS(address)
- | BF_GPMI_CTRL0_XFER_COUNT(0);
- pio[1] = 0;
- pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC
- | BF_GPMI_ECCCTRL_ECC_CMD(ecc_command)
- | BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask);
- pio[3] = geo->page_size;
- pio[4] = payload;
- pio[5] = auxiliary;
-
- desc = dmaengine_prep_slave_sg(channel,
- (struct scatterlist *)pio,
- ARRAY_SIZE(pio), DMA_TRANS_NONE,
- DMA_CTRL_ACK);
- if (!desc)
- return -EINVAL;
-
- set_dma_type(this, DMA_FOR_WRITE_ECC_PAGE);
- return start_dma_with_bch_irq(this, desc);
-}
-
-int gpmi_read_page(struct gpmi_nand_data *this,
- dma_addr_t payload, dma_addr_t auxiliary)
-{
- struct bch_geometry *geo = &this->bch_geometry;
- uint32_t command_mode;
- uint32_t address;
- uint32_t ecc_command;
- uint32_t buffer_mask;
- struct dma_async_tx_descriptor *desc;
- struct dma_chan *channel = get_dma_chan(this);
- int chip = this->current_chip;
- u32 pio[6];
-
- /* [1] Wait for the chip to report ready. */
- command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY;
- address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
-
- pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode)
- | BM_GPMI_CTRL0_WORD_LENGTH
- | BF_GPMI_CTRL0_CS(chip, this)
- | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
- | BF_GPMI_CTRL0_ADDRESS(address)
- | BF_GPMI_CTRL0_XFER_COUNT(0);
- pio[1] = 0;
- desc = dmaengine_prep_slave_sg(channel,
- (struct scatterlist *)pio, 2,
- DMA_TRANS_NONE, 0);
- if (!desc)
- return -EINVAL;
-
- /* [2] Enable the BCH block and read. */
- command_mode = BV_GPMI_CTRL0_COMMAND_MODE__READ;
- address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
- ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE;
- buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE
- | BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY;
-
- pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode)
- | BM_GPMI_CTRL0_WORD_LENGTH
- | BF_GPMI_CTRL0_CS(chip, this)
- | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
- | BF_GPMI_CTRL0_ADDRESS(address)
- | BF_GPMI_CTRL0_XFER_COUNT(geo->page_size);
-
- pio[1] = 0;
- pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC
- | BF_GPMI_ECCCTRL_ECC_CMD(ecc_command)
- | BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask);
- pio[3] = geo->page_size;
- pio[4] = payload;
- pio[5] = auxiliary;
- desc = dmaengine_prep_slave_sg(channel,
- (struct scatterlist *)pio,
- ARRAY_SIZE(pio), DMA_TRANS_NONE,
- DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
- if (!desc)
- return -EINVAL;
-
- /* [3] Disable the BCH block */
- command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY;
- address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
-
- pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode)
- | BM_GPMI_CTRL0_WORD_LENGTH
- | BF_GPMI_CTRL0_CS(chip, this)
- | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
- | BF_GPMI_CTRL0_ADDRESS(address)
- | BF_GPMI_CTRL0_XFER_COUNT(geo->page_size);
- pio[1] = 0;
- pio[2] = 0; /* clear GPMI_HW_GPMI_ECCCTRL, disable the BCH. */
- desc = dmaengine_prep_slave_sg(channel,
- (struct scatterlist *)pio, 3,
- DMA_TRANS_NONE,
- DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
- if (!desc)
- return -EINVAL;
-
- /* [4] submit the DMA */
- set_dma_type(this, DMA_FOR_READ_ECC_PAGE);
- return start_dma_with_bch_irq(this, desc);
-}
-
-/**
- * gpmi_copy_bits - copy bits from one memory region to another
- * @dst: destination buffer
- * @dst_bit_off: bit offset we're starting to write at
- * @src: source buffer
- * @src_bit_off: bit offset we're starting to read from
- * @nbits: number of bits to copy
- *
- * This functions copies bits from one memory region to another, and is used by
- * the GPMI driver to copy ECC sections which are not guaranteed to be byte
- * aligned.
- *
- * src and dst should not overlap.
- *
- */
-void gpmi_copy_bits(u8 *dst, size_t dst_bit_off,
- const u8 *src, size_t src_bit_off,
- size_t nbits)
-{
- size_t i;
- size_t nbytes;
- u32 src_buffer = 0;
- size_t bits_in_src_buffer = 0;
-
- if (!nbits)
- return;
-
- /*
- * Move src and dst pointers to the closest byte pointer and store bit
- * offsets within a byte.
- */
- src += src_bit_off / 8;
- src_bit_off %= 8;
-
- dst += dst_bit_off / 8;
- dst_bit_off %= 8;
-
- /*
- * Initialize the src_buffer value with bits available in the first
- * byte of data so that we end up with a byte aligned src pointer.
- */
- if (src_bit_off) {
- src_buffer = src[0] >> src_bit_off;
- if (nbits >= (8 - src_bit_off)) {
- bits_in_src_buffer += 8 - src_bit_off;
- } else {
- src_buffer &= GENMASK(nbits - 1, 0);
- bits_in_src_buffer += nbits;
- }
- nbits -= bits_in_src_buffer;
- src++;
- }
-
- /* Calculate the number of bytes that can be copied from src to dst. */
- nbytes = nbits / 8;
-
- /* Try to align dst to a byte boundary. */
- if (dst_bit_off) {
- if (bits_in_src_buffer < (8 - dst_bit_off) && nbytes) {
- src_buffer |= src[0] << bits_in_src_buffer;
- bits_in_src_buffer += 8;
- src++;
- nbytes--;
- }
-
- if (bits_in_src_buffer >= (8 - dst_bit_off)) {
- dst[0] &= GENMASK(dst_bit_off - 1, 0);
- dst[0] |= src_buffer << dst_bit_off;
- src_buffer >>= (8 - dst_bit_off);
- bits_in_src_buffer -= (8 - dst_bit_off);
- dst_bit_off = 0;
- dst++;
- if (bits_in_src_buffer > 7) {
- bits_in_src_buffer -= 8;
- dst[0] = src_buffer;
- dst++;
- src_buffer >>= 8;
- }
- }
- }
-
- if (!bits_in_src_buffer && !dst_bit_off) {
- /*
- * Both src and dst pointers are byte aligned, thus we can
- * just use the optimized memcpy function.
- */
- if (nbytes)
- memcpy(dst, src, nbytes);
- } else {
- /*
- * src buffer is not byte aligned, hence we have to copy each
- * src byte to the src_buffer variable before extracting a byte
- * to store in dst.
- */
- for (i = 0; i < nbytes; i++) {
- src_buffer |= src[i] << bits_in_src_buffer;
- dst[i] = src_buffer;
- src_buffer >>= 8;
- }
- }
- /* Update dst and src pointers */
- dst += nbytes;
- src += nbytes;
-
- /*
- * nbits is the number of remaining bits. It should not exceed 8 as
- * we've already copied as much bytes as possible.
- */
- nbits %= 8;
-
- /*
- * If there's no more bits to copy to the destination and src buffer
- * was already byte aligned, then we're done.
- */
- if (!nbits && !bits_in_src_buffer)
- return;
-
- /* Copy the remaining bits to src_buffer */
- if (nbits)
- src_buffer |= (*src & GENMASK(nbits - 1, 0)) <<
- bits_in_src_buffer;
- bits_in_src_buffer += nbits;
-
- /*
- * In case there were not enough bits to get a byte aligned dst buffer
- * prepare the src_buffer variable to match the dst organization (shift
- * src_buffer by dst_bit_off and retrieve the least significant bits
- * from dst).
- */
- if (dst_bit_off)
- src_buffer = (src_buffer << dst_bit_off) |
- (*dst & GENMASK(dst_bit_off - 1, 0));
- bits_in_src_buffer += dst_bit_off;
-
- /*
- * Keep most significant bits from dst if we end up with an unaligned
- * number of bits.
- */
- nbytes = bits_in_src_buffer / 8;
- if (bits_in_src_buffer % 8) {
- src_buffer |= (dst[nbytes] &
- GENMASK(7, bits_in_src_buffer % 8)) <<
- (nbytes * 8);
- nbytes++;
- }
-
- /* Copy the remaining bytes to dst */
- for (i = 0; i < nbytes; i++) {
- dst[i] = src_buffer;
- src_buffer >>= 8;
- }
-}
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
deleted file mode 100644
index 61fdd733492f..000000000000
--- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
+++ /dev/null
@@ -1,2182 +0,0 @@
-/*
- * Freescale GPMI NAND Flash Driver
- *
- * Copyright (C) 2010-2015 Freescale Semiconductor, Inc.
- * Copyright (C) 2008 Embedded Alley Solutions, Inc.
- *
- * 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/clk.h>
-#include <linux/slab.h>
-#include <linux/sched/task_stack.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/mtd/partitions.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include "gpmi-nand.h"
-#include "bch-regs.h"
-
-/* Resource names for the GPMI NAND driver. */
-#define GPMI_NAND_GPMI_REGS_ADDR_RES_NAME "gpmi-nand"
-#define GPMI_NAND_BCH_REGS_ADDR_RES_NAME "bch"
-#define GPMI_NAND_BCH_INTERRUPT_RES_NAME "bch"
-
-/* add our owner bbt descriptor */
-static uint8_t scan_ff_pattern[] = { 0xff };
-static struct nand_bbt_descr gpmi_bbt_descr = {
- .options = 0,
- .offs = 0,
- .len = 1,
- .pattern = scan_ff_pattern
-};
-
-/*
- * We may change the layout if we can get the ECC info from the datasheet,
- * else we will use all the (page + OOB).
- */
-static int gpmi_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct gpmi_nand_data *this = nand_get_controller_data(chip);
- struct bch_geometry *geo = &this->bch_geometry;
-
- if (section)
- return -ERANGE;
-
- oobregion->offset = 0;
- oobregion->length = geo->page_size - mtd->writesize;
-
- return 0;
-}
-
-static int gpmi_ooblayout_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct gpmi_nand_data *this = nand_get_controller_data(chip);
- struct bch_geometry *geo = &this->bch_geometry;
-
- if (section)
- return -ERANGE;
-
- /* The available oob size we have. */
- if (geo->page_size < mtd->writesize + mtd->oobsize) {
- oobregion->offset = geo->page_size - mtd->writesize;
- oobregion->length = mtd->oobsize - oobregion->offset;
- }
-
- return 0;
-}
-
-static const char * const gpmi_clks_for_mx2x[] = {
- "gpmi_io",
-};
-
-static const struct mtd_ooblayout_ops gpmi_ooblayout_ops = {
- .ecc = gpmi_ooblayout_ecc,
- .free = gpmi_ooblayout_free,
-};
-
-static const struct gpmi_devdata gpmi_devdata_imx23 = {
- .type = IS_MX23,
- .bch_max_ecc_strength = 20,
- .max_chain_delay = 16,
- .clks = gpmi_clks_for_mx2x,
- .clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x),
-};
-
-static const struct gpmi_devdata gpmi_devdata_imx28 = {
- .type = IS_MX28,
- .bch_max_ecc_strength = 20,
- .max_chain_delay = 16,
- .clks = gpmi_clks_for_mx2x,
- .clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x),
-};
-
-static const char * const gpmi_clks_for_mx6[] = {
- "gpmi_io", "gpmi_apb", "gpmi_bch", "gpmi_bch_apb", "per1_bch",
-};
-
-static const struct gpmi_devdata gpmi_devdata_imx6q = {
- .type = IS_MX6Q,
- .bch_max_ecc_strength = 40,
- .max_chain_delay = 12,
- .clks = gpmi_clks_for_mx6,
- .clks_count = ARRAY_SIZE(gpmi_clks_for_mx6),
-};
-
-static const struct gpmi_devdata gpmi_devdata_imx6sx = {
- .type = IS_MX6SX,
- .bch_max_ecc_strength = 62,
- .max_chain_delay = 12,
- .clks = gpmi_clks_for_mx6,
- .clks_count = ARRAY_SIZE(gpmi_clks_for_mx6),
-};
-
-static const char * const gpmi_clks_for_mx7d[] = {
- "gpmi_io", "gpmi_bch_apb",
-};
-
-static const struct gpmi_devdata gpmi_devdata_imx7d = {
- .type = IS_MX7D,
- .bch_max_ecc_strength = 62,
- .max_chain_delay = 12,
- .clks = gpmi_clks_for_mx7d,
- .clks_count = ARRAY_SIZE(gpmi_clks_for_mx7d),
-};
-
-static irqreturn_t bch_irq(int irq, void *cookie)
-{
- struct gpmi_nand_data *this = cookie;
-
- gpmi_clear_bch(this);
- complete(&this->bch_done);
- return IRQ_HANDLED;
-}
-
-/*
- * Calculate the ECC strength by hand:
- * E : The ECC strength.
- * G : the length of Galois Field.
- * N : The chunk count of per page.
- * O : the oobsize of the NAND chip.
- * M : the metasize of per page.
- *
- * The formula is :
- * E * G * N
- * ------------ <= (O - M)
- * 8
- *
- * So, we get E by:
- * (O - M) * 8
- * E <= -------------
- * G * N
- */
-static inline int get_ecc_strength(struct gpmi_nand_data *this)
-{
- struct bch_geometry *geo = &this->bch_geometry;
- struct mtd_info *mtd = nand_to_mtd(&this->nand);
- int ecc_strength;
-
- ecc_strength = ((mtd->oobsize - geo->metadata_size) * 8)
- / (geo->gf_len * geo->ecc_chunk_count);
-
- /* We need the minor even number. */
- return round_down(ecc_strength, 2);
-}
-
-static inline bool gpmi_check_ecc(struct gpmi_nand_data *this)
-{
- struct bch_geometry *geo = &this->bch_geometry;
-
- /* Do the sanity check. */
- if (GPMI_IS_MX23(this) || GPMI_IS_MX28(this)) {
- /* The mx23/mx28 only support the GF13. */
- if (geo->gf_len == 14)
- return false;
- }
- return geo->ecc_strength <= this->devdata->bch_max_ecc_strength;
-}
-
-/*
- * If we can get the ECC information from the nand chip, we do not
- * need to calculate them ourselves.
- *
- * We may have available oob space in this case.
- */
-static int set_geometry_by_ecc_info(struct gpmi_nand_data *this)
-{
- struct bch_geometry *geo = &this->bch_geometry;
- struct nand_chip *chip = &this->nand;
- struct mtd_info *mtd = nand_to_mtd(chip);
- unsigned int block_mark_bit_offset;
-
- if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0))
- return -EINVAL;
-
- switch (chip->ecc_step_ds) {
- case SZ_512:
- geo->gf_len = 13;
- break;
- case SZ_1K:
- geo->gf_len = 14;
- break;
- default:
- dev_err(this->dev,
- "unsupported nand chip. ecc bits : %d, ecc size : %d\n",
- chip->ecc_strength_ds, chip->ecc_step_ds);
- return -EINVAL;
- }
- geo->ecc_chunk_size = chip->ecc_step_ds;
- geo->ecc_strength = round_up(chip->ecc_strength_ds, 2);
- if (!gpmi_check_ecc(this))
- return -EINVAL;
-
- /* Keep the C >= O */
- if (geo->ecc_chunk_size < mtd->oobsize) {
- dev_err(this->dev,
- "unsupported nand chip. ecc size: %d, oob size : %d\n",
- chip->ecc_step_ds, mtd->oobsize);
- return -EINVAL;
- }
-
- /* The default value, see comment in the legacy_set_geometry(). */
- geo->metadata_size = 10;
-
- geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size;
-
- /*
- * Now, the NAND chip with 2K page(data chunk is 512byte) shows below:
- *
- * | P |
- * |<----------------------------------------------------->|
- * | |
- * | (Block Mark) |
- * | P' | | | |
- * |<-------------------------------------------->| D | | O' |
- * | |<---->| |<--->|
- * V V V V V
- * +---+----------+-+----------+-+----------+-+----------+-+-----+
- * | M | data |E| data |E| data |E| data |E| |
- * +---+----------+-+----------+-+----------+-+----------+-+-----+
- * ^ ^
- * | O |
- * |<------------>|
- * | |
- *
- * P : the page size for BCH module.
- * E : The ECC strength.
- * G : the length of Galois Field.
- * N : The chunk count of per page.
- * M : the metasize of per page.
- * C : the ecc chunk size, aka the "data" above.
- * P': the nand chip's page size.
- * O : the nand chip's oob size.
- * O': the free oob.
- *
- * The formula for P is :
- *
- * E * G * N
- * P = ------------ + P' + M
- * 8
- *
- * The position of block mark moves forward in the ECC-based view
- * of page, and the delta is:
- *
- * E * G * (N - 1)
- * D = (---------------- + M)
- * 8
- *
- * Please see the comment in legacy_set_geometry().
- * With the condition C >= O , we still can get same result.
- * So the bit position of the physical block mark within the ECC-based
- * view of the page is :
- * (P' - D) * 8
- */
- geo->page_size = mtd->writesize + geo->metadata_size +
- (geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8;
-
- geo->payload_size = mtd->writesize;
-
- geo->auxiliary_status_offset = ALIGN(geo->metadata_size, 4);
- geo->auxiliary_size = ALIGN(geo->metadata_size, 4)
- + ALIGN(geo->ecc_chunk_count, 4);
-
- if (!this->swap_block_mark)
- return 0;
-
- /* For bit swap. */
- block_mark_bit_offset = mtd->writesize * 8 -
- (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - 1)
- + geo->metadata_size * 8);
-
- geo->block_mark_byte_offset = block_mark_bit_offset / 8;
- geo->block_mark_bit_offset = block_mark_bit_offset % 8;
- return 0;
-}
-
-static int legacy_set_geometry(struct gpmi_nand_data *this)
-{
- struct bch_geometry *geo = &this->bch_geometry;
- struct mtd_info *mtd = nand_to_mtd(&this->nand);
- unsigned int metadata_size;
- unsigned int status_size;
- unsigned int block_mark_bit_offset;
-
- /*
- * The size of the metadata can be changed, though we set it to 10
- * bytes now. But it can't be too large, because we have to save
- * enough space for BCH.
- */
- geo->metadata_size = 10;
-
- /* The default for the length of Galois Field. */
- geo->gf_len = 13;
-
- /* The default for chunk size. */
- geo->ecc_chunk_size = 512;
- while (geo->ecc_chunk_size < mtd->oobsize) {
- geo->ecc_chunk_size *= 2; /* keep C >= O */
- geo->gf_len = 14;
- }
-
- geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size;
-
- /* We use the same ECC strength for all chunks. */
- geo->ecc_strength = get_ecc_strength(this);
- if (!gpmi_check_ecc(this)) {
- dev_err(this->dev,
- "ecc strength: %d cannot be supported by the controller (%d)\n"
- "try to use minimum ecc strength that NAND chip required\n",
- geo->ecc_strength,
- this->devdata->bch_max_ecc_strength);
- return -EINVAL;
- }
-
- geo->page_size = mtd->writesize + geo->metadata_size +
- (geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8;
- geo->payload_size = mtd->writesize;
-
- /*
- * The auxiliary buffer contains the metadata and the ECC status. The
- * metadata is padded to the nearest 32-bit boundary. The ECC status
- * contains one byte for every ECC chunk, and is also padded to the
- * nearest 32-bit boundary.
- */
- metadata_size = ALIGN(geo->metadata_size, 4);
- status_size = ALIGN(geo->ecc_chunk_count, 4);
-
- geo->auxiliary_size = metadata_size + status_size;
- geo->auxiliary_status_offset = metadata_size;
-
- if (!this->swap_block_mark)
- return 0;
-
- /*
- * We need to compute the byte and bit offsets of
- * the physical block mark within the ECC-based view of the page.
- *
- * NAND chip with 2K page shows below:
- * (Block Mark)
- * | |
- * | D |
- * |<---->|
- * V V
- * +---+----------+-+----------+-+----------+-+----------+-+
- * | M | data |E| data |E| data |E| data |E|
- * +---+----------+-+----------+-+----------+-+----------+-+
- *
- * The position of block mark moves forward in the ECC-based view
- * of page, and the delta is:
- *
- * E * G * (N - 1)
- * D = (---------------- + M)
- * 8
- *
- * With the formula to compute the ECC strength, and the condition
- * : C >= O (C is the ecc chunk size)
- *
- * It's easy to deduce to the following result:
- *
- * E * G (O - M) C - M C - M
- * ----------- <= ------- <= -------- < ---------
- * 8 N N (N - 1)
- *
- * So, we get:
- *
- * E * G * (N - 1)
- * D = (---------------- + M) < C
- * 8
- *
- * The above inequality means the position of block mark
- * within the ECC-based view of the page is still in the data chunk,
- * and it's NOT in the ECC bits of the chunk.
- *
- * Use the following to compute the bit position of the
- * physical block mark within the ECC-based view of the page:
- * (page_size - D) * 8
- *
- * --Huang Shijie
- */
- block_mark_bit_offset = mtd->writesize * 8 -
- (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - 1)
- + geo->metadata_size * 8);
-
- geo->block_mark_byte_offset = block_mark_bit_offset / 8;
- geo->block_mark_bit_offset = block_mark_bit_offset % 8;
- return 0;
-}
-
-int common_nfc_set_geometry(struct gpmi_nand_data *this)
-{
- if ((of_property_read_bool(this->dev->of_node, "fsl,use-minimum-ecc"))
- || legacy_set_geometry(this))
- return set_geometry_by_ecc_info(this);
-
- return 0;
-}
-
-struct dma_chan *get_dma_chan(struct gpmi_nand_data *this)
-{
- /* We use the DMA channel 0 to access all the nand chips. */
- return this->dma_chans[0];
-}
-
-/* Can we use the upper's buffer directly for DMA? */
-void prepare_data_dma(struct gpmi_nand_data *this, enum dma_data_direction dr)
-{
- struct scatterlist *sgl = &this->data_sgl;
- int ret;
-
- /* first try to map the upper buffer directly */
- if (virt_addr_valid(this->upper_buf) &&
- !object_is_on_stack(this->upper_buf)) {
- sg_init_one(sgl, this->upper_buf, this->upper_len);
- ret = dma_map_sg(this->dev, sgl, 1, dr);
- if (ret == 0)
- goto map_fail;
-
- this->direct_dma_map_ok = true;
- return;
- }
-
-map_fail:
- /* We have to use our own DMA buffer. */
- sg_init_one(sgl, this->data_buffer_dma, this->upper_len);
-
- if (dr == DMA_TO_DEVICE)
- memcpy(this->data_buffer_dma, this->upper_buf, this->upper_len);
-
- dma_map_sg(this->dev, sgl, 1, dr);
-
- this->direct_dma_map_ok = false;
-}
-
-/* This will be called after the DMA operation is finished. */
-static void dma_irq_callback(void *param)
-{
- struct gpmi_nand_data *this = param;
- struct completion *dma_c = &this->dma_done;
-
- switch (this->dma_type) {
- case DMA_FOR_COMMAND:
- dma_unmap_sg(this->dev, &this->cmd_sgl, 1, DMA_TO_DEVICE);
- break;
-
- case DMA_FOR_READ_DATA:
- dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_FROM_DEVICE);
- if (this->direct_dma_map_ok == false)
- memcpy(this->upper_buf, this->data_buffer_dma,
- this->upper_len);
- break;
-
- case DMA_FOR_WRITE_DATA:
- dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_TO_DEVICE);
- break;
-
- case DMA_FOR_READ_ECC_PAGE:
- case DMA_FOR_WRITE_ECC_PAGE:
- /* We have to wait the BCH interrupt to finish. */
- break;
-
- default:
- dev_err(this->dev, "in wrong DMA operation.\n");
- }
-
- complete(dma_c);
-}
-
-int start_dma_without_bch_irq(struct gpmi_nand_data *this,
- struct dma_async_tx_descriptor *desc)
-{
- struct completion *dma_c = &this->dma_done;
- unsigned long timeout;
-
- init_completion(dma_c);
-
- desc->callback = dma_irq_callback;
- desc->callback_param = this;
- dmaengine_submit(desc);
- dma_async_issue_pending(get_dma_chan(this));
-
- /* Wait for the interrupt from the DMA block. */
- timeout = wait_for_completion_timeout(dma_c, msecs_to_jiffies(1000));
- if (!timeout) {
- dev_err(this->dev, "DMA timeout, last DMA :%d\n",
- this->last_dma_type);
- gpmi_dump_info(this);
- return -ETIMEDOUT;
- }
- return 0;
-}
-
-/*
- * This function is used in BCH reading or BCH writing pages.
- * It will wait for the BCH interrupt as long as ONE second.
- * Actually, we must wait for two interrupts :
- * [1] firstly the DMA interrupt and
- * [2] secondly the BCH interrupt.
- */
-int start_dma_with_bch_irq(struct gpmi_nand_data *this,
- struct dma_async_tx_descriptor *desc)
-{
- struct completion *bch_c = &this->bch_done;
- unsigned long timeout;
-
- /* Prepare to receive an interrupt from the BCH block. */
- init_completion(bch_c);
-
- /* start the DMA */
- start_dma_without_bch_irq(this, desc);
-
- /* Wait for the interrupt from the BCH block. */
- timeout = wait_for_completion_timeout(bch_c, msecs_to_jiffies(1000));
- if (!timeout) {
- dev_err(this->dev, "BCH timeout, last DMA :%d\n",
- this->last_dma_type);
- gpmi_dump_info(this);
- return -ETIMEDOUT;
- }
- return 0;
-}
-
-static int acquire_register_block(struct gpmi_nand_data *this,
- const char *res_name)
-{
- struct platform_device *pdev = this->pdev;
- struct resources *res = &this->resources;
- struct resource *r;
- void __iomem *p;
-
- r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res_name);
- p = devm_ioremap_resource(&pdev->dev, r);
- if (IS_ERR(p))
- return PTR_ERR(p);
-
- if (!strcmp(res_name, GPMI_NAND_GPMI_REGS_ADDR_RES_NAME))
- res->gpmi_regs = p;
- else if (!strcmp(res_name, GPMI_NAND_BCH_REGS_ADDR_RES_NAME))
- res->bch_regs = p;
- else
- dev_err(this->dev, "unknown resource name : %s\n", res_name);
-
- return 0;
-}
-
-static int acquire_bch_irq(struct gpmi_nand_data *this, irq_handler_t irq_h)
-{
- struct platform_device *pdev = this->pdev;
- const char *res_name = GPMI_NAND_BCH_INTERRUPT_RES_NAME;
- struct resource *r;
- int err;
-
- r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, res_name);
- if (!r) {
- dev_err(this->dev, "Can't get resource for %s\n", res_name);
- return -ENODEV;
- }
-
- err = devm_request_irq(this->dev, r->start, irq_h, 0, res_name, this);
- if (err)
- dev_err(this->dev, "error requesting BCH IRQ\n");
-
- return err;
-}
-
-static void release_dma_channels(struct gpmi_nand_data *this)
-{
- unsigned int i;
- for (i = 0; i < DMA_CHANS; i++)
- if (this->dma_chans[i]) {
- dma_release_channel(this->dma_chans[i]);
- this->dma_chans[i] = NULL;
- }
-}
-
-static int acquire_dma_channels(struct gpmi_nand_data *this)
-{
- struct platform_device *pdev = this->pdev;
- struct dma_chan *dma_chan;
-
- /* request dma channel */
- dma_chan = dma_request_slave_channel(&pdev->dev, "rx-tx");
- if (!dma_chan) {
- dev_err(this->dev, "Failed to request DMA channel.\n");
- goto acquire_err;
- }
-
- this->dma_chans[0] = dma_chan;
- return 0;
-
-acquire_err:
- release_dma_channels(this);
- return -EINVAL;
-}
-
-static int gpmi_get_clks(struct gpmi_nand_data *this)
-{
- struct resources *r = &this->resources;
- struct clk *clk;
- int err, i;
-
- for (i = 0; i < this->devdata->clks_count; i++) {
- clk = devm_clk_get(this->dev, this->devdata->clks[i]);
- if (IS_ERR(clk)) {
- err = PTR_ERR(clk);
- goto err_clock;
- }
-
- r->clock[i] = clk;
- }
-
- if (GPMI_IS_MX6(this))
- /*
- * Set the default value for the gpmi clock.
- *
- * If you want to use the ONFI nand which is in the
- * Synchronous Mode, you should change the clock as you need.
- */
- clk_set_rate(r->clock[0], 22000000);
-
- return 0;
-
-err_clock:
- dev_dbg(this->dev, "failed in finding the clocks.\n");
- return err;
-}
-
-static int acquire_resources(struct gpmi_nand_data *this)
-{
- int ret;
-
- ret = acquire_register_block(this, GPMI_NAND_GPMI_REGS_ADDR_RES_NAME);
- if (ret)
- goto exit_regs;
-
- ret = acquire_register_block(this, GPMI_NAND_BCH_REGS_ADDR_RES_NAME);
- if (ret)
- goto exit_regs;
-
- ret = acquire_bch_irq(this, bch_irq);
- if (ret)
- goto exit_regs;
-
- ret = acquire_dma_channels(this);
- if (ret)
- goto exit_regs;
-
- ret = gpmi_get_clks(this);
- if (ret)
- goto exit_clock;
- return 0;
-
-exit_clock:
- release_dma_channels(this);
-exit_regs:
- return ret;
-}
-
-static void release_resources(struct gpmi_nand_data *this)
-{
- release_dma_channels(this);
-}
-
-static int init_hardware(struct gpmi_nand_data *this)
-{
- int ret;
-
- /*
- * This structure contains the "safe" GPMI timing that should succeed
- * with any NAND Flash device
- * (although, with less-than-optimal performance).
- */
- struct nand_timing safe_timing = {
- .data_setup_in_ns = 80,
- .data_hold_in_ns = 60,
- .address_setup_in_ns = 25,
- .gpmi_sample_delay_in_ns = 6,
- .tREA_in_ns = -1,
- .tRLOH_in_ns = -1,
- .tRHOH_in_ns = -1,
- };
-
- /* Initialize the hardwares. */
- ret = gpmi_init(this);
- if (ret)
- return ret;
-
- this->timing = safe_timing;
- return 0;
-}
-
-static int read_page_prepare(struct gpmi_nand_data *this,
- void *destination, unsigned length,
- void *alt_virt, dma_addr_t alt_phys, unsigned alt_size,
- void **use_virt, dma_addr_t *use_phys)
-{
- struct device *dev = this->dev;
-
- if (virt_addr_valid(destination)) {
- dma_addr_t dest_phys;
-
- dest_phys = dma_map_single(dev, destination,
- length, DMA_FROM_DEVICE);
- if (dma_mapping_error(dev, dest_phys)) {
- if (alt_size < length) {
- dev_err(dev, "Alternate buffer is too small\n");
- return -ENOMEM;
- }
- goto map_failed;
- }
- *use_virt = destination;
- *use_phys = dest_phys;
- this->direct_dma_map_ok = true;
- return 0;
- }
-
-map_failed:
- *use_virt = alt_virt;
- *use_phys = alt_phys;
- this->direct_dma_map_ok = false;
- return 0;
-}
-
-static inline void read_page_end(struct gpmi_nand_data *this,
- void *destination, unsigned length,
- void *alt_virt, dma_addr_t alt_phys, unsigned alt_size,
- void *used_virt, dma_addr_t used_phys)
-{
- if (this->direct_dma_map_ok)
- dma_unmap_single(this->dev, used_phys, length, DMA_FROM_DEVICE);
-}
-
-static inline void read_page_swap_end(struct gpmi_nand_data *this,
- void *destination, unsigned length,
- void *alt_virt, dma_addr_t alt_phys, unsigned alt_size,
- void *used_virt, dma_addr_t used_phys)
-{
- if (!this->direct_dma_map_ok)
- memcpy(destination, alt_virt, length);
-}
-
-static int send_page_prepare(struct gpmi_nand_data *this,
- const void *source, unsigned length,
- void *alt_virt, dma_addr_t alt_phys, unsigned alt_size,
- const void **use_virt, dma_addr_t *use_phys)
-{
- struct device *dev = this->dev;
-
- if (virt_addr_valid(source)) {
- dma_addr_t source_phys;
-
- source_phys = dma_map_single(dev, (void *)source, length,
- DMA_TO_DEVICE);
- if (dma_mapping_error(dev, source_phys)) {
- if (alt_size < length) {
- dev_err(dev, "Alternate buffer is too small\n");
- return -ENOMEM;
- }
- goto map_failed;
- }
- *use_virt = source;
- *use_phys = source_phys;
- return 0;
- }
-map_failed:
- /*
- * Copy the content of the source buffer into the alternate
- * buffer and set up the return values accordingly.
- */
- memcpy(alt_virt, source, length);
-
- *use_virt = alt_virt;
- *use_phys = alt_phys;
- return 0;
-}
-
-static void send_page_end(struct gpmi_nand_data *this,
- const void *source, unsigned length,
- void *alt_virt, dma_addr_t alt_phys, unsigned alt_size,
- const void *used_virt, dma_addr_t used_phys)
-{
- struct device *dev = this->dev;
- if (used_virt == source)
- dma_unmap_single(dev, used_phys, length, DMA_TO_DEVICE);
-}
-
-static void gpmi_free_dma_buffer(struct gpmi_nand_data *this)
-{
- struct device *dev = this->dev;
-
- if (this->page_buffer_virt && virt_addr_valid(this->page_buffer_virt))
- dma_free_coherent(dev, this->page_buffer_size,
- this->page_buffer_virt,
- this->page_buffer_phys);
- kfree(this->cmd_buffer);
- kfree(this->data_buffer_dma);
- kfree(this->raw_buffer);
-
- this->cmd_buffer = NULL;
- this->data_buffer_dma = NULL;
- this->raw_buffer = NULL;
- this->page_buffer_virt = NULL;
- this->page_buffer_size = 0;
-}
-
-/* Allocate the DMA buffers */
-static int gpmi_alloc_dma_buffer(struct gpmi_nand_data *this)
-{
- struct bch_geometry *geo = &this->bch_geometry;
- struct device *dev = this->dev;
- struct mtd_info *mtd = nand_to_mtd(&this->nand);
-
- /* [1] Allocate a command buffer. PAGE_SIZE is enough. */
- this->cmd_buffer = kzalloc(PAGE_SIZE, GFP_DMA | GFP_KERNEL);
- if (this->cmd_buffer == NULL)
- goto error_alloc;
-
- /*
- * [2] Allocate a read/write data buffer.
- * The gpmi_alloc_dma_buffer can be called twice.
- * We allocate a PAGE_SIZE length buffer if gpmi_alloc_dma_buffer
- * is called before the nand_scan_ident; and we allocate a buffer
- * of the real NAND page size when the gpmi_alloc_dma_buffer is
- * called after the nand_scan_ident.
- */
- this->data_buffer_dma = kzalloc(mtd->writesize ?: PAGE_SIZE,
- GFP_DMA | GFP_KERNEL);
- if (this->data_buffer_dma == NULL)
- goto error_alloc;
-
- /*
- * [3] Allocate the page buffer.
- *
- * Both the payload buffer and the auxiliary buffer must appear on
- * 32-bit boundaries. We presume the size of the payload buffer is a
- * power of two and is much larger than four, which guarantees the
- * auxiliary buffer will appear on a 32-bit boundary.
- */
- this->page_buffer_size = geo->payload_size + geo->auxiliary_size;
- this->page_buffer_virt = dma_alloc_coherent(dev, this->page_buffer_size,
- &this->page_buffer_phys, GFP_DMA);
- if (!this->page_buffer_virt)
- goto error_alloc;
-
- this->raw_buffer = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
- if (!this->raw_buffer)
- goto error_alloc;
-
- /* Slice up the page buffer. */
- this->payload_virt = this->page_buffer_virt;
- this->payload_phys = this->page_buffer_phys;
- this->auxiliary_virt = this->payload_virt + geo->payload_size;
- this->auxiliary_phys = this->payload_phys + geo->payload_size;
- return 0;
-
-error_alloc:
- gpmi_free_dma_buffer(this);
- return -ENOMEM;
-}
-
-static void gpmi_cmd_ctrl(struct mtd_info *mtd, int data, unsigned int ctrl)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct gpmi_nand_data *this = nand_get_controller_data(chip);
- int ret;
-
- /*
- * Every operation begins with a command byte and a series of zero or
- * more address bytes. These are distinguished by either the Address
- * Latch Enable (ALE) or Command Latch Enable (CLE) signals being
- * asserted. When MTD is ready to execute the command, it will deassert
- * both latch enables.
- *
- * Rather than run a separate DMA operation for every single byte, we
- * queue them up and run a single DMA operation for the entire series
- * of command and data bytes. NAND_CMD_NONE means the END of the queue.
- */
- if ((ctrl & (NAND_ALE | NAND_CLE))) {
- if (data != NAND_CMD_NONE)
- this->cmd_buffer[this->command_length++] = data;
- return;
- }
-
- if (!this->command_length)
- return;
-
- ret = gpmi_send_command(this);
- if (ret)
- dev_err(this->dev, "Chip: %u, Error %d\n",
- this->current_chip, ret);
-
- this->command_length = 0;
-}
-
-static int gpmi_dev_ready(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct gpmi_nand_data *this = nand_get_controller_data(chip);
-
- return gpmi_is_ready(this, this->current_chip);
-}
-
-static void gpmi_select_chip(struct mtd_info *mtd, int chipnr)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct gpmi_nand_data *this = nand_get_controller_data(chip);
-
- if ((this->current_chip < 0) && (chipnr >= 0))
- gpmi_begin(this);
- else if ((this->current_chip >= 0) && (chipnr < 0))
- gpmi_end(this);
-
- this->current_chip = chipnr;
-}
-
-static void gpmi_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct gpmi_nand_data *this = nand_get_controller_data(chip);
-
- dev_dbg(this->dev, "len is %d\n", len);
- this->upper_buf = buf;
- this->upper_len = len;
-
- gpmi_read_data(this);
-}
-
-static void gpmi_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct gpmi_nand_data *this = nand_get_controller_data(chip);
-
- dev_dbg(this->dev, "len is %d\n", len);
- this->upper_buf = (uint8_t *)buf;
- this->upper_len = len;
-
- gpmi_send_data(this);
-}
-
-static uint8_t gpmi_read_byte(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct gpmi_nand_data *this = nand_get_controller_data(chip);
- uint8_t *buf = this->data_buffer_dma;
-
- gpmi_read_buf(mtd, buf, 1);
- return buf[0];
-}
-
-/*
- * Handles block mark swapping.
- * It can be called in swapping the block mark, or swapping it back,
- * because the the operations are the same.
- */
-static void block_mark_swapping(struct gpmi_nand_data *this,
- void *payload, void *auxiliary)
-{
- struct bch_geometry *nfc_geo = &this->bch_geometry;
- unsigned char *p;
- unsigned char *a;
- unsigned int bit;
- unsigned char mask;
- unsigned char from_data;
- unsigned char from_oob;
-
- if (!this->swap_block_mark)
- return;
-
- /*
- * If control arrives here, we're swapping. Make some convenience
- * variables.
- */
- bit = nfc_geo->block_mark_bit_offset;
- p = payload + nfc_geo->block_mark_byte_offset;
- a = auxiliary;
-
- /*
- * Get the byte from the data area that overlays the block mark. Since
- * the ECC engine applies its own view to the bits in the page, the
- * physical block mark won't (in general) appear on a byte boundary in
- * the data.
- */
- from_data = (p[0] >> bit) | (p[1] << (8 - bit));
-
- /* Get the byte from the OOB. */
- from_oob = a[0];
-
- /* Swap them. */
- a[0] = from_data;
-
- mask = (0x1 << bit) - 1;
- p[0] = (p[0] & mask) | (from_oob << bit);
-
- mask = ~0 << bit;
- p[1] = (p[1] & mask) | (from_oob >> (8 - bit));
-}
-
-static int gpmi_ecc_read_page_data(struct nand_chip *chip,
- uint8_t *buf, int oob_required,
- int page)
-{
- struct gpmi_nand_data *this = nand_get_controller_data(chip);
- struct bch_geometry *nfc_geo = &this->bch_geometry;
- struct mtd_info *mtd = nand_to_mtd(chip);
- void *payload_virt;
- dma_addr_t payload_phys;
- void *auxiliary_virt;
- dma_addr_t auxiliary_phys;
- unsigned int i;
- unsigned char *status;
- unsigned int max_bitflips = 0;
- int ret;
-
- dev_dbg(this->dev, "page number is : %d\n", page);
- ret = read_page_prepare(this, buf, nfc_geo->payload_size,
- this->payload_virt, this->payload_phys,
- nfc_geo->payload_size,
- &payload_virt, &payload_phys);
- if (ret) {
- dev_err(this->dev, "Inadequate DMA buffer\n");
- ret = -ENOMEM;
- return ret;
- }
- auxiliary_virt = this->auxiliary_virt;
- auxiliary_phys = this->auxiliary_phys;
-
- /* go! */
- ret = gpmi_read_page(this, payload_phys, auxiliary_phys);
- read_page_end(this, buf, nfc_geo->payload_size,
- this->payload_virt, this->payload_phys,
- nfc_geo->payload_size,
- payload_virt, payload_phys);
- if (ret) {
- dev_err(this->dev, "Error in ECC-based read: %d\n", ret);
- return ret;
- }
-
- /* Loop over status bytes, accumulating ECC status. */
- status = auxiliary_virt + nfc_geo->auxiliary_status_offset;
-
- read_page_swap_end(this, buf, nfc_geo->payload_size,
- this->payload_virt, this->payload_phys,
- nfc_geo->payload_size,
- payload_virt, payload_phys);
-
- for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) {
- if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED))
- continue;
-
- if (*status == STATUS_UNCORRECTABLE) {
- int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len;
- u8 *eccbuf = this->raw_buffer;
- int offset, bitoffset;
- int eccbytes;
- int flips;
-
- /* Read ECC bytes into our internal raw_buffer */
- offset = nfc_geo->metadata_size * 8;
- offset += ((8 * nfc_geo->ecc_chunk_size) + eccbits) * (i + 1);
- offset -= eccbits;
- bitoffset = offset % 8;
- eccbytes = DIV_ROUND_UP(offset + eccbits, 8);
- offset /= 8;
- eccbytes -= offset;
- nand_change_read_column_op(chip, offset, eccbuf,
- eccbytes, false);
-
- /*
- * ECC data are not byte aligned and we may have
- * in-band data in the first and last byte of
- * eccbuf. Set non-eccbits to one so that
- * nand_check_erased_ecc_chunk() does not count them
- * as bitflips.
- */
- if (bitoffset)
- eccbuf[0] |= GENMASK(bitoffset - 1, 0);
-
- bitoffset = (bitoffset + eccbits) % 8;
- if (bitoffset)
- eccbuf[eccbytes - 1] |= GENMASK(7, bitoffset);
-
- /*
- * The ECC hardware has an uncorrectable ECC status
- * code in case we have bitflips in an erased page. As
- * nothing was written into this subpage the ECC is
- * obviously wrong and we can not trust it. We assume
- * at this point that we are reading an erased page and
- * try to correct the bitflips in buffer up to
- * ecc_strength bitflips. If this is a page with random
- * data, we exceed this number of bitflips and have a
- * ECC failure. Otherwise we use the corrected buffer.
- */
- if (i == 0) {
- /* The first block includes metadata */
- flips = nand_check_erased_ecc_chunk(
- buf + i * nfc_geo->ecc_chunk_size,
- nfc_geo->ecc_chunk_size,
- eccbuf, eccbytes,
- auxiliary_virt,
- nfc_geo->metadata_size,
- nfc_geo->ecc_strength);
- } else {
- flips = nand_check_erased_ecc_chunk(
- buf + i * nfc_geo->ecc_chunk_size,
- nfc_geo->ecc_chunk_size,
- eccbuf, eccbytes,
- NULL, 0,
- nfc_geo->ecc_strength);
- }
-
- if (flips > 0) {
- max_bitflips = max_t(unsigned int, max_bitflips,
- flips);
- mtd->ecc_stats.corrected += flips;
- continue;
- }
-
- mtd->ecc_stats.failed++;
- continue;
- }
-
- mtd->ecc_stats.corrected += *status;
- max_bitflips = max_t(unsigned int, max_bitflips, *status);
- }
-
- /* handle the block mark swapping */
- block_mark_swapping(this, buf, auxiliary_virt);
-
- if (oob_required) {
- /*
- * It's time to deliver the OOB bytes. See gpmi_ecc_read_oob()
- * for details about our policy for delivering the OOB.
- *
- * We fill the caller's buffer with set bits, and then copy the
- * block mark to th caller's buffer. Note that, if block mark
- * swapping was necessary, it has already been done, so we can
- * rely on the first byte of the auxiliary buffer to contain
- * the block mark.
- */
- memset(chip->oob_poi, ~0, mtd->oobsize);
- chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0];
- }
-
- return max_bitflips;
-}
-
-static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page)
-{
- nand_read_page_op(chip, page, 0, NULL, 0);
-
- return gpmi_ecc_read_page_data(chip, buf, oob_required, page);
-}
-
-/* Fake a virtual small page for the subpage read */
-static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
- uint32_t offs, uint32_t len, uint8_t *buf, int page)
-{
- struct gpmi_nand_data *this = nand_get_controller_data(chip);
- void __iomem *bch_regs = this->resources.bch_regs;
- struct bch_geometry old_geo = this->bch_geometry;
- struct bch_geometry *geo = &this->bch_geometry;
- int size = chip->ecc.size; /* ECC chunk size */
- int meta, n, page_size;
- u32 r1_old, r2_old, r1_new, r2_new;
- unsigned int max_bitflips;
- int first, last, marker_pos;
- int ecc_parity_size;
- int col = 0;
- int old_swap_block_mark = this->swap_block_mark;
-
- /* The size of ECC parity */
- ecc_parity_size = geo->gf_len * geo->ecc_strength / 8;
-
- /* Align it with the chunk size */
- first = offs / size;
- last = (offs + len - 1) / size;
-
- if (this->swap_block_mark) {
- /*
- * Find the chunk which contains the Block Marker.
- * If this chunk is in the range of [first, last],
- * we have to read out the whole page.
- * Why? since we had swapped the data at the position of Block
- * Marker to the metadata which is bound with the chunk 0.
- */
- marker_pos = geo->block_mark_byte_offset / size;
- if (last >= marker_pos && first <= marker_pos) {
- dev_dbg(this->dev,
- "page:%d, first:%d, last:%d, marker at:%d\n",
- page, first, last, marker_pos);
- return gpmi_ecc_read_page(mtd, chip, buf, 0, page);
- }
- }
-
- meta = geo->metadata_size;
- if (first) {
- col = meta + (size + ecc_parity_size) * first;
- meta = 0;
- buf = buf + first * size;
- }
-
- nand_read_page_op(chip, page, col, NULL, 0);
-
- /* Save the old environment */
- r1_old = r1_new = readl(bch_regs + HW_BCH_FLASH0LAYOUT0);
- r2_old = r2_new = readl(bch_regs + HW_BCH_FLASH0LAYOUT1);
-
- /* change the BCH registers and bch_geometry{} */
- n = last - first + 1;
- page_size = meta + (size + ecc_parity_size) * n;
-
- r1_new &= ~(BM_BCH_FLASH0LAYOUT0_NBLOCKS |
- BM_BCH_FLASH0LAYOUT0_META_SIZE);
- r1_new |= BF_BCH_FLASH0LAYOUT0_NBLOCKS(n - 1)
- | BF_BCH_FLASH0LAYOUT0_META_SIZE(meta);
- writel(r1_new, bch_regs + HW_BCH_FLASH0LAYOUT0);
-
- r2_new &= ~BM_BCH_FLASH0LAYOUT1_PAGE_SIZE;
- r2_new |= BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size);
- writel(r2_new, bch_regs + HW_BCH_FLASH0LAYOUT1);
-
- geo->ecc_chunk_count = n;
- geo->payload_size = n * size;
- geo->page_size = page_size;
- geo->auxiliary_status_offset = ALIGN(meta, 4);
-
- dev_dbg(this->dev, "page:%d(%d:%d)%d, chunk:(%d:%d), BCH PG size:%d\n",
- page, offs, len, col, first, n, page_size);
-
- /* Read the subpage now */
- this->swap_block_mark = false;
- max_bitflips = gpmi_ecc_read_page_data(chip, buf, 0, page);
-
- /* Restore */
- writel(r1_old, bch_regs + HW_BCH_FLASH0LAYOUT0);
- writel(r2_old, bch_regs + HW_BCH_FLASH0LAYOUT1);
- this->bch_geometry = old_geo;
- this->swap_block_mark = old_swap_block_mark;
-
- return max_bitflips;
-}
-
-static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf, int oob_required, int page)
-{
- struct gpmi_nand_data *this = nand_get_controller_data(chip);
- struct bch_geometry *nfc_geo = &this->bch_geometry;
- const void *payload_virt;
- dma_addr_t payload_phys;
- const void *auxiliary_virt;
- dma_addr_t auxiliary_phys;
- int ret;
-
- dev_dbg(this->dev, "ecc write page.\n");
-
- nand_prog_page_begin_op(chip, page, 0, NULL, 0);
-
- if (this->swap_block_mark) {
- /*
- * If control arrives here, we're doing block mark swapping.
- * Since we can't modify the caller's buffers, we must copy them
- * into our own.
- */
- memcpy(this->payload_virt, buf, mtd->writesize);
- payload_virt = this->payload_virt;
- payload_phys = this->payload_phys;
-
- memcpy(this->auxiliary_virt, chip->oob_poi,
- nfc_geo->auxiliary_size);
- auxiliary_virt = this->auxiliary_virt;
- auxiliary_phys = this->auxiliary_phys;
-
- /* Handle block mark swapping. */
- block_mark_swapping(this,
- (void *)payload_virt, (void *)auxiliary_virt);
- } else {
- /*
- * If control arrives here, we're not doing block mark swapping,
- * so we can to try and use the caller's buffers.
- */
- ret = send_page_prepare(this,
- buf, mtd->writesize,
- this->payload_virt, this->payload_phys,
- nfc_geo->payload_size,
- &payload_virt, &payload_phys);
- if (ret) {
- dev_err(this->dev, "Inadequate payload DMA buffer\n");
- return 0;
- }
-
- ret = send_page_prepare(this,
- chip->oob_poi, mtd->oobsize,
- this->auxiliary_virt, this->auxiliary_phys,
- nfc_geo->auxiliary_size,
- &auxiliary_virt, &auxiliary_phys);
- if (ret) {
- dev_err(this->dev, "Inadequate auxiliary DMA buffer\n");
- goto exit_auxiliary;
- }
- }
-
- /* Ask the NFC. */
- ret = gpmi_send_page(this, payload_phys, auxiliary_phys);
- if (ret)
- dev_err(this->dev, "Error in ECC-based write: %d\n", ret);
-
- if (!this->swap_block_mark) {
- send_page_end(this, chip->oob_poi, mtd->oobsize,
- this->auxiliary_virt, this->auxiliary_phys,
- nfc_geo->auxiliary_size,
- auxiliary_virt, auxiliary_phys);
-exit_auxiliary:
- send_page_end(this, buf, mtd->writesize,
- this->payload_virt, this->payload_phys,
- nfc_geo->payload_size,
- payload_virt, payload_phys);
- }
-
- if (ret)
- return ret;
-
- return nand_prog_page_end_op(chip);
-}
-
-/*
- * There are several places in this driver where we have to handle the OOB and
- * block marks. This is the function where things are the most complicated, so
- * this is where we try to explain it all. All the other places refer back to
- * here.
- *
- * These are the rules, in order of decreasing importance:
- *
- * 1) Nothing the caller does can be allowed to imperil the block mark.
- *
- * 2) In read operations, the first byte of the OOB we return must reflect the
- * true state of the block mark, no matter where that block mark appears in
- * the physical page.
- *
- * 3) ECC-based read operations return an OOB full of set bits (since we never
- * allow ECC-based writes to the OOB, it doesn't matter what ECC-based reads
- * return).
- *
- * 4) "Raw" read operations return a direct view of the physical bytes in the
- * page, using the conventional definition of which bytes are data and which
- * are OOB. This gives the caller a way to see the actual, physical bytes
- * in the page, without the distortions applied by our ECC engine.
- *
- *
- * What we do for this specific read operation depends on two questions:
- *
- * 1) Are we doing a "raw" read, or an ECC-based read?
- *
- * 2) Are we using block mark swapping or transcription?
- *
- * There are four cases, illustrated by the following Karnaugh map:
- *
- * | Raw | ECC-based |
- * -------------+-------------------------+-------------------------+
- * | Read the conventional | |
- * | OOB at the end of the | |
- * Swapping | page and return it. It | |
- * | contains exactly what | |
- * | we want. | Read the block mark and |
- * -------------+-------------------------+ return it in a buffer |
- * | Read the conventional | full of set bits. |
- * | OOB at the end of the | |
- * | page and also the block | |
- * Transcribing | mark in the metadata. | |
- * | Copy the block mark | |
- * | into the first byte of | |
- * | the OOB. | |
- * -------------+-------------------------+-------------------------+
- *
- * Note that we break rule #4 in the Transcribing/Raw case because we're not
- * giving an accurate view of the actual, physical bytes in the page (we're
- * overwriting the block mark). That's OK because it's more important to follow
- * rule #2.
- *
- * It turns out that knowing whether we want an "ECC-based" or "raw" read is not
- * easy. When reading a page, for example, the NAND Flash MTD code calls our
- * ecc.read_page or ecc.read_page_raw function. Thus, the fact that MTD wants an
- * ECC-based or raw view of the page is implicit in which function it calls
- * (there is a similar pair of ECC-based/raw functions for writing).
- */
-static int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
- int page)
-{
- struct gpmi_nand_data *this = nand_get_controller_data(chip);
-
- dev_dbg(this->dev, "page number is %d\n", page);
- /* clear the OOB buffer */
- memset(chip->oob_poi, ~0, mtd->oobsize);
-
- /* Read out the conventional OOB. */
- nand_read_page_op(chip, page, mtd->writesize, NULL, 0);
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
-
- /*
- * Now, we want to make sure the block mark is correct. In the
- * non-transcribing case (!GPMI_IS_MX23()), we already have it.
- * Otherwise, we need to explicitly read it.
- */
- if (GPMI_IS_MX23(this)) {
- /* Read the block mark into the first byte of the OOB buffer. */
- nand_read_page_op(chip, page, 0, NULL, 0);
- chip->oob_poi[0] = chip->read_byte(mtd);
- }
-
- return 0;
-}
-
-static int
-gpmi_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page)
-{
- struct mtd_oob_region of = { };
-
- /* Do we have available oob area? */
- mtd_ooblayout_free(mtd, 0, &of);
- if (!of.length)
- return -EPERM;
-
- if (!nand_is_slc(chip))
- return -EPERM;
-
- return nand_prog_page_op(chip, page, mtd->writesize + of.offset,
- chip->oob_poi + of.offset, of.length);
-}
-
-/*
- * This function reads a NAND page without involving the ECC engine (no HW
- * ECC correction).
- * The tricky part in the GPMI/BCH controller is that it stores ECC bits
- * inline (interleaved with payload DATA), and do not align data chunk on
- * byte boundaries.
- * We thus need to take care moving the payload data and ECC bits stored in the
- * page into the provided buffers, which is why we're using gpmi_copy_bits.
- *
- * See set_geometry_by_ecc_info inline comments to have a full description
- * of the layout used by the GPMI controller.
- */
-static int gpmi_ecc_read_page_raw(struct mtd_info *mtd,
- struct nand_chip *chip, uint8_t *buf,
- int oob_required, int page)
-{
- struct gpmi_nand_data *this = nand_get_controller_data(chip);
- struct bch_geometry *nfc_geo = &this->bch_geometry;
- int eccsize = nfc_geo->ecc_chunk_size;
- int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len;
- u8 *tmp_buf = this->raw_buffer;
- size_t src_bit_off;
- size_t oob_bit_off;
- size_t oob_byte_off;
- uint8_t *oob = chip->oob_poi;
- int step;
-
- nand_read_page_op(chip, page, 0, tmp_buf,
- mtd->writesize + mtd->oobsize);
-
- /*
- * If required, swap the bad block marker and the data stored in the
- * metadata section, so that we don't wrongly consider a block as bad.
- *
- * See the layout description for a detailed explanation on why this
- * is needed.
- */
- if (this->swap_block_mark)
- swap(tmp_buf[0], tmp_buf[mtd->writesize]);
-
- /*
- * Copy the metadata section into the oob buffer (this section is
- * guaranteed to be aligned on a byte boundary).
- */
- if (oob_required)
- memcpy(oob, tmp_buf, nfc_geo->metadata_size);
-
- oob_bit_off = nfc_geo->metadata_size * 8;
- src_bit_off = oob_bit_off;
-
- /* Extract interleaved payload data and ECC bits */
- for (step = 0; step < nfc_geo->ecc_chunk_count; step++) {
- if (buf)
- gpmi_copy_bits(buf, step * eccsize * 8,
- tmp_buf, src_bit_off,
- eccsize * 8);
- src_bit_off += eccsize * 8;
-
- /* Align last ECC block to align a byte boundary */
- if (step == nfc_geo->ecc_chunk_count - 1 &&
- (oob_bit_off + eccbits) % 8)
- eccbits += 8 - ((oob_bit_off + eccbits) % 8);
-
- if (oob_required)
- gpmi_copy_bits(oob, oob_bit_off,
- tmp_buf, src_bit_off,
- eccbits);
-
- src_bit_off += eccbits;
- oob_bit_off += eccbits;
- }
-
- if (oob_required) {
- oob_byte_off = oob_bit_off / 8;
-
- if (oob_byte_off < mtd->oobsize)
- memcpy(oob + oob_byte_off,
- tmp_buf + mtd->writesize + oob_byte_off,
- mtd->oobsize - oob_byte_off);
- }
-
- return 0;
-}
-
-/*
- * This function writes a NAND page without involving the ECC engine (no HW
- * ECC generation).
- * The tricky part in the GPMI/BCH controller is that it stores ECC bits
- * inline (interleaved with payload DATA), and do not align data chunk on
- * byte boundaries.
- * We thus need to take care moving the OOB area at the right place in the
- * final page, which is why we're using gpmi_copy_bits.
- *
- * See set_geometry_by_ecc_info inline comments to have a full description
- * of the layout used by the GPMI controller.
- */
-static int gpmi_ecc_write_page_raw(struct mtd_info *mtd,
- struct nand_chip *chip,
- const uint8_t *buf,
- int oob_required, int page)
-{
- struct gpmi_nand_data *this = nand_get_controller_data(chip);
- struct bch_geometry *nfc_geo = &this->bch_geometry;
- int eccsize = nfc_geo->ecc_chunk_size;
- int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len;
- u8 *tmp_buf = this->raw_buffer;
- uint8_t *oob = chip->oob_poi;
- size_t dst_bit_off;
- size_t oob_bit_off;
- size_t oob_byte_off;
- int step;
-
- /*
- * Initialize all bits to 1 in case we don't have a buffer for the
- * payload or oob data in order to leave unspecified bits of data
- * to their initial state.
- */
- if (!buf || !oob_required)
- memset(tmp_buf, 0xff, mtd->writesize + mtd->oobsize);
-
- /*
- * First copy the metadata section (stored in oob buffer) at the
- * beginning of the page, as imposed by the GPMI layout.
- */
- memcpy(tmp_buf, oob, nfc_geo->metadata_size);
- oob_bit_off = nfc_geo->metadata_size * 8;
- dst_bit_off = oob_bit_off;
-
- /* Interleave payload data and ECC bits */
- for (step = 0; step < nfc_geo->ecc_chunk_count; step++) {
- if (buf)
- gpmi_copy_bits(tmp_buf, dst_bit_off,
- buf, step * eccsize * 8, eccsize * 8);
- dst_bit_off += eccsize * 8;
-
- /* Align last ECC block to align a byte boundary */
- if (step == nfc_geo->ecc_chunk_count - 1 &&
- (oob_bit_off + eccbits) % 8)
- eccbits += 8 - ((oob_bit_off + eccbits) % 8);
-
- if (oob_required)
- gpmi_copy_bits(tmp_buf, dst_bit_off,
- oob, oob_bit_off, eccbits);
-
- dst_bit_off += eccbits;
- oob_bit_off += eccbits;
- }
-
- oob_byte_off = oob_bit_off / 8;
-
- if (oob_required && oob_byte_off < mtd->oobsize)
- memcpy(tmp_buf + mtd->writesize + oob_byte_off,
- oob + oob_byte_off, mtd->oobsize - oob_byte_off);
-
- /*
- * If required, swap the bad block marker and the first byte of the
- * metadata section, so that we don't modify the bad block marker.
- *
- * See the layout description for a detailed explanation on why this
- * is needed.
- */
- if (this->swap_block_mark)
- swap(tmp_buf[0], tmp_buf[mtd->writesize]);
-
- return nand_prog_page_op(chip, page, 0, tmp_buf,
- mtd->writesize + mtd->oobsize);
-}
-
-static int gpmi_ecc_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
- int page)
-{
- return gpmi_ecc_read_page_raw(mtd, chip, NULL, 1, page);
-}
-
-static int gpmi_ecc_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
- int page)
-{
- return gpmi_ecc_write_page_raw(mtd, chip, NULL, 1, page);
-}
-
-static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct gpmi_nand_data *this = nand_get_controller_data(chip);
- int ret = 0;
- uint8_t *block_mark;
- int column, page, chipnr;
-
- chipnr = (int)(ofs >> chip->chip_shift);
- chip->select_chip(mtd, chipnr);
-
- column = !GPMI_IS_MX23(this) ? mtd->writesize : 0;
-
- /* Write the block mark. */
- block_mark = this->data_buffer_dma;
- block_mark[0] = 0; /* bad block marker */
-
- /* Shift to get page */
- page = (int)(ofs >> chip->page_shift);
-
- ret = nand_prog_page_op(chip, page, column, block_mark, 1);
-
- chip->select_chip(mtd, -1);
-
- return ret;
-}
-
-static int nand_boot_set_geometry(struct gpmi_nand_data *this)
-{
- struct boot_rom_geometry *geometry = &this->rom_geometry;
-
- /*
- * Set the boot block stride size.
- *
- * In principle, we should be reading this from the OTP bits, since
- * that's where the ROM is going to get it. In fact, we don't have any
- * way to read the OTP bits, so we go with the default and hope for the
- * best.
- */
- geometry->stride_size_in_pages = 64;
-
- /*
- * Set the search area stride exponent.
- *
- * In principle, we should be reading this from the OTP bits, since
- * that's where the ROM is going to get it. In fact, we don't have any
- * way to read the OTP bits, so we go with the default and hope for the
- * best.
- */
- geometry->search_area_stride_exponent = 2;
- return 0;
-}
-
-static const char *fingerprint = "STMP";
-static int mx23_check_transcription_stamp(struct gpmi_nand_data *this)
-{
- struct boot_rom_geometry *rom_geo = &this->rom_geometry;
- struct device *dev = this->dev;
- struct nand_chip *chip = &this->nand;
- struct mtd_info *mtd = nand_to_mtd(chip);
- unsigned int search_area_size_in_strides;
- unsigned int stride;
- unsigned int page;
- uint8_t *buffer = chip->data_buf;
- int saved_chip_number;
- int found_an_ncb_fingerprint = false;
-
- /* Compute the number of strides in a search area. */
- search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent;
-
- saved_chip_number = this->current_chip;
- chip->select_chip(mtd, 0);
-
- /*
- * Loop through the first search area, looking for the NCB fingerprint.
- */
- dev_dbg(dev, "Scanning for an NCB fingerprint...\n");
-
- for (stride = 0; stride < search_area_size_in_strides; stride++) {
- /* Compute the page addresses. */
- page = stride * rom_geo->stride_size_in_pages;
-
- dev_dbg(dev, "Looking for a fingerprint in page 0x%x\n", page);
-
- /*
- * Read the NCB fingerprint. The fingerprint is four bytes long
- * and starts in the 12th byte of the page.
- */
- nand_read_page_op(chip, page, 12, NULL, 0);
- chip->read_buf(mtd, buffer, strlen(fingerprint));
-
- /* Look for the fingerprint. */
- if (!memcmp(buffer, fingerprint, strlen(fingerprint))) {
- found_an_ncb_fingerprint = true;
- break;
- }
-
- }
-
- chip->select_chip(mtd, saved_chip_number);
-
- if (found_an_ncb_fingerprint)
- dev_dbg(dev, "\tFound a fingerprint\n");
- else
- dev_dbg(dev, "\tNo fingerprint found\n");
- return found_an_ncb_fingerprint;
-}
-
-/* Writes a transcription stamp. */
-static int mx23_write_transcription_stamp(struct gpmi_nand_data *this)
-{
- struct device *dev = this->dev;
- struct boot_rom_geometry *rom_geo = &this->rom_geometry;
- struct nand_chip *chip = &this->nand;
- struct mtd_info *mtd = nand_to_mtd(chip);
- unsigned int block_size_in_pages;
- unsigned int search_area_size_in_strides;
- unsigned int search_area_size_in_pages;
- unsigned int search_area_size_in_blocks;
- unsigned int block;
- unsigned int stride;
- unsigned int page;
- uint8_t *buffer = chip->data_buf;
- int saved_chip_number;
- int status;
-
- /* Compute the search area geometry. */
- block_size_in_pages = mtd->erasesize / mtd->writesize;
- search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent;
- search_area_size_in_pages = search_area_size_in_strides *
- rom_geo->stride_size_in_pages;
- search_area_size_in_blocks =
- (search_area_size_in_pages + (block_size_in_pages - 1)) /
- block_size_in_pages;
-
- dev_dbg(dev, "Search Area Geometry :\n");
- dev_dbg(dev, "\tin Blocks : %u\n", search_area_size_in_blocks);
- dev_dbg(dev, "\tin Strides: %u\n", search_area_size_in_strides);
- dev_dbg(dev, "\tin Pages : %u\n", search_area_size_in_pages);
-
- /* Select chip 0. */
- saved_chip_number = this->current_chip;
- chip->select_chip(mtd, 0);
-
- /* Loop over blocks in the first search area, erasing them. */
- dev_dbg(dev, "Erasing the search area...\n");
-
- for (block = 0; block < search_area_size_in_blocks; block++) {
- /* Erase this block. */
- dev_dbg(dev, "\tErasing block 0x%x\n", block);
- status = nand_erase_op(chip, block);
- if (status)
- dev_err(dev, "[%s] Erase failed.\n", __func__);
- }
-
- /* Write the NCB fingerprint into the page buffer. */
- memset(buffer, ~0, mtd->writesize);
- memcpy(buffer + 12, fingerprint, strlen(fingerprint));
-
- /* Loop through the first search area, writing NCB fingerprints. */
- dev_dbg(dev, "Writing NCB fingerprints...\n");
- for (stride = 0; stride < search_area_size_in_strides; stride++) {
- /* Compute the page addresses. */
- page = stride * rom_geo->stride_size_in_pages;
-
- /* Write the first page of the current stride. */
- dev_dbg(dev, "Writing an NCB fingerprint in page 0x%x\n", page);
-
- status = chip->ecc.write_page_raw(mtd, chip, buffer, 0, page);
- if (status)
- dev_err(dev, "[%s] Write failed.\n", __func__);
- }
-
- /* Deselect chip 0. */
- chip->select_chip(mtd, saved_chip_number);
- return 0;
-}
-
-static int mx23_boot_init(struct gpmi_nand_data *this)
-{
- struct device *dev = this->dev;
- struct nand_chip *chip = &this->nand;
- struct mtd_info *mtd = nand_to_mtd(chip);
- unsigned int block_count;
- unsigned int block;
- int chipnr;
- int page;
- loff_t byte;
- uint8_t block_mark;
- int ret = 0;
-
- /*
- * If control arrives here, we can't use block mark swapping, which
- * means we're forced to use transcription. First, scan for the
- * transcription stamp. If we find it, then we don't have to do
- * anything -- the block marks are already transcribed.
- */
- if (mx23_check_transcription_stamp(this))
- return 0;
-
- /*
- * If control arrives here, we couldn't find a transcription stamp, so
- * so we presume the block marks are in the conventional location.
- */
- dev_dbg(dev, "Transcribing bad block marks...\n");
-
- /* Compute the number of blocks in the entire medium. */
- block_count = chip->chipsize >> chip->phys_erase_shift;
-
- /*
- * Loop over all the blocks in the medium, transcribing block marks as
- * we go.
- */
- for (block = 0; block < block_count; block++) {
- /*
- * Compute the chip, page and byte addresses for this block's
- * conventional mark.
- */
- chipnr = block >> (chip->chip_shift - chip->phys_erase_shift);
- page = block << (chip->phys_erase_shift - chip->page_shift);
- byte = block << chip->phys_erase_shift;
-
- /* Send the command to read the conventional block mark. */
- chip->select_chip(mtd, chipnr);
- nand_read_page_op(chip, page, mtd->writesize, NULL, 0);
- block_mark = chip->read_byte(mtd);
- chip->select_chip(mtd, -1);
-
- /*
- * Check if the block is marked bad. If so, we need to mark it
- * again, but this time the result will be a mark in the
- * location where we transcribe block marks.
- */
- if (block_mark != 0xff) {
- dev_dbg(dev, "Transcribing mark in block %u\n", block);
- ret = chip->block_markbad(mtd, byte);
- if (ret)
- dev_err(dev,
- "Failed to mark block bad with ret %d\n",
- ret);
- }
- }
-
- /* Write the stamp that indicates we've transcribed the block marks. */
- mx23_write_transcription_stamp(this);
- return 0;
-}
-
-static int nand_boot_init(struct gpmi_nand_data *this)
-{
- nand_boot_set_geometry(this);
-
- /* This is ROM arch-specific initilization before the BBT scanning. */
- if (GPMI_IS_MX23(this))
- return mx23_boot_init(this);
- return 0;
-}
-
-static int gpmi_set_geometry(struct gpmi_nand_data *this)
-{
- int ret;
-
- /* Free the temporary DMA memory for reading ID. */
- gpmi_free_dma_buffer(this);
-
- /* Set up the NFC geometry which is used by BCH. */
- ret = bch_set_geometry(this);
- if (ret) {
- dev_err(this->dev, "Error setting BCH geometry : %d\n", ret);
- return ret;
- }
-
- /* Alloc the new DMA buffers according to the pagesize and oobsize */
- return gpmi_alloc_dma_buffer(this);
-}
-
-static int gpmi_init_last(struct gpmi_nand_data *this)
-{
- struct nand_chip *chip = &this->nand;
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
- struct bch_geometry *bch_geo = &this->bch_geometry;
- int ret;
-
- /* Set up the medium geometry */
- ret = gpmi_set_geometry(this);
- if (ret)
- return ret;
-
- /* Init the nand_ecc_ctrl{} */
- ecc->read_page = gpmi_ecc_read_page;
- ecc->write_page = gpmi_ecc_write_page;
- ecc->read_oob = gpmi_ecc_read_oob;
- ecc->write_oob = gpmi_ecc_write_oob;
- ecc->read_page_raw = gpmi_ecc_read_page_raw;
- ecc->write_page_raw = gpmi_ecc_write_page_raw;
- ecc->read_oob_raw = gpmi_ecc_read_oob_raw;
- ecc->write_oob_raw = gpmi_ecc_write_oob_raw;
- ecc->mode = NAND_ECC_HW;
- ecc->size = bch_geo->ecc_chunk_size;
- ecc->strength = bch_geo->ecc_strength;
- mtd_set_ooblayout(mtd, &gpmi_ooblayout_ops);
-
- /*
- * We only enable the subpage read when:
- * (1) the chip is imx6, and
- * (2) the size of the ECC parity is byte aligned.
- */
- if (GPMI_IS_MX6(this) &&
- ((bch_geo->gf_len * bch_geo->ecc_strength) % 8) == 0) {
- ecc->read_subpage = gpmi_ecc_read_subpage;
- chip->options |= NAND_SUBPAGE_READ;
- }
-
- /*
- * Can we enable the extra features? such as EDO or Sync mode.
- *
- * We do not check the return value now. That's means if we fail in
- * enable the extra features, we still can run in the normal way.
- */
- gpmi_extra_init(this);
-
- return 0;
-}
-
-static int gpmi_nand_init(struct gpmi_nand_data *this)
-{
- struct nand_chip *chip = &this->nand;
- struct mtd_info *mtd = nand_to_mtd(chip);
- int ret;
-
- /* init current chip */
- this->current_chip = -1;
-
- /* init the MTD data structures */
- mtd->name = "gpmi-nand";
- mtd->dev.parent = this->dev;
-
- /* init the nand_chip{}, we don't support a 16-bit NAND Flash bus. */
- nand_set_controller_data(chip, this);
- nand_set_flash_node(chip, this->pdev->dev.of_node);
- chip->select_chip = gpmi_select_chip;
- chip->cmd_ctrl = gpmi_cmd_ctrl;
- chip->dev_ready = gpmi_dev_ready;
- chip->read_byte = gpmi_read_byte;
- chip->read_buf = gpmi_read_buf;
- chip->write_buf = gpmi_write_buf;
- chip->badblock_pattern = &gpmi_bbt_descr;
- chip->block_markbad = gpmi_block_markbad;
- chip->options |= NAND_NO_SUBPAGE_WRITE;
-
- /* Set up swap_block_mark, must be set before the gpmi_set_geometry() */
- this->swap_block_mark = !GPMI_IS_MX23(this);
-
- /*
- * Allocate a temporary DMA buffer for reading ID in the
- * nand_scan_ident().
- */
- this->bch_geometry.payload_size = 1024;
- this->bch_geometry.auxiliary_size = 128;
- ret = gpmi_alloc_dma_buffer(this);
- if (ret)
- goto err_out;
-
- ret = nand_scan_ident(mtd, GPMI_IS_MX6(this) ? 2 : 1, NULL);
- if (ret)
- goto err_out;
-
- if (chip->bbt_options & NAND_BBT_USE_FLASH) {
- chip->bbt_options |= NAND_BBT_NO_OOB;
-
- if (of_property_read_bool(this->dev->of_node,
- "fsl,no-blockmark-swap"))
- this->swap_block_mark = false;
- }
- dev_dbg(this->dev, "Blockmark swapping %sabled\n",
- this->swap_block_mark ? "en" : "dis");
-
- ret = gpmi_init_last(this);
- if (ret)
- goto err_out;
-
- chip->options |= NAND_SKIP_BBTSCAN;
- ret = nand_scan_tail(mtd);
- if (ret)
- goto err_out;
-
- ret = nand_boot_init(this);
- if (ret)
- goto err_nand_cleanup;
- ret = chip->scan_bbt(mtd);
- if (ret)
- goto err_nand_cleanup;
-
- ret = mtd_device_register(mtd, NULL, 0);
- if (ret)
- goto err_nand_cleanup;
- return 0;
-
-err_nand_cleanup:
- nand_cleanup(chip);
-err_out:
- gpmi_free_dma_buffer(this);
- return ret;
-}
-
-static const struct of_device_id gpmi_nand_id_table[] = {
- {
- .compatible = "fsl,imx23-gpmi-nand",
- .data = &gpmi_devdata_imx23,
- }, {
- .compatible = "fsl,imx28-gpmi-nand",
- .data = &gpmi_devdata_imx28,
- }, {
- .compatible = "fsl,imx6q-gpmi-nand",
- .data = &gpmi_devdata_imx6q,
- }, {
- .compatible = "fsl,imx6sx-gpmi-nand",
- .data = &gpmi_devdata_imx6sx,
- }, {
- .compatible = "fsl,imx7d-gpmi-nand",
- .data = &gpmi_devdata_imx7d,
- }, {}
-};
-MODULE_DEVICE_TABLE(of, gpmi_nand_id_table);
-
-static int gpmi_nand_probe(struct platform_device *pdev)
-{
- struct gpmi_nand_data *this;
- const struct of_device_id *of_id;
- int ret;
-
- this = devm_kzalloc(&pdev->dev, sizeof(*this), GFP_KERNEL);
- if (!this)
- return -ENOMEM;
-
- of_id = of_match_device(gpmi_nand_id_table, &pdev->dev);
- if (of_id) {
- this->devdata = of_id->data;
- } else {
- dev_err(&pdev->dev, "Failed to find the right device id.\n");
- return -ENODEV;
- }
-
- platform_set_drvdata(pdev, this);
- this->pdev = pdev;
- this->dev = &pdev->dev;
-
- ret = acquire_resources(this);
- if (ret)
- goto exit_acquire_resources;
-
- ret = init_hardware(this);
- if (ret)
- goto exit_nfc_init;
-
- ret = gpmi_nand_init(this);
- if (ret)
- goto exit_nfc_init;
-
- dev_info(this->dev, "driver registered.\n");
-
- return 0;
-
-exit_nfc_init:
- release_resources(this);
-exit_acquire_resources:
-
- return ret;
-}
-
-static int gpmi_nand_remove(struct platform_device *pdev)
-{
- struct gpmi_nand_data *this = platform_get_drvdata(pdev);
-
- nand_release(nand_to_mtd(&this->nand));
- gpmi_free_dma_buffer(this);
- release_resources(this);
- return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int gpmi_pm_suspend(struct device *dev)
-{
- struct gpmi_nand_data *this = dev_get_drvdata(dev);
-
- release_dma_channels(this);
- return 0;
-}
-
-static int gpmi_pm_resume(struct device *dev)
-{
- struct gpmi_nand_data *this = dev_get_drvdata(dev);
- int ret;
-
- ret = acquire_dma_channels(this);
- if (ret < 0)
- return ret;
-
- /* re-init the GPMI registers */
- this->flags &= ~GPMI_TIMING_INIT_OK;
- ret = gpmi_init(this);
- if (ret) {
- dev_err(this->dev, "Error setting GPMI : %d\n", ret);
- return ret;
- }
-
- /* re-init the BCH registers */
- ret = bch_set_geometry(this);
- if (ret) {
- dev_err(this->dev, "Error setting BCH : %d\n", ret);
- return ret;
- }
-
- /* re-init others */
- gpmi_extra_init(this);
-
- return 0;
-}
-#endif /* CONFIG_PM_SLEEP */
-
-static const struct dev_pm_ops gpmi_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(gpmi_pm_suspend, gpmi_pm_resume)
-};
-
-static struct platform_driver gpmi_nand_driver = {
- .driver = {
- .name = "gpmi-nand",
- .pm = &gpmi_pm_ops,
- .of_match_table = gpmi_nand_id_table,
- },
- .probe = gpmi_nand_probe,
- .remove = gpmi_nand_remove,
-};
-module_platform_driver(gpmi_nand_driver);
-
-MODULE_AUTHOR("Freescale Semiconductor, Inc.");
-MODULE_DESCRIPTION("i.MX GPMI NAND Flash Controller Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
deleted file mode 100644
index 06c1f993912c..000000000000
--- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
+++ /dev/null
@@ -1,315 +0,0 @@
-/*
- * Freescale GPMI NAND Flash Driver
- *
- * Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
- * Copyright (C) 2008 Embedded Alley Solutions, Inc.
- *
- * 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.
- */
-#ifndef __DRIVERS_MTD_NAND_GPMI_NAND_H
-#define __DRIVERS_MTD_NAND_GPMI_NAND_H
-
-#include <linux/mtd/rawnand.h>
-#include <linux/platform_device.h>
-#include <linux/dma-mapping.h>
-#include <linux/dmaengine.h>
-
-#define GPMI_CLK_MAX 5 /* MX6Q needs five clocks */
-struct resources {
- void __iomem *gpmi_regs;
- void __iomem *bch_regs;
- unsigned int dma_low_channel;
- unsigned int dma_high_channel;
- struct clk *clock[GPMI_CLK_MAX];
-};
-
-/**
- * struct bch_geometry - BCH geometry description.
- * @gf_len: The length of Galois Field. (e.g., 13 or 14)
- * @ecc_strength: A number that describes the strength of the ECC
- * algorithm.
- * @page_size: The size, in bytes, of a physical page, including
- * both data and OOB.
- * @metadata_size: The size, in bytes, of the metadata.
- * @ecc_chunk_size: The size, in bytes, of a single ECC chunk. Note
- * the first chunk in the page includes both data and
- * metadata, so it's a bit larger than this value.
- * @ecc_chunk_count: The number of ECC chunks in the page,
- * @payload_size: The size, in bytes, of the payload buffer.
- * @auxiliary_size: The size, in bytes, of the auxiliary buffer.
- * @auxiliary_status_offset: The offset into the auxiliary buffer at which
- * the ECC status appears.
- * @block_mark_byte_offset: The byte offset in the ECC-based page view at
- * which the underlying physical block mark appears.
- * @block_mark_bit_offset: The bit offset into the ECC-based page view at
- * which the underlying physical block mark appears.
- */
-struct bch_geometry {
- unsigned int gf_len;
- unsigned int ecc_strength;
- unsigned int page_size;
- unsigned int metadata_size;
- unsigned int ecc_chunk_size;
- unsigned int ecc_chunk_count;
- unsigned int payload_size;
- unsigned int auxiliary_size;
- unsigned int auxiliary_status_offset;
- unsigned int block_mark_byte_offset;
- unsigned int block_mark_bit_offset;
-};
-
-/**
- * struct boot_rom_geometry - Boot ROM geometry description.
- * @stride_size_in_pages: The size of a boot block stride, in pages.
- * @search_area_stride_exponent: The logarithm to base 2 of the size of a
- * search area in boot block strides.
- */
-struct boot_rom_geometry {
- unsigned int stride_size_in_pages;
- unsigned int search_area_stride_exponent;
-};
-
-/* DMA operations types */
-enum dma_ops_type {
- DMA_FOR_COMMAND = 1,
- DMA_FOR_READ_DATA,
- DMA_FOR_WRITE_DATA,
- DMA_FOR_READ_ECC_PAGE,
- DMA_FOR_WRITE_ECC_PAGE
-};
-
-/**
- * struct nand_timing - Fundamental timing attributes for NAND.
- * @data_setup_in_ns: The data setup time, in nanoseconds. Usually the
- * maximum of tDS and tWP. A negative value
- * indicates this characteristic isn't known.
- * @data_hold_in_ns: The data hold time, in nanoseconds. Usually the
- * maximum of tDH, tWH and tREH. A negative value
- * indicates this characteristic isn't known.
- * @address_setup_in_ns: The address setup time, in nanoseconds. Usually
- * the maximum of tCLS, tCS and tALS. A negative
- * value indicates this characteristic isn't known.
- * @gpmi_sample_delay_in_ns: A GPMI-specific timing parameter. A negative value
- * indicates this characteristic isn't known.
- * @tREA_in_ns: tREA, in nanoseconds, from the data sheet. A
- * negative value indicates this characteristic isn't
- * known.
- * @tRLOH_in_ns: tRLOH, in nanoseconds, from the data sheet. A
- * negative value indicates this characteristic isn't
- * known.
- * @tRHOH_in_ns: tRHOH, in nanoseconds, from the data sheet. A
- * negative value indicates this characteristic isn't
- * known.
- */
-struct nand_timing {
- int8_t data_setup_in_ns;
- int8_t data_hold_in_ns;
- int8_t address_setup_in_ns;
- int8_t gpmi_sample_delay_in_ns;
- int8_t tREA_in_ns;
- int8_t tRLOH_in_ns;
- int8_t tRHOH_in_ns;
-};
-
-enum gpmi_type {
- IS_MX23,
- IS_MX28,
- IS_MX6Q,
- IS_MX6SX,
- IS_MX7D,
-};
-
-struct gpmi_devdata {
- enum gpmi_type type;
- int bch_max_ecc_strength;
- int max_chain_delay; /* See the async EDO mode */
- const char * const *clks;
- const int clks_count;
-};
-
-struct gpmi_nand_data {
- /* flags */
-#define GPMI_ASYNC_EDO_ENABLED (1 << 0)
-#define GPMI_TIMING_INIT_OK (1 << 1)
- int flags;
- const struct gpmi_devdata *devdata;
-
- /* System Interface */
- struct device *dev;
- struct platform_device *pdev;
-
- /* Resources */
- struct resources resources;
-
- /* Flash Hardware */
- struct nand_timing timing;
- int timing_mode;
-
- /* BCH */
- struct bch_geometry bch_geometry;
- struct completion bch_done;
-
- /* NAND Boot issue */
- bool swap_block_mark;
- struct boot_rom_geometry rom_geometry;
-
- /* MTD / NAND */
- struct nand_chip nand;
-
- /* General-use Variables */
- int current_chip;
- unsigned int command_length;
-
- /* passed from upper layer */
- uint8_t *upper_buf;
- int upper_len;
-
- /* for DMA operations */
- bool direct_dma_map_ok;
-
- struct scatterlist cmd_sgl;
- char *cmd_buffer;
-
- struct scatterlist data_sgl;
- char *data_buffer_dma;
-
- void *page_buffer_virt;
- dma_addr_t page_buffer_phys;
- unsigned int page_buffer_size;
-
- void *payload_virt;
- dma_addr_t payload_phys;
-
- void *auxiliary_virt;
- dma_addr_t auxiliary_phys;
-
- void *raw_buffer;
-
- /* DMA channels */
-#define DMA_CHANS 8
- struct dma_chan *dma_chans[DMA_CHANS];
- enum dma_ops_type last_dma_type;
- enum dma_ops_type dma_type;
- struct completion dma_done;
-
- /* private */
- void *private;
-};
-
-/**
- * struct gpmi_nfc_hardware_timing - GPMI hardware timing parameters.
- * @data_setup_in_cycles: The data setup time, in cycles.
- * @data_hold_in_cycles: The data hold time, in cycles.
- * @address_setup_in_cycles: The address setup time, in cycles.
- * @device_busy_timeout: The timeout waiting for NAND Ready/Busy,
- * this value is the number of cycles multiplied
- * by 4096.
- * @use_half_periods: Indicates the clock is running slowly, so the
- * NFC DLL should use half-periods.
- * @sample_delay_factor: The sample delay factor.
- * @wrn_dly_sel: The delay on the GPMI write strobe.
- */
-struct gpmi_nfc_hardware_timing {
- /* for HW_GPMI_TIMING0 */
- uint8_t data_setup_in_cycles;
- uint8_t data_hold_in_cycles;
- uint8_t address_setup_in_cycles;
-
- /* for HW_GPMI_TIMING1 */
- uint16_t device_busy_timeout;
-#define GPMI_DEFAULT_BUSY_TIMEOUT 0x500 /* default busy timeout value.*/
-
- /* for HW_GPMI_CTRL1 */
- bool use_half_periods;
- uint8_t sample_delay_factor;
- uint8_t wrn_dly_sel;
-};
-
-/**
- * struct timing_threshold - Timing threshold
- * @max_data_setup_cycles: The maximum number of data setup cycles that
- * can be expressed in the hardware.
- * @internal_data_setup_in_ns: The time, in ns, that the NFC hardware requires
- * for data read internal setup. In the Reference
- * Manual, see the chapter "High-Speed NAND
- * Timing" for more details.
- * @max_sample_delay_factor: The maximum sample delay factor that can be
- * expressed in the hardware.
- * @max_dll_clock_period_in_ns: The maximum period of the GPMI clock that the
- * sample delay DLL hardware can possibly work
- * with (the DLL is unusable with longer periods).
- * If the full-cycle period is greater than HALF
- * this value, the DLL must be configured to use
- * half-periods.
- * @max_dll_delay_in_ns: The maximum amount of delay, in ns, that the
- * DLL can implement.
- * @clock_frequency_in_hz: The clock frequency, in Hz, during the current
- * I/O transaction. If no I/O transaction is in
- * progress, this is the clock frequency during
- * the most recent I/O transaction.
- */
-struct timing_threshold {
- const unsigned int max_chip_count;
- const unsigned int max_data_setup_cycles;
- const unsigned int internal_data_setup_in_ns;
- const unsigned int max_sample_delay_factor;
- const unsigned int max_dll_clock_period_in_ns;
- const unsigned int max_dll_delay_in_ns;
- unsigned long clock_frequency_in_hz;
-
-};
-
-/* Common Services */
-int common_nfc_set_geometry(struct gpmi_nand_data *);
-struct dma_chan *get_dma_chan(struct gpmi_nand_data *);
-void prepare_data_dma(struct gpmi_nand_data *,
- enum dma_data_direction dr);
-int start_dma_without_bch_irq(struct gpmi_nand_data *,
- struct dma_async_tx_descriptor *);
-int start_dma_with_bch_irq(struct gpmi_nand_data *,
- struct dma_async_tx_descriptor *);
-
-/* GPMI-NAND helper function library */
-int gpmi_init(struct gpmi_nand_data *);
-int gpmi_extra_init(struct gpmi_nand_data *);
-void gpmi_clear_bch(struct gpmi_nand_data *);
-void gpmi_dump_info(struct gpmi_nand_data *);
-int bch_set_geometry(struct gpmi_nand_data *);
-int gpmi_is_ready(struct gpmi_nand_data *, unsigned chip);
-int gpmi_send_command(struct gpmi_nand_data *);
-void gpmi_begin(struct gpmi_nand_data *);
-void gpmi_end(struct gpmi_nand_data *);
-int gpmi_read_data(struct gpmi_nand_data *);
-int gpmi_send_data(struct gpmi_nand_data *);
-int gpmi_send_page(struct gpmi_nand_data *,
- dma_addr_t payload, dma_addr_t auxiliary);
-int gpmi_read_page(struct gpmi_nand_data *,
- dma_addr_t payload, dma_addr_t auxiliary);
-
-void gpmi_copy_bits(u8 *dst, size_t dst_bit_off,
- const u8 *src, size_t src_bit_off,
- size_t nbits);
-
-/* BCH : Status Block Completion Codes */
-#define STATUS_GOOD 0x00
-#define STATUS_ERASED 0xff
-#define STATUS_UNCORRECTABLE 0xfe
-
-/* Use the devdata to distinguish different Archs. */
-#define GPMI_IS_MX23(x) ((x)->devdata->type == IS_MX23)
-#define GPMI_IS_MX28(x) ((x)->devdata->type == IS_MX28)
-#define GPMI_IS_MX6Q(x) ((x)->devdata->type == IS_MX6Q)
-#define GPMI_IS_MX6SX(x) ((x)->devdata->type == IS_MX6SX)
-#define GPMI_IS_MX7D(x) ((x)->devdata->type == IS_MX7D)
-
-#define GPMI_IS_MX6(x) (GPMI_IS_MX6Q(x) || GPMI_IS_MX6SX(x) || \
- GPMI_IS_MX7D(x))
-#endif
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-regs.h b/drivers/mtd/nand/gpmi-nand/gpmi-regs.h
deleted file mode 100644
index 82114cdc8330..000000000000
--- a/drivers/mtd/nand/gpmi-nand/gpmi-regs.h
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Freescale GPMI NAND Flash Driver
- *
- * Copyright 2008-2011 Freescale Semiconductor, Inc.
- * Copyright 2008 Embedded Alley Solutions, Inc.
- *
- * 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.
- */
-#ifndef __GPMI_NAND_GPMI_REGS_H
-#define __GPMI_NAND_GPMI_REGS_H
-
-#define HW_GPMI_CTRL0 0x00000000
-#define HW_GPMI_CTRL0_SET 0x00000004
-#define HW_GPMI_CTRL0_CLR 0x00000008
-#define HW_GPMI_CTRL0_TOG 0x0000000c
-
-#define BP_GPMI_CTRL0_COMMAND_MODE 24
-#define BM_GPMI_CTRL0_COMMAND_MODE (3 << BP_GPMI_CTRL0_COMMAND_MODE)
-#define BF_GPMI_CTRL0_COMMAND_MODE(v) \
- (((v) << BP_GPMI_CTRL0_COMMAND_MODE) & BM_GPMI_CTRL0_COMMAND_MODE)
-#define BV_GPMI_CTRL0_COMMAND_MODE__WRITE 0x0
-#define BV_GPMI_CTRL0_COMMAND_MODE__READ 0x1
-#define BV_GPMI_CTRL0_COMMAND_MODE__READ_AND_COMPARE 0x2
-#define BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY 0x3
-
-#define BM_GPMI_CTRL0_WORD_LENGTH (1 << 23)
-#define BV_GPMI_CTRL0_WORD_LENGTH__16_BIT 0x0
-#define BV_GPMI_CTRL0_WORD_LENGTH__8_BIT 0x1
-
-/*
- * Difference in LOCK_CS between imx23 and imx28 :
- * This bit may impact the _POWER_ consumption. So some chips
- * do not set it.
- */
-#define MX23_BP_GPMI_CTRL0_LOCK_CS 22
-#define MX28_BP_GPMI_CTRL0_LOCK_CS 27
-#define LOCK_CS_ENABLE 0x1
-#define BF_GPMI_CTRL0_LOCK_CS(v, x) 0x0
-
-/* Difference in CS between imx23 and imx28 */
-#define BP_GPMI_CTRL0_CS 20
-#define MX23_BM_GPMI_CTRL0_CS (3 << BP_GPMI_CTRL0_CS)
-#define MX28_BM_GPMI_CTRL0_CS (7 << BP_GPMI_CTRL0_CS)
-#define BF_GPMI_CTRL0_CS(v, x) (((v) << BP_GPMI_CTRL0_CS) & \
- (GPMI_IS_MX23((x)) \
- ? MX23_BM_GPMI_CTRL0_CS \
- : MX28_BM_GPMI_CTRL0_CS))
-
-#define BP_GPMI_CTRL0_ADDRESS 17
-#define BM_GPMI_CTRL0_ADDRESS (3 << BP_GPMI_CTRL0_ADDRESS)
-#define BF_GPMI_CTRL0_ADDRESS(v) \
- (((v) << BP_GPMI_CTRL0_ADDRESS) & BM_GPMI_CTRL0_ADDRESS)
-#define BV_GPMI_CTRL0_ADDRESS__NAND_DATA 0x0
-#define BV_GPMI_CTRL0_ADDRESS__NAND_CLE 0x1
-#define BV_GPMI_CTRL0_ADDRESS__NAND_ALE 0x2
-
-#define BM_GPMI_CTRL0_ADDRESS_INCREMENT (1 << 16)
-#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__DISABLED 0x0
-#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__ENABLED 0x1
-
-#define BP_GPMI_CTRL0_XFER_COUNT 0
-#define BM_GPMI_CTRL0_XFER_COUNT (0xffff << BP_GPMI_CTRL0_XFER_COUNT)
-#define BF_GPMI_CTRL0_XFER_COUNT(v) \
- (((v) << BP_GPMI_CTRL0_XFER_COUNT) & BM_GPMI_CTRL0_XFER_COUNT)
-
-#define HW_GPMI_COMPARE 0x00000010
-
-#define HW_GPMI_ECCCTRL 0x00000020
-#define HW_GPMI_ECCCTRL_SET 0x00000024
-#define HW_GPMI_ECCCTRL_CLR 0x00000028
-#define HW_GPMI_ECCCTRL_TOG 0x0000002c
-
-#define BP_GPMI_ECCCTRL_ECC_CMD 13
-#define BM_GPMI_ECCCTRL_ECC_CMD (3 << BP_GPMI_ECCCTRL_ECC_CMD)
-#define BF_GPMI_ECCCTRL_ECC_CMD(v) \
- (((v) << BP_GPMI_ECCCTRL_ECC_CMD) & BM_GPMI_ECCCTRL_ECC_CMD)
-#define BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE 0x0
-#define BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE 0x1
-
-#define BM_GPMI_ECCCTRL_ENABLE_ECC (1 << 12)
-#define BV_GPMI_ECCCTRL_ENABLE_ECC__ENABLE 0x1
-#define BV_GPMI_ECCCTRL_ENABLE_ECC__DISABLE 0x0
-
-#define BP_GPMI_ECCCTRL_BUFFER_MASK 0
-#define BM_GPMI_ECCCTRL_BUFFER_MASK (0x1ff << BP_GPMI_ECCCTRL_BUFFER_MASK)
-#define BF_GPMI_ECCCTRL_BUFFER_MASK(v) \
- (((v) << BP_GPMI_ECCCTRL_BUFFER_MASK) & BM_GPMI_ECCCTRL_BUFFER_MASK)
-#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY 0x100
-#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE 0x1FF
-
-#define HW_GPMI_ECCCOUNT 0x00000030
-#define HW_GPMI_PAYLOAD 0x00000040
-#define HW_GPMI_AUXILIARY 0x00000050
-#define HW_GPMI_CTRL1 0x00000060
-#define HW_GPMI_CTRL1_SET 0x00000064
-#define HW_GPMI_CTRL1_CLR 0x00000068
-#define HW_GPMI_CTRL1_TOG 0x0000006c
-
-#define BP_GPMI_CTRL1_DECOUPLE_CS 24
-#define BM_GPMI_CTRL1_DECOUPLE_CS (1 << BP_GPMI_CTRL1_DECOUPLE_CS)
-
-#define BP_GPMI_CTRL1_WRN_DLY_SEL 22
-#define BM_GPMI_CTRL1_WRN_DLY_SEL (0x3 << BP_GPMI_CTRL1_WRN_DLY_SEL)
-#define BF_GPMI_CTRL1_WRN_DLY_SEL(v) \
- (((v) << BP_GPMI_CTRL1_WRN_DLY_SEL) & BM_GPMI_CTRL1_WRN_DLY_SEL)
-#define BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS 0x0
-#define BV_GPMI_CTRL1_WRN_DLY_SEL_6_TO_10NS 0x1
-#define BV_GPMI_CTRL1_WRN_DLY_SEL_7_TO_12NS 0x2
-#define BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY 0x3
-
-#define BM_GPMI_CTRL1_BCH_MODE (1 << 18)
-
-#define BP_GPMI_CTRL1_DLL_ENABLE 17
-#define BM_GPMI_CTRL1_DLL_ENABLE (1 << BP_GPMI_CTRL1_DLL_ENABLE)
-
-#define BP_GPMI_CTRL1_HALF_PERIOD 16
-#define BM_GPMI_CTRL1_HALF_PERIOD (1 << BP_GPMI_CTRL1_HALF_PERIOD)
-
-#define BP_GPMI_CTRL1_RDN_DELAY 12
-#define BM_GPMI_CTRL1_RDN_DELAY (0xf << BP_GPMI_CTRL1_RDN_DELAY)
-#define BF_GPMI_CTRL1_RDN_DELAY(v) \
- (((v) << BP_GPMI_CTRL1_RDN_DELAY) & BM_GPMI_CTRL1_RDN_DELAY)
-
-#define BM_GPMI_CTRL1_DEV_RESET (1 << 3)
-#define BV_GPMI_CTRL1_DEV_RESET__ENABLED 0x0
-#define BV_GPMI_CTRL1_DEV_RESET__DISABLED 0x1
-
-#define BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY (1 << 2)
-#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVELOW 0x0
-#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVEHIGH 0x1
-
-#define BM_GPMI_CTRL1_CAMERA_MODE (1 << 1)
-#define BV_GPMI_CTRL1_GPMI_MODE__NAND 0x0
-#define BV_GPMI_CTRL1_GPMI_MODE__ATA 0x1
-
-#define BM_GPMI_CTRL1_GPMI_MODE (1 << 0)
-
-#define HW_GPMI_TIMING0 0x00000070
-
-#define BP_GPMI_TIMING0_ADDRESS_SETUP 16
-#define BM_GPMI_TIMING0_ADDRESS_SETUP (0xff << BP_GPMI_TIMING0_ADDRESS_SETUP)
-#define BF_GPMI_TIMING0_ADDRESS_SETUP(v) \
- (((v) << BP_GPMI_TIMING0_ADDRESS_SETUP) & BM_GPMI_TIMING0_ADDRESS_SETUP)
-
-#define BP_GPMI_TIMING0_DATA_HOLD 8
-#define BM_GPMI_TIMING0_DATA_HOLD (0xff << BP_GPMI_TIMING0_DATA_HOLD)
-#define BF_GPMI_TIMING0_DATA_HOLD(v) \
- (((v) << BP_GPMI_TIMING0_DATA_HOLD) & BM_GPMI_TIMING0_DATA_HOLD)
-
-#define BP_GPMI_TIMING0_DATA_SETUP 0
-#define BM_GPMI_TIMING0_DATA_SETUP (0xff << BP_GPMI_TIMING0_DATA_SETUP)
-#define BF_GPMI_TIMING0_DATA_SETUP(v) \
- (((v) << BP_GPMI_TIMING0_DATA_SETUP) & BM_GPMI_TIMING0_DATA_SETUP)
-
-#define HW_GPMI_TIMING1 0x00000080
-#define BP_GPMI_TIMING1_BUSY_TIMEOUT 16
-#define BM_GPMI_TIMING1_BUSY_TIMEOUT (0xffff << BP_GPMI_TIMING1_BUSY_TIMEOUT)
-#define BF_GPMI_TIMING1_BUSY_TIMEOUT(v) \
- (((v) << BP_GPMI_TIMING1_BUSY_TIMEOUT) & BM_GPMI_TIMING1_BUSY_TIMEOUT)
-
-#define HW_GPMI_TIMING2 0x00000090
-#define HW_GPMI_DATA 0x000000a0
-
-/* MX28 uses this to detect READY. */
-#define HW_GPMI_STAT 0x000000b0
-#define MX28_BP_GPMI_STAT_READY_BUSY 24
-#define MX28_BM_GPMI_STAT_READY_BUSY (0xff << MX28_BP_GPMI_STAT_READY_BUSY)
-#define MX28_BF_GPMI_STAT_READY_BUSY(v) \
- (((v) << MX28_BP_GPMI_STAT_READY_BUSY) & MX28_BM_GPMI_STAT_READY_BUSY)
-
-/* MX23 uses this to detect READY. */
-#define HW_GPMI_DEBUG 0x000000c0
-#define MX23_BP_GPMI_DEBUG_READY0 28
-#define MX23_BM_GPMI_DEBUG_READY0 (1 << MX23_BP_GPMI_DEBUG_READY0)
-#endif
diff --git a/drivers/mtd/nand/hisi504_nand.c b/drivers/mtd/nand/hisi504_nand.c
deleted file mode 100644
index cb862793ab6d..000000000000
--- a/drivers/mtd/nand/hisi504_nand.c
+++ /dev/null
@@ -1,896 +0,0 @@
-/*
- * Hisilicon NAND Flash controller driver
- *
- * Copyright © 2012-2014 HiSilicon Technologies Co., Ltd.
- * http://www.hisilicon.com
- *
- * Author: Zhou Wang <wangzhou.bry@gmail.com>
- * The initial developer of the original code is Zhiyong Cai
- * <caizhiyong@huawei.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.
- */
-#include <linux/of.h>
-#include <linux/mtd/mtd.h>
-#include <linux/sizes.h>
-#include <linux/clk.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/dma-mapping.h>
-#include <linux/platform_device.h>
-#include <linux/mtd/partitions.h>
-
-#define HINFC504_MAX_CHIP (4)
-#define HINFC504_W_LATCH (5)
-#define HINFC504_R_LATCH (7)
-#define HINFC504_RW_LATCH (3)
-
-#define HINFC504_NFC_TIMEOUT (2 * HZ)
-#define HINFC504_NFC_PM_TIMEOUT (1 * HZ)
-#define HINFC504_NFC_DMA_TIMEOUT (5 * HZ)
-#define HINFC504_CHIP_DELAY (25)
-
-#define HINFC504_REG_BASE_ADDRESS_LEN (0x100)
-#define HINFC504_BUFFER_BASE_ADDRESS_LEN (2048 + 128)
-
-#define HINFC504_ADDR_CYCLE_MASK 0x4
-
-#define HINFC504_CON 0x00
-#define HINFC504_CON_OP_MODE_NORMAL BIT(0)
-#define HINFC504_CON_PAGEISZE_SHIFT (1)
-#define HINFC504_CON_PAGESIZE_MASK (0x07)
-#define HINFC504_CON_BUS_WIDTH BIT(4)
-#define HINFC504_CON_READY_BUSY_SEL BIT(8)
-#define HINFC504_CON_ECCTYPE_SHIFT (9)
-#define HINFC504_CON_ECCTYPE_MASK (0x07)
-
-#define HINFC504_PWIDTH 0x04
-#define SET_HINFC504_PWIDTH(_w_lcnt, _r_lcnt, _rw_hcnt) \
- ((_w_lcnt) | (((_r_lcnt) & 0x0F) << 4) | (((_rw_hcnt) & 0x0F) << 8))
-
-#define HINFC504_CMD 0x0C
-#define HINFC504_ADDRL 0x10
-#define HINFC504_ADDRH 0x14
-#define HINFC504_DATA_NUM 0x18
-
-#define HINFC504_OP 0x1C
-#define HINFC504_OP_READ_DATA_EN BIT(1)
-#define HINFC504_OP_WAIT_READY_EN BIT(2)
-#define HINFC504_OP_CMD2_EN BIT(3)
-#define HINFC504_OP_WRITE_DATA_EN BIT(4)
-#define HINFC504_OP_ADDR_EN BIT(5)
-#define HINFC504_OP_CMD1_EN BIT(6)
-#define HINFC504_OP_NF_CS_SHIFT (7)
-#define HINFC504_OP_NF_CS_MASK (3)
-#define HINFC504_OP_ADDR_CYCLE_SHIFT (9)
-#define HINFC504_OP_ADDR_CYCLE_MASK (7)
-
-#define HINFC504_STATUS 0x20
-#define HINFC504_READY BIT(0)
-
-#define HINFC504_INTEN 0x24
-#define HINFC504_INTEN_DMA BIT(9)
-#define HINFC504_INTEN_UE BIT(6)
-#define HINFC504_INTEN_CE BIT(5)
-
-#define HINFC504_INTS 0x28
-#define HINFC504_INTS_DMA BIT(9)
-#define HINFC504_INTS_UE BIT(6)
-#define HINFC504_INTS_CE BIT(5)
-
-#define HINFC504_INTCLR 0x2C
-#define HINFC504_INTCLR_DMA BIT(9)
-#define HINFC504_INTCLR_UE BIT(6)
-#define HINFC504_INTCLR_CE BIT(5)
-
-#define HINFC504_ECC_STATUS 0x5C
-#define HINFC504_ECC_16_BIT_SHIFT 12
-
-#define HINFC504_DMA_CTRL 0x60
-#define HINFC504_DMA_CTRL_DMA_START BIT(0)
-#define HINFC504_DMA_CTRL_WE BIT(1)
-#define HINFC504_DMA_CTRL_DATA_AREA_EN BIT(2)
-#define HINFC504_DMA_CTRL_OOB_AREA_EN BIT(3)
-#define HINFC504_DMA_CTRL_BURST4_EN BIT(4)
-#define HINFC504_DMA_CTRL_BURST8_EN BIT(5)
-#define HINFC504_DMA_CTRL_BURST16_EN BIT(6)
-#define HINFC504_DMA_CTRL_ADDR_NUM_SHIFT (7)
-#define HINFC504_DMA_CTRL_ADDR_NUM_MASK (1)
-#define HINFC504_DMA_CTRL_CS_SHIFT (8)
-#define HINFC504_DMA_CTRL_CS_MASK (0x03)
-
-#define HINFC504_DMA_ADDR_DATA 0x64
-#define HINFC504_DMA_ADDR_OOB 0x68
-
-#define HINFC504_DMA_LEN 0x6C
-#define HINFC504_DMA_LEN_OOB_SHIFT (16)
-#define HINFC504_DMA_LEN_OOB_MASK (0xFFF)
-
-#define HINFC504_DMA_PARA 0x70
-#define HINFC504_DMA_PARA_DATA_RW_EN BIT(0)
-#define HINFC504_DMA_PARA_OOB_RW_EN BIT(1)
-#define HINFC504_DMA_PARA_DATA_EDC_EN BIT(2)
-#define HINFC504_DMA_PARA_OOB_EDC_EN BIT(3)
-#define HINFC504_DMA_PARA_DATA_ECC_EN BIT(4)
-#define HINFC504_DMA_PARA_OOB_ECC_EN BIT(5)
-
-#define HINFC_VERSION 0x74
-#define HINFC504_LOG_READ_ADDR 0x7C
-#define HINFC504_LOG_READ_LEN 0x80
-
-#define HINFC504_NANDINFO_LEN 0x10
-
-struct hinfc_host {
- struct nand_chip chip;
- struct device *dev;
- void __iomem *iobase;
- void __iomem *mmio;
- struct completion cmd_complete;
- unsigned int offset;
- unsigned int command;
- int chipselect;
- unsigned int addr_cycle;
- u32 addr_value[2];
- u32 cache_addr_value[2];
- char *buffer;
- dma_addr_t dma_buffer;
- dma_addr_t dma_oob;
- int version;
- unsigned int irq_status; /* interrupt status */
-};
-
-static inline unsigned int hinfc_read(struct hinfc_host *host, unsigned int reg)
-{
- return readl(host->iobase + reg);
-}
-
-static inline void hinfc_write(struct hinfc_host *host, unsigned int value,
- unsigned int reg)
-{
- writel(value, host->iobase + reg);
-}
-
-static void wait_controller_finished(struct hinfc_host *host)
-{
- unsigned long timeout = jiffies + HINFC504_NFC_TIMEOUT;
- int val;
-
- while (time_before(jiffies, timeout)) {
- val = hinfc_read(host, HINFC504_STATUS);
- if (host->command == NAND_CMD_ERASE2) {
- /* nfc is ready */
- while (!(val & HINFC504_READY)) {
- usleep_range(500, 1000);
- val = hinfc_read(host, HINFC504_STATUS);
- }
- return;
- }
-
- if (val & HINFC504_READY)
- return;
- }
-
- /* wait cmd timeout */
- dev_err(host->dev, "Wait NAND controller exec cmd timeout.\n");
-}
-
-static void hisi_nfc_dma_transfer(struct hinfc_host *host, int todev)
-{
- struct nand_chip *chip = &host->chip;
- struct mtd_info *mtd = nand_to_mtd(chip);
- unsigned long val;
- int ret;
-
- hinfc_write(host, host->dma_buffer, HINFC504_DMA_ADDR_DATA);
- hinfc_write(host, host->dma_oob, HINFC504_DMA_ADDR_OOB);
-
- if (chip->ecc.mode == NAND_ECC_NONE) {
- hinfc_write(host, ((mtd->oobsize & HINFC504_DMA_LEN_OOB_MASK)
- << HINFC504_DMA_LEN_OOB_SHIFT), HINFC504_DMA_LEN);
-
- hinfc_write(host, HINFC504_DMA_PARA_DATA_RW_EN
- | HINFC504_DMA_PARA_OOB_RW_EN, HINFC504_DMA_PARA);
- } else {
- if (host->command == NAND_CMD_READOOB)
- hinfc_write(host, HINFC504_DMA_PARA_OOB_RW_EN
- | HINFC504_DMA_PARA_OOB_EDC_EN
- | HINFC504_DMA_PARA_OOB_ECC_EN, HINFC504_DMA_PARA);
- else
- hinfc_write(host, HINFC504_DMA_PARA_DATA_RW_EN
- | HINFC504_DMA_PARA_OOB_RW_EN
- | HINFC504_DMA_PARA_DATA_EDC_EN
- | HINFC504_DMA_PARA_OOB_EDC_EN
- | HINFC504_DMA_PARA_DATA_ECC_EN
- | HINFC504_DMA_PARA_OOB_ECC_EN, HINFC504_DMA_PARA);
-
- }
-
- val = (HINFC504_DMA_CTRL_DMA_START | HINFC504_DMA_CTRL_BURST4_EN
- | HINFC504_DMA_CTRL_BURST8_EN | HINFC504_DMA_CTRL_BURST16_EN
- | HINFC504_DMA_CTRL_DATA_AREA_EN | HINFC504_DMA_CTRL_OOB_AREA_EN
- | ((host->addr_cycle == 4 ? 1 : 0)
- << HINFC504_DMA_CTRL_ADDR_NUM_SHIFT)
- | ((host->chipselect & HINFC504_DMA_CTRL_CS_MASK)
- << HINFC504_DMA_CTRL_CS_SHIFT));
-
- if (todev)
- val |= HINFC504_DMA_CTRL_WE;
-
- init_completion(&host->cmd_complete);
-
- hinfc_write(host, val, HINFC504_DMA_CTRL);
- ret = wait_for_completion_timeout(&host->cmd_complete,
- HINFC504_NFC_DMA_TIMEOUT);
-
- if (!ret) {
- dev_err(host->dev, "DMA operation(irq) timeout!\n");
- /* sanity check */
- val = hinfc_read(host, HINFC504_DMA_CTRL);
- if (!(val & HINFC504_DMA_CTRL_DMA_START))
- dev_err(host->dev, "DMA is already done but without irq ACK!\n");
- else
- dev_err(host->dev, "DMA is really timeout!\n");
- }
-}
-
-static int hisi_nfc_send_cmd_pageprog(struct hinfc_host *host)
-{
- host->addr_value[0] &= 0xffff0000;
-
- hinfc_write(host, host->addr_value[0], HINFC504_ADDRL);
- hinfc_write(host, host->addr_value[1], HINFC504_ADDRH);
- hinfc_write(host, NAND_CMD_PAGEPROG << 8 | NAND_CMD_SEQIN,
- HINFC504_CMD);
-
- hisi_nfc_dma_transfer(host, 1);
-
- return 0;
-}
-
-static int hisi_nfc_send_cmd_readstart(struct hinfc_host *host)
-{
- struct mtd_info *mtd = nand_to_mtd(&host->chip);
-
- if ((host->addr_value[0] == host->cache_addr_value[0]) &&
- (host->addr_value[1] == host->cache_addr_value[1]))
- return 0;
-
- host->addr_value[0] &= 0xffff0000;
-
- hinfc_write(host, host->addr_value[0], HINFC504_ADDRL);
- hinfc_write(host, host->addr_value[1], HINFC504_ADDRH);
- hinfc_write(host, NAND_CMD_READSTART << 8 | NAND_CMD_READ0,
- HINFC504_CMD);
-
- hinfc_write(host, 0, HINFC504_LOG_READ_ADDR);
- hinfc_write(host, mtd->writesize + mtd->oobsize,
- HINFC504_LOG_READ_LEN);
-
- hisi_nfc_dma_transfer(host, 0);
-
- host->cache_addr_value[0] = host->addr_value[0];
- host->cache_addr_value[1] = host->addr_value[1];
-
- return 0;
-}
-
-static int hisi_nfc_send_cmd_erase(struct hinfc_host *host)
-{
- hinfc_write(host, host->addr_value[0], HINFC504_ADDRL);
- hinfc_write(host, (NAND_CMD_ERASE2 << 8) | NAND_CMD_ERASE1,
- HINFC504_CMD);
-
- hinfc_write(host, HINFC504_OP_WAIT_READY_EN
- | HINFC504_OP_CMD2_EN
- | HINFC504_OP_CMD1_EN
- | HINFC504_OP_ADDR_EN
- | ((host->chipselect & HINFC504_OP_NF_CS_MASK)
- << HINFC504_OP_NF_CS_SHIFT)
- | ((host->addr_cycle & HINFC504_OP_ADDR_CYCLE_MASK)
- << HINFC504_OP_ADDR_CYCLE_SHIFT),
- HINFC504_OP);
-
- wait_controller_finished(host);
-
- return 0;
-}
-
-static int hisi_nfc_send_cmd_readid(struct hinfc_host *host)
-{
- hinfc_write(host, HINFC504_NANDINFO_LEN, HINFC504_DATA_NUM);
- hinfc_write(host, NAND_CMD_READID, HINFC504_CMD);
- hinfc_write(host, 0, HINFC504_ADDRL);
-
- hinfc_write(host, HINFC504_OP_CMD1_EN | HINFC504_OP_ADDR_EN
- | HINFC504_OP_READ_DATA_EN
- | ((host->chipselect & HINFC504_OP_NF_CS_MASK)
- << HINFC504_OP_NF_CS_SHIFT)
- | 1 << HINFC504_OP_ADDR_CYCLE_SHIFT, HINFC504_OP);
-
- wait_controller_finished(host);
-
- return 0;
-}
-
-static int hisi_nfc_send_cmd_status(struct hinfc_host *host)
-{
- hinfc_write(host, HINFC504_NANDINFO_LEN, HINFC504_DATA_NUM);
- hinfc_write(host, NAND_CMD_STATUS, HINFC504_CMD);
- hinfc_write(host, HINFC504_OP_CMD1_EN
- | HINFC504_OP_READ_DATA_EN
- | ((host->chipselect & HINFC504_OP_NF_CS_MASK)
- << HINFC504_OP_NF_CS_SHIFT),
- HINFC504_OP);
-
- wait_controller_finished(host);
-
- return 0;
-}
-
-static int hisi_nfc_send_cmd_reset(struct hinfc_host *host, int chipselect)
-{
- hinfc_write(host, NAND_CMD_RESET, HINFC504_CMD);
-
- hinfc_write(host, HINFC504_OP_CMD1_EN
- | ((chipselect & HINFC504_OP_NF_CS_MASK)
- << HINFC504_OP_NF_CS_SHIFT)
- | HINFC504_OP_WAIT_READY_EN,
- HINFC504_OP);
-
- wait_controller_finished(host);
-
- return 0;
-}
-
-static void hisi_nfc_select_chip(struct mtd_info *mtd, int chipselect)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct hinfc_host *host = nand_get_controller_data(chip);
-
- if (chipselect < 0)
- return;
-
- host->chipselect = chipselect;
-}
-
-static uint8_t hisi_nfc_read_byte(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct hinfc_host *host = nand_get_controller_data(chip);
-
- if (host->command == NAND_CMD_STATUS)
- return *(uint8_t *)(host->mmio);
-
- host->offset++;
-
- if (host->command == NAND_CMD_READID)
- return *(uint8_t *)(host->mmio + host->offset - 1);
-
- return *(uint8_t *)(host->buffer + host->offset - 1);
-}
-
-static u16 hisi_nfc_read_word(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct hinfc_host *host = nand_get_controller_data(chip);
-
- host->offset += 2;
- return *(u16 *)(host->buffer + host->offset - 2);
-}
-
-static void
-hisi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct hinfc_host *host = nand_get_controller_data(chip);
-
- memcpy(host->buffer + host->offset, buf, len);
- host->offset += len;
-}
-
-static void hisi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct hinfc_host *host = nand_get_controller_data(chip);
-
- memcpy(buf, host->buffer + host->offset, len);
- host->offset += len;
-}
-
-static void set_addr(struct mtd_info *mtd, int column, int page_addr)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct hinfc_host *host = nand_get_controller_data(chip);
- unsigned int command = host->command;
-
- host->addr_cycle = 0;
- host->addr_value[0] = 0;
- host->addr_value[1] = 0;
-
- /* Serially input address */
- if (column != -1) {
- /* Adjust columns for 16 bit buswidth */
- if (chip->options & NAND_BUSWIDTH_16 &&
- !nand_opcode_8bits(command))
- column >>= 1;
-
- host->addr_value[0] = column & 0xffff;
- host->addr_cycle = 2;
- }
- if (page_addr != -1) {
- host->addr_value[0] |= (page_addr & 0xffff)
- << (host->addr_cycle * 8);
- host->addr_cycle += 2;
- if (chip->options & NAND_ROW_ADDR_3) {
- host->addr_cycle += 1;
- if (host->command == NAND_CMD_ERASE1)
- host->addr_value[0] |= ((page_addr >> 16) & 0xff) << 16;
- else
- host->addr_value[1] |= ((page_addr >> 16) & 0xff);
- }
- }
-}
-
-static void hisi_nfc_cmdfunc(struct mtd_info *mtd, unsigned command, int column,
- int page_addr)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct hinfc_host *host = nand_get_controller_data(chip);
- int is_cache_invalid = 1;
- unsigned int flag = 0;
-
- host->command = command;
-
- switch (command) {
- case NAND_CMD_READ0:
- case NAND_CMD_READOOB:
- if (command == NAND_CMD_READ0)
- host->offset = column;
- else
- host->offset = column + mtd->writesize;
-
- is_cache_invalid = 0;
- set_addr(mtd, column, page_addr);
- hisi_nfc_send_cmd_readstart(host);
- break;
-
- case NAND_CMD_SEQIN:
- host->offset = column;
- set_addr(mtd, column, page_addr);
- break;
-
- case NAND_CMD_ERASE1:
- set_addr(mtd, column, page_addr);
- break;
-
- case NAND_CMD_PAGEPROG:
- hisi_nfc_send_cmd_pageprog(host);
- break;
-
- case NAND_CMD_ERASE2:
- hisi_nfc_send_cmd_erase(host);
- break;
-
- case NAND_CMD_READID:
- host->offset = column;
- memset(host->mmio, 0, 0x10);
- hisi_nfc_send_cmd_readid(host);
- break;
-
- case NAND_CMD_STATUS:
- flag = hinfc_read(host, HINFC504_CON);
- if (chip->ecc.mode == NAND_ECC_HW)
- hinfc_write(host,
- flag & ~(HINFC504_CON_ECCTYPE_MASK <<
- HINFC504_CON_ECCTYPE_SHIFT), HINFC504_CON);
-
- host->offset = 0;
- memset(host->mmio, 0, 0x10);
- hisi_nfc_send_cmd_status(host);
- hinfc_write(host, flag, HINFC504_CON);
- break;
-
- case NAND_CMD_RESET:
- hisi_nfc_send_cmd_reset(host, host->chipselect);
- break;
-
- default:
- dev_err(host->dev, "Error: unsupported cmd(cmd=%x, col=%x, page=%x)\n",
- command, column, page_addr);
- }
-
- if (is_cache_invalid) {
- host->cache_addr_value[0] = ~0;
- host->cache_addr_value[1] = ~0;
- }
-}
-
-static irqreturn_t hinfc_irq_handle(int irq, void *devid)
-{
- struct hinfc_host *host = devid;
- unsigned int flag;
-
- flag = hinfc_read(host, HINFC504_INTS);
- /* store interrupts state */
- host->irq_status |= flag;
-
- if (flag & HINFC504_INTS_DMA) {
- hinfc_write(host, HINFC504_INTCLR_DMA, HINFC504_INTCLR);
- complete(&host->cmd_complete);
- } else if (flag & HINFC504_INTS_CE) {
- hinfc_write(host, HINFC504_INTCLR_CE, HINFC504_INTCLR);
- } else if (flag & HINFC504_INTS_UE) {
- hinfc_write(host, HINFC504_INTCLR_UE, HINFC504_INTCLR);
- }
-
- return IRQ_HANDLED;
-}
-
-static int hisi_nand_read_page_hwecc(struct mtd_info *mtd,
- struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
-{
- struct hinfc_host *host = nand_get_controller_data(chip);
- int max_bitflips = 0, stat = 0, stat_max = 0, status_ecc;
- int stat_1, stat_2;
-
- nand_read_page_op(chip, page, 0, buf, mtd->writesize);
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
-
- /* errors which can not be corrected by ECC */
- if (host->irq_status & HINFC504_INTS_UE) {
- mtd->ecc_stats.failed++;
- } else if (host->irq_status & HINFC504_INTS_CE) {
- /* TODO: need add other ECC modes! */
- switch (chip->ecc.strength) {
- case 16:
- status_ecc = hinfc_read(host, HINFC504_ECC_STATUS) >>
- HINFC504_ECC_16_BIT_SHIFT & 0x0fff;
- stat_2 = status_ecc & 0x3f;
- stat_1 = status_ecc >> 6 & 0x3f;
- stat = stat_1 + stat_2;
- stat_max = max_t(int, stat_1, stat_2);
- }
- mtd->ecc_stats.corrected += stat;
- max_bitflips = max_t(int, max_bitflips, stat_max);
- }
- host->irq_status = 0;
-
- return max_bitflips;
-}
-
-static int hisi_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
- int page)
-{
- struct hinfc_host *host = nand_get_controller_data(chip);
-
- nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize);
-
- if (host->irq_status & HINFC504_INTS_UE) {
- host->irq_status = 0;
- return -EBADMSG;
- }
-
- host->irq_status = 0;
- return 0;
-}
-
-static int hisi_nand_write_page_hwecc(struct mtd_info *mtd,
- struct nand_chip *chip, const uint8_t *buf, int oob_required,
- int page)
-{
- nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);
- if (oob_required)
- chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
-
- return nand_prog_page_end_op(chip);
-}
-
-static void hisi_nfc_host_init(struct hinfc_host *host)
-{
- struct nand_chip *chip = &host->chip;
- unsigned int flag = 0;
-
- host->version = hinfc_read(host, HINFC_VERSION);
- host->addr_cycle = 0;
- host->addr_value[0] = 0;
- host->addr_value[1] = 0;
- host->cache_addr_value[0] = ~0;
- host->cache_addr_value[1] = ~0;
- host->chipselect = 0;
-
- /* default page size: 2K, ecc_none. need modify */
- flag = HINFC504_CON_OP_MODE_NORMAL | HINFC504_CON_READY_BUSY_SEL
- | ((0x001 & HINFC504_CON_PAGESIZE_MASK)
- << HINFC504_CON_PAGEISZE_SHIFT)
- | ((0x0 & HINFC504_CON_ECCTYPE_MASK)
- << HINFC504_CON_ECCTYPE_SHIFT)
- | ((chip->options & NAND_BUSWIDTH_16) ?
- HINFC504_CON_BUS_WIDTH : 0);
- hinfc_write(host, flag, HINFC504_CON);
-
- memset(host->mmio, 0xff, HINFC504_BUFFER_BASE_ADDRESS_LEN);
-
- hinfc_write(host, SET_HINFC504_PWIDTH(HINFC504_W_LATCH,
- HINFC504_R_LATCH, HINFC504_RW_LATCH), HINFC504_PWIDTH);
-
- /* enable DMA irq */
- hinfc_write(host, HINFC504_INTEN_DMA, HINFC504_INTEN);
-}
-
-static int hisi_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- /* FIXME: add ECC bytes position */
- return -ENOTSUPP;
-}
-
-static int hisi_ooblayout_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- if (section)
- return -ERANGE;
-
- oobregion->offset = 2;
- oobregion->length = 6;
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops hisi_ooblayout_ops = {
- .ecc = hisi_ooblayout_ecc,
- .free = hisi_ooblayout_free,
-};
-
-static int hisi_nfc_ecc_probe(struct hinfc_host *host)
-{
- unsigned int flag;
- int size, strength, ecc_bits;
- struct device *dev = host->dev;
- struct nand_chip *chip = &host->chip;
- struct mtd_info *mtd = nand_to_mtd(chip);
-
- size = chip->ecc.size;
- strength = chip->ecc.strength;
- if (size != 1024) {
- dev_err(dev, "error ecc size: %d\n", size);
- return -EINVAL;
- }
-
- if ((size == 1024) && ((strength != 8) && (strength != 16) &&
- (strength != 24) && (strength != 40))) {
- dev_err(dev, "ecc size and strength do not match\n");
- return -EINVAL;
- }
-
- chip->ecc.size = size;
- chip->ecc.strength = strength;
-
- chip->ecc.read_page = hisi_nand_read_page_hwecc;
- chip->ecc.read_oob = hisi_nand_read_oob;
- chip->ecc.write_page = hisi_nand_write_page_hwecc;
-
- switch (chip->ecc.strength) {
- case 16:
- ecc_bits = 6;
- if (mtd->writesize == 2048)
- mtd_set_ooblayout(mtd, &hisi_ooblayout_ops);
-
- /* TODO: add more page size support */
- break;
-
- /* TODO: add more ecc strength support */
- default:
- dev_err(dev, "not support strength: %d\n", chip->ecc.strength);
- return -EINVAL;
- }
-
- flag = hinfc_read(host, HINFC504_CON);
- /* add ecc type configure */
- flag |= ((ecc_bits & HINFC504_CON_ECCTYPE_MASK)
- << HINFC504_CON_ECCTYPE_SHIFT);
- hinfc_write(host, flag, HINFC504_CON);
-
- /* enable ecc irq */
- flag = hinfc_read(host, HINFC504_INTEN) & 0xfff;
- hinfc_write(host, flag | HINFC504_INTEN_UE | HINFC504_INTEN_CE,
- HINFC504_INTEN);
-
- return 0;
-}
-
-static int hisi_nfc_probe(struct platform_device *pdev)
-{
- int ret = 0, irq, flag, max_chips = HINFC504_MAX_CHIP;
- struct device *dev = &pdev->dev;
- struct hinfc_host *host;
- struct nand_chip *chip;
- struct mtd_info *mtd;
- struct resource *res;
- struct device_node *np = dev->of_node;
-
- host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
- if (!host)
- return -ENOMEM;
- host->dev = dev;
-
- platform_set_drvdata(pdev, host);
- chip = &host->chip;
- mtd = nand_to_mtd(chip);
-
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(dev, "no IRQ resource defined\n");
- ret = -ENXIO;
- goto err_res;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- host->iobase = devm_ioremap_resource(dev, res);
- if (IS_ERR(host->iobase)) {
- ret = PTR_ERR(host->iobase);
- goto err_res;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- host->mmio = devm_ioremap_resource(dev, res);
- if (IS_ERR(host->mmio)) {
- ret = PTR_ERR(host->mmio);
- dev_err(dev, "devm_ioremap_resource[1] fail\n");
- goto err_res;
- }
-
- mtd->name = "hisi_nand";
- mtd->dev.parent = &pdev->dev;
-
- nand_set_controller_data(chip, host);
- nand_set_flash_node(chip, np);
- chip->cmdfunc = hisi_nfc_cmdfunc;
- chip->select_chip = hisi_nfc_select_chip;
- chip->read_byte = hisi_nfc_read_byte;
- chip->read_word = hisi_nfc_read_word;
- chip->write_buf = hisi_nfc_write_buf;
- chip->read_buf = hisi_nfc_read_buf;
- chip->chip_delay = HINFC504_CHIP_DELAY;
- chip->onfi_set_features = nand_onfi_get_set_features_notsupp;
- chip->onfi_get_features = nand_onfi_get_set_features_notsupp;
-
- hisi_nfc_host_init(host);
-
- ret = devm_request_irq(dev, irq, hinfc_irq_handle, 0x0, "nandc", host);
- if (ret) {
- dev_err(dev, "failed to request IRQ\n");
- goto err_res;
- }
-
- ret = nand_scan_ident(mtd, max_chips, NULL);
- if (ret)
- goto err_res;
-
- host->buffer = dmam_alloc_coherent(dev, mtd->writesize + mtd->oobsize,
- &host->dma_buffer, GFP_KERNEL);
- if (!host->buffer) {
- ret = -ENOMEM;
- goto err_res;
- }
-
- host->dma_oob = host->dma_buffer + mtd->writesize;
- memset(host->buffer, 0xff, mtd->writesize + mtd->oobsize);
-
- flag = hinfc_read(host, HINFC504_CON);
- flag &= ~(HINFC504_CON_PAGESIZE_MASK << HINFC504_CON_PAGEISZE_SHIFT);
- switch (mtd->writesize) {
- case 2048:
- flag |= (0x001 << HINFC504_CON_PAGEISZE_SHIFT); break;
- /*
- * TODO: add more pagesize support,
- * default pagesize has been set in hisi_nfc_host_init
- */
- default:
- dev_err(dev, "NON-2KB page size nand flash\n");
- ret = -EINVAL;
- goto err_res;
- }
- hinfc_write(host, flag, HINFC504_CON);
-
- if (chip->ecc.mode == NAND_ECC_HW)
- hisi_nfc_ecc_probe(host);
-
- ret = nand_scan_tail(mtd);
- if (ret) {
- dev_err(dev, "nand_scan_tail failed: %d\n", ret);
- goto err_res;
- }
-
- ret = mtd_device_register(mtd, NULL, 0);
- if (ret) {
- dev_err(dev, "Err MTD partition=%d\n", ret);
- goto err_mtd;
- }
-
- return 0;
-
-err_mtd:
- nand_release(mtd);
-err_res:
- return ret;
-}
-
-static int hisi_nfc_remove(struct platform_device *pdev)
-{
- struct hinfc_host *host = platform_get_drvdata(pdev);
- struct mtd_info *mtd = nand_to_mtd(&host->chip);
-
- nand_release(mtd);
-
- return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int hisi_nfc_suspend(struct device *dev)
-{
- struct hinfc_host *host = dev_get_drvdata(dev);
- unsigned long timeout = jiffies + HINFC504_NFC_PM_TIMEOUT;
-
- while (time_before(jiffies, timeout)) {
- if (((hinfc_read(host, HINFC504_STATUS) & 0x1) == 0x0) &&
- (hinfc_read(host, HINFC504_DMA_CTRL) &
- HINFC504_DMA_CTRL_DMA_START)) {
- cond_resched();
- return 0;
- }
- }
-
- dev_err(host->dev, "nand controller suspend timeout.\n");
-
- return -EAGAIN;
-}
-
-static int hisi_nfc_resume(struct device *dev)
-{
- int cs;
- struct hinfc_host *host = dev_get_drvdata(dev);
- struct nand_chip *chip = &host->chip;
-
- for (cs = 0; cs < chip->numchips; cs++)
- hisi_nfc_send_cmd_reset(host, cs);
- hinfc_write(host, SET_HINFC504_PWIDTH(HINFC504_W_LATCH,
- HINFC504_R_LATCH, HINFC504_RW_LATCH), HINFC504_PWIDTH);
-
- return 0;
-}
-#endif
-static SIMPLE_DEV_PM_OPS(hisi_nfc_pm_ops, hisi_nfc_suspend, hisi_nfc_resume);
-
-static const struct of_device_id nfc_id_table[] = {
- { .compatible = "hisilicon,504-nfc" },
- {}
-};
-MODULE_DEVICE_TABLE(of, nfc_id_table);
-
-static struct platform_driver hisi_nfc_driver = {
- .driver = {
- .name = "hisi_nand",
- .of_match_table = nfc_id_table,
- .pm = &hisi_nfc_pm_ops,
- },
- .probe = hisi_nfc_probe,
- .remove = hisi_nfc_remove,
-};
-
-module_platform_driver(hisi_nfc_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Zhou Wang");
-MODULE_AUTHOR("Zhiyong Cai");
-MODULE_DESCRIPTION("Hisilicon Nand Flash Controller Driver");
diff --git a/drivers/mtd/nand/marvell_nand.c b/drivers/mtd/nand/marvell_nand.c
deleted file mode 100644
index 2196f2a233d6..000000000000
--- a/drivers/mtd/nand/marvell_nand.c
+++ /dev/null
@@ -1,2896 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Marvell NAND flash controller driver
- *
- * Copyright (C) 2017 Marvell
- * Author: Miquel RAYNAL <miquel.raynal@free-electrons.com>
- *
- */
-
-#include <linux/module.h>
-#include <linux/clk.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/of_platform.h>
-#include <linux/iopoll.h>
-#include <linux/interrupt.h>
-#include <linux/slab.h>
-#include <linux/mfd/syscon.h>
-#include <linux/regmap.h>
-#include <asm/unaligned.h>
-
-#include <linux/dmaengine.h>
-#include <linux/dma-mapping.h>
-#include <linux/dma/pxa-dma.h>
-#include <linux/platform_data/mtd-nand-pxa3xx.h>
-
-/* Data FIFO granularity, FIFO reads/writes must be a multiple of this length */
-#define FIFO_DEPTH 8
-#define FIFO_REP(x) (x / sizeof(u32))
-#define BCH_SEQ_READS (32 / FIFO_DEPTH)
-/* NFC does not support transfers of larger chunks at a time */
-#define MAX_CHUNK_SIZE 2112
-/* NFCv1 cannot read more that 7 bytes of ID */
-#define NFCV1_READID_LEN 7
-/* Polling is done at a pace of POLL_PERIOD us until POLL_TIMEOUT is reached */
-#define POLL_PERIOD 0
-#define POLL_TIMEOUT 100000
-/* Interrupt maximum wait period in ms */
-#define IRQ_TIMEOUT 1000
-/* Latency in clock cycles between SoC pins and NFC logic */
-#define MIN_RD_DEL_CNT 3
-/* Maximum number of contiguous address cycles */
-#define MAX_ADDRESS_CYC_NFCV1 5
-#define MAX_ADDRESS_CYC_NFCV2 7
-/* System control registers/bits to enable the NAND controller on some SoCs */
-#define GENCONF_SOC_DEVICE_MUX 0x208
-#define GENCONF_SOC_DEVICE_MUX_NFC_EN BIT(0)
-#define GENCONF_SOC_DEVICE_MUX_ECC_CLK_RST BIT(20)
-#define GENCONF_SOC_DEVICE_MUX_ECC_CORE_RST BIT(21)
-#define GENCONF_SOC_DEVICE_MUX_NFC_INT_EN BIT(25)
-#define GENCONF_CLK_GATING_CTRL 0x220
-#define GENCONF_CLK_GATING_CTRL_ND_GATE BIT(2)
-#define GENCONF_ND_CLK_CTRL 0x700
-#define GENCONF_ND_CLK_CTRL_EN BIT(0)
-
-/* NAND controller data flash control register */
-#define NDCR 0x00
-#define NDCR_ALL_INT GENMASK(11, 0)
-#define NDCR_CS1_CMDDM BIT(7)
-#define NDCR_CS0_CMDDM BIT(8)
-#define NDCR_RDYM BIT(11)
-#define NDCR_ND_ARB_EN BIT(12)
-#define NDCR_RA_START BIT(15)
-#define NDCR_RD_ID_CNT(x) (min_t(unsigned int, x, 0x7) << 16)
-#define NDCR_PAGE_SZ(x) (x >= 2048 ? BIT(24) : 0)
-#define NDCR_DWIDTH_M BIT(26)
-#define NDCR_DWIDTH_C BIT(27)
-#define NDCR_ND_RUN BIT(28)
-#define NDCR_DMA_EN BIT(29)
-#define NDCR_ECC_EN BIT(30)
-#define NDCR_SPARE_EN BIT(31)
-#define NDCR_GENERIC_FIELDS_MASK (~(NDCR_RA_START | NDCR_PAGE_SZ(2048) | \
- NDCR_DWIDTH_M | NDCR_DWIDTH_C))
-
-/* NAND interface timing parameter 0 register */
-#define NDTR0 0x04
-#define NDTR0_TRP(x) ((min_t(unsigned int, x, 0xF) & 0x7) << 0)
-#define NDTR0_TRH(x) (min_t(unsigned int, x, 0x7) << 3)
-#define NDTR0_ETRP(x) ((min_t(unsigned int, x, 0xF) & 0x8) << 3)
-#define NDTR0_SEL_NRE_EDGE BIT(7)
-#define NDTR0_TWP(x) (min_t(unsigned int, x, 0x7) << 8)
-#define NDTR0_TWH(x) (min_t(unsigned int, x, 0x7) << 11)
-#define NDTR0_TCS(x) (min_t(unsigned int, x, 0x7) << 16)
-#define NDTR0_TCH(x) (min_t(unsigned int, x, 0x7) << 19)
-#define NDTR0_RD_CNT_DEL(x) (min_t(unsigned int, x, 0xF) << 22)
-#define NDTR0_SELCNTR BIT(26)
-#define NDTR0_TADL(x) (min_t(unsigned int, x, 0x1F) << 27)
-
-/* NAND interface timing parameter 1 register */
-#define NDTR1 0x0C
-#define NDTR1_TAR(x) (min_t(unsigned int, x, 0xF) << 0)
-#define NDTR1_TWHR(x) (min_t(unsigned int, x, 0xF) << 4)
-#define NDTR1_TRHW(x) (min_t(unsigned int, x / 16, 0x3) << 8)
-#define NDTR1_PRESCALE BIT(14)
-#define NDTR1_WAIT_MODE BIT(15)
-#define NDTR1_TR(x) (min_t(unsigned int, x, 0xFFFF) << 16)
-
-/* NAND controller status register */
-#define NDSR 0x14
-#define NDSR_WRCMDREQ BIT(0)
-#define NDSR_RDDREQ BIT(1)
-#define NDSR_WRDREQ BIT(2)
-#define NDSR_CORERR BIT(3)
-#define NDSR_UNCERR BIT(4)
-#define NDSR_CMDD(cs) BIT(8 - cs)
-#define NDSR_RDY(rb) BIT(11 + rb)
-#define NDSR_ERRCNT(x) ((x >> 16) & 0x1F)
-
-/* NAND ECC control register */
-#define NDECCCTRL 0x28
-#define NDECCCTRL_BCH_EN BIT(0)
-
-/* NAND controller data buffer register */
-#define NDDB 0x40
-
-/* NAND controller command buffer 0 register */
-#define NDCB0 0x48
-#define NDCB0_CMD1(x) ((x & 0xFF) << 0)
-#define NDCB0_CMD2(x) ((x & 0xFF) << 8)
-#define NDCB0_ADDR_CYC(x) ((x & 0x7) << 16)
-#define NDCB0_ADDR_GET_NUM_CYC(x) (((x) >> 16) & 0x7)
-#define NDCB0_DBC BIT(19)
-#define NDCB0_CMD_TYPE(x) ((x & 0x7) << 21)
-#define NDCB0_CSEL BIT(24)
-#define NDCB0_RDY_BYP BIT(27)
-#define NDCB0_LEN_OVRD BIT(28)
-#define NDCB0_CMD_XTYPE(x) ((x & 0x7) << 29)
-
-/* NAND controller command buffer 1 register */
-#define NDCB1 0x4C
-#define NDCB1_COLS(x) ((x & 0xFFFF) << 0)
-#define NDCB1_ADDRS_PAGE(x) (x << 16)
-
-/* NAND controller command buffer 2 register */
-#define NDCB2 0x50
-#define NDCB2_ADDR5_PAGE(x) (((x >> 16) & 0xFF) << 0)
-#define NDCB2_ADDR5_CYC(x) ((x & 0xFF) << 0)
-
-/* NAND controller command buffer 3 register */
-#define NDCB3 0x54
-#define NDCB3_ADDR6_CYC(x) ((x & 0xFF) << 16)
-#define NDCB3_ADDR7_CYC(x) ((x & 0xFF) << 24)
-
-/* NAND controller command buffer 0 register 'type' and 'xtype' fields */
-#define TYPE_READ 0
-#define TYPE_WRITE 1
-#define TYPE_ERASE 2
-#define TYPE_READ_ID 3
-#define TYPE_STATUS 4
-#define TYPE_RESET 5
-#define TYPE_NAKED_CMD 6
-#define TYPE_NAKED_ADDR 7
-#define TYPE_MASK 7
-#define XTYPE_MONOLITHIC_RW 0
-#define XTYPE_LAST_NAKED_RW 1
-#define XTYPE_FINAL_COMMAND 3
-#define XTYPE_READ 4
-#define XTYPE_WRITE_DISPATCH 4
-#define XTYPE_NAKED_RW 5
-#define XTYPE_COMMAND_DISPATCH 6
-#define XTYPE_MASK 7
-
-/**
- * Marvell ECC engine works differently than the others, in order to limit the
- * size of the IP, hardware engineers chose to set a fixed strength at 16 bits
- * per subpage, and depending on a the desired strength needed by the NAND chip,
- * a particular layout mixing data/spare/ecc is defined, with a possible last
- * chunk smaller that the others.
- *
- * @writesize: Full page size on which the layout applies
- * @chunk: Desired ECC chunk size on which the layout applies
- * @strength: Desired ECC strength (per chunk size bytes) on which the
- * layout applies
- * @nchunks: Total number of chunks
- * @full_chunk_cnt: Number of full-sized chunks, which is the number of
- * repetitions of the pattern:
- * (data_bytes + spare_bytes + ecc_bytes).
- * @data_bytes: Number of data bytes per chunk
- * @spare_bytes: Number of spare bytes per chunk
- * @ecc_bytes: Number of ecc bytes per chunk
- * @last_data_bytes: Number of data bytes in the last chunk
- * @last_spare_bytes: Number of spare bytes in the last chunk
- * @last_ecc_bytes: Number of ecc bytes in the last chunk
- */
-struct marvell_hw_ecc_layout {
- /* Constraints */
- int writesize;
- int chunk;
- int strength;
- /* Corresponding layout */
- int nchunks;
- int full_chunk_cnt;
- int data_bytes;
- int spare_bytes;
- int ecc_bytes;
- int last_data_bytes;
- int last_spare_bytes;
- int last_ecc_bytes;
-};
-
-#define MARVELL_LAYOUT(ws, dc, ds, nc, fcc, db, sb, eb, ldb, lsb, leb) \
- { \
- .writesize = ws, \
- .chunk = dc, \
- .strength = ds, \
- .nchunks = nc, \
- .full_chunk_cnt = fcc, \
- .data_bytes = db, \
- .spare_bytes = sb, \
- .ecc_bytes = eb, \
- .last_data_bytes = ldb, \
- .last_spare_bytes = lsb, \
- .last_ecc_bytes = leb, \
- }
-
-/* Layouts explained in AN-379_Marvell_SoC_NFC_ECC */
-static const struct marvell_hw_ecc_layout marvell_nfc_layouts[] = {
- MARVELL_LAYOUT( 512, 512, 1, 1, 1, 512, 8, 8, 0, 0, 0),
- MARVELL_LAYOUT( 2048, 512, 1, 1, 1, 2048, 40, 24, 0, 0, 0),
- MARVELL_LAYOUT( 2048, 512, 4, 1, 1, 2048, 32, 30, 0, 0, 0),
- MARVELL_LAYOUT( 4096, 512, 4, 2, 2, 2048, 32, 30, 0, 0, 0),
- MARVELL_LAYOUT( 4096, 512, 8, 5, 4, 1024, 0, 30, 0, 64, 30),
-};
-
-/**
- * The Nand Flash Controller has up to 4 CE and 2 RB pins. The CE selection
- * is made by a field in NDCB0 register, and in another field in NDCB2 register.
- * The datasheet describes the logic with an error: ADDR5 field is once
- * declared at the beginning of NDCB2, and another time at its end. Because the
- * ADDR5 field of NDCB2 may be used by other bytes, it would be more logical
- * to use the last bit of this field instead of the first ones.
- *
- * @cs: Wanted CE lane.
- * @ndcb0_csel: Value of the NDCB0 register with or without the flag
- * selecting the wanted CE lane. This is set once when
- * the Device Tree is probed.
- * @rb: Ready/Busy pin for the flash chip
- */
-struct marvell_nand_chip_sel {
- unsigned int cs;
- u32 ndcb0_csel;
- unsigned int rb;
-};
-
-/**
- * NAND chip structure: stores NAND chip device related information
- *
- * @chip: Base NAND chip structure
- * @node: Used to store NAND chips into a list
- * @layout NAND layout when using hardware ECC
- * @ndcr: Controller register value for this NAND chip
- * @ndtr0: Timing registers 0 value for this NAND chip
- * @ndtr1: Timing registers 1 value for this NAND chip
- * @selected_die: Current active CS
- * @nsels: Number of CS lines required by the NAND chip
- * @sels: Array of CS lines descriptions
- */
-struct marvell_nand_chip {
- struct nand_chip chip;
- struct list_head node;
- const struct marvell_hw_ecc_layout *layout;
- u32 ndcr;
- u32 ndtr0;
- u32 ndtr1;
- int addr_cyc;
- int selected_die;
- unsigned int nsels;
- struct marvell_nand_chip_sel sels[0];
-};
-
-static inline struct marvell_nand_chip *to_marvell_nand(struct nand_chip *chip)
-{
- return container_of(chip, struct marvell_nand_chip, chip);
-}
-
-static inline struct marvell_nand_chip_sel *to_nand_sel(struct marvell_nand_chip
- *nand)
-{
- return &nand->sels[nand->selected_die];
-}
-
-/**
- * NAND controller capabilities for distinction between compatible strings
- *
- * @max_cs_nb: Number of Chip Select lines available
- * @max_rb_nb: Number of Ready/Busy lines available
- * @need_system_controller: Indicates if the SoC needs to have access to the
- * system controller (ie. to enable the NAND controller)
- * @legacy_of_bindings: Indicates if DT parsing must be done using the old
- * fashion way
- * @is_nfcv2: NFCv2 has numerous enhancements compared to NFCv1, ie.
- * BCH error detection and correction algorithm,
- * NDCB3 register has been added
- * @use_dma: Use dma for data transfers
- */
-struct marvell_nfc_caps {
- unsigned int max_cs_nb;
- unsigned int max_rb_nb;
- bool need_system_controller;
- bool legacy_of_bindings;
- bool is_nfcv2;
- bool use_dma;
-};
-
-/**
- * NAND controller structure: stores Marvell NAND controller information
- *
- * @controller: Base controller structure
- * @dev: Parent device (used to print error messages)
- * @regs: NAND controller registers
- * @ecc_clk: ECC block clock, two times the NAND controller clock
- * @complete: Completion object to wait for NAND controller events
- * @assigned_cs: Bitmask describing already assigned CS lines
- * @chips: List containing all the NAND chips attached to
- * this NAND controller
- * @caps: NAND controller capabilities for each compatible string
- * @dma_chan: DMA channel (NFCv1 only)
- * @dma_buf: 32-bit aligned buffer for DMA transfers (NFCv1 only)
- */
-struct marvell_nfc {
- struct nand_hw_control controller;
- struct device *dev;
- void __iomem *regs;
- struct clk *ecc_clk;
- struct completion complete;
- unsigned long assigned_cs;
- struct list_head chips;
- struct nand_chip *selected_chip;
- const struct marvell_nfc_caps *caps;
-
- /* DMA (NFCv1 only) */
- bool use_dma;
- struct dma_chan *dma_chan;
- u8 *dma_buf;
-};
-
-static inline struct marvell_nfc *to_marvell_nfc(struct nand_hw_control *ctrl)
-{
- return container_of(ctrl, struct marvell_nfc, controller);
-}
-
-/**
- * NAND controller timings expressed in NAND Controller clock cycles
- *
- * @tRP: ND_nRE pulse width
- * @tRH: ND_nRE high duration
- * @tWP: ND_nWE pulse time
- * @tWH: ND_nWE high duration
- * @tCS: Enable signal setup time
- * @tCH: Enable signal hold time
- * @tADL: Address to write data delay
- * @tAR: ND_ALE low to ND_nRE low delay
- * @tWHR: ND_nWE high to ND_nRE low for status read
- * @tRHW: ND_nRE high duration, read to write delay
- * @tR: ND_nWE high to ND_nRE low for read
- */
-struct marvell_nfc_timings {
- /* NDTR0 fields */
- unsigned int tRP;
- unsigned int tRH;
- unsigned int tWP;
- unsigned int tWH;
- unsigned int tCS;
- unsigned int tCH;
- unsigned int tADL;
- /* NDTR1 fields */
- unsigned int tAR;
- unsigned int tWHR;
- unsigned int tRHW;
- unsigned int tR;
-};
-
-/**
- * Derives a duration in numbers of clock cycles.
- *
- * @ps: Duration in pico-seconds
- * @period_ns: Clock period in nano-seconds
- *
- * Convert the duration in nano-seconds, then divide by the period and
- * return the number of clock periods.
- */
-#define TO_CYCLES(ps, period_ns) (DIV_ROUND_UP(ps / 1000, period_ns))
-
-/**
- * NAND driver structure filled during the parsing of the ->exec_op() subop
- * subset of instructions.
- *
- * @ndcb: Array of values written to NDCBx registers
- * @cle_ale_delay_ns: Optional delay after the last CMD or ADDR cycle
- * @rdy_timeout_ms: Timeout for waits on Ready/Busy pin
- * @rdy_delay_ns: Optional delay after waiting for the RB pin
- * @data_delay_ns: Optional delay after the data xfer
- * @data_instr_idx: Index of the data instruction in the subop
- * @data_instr: Pointer to the data instruction in the subop
- */
-struct marvell_nfc_op {
- u32 ndcb[4];
- unsigned int cle_ale_delay_ns;
- unsigned int rdy_timeout_ms;
- unsigned int rdy_delay_ns;
- unsigned int data_delay_ns;
- unsigned int data_instr_idx;
- const struct nand_op_instr *data_instr;
-};
-
-/*
- * Internal helper to conditionnally apply a delay (from the above structure,
- * most of the time).
- */
-static void cond_delay(unsigned int ns)
-{
- if (!ns)
- return;
-
- if (ns < 10000)
- ndelay(ns);
- else
- udelay(DIV_ROUND_UP(ns, 1000));
-}
-
-/*
- * The controller has many flags that could generate interrupts, most of them
- * are disabled and polling is used. For the very slow signals, using interrupts
- * may relax the CPU charge.
- */
-static void marvell_nfc_disable_int(struct marvell_nfc *nfc, u32 int_mask)
-{
- u32 reg;
-
- /* Writing 1 disables the interrupt */
- reg = readl_relaxed(nfc->regs + NDCR);
- writel_relaxed(reg | int_mask, nfc->regs + NDCR);
-}
-
-static void marvell_nfc_enable_int(struct marvell_nfc *nfc, u32 int_mask)
-{
- u32 reg;
-
- /* Writing 0 enables the interrupt */
- reg = readl_relaxed(nfc->regs + NDCR);
- writel_relaxed(reg & ~int_mask, nfc->regs + NDCR);
-}
-
-static void marvell_nfc_clear_int(struct marvell_nfc *nfc, u32 int_mask)
-{
- writel_relaxed(int_mask, nfc->regs + NDSR);
-}
-
-static void marvell_nfc_force_byte_access(struct nand_chip *chip,
- bool force_8bit)
-{
- struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
- u32 ndcr;
-
- /*
- * Callers of this function do not verify if the NAND is using a 16-bit
- * an 8-bit bus for normal operations, so we need to take care of that
- * here by leaving the configuration unchanged if the NAND does not have
- * the NAND_BUSWIDTH_16 flag set.
- */
- if (!(chip->options & NAND_BUSWIDTH_16))
- return;
-
- ndcr = readl_relaxed(nfc->regs + NDCR);
-
- if (force_8bit)
- ndcr &= ~(NDCR_DWIDTH_M | NDCR_DWIDTH_C);
- else
- ndcr |= NDCR_DWIDTH_M | NDCR_DWIDTH_C;
-
- writel_relaxed(ndcr, nfc->regs + NDCR);
-}
-
-static int marvell_nfc_wait_ndrun(struct nand_chip *chip)
-{
- struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
- u32 val;
- int ret;
-
- /*
- * The command is being processed, wait for the ND_RUN bit to be
- * cleared by the NFC. If not, we must clear it by hand.
- */
- ret = readl_relaxed_poll_timeout(nfc->regs + NDCR, val,
- (val & NDCR_ND_RUN) == 0,
- POLL_PERIOD, POLL_TIMEOUT);
- if (ret) {
- dev_err(nfc->dev, "Timeout on NAND controller run mode\n");
- writel_relaxed(readl(nfc->regs + NDCR) & ~NDCR_ND_RUN,
- nfc->regs + NDCR);
- return ret;
- }
-
- return 0;
-}
-
-/*
- * Any time a command has to be sent to the controller, the following sequence
- * has to be followed:
- * - call marvell_nfc_prepare_cmd()
- * -> activate the ND_RUN bit that will kind of 'start a job'
- * -> wait the signal indicating the NFC is waiting for a command
- * - send the command (cmd and address cycles)
- * - enventually send or receive the data
- * - call marvell_nfc_end_cmd() with the corresponding flag
- * -> wait the flag to be triggered or cancel the job with a timeout
- *
- * The following helpers are here to factorize the code a bit so that
- * specialized functions responsible for executing the actual NAND
- * operations do not have to replicate the same code blocks.
- */
-static int marvell_nfc_prepare_cmd(struct nand_chip *chip)
-{
- struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
- u32 ndcr, val;
- int ret;
-
- /* Poll ND_RUN and clear NDSR before issuing any command */
- ret = marvell_nfc_wait_ndrun(chip);
- if (ret) {
- dev_err(nfc->dev, "Last operation did not succeed\n");
- return ret;
- }
-
- ndcr = readl_relaxed(nfc->regs + NDCR);
- writel_relaxed(readl(nfc->regs + NDSR), nfc->regs + NDSR);
-
- /* Assert ND_RUN bit and wait the NFC to be ready */
- writel_relaxed(ndcr | NDCR_ND_RUN, nfc->regs + NDCR);
- ret = readl_relaxed_poll_timeout(nfc->regs + NDSR, val,
- val & NDSR_WRCMDREQ,
- POLL_PERIOD, POLL_TIMEOUT);
- if (ret) {
- dev_err(nfc->dev, "Timeout on WRCMDRE\n");
- return -ETIMEDOUT;
- }
-
- /* Command may be written, clear WRCMDREQ status bit */
- writel_relaxed(NDSR_WRCMDREQ, nfc->regs + NDSR);
-
- return 0;
-}
-
-static void marvell_nfc_send_cmd(struct nand_chip *chip,
- struct marvell_nfc_op *nfc_op)
-{
- struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
- struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
-
- dev_dbg(nfc->dev, "\nNDCR: 0x%08x\n"
- "NDCB0: 0x%08x\nNDCB1: 0x%08x\nNDCB2: 0x%08x\nNDCB3: 0x%08x\n",
- (u32)readl_relaxed(nfc->regs + NDCR), nfc_op->ndcb[0],
- nfc_op->ndcb[1], nfc_op->ndcb[2], nfc_op->ndcb[3]);
-
- writel_relaxed(to_nand_sel(marvell_nand)->ndcb0_csel | nfc_op->ndcb[0],
- nfc->regs + NDCB0);
- writel_relaxed(nfc_op->ndcb[1], nfc->regs + NDCB0);
- writel(nfc_op->ndcb[2], nfc->regs + NDCB0);
-
- /*
- * Write NDCB0 four times only if LEN_OVRD is set or if ADDR6 or ADDR7
- * fields are used (only available on NFCv2).
- */
- if (nfc_op->ndcb[0] & NDCB0_LEN_OVRD ||
- NDCB0_ADDR_GET_NUM_CYC(nfc_op->ndcb[0]) >= 6) {
- if (!WARN_ON_ONCE(!nfc->caps->is_nfcv2))
- writel(nfc_op->ndcb[3], nfc->regs + NDCB0);
- }
-}
-
-static int marvell_nfc_end_cmd(struct nand_chip *chip, int flag,
- const char *label)
-{
- struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
- u32 val;
- int ret;
-
- ret = readl_relaxed_poll_timeout(nfc->regs + NDSR, val,
- val & flag,
- POLL_PERIOD, POLL_TIMEOUT);
-
- if (ret) {
- dev_err(nfc->dev, "Timeout on %s (NDSR: 0x%08x)\n",
- label, val);
- if (nfc->dma_chan)
- dmaengine_terminate_all(nfc->dma_chan);
- return ret;
- }
-
- /*
- * DMA function uses this helper to poll on CMDD bits without wanting
- * them to be cleared.
- */
- if (nfc->use_dma && (readl_relaxed(nfc->regs + NDCR) & NDCR_DMA_EN))
- return 0;
-
- writel_relaxed(flag, nfc->regs + NDSR);
-
- return 0;
-}
-
-static int marvell_nfc_wait_cmdd(struct nand_chip *chip)
-{
- struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
- int cs_flag = NDSR_CMDD(to_nand_sel(marvell_nand)->ndcb0_csel);
-
- return marvell_nfc_end_cmd(chip, cs_flag, "CMDD");
-}
-
-static int marvell_nfc_wait_op(struct nand_chip *chip, unsigned int timeout_ms)
-{
- struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
- int ret;
-
- /* Timeout is expressed in ms */
- if (!timeout_ms)
- timeout_ms = IRQ_TIMEOUT;
-
- init_completion(&nfc->complete);
-
- marvell_nfc_enable_int(nfc, NDCR_RDYM);
- ret = wait_for_completion_timeout(&nfc->complete,
- msecs_to_jiffies(timeout_ms));
- marvell_nfc_disable_int(nfc, NDCR_RDYM);
- marvell_nfc_clear_int(nfc, NDSR_RDY(0) | NDSR_RDY(1));
- if (!ret) {
- dev_err(nfc->dev, "Timeout waiting for RB signal\n");
- return -ETIMEDOUT;
- }
-
- return 0;
-}
-
-static void marvell_nfc_select_chip(struct mtd_info *mtd, int die_nr)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
- struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
- u32 ndcr_generic;
-
- if (chip == nfc->selected_chip && die_nr == marvell_nand->selected_die)
- return;
-
- if (die_nr < 0 || die_nr >= marvell_nand->nsels) {
- nfc->selected_chip = NULL;
- marvell_nand->selected_die = -1;
- return;
- }
-
- /*
- * Do not change the timing registers when using the DT property
- * marvell,nand-keep-config; in that case ->ndtr0 and ->ndtr1 from the
- * marvell_nand structure are supposedly empty.
- */
- writel_relaxed(marvell_nand->ndtr0, nfc->regs + NDTR0);
- writel_relaxed(marvell_nand->ndtr1, nfc->regs + NDTR1);
-
- /*
- * Reset the NDCR register to a clean state for this particular chip,
- * also clear ND_RUN bit.
- */
- ndcr_generic = readl_relaxed(nfc->regs + NDCR) &
- NDCR_GENERIC_FIELDS_MASK & ~NDCR_ND_RUN;
- writel_relaxed(ndcr_generic | marvell_nand->ndcr, nfc->regs + NDCR);
-
- /* Also reset the interrupt status register */
- marvell_nfc_clear_int(nfc, NDCR_ALL_INT);
-
- nfc->selected_chip = chip;
- marvell_nand->selected_die = die_nr;
-}
-
-static irqreturn_t marvell_nfc_isr(int irq, void *dev_id)
-{
- struct marvell_nfc *nfc = dev_id;
- u32 st = readl_relaxed(nfc->regs + NDSR);
- u32 ien = (~readl_relaxed(nfc->regs + NDCR)) & NDCR_ALL_INT;
-
- /*
- * RDY interrupt mask is one bit in NDCR while there are two status
- * bit in NDSR (RDY[cs0/cs2] and RDY[cs1/cs3]).
- */
- if (st & NDSR_RDY(1))
- st |= NDSR_RDY(0);
-
- if (!(st & ien))
- return IRQ_NONE;
-
- marvell_nfc_disable_int(nfc, st & NDCR_ALL_INT);
-
- if (!(st & (NDSR_RDDREQ | NDSR_WRDREQ | NDSR_WRCMDREQ)))
- complete(&nfc->complete);
-
- return IRQ_HANDLED;
-}
-
-/* HW ECC related functions */
-static void marvell_nfc_enable_hw_ecc(struct nand_chip *chip)
-{
- struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
- u32 ndcr = readl_relaxed(nfc->regs + NDCR);
-
- if (!(ndcr & NDCR_ECC_EN)) {
- writel_relaxed(ndcr | NDCR_ECC_EN, nfc->regs + NDCR);
-
- /*
- * When enabling BCH, set threshold to 0 to always know the
- * number of corrected bitflips.
- */
- if (chip->ecc.algo == NAND_ECC_BCH)
- writel_relaxed(NDECCCTRL_BCH_EN, nfc->regs + NDECCCTRL);
- }
-}
-
-static void marvell_nfc_disable_hw_ecc(struct nand_chip *chip)
-{
- struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
- u32 ndcr = readl_relaxed(nfc->regs + NDCR);
-
- if (ndcr & NDCR_ECC_EN) {
- writel_relaxed(ndcr & ~NDCR_ECC_EN, nfc->regs + NDCR);
- if (chip->ecc.algo == NAND_ECC_BCH)
- writel_relaxed(0, nfc->regs + NDECCCTRL);
- }
-}
-
-/* DMA related helpers */
-static void marvell_nfc_enable_dma(struct marvell_nfc *nfc)
-{
- u32 reg;
-
- reg = readl_relaxed(nfc->regs + NDCR);
- writel_relaxed(reg | NDCR_DMA_EN, nfc->regs + NDCR);
-}
-
-static void marvell_nfc_disable_dma(struct marvell_nfc *nfc)
-{
- u32 reg;
-
- reg = readl_relaxed(nfc->regs + NDCR);
- writel_relaxed(reg & ~NDCR_DMA_EN, nfc->regs + NDCR);
-}
-
-/* Read/write PIO/DMA accessors */
-static int marvell_nfc_xfer_data_dma(struct marvell_nfc *nfc,
- enum dma_data_direction direction,
- unsigned int len)
-{
- unsigned int dma_len = min_t(int, ALIGN(len, 32), MAX_CHUNK_SIZE);
- struct dma_async_tx_descriptor *tx;
- struct scatterlist sg;
- dma_cookie_t cookie;
- int ret;
-
- marvell_nfc_enable_dma(nfc);
- /* Prepare the DMA transfer */
- sg_init_one(&sg, nfc->dma_buf, dma_len);
- dma_map_sg(nfc->dma_chan->device->dev, &sg, 1, direction);
- tx = dmaengine_prep_slave_sg(nfc->dma_chan, &sg, 1,
- direction == DMA_FROM_DEVICE ?
- DMA_DEV_TO_MEM : DMA_MEM_TO_DEV,
- DMA_PREP_INTERRUPT);
- if (!tx) {
- dev_err(nfc->dev, "Could not prepare DMA S/G list\n");
- return -ENXIO;
- }
-
- /* Do the task and wait for it to finish */
- cookie = dmaengine_submit(tx);
- ret = dma_submit_error(cookie);
- if (ret)
- return -EIO;
-
- dma_async_issue_pending(nfc->dma_chan);
- ret = marvell_nfc_wait_cmdd(nfc->selected_chip);
- dma_unmap_sg(nfc->dma_chan->device->dev, &sg, 1, direction);
- marvell_nfc_disable_dma(nfc);
- if (ret) {
- dev_err(nfc->dev, "Timeout waiting for DMA (status: %d)\n",
- dmaengine_tx_status(nfc->dma_chan, cookie, NULL));
- dmaengine_terminate_all(nfc->dma_chan);
- return -ETIMEDOUT;
- }
-
- return 0;
-}
-
-static int marvell_nfc_xfer_data_in_pio(struct marvell_nfc *nfc, u8 *in,
- unsigned int len)
-{
- unsigned int last_len = len % FIFO_DEPTH;
- unsigned int last_full_offset = round_down(len, FIFO_DEPTH);
- int i;
-
- for (i = 0; i < last_full_offset; i += FIFO_DEPTH)
- ioread32_rep(nfc->regs + NDDB, in + i, FIFO_REP(FIFO_DEPTH));
-
- if (last_len) {
- u8 tmp_buf[FIFO_DEPTH];
-
- ioread32_rep(nfc->regs + NDDB, tmp_buf, FIFO_REP(FIFO_DEPTH));
- memcpy(in + last_full_offset, tmp_buf, last_len);
- }
-
- return 0;
-}
-
-static int marvell_nfc_xfer_data_out_pio(struct marvell_nfc *nfc, const u8 *out,
- unsigned int len)
-{
- unsigned int last_len = len % FIFO_DEPTH;
- unsigned int last_full_offset = round_down(len, FIFO_DEPTH);
- int i;
-
- for (i = 0; i < last_full_offset; i += FIFO_DEPTH)
- iowrite32_rep(nfc->regs + NDDB, out + i, FIFO_REP(FIFO_DEPTH));
-
- if (last_len) {
- u8 tmp_buf[FIFO_DEPTH];
-
- memcpy(tmp_buf, out + last_full_offset, last_len);
- iowrite32_rep(nfc->regs + NDDB, tmp_buf, FIFO_REP(FIFO_DEPTH));
- }
-
- return 0;
-}
-
-static void marvell_nfc_check_empty_chunk(struct nand_chip *chip,
- u8 *data, int data_len,
- u8 *spare, int spare_len,
- u8 *ecc, int ecc_len,
- unsigned int *max_bitflips)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- int bf;
-
- /*
- * Blank pages (all 0xFF) that have not been written may be recognized
- * as bad if bitflips occur, so whenever an uncorrectable error occurs,
- * check if the entire page (with ECC bytes) is actually blank or not.
- */
- if (!data)
- data_len = 0;
- if (!spare)
- spare_len = 0;
- if (!ecc)
- ecc_len = 0;
-
- bf = nand_check_erased_ecc_chunk(data, data_len, ecc, ecc_len,
- spare, spare_len, chip->ecc.strength);
- if (bf < 0) {
- mtd->ecc_stats.failed++;
- return;
- }
-
- /* Update the stats and max_bitflips */
- mtd->ecc_stats.corrected += bf;
- *max_bitflips = max_t(unsigned int, *max_bitflips, bf);
-}
-
-/*
- * Check a chunk is correct or not according to hardware ECC engine.
- * mtd->ecc_stats.corrected is updated, as well as max_bitflips, however
- * mtd->ecc_stats.failure is not, the function will instead return a non-zero
- * value indicating that a check on the emptyness of the subpage must be
- * performed before declaring the subpage corrupted.
- */
-static int marvell_nfc_hw_ecc_correct(struct nand_chip *chip,
- unsigned int *max_bitflips)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
- int bf = 0;
- u32 ndsr;
-
- ndsr = readl_relaxed(nfc->regs + NDSR);
-
- /* Check uncorrectable error flag */
- if (ndsr & NDSR_UNCERR) {
- writel_relaxed(ndsr, nfc->regs + NDSR);
-
- /*
- * Do not increment ->ecc_stats.failed now, instead, return a
- * non-zero value to indicate that this chunk was apparently
- * bad, and it should be check to see if it empty or not. If
- * the chunk (with ECC bytes) is not declared empty, the calling
- * function must increment the failure count.
- */
- return -EBADMSG;
- }
-
- /* Check correctable error flag */
- if (ndsr & NDSR_CORERR) {
- writel_relaxed(ndsr, nfc->regs + NDSR);
-
- if (chip->ecc.algo == NAND_ECC_BCH)
- bf = NDSR_ERRCNT(ndsr);
- else
- bf = 1;
- }
-
- /* Update the stats and max_bitflips */
- mtd->ecc_stats.corrected += bf;
- *max_bitflips = max_t(unsigned int, *max_bitflips, bf);
-
- return 0;
-}
-
-/* Hamming read helpers */
-static int marvell_nfc_hw_ecc_hmg_do_read_page(struct nand_chip *chip,
- u8 *data_buf, u8 *oob_buf,
- bool raw, int page)
-{
- struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
- struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
- const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
- struct marvell_nfc_op nfc_op = {
- .ndcb[0] = NDCB0_CMD_TYPE(TYPE_READ) |
- NDCB0_ADDR_CYC(marvell_nand->addr_cyc) |
- NDCB0_DBC |
- NDCB0_CMD1(NAND_CMD_READ0) |
- NDCB0_CMD2(NAND_CMD_READSTART),
- .ndcb[1] = NDCB1_ADDRS_PAGE(page),
- .ndcb[2] = NDCB2_ADDR5_PAGE(page),
- };
- unsigned int oob_bytes = lt->spare_bytes + (raw ? lt->ecc_bytes : 0);
- int ret;
-
- /* NFCv2 needs more information about the operation being executed */
- if (nfc->caps->is_nfcv2)
- nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW);
-
- ret = marvell_nfc_prepare_cmd(chip);
- if (ret)
- return ret;
-
- marvell_nfc_send_cmd(chip, &nfc_op);
- ret = marvell_nfc_end_cmd(chip, NDSR_RDDREQ,
- "RDDREQ while draining FIFO (data/oob)");
- if (ret)
- return ret;
-
- /*
- * Read the page then the OOB area. Unlike what is shown in current
- * documentation, spare bytes are protected by the ECC engine, and must
- * be at the beginning of the OOB area or running this driver on legacy
- * systems will prevent the discovery of the BBM/BBT.
- */
- if (nfc->use_dma) {
- marvell_nfc_xfer_data_dma(nfc, DMA_FROM_DEVICE,
- lt->data_bytes + oob_bytes);
- memcpy(data_buf, nfc->dma_buf, lt->data_bytes);
- memcpy(oob_buf, nfc->dma_buf + lt->data_bytes, oob_bytes);
- } else {
- marvell_nfc_xfer_data_in_pio(nfc, data_buf, lt->data_bytes);
- marvell_nfc_xfer_data_in_pio(nfc, oob_buf, oob_bytes);
- }
-
- ret = marvell_nfc_wait_cmdd(chip);
-
- return ret;
-}
-
-static int marvell_nfc_hw_ecc_hmg_read_page_raw(struct mtd_info *mtd,
- struct nand_chip *chip, u8 *buf,
- int oob_required, int page)
-{
- return marvell_nfc_hw_ecc_hmg_do_read_page(chip, buf, chip->oob_poi,
- true, page);
-}
-
-static int marvell_nfc_hw_ecc_hmg_read_page(struct mtd_info *mtd,
- struct nand_chip *chip,
- u8 *buf, int oob_required,
- int page)
-{
- const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
- unsigned int full_sz = lt->data_bytes + lt->spare_bytes + lt->ecc_bytes;
- int max_bitflips = 0, ret;
- u8 *raw_buf;
-
- marvell_nfc_enable_hw_ecc(chip);
- marvell_nfc_hw_ecc_hmg_do_read_page(chip, buf, chip->oob_poi, false,
- page);
- ret = marvell_nfc_hw_ecc_correct(chip, &max_bitflips);
- marvell_nfc_disable_hw_ecc(chip);
-
- if (!ret)
- return max_bitflips;
-
- /*
- * When ECC failures are detected, check if the full page has been
- * written or not. Ignore the failure if it is actually empty.
- */
- raw_buf = kmalloc(full_sz, GFP_KERNEL);
- if (!raw_buf)
- return -ENOMEM;
-
- marvell_nfc_hw_ecc_hmg_do_read_page(chip, raw_buf, raw_buf +
- lt->data_bytes, true, page);
- marvell_nfc_check_empty_chunk(chip, raw_buf, full_sz, NULL, 0, NULL, 0,
- &max_bitflips);
- kfree(raw_buf);
-
- return max_bitflips;
-}
-
-/*
- * Spare area in Hamming layouts is not protected by the ECC engine (even if
- * it appears before the ECC bytes when reading), the ->read_oob_raw() function
- * also stands for ->read_oob().
- */
-static int marvell_nfc_hw_ecc_hmg_read_oob_raw(struct mtd_info *mtd,
- struct nand_chip *chip, int page)
-{
- /* Invalidate page cache */
- chip->pagebuf = -1;
-
- return marvell_nfc_hw_ecc_hmg_do_read_page(chip, chip->data_buf,
- chip->oob_poi, true, page);
-}
-
-/* Hamming write helpers */
-static int marvell_nfc_hw_ecc_hmg_do_write_page(struct nand_chip *chip,
- const u8 *data_buf,
- const u8 *oob_buf, bool raw,
- int page)
-{
- struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
- struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
- const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
- struct marvell_nfc_op nfc_op = {
- .ndcb[0] = NDCB0_CMD_TYPE(TYPE_WRITE) |
- NDCB0_ADDR_CYC(marvell_nand->addr_cyc) |
- NDCB0_CMD1(NAND_CMD_SEQIN) |
- NDCB0_CMD2(NAND_CMD_PAGEPROG) |
- NDCB0_DBC,
- .ndcb[1] = NDCB1_ADDRS_PAGE(page),
- .ndcb[2] = NDCB2_ADDR5_PAGE(page),
- };
- unsigned int oob_bytes = lt->spare_bytes + (raw ? lt->ecc_bytes : 0);
- int ret;
-
- /* NFCv2 needs more information about the operation being executed */
- if (nfc->caps->is_nfcv2)
- nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW);
-
- ret = marvell_nfc_prepare_cmd(chip);
- if (ret)
- return ret;
-
- marvell_nfc_send_cmd(chip, &nfc_op);
- ret = marvell_nfc_end_cmd(chip, NDSR_WRDREQ,
- "WRDREQ while loading FIFO (data)");
- if (ret)
- return ret;
-
- /* Write the page then the OOB area */
- if (nfc->use_dma) {
- memcpy(nfc->dma_buf, data_buf, lt->data_bytes);
- memcpy(nfc->dma_buf + lt->data_bytes, oob_buf, oob_bytes);
- marvell_nfc_xfer_data_dma(nfc, DMA_TO_DEVICE, lt->data_bytes +
- lt->ecc_bytes + lt->spare_bytes);
- } else {
- marvell_nfc_xfer_data_out_pio(nfc, data_buf, lt->data_bytes);
- marvell_nfc_xfer_data_out_pio(nfc, oob_buf, oob_bytes);
- }
-
- ret = marvell_nfc_wait_cmdd(chip);
- if (ret)
- return ret;
-
- ret = marvell_nfc_wait_op(chip,
- chip->data_interface.timings.sdr.tPROG_max);
- return ret;
-}
-
-static int marvell_nfc_hw_ecc_hmg_write_page_raw(struct mtd_info *mtd,
- struct nand_chip *chip,
- const u8 *buf,
- int oob_required, int page)
-{
- return marvell_nfc_hw_ecc_hmg_do_write_page(chip, buf, chip->oob_poi,
- true, page);
-}
-
-static int marvell_nfc_hw_ecc_hmg_write_page(struct mtd_info *mtd,
- struct nand_chip *chip,
- const u8 *buf,
- int oob_required, int page)
-{
- int ret;
-
- marvell_nfc_enable_hw_ecc(chip);
- ret = marvell_nfc_hw_ecc_hmg_do_write_page(chip, buf, chip->oob_poi,
- false, page);
- marvell_nfc_disable_hw_ecc(chip);
-
- return ret;
-}
-
-/*
- * Spare area in Hamming layouts is not protected by the ECC engine (even if
- * it appears before the ECC bytes when reading), the ->write_oob_raw() function
- * also stands for ->write_oob().
- */
-static int marvell_nfc_hw_ecc_hmg_write_oob_raw(struct mtd_info *mtd,
- struct nand_chip *chip,
- int page)
-{
- /* Invalidate page cache */
- chip->pagebuf = -1;
-
- memset(chip->data_buf, 0xFF, mtd->writesize);
-
- return marvell_nfc_hw_ecc_hmg_do_write_page(chip, chip->data_buf,
- chip->oob_poi, true, page);
-}
-
-/* BCH read helpers */
-static int marvell_nfc_hw_ecc_bch_read_page_raw(struct mtd_info *mtd,
- struct nand_chip *chip, u8 *buf,
- int oob_required, int page)
-{
- const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
- u8 *oob = chip->oob_poi;
- int chunk_size = lt->data_bytes + lt->spare_bytes + lt->ecc_bytes;
- int ecc_offset = (lt->full_chunk_cnt * lt->spare_bytes) +
- lt->last_spare_bytes;
- int data_len = lt->data_bytes;
- int spare_len = lt->spare_bytes;
- int ecc_len = lt->ecc_bytes;
- int chunk;
-
- if (oob_required)
- memset(chip->oob_poi, 0xFF, mtd->oobsize);
-
- nand_read_page_op(chip, page, 0, NULL, 0);
-
- for (chunk = 0; chunk < lt->nchunks; chunk++) {
- /* Update last chunk length */
- if (chunk >= lt->full_chunk_cnt) {
- data_len = lt->last_data_bytes;
- spare_len = lt->last_spare_bytes;
- ecc_len = lt->last_ecc_bytes;
- }
-
- /* Read data bytes*/
- nand_change_read_column_op(chip, chunk * chunk_size,
- buf + (lt->data_bytes * chunk),
- data_len, false);
-
- /* Read spare bytes */
- nand_read_data_op(chip, oob + (lt->spare_bytes * chunk),
- spare_len, false);
-
- /* Read ECC bytes */
- nand_read_data_op(chip, oob + ecc_offset +
- (ALIGN(lt->ecc_bytes, 32) * chunk),
- ecc_len, false);
- }
-
- return 0;
-}
-
-static void marvell_nfc_hw_ecc_bch_read_chunk(struct nand_chip *chip, int chunk,
- u8 *data, unsigned int data_len,
- u8 *spare, unsigned int spare_len,
- int page)
-{
- struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
- struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
- const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
- int i, ret;
- struct marvell_nfc_op nfc_op = {
- .ndcb[0] = NDCB0_CMD_TYPE(TYPE_READ) |
- NDCB0_ADDR_CYC(marvell_nand->addr_cyc) |
- NDCB0_LEN_OVRD,
- .ndcb[1] = NDCB1_ADDRS_PAGE(page),
- .ndcb[2] = NDCB2_ADDR5_PAGE(page),
- .ndcb[3] = data_len + spare_len,
- };
-
- ret = marvell_nfc_prepare_cmd(chip);
- if (ret)
- return;
-
- if (chunk == 0)
- nfc_op.ndcb[0] |= NDCB0_DBC |
- NDCB0_CMD1(NAND_CMD_READ0) |
- NDCB0_CMD2(NAND_CMD_READSTART);
-
- /*
- * Trigger the naked read operation only on the last chunk.
- * Otherwise, use monolithic read.
- */
- if (lt->nchunks == 1 || (chunk < lt->nchunks - 1))
- nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW);
- else
- nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_LAST_NAKED_RW);
-
- marvell_nfc_send_cmd(chip, &nfc_op);
-
- /*
- * According to the datasheet, when reading from NDDB
- * with BCH enabled, after each 32 bytes reads, we
- * have to make sure that the NDSR.RDDREQ bit is set.
- *
- * Drain the FIFO, 8 32-bit reads at a time, and skip
- * the polling on the last read.
- *
- * Length is a multiple of 32 bytes, hence it is a multiple of 8 too.
- */
- for (i = 0; i < data_len; i += FIFO_DEPTH * BCH_SEQ_READS) {
- marvell_nfc_end_cmd(chip, NDSR_RDDREQ,
- "RDDREQ while draining FIFO (data)");
- marvell_nfc_xfer_data_in_pio(nfc, data,
- FIFO_DEPTH * BCH_SEQ_READS);
- data += FIFO_DEPTH * BCH_SEQ_READS;
- }
-
- for (i = 0; i < spare_len; i += FIFO_DEPTH * BCH_SEQ_READS) {
- marvell_nfc_end_cmd(chip, NDSR_RDDREQ,
- "RDDREQ while draining FIFO (OOB)");
- marvell_nfc_xfer_data_in_pio(nfc, spare,
- FIFO_DEPTH * BCH_SEQ_READS);
- spare += FIFO_DEPTH * BCH_SEQ_READS;
- }
-}
-
-static int marvell_nfc_hw_ecc_bch_read_page(struct mtd_info *mtd,
- struct nand_chip *chip,
- u8 *buf, int oob_required,
- int page)
-{
- const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
- int data_len = lt->data_bytes, spare_len = lt->spare_bytes, ecc_len;
- u8 *data = buf, *spare = chip->oob_poi, *ecc;
- int max_bitflips = 0;
- u32 failure_mask = 0;
- int chunk, ecc_offset_in_page, ret;
-
- /*
- * With BCH, OOB is not fully used (and thus not read entirely), not
- * expected bytes could show up at the end of the OOB buffer if not
- * explicitly erased.
- */
- if (oob_required)
- memset(chip->oob_poi, 0xFF, mtd->oobsize);
-
- marvell_nfc_enable_hw_ecc(chip);
-
- for (chunk = 0; chunk < lt->nchunks; chunk++) {
- /* Update length for the last chunk */
- if (chunk >= lt->full_chunk_cnt) {
- data_len = lt->last_data_bytes;
- spare_len = lt->last_spare_bytes;
- }
-
- /* Read the chunk and detect number of bitflips */
- marvell_nfc_hw_ecc_bch_read_chunk(chip, chunk, data, data_len,
- spare, spare_len, page);
- ret = marvell_nfc_hw_ecc_correct(chip, &max_bitflips);
- if (ret)
- failure_mask |= BIT(chunk);
-
- data += data_len;
- spare += spare_len;
- }
-
- marvell_nfc_disable_hw_ecc(chip);
-
- if (!failure_mask)
- return max_bitflips;
-
- /*
- * Please note that dumping the ECC bytes during a normal read with OOB
- * area would add a significant overhead as ECC bytes are "consumed" by
- * the controller in normal mode and must be re-read in raw mode. To
- * avoid dropping the performances, we prefer not to include them. The
- * user should re-read the page in raw mode if ECC bytes are required.
- *
- * However, for any subpage read error reported by ->correct(), the ECC
- * bytes must be read in raw mode and the full subpage must be checked
- * to see if it is entirely empty of if there was an actual error.
- */
- for (chunk = 0; chunk < lt->nchunks; chunk++) {
- /* No failure reported for this chunk, move to the next one */
- if (!(failure_mask & BIT(chunk)))
- continue;
-
- /* Derive ECC bytes positions (in page/buffer) and length */
- ecc = chip->oob_poi +
- (lt->full_chunk_cnt * lt->spare_bytes) +
- lt->last_spare_bytes +
- (chunk * ALIGN(lt->ecc_bytes, 32));
- ecc_offset_in_page =
- (chunk * (lt->data_bytes + lt->spare_bytes +
- lt->ecc_bytes)) +
- (chunk < lt->full_chunk_cnt ?
- lt->data_bytes + lt->spare_bytes :
- lt->last_data_bytes + lt->last_spare_bytes);
- ecc_len = chunk < lt->full_chunk_cnt ?
- lt->ecc_bytes : lt->last_ecc_bytes;
-
- /* Do the actual raw read of the ECC bytes */
- nand_change_read_column_op(chip, ecc_offset_in_page,
- ecc, ecc_len, false);
-
- /* Derive data/spare bytes positions (in buffer) and length */
- data = buf + (chunk * lt->data_bytes);
- data_len = chunk < lt->full_chunk_cnt ?
- lt->data_bytes : lt->last_data_bytes;
- spare = chip->oob_poi + (chunk * (lt->spare_bytes +
- lt->ecc_bytes));
- spare_len = chunk < lt->full_chunk_cnt ?
- lt->spare_bytes : lt->last_spare_bytes;
-
- /* Check the entire chunk (data + spare + ecc) for emptyness */
- marvell_nfc_check_empty_chunk(chip, data, data_len, spare,
- spare_len, ecc, ecc_len,
- &max_bitflips);
- }
-
- return max_bitflips;
-}
-
-static int marvell_nfc_hw_ecc_bch_read_oob_raw(struct mtd_info *mtd,
- struct nand_chip *chip, int page)
-{
- /* Invalidate page cache */
- chip->pagebuf = -1;
-
- return chip->ecc.read_page_raw(mtd, chip, chip->data_buf, true, page);
-}
-
-static int marvell_nfc_hw_ecc_bch_read_oob(struct mtd_info *mtd,
- struct nand_chip *chip, int page)
-{
- /* Invalidate page cache */
- chip->pagebuf = -1;
-
- return chip->ecc.read_page(mtd, chip, chip->data_buf, true, page);
-}
-
-/* BCH write helpers */
-static int marvell_nfc_hw_ecc_bch_write_page_raw(struct mtd_info *mtd,
- struct nand_chip *chip,
- const u8 *buf,
- int oob_required, int page)
-{
- const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
- int full_chunk_size = lt->data_bytes + lt->spare_bytes + lt->ecc_bytes;
- int data_len = lt->data_bytes;
- int spare_len = lt->spare_bytes;
- int ecc_len = lt->ecc_bytes;
- int spare_offset = 0;
- int ecc_offset = (lt->full_chunk_cnt * lt->spare_bytes) +
- lt->last_spare_bytes;
- int chunk;
-
- nand_prog_page_begin_op(chip, page, 0, NULL, 0);
-
- for (chunk = 0; chunk < lt->nchunks; chunk++) {
- if (chunk >= lt->full_chunk_cnt) {
- data_len = lt->last_data_bytes;
- spare_len = lt->last_spare_bytes;
- ecc_len = lt->last_ecc_bytes;
- }
-
- /* Point to the column of the next chunk */
- nand_change_write_column_op(chip, chunk * full_chunk_size,
- NULL, 0, false);
-
- /* Write the data */
- nand_write_data_op(chip, buf + (chunk * lt->data_bytes),
- data_len, false);
-
- if (!oob_required)
- continue;
-
- /* Write the spare bytes */
- if (spare_len)
- nand_write_data_op(chip, chip->oob_poi + spare_offset,
- spare_len, false);
-
- /* Write the ECC bytes */
- if (ecc_len)
- nand_write_data_op(chip, chip->oob_poi + ecc_offset,
- ecc_len, false);
-
- spare_offset += spare_len;
- ecc_offset += ALIGN(ecc_len, 32);
- }
-
- return nand_prog_page_end_op(chip);
-}
-
-static int
-marvell_nfc_hw_ecc_bch_write_chunk(struct nand_chip *chip, int chunk,
- const u8 *data, unsigned int data_len,
- const u8 *spare, unsigned int spare_len,
- int page)
-{
- struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
- struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
- const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
- int ret;
- struct marvell_nfc_op nfc_op = {
- .ndcb[0] = NDCB0_CMD_TYPE(TYPE_WRITE) | NDCB0_LEN_OVRD,
- .ndcb[3] = data_len + spare_len,
- };
-
- /*
- * First operation dispatches the CMD_SEQIN command, issue the address
- * cycles and asks for the first chunk of data.
- * All operations in the middle (if any) will issue a naked write and
- * also ask for data.
- * Last operation (if any) asks for the last chunk of data through a
- * last naked write.
- */
- if (chunk == 0) {
- nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_WRITE_DISPATCH) |
- NDCB0_ADDR_CYC(marvell_nand->addr_cyc) |
- NDCB0_CMD1(NAND_CMD_SEQIN);
- nfc_op.ndcb[1] |= NDCB1_ADDRS_PAGE(page);
- nfc_op.ndcb[2] |= NDCB2_ADDR5_PAGE(page);
- } else if (chunk < lt->nchunks - 1) {
- nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_NAKED_RW);
- } else {
- nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_LAST_NAKED_RW);
- }
-
- /* Always dispatch the PAGEPROG command on the last chunk */
- if (chunk == lt->nchunks - 1)
- nfc_op.ndcb[0] |= NDCB0_CMD2(NAND_CMD_PAGEPROG) | NDCB0_DBC;
-
- ret = marvell_nfc_prepare_cmd(chip);
- if (ret)
- return ret;
-
- marvell_nfc_send_cmd(chip, &nfc_op);
- ret = marvell_nfc_end_cmd(chip, NDSR_WRDREQ,
- "WRDREQ while loading FIFO (data)");
- if (ret)
- return ret;
-
- /* Transfer the contents */
- iowrite32_rep(nfc->regs + NDDB, data, FIFO_REP(data_len));
- iowrite32_rep(nfc->regs + NDDB, spare, FIFO_REP(spare_len));
-
- return 0;
-}
-
-static int marvell_nfc_hw_ecc_bch_write_page(struct mtd_info *mtd,
- struct nand_chip *chip,
- const u8 *buf,
- int oob_required, int page)
-{
- const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
- const u8 *data = buf;
- const u8 *spare = chip->oob_poi;
- int data_len = lt->data_bytes;
- int spare_len = lt->spare_bytes;
- int chunk, ret;
-
- /* Spare data will be written anyway, so clear it to avoid garbage */
- if (!oob_required)
- memset(chip->oob_poi, 0xFF, mtd->oobsize);
-
- marvell_nfc_enable_hw_ecc(chip);
-
- for (chunk = 0; chunk < lt->nchunks; chunk++) {
- if (chunk >= lt->full_chunk_cnt) {
- data_len = lt->last_data_bytes;
- spare_len = lt->last_spare_bytes;
- }
-
- marvell_nfc_hw_ecc_bch_write_chunk(chip, chunk, data, data_len,
- spare, spare_len, page);
- data += data_len;
- spare += spare_len;
-
- /*
- * Waiting only for CMDD or PAGED is not enough, ECC are
- * partially written. No flag is set once the operation is
- * really finished but the ND_RUN bit is cleared, so wait for it
- * before stepping into the next command.
- */
- marvell_nfc_wait_ndrun(chip);
- }
-
- ret = marvell_nfc_wait_op(chip,
- chip->data_interface.timings.sdr.tPROG_max);
-
- marvell_nfc_disable_hw_ecc(chip);
-
- if (ret)
- return ret;
-
- return 0;
-}
-
-static int marvell_nfc_hw_ecc_bch_write_oob_raw(struct mtd_info *mtd,
- struct nand_chip *chip,
- int page)
-{
- /* Invalidate page cache */
- chip->pagebuf = -1;
-
- memset(chip->data_buf, 0xFF, mtd->writesize);
-
- return chip->ecc.write_page_raw(mtd, chip, chip->data_buf, true, page);
-}
-
-static int marvell_nfc_hw_ecc_bch_write_oob(struct mtd_info *mtd,
- struct nand_chip *chip, int page)
-{
- /* Invalidate page cache */
- chip->pagebuf = -1;
-
- memset(chip->data_buf, 0xFF, mtd->writesize);
-
- return chip->ecc.write_page(mtd, chip, chip->data_buf, true, page);
-}
-
-/* NAND framework ->exec_op() hooks and related helpers */
-static void marvell_nfc_parse_instructions(struct nand_chip *chip,
- const struct nand_subop *subop,
- struct marvell_nfc_op *nfc_op)
-{
- const struct nand_op_instr *instr = NULL;
- struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
- bool first_cmd = true;
- unsigned int op_id;
- int i;
-
- /* Reset the input structure as most of its fields will be OR'ed */
- memset(nfc_op, 0, sizeof(struct marvell_nfc_op));
-
- for (op_id = 0; op_id < subop->ninstrs; op_id++) {
- unsigned int offset, naddrs;
- const u8 *addrs;
- int len = nand_subop_get_data_len(subop, op_id);
-
- instr = &subop->instrs[op_id];
-
- switch (instr->type) {
- case NAND_OP_CMD_INSTR:
- if (first_cmd)
- nfc_op->ndcb[0] |=
- NDCB0_CMD1(instr->ctx.cmd.opcode);
- else
- nfc_op->ndcb[0] |=
- NDCB0_CMD2(instr->ctx.cmd.opcode) |
- NDCB0_DBC;
-
- nfc_op->cle_ale_delay_ns = instr->delay_ns;
- first_cmd = false;
- break;
-
- case NAND_OP_ADDR_INSTR:
- offset = nand_subop_get_addr_start_off(subop, op_id);
- naddrs = nand_subop_get_num_addr_cyc(subop, op_id);
- addrs = &instr->ctx.addr.addrs[offset];
-
- nfc_op->ndcb[0] |= NDCB0_ADDR_CYC(naddrs);
-
- for (i = 0; i < min_t(unsigned int, 4, naddrs); i++)
- nfc_op->ndcb[1] |= addrs[i] << (8 * i);
-
- if (naddrs >= 5)
- nfc_op->ndcb[2] |= NDCB2_ADDR5_CYC(addrs[4]);
- if (naddrs >= 6)
- nfc_op->ndcb[3] |= NDCB3_ADDR6_CYC(addrs[5]);
- if (naddrs == 7)
- nfc_op->ndcb[3] |= NDCB3_ADDR7_CYC(addrs[6]);
-
- nfc_op->cle_ale_delay_ns = instr->delay_ns;
- break;
-
- case NAND_OP_DATA_IN_INSTR:
- nfc_op->data_instr = instr;
- nfc_op->data_instr_idx = op_id;
- nfc_op->ndcb[0] |= NDCB0_CMD_TYPE(TYPE_READ);
- if (nfc->caps->is_nfcv2) {
- nfc_op->ndcb[0] |=
- NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW) |
- NDCB0_LEN_OVRD;
- nfc_op->ndcb[3] |= round_up(len, FIFO_DEPTH);
- }
- nfc_op->data_delay_ns = instr->delay_ns;
- break;
-
- case NAND_OP_DATA_OUT_INSTR:
- nfc_op->data_instr = instr;
- nfc_op->data_instr_idx = op_id;
- nfc_op->ndcb[0] |= NDCB0_CMD_TYPE(TYPE_WRITE);
- if (nfc->caps->is_nfcv2) {
- nfc_op->ndcb[0] |=
- NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW) |
- NDCB0_LEN_OVRD;
- nfc_op->ndcb[3] |= round_up(len, FIFO_DEPTH);
- }
- nfc_op->data_delay_ns = instr->delay_ns;
- break;
-
- case NAND_OP_WAITRDY_INSTR:
- nfc_op->rdy_timeout_ms = instr->ctx.waitrdy.timeout_ms;
- nfc_op->rdy_delay_ns = instr->delay_ns;
- break;
- }
- }
-}
-
-static int marvell_nfc_xfer_data_pio(struct nand_chip *chip,
- const struct nand_subop *subop,
- struct marvell_nfc_op *nfc_op)
-{
- struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
- const struct nand_op_instr *instr = nfc_op->data_instr;
- unsigned int op_id = nfc_op->data_instr_idx;
- unsigned int len = nand_subop_get_data_len(subop, op_id);
- unsigned int offset = nand_subop_get_data_start_off(subop, op_id);
- bool reading = (instr->type == NAND_OP_DATA_IN_INSTR);
- int ret;
-
- if (instr->ctx.data.force_8bit)
- marvell_nfc_force_byte_access(chip, true);
-
- if (reading) {
- u8 *in = instr->ctx.data.buf.in + offset;
-
- ret = marvell_nfc_xfer_data_in_pio(nfc, in, len);
- } else {
- const u8 *out = instr->ctx.data.buf.out + offset;
-
- ret = marvell_nfc_xfer_data_out_pio(nfc, out, len);
- }
-
- if (instr->ctx.data.force_8bit)
- marvell_nfc_force_byte_access(chip, false);
-
- return ret;
-}
-
-static int marvell_nfc_monolithic_access_exec(struct nand_chip *chip,
- const struct nand_subop *subop)
-{
- struct marvell_nfc_op nfc_op;
- bool reading;
- int ret;
-
- marvell_nfc_parse_instructions(chip, subop, &nfc_op);
- reading = (nfc_op.data_instr->type == NAND_OP_DATA_IN_INSTR);
-
- ret = marvell_nfc_prepare_cmd(chip);
- if (ret)
- return ret;
-
- marvell_nfc_send_cmd(chip, &nfc_op);
- ret = marvell_nfc_end_cmd(chip, NDSR_RDDREQ | NDSR_WRDREQ,
- "RDDREQ/WRDREQ while draining raw data");
- if (ret)
- return ret;
-
- cond_delay(nfc_op.cle_ale_delay_ns);
-
- if (reading) {
- if (nfc_op.rdy_timeout_ms) {
- ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms);
- if (ret)
- return ret;
- }
-
- cond_delay(nfc_op.rdy_delay_ns);
- }
-
- marvell_nfc_xfer_data_pio(chip, subop, &nfc_op);
- ret = marvell_nfc_wait_cmdd(chip);
- if (ret)
- return ret;
-
- cond_delay(nfc_op.data_delay_ns);
-
- if (!reading) {
- if (nfc_op.rdy_timeout_ms) {
- ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms);
- if (ret)
- return ret;
- }
-
- cond_delay(nfc_op.rdy_delay_ns);
- }
-
- /*
- * NDCR ND_RUN bit should be cleared automatically at the end of each
- * operation but experience shows that the behavior is buggy when it
- * comes to writes (with LEN_OVRD). Clear it by hand in this case.
- */
- if (!reading) {
- struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
-
- writel_relaxed(readl(nfc->regs + NDCR) & ~NDCR_ND_RUN,
- nfc->regs + NDCR);
- }
-
- return 0;
-}
-
-static int marvell_nfc_naked_access_exec(struct nand_chip *chip,
- const struct nand_subop *subop)
-{
- struct marvell_nfc_op nfc_op;
- int ret;
-
- marvell_nfc_parse_instructions(chip, subop, &nfc_op);
-
- /*
- * Naked access are different in that they need to be flagged as naked
- * by the controller. Reset the controller registers fields that inform
- * on the type and refill them according to the ongoing operation.
- */
- nfc_op.ndcb[0] &= ~(NDCB0_CMD_TYPE(TYPE_MASK) |
- NDCB0_CMD_XTYPE(XTYPE_MASK));
- switch (subop->instrs[0].type) {
- case NAND_OP_CMD_INSTR:
- nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_NAKED_CMD);
- break;
- case NAND_OP_ADDR_INSTR:
- nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_NAKED_ADDR);
- break;
- case NAND_OP_DATA_IN_INSTR:
- nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_READ) |
- NDCB0_CMD_XTYPE(XTYPE_LAST_NAKED_RW);
- break;
- case NAND_OP_DATA_OUT_INSTR:
- nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_WRITE) |
- NDCB0_CMD_XTYPE(XTYPE_LAST_NAKED_RW);
- break;
- default:
- /* This should never happen */
- break;
- }
-
- ret = marvell_nfc_prepare_cmd(chip);
- if (ret)
- return ret;
-
- marvell_nfc_send_cmd(chip, &nfc_op);
-
- if (!nfc_op.data_instr) {
- ret = marvell_nfc_wait_cmdd(chip);
- cond_delay(nfc_op.cle_ale_delay_ns);
- return ret;
- }
-
- ret = marvell_nfc_end_cmd(chip, NDSR_RDDREQ | NDSR_WRDREQ,
- "RDDREQ/WRDREQ while draining raw data");
- if (ret)
- return ret;
-
- marvell_nfc_xfer_data_pio(chip, subop, &nfc_op);
- ret = marvell_nfc_wait_cmdd(chip);
- if (ret)
- return ret;
-
- /*
- * NDCR ND_RUN bit should be cleared automatically at the end of each
- * operation but experience shows that the behavior is buggy when it
- * comes to writes (with LEN_OVRD). Clear it by hand in this case.
- */
- if (subop->instrs[0].type == NAND_OP_DATA_OUT_INSTR) {
- struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
-
- writel_relaxed(readl(nfc->regs + NDCR) & ~NDCR_ND_RUN,
- nfc->regs + NDCR);
- }
-
- return 0;
-}
-
-static int marvell_nfc_naked_waitrdy_exec(struct nand_chip *chip,
- const struct nand_subop *subop)
-{
- struct marvell_nfc_op nfc_op;
- int ret;
-
- marvell_nfc_parse_instructions(chip, subop, &nfc_op);
-
- ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms);
- cond_delay(nfc_op.rdy_delay_ns);
-
- return ret;
-}
-
-static int marvell_nfc_read_id_type_exec(struct nand_chip *chip,
- const struct nand_subop *subop)
-{
- struct marvell_nfc_op nfc_op;
- int ret;
-
- marvell_nfc_parse_instructions(chip, subop, &nfc_op);
- nfc_op.ndcb[0] &= ~NDCB0_CMD_TYPE(TYPE_READ);
- nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_READ_ID);
-
- ret = marvell_nfc_prepare_cmd(chip);
- if (ret)
- return ret;
-
- marvell_nfc_send_cmd(chip, &nfc_op);
- ret = marvell_nfc_end_cmd(chip, NDSR_RDDREQ,
- "RDDREQ while reading ID");
- if (ret)
- return ret;
-
- cond_delay(nfc_op.cle_ale_delay_ns);
-
- if (nfc_op.rdy_timeout_ms) {
- ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms);
- if (ret)
- return ret;
- }
-
- cond_delay(nfc_op.rdy_delay_ns);
-
- marvell_nfc_xfer_data_pio(chip, subop, &nfc_op);
- ret = marvell_nfc_wait_cmdd(chip);
- if (ret)
- return ret;
-
- cond_delay(nfc_op.data_delay_ns);
-
- return 0;
-}
-
-static int marvell_nfc_read_status_exec(struct nand_chip *chip,
- const struct nand_subop *subop)
-{
- struct marvell_nfc_op nfc_op;
- int ret;
-
- marvell_nfc_parse_instructions(chip, subop, &nfc_op);
- nfc_op.ndcb[0] &= ~NDCB0_CMD_TYPE(TYPE_READ);
- nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_STATUS);
-
- ret = marvell_nfc_prepare_cmd(chip);
- if (ret)
- return ret;
-
- marvell_nfc_send_cmd(chip, &nfc_op);
- ret = marvell_nfc_end_cmd(chip, NDSR_RDDREQ,
- "RDDREQ while reading status");
- if (ret)
- return ret;
-
- cond_delay(nfc_op.cle_ale_delay_ns);
-
- if (nfc_op.rdy_timeout_ms) {
- ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms);
- if (ret)
- return ret;
- }
-
- cond_delay(nfc_op.rdy_delay_ns);
-
- marvell_nfc_xfer_data_pio(chip, subop, &nfc_op);
- ret = marvell_nfc_wait_cmdd(chip);
- if (ret)
- return ret;
-
- cond_delay(nfc_op.data_delay_ns);
-
- return 0;
-}
-
-static int marvell_nfc_reset_cmd_type_exec(struct nand_chip *chip,
- const struct nand_subop *subop)
-{
- struct marvell_nfc_op nfc_op;
- int ret;
-
- marvell_nfc_parse_instructions(chip, subop, &nfc_op);
- nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_RESET);
-
- ret = marvell_nfc_prepare_cmd(chip);
- if (ret)
- return ret;
-
- marvell_nfc_send_cmd(chip, &nfc_op);
- ret = marvell_nfc_wait_cmdd(chip);
- if (ret)
- return ret;
-
- cond_delay(nfc_op.cle_ale_delay_ns);
-
- ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms);
- if (ret)
- return ret;
-
- cond_delay(nfc_op.rdy_delay_ns);
-
- return 0;
-}
-
-static int marvell_nfc_erase_cmd_type_exec(struct nand_chip *chip,
- const struct nand_subop *subop)
-{
- struct marvell_nfc_op nfc_op;
- int ret;
-
- marvell_nfc_parse_instructions(chip, subop, &nfc_op);
- nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_ERASE);
-
- ret = marvell_nfc_prepare_cmd(chip);
- if (ret)
- return ret;
-
- marvell_nfc_send_cmd(chip, &nfc_op);
- ret = marvell_nfc_wait_cmdd(chip);
- if (ret)
- return ret;
-
- cond_delay(nfc_op.cle_ale_delay_ns);
-
- ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms);
- if (ret)
- return ret;
-
- cond_delay(nfc_op.rdy_delay_ns);
-
- return 0;
-}
-
-static const struct nand_op_parser marvell_nfcv2_op_parser = NAND_OP_PARSER(
- /* Monolithic reads/writes */
- NAND_OP_PARSER_PATTERN(
- marvell_nfc_monolithic_access_exec,
- NAND_OP_PARSER_PAT_CMD_ELEM(false),
- NAND_OP_PARSER_PAT_ADDR_ELEM(true, MAX_ADDRESS_CYC_NFCV2),
- NAND_OP_PARSER_PAT_CMD_ELEM(true),
- NAND_OP_PARSER_PAT_WAITRDY_ELEM(true),
- NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, MAX_CHUNK_SIZE)),
- NAND_OP_PARSER_PATTERN(
- marvell_nfc_monolithic_access_exec,
- NAND_OP_PARSER_PAT_CMD_ELEM(false),
- NAND_OP_PARSER_PAT_ADDR_ELEM(false, MAX_ADDRESS_CYC_NFCV2),
- NAND_OP_PARSER_PAT_DATA_OUT_ELEM(false, MAX_CHUNK_SIZE),
- NAND_OP_PARSER_PAT_CMD_ELEM(true),
- NAND_OP_PARSER_PAT_WAITRDY_ELEM(true)),
- /* Naked commands */
- NAND_OP_PARSER_PATTERN(
- marvell_nfc_naked_access_exec,
- NAND_OP_PARSER_PAT_CMD_ELEM(false)),
- NAND_OP_PARSER_PATTERN(
- marvell_nfc_naked_access_exec,
- NAND_OP_PARSER_PAT_ADDR_ELEM(false, MAX_ADDRESS_CYC_NFCV2)),
- NAND_OP_PARSER_PATTERN(
- marvell_nfc_naked_access_exec,
- NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, MAX_CHUNK_SIZE)),
- NAND_OP_PARSER_PATTERN(
- marvell_nfc_naked_access_exec,
- NAND_OP_PARSER_PAT_DATA_OUT_ELEM(false, MAX_CHUNK_SIZE)),
- NAND_OP_PARSER_PATTERN(
- marvell_nfc_naked_waitrdy_exec,
- NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)),
- );
-
-static const struct nand_op_parser marvell_nfcv1_op_parser = NAND_OP_PARSER(
- /* Naked commands not supported, use a function for each pattern */
- NAND_OP_PARSER_PATTERN(
- marvell_nfc_read_id_type_exec,
- NAND_OP_PARSER_PAT_CMD_ELEM(false),
- NAND_OP_PARSER_PAT_ADDR_ELEM(false, MAX_ADDRESS_CYC_NFCV1),
- NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, 8)),
- NAND_OP_PARSER_PATTERN(
- marvell_nfc_erase_cmd_type_exec,
- NAND_OP_PARSER_PAT_CMD_ELEM(false),
- NAND_OP_PARSER_PAT_ADDR_ELEM(false, MAX_ADDRESS_CYC_NFCV1),
- NAND_OP_PARSER_PAT_CMD_ELEM(false),
- NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)),
- NAND_OP_PARSER_PATTERN(
- marvell_nfc_read_status_exec,
- NAND_OP_PARSER_PAT_CMD_ELEM(false),
- NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, 1)),
- NAND_OP_PARSER_PATTERN(
- marvell_nfc_reset_cmd_type_exec,
- NAND_OP_PARSER_PAT_CMD_ELEM(false),
- NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)),
- NAND_OP_PARSER_PATTERN(
- marvell_nfc_naked_waitrdy_exec,
- NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)),
- );
-
-static int marvell_nfc_exec_op(struct nand_chip *chip,
- const struct nand_operation *op,
- bool check_only)
-{
- struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
-
- if (nfc->caps->is_nfcv2)
- return nand_op_parser_exec_op(chip, &marvell_nfcv2_op_parser,
- op, check_only);
- else
- return nand_op_parser_exec_op(chip, &marvell_nfcv1_op_parser,
- op, check_only);
-}
-
-/*
- * Layouts were broken in old pxa3xx_nand driver, these are supposed to be
- * usable.
- */
-static int marvell_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
-
- if (section)
- return -ERANGE;
-
- oobregion->length = (lt->full_chunk_cnt * lt->ecc_bytes) +
- lt->last_ecc_bytes;
- oobregion->offset = mtd->oobsize - oobregion->length;
-
- return 0;
-}
-
-static int marvell_nand_ooblayout_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
-
- if (section)
- return -ERANGE;
-
- /*
- * Bootrom looks in bytes 0 & 5 for bad blocks for the
- * 4KB page / 4bit BCH combination.
- */
- if (mtd->writesize == SZ_4K && lt->data_bytes == SZ_2K)
- oobregion->offset = 6;
- else
- oobregion->offset = 2;
-
- oobregion->length = (lt->full_chunk_cnt * lt->spare_bytes) +
- lt->last_spare_bytes - oobregion->offset;
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops marvell_nand_ooblayout_ops = {
- .ecc = marvell_nand_ooblayout_ecc,
- .free = marvell_nand_ooblayout_free,
-};
-
-static int marvell_nand_hw_ecc_ctrl_init(struct mtd_info *mtd,
- struct nand_ecc_ctrl *ecc)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
- const struct marvell_hw_ecc_layout *l;
- int i;
-
- if (!nfc->caps->is_nfcv2 &&
- (mtd->writesize + mtd->oobsize > MAX_CHUNK_SIZE)) {
- dev_err(nfc->dev,
- "NFCv1: writesize (%d) cannot be bigger than a chunk (%d)\n",
- mtd->writesize, MAX_CHUNK_SIZE - mtd->oobsize);
- return -ENOTSUPP;
- }
-
- to_marvell_nand(chip)->layout = NULL;
- for (i = 0; i < ARRAY_SIZE(marvell_nfc_layouts); i++) {
- l = &marvell_nfc_layouts[i];
- if (mtd->writesize == l->writesize &&
- ecc->size == l->chunk && ecc->strength == l->strength) {
- to_marvell_nand(chip)->layout = l;
- break;
- }
- }
-
- if (!to_marvell_nand(chip)->layout ||
- (!nfc->caps->is_nfcv2 && ecc->strength > 1)) {
- dev_err(nfc->dev,
- "ECC strength %d at page size %d is not supported\n",
- ecc->strength, mtd->writesize);
- return -ENOTSUPP;
- }
-
- mtd_set_ooblayout(mtd, &marvell_nand_ooblayout_ops);
- ecc->steps = l->nchunks;
- ecc->size = l->data_bytes;
-
- if (ecc->strength == 1) {
- chip->ecc.algo = NAND_ECC_HAMMING;
- ecc->read_page_raw = marvell_nfc_hw_ecc_hmg_read_page_raw;
- ecc->read_page = marvell_nfc_hw_ecc_hmg_read_page;
- ecc->read_oob_raw = marvell_nfc_hw_ecc_hmg_read_oob_raw;
- ecc->read_oob = ecc->read_oob_raw;
- ecc->write_page_raw = marvell_nfc_hw_ecc_hmg_write_page_raw;
- ecc->write_page = marvell_nfc_hw_ecc_hmg_write_page;
- ecc->write_oob_raw = marvell_nfc_hw_ecc_hmg_write_oob_raw;
- ecc->write_oob = ecc->write_oob_raw;
- } else {
- chip->ecc.algo = NAND_ECC_BCH;
- ecc->strength = 16;
- ecc->read_page_raw = marvell_nfc_hw_ecc_bch_read_page_raw;
- ecc->read_page = marvell_nfc_hw_ecc_bch_read_page;
- ecc->read_oob_raw = marvell_nfc_hw_ecc_bch_read_oob_raw;
- ecc->read_oob = marvell_nfc_hw_ecc_bch_read_oob;
- ecc->write_page_raw = marvell_nfc_hw_ecc_bch_write_page_raw;
- ecc->write_page = marvell_nfc_hw_ecc_bch_write_page;
- ecc->write_oob_raw = marvell_nfc_hw_ecc_bch_write_oob_raw;
- ecc->write_oob = marvell_nfc_hw_ecc_bch_write_oob;
- }
-
- return 0;
-}
-
-static int marvell_nand_ecc_init(struct mtd_info *mtd,
- struct nand_ecc_ctrl *ecc)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
- int ret;
-
- if (ecc->mode != NAND_ECC_NONE && (!ecc->size || !ecc->strength)) {
- if (chip->ecc_step_ds && chip->ecc_strength_ds) {
- ecc->size = chip->ecc_step_ds;
- ecc->strength = chip->ecc_strength_ds;
- } else {
- dev_info(nfc->dev,
- "No minimum ECC strength, using 1b/512B\n");
- ecc->size = 512;
- ecc->strength = 1;
- }
- }
-
- switch (ecc->mode) {
- case NAND_ECC_HW:
- ret = marvell_nand_hw_ecc_ctrl_init(mtd, ecc);
- if (ret)
- return ret;
- break;
- case NAND_ECC_NONE:
- case NAND_ECC_SOFT:
- if (!nfc->caps->is_nfcv2 && mtd->writesize != SZ_512 &&
- mtd->writesize != SZ_2K) {
- dev_err(nfc->dev, "NFCv1 cannot write %d bytes pages\n",
- mtd->writesize);
- return -EINVAL;
- }
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static u8 bbt_pattern[] = {'M', 'V', 'B', 'b', 't', '0' };
-static u8 bbt_mirror_pattern[] = {'1', 't', 'b', 'B', 'V', 'M' };
-
-static struct nand_bbt_descr bbt_main_descr = {
- .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
- NAND_BBT_2BIT | NAND_BBT_VERSION,
- .offs = 8,
- .len = 6,
- .veroffs = 14,
- .maxblocks = 8, /* Last 8 blocks in each chip */
- .pattern = bbt_pattern
-};
-
-static struct nand_bbt_descr bbt_mirror_descr = {
- .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
- NAND_BBT_2BIT | NAND_BBT_VERSION,
- .offs = 8,
- .len = 6,
- .veroffs = 14,
- .maxblocks = 8, /* Last 8 blocks in each chip */
- .pattern = bbt_mirror_pattern
-};
-
-static int marvell_nfc_setup_data_interface(struct mtd_info *mtd, int chipnr,
- const struct nand_data_interface
- *conf)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
- struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
- unsigned int period_ns = 1000000000 / clk_get_rate(nfc->ecc_clk) * 2;
- const struct nand_sdr_timings *sdr;
- struct marvell_nfc_timings nfc_tmg;
- int read_delay;
-
- sdr = nand_get_sdr_timings(conf);
- if (IS_ERR(sdr))
- return PTR_ERR(sdr);
-
- /*
- * SDR timings are given in pico-seconds while NFC timings must be
- * expressed in NAND controller clock cycles, which is half of the
- * frequency of the accessible ECC clock retrieved by clk_get_rate().
- * This is not written anywhere in the datasheet but was observed
- * with an oscilloscope.
- *
- * NFC datasheet gives equations from which thoses calculations
- * are derived, they tend to be slightly more restrictives than the
- * given core timings and may improve the overall speed.
- */
- nfc_tmg.tRP = TO_CYCLES(DIV_ROUND_UP(sdr->tRC_min, 2), period_ns) - 1;
- nfc_tmg.tRH = nfc_tmg.tRP;
- nfc_tmg.tWP = TO_CYCLES(DIV_ROUND_UP(sdr->tWC_min, 2), period_ns) - 1;
- nfc_tmg.tWH = nfc_tmg.tWP;
- nfc_tmg.tCS = TO_CYCLES(sdr->tCS_min, period_ns);
- nfc_tmg.tCH = TO_CYCLES(sdr->tCH_min, period_ns) - 1;
- nfc_tmg.tADL = TO_CYCLES(sdr->tADL_min, period_ns);
- /*
- * Read delay is the time of propagation from SoC pins to NFC internal
- * logic. With non-EDO timings, this is MIN_RD_DEL_CNT clock cycles. In
- * EDO mode, an additional delay of tRH must be taken into account so
- * the data is sampled on the falling edge instead of the rising edge.
- */
- read_delay = sdr->tRC_min >= 30000 ?
- MIN_RD_DEL_CNT : MIN_RD_DEL_CNT + nfc_tmg.tRH;
-
- nfc_tmg.tAR = TO_CYCLES(sdr->tAR_min, period_ns);
- /*
- * tWHR and tRHW are supposed to be read to write delays (and vice
- * versa) but in some cases, ie. when doing a change column, they must
- * be greater than that to be sure tCCS delay is respected.
- */
- nfc_tmg.tWHR = TO_CYCLES(max_t(int, sdr->tWHR_min, sdr->tCCS_min),
- period_ns) - 2,
- nfc_tmg.tRHW = TO_CYCLES(max_t(int, sdr->tRHW_min, sdr->tCCS_min),
- period_ns);
-
- /* Use WAIT_MODE (wait for RB line) instead of only relying on delays */
- nfc_tmg.tR = TO_CYCLES(sdr->tWB_max, period_ns);
-
- if (chipnr < 0)
- return 0;
-
- marvell_nand->ndtr0 =
- NDTR0_TRP(nfc_tmg.tRP) |
- NDTR0_TRH(nfc_tmg.tRH) |
- NDTR0_ETRP(nfc_tmg.tRP) |
- NDTR0_TWP(nfc_tmg.tWP) |
- NDTR0_TWH(nfc_tmg.tWH) |
- NDTR0_TCS(nfc_tmg.tCS) |
- NDTR0_TCH(nfc_tmg.tCH) |
- NDTR0_RD_CNT_DEL(read_delay) |
- NDTR0_SELCNTR |
- NDTR0_TADL(nfc_tmg.tADL);
-
- marvell_nand->ndtr1 =
- NDTR1_TAR(nfc_tmg.tAR) |
- NDTR1_TWHR(nfc_tmg.tWHR) |
- NDTR1_TRHW(nfc_tmg.tRHW) |
- NDTR1_WAIT_MODE |
- NDTR1_TR(nfc_tmg.tR);
-
- return 0;
-}
-
-static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc,
- struct device_node *np)
-{
- struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(dev);
- struct marvell_nand_chip *marvell_nand;
- struct mtd_info *mtd;
- struct nand_chip *chip;
- int nsels, ret, i;
- u32 cs, rb;
-
- /*
- * The legacy "num-cs" property indicates the number of CS on the only
- * chip connected to the controller (legacy bindings does not support
- * more than one chip). CS are only incremented one by one while the RB
- * pin is always the #0.
- *
- * When not using legacy bindings, a couple of "reg" and "nand-rb"
- * properties must be filled. For each chip, expressed as a subnode,
- * "reg" points to the CS lines and "nand-rb" to the RB line.
- */
- if (pdata) {
- nsels = 1;
- } else if (nfc->caps->legacy_of_bindings &&
- !of_get_property(np, "num-cs", &nsels)) {
- dev_err(dev, "missing num-cs property\n");
- return -EINVAL;
- } else if (!of_get_property(np, "reg", &nsels)) {
- dev_err(dev, "missing reg property\n");
- return -EINVAL;
- }
-
- if (!pdata)
- nsels /= sizeof(u32);
- if (!nsels) {
- dev_err(dev, "invalid reg property size\n");
- return -EINVAL;
- }
-
- /* Alloc the nand chip structure */
- marvell_nand = devm_kzalloc(dev, sizeof(*marvell_nand) +
- (nsels *
- sizeof(struct marvell_nand_chip_sel)),
- GFP_KERNEL);
- if (!marvell_nand) {
- dev_err(dev, "could not allocate chip structure\n");
- return -ENOMEM;
- }
-
- marvell_nand->nsels = nsels;
- marvell_nand->selected_die = -1;
-
- for (i = 0; i < nsels; i++) {
- if (pdata || nfc->caps->legacy_of_bindings) {
- /*
- * Legacy bindings use the CS lines in natural
- * order (0, 1, ...)
- */
- cs = i;
- } else {
- /* Retrieve CS id */
- ret = of_property_read_u32_index(np, "reg", i, &cs);
- if (ret) {
- dev_err(dev, "could not retrieve reg property: %d\n",
- ret);
- return ret;
- }
- }
-
- if (cs >= nfc->caps->max_cs_nb) {
- dev_err(dev, "invalid reg value: %u (max CS = %d)\n",
- cs, nfc->caps->max_cs_nb);
- return -EINVAL;
- }
-
- if (test_and_set_bit(cs, &nfc->assigned_cs)) {
- dev_err(dev, "CS %d already assigned\n", cs);
- return -EINVAL;
- }
-
- /*
- * The cs variable represents the chip select id, which must be
- * converted in bit fields for NDCB0 and NDCB2 to select the
- * right chip. Unfortunately, due to a lack of information on
- * the subject and incoherent documentation, the user should not
- * use CS1 and CS3 at all as asserting them is not supported in
- * a reliable way (due to multiplexing inside ADDR5 field).
- */
- marvell_nand->sels[i].cs = cs;
- switch (cs) {
- case 0:
- case 2:
- marvell_nand->sels[i].ndcb0_csel = 0;
- break;
- case 1:
- case 3:
- marvell_nand->sels[i].ndcb0_csel = NDCB0_CSEL;
- break;
- default:
- return -EINVAL;
- }
-
- /* Retrieve RB id */
- if (pdata || nfc->caps->legacy_of_bindings) {
- /* Legacy bindings always use RB #0 */
- rb = 0;
- } else {
- ret = of_property_read_u32_index(np, "nand-rb", i,
- &rb);
- if (ret) {
- dev_err(dev,
- "could not retrieve RB property: %d\n",
- ret);
- return ret;
- }
- }
-
- if (rb >= nfc->caps->max_rb_nb) {
- dev_err(dev, "invalid reg value: %u (max RB = %d)\n",
- rb, nfc->caps->max_rb_nb);
- return -EINVAL;
- }
-
- marvell_nand->sels[i].rb = rb;
- }
-
- chip = &marvell_nand->chip;
- chip->controller = &nfc->controller;
- nand_set_flash_node(chip, np);
-
- chip->exec_op = marvell_nfc_exec_op;
- chip->select_chip = marvell_nfc_select_chip;
- if (nfc->caps->is_nfcv2 &&
- !of_property_read_bool(np, "marvell,nand-keep-config"))
- chip->setup_data_interface = marvell_nfc_setup_data_interface;
-
- mtd = nand_to_mtd(chip);
- mtd->dev.parent = dev;
-
- /*
- * Default to HW ECC engine mode. If the nand-ecc-mode property is given
- * in the DT node, this entry will be overwritten in nand_scan_ident().
- */
- chip->ecc.mode = NAND_ECC_HW;
-
- /*
- * Save a reference value for timing registers before
- * ->setup_data_interface() is called.
- */
- marvell_nand->ndtr0 = readl_relaxed(nfc->regs + NDTR0);
- marvell_nand->ndtr1 = readl_relaxed(nfc->regs + NDTR1);
-
- chip->options |= NAND_BUSWIDTH_AUTO;
- ret = nand_scan_ident(mtd, marvell_nand->nsels, NULL);
- if (ret) {
- dev_err(dev, "could not identify the nand chip\n");
- return ret;
- }
-
- if (pdata && pdata->flash_bbt)
- chip->bbt_options |= NAND_BBT_USE_FLASH;
-
- if (chip->bbt_options & NAND_BBT_USE_FLASH) {
- /*
- * We'll use a bad block table stored in-flash and don't
- * allow writing the bad block marker to the flash.
- */
- chip->bbt_options |= NAND_BBT_NO_OOB_BBM;
- chip->bbt_td = &bbt_main_descr;
- chip->bbt_md = &bbt_mirror_descr;
- }
-
- /* Save the chip-specific fields of NDCR */
- marvell_nand->ndcr = NDCR_PAGE_SZ(mtd->writesize);
- if (chip->options & NAND_BUSWIDTH_16)
- marvell_nand->ndcr |= NDCR_DWIDTH_M | NDCR_DWIDTH_C;
-
- /*
- * On small page NANDs, only one cycle is needed to pass the
- * column address.
- */
- if (mtd->writesize <= 512) {
- marvell_nand->addr_cyc = 1;
- } else {
- marvell_nand->addr_cyc = 2;
- marvell_nand->ndcr |= NDCR_RA_START;
- }
-
- /*
- * Now add the number of cycles needed to pass the row
- * address.
- *
- * Addressing a chip using CS 2 or 3 should also need the third row
- * cycle but due to inconsistance in the documentation and lack of
- * hardware to test this situation, this case is not supported.
- */
- if (chip->options & NAND_ROW_ADDR_3)
- marvell_nand->addr_cyc += 3;
- else
- marvell_nand->addr_cyc += 2;
-
- if (pdata) {
- chip->ecc.size = pdata->ecc_step_size;
- chip->ecc.strength = pdata->ecc_strength;
- }
-
- ret = marvell_nand_ecc_init(mtd, &chip->ecc);
- if (ret) {
- dev_err(dev, "ECC init failed: %d\n", ret);
- return ret;
- }
-
- if (chip->ecc.mode == NAND_ECC_HW) {
- /*
- * Subpage write not available with hardware ECC, prohibit also
- * subpage read as in userspace subpage access would still be
- * allowed and subpage write, if used, would lead to numerous
- * uncorrectable ECC errors.
- */
- chip->options |= NAND_NO_SUBPAGE_WRITE;
- }
-
- if (pdata || nfc->caps->legacy_of_bindings) {
- /*
- * We keep the MTD name unchanged to avoid breaking platforms
- * where the MTD cmdline parser is used and the bootloader
- * has not been updated to use the new naming scheme.
- */
- mtd->name = "pxa3xx_nand-0";
- } else if (!mtd->name) {
- /*
- * If the new bindings are used and the bootloader has not been
- * updated to pass a new mtdparts parameter on the cmdline, you
- * should define the following property in your NAND node, ie:
- *
- * label = "main-storage";
- *
- * This way, mtd->name will be set by the core when
- * nand_set_flash_node() is called.
- */
- mtd->name = devm_kasprintf(nfc->dev, GFP_KERNEL,
- "%s:nand.%d", dev_name(nfc->dev),
- marvell_nand->sels[0].cs);
- if (!mtd->name) {
- dev_err(nfc->dev, "Failed to allocate mtd->name\n");
- return -ENOMEM;
- }
- }
-
- ret = nand_scan_tail(mtd);
- if (ret) {
- dev_err(dev, "nand_scan_tail failed: %d\n", ret);
- return ret;
- }
-
- if (pdata)
- /* Legacy bindings support only one chip */
- ret = mtd_device_register(mtd, pdata->parts[0],
- pdata->nr_parts[0]);
- else
- ret = mtd_device_register(mtd, NULL, 0);
- if (ret) {
- dev_err(dev, "failed to register mtd device: %d\n", ret);
- nand_release(mtd);
- return ret;
- }
-
- list_add_tail(&marvell_nand->node, &nfc->chips);
-
- return 0;
-}
-
-static int marvell_nand_chips_init(struct device *dev, struct marvell_nfc *nfc)
-{
- struct device_node *np = dev->of_node;
- struct device_node *nand_np;
- int max_cs = nfc->caps->max_cs_nb;
- int nchips;
- int ret;
-
- if (!np)
- nchips = 1;
- else
- nchips = of_get_child_count(np);
-
- if (nchips > max_cs) {
- dev_err(dev, "too many NAND chips: %d (max = %d CS)\n", nchips,
- max_cs);
- return -EINVAL;
- }
-
- /*
- * Legacy bindings do not use child nodes to exhibit NAND chip
- * properties and layout. Instead, NAND properties are mixed with the
- * controller ones, and partitions are defined as direct subnodes of the
- * NAND controller node.
- */
- if (nfc->caps->legacy_of_bindings) {
- ret = marvell_nand_chip_init(dev, nfc, np);
- return ret;
- }
-
- for_each_child_of_node(np, nand_np) {
- ret = marvell_nand_chip_init(dev, nfc, nand_np);
- if (ret) {
- of_node_put(nand_np);
- return ret;
- }
- }
-
- return 0;
-}
-
-static void marvell_nand_chips_cleanup(struct marvell_nfc *nfc)
-{
- struct marvell_nand_chip *entry, *temp;
-
- list_for_each_entry_safe(entry, temp, &nfc->chips, node) {
- nand_release(nand_to_mtd(&entry->chip));
- list_del(&entry->node);
- }
-}
-
-static int marvell_nfc_init_dma(struct marvell_nfc *nfc)
-{
- struct platform_device *pdev = container_of(nfc->dev,
- struct platform_device,
- dev);
- struct dma_slave_config config = {};
- struct resource *r;
- dma_cap_mask_t mask;
- struct pxad_param param;
- int ret;
-
- if (!IS_ENABLED(CONFIG_PXA_DMA)) {
- dev_warn(nfc->dev,
- "DMA not enabled in configuration\n");
- return -ENOTSUPP;
- }
-
- ret = dma_set_mask_and_coherent(nfc->dev, DMA_BIT_MASK(32));
- if (ret)
- return ret;
-
- r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (!r) {
- dev_err(nfc->dev, "No resource defined for data DMA\n");
- return -ENXIO;
- }
-
- param.drcmr = r->start;
- param.prio = PXAD_PRIO_LOWEST;
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
- nfc->dma_chan =
- dma_request_slave_channel_compat(mask, pxad_filter_fn,
- &param, nfc->dev,
- "data");
- if (!nfc->dma_chan) {
- dev_err(nfc->dev,
- "Unable to request data DMA channel\n");
- return -ENODEV;
- }
-
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!r)
- return -ENXIO;
-
- config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- config.src_addr = r->start + NDDB;
- config.dst_addr = r->start + NDDB;
- config.src_maxburst = 32;
- config.dst_maxburst = 32;
- ret = dmaengine_slave_config(nfc->dma_chan, &config);
- if (ret < 0) {
- dev_err(nfc->dev, "Failed to configure DMA channel\n");
- return ret;
- }
-
- /*
- * DMA must act on length multiple of 32 and this length may be
- * bigger than the destination buffer. Use this buffer instead
- * for DMA transfers and then copy the desired amount of data to
- * the provided buffer.
- */
- nfc->dma_buf = kmalloc(MAX_CHUNK_SIZE, GFP_KERNEL | GFP_DMA);
- if (!nfc->dma_buf)
- return -ENOMEM;
-
- nfc->use_dma = true;
-
- return 0;
-}
-
-static int marvell_nfc_init(struct marvell_nfc *nfc)
-{
- struct device_node *np = nfc->dev->of_node;
-
- /*
- * Some SoCs like A7k/A8k need to enable manually the NAND
- * controller, gated clocks and reset bits to avoid being bootloader
- * dependent. This is done through the use of the System Functions
- * registers.
- */
- if (nfc->caps->need_system_controller) {
- struct regmap *sysctrl_base =
- syscon_regmap_lookup_by_phandle(np,
- "marvell,system-controller");
- u32 reg;
-
- if (IS_ERR(sysctrl_base))
- return PTR_ERR(sysctrl_base);
-
- reg = GENCONF_SOC_DEVICE_MUX_NFC_EN |
- GENCONF_SOC_DEVICE_MUX_ECC_CLK_RST |
- GENCONF_SOC_DEVICE_MUX_ECC_CORE_RST |
- GENCONF_SOC_DEVICE_MUX_NFC_INT_EN;
- regmap_write(sysctrl_base, GENCONF_SOC_DEVICE_MUX, reg);
-
- regmap_read(sysctrl_base, GENCONF_CLK_GATING_CTRL, &reg);
- reg |= GENCONF_CLK_GATING_CTRL_ND_GATE;
- regmap_write(sysctrl_base, GENCONF_CLK_GATING_CTRL, reg);
-
- regmap_read(sysctrl_base, GENCONF_ND_CLK_CTRL, &reg);
- reg |= GENCONF_ND_CLK_CTRL_EN;
- regmap_write(sysctrl_base, GENCONF_ND_CLK_CTRL, reg);
- }
-
- /* Configure the DMA if appropriate */
- if (!nfc->caps->is_nfcv2)
- marvell_nfc_init_dma(nfc);
-
- /*
- * ECC operations and interruptions are only enabled when specifically
- * needed. ECC shall not be activated in the early stages (fails probe).
- * Arbiter flag, even if marked as "reserved", must be set (empirical).
- * SPARE_EN bit must always be set or ECC bytes will not be at the same
- * offset in the read page and this will fail the protection.
- */
- writel_relaxed(NDCR_ALL_INT | NDCR_ND_ARB_EN | NDCR_SPARE_EN |
- NDCR_RD_ID_CNT(NFCV1_READID_LEN), nfc->regs + NDCR);
- writel_relaxed(0xFFFFFFFF, nfc->regs + NDSR);
- writel_relaxed(0, nfc->regs + NDECCCTRL);
-
- return 0;
-}
-
-static int marvell_nfc_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct resource *r;
- struct marvell_nfc *nfc;
- int ret;
- int irq;
-
- nfc = devm_kzalloc(&pdev->dev, sizeof(struct marvell_nfc),
- GFP_KERNEL);
- if (!nfc)
- return -ENOMEM;
-
- nfc->dev = dev;
- nand_hw_control_init(&nfc->controller);
- INIT_LIST_HEAD(&nfc->chips);
-
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- nfc->regs = devm_ioremap_resource(dev, r);
- if (IS_ERR(nfc->regs))
- return PTR_ERR(nfc->regs);
-
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(dev, "failed to retrieve irq\n");
- return irq;
- }
-
- nfc->ecc_clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(nfc->ecc_clk))
- return PTR_ERR(nfc->ecc_clk);
-
- ret = clk_prepare_enable(nfc->ecc_clk);
- if (ret)
- return ret;
-
- marvell_nfc_disable_int(nfc, NDCR_ALL_INT);
- marvell_nfc_clear_int(nfc, NDCR_ALL_INT);
- ret = devm_request_irq(dev, irq, marvell_nfc_isr,
- 0, "marvell-nfc", nfc);
- if (ret)
- goto unprepare_clk;
-
- /* Get NAND controller capabilities */
- if (pdev->id_entry)
- nfc->caps = (void *)pdev->id_entry->driver_data;
- else
- nfc->caps = of_device_get_match_data(&pdev->dev);
-
- if (!nfc->caps) {
- dev_err(dev, "Could not retrieve NFC caps\n");
- ret = -EINVAL;
- goto unprepare_clk;
- }
-
- /* Init the controller and then probe the chips */
- ret = marvell_nfc_init(nfc);
- if (ret)
- goto unprepare_clk;
-
- platform_set_drvdata(pdev, nfc);
-
- ret = marvell_nand_chips_init(dev, nfc);
- if (ret)
- goto unprepare_clk;
-
- return 0;
-
-unprepare_clk:
- clk_disable_unprepare(nfc->ecc_clk);
-
- return ret;
-}
-
-static int marvell_nfc_remove(struct platform_device *pdev)
-{
- struct marvell_nfc *nfc = platform_get_drvdata(pdev);
-
- marvell_nand_chips_cleanup(nfc);
-
- if (nfc->use_dma) {
- dmaengine_terminate_all(nfc->dma_chan);
- dma_release_channel(nfc->dma_chan);
- }
-
- clk_disable_unprepare(nfc->ecc_clk);
-
- return 0;
-}
-
-static const struct marvell_nfc_caps marvell_armada_8k_nfc_caps = {
- .max_cs_nb = 4,
- .max_rb_nb = 2,
- .need_system_controller = true,
- .is_nfcv2 = true,
-};
-
-static const struct marvell_nfc_caps marvell_armada370_nfc_caps = {
- .max_cs_nb = 4,
- .max_rb_nb = 2,
- .is_nfcv2 = true,
-};
-
-static const struct marvell_nfc_caps marvell_pxa3xx_nfc_caps = {
- .max_cs_nb = 2,
- .max_rb_nb = 1,
- .use_dma = true,
-};
-
-static const struct marvell_nfc_caps marvell_armada_8k_nfc_legacy_caps = {
- .max_cs_nb = 4,
- .max_rb_nb = 2,
- .need_system_controller = true,
- .legacy_of_bindings = true,
- .is_nfcv2 = true,
-};
-
-static const struct marvell_nfc_caps marvell_armada370_nfc_legacy_caps = {
- .max_cs_nb = 4,
- .max_rb_nb = 2,
- .legacy_of_bindings = true,
- .is_nfcv2 = true,
-};
-
-static const struct marvell_nfc_caps marvell_pxa3xx_nfc_legacy_caps = {
- .max_cs_nb = 2,
- .max_rb_nb = 1,
- .legacy_of_bindings = true,
- .use_dma = true,
-};
-
-static const struct platform_device_id marvell_nfc_platform_ids[] = {
- {
- .name = "pxa3xx-nand",
- .driver_data = (kernel_ulong_t)&marvell_pxa3xx_nfc_legacy_caps,
- },
- { /* sentinel */ },
-};
-MODULE_DEVICE_TABLE(platform, marvell_nfc_platform_ids);
-
-static const struct of_device_id marvell_nfc_of_ids[] = {
- {
- .compatible = "marvell,armada-8k-nand-controller",
- .data = &marvell_armada_8k_nfc_caps,
- },
- {
- .compatible = "marvell,armada370-nand-controller",
- .data = &marvell_armada370_nfc_caps,
- },
- {
- .compatible = "marvell,pxa3xx-nand-controller",
- .data = &marvell_pxa3xx_nfc_caps,
- },
- /* Support for old/deprecated bindings: */
- {
- .compatible = "marvell,armada-8k-nand",
- .data = &marvell_armada_8k_nfc_legacy_caps,
- },
- {
- .compatible = "marvell,armada370-nand",
- .data = &marvell_armada370_nfc_legacy_caps,
- },
- {
- .compatible = "marvell,pxa3xx-nand",
- .data = &marvell_pxa3xx_nfc_legacy_caps,
- },
- { /* sentinel */ },
-};
-MODULE_DEVICE_TABLE(of, marvell_nfc_of_ids);
-
-static struct platform_driver marvell_nfc_driver = {
- .driver = {
- .name = "marvell-nfc",
- .of_match_table = marvell_nfc_of_ids,
- },
- .id_table = marvell_nfc_platform_ids,
- .probe = marvell_nfc_probe,
- .remove = marvell_nfc_remove,
-};
-module_platform_driver(marvell_nfc_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Marvell NAND controller driver");
diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c
deleted file mode 100644
index b6b97cc9fba6..000000000000
--- a/drivers/mtd/nand/mpc5121_nfc.c
+++ /dev/null
@@ -1,857 +0,0 @@
-/*
- * Copyright 2004-2008 Freescale Semiconductor, Inc.
- * Copyright 2009 Semihalf.
- *
- * Approved as OSADL project by a majority of OSADL members and funded
- * by OSADL membership fees in 2009; for details see www.osadl.org.
- *
- * Based on original driver from Freescale Semiconductor
- * written by John Rigby <jrigby@freescale.com> on basis
- * of drivers/mtd/nand/mxc_nand.c. Reworked and extended
- * Piotr Ziecik <kosmo@semihalf.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., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301, USA.
- */
-
-#include <linux/module.h>
-#include <linux/clk.h>
-#include <linux/gfp.h>
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/partitions.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
-#include <linux/of_irq.h>
-#include <linux/of_platform.h>
-
-#include <asm/mpc5121.h>
-
-/* Addresses for NFC MAIN RAM BUFFER areas */
-#define NFC_MAIN_AREA(n) ((n) * 0x200)
-
-/* Addresses for NFC SPARE BUFFER areas */
-#define NFC_SPARE_BUFFERS 8
-#define NFC_SPARE_LEN 0x40
-#define NFC_SPARE_AREA(n) (0x1000 + ((n) * NFC_SPARE_LEN))
-
-/* MPC5121 NFC registers */
-#define NFC_BUF_ADDR 0x1E04
-#define NFC_FLASH_ADDR 0x1E06
-#define NFC_FLASH_CMD 0x1E08
-#define NFC_CONFIG 0x1E0A
-#define NFC_ECC_STATUS1 0x1E0C
-#define NFC_ECC_STATUS2 0x1E0E
-#define NFC_SPAS 0x1E10
-#define NFC_WRPROT 0x1E12
-#define NFC_NF_WRPRST 0x1E18
-#define NFC_CONFIG1 0x1E1A
-#define NFC_CONFIG2 0x1E1C
-#define NFC_UNLOCKSTART_BLK0 0x1E20
-#define NFC_UNLOCKEND_BLK0 0x1E22
-#define NFC_UNLOCKSTART_BLK1 0x1E24
-#define NFC_UNLOCKEND_BLK1 0x1E26
-#define NFC_UNLOCKSTART_BLK2 0x1E28
-#define NFC_UNLOCKEND_BLK2 0x1E2A
-#define NFC_UNLOCKSTART_BLK3 0x1E2C
-#define NFC_UNLOCKEND_BLK3 0x1E2E
-
-/* Bit Definitions: NFC_BUF_ADDR */
-#define NFC_RBA_MASK (7 << 0)
-#define NFC_ACTIVE_CS_SHIFT 5
-#define NFC_ACTIVE_CS_MASK (3 << NFC_ACTIVE_CS_SHIFT)
-
-/* Bit Definitions: NFC_CONFIG */
-#define NFC_BLS_UNLOCKED (1 << 1)
-
-/* Bit Definitions: NFC_CONFIG1 */
-#define NFC_ECC_4BIT (1 << 0)
-#define NFC_FULL_PAGE_DMA (1 << 1)
-#define NFC_SPARE_ONLY (1 << 2)
-#define NFC_ECC_ENABLE (1 << 3)
-#define NFC_INT_MASK (1 << 4)
-#define NFC_BIG_ENDIAN (1 << 5)
-#define NFC_RESET (1 << 6)
-#define NFC_CE (1 << 7)
-#define NFC_ONE_CYCLE (1 << 8)
-#define NFC_PPB_32 (0 << 9)
-#define NFC_PPB_64 (1 << 9)
-#define NFC_PPB_128 (2 << 9)
-#define NFC_PPB_256 (3 << 9)
-#define NFC_PPB_MASK (3 << 9)
-#define NFC_FULL_PAGE_INT (1 << 11)
-
-/* Bit Definitions: NFC_CONFIG2 */
-#define NFC_COMMAND (1 << 0)
-#define NFC_ADDRESS (1 << 1)
-#define NFC_INPUT (1 << 2)
-#define NFC_OUTPUT (1 << 3)
-#define NFC_ID (1 << 4)
-#define NFC_STATUS (1 << 5)
-#define NFC_CMD_FAIL (1 << 15)
-#define NFC_INT (1 << 15)
-
-/* Bit Definitions: NFC_WRPROT */
-#define NFC_WPC_LOCK_TIGHT (1 << 0)
-#define NFC_WPC_LOCK (1 << 1)
-#define NFC_WPC_UNLOCK (1 << 2)
-
-#define DRV_NAME "mpc5121_nfc"
-
-/* Timeouts */
-#define NFC_RESET_TIMEOUT 1000 /* 1 ms */
-#define NFC_TIMEOUT (HZ / 10) /* 1/10 s */
-
-struct mpc5121_nfc_prv {
- struct nand_chip chip;
- int irq;
- void __iomem *regs;
- struct clk *clk;
- wait_queue_head_t irq_waitq;
- uint column;
- int spareonly;
- void __iomem *csreg;
- struct device *dev;
-};
-
-static void mpc5121_nfc_done(struct mtd_info *mtd);
-
-/* Read NFC register */
-static inline u16 nfc_read(struct mtd_info *mtd, uint reg)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
-
- return in_be16(prv->regs + reg);
-}
-
-/* Write NFC register */
-static inline void nfc_write(struct mtd_info *mtd, uint reg, u16 val)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
-
- out_be16(prv->regs + reg, val);
-}
-
-/* Set bits in NFC register */
-static inline void nfc_set(struct mtd_info *mtd, uint reg, u16 bits)
-{
- nfc_write(mtd, reg, nfc_read(mtd, reg) | bits);
-}
-
-/* Clear bits in NFC register */
-static inline void nfc_clear(struct mtd_info *mtd, uint reg, u16 bits)
-{
- nfc_write(mtd, reg, nfc_read(mtd, reg) & ~bits);
-}
-
-/* Invoke address cycle */
-static inline void mpc5121_nfc_send_addr(struct mtd_info *mtd, u16 addr)
-{
- nfc_write(mtd, NFC_FLASH_ADDR, addr);
- nfc_write(mtd, NFC_CONFIG2, NFC_ADDRESS);
- mpc5121_nfc_done(mtd);
-}
-
-/* Invoke command cycle */
-static inline void mpc5121_nfc_send_cmd(struct mtd_info *mtd, u16 cmd)
-{
- nfc_write(mtd, NFC_FLASH_CMD, cmd);
- nfc_write(mtd, NFC_CONFIG2, NFC_COMMAND);
- mpc5121_nfc_done(mtd);
-}
-
-/* Send data from NFC buffers to NAND flash */
-static inline void mpc5121_nfc_send_prog_page(struct mtd_info *mtd)
-{
- nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK);
- nfc_write(mtd, NFC_CONFIG2, NFC_INPUT);
- mpc5121_nfc_done(mtd);
-}
-
-/* Receive data from NAND flash */
-static inline void mpc5121_nfc_send_read_page(struct mtd_info *mtd)
-{
- nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK);
- nfc_write(mtd, NFC_CONFIG2, NFC_OUTPUT);
- mpc5121_nfc_done(mtd);
-}
-
-/* Receive ID from NAND flash */
-static inline void mpc5121_nfc_send_read_id(struct mtd_info *mtd)
-{
- nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK);
- nfc_write(mtd, NFC_CONFIG2, NFC_ID);
- mpc5121_nfc_done(mtd);
-}
-
-/* Receive status from NAND flash */
-static inline void mpc5121_nfc_send_read_status(struct mtd_info *mtd)
-{
- nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK);
- nfc_write(mtd, NFC_CONFIG2, NFC_STATUS);
- mpc5121_nfc_done(mtd);
-}
-
-/* NFC interrupt handler */
-static irqreturn_t mpc5121_nfc_irq(int irq, void *data)
-{
- struct mtd_info *mtd = data;
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
-
- nfc_set(mtd, NFC_CONFIG1, NFC_INT_MASK);
- wake_up(&prv->irq_waitq);
-
- return IRQ_HANDLED;
-}
-
-/* Wait for operation complete */
-static void mpc5121_nfc_done(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
- int rv;
-
- if ((nfc_read(mtd, NFC_CONFIG2) & NFC_INT) == 0) {
- nfc_clear(mtd, NFC_CONFIG1, NFC_INT_MASK);
- rv = wait_event_timeout(prv->irq_waitq,
- (nfc_read(mtd, NFC_CONFIG2) & NFC_INT), NFC_TIMEOUT);
-
- if (!rv)
- dev_warn(prv->dev,
- "Timeout while waiting for interrupt.\n");
- }
-
- nfc_clear(mtd, NFC_CONFIG2, NFC_INT);
-}
-
-/* Do address cycle(s) */
-static void mpc5121_nfc_addr_cycle(struct mtd_info *mtd, int column, int page)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- u32 pagemask = chip->pagemask;
-
- if (column != -1) {
- mpc5121_nfc_send_addr(mtd, column);
- if (mtd->writesize > 512)
- mpc5121_nfc_send_addr(mtd, column >> 8);
- }
-
- if (page != -1) {
- do {
- mpc5121_nfc_send_addr(mtd, page & 0xFF);
- page >>= 8;
- pagemask >>= 8;
- } while (pagemask);
- }
-}
-
-/* Control chip select signals */
-static void mpc5121_nfc_select_chip(struct mtd_info *mtd, int chip)
-{
- if (chip < 0) {
- nfc_clear(mtd, NFC_CONFIG1, NFC_CE);
- return;
- }
-
- nfc_clear(mtd, NFC_BUF_ADDR, NFC_ACTIVE_CS_MASK);
- nfc_set(mtd, NFC_BUF_ADDR, (chip << NFC_ACTIVE_CS_SHIFT) &
- NFC_ACTIVE_CS_MASK);
- nfc_set(mtd, NFC_CONFIG1, NFC_CE);
-}
-
-/* Init external chip select logic on ADS5121 board */
-static int ads5121_chipselect_init(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
- struct device_node *dn;
-
- dn = of_find_compatible_node(NULL, NULL, "fsl,mpc5121ads-cpld");
- if (dn) {
- prv->csreg = of_iomap(dn, 0);
- of_node_put(dn);
- if (!prv->csreg)
- return -ENOMEM;
-
- /* CPLD Register 9 controls NAND /CE Lines */
- prv->csreg += 9;
- return 0;
- }
-
- return -EINVAL;
-}
-
-/* Control chips select signal on ADS5121 board */
-static void ads5121_select_chip(struct mtd_info *mtd, int chip)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct mpc5121_nfc_prv *prv = nand_get_controller_data(nand);
- u8 v;
-
- v = in_8(prv->csreg);
- v |= 0x0F;
-
- if (chip >= 0) {
- mpc5121_nfc_select_chip(mtd, 0);
- v &= ~(1 << chip);
- } else
- mpc5121_nfc_select_chip(mtd, -1);
-
- out_8(prv->csreg, v);
-}
-
-/* Read NAND Ready/Busy signal */
-static int mpc5121_nfc_dev_ready(struct mtd_info *mtd)
-{
- /*
- * NFC handles ready/busy signal internally. Therefore, this function
- * always returns status as ready.
- */
- return 1;
-}
-
-/* Write command to NAND flash */
-static void mpc5121_nfc_command(struct mtd_info *mtd, unsigned command,
- int column, int page)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
-
- prv->column = (column >= 0) ? column : 0;
- prv->spareonly = 0;
-
- switch (command) {
- case NAND_CMD_PAGEPROG:
- mpc5121_nfc_send_prog_page(mtd);
- break;
- /*
- * NFC does not support sub-page reads and writes,
- * so emulate them using full page transfers.
- */
- case NAND_CMD_READ0:
- column = 0;
- break;
-
- case NAND_CMD_READ1:
- prv->column += 256;
- command = NAND_CMD_READ0;
- column = 0;
- break;
-
- case NAND_CMD_READOOB:
- prv->spareonly = 1;
- command = NAND_CMD_READ0;
- column = 0;
- break;
-
- case NAND_CMD_SEQIN:
- mpc5121_nfc_command(mtd, NAND_CMD_READ0, column, page);
- column = 0;
- break;
-
- case NAND_CMD_ERASE1:
- case NAND_CMD_ERASE2:
- case NAND_CMD_READID:
- case NAND_CMD_STATUS:
- break;
-
- default:
- return;
- }
-
- mpc5121_nfc_send_cmd(mtd, command);
- mpc5121_nfc_addr_cycle(mtd, column, page);
-
- switch (command) {
- case NAND_CMD_READ0:
- if (mtd->writesize > 512)
- mpc5121_nfc_send_cmd(mtd, NAND_CMD_READSTART);
- mpc5121_nfc_send_read_page(mtd);
- break;
-
- case NAND_CMD_READID:
- mpc5121_nfc_send_read_id(mtd);
- break;
-
- case NAND_CMD_STATUS:
- mpc5121_nfc_send_read_status(mtd);
- if (chip->options & NAND_BUSWIDTH_16)
- prv->column = 1;
- else
- prv->column = 0;
- break;
- }
-}
-
-/* Copy data from/to NFC spare buffers. */
-static void mpc5121_nfc_copy_spare(struct mtd_info *mtd, uint offset,
- u8 *buffer, uint size, int wr)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct mpc5121_nfc_prv *prv = nand_get_controller_data(nand);
- uint o, s, sbsize, blksize;
-
- /*
- * NAND spare area is available through NFC spare buffers.
- * The NFC divides spare area into (page_size / 512) chunks.
- * Each chunk is placed into separate spare memory area, using
- * first (spare_size / num_of_chunks) bytes of the buffer.
- *
- * For NAND device in which the spare area is not divided fully
- * by the number of chunks, number of used bytes in each spare
- * buffer is rounded down to the nearest even number of bytes,
- * and all remaining bytes are added to the last used spare area.
- *
- * For more information read section 26.6.10 of MPC5121e
- * Microcontroller Reference Manual, Rev. 3.
- */
-
- /* Calculate number of valid bytes in each spare buffer */
- sbsize = (mtd->oobsize / (mtd->writesize / 512)) & ~1;
-
- while (size) {
- /* Calculate spare buffer number */
- s = offset / sbsize;
- if (s > NFC_SPARE_BUFFERS - 1)
- s = NFC_SPARE_BUFFERS - 1;
-
- /*
- * Calculate offset to requested data block in selected spare
- * buffer and its size.
- */
- o = offset - (s * sbsize);
- blksize = min(sbsize - o, size);
-
- if (wr)
- memcpy_toio(prv->regs + NFC_SPARE_AREA(s) + o,
- buffer, blksize);
- else
- memcpy_fromio(buffer,
- prv->regs + NFC_SPARE_AREA(s) + o, blksize);
-
- buffer += blksize;
- offset += blksize;
- size -= blksize;
- };
-}
-
-/* Copy data from/to NFC main and spare buffers */
-static void mpc5121_nfc_buf_copy(struct mtd_info *mtd, u_char *buf, int len,
- int wr)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
- uint c = prv->column;
- uint l;
-
- /* Handle spare area access */
- if (prv->spareonly || c >= mtd->writesize) {
- /* Calculate offset from beginning of spare area */
- if (c >= mtd->writesize)
- c -= mtd->writesize;
-
- prv->column += len;
- mpc5121_nfc_copy_spare(mtd, c, buf, len, wr);
- return;
- }
-
- /*
- * Handle main area access - limit copy length to prevent
- * crossing main/spare boundary.
- */
- l = min((uint)len, mtd->writesize - c);
- prv->column += l;
-
- if (wr)
- memcpy_toio(prv->regs + NFC_MAIN_AREA(0) + c, buf, l);
- else
- memcpy_fromio(buf, prv->regs + NFC_MAIN_AREA(0) + c, l);
-
- /* Handle crossing main/spare boundary */
- if (l != len) {
- buf += l;
- len -= l;
- mpc5121_nfc_buf_copy(mtd, buf, len, wr);
- }
-}
-
-/* Read data from NFC buffers */
-static void mpc5121_nfc_read_buf(struct mtd_info *mtd, u_char *buf, int len)
-{
- mpc5121_nfc_buf_copy(mtd, buf, len, 0);
-}
-
-/* Write data to NFC buffers */
-static void mpc5121_nfc_write_buf(struct mtd_info *mtd,
- const u_char *buf, int len)
-{
- mpc5121_nfc_buf_copy(mtd, (u_char *)buf, len, 1);
-}
-
-/* Read byte from NFC buffers */
-static u8 mpc5121_nfc_read_byte(struct mtd_info *mtd)
-{
- u8 tmp;
-
- mpc5121_nfc_read_buf(mtd, &tmp, sizeof(tmp));
-
- return tmp;
-}
-
-/* Read word from NFC buffers */
-static u16 mpc5121_nfc_read_word(struct mtd_info *mtd)
-{
- u16 tmp;
-
- mpc5121_nfc_read_buf(mtd, (u_char *)&tmp, sizeof(tmp));
-
- return tmp;
-}
-
-/*
- * Read NFC configuration from Reset Config Word
- *
- * NFC is configured during reset in basis of information stored
- * in Reset Config Word. There is no other way to set NAND block
- * size, spare size and bus width.
- */
-static int mpc5121_nfc_read_hw_config(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
- struct mpc512x_reset_module *rm;
- struct device_node *rmnode;
- uint rcw_pagesize = 0;
- uint rcw_sparesize = 0;
- uint rcw_width;
- uint rcwh;
- uint romloc, ps;
- int ret = 0;
-
- rmnode = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-reset");
- if (!rmnode) {
- dev_err(prv->dev, "Missing 'fsl,mpc5121-reset' "
- "node in device tree!\n");
- return -ENODEV;
- }
-
- rm = of_iomap(rmnode, 0);
- if (!rm) {
- dev_err(prv->dev, "Error mapping reset module node!\n");
- ret = -EBUSY;
- goto out;
- }
-
- rcwh = in_be32(&rm->rcwhr);
-
- /* Bit 6: NFC bus width */
- rcw_width = ((rcwh >> 6) & 0x1) ? 2 : 1;
-
- /* Bit 7: NFC Page/Spare size */
- ps = (rcwh >> 7) & 0x1;
-
- /* Bits [22:21]: ROM Location */
- romloc = (rcwh >> 21) & 0x3;
-
- /* Decode RCW bits */
- switch ((ps << 2) | romloc) {
- case 0x00:
- case 0x01:
- rcw_pagesize = 512;
- rcw_sparesize = 16;
- break;
- case 0x02:
- case 0x03:
- rcw_pagesize = 4096;
- rcw_sparesize = 128;
- break;
- case 0x04:
- case 0x05:
- rcw_pagesize = 2048;
- rcw_sparesize = 64;
- break;
- case 0x06:
- case 0x07:
- rcw_pagesize = 4096;
- rcw_sparesize = 218;
- break;
- }
-
- mtd->writesize = rcw_pagesize;
- mtd->oobsize = rcw_sparesize;
- if (rcw_width == 2)
- chip->options |= NAND_BUSWIDTH_16;
-
- dev_notice(prv->dev, "Configured for "
- "%u-bit NAND, page size %u "
- "with %u spare.\n",
- rcw_width * 8, rcw_pagesize,
- rcw_sparesize);
- iounmap(rm);
-out:
- of_node_put(rmnode);
- return ret;
-}
-
-/* Free driver resources */
-static void mpc5121_nfc_free(struct device *dev, struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
-
- if (prv->clk)
- clk_disable_unprepare(prv->clk);
-
- if (prv->csreg)
- iounmap(prv->csreg);
-}
-
-static int mpc5121_nfc_probe(struct platform_device *op)
-{
- struct device_node *dn = op->dev.of_node;
- struct clk *clk;
- struct device *dev = &op->dev;
- struct mpc5121_nfc_prv *prv;
- struct resource res;
- struct mtd_info *mtd;
- struct nand_chip *chip;
- unsigned long regs_paddr, regs_size;
- const __be32 *chips_no;
- int resettime = 0;
- int retval = 0;
- int rev, len;
-
- /*
- * Check SoC revision. This driver supports only NFC
- * in MPC5121 revision 2 and MPC5123 revision 3.
- */
- rev = (mfspr(SPRN_SVR) >> 4) & 0xF;
- if ((rev != 2) && (rev != 3)) {
- dev_err(dev, "SoC revision %u is not supported!\n", rev);
- return -ENXIO;
- }
-
- prv = devm_kzalloc(dev, sizeof(*prv), GFP_KERNEL);
- if (!prv)
- return -ENOMEM;
-
- chip = &prv->chip;
- mtd = nand_to_mtd(chip);
-
- mtd->dev.parent = dev;
- nand_set_controller_data(chip, prv);
- nand_set_flash_node(chip, dn);
- prv->dev = dev;
-
- /* Read NFC configuration from Reset Config Word */
- retval = mpc5121_nfc_read_hw_config(mtd);
- if (retval) {
- dev_err(dev, "Unable to read NFC config!\n");
- return retval;
- }
-
- prv->irq = irq_of_parse_and_map(dn, 0);
- if (prv->irq == NO_IRQ) {
- dev_err(dev, "Error mapping IRQ!\n");
- return -EINVAL;
- }
-
- retval = of_address_to_resource(dn, 0, &res);
- if (retval) {
- dev_err(dev, "Error parsing memory region!\n");
- return retval;
- }
-
- chips_no = of_get_property(dn, "chips", &len);
- if (!chips_no || len != sizeof(*chips_no)) {
- dev_err(dev, "Invalid/missing 'chips' property!\n");
- return -EINVAL;
- }
-
- regs_paddr = res.start;
- regs_size = resource_size(&res);
-
- if (!devm_request_mem_region(dev, regs_paddr, regs_size, DRV_NAME)) {
- dev_err(dev, "Error requesting memory region!\n");
- return -EBUSY;
- }
-
- prv->regs = devm_ioremap(dev, regs_paddr, regs_size);
- if (!prv->regs) {
- dev_err(dev, "Error mapping memory region!\n");
- return -ENOMEM;
- }
-
- mtd->name = "MPC5121 NAND";
- chip->dev_ready = mpc5121_nfc_dev_ready;
- chip->cmdfunc = mpc5121_nfc_command;
- chip->read_byte = mpc5121_nfc_read_byte;
- chip->read_word = mpc5121_nfc_read_word;
- chip->read_buf = mpc5121_nfc_read_buf;
- chip->write_buf = mpc5121_nfc_write_buf;
- chip->select_chip = mpc5121_nfc_select_chip;
- chip->onfi_set_features = nand_onfi_get_set_features_notsupp;
- chip->onfi_get_features = nand_onfi_get_set_features_notsupp;
- chip->bbt_options = NAND_BBT_USE_FLASH;
- chip->ecc.mode = NAND_ECC_SOFT;
- chip->ecc.algo = NAND_ECC_HAMMING;
-
- /* Support external chip-select logic on ADS5121 board */
- if (of_machine_is_compatible("fsl,mpc5121ads")) {
- retval = ads5121_chipselect_init(mtd);
- if (retval) {
- dev_err(dev, "Chipselect init error!\n");
- return retval;
- }
-
- chip->select_chip = ads5121_select_chip;
- }
-
- /* Enable NFC clock */
- clk = devm_clk_get(dev, "ipg");
- if (IS_ERR(clk)) {
- dev_err(dev, "Unable to acquire NFC clock!\n");
- retval = PTR_ERR(clk);
- goto error;
- }
- retval = clk_prepare_enable(clk);
- if (retval) {
- dev_err(dev, "Unable to enable NFC clock!\n");
- goto error;
- }
- prv->clk = clk;
-
- /* Reset NAND Flash controller */
- nfc_set(mtd, NFC_CONFIG1, NFC_RESET);
- while (nfc_read(mtd, NFC_CONFIG1) & NFC_RESET) {
- if (resettime++ >= NFC_RESET_TIMEOUT) {
- dev_err(dev, "Timeout while resetting NFC!\n");
- retval = -EINVAL;
- goto error;
- }
-
- udelay(1);
- }
-
- /* Enable write to NFC memory */
- nfc_write(mtd, NFC_CONFIG, NFC_BLS_UNLOCKED);
-
- /* Enable write to all NAND pages */
- nfc_write(mtd, NFC_UNLOCKSTART_BLK0, 0x0000);
- nfc_write(mtd, NFC_UNLOCKEND_BLK0, 0xFFFF);
- nfc_write(mtd, NFC_WRPROT, NFC_WPC_UNLOCK);
-
- /*
- * Setup NFC:
- * - Big Endian transfers,
- * - Interrupt after full page read/write.
- */
- nfc_write(mtd, NFC_CONFIG1, NFC_BIG_ENDIAN | NFC_INT_MASK |
- NFC_FULL_PAGE_INT);
-
- /* Set spare area size */
- nfc_write(mtd, NFC_SPAS, mtd->oobsize >> 1);
-
- init_waitqueue_head(&prv->irq_waitq);
- retval = devm_request_irq(dev, prv->irq, &mpc5121_nfc_irq, 0, DRV_NAME,
- mtd);
- if (retval) {
- dev_err(dev, "Error requesting IRQ!\n");
- goto error;
- }
-
- /* Detect NAND chips */
- retval = nand_scan(mtd, be32_to_cpup(chips_no));
- if (retval) {
- dev_err(dev, "NAND Flash not found !\n");
- goto error;
- }
-
- /* Set erase block size */
- switch (mtd->erasesize / mtd->writesize) {
- case 32:
- nfc_set(mtd, NFC_CONFIG1, NFC_PPB_32);
- break;
-
- case 64:
- nfc_set(mtd, NFC_CONFIG1, NFC_PPB_64);
- break;
-
- case 128:
- nfc_set(mtd, NFC_CONFIG1, NFC_PPB_128);
- break;
-
- case 256:
- nfc_set(mtd, NFC_CONFIG1, NFC_PPB_256);
- break;
-
- default:
- dev_err(dev, "Unsupported NAND flash!\n");
- retval = -ENXIO;
- goto error;
- }
-
- dev_set_drvdata(dev, mtd);
-
- /* Register device in MTD */
- retval = mtd_device_register(mtd, NULL, 0);
- if (retval) {
- dev_err(dev, "Error adding MTD device!\n");
- goto error;
- }
-
- return 0;
-error:
- mpc5121_nfc_free(dev, mtd);
- return retval;
-}
-
-static int mpc5121_nfc_remove(struct platform_device *op)
-{
- struct device *dev = &op->dev;
- struct mtd_info *mtd = dev_get_drvdata(dev);
-
- nand_release(mtd);
- mpc5121_nfc_free(dev, mtd);
-
- return 0;
-}
-
-static const struct of_device_id mpc5121_nfc_match[] = {
- { .compatible = "fsl,mpc5121-nfc", },
- {},
-};
-MODULE_DEVICE_TABLE(of, mpc5121_nfc_match);
-
-static struct platform_driver mpc5121_nfc_driver = {
- .probe = mpc5121_nfc_probe,
- .remove = mpc5121_nfc_remove,
- .driver = {
- .name = DRV_NAME,
- .of_match_table = mpc5121_nfc_match,
- },
-};
-
-module_platform_driver(mpc5121_nfc_driver);
-
-MODULE_AUTHOR("Freescale Semiconductor, Inc.");
-MODULE_DESCRIPTION("MPC5121 NAND MTD driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
deleted file mode 100644
index f3be0b2a8869..000000000000
--- a/drivers/mtd/nand/mxc_nand.c
+++ /dev/null
@@ -1,1853 +0,0 @@
-/*
- * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
- * Copyright 2008 Sascha Hauer, kernel@pengutronix.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 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/delay.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/partitions.h>
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/clk.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/completion.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-
-#include <asm/mach/flash.h>
-#include <linux/platform_data/mtd-mxc_nand.h>
-
-#define DRIVER_NAME "mxc_nand"
-
-/* Addresses for NFC registers */
-#define NFC_V1_V2_BUF_SIZE (host->regs + 0x00)
-#define NFC_V1_V2_BUF_ADDR (host->regs + 0x04)
-#define NFC_V1_V2_FLASH_ADDR (host->regs + 0x06)
-#define NFC_V1_V2_FLASH_CMD (host->regs + 0x08)
-#define NFC_V1_V2_CONFIG (host->regs + 0x0a)
-#define NFC_V1_V2_ECC_STATUS_RESULT (host->regs + 0x0c)
-#define NFC_V1_V2_RSLTMAIN_AREA (host->regs + 0x0e)
-#define NFC_V1_V2_RSLTSPARE_AREA (host->regs + 0x10)
-#define NFC_V1_V2_WRPROT (host->regs + 0x12)
-#define NFC_V1_UNLOCKSTART_BLKADDR (host->regs + 0x14)
-#define NFC_V1_UNLOCKEND_BLKADDR (host->regs + 0x16)
-#define NFC_V21_UNLOCKSTART_BLKADDR0 (host->regs + 0x20)
-#define NFC_V21_UNLOCKSTART_BLKADDR1 (host->regs + 0x24)
-#define NFC_V21_UNLOCKSTART_BLKADDR2 (host->regs + 0x28)
-#define NFC_V21_UNLOCKSTART_BLKADDR3 (host->regs + 0x2c)
-#define NFC_V21_UNLOCKEND_BLKADDR0 (host->regs + 0x22)
-#define NFC_V21_UNLOCKEND_BLKADDR1 (host->regs + 0x26)
-#define NFC_V21_UNLOCKEND_BLKADDR2 (host->regs + 0x2a)
-#define NFC_V21_UNLOCKEND_BLKADDR3 (host->regs + 0x2e)
-#define NFC_V1_V2_NF_WRPRST (host->regs + 0x18)
-#define NFC_V1_V2_CONFIG1 (host->regs + 0x1a)
-#define NFC_V1_V2_CONFIG2 (host->regs + 0x1c)
-
-#define NFC_V2_CONFIG1_ECC_MODE_4 (1 << 0)
-#define NFC_V1_V2_CONFIG1_SP_EN (1 << 2)
-#define NFC_V1_V2_CONFIG1_ECC_EN (1 << 3)
-#define NFC_V1_V2_CONFIG1_INT_MSK (1 << 4)
-#define NFC_V1_V2_CONFIG1_BIG (1 << 5)
-#define NFC_V1_V2_CONFIG1_RST (1 << 6)
-#define NFC_V1_V2_CONFIG1_CE (1 << 7)
-#define NFC_V2_CONFIG1_ONE_CYCLE (1 << 8)
-#define NFC_V2_CONFIG1_PPB(x) (((x) & 0x3) << 9)
-#define NFC_V2_CONFIG1_FP_INT (1 << 11)
-
-#define NFC_V1_V2_CONFIG2_INT (1 << 15)
-
-/*
- * Operation modes for the NFC. Valid for v1, v2 and v3
- * type controllers.
- */
-#define NFC_CMD (1 << 0)
-#define NFC_ADDR (1 << 1)
-#define NFC_INPUT (1 << 2)
-#define NFC_OUTPUT (1 << 3)
-#define NFC_ID (1 << 4)
-#define NFC_STATUS (1 << 5)
-
-#define NFC_V3_FLASH_CMD (host->regs_axi + 0x00)
-#define NFC_V3_FLASH_ADDR0 (host->regs_axi + 0x04)
-
-#define NFC_V3_CONFIG1 (host->regs_axi + 0x34)
-#define NFC_V3_CONFIG1_SP_EN (1 << 0)
-#define NFC_V3_CONFIG1_RBA(x) (((x) & 0x7 ) << 4)
-
-#define NFC_V3_ECC_STATUS_RESULT (host->regs_axi + 0x38)
-
-#define NFC_V3_LAUNCH (host->regs_axi + 0x40)
-
-#define NFC_V3_WRPROT (host->regs_ip + 0x0)
-#define NFC_V3_WRPROT_LOCK_TIGHT (1 << 0)
-#define NFC_V3_WRPROT_LOCK (1 << 1)
-#define NFC_V3_WRPROT_UNLOCK (1 << 2)
-#define NFC_V3_WRPROT_BLS_UNLOCK (2 << 6)
-
-#define NFC_V3_WRPROT_UNLOCK_BLK_ADD0 (host->regs_ip + 0x04)
-
-#define NFC_V3_CONFIG2 (host->regs_ip + 0x24)
-#define NFC_V3_CONFIG2_PS_512 (0 << 0)
-#define NFC_V3_CONFIG2_PS_2048 (1 << 0)
-#define NFC_V3_CONFIG2_PS_4096 (2 << 0)
-#define NFC_V3_CONFIG2_ONE_CYCLE (1 << 2)
-#define NFC_V3_CONFIG2_ECC_EN (1 << 3)
-#define NFC_V3_CONFIG2_2CMD_PHASES (1 << 4)
-#define NFC_V3_CONFIG2_NUM_ADDR_PHASE0 (1 << 5)
-#define NFC_V3_CONFIG2_ECC_MODE_8 (1 << 6)
-#define NFC_V3_CONFIG2_PPB(x, shift) (((x) & 0x3) << shift)
-#define NFC_V3_CONFIG2_NUM_ADDR_PHASE1(x) (((x) & 0x3) << 12)
-#define NFC_V3_CONFIG2_INT_MSK (1 << 15)
-#define NFC_V3_CONFIG2_ST_CMD(x) (((x) & 0xff) << 24)
-#define NFC_V3_CONFIG2_SPAS(x) (((x) & 0xff) << 16)
-
-#define NFC_V3_CONFIG3 (host->regs_ip + 0x28)
-#define NFC_V3_CONFIG3_ADD_OP(x) (((x) & 0x3) << 0)
-#define NFC_V3_CONFIG3_FW8 (1 << 3)
-#define NFC_V3_CONFIG3_SBB(x) (((x) & 0x7) << 8)
-#define NFC_V3_CONFIG3_NUM_OF_DEVICES(x) (((x) & 0x7) << 12)
-#define NFC_V3_CONFIG3_RBB_MODE (1 << 15)
-#define NFC_V3_CONFIG3_NO_SDMA (1 << 20)
-
-#define NFC_V3_IPC (host->regs_ip + 0x2C)
-#define NFC_V3_IPC_CREQ (1 << 0)
-#define NFC_V3_IPC_INT (1 << 31)
-
-#define NFC_V3_DELAY_LINE (host->regs_ip + 0x34)
-
-struct mxc_nand_host;
-
-struct mxc_nand_devtype_data {
- void (*preset)(struct mtd_info *);
- void (*send_cmd)(struct mxc_nand_host *, uint16_t, int);
- void (*send_addr)(struct mxc_nand_host *, uint16_t, int);
- void (*send_page)(struct mtd_info *, unsigned int);
- void (*send_read_id)(struct mxc_nand_host *);
- uint16_t (*get_dev_status)(struct mxc_nand_host *);
- int (*check_int)(struct mxc_nand_host *);
- void (*irq_control)(struct mxc_nand_host *, int);
- u32 (*get_ecc_status)(struct mxc_nand_host *);
- const struct mtd_ooblayout_ops *ooblayout;
- void (*select_chip)(struct mtd_info *mtd, int chip);
- int (*correct_data)(struct mtd_info *mtd, u_char *dat,
- u_char *read_ecc, u_char *calc_ecc);
- int (*setup_data_interface)(struct mtd_info *mtd, int csline,
- const struct nand_data_interface *conf);
-
- /*
- * On i.MX21 the CONFIG2:INT bit cannot be read if interrupts are masked
- * (CONFIG1:INT_MSK is set). To handle this the driver uses
- * enable_irq/disable_irq_nosync instead of CONFIG1:INT_MSK
- */
- int irqpending_quirk;
- int needs_ip;
-
- size_t regs_offset;
- size_t spare0_offset;
- size_t axi_offset;
-
- int spare_len;
- int eccbytes;
- int eccsize;
- int ppb_shift;
-};
-
-struct mxc_nand_host {
- struct nand_chip nand;
- struct device *dev;
-
- void __iomem *spare0;
- void __iomem *main_area0;
-
- void __iomem *base;
- void __iomem *regs;
- void __iomem *regs_axi;
- void __iomem *regs_ip;
- int status_request;
- struct clk *clk;
- int clk_act;
- int irq;
- int eccsize;
- int used_oobsize;
- int active_cs;
-
- struct completion op_completion;
-
- uint8_t *data_buf;
- unsigned int buf_start;
-
- const struct mxc_nand_devtype_data *devtype_data;
- struct mxc_nand_platform_data pdata;
-};
-
-static const char * const part_probes[] = {
- "cmdlinepart", "RedBoot", "ofpart", NULL };
-
-static void memcpy32_fromio(void *trg, const void __iomem *src, size_t size)
-{
- int i;
- u32 *t = trg;
- const __iomem u32 *s = src;
-
- for (i = 0; i < (size >> 2); i++)
- *t++ = __raw_readl(s++);
-}
-
-static void memcpy16_fromio(void *trg, const void __iomem *src, size_t size)
-{
- int i;
- u16 *t = trg;
- const __iomem u16 *s = src;
-
- /* We assume that src (IO) is always 32bit aligned */
- if (PTR_ALIGN(trg, 4) == trg && IS_ALIGNED(size, 4)) {
- memcpy32_fromio(trg, src, size);
- return;
- }
-
- for (i = 0; i < (size >> 1); i++)
- *t++ = __raw_readw(s++);
-}
-
-static inline void memcpy32_toio(void __iomem *trg, const void *src, int size)
-{
- /* __iowrite32_copy use 32bit size values so divide by 4 */
- __iowrite32_copy(trg, src, size / 4);
-}
-
-static void memcpy16_toio(void __iomem *trg, const void *src, int size)
-{
- int i;
- __iomem u16 *t = trg;
- const u16 *s = src;
-
- /* We assume that trg (IO) is always 32bit aligned */
- if (PTR_ALIGN(src, 4) == src && IS_ALIGNED(size, 4)) {
- memcpy32_toio(trg, src, size);
- return;
- }
-
- for (i = 0; i < (size >> 1); i++)
- __raw_writew(*s++, t++);
-}
-
-static int check_int_v3(struct mxc_nand_host *host)
-{
- uint32_t tmp;
-
- tmp = readl(NFC_V3_IPC);
- if (!(tmp & NFC_V3_IPC_INT))
- return 0;
-
- tmp &= ~NFC_V3_IPC_INT;
- writel(tmp, NFC_V3_IPC);
-
- return 1;
-}
-
-static int check_int_v1_v2(struct mxc_nand_host *host)
-{
- uint32_t tmp;
-
- tmp = readw(NFC_V1_V2_CONFIG2);
- if (!(tmp & NFC_V1_V2_CONFIG2_INT))
- return 0;
-
- if (!host->devtype_data->irqpending_quirk)
- writew(tmp & ~NFC_V1_V2_CONFIG2_INT, NFC_V1_V2_CONFIG2);
-
- return 1;
-}
-
-static void irq_control_v1_v2(struct mxc_nand_host *host, int activate)
-{
- uint16_t tmp;
-
- tmp = readw(NFC_V1_V2_CONFIG1);
-
- if (activate)
- tmp &= ~NFC_V1_V2_CONFIG1_INT_MSK;
- else
- tmp |= NFC_V1_V2_CONFIG1_INT_MSK;
-
- writew(tmp, NFC_V1_V2_CONFIG1);
-}
-
-static void irq_control_v3(struct mxc_nand_host *host, int activate)
-{
- uint32_t tmp;
-
- tmp = readl(NFC_V3_CONFIG2);
-
- if (activate)
- tmp &= ~NFC_V3_CONFIG2_INT_MSK;
- else
- tmp |= NFC_V3_CONFIG2_INT_MSK;
-
- writel(tmp, NFC_V3_CONFIG2);
-}
-
-static void irq_control(struct mxc_nand_host *host, int activate)
-{
- if (host->devtype_data->irqpending_quirk) {
- if (activate)
- enable_irq(host->irq);
- else
- disable_irq_nosync(host->irq);
- } else {
- host->devtype_data->irq_control(host, activate);
- }
-}
-
-static u32 get_ecc_status_v1(struct mxc_nand_host *host)
-{
- return readw(NFC_V1_V2_ECC_STATUS_RESULT);
-}
-
-static u32 get_ecc_status_v2(struct mxc_nand_host *host)
-{
- return readl(NFC_V1_V2_ECC_STATUS_RESULT);
-}
-
-static u32 get_ecc_status_v3(struct mxc_nand_host *host)
-{
- return readl(NFC_V3_ECC_STATUS_RESULT);
-}
-
-static irqreturn_t mxc_nfc_irq(int irq, void *dev_id)
-{
- struct mxc_nand_host *host = dev_id;
-
- if (!host->devtype_data->check_int(host))
- return IRQ_NONE;
-
- irq_control(host, 0);
-
- complete(&host->op_completion);
-
- return IRQ_HANDLED;
-}
-
-/* This function polls the NANDFC to wait for the basic operation to
- * complete by checking the INT bit of config2 register.
- */
-static int wait_op_done(struct mxc_nand_host *host, int useirq)
-{
- int ret = 0;
-
- /*
- * If operation is already complete, don't bother to setup an irq or a
- * loop.
- */
- if (host->devtype_data->check_int(host))
- return 0;
-
- if (useirq) {
- unsigned long timeout;
-
- reinit_completion(&host->op_completion);
-
- irq_control(host, 1);
-
- timeout = wait_for_completion_timeout(&host->op_completion, HZ);
- if (!timeout && !host->devtype_data->check_int(host)) {
- dev_dbg(host->dev, "timeout waiting for irq\n");
- ret = -ETIMEDOUT;
- }
- } else {
- int max_retries = 8000;
- int done;
-
- do {
- udelay(1);
-
- done = host->devtype_data->check_int(host);
- if (done)
- break;
-
- } while (--max_retries);
-
- if (!done) {
- dev_dbg(host->dev, "timeout polling for completion\n");
- ret = -ETIMEDOUT;
- }
- }
-
- WARN_ONCE(ret < 0, "timeout! useirq=%d\n", useirq);
-
- return ret;
-}
-
-static void send_cmd_v3(struct mxc_nand_host *host, uint16_t cmd, int useirq)
-{
- /* fill command */
- writel(cmd, NFC_V3_FLASH_CMD);
-
- /* send out command */
- writel(NFC_CMD, NFC_V3_LAUNCH);
-
- /* Wait for operation to complete */
- wait_op_done(host, useirq);
-}
-
-/* This function issues the specified command to the NAND device and
- * waits for completion. */
-static void send_cmd_v1_v2(struct mxc_nand_host *host, uint16_t cmd, int useirq)
-{
- dev_dbg(host->dev, "send_cmd(host, 0x%x, %d)\n", cmd, useirq);
-
- writew(cmd, NFC_V1_V2_FLASH_CMD);
- writew(NFC_CMD, NFC_V1_V2_CONFIG2);
-
- if (host->devtype_data->irqpending_quirk && (cmd == NAND_CMD_RESET)) {
- int max_retries = 100;
- /* Reset completion is indicated by NFC_CONFIG2 */
- /* being set to 0 */
- while (max_retries-- > 0) {
- if (readw(NFC_V1_V2_CONFIG2) == 0) {
- break;
- }
- udelay(1);
- }
- if (max_retries < 0)
- dev_dbg(host->dev, "%s: RESET failed\n", __func__);
- } else {
- /* Wait for operation to complete */
- wait_op_done(host, useirq);
- }
-}
-
-static void send_addr_v3(struct mxc_nand_host *host, uint16_t addr, int islast)
-{
- /* fill address */
- writel(addr, NFC_V3_FLASH_ADDR0);
-
- /* send out address */
- writel(NFC_ADDR, NFC_V3_LAUNCH);
-
- wait_op_done(host, 0);
-}
-
-/* This function sends an address (or partial address) to the
- * NAND device. The address is used to select the source/destination for
- * a NAND command. */
-static void send_addr_v1_v2(struct mxc_nand_host *host, uint16_t addr, int islast)
-{
- dev_dbg(host->dev, "send_addr(host, 0x%x %d)\n", addr, islast);
-
- writew(addr, NFC_V1_V2_FLASH_ADDR);
- writew(NFC_ADDR, NFC_V1_V2_CONFIG2);
-
- /* Wait for operation to complete */
- wait_op_done(host, islast);
-}
-
-static void send_page_v3(struct mtd_info *mtd, unsigned int ops)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
- uint32_t tmp;
-
- tmp = readl(NFC_V3_CONFIG1);
- tmp &= ~(7 << 4);
- writel(tmp, NFC_V3_CONFIG1);
-
- /* transfer data from NFC ram to nand */
- writel(ops, NFC_V3_LAUNCH);
-
- wait_op_done(host, false);
-}
-
-static void send_page_v2(struct mtd_info *mtd, unsigned int ops)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
-
- /* NANDFC buffer 0 is used for page read/write */
- writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR);
-
- writew(ops, NFC_V1_V2_CONFIG2);
-
- /* Wait for operation to complete */
- wait_op_done(host, true);
-}
-
-static void send_page_v1(struct mtd_info *mtd, unsigned int ops)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
- int bufs, i;
-
- if (mtd->writesize > 512)
- bufs = 4;
- else
- bufs = 1;
-
- for (i = 0; i < bufs; i++) {
-
- /* NANDFC buffer 0 is used for page read/write */
- writew((host->active_cs << 4) | i, NFC_V1_V2_BUF_ADDR);
-
- writew(ops, NFC_V1_V2_CONFIG2);
-
- /* Wait for operation to complete */
- wait_op_done(host, true);
- }
-}
-
-static void send_read_id_v3(struct mxc_nand_host *host)
-{
- /* Read ID into main buffer */
- writel(NFC_ID, NFC_V3_LAUNCH);
-
- wait_op_done(host, true);
-
- memcpy32_fromio(host->data_buf, host->main_area0, 16);
-}
-
-/* Request the NANDFC to perform a read of the NAND device ID. */
-static void send_read_id_v1_v2(struct mxc_nand_host *host)
-{
- /* NANDFC buffer 0 is used for device ID output */
- writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR);
-
- writew(NFC_ID, NFC_V1_V2_CONFIG2);
-
- /* Wait for operation to complete */
- wait_op_done(host, true);
-
- memcpy32_fromio(host->data_buf, host->main_area0, 16);
-}
-
-static uint16_t get_dev_status_v3(struct mxc_nand_host *host)
-{
- writew(NFC_STATUS, NFC_V3_LAUNCH);
- wait_op_done(host, true);
-
- return readl(NFC_V3_CONFIG1) >> 16;
-}
-
-/* This function requests the NANDFC to perform a read of the
- * NAND device status and returns the current status. */
-static uint16_t get_dev_status_v1_v2(struct mxc_nand_host *host)
-{
- void __iomem *main_buf = host->main_area0;
- uint32_t store;
- uint16_t ret;
-
- writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR);
-
- /*
- * The device status is stored in main_area0. To
- * prevent corruption of the buffer save the value
- * and restore it afterwards.
- */
- store = readl(main_buf);
-
- writew(NFC_STATUS, NFC_V1_V2_CONFIG2);
- wait_op_done(host, true);
-
- ret = readw(main_buf);
-
- writel(store, main_buf);
-
- return ret;
-}
-
-/* This functions is used by upper layer to checks if device is ready */
-static int mxc_nand_dev_ready(struct mtd_info *mtd)
-{
- /*
- * NFC handles R/B internally. Therefore, this function
- * always returns status as ready.
- */
- return 1;
-}
-
-static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode)
-{
- /*
- * If HW ECC is enabled, we turn it on during init. There is
- * no need to enable again here.
- */
-}
-
-static int mxc_nand_correct_data_v1(struct mtd_info *mtd, u_char *dat,
- u_char *read_ecc, u_char *calc_ecc)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
-
- /*
- * 1-Bit errors are automatically corrected in HW. No need for
- * additional correction. 2-Bit errors cannot be corrected by
- * HW ECC, so we need to return failure
- */
- uint16_t ecc_status = get_ecc_status_v1(host);
-
- if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) {
- dev_dbg(host->dev, "HWECC uncorrectable 2-bit ECC error\n");
- return -EBADMSG;
- }
-
- return 0;
-}
-
-static int mxc_nand_correct_data_v2_v3(struct mtd_info *mtd, u_char *dat,
- u_char *read_ecc, u_char *calc_ecc)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
- u32 ecc_stat, err;
- int no_subpages = 1;
- int ret = 0;
- u8 ecc_bit_mask, err_limit;
-
- ecc_bit_mask = (host->eccsize == 4) ? 0x7 : 0xf;
- err_limit = (host->eccsize == 4) ? 0x4 : 0x8;
-
- no_subpages = mtd->writesize >> 9;
-
- ecc_stat = host->devtype_data->get_ecc_status(host);
-
- do {
- err = ecc_stat & ecc_bit_mask;
- if (err > err_limit) {
- dev_dbg(host->dev, "UnCorrectable RS-ECC Error\n");
- return -EBADMSG;
- } else {
- ret += err;
- }
- ecc_stat >>= 4;
- } while (--no_subpages);
-
- dev_dbg(host->dev, "%d Symbol Correctable RS-ECC Error\n", ret);
-
- return ret;
-}
-
-static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
- u_char *ecc_code)
-{
- return 0;
-}
-
-static u_char mxc_nand_read_byte(struct mtd_info *mtd)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
- uint8_t ret;
-
- /* Check for status request */
- if (host->status_request)
- return host->devtype_data->get_dev_status(host) & 0xFF;
-
- if (nand_chip->options & NAND_BUSWIDTH_16) {
- /* only take the lower byte of each word */
- ret = *(uint16_t *)(host->data_buf + host->buf_start);
-
- host->buf_start += 2;
- } else {
- ret = *(uint8_t *)(host->data_buf + host->buf_start);
- host->buf_start++;
- }
-
- dev_dbg(host->dev, "%s: ret=0x%hhx (start=%u)\n", __func__, ret, host->buf_start);
- return ret;
-}
-
-static uint16_t mxc_nand_read_word(struct mtd_info *mtd)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
- uint16_t ret;
-
- ret = *(uint16_t *)(host->data_buf + host->buf_start);
- host->buf_start += 2;
-
- return ret;
-}
-
-/* Write data of length len to buffer buf. The data to be
- * written on NAND Flash is first copied to RAMbuffer. After the Data Input
- * Operation by the NFC, the data is written to NAND Flash */
-static void mxc_nand_write_buf(struct mtd_info *mtd,
- const u_char *buf, int len)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
- u16 col = host->buf_start;
- int n = mtd->oobsize + mtd->writesize - col;
-
- n = min(n, len);
-
- memcpy(host->data_buf + col, buf, n);
-
- host->buf_start += n;
-}
-
-/* Read the data buffer from the NAND Flash. To read the data from NAND
- * Flash first the data output cycle is initiated by the NFC, which copies
- * the data to RAMbuffer. This data of length len is then copied to buffer buf.
- */
-static void mxc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
- u16 col = host->buf_start;
- int n = mtd->oobsize + mtd->writesize - col;
-
- n = min(n, len);
-
- memcpy(buf, host->data_buf + col, n);
-
- host->buf_start += n;
-}
-
-/* This function is used by upper layer for select and
- * deselect of the NAND chip */
-static void mxc_nand_select_chip_v1_v3(struct mtd_info *mtd, int chip)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
-
- if (chip == -1) {
- /* Disable the NFC clock */
- if (host->clk_act) {
- clk_disable_unprepare(host->clk);
- host->clk_act = 0;
- }
- return;
- }
-
- if (!host->clk_act) {
- /* Enable the NFC clock */
- clk_prepare_enable(host->clk);
- host->clk_act = 1;
- }
-}
-
-static void mxc_nand_select_chip_v2(struct mtd_info *mtd, int chip)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
-
- if (chip == -1) {
- /* Disable the NFC clock */
- if (host->clk_act) {
- clk_disable_unprepare(host->clk);
- host->clk_act = 0;
- }
- return;
- }
-
- if (!host->clk_act) {
- /* Enable the NFC clock */
- clk_prepare_enable(host->clk);
- host->clk_act = 1;
- }
-
- host->active_cs = chip;
- writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR);
-}
-
-/*
- * The controller splits a page into data chunks of 512 bytes + partial oob.
- * There are writesize / 512 such chunks, the size of the partial oob parts is
- * oobsize / #chunks rounded down to a multiple of 2. The last oob chunk then
- * contains additionally the byte lost by rounding (if any).
- * This function handles the needed shuffling between host->data_buf (which
- * holds a page in natural order, i.e. writesize bytes data + oobsize bytes
- * spare) and the NFC buffer.
- */
-static void copy_spare(struct mtd_info *mtd, bool bfrom)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct mxc_nand_host *host = nand_get_controller_data(this);
- u16 i, oob_chunk_size;
- u16 num_chunks = mtd->writesize / 512;
-
- u8 *d = host->data_buf + mtd->writesize;
- u8 __iomem *s = host->spare0;
- u16 sparebuf_size = host->devtype_data->spare_len;
-
- /* size of oob chunk for all but possibly the last one */
- oob_chunk_size = (host->used_oobsize / num_chunks) & ~1;
-
- if (bfrom) {
- for (i = 0; i < num_chunks - 1; i++)
- memcpy16_fromio(d + i * oob_chunk_size,
- s + i * sparebuf_size,
- oob_chunk_size);
-
- /* the last chunk */
- memcpy16_fromio(d + i * oob_chunk_size,
- s + i * sparebuf_size,
- host->used_oobsize - i * oob_chunk_size);
- } else {
- for (i = 0; i < num_chunks - 1; i++)
- memcpy16_toio(&s[i * sparebuf_size],
- &d[i * oob_chunk_size],
- oob_chunk_size);
-
- /* the last chunk */
- memcpy16_toio(&s[i * sparebuf_size],
- &d[i * oob_chunk_size],
- host->used_oobsize - i * oob_chunk_size);
- }
-}
-
-/*
- * MXC NANDFC can only perform full page+spare or spare-only read/write. When
- * the upper layers perform a read/write buf operation, the saved column address
- * is used to index into the full page. So usually this function is called with
- * column == 0 (unless no column cycle is needed indicated by column == -1)
- */
-static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
-
- /* Write out column address, if necessary */
- if (column != -1) {
- host->devtype_data->send_addr(host, column & 0xff,
- page_addr == -1);
- if (mtd->writesize > 512)
- /* another col addr cycle for 2k page */
- host->devtype_data->send_addr(host,
- (column >> 8) & 0xff,
- false);
- }
-
- /* Write out page address, if necessary */
- if (page_addr != -1) {
- /* paddr_0 - p_addr_7 */
- host->devtype_data->send_addr(host, (page_addr & 0xff), false);
-
- if (mtd->writesize > 512) {
- if (mtd->size >= 0x10000000) {
- /* paddr_8 - paddr_15 */
- host->devtype_data->send_addr(host,
- (page_addr >> 8) & 0xff,
- false);
- host->devtype_data->send_addr(host,
- (page_addr >> 16) & 0xff,
- true);
- } else
- /* paddr_8 - paddr_15 */
- host->devtype_data->send_addr(host,
- (page_addr >> 8) & 0xff, true);
- } else {
- if (nand_chip->options & NAND_ROW_ADDR_3) {
- /* paddr_8 - paddr_15 */
- host->devtype_data->send_addr(host,
- (page_addr >> 8) & 0xff,
- false);
- host->devtype_data->send_addr(host,
- (page_addr >> 16) & 0xff,
- true);
- } else
- /* paddr_8 - paddr_15 */
- host->devtype_data->send_addr(host,
- (page_addr >> 8) & 0xff, true);
- }
- }
-}
-
-#define MXC_V1_ECCBYTES 5
-
-static int mxc_v1_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
-
- if (section >= nand_chip->ecc.steps)
- return -ERANGE;
-
- oobregion->offset = (section * 16) + 6;
- oobregion->length = MXC_V1_ECCBYTES;
-
- return 0;
-}
-
-static int mxc_v1_ooblayout_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
-
- if (section > nand_chip->ecc.steps)
- return -ERANGE;
-
- if (!section) {
- if (mtd->writesize <= 512) {
- oobregion->offset = 0;
- oobregion->length = 5;
- } else {
- oobregion->offset = 2;
- oobregion->length = 4;
- }
- } else {
- oobregion->offset = ((section - 1) * 16) + MXC_V1_ECCBYTES + 6;
- if (section < nand_chip->ecc.steps)
- oobregion->length = (section * 16) + 6 -
- oobregion->offset;
- else
- oobregion->length = mtd->oobsize - oobregion->offset;
- }
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops mxc_v1_ooblayout_ops = {
- .ecc = mxc_v1_ooblayout_ecc,
- .free = mxc_v1_ooblayout_free,
-};
-
-static int mxc_v2_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- int stepsize = nand_chip->ecc.bytes == 9 ? 16 : 26;
-
- if (section >= nand_chip->ecc.steps)
- return -ERANGE;
-
- oobregion->offset = (section * stepsize) + 7;
- oobregion->length = nand_chip->ecc.bytes;
-
- return 0;
-}
-
-static int mxc_v2_ooblayout_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- int stepsize = nand_chip->ecc.bytes == 9 ? 16 : 26;
-
- if (section >= nand_chip->ecc.steps)
- return -ERANGE;
-
- if (!section) {
- if (mtd->writesize <= 512) {
- oobregion->offset = 0;
- oobregion->length = 5;
- } else {
- oobregion->offset = 2;
- oobregion->length = 4;
- }
- } else {
- oobregion->offset = section * stepsize;
- oobregion->length = 7;
- }
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops mxc_v2_ooblayout_ops = {
- .ecc = mxc_v2_ooblayout_ecc,
- .free = mxc_v2_ooblayout_free,
-};
-
-/*
- * v2 and v3 type controllers can do 4bit or 8bit ecc depending
- * on how much oob the nand chip has. For 8bit ecc we need at least
- * 26 bytes of oob data per 512 byte block.
- */
-static int get_eccsize(struct mtd_info *mtd)
-{
- int oobbytes_per_512 = 0;
-
- oobbytes_per_512 = mtd->oobsize * 512 / mtd->writesize;
-
- if (oobbytes_per_512 < 26)
- return 4;
- else
- return 8;
-}
-
-static void preset_v1(struct mtd_info *mtd)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
- uint16_t config1 = 0;
-
- if (nand_chip->ecc.mode == NAND_ECC_HW && mtd->writesize)
- config1 |= NFC_V1_V2_CONFIG1_ECC_EN;
-
- if (!host->devtype_data->irqpending_quirk)
- config1 |= NFC_V1_V2_CONFIG1_INT_MSK;
-
- host->eccsize = 1;
-
- writew(config1, NFC_V1_V2_CONFIG1);
- /* preset operation */
-
- /* Unlock the internal RAM Buffer */
- writew(0x2, NFC_V1_V2_CONFIG);
-
- /* Blocks to be unlocked */
- writew(0x0, NFC_V1_UNLOCKSTART_BLKADDR);
- writew(0xffff, NFC_V1_UNLOCKEND_BLKADDR);
-
- /* Unlock Block Command for given address range */
- writew(0x4, NFC_V1_V2_WRPROT);
-}
-
-static int mxc_nand_v2_setup_data_interface(struct mtd_info *mtd, int csline,
- const struct nand_data_interface *conf)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
- int tRC_min_ns, tRC_ps, ret;
- unsigned long rate, rate_round;
- const struct nand_sdr_timings *timings;
- u16 config1;
-
- timings = nand_get_sdr_timings(conf);
- if (IS_ERR(timings))
- return -ENOTSUPP;
-
- config1 = readw(NFC_V1_V2_CONFIG1);
-
- tRC_min_ns = timings->tRC_min / 1000;
- rate = 1000000000 / tRC_min_ns;
-
- /*
- * For tRC < 30ns we have to use EDO mode. In this case the controller
- * does one access per clock cycle. Otherwise the controller does one
- * access in two clock cycles, thus we have to double the rate to the
- * controller.
- */
- if (tRC_min_ns < 30) {
- rate_round = clk_round_rate(host->clk, rate);
- config1 |= NFC_V2_CONFIG1_ONE_CYCLE;
- tRC_ps = 1000000000 / (rate_round / 1000);
- } else {
- rate *= 2;
- rate_round = clk_round_rate(host->clk, rate);
- config1 &= ~NFC_V2_CONFIG1_ONE_CYCLE;
- tRC_ps = 1000000000 / (rate_round / 1000 / 2);
- }
-
- /*
- * The timing values compared against are from the i.MX25 Automotive
- * datasheet, Table 50. NFC Timing Parameters
- */
- if (timings->tCLS_min > tRC_ps - 1000 ||
- timings->tCLH_min > tRC_ps - 2000 ||
- timings->tCS_min > tRC_ps - 1000 ||
- timings->tCH_min > tRC_ps - 2000 ||
- timings->tWP_min > tRC_ps - 1500 ||
- timings->tALS_min > tRC_ps ||
- timings->tALH_min > tRC_ps - 3000 ||
- timings->tDS_min > tRC_ps ||
- timings->tDH_min > tRC_ps - 5000 ||
- timings->tWC_min > 2 * tRC_ps ||
- timings->tWH_min > tRC_ps - 2500 ||
- timings->tRR_min > 6 * tRC_ps ||
- timings->tRP_min > 3 * tRC_ps / 2 ||
- timings->tRC_min > 2 * tRC_ps ||
- timings->tREH_min > (tRC_ps / 2) - 2500) {
- dev_dbg(host->dev, "Timing out of bounds\n");
- return -EINVAL;
- }
-
- if (csline == NAND_DATA_IFACE_CHECK_ONLY)
- return 0;
-
- ret = clk_set_rate(host->clk, rate);
- if (ret)
- return ret;
-
- writew(config1, NFC_V1_V2_CONFIG1);
-
- dev_dbg(host->dev, "Setting rate to %ldHz, %s mode\n", rate_round,
- config1 & NFC_V2_CONFIG1_ONE_CYCLE ? "One cycle (EDO)" :
- "normal");
-
- return 0;
-}
-
-static void preset_v2(struct mtd_info *mtd)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
- uint16_t config1 = 0;
-
- config1 |= NFC_V2_CONFIG1_FP_INT;
-
- if (!host->devtype_data->irqpending_quirk)
- config1 |= NFC_V1_V2_CONFIG1_INT_MSK;
-
- if (mtd->writesize) {
- uint16_t pages_per_block = mtd->erasesize / mtd->writesize;
-
- if (nand_chip->ecc.mode == NAND_ECC_HW)
- config1 |= NFC_V1_V2_CONFIG1_ECC_EN;
-
- host->eccsize = get_eccsize(mtd);
- if (host->eccsize == 4)
- config1 |= NFC_V2_CONFIG1_ECC_MODE_4;
-
- config1 |= NFC_V2_CONFIG1_PPB(ffs(pages_per_block) - 6);
- } else {
- host->eccsize = 1;
- }
-
- writew(config1, NFC_V1_V2_CONFIG1);
- /* preset operation */
-
- /* Unlock the internal RAM Buffer */
- writew(0x2, NFC_V1_V2_CONFIG);
-
- /* Blocks to be unlocked */
- writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR0);
- writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR1);
- writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR2);
- writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR3);
- writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR0);
- writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR1);
- writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR2);
- writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR3);
-
- /* Unlock Block Command for given address range */
- writew(0x4, NFC_V1_V2_WRPROT);
-}
-
-static void preset_v3(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct mxc_nand_host *host = nand_get_controller_data(chip);
- uint32_t config2, config3;
- int i, addr_phases;
-
- writel(NFC_V3_CONFIG1_RBA(0), NFC_V3_CONFIG1);
- writel(NFC_V3_IPC_CREQ, NFC_V3_IPC);
-
- /* Unlock the internal RAM Buffer */
- writel(NFC_V3_WRPROT_BLS_UNLOCK | NFC_V3_WRPROT_UNLOCK,
- NFC_V3_WRPROT);
-
- /* Blocks to be unlocked */
- for (i = 0; i < NAND_MAX_CHIPS; i++)
- writel(0xffff << 16, NFC_V3_WRPROT_UNLOCK_BLK_ADD0 + (i << 2));
-
- writel(0, NFC_V3_IPC);
-
- config2 = NFC_V3_CONFIG2_ONE_CYCLE |
- NFC_V3_CONFIG2_2CMD_PHASES |
- NFC_V3_CONFIG2_SPAS(mtd->oobsize >> 1) |
- NFC_V3_CONFIG2_ST_CMD(0x70) |
- NFC_V3_CONFIG2_INT_MSK |
- NFC_V3_CONFIG2_NUM_ADDR_PHASE0;
-
- addr_phases = fls(chip->pagemask) >> 3;
-
- if (mtd->writesize == 2048) {
- config2 |= NFC_V3_CONFIG2_PS_2048;
- config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases);
- } else if (mtd->writesize == 4096) {
- config2 |= NFC_V3_CONFIG2_PS_4096;
- config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases);
- } else {
- config2 |= NFC_V3_CONFIG2_PS_512;
- config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases - 1);
- }
-
- if (mtd->writesize) {
- if (chip->ecc.mode == NAND_ECC_HW)
- config2 |= NFC_V3_CONFIG2_ECC_EN;
-
- config2 |= NFC_V3_CONFIG2_PPB(
- ffs(mtd->erasesize / mtd->writesize) - 6,
- host->devtype_data->ppb_shift);
- host->eccsize = get_eccsize(mtd);
- if (host->eccsize == 8)
- config2 |= NFC_V3_CONFIG2_ECC_MODE_8;
- }
-
- writel(config2, NFC_V3_CONFIG2);
-
- config3 = NFC_V3_CONFIG3_NUM_OF_DEVICES(0) |
- NFC_V3_CONFIG3_NO_SDMA |
- NFC_V3_CONFIG3_RBB_MODE |
- NFC_V3_CONFIG3_SBB(6) | /* Reset default */
- NFC_V3_CONFIG3_ADD_OP(0);
-
- if (!(chip->options & NAND_BUSWIDTH_16))
- config3 |= NFC_V3_CONFIG3_FW8;
-
- writel(config3, NFC_V3_CONFIG3);
-
- writel(0, NFC_V3_DELAY_LINE);
-}
-
-/* Used by the upper layer to write command to NAND Flash for
- * different operations to be carried out on NAND Flash */
-static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
- int column, int page_addr)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
-
- dev_dbg(host->dev, "mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n",
- command, column, page_addr);
-
- /* Reset command state information */
- host->status_request = false;
-
- /* Command pre-processing step */
- switch (command) {
- case NAND_CMD_RESET:
- host->devtype_data->preset(mtd);
- host->devtype_data->send_cmd(host, command, false);
- break;
-
- case NAND_CMD_STATUS:
- host->buf_start = 0;
- host->status_request = true;
-
- host->devtype_data->send_cmd(host, command, true);
- WARN_ONCE(column != -1 || page_addr != -1,
- "Unexpected column/row value (cmd=%u, col=%d, row=%d)\n",
- command, column, page_addr);
- mxc_do_addr_cycle(mtd, column, page_addr);
- break;
-
- case NAND_CMD_READ0:
- case NAND_CMD_READOOB:
- if (command == NAND_CMD_READ0)
- host->buf_start = column;
- else
- host->buf_start = column + mtd->writesize;
-
- command = NAND_CMD_READ0; /* only READ0 is valid */
-
- host->devtype_data->send_cmd(host, command, false);
- WARN_ONCE(column < 0,
- "Unexpected column/row value (cmd=%u, col=%d, row=%d)\n",
- command, column, page_addr);
- mxc_do_addr_cycle(mtd, 0, page_addr);
-
- if (mtd->writesize > 512)
- host->devtype_data->send_cmd(host,
- NAND_CMD_READSTART, true);
-
- host->devtype_data->send_page(mtd, NFC_OUTPUT);
-
- memcpy32_fromio(host->data_buf, host->main_area0,
- mtd->writesize);
- copy_spare(mtd, true);
- break;
-
- case NAND_CMD_SEQIN:
- if (column >= mtd->writesize)
- /* call ourself to read a page */
- mxc_nand_command(mtd, NAND_CMD_READ0, 0, page_addr);
-
- host->buf_start = column;
-
- host->devtype_data->send_cmd(host, command, false);
- WARN_ONCE(column < -1,
- "Unexpected column/row value (cmd=%u, col=%d, row=%d)\n",
- command, column, page_addr);
- mxc_do_addr_cycle(mtd, 0, page_addr);
- break;
-
- case NAND_CMD_PAGEPROG:
- memcpy32_toio(host->main_area0, host->data_buf, mtd->writesize);
- copy_spare(mtd, false);
- host->devtype_data->send_page(mtd, NFC_INPUT);
- host->devtype_data->send_cmd(host, command, true);
- WARN_ONCE(column != -1 || page_addr != -1,
- "Unexpected column/row value (cmd=%u, col=%d, row=%d)\n",
- command, column, page_addr);
- mxc_do_addr_cycle(mtd, column, page_addr);
- break;
-
- case NAND_CMD_READID:
- host->devtype_data->send_cmd(host, command, true);
- mxc_do_addr_cycle(mtd, column, page_addr);
- host->devtype_data->send_read_id(host);
- host->buf_start = 0;
- break;
-
- case NAND_CMD_ERASE1:
- case NAND_CMD_ERASE2:
- host->devtype_data->send_cmd(host, command, false);
- WARN_ONCE(column != -1,
- "Unexpected column value (cmd=%u, col=%d)\n",
- command, column);
- mxc_do_addr_cycle(mtd, column, page_addr);
-
- break;
- case NAND_CMD_PARAM:
- host->devtype_data->send_cmd(host, command, false);
- mxc_do_addr_cycle(mtd, column, page_addr);
- host->devtype_data->send_page(mtd, NFC_OUTPUT);
- memcpy32_fromio(host->data_buf, host->main_area0, 512);
- host->buf_start = 0;
- break;
- default:
- WARN_ONCE(1, "Unimplemented command (cmd=%u)\n",
- command);
- break;
- }
-}
-
-static int mxc_nand_onfi_set_features(struct mtd_info *mtd,
- struct nand_chip *chip, int addr,
- u8 *subfeature_param)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
- int i;
-
- if (!chip->onfi_version ||
- !(le16_to_cpu(chip->onfi_params.opt_cmd)
- & ONFI_OPT_CMD_SET_GET_FEATURES))
- return -EINVAL;
-
- host->buf_start = 0;
-
- for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
- chip->write_byte(mtd, subfeature_param[i]);
-
- memcpy32_toio(host->main_area0, host->data_buf, mtd->writesize);
- host->devtype_data->send_cmd(host, NAND_CMD_SET_FEATURES, false);
- mxc_do_addr_cycle(mtd, addr, -1);
- host->devtype_data->send_page(mtd, NFC_INPUT);
-
- return 0;
-}
-
-static int mxc_nand_onfi_get_features(struct mtd_info *mtd,
- struct nand_chip *chip, int addr,
- u8 *subfeature_param)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
- int i;
-
- if (!chip->onfi_version ||
- !(le16_to_cpu(chip->onfi_params.opt_cmd)
- & ONFI_OPT_CMD_SET_GET_FEATURES))
- return -EINVAL;
-
- host->devtype_data->send_cmd(host, NAND_CMD_GET_FEATURES, false);
- mxc_do_addr_cycle(mtd, addr, -1);
- host->devtype_data->send_page(mtd, NFC_OUTPUT);
- memcpy32_fromio(host->data_buf, host->main_area0, 512);
- host->buf_start = 0;
-
- for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
- *subfeature_param++ = chip->read_byte(mtd);
-
- return 0;
-}
-
-/*
- * The generic flash bbt decriptors overlap with our ecc
- * hardware, so define some i.MX specific ones.
- */
-static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' };
-static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' };
-
-static struct nand_bbt_descr bbt_main_descr = {
- .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
- | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
- .offs = 0,
- .len = 4,
- .veroffs = 4,
- .maxblocks = 4,
- .pattern = bbt_pattern,
-};
-
-static struct nand_bbt_descr bbt_mirror_descr = {
- .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
- | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
- .offs = 0,
- .len = 4,
- .veroffs = 4,
- .maxblocks = 4,
- .pattern = mirror_pattern,
-};
-
-/* v1 + irqpending_quirk: i.MX21 */
-static const struct mxc_nand_devtype_data imx21_nand_devtype_data = {
- .preset = preset_v1,
- .send_cmd = send_cmd_v1_v2,
- .send_addr = send_addr_v1_v2,
- .send_page = send_page_v1,
- .send_read_id = send_read_id_v1_v2,
- .get_dev_status = get_dev_status_v1_v2,
- .check_int = check_int_v1_v2,
- .irq_control = irq_control_v1_v2,
- .get_ecc_status = get_ecc_status_v1,
- .ooblayout = &mxc_v1_ooblayout_ops,
- .select_chip = mxc_nand_select_chip_v1_v3,
- .correct_data = mxc_nand_correct_data_v1,
- .irqpending_quirk = 1,
- .needs_ip = 0,
- .regs_offset = 0xe00,
- .spare0_offset = 0x800,
- .spare_len = 16,
- .eccbytes = 3,
- .eccsize = 1,
-};
-
-/* v1 + !irqpending_quirk: i.MX27, i.MX31 */
-static const struct mxc_nand_devtype_data imx27_nand_devtype_data = {
- .preset = preset_v1,
- .send_cmd = send_cmd_v1_v2,
- .send_addr = send_addr_v1_v2,
- .send_page = send_page_v1,
- .send_read_id = send_read_id_v1_v2,
- .get_dev_status = get_dev_status_v1_v2,
- .check_int = check_int_v1_v2,
- .irq_control = irq_control_v1_v2,
- .get_ecc_status = get_ecc_status_v1,
- .ooblayout = &mxc_v1_ooblayout_ops,
- .select_chip = mxc_nand_select_chip_v1_v3,
- .correct_data = mxc_nand_correct_data_v1,
- .irqpending_quirk = 0,
- .needs_ip = 0,
- .regs_offset = 0xe00,
- .spare0_offset = 0x800,
- .axi_offset = 0,
- .spare_len = 16,
- .eccbytes = 3,
- .eccsize = 1,
-};
-
-/* v21: i.MX25, i.MX35 */
-static const struct mxc_nand_devtype_data imx25_nand_devtype_data = {
- .preset = preset_v2,
- .send_cmd = send_cmd_v1_v2,
- .send_addr = send_addr_v1_v2,
- .send_page = send_page_v2,
- .send_read_id = send_read_id_v1_v2,
- .get_dev_status = get_dev_status_v1_v2,
- .check_int = check_int_v1_v2,
- .irq_control = irq_control_v1_v2,
- .get_ecc_status = get_ecc_status_v2,
- .ooblayout = &mxc_v2_ooblayout_ops,
- .select_chip = mxc_nand_select_chip_v2,
- .correct_data = mxc_nand_correct_data_v2_v3,
- .setup_data_interface = mxc_nand_v2_setup_data_interface,
- .irqpending_quirk = 0,
- .needs_ip = 0,
- .regs_offset = 0x1e00,
- .spare0_offset = 0x1000,
- .axi_offset = 0,
- .spare_len = 64,
- .eccbytes = 9,
- .eccsize = 0,
-};
-
-/* v3.2a: i.MX51 */
-static const struct mxc_nand_devtype_data imx51_nand_devtype_data = {
- .preset = preset_v3,
- .send_cmd = send_cmd_v3,
- .send_addr = send_addr_v3,
- .send_page = send_page_v3,
- .send_read_id = send_read_id_v3,
- .get_dev_status = get_dev_status_v3,
- .check_int = check_int_v3,
- .irq_control = irq_control_v3,
- .get_ecc_status = get_ecc_status_v3,
- .ooblayout = &mxc_v2_ooblayout_ops,
- .select_chip = mxc_nand_select_chip_v1_v3,
- .correct_data = mxc_nand_correct_data_v2_v3,
- .irqpending_quirk = 0,
- .needs_ip = 1,
- .regs_offset = 0,
- .spare0_offset = 0x1000,
- .axi_offset = 0x1e00,
- .spare_len = 64,
- .eccbytes = 0,
- .eccsize = 0,
- .ppb_shift = 7,
-};
-
-/* v3.2b: i.MX53 */
-static const struct mxc_nand_devtype_data imx53_nand_devtype_data = {
- .preset = preset_v3,
- .send_cmd = send_cmd_v3,
- .send_addr = send_addr_v3,
- .send_page = send_page_v3,
- .send_read_id = send_read_id_v3,
- .get_dev_status = get_dev_status_v3,
- .check_int = check_int_v3,
- .irq_control = irq_control_v3,
- .get_ecc_status = get_ecc_status_v3,
- .ooblayout = &mxc_v2_ooblayout_ops,
- .select_chip = mxc_nand_select_chip_v1_v3,
- .correct_data = mxc_nand_correct_data_v2_v3,
- .irqpending_quirk = 0,
- .needs_ip = 1,
- .regs_offset = 0,
- .spare0_offset = 0x1000,
- .axi_offset = 0x1e00,
- .spare_len = 64,
- .eccbytes = 0,
- .eccsize = 0,
- .ppb_shift = 8,
-};
-
-static inline int is_imx21_nfc(struct mxc_nand_host *host)
-{
- return host->devtype_data == &imx21_nand_devtype_data;
-}
-
-static inline int is_imx27_nfc(struct mxc_nand_host *host)
-{
- return host->devtype_data == &imx27_nand_devtype_data;
-}
-
-static inline int is_imx25_nfc(struct mxc_nand_host *host)
-{
- return host->devtype_data == &imx25_nand_devtype_data;
-}
-
-static inline int is_imx51_nfc(struct mxc_nand_host *host)
-{
- return host->devtype_data == &imx51_nand_devtype_data;
-}
-
-static inline int is_imx53_nfc(struct mxc_nand_host *host)
-{
- return host->devtype_data == &imx53_nand_devtype_data;
-}
-
-static const struct platform_device_id mxcnd_devtype[] = {
- {
- .name = "imx21-nand",
- .driver_data = (kernel_ulong_t) &imx21_nand_devtype_data,
- }, {
- .name = "imx27-nand",
- .driver_data = (kernel_ulong_t) &imx27_nand_devtype_data,
- }, {
- .name = "imx25-nand",
- .driver_data = (kernel_ulong_t) &imx25_nand_devtype_data,
- }, {
- .name = "imx51-nand",
- .driver_data = (kernel_ulong_t) &imx51_nand_devtype_data,
- }, {
- .name = "imx53-nand",
- .driver_data = (kernel_ulong_t) &imx53_nand_devtype_data,
- }, {
- /* sentinel */
- }
-};
-MODULE_DEVICE_TABLE(platform, mxcnd_devtype);
-
-#ifdef CONFIG_OF
-static const struct of_device_id mxcnd_dt_ids[] = {
- {
- .compatible = "fsl,imx21-nand",
- .data = &imx21_nand_devtype_data,
- }, {
- .compatible = "fsl,imx27-nand",
- .data = &imx27_nand_devtype_data,
- }, {
- .compatible = "fsl,imx25-nand",
- .data = &imx25_nand_devtype_data,
- }, {
- .compatible = "fsl,imx51-nand",
- .data = &imx51_nand_devtype_data,
- }, {
- .compatible = "fsl,imx53-nand",
- .data = &imx53_nand_devtype_data,
- },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, mxcnd_dt_ids);
-
-static int __init mxcnd_probe_dt(struct mxc_nand_host *host)
-{
- struct device_node *np = host->dev->of_node;
- const struct of_device_id *of_id =
- of_match_device(mxcnd_dt_ids, host->dev);
-
- if (!np)
- return 1;
-
- host->devtype_data = of_id->data;
-
- return 0;
-}
-#else
-static int __init mxcnd_probe_dt(struct mxc_nand_host *host)
-{
- return 1;
-}
-#endif
-
-static int mxcnd_probe(struct platform_device *pdev)
-{
- struct nand_chip *this;
- struct mtd_info *mtd;
- struct mxc_nand_host *host;
- struct resource *res;
- int err = 0;
-
- /* Allocate memory for MTD device structure and private data */
- host = devm_kzalloc(&pdev->dev, sizeof(struct mxc_nand_host),
- GFP_KERNEL);
- if (!host)
- return -ENOMEM;
-
- /* allocate a temporary buffer for the nand_scan_ident() */
- host->data_buf = devm_kzalloc(&pdev->dev, PAGE_SIZE, GFP_KERNEL);
- if (!host->data_buf)
- return -ENOMEM;
-
- host->dev = &pdev->dev;
- /* structures must be linked */
- this = &host->nand;
- mtd = nand_to_mtd(this);
- mtd->dev.parent = &pdev->dev;
- mtd->name = DRIVER_NAME;
-
- /* 50 us command delay time */
- this->chip_delay = 5;
-
- nand_set_controller_data(this, host);
- nand_set_flash_node(this, pdev->dev.of_node),
- this->dev_ready = mxc_nand_dev_ready;
- this->cmdfunc = mxc_nand_command;
- this->read_byte = mxc_nand_read_byte;
- this->read_word = mxc_nand_read_word;
- this->write_buf = mxc_nand_write_buf;
- this->read_buf = mxc_nand_read_buf;
- this->onfi_set_features = mxc_nand_onfi_set_features;
- this->onfi_get_features = mxc_nand_onfi_get_features;
-
- host->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(host->clk))
- return PTR_ERR(host->clk);
-
- err = mxcnd_probe_dt(host);
- if (err > 0) {
- struct mxc_nand_platform_data *pdata =
- dev_get_platdata(&pdev->dev);
- if (pdata) {
- host->pdata = *pdata;
- host->devtype_data = (struct mxc_nand_devtype_data *)
- pdev->id_entry->driver_data;
- } else {
- err = -ENODEV;
- }
- }
- if (err < 0)
- return err;
-
- this->setup_data_interface = host->devtype_data->setup_data_interface;
-
- if (host->devtype_data->needs_ip) {
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- host->regs_ip = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(host->regs_ip))
- return PTR_ERR(host->regs_ip);
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- } else {
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- }
-
- host->base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(host->base))
- return PTR_ERR(host->base);
-
- host->main_area0 = host->base;
-
- if (host->devtype_data->regs_offset)
- host->regs = host->base + host->devtype_data->regs_offset;
- host->spare0 = host->base + host->devtype_data->spare0_offset;
- if (host->devtype_data->axi_offset)
- host->regs_axi = host->base + host->devtype_data->axi_offset;
-
- this->ecc.bytes = host->devtype_data->eccbytes;
- host->eccsize = host->devtype_data->eccsize;
-
- this->select_chip = host->devtype_data->select_chip;
- this->ecc.size = 512;
- mtd_set_ooblayout(mtd, host->devtype_data->ooblayout);
-
- if (host->pdata.hw_ecc) {
- this->ecc.mode = NAND_ECC_HW;
- } else {
- this->ecc.mode = NAND_ECC_SOFT;
- this->ecc.algo = NAND_ECC_HAMMING;
- }
-
- /* NAND bus width determines access functions used by upper layer */
- if (host->pdata.width == 2)
- this->options |= NAND_BUSWIDTH_16;
-
- /* update flash based bbt */
- if (host->pdata.flash_bbt)
- this->bbt_options |= NAND_BBT_USE_FLASH;
-
- init_completion(&host->op_completion);
-
- host->irq = platform_get_irq(pdev, 0);
- if (host->irq < 0)
- return host->irq;
-
- /*
- * Use host->devtype_data->irq_control() here instead of irq_control()
- * because we must not disable_irq_nosync without having requested the
- * irq.
- */
- host->devtype_data->irq_control(host, 0);
-
- err = devm_request_irq(&pdev->dev, host->irq, mxc_nfc_irq,
- 0, DRIVER_NAME, host);
- if (err)
- return err;
-
- err = clk_prepare_enable(host->clk);
- if (err)
- return err;
- host->clk_act = 1;
-
- /*
- * Now that we "own" the interrupt make sure the interrupt mask bit is
- * cleared on i.MX21. Otherwise we can't read the interrupt status bit
- * on this machine.
- */
- if (host->devtype_data->irqpending_quirk) {
- disable_irq_nosync(host->irq);
- host->devtype_data->irq_control(host, 1);
- }
-
- /* first scan to find the device and get the page size */
- err = nand_scan_ident(mtd, is_imx25_nfc(host) ? 4 : 1, NULL);
- if (err)
- goto escan;
-
- switch (this->ecc.mode) {
- case NAND_ECC_HW:
- this->ecc.calculate = mxc_nand_calculate_ecc;
- this->ecc.hwctl = mxc_nand_enable_hwecc;
- this->ecc.correct = host->devtype_data->correct_data;
- break;
-
- case NAND_ECC_SOFT:
- break;
-
- default:
- err = -EINVAL;
- goto escan;
- }
-
- if (this->bbt_options & NAND_BBT_USE_FLASH) {
- this->bbt_td = &bbt_main_descr;
- this->bbt_md = &bbt_mirror_descr;
- }
-
- /* allocate the right size buffer now */
- devm_kfree(&pdev->dev, (void *)host->data_buf);
- host->data_buf = devm_kzalloc(&pdev->dev, mtd->writesize + mtd->oobsize,
- GFP_KERNEL);
- if (!host->data_buf) {
- err = -ENOMEM;
- goto escan;
- }
-
- /* Call preset again, with correct writesize this time */
- host->devtype_data->preset(mtd);
-
- if (!this->ecc.bytes) {
- if (host->eccsize == 8)
- this->ecc.bytes = 18;
- else if (host->eccsize == 4)
- this->ecc.bytes = 9;
- }
-
- /*
- * Experimentation shows that i.MX NFC can only handle up to 218 oob
- * bytes. Limit used_oobsize to 218 so as to not confuse copy_spare()
- * into copying invalid data to/from the spare IO buffer, as this
- * might cause ECC data corruption when doing sub-page write to a
- * partially written page.
- */
- host->used_oobsize = min(mtd->oobsize, 218U);
-
- if (this->ecc.mode == NAND_ECC_HW) {
- if (is_imx21_nfc(host) || is_imx27_nfc(host))
- this->ecc.strength = 1;
- else
- this->ecc.strength = (host->eccsize == 4) ? 4 : 8;
- }
-
- /* second phase scan */
- err = nand_scan_tail(mtd);
- if (err)
- goto escan;
-
- /* Register the partitions */
- mtd_device_parse_register(mtd, part_probes,
- NULL,
- host->pdata.parts,
- host->pdata.nr_parts);
-
- platform_set_drvdata(pdev, host);
-
- return 0;
-
-escan:
- if (host->clk_act)
- clk_disable_unprepare(host->clk);
-
- return err;
-}
-
-static int mxcnd_remove(struct platform_device *pdev)
-{
- struct mxc_nand_host *host = platform_get_drvdata(pdev);
-
- nand_release(nand_to_mtd(&host->nand));
- if (host->clk_act)
- clk_disable_unprepare(host->clk);
-
- return 0;
-}
-
-static struct platform_driver mxcnd_driver = {
- .driver = {
- .name = DRIVER_NAME,
- .of_match_table = of_match_ptr(mxcnd_dt_ids),
- },
- .id_table = mxcnd_devtype,
- .probe = mxcnd_probe,
- .remove = mxcnd_remove,
-};
-module_platform_driver(mxcnd_driver);
-
-MODULE_AUTHOR("Freescale Semiconductor, Inc.");
-MODULE_DESCRIPTION("MXC NAND MTD driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
deleted file mode 100644
index e70ca16a5118..000000000000
--- a/drivers/mtd/nand/nand_base.c
+++ /dev/null
@@ -1,6582 +0,0 @@
-/*
- * Overview:
- * This is the generic MTD driver for NAND flash devices. It should be
- * capable of working with almost all NAND chips currently available.
- *
- * Additional technical information is available on
- * http://www.linux-mtd.infradead.org/doc/nand.html
- *
- * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
- * 2002-2006 Thomas Gleixner (tglx@linutronix.de)
- *
- * Credits:
- * David Woodhouse for adding multichip support
- *
- * Aleph One Ltd. and Toby Churchill Ltd. for supporting the
- * rework for 2K page size chips
- *
- * TODO:
- * Enable cached programming for 2k page size chips
- * Check, if mtd->ecctype should be set to MTD_ECC_HW
- * if we have HW ECC support.
- * BBT table is not serialized, has to be fixed
- *
- * 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.
- *
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/err.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/nmi.h>
-#include <linux/types.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand_ecc.h>
-#include <linux/mtd/nand_bch.h>
-#include <linux/interrupt.h>
-#include <linux/bitops.h>
-#include <linux/io.h>
-#include <linux/mtd/partitions.h>
-#include <linux/of.h>
-
-static int nand_get_device(struct mtd_info *mtd, int new_state);
-
-static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
- struct mtd_oob_ops *ops);
-
-/* Define default oob placement schemes for large and small page devices */
-static int nand_ooblayout_ecc_sp(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
-
- if (section > 1)
- return -ERANGE;
-
- if (!section) {
- oobregion->offset = 0;
- if (mtd->oobsize == 16)
- oobregion->length = 4;
- else
- oobregion->length = 3;
- } else {
- if (mtd->oobsize == 8)
- return -ERANGE;
-
- oobregion->offset = 6;
- oobregion->length = ecc->total - 4;
- }
-
- return 0;
-}
-
-static int nand_ooblayout_free_sp(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- if (section > 1)
- return -ERANGE;
-
- if (mtd->oobsize == 16) {
- if (section)
- return -ERANGE;
-
- oobregion->length = 8;
- oobregion->offset = 8;
- } else {
- oobregion->length = 2;
- if (!section)
- oobregion->offset = 3;
- else
- oobregion->offset = 6;
- }
-
- return 0;
-}
-
-const struct mtd_ooblayout_ops nand_ooblayout_sp_ops = {
- .ecc = nand_ooblayout_ecc_sp,
- .free = nand_ooblayout_free_sp,
-};
-EXPORT_SYMBOL_GPL(nand_ooblayout_sp_ops);
-
-static int nand_ooblayout_ecc_lp(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
-
- if (section || !ecc->total)
- return -ERANGE;
-
- oobregion->length = ecc->total;
- oobregion->offset = mtd->oobsize - oobregion->length;
-
- return 0;
-}
-
-static int nand_ooblayout_free_lp(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
-
- if (section)
- return -ERANGE;
-
- oobregion->length = mtd->oobsize - ecc->total - 2;
- oobregion->offset = 2;
-
- return 0;
-}
-
-const struct mtd_ooblayout_ops nand_ooblayout_lp_ops = {
- .ecc = nand_ooblayout_ecc_lp,
- .free = nand_ooblayout_free_lp,
-};
-EXPORT_SYMBOL_GPL(nand_ooblayout_lp_ops);
-
-/*
- * Support the old "large page" layout used for 1-bit Hamming ECC where ECC
- * are placed at a fixed offset.
- */
-static int nand_ooblayout_ecc_lp_hamming(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
-
- if (section)
- return -ERANGE;
-
- switch (mtd->oobsize) {
- case 64:
- oobregion->offset = 40;
- break;
- case 128:
- oobregion->offset = 80;
- break;
- default:
- return -EINVAL;
- }
-
- oobregion->length = ecc->total;
- if (oobregion->offset + oobregion->length > mtd->oobsize)
- return -ERANGE;
-
- return 0;
-}
-
-static int nand_ooblayout_free_lp_hamming(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
- int ecc_offset = 0;
-
- if (section < 0 || section > 1)
- return -ERANGE;
-
- switch (mtd->oobsize) {
- case 64:
- ecc_offset = 40;
- break;
- case 128:
- ecc_offset = 80;
- break;
- default:
- return -EINVAL;
- }
-
- if (section == 0) {
- oobregion->offset = 2;
- oobregion->length = ecc_offset - 2;
- } else {
- oobregion->offset = ecc_offset + ecc->total;
- oobregion->length = mtd->oobsize - oobregion->offset;
- }
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops nand_ooblayout_lp_hamming_ops = {
- .ecc = nand_ooblayout_ecc_lp_hamming,
- .free = nand_ooblayout_free_lp_hamming,
-};
-
-static int check_offs_len(struct mtd_info *mtd,
- loff_t ofs, uint64_t len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- int ret = 0;
-
- /* Start address must align on block boundary */
- if (ofs & ((1ULL << chip->phys_erase_shift) - 1)) {
- pr_debug("%s: unaligned address\n", __func__);
- ret = -EINVAL;
- }
-
- /* Length must align on block boundary */
- if (len & ((1ULL << chip->phys_erase_shift) - 1)) {
- pr_debug("%s: length not block aligned\n", __func__);
- ret = -EINVAL;
- }
-
- return ret;
-}
-
-/**
- * nand_release_device - [GENERIC] release chip
- * @mtd: MTD device structure
- *
- * Release chip lock and wake up anyone waiting on the device.
- */
-static void nand_release_device(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- /* Release the controller and the chip */
- spin_lock(&chip->controller->lock);
- chip->controller->active = NULL;
- chip->state = FL_READY;
- wake_up(&chip->controller->wq);
- spin_unlock(&chip->controller->lock);
-}
-
-/**
- * nand_read_byte - [DEFAULT] read one byte from the chip
- * @mtd: MTD device structure
- *
- * Default read function for 8bit buswidth
- */
-static uint8_t nand_read_byte(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- return readb(chip->IO_ADDR_R);
-}
-
-/**
- * nand_read_byte16 - [DEFAULT] read one byte endianness aware from the chip
- * @mtd: MTD device structure
- *
- * Default read function for 16bit buswidth with endianness conversion.
- *
- */
-static uint8_t nand_read_byte16(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- return (uint8_t) cpu_to_le16(readw(chip->IO_ADDR_R));
-}
-
-/**
- * nand_read_word - [DEFAULT] read one word from the chip
- * @mtd: MTD device structure
- *
- * Default read function for 16bit buswidth without endianness conversion.
- */
-static u16 nand_read_word(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- return readw(chip->IO_ADDR_R);
-}
-
-/**
- * nand_select_chip - [DEFAULT] control CE line
- * @mtd: MTD device structure
- * @chipnr: chipnumber to select, -1 for deselect
- *
- * Default select function for 1 chip devices.
- */
-static void nand_select_chip(struct mtd_info *mtd, int chipnr)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- switch (chipnr) {
- case -1:
- chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
- break;
- case 0:
- break;
-
- default:
- BUG();
- }
-}
-
-/**
- * nand_write_byte - [DEFAULT] write single byte to chip
- * @mtd: MTD device structure
- * @byte: value to write
- *
- * Default function to write a byte to I/O[7:0]
- */
-static void nand_write_byte(struct mtd_info *mtd, uint8_t byte)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- chip->write_buf(mtd, &byte, 1);
-}
-
-/**
- * nand_write_byte16 - [DEFAULT] write single byte to a chip with width 16
- * @mtd: MTD device structure
- * @byte: value to write
- *
- * Default function to write a byte to I/O[7:0] on a 16-bit wide chip.
- */
-static void nand_write_byte16(struct mtd_info *mtd, uint8_t byte)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- uint16_t word = byte;
-
- /*
- * It's not entirely clear what should happen to I/O[15:8] when writing
- * a byte. The ONFi spec (Revision 3.1; 2012-09-19, Section 2.16) reads:
- *
- * When the host supports a 16-bit bus width, only data is
- * transferred at the 16-bit width. All address and command line
- * transfers shall use only the lower 8-bits of the data bus. During
- * command transfers, the host may place any value on the upper
- * 8-bits of the data bus. During address transfers, the host shall
- * set the upper 8-bits of the data bus to 00h.
- *
- * One user of the write_byte callback is nand_onfi_set_features. The
- * four parameters are specified to be written to I/O[7:0], but this is
- * neither an address nor a command transfer. Let's assume a 0 on the
- * upper I/O lines is OK.
- */
- chip->write_buf(mtd, (uint8_t *)&word, 2);
-}
-
-/**
- * nand_write_buf - [DEFAULT] write buffer to chip
- * @mtd: MTD device structure
- * @buf: data buffer
- * @len: number of bytes to write
- *
- * Default write function for 8bit buswidth.
- */
-static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- iowrite8_rep(chip->IO_ADDR_W, buf, len);
-}
-
-/**
- * nand_read_buf - [DEFAULT] read chip data into buffer
- * @mtd: MTD device structure
- * @buf: buffer to store date
- * @len: number of bytes to read
- *
- * Default read function for 8bit buswidth.
- */
-static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- ioread8_rep(chip->IO_ADDR_R, buf, len);
-}
-
-/**
- * nand_write_buf16 - [DEFAULT] write buffer to chip
- * @mtd: MTD device structure
- * @buf: data buffer
- * @len: number of bytes to write
- *
- * Default write function for 16bit buswidth.
- */
-static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- u16 *p = (u16 *) buf;
-
- iowrite16_rep(chip->IO_ADDR_W, p, len >> 1);
-}
-
-/**
- * nand_read_buf16 - [DEFAULT] read chip data into buffer
- * @mtd: MTD device structure
- * @buf: buffer to store date
- * @len: number of bytes to read
- *
- * Default read function for 16bit buswidth.
- */
-static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- u16 *p = (u16 *) buf;
-
- ioread16_rep(chip->IO_ADDR_R, p, len >> 1);
-}
-
-/**
- * nand_block_bad - [DEFAULT] Read bad block marker from the chip
- * @mtd: MTD device structure
- * @ofs: offset from device start
- *
- * Check, if the block is bad.
- */
-static int nand_block_bad(struct mtd_info *mtd, loff_t ofs)
-{
- int page, page_end, res;
- struct nand_chip *chip = mtd_to_nand(mtd);
- u8 bad;
-
- if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
- ofs += mtd->erasesize - mtd->writesize;
-
- page = (int)(ofs >> chip->page_shift) & chip->pagemask;
- page_end = page + (chip->bbt_options & NAND_BBT_SCAN2NDPAGE ? 2 : 1);
-
- for (; page < page_end; page++) {
- res = chip->ecc.read_oob(mtd, chip, page);
- if (res)
- return res;
-
- bad = chip->oob_poi[chip->badblockpos];
-
- if (likely(chip->badblockbits == 8))
- res = bad != 0xFF;
- else
- res = hweight8(bad) < chip->badblockbits;
- if (res)
- return res;
- }
-
- return 0;
-}
-
-/**
- * nand_default_block_markbad - [DEFAULT] mark a block bad via bad block marker
- * @mtd: MTD device structure
- * @ofs: offset from device start
- *
- * This is the default implementation, which can be overridden by a hardware
- * specific driver. It provides the details for writing a bad block marker to a
- * block.
- */
-static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct mtd_oob_ops ops;
- uint8_t buf[2] = { 0, 0 };
- int ret = 0, res, i = 0;
-
- memset(&ops, 0, sizeof(ops));
- ops.oobbuf = buf;
- ops.ooboffs = chip->badblockpos;
- if (chip->options & NAND_BUSWIDTH_16) {
- ops.ooboffs &= ~0x01;
- ops.len = ops.ooblen = 2;
- } else {
- ops.len = ops.ooblen = 1;
- }
- ops.mode = MTD_OPS_PLACE_OOB;
-
- /* Write to first/last page(s) if necessary */
- if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
- ofs += mtd->erasesize - mtd->writesize;
- do {
- res = nand_do_write_oob(mtd, ofs, &ops);
- if (!ret)
- ret = res;
-
- i++;
- ofs += mtd->writesize;
- } while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
-
- return ret;
-}
-
-/**
- * nand_block_markbad_lowlevel - mark a block bad
- * @mtd: MTD device structure
- * @ofs: offset from device start
- *
- * This function performs the generic NAND bad block marking steps (i.e., bad
- * block table(s) and/or marker(s)). We only allow the hardware driver to
- * specify how to write bad block markers to OOB (chip->block_markbad).
- *
- * We try operations in the following order:
- *
- * (1) erase the affected block, to allow OOB marker to be written cleanly
- * (2) write bad block marker to OOB area of affected block (unless flag
- * NAND_BBT_NO_OOB_BBM is present)
- * (3) update the BBT
- *
- * Note that we retain the first error encountered in (2) or (3), finish the
- * procedures, and dump the error in the end.
-*/
-static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- int res, ret = 0;
-
- if (!(chip->bbt_options & NAND_BBT_NO_OOB_BBM)) {
- struct erase_info einfo;
-
- /* Attempt erase before marking OOB */
- memset(&einfo, 0, sizeof(einfo));
- einfo.mtd = mtd;
- einfo.addr = ofs;
- einfo.len = 1ULL << chip->phys_erase_shift;
- nand_erase_nand(mtd, &einfo, 0);
-
- /* Write bad block marker to OOB */
- nand_get_device(mtd, FL_WRITING);
- ret = chip->block_markbad(mtd, ofs);
- nand_release_device(mtd);
- }
-
- /* Mark block bad in BBT */
- if (chip->bbt) {
- res = nand_markbad_bbt(mtd, ofs);
- if (!ret)
- ret = res;
- }
-
- if (!ret)
- mtd->ecc_stats.badblocks++;
-
- return ret;
-}
-
-/**
- * nand_check_wp - [GENERIC] check if the chip is write protected
- * @mtd: MTD device structure
- *
- * Check, if the device is write protected. The function expects, that the
- * device is already selected.
- */
-static int nand_check_wp(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- u8 status;
- int ret;
-
- /* Broken xD cards report WP despite being writable */
- if (chip->options & NAND_BROKEN_XD)
- return 0;
-
- /* Check the WP bit */
- ret = nand_status_op(chip, &status);
- if (ret)
- return ret;
-
- return status & NAND_STATUS_WP ? 0 : 1;
-}
-
-/**
- * nand_block_isreserved - [GENERIC] Check if a block is marked reserved.
- * @mtd: MTD device structure
- * @ofs: offset from device start
- *
- * Check if the block is marked as reserved.
- */
-static int nand_block_isreserved(struct mtd_info *mtd, loff_t ofs)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- if (!chip->bbt)
- return 0;
- /* Return info from the table */
- return nand_isreserved_bbt(mtd, ofs);
-}
-
-/**
- * nand_block_checkbad - [GENERIC] Check if a block is marked bad
- * @mtd: MTD device structure
- * @ofs: offset from device start
- * @allowbbt: 1, if its allowed to access the bbt area
- *
- * Check, if the block is bad. Either by reading the bad block table or
- * calling of the scan function.
- */
-static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int allowbbt)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- if (!chip->bbt)
- return chip->block_bad(mtd, ofs);
-
- /* Return info from the table */
- return nand_isbad_bbt(mtd, ofs, allowbbt);
-}
-
-/**
- * panic_nand_wait_ready - [GENERIC] Wait for the ready pin after commands.
- * @mtd: MTD device structure
- * @timeo: Timeout
- *
- * Helper function for nand_wait_ready used when needing to wait in interrupt
- * context.
- */
-static void panic_nand_wait_ready(struct mtd_info *mtd, unsigned long timeo)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- int i;
-
- /* Wait for the device to get ready */
- for (i = 0; i < timeo; i++) {
- if (chip->dev_ready(mtd))
- break;
- touch_softlockup_watchdog();
- mdelay(1);
- }
-}
-
-/**
- * nand_wait_ready - [GENERIC] Wait for the ready pin after commands.
- * @mtd: MTD device structure
- *
- * Wait for the ready pin after a command, and warn if a timeout occurs.
- */
-void nand_wait_ready(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- unsigned long timeo = 400;
-
- if (in_interrupt() || oops_in_progress)
- return panic_nand_wait_ready(mtd, timeo);
-
- /* Wait until command is processed or timeout occurs */
- timeo = jiffies + msecs_to_jiffies(timeo);
- do {
- if (chip->dev_ready(mtd))
- return;
- cond_resched();
- } while (time_before(jiffies, timeo));
-
- if (!chip->dev_ready(mtd))
- pr_warn_ratelimited("timeout while waiting for chip to become ready\n");
-}
-EXPORT_SYMBOL_GPL(nand_wait_ready);
-
-/**
- * nand_wait_status_ready - [GENERIC] Wait for the ready status after commands.
- * @mtd: MTD device structure
- * @timeo: Timeout in ms
- *
- * Wait for status ready (i.e. command done) or timeout.
- */
-static void nand_wait_status_ready(struct mtd_info *mtd, unsigned long timeo)
-{
- register struct nand_chip *chip = mtd_to_nand(mtd);
- int ret;
-
- timeo = jiffies + msecs_to_jiffies(timeo);
- do {
- u8 status;
-
- ret = nand_read_data_op(chip, &status, sizeof(status), true);
- if (ret)
- return;
-
- if (status & NAND_STATUS_READY)
- break;
- touch_softlockup_watchdog();
- } while (time_before(jiffies, timeo));
-};
-
-/**
- * nand_soft_waitrdy - Poll STATUS reg until RDY bit is set to 1
- * @chip: NAND chip structure
- * @timeout_ms: Timeout in ms
- *
- * Poll the STATUS register using ->exec_op() until the RDY bit becomes 1.
- * If that does not happen whitin the specified timeout, -ETIMEDOUT is
- * returned.
- *
- * This helper is intended to be used when the controller does not have access
- * to the NAND R/B pin.
- *
- * Be aware that calling this helper from an ->exec_op() implementation means
- * ->exec_op() must be re-entrant.
- *
- * Return 0 if the NAND chip is ready, a negative error otherwise.
- */
-int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms)
-{
- u8 status = 0;
- int ret;
-
- if (!chip->exec_op)
- return -ENOTSUPP;
-
- ret = nand_status_op(chip, NULL);
- if (ret)
- return ret;
-
- timeout_ms = jiffies + msecs_to_jiffies(timeout_ms);
- do {
- ret = nand_read_data_op(chip, &status, sizeof(status), true);
- if (ret)
- break;
-
- if (status & NAND_STATUS_READY)
- break;
-
- /*
- * Typical lowest execution time for a tR on most NANDs is 10us,
- * use this as polling delay before doing something smarter (ie.
- * deriving a delay from the timeout value, timeout_ms/ratio).
- */
- udelay(10);
- } while (time_before(jiffies, timeout_ms));
-
- /*
- * We have to exit READ_STATUS mode in order to read real data on the
- * bus in case the WAITRDY instruction is preceding a DATA_IN
- * instruction.
- */
- nand_exit_status_op(chip);
-
- if (ret)
- return ret;
-
- return status & NAND_STATUS_READY ? 0 : -ETIMEDOUT;
-};
-EXPORT_SYMBOL_GPL(nand_soft_waitrdy);
-
-/**
- * nand_command - [DEFAULT] Send command to NAND device
- * @mtd: MTD device structure
- * @command: the command to be sent
- * @column: the column address for this command, -1 if none
- * @page_addr: the page address for this command, -1 if none
- *
- * Send command to NAND device. This function is used for small page devices
- * (512 Bytes per page).
- */
-static void nand_command(struct mtd_info *mtd, unsigned int command,
- int column, int page_addr)
-{
- register struct nand_chip *chip = mtd_to_nand(mtd);
- int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE;
-
- /* Write out the command to the device */
- if (command == NAND_CMD_SEQIN) {
- int readcmd;
-
- if (column >= mtd->writesize) {
- /* OOB area */
- column -= mtd->writesize;
- readcmd = NAND_CMD_READOOB;
- } else if (column < 256) {
- /* First 256 bytes --> READ0 */
- readcmd = NAND_CMD_READ0;
- } else {
- column -= 256;
- readcmd = NAND_CMD_READ1;
- }
- chip->cmd_ctrl(mtd, readcmd, ctrl);
- ctrl &= ~NAND_CTRL_CHANGE;
- }
- if (command != NAND_CMD_NONE)
- chip->cmd_ctrl(mtd, command, ctrl);
-
- /* Address cycle, when necessary */
- ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE;
- /* Serially input address */
- if (column != -1) {
- /* Adjust columns for 16 bit buswidth */
- if (chip->options & NAND_BUSWIDTH_16 &&
- !nand_opcode_8bits(command))
- column >>= 1;
- chip->cmd_ctrl(mtd, column, ctrl);
- ctrl &= ~NAND_CTRL_CHANGE;
- }
- if (page_addr != -1) {
- chip->cmd_ctrl(mtd, page_addr, ctrl);
- ctrl &= ~NAND_CTRL_CHANGE;
- chip->cmd_ctrl(mtd, page_addr >> 8, ctrl);
- if (chip->options & NAND_ROW_ADDR_3)
- chip->cmd_ctrl(mtd, page_addr >> 16, ctrl);
- }
- chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
-
- /*
- * Program and erase have their own busy handlers status and sequential
- * in needs no delay
- */
- switch (command) {
-
- case NAND_CMD_NONE:
- case NAND_CMD_PAGEPROG:
- case NAND_CMD_ERASE1:
- case NAND_CMD_ERASE2:
- case NAND_CMD_SEQIN:
- case NAND_CMD_STATUS:
- case NAND_CMD_READID:
- case NAND_CMD_SET_FEATURES:
- return;
-
- case NAND_CMD_RESET:
- if (chip->dev_ready)
- break;
- udelay(chip->chip_delay);
- chip->cmd_ctrl(mtd, NAND_CMD_STATUS,
- NAND_CTRL_CLE | NAND_CTRL_CHANGE);
- chip->cmd_ctrl(mtd,
- NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
- /* EZ-NAND can take upto 250ms as per ONFi v4.0 */
- nand_wait_status_ready(mtd, 250);
- return;
-
- /* This applies to read commands */
- case NAND_CMD_READ0:
- /*
- * READ0 is sometimes used to exit GET STATUS mode. When this
- * is the case no address cycles are requested, and we can use
- * this information to detect that we should not wait for the
- * device to be ready.
- */
- if (column == -1 && page_addr == -1)
- return;
-
- default:
- /*
- * If we don't have access to the busy pin, we apply the given
- * command delay
- */
- if (!chip->dev_ready) {
- udelay(chip->chip_delay);
- return;
- }
- }
- /*
- * Apply this short delay always to ensure that we do wait tWB in
- * any case on any machine.
- */
- ndelay(100);
-
- nand_wait_ready(mtd);
-}
-
-static void nand_ccs_delay(struct nand_chip *chip)
-{
- /*
- * The controller already takes care of waiting for tCCS when the RNDIN
- * or RNDOUT command is sent, return directly.
- */
- if (!(chip->options & NAND_WAIT_TCCS))
- return;
-
- /*
- * Wait tCCS_min if it is correctly defined, otherwise wait 500ns
- * (which should be safe for all NANDs).
- */
- if (chip->setup_data_interface)
- ndelay(chip->data_interface.timings.sdr.tCCS_min / 1000);
- else
- ndelay(500);
-}
-
-/**
- * nand_command_lp - [DEFAULT] Send command to NAND large page device
- * @mtd: MTD device structure
- * @command: the command to be sent
- * @column: the column address for this command, -1 if none
- * @page_addr: the page address for this command, -1 if none
- *
- * Send command to NAND device. This is the version for the new large page
- * devices. We don't have the separate regions as we have in the small page
- * devices. We must emulate NAND_CMD_READOOB to keep the code compatible.
- */
-static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
- int column, int page_addr)
-{
- register struct nand_chip *chip = mtd_to_nand(mtd);
-
- /* Emulate NAND_CMD_READOOB */
- if (command == NAND_CMD_READOOB) {
- column += mtd->writesize;
- command = NAND_CMD_READ0;
- }
-
- /* Command latch cycle */
- if (command != NAND_CMD_NONE)
- chip->cmd_ctrl(mtd, command,
- NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
-
- if (column != -1 || page_addr != -1) {
- int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
-
- /* Serially input address */
- if (column != -1) {
- /* Adjust columns for 16 bit buswidth */
- if (chip->options & NAND_BUSWIDTH_16 &&
- !nand_opcode_8bits(command))
- column >>= 1;
- chip->cmd_ctrl(mtd, column, ctrl);
- ctrl &= ~NAND_CTRL_CHANGE;
-
- /* Only output a single addr cycle for 8bits opcodes. */
- if (!nand_opcode_8bits(command))
- chip->cmd_ctrl(mtd, column >> 8, ctrl);
- }
- if (page_addr != -1) {
- chip->cmd_ctrl(mtd, page_addr, ctrl);
- chip->cmd_ctrl(mtd, page_addr >> 8,
- NAND_NCE | NAND_ALE);
- if (chip->options & NAND_ROW_ADDR_3)
- chip->cmd_ctrl(mtd, page_addr >> 16,
- NAND_NCE | NAND_ALE);
- }
- }
- chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
-
- /*
- * Program and erase have their own busy handlers status, sequential
- * in and status need no delay.
- */
- switch (command) {
-
- case NAND_CMD_NONE:
- case NAND_CMD_CACHEDPROG:
- case NAND_CMD_PAGEPROG:
- case NAND_CMD_ERASE1:
- case NAND_CMD_ERASE2:
- case NAND_CMD_SEQIN:
- case NAND_CMD_STATUS:
- case NAND_CMD_READID:
- case NAND_CMD_SET_FEATURES:
- return;
-
- case NAND_CMD_RNDIN:
- nand_ccs_delay(chip);
- return;
-
- case NAND_CMD_RESET:
- if (chip->dev_ready)
- break;
- udelay(chip->chip_delay);
- chip->cmd_ctrl(mtd, NAND_CMD_STATUS,
- NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
- chip->cmd_ctrl(mtd, NAND_CMD_NONE,
- NAND_NCE | NAND_CTRL_CHANGE);
- /* EZ-NAND can take upto 250ms as per ONFi v4.0 */
- nand_wait_status_ready(mtd, 250);
- return;
-
- case NAND_CMD_RNDOUT:
- /* No ready / busy check necessary */
- chip->cmd_ctrl(mtd, NAND_CMD_RNDOUTSTART,
- NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
- chip->cmd_ctrl(mtd, NAND_CMD_NONE,
- NAND_NCE | NAND_CTRL_CHANGE);
-
- nand_ccs_delay(chip);
- return;
-
- case NAND_CMD_READ0:
- /*
- * READ0 is sometimes used to exit GET STATUS mode. When this
- * is the case no address cycles are requested, and we can use
- * this information to detect that READSTART should not be
- * issued.
- */
- if (column == -1 && page_addr == -1)
- return;
-
- chip->cmd_ctrl(mtd, NAND_CMD_READSTART,
- NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
- chip->cmd_ctrl(mtd, NAND_CMD_NONE,
- NAND_NCE | NAND_CTRL_CHANGE);
-
- /* This applies to read commands */
- default:
- /*
- * If we don't have access to the busy pin, we apply the given
- * command delay.
- */
- if (!chip->dev_ready) {
- udelay(chip->chip_delay);
- return;
- }
- }
-
- /*
- * Apply this short delay always to ensure that we do wait tWB in
- * any case on any machine.
- */
- ndelay(100);
-
- nand_wait_ready(mtd);
-}
-
-/**
- * panic_nand_get_device - [GENERIC] Get chip for selected access
- * @chip: the nand chip descriptor
- * @mtd: MTD device structure
- * @new_state: the state which is requested
- *
- * Used when in panic, no locks are taken.
- */
-static void panic_nand_get_device(struct nand_chip *chip,
- struct mtd_info *mtd, int new_state)
-{
- /* Hardware controller shared among independent devices */
- chip->controller->active = chip;
- chip->state = new_state;
-}
-
-/**
- * nand_get_device - [GENERIC] Get chip for selected access
- * @mtd: MTD device structure
- * @new_state: the state which is requested
- *
- * Get the device and lock it for exclusive access
- */
-static int
-nand_get_device(struct mtd_info *mtd, int new_state)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- spinlock_t *lock = &chip->controller->lock;
- wait_queue_head_t *wq = &chip->controller->wq;
- DECLARE_WAITQUEUE(wait, current);
-retry:
- spin_lock(lock);
-
- /* Hardware controller shared among independent devices */
- if (!chip->controller->active)
- chip->controller->active = chip;
-
- if (chip->controller->active == chip && chip->state == FL_READY) {
- chip->state = new_state;
- spin_unlock(lock);
- return 0;
- }
- if (new_state == FL_PM_SUSPENDED) {
- if (chip->controller->active->state == FL_PM_SUSPENDED) {
- chip->state = FL_PM_SUSPENDED;
- spin_unlock(lock);
- return 0;
- }
- }
- set_current_state(TASK_UNINTERRUPTIBLE);
- add_wait_queue(wq, &wait);
- spin_unlock(lock);
- schedule();
- remove_wait_queue(wq, &wait);
- goto retry;
-}
-
-/**
- * panic_nand_wait - [GENERIC] wait until the command is done
- * @mtd: MTD device structure
- * @chip: NAND chip structure
- * @timeo: timeout
- *
- * Wait for command done. This is a helper function for nand_wait used when
- * we are in interrupt context. May happen when in panic and trying to write
- * an oops through mtdoops.
- */
-static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip,
- unsigned long timeo)
-{
- int i;
- for (i = 0; i < timeo; i++) {
- if (chip->dev_ready) {
- if (chip->dev_ready(mtd))
- break;
- } else {
- int ret;
- u8 status;
-
- ret = nand_read_data_op(chip, &status, sizeof(status),
- true);
- if (ret)
- return;
-
- if (status & NAND_STATUS_READY)
- break;
- }
- mdelay(1);
- }
-}
-
-/**
- * nand_wait - [DEFAULT] wait until the command is done
- * @mtd: MTD device structure
- * @chip: NAND chip structure
- *
- * Wait for command done. This applies to erase and program only.
- */
-static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
-{
-
- unsigned long timeo = 400;
- u8 status;
- int ret;
-
- /*
- * Apply this short delay always to ensure that we do wait tWB in any
- * case on any machine.
- */
- ndelay(100);
-
- ret = nand_status_op(chip, NULL);
- if (ret)
- return ret;
-
- if (in_interrupt() || oops_in_progress)
- panic_nand_wait(mtd, chip, timeo);
- else {
- timeo = jiffies + msecs_to_jiffies(timeo);
- do {
- if (chip->dev_ready) {
- if (chip->dev_ready(mtd))
- break;
- } else {
- ret = nand_read_data_op(chip, &status,
- sizeof(status), true);
- if (ret)
- return ret;
-
- if (status & NAND_STATUS_READY)
- break;
- }
- cond_resched();
- } while (time_before(jiffies, timeo));
- }
-
- ret = nand_read_data_op(chip, &status, sizeof(status), true);
- if (ret)
- return ret;
-
- /* This can happen if in case of timeout or buggy dev_ready */
- WARN_ON(!(status & NAND_STATUS_READY));
- return status;
-}
-
-/**
- * nand_reset_data_interface - Reset data interface and timings
- * @chip: The NAND chip
- * @chipnr: Internal die id
- *
- * Reset the Data interface and timings to ONFI mode 0.
- *
- * Returns 0 for success or negative error code otherwise.
- */
-static int nand_reset_data_interface(struct nand_chip *chip, int chipnr)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- int ret;
-
- if (!chip->setup_data_interface)
- return 0;
-
- /*
- * The ONFI specification says:
- * "
- * To transition from NV-DDR or NV-DDR2 to the SDR data
- * interface, the host shall use the Reset (FFh) command
- * using SDR timing mode 0. A device in any timing mode is
- * required to recognize Reset (FFh) command issued in SDR
- * timing mode 0.
- * "
- *
- * Configure the data interface in SDR mode and set the
- * timings to timing mode 0.
- */
-
- onfi_fill_data_interface(chip, NAND_SDR_IFACE, 0);
- ret = chip->setup_data_interface(mtd, chipnr, &chip->data_interface);
- if (ret)
- pr_err("Failed to configure data interface to SDR timing mode 0\n");
-
- return ret;
-}
-
-/**
- * nand_setup_data_interface - Setup the best data interface and timings
- * @chip: The NAND chip
- * @chipnr: Internal die id
- *
- * Find and configure the best data interface and NAND timings supported by
- * the chip and the driver.
- * First tries to retrieve supported timing modes from ONFI information,
- * and if the NAND chip does not support ONFI, relies on the
- * ->onfi_timing_mode_default specified in the nand_ids table.
- *
- * Returns 0 for success or negative error code otherwise.
- */
-static int nand_setup_data_interface(struct nand_chip *chip, int chipnr)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- int ret;
-
- if (!chip->setup_data_interface)
- return 0;
-
- /*
- * Ensure the timing mode has been changed on the chip side
- * before changing timings on the controller side.
- */
- if (chip->onfi_version &&
- (le16_to_cpu(chip->onfi_params.opt_cmd) &
- ONFI_OPT_CMD_SET_GET_FEATURES)) {
- u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = {
- chip->onfi_timing_mode_default,
- };
-
- ret = chip->onfi_set_features(mtd, chip,
- ONFI_FEATURE_ADDR_TIMING_MODE,
- tmode_param);
- if (ret)
- goto err;
- }
-
- ret = chip->setup_data_interface(mtd, chipnr, &chip->data_interface);
-err:
- return ret;
-}
-
-/**
- * nand_init_data_interface - find the best data interface and timings
- * @chip: The NAND chip
- *
- * Find the best data interface and NAND timings supported by the chip
- * and the driver.
- * First tries to retrieve supported timing modes from ONFI information,
- * and if the NAND chip does not support ONFI, relies on the
- * ->onfi_timing_mode_default specified in the nand_ids table. After this
- * function nand_chip->data_interface is initialized with the best timing mode
- * available.
- *
- * Returns 0 for success or negative error code otherwise.
- */
-static int nand_init_data_interface(struct nand_chip *chip)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- int modes, mode, ret;
-
- if (!chip->setup_data_interface)
- return 0;
-
- /*
- * First try to identify the best timings from ONFI parameters and
- * if the NAND does not support ONFI, fallback to the default ONFI
- * timing mode.
- */
- modes = onfi_get_async_timing_mode(chip);
- if (modes == ONFI_TIMING_MODE_UNKNOWN) {
- if (!chip->onfi_timing_mode_default)
- return 0;
-
- modes = GENMASK(chip->onfi_timing_mode_default, 0);
- }
-
-
- for (mode = fls(modes) - 1; mode >= 0; mode--) {
- ret = onfi_fill_data_interface(chip, NAND_SDR_IFACE, mode);
- if (ret)
- continue;
-
- /*
- * Pass NAND_DATA_IFACE_CHECK_ONLY to only check if the
- * controller supports the requested timings.
- */
- ret = chip->setup_data_interface(mtd,
- NAND_DATA_IFACE_CHECK_ONLY,
- &chip->data_interface);
- if (!ret) {
- chip->onfi_timing_mode_default = mode;
- break;
- }
- }
-
- return 0;
-}
-
-/**
- * nand_fill_column_cycles - fill the column cycles of an address
- * @chip: The NAND chip
- * @addrs: Array of address cycles to fill
- * @offset_in_page: The offset in the page
- *
- * Fills the first or the first two bytes of the @addrs field depending
- * on the NAND bus width and the page size.
- *
- * Returns the number of cycles needed to encode the column, or a negative
- * error code in case one of the arguments is invalid.
- */
-static int nand_fill_column_cycles(struct nand_chip *chip, u8 *addrs,
- unsigned int offset_in_page)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
-
- /* Make sure the offset is less than the actual page size. */
- if (offset_in_page > mtd->writesize + mtd->oobsize)
- return -EINVAL;
-
- /*
- * On small page NANDs, there's a dedicated command to access the OOB
- * area, and the column address is relative to the start of the OOB
- * area, not the start of the page. Asjust the address accordingly.
- */
- if (mtd->writesize <= 512 && offset_in_page >= mtd->writesize)
- offset_in_page -= mtd->writesize;
-
- /*
- * The offset in page is expressed in bytes, if the NAND bus is 16-bit
- * wide, then it must be divided by 2.
- */
- if (chip->options & NAND_BUSWIDTH_16) {
- if (WARN_ON(offset_in_page % 2))
- return -EINVAL;
-
- offset_in_page /= 2;
- }
-
- addrs[0] = offset_in_page;
-
- /*
- * Small page NANDs use 1 cycle for the columns, while large page NANDs
- * need 2
- */
- if (mtd->writesize <= 512)
- return 1;
-
- addrs[1] = offset_in_page >> 8;
-
- return 2;
-}
-
-static int nand_sp_exec_read_page_op(struct nand_chip *chip, unsigned int page,
- unsigned int offset_in_page, void *buf,
- unsigned int len)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- const struct nand_sdr_timings *sdr =
- nand_get_sdr_timings(&chip->data_interface);
- u8 addrs[4];
- struct nand_op_instr instrs[] = {
- NAND_OP_CMD(NAND_CMD_READ0, 0),
- NAND_OP_ADDR(3, addrs, PSEC_TO_NSEC(sdr->tWB_max)),
- NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tR_max),
- PSEC_TO_NSEC(sdr->tRR_min)),
- NAND_OP_DATA_IN(len, buf, 0),
- };
- struct nand_operation op = NAND_OPERATION(instrs);
- int ret;
-
- /* Drop the DATA_IN instruction if len is set to 0. */
- if (!len)
- op.ninstrs--;
-
- if (offset_in_page >= mtd->writesize)
- instrs[0].ctx.cmd.opcode = NAND_CMD_READOOB;
- else if (offset_in_page >= 256 &&
- !(chip->options & NAND_BUSWIDTH_16))
- instrs[0].ctx.cmd.opcode = NAND_CMD_READ1;
-
- ret = nand_fill_column_cycles(chip, addrs, offset_in_page);
- if (ret < 0)
- return ret;
-
- addrs[1] = page;
- addrs[2] = page >> 8;
-
- if (chip->options & NAND_ROW_ADDR_3) {
- addrs[3] = page >> 16;
- instrs[1].ctx.addr.naddrs++;
- }
-
- return nand_exec_op(chip, &op);
-}
-
-static int nand_lp_exec_read_page_op(struct nand_chip *chip, unsigned int page,
- unsigned int offset_in_page, void *buf,
- unsigned int len)
-{
- const struct nand_sdr_timings *sdr =
- nand_get_sdr_timings(&chip->data_interface);
- u8 addrs[5];
- struct nand_op_instr instrs[] = {
- NAND_OP_CMD(NAND_CMD_READ0, 0),
- NAND_OP_ADDR(4, addrs, 0),
- NAND_OP_CMD(NAND_CMD_READSTART, PSEC_TO_NSEC(sdr->tWB_max)),
- NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tR_max),
- PSEC_TO_NSEC(sdr->tRR_min)),
- NAND_OP_DATA_IN(len, buf, 0),
- };
- struct nand_operation op = NAND_OPERATION(instrs);
- int ret;
-
- /* Drop the DATA_IN instruction if len is set to 0. */
- if (!len)
- op.ninstrs--;
-
- ret = nand_fill_column_cycles(chip, addrs, offset_in_page);
- if (ret < 0)
- return ret;
-
- addrs[2] = page;
- addrs[3] = page >> 8;
-
- if (chip->options & NAND_ROW_ADDR_3) {
- addrs[4] = page >> 16;
- instrs[1].ctx.addr.naddrs++;
- }
-
- return nand_exec_op(chip, &op);
-}
-
-/**
- * nand_read_page_op - Do a READ PAGE operation
- * @chip: The NAND chip
- * @page: page to read
- * @offset_in_page: offset within the page
- * @buf: buffer used to store the data
- * @len: length of the buffer
- *
- * This function issues a READ PAGE operation.
- * This function does not select/unselect the CS line.
- *
- * Returns 0 on success, a negative error code otherwise.
- */
-int nand_read_page_op(struct nand_chip *chip, unsigned int page,
- unsigned int offset_in_page, void *buf, unsigned int len)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
-
- if (len && !buf)
- return -EINVAL;
-
- if (offset_in_page + len > mtd->writesize + mtd->oobsize)
- return -EINVAL;
-
- if (chip->exec_op) {
- if (mtd->writesize > 512)
- return nand_lp_exec_read_page_op(chip, page,
- offset_in_page, buf,
- len);
-
- return nand_sp_exec_read_page_op(chip, page, offset_in_page,
- buf, len);
- }
-
- chip->cmdfunc(mtd, NAND_CMD_READ0, offset_in_page, page);
- if (len)
- chip->read_buf(mtd, buf, len);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(nand_read_page_op);
-
-/**
- * nand_read_param_page_op - Do a READ PARAMETER PAGE operation
- * @chip: The NAND chip
- * @page: parameter page to read
- * @buf: buffer used to store the data
- * @len: length of the buffer
- *
- * This function issues a READ PARAMETER PAGE operation.
- * This function does not select/unselect the CS line.
- *
- * Returns 0 on success, a negative error code otherwise.
- */
-static int nand_read_param_page_op(struct nand_chip *chip, u8 page, void *buf,
- unsigned int len)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- unsigned int i;
- u8 *p = buf;
-
- if (len && !buf)
- return -EINVAL;
-
- if (chip->exec_op) {
- const struct nand_sdr_timings *sdr =
- nand_get_sdr_timings(&chip->data_interface);
- struct nand_op_instr instrs[] = {
- NAND_OP_CMD(NAND_CMD_PARAM, 0),
- NAND_OP_ADDR(1, &page, PSEC_TO_NSEC(sdr->tWB_max)),
- NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tR_max),
- PSEC_TO_NSEC(sdr->tRR_min)),
- NAND_OP_8BIT_DATA_IN(len, buf, 0),
- };
- struct nand_operation op = NAND_OPERATION(instrs);
-
- /* Drop the DATA_IN instruction if len is set to 0. */
- if (!len)
- op.ninstrs--;
-
- return nand_exec_op(chip, &op);
- }
-
- chip->cmdfunc(mtd, NAND_CMD_PARAM, page, -1);
- for (i = 0; i < len; i++)
- p[i] = chip->read_byte(mtd);
-
- return 0;
-}
-
-/**
- * nand_change_read_column_op - Do a CHANGE READ COLUMN operation
- * @chip: The NAND chip
- * @offset_in_page: offset within the page
- * @buf: buffer used to store the data
- * @len: length of the buffer
- * @force_8bit: force 8-bit bus access
- *
- * This function issues a CHANGE READ COLUMN operation.
- * This function does not select/unselect the CS line.
- *
- * Returns 0 on success, a negative error code otherwise.
- */
-int nand_change_read_column_op(struct nand_chip *chip,
- unsigned int offset_in_page, void *buf,
- unsigned int len, bool force_8bit)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
-
- if (len && !buf)
- return -EINVAL;
-
- if (offset_in_page + len > mtd->writesize + mtd->oobsize)
- return -EINVAL;
-
- /* Small page NANDs do not support column change. */
- if (mtd->writesize <= 512)
- return -ENOTSUPP;
-
- if (chip->exec_op) {
- const struct nand_sdr_timings *sdr =
- nand_get_sdr_timings(&chip->data_interface);
- u8 addrs[2] = {};
- struct nand_op_instr instrs[] = {
- NAND_OP_CMD(NAND_CMD_RNDOUT, 0),
- NAND_OP_ADDR(2, addrs, 0),
- NAND_OP_CMD(NAND_CMD_RNDOUTSTART,
- PSEC_TO_NSEC(sdr->tCCS_min)),
- NAND_OP_DATA_IN(len, buf, 0),
- };
- struct nand_operation op = NAND_OPERATION(instrs);
- int ret;
-
- ret = nand_fill_column_cycles(chip, addrs, offset_in_page);
- if (ret < 0)
- return ret;
-
- /* Drop the DATA_IN instruction if len is set to 0. */
- if (!len)
- op.ninstrs--;
-
- instrs[3].ctx.data.force_8bit = force_8bit;
-
- return nand_exec_op(chip, &op);
- }
-
- chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset_in_page, -1);
- if (len)
- chip->read_buf(mtd, buf, len);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(nand_change_read_column_op);
-
-/**
- * nand_read_oob_op - Do a READ OOB operation
- * @chip: The NAND chip
- * @page: page to read
- * @offset_in_oob: offset within the OOB area
- * @buf: buffer used to store the data
- * @len: length of the buffer
- *
- * This function issues a READ OOB operation.
- * This function does not select/unselect the CS line.
- *
- * Returns 0 on success, a negative error code otherwise.
- */
-int nand_read_oob_op(struct nand_chip *chip, unsigned int page,
- unsigned int offset_in_oob, void *buf, unsigned int len)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
-
- if (len && !buf)
- return -EINVAL;
-
- if (offset_in_oob + len > mtd->oobsize)
- return -EINVAL;
-
- if (chip->exec_op)
- return nand_read_page_op(chip, page,
- mtd->writesize + offset_in_oob,
- buf, len);
-
- chip->cmdfunc(mtd, NAND_CMD_READOOB, offset_in_oob, page);
- if (len)
- chip->read_buf(mtd, buf, len);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(nand_read_oob_op);
-
-static int nand_exec_prog_page_op(struct nand_chip *chip, unsigned int page,
- unsigned int offset_in_page, const void *buf,
- unsigned int len, bool prog)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- const struct nand_sdr_timings *sdr =
- nand_get_sdr_timings(&chip->data_interface);
- u8 addrs[5] = {};
- struct nand_op_instr instrs[] = {
- /*
- * The first instruction will be dropped if we're dealing
- * with a large page NAND and adjusted if we're dealing
- * with a small page NAND and the page offset is > 255.
- */
- NAND_OP_CMD(NAND_CMD_READ0, 0),
- NAND_OP_CMD(NAND_CMD_SEQIN, 0),
- NAND_OP_ADDR(0, addrs, PSEC_TO_NSEC(sdr->tADL_min)),
- NAND_OP_DATA_OUT(len, buf, 0),
- NAND_OP_CMD(NAND_CMD_PAGEPROG, PSEC_TO_NSEC(sdr->tWB_max)),
- NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tPROG_max), 0),
- };
- struct nand_operation op = NAND_OPERATION(instrs);
- int naddrs = nand_fill_column_cycles(chip, addrs, offset_in_page);
- int ret;
- u8 status;
-
- if (naddrs < 0)
- return naddrs;
-
- addrs[naddrs++] = page;
- addrs[naddrs++] = page >> 8;
- if (chip->options & NAND_ROW_ADDR_3)
- addrs[naddrs++] = page >> 16;
-
- instrs[2].ctx.addr.naddrs = naddrs;
-
- /* Drop the last two instructions if we're not programming the page. */
- if (!prog) {
- op.ninstrs -= 2;
- /* Also drop the DATA_OUT instruction if empty. */
- if (!len)
- op.ninstrs--;
- }
-
- if (mtd->writesize <= 512) {
- /*
- * Small pages need some more tweaking: we have to adjust the
- * first instruction depending on the page offset we're trying
- * to access.
- */
- if (offset_in_page >= mtd->writesize)
- instrs[0].ctx.cmd.opcode = NAND_CMD_READOOB;
- else if (offset_in_page >= 256 &&
- !(chip->options & NAND_BUSWIDTH_16))
- instrs[0].ctx.cmd.opcode = NAND_CMD_READ1;
- } else {
- /*
- * Drop the first command if we're dealing with a large page
- * NAND.
- */
- op.instrs++;
- op.ninstrs--;
- }
-
- ret = nand_exec_op(chip, &op);
- if (!prog || ret)
- return ret;
-
- ret = nand_status_op(chip, &status);
- if (ret)
- return ret;
-
- return status;
-}
-
-/**
- * nand_prog_page_begin_op - starts a PROG PAGE operation
- * @chip: The NAND chip
- * @page: page to write
- * @offset_in_page: offset within the page
- * @buf: buffer containing the data to write to the page
- * @len: length of the buffer
- *
- * This function issues the first half of a PROG PAGE operation.
- * This function does not select/unselect the CS line.
- *
- * Returns 0 on success, a negative error code otherwise.
- */
-int nand_prog_page_begin_op(struct nand_chip *chip, unsigned int page,
- unsigned int offset_in_page, const void *buf,
- unsigned int len)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
-
- if (len && !buf)
- return -EINVAL;
-
- if (offset_in_page + len > mtd->writesize + mtd->oobsize)
- return -EINVAL;
-
- if (chip->exec_op)
- return nand_exec_prog_page_op(chip, page, offset_in_page, buf,
- len, false);
-
- chip->cmdfunc(mtd, NAND_CMD_SEQIN, offset_in_page, page);
-
- if (buf)
- chip->write_buf(mtd, buf, len);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(nand_prog_page_begin_op);
-
-/**
- * nand_prog_page_end_op - ends a PROG PAGE operation
- * @chip: The NAND chip
- *
- * This function issues the second half of a PROG PAGE operation.
- * This function does not select/unselect the CS line.
- *
- * Returns 0 on success, a negative error code otherwise.
- */
-int nand_prog_page_end_op(struct nand_chip *chip)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- int ret;
- u8 status;
-
- if (chip->exec_op) {
- const struct nand_sdr_timings *sdr =
- nand_get_sdr_timings(&chip->data_interface);
- struct nand_op_instr instrs[] = {
- NAND_OP_CMD(NAND_CMD_PAGEPROG,
- PSEC_TO_NSEC(sdr->tWB_max)),
- NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tPROG_max), 0),
- };
- struct nand_operation op = NAND_OPERATION(instrs);
-
- ret = nand_exec_op(chip, &op);
- if (ret)
- return ret;
-
- ret = nand_status_op(chip, &status);
- if (ret)
- return ret;
- } else {
- chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
- ret = chip->waitfunc(mtd, chip);
- if (ret < 0)
- return ret;
-
- status = ret;
- }
-
- if (status & NAND_STATUS_FAIL)
- return -EIO;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(nand_prog_page_end_op);
-
-/**
- * nand_prog_page_op - Do a full PROG PAGE operation
- * @chip: The NAND chip
- * @page: page to write
- * @offset_in_page: offset within the page
- * @buf: buffer containing the data to write to the page
- * @len: length of the buffer
- *
- * This function issues a full PROG PAGE operation.
- * This function does not select/unselect the CS line.
- *
- * Returns 0 on success, a negative error code otherwise.
- */
-int nand_prog_page_op(struct nand_chip *chip, unsigned int page,
- unsigned int offset_in_page, const void *buf,
- unsigned int len)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- int status;
-
- if (!len || !buf)
- return -EINVAL;
-
- if (offset_in_page + len > mtd->writesize + mtd->oobsize)
- return -EINVAL;
-
- if (chip->exec_op) {
- status = nand_exec_prog_page_op(chip, page, offset_in_page, buf,
- len, true);
- } else {
- chip->cmdfunc(mtd, NAND_CMD_SEQIN, offset_in_page, page);
- chip->write_buf(mtd, buf, len);
- chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
- status = chip->waitfunc(mtd, chip);
- }
-
- if (status & NAND_STATUS_FAIL)
- return -EIO;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(nand_prog_page_op);
-
-/**
- * nand_change_write_column_op - Do a CHANGE WRITE COLUMN operation
- * @chip: The NAND chip
- * @offset_in_page: offset within the page
- * @buf: buffer containing the data to send to the NAND
- * @len: length of the buffer
- * @force_8bit: force 8-bit bus access
- *
- * This function issues a CHANGE WRITE COLUMN operation.
- * This function does not select/unselect the CS line.
- *
- * Returns 0 on success, a negative error code otherwise.
- */
-int nand_change_write_column_op(struct nand_chip *chip,
- unsigned int offset_in_page,
- const void *buf, unsigned int len,
- bool force_8bit)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
-
- if (len && !buf)
- return -EINVAL;
-
- if (offset_in_page + len > mtd->writesize + mtd->oobsize)
- return -EINVAL;
-
- /* Small page NANDs do not support column change. */
- if (mtd->writesize <= 512)
- return -ENOTSUPP;
-
- if (chip->exec_op) {
- const struct nand_sdr_timings *sdr =
- nand_get_sdr_timings(&chip->data_interface);
- u8 addrs[2];
- struct nand_op_instr instrs[] = {
- NAND_OP_CMD(NAND_CMD_RNDIN, 0),
- NAND_OP_ADDR(2, addrs, PSEC_TO_NSEC(sdr->tCCS_min)),
- NAND_OP_DATA_OUT(len, buf, 0),
- };
- struct nand_operation op = NAND_OPERATION(instrs);
- int ret;
-
- ret = nand_fill_column_cycles(chip, addrs, offset_in_page);
- if (ret < 0)
- return ret;
-
- instrs[2].ctx.data.force_8bit = force_8bit;
-
- /* Drop the DATA_OUT instruction if len is set to 0. */
- if (!len)
- op.ninstrs--;
-
- return nand_exec_op(chip, &op);
- }
-
- chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset_in_page, -1);
- if (len)
- chip->write_buf(mtd, buf, len);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(nand_change_write_column_op);
-
-/**
- * nand_readid_op - Do a READID operation
- * @chip: The NAND chip
- * @addr: address cycle to pass after the READID command
- * @buf: buffer used to store the ID
- * @len: length of the buffer
- *
- * This function sends a READID command and reads back the ID returned by the
- * NAND.
- * This function does not select/unselect the CS line.
- *
- * Returns 0 on success, a negative error code otherwise.
- */
-int nand_readid_op(struct nand_chip *chip, u8 addr, void *buf,
- unsigned int len)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- unsigned int i;
- u8 *id = buf;
-
- if (len && !buf)
- return -EINVAL;
-
- if (chip->exec_op) {
- const struct nand_sdr_timings *sdr =
- nand_get_sdr_timings(&chip->data_interface);
- struct nand_op_instr instrs[] = {
- NAND_OP_CMD(NAND_CMD_READID, 0),
- NAND_OP_ADDR(1, &addr, PSEC_TO_NSEC(sdr->tADL_min)),
- NAND_OP_8BIT_DATA_IN(len, buf, 0),
- };
- struct nand_operation op = NAND_OPERATION(instrs);
-
- /* Drop the DATA_IN instruction if len is set to 0. */
- if (!len)
- op.ninstrs--;
-
- return nand_exec_op(chip, &op);
- }
-
- chip->cmdfunc(mtd, NAND_CMD_READID, addr, -1);
-
- for (i = 0; i < len; i++)
- id[i] = chip->read_byte(mtd);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(nand_readid_op);
-
-/**
- * nand_status_op - Do a STATUS operation
- * @chip: The NAND chip
- * @status: out variable to store the NAND status
- *
- * This function sends a STATUS command and reads back the status returned by
- * the NAND.
- * This function does not select/unselect the CS line.
- *
- * Returns 0 on success, a negative error code otherwise.
- */
-int nand_status_op(struct nand_chip *chip, u8 *status)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
-
- if (chip->exec_op) {
- const struct nand_sdr_timings *sdr =
- nand_get_sdr_timings(&chip->data_interface);
- struct nand_op_instr instrs[] = {
- NAND_OP_CMD(NAND_CMD_STATUS,
- PSEC_TO_NSEC(sdr->tADL_min)),
- NAND_OP_8BIT_DATA_IN(1, status, 0),
- };
- struct nand_operation op = NAND_OPERATION(instrs);
-
- if (!status)
- op.ninstrs--;
-
- return nand_exec_op(chip, &op);
- }
-
- chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
- if (status)
- *status = chip->read_byte(mtd);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(nand_status_op);
-
-/**
- * nand_exit_status_op - Exit a STATUS operation
- * @chip: The NAND chip
- *
- * This function sends a READ0 command to cancel the effect of the STATUS
- * command to avoid reading only the status until a new read command is sent.
- *
- * This function does not select/unselect the CS line.
- *
- * Returns 0 on success, a negative error code otherwise.
- */
-int nand_exit_status_op(struct nand_chip *chip)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
-
- if (chip->exec_op) {
- struct nand_op_instr instrs[] = {
- NAND_OP_CMD(NAND_CMD_READ0, 0),
- };
- struct nand_operation op = NAND_OPERATION(instrs);
-
- return nand_exec_op(chip, &op);
- }
-
- chip->cmdfunc(mtd, NAND_CMD_READ0, -1, -1);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(nand_exit_status_op);
-
-/**
- * nand_erase_op - Do an erase operation
- * @chip: The NAND chip
- * @eraseblock: block to erase
- *
- * This function sends an ERASE command and waits for the NAND to be ready
- * before returning.
- * This function does not select/unselect the CS line.
- *
- * Returns 0 on success, a negative error code otherwise.
- */
-int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- unsigned int page = eraseblock <<
- (chip->phys_erase_shift - chip->page_shift);
- int ret;
- u8 status;
-
- if (chip->exec_op) {
- const struct nand_sdr_timings *sdr =
- nand_get_sdr_timings(&chip->data_interface);
- u8 addrs[3] = { page, page >> 8, page >> 16 };
- struct nand_op_instr instrs[] = {
- NAND_OP_CMD(NAND_CMD_ERASE1, 0),
- NAND_OP_ADDR(2, addrs, 0),
- NAND_OP_CMD(NAND_CMD_ERASE2,
- PSEC_TO_MSEC(sdr->tWB_max)),
- NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tBERS_max), 0),
- };
- struct nand_operation op = NAND_OPERATION(instrs);
-
- if (chip->options & NAND_ROW_ADDR_3)
- instrs[1].ctx.addr.naddrs++;
-
- ret = nand_exec_op(chip, &op);
- if (ret)
- return ret;
-
- ret = nand_status_op(chip, &status);
- if (ret)
- return ret;
- } else {
- chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
- chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
-
- ret = chip->waitfunc(mtd, chip);
- if (ret < 0)
- return ret;
-
- status = ret;
- }
-
- if (status & NAND_STATUS_FAIL)
- return -EIO;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(nand_erase_op);
-
-/**
- * nand_set_features_op - Do a SET FEATURES operation
- * @chip: The NAND chip
- * @feature: feature id
- * @data: 4 bytes of data
- *
- * This function sends a SET FEATURES command and waits for the NAND to be
- * ready before returning.
- * This function does not select/unselect the CS line.
- *
- * Returns 0 on success, a negative error code otherwise.
- */
-static int nand_set_features_op(struct nand_chip *chip, u8 feature,
- const void *data)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- const u8 *params = data;
- int i, ret;
- u8 status;
-
- if (chip->exec_op) {
- const struct nand_sdr_timings *sdr =
- nand_get_sdr_timings(&chip->data_interface);
- struct nand_op_instr instrs[] = {
- NAND_OP_CMD(NAND_CMD_SET_FEATURES, 0),
- NAND_OP_ADDR(1, &feature, PSEC_TO_NSEC(sdr->tADL_min)),
- NAND_OP_8BIT_DATA_OUT(ONFI_SUBFEATURE_PARAM_LEN, data,
- PSEC_TO_NSEC(sdr->tWB_max)),
- NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tFEAT_max), 0),
- };
- struct nand_operation op = NAND_OPERATION(instrs);
-
- ret = nand_exec_op(chip, &op);
- if (ret)
- return ret;
-
- ret = nand_status_op(chip, &status);
- if (ret)
- return ret;
- } else {
- chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, feature, -1);
- for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
- chip->write_byte(mtd, params[i]);
-
- ret = chip->waitfunc(mtd, chip);
- if (ret < 0)
- return ret;
-
- status = ret;
- }
-
- if (status & NAND_STATUS_FAIL)
- return -EIO;
-
- return 0;
-}
-
-/**
- * nand_get_features_op - Do a GET FEATURES operation
- * @chip: The NAND chip
- * @feature: feature id
- * @data: 4 bytes of data
- *
- * This function sends a GET FEATURES command and waits for the NAND to be
- * ready before returning.
- * This function does not select/unselect the CS line.
- *
- * Returns 0 on success, a negative error code otherwise.
- */
-static int nand_get_features_op(struct nand_chip *chip, u8 feature,
- void *data)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- u8 *params = data;
- int i;
-
- if (chip->exec_op) {
- const struct nand_sdr_timings *sdr =
- nand_get_sdr_timings(&chip->data_interface);
- struct nand_op_instr instrs[] = {
- NAND_OP_CMD(NAND_CMD_GET_FEATURES, 0),
- NAND_OP_ADDR(1, &feature, PSEC_TO_NSEC(sdr->tWB_max)),
- NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tFEAT_max),
- PSEC_TO_NSEC(sdr->tRR_min)),
- NAND_OP_8BIT_DATA_IN(ONFI_SUBFEATURE_PARAM_LEN,
- data, 0),
- };
- struct nand_operation op = NAND_OPERATION(instrs);
-
- return nand_exec_op(chip, &op);
- }
-
- chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, feature, -1);
- for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
- params[i] = chip->read_byte(mtd);
-
- return 0;
-}
-
-/**
- * nand_reset_op - Do a reset operation
- * @chip: The NAND chip
- *
- * This function sends a RESET command and waits for the NAND to be ready
- * before returning.
- * This function does not select/unselect the CS line.
- *
- * Returns 0 on success, a negative error code otherwise.
- */
-int nand_reset_op(struct nand_chip *chip)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
-
- if (chip->exec_op) {
- const struct nand_sdr_timings *sdr =
- nand_get_sdr_timings(&chip->data_interface);
- struct nand_op_instr instrs[] = {
- NAND_OP_CMD(NAND_CMD_RESET, PSEC_TO_NSEC(sdr->tWB_max)),
- NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tRST_max), 0),
- };
- struct nand_operation op = NAND_OPERATION(instrs);
-
- return nand_exec_op(chip, &op);
- }
-
- chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(nand_reset_op);
-
-/**
- * nand_read_data_op - Read data from the NAND
- * @chip: The NAND chip
- * @buf: buffer used to store the data
- * @len: length of the buffer
- * @force_8bit: force 8-bit bus access
- *
- * This function does a raw data read on the bus. Usually used after launching
- * another NAND operation like nand_read_page_op().
- * This function does not select/unselect the CS line.
- *
- * Returns 0 on success, a negative error code otherwise.
- */
-int nand_read_data_op(struct nand_chip *chip, void *buf, unsigned int len,
- bool force_8bit)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
-
- if (!len || !buf)
- return -EINVAL;
-
- if (chip->exec_op) {
- struct nand_op_instr instrs[] = {
- NAND_OP_DATA_IN(len, buf, 0),
- };
- struct nand_operation op = NAND_OPERATION(instrs);
-
- instrs[0].ctx.data.force_8bit = force_8bit;
-
- return nand_exec_op(chip, &op);
- }
-
- if (force_8bit) {
- u8 *p = buf;
- unsigned int i;
-
- for (i = 0; i < len; i++)
- p[i] = chip->read_byte(mtd);
- } else {
- chip->read_buf(mtd, buf, len);
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(nand_read_data_op);
-
-/**
- * nand_write_data_op - Write data from the NAND
- * @chip: The NAND chip
- * @buf: buffer containing the data to send on the bus
- * @len: length of the buffer
- * @force_8bit: force 8-bit bus access
- *
- * This function does a raw data write on the bus. Usually used after launching
- * another NAND operation like nand_write_page_begin_op().
- * This function does not select/unselect the CS line.
- *
- * Returns 0 on success, a negative error code otherwise.
- */
-int nand_write_data_op(struct nand_chip *chip, const void *buf,
- unsigned int len, bool force_8bit)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
-
- if (!len || !buf)
- return -EINVAL;
-
- if (chip->exec_op) {
- struct nand_op_instr instrs[] = {
- NAND_OP_DATA_OUT(len, buf, 0),
- };
- struct nand_operation op = NAND_OPERATION(instrs);
-
- instrs[0].ctx.data.force_8bit = force_8bit;
-
- return nand_exec_op(chip, &op);
- }
-
- if (force_8bit) {
- const u8 *p = buf;
- unsigned int i;
-
- for (i = 0; i < len; i++)
- chip->write_byte(mtd, p[i]);
- } else {
- chip->write_buf(mtd, buf, len);
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(nand_write_data_op);
-
-/**
- * struct nand_op_parser_ctx - Context used by the parser
- * @instrs: array of all the instructions that must be addressed
- * @ninstrs: length of the @instrs array
- * @subop: Sub-operation to be passed to the NAND controller
- *
- * This structure is used by the core to split NAND operations into
- * sub-operations that can be handled by the NAND controller.
- */
-struct nand_op_parser_ctx {
- const struct nand_op_instr *instrs;
- unsigned int ninstrs;
- struct nand_subop subop;
-};
-
-/**
- * nand_op_parser_must_split_instr - Checks if an instruction must be split
- * @pat: the parser pattern element that matches @instr
- * @instr: pointer to the instruction to check
- * @start_offset: this is an in/out parameter. If @instr has already been
- * split, then @start_offset is the offset from which to start
- * (either an address cycle or an offset in the data buffer).
- * Conversely, if the function returns true (ie. instr must be
- * split), this parameter is updated to point to the first
- * data/address cycle that has not been taken care of.
- *
- * Some NAND controllers are limited and cannot send X address cycles with a
- * unique operation, or cannot read/write more than Y bytes at the same time.
- * In this case, split the instruction that does not fit in a single
- * controller-operation into two or more chunks.
- *
- * Returns true if the instruction must be split, false otherwise.
- * The @start_offset parameter is also updated to the offset at which the next
- * bundle of instruction must start (if an address or a data instruction).
- */
-static bool
-nand_op_parser_must_split_instr(const struct nand_op_parser_pattern_elem *pat,
- const struct nand_op_instr *instr,
- unsigned int *start_offset)
-{
- switch (pat->type) {
- case NAND_OP_ADDR_INSTR:
- if (!pat->ctx.addr.maxcycles)
- break;
-
- if (instr->ctx.addr.naddrs - *start_offset >
- pat->ctx.addr.maxcycles) {
- *start_offset += pat->ctx.addr.maxcycles;
- return true;
- }
- break;
-
- case NAND_OP_DATA_IN_INSTR:
- case NAND_OP_DATA_OUT_INSTR:
- if (!pat->ctx.data.maxlen)
- break;
-
- if (instr->ctx.data.len - *start_offset >
- pat->ctx.data.maxlen) {
- *start_offset += pat->ctx.data.maxlen;
- return true;
- }
- break;
-
- default:
- break;
- }
-
- return false;
-}
-
-/**
- * nand_op_parser_match_pat - Checks if a pattern matches the instructions
- * remaining in the parser context
- * @pat: the pattern to test
- * @ctx: the parser context structure to match with the pattern @pat
- *
- * Check if @pat matches the set or a sub-set of instructions remaining in @ctx.
- * Returns true if this is the case, false ortherwise. When true is returned,
- * @ctx->subop is updated with the set of instructions to be passed to the
- * controller driver.
- */
-static bool
-nand_op_parser_match_pat(const struct nand_op_parser_pattern *pat,
- struct nand_op_parser_ctx *ctx)
-{
- unsigned int instr_offset = ctx->subop.first_instr_start_off;
- const struct nand_op_instr *end = ctx->instrs + ctx->ninstrs;
- const struct nand_op_instr *instr = ctx->subop.instrs;
- unsigned int i, ninstrs;
-
- for (i = 0, ninstrs = 0; i < pat->nelems && instr < end; i++) {
- /*
- * The pattern instruction does not match the operation
- * instruction. If the instruction is marked optional in the
- * pattern definition, we skip the pattern element and continue
- * to the next one. If the element is mandatory, there's no
- * match and we can return false directly.
- */
- if (instr->type != pat->elems[i].type) {
- if (!pat->elems[i].optional)
- return false;
-
- continue;
- }
-
- /*
- * Now check the pattern element constraints. If the pattern is
- * not able to handle the whole instruction in a single step,
- * we have to split it.
- * The last_instr_end_off value comes back updated to point to
- * the position where we have to split the instruction (the
- * start of the next subop chunk).
- */
- if (nand_op_parser_must_split_instr(&pat->elems[i], instr,
- &instr_offset)) {
- ninstrs++;
- i++;
- break;
- }
-
- instr++;
- ninstrs++;
- instr_offset = 0;
- }
-
- /*
- * This can happen if all instructions of a pattern are optional.
- * Still, if there's not at least one instruction handled by this
- * pattern, this is not a match, and we should try the next one (if
- * any).
- */
- if (!ninstrs)
- return false;
-
- /*
- * We had a match on the pattern head, but the pattern may be longer
- * than the instructions we're asked to execute. We need to make sure
- * there's no mandatory elements in the pattern tail.
- */
- for (; i < pat->nelems; i++) {
- if (!pat->elems[i].optional)
- return false;
- }
-
- /*
- * We have a match: update the subop structure accordingly and return
- * true.
- */
- ctx->subop.ninstrs = ninstrs;
- ctx->subop.last_instr_end_off = instr_offset;
-
- return true;
-}
-
-#if IS_ENABLED(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG)
-static void nand_op_parser_trace(const struct nand_op_parser_ctx *ctx)
-{
- const struct nand_op_instr *instr;
- char *prefix = " ";
- unsigned int i;
-
- pr_debug("executing subop:\n");
-
- for (i = 0; i < ctx->ninstrs; i++) {
- instr = &ctx->instrs[i];
-
- if (instr == &ctx->subop.instrs[0])
- prefix = " ->";
-
- switch (instr->type) {
- case NAND_OP_CMD_INSTR:
- pr_debug("%sCMD [0x%02x]\n", prefix,
- instr->ctx.cmd.opcode);
- break;
- case NAND_OP_ADDR_INSTR:
- pr_debug("%sADDR [%d cyc: %*ph]\n", prefix,
- instr->ctx.addr.naddrs,
- instr->ctx.addr.naddrs < 64 ?
- instr->ctx.addr.naddrs : 64,
- instr->ctx.addr.addrs);
- break;
- case NAND_OP_DATA_IN_INSTR:
- pr_debug("%sDATA_IN [%d B%s]\n", prefix,
- instr->ctx.data.len,
- instr->ctx.data.force_8bit ?
- ", force 8-bit" : "");
- break;
- case NAND_OP_DATA_OUT_INSTR:
- pr_debug("%sDATA_OUT [%d B%s]\n", prefix,
- instr->ctx.data.len,
- instr->ctx.data.force_8bit ?
- ", force 8-bit" : "");
- break;
- case NAND_OP_WAITRDY_INSTR:
- pr_debug("%sWAITRDY [max %d ms]\n", prefix,
- instr->ctx.waitrdy.timeout_ms);
- break;
- }
-
- if (instr == &ctx->subop.instrs[ctx->subop.ninstrs - 1])
- prefix = " ";
- }
-}
-#else
-static void nand_op_parser_trace(const struct nand_op_parser_ctx *ctx)
-{
- /* NOP */
-}
-#endif
-
-/**
- * nand_op_parser_exec_op - exec_op parser
- * @chip: the NAND chip
- * @parser: patterns description provided by the controller driver
- * @op: the NAND operation to address
- * @check_only: when true, the function only checks if @op can be handled but
- * does not execute the operation
- *
- * Helper function designed to ease integration of NAND controller drivers that
- * only support a limited set of instruction sequences. The supported sequences
- * are described in @parser, and the framework takes care of splitting @op into
- * multiple sub-operations (if required) and pass them back to the ->exec()
- * callback of the matching pattern if @check_only is set to false.
- *
- * NAND controller drivers should call this function from their own ->exec_op()
- * implementation.
- *
- * Returns 0 on success, a negative error code otherwise. A failure can be
- * caused by an unsupported operation (none of the supported patterns is able
- * to handle the requested operation), or an error returned by one of the
- * matching pattern->exec() hook.
- */
-int nand_op_parser_exec_op(struct nand_chip *chip,
- const struct nand_op_parser *parser,
- const struct nand_operation *op, bool check_only)
-{
- struct nand_op_parser_ctx ctx = {
- .subop.instrs = op->instrs,
- .instrs = op->instrs,
- .ninstrs = op->ninstrs,
- };
- unsigned int i;
-
- while (ctx.subop.instrs < op->instrs + op->ninstrs) {
- int ret;
-
- for (i = 0; i < parser->npatterns; i++) {
- const struct nand_op_parser_pattern *pattern;
-
- pattern = &parser->patterns[i];
- if (!nand_op_parser_match_pat(pattern, &ctx))
- continue;
-
- nand_op_parser_trace(&ctx);
-
- if (check_only)
- break;
-
- ret = pattern->exec(chip, &ctx.subop);
- if (ret)
- return ret;
-
- break;
- }
-
- if (i == parser->npatterns) {
- pr_debug("->exec_op() parser: pattern not found!\n");
- return -ENOTSUPP;
- }
-
- /*
- * Update the context structure by pointing to the start of the
- * next subop.
- */
- ctx.subop.instrs = ctx.subop.instrs + ctx.subop.ninstrs;
- if (ctx.subop.last_instr_end_off)
- ctx.subop.instrs -= 1;
-
- ctx.subop.first_instr_start_off = ctx.subop.last_instr_end_off;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(nand_op_parser_exec_op);
-
-static bool nand_instr_is_data(const struct nand_op_instr *instr)
-{
- return instr && (instr->type == NAND_OP_DATA_IN_INSTR ||
- instr->type == NAND_OP_DATA_OUT_INSTR);
-}
-
-static bool nand_subop_instr_is_valid(const struct nand_subop *subop,
- unsigned int instr_idx)
-{
- return subop && instr_idx < subop->ninstrs;
-}
-
-static int nand_subop_get_start_off(const struct nand_subop *subop,
- unsigned int instr_idx)
-{
- if (instr_idx)
- return 0;
-
- return subop->first_instr_start_off;
-}
-
-/**
- * nand_subop_get_addr_start_off - Get the start offset in an address array
- * @subop: The entire sub-operation
- * @instr_idx: Index of the instruction inside the sub-operation
- *
- * During driver development, one could be tempted to directly use the
- * ->addr.addrs field of address instructions. This is wrong as address
- * instructions might be split.
- *
- * Given an address instruction, returns the offset of the first cycle to issue.
- */
-int nand_subop_get_addr_start_off(const struct nand_subop *subop,
- unsigned int instr_idx)
-{
- if (!nand_subop_instr_is_valid(subop, instr_idx) ||
- subop->instrs[instr_idx].type != NAND_OP_ADDR_INSTR)
- return -EINVAL;
-
- return nand_subop_get_start_off(subop, instr_idx);
-}
-EXPORT_SYMBOL_GPL(nand_subop_get_addr_start_off);
-
-/**
- * nand_subop_get_num_addr_cyc - Get the remaining address cycles to assert
- * @subop: The entire sub-operation
- * @instr_idx: Index of the instruction inside the sub-operation
- *
- * During driver development, one could be tempted to directly use the
- * ->addr->naddrs field of a data instruction. This is wrong as instructions
- * might be split.
- *
- * Given an address instruction, returns the number of address cycle to issue.
- */
-int nand_subop_get_num_addr_cyc(const struct nand_subop *subop,
- unsigned int instr_idx)
-{
- int start_off, end_off;
-
- if (!nand_subop_instr_is_valid(subop, instr_idx) ||
- subop->instrs[instr_idx].type != NAND_OP_ADDR_INSTR)
- return -EINVAL;
-
- start_off = nand_subop_get_addr_start_off(subop, instr_idx);
-
- if (instr_idx == subop->ninstrs - 1 &&
- subop->last_instr_end_off)
- end_off = subop->last_instr_end_off;
- else
- end_off = subop->instrs[instr_idx].ctx.addr.naddrs;
-
- return end_off - start_off;
-}
-EXPORT_SYMBOL_GPL(nand_subop_get_num_addr_cyc);
-
-/**
- * nand_subop_get_data_start_off - Get the start offset in a data array
- * @subop: The entire sub-operation
- * @instr_idx: Index of the instruction inside the sub-operation
- *
- * During driver development, one could be tempted to directly use the
- * ->data->buf.{in,out} field of data instructions. This is wrong as data
- * instructions might be split.
- *
- * Given a data instruction, returns the offset to start from.
- */
-int nand_subop_get_data_start_off(const struct nand_subop *subop,
- unsigned int instr_idx)
-{
- if (!nand_subop_instr_is_valid(subop, instr_idx) ||
- !nand_instr_is_data(&subop->instrs[instr_idx]))
- return -EINVAL;
-
- return nand_subop_get_start_off(subop, instr_idx);
-}
-EXPORT_SYMBOL_GPL(nand_subop_get_data_start_off);
-
-/**
- * nand_subop_get_data_len - Get the number of bytes to retrieve
- * @subop: The entire sub-operation
- * @instr_idx: Index of the instruction inside the sub-operation
- *
- * During driver development, one could be tempted to directly use the
- * ->data->len field of a data instruction. This is wrong as data instructions
- * might be split.
- *
- * Returns the length of the chunk of data to send/receive.
- */
-int nand_subop_get_data_len(const struct nand_subop *subop,
- unsigned int instr_idx)
-{
- int start_off = 0, end_off;
-
- if (!nand_subop_instr_is_valid(subop, instr_idx) ||
- !nand_instr_is_data(&subop->instrs[instr_idx]))
- return -EINVAL;
-
- start_off = nand_subop_get_data_start_off(subop, instr_idx);
-
- if (instr_idx == subop->ninstrs - 1 &&
- subop->last_instr_end_off)
- end_off = subop->last_instr_end_off;
- else
- end_off = subop->instrs[instr_idx].ctx.data.len;
-
- return end_off - start_off;
-}
-EXPORT_SYMBOL_GPL(nand_subop_get_data_len);
-
-/**
- * nand_reset - Reset and initialize a NAND device
- * @chip: The NAND chip
- * @chipnr: Internal die id
- *
- * Save the timings data structure, then apply SDR timings mode 0 (see
- * nand_reset_data_interface for details), do the reset operation, and
- * apply back the previous timings.
- *
- * Returns 0 on success, a negative error code otherwise.
- */
-int nand_reset(struct nand_chip *chip, int chipnr)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct nand_data_interface saved_data_intf = chip->data_interface;
- int ret;
-
- ret = nand_reset_data_interface(chip, chipnr);
- if (ret)
- return ret;
-
- /*
- * The CS line has to be released before we can apply the new NAND
- * interface settings, hence this weird ->select_chip() dance.
- */
- chip->select_chip(mtd, chipnr);
- ret = nand_reset_op(chip);
- chip->select_chip(mtd, -1);
- if (ret)
- return ret;
-
- chip->select_chip(mtd, chipnr);
- chip->data_interface = saved_data_intf;
- ret = nand_setup_data_interface(chip, chipnr);
- chip->select_chip(mtd, -1);
- if (ret)
- return ret;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(nand_reset);
-
-/**
- * nand_check_erased_buf - check if a buffer contains (almost) only 0xff data
- * @buf: buffer to test
- * @len: buffer length
- * @bitflips_threshold: maximum number of bitflips
- *
- * Check if a buffer contains only 0xff, which means the underlying region
- * has been erased and is ready to be programmed.
- * The bitflips_threshold specify the maximum number of bitflips before
- * considering the region is not erased.
- * Note: The logic of this function has been extracted from the memweight
- * implementation, except that nand_check_erased_buf function exit before
- * testing the whole buffer if the number of bitflips exceed the
- * bitflips_threshold value.
- *
- * Returns a positive number of bitflips less than or equal to
- * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the
- * threshold.
- */
-static int nand_check_erased_buf(void *buf, int len, int bitflips_threshold)
-{
- const unsigned char *bitmap = buf;
- int bitflips = 0;
- int weight;
-
- for (; len && ((uintptr_t)bitmap) % sizeof(long);
- len--, bitmap++) {
- weight = hweight8(*bitmap);
- bitflips += BITS_PER_BYTE - weight;
- if (unlikely(bitflips > bitflips_threshold))
- return -EBADMSG;
- }
-
- for (; len >= sizeof(long);
- len -= sizeof(long), bitmap += sizeof(long)) {
- unsigned long d = *((unsigned long *)bitmap);
- if (d == ~0UL)
- continue;
- weight = hweight_long(d);
- bitflips += BITS_PER_LONG - weight;
- if (unlikely(bitflips > bitflips_threshold))
- return -EBADMSG;
- }
-
- for (; len > 0; len--, bitmap++) {
- weight = hweight8(*bitmap);
- bitflips += BITS_PER_BYTE - weight;
- if (unlikely(bitflips > bitflips_threshold))
- return -EBADMSG;
- }
-
- return bitflips;
-}
-
-/**
- * nand_check_erased_ecc_chunk - check if an ECC chunk contains (almost) only
- * 0xff data
- * @data: data buffer to test
- * @datalen: data length
- * @ecc: ECC buffer
- * @ecclen: ECC length
- * @extraoob: extra OOB buffer
- * @extraooblen: extra OOB length
- * @bitflips_threshold: maximum number of bitflips
- *
- * Check if a data buffer and its associated ECC and OOB data contains only
- * 0xff pattern, which means the underlying region has been erased and is
- * ready to be programmed.
- * The bitflips_threshold specify the maximum number of bitflips before
- * considering the region as not erased.
- *
- * Note:
- * 1/ ECC algorithms are working on pre-defined block sizes which are usually
- * different from the NAND page size. When fixing bitflips, ECC engines will
- * report the number of errors per chunk, and the NAND core infrastructure
- * expect you to return the maximum number of bitflips for the whole page.
- * This is why you should always use this function on a single chunk and
- * not on the whole page. After checking each chunk you should update your
- * max_bitflips value accordingly.
- * 2/ When checking for bitflips in erased pages you should not only check
- * the payload data but also their associated ECC data, because a user might
- * have programmed almost all bits to 1 but a few. In this case, we
- * shouldn't consider the chunk as erased, and checking ECC bytes prevent
- * this case.
- * 3/ The extraoob argument is optional, and should be used if some of your OOB
- * data are protected by the ECC engine.
- * It could also be used if you support subpages and want to attach some
- * extra OOB data to an ECC chunk.
- *
- * Returns a positive number of bitflips less than or equal to
- * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the
- * threshold. In case of success, the passed buffers are filled with 0xff.
- */
-int nand_check_erased_ecc_chunk(void *data, int datalen,
- void *ecc, int ecclen,
- void *extraoob, int extraooblen,
- int bitflips_threshold)
-{
- int data_bitflips = 0, ecc_bitflips = 0, extraoob_bitflips = 0;
-
- data_bitflips = nand_check_erased_buf(data, datalen,
- bitflips_threshold);
- if (data_bitflips < 0)
- return data_bitflips;
-
- bitflips_threshold -= data_bitflips;
-
- ecc_bitflips = nand_check_erased_buf(ecc, ecclen, bitflips_threshold);
- if (ecc_bitflips < 0)
- return ecc_bitflips;
-
- bitflips_threshold -= ecc_bitflips;
-
- extraoob_bitflips = nand_check_erased_buf(extraoob, extraooblen,
- bitflips_threshold);
- if (extraoob_bitflips < 0)
- return extraoob_bitflips;
-
- if (data_bitflips)
- memset(data, 0xff, datalen);
-
- if (ecc_bitflips)
- memset(ecc, 0xff, ecclen);
-
- if (extraoob_bitflips)
- memset(extraoob, 0xff, extraooblen);
-
- return data_bitflips + ecc_bitflips + extraoob_bitflips;
-}
-EXPORT_SYMBOL(nand_check_erased_ecc_chunk);
-
-/**
- * nand_read_page_raw - [INTERN] read raw page data without ecc
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- * @oob_required: caller requires OOB data read to chip->oob_poi
- * @page: page number to read
- *
- * Not for syndrome calculating ECC controllers, which use a special oob layout.
- */
-int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page)
-{
- int ret;
-
- ret = nand_read_page_op(chip, page, 0, buf, mtd->writesize);
- if (ret)
- return ret;
-
- if (oob_required) {
- ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize,
- false);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-EXPORT_SYMBOL(nand_read_page_raw);
-
-/**
- * nand_read_page_raw_syndrome - [INTERN] read raw page data without ecc
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- * @oob_required: caller requires OOB data read to chip->oob_poi
- * @page: page number to read
- *
- * We need a special oob layout and handling even when OOB isn't used.
- */
-static int nand_read_page_raw_syndrome(struct mtd_info *mtd,
- struct nand_chip *chip, uint8_t *buf,
- int oob_required, int page)
-{
- int eccsize = chip->ecc.size;
- int eccbytes = chip->ecc.bytes;
- uint8_t *oob = chip->oob_poi;
- int steps, size, ret;
-
- ret = nand_read_page_op(chip, page, 0, NULL, 0);
- if (ret)
- return ret;
-
- for (steps = chip->ecc.steps; steps > 0; steps--) {
- ret = nand_read_data_op(chip, buf, eccsize, false);
- if (ret)
- return ret;
-
- buf += eccsize;
-
- if (chip->ecc.prepad) {
- ret = nand_read_data_op(chip, oob, chip->ecc.prepad,
- false);
- if (ret)
- return ret;
-
- oob += chip->ecc.prepad;
- }
-
- ret = nand_read_data_op(chip, oob, eccbytes, false);
- if (ret)
- return ret;
-
- oob += eccbytes;
-
- if (chip->ecc.postpad) {
- ret = nand_read_data_op(chip, oob, chip->ecc.postpad,
- false);
- if (ret)
- return ret;
-
- oob += chip->ecc.postpad;
- }
- }
-
- size = mtd->oobsize - (oob - chip->oob_poi);
- if (size) {
- ret = nand_read_data_op(chip, oob, size, false);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-/**
- * nand_read_page_swecc - [REPLACEABLE] software ECC based page read function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- * @oob_required: caller requires OOB data read to chip->oob_poi
- * @page: page number to read
- */
-static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page)
-{
- int i, eccsize = chip->ecc.size, ret;
- int eccbytes = chip->ecc.bytes;
- int eccsteps = chip->ecc.steps;
- uint8_t *p = buf;
- uint8_t *ecc_calc = chip->ecc.calc_buf;
- uint8_t *ecc_code = chip->ecc.code_buf;
- unsigned int max_bitflips = 0;
-
- chip->ecc.read_page_raw(mtd, chip, buf, 1, page);
-
- for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
- chip->ecc.calculate(mtd, p, &ecc_calc[i]);
-
- ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
- chip->ecc.total);
- if (ret)
- return ret;
-
- eccsteps = chip->ecc.steps;
- p = buf;
-
- for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
- int stat;
-
- stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
- if (stat < 0) {
- mtd->ecc_stats.failed++;
- } else {
- mtd->ecc_stats.corrected += stat;
- max_bitflips = max_t(unsigned int, max_bitflips, stat);
- }
- }
- return max_bitflips;
-}
-
-/**
- * nand_read_subpage - [REPLACEABLE] ECC based sub-page read function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @data_offs: offset of requested data within the page
- * @readlen: data length
- * @bufpoi: buffer to store read data
- * @page: page number to read
- */
-static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
- uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi,
- int page)
-{
- int start_step, end_step, num_steps, ret;
- uint8_t *p;
- int data_col_addr, i, gaps = 0;
- int datafrag_len, eccfrag_len, aligned_len, aligned_pos;
- int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;
- int index, section = 0;
- unsigned int max_bitflips = 0;
- struct mtd_oob_region oobregion = { };
-
- /* Column address within the page aligned to ECC size (256bytes) */
- start_step = data_offs / chip->ecc.size;
- end_step = (data_offs + readlen - 1) / chip->ecc.size;
- num_steps = end_step - start_step + 1;
- index = start_step * chip->ecc.bytes;
-
- /* Data size aligned to ECC ecc.size */
- datafrag_len = num_steps * chip->ecc.size;
- eccfrag_len = num_steps * chip->ecc.bytes;
-
- data_col_addr = start_step * chip->ecc.size;
- /* If we read not a page aligned data */
- p = bufpoi + data_col_addr;
- ret = nand_read_page_op(chip, page, data_col_addr, p, datafrag_len);
- if (ret)
- return ret;
-
- /* Calculate ECC */
- for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size)
- chip->ecc.calculate(mtd, p, &chip->ecc.calc_buf[i]);
-
- /*
- * The performance is faster if we position offsets according to
- * ecc.pos. Let's make sure that there are no gaps in ECC positions.
- */
- ret = mtd_ooblayout_find_eccregion(mtd, index, &section, &oobregion);
- if (ret)
- return ret;
-
- if (oobregion.length < eccfrag_len)
- gaps = 1;
-
- if (gaps) {
- ret = nand_change_read_column_op(chip, mtd->writesize,
- chip->oob_poi, mtd->oobsize,
- false);
- if (ret)
- return ret;
- } else {
- /*
- * Send the command to read the particular ECC bytes take care
- * about buswidth alignment in read_buf.
- */
- aligned_pos = oobregion.offset & ~(busw - 1);
- aligned_len = eccfrag_len;
- if (oobregion.offset & (busw - 1))
- aligned_len++;
- if ((oobregion.offset + (num_steps * chip->ecc.bytes)) &
- (busw - 1))
- aligned_len++;
-
- ret = nand_change_read_column_op(chip,
- mtd->writesize + aligned_pos,
- &chip->oob_poi[aligned_pos],
- aligned_len, false);
- if (ret)
- return ret;
- }
-
- ret = mtd_ooblayout_get_eccbytes(mtd, chip->ecc.code_buf,
- chip->oob_poi, index, eccfrag_len);
- if (ret)
- return ret;
-
- p = bufpoi + data_col_addr;
- for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) {
- int stat;
-
- stat = chip->ecc.correct(mtd, p, &chip->ecc.code_buf[i],
- &chip->ecc.calc_buf[i]);
- if (stat == -EBADMSG &&
- (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) {
- /* check for empty pages with bitflips */
- stat = nand_check_erased_ecc_chunk(p, chip->ecc.size,
- &chip->ecc.code_buf[i],
- chip->ecc.bytes,
- NULL, 0,
- chip->ecc.strength);
- }
-
- if (stat < 0) {
- mtd->ecc_stats.failed++;
- } else {
- mtd->ecc_stats.corrected += stat;
- max_bitflips = max_t(unsigned int, max_bitflips, stat);
- }
- }
- return max_bitflips;
-}
-
-/**
- * nand_read_page_hwecc - [REPLACEABLE] hardware ECC based page read function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- * @oob_required: caller requires OOB data read to chip->oob_poi
- * @page: page number to read
- *
- * Not for syndrome calculating ECC controllers which need a special oob layout.
- */
-static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page)
-{
- int i, eccsize = chip->ecc.size, ret;
- int eccbytes = chip->ecc.bytes;
- int eccsteps = chip->ecc.steps;
- uint8_t *p = buf;
- uint8_t *ecc_calc = chip->ecc.calc_buf;
- uint8_t *ecc_code = chip->ecc.code_buf;
- unsigned int max_bitflips = 0;
-
- ret = nand_read_page_op(chip, page, 0, NULL, 0);
- if (ret)
- return ret;
-
- for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
- chip->ecc.hwctl(mtd, NAND_ECC_READ);
-
- ret = nand_read_data_op(chip, p, eccsize, false);
- if (ret)
- return ret;
-
- chip->ecc.calculate(mtd, p, &ecc_calc[i]);
- }
-
- ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, false);
- if (ret)
- return ret;
-
- ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
- chip->ecc.total);
- if (ret)
- return ret;
-
- eccsteps = chip->ecc.steps;
- p = buf;
-
- for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
- int stat;
-
- stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
- if (stat == -EBADMSG &&
- (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) {
- /* check for empty pages with bitflips */
- stat = nand_check_erased_ecc_chunk(p, eccsize,
- &ecc_code[i], eccbytes,
- NULL, 0,
- chip->ecc.strength);
- }
-
- if (stat < 0) {
- mtd->ecc_stats.failed++;
- } else {
- mtd->ecc_stats.corrected += stat;
- max_bitflips = max_t(unsigned int, max_bitflips, stat);
- }
- }
- return max_bitflips;
-}
-
-/**
- * nand_read_page_hwecc_oob_first - [REPLACEABLE] hw ecc, read oob first
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- * @oob_required: caller requires OOB data read to chip->oob_poi
- * @page: page number to read
- *
- * Hardware ECC for large page chips, require OOB to be read first. For this
- * ECC mode, the write_page method is re-used from ECC_HW. These methods
- * read/write ECC from the OOB area, unlike the ECC_HW_SYNDROME support with
- * multiple ECC steps, follows the "infix ECC" scheme and reads/writes ECC from
- * the data area, by overwriting the NAND manufacturer bad block markings.
- */
-static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
- struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
-{
- int i, eccsize = chip->ecc.size, ret;
- int eccbytes = chip->ecc.bytes;
- int eccsteps = chip->ecc.steps;
- uint8_t *p = buf;
- uint8_t *ecc_code = chip->ecc.code_buf;
- uint8_t *ecc_calc = chip->ecc.calc_buf;
- unsigned int max_bitflips = 0;
-
- /* Read the OOB area first */
- ret = nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize);
- if (ret)
- return ret;
-
- ret = nand_read_page_op(chip, page, 0, NULL, 0);
- if (ret)
- return ret;
-
- ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
- chip->ecc.total);
- if (ret)
- return ret;
-
- for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
- int stat;
-
- chip->ecc.hwctl(mtd, NAND_ECC_READ);
-
- ret = nand_read_data_op(chip, p, eccsize, false);
- if (ret)
- return ret;
-
- chip->ecc.calculate(mtd, p, &ecc_calc[i]);
-
- stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL);
- if (stat == -EBADMSG &&
- (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) {
- /* check for empty pages with bitflips */
- stat = nand_check_erased_ecc_chunk(p, eccsize,
- &ecc_code[i], eccbytes,
- NULL, 0,
- chip->ecc.strength);
- }
-
- if (stat < 0) {
- mtd->ecc_stats.failed++;
- } else {
- mtd->ecc_stats.corrected += stat;
- max_bitflips = max_t(unsigned int, max_bitflips, stat);
- }
- }
- return max_bitflips;
-}
-
-/**
- * nand_read_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page read
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- * @oob_required: caller requires OOB data read to chip->oob_poi
- * @page: page number to read
- *
- * The hw generator calculates the error syndrome automatically. Therefore we
- * need a special oob layout and handling.
- */
-static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page)
-{
- int ret, i, eccsize = chip->ecc.size;
- int eccbytes = chip->ecc.bytes;
- int eccsteps = chip->ecc.steps;
- int eccpadbytes = eccbytes + chip->ecc.prepad + chip->ecc.postpad;
- uint8_t *p = buf;
- uint8_t *oob = chip->oob_poi;
- unsigned int max_bitflips = 0;
-
- ret = nand_read_page_op(chip, page, 0, NULL, 0);
- if (ret)
- return ret;
-
- for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
- int stat;
-
- chip->ecc.hwctl(mtd, NAND_ECC_READ);
-
- ret = nand_read_data_op(chip, p, eccsize, false);
- if (ret)
- return ret;
-
- if (chip->ecc.prepad) {
- ret = nand_read_data_op(chip, oob, chip->ecc.prepad,
- false);
- if (ret)
- return ret;
-
- oob += chip->ecc.prepad;
- }
-
- chip->ecc.hwctl(mtd, NAND_ECC_READSYN);
-
- ret = nand_read_data_op(chip, oob, eccbytes, false);
- if (ret)
- return ret;
-
- stat = chip->ecc.correct(mtd, p, oob, NULL);
-
- oob += eccbytes;
-
- if (chip->ecc.postpad) {
- ret = nand_read_data_op(chip, oob, chip->ecc.postpad,
- false);
- if (ret)
- return ret;
-
- oob += chip->ecc.postpad;
- }
-
- if (stat == -EBADMSG &&
- (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) {
- /* check for empty pages with bitflips */
- stat = nand_check_erased_ecc_chunk(p, chip->ecc.size,
- oob - eccpadbytes,
- eccpadbytes,
- NULL, 0,
- chip->ecc.strength);
- }
-
- if (stat < 0) {
- mtd->ecc_stats.failed++;
- } else {
- mtd->ecc_stats.corrected += stat;
- max_bitflips = max_t(unsigned int, max_bitflips, stat);
- }
- }
-
- /* Calculate remaining oob bytes */
- i = mtd->oobsize - (oob - chip->oob_poi);
- if (i) {
- ret = nand_read_data_op(chip, oob, i, false);
- if (ret)
- return ret;
- }
-
- return max_bitflips;
-}
-
-/**
- * nand_transfer_oob - [INTERN] Transfer oob to client buffer
- * @mtd: mtd info structure
- * @oob: oob destination address
- * @ops: oob ops structure
- * @len: size of oob to transfer
- */
-static uint8_t *nand_transfer_oob(struct mtd_info *mtd, uint8_t *oob,
- struct mtd_oob_ops *ops, size_t len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- int ret;
-
- switch (ops->mode) {
-
- case MTD_OPS_PLACE_OOB:
- case MTD_OPS_RAW:
- memcpy(oob, chip->oob_poi + ops->ooboffs, len);
- return oob + len;
-
- case MTD_OPS_AUTO_OOB:
- ret = mtd_ooblayout_get_databytes(mtd, oob, chip->oob_poi,
- ops->ooboffs, len);
- BUG_ON(ret);
- return oob + len;
-
- default:
- BUG();
- }
- return NULL;
-}
-
-/**
- * nand_setup_read_retry - [INTERN] Set the READ RETRY mode
- * @mtd: MTD device structure
- * @retry_mode: the retry mode to use
- *
- * Some vendors supply a special command to shift the Vt threshold, to be used
- * when there are too many bitflips in a page (i.e., ECC error). After setting
- * a new threshold, the host should retry reading the page.
- */
-static int nand_setup_read_retry(struct mtd_info *mtd, int retry_mode)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- pr_debug("setting READ RETRY mode %d\n", retry_mode);
-
- if (retry_mode >= chip->read_retries)
- return -EINVAL;
-
- if (!chip->setup_read_retry)
- return -EOPNOTSUPP;
-
- return chip->setup_read_retry(mtd, retry_mode);
-}
-
-/**
- * nand_do_read_ops - [INTERN] Read data with ECC
- * @mtd: MTD device structure
- * @from: offset to read from
- * @ops: oob ops structure
- *
- * Internal function. Called with chip held.
- */
-static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
- struct mtd_oob_ops *ops)
-{
- int chipnr, page, realpage, col, bytes, aligned, oob_required;
- struct nand_chip *chip = mtd_to_nand(mtd);
- int ret = 0;
- uint32_t readlen = ops->len;
- uint32_t oobreadlen = ops->ooblen;
- uint32_t max_oobsize = mtd_oobavail(mtd, ops);
-
- uint8_t *bufpoi, *oob, *buf;
- int use_bufpoi;
- unsigned int max_bitflips = 0;
- int retry_mode = 0;
- bool ecc_fail = false;
-
- chipnr = (int)(from >> chip->chip_shift);
- chip->select_chip(mtd, chipnr);
-
- realpage = (int)(from >> chip->page_shift);
- page = realpage & chip->pagemask;
-
- col = (int)(from & (mtd->writesize - 1));
-
- buf = ops->datbuf;
- oob = ops->oobbuf;
- oob_required = oob ? 1 : 0;
-
- while (1) {
- unsigned int ecc_failures = mtd->ecc_stats.failed;
-
- bytes = min(mtd->writesize - col, readlen);
- aligned = (bytes == mtd->writesize);
-
- if (!aligned)
- use_bufpoi = 1;
- else if (chip->options & NAND_USE_BOUNCE_BUFFER)
- use_bufpoi = !virt_addr_valid(buf) ||
- !IS_ALIGNED((unsigned long)buf,
- chip->buf_align);
- else
- use_bufpoi = 0;
-
- /* Is the current page in the buffer? */
- if (realpage != chip->pagebuf || oob) {
- bufpoi = use_bufpoi ? chip->data_buf : buf;
-
- if (use_bufpoi && aligned)
- pr_debug("%s: using read bounce buffer for buf@%p\n",
- __func__, buf);
-
-read_retry:
- /*
- * Now read the page into the buffer. Absent an error,
- * the read methods return max bitflips per ecc step.
- */
- if (unlikely(ops->mode == MTD_OPS_RAW))
- ret = chip->ecc.read_page_raw(mtd, chip, bufpoi,
- oob_required,
- page);
- else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) &&
- !oob)
- ret = chip->ecc.read_subpage(mtd, chip,
- col, bytes, bufpoi,
- page);
- else
- ret = chip->ecc.read_page(mtd, chip, bufpoi,
- oob_required, page);
- if (ret < 0) {
- if (use_bufpoi)
- /* Invalidate page cache */
- chip->pagebuf = -1;
- break;
- }
-
- /* Transfer not aligned data */
- if (use_bufpoi) {
- if (!NAND_HAS_SUBPAGE_READ(chip) && !oob &&
- !(mtd->ecc_stats.failed - ecc_failures) &&
- (ops->mode != MTD_OPS_RAW)) {
- chip->pagebuf = realpage;
- chip->pagebuf_bitflips = ret;
- } else {
- /* Invalidate page cache */
- chip->pagebuf = -1;
- }
- memcpy(buf, chip->data_buf + col, bytes);
- }
-
- if (unlikely(oob)) {
- int toread = min(oobreadlen, max_oobsize);
-
- if (toread) {
- oob = nand_transfer_oob(mtd,
- oob, ops, toread);
- oobreadlen -= toread;
- }
- }
-
- if (chip->options & NAND_NEED_READRDY) {
- /* Apply delay or wait for ready/busy pin */
- if (!chip->dev_ready)
- udelay(chip->chip_delay);
- else
- nand_wait_ready(mtd);
- }
-
- if (mtd->ecc_stats.failed - ecc_failures) {
- if (retry_mode + 1 < chip->read_retries) {
- retry_mode++;
- ret = nand_setup_read_retry(mtd,
- retry_mode);
- if (ret < 0)
- break;
-
- /* Reset failures; retry */
- mtd->ecc_stats.failed = ecc_failures;
- goto read_retry;
- } else {
- /* No more retry modes; real failure */
- ecc_fail = true;
- }
- }
-
- buf += bytes;
- max_bitflips = max_t(unsigned int, max_bitflips, ret);
- } else {
- memcpy(buf, chip->data_buf + col, bytes);
- buf += bytes;
- max_bitflips = max_t(unsigned int, max_bitflips,
- chip->pagebuf_bitflips);
- }
-
- readlen -= bytes;
-
- /* Reset to retry mode 0 */
- if (retry_mode) {
- ret = nand_setup_read_retry(mtd, 0);
- if (ret < 0)
- break;
- retry_mode = 0;
- }
-
- if (!readlen)
- break;
-
- /* For subsequent reads align to page boundary */
- col = 0;
- /* Increment page address */
- realpage++;
-
- page = realpage & chip->pagemask;
- /* Check, if we cross a chip boundary */
- if (!page) {
- chipnr++;
- chip->select_chip(mtd, -1);
- chip->select_chip(mtd, chipnr);
- }
- }
- chip->select_chip(mtd, -1);
-
- ops->retlen = ops->len - (size_t) readlen;
- if (oob)
- ops->oobretlen = ops->ooblen - oobreadlen;
-
- if (ret < 0)
- return ret;
-
- if (ecc_fail)
- return -EBADMSG;
-
- return max_bitflips;
-}
-
-/**
- * nand_read_oob_std - [REPLACEABLE] the most common OOB data read function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @page: page number to read
- */
-int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page)
-{
- return nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize);
-}
-EXPORT_SYMBOL(nand_read_oob_std);
-
-/**
- * nand_read_oob_syndrome - [REPLACEABLE] OOB data read function for HW ECC
- * with syndromes
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @page: page number to read
- */
-int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
- int page)
-{
- int length = mtd->oobsize;
- int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
- int eccsize = chip->ecc.size;
- uint8_t *bufpoi = chip->oob_poi;
- int i, toread, sndrnd = 0, pos, ret;
-
- ret = nand_read_page_op(chip, page, chip->ecc.size, NULL, 0);
- if (ret)
- return ret;
-
- for (i = 0; i < chip->ecc.steps; i++) {
- if (sndrnd) {
- int ret;
-
- pos = eccsize + i * (eccsize + chunk);
- if (mtd->writesize > 512)
- ret = nand_change_read_column_op(chip, pos,
- NULL, 0,
- false);
- else
- ret = nand_read_page_op(chip, page, pos, NULL,
- 0);
-
- if (ret)
- return ret;
- } else
- sndrnd = 1;
- toread = min_t(int, length, chunk);
-
- ret = nand_read_data_op(chip, bufpoi, toread, false);
- if (ret)
- return ret;
-
- bufpoi += toread;
- length -= toread;
- }
- if (length > 0) {
- ret = nand_read_data_op(chip, bufpoi, length, false);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-EXPORT_SYMBOL(nand_read_oob_syndrome);
-
-/**
- * nand_write_oob_std - [REPLACEABLE] the most common OOB data write function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @page: page number to write
- */
-int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page)
-{
- return nand_prog_page_op(chip, page, mtd->writesize, chip->oob_poi,
- mtd->oobsize);
-}
-EXPORT_SYMBOL(nand_write_oob_std);
-
-/**
- * nand_write_oob_syndrome - [REPLACEABLE] OOB data write function for HW ECC
- * with syndrome - only for large page flash
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @page: page number to write
- */
-int nand_write_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
- int page)
-{
- int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
- int eccsize = chip->ecc.size, length = mtd->oobsize;
- int ret, i, len, pos, sndcmd = 0, steps = chip->ecc.steps;
- const uint8_t *bufpoi = chip->oob_poi;
-
- /*
- * data-ecc-data-ecc ... ecc-oob
- * or
- * data-pad-ecc-pad-data-pad .... ecc-pad-oob
- */
- if (!chip->ecc.prepad && !chip->ecc.postpad) {
- pos = steps * (eccsize + chunk);
- steps = 0;
- } else
- pos = eccsize;
-
- ret = nand_prog_page_begin_op(chip, page, pos, NULL, 0);
- if (ret)
- return ret;
-
- for (i = 0; i < steps; i++) {
- if (sndcmd) {
- if (mtd->writesize <= 512) {
- uint32_t fill = 0xFFFFFFFF;
-
- len = eccsize;
- while (len > 0) {
- int num = min_t(int, len, 4);
-
- ret = nand_write_data_op(chip, &fill,
- num, false);
- if (ret)
- return ret;
-
- len -= num;
- }
- } else {
- pos = eccsize + i * (eccsize + chunk);
- ret = nand_change_write_column_op(chip, pos,
- NULL, 0,
- false);
- if (ret)
- return ret;
- }
- } else
- sndcmd = 1;
- len = min_t(int, length, chunk);
-
- ret = nand_write_data_op(chip, bufpoi, len, false);
- if (ret)
- return ret;
-
- bufpoi += len;
- length -= len;
- }
- if (length > 0) {
- ret = nand_write_data_op(chip, bufpoi, length, false);
- if (ret)
- return ret;
- }
-
- return nand_prog_page_end_op(chip);
-}
-EXPORT_SYMBOL(nand_write_oob_syndrome);
-
-/**
- * nand_do_read_oob - [INTERN] NAND read out-of-band
- * @mtd: MTD device structure
- * @from: offset to read from
- * @ops: oob operations description structure
- *
- * NAND read out-of-band data from the spare area.
- */
-static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
- struct mtd_oob_ops *ops)
-{
- unsigned int max_bitflips = 0;
- int page, realpage, chipnr;
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct mtd_ecc_stats stats;
- int readlen = ops->ooblen;
- int len;
- uint8_t *buf = ops->oobbuf;
- int ret = 0;
-
- pr_debug("%s: from = 0x%08Lx, len = %i\n",
- __func__, (unsigned long long)from, readlen);
-
- stats = mtd->ecc_stats;
-
- len = mtd_oobavail(mtd, ops);
-
- chipnr = (int)(from >> chip->chip_shift);
- chip->select_chip(mtd, chipnr);
-
- /* Shift to get page */
- realpage = (int)(from >> chip->page_shift);
- page = realpage & chip->pagemask;
-
- while (1) {
- if (ops->mode == MTD_OPS_RAW)
- ret = chip->ecc.read_oob_raw(mtd, chip, page);
- else
- ret = chip->ecc.read_oob(mtd, chip, page);
-
- if (ret < 0)
- break;
-
- len = min(len, readlen);
- buf = nand_transfer_oob(mtd, buf, ops, len);
-
- if (chip->options & NAND_NEED_READRDY) {
- /* Apply delay or wait for ready/busy pin */
- if (!chip->dev_ready)
- udelay(chip->chip_delay);
- else
- nand_wait_ready(mtd);
- }
-
- max_bitflips = max_t(unsigned int, max_bitflips, ret);
-
- readlen -= len;
- if (!readlen)
- break;
-
- /* Increment page address */
- realpage++;
-
- page = realpage & chip->pagemask;
- /* Check, if we cross a chip boundary */
- if (!page) {
- chipnr++;
- chip->select_chip(mtd, -1);
- chip->select_chip(mtd, chipnr);
- }
- }
- chip->select_chip(mtd, -1);
-
- ops->oobretlen = ops->ooblen - readlen;
-
- if (ret < 0)
- return ret;
-
- if (mtd->ecc_stats.failed - stats.failed)
- return -EBADMSG;
-
- return max_bitflips;
-}
-
-/**
- * nand_read_oob - [MTD Interface] NAND read data and/or out-of-band
- * @mtd: MTD device structure
- * @from: offset to read from
- * @ops: oob operation description structure
- *
- * NAND read data and/or out-of-band data.
- */
-static int nand_read_oob(struct mtd_info *mtd, loff_t from,
- struct mtd_oob_ops *ops)
-{
- int ret;
-
- ops->retlen = 0;
-
- if (ops->mode != MTD_OPS_PLACE_OOB &&
- ops->mode != MTD_OPS_AUTO_OOB &&
- ops->mode != MTD_OPS_RAW)
- return -ENOTSUPP;
-
- nand_get_device(mtd, FL_READING);
-
- if (!ops->datbuf)
- ret = nand_do_read_oob(mtd, from, ops);
- else
- ret = nand_do_read_ops(mtd, from, ops);
-
- nand_release_device(mtd);
- return ret;
-}
-
-
-/**
- * nand_write_page_raw - [INTERN] raw page write function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: data buffer
- * @oob_required: must write chip->oob_poi to OOB
- * @page: page number to write
- *
- * Not for syndrome calculating ECC controllers, which use a special oob layout.
- */
-int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf, int oob_required, int page)
-{
- int ret;
-
- ret = nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);
- if (ret)
- return ret;
-
- if (oob_required) {
- ret = nand_write_data_op(chip, chip->oob_poi, mtd->oobsize,
- false);
- if (ret)
- return ret;
- }
-
- return nand_prog_page_end_op(chip);
-}
-EXPORT_SYMBOL(nand_write_page_raw);
-
-/**
- * nand_write_page_raw_syndrome - [INTERN] raw page write function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: data buffer
- * @oob_required: must write chip->oob_poi to OOB
- * @page: page number to write
- *
- * We need a special oob layout and handling even when ECC isn't checked.
- */
-static int nand_write_page_raw_syndrome(struct mtd_info *mtd,
- struct nand_chip *chip,
- const uint8_t *buf, int oob_required,
- int page)
-{
- int eccsize = chip->ecc.size;
- int eccbytes = chip->ecc.bytes;
- uint8_t *oob = chip->oob_poi;
- int steps, size, ret;
-
- ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
- if (ret)
- return ret;
-
- for (steps = chip->ecc.steps; steps > 0; steps--) {
- ret = nand_write_data_op(chip, buf, eccsize, false);
- if (ret)
- return ret;
-
- buf += eccsize;
-
- if (chip->ecc.prepad) {
- ret = nand_write_data_op(chip, oob, chip->ecc.prepad,
- false);
- if (ret)
- return ret;
-
- oob += chip->ecc.prepad;
- }
-
- ret = nand_write_data_op(chip, oob, eccbytes, false);
- if (ret)
- return ret;
-
- oob += eccbytes;
-
- if (chip->ecc.postpad) {
- ret = nand_write_data_op(chip, oob, chip->ecc.postpad,
- false);
- if (ret)
- return ret;
-
- oob += chip->ecc.postpad;
- }
- }
-
- size = mtd->oobsize - (oob - chip->oob_poi);
- if (size) {
- ret = nand_write_data_op(chip, oob, size, false);
- if (ret)
- return ret;
- }
-
- return nand_prog_page_end_op(chip);
-}
-/**
- * nand_write_page_swecc - [REPLACEABLE] software ECC based page write function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: data buffer
- * @oob_required: must write chip->oob_poi to OOB
- * @page: page number to write
- */
-static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf, int oob_required,
- int page)
-{
- int i, eccsize = chip->ecc.size, ret;
- int eccbytes = chip->ecc.bytes;
- int eccsteps = chip->ecc.steps;
- uint8_t *ecc_calc = chip->ecc.calc_buf;
- const uint8_t *p = buf;
-
- /* Software ECC calculation */
- for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
- chip->ecc.calculate(mtd, p, &ecc_calc[i]);
-
- ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
- chip->ecc.total);
- if (ret)
- return ret;
-
- return chip->ecc.write_page_raw(mtd, chip, buf, 1, page);
-}
-
-/**
- * nand_write_page_hwecc - [REPLACEABLE] hardware ECC based page write function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: data buffer
- * @oob_required: must write chip->oob_poi to OOB
- * @page: page number to write
- */
-static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf, int oob_required,
- int page)
-{
- int i, eccsize = chip->ecc.size, ret;
- int eccbytes = chip->ecc.bytes;
- int eccsteps = chip->ecc.steps;
- uint8_t *ecc_calc = chip->ecc.calc_buf;
- const uint8_t *p = buf;
-
- ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
- if (ret)
- return ret;
-
- for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
- chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
-
- ret = nand_write_data_op(chip, p, eccsize, false);
- if (ret)
- return ret;
-
- chip->ecc.calculate(mtd, p, &ecc_calc[i]);
- }
-
- ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
- chip->ecc.total);
- if (ret)
- return ret;
-
- ret = nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, false);
- if (ret)
- return ret;
-
- return nand_prog_page_end_op(chip);
-}
-
-
-/**
- * nand_write_subpage_hwecc - [REPLACEABLE] hardware ECC based subpage write
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @offset: column address of subpage within the page
- * @data_len: data length
- * @buf: data buffer
- * @oob_required: must write chip->oob_poi to OOB
- * @page: page number to write
- */
-static int nand_write_subpage_hwecc(struct mtd_info *mtd,
- struct nand_chip *chip, uint32_t offset,
- uint32_t data_len, const uint8_t *buf,
- int oob_required, int page)
-{
- uint8_t *oob_buf = chip->oob_poi;
- uint8_t *ecc_calc = chip->ecc.calc_buf;
- int ecc_size = chip->ecc.size;
- int ecc_bytes = chip->ecc.bytes;
- int ecc_steps = chip->ecc.steps;
- uint32_t start_step = offset / ecc_size;
- uint32_t end_step = (offset + data_len - 1) / ecc_size;
- int oob_bytes = mtd->oobsize / ecc_steps;
- int step, ret;
-
- ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
- if (ret)
- return ret;
-
- for (step = 0; step < ecc_steps; step++) {
- /* configure controller for WRITE access */
- chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
-
- /* write data (untouched subpages already masked by 0xFF) */
- ret = nand_write_data_op(chip, buf, ecc_size, false);
- if (ret)
- return ret;
-
- /* mask ECC of un-touched subpages by padding 0xFF */
- if ((step < start_step) || (step > end_step))
- memset(ecc_calc, 0xff, ecc_bytes);
- else
- chip->ecc.calculate(mtd, buf, ecc_calc);
-
- /* mask OOB of un-touched subpages by padding 0xFF */
- /* if oob_required, preserve OOB metadata of written subpage */
- if (!oob_required || (step < start_step) || (step > end_step))
- memset(oob_buf, 0xff, oob_bytes);
-
- buf += ecc_size;
- ecc_calc += ecc_bytes;
- oob_buf += oob_bytes;
- }
-
- /* copy calculated ECC for whole page to chip->buffer->oob */
- /* this include masked-value(0xFF) for unwritten subpages */
- ecc_calc = chip->ecc.calc_buf;
- ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
- chip->ecc.total);
- if (ret)
- return ret;
-
- /* write OOB buffer to NAND device */
- ret = nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, false);
- if (ret)
- return ret;
-
- return nand_prog_page_end_op(chip);
-}
-
-
-/**
- * nand_write_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page write
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: data buffer
- * @oob_required: must write chip->oob_poi to OOB
- * @page: page number to write
- *
- * The hw generator calculates the error syndrome automatically. Therefore we
- * need a special oob layout and handling.
- */
-static int nand_write_page_syndrome(struct mtd_info *mtd,
- struct nand_chip *chip,
- const uint8_t *buf, int oob_required,
- int page)
-{
- int i, eccsize = chip->ecc.size;
- int eccbytes = chip->ecc.bytes;
- int eccsteps = chip->ecc.steps;
- const uint8_t *p = buf;
- uint8_t *oob = chip->oob_poi;
- int ret;
-
- ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
- if (ret)
- return ret;
-
- for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
- chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
-
- ret = nand_write_data_op(chip, p, eccsize, false);
- if (ret)
- return ret;
-
- if (chip->ecc.prepad) {
- ret = nand_write_data_op(chip, oob, chip->ecc.prepad,
- false);
- if (ret)
- return ret;
-
- oob += chip->ecc.prepad;
- }
-
- chip->ecc.calculate(mtd, p, oob);
-
- ret = nand_write_data_op(chip, oob, eccbytes, false);
- if (ret)
- return ret;
-
- oob += eccbytes;
-
- if (chip->ecc.postpad) {
- ret = nand_write_data_op(chip, oob, chip->ecc.postpad,
- false);
- if (ret)
- return ret;
-
- oob += chip->ecc.postpad;
- }
- }
-
- /* Calculate remaining oob bytes */
- i = mtd->oobsize - (oob - chip->oob_poi);
- if (i) {
- ret = nand_write_data_op(chip, oob, i, false);
- if (ret)
- return ret;
- }
-
- return nand_prog_page_end_op(chip);
-}
-
-/**
- * nand_write_page - write one page
- * @mtd: MTD device structure
- * @chip: NAND chip descriptor
- * @offset: address offset within the page
- * @data_len: length of actual data to be written
- * @buf: the data to write
- * @oob_required: must write chip->oob_poi to OOB
- * @page: page number to write
- * @raw: use _raw version of write_page
- */
-static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
- uint32_t offset, int data_len, const uint8_t *buf,
- int oob_required, int page, int raw)
-{
- int status, subpage;
-
- if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&
- chip->ecc.write_subpage)
- subpage = offset || (data_len < mtd->writesize);
- else
- subpage = 0;
-
- if (unlikely(raw))
- status = chip->ecc.write_page_raw(mtd, chip, buf,
- oob_required, page);
- else if (subpage)
- status = chip->ecc.write_subpage(mtd, chip, offset, data_len,
- buf, oob_required, page);
- else
- status = chip->ecc.write_page(mtd, chip, buf, oob_required,
- page);
-
- if (status < 0)
- return status;
-
- return 0;
-}
-
-/**
- * nand_fill_oob - [INTERN] Transfer client buffer to oob
- * @mtd: MTD device structure
- * @oob: oob data buffer
- * @len: oob data write length
- * @ops: oob ops structure
- */
-static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len,
- struct mtd_oob_ops *ops)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- int ret;
-
- /*
- * Initialise to all 0xFF, to avoid the possibility of left over OOB
- * data from a previous OOB read.
- */
- memset(chip->oob_poi, 0xff, mtd->oobsize);
-
- switch (ops->mode) {
-
- case MTD_OPS_PLACE_OOB:
- case MTD_OPS_RAW:
- memcpy(chip->oob_poi + ops->ooboffs, oob, len);
- return oob + len;
-
- case MTD_OPS_AUTO_OOB:
- ret = mtd_ooblayout_set_databytes(mtd, oob, chip->oob_poi,
- ops->ooboffs, len);
- BUG_ON(ret);
- return oob + len;
-
- default:
- BUG();
- }
- return NULL;
-}
-
-#define NOTALIGNED(x) ((x & (chip->subpagesize - 1)) != 0)
-
-/**
- * nand_do_write_ops - [INTERN] NAND write with ECC
- * @mtd: MTD device structure
- * @to: offset to write to
- * @ops: oob operations description structure
- *
- * NAND write with ECC.
- */
-static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
- struct mtd_oob_ops *ops)
-{
- int chipnr, realpage, page, column;
- struct nand_chip *chip = mtd_to_nand(mtd);
- uint32_t writelen = ops->len;
-
- uint32_t oobwritelen = ops->ooblen;
- uint32_t oobmaxlen = mtd_oobavail(mtd, ops);
-
- uint8_t *oob = ops->oobbuf;
- uint8_t *buf = ops->datbuf;
- int ret;
- int oob_required = oob ? 1 : 0;
-
- ops->retlen = 0;
- if (!writelen)
- return 0;
-
- /* Reject writes, which are not page aligned */
- if (NOTALIGNED(to) || NOTALIGNED(ops->len)) {
- pr_notice("%s: attempt to write non page aligned data\n",
- __func__);
- return -EINVAL;
- }
-
- column = to & (mtd->writesize - 1);
-
- chipnr = (int)(to >> chip->chip_shift);
- chip->select_chip(mtd, chipnr);
-
- /* Check, if it is write protected */
- if (nand_check_wp(mtd)) {
- ret = -EIO;
- goto err_out;
- }
-
- realpage = (int)(to >> chip->page_shift);
- page = realpage & chip->pagemask;
-
- /* Invalidate the page cache, when we write to the cached page */
- if (to <= ((loff_t)chip->pagebuf << chip->page_shift) &&
- ((loff_t)chip->pagebuf << chip->page_shift) < (to + ops->len))
- chip->pagebuf = -1;
-
- /* Don't allow multipage oob writes with offset */
- if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen)) {
- ret = -EINVAL;
- goto err_out;
- }
-
- while (1) {
- int bytes = mtd->writesize;
- uint8_t *wbuf = buf;
- int use_bufpoi;
- int part_pagewr = (column || writelen < mtd->writesize);
-
- if (part_pagewr)
- use_bufpoi = 1;
- else if (chip->options & NAND_USE_BOUNCE_BUFFER)
- use_bufpoi = !virt_addr_valid(buf) ||
- !IS_ALIGNED((unsigned long)buf,
- chip->buf_align);
- else
- use_bufpoi = 0;
-
- /* Partial page write?, or need to use bounce buffer */
- if (use_bufpoi) {
- pr_debug("%s: using write bounce buffer for buf@%p\n",
- __func__, buf);
- if (part_pagewr)
- bytes = min_t(int, bytes - column, writelen);
- chip->pagebuf = -1;
- memset(chip->data_buf, 0xff, mtd->writesize);
- memcpy(&chip->data_buf[column], buf, bytes);
- wbuf = chip->data_buf;
- }
-
- if (unlikely(oob)) {
- size_t len = min(oobwritelen, oobmaxlen);
- oob = nand_fill_oob(mtd, oob, len, ops);
- oobwritelen -= len;
- } else {
- /* We still need to erase leftover OOB data */
- memset(chip->oob_poi, 0xff, mtd->oobsize);
- }
-
- ret = nand_write_page(mtd, chip, column, bytes, wbuf,
- oob_required, page,
- (ops->mode == MTD_OPS_RAW));
- if (ret)
- break;
-
- writelen -= bytes;
- if (!writelen)
- break;
-
- column = 0;
- buf += bytes;
- realpage++;
-
- page = realpage & chip->pagemask;
- /* Check, if we cross a chip boundary */
- if (!page) {
- chipnr++;
- chip->select_chip(mtd, -1);
- chip->select_chip(mtd, chipnr);
- }
- }
-
- ops->retlen = ops->len - writelen;
- if (unlikely(oob))
- ops->oobretlen = ops->ooblen;
-
-err_out:
- chip->select_chip(mtd, -1);
- return ret;
-}
-
-/**
- * panic_nand_write - [MTD Interface] NAND write with ECC
- * @mtd: MTD device structure
- * @to: offset to write to
- * @len: number of bytes to write
- * @retlen: pointer to variable to store the number of written bytes
- * @buf: the data to write
- *
- * NAND write with ECC. Used when performing writes in interrupt context, this
- * may for example be called by mtdoops when writing an oops while in panic.
- */
-static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
- size_t *retlen, const uint8_t *buf)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- int chipnr = (int)(to >> chip->chip_shift);
- struct mtd_oob_ops ops;
- int ret;
-
- /* Grab the device */
- panic_nand_get_device(chip, mtd, FL_WRITING);
-
- chip->select_chip(mtd, chipnr);
-
- /* Wait for the device to get ready */
- panic_nand_wait(mtd, chip, 400);
-
- memset(&ops, 0, sizeof(ops));
- ops.len = len;
- ops.datbuf = (uint8_t *)buf;
- ops.mode = MTD_OPS_PLACE_OOB;
-
- ret = nand_do_write_ops(mtd, to, &ops);
-
- *retlen = ops.retlen;
- return ret;
-}
-
-/**
- * nand_do_write_oob - [MTD Interface] NAND write out-of-band
- * @mtd: MTD device structure
- * @to: offset to write to
- * @ops: oob operation description structure
- *
- * NAND write out-of-band.
- */
-static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
- struct mtd_oob_ops *ops)
-{
- int chipnr, page, status, len;
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- pr_debug("%s: to = 0x%08x, len = %i\n",
- __func__, (unsigned int)to, (int)ops->ooblen);
-
- len = mtd_oobavail(mtd, ops);
-
- /* Do not allow write past end of page */
- if ((ops->ooboffs + ops->ooblen) > len) {
- pr_debug("%s: attempt to write past end of page\n",
- __func__);
- return -EINVAL;
- }
-
- chipnr = (int)(to >> chip->chip_shift);
-
- /*
- * Reset the chip. Some chips (like the Toshiba TC5832DC found in one
- * of my DiskOnChip 2000 test units) will clear the whole data page too
- * if we don't do this. I have no clue why, but I seem to have 'fixed'
- * it in the doc2000 driver in August 1999. dwmw2.
- */
- nand_reset(chip, chipnr);
-
- chip->select_chip(mtd, chipnr);
-
- /* Shift to get page */
- page = (int)(to >> chip->page_shift);
-
- /* Check, if it is write protected */
- if (nand_check_wp(mtd)) {
- chip->select_chip(mtd, -1);
- return -EROFS;
- }
-
- /* Invalidate the page cache, if we write to the cached page */
- if (page == chip->pagebuf)
- chip->pagebuf = -1;
-
- nand_fill_oob(mtd, ops->oobbuf, ops->ooblen, ops);
-
- if (ops->mode == MTD_OPS_RAW)
- status = chip->ecc.write_oob_raw(mtd, chip, page & chip->pagemask);
- else
- status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
-
- chip->select_chip(mtd, -1);
-
- if (status)
- return status;
-
- ops->oobretlen = ops->ooblen;
-
- return 0;
-}
-
-/**
- * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band
- * @mtd: MTD device structure
- * @to: offset to write to
- * @ops: oob operation description structure
- */
-static int nand_write_oob(struct mtd_info *mtd, loff_t to,
- struct mtd_oob_ops *ops)
-{
- int ret = -ENOTSUPP;
-
- ops->retlen = 0;
-
- nand_get_device(mtd, FL_WRITING);
-
- switch (ops->mode) {
- case MTD_OPS_PLACE_OOB:
- case MTD_OPS_AUTO_OOB:
- case MTD_OPS_RAW:
- break;
-
- default:
- goto out;
- }
-
- if (!ops->datbuf)
- ret = nand_do_write_oob(mtd, to, ops);
- else
- ret = nand_do_write_ops(mtd, to, ops);
-
-out:
- nand_release_device(mtd);
- return ret;
-}
-
-/**
- * single_erase - [GENERIC] NAND standard block erase command function
- * @mtd: MTD device structure
- * @page: the page address of the block which will be erased
- *
- * Standard erase command for NAND chips. Returns NAND status.
- */
-static int single_erase(struct mtd_info *mtd, int page)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- unsigned int eraseblock;
-
- /* Send commands to erase a block */
- eraseblock = page >> (chip->phys_erase_shift - chip->page_shift);
-
- return nand_erase_op(chip, eraseblock);
-}
-
-/**
- * nand_erase - [MTD Interface] erase block(s)
- * @mtd: MTD device structure
- * @instr: erase instruction
- *
- * Erase one ore more blocks.
- */
-static int nand_erase(struct mtd_info *mtd, struct erase_info *instr)
-{
- return nand_erase_nand(mtd, instr, 0);
-}
-
-/**
- * nand_erase_nand - [INTERN] erase block(s)
- * @mtd: MTD device structure
- * @instr: erase instruction
- * @allowbbt: allow erasing the bbt area
- *
- * Erase one ore more blocks.
- */
-int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
- int allowbbt)
-{
- int page, status, pages_per_block, ret, chipnr;
- struct nand_chip *chip = mtd_to_nand(mtd);
- loff_t len;
-
- pr_debug("%s: start = 0x%012llx, len = %llu\n",
- __func__, (unsigned long long)instr->addr,
- (unsigned long long)instr->len);
-
- if (check_offs_len(mtd, instr->addr, instr->len))
- return -EINVAL;
-
- /* Grab the lock and see if the device is available */
- nand_get_device(mtd, FL_ERASING);
-
- /* Shift to get first page */
- page = (int)(instr->addr >> chip->page_shift);
- chipnr = (int)(instr->addr >> chip->chip_shift);
-
- /* Calculate pages in each block */
- pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
-
- /* Select the NAND device */
- chip->select_chip(mtd, chipnr);
-
- /* Check, if it is write protected */
- if (nand_check_wp(mtd)) {
- pr_debug("%s: device is write protected!\n",
- __func__);
- instr->state = MTD_ERASE_FAILED;
- goto erase_exit;
- }
-
- /* Loop through the pages */
- len = instr->len;
-
- instr->state = MTD_ERASING;
-
- while (len) {
- /* Check if we have a bad block, we do not erase bad blocks! */
- if (nand_block_checkbad(mtd, ((loff_t) page) <<
- chip->page_shift, allowbbt)) {
- pr_warn("%s: attempt to erase a bad block at page 0x%08x\n",
- __func__, page);
- instr->state = MTD_ERASE_FAILED;
- goto erase_exit;
- }
-
- /*
- * Invalidate the page cache, if we erase the block which
- * contains the current cached page.
- */
- if (page <= chip->pagebuf && chip->pagebuf <
- (page + pages_per_block))
- chip->pagebuf = -1;
-
- status = chip->erase(mtd, page & chip->pagemask);
-
- /* See if block erase succeeded */
- if (status) {
- pr_debug("%s: failed erase, page 0x%08x\n",
- __func__, page);
- instr->state = MTD_ERASE_FAILED;
- instr->fail_addr =
- ((loff_t)page << chip->page_shift);
- goto erase_exit;
- }
-
- /* Increment page address and decrement length */
- len -= (1ULL << chip->phys_erase_shift);
- page += pages_per_block;
-
- /* Check, if we cross a chip boundary */
- if (len && !(page & chip->pagemask)) {
- chipnr++;
- chip->select_chip(mtd, -1);
- chip->select_chip(mtd, chipnr);
- }
- }
- instr->state = MTD_ERASE_DONE;
-
-erase_exit:
-
- ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
-
- /* Deselect and wake up anyone waiting on the device */
- chip->select_chip(mtd, -1);
- nand_release_device(mtd);
-
- /* Do call back function */
- if (!ret)
- mtd_erase_callback(instr);
-
- /* Return more or less happy */
- return ret;
-}
-
-/**
- * nand_sync - [MTD Interface] sync
- * @mtd: MTD device structure
- *
- * Sync is actually a wait for chip ready function.
- */
-static void nand_sync(struct mtd_info *mtd)
-{
- pr_debug("%s: called\n", __func__);
-
- /* Grab the lock and see if the device is available */
- nand_get_device(mtd, FL_SYNCING);
- /* Release it and go back */
- nand_release_device(mtd);
-}
-
-/**
- * nand_block_isbad - [MTD Interface] Check if block at offset is bad
- * @mtd: MTD device structure
- * @offs: offset relative to mtd start
- */
-static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- int chipnr = (int)(offs >> chip->chip_shift);
- int ret;
-
- /* Select the NAND device */
- nand_get_device(mtd, FL_READING);
- chip->select_chip(mtd, chipnr);
-
- ret = nand_block_checkbad(mtd, offs, 0);
-
- chip->select_chip(mtd, -1);
- nand_release_device(mtd);
-
- return ret;
-}
-
-/**
- * nand_block_markbad - [MTD Interface] Mark block at the given offset as bad
- * @mtd: MTD device structure
- * @ofs: offset relative to mtd start
- */
-static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
-{
- int ret;
-
- ret = nand_block_isbad(mtd, ofs);
- if (ret) {
- /* If it was bad already, return success and do nothing */
- if (ret > 0)
- return 0;
- return ret;
- }
-
- return nand_block_markbad_lowlevel(mtd, ofs);
-}
-
-/**
- * nand_max_bad_blocks - [MTD Interface] Max number of bad blocks for an mtd
- * @mtd: MTD device structure
- * @ofs: offset relative to mtd start
- * @len: length of mtd
- */
-static int nand_max_bad_blocks(struct mtd_info *mtd, loff_t ofs, size_t len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- u32 part_start_block;
- u32 part_end_block;
- u32 part_start_die;
- u32 part_end_die;
-
- /*
- * max_bb_per_die and blocks_per_die used to determine
- * the maximum bad block count.
- */
- if (!chip->max_bb_per_die || !chip->blocks_per_die)
- return -ENOTSUPP;
-
- /* Get the start and end of the partition in erase blocks. */
- part_start_block = mtd_div_by_eb(ofs, mtd);
- part_end_block = mtd_div_by_eb(len, mtd) + part_start_block - 1;
-
- /* Get the start and end LUNs of the partition. */
- part_start_die = part_start_block / chip->blocks_per_die;
- part_end_die = part_end_block / chip->blocks_per_die;
-
- /*
- * Look up the bad blocks per unit and multiply by the number of units
- * that the partition spans.
- */
- return chip->max_bb_per_die * (part_end_die - part_start_die + 1);
-}
-
-/**
- * nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand
- * @mtd: MTD device structure
- * @chip: nand chip info structure
- * @addr: feature address.
- * @subfeature_param: the subfeature parameters, a four bytes array.
- */
-static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
- int addr, uint8_t *subfeature_param)
-{
- if (!chip->onfi_version ||
- !(le16_to_cpu(chip->onfi_params.opt_cmd)
- & ONFI_OPT_CMD_SET_GET_FEATURES))
- return -EINVAL;
-
- return nand_set_features_op(chip, addr, subfeature_param);
-}
-
-/**
- * nand_onfi_get_features- [REPLACEABLE] get features for ONFI nand
- * @mtd: MTD device structure
- * @chip: nand chip info structure
- * @addr: feature address.
- * @subfeature_param: the subfeature parameters, a four bytes array.
- */
-static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
- int addr, uint8_t *subfeature_param)
-{
- if (!chip->onfi_version ||
- !(le16_to_cpu(chip->onfi_params.opt_cmd)
- & ONFI_OPT_CMD_SET_GET_FEATURES))
- return -EINVAL;
-
- return nand_get_features_op(chip, addr, subfeature_param);
-}
-
-/**
- * nand_onfi_get_set_features_notsupp - set/get features stub returning
- * -ENOTSUPP
- * @mtd: MTD device structure
- * @chip: nand chip info structure
- * @addr: feature address.
- * @subfeature_param: the subfeature parameters, a four bytes array.
- *
- * Should be used by NAND controller drivers that do not support the SET/GET
- * FEATURES operations.
- */
-int nand_onfi_get_set_features_notsupp(struct mtd_info *mtd,
- struct nand_chip *chip, int addr,
- u8 *subfeature_param)
-{
- return -ENOTSUPP;
-}
-EXPORT_SYMBOL(nand_onfi_get_set_features_notsupp);
-
-/**
- * nand_suspend - [MTD Interface] Suspend the NAND flash
- * @mtd: MTD device structure
- */
-static int nand_suspend(struct mtd_info *mtd)
-{
- return nand_get_device(mtd, FL_PM_SUSPENDED);
-}
-
-/**
- * nand_resume - [MTD Interface] Resume the NAND flash
- * @mtd: MTD device structure
- */
-static void nand_resume(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- if (chip->state == FL_PM_SUSPENDED)
- nand_release_device(mtd);
- else
- pr_err("%s called for a chip which is not in suspended state\n",
- __func__);
-}
-
-/**
- * nand_shutdown - [MTD Interface] Finish the current NAND operation and
- * prevent further operations
- * @mtd: MTD device structure
- */
-static void nand_shutdown(struct mtd_info *mtd)
-{
- nand_get_device(mtd, FL_PM_SUSPENDED);
-}
-
-/* Set default functions */
-static void nand_set_defaults(struct nand_chip *chip)
-{
- unsigned int busw = chip->options & NAND_BUSWIDTH_16;
-
- /* check for proper chip_delay setup, set 20us if not */
- if (!chip->chip_delay)
- chip->chip_delay = 20;
-
- /* check, if a user supplied command function given */
- if (!chip->cmdfunc && !chip->exec_op)
- chip->cmdfunc = nand_command;
-
- /* check, if a user supplied wait function given */
- if (chip->waitfunc == NULL)
- chip->waitfunc = nand_wait;
-
- if (!chip->select_chip)
- chip->select_chip = nand_select_chip;
-
- /* set for ONFI nand */
- if (!chip->onfi_set_features)
- chip->onfi_set_features = nand_onfi_set_features;
- if (!chip->onfi_get_features)
- chip->onfi_get_features = nand_onfi_get_features;
-
- /* If called twice, pointers that depend on busw may need to be reset */
- if (!chip->read_byte || chip->read_byte == nand_read_byte)
- chip->read_byte = busw ? nand_read_byte16 : nand_read_byte;
- if (!chip->read_word)
- chip->read_word = nand_read_word;
- if (!chip->block_bad)
- chip->block_bad = nand_block_bad;
- if (!chip->block_markbad)
- chip->block_markbad = nand_default_block_markbad;
- if (!chip->write_buf || chip->write_buf == nand_write_buf)
- chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;
- if (!chip->write_byte || chip->write_byte == nand_write_byte)
- chip->write_byte = busw ? nand_write_byte16 : nand_write_byte;
- if (!chip->read_buf || chip->read_buf == nand_read_buf)
- chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;
- if (!chip->scan_bbt)
- chip->scan_bbt = nand_default_bbt;
-
- if (!chip->controller) {
- chip->controller = &chip->hwcontrol;
- nand_hw_control_init(chip->controller);
- }
-
- if (!chip->buf_align)
- chip->buf_align = 1;
-}
-
-/* Sanitize ONFI strings so we can safely print them */
-static void sanitize_string(uint8_t *s, size_t len)
-{
- ssize_t i;
-
- /* Null terminate */
- s[len - 1] = 0;
-
- /* Remove non printable chars */
- for (i = 0; i < len - 1; i++) {
- if (s[i] < ' ' || s[i] > 127)
- s[i] = '?';
- }
-
- /* Remove trailing spaces */
- strim(s);
-}
-
-static u16 onfi_crc16(u16 crc, u8 const *p, size_t len)
-{
- int i;
- while (len--) {
- crc ^= *p++ << 8;
- for (i = 0; i < 8; i++)
- crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0);
- }
-
- return crc;
-}
-
-/* Parse the Extended Parameter Page. */
-static int nand_flash_detect_ext_param_page(struct nand_chip *chip,
- struct nand_onfi_params *p)
-{
- struct onfi_ext_param_page *ep;
- struct onfi_ext_section *s;
- struct onfi_ext_ecc_info *ecc;
- uint8_t *cursor;
- int ret;
- int len;
- int i;
-
- len = le16_to_cpu(p->ext_param_page_length) * 16;
- ep = kmalloc(len, GFP_KERNEL);
- if (!ep)
- return -ENOMEM;
-
- /* Send our own NAND_CMD_PARAM. */
- ret = nand_read_param_page_op(chip, 0, NULL, 0);
- if (ret)
- goto ext_out;
-
- /* Use the Change Read Column command to skip the ONFI param pages. */
- ret = nand_change_read_column_op(chip,
- sizeof(*p) * p->num_of_param_pages,
- ep, len, true);
- if (ret)
- goto ext_out;
-
- ret = -EINVAL;
- if ((onfi_crc16(ONFI_CRC_BASE, ((uint8_t *)ep) + 2, len - 2)
- != le16_to_cpu(ep->crc))) {
- pr_debug("fail in the CRC.\n");
- goto ext_out;
- }
-
- /*
- * Check the signature.
- * Do not strictly follow the ONFI spec, maybe changed in future.
- */
- if (strncmp(ep->sig, "EPPS", 4)) {
- pr_debug("The signature is invalid.\n");
- goto ext_out;
- }
-
- /* find the ECC section. */
- cursor = (uint8_t *)(ep + 1);
- for (i = 0; i < ONFI_EXT_SECTION_MAX; i++) {
- s = ep->sections + i;
- if (s->type == ONFI_SECTION_TYPE_2)
- break;
- cursor += s->length * 16;
- }
- if (i == ONFI_EXT_SECTION_MAX) {
- pr_debug("We can not find the ECC section.\n");
- goto ext_out;
- }
-
- /* get the info we want. */
- ecc = (struct onfi_ext_ecc_info *)cursor;
-
- if (!ecc->codeword_size) {
- pr_debug("Invalid codeword size\n");
- goto ext_out;
- }
-
- chip->ecc_strength_ds = ecc->ecc_bits;
- chip->ecc_step_ds = 1 << ecc->codeword_size;
- ret = 0;
-
-ext_out:
- kfree(ep);
- return ret;
-}
-
-/*
- * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.
- */
-static int nand_flash_detect_onfi(struct nand_chip *chip)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct nand_onfi_params *p = &chip->onfi_params;
- char id[4];
- int i, ret, val;
-
- /* Try ONFI for unknown chip or LP */
- ret = nand_readid_op(chip, 0x20, id, sizeof(id));
- if (ret || strncmp(id, "ONFI", 4))
- return 0;
-
- ret = nand_read_param_page_op(chip, 0, NULL, 0);
- if (ret)
- return 0;
-
- for (i = 0; i < 3; i++) {
- ret = nand_read_data_op(chip, p, sizeof(*p), true);
- if (ret)
- return 0;
-
- if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
- le16_to_cpu(p->crc)) {
- break;
- }
- }
-
- if (i == 3) {
- pr_err("Could not find valid ONFI parameter page; aborting\n");
- return 0;
- }
-
- /* Check version */
- val = le16_to_cpu(p->revision);
- if (val & (1 << 5))
- chip->onfi_version = 23;
- else if (val & (1 << 4))
- chip->onfi_version = 22;
- else if (val & (1 << 3))
- chip->onfi_version = 21;
- else if (val & (1 << 2))
- chip->onfi_version = 20;
- else if (val & (1 << 1))
- chip->onfi_version = 10;
-
- if (!chip->onfi_version) {
- pr_info("unsupported ONFI version: %d\n", val);
- return 0;
- }
-
- sanitize_string(p->manufacturer, sizeof(p->manufacturer));
- sanitize_string(p->model, sizeof(p->model));
- if (!mtd->name)
- mtd->name = p->model;
-
- mtd->writesize = le32_to_cpu(p->byte_per_page);
-
- /*
- * pages_per_block and blocks_per_lun may not be a power-of-2 size
- * (don't ask me who thought of this...). MTD assumes that these
- * dimensions will be power-of-2, so just truncate the remaining area.
- */
- mtd->erasesize = 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1);
- mtd->erasesize *= mtd->writesize;
-
- mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
-
- /* See erasesize comment */
- chip->chipsize = 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1);
- chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count;
- chip->bits_per_cell = p->bits_per_cell;
-
- chip->max_bb_per_die = le16_to_cpu(p->bb_per_lun);
- chip->blocks_per_die = le32_to_cpu(p->blocks_per_lun);
-
- if (onfi_feature(chip) & ONFI_FEATURE_16_BIT_BUS)
- chip->options |= NAND_BUSWIDTH_16;
-
- if (p->ecc_bits != 0xff) {
- chip->ecc_strength_ds = p->ecc_bits;
- chip->ecc_step_ds = 512;
- } else if (chip->onfi_version >= 21 &&
- (onfi_feature(chip) & ONFI_FEATURE_EXT_PARAM_PAGE)) {
-
- /*
- * The nand_flash_detect_ext_param_page() uses the
- * Change Read Column command which maybe not supported
- * by the chip->cmdfunc. So try to update the chip->cmdfunc
- * now. We do not replace user supplied command function.
- */
- if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
- chip->cmdfunc = nand_command_lp;
-
- /* The Extended Parameter Page is supported since ONFI 2.1. */
- if (nand_flash_detect_ext_param_page(chip, p))
- pr_warn("Failed to detect ONFI extended param page\n");
- } else {
- pr_warn("Could not retrieve ONFI ECC requirements\n");
- }
-
- return 1;
-}
-
-/*
- * Check if the NAND chip is JEDEC compliant, returns 1 if it is, 0 otherwise.
- */
-static int nand_flash_detect_jedec(struct nand_chip *chip)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct nand_jedec_params *p = &chip->jedec_params;
- struct jedec_ecc_info *ecc;
- char id[5];
- int i, val, ret;
-
- /* Try JEDEC for unknown chip or LP */
- ret = nand_readid_op(chip, 0x40, id, sizeof(id));
- if (ret || strncmp(id, "JEDEC", sizeof(id)))
- return 0;
-
- ret = nand_read_param_page_op(chip, 0x40, NULL, 0);
- if (ret)
- return 0;
-
- for (i = 0; i < 3; i++) {
- ret = nand_read_data_op(chip, p, sizeof(*p), true);
- if (ret)
- return 0;
-
- if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 510) ==
- le16_to_cpu(p->crc))
- break;
- }
-
- if (i == 3) {
- pr_err("Could not find valid JEDEC parameter page; aborting\n");
- return 0;
- }
-
- /* Check version */
- val = le16_to_cpu(p->revision);
- if (val & (1 << 2))
- chip->jedec_version = 10;
- else if (val & (1 << 1))
- chip->jedec_version = 1; /* vendor specific version */
-
- if (!chip->jedec_version) {
- pr_info("unsupported JEDEC version: %d\n", val);
- return 0;
- }
-
- sanitize_string(p->manufacturer, sizeof(p->manufacturer));
- sanitize_string(p->model, sizeof(p->model));
- if (!mtd->name)
- mtd->name = p->model;
-
- mtd->writesize = le32_to_cpu(p->byte_per_page);
-
- /* Please reference to the comment for nand_flash_detect_onfi. */
- mtd->erasesize = 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1);
- mtd->erasesize *= mtd->writesize;
-
- mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
-
- /* Please reference to the comment for nand_flash_detect_onfi. */
- chip->chipsize = 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1);
- chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count;
- chip->bits_per_cell = p->bits_per_cell;
-
- if (jedec_feature(chip) & JEDEC_FEATURE_16_BIT_BUS)
- chip->options |= NAND_BUSWIDTH_16;
-
- /* ECC info */
- ecc = &p->ecc_info[0];
-
- if (ecc->codeword_size >= 9) {
- chip->ecc_strength_ds = ecc->ecc_bits;
- chip->ecc_step_ds = 1 << ecc->codeword_size;
- } else {
- pr_warn("Invalid codeword size\n");
- }
-
- return 1;
-}
-
-/*
- * nand_id_has_period - Check if an ID string has a given wraparound period
- * @id_data: the ID string
- * @arrlen: the length of the @id_data array
- * @period: the period of repitition
- *
- * Check if an ID string is repeated within a given sequence of bytes at
- * specific repetition interval period (e.g., {0x20,0x01,0x7F,0x20} has a
- * period of 3). This is a helper function for nand_id_len(). Returns non-zero
- * if the repetition has a period of @period; otherwise, returns zero.
- */
-static int nand_id_has_period(u8 *id_data, int arrlen, int period)
-{
- int i, j;
- for (i = 0; i < period; i++)
- for (j = i + period; j < arrlen; j += period)
- if (id_data[i] != id_data[j])
- return 0;
- return 1;
-}
-
-/*
- * nand_id_len - Get the length of an ID string returned by CMD_READID
- * @id_data: the ID string
- * @arrlen: the length of the @id_data array
-
- * Returns the length of the ID string, according to known wraparound/trailing
- * zero patterns. If no pattern exists, returns the length of the array.
- */
-static int nand_id_len(u8 *id_data, int arrlen)
-{
- int last_nonzero, period;
-
- /* Find last non-zero byte */
- for (last_nonzero = arrlen - 1; last_nonzero >= 0; last_nonzero--)
- if (id_data[last_nonzero])
- break;
-
- /* All zeros */
- if (last_nonzero < 0)
- return 0;
-
- /* Calculate wraparound period */
- for (period = 1; period < arrlen; period++)
- if (nand_id_has_period(id_data, arrlen, period))
- break;
-
- /* There's a repeated pattern */
- if (period < arrlen)
- return period;
-
- /* There are trailing zeros */
- if (last_nonzero < arrlen - 1)
- return last_nonzero + 1;
-
- /* No pattern detected */
- return arrlen;
-}
-
-/* Extract the bits of per cell from the 3rd byte of the extended ID */
-static int nand_get_bits_per_cell(u8 cellinfo)
-{
- int bits;
-
- bits = cellinfo & NAND_CI_CELLTYPE_MSK;
- bits >>= NAND_CI_CELLTYPE_SHIFT;
- return bits + 1;
-}
-
-/*
- * Many new NAND share similar device ID codes, which represent the size of the
- * chip. The rest of the parameters must be decoded according to generic or
- * manufacturer-specific "extended ID" decoding patterns.
- */
-void nand_decode_ext_id(struct nand_chip *chip)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- int extid;
- u8 *id_data = chip->id.data;
- /* The 3rd id byte holds MLC / multichip data */
- chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]);
- /* The 4th id byte is the important one */
- extid = id_data[3];
-
- /* Calc pagesize */
- mtd->writesize = 1024 << (extid & 0x03);
- extid >>= 2;
- /* Calc oobsize */
- mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9);
- extid >>= 2;
- /* Calc blocksize. Blocksize is multiples of 64KiB */
- mtd->erasesize = (64 * 1024) << (extid & 0x03);
- extid >>= 2;
- /* Get buswidth information */
- if (extid & 0x1)
- chip->options |= NAND_BUSWIDTH_16;
-}
-EXPORT_SYMBOL_GPL(nand_decode_ext_id);
-
-/*
- * Old devices have chip data hardcoded in the device ID table. nand_decode_id
- * decodes a matching ID table entry and assigns the MTD size parameters for
- * the chip.
- */
-static void nand_decode_id(struct nand_chip *chip, struct nand_flash_dev *type)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
-
- mtd->erasesize = type->erasesize;
- mtd->writesize = type->pagesize;
- mtd->oobsize = mtd->writesize / 32;
-
- /* All legacy ID NAND are small-page, SLC */
- chip->bits_per_cell = 1;
-}
-
-/*
- * Set the bad block marker/indicator (BBM/BBI) patterns according to some
- * heuristic patterns using various detected parameters (e.g., manufacturer,
- * page size, cell-type information).
- */
-static void nand_decode_bbm_options(struct nand_chip *chip)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
-
- /* Set the bad block position */
- if (mtd->writesize > 512 || (chip->options & NAND_BUSWIDTH_16))
- chip->badblockpos = NAND_LARGE_BADBLOCK_POS;
- else
- chip->badblockpos = NAND_SMALL_BADBLOCK_POS;
-}
-
-static inline bool is_full_id_nand(struct nand_flash_dev *type)
-{
- return type->id_len;
-}
-
-static bool find_full_id_nand(struct nand_chip *chip,
- struct nand_flash_dev *type)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- u8 *id_data = chip->id.data;
-
- if (!strncmp(type->id, id_data, type->id_len)) {
- mtd->writesize = type->pagesize;
- mtd->erasesize = type->erasesize;
- mtd->oobsize = type->oobsize;
-
- chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]);
- chip->chipsize = (uint64_t)type->chipsize << 20;
- chip->options |= type->options;
- chip->ecc_strength_ds = NAND_ECC_STRENGTH(type);
- chip->ecc_step_ds = NAND_ECC_STEP(type);
- chip->onfi_timing_mode_default =
- type->onfi_timing_mode_default;
-
- if (!mtd->name)
- mtd->name = type->name;
-
- return true;
- }
- return false;
-}
-
-/*
- * Manufacturer detection. Only used when the NAND is not ONFI or JEDEC
- * compliant and does not have a full-id or legacy-id entry in the nand_ids
- * table.
- */
-static void nand_manufacturer_detect(struct nand_chip *chip)
-{
- /*
- * Try manufacturer detection if available and use
- * nand_decode_ext_id() otherwise.
- */
- if (chip->manufacturer.desc && chip->manufacturer.desc->ops &&
- chip->manufacturer.desc->ops->detect) {
- /* The 3rd id byte holds MLC / multichip data */
- chip->bits_per_cell = nand_get_bits_per_cell(chip->id.data[2]);
- chip->manufacturer.desc->ops->detect(chip);
- } else {
- nand_decode_ext_id(chip);
- }
-}
-
-/*
- * Manufacturer initialization. This function is called for all NANDs including
- * ONFI and JEDEC compliant ones.
- * Manufacturer drivers should put all their specific initialization code in
- * their ->init() hook.
- */
-static int nand_manufacturer_init(struct nand_chip *chip)
-{
- if (!chip->manufacturer.desc || !chip->manufacturer.desc->ops ||
- !chip->manufacturer.desc->ops->init)
- return 0;
-
- return chip->manufacturer.desc->ops->init(chip);
-}
-
-/*
- * Manufacturer cleanup. This function is called for all NANDs including
- * ONFI and JEDEC compliant ones.
- * Manufacturer drivers should put all their specific cleanup code in their
- * ->cleanup() hook.
- */
-static void nand_manufacturer_cleanup(struct nand_chip *chip)
-{
- /* Release manufacturer private data */
- if (chip->manufacturer.desc && chip->manufacturer.desc->ops &&
- chip->manufacturer.desc->ops->cleanup)
- chip->manufacturer.desc->ops->cleanup(chip);
-}
-
-/*
- * Get the flash and manufacturer id and lookup if the type is supported.
- */
-static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type)
-{
- const struct nand_manufacturer *manufacturer;
- struct mtd_info *mtd = nand_to_mtd(chip);
- int busw, ret;
- u8 *id_data = chip->id.data;
- u8 maf_id, dev_id;
-
- /*
- * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
- * after power-up.
- */
- ret = nand_reset(chip, 0);
- if (ret)
- return ret;
-
- /* Select the device */
- chip->select_chip(mtd, 0);
-
- /* Send the command for reading device ID */
- ret = nand_readid_op(chip, 0, id_data, 2);
- if (ret)
- return ret;
-
- /* Read manufacturer and device IDs */
- maf_id = id_data[0];
- dev_id = id_data[1];
-
- /*
- * Try again to make sure, as some systems the bus-hold or other
- * interface concerns can cause random data which looks like a
- * possibly credible NAND flash to appear. If the two results do
- * not match, ignore the device completely.
- */
-
- /* Read entire ID string */
- ret = nand_readid_op(chip, 0, id_data, sizeof(chip->id.data));
- if (ret)
- return ret;
-
- if (id_data[0] != maf_id || id_data[1] != dev_id) {
- pr_info("second ID read did not match %02x,%02x against %02x,%02x\n",
- maf_id, dev_id, id_data[0], id_data[1]);
- return -ENODEV;
- }
-
- chip->id.len = nand_id_len(id_data, ARRAY_SIZE(chip->id.data));
-
- /* Try to identify manufacturer */
- manufacturer = nand_get_manufacturer(maf_id);
- chip->manufacturer.desc = manufacturer;
-
- if (!type)
- type = nand_flash_ids;
-
- /*
- * Save the NAND_BUSWIDTH_16 flag before letting auto-detection logic
- * override it.
- * This is required to make sure initial NAND bus width set by the
- * NAND controller driver is coherent with the real NAND bus width
- * (extracted by auto-detection code).
- */
- busw = chip->options & NAND_BUSWIDTH_16;
-
- /*
- * The flag is only set (never cleared), reset it to its default value
- * before starting auto-detection.
- */
- chip->options &= ~NAND_BUSWIDTH_16;
-
- for (; type->name != NULL; type++) {
- if (is_full_id_nand(type)) {
- if (find_full_id_nand(chip, type))
- goto ident_done;
- } else if (dev_id == type->dev_id) {
- break;
- }
- }
-
- chip->onfi_version = 0;
- if (!type->name || !type->pagesize) {
- /* Check if the chip is ONFI compliant */
- if (nand_flash_detect_onfi(chip))
- goto ident_done;
-
- /* Check if the chip is JEDEC compliant */
- if (nand_flash_detect_jedec(chip))
- goto ident_done;
- }
-
- if (!type->name)
- return -ENODEV;
-
- if (!mtd->name)
- mtd->name = type->name;
-
- chip->chipsize = (uint64_t)type->chipsize << 20;
-
- if (!type->pagesize)
- nand_manufacturer_detect(chip);
- else
- nand_decode_id(chip, type);
-
- /* Get chip options */
- chip->options |= type->options;
-
-ident_done:
-
- if (chip->options & NAND_BUSWIDTH_AUTO) {
- WARN_ON(busw & NAND_BUSWIDTH_16);
- nand_set_defaults(chip);
- } else if (busw != (chip->options & NAND_BUSWIDTH_16)) {
- /*
- * Check, if buswidth is correct. Hardware drivers should set
- * chip correct!
- */
- pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
- maf_id, dev_id);
- pr_info("%s %s\n", nand_manufacturer_name(manufacturer),
- mtd->name);
- pr_warn("bus width %d instead of %d bits\n", busw ? 16 : 8,
- (chip->options & NAND_BUSWIDTH_16) ? 16 : 8);
- return -EINVAL;
- }
-
- nand_decode_bbm_options(chip);
-
- /* Calculate the address shift from the page size */
- chip->page_shift = ffs(mtd->writesize) - 1;
- /* Convert chipsize to number of pages per chip -1 */
- chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;
-
- chip->bbt_erase_shift = chip->phys_erase_shift =
- ffs(mtd->erasesize) - 1;
- if (chip->chipsize & 0xffffffff)
- chip->chip_shift = ffs((unsigned)chip->chipsize) - 1;
- else {
- chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32));
- chip->chip_shift += 32 - 1;
- }
-
- if (chip->chip_shift - chip->page_shift > 16)
- chip->options |= NAND_ROW_ADDR_3;
-
- chip->badblockbits = 8;
- chip->erase = single_erase;
-
- /* Do not replace user supplied command function! */
- if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
- chip->cmdfunc = nand_command_lp;
-
- pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
- maf_id, dev_id);
-
- if (chip->onfi_version)
- pr_info("%s %s\n", nand_manufacturer_name(manufacturer),
- chip->onfi_params.model);
- else if (chip->jedec_version)
- pr_info("%s %s\n", nand_manufacturer_name(manufacturer),
- chip->jedec_params.model);
- else
- pr_info("%s %s\n", nand_manufacturer_name(manufacturer),
- type->name);
-
- pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, OOB size: %d\n",
- (int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC",
- mtd->erasesize >> 10, mtd->writesize, mtd->oobsize);
- return 0;
-}
-
-static const char * const nand_ecc_modes[] = {
- [NAND_ECC_NONE] = "none",
- [NAND_ECC_SOFT] = "soft",
- [NAND_ECC_HW] = "hw",
- [NAND_ECC_HW_SYNDROME] = "hw_syndrome",
- [NAND_ECC_HW_OOB_FIRST] = "hw_oob_first",
- [NAND_ECC_ON_DIE] = "on-die",
-};
-
-static int of_get_nand_ecc_mode(struct device_node *np)
-{
- const char *pm;
- int err, i;
-
- err = of_property_read_string(np, "nand-ecc-mode", &pm);
- if (err < 0)
- return err;
-
- for (i = 0; i < ARRAY_SIZE(nand_ecc_modes); i++)
- if (!strcasecmp(pm, nand_ecc_modes[i]))
- return i;
-
- /*
- * For backward compatibility we support few obsoleted values that don't
- * have their mappings into nand_ecc_modes_t anymore (they were merged
- * with other enums).
- */
- if (!strcasecmp(pm, "soft_bch"))
- return NAND_ECC_SOFT;
-
- return -ENODEV;
-}
-
-static const char * const nand_ecc_algos[] = {
- [NAND_ECC_HAMMING] = "hamming",
- [NAND_ECC_BCH] = "bch",
-};
-
-static int of_get_nand_ecc_algo(struct device_node *np)
-{
- const char *pm;
- int err, i;
-
- err = of_property_read_string(np, "nand-ecc-algo", &pm);
- if (!err) {
- for (i = NAND_ECC_HAMMING; i < ARRAY_SIZE(nand_ecc_algos); i++)
- if (!strcasecmp(pm, nand_ecc_algos[i]))
- return i;
- return -ENODEV;
- }
-
- /*
- * For backward compatibility we also read "nand-ecc-mode" checking
- * for some obsoleted values that were specifying ECC algorithm.
- */
- err = of_property_read_string(np, "nand-ecc-mode", &pm);
- if (err < 0)
- return err;
-
- if (!strcasecmp(pm, "soft"))
- return NAND_ECC_HAMMING;
- else if (!strcasecmp(pm, "soft_bch"))
- return NAND_ECC_BCH;
-
- return -ENODEV;
-}
-
-static int of_get_nand_ecc_step_size(struct device_node *np)
-{
- int ret;
- u32 val;
-
- ret = of_property_read_u32(np, "nand-ecc-step-size", &val);
- return ret ? ret : val;
-}
-
-static int of_get_nand_ecc_strength(struct device_node *np)
-{
- int ret;
- u32 val;
-
- ret = of_property_read_u32(np, "nand-ecc-strength", &val);
- return ret ? ret : val;
-}
-
-static int of_get_nand_bus_width(struct device_node *np)
-{
- u32 val;
-
- if (of_property_read_u32(np, "nand-bus-width", &val))
- return 8;
-
- switch (val) {
- case 8:
- case 16:
- return val;
- default:
- return -EIO;
- }
-}
-
-static bool of_get_nand_on_flash_bbt(struct device_node *np)
-{
- return of_property_read_bool(np, "nand-on-flash-bbt");
-}
-
-static int nand_dt_init(struct nand_chip *chip)
-{
- struct device_node *dn = nand_get_flash_node(chip);
- int ecc_mode, ecc_algo, ecc_strength, ecc_step;
-
- if (!dn)
- return 0;
-
- if (of_get_nand_bus_width(dn) == 16)
- chip->options |= NAND_BUSWIDTH_16;
-
- if (of_get_nand_on_flash_bbt(dn))
- chip->bbt_options |= NAND_BBT_USE_FLASH;
-
- ecc_mode = of_get_nand_ecc_mode(dn);
- ecc_algo = of_get_nand_ecc_algo(dn);
- ecc_strength = of_get_nand_ecc_strength(dn);
- ecc_step = of_get_nand_ecc_step_size(dn);
-
- if (ecc_mode >= 0)
- chip->ecc.mode = ecc_mode;
-
- if (ecc_algo >= 0)
- chip->ecc.algo = ecc_algo;
-
- if (ecc_strength >= 0)
- chip->ecc.strength = ecc_strength;
-
- if (ecc_step > 0)
- chip->ecc.size = ecc_step;
-
- if (of_property_read_bool(dn, "nand-ecc-maximize"))
- chip->ecc.options |= NAND_ECC_MAXIMIZE;
-
- return 0;
-}
-
-/**
- * nand_scan_ident - [NAND Interface] Scan for the NAND device
- * @mtd: MTD device structure
- * @maxchips: number of chips to scan for
- * @table: alternative NAND ID table
- *
- * This is the first phase of the normal nand_scan() function. It reads the
- * flash ID and sets up MTD fields accordingly.
- *
- */
-int nand_scan_ident(struct mtd_info *mtd, int maxchips,
- struct nand_flash_dev *table)
-{
- int i, nand_maf_id, nand_dev_id;
- struct nand_chip *chip = mtd_to_nand(mtd);
- int ret;
-
- /* Enforce the right timings for reset/detection */
- onfi_fill_data_interface(chip, NAND_SDR_IFACE, 0);
-
- ret = nand_dt_init(chip);
- if (ret)
- return ret;
-
- if (!mtd->name && mtd->dev.parent)
- mtd->name = dev_name(mtd->dev.parent);
-
- /*
- * ->cmdfunc() is legacy and will only be used if ->exec_op() is not
- * populated.
- */
- if (!chip->exec_op) {
- /*
- * Default functions assigned for ->cmdfunc() and
- * ->select_chip() both expect ->cmd_ctrl() to be populated.
- */
- if ((!chip->cmdfunc || !chip->select_chip) && !chip->cmd_ctrl) {
- pr_err("->cmd_ctrl() should be provided\n");
- return -EINVAL;
- }
- }
-
- /* Set the default functions */
- nand_set_defaults(chip);
-
- /* Read the flash type */
- ret = nand_detect(chip, table);
- if (ret) {
- if (!(chip->options & NAND_SCAN_SILENT_NODEV))
- pr_warn("No NAND device found\n");
- chip->select_chip(mtd, -1);
- return ret;
- }
-
- nand_maf_id = chip->id.data[0];
- nand_dev_id = chip->id.data[1];
-
- chip->select_chip(mtd, -1);
-
- /* Check for a chip array */
- for (i = 1; i < maxchips; i++) {
- u8 id[2];
-
- /* See comment in nand_get_flash_type for reset */
- nand_reset(chip, i);
-
- chip->select_chip(mtd, i);
- /* Send the command for reading device ID */
- nand_readid_op(chip, 0, id, sizeof(id));
- /* Read manufacturer and device IDs */
- if (nand_maf_id != id[0] || nand_dev_id != id[1]) {
- chip->select_chip(mtd, -1);
- break;
- }
- chip->select_chip(mtd, -1);
- }
- if (i > 1)
- pr_info("%d chips detected\n", i);
-
- /* Store the number of chips and calc total size for mtd */
- chip->numchips = i;
- mtd->size = i * chip->chipsize;
-
- return 0;
-}
-EXPORT_SYMBOL(nand_scan_ident);
-
-static int nand_set_ecc_soft_ops(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
-
- if (WARN_ON(ecc->mode != NAND_ECC_SOFT))
- return -EINVAL;
-
- switch (ecc->algo) {
- case NAND_ECC_HAMMING:
- ecc->calculate = nand_calculate_ecc;
- ecc->correct = nand_correct_data;
- ecc->read_page = nand_read_page_swecc;
- ecc->read_subpage = nand_read_subpage;
- ecc->write_page = nand_write_page_swecc;
- ecc->read_page_raw = nand_read_page_raw;
- ecc->write_page_raw = nand_write_page_raw;
- ecc->read_oob = nand_read_oob_std;
- ecc->write_oob = nand_write_oob_std;
- if (!ecc->size)
- ecc->size = 256;
- ecc->bytes = 3;
- ecc->strength = 1;
- return 0;
- case NAND_ECC_BCH:
- if (!mtd_nand_has_bch()) {
- WARN(1, "CONFIG_MTD_NAND_ECC_BCH not enabled\n");
- return -EINVAL;
- }
- ecc->calculate = nand_bch_calculate_ecc;
- ecc->correct = nand_bch_correct_data;
- ecc->read_page = nand_read_page_swecc;
- ecc->read_subpage = nand_read_subpage;
- ecc->write_page = nand_write_page_swecc;
- ecc->read_page_raw = nand_read_page_raw;
- ecc->write_page_raw = nand_write_page_raw;
- ecc->read_oob = nand_read_oob_std;
- ecc->write_oob = nand_write_oob_std;
-
- /*
- * Board driver should supply ecc.size and ecc.strength
- * values to select how many bits are correctable.
- * Otherwise, default to 4 bits for large page devices.
- */
- if (!ecc->size && (mtd->oobsize >= 64)) {
- ecc->size = 512;
- ecc->strength = 4;
- }
-
- /*
- * if no ecc placement scheme was provided pickup the default
- * large page one.
- */
- if (!mtd->ooblayout) {
- /* handle large page devices only */
- if (mtd->oobsize < 64) {
- WARN(1, "OOB layout is required when using software BCH on small pages\n");
- return -EINVAL;
- }
-
- mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
-
- }
-
- /*
- * We can only maximize ECC config when the default layout is
- * used, otherwise we don't know how many bytes can really be
- * used.
- */
- if (mtd->ooblayout == &nand_ooblayout_lp_ops &&
- ecc->options & NAND_ECC_MAXIMIZE) {
- int steps, bytes;
-
- /* Always prefer 1k blocks over 512bytes ones */
- ecc->size = 1024;
- steps = mtd->writesize / ecc->size;
-
- /* Reserve 2 bytes for the BBM */
- bytes = (mtd->oobsize - 2) / steps;
- ecc->strength = bytes * 8 / fls(8 * ecc->size);
- }
-
- /* See nand_bch_init() for details. */
- ecc->bytes = 0;
- ecc->priv = nand_bch_init(mtd);
- if (!ecc->priv) {
- WARN(1, "BCH ECC initialization failed!\n");
- return -EINVAL;
- }
- return 0;
- default:
- WARN(1, "Unsupported ECC algorithm!\n");
- return -EINVAL;
- }
-}
-
-/**
- * nand_check_ecc_caps - check the sanity of preset ECC settings
- * @chip: nand chip info structure
- * @caps: ECC caps info structure
- * @oobavail: OOB size that the ECC engine can use
- *
- * When ECC step size and strength are already set, check if they are supported
- * by the controller and the calculated ECC bytes fit within the chip's OOB.
- * On success, the calculated ECC bytes is set.
- */
-int nand_check_ecc_caps(struct nand_chip *chip,
- const struct nand_ecc_caps *caps, int oobavail)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- const struct nand_ecc_step_info *stepinfo;
- int preset_step = chip->ecc.size;
- int preset_strength = chip->ecc.strength;
- int nsteps, ecc_bytes;
- int i, j;
-
- if (WARN_ON(oobavail < 0))
- return -EINVAL;
-
- if (!preset_step || !preset_strength)
- return -ENODATA;
-
- nsteps = mtd->writesize / preset_step;
-
- for (i = 0; i < caps->nstepinfos; i++) {
- stepinfo = &caps->stepinfos[i];
-
- if (stepinfo->stepsize != preset_step)
- continue;
-
- for (j = 0; j < stepinfo->nstrengths; j++) {
- if (stepinfo->strengths[j] != preset_strength)
- continue;
-
- ecc_bytes = caps->calc_ecc_bytes(preset_step,
- preset_strength);
- if (WARN_ON_ONCE(ecc_bytes < 0))
- return ecc_bytes;
-
- if (ecc_bytes * nsteps > oobavail) {
- pr_err("ECC (step, strength) = (%d, %d) does not fit in OOB",
- preset_step, preset_strength);
- return -ENOSPC;
- }
-
- chip->ecc.bytes = ecc_bytes;
-
- return 0;
- }
- }
-
- pr_err("ECC (step, strength) = (%d, %d) not supported on this controller",
- preset_step, preset_strength);
-
- return -ENOTSUPP;
-}
-EXPORT_SYMBOL_GPL(nand_check_ecc_caps);
-
-/**
- * nand_match_ecc_req - meet the chip's requirement with least ECC bytes
- * @chip: nand chip info structure
- * @caps: ECC engine caps info structure
- * @oobavail: OOB size that the ECC engine can use
- *
- * If a chip's ECC requirement is provided, try to meet it with the least
- * number of ECC bytes (i.e. with the largest number of OOB-free bytes).
- * On success, the chosen ECC settings are set.
- */
-int nand_match_ecc_req(struct nand_chip *chip,
- const struct nand_ecc_caps *caps, int oobavail)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- const struct nand_ecc_step_info *stepinfo;
- int req_step = chip->ecc_step_ds;
- int req_strength = chip->ecc_strength_ds;
- int req_corr, step_size, strength, nsteps, ecc_bytes, ecc_bytes_total;
- int best_step, best_strength, best_ecc_bytes;
- int best_ecc_bytes_total = INT_MAX;
- int i, j;
-
- if (WARN_ON(oobavail < 0))
- return -EINVAL;
-
- /* No information provided by the NAND chip */
- if (!req_step || !req_strength)
- return -ENOTSUPP;
-
- /* number of correctable bits the chip requires in a page */
- req_corr = mtd->writesize / req_step * req_strength;
-
- for (i = 0; i < caps->nstepinfos; i++) {
- stepinfo = &caps->stepinfos[i];
- step_size = stepinfo->stepsize;
-
- for (j = 0; j < stepinfo->nstrengths; j++) {
- strength = stepinfo->strengths[j];
-
- /*
- * If both step size and strength are smaller than the
- * chip's requirement, it is not easy to compare the
- * resulted reliability.
- */
- if (step_size < req_step && strength < req_strength)
- continue;
-
- if (mtd->writesize % step_size)
- continue;
-
- nsteps = mtd->writesize / step_size;
-
- ecc_bytes = caps->calc_ecc_bytes(step_size, strength);
- if (WARN_ON_ONCE(ecc_bytes < 0))
- continue;
- ecc_bytes_total = ecc_bytes * nsteps;
-
- if (ecc_bytes_total > oobavail ||
- strength * nsteps < req_corr)
- continue;
-
- /*
- * We assume the best is to meet the chip's requrement
- * with the least number of ECC bytes.
- */
- if (ecc_bytes_total < best_ecc_bytes_total) {
- best_ecc_bytes_total = ecc_bytes_total;
- best_step = step_size;
- best_strength = strength;
- best_ecc_bytes = ecc_bytes;
- }
- }
- }
-
- if (best_ecc_bytes_total == INT_MAX)
- return -ENOTSUPP;
-
- chip->ecc.size = best_step;
- chip->ecc.strength = best_strength;
- chip->ecc.bytes = best_ecc_bytes;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(nand_match_ecc_req);
-
-/**
- * nand_maximize_ecc - choose the max ECC strength available
- * @chip: nand chip info structure
- * @caps: ECC engine caps info structure
- * @oobavail: OOB size that the ECC engine can use
- *
- * Choose the max ECC strength that is supported on the controller, and can fit
- * within the chip's OOB. On success, the chosen ECC settings are set.
- */
-int nand_maximize_ecc(struct nand_chip *chip,
- const struct nand_ecc_caps *caps, int oobavail)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- const struct nand_ecc_step_info *stepinfo;
- int step_size, strength, nsteps, ecc_bytes, corr;
- int best_corr = 0;
- int best_step = 0;
- int best_strength, best_ecc_bytes;
- int i, j;
-
- if (WARN_ON(oobavail < 0))
- return -EINVAL;
-
- for (i = 0; i < caps->nstepinfos; i++) {
- stepinfo = &caps->stepinfos[i];
- step_size = stepinfo->stepsize;
-
- /* If chip->ecc.size is already set, respect it */
- if (chip->ecc.size && step_size != chip->ecc.size)
- continue;
-
- for (j = 0; j < stepinfo->nstrengths; j++) {
- strength = stepinfo->strengths[j];
-
- if (mtd->writesize % step_size)
- continue;
-
- nsteps = mtd->writesize / step_size;
-
- ecc_bytes = caps->calc_ecc_bytes(step_size, strength);
- if (WARN_ON_ONCE(ecc_bytes < 0))
- continue;
-
- if (ecc_bytes * nsteps > oobavail)
- continue;
-
- corr = strength * nsteps;
-
- /*
- * If the number of correctable bits is the same,
- * bigger step_size has more reliability.
- */
- if (corr > best_corr ||
- (corr == best_corr && step_size > best_step)) {
- best_corr = corr;
- best_step = step_size;
- best_strength = strength;
- best_ecc_bytes = ecc_bytes;
- }
- }
- }
-
- if (!best_corr)
- return -ENOTSUPP;
-
- chip->ecc.size = best_step;
- chip->ecc.strength = best_strength;
- chip->ecc.bytes = best_ecc_bytes;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(nand_maximize_ecc);
-
-/*
- * Check if the chip configuration meet the datasheet requirements.
-
- * If our configuration corrects A bits per B bytes and the minimum
- * required correction level is X bits per Y bytes, then we must ensure
- * both of the following are true:
- *
- * (1) A / B >= X / Y
- * (2) A >= X
- *
- * Requirement (1) ensures we can correct for the required bitflip density.
- * Requirement (2) ensures we can correct even when all bitflips are clumped
- * in the same sector.
- */
-static bool nand_ecc_strength_good(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
- int corr, ds_corr;
-
- if (ecc->size == 0 || chip->ecc_step_ds == 0)
- /* Not enough information */
- return true;
-
- /*
- * We get the number of corrected bits per page to compare
- * the correction density.
- */
- corr = (mtd->writesize * ecc->strength) / ecc->size;
- ds_corr = (mtd->writesize * chip->ecc_strength_ds) / chip->ecc_step_ds;
-
- return corr >= ds_corr && ecc->strength >= chip->ecc_strength_ds;
-}
-
-/**
- * nand_scan_tail - [NAND Interface] Scan for the NAND device
- * @mtd: MTD device structure
- *
- * This is the second phase of the normal nand_scan() function. It fills out
- * all the uninitialized function pointers with the defaults and scans for a
- * bad block table if appropriate.
- */
-int nand_scan_tail(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
- int ret, i;
-
- /* New bad blocks should be marked in OOB, flash-based BBT, or both */
- if (WARN_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
- !(chip->bbt_options & NAND_BBT_USE_FLASH))) {
- return -EINVAL;
- }
-
- chip->data_buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
- if (!chip->data_buf)
- return -ENOMEM;
-
- /*
- * FIXME: some NAND manufacturer drivers expect the first die to be
- * selected when manufacturer->init() is called. They should be fixed
- * to explictly select the relevant die when interacting with the NAND
- * chip.
- */
- chip->select_chip(mtd, 0);
- ret = nand_manufacturer_init(chip);
- chip->select_chip(mtd, -1);
- if (ret)
- goto err_free_buf;
-
- /* Set the internal oob buffer location, just after the page data */
- chip->oob_poi = chip->data_buf + mtd->writesize;
-
- /*
- * If no default placement scheme is given, select an appropriate one.
- */
- if (!mtd->ooblayout &&
- !(ecc->mode == NAND_ECC_SOFT && ecc->algo == NAND_ECC_BCH)) {
- switch (mtd->oobsize) {
- case 8:
- case 16:
- mtd_set_ooblayout(mtd, &nand_ooblayout_sp_ops);
- break;
- case 64:
- case 128:
- mtd_set_ooblayout(mtd, &nand_ooblayout_lp_hamming_ops);
- break;
- default:
- /*
- * Expose the whole OOB area to users if ECC_NONE
- * is passed. We could do that for all kind of
- * ->oobsize, but we must keep the old large/small
- * page with ECC layout when ->oobsize <= 128 for
- * compatibility reasons.
- */
- if (ecc->mode == NAND_ECC_NONE) {
- mtd_set_ooblayout(mtd,
- &nand_ooblayout_lp_ops);
- break;
- }
-
- WARN(1, "No oob scheme defined for oobsize %d\n",
- mtd->oobsize);
- ret = -EINVAL;
- goto err_nand_manuf_cleanup;
- }
- }
-
- /*
- * Check ECC mode, default to software if 3byte/512byte hardware ECC is
- * selected and we have 256 byte pagesize fallback to software ECC
- */
-
- switch (ecc->mode) {
- case NAND_ECC_HW_OOB_FIRST:
- /* Similar to NAND_ECC_HW, but a separate read_page handle */
- if (!ecc->calculate || !ecc->correct || !ecc->hwctl) {
- WARN(1, "No ECC functions supplied; hardware ECC not possible\n");
- ret = -EINVAL;
- goto err_nand_manuf_cleanup;
- }
- if (!ecc->read_page)
- ecc->read_page = nand_read_page_hwecc_oob_first;
-
- case NAND_ECC_HW:
- /* Use standard hwecc read page function? */
- if (!ecc->read_page)
- ecc->read_page = nand_read_page_hwecc;
- if (!ecc->write_page)
- ecc->write_page = nand_write_page_hwecc;
- if (!ecc->read_page_raw)
- ecc->read_page_raw = nand_read_page_raw;
- if (!ecc->write_page_raw)
- ecc->write_page_raw = nand_write_page_raw;
- if (!ecc->read_oob)
- ecc->read_oob = nand_read_oob_std;
- if (!ecc->write_oob)
- ecc->write_oob = nand_write_oob_std;
- if (!ecc->read_subpage)
- ecc->read_subpage = nand_read_subpage;
- if (!ecc->write_subpage && ecc->hwctl && ecc->calculate)
- ecc->write_subpage = nand_write_subpage_hwecc;
-
- case NAND_ECC_HW_SYNDROME:
- if ((!ecc->calculate || !ecc->correct || !ecc->hwctl) &&
- (!ecc->read_page ||
- ecc->read_page == nand_read_page_hwecc ||
- !ecc->write_page ||
- ecc->write_page == nand_write_page_hwecc)) {
- WARN(1, "No ECC functions supplied; hardware ECC not possible\n");
- ret = -EINVAL;
- goto err_nand_manuf_cleanup;
- }
- /* Use standard syndrome read/write page function? */
- if (!ecc->read_page)
- ecc->read_page = nand_read_page_syndrome;
- if (!ecc->write_page)
- ecc->write_page = nand_write_page_syndrome;
- if (!ecc->read_page_raw)
- ecc->read_page_raw = nand_read_page_raw_syndrome;
- if (!ecc->write_page_raw)
- ecc->write_page_raw = nand_write_page_raw_syndrome;
- if (!ecc->read_oob)
- ecc->read_oob = nand_read_oob_syndrome;
- if (!ecc->write_oob)
- ecc->write_oob = nand_write_oob_syndrome;
-
- if (mtd->writesize >= ecc->size) {
- if (!ecc->strength) {
- WARN(1, "Driver must set ecc.strength when using hardware ECC\n");
- ret = -EINVAL;
- goto err_nand_manuf_cleanup;
- }
- break;
- }
- pr_warn("%d byte HW ECC not possible on %d byte page size, fallback to SW ECC\n",
- ecc->size, mtd->writesize);
- ecc->mode = NAND_ECC_SOFT;
- ecc->algo = NAND_ECC_HAMMING;
-
- case NAND_ECC_SOFT:
- ret = nand_set_ecc_soft_ops(mtd);
- if (ret) {
- ret = -EINVAL;
- goto err_nand_manuf_cleanup;
- }
- break;
-
- case NAND_ECC_ON_DIE:
- if (!ecc->read_page || !ecc->write_page) {
- WARN(1, "No ECC functions supplied; on-die ECC not possible\n");
- ret = -EINVAL;
- goto err_nand_manuf_cleanup;
- }
- if (!ecc->read_oob)
- ecc->read_oob = nand_read_oob_std;
- if (!ecc->write_oob)
- ecc->write_oob = nand_write_oob_std;
- break;
-
- case NAND_ECC_NONE:
- pr_warn("NAND_ECC_NONE selected by board driver. This is not recommended!\n");
- ecc->read_page = nand_read_page_raw;
- ecc->write_page = nand_write_page_raw;
- ecc->read_oob = nand_read_oob_std;
- ecc->read_page_raw = nand_read_page_raw;
- ecc->write_page_raw = nand_write_page_raw;
- ecc->write_oob = nand_write_oob_std;
- ecc->size = mtd->writesize;
- ecc->bytes = 0;
- ecc->strength = 0;
- break;
-
- default:
- WARN(1, "Invalid NAND_ECC_MODE %d\n", ecc->mode);
- ret = -EINVAL;
- goto err_nand_manuf_cleanup;
- }
-
- if (ecc->correct || ecc->calculate) {
- ecc->calc_buf = kmalloc(mtd->oobsize, GFP_KERNEL);
- ecc->code_buf = kmalloc(mtd->oobsize, GFP_KERNEL);
- if (!ecc->calc_buf || !ecc->code_buf) {
- ret = -ENOMEM;
- goto err_nand_manuf_cleanup;
- }
- }
-
- /* For many systems, the standard OOB write also works for raw */
- if (!ecc->read_oob_raw)
- ecc->read_oob_raw = ecc->read_oob;
- if (!ecc->write_oob_raw)
- ecc->write_oob_raw = ecc->write_oob;
-
- /* propagate ecc info to mtd_info */
- mtd->ecc_strength = ecc->strength;
- mtd->ecc_step_size = ecc->size;
-
- /*
- * Set the number of read / write steps for one page depending on ECC
- * mode.
- */
- ecc->steps = mtd->writesize / ecc->size;
- if (ecc->steps * ecc->size != mtd->writesize) {
- WARN(1, "Invalid ECC parameters\n");
- ret = -EINVAL;
- goto err_nand_manuf_cleanup;
- }
- ecc->total = ecc->steps * ecc->bytes;
- if (ecc->total > mtd->oobsize) {
- WARN(1, "Total number of ECC bytes exceeded oobsize\n");
- ret = -EINVAL;
- goto err_nand_manuf_cleanup;
- }
-
- /*
- * The number of bytes available for a client to place data into
- * the out of band area.
- */
- ret = mtd_ooblayout_count_freebytes(mtd);
- if (ret < 0)
- ret = 0;
-
- mtd->oobavail = ret;
-
- /* ECC sanity check: warn if it's too weak */
- if (!nand_ecc_strength_good(mtd))
- pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n",
- mtd->name);
-
- /* Allow subpage writes up to ecc.steps. Not possible for MLC flash */
- if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) {
- switch (ecc->steps) {
- case 2:
- mtd->subpage_sft = 1;
- break;
- case 4:
- case 8:
- case 16:
- mtd->subpage_sft = 2;
- break;
- }
- }
- chip->subpagesize = mtd->writesize >> mtd->subpage_sft;
-
- /* Initialize state */
- chip->state = FL_READY;
-
- /* Invalidate the pagebuffer reference */
- chip->pagebuf = -1;
-
- /* Large page NAND with SOFT_ECC should support subpage reads */
- switch (ecc->mode) {
- case NAND_ECC_SOFT:
- if (chip->page_shift > 9)
- chip->options |= NAND_SUBPAGE_READ;
- break;
-
- default:
- break;
- }
-
- /* Fill in remaining MTD driver data */
- mtd->type = nand_is_slc(chip) ? MTD_NANDFLASH : MTD_MLCNANDFLASH;
- mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM :
- MTD_CAP_NANDFLASH;
- mtd->_erase = nand_erase;
- mtd->_point = NULL;
- mtd->_unpoint = NULL;
- mtd->_panic_write = panic_nand_write;
- mtd->_read_oob = nand_read_oob;
- mtd->_write_oob = nand_write_oob;
- mtd->_sync = nand_sync;
- mtd->_lock = NULL;
- mtd->_unlock = NULL;
- mtd->_suspend = nand_suspend;
- mtd->_resume = nand_resume;
- mtd->_reboot = nand_shutdown;
- mtd->_block_isreserved = nand_block_isreserved;
- mtd->_block_isbad = nand_block_isbad;
- mtd->_block_markbad = nand_block_markbad;
- mtd->_max_bad_blocks = nand_max_bad_blocks;
- mtd->writebufsize = mtd->writesize;
-
- /*
- * Initialize bitflip_threshold to its default prior scan_bbt() call.
- * scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be
- * properly set.
- */
- if (!mtd->bitflip_threshold)
- mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3, 4);
-
- /* Initialize the ->data_interface field. */
- ret = nand_init_data_interface(chip);
- if (ret)
- goto err_nand_manuf_cleanup;
-
- /* Enter fastest possible mode on all dies. */
- for (i = 0; i < chip->numchips; i++) {
- chip->select_chip(mtd, i);
- ret = nand_setup_data_interface(chip, i);
- chip->select_chip(mtd, -1);
-
- if (ret)
- goto err_nand_manuf_cleanup;
- }
-
- /* Check, if we should skip the bad block table scan */
- if (chip->options & NAND_SKIP_BBTSCAN)
- return 0;
-
- /* Build bad block table */
- ret = chip->scan_bbt(mtd);
- if (ret)
- goto err_nand_manuf_cleanup;
-
- return 0;
-
-
-err_nand_manuf_cleanup:
- nand_manufacturer_cleanup(chip);
-
-err_free_buf:
- kfree(chip->data_buf);
- kfree(ecc->code_buf);
- kfree(ecc->calc_buf);
-
- return ret;
-}
-EXPORT_SYMBOL(nand_scan_tail);
-
-/*
- * is_module_text_address() isn't exported, and it's mostly a pointless
- * test if this is a module _anyway_ -- they'd have to try _really_ hard
- * to call us from in-kernel code if the core NAND support is modular.
- */
-#ifdef MODULE
-#define caller_is_module() (1)
-#else
-#define caller_is_module() \
- is_module_text_address((unsigned long)__builtin_return_address(0))
-#endif
-
-/**
- * nand_scan - [NAND Interface] Scan for the NAND device
- * @mtd: MTD device structure
- * @maxchips: number of chips to scan for
- *
- * This fills out all the uninitialized function pointers with the defaults.
- * The flash ID is read and the mtd/chip structures are filled with the
- * appropriate values.
- */
-int nand_scan(struct mtd_info *mtd, int maxchips)
-{
- int ret;
-
- ret = nand_scan_ident(mtd, maxchips, NULL);
- if (!ret)
- ret = nand_scan_tail(mtd);
- return ret;
-}
-EXPORT_SYMBOL(nand_scan);
-
-/**
- * nand_cleanup - [NAND Interface] Free resources held by the NAND device
- * @chip: NAND chip object
- */
-void nand_cleanup(struct nand_chip *chip)
-{
- if (chip->ecc.mode == NAND_ECC_SOFT &&
- chip->ecc.algo == NAND_ECC_BCH)
- nand_bch_free((struct nand_bch_control *)chip->ecc.priv);
-
- /* Free bad block table memory */
- kfree(chip->bbt);
- kfree(chip->data_buf);
- kfree(chip->ecc.code_buf);
- kfree(chip->ecc.calc_buf);
-
- /* Free bad block descriptor memory */
- if (chip->badblock_pattern && chip->badblock_pattern->options
- & NAND_BBT_DYNAMICSTRUCT)
- kfree(chip->badblock_pattern);
-
- /* Free manufacturer priv data. */
- nand_manufacturer_cleanup(chip);
-}
-EXPORT_SYMBOL_GPL(nand_cleanup);
-
-/**
- * nand_release - [NAND Interface] Unregister the MTD device and free resources
- * held by the NAND device
- * @mtd: MTD device structure
- */
-void nand_release(struct mtd_info *mtd)
-{
- mtd_device_unregister(mtd);
- nand_cleanup(mtd_to_nand(mtd));
-}
-EXPORT_SYMBOL_GPL(nand_release);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com>");
-MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
-MODULE_DESCRIPTION("Generic NAND flash driver code");
diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c
deleted file mode 100644
index 36092850be2c..000000000000
--- a/drivers/mtd/nand/nand_bbt.c
+++ /dev/null
@@ -1,1452 +0,0 @@
-/*
- * Overview:
- * Bad block table support for the NAND driver
- *
- * Copyright © 2004 Thomas Gleixner (tglx@linutronix.de)
- *
- * 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.
- *
- * Description:
- *
- * When nand_scan_bbt is called, then it tries to find the bad block table
- * depending on the options in the BBT descriptor(s). If no flash based BBT
- * (NAND_BBT_USE_FLASH) is specified then the device is scanned for factory
- * marked good / bad blocks. This information is used to create a memory BBT.
- * Once a new bad block is discovered then the "factory" information is updated
- * on the device.
- * If a flash based BBT is specified then the function first tries to find the
- * BBT on flash. If a BBT is found then the contents are read and the memory
- * based BBT is created. If a mirrored BBT is selected then the mirror is
- * searched too and the versions are compared. If the mirror has a greater
- * version number, then the mirror BBT is used to build the memory based BBT.
- * If the tables are not versioned, then we "or" the bad block information.
- * If one of the BBTs is out of date or does not exist it is (re)created.
- * If no BBT exists at all then the device is scanned for factory marked
- * good / bad blocks and the bad block tables are created.
- *
- * For manufacturer created BBTs like the one found on M-SYS DOC devices
- * the BBT is searched and read but never created
- *
- * The auto generated bad block table is located in the last good blocks
- * of the device. The table is mirrored, so it can be updated eventually.
- * The table is marked in the OOB area with an ident pattern and a version
- * number which indicates which of both tables is more up to date. If the NAND
- * controller needs the complete OOB area for the ECC information then the
- * option NAND_BBT_NO_OOB should be used (along with NAND_BBT_USE_FLASH, of
- * course): it moves the ident pattern and the version byte into the data area
- * and the OOB area will remain untouched.
- *
- * The table uses 2 bits per block
- * 11b: block is good
- * 00b: block is factory marked bad
- * 01b, 10b: block is marked bad due to wear
- *
- * The memory bad block table uses the following scheme:
- * 00b: block is good
- * 01b: block is marked bad due to wear
- * 10b: block is reserved (to protect the bbt area)
- * 11b: block is factory marked bad
- *
- * Multichip devices like DOC store the bad block info per floor.
- *
- * Following assumptions are made:
- * - bbts start at a page boundary, if autolocated on a block boundary
- * - the space necessary for a bbt in FLASH does not exceed a block boundary
- *
- */
-
-#include <linux/slab.h>
-#include <linux/types.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/bbm.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/bitops.h>
-#include <linux/delay.h>
-#include <linux/vmalloc.h>
-#include <linux/export.h>
-#include <linux/string.h>
-
-#define BBT_BLOCK_GOOD 0x00
-#define BBT_BLOCK_WORN 0x01
-#define BBT_BLOCK_RESERVED 0x02
-#define BBT_BLOCK_FACTORY_BAD 0x03
-
-#define BBT_ENTRY_MASK 0x03
-#define BBT_ENTRY_SHIFT 2
-
-static int nand_update_bbt(struct mtd_info *mtd, loff_t offs);
-
-static inline uint8_t bbt_get_entry(struct nand_chip *chip, int block)
-{
- uint8_t entry = chip->bbt[block >> BBT_ENTRY_SHIFT];
- entry >>= (block & BBT_ENTRY_MASK) * 2;
- return entry & BBT_ENTRY_MASK;
-}
-
-static inline void bbt_mark_entry(struct nand_chip *chip, int block,
- uint8_t mark)
-{
- uint8_t msk = (mark & BBT_ENTRY_MASK) << ((block & BBT_ENTRY_MASK) * 2);
- chip->bbt[block >> BBT_ENTRY_SHIFT] |= msk;
-}
-
-static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td)
-{
- if (memcmp(buf, td->pattern, td->len))
- return -1;
- return 0;
-}
-
-/**
- * check_pattern - [GENERIC] check if a pattern is in the buffer
- * @buf: the buffer to search
- * @len: the length of buffer to search
- * @paglen: the pagelength
- * @td: search pattern descriptor
- *
- * Check for a pattern at the given place. Used to search bad block tables and
- * good / bad block identifiers.
- */
-static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
-{
- if (td->options & NAND_BBT_NO_OOB)
- return check_pattern_no_oob(buf, td);
-
- /* Compare the pattern */
- if (memcmp(buf + paglen + td->offs, td->pattern, td->len))
- return -1;
-
- return 0;
-}
-
-/**
- * check_short_pattern - [GENERIC] check if a pattern is in the buffer
- * @buf: the buffer to search
- * @td: search pattern descriptor
- *
- * Check for a pattern at the given place. Used to search bad block tables and
- * good / bad block identifiers. Same as check_pattern, but no optional empty
- * check.
- */
-static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td)
-{
- /* Compare the pattern */
- if (memcmp(buf + td->offs, td->pattern, td->len))
- return -1;
- return 0;
-}
-
-/**
- * add_marker_len - compute the length of the marker in data area
- * @td: BBT descriptor used for computation
- *
- * The length will be 0 if the marker is located in OOB area.
- */
-static u32 add_marker_len(struct nand_bbt_descr *td)
-{
- u32 len;
-
- if (!(td->options & NAND_BBT_NO_OOB))
- return 0;
-
- len = td->len;
- if (td->options & NAND_BBT_VERSION)
- len++;
- return len;
-}
-
-/**
- * read_bbt - [GENERIC] Read the bad block table starting from page
- * @mtd: MTD device structure
- * @buf: temporary buffer
- * @page: the starting page
- * @num: the number of bbt descriptors to read
- * @td: the bbt describtion table
- * @offs: block number offset in the table
- *
- * Read the bad block table starting from page.
- */
-static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
- struct nand_bbt_descr *td, int offs)
-{
- int res, ret = 0, i, j, act = 0;
- struct nand_chip *this = mtd_to_nand(mtd);
- size_t retlen, len, totlen;
- loff_t from;
- int bits = td->options & NAND_BBT_NRBITS_MSK;
- uint8_t msk = (uint8_t)((1 << bits) - 1);
- u32 marker_len;
- int reserved_block_code = td->reserved_block_code;
-
- totlen = (num * bits) >> 3;
- marker_len = add_marker_len(td);
- from = ((loff_t)page) << this->page_shift;
-
- while (totlen) {
- len = min(totlen, (size_t)(1 << this->bbt_erase_shift));
- if (marker_len) {
- /*
- * In case the BBT marker is not in the OOB area it
- * will be just in the first page.
- */
- len -= marker_len;
- from += marker_len;
- marker_len = 0;
- }
- res = mtd_read(mtd, from, len, &retlen, buf);
- if (res < 0) {
- if (mtd_is_eccerr(res)) {
- pr_info("nand_bbt: ECC error in BBT at 0x%012llx\n",
- from & ~mtd->writesize);
- return res;
- } else if (mtd_is_bitflip(res)) {
- pr_info("nand_bbt: corrected error in BBT at 0x%012llx\n",
- from & ~mtd->writesize);
- ret = res;
- } else {
- pr_info("nand_bbt: error reading BBT\n");
- return res;
- }
- }
-
- /* Analyse data */
- for (i = 0; i < len; i++) {
- uint8_t dat = buf[i];
- for (j = 0; j < 8; j += bits, act++) {
- uint8_t tmp = (dat >> j) & msk;
- if (tmp == msk)
- continue;
- if (reserved_block_code && (tmp == reserved_block_code)) {
- pr_info("nand_read_bbt: reserved block at 0x%012llx\n",
- (loff_t)(offs + act) <<
- this->bbt_erase_shift);
- bbt_mark_entry(this, offs + act,
- BBT_BLOCK_RESERVED);
- mtd->ecc_stats.bbtblocks++;
- continue;
- }
- /*
- * Leave it for now, if it's matured we can
- * move this message to pr_debug.
- */
- pr_info("nand_read_bbt: bad block at 0x%012llx\n",
- (loff_t)(offs + act) <<
- this->bbt_erase_shift);
- /* Factory marked bad or worn out? */
- if (tmp == 0)
- bbt_mark_entry(this, offs + act,
- BBT_BLOCK_FACTORY_BAD);
- else
- bbt_mark_entry(this, offs + act,
- BBT_BLOCK_WORN);
- mtd->ecc_stats.badblocks++;
- }
- }
- totlen -= len;
- from += len;
- }
- return ret;
-}
-
-/**
- * read_abs_bbt - [GENERIC] Read the bad block table starting at a given page
- * @mtd: MTD device structure
- * @buf: temporary buffer
- * @td: descriptor for the bad block table
- * @chip: read the table for a specific chip, -1 read all chips; applies only if
- * NAND_BBT_PERCHIP option is set
- *
- * Read the bad block table for all chips starting at a given page. We assume
- * that the bbt bits are in consecutive order.
- */
-static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- int res = 0, i;
-
- if (td->options & NAND_BBT_PERCHIP) {
- int offs = 0;
- for (i = 0; i < this->numchips; i++) {
- if (chip == -1 || chip == i)
- res = read_bbt(mtd, buf, td->pages[i],
- this->chipsize >> this->bbt_erase_shift,
- td, offs);
- if (res)
- return res;
- offs += this->chipsize >> this->bbt_erase_shift;
- }
- } else {
- res = read_bbt(mtd, buf, td->pages[0],
- mtd->size >> this->bbt_erase_shift, td, 0);
- if (res)
- return res;
- }
- return 0;
-}
-
-/* BBT marker is in the first page, no OOB */
-static int scan_read_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
- struct nand_bbt_descr *td)
-{
- size_t retlen;
- size_t len;
-
- len = td->len;
- if (td->options & NAND_BBT_VERSION)
- len++;
-
- return mtd_read(mtd, offs, len, &retlen, buf);
-}
-
-/**
- * scan_read_oob - [GENERIC] Scan data+OOB region to buffer
- * @mtd: MTD device structure
- * @buf: temporary buffer
- * @offs: offset at which to scan
- * @len: length of data region to read
- *
- * Scan read data from data+OOB. May traverse multiple pages, interleaving
- * page,OOB,page,OOB,... in buf. Completes transfer and returns the "strongest"
- * ECC condition (error or bitflip). May quit on the first (non-ECC) error.
- */
-static int scan_read_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
- size_t len)
-{
- struct mtd_oob_ops ops;
- int res, ret = 0;
-
- ops.mode = MTD_OPS_PLACE_OOB;
- ops.ooboffs = 0;
- ops.ooblen = mtd->oobsize;
-
- while (len > 0) {
- ops.datbuf = buf;
- ops.len = min(len, (size_t)mtd->writesize);
- ops.oobbuf = buf + ops.len;
-
- res = mtd_read_oob(mtd, offs, &ops);
- if (res) {
- if (!mtd_is_bitflip_or_eccerr(res))
- return res;
- else if (mtd_is_eccerr(res) || !ret)
- ret = res;
- }
-
- buf += mtd->oobsize + mtd->writesize;
- len -= mtd->writesize;
- offs += mtd->writesize;
- }
- return ret;
-}
-
-static int scan_read(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
- size_t len, struct nand_bbt_descr *td)
-{
- if (td->options & NAND_BBT_NO_OOB)
- return scan_read_data(mtd, buf, offs, td);
- else
- return scan_read_oob(mtd, buf, offs, len);
-}
-
-/* Scan write data with oob to flash */
-static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len,
- uint8_t *buf, uint8_t *oob)
-{
- struct mtd_oob_ops ops;
-
- ops.mode = MTD_OPS_PLACE_OOB;
- ops.ooboffs = 0;
- ops.ooblen = mtd->oobsize;
- ops.datbuf = buf;
- ops.oobbuf = oob;
- ops.len = len;
-
- return mtd_write_oob(mtd, offs, &ops);
-}
-
-static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td)
-{
- u32 ver_offs = td->veroffs;
-
- if (!(td->options & NAND_BBT_NO_OOB))
- ver_offs += mtd->writesize;
- return ver_offs;
-}
-
-/**
- * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page
- * @mtd: MTD device structure
- * @buf: temporary buffer
- * @td: descriptor for the bad block table
- * @md: descriptor for the bad block table mirror
- *
- * Read the bad block table(s) for all chips starting at a given page. We
- * assume that the bbt bits are in consecutive order.
- */
-static void read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
- struct nand_bbt_descr *td, struct nand_bbt_descr *md)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
-
- /* Read the primary version, if available */
- if (td->options & NAND_BBT_VERSION) {
- scan_read(mtd, buf, (loff_t)td->pages[0] << this->page_shift,
- mtd->writesize, td);
- td->version[0] = buf[bbt_get_ver_offs(mtd, td)];
- pr_info("Bad block table at page %d, version 0x%02X\n",
- td->pages[0], td->version[0]);
- }
-
- /* Read the mirror version, if available */
- if (md && (md->options & NAND_BBT_VERSION)) {
- scan_read(mtd, buf, (loff_t)md->pages[0] << this->page_shift,
- mtd->writesize, md);
- md->version[0] = buf[bbt_get_ver_offs(mtd, md)];
- pr_info("Bad block table at page %d, version 0x%02X\n",
- md->pages[0], md->version[0]);
- }
-}
-
-/* Scan a given block partially */
-static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd,
- loff_t offs, uint8_t *buf, int numpages)
-{
- struct mtd_oob_ops ops;
- int j, ret;
-
- ops.ooblen = mtd->oobsize;
- ops.oobbuf = buf;
- ops.ooboffs = 0;
- ops.datbuf = NULL;
- ops.mode = MTD_OPS_PLACE_OOB;
-
- for (j = 0; j < numpages; j++) {
- /*
- * Read the full oob until read_oob is fixed to handle single
- * byte reads for 16 bit buswidth.
- */
- ret = mtd_read_oob(mtd, offs, &ops);
- /* Ignore ECC errors when checking for BBM */
- if (ret && !mtd_is_bitflip_or_eccerr(ret))
- return ret;
-
- if (check_short_pattern(buf, bd))
- return 1;
-
- offs += mtd->writesize;
- }
- return 0;
-}
-
-/**
- * create_bbt - [GENERIC] Create a bad block table by scanning the device
- * @mtd: MTD device structure
- * @buf: temporary buffer
- * @bd: descriptor for the good/bad block search pattern
- * @chip: create the table for a specific chip, -1 read all chips; applies only
- * if NAND_BBT_PERCHIP option is set
- *
- * Create a bad block table by scanning the device for the given good/bad block
- * identify pattern.
- */
-static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
- struct nand_bbt_descr *bd, int chip)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- int i, numblocks, numpages;
- int startblock;
- loff_t from;
-
- pr_info("Scanning device for bad blocks\n");
-
- if (bd->options & NAND_BBT_SCAN2NDPAGE)
- numpages = 2;
- else
- numpages = 1;
-
- if (chip == -1) {
- numblocks = mtd->size >> this->bbt_erase_shift;
- startblock = 0;
- from = 0;
- } else {
- if (chip >= this->numchips) {
- pr_warn("create_bbt(): chipnr (%d) > available chips (%d)\n",
- chip + 1, this->numchips);
- return -EINVAL;
- }
- numblocks = this->chipsize >> this->bbt_erase_shift;
- startblock = chip * numblocks;
- numblocks += startblock;
- from = (loff_t)startblock << this->bbt_erase_shift;
- }
-
- if (this->bbt_options & NAND_BBT_SCANLASTPAGE)
- from += mtd->erasesize - (mtd->writesize * numpages);
-
- for (i = startblock; i < numblocks; i++) {
- int ret;
-
- BUG_ON(bd->options & NAND_BBT_NO_OOB);
-
- ret = scan_block_fast(mtd, bd, from, buf, numpages);
- if (ret < 0)
- return ret;
-
- if (ret) {
- bbt_mark_entry(this, i, BBT_BLOCK_FACTORY_BAD);
- pr_warn("Bad eraseblock %d at 0x%012llx\n",
- i, (unsigned long long)from);
- mtd->ecc_stats.badblocks++;
- }
-
- from += (1 << this->bbt_erase_shift);
- }
- return 0;
-}
-
-/**
- * search_bbt - [GENERIC] scan the device for a specific bad block table
- * @mtd: MTD device structure
- * @buf: temporary buffer
- * @td: descriptor for the bad block table
- *
- * Read the bad block table by searching for a given ident pattern. Search is
- * preformed either from the beginning up or from the end of the device
- * downwards. The search starts always at the start of a block. If the option
- * NAND_BBT_PERCHIP is given, each chip is searched for a bbt, which contains
- * the bad block information of this chip. This is necessary to provide support
- * for certain DOC devices.
- *
- * The bbt ident pattern resides in the oob area of the first page in a block.
- */
-static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- int i, chips;
- int startblock, block, dir;
- int scanlen = mtd->writesize + mtd->oobsize;
- int bbtblocks;
- int blocktopage = this->bbt_erase_shift - this->page_shift;
-
- /* Search direction top -> down? */
- if (td->options & NAND_BBT_LASTBLOCK) {
- startblock = (mtd->size >> this->bbt_erase_shift) - 1;
- dir = -1;
- } else {
- startblock = 0;
- dir = 1;
- }
-
- /* Do we have a bbt per chip? */
- if (td->options & NAND_BBT_PERCHIP) {
- chips = this->numchips;
- bbtblocks = this->chipsize >> this->bbt_erase_shift;
- startblock &= bbtblocks - 1;
- } else {
- chips = 1;
- bbtblocks = mtd->size >> this->bbt_erase_shift;
- }
-
- for (i = 0; i < chips; i++) {
- /* Reset version information */
- td->version[i] = 0;
- td->pages[i] = -1;
- /* Scan the maximum number of blocks */
- for (block = 0; block < td->maxblocks; block++) {
-
- int actblock = startblock + dir * block;
- loff_t offs = (loff_t)actblock << this->bbt_erase_shift;
-
- /* Read first page */
- scan_read(mtd, buf, offs, mtd->writesize, td);
- if (!check_pattern(buf, scanlen, mtd->writesize, td)) {
- td->pages[i] = actblock << blocktopage;
- if (td->options & NAND_BBT_VERSION) {
- offs = bbt_get_ver_offs(mtd, td);
- td->version[i] = buf[offs];
- }
- break;
- }
- }
- startblock += this->chipsize >> this->bbt_erase_shift;
- }
- /* Check, if we found a bbt for each requested chip */
- for (i = 0; i < chips; i++) {
- if (td->pages[i] == -1)
- pr_warn("Bad block table not found for chip %d\n", i);
- else
- pr_info("Bad block table found at page %d, version 0x%02X\n",
- td->pages[i], td->version[i]);
- }
- return 0;
-}
-
-/**
- * search_read_bbts - [GENERIC] scan the device for bad block table(s)
- * @mtd: MTD device structure
- * @buf: temporary buffer
- * @td: descriptor for the bad block table
- * @md: descriptor for the bad block table mirror
- *
- * Search and read the bad block table(s).
- */
-static void search_read_bbts(struct mtd_info *mtd, uint8_t *buf,
- struct nand_bbt_descr *td,
- struct nand_bbt_descr *md)
-{
- /* Search the primary table */
- search_bbt(mtd, buf, td);
-
- /* Search the mirror table */
- if (md)
- search_bbt(mtd, buf, md);
-}
-
-/**
- * get_bbt_block - Get the first valid eraseblock suitable to store a BBT
- * @this: the NAND device
- * @td: the BBT description
- * @md: the mirror BBT descriptor
- * @chip: the CHIP selector
- *
- * This functions returns a positive block number pointing a valid eraseblock
- * suitable to store a BBT (i.e. in the range reserved for BBT), or -ENOSPC if
- * all blocks are already used of marked bad. If td->pages[chip] was already
- * pointing to a valid block we re-use it, otherwise we search for the next
- * valid one.
- */
-static int get_bbt_block(struct nand_chip *this, struct nand_bbt_descr *td,
- struct nand_bbt_descr *md, int chip)
-{
- int startblock, dir, page, numblocks, i;
-
- /*
- * There was already a version of the table, reuse the page. This
- * applies for absolute placement too, as we have the page number in
- * td->pages.
- */
- if (td->pages[chip] != -1)
- return td->pages[chip] >>
- (this->bbt_erase_shift - this->page_shift);
-
- numblocks = (int)(this->chipsize >> this->bbt_erase_shift);
- if (!(td->options & NAND_BBT_PERCHIP))
- numblocks *= this->numchips;
-
- /*
- * Automatic placement of the bad block table. Search direction
- * top -> down?
- */
- if (td->options & NAND_BBT_LASTBLOCK) {
- startblock = numblocks * (chip + 1) - 1;
- dir = -1;
- } else {
- startblock = chip * numblocks;
- dir = 1;
- }
-
- for (i = 0; i < td->maxblocks; i++) {
- int block = startblock + dir * i;
-
- /* Check, if the block is bad */
- switch (bbt_get_entry(this, block)) {
- case BBT_BLOCK_WORN:
- case BBT_BLOCK_FACTORY_BAD:
- continue;
- }
-
- page = block << (this->bbt_erase_shift - this->page_shift);
-
- /* Check, if the block is used by the mirror table */
- if (!md || md->pages[chip] != page)
- return block;
- }
-
- return -ENOSPC;
-}
-
-/**
- * mark_bbt_block_bad - Mark one of the block reserved for BBT bad
- * @this: the NAND device
- * @td: the BBT description
- * @chip: the CHIP selector
- * @block: the BBT block to mark
- *
- * Blocks reserved for BBT can become bad. This functions is an helper to mark
- * such blocks as bad. It takes care of updating the in-memory BBT, marking the
- * block as bad using a bad block marker and invalidating the associated
- * td->pages[] entry.
- */
-static void mark_bbt_block_bad(struct nand_chip *this,
- struct nand_bbt_descr *td,
- int chip, int block)
-{
- struct mtd_info *mtd = nand_to_mtd(this);
- loff_t to;
- int res;
-
- bbt_mark_entry(this, block, BBT_BLOCK_WORN);
-
- to = (loff_t)block << this->bbt_erase_shift;
- res = this->block_markbad(mtd, to);
- if (res)
- pr_warn("nand_bbt: error %d while marking block %d bad\n",
- res, block);
-
- td->pages[chip] = -1;
-}
-
-/**
- * write_bbt - [GENERIC] (Re)write the bad block table
- * @mtd: MTD device structure
- * @buf: temporary buffer
- * @td: descriptor for the bad block table
- * @md: descriptor for the bad block table mirror
- * @chipsel: selector for a specific chip, -1 for all
- *
- * (Re)write the bad block table.
- */
-static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
- struct nand_bbt_descr *td, struct nand_bbt_descr *md,
- int chipsel)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct erase_info einfo;
- int i, res, chip = 0;
- int bits, page, offs, numblocks, sft, sftmsk;
- int nrchips, pageoffs, ooboffs;
- uint8_t msk[4];
- uint8_t rcode = td->reserved_block_code;
- size_t retlen, len = 0;
- loff_t to;
- struct mtd_oob_ops ops;
-
- ops.ooblen = mtd->oobsize;
- ops.ooboffs = 0;
- ops.datbuf = NULL;
- ops.mode = MTD_OPS_PLACE_OOB;
-
- if (!rcode)
- rcode = 0xff;
- /* Write bad block table per chip rather than per device? */
- if (td->options & NAND_BBT_PERCHIP) {
- numblocks = (int)(this->chipsize >> this->bbt_erase_shift);
- /* Full device write or specific chip? */
- if (chipsel == -1) {
- nrchips = this->numchips;
- } else {
- nrchips = chipsel + 1;
- chip = chipsel;
- }
- } else {
- numblocks = (int)(mtd->size >> this->bbt_erase_shift);
- nrchips = 1;
- }
-
- /* Loop through the chips */
- while (chip < nrchips) {
- int block;
-
- block = get_bbt_block(this, td, md, chip);
- if (block < 0) {
- pr_err("No space left to write bad block table\n");
- res = block;
- goto outerr;
- }
-
- /*
- * get_bbt_block() returns a block number, shift the value to
- * get a page number.
- */
- page = block << (this->bbt_erase_shift - this->page_shift);
-
- /* Set up shift count and masks for the flash table */
- bits = td->options & NAND_BBT_NRBITS_MSK;
- msk[2] = ~rcode;
- switch (bits) {
- case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01;
- msk[3] = 0x01;
- break;
- case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01;
- msk[3] = 0x03;
- break;
- case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C;
- msk[3] = 0x0f;
- break;
- case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F;
- msk[3] = 0xff;
- break;
- default: return -EINVAL;
- }
-
- to = ((loff_t)page) << this->page_shift;
-
- /* Must we save the block contents? */
- if (td->options & NAND_BBT_SAVECONTENT) {
- /* Make it block aligned */
- to &= ~(((loff_t)1 << this->bbt_erase_shift) - 1);
- len = 1 << this->bbt_erase_shift;
- res = mtd_read(mtd, to, len, &retlen, buf);
- if (res < 0) {
- if (retlen != len) {
- pr_info("nand_bbt: error reading block for writing the bad block table\n");
- return res;
- }
- pr_warn("nand_bbt: ECC error while reading block for writing bad block table\n");
- }
- /* Read oob data */
- ops.ooblen = (len >> this->page_shift) * mtd->oobsize;
- ops.oobbuf = &buf[len];
- res = mtd_read_oob(mtd, to + mtd->writesize, &ops);
- if (res < 0 || ops.oobretlen != ops.ooblen)
- goto outerr;
-
- /* Calc the byte offset in the buffer */
- pageoffs = page - (int)(to >> this->page_shift);
- offs = pageoffs << this->page_shift;
- /* Preset the bbt area with 0xff */
- memset(&buf[offs], 0xff, (size_t)(numblocks >> sft));
- ooboffs = len + (pageoffs * mtd->oobsize);
-
- } else if (td->options & NAND_BBT_NO_OOB) {
- ooboffs = 0;
- offs = td->len;
- /* The version byte */
- if (td->options & NAND_BBT_VERSION)
- offs++;
- /* Calc length */
- len = (size_t)(numblocks >> sft);
- len += offs;
- /* Make it page aligned! */
- len = ALIGN(len, mtd->writesize);
- /* Preset the buffer with 0xff */
- memset(buf, 0xff, len);
- /* Pattern is located at the begin of first page */
- memcpy(buf, td->pattern, td->len);
- } else {
- /* Calc length */
- len = (size_t)(numblocks >> sft);
- /* Make it page aligned! */
- len = ALIGN(len, mtd->writesize);
- /* Preset the buffer with 0xff */
- memset(buf, 0xff, len +
- (len >> this->page_shift)* mtd->oobsize);
- offs = 0;
- ooboffs = len;
- /* Pattern is located in oob area of first page */
- memcpy(&buf[ooboffs + td->offs], td->pattern, td->len);
- }
-
- if (td->options & NAND_BBT_VERSION)
- buf[ooboffs + td->veroffs] = td->version[chip];
-
- /* Walk through the memory table */
- for (i = 0; i < numblocks; i++) {
- uint8_t dat;
- int sftcnt = (i << (3 - sft)) & sftmsk;
- dat = bbt_get_entry(this, chip * numblocks + i);
- /* Do not store the reserved bbt blocks! */
- buf[offs + (i >> sft)] &= ~(msk[dat] << sftcnt);
- }
-
- memset(&einfo, 0, sizeof(einfo));
- einfo.mtd = mtd;
- einfo.addr = to;
- einfo.len = 1 << this->bbt_erase_shift;
- res = nand_erase_nand(mtd, &einfo, 1);
- if (res < 0) {
- pr_warn("nand_bbt: error while erasing BBT block %d\n",
- res);
- mark_bbt_block_bad(this, td, chip, block);
- continue;
- }
-
- res = scan_write_bbt(mtd, to, len, buf,
- td->options & NAND_BBT_NO_OOB ? NULL :
- &buf[len]);
- if (res < 0) {
- pr_warn("nand_bbt: error while writing BBT block %d\n",
- res);
- mark_bbt_block_bad(this, td, chip, block);
- continue;
- }
-
- pr_info("Bad block table written to 0x%012llx, version 0x%02X\n",
- (unsigned long long)to, td->version[chip]);
-
- /* Mark it as used */
- td->pages[chip++] = page;
- }
- return 0;
-
- outerr:
- pr_warn("nand_bbt: error while writing bad block table %d\n", res);
- return res;
-}
-
-/**
- * nand_memory_bbt - [GENERIC] create a memory based bad block table
- * @mtd: MTD device structure
- * @bd: descriptor for the good/bad block search pattern
- *
- * The function creates a memory based bbt by scanning the device for
- * manufacturer / software marked good / bad blocks.
- */
-static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
-
- return create_bbt(mtd, this->data_buf, bd, -1);
-}
-
-/**
- * check_create - [GENERIC] create and write bbt(s) if necessary
- * @mtd: MTD device structure
- * @buf: temporary buffer
- * @bd: descriptor for the good/bad block search pattern
- *
- * The function checks the results of the previous call to read_bbt and creates
- * / updates the bbt(s) if necessary. Creation is necessary if no bbt was found
- * for the chip/device. Update is necessary if one of the tables is missing or
- * the version nr. of one table is less than the other.
- */
-static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd)
-{
- int i, chips, writeops, create, chipsel, res, res2;
- struct nand_chip *this = mtd_to_nand(mtd);
- struct nand_bbt_descr *td = this->bbt_td;
- struct nand_bbt_descr *md = this->bbt_md;
- struct nand_bbt_descr *rd, *rd2;
-
- /* Do we have a bbt per chip? */
- if (td->options & NAND_BBT_PERCHIP)
- chips = this->numchips;
- else
- chips = 1;
-
- for (i = 0; i < chips; i++) {
- writeops = 0;
- create = 0;
- rd = NULL;
- rd2 = NULL;
- res = res2 = 0;
- /* Per chip or per device? */
- chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1;
- /* Mirrored table available? */
- if (md) {
- if (td->pages[i] == -1 && md->pages[i] == -1) {
- create = 1;
- writeops = 0x03;
- } else if (td->pages[i] == -1) {
- rd = md;
- writeops = 0x01;
- } else if (md->pages[i] == -1) {
- rd = td;
- writeops = 0x02;
- } else if (td->version[i] == md->version[i]) {
- rd = td;
- if (!(td->options & NAND_BBT_VERSION))
- rd2 = md;
- } else if (((int8_t)(td->version[i] - md->version[i])) > 0) {
- rd = td;
- writeops = 0x02;
- } else {
- rd = md;
- writeops = 0x01;
- }
- } else {
- if (td->pages[i] == -1) {
- create = 1;
- writeops = 0x01;
- } else {
- rd = td;
- }
- }
-
- if (create) {
- /* Create the bad block table by scanning the device? */
- if (!(td->options & NAND_BBT_CREATE))
- continue;
-
- /* Create the table in memory by scanning the chip(s) */
- if (!(this->bbt_options & NAND_BBT_CREATE_EMPTY))
- create_bbt(mtd, buf, bd, chipsel);
-
- td->version[i] = 1;
- if (md)
- md->version[i] = 1;
- }
-
- /* Read back first? */
- if (rd) {
- res = read_abs_bbt(mtd, buf, rd, chipsel);
- if (mtd_is_eccerr(res)) {
- /* Mark table as invalid */
- rd->pages[i] = -1;
- rd->version[i] = 0;
- i--;
- continue;
- }
- }
- /* If they weren't versioned, read both */
- if (rd2) {
- res2 = read_abs_bbt(mtd, buf, rd2, chipsel);
- if (mtd_is_eccerr(res2)) {
- /* Mark table as invalid */
- rd2->pages[i] = -1;
- rd2->version[i] = 0;
- i--;
- continue;
- }
- }
-
- /* Scrub the flash table(s)? */
- if (mtd_is_bitflip(res) || mtd_is_bitflip(res2))
- writeops = 0x03;
-
- /* Update version numbers before writing */
- if (md) {
- td->version[i] = max(td->version[i], md->version[i]);
- md->version[i] = td->version[i];
- }
-
- /* Write the bad block table to the device? */
- if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
- res = write_bbt(mtd, buf, td, md, chipsel);
- if (res < 0)
- return res;
- }
-
- /* Write the mirror bad block table to the device? */
- if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
- res = write_bbt(mtd, buf, md, td, chipsel);
- if (res < 0)
- return res;
- }
- }
- return 0;
-}
-
-/**
- * mark_bbt_regions - [GENERIC] mark the bad block table regions
- * @mtd: MTD device structure
- * @td: bad block table descriptor
- *
- * The bad block table regions are marked as "bad" to prevent accidental
- * erasures / writes. The regions are identified by the mark 0x02.
- */
-static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- int i, j, chips, block, nrblocks, update;
- uint8_t oldval;
-
- /* Do we have a bbt per chip? */
- if (td->options & NAND_BBT_PERCHIP) {
- chips = this->numchips;
- nrblocks = (int)(this->chipsize >> this->bbt_erase_shift);
- } else {
- chips = 1;
- nrblocks = (int)(mtd->size >> this->bbt_erase_shift);
- }
-
- for (i = 0; i < chips; i++) {
- if ((td->options & NAND_BBT_ABSPAGE) ||
- !(td->options & NAND_BBT_WRITE)) {
- if (td->pages[i] == -1)
- continue;
- block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift);
- oldval = bbt_get_entry(this, block);
- bbt_mark_entry(this, block, BBT_BLOCK_RESERVED);
- if ((oldval != BBT_BLOCK_RESERVED) &&
- td->reserved_block_code)
- nand_update_bbt(mtd, (loff_t)block <<
- this->bbt_erase_shift);
- continue;
- }
- update = 0;
- if (td->options & NAND_BBT_LASTBLOCK)
- block = ((i + 1) * nrblocks) - td->maxblocks;
- else
- block = i * nrblocks;
- for (j = 0; j < td->maxblocks; j++) {
- oldval = bbt_get_entry(this, block);
- bbt_mark_entry(this, block, BBT_BLOCK_RESERVED);
- if (oldval != BBT_BLOCK_RESERVED)
- update = 1;
- block++;
- }
- /*
- * If we want reserved blocks to be recorded to flash, and some
- * new ones have been marked, then we need to update the stored
- * bbts. This should only happen once.
- */
- if (update && td->reserved_block_code)
- nand_update_bbt(mtd, (loff_t)(block - 1) <<
- this->bbt_erase_shift);
- }
-}
-
-/**
- * verify_bbt_descr - verify the bad block description
- * @mtd: MTD device structure
- * @bd: the table to verify
- *
- * This functions performs a few sanity checks on the bad block description
- * table.
- */
-static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- u32 pattern_len;
- u32 bits;
- u32 table_size;
-
- if (!bd)
- return;
-
- pattern_len = bd->len;
- bits = bd->options & NAND_BBT_NRBITS_MSK;
-
- BUG_ON((this->bbt_options & NAND_BBT_NO_OOB) &&
- !(this->bbt_options & NAND_BBT_USE_FLASH));
- BUG_ON(!bits);
-
- if (bd->options & NAND_BBT_VERSION)
- pattern_len++;
-
- if (bd->options & NAND_BBT_NO_OOB) {
- BUG_ON(!(this->bbt_options & NAND_BBT_USE_FLASH));
- BUG_ON(!(this->bbt_options & NAND_BBT_NO_OOB));
- BUG_ON(bd->offs);
- if (bd->options & NAND_BBT_VERSION)
- BUG_ON(bd->veroffs != bd->len);
- BUG_ON(bd->options & NAND_BBT_SAVECONTENT);
- }
-
- if (bd->options & NAND_BBT_PERCHIP)
- table_size = this->chipsize >> this->bbt_erase_shift;
- else
- table_size = mtd->size >> this->bbt_erase_shift;
- table_size >>= 3;
- table_size *= bits;
- if (bd->options & NAND_BBT_NO_OOB)
- table_size += pattern_len;
- BUG_ON(table_size > (1 << this->bbt_erase_shift));
-}
-
-/**
- * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s)
- * @mtd: MTD device structure
- * @bd: descriptor for the good/bad block search pattern
- *
- * The function checks, if a bad block table(s) is/are already available. If
- * not it scans the device for manufacturer marked good / bad blocks and writes
- * the bad block table(s) to the selected place.
- *
- * The bad block table memory is allocated here. It must be freed by calling
- * the nand_free_bbt function.
- */
-static int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- int len, res;
- uint8_t *buf;
- struct nand_bbt_descr *td = this->bbt_td;
- struct nand_bbt_descr *md = this->bbt_md;
-
- len = (mtd->size >> (this->bbt_erase_shift + 2)) ? : 1;
- /*
- * Allocate memory (2bit per block) and clear the memory bad block
- * table.
- */
- this->bbt = kzalloc(len, GFP_KERNEL);
- if (!this->bbt)
- return -ENOMEM;
-
- /*
- * If no primary table decriptor is given, scan the device to build a
- * memory based bad block table.
- */
- if (!td) {
- if ((res = nand_memory_bbt(mtd, bd))) {
- pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n");
- goto err;
- }
- return 0;
- }
- verify_bbt_descr(mtd, td);
- verify_bbt_descr(mtd, md);
-
- /* Allocate a temporary buffer for one eraseblock incl. oob */
- len = (1 << this->bbt_erase_shift);
- len += (len >> this->page_shift) * mtd->oobsize;
- buf = vmalloc(len);
- if (!buf) {
- res = -ENOMEM;
- goto err;
- }
-
- /* Is the bbt at a given page? */
- if (td->options & NAND_BBT_ABSPAGE) {
- read_abs_bbts(mtd, buf, td, md);
- } else {
- /* Search the bad block table using a pattern in oob */
- search_read_bbts(mtd, buf, td, md);
- }
-
- res = check_create(mtd, buf, bd);
- if (res)
- goto err;
-
- /* Prevent the bbt regions from erasing / writing */
- mark_bbt_region(mtd, td);
- if (md)
- mark_bbt_region(mtd, md);
-
- vfree(buf);
- return 0;
-
-err:
- kfree(this->bbt);
- this->bbt = NULL;
- return res;
-}
-
-/**
- * nand_update_bbt - update bad block table(s)
- * @mtd: MTD device structure
- * @offs: the offset of the newly marked block
- *
- * The function updates the bad block table(s).
- */
-static int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- int len, res = 0;
- int chip, chipsel;
- uint8_t *buf;
- struct nand_bbt_descr *td = this->bbt_td;
- struct nand_bbt_descr *md = this->bbt_md;
-
- if (!this->bbt || !td)
- return -EINVAL;
-
- /* Allocate a temporary buffer for one eraseblock incl. oob */
- len = (1 << this->bbt_erase_shift);
- len += (len >> this->page_shift) * mtd->oobsize;
- buf = kmalloc(len, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- /* Do we have a bbt per chip? */
- if (td->options & NAND_BBT_PERCHIP) {
- chip = (int)(offs >> this->chip_shift);
- chipsel = chip;
- } else {
- chip = 0;
- chipsel = -1;
- }
-
- td->version[chip]++;
- if (md)
- md->version[chip]++;
-
- /* Write the bad block table to the device? */
- if (td->options & NAND_BBT_WRITE) {
- res = write_bbt(mtd, buf, td, md, chipsel);
- if (res < 0)
- goto out;
- }
- /* Write the mirror bad block table to the device? */
- if (md && (md->options & NAND_BBT_WRITE)) {
- res = write_bbt(mtd, buf, md, td, chipsel);
- }
-
- out:
- kfree(buf);
- return res;
-}
-
-/*
- * Define some generic bad / good block scan pattern which are used
- * while scanning a device for factory marked good / bad blocks.
- */
-static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
-
-/* Generic flash bbt descriptors */
-static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
-static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
-
-static struct nand_bbt_descr bbt_main_descr = {
- .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
- | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
- .offs = 8,
- .len = 4,
- .veroffs = 12,
- .maxblocks = NAND_BBT_SCAN_MAXBLOCKS,
- .pattern = bbt_pattern
-};
-
-static struct nand_bbt_descr bbt_mirror_descr = {
- .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
- | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
- .offs = 8,
- .len = 4,
- .veroffs = 12,
- .maxblocks = NAND_BBT_SCAN_MAXBLOCKS,
- .pattern = mirror_pattern
-};
-
-static struct nand_bbt_descr bbt_main_no_oob_descr = {
- .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
- | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP
- | NAND_BBT_NO_OOB,
- .len = 4,
- .veroffs = 4,
- .maxblocks = NAND_BBT_SCAN_MAXBLOCKS,
- .pattern = bbt_pattern
-};
-
-static struct nand_bbt_descr bbt_mirror_no_oob_descr = {
- .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
- | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP
- | NAND_BBT_NO_OOB,
- .len = 4,
- .veroffs = 4,
- .maxblocks = NAND_BBT_SCAN_MAXBLOCKS,
- .pattern = mirror_pattern
-};
-
-#define BADBLOCK_SCAN_MASK (~NAND_BBT_NO_OOB)
-/**
- * nand_create_badblock_pattern - [INTERN] Creates a BBT descriptor structure
- * @this: NAND chip to create descriptor for
- *
- * This function allocates and initializes a nand_bbt_descr for BBM detection
- * based on the properties of @this. The new descriptor is stored in
- * this->badblock_pattern. Thus, this->badblock_pattern should be NULL when
- * passed to this function.
- */
-static int nand_create_badblock_pattern(struct nand_chip *this)
-{
- struct nand_bbt_descr *bd;
- if (this->badblock_pattern) {
- pr_warn("Bad block pattern already allocated; not replacing\n");
- return -EINVAL;
- }
- bd = kzalloc(sizeof(*bd), GFP_KERNEL);
- if (!bd)
- return -ENOMEM;
- bd->options = this->bbt_options & BADBLOCK_SCAN_MASK;
- bd->offs = this->badblockpos;
- bd->len = (this->options & NAND_BUSWIDTH_16) ? 2 : 1;
- bd->pattern = scan_ff_pattern;
- bd->options |= NAND_BBT_DYNAMICSTRUCT;
- this->badblock_pattern = bd;
- return 0;
-}
-
-/**
- * nand_default_bbt - [NAND Interface] Select a default bad block table for the device
- * @mtd: MTD device structure
- *
- * This function selects the default bad block table support for the device and
- * calls the nand_scan_bbt function.
- */
-int nand_default_bbt(struct mtd_info *mtd)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- int ret;
-
- /* Is a flash based bad block table requested? */
- if (this->bbt_options & NAND_BBT_USE_FLASH) {
- /* Use the default pattern descriptors */
- if (!this->bbt_td) {
- if (this->bbt_options & NAND_BBT_NO_OOB) {
- this->bbt_td = &bbt_main_no_oob_descr;
- this->bbt_md = &bbt_mirror_no_oob_descr;
- } else {
- this->bbt_td = &bbt_main_descr;
- this->bbt_md = &bbt_mirror_descr;
- }
- }
- } else {
- this->bbt_td = NULL;
- this->bbt_md = NULL;
- }
-
- if (!this->badblock_pattern) {
- ret = nand_create_badblock_pattern(this);
- if (ret)
- return ret;
- }
-
- return nand_scan_bbt(mtd, this->badblock_pattern);
-}
-
-/**
- * nand_isreserved_bbt - [NAND Interface] Check if a block is reserved
- * @mtd: MTD device structure
- * @offs: offset in the device
- */
-int nand_isreserved_bbt(struct mtd_info *mtd, loff_t offs)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- int block;
-
- block = (int)(offs >> this->bbt_erase_shift);
- return bbt_get_entry(this, block) == BBT_BLOCK_RESERVED;
-}
-
-/**
- * nand_isbad_bbt - [NAND Interface] Check if a block is bad
- * @mtd: MTD device structure
- * @offs: offset in the device
- * @allowbbt: allow access to bad block table region
- */
-int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- int block, res;
-
- block = (int)(offs >> this->bbt_erase_shift);
- res = bbt_get_entry(this, block);
-
- pr_debug("nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n",
- (unsigned int)offs, block, res);
-
- switch (res) {
- case BBT_BLOCK_GOOD:
- return 0;
- case BBT_BLOCK_WORN:
- return 1;
- case BBT_BLOCK_RESERVED:
- return allowbbt ? 0 : 1;
- }
- return 1;
-}
-
-/**
- * nand_markbad_bbt - [NAND Interface] Mark a block bad in the BBT
- * @mtd: MTD device structure
- * @offs: offset of the bad block
- */
-int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- int block, ret = 0;
-
- block = (int)(offs >> this->bbt_erase_shift);
-
- /* Mark bad block in memory */
- bbt_mark_entry(this, block, BBT_BLOCK_WORN);
-
- /* Update flash-based bad block table */
- if (this->bbt_options & NAND_BBT_USE_FLASH)
- ret = nand_update_bbt(mtd, offs);
-
- return ret;
-}
diff --git a/drivers/mtd/nand/nand_bch.c b/drivers/mtd/nand/nand_bch.c
deleted file mode 100644
index 505441c9373b..000000000000
--- a/drivers/mtd/nand/nand_bch.c
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * This file provides ECC correction for more than 1 bit per block of data,
- * using binary BCH codes. It relies on the generic BCH library lib/bch.c.
- *
- * Copyright © 2011 Ivan Djelic <ivan.djelic@parrot.com>
- *
- * This file 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 file 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 file; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/bitops.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand_bch.h>
-#include <linux/bch.h>
-
-/**
- * struct nand_bch_control - private NAND BCH control structure
- * @bch: BCH control structure
- * @errloc: error location array
- * @eccmask: XOR ecc mask, allows erased pages to be decoded as valid
- */
-struct nand_bch_control {
- struct bch_control *bch;
- unsigned int *errloc;
- unsigned char *eccmask;
-};
-
-/**
- * nand_bch_calculate_ecc - [NAND Interface] Calculate ECC for data block
- * @mtd: MTD block structure
- * @buf: input buffer with raw data
- * @code: output buffer with ECC
- */
-int nand_bch_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf,
- unsigned char *code)
-{
- const struct nand_chip *chip = mtd_to_nand(mtd);
- struct nand_bch_control *nbc = chip->ecc.priv;
- unsigned int i;
-
- memset(code, 0, chip->ecc.bytes);
- encode_bch(nbc->bch, buf, chip->ecc.size, code);
-
- /* apply mask so that an erased page is a valid codeword */
- for (i = 0; i < chip->ecc.bytes; i++)
- code[i] ^= nbc->eccmask[i];
-
- return 0;
-}
-EXPORT_SYMBOL(nand_bch_calculate_ecc);
-
-/**
- * nand_bch_correct_data - [NAND Interface] Detect and correct bit error(s)
- * @mtd: MTD block structure
- * @buf: raw data read from the chip
- * @read_ecc: ECC from the chip
- * @calc_ecc: the ECC calculated from raw data
- *
- * Detect and correct bit errors for a data byte block
- */
-int nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf,
- unsigned char *read_ecc, unsigned char *calc_ecc)
-{
- const struct nand_chip *chip = mtd_to_nand(mtd);
- struct nand_bch_control *nbc = chip->ecc.priv;
- unsigned int *errloc = nbc->errloc;
- int i, count;
-
- count = decode_bch(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc,
- NULL, errloc);
- if (count > 0) {
- for (i = 0; i < count; i++) {
- if (errloc[i] < (chip->ecc.size*8))
- /* error is located in data, correct it */
- buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7));
- /* else error in ecc, no action needed */
-
- pr_debug("%s: corrected bitflip %u\n", __func__,
- errloc[i]);
- }
- } else if (count < 0) {
- printk(KERN_ERR "ecc unrecoverable error\n");
- count = -EBADMSG;
- }
- return count;
-}
-EXPORT_SYMBOL(nand_bch_correct_data);
-
-/**
- * nand_bch_init - [NAND Interface] Initialize NAND BCH error correction
- * @mtd: MTD block structure
- *
- * Returns:
- * a pointer to a new NAND BCH control structure, or NULL upon failure
- *
- * Initialize NAND BCH error correction. Parameters @eccsize and @eccbytes
- * are used to compute BCH parameters m (Galois field order) and t (error
- * correction capability). @eccbytes should be equal to the number of bytes
- * required to store m*t bits, where m is such that 2^m-1 > @eccsize*8.
- *
- * Example: to configure 4 bit correction per 512 bytes, you should pass
- * @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8)
- * @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits)
- */
-struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- unsigned int m, t, eccsteps, i;
- struct nand_bch_control *nbc = NULL;
- unsigned char *erased_page;
- unsigned int eccsize = nand->ecc.size;
- unsigned int eccbytes = nand->ecc.bytes;
- unsigned int eccstrength = nand->ecc.strength;
-
- if (!eccbytes && eccstrength) {
- eccbytes = DIV_ROUND_UP(eccstrength * fls(8 * eccsize), 8);
- nand->ecc.bytes = eccbytes;
- }
-
- if (!eccsize || !eccbytes) {
- printk(KERN_WARNING "ecc parameters not supplied\n");
- goto fail;
- }
-
- m = fls(1+8*eccsize);
- t = (eccbytes*8)/m;
-
- nbc = kzalloc(sizeof(*nbc), GFP_KERNEL);
- if (!nbc)
- goto fail;
-
- nbc->bch = init_bch(m, t, 0);
- if (!nbc->bch)
- goto fail;
-
- /* verify that eccbytes has the expected value */
- if (nbc->bch->ecc_bytes != eccbytes) {
- printk(KERN_WARNING "invalid eccbytes %u, should be %u\n",
- eccbytes, nbc->bch->ecc_bytes);
- goto fail;
- }
-
- eccsteps = mtd->writesize/eccsize;
-
- /* Check that we have an oob layout description. */
- if (!mtd->ooblayout) {
- pr_warn("missing oob scheme");
- goto fail;
- }
-
- /* sanity checks */
- if (8*(eccsize+eccbytes) >= (1 << m)) {
- printk(KERN_WARNING "eccsize %u is too large\n", eccsize);
- goto fail;
- }
-
- /*
- * ecc->steps and ecc->total might be used by mtd->ooblayout->ecc(),
- * which is called by mtd_ooblayout_count_eccbytes().
- * Make sure they are properly initialized before calling
- * mtd_ooblayout_count_eccbytes().
- * FIXME: we should probably rework the sequencing in nand_scan_tail()
- * to avoid setting those fields twice.
- */
- nand->ecc.steps = eccsteps;
- nand->ecc.total = eccsteps * eccbytes;
- if (mtd_ooblayout_count_eccbytes(mtd) != (eccsteps*eccbytes)) {
- printk(KERN_WARNING "invalid ecc layout\n");
- goto fail;
- }
-
- nbc->eccmask = kmalloc(eccbytes, GFP_KERNEL);
- nbc->errloc = kmalloc(t*sizeof(*nbc->errloc), GFP_KERNEL);
- if (!nbc->eccmask || !nbc->errloc)
- goto fail;
- /*
- * compute and store the inverted ecc of an erased ecc block
- */
- erased_page = kmalloc(eccsize, GFP_KERNEL);
- if (!erased_page)
- goto fail;
-
- memset(erased_page, 0xff, eccsize);
- memset(nbc->eccmask, 0, eccbytes);
- encode_bch(nbc->bch, erased_page, eccsize, nbc->eccmask);
- kfree(erased_page);
-
- for (i = 0; i < eccbytes; i++)
- nbc->eccmask[i] ^= 0xff;
-
- if (!eccstrength)
- nand->ecc.strength = (eccbytes * 8) / fls(8 * eccsize);
-
- return nbc;
-fail:
- nand_bch_free(nbc);
- return NULL;
-}
-EXPORT_SYMBOL(nand_bch_init);
-
-/**
- * nand_bch_free - [NAND Interface] Release NAND BCH ECC resources
- * @nbc: NAND BCH control structure
- */
-void nand_bch_free(struct nand_bch_control *nbc)
-{
- if (nbc) {
- free_bch(nbc->bch);
- kfree(nbc->errloc);
- kfree(nbc->eccmask);
- kfree(nbc);
- }
-}
-EXPORT_SYMBOL(nand_bch_free);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Ivan Djelic <ivan.djelic@parrot.com>");
-MODULE_DESCRIPTION("NAND software BCH ECC support");
diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c
deleted file mode 100644
index 7613a0388044..000000000000
--- a/drivers/mtd/nand/nand_ecc.c
+++ /dev/null
@@ -1,533 +0,0 @@
-/*
- * This file contains an ECC algorithm that detects and corrects 1 bit
- * errors in a 256 byte block of data.
- *
- * drivers/mtd/nand/nand_ecc.c
- *
- * Copyright © 2008 Koninklijke Philips Electronics NV.
- * Author: Frans Meulenbroeks
- *
- * Completely replaces the previous ECC implementation which was written by:
- * Steven J. Hill (sjhill@realitydiluted.com)
- * Thomas Gleixner (tglx@linutronix.de)
- *
- * Information on how this algorithm works and how it was developed
- * can be found in Documentation/mtd/nand_ecc.txt
- *
- * This file 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 file 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 file; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- *
- */
-
-/*
- * The STANDALONE macro is useful when running the code outside the kernel
- * e.g. when running the code in a testbed or a benchmark program.
- * When STANDALONE is used, the module related macros are commented out
- * as well as the linux include files.
- * Instead a private definition of mtd_info is given to satisfy the compiler
- * (the code does not use mtd_info, so the code does not care)
- */
-#ifndef STANDALONE
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand_ecc.h>
-#include <asm/byteorder.h>
-#else
-#include <stdint.h>
-struct mtd_info;
-#define EXPORT_SYMBOL(x) /* x */
-
-#define MODULE_LICENSE(x) /* x */
-#define MODULE_AUTHOR(x) /* x */
-#define MODULE_DESCRIPTION(x) /* x */
-
-#define pr_err printf
-#endif
-
-/*
- * invparity is a 256 byte table that contains the odd parity
- * for each byte. So if the number of bits in a byte is even,
- * the array element is 1, and when the number of bits is odd
- * the array eleemnt is 0.
- */
-static const char invparity[256] = {
- 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
- 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
- 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
- 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
- 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
- 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
- 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
- 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
- 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
- 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
- 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
- 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
- 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
- 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
- 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
- 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1
-};
-
-/*
- * bitsperbyte contains the number of bits per byte
- * this is only used for testing and repairing parity
- * (a precalculated value slightly improves performance)
- */
-static const char bitsperbyte[256] = {
- 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
- 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
- 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
- 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
- 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
- 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
- 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
- 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
- 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
- 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
- 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
- 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
- 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
- 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
- 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
- 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8,
-};
-
-/*
- * addressbits is a lookup table to filter out the bits from the xor-ed
- * ECC data that identify the faulty location.
- * this is only used for repairing parity
- * see the comments in nand_correct_data for more details
- */
-static const char addressbits[256] = {
- 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
- 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03,
- 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
- 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03,
- 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05,
- 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07,
- 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05,
- 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07,
- 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
- 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03,
- 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
- 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03,
- 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05,
- 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07,
- 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05,
- 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07,
- 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09,
- 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b,
- 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09,
- 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b,
- 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d,
- 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f,
- 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d,
- 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f,
- 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09,
- 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b,
- 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09,
- 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b,
- 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d,
- 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f,
- 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d,
- 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f
-};
-
-/**
- * __nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte
- * block
- * @buf: input buffer with raw data
- * @eccsize: data bytes per ECC step (256 or 512)
- * @code: output buffer with ECC
- */
-void __nand_calculate_ecc(const unsigned char *buf, unsigned int eccsize,
- unsigned char *code)
-{
- int i;
- const uint32_t *bp = (uint32_t *)buf;
- /* 256 or 512 bytes/ecc */
- const uint32_t eccsize_mult = eccsize >> 8;
- uint32_t cur; /* current value in buffer */
- /* rp0..rp15..rp17 are the various accumulated parities (per byte) */
- uint32_t rp0, rp1, rp2, rp3, rp4, rp5, rp6, rp7;
- uint32_t rp8, rp9, rp10, rp11, rp12, rp13, rp14, rp15, rp16;
- uint32_t uninitialized_var(rp17); /* to make compiler happy */
- uint32_t par; /* the cumulative parity for all data */
- uint32_t tmppar; /* the cumulative parity for this iteration;
- for rp12, rp14 and rp16 at the end of the
- loop */
-
- par = 0;
- rp4 = 0;
- rp6 = 0;
- rp8 = 0;
- rp10 = 0;
- rp12 = 0;
- rp14 = 0;
- rp16 = 0;
-
- /*
- * The loop is unrolled a number of times;
- * This avoids if statements to decide on which rp value to update
- * Also we process the data by longwords.
- * Note: passing unaligned data might give a performance penalty.
- * It is assumed that the buffers are aligned.
- * tmppar is the cumulative sum of this iteration.
- * needed for calculating rp12, rp14, rp16 and par
- * also used as a performance improvement for rp6, rp8 and rp10
- */
- for (i = 0; i < eccsize_mult << 2; i++) {
- cur = *bp++;
- tmppar = cur;
- rp4 ^= cur;
- cur = *bp++;
- tmppar ^= cur;
- rp6 ^= tmppar;
- cur = *bp++;
- tmppar ^= cur;
- rp4 ^= cur;
- cur = *bp++;
- tmppar ^= cur;
- rp8 ^= tmppar;
-
- cur = *bp++;
- tmppar ^= cur;
- rp4 ^= cur;
- rp6 ^= cur;
- cur = *bp++;
- tmppar ^= cur;
- rp6 ^= cur;
- cur = *bp++;
- tmppar ^= cur;
- rp4 ^= cur;
- cur = *bp++;
- tmppar ^= cur;
- rp10 ^= tmppar;
-
- cur = *bp++;
- tmppar ^= cur;
- rp4 ^= cur;
- rp6 ^= cur;
- rp8 ^= cur;
- cur = *bp++;
- tmppar ^= cur;
- rp6 ^= cur;
- rp8 ^= cur;
- cur = *bp++;
- tmppar ^= cur;
- rp4 ^= cur;
- rp8 ^= cur;
- cur = *bp++;
- tmppar ^= cur;
- rp8 ^= cur;
-
- cur = *bp++;
- tmppar ^= cur;
- rp4 ^= cur;
- rp6 ^= cur;
- cur = *bp++;
- tmppar ^= cur;
- rp6 ^= cur;
- cur = *bp++;
- tmppar ^= cur;
- rp4 ^= cur;
- cur = *bp++;
- tmppar ^= cur;
-
- par ^= tmppar;
- if ((i & 0x1) == 0)
- rp12 ^= tmppar;
- if ((i & 0x2) == 0)
- rp14 ^= tmppar;
- if (eccsize_mult == 2 && (i & 0x4) == 0)
- rp16 ^= tmppar;
- }
-
- /*
- * handle the fact that we use longword operations
- * we'll bring rp4..rp14..rp16 back to single byte entities by
- * shifting and xoring first fold the upper and lower 16 bits,
- * then the upper and lower 8 bits.
- */
- rp4 ^= (rp4 >> 16);
- rp4 ^= (rp4 >> 8);
- rp4 &= 0xff;
- rp6 ^= (rp6 >> 16);
- rp6 ^= (rp6 >> 8);
- rp6 &= 0xff;
- rp8 ^= (rp8 >> 16);
- rp8 ^= (rp8 >> 8);
- rp8 &= 0xff;
- rp10 ^= (rp10 >> 16);
- rp10 ^= (rp10 >> 8);
- rp10 &= 0xff;
- rp12 ^= (rp12 >> 16);
- rp12 ^= (rp12 >> 8);
- rp12 &= 0xff;
- rp14 ^= (rp14 >> 16);
- rp14 ^= (rp14 >> 8);
- rp14 &= 0xff;
- if (eccsize_mult == 2) {
- rp16 ^= (rp16 >> 16);
- rp16 ^= (rp16 >> 8);
- rp16 &= 0xff;
- }
-
- /*
- * we also need to calculate the row parity for rp0..rp3
- * This is present in par, because par is now
- * rp3 rp3 rp2 rp2 in little endian and
- * rp2 rp2 rp3 rp3 in big endian
- * as well as
- * rp1 rp0 rp1 rp0 in little endian and
- * rp0 rp1 rp0 rp1 in big endian
- * First calculate rp2 and rp3
- */
-#ifdef __BIG_ENDIAN
- rp2 = (par >> 16);
- rp2 ^= (rp2 >> 8);
- rp2 &= 0xff;
- rp3 = par & 0xffff;
- rp3 ^= (rp3 >> 8);
- rp3 &= 0xff;
-#else
- rp3 = (par >> 16);
- rp3 ^= (rp3 >> 8);
- rp3 &= 0xff;
- rp2 = par & 0xffff;
- rp2 ^= (rp2 >> 8);
- rp2 &= 0xff;
-#endif
-
- /* reduce par to 16 bits then calculate rp1 and rp0 */
- par ^= (par >> 16);
-#ifdef __BIG_ENDIAN
- rp0 = (par >> 8) & 0xff;
- rp1 = (par & 0xff);
-#else
- rp1 = (par >> 8) & 0xff;
- rp0 = (par & 0xff);
-#endif
-
- /* finally reduce par to 8 bits */
- par ^= (par >> 8);
- par &= 0xff;
-
- /*
- * and calculate rp5..rp15..rp17
- * note that par = rp4 ^ rp5 and due to the commutative property
- * of the ^ operator we can say:
- * rp5 = (par ^ rp4);
- * The & 0xff seems superfluous, but benchmarking learned that
- * leaving it out gives slightly worse results. No idea why, probably
- * it has to do with the way the pipeline in pentium is organized.
- */
- rp5 = (par ^ rp4) & 0xff;
- rp7 = (par ^ rp6) & 0xff;
- rp9 = (par ^ rp8) & 0xff;
- rp11 = (par ^ rp10) & 0xff;
- rp13 = (par ^ rp12) & 0xff;
- rp15 = (par ^ rp14) & 0xff;
- if (eccsize_mult == 2)
- rp17 = (par ^ rp16) & 0xff;
-
- /*
- * Finally calculate the ECC bits.
- * Again here it might seem that there are performance optimisations
- * possible, but benchmarks showed that on the system this is developed
- * the code below is the fastest
- */
-#ifdef CONFIG_MTD_NAND_ECC_SMC
- code[0] =
- (invparity[rp7] << 7) |
- (invparity[rp6] << 6) |
- (invparity[rp5] << 5) |
- (invparity[rp4] << 4) |
- (invparity[rp3] << 3) |
- (invparity[rp2] << 2) |
- (invparity[rp1] << 1) |
- (invparity[rp0]);
- code[1] =
- (invparity[rp15] << 7) |
- (invparity[rp14] << 6) |
- (invparity[rp13] << 5) |
- (invparity[rp12] << 4) |
- (invparity[rp11] << 3) |
- (invparity[rp10] << 2) |
- (invparity[rp9] << 1) |
- (invparity[rp8]);
-#else
- code[1] =
- (invparity[rp7] << 7) |
- (invparity[rp6] << 6) |
- (invparity[rp5] << 5) |
- (invparity[rp4] << 4) |
- (invparity[rp3] << 3) |
- (invparity[rp2] << 2) |
- (invparity[rp1] << 1) |
- (invparity[rp0]);
- code[0] =
- (invparity[rp15] << 7) |
- (invparity[rp14] << 6) |
- (invparity[rp13] << 5) |
- (invparity[rp12] << 4) |
- (invparity[rp11] << 3) |
- (invparity[rp10] << 2) |
- (invparity[rp9] << 1) |
- (invparity[rp8]);
-#endif
- if (eccsize_mult == 1)
- code[2] =
- (invparity[par & 0xf0] << 7) |
- (invparity[par & 0x0f] << 6) |
- (invparity[par & 0xcc] << 5) |
- (invparity[par & 0x33] << 4) |
- (invparity[par & 0xaa] << 3) |
- (invparity[par & 0x55] << 2) |
- 3;
- else
- code[2] =
- (invparity[par & 0xf0] << 7) |
- (invparity[par & 0x0f] << 6) |
- (invparity[par & 0xcc] << 5) |
- (invparity[par & 0x33] << 4) |
- (invparity[par & 0xaa] << 3) |
- (invparity[par & 0x55] << 2) |
- (invparity[rp17] << 1) |
- (invparity[rp16] << 0);
-}
-EXPORT_SYMBOL(__nand_calculate_ecc);
-
-/**
- * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte
- * block
- * @mtd: MTD block structure
- * @buf: input buffer with raw data
- * @code: output buffer with ECC
- */
-int nand_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf,
- unsigned char *code)
-{
- __nand_calculate_ecc(buf,
- mtd_to_nand(mtd)->ecc.size, code);
-
- return 0;
-}
-EXPORT_SYMBOL(nand_calculate_ecc);
-
-/**
- * __nand_correct_data - [NAND Interface] Detect and correct bit error(s)
- * @buf: raw data read from the chip
- * @read_ecc: ECC from the chip
- * @calc_ecc: the ECC calculated from raw data
- * @eccsize: data bytes per ECC step (256 or 512)
- *
- * Detect and correct a 1 bit error for eccsize byte block
- */
-int __nand_correct_data(unsigned char *buf,
- unsigned char *read_ecc, unsigned char *calc_ecc,
- unsigned int eccsize)
-{
- unsigned char b0, b1, b2, bit_addr;
- unsigned int byte_addr;
- /* 256 or 512 bytes/ecc */
- const uint32_t eccsize_mult = eccsize >> 8;
-
- /*
- * b0 to b2 indicate which bit is faulty (if any)
- * we might need the xor result more than once,
- * so keep them in a local var
- */
-#ifdef CONFIG_MTD_NAND_ECC_SMC
- b0 = read_ecc[0] ^ calc_ecc[0];
- b1 = read_ecc[1] ^ calc_ecc[1];
-#else
- b0 = read_ecc[1] ^ calc_ecc[1];
- b1 = read_ecc[0] ^ calc_ecc[0];
-#endif
- b2 = read_ecc[2] ^ calc_ecc[2];
-
- /* check if there are any bitfaults */
-
- /* repeated if statements are slightly more efficient than switch ... */
- /* ordered in order of likelihood */
-
- if ((b0 | b1 | b2) == 0)
- return 0; /* no error */
-
- if ((((b0 ^ (b0 >> 1)) & 0x55) == 0x55) &&
- (((b1 ^ (b1 >> 1)) & 0x55) == 0x55) &&
- ((eccsize_mult == 1 && ((b2 ^ (b2 >> 1)) & 0x54) == 0x54) ||
- (eccsize_mult == 2 && ((b2 ^ (b2 >> 1)) & 0x55) == 0x55))) {
- /* single bit error */
- /*
- * rp17/rp15/13/11/9/7/5/3/1 indicate which byte is the faulty
- * byte, cp 5/3/1 indicate the faulty bit.
- * A lookup table (called addressbits) is used to filter
- * the bits from the byte they are in.
- * A marginal optimisation is possible by having three
- * different lookup tables.
- * One as we have now (for b0), one for b2
- * (that would avoid the >> 1), and one for b1 (with all values
- * << 4). However it was felt that introducing two more tables
- * hardly justify the gain.
- *
- * The b2 shift is there to get rid of the lowest two bits.
- * We could also do addressbits[b2] >> 1 but for the
- * performance it does not make any difference
- */
- if (eccsize_mult == 1)
- byte_addr = (addressbits[b1] << 4) + addressbits[b0];
- else
- byte_addr = (addressbits[b2 & 0x3] << 8) +
- (addressbits[b1] << 4) + addressbits[b0];
- bit_addr = addressbits[b2 >> 2];
- /* flip the bit */
- buf[byte_addr] ^= (1 << bit_addr);
- return 1;
-
- }
- /* count nr of bits; use table lookup, faster than calculating it */
- if ((bitsperbyte[b0] + bitsperbyte[b1] + bitsperbyte[b2]) == 1)
- return 1; /* error in ECC data; no action needed */
-
- pr_err("%s: uncorrectable ECC error\n", __func__);
- return -EBADMSG;
-}
-EXPORT_SYMBOL(__nand_correct_data);
-
-/**
- * nand_correct_data - [NAND Interface] Detect and correct bit error(s)
- * @mtd: MTD block structure
- * @buf: raw data read from the chip
- * @read_ecc: ECC from the chip
- * @calc_ecc: the ECC calculated from raw data
- *
- * Detect and correct a 1 bit error for 256/512 byte block
- */
-int nand_correct_data(struct mtd_info *mtd, unsigned char *buf,
- unsigned char *read_ecc, unsigned char *calc_ecc)
-{
- return __nand_correct_data(buf, read_ecc, calc_ecc,
- mtd_to_nand(mtd)->ecc.size);
-}
-EXPORT_SYMBOL(nand_correct_data);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Frans Meulenbroeks <fransmeulenbroeks@gmail.com>");
-MODULE_DESCRIPTION("Generic NAND ECC support");
diff --git a/drivers/mtd/nand/nand_macronix.c b/drivers/mtd/nand/nand_macronix.c
deleted file mode 100644
index d290ff2a6d2f..000000000000
--- a/drivers/mtd/nand/nand_macronix.c
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2017 Free Electrons
- * Copyright (C) 2017 NextThing Co
- *
- * Author: Boris Brezillon <boris.brezillon@free-electrons.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.
- */
-
-#include <linux/mtd/rawnand.h>
-
-static int macronix_nand_init(struct nand_chip *chip)
-{
- if (nand_is_slc(chip))
- chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
-
- return 0;
-}
-
-const struct nand_manufacturer_ops macronix_nand_manuf_ops = {
- .init = macronix_nand_init,
-};
diff --git a/drivers/mtd/nand/nand_micron.c b/drivers/mtd/nand/nand_micron.c
deleted file mode 100644
index 02e109ae73f1..000000000000
--- a/drivers/mtd/nand/nand_micron.c
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- * Copyright (C) 2017 Free Electrons
- * Copyright (C) 2017 NextThing Co
- *
- * Author: Boris Brezillon <boris.brezillon@free-electrons.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.
- */
-
-#include <linux/mtd/rawnand.h>
-
-/*
- * Special Micron status bit that indicates when the block has been
- * corrected by on-die ECC and should be rewritten
- */
-#define NAND_STATUS_WRITE_RECOMMENDED BIT(3)
-
-struct nand_onfi_vendor_micron {
- u8 two_plane_read;
- u8 read_cache;
- u8 read_unique_id;
- u8 dq_imped;
- u8 dq_imped_num_settings;
- u8 dq_imped_feat_addr;
- u8 rb_pulldown_strength;
- u8 rb_pulldown_strength_feat_addr;
- u8 rb_pulldown_strength_num_settings;
- u8 otp_mode;
- u8 otp_page_start;
- u8 otp_data_prot_addr;
- u8 otp_num_pages;
- u8 otp_feat_addr;
- u8 read_retry_options;
- u8 reserved[72];
- u8 param_revision;
-} __packed;
-
-static int micron_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = {retry_mode};
-
- return chip->onfi_set_features(mtd, chip, ONFI_FEATURE_ADDR_READ_RETRY,
- feature);
-}
-
-/*
- * Configure chip properties from Micron vendor-specific ONFI table
- */
-static int micron_nand_onfi_init(struct nand_chip *chip)
-{
- struct nand_onfi_params *p = &chip->onfi_params;
- struct nand_onfi_vendor_micron *micron = (void *)p->vendor;
-
- if (!chip->onfi_version)
- return 0;
-
- if (le16_to_cpu(p->vendor_revision) < 1)
- return 0;
-
- chip->read_retries = micron->read_retry_options;
- chip->setup_read_retry = micron_nand_setup_read_retry;
-
- return 0;
-}
-
-static int micron_nand_on_die_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- if (section >= 4)
- return -ERANGE;
-
- oobregion->offset = (section * 16) + 8;
- oobregion->length = 8;
-
- return 0;
-}
-
-static int micron_nand_on_die_ooblayout_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- if (section >= 4)
- return -ERANGE;
-
- oobregion->offset = (section * 16) + 2;
- oobregion->length = 6;
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops micron_nand_on_die_ooblayout_ops = {
- .ecc = micron_nand_on_die_ooblayout_ecc,
- .free = micron_nand_on_die_ooblayout_free,
-};
-
-static int micron_nand_on_die_ecc_setup(struct nand_chip *chip, bool enable)
-{
- u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = { 0, };
-
- if (enable)
- feature[0] |= ONFI_FEATURE_ON_DIE_ECC_EN;
-
- return chip->onfi_set_features(nand_to_mtd(chip), chip,
- ONFI_FEATURE_ON_DIE_ECC, feature);
-}
-
-static int
-micron_nand_read_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int oob_required,
- int page)
-{
- u8 status;
- int ret, max_bitflips = 0;
-
- ret = micron_nand_on_die_ecc_setup(chip, true);
- if (ret)
- return ret;
-
- ret = nand_read_page_op(chip, page, 0, NULL, 0);
- if (ret)
- goto out;
-
- ret = nand_status_op(chip, &status);
- if (ret)
- goto out;
-
- ret = nand_exit_status_op(chip);
- if (ret)
- goto out;
-
- if (status & NAND_STATUS_FAIL)
- mtd->ecc_stats.failed++;
-
- /*
- * The internal ECC doesn't tell us the number of bitflips
- * that have been corrected, but tells us if it recommends to
- * rewrite the block. If it's the case, then we pretend we had
- * a number of bitflips equal to the ECC strength, which will
- * hint the NAND core to rewrite the block.
- */
- else if (status & NAND_STATUS_WRITE_RECOMMENDED)
- max_bitflips = chip->ecc.strength;
-
- ret = nand_read_data_op(chip, buf, mtd->writesize, false);
- if (!ret && oob_required)
- ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize,
- false);
-
-out:
- micron_nand_on_die_ecc_setup(chip, false);
-
- return ret ? ret : max_bitflips;
-}
-
-static int
-micron_nand_write_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf, int oob_required,
- int page)
-{
- int ret;
-
- ret = micron_nand_on_die_ecc_setup(chip, true);
- if (ret)
- return ret;
-
- ret = nand_write_page_raw(mtd, chip, buf, oob_required, page);
- micron_nand_on_die_ecc_setup(chip, false);
-
- return ret;
-}
-
-enum {
- /* The NAND flash doesn't support on-die ECC */
- MICRON_ON_DIE_UNSUPPORTED,
-
- /*
- * The NAND flash supports on-die ECC and it can be
- * enabled/disabled by a set features command.
- */
- MICRON_ON_DIE_SUPPORTED,
-
- /*
- * The NAND flash supports on-die ECC, and it cannot be
- * disabled.
- */
- MICRON_ON_DIE_MANDATORY,
-};
-
-/*
- * Try to detect if the NAND support on-die ECC. To do this, we enable
- * the feature, and read back if it has been enabled as expected. We
- * also check if it can be disabled, because some Micron NANDs do not
- * allow disabling the on-die ECC and we don't support such NANDs for
- * now.
- *
- * This function also has the side effect of disabling on-die ECC if
- * it had been left enabled by the firmware/bootloader.
- */
-static int micron_supports_on_die_ecc(struct nand_chip *chip)
-{
- u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = { 0, };
- int ret;
-
- if (chip->onfi_version == 0)
- return MICRON_ON_DIE_UNSUPPORTED;
-
- if (chip->bits_per_cell != 1)
- return MICRON_ON_DIE_UNSUPPORTED;
-
- ret = micron_nand_on_die_ecc_setup(chip, true);
- if (ret)
- return MICRON_ON_DIE_UNSUPPORTED;
-
- chip->onfi_get_features(nand_to_mtd(chip), chip,
- ONFI_FEATURE_ON_DIE_ECC, feature);
- if ((feature[0] & ONFI_FEATURE_ON_DIE_ECC_EN) == 0)
- return MICRON_ON_DIE_UNSUPPORTED;
-
- ret = micron_nand_on_die_ecc_setup(chip, false);
- if (ret)
- return MICRON_ON_DIE_UNSUPPORTED;
-
- chip->onfi_get_features(nand_to_mtd(chip), chip,
- ONFI_FEATURE_ON_DIE_ECC, feature);
- if (feature[0] & ONFI_FEATURE_ON_DIE_ECC_EN)
- return MICRON_ON_DIE_MANDATORY;
-
- /*
- * Some Micron NANDs have an on-die ECC of 4/512, some other
- * 8/512. We only support the former.
- */
- if (chip->onfi_params.ecc_bits != 4)
- return MICRON_ON_DIE_UNSUPPORTED;
-
- return MICRON_ON_DIE_SUPPORTED;
-}
-
-static int micron_nand_init(struct nand_chip *chip)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- int ondie;
- int ret;
-
- ret = micron_nand_onfi_init(chip);
- if (ret)
- return ret;
-
- if (mtd->writesize == 2048)
- chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
-
- ondie = micron_supports_on_die_ecc(chip);
-
- if (ondie == MICRON_ON_DIE_MANDATORY) {
- pr_err("On-die ECC forcefully enabled, not supported\n");
- return -EINVAL;
- }
-
- if (chip->ecc.mode == NAND_ECC_ON_DIE) {
- if (ondie == MICRON_ON_DIE_UNSUPPORTED) {
- pr_err("On-die ECC selected but not supported\n");
- return -EINVAL;
- }
-
- chip->ecc.bytes = 8;
- chip->ecc.size = 512;
- chip->ecc.strength = 4;
- chip->ecc.algo = NAND_ECC_BCH;
- chip->ecc.read_page = micron_nand_read_page_on_die_ecc;
- chip->ecc.write_page = micron_nand_write_page_on_die_ecc;
- chip->ecc.read_page_raw = nand_read_page_raw;
- chip->ecc.write_page_raw = nand_write_page_raw;
-
- mtd_set_ooblayout(mtd, &micron_nand_on_die_ooblayout_ops);
- }
-
- return 0;
-}
-
-const struct nand_manufacturer_ops micron_nand_manuf_ops = {
- .init = micron_nand_init,
-};
diff --git a/drivers/mtd/nand/nand_timings.c b/drivers/mtd/nand/nand_timings.c
deleted file mode 100644
index 9400d039ddbd..000000000000
--- a/drivers/mtd/nand/nand_timings.c
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
- * Copyright (C) 2014 Free Electrons
- *
- * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
- *
- */
-#include <linux/kernel.h>
-#include <linux/err.h>
-#include <linux/export.h>
-#include <linux/mtd/rawnand.h>
-
-static const struct nand_data_interface onfi_sdr_timings[] = {
- /* Mode 0 */
- {
- .type = NAND_SDR_IFACE,
- .timings.sdr = {
- .tCCS_min = 500000,
- .tR_max = 200000000,
- .tADL_min = 400000,
- .tALH_min = 20000,
- .tALS_min = 50000,
- .tAR_min = 25000,
- .tCEA_max = 100000,
- .tCEH_min = 20000,
- .tCH_min = 20000,
- .tCHZ_max = 100000,
- .tCLH_min = 20000,
- .tCLR_min = 20000,
- .tCLS_min = 50000,
- .tCOH_min = 0,
- .tCS_min = 70000,
- .tDH_min = 20000,
- .tDS_min = 40000,
- .tFEAT_max = 1000000,
- .tIR_min = 10000,
- .tITC_max = 1000000,
- .tRC_min = 100000,
- .tREA_max = 40000,
- .tREH_min = 30000,
- .tRHOH_min = 0,
- .tRHW_min = 200000,
- .tRHZ_max = 200000,
- .tRLOH_min = 0,
- .tRP_min = 50000,
- .tRR_min = 40000,
- .tRST_max = 250000000000ULL,
- .tWB_max = 200000,
- .tWC_min = 100000,
- .tWH_min = 30000,
- .tWHR_min = 120000,
- .tWP_min = 50000,
- .tWW_min = 100000,
- },
- },
- /* Mode 1 */
- {
- .type = NAND_SDR_IFACE,
- .timings.sdr = {
- .tCCS_min = 500000,
- .tR_max = 200000000,
- .tADL_min = 400000,
- .tALH_min = 10000,
- .tALS_min = 25000,
- .tAR_min = 10000,
- .tCEA_max = 45000,
- .tCEH_min = 20000,
- .tCH_min = 10000,
- .tCHZ_max = 50000,
- .tCLH_min = 10000,
- .tCLR_min = 10000,
- .tCLS_min = 25000,
- .tCOH_min = 15000,
- .tCS_min = 35000,
- .tDH_min = 10000,
- .tDS_min = 20000,
- .tFEAT_max = 1000000,
- .tIR_min = 0,
- .tITC_max = 1000000,
- .tRC_min = 50000,
- .tREA_max = 30000,
- .tREH_min = 15000,
- .tRHOH_min = 15000,
- .tRHW_min = 100000,
- .tRHZ_max = 100000,
- .tRLOH_min = 0,
- .tRP_min = 25000,
- .tRR_min = 20000,
- .tRST_max = 500000000,
- .tWB_max = 100000,
- .tWC_min = 45000,
- .tWH_min = 15000,
- .tWHR_min = 80000,
- .tWP_min = 25000,
- .tWW_min = 100000,
- },
- },
- /* Mode 2 */
- {
- .type = NAND_SDR_IFACE,
- .timings.sdr = {
- .tCCS_min = 500000,
- .tR_max = 200000000,
- .tADL_min = 400000,
- .tALH_min = 10000,
- .tALS_min = 15000,
- .tAR_min = 10000,
- .tCEA_max = 30000,
- .tCEH_min = 20000,
- .tCH_min = 10000,
- .tCHZ_max = 50000,
- .tCLH_min = 10000,
- .tCLR_min = 10000,
- .tCLS_min = 15000,
- .tCOH_min = 15000,
- .tCS_min = 25000,
- .tDH_min = 5000,
- .tDS_min = 15000,
- .tFEAT_max = 1000000,
- .tIR_min = 0,
- .tITC_max = 1000000,
- .tRC_min = 35000,
- .tREA_max = 25000,
- .tREH_min = 15000,
- .tRHOH_min = 15000,
- .tRHW_min = 100000,
- .tRHZ_max = 100000,
- .tRLOH_min = 0,
- .tRR_min = 20000,
- .tRST_max = 500000000,
- .tWB_max = 100000,
- .tRP_min = 17000,
- .tWC_min = 35000,
- .tWH_min = 15000,
- .tWHR_min = 80000,
- .tWP_min = 17000,
- .tWW_min = 100000,
- },
- },
- /* Mode 3 */
- {
- .type = NAND_SDR_IFACE,
- .timings.sdr = {
- .tCCS_min = 500000,
- .tR_max = 200000000,
- .tADL_min = 400000,
- .tALH_min = 5000,
- .tALS_min = 10000,
- .tAR_min = 10000,
- .tCEA_max = 25000,
- .tCEH_min = 20000,
- .tCH_min = 5000,
- .tCHZ_max = 50000,
- .tCLH_min = 5000,
- .tCLR_min = 10000,
- .tCLS_min = 10000,
- .tCOH_min = 15000,
- .tCS_min = 25000,
- .tDH_min = 5000,
- .tDS_min = 10000,
- .tFEAT_max = 1000000,
- .tIR_min = 0,
- .tITC_max = 1000000,
- .tRC_min = 30000,
- .tREA_max = 20000,
- .tREH_min = 10000,
- .tRHOH_min = 15000,
- .tRHW_min = 100000,
- .tRHZ_max = 100000,
- .tRLOH_min = 0,
- .tRP_min = 15000,
- .tRR_min = 20000,
- .tRST_max = 500000000,
- .tWB_max = 100000,
- .tWC_min = 30000,
- .tWH_min = 10000,
- .tWHR_min = 80000,
- .tWP_min = 15000,
- .tWW_min = 100000,
- },
- },
- /* Mode 4 */
- {
- .type = NAND_SDR_IFACE,
- .timings.sdr = {
- .tCCS_min = 500000,
- .tR_max = 200000000,
- .tADL_min = 400000,
- .tALH_min = 5000,
- .tALS_min = 10000,
- .tAR_min = 10000,
- .tCEA_max = 25000,
- .tCEH_min = 20000,
- .tCH_min = 5000,
- .tCHZ_max = 30000,
- .tCLH_min = 5000,
- .tCLR_min = 10000,
- .tCLS_min = 10000,
- .tCOH_min = 15000,
- .tCS_min = 20000,
- .tDH_min = 5000,
- .tDS_min = 10000,
- .tFEAT_max = 1000000,
- .tIR_min = 0,
- .tITC_max = 1000000,
- .tRC_min = 25000,
- .tREA_max = 20000,
- .tREH_min = 10000,
- .tRHOH_min = 15000,
- .tRHW_min = 100000,
- .tRHZ_max = 100000,
- .tRLOH_min = 5000,
- .tRP_min = 12000,
- .tRR_min = 20000,
- .tRST_max = 500000000,
- .tWB_max = 100000,
- .tWC_min = 25000,
- .tWH_min = 10000,
- .tWHR_min = 80000,
- .tWP_min = 12000,
- .tWW_min = 100000,
- },
- },
- /* Mode 5 */
- {
- .type = NAND_SDR_IFACE,
- .timings.sdr = {
- .tCCS_min = 500000,
- .tR_max = 200000000,
- .tADL_min = 400000,
- .tALH_min = 5000,
- .tALS_min = 10000,
- .tAR_min = 10000,
- .tCEA_max = 25000,
- .tCEH_min = 20000,
- .tCH_min = 5000,
- .tCHZ_max = 30000,
- .tCLH_min = 5000,
- .tCLR_min = 10000,
- .tCLS_min = 10000,
- .tCOH_min = 15000,
- .tCS_min = 15000,
- .tDH_min = 5000,
- .tDS_min = 7000,
- .tFEAT_max = 1000000,
- .tIR_min = 0,
- .tITC_max = 1000000,
- .tRC_min = 20000,
- .tREA_max = 16000,
- .tREH_min = 7000,
- .tRHOH_min = 15000,
- .tRHW_min = 100000,
- .tRHZ_max = 100000,
- .tRLOH_min = 5000,
- .tRP_min = 10000,
- .tRR_min = 20000,
- .tRST_max = 500000000,
- .tWB_max = 100000,
- .tWC_min = 20000,
- .tWH_min = 7000,
- .tWHR_min = 80000,
- .tWP_min = 10000,
- .tWW_min = 100000,
- },
- },
-};
-
-/**
- * onfi_async_timing_mode_to_sdr_timings - [NAND Interface] Retrieve NAND
- * timings according to the given ONFI timing mode
- * @mode: ONFI timing mode
- */
-const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode)
-{
- if (mode < 0 || mode >= ARRAY_SIZE(onfi_sdr_timings))
- return ERR_PTR(-EINVAL);
-
- return &onfi_sdr_timings[mode].timings.sdr;
-}
-EXPORT_SYMBOL(onfi_async_timing_mode_to_sdr_timings);
-
-/**
- * onfi_fill_data_interface - [NAND Interface] Initialize a data interface from
- * given ONFI mode
- * @mode: The ONFI timing mode
- */
-int onfi_fill_data_interface(struct nand_chip *chip,
- enum nand_data_interface_type type,
- int timing_mode)
-{
- struct nand_data_interface *iface = &chip->data_interface;
-
- if (type != NAND_SDR_IFACE)
- return -EINVAL;
-
- if (timing_mode < 0 || timing_mode >= ARRAY_SIZE(onfi_sdr_timings))
- return -EINVAL;
-
- *iface = onfi_sdr_timings[timing_mode];
-
- /*
- * Initialize timings that cannot be deduced from timing mode:
- * tR, tPROG, tCCS, ...
- * These information are part of the ONFI parameter page.
- */
- if (chip->onfi_version) {
- struct nand_onfi_params *params = &chip->onfi_params;
- struct nand_sdr_timings *timings = &iface->timings.sdr;
-
- /* microseconds -> picoseconds */
- timings->tPROG_max = 1000000ULL * le16_to_cpu(params->t_prog);
- timings->tBERS_max = 1000000ULL * le16_to_cpu(params->t_bers);
- timings->tR_max = 1000000ULL * le16_to_cpu(params->t_r);
-
- /* nanoseconds -> picoseconds */
- timings->tCCS_min = 1000UL * le16_to_cpu(params->t_ccs);
- }
-
- return 0;
-}
-EXPORT_SYMBOL(onfi_fill_data_interface);
diff --git a/drivers/mtd/nand/nand_toshiba.c b/drivers/mtd/nand/nand_toshiba.c
deleted file mode 100644
index 57df857074e6..000000000000
--- a/drivers/mtd/nand/nand_toshiba.c
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2017 Free Electrons
- * Copyright (C) 2017 NextThing Co
- *
- * Author: Boris Brezillon <boris.brezillon@free-electrons.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.
- */
-
-#include <linux/mtd/rawnand.h>
-
-static void toshiba_nand_decode_id(struct nand_chip *chip)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
-
- nand_decode_ext_id(chip);
-
- /*
- * Toshiba 24nm raw SLC (i.e., not BENAND) have 32B OOB per
- * 512B page. For Toshiba SLC, we decode the 5th/6th byte as
- * follows:
- * - ID byte 6, bits[2:0]: 100b -> 43nm, 101b -> 32nm,
- * 110b -> 24nm
- * - ID byte 5, bit[7]: 1 -> BENAND, 0 -> raw SLC
- */
- if (chip->id.len >= 6 && nand_is_slc(chip) &&
- (chip->id.data[5] & 0x7) == 0x6 /* 24nm */ &&
- !(chip->id.data[4] & 0x80) /* !BENAND */)
- mtd->oobsize = 32 * mtd->writesize >> 9;
-}
-
-static int toshiba_nand_init(struct nand_chip *chip)
-{
- if (nand_is_slc(chip))
- chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
-
- return 0;
-}
-
-const struct nand_manufacturer_ops toshiba_nand_manuf_ops = {
- .detect = toshiba_nand_decode_id,
- .init = toshiba_nand_init,
-};
diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c
deleted file mode 100644
index 44322a363ba5..000000000000
--- a/drivers/mtd/nand/nandsim.c
+++ /dev/null
@@ -1,2392 +0,0 @@
-/*
- * NAND flash simulator.
- *
- * Author: Artem B. Bityuckiy <dedekind@oktetlabs.ru>, <dedekind@infradead.org>
- *
- * Copyright (C) 2004 Nokia Corporation
- *
- * Note: NS means "NAND Simulator".
- * Note: Input means input TO flash chip, output means output FROM 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
- */
-
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/vmalloc.h>
-#include <linux/math64.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand_bch.h>
-#include <linux/mtd/partitions.h>
-#include <linux/delay.h>
-#include <linux/list.h>
-#include <linux/random.h>
-#include <linux/sched.h>
-#include <linux/sched/mm.h>
-#include <linux/fs.h>
-#include <linux/pagemap.h>
-#include <linux/seq_file.h>
-#include <linux/debugfs.h>
-
-/* Default simulator parameters values */
-#if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE) || \
- !defined(CONFIG_NANDSIM_SECOND_ID_BYTE) || \
- !defined(CONFIG_NANDSIM_THIRD_ID_BYTE) || \
- !defined(CONFIG_NANDSIM_FOURTH_ID_BYTE)
-#define CONFIG_NANDSIM_FIRST_ID_BYTE 0x98
-#define CONFIG_NANDSIM_SECOND_ID_BYTE 0x39
-#define CONFIG_NANDSIM_THIRD_ID_BYTE 0xFF /* No byte */
-#define CONFIG_NANDSIM_FOURTH_ID_BYTE 0xFF /* No byte */
-#endif
-
-#ifndef CONFIG_NANDSIM_ACCESS_DELAY
-#define CONFIG_NANDSIM_ACCESS_DELAY 25
-#endif
-#ifndef CONFIG_NANDSIM_PROGRAMM_DELAY
-#define CONFIG_NANDSIM_PROGRAMM_DELAY 200
-#endif
-#ifndef CONFIG_NANDSIM_ERASE_DELAY
-#define CONFIG_NANDSIM_ERASE_DELAY 2
-#endif
-#ifndef CONFIG_NANDSIM_OUTPUT_CYCLE
-#define CONFIG_NANDSIM_OUTPUT_CYCLE 40
-#endif
-#ifndef CONFIG_NANDSIM_INPUT_CYCLE
-#define CONFIG_NANDSIM_INPUT_CYCLE 50
-#endif
-#ifndef CONFIG_NANDSIM_BUS_WIDTH
-#define CONFIG_NANDSIM_BUS_WIDTH 8
-#endif
-#ifndef CONFIG_NANDSIM_DO_DELAYS
-#define CONFIG_NANDSIM_DO_DELAYS 0
-#endif
-#ifndef CONFIG_NANDSIM_LOG
-#define CONFIG_NANDSIM_LOG 0
-#endif
-#ifndef CONFIG_NANDSIM_DBG
-#define CONFIG_NANDSIM_DBG 0
-#endif
-#ifndef CONFIG_NANDSIM_MAX_PARTS
-#define CONFIG_NANDSIM_MAX_PARTS 32
-#endif
-
-static uint access_delay = CONFIG_NANDSIM_ACCESS_DELAY;
-static uint programm_delay = CONFIG_NANDSIM_PROGRAMM_DELAY;
-static uint erase_delay = CONFIG_NANDSIM_ERASE_DELAY;
-static uint output_cycle = CONFIG_NANDSIM_OUTPUT_CYCLE;
-static uint input_cycle = CONFIG_NANDSIM_INPUT_CYCLE;
-static uint bus_width = CONFIG_NANDSIM_BUS_WIDTH;
-static uint do_delays = CONFIG_NANDSIM_DO_DELAYS;
-static uint log = CONFIG_NANDSIM_LOG;
-static uint dbg = CONFIG_NANDSIM_DBG;
-static unsigned long parts[CONFIG_NANDSIM_MAX_PARTS];
-static unsigned int parts_num;
-static char *badblocks = NULL;
-static char *weakblocks = NULL;
-static char *weakpages = NULL;
-static unsigned int bitflips = 0;
-static char *gravepages = NULL;
-static unsigned int overridesize = 0;
-static char *cache_file = NULL;
-static unsigned int bbt;
-static unsigned int bch;
-static u_char id_bytes[8] = {
- [0] = CONFIG_NANDSIM_FIRST_ID_BYTE,
- [1] = CONFIG_NANDSIM_SECOND_ID_BYTE,
- [2] = CONFIG_NANDSIM_THIRD_ID_BYTE,
- [3] = CONFIG_NANDSIM_FOURTH_ID_BYTE,
- [4 ... 7] = 0xFF,
-};
-
-module_param_array(id_bytes, byte, NULL, 0400);
-module_param_named(first_id_byte, id_bytes[0], byte, 0400);
-module_param_named(second_id_byte, id_bytes[1], byte, 0400);
-module_param_named(third_id_byte, id_bytes[2], byte, 0400);
-module_param_named(fourth_id_byte, id_bytes[3], byte, 0400);
-module_param(access_delay, uint, 0400);
-module_param(programm_delay, uint, 0400);
-module_param(erase_delay, uint, 0400);
-module_param(output_cycle, uint, 0400);
-module_param(input_cycle, uint, 0400);
-module_param(bus_width, uint, 0400);
-module_param(do_delays, uint, 0400);
-module_param(log, uint, 0400);
-module_param(dbg, uint, 0400);
-module_param_array(parts, ulong, &parts_num, 0400);
-module_param(badblocks, charp, 0400);
-module_param(weakblocks, charp, 0400);
-module_param(weakpages, charp, 0400);
-module_param(bitflips, uint, 0400);
-module_param(gravepages, charp, 0400);
-module_param(overridesize, uint, 0400);
-module_param(cache_file, charp, 0400);
-module_param(bbt, uint, 0400);
-module_param(bch, uint, 0400);
-
-MODULE_PARM_DESC(id_bytes, "The ID bytes returned by NAND Flash 'read ID' command");
-MODULE_PARM_DESC(first_id_byte, "The first byte returned by NAND Flash 'read ID' command (manufacturer ID) (obsolete)");
-MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID) (obsolete)");
-MODULE_PARM_DESC(third_id_byte, "The third byte returned by NAND Flash 'read ID' command (obsolete)");
-MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read ID' command (obsolete)");
-MODULE_PARM_DESC(access_delay, "Initial page access delay (microseconds)");
-MODULE_PARM_DESC(programm_delay, "Page programm delay (microseconds");
-MODULE_PARM_DESC(erase_delay, "Sector erase delay (milliseconds)");
-MODULE_PARM_DESC(output_cycle, "Word output (from flash) time (nanoseconds)");
-MODULE_PARM_DESC(input_cycle, "Word input (to flash) time (nanoseconds)");
-MODULE_PARM_DESC(bus_width, "Chip's bus width (8- or 16-bit)");
-MODULE_PARM_DESC(do_delays, "Simulate NAND delays using busy-waits if not zero");
-MODULE_PARM_DESC(log, "Perform logging if not zero");
-MODULE_PARM_DESC(dbg, "Output debug information if not zero");
-MODULE_PARM_DESC(parts, "Partition sizes (in erase blocks) separated by commas");
-/* Page and erase block positions for the following parameters are independent of any partitions */
-MODULE_PARM_DESC(badblocks, "Erase blocks that are initially marked bad, separated by commas");
-MODULE_PARM_DESC(weakblocks, "Weak erase blocks [: remaining erase cycles (defaults to 3)]"
- " separated by commas e.g. 113:2 means eb 113"
- " can be erased only twice before failing");
-MODULE_PARM_DESC(weakpages, "Weak pages [: maximum writes (defaults to 3)]"
- " separated by commas e.g. 1401:2 means page 1401"
- " can be written only twice before failing");
-MODULE_PARM_DESC(bitflips, "Maximum number of random bit flips per page (zero by default)");
-MODULE_PARM_DESC(gravepages, "Pages that lose data [: maximum reads (defaults to 3)]"
- " separated by commas e.g. 1401:2 means page 1401"
- " can be read only twice before failing");
-MODULE_PARM_DESC(overridesize, "Specifies the NAND Flash size overriding the ID bytes. "
- "The size is specified in erase blocks and as the exponent of a power of two"
- " e.g. 5 means a size of 32 erase blocks");
-MODULE_PARM_DESC(cache_file, "File to use to cache nand pages instead of memory");
-MODULE_PARM_DESC(bbt, "0 OOB, 1 BBT with marker in OOB, 2 BBT with marker in data area");
-MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits should "
- "be correctable in 512-byte blocks");
-
-/* The largest possible page size */
-#define NS_LARGEST_PAGE_SIZE 4096
-
-/* The prefix for simulator output */
-#define NS_OUTPUT_PREFIX "[nandsim]"
-
-/* Simulator's output macros (logging, debugging, warning, error) */
-#define NS_LOG(args...) \
- do { if (log) printk(KERN_DEBUG NS_OUTPUT_PREFIX " log: " args); } while(0)
-#define NS_DBG(args...) \
- do { if (dbg) printk(KERN_DEBUG NS_OUTPUT_PREFIX " debug: " args); } while(0)
-#define NS_WARN(args...) \
- do { printk(KERN_WARNING NS_OUTPUT_PREFIX " warning: " args); } while(0)
-#define NS_ERR(args...) \
- do { printk(KERN_ERR NS_OUTPUT_PREFIX " error: " args); } while(0)
-#define NS_INFO(args...) \
- do { printk(KERN_INFO NS_OUTPUT_PREFIX " " args); } while(0)
-
-/* Busy-wait delay macros (microseconds, milliseconds) */
-#define NS_UDELAY(us) \
- do { if (do_delays) udelay(us); } while(0)
-#define NS_MDELAY(us) \
- do { if (do_delays) mdelay(us); } while(0)
-
-/* Is the nandsim structure initialized ? */
-#define NS_IS_INITIALIZED(ns) ((ns)->geom.totsz != 0)
-
-/* Good operation completion status */
-#define NS_STATUS_OK(ns) (NAND_STATUS_READY | (NAND_STATUS_WP * ((ns)->lines.wp == 0)))
-
-/* Operation failed completion status */
-#define NS_STATUS_FAILED(ns) (NAND_STATUS_FAIL | NS_STATUS_OK(ns))
-
-/* Calculate the page offset in flash RAM image by (row, column) address */
-#define NS_RAW_OFFSET(ns) \
- (((ns)->regs.row * (ns)->geom.pgszoob) + (ns)->regs.column)
-
-/* Calculate the OOB offset in flash RAM image by (row, column) address */
-#define NS_RAW_OFFSET_OOB(ns) (NS_RAW_OFFSET(ns) + ns->geom.pgsz)
-
-/* After a command is input, the simulator goes to one of the following states */
-#define STATE_CMD_READ0 0x00000001 /* read data from the beginning of page */
-#define STATE_CMD_READ1 0x00000002 /* read data from the second half of page */
-#define STATE_CMD_READSTART 0x00000003 /* read data second command (large page devices) */
-#define STATE_CMD_PAGEPROG 0x00000004 /* start page program */
-#define STATE_CMD_READOOB 0x00000005 /* read OOB area */
-#define STATE_CMD_ERASE1 0x00000006 /* sector erase first command */
-#define STATE_CMD_STATUS 0x00000007 /* read status */
-#define STATE_CMD_SEQIN 0x00000009 /* sequential data input */
-#define STATE_CMD_READID 0x0000000A /* read ID */
-#define STATE_CMD_ERASE2 0x0000000B /* sector erase second command */
-#define STATE_CMD_RESET 0x0000000C /* reset */
-#define STATE_CMD_RNDOUT 0x0000000D /* random output command */
-#define STATE_CMD_RNDOUTSTART 0x0000000E /* random output start command */
-#define STATE_CMD_MASK 0x0000000F /* command states mask */
-
-/* After an address is input, the simulator goes to one of these states */
-#define STATE_ADDR_PAGE 0x00000010 /* full (row, column) address is accepted */
-#define STATE_ADDR_SEC 0x00000020 /* sector address was accepted */
-#define STATE_ADDR_COLUMN 0x00000030 /* column address was accepted */
-#define STATE_ADDR_ZERO 0x00000040 /* one byte zero address was accepted */
-#define STATE_ADDR_MASK 0x00000070 /* address states mask */
-
-/* During data input/output the simulator is in these states */
-#define STATE_DATAIN 0x00000100 /* waiting for data input */
-#define STATE_DATAIN_MASK 0x00000100 /* data input states mask */
-
-#define STATE_DATAOUT 0x00001000 /* waiting for page data output */
-#define STATE_DATAOUT_ID 0x00002000 /* waiting for ID bytes output */
-#define STATE_DATAOUT_STATUS 0x00003000 /* waiting for status output */
-#define STATE_DATAOUT_MASK 0x00007000 /* data output states mask */
-
-/* Previous operation is done, ready to accept new requests */
-#define STATE_READY 0x00000000
-
-/* This state is used to mark that the next state isn't known yet */
-#define STATE_UNKNOWN 0x10000000
-
-/* Simulator's actions bit masks */
-#define ACTION_CPY 0x00100000 /* copy page/OOB to the internal buffer */
-#define ACTION_PRGPAGE 0x00200000 /* program the internal buffer to flash */
-#define ACTION_SECERASE 0x00300000 /* erase sector */
-#define ACTION_ZEROOFF 0x00400000 /* don't add any offset to address */
-#define ACTION_HALFOFF 0x00500000 /* add to address half of page */
-#define ACTION_OOBOFF 0x00600000 /* add to address OOB offset */
-#define ACTION_MASK 0x00700000 /* action mask */
-
-#define NS_OPER_NUM 13 /* Number of operations supported by the simulator */
-#define NS_OPER_STATES 6 /* Maximum number of states in operation */
-
-#define OPT_ANY 0xFFFFFFFF /* any chip supports this operation */
-#define OPT_PAGE512 0x00000002 /* 512-byte page chips */
-#define OPT_PAGE2048 0x00000008 /* 2048-byte page chips */
-#define OPT_PAGE512_8BIT 0x00000040 /* 512-byte page chips with 8-bit bus width */
-#define OPT_PAGE4096 0x00000080 /* 4096-byte page chips */
-#define OPT_LARGEPAGE (OPT_PAGE2048 | OPT_PAGE4096) /* 2048 & 4096-byte page chips */
-#define OPT_SMALLPAGE (OPT_PAGE512) /* 512-byte page chips */
-
-/* Remove action bits from state */
-#define NS_STATE(x) ((x) & ~ACTION_MASK)
-
-/*
- * Maximum previous states which need to be saved. Currently saving is
- * only needed for page program operation with preceded read command
- * (which is only valid for 512-byte pages).
- */
-#define NS_MAX_PREVSTATES 1
-
-/* Maximum page cache pages needed to read or write a NAND page to the cache_file */
-#define NS_MAX_HELD_PAGES 16
-
-/*
- * A union to represent flash memory contents and flash buffer.
- */
-union ns_mem {
- u_char *byte; /* for byte access */
- uint16_t *word; /* for 16-bit word access */
-};
-
-/*
- * The structure which describes all the internal simulator data.
- */
-struct nandsim {
- struct mtd_partition partitions[CONFIG_NANDSIM_MAX_PARTS];
- unsigned int nbparts;
-
- uint busw; /* flash chip bus width (8 or 16) */
- u_char ids[8]; /* chip's ID bytes */
- uint32_t options; /* chip's characteristic bits */
- uint32_t state; /* current chip state */
- uint32_t nxstate; /* next expected state */
-
- uint32_t *op; /* current operation, NULL operations isn't known yet */
- uint32_t pstates[NS_MAX_PREVSTATES]; /* previous states */
- uint16_t npstates; /* number of previous states saved */
- uint16_t stateidx; /* current state index */
-
- /* The simulated NAND flash pages array */
- union ns_mem *pages;
-
- /* Slab allocator for nand pages */
- struct kmem_cache *nand_pages_slab;
-
- /* Internal buffer of page + OOB size bytes */
- union ns_mem buf;
-
- /* NAND flash "geometry" */
- struct {
- uint64_t totsz; /* total flash size, bytes */
- uint32_t secsz; /* flash sector (erase block) size, bytes */
- uint pgsz; /* NAND flash page size, bytes */
- uint oobsz; /* page OOB area size, bytes */
- uint64_t totszoob; /* total flash size including OOB, bytes */
- uint pgszoob; /* page size including OOB , bytes*/
- uint secszoob; /* sector size including OOB, bytes */
- uint pgnum; /* total number of pages */
- uint pgsec; /* number of pages per sector */
- uint secshift; /* bits number in sector size */
- uint pgshift; /* bits number in page size */
- uint pgaddrbytes; /* bytes per page address */
- uint secaddrbytes; /* bytes per sector address */
- uint idbytes; /* the number ID bytes that this chip outputs */
- } geom;
-
- /* NAND flash internal registers */
- struct {
- unsigned command; /* the command register */
- u_char status; /* the status register */
- uint row; /* the page number */
- uint column; /* the offset within page */
- uint count; /* internal counter */
- uint num; /* number of bytes which must be processed */
- uint off; /* fixed page offset */
- } regs;
-
- /* NAND flash lines state */
- struct {
- int ce; /* chip Enable */
- int cle; /* command Latch Enable */
- int ale; /* address Latch Enable */
- int wp; /* write Protect */
- } lines;
-
- /* Fields needed when using a cache file */
- struct file *cfile; /* Open file */
- unsigned long *pages_written; /* Which pages have been written */
- void *file_buf;
- struct page *held_pages[NS_MAX_HELD_PAGES];
- int held_cnt;
-};
-
-/*
- * Operations array. To perform any operation the simulator must pass
- * through the correspondent states chain.
- */
-static struct nandsim_operations {
- uint32_t reqopts; /* options which are required to perform the operation */
- uint32_t states[NS_OPER_STATES]; /* operation's states */
-} ops[NS_OPER_NUM] = {
- /* Read page + OOB from the beginning */
- {OPT_SMALLPAGE, {STATE_CMD_READ0 | ACTION_ZEROOFF, STATE_ADDR_PAGE | ACTION_CPY,
- STATE_DATAOUT, STATE_READY}},
- /* Read page + OOB from the second half */
- {OPT_PAGE512_8BIT, {STATE_CMD_READ1 | ACTION_HALFOFF, STATE_ADDR_PAGE | ACTION_CPY,
- STATE_DATAOUT, STATE_READY}},
- /* Read OOB */
- {OPT_SMALLPAGE, {STATE_CMD_READOOB | ACTION_OOBOFF, STATE_ADDR_PAGE | ACTION_CPY,
- STATE_DATAOUT, STATE_READY}},
- /* Program page starting from the beginning */
- {OPT_ANY, {STATE_CMD_SEQIN, STATE_ADDR_PAGE, STATE_DATAIN,
- STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}},
- /* Program page starting from the beginning */
- {OPT_SMALLPAGE, {STATE_CMD_READ0, STATE_CMD_SEQIN | ACTION_ZEROOFF, STATE_ADDR_PAGE,
- STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}},
- /* Program page starting from the second half */
- {OPT_PAGE512, {STATE_CMD_READ1, STATE_CMD_SEQIN | ACTION_HALFOFF, STATE_ADDR_PAGE,
- STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}},
- /* Program OOB */
- {OPT_SMALLPAGE, {STATE_CMD_READOOB, STATE_CMD_SEQIN | ACTION_OOBOFF, STATE_ADDR_PAGE,
- STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}},
- /* Erase sector */
- {OPT_ANY, {STATE_CMD_ERASE1, STATE_ADDR_SEC, STATE_CMD_ERASE2 | ACTION_SECERASE, STATE_READY}},
- /* Read status */
- {OPT_ANY, {STATE_CMD_STATUS, STATE_DATAOUT_STATUS, STATE_READY}},
- /* Read ID */
- {OPT_ANY, {STATE_CMD_READID, STATE_ADDR_ZERO, STATE_DATAOUT_ID, STATE_READY}},
- /* Large page devices read page */
- {OPT_LARGEPAGE, {STATE_CMD_READ0, STATE_ADDR_PAGE, STATE_CMD_READSTART | ACTION_CPY,
- STATE_DATAOUT, STATE_READY}},
- /* Large page devices random page read */
- {OPT_LARGEPAGE, {STATE_CMD_RNDOUT, STATE_ADDR_COLUMN, STATE_CMD_RNDOUTSTART | ACTION_CPY,
- STATE_DATAOUT, STATE_READY}},
-};
-
-struct weak_block {
- struct list_head list;
- unsigned int erase_block_no;
- unsigned int max_erases;
- unsigned int erases_done;
-};
-
-static LIST_HEAD(weak_blocks);
-
-struct weak_page {
- struct list_head list;
- unsigned int page_no;
- unsigned int max_writes;
- unsigned int writes_done;
-};
-
-static LIST_HEAD(weak_pages);
-
-struct grave_page {
- struct list_head list;
- unsigned int page_no;
- unsigned int max_reads;
- unsigned int reads_done;
-};
-
-static LIST_HEAD(grave_pages);
-
-static unsigned long *erase_block_wear = NULL;
-static unsigned int wear_eb_count = 0;
-static unsigned long total_wear = 0;
-
-/* MTD structure for NAND controller */
-static struct mtd_info *nsmtd;
-
-static int nandsim_debugfs_show(struct seq_file *m, void *private)
-{
- unsigned long wmin = -1, wmax = 0, avg;
- unsigned long deciles[10], decile_max[10], tot = 0;
- unsigned int i;
-
- /* Calc wear stats */
- for (i = 0; i < wear_eb_count; ++i) {
- unsigned long wear = erase_block_wear[i];
- if (wear < wmin)
- wmin = wear;
- if (wear > wmax)
- wmax = wear;
- tot += wear;
- }
-
- for (i = 0; i < 9; ++i) {
- deciles[i] = 0;
- decile_max[i] = (wmax * (i + 1) + 5) / 10;
- }
- deciles[9] = 0;
- decile_max[9] = wmax;
- for (i = 0; i < wear_eb_count; ++i) {
- int d;
- unsigned long wear = erase_block_wear[i];
- for (d = 0; d < 10; ++d)
- if (wear <= decile_max[d]) {
- deciles[d] += 1;
- break;
- }
- }
- avg = tot / wear_eb_count;
-
- /* Output wear report */
- seq_printf(m, "Total numbers of erases: %lu\n", tot);
- seq_printf(m, "Number of erase blocks: %u\n", wear_eb_count);
- seq_printf(m, "Average number of erases: %lu\n", avg);
- seq_printf(m, "Maximum number of erases: %lu\n", wmax);
- seq_printf(m, "Minimum number of erases: %lu\n", wmin);
- for (i = 0; i < 10; ++i) {
- unsigned long from = (i ? decile_max[i - 1] + 1 : 0);
- if (from > decile_max[i])
- continue;
- seq_printf(m, "Number of ebs with erase counts from %lu to %lu : %lu\n",
- from,
- decile_max[i],
- deciles[i]);
- }
-
- return 0;
-}
-
-static int nandsim_debugfs_open(struct inode *inode, struct file *file)
-{
- return single_open(file, nandsim_debugfs_show, inode->i_private);
-}
-
-static const struct file_operations dfs_fops = {
- .open = nandsim_debugfs_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-/**
- * nandsim_debugfs_create - initialize debugfs
- * @dev: nandsim device description object
- *
- * This function creates all debugfs files for UBI device @ubi. Returns zero in
- * case of success and a negative error code in case of failure.
- */
-static int nandsim_debugfs_create(struct nandsim *dev)
-{
- struct dentry *root = nsmtd->dbg.dfs_dir;
- struct dentry *dent;
-
- /*
- * Just skip debugfs initialization when the debugfs directory is
- * missing.
- */
- if (IS_ERR_OR_NULL(root)) {
- if (IS_ENABLED(CONFIG_DEBUG_FS) &&
- !IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER))
- NS_WARN("CONFIG_MTD_PARTITIONED_MASTER must be enabled to expose debugfs stuff\n");
- return 0;
- }
-
- dent = debugfs_create_file("nandsim_wear_report", S_IRUSR,
- root, dev, &dfs_fops);
- if (IS_ERR_OR_NULL(dent)) {
- NS_ERR("cannot create \"nandsim_wear_report\" debugfs entry\n");
- return -1;
- }
-
- return 0;
-}
-
-/*
- * Allocate array of page pointers, create slab allocation for an array
- * and initialize the array by NULL pointers.
- *
- * RETURNS: 0 if success, -ENOMEM if memory alloc fails.
- */
-static int __init alloc_device(struct nandsim *ns)
-{
- struct file *cfile;
- int i, err;
-
- if (cache_file) {
- cfile = filp_open(cache_file, O_CREAT | O_RDWR | O_LARGEFILE, 0600);
- if (IS_ERR(cfile))
- return PTR_ERR(cfile);
- if (!(cfile->f_mode & FMODE_CAN_READ)) {
- NS_ERR("alloc_device: cache file not readable\n");
- err = -EINVAL;
- goto err_close;
- }
- if (!(cfile->f_mode & FMODE_CAN_WRITE)) {
- NS_ERR("alloc_device: cache file not writeable\n");
- err = -EINVAL;
- goto err_close;
- }
- ns->pages_written = vzalloc(BITS_TO_LONGS(ns->geom.pgnum) *
- sizeof(unsigned long));
- if (!ns->pages_written) {
- NS_ERR("alloc_device: unable to allocate pages written array\n");
- err = -ENOMEM;
- goto err_close;
- }
- ns->file_buf = kmalloc(ns->geom.pgszoob, GFP_KERNEL);
- if (!ns->file_buf) {
- NS_ERR("alloc_device: unable to allocate file buf\n");
- err = -ENOMEM;
- goto err_free;
- }
- ns->cfile = cfile;
- return 0;
- }
-
- ns->pages = vmalloc(ns->geom.pgnum * sizeof(union ns_mem));
- if (!ns->pages) {
- NS_ERR("alloc_device: unable to allocate page array\n");
- return -ENOMEM;
- }
- for (i = 0; i < ns->geom.pgnum; i++) {
- ns->pages[i].byte = NULL;
- }
- ns->nand_pages_slab = kmem_cache_create("nandsim",
- ns->geom.pgszoob, 0, 0, NULL);
- if (!ns->nand_pages_slab) {
- NS_ERR("cache_create: unable to create kmem_cache\n");
- return -ENOMEM;
- }
-
- return 0;
-
-err_free:
- vfree(ns->pages_written);
-err_close:
- filp_close(cfile, NULL);
- return err;
-}
-
-/*
- * Free any allocated pages, and free the array of page pointers.
- */
-static void free_device(struct nandsim *ns)
-{
- int i;
-
- if (ns->cfile) {
- kfree(ns->file_buf);
- vfree(ns->pages_written);
- filp_close(ns->cfile, NULL);
- return;
- }
-
- if (ns->pages) {
- for (i = 0; i < ns->geom.pgnum; i++) {
- if (ns->pages[i].byte)
- kmem_cache_free(ns->nand_pages_slab,
- ns->pages[i].byte);
- }
- kmem_cache_destroy(ns->nand_pages_slab);
- vfree(ns->pages);
- }
-}
-
-static char __init *get_partition_name(int i)
-{
- return kasprintf(GFP_KERNEL, "NAND simulator partition %d", i);
-}
-
-/*
- * Initialize the nandsim structure.
- *
- * RETURNS: 0 if success, -ERRNO if failure.
- */
-static int __init init_nandsim(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct nandsim *ns = nand_get_controller_data(chip);
- int i, ret = 0;
- uint64_t remains;
- uint64_t next_offset;
-
- if (NS_IS_INITIALIZED(ns)) {
- NS_ERR("init_nandsim: nandsim is already initialized\n");
- return -EIO;
- }
-
- /* Force mtd to not do delays */
- chip->chip_delay = 0;
-
- /* Initialize the NAND flash parameters */
- ns->busw = chip->options & NAND_BUSWIDTH_16 ? 16 : 8;
- ns->geom.totsz = mtd->size;
- ns->geom.pgsz = mtd->writesize;
- ns->geom.oobsz = mtd->oobsize;
- ns->geom.secsz = mtd->erasesize;
- ns->geom.pgszoob = ns->geom.pgsz + ns->geom.oobsz;
- ns->geom.pgnum = div_u64(ns->geom.totsz, ns->geom.pgsz);
- ns->geom.totszoob = ns->geom.totsz + (uint64_t)ns->geom.pgnum * ns->geom.oobsz;
- ns->geom.secshift = ffs(ns->geom.secsz) - 1;
- ns->geom.pgshift = chip->page_shift;
- ns->geom.pgsec = ns->geom.secsz / ns->geom.pgsz;
- ns->geom.secszoob = ns->geom.secsz + ns->geom.oobsz * ns->geom.pgsec;
- ns->options = 0;
-
- if (ns->geom.pgsz == 512) {
- ns->options |= OPT_PAGE512;
- if (ns->busw == 8)
- ns->options |= OPT_PAGE512_8BIT;
- } else if (ns->geom.pgsz == 2048) {
- ns->options |= OPT_PAGE2048;
- } else if (ns->geom.pgsz == 4096) {
- ns->options |= OPT_PAGE4096;
- } else {
- NS_ERR("init_nandsim: unknown page size %u\n", ns->geom.pgsz);
- return -EIO;
- }
-
- if (ns->options & OPT_SMALLPAGE) {
- if (ns->geom.totsz <= (32 << 20)) {
- ns->geom.pgaddrbytes = 3;
- ns->geom.secaddrbytes = 2;
- } else {
- ns->geom.pgaddrbytes = 4;
- ns->geom.secaddrbytes = 3;
- }
- } else {
- if (ns->geom.totsz <= (128 << 20)) {
- ns->geom.pgaddrbytes = 4;
- ns->geom.secaddrbytes = 2;
- } else {
- ns->geom.pgaddrbytes = 5;
- ns->geom.secaddrbytes = 3;
- }
- }
-
- /* Fill the partition_info structure */
- if (parts_num > ARRAY_SIZE(ns->partitions)) {
- NS_ERR("too many partitions.\n");
- return -EINVAL;
- }
- remains = ns->geom.totsz;
- next_offset = 0;
- for (i = 0; i < parts_num; ++i) {
- uint64_t part_sz = (uint64_t)parts[i] * ns->geom.secsz;
-
- if (!part_sz || part_sz > remains) {
- NS_ERR("bad partition size.\n");
- return -EINVAL;
- }
- ns->partitions[i].name = get_partition_name(i);
- if (!ns->partitions[i].name) {
- NS_ERR("unable to allocate memory.\n");
- return -ENOMEM;
- }
- ns->partitions[i].offset = next_offset;
- ns->partitions[i].size = part_sz;
- next_offset += ns->partitions[i].size;
- remains -= ns->partitions[i].size;
- }
- ns->nbparts = parts_num;
- if (remains) {
- if (parts_num + 1 > ARRAY_SIZE(ns->partitions)) {
- NS_ERR("too many partitions.\n");
- return -EINVAL;
- }
- ns->partitions[i].name = get_partition_name(i);
- if (!ns->partitions[i].name) {
- NS_ERR("unable to allocate memory.\n");
- return -ENOMEM;
- }
- ns->partitions[i].offset = next_offset;
- ns->partitions[i].size = remains;
- ns->nbparts += 1;
- }
-
- if (ns->busw == 16)
- NS_WARN("16-bit flashes support wasn't tested\n");
-
- printk("flash size: %llu MiB\n",
- (unsigned long long)ns->geom.totsz >> 20);
- printk("page size: %u bytes\n", ns->geom.pgsz);
- printk("OOB area size: %u bytes\n", ns->geom.oobsz);
- printk("sector size: %u KiB\n", ns->geom.secsz >> 10);
- printk("pages number: %u\n", ns->geom.pgnum);
- printk("pages per sector: %u\n", ns->geom.pgsec);
- printk("bus width: %u\n", ns->busw);
- printk("bits in sector size: %u\n", ns->geom.secshift);
- printk("bits in page size: %u\n", ns->geom.pgshift);
- printk("bits in OOB size: %u\n", ffs(ns->geom.oobsz) - 1);
- printk("flash size with OOB: %llu KiB\n",
- (unsigned long long)ns->geom.totszoob >> 10);
- printk("page address bytes: %u\n", ns->geom.pgaddrbytes);
- printk("sector address bytes: %u\n", ns->geom.secaddrbytes);
- printk("options: %#x\n", ns->options);
-
- if ((ret = alloc_device(ns)) != 0)
- return ret;
-
- /* Allocate / initialize the internal buffer */
- ns->buf.byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL);
- if (!ns->buf.byte) {
- NS_ERR("init_nandsim: unable to allocate %u bytes for the internal buffer\n",
- ns->geom.pgszoob);
- return -ENOMEM;
- }
- memset(ns->buf.byte, 0xFF, ns->geom.pgszoob);
-
- return 0;
-}
-
-/*
- * Free the nandsim structure.
- */
-static void free_nandsim(struct nandsim *ns)
-{
- kfree(ns->buf.byte);
- free_device(ns);
-
- return;
-}
-
-static int parse_badblocks(struct nandsim *ns, struct mtd_info *mtd)
-{
- char *w;
- int zero_ok;
- unsigned int erase_block_no;
- loff_t offset;
-
- if (!badblocks)
- return 0;
- w = badblocks;
- do {
- zero_ok = (*w == '0' ? 1 : 0);
- erase_block_no = simple_strtoul(w, &w, 0);
- if (!zero_ok && !erase_block_no) {
- NS_ERR("invalid badblocks.\n");
- return -EINVAL;
- }
- offset = (loff_t)erase_block_no * ns->geom.secsz;
- if (mtd_block_markbad(mtd, offset)) {
- NS_ERR("invalid badblocks.\n");
- return -EINVAL;
- }
- if (*w == ',')
- w += 1;
- } while (*w);
- return 0;
-}
-
-static int parse_weakblocks(void)
-{
- char *w;
- int zero_ok;
- unsigned int erase_block_no;
- unsigned int max_erases;
- struct weak_block *wb;
-
- if (!weakblocks)
- return 0;
- w = weakblocks;
- do {
- zero_ok = (*w == '0' ? 1 : 0);
- erase_block_no = simple_strtoul(w, &w, 0);
- if (!zero_ok && !erase_block_no) {
- NS_ERR("invalid weakblocks.\n");
- return -EINVAL;
- }
- max_erases = 3;
- if (*w == ':') {
- w += 1;
- max_erases = simple_strtoul(w, &w, 0);
- }
- if (*w == ',')
- w += 1;
- wb = kzalloc(sizeof(*wb), GFP_KERNEL);
- if (!wb) {
- NS_ERR("unable to allocate memory.\n");
- return -ENOMEM;
- }
- wb->erase_block_no = erase_block_no;
- wb->max_erases = max_erases;
- list_add(&wb->list, &weak_blocks);
- } while (*w);
- return 0;
-}
-
-static int erase_error(unsigned int erase_block_no)
-{
- struct weak_block *wb;
-
- list_for_each_entry(wb, &weak_blocks, list)
- if (wb->erase_block_no == erase_block_no) {
- if (wb->erases_done >= wb->max_erases)
- return 1;
- wb->erases_done += 1;
- return 0;
- }
- return 0;
-}
-
-static int parse_weakpages(void)
-{
- char *w;
- int zero_ok;
- unsigned int page_no;
- unsigned int max_writes;
- struct weak_page *wp;
-
- if (!weakpages)
- return 0;
- w = weakpages;
- do {
- zero_ok = (*w == '0' ? 1 : 0);
- page_no = simple_strtoul(w, &w, 0);
- if (!zero_ok && !page_no) {
- NS_ERR("invalid weakpages.\n");
- return -EINVAL;
- }
- max_writes = 3;
- if (*w == ':') {
- w += 1;
- max_writes = simple_strtoul(w, &w, 0);
- }
- if (*w == ',')
- w += 1;
- wp = kzalloc(sizeof(*wp), GFP_KERNEL);
- if (!wp) {
- NS_ERR("unable to allocate memory.\n");
- return -ENOMEM;
- }
- wp->page_no = page_no;
- wp->max_writes = max_writes;
- list_add(&wp->list, &weak_pages);
- } while (*w);
- return 0;
-}
-
-static int write_error(unsigned int page_no)
-{
- struct weak_page *wp;
-
- list_for_each_entry(wp, &weak_pages, list)
- if (wp->page_no == page_no) {
- if (wp->writes_done >= wp->max_writes)
- return 1;
- wp->writes_done += 1;
- return 0;
- }
- return 0;
-}
-
-static int parse_gravepages(void)
-{
- char *g;
- int zero_ok;
- unsigned int page_no;
- unsigned int max_reads;
- struct grave_page *gp;
-
- if (!gravepages)
- return 0;
- g = gravepages;
- do {
- zero_ok = (*g == '0' ? 1 : 0);
- page_no = simple_strtoul(g, &g, 0);
- if (!zero_ok && !page_no) {
- NS_ERR("invalid gravepagess.\n");
- return -EINVAL;
- }
- max_reads = 3;
- if (*g == ':') {
- g += 1;
- max_reads = simple_strtoul(g, &g, 0);
- }
- if (*g == ',')
- g += 1;
- gp = kzalloc(sizeof(*gp), GFP_KERNEL);
- if (!gp) {
- NS_ERR("unable to allocate memory.\n");
- return -ENOMEM;
- }
- gp->page_no = page_no;
- gp->max_reads = max_reads;
- list_add(&gp->list, &grave_pages);
- } while (*g);
- return 0;
-}
-
-static int read_error(unsigned int page_no)
-{
- struct grave_page *gp;
-
- list_for_each_entry(gp, &grave_pages, list)
- if (gp->page_no == page_no) {
- if (gp->reads_done >= gp->max_reads)
- return 1;
- gp->reads_done += 1;
- return 0;
- }
- return 0;
-}
-
-static void free_lists(void)
-{
- struct list_head *pos, *n;
- list_for_each_safe(pos, n, &weak_blocks) {
- list_del(pos);
- kfree(list_entry(pos, struct weak_block, list));
- }
- list_for_each_safe(pos, n, &weak_pages) {
- list_del(pos);
- kfree(list_entry(pos, struct weak_page, list));
- }
- list_for_each_safe(pos, n, &grave_pages) {
- list_del(pos);
- kfree(list_entry(pos, struct grave_page, list));
- }
- kfree(erase_block_wear);
-}
-
-static int setup_wear_reporting(struct mtd_info *mtd)
-{
- size_t mem;
-
- wear_eb_count = div_u64(mtd->size, mtd->erasesize);
- mem = wear_eb_count * sizeof(unsigned long);
- if (mem / sizeof(unsigned long) != wear_eb_count) {
- NS_ERR("Too many erase blocks for wear reporting\n");
- return -ENOMEM;
- }
- erase_block_wear = kzalloc(mem, GFP_KERNEL);
- if (!erase_block_wear) {
- NS_ERR("Too many erase blocks for wear reporting\n");
- return -ENOMEM;
- }
- return 0;
-}
-
-static void update_wear(unsigned int erase_block_no)
-{
- if (!erase_block_wear)
- return;
- total_wear += 1;
- /*
- * TODO: Notify this through a debugfs entry,
- * instead of showing an error message.
- */
- if (total_wear == 0)
- NS_ERR("Erase counter total overflow\n");
- erase_block_wear[erase_block_no] += 1;
- if (erase_block_wear[erase_block_no] == 0)
- NS_ERR("Erase counter overflow for erase block %u\n", erase_block_no);
-}
-
-/*
- * Returns the string representation of 'state' state.
- */
-static char *get_state_name(uint32_t state)
-{
- switch (NS_STATE(state)) {
- case STATE_CMD_READ0:
- return "STATE_CMD_READ0";
- case STATE_CMD_READ1:
- return "STATE_CMD_READ1";
- case STATE_CMD_PAGEPROG:
- return "STATE_CMD_PAGEPROG";
- case STATE_CMD_READOOB:
- return "STATE_CMD_READOOB";
- case STATE_CMD_READSTART:
- return "STATE_CMD_READSTART";
- case STATE_CMD_ERASE1:
- return "STATE_CMD_ERASE1";
- case STATE_CMD_STATUS:
- return "STATE_CMD_STATUS";
- case STATE_CMD_SEQIN:
- return "STATE_CMD_SEQIN";
- case STATE_CMD_READID:
- return "STATE_CMD_READID";
- case STATE_CMD_ERASE2:
- return "STATE_CMD_ERASE2";
- case STATE_CMD_RESET:
- return "STATE_CMD_RESET";
- case STATE_CMD_RNDOUT:
- return "STATE_CMD_RNDOUT";
- case STATE_CMD_RNDOUTSTART:
- return "STATE_CMD_RNDOUTSTART";
- case STATE_ADDR_PAGE:
- return "STATE_ADDR_PAGE";
- case STATE_ADDR_SEC:
- return "STATE_ADDR_SEC";
- case STATE_ADDR_ZERO:
- return "STATE_ADDR_ZERO";
- case STATE_ADDR_COLUMN:
- return "STATE_ADDR_COLUMN";
- case STATE_DATAIN:
- return "STATE_DATAIN";
- case STATE_DATAOUT:
- return "STATE_DATAOUT";
- case STATE_DATAOUT_ID:
- return "STATE_DATAOUT_ID";
- case STATE_DATAOUT_STATUS:
- return "STATE_DATAOUT_STATUS";
- case STATE_READY:
- return "STATE_READY";
- case STATE_UNKNOWN:
- return "STATE_UNKNOWN";
- }
-
- NS_ERR("get_state_name: unknown state, BUG\n");
- return NULL;
-}
-
-/*
- * Check if command is valid.
- *
- * RETURNS: 1 if wrong command, 0 if right.
- */
-static int check_command(int cmd)
-{
- switch (cmd) {
-
- case NAND_CMD_READ0:
- case NAND_CMD_READ1:
- case NAND_CMD_READSTART:
- case NAND_CMD_PAGEPROG:
- case NAND_CMD_READOOB:
- case NAND_CMD_ERASE1:
- case NAND_CMD_STATUS:
- case NAND_CMD_SEQIN:
- case NAND_CMD_READID:
- case NAND_CMD_ERASE2:
- case NAND_CMD_RESET:
- case NAND_CMD_RNDOUT:
- case NAND_CMD_RNDOUTSTART:
- return 0;
-
- default:
- return 1;
- }
-}
-
-/*
- * Returns state after command is accepted by command number.
- */
-static uint32_t get_state_by_command(unsigned command)
-{
- switch (command) {
- case NAND_CMD_READ0:
- return STATE_CMD_READ0;
- case NAND_CMD_READ1:
- return STATE_CMD_READ1;
- case NAND_CMD_PAGEPROG:
- return STATE_CMD_PAGEPROG;
- case NAND_CMD_READSTART:
- return STATE_CMD_READSTART;
- case NAND_CMD_READOOB:
- return STATE_CMD_READOOB;
- case NAND_CMD_ERASE1:
- return STATE_CMD_ERASE1;
- case NAND_CMD_STATUS:
- return STATE_CMD_STATUS;
- case NAND_CMD_SEQIN:
- return STATE_CMD_SEQIN;
- case NAND_CMD_READID:
- return STATE_CMD_READID;
- case NAND_CMD_ERASE2:
- return STATE_CMD_ERASE2;
- case NAND_CMD_RESET:
- return STATE_CMD_RESET;
- case NAND_CMD_RNDOUT:
- return STATE_CMD_RNDOUT;
- case NAND_CMD_RNDOUTSTART:
- return STATE_CMD_RNDOUTSTART;
- }
-
- NS_ERR("get_state_by_command: unknown command, BUG\n");
- return 0;
-}
-
-/*
- * Move an address byte to the correspondent internal register.
- */
-static inline void accept_addr_byte(struct nandsim *ns, u_char bt)
-{
- uint byte = (uint)bt;
-
- if (ns->regs.count < (ns->geom.pgaddrbytes - ns->geom.secaddrbytes))
- ns->regs.column |= (byte << 8 * ns->regs.count);
- else {
- ns->regs.row |= (byte << 8 * (ns->regs.count -
- ns->geom.pgaddrbytes +
- ns->geom.secaddrbytes));
- }
-
- return;
-}
-
-/*
- * Switch to STATE_READY state.
- */
-static inline void switch_to_ready_state(struct nandsim *ns, u_char status)
-{
- NS_DBG("switch_to_ready_state: switch to %s state\n", get_state_name(STATE_READY));
-
- ns->state = STATE_READY;
- ns->nxstate = STATE_UNKNOWN;
- ns->op = NULL;
- ns->npstates = 0;
- ns->stateidx = 0;
- ns->regs.num = 0;
- ns->regs.count = 0;
- ns->regs.off = 0;
- ns->regs.row = 0;
- ns->regs.column = 0;
- ns->regs.status = status;
-}
-
-/*
- * If the operation isn't known yet, try to find it in the global array
- * of supported operations.
- *
- * Operation can be unknown because of the following.
- * 1. New command was accepted and this is the first call to find the
- * correspondent states chain. In this case ns->npstates = 0;
- * 2. There are several operations which begin with the same command(s)
- * (for example program from the second half and read from the
- * second half operations both begin with the READ1 command). In this
- * case the ns->pstates[] array contains previous states.
- *
- * Thus, the function tries to find operation containing the following
- * states (if the 'flag' parameter is 0):
- * ns->pstates[0], ... ns->pstates[ns->npstates], ns->state
- *
- * If (one and only one) matching operation is found, it is accepted (
- * ns->ops, ns->state, ns->nxstate are initialized, ns->npstate is
- * zeroed).
- *
- * If there are several matches, the current state is pushed to the
- * ns->pstates.
- *
- * The operation can be unknown only while commands are input to the chip.
- * As soon as address command is accepted, the operation must be known.
- * In such situation the function is called with 'flag' != 0, and the
- * operation is searched using the following pattern:
- * ns->pstates[0], ... ns->pstates[ns->npstates], <address input>
- *
- * It is supposed that this pattern must either match one operation or
- * none. There can't be ambiguity in that case.
- *
- * If no matches found, the function does the following:
- * 1. if there are saved states present, try to ignore them and search
- * again only using the last command. If nothing was found, switch
- * to the STATE_READY state.
- * 2. if there are no saved states, switch to the STATE_READY state.
- *
- * RETURNS: -2 - no matched operations found.
- * -1 - several matches.
- * 0 - operation is found.
- */
-static int find_operation(struct nandsim *ns, uint32_t flag)
-{
- int opsfound = 0;
- int i, j, idx = 0;
-
- for (i = 0; i < NS_OPER_NUM; i++) {
-
- int found = 1;
-
- if (!(ns->options & ops[i].reqopts))
- /* Ignore operations we can't perform */
- continue;
-
- if (flag) {
- if (!(ops[i].states[ns->npstates] & STATE_ADDR_MASK))
- continue;
- } else {
- if (NS_STATE(ns->state) != NS_STATE(ops[i].states[ns->npstates]))
- continue;
- }
-
- for (j = 0; j < ns->npstates; j++)
- if (NS_STATE(ops[i].states[j]) != NS_STATE(ns->pstates[j])
- && (ns->options & ops[idx].reqopts)) {
- found = 0;
- break;
- }
-
- if (found) {
- idx = i;
- opsfound += 1;
- }
- }
-
- if (opsfound == 1) {
- /* Exact match */
- ns->op = &ops[idx].states[0];
- if (flag) {
- /*
- * In this case the find_operation function was
- * called when address has just began input. But it isn't
- * yet fully input and the current state must
- * not be one of STATE_ADDR_*, but the STATE_ADDR_*
- * state must be the next state (ns->nxstate).
- */
- ns->stateidx = ns->npstates - 1;
- } else {
- ns->stateidx = ns->npstates;
- }
- ns->npstates = 0;
- ns->state = ns->op[ns->stateidx];
- ns->nxstate = ns->op[ns->stateidx + 1];
- NS_DBG("find_operation: operation found, index: %d, state: %s, nxstate %s\n",
- idx, get_state_name(ns->state), get_state_name(ns->nxstate));
- return 0;
- }
-
- if (opsfound == 0) {
- /* Nothing was found. Try to ignore previous commands (if any) and search again */
- if (ns->npstates != 0) {
- NS_DBG("find_operation: no operation found, try again with state %s\n",
- get_state_name(ns->state));
- ns->npstates = 0;
- return find_operation(ns, 0);
-
- }
- NS_DBG("find_operation: no operations found\n");
- switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
- return -2;
- }
-
- if (flag) {
- /* This shouldn't happen */
- NS_DBG("find_operation: BUG, operation must be known if address is input\n");
- return -2;
- }
-
- NS_DBG("find_operation: there is still ambiguity\n");
-
- ns->pstates[ns->npstates++] = ns->state;
-
- return -1;
-}
-
-static void put_pages(struct nandsim *ns)
-{
- int i;
-
- for (i = 0; i < ns->held_cnt; i++)
- put_page(ns->held_pages[i]);
-}
-
-/* Get page cache pages in advance to provide NOFS memory allocation */
-static int get_pages(struct nandsim *ns, struct file *file, size_t count, loff_t pos)
-{
- pgoff_t index, start_index, end_index;
- struct page *page;
- struct address_space *mapping = file->f_mapping;
-
- start_index = pos >> PAGE_SHIFT;
- end_index = (pos + count - 1) >> PAGE_SHIFT;
- if (end_index - start_index + 1 > NS_MAX_HELD_PAGES)
- return -EINVAL;
- ns->held_cnt = 0;
- for (index = start_index; index <= end_index; index++) {
- page = find_get_page(mapping, index);
- if (page == NULL) {
- page = find_or_create_page(mapping, index, GFP_NOFS);
- if (page == NULL) {
- write_inode_now(mapping->host, 1);
- page = find_or_create_page(mapping, index, GFP_NOFS);
- }
- if (page == NULL) {
- put_pages(ns);
- return -ENOMEM;
- }
- unlock_page(page);
- }
- ns->held_pages[ns->held_cnt++] = page;
- }
- return 0;
-}
-
-static ssize_t read_file(struct nandsim *ns, struct file *file, void *buf, size_t count, loff_t pos)
-{
- ssize_t tx;
- int err;
- unsigned int noreclaim_flag;
-
- err = get_pages(ns, file, count, pos);
- if (err)
- return err;
- noreclaim_flag = memalloc_noreclaim_save();
- tx = kernel_read(file, buf, count, &pos);
- memalloc_noreclaim_restore(noreclaim_flag);
- put_pages(ns);
- return tx;
-}
-
-static ssize_t write_file(struct nandsim *ns, struct file *file, void *buf, size_t count, loff_t pos)
-{
- ssize_t tx;
- int err;
- unsigned int noreclaim_flag;
-
- err = get_pages(ns, file, count, pos);
- if (err)
- return err;
- noreclaim_flag = memalloc_noreclaim_save();
- tx = kernel_write(file, buf, count, &pos);
- memalloc_noreclaim_restore(noreclaim_flag);
- put_pages(ns);
- return tx;
-}
-
-/*
- * Returns a pointer to the current page.
- */
-static inline union ns_mem *NS_GET_PAGE(struct nandsim *ns)
-{
- return &(ns->pages[ns->regs.row]);
-}
-
-/*
- * Retuns a pointer to the current byte, within the current page.
- */
-static inline u_char *NS_PAGE_BYTE_OFF(struct nandsim *ns)
-{
- return NS_GET_PAGE(ns)->byte + ns->regs.column + ns->regs.off;
-}
-
-static int do_read_error(struct nandsim *ns, int num)
-{
- unsigned int page_no = ns->regs.row;
-
- if (read_error(page_no)) {
- prandom_bytes(ns->buf.byte, num);
- NS_WARN("simulating read error in page %u\n", page_no);
- return 1;
- }
- return 0;
-}
-
-static void do_bit_flips(struct nandsim *ns, int num)
-{
- if (bitflips && prandom_u32() < (1 << 22)) {
- int flips = 1;
- if (bitflips > 1)
- flips = (prandom_u32() % (int) bitflips) + 1;
- while (flips--) {
- int pos = prandom_u32() % (num * 8);
- ns->buf.byte[pos / 8] ^= (1 << (pos % 8));
- NS_WARN("read_page: flipping bit %d in page %d "
- "reading from %d ecc: corrected=%u failed=%u\n",
- pos, ns->regs.row, ns->regs.column + ns->regs.off,
- nsmtd->ecc_stats.corrected, nsmtd->ecc_stats.failed);
- }
- }
-}
-
-/*
- * Fill the NAND buffer with data read from the specified page.
- */
-static void read_page(struct nandsim *ns, int num)
-{
- union ns_mem *mypage;
-
- if (ns->cfile) {
- if (!test_bit(ns->regs.row, ns->pages_written)) {
- NS_DBG("read_page: page %d not written\n", ns->regs.row);
- memset(ns->buf.byte, 0xFF, num);
- } else {
- loff_t pos;
- ssize_t tx;
-
- NS_DBG("read_page: page %d written, reading from %d\n",
- ns->regs.row, ns->regs.column + ns->regs.off);
- if (do_read_error(ns, num))
- return;
- pos = (loff_t)NS_RAW_OFFSET(ns) + ns->regs.off;
- tx = read_file(ns, ns->cfile, ns->buf.byte, num, pos);
- if (tx != num) {
- NS_ERR("read_page: read error for page %d ret %ld\n", ns->regs.row, (long)tx);
- return;
- }
- do_bit_flips(ns, num);
- }
- return;
- }
-
- mypage = NS_GET_PAGE(ns);
- if (mypage->byte == NULL) {
- NS_DBG("read_page: page %d not allocated\n", ns->regs.row);
- memset(ns->buf.byte, 0xFF, num);
- } else {
- NS_DBG("read_page: page %d allocated, reading from %d\n",
- ns->regs.row, ns->regs.column + ns->regs.off);
- if (do_read_error(ns, num))
- return;
- memcpy(ns->buf.byte, NS_PAGE_BYTE_OFF(ns), num);
- do_bit_flips(ns, num);
- }
-}
-
-/*
- * Erase all pages in the specified sector.
- */
-static void erase_sector(struct nandsim *ns)
-{
- union ns_mem *mypage;
- int i;
-
- if (ns->cfile) {
- for (i = 0; i < ns->geom.pgsec; i++)
- if (__test_and_clear_bit(ns->regs.row + i,
- ns->pages_written)) {
- NS_DBG("erase_sector: freeing page %d\n", ns->regs.row + i);
- }
- return;
- }
-
- mypage = NS_GET_PAGE(ns);
- for (i = 0; i < ns->geom.pgsec; i++) {
- if (mypage->byte != NULL) {
- NS_DBG("erase_sector: freeing page %d\n", ns->regs.row+i);
- kmem_cache_free(ns->nand_pages_slab, mypage->byte);
- mypage->byte = NULL;
- }
- mypage++;
- }
-}
-
-/*
- * Program the specified page with the contents from the NAND buffer.
- */
-static int prog_page(struct nandsim *ns, int num)
-{
- int i;
- union ns_mem *mypage;
- u_char *pg_off;
-
- if (ns->cfile) {
- loff_t off;
- ssize_t tx;
- int all;
-
- NS_DBG("prog_page: writing page %d\n", ns->regs.row);
- pg_off = ns->file_buf + ns->regs.column + ns->regs.off;
- off = (loff_t)NS_RAW_OFFSET(ns) + ns->regs.off;
- if (!test_bit(ns->regs.row, ns->pages_written)) {
- all = 1;
- memset(ns->file_buf, 0xff, ns->geom.pgszoob);
- } else {
- all = 0;
- tx = read_file(ns, ns->cfile, pg_off, num, off);
- if (tx != num) {
- NS_ERR("prog_page: read error for page %d ret %ld\n", ns->regs.row, (long)tx);
- return -1;
- }
- }
- for (i = 0; i < num; i++)
- pg_off[i] &= ns->buf.byte[i];
- if (all) {
- loff_t pos = (loff_t)ns->regs.row * ns->geom.pgszoob;
- tx = write_file(ns, ns->cfile, ns->file_buf, ns->geom.pgszoob, pos);
- if (tx != ns->geom.pgszoob) {
- NS_ERR("prog_page: write error for page %d ret %ld\n", ns->regs.row, (long)tx);
- return -1;
- }
- __set_bit(ns->regs.row, ns->pages_written);
- } else {
- tx = write_file(ns, ns->cfile, pg_off, num, off);
- if (tx != num) {
- NS_ERR("prog_page: write error for page %d ret %ld\n", ns->regs.row, (long)tx);
- return -1;
- }
- }
- return 0;
- }
-
- mypage = NS_GET_PAGE(ns);
- if (mypage->byte == NULL) {
- NS_DBG("prog_page: allocating page %d\n", ns->regs.row);
- /*
- * We allocate memory with GFP_NOFS because a flash FS may
- * utilize this. If it is holding an FS lock, then gets here,
- * then kernel memory alloc runs writeback which goes to the FS
- * again and deadlocks. This was seen in practice.
- */
- mypage->byte = kmem_cache_alloc(ns->nand_pages_slab, GFP_NOFS);
- if (mypage->byte == NULL) {
- NS_ERR("prog_page: error allocating memory for page %d\n", ns->regs.row);
- return -1;
- }
- memset(mypage->byte, 0xFF, ns->geom.pgszoob);
- }
-
- pg_off = NS_PAGE_BYTE_OFF(ns);
- for (i = 0; i < num; i++)
- pg_off[i] &= ns->buf.byte[i];
-
- return 0;
-}
-
-/*
- * If state has any action bit, perform this action.
- *
- * RETURNS: 0 if success, -1 if error.
- */
-static int do_state_action(struct nandsim *ns, uint32_t action)
-{
- int num;
- int busdiv = ns->busw == 8 ? 1 : 2;
- unsigned int erase_block_no, page_no;
-
- action &= ACTION_MASK;
-
- /* Check that page address input is correct */
- if (action != ACTION_SECERASE && ns->regs.row >= ns->geom.pgnum) {
- NS_WARN("do_state_action: wrong page number (%#x)\n", ns->regs.row);
- return -1;
- }
-
- switch (action) {
-
- case ACTION_CPY:
- /*
- * Copy page data to the internal buffer.
- */
-
- /* Column shouldn't be very large */
- if (ns->regs.column >= (ns->geom.pgszoob - ns->regs.off)) {
- NS_ERR("do_state_action: column number is too large\n");
- break;
- }
- num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
- read_page(ns, num);
-
- NS_DBG("do_state_action: (ACTION_CPY:) copy %d bytes to int buf, raw offset %d\n",
- num, NS_RAW_OFFSET(ns) + ns->regs.off);
-
- if (ns->regs.off == 0)
- NS_LOG("read page %d\n", ns->regs.row);
- else if (ns->regs.off < ns->geom.pgsz)
- NS_LOG("read page %d (second half)\n", ns->regs.row);
- else
- NS_LOG("read OOB of page %d\n", ns->regs.row);
-
- NS_UDELAY(access_delay);
- NS_UDELAY(input_cycle * ns->geom.pgsz / 1000 / busdiv);
-
- break;
-
- case ACTION_SECERASE:
- /*
- * Erase sector.
- */
-
- if (ns->lines.wp) {
- NS_ERR("do_state_action: device is write-protected, ignore sector erase\n");
- return -1;
- }
-
- if (ns->regs.row >= ns->geom.pgnum - ns->geom.pgsec
- || (ns->regs.row & ~(ns->geom.secsz - 1))) {
- NS_ERR("do_state_action: wrong sector address (%#x)\n", ns->regs.row);
- return -1;
- }
-
- ns->regs.row = (ns->regs.row <<
- 8 * (ns->geom.pgaddrbytes - ns->geom.secaddrbytes)) | ns->regs.column;
- ns->regs.column = 0;
-
- erase_block_no = ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift);
-
- NS_DBG("do_state_action: erase sector at address %#x, off = %d\n",
- ns->regs.row, NS_RAW_OFFSET(ns));
- NS_LOG("erase sector %u\n", erase_block_no);
-
- erase_sector(ns);
-
- NS_MDELAY(erase_delay);
-
- if (erase_block_wear)
- update_wear(erase_block_no);
-
- if (erase_error(erase_block_no)) {
- NS_WARN("simulating erase failure in erase block %u\n", erase_block_no);
- return -1;
- }
-
- break;
-
- case ACTION_PRGPAGE:
- /*
- * Program page - move internal buffer data to the page.
- */
-
- if (ns->lines.wp) {
- NS_WARN("do_state_action: device is write-protected, programm\n");
- return -1;
- }
-
- num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
- if (num != ns->regs.count) {
- NS_ERR("do_state_action: too few bytes were input (%d instead of %d)\n",
- ns->regs.count, num);
- return -1;
- }
-
- if (prog_page(ns, num) == -1)
- return -1;
-
- page_no = ns->regs.row;
-
- NS_DBG("do_state_action: copy %d bytes from int buf to (%#x, %#x), raw off = %d\n",
- num, ns->regs.row, ns->regs.column, NS_RAW_OFFSET(ns) + ns->regs.off);
- NS_LOG("programm page %d\n", ns->regs.row);
-
- NS_UDELAY(programm_delay);
- NS_UDELAY(output_cycle * ns->geom.pgsz / 1000 / busdiv);
-
- if (write_error(page_no)) {
- NS_WARN("simulating write failure in page %u\n", page_no);
- return -1;
- }
-
- break;
-
- case ACTION_ZEROOFF:
- NS_DBG("do_state_action: set internal offset to 0\n");
- ns->regs.off = 0;
- break;
-
- case ACTION_HALFOFF:
- if (!(ns->options & OPT_PAGE512_8BIT)) {
- NS_ERR("do_state_action: BUG! can't skip half of page for non-512"
- "byte page size 8x chips\n");
- return -1;
- }
- NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz/2);
- ns->regs.off = ns->geom.pgsz/2;
- break;
-
- case ACTION_OOBOFF:
- NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz);
- ns->regs.off = ns->geom.pgsz;
- break;
-
- default:
- NS_DBG("do_state_action: BUG! unknown action\n");
- }
-
- return 0;
-}
-
-/*
- * Switch simulator's state.
- */
-static void switch_state(struct nandsim *ns)
-{
- if (ns->op) {
- /*
- * The current operation have already been identified.
- * Just follow the states chain.
- */
-
- ns->stateidx += 1;
- ns->state = ns->nxstate;
- ns->nxstate = ns->op[ns->stateidx + 1];
-
- NS_DBG("switch_state: operation is known, switch to the next state, "
- "state: %s, nxstate: %s\n",
- get_state_name(ns->state), get_state_name(ns->nxstate));
-
- /* See, whether we need to do some action */
- if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) {
- switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
- return;
- }
-
- } else {
- /*
- * We don't yet know which operation we perform.
- * Try to identify it.
- */
-
- /*
- * The only event causing the switch_state function to
- * be called with yet unknown operation is new command.
- */
- ns->state = get_state_by_command(ns->regs.command);
-
- NS_DBG("switch_state: operation is unknown, try to find it\n");
-
- if (find_operation(ns, 0) != 0)
- return;
-
- if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) {
- switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
- return;
- }
- }
-
- /* For 16x devices column means the page offset in words */
- if ((ns->nxstate & STATE_ADDR_MASK) && ns->busw == 16) {
- NS_DBG("switch_state: double the column number for 16x device\n");
- ns->regs.column <<= 1;
- }
-
- if (NS_STATE(ns->nxstate) == STATE_READY) {
- /*
- * The current state is the last. Return to STATE_READY
- */
-
- u_char status = NS_STATUS_OK(ns);
-
- /* In case of data states, see if all bytes were input/output */
- if ((ns->state & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK))
- && ns->regs.count != ns->regs.num) {
- NS_WARN("switch_state: not all bytes were processed, %d left\n",
- ns->regs.num - ns->regs.count);
- status = NS_STATUS_FAILED(ns);
- }
-
- NS_DBG("switch_state: operation complete, switch to STATE_READY state\n");
-
- switch_to_ready_state(ns, status);
-
- return;
- } else if (ns->nxstate & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK)) {
- /*
- * If the next state is data input/output, switch to it now
- */
-
- ns->state = ns->nxstate;
- ns->nxstate = ns->op[++ns->stateidx + 1];
- ns->regs.num = ns->regs.count = 0;
-
- NS_DBG("switch_state: the next state is data I/O, switch, "
- "state: %s, nxstate: %s\n",
- get_state_name(ns->state), get_state_name(ns->nxstate));
-
- /*
- * Set the internal register to the count of bytes which
- * are expected to be input or output
- */
- switch (NS_STATE(ns->state)) {
- case STATE_DATAIN:
- case STATE_DATAOUT:
- ns->regs.num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
- break;
-
- case STATE_DATAOUT_ID:
- ns->regs.num = ns->geom.idbytes;
- break;
-
- case STATE_DATAOUT_STATUS:
- ns->regs.count = ns->regs.num = 0;
- break;
-
- default:
- NS_ERR("switch_state: BUG! unknown data state\n");
- }
-
- } else if (ns->nxstate & STATE_ADDR_MASK) {
- /*
- * If the next state is address input, set the internal
- * register to the number of expected address bytes
- */
-
- ns->regs.count = 0;
-
- switch (NS_STATE(ns->nxstate)) {
- case STATE_ADDR_PAGE:
- ns->regs.num = ns->geom.pgaddrbytes;
-
- break;
- case STATE_ADDR_SEC:
- ns->regs.num = ns->geom.secaddrbytes;
- break;
-
- case STATE_ADDR_ZERO:
- ns->regs.num = 1;
- break;
-
- case STATE_ADDR_COLUMN:
- /* Column address is always 2 bytes */
- ns->regs.num = ns->geom.pgaddrbytes - ns->geom.secaddrbytes;
- break;
-
- default:
- NS_ERR("switch_state: BUG! unknown address state\n");
- }
- } else {
- /*
- * Just reset internal counters.
- */
-
- ns->regs.num = 0;
- ns->regs.count = 0;
- }
-}
-
-static u_char ns_nand_read_byte(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct nandsim *ns = nand_get_controller_data(chip);
- u_char outb = 0x00;
-
- /* Sanity and correctness checks */
- if (!ns->lines.ce) {
- NS_ERR("read_byte: chip is disabled, return %#x\n", (uint)outb);
- return outb;
- }
- if (ns->lines.ale || ns->lines.cle) {
- NS_ERR("read_byte: ALE or CLE pin is high, return %#x\n", (uint)outb);
- return outb;
- }
- if (!(ns->state & STATE_DATAOUT_MASK)) {
- NS_WARN("read_byte: unexpected data output cycle, state is %s "
- "return %#x\n", get_state_name(ns->state), (uint)outb);
- return outb;
- }
-
- /* Status register may be read as many times as it is wanted */
- if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS) {
- NS_DBG("read_byte: return %#x status\n", ns->regs.status);
- return ns->regs.status;
- }
-
- /* Check if there is any data in the internal buffer which may be read */
- if (ns->regs.count == ns->regs.num) {
- NS_WARN("read_byte: no more data to output, return %#x\n", (uint)outb);
- return outb;
- }
-
- switch (NS_STATE(ns->state)) {
- case STATE_DATAOUT:
- if (ns->busw == 8) {
- outb = ns->buf.byte[ns->regs.count];
- ns->regs.count += 1;
- } else {
- outb = (u_char)cpu_to_le16(ns->buf.word[ns->regs.count >> 1]);
- ns->regs.count += 2;
- }
- break;
- case STATE_DATAOUT_ID:
- NS_DBG("read_byte: read ID byte %d, total = %d\n", ns->regs.count, ns->regs.num);
- outb = ns->ids[ns->regs.count];
- ns->regs.count += 1;
- break;
- default:
- BUG();
- }
-
- if (ns->regs.count == ns->regs.num) {
- NS_DBG("read_byte: all bytes were read\n");
-
- if (NS_STATE(ns->nxstate) == STATE_READY)
- switch_state(ns);
- }
-
- return outb;
-}
-
-static void ns_nand_write_byte(struct mtd_info *mtd, u_char byte)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct nandsim *ns = nand_get_controller_data(chip);
-
- /* Sanity and correctness checks */
- if (!ns->lines.ce) {
- NS_ERR("write_byte: chip is disabled, ignore write\n");
- return;
- }
- if (ns->lines.ale && ns->lines.cle) {
- NS_ERR("write_byte: ALE and CLE pins are high simultaneously, ignore write\n");
- return;
- }
-
- if (ns->lines.cle == 1) {
- /*
- * The byte written is a command.
- */
-
- if (byte == NAND_CMD_RESET) {
- NS_LOG("reset chip\n");
- switch_to_ready_state(ns, NS_STATUS_OK(ns));
- return;
- }
-
- /* Check that the command byte is correct */
- if (check_command(byte)) {
- NS_ERR("write_byte: unknown command %#x\n", (uint)byte);
- return;
- }
-
- if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS
- || NS_STATE(ns->state) == STATE_DATAOUT) {
- int row = ns->regs.row;
-
- switch_state(ns);
- if (byte == NAND_CMD_RNDOUT)
- ns->regs.row = row;
- }
-
- /* Check if chip is expecting command */
- if (NS_STATE(ns->nxstate) != STATE_UNKNOWN && !(ns->nxstate & STATE_CMD_MASK)) {
- /* Do not warn if only 2 id bytes are read */
- if (!(ns->regs.command == NAND_CMD_READID &&
- NS_STATE(ns->state) == STATE_DATAOUT_ID && ns->regs.count == 2)) {
- /*
- * We are in situation when something else (not command)
- * was expected but command was input. In this case ignore
- * previous command(s)/state(s) and accept the last one.
- */
- NS_WARN("write_byte: command (%#x) wasn't expected, expected state is %s, "
- "ignore previous states\n", (uint)byte, get_state_name(ns->nxstate));
- }
- switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
- }
-
- NS_DBG("command byte corresponding to %s state accepted\n",
- get_state_name(get_state_by_command(byte)));
- ns->regs.command = byte;
- switch_state(ns);
-
- } else if (ns->lines.ale == 1) {
- /*
- * The byte written is an address.
- */
-
- if (NS_STATE(ns->nxstate) == STATE_UNKNOWN) {
-
- NS_DBG("write_byte: operation isn't known yet, identify it\n");
-
- if (find_operation(ns, 1) < 0)
- return;
-
- if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) {
- switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
- return;
- }
-
- ns->regs.count = 0;
- switch (NS_STATE(ns->nxstate)) {
- case STATE_ADDR_PAGE:
- ns->regs.num = ns->geom.pgaddrbytes;
- break;
- case STATE_ADDR_SEC:
- ns->regs.num = ns->geom.secaddrbytes;
- break;
- case STATE_ADDR_ZERO:
- ns->regs.num = 1;
- break;
- default:
- BUG();
- }
- }
-
- /* Check that chip is expecting address */
- if (!(ns->nxstate & STATE_ADDR_MASK)) {
- NS_ERR("write_byte: address (%#x) isn't expected, expected state is %s, "
- "switch to STATE_READY\n", (uint)byte, get_state_name(ns->nxstate));
- switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
- return;
- }
-
- /* Check if this is expected byte */
- if (ns->regs.count == ns->regs.num) {
- NS_ERR("write_byte: no more address bytes expected\n");
- switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
- return;
- }
-
- accept_addr_byte(ns, byte);
-
- ns->regs.count += 1;
-
- NS_DBG("write_byte: address byte %#x was accepted (%d bytes input, %d expected)\n",
- (uint)byte, ns->regs.count, ns->regs.num);
-
- if (ns->regs.count == ns->regs.num) {
- NS_DBG("address (%#x, %#x) is accepted\n", ns->regs.row, ns->regs.column);
- switch_state(ns);
- }
-
- } else {
- /*
- * The byte written is an input data.
- */
-
- /* Check that chip is expecting data input */
- if (!(ns->state & STATE_DATAIN_MASK)) {
- NS_ERR("write_byte: data input (%#x) isn't expected, state is %s, "
- "switch to %s\n", (uint)byte,
- get_state_name(ns->state), get_state_name(STATE_READY));
- switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
- return;
- }
-
- /* Check if this is expected byte */
- if (ns->regs.count == ns->regs.num) {
- NS_WARN("write_byte: %u input bytes has already been accepted, ignore write\n",
- ns->regs.num);
- return;
- }
-
- if (ns->busw == 8) {
- ns->buf.byte[ns->regs.count] = byte;
- ns->regs.count += 1;
- } else {
- ns->buf.word[ns->regs.count >> 1] = cpu_to_le16((uint16_t)byte);
- ns->regs.count += 2;
- }
- }
-
- return;
-}
-
-static void ns_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int bitmask)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct nandsim *ns = nand_get_controller_data(chip);
-
- ns->lines.cle = bitmask & NAND_CLE ? 1 : 0;
- ns->lines.ale = bitmask & NAND_ALE ? 1 : 0;
- ns->lines.ce = bitmask & NAND_NCE ? 1 : 0;
-
- if (cmd != NAND_CMD_NONE)
- ns_nand_write_byte(mtd, cmd);
-}
-
-static int ns_device_ready(struct mtd_info *mtd)
-{
- NS_DBG("device_ready\n");
- return 1;
-}
-
-static uint16_t ns_nand_read_word(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- NS_DBG("read_word\n");
-
- return chip->read_byte(mtd) | (chip->read_byte(mtd) << 8);
-}
-
-static void ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct nandsim *ns = nand_get_controller_data(chip);
-
- /* Check that chip is expecting data input */
- if (!(ns->state & STATE_DATAIN_MASK)) {
- NS_ERR("write_buf: data input isn't expected, state is %s, "
- "switch to STATE_READY\n", get_state_name(ns->state));
- switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
- return;
- }
-
- /* Check if these are expected bytes */
- if (ns->regs.count + len > ns->regs.num) {
- NS_ERR("write_buf: too many input bytes\n");
- switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
- return;
- }
-
- memcpy(ns->buf.byte + ns->regs.count, buf, len);
- ns->regs.count += len;
-
- if (ns->regs.count == ns->regs.num) {
- NS_DBG("write_buf: %d bytes were written\n", ns->regs.count);
- }
-}
-
-static void ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct nandsim *ns = nand_get_controller_data(chip);
-
- /* Sanity and correctness checks */
- if (!ns->lines.ce) {
- NS_ERR("read_buf: chip is disabled\n");
- return;
- }
- if (ns->lines.ale || ns->lines.cle) {
- NS_ERR("read_buf: ALE or CLE pin is high\n");
- return;
- }
- if (!(ns->state & STATE_DATAOUT_MASK)) {
- NS_WARN("read_buf: unexpected data output cycle, current state is %s\n",
- get_state_name(ns->state));
- return;
- }
-
- if (NS_STATE(ns->state) != STATE_DATAOUT) {
- int i;
-
- for (i = 0; i < len; i++)
- buf[i] = mtd_to_nand(mtd)->read_byte(mtd);
-
- return;
- }
-
- /* Check if these are expected bytes */
- if (ns->regs.count + len > ns->regs.num) {
- NS_ERR("read_buf: too many bytes to read\n");
- switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
- return;
- }
-
- memcpy(buf, ns->buf.byte + ns->regs.count, len);
- ns->regs.count += len;
-
- if (ns->regs.count == ns->regs.num) {
- if (NS_STATE(ns->nxstate) == STATE_READY)
- switch_state(ns);
- }
-
- return;
-}
-
-/*
- * Module initialization function
- */
-static int __init ns_init_module(void)
-{
- struct nand_chip *chip;
- struct nandsim *nand;
- int retval = -ENOMEM, i;
-
- if (bus_width != 8 && bus_width != 16) {
- NS_ERR("wrong bus width (%d), use only 8 or 16\n", bus_width);
- return -EINVAL;
- }
-
- /* Allocate and initialize mtd_info, nand_chip and nandsim structures */
- chip = kzalloc(sizeof(struct nand_chip) + sizeof(struct nandsim),
- GFP_KERNEL);
- if (!chip) {
- NS_ERR("unable to allocate core structures.\n");
- return -ENOMEM;
- }
- nsmtd = nand_to_mtd(chip);
- nand = (struct nandsim *)(chip + 1);
- nand_set_controller_data(chip, (void *)nand);
-
- /*
- * Register simulator's callbacks.
- */
- chip->cmd_ctrl = ns_hwcontrol;
- chip->read_byte = ns_nand_read_byte;
- chip->dev_ready = ns_device_ready;
- chip->write_buf = ns_nand_write_buf;
- chip->read_buf = ns_nand_read_buf;
- chip->read_word = ns_nand_read_word;
- chip->ecc.mode = NAND_ECC_SOFT;
- chip->ecc.algo = NAND_ECC_HAMMING;
- /* The NAND_SKIP_BBTSCAN option is necessary for 'overridesize' */
- /* and 'badblocks' parameters to work */
- chip->options |= NAND_SKIP_BBTSCAN;
-
- switch (bbt) {
- case 2:
- chip->bbt_options |= NAND_BBT_NO_OOB;
- case 1:
- chip->bbt_options |= NAND_BBT_USE_FLASH;
- case 0:
- break;
- default:
- NS_ERR("bbt has to be 0..2\n");
- retval = -EINVAL;
- goto error;
- }
- /*
- * Perform minimum nandsim structure initialization to handle
- * the initial ID read command correctly
- */
- if (id_bytes[6] != 0xFF || id_bytes[7] != 0xFF)
- nand->geom.idbytes = 8;
- else if (id_bytes[4] != 0xFF || id_bytes[5] != 0xFF)
- nand->geom.idbytes = 6;
- else if (id_bytes[2] != 0xFF || id_bytes[3] != 0xFF)
- nand->geom.idbytes = 4;
- else
- nand->geom.idbytes = 2;
- nand->regs.status = NS_STATUS_OK(nand);
- nand->nxstate = STATE_UNKNOWN;
- nand->options |= OPT_PAGE512; /* temporary value */
- memcpy(nand->ids, id_bytes, sizeof(nand->ids));
- if (bus_width == 16) {
- nand->busw = 16;
- chip->options |= NAND_BUSWIDTH_16;
- }
-
- nsmtd->owner = THIS_MODULE;
-
- if ((retval = parse_weakblocks()) != 0)
- goto error;
-
- if ((retval = parse_weakpages()) != 0)
- goto error;
-
- if ((retval = parse_gravepages()) != 0)
- goto error;
-
- retval = nand_scan_ident(nsmtd, 1, NULL);
- if (retval) {
- NS_ERR("cannot scan NAND Simulator device\n");
- goto error;
- }
-
- if (bch) {
- unsigned int eccsteps, eccbytes;
- if (!mtd_nand_has_bch()) {
- NS_ERR("BCH ECC support is disabled\n");
- retval = -EINVAL;
- goto error;
- }
- /* use 512-byte ecc blocks */
- eccsteps = nsmtd->writesize/512;
- eccbytes = (bch*13+7)/8;
- /* do not bother supporting small page devices */
- if ((nsmtd->oobsize < 64) || !eccsteps) {
- NS_ERR("bch not available on small page devices\n");
- retval = -EINVAL;
- goto error;
- }
- if ((eccbytes*eccsteps+2) > nsmtd->oobsize) {
- NS_ERR("invalid bch value %u\n", bch);
- retval = -EINVAL;
- goto error;
- }
- chip->ecc.mode = NAND_ECC_SOFT;
- chip->ecc.algo = NAND_ECC_BCH;
- chip->ecc.size = 512;
- chip->ecc.strength = bch;
- chip->ecc.bytes = eccbytes;
- NS_INFO("using %u-bit/%u bytes BCH ECC\n", bch, chip->ecc.size);
- }
-
- retval = nand_scan_tail(nsmtd);
- if (retval) {
- NS_ERR("can't register NAND Simulator\n");
- goto error;
- }
-
- if (overridesize) {
- uint64_t new_size = (uint64_t)nsmtd->erasesize << overridesize;
- if (new_size >> overridesize != nsmtd->erasesize) {
- NS_ERR("overridesize is too big\n");
- retval = -EINVAL;
- goto err_exit;
- }
- /* N.B. This relies on nand_scan not doing anything with the size before we change it */
- nsmtd->size = new_size;
- chip->chipsize = new_size;
- chip->chip_shift = ffs(nsmtd->erasesize) + overridesize - 1;
- chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;
- }
-
- if ((retval = setup_wear_reporting(nsmtd)) != 0)
- goto err_exit;
-
- if ((retval = init_nandsim(nsmtd)) != 0)
- goto err_exit;
-
- if ((retval = chip->scan_bbt(nsmtd)) != 0)
- goto err_exit;
-
- if ((retval = parse_badblocks(nand, nsmtd)) != 0)
- goto err_exit;
-
- /* Register NAND partitions */
- retval = mtd_device_register(nsmtd, &nand->partitions[0],
- nand->nbparts);
- if (retval != 0)
- goto err_exit;
-
- if ((retval = nandsim_debugfs_create(nand)) != 0)
- goto err_exit;
-
- return 0;
-
-err_exit:
- free_nandsim(nand);
- nand_release(nsmtd);
- for (i = 0;i < ARRAY_SIZE(nand->partitions); ++i)
- kfree(nand->partitions[i].name);
-error:
- kfree(chip);
- free_lists();
-
- return retval;
-}
-
-module_init(ns_init_module);
-
-/*
- * Module clean-up function
- */
-static void __exit ns_cleanup_module(void)
-{
- struct nand_chip *chip = mtd_to_nand(nsmtd);
- struct nandsim *ns = nand_get_controller_data(chip);
- int i;
-
- free_nandsim(ns); /* Free nandsim private resources */
- nand_release(nsmtd); /* Unregister driver */
- for (i = 0;i < ARRAY_SIZE(ns->partitions); ++i)
- kfree(ns->partitions[i].name);
- kfree(mtd_to_nand(nsmtd)); /* Free other structures */
- free_lists();
-}
-
-module_exit(ns_cleanup_module);
-
-MODULE_LICENSE ("GPL");
-MODULE_AUTHOR ("Artem B. Bityuckiy");
-MODULE_DESCRIPTION ("The NAND flash simulator");
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
deleted file mode 100644
index 8cdf7d3d8fa7..000000000000
--- a/drivers/mtd/nand/omap2.c
+++ /dev/null
@@ -1,2316 +0,0 @@
-/*
- * Copyright © 2004 Texas Instruments, Jian Zhang <jzhang@ti.com>
- * Copyright © 2004 Micron Technology Inc.
- * Copyright © 2004 David Brownell
- *
- * 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.
- */
-
-#include <linux/platform_device.h>
-#include <linux/dmaengine.h>
-#include <linux/dma-mapping.h>
-#include <linux/delay.h>
-#include <linux/gpio/consumer.h>
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/jiffies.h>
-#include <linux/sched.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/partitions.h>
-#include <linux/omap-dma.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-
-#include <linux/mtd/nand_bch.h>
-#include <linux/platform_data/elm.h>
-
-#include <linux/omap-gpmc.h>
-#include <linux/platform_data/mtd-nand-omap2.h>
-
-#define DRIVER_NAME "omap2-nand"
-#define OMAP_NAND_TIMEOUT_MS 5000
-
-#define NAND_Ecc_P1e (1 << 0)
-#define NAND_Ecc_P2e (1 << 1)
-#define NAND_Ecc_P4e (1 << 2)
-#define NAND_Ecc_P8e (1 << 3)
-#define NAND_Ecc_P16e (1 << 4)
-#define NAND_Ecc_P32e (1 << 5)
-#define NAND_Ecc_P64e (1 << 6)
-#define NAND_Ecc_P128e (1 << 7)
-#define NAND_Ecc_P256e (1 << 8)
-#define NAND_Ecc_P512e (1 << 9)
-#define NAND_Ecc_P1024e (1 << 10)
-#define NAND_Ecc_P2048e (1 << 11)
-
-#define NAND_Ecc_P1o (1 << 16)
-#define NAND_Ecc_P2o (1 << 17)
-#define NAND_Ecc_P4o (1 << 18)
-#define NAND_Ecc_P8o (1 << 19)
-#define NAND_Ecc_P16o (1 << 20)
-#define NAND_Ecc_P32o (1 << 21)
-#define NAND_Ecc_P64o (1 << 22)
-#define NAND_Ecc_P128o (1 << 23)
-#define NAND_Ecc_P256o (1 << 24)
-#define NAND_Ecc_P512o (1 << 25)
-#define NAND_Ecc_P1024o (1 << 26)
-#define NAND_Ecc_P2048o (1 << 27)
-
-#define TF(value) (value ? 1 : 0)
-
-#define P2048e(a) (TF(a & NAND_Ecc_P2048e) << 0)
-#define P2048o(a) (TF(a & NAND_Ecc_P2048o) << 1)
-#define P1e(a) (TF(a & NAND_Ecc_P1e) << 2)
-#define P1o(a) (TF(a & NAND_Ecc_P1o) << 3)
-#define P2e(a) (TF(a & NAND_Ecc_P2e) << 4)
-#define P2o(a) (TF(a & NAND_Ecc_P2o) << 5)
-#define P4e(a) (TF(a & NAND_Ecc_P4e) << 6)
-#define P4o(a) (TF(a & NAND_Ecc_P4o) << 7)
-
-#define P8e(a) (TF(a & NAND_Ecc_P8e) << 0)
-#define P8o(a) (TF(a & NAND_Ecc_P8o) << 1)
-#define P16e(a) (TF(a & NAND_Ecc_P16e) << 2)
-#define P16o(a) (TF(a & NAND_Ecc_P16o) << 3)
-#define P32e(a) (TF(a & NAND_Ecc_P32e) << 4)
-#define P32o(a) (TF(a & NAND_Ecc_P32o) << 5)
-#define P64e(a) (TF(a & NAND_Ecc_P64e) << 6)
-#define P64o(a) (TF(a & NAND_Ecc_P64o) << 7)
-
-#define P128e(a) (TF(a & NAND_Ecc_P128e) << 0)
-#define P128o(a) (TF(a & NAND_Ecc_P128o) << 1)
-#define P256e(a) (TF(a & NAND_Ecc_P256e) << 2)
-#define P256o(a) (TF(a & NAND_Ecc_P256o) << 3)
-#define P512e(a) (TF(a & NAND_Ecc_P512e) << 4)
-#define P512o(a) (TF(a & NAND_Ecc_P512o) << 5)
-#define P1024e(a) (TF(a & NAND_Ecc_P1024e) << 6)
-#define P1024o(a) (TF(a & NAND_Ecc_P1024o) << 7)
-
-#define P8e_s(a) (TF(a & NAND_Ecc_P8e) << 0)
-#define P8o_s(a) (TF(a & NAND_Ecc_P8o) << 1)
-#define P16e_s(a) (TF(a & NAND_Ecc_P16e) << 2)
-#define P16o_s(a) (TF(a & NAND_Ecc_P16o) << 3)
-#define P1e_s(a) (TF(a & NAND_Ecc_P1e) << 4)
-#define P1o_s(a) (TF(a & NAND_Ecc_P1o) << 5)
-#define P2e_s(a) (TF(a & NAND_Ecc_P2e) << 6)
-#define P2o_s(a) (TF(a & NAND_Ecc_P2o) << 7)
-
-#define P4e_s(a) (TF(a & NAND_Ecc_P4e) << 0)
-#define P4o_s(a) (TF(a & NAND_Ecc_P4o) << 1)
-
-#define PREFETCH_CONFIG1_CS_SHIFT 24
-#define ECC_CONFIG_CS_SHIFT 1
-#define CS_MASK 0x7
-#define ENABLE_PREFETCH (0x1 << 7)
-#define DMA_MPU_MODE_SHIFT 2
-#define ECCSIZE0_SHIFT 12
-#define ECCSIZE1_SHIFT 22
-#define ECC1RESULTSIZE 0x1
-#define ECCCLEAR 0x100
-#define ECC1 0x1
-#define PREFETCH_FIFOTHRESHOLD_MAX 0x40
-#define PREFETCH_FIFOTHRESHOLD(val) ((val) << 8)
-#define PREFETCH_STATUS_COUNT(val) (val & 0x00003fff)
-#define PREFETCH_STATUS_FIFO_CNT(val) ((val >> 24) & 0x7F)
-#define STATUS_BUFF_EMPTY 0x00000001
-
-#define SECTOR_BYTES 512
-/* 4 bit padding to make byte aligned, 56 = 52 + 4 */
-#define BCH4_BIT_PAD 4
-
-/* GPMC ecc engine settings for read */
-#define BCH_WRAPMODE_1 1 /* BCH wrap mode 1 */
-#define BCH8R_ECC_SIZE0 0x1a /* ecc_size0 = 26 */
-#define BCH8R_ECC_SIZE1 0x2 /* ecc_size1 = 2 */
-#define BCH4R_ECC_SIZE0 0xd /* ecc_size0 = 13 */
-#define BCH4R_ECC_SIZE1 0x3 /* ecc_size1 = 3 */
-
-/* GPMC ecc engine settings for write */
-#define BCH_WRAPMODE_6 6 /* BCH wrap mode 6 */
-#define BCH_ECC_SIZE0 0x0 /* ecc_size0 = 0, no oob protection */
-#define BCH_ECC_SIZE1 0x20 /* ecc_size1 = 32 */
-
-#define BADBLOCK_MARKER_LENGTH 2
-
-static u_char bch16_vector[] = {0xf5, 0x24, 0x1c, 0xd0, 0x61, 0xb3, 0xf1, 0x55,
- 0x2e, 0x2c, 0x86, 0xa3, 0xed, 0x36, 0x1b, 0x78,
- 0x48, 0x76, 0xa9, 0x3b, 0x97, 0xd1, 0x7a, 0x93,
- 0x07, 0x0e};
-static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc,
- 0xac, 0x6b, 0xff, 0x99, 0x7b};
-static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10};
-
-/* Shared among all NAND instances to synchronize access to the ECC Engine */
-static struct nand_hw_control omap_gpmc_controller = {
- .lock = __SPIN_LOCK_UNLOCKED(omap_gpmc_controller.lock),
- .wq = __WAIT_QUEUE_HEAD_INITIALIZER(omap_gpmc_controller.wq),
-};
-
-struct omap_nand_info {
- struct nand_chip nand;
- struct platform_device *pdev;
-
- int gpmc_cs;
- bool dev_ready;
- enum nand_io xfer_type;
- int devsize;
- enum omap_ecc ecc_opt;
- struct device_node *elm_of_node;
-
- unsigned long phys_base;
- struct completion comp;
- struct dma_chan *dma;
- int gpmc_irq_fifo;
- int gpmc_irq_count;
- enum {
- OMAP_NAND_IO_READ = 0, /* read */
- OMAP_NAND_IO_WRITE, /* write */
- } iomode;
- u_char *buf;
- int buf_len;
- /* Interface to GPMC */
- struct gpmc_nand_regs reg;
- struct gpmc_nand_ops *ops;
- bool flash_bbt;
- /* fields specific for BCHx_HW ECC scheme */
- struct device *elm_dev;
- /* NAND ready gpio */
- struct gpio_desc *ready_gpiod;
-};
-
-static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd)
-{
- return container_of(mtd_to_nand(mtd), struct omap_nand_info, nand);
-}
-
-/**
- * omap_prefetch_enable - configures and starts prefetch transfer
- * @cs: cs (chip select) number
- * @fifo_th: fifo threshold to be used for read/ write
- * @dma_mode: dma mode enable (1) or disable (0)
- * @u32_count: number of bytes to be transferred
- * @is_write: prefetch read(0) or write post(1) mode
- */
-static int omap_prefetch_enable(int cs, int fifo_th, int dma_mode,
- unsigned int u32_count, int is_write, struct omap_nand_info *info)
-{
- u32 val;
-
- if (fifo_th > PREFETCH_FIFOTHRESHOLD_MAX)
- return -1;
-
- if (readl(info->reg.gpmc_prefetch_control))
- return -EBUSY;
-
- /* Set the amount of bytes to be prefetched */
- writel(u32_count, info->reg.gpmc_prefetch_config2);
-
- /* Set dma/mpu mode, the prefetch read / post write and
- * enable the engine. Set which cs is has requested for.
- */
- val = ((cs << PREFETCH_CONFIG1_CS_SHIFT) |
- PREFETCH_FIFOTHRESHOLD(fifo_th) | ENABLE_PREFETCH |
- (dma_mode << DMA_MPU_MODE_SHIFT) | (is_write & 0x1));
- writel(val, info->reg.gpmc_prefetch_config1);
-
- /* Start the prefetch engine */
- writel(0x1, info->reg.gpmc_prefetch_control);
-
- return 0;
-}
-
-/**
- * omap_prefetch_reset - disables and stops the prefetch engine
- */
-static int omap_prefetch_reset(int cs, struct omap_nand_info *info)
-{
- u32 config1;
-
- /* check if the same module/cs is trying to reset */
- config1 = readl(info->reg.gpmc_prefetch_config1);
- if (((config1 >> PREFETCH_CONFIG1_CS_SHIFT) & CS_MASK) != cs)
- return -EINVAL;
-
- /* Stop the PFPW engine */
- writel(0x0, info->reg.gpmc_prefetch_control);
-
- /* Reset/disable the PFPW engine */
- writel(0x0, info->reg.gpmc_prefetch_config1);
-
- return 0;
-}
-
-/**
- * omap_hwcontrol - hardware specific access to control-lines
- * @mtd: MTD device structure
- * @cmd: command to device
- * @ctrl:
- * NAND_NCE: bit 0 -> don't care
- * NAND_CLE: bit 1 -> Command Latch
- * NAND_ALE: bit 2 -> Address Latch
- *
- * NOTE: boards may use different bits for these!!
- */
-static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
-{
- struct omap_nand_info *info = mtd_to_omap(mtd);
-
- if (cmd != NAND_CMD_NONE) {
- if (ctrl & NAND_CLE)
- writeb(cmd, info->reg.gpmc_nand_command);
-
- else if (ctrl & NAND_ALE)
- writeb(cmd, info->reg.gpmc_nand_address);
-
- else /* NAND_NCE */
- writeb(cmd, info->reg.gpmc_nand_data);
- }
-}
-
-/**
- * omap_read_buf8 - read data from NAND controller into buffer
- * @mtd: MTD device structure
- * @buf: buffer to store date
- * @len: number of bytes to read
- */
-static void omap_read_buf8(struct mtd_info *mtd, u_char *buf, int len)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
-
- ioread8_rep(nand->IO_ADDR_R, buf, len);
-}
-
-/**
- * omap_write_buf8 - write buffer to NAND controller
- * @mtd: MTD device structure
- * @buf: data buffer
- * @len: number of bytes to write
- */
-static void omap_write_buf8(struct mtd_info *mtd, const u_char *buf, int len)
-{
- struct omap_nand_info *info = mtd_to_omap(mtd);
- u_char *p = (u_char *)buf;
- bool status;
-
- while (len--) {
- iowrite8(*p++, info->nand.IO_ADDR_W);
- /* wait until buffer is available for write */
- do {
- status = info->ops->nand_writebuffer_empty();
- } while (!status);
- }
-}
-
-/**
- * omap_read_buf16 - read data from NAND controller into buffer
- * @mtd: MTD device structure
- * @buf: buffer to store date
- * @len: number of bytes to read
- */
-static void omap_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
-
- ioread16_rep(nand->IO_ADDR_R, buf, len / 2);
-}
-
-/**
- * omap_write_buf16 - write buffer to NAND controller
- * @mtd: MTD device structure
- * @buf: data buffer
- * @len: number of bytes to write
- */
-static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len)
-{
- struct omap_nand_info *info = mtd_to_omap(mtd);
- u16 *p = (u16 *) buf;
- bool status;
- /* FIXME try bursts of writesw() or DMA ... */
- len >>= 1;
-
- while (len--) {
- iowrite16(*p++, info->nand.IO_ADDR_W);
- /* wait until buffer is available for write */
- do {
- status = info->ops->nand_writebuffer_empty();
- } while (!status);
- }
-}
-
-/**
- * omap_read_buf_pref - read data from NAND controller into buffer
- * @mtd: MTD device structure
- * @buf: buffer to store date
- * @len: number of bytes to read
- */
-static void omap_read_buf_pref(struct mtd_info *mtd, u_char *buf, int len)
-{
- struct omap_nand_info *info = mtd_to_omap(mtd);
- uint32_t r_count = 0;
- int ret = 0;
- u32 *p = (u32 *)buf;
-
- /* take care of subpage reads */
- if (len % 4) {
- if (info->nand.options & NAND_BUSWIDTH_16)
- omap_read_buf16(mtd, buf, len % 4);
- else
- omap_read_buf8(mtd, buf, len % 4);
- p = (u32 *) (buf + len % 4);
- len -= len % 4;
- }
-
- /* configure and start prefetch transfer */
- ret = omap_prefetch_enable(info->gpmc_cs,
- PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x0, info);
- if (ret) {
- /* PFPW engine is busy, use cpu copy method */
- if (info->nand.options & NAND_BUSWIDTH_16)
- omap_read_buf16(mtd, (u_char *)p, len);
- else
- omap_read_buf8(mtd, (u_char *)p, len);
- } else {
- do {
- r_count = readl(info->reg.gpmc_prefetch_status);
- r_count = PREFETCH_STATUS_FIFO_CNT(r_count);
- r_count = r_count >> 2;
- ioread32_rep(info->nand.IO_ADDR_R, p, r_count);
- p += r_count;
- len -= r_count << 2;
- } while (len);
- /* disable and stop the PFPW engine */
- omap_prefetch_reset(info->gpmc_cs, info);
- }
-}
-
-/**
- * omap_write_buf_pref - write buffer to NAND controller
- * @mtd: MTD device structure
- * @buf: data buffer
- * @len: number of bytes to write
- */
-static void omap_write_buf_pref(struct mtd_info *mtd,
- const u_char *buf, int len)
-{
- struct omap_nand_info *info = mtd_to_omap(mtd);
- uint32_t w_count = 0;
- int i = 0, ret = 0;
- u16 *p = (u16 *)buf;
- unsigned long tim, limit;
- u32 val;
-
- /* take care of subpage writes */
- if (len % 2 != 0) {
- writeb(*buf, info->nand.IO_ADDR_W);
- p = (u16 *)(buf + 1);
- len--;
- }
-
- /* configure and start prefetch transfer */
- ret = omap_prefetch_enable(info->gpmc_cs,
- PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x1, info);
- if (ret) {
- /* PFPW engine is busy, use cpu copy method */
- if (info->nand.options & NAND_BUSWIDTH_16)
- omap_write_buf16(mtd, (u_char *)p, len);
- else
- omap_write_buf8(mtd, (u_char *)p, len);
- } else {
- while (len) {
- w_count = readl(info->reg.gpmc_prefetch_status);
- w_count = PREFETCH_STATUS_FIFO_CNT(w_count);
- w_count = w_count >> 1;
- for (i = 0; (i < w_count) && len; i++, len -= 2)
- iowrite16(*p++, info->nand.IO_ADDR_W);
- }
- /* wait for data to flushed-out before reset the prefetch */
- tim = 0;
- limit = (loops_per_jiffy *
- msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS));
- do {
- cpu_relax();
- val = readl(info->reg.gpmc_prefetch_status);
- val = PREFETCH_STATUS_COUNT(val);
- } while (val && (tim++ < limit));
-
- /* disable and stop the PFPW engine */
- omap_prefetch_reset(info->gpmc_cs, info);
- }
-}
-
-/*
- * omap_nand_dma_callback: callback on the completion of dma transfer
- * @data: pointer to completion data structure
- */
-static void omap_nand_dma_callback(void *data)
-{
- complete((struct completion *) data);
-}
-
-/*
- * omap_nand_dma_transfer: configure and start dma transfer
- * @mtd: MTD device structure
- * @addr: virtual address in RAM of source/destination
- * @len: number of data bytes to be transferred
- * @is_write: flag for read/write operation
- */
-static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
- unsigned int len, int is_write)
-{
- struct omap_nand_info *info = mtd_to_omap(mtd);
- struct dma_async_tx_descriptor *tx;
- enum dma_data_direction dir = is_write ? DMA_TO_DEVICE :
- DMA_FROM_DEVICE;
- struct scatterlist sg;
- unsigned long tim, limit;
- unsigned n;
- int ret;
- u32 val;
-
- if (!virt_addr_valid(addr))
- goto out_copy;
-
- sg_init_one(&sg, addr, len);
- n = dma_map_sg(info->dma->device->dev, &sg, 1, dir);
- if (n == 0) {
- dev_err(&info->pdev->dev,
- "Couldn't DMA map a %d byte buffer\n", len);
- goto out_copy;
- }
-
- tx = dmaengine_prep_slave_sg(info->dma, &sg, n,
- is_write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
- DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
- if (!tx)
- goto out_copy_unmap;
-
- tx->callback = omap_nand_dma_callback;
- tx->callback_param = &info->comp;
- dmaengine_submit(tx);
-
- init_completion(&info->comp);
-
- /* setup and start DMA using dma_addr */
- dma_async_issue_pending(info->dma);
-
- /* configure and start prefetch transfer */
- ret = omap_prefetch_enable(info->gpmc_cs,
- PREFETCH_FIFOTHRESHOLD_MAX, 0x1, len, is_write, info);
- if (ret)
- /* PFPW engine is busy, use cpu copy method */
- goto out_copy_unmap;
-
- wait_for_completion(&info->comp);
- tim = 0;
- limit = (loops_per_jiffy * msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS));
-
- do {
- cpu_relax();
- val = readl(info->reg.gpmc_prefetch_status);
- val = PREFETCH_STATUS_COUNT(val);
- } while (val && (tim++ < limit));
-
- /* disable and stop the PFPW engine */
- omap_prefetch_reset(info->gpmc_cs, info);
-
- dma_unmap_sg(info->dma->device->dev, &sg, 1, dir);
- return 0;
-
-out_copy_unmap:
- dma_unmap_sg(info->dma->device->dev, &sg, 1, dir);
-out_copy:
- if (info->nand.options & NAND_BUSWIDTH_16)
- is_write == 0 ? omap_read_buf16(mtd, (u_char *) addr, len)
- : omap_write_buf16(mtd, (u_char *) addr, len);
- else
- is_write == 0 ? omap_read_buf8(mtd, (u_char *) addr, len)
- : omap_write_buf8(mtd, (u_char *) addr, len);
- return 0;
-}
-
-/**
- * omap_read_buf_dma_pref - read data from NAND controller into buffer
- * @mtd: MTD device structure
- * @buf: buffer to store date
- * @len: number of bytes to read
- */
-static void omap_read_buf_dma_pref(struct mtd_info *mtd, u_char *buf, int len)
-{
- if (len <= mtd->oobsize)
- omap_read_buf_pref(mtd, buf, len);
- else
- /* start transfer in DMA mode */
- omap_nand_dma_transfer(mtd, buf, len, 0x0);
-}
-
-/**
- * omap_write_buf_dma_pref - write buffer to NAND controller
- * @mtd: MTD device structure
- * @buf: data buffer
- * @len: number of bytes to write
- */
-static void omap_write_buf_dma_pref(struct mtd_info *mtd,
- const u_char *buf, int len)
-{
- if (len <= mtd->oobsize)
- omap_write_buf_pref(mtd, buf, len);
- else
- /* start transfer in DMA mode */
- omap_nand_dma_transfer(mtd, (u_char *) buf, len, 0x1);
-}
-
-/*
- * omap_nand_irq - GPMC irq handler
- * @this_irq: gpmc irq number
- * @dev: omap_nand_info structure pointer is passed here
- */
-static irqreturn_t omap_nand_irq(int this_irq, void *dev)
-{
- struct omap_nand_info *info = (struct omap_nand_info *) dev;
- u32 bytes;
-
- bytes = readl(info->reg.gpmc_prefetch_status);
- bytes = PREFETCH_STATUS_FIFO_CNT(bytes);
- bytes = bytes & 0xFFFC; /* io in multiple of 4 bytes */
- if (info->iomode == OMAP_NAND_IO_WRITE) { /* checks for write io */
- if (this_irq == info->gpmc_irq_count)
- goto done;
-
- if (info->buf_len && (info->buf_len < bytes))
- bytes = info->buf_len;
- else if (!info->buf_len)
- bytes = 0;
- iowrite32_rep(info->nand.IO_ADDR_W,
- (u32 *)info->buf, bytes >> 2);
- info->buf = info->buf + bytes;
- info->buf_len -= bytes;
-
- } else {
- ioread32_rep(info->nand.IO_ADDR_R,
- (u32 *)info->buf, bytes >> 2);
- info->buf = info->buf + bytes;
-
- if (this_irq == info->gpmc_irq_count)
- goto done;
- }
-
- return IRQ_HANDLED;
-
-done:
- complete(&info->comp);
-
- disable_irq_nosync(info->gpmc_irq_fifo);
- disable_irq_nosync(info->gpmc_irq_count);
-
- return IRQ_HANDLED;
-}
-
-/*
- * omap_read_buf_irq_pref - read data from NAND controller into buffer
- * @mtd: MTD device structure
- * @buf: buffer to store date
- * @len: number of bytes to read
- */
-static void omap_read_buf_irq_pref(struct mtd_info *mtd, u_char *buf, int len)
-{
- struct omap_nand_info *info = mtd_to_omap(mtd);
- int ret = 0;
-
- if (len <= mtd->oobsize) {
- omap_read_buf_pref(mtd, buf, len);
- return;
- }
-
- info->iomode = OMAP_NAND_IO_READ;
- info->buf = buf;
- init_completion(&info->comp);
-
- /* configure and start prefetch transfer */
- ret = omap_prefetch_enable(info->gpmc_cs,
- PREFETCH_FIFOTHRESHOLD_MAX/2, 0x0, len, 0x0, info);
- if (ret)
- /* PFPW engine is busy, use cpu copy method */
- goto out_copy;
-
- info->buf_len = len;
-
- enable_irq(info->gpmc_irq_count);
- enable_irq(info->gpmc_irq_fifo);
-
- /* waiting for read to complete */
- wait_for_completion(&info->comp);
-
- /* disable and stop the PFPW engine */
- omap_prefetch_reset(info->gpmc_cs, info);
- return;
-
-out_copy:
- if (info->nand.options & NAND_BUSWIDTH_16)
- omap_read_buf16(mtd, buf, len);
- else
- omap_read_buf8(mtd, buf, len);
-}
-
-/*
- * omap_write_buf_irq_pref - write buffer to NAND controller
- * @mtd: MTD device structure
- * @buf: data buffer
- * @len: number of bytes to write
- */
-static void omap_write_buf_irq_pref(struct mtd_info *mtd,
- const u_char *buf, int len)
-{
- struct omap_nand_info *info = mtd_to_omap(mtd);
- int ret = 0;
- unsigned long tim, limit;
- u32 val;
-
- if (len <= mtd->oobsize) {
- omap_write_buf_pref(mtd, buf, len);
- return;
- }
-
- info->iomode = OMAP_NAND_IO_WRITE;
- info->buf = (u_char *) buf;
- init_completion(&info->comp);
-
- /* configure and start prefetch transfer : size=24 */
- ret = omap_prefetch_enable(info->gpmc_cs,
- (PREFETCH_FIFOTHRESHOLD_MAX * 3) / 8, 0x0, len, 0x1, info);
- if (ret)
- /* PFPW engine is busy, use cpu copy method */
- goto out_copy;
-
- info->buf_len = len;
-
- enable_irq(info->gpmc_irq_count);
- enable_irq(info->gpmc_irq_fifo);
-
- /* waiting for write to complete */
- wait_for_completion(&info->comp);
-
- /* wait for data to flushed-out before reset the prefetch */
- tim = 0;
- limit = (loops_per_jiffy * msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS));
- do {
- val = readl(info->reg.gpmc_prefetch_status);
- val = PREFETCH_STATUS_COUNT(val);
- cpu_relax();
- } while (val && (tim++ < limit));
-
- /* disable and stop the PFPW engine */
- omap_prefetch_reset(info->gpmc_cs, info);
- return;
-
-out_copy:
- if (info->nand.options & NAND_BUSWIDTH_16)
- omap_write_buf16(mtd, buf, len);
- else
- omap_write_buf8(mtd, buf, len);
-}
-
-/**
- * gen_true_ecc - This function will generate true ECC value
- * @ecc_buf: buffer to store ecc code
- *
- * This generated true ECC value can be used when correcting
- * data read from NAND flash memory core
- */
-static void gen_true_ecc(u8 *ecc_buf)
-{
- u32 tmp = ecc_buf[0] | (ecc_buf[1] << 16) |
- ((ecc_buf[2] & 0xF0) << 20) | ((ecc_buf[2] & 0x0F) << 8);
-
- ecc_buf[0] = ~(P64o(tmp) | P64e(tmp) | P32o(tmp) | P32e(tmp) |
- P16o(tmp) | P16e(tmp) | P8o(tmp) | P8e(tmp));
- ecc_buf[1] = ~(P1024o(tmp) | P1024e(tmp) | P512o(tmp) | P512e(tmp) |
- P256o(tmp) | P256e(tmp) | P128o(tmp) | P128e(tmp));
- ecc_buf[2] = ~(P4o(tmp) | P4e(tmp) | P2o(tmp) | P2e(tmp) | P1o(tmp) |
- P1e(tmp) | P2048o(tmp) | P2048e(tmp));
-}
-
-/**
- * omap_compare_ecc - Detect (2 bits) and correct (1 bit) error in data
- * @ecc_data1: ecc code from nand spare area
- * @ecc_data2: ecc code from hardware register obtained from hardware ecc
- * @page_data: page data
- *
- * This function compares two ECC's and indicates if there is an error.
- * If the error can be corrected it will be corrected to the buffer.
- * If there is no error, %0 is returned. If there is an error but it
- * was corrected, %1 is returned. Otherwise, %-1 is returned.
- */
-static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */
- u8 *ecc_data2, /* read from register */
- u8 *page_data)
-{
- uint i;
- u8 tmp0_bit[8], tmp1_bit[8], tmp2_bit[8];
- u8 comp0_bit[8], comp1_bit[8], comp2_bit[8];
- u8 ecc_bit[24];
- u8 ecc_sum = 0;
- u8 find_bit = 0;
- uint find_byte = 0;
- int isEccFF;
-
- isEccFF = ((*(u32 *)ecc_data1 & 0xFFFFFF) == 0xFFFFFF);
-
- gen_true_ecc(ecc_data1);
- gen_true_ecc(ecc_data2);
-
- for (i = 0; i <= 2; i++) {
- *(ecc_data1 + i) = ~(*(ecc_data1 + i));
- *(ecc_data2 + i) = ~(*(ecc_data2 + i));
- }
-
- for (i = 0; i < 8; i++) {
- tmp0_bit[i] = *ecc_data1 % 2;
- *ecc_data1 = *ecc_data1 / 2;
- }
-
- for (i = 0; i < 8; i++) {
- tmp1_bit[i] = *(ecc_data1 + 1) % 2;
- *(ecc_data1 + 1) = *(ecc_data1 + 1) / 2;
- }
-
- for (i = 0; i < 8; i++) {
- tmp2_bit[i] = *(ecc_data1 + 2) % 2;
- *(ecc_data1 + 2) = *(ecc_data1 + 2) / 2;
- }
-
- for (i = 0; i < 8; i++) {
- comp0_bit[i] = *ecc_data2 % 2;
- *ecc_data2 = *ecc_data2 / 2;
- }
-
- for (i = 0; i < 8; i++) {
- comp1_bit[i] = *(ecc_data2 + 1) % 2;
- *(ecc_data2 + 1) = *(ecc_data2 + 1) / 2;
- }
-
- for (i = 0; i < 8; i++) {
- comp2_bit[i] = *(ecc_data2 + 2) % 2;
- *(ecc_data2 + 2) = *(ecc_data2 + 2) / 2;
- }
-
- for (i = 0; i < 6; i++)
- ecc_bit[i] = tmp2_bit[i + 2] ^ comp2_bit[i + 2];
-
- for (i = 0; i < 8; i++)
- ecc_bit[i + 6] = tmp0_bit[i] ^ comp0_bit[i];
-
- for (i = 0; i < 8; i++)
- ecc_bit[i + 14] = tmp1_bit[i] ^ comp1_bit[i];
-
- ecc_bit[22] = tmp2_bit[0] ^ comp2_bit[0];
- ecc_bit[23] = tmp2_bit[1] ^ comp2_bit[1];
-
- for (i = 0; i < 24; i++)
- ecc_sum += ecc_bit[i];
-
- switch (ecc_sum) {
- case 0:
- /* Not reached because this function is not called if
- * ECC values are equal
- */
- return 0;
-
- case 1:
- /* Uncorrectable error */
- pr_debug("ECC UNCORRECTED_ERROR 1\n");
- return -EBADMSG;
-
- case 11:
- /* UN-Correctable error */
- pr_debug("ECC UNCORRECTED_ERROR B\n");
- return -EBADMSG;
-
- case 12:
- /* Correctable error */
- find_byte = (ecc_bit[23] << 8) +
- (ecc_bit[21] << 7) +
- (ecc_bit[19] << 6) +
- (ecc_bit[17] << 5) +
- (ecc_bit[15] << 4) +
- (ecc_bit[13] << 3) +
- (ecc_bit[11] << 2) +
- (ecc_bit[9] << 1) +
- ecc_bit[7];
-
- find_bit = (ecc_bit[5] << 2) + (ecc_bit[3] << 1) + ecc_bit[1];
-
- pr_debug("Correcting single bit ECC error at offset: "
- "%d, bit: %d\n", find_byte, find_bit);
-
- page_data[find_byte] ^= (1 << find_bit);
-
- return 1;
- default:
- if (isEccFF) {
- if (ecc_data2[0] == 0 &&
- ecc_data2[1] == 0 &&
- ecc_data2[2] == 0)
- return 0;
- }
- pr_debug("UNCORRECTED_ERROR default\n");
- return -EBADMSG;
- }
-}
-
-/**
- * omap_correct_data - Compares the ECC read with HW generated ECC
- * @mtd: MTD device structure
- * @dat: page data
- * @read_ecc: ecc read from nand flash
- * @calc_ecc: ecc read from HW ECC registers
- *
- * Compares the ecc read from nand spare area with ECC registers values
- * and if ECC's mismatched, it will call 'omap_compare_ecc' for error
- * detection and correction. If there are no errors, %0 is returned. If
- * there were errors and all of the errors were corrected, the number of
- * corrected errors is returned. If uncorrectable errors exist, %-1 is
- * returned.
- */
-static int omap_correct_data(struct mtd_info *mtd, u_char *dat,
- u_char *read_ecc, u_char *calc_ecc)
-{
- struct omap_nand_info *info = mtd_to_omap(mtd);
- int blockCnt = 0, i = 0, ret = 0;
- int stat = 0;
-
- /* Ex NAND_ECC_HW12_2048 */
- if ((info->nand.ecc.mode == NAND_ECC_HW) &&
- (info->nand.ecc.size == 2048))
- blockCnt = 4;
- else
- blockCnt = 1;
-
- for (i = 0; i < blockCnt; i++) {
- if (memcmp(read_ecc, calc_ecc, 3) != 0) {
- ret = omap_compare_ecc(read_ecc, calc_ecc, dat);
- if (ret < 0)
- return ret;
- /* keep track of the number of corrected errors */
- stat += ret;
- }
- read_ecc += 3;
- calc_ecc += 3;
- dat += 512;
- }
- return stat;
-}
-
-/**
- * omap_calcuate_ecc - Generate non-inverted ECC bytes.
- * @mtd: MTD device structure
- * @dat: The pointer to data on which ecc is computed
- * @ecc_code: The ecc_code buffer
- *
- * Using noninverted ECC can be considered ugly since writing a blank
- * page ie. padding will clear the ECC bytes. This is no problem as long
- * nobody is trying to write data on the seemingly unused page. Reading
- * an erased page will produce an ECC mismatch between generated and read
- * ECC bytes that has to be dealt with separately.
- */
-static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
- u_char *ecc_code)
-{
- struct omap_nand_info *info = mtd_to_omap(mtd);
- u32 val;
-
- val = readl(info->reg.gpmc_ecc_config);
- if (((val >> ECC_CONFIG_CS_SHIFT) & CS_MASK) != info->gpmc_cs)
- return -EINVAL;
-
- /* read ecc result */
- val = readl(info->reg.gpmc_ecc1_result);
- *ecc_code++ = val; /* P128e, ..., P1e */
- *ecc_code++ = val >> 16; /* P128o, ..., P1o */
- /* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */
- *ecc_code++ = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0);
-
- return 0;
-}
-
-/**
- * omap_enable_hwecc - This function enables the hardware ecc functionality
- * @mtd: MTD device structure
- * @mode: Read/Write mode
- */
-static void omap_enable_hwecc(struct mtd_info *mtd, int mode)
-{
- struct omap_nand_info *info = mtd_to_omap(mtd);
- struct nand_chip *chip = mtd_to_nand(mtd);
- unsigned int dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0;
- u32 val;
-
- /* clear ecc and enable bits */
- val = ECCCLEAR | ECC1;
- writel(val, info->reg.gpmc_ecc_control);
-
- /* program ecc and result sizes */
- val = ((((info->nand.ecc.size >> 1) - 1) << ECCSIZE1_SHIFT) |
- ECC1RESULTSIZE);
- writel(val, info->reg.gpmc_ecc_size_config);
-
- switch (mode) {
- case NAND_ECC_READ:
- case NAND_ECC_WRITE:
- writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control);
- break;
- case NAND_ECC_READSYN:
- writel(ECCCLEAR, info->reg.gpmc_ecc_control);
- break;
- default:
- dev_info(&info->pdev->dev,
- "error: unrecognized Mode[%d]!\n", mode);
- break;
- }
-
- /* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */
- val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1);
- writel(val, info->reg.gpmc_ecc_config);
-}
-
-/**
- * omap_wait - wait until the command is done
- * @mtd: MTD device structure
- * @chip: NAND Chip structure
- *
- * Wait function is called during Program and erase operations and
- * the way it is called from MTD layer, we should wait till the NAND
- * chip is ready after the programming/erase operation has completed.
- *
- * Erase can take up to 400ms and program up to 20ms according to
- * general NAND and SmartMedia specs
- */
-static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct omap_nand_info *info = mtd_to_omap(mtd);
- unsigned long timeo = jiffies;
- int status, state = this->state;
-
- if (state == FL_ERASING)
- timeo += msecs_to_jiffies(400);
- else
- timeo += msecs_to_jiffies(20);
-
- writeb(NAND_CMD_STATUS & 0xFF, info->reg.gpmc_nand_command);
- while (time_before(jiffies, timeo)) {
- status = readb(info->reg.gpmc_nand_data);
- if (status & NAND_STATUS_READY)
- break;
- cond_resched();
- }
-
- status = readb(info->reg.gpmc_nand_data);
- return status;
-}
-
-/**
- * omap_dev_ready - checks the NAND Ready GPIO line
- * @mtd: MTD device structure
- *
- * Returns true if ready and false if busy.
- */
-static int omap_dev_ready(struct mtd_info *mtd)
-{
- struct omap_nand_info *info = mtd_to_omap(mtd);
-
- return gpiod_get_value(info->ready_gpiod);
-}
-
-/**
- * omap_enable_hwecc_bch - Program GPMC to perform BCH ECC calculation
- * @mtd: MTD device structure
- * @mode: Read/Write mode
- *
- * When using BCH with SW correction (i.e. no ELM), sector size is set
- * to 512 bytes and we use BCH_WRAPMODE_6 wrapping mode
- * for both reading and writing with:
- * eccsize0 = 0 (no additional protected byte in spare area)
- * eccsize1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area)
- */
-static void __maybe_unused omap_enable_hwecc_bch(struct mtd_info *mtd, int mode)
-{
- unsigned int bch_type;
- unsigned int dev_width, nsectors;
- struct omap_nand_info *info = mtd_to_omap(mtd);
- enum omap_ecc ecc_opt = info->ecc_opt;
- struct nand_chip *chip = mtd_to_nand(mtd);
- u32 val, wr_mode;
- unsigned int ecc_size1, ecc_size0;
-
- /* GPMC configurations for calculating ECC */
- switch (ecc_opt) {
- case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
- bch_type = 0;
- nsectors = 1;
- wr_mode = BCH_WRAPMODE_6;
- ecc_size0 = BCH_ECC_SIZE0;
- ecc_size1 = BCH_ECC_SIZE1;
- break;
- case OMAP_ECC_BCH4_CODE_HW:
- bch_type = 0;
- nsectors = chip->ecc.steps;
- if (mode == NAND_ECC_READ) {
- wr_mode = BCH_WRAPMODE_1;
- ecc_size0 = BCH4R_ECC_SIZE0;
- ecc_size1 = BCH4R_ECC_SIZE1;
- } else {
- wr_mode = BCH_WRAPMODE_6;
- ecc_size0 = BCH_ECC_SIZE0;
- ecc_size1 = BCH_ECC_SIZE1;
- }
- break;
- case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
- bch_type = 1;
- nsectors = 1;
- wr_mode = BCH_WRAPMODE_6;
- ecc_size0 = BCH_ECC_SIZE0;
- ecc_size1 = BCH_ECC_SIZE1;
- break;
- case OMAP_ECC_BCH8_CODE_HW:
- bch_type = 1;
- nsectors = chip->ecc.steps;
- if (mode == NAND_ECC_READ) {
- wr_mode = BCH_WRAPMODE_1;
- ecc_size0 = BCH8R_ECC_SIZE0;
- ecc_size1 = BCH8R_ECC_SIZE1;
- } else {
- wr_mode = BCH_WRAPMODE_6;
- ecc_size0 = BCH_ECC_SIZE0;
- ecc_size1 = BCH_ECC_SIZE1;
- }
- break;
- case OMAP_ECC_BCH16_CODE_HW:
- bch_type = 0x2;
- nsectors = chip->ecc.steps;
- if (mode == NAND_ECC_READ) {
- wr_mode = 0x01;
- ecc_size0 = 52; /* ECC bits in nibbles per sector */
- ecc_size1 = 0; /* non-ECC bits in nibbles per sector */
- } else {
- wr_mode = 0x01;
- ecc_size0 = 0; /* extra bits in nibbles per sector */
- ecc_size1 = 52; /* OOB bits in nibbles per sector */
- }
- break;
- default:
- return;
- }
-
- writel(ECC1, info->reg.gpmc_ecc_control);
-
- /* Configure ecc size for BCH */
- val = (ecc_size1 << ECCSIZE1_SHIFT) | (ecc_size0 << ECCSIZE0_SHIFT);
- writel(val, info->reg.gpmc_ecc_size_config);
-
- dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0;
-
- /* BCH configuration */
- val = ((1 << 16) | /* enable BCH */
- (bch_type << 12) | /* BCH4/BCH8/BCH16 */
- (wr_mode << 8) | /* wrap mode */
- (dev_width << 7) | /* bus width */
- (((nsectors-1) & 0x7) << 4) | /* number of sectors */
- (info->gpmc_cs << 1) | /* ECC CS */
- (0x1)); /* enable ECC */
-
- writel(val, info->reg.gpmc_ecc_config);
-
- /* Clear ecc and enable bits */
- writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control);
-}
-
-static u8 bch4_polynomial[] = {0x28, 0x13, 0xcc, 0x39, 0x96, 0xac, 0x7f};
-static u8 bch8_polynomial[] = {0xef, 0x51, 0x2e, 0x09, 0xed, 0x93, 0x9a, 0xc2,
- 0x97, 0x79, 0xe5, 0x24, 0xb5};
-
-/**
- * _omap_calculate_ecc_bch - Generate ECC bytes for one sector
- * @mtd: MTD device structure
- * @dat: The pointer to data on which ecc is computed
- * @ecc_code: The ecc_code buffer
- * @i: The sector number (for a multi sector page)
- *
- * Support calculating of BCH4/8/16 ECC vectors for one sector
- * within a page. Sector number is in @i.
- */
-static int _omap_calculate_ecc_bch(struct mtd_info *mtd,
- const u_char *dat, u_char *ecc_calc, int i)
-{
- struct omap_nand_info *info = mtd_to_omap(mtd);
- int eccbytes = info->nand.ecc.bytes;
- struct gpmc_nand_regs *gpmc_regs = &info->reg;
- u8 *ecc_code;
- unsigned long bch_val1, bch_val2, bch_val3, bch_val4;
- u32 val;
- int j;
-
- ecc_code = ecc_calc;
- switch (info->ecc_opt) {
- case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
- case OMAP_ECC_BCH8_CODE_HW:
- bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]);
- bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]);
- bch_val3 = readl(gpmc_regs->gpmc_bch_result2[i]);
- bch_val4 = readl(gpmc_regs->gpmc_bch_result3[i]);
- *ecc_code++ = (bch_val4 & 0xFF);
- *ecc_code++ = ((bch_val3 >> 24) & 0xFF);
- *ecc_code++ = ((bch_val3 >> 16) & 0xFF);
- *ecc_code++ = ((bch_val3 >> 8) & 0xFF);
- *ecc_code++ = (bch_val3 & 0xFF);
- *ecc_code++ = ((bch_val2 >> 24) & 0xFF);
- *ecc_code++ = ((bch_val2 >> 16) & 0xFF);
- *ecc_code++ = ((bch_val2 >> 8) & 0xFF);
- *ecc_code++ = (bch_val2 & 0xFF);
- *ecc_code++ = ((bch_val1 >> 24) & 0xFF);
- *ecc_code++ = ((bch_val1 >> 16) & 0xFF);
- *ecc_code++ = ((bch_val1 >> 8) & 0xFF);
- *ecc_code++ = (bch_val1 & 0xFF);
- break;
- case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
- case OMAP_ECC_BCH4_CODE_HW:
- bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]);
- bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]);
- *ecc_code++ = ((bch_val2 >> 12) & 0xFF);
- *ecc_code++ = ((bch_val2 >> 4) & 0xFF);
- *ecc_code++ = ((bch_val2 & 0xF) << 4) |
- ((bch_val1 >> 28) & 0xF);
- *ecc_code++ = ((bch_val1 >> 20) & 0xFF);
- *ecc_code++ = ((bch_val1 >> 12) & 0xFF);
- *ecc_code++ = ((bch_val1 >> 4) & 0xFF);
- *ecc_code++ = ((bch_val1 & 0xF) << 4);
- break;
- case OMAP_ECC_BCH16_CODE_HW:
- val = readl(gpmc_regs->gpmc_bch_result6[i]);
- ecc_code[0] = ((val >> 8) & 0xFF);
- ecc_code[1] = ((val >> 0) & 0xFF);
- val = readl(gpmc_regs->gpmc_bch_result5[i]);
- ecc_code[2] = ((val >> 24) & 0xFF);
- ecc_code[3] = ((val >> 16) & 0xFF);
- ecc_code[4] = ((val >> 8) & 0xFF);
- ecc_code[5] = ((val >> 0) & 0xFF);
- val = readl(gpmc_regs->gpmc_bch_result4[i]);
- ecc_code[6] = ((val >> 24) & 0xFF);
- ecc_code[7] = ((val >> 16) & 0xFF);
- ecc_code[8] = ((val >> 8) & 0xFF);
- ecc_code[9] = ((val >> 0) & 0xFF);
- val = readl(gpmc_regs->gpmc_bch_result3[i]);
- ecc_code[10] = ((val >> 24) & 0xFF);
- ecc_code[11] = ((val >> 16) & 0xFF);
- ecc_code[12] = ((val >> 8) & 0xFF);
- ecc_code[13] = ((val >> 0) & 0xFF);
- val = readl(gpmc_regs->gpmc_bch_result2[i]);
- ecc_code[14] = ((val >> 24) & 0xFF);
- ecc_code[15] = ((val >> 16) & 0xFF);
- ecc_code[16] = ((val >> 8) & 0xFF);
- ecc_code[17] = ((val >> 0) & 0xFF);
- val = readl(gpmc_regs->gpmc_bch_result1[i]);
- ecc_code[18] = ((val >> 24) & 0xFF);
- ecc_code[19] = ((val >> 16) & 0xFF);
- ecc_code[20] = ((val >> 8) & 0xFF);
- ecc_code[21] = ((val >> 0) & 0xFF);
- val = readl(gpmc_regs->gpmc_bch_result0[i]);
- ecc_code[22] = ((val >> 24) & 0xFF);
- ecc_code[23] = ((val >> 16) & 0xFF);
- ecc_code[24] = ((val >> 8) & 0xFF);
- ecc_code[25] = ((val >> 0) & 0xFF);
- break;
- default:
- return -EINVAL;
- }
-
- /* ECC scheme specific syndrome customizations */
- switch (info->ecc_opt) {
- case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
- /* Add constant polynomial to remainder, so that
- * ECC of blank pages results in 0x0 on reading back
- */
- for (j = 0; j < eccbytes; j++)
- ecc_calc[j] ^= bch4_polynomial[j];
- break;
- case OMAP_ECC_BCH4_CODE_HW:
- /* Set 8th ECC byte as 0x0 for ROM compatibility */
- ecc_calc[eccbytes - 1] = 0x0;
- break;
- case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
- /* Add constant polynomial to remainder, so that
- * ECC of blank pages results in 0x0 on reading back
- */
- for (j = 0; j < eccbytes; j++)
- ecc_calc[j] ^= bch8_polynomial[j];
- break;
- case OMAP_ECC_BCH8_CODE_HW:
- /* Set 14th ECC byte as 0x0 for ROM compatibility */
- ecc_calc[eccbytes - 1] = 0x0;
- break;
- case OMAP_ECC_BCH16_CODE_HW:
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-/**
- * omap_calculate_ecc_bch_sw - ECC generator for sector for SW based correction
- * @mtd: MTD device structure
- * @dat: The pointer to data on which ecc is computed
- * @ecc_code: The ecc_code buffer
- *
- * Support calculating of BCH4/8/16 ECC vectors for one sector. This is used
- * when SW based correction is required as ECC is required for one sector
- * at a time.
- */
-static int omap_calculate_ecc_bch_sw(struct mtd_info *mtd,
- const u_char *dat, u_char *ecc_calc)
-{
- return _omap_calculate_ecc_bch(mtd, dat, ecc_calc, 0);
-}
-
-/**
- * omap_calculate_ecc_bch_multi - Generate ECC for multiple sectors
- * @mtd: MTD device structure
- * @dat: The pointer to data on which ecc is computed
- * @ecc_code: The ecc_code buffer
- *
- * Support calculating of BCH4/8/16 ecc vectors for the entire page in one go.
- */
-static int omap_calculate_ecc_bch_multi(struct mtd_info *mtd,
- const u_char *dat, u_char *ecc_calc)
-{
- struct omap_nand_info *info = mtd_to_omap(mtd);
- int eccbytes = info->nand.ecc.bytes;
- unsigned long nsectors;
- int i, ret;
-
- nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1;
- for (i = 0; i < nsectors; i++) {
- ret = _omap_calculate_ecc_bch(mtd, dat, ecc_calc, i);
- if (ret)
- return ret;
-
- ecc_calc += eccbytes;
- }
-
- return 0;
-}
-
-/**
- * erased_sector_bitflips - count bit flips
- * @data: data sector buffer
- * @oob: oob buffer
- * @info: omap_nand_info
- *
- * Check the bit flips in erased page falls below correctable level.
- * If falls below, report the page as erased with correctable bit
- * flip, else report as uncorrectable page.
- */
-static int erased_sector_bitflips(u_char *data, u_char *oob,
- struct omap_nand_info *info)
-{
- int flip_bits = 0, i;
-
- for (i = 0; i < info->nand.ecc.size; i++) {
- flip_bits += hweight8(~data[i]);
- if (flip_bits > info->nand.ecc.strength)
- return 0;
- }
-
- for (i = 0; i < info->nand.ecc.bytes - 1; i++) {
- flip_bits += hweight8(~oob[i]);
- if (flip_bits > info->nand.ecc.strength)
- return 0;
- }
-
- /*
- * Bit flips falls in correctable level.
- * Fill data area with 0xFF
- */
- if (flip_bits) {
- memset(data, 0xFF, info->nand.ecc.size);
- memset(oob, 0xFF, info->nand.ecc.bytes);
- }
-
- return flip_bits;
-}
-
-/**
- * omap_elm_correct_data - corrects page data area in case error reported
- * @mtd: MTD device structure
- * @data: page data
- * @read_ecc: ecc read from nand flash
- * @calc_ecc: ecc read from HW ECC registers
- *
- * Calculated ecc vector reported as zero in case of non-error pages.
- * In case of non-zero ecc vector, first filter out erased-pages, and
- * then process data via ELM to detect bit-flips.
- */
-static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
- u_char *read_ecc, u_char *calc_ecc)
-{
- struct omap_nand_info *info = mtd_to_omap(mtd);
- struct nand_ecc_ctrl *ecc = &info->nand.ecc;
- int eccsteps = info->nand.ecc.steps;
- int i , j, stat = 0;
- int eccflag, actual_eccbytes;
- struct elm_errorvec err_vec[ERROR_VECTOR_MAX];
- u_char *ecc_vec = calc_ecc;
- u_char *spare_ecc = read_ecc;
- u_char *erased_ecc_vec;
- u_char *buf;
- int bitflip_count;
- bool is_error_reported = false;
- u32 bit_pos, byte_pos, error_max, pos;
- int err;
-
- switch (info->ecc_opt) {
- case OMAP_ECC_BCH4_CODE_HW:
- /* omit 7th ECC byte reserved for ROM code compatibility */
- actual_eccbytes = ecc->bytes - 1;
- erased_ecc_vec = bch4_vector;
- break;
- case OMAP_ECC_BCH8_CODE_HW:
- /* omit 14th ECC byte reserved for ROM code compatibility */
- actual_eccbytes = ecc->bytes - 1;
- erased_ecc_vec = bch8_vector;
- break;
- case OMAP_ECC_BCH16_CODE_HW:
- actual_eccbytes = ecc->bytes;
- erased_ecc_vec = bch16_vector;
- break;
- default:
- dev_err(&info->pdev->dev, "invalid driver configuration\n");
- return -EINVAL;
- }
-
- /* Initialize elm error vector to zero */
- memset(err_vec, 0, sizeof(err_vec));
-
- for (i = 0; i < eccsteps ; i++) {
- eccflag = 0; /* initialize eccflag */
-
- /*
- * Check any error reported,
- * In case of error, non zero ecc reported.
- */
- for (j = 0; j < actual_eccbytes; j++) {
- if (calc_ecc[j] != 0) {
- eccflag = 1; /* non zero ecc, error present */
- break;
- }
- }
-
- if (eccflag == 1) {
- if (memcmp(calc_ecc, erased_ecc_vec,
- actual_eccbytes) == 0) {
- /*
- * calc_ecc[] matches pattern for ECC(all 0xff)
- * so this is definitely an erased-page
- */
- } else {
- buf = &data[info->nand.ecc.size * i];
- /*
- * count number of 0-bits in read_buf.
- * This check can be removed once a similar
- * check is introduced in generic NAND driver
- */
- bitflip_count = erased_sector_bitflips(
- buf, read_ecc, info);
- if (bitflip_count) {
- /*
- * number of 0-bits within ECC limits
- * So this may be an erased-page
- */
- stat += bitflip_count;
- } else {
- /*
- * Too many 0-bits. It may be a
- * - programmed-page, OR
- * - erased-page with many bit-flips
- * So this page requires check by ELM
- */
- err_vec[i].error_reported = true;
- is_error_reported = true;
- }
- }
- }
-
- /* Update the ecc vector */
- calc_ecc += ecc->bytes;
- read_ecc += ecc->bytes;
- }
-
- /* Check if any error reported */
- if (!is_error_reported)
- return stat;
-
- /* Decode BCH error using ELM module */
- elm_decode_bch_error_page(info->elm_dev, ecc_vec, err_vec);
-
- err = 0;
- for (i = 0; i < eccsteps; i++) {
- if (err_vec[i].error_uncorrectable) {
- dev_err(&info->pdev->dev,
- "uncorrectable bit-flips found\n");
- err = -EBADMSG;
- } else if (err_vec[i].error_reported) {
- for (j = 0; j < err_vec[i].error_count; j++) {
- switch (info->ecc_opt) {
- case OMAP_ECC_BCH4_CODE_HW:
- /* Add 4 bits to take care of padding */
- pos = err_vec[i].error_loc[j] +
- BCH4_BIT_PAD;
- break;
- case OMAP_ECC_BCH8_CODE_HW:
- case OMAP_ECC_BCH16_CODE_HW:
- pos = err_vec[i].error_loc[j];
- break;
- default:
- return -EINVAL;
- }
- error_max = (ecc->size + actual_eccbytes) * 8;
- /* Calculate bit position of error */
- bit_pos = pos % 8;
-
- /* Calculate byte position of error */
- byte_pos = (error_max - pos - 1) / 8;
-
- if (pos < error_max) {
- if (byte_pos < 512) {
- pr_debug("bitflip@dat[%d]=%x\n",
- byte_pos, data[byte_pos]);
- data[byte_pos] ^= 1 << bit_pos;
- } else {
- pr_debug("bitflip@oob[%d]=%x\n",
- (byte_pos - 512),
- spare_ecc[byte_pos - 512]);
- spare_ecc[byte_pos - 512] ^=
- 1 << bit_pos;
- }
- } else {
- dev_err(&info->pdev->dev,
- "invalid bit-flip @ %d:%d\n",
- byte_pos, bit_pos);
- err = -EBADMSG;
- }
- }
- }
-
- /* Update number of correctable errors */
- stat += err_vec[i].error_count;
-
- /* Update page data with sector size */
- data += ecc->size;
- spare_ecc += ecc->bytes;
- }
-
- return (err) ? err : stat;
-}
-
-/**
- * omap_write_page_bch - BCH ecc based write page function for entire page
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: data buffer
- * @oob_required: must write chip->oob_poi to OOB
- * @page: page
- *
- * Custom write page method evolved to support multi sector writing in one shot
- */
-static int omap_write_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf, int oob_required, int page)
-{
- int ret;
- uint8_t *ecc_calc = chip->ecc.calc_buf;
-
- nand_prog_page_begin_op(chip, page, 0, NULL, 0);
-
- /* Enable GPMC ecc engine */
- chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
-
- /* Write data */
- chip->write_buf(mtd, buf, mtd->writesize);
-
- /* Update ecc vector from GPMC result registers */
- omap_calculate_ecc_bch_multi(mtd, buf, &ecc_calc[0]);
-
- ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
- chip->ecc.total);
- if (ret)
- return ret;
-
- /* Write ecc vector to OOB area */
- chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
-
- return nand_prog_page_end_op(chip);
-}
-
-/**
- * omap_write_subpage_bch - BCH hardware ECC based subpage write
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @offset: column address of subpage within the page
- * @data_len: data length
- * @buf: data buffer
- * @oob_required: must write chip->oob_poi to OOB
- * @page: page number to write
- *
- * OMAP optimized subpage write method.
- */
-static int omap_write_subpage_bch(struct mtd_info *mtd,
- struct nand_chip *chip, u32 offset,
- u32 data_len, const u8 *buf,
- int oob_required, int page)
-{
- u8 *ecc_calc = chip->ecc.calc_buf;
- int ecc_size = chip->ecc.size;
- int ecc_bytes = chip->ecc.bytes;
- int ecc_steps = chip->ecc.steps;
- u32 start_step = offset / ecc_size;
- u32 end_step = (offset + data_len - 1) / ecc_size;
- int step, ret = 0;
-
- /*
- * Write entire page at one go as it would be optimal
- * as ECC is calculated by hardware.
- * ECC is calculated for all subpages but we choose
- * only what we want.
- */
- nand_prog_page_begin_op(chip, page, 0, NULL, 0);
-
- /* Enable GPMC ECC engine */
- chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
-
- /* Write data */
- chip->write_buf(mtd, buf, mtd->writesize);
-
- for (step = 0; step < ecc_steps; step++) {
- /* mask ECC of un-touched subpages by padding 0xFF */
- if (step < start_step || step > end_step)
- memset(ecc_calc, 0xff, ecc_bytes);
- else
- ret = _omap_calculate_ecc_bch(mtd, buf, ecc_calc, step);
-
- if (ret)
- return ret;
-
- buf += ecc_size;
- ecc_calc += ecc_bytes;
- }
-
- /* copy calculated ECC for whole page to chip->buffer->oob */
- /* this include masked-value(0xFF) for unwritten subpages */
- ecc_calc = chip->ecc.calc_buf;
- ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
- chip->ecc.total);
- if (ret)
- return ret;
-
- /* write OOB buffer to NAND device */
- chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
-
- return nand_prog_page_end_op(chip);
-}
-
-/**
- * omap_read_page_bch - BCH ecc based page read function for entire page
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- * @oob_required: caller requires OOB data read to chip->oob_poi
- * @page: page number to read
- *
- * For BCH ecc scheme, GPMC used for syndrome calculation and ELM module
- * used for error correction.
- * Custom method evolved to support ELM error correction & multi sector
- * reading. On reading page data area is read along with OOB data with
- * ecc engine enabled. ecc vector updated after read of OOB data.
- * For non error pages ecc vector reported as zero.
- */
-static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page)
-{
- uint8_t *ecc_calc = chip->ecc.calc_buf;
- uint8_t *ecc_code = chip->ecc.code_buf;
- int stat, ret;
- unsigned int max_bitflips = 0;
-
- nand_read_page_op(chip, page, 0, NULL, 0);
-
- /* Enable GPMC ecc engine */
- chip->ecc.hwctl(mtd, NAND_ECC_READ);
-
- /* Read data */
- chip->read_buf(mtd, buf, mtd->writesize);
-
- /* Read oob bytes */
- nand_change_read_column_op(chip,
- mtd->writesize + BADBLOCK_MARKER_LENGTH,
- chip->oob_poi + BADBLOCK_MARKER_LENGTH,
- chip->ecc.total, false);
-
- /* Calculate ecc bytes */
- omap_calculate_ecc_bch_multi(mtd, buf, ecc_calc);
-
- ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
- chip->ecc.total);
- if (ret)
- return ret;
-
- stat = chip->ecc.correct(mtd, buf, ecc_code, ecc_calc);
-
- if (stat < 0) {
- mtd->ecc_stats.failed++;
- } else {
- mtd->ecc_stats.corrected += stat;
- max_bitflips = max_t(unsigned int, max_bitflips, stat);
- }
-
- return max_bitflips;
-}
-
-/**
- * is_elm_present - checks for presence of ELM module by scanning DT nodes
- * @omap_nand_info: NAND device structure containing platform data
- */
-static bool is_elm_present(struct omap_nand_info *info,
- struct device_node *elm_node)
-{
- struct platform_device *pdev;
-
- /* check whether elm-id is passed via DT */
- if (!elm_node) {
- dev_err(&info->pdev->dev, "ELM devicetree node not found\n");
- return false;
- }
- pdev = of_find_device_by_node(elm_node);
- /* check whether ELM device is registered */
- if (!pdev) {
- dev_err(&info->pdev->dev, "ELM device not found\n");
- return false;
- }
- /* ELM module available, now configure it */
- info->elm_dev = &pdev->dev;
- return true;
-}
-
-static bool omap2_nand_ecc_check(struct omap_nand_info *info)
-{
- bool ecc_needs_bch, ecc_needs_omap_bch, ecc_needs_elm;
-
- switch (info->ecc_opt) {
- case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
- case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
- ecc_needs_omap_bch = false;
- ecc_needs_bch = true;
- ecc_needs_elm = false;
- break;
- case OMAP_ECC_BCH4_CODE_HW:
- case OMAP_ECC_BCH8_CODE_HW:
- case OMAP_ECC_BCH16_CODE_HW:
- ecc_needs_omap_bch = true;
- ecc_needs_bch = false;
- ecc_needs_elm = true;
- break;
- default:
- ecc_needs_omap_bch = false;
- ecc_needs_bch = false;
- ecc_needs_elm = false;
- break;
- }
-
- if (ecc_needs_bch && !IS_ENABLED(CONFIG_MTD_NAND_ECC_BCH)) {
- dev_err(&info->pdev->dev,
- "CONFIG_MTD_NAND_ECC_BCH not enabled\n");
- return false;
- }
- if (ecc_needs_omap_bch && !IS_ENABLED(CONFIG_MTD_NAND_OMAP_BCH)) {
- dev_err(&info->pdev->dev,
- "CONFIG_MTD_NAND_OMAP_BCH not enabled\n");
- return false;
- }
- if (ecc_needs_elm && !is_elm_present(info, info->elm_of_node)) {
- dev_err(&info->pdev->dev, "ELM not available\n");
- return false;
- }
-
- return true;
-}
-
-static const char * const nand_xfer_types[] = {
- [NAND_OMAP_PREFETCH_POLLED] = "prefetch-polled",
- [NAND_OMAP_POLLED] = "polled",
- [NAND_OMAP_PREFETCH_DMA] = "prefetch-dma",
- [NAND_OMAP_PREFETCH_IRQ] = "prefetch-irq",
-};
-
-static int omap_get_dt_info(struct device *dev, struct omap_nand_info *info)
-{
- struct device_node *child = dev->of_node;
- int i;
- const char *s;
- u32 cs;
-
- if (of_property_read_u32(child, "reg", &cs) < 0) {
- dev_err(dev, "reg not found in DT\n");
- return -EINVAL;
- }
-
- info->gpmc_cs = cs;
-
- /* detect availability of ELM module. Won't be present pre-OMAP4 */
- info->elm_of_node = of_parse_phandle(child, "ti,elm-id", 0);
- if (!info->elm_of_node) {
- info->elm_of_node = of_parse_phandle(child, "elm_id", 0);
- if (!info->elm_of_node)
- dev_dbg(dev, "ti,elm-id not in DT\n");
- }
-
- /* select ecc-scheme for NAND */
- if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) {
- dev_err(dev, "ti,nand-ecc-opt not found\n");
- return -EINVAL;
- }
-
- if (!strcmp(s, "sw")) {
- info->ecc_opt = OMAP_ECC_HAM1_CODE_SW;
- } else if (!strcmp(s, "ham1") ||
- !strcmp(s, "hw") || !strcmp(s, "hw-romcode")) {
- info->ecc_opt = OMAP_ECC_HAM1_CODE_HW;
- } else if (!strcmp(s, "bch4")) {
- if (info->elm_of_node)
- info->ecc_opt = OMAP_ECC_BCH4_CODE_HW;
- else
- info->ecc_opt = OMAP_ECC_BCH4_CODE_HW_DETECTION_SW;
- } else if (!strcmp(s, "bch8")) {
- if (info->elm_of_node)
- info->ecc_opt = OMAP_ECC_BCH8_CODE_HW;
- else
- info->ecc_opt = OMAP_ECC_BCH8_CODE_HW_DETECTION_SW;
- } else if (!strcmp(s, "bch16")) {
- info->ecc_opt = OMAP_ECC_BCH16_CODE_HW;
- } else {
- dev_err(dev, "unrecognized value for ti,nand-ecc-opt\n");
- return -EINVAL;
- }
-
- /* select data transfer mode */
- if (!of_property_read_string(child, "ti,nand-xfer-type", &s)) {
- for (i = 0; i < ARRAY_SIZE(nand_xfer_types); i++) {
- if (!strcasecmp(s, nand_xfer_types[i])) {
- info->xfer_type = i;
- return 0;
- }
- }
-
- dev_err(dev, "unrecognized value for ti,nand-xfer-type\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int omap_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct omap_nand_info *info = mtd_to_omap(mtd);
- struct nand_chip *chip = &info->nand;
- int off = BADBLOCK_MARKER_LENGTH;
-
- if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW &&
- !(chip->options & NAND_BUSWIDTH_16))
- off = 1;
-
- if (section)
- return -ERANGE;
-
- oobregion->offset = off;
- oobregion->length = chip->ecc.total;
-
- return 0;
-}
-
-static int omap_ooblayout_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct omap_nand_info *info = mtd_to_omap(mtd);
- struct nand_chip *chip = &info->nand;
- int off = BADBLOCK_MARKER_LENGTH;
-
- if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW &&
- !(chip->options & NAND_BUSWIDTH_16))
- off = 1;
-
- if (section)
- return -ERANGE;
-
- off += chip->ecc.total;
- if (off >= mtd->oobsize)
- return -ERANGE;
-
- oobregion->offset = off;
- oobregion->length = mtd->oobsize - off;
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops omap_ooblayout_ops = {
- .ecc = omap_ooblayout_ecc,
- .free = omap_ooblayout_free,
-};
-
-static int omap_sw_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- int off = BADBLOCK_MARKER_LENGTH;
-
- if (section >= chip->ecc.steps)
- return -ERANGE;
-
- /*
- * When SW correction is employed, one OMAP specific marker byte is
- * reserved after each ECC step.
- */
- oobregion->offset = off + (section * (chip->ecc.bytes + 1));
- oobregion->length = chip->ecc.bytes;
-
- return 0;
-}
-
-static int omap_sw_ooblayout_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- int off = BADBLOCK_MARKER_LENGTH;
-
- if (section)
- return -ERANGE;
-
- /*
- * When SW correction is employed, one OMAP specific marker byte is
- * reserved after each ECC step.
- */
- off += ((chip->ecc.bytes + 1) * chip->ecc.steps);
- if (off >= mtd->oobsize)
- return -ERANGE;
-
- oobregion->offset = off;
- oobregion->length = mtd->oobsize - off;
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops omap_sw_ooblayout_ops = {
- .ecc = omap_sw_ooblayout_ecc,
- .free = omap_sw_ooblayout_free,
-};
-
-static int omap_nand_probe(struct platform_device *pdev)
-{
- struct omap_nand_info *info;
- struct mtd_info *mtd;
- struct nand_chip *nand_chip;
- int err;
- dma_cap_mask_t mask;
- struct resource *res;
- struct device *dev = &pdev->dev;
- int min_oobbytes = BADBLOCK_MARKER_LENGTH;
- int oobbytes_per_step;
-
- info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info),
- GFP_KERNEL);
- if (!info)
- return -ENOMEM;
-
- info->pdev = pdev;
-
- err = omap_get_dt_info(dev, info);
- if (err)
- return err;
-
- info->ops = gpmc_omap_get_nand_ops(&info->reg, info->gpmc_cs);
- if (!info->ops) {
- dev_err(&pdev->dev, "Failed to get GPMC->NAND interface\n");
- return -ENODEV;
- }
-
- nand_chip = &info->nand;
- mtd = nand_to_mtd(nand_chip);
- mtd->dev.parent = &pdev->dev;
- nand_chip->ecc.priv = NULL;
- nand_set_flash_node(nand_chip, dev->of_node);
-
- if (!mtd->name) {
- mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
- "omap2-nand.%d", info->gpmc_cs);
- if (!mtd->name) {
- dev_err(&pdev->dev, "Failed to set MTD name\n");
- return -ENOMEM;
- }
- }
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(nand_chip->IO_ADDR_R))
- return PTR_ERR(nand_chip->IO_ADDR_R);
-
- info->phys_base = res->start;
-
- nand_chip->controller = &omap_gpmc_controller;
-
- nand_chip->IO_ADDR_W = nand_chip->IO_ADDR_R;
- nand_chip->cmd_ctrl = omap_hwcontrol;
-
- info->ready_gpiod = devm_gpiod_get_optional(&pdev->dev, "rb",
- GPIOD_IN);
- if (IS_ERR(info->ready_gpiod)) {
- dev_err(dev, "failed to get ready gpio\n");
- return PTR_ERR(info->ready_gpiod);
- }
-
- /*
- * If RDY/BSY line is connected to OMAP then use the omap ready
- * function and the generic nand_wait function which reads the status
- * register after monitoring the RDY/BSY line. Otherwise use a standard
- * chip delay which is slightly more than tR (AC Timing) of the NAND
- * device and read status register until you get a failure or success
- */
- if (info->ready_gpiod) {
- nand_chip->dev_ready = omap_dev_ready;
- nand_chip->chip_delay = 0;
- } else {
- nand_chip->waitfunc = omap_wait;
- nand_chip->chip_delay = 50;
- }
-
- if (info->flash_bbt)
- nand_chip->bbt_options |= NAND_BBT_USE_FLASH;
-
- /* scan NAND device connected to chip controller */
- nand_chip->options |= info->devsize & NAND_BUSWIDTH_16;
- err = nand_scan_ident(mtd, 1, NULL);
- if (err) {
- dev_err(&info->pdev->dev,
- "scan failed, may be bus-width mismatch\n");
- goto return_error;
- }
-
- if (nand_chip->bbt_options & NAND_BBT_USE_FLASH)
- nand_chip->bbt_options |= NAND_BBT_NO_OOB;
- else
- nand_chip->options |= NAND_SKIP_BBTSCAN;
-
- /* re-populate low-level callbacks based on xfer modes */
- switch (info->xfer_type) {
- case NAND_OMAP_PREFETCH_POLLED:
- nand_chip->read_buf = omap_read_buf_pref;
- nand_chip->write_buf = omap_write_buf_pref;
- break;
-
- case NAND_OMAP_POLLED:
- /* Use nand_base defaults for {read,write}_buf */
- break;
-
- case NAND_OMAP_PREFETCH_DMA:
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
- info->dma = dma_request_chan(pdev->dev.parent, "rxtx");
-
- if (IS_ERR(info->dma)) {
- dev_err(&pdev->dev, "DMA engine request failed\n");
- err = PTR_ERR(info->dma);
- goto return_error;
- } else {
- struct dma_slave_config cfg;
-
- memset(&cfg, 0, sizeof(cfg));
- cfg.src_addr = info->phys_base;
- cfg.dst_addr = info->phys_base;
- cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- cfg.src_maxburst = 16;
- cfg.dst_maxburst = 16;
- err = dmaengine_slave_config(info->dma, &cfg);
- if (err) {
- dev_err(&pdev->dev, "DMA engine slave config failed: %d\n",
- err);
- goto return_error;
- }
- nand_chip->read_buf = omap_read_buf_dma_pref;
- nand_chip->write_buf = omap_write_buf_dma_pref;
- }
- break;
-
- case NAND_OMAP_PREFETCH_IRQ:
- info->gpmc_irq_fifo = platform_get_irq(pdev, 0);
- if (info->gpmc_irq_fifo <= 0) {
- dev_err(&pdev->dev, "error getting fifo irq\n");
- err = -ENODEV;
- goto return_error;
- }
- err = devm_request_irq(&pdev->dev, info->gpmc_irq_fifo,
- omap_nand_irq, IRQF_SHARED,
- "gpmc-nand-fifo", info);
- if (err) {
- dev_err(&pdev->dev, "requesting irq(%d) error:%d",
- info->gpmc_irq_fifo, err);
- info->gpmc_irq_fifo = 0;
- goto return_error;
- }
-
- info->gpmc_irq_count = platform_get_irq(pdev, 1);
- if (info->gpmc_irq_count <= 0) {
- dev_err(&pdev->dev, "error getting count irq\n");
- err = -ENODEV;
- goto return_error;
- }
- err = devm_request_irq(&pdev->dev, info->gpmc_irq_count,
- omap_nand_irq, IRQF_SHARED,
- "gpmc-nand-count", info);
- if (err) {
- dev_err(&pdev->dev, "requesting irq(%d) error:%d",
- info->gpmc_irq_count, err);
- info->gpmc_irq_count = 0;
- goto return_error;
- }
-
- nand_chip->read_buf = omap_read_buf_irq_pref;
- nand_chip->write_buf = omap_write_buf_irq_pref;
-
- break;
-
- default:
- dev_err(&pdev->dev,
- "xfer_type(%d) not supported!\n", info->xfer_type);
- err = -EINVAL;
- goto return_error;
- }
-
- if (!omap2_nand_ecc_check(info)) {
- err = -EINVAL;
- goto return_error;
- }
-
- /*
- * Bail out earlier to let NAND_ECC_SOFT code create its own
- * ooblayout instead of using ours.
- */
- if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) {
- nand_chip->ecc.mode = NAND_ECC_SOFT;
- nand_chip->ecc.algo = NAND_ECC_HAMMING;
- goto scan_tail;
- }
-
- /* populate MTD interface based on ECC scheme */
- switch (info->ecc_opt) {
- case OMAP_ECC_HAM1_CODE_HW:
- pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n");
- nand_chip->ecc.mode = NAND_ECC_HW;
- nand_chip->ecc.bytes = 3;
- nand_chip->ecc.size = 512;
- nand_chip->ecc.strength = 1;
- nand_chip->ecc.calculate = omap_calculate_ecc;
- nand_chip->ecc.hwctl = omap_enable_hwecc;
- nand_chip->ecc.correct = omap_correct_data;
- mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
- oobbytes_per_step = nand_chip->ecc.bytes;
-
- if (!(nand_chip->options & NAND_BUSWIDTH_16))
- min_oobbytes = 1;
-
- break;
-
- case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
- pr_info("nand: using OMAP_ECC_BCH4_CODE_HW_DETECTION_SW\n");
- nand_chip->ecc.mode = NAND_ECC_HW;
- nand_chip->ecc.size = 512;
- nand_chip->ecc.bytes = 7;
- nand_chip->ecc.strength = 4;
- nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
- nand_chip->ecc.correct = nand_bch_correct_data;
- nand_chip->ecc.calculate = omap_calculate_ecc_bch_sw;
- mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
- /* Reserve one byte for the OMAP marker */
- oobbytes_per_step = nand_chip->ecc.bytes + 1;
- /* software bch library is used for locating errors */
- nand_chip->ecc.priv = nand_bch_init(mtd);
- if (!nand_chip->ecc.priv) {
- dev_err(&info->pdev->dev, "unable to use BCH library\n");
- err = -EINVAL;
- goto return_error;
- }
- break;
-
- case OMAP_ECC_BCH4_CODE_HW:
- pr_info("nand: using OMAP_ECC_BCH4_CODE_HW ECC scheme\n");
- nand_chip->ecc.mode = NAND_ECC_HW;
- nand_chip->ecc.size = 512;
- /* 14th bit is kept reserved for ROM-code compatibility */
- nand_chip->ecc.bytes = 7 + 1;
- nand_chip->ecc.strength = 4;
- nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
- nand_chip->ecc.correct = omap_elm_correct_data;
- nand_chip->ecc.read_page = omap_read_page_bch;
- nand_chip->ecc.write_page = omap_write_page_bch;
- nand_chip->ecc.write_subpage = omap_write_subpage_bch;
- mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
- oobbytes_per_step = nand_chip->ecc.bytes;
-
- err = elm_config(info->elm_dev, BCH4_ECC,
- mtd->writesize / nand_chip->ecc.size,
- nand_chip->ecc.size, nand_chip->ecc.bytes);
- if (err < 0)
- goto return_error;
- break;
-
- case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
- pr_info("nand: using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n");
- nand_chip->ecc.mode = NAND_ECC_HW;
- nand_chip->ecc.size = 512;
- nand_chip->ecc.bytes = 13;
- nand_chip->ecc.strength = 8;
- nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
- nand_chip->ecc.correct = nand_bch_correct_data;
- nand_chip->ecc.calculate = omap_calculate_ecc_bch_sw;
- mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
- /* Reserve one byte for the OMAP marker */
- oobbytes_per_step = nand_chip->ecc.bytes + 1;
- /* software bch library is used for locating errors */
- nand_chip->ecc.priv = nand_bch_init(mtd);
- if (!nand_chip->ecc.priv) {
- dev_err(&info->pdev->dev, "unable to use BCH library\n");
- err = -EINVAL;
- goto return_error;
- }
- break;
-
- case OMAP_ECC_BCH8_CODE_HW:
- pr_info("nand: using OMAP_ECC_BCH8_CODE_HW ECC scheme\n");
- nand_chip->ecc.mode = NAND_ECC_HW;
- nand_chip->ecc.size = 512;
- /* 14th bit is kept reserved for ROM-code compatibility */
- nand_chip->ecc.bytes = 13 + 1;
- nand_chip->ecc.strength = 8;
- nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
- nand_chip->ecc.correct = omap_elm_correct_data;
- nand_chip->ecc.read_page = omap_read_page_bch;
- nand_chip->ecc.write_page = omap_write_page_bch;
- nand_chip->ecc.write_subpage = omap_write_subpage_bch;
- mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
- oobbytes_per_step = nand_chip->ecc.bytes;
-
- err = elm_config(info->elm_dev, BCH8_ECC,
- mtd->writesize / nand_chip->ecc.size,
- nand_chip->ecc.size, nand_chip->ecc.bytes);
- if (err < 0)
- goto return_error;
-
- break;
-
- case OMAP_ECC_BCH16_CODE_HW:
- pr_info("using OMAP_ECC_BCH16_CODE_HW ECC scheme\n");
- nand_chip->ecc.mode = NAND_ECC_HW;
- nand_chip->ecc.size = 512;
- nand_chip->ecc.bytes = 26;
- nand_chip->ecc.strength = 16;
- nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
- nand_chip->ecc.correct = omap_elm_correct_data;
- nand_chip->ecc.read_page = omap_read_page_bch;
- nand_chip->ecc.write_page = omap_write_page_bch;
- nand_chip->ecc.write_subpage = omap_write_subpage_bch;
- mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
- oobbytes_per_step = nand_chip->ecc.bytes;
-
- err = elm_config(info->elm_dev, BCH16_ECC,
- mtd->writesize / nand_chip->ecc.size,
- nand_chip->ecc.size, nand_chip->ecc.bytes);
- if (err < 0)
- goto return_error;
-
- break;
- default:
- dev_err(&info->pdev->dev, "invalid or unsupported ECC scheme\n");
- err = -EINVAL;
- goto return_error;
- }
-
- /* check if NAND device's OOB is enough to store ECC signatures */
- min_oobbytes += (oobbytes_per_step *
- (mtd->writesize / nand_chip->ecc.size));
- if (mtd->oobsize < min_oobbytes) {
- dev_err(&info->pdev->dev,
- "not enough OOB bytes required = %d, available=%d\n",
- min_oobbytes, mtd->oobsize);
- err = -EINVAL;
- goto return_error;
- }
-
-scan_tail:
- /* second phase scan */
- err = nand_scan_tail(mtd);
- if (err)
- goto return_error;
-
- err = mtd_device_register(mtd, NULL, 0);
- if (err)
- goto return_error;
-
- platform_set_drvdata(pdev, mtd);
-
- return 0;
-
-return_error:
- if (!IS_ERR_OR_NULL(info->dma))
- dma_release_channel(info->dma);
- if (nand_chip->ecc.priv) {
- nand_bch_free(nand_chip->ecc.priv);
- nand_chip->ecc.priv = NULL;
- }
- return err;
-}
-
-static int omap_nand_remove(struct platform_device *pdev)
-{
- struct mtd_info *mtd = platform_get_drvdata(pdev);
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct omap_nand_info *info = mtd_to_omap(mtd);
- if (nand_chip->ecc.priv) {
- nand_bch_free(nand_chip->ecc.priv);
- nand_chip->ecc.priv = NULL;
- }
- if (info->dma)
- dma_release_channel(info->dma);
- nand_release(mtd);
- return 0;
-}
-
-static const struct of_device_id omap_nand_ids[] = {
- { .compatible = "ti,omap2-nand", },
- {},
-};
-MODULE_DEVICE_TABLE(of, omap_nand_ids);
-
-static struct platform_driver omap_nand_driver = {
- .probe = omap_nand_probe,
- .remove = omap_nand_remove,
- .driver = {
- .name = DRIVER_NAME,
- .of_match_table = of_match_ptr(omap_nand_ids),
- },
-};
-
-module_platform_driver(omap_nand_driver);
-
-MODULE_ALIAS("platform:" DRIVER_NAME);
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Glue layer for NAND flash on TI OMAP boards");
diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/nand/onenand/Kconfig
index 9dc15748947b..9dc15748947b 100644
--- a/drivers/mtd/onenand/Kconfig
+++ b/drivers/mtd/nand/onenand/Kconfig
diff --git a/drivers/mtd/onenand/Makefile b/drivers/mtd/nand/onenand/Makefile
index f8b624aca9cc..f8b624aca9cc 100644
--- a/drivers/mtd/onenand/Makefile
+++ b/drivers/mtd/nand/onenand/Makefile
diff --git a/drivers/mtd/nand/onenand/generic.c b/drivers/mtd/nand/onenand/generic.c
new file mode 100644
index 000000000000..d5ccaf943b91
--- /dev/null
+++ b/drivers/mtd/nand/onenand/generic.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2005 Samsung Electronics
+ * Kyungmin Park <kyungmin.park@samsung.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.
+ *
+ * Overview:
+ * This is a device driver for the OneNAND flash for generic boards.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/onenand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/io.h>
+
+/*
+ * Note: Driver name and platform data format have been updated!
+ *
+ * This version of the driver is named "onenand-flash" and takes struct
+ * onenand_platform_data as platform data. The old ARM-specific version
+ * with the name "onenand" used to take struct flash_platform_data.
+ */
+#define DRIVER_NAME "onenand-flash"
+
+struct onenand_info {
+ struct mtd_info mtd;
+ struct onenand_chip onenand;
+};
+
+static int generic_onenand_probe(struct platform_device *pdev)
+{
+ struct onenand_info *info;
+ struct onenand_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct resource *res = pdev->resource;
+ unsigned long size = resource_size(res);
+ int err;
+
+ info = kzalloc(sizeof(struct onenand_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ if (!request_mem_region(res->start, size, dev_name(&pdev->dev))) {
+ err = -EBUSY;
+ goto out_free_info;
+ }
+
+ info->onenand.base = ioremap(res->start, size);
+ if (!info->onenand.base) {
+ err = -ENOMEM;
+ goto out_release_mem_region;
+ }
+
+ info->onenand.mmcontrol = pdata ? pdata->mmcontrol : NULL;
+ info->onenand.irq = platform_get_irq(pdev, 0);
+
+ info->mtd.dev.parent = &pdev->dev;
+ info->mtd.priv = &info->onenand;
+
+ if (onenand_scan(&info->mtd, 1)) {
+ err = -ENXIO;
+ goto out_iounmap;
+ }
+
+ err = mtd_device_parse_register(&info->mtd, NULL, NULL,
+ pdata ? pdata->parts : NULL,
+ pdata ? pdata->nr_parts : 0);
+
+ platform_set_drvdata(pdev, info);
+
+ return 0;
+
+out_iounmap:
+ iounmap(info->onenand.base);
+out_release_mem_region:
+ release_mem_region(res->start, size);
+out_free_info:
+ kfree(info);
+
+ return err;
+}
+
+static int generic_onenand_remove(struct platform_device *pdev)
+{
+ struct onenand_info *info = platform_get_drvdata(pdev);
+ struct resource *res = pdev->resource;
+ unsigned long size = resource_size(res);
+
+ if (info) {
+ onenand_release(&info->mtd);
+ release_mem_region(res->start, size);
+ iounmap(info->onenand.base);
+ kfree(info);
+ }
+
+ return 0;
+}
+
+static struct platform_driver generic_onenand_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+ .probe = generic_onenand_probe,
+ .remove = generic_onenand_remove,
+};
+
+module_platform_driver(generic_onenand_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>");
+MODULE_DESCRIPTION("Glue layer for OneNAND flash on generic boards");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/mtd/nand/onenand/omap2.c b/drivers/mtd/nand/onenand/omap2.c
new file mode 100644
index 000000000000..9c159f0dd9a6
--- /dev/null
+++ b/drivers/mtd/nand/onenand/omap2.c
@@ -0,0 +1,660 @@
+/*
+ * OneNAND driver for OMAP2 / OMAP3
+ *
+ * Copyright © 2005-2006 Nokia Corporation
+ *
+ * Author: Jarkko Lavinen <jarkko.lavinen@nokia.com> and Juha Yrjölä
+ * IRQ and DMA support written by Timo Teras
+ *
+ * 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.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/onenand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/of_device.h>
+#include <linux/omap-gpmc.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/gpio/consumer.h>
+
+#include <asm/mach/flash.h>
+
+#define DRIVER_NAME "omap2-onenand"
+
+#define ONENAND_BUFRAM_SIZE (1024 * 5)
+
+struct omap2_onenand {
+ struct platform_device *pdev;
+ int gpmc_cs;
+ unsigned long phys_base;
+ struct gpio_desc *int_gpiod;
+ struct mtd_info mtd;
+ struct onenand_chip onenand;
+ struct completion irq_done;
+ struct completion dma_done;
+ struct dma_chan *dma_chan;
+};
+
+static void omap2_onenand_dma_complete_func(void *completion)
+{
+ complete(completion);
+}
+
+static irqreturn_t omap2_onenand_interrupt(int irq, void *dev_id)
+{
+ struct omap2_onenand *c = dev_id;
+
+ complete(&c->irq_done);
+
+ return IRQ_HANDLED;
+}
+
+static inline unsigned short read_reg(struct omap2_onenand *c, int reg)
+{
+ return readw(c->onenand.base + reg);
+}
+
+static inline void write_reg(struct omap2_onenand *c, unsigned short value,
+ int reg)
+{
+ writew(value, c->onenand.base + reg);
+}
+
+static int omap2_onenand_set_cfg(struct omap2_onenand *c,
+ bool sr, bool sw,
+ int latency, int burst_len)
+{
+ unsigned short reg = ONENAND_SYS_CFG1_RDY | ONENAND_SYS_CFG1_INT;
+
+ reg |= latency << ONENAND_SYS_CFG1_BRL_SHIFT;
+
+ switch (burst_len) {
+ case 0: /* continuous */
+ break;
+ case 4:
+ reg |= ONENAND_SYS_CFG1_BL_4;
+ break;
+ case 8:
+ reg |= ONENAND_SYS_CFG1_BL_8;
+ break;
+ case 16:
+ reg |= ONENAND_SYS_CFG1_BL_16;
+ break;
+ case 32:
+ reg |= ONENAND_SYS_CFG1_BL_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (latency > 5)
+ reg |= ONENAND_SYS_CFG1_HF;
+ if (latency > 7)
+ reg |= ONENAND_SYS_CFG1_VHF;
+ if (sr)
+ reg |= ONENAND_SYS_CFG1_SYNC_READ;
+ if (sw)
+ reg |= ONENAND_SYS_CFG1_SYNC_WRITE;
+
+ write_reg(c, reg, ONENAND_REG_SYS_CFG1);
+
+ return 0;
+}
+
+static int omap2_onenand_get_freq(int ver)
+{
+ switch ((ver >> 4) & 0xf) {
+ case 0:
+ return 40;
+ case 1:
+ return 54;
+ case 2:
+ return 66;
+ case 3:
+ return 83;
+ case 4:
+ return 104;
+ }
+
+ return -EINVAL;
+}
+
+static void wait_err(char *msg, int state, unsigned int ctrl, unsigned int intr)
+{
+ printk(KERN_ERR "onenand_wait: %s! state %d ctrl 0x%04x intr 0x%04x\n",
+ msg, state, ctrl, intr);
+}
+
+static void wait_warn(char *msg, int state, unsigned int ctrl,
+ unsigned int intr)
+{
+ printk(KERN_WARNING "onenand_wait: %s! state %d ctrl 0x%04x "
+ "intr 0x%04x\n", msg, state, ctrl, intr);
+}
+
+static int omap2_onenand_wait(struct mtd_info *mtd, int state)
+{
+ struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd);
+ struct onenand_chip *this = mtd->priv;
+ unsigned int intr = 0;
+ unsigned int ctrl, ctrl_mask;
+ unsigned long timeout;
+ u32 syscfg;
+
+ if (state == FL_RESETING || state == FL_PREPARING_ERASE ||
+ state == FL_VERIFYING_ERASE) {
+ int i = 21;
+ unsigned int intr_flags = ONENAND_INT_MASTER;
+
+ switch (state) {
+ case FL_RESETING:
+ intr_flags |= ONENAND_INT_RESET;
+ break;
+ case FL_PREPARING_ERASE:
+ intr_flags |= ONENAND_INT_ERASE;
+ break;
+ case FL_VERIFYING_ERASE:
+ i = 101;
+ break;
+ }
+
+ while (--i) {
+ udelay(1);
+ intr = read_reg(c, ONENAND_REG_INTERRUPT);
+ if (intr & ONENAND_INT_MASTER)
+ break;
+ }
+ ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS);
+ if (ctrl & ONENAND_CTRL_ERROR) {
+ wait_err("controller error", state, ctrl, intr);
+ return -EIO;
+ }
+ if ((intr & intr_flags) == intr_flags)
+ return 0;
+ /* Continue in wait for interrupt branch */
+ }
+
+ if (state != FL_READING) {
+ int result;
+
+ /* Turn interrupts on */
+ syscfg = read_reg(c, ONENAND_REG_SYS_CFG1);
+ if (!(syscfg & ONENAND_SYS_CFG1_IOBE)) {
+ syscfg |= ONENAND_SYS_CFG1_IOBE;
+ write_reg(c, syscfg, ONENAND_REG_SYS_CFG1);
+ /* Add a delay to let GPIO settle */
+ syscfg = read_reg(c, ONENAND_REG_SYS_CFG1);
+ }
+
+ reinit_completion(&c->irq_done);
+ result = gpiod_get_value(c->int_gpiod);
+ if (result < 0) {
+ ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS);
+ intr = read_reg(c, ONENAND_REG_INTERRUPT);
+ wait_err("gpio error", state, ctrl, intr);
+ return result;
+ } else if (result == 0) {
+ int retry_cnt = 0;
+retry:
+ if (!wait_for_completion_io_timeout(&c->irq_done,
+ msecs_to_jiffies(20))) {
+ /* Timeout after 20ms */
+ ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS);
+ if (ctrl & ONENAND_CTRL_ONGO &&
+ !this->ongoing) {
+ /*
+ * The operation seems to be still going
+ * so give it some more time.
+ */
+ retry_cnt += 1;
+ if (retry_cnt < 3)
+ goto retry;
+ intr = read_reg(c,
+ ONENAND_REG_INTERRUPT);
+ wait_err("timeout", state, ctrl, intr);
+ return -EIO;
+ }
+ intr = read_reg(c, ONENAND_REG_INTERRUPT);
+ if ((intr & ONENAND_INT_MASTER) == 0)
+ wait_warn("timeout", state, ctrl, intr);
+ }
+ }
+ } else {
+ int retry_cnt = 0;
+
+ /* Turn interrupts off */
+ syscfg = read_reg(c, ONENAND_REG_SYS_CFG1);
+ syscfg &= ~ONENAND_SYS_CFG1_IOBE;
+ write_reg(c, syscfg, ONENAND_REG_SYS_CFG1);
+
+ timeout = jiffies + msecs_to_jiffies(20);
+ while (1) {
+ if (time_before(jiffies, timeout)) {
+ intr = read_reg(c, ONENAND_REG_INTERRUPT);
+ if (intr & ONENAND_INT_MASTER)
+ break;
+ } else {
+ /* Timeout after 20ms */
+ ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS);
+ if (ctrl & ONENAND_CTRL_ONGO) {
+ /*
+ * The operation seems to be still going
+ * so give it some more time.
+ */
+ retry_cnt += 1;
+ if (retry_cnt < 3) {
+ timeout = jiffies +
+ msecs_to_jiffies(20);
+ continue;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ intr = read_reg(c, ONENAND_REG_INTERRUPT);
+ ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS);
+
+ if (intr & ONENAND_INT_READ) {
+ int ecc = read_reg(c, ONENAND_REG_ECC_STATUS);
+
+ if (ecc) {
+ unsigned int addr1, addr8;
+
+ addr1 = read_reg(c, ONENAND_REG_START_ADDRESS1);
+ addr8 = read_reg(c, ONENAND_REG_START_ADDRESS8);
+ if (ecc & ONENAND_ECC_2BIT_ALL) {
+ printk(KERN_ERR "onenand_wait: ECC error = "
+ "0x%04x, addr1 %#x, addr8 %#x\n",
+ ecc, addr1, addr8);
+ mtd->ecc_stats.failed++;
+ return -EBADMSG;
+ } else if (ecc & ONENAND_ECC_1BIT_ALL) {
+ printk(KERN_NOTICE "onenand_wait: correctable "
+ "ECC error = 0x%04x, addr1 %#x, "
+ "addr8 %#x\n", ecc, addr1, addr8);
+ mtd->ecc_stats.corrected++;
+ }
+ }
+ } else if (state == FL_READING) {
+ wait_err("timeout", state, ctrl, intr);
+ return -EIO;
+ }
+
+ if (ctrl & ONENAND_CTRL_ERROR) {
+ wait_err("controller error", state, ctrl, intr);
+ if (ctrl & ONENAND_CTRL_LOCK)
+ printk(KERN_ERR "onenand_wait: "
+ "Device is write protected!!!\n");
+ return -EIO;
+ }
+
+ ctrl_mask = 0xFE9F;
+ if (this->ongoing)
+ ctrl_mask &= ~0x8000;
+
+ if (ctrl & ctrl_mask)
+ wait_warn("unexpected controller status", state, ctrl, intr);
+
+ return 0;
+}
+
+static inline int omap2_onenand_bufferram_offset(struct mtd_info *mtd, int area)
+{
+ struct onenand_chip *this = mtd->priv;
+
+ if (ONENAND_CURRENT_BUFFERRAM(this)) {
+ if (area == ONENAND_DATARAM)
+ return this->writesize;
+ if (area == ONENAND_SPARERAM)
+ return mtd->oobsize;
+ }
+
+ return 0;
+}
+
+static inline int omap2_onenand_dma_transfer(struct omap2_onenand *c,
+ dma_addr_t src, dma_addr_t dst,
+ size_t count)
+{
+ struct dma_async_tx_descriptor *tx;
+ dma_cookie_t cookie;
+
+ tx = dmaengine_prep_dma_memcpy(c->dma_chan, dst, src, count, 0);
+ if (!tx) {
+ dev_err(&c->pdev->dev, "Failed to prepare DMA memcpy\n");
+ return -EIO;
+ }
+
+ reinit_completion(&c->dma_done);
+
+ tx->callback = omap2_onenand_dma_complete_func;
+ tx->callback_param = &c->dma_done;
+
+ cookie = tx->tx_submit(tx);
+ if (dma_submit_error(cookie)) {
+ dev_err(&c->pdev->dev, "Failed to do DMA tx_submit\n");
+ return -EIO;
+ }
+
+ dma_async_issue_pending(c->dma_chan);
+
+ if (!wait_for_completion_io_timeout(&c->dma_done,
+ msecs_to_jiffies(20))) {
+ dmaengine_terminate_sync(c->dma_chan);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int omap2_onenand_read_bufferram(struct mtd_info *mtd, int area,
+ unsigned char *buffer, int offset,
+ size_t count)
+{
+ struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd);
+ struct onenand_chip *this = mtd->priv;
+ dma_addr_t dma_src, dma_dst;
+ int bram_offset;
+ void *buf = (void *)buffer;
+ size_t xtra;
+ int ret;
+
+ bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset;
+ if (bram_offset & 3 || (size_t)buf & 3 || count < 384)
+ goto out_copy;
+
+ /* panic_write() may be in an interrupt context */
+ if (in_interrupt() || oops_in_progress)
+ goto out_copy;
+
+ if (buf >= high_memory) {
+ struct page *p1;
+
+ if (((size_t)buf & PAGE_MASK) !=
+ ((size_t)(buf + count - 1) & PAGE_MASK))
+ goto out_copy;
+ p1 = vmalloc_to_page(buf);
+ if (!p1)
+ goto out_copy;
+ buf = page_address(p1) + ((size_t)buf & ~PAGE_MASK);
+ }
+
+ xtra = count & 3;
+ if (xtra) {
+ count -= xtra;
+ memcpy(buf + count, this->base + bram_offset + count, xtra);
+ }
+
+ dma_src = c->phys_base + bram_offset;
+ dma_dst = dma_map_single(&c->pdev->dev, buf, count, DMA_FROM_DEVICE);
+ if (dma_mapping_error(&c->pdev->dev, dma_dst)) {
+ dev_err(&c->pdev->dev,
+ "Couldn't DMA map a %d byte buffer\n",
+ count);
+ goto out_copy;
+ }
+
+ ret = omap2_onenand_dma_transfer(c, dma_src, dma_dst, count);
+ dma_unmap_single(&c->pdev->dev, dma_dst, count, DMA_FROM_DEVICE);
+
+ if (ret) {
+ dev_err(&c->pdev->dev, "timeout waiting for DMA\n");
+ goto out_copy;
+ }
+
+ return 0;
+
+out_copy:
+ memcpy(buf, this->base + bram_offset, count);
+ return 0;
+}
+
+static int omap2_onenand_write_bufferram(struct mtd_info *mtd, int area,
+ const unsigned char *buffer,
+ int offset, size_t count)
+{
+ struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd);
+ struct onenand_chip *this = mtd->priv;
+ dma_addr_t dma_src, dma_dst;
+ int bram_offset;
+ void *buf = (void *)buffer;
+ int ret;
+
+ bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset;
+ if (bram_offset & 3 || (size_t)buf & 3 || count < 384)
+ goto out_copy;
+
+ /* panic_write() may be in an interrupt context */
+ if (in_interrupt() || oops_in_progress)
+ goto out_copy;
+
+ if (buf >= high_memory) {
+ struct page *p1;
+
+ if (((size_t)buf & PAGE_MASK) !=
+ ((size_t)(buf + count - 1) & PAGE_MASK))
+ goto out_copy;
+ p1 = vmalloc_to_page(buf);
+ if (!p1)
+ goto out_copy;
+ buf = page_address(p1) + ((size_t)buf & ~PAGE_MASK);
+ }
+
+ dma_src = dma_map_single(&c->pdev->dev, buf, count, DMA_TO_DEVICE);
+ dma_dst = c->phys_base + bram_offset;
+ if (dma_mapping_error(&c->pdev->dev, dma_src)) {
+ dev_err(&c->pdev->dev,
+ "Couldn't DMA map a %d byte buffer\n",
+ count);
+ return -1;
+ }
+
+ ret = omap2_onenand_dma_transfer(c, dma_src, dma_dst, count);
+ dma_unmap_single(&c->pdev->dev, dma_src, count, DMA_TO_DEVICE);
+
+ if (ret) {
+ dev_err(&c->pdev->dev, "timeout waiting for DMA\n");
+ goto out_copy;
+ }
+
+ return 0;
+
+out_copy:
+ memcpy(this->base + bram_offset, buf, count);
+ return 0;
+}
+
+static void omap2_onenand_shutdown(struct platform_device *pdev)
+{
+ struct omap2_onenand *c = dev_get_drvdata(&pdev->dev);
+
+ /* With certain content in the buffer RAM, the OMAP boot ROM code
+ * can recognize the flash chip incorrectly. Zero it out before
+ * soft reset.
+ */
+ memset((__force void *)c->onenand.base, 0, ONENAND_BUFRAM_SIZE);
+}
+
+static int omap2_onenand_probe(struct platform_device *pdev)
+{
+ u32 val;
+ dma_cap_mask_t mask;
+ int freq, latency, r;
+ struct resource *res;
+ struct omap2_onenand *c;
+ struct gpmc_onenand_info info;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "error getting memory resource\n");
+ return -EINVAL;
+ }
+
+ r = of_property_read_u32(np, "reg", &val);
+ if (r) {
+ dev_err(dev, "reg not found in DT\n");
+ return r;
+ }
+
+ c = devm_kzalloc(dev, sizeof(struct omap2_onenand), GFP_KERNEL);
+ if (!c)
+ return -ENOMEM;
+
+ init_completion(&c->irq_done);
+ init_completion(&c->dma_done);
+ c->gpmc_cs = val;
+ c->phys_base = res->start;
+
+ c->onenand.base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(c->onenand.base))
+ return PTR_ERR(c->onenand.base);
+
+ c->int_gpiod = devm_gpiod_get_optional(dev, "int", GPIOD_IN);
+ if (IS_ERR(c->int_gpiod)) {
+ r = PTR_ERR(c->int_gpiod);
+ /* Just try again if this happens */
+ if (r != -EPROBE_DEFER)
+ dev_err(dev, "error getting gpio: %d\n", r);
+ return r;
+ }
+
+ if (c->int_gpiod) {
+ r = devm_request_irq(dev, gpiod_to_irq(c->int_gpiod),
+ omap2_onenand_interrupt,
+ IRQF_TRIGGER_RISING, "onenand", c);
+ if (r)
+ return r;
+
+ c->onenand.wait = omap2_onenand_wait;
+ }
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+
+ c->dma_chan = dma_request_channel(mask, NULL, NULL);
+ if (c->dma_chan) {
+ c->onenand.read_bufferram = omap2_onenand_read_bufferram;
+ c->onenand.write_bufferram = omap2_onenand_write_bufferram;
+ }
+
+ c->pdev = pdev;
+ c->mtd.priv = &c->onenand;
+ c->mtd.dev.parent = dev;
+ mtd_set_of_node(&c->mtd, dev->of_node);
+
+ dev_info(dev, "initializing on CS%d (0x%08lx), va %p, %s mode\n",
+ c->gpmc_cs, c->phys_base, c->onenand.base,
+ c->dma_chan ? "DMA" : "PIO");
+
+ if ((r = onenand_scan(&c->mtd, 1)) < 0)
+ goto err_release_dma;
+
+ freq = omap2_onenand_get_freq(c->onenand.version_id);
+ if (freq > 0) {
+ switch (freq) {
+ case 104:
+ latency = 7;
+ break;
+ case 83:
+ latency = 6;
+ break;
+ case 66:
+ latency = 5;
+ break;
+ case 56:
+ latency = 4;
+ break;
+ default: /* 40 MHz or lower */
+ latency = 3;
+ break;
+ }
+
+ r = gpmc_omap_onenand_set_timings(dev, c->gpmc_cs,
+ freq, latency, &info);
+ if (r)
+ goto err_release_onenand;
+
+ r = omap2_onenand_set_cfg(c, info.sync_read, info.sync_write,
+ latency, info.burst_len);
+ if (r)
+ goto err_release_onenand;
+
+ if (info.sync_read || info.sync_write)
+ dev_info(dev, "optimized timings for %d MHz\n", freq);
+ }
+
+ r = mtd_device_register(&c->mtd, NULL, 0);
+ if (r)
+ goto err_release_onenand;
+
+ platform_set_drvdata(pdev, c);
+
+ return 0;
+
+err_release_onenand:
+ onenand_release(&c->mtd);
+err_release_dma:
+ if (c->dma_chan)
+ dma_release_channel(c->dma_chan);
+
+ return r;
+}
+
+static int omap2_onenand_remove(struct platform_device *pdev)
+{
+ struct omap2_onenand *c = dev_get_drvdata(&pdev->dev);
+
+ onenand_release(&c->mtd);
+ if (c->dma_chan)
+ dma_release_channel(c->dma_chan);
+ omap2_onenand_shutdown(pdev);
+
+ return 0;
+}
+
+static const struct of_device_id omap2_onenand_id_table[] = {
+ { .compatible = "ti,omap2-onenand", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, omap2_onenand_id_table);
+
+static struct platform_driver omap2_onenand_driver = {
+ .probe = omap2_onenand_probe,
+ .remove = omap2_onenand_remove,
+ .shutdown = omap2_onenand_shutdown,
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = omap2_onenand_id_table,
+ },
+};
+
+module_platform_driver(omap2_onenand_driver);
+
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jarkko Lavinen <jarkko.lavinen@nokia.com>");
+MODULE_DESCRIPTION("Glue layer for OneNAND flash on OMAP2 / OMAP3");
diff --git a/drivers/mtd/nand/onenand/onenand_base.c b/drivers/mtd/nand/onenand/onenand_base.c
new file mode 100644
index 000000000000..b7105192cb12
--- /dev/null
+++ b/drivers/mtd/nand/onenand/onenand_base.c
@@ -0,0 +1,4014 @@
+/*
+ * Copyright © 2005-2009 Samsung Electronics
+ * Copyright © 2007 Nokia Corporation
+ *
+ * Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ * Credits:
+ * Adrian Hunter <ext-adrian.hunter@nokia.com>:
+ * auto-placement support, read-while load support, various fixes
+ *
+ * Vishak G <vishak.g at samsung.com>, Rohit Hagargundgi <h.rohit at samsung.com>
+ * Flex-OneNAND support
+ * Amul Kumar Saha <amul.saha at samsung.com>
+ * OTP support
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/onenand.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+
+/*
+ * Multiblock erase if number of blocks to erase is 2 or more.
+ * Maximum number of blocks for simultaneous erase is 64.
+ */
+#define MB_ERASE_MIN_BLK_COUNT 2
+#define MB_ERASE_MAX_BLK_COUNT 64
+
+/* Default Flex-OneNAND boundary and lock respectively */
+static int flex_bdry[MAX_DIES * 2] = { -1, 0, -1, 0 };
+
+module_param_array(flex_bdry, int, NULL, 0400);
+MODULE_PARM_DESC(flex_bdry, "SLC Boundary information for Flex-OneNAND"
+ "Syntax:flex_bdry=DIE_BDRY,LOCK,..."
+ "DIE_BDRY: SLC boundary of the die"
+ "LOCK: Locking information for SLC boundary"
+ " : 0->Set boundary in unlocked status"
+ " : 1->Set boundary in locked status");
+
+/* Default OneNAND/Flex-OneNAND OTP options*/
+static int otp;
+
+module_param(otp, int, 0400);
+MODULE_PARM_DESC(otp, "Corresponding behaviour of OneNAND in OTP"
+ "Syntax : otp=LOCK_TYPE"
+ "LOCK_TYPE : Keys issued, for specific OTP Lock type"
+ " : 0 -> Default (No Blocks Locked)"
+ " : 1 -> OTP Block lock"
+ " : 2 -> 1st Block lock"
+ " : 3 -> BOTH OTP Block and 1st Block lock");
+
+/*
+ * flexonenand_oob_128 - oob info for Flex-Onenand with 4KB page
+ * For now, we expose only 64 out of 80 ecc bytes
+ */
+static int flexonenand_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ if (section > 7)
+ return -ERANGE;
+
+ oobregion->offset = (section * 16) + 6;
+ oobregion->length = 10;
+
+ return 0;
+}
+
+static int flexonenand_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ if (section > 7)
+ return -ERANGE;
+
+ oobregion->offset = (section * 16) + 2;
+ oobregion->length = 4;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops flexonenand_ooblayout_ops = {
+ .ecc = flexonenand_ooblayout_ecc,
+ .free = flexonenand_ooblayout_free,
+};
+
+/*
+ * onenand_oob_128 - oob info for OneNAND with 4KB page
+ *
+ * Based on specification:
+ * 4Gb M-die OneNAND Flash (KFM4G16Q4M, KFN8G16Q4M). Rev. 1.3, Apr. 2010
+ *
+ */
+static int onenand_ooblayout_128_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ if (section > 7)
+ return -ERANGE;
+
+ oobregion->offset = (section * 16) + 7;
+ oobregion->length = 9;
+
+ return 0;
+}
+
+static int onenand_ooblayout_128_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ if (section >= 8)
+ return -ERANGE;
+
+ /*
+ * free bytes are using the spare area fields marked as
+ * "Managed by internal ECC logic for Logical Sector Number area"
+ */
+ oobregion->offset = (section * 16) + 2;
+ oobregion->length = 3;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops onenand_oob_128_ooblayout_ops = {
+ .ecc = onenand_ooblayout_128_ecc,
+ .free = onenand_ooblayout_128_free,
+};
+
+/**
+ * onenand_oob_32_64 - oob info for large (2KB) page
+ */
+static int onenand_ooblayout_32_64_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ if (section > 3)
+ return -ERANGE;
+
+ oobregion->offset = (section * 16) + 8;
+ oobregion->length = 5;
+
+ return 0;
+}
+
+static int onenand_ooblayout_32_64_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ int sections = (mtd->oobsize / 32) * 2;
+
+ if (section >= sections)
+ return -ERANGE;
+
+ if (section & 1) {
+ oobregion->offset = ((section - 1) * 16) + 14;
+ oobregion->length = 2;
+ } else {
+ oobregion->offset = (section * 16) + 2;
+ oobregion->length = 3;
+ }
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops onenand_oob_32_64_ooblayout_ops = {
+ .ecc = onenand_ooblayout_32_64_ecc,
+ .free = onenand_ooblayout_32_64_free,
+};
+
+static const unsigned char ffchars[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 16 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 32 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 48 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 64 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 80 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 96 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 112 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128 */
+};
+
+/**
+ * onenand_readw - [OneNAND Interface] Read OneNAND register
+ * @param addr address to read
+ *
+ * Read OneNAND register
+ */
+static unsigned short onenand_readw(void __iomem *addr)
+{
+ return readw(addr);
+}
+
+/**
+ * onenand_writew - [OneNAND Interface] Write OneNAND register with value
+ * @param value value to write
+ * @param addr address to write
+ *
+ * Write OneNAND register with value
+ */
+static void onenand_writew(unsigned short value, void __iomem *addr)
+{
+ writew(value, addr);
+}
+
+/**
+ * onenand_block_address - [DEFAULT] Get block address
+ * @param this onenand chip data structure
+ * @param block the block
+ * @return translated block address if DDP, otherwise same
+ *
+ * Setup Start Address 1 Register (F100h)
+ */
+static int onenand_block_address(struct onenand_chip *this, int block)
+{
+ /* Device Flash Core select, NAND Flash Block Address */
+ if (block & this->density_mask)
+ return ONENAND_DDP_CHIP1 | (block ^ this->density_mask);
+
+ return block;
+}
+
+/**
+ * onenand_bufferram_address - [DEFAULT] Get bufferram address
+ * @param this onenand chip data structure
+ * @param block the block
+ * @return set DBS value if DDP, otherwise 0
+ *
+ * Setup Start Address 2 Register (F101h) for DDP
+ */
+static int onenand_bufferram_address(struct onenand_chip *this, int block)
+{
+ /* Device BufferRAM Select */
+ if (block & this->density_mask)
+ return ONENAND_DDP_CHIP1;
+
+ return ONENAND_DDP_CHIP0;
+}
+
+/**
+ * onenand_page_address - [DEFAULT] Get page address
+ * @param page the page address
+ * @param sector the sector address
+ * @return combined page and sector address
+ *
+ * Setup Start Address 8 Register (F107h)
+ */
+static int onenand_page_address(int page, int sector)
+{
+ /* Flash Page Address, Flash Sector Address */
+ int fpa, fsa;
+
+ fpa = page & ONENAND_FPA_MASK;
+ fsa = sector & ONENAND_FSA_MASK;
+
+ return ((fpa << ONENAND_FPA_SHIFT) | fsa);
+}
+
+/**
+ * onenand_buffer_address - [DEFAULT] Get buffer address
+ * @param dataram1 DataRAM index
+ * @param sectors the sector address
+ * @param count the number of sectors
+ * @return the start buffer value
+ *
+ * Setup Start Buffer Register (F200h)
+ */
+static int onenand_buffer_address(int dataram1, int sectors, int count)
+{
+ int bsa, bsc;
+
+ /* BufferRAM Sector Address */
+ bsa = sectors & ONENAND_BSA_MASK;
+
+ if (dataram1)
+ bsa |= ONENAND_BSA_DATARAM1; /* DataRAM1 */
+ else
+ bsa |= ONENAND_BSA_DATARAM0; /* DataRAM0 */
+
+ /* BufferRAM Sector Count */
+ bsc = count & ONENAND_BSC_MASK;
+
+ return ((bsa << ONENAND_BSA_SHIFT) | bsc);
+}
+
+/**
+ * flexonenand_block- For given address return block number
+ * @param this - OneNAND device structure
+ * @param addr - Address for which block number is needed
+ */
+static unsigned flexonenand_block(struct onenand_chip *this, loff_t addr)
+{
+ unsigned boundary, blk, die = 0;
+
+ if (ONENAND_IS_DDP(this) && addr >= this->diesize[0]) {
+ die = 1;
+ addr -= this->diesize[0];
+ }
+
+ boundary = this->boundary[die];
+
+ blk = addr >> (this->erase_shift - 1);
+ if (blk > boundary)
+ blk = (blk + boundary + 1) >> 1;
+
+ blk += die ? this->density_mask : 0;
+ return blk;
+}
+
+inline unsigned onenand_block(struct onenand_chip *this, loff_t addr)
+{
+ if (!FLEXONENAND(this))
+ return addr >> this->erase_shift;
+ return flexonenand_block(this, addr);
+}
+
+/**
+ * flexonenand_addr - Return address of the block
+ * @this: OneNAND device structure
+ * @block: Block number on Flex-OneNAND
+ *
+ * Return address of the block
+ */
+static loff_t flexonenand_addr(struct onenand_chip *this, int block)
+{
+ loff_t ofs = 0;
+ int die = 0, boundary;
+
+ if (ONENAND_IS_DDP(this) && block >= this->density_mask) {
+ block -= this->density_mask;
+ die = 1;
+ ofs = this->diesize[0];
+ }
+
+ boundary = this->boundary[die];
+ ofs += (loff_t)block << (this->erase_shift - 1);
+ if (block > (boundary + 1))
+ ofs += (loff_t)(block - boundary - 1) << (this->erase_shift - 1);
+ return ofs;
+}
+
+loff_t onenand_addr(struct onenand_chip *this, int block)
+{
+ if (!FLEXONENAND(this))
+ return (loff_t)block << this->erase_shift;
+ return flexonenand_addr(this, block);
+}
+EXPORT_SYMBOL(onenand_addr);
+
+/**
+ * onenand_get_density - [DEFAULT] Get OneNAND density
+ * @param dev_id OneNAND device ID
+ *
+ * Get OneNAND density from device ID
+ */
+static inline int onenand_get_density(int dev_id)
+{
+ int density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
+ return (density & ONENAND_DEVICE_DENSITY_MASK);
+}
+
+/**
+ * flexonenand_region - [Flex-OneNAND] Return erase region of addr
+ * @param mtd MTD device structure
+ * @param addr address whose erase region needs to be identified
+ */
+int flexonenand_region(struct mtd_info *mtd, loff_t addr)
+{
+ int i;
+
+ for (i = 0; i < mtd->numeraseregions; i++)
+ if (addr < mtd->eraseregions[i].offset)
+ break;
+ return i - 1;
+}
+EXPORT_SYMBOL(flexonenand_region);
+
+/**
+ * onenand_command - [DEFAULT] Send command to OneNAND device
+ * @param mtd MTD device structure
+ * @param cmd the command to be sent
+ * @param addr offset to read from or write to
+ * @param len number of bytes to read or write
+ *
+ * Send command to OneNAND device. This function is used for middle/large page
+ * devices (1KB/2KB Bytes per page)
+ */
+static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t len)
+{
+ struct onenand_chip *this = mtd->priv;
+ int value, block, page;
+
+ /* Address translation */
+ switch (cmd) {
+ case ONENAND_CMD_UNLOCK:
+ case ONENAND_CMD_LOCK:
+ case ONENAND_CMD_LOCK_TIGHT:
+ case ONENAND_CMD_UNLOCK_ALL:
+ block = -1;
+ page = -1;
+ break;
+
+ case FLEXONENAND_CMD_PI_ACCESS:
+ /* addr contains die index */
+ block = addr * this->density_mask;
+ page = -1;
+ break;
+
+ case ONENAND_CMD_ERASE:
+ case ONENAND_CMD_MULTIBLOCK_ERASE:
+ case ONENAND_CMD_ERASE_VERIFY:
+ case ONENAND_CMD_BUFFERRAM:
+ case ONENAND_CMD_OTP_ACCESS:
+ block = onenand_block(this, addr);
+ page = -1;
+ break;
+
+ case FLEXONENAND_CMD_READ_PI:
+ cmd = ONENAND_CMD_READ;
+ block = addr * this->density_mask;
+ page = 0;
+ break;
+
+ default:
+ block = onenand_block(this, addr);
+ if (FLEXONENAND(this))
+ page = (int) (addr - onenand_addr(this, block))>>\
+ this->page_shift;
+ else
+ page = (int) (addr >> this->page_shift);
+ if (ONENAND_IS_2PLANE(this)) {
+ /* Make the even block number */
+ block &= ~1;
+ /* Is it the odd plane? */
+ if (addr & this->writesize)
+ block++;
+ page >>= 1;
+ }
+ page &= this->page_mask;
+ break;
+ }
+
+ /* NOTE: The setting order of the registers is very important! */
+ if (cmd == ONENAND_CMD_BUFFERRAM) {
+ /* Select DataRAM for DDP */
+ value = onenand_bufferram_address(this, block);
+ this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
+
+ if (ONENAND_IS_2PLANE(this) || ONENAND_IS_4KB_PAGE(this))
+ /* It is always BufferRAM0 */
+ ONENAND_SET_BUFFERRAM0(this);
+ else
+ /* Switch to the next data buffer */
+ ONENAND_SET_NEXT_BUFFERRAM(this);
+
+ return 0;
+ }
+
+ if (block != -1) {
+ /* Write 'DFS, FBA' of Flash */
+ value = onenand_block_address(this, block);
+ this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
+
+ /* Select DataRAM for DDP */
+ value = onenand_bufferram_address(this, block);
+ this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
+ }
+
+ if (page != -1) {
+ /* Now we use page size operation */
+ int sectors = 0, count = 0;
+ int dataram;
+
+ switch (cmd) {
+ case FLEXONENAND_CMD_RECOVER_LSB:
+ case ONENAND_CMD_READ:
+ case ONENAND_CMD_READOOB:
+ if (ONENAND_IS_4KB_PAGE(this))
+ /* It is always BufferRAM0 */
+ dataram = ONENAND_SET_BUFFERRAM0(this);
+ else
+ dataram = ONENAND_SET_NEXT_BUFFERRAM(this);
+ break;
+
+ default:
+ if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG)
+ cmd = ONENAND_CMD_2X_PROG;
+ dataram = ONENAND_CURRENT_BUFFERRAM(this);
+ break;
+ }
+
+ /* Write 'FPA, FSA' of Flash */
+ value = onenand_page_address(page, sectors);
+ this->write_word(value, this->base + ONENAND_REG_START_ADDRESS8);
+
+ /* Write 'BSA, BSC' of DataRAM */
+ value = onenand_buffer_address(dataram, sectors, count);
+ this->write_word(value, this->base + ONENAND_REG_START_BUFFER);
+ }
+
+ /* Interrupt clear */
+ this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT);
+
+ /* Write command */
+ this->write_word(cmd, this->base + ONENAND_REG_COMMAND);
+
+ return 0;
+}
+
+/**
+ * onenand_read_ecc - return ecc status
+ * @param this onenand chip structure
+ */
+static inline int onenand_read_ecc(struct onenand_chip *this)
+{
+ int ecc, i, result = 0;
+
+ if (!FLEXONENAND(this) && !ONENAND_IS_4KB_PAGE(this))
+ return this->read_word(this->base + ONENAND_REG_ECC_STATUS);
+
+ for (i = 0; i < 4; i++) {
+ ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS + i*2);
+ if (likely(!ecc))
+ continue;
+ if (ecc & FLEXONENAND_UNCORRECTABLE_ERROR)
+ return ONENAND_ECC_2BIT_ALL;
+ else
+ result = ONENAND_ECC_1BIT_ALL;
+ }
+
+ return result;
+}
+
+/**
+ * onenand_wait - [DEFAULT] wait until the command is done
+ * @param mtd MTD device structure
+ * @param state state to select the max. timeout value
+ *
+ * Wait for command done. This applies to all OneNAND command
+ * Read can take up to 30us, erase up to 2ms and program up to 350us
+ * according to general OneNAND specs
+ */
+static int onenand_wait(struct mtd_info *mtd, int state)
+{
+ struct onenand_chip * this = mtd->priv;
+ unsigned long timeout;
+ unsigned int flags = ONENAND_INT_MASTER;
+ unsigned int interrupt = 0;
+ unsigned int ctrl;
+
+ /* The 20 msec is enough */
+ timeout = jiffies + msecs_to_jiffies(20);
+ while (time_before(jiffies, timeout)) {
+ interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
+
+ if (interrupt & flags)
+ break;
+
+ if (state != FL_READING && state != FL_PREPARING_ERASE)
+ cond_resched();
+ }
+ /* To get correct interrupt status in timeout case */
+ interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
+
+ ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
+
+ /*
+ * In the Spec. it checks the controller status first
+ * However if you get the correct information in case of
+ * power off recovery (POR) test, it should read ECC status first
+ */
+ if (interrupt & ONENAND_INT_READ) {
+ int ecc = onenand_read_ecc(this);
+ if (ecc) {
+ if (ecc & ONENAND_ECC_2BIT_ALL) {
+ printk(KERN_ERR "%s: ECC error = 0x%04x\n",
+ __func__, ecc);
+ mtd->ecc_stats.failed++;
+ return -EBADMSG;
+ } else if (ecc & ONENAND_ECC_1BIT_ALL) {
+ printk(KERN_DEBUG "%s: correctable ECC error = 0x%04x\n",
+ __func__, ecc);
+ mtd->ecc_stats.corrected++;
+ }
+ }
+ } else if (state == FL_READING) {
+ printk(KERN_ERR "%s: read timeout! ctrl=0x%04x intr=0x%04x\n",
+ __func__, ctrl, interrupt);
+ return -EIO;
+ }
+
+ if (state == FL_PREPARING_ERASE && !(interrupt & ONENAND_INT_ERASE)) {
+ printk(KERN_ERR "%s: mb erase timeout! ctrl=0x%04x intr=0x%04x\n",
+ __func__, ctrl, interrupt);
+ return -EIO;
+ }
+
+ if (!(interrupt & ONENAND_INT_MASTER)) {
+ printk(KERN_ERR "%s: timeout! ctrl=0x%04x intr=0x%04x\n",
+ __func__, ctrl, interrupt);
+ return -EIO;
+ }
+
+ /* If there's controller error, it's a real error */
+ if (ctrl & ONENAND_CTRL_ERROR) {
+ printk(KERN_ERR "%s: controller error = 0x%04x\n",
+ __func__, ctrl);
+ if (ctrl & ONENAND_CTRL_LOCK)
+ printk(KERN_ERR "%s: it's locked error.\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * onenand_interrupt - [DEFAULT] onenand interrupt handler
+ * @param irq onenand interrupt number
+ * @param dev_id interrupt data
+ *
+ * complete the work
+ */
+static irqreturn_t onenand_interrupt(int irq, void *data)
+{
+ struct onenand_chip *this = data;
+
+ /* To handle shared interrupt */
+ if (!this->complete.done)
+ complete(&this->complete);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * onenand_interrupt_wait - [DEFAULT] wait until the command is done
+ * @param mtd MTD device structure
+ * @param state state to select the max. timeout value
+ *
+ * Wait for command done.
+ */
+static int onenand_interrupt_wait(struct mtd_info *mtd, int state)
+{
+ struct onenand_chip *this = mtd->priv;
+
+ wait_for_completion(&this->complete);
+
+ return onenand_wait(mtd, state);
+}
+
+/*
+ * onenand_try_interrupt_wait - [DEFAULT] try interrupt wait
+ * @param mtd MTD device structure
+ * @param state state to select the max. timeout value
+ *
+ * Try interrupt based wait (It is used one-time)
+ */
+static int onenand_try_interrupt_wait(struct mtd_info *mtd, int state)
+{
+ struct onenand_chip *this = mtd->priv;
+ unsigned long remain, timeout;
+
+ /* We use interrupt wait first */
+ this->wait = onenand_interrupt_wait;
+
+ timeout = msecs_to_jiffies(100);
+ remain = wait_for_completion_timeout(&this->complete, timeout);
+ if (!remain) {
+ printk(KERN_INFO "OneNAND: There's no interrupt. "
+ "We use the normal wait\n");
+
+ /* Release the irq */
+ free_irq(this->irq, this);
+
+ this->wait = onenand_wait;
+ }
+
+ return onenand_wait(mtd, state);
+}
+
+/*
+ * onenand_setup_wait - [OneNAND Interface] setup onenand wait method
+ * @param mtd MTD device structure
+ *
+ * There's two method to wait onenand work
+ * 1. polling - read interrupt status register
+ * 2. interrupt - use the kernel interrupt method
+ */
+static void onenand_setup_wait(struct mtd_info *mtd)
+{
+ struct onenand_chip *this = mtd->priv;
+ int syscfg;
+
+ init_completion(&this->complete);
+
+ if (this->irq <= 0) {
+ this->wait = onenand_wait;
+ return;
+ }
+
+ if (request_irq(this->irq, &onenand_interrupt,
+ IRQF_SHARED, "onenand", this)) {
+ /* If we can't get irq, use the normal wait */
+ this->wait = onenand_wait;
+ return;
+ }
+
+ /* Enable interrupt */
+ syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
+ syscfg |= ONENAND_SYS_CFG1_IOBE;
+ this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);
+
+ this->wait = onenand_try_interrupt_wait;
+}
+
+/**
+ * onenand_bufferram_offset - [DEFAULT] BufferRAM offset
+ * @param mtd MTD data structure
+ * @param area BufferRAM area
+ * @return offset given area
+ *
+ * Return BufferRAM offset given area
+ */
+static inline int onenand_bufferram_offset(struct mtd_info *mtd, int area)
+{
+ struct onenand_chip *this = mtd->priv;
+
+ if (ONENAND_CURRENT_BUFFERRAM(this)) {
+ /* Note: the 'this->writesize' is a real page size */
+ if (area == ONENAND_DATARAM)
+ return this->writesize;
+ if (area == ONENAND_SPARERAM)
+ return mtd->oobsize;
+ }
+
+ return 0;
+}
+
+/**
+ * onenand_read_bufferram - [OneNAND Interface] Read the bufferram area
+ * @param mtd MTD data structure
+ * @param area BufferRAM area
+ * @param buffer the databuffer to put/get data
+ * @param offset offset to read from or write to
+ * @param count number of bytes to read/write
+ *
+ * Read the BufferRAM area
+ */
+static int onenand_read_bufferram(struct mtd_info *mtd, int area,
+ unsigned char *buffer, int offset, size_t count)
+{
+ struct onenand_chip *this = mtd->priv;
+ void __iomem *bufferram;
+
+ bufferram = this->base + area;
+
+ bufferram += onenand_bufferram_offset(mtd, area);
+
+ if (ONENAND_CHECK_BYTE_ACCESS(count)) {
+ unsigned short word;
+
+ /* Align with word(16-bit) size */
+ count--;
+
+ /* Read word and save byte */
+ word = this->read_word(bufferram + offset + count);
+ buffer[count] = (word & 0xff);
+ }
+
+ memcpy(buffer, bufferram + offset, count);
+
+ return 0;
+}
+
+/**
+ * onenand_sync_read_bufferram - [OneNAND Interface] Read the bufferram area with Sync. Burst mode
+ * @param mtd MTD data structure
+ * @param area BufferRAM area
+ * @param buffer the databuffer to put/get data
+ * @param offset offset to read from or write to
+ * @param count number of bytes to read/write
+ *
+ * Read the BufferRAM area with Sync. Burst Mode
+ */
+static int onenand_sync_read_bufferram(struct mtd_info *mtd, int area,
+ unsigned char *buffer, int offset, size_t count)
+{
+ struct onenand_chip *this = mtd->priv;
+ void __iomem *bufferram;
+
+ bufferram = this->base + area;
+
+ bufferram += onenand_bufferram_offset(mtd, area);
+
+ this->mmcontrol(mtd, ONENAND_SYS_CFG1_SYNC_READ);
+
+ if (ONENAND_CHECK_BYTE_ACCESS(count)) {
+ unsigned short word;
+
+ /* Align with word(16-bit) size */
+ count--;
+
+ /* Read word and save byte */
+ word = this->read_word(bufferram + offset + count);
+ buffer[count] = (word & 0xff);
+ }
+
+ memcpy(buffer, bufferram + offset, count);
+
+ this->mmcontrol(mtd, 0);
+
+ return 0;
+}
+
+/**
+ * onenand_write_bufferram - [OneNAND Interface] Write the bufferram area
+ * @param mtd MTD data structure
+ * @param area BufferRAM area
+ * @param buffer the databuffer to put/get data
+ * @param offset offset to read from or write to
+ * @param count number of bytes to read/write
+ *
+ * Write the BufferRAM area
+ */
+static int onenand_write_bufferram(struct mtd_info *mtd, int area,
+ const unsigned char *buffer, int offset, size_t count)
+{
+ struct onenand_chip *this = mtd->priv;
+ void __iomem *bufferram;
+
+ bufferram = this->base + area;
+
+ bufferram += onenand_bufferram_offset(mtd, area);
+
+ if (ONENAND_CHECK_BYTE_ACCESS(count)) {
+ unsigned short word;
+ int byte_offset;
+
+ /* Align with word(16-bit) size */
+ count--;
+
+ /* Calculate byte access offset */
+ byte_offset = offset + count;
+
+ /* Read word and save byte */
+ word = this->read_word(bufferram + byte_offset);
+ word = (word & ~0xff) | buffer[count];
+ this->write_word(word, bufferram + byte_offset);
+ }
+
+ memcpy(bufferram + offset, buffer, count);
+
+ return 0;
+}
+
+/**
+ * onenand_get_2x_blockpage - [GENERIC] Get blockpage at 2x program mode
+ * @param mtd MTD data structure
+ * @param addr address to check
+ * @return blockpage address
+ *
+ * Get blockpage address at 2x program mode
+ */
+static int onenand_get_2x_blockpage(struct mtd_info *mtd, loff_t addr)
+{
+ struct onenand_chip *this = mtd->priv;
+ int blockpage, block, page;
+
+ /* Calculate the even block number */
+ block = (int) (addr >> this->erase_shift) & ~1;
+ /* Is it the odd plane? */
+ if (addr & this->writesize)
+ block++;
+ page = (int) (addr >> (this->page_shift + 1)) & this->page_mask;
+ blockpage = (block << 7) | page;
+
+ return blockpage;
+}
+
+/**
+ * onenand_check_bufferram - [GENERIC] Check BufferRAM information
+ * @param mtd MTD data structure
+ * @param addr address to check
+ * @return 1 if there are valid data, otherwise 0
+ *
+ * Check bufferram if there is data we required
+ */
+static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr)
+{
+ struct onenand_chip *this = mtd->priv;
+ int blockpage, found = 0;
+ unsigned int i;
+
+ if (ONENAND_IS_2PLANE(this))
+ blockpage = onenand_get_2x_blockpage(mtd, addr);
+ else
+ blockpage = (int) (addr >> this->page_shift);
+
+ /* Is there valid data? */
+ i = ONENAND_CURRENT_BUFFERRAM(this);
+ if (this->bufferram[i].blockpage == blockpage)
+ found = 1;
+ else {
+ /* Check another BufferRAM */
+ i = ONENAND_NEXT_BUFFERRAM(this);
+ if (this->bufferram[i].blockpage == blockpage) {
+ ONENAND_SET_NEXT_BUFFERRAM(this);
+ found = 1;
+ }
+ }
+
+ if (found && ONENAND_IS_DDP(this)) {
+ /* Select DataRAM for DDP */
+ int block = onenand_block(this, addr);
+ int value = onenand_bufferram_address(this, block);
+ this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
+ }
+
+ return found;
+}
+
+/**
+ * onenand_update_bufferram - [GENERIC] Update BufferRAM information
+ * @param mtd MTD data structure
+ * @param addr address to update
+ * @param valid valid flag
+ *
+ * Update BufferRAM information
+ */
+static void onenand_update_bufferram(struct mtd_info *mtd, loff_t addr,
+ int valid)
+{
+ struct onenand_chip *this = mtd->priv;
+ int blockpage;
+ unsigned int i;
+
+ if (ONENAND_IS_2PLANE(this))
+ blockpage = onenand_get_2x_blockpage(mtd, addr);
+ else
+ blockpage = (int) (addr >> this->page_shift);
+
+ /* Invalidate another BufferRAM */
+ i = ONENAND_NEXT_BUFFERRAM(this);
+ if (this->bufferram[i].blockpage == blockpage)
+ this->bufferram[i].blockpage = -1;
+
+ /* Update BufferRAM */
+ i = ONENAND_CURRENT_BUFFERRAM(this);
+ if (valid)
+ this->bufferram[i].blockpage = blockpage;
+ else
+ this->bufferram[i].blockpage = -1;
+}
+
+/**
+ * onenand_invalidate_bufferram - [GENERIC] Invalidate BufferRAM information
+ * @param mtd MTD data structure
+ * @param addr start address to invalidate
+ * @param len length to invalidate
+ *
+ * Invalidate BufferRAM information
+ */
+static void onenand_invalidate_bufferram(struct mtd_info *mtd, loff_t addr,
+ unsigned int len)
+{
+ struct onenand_chip *this = mtd->priv;
+ int i;
+ loff_t end_addr = addr + len;
+
+ /* Invalidate BufferRAM */
+ for (i = 0; i < MAX_BUFFERRAM; i++) {
+ loff_t buf_addr = this->bufferram[i].blockpage << this->page_shift;
+ if (buf_addr >= addr && buf_addr < end_addr)
+ this->bufferram[i].blockpage = -1;
+ }
+}
+
+/**
+ * onenand_get_device - [GENERIC] Get chip for selected access
+ * @param mtd MTD device structure
+ * @param new_state the state which is requested
+ *
+ * Get the device and lock it for exclusive access
+ */
+static int onenand_get_device(struct mtd_info *mtd, int new_state)
+{
+ struct onenand_chip *this = mtd->priv;
+ DECLARE_WAITQUEUE(wait, current);
+
+ /*
+ * Grab the lock and see if the device is available
+ */
+ while (1) {
+ spin_lock(&this->chip_lock);
+ if (this->state == FL_READY) {
+ this->state = new_state;
+ spin_unlock(&this->chip_lock);
+ if (new_state != FL_PM_SUSPENDED && this->enable)
+ this->enable(mtd);
+ break;
+ }
+ if (new_state == FL_PM_SUSPENDED) {
+ spin_unlock(&this->chip_lock);
+ return (this->state == FL_PM_SUSPENDED) ? 0 : -EAGAIN;
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ add_wait_queue(&this->wq, &wait);
+ spin_unlock(&this->chip_lock);
+ schedule();
+ remove_wait_queue(&this->wq, &wait);
+ }
+
+ return 0;
+}
+
+/**
+ * onenand_release_device - [GENERIC] release chip
+ * @param mtd MTD device structure
+ *
+ * Deselect, release chip lock and wake up anyone waiting on the device
+ */
+static void onenand_release_device(struct mtd_info *mtd)
+{
+ struct onenand_chip *this = mtd->priv;
+
+ if (this->state != FL_PM_SUSPENDED && this->disable)
+ this->disable(mtd);
+ /* Release the chip */
+ spin_lock(&this->chip_lock);
+ this->state = FL_READY;
+ wake_up(&this->wq);
+ spin_unlock(&this->chip_lock);
+}
+
+/**
+ * onenand_transfer_auto_oob - [INTERN] oob auto-placement transfer
+ * @param mtd MTD device structure
+ * @param buf destination address
+ * @param column oob offset to read from
+ * @param thislen oob length to read
+ */
+static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int column,
+ int thislen)
+{
+ struct onenand_chip *this = mtd->priv;
+ int ret;
+
+ this->read_bufferram(mtd, ONENAND_SPARERAM, this->oob_buf, 0,
+ mtd->oobsize);
+ ret = mtd_ooblayout_get_databytes(mtd, buf, this->oob_buf,
+ column, thislen);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/**
+ * onenand_recover_lsb - [Flex-OneNAND] Recover LSB page data
+ * @param mtd MTD device structure
+ * @param addr address to recover
+ * @param status return value from onenand_wait / onenand_bbt_wait
+ *
+ * MLC NAND Flash cell has paired pages - LSB page and MSB page. LSB page has
+ * lower page address and MSB page has higher page address in paired pages.
+ * If power off occurs during MSB page program, the paired LSB page data can
+ * become corrupt. LSB page recovery read is a way to read LSB page though page
+ * data are corrupted. When uncorrectable error occurs as a result of LSB page
+ * read after power up, issue LSB page recovery read.
+ */
+static int onenand_recover_lsb(struct mtd_info *mtd, loff_t addr, int status)
+{
+ struct onenand_chip *this = mtd->priv;
+ int i;
+
+ /* Recovery is only for Flex-OneNAND */
+ if (!FLEXONENAND(this))
+ return status;
+
+ /* check if we failed due to uncorrectable error */
+ if (!mtd_is_eccerr(status) && status != ONENAND_BBT_READ_ECC_ERROR)
+ return status;
+
+ /* check if address lies in MLC region */
+ i = flexonenand_region(mtd, addr);
+ if (mtd->eraseregions[i].erasesize < (1 << this->erase_shift))
+ return status;
+
+ /* We are attempting to reread, so decrement stats.failed
+ * which was incremented by onenand_wait due to read failure
+ */
+ printk(KERN_INFO "%s: Attempting to recover from uncorrectable read\n",
+ __func__);
+ mtd->ecc_stats.failed--;
+
+ /* Issue the LSB page recovery command */
+ this->command(mtd, FLEXONENAND_CMD_RECOVER_LSB, addr, this->writesize);
+ return this->wait(mtd, FL_READING);
+}
+
+/**
+ * onenand_mlc_read_ops_nolock - MLC OneNAND read main and/or out-of-band
+ * @param mtd MTD device structure
+ * @param from offset to read from
+ * @param ops: oob operation description structure
+ *
+ * MLC OneNAND / Flex-OneNAND has 4KB page size and 4KB dataram.
+ * So, read-while-load is not present.
+ */
+static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct mtd_ecc_stats stats;
+ size_t len = ops->len;
+ size_t ooblen = ops->ooblen;
+ u_char *buf = ops->datbuf;
+ u_char *oobbuf = ops->oobbuf;
+ int read = 0, column, thislen;
+ int oobread = 0, oobcolumn, thisooblen, oobsize;
+ int ret = 0;
+ int writesize = this->writesize;
+
+ pr_debug("%s: from = 0x%08x, len = %i\n", __func__, (unsigned int)from,
+ (int)len);
+
+ oobsize = mtd_oobavail(mtd, ops);
+ oobcolumn = from & (mtd->oobsize - 1);
+
+ /* Do not allow reads past end of device */
+ if (from + len > mtd->size) {
+ printk(KERN_ERR "%s: Attempt read beyond end of device\n",
+ __func__);
+ ops->retlen = 0;
+ ops->oobretlen = 0;
+ return -EINVAL;
+ }
+
+ stats = mtd->ecc_stats;
+
+ while (read < len) {
+ cond_resched();
+
+ thislen = min_t(int, writesize, len - read);
+
+ column = from & (writesize - 1);
+ if (column + thislen > writesize)
+ thislen = writesize - column;
+
+ if (!onenand_check_bufferram(mtd, from)) {
+ this->command(mtd, ONENAND_CMD_READ, from, writesize);
+
+ ret = this->wait(mtd, FL_READING);
+ if (unlikely(ret))
+ ret = onenand_recover_lsb(mtd, from, ret);
+ onenand_update_bufferram(mtd, from, !ret);
+ if (mtd_is_eccerr(ret))
+ ret = 0;
+ if (ret)
+ break;
+ }
+
+ this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen);
+ if (oobbuf) {
+ thisooblen = oobsize - oobcolumn;
+ thisooblen = min_t(int, thisooblen, ooblen - oobread);
+
+ if (ops->mode == MTD_OPS_AUTO_OOB)
+ onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen);
+ else
+ this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen);
+ oobread += thisooblen;
+ oobbuf += thisooblen;
+ oobcolumn = 0;
+ }
+
+ read += thislen;
+ if (read == len)
+ break;
+
+ from += thislen;
+ buf += thislen;
+ }
+
+ /*
+ * Return success, if no ECC failures, else -EBADMSG
+ * fs driver will take care of that, because
+ * retlen == desired len and result == -EBADMSG
+ */
+ ops->retlen = read;
+ ops->oobretlen = oobread;
+
+ if (ret)
+ return ret;
+
+ if (mtd->ecc_stats.failed - stats.failed)
+ return -EBADMSG;
+
+ /* return max bitflips per ecc step; ONENANDs correct 1 bit only */
+ return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0;
+}
+
+/**
+ * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band
+ * @param mtd MTD device structure
+ * @param from offset to read from
+ * @param ops: oob operation description structure
+ *
+ * OneNAND read main and/or out-of-band data
+ */
+static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct mtd_ecc_stats stats;
+ size_t len = ops->len;
+ size_t ooblen = ops->ooblen;
+ u_char *buf = ops->datbuf;
+ u_char *oobbuf = ops->oobbuf;
+ int read = 0, column, thislen;
+ int oobread = 0, oobcolumn, thisooblen, oobsize;
+ int ret = 0, boundary = 0;
+ int writesize = this->writesize;
+
+ pr_debug("%s: from = 0x%08x, len = %i\n", __func__, (unsigned int)from,
+ (int)len);
+
+ oobsize = mtd_oobavail(mtd, ops);
+ oobcolumn = from & (mtd->oobsize - 1);
+
+ /* Do not allow reads past end of device */
+ if ((from + len) > mtd->size) {
+ printk(KERN_ERR "%s: Attempt read beyond end of device\n",
+ __func__);
+ ops->retlen = 0;
+ ops->oobretlen = 0;
+ return -EINVAL;
+ }
+
+ stats = mtd->ecc_stats;
+
+ /* Read-while-load method */
+
+ /* Do first load to bufferRAM */
+ if (read < len) {
+ if (!onenand_check_bufferram(mtd, from)) {
+ this->command(mtd, ONENAND_CMD_READ, from, writesize);
+ ret = this->wait(mtd, FL_READING);
+ onenand_update_bufferram(mtd, from, !ret);
+ if (mtd_is_eccerr(ret))
+ ret = 0;
+ }
+ }
+
+ thislen = min_t(int, writesize, len - read);
+ column = from & (writesize - 1);
+ if (column + thislen > writesize)
+ thislen = writesize - column;
+
+ while (!ret) {
+ /* If there is more to load then start next load */
+ from += thislen;
+ if (read + thislen < len) {
+ this->command(mtd, ONENAND_CMD_READ, from, writesize);
+ /*
+ * Chip boundary handling in DDP
+ * Now we issued chip 1 read and pointed chip 1
+ * bufferram so we have to point chip 0 bufferram.
+ */
+ if (ONENAND_IS_DDP(this) &&
+ unlikely(from == (this->chipsize >> 1))) {
+ this->write_word(ONENAND_DDP_CHIP0, this->base + ONENAND_REG_START_ADDRESS2);
+ boundary = 1;
+ } else
+ boundary = 0;
+ ONENAND_SET_PREV_BUFFERRAM(this);
+ }
+ /* While load is going, read from last bufferRAM */
+ this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen);
+
+ /* Read oob area if needed */
+ if (oobbuf) {
+ thisooblen = oobsize - oobcolumn;
+ thisooblen = min_t(int, thisooblen, ooblen - oobread);
+
+ if (ops->mode == MTD_OPS_AUTO_OOB)
+ onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen);
+ else
+ this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen);
+ oobread += thisooblen;
+ oobbuf += thisooblen;
+ oobcolumn = 0;
+ }
+
+ /* See if we are done */
+ read += thislen;
+ if (read == len)
+ break;
+ /* Set up for next read from bufferRAM */
+ if (unlikely(boundary))
+ this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2);
+ ONENAND_SET_NEXT_BUFFERRAM(this);
+ buf += thislen;
+ thislen = min_t(int, writesize, len - read);
+ column = 0;
+ cond_resched();
+ /* Now wait for load */
+ ret = this->wait(mtd, FL_READING);
+ onenand_update_bufferram(mtd, from, !ret);
+ if (mtd_is_eccerr(ret))
+ ret = 0;
+ }
+
+ /*
+ * Return success, if no ECC failures, else -EBADMSG
+ * fs driver will take care of that, because
+ * retlen == desired len and result == -EBADMSG
+ */
+ ops->retlen = read;
+ ops->oobretlen = oobread;
+
+ if (ret)
+ return ret;
+
+ if (mtd->ecc_stats.failed - stats.failed)
+ return -EBADMSG;
+
+ /* return max bitflips per ecc step; ONENANDs correct 1 bit only */
+ return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0;
+}
+
+/**
+ * onenand_read_oob_nolock - [MTD Interface] OneNAND read out-of-band
+ * @param mtd MTD device structure
+ * @param from offset to read from
+ * @param ops: oob operation description structure
+ *
+ * OneNAND read out-of-band data from the spare area
+ */
+static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct mtd_ecc_stats stats;
+ int read = 0, thislen, column, oobsize;
+ size_t len = ops->ooblen;
+ unsigned int mode = ops->mode;
+ u_char *buf = ops->oobbuf;
+ int ret = 0, readcmd;
+
+ from += ops->ooboffs;
+
+ pr_debug("%s: from = 0x%08x, len = %i\n", __func__, (unsigned int)from,
+ (int)len);
+
+ /* Initialize return length value */
+ ops->oobretlen = 0;
+
+ if (mode == MTD_OPS_AUTO_OOB)
+ oobsize = mtd->oobavail;
+ else
+ oobsize = mtd->oobsize;
+
+ column = from & (mtd->oobsize - 1);
+
+ if (unlikely(column >= oobsize)) {
+ printk(KERN_ERR "%s: Attempted to start read outside oob\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ stats = mtd->ecc_stats;
+
+ readcmd = ONENAND_IS_4KB_PAGE(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB;
+
+ while (read < len) {
+ cond_resched();
+
+ thislen = oobsize - column;
+ thislen = min_t(int, thislen, len);
+
+ this->command(mtd, readcmd, from, mtd->oobsize);
+
+ onenand_update_bufferram(mtd, from, 0);
+
+ ret = this->wait(mtd, FL_READING);
+ if (unlikely(ret))
+ ret = onenand_recover_lsb(mtd, from, ret);
+
+ if (ret && !mtd_is_eccerr(ret)) {
+ printk(KERN_ERR "%s: read failed = 0x%x\n",
+ __func__, ret);
+ break;
+ }
+
+ if (mode == MTD_OPS_AUTO_OOB)
+ onenand_transfer_auto_oob(mtd, buf, column, thislen);
+ else
+ this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
+
+ read += thislen;
+
+ if (read == len)
+ break;
+
+ buf += thislen;
+
+ /* Read more? */
+ if (read < len) {
+ /* Page size */
+ from += mtd->writesize;
+ column = 0;
+ }
+ }
+
+ ops->oobretlen = read;
+
+ if (ret)
+ return ret;
+
+ if (mtd->ecc_stats.failed - stats.failed)
+ return -EBADMSG;
+
+ return 0;
+}
+
+/**
+ * onenand_read_oob - [MTD Interface] Read main and/or out-of-band
+ * @param mtd: MTD device structure
+ * @param from: offset to read from
+ * @param ops: oob operation description structure
+
+ * Read main and/or out-of-band
+ */
+static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops)
+{
+ struct onenand_chip *this = mtd->priv;
+ int ret;
+
+ switch (ops->mode) {
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_AUTO_OOB:
+ break;
+ case MTD_OPS_RAW:
+ /* Not implemented yet */
+ default:
+ return -EINVAL;
+ }
+
+ onenand_get_device(mtd, FL_READING);
+ if (ops->datbuf)
+ ret = ONENAND_IS_4KB_PAGE(this) ?
+ onenand_mlc_read_ops_nolock(mtd, from, ops) :
+ onenand_read_ops_nolock(mtd, from, ops);
+ else
+ ret = onenand_read_oob_nolock(mtd, from, ops);
+ onenand_release_device(mtd);
+
+ return ret;
+}
+
+/**
+ * onenand_bbt_wait - [DEFAULT] wait until the command is done
+ * @param mtd MTD device structure
+ * @param state state to select the max. timeout value
+ *
+ * Wait for command done.
+ */
+static int onenand_bbt_wait(struct mtd_info *mtd, int state)
+{
+ struct onenand_chip *this = mtd->priv;
+ unsigned long timeout;
+ unsigned int interrupt, ctrl, ecc, addr1, addr8;
+
+ /* The 20 msec is enough */
+ timeout = jiffies + msecs_to_jiffies(20);
+ while (time_before(jiffies, timeout)) {
+ interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
+ if (interrupt & ONENAND_INT_MASTER)
+ break;
+ }
+ /* To get correct interrupt status in timeout case */
+ interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
+ ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
+ addr1 = this->read_word(this->base + ONENAND_REG_START_ADDRESS1);
+ addr8 = this->read_word(this->base + ONENAND_REG_START_ADDRESS8);
+
+ if (interrupt & ONENAND_INT_READ) {
+ ecc = onenand_read_ecc(this);
+ if (ecc & ONENAND_ECC_2BIT_ALL) {
+ printk(KERN_DEBUG "%s: ecc 0x%04x ctrl 0x%04x "
+ "intr 0x%04x addr1 %#x addr8 %#x\n",
+ __func__, ecc, ctrl, interrupt, addr1, addr8);
+ return ONENAND_BBT_READ_ECC_ERROR;
+ }
+ } else {
+ printk(KERN_ERR "%s: read timeout! ctrl 0x%04x "
+ "intr 0x%04x addr1 %#x addr8 %#x\n",
+ __func__, ctrl, interrupt, addr1, addr8);
+ return ONENAND_BBT_READ_FATAL_ERROR;
+ }
+
+ /* Initial bad block case: 0x2400 or 0x0400 */
+ if (ctrl & ONENAND_CTRL_ERROR) {
+ printk(KERN_DEBUG "%s: ctrl 0x%04x intr 0x%04x addr1 %#x "
+ "addr8 %#x\n", __func__, ctrl, interrupt, addr1, addr8);
+ return ONENAND_BBT_READ_ERROR;
+ }
+
+ return 0;
+}
+
+/**
+ * onenand_bbt_read_oob - [MTD Interface] OneNAND read out-of-band for bbt scan
+ * @param mtd MTD device structure
+ * @param from offset to read from
+ * @param ops oob operation description structure
+ *
+ * OneNAND read out-of-band data from the spare area for bbt scan
+ */
+int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops)
+{
+ struct onenand_chip *this = mtd->priv;
+ int read = 0, thislen, column;
+ int ret = 0, readcmd;
+ size_t len = ops->ooblen;
+ u_char *buf = ops->oobbuf;
+
+ pr_debug("%s: from = 0x%08x, len = %zi\n", __func__, (unsigned int)from,
+ len);
+
+ /* Initialize return value */
+ ops->oobretlen = 0;
+
+ /* Do not allow reads past end of device */
+ if (unlikely((from + len) > mtd->size)) {
+ printk(KERN_ERR "%s: Attempt read beyond end of device\n",
+ __func__);
+ return ONENAND_BBT_READ_FATAL_ERROR;
+ }
+
+ /* Grab the lock and see if the device is available */
+ onenand_get_device(mtd, FL_READING);
+
+ column = from & (mtd->oobsize - 1);
+
+ readcmd = ONENAND_IS_4KB_PAGE(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB;
+
+ while (read < len) {
+ cond_resched();
+
+ thislen = mtd->oobsize - column;
+ thislen = min_t(int, thislen, len);
+
+ this->command(mtd, readcmd, from, mtd->oobsize);
+
+ onenand_update_bufferram(mtd, from, 0);
+
+ ret = this->bbt_wait(mtd, FL_READING);
+ if (unlikely(ret))
+ ret = onenand_recover_lsb(mtd, from, ret);
+
+ if (ret)
+ break;
+
+ this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
+ read += thislen;
+ if (read == len)
+ break;
+
+ buf += thislen;
+
+ /* Read more? */
+ if (read < len) {
+ /* Update Page size */
+ from += this->writesize;
+ column = 0;
+ }
+ }
+
+ /* Deselect and wake up anyone waiting on the device */
+ onenand_release_device(mtd);
+
+ ops->oobretlen = read;
+ return ret;
+}
+
+#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
+/**
+ * onenand_verify_oob - [GENERIC] verify the oob contents after a write
+ * @param mtd MTD device structure
+ * @param buf the databuffer to verify
+ * @param to offset to read from
+ */
+static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to)
+{
+ struct onenand_chip *this = mtd->priv;
+ u_char *oob_buf = this->oob_buf;
+ int status, i, readcmd;
+
+ readcmd = ONENAND_IS_4KB_PAGE(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB;
+
+ this->command(mtd, readcmd, to, mtd->oobsize);
+ onenand_update_bufferram(mtd, to, 0);
+ status = this->wait(mtd, FL_READING);
+ if (status)
+ return status;
+
+ this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize);
+ for (i = 0; i < mtd->oobsize; i++)
+ if (buf[i] != 0xFF && buf[i] != oob_buf[i])
+ return -EBADMSG;
+
+ return 0;
+}
+
+/**
+ * onenand_verify - [GENERIC] verify the chip contents after a write
+ * @param mtd MTD device structure
+ * @param buf the databuffer to verify
+ * @param addr offset to read from
+ * @param len number of bytes to read and compare
+ */
+static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len)
+{
+ struct onenand_chip *this = mtd->priv;
+ int ret = 0;
+ int thislen, column;
+
+ column = addr & (this->writesize - 1);
+
+ while (len != 0) {
+ thislen = min_t(int, this->writesize - column, len);
+
+ this->command(mtd, ONENAND_CMD_READ, addr, this->writesize);
+
+ onenand_update_bufferram(mtd, addr, 0);
+
+ ret = this->wait(mtd, FL_READING);
+ if (ret)
+ return ret;
+
+ onenand_update_bufferram(mtd, addr, 1);
+
+ this->read_bufferram(mtd, ONENAND_DATARAM, this->verify_buf, 0, mtd->writesize);
+
+ if (memcmp(buf, this->verify_buf + column, thislen))
+ return -EBADMSG;
+
+ len -= thislen;
+ buf += thislen;
+ addr += thislen;
+ column = 0;
+ }
+
+ return 0;
+}
+#else
+#define onenand_verify(...) (0)
+#define onenand_verify_oob(...) (0)
+#endif
+
+#define NOTALIGNED(x) ((x & (this->subpagesize - 1)) != 0)
+
+static void onenand_panic_wait(struct mtd_info *mtd)
+{
+ struct onenand_chip *this = mtd->priv;
+ unsigned int interrupt;
+ int i;
+
+ for (i = 0; i < 2000; i++) {
+ interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
+ if (interrupt & ONENAND_INT_MASTER)
+ break;
+ udelay(10);
+ }
+}
+
+/**
+ * onenand_panic_write - [MTD Interface] write buffer to FLASH in a panic context
+ * @param mtd MTD device structure
+ * @param to offset to write to
+ * @param len number of bytes to write
+ * @param retlen pointer to variable to store the number of written bytes
+ * @param buf the data to write
+ *
+ * Write with ECC
+ */
+static int onenand_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ struct onenand_chip *this = mtd->priv;
+ int column, subpage;
+ int written = 0;
+
+ if (this->state == FL_PM_SUSPENDED)
+ return -EBUSY;
+
+ /* Wait for any existing operation to clear */
+ onenand_panic_wait(mtd);
+
+ pr_debug("%s: to = 0x%08x, len = %i\n", __func__, (unsigned int)to,
+ (int)len);
+
+ /* Reject writes, which are not page aligned */
+ if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) {
+ printk(KERN_ERR "%s: Attempt to write not page aligned data\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ column = to & (mtd->writesize - 1);
+
+ /* Loop until all data write */
+ while (written < len) {
+ int thislen = min_t(int, mtd->writesize - column, len - written);
+ u_char *wbuf = (u_char *) buf;
+
+ this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen);
+
+ /* Partial page write */
+ subpage = thislen < mtd->writesize;
+ if (subpage) {
+ memset(this->page_buf, 0xff, mtd->writesize);
+ memcpy(this->page_buf + column, buf, thislen);
+ wbuf = this->page_buf;
+ }
+
+ this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize);
+ this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize);
+
+ this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);
+
+ onenand_panic_wait(mtd);
+
+ /* In partial page write we don't update bufferram */
+ onenand_update_bufferram(mtd, to, !subpage);
+ if (ONENAND_IS_2PLANE(this)) {
+ ONENAND_SET_BUFFERRAM1(this);
+ onenand_update_bufferram(mtd, to + this->writesize, !subpage);
+ }
+
+ written += thislen;
+
+ if (written == len)
+ break;
+
+ column = 0;
+ to += thislen;
+ buf += thislen;
+ }
+
+ *retlen = written;
+ return 0;
+}
+
+/**
+ * onenand_fill_auto_oob - [INTERN] oob auto-placement transfer
+ * @param mtd MTD device structure
+ * @param oob_buf oob buffer
+ * @param buf source address
+ * @param column oob offset to write to
+ * @param thislen oob length to write
+ */
+static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
+ const u_char *buf, int column, int thislen)
+{
+ return mtd_ooblayout_set_databytes(mtd, buf, oob_buf, column, thislen);
+}
+
+/**
+ * onenand_write_ops_nolock - [OneNAND Interface] write main and/or out-of-band
+ * @param mtd MTD device structure
+ * @param to offset to write to
+ * @param ops oob operation description structure
+ *
+ * Write main and/or oob with ECC
+ */
+static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+{
+ struct onenand_chip *this = mtd->priv;
+ int written = 0, column, thislen = 0, subpage = 0;
+ int prev = 0, prevlen = 0, prev_subpage = 0, first = 1;
+ int oobwritten = 0, oobcolumn, thisooblen, oobsize;
+ size_t len = ops->len;
+ size_t ooblen = ops->ooblen;
+ const u_char *buf = ops->datbuf;
+ const u_char *oob = ops->oobbuf;
+ u_char *oobbuf;
+ int ret = 0, cmd;
+
+ pr_debug("%s: to = 0x%08x, len = %i\n", __func__, (unsigned int)to,
+ (int)len);
+
+ /* Initialize retlen, in case of early exit */
+ ops->retlen = 0;
+ ops->oobretlen = 0;
+
+ /* Reject writes, which are not page aligned */
+ if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) {
+ printk(KERN_ERR "%s: Attempt to write not page aligned data\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ /* Check zero length */
+ if (!len)
+ return 0;
+ oobsize = mtd_oobavail(mtd, ops);
+ oobcolumn = to & (mtd->oobsize - 1);
+
+ column = to & (mtd->writesize - 1);
+
+ /* Loop until all data write */
+ while (1) {
+ if (written < len) {
+ u_char *wbuf = (u_char *) buf;
+
+ thislen = min_t(int, mtd->writesize - column, len - written);
+ thisooblen = min_t(int, oobsize - oobcolumn, ooblen - oobwritten);
+
+ cond_resched();
+
+ this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen);
+
+ /* Partial page write */
+ subpage = thislen < mtd->writesize;
+ if (subpage) {
+ memset(this->page_buf, 0xff, mtd->writesize);
+ memcpy(this->page_buf + column, buf, thislen);
+ wbuf = this->page_buf;
+ }
+
+ this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize);
+
+ if (oob) {
+ oobbuf = this->oob_buf;
+
+ /* We send data to spare ram with oobsize
+ * to prevent byte access */
+ memset(oobbuf, 0xff, mtd->oobsize);
+ if (ops->mode == MTD_OPS_AUTO_OOB)
+ onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen);
+ else
+ memcpy(oobbuf + oobcolumn, oob, thisooblen);
+
+ oobwritten += thisooblen;
+ oob += thisooblen;
+ oobcolumn = 0;
+ } else
+ oobbuf = (u_char *) ffchars;
+
+ this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
+ } else
+ ONENAND_SET_NEXT_BUFFERRAM(this);
+
+ /*
+ * 2 PLANE, MLC, and Flex-OneNAND do not support
+ * write-while-program feature.
+ */
+ if (!ONENAND_IS_2PLANE(this) && !ONENAND_IS_4KB_PAGE(this) && !first) {
+ ONENAND_SET_PREV_BUFFERRAM(this);
+
+ ret = this->wait(mtd, FL_WRITING);
+
+ /* In partial page write we don't update bufferram */
+ onenand_update_bufferram(mtd, prev, !ret && !prev_subpage);
+ if (ret) {
+ written -= prevlen;
+ printk(KERN_ERR "%s: write failed %d\n",
+ __func__, ret);
+ break;
+ }
+
+ if (written == len) {
+ /* Only check verify write turn on */
+ ret = onenand_verify(mtd, buf - len, to - len, len);
+ if (ret)
+ printk(KERN_ERR "%s: verify failed %d\n",
+ __func__, ret);
+ break;
+ }
+
+ ONENAND_SET_NEXT_BUFFERRAM(this);
+ }
+
+ this->ongoing = 0;
+ cmd = ONENAND_CMD_PROG;
+
+ /* Exclude 1st OTP and OTP blocks for cache program feature */
+ if (ONENAND_IS_CACHE_PROGRAM(this) &&
+ likely(onenand_block(this, to) != 0) &&
+ ONENAND_IS_4KB_PAGE(this) &&
+ ((written + thislen) < len)) {
+ cmd = ONENAND_CMD_2X_CACHE_PROG;
+ this->ongoing = 1;
+ }
+
+ this->command(mtd, cmd, to, mtd->writesize);
+
+ /*
+ * 2 PLANE, MLC, and Flex-OneNAND wait here
+ */
+ if (ONENAND_IS_2PLANE(this) || ONENAND_IS_4KB_PAGE(this)) {
+ ret = this->wait(mtd, FL_WRITING);
+
+ /* In partial page write we don't update bufferram */
+ onenand_update_bufferram(mtd, to, !ret && !subpage);
+ if (ret) {
+ printk(KERN_ERR "%s: write failed %d\n",
+ __func__, ret);
+ break;
+ }
+
+ /* Only check verify write turn on */
+ ret = onenand_verify(mtd, buf, to, thislen);
+ if (ret) {
+ printk(KERN_ERR "%s: verify failed %d\n",
+ __func__, ret);
+ break;
+ }
+
+ written += thislen;
+
+ if (written == len)
+ break;
+
+ } else
+ written += thislen;
+
+ column = 0;
+ prev_subpage = subpage;
+ prev = to;
+ prevlen = thislen;
+ to += thislen;
+ buf += thislen;
+ first = 0;
+ }
+
+ /* In error case, clear all bufferrams */
+ if (written != len)
+ onenand_invalidate_bufferram(mtd, 0, -1);
+
+ ops->retlen = written;
+ ops->oobretlen = oobwritten;
+
+ return ret;
+}
+
+
+/**
+ * onenand_write_oob_nolock - [INTERN] OneNAND write out-of-band
+ * @param mtd MTD device structure
+ * @param to offset to write to
+ * @param len number of bytes to write
+ * @param retlen pointer to variable to store the number of written bytes
+ * @param buf the data to write
+ * @param mode operation mode
+ *
+ * OneNAND write out-of-band
+ */
+static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+{
+ struct onenand_chip *this = mtd->priv;
+ int column, ret = 0, oobsize;
+ int written = 0, oobcmd;
+ u_char *oobbuf;
+ size_t len = ops->ooblen;
+ const u_char *buf = ops->oobbuf;
+ unsigned int mode = ops->mode;
+
+ to += ops->ooboffs;
+
+ pr_debug("%s: to = 0x%08x, len = %i\n", __func__, (unsigned int)to,
+ (int)len);
+
+ /* Initialize retlen, in case of early exit */
+ ops->oobretlen = 0;
+
+ if (mode == MTD_OPS_AUTO_OOB)
+ oobsize = mtd->oobavail;
+ else
+ oobsize = mtd->oobsize;
+
+ column = to & (mtd->oobsize - 1);
+
+ if (unlikely(column >= oobsize)) {
+ printk(KERN_ERR "%s: Attempted to start write outside oob\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ /* For compatibility with NAND: Do not allow write past end of page */
+ if (unlikely(column + len > oobsize)) {
+ printk(KERN_ERR "%s: Attempt to write past end of page\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ oobbuf = this->oob_buf;
+
+ oobcmd = ONENAND_IS_4KB_PAGE(this) ? ONENAND_CMD_PROG : ONENAND_CMD_PROGOOB;
+
+ /* Loop until all data write */
+ while (written < len) {
+ int thislen = min_t(int, oobsize, len - written);
+
+ cond_resched();
+
+ this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize);
+
+ /* We send data to spare ram with oobsize
+ * to prevent byte access */
+ memset(oobbuf, 0xff, mtd->oobsize);
+ if (mode == MTD_OPS_AUTO_OOB)
+ onenand_fill_auto_oob(mtd, oobbuf, buf, column, thislen);
+ else
+ memcpy(oobbuf + column, buf, thislen);
+ this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
+
+ if (ONENAND_IS_4KB_PAGE(this)) {
+ /* Set main area of DataRAM to 0xff*/
+ memset(this->page_buf, 0xff, mtd->writesize);
+ this->write_bufferram(mtd, ONENAND_DATARAM,
+ this->page_buf, 0, mtd->writesize);
+ }
+
+ this->command(mtd, oobcmd, to, mtd->oobsize);
+
+ onenand_update_bufferram(mtd, to, 0);
+ if (ONENAND_IS_2PLANE(this)) {
+ ONENAND_SET_BUFFERRAM1(this);
+ onenand_update_bufferram(mtd, to + this->writesize, 0);
+ }
+
+ ret = this->wait(mtd, FL_WRITING);
+ if (ret) {
+ printk(KERN_ERR "%s: write failed %d\n", __func__, ret);
+ break;
+ }
+
+ ret = onenand_verify_oob(mtd, oobbuf, to);
+ if (ret) {
+ printk(KERN_ERR "%s: verify failed %d\n",
+ __func__, ret);
+ break;
+ }
+
+ written += thislen;
+ if (written == len)
+ break;
+
+ to += mtd->writesize;
+ buf += thislen;
+ column = 0;
+ }
+
+ ops->oobretlen = written;
+
+ return ret;
+}
+
+/**
+ * onenand_write_oob - [MTD Interface] NAND write data and/or out-of-band
+ * @param mtd: MTD device structure
+ * @param to: offset to write
+ * @param ops: oob operation description structure
+ */
+static int onenand_write_oob(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+{
+ int ret;
+
+ switch (ops->mode) {
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_AUTO_OOB:
+ break;
+ case MTD_OPS_RAW:
+ /* Not implemented yet */
+ default:
+ return -EINVAL;
+ }
+
+ onenand_get_device(mtd, FL_WRITING);
+ if (ops->datbuf)
+ ret = onenand_write_ops_nolock(mtd, to, ops);
+ else
+ ret = onenand_write_oob_nolock(mtd, to, ops);
+ onenand_release_device(mtd);
+
+ return ret;
+}
+
+/**
+ * onenand_block_isbad_nolock - [GENERIC] Check if a block is marked bad
+ * @param mtd MTD device structure
+ * @param ofs offset from device start
+ * @param allowbbt 1, if its allowed to access the bbt area
+ *
+ * Check, if the block is bad. Either by reading the bad block table or
+ * calling of the scan function.
+ */
+static int onenand_block_isbad_nolock(struct mtd_info *mtd, loff_t ofs, int allowbbt)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct bbm_info *bbm = this->bbm;
+
+ /* Return info from the table */
+ return bbm->isbad_bbt(mtd, ofs, allowbbt);
+}
+
+
+static int onenand_multiblock_erase_verify(struct mtd_info *mtd,
+ struct erase_info *instr)
+{
+ struct onenand_chip *this = mtd->priv;
+ loff_t addr = instr->addr;
+ int len = instr->len;
+ unsigned int block_size = (1 << this->erase_shift);
+ int ret = 0;
+
+ while (len) {
+ this->command(mtd, ONENAND_CMD_ERASE_VERIFY, addr, block_size);
+ ret = this->wait(mtd, FL_VERIFYING_ERASE);
+ if (ret) {
+ printk(KERN_ERR "%s: Failed verify, block %d\n",
+ __func__, onenand_block(this, addr));
+ instr->fail_addr = addr;
+ return -1;
+ }
+ len -= block_size;
+ addr += block_size;
+ }
+ return 0;
+}
+
+/**
+ * onenand_multiblock_erase - [INTERN] erase block(s) using multiblock erase
+ * @param mtd MTD device structure
+ * @param instr erase instruction
+ * @param region erase region
+ *
+ * Erase one or more blocks up to 64 block at a time
+ */
+static int onenand_multiblock_erase(struct mtd_info *mtd,
+ struct erase_info *instr,
+ unsigned int block_size)
+{
+ struct onenand_chip *this = mtd->priv;
+ loff_t addr = instr->addr;
+ int len = instr->len;
+ int eb_count = 0;
+ int ret = 0;
+ int bdry_block = 0;
+
+ if (ONENAND_IS_DDP(this)) {
+ loff_t bdry_addr = this->chipsize >> 1;
+ if (addr < bdry_addr && (addr + len) > bdry_addr)
+ bdry_block = bdry_addr >> this->erase_shift;
+ }
+
+ /* Pre-check bbs */
+ while (len) {
+ /* Check if we have a bad block, we do not erase bad blocks */
+ if (onenand_block_isbad_nolock(mtd, addr, 0)) {
+ printk(KERN_WARNING "%s: attempt to erase a bad block "
+ "at addr 0x%012llx\n",
+ __func__, (unsigned long long) addr);
+ return -EIO;
+ }
+ len -= block_size;
+ addr += block_size;
+ }
+
+ len = instr->len;
+ addr = instr->addr;
+
+ /* loop over 64 eb batches */
+ while (len) {
+ struct erase_info verify_instr = *instr;
+ int max_eb_count = MB_ERASE_MAX_BLK_COUNT;
+
+ verify_instr.addr = addr;
+ verify_instr.len = 0;
+
+ /* do not cross chip boundary */
+ if (bdry_block) {
+ int this_block = (addr >> this->erase_shift);
+
+ if (this_block < bdry_block) {
+ max_eb_count = min(max_eb_count,
+ (bdry_block - this_block));
+ }
+ }
+
+ eb_count = 0;
+
+ while (len > block_size && eb_count < (max_eb_count - 1)) {
+ this->command(mtd, ONENAND_CMD_MULTIBLOCK_ERASE,
+ addr, block_size);
+ onenand_invalidate_bufferram(mtd, addr, block_size);
+
+ ret = this->wait(mtd, FL_PREPARING_ERASE);
+ if (ret) {
+ printk(KERN_ERR "%s: Failed multiblock erase, "
+ "block %d\n", __func__,
+ onenand_block(this, addr));
+ instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
+ return -EIO;
+ }
+
+ len -= block_size;
+ addr += block_size;
+ eb_count++;
+ }
+
+ /* last block of 64-eb series */
+ cond_resched();
+ this->command(mtd, ONENAND_CMD_ERASE, addr, block_size);
+ onenand_invalidate_bufferram(mtd, addr, block_size);
+
+ ret = this->wait(mtd, FL_ERASING);
+ /* Check if it is write protected */
+ if (ret) {
+ printk(KERN_ERR "%s: Failed erase, block %d\n",
+ __func__, onenand_block(this, addr));
+ instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
+ return -EIO;
+ }
+
+ len -= block_size;
+ addr += block_size;
+ eb_count++;
+
+ /* verify */
+ verify_instr.len = eb_count * block_size;
+ if (onenand_multiblock_erase_verify(mtd, &verify_instr)) {
+ instr->fail_addr = verify_instr.fail_addr;
+ return -EIO;
+ }
+
+ }
+ return 0;
+}
+
+
+/**
+ * onenand_block_by_block_erase - [INTERN] erase block(s) using regular erase
+ * @param mtd MTD device structure
+ * @param instr erase instruction
+ * @param region erase region
+ * @param block_size erase block size
+ *
+ * Erase one or more blocks one block at a time
+ */
+static int onenand_block_by_block_erase(struct mtd_info *mtd,
+ struct erase_info *instr,
+ struct mtd_erase_region_info *region,
+ unsigned int block_size)
+{
+ struct onenand_chip *this = mtd->priv;
+ loff_t addr = instr->addr;
+ int len = instr->len;
+ loff_t region_end = 0;
+ int ret = 0;
+
+ if (region) {
+ /* region is set for Flex-OneNAND */
+ region_end = region->offset + region->erasesize * region->numblocks;
+ }
+
+ /* Loop through the blocks */
+ while (len) {
+ cond_resched();
+
+ /* Check if we have a bad block, we do not erase bad blocks */
+ if (onenand_block_isbad_nolock(mtd, addr, 0)) {
+ printk(KERN_WARNING "%s: attempt to erase a bad block "
+ "at addr 0x%012llx\n",
+ __func__, (unsigned long long) addr);
+ return -EIO;
+ }
+
+ this->command(mtd, ONENAND_CMD_ERASE, addr, block_size);
+
+ onenand_invalidate_bufferram(mtd, addr, block_size);
+
+ ret = this->wait(mtd, FL_ERASING);
+ /* Check, if it is write protected */
+ if (ret) {
+ printk(KERN_ERR "%s: Failed erase, block %d\n",
+ __func__, onenand_block(this, addr));
+ instr->fail_addr = addr;
+ return -EIO;
+ }
+
+ len -= block_size;
+ addr += block_size;
+
+ if (region && addr == region_end) {
+ if (!len)
+ break;
+ region++;
+
+ block_size = region->erasesize;
+ region_end = region->offset + region->erasesize * region->numblocks;
+
+ if (len & (block_size - 1)) {
+ /* FIXME: This should be handled at MTD partitioning level. */
+ printk(KERN_ERR "%s: Unaligned address\n",
+ __func__);
+ return -EIO;
+ }
+ }
+ }
+ return 0;
+}
+
+/**
+ * onenand_erase - [MTD Interface] erase block(s)
+ * @param mtd MTD device structure
+ * @param instr erase instruction
+ *
+ * Erase one or more blocks
+ */
+static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+ struct onenand_chip *this = mtd->priv;
+ unsigned int block_size;
+ loff_t addr = instr->addr;
+ loff_t len = instr->len;
+ int ret = 0;
+ struct mtd_erase_region_info *region = NULL;
+ loff_t region_offset = 0;
+
+ pr_debug("%s: start=0x%012llx, len=%llu\n", __func__,
+ (unsigned long long)instr->addr,
+ (unsigned long long)instr->len);
+
+ if (FLEXONENAND(this)) {
+ /* Find the eraseregion of this address */
+ int i = flexonenand_region(mtd, addr);
+
+ region = &mtd->eraseregions[i];
+ block_size = region->erasesize;
+
+ /* Start address within region must align on block boundary.
+ * Erase region's start offset is always block start address.
+ */
+ region_offset = region->offset;
+ } else
+ block_size = 1 << this->erase_shift;
+
+ /* Start address must align on block boundary */
+ if (unlikely((addr - region_offset) & (block_size - 1))) {
+ printk(KERN_ERR "%s: Unaligned address\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Length must align on block boundary */
+ if (unlikely(len & (block_size - 1))) {
+ printk(KERN_ERR "%s: Length not block aligned\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Grab the lock and see if the device is available */
+ onenand_get_device(mtd, FL_ERASING);
+
+ if (ONENAND_IS_4KB_PAGE(this) || region ||
+ instr->len < MB_ERASE_MIN_BLK_COUNT * block_size) {
+ /* region is set for Flex-OneNAND (no mb erase) */
+ ret = onenand_block_by_block_erase(mtd, instr,
+ region, block_size);
+ } else {
+ ret = onenand_multiblock_erase(mtd, instr, block_size);
+ }
+
+ /* Deselect and wake up anyone waiting on the device */
+ onenand_release_device(mtd);
+
+ return ret;
+}
+
+/**
+ * onenand_sync - [MTD Interface] sync
+ * @param mtd MTD device structure
+ *
+ * Sync is actually a wait for chip ready function
+ */
+static void onenand_sync(struct mtd_info *mtd)
+{
+ pr_debug("%s: called\n", __func__);
+
+ /* Grab the lock and see if the device is available */
+ onenand_get_device(mtd, FL_SYNCING);
+
+ /* Release it and go back */
+ onenand_release_device(mtd);
+}
+
+/**
+ * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad
+ * @param mtd MTD device structure
+ * @param ofs offset relative to mtd start
+ *
+ * Check whether the block is bad
+ */
+static int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs)
+{
+ int ret;
+
+ onenand_get_device(mtd, FL_READING);
+ ret = onenand_block_isbad_nolock(mtd, ofs, 0);
+ onenand_release_device(mtd);
+ return ret;
+}
+
+/**
+ * onenand_default_block_markbad - [DEFAULT] mark a block bad
+ * @param mtd MTD device structure
+ * @param ofs offset from device start
+ *
+ * This is the default implementation, which can be overridden by
+ * a hardware specific driver.
+ */
+static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct bbm_info *bbm = this->bbm;
+ u_char buf[2] = {0, 0};
+ struct mtd_oob_ops ops = {
+ .mode = MTD_OPS_PLACE_OOB,
+ .ooblen = 2,
+ .oobbuf = buf,
+ .ooboffs = 0,
+ };
+ int block;
+
+ /* Get block number */
+ block = onenand_block(this, ofs);
+ if (bbm->bbt)
+ bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
+
+ /* We write two bytes, so we don't have to mess with 16-bit access */
+ ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
+ /* FIXME : What to do when marking SLC block in partition
+ * with MLC erasesize? For now, it is not advisable to
+ * create partitions containing both SLC and MLC regions.
+ */
+ return onenand_write_oob_nolock(mtd, ofs, &ops);
+}
+
+/**
+ * onenand_block_markbad - [MTD Interface] Mark the block at the given offset as bad
+ * @param mtd MTD device structure
+ * @param ofs offset relative to mtd start
+ *
+ * Mark the block as bad
+ */
+static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ struct onenand_chip *this = mtd->priv;
+ int ret;
+
+ ret = onenand_block_isbad(mtd, ofs);
+ if (ret) {
+ /* If it was bad already, return success and do nothing */
+ if (ret > 0)
+ return 0;
+ return ret;
+ }
+
+ onenand_get_device(mtd, FL_WRITING);
+ ret = this->block_markbad(mtd, ofs);
+ onenand_release_device(mtd);
+ return ret;
+}
+
+/**
+ * onenand_do_lock_cmd - [OneNAND Interface] Lock or unlock block(s)
+ * @param mtd MTD device structure
+ * @param ofs offset relative to mtd start
+ * @param len number of bytes to lock or unlock
+ * @param cmd lock or unlock command
+ *
+ * Lock or unlock one or more blocks
+ */
+static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int cmd)
+{
+ struct onenand_chip *this = mtd->priv;
+ int start, end, block, value, status;
+ int wp_status_mask;
+
+ start = onenand_block(this, ofs);
+ end = onenand_block(this, ofs + len) - 1;
+
+ if (cmd == ONENAND_CMD_LOCK)
+ wp_status_mask = ONENAND_WP_LS;
+ else
+ wp_status_mask = ONENAND_WP_US;
+
+ /* Continuous lock scheme */
+ if (this->options & ONENAND_HAS_CONT_LOCK) {
+ /* Set start block address */
+ this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
+ /* Set end block address */
+ this->write_word(end, this->base + ONENAND_REG_END_BLOCK_ADDRESS);
+ /* Write lock command */
+ this->command(mtd, cmd, 0, 0);
+
+ /* There's no return value */
+ this->wait(mtd, FL_LOCKING);
+
+ /* Sanity check */
+ while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
+ & ONENAND_CTRL_ONGO)
+ continue;
+
+ /* Check lock status */
+ status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
+ if (!(status & wp_status_mask))
+ printk(KERN_ERR "%s: wp status = 0x%x\n",
+ __func__, status);
+
+ return 0;
+ }
+
+ /* Block lock scheme */
+ for (block = start; block < end + 1; block++) {
+ /* Set block address */
+ value = onenand_block_address(this, block);
+ this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
+ /* Select DataRAM for DDP */
+ value = onenand_bufferram_address(this, block);
+ this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
+ /* Set start block address */
+ this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
+ /* Write lock command */
+ this->command(mtd, cmd, 0, 0);
+
+ /* There's no return value */
+ this->wait(mtd, FL_LOCKING);
+
+ /* Sanity check */
+ while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
+ & ONENAND_CTRL_ONGO)
+ continue;
+
+ /* Check lock status */
+ status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
+ if (!(status & wp_status_mask))
+ printk(KERN_ERR "%s: block = %d, wp status = 0x%x\n",
+ __func__, block, status);
+ }
+
+ return 0;
+}
+
+/**
+ * onenand_lock - [MTD Interface] Lock block(s)
+ * @param mtd MTD device structure
+ * @param ofs offset relative to mtd start
+ * @param len number of bytes to unlock
+ *
+ * Lock one or more blocks
+ */
+static int onenand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+ int ret;
+
+ onenand_get_device(mtd, FL_LOCKING);
+ ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_LOCK);
+ onenand_release_device(mtd);
+ return ret;
+}
+
+/**
+ * onenand_unlock - [MTD Interface] Unlock block(s)
+ * @param mtd MTD device structure
+ * @param ofs offset relative to mtd start
+ * @param len number of bytes to unlock
+ *
+ * Unlock one or more blocks
+ */
+static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+ int ret;
+
+ onenand_get_device(mtd, FL_LOCKING);
+ ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK);
+ onenand_release_device(mtd);
+ return ret;
+}
+
+/**
+ * onenand_check_lock_status - [OneNAND Interface] Check lock status
+ * @param this onenand chip data structure
+ *
+ * Check lock status
+ */
+static int onenand_check_lock_status(struct onenand_chip *this)
+{
+ unsigned int value, block, status;
+ unsigned int end;
+
+ end = this->chipsize >> this->erase_shift;
+ for (block = 0; block < end; block++) {
+ /* Set block address */
+ value = onenand_block_address(this, block);
+ this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
+ /* Select DataRAM for DDP */
+ value = onenand_bufferram_address(this, block);
+ this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
+ /* Set start block address */
+ this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
+
+ /* Check lock status */
+ status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
+ if (!(status & ONENAND_WP_US)) {
+ printk(KERN_ERR "%s: block = %d, wp status = 0x%x\n",
+ __func__, block, status);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/**
+ * onenand_unlock_all - [OneNAND Interface] unlock all blocks
+ * @param mtd MTD device structure
+ *
+ * Unlock all blocks
+ */
+static void onenand_unlock_all(struct mtd_info *mtd)
+{
+ struct onenand_chip *this = mtd->priv;
+ loff_t ofs = 0;
+ loff_t len = mtd->size;
+
+ if (this->options & ONENAND_HAS_UNLOCK_ALL) {
+ /* Set start block address */
+ this->write_word(0, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
+ /* Write unlock command */
+ this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0);
+
+ /* There's no return value */
+ this->wait(mtd, FL_LOCKING);
+
+ /* Sanity check */
+ while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
+ & ONENAND_CTRL_ONGO)
+ continue;
+
+ /* Don't check lock status */
+ if (this->options & ONENAND_SKIP_UNLOCK_CHECK)
+ return;
+
+ /* Check lock status */
+ if (onenand_check_lock_status(this))
+ return;
+
+ /* Workaround for all block unlock in DDP */
+ if (ONENAND_IS_DDP(this) && !FLEXONENAND(this)) {
+ /* All blocks on another chip */
+ ofs = this->chipsize >> 1;
+ len = this->chipsize >> 1;
+ }
+ }
+
+ onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK);
+}
+
+#ifdef CONFIG_MTD_ONENAND_OTP
+
+/**
+ * onenand_otp_command - Send OTP specific command to OneNAND device
+ * @param mtd MTD device structure
+ * @param cmd the command to be sent
+ * @param addr offset to read from or write to
+ * @param len number of bytes to read or write
+ */
+static int onenand_otp_command(struct mtd_info *mtd, int cmd, loff_t addr,
+ size_t len)
+{
+ struct onenand_chip *this = mtd->priv;
+ int value, block, page;
+
+ /* Address translation */
+ switch (cmd) {
+ case ONENAND_CMD_OTP_ACCESS:
+ block = (int) (addr >> this->erase_shift);
+ page = -1;
+ break;
+
+ default:
+ block = (int) (addr >> this->erase_shift);
+ page = (int) (addr >> this->page_shift);
+
+ if (ONENAND_IS_2PLANE(this)) {
+ /* Make the even block number */
+ block &= ~1;
+ /* Is it the odd plane? */
+ if (addr & this->writesize)
+ block++;
+ page >>= 1;
+ }
+ page &= this->page_mask;
+ break;
+ }
+
+ if (block != -1) {
+ /* Write 'DFS, FBA' of Flash */
+ value = onenand_block_address(this, block);
+ this->write_word(value, this->base +
+ ONENAND_REG_START_ADDRESS1);
+ }
+
+ if (page != -1) {
+ /* Now we use page size operation */
+ int sectors = 4, count = 4;
+ int dataram;
+
+ switch (cmd) {
+ default:
+ if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG)
+ cmd = ONENAND_CMD_2X_PROG;
+ dataram = ONENAND_CURRENT_BUFFERRAM(this);
+ break;
+ }
+
+ /* Write 'FPA, FSA' of Flash */
+ value = onenand_page_address(page, sectors);
+ this->write_word(value, this->base +
+ ONENAND_REG_START_ADDRESS8);
+
+ /* Write 'BSA, BSC' of DataRAM */
+ value = onenand_buffer_address(dataram, sectors, count);
+ this->write_word(value, this->base + ONENAND_REG_START_BUFFER);
+ }
+
+ /* Interrupt clear */
+ this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT);
+
+ /* Write command */
+ this->write_word(cmd, this->base + ONENAND_REG_COMMAND);
+
+ return 0;
+}
+
+/**
+ * onenand_otp_write_oob_nolock - [INTERN] OneNAND write out-of-band, specific to OTP
+ * @param mtd MTD device structure
+ * @param to offset to write to
+ * @param len number of bytes to write
+ * @param retlen pointer to variable to store the number of written bytes
+ * @param buf the data to write
+ *
+ * OneNAND write out-of-band only for OTP
+ */
+static int onenand_otp_write_oob_nolock(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+{
+ struct onenand_chip *this = mtd->priv;
+ int column, ret = 0, oobsize;
+ int written = 0;
+ u_char *oobbuf;
+ size_t len = ops->ooblen;
+ const u_char *buf = ops->oobbuf;
+ int block, value, status;
+
+ to += ops->ooboffs;
+
+ /* Initialize retlen, in case of early exit */
+ ops->oobretlen = 0;
+
+ oobsize = mtd->oobsize;
+
+ column = to & (mtd->oobsize - 1);
+
+ oobbuf = this->oob_buf;
+
+ /* Loop until all data write */
+ while (written < len) {
+ int thislen = min_t(int, oobsize, len - written);
+
+ cond_resched();
+
+ block = (int) (to >> this->erase_shift);
+ /*
+ * Write 'DFS, FBA' of Flash
+ * Add: F100h DQ=DFS, FBA
+ */
+
+ value = onenand_block_address(this, block);
+ this->write_word(value, this->base +
+ ONENAND_REG_START_ADDRESS1);
+
+ /*
+ * Select DataRAM for DDP
+ * Add: F101h DQ=DBS
+ */
+
+ value = onenand_bufferram_address(this, block);
+ this->write_word(value, this->base +
+ ONENAND_REG_START_ADDRESS2);
+ ONENAND_SET_NEXT_BUFFERRAM(this);
+
+ /*
+ * Enter OTP access mode
+ */
+ this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
+ this->wait(mtd, FL_OTPING);
+
+ /* We send data to spare ram with oobsize
+ * to prevent byte access */
+ memcpy(oobbuf + column, buf, thislen);
+
+ /*
+ * Write Data into DataRAM
+ * Add: 8th Word
+ * in sector0/spare/page0
+ * DQ=XXFCh
+ */
+ this->write_bufferram(mtd, ONENAND_SPARERAM,
+ oobbuf, 0, mtd->oobsize);
+
+ onenand_otp_command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize);
+ onenand_update_bufferram(mtd, to, 0);
+ if (ONENAND_IS_2PLANE(this)) {
+ ONENAND_SET_BUFFERRAM1(this);
+ onenand_update_bufferram(mtd, to + this->writesize, 0);
+ }
+
+ ret = this->wait(mtd, FL_WRITING);
+ if (ret) {
+ printk(KERN_ERR "%s: write failed %d\n", __func__, ret);
+ break;
+ }
+
+ /* Exit OTP access mode */
+ this->command(mtd, ONENAND_CMD_RESET, 0, 0);
+ this->wait(mtd, FL_RESETING);
+
+ status = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
+ status &= 0x60;
+
+ if (status == 0x60) {
+ printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
+ printk(KERN_DEBUG "1st Block\tLOCKED\n");
+ printk(KERN_DEBUG "OTP Block\tLOCKED\n");
+ } else if (status == 0x20) {
+ printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
+ printk(KERN_DEBUG "1st Block\tLOCKED\n");
+ printk(KERN_DEBUG "OTP Block\tUN-LOCKED\n");
+ } else if (status == 0x40) {
+ printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
+ printk(KERN_DEBUG "1st Block\tUN-LOCKED\n");
+ printk(KERN_DEBUG "OTP Block\tLOCKED\n");
+ } else {
+ printk(KERN_DEBUG "Reboot to check\n");
+ }
+
+ written += thislen;
+ if (written == len)
+ break;
+
+ to += mtd->writesize;
+ buf += thislen;
+ column = 0;
+ }
+
+ ops->oobretlen = written;
+
+ return ret;
+}
+
+/* Internal OTP operation */
+typedef int (*otp_op_t)(struct mtd_info *mtd, loff_t form, size_t len,
+ size_t *retlen, u_char *buf);
+
+/**
+ * do_otp_read - [DEFAULT] Read OTP block area
+ * @param mtd MTD device structure
+ * @param from The offset to read
+ * @param len number of bytes to read
+ * @param retlen pointer to variable to store the number of readbytes
+ * @param buf the databuffer to put/get data
+ *
+ * Read OTP block area.
+ */
+static int do_otp_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct mtd_oob_ops ops = {
+ .len = len,
+ .ooblen = 0,
+ .datbuf = buf,
+ .oobbuf = NULL,
+ };
+ int ret;
+
+ /* Enter OTP access mode */
+ this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
+ this->wait(mtd, FL_OTPING);
+
+ ret = ONENAND_IS_4KB_PAGE(this) ?
+ onenand_mlc_read_ops_nolock(mtd, from, &ops) :
+ onenand_read_ops_nolock(mtd, from, &ops);
+
+ /* Exit OTP access mode */
+ this->command(mtd, ONENAND_CMD_RESET, 0, 0);
+ this->wait(mtd, FL_RESETING);
+
+ return ret;
+}
+
+/**
+ * do_otp_write - [DEFAULT] Write OTP block area
+ * @param mtd MTD device structure
+ * @param to The offset to write
+ * @param len number of bytes to write
+ * @param retlen pointer to variable to store the number of write bytes
+ * @param buf the databuffer to put/get data
+ *
+ * Write OTP block area.
+ */
+static int do_otp_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ struct onenand_chip *this = mtd->priv;
+ unsigned char *pbuf = buf;
+ int ret;
+ struct mtd_oob_ops ops;
+
+ /* Force buffer page aligned */
+ if (len < mtd->writesize) {
+ memcpy(this->page_buf, buf, len);
+ memset(this->page_buf + len, 0xff, mtd->writesize - len);
+ pbuf = this->page_buf;
+ len = mtd->writesize;
+ }
+
+ /* Enter OTP access mode */
+ this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
+ this->wait(mtd, FL_OTPING);
+
+ ops.len = len;
+ ops.ooblen = 0;
+ ops.datbuf = pbuf;
+ ops.oobbuf = NULL;
+ ret = onenand_write_ops_nolock(mtd, to, &ops);
+ *retlen = ops.retlen;
+
+ /* Exit OTP access mode */
+ this->command(mtd, ONENAND_CMD_RESET, 0, 0);
+ this->wait(mtd, FL_RESETING);
+
+ return ret;
+}
+
+/**
+ * do_otp_lock - [DEFAULT] Lock OTP block area
+ * @param mtd MTD device structure
+ * @param from The offset to lock
+ * @param len number of bytes to lock
+ * @param retlen pointer to variable to store the number of lock bytes
+ * @param buf the databuffer to put/get data
+ *
+ * Lock OTP block area.
+ */
+static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct mtd_oob_ops ops;
+ int ret;
+
+ if (FLEXONENAND(this)) {
+
+ /* Enter OTP access mode */
+ this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
+ this->wait(mtd, FL_OTPING);
+ /*
+ * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
+ * main area of page 49.
+ */
+ ops.len = mtd->writesize;
+ ops.ooblen = 0;
+ ops.datbuf = buf;
+ ops.oobbuf = NULL;
+ ret = onenand_write_ops_nolock(mtd, mtd->writesize * 49, &ops);
+ *retlen = ops.retlen;
+
+ /* Exit OTP access mode */
+ this->command(mtd, ONENAND_CMD_RESET, 0, 0);
+ this->wait(mtd, FL_RESETING);
+ } else {
+ ops.mode = MTD_OPS_PLACE_OOB;
+ ops.ooblen = len;
+ ops.oobbuf = buf;
+ ops.ooboffs = 0;
+ ret = onenand_otp_write_oob_nolock(mtd, from, &ops);
+ *retlen = ops.oobretlen;
+ }
+
+ return ret;
+}
+
+/**
+ * onenand_otp_walk - [DEFAULT] Handle OTP operation
+ * @param mtd MTD device structure
+ * @param from The offset to read/write
+ * @param len number of bytes to read/write
+ * @param retlen pointer to variable to store the number of read bytes
+ * @param buf the databuffer to put/get data
+ * @param action do given action
+ * @param mode specify user and factory
+ *
+ * Handle OTP operation.
+ */
+static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf,
+ otp_op_t action, int mode)
+{
+ struct onenand_chip *this = mtd->priv;
+ int otp_pages;
+ int density;
+ int ret = 0;
+
+ *retlen = 0;
+
+ density = onenand_get_density(this->device_id);
+ if (density < ONENAND_DEVICE_DENSITY_512Mb)
+ otp_pages = 20;
+ else
+ otp_pages = 50;
+
+ if (mode == MTD_OTP_FACTORY) {
+ from += mtd->writesize * otp_pages;
+ otp_pages = ONENAND_PAGES_PER_BLOCK - otp_pages;
+ }
+
+ /* Check User/Factory boundary */
+ if (mode == MTD_OTP_USER) {
+ if (mtd->writesize * otp_pages < from + len)
+ return 0;
+ } else {
+ if (mtd->writesize * otp_pages < len)
+ return 0;
+ }
+
+ onenand_get_device(mtd, FL_OTPING);
+ while (len > 0 && otp_pages > 0) {
+ if (!action) { /* OTP Info functions */
+ struct otp_info *otpinfo;
+
+ len -= sizeof(struct otp_info);
+ if (len <= 0) {
+ ret = -ENOSPC;
+ break;
+ }
+
+ otpinfo = (struct otp_info *) buf;
+ otpinfo->start = from;
+ otpinfo->length = mtd->writesize;
+ otpinfo->locked = 0;
+
+ from += mtd->writesize;
+ buf += sizeof(struct otp_info);
+ *retlen += sizeof(struct otp_info);
+ } else {
+ size_t tmp_retlen;
+
+ ret = action(mtd, from, len, &tmp_retlen, buf);
+ if (ret)
+ break;
+
+ buf += tmp_retlen;
+ len -= tmp_retlen;
+ *retlen += tmp_retlen;
+
+ }
+ otp_pages--;
+ }
+ onenand_release_device(mtd);
+
+ return ret;
+}
+
+/**
+ * onenand_get_fact_prot_info - [MTD Interface] Read factory OTP info
+ * @param mtd MTD device structure
+ * @param len number of bytes to read
+ * @param retlen pointer to variable to store the number of read bytes
+ * @param buf the databuffer to put/get data
+ *
+ * Read factory OTP info.
+ */
+static int onenand_get_fact_prot_info(struct mtd_info *mtd, size_t len,
+ size_t *retlen, struct otp_info *buf)
+{
+ return onenand_otp_walk(mtd, 0, len, retlen, (u_char *) buf, NULL,
+ MTD_OTP_FACTORY);
+}
+
+/**
+ * onenand_read_fact_prot_reg - [MTD Interface] Read factory OTP area
+ * @param mtd MTD device structure
+ * @param from The offset to read
+ * @param len number of bytes to read
+ * @param retlen pointer to variable to store the number of read bytes
+ * @param buf the databuffer to put/get data
+ *
+ * Read factory OTP area.
+ */
+static int onenand_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
+ size_t len, size_t *retlen, u_char *buf)
+{
+ return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_read, MTD_OTP_FACTORY);
+}
+
+/**
+ * onenand_get_user_prot_info - [MTD Interface] Read user OTP info
+ * @param mtd MTD device structure
+ * @param retlen pointer to variable to store the number of read bytes
+ * @param len number of bytes to read
+ * @param buf the databuffer to put/get data
+ *
+ * Read user OTP info.
+ */
+static int onenand_get_user_prot_info(struct mtd_info *mtd, size_t len,
+ size_t *retlen, struct otp_info *buf)
+{
+ return onenand_otp_walk(mtd, 0, len, retlen, (u_char *) buf, NULL,
+ MTD_OTP_USER);
+}
+
+/**
+ * onenand_read_user_prot_reg - [MTD Interface] Read user OTP area
+ * @param mtd MTD device structure
+ * @param from The offset to read
+ * @param len number of bytes to read
+ * @param retlen pointer to variable to store the number of read bytes
+ * @param buf the databuffer to put/get data
+ *
+ * Read user OTP area.
+ */
+static int onenand_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
+ size_t len, size_t *retlen, u_char *buf)
+{
+ return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_read, MTD_OTP_USER);
+}
+
+/**
+ * onenand_write_user_prot_reg - [MTD Interface] Write user OTP area
+ * @param mtd MTD device structure
+ * @param from The offset to write
+ * @param len number of bytes to write
+ * @param retlen pointer to variable to store the number of write bytes
+ * @param buf the databuffer to put/get data
+ *
+ * Write user OTP area.
+ */
+static int onenand_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
+ size_t len, size_t *retlen, u_char *buf)
+{
+ return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_write, MTD_OTP_USER);
+}
+
+/**
+ * onenand_lock_user_prot_reg - [MTD Interface] Lock user OTP area
+ * @param mtd MTD device structure
+ * @param from The offset to lock
+ * @param len number of bytes to unlock
+ *
+ * Write lock mark on spare area in page 0 in OTP block
+ */
+static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
+ size_t len)
+{
+ struct onenand_chip *this = mtd->priv;
+ u_char *buf = FLEXONENAND(this) ? this->page_buf : this->oob_buf;
+ size_t retlen;
+ int ret;
+ unsigned int otp_lock_offset = ONENAND_OTP_LOCK_OFFSET;
+
+ memset(buf, 0xff, FLEXONENAND(this) ? this->writesize
+ : mtd->oobsize);
+ /*
+ * Write lock mark to 8th word of sector0 of page0 of the spare0.
+ * We write 16 bytes spare area instead of 2 bytes.
+ * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
+ * main area of page 49.
+ */
+
+ from = 0;
+ len = FLEXONENAND(this) ? mtd->writesize : 16;
+
+ /*
+ * Note: OTP lock operation
+ * OTP block : 0xXXFC XX 1111 1100
+ * 1st block : 0xXXF3 (If chip support) XX 1111 0011
+ * Both : 0xXXF0 (If chip support) XX 1111 0000
+ */
+ if (FLEXONENAND(this))
+ otp_lock_offset = FLEXONENAND_OTP_LOCK_OFFSET;
+
+ /* ONENAND_OTP_AREA | ONENAND_OTP_BLOCK0 | ONENAND_OTP_AREA_BLOCK0 */
+ if (otp == 1)
+ buf[otp_lock_offset] = 0xFC;
+ else if (otp == 2)
+ buf[otp_lock_offset] = 0xF3;
+ else if (otp == 3)
+ buf[otp_lock_offset] = 0xF0;
+ else if (otp != 0)
+ printk(KERN_DEBUG "[OneNAND] Invalid option selected for OTP\n");
+
+ ret = onenand_otp_walk(mtd, from, len, &retlen, buf, do_otp_lock, MTD_OTP_USER);
+
+ return ret ? : retlen;
+}
+
+#endif /* CONFIG_MTD_ONENAND_OTP */
+
+/**
+ * onenand_check_features - Check and set OneNAND features
+ * @param mtd MTD data structure
+ *
+ * Check and set OneNAND features
+ * - lock scheme
+ * - two plane
+ */
+static void onenand_check_features(struct mtd_info *mtd)
+{
+ struct onenand_chip *this = mtd->priv;
+ unsigned int density, process, numbufs;
+
+ /* Lock scheme depends on density and process */
+ density = onenand_get_density(this->device_id);
+ process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT;
+ numbufs = this->read_word(this->base + ONENAND_REG_NUM_BUFFERS) >> 8;
+
+ /* Lock scheme */
+ switch (density) {
+ case ONENAND_DEVICE_DENSITY_4Gb:
+ if (ONENAND_IS_DDP(this))
+ this->options |= ONENAND_HAS_2PLANE;
+ else if (numbufs == 1) {
+ this->options |= ONENAND_HAS_4KB_PAGE;
+ this->options |= ONENAND_HAS_CACHE_PROGRAM;
+ /*
+ * There are two different 4KiB pagesize chips
+ * and no way to detect it by H/W config values.
+ *
+ * To detect the correct NOP for each chips,
+ * It should check the version ID as workaround.
+ *
+ * Now it has as following
+ * KFM4G16Q4M has NOP 4 with version ID 0x0131
+ * KFM4G16Q5M has NOP 1 with versoin ID 0x013e
+ */
+ if ((this->version_id & 0xf) == 0xe)
+ this->options |= ONENAND_HAS_NOP_1;
+ }
+
+ case ONENAND_DEVICE_DENSITY_2Gb:
+ /* 2Gb DDP does not have 2 plane */
+ if (!ONENAND_IS_DDP(this))
+ this->options |= ONENAND_HAS_2PLANE;
+ this->options |= ONENAND_HAS_UNLOCK_ALL;
+
+ case ONENAND_DEVICE_DENSITY_1Gb:
+ /* A-Die has all block unlock */
+ if (process)
+ this->options |= ONENAND_HAS_UNLOCK_ALL;
+ break;
+
+ default:
+ /* Some OneNAND has continuous lock scheme */
+ if (!process)
+ this->options |= ONENAND_HAS_CONT_LOCK;
+ break;
+ }
+
+ /* The MLC has 4KiB pagesize. */
+ if (ONENAND_IS_MLC(this))
+ this->options |= ONENAND_HAS_4KB_PAGE;
+
+ if (ONENAND_IS_4KB_PAGE(this))
+ this->options &= ~ONENAND_HAS_2PLANE;
+
+ if (FLEXONENAND(this)) {
+ this->options &= ~ONENAND_HAS_CONT_LOCK;
+ this->options |= ONENAND_HAS_UNLOCK_ALL;
+ }
+
+ if (this->options & ONENAND_HAS_CONT_LOCK)
+ printk(KERN_DEBUG "Lock scheme is Continuous Lock\n");
+ if (this->options & ONENAND_HAS_UNLOCK_ALL)
+ printk(KERN_DEBUG "Chip support all block unlock\n");
+ if (this->options & ONENAND_HAS_2PLANE)
+ printk(KERN_DEBUG "Chip has 2 plane\n");
+ if (this->options & ONENAND_HAS_4KB_PAGE)
+ printk(KERN_DEBUG "Chip has 4KiB pagesize\n");
+ if (this->options & ONENAND_HAS_CACHE_PROGRAM)
+ printk(KERN_DEBUG "Chip has cache program feature\n");
+}
+
+/**
+ * onenand_print_device_info - Print device & version ID
+ * @param device device ID
+ * @param version version ID
+ *
+ * Print device & version ID
+ */
+static void onenand_print_device_info(int device, int version)
+{
+ int vcc, demuxed, ddp, density, flexonenand;
+
+ vcc = device & ONENAND_DEVICE_VCC_MASK;
+ demuxed = device & ONENAND_DEVICE_IS_DEMUX;
+ ddp = device & ONENAND_DEVICE_IS_DDP;
+ density = onenand_get_density(device);
+ flexonenand = device & DEVICE_IS_FLEXONENAND;
+ printk(KERN_INFO "%s%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n",
+ demuxed ? "" : "Muxed ",
+ flexonenand ? "Flex-" : "",
+ ddp ? "(DDP)" : "",
+ (16 << density),
+ vcc ? "2.65/3.3" : "1.8",
+ device);
+ printk(KERN_INFO "OneNAND version = 0x%04x\n", version);
+}
+
+static const struct onenand_manufacturers onenand_manuf_ids[] = {
+ {ONENAND_MFR_SAMSUNG, "Samsung"},
+ {ONENAND_MFR_NUMONYX, "Numonyx"},
+};
+
+/**
+ * onenand_check_maf - Check manufacturer ID
+ * @param manuf manufacturer ID
+ *
+ * Check manufacturer ID
+ */
+static int onenand_check_maf(int manuf)
+{
+ int size = ARRAY_SIZE(onenand_manuf_ids);
+ char *name;
+ int i;
+
+ for (i = 0; i < size; i++)
+ if (manuf == onenand_manuf_ids[i].id)
+ break;
+
+ if (i < size)
+ name = onenand_manuf_ids[i].name;
+ else
+ name = "Unknown";
+
+ printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n", name, manuf);
+
+ return (i == size);
+}
+
+/**
+* flexonenand_get_boundary - Reads the SLC boundary
+* @param onenand_info - onenand info structure
+**/
+static int flexonenand_get_boundary(struct mtd_info *mtd)
+{
+ struct onenand_chip *this = mtd->priv;
+ unsigned die, bdry;
+ int syscfg, locked;
+
+ /* Disable ECC */
+ syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
+ this->write_word((syscfg | 0x0100), this->base + ONENAND_REG_SYS_CFG1);
+
+ for (die = 0; die < this->dies; die++) {
+ this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0);
+ this->wait(mtd, FL_SYNCING);
+
+ this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0);
+ this->wait(mtd, FL_READING);
+
+ bdry = this->read_word(this->base + ONENAND_DATARAM);
+ if ((bdry >> FLEXONENAND_PI_UNLOCK_SHIFT) == 3)
+ locked = 0;
+ else
+ locked = 1;
+ this->boundary[die] = bdry & FLEXONENAND_PI_MASK;
+
+ this->command(mtd, ONENAND_CMD_RESET, 0, 0);
+ this->wait(mtd, FL_RESETING);
+
+ printk(KERN_INFO "Die %d boundary: %d%s\n", die,
+ this->boundary[die], locked ? "(Locked)" : "(Unlocked)");
+ }
+
+ /* Enable ECC */
+ this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);
+ return 0;
+}
+
+/**
+ * flexonenand_get_size - Fill up fields in onenand_chip and mtd_info
+ * boundary[], diesize[], mtd->size, mtd->erasesize
+ * @param mtd - MTD device structure
+ */
+static void flexonenand_get_size(struct mtd_info *mtd)
+{
+ struct onenand_chip *this = mtd->priv;
+ int die, i, eraseshift, density;
+ int blksperdie, maxbdry;
+ loff_t ofs;
+
+ density = onenand_get_density(this->device_id);
+ blksperdie = ((loff_t)(16 << density) << 20) >> (this->erase_shift);
+ blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0;
+ maxbdry = blksperdie - 1;
+ eraseshift = this->erase_shift - 1;
+
+ mtd->numeraseregions = this->dies << 1;
+
+ /* This fills up the device boundary */
+ flexonenand_get_boundary(mtd);
+ die = ofs = 0;
+ i = -1;
+ for (; die < this->dies; die++) {
+ if (!die || this->boundary[die-1] != maxbdry) {
+ i++;
+ mtd->eraseregions[i].offset = ofs;
+ mtd->eraseregions[i].erasesize = 1 << eraseshift;
+ mtd->eraseregions[i].numblocks =
+ this->boundary[die] + 1;
+ ofs += mtd->eraseregions[i].numblocks << eraseshift;
+ eraseshift++;
+ } else {
+ mtd->numeraseregions -= 1;
+ mtd->eraseregions[i].numblocks +=
+ this->boundary[die] + 1;
+ ofs += (this->boundary[die] + 1) << (eraseshift - 1);
+ }
+ if (this->boundary[die] != maxbdry) {
+ i++;
+ mtd->eraseregions[i].offset = ofs;
+ mtd->eraseregions[i].erasesize = 1 << eraseshift;
+ mtd->eraseregions[i].numblocks = maxbdry ^
+ this->boundary[die];
+ ofs += mtd->eraseregions[i].numblocks << eraseshift;
+ eraseshift--;
+ } else
+ mtd->numeraseregions -= 1;
+ }
+
+ /* Expose MLC erase size except when all blocks are SLC */
+ mtd->erasesize = 1 << this->erase_shift;
+ if (mtd->numeraseregions == 1)
+ mtd->erasesize >>= 1;
+
+ printk(KERN_INFO "Device has %d eraseregions\n", mtd->numeraseregions);
+ for (i = 0; i < mtd->numeraseregions; i++)
+ printk(KERN_INFO "[offset: 0x%08x, erasesize: 0x%05x,"
+ " numblocks: %04u]\n",
+ (unsigned int) mtd->eraseregions[i].offset,
+ mtd->eraseregions[i].erasesize,
+ mtd->eraseregions[i].numblocks);
+
+ for (die = 0, mtd->size = 0; die < this->dies; die++) {
+ this->diesize[die] = (loff_t)blksperdie << this->erase_shift;
+ this->diesize[die] -= (loff_t)(this->boundary[die] + 1)
+ << (this->erase_shift - 1);
+ mtd->size += this->diesize[die];
+ }
+}
+
+/**
+ * flexonenand_check_blocks_erased - Check if blocks are erased
+ * @param mtd_info - mtd info structure
+ * @param start - first erase block to check
+ * @param end - last erase block to check
+ *
+ * Converting an unerased block from MLC to SLC
+ * causes byte values to change. Since both data and its ECC
+ * have changed, reads on the block give uncorrectable error.
+ * This might lead to the block being detected as bad.
+ *
+ * Avoid this by ensuring that the block to be converted is
+ * erased.
+ */
+static int flexonenand_check_blocks_erased(struct mtd_info *mtd, int start, int end)
+{
+ struct onenand_chip *this = mtd->priv;
+ int i, ret;
+ int block;
+ struct mtd_oob_ops ops = {
+ .mode = MTD_OPS_PLACE_OOB,
+ .ooboffs = 0,
+ .ooblen = mtd->oobsize,
+ .datbuf = NULL,
+ .oobbuf = this->oob_buf,
+ };
+ loff_t addr;
+
+ printk(KERN_DEBUG "Check blocks from %d to %d\n", start, end);
+
+ for (block = start; block <= end; block++) {
+ addr = flexonenand_addr(this, block);
+ if (onenand_block_isbad_nolock(mtd, addr, 0))
+ continue;
+
+ /*
+ * Since main area write results in ECC write to spare,
+ * it is sufficient to check only ECC bytes for change.
+ */
+ ret = onenand_read_oob_nolock(mtd, addr, &ops);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < mtd->oobsize; i++)
+ if (this->oob_buf[i] != 0xff)
+ break;
+
+ if (i != mtd->oobsize) {
+ printk(KERN_WARNING "%s: Block %d not erased.\n",
+ __func__, block);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * flexonenand_set_boundary - Writes the SLC boundary
+ * @param mtd - mtd info structure
+ */
+static int flexonenand_set_boundary(struct mtd_info *mtd, int die,
+ int boundary, int lock)
+{
+ struct onenand_chip *this = mtd->priv;
+ int ret, density, blksperdie, old, new, thisboundary;
+ loff_t addr;
+
+ /* Change only once for SDP Flex-OneNAND */
+ if (die && (!ONENAND_IS_DDP(this)))
+ return 0;
+
+ /* boundary value of -1 indicates no required change */
+ if (boundary < 0 || boundary == this->boundary[die])
+ return 0;
+
+ density = onenand_get_density(this->device_id);
+ blksperdie = ((16 << density) << 20) >> this->erase_shift;
+ blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0;
+
+ if (boundary >= blksperdie) {
+ printk(KERN_ERR "%s: Invalid boundary value. "
+ "Boundary not changed.\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Check if converting blocks are erased */
+ old = this->boundary[die] + (die * this->density_mask);
+ new = boundary + (die * this->density_mask);
+ ret = flexonenand_check_blocks_erased(mtd, min(old, new) + 1, max(old, new));
+ if (ret) {
+ printk(KERN_ERR "%s: Please erase blocks "
+ "before boundary change\n", __func__);
+ return ret;
+ }
+
+ this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0);
+ this->wait(mtd, FL_SYNCING);
+
+ /* Check is boundary is locked */
+ this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0);
+ this->wait(mtd, FL_READING);
+
+ thisboundary = this->read_word(this->base + ONENAND_DATARAM);
+ if ((thisboundary >> FLEXONENAND_PI_UNLOCK_SHIFT) != 3) {
+ printk(KERN_ERR "%s: boundary locked\n", __func__);
+ ret = 1;
+ goto out;
+ }
+
+ printk(KERN_INFO "Changing die %d boundary: %d%s\n",
+ die, boundary, lock ? "(Locked)" : "(Unlocked)");
+
+ addr = die ? this->diesize[0] : 0;
+
+ boundary &= FLEXONENAND_PI_MASK;
+ boundary |= lock ? 0 : (3 << FLEXONENAND_PI_UNLOCK_SHIFT);
+
+ this->command(mtd, ONENAND_CMD_ERASE, addr, 0);
+ ret = this->wait(mtd, FL_ERASING);
+ if (ret) {
+ printk(KERN_ERR "%s: Failed PI erase for Die %d\n",
+ __func__, die);
+ goto out;
+ }
+
+ this->write_word(boundary, this->base + ONENAND_DATARAM);
+ this->command(mtd, ONENAND_CMD_PROG, addr, 0);
+ ret = this->wait(mtd, FL_WRITING);
+ if (ret) {
+ printk(KERN_ERR "%s: Failed PI write for Die %d\n",
+ __func__, die);
+ goto out;
+ }
+
+ this->command(mtd, FLEXONENAND_CMD_PI_UPDATE, die, 0);
+ ret = this->wait(mtd, FL_WRITING);
+out:
+ this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_REG_COMMAND);
+ this->wait(mtd, FL_RESETING);
+ if (!ret)
+ /* Recalculate device size on boundary change*/
+ flexonenand_get_size(mtd);
+
+ return ret;
+}
+
+/**
+ * onenand_chip_probe - [OneNAND Interface] The generic chip probe
+ * @param mtd MTD device structure
+ *
+ * OneNAND detection method:
+ * Compare the values from command with ones from register
+ */
+static int onenand_chip_probe(struct mtd_info *mtd)
+{
+ struct onenand_chip *this = mtd->priv;
+ int bram_maf_id, bram_dev_id, maf_id, dev_id;
+ int syscfg;
+
+ /* Save system configuration 1 */
+ syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
+ /* Clear Sync. Burst Read mode to read BootRAM */
+ this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ & ~ONENAND_SYS_CFG1_SYNC_WRITE), this->base + ONENAND_REG_SYS_CFG1);
+
+ /* Send the command for reading device ID from BootRAM */
+ this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM);
+
+ /* Read manufacturer and device IDs from BootRAM */
+ bram_maf_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x0);
+ bram_dev_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x2);
+
+ /* Reset OneNAND to read default register values */
+ this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_BOOTRAM);
+ /* Wait reset */
+ this->wait(mtd, FL_RESETING);
+
+ /* Restore system configuration 1 */
+ this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);
+
+ /* Check manufacturer ID */
+ if (onenand_check_maf(bram_maf_id))
+ return -ENXIO;
+
+ /* Read manufacturer and device IDs from Register */
+ maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID);
+ dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID);
+
+ /* Check OneNAND device */
+ if (maf_id != bram_maf_id || dev_id != bram_dev_id)
+ return -ENXIO;
+
+ return 0;
+}
+
+/**
+ * onenand_probe - [OneNAND Interface] Probe the OneNAND device
+ * @param mtd MTD device structure
+ */
+static int onenand_probe(struct mtd_info *mtd)
+{
+ struct onenand_chip *this = mtd->priv;
+ int dev_id, ver_id;
+ int density;
+ int ret;
+
+ ret = this->chip_probe(mtd);
+ if (ret)
+ return ret;
+
+ /* Device and version IDs from Register */
+ dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID);
+ ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID);
+ this->technology = this->read_word(this->base + ONENAND_REG_TECHNOLOGY);
+
+ /* Flash device information */
+ onenand_print_device_info(dev_id, ver_id);
+ this->device_id = dev_id;
+ this->version_id = ver_id;
+
+ /* Check OneNAND features */
+ onenand_check_features(mtd);
+
+ density = onenand_get_density(dev_id);
+ if (FLEXONENAND(this)) {
+ this->dies = ONENAND_IS_DDP(this) ? 2 : 1;
+ /* Maximum possible erase regions */
+ mtd->numeraseregions = this->dies << 1;
+ mtd->eraseregions = kzalloc(sizeof(struct mtd_erase_region_info)
+ * (this->dies << 1), GFP_KERNEL);
+ if (!mtd->eraseregions)
+ return -ENOMEM;
+ }
+
+ /*
+ * For Flex-OneNAND, chipsize represents maximum possible device size.
+ * mtd->size represents the actual device size.
+ */
+ this->chipsize = (16 << density) << 20;
+
+ /* OneNAND page size & block size */
+ /* The data buffer size is equal to page size */
+ mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE);
+ /* We use the full BufferRAM */
+ if (ONENAND_IS_4KB_PAGE(this))
+ mtd->writesize <<= 1;
+
+ mtd->oobsize = mtd->writesize >> 5;
+ /* Pages per a block are always 64 in OneNAND */
+ mtd->erasesize = mtd->writesize << 6;
+ /*
+ * Flex-OneNAND SLC area has 64 pages per block.
+ * Flex-OneNAND MLC area has 128 pages per block.
+ * Expose MLC erase size to find erase_shift and page_mask.
+ */
+ if (FLEXONENAND(this))
+ mtd->erasesize <<= 1;
+
+ this->erase_shift = ffs(mtd->erasesize) - 1;
+ this->page_shift = ffs(mtd->writesize) - 1;
+ this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1;
+ /* Set density mask. it is used for DDP */
+ if (ONENAND_IS_DDP(this))
+ this->density_mask = this->chipsize >> (this->erase_shift + 1);
+ /* It's real page size */
+ this->writesize = mtd->writesize;
+
+ /* REVISIT: Multichip handling */
+
+ if (FLEXONENAND(this))
+ flexonenand_get_size(mtd);
+ else
+ mtd->size = this->chipsize;
+
+ /*
+ * We emulate the 4KiB page and 256KiB erase block size
+ * But oobsize is still 64 bytes.
+ * It is only valid if you turn on 2X program support,
+ * Otherwise it will be ignored by compiler.
+ */
+ if (ONENAND_IS_2PLANE(this)) {
+ mtd->writesize <<= 1;
+ mtd->erasesize <<= 1;
+ }
+
+ return 0;
+}
+
+/**
+ * onenand_suspend - [MTD Interface] Suspend the OneNAND flash
+ * @param mtd MTD device structure
+ */
+static int onenand_suspend(struct mtd_info *mtd)
+{
+ return onenand_get_device(mtd, FL_PM_SUSPENDED);
+}
+
+/**
+ * onenand_resume - [MTD Interface] Resume the OneNAND flash
+ * @param mtd MTD device structure
+ */
+static void onenand_resume(struct mtd_info *mtd)
+{
+ struct onenand_chip *this = mtd->priv;
+
+ if (this->state == FL_PM_SUSPENDED)
+ onenand_release_device(mtd);
+ else
+ printk(KERN_ERR "%s: resume() called for the chip which is not "
+ "in suspended state\n", __func__);
+}
+
+/**
+ * onenand_scan - [OneNAND Interface] Scan for the OneNAND device
+ * @param mtd MTD device structure
+ * @param maxchips Number of chips to scan for
+ *
+ * This fills out all the not initialized function pointers
+ * with the defaults.
+ * The flash ID is read and the mtd/chip structures are
+ * filled with the appropriate values.
+ */
+int onenand_scan(struct mtd_info *mtd, int maxchips)
+{
+ int i, ret;
+ struct onenand_chip *this = mtd->priv;
+
+ if (!this->read_word)
+ this->read_word = onenand_readw;
+ if (!this->write_word)
+ this->write_word = onenand_writew;
+
+ if (!this->command)
+ this->command = onenand_command;
+ if (!this->wait)
+ onenand_setup_wait(mtd);
+ if (!this->bbt_wait)
+ this->bbt_wait = onenand_bbt_wait;
+ if (!this->unlock_all)
+ this->unlock_all = onenand_unlock_all;
+
+ if (!this->chip_probe)
+ this->chip_probe = onenand_chip_probe;
+
+ if (!this->read_bufferram)
+ this->read_bufferram = onenand_read_bufferram;
+ if (!this->write_bufferram)
+ this->write_bufferram = onenand_write_bufferram;
+
+ if (!this->block_markbad)
+ this->block_markbad = onenand_default_block_markbad;
+ if (!this->scan_bbt)
+ this->scan_bbt = onenand_default_bbt;
+
+ if (onenand_probe(mtd))
+ return -ENXIO;
+
+ /* Set Sync. Burst Read after probing */
+ if (this->mmcontrol) {
+ printk(KERN_INFO "OneNAND Sync. Burst Read support\n");
+ this->read_bufferram = onenand_sync_read_bufferram;
+ }
+
+ /* Allocate buffers, if necessary */
+ if (!this->page_buf) {
+ this->page_buf = kzalloc(mtd->writesize, GFP_KERNEL);
+ if (!this->page_buf)
+ return -ENOMEM;
+#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
+ this->verify_buf = kzalloc(mtd->writesize, GFP_KERNEL);
+ if (!this->verify_buf) {
+ kfree(this->page_buf);
+ return -ENOMEM;
+ }
+#endif
+ this->options |= ONENAND_PAGEBUF_ALLOC;
+ }
+ if (!this->oob_buf) {
+ this->oob_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
+ if (!this->oob_buf) {
+ if (this->options & ONENAND_PAGEBUF_ALLOC) {
+ this->options &= ~ONENAND_PAGEBUF_ALLOC;
+ kfree(this->page_buf);
+ }
+ return -ENOMEM;
+ }
+ this->options |= ONENAND_OOBBUF_ALLOC;
+ }
+
+ this->state = FL_READY;
+ init_waitqueue_head(&this->wq);
+ spin_lock_init(&this->chip_lock);
+
+ /*
+ * Allow subpage writes up to oobsize.
+ */
+ switch (mtd->oobsize) {
+ case 128:
+ if (FLEXONENAND(this)) {
+ mtd_set_ooblayout(mtd, &flexonenand_ooblayout_ops);
+ mtd->subpage_sft = 0;
+ } else {
+ mtd_set_ooblayout(mtd, &onenand_oob_128_ooblayout_ops);
+ mtd->subpage_sft = 2;
+ }
+ if (ONENAND_IS_NOP_1(this))
+ mtd->subpage_sft = 0;
+ break;
+ case 64:
+ mtd_set_ooblayout(mtd, &onenand_oob_32_64_ooblayout_ops);
+ mtd->subpage_sft = 2;
+ break;
+
+ case 32:
+ mtd_set_ooblayout(mtd, &onenand_oob_32_64_ooblayout_ops);
+ mtd->subpage_sft = 1;
+ break;
+
+ default:
+ printk(KERN_WARNING "%s: No OOB scheme defined for oobsize %d\n",
+ __func__, mtd->oobsize);
+ mtd->subpage_sft = 0;
+ /* To prevent kernel oops */
+ mtd_set_ooblayout(mtd, &onenand_oob_32_64_ooblayout_ops);
+ break;
+ }
+
+ this->subpagesize = mtd->writesize >> mtd->subpage_sft;
+
+ /*
+ * The number of bytes available for a client to place data into
+ * the out of band area
+ */
+ ret = mtd_ooblayout_count_freebytes(mtd);
+ if (ret < 0)
+ ret = 0;
+
+ mtd->oobavail = ret;
+
+ mtd->ecc_strength = 1;
+
+ /* Fill in remaining MTD driver data */
+ mtd->type = ONENAND_IS_MLC(this) ? MTD_MLCNANDFLASH : MTD_NANDFLASH;
+ mtd->flags = MTD_CAP_NANDFLASH;
+ mtd->_erase = onenand_erase;
+ mtd->_point = NULL;
+ mtd->_unpoint = NULL;
+ mtd->_read_oob = onenand_read_oob;
+ mtd->_write_oob = onenand_write_oob;
+ mtd->_panic_write = onenand_panic_write;
+#ifdef CONFIG_MTD_ONENAND_OTP
+ mtd->_get_fact_prot_info = onenand_get_fact_prot_info;
+ mtd->_read_fact_prot_reg = onenand_read_fact_prot_reg;
+ mtd->_get_user_prot_info = onenand_get_user_prot_info;
+ mtd->_read_user_prot_reg = onenand_read_user_prot_reg;
+ mtd->_write_user_prot_reg = onenand_write_user_prot_reg;
+ mtd->_lock_user_prot_reg = onenand_lock_user_prot_reg;
+#endif
+ mtd->_sync = onenand_sync;
+ mtd->_lock = onenand_lock;
+ mtd->_unlock = onenand_unlock;
+ mtd->_suspend = onenand_suspend;
+ mtd->_resume = onenand_resume;
+ mtd->_block_isbad = onenand_block_isbad;
+ mtd->_block_markbad = onenand_block_markbad;
+ mtd->owner = THIS_MODULE;
+ mtd->writebufsize = mtd->writesize;
+
+ /* Unlock whole block */
+ if (!(this->options & ONENAND_SKIP_INITIAL_UNLOCKING))
+ this->unlock_all(mtd);
+
+ ret = this->scan_bbt(mtd);
+ if ((!FLEXONENAND(this)) || ret)
+ return ret;
+
+ /* Change Flex-OneNAND boundaries if required */
+ for (i = 0; i < MAX_DIES; i++)
+ flexonenand_set_boundary(mtd, i, flex_bdry[2 * i],
+ flex_bdry[(2 * i) + 1]);
+
+ return 0;
+}
+
+/**
+ * onenand_release - [OneNAND Interface] Free resources held by the OneNAND device
+ * @param mtd MTD device structure
+ */
+void onenand_release(struct mtd_info *mtd)
+{
+ struct onenand_chip *this = mtd->priv;
+
+ /* Deregister partitions */
+ mtd_device_unregister(mtd);
+
+ /* Free bad block table memory, if allocated */
+ if (this->bbm) {
+ struct bbm_info *bbm = this->bbm;
+ kfree(bbm->bbt);
+ kfree(this->bbm);
+ }
+ /* Buffers allocated by onenand_scan */
+ if (this->options & ONENAND_PAGEBUF_ALLOC) {
+ kfree(this->page_buf);
+#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
+ kfree(this->verify_buf);
+#endif
+ }
+ if (this->options & ONENAND_OOBBUF_ALLOC)
+ kfree(this->oob_buf);
+ kfree(mtd->eraseregions);
+}
+
+EXPORT_SYMBOL_GPL(onenand_scan);
+EXPORT_SYMBOL_GPL(onenand_release);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>");
+MODULE_DESCRIPTION("Generic OneNAND flash driver code");
diff --git a/drivers/mtd/nand/onenand/onenand_bbt.c b/drivers/mtd/nand/onenand/onenand_bbt.c
new file mode 100644
index 000000000000..dde20487937d
--- /dev/null
+++ b/drivers/mtd/nand/onenand/onenand_bbt.c
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Bad Block Table support for the OneNAND driver
+ *
+ * Copyright(c) 2005 Samsung Electronics
+ * Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ * Derived from nand_bbt.c
+ *
+ * TODO:
+ * Split BBT core and chip specific BBT.
+ */
+
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/onenand.h>
+#include <linux/export.h>
+
+/**
+ * check_short_pattern - [GENERIC] check if a pattern is in the buffer
+ * @param buf the buffer to search
+ * @param len the length of buffer to search
+ * @param paglen the pagelength
+ * @param td search pattern descriptor
+ *
+ * Check for a pattern at the given place. Used to search bad block
+ * tables and good / bad block identifiers. Same as check_pattern, but
+ * no optional empty check and the pattern is expected to start
+ * at offset 0.
+ *
+ */
+static int check_short_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
+{
+ int i;
+ uint8_t *p = buf;
+
+ /* Compare the pattern */
+ for (i = 0; i < td->len; i++) {
+ if (p[i] != td->pattern[i])
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * create_bbt - [GENERIC] Create a bad block table by scanning the device
+ * @param mtd MTD device structure
+ * @param buf temporary buffer
+ * @param bd descriptor for the good/bad block search pattern
+ * @param chip create the table for a specific chip, -1 read all chips.
+ * Applies only if NAND_BBT_PERCHIP option is set
+ *
+ * Create a bad block table by scanning the device
+ * for the given good/bad block identify pattern
+ */
+static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct bbm_info *bbm = this->bbm;
+ int i, j, numblocks, len, scanlen;
+ int startblock;
+ loff_t from;
+ size_t readlen, ooblen;
+ struct mtd_oob_ops ops;
+ int rgn;
+
+ printk(KERN_INFO "Scanning device for bad blocks\n");
+
+ len = 2;
+
+ /* We need only read few bytes from the OOB area */
+ scanlen = ooblen = 0;
+ readlen = bd->len;
+
+ /* chip == -1 case only */
+ /* Note that numblocks is 2 * (real numblocks) here;
+ * see i += 2 below as it makses shifting and masking less painful
+ */
+ numblocks = this->chipsize >> (bbm->bbt_erase_shift - 1);
+ startblock = 0;
+ from = 0;
+
+ ops.mode = MTD_OPS_PLACE_OOB;
+ ops.ooblen = readlen;
+ ops.oobbuf = buf;
+ ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
+
+ for (i = startblock; i < numblocks; ) {
+ int ret;
+
+ for (j = 0; j < len; j++) {
+ /* No need to read pages fully,
+ * just read required OOB bytes */
+ ret = onenand_bbt_read_oob(mtd,
+ from + j * this->writesize + bd->offs, &ops);
+
+ /* If it is a initial bad block, just ignore it */
+ if (ret == ONENAND_BBT_READ_FATAL_ERROR)
+ return -EIO;
+
+ if (ret || check_short_pattern(&buf[j * scanlen],
+ scanlen, this->writesize, bd)) {
+ bbm->bbt[i >> 3] |= 0x03 << (i & 0x6);
+ printk(KERN_INFO "OneNAND eraseblock %d is an "
+ "initial bad block\n", i >> 1);
+ mtd->ecc_stats.badblocks++;
+ break;
+ }
+ }
+ i += 2;
+
+ if (FLEXONENAND(this)) {
+ rgn = flexonenand_region(mtd, from);
+ from += mtd->eraseregions[rgn].erasesize;
+ } else
+ from += (1 << bbm->bbt_erase_shift);
+ }
+
+ return 0;
+}
+
+
+/**
+ * onenand_memory_bbt - [GENERIC] create a memory based bad block table
+ * @param mtd MTD device structure
+ * @param bd descriptor for the good/bad block search pattern
+ *
+ * The function creates a memory based bbt by scanning the device
+ * for manufacturer / software marked good / bad blocks
+ */
+static inline int onenand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
+{
+ struct onenand_chip *this = mtd->priv;
+
+ return create_bbt(mtd, this->page_buf, bd, -1);
+}
+
+/**
+ * onenand_isbad_bbt - [OneNAND Interface] Check if a block is bad
+ * @param mtd MTD device structure
+ * @param offs offset in the device
+ * @param allowbbt allow access to bad block table region
+ */
+static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct bbm_info *bbm = this->bbm;
+ int block;
+ uint8_t res;
+
+ /* Get block number * 2 */
+ block = (int) (onenand_block(this, offs) << 1);
+ res = (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03;
+
+ pr_debug("onenand_isbad_bbt: bbt info for offs 0x%08x: (block %d) 0x%02x\n",
+ (unsigned int) offs, block >> 1, res);
+
+ switch ((int) res) {
+ case 0x00: return 0;
+ case 0x01: return 1;
+ case 0x02: return allowbbt ? 0 : 1;
+ }
+
+ return 1;
+}
+
+/**
+ * onenand_scan_bbt - [OneNAND Interface] scan, find, read and maybe create bad block table(s)
+ * @param mtd MTD device structure
+ * @param bd descriptor for the good/bad block search pattern
+ *
+ * The function checks, if a bad block table(s) is/are already
+ * available. If not it scans the device for manufacturer
+ * marked good / bad blocks and writes the bad block table(s) to
+ * the selected place.
+ *
+ * The bad block table memory is allocated here. It is freed
+ * by the onenand_release function.
+ *
+ */
+static int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct bbm_info *bbm = this->bbm;
+ int len, ret = 0;
+
+ len = this->chipsize >> (this->erase_shift + 2);
+ /* Allocate memory (2bit per block) and clear the memory bad block table */
+ bbm->bbt = kzalloc(len, GFP_KERNEL);
+ if (!bbm->bbt)
+ return -ENOMEM;
+
+ /* Set the bad block position */
+ bbm->badblockpos = ONENAND_BADBLOCK_POS;
+
+ /* Set erase shift */
+ bbm->bbt_erase_shift = this->erase_shift;
+
+ if (!bbm->isbad_bbt)
+ bbm->isbad_bbt = onenand_isbad_bbt;
+
+ /* Scan the device to build a memory based bad block table */
+ if ((ret = onenand_memory_bbt(mtd, bd))) {
+ printk(KERN_ERR "onenand_scan_bbt: Can't scan flash and build the RAM-based BBT\n");
+ kfree(bbm->bbt);
+ bbm->bbt = NULL;
+ }
+
+ return ret;
+}
+
+/*
+ * Define some generic bad / good block scan pattern which are used
+ * while scanning a device for factory marked good / bad blocks.
+ */
+static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
+
+static struct nand_bbt_descr largepage_memorybased = {
+ .options = 0,
+ .offs = 0,
+ .len = 2,
+ .pattern = scan_ff_pattern,
+};
+
+/**
+ * onenand_default_bbt - [OneNAND Interface] Select a default bad block table for the device
+ * @param mtd MTD device structure
+ *
+ * This function selects the default bad block table
+ * support for the device and calls the onenand_scan_bbt function
+ */
+int onenand_default_bbt(struct mtd_info *mtd)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct bbm_info *bbm;
+
+ this->bbm = kzalloc(sizeof(struct bbm_info), GFP_KERNEL);
+ if (!this->bbm)
+ return -ENOMEM;
+
+ bbm = this->bbm;
+
+ /* 1KB page has same configuration as 2KB page */
+ if (!bbm->badblock_pattern)
+ bbm->badblock_pattern = &largepage_memorybased;
+
+ return onenand_scan_bbt(mtd, bbm->badblock_pattern);
+}
diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/nand/onenand/samsung.c
index 2e9d076e445a..2e9d076e445a 100644
--- a/drivers/mtd/onenand/samsung.c
+++ b/drivers/mtd/nand/onenand/samsung.c
diff --git a/drivers/mtd/onenand/samsung.h b/drivers/mtd/nand/onenand/samsung.h
index 9016dc0136a8..9016dc0136a8 100644
--- a/drivers/mtd/onenand/samsung.h
+++ b/drivers/mtd/nand/onenand/samsung.h
diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c
deleted file mode 100644
index 5a5aa1f07d07..000000000000
--- a/drivers/mtd/nand/orion_nand.c
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * drivers/mtd/nand/orion_nand.c
- *
- * NAND support for Marvell Orion SoC platforms
- *
- * Tzachi Perelstein <tzachi@marvell.com>
- *
- * 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/slab.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/of.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/partitions.h>
-#include <linux/clk.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <asm/sizes.h>
-#include <linux/platform_data/mtd-orion_nand.h>
-
-struct orion_nand_info {
- struct nand_chip chip;
- struct clk *clk;
-};
-
-static void orion_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
-{
- struct nand_chip *nc = mtd_to_nand(mtd);
- struct orion_nand_data *board = nand_get_controller_data(nc);
- u32 offs;
-
- if (cmd == NAND_CMD_NONE)
- return;
-
- if (ctrl & NAND_CLE)
- offs = (1 << board->cle);
- else if (ctrl & NAND_ALE)
- offs = (1 << board->ale);
- else
- return;
-
- if (nc->options & NAND_BUSWIDTH_16)
- offs <<= 1;
-
- writeb(cmd, nc->IO_ADDR_W + offs);
-}
-
-static void orion_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- void __iomem *io_base = chip->IO_ADDR_R;
-#if __LINUX_ARM_ARCH__ >= 5
- uint64_t *buf64;
-#endif
- int i = 0;
-
- while (len && (unsigned long)buf & 7) {
- *buf++ = readb(io_base);
- len--;
- }
-#if __LINUX_ARM_ARCH__ >= 5
- buf64 = (uint64_t *)buf;
- while (i < len/8) {
- /*
- * Since GCC has no proper constraint (PR 43518)
- * force x variable to r2/r3 registers as ldrd instruction
- * requires first register to be even.
- */
- register uint64_t x asm ("r2");
-
- asm volatile ("ldrd\t%0, [%1]" : "=&r" (x) : "r" (io_base));
- buf64[i++] = x;
- }
- i *= 8;
-#else
- readsl(io_base, buf, len/4);
- i = len / 4 * 4;
-#endif
- while (i < len)
- buf[i++] = readb(io_base);
-}
-
-static int __init orion_nand_probe(struct platform_device *pdev)
-{
- struct orion_nand_info *info;
- struct mtd_info *mtd;
- struct nand_chip *nc;
- struct orion_nand_data *board;
- struct resource *res;
- void __iomem *io_base;
- int ret = 0;
- u32 val = 0;
-
- info = devm_kzalloc(&pdev->dev,
- sizeof(struct orion_nand_info),
- GFP_KERNEL);
- if (!info)
- return -ENOMEM;
- nc = &info->chip;
- mtd = nand_to_mtd(nc);
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- io_base = devm_ioremap_resource(&pdev->dev, res);
-
- if (IS_ERR(io_base))
- return PTR_ERR(io_base);
-
- if (pdev->dev.of_node) {
- board = devm_kzalloc(&pdev->dev, sizeof(struct orion_nand_data),
- GFP_KERNEL);
- if (!board)
- return -ENOMEM;
- if (!of_property_read_u32(pdev->dev.of_node, "cle", &val))
- board->cle = (u8)val;
- else
- board->cle = 0;
- if (!of_property_read_u32(pdev->dev.of_node, "ale", &val))
- board->ale = (u8)val;
- else
- board->ale = 1;
- if (!of_property_read_u32(pdev->dev.of_node,
- "bank-width", &val))
- board->width = (u8)val * 8;
- else
- board->width = 8;
- if (!of_property_read_u32(pdev->dev.of_node,
- "chip-delay", &val))
- board->chip_delay = (u8)val;
- } else {
- board = dev_get_platdata(&pdev->dev);
- }
-
- mtd->dev.parent = &pdev->dev;
-
- nand_set_controller_data(nc, board);
- nand_set_flash_node(nc, pdev->dev.of_node);
- nc->IO_ADDR_R = nc->IO_ADDR_W = io_base;
- nc->cmd_ctrl = orion_nand_cmd_ctrl;
- nc->read_buf = orion_nand_read_buf;
- nc->ecc.mode = NAND_ECC_SOFT;
- nc->ecc.algo = NAND_ECC_HAMMING;
-
- if (board->chip_delay)
- nc->chip_delay = board->chip_delay;
-
- WARN(board->width > 16,
- "%d bit bus width out of range",
- board->width);
-
- if (board->width == 16)
- nc->options |= NAND_BUSWIDTH_16;
-
- if (board->dev_ready)
- nc->dev_ready = board->dev_ready;
-
- platform_set_drvdata(pdev, info);
-
- /* Not all platforms can gate the clock, so it is not
- an error if the clock does not exists. */
- info->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(info->clk)) {
- ret = PTR_ERR(info->clk);
- if (ret == -ENOENT) {
- info->clk = NULL;
- } else {
- dev_err(&pdev->dev, "failed to get clock!\n");
- return ret;
- }
- }
-
- ret = clk_prepare_enable(info->clk);
- if (ret) {
- dev_err(&pdev->dev, "failed to prepare clock!\n");
- return ret;
- }
-
- ret = nand_scan(mtd, 1);
- if (ret)
- goto no_dev;
-
- mtd->name = "orion_nand";
- ret = mtd_device_register(mtd, board->parts, board->nr_parts);
- if (ret) {
- nand_release(mtd);
- goto no_dev;
- }
-
- return 0;
-
-no_dev:
- clk_disable_unprepare(info->clk);
- return ret;
-}
-
-static int orion_nand_remove(struct platform_device *pdev)
-{
- struct orion_nand_info *info = platform_get_drvdata(pdev);
- struct nand_chip *chip = &info->chip;
- struct mtd_info *mtd = nand_to_mtd(chip);
-
- nand_release(mtd);
-
- clk_disable_unprepare(info->clk);
-
- return 0;
-}
-
-#ifdef CONFIG_OF
-static const struct of_device_id orion_nand_of_match_table[] = {
- { .compatible = "marvell,orion-nand", },
- {},
-};
-MODULE_DEVICE_TABLE(of, orion_nand_of_match_table);
-#endif
-
-static struct platform_driver orion_nand_driver = {
- .remove = orion_nand_remove,
- .driver = {
- .name = "orion_nand",
- .of_match_table = of_match_ptr(orion_nand_of_match_table),
- },
-};
-
-module_platform_driver_probe(orion_nand_driver, orion_nand_probe);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Tzachi Perelstein");
-MODULE_DESCRIPTION("NAND glue for Orion platforms");
-MODULE_ALIAS("platform:orion_nand");
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
deleted file mode 100644
index d1979c7dbe7e..000000000000
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ /dev/null
@@ -1,2105 +0,0 @@
-/*
- * drivers/mtd/nand/pxa3xx_nand.c
- *
- * Copyright © 2005 Intel Corporation
- * Copyright © 2006 Marvell International Ltd.
- *
- * 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.
- *
- * See Documentation/mtd/nand/pxa3xx-nand.txt for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/dmaengine.h>
-#include <linux/dma-mapping.h>
-#include <linux/dma/pxa-dma.h>
-#include <linux/delay.h>
-#include <linux/clk.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/partitions.h>
-#include <linux/io.h>
-#include <linux/iopoll.h>
-#include <linux/irq.h>
-#include <linux/slab.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/platform_data/mtd-nand-pxa3xx.h>
-#include <linux/mfd/syscon.h>
-#include <linux/regmap.h>
-
-#define CHIP_DELAY_TIMEOUT msecs_to_jiffies(200)
-#define NAND_STOP_DELAY msecs_to_jiffies(40)
-#define PAGE_CHUNK_SIZE (2048)
-
-/*
- * Define a buffer size for the initial command that detects the flash device:
- * STATUS, READID and PARAM.
- * ONFI param page is 256 bytes, and there are three redundant copies
- * to be read. JEDEC param page is 512 bytes, and there are also three
- * redundant copies to be read.
- * Hence this buffer should be at least 512 x 3. Let's pick 2048.
- */
-#define INIT_BUFFER_SIZE 2048
-
-/* System control register and bit to enable NAND on some SoCs */
-#define GENCONF_SOC_DEVICE_MUX 0x208
-#define GENCONF_SOC_DEVICE_MUX_NFC_EN BIT(0)
-
-/* registers and bit definitions */
-#define NDCR (0x00) /* Control register */
-#define NDTR0CS0 (0x04) /* Timing Parameter 0 for CS0 */
-#define NDTR1CS0 (0x0C) /* Timing Parameter 1 for CS0 */
-#define NDSR (0x14) /* Status Register */
-#define NDPCR (0x18) /* Page Count Register */
-#define NDBDR0 (0x1C) /* Bad Block Register 0 */
-#define NDBDR1 (0x20) /* Bad Block Register 1 */
-#define NDECCCTRL (0x28) /* ECC control */
-#define NDDB (0x40) /* Data Buffer */
-#define NDCB0 (0x48) /* Command Buffer0 */
-#define NDCB1 (0x4C) /* Command Buffer1 */
-#define NDCB2 (0x50) /* Command Buffer2 */
-
-#define NDCR_SPARE_EN (0x1 << 31)
-#define NDCR_ECC_EN (0x1 << 30)
-#define NDCR_DMA_EN (0x1 << 29)
-#define NDCR_ND_RUN (0x1 << 28)
-#define NDCR_DWIDTH_C (0x1 << 27)
-#define NDCR_DWIDTH_M (0x1 << 26)
-#define NDCR_PAGE_SZ (0x1 << 24)
-#define NDCR_NCSX (0x1 << 23)
-#define NDCR_ND_MODE (0x3 << 21)
-#define NDCR_NAND_MODE (0x0)
-#define NDCR_CLR_PG_CNT (0x1 << 20)
-#define NFCV1_NDCR_ARB_CNTL (0x1 << 19)
-#define NFCV2_NDCR_STOP_ON_UNCOR (0x1 << 19)
-#define NDCR_RD_ID_CNT_MASK (0x7 << 16)
-#define NDCR_RD_ID_CNT(x) (((x) << 16) & NDCR_RD_ID_CNT_MASK)
-
-#define NDCR_RA_START (0x1 << 15)
-#define NDCR_PG_PER_BLK (0x1 << 14)
-#define NDCR_ND_ARB_EN (0x1 << 12)
-#define NDCR_INT_MASK (0xFFF)
-
-#define NDSR_MASK (0xfff)
-#define NDSR_ERR_CNT_OFF (16)
-#define NDSR_ERR_CNT_MASK (0x1f)
-#define NDSR_ERR_CNT(sr) ((sr >> NDSR_ERR_CNT_OFF) & NDSR_ERR_CNT_MASK)
-#define NDSR_RDY (0x1 << 12)
-#define NDSR_FLASH_RDY (0x1 << 11)
-#define NDSR_CS0_PAGED (0x1 << 10)
-#define NDSR_CS1_PAGED (0x1 << 9)
-#define NDSR_CS0_CMDD (0x1 << 8)
-#define NDSR_CS1_CMDD (0x1 << 7)
-#define NDSR_CS0_BBD (0x1 << 6)
-#define NDSR_CS1_BBD (0x1 << 5)
-#define NDSR_UNCORERR (0x1 << 4)
-#define NDSR_CORERR (0x1 << 3)
-#define NDSR_WRDREQ (0x1 << 2)
-#define NDSR_RDDREQ (0x1 << 1)
-#define NDSR_WRCMDREQ (0x1)
-
-#define NDCB0_LEN_OVRD (0x1 << 28)
-#define NDCB0_ST_ROW_EN (0x1 << 26)
-#define NDCB0_AUTO_RS (0x1 << 25)
-#define NDCB0_CSEL (0x1 << 24)
-#define NDCB0_EXT_CMD_TYPE_MASK (0x7 << 29)
-#define NDCB0_EXT_CMD_TYPE(x) (((x) << 29) & NDCB0_EXT_CMD_TYPE_MASK)
-#define NDCB0_CMD_TYPE_MASK (0x7 << 21)
-#define NDCB0_CMD_TYPE(x) (((x) << 21) & NDCB0_CMD_TYPE_MASK)
-#define NDCB0_NC (0x1 << 20)
-#define NDCB0_DBC (0x1 << 19)
-#define NDCB0_ADDR_CYC_MASK (0x7 << 16)
-#define NDCB0_ADDR_CYC(x) (((x) << 16) & NDCB0_ADDR_CYC_MASK)
-#define NDCB0_CMD2_MASK (0xff << 8)
-#define NDCB0_CMD1_MASK (0xff)
-#define NDCB0_ADDR_CYC_SHIFT (16)
-
-#define EXT_CMD_TYPE_DISPATCH 6 /* Command dispatch */
-#define EXT_CMD_TYPE_NAKED_RW 5 /* Naked read or Naked write */
-#define EXT_CMD_TYPE_READ 4 /* Read */
-#define EXT_CMD_TYPE_DISP_WR 4 /* Command dispatch with write */
-#define EXT_CMD_TYPE_FINAL 3 /* Final command */
-#define EXT_CMD_TYPE_LAST_RW 1 /* Last naked read/write */
-#define EXT_CMD_TYPE_MONO 0 /* Monolithic read/write */
-
-/*
- * This should be large enough to read 'ONFI' and 'JEDEC'.
- * Let's use 7 bytes, which is the maximum ID count supported
- * by the controller (see NDCR_RD_ID_CNT_MASK).
- */
-#define READ_ID_BYTES 7
-
-/* macros for registers read/write */
-#define nand_writel(info, off, val) \
- do { \
- dev_vdbg(&info->pdev->dev, \
- "%s():%d nand_writel(0x%x, 0x%04x)\n", \
- __func__, __LINE__, (val), (off)); \
- writel_relaxed((val), (info)->mmio_base + (off)); \
- } while (0)
-
-#define nand_readl(info, off) \
- ({ \
- unsigned int _v; \
- _v = readl_relaxed((info)->mmio_base + (off)); \
- dev_vdbg(&info->pdev->dev, \
- "%s():%d nand_readl(0x%04x) = 0x%x\n", \
- __func__, __LINE__, (off), _v); \
- _v; \
- })
-
-/* error code and state */
-enum {
- ERR_NONE = 0,
- ERR_DMABUSERR = -1,
- ERR_SENDCMD = -2,
- ERR_UNCORERR = -3,
- ERR_BBERR = -4,
- ERR_CORERR = -5,
-};
-
-enum {
- STATE_IDLE = 0,
- STATE_PREPARED,
- STATE_CMD_HANDLE,
- STATE_DMA_READING,
- STATE_DMA_WRITING,
- STATE_DMA_DONE,
- STATE_PIO_READING,
- STATE_PIO_WRITING,
- STATE_CMD_DONE,
- STATE_READY,
-};
-
-enum pxa3xx_nand_variant {
- PXA3XX_NAND_VARIANT_PXA,
- PXA3XX_NAND_VARIANT_ARMADA370,
- PXA3XX_NAND_VARIANT_ARMADA_8K,
-};
-
-struct pxa3xx_nand_host {
- struct nand_chip chip;
- void *info_data;
-
- /* page size of attached chip */
- int use_ecc;
- int cs;
-
- /* calculated from pxa3xx_nand_flash data */
- unsigned int col_addr_cycles;
- unsigned int row_addr_cycles;
-};
-
-struct pxa3xx_nand_info {
- struct nand_hw_control controller;
- struct platform_device *pdev;
-
- struct clk *clk;
- void __iomem *mmio_base;
- unsigned long mmio_phys;
- struct completion cmd_complete, dev_ready;
-
- unsigned int buf_start;
- unsigned int buf_count;
- unsigned int buf_size;
- unsigned int data_buff_pos;
- unsigned int oob_buff_pos;
-
- /* DMA information */
- struct scatterlist sg;
- enum dma_data_direction dma_dir;
- struct dma_chan *dma_chan;
- dma_cookie_t dma_cookie;
- int drcmr_dat;
-
- unsigned char *data_buff;
- unsigned char *oob_buff;
- dma_addr_t data_buff_phys;
- int data_dma_ch;
-
- struct pxa3xx_nand_host *host[NUM_CHIP_SELECT];
- unsigned int state;
-
- /*
- * This driver supports NFCv1 (as found in PXA SoC)
- * and NFCv2 (as found in Armada 370/XP SoC).
- */
- enum pxa3xx_nand_variant variant;
-
- int cs;
- int use_ecc; /* use HW ECC ? */
- int ecc_bch; /* using BCH ECC? */
- int use_dma; /* use DMA ? */
- int use_spare; /* use spare ? */
- int need_wait;
-
- /* Amount of real data per full chunk */
- unsigned int chunk_size;
-
- /* Amount of spare data per full chunk */
- unsigned int spare_size;
-
- /* Number of full chunks (i.e chunk_size + spare_size) */
- unsigned int nfullchunks;
-
- /*
- * Total number of chunks. If equal to nfullchunks, then there
- * are only full chunks. Otherwise, there is one last chunk of
- * size (last_chunk_size + last_spare_size)
- */
- unsigned int ntotalchunks;
-
- /* Amount of real data in the last chunk */
- unsigned int last_chunk_size;
-
- /* Amount of spare data in the last chunk */
- unsigned int last_spare_size;
-
- unsigned int ecc_size;
- unsigned int ecc_err_cnt;
- unsigned int max_bitflips;
- int retcode;
-
- /*
- * Variables only valid during command
- * execution. step_chunk_size and step_spare_size is the
- * amount of real data and spare data in the current
- * chunk. cur_chunk is the current chunk being
- * read/programmed.
- */
- unsigned int step_chunk_size;
- unsigned int step_spare_size;
- unsigned int cur_chunk;
-
- /* cached register value */
- uint32_t reg_ndcr;
- uint32_t ndtr0cs0;
- uint32_t ndtr1cs0;
-
- /* generated NDCBx register values */
- uint32_t ndcb0;
- uint32_t ndcb1;
- uint32_t ndcb2;
- uint32_t ndcb3;
-};
-
-static bool use_dma = 1;
-module_param(use_dma, bool, 0444);
-MODULE_PARM_DESC(use_dma, "enable DMA for data transferring to/from NAND HW");
-
-struct pxa3xx_nand_timing {
- unsigned int tCH; /* Enable signal hold time */
- unsigned int tCS; /* Enable signal setup time */
- unsigned int tWH; /* ND_nWE high duration */
- unsigned int tWP; /* ND_nWE pulse time */
- unsigned int tRH; /* ND_nRE high duration */
- unsigned int tRP; /* ND_nRE pulse width */
- unsigned int tR; /* ND_nWE high to ND_nRE low for read */
- unsigned int tWHR; /* ND_nWE high to ND_nRE low for status read */
- unsigned int tAR; /* ND_ALE low to ND_nRE low delay */
-};
-
-struct pxa3xx_nand_flash {
- uint32_t chip_id;
- unsigned int flash_width; /* Width of Flash memory (DWIDTH_M) */
- unsigned int dfc_width; /* Width of flash controller(DWIDTH_C) */
- struct pxa3xx_nand_timing *timing; /* NAND Flash timing */
-};
-
-static struct pxa3xx_nand_timing timing[] = {
- { 40, 80, 60, 100, 80, 100, 90000, 400, 40, },
- { 10, 0, 20, 40, 30, 40, 11123, 110, 10, },
- { 10, 25, 15, 25, 15, 30, 25000, 60, 10, },
- { 10, 35, 15, 25, 15, 25, 25000, 60, 10, },
-};
-
-static struct pxa3xx_nand_flash builtin_flash_types[] = {
- { 0x46ec, 16, 16, &timing[1] },
- { 0xdaec, 8, 8, &timing[1] },
- { 0xd7ec, 8, 8, &timing[1] },
- { 0xa12c, 8, 8, &timing[2] },
- { 0xb12c, 16, 16, &timing[2] },
- { 0xdc2c, 8, 8, &timing[2] },
- { 0xcc2c, 16, 16, &timing[2] },
- { 0xba20, 16, 16, &timing[3] },
-};
-
-static int pxa3xx_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
- struct pxa3xx_nand_info *info = host->info_data;
- int nchunks = mtd->writesize / info->chunk_size;
-
- if (section >= nchunks)
- return -ERANGE;
-
- oobregion->offset = ((info->ecc_size + info->spare_size) * section) +
- info->spare_size;
- oobregion->length = info->ecc_size;
-
- return 0;
-}
-
-static int pxa3xx_ooblayout_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
- struct pxa3xx_nand_info *info = host->info_data;
- int nchunks = mtd->writesize / info->chunk_size;
-
- if (section >= nchunks)
- return -ERANGE;
-
- if (!info->spare_size)
- return 0;
-
- oobregion->offset = section * (info->ecc_size + info->spare_size);
- oobregion->length = info->spare_size;
- if (!section) {
- /*
- * Bootrom looks in bytes 0 & 5 for bad blocks for the
- * 4KB page / 4bit BCH combination.
- */
- if (mtd->writesize == 4096 && info->chunk_size == 2048) {
- oobregion->offset += 6;
- oobregion->length -= 6;
- } else {
- oobregion->offset += 2;
- oobregion->length -= 2;
- }
- }
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops pxa3xx_ooblayout_ops = {
- .ecc = pxa3xx_ooblayout_ecc,
- .free = pxa3xx_ooblayout_free,
-};
-
-static u8 bbt_pattern[] = {'M', 'V', 'B', 'b', 't', '0' };
-static u8 bbt_mirror_pattern[] = {'1', 't', 'b', 'B', 'V', 'M' };
-
-static struct nand_bbt_descr bbt_main_descr = {
- .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
- | NAND_BBT_2BIT | NAND_BBT_VERSION,
- .offs = 8,
- .len = 6,
- .veroffs = 14,
- .maxblocks = 8, /* Last 8 blocks in each chip */
- .pattern = bbt_pattern
-};
-
-static struct nand_bbt_descr bbt_mirror_descr = {
- .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
- | NAND_BBT_2BIT | NAND_BBT_VERSION,
- .offs = 8,
- .len = 6,
- .veroffs = 14,
- .maxblocks = 8, /* Last 8 blocks in each chip */
- .pattern = bbt_mirror_pattern
-};
-
-#define NDTR0_tCH(c) (min((c), 7) << 19)
-#define NDTR0_tCS(c) (min((c), 7) << 16)
-#define NDTR0_tWH(c) (min((c), 7) << 11)
-#define NDTR0_tWP(c) (min((c), 7) << 8)
-#define NDTR0_tRH(c) (min((c), 7) << 3)
-#define NDTR0_tRP(c) (min((c), 7) << 0)
-
-#define NDTR1_tR(c) (min((c), 65535) << 16)
-#define NDTR1_tWHR(c) (min((c), 15) << 4)
-#define NDTR1_tAR(c) (min((c), 15) << 0)
-
-/* convert nano-seconds to nand flash controller clock cycles */
-#define ns2cycle(ns, clk) (int)((ns) * (clk / 1000000) / 1000)
-
-static const struct of_device_id pxa3xx_nand_dt_ids[] = {
- {
- .compatible = "marvell,pxa3xx-nand",
- .data = (void *)PXA3XX_NAND_VARIANT_PXA,
- },
- {
- .compatible = "marvell,armada370-nand",
- .data = (void *)PXA3XX_NAND_VARIANT_ARMADA370,
- },
- {
- .compatible = "marvell,armada-8k-nand",
- .data = (void *)PXA3XX_NAND_VARIANT_ARMADA_8K,
- },
- {}
-};
-MODULE_DEVICE_TABLE(of, pxa3xx_nand_dt_ids);
-
-static enum pxa3xx_nand_variant
-pxa3xx_nand_get_variant(struct platform_device *pdev)
-{
- const struct of_device_id *of_id =
- of_match_device(pxa3xx_nand_dt_ids, &pdev->dev);
- if (!of_id)
- return PXA3XX_NAND_VARIANT_PXA;
- return (enum pxa3xx_nand_variant)of_id->data;
-}
-
-static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
- const struct pxa3xx_nand_timing *t)
-{
- struct pxa3xx_nand_info *info = host->info_data;
- unsigned long nand_clk = clk_get_rate(info->clk);
- uint32_t ndtr0, ndtr1;
-
- ndtr0 = NDTR0_tCH(ns2cycle(t->tCH, nand_clk)) |
- NDTR0_tCS(ns2cycle(t->tCS, nand_clk)) |
- NDTR0_tWH(ns2cycle(t->tWH, nand_clk)) |
- NDTR0_tWP(ns2cycle(t->tWP, nand_clk)) |
- NDTR0_tRH(ns2cycle(t->tRH, nand_clk)) |
- NDTR0_tRP(ns2cycle(t->tRP, nand_clk));
-
- ndtr1 = NDTR1_tR(ns2cycle(t->tR, nand_clk)) |
- NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) |
- NDTR1_tAR(ns2cycle(t->tAR, nand_clk));
-
- info->ndtr0cs0 = ndtr0;
- info->ndtr1cs0 = ndtr1;
- nand_writel(info, NDTR0CS0, ndtr0);
- nand_writel(info, NDTR1CS0, ndtr1);
-}
-
-static void pxa3xx_nand_set_sdr_timing(struct pxa3xx_nand_host *host,
- const struct nand_sdr_timings *t)
-{
- struct pxa3xx_nand_info *info = host->info_data;
- struct nand_chip *chip = &host->chip;
- unsigned long nand_clk = clk_get_rate(info->clk);
- uint32_t ndtr0, ndtr1;
-
- u32 tCH_min = DIV_ROUND_UP(t->tCH_min, 1000);
- u32 tCS_min = DIV_ROUND_UP(t->tCS_min, 1000);
- u32 tWH_min = DIV_ROUND_UP(t->tWH_min, 1000);
- u32 tWP_min = DIV_ROUND_UP(t->tWC_min - t->tWH_min, 1000);
- u32 tREH_min = DIV_ROUND_UP(t->tREH_min, 1000);
- u32 tRP_min = DIV_ROUND_UP(t->tRC_min - t->tREH_min, 1000);
- u32 tR = chip->chip_delay * 1000;
- u32 tWHR_min = DIV_ROUND_UP(t->tWHR_min, 1000);
- u32 tAR_min = DIV_ROUND_UP(t->tAR_min, 1000);
-
- /* fallback to a default value if tR = 0 */
- if (!tR)
- tR = 20000;
-
- ndtr0 = NDTR0_tCH(ns2cycle(tCH_min, nand_clk)) |
- NDTR0_tCS(ns2cycle(tCS_min, nand_clk)) |
- NDTR0_tWH(ns2cycle(tWH_min, nand_clk)) |
- NDTR0_tWP(ns2cycle(tWP_min, nand_clk)) |
- NDTR0_tRH(ns2cycle(tREH_min, nand_clk)) |
- NDTR0_tRP(ns2cycle(tRP_min, nand_clk));
-
- ndtr1 = NDTR1_tR(ns2cycle(tR, nand_clk)) |
- NDTR1_tWHR(ns2cycle(tWHR_min, nand_clk)) |
- NDTR1_tAR(ns2cycle(tAR_min, nand_clk));
-
- info->ndtr0cs0 = ndtr0;
- info->ndtr1cs0 = ndtr1;
- nand_writel(info, NDTR0CS0, ndtr0);
- nand_writel(info, NDTR1CS0, ndtr1);
-}
-
-static int pxa3xx_nand_init_timings_compat(struct pxa3xx_nand_host *host,
- unsigned int *flash_width,
- unsigned int *dfc_width)
-{
- struct nand_chip *chip = &host->chip;
- struct pxa3xx_nand_info *info = host->info_data;
- const struct pxa3xx_nand_flash *f = NULL;
- int i, id, ntypes;
- u8 idbuf[2];
-
- ntypes = ARRAY_SIZE(builtin_flash_types);
-
- nand_readid_op(chip, 0, idbuf, sizeof(idbuf));
- id = idbuf[0] | (idbuf[1] << 8);
-
- for (i = 0; i < ntypes; i++) {
- f = &builtin_flash_types[i];
-
- if (f->chip_id == id)
- break;
- }
-
- if (i == ntypes) {
- dev_err(&info->pdev->dev, "Error: timings not found\n");
- return -EINVAL;
- }
-
- pxa3xx_nand_set_timing(host, f->timing);
-
- *flash_width = f->flash_width;
- *dfc_width = f->dfc_width;
-
- return 0;
-}
-
-static int pxa3xx_nand_init_timings_onfi(struct pxa3xx_nand_host *host,
- int mode)
-{
- const struct nand_sdr_timings *timings;
-
- mode = fls(mode) - 1;
- if (mode < 0)
- mode = 0;
-
- timings = onfi_async_timing_mode_to_sdr_timings(mode);
- if (IS_ERR(timings))
- return PTR_ERR(timings);
-
- pxa3xx_nand_set_sdr_timing(host, timings);
-
- return 0;
-}
-
-static int pxa3xx_nand_init(struct pxa3xx_nand_host *host)
-{
- struct nand_chip *chip = &host->chip;
- struct pxa3xx_nand_info *info = host->info_data;
- unsigned int flash_width = 0, dfc_width = 0;
- int mode, err;
-
- mode = onfi_get_async_timing_mode(chip);
- if (mode == ONFI_TIMING_MODE_UNKNOWN) {
- err = pxa3xx_nand_init_timings_compat(host, &flash_width,
- &dfc_width);
- if (err)
- return err;
-
- if (flash_width == 16) {
- info->reg_ndcr |= NDCR_DWIDTH_M;
- chip->options |= NAND_BUSWIDTH_16;
- }
-
- info->reg_ndcr |= (dfc_width == 16) ? NDCR_DWIDTH_C : 0;
- } else {
- err = pxa3xx_nand_init_timings_onfi(host, mode);
- if (err)
- return err;
- }
-
- return 0;
-}
-
-/**
- * NOTE: it is a must to set ND_RUN firstly, then write
- * command buffer, otherwise, it does not work.
- * We enable all the interrupt at the same time, and
- * let pxa3xx_nand_irq to handle all logic.
- */
-static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
-{
- uint32_t ndcr;
-
- ndcr = info->reg_ndcr;
-
- if (info->use_ecc) {
- ndcr |= NDCR_ECC_EN;
- if (info->ecc_bch)
- nand_writel(info, NDECCCTRL, 0x1);
- } else {
- ndcr &= ~NDCR_ECC_EN;
- if (info->ecc_bch)
- nand_writel(info, NDECCCTRL, 0x0);
- }
-
- if (info->use_dma)
- ndcr |= NDCR_DMA_EN;
- else
- ndcr &= ~NDCR_DMA_EN;
-
- if (info->use_spare)
- ndcr |= NDCR_SPARE_EN;
- else
- ndcr &= ~NDCR_SPARE_EN;
-
- ndcr |= NDCR_ND_RUN;
-
- /* clear status bits and run */
- nand_writel(info, NDSR, NDSR_MASK);
- nand_writel(info, NDCR, 0);
- nand_writel(info, NDCR, ndcr);
-}
-
-static void pxa3xx_nand_stop(struct pxa3xx_nand_info *info)
-{
- uint32_t ndcr;
- int timeout = NAND_STOP_DELAY;
-
- /* wait RUN bit in NDCR become 0 */
- ndcr = nand_readl(info, NDCR);
- while ((ndcr & NDCR_ND_RUN) && (timeout-- > 0)) {
- ndcr = nand_readl(info, NDCR);
- udelay(1);
- }
-
- if (timeout <= 0) {
- ndcr &= ~NDCR_ND_RUN;
- nand_writel(info, NDCR, ndcr);
- }
- if (info->dma_chan)
- dmaengine_terminate_all(info->dma_chan);
-
- /* clear status bits */
- nand_writel(info, NDSR, NDSR_MASK);
-}
-
-static void __maybe_unused
-enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
-{
- uint32_t ndcr;
-
- ndcr = nand_readl(info, NDCR);
- nand_writel(info, NDCR, ndcr & ~int_mask);
-}
-
-static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
-{
- uint32_t ndcr;
-
- ndcr = nand_readl(info, NDCR);
- nand_writel(info, NDCR, ndcr | int_mask);
-}
-
-static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len)
-{
- if (info->ecc_bch) {
- u32 val;
- int ret;
-
- /*
- * According to the datasheet, when reading from NDDB
- * with BCH enabled, after each 32 bytes reads, we
- * have to make sure that the NDSR.RDDREQ bit is set.
- *
- * Drain the FIFO 8 32 bits reads at a time, and skip
- * the polling on the last read.
- */
- while (len > 8) {
- ioread32_rep(info->mmio_base + NDDB, data, 8);
-
- ret = readl_relaxed_poll_timeout(info->mmio_base + NDSR, val,
- val & NDSR_RDDREQ, 1000, 5000);
- if (ret) {
- dev_err(&info->pdev->dev,
- "Timeout on RDDREQ while draining the FIFO\n");
- return;
- }
-
- data += 32;
- len -= 8;
- }
- }
-
- ioread32_rep(info->mmio_base + NDDB, data, len);
-}
-
-static void handle_data_pio(struct pxa3xx_nand_info *info)
-{
- switch (info->state) {
- case STATE_PIO_WRITING:
- if (info->step_chunk_size)
- writesl(info->mmio_base + NDDB,
- info->data_buff + info->data_buff_pos,
- DIV_ROUND_UP(info->step_chunk_size, 4));
-
- if (info->step_spare_size)
- writesl(info->mmio_base + NDDB,
- info->oob_buff + info->oob_buff_pos,
- DIV_ROUND_UP(info->step_spare_size, 4));
- break;
- case STATE_PIO_READING:
- if (info->step_chunk_size)
- drain_fifo(info,
- info->data_buff + info->data_buff_pos,
- DIV_ROUND_UP(info->step_chunk_size, 4));
-
- if (info->step_spare_size)
- drain_fifo(info,
- info->oob_buff + info->oob_buff_pos,
- DIV_ROUND_UP(info->step_spare_size, 4));
- break;
- default:
- dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
- info->state);
- BUG();
- }
-
- /* Update buffer pointers for multi-page read/write */
- info->data_buff_pos += info->step_chunk_size;
- info->oob_buff_pos += info->step_spare_size;
-}
-
-static void pxa3xx_nand_data_dma_irq(void *data)
-{
- struct pxa3xx_nand_info *info = data;
- struct dma_tx_state state;
- enum dma_status status;
-
- status = dmaengine_tx_status(info->dma_chan, info->dma_cookie, &state);
- if (likely(status == DMA_COMPLETE)) {
- info->state = STATE_DMA_DONE;
- } else {
- dev_err(&info->pdev->dev, "DMA error on data channel\n");
- info->retcode = ERR_DMABUSERR;
- }
- dma_unmap_sg(info->dma_chan->device->dev, &info->sg, 1, info->dma_dir);
-
- nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
- enable_int(info, NDCR_INT_MASK);
-}
-
-static void start_data_dma(struct pxa3xx_nand_info *info)
-{
- enum dma_transfer_direction direction;
- struct dma_async_tx_descriptor *tx;
-
- switch (info->state) {
- case STATE_DMA_WRITING:
- info->dma_dir = DMA_TO_DEVICE;
- direction = DMA_MEM_TO_DEV;
- break;
- case STATE_DMA_READING:
- info->dma_dir = DMA_FROM_DEVICE;
- direction = DMA_DEV_TO_MEM;
- break;
- default:
- dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
- info->state);
- BUG();
- }
- info->sg.length = info->chunk_size;
- if (info->use_spare)
- info->sg.length += info->spare_size + info->ecc_size;
- dma_map_sg(info->dma_chan->device->dev, &info->sg, 1, info->dma_dir);
-
- tx = dmaengine_prep_slave_sg(info->dma_chan, &info->sg, 1, direction,
- DMA_PREP_INTERRUPT);
- if (!tx) {
- dev_err(&info->pdev->dev, "prep_slave_sg() failed\n");
- return;
- }
- tx->callback = pxa3xx_nand_data_dma_irq;
- tx->callback_param = info;
- info->dma_cookie = dmaengine_submit(tx);
- dma_async_issue_pending(info->dma_chan);
- dev_dbg(&info->pdev->dev, "%s(dir=%d cookie=%x size=%u)\n",
- __func__, direction, info->dma_cookie, info->sg.length);
-}
-
-static irqreturn_t pxa3xx_nand_irq_thread(int irq, void *data)
-{
- struct pxa3xx_nand_info *info = data;
-
- handle_data_pio(info);
-
- info->state = STATE_CMD_DONE;
- nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
-{
- struct pxa3xx_nand_info *info = devid;
- unsigned int status, is_completed = 0, is_ready = 0;
- unsigned int ready, cmd_done;
- irqreturn_t ret = IRQ_HANDLED;
-
- if (info->cs == 0) {
- ready = NDSR_FLASH_RDY;
- cmd_done = NDSR_CS0_CMDD;
- } else {
- ready = NDSR_RDY;
- cmd_done = NDSR_CS1_CMDD;
- }
-
- status = nand_readl(info, NDSR);
-
- if (status & NDSR_UNCORERR)
- info->retcode = ERR_UNCORERR;
- if (status & NDSR_CORERR) {
- info->retcode = ERR_CORERR;
- if ((info->variant == PXA3XX_NAND_VARIANT_ARMADA370 ||
- info->variant == PXA3XX_NAND_VARIANT_ARMADA_8K) &&
- info->ecc_bch)
- info->ecc_err_cnt = NDSR_ERR_CNT(status);
- else
- info->ecc_err_cnt = 1;
-
- /*
- * Each chunk composing a page is corrected independently,
- * and we need to store maximum number of corrected bitflips
- * to return it to the MTD layer in ecc.read_page().
- */
- info->max_bitflips = max_t(unsigned int,
- info->max_bitflips,
- info->ecc_err_cnt);
- }
- if (status & (NDSR_RDDREQ | NDSR_WRDREQ)) {
- /* whether use dma to transfer data */
- if (info->use_dma) {
- disable_int(info, NDCR_INT_MASK);
- info->state = (status & NDSR_RDDREQ) ?
- STATE_DMA_READING : STATE_DMA_WRITING;
- start_data_dma(info);
- goto NORMAL_IRQ_EXIT;
- } else {
- info->state = (status & NDSR_RDDREQ) ?
- STATE_PIO_READING : STATE_PIO_WRITING;
- ret = IRQ_WAKE_THREAD;
- goto NORMAL_IRQ_EXIT;
- }
- }
- if (status & cmd_done) {
- info->state = STATE_CMD_DONE;
- is_completed = 1;
- }
- if (status & ready) {
- info->state = STATE_READY;
- is_ready = 1;
- }
-
- /*
- * Clear all status bit before issuing the next command, which
- * can and will alter the status bits and will deserve a new
- * interrupt on its own. This lets the controller exit the IRQ
- */
- nand_writel(info, NDSR, status);
-
- if (status & NDSR_WRCMDREQ) {
- status &= ~NDSR_WRCMDREQ;
- info->state = STATE_CMD_HANDLE;
-
- /*
- * Command buffer registers NDCB{0-2} (and optionally NDCB3)
- * must be loaded by writing directly either 12 or 16
- * bytes directly to NDCB0, four bytes at a time.
- *
- * Direct write access to NDCB1, NDCB2 and NDCB3 is ignored
- * but each NDCBx register can be read.
- */
- nand_writel(info, NDCB0, info->ndcb0);
- nand_writel(info, NDCB0, info->ndcb1);
- nand_writel(info, NDCB0, info->ndcb2);
-
- /* NDCB3 register is available in NFCv2 (Armada 370/XP SoC) */
- if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370 ||
- info->variant == PXA3XX_NAND_VARIANT_ARMADA_8K)
- nand_writel(info, NDCB0, info->ndcb3);
- }
-
- if (is_completed)
- complete(&info->cmd_complete);
- if (is_ready)
- complete(&info->dev_ready);
-NORMAL_IRQ_EXIT:
- return ret;
-}
-
-static inline int is_buf_blank(uint8_t *buf, size_t len)
-{
- for (; len > 0; len--)
- if (*buf++ != 0xff)
- return 0;
- return 1;
-}
-
-static void set_command_address(struct pxa3xx_nand_info *info,
- unsigned int page_size, uint16_t column, int page_addr)
-{
- /* small page addr setting */
- if (page_size < PAGE_CHUNK_SIZE) {
- info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
- | (column & 0xFF);
-
- info->ndcb2 = 0;
- } else {
- info->ndcb1 = ((page_addr & 0xFFFF) << 16)
- | (column & 0xFFFF);
-
- if (page_addr & 0xFF0000)
- info->ndcb2 = (page_addr & 0xFF0000) >> 16;
- else
- info->ndcb2 = 0;
- }
-}
-
-static void prepare_start_command(struct pxa3xx_nand_info *info, int command)
-{
- struct pxa3xx_nand_host *host = info->host[info->cs];
- struct mtd_info *mtd = nand_to_mtd(&host->chip);
-
- /* reset data and oob column point to handle data */
- info->buf_start = 0;
- info->buf_count = 0;
- info->data_buff_pos = 0;
- info->oob_buff_pos = 0;
- info->step_chunk_size = 0;
- info->step_spare_size = 0;
- info->cur_chunk = 0;
- info->use_ecc = 0;
- info->use_spare = 1;
- info->retcode = ERR_NONE;
- info->ecc_err_cnt = 0;
- info->ndcb3 = 0;
- info->need_wait = 0;
-
- switch (command) {
- case NAND_CMD_READ0:
- case NAND_CMD_READOOB:
- case NAND_CMD_PAGEPROG:
- info->use_ecc = 1;
- break;
- case NAND_CMD_PARAM:
- info->use_spare = 0;
- break;
- default:
- info->ndcb1 = 0;
- info->ndcb2 = 0;
- break;
- }
-
- /*
- * If we are about to issue a read command, or about to set
- * the write address, then clean the data buffer.
- */
- if (command == NAND_CMD_READ0 ||
- command == NAND_CMD_READOOB ||
- command == NAND_CMD_SEQIN) {
-
- info->buf_count = mtd->writesize + mtd->oobsize;
- memset(info->data_buff, 0xFF, info->buf_count);
- }
-
-}
-
-static int prepare_set_command(struct pxa3xx_nand_info *info, int command,
- int ext_cmd_type, uint16_t column, int page_addr)
-{
- int addr_cycle, exec_cmd;
- struct pxa3xx_nand_host *host;
- struct mtd_info *mtd;
-
- host = info->host[info->cs];
- mtd = nand_to_mtd(&host->chip);
- addr_cycle = 0;
- exec_cmd = 1;
-
- if (info->cs != 0)
- info->ndcb0 = NDCB0_CSEL;
- else
- info->ndcb0 = 0;
-
- if (command == NAND_CMD_SEQIN)
- exec_cmd = 0;
-
- addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles
- + host->col_addr_cycles);
-
- switch (command) {
- case NAND_CMD_READOOB:
- case NAND_CMD_READ0:
- info->buf_start = column;
- info->ndcb0 |= NDCB0_CMD_TYPE(0)
- | addr_cycle
- | NAND_CMD_READ0;
-
- if (command == NAND_CMD_READOOB)
- info->buf_start += mtd->writesize;
-
- if (info->cur_chunk < info->nfullchunks) {
- info->step_chunk_size = info->chunk_size;
- info->step_spare_size = info->spare_size;
- } else {
- info->step_chunk_size = info->last_chunk_size;
- info->step_spare_size = info->last_spare_size;
- }
-
- /*
- * Multiple page read needs an 'extended command type' field,
- * which is either naked-read or last-read according to the
- * state.
- */
- if (mtd->writesize == PAGE_CHUNK_SIZE) {
- info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8);
- } else if (mtd->writesize > PAGE_CHUNK_SIZE) {
- info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8)
- | NDCB0_LEN_OVRD
- | NDCB0_EXT_CMD_TYPE(ext_cmd_type);
- info->ndcb3 = info->step_chunk_size +
- info->step_spare_size;
- }
-
- set_command_address(info, mtd->writesize, column, page_addr);
- break;
-
- case NAND_CMD_SEQIN:
-
- info->buf_start = column;
- set_command_address(info, mtd->writesize, 0, page_addr);
-
- /*
- * Multiple page programming needs to execute the initial
- * SEQIN command that sets the page address.
- */
- if (mtd->writesize > PAGE_CHUNK_SIZE) {
- info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
- | NDCB0_EXT_CMD_TYPE(ext_cmd_type)
- | addr_cycle
- | command;
- exec_cmd = 1;
- }
- break;
-
- case NAND_CMD_PAGEPROG:
- if (is_buf_blank(info->data_buff,
- (mtd->writesize + mtd->oobsize))) {
- exec_cmd = 0;
- break;
- }
-
- if (info->cur_chunk < info->nfullchunks) {
- info->step_chunk_size = info->chunk_size;
- info->step_spare_size = info->spare_size;
- } else {
- info->step_chunk_size = info->last_chunk_size;
- info->step_spare_size = info->last_spare_size;
- }
-
- /* Second command setting for large pages */
- if (mtd->writesize > PAGE_CHUNK_SIZE) {
- /*
- * Multiple page write uses the 'extended command'
- * field. This can be used to issue a command dispatch
- * or a naked-write depending on the current stage.
- */
- info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
- | NDCB0_LEN_OVRD
- | NDCB0_EXT_CMD_TYPE(ext_cmd_type);
- info->ndcb3 = info->step_chunk_size +
- info->step_spare_size;
-
- /*
- * This is the command dispatch that completes a chunked
- * page program operation.
- */
- if (info->cur_chunk == info->ntotalchunks) {
- info->ndcb0 = NDCB0_CMD_TYPE(0x1)
- | NDCB0_EXT_CMD_TYPE(ext_cmd_type)
- | command;
- info->ndcb1 = 0;
- info->ndcb2 = 0;
- info->ndcb3 = 0;
- }
- } else {
- info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
- | NDCB0_AUTO_RS
- | NDCB0_ST_ROW_EN
- | NDCB0_DBC
- | (NAND_CMD_PAGEPROG << 8)
- | NAND_CMD_SEQIN
- | addr_cycle;
- }
- break;
-
- case NAND_CMD_PARAM:
- info->buf_count = INIT_BUFFER_SIZE;
- info->ndcb0 |= NDCB0_CMD_TYPE(0)
- | NDCB0_ADDR_CYC(1)
- | NDCB0_LEN_OVRD
- | command;
- info->ndcb1 = (column & 0xFF);
- info->ndcb3 = INIT_BUFFER_SIZE;
- info->step_chunk_size = INIT_BUFFER_SIZE;
- break;
-
- case NAND_CMD_READID:
- info->buf_count = READ_ID_BYTES;
- info->ndcb0 |= NDCB0_CMD_TYPE(3)
- | NDCB0_ADDR_CYC(1)
- | command;
- info->ndcb1 = (column & 0xFF);
-
- info->step_chunk_size = 8;
- break;
- case NAND_CMD_STATUS:
- info->buf_count = 1;
- info->ndcb0 |= NDCB0_CMD_TYPE(4)
- | NDCB0_ADDR_CYC(1)
- | command;
-
- info->step_chunk_size = 8;
- break;
-
- case NAND_CMD_ERASE1:
- info->ndcb0 |= NDCB0_CMD_TYPE(2)
- | NDCB0_AUTO_RS
- | NDCB0_ADDR_CYC(3)
- | NDCB0_DBC
- | (NAND_CMD_ERASE2 << 8)
- | NAND_CMD_ERASE1;
- info->ndcb1 = page_addr;
- info->ndcb2 = 0;
-
- break;
- case NAND_CMD_RESET:
- info->ndcb0 |= NDCB0_CMD_TYPE(5)
- | command;
-
- break;
-
- case NAND_CMD_ERASE2:
- exec_cmd = 0;
- break;
-
- default:
- exec_cmd = 0;
- dev_err(&info->pdev->dev, "non-supported command %x\n",
- command);
- break;
- }
-
- return exec_cmd;
-}
-
-static void nand_cmdfunc(struct mtd_info *mtd, unsigned command,
- int column, int page_addr)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
- struct pxa3xx_nand_info *info = host->info_data;
- int exec_cmd;
-
- /*
- * if this is a x16 device ,then convert the input
- * "byte" address into a "word" address appropriate
- * for indexing a word-oriented device
- */
- if (info->reg_ndcr & NDCR_DWIDTH_M)
- column /= 2;
-
- /*
- * There may be different NAND chip hooked to
- * different chip select, so check whether
- * chip select has been changed, if yes, reset the timing
- */
- if (info->cs != host->cs) {
- info->cs = host->cs;
- nand_writel(info, NDTR0CS0, info->ndtr0cs0);
- nand_writel(info, NDTR1CS0, info->ndtr1cs0);
- }
-
- prepare_start_command(info, command);
-
- info->state = STATE_PREPARED;
- exec_cmd = prepare_set_command(info, command, 0, column, page_addr);
-
- if (exec_cmd) {
- init_completion(&info->cmd_complete);
- init_completion(&info->dev_ready);
- info->need_wait = 1;
- pxa3xx_nand_start(info);
-
- if (!wait_for_completion_timeout(&info->cmd_complete,
- CHIP_DELAY_TIMEOUT)) {
- dev_err(&info->pdev->dev, "Wait time out!!!\n");
- /* Stop State Machine for next command cycle */
- pxa3xx_nand_stop(info);
- }
- }
- info->state = STATE_IDLE;
-}
-
-static void nand_cmdfunc_extended(struct mtd_info *mtd,
- const unsigned command,
- int column, int page_addr)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
- struct pxa3xx_nand_info *info = host->info_data;
- int exec_cmd, ext_cmd_type;
-
- /*
- * if this is a x16 device then convert the input
- * "byte" address into a "word" address appropriate
- * for indexing a word-oriented device
- */
- if (info->reg_ndcr & NDCR_DWIDTH_M)
- column /= 2;
-
- /*
- * There may be different NAND chip hooked to
- * different chip select, so check whether
- * chip select has been changed, if yes, reset the timing
- */
- if (info->cs != host->cs) {
- info->cs = host->cs;
- nand_writel(info, NDTR0CS0, info->ndtr0cs0);
- nand_writel(info, NDTR1CS0, info->ndtr1cs0);
- }
-
- /* Select the extended command for the first command */
- switch (command) {
- case NAND_CMD_READ0:
- case NAND_CMD_READOOB:
- ext_cmd_type = EXT_CMD_TYPE_MONO;
- break;
- case NAND_CMD_SEQIN:
- ext_cmd_type = EXT_CMD_TYPE_DISPATCH;
- break;
- case NAND_CMD_PAGEPROG:
- ext_cmd_type = EXT_CMD_TYPE_NAKED_RW;
- break;
- default:
- ext_cmd_type = 0;
- break;
- }
-
- prepare_start_command(info, command);
-
- /*
- * Prepare the "is ready" completion before starting a command
- * transaction sequence. If the command is not executed the
- * completion will be completed, see below.
- *
- * We can do that inside the loop because the command variable
- * is invariant and thus so is the exec_cmd.
- */
- info->need_wait = 1;
- init_completion(&info->dev_ready);
- do {
- info->state = STATE_PREPARED;
-
- exec_cmd = prepare_set_command(info, command, ext_cmd_type,
- column, page_addr);
- if (!exec_cmd) {
- info->need_wait = 0;
- complete(&info->dev_ready);
- break;
- }
-
- init_completion(&info->cmd_complete);
- pxa3xx_nand_start(info);
-
- if (!wait_for_completion_timeout(&info->cmd_complete,
- CHIP_DELAY_TIMEOUT)) {
- dev_err(&info->pdev->dev, "Wait time out!!!\n");
- /* Stop State Machine for next command cycle */
- pxa3xx_nand_stop(info);
- break;
- }
-
- /* Only a few commands need several steps */
- if (command != NAND_CMD_PAGEPROG &&
- command != NAND_CMD_READ0 &&
- command != NAND_CMD_READOOB)
- break;
-
- info->cur_chunk++;
-
- /* Check if the sequence is complete */
- if (info->cur_chunk == info->ntotalchunks && command != NAND_CMD_PAGEPROG)
- break;
-
- /*
- * After a splitted program command sequence has issued
- * the command dispatch, the command sequence is complete.
- */
- if (info->cur_chunk == (info->ntotalchunks + 1) &&
- command == NAND_CMD_PAGEPROG &&
- ext_cmd_type == EXT_CMD_TYPE_DISPATCH)
- break;
-
- if (command == NAND_CMD_READ0 || command == NAND_CMD_READOOB) {
- /* Last read: issue a 'last naked read' */
- if (info->cur_chunk == info->ntotalchunks - 1)
- ext_cmd_type = EXT_CMD_TYPE_LAST_RW;
- else
- ext_cmd_type = EXT_CMD_TYPE_NAKED_RW;
-
- /*
- * If a splitted program command has no more data to transfer,
- * the command dispatch must be issued to complete.
- */
- } else if (command == NAND_CMD_PAGEPROG &&
- info->cur_chunk == info->ntotalchunks) {
- ext_cmd_type = EXT_CMD_TYPE_DISPATCH;
- }
- } while (1);
-
- info->state = STATE_IDLE;
-}
-
-static int pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
- struct nand_chip *chip, const uint8_t *buf, int oob_required,
- int page)
-{
- nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);
- chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
-
- return nand_prog_page_end_op(chip);
-}
-
-static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
- struct nand_chip *chip, uint8_t *buf, int oob_required,
- int page)
-{
- struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
- struct pxa3xx_nand_info *info = host->info_data;
-
- nand_read_page_op(chip, page, 0, buf, mtd->writesize);
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
-
- if (info->retcode == ERR_CORERR && info->use_ecc) {
- mtd->ecc_stats.corrected += info->ecc_err_cnt;
-
- } else if (info->retcode == ERR_UNCORERR) {
- /*
- * for blank page (all 0xff), HW will calculate its ECC as
- * 0, which is different from the ECC information within
- * OOB, ignore such uncorrectable errors
- */
- if (is_buf_blank(buf, mtd->writesize))
- info->retcode = ERR_NONE;
- else
- mtd->ecc_stats.failed++;
- }
-
- return info->max_bitflips;
-}
-
-static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
- struct pxa3xx_nand_info *info = host->info_data;
- char retval = 0xFF;
-
- if (info->buf_start < info->buf_count)
- /* Has just send a new command? */
- retval = info->data_buff[info->buf_start++];
-
- return retval;
-}
-
-static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
- struct pxa3xx_nand_info *info = host->info_data;
- u16 retval = 0xFFFF;
-
- if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
- retval = *((u16 *)(info->data_buff+info->buf_start));
- info->buf_start += 2;
- }
- return retval;
-}
-
-static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
- struct pxa3xx_nand_info *info = host->info_data;
- int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
-
- memcpy(buf, info->data_buff + info->buf_start, real_len);
- info->buf_start += real_len;
-}
-
-static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
- const uint8_t *buf, int len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
- struct pxa3xx_nand_info *info = host->info_data;
- int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
-
- memcpy(info->data_buff + info->buf_start, buf, real_len);
- info->buf_start += real_len;
-}
-
-static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
-{
- return;
-}
-
-static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
- struct pxa3xx_nand_info *info = host->info_data;
-
- if (info->need_wait) {
- info->need_wait = 0;
- if (!wait_for_completion_timeout(&info->dev_ready,
- CHIP_DELAY_TIMEOUT)) {
- dev_err(&info->pdev->dev, "Ready time out!!!\n");
- return NAND_STATUS_FAIL;
- }
- }
-
- /* pxa3xx_nand_send_command has waited for command complete */
- if (this->state == FL_WRITING || this->state == FL_ERASING) {
- if (info->retcode == ERR_NONE)
- return 0;
- else
- return NAND_STATUS_FAIL;
- }
-
- return NAND_STATUS_READY;
-}
-
-static int pxa3xx_nand_config_ident(struct pxa3xx_nand_info *info)
-{
- struct pxa3xx_nand_host *host = info->host[info->cs];
- struct platform_device *pdev = info->pdev;
- struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
- const struct nand_sdr_timings *timings;
-
- /* Configure default flash values */
- info->chunk_size = PAGE_CHUNK_SIZE;
- info->reg_ndcr = 0x0; /* enable all interrupts */
- info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
- info->reg_ndcr |= NDCR_RD_ID_CNT(READ_ID_BYTES);
- info->reg_ndcr |= NDCR_SPARE_EN;
-
- /* use the common timing to make a try */
- timings = onfi_async_timing_mode_to_sdr_timings(0);
- if (IS_ERR(timings))
- return PTR_ERR(timings);
-
- pxa3xx_nand_set_sdr_timing(host, timings);
- return 0;
-}
-
-static void pxa3xx_nand_config_tail(struct pxa3xx_nand_info *info)
-{
- struct pxa3xx_nand_host *host = info->host[info->cs];
- struct nand_chip *chip = &host->chip;
- struct mtd_info *mtd = nand_to_mtd(chip);
-
- info->reg_ndcr |= (host->col_addr_cycles == 2) ? NDCR_RA_START : 0;
- info->reg_ndcr |= (chip->page_shift == 6) ? NDCR_PG_PER_BLK : 0;
- info->reg_ndcr |= (mtd->writesize == 2048) ? NDCR_PAGE_SZ : 0;
-}
-
-static void pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
-{
- struct platform_device *pdev = info->pdev;
- struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
- uint32_t ndcr = nand_readl(info, NDCR);
-
- /* Set an initial chunk size */
- info->chunk_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
- info->reg_ndcr = ndcr &
- ~(NDCR_INT_MASK | NDCR_ND_ARB_EN | NFCV1_NDCR_ARB_CNTL);
- info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
- info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
- info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
-}
-
-static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
-{
- struct platform_device *pdev = info->pdev;
- struct dma_slave_config config;
- dma_cap_mask_t mask;
- struct pxad_param param;
- int ret;
-
- info->data_buff = kmalloc(info->buf_size, GFP_KERNEL);
- if (info->data_buff == NULL)
- return -ENOMEM;
- if (use_dma == 0)
- return 0;
-
- ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
- if (ret)
- return ret;
-
- sg_init_one(&info->sg, info->data_buff, info->buf_size);
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
- param.prio = PXAD_PRIO_LOWEST;
- param.drcmr = info->drcmr_dat;
- info->dma_chan = dma_request_slave_channel_compat(mask, pxad_filter_fn,
- &param, &pdev->dev,
- "data");
- if (!info->dma_chan) {
- dev_err(&pdev->dev, "unable to request data dma channel\n");
- return -ENODEV;
- }
-
- memset(&config, 0, sizeof(config));
- config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- config.src_addr = info->mmio_phys + NDDB;
- config.dst_addr = info->mmio_phys + NDDB;
- config.src_maxburst = 32;
- config.dst_maxburst = 32;
- ret = dmaengine_slave_config(info->dma_chan, &config);
- if (ret < 0) {
- dev_err(&info->pdev->dev,
- "dma channel configuration failed: %d\n",
- ret);
- return ret;
- }
-
- /*
- * Now that DMA buffers are allocated we turn on
- * DMA proper for I/O operations.
- */
- info->use_dma = 1;
- return 0;
-}
-
-static void pxa3xx_nand_free_buff(struct pxa3xx_nand_info *info)
-{
- if (info->use_dma) {
- dmaengine_terminate_all(info->dma_chan);
- dma_release_channel(info->dma_chan);
- }
- kfree(info->data_buff);
-}
-
-static int pxa_ecc_init(struct pxa3xx_nand_info *info,
- struct mtd_info *mtd,
- int strength, int ecc_stepsize, int page_size)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
-
- if (strength == 1 && ecc_stepsize == 512 && page_size == 2048) {
- info->nfullchunks = 1;
- info->ntotalchunks = 1;
- info->chunk_size = 2048;
- info->spare_size = 40;
- info->ecc_size = 24;
- ecc->mode = NAND_ECC_HW;
- ecc->size = 512;
- ecc->strength = 1;
-
- } else if (strength == 1 && ecc_stepsize == 512 && page_size == 512) {
- info->nfullchunks = 1;
- info->ntotalchunks = 1;
- info->chunk_size = 512;
- info->spare_size = 8;
- info->ecc_size = 8;
- ecc->mode = NAND_ECC_HW;
- ecc->size = 512;
- ecc->strength = 1;
-
- /*
- * Required ECC: 4-bit correction per 512 bytes
- * Select: 16-bit correction per 2048 bytes
- */
- } else if (strength == 4 && ecc_stepsize == 512 && page_size == 2048) {
- info->ecc_bch = 1;
- info->nfullchunks = 1;
- info->ntotalchunks = 1;
- info->chunk_size = 2048;
- info->spare_size = 32;
- info->ecc_size = 32;
- ecc->mode = NAND_ECC_HW;
- ecc->size = info->chunk_size;
- mtd_set_ooblayout(mtd, &pxa3xx_ooblayout_ops);
- ecc->strength = 16;
-
- } else if (strength == 4 && ecc_stepsize == 512 && page_size == 4096) {
- info->ecc_bch = 1;
- info->nfullchunks = 2;
- info->ntotalchunks = 2;
- info->chunk_size = 2048;
- info->spare_size = 32;
- info->ecc_size = 32;
- ecc->mode = NAND_ECC_HW;
- ecc->size = info->chunk_size;
- mtd_set_ooblayout(mtd, &pxa3xx_ooblayout_ops);
- ecc->strength = 16;
-
- /*
- * Required ECC: 8-bit correction per 512 bytes
- * Select: 16-bit correction per 1024 bytes
- */
- } else if (strength == 8 && ecc_stepsize == 512 && page_size == 4096) {
- info->ecc_bch = 1;
- info->nfullchunks = 4;
- info->ntotalchunks = 5;
- info->chunk_size = 1024;
- info->spare_size = 0;
- info->last_chunk_size = 0;
- info->last_spare_size = 64;
- info->ecc_size = 32;
- ecc->mode = NAND_ECC_HW;
- ecc->size = info->chunk_size;
- mtd_set_ooblayout(mtd, &pxa3xx_ooblayout_ops);
- ecc->strength = 16;
- } else {
- dev_err(&info->pdev->dev,
- "ECC strength %d at page size %d is not supported\n",
- strength, page_size);
- return -ENODEV;
- }
-
- dev_info(&info->pdev->dev, "ECC strength %d, ECC step size %d\n",
- ecc->strength, ecc->size);
- return 0;
-}
-
-static int pxa3xx_nand_scan(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
- struct pxa3xx_nand_info *info = host->info_data;
- struct platform_device *pdev = info->pdev;
- struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
- int ret;
- uint16_t ecc_strength, ecc_step;
-
- if (pdata->keep_config) {
- pxa3xx_nand_detect_config(info);
- } else {
- ret = pxa3xx_nand_config_ident(info);
- if (ret)
- return ret;
- }
-
- if (info->reg_ndcr & NDCR_DWIDTH_M)
- chip->options |= NAND_BUSWIDTH_16;
-
- /* Device detection must be done with ECC disabled */
- if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370 ||
- info->variant == PXA3XX_NAND_VARIANT_ARMADA_8K)
- nand_writel(info, NDECCCTRL, 0x0);
-
- if (pdata->flash_bbt)
- chip->bbt_options |= NAND_BBT_USE_FLASH;
-
- chip->ecc.strength = pdata->ecc_strength;
- chip->ecc.size = pdata->ecc_step_size;
-
- ret = nand_scan_ident(mtd, 1, NULL);
- if (ret)
- return ret;
-
- if (!pdata->keep_config) {
- ret = pxa3xx_nand_init(host);
- if (ret) {
- dev_err(&info->pdev->dev, "Failed to init nand: %d\n",
- ret);
- return ret;
- }
- }
-
- if (chip->bbt_options & NAND_BBT_USE_FLASH) {
- /*
- * We'll use a bad block table stored in-flash and don't
- * allow writing the bad block marker to the flash.
- */
- chip->bbt_options |= NAND_BBT_NO_OOB_BBM;
- chip->bbt_td = &bbt_main_descr;
- chip->bbt_md = &bbt_mirror_descr;
- }
-
- /*
- * If the page size is bigger than the FIFO size, let's check
- * we are given the right variant and then switch to the extended
- * (aka splitted) command handling,
- */
- if (mtd->writesize > PAGE_CHUNK_SIZE) {
- if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370 ||
- info->variant == PXA3XX_NAND_VARIANT_ARMADA_8K) {
- chip->cmdfunc = nand_cmdfunc_extended;
- } else {
- dev_err(&info->pdev->dev,
- "unsupported page size on this variant\n");
- return -ENODEV;
- }
- }
-
- ecc_strength = chip->ecc.strength;
- ecc_step = chip->ecc.size;
- if (!ecc_strength || !ecc_step) {
- ecc_strength = chip->ecc_strength_ds;
- ecc_step = chip->ecc_step_ds;
- }
-
- /* Set default ECC strength requirements on non-ONFI devices */
- if (ecc_strength < 1 && ecc_step < 1) {
- ecc_strength = 1;
- ecc_step = 512;
- }
-
- ret = pxa_ecc_init(info, mtd, ecc_strength,
- ecc_step, mtd->writesize);
- if (ret)
- return ret;
-
- /* calculate addressing information */
- if (mtd->writesize >= 2048)
- host->col_addr_cycles = 2;
- else
- host->col_addr_cycles = 1;
-
- /* release the initial buffer */
- kfree(info->data_buff);
-
- /* allocate the real data + oob buffer */
- info->buf_size = mtd->writesize + mtd->oobsize;
- ret = pxa3xx_nand_init_buff(info);
- if (ret)
- return ret;
- info->oob_buff = info->data_buff + mtd->writesize;
-
- if ((mtd->size >> chip->page_shift) > 65536)
- host->row_addr_cycles = 3;
- else
- host->row_addr_cycles = 2;
-
- if (!pdata->keep_config)
- pxa3xx_nand_config_tail(info);
-
- return nand_scan_tail(mtd);
-}
-
-static int alloc_nand_resource(struct platform_device *pdev)
-{
- struct device_node *np = pdev->dev.of_node;
- struct pxa3xx_nand_platform_data *pdata;
- struct pxa3xx_nand_info *info;
- struct pxa3xx_nand_host *host;
- struct nand_chip *chip = NULL;
- struct mtd_info *mtd;
- struct resource *r;
- int ret, irq, cs;
-
- pdata = dev_get_platdata(&pdev->dev);
- if (pdata->num_cs <= 0) {
- dev_err(&pdev->dev, "invalid number of chip selects\n");
- return -ENODEV;
- }
-
- info = devm_kzalloc(&pdev->dev,
- sizeof(*info) + sizeof(*host) * pdata->num_cs,
- GFP_KERNEL);
- if (!info)
- return -ENOMEM;
-
- info->pdev = pdev;
- info->variant = pxa3xx_nand_get_variant(pdev);
- for (cs = 0; cs < pdata->num_cs; cs++) {
- host = (void *)&info[1] + sizeof(*host) * cs;
- chip = &host->chip;
- nand_set_controller_data(chip, host);
- mtd = nand_to_mtd(chip);
- info->host[cs] = host;
- host->cs = cs;
- host->info_data = info;
- mtd->dev.parent = &pdev->dev;
- /* FIXME: all chips use the same device tree partitions */
- nand_set_flash_node(chip, np);
-
- nand_set_controller_data(chip, host);
- chip->ecc.read_page = pxa3xx_nand_read_page_hwecc;
- chip->ecc.write_page = pxa3xx_nand_write_page_hwecc;
- chip->controller = &info->controller;
- chip->waitfunc = pxa3xx_nand_waitfunc;
- chip->select_chip = pxa3xx_nand_select_chip;
- chip->read_word = pxa3xx_nand_read_word;
- chip->read_byte = pxa3xx_nand_read_byte;
- chip->read_buf = pxa3xx_nand_read_buf;
- chip->write_buf = pxa3xx_nand_write_buf;
- chip->options |= NAND_NO_SUBPAGE_WRITE;
- chip->cmdfunc = nand_cmdfunc;
- chip->onfi_set_features = nand_onfi_get_set_features_notsupp;
- chip->onfi_get_features = nand_onfi_get_set_features_notsupp;
- }
-
- nand_hw_control_init(chip->controller);
- info->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(info->clk)) {
- ret = PTR_ERR(info->clk);
- dev_err(&pdev->dev, "failed to get nand clock: %d\n", ret);
- return ret;
- }
- ret = clk_prepare_enable(info->clk);
- if (ret < 0)
- return ret;
-
- if (!np && use_dma) {
- r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (r == NULL) {
- dev_err(&pdev->dev,
- "no resource defined for data DMA\n");
- ret = -ENXIO;
- goto fail_disable_clk;
- }
- info->drcmr_dat = r->start;
- }
-
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "no IRQ resource defined\n");
- ret = -ENXIO;
- goto fail_disable_clk;
- }
-
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- info->mmio_base = devm_ioremap_resource(&pdev->dev, r);
- if (IS_ERR(info->mmio_base)) {
- ret = PTR_ERR(info->mmio_base);
- dev_err(&pdev->dev, "failed to map register space: %d\n", ret);
- goto fail_disable_clk;
- }
- info->mmio_phys = r->start;
-
- /* Allocate a buffer to allow flash detection */
- info->buf_size = INIT_BUFFER_SIZE;
- info->data_buff = kmalloc(info->buf_size, GFP_KERNEL);
- if (info->data_buff == NULL) {
- ret = -ENOMEM;
- goto fail_disable_clk;
- }
-
- /* initialize all interrupts to be disabled */
- disable_int(info, NDSR_MASK);
-
- ret = request_threaded_irq(irq, pxa3xx_nand_irq,
- pxa3xx_nand_irq_thread, IRQF_ONESHOT,
- pdev->name, info);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to request IRQ: %d\n", ret);
- goto fail_free_buf;
- }
-
- platform_set_drvdata(pdev, info);
-
- return 0;
-
-fail_free_buf:
- free_irq(irq, info);
- kfree(info->data_buff);
-fail_disable_clk:
- clk_disable_unprepare(info->clk);
- return ret;
-}
-
-static int pxa3xx_nand_remove(struct platform_device *pdev)
-{
- struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
- struct pxa3xx_nand_platform_data *pdata;
- int irq, cs;
-
- if (!info)
- return 0;
-
- pdata = dev_get_platdata(&pdev->dev);
-
- irq = platform_get_irq(pdev, 0);
- if (irq >= 0)
- free_irq(irq, info);
- pxa3xx_nand_free_buff(info);
-
- /*
- * In the pxa3xx case, the DFI bus is shared between the SMC and NFC.
- * In order to prevent a lockup of the system bus, the DFI bus
- * arbitration is granted to SMC upon driver removal. This is done by
- * setting the x_ARB_CNTL bit, which also prevents the NAND to have
- * access to the bus anymore.
- */
- nand_writel(info, NDCR,
- (nand_readl(info, NDCR) & ~NDCR_ND_ARB_EN) |
- NFCV1_NDCR_ARB_CNTL);
- clk_disable_unprepare(info->clk);
-
- for (cs = 0; cs < pdata->num_cs; cs++)
- nand_release(nand_to_mtd(&info->host[cs]->chip));
- return 0;
-}
-
-static int pxa3xx_nand_probe_dt(struct platform_device *pdev)
-{
- struct pxa3xx_nand_platform_data *pdata;
- struct device_node *np = pdev->dev.of_node;
- const struct of_device_id *of_id =
- of_match_device(pxa3xx_nand_dt_ids, &pdev->dev);
-
- if (!of_id)
- return 0;
-
- /*
- * Some SoCs like A7k/A8k need to enable manually the NAND
- * controller to avoid being bootloader dependent. This is done
- * through the use of a single bit in the System Functions registers.
- */
- if (pxa3xx_nand_get_variant(pdev) == PXA3XX_NAND_VARIANT_ARMADA_8K) {
- struct regmap *sysctrl_base = syscon_regmap_lookup_by_phandle(
- pdev->dev.of_node, "marvell,system-controller");
- u32 reg;
-
- if (IS_ERR(sysctrl_base))
- return PTR_ERR(sysctrl_base);
-
- regmap_read(sysctrl_base, GENCONF_SOC_DEVICE_MUX, &reg);
- reg |= GENCONF_SOC_DEVICE_MUX_NFC_EN;
- regmap_write(sysctrl_base, GENCONF_SOC_DEVICE_MUX, reg);
- }
-
- pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
-
- if (of_get_property(np, "marvell,nand-enable-arbiter", NULL))
- pdata->enable_arbiter = 1;
- if (of_get_property(np, "marvell,nand-keep-config", NULL))
- pdata->keep_config = 1;
- of_property_read_u32(np, "num-cs", &pdata->num_cs);
-
- pdev->dev.platform_data = pdata;
-
- return 0;
-}
-
-static int pxa3xx_nand_probe(struct platform_device *pdev)
-{
- struct pxa3xx_nand_platform_data *pdata;
- struct pxa3xx_nand_info *info;
- int ret, cs, probe_success, dma_available;
-
- dma_available = IS_ENABLED(CONFIG_ARM) &&
- (IS_ENABLED(CONFIG_ARCH_PXA) || IS_ENABLED(CONFIG_ARCH_MMP));
- if (use_dma && !dma_available) {
- use_dma = 0;
- dev_warn(&pdev->dev,
- "This platform can't do DMA on this device\n");
- }
-
- ret = pxa3xx_nand_probe_dt(pdev);
- if (ret)
- return ret;
-
- pdata = dev_get_platdata(&pdev->dev);
- if (!pdata) {
- dev_err(&pdev->dev, "no platform data defined\n");
- return -ENODEV;
- }
-
- ret = alloc_nand_resource(pdev);
- if (ret)
- return ret;
-
- info = platform_get_drvdata(pdev);
- probe_success = 0;
- for (cs = 0; cs < pdata->num_cs; cs++) {
- struct mtd_info *mtd = nand_to_mtd(&info->host[cs]->chip);
-
- /*
- * The mtd name matches the one used in 'mtdparts' kernel
- * parameter. This name cannot be changed or otherwise
- * user's mtd partitions configuration would get broken.
- */
- mtd->name = "pxa3xx_nand-0";
- info->cs = cs;
- ret = pxa3xx_nand_scan(mtd);
- if (ret) {
- dev_warn(&pdev->dev, "failed to scan nand at cs %d\n",
- cs);
- continue;
- }
-
- ret = mtd_device_register(mtd, pdata->parts[cs],
- pdata->nr_parts[cs]);
- if (!ret)
- probe_success = 1;
- }
-
- if (!probe_success) {
- pxa3xx_nand_remove(pdev);
- return -ENODEV;
- }
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int pxa3xx_nand_suspend(struct device *dev)
-{
- struct pxa3xx_nand_info *info = dev_get_drvdata(dev);
-
- if (info->state) {
- dev_err(dev, "driver busy, state = %d\n", info->state);
- return -EAGAIN;
- }
-
- clk_disable(info->clk);
- return 0;
-}
-
-static int pxa3xx_nand_resume(struct device *dev)
-{
- struct pxa3xx_nand_info *info = dev_get_drvdata(dev);
- int ret;
-
- ret = clk_enable(info->clk);
- if (ret < 0)
- return ret;
-
- /* We don't want to handle interrupt without calling mtd routine */
- disable_int(info, NDCR_INT_MASK);
-
- /*
- * Directly set the chip select to a invalid value,
- * then the driver would reset the timing according
- * to current chip select at the beginning of cmdfunc
- */
- info->cs = 0xff;
-
- /*
- * As the spec says, the NDSR would be updated to 0x1800 when
- * doing the nand_clk disable/enable.
- * To prevent it damaging state machine of the driver, clear
- * all status before resume
- */
- nand_writel(info, NDSR, NDSR_MASK);
-
- return 0;
-}
-#else
-#define pxa3xx_nand_suspend NULL
-#define pxa3xx_nand_resume NULL
-#endif
-
-static const struct dev_pm_ops pxa3xx_nand_pm_ops = {
- .suspend = pxa3xx_nand_suspend,
- .resume = pxa3xx_nand_resume,
-};
-
-static struct platform_driver pxa3xx_nand_driver = {
- .driver = {
- .name = "pxa3xx-nand",
- .of_match_table = pxa3xx_nand_dt_ids,
- .pm = &pxa3xx_nand_pm_ops,
- },
- .probe = pxa3xx_nand_probe,
- .remove = pxa3xx_nand_remove,
-};
-
-module_platform_driver(pxa3xx_nand_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("PXA3xx NAND controller driver");
diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c
deleted file mode 100644
index 563b759ffca6..000000000000
--- a/drivers/mtd/nand/qcom_nandc.c
+++ /dev/null
@@ -1,2921 +0,0 @@
-/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * 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.
- */
-
-#include <linux/clk.h>
-#include <linux/slab.h>
-#include <linux/bitops.h>
-#include <linux/dma-mapping.h>
-#include <linux/dmaengine.h>
-#include <linux/module.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/partitions.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/delay.h>
-#include <linux/dma/qcom_bam_dma.h>
-#include <linux/dma-direct.h> /* XXX: drivers shall never use this directly! */
-
-/* NANDc reg offsets */
-#define NAND_FLASH_CMD 0x00
-#define NAND_ADDR0 0x04
-#define NAND_ADDR1 0x08
-#define NAND_FLASH_CHIP_SELECT 0x0c
-#define NAND_EXEC_CMD 0x10
-#define NAND_FLASH_STATUS 0x14
-#define NAND_BUFFER_STATUS 0x18
-#define NAND_DEV0_CFG0 0x20
-#define NAND_DEV0_CFG1 0x24
-#define NAND_DEV0_ECC_CFG 0x28
-#define NAND_DEV1_ECC_CFG 0x2c
-#define NAND_DEV1_CFG0 0x30
-#define NAND_DEV1_CFG1 0x34
-#define NAND_READ_ID 0x40
-#define NAND_READ_STATUS 0x44
-#define NAND_DEV_CMD0 0xa0
-#define NAND_DEV_CMD1 0xa4
-#define NAND_DEV_CMD2 0xa8
-#define NAND_DEV_CMD_VLD 0xac
-#define SFLASHC_BURST_CFG 0xe0
-#define NAND_ERASED_CW_DETECT_CFG 0xe8
-#define NAND_ERASED_CW_DETECT_STATUS 0xec
-#define NAND_EBI2_ECC_BUF_CFG 0xf0
-#define FLASH_BUF_ACC 0x100
-
-#define NAND_CTRL 0xf00
-#define NAND_VERSION 0xf08
-#define NAND_READ_LOCATION_0 0xf20
-#define NAND_READ_LOCATION_1 0xf24
-#define NAND_READ_LOCATION_2 0xf28
-#define NAND_READ_LOCATION_3 0xf2c
-
-/* dummy register offsets, used by write_reg_dma */
-#define NAND_DEV_CMD1_RESTORE 0xdead
-#define NAND_DEV_CMD_VLD_RESTORE 0xbeef
-
-/* NAND_FLASH_CMD bits */
-#define PAGE_ACC BIT(4)
-#define LAST_PAGE BIT(5)
-
-/* NAND_FLASH_CHIP_SELECT bits */
-#define NAND_DEV_SEL 0
-#define DM_EN BIT(2)
-
-/* NAND_FLASH_STATUS bits */
-#define FS_OP_ERR BIT(4)
-#define FS_READY_BSY_N BIT(5)
-#define FS_MPU_ERR BIT(8)
-#define FS_DEVICE_STS_ERR BIT(16)
-#define FS_DEVICE_WP BIT(23)
-
-/* NAND_BUFFER_STATUS bits */
-#define BS_UNCORRECTABLE_BIT BIT(8)
-#define BS_CORRECTABLE_ERR_MSK 0x1f
-
-/* NAND_DEVn_CFG0 bits */
-#define DISABLE_STATUS_AFTER_WRITE 4
-#define CW_PER_PAGE 6
-#define UD_SIZE_BYTES 9
-#define ECC_PARITY_SIZE_BYTES_RS 19
-#define SPARE_SIZE_BYTES 23
-#define NUM_ADDR_CYCLES 27
-#define STATUS_BFR_READ 30
-#define SET_RD_MODE_AFTER_STATUS 31
-
-/* NAND_DEVn_CFG0 bits */
-#define DEV0_CFG1_ECC_DISABLE 0
-#define WIDE_FLASH 1
-#define NAND_RECOVERY_CYCLES 2
-#define CS_ACTIVE_BSY 5
-#define BAD_BLOCK_BYTE_NUM 6
-#define BAD_BLOCK_IN_SPARE_AREA 16
-#define WR_RD_BSY_GAP 17
-#define ENABLE_BCH_ECC 27
-
-/* NAND_DEV0_ECC_CFG bits */
-#define ECC_CFG_ECC_DISABLE 0
-#define ECC_SW_RESET 1
-#define ECC_MODE 4
-#define ECC_PARITY_SIZE_BYTES_BCH 8
-#define ECC_NUM_DATA_BYTES 16
-#define ECC_FORCE_CLK_OPEN 30
-
-/* NAND_DEV_CMD1 bits */
-#define READ_ADDR 0
-
-/* NAND_DEV_CMD_VLD bits */
-#define READ_START_VLD BIT(0)
-#define READ_STOP_VLD BIT(1)
-#define WRITE_START_VLD BIT(2)
-#define ERASE_START_VLD BIT(3)
-#define SEQ_READ_START_VLD BIT(4)
-
-/* NAND_EBI2_ECC_BUF_CFG bits */
-#define NUM_STEPS 0
-
-/* NAND_ERASED_CW_DETECT_CFG bits */
-#define ERASED_CW_ECC_MASK 1
-#define AUTO_DETECT_RES 0
-#define MASK_ECC (1 << ERASED_CW_ECC_MASK)
-#define RESET_ERASED_DET (1 << AUTO_DETECT_RES)
-#define ACTIVE_ERASED_DET (0 << AUTO_DETECT_RES)
-#define CLR_ERASED_PAGE_DET (RESET_ERASED_DET | MASK_ECC)
-#define SET_ERASED_PAGE_DET (ACTIVE_ERASED_DET | MASK_ECC)
-
-/* NAND_ERASED_CW_DETECT_STATUS bits */
-#define PAGE_ALL_ERASED BIT(7)
-#define CODEWORD_ALL_ERASED BIT(6)
-#define PAGE_ERASED BIT(5)
-#define CODEWORD_ERASED BIT(4)
-#define ERASED_PAGE (PAGE_ALL_ERASED | PAGE_ERASED)
-#define ERASED_CW (CODEWORD_ALL_ERASED | CODEWORD_ERASED)
-
-/* NAND_READ_LOCATION_n bits */
-#define READ_LOCATION_OFFSET 0
-#define READ_LOCATION_SIZE 16
-#define READ_LOCATION_LAST 31
-
-/* Version Mask */
-#define NAND_VERSION_MAJOR_MASK 0xf0000000
-#define NAND_VERSION_MAJOR_SHIFT 28
-#define NAND_VERSION_MINOR_MASK 0x0fff0000
-#define NAND_VERSION_MINOR_SHIFT 16
-
-/* NAND OP_CMDs */
-#define PAGE_READ 0x2
-#define PAGE_READ_WITH_ECC 0x3
-#define PAGE_READ_WITH_ECC_SPARE 0x4
-#define PROGRAM_PAGE 0x6
-#define PAGE_PROGRAM_WITH_ECC 0x7
-#define PROGRAM_PAGE_SPARE 0x9
-#define BLOCK_ERASE 0xa
-#define FETCH_ID 0xb
-#define RESET_DEVICE 0xd
-
-/* Default Value for NAND_DEV_CMD_VLD */
-#define NAND_DEV_CMD_VLD_VAL (READ_START_VLD | WRITE_START_VLD | \
- ERASE_START_VLD | SEQ_READ_START_VLD)
-
-/* NAND_CTRL bits */
-#define BAM_MODE_EN BIT(0)
-
-/*
- * the NAND controller performs reads/writes with ECC in 516 byte chunks.
- * the driver calls the chunks 'step' or 'codeword' interchangeably
- */
-#define NANDC_STEP_SIZE 512
-
-/*
- * the largest page size we support is 8K, this will have 16 steps/codewords
- * of 512 bytes each
- */
-#define MAX_NUM_STEPS (SZ_8K / NANDC_STEP_SIZE)
-
-/* we read at most 3 registers per codeword scan */
-#define MAX_REG_RD (3 * MAX_NUM_STEPS)
-
-/* ECC modes supported by the controller */
-#define ECC_NONE BIT(0)
-#define ECC_RS_4BIT BIT(1)
-#define ECC_BCH_4BIT BIT(2)
-#define ECC_BCH_8BIT BIT(3)
-
-#define nandc_set_read_loc(nandc, reg, offset, size, is_last) \
-nandc_set_reg(nandc, NAND_READ_LOCATION_##reg, \
- ((offset) << READ_LOCATION_OFFSET) | \
- ((size) << READ_LOCATION_SIZE) | \
- ((is_last) << READ_LOCATION_LAST))
-
-/*
- * Returns the actual register address for all NAND_DEV_ registers
- * (i.e. NAND_DEV_CMD0, NAND_DEV_CMD1, NAND_DEV_CMD2 and NAND_DEV_CMD_VLD)
- */
-#define dev_cmd_reg_addr(nandc, reg) ((nandc)->props->dev_cmd_reg_start + (reg))
-
-/* Returns the NAND register physical address */
-#define nandc_reg_phys(chip, offset) ((chip)->base_phys + (offset))
-
-/* Returns the dma address for reg read buffer */
-#define reg_buf_dma_addr(chip, vaddr) \
- ((chip)->reg_read_dma + \
- ((uint8_t *)(vaddr) - (uint8_t *)(chip)->reg_read_buf))
-
-#define QPIC_PER_CW_CMD_ELEMENTS 32
-#define QPIC_PER_CW_CMD_SGL 32
-#define QPIC_PER_CW_DATA_SGL 8
-
-/*
- * Flags used in DMA descriptor preparation helper functions
- * (i.e. read_reg_dma/write_reg_dma/read_data_dma/write_data_dma)
- */
-/* Don't set the EOT in current tx BAM sgl */
-#define NAND_BAM_NO_EOT BIT(0)
-/* Set the NWD flag in current BAM sgl */
-#define NAND_BAM_NWD BIT(1)
-/* Finish writing in the current BAM sgl and start writing in another BAM sgl */
-#define NAND_BAM_NEXT_SGL BIT(2)
-/*
- * Erased codeword status is being used two times in single transfer so this
- * flag will determine the current value of erased codeword status register
- */
-#define NAND_ERASED_CW_SET BIT(4)
-
-/*
- * This data type corresponds to the BAM transaction which will be used for all
- * NAND transfers.
- * @bam_ce - the array of BAM command elements
- * @cmd_sgl - sgl for NAND BAM command pipe
- * @data_sgl - sgl for NAND BAM consumer/producer pipe
- * @bam_ce_pos - the index in bam_ce which is available for next sgl
- * @bam_ce_start - the index in bam_ce which marks the start position ce
- * for current sgl. It will be used for size calculation
- * for current sgl
- * @cmd_sgl_pos - current index in command sgl.
- * @cmd_sgl_start - start index in command sgl.
- * @tx_sgl_pos - current index in data sgl for tx.
- * @tx_sgl_start - start index in data sgl for tx.
- * @rx_sgl_pos - current index in data sgl for rx.
- * @rx_sgl_start - start index in data sgl for rx.
- */
-struct bam_transaction {
- struct bam_cmd_element *bam_ce;
- struct scatterlist *cmd_sgl;
- struct scatterlist *data_sgl;
- u32 bam_ce_pos;
- u32 bam_ce_start;
- u32 cmd_sgl_pos;
- u32 cmd_sgl_start;
- u32 tx_sgl_pos;
- u32 tx_sgl_start;
- u32 rx_sgl_pos;
- u32 rx_sgl_start;
-};
-
-/*
- * This data type corresponds to the nand dma descriptor
- * @list - list for desc_info
- * @dir - DMA transfer direction
- * @adm_sgl - sgl which will be used for single sgl dma descriptor. Only used by
- * ADM
- * @bam_sgl - sgl which will be used for dma descriptor. Only used by BAM
- * @sgl_cnt - number of SGL in bam_sgl. Only used by BAM
- * @dma_desc - low level DMA engine descriptor
- */
-struct desc_info {
- struct list_head node;
-
- enum dma_data_direction dir;
- union {
- struct scatterlist adm_sgl;
- struct {
- struct scatterlist *bam_sgl;
- int sgl_cnt;
- };
- };
- struct dma_async_tx_descriptor *dma_desc;
-};
-
-/*
- * holds the current register values that we want to write. acts as a contiguous
- * chunk of memory which we use to write the controller registers through DMA.
- */
-struct nandc_regs {
- __le32 cmd;
- __le32 addr0;
- __le32 addr1;
- __le32 chip_sel;
- __le32 exec;
-
- __le32 cfg0;
- __le32 cfg1;
- __le32 ecc_bch_cfg;
-
- __le32 clrflashstatus;
- __le32 clrreadstatus;
-
- __le32 cmd1;
- __le32 vld;
-
- __le32 orig_cmd1;
- __le32 orig_vld;
-
- __le32 ecc_buf_cfg;
- __le32 read_location0;
- __le32 read_location1;
- __le32 read_location2;
- __le32 read_location3;
-
- __le32 erased_cw_detect_cfg_clr;
- __le32 erased_cw_detect_cfg_set;
-};
-
-/*
- * NAND controller data struct
- *
- * @controller: base controller structure
- * @host_list: list containing all the chips attached to the
- * controller
- * @dev: parent device
- * @base: MMIO base
- * @base_phys: physical base address of controller registers
- * @base_dma: dma base address of controller registers
- * @core_clk: controller clock
- * @aon_clk: another controller clock
- *
- * @chan: dma channel
- * @cmd_crci: ADM DMA CRCI for command flow control
- * @data_crci: ADM DMA CRCI for data flow control
- * @desc_list: DMA descriptor list (list of desc_infos)
- *
- * @data_buffer: our local DMA buffer for page read/writes,
- * used when we can't use the buffer provided
- * by upper layers directly
- * @buf_size/count/start: markers for chip->read_buf/write_buf functions
- * @reg_read_buf: local buffer for reading back registers via DMA
- * @reg_read_dma: contains dma address for register read buffer
- * @reg_read_pos: marker for data read in reg_read_buf
- *
- * @regs: a contiguous chunk of memory for DMA register
- * writes. contains the register values to be
- * written to controller
- * @cmd1/vld: some fixed controller register values
- * @props: properties of current NAND controller,
- * initialized via DT match data
- * @max_cwperpage: maximum QPIC codewords required. calculated
- * from all connected NAND devices pagesize
- */
-struct qcom_nand_controller {
- struct nand_hw_control controller;
- struct list_head host_list;
-
- struct device *dev;
-
- void __iomem *base;
- phys_addr_t base_phys;
- dma_addr_t base_dma;
-
- struct clk *core_clk;
- struct clk *aon_clk;
-
- union {
- /* will be used only by QPIC for BAM DMA */
- struct {
- struct dma_chan *tx_chan;
- struct dma_chan *rx_chan;
- struct dma_chan *cmd_chan;
- };
-
- /* will be used only by EBI2 for ADM DMA */
- struct {
- struct dma_chan *chan;
- unsigned int cmd_crci;
- unsigned int data_crci;
- };
- };
-
- struct list_head desc_list;
- struct bam_transaction *bam_txn;
-
- u8 *data_buffer;
- int buf_size;
- int buf_count;
- int buf_start;
- unsigned int max_cwperpage;
-
- __le32 *reg_read_buf;
- dma_addr_t reg_read_dma;
- int reg_read_pos;
-
- struct nandc_regs *regs;
-
- u32 cmd1, vld;
- const struct qcom_nandc_props *props;
-};
-
-/*
- * NAND chip structure
- *
- * @chip: base NAND chip structure
- * @node: list node to add itself to host_list in
- * qcom_nand_controller
- *
- * @cs: chip select value for this chip
- * @cw_size: the number of bytes in a single step/codeword
- * of a page, consisting of all data, ecc, spare
- * and reserved bytes
- * @cw_data: the number of bytes within a codeword protected
- * by ECC
- * @use_ecc: request the controller to use ECC for the
- * upcoming read/write
- * @bch_enabled: flag to tell whether BCH ECC mode is used
- * @ecc_bytes_hw: ECC bytes used by controller hardware for this
- * chip
- * @status: value to be returned if NAND_CMD_STATUS command
- * is executed
- * @last_command: keeps track of last command on this chip. used
- * for reading correct status
- *
- * @cfg0, cfg1, cfg0_raw..: NANDc register configurations needed for
- * ecc/non-ecc mode for the current nand flash
- * device
- */
-struct qcom_nand_host {
- struct nand_chip chip;
- struct list_head node;
-
- int cs;
- int cw_size;
- int cw_data;
- bool use_ecc;
- bool bch_enabled;
- int ecc_bytes_hw;
- int spare_bytes;
- int bbm_size;
- u8 status;
- int last_command;
-
- u32 cfg0, cfg1;
- u32 cfg0_raw, cfg1_raw;
- u32 ecc_buf_cfg;
- u32 ecc_bch_cfg;
- u32 clrflashstatus;
- u32 clrreadstatus;
-};
-
-/*
- * This data type corresponds to the NAND controller properties which varies
- * among different NAND controllers.
- * @ecc_modes - ecc mode for NAND
- * @is_bam - whether NAND controller is using BAM
- * @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset
- */
-struct qcom_nandc_props {
- u32 ecc_modes;
- bool is_bam;
- u32 dev_cmd_reg_start;
-};
-
-/* Frees the BAM transaction memory */
-static void free_bam_transaction(struct qcom_nand_controller *nandc)
-{
- struct bam_transaction *bam_txn = nandc->bam_txn;
-
- devm_kfree(nandc->dev, bam_txn);
-}
-
-/* Allocates and Initializes the BAM transaction */
-static struct bam_transaction *
-alloc_bam_transaction(struct qcom_nand_controller *nandc)
-{
- struct bam_transaction *bam_txn;
- size_t bam_txn_size;
- unsigned int num_cw = nandc->max_cwperpage;
- void *bam_txn_buf;
-
- bam_txn_size =
- sizeof(*bam_txn) + num_cw *
- ((sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS) +
- (sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL) +
- (sizeof(*bam_txn->data_sgl) * QPIC_PER_CW_DATA_SGL));
-
- bam_txn_buf = devm_kzalloc(nandc->dev, bam_txn_size, GFP_KERNEL);
- if (!bam_txn_buf)
- return NULL;
-
- bam_txn = bam_txn_buf;
- bam_txn_buf += sizeof(*bam_txn);
-
- bam_txn->bam_ce = bam_txn_buf;
- bam_txn_buf +=
- sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS * num_cw;
-
- bam_txn->cmd_sgl = bam_txn_buf;
- bam_txn_buf +=
- sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL * num_cw;
-
- bam_txn->data_sgl = bam_txn_buf;
-
- return bam_txn;
-}
-
-/* Clears the BAM transaction indexes */
-static void clear_bam_transaction(struct qcom_nand_controller *nandc)
-{
- struct bam_transaction *bam_txn = nandc->bam_txn;
-
- if (!nandc->props->is_bam)
- return;
-
- bam_txn->bam_ce_pos = 0;
- bam_txn->bam_ce_start = 0;
- bam_txn->cmd_sgl_pos = 0;
- bam_txn->cmd_sgl_start = 0;
- bam_txn->tx_sgl_pos = 0;
- bam_txn->tx_sgl_start = 0;
- bam_txn->rx_sgl_pos = 0;
- bam_txn->rx_sgl_start = 0;
-
- sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage *
- QPIC_PER_CW_CMD_SGL);
- sg_init_table(bam_txn->data_sgl, nandc->max_cwperpage *
- QPIC_PER_CW_DATA_SGL);
-}
-
-static inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip)
-{
- return container_of(chip, struct qcom_nand_host, chip);
-}
-
-static inline struct qcom_nand_controller *
-get_qcom_nand_controller(struct nand_chip *chip)
-{
- return container_of(chip->controller, struct qcom_nand_controller,
- controller);
-}
-
-static inline u32 nandc_read(struct qcom_nand_controller *nandc, int offset)
-{
- return ioread32(nandc->base + offset);
-}
-
-static inline void nandc_write(struct qcom_nand_controller *nandc, int offset,
- u32 val)
-{
- iowrite32(val, nandc->base + offset);
-}
-
-static inline void nandc_read_buffer_sync(struct qcom_nand_controller *nandc,
- bool is_cpu)
-{
- if (!nandc->props->is_bam)
- return;
-
- if (is_cpu)
- dma_sync_single_for_cpu(nandc->dev, nandc->reg_read_dma,
- MAX_REG_RD *
- sizeof(*nandc->reg_read_buf),
- DMA_FROM_DEVICE);
- else
- dma_sync_single_for_device(nandc->dev, nandc->reg_read_dma,
- MAX_REG_RD *
- sizeof(*nandc->reg_read_buf),
- DMA_FROM_DEVICE);
-}
-
-static __le32 *offset_to_nandc_reg(struct nandc_regs *regs, int offset)
-{
- switch (offset) {
- case NAND_FLASH_CMD:
- return &regs->cmd;
- case NAND_ADDR0:
- return &regs->addr0;
- case NAND_ADDR1:
- return &regs->addr1;
- case NAND_FLASH_CHIP_SELECT:
- return &regs->chip_sel;
- case NAND_EXEC_CMD:
- return &regs->exec;
- case NAND_FLASH_STATUS:
- return &regs->clrflashstatus;
- case NAND_DEV0_CFG0:
- return &regs->cfg0;
- case NAND_DEV0_CFG1:
- return &regs->cfg1;
- case NAND_DEV0_ECC_CFG:
- return &regs->ecc_bch_cfg;
- case NAND_READ_STATUS:
- return &regs->clrreadstatus;
- case NAND_DEV_CMD1:
- return &regs->cmd1;
- case NAND_DEV_CMD1_RESTORE:
- return &regs->orig_cmd1;
- case NAND_DEV_CMD_VLD:
- return &regs->vld;
- case NAND_DEV_CMD_VLD_RESTORE:
- return &regs->orig_vld;
- case NAND_EBI2_ECC_BUF_CFG:
- return &regs->ecc_buf_cfg;
- case NAND_READ_LOCATION_0:
- return &regs->read_location0;
- case NAND_READ_LOCATION_1:
- return &regs->read_location1;
- case NAND_READ_LOCATION_2:
- return &regs->read_location2;
- case NAND_READ_LOCATION_3:
- return &regs->read_location3;
- default:
- return NULL;
- }
-}
-
-static void nandc_set_reg(struct qcom_nand_controller *nandc, int offset,
- u32 val)
-{
- struct nandc_regs *regs = nandc->regs;
- __le32 *reg;
-
- reg = offset_to_nandc_reg(regs, offset);
-
- if (reg)
- *reg = cpu_to_le32(val);
-}
-
-/* helper to configure address register values */
-static void set_address(struct qcom_nand_host *host, u16 column, int page)
-{
- struct nand_chip *chip = &host->chip;
- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
-
- if (chip->options & NAND_BUSWIDTH_16)
- column >>= 1;
-
- nandc_set_reg(nandc, NAND_ADDR0, page << 16 | column);
- nandc_set_reg(nandc, NAND_ADDR1, page >> 16 & 0xff);
-}
-
-/*
- * update_rw_regs: set up read/write register values, these will be
- * written to the NAND controller registers via DMA
- *
- * @num_cw: number of steps for the read/write operation
- * @read: read or write operation
- */
-static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read)
-{
- struct nand_chip *chip = &host->chip;
- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
- u32 cmd, cfg0, cfg1, ecc_bch_cfg;
-
- if (read) {
- if (host->use_ecc)
- cmd = PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE;
- else
- cmd = PAGE_READ | PAGE_ACC | LAST_PAGE;
- } else {
- cmd = PROGRAM_PAGE | PAGE_ACC | LAST_PAGE;
- }
-
- if (host->use_ecc) {
- cfg0 = (host->cfg0 & ~(7U << CW_PER_PAGE)) |
- (num_cw - 1) << CW_PER_PAGE;
-
- cfg1 = host->cfg1;
- ecc_bch_cfg = host->ecc_bch_cfg;
- } else {
- cfg0 = (host->cfg0_raw & ~(7U << CW_PER_PAGE)) |
- (num_cw - 1) << CW_PER_PAGE;
-
- cfg1 = host->cfg1_raw;
- ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE;
- }
-
- nandc_set_reg(nandc, NAND_FLASH_CMD, cmd);
- nandc_set_reg(nandc, NAND_DEV0_CFG0, cfg0);
- nandc_set_reg(nandc, NAND_DEV0_CFG1, cfg1);
- nandc_set_reg(nandc, NAND_DEV0_ECC_CFG, ecc_bch_cfg);
- nandc_set_reg(nandc, NAND_EBI2_ECC_BUF_CFG, host->ecc_buf_cfg);
- nandc_set_reg(nandc, NAND_FLASH_STATUS, host->clrflashstatus);
- nandc_set_reg(nandc, NAND_READ_STATUS, host->clrreadstatus);
- nandc_set_reg(nandc, NAND_EXEC_CMD, 1);
-
- if (read)
- nandc_set_read_loc(nandc, 0, 0, host->use_ecc ?
- host->cw_data : host->cw_size, 1);
-}
-
-/*
- * Maps the scatter gather list for DMA transfer and forms the DMA descriptor
- * for BAM. This descriptor will be added in the NAND DMA descriptor queue
- * which will be submitted to DMA engine.
- */
-static int prepare_bam_async_desc(struct qcom_nand_controller *nandc,
- struct dma_chan *chan,
- unsigned long flags)
-{
- struct desc_info *desc;
- struct scatterlist *sgl;
- unsigned int sgl_cnt;
- int ret;
- struct bam_transaction *bam_txn = nandc->bam_txn;
- enum dma_transfer_direction dir_eng;
- struct dma_async_tx_descriptor *dma_desc;
-
- desc = kzalloc(sizeof(*desc), GFP_KERNEL);
- if (!desc)
- return -ENOMEM;
-
- if (chan == nandc->cmd_chan) {
- sgl = &bam_txn->cmd_sgl[bam_txn->cmd_sgl_start];
- sgl_cnt = bam_txn->cmd_sgl_pos - bam_txn->cmd_sgl_start;
- bam_txn->cmd_sgl_start = bam_txn->cmd_sgl_pos;
- dir_eng = DMA_MEM_TO_DEV;
- desc->dir = DMA_TO_DEVICE;
- } else if (chan == nandc->tx_chan) {
- sgl = &bam_txn->data_sgl[bam_txn->tx_sgl_start];
- sgl_cnt = bam_txn->tx_sgl_pos - bam_txn->tx_sgl_start;
- bam_txn->tx_sgl_start = bam_txn->tx_sgl_pos;
- dir_eng = DMA_MEM_TO_DEV;
- desc->dir = DMA_TO_DEVICE;
- } else {
- sgl = &bam_txn->data_sgl[bam_txn->rx_sgl_start];
- sgl_cnt = bam_txn->rx_sgl_pos - bam_txn->rx_sgl_start;
- bam_txn->rx_sgl_start = bam_txn->rx_sgl_pos;
- dir_eng = DMA_DEV_TO_MEM;
- desc->dir = DMA_FROM_DEVICE;
- }
-
- sg_mark_end(sgl + sgl_cnt - 1);
- ret = dma_map_sg(nandc->dev, sgl, sgl_cnt, desc->dir);
- if (ret == 0) {
- dev_err(nandc->dev, "failure in mapping desc\n");
- kfree(desc);
- return -ENOMEM;
- }
-
- desc->sgl_cnt = sgl_cnt;
- desc->bam_sgl = sgl;
-
- dma_desc = dmaengine_prep_slave_sg(chan, sgl, sgl_cnt, dir_eng,
- flags);
-
- if (!dma_desc) {
- dev_err(nandc->dev, "failure in prep desc\n");
- dma_unmap_sg(nandc->dev, sgl, sgl_cnt, desc->dir);
- kfree(desc);
- return -EINVAL;
- }
-
- desc->dma_desc = dma_desc;
-
- list_add_tail(&desc->node, &nandc->desc_list);
-
- return 0;
-}
-
-/*
- * Prepares the command descriptor for BAM DMA which will be used for NAND
- * register reads and writes. The command descriptor requires the command
- * to be formed in command element type so this function uses the command
- * element from bam transaction ce array and fills the same with required
- * data. A single SGL can contain multiple command elements so
- * NAND_BAM_NEXT_SGL will be used for starting the separate SGL
- * after the current command element.
- */
-static int prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc, bool read,
- int reg_off, const void *vaddr,
- int size, unsigned int flags)
-{
- int bam_ce_size;
- int i, ret;
- struct bam_cmd_element *bam_ce_buffer;
- struct bam_transaction *bam_txn = nandc->bam_txn;
-
- bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_pos];
-
- /* fill the command desc */
- for (i = 0; i < size; i++) {
- if (read)
- bam_prep_ce(&bam_ce_buffer[i],
- nandc_reg_phys(nandc, reg_off + 4 * i),
- BAM_READ_COMMAND,
- reg_buf_dma_addr(nandc,
- (__le32 *)vaddr + i));
- else
- bam_prep_ce_le32(&bam_ce_buffer[i],
- nandc_reg_phys(nandc, reg_off + 4 * i),
- BAM_WRITE_COMMAND,
- *((__le32 *)vaddr + i));
- }
-
- bam_txn->bam_ce_pos += size;
-
- /* use the separate sgl after this command */
- if (flags & NAND_BAM_NEXT_SGL) {
- bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_start];
- bam_ce_size = (bam_txn->bam_ce_pos -
- bam_txn->bam_ce_start) *
- sizeof(struct bam_cmd_element);
- sg_set_buf(&bam_txn->cmd_sgl[bam_txn->cmd_sgl_pos],
- bam_ce_buffer, bam_ce_size);
- bam_txn->cmd_sgl_pos++;
- bam_txn->bam_ce_start = bam_txn->bam_ce_pos;
-
- if (flags & NAND_BAM_NWD) {
- ret = prepare_bam_async_desc(nandc, nandc->cmd_chan,
- DMA_PREP_FENCE |
- DMA_PREP_CMD);
- if (ret)
- return ret;
- }
- }
-
- return 0;
-}
-
-/*
- * Prepares the data descriptor for BAM DMA which will be used for NAND
- * data reads and writes.
- */
-static int prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read,
- const void *vaddr,
- int size, unsigned int flags)
-{
- int ret;
- struct bam_transaction *bam_txn = nandc->bam_txn;
-
- if (read) {
- sg_set_buf(&bam_txn->data_sgl[bam_txn->rx_sgl_pos],
- vaddr, size);
- bam_txn->rx_sgl_pos++;
- } else {
- sg_set_buf(&bam_txn->data_sgl[bam_txn->tx_sgl_pos],
- vaddr, size);
- bam_txn->tx_sgl_pos++;
-
- /*
- * BAM will only set EOT for DMA_PREP_INTERRUPT so if this flag
- * is not set, form the DMA descriptor
- */
- if (!(flags & NAND_BAM_NO_EOT)) {
- ret = prepare_bam_async_desc(nandc, nandc->tx_chan,
- DMA_PREP_INTERRUPT);
- if (ret)
- return ret;
- }
- }
-
- return 0;
-}
-
-static int prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read,
- int reg_off, const void *vaddr, int size,
- bool flow_control)
-{
- struct desc_info *desc;
- struct dma_async_tx_descriptor *dma_desc;
- struct scatterlist *sgl;
- struct dma_slave_config slave_conf;
- enum dma_transfer_direction dir_eng;
- int ret;
-
- desc = kzalloc(sizeof(*desc), GFP_KERNEL);
- if (!desc)
- return -ENOMEM;
-
- sgl = &desc->adm_sgl;
-
- sg_init_one(sgl, vaddr, size);
-
- if (read) {
- dir_eng = DMA_DEV_TO_MEM;
- desc->dir = DMA_FROM_DEVICE;
- } else {
- dir_eng = DMA_MEM_TO_DEV;
- desc->dir = DMA_TO_DEVICE;
- }
-
- ret = dma_map_sg(nandc->dev, sgl, 1, desc->dir);
- if (ret == 0) {
- ret = -ENOMEM;
- goto err;
- }
-
- memset(&slave_conf, 0x00, sizeof(slave_conf));
-
- slave_conf.device_fc = flow_control;
- if (read) {
- slave_conf.src_maxburst = 16;
- slave_conf.src_addr = nandc->base_dma + reg_off;
- slave_conf.slave_id = nandc->data_crci;
- } else {
- slave_conf.dst_maxburst = 16;
- slave_conf.dst_addr = nandc->base_dma + reg_off;
- slave_conf.slave_id = nandc->cmd_crci;
- }
-
- ret = dmaengine_slave_config(nandc->chan, &slave_conf);
- if (ret) {
- dev_err(nandc->dev, "failed to configure dma channel\n");
- goto err;
- }
-
- dma_desc = dmaengine_prep_slave_sg(nandc->chan, sgl, 1, dir_eng, 0);
- if (!dma_desc) {
- dev_err(nandc->dev, "failed to prepare desc\n");
- ret = -EINVAL;
- goto err;
- }
-
- desc->dma_desc = dma_desc;
-
- list_add_tail(&desc->node, &nandc->desc_list);
-
- return 0;
-err:
- kfree(desc);
-
- return ret;
-}
-
-/*
- * read_reg_dma: prepares a descriptor to read a given number of
- * contiguous registers to the reg_read_buf pointer
- *
- * @first: offset of the first register in the contiguous block
- * @num_regs: number of registers to read
- * @flags: flags to control DMA descriptor preparation
- */
-static int read_reg_dma(struct qcom_nand_controller *nandc, int first,
- int num_regs, unsigned int flags)
-{
- bool flow_control = false;
- void *vaddr;
-
- vaddr = nandc->reg_read_buf + nandc->reg_read_pos;
- nandc->reg_read_pos += num_regs;
-
- if (first == NAND_DEV_CMD_VLD || first == NAND_DEV_CMD1)
- first = dev_cmd_reg_addr(nandc, first);
-
- if (nandc->props->is_bam)
- return prep_bam_dma_desc_cmd(nandc, true, first, vaddr,
- num_regs, flags);
-
- if (first == NAND_READ_ID || first == NAND_FLASH_STATUS)
- flow_control = true;
-
- return prep_adm_dma_desc(nandc, true, first, vaddr,
- num_regs * sizeof(u32), flow_control);
-}
-
-/*
- * write_reg_dma: prepares a descriptor to write a given number of
- * contiguous registers
- *
- * @first: offset of the first register in the contiguous block
- * @num_regs: number of registers to write
- * @flags: flags to control DMA descriptor preparation
- */
-static int write_reg_dma(struct qcom_nand_controller *nandc, int first,
- int num_regs, unsigned int flags)
-{
- bool flow_control = false;
- struct nandc_regs *regs = nandc->regs;
- void *vaddr;
-
- vaddr = offset_to_nandc_reg(regs, first);
-
- if (first == NAND_ERASED_CW_DETECT_CFG) {
- if (flags & NAND_ERASED_CW_SET)
- vaddr = &regs->erased_cw_detect_cfg_set;
- else
- vaddr = &regs->erased_cw_detect_cfg_clr;
- }
-
- if (first == NAND_EXEC_CMD)
- flags |= NAND_BAM_NWD;
-
- if (first == NAND_DEV_CMD1_RESTORE || first == NAND_DEV_CMD1)
- first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD1);
-
- if (first == NAND_DEV_CMD_VLD_RESTORE || first == NAND_DEV_CMD_VLD)
- first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD);
-
- if (nandc->props->is_bam)
- return prep_bam_dma_desc_cmd(nandc, false, first, vaddr,
- num_regs, flags);
-
- if (first == NAND_FLASH_CMD)
- flow_control = true;
-
- return prep_adm_dma_desc(nandc, false, first, vaddr,
- num_regs * sizeof(u32), flow_control);
-}
-
-/*
- * read_data_dma: prepares a DMA descriptor to transfer data from the
- * controller's internal buffer to the buffer 'vaddr'
- *
- * @reg_off: offset within the controller's data buffer
- * @vaddr: virtual address of the buffer we want to write to
- * @size: DMA transaction size in bytes
- * @flags: flags to control DMA descriptor preparation
- */
-static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off,
- const u8 *vaddr, int size, unsigned int flags)
-{
- if (nandc->props->is_bam)
- return prep_bam_dma_desc_data(nandc, true, vaddr, size, flags);
-
- return prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false);
-}
-
-/*
- * write_data_dma: prepares a DMA descriptor to transfer data from
- * 'vaddr' to the controller's internal buffer
- *
- * @reg_off: offset within the controller's data buffer
- * @vaddr: virtual address of the buffer we want to read from
- * @size: DMA transaction size in bytes
- * @flags: flags to control DMA descriptor preparation
- */
-static int write_data_dma(struct qcom_nand_controller *nandc, int reg_off,
- const u8 *vaddr, int size, unsigned int flags)
-{
- if (nandc->props->is_bam)
- return prep_bam_dma_desc_data(nandc, false, vaddr, size, flags);
-
- return prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false);
-}
-
-/*
- * Helper to prepare DMA descriptors for configuring registers
- * before reading a NAND page.
- */
-static void config_nand_page_read(struct qcom_nand_controller *nandc)
-{
- write_reg_dma(nandc, NAND_ADDR0, 2, 0);
- write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0);
- write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, 0);
- write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, 0);
- write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1,
- NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL);
-}
-
-/*
- * Helper to prepare DMA descriptors for configuring registers
- * before reading each codeword in NAND page.
- */
-static void config_nand_cw_read(struct qcom_nand_controller *nandc)
-{
- if (nandc->props->is_bam)
- write_reg_dma(nandc, NAND_READ_LOCATION_0, 4,
- NAND_BAM_NEXT_SGL);
-
- write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
- write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
-
- read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0);
- read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1,
- NAND_BAM_NEXT_SGL);
-}
-
-/*
- * Helper to prepare dma descriptors to configure registers needed for reading a
- * single codeword in page
- */
-static void config_nand_single_cw_page_read(struct qcom_nand_controller *nandc)
-{
- config_nand_page_read(nandc);
- config_nand_cw_read(nandc);
-}
-
-/*
- * Helper to prepare DMA descriptors used to configure registers needed for
- * before writing a NAND page.
- */
-static void config_nand_page_write(struct qcom_nand_controller *nandc)
-{
- write_reg_dma(nandc, NAND_ADDR0, 2, 0);
- write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0);
- write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1,
- NAND_BAM_NEXT_SGL);
-}
-
-/*
- * Helper to prepare DMA descriptors for configuring registers
- * before writing each codeword in NAND page.
- */
-static void config_nand_cw_write(struct qcom_nand_controller *nandc)
-{
- write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
- write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
-
- read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
-
- write_reg_dma(nandc, NAND_FLASH_STATUS, 1, 0);
- write_reg_dma(nandc, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL);
-}
-
-/*
- * the following functions are used within chip->cmdfunc() to perform different
- * NAND_CMD_* commands
- */
-
-/* sets up descriptors for NAND_CMD_PARAM */
-static int nandc_param(struct qcom_nand_host *host)
-{
- struct nand_chip *chip = &host->chip;
- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
-
- /*
- * NAND_CMD_PARAM is called before we know much about the FLASH chip
- * in use. we configure the controller to perform a raw read of 512
- * bytes to read onfi params
- */
- nandc_set_reg(nandc, NAND_FLASH_CMD, PAGE_READ | PAGE_ACC | LAST_PAGE);
- nandc_set_reg(nandc, NAND_ADDR0, 0);
- nandc_set_reg(nandc, NAND_ADDR1, 0);
- nandc_set_reg(nandc, NAND_DEV0_CFG0, 0 << CW_PER_PAGE
- | 512 << UD_SIZE_BYTES
- | 5 << NUM_ADDR_CYCLES
- | 0 << SPARE_SIZE_BYTES);
- nandc_set_reg(nandc, NAND_DEV0_CFG1, 7 << NAND_RECOVERY_CYCLES
- | 0 << CS_ACTIVE_BSY
- | 17 << BAD_BLOCK_BYTE_NUM
- | 1 << BAD_BLOCK_IN_SPARE_AREA
- | 2 << WR_RD_BSY_GAP
- | 0 << WIDE_FLASH
- | 1 << DEV0_CFG1_ECC_DISABLE);
- nandc_set_reg(nandc, NAND_EBI2_ECC_BUF_CFG, 1 << ECC_CFG_ECC_DISABLE);
-
- /* configure CMD1 and VLD for ONFI param probing */
- nandc_set_reg(nandc, NAND_DEV_CMD_VLD,
- (nandc->vld & ~READ_START_VLD));
- nandc_set_reg(nandc, NAND_DEV_CMD1,
- (nandc->cmd1 & ~(0xFF << READ_ADDR))
- | NAND_CMD_PARAM << READ_ADDR);
-
- nandc_set_reg(nandc, NAND_EXEC_CMD, 1);
-
- nandc_set_reg(nandc, NAND_DEV_CMD1_RESTORE, nandc->cmd1);
- nandc_set_reg(nandc, NAND_DEV_CMD_VLD_RESTORE, nandc->vld);
- nandc_set_read_loc(nandc, 0, 0, 512, 1);
-
- write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1, 0);
- write_reg_dma(nandc, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL);
-
- nandc->buf_count = 512;
- memset(nandc->data_buffer, 0xff, nandc->buf_count);
-
- config_nand_single_cw_page_read(nandc);
-
- read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer,
- nandc->buf_count, 0);
-
- /* restore CMD1 and VLD regs */
- write_reg_dma(nandc, NAND_DEV_CMD1_RESTORE, 1, 0);
- write_reg_dma(nandc, NAND_DEV_CMD_VLD_RESTORE, 1, NAND_BAM_NEXT_SGL);
-
- return 0;
-}
-
-/* sets up descriptors for NAND_CMD_ERASE1 */
-static int erase_block(struct qcom_nand_host *host, int page_addr)
-{
- struct nand_chip *chip = &host->chip;
- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
-
- nandc_set_reg(nandc, NAND_FLASH_CMD,
- BLOCK_ERASE | PAGE_ACC | LAST_PAGE);
- nandc_set_reg(nandc, NAND_ADDR0, page_addr);
- nandc_set_reg(nandc, NAND_ADDR1, 0);
- nandc_set_reg(nandc, NAND_DEV0_CFG0,
- host->cfg0_raw & ~(7 << CW_PER_PAGE));
- nandc_set_reg(nandc, NAND_DEV0_CFG1, host->cfg1_raw);
- nandc_set_reg(nandc, NAND_EXEC_CMD, 1);
- nandc_set_reg(nandc, NAND_FLASH_STATUS, host->clrflashstatus);
- nandc_set_reg(nandc, NAND_READ_STATUS, host->clrreadstatus);
-
- write_reg_dma(nandc, NAND_FLASH_CMD, 3, NAND_BAM_NEXT_SGL);
- write_reg_dma(nandc, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL);
- write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
-
- read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
-
- write_reg_dma(nandc, NAND_FLASH_STATUS, 1, 0);
- write_reg_dma(nandc, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL);
-
- return 0;
-}
-
-/* sets up descriptors for NAND_CMD_READID */
-static int read_id(struct qcom_nand_host *host, int column)
-{
- struct nand_chip *chip = &host->chip;
- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
-
- if (column == -1)
- return 0;
-
- nandc_set_reg(nandc, NAND_FLASH_CMD, FETCH_ID);
- nandc_set_reg(nandc, NAND_ADDR0, column);
- nandc_set_reg(nandc, NAND_ADDR1, 0);
- nandc_set_reg(nandc, NAND_FLASH_CHIP_SELECT,
- nandc->props->is_bam ? 0 : DM_EN);
- nandc_set_reg(nandc, NAND_EXEC_CMD, 1);
-
- write_reg_dma(nandc, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL);
- write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
-
- read_reg_dma(nandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL);
-
- return 0;
-}
-
-/* sets up descriptors for NAND_CMD_RESET */
-static int reset(struct qcom_nand_host *host)
-{
- struct nand_chip *chip = &host->chip;
- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
-
- nandc_set_reg(nandc, NAND_FLASH_CMD, RESET_DEVICE);
- nandc_set_reg(nandc, NAND_EXEC_CMD, 1);
-
- write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
- write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
-
- read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
-
- return 0;
-}
-
-/* helpers to submit/free our list of dma descriptors */
-static int submit_descs(struct qcom_nand_controller *nandc)
-{
- struct desc_info *desc;
- dma_cookie_t cookie = 0;
- struct bam_transaction *bam_txn = nandc->bam_txn;
- int r;
-
- if (nandc->props->is_bam) {
- if (bam_txn->rx_sgl_pos > bam_txn->rx_sgl_start) {
- r = prepare_bam_async_desc(nandc, nandc->rx_chan, 0);
- if (r)
- return r;
- }
-
- if (bam_txn->tx_sgl_pos > bam_txn->tx_sgl_start) {
- r = prepare_bam_async_desc(nandc, nandc->tx_chan,
- DMA_PREP_INTERRUPT);
- if (r)
- return r;
- }
-
- if (bam_txn->cmd_sgl_pos > bam_txn->cmd_sgl_start) {
- r = prepare_bam_async_desc(nandc, nandc->cmd_chan,
- DMA_PREP_CMD);
- if (r)
- return r;
- }
- }
-
- list_for_each_entry(desc, &nandc->desc_list, node)
- cookie = dmaengine_submit(desc->dma_desc);
-
- if (nandc->props->is_bam) {
- dma_async_issue_pending(nandc->tx_chan);
- dma_async_issue_pending(nandc->rx_chan);
-
- if (dma_sync_wait(nandc->cmd_chan, cookie) != DMA_COMPLETE)
- return -ETIMEDOUT;
- } else {
- if (dma_sync_wait(nandc->chan, cookie) != DMA_COMPLETE)
- return -ETIMEDOUT;
- }
-
- return 0;
-}
-
-static void free_descs(struct qcom_nand_controller *nandc)
-{
- struct desc_info *desc, *n;
-
- list_for_each_entry_safe(desc, n, &nandc->desc_list, node) {
- list_del(&desc->node);
-
- if (nandc->props->is_bam)
- dma_unmap_sg(nandc->dev, desc->bam_sgl,
- desc->sgl_cnt, desc->dir);
- else
- dma_unmap_sg(nandc->dev, &desc->adm_sgl, 1,
- desc->dir);
-
- kfree(desc);
- }
-}
-
-/* reset the register read buffer for next NAND operation */
-static void clear_read_regs(struct qcom_nand_controller *nandc)
-{
- nandc->reg_read_pos = 0;
- nandc_read_buffer_sync(nandc, false);
-}
-
-static void pre_command(struct qcom_nand_host *host, int command)
-{
- struct nand_chip *chip = &host->chip;
- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
-
- nandc->buf_count = 0;
- nandc->buf_start = 0;
- host->use_ecc = false;
- host->last_command = command;
-
- clear_read_regs(nandc);
-
- if (command == NAND_CMD_RESET || command == NAND_CMD_READID ||
- command == NAND_CMD_PARAM || command == NAND_CMD_ERASE1)
- clear_bam_transaction(nandc);
-}
-
-/*
- * this is called after NAND_CMD_PAGEPROG and NAND_CMD_ERASE1 to set our
- * privately maintained status byte, this status byte can be read after
- * NAND_CMD_STATUS is called
- */
-static void parse_erase_write_errors(struct qcom_nand_host *host, int command)
-{
- struct nand_chip *chip = &host->chip;
- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
- int num_cw;
- int i;
-
- num_cw = command == NAND_CMD_PAGEPROG ? ecc->steps : 1;
- nandc_read_buffer_sync(nandc, true);
-
- for (i = 0; i < num_cw; i++) {
- u32 flash_status = le32_to_cpu(nandc->reg_read_buf[i]);
-
- if (flash_status & FS_MPU_ERR)
- host->status &= ~NAND_STATUS_WP;
-
- if (flash_status & FS_OP_ERR || (i == (num_cw - 1) &&
- (flash_status &
- FS_DEVICE_STS_ERR)))
- host->status |= NAND_STATUS_FAIL;
- }
-}
-
-static void post_command(struct qcom_nand_host *host, int command)
-{
- struct nand_chip *chip = &host->chip;
- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
-
- switch (command) {
- case NAND_CMD_READID:
- nandc_read_buffer_sync(nandc, true);
- memcpy(nandc->data_buffer, nandc->reg_read_buf,
- nandc->buf_count);
- break;
- case NAND_CMD_PAGEPROG:
- case NAND_CMD_ERASE1:
- parse_erase_write_errors(host, command);
- break;
- default:
- break;
- }
-}
-
-/*
- * Implements chip->cmdfunc. It's only used for a limited set of commands.
- * The rest of the commands wouldn't be called by upper layers. For example,
- * NAND_CMD_READOOB would never be called because we have our own versions
- * of read_oob ops for nand_ecc_ctrl.
- */
-static void qcom_nandc_command(struct mtd_info *mtd, unsigned int command,
- int column, int page_addr)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct qcom_nand_host *host = to_qcom_nand_host(chip);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
- bool wait = false;
- int ret = 0;
-
- pre_command(host, command);
-
- switch (command) {
- case NAND_CMD_RESET:
- ret = reset(host);
- wait = true;
- break;
-
- case NAND_CMD_READID:
- nandc->buf_count = 4;
- ret = read_id(host, column);
- wait = true;
- break;
-
- case NAND_CMD_PARAM:
- ret = nandc_param(host);
- wait = true;
- break;
-
- case NAND_CMD_ERASE1:
- ret = erase_block(host, page_addr);
- wait = true;
- break;
-
- case NAND_CMD_READ0:
- /* we read the entire page for now */
- WARN_ON(column != 0);
-
- host->use_ecc = true;
- set_address(host, 0, page_addr);
- update_rw_regs(host, ecc->steps, true);
- break;
-
- case NAND_CMD_SEQIN:
- WARN_ON(column != 0);
- set_address(host, 0, page_addr);
- break;
-
- case NAND_CMD_PAGEPROG:
- case NAND_CMD_STATUS:
- case NAND_CMD_NONE:
- default:
- break;
- }
-
- if (ret) {
- dev_err(nandc->dev, "failure executing command %d\n",
- command);
- free_descs(nandc);
- return;
- }
-
- if (wait) {
- ret = submit_descs(nandc);
- if (ret)
- dev_err(nandc->dev,
- "failure submitting descs for command %d\n",
- command);
- }
-
- free_descs(nandc);
-
- post_command(host, command);
-}
-
-/*
- * when using BCH ECC, the HW flags an error in NAND_FLASH_STATUS if it read
- * an erased CW, and reports an erased CW in NAND_ERASED_CW_DETECT_STATUS.
- *
- * when using RS ECC, the HW reports the same erros when reading an erased CW,
- * but it notifies that it is an erased CW by placing special characters at
- * certain offsets in the buffer.
- *
- * verify if the page is erased or not, and fix up the page for RS ECC by
- * replacing the special characters with 0xff.
- */
-static bool erased_chunk_check_and_fixup(u8 *data_buf, int data_len)
-{
- u8 empty1, empty2;
-
- /*
- * an erased page flags an error in NAND_FLASH_STATUS, check if the page
- * is erased by looking for 0x54s at offsets 3 and 175 from the
- * beginning of each codeword
- */
-
- empty1 = data_buf[3];
- empty2 = data_buf[175];
-
- /*
- * if the erased codework markers, if they exist override them with
- * 0xffs
- */
- if ((empty1 == 0x54 && empty2 == 0xff) ||
- (empty1 == 0xff && empty2 == 0x54)) {
- data_buf[3] = 0xff;
- data_buf[175] = 0xff;
- }
-
- /*
- * check if the entire chunk contains 0xffs or not. if it doesn't, then
- * restore the original values at the special offsets
- */
- if (memchr_inv(data_buf, 0xff, data_len)) {
- data_buf[3] = empty1;
- data_buf[175] = empty2;
-
- return false;
- }
-
- return true;
-}
-
-struct read_stats {
- __le32 flash;
- __le32 buffer;
- __le32 erased_cw;
-};
-
-/*
- * reads back status registers set by the controller to notify page read
- * errors. this is equivalent to what 'ecc->correct()' would do.
- */
-static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf,
- u8 *oob_buf)
-{
- struct nand_chip *chip = &host->chip;
- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
- unsigned int max_bitflips = 0;
- struct read_stats *buf;
- int i;
-
- buf = (struct read_stats *)nandc->reg_read_buf;
- nandc_read_buffer_sync(nandc, true);
-
- for (i = 0; i < ecc->steps; i++, buf++) {
- u32 flash, buffer, erased_cw;
- int data_len, oob_len;
-
- if (i == (ecc->steps - 1)) {
- data_len = ecc->size - ((ecc->steps - 1) << 2);
- oob_len = ecc->steps << 2;
- } else {
- data_len = host->cw_data;
- oob_len = 0;
- }
-
- flash = le32_to_cpu(buf->flash);
- buffer = le32_to_cpu(buf->buffer);
- erased_cw = le32_to_cpu(buf->erased_cw);
-
- if (flash & (FS_OP_ERR | FS_MPU_ERR)) {
- bool erased;
-
- /* ignore erased codeword errors */
- if (host->bch_enabled) {
- erased = (erased_cw & ERASED_CW) == ERASED_CW ?
- true : false;
- } else {
- erased = erased_chunk_check_and_fixup(data_buf,
- data_len);
- }
-
- if (erased) {
- data_buf += data_len;
- if (oob_buf)
- oob_buf += oob_len + ecc->bytes;
- continue;
- }
-
- if (buffer & BS_UNCORRECTABLE_BIT) {
- int ret, ecclen, extraooblen;
- void *eccbuf;
-
- eccbuf = oob_buf ? oob_buf + oob_len : NULL;
- ecclen = oob_buf ? host->ecc_bytes_hw : 0;
- extraooblen = oob_buf ? oob_len : 0;
-
- /*
- * make sure it isn't an erased page reported
- * as not-erased by HW because of a few bitflips
- */
- ret = nand_check_erased_ecc_chunk(data_buf,
- data_len, eccbuf, ecclen, oob_buf,
- extraooblen, ecc->strength);
- if (ret < 0) {
- mtd->ecc_stats.failed++;
- } else {
- mtd->ecc_stats.corrected += ret;
- max_bitflips =
- max_t(unsigned int, max_bitflips, ret);
- }
- }
- } else {
- unsigned int stat;
-
- stat = buffer & BS_CORRECTABLE_ERR_MSK;
- mtd->ecc_stats.corrected += stat;
- max_bitflips = max(max_bitflips, stat);
- }
-
- data_buf += data_len;
- if (oob_buf)
- oob_buf += oob_len + ecc->bytes;
- }
-
- return max_bitflips;
-}
-
-/*
- * helper to perform the actual page read operation, used by ecc->read_page(),
- * ecc->read_oob()
- */
-static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf,
- u8 *oob_buf)
-{
- struct nand_chip *chip = &host->chip;
- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
- int i, ret;
-
- config_nand_page_read(nandc);
-
- /* queue cmd descs for each codeword */
- for (i = 0; i < ecc->steps; i++) {
- int data_size, oob_size;
-
- if (i == (ecc->steps - 1)) {
- data_size = ecc->size - ((ecc->steps - 1) << 2);
- oob_size = (ecc->steps << 2) + host->ecc_bytes_hw +
- host->spare_bytes;
- } else {
- data_size = host->cw_data;
- oob_size = host->ecc_bytes_hw + host->spare_bytes;
- }
-
- if (nandc->props->is_bam) {
- if (data_buf && oob_buf) {
- nandc_set_read_loc(nandc, 0, 0, data_size, 0);
- nandc_set_read_loc(nandc, 1, data_size,
- oob_size, 1);
- } else if (data_buf) {
- nandc_set_read_loc(nandc, 0, 0, data_size, 1);
- } else {
- nandc_set_read_loc(nandc, 0, data_size,
- oob_size, 1);
- }
- }
-
- config_nand_cw_read(nandc);
-
- if (data_buf)
- read_data_dma(nandc, FLASH_BUF_ACC, data_buf,
- data_size, 0);
-
- /*
- * when ecc is enabled, the controller doesn't read the real
- * or dummy bad block markers in each chunk. To maintain a
- * consistent layout across RAW and ECC reads, we just
- * leave the real/dummy BBM offsets empty (i.e, filled with
- * 0xffs)
- */
- if (oob_buf) {
- int j;
-
- for (j = 0; j < host->bbm_size; j++)
- *oob_buf++ = 0xff;
-
- read_data_dma(nandc, FLASH_BUF_ACC + data_size,
- oob_buf, oob_size, 0);
- }
-
- if (data_buf)
- data_buf += data_size;
- if (oob_buf)
- oob_buf += oob_size;
- }
-
- ret = submit_descs(nandc);
- if (ret)
- dev_err(nandc->dev, "failure to read page/oob\n");
-
- free_descs(nandc);
-
- return ret;
-}
-
-/*
- * a helper that copies the last step/codeword of a page (containing free oob)
- * into our local buffer
- */
-static int copy_last_cw(struct qcom_nand_host *host, int page)
-{
- struct nand_chip *chip = &host->chip;
- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
- int size;
- int ret;
-
- clear_read_regs(nandc);
-
- size = host->use_ecc ? host->cw_data : host->cw_size;
-
- /* prepare a clean read buffer */
- memset(nandc->data_buffer, 0xff, size);
-
- set_address(host, host->cw_size * (ecc->steps - 1), page);
- update_rw_regs(host, 1, true);
-
- config_nand_single_cw_page_read(nandc);
-
- read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0);
-
- ret = submit_descs(nandc);
- if (ret)
- dev_err(nandc->dev, "failed to copy last codeword\n");
-
- free_descs(nandc);
-
- return ret;
-}
-
-/* implements ecc->read_page() */
-static int qcom_nandc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page)
-{
- struct qcom_nand_host *host = to_qcom_nand_host(chip);
- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
- u8 *data_buf, *oob_buf = NULL;
- int ret;
-
- nand_read_page_op(chip, page, 0, NULL, 0);
- data_buf = buf;
- oob_buf = oob_required ? chip->oob_poi : NULL;
-
- clear_bam_transaction(nandc);
- ret = read_page_ecc(host, data_buf, oob_buf);
- if (ret) {
- dev_err(nandc->dev, "failure to read page\n");
- return ret;
- }
-
- return parse_read_errors(host, data_buf, oob_buf);
-}
-
-/* implements ecc->read_page_raw() */
-static int qcom_nandc_read_page_raw(struct mtd_info *mtd,
- struct nand_chip *chip, uint8_t *buf,
- int oob_required, int page)
-{
- struct qcom_nand_host *host = to_qcom_nand_host(chip);
- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
- u8 *data_buf, *oob_buf;
- struct nand_ecc_ctrl *ecc = &chip->ecc;
- int i, ret;
- int read_loc;
-
- nand_read_page_op(chip, page, 0, NULL, 0);
- data_buf = buf;
- oob_buf = chip->oob_poi;
-
- host->use_ecc = false;
-
- clear_bam_transaction(nandc);
- update_rw_regs(host, ecc->steps, true);
- config_nand_page_read(nandc);
-
- for (i = 0; i < ecc->steps; i++) {
- int data_size1, data_size2, oob_size1, oob_size2;
- int reg_off = FLASH_BUF_ACC;
-
- data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1);
- oob_size1 = host->bbm_size;
-
- if (i == (ecc->steps - 1)) {
- data_size2 = ecc->size - data_size1 -
- ((ecc->steps - 1) << 2);
- oob_size2 = (ecc->steps << 2) + host->ecc_bytes_hw +
- host->spare_bytes;
- } else {
- data_size2 = host->cw_data - data_size1;
- oob_size2 = host->ecc_bytes_hw + host->spare_bytes;
- }
-
- if (nandc->props->is_bam) {
- read_loc = 0;
- nandc_set_read_loc(nandc, 0, read_loc, data_size1, 0);
- read_loc += data_size1;
-
- nandc_set_read_loc(nandc, 1, read_loc, oob_size1, 0);
- read_loc += oob_size1;
-
- nandc_set_read_loc(nandc, 2, read_loc, data_size2, 0);
- read_loc += data_size2;
-
- nandc_set_read_loc(nandc, 3, read_loc, oob_size2, 1);
- }
-
- config_nand_cw_read(nandc);
-
- read_data_dma(nandc, reg_off, data_buf, data_size1, 0);
- reg_off += data_size1;
- data_buf += data_size1;
-
- read_data_dma(nandc, reg_off, oob_buf, oob_size1, 0);
- reg_off += oob_size1;
- oob_buf += oob_size1;
-
- read_data_dma(nandc, reg_off, data_buf, data_size2, 0);
- reg_off += data_size2;
- data_buf += data_size2;
-
- read_data_dma(nandc, reg_off, oob_buf, oob_size2, 0);
- oob_buf += oob_size2;
- }
-
- ret = submit_descs(nandc);
- if (ret)
- dev_err(nandc->dev, "failure to read raw page\n");
-
- free_descs(nandc);
-
- return 0;
-}
-
-/* implements ecc->read_oob() */
-static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
- int page)
-{
- struct qcom_nand_host *host = to_qcom_nand_host(chip);
- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
- int ret;
-
- clear_read_regs(nandc);
- clear_bam_transaction(nandc);
-
- host->use_ecc = true;
- set_address(host, 0, page);
- update_rw_regs(host, ecc->steps, true);
-
- ret = read_page_ecc(host, NULL, chip->oob_poi);
- if (ret)
- dev_err(nandc->dev, "failure to read oob\n");
-
- return ret;
-}
-
-/* implements ecc->write_page() */
-static int qcom_nandc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf, int oob_required, int page)
-{
- struct qcom_nand_host *host = to_qcom_nand_host(chip);
- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
- u8 *data_buf, *oob_buf;
- int i, ret;
-
- nand_prog_page_begin_op(chip, page, 0, NULL, 0);
-
- clear_read_regs(nandc);
- clear_bam_transaction(nandc);
-
- data_buf = (u8 *)buf;
- oob_buf = chip->oob_poi;
-
- host->use_ecc = true;
- update_rw_regs(host, ecc->steps, false);
- config_nand_page_write(nandc);
-
- for (i = 0; i < ecc->steps; i++) {
- int data_size, oob_size;
-
- if (i == (ecc->steps - 1)) {
- data_size = ecc->size - ((ecc->steps - 1) << 2);
- oob_size = (ecc->steps << 2) + host->ecc_bytes_hw +
- host->spare_bytes;
- } else {
- data_size = host->cw_data;
- oob_size = ecc->bytes;
- }
-
-
- write_data_dma(nandc, FLASH_BUF_ACC, data_buf, data_size,
- i == (ecc->steps - 1) ? NAND_BAM_NO_EOT : 0);
-
- /*
- * when ECC is enabled, we don't really need to write anything
- * to oob for the first n - 1 codewords since these oob regions
- * just contain ECC bytes that's written by the controller
- * itself. For the last codeword, we skip the bbm positions and
- * write to the free oob area.
- */
- if (i == (ecc->steps - 1)) {
- oob_buf += host->bbm_size;
-
- write_data_dma(nandc, FLASH_BUF_ACC + data_size,
- oob_buf, oob_size, 0);
- }
-
- config_nand_cw_write(nandc);
-
- data_buf += data_size;
- oob_buf += oob_size;
- }
-
- ret = submit_descs(nandc);
- if (ret)
- dev_err(nandc->dev, "failure to write page\n");
-
- free_descs(nandc);
-
- if (!ret)
- ret = nand_prog_page_end_op(chip);
-
- return ret;
-}
-
-/* implements ecc->write_page_raw() */
-static int qcom_nandc_write_page_raw(struct mtd_info *mtd,
- struct nand_chip *chip, const uint8_t *buf,
- int oob_required, int page)
-{
- struct qcom_nand_host *host = to_qcom_nand_host(chip);
- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
- u8 *data_buf, *oob_buf;
- int i, ret;
-
- nand_prog_page_begin_op(chip, page, 0, NULL, 0);
- clear_read_regs(nandc);
- clear_bam_transaction(nandc);
-
- data_buf = (u8 *)buf;
- oob_buf = chip->oob_poi;
-
- host->use_ecc = false;
- update_rw_regs(host, ecc->steps, false);
- config_nand_page_write(nandc);
-
- for (i = 0; i < ecc->steps; i++) {
- int data_size1, data_size2, oob_size1, oob_size2;
- int reg_off = FLASH_BUF_ACC;
-
- data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1);
- oob_size1 = host->bbm_size;
-
- if (i == (ecc->steps - 1)) {
- data_size2 = ecc->size - data_size1 -
- ((ecc->steps - 1) << 2);
- oob_size2 = (ecc->steps << 2) + host->ecc_bytes_hw +
- host->spare_bytes;
- } else {
- data_size2 = host->cw_data - data_size1;
- oob_size2 = host->ecc_bytes_hw + host->spare_bytes;
- }
-
- write_data_dma(nandc, reg_off, data_buf, data_size1,
- NAND_BAM_NO_EOT);
- reg_off += data_size1;
- data_buf += data_size1;
-
- write_data_dma(nandc, reg_off, oob_buf, oob_size1,
- NAND_BAM_NO_EOT);
- reg_off += oob_size1;
- oob_buf += oob_size1;
-
- write_data_dma(nandc, reg_off, data_buf, data_size2,
- NAND_BAM_NO_EOT);
- reg_off += data_size2;
- data_buf += data_size2;
-
- write_data_dma(nandc, reg_off, oob_buf, oob_size2, 0);
- oob_buf += oob_size2;
-
- config_nand_cw_write(nandc);
- }
-
- ret = submit_descs(nandc);
- if (ret)
- dev_err(nandc->dev, "failure to write raw page\n");
-
- free_descs(nandc);
-
- if (!ret)
- ret = nand_prog_page_end_op(chip);
-
- return ret;
-}
-
-/*
- * implements ecc->write_oob()
- *
- * the NAND controller cannot write only data or only oob within a codeword,
- * since ecc is calculated for the combined codeword. we first copy the
- * entire contents for the last codeword(data + oob), replace the old oob
- * with the new one in chip->oob_poi, and then write the entire codeword.
- * this read-copy-write operation results in a slight performance loss.
- */
-static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
- int page)
-{
- struct qcom_nand_host *host = to_qcom_nand_host(chip);
- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
- u8 *oob = chip->oob_poi;
- int data_size, oob_size;
- int ret;
-
- host->use_ecc = true;
-
- clear_bam_transaction(nandc);
- ret = copy_last_cw(host, page);
- if (ret)
- return ret;
-
- clear_read_regs(nandc);
- clear_bam_transaction(nandc);
-
- /* calculate the data and oob size for the last codeword/step */
- data_size = ecc->size - ((ecc->steps - 1) << 2);
- oob_size = mtd->oobavail;
-
- /* override new oob content to last codeword */
- mtd_ooblayout_get_databytes(mtd, nandc->data_buffer + data_size, oob,
- 0, mtd->oobavail);
-
- set_address(host, host->cw_size * (ecc->steps - 1), page);
- update_rw_regs(host, 1, false);
-
- config_nand_page_write(nandc);
- write_data_dma(nandc, FLASH_BUF_ACC,
- nandc->data_buffer, data_size + oob_size, 0);
- config_nand_cw_write(nandc);
-
- ret = submit_descs(nandc);
-
- free_descs(nandc);
-
- if (ret) {
- dev_err(nandc->dev, "failure to write oob\n");
- return -EIO;
- }
-
- return nand_prog_page_end_op(chip);
-}
-
-static int qcom_nandc_block_bad(struct mtd_info *mtd, loff_t ofs)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct qcom_nand_host *host = to_qcom_nand_host(chip);
- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
- int page, ret, bbpos, bad = 0;
- u32 flash_status;
-
- page = (int)(ofs >> chip->page_shift) & chip->pagemask;
-
- /*
- * configure registers for a raw sub page read, the address is set to
- * the beginning of the last codeword, we don't care about reading ecc
- * portion of oob. we just want the first few bytes from this codeword
- * that contains the BBM
- */
- host->use_ecc = false;
-
- clear_bam_transaction(nandc);
- ret = copy_last_cw(host, page);
- if (ret)
- goto err;
-
- flash_status = le32_to_cpu(nandc->reg_read_buf[0]);
-
- if (flash_status & (FS_OP_ERR | FS_MPU_ERR)) {
- dev_warn(nandc->dev, "error when trying to read BBM\n");
- goto err;
- }
-
- bbpos = mtd->writesize - host->cw_size * (ecc->steps - 1);
-
- bad = nandc->data_buffer[bbpos] != 0xff;
-
- if (chip->options & NAND_BUSWIDTH_16)
- bad = bad || (nandc->data_buffer[bbpos + 1] != 0xff);
-err:
- return bad;
-}
-
-static int qcom_nandc_block_markbad(struct mtd_info *mtd, loff_t ofs)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct qcom_nand_host *host = to_qcom_nand_host(chip);
- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
- int page, ret;
-
- clear_read_regs(nandc);
- clear_bam_transaction(nandc);
-
- /*
- * to mark the BBM as bad, we flash the entire last codeword with 0s.
- * we don't care about the rest of the content in the codeword since
- * we aren't going to use this block again
- */
- memset(nandc->data_buffer, 0x00, host->cw_size);
-
- page = (int)(ofs >> chip->page_shift) & chip->pagemask;
-
- /* prepare write */
- host->use_ecc = false;
- set_address(host, host->cw_size * (ecc->steps - 1), page);
- update_rw_regs(host, 1, false);
-
- config_nand_page_write(nandc);
- write_data_dma(nandc, FLASH_BUF_ACC,
- nandc->data_buffer, host->cw_size, 0);
- config_nand_cw_write(nandc);
-
- ret = submit_descs(nandc);
-
- free_descs(nandc);
-
- if (ret) {
- dev_err(nandc->dev, "failure to update BBM\n");
- return -EIO;
- }
-
- return nand_prog_page_end_op(chip);
-}
-
-/*
- * the three functions below implement chip->read_byte(), chip->read_buf()
- * and chip->write_buf() respectively. these aren't used for
- * reading/writing page data, they are used for smaller data like reading
- * id, status etc
- */
-static uint8_t qcom_nandc_read_byte(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct qcom_nand_host *host = to_qcom_nand_host(chip);
- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
- u8 *buf = nandc->data_buffer;
- u8 ret = 0x0;
-
- if (host->last_command == NAND_CMD_STATUS) {
- ret = host->status;
-
- host->status = NAND_STATUS_READY | NAND_STATUS_WP;
-
- return ret;
- }
-
- if (nandc->buf_start < nandc->buf_count)
- ret = buf[nandc->buf_start++];
-
- return ret;
-}
-
-static void qcom_nandc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
- int real_len = min_t(size_t, len, nandc->buf_count - nandc->buf_start);
-
- memcpy(buf, nandc->data_buffer + nandc->buf_start, real_len);
- nandc->buf_start += real_len;
-}
-
-static void qcom_nandc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
- int len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
- int real_len = min_t(size_t, len, nandc->buf_count - nandc->buf_start);
-
- memcpy(nandc->data_buffer + nandc->buf_start, buf, real_len);
-
- nandc->buf_start += real_len;
-}
-
-/* we support only one external chip for now */
-static void qcom_nandc_select_chip(struct mtd_info *mtd, int chipnr)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
-
- if (chipnr <= 0)
- return;
-
- dev_warn(nandc->dev, "invalid chip select\n");
-}
-
-/*
- * NAND controller page layout info
- *
- * Layout with ECC enabled:
- *
- * |----------------------| |---------------------------------|
- * | xx.......yy| | *********xx.......yy|
- * | DATA xx..ECC..yy| | DATA **SPARE**xx..ECC..yy|
- * | (516) xx.......yy| | (516-n*4) **(n*4)**xx.......yy|
- * | xx.......yy| | *********xx.......yy|
- * |----------------------| |---------------------------------|
- * codeword 1,2..n-1 codeword n
- * <---(528/532 Bytes)--> <-------(528/532 Bytes)--------->
- *
- * n = Number of codewords in the page
- * . = ECC bytes
- * * = Spare/free bytes
- * x = Unused byte(s)
- * y = Reserved byte(s)
- *
- * 2K page: n = 4, spare = 16 bytes
- * 4K page: n = 8, spare = 32 bytes
- * 8K page: n = 16, spare = 64 bytes
- *
- * the qcom nand controller operates at a sub page/codeword level. each
- * codeword is 528 and 532 bytes for 4 bit and 8 bit ECC modes respectively.
- * the number of ECC bytes vary based on the ECC strength and the bus width.
- *
- * the first n - 1 codewords contains 516 bytes of user data, the remaining
- * 12/16 bytes consist of ECC and reserved data. The nth codeword contains
- * both user data and spare(oobavail) bytes that sum up to 516 bytes.
- *
- * When we access a page with ECC enabled, the reserved bytes(s) are not
- * accessible at all. When reading, we fill up these unreadable positions
- * with 0xffs. When writing, the controller skips writing the inaccessible
- * bytes.
- *
- * Layout with ECC disabled:
- *
- * |------------------------------| |---------------------------------------|
- * | yy xx.......| | bb *********xx.......|
- * | DATA1 yy DATA2 xx..ECC..| | DATA1 bb DATA2 **SPARE**xx..ECC..|
- * | (size1) yy (size2) xx.......| | (size1) bb (size2) **(n*4)**xx.......|
- * | yy xx.......| | bb *********xx.......|
- * |------------------------------| |---------------------------------------|
- * codeword 1,2..n-1 codeword n
- * <-------(528/532 Bytes)------> <-----------(528/532 Bytes)----------->
- *
- * n = Number of codewords in the page
- * . = ECC bytes
- * * = Spare/free bytes
- * x = Unused byte(s)
- * y = Dummy Bad Bock byte(s)
- * b = Real Bad Block byte(s)
- * size1/size2 = function of codeword size and 'n'
- *
- * when the ECC block is disabled, one reserved byte (or two for 16 bit bus
- * width) is now accessible. For the first n - 1 codewords, these are dummy Bad
- * Block Markers. In the last codeword, this position contains the real BBM
- *
- * In order to have a consistent layout between RAW and ECC modes, we assume
- * the following OOB layout arrangement:
- *
- * |-----------| |--------------------|
- * |yyxx.......| |bb*********xx.......|
- * |yyxx..ECC..| |bb*FREEOOB*xx..ECC..|
- * |yyxx.......| |bb*********xx.......|
- * |yyxx.......| |bb*********xx.......|
- * |-----------| |--------------------|
- * first n - 1 nth OOB region
- * OOB regions
- *
- * n = Number of codewords in the page
- * . = ECC bytes
- * * = FREE OOB bytes
- * y = Dummy bad block byte(s) (inaccessible when ECC enabled)
- * x = Unused byte(s)
- * b = Real bad block byte(s) (inaccessible when ECC enabled)
- *
- * This layout is read as is when ECC is disabled. When ECC is enabled, the
- * inaccessible Bad Block byte(s) are ignored when we write to a page/oob,
- * and assumed as 0xffs when we read a page/oob. The ECC, unused and
- * dummy/real bad block bytes are grouped as ecc bytes (i.e, ecc->bytes is
- * the sum of the three).
- */
-static int qcom_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct qcom_nand_host *host = to_qcom_nand_host(chip);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
-
- if (section > 1)
- return -ERANGE;
-
- if (!section) {
- oobregion->length = (ecc->bytes * (ecc->steps - 1)) +
- host->bbm_size;
- oobregion->offset = 0;
- } else {
- oobregion->length = host->ecc_bytes_hw + host->spare_bytes;
- oobregion->offset = mtd->oobsize - oobregion->length;
- }
-
- return 0;
-}
-
-static int qcom_nand_ooblayout_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct qcom_nand_host *host = to_qcom_nand_host(chip);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
-
- if (section)
- return -ERANGE;
-
- oobregion->length = ecc->steps * 4;
- oobregion->offset = ((ecc->steps - 1) * ecc->bytes) + host->bbm_size;
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops qcom_nand_ooblayout_ops = {
- .ecc = qcom_nand_ooblayout_ecc,
- .free = qcom_nand_ooblayout_free,
-};
-
-static int qcom_nand_host_setup(struct qcom_nand_host *host)
-{
- struct nand_chip *chip = &host->chip;
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
- int cwperpage, bad_block_byte;
- bool wide_bus;
- int ecc_mode = 1;
-
- /*
- * the controller requires each step consists of 512 bytes of data.
- * bail out if DT has populated a wrong step size.
- */
- if (ecc->size != NANDC_STEP_SIZE) {
- dev_err(nandc->dev, "invalid ecc size\n");
- return -EINVAL;
- }
-
- wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false;
-
- if (ecc->strength >= 8) {
- /* 8 bit ECC defaults to BCH ECC on all platforms */
- host->bch_enabled = true;
- ecc_mode = 1;
-
- if (wide_bus) {
- host->ecc_bytes_hw = 14;
- host->spare_bytes = 0;
- host->bbm_size = 2;
- } else {
- host->ecc_bytes_hw = 13;
- host->spare_bytes = 2;
- host->bbm_size = 1;
- }
- } else {
- /*
- * if the controller supports BCH for 4 bit ECC, the controller
- * uses lesser bytes for ECC. If RS is used, the ECC bytes is
- * always 10 bytes
- */
- if (nandc->props->ecc_modes & ECC_BCH_4BIT) {
- /* BCH */
- host->bch_enabled = true;
- ecc_mode = 0;
-
- if (wide_bus) {
- host->ecc_bytes_hw = 8;
- host->spare_bytes = 2;
- host->bbm_size = 2;
- } else {
- host->ecc_bytes_hw = 7;
- host->spare_bytes = 4;
- host->bbm_size = 1;
- }
- } else {
- /* RS */
- host->ecc_bytes_hw = 10;
-
- if (wide_bus) {
- host->spare_bytes = 0;
- host->bbm_size = 2;
- } else {
- host->spare_bytes = 1;
- host->bbm_size = 1;
- }
- }
- }
-
- /*
- * we consider ecc->bytes as the sum of all the non-data content in a
- * step. It gives us a clean representation of the oob area (even if
- * all the bytes aren't used for ECC).It is always 16 bytes for 8 bit
- * ECC and 12 bytes for 4 bit ECC
- */
- ecc->bytes = host->ecc_bytes_hw + host->spare_bytes + host->bbm_size;
-
- ecc->read_page = qcom_nandc_read_page;
- ecc->read_page_raw = qcom_nandc_read_page_raw;
- ecc->read_oob = qcom_nandc_read_oob;
- ecc->write_page = qcom_nandc_write_page;
- ecc->write_page_raw = qcom_nandc_write_page_raw;
- ecc->write_oob = qcom_nandc_write_oob;
-
- ecc->mode = NAND_ECC_HW;
-
- mtd_set_ooblayout(mtd, &qcom_nand_ooblayout_ops);
-
- cwperpage = mtd->writesize / ecc->size;
- nandc->max_cwperpage = max_t(unsigned int, nandc->max_cwperpage,
- cwperpage);
-
- /*
- * DATA_UD_BYTES varies based on whether the read/write command protects
- * spare data with ECC too. We protect spare data by default, so we set
- * it to main + spare data, which are 512 and 4 bytes respectively.
- */
- host->cw_data = 516;
-
- /*
- * total bytes in a step, either 528 bytes for 4 bit ECC, or 532 bytes
- * for 8 bit ECC
- */
- host->cw_size = host->cw_data + ecc->bytes;
-
- if (ecc->bytes * (mtd->writesize / ecc->size) > mtd->oobsize) {
- dev_err(nandc->dev, "ecc data doesn't fit in OOB area\n");
- return -EINVAL;
- }
-
- bad_block_byte = mtd->writesize - host->cw_size * (cwperpage - 1) + 1;
-
- host->cfg0 = (cwperpage - 1) << CW_PER_PAGE
- | host->cw_data << UD_SIZE_BYTES
- | 0 << DISABLE_STATUS_AFTER_WRITE
- | 5 << NUM_ADDR_CYCLES
- | host->ecc_bytes_hw << ECC_PARITY_SIZE_BYTES_RS
- | 0 << STATUS_BFR_READ
- | 1 << SET_RD_MODE_AFTER_STATUS
- | host->spare_bytes << SPARE_SIZE_BYTES;
-
- host->cfg1 = 7 << NAND_RECOVERY_CYCLES
- | 0 << CS_ACTIVE_BSY
- | bad_block_byte << BAD_BLOCK_BYTE_NUM
- | 0 << BAD_BLOCK_IN_SPARE_AREA
- | 2 << WR_RD_BSY_GAP
- | wide_bus << WIDE_FLASH
- | host->bch_enabled << ENABLE_BCH_ECC;
-
- host->cfg0_raw = (cwperpage - 1) << CW_PER_PAGE
- | host->cw_size << UD_SIZE_BYTES
- | 5 << NUM_ADDR_CYCLES
- | 0 << SPARE_SIZE_BYTES;
-
- host->cfg1_raw = 7 << NAND_RECOVERY_CYCLES
- | 0 << CS_ACTIVE_BSY
- | 17 << BAD_BLOCK_BYTE_NUM
- | 1 << BAD_BLOCK_IN_SPARE_AREA
- | 2 << WR_RD_BSY_GAP
- | wide_bus << WIDE_FLASH
- | 1 << DEV0_CFG1_ECC_DISABLE;
-
- host->ecc_bch_cfg = !host->bch_enabled << ECC_CFG_ECC_DISABLE
- | 0 << ECC_SW_RESET
- | host->cw_data << ECC_NUM_DATA_BYTES
- | 1 << ECC_FORCE_CLK_OPEN
- | ecc_mode << ECC_MODE
- | host->ecc_bytes_hw << ECC_PARITY_SIZE_BYTES_BCH;
-
- host->ecc_buf_cfg = 0x203 << NUM_STEPS;
-
- host->clrflashstatus = FS_READY_BSY_N;
- host->clrreadstatus = 0xc0;
- nandc->regs->erased_cw_detect_cfg_clr =
- cpu_to_le32(CLR_ERASED_PAGE_DET);
- nandc->regs->erased_cw_detect_cfg_set =
- cpu_to_le32(SET_ERASED_PAGE_DET);
-
- dev_dbg(nandc->dev,
- "cfg0 %x cfg1 %x ecc_buf_cfg %x ecc_bch cfg %x cw_size %d cw_data %d strength %d parity_bytes %d steps %d\n",
- host->cfg0, host->cfg1, host->ecc_buf_cfg, host->ecc_bch_cfg,
- host->cw_size, host->cw_data, ecc->strength, ecc->bytes,
- cwperpage);
-
- return 0;
-}
-
-static int qcom_nandc_alloc(struct qcom_nand_controller *nandc)
-{
- int ret;
-
- ret = dma_set_coherent_mask(nandc->dev, DMA_BIT_MASK(32));
- if (ret) {
- dev_err(nandc->dev, "failed to set DMA mask\n");
- return ret;
- }
-
- /*
- * we use the internal buffer for reading ONFI params, reading small
- * data like ID and status, and preforming read-copy-write operations
- * when writing to a codeword partially. 532 is the maximum possible
- * size of a codeword for our nand controller
- */
- nandc->buf_size = 532;
-
- nandc->data_buffer = devm_kzalloc(nandc->dev, nandc->buf_size,
- GFP_KERNEL);
- if (!nandc->data_buffer)
- return -ENOMEM;
-
- nandc->regs = devm_kzalloc(nandc->dev, sizeof(*nandc->regs),
- GFP_KERNEL);
- if (!nandc->regs)
- return -ENOMEM;
-
- nandc->reg_read_buf = devm_kzalloc(nandc->dev,
- MAX_REG_RD * sizeof(*nandc->reg_read_buf),
- GFP_KERNEL);
- if (!nandc->reg_read_buf)
- return -ENOMEM;
-
- if (nandc->props->is_bam) {
- nandc->reg_read_dma =
- dma_map_single(nandc->dev, nandc->reg_read_buf,
- MAX_REG_RD *
- sizeof(*nandc->reg_read_buf),
- DMA_FROM_DEVICE);
- if (dma_mapping_error(nandc->dev, nandc->reg_read_dma)) {
- dev_err(nandc->dev, "failed to DMA MAP reg buffer\n");
- return -EIO;
- }
-
- nandc->tx_chan = dma_request_slave_channel(nandc->dev, "tx");
- if (!nandc->tx_chan) {
- dev_err(nandc->dev, "failed to request tx channel\n");
- return -ENODEV;
- }
-
- nandc->rx_chan = dma_request_slave_channel(nandc->dev, "rx");
- if (!nandc->rx_chan) {
- dev_err(nandc->dev, "failed to request rx channel\n");
- return -ENODEV;
- }
-
- nandc->cmd_chan = dma_request_slave_channel(nandc->dev, "cmd");
- if (!nandc->cmd_chan) {
- dev_err(nandc->dev, "failed to request cmd channel\n");
- return -ENODEV;
- }
-
- /*
- * Initially allocate BAM transaction to read ONFI param page.
- * After detecting all the devices, this BAM transaction will
- * be freed and the next BAM tranasction will be allocated with
- * maximum codeword size
- */
- nandc->max_cwperpage = 1;
- nandc->bam_txn = alloc_bam_transaction(nandc);
- if (!nandc->bam_txn) {
- dev_err(nandc->dev,
- "failed to allocate bam transaction\n");
- return -ENOMEM;
- }
- } else {
- nandc->chan = dma_request_slave_channel(nandc->dev, "rxtx");
- if (!nandc->chan) {
- dev_err(nandc->dev,
- "failed to request slave channel\n");
- return -ENODEV;
- }
- }
-
- INIT_LIST_HEAD(&nandc->desc_list);
- INIT_LIST_HEAD(&nandc->host_list);
-
- nand_hw_control_init(&nandc->controller);
-
- return 0;
-}
-
-static void qcom_nandc_unalloc(struct qcom_nand_controller *nandc)
-{
- if (nandc->props->is_bam) {
- if (!dma_mapping_error(nandc->dev, nandc->reg_read_dma))
- dma_unmap_single(nandc->dev, nandc->reg_read_dma,
- MAX_REG_RD *
- sizeof(*nandc->reg_read_buf),
- DMA_FROM_DEVICE);
-
- if (nandc->tx_chan)
- dma_release_channel(nandc->tx_chan);
-
- if (nandc->rx_chan)
- dma_release_channel(nandc->rx_chan);
-
- if (nandc->cmd_chan)
- dma_release_channel(nandc->cmd_chan);
- } else {
- if (nandc->chan)
- dma_release_channel(nandc->chan);
- }
-}
-
-/* one time setup of a few nand controller registers */
-static int qcom_nandc_setup(struct qcom_nand_controller *nandc)
-{
- u32 nand_ctrl;
-
- /* kill onenand */
- nandc_write(nandc, SFLASHC_BURST_CFG, 0);
- nandc_write(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD),
- NAND_DEV_CMD_VLD_VAL);
-
- /* enable ADM or BAM DMA */
- if (nandc->props->is_bam) {
- nand_ctrl = nandc_read(nandc, NAND_CTRL);
- nandc_write(nandc, NAND_CTRL, nand_ctrl | BAM_MODE_EN);
- } else {
- nandc_write(nandc, NAND_FLASH_CHIP_SELECT, DM_EN);
- }
-
- /* save the original values of these registers */
- nandc->cmd1 = nandc_read(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD1));
- nandc->vld = NAND_DEV_CMD_VLD_VAL;
-
- return 0;
-}
-
-static int qcom_nand_host_init(struct qcom_nand_controller *nandc,
- struct qcom_nand_host *host,
- struct device_node *dn)
-{
- struct nand_chip *chip = &host->chip;
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct device *dev = nandc->dev;
- int ret;
-
- ret = of_property_read_u32(dn, "reg", &host->cs);
- if (ret) {
- dev_err(dev, "can't get chip-select\n");
- return -ENXIO;
- }
-
- nand_set_flash_node(chip, dn);
- mtd->name = devm_kasprintf(dev, GFP_KERNEL, "qcom_nand.%d", host->cs);
- if (!mtd->name)
- return -ENOMEM;
-
- mtd->owner = THIS_MODULE;
- mtd->dev.parent = dev;
-
- chip->cmdfunc = qcom_nandc_command;
- chip->select_chip = qcom_nandc_select_chip;
- chip->read_byte = qcom_nandc_read_byte;
- chip->read_buf = qcom_nandc_read_buf;
- chip->write_buf = qcom_nandc_write_buf;
- chip->onfi_set_features = nand_onfi_get_set_features_notsupp;
- chip->onfi_get_features = nand_onfi_get_set_features_notsupp;
-
- /*
- * the bad block marker is readable only when we read the last codeword
- * of a page with ECC disabled. currently, the nand_base and nand_bbt
- * helpers don't allow us to read BB from a nand chip with ECC
- * disabled (MTD_OPS_PLACE_OOB is set by default). use the block_bad
- * and block_markbad helpers until we permanently switch to using
- * MTD_OPS_RAW for all drivers (with the help of badblockbits)
- */
- chip->block_bad = qcom_nandc_block_bad;
- chip->block_markbad = qcom_nandc_block_markbad;
-
- chip->controller = &nandc->controller;
- chip->options |= NAND_NO_SUBPAGE_WRITE | NAND_USE_BOUNCE_BUFFER |
- NAND_SKIP_BBTSCAN;
-
- /* set up initial status value */
- host->status = NAND_STATUS_READY | NAND_STATUS_WP;
-
- ret = nand_scan_ident(mtd, 1, NULL);
- if (ret)
- return ret;
-
- ret = qcom_nand_host_setup(host);
-
- return ret;
-}
-
-static int qcom_nand_mtd_register(struct qcom_nand_controller *nandc,
- struct qcom_nand_host *host,
- struct device_node *dn)
-{
- struct nand_chip *chip = &host->chip;
- struct mtd_info *mtd = nand_to_mtd(chip);
- int ret;
-
- ret = nand_scan_tail(mtd);
- if (ret)
- return ret;
-
- ret = mtd_device_register(mtd, NULL, 0);
- if (ret)
- nand_cleanup(mtd_to_nand(mtd));
-
- return ret;
-}
-
-static int qcom_probe_nand_devices(struct qcom_nand_controller *nandc)
-{
- struct device *dev = nandc->dev;
- struct device_node *dn = dev->of_node, *child;
- struct qcom_nand_host *host, *tmp;
- int ret;
-
- for_each_available_child_of_node(dn, child) {
- host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
- if (!host) {
- of_node_put(child);
- return -ENOMEM;
- }
-
- ret = qcom_nand_host_init(nandc, host, child);
- if (ret) {
- devm_kfree(dev, host);
- continue;
- }
-
- list_add_tail(&host->node, &nandc->host_list);
- }
-
- if (list_empty(&nandc->host_list))
- return -ENODEV;
-
- if (nandc->props->is_bam) {
- free_bam_transaction(nandc);
- nandc->bam_txn = alloc_bam_transaction(nandc);
- if (!nandc->bam_txn) {
- dev_err(nandc->dev,
- "failed to allocate bam transaction\n");
- return -ENOMEM;
- }
- }
-
- list_for_each_entry_safe(host, tmp, &nandc->host_list, node) {
- ret = qcom_nand_mtd_register(nandc, host, child);
- if (ret) {
- list_del(&host->node);
- devm_kfree(dev, host);
- }
- }
-
- if (list_empty(&nandc->host_list))
- return -ENODEV;
-
- return 0;
-}
-
-/* parse custom DT properties here */
-static int qcom_nandc_parse_dt(struct platform_device *pdev)
-{
- struct qcom_nand_controller *nandc = platform_get_drvdata(pdev);
- struct device_node *np = nandc->dev->of_node;
- int ret;
-
- if (!nandc->props->is_bam) {
- ret = of_property_read_u32(np, "qcom,cmd-crci",
- &nandc->cmd_crci);
- if (ret) {
- dev_err(nandc->dev, "command CRCI unspecified\n");
- return ret;
- }
-
- ret = of_property_read_u32(np, "qcom,data-crci",
- &nandc->data_crci);
- if (ret) {
- dev_err(nandc->dev, "data CRCI unspecified\n");
- return ret;
- }
- }
-
- return 0;
-}
-
-static int qcom_nandc_probe(struct platform_device *pdev)
-{
- struct qcom_nand_controller *nandc;
- const void *dev_data;
- struct device *dev = &pdev->dev;
- struct resource *res;
- int ret;
-
- nandc = devm_kzalloc(&pdev->dev, sizeof(*nandc), GFP_KERNEL);
- if (!nandc)
- return -ENOMEM;
-
- platform_set_drvdata(pdev, nandc);
- nandc->dev = dev;
-
- dev_data = of_device_get_match_data(dev);
- if (!dev_data) {
- dev_err(&pdev->dev, "failed to get device data\n");
- return -ENODEV;
- }
-
- nandc->props = dev_data;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- nandc->base = devm_ioremap_resource(dev, res);
- if (IS_ERR(nandc->base))
- return PTR_ERR(nandc->base);
-
- nandc->base_phys = res->start;
- nandc->base_dma = phys_to_dma(dev, (phys_addr_t)res->start);
-
- nandc->core_clk = devm_clk_get(dev, "core");
- if (IS_ERR(nandc->core_clk))
- return PTR_ERR(nandc->core_clk);
-
- nandc->aon_clk = devm_clk_get(dev, "aon");
- if (IS_ERR(nandc->aon_clk))
- return PTR_ERR(nandc->aon_clk);
-
- ret = qcom_nandc_parse_dt(pdev);
- if (ret)
- return ret;
-
- ret = qcom_nandc_alloc(nandc);
- if (ret)
- goto err_core_clk;
-
- ret = clk_prepare_enable(nandc->core_clk);
- if (ret)
- goto err_core_clk;
-
- ret = clk_prepare_enable(nandc->aon_clk);
- if (ret)
- goto err_aon_clk;
-
- ret = qcom_nandc_setup(nandc);
- if (ret)
- goto err_setup;
-
- ret = qcom_probe_nand_devices(nandc);
- if (ret)
- goto err_setup;
-
- return 0;
-
-err_setup:
- clk_disable_unprepare(nandc->aon_clk);
-err_aon_clk:
- clk_disable_unprepare(nandc->core_clk);
-err_core_clk:
- qcom_nandc_unalloc(nandc);
-
- return ret;
-}
-
-static int qcom_nandc_remove(struct platform_device *pdev)
-{
- struct qcom_nand_controller *nandc = platform_get_drvdata(pdev);
- struct qcom_nand_host *host;
-
- list_for_each_entry(host, &nandc->host_list, node)
- nand_release(nand_to_mtd(&host->chip));
-
- qcom_nandc_unalloc(nandc);
-
- clk_disable_unprepare(nandc->aon_clk);
- clk_disable_unprepare(nandc->core_clk);
-
- return 0;
-}
-
-static const struct qcom_nandc_props ipq806x_nandc_props = {
- .ecc_modes = (ECC_RS_4BIT | ECC_BCH_8BIT),
- .is_bam = false,
- .dev_cmd_reg_start = 0x0,
-};
-
-static const struct qcom_nandc_props ipq4019_nandc_props = {
- .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
- .is_bam = true,
- .dev_cmd_reg_start = 0x0,
-};
-
-static const struct qcom_nandc_props ipq8074_nandc_props = {
- .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
- .is_bam = true,
- .dev_cmd_reg_start = 0x7000,
-};
-
-/*
- * data will hold a struct pointer containing more differences once we support
- * more controller variants
- */
-static const struct of_device_id qcom_nandc_of_match[] = {
- {
- .compatible = "qcom,ipq806x-nand",
- .data = &ipq806x_nandc_props,
- },
- {
- .compatible = "qcom,ipq4019-nand",
- .data = &ipq4019_nandc_props,
- },
- {
- .compatible = "qcom,ipq8074-nand",
- .data = &ipq8074_nandc_props,
- },
- {}
-};
-MODULE_DEVICE_TABLE(of, qcom_nandc_of_match);
-
-static struct platform_driver qcom_nandc_driver = {
- .driver = {
- .name = "qcom-nandc",
- .of_match_table = qcom_nandc_of_match,
- },
- .probe = qcom_nandc_probe,
- .remove = qcom_nandc_remove,
-};
-module_platform_driver(qcom_nandc_driver);
-
-MODULE_AUTHOR("Archit Taneja <architt@codeaurora.org>");
-MODULE_DESCRIPTION("Qualcomm NAND Controller driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c
deleted file mode 100644
index 595635b9e9de..000000000000
--- a/drivers/mtd/nand/r852.c
+++ /dev/null
@@ -1,1079 +0,0 @@
-/*
- * Copyright © 2009 - Maxim Levitsky
- * driver for Ricoh xD readers
- *
- * 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.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/jiffies.h>
-#include <linux/workqueue.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/pci_ids.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <asm/byteorder.h>
-#include <linux/sched.h>
-#include "sm_common.h"
-#include "r852.h"
-
-
-static bool r852_enable_dma = 1;
-module_param(r852_enable_dma, bool, S_IRUGO);
-MODULE_PARM_DESC(r852_enable_dma, "Enable usage of the DMA (default)");
-
-static int debug;
-module_param(debug, int, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug level (0-2)");
-
-/* read register */
-static inline uint8_t r852_read_reg(struct r852_device *dev, int address)
-{
- uint8_t reg = readb(dev->mmio + address);
- return reg;
-}
-
-/* write register */
-static inline void r852_write_reg(struct r852_device *dev,
- int address, uint8_t value)
-{
- writeb(value, dev->mmio + address);
- mmiowb();
-}
-
-
-/* read dword sized register */
-static inline uint32_t r852_read_reg_dword(struct r852_device *dev, int address)
-{
- uint32_t reg = le32_to_cpu(readl(dev->mmio + address));
- return reg;
-}
-
-/* write dword sized register */
-static inline void r852_write_reg_dword(struct r852_device *dev,
- int address, uint32_t value)
-{
- writel(cpu_to_le32(value), dev->mmio + address);
- mmiowb();
-}
-
-/* returns pointer to our private structure */
-static inline struct r852_device *r852_get_dev(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- return nand_get_controller_data(chip);
-}
-
-
-/* check if controller supports dma */
-static void r852_dma_test(struct r852_device *dev)
-{
- dev->dma_usable = (r852_read_reg(dev, R852_DMA_CAP) &
- (R852_DMA1 | R852_DMA2)) == (R852_DMA1 | R852_DMA2);
-
- if (!dev->dma_usable)
- message("Non dma capable device detected, dma disabled");
-
- if (!r852_enable_dma) {
- message("disabling dma on user request");
- dev->dma_usable = 0;
- }
-}
-
-/*
- * Enable dma. Enables ether first or second stage of the DMA,
- * Expects dev->dma_dir and dev->dma_state be set
- */
-static void r852_dma_enable(struct r852_device *dev)
-{
- uint8_t dma_reg, dma_irq_reg;
-
- /* Set up dma settings */
- dma_reg = r852_read_reg_dword(dev, R852_DMA_SETTINGS);
- dma_reg &= ~(R852_DMA_READ | R852_DMA_INTERNAL | R852_DMA_MEMORY);
-
- if (dev->dma_dir)
- dma_reg |= R852_DMA_READ;
-
- if (dev->dma_state == DMA_INTERNAL) {
- dma_reg |= R852_DMA_INTERNAL;
- /* Precaution to make sure HW doesn't write */
- /* to random kernel memory */
- r852_write_reg_dword(dev, R852_DMA_ADDR,
- cpu_to_le32(dev->phys_bounce_buffer));
- } else {
- dma_reg |= R852_DMA_MEMORY;
- r852_write_reg_dword(dev, R852_DMA_ADDR,
- cpu_to_le32(dev->phys_dma_addr));
- }
-
- /* Precaution: make sure write reached the device */
- r852_read_reg_dword(dev, R852_DMA_ADDR);
-
- r852_write_reg_dword(dev, R852_DMA_SETTINGS, dma_reg);
-
- /* Set dma irq */
- dma_irq_reg = r852_read_reg_dword(dev, R852_DMA_IRQ_ENABLE);
- r852_write_reg_dword(dev, R852_DMA_IRQ_ENABLE,
- dma_irq_reg |
- R852_DMA_IRQ_INTERNAL |
- R852_DMA_IRQ_ERROR |
- R852_DMA_IRQ_MEMORY);
-}
-
-/*
- * Disable dma, called from the interrupt handler, which specifies
- * success of the operation via 'error' argument
- */
-static void r852_dma_done(struct r852_device *dev, int error)
-{
- WARN_ON(dev->dma_stage == 0);
-
- r852_write_reg_dword(dev, R852_DMA_IRQ_STA,
- r852_read_reg_dword(dev, R852_DMA_IRQ_STA));
-
- r852_write_reg_dword(dev, R852_DMA_SETTINGS, 0);
- r852_write_reg_dword(dev, R852_DMA_IRQ_ENABLE, 0);
-
- /* Precaution to make sure HW doesn't write to random kernel memory */
- r852_write_reg_dword(dev, R852_DMA_ADDR,
- cpu_to_le32(dev->phys_bounce_buffer));
- r852_read_reg_dword(dev, R852_DMA_ADDR);
-
- dev->dma_error = error;
- dev->dma_stage = 0;
-
- if (dev->phys_dma_addr && dev->phys_dma_addr != dev->phys_bounce_buffer)
- pci_unmap_single(dev->pci_dev, dev->phys_dma_addr, R852_DMA_LEN,
- dev->dma_dir ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE);
-}
-
-/*
- * Wait, till dma is done, which includes both phases of it
- */
-static int r852_dma_wait(struct r852_device *dev)
-{
- long timeout = wait_for_completion_timeout(&dev->dma_done,
- msecs_to_jiffies(1000));
- if (!timeout) {
- dbg("timeout waiting for DMA interrupt");
- return -ETIMEDOUT;
- }
-
- return 0;
-}
-
-/*
- * Read/Write one page using dma. Only pages can be read (512 bytes)
-*/
-static void r852_do_dma(struct r852_device *dev, uint8_t *buf, int do_read)
-{
- int bounce = 0;
- unsigned long flags;
- int error;
-
- dev->dma_error = 0;
-
- /* Set dma direction */
- dev->dma_dir = do_read;
- dev->dma_stage = 1;
- reinit_completion(&dev->dma_done);
-
- dbg_verbose("doing dma %s ", do_read ? "read" : "write");
-
- /* Set initial dma state: for reading first fill on board buffer,
- from device, for writes first fill the buffer from memory*/
- dev->dma_state = do_read ? DMA_INTERNAL : DMA_MEMORY;
-
- /* if incoming buffer is not page aligned, we should do bounce */
- if ((unsigned long)buf & (R852_DMA_LEN-1))
- bounce = 1;
-
- if (!bounce) {
- dev->phys_dma_addr = pci_map_single(dev->pci_dev, (void *)buf,
- R852_DMA_LEN,
- (do_read ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE));
-
- if (pci_dma_mapping_error(dev->pci_dev, dev->phys_dma_addr))
- bounce = 1;
- }
-
- if (bounce) {
- dbg_verbose("dma: using bounce buffer");
- dev->phys_dma_addr = dev->phys_bounce_buffer;
- if (!do_read)
- memcpy(dev->bounce_buffer, buf, R852_DMA_LEN);
- }
-
- /* Enable DMA */
- spin_lock_irqsave(&dev->irqlock, flags);
- r852_dma_enable(dev);
- spin_unlock_irqrestore(&dev->irqlock, flags);
-
- /* Wait till complete */
- error = r852_dma_wait(dev);
-
- if (error) {
- r852_dma_done(dev, error);
- return;
- }
-
- if (do_read && bounce)
- memcpy((void *)buf, dev->bounce_buffer, R852_DMA_LEN);
-}
-
-/*
- * Program data lines of the nand chip to send data to it
- */
-static void r852_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
-{
- struct r852_device *dev = r852_get_dev(mtd);
- uint32_t reg;
-
- /* Don't allow any access to hardware if we suspect card removal */
- if (dev->card_unstable)
- return;
-
- /* Special case for whole sector read */
- if (len == R852_DMA_LEN && dev->dma_usable) {
- r852_do_dma(dev, (uint8_t *)buf, 0);
- return;
- }
-
- /* write DWORD chinks - faster */
- while (len >= 4) {
- reg = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24;
- r852_write_reg_dword(dev, R852_DATALINE, reg);
- buf += 4;
- len -= 4;
-
- }
-
- /* write rest */
- while (len > 0) {
- r852_write_reg(dev, R852_DATALINE, *buf++);
- len--;
- }
-}
-
-/*
- * Read data lines of the nand chip to retrieve data
- */
-static void r852_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
-{
- struct r852_device *dev = r852_get_dev(mtd);
- uint32_t reg;
-
- if (dev->card_unstable) {
- /* since we can't signal error here, at least, return
- predictable buffer */
- memset(buf, 0, len);
- return;
- }
-
- /* special case for whole sector read */
- if (len == R852_DMA_LEN && dev->dma_usable) {
- r852_do_dma(dev, buf, 1);
- return;
- }
-
- /* read in dword sized chunks */
- while (len >= 4) {
-
- reg = r852_read_reg_dword(dev, R852_DATALINE);
- *buf++ = reg & 0xFF;
- *buf++ = (reg >> 8) & 0xFF;
- *buf++ = (reg >> 16) & 0xFF;
- *buf++ = (reg >> 24) & 0xFF;
- len -= 4;
- }
-
- /* read the reset by bytes */
- while (len--)
- *buf++ = r852_read_reg(dev, R852_DATALINE);
-}
-
-/*
- * Read one byte from nand chip
- */
-static uint8_t r852_read_byte(struct mtd_info *mtd)
-{
- struct r852_device *dev = r852_get_dev(mtd);
-
- /* Same problem as in r852_read_buf.... */
- if (dev->card_unstable)
- return 0;
-
- return r852_read_reg(dev, R852_DATALINE);
-}
-
-/*
- * Control several chip lines & send commands
- */
-static void r852_cmdctl(struct mtd_info *mtd, int dat, unsigned int ctrl)
-{
- struct r852_device *dev = r852_get_dev(mtd);
-
- if (dev->card_unstable)
- return;
-
- if (ctrl & NAND_CTRL_CHANGE) {
-
- dev->ctlreg &= ~(R852_CTL_DATA | R852_CTL_COMMAND |
- R852_CTL_ON | R852_CTL_CARDENABLE);
-
- if (ctrl & NAND_ALE)
- dev->ctlreg |= R852_CTL_DATA;
-
- if (ctrl & NAND_CLE)
- dev->ctlreg |= R852_CTL_COMMAND;
-
- if (ctrl & NAND_NCE)
- dev->ctlreg |= (R852_CTL_CARDENABLE | R852_CTL_ON);
- else
- dev->ctlreg &= ~R852_CTL_WRITE;
-
- /* when write is stareted, enable write access */
- if (dat == NAND_CMD_ERASE1)
- dev->ctlreg |= R852_CTL_WRITE;
-
- r852_write_reg(dev, R852_CTL, dev->ctlreg);
- }
-
- /* HACK: NAND_CMD_SEQIN is called without NAND_CTRL_CHANGE, but we need
- to set write mode */
- if (dat == NAND_CMD_SEQIN && (dev->ctlreg & R852_CTL_COMMAND)) {
- dev->ctlreg |= R852_CTL_WRITE;
- r852_write_reg(dev, R852_CTL, dev->ctlreg);
- }
-
- if (dat != NAND_CMD_NONE)
- r852_write_reg(dev, R852_DATALINE, dat);
-}
-
-/*
- * Wait till card is ready.
- * based on nand_wait, but returns errors on DMA error
- */
-static int r852_wait(struct mtd_info *mtd, struct nand_chip *chip)
-{
- struct r852_device *dev = nand_get_controller_data(chip);
-
- unsigned long timeout;
- u8 status;
-
- timeout = jiffies + (chip->state == FL_ERASING ?
- msecs_to_jiffies(400) : msecs_to_jiffies(20));
-
- while (time_before(jiffies, timeout))
- if (chip->dev_ready(mtd))
- break;
-
- nand_status_op(chip, &status);
-
- /* Unfortunelly, no way to send detailed error status... */
- if (dev->dma_error) {
- status |= NAND_STATUS_FAIL;
- dev->dma_error = 0;
- }
- return status;
-}
-
-/*
- * Check if card is ready
- */
-
-static int r852_ready(struct mtd_info *mtd)
-{
- struct r852_device *dev = r852_get_dev(mtd);
- return !(r852_read_reg(dev, R852_CARD_STA) & R852_CARD_STA_BUSY);
-}
-
-
-/*
- * Set ECC engine mode
-*/
-
-static void r852_ecc_hwctl(struct mtd_info *mtd, int mode)
-{
- struct r852_device *dev = r852_get_dev(mtd);
-
- if (dev->card_unstable)
- return;
-
- switch (mode) {
- case NAND_ECC_READ:
- case NAND_ECC_WRITE:
- /* enable ecc generation/check*/
- dev->ctlreg |= R852_CTL_ECC_ENABLE;
-
- /* flush ecc buffer */
- r852_write_reg(dev, R852_CTL,
- dev->ctlreg | R852_CTL_ECC_ACCESS);
-
- r852_read_reg_dword(dev, R852_DATALINE);
- r852_write_reg(dev, R852_CTL, dev->ctlreg);
- return;
-
- case NAND_ECC_READSYN:
- /* disable ecc generation */
- dev->ctlreg &= ~R852_CTL_ECC_ENABLE;
- r852_write_reg(dev, R852_CTL, dev->ctlreg);
- }
-}
-
-/*
- * Calculate ECC, only used for writes
- */
-
-static int r852_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat,
- uint8_t *ecc_code)
-{
- struct r852_device *dev = r852_get_dev(mtd);
- struct sm_oob *oob = (struct sm_oob *)ecc_code;
- uint32_t ecc1, ecc2;
-
- if (dev->card_unstable)
- return 0;
-
- dev->ctlreg &= ~R852_CTL_ECC_ENABLE;
- r852_write_reg(dev, R852_CTL, dev->ctlreg | R852_CTL_ECC_ACCESS);
-
- ecc1 = r852_read_reg_dword(dev, R852_DATALINE);
- ecc2 = r852_read_reg_dword(dev, R852_DATALINE);
-
- oob->ecc1[0] = (ecc1) & 0xFF;
- oob->ecc1[1] = (ecc1 >> 8) & 0xFF;
- oob->ecc1[2] = (ecc1 >> 16) & 0xFF;
-
- oob->ecc2[0] = (ecc2) & 0xFF;
- oob->ecc2[1] = (ecc2 >> 8) & 0xFF;
- oob->ecc2[2] = (ecc2 >> 16) & 0xFF;
-
- r852_write_reg(dev, R852_CTL, dev->ctlreg);
- return 0;
-}
-
-/*
- * Correct the data using ECC, hw did almost everything for us
- */
-
-static int r852_ecc_correct(struct mtd_info *mtd, uint8_t *dat,
- uint8_t *read_ecc, uint8_t *calc_ecc)
-{
- uint32_t ecc_reg;
- uint8_t ecc_status, err_byte;
- int i, error = 0;
-
- struct r852_device *dev = r852_get_dev(mtd);
-
- if (dev->card_unstable)
- return 0;
-
- if (dev->dma_error) {
- dev->dma_error = 0;
- return -EIO;
- }
-
- r852_write_reg(dev, R852_CTL, dev->ctlreg | R852_CTL_ECC_ACCESS);
- ecc_reg = r852_read_reg_dword(dev, R852_DATALINE);
- r852_write_reg(dev, R852_CTL, dev->ctlreg);
-
- for (i = 0 ; i <= 1 ; i++) {
-
- ecc_status = (ecc_reg >> 8) & 0xFF;
-
- /* ecc uncorrectable error */
- if (ecc_status & R852_ECC_FAIL) {
- dbg("ecc: unrecoverable error, in half %d", i);
- error = -EBADMSG;
- goto exit;
- }
-
- /* correctable error */
- if (ecc_status & R852_ECC_CORRECTABLE) {
-
- err_byte = ecc_reg & 0xFF;
- dbg("ecc: recoverable error, "
- "in half %d, byte %d, bit %d", i,
- err_byte, ecc_status & R852_ECC_ERR_BIT_MSK);
-
- dat[err_byte] ^=
- 1 << (ecc_status & R852_ECC_ERR_BIT_MSK);
- error++;
- }
-
- dat += 256;
- ecc_reg >>= 16;
- }
-exit:
- return error;
-}
-
-/*
- * This is copy of nand_read_oob_std
- * nand_read_oob_syndrome assumes we can send column address - we can't
- */
-static int r852_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
- int page)
-{
- return nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize);
-}
-
-/*
- * Start the nand engine
- */
-
-static void r852_engine_enable(struct r852_device *dev)
-{
- if (r852_read_reg_dword(dev, R852_HW) & R852_HW_UNKNOWN) {
- r852_write_reg(dev, R852_CTL, R852_CTL_RESET | R852_CTL_ON);
- r852_write_reg_dword(dev, R852_HW, R852_HW_ENABLED);
- } else {
- r852_write_reg_dword(dev, R852_HW, R852_HW_ENABLED);
- r852_write_reg(dev, R852_CTL, R852_CTL_RESET | R852_CTL_ON);
- }
- msleep(300);
- r852_write_reg(dev, R852_CTL, 0);
-}
-
-
-/*
- * Stop the nand engine
- */
-
-static void r852_engine_disable(struct r852_device *dev)
-{
- r852_write_reg_dword(dev, R852_HW, 0);
- r852_write_reg(dev, R852_CTL, R852_CTL_RESET);
-}
-
-/*
- * Test if card is present
- */
-
-static void r852_card_update_present(struct r852_device *dev)
-{
- unsigned long flags;
- uint8_t reg;
-
- spin_lock_irqsave(&dev->irqlock, flags);
- reg = r852_read_reg(dev, R852_CARD_STA);
- dev->card_detected = !!(reg & R852_CARD_STA_PRESENT);
- spin_unlock_irqrestore(&dev->irqlock, flags);
-}
-
-/*
- * Update card detection IRQ state according to current card state
- * which is read in r852_card_update_present
- */
-static void r852_update_card_detect(struct r852_device *dev)
-{
- int card_detect_reg = r852_read_reg(dev, R852_CARD_IRQ_ENABLE);
- dev->card_unstable = 0;
-
- card_detect_reg &= ~(R852_CARD_IRQ_REMOVE | R852_CARD_IRQ_INSERT);
- card_detect_reg |= R852_CARD_IRQ_GENABLE;
-
- card_detect_reg |= dev->card_detected ?
- R852_CARD_IRQ_REMOVE : R852_CARD_IRQ_INSERT;
-
- r852_write_reg(dev, R852_CARD_IRQ_ENABLE, card_detect_reg);
-}
-
-static ssize_t r852_media_type_show(struct device *sys_dev,
- struct device_attribute *attr, char *buf)
-{
- struct mtd_info *mtd = container_of(sys_dev, struct mtd_info, dev);
- struct r852_device *dev = r852_get_dev(mtd);
- char *data = dev->sm ? "smartmedia" : "xd";
-
- strcpy(buf, data);
- return strlen(data);
-}
-
-static DEVICE_ATTR(media_type, S_IRUGO, r852_media_type_show, NULL);
-
-
-/* Detect properties of card in slot */
-static void r852_update_media_status(struct r852_device *dev)
-{
- uint8_t reg;
- unsigned long flags;
- int readonly;
-
- spin_lock_irqsave(&dev->irqlock, flags);
- if (!dev->card_detected) {
- message("card removed");
- spin_unlock_irqrestore(&dev->irqlock, flags);
- return ;
- }
-
- readonly = r852_read_reg(dev, R852_CARD_STA) & R852_CARD_STA_RO;
- reg = r852_read_reg(dev, R852_DMA_CAP);
- dev->sm = (reg & (R852_DMA1 | R852_DMA2)) && (reg & R852_SMBIT);
-
- message("detected %s %s card in slot",
- dev->sm ? "SmartMedia" : "xD",
- readonly ? "readonly" : "writeable");
-
- dev->readonly = readonly;
- spin_unlock_irqrestore(&dev->irqlock, flags);
-}
-
-/*
- * Register the nand device
- * Called when the card is detected
- */
-static int r852_register_nand_device(struct r852_device *dev)
-{
- struct mtd_info *mtd = nand_to_mtd(dev->chip);
-
- WARN_ON(dev->card_registred);
-
- mtd->dev.parent = &dev->pci_dev->dev;
-
- if (dev->readonly)
- dev->chip->options |= NAND_ROM;
-
- r852_engine_enable(dev);
-
- if (sm_register_device(mtd, dev->sm))
- goto error1;
-
- if (device_create_file(&mtd->dev, &dev_attr_media_type)) {
- message("can't create media type sysfs attribute");
- goto error3;
- }
-
- dev->card_registred = 1;
- return 0;
-error3:
- nand_release(mtd);
-error1:
- /* Force card redetect */
- dev->card_detected = 0;
- return -1;
-}
-
-/*
- * Unregister the card
- */
-
-static void r852_unregister_nand_device(struct r852_device *dev)
-{
- struct mtd_info *mtd = nand_to_mtd(dev->chip);
-
- if (!dev->card_registred)
- return;
-
- device_remove_file(&mtd->dev, &dev_attr_media_type);
- nand_release(mtd);
- r852_engine_disable(dev);
- dev->card_registred = 0;
-}
-
-/* Card state updater */
-static void r852_card_detect_work(struct work_struct *work)
-{
- struct r852_device *dev =
- container_of(work, struct r852_device, card_detect_work.work);
-
- r852_card_update_present(dev);
- r852_update_card_detect(dev);
- dev->card_unstable = 0;
-
- /* False alarm */
- if (dev->card_detected == dev->card_registred)
- goto exit;
-
- /* Read media properties */
- r852_update_media_status(dev);
-
- /* Register the card */
- if (dev->card_detected)
- r852_register_nand_device(dev);
- else
- r852_unregister_nand_device(dev);
-exit:
- r852_update_card_detect(dev);
-}
-
-/* Ack + disable IRQ generation */
-static void r852_disable_irqs(struct r852_device *dev)
-{
- uint8_t reg;
- reg = r852_read_reg(dev, R852_CARD_IRQ_ENABLE);
- r852_write_reg(dev, R852_CARD_IRQ_ENABLE, reg & ~R852_CARD_IRQ_MASK);
-
- reg = r852_read_reg_dword(dev, R852_DMA_IRQ_ENABLE);
- r852_write_reg_dword(dev, R852_DMA_IRQ_ENABLE,
- reg & ~R852_DMA_IRQ_MASK);
-
- r852_write_reg(dev, R852_CARD_IRQ_STA, R852_CARD_IRQ_MASK);
- r852_write_reg_dword(dev, R852_DMA_IRQ_STA, R852_DMA_IRQ_MASK);
-}
-
-/* Interrupt handler */
-static irqreturn_t r852_irq(int irq, void *data)
-{
- struct r852_device *dev = (struct r852_device *)data;
-
- uint8_t card_status, dma_status;
- unsigned long flags;
- irqreturn_t ret = IRQ_NONE;
-
- spin_lock_irqsave(&dev->irqlock, flags);
-
- /* handle card detection interrupts first */
- card_status = r852_read_reg(dev, R852_CARD_IRQ_STA);
- r852_write_reg(dev, R852_CARD_IRQ_STA, card_status);
-
- if (card_status & (R852_CARD_IRQ_INSERT|R852_CARD_IRQ_REMOVE)) {
-
- ret = IRQ_HANDLED;
- dev->card_detected = !!(card_status & R852_CARD_IRQ_INSERT);
-
- /* we shouldn't receive any interrupts if we wait for card
- to settle */
- WARN_ON(dev->card_unstable);
-
- /* disable irqs while card is unstable */
- /* this will timeout DMA if active, but better that garbage */
- r852_disable_irqs(dev);
-
- if (dev->card_unstable)
- goto out;
-
- /* let, card state to settle a bit, and then do the work */
- dev->card_unstable = 1;
- queue_delayed_work(dev->card_workqueue,
- &dev->card_detect_work, msecs_to_jiffies(100));
- goto out;
- }
-
-
- /* Handle dma interrupts */
- dma_status = r852_read_reg_dword(dev, R852_DMA_IRQ_STA);
- r852_write_reg_dword(dev, R852_DMA_IRQ_STA, dma_status);
-
- if (dma_status & R852_DMA_IRQ_MASK) {
-
- ret = IRQ_HANDLED;
-
- if (dma_status & R852_DMA_IRQ_ERROR) {
- dbg("received dma error IRQ");
- r852_dma_done(dev, -EIO);
- complete(&dev->dma_done);
- goto out;
- }
-
- /* received DMA interrupt out of nowhere? */
- WARN_ON_ONCE(dev->dma_stage == 0);
-
- if (dev->dma_stage == 0)
- goto out;
-
- /* done device access */
- if (dev->dma_state == DMA_INTERNAL &&
- (dma_status & R852_DMA_IRQ_INTERNAL)) {
-
- dev->dma_state = DMA_MEMORY;
- dev->dma_stage++;
- }
-
- /* done memory DMA */
- if (dev->dma_state == DMA_MEMORY &&
- (dma_status & R852_DMA_IRQ_MEMORY)) {
- dev->dma_state = DMA_INTERNAL;
- dev->dma_stage++;
- }
-
- /* Enable 2nd half of dma dance */
- if (dev->dma_stage == 2)
- r852_dma_enable(dev);
-
- /* Operation done */
- if (dev->dma_stage == 3) {
- r852_dma_done(dev, 0);
- complete(&dev->dma_done);
- }
- goto out;
- }
-
- /* Handle unknown interrupts */
- if (dma_status)
- dbg("bad dma IRQ status = %x", dma_status);
-
- if (card_status & ~R852_CARD_STA_CD)
- dbg("strange card status = %x", card_status);
-
-out:
- spin_unlock_irqrestore(&dev->irqlock, flags);
- return ret;
-}
-
-static int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
-{
- int error;
- struct nand_chip *chip;
- struct r852_device *dev;
-
- /* pci initialization */
- error = pci_enable_device(pci_dev);
-
- if (error)
- goto error1;
-
- pci_set_master(pci_dev);
-
- error = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32));
- if (error)
- goto error2;
-
- error = pci_request_regions(pci_dev, DRV_NAME);
-
- if (error)
- goto error3;
-
- error = -ENOMEM;
-
- /* init nand chip, but register it only on card insert */
- chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
-
- if (!chip)
- goto error4;
-
- /* commands */
- chip->cmd_ctrl = r852_cmdctl;
- chip->waitfunc = r852_wait;
- chip->dev_ready = r852_ready;
-
- /* I/O */
- chip->read_byte = r852_read_byte;
- chip->read_buf = r852_read_buf;
- chip->write_buf = r852_write_buf;
-
- /* ecc */
- chip->ecc.mode = NAND_ECC_HW_SYNDROME;
- chip->ecc.size = R852_DMA_LEN;
- chip->ecc.bytes = SM_OOB_SIZE;
- chip->ecc.strength = 2;
- chip->ecc.hwctl = r852_ecc_hwctl;
- chip->ecc.calculate = r852_ecc_calculate;
- chip->ecc.correct = r852_ecc_correct;
-
- /* TODO: hack */
- chip->ecc.read_oob = r852_read_oob;
-
- /* init our device structure */
- dev = kzalloc(sizeof(struct r852_device), GFP_KERNEL);
-
- if (!dev)
- goto error5;
-
- nand_set_controller_data(chip, dev);
- dev->chip = chip;
- dev->pci_dev = pci_dev;
- pci_set_drvdata(pci_dev, dev);
-
- dev->bounce_buffer = pci_alloc_consistent(pci_dev, R852_DMA_LEN,
- &dev->phys_bounce_buffer);
-
- if (!dev->bounce_buffer)
- goto error6;
-
-
- error = -ENODEV;
- dev->mmio = pci_ioremap_bar(pci_dev, 0);
-
- if (!dev->mmio)
- goto error7;
-
- error = -ENOMEM;
- dev->tmp_buffer = kzalloc(SM_SECTOR_SIZE, GFP_KERNEL);
-
- if (!dev->tmp_buffer)
- goto error8;
-
- init_completion(&dev->dma_done);
-
- dev->card_workqueue = create_freezable_workqueue(DRV_NAME);
-
- if (!dev->card_workqueue)
- goto error9;
-
- INIT_DELAYED_WORK(&dev->card_detect_work, r852_card_detect_work);
-
- /* shutdown everything - precation */
- r852_engine_disable(dev);
- r852_disable_irqs(dev);
-
- r852_dma_test(dev);
-
- dev->irq = pci_dev->irq;
- spin_lock_init(&dev->irqlock);
-
- dev->card_detected = 0;
- r852_card_update_present(dev);
-
- /*register irq handler*/
- error = -ENODEV;
- if (request_irq(pci_dev->irq, &r852_irq, IRQF_SHARED,
- DRV_NAME, dev))
- goto error10;
-
- /* kick initial present test */
- queue_delayed_work(dev->card_workqueue,
- &dev->card_detect_work, 0);
-
-
- printk(KERN_NOTICE DRV_NAME ": driver loaded successfully\n");
- return 0;
-
-error10:
- destroy_workqueue(dev->card_workqueue);
-error9:
- kfree(dev->tmp_buffer);
-error8:
- pci_iounmap(pci_dev, dev->mmio);
-error7:
- pci_free_consistent(pci_dev, R852_DMA_LEN,
- dev->bounce_buffer, dev->phys_bounce_buffer);
-error6:
- kfree(dev);
-error5:
- kfree(chip);
-error4:
- pci_release_regions(pci_dev);
-error3:
-error2:
- pci_disable_device(pci_dev);
-error1:
- return error;
-}
-
-static void r852_remove(struct pci_dev *pci_dev)
-{
- struct r852_device *dev = pci_get_drvdata(pci_dev);
-
- /* Stop detect workqueue -
- we are going to unregister the device anyway*/
- cancel_delayed_work_sync(&dev->card_detect_work);
- destroy_workqueue(dev->card_workqueue);
-
- /* Unregister the device, this might make more IO */
- r852_unregister_nand_device(dev);
-
- /* Stop interrupts */
- r852_disable_irqs(dev);
- free_irq(dev->irq, dev);
-
- /* Cleanup */
- kfree(dev->tmp_buffer);
- pci_iounmap(pci_dev, dev->mmio);
- pci_free_consistent(pci_dev, R852_DMA_LEN,
- dev->bounce_buffer, dev->phys_bounce_buffer);
-
- kfree(dev->chip);
- kfree(dev);
-
- /* Shutdown the PCI device */
- pci_release_regions(pci_dev);
- pci_disable_device(pci_dev);
-}
-
-static void r852_shutdown(struct pci_dev *pci_dev)
-{
- struct r852_device *dev = pci_get_drvdata(pci_dev);
-
- cancel_delayed_work_sync(&dev->card_detect_work);
- r852_disable_irqs(dev);
- synchronize_irq(dev->irq);
- pci_disable_device(pci_dev);
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int r852_suspend(struct device *device)
-{
- struct r852_device *dev = pci_get_drvdata(to_pci_dev(device));
-
- if (dev->ctlreg & R852_CTL_CARDENABLE)
- return -EBUSY;
-
- /* First make sure the detect work is gone */
- cancel_delayed_work_sync(&dev->card_detect_work);
-
- /* Turn off the interrupts and stop the device */
- r852_disable_irqs(dev);
- r852_engine_disable(dev);
-
- /* If card was pulled off just during the suspend, which is very
- unlikely, we will remove it on resume, it too late now
- anyway... */
- dev->card_unstable = 0;
- return 0;
-}
-
-static int r852_resume(struct device *device)
-{
- struct r852_device *dev = pci_get_drvdata(to_pci_dev(device));
- struct mtd_info *mtd = nand_to_mtd(dev->chip);
-
- r852_disable_irqs(dev);
- r852_card_update_present(dev);
- r852_engine_disable(dev);
-
-
- /* If card status changed, just do the work */
- if (dev->card_detected != dev->card_registred) {
- dbg("card was %s during low power state",
- dev->card_detected ? "added" : "removed");
-
- queue_delayed_work(dev->card_workqueue,
- &dev->card_detect_work, msecs_to_jiffies(1000));
- return 0;
- }
-
- /* Otherwise, initialize the card */
- if (dev->card_registred) {
- r852_engine_enable(dev);
- dev->chip->select_chip(mtd, 0);
- nand_reset_op(dev->chip);
- dev->chip->select_chip(mtd, -1);
- }
-
- /* Program card detection IRQ */
- r852_update_card_detect(dev);
- return 0;
-}
-#endif
-
-static const struct pci_device_id r852_pci_id_tbl[] = {
-
- { PCI_VDEVICE(RICOH, 0x0852), },
- { },
-};
-
-MODULE_DEVICE_TABLE(pci, r852_pci_id_tbl);
-
-static SIMPLE_DEV_PM_OPS(r852_pm_ops, r852_suspend, r852_resume);
-
-static struct pci_driver r852_pci_driver = {
- .name = DRV_NAME,
- .id_table = r852_pci_id_tbl,
- .probe = r852_probe,
- .remove = r852_remove,
- .shutdown = r852_shutdown,
- .driver.pm = &r852_pm_ops,
-};
-
-module_pci_driver(r852_pci_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>");
-MODULE_DESCRIPTION("Ricoh 85xx xD/smartmedia card reader driver");
diff --git a/drivers/mtd/nand/r852.h b/drivers/mtd/nand/r852.h
deleted file mode 100644
index 8713c57f6207..000000000000
--- a/drivers/mtd/nand/r852.h
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Copyright © 2009 - Maxim Levitsky
- * driver for Ricoh xD readers
- *
- * 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.
- */
-
-#include <linux/pci.h>
-#include <linux/completion.h>
-#include <linux/workqueue.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/spinlock.h>
-
-
-/* nand interface + ecc
- byte write/read does one cycle on nand data lines.
- dword write/read does 4 cycles
- if R852_CTL_ECC_ACCESS is set in R852_CTL, then dword read reads
- results of ecc correction, if DMA read was done before.
- If write was done two dword reads read generated ecc checksums
-*/
-#define R852_DATALINE 0x00
-
-/* control register */
-#define R852_CTL 0x04
-#define R852_CTL_COMMAND 0x01 /* send command (#CLE)*/
-#define R852_CTL_DATA 0x02 /* read/write data (#ALE)*/
-#define R852_CTL_ON 0x04 /* only seem to controls the hd led, */
- /* but has to be set on start...*/
-#define R852_CTL_RESET 0x08 /* unknown, set only on start once*/
-#define R852_CTL_CARDENABLE 0x10 /* probably (#CE) - always set*/
-#define R852_CTL_ECC_ENABLE 0x20 /* enable ecc engine */
-#define R852_CTL_ECC_ACCESS 0x40 /* read/write ecc via reg #0*/
-#define R852_CTL_WRITE 0x80 /* set when performing writes (#WP) */
-
-/* card detection status */
-#define R852_CARD_STA 0x05
-
-#define R852_CARD_STA_CD 0x01 /* state of #CD line, same as 0x04 */
-#define R852_CARD_STA_RO 0x02 /* card is readonly */
-#define R852_CARD_STA_PRESENT 0x04 /* card is present (#CD) */
-#define R852_CARD_STA_ABSENT 0x08 /* card is absent */
-#define R852_CARD_STA_BUSY 0x80 /* card is busy - (#R/B) */
-
-/* card detection irq status & enable*/
-#define R852_CARD_IRQ_STA 0x06 /* IRQ status */
-#define R852_CARD_IRQ_ENABLE 0x07 /* IRQ enable */
-
-#define R852_CARD_IRQ_CD 0x01 /* fire when #CD lights, same as 0x04*/
-#define R852_CARD_IRQ_REMOVE 0x04 /* detect card removal */
-#define R852_CARD_IRQ_INSERT 0x08 /* detect card insert */
-#define R852_CARD_IRQ_UNK1 0x10 /* unknown */
-#define R852_CARD_IRQ_GENABLE 0x80 /* general enable */
-#define R852_CARD_IRQ_MASK 0x1D
-
-
-
-/* hardware enable */
-#define R852_HW 0x08
-#define R852_HW_ENABLED 0x01 /* hw enabled */
-#define R852_HW_UNKNOWN 0x80
-
-
-/* dma capabilities */
-#define R852_DMA_CAP 0x09
-#define R852_SMBIT 0x20 /* if set with bit #6 or bit #7, then */
- /* hw is smartmedia */
-#define R852_DMA1 0x40 /* if set w/bit #7, dma is supported */
-#define R852_DMA2 0x80 /* if set w/bit #6, dma is supported */
-
-
-/* physical DMA address - 32 bit value*/
-#define R852_DMA_ADDR 0x0C
-
-
-/* dma settings */
-#define R852_DMA_SETTINGS 0x10
-#define R852_DMA_MEMORY 0x01 /* (memory <-> internal hw buffer) */
-#define R852_DMA_READ 0x02 /* 0 = write, 1 = read */
-#define R852_DMA_INTERNAL 0x04 /* (internal hw buffer <-> card) */
-
-/* dma IRQ status */
-#define R852_DMA_IRQ_STA 0x14
-
-/* dma IRQ enable */
-#define R852_DMA_IRQ_ENABLE 0x18
-
-#define R852_DMA_IRQ_MEMORY 0x01 /* (memory <-> internal hw buffer) */
-#define R852_DMA_IRQ_ERROR 0x02 /* error did happen */
-#define R852_DMA_IRQ_INTERNAL 0x04 /* (internal hw buffer <-> card) */
-#define R852_DMA_IRQ_MASK 0x07 /* mask of all IRQ bits */
-
-
-/* ECC syndrome format - read from reg #0 will return two copies of these for
- each half of the page.
- first byte is error byte location, and second, bit location + flags */
-#define R852_ECC_ERR_BIT_MSK 0x07 /* error bit location */
-#define R852_ECC_CORRECT 0x10 /* no errors - (guessed) */
-#define R852_ECC_CORRECTABLE 0x20 /* correctable error exist */
-#define R852_ECC_FAIL 0x40 /* non correctable error detected */
-
-#define R852_DMA_LEN 512
-
-#define DMA_INTERNAL 0
-#define DMA_MEMORY 1
-
-struct r852_device {
- void __iomem *mmio; /* mmio */
- struct nand_chip *chip; /* nand chip backpointer */
- struct pci_dev *pci_dev; /* pci backpointer */
-
- /* dma area */
- dma_addr_t phys_dma_addr; /* bus address of buffer*/
- struct completion dma_done; /* data transfer done */
-
- dma_addr_t phys_bounce_buffer; /* bus address of bounce buffer */
- uint8_t *bounce_buffer; /* virtual address of bounce buffer */
-
- int dma_dir; /* 1 = read, 0 = write */
- int dma_stage; /* 0 - idle, 1 - first step,
- 2 - second step */
-
- int dma_state; /* 0 = internal, 1 = memory */
- int dma_error; /* dma errors */
- int dma_usable; /* is it possible to use dma */
-
- /* card status area */
- struct delayed_work card_detect_work;
- struct workqueue_struct *card_workqueue;
- int card_registred; /* card registered with mtd */
- int card_detected; /* card detected in slot */
- int card_unstable; /* whenever the card is inserted,
- is not known yet */
- int readonly; /* card is readonly */
- int sm; /* Is card smartmedia */
-
- /* interrupt handling */
- spinlock_t irqlock; /* IRQ protecting lock */
- int irq; /* irq num */
- /* misc */
- void *tmp_buffer; /* temporary buffer */
- uint8_t ctlreg; /* cached contents of control reg */
-};
-
-#define DRV_NAME "r852"
-
-
-#define dbg(format, ...) \
- if (debug) \
- printk(KERN_DEBUG DRV_NAME ": " format "\n", ## __VA_ARGS__)
-
-#define dbg_verbose(format, ...) \
- if (debug > 1) \
- printk(KERN_DEBUG DRV_NAME ": " format "\n", ## __VA_ARGS__)
-
-
-#define message(format, ...) \
- printk(KERN_INFO DRV_NAME ": " format "\n", ## __VA_ARGS__)
diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
new file mode 100644
index 000000000000..19a2b283fbbe
--- /dev/null
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -0,0 +1,537 @@
+config MTD_NAND_ECC
+ tristate
+
+config MTD_NAND_ECC_SMC
+ bool "NAND ECC Smart Media byte order"
+ depends on MTD_NAND_ECC
+ default n
+ help
+ Software ECC according to the Smart Media Specification.
+ The original Linux implementation had byte 0 and 1 swapped.
+
+
+menuconfig MTD_NAND
+ tristate "Raw/Parallel NAND Device Support"
+ depends on MTD
+ select MTD_NAND_ECC
+ help
+ This enables support for accessing all type of raw/parallel
+ NAND flash devices. For further information see
+ <http://www.linux-mtd.infradead.org/doc/nand.html>.
+
+if MTD_NAND
+
+config MTD_NAND_BCH
+ tristate
+ select BCH
+ depends on MTD_NAND_ECC_BCH
+ default MTD_NAND
+
+config MTD_NAND_ECC_BCH
+ bool "Support software BCH ECC"
+ default n
+ help
+ This enables support for software BCH error correction. Binary BCH
+ codes are more powerful and cpu intensive than traditional Hamming
+ ECC codes. They are used with NAND devices requiring more than 1 bit
+ of error correction.
+
+config MTD_SM_COMMON
+ tristate
+ default n
+
+config MTD_NAND_DENALI
+ tristate
+
+config MTD_NAND_DENALI_PCI
+ tristate "Support Denali NAND controller on Intel Moorestown"
+ select MTD_NAND_DENALI
+ depends on HAS_DMA && PCI
+ help
+ Enable the driver for NAND flash on Intel Moorestown, using the
+ Denali NAND controller core.
+
+config MTD_NAND_DENALI_DT
+ tristate "Support Denali NAND controller as a DT device"
+ select MTD_NAND_DENALI
+ depends on HAS_DMA && HAVE_CLK && OF
+ help
+ Enable the driver for NAND flash on platforms using a Denali NAND
+ controller as a DT device.
+
+config MTD_NAND_GPIO
+ tristate "GPIO assisted NAND Flash driver"
+ depends on GPIOLIB || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ This enables a NAND flash driver where control signals are
+ connected to GPIO pins, and commands and data are communicated
+ via a memory mapped interface.
+
+config MTD_NAND_AMS_DELTA
+ tristate "NAND Flash device on Amstrad E3"
+ depends on MACH_AMS_DELTA
+ default y
+ help
+ Support for NAND flash on Amstrad E3 (Delta).
+
+config MTD_NAND_OMAP2
+ tristate "NAND Flash device on OMAP2, OMAP3, OMAP4 and Keystone"
+ depends on (ARCH_OMAP2PLUS || ARCH_KEYSTONE)
+ help
+ Support for NAND flash on Texas Instruments OMAP2, OMAP3, OMAP4
+ and Keystone platforms.
+
+config MTD_NAND_OMAP_BCH
+ depends on MTD_NAND_OMAP2
+ bool "Support hardware based BCH error correction"
+ default n
+ select BCH
+ help
+ This config enables the ELM hardware engine, which can be used to
+ locate and correct errors when using BCH ECC scheme. This offloads
+ the cpu from doing ECC error searching and correction. However some
+ legacy OMAP families like OMAP2xxx, OMAP3xxx do not have ELM engine
+ so this is optional for them.
+
+config MTD_NAND_OMAP_BCH_BUILD
+ def_tristate MTD_NAND_OMAP2 && MTD_NAND_OMAP_BCH
+
+config MTD_NAND_RICOH
+ tristate "Ricoh xD card reader"
+ default n
+ depends on PCI
+ select MTD_SM_COMMON
+ help
+ Enable support for Ricoh R5C852 xD card reader
+ You also need to enable ether
+ NAND SSFDC (SmartMedia) read only translation layer' or new
+ expermental, readwrite
+ 'SmartMedia/xD new translation layer'
+
+config MTD_NAND_AU1550
+ tristate "Au1550/1200 NAND support"
+ depends on MIPS_ALCHEMY
+ help
+ This enables the driver for the NAND flash controller on the
+ AMD/Alchemy 1550 SOC.
+
+config MTD_NAND_S3C2410
+ tristate "NAND Flash support for Samsung S3C SoCs"
+ depends on ARCH_S3C24XX || ARCH_S3C64XX
+ help
+ This enables the NAND flash controller on the S3C24xx and S3C64xx
+ SoCs
+
+ No board specific support is done by this driver, each board
+ must advertise a platform_device for the driver to attach.
+
+config MTD_NAND_S3C2410_DEBUG
+ bool "Samsung S3C NAND driver debug"
+ depends on MTD_NAND_S3C2410
+ help
+ Enable debugging of the S3C NAND driver
+
+config MTD_NAND_NDFC
+ tristate "NDFC NanD Flash Controller"
+ depends on 4xx
+ select MTD_NAND_ECC_SMC
+ help
+ NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs
+
+config MTD_NAND_S3C2410_CLKSTOP
+ bool "Samsung S3C NAND IDLE clock stop"
+ depends on MTD_NAND_S3C2410
+ default n
+ help
+ Stop the clock to the NAND controller when there is no chip
+ selected to save power. This will mean there is a small delay
+ when the is NAND chip selected or released, but will save
+ approximately 5mA of power when there is nothing happening.
+
+config MTD_NAND_TANGO
+ tristate "NAND Flash support for Tango chips"
+ depends on ARCH_TANGO || COMPILE_TEST
+ depends on HAS_DMA
+ help
+ Enables the NAND Flash controller on Tango chips.
+
+config MTD_NAND_DISKONCHIP
+ tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation)"
+ depends on HAS_IOMEM
+ select REED_SOLOMON
+ select REED_SOLOMON_DEC16
+ help
+ This is a reimplementation of M-Systems DiskOnChip 2000,
+ Millennium and Millennium Plus as a standard NAND device driver,
+ as opposed to the earlier self-contained MTD device drivers.
+ This should enable, among other things, proper JFFS2 operation on
+ these devices.
+
+config MTD_NAND_DISKONCHIP_PROBE_ADVANCED
+ bool "Advanced detection options for DiskOnChip"
+ depends on MTD_NAND_DISKONCHIP
+ help
+ This option allows you to specify nonstandard address at which to
+ probe for a DiskOnChip, or to change the detection options. You
+ are unlikely to need any of this unless you are using LinuxBIOS.
+ Say 'N'.
+
+config MTD_NAND_DISKONCHIP_PROBE_ADDRESS
+ hex "Physical address of DiskOnChip" if MTD_NAND_DISKONCHIP_PROBE_ADVANCED
+ depends on MTD_NAND_DISKONCHIP
+ default "0"
+ ---help---
+ By default, the probe for DiskOnChip devices will look for a
+ DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000.
+ This option allows you to specify a single address at which to probe
+ for the device, which is useful if you have other devices in that
+ range which get upset when they are probed.
+
+ (Note that on PowerPC, the normal probe will only check at
+ 0xE4000000.)
+
+ Normally, you should leave this set to zero, to allow the probe at
+ the normal addresses.
+
+config MTD_NAND_DISKONCHIP_PROBE_HIGH
+ bool "Probe high addresses"
+ depends on MTD_NAND_DISKONCHIP_PROBE_ADVANCED
+ help
+ By default, the probe for DiskOnChip devices will look for a
+ DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000.
+ This option changes to make it probe between 0xFFFC8000 and
+ 0xFFFEE000. Unless you are using LinuxBIOS, this is unlikely to be
+ useful to you. Say 'N'.
+
+config MTD_NAND_DISKONCHIP_BBTWRITE
+ bool "Allow BBT writes on DiskOnChip Millennium and 2000TSOP"
+ depends on MTD_NAND_DISKONCHIP
+ help
+ On DiskOnChip devices shipped with the INFTL filesystem (Millennium
+ and 2000 TSOP/Alon), Linux reserves some space at the end of the
+ device for the Bad Block Table (BBT). If you have existing INFTL
+ data on your device (created by non-Linux tools such as M-Systems'
+ DOS drivers), your data might overlap the area Linux wants to use for
+ the BBT. If this is a concern for you, leave this option disabled and
+ Linux will not write BBT data into this area.
+ The downside of leaving this option disabled is that if bad blocks
+ are detected by Linux, they will not be recorded in the BBT, which
+ could cause future problems.
+ Once you enable this option, new filesystems (INFTL or others, created
+ in Linux or other operating systems) will not use the reserved area.
+ The only reason not to enable this option is to prevent damage to
+ preexisting filesystems.
+ Even if you leave this disabled, you can enable BBT writes at module
+ load time (assuming you build diskonchip as a module) with the module
+ parameter "inftl_bbt_write=1".
+
+config MTD_NAND_DOCG4
+ tristate "Support for DiskOnChip G4"
+ depends on HAS_IOMEM
+ select BCH
+ select BITREVERSE
+ help
+ Support for diskonchip G4 nand flash, found in various smartphones and
+ PDAs, among them the Palm Treo680, HTC Prophet and Wizard, Toshiba
+ Portege G900, Asus P526, and O2 XDA Zinc.
+
+ With this driver you will be able to use UBI and create a ubifs on the
+ device, so you may wish to consider enabling UBI and UBIFS as well.
+
+ These devices ship with the Mys/Sandisk SAFTL formatting, for which
+ there is currently no mtd parser, so you may want to use command line
+ partitioning to segregate write-protected blocks. On the Treo680, the
+ first five erase blocks (256KiB each) are write-protected, followed
+ by the block containing the saftl partition table. This is probably
+ typical.
+
+config MTD_NAND_SHARPSL
+ tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
+ depends on ARCH_PXA
+
+config MTD_NAND_CAFE
+ tristate "NAND support for OLPC CAFÉ chip"
+ depends on PCI
+ select REED_SOLOMON
+ select REED_SOLOMON_DEC16
+ help
+ Use NAND flash attached to the CAFÉ chip designed for the OLPC
+ laptop.
+
+config MTD_NAND_CS553X
+ tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)"
+ depends on X86_32
+ depends on !UML && HAS_IOMEM
+ help
+ The CS553x companion chips for the AMD Geode processor
+ include NAND flash controllers with built-in hardware ECC
+ capabilities; enabling this option will allow you to use
+ these. The driver will check the MSRs to verify that the
+ controller is enabled for NAND, and currently requires that
+ the controller be in MMIO mode.
+
+ If you say "m", the module will be called cs553x_nand.
+
+config MTD_NAND_ATMEL
+ tristate "Support for NAND Flash / SmartMedia on AT91"
+ depends on ARCH_AT91
+ select MFD_ATMEL_SMC
+ help
+ Enables support for NAND Flash / Smart Media Card interface
+ on Atmel AT91 processors.
+
+config MTD_NAND_MARVELL
+ tristate "NAND controller support on Marvell boards"
+ depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU || \
+ COMPILE_TEST
+ depends on HAS_IOMEM && HAS_DMA
+ help
+ This enables the NAND flash controller driver for Marvell boards,
+ including:
+ - PXA3xx processors (NFCv1)
+ - 32-bit Armada platforms (XP, 37x, 38x, 39x) (NFCv2)
+ - 64-bit Aramda platforms (7k, 8k) (NFCv2)
+
+config MTD_NAND_SLC_LPC32XX
+ tristate "NXP LPC32xx SLC Controller"
+ depends on ARCH_LPC32XX
+ help
+ Enables support for NXP's LPC32XX SLC (i.e. for Single Level Cell
+ chips) NAND controller. This is the default for the PHYTEC 3250
+ reference board which contains a NAND256R3A2CZA6 chip.
+
+ Please check the actual NAND chip connected and its support
+ by the SLC NAND controller.
+
+config MTD_NAND_MLC_LPC32XX
+ tristate "NXP LPC32xx MLC Controller"
+ depends on ARCH_LPC32XX
+ help
+ Uses the LPC32XX MLC (i.e. for Multi Level Cell chips) NAND
+ controller. This is the default for the WORK92105 controller
+ board.
+
+ Please check the actual NAND chip connected and its support
+ by the MLC NAND controller.
+
+config MTD_NAND_CM_X270
+ tristate "Support for NAND Flash on CM-X270 modules"
+ depends on MACH_ARMCORE
+
+config MTD_NAND_PASEMI
+ tristate "NAND support for PA Semi PWRficient"
+ depends on PPC_PASEMI
+ help
+ Enables support for NAND Flash interface on PA Semi PWRficient
+ based boards
+
+config MTD_NAND_TMIO
+ tristate "NAND Flash device on Toshiba Mobile IO Controller"
+ depends on MFD_TMIO
+ help
+ Support for NAND flash connected to a Toshiba Mobile IO
+ Controller in some PDAs, including the Sharp SL6000x.
+
+config MTD_NAND_NANDSIM
+ tristate "Support for NAND Flash Simulator"
+ help
+ The simulator may simulate various NAND flash chips for the
+ MTD nand layer.
+
+config MTD_NAND_GPMI_NAND
+ tristate "GPMI NAND Flash Controller driver"
+ depends on MTD_NAND && MXS_DMA
+ help
+ Enables NAND Flash support for IMX23, IMX28 or IMX6.
+ The GPMI controller is very powerful, with the help of BCH
+ module, it can do the hardware ECC. The GPMI supports several
+ NAND flashs at the same time.
+
+config MTD_NAND_BRCMNAND
+ tristate "Broadcom STB NAND controller"
+ depends on ARM || ARM64 || MIPS
+ help
+ Enables the Broadcom NAND controller driver. The controller was
+ originally designed for Set-Top Box but is used on various BCM7xxx,
+ BCM3xxx, BCM63xxx, iProc/Cygnus and more.
+
+config MTD_NAND_BCM47XXNFLASH
+ tristate "Support for NAND flash on BCM4706 BCMA bus"
+ depends on BCMA_NFLASH
+ help
+ BCMA bus can have various flash memories attached, they are
+ registered by bcma as platform devices. This enables driver for
+ NAND flash memories. For now only BCM4706 is supported.
+
+config MTD_NAND_PLATFORM
+ tristate "Support for generic platform NAND driver"
+ depends on HAS_IOMEM
+ help
+ This implements a generic NAND driver for on-SOC platform
+ devices. You will need to provide platform-specific functions
+ via platform_data.
+
+config MTD_NAND_ORION
+ tristate "NAND Flash support for Marvell Orion SoC"
+ depends on PLAT_ORION
+ help
+ This enables the NAND flash controller on Orion machines.
+
+ No board specific support is done by this driver, each board
+ must advertise a platform_device for the driver to attach.
+
+config MTD_NAND_OXNAS
+ tristate "NAND Flash support for Oxford Semiconductor SoC"
+ depends on ARCH_OXNAS || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ This enables the NAND flash controller on Oxford Semiconductor SoCs.
+
+config MTD_NAND_FSL_ELBC
+ tristate "NAND support for Freescale eLBC controllers"
+ depends on FSL_SOC
+ select FSL_LBC
+ help
+ Various Freescale chips, including the 8313, include a NAND Flash
+ Controller Module with built-in hardware ECC capabilities.
+ Enabling this option will enable you to use this to control
+ external NAND devices.
+
+config MTD_NAND_FSL_IFC
+ tristate "NAND support for Freescale IFC controller"
+ depends on FSL_SOC || ARCH_LAYERSCAPE || SOC_LS1021A
+ select FSL_IFC
+ select MEMORY
+ help
+ Various Freescale chips e.g P1010, include a NAND Flash machine
+ with built-in hardware ECC capabilities.
+ Enabling this option will enable you to use this to control
+ external NAND devices.
+
+config MTD_NAND_FSL_UPM
+ tristate "Support for NAND on Freescale UPM"
+ depends on PPC_83xx || PPC_85xx
+ select FSL_LBC
+ help
+ Enables support for NAND Flash chips wired onto Freescale PowerPC
+ processor localbus with User-Programmable Machine support.
+
+config MTD_NAND_MPC5121_NFC
+ tristate "MPC5121 built-in NAND Flash Controller support"
+ depends on PPC_MPC512x
+ help
+ This enables the driver for the NAND flash controller on the
+ MPC5121 SoC.
+
+config MTD_NAND_VF610_NFC
+ tristate "Support for Freescale NFC for VF610/MPC5125"
+ depends on (SOC_VF610 || COMPILE_TEST)
+ depends on HAS_IOMEM
+ help
+ Enables support for NAND Flash Controller on some Freescale
+ processors like the VF610, MPC5125, MCF54418 or Kinetis K70.
+ The driver supports a maximum 2k page size. With 2k pages and
+ 64 bytes or more of OOB, hardware ECC with up to 32-bit error
+ correction is supported. Hardware ECC is only enabled through
+ device tree.
+
+config MTD_NAND_MXC
+ tristate "MXC NAND support"
+ depends on ARCH_MXC
+ help
+ This enables the driver for the NAND flash controller on the
+ MXC processors.
+
+config MTD_NAND_SH_FLCTL
+ tristate "Support for NAND on Renesas SuperH FLCTL"
+ depends on SUPERH || COMPILE_TEST
+ depends on HAS_IOMEM
+ depends on HAS_DMA
+ help
+ Several Renesas SuperH CPU has FLCTL. This option enables support
+ for NAND Flash using FLCTL.
+
+config MTD_NAND_DAVINCI
+ tristate "Support NAND on DaVinci/Keystone SoC"
+ depends on ARCH_DAVINCI || (ARCH_KEYSTONE && TI_AEMIF)
+ help
+ Enable the driver for NAND flash chips on Texas Instruments
+ DaVinci/Keystone processors.
+
+config MTD_NAND_TXX9NDFMC
+ tristate "NAND Flash support for TXx9 SoC"
+ depends on SOC_TX4938 || SOC_TX4939
+ help
+ This enables the NAND flash controller on the TXx9 SoCs.
+
+config MTD_NAND_SOCRATES
+ tristate "Support for NAND on Socrates board"
+ depends on SOCRATES
+ help
+ Enables support for NAND Flash chips wired onto Socrates board.
+
+config MTD_NAND_NUC900
+ tristate "Support for NAND on Nuvoton NUC9xx/w90p910 evaluation boards."
+ depends on ARCH_W90X900
+ help
+ This enables the driver for the NAND Flash on evaluation board based
+ on w90p910 / NUC9xx.
+
+config MTD_NAND_JZ4740
+ tristate "Support for JZ4740 SoC NAND controller"
+ depends on MACH_JZ4740
+ help
+ Enables support for NAND Flash on JZ4740 SoC based boards.
+
+config MTD_NAND_JZ4780
+ tristate "Support for NAND on JZ4780 SoC"
+ depends on MACH_JZ4780 && JZ4780_NEMC
+ help
+ Enables support for NAND Flash connected to the NEMC on JZ4780 SoC
+ based boards, using the BCH controller for hardware error correction.
+
+config MTD_NAND_FSMC
+ tristate "Support for NAND on ST Micros FSMC"
+ depends on OF
+ depends on PLAT_SPEAR || ARCH_NOMADIK || ARCH_U8500 || MACH_U300
+ help
+ Enables support for NAND Flash chips on the ST Microelectronics
+ Flexible Static Memory Controller (FSMC)
+
+config MTD_NAND_XWAY
+ bool "Support for NAND on Lantiq XWAY SoC"
+ depends on LANTIQ && SOC_TYPE_XWAY
+ help
+ Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached
+ to the External Bus Unit (EBU).
+
+config MTD_NAND_SUNXI
+ tristate "Support for NAND on Allwinner SoCs"
+ depends on ARCH_SUNXI
+ help
+ Enables support for NAND Flash chips on Allwinner SoCs.
+
+config MTD_NAND_HISI504
+ tristate "Support for NAND controller on Hisilicon SoC Hip04"
+ depends on ARCH_HISI || COMPILE_TEST
+ depends on HAS_DMA
+ help
+ Enables support for NAND controller on Hisilicon SoC Hip04.
+
+config MTD_NAND_QCOM
+ tristate "Support for NAND on QCOM SoCs"
+ depends on ARCH_QCOM
+ help
+ Enables support for NAND flash chips on SoCs containing the EBI2 NAND
+ controller. This controller is found on IPQ806x SoC.
+
+config MTD_NAND_MTK
+ tristate "Support for NAND controller on MTK SoCs"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on HAS_DMA
+ help
+ Enables support for NAND controller on MTK SoCs.
+ This controller is found on mt27xx, mt81xx, mt65xx SoCs.
+
+endif # MTD_NAND
diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
new file mode 100644
index 000000000000..165b7ef9e9a1
--- /dev/null
+++ b/drivers/mtd/nand/raw/Makefile
@@ -0,0 +1,66 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_MTD_NAND) += nand.o
+obj-$(CONFIG_MTD_NAND_ECC) += nand_ecc.o
+obj-$(CONFIG_MTD_NAND_BCH) += nand_bch.o
+obj-$(CONFIG_MTD_SM_COMMON) += sm_common.o
+
+obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o
+obj-$(CONFIG_MTD_NAND_AMS_DELTA) += ams-delta.o
+obj-$(CONFIG_MTD_NAND_DENALI) += denali.o
+obj-$(CONFIG_MTD_NAND_DENALI_PCI) += denali_pci.o
+obj-$(CONFIG_MTD_NAND_DENALI_DT) += denali_dt.o
+obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o
+obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o
+obj-$(CONFIG_MTD_NAND_TANGO) += tango_nand.o
+obj-$(CONFIG_MTD_NAND_DAVINCI) += davinci_nand.o
+obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o
+obj-$(CONFIG_MTD_NAND_DOCG4) += docg4.o
+obj-$(CONFIG_MTD_NAND_FSMC) += fsmc_nand.o
+obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o
+obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o
+obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o
+obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o
+obj-$(CONFIG_MTD_NAND_ATMEL) += atmel/
+obj-$(CONFIG_MTD_NAND_GPIO) += gpio.o
+omap2_nand-objs := omap2.o
+obj-$(CONFIG_MTD_NAND_OMAP2) += omap2_nand.o
+obj-$(CONFIG_MTD_NAND_OMAP_BCH_BUILD) += omap_elm.o
+obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o
+obj-$(CONFIG_MTD_NAND_MARVELL) += marvell_nand.o
+obj-$(CONFIG_MTD_NAND_TMIO) += tmio_nand.o
+obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o
+obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o
+obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o
+obj-$(CONFIG_MTD_NAND_OXNAS) += oxnas_nand.o
+obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o
+obj-$(CONFIG_MTD_NAND_FSL_IFC) += fsl_ifc_nand.o
+obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o
+obj-$(CONFIG_MTD_NAND_SLC_LPC32XX) += lpc32xx_slc.o
+obj-$(CONFIG_MTD_NAND_MLC_LPC32XX) += lpc32xx_mlc.o
+obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o
+obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o
+obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o
+obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o
+obj-$(CONFIG_MTD_NAND_NUC900) += nuc900_nand.o
+obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o
+obj-$(CONFIG_MTD_NAND_VF610_NFC) += vf610_nfc.o
+obj-$(CONFIG_MTD_NAND_RICOH) += r852.o
+obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o
+obj-$(CONFIG_MTD_NAND_JZ4780) += jz4780_nand.o jz4780_bch.o
+obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/
+obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o
+obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/
+obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o
+obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o
+obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/
+obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o
+obj-$(CONFIG_MTD_NAND_MTK) += mtk_ecc.o mtk_nand.o
+
+nand-objs := nand_base.o nand_bbt.o nand_timings.o nand_ids.o
+nand-objs += nand_amd.o
+nand-objs += nand_hynix.o
+nand-objs += nand_macronix.o
+nand-objs += nand_micron.o
+nand-objs += nand_samsung.o
+nand-objs += nand_toshiba.o
diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
new file mode 100644
index 000000000000..37a3cc21c7bc
--- /dev/null
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2006 Jonathan McDowell <noodles@earth.li>
+ *
+ * Derived from drivers/mtd/nand/toto.c (removed in v2.6.28)
+ * Copyright (c) 2003 Texas Instruments
+ * Copyright (c) 2002 Thomas Gleixner <tgxl@linutronix.de>
+ *
+ * Converted to platform driver by Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
+ * Partially stolen from plat_nand.c
+ *
+ * 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.
+ *
+ * Overview:
+ * This is a device driver for the NAND flash device found on the
+ * Amstrad E3 (Delta).
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/gpio.h>
+#include <linux/platform_data/gpio-omap.h>
+
+#include <asm/io.h>
+#include <asm/sizes.h>
+
+#include <mach/board-ams-delta.h>
+
+#include <mach/hardware.h>
+
+/*
+ * MTD structure for E3 (Delta)
+ */
+static struct mtd_info *ams_delta_mtd = NULL;
+
+/*
+ * Define partitions for flash devices
+ */
+
+static const struct mtd_partition partition_info[] = {
+ { .name = "Kernel",
+ .offset = 0,
+ .size = 3 * SZ_1M + SZ_512K },
+ { .name = "u-boot",
+ .offset = 3 * SZ_1M + SZ_512K,
+ .size = SZ_256K },
+ { .name = "u-boot params",
+ .offset = 3 * SZ_1M + SZ_512K + SZ_256K,
+ .size = SZ_256K },
+ { .name = "Amstrad LDR",
+ .offset = 4 * SZ_1M,
+ .size = SZ_256K },
+ { .name = "File system",
+ .offset = 4 * SZ_1M + 1 * SZ_256K,
+ .size = 27 * SZ_1M },
+ { .name = "PBL reserved",
+ .offset = 32 * SZ_1M - 3 * SZ_256K,
+ .size = 3 * SZ_256K },
+};
+
+static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ void __iomem *io_base = (void __iomem *)nand_get_controller_data(this);
+
+ writew(0, io_base + OMAP_MPUIO_IO_CNTL);
+ writew(byte, this->IO_ADDR_W);
+ gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NWE, 0);
+ ndelay(40);
+ gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NWE, 1);
+}
+
+static u_char ams_delta_read_byte(struct mtd_info *mtd)
+{
+ u_char res;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ void __iomem *io_base = (void __iomem *)nand_get_controller_data(this);
+
+ gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NRE, 0);
+ ndelay(40);
+ writew(~0, io_base + OMAP_MPUIO_IO_CNTL);
+ res = readw(this->IO_ADDR_R);
+ gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NRE, 1);
+
+ return res;
+}
+
+static void ams_delta_write_buf(struct mtd_info *mtd, const u_char *buf,
+ int len)
+{
+ int i;
+
+ for (i=0; i<len; i++)
+ ams_delta_write_byte(mtd, buf[i]);
+}
+
+static void ams_delta_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+ int i;
+
+ for (i=0; i<len; i++)
+ buf[i] = ams_delta_read_byte(mtd);
+}
+
+/*
+ * Command control function
+ *
+ * ctrl:
+ * NAND_NCE: bit 0 -> bit 2
+ * NAND_CLE: bit 1 -> bit 7
+ * NAND_ALE: bit 2 -> bit 6
+ */
+static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd,
+ unsigned int ctrl)
+{
+
+ if (ctrl & NAND_CTRL_CHANGE) {
+ gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NCE,
+ (ctrl & NAND_NCE) == 0);
+ gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_CLE,
+ (ctrl & NAND_CLE) != 0);
+ gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_ALE,
+ (ctrl & NAND_ALE) != 0);
+ }
+
+ if (cmd != NAND_CMD_NONE)
+ ams_delta_write_byte(mtd, cmd);
+}
+
+static int ams_delta_nand_ready(struct mtd_info *mtd)
+{
+ return gpio_get_value(AMS_DELTA_GPIO_PIN_NAND_RB);
+}
+
+static const struct gpio _mandatory_gpio[] = {
+ {
+ .gpio = AMS_DELTA_GPIO_PIN_NAND_NCE,
+ .flags = GPIOF_OUT_INIT_HIGH,
+ .label = "nand_nce",
+ },
+ {
+ .gpio = AMS_DELTA_GPIO_PIN_NAND_NRE,
+ .flags = GPIOF_OUT_INIT_HIGH,
+ .label = "nand_nre",
+ },
+ {
+ .gpio = AMS_DELTA_GPIO_PIN_NAND_NWP,
+ .flags = GPIOF_OUT_INIT_HIGH,
+ .label = "nand_nwp",
+ },
+ {
+ .gpio = AMS_DELTA_GPIO_PIN_NAND_NWE,
+ .flags = GPIOF_OUT_INIT_HIGH,
+ .label = "nand_nwe",
+ },
+ {
+ .gpio = AMS_DELTA_GPIO_PIN_NAND_ALE,
+ .flags = GPIOF_OUT_INIT_LOW,
+ .label = "nand_ale",
+ },
+ {
+ .gpio = AMS_DELTA_GPIO_PIN_NAND_CLE,
+ .flags = GPIOF_OUT_INIT_LOW,
+ .label = "nand_cle",
+ },
+};
+
+/*
+ * Main initialization routine
+ */
+static int ams_delta_init(struct platform_device *pdev)
+{
+ struct nand_chip *this;
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ void __iomem *io_base;
+ int err = 0;
+
+ if (!res)
+ return -ENXIO;
+
+ /* Allocate memory for MTD device structure and private data */
+ this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
+ if (!this) {
+ pr_warn("Unable to allocate E3 NAND MTD device structure.\n");
+ err = -ENOMEM;
+ goto out;
+ }
+
+ ams_delta_mtd = nand_to_mtd(this);
+ ams_delta_mtd->owner = THIS_MODULE;
+
+ /*
+ * Don't try to request the memory region from here,
+ * it should have been already requested from the
+ * gpio-omap driver and requesting it again would fail.
+ */
+
+ io_base = ioremap(res->start, resource_size(res));
+ if (io_base == NULL) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ err = -EIO;
+ goto out_free;
+ }
+
+ nand_set_controller_data(this, (void *)io_base);
+
+ /* Set address of NAND IO lines */
+ this->IO_ADDR_R = io_base + OMAP_MPUIO_INPUT_LATCH;
+ this->IO_ADDR_W = io_base + OMAP_MPUIO_OUTPUT;
+ this->read_byte = ams_delta_read_byte;
+ this->write_buf = ams_delta_write_buf;
+ this->read_buf = ams_delta_read_buf;
+ this->cmd_ctrl = ams_delta_hwcontrol;
+ if (gpio_request(AMS_DELTA_GPIO_PIN_NAND_RB, "nand_rdy") == 0) {
+ this->dev_ready = ams_delta_nand_ready;
+ } else {
+ this->dev_ready = NULL;
+ pr_notice("Couldn't request gpio for Delta NAND ready.\n");
+ }
+ /* 25 us command delay time */
+ this->chip_delay = 30;
+ this->ecc.mode = NAND_ECC_SOFT;
+ this->ecc.algo = NAND_ECC_HAMMING;
+
+ platform_set_drvdata(pdev, io_base);
+
+ /* Set chip enabled, but */
+ err = gpio_request_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio));
+ if (err)
+ goto out_gpio;
+
+ /* Scan to find existence of the device */
+ err = nand_scan(ams_delta_mtd, 1);
+ if (err)
+ goto out_mtd;
+
+ /* Register the partitions */
+ mtd_device_register(ams_delta_mtd, partition_info,
+ ARRAY_SIZE(partition_info));
+
+ goto out;
+
+ out_mtd:
+ gpio_free_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio));
+out_gpio:
+ gpio_free(AMS_DELTA_GPIO_PIN_NAND_RB);
+ iounmap(io_base);
+out_free:
+ kfree(this);
+ out:
+ return err;
+}
+
+/*
+ * Clean up routine
+ */
+static int ams_delta_cleanup(struct platform_device *pdev)
+{
+ void __iomem *io_base = platform_get_drvdata(pdev);
+
+ /* Release resources, unregister device */
+ nand_release(ams_delta_mtd);
+
+ gpio_free_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio));
+ gpio_free(AMS_DELTA_GPIO_PIN_NAND_RB);
+ iounmap(io_base);
+
+ /* Free the MTD device structure */
+ kfree(mtd_to_nand(ams_delta_mtd));
+
+ return 0;
+}
+
+static struct platform_driver ams_delta_nand_driver = {
+ .probe = ams_delta_init,
+ .remove = ams_delta_cleanup,
+ .driver = {
+ .name = "ams-delta-nand",
+ },
+};
+
+module_platform_driver(ams_delta_nand_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jonathan McDowell <noodles@earth.li>");
+MODULE_DESCRIPTION("Glue layer for NAND flash on Amstrad E3 (Delta)");
diff --git a/drivers/mtd/nand/atmel/Makefile b/drivers/mtd/nand/raw/atmel/Makefile
index 288db4f38a8f..288db4f38a8f 100644
--- a/drivers/mtd/nand/atmel/Makefile
+++ b/drivers/mtd/nand/raw/atmel/Makefile
diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c
new file mode 100644
index 000000000000..12f6753d47ae
--- /dev/null
+++ b/drivers/mtd/nand/raw/atmel/nand-controller.c
@@ -0,0 +1,2565 @@
+/*
+ * Copyright 2017 ATMEL
+ * Copyright 2017 Free Electrons
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
+ *
+ * Derived from the atmel_nand.c driver which contained the following
+ * copyrights:
+ *
+ * Copyright 2003 Rick Bronson
+ *
+ * Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8)
+ * Copyright 2001 Thomas Gleixner (gleixner@autronix.de)
+ *
+ * Derived from drivers/mtd/spia.c (removed in v3.8)
+ * Copyright 2000 Steven J. Hill (sjhill@cotw.com)
+ *
+ *
+ * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263
+ * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright 2007
+ *
+ * Derived from Das U-Boot source code
+ * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c)
+ * Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas
+ *
+ * Add Programmable Multibit ECC support for various AT91 SoC
+ * Copyright 2012 ATMEL, Hong Xu
+ *
+ * Add Nand Flash Controller support for SAMA5 SoC
+ * Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.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.
+ *
+ * A few words about the naming convention in this file. This convention
+ * applies to structure and function names.
+ *
+ * Prefixes:
+ *
+ * - atmel_nand_: all generic structures/functions
+ * - atmel_smc_nand_: all structures/functions specific to the SMC interface
+ * (at91sam9 and avr32 SoCs)
+ * - atmel_hsmc_nand_: all structures/functions specific to the HSMC interface
+ * (sama5 SoCs and later)
+ * - atmel_nfc_: all structures/functions used to manipulate the NFC sub-block
+ * that is available in the HSMC block
+ * - <soc>_nand_: all SoC specific structures/functions
+ */
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/genalloc.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/atmel-matrix.h>
+#include <linux/mfd/syscon/atmel-smc.h>
+#include <linux/module.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/iopoll.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "pmecc.h"
+
+#define ATMEL_HSMC_NFC_CFG 0x0
+#define ATMEL_HSMC_NFC_CFG_SPARESIZE(x) (((x) / 4) << 24)
+#define ATMEL_HSMC_NFC_CFG_SPARESIZE_MASK GENMASK(30, 24)
+#define ATMEL_HSMC_NFC_CFG_DTO(cyc, mul) (((cyc) << 16) | ((mul) << 20))
+#define ATMEL_HSMC_NFC_CFG_DTO_MAX GENMASK(22, 16)
+#define ATMEL_HSMC_NFC_CFG_RBEDGE BIT(13)
+#define ATMEL_HSMC_NFC_CFG_FALLING_EDGE BIT(12)
+#define ATMEL_HSMC_NFC_CFG_RSPARE BIT(9)
+#define ATMEL_HSMC_NFC_CFG_WSPARE BIT(8)
+#define ATMEL_HSMC_NFC_CFG_PAGESIZE_MASK GENMASK(2, 0)
+#define ATMEL_HSMC_NFC_CFG_PAGESIZE(x) (fls((x) / 512) - 1)
+
+#define ATMEL_HSMC_NFC_CTRL 0x4
+#define ATMEL_HSMC_NFC_CTRL_EN BIT(0)
+#define ATMEL_HSMC_NFC_CTRL_DIS BIT(1)
+
+#define ATMEL_HSMC_NFC_SR 0x8
+#define ATMEL_HSMC_NFC_IER 0xc
+#define ATMEL_HSMC_NFC_IDR 0x10
+#define ATMEL_HSMC_NFC_IMR 0x14
+#define ATMEL_HSMC_NFC_SR_ENABLED BIT(1)
+#define ATMEL_HSMC_NFC_SR_RB_RISE BIT(4)
+#define ATMEL_HSMC_NFC_SR_RB_FALL BIT(5)
+#define ATMEL_HSMC_NFC_SR_BUSY BIT(8)
+#define ATMEL_HSMC_NFC_SR_WR BIT(11)
+#define ATMEL_HSMC_NFC_SR_CSID GENMASK(14, 12)
+#define ATMEL_HSMC_NFC_SR_XFRDONE BIT(16)
+#define ATMEL_HSMC_NFC_SR_CMDDONE BIT(17)
+#define ATMEL_HSMC_NFC_SR_DTOE BIT(20)
+#define ATMEL_HSMC_NFC_SR_UNDEF BIT(21)
+#define ATMEL_HSMC_NFC_SR_AWB BIT(22)
+#define ATMEL_HSMC_NFC_SR_NFCASE BIT(23)
+#define ATMEL_HSMC_NFC_SR_ERRORS (ATMEL_HSMC_NFC_SR_DTOE | \
+ ATMEL_HSMC_NFC_SR_UNDEF | \
+ ATMEL_HSMC_NFC_SR_AWB | \
+ ATMEL_HSMC_NFC_SR_NFCASE)
+#define ATMEL_HSMC_NFC_SR_RBEDGE(x) BIT((x) + 24)
+
+#define ATMEL_HSMC_NFC_ADDR 0x18
+#define ATMEL_HSMC_NFC_BANK 0x1c
+
+#define ATMEL_NFC_MAX_RB_ID 7
+
+#define ATMEL_NFC_SRAM_SIZE 0x2400
+
+#define ATMEL_NFC_CMD(pos, cmd) ((cmd) << (((pos) * 8) + 2))
+#define ATMEL_NFC_VCMD2 BIT(18)
+#define ATMEL_NFC_ACYCLE(naddrs) ((naddrs) << 19)
+#define ATMEL_NFC_CSID(cs) ((cs) << 22)
+#define ATMEL_NFC_DATAEN BIT(25)
+#define ATMEL_NFC_NFCWR BIT(26)
+
+#define ATMEL_NFC_MAX_ADDR_CYCLES 5
+
+#define ATMEL_NAND_ALE_OFFSET BIT(21)
+#define ATMEL_NAND_CLE_OFFSET BIT(22)
+
+#define DEFAULT_TIMEOUT_MS 1000
+#define MIN_DMA_LEN 128
+
+enum atmel_nand_rb_type {
+ ATMEL_NAND_NO_RB,
+ ATMEL_NAND_NATIVE_RB,
+ ATMEL_NAND_GPIO_RB,
+};
+
+struct atmel_nand_rb {
+ enum atmel_nand_rb_type type;
+ union {
+ struct gpio_desc *gpio;
+ int id;
+ };
+};
+
+struct atmel_nand_cs {
+ int id;
+ struct atmel_nand_rb rb;
+ struct gpio_desc *csgpio;
+ struct {
+ void __iomem *virt;
+ dma_addr_t dma;
+ } io;
+
+ struct atmel_smc_cs_conf smcconf;
+};
+
+struct atmel_nand {
+ struct list_head node;
+ struct device *dev;
+ struct nand_chip base;
+ struct atmel_nand_cs *activecs;
+ struct atmel_pmecc_user *pmecc;
+ struct gpio_desc *cdgpio;
+ int numcs;
+ struct atmel_nand_cs cs[];
+};
+
+static inline struct atmel_nand *to_atmel_nand(struct nand_chip *chip)
+{
+ return container_of(chip, struct atmel_nand, base);
+}
+
+enum atmel_nfc_data_xfer {
+ ATMEL_NFC_NO_DATA,
+ ATMEL_NFC_READ_DATA,
+ ATMEL_NFC_WRITE_DATA,
+};
+
+struct atmel_nfc_op {
+ u8 cs;
+ u8 ncmds;
+ u8 cmds[2];
+ u8 naddrs;
+ u8 addrs[5];
+ enum atmel_nfc_data_xfer data;
+ u32 wait;
+ u32 errors;
+};
+
+struct atmel_nand_controller;
+struct atmel_nand_controller_caps;
+
+struct atmel_nand_controller_ops {
+ int (*probe)(struct platform_device *pdev,
+ const struct atmel_nand_controller_caps *caps);
+ int (*remove)(struct atmel_nand_controller *nc);
+ void (*nand_init)(struct atmel_nand_controller *nc,
+ struct atmel_nand *nand);
+ int (*ecc_init)(struct atmel_nand *nand);
+ int (*setup_data_interface)(struct atmel_nand *nand, int csline,
+ const struct nand_data_interface *conf);
+};
+
+struct atmel_nand_controller_caps {
+ bool has_dma;
+ bool legacy_of_bindings;
+ u32 ale_offs;
+ u32 cle_offs;
+ const struct atmel_nand_controller_ops *ops;
+};
+
+struct atmel_nand_controller {
+ struct nand_hw_control base;
+ const struct atmel_nand_controller_caps *caps;
+ struct device *dev;
+ struct regmap *smc;
+ struct dma_chan *dmac;
+ struct atmel_pmecc *pmecc;
+ struct list_head chips;
+ struct clk *mck;
+};
+
+static inline struct atmel_nand_controller *
+to_nand_controller(struct nand_hw_control *ctl)
+{
+ return container_of(ctl, struct atmel_nand_controller, base);
+}
+
+struct atmel_smc_nand_controller {
+ struct atmel_nand_controller base;
+ struct regmap *matrix;
+ unsigned int ebi_csa_offs;
+};
+
+static inline struct atmel_smc_nand_controller *
+to_smc_nand_controller(struct nand_hw_control *ctl)
+{
+ return container_of(to_nand_controller(ctl),
+ struct atmel_smc_nand_controller, base);
+}
+
+struct atmel_hsmc_nand_controller {
+ struct atmel_nand_controller base;
+ struct {
+ struct gen_pool *pool;
+ void __iomem *virt;
+ dma_addr_t dma;
+ } sram;
+ const struct atmel_hsmc_reg_layout *hsmc_layout;
+ struct regmap *io;
+ struct atmel_nfc_op op;
+ struct completion complete;
+ int irq;
+
+ /* Only used when instantiating from legacy DT bindings. */
+ struct clk *clk;
+};
+
+static inline struct atmel_hsmc_nand_controller *
+to_hsmc_nand_controller(struct nand_hw_control *ctl)
+{
+ return container_of(to_nand_controller(ctl),
+ struct atmel_hsmc_nand_controller, base);
+}
+
+static bool atmel_nfc_op_done(struct atmel_nfc_op *op, u32 status)
+{
+ op->errors |= status & ATMEL_HSMC_NFC_SR_ERRORS;
+ op->wait ^= status & op->wait;
+
+ return !op->wait || op->errors;
+}
+
+static irqreturn_t atmel_nfc_interrupt(int irq, void *data)
+{
+ struct atmel_hsmc_nand_controller *nc = data;
+ u32 sr, rcvd;
+ bool done;
+
+ regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &sr);
+
+ rcvd = sr & (nc->op.wait | ATMEL_HSMC_NFC_SR_ERRORS);
+ done = atmel_nfc_op_done(&nc->op, sr);
+
+ if (rcvd)
+ regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, rcvd);
+
+ if (done)
+ complete(&nc->complete);
+
+ return rcvd ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int atmel_nfc_wait(struct atmel_hsmc_nand_controller *nc, bool poll,
+ unsigned int timeout_ms)
+{
+ int ret;
+
+ if (!timeout_ms)
+ timeout_ms = DEFAULT_TIMEOUT_MS;
+
+ if (poll) {
+ u32 status;
+
+ ret = regmap_read_poll_timeout(nc->base.smc,
+ ATMEL_HSMC_NFC_SR, status,
+ atmel_nfc_op_done(&nc->op,
+ status),
+ 0, timeout_ms * 1000);
+ } else {
+ init_completion(&nc->complete);
+ regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IER,
+ nc->op.wait | ATMEL_HSMC_NFC_SR_ERRORS);
+ ret = wait_for_completion_timeout(&nc->complete,
+ msecs_to_jiffies(timeout_ms));
+ if (!ret)
+ ret = -ETIMEDOUT;
+ else
+ ret = 0;
+
+ regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, 0xffffffff);
+ }
+
+ if (nc->op.errors & ATMEL_HSMC_NFC_SR_DTOE) {
+ dev_err(nc->base.dev, "Waiting NAND R/B Timeout\n");
+ ret = -ETIMEDOUT;
+ }
+
+ if (nc->op.errors & ATMEL_HSMC_NFC_SR_UNDEF) {
+ dev_err(nc->base.dev, "Access to an undefined area\n");
+ ret = -EIO;
+ }
+
+ if (nc->op.errors & ATMEL_HSMC_NFC_SR_AWB) {
+ dev_err(nc->base.dev, "Access while busy\n");
+ ret = -EIO;
+ }
+
+ if (nc->op.errors & ATMEL_HSMC_NFC_SR_NFCASE) {
+ dev_err(nc->base.dev, "Wrong access size\n");
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static void atmel_nand_dma_transfer_finished(void *data)
+{
+ struct completion *finished = data;
+
+ complete(finished);
+}
+
+static int atmel_nand_dma_transfer(struct atmel_nand_controller *nc,
+ void *buf, dma_addr_t dev_dma, size_t len,
+ enum dma_data_direction dir)
+{
+ DECLARE_COMPLETION_ONSTACK(finished);
+ dma_addr_t src_dma, dst_dma, buf_dma;
+ struct dma_async_tx_descriptor *tx;
+ dma_cookie_t cookie;
+
+ buf_dma = dma_map_single(nc->dev, buf, len, dir);
+ if (dma_mapping_error(nc->dev, dev_dma)) {
+ dev_err(nc->dev,
+ "Failed to prepare a buffer for DMA access\n");
+ goto err;
+ }
+
+ if (dir == DMA_FROM_DEVICE) {
+ src_dma = dev_dma;
+ dst_dma = buf_dma;
+ } else {
+ src_dma = buf_dma;
+ dst_dma = dev_dma;
+ }
+
+ tx = dmaengine_prep_dma_memcpy(nc->dmac, dst_dma, src_dma, len,
+ DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
+ if (!tx) {
+ dev_err(nc->dev, "Failed to prepare DMA memcpy\n");
+ goto err_unmap;
+ }
+
+ tx->callback = atmel_nand_dma_transfer_finished;
+ tx->callback_param = &finished;
+
+ cookie = dmaengine_submit(tx);
+ if (dma_submit_error(cookie)) {
+ dev_err(nc->dev, "Failed to do DMA tx_submit\n");
+ goto err_unmap;
+ }
+
+ dma_async_issue_pending(nc->dmac);
+ wait_for_completion(&finished);
+
+ return 0;
+
+err_unmap:
+ dma_unmap_single(nc->dev, buf_dma, len, dir);
+
+err:
+ dev_dbg(nc->dev, "Fall back to CPU I/O\n");
+
+ return -EIO;
+}
+
+static u8 atmel_nand_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct atmel_nand *nand = to_atmel_nand(chip);
+
+ return ioread8(nand->activecs->io.virt);
+}
+
+static u16 atmel_nand_read_word(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct atmel_nand *nand = to_atmel_nand(chip);
+
+ return ioread16(nand->activecs->io.virt);
+}
+
+static void atmel_nand_write_byte(struct mtd_info *mtd, u8 byte)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct atmel_nand *nand = to_atmel_nand(chip);
+
+ if (chip->options & NAND_BUSWIDTH_16)
+ iowrite16(byte | (byte << 8), nand->activecs->io.virt);
+ else
+ iowrite8(byte, nand->activecs->io.virt);
+}
+
+static void atmel_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct atmel_nand *nand = to_atmel_nand(chip);
+ struct atmel_nand_controller *nc;
+
+ nc = to_nand_controller(chip->controller);
+
+ /*
+ * If the controller supports DMA, the buffer address is DMA-able and
+ * len is long enough to make DMA transfers profitable, let's trigger
+ * a DMA transfer. If it fails, fallback to PIO mode.
+ */
+ if (nc->dmac && virt_addr_valid(buf) &&
+ len >= MIN_DMA_LEN &&
+ !atmel_nand_dma_transfer(nc, buf, nand->activecs->io.dma, len,
+ DMA_FROM_DEVICE))
+ return;
+
+ if (chip->options & NAND_BUSWIDTH_16)
+ ioread16_rep(nand->activecs->io.virt, buf, len / 2);
+ else
+ ioread8_rep(nand->activecs->io.virt, buf, len);
+}
+
+static void atmel_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct atmel_nand *nand = to_atmel_nand(chip);
+ struct atmel_nand_controller *nc;
+
+ nc = to_nand_controller(chip->controller);
+
+ /*
+ * If the controller supports DMA, the buffer address is DMA-able and
+ * len is long enough to make DMA transfers profitable, let's trigger
+ * a DMA transfer. If it fails, fallback to PIO mode.
+ */
+ if (nc->dmac && virt_addr_valid(buf) &&
+ len >= MIN_DMA_LEN &&
+ !atmel_nand_dma_transfer(nc, (void *)buf, nand->activecs->io.dma,
+ len, DMA_TO_DEVICE))
+ return;
+
+ if (chip->options & NAND_BUSWIDTH_16)
+ iowrite16_rep(nand->activecs->io.virt, buf, len / 2);
+ else
+ iowrite8_rep(nand->activecs->io.virt, buf, len);
+}
+
+static int atmel_nand_dev_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct atmel_nand *nand = to_atmel_nand(chip);
+
+ return gpiod_get_value(nand->activecs->rb.gpio);
+}
+
+static void atmel_nand_select_chip(struct mtd_info *mtd, int cs)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct atmel_nand *nand = to_atmel_nand(chip);
+
+ if (cs < 0 || cs >= nand->numcs) {
+ nand->activecs = NULL;
+ chip->dev_ready = NULL;
+ return;
+ }
+
+ nand->activecs = &nand->cs[cs];
+
+ if (nand->activecs->rb.type == ATMEL_NAND_GPIO_RB)
+ chip->dev_ready = atmel_nand_dev_ready;
+}
+
+static int atmel_hsmc_nand_dev_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct atmel_nand *nand = to_atmel_nand(chip);
+ struct atmel_hsmc_nand_controller *nc;
+ u32 status;
+
+ nc = to_hsmc_nand_controller(chip->controller);
+
+ regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &status);
+
+ return status & ATMEL_HSMC_NFC_SR_RBEDGE(nand->activecs->rb.id);
+}
+
+static void atmel_hsmc_nand_select_chip(struct mtd_info *mtd, int cs)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct atmel_nand *nand = to_atmel_nand(chip);
+ struct atmel_hsmc_nand_controller *nc;
+
+ nc = to_hsmc_nand_controller(chip->controller);
+
+ atmel_nand_select_chip(mtd, cs);
+
+ if (!nand->activecs) {
+ regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CTRL,
+ ATMEL_HSMC_NFC_CTRL_DIS);
+ return;
+ }
+
+ if (nand->activecs->rb.type == ATMEL_NAND_NATIVE_RB)
+ chip->dev_ready = atmel_hsmc_nand_dev_ready;
+
+ regmap_update_bits(nc->base.smc, ATMEL_HSMC_NFC_CFG,
+ ATMEL_HSMC_NFC_CFG_PAGESIZE_MASK |
+ ATMEL_HSMC_NFC_CFG_SPARESIZE_MASK |
+ ATMEL_HSMC_NFC_CFG_RSPARE |
+ ATMEL_HSMC_NFC_CFG_WSPARE,
+ ATMEL_HSMC_NFC_CFG_PAGESIZE(mtd->writesize) |
+ ATMEL_HSMC_NFC_CFG_SPARESIZE(mtd->oobsize) |
+ ATMEL_HSMC_NFC_CFG_RSPARE);
+ regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CTRL,
+ ATMEL_HSMC_NFC_CTRL_EN);
+}
+
+static int atmel_nfc_exec_op(struct atmel_hsmc_nand_controller *nc, bool poll)
+{
+ u8 *addrs = nc->op.addrs;
+ unsigned int op = 0;
+ u32 addr, val;
+ int i, ret;
+
+ nc->op.wait = ATMEL_HSMC_NFC_SR_CMDDONE;
+
+ for (i = 0; i < nc->op.ncmds; i++)
+ op |= ATMEL_NFC_CMD(i, nc->op.cmds[i]);
+
+ if (nc->op.naddrs == ATMEL_NFC_MAX_ADDR_CYCLES)
+ regmap_write(nc->base.smc, ATMEL_HSMC_NFC_ADDR, *addrs++);
+
+ op |= ATMEL_NFC_CSID(nc->op.cs) |
+ ATMEL_NFC_ACYCLE(nc->op.naddrs);
+
+ if (nc->op.ncmds > 1)
+ op |= ATMEL_NFC_VCMD2;
+
+ addr = addrs[0] | (addrs[1] << 8) | (addrs[2] << 16) |
+ (addrs[3] << 24);
+
+ if (nc->op.data != ATMEL_NFC_NO_DATA) {
+ op |= ATMEL_NFC_DATAEN;
+ nc->op.wait |= ATMEL_HSMC_NFC_SR_XFRDONE;
+
+ if (nc->op.data == ATMEL_NFC_WRITE_DATA)
+ op |= ATMEL_NFC_NFCWR;
+ }
+
+ /* Clear all flags. */
+ regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &val);
+
+ /* Send the command. */
+ regmap_write(nc->io, op, addr);
+
+ ret = atmel_nfc_wait(nc, poll, 0);
+ if (ret)
+ dev_err(nc->base.dev,
+ "Failed to send NAND command (err = %d)!",
+ ret);
+
+ /* Reset the op state. */
+ memset(&nc->op, 0, sizeof(nc->op));
+
+ return ret;
+}
+
+static void atmel_hsmc_nand_cmd_ctrl(struct mtd_info *mtd, int dat,
+ unsigned int ctrl)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct atmel_nand *nand = to_atmel_nand(chip);
+ struct atmel_hsmc_nand_controller *nc;
+
+ nc = to_hsmc_nand_controller(chip->controller);
+
+ if (ctrl & NAND_ALE) {
+ if (nc->op.naddrs == ATMEL_NFC_MAX_ADDR_CYCLES)
+ return;
+
+ nc->op.addrs[nc->op.naddrs++] = dat;
+ } else if (ctrl & NAND_CLE) {
+ if (nc->op.ncmds > 1)
+ return;
+
+ nc->op.cmds[nc->op.ncmds++] = dat;
+ }
+
+ if (dat == NAND_CMD_NONE) {
+ nc->op.cs = nand->activecs->id;
+ atmel_nfc_exec_op(nc, true);
+ }
+}
+
+static void atmel_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
+ unsigned int ctrl)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct atmel_nand *nand = to_atmel_nand(chip);
+ struct atmel_nand_controller *nc;
+
+ nc = to_nand_controller(chip->controller);
+
+ if ((ctrl & NAND_CTRL_CHANGE) && nand->activecs->csgpio) {
+ if (ctrl & NAND_NCE)
+ gpiod_set_value(nand->activecs->csgpio, 0);
+ else
+ gpiod_set_value(nand->activecs->csgpio, 1);
+ }
+
+ if (ctrl & NAND_ALE)
+ writeb(cmd, nand->activecs->io.virt + nc->caps->ale_offs);
+ else if (ctrl & NAND_CLE)
+ writeb(cmd, nand->activecs->io.virt + nc->caps->cle_offs);
+}
+
+static void atmel_nfc_copy_to_sram(struct nand_chip *chip, const u8 *buf,
+ bool oob_required)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct atmel_hsmc_nand_controller *nc;
+ int ret = -EIO;
+
+ nc = to_hsmc_nand_controller(chip->controller);
+
+ if (nc->base.dmac)
+ ret = atmel_nand_dma_transfer(&nc->base, (void *)buf,
+ nc->sram.dma, mtd->writesize,
+ DMA_TO_DEVICE);
+
+ /* Falling back to CPU copy. */
+ if (ret)
+ memcpy_toio(nc->sram.virt, buf, mtd->writesize);
+
+ if (oob_required)
+ memcpy_toio(nc->sram.virt + mtd->writesize, chip->oob_poi,
+ mtd->oobsize);
+}
+
+static void atmel_nfc_copy_from_sram(struct nand_chip *chip, u8 *buf,
+ bool oob_required)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct atmel_hsmc_nand_controller *nc;
+ int ret = -EIO;
+
+ nc = to_hsmc_nand_controller(chip->controller);
+
+ if (nc->base.dmac)
+ ret = atmel_nand_dma_transfer(&nc->base, buf, nc->sram.dma,
+ mtd->writesize, DMA_FROM_DEVICE);
+
+ /* Falling back to CPU copy. */
+ if (ret)
+ memcpy_fromio(buf, nc->sram.virt, mtd->writesize);
+
+ if (oob_required)
+ memcpy_fromio(chip->oob_poi, nc->sram.virt + mtd->writesize,
+ mtd->oobsize);
+}
+
+static void atmel_nfc_set_op_addr(struct nand_chip *chip, int page, int column)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct atmel_hsmc_nand_controller *nc;
+
+ nc = to_hsmc_nand_controller(chip->controller);
+
+ if (column >= 0) {
+ nc->op.addrs[nc->op.naddrs++] = column;
+
+ /*
+ * 2 address cycles for the column offset on large page NANDs.
+ */
+ if (mtd->writesize > 512)
+ nc->op.addrs[nc->op.naddrs++] = column >> 8;
+ }
+
+ if (page >= 0) {
+ nc->op.addrs[nc->op.naddrs++] = page;
+ nc->op.addrs[nc->op.naddrs++] = page >> 8;
+
+ if (chip->options & NAND_ROW_ADDR_3)
+ nc->op.addrs[nc->op.naddrs++] = page >> 16;
+ }
+}
+
+static int atmel_nand_pmecc_enable(struct nand_chip *chip, int op, bool raw)
+{
+ struct atmel_nand *nand = to_atmel_nand(chip);
+ struct atmel_nand_controller *nc;
+ int ret;
+
+ nc = to_nand_controller(chip->controller);
+
+ if (raw)
+ return 0;
+
+ ret = atmel_pmecc_enable(nand->pmecc, op);
+ if (ret)
+ dev_err(nc->dev,
+ "Failed to enable ECC engine (err = %d)\n", ret);
+
+ return ret;
+}
+
+static void atmel_nand_pmecc_disable(struct nand_chip *chip, bool raw)
+{
+ struct atmel_nand *nand = to_atmel_nand(chip);
+
+ if (!raw)
+ atmel_pmecc_disable(nand->pmecc);
+}
+
+static int atmel_nand_pmecc_generate_eccbytes(struct nand_chip *chip, bool raw)
+{
+ struct atmel_nand *nand = to_atmel_nand(chip);
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct atmel_nand_controller *nc;
+ struct mtd_oob_region oobregion;
+ void *eccbuf;
+ int ret, i;
+
+ nc = to_nand_controller(chip->controller);
+
+ if (raw)
+ return 0;
+
+ ret = atmel_pmecc_wait_rdy(nand->pmecc);
+ if (ret) {
+ dev_err(nc->dev,
+ "Failed to transfer NAND page data (err = %d)\n",
+ ret);
+ return ret;
+ }
+
+ mtd_ooblayout_ecc(mtd, 0, &oobregion);
+ eccbuf = chip->oob_poi + oobregion.offset;
+
+ for (i = 0; i < chip->ecc.steps; i++) {
+ atmel_pmecc_get_generated_eccbytes(nand->pmecc, i,
+ eccbuf);
+ eccbuf += chip->ecc.bytes;
+ }
+
+ return 0;
+}
+
+static int atmel_nand_pmecc_correct_data(struct nand_chip *chip, void *buf,
+ bool raw)
+{
+ struct atmel_nand *nand = to_atmel_nand(chip);
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct atmel_nand_controller *nc;
+ struct mtd_oob_region oobregion;
+ int ret, i, max_bitflips = 0;
+ void *databuf, *eccbuf;
+
+ nc = to_nand_controller(chip->controller);
+
+ if (raw)
+ return 0;
+
+ ret = atmel_pmecc_wait_rdy(nand->pmecc);
+ if (ret) {
+ dev_err(nc->dev,
+ "Failed to read NAND page data (err = %d)\n",
+ ret);
+ return ret;
+ }
+
+ mtd_ooblayout_ecc(mtd, 0, &oobregion);
+ eccbuf = chip->oob_poi + oobregion.offset;
+ databuf = buf;
+
+ for (i = 0; i < chip->ecc.steps; i++) {
+ ret = atmel_pmecc_correct_sector(nand->pmecc, i, databuf,
+ eccbuf);
+ if (ret < 0 && !atmel_pmecc_correct_erased_chunks(nand->pmecc))
+ ret = nand_check_erased_ecc_chunk(databuf,
+ chip->ecc.size,
+ eccbuf,
+ chip->ecc.bytes,
+ NULL, 0,
+ chip->ecc.strength);
+
+ if (ret >= 0)
+ max_bitflips = max(ret, max_bitflips);
+ else
+ mtd->ecc_stats.failed++;
+
+ databuf += chip->ecc.size;
+ eccbuf += chip->ecc.bytes;
+ }
+
+ return max_bitflips;
+}
+
+static int atmel_nand_pmecc_write_pg(struct nand_chip *chip, const u8 *buf,
+ bool oob_required, int page, bool raw)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct atmel_nand *nand = to_atmel_nand(chip);
+ int ret;
+
+ nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+
+ ret = atmel_nand_pmecc_enable(chip, NAND_ECC_WRITE, raw);
+ if (ret)
+ return ret;
+
+ atmel_nand_write_buf(mtd, buf, mtd->writesize);
+
+ ret = atmel_nand_pmecc_generate_eccbytes(chip, raw);
+ if (ret) {
+ atmel_pmecc_disable(nand->pmecc);
+ return ret;
+ }
+
+ atmel_nand_pmecc_disable(chip, raw);
+
+ atmel_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ return nand_prog_page_end_op(chip);
+}
+
+static int atmel_nand_pmecc_write_page(struct mtd_info *mtd,
+ struct nand_chip *chip, const u8 *buf,
+ int oob_required, int page)
+{
+ return atmel_nand_pmecc_write_pg(chip, buf, oob_required, page, false);
+}
+
+static int atmel_nand_pmecc_write_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ const u8 *buf, int oob_required,
+ int page)
+{
+ return atmel_nand_pmecc_write_pg(chip, buf, oob_required, page, true);
+}
+
+static int atmel_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf,
+ bool oob_required, int page, bool raw)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int ret;
+
+ nand_read_page_op(chip, page, 0, NULL, 0);
+
+ ret = atmel_nand_pmecc_enable(chip, NAND_ECC_READ, raw);
+ if (ret)
+ return ret;
+
+ atmel_nand_read_buf(mtd, buf, mtd->writesize);
+ atmel_nand_read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ ret = atmel_nand_pmecc_correct_data(chip, buf, raw);
+
+ atmel_nand_pmecc_disable(chip, raw);
+
+ return ret;
+}
+
+static int atmel_nand_pmecc_read_page(struct mtd_info *mtd,
+ struct nand_chip *chip, u8 *buf,
+ int oob_required, int page)
+{
+ return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, false);
+}
+
+static int atmel_nand_pmecc_read_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, u8 *buf,
+ int oob_required, int page)
+{
+ return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, true);
+}
+
+static int atmel_hsmc_nand_pmecc_write_pg(struct nand_chip *chip,
+ const u8 *buf, bool oob_required,
+ int page, bool raw)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct atmel_nand *nand = to_atmel_nand(chip);
+ struct atmel_hsmc_nand_controller *nc;
+ int ret, status;
+
+ nc = to_hsmc_nand_controller(chip->controller);
+
+ atmel_nfc_copy_to_sram(chip, buf, false);
+
+ nc->op.cmds[0] = NAND_CMD_SEQIN;
+ nc->op.ncmds = 1;
+ atmel_nfc_set_op_addr(chip, page, 0x0);
+ nc->op.cs = nand->activecs->id;
+ nc->op.data = ATMEL_NFC_WRITE_DATA;
+
+ ret = atmel_nand_pmecc_enable(chip, NAND_ECC_WRITE, raw);
+ if (ret)
+ return ret;
+
+ ret = atmel_nfc_exec_op(nc, false);
+ if (ret) {
+ atmel_nand_pmecc_disable(chip, raw);
+ dev_err(nc->base.dev,
+ "Failed to transfer NAND page data (err = %d)\n",
+ ret);
+ return ret;
+ }
+
+ ret = atmel_nand_pmecc_generate_eccbytes(chip, raw);
+
+ atmel_nand_pmecc_disable(chip, raw);
+
+ if (ret)
+ return ret;
+
+ atmel_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ nc->op.cmds[0] = NAND_CMD_PAGEPROG;
+ nc->op.ncmds = 1;
+ nc->op.cs = nand->activecs->id;
+ ret = atmel_nfc_exec_op(nc, false);
+ if (ret)
+ dev_err(nc->base.dev, "Failed to program NAND page (err = %d)\n",
+ ret);
+
+ status = chip->waitfunc(mtd, chip);
+ if (status & NAND_STATUS_FAIL)
+ return -EIO;
+
+ return ret;
+}
+
+static int atmel_hsmc_nand_pmecc_write_page(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ const u8 *buf, int oob_required,
+ int page)
+{
+ return atmel_hsmc_nand_pmecc_write_pg(chip, buf, oob_required, page,
+ false);
+}
+
+static int atmel_hsmc_nand_pmecc_write_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ const u8 *buf,
+ int oob_required, int page)
+{
+ return atmel_hsmc_nand_pmecc_write_pg(chip, buf, oob_required, page,
+ true);
+}
+
+static int atmel_hsmc_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf,
+ bool oob_required, int page,
+ bool raw)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct atmel_nand *nand = to_atmel_nand(chip);
+ struct atmel_hsmc_nand_controller *nc;
+ int ret;
+
+ nc = to_hsmc_nand_controller(chip->controller);
+
+ /*
+ * Optimized read page accessors only work when the NAND R/B pin is
+ * connected to a native SoC R/B pin. If that's not the case, fallback
+ * to the non-optimized one.
+ */
+ if (nand->activecs->rb.type != ATMEL_NAND_NATIVE_RB) {
+ nand_read_page_op(chip, page, 0, NULL, 0);
+
+ return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page,
+ raw);
+ }
+
+ nc->op.cmds[nc->op.ncmds++] = NAND_CMD_READ0;
+
+ if (mtd->writesize > 512)
+ nc->op.cmds[nc->op.ncmds++] = NAND_CMD_READSTART;
+
+ atmel_nfc_set_op_addr(chip, page, 0x0);
+ nc->op.cs = nand->activecs->id;
+ nc->op.data = ATMEL_NFC_READ_DATA;
+
+ ret = atmel_nand_pmecc_enable(chip, NAND_ECC_READ, raw);
+ if (ret)
+ return ret;
+
+ ret = atmel_nfc_exec_op(nc, false);
+ if (ret) {
+ atmel_nand_pmecc_disable(chip, raw);
+ dev_err(nc->base.dev,
+ "Failed to load NAND page data (err = %d)\n",
+ ret);
+ return ret;
+ }
+
+ atmel_nfc_copy_from_sram(chip, buf, true);
+
+ ret = atmel_nand_pmecc_correct_data(chip, buf, raw);
+
+ atmel_nand_pmecc_disable(chip, raw);
+
+ return ret;
+}
+
+static int atmel_hsmc_nand_pmecc_read_page(struct mtd_info *mtd,
+ struct nand_chip *chip, u8 *buf,
+ int oob_required, int page)
+{
+ return atmel_hsmc_nand_pmecc_read_pg(chip, buf, oob_required, page,
+ false);
+}
+
+static int atmel_hsmc_nand_pmecc_read_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ u8 *buf, int oob_required,
+ int page)
+{
+ return atmel_hsmc_nand_pmecc_read_pg(chip, buf, oob_required, page,
+ true);
+}
+
+static int atmel_nand_pmecc_init(struct nand_chip *chip)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct atmel_nand *nand = to_atmel_nand(chip);
+ struct atmel_nand_controller *nc;
+ struct atmel_pmecc_user_req req;
+
+ nc = to_nand_controller(chip->controller);
+
+ if (!nc->pmecc) {
+ dev_err(nc->dev, "HW ECC not supported\n");
+ return -ENOTSUPP;
+ }
+
+ if (nc->caps->legacy_of_bindings) {
+ u32 val;
+
+ if (!of_property_read_u32(nc->dev->of_node, "atmel,pmecc-cap",
+ &val))
+ chip->ecc.strength = val;
+
+ if (!of_property_read_u32(nc->dev->of_node,
+ "atmel,pmecc-sector-size",
+ &val))
+ chip->ecc.size = val;
+ }
+
+ if (chip->ecc.options & NAND_ECC_MAXIMIZE)
+ req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH;
+ else if (chip->ecc.strength)
+ req.ecc.strength = chip->ecc.strength;
+ else if (chip->ecc_strength_ds)
+ req.ecc.strength = chip->ecc_strength_ds;
+ else
+ req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH;
+
+ if (chip->ecc.size)
+ req.ecc.sectorsize = chip->ecc.size;
+ else if (chip->ecc_step_ds)
+ req.ecc.sectorsize = chip->ecc_step_ds;
+ else
+ req.ecc.sectorsize = ATMEL_PMECC_SECTOR_SIZE_AUTO;
+
+ req.pagesize = mtd->writesize;
+ req.oobsize = mtd->oobsize;
+
+ if (mtd->writesize <= 512) {
+ req.ecc.bytes = 4;
+ req.ecc.ooboffset = 0;
+ } else {
+ req.ecc.bytes = mtd->oobsize - 2;
+ req.ecc.ooboffset = ATMEL_PMECC_OOBOFFSET_AUTO;
+ }
+
+ nand->pmecc = atmel_pmecc_create_user(nc->pmecc, &req);
+ if (IS_ERR(nand->pmecc))
+ return PTR_ERR(nand->pmecc);
+
+ chip->ecc.algo = NAND_ECC_BCH;
+ chip->ecc.size = req.ecc.sectorsize;
+ chip->ecc.bytes = req.ecc.bytes / req.ecc.nsectors;
+ chip->ecc.strength = req.ecc.strength;
+
+ chip->options |= NAND_NO_SUBPAGE_WRITE;
+
+ mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
+
+ return 0;
+}
+
+static int atmel_nand_ecc_init(struct atmel_nand *nand)
+{
+ struct nand_chip *chip = &nand->base;
+ struct atmel_nand_controller *nc;
+ int ret;
+
+ nc = to_nand_controller(chip->controller);
+
+ switch (chip->ecc.mode) {
+ case NAND_ECC_NONE:
+ case NAND_ECC_SOFT:
+ /*
+ * Nothing to do, the core will initialize everything for us.
+ */
+ break;
+
+ case NAND_ECC_HW:
+ ret = atmel_nand_pmecc_init(chip);
+ if (ret)
+ return ret;
+
+ chip->ecc.read_page = atmel_nand_pmecc_read_page;
+ chip->ecc.write_page = atmel_nand_pmecc_write_page;
+ chip->ecc.read_page_raw = atmel_nand_pmecc_read_page_raw;
+ chip->ecc.write_page_raw = atmel_nand_pmecc_write_page_raw;
+ break;
+
+ default:
+ /* Other modes are not supported. */
+ dev_err(nc->dev, "Unsupported ECC mode: %d\n",
+ chip->ecc.mode);
+ return -ENOTSUPP;
+ }
+
+ return 0;
+}
+
+static int atmel_hsmc_nand_ecc_init(struct atmel_nand *nand)
+{
+ struct nand_chip *chip = &nand->base;
+ int ret;
+
+ ret = atmel_nand_ecc_init(nand);
+ if (ret)
+ return ret;
+
+ if (chip->ecc.mode != NAND_ECC_HW)
+ return 0;
+
+ /* Adjust the ECC operations for the HSMC IP. */
+ chip->ecc.read_page = atmel_hsmc_nand_pmecc_read_page;
+ chip->ecc.write_page = atmel_hsmc_nand_pmecc_write_page;
+ chip->ecc.read_page_raw = atmel_hsmc_nand_pmecc_read_page_raw;
+ chip->ecc.write_page_raw = atmel_hsmc_nand_pmecc_write_page_raw;
+
+ return 0;
+}
+
+static int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand,
+ const struct nand_data_interface *conf,
+ struct atmel_smc_cs_conf *smcconf)
+{
+ u32 ncycles, totalcycles, timeps, mckperiodps;
+ struct atmel_nand_controller *nc;
+ int ret;
+
+ nc = to_nand_controller(nand->base.controller);
+
+ /* DDR interface not supported. */
+ if (conf->type != NAND_SDR_IFACE)
+ return -ENOTSUPP;
+
+ /*
+ * tRC < 30ns implies EDO mode. This controller does not support this
+ * mode.
+ */
+ if (conf->timings.sdr.tRC_min < 30000)
+ return -ENOTSUPP;
+
+ atmel_smc_cs_conf_init(smcconf);
+
+ mckperiodps = NSEC_PER_SEC / clk_get_rate(nc->mck);
+ mckperiodps *= 1000;
+
+ /*
+ * Set write pulse timing. This one is easy to extract:
+ *
+ * NWE_PULSE = tWP
+ */
+ ncycles = DIV_ROUND_UP(conf->timings.sdr.tWP_min, mckperiodps);
+ totalcycles = ncycles;
+ ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NWE_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ /*
+ * The write setup timing depends on the operation done on the NAND.
+ * All operations goes through the same data bus, but the operation
+ * type depends on the address we are writing to (ALE/CLE address
+ * lines).
+ * Since we have no way to differentiate the different operations at
+ * the SMC level, we must consider the worst case (the biggest setup
+ * time among all operation types):
+ *
+ * NWE_SETUP = max(tCLS, tCS, tALS, tDS) - NWE_PULSE
+ */
+ timeps = max3(conf->timings.sdr.tCLS_min, conf->timings.sdr.tCS_min,
+ conf->timings.sdr.tALS_min);
+ timeps = max(timeps, conf->timings.sdr.tDS_min);
+ ncycles = DIV_ROUND_UP(timeps, mckperiodps);
+ ncycles = ncycles > totalcycles ? ncycles - totalcycles : 0;
+ totalcycles += ncycles;
+ ret = atmel_smc_cs_conf_set_setup(smcconf, ATMEL_SMC_NWE_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ /*
+ * As for the write setup timing, the write hold timing depends on the
+ * operation done on the NAND:
+ *
+ * NWE_HOLD = max(tCLH, tCH, tALH, tDH, tWH)
+ */
+ timeps = max3(conf->timings.sdr.tCLH_min, conf->timings.sdr.tCH_min,
+ conf->timings.sdr.tALH_min);
+ timeps = max3(timeps, conf->timings.sdr.tDH_min,
+ conf->timings.sdr.tWH_min);
+ ncycles = DIV_ROUND_UP(timeps, mckperiodps);
+ totalcycles += ncycles;
+
+ /*
+ * The write cycle timing is directly matching tWC, but is also
+ * dependent on the other timings on the setup and hold timings we
+ * calculated earlier, which gives:
+ *
+ * NWE_CYCLE = max(tWC, NWE_SETUP + NWE_PULSE + NWE_HOLD)
+ */
+ ncycles = DIV_ROUND_UP(conf->timings.sdr.tWC_min, mckperiodps);
+ ncycles = max(totalcycles, ncycles);
+ ret = atmel_smc_cs_conf_set_cycle(smcconf, ATMEL_SMC_NWE_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ /*
+ * We don't want the CS line to be toggled between each byte/word
+ * transfer to the NAND. The only way to guarantee that is to have the
+ * NCS_{WR,RD}_{SETUP,HOLD} timings set to 0, which in turn means:
+ *
+ * NCS_WR_PULSE = NWE_CYCLE
+ */
+ ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NCS_WR_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ /*
+ * As for the write setup timing, the read hold timing depends on the
+ * operation done on the NAND:
+ *
+ * NRD_HOLD = max(tREH, tRHOH)
+ */
+ timeps = max(conf->timings.sdr.tREH_min, conf->timings.sdr.tRHOH_min);
+ ncycles = DIV_ROUND_UP(timeps, mckperiodps);
+ totalcycles = ncycles;
+
+ /*
+ * TDF = tRHZ - NRD_HOLD
+ */
+ ncycles = DIV_ROUND_UP(conf->timings.sdr.tRHZ_max, mckperiodps);
+ ncycles -= totalcycles;
+
+ /*
+ * In ONFI 4.0 specs, tRHZ has been increased to support EDO NANDs and
+ * we might end up with a config that does not fit in the TDF field.
+ * Just take the max value in this case and hope that the NAND is more
+ * tolerant than advertised.
+ */
+ if (ncycles > ATMEL_SMC_MODE_TDF_MAX)
+ ncycles = ATMEL_SMC_MODE_TDF_MAX;
+ else if (ncycles < ATMEL_SMC_MODE_TDF_MIN)
+ ncycles = ATMEL_SMC_MODE_TDF_MIN;
+
+ smcconf->mode |= ATMEL_SMC_MODE_TDF(ncycles) |
+ ATMEL_SMC_MODE_TDFMODE_OPTIMIZED;
+
+ /*
+ * Read pulse timing directly matches tRP:
+ *
+ * NRD_PULSE = tRP
+ */
+ ncycles = DIV_ROUND_UP(conf->timings.sdr.tRP_min, mckperiodps);
+ totalcycles += ncycles;
+ ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NRD_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ /*
+ * The write cycle timing is directly matching tWC, but is also
+ * dependent on the setup and hold timings we calculated earlier,
+ * which gives:
+ *
+ * NRD_CYCLE = max(tRC, NRD_PULSE + NRD_HOLD)
+ *
+ * NRD_SETUP is always 0.
+ */
+ ncycles = DIV_ROUND_UP(conf->timings.sdr.tRC_min, mckperiodps);
+ ncycles = max(totalcycles, ncycles);
+ ret = atmel_smc_cs_conf_set_cycle(smcconf, ATMEL_SMC_NRD_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ /*
+ * We don't want the CS line to be toggled between each byte/word
+ * transfer from the NAND. The only way to guarantee that is to have
+ * the NCS_{WR,RD}_{SETUP,HOLD} timings set to 0, which in turn means:
+ *
+ * NCS_RD_PULSE = NRD_CYCLE
+ */
+ ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NCS_RD_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ /* Txxx timings are directly matching tXXX ones. */
+ ncycles = DIV_ROUND_UP(conf->timings.sdr.tCLR_min, mckperiodps);
+ ret = atmel_smc_cs_conf_set_timing(smcconf,
+ ATMEL_HSMC_TIMINGS_TCLR_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ ncycles = DIV_ROUND_UP(conf->timings.sdr.tADL_min, mckperiodps);
+ ret = atmel_smc_cs_conf_set_timing(smcconf,
+ ATMEL_HSMC_TIMINGS_TADL_SHIFT,
+ ncycles);
+ /*
+ * Version 4 of the ONFI spec mandates that tADL be at least 400
+ * nanoseconds, but, depending on the master clock rate, 400 ns may not
+ * fit in the tADL field of the SMC reg. We need to relax the check and
+ * accept the -ERANGE return code.
+ *
+ * Note that previous versions of the ONFI spec had a lower tADL_min
+ * (100 or 200 ns). It's not clear why this timing constraint got
+ * increased but it seems most NANDs are fine with values lower than
+ * 400ns, so we should be safe.
+ */
+ if (ret && ret != -ERANGE)
+ return ret;
+
+ ncycles = DIV_ROUND_UP(conf->timings.sdr.tAR_min, mckperiodps);
+ ret = atmel_smc_cs_conf_set_timing(smcconf,
+ ATMEL_HSMC_TIMINGS_TAR_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ ncycles = DIV_ROUND_UP(conf->timings.sdr.tRR_min, mckperiodps);
+ ret = atmel_smc_cs_conf_set_timing(smcconf,
+ ATMEL_HSMC_TIMINGS_TRR_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ ncycles = DIV_ROUND_UP(conf->timings.sdr.tWB_max, mckperiodps);
+ ret = atmel_smc_cs_conf_set_timing(smcconf,
+ ATMEL_HSMC_TIMINGS_TWB_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ /* Attach the CS line to the NFC logic. */
+ smcconf->timings |= ATMEL_HSMC_TIMINGS_NFSEL;
+
+ /* Set the appropriate data bus width. */
+ if (nand->base.options & NAND_BUSWIDTH_16)
+ smcconf->mode |= ATMEL_SMC_MODE_DBW_16;
+
+ /* Operate in NRD/NWE READ/WRITEMODE. */
+ smcconf->mode |= ATMEL_SMC_MODE_READMODE_NRD |
+ ATMEL_SMC_MODE_WRITEMODE_NWE;
+
+ return 0;
+}
+
+static int atmel_smc_nand_setup_data_interface(struct atmel_nand *nand,
+ int csline,
+ const struct nand_data_interface *conf)
+{
+ struct atmel_nand_controller *nc;
+ struct atmel_smc_cs_conf smcconf;
+ struct atmel_nand_cs *cs;
+ int ret;
+
+ nc = to_nand_controller(nand->base.controller);
+
+ ret = atmel_smc_nand_prepare_smcconf(nand, conf, &smcconf);
+ if (ret)
+ return ret;
+
+ if (csline == NAND_DATA_IFACE_CHECK_ONLY)
+ return 0;
+
+ cs = &nand->cs[csline];
+ cs->smcconf = smcconf;
+ atmel_smc_cs_conf_apply(nc->smc, cs->id, &cs->smcconf);
+
+ return 0;
+}
+
+static int atmel_hsmc_nand_setup_data_interface(struct atmel_nand *nand,
+ int csline,
+ const struct nand_data_interface *conf)
+{
+ struct atmel_hsmc_nand_controller *nc;
+ struct atmel_smc_cs_conf smcconf;
+ struct atmel_nand_cs *cs;
+ int ret;
+
+ nc = to_hsmc_nand_controller(nand->base.controller);
+
+ ret = atmel_smc_nand_prepare_smcconf(nand, conf, &smcconf);
+ if (ret)
+ return ret;
+
+ if (csline == NAND_DATA_IFACE_CHECK_ONLY)
+ return 0;
+
+ cs = &nand->cs[csline];
+ cs->smcconf = smcconf;
+
+ if (cs->rb.type == ATMEL_NAND_NATIVE_RB)
+ cs->smcconf.timings |= ATMEL_HSMC_TIMINGS_RBNSEL(cs->rb.id);
+
+ atmel_hsmc_cs_conf_apply(nc->base.smc, nc->hsmc_layout, cs->id,
+ &cs->smcconf);
+
+ return 0;
+}
+
+static int atmel_nand_setup_data_interface(struct mtd_info *mtd, int csline,
+ const struct nand_data_interface *conf)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct atmel_nand *nand = to_atmel_nand(chip);
+ struct atmel_nand_controller *nc;
+
+ nc = to_nand_controller(nand->base.controller);
+
+ if (csline >= nand->numcs ||
+ (csline < 0 && csline != NAND_DATA_IFACE_CHECK_ONLY))
+ return -EINVAL;
+
+ return nc->caps->ops->setup_data_interface(nand, csline, conf);
+}
+
+static void atmel_nand_init(struct atmel_nand_controller *nc,
+ struct atmel_nand *nand)
+{
+ struct nand_chip *chip = &nand->base;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ mtd->dev.parent = nc->dev;
+ nand->base.controller = &nc->base;
+
+ chip->cmd_ctrl = atmel_nand_cmd_ctrl;
+ chip->read_byte = atmel_nand_read_byte;
+ chip->read_word = atmel_nand_read_word;
+ chip->write_byte = atmel_nand_write_byte;
+ chip->read_buf = atmel_nand_read_buf;
+ chip->write_buf = atmel_nand_write_buf;
+ chip->select_chip = atmel_nand_select_chip;
+
+ if (nc->mck && nc->caps->ops->setup_data_interface)
+ chip->setup_data_interface = atmel_nand_setup_data_interface;
+
+ /* Some NANDs require a longer delay than the default one (20us). */
+ chip->chip_delay = 40;
+
+ /*
+ * Use a bounce buffer when the buffer passed by the MTD user is not
+ * suitable for DMA.
+ */
+ if (nc->dmac)
+ chip->options |= NAND_USE_BOUNCE_BUFFER;
+
+ /* Default to HW ECC if pmecc is available. */
+ if (nc->pmecc)
+ chip->ecc.mode = NAND_ECC_HW;
+}
+
+static void atmel_smc_nand_init(struct atmel_nand_controller *nc,
+ struct atmel_nand *nand)
+{
+ struct nand_chip *chip = &nand->base;
+ struct atmel_smc_nand_controller *smc_nc;
+ int i;
+
+ atmel_nand_init(nc, nand);
+
+ smc_nc = to_smc_nand_controller(chip->controller);
+ if (!smc_nc->matrix)
+ return;
+
+ /* Attach the CS to the NAND Flash logic. */
+ for (i = 0; i < nand->numcs; i++)
+ regmap_update_bits(smc_nc->matrix, smc_nc->ebi_csa_offs,
+ BIT(nand->cs[i].id), BIT(nand->cs[i].id));
+}
+
+static void atmel_hsmc_nand_init(struct atmel_nand_controller *nc,
+ struct atmel_nand *nand)
+{
+ struct nand_chip *chip = &nand->base;
+
+ atmel_nand_init(nc, nand);
+
+ /* Overload some methods for the HSMC controller. */
+ chip->cmd_ctrl = atmel_hsmc_nand_cmd_ctrl;
+ chip->select_chip = atmel_hsmc_nand_select_chip;
+}
+
+static int atmel_nand_detect(struct atmel_nand *nand)
+{
+ struct nand_chip *chip = &nand->base;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct atmel_nand_controller *nc;
+ int ret;
+
+ nc = to_nand_controller(chip->controller);
+
+ ret = nand_scan_ident(mtd, nand->numcs, NULL);
+ if (ret)
+ dev_err(nc->dev, "nand_scan_ident() failed: %d\n", ret);
+
+ return ret;
+}
+
+static int atmel_nand_unregister(struct atmel_nand *nand)
+{
+ struct nand_chip *chip = &nand->base;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int ret;
+
+ ret = mtd_device_unregister(mtd);
+ if (ret)
+ return ret;
+
+ nand_cleanup(chip);
+ list_del(&nand->node);
+
+ return 0;
+}
+
+static int atmel_nand_register(struct atmel_nand *nand)
+{
+ struct nand_chip *chip = &nand->base;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct atmel_nand_controller *nc;
+ int ret;
+
+ nc = to_nand_controller(chip->controller);
+
+ if (nc->caps->legacy_of_bindings || !nc->dev->of_node) {
+ /*
+ * We keep the MTD name unchanged to avoid breaking platforms
+ * where the MTD cmdline parser is used and the bootloader
+ * has not been updated to use the new naming scheme.
+ */
+ mtd->name = "atmel_nand";
+ } else if (!mtd->name) {
+ /*
+ * If the new bindings are used and the bootloader has not been
+ * updated to pass a new mtdparts parameter on the cmdline, you
+ * should define the following property in your nand node:
+ *
+ * label = "atmel_nand";
+ *
+ * This way, mtd->name will be set by the core when
+ * nand_set_flash_node() is called.
+ */
+ mtd->name = devm_kasprintf(nc->dev, GFP_KERNEL,
+ "%s:nand.%d", dev_name(nc->dev),
+ nand->cs[0].id);
+ if (!mtd->name) {
+ dev_err(nc->dev, "Failed to allocate mtd->name\n");
+ return -ENOMEM;
+ }
+ }
+
+ ret = nand_scan_tail(mtd);
+ if (ret) {
+ dev_err(nc->dev, "nand_scan_tail() failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = mtd_device_register(mtd, NULL, 0);
+ if (ret) {
+ dev_err(nc->dev, "Failed to register mtd device: %d\n", ret);
+ nand_cleanup(chip);
+ return ret;
+ }
+
+ list_add_tail(&nand->node, &nc->chips);
+
+ return 0;
+}
+
+static struct atmel_nand *atmel_nand_create(struct atmel_nand_controller *nc,
+ struct device_node *np,
+ int reg_cells)
+{
+ struct atmel_nand *nand;
+ struct gpio_desc *gpio;
+ int numcs, ret, i;
+
+ numcs = of_property_count_elems_of_size(np, "reg",
+ reg_cells * sizeof(u32));
+ if (numcs < 1) {
+ dev_err(nc->dev, "Missing or invalid reg property\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ nand = devm_kzalloc(nc->dev,
+ sizeof(*nand) + (numcs * sizeof(*nand->cs)),
+ GFP_KERNEL);
+ if (!nand) {
+ dev_err(nc->dev, "Failed to allocate NAND object\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ nand->numcs = numcs;
+
+ gpio = devm_fwnode_get_index_gpiod_from_child(nc->dev, "det", 0,
+ &np->fwnode, GPIOD_IN,
+ "nand-det");
+ if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) {
+ dev_err(nc->dev,
+ "Failed to get detect gpio (err = %ld)\n",
+ PTR_ERR(gpio));
+ return ERR_CAST(gpio);
+ }
+
+ if (!IS_ERR(gpio))
+ nand->cdgpio = gpio;
+
+ for (i = 0; i < numcs; i++) {
+ struct resource res;
+ u32 val;
+
+ ret = of_address_to_resource(np, 0, &res);
+ if (ret) {
+ dev_err(nc->dev, "Invalid reg property (err = %d)\n",
+ ret);
+ return ERR_PTR(ret);
+ }
+
+ ret = of_property_read_u32_index(np, "reg", i * reg_cells,
+ &val);
+ if (ret) {
+ dev_err(nc->dev, "Invalid reg property (err = %d)\n",
+ ret);
+ return ERR_PTR(ret);
+ }
+
+ nand->cs[i].id = val;
+
+ nand->cs[i].io.dma = res.start;
+ nand->cs[i].io.virt = devm_ioremap_resource(nc->dev, &res);
+ if (IS_ERR(nand->cs[i].io.virt))
+ return ERR_CAST(nand->cs[i].io.virt);
+
+ if (!of_property_read_u32(np, "atmel,rb", &val)) {
+ if (val > ATMEL_NFC_MAX_RB_ID)
+ return ERR_PTR(-EINVAL);
+
+ nand->cs[i].rb.type = ATMEL_NAND_NATIVE_RB;
+ nand->cs[i].rb.id = val;
+ } else {
+ gpio = devm_fwnode_get_index_gpiod_from_child(nc->dev,
+ "rb", i, &np->fwnode,
+ GPIOD_IN, "nand-rb");
+ if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) {
+ dev_err(nc->dev,
+ "Failed to get R/B gpio (err = %ld)\n",
+ PTR_ERR(gpio));
+ return ERR_CAST(gpio);
+ }
+
+ if (!IS_ERR(gpio)) {
+ nand->cs[i].rb.type = ATMEL_NAND_GPIO_RB;
+ nand->cs[i].rb.gpio = gpio;
+ }
+ }
+
+ gpio = devm_fwnode_get_index_gpiod_from_child(nc->dev, "cs",
+ i, &np->fwnode,
+ GPIOD_OUT_HIGH,
+ "nand-cs");
+ if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) {
+ dev_err(nc->dev,
+ "Failed to get CS gpio (err = %ld)\n",
+ PTR_ERR(gpio));
+ return ERR_CAST(gpio);
+ }
+
+ if (!IS_ERR(gpio))
+ nand->cs[i].csgpio = gpio;
+ }
+
+ nand_set_flash_node(&nand->base, np);
+
+ return nand;
+}
+
+static int
+atmel_nand_controller_add_nand(struct atmel_nand_controller *nc,
+ struct atmel_nand *nand)
+{
+ int ret;
+
+ /* No card inserted, skip this NAND. */
+ if (nand->cdgpio && gpiod_get_value(nand->cdgpio)) {
+ dev_info(nc->dev, "No SmartMedia card inserted.\n");
+ return 0;
+ }
+
+ nc->caps->ops->nand_init(nc, nand);
+
+ ret = atmel_nand_detect(nand);
+ if (ret)
+ return ret;
+
+ ret = nc->caps->ops->ecc_init(nand);
+ if (ret)
+ return ret;
+
+ return atmel_nand_register(nand);
+}
+
+static int
+atmel_nand_controller_remove_nands(struct atmel_nand_controller *nc)
+{
+ struct atmel_nand *nand, *tmp;
+ int ret;
+
+ list_for_each_entry_safe(nand, tmp, &nc->chips, node) {
+ ret = atmel_nand_unregister(nand);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int
+atmel_nand_controller_legacy_add_nands(struct atmel_nand_controller *nc)
+{
+ struct device *dev = nc->dev;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct atmel_nand *nand;
+ struct gpio_desc *gpio;
+ struct resource *res;
+
+ /*
+ * Legacy bindings only allow connecting a single NAND with a unique CS
+ * line to the controller.
+ */
+ nand = devm_kzalloc(nc->dev, sizeof(*nand) + sizeof(*nand->cs),
+ GFP_KERNEL);
+ if (!nand)
+ return -ENOMEM;
+
+ nand->numcs = 1;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ nand->cs[0].io.virt = devm_ioremap_resource(dev, res);
+ if (IS_ERR(nand->cs[0].io.virt))
+ return PTR_ERR(nand->cs[0].io.virt);
+
+ nand->cs[0].io.dma = res->start;
+
+ /*
+ * The old driver was hardcoding the CS id to 3 for all sama5
+ * controllers. Since this id is only meaningful for the sama5
+ * controller we can safely assign this id to 3 no matter the
+ * controller.
+ * If one wants to connect a NAND to a different CS line, he will
+ * have to use the new bindings.
+ */
+ nand->cs[0].id = 3;
+
+ /* R/B GPIO. */
+ gpio = devm_gpiod_get_index_optional(dev, NULL, 0, GPIOD_IN);
+ if (IS_ERR(gpio)) {
+ dev_err(dev, "Failed to get R/B gpio (err = %ld)\n",
+ PTR_ERR(gpio));
+ return PTR_ERR(gpio);
+ }
+
+ if (gpio) {
+ nand->cs[0].rb.type = ATMEL_NAND_GPIO_RB;
+ nand->cs[0].rb.gpio = gpio;
+ }
+
+ /* CS GPIO. */
+ gpio = devm_gpiod_get_index_optional(dev, NULL, 1, GPIOD_OUT_HIGH);
+ if (IS_ERR(gpio)) {
+ dev_err(dev, "Failed to get CS gpio (err = %ld)\n",
+ PTR_ERR(gpio));
+ return PTR_ERR(gpio);
+ }
+
+ nand->cs[0].csgpio = gpio;
+
+ /* Card detect GPIO. */
+ gpio = devm_gpiod_get_index_optional(nc->dev, NULL, 2, GPIOD_IN);
+ if (IS_ERR(gpio)) {
+ dev_err(dev,
+ "Failed to get detect gpio (err = %ld)\n",
+ PTR_ERR(gpio));
+ return PTR_ERR(gpio);
+ }
+
+ nand->cdgpio = gpio;
+
+ nand_set_flash_node(&nand->base, nc->dev->of_node);
+
+ return atmel_nand_controller_add_nand(nc, nand);
+}
+
+static int atmel_nand_controller_add_nands(struct atmel_nand_controller *nc)
+{
+ struct device_node *np, *nand_np;
+ struct device *dev = nc->dev;
+ int ret, reg_cells;
+ u32 val;
+
+ /* We do not retrieve the SMC syscon when parsing old DTs. */
+ if (nc->caps->legacy_of_bindings)
+ return atmel_nand_controller_legacy_add_nands(nc);
+
+ np = dev->of_node;
+
+ ret = of_property_read_u32(np, "#address-cells", &val);
+ if (ret) {
+ dev_err(dev, "missing #address-cells property\n");
+ return ret;
+ }
+
+ reg_cells = val;
+
+ ret = of_property_read_u32(np, "#size-cells", &val);
+ if (ret) {
+ dev_err(dev, "missing #address-cells property\n");
+ return ret;
+ }
+
+ reg_cells += val;
+
+ for_each_child_of_node(np, nand_np) {
+ struct atmel_nand *nand;
+
+ nand = atmel_nand_create(nc, nand_np, reg_cells);
+ if (IS_ERR(nand)) {
+ ret = PTR_ERR(nand);
+ goto err;
+ }
+
+ ret = atmel_nand_controller_add_nand(nc, nand);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ atmel_nand_controller_remove_nands(nc);
+
+ return ret;
+}
+
+static void atmel_nand_controller_cleanup(struct atmel_nand_controller *nc)
+{
+ if (nc->dmac)
+ dma_release_channel(nc->dmac);
+
+ clk_put(nc->mck);
+}
+
+static const struct of_device_id atmel_matrix_of_ids[] = {
+ {
+ .compatible = "atmel,at91sam9260-matrix",
+ .data = (void *)AT91SAM9260_MATRIX_EBICSA,
+ },
+ {
+ .compatible = "atmel,at91sam9261-matrix",
+ .data = (void *)AT91SAM9261_MATRIX_EBICSA,
+ },
+ {
+ .compatible = "atmel,at91sam9263-matrix",
+ .data = (void *)AT91SAM9263_MATRIX_EBI0CSA,
+ },
+ {
+ .compatible = "atmel,at91sam9rl-matrix",
+ .data = (void *)AT91SAM9RL_MATRIX_EBICSA,
+ },
+ {
+ .compatible = "atmel,at91sam9g45-matrix",
+ .data = (void *)AT91SAM9G45_MATRIX_EBICSA,
+ },
+ {
+ .compatible = "atmel,at91sam9n12-matrix",
+ .data = (void *)AT91SAM9N12_MATRIX_EBICSA,
+ },
+ {
+ .compatible = "atmel,at91sam9x5-matrix",
+ .data = (void *)AT91SAM9X5_MATRIX_EBICSA,
+ },
+ { /* sentinel */ },
+};
+
+static int atmel_nand_controller_init(struct atmel_nand_controller *nc,
+ struct platform_device *pdev,
+ const struct atmel_nand_controller_caps *caps)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ int ret;
+
+ nand_hw_control_init(&nc->base);
+ INIT_LIST_HEAD(&nc->chips);
+ nc->dev = dev;
+ nc->caps = caps;
+
+ platform_set_drvdata(pdev, nc);
+
+ nc->pmecc = devm_atmel_pmecc_get(dev);
+ if (IS_ERR(nc->pmecc)) {
+ ret = PTR_ERR(nc->pmecc);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "Could not get PMECC object (err = %d)\n",
+ ret);
+ return ret;
+ }
+
+ if (nc->caps->has_dma) {
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+
+ nc->dmac = dma_request_channel(mask, NULL, NULL);
+ if (!nc->dmac)
+ dev_err(nc->dev, "Failed to request DMA channel\n");
+ }
+
+ /* We do not retrieve the SMC syscon when parsing old DTs. */
+ if (nc->caps->legacy_of_bindings)
+ return 0;
+
+ nc->mck = of_clk_get(dev->parent->of_node, 0);
+ if (IS_ERR(nc->mck)) {
+ dev_err(dev, "Failed to retrieve MCK clk\n");
+ return PTR_ERR(nc->mck);
+ }
+
+ np = of_parse_phandle(dev->parent->of_node, "atmel,smc", 0);
+ if (!np) {
+ dev_err(dev, "Missing or invalid atmel,smc property\n");
+ return -EINVAL;
+ }
+
+ nc->smc = syscon_node_to_regmap(np);
+ of_node_put(np);
+ if (IS_ERR(nc->smc)) {
+ ret = PTR_ERR(nc->smc);
+ dev_err(dev, "Could not get SMC regmap (err = %d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int
+atmel_smc_nand_controller_init(struct atmel_smc_nand_controller *nc)
+{
+ struct device *dev = nc->base.dev;
+ const struct of_device_id *match;
+ struct device_node *np;
+ int ret;
+
+ /* We do not retrieve the matrix syscon when parsing old DTs. */
+ if (nc->base.caps->legacy_of_bindings)
+ return 0;
+
+ np = of_parse_phandle(dev->parent->of_node, "atmel,matrix", 0);
+ if (!np)
+ return 0;
+
+ match = of_match_node(atmel_matrix_of_ids, np);
+ if (!match) {
+ of_node_put(np);
+ return 0;
+ }
+
+ nc->matrix = syscon_node_to_regmap(np);
+ of_node_put(np);
+ if (IS_ERR(nc->matrix)) {
+ ret = PTR_ERR(nc->matrix);
+ dev_err(dev, "Could not get Matrix regmap (err = %d)\n", ret);
+ return ret;
+ }
+
+ nc->ebi_csa_offs = (unsigned int)match->data;
+
+ /*
+ * The at91sam9263 has 2 EBIs, if the NAND controller is under EBI1
+ * add 4 to ->ebi_csa_offs.
+ */
+ if (of_device_is_compatible(dev->parent->of_node,
+ "atmel,at91sam9263-ebi1"))
+ nc->ebi_csa_offs += 4;
+
+ return 0;
+}
+
+static int
+atmel_hsmc_nand_controller_legacy_init(struct atmel_hsmc_nand_controller *nc)
+{
+ struct regmap_config regmap_conf = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ };
+
+ struct device *dev = nc->base.dev;
+ struct device_node *nand_np, *nfc_np;
+ void __iomem *iomem;
+ struct resource res;
+ int ret;
+
+ nand_np = dev->of_node;
+ nfc_np = of_find_compatible_node(dev->of_node, NULL,
+ "atmel,sama5d3-nfc");
+
+ nc->clk = of_clk_get(nfc_np, 0);
+ if (IS_ERR(nc->clk)) {
+ ret = PTR_ERR(nc->clk);
+ dev_err(dev, "Failed to retrieve HSMC clock (err = %d)\n",
+ ret);
+ goto out;
+ }
+
+ ret = clk_prepare_enable(nc->clk);
+ if (ret) {
+ dev_err(dev, "Failed to enable the HSMC clock (err = %d)\n",
+ ret);
+ goto out;
+ }
+
+ nc->irq = of_irq_get(nand_np, 0);
+ if (nc->irq <= 0) {
+ ret = nc->irq ?: -ENXIO;
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get IRQ number (err = %d)\n",
+ ret);
+ goto out;
+ }
+
+ ret = of_address_to_resource(nfc_np, 0, &res);
+ if (ret) {
+ dev_err(dev, "Invalid or missing NFC IO resource (err = %d)\n",
+ ret);
+ goto out;
+ }
+
+ iomem = devm_ioremap_resource(dev, &res);
+ if (IS_ERR(iomem)) {
+ ret = PTR_ERR(iomem);
+ goto out;
+ }
+
+ regmap_conf.name = "nfc-io";
+ regmap_conf.max_register = resource_size(&res) - 4;
+ nc->io = devm_regmap_init_mmio(dev, iomem, &regmap_conf);
+ if (IS_ERR(nc->io)) {
+ ret = PTR_ERR(nc->io);
+ dev_err(dev, "Could not create NFC IO regmap (err = %d)\n",
+ ret);
+ goto out;
+ }
+
+ ret = of_address_to_resource(nfc_np, 1, &res);
+ if (ret) {
+ dev_err(dev, "Invalid or missing HSMC resource (err = %d)\n",
+ ret);
+ goto out;
+ }
+
+ iomem = devm_ioremap_resource(dev, &res);
+ if (IS_ERR(iomem)) {
+ ret = PTR_ERR(iomem);
+ goto out;
+ }
+
+ regmap_conf.name = "smc";
+ regmap_conf.max_register = resource_size(&res) - 4;
+ nc->base.smc = devm_regmap_init_mmio(dev, iomem, &regmap_conf);
+ if (IS_ERR(nc->base.smc)) {
+ ret = PTR_ERR(nc->base.smc);
+ dev_err(dev, "Could not create NFC IO regmap (err = %d)\n",
+ ret);
+ goto out;
+ }
+
+ ret = of_address_to_resource(nfc_np, 2, &res);
+ if (ret) {
+ dev_err(dev, "Invalid or missing SRAM resource (err = %d)\n",
+ ret);
+ goto out;
+ }
+
+ nc->sram.virt = devm_ioremap_resource(dev, &res);
+ if (IS_ERR(nc->sram.virt)) {
+ ret = PTR_ERR(nc->sram.virt);
+ goto out;
+ }
+
+ nc->sram.dma = res.start;
+
+out:
+ of_node_put(nfc_np);
+
+ return ret;
+}
+
+static int
+atmel_hsmc_nand_controller_init(struct atmel_hsmc_nand_controller *nc)
+{
+ struct device *dev = nc->base.dev;
+ struct device_node *np;
+ int ret;
+
+ np = of_parse_phandle(dev->parent->of_node, "atmel,smc", 0);
+ if (!np) {
+ dev_err(dev, "Missing or invalid atmel,smc property\n");
+ return -EINVAL;
+ }
+
+ nc->hsmc_layout = atmel_hsmc_get_reg_layout(np);
+
+ nc->irq = of_irq_get(np, 0);
+ of_node_put(np);
+ if (nc->irq <= 0) {
+ ret = nc->irq ?: -ENXIO;
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get IRQ number (err = %d)\n",
+ ret);
+ return ret;
+ }
+
+ np = of_parse_phandle(dev->of_node, "atmel,nfc-io", 0);
+ if (!np) {
+ dev_err(dev, "Missing or invalid atmel,nfc-io property\n");
+ return -EINVAL;
+ }
+
+ nc->io = syscon_node_to_regmap(np);
+ of_node_put(np);
+ if (IS_ERR(nc->io)) {
+ ret = PTR_ERR(nc->io);
+ dev_err(dev, "Could not get NFC IO regmap (err = %d)\n", ret);
+ return ret;
+ }
+
+ nc->sram.pool = of_gen_pool_get(nc->base.dev->of_node,
+ "atmel,nfc-sram", 0);
+ if (!nc->sram.pool) {
+ dev_err(nc->base.dev, "Missing SRAM\n");
+ return -ENOMEM;
+ }
+
+ nc->sram.virt = gen_pool_dma_alloc(nc->sram.pool,
+ ATMEL_NFC_SRAM_SIZE,
+ &nc->sram.dma);
+ if (!nc->sram.virt) {
+ dev_err(nc->base.dev,
+ "Could not allocate memory from the NFC SRAM pool\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int
+atmel_hsmc_nand_controller_remove(struct atmel_nand_controller *nc)
+{
+ struct atmel_hsmc_nand_controller *hsmc_nc;
+ int ret;
+
+ ret = atmel_nand_controller_remove_nands(nc);
+ if (ret)
+ return ret;
+
+ hsmc_nc = container_of(nc, struct atmel_hsmc_nand_controller, base);
+ if (hsmc_nc->sram.pool)
+ gen_pool_free(hsmc_nc->sram.pool,
+ (unsigned long)hsmc_nc->sram.virt,
+ ATMEL_NFC_SRAM_SIZE);
+
+ if (hsmc_nc->clk) {
+ clk_disable_unprepare(hsmc_nc->clk);
+ clk_put(hsmc_nc->clk);
+ }
+
+ atmel_nand_controller_cleanup(nc);
+
+ return 0;
+}
+
+static int atmel_hsmc_nand_controller_probe(struct platform_device *pdev,
+ const struct atmel_nand_controller_caps *caps)
+{
+ struct device *dev = &pdev->dev;
+ struct atmel_hsmc_nand_controller *nc;
+ int ret;
+
+ nc = devm_kzalloc(dev, sizeof(*nc), GFP_KERNEL);
+ if (!nc)
+ return -ENOMEM;
+
+ ret = atmel_nand_controller_init(&nc->base, pdev, caps);
+ if (ret)
+ return ret;
+
+ if (caps->legacy_of_bindings)
+ ret = atmel_hsmc_nand_controller_legacy_init(nc);
+ else
+ ret = atmel_hsmc_nand_controller_init(nc);
+
+ if (ret)
+ return ret;
+
+ /* Make sure all irqs are masked before registering our IRQ handler. */
+ regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, 0xffffffff);
+ ret = devm_request_irq(dev, nc->irq, atmel_nfc_interrupt,
+ IRQF_SHARED, "nfc", nc);
+ if (ret) {
+ dev_err(dev,
+ "Could not get register NFC interrupt handler (err = %d)\n",
+ ret);
+ goto err;
+ }
+
+ /* Initial NFC configuration. */
+ regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CFG,
+ ATMEL_HSMC_NFC_CFG_DTO_MAX);
+
+ ret = atmel_nand_controller_add_nands(&nc->base);
+ if (ret)
+ goto err;
+
+ return 0;
+
+err:
+ atmel_hsmc_nand_controller_remove(&nc->base);
+
+ return ret;
+}
+
+static const struct atmel_nand_controller_ops atmel_hsmc_nc_ops = {
+ .probe = atmel_hsmc_nand_controller_probe,
+ .remove = atmel_hsmc_nand_controller_remove,
+ .ecc_init = atmel_hsmc_nand_ecc_init,
+ .nand_init = atmel_hsmc_nand_init,
+ .setup_data_interface = atmel_hsmc_nand_setup_data_interface,
+};
+
+static const struct atmel_nand_controller_caps atmel_sama5_nc_caps = {
+ .has_dma = true,
+ .ale_offs = BIT(21),
+ .cle_offs = BIT(22),
+ .ops = &atmel_hsmc_nc_ops,
+};
+
+/* Only used to parse old bindings. */
+static const struct atmel_nand_controller_caps atmel_sama5_nand_caps = {
+ .has_dma = true,
+ .ale_offs = BIT(21),
+ .cle_offs = BIT(22),
+ .ops = &atmel_hsmc_nc_ops,
+ .legacy_of_bindings = true,
+};
+
+static int atmel_smc_nand_controller_probe(struct platform_device *pdev,
+ const struct atmel_nand_controller_caps *caps)
+{
+ struct device *dev = &pdev->dev;
+ struct atmel_smc_nand_controller *nc;
+ int ret;
+
+ nc = devm_kzalloc(dev, sizeof(*nc), GFP_KERNEL);
+ if (!nc)
+ return -ENOMEM;
+
+ ret = atmel_nand_controller_init(&nc->base, pdev, caps);
+ if (ret)
+ return ret;
+
+ ret = atmel_smc_nand_controller_init(nc);
+ if (ret)
+ return ret;
+
+ return atmel_nand_controller_add_nands(&nc->base);
+}
+
+static int
+atmel_smc_nand_controller_remove(struct atmel_nand_controller *nc)
+{
+ int ret;
+
+ ret = atmel_nand_controller_remove_nands(nc);
+ if (ret)
+ return ret;
+
+ atmel_nand_controller_cleanup(nc);
+
+ return 0;
+}
+
+/*
+ * The SMC reg layout of at91rm9200 is completely different which prevents us
+ * from re-using atmel_smc_nand_setup_data_interface() for the
+ * ->setup_data_interface() hook.
+ * At this point, there's no support for the at91rm9200 SMC IP, so we leave
+ * ->setup_data_interface() unassigned.
+ */
+static const struct atmel_nand_controller_ops at91rm9200_nc_ops = {
+ .probe = atmel_smc_nand_controller_probe,
+ .remove = atmel_smc_nand_controller_remove,
+ .ecc_init = atmel_nand_ecc_init,
+ .nand_init = atmel_smc_nand_init,
+};
+
+static const struct atmel_nand_controller_caps atmel_rm9200_nc_caps = {
+ .ale_offs = BIT(21),
+ .cle_offs = BIT(22),
+ .ops = &at91rm9200_nc_ops,
+};
+
+static const struct atmel_nand_controller_ops atmel_smc_nc_ops = {
+ .probe = atmel_smc_nand_controller_probe,
+ .remove = atmel_smc_nand_controller_remove,
+ .ecc_init = atmel_nand_ecc_init,
+ .nand_init = atmel_smc_nand_init,
+ .setup_data_interface = atmel_smc_nand_setup_data_interface,
+};
+
+static const struct atmel_nand_controller_caps atmel_sam9260_nc_caps = {
+ .ale_offs = BIT(21),
+ .cle_offs = BIT(22),
+ .ops = &atmel_smc_nc_ops,
+};
+
+static const struct atmel_nand_controller_caps atmel_sam9261_nc_caps = {
+ .ale_offs = BIT(22),
+ .cle_offs = BIT(21),
+ .ops = &atmel_smc_nc_ops,
+};
+
+static const struct atmel_nand_controller_caps atmel_sam9g45_nc_caps = {
+ .has_dma = true,
+ .ale_offs = BIT(21),
+ .cle_offs = BIT(22),
+ .ops = &atmel_smc_nc_ops,
+};
+
+/* Only used to parse old bindings. */
+static const struct atmel_nand_controller_caps atmel_rm9200_nand_caps = {
+ .ale_offs = BIT(21),
+ .cle_offs = BIT(22),
+ .ops = &atmel_smc_nc_ops,
+ .legacy_of_bindings = true,
+};
+
+static const struct atmel_nand_controller_caps atmel_sam9261_nand_caps = {
+ .ale_offs = BIT(22),
+ .cle_offs = BIT(21),
+ .ops = &atmel_smc_nc_ops,
+ .legacy_of_bindings = true,
+};
+
+static const struct atmel_nand_controller_caps atmel_sam9g45_nand_caps = {
+ .has_dma = true,
+ .ale_offs = BIT(21),
+ .cle_offs = BIT(22),
+ .ops = &atmel_smc_nc_ops,
+ .legacy_of_bindings = true,
+};
+
+static const struct of_device_id atmel_nand_controller_of_ids[] = {
+ {
+ .compatible = "atmel,at91rm9200-nand-controller",
+ .data = &atmel_rm9200_nc_caps,
+ },
+ {
+ .compatible = "atmel,at91sam9260-nand-controller",
+ .data = &atmel_sam9260_nc_caps,
+ },
+ {
+ .compatible = "atmel,at91sam9261-nand-controller",
+ .data = &atmel_sam9261_nc_caps,
+ },
+ {
+ .compatible = "atmel,at91sam9g45-nand-controller",
+ .data = &atmel_sam9g45_nc_caps,
+ },
+ {
+ .compatible = "atmel,sama5d3-nand-controller",
+ .data = &atmel_sama5_nc_caps,
+ },
+ /* Support for old/deprecated bindings: */
+ {
+ .compatible = "atmel,at91rm9200-nand",
+ .data = &atmel_rm9200_nand_caps,
+ },
+ {
+ .compatible = "atmel,sama5d4-nand",
+ .data = &atmel_rm9200_nand_caps,
+ },
+ {
+ .compatible = "atmel,sama5d2-nand",
+ .data = &atmel_rm9200_nand_caps,
+ },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, atmel_nand_controller_of_ids);
+
+static int atmel_nand_controller_probe(struct platform_device *pdev)
+{
+ const struct atmel_nand_controller_caps *caps;
+
+ if (pdev->id_entry)
+ caps = (void *)pdev->id_entry->driver_data;
+ else
+ caps = of_device_get_match_data(&pdev->dev);
+
+ if (!caps) {
+ dev_err(&pdev->dev, "Could not retrieve NFC caps\n");
+ return -EINVAL;
+ }
+
+ if (caps->legacy_of_bindings) {
+ u32 ale_offs = 21;
+
+ /*
+ * If we are parsing legacy DT props and the DT contains a
+ * valid NFC node, forward the request to the sama5 logic.
+ */
+ if (of_find_compatible_node(pdev->dev.of_node, NULL,
+ "atmel,sama5d3-nfc"))
+ caps = &atmel_sama5_nand_caps;
+
+ /*
+ * Even if the compatible says we are dealing with an
+ * at91rm9200 controller, the atmel,nand-has-dma specify that
+ * this controller supports DMA, which means we are in fact
+ * dealing with an at91sam9g45+ controller.
+ */
+ if (!caps->has_dma &&
+ of_property_read_bool(pdev->dev.of_node,
+ "atmel,nand-has-dma"))
+ caps = &atmel_sam9g45_nand_caps;
+
+ /*
+ * All SoCs except the at91sam9261 are assigning ALE to A21 and
+ * CLE to A22. If atmel,nand-addr-offset != 21 this means we're
+ * actually dealing with an at91sam9261 controller.
+ */
+ of_property_read_u32(pdev->dev.of_node,
+ "atmel,nand-addr-offset", &ale_offs);
+ if (ale_offs != 21)
+ caps = &atmel_sam9261_nand_caps;
+ }
+
+ return caps->ops->probe(pdev, caps);
+}
+
+static int atmel_nand_controller_remove(struct platform_device *pdev)
+{
+ struct atmel_nand_controller *nc = platform_get_drvdata(pdev);
+
+ return nc->caps->ops->remove(nc);
+}
+
+static __maybe_unused int atmel_nand_controller_resume(struct device *dev)
+{
+ struct atmel_nand_controller *nc = dev_get_drvdata(dev);
+ struct atmel_nand *nand;
+
+ if (nc->pmecc)
+ atmel_pmecc_reset(nc->pmecc);
+
+ list_for_each_entry(nand, &nc->chips, node) {
+ int i;
+
+ for (i = 0; i < nand->numcs; i++)
+ nand_reset(&nand->base, i);
+ }
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(atmel_nand_controller_pm_ops, NULL,
+ atmel_nand_controller_resume);
+
+static struct platform_driver atmel_nand_controller_driver = {
+ .driver = {
+ .name = "atmel-nand-controller",
+ .of_match_table = of_match_ptr(atmel_nand_controller_of_ids),
+ .pm = &atmel_nand_controller_pm_ops,
+ },
+ .probe = atmel_nand_controller_probe,
+ .remove = atmel_nand_controller_remove,
+};
+module_platform_driver(atmel_nand_controller_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
+MODULE_DESCRIPTION("NAND Flash Controller driver for Atmel SoCs");
+MODULE_ALIAS("platform:atmel-nand-controller");
diff --git a/drivers/mtd/nand/raw/atmel/pmecc.c b/drivers/mtd/nand/raw/atmel/pmecc.c
new file mode 100644
index 000000000000..555a74e15269
--- /dev/null
+++ b/drivers/mtd/nand/raw/atmel/pmecc.c
@@ -0,0 +1,1012 @@
+/*
+ * Copyright 2017 ATMEL
+ * Copyright 2017 Free Electrons
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
+ *
+ * Derived from the atmel_nand.c driver which contained the following
+ * copyrights:
+ *
+ * Copyright 2003 Rick Bronson
+ *
+ * Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8)
+ * Copyright 2001 Thomas Gleixner (gleixner@autronix.de)
+ *
+ * Derived from drivers/mtd/spia.c (removed in v3.8)
+ * Copyright 2000 Steven J. Hill (sjhill@cotw.com)
+ *
+ * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263
+ * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright 2007
+ *
+ * Derived from Das U-Boot source code
+ * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c)
+ * Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas
+ *
+ * Add Programmable Multibit ECC support for various AT91 SoC
+ * Copyright 2012 ATMEL, Hong Xu
+ *
+ * Add Nand Flash Controller support for SAMA5 SoC
+ * Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.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 PMECC is an hardware assisted BCH engine, which means part of the
+ * ECC algorithm is left to the software. The hardware/software repartition
+ * is explained in the "PMECC Controller Functional Description" chapter in
+ * Atmel datasheets, and some of the functions in this file are directly
+ * implementing the algorithms described in the "Software Implementation"
+ * sub-section.
+ *
+ * TODO: it seems that the software BCH implementation in lib/bch.c is already
+ * providing some of the logic we are implementing here. It would be smart
+ * to expose the needed lib/bch.c helpers/functions and re-use them here.
+ */
+
+#include <linux/genalloc.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "pmecc.h"
+
+/* Galois field dimension */
+#define PMECC_GF_DIMENSION_13 13
+#define PMECC_GF_DIMENSION_14 14
+
+/* Primitive Polynomial used by PMECC */
+#define PMECC_GF_13_PRIMITIVE_POLY 0x201b
+#define PMECC_GF_14_PRIMITIVE_POLY 0x4443
+
+#define PMECC_LOOKUP_TABLE_SIZE_512 0x2000
+#define PMECC_LOOKUP_TABLE_SIZE_1024 0x4000
+
+/* Time out value for reading PMECC status register */
+#define PMECC_MAX_TIMEOUT_MS 100
+
+/* PMECC Register Definitions */
+#define ATMEL_PMECC_CFG 0x0
+#define PMECC_CFG_BCH_STRENGTH(x) (x)
+#define PMECC_CFG_BCH_STRENGTH_MASK GENMASK(2, 0)
+#define PMECC_CFG_SECTOR512 (0 << 4)
+#define PMECC_CFG_SECTOR1024 (1 << 4)
+#define PMECC_CFG_NSECTORS(x) ((fls(x) - 1) << 8)
+#define PMECC_CFG_READ_OP (0 << 12)
+#define PMECC_CFG_WRITE_OP (1 << 12)
+#define PMECC_CFG_SPARE_ENABLE BIT(16)
+#define PMECC_CFG_AUTO_ENABLE BIT(20)
+
+#define ATMEL_PMECC_SAREA 0x4
+#define ATMEL_PMECC_SADDR 0x8
+#define ATMEL_PMECC_EADDR 0xc
+
+#define ATMEL_PMECC_CLK 0x10
+#define PMECC_CLK_133MHZ (2 << 0)
+
+#define ATMEL_PMECC_CTRL 0x14
+#define PMECC_CTRL_RST BIT(0)
+#define PMECC_CTRL_DATA BIT(1)
+#define PMECC_CTRL_USER BIT(2)
+#define PMECC_CTRL_ENABLE BIT(4)
+#define PMECC_CTRL_DISABLE BIT(5)
+
+#define ATMEL_PMECC_SR 0x18
+#define PMECC_SR_BUSY BIT(0)
+#define PMECC_SR_ENABLE BIT(4)
+
+#define ATMEL_PMECC_IER 0x1c
+#define ATMEL_PMECC_IDR 0x20
+#define ATMEL_PMECC_IMR 0x24
+#define ATMEL_PMECC_ISR 0x28
+#define PMECC_ERROR_INT BIT(0)
+
+#define ATMEL_PMECC_ECC(sector, n) \
+ ((((sector) + 1) * 0x40) + (n))
+
+#define ATMEL_PMECC_REM(sector, n) \
+ ((((sector) + 1) * 0x40) + ((n) * 4) + 0x200)
+
+/* PMERRLOC Register Definitions */
+#define ATMEL_PMERRLOC_ELCFG 0x0
+#define PMERRLOC_ELCFG_SECTOR_512 (0 << 0)
+#define PMERRLOC_ELCFG_SECTOR_1024 (1 << 0)
+#define PMERRLOC_ELCFG_NUM_ERRORS(n) ((n) << 16)
+
+#define ATMEL_PMERRLOC_ELPRIM 0x4
+#define ATMEL_PMERRLOC_ELEN 0x8
+#define ATMEL_PMERRLOC_ELDIS 0xc
+#define PMERRLOC_DISABLE BIT(0)
+
+#define ATMEL_PMERRLOC_ELSR 0x10
+#define PMERRLOC_ELSR_BUSY BIT(0)
+
+#define ATMEL_PMERRLOC_ELIER 0x14
+#define ATMEL_PMERRLOC_ELIDR 0x18
+#define ATMEL_PMERRLOC_ELIMR 0x1c
+#define ATMEL_PMERRLOC_ELISR 0x20
+#define PMERRLOC_ERR_NUM_MASK GENMASK(12, 8)
+#define PMERRLOC_CALC_DONE BIT(0)
+
+#define ATMEL_PMERRLOC_SIGMA(x) (((x) * 0x4) + 0x28)
+
+#define ATMEL_PMERRLOC_EL(offs, x) (((x) * 0x4) + (offs))
+
+struct atmel_pmecc_gf_tables {
+ u16 *alpha_to;
+ u16 *index_of;
+};
+
+struct atmel_pmecc_caps {
+ const int *strengths;
+ int nstrengths;
+ int el_offset;
+ bool correct_erased_chunks;
+};
+
+struct atmel_pmecc {
+ struct device *dev;
+ const struct atmel_pmecc_caps *caps;
+
+ struct {
+ void __iomem *base;
+ void __iomem *errloc;
+ } regs;
+
+ struct mutex lock;
+};
+
+struct atmel_pmecc_user_conf_cache {
+ u32 cfg;
+ u32 sarea;
+ u32 saddr;
+ u32 eaddr;
+};
+
+struct atmel_pmecc_user {
+ struct atmel_pmecc_user_conf_cache cache;
+ struct atmel_pmecc *pmecc;
+ const struct atmel_pmecc_gf_tables *gf_tables;
+ int eccbytes;
+ s16 *partial_syn;
+ s16 *si;
+ s16 *lmu;
+ s16 *smu;
+ s32 *mu;
+ s32 *dmu;
+ s32 *delta;
+ u32 isr;
+};
+
+static DEFINE_MUTEX(pmecc_gf_tables_lock);
+static const struct atmel_pmecc_gf_tables *pmecc_gf_tables_512;
+static const struct atmel_pmecc_gf_tables *pmecc_gf_tables_1024;
+
+static inline int deg(unsigned int poly)
+{
+ /* polynomial degree is the most-significant bit index */
+ return fls(poly) - 1;
+}
+
+static int atmel_pmecc_build_gf_tables(int mm, unsigned int poly,
+ struct atmel_pmecc_gf_tables *gf_tables)
+{
+ unsigned int i, x = 1;
+ const unsigned int k = BIT(deg(poly));
+ unsigned int nn = BIT(mm) - 1;
+
+ /* primitive polynomial must be of degree m */
+ if (k != (1u << mm))
+ return -EINVAL;
+
+ for (i = 0; i < nn; i++) {
+ gf_tables->alpha_to[i] = x;
+ gf_tables->index_of[x] = i;
+ if (i && (x == 1))
+ /* polynomial is not primitive (a^i=1 with 0<i<2^m-1) */
+ return -EINVAL;
+ x <<= 1;
+ if (x & k)
+ x ^= poly;
+ }
+ gf_tables->alpha_to[nn] = 1;
+ gf_tables->index_of[0] = 0;
+
+ return 0;
+}
+
+static const struct atmel_pmecc_gf_tables *
+atmel_pmecc_create_gf_tables(const struct atmel_pmecc_user_req *req)
+{
+ struct atmel_pmecc_gf_tables *gf_tables;
+ unsigned int poly, degree, table_size;
+ int ret;
+
+ if (req->ecc.sectorsize == 512) {
+ degree = PMECC_GF_DIMENSION_13;
+ poly = PMECC_GF_13_PRIMITIVE_POLY;
+ table_size = PMECC_LOOKUP_TABLE_SIZE_512;
+ } else {
+ degree = PMECC_GF_DIMENSION_14;
+ poly = PMECC_GF_14_PRIMITIVE_POLY;
+ table_size = PMECC_LOOKUP_TABLE_SIZE_1024;
+ }
+
+ gf_tables = kzalloc(sizeof(*gf_tables) +
+ (2 * table_size * sizeof(u16)),
+ GFP_KERNEL);
+ if (!gf_tables)
+ return ERR_PTR(-ENOMEM);
+
+ gf_tables->alpha_to = (void *)(gf_tables + 1);
+ gf_tables->index_of = gf_tables->alpha_to + table_size;
+
+ ret = atmel_pmecc_build_gf_tables(degree, poly, gf_tables);
+ if (ret) {
+ kfree(gf_tables);
+ return ERR_PTR(ret);
+ }
+
+ return gf_tables;
+}
+
+static const struct atmel_pmecc_gf_tables *
+atmel_pmecc_get_gf_tables(const struct atmel_pmecc_user_req *req)
+{
+ const struct atmel_pmecc_gf_tables **gf_tables, *ret;
+
+ mutex_lock(&pmecc_gf_tables_lock);
+ if (req->ecc.sectorsize == 512)
+ gf_tables = &pmecc_gf_tables_512;
+ else
+ gf_tables = &pmecc_gf_tables_1024;
+
+ ret = *gf_tables;
+
+ if (!ret) {
+ ret = atmel_pmecc_create_gf_tables(req);
+ if (!IS_ERR(ret))
+ *gf_tables = ret;
+ }
+ mutex_unlock(&pmecc_gf_tables_lock);
+
+ return ret;
+}
+
+static int atmel_pmecc_prepare_user_req(struct atmel_pmecc *pmecc,
+ struct atmel_pmecc_user_req *req)
+{
+ int i, max_eccbytes, eccbytes = 0, eccstrength = 0;
+
+ if (req->pagesize <= 0 || req->oobsize <= 0 || req->ecc.bytes <= 0)
+ return -EINVAL;
+
+ if (req->ecc.ooboffset >= 0 &&
+ req->ecc.ooboffset + req->ecc.bytes > req->oobsize)
+ return -EINVAL;
+
+ if (req->ecc.sectorsize == ATMEL_PMECC_SECTOR_SIZE_AUTO) {
+ if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH)
+ return -EINVAL;
+
+ if (req->pagesize > 512)
+ req->ecc.sectorsize = 1024;
+ else
+ req->ecc.sectorsize = 512;
+ }
+
+ if (req->ecc.sectorsize != 512 && req->ecc.sectorsize != 1024)
+ return -EINVAL;
+
+ if (req->pagesize % req->ecc.sectorsize)
+ return -EINVAL;
+
+ req->ecc.nsectors = req->pagesize / req->ecc.sectorsize;
+
+ max_eccbytes = req->ecc.bytes;
+
+ for (i = 0; i < pmecc->caps->nstrengths; i++) {
+ int nbytes, strength = pmecc->caps->strengths[i];
+
+ if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH &&
+ strength < req->ecc.strength)
+ continue;
+
+ nbytes = DIV_ROUND_UP(strength * fls(8 * req->ecc.sectorsize),
+ 8);
+ nbytes *= req->ecc.nsectors;
+
+ if (nbytes > max_eccbytes)
+ break;
+
+ eccstrength = strength;
+ eccbytes = nbytes;
+
+ if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH)
+ break;
+ }
+
+ if (!eccstrength)
+ return -EINVAL;
+
+ req->ecc.bytes = eccbytes;
+ req->ecc.strength = eccstrength;
+
+ if (req->ecc.ooboffset < 0)
+ req->ecc.ooboffset = req->oobsize - eccbytes;
+
+ return 0;
+}
+
+struct atmel_pmecc_user *
+atmel_pmecc_create_user(struct atmel_pmecc *pmecc,
+ struct atmel_pmecc_user_req *req)
+{
+ struct atmel_pmecc_user *user;
+ const struct atmel_pmecc_gf_tables *gf_tables;
+ int strength, size, ret;
+
+ ret = atmel_pmecc_prepare_user_req(pmecc, req);
+ if (ret)
+ return ERR_PTR(ret);
+
+ size = sizeof(*user);
+ size = ALIGN(size, sizeof(u16));
+ /* Reserve space for partial_syn, si and smu */
+ size += ((2 * req->ecc.strength) + 1) * sizeof(u16) *
+ (2 + req->ecc.strength + 2);
+ /* Reserve space for lmu. */
+ size += (req->ecc.strength + 1) * sizeof(u16);
+ /* Reserve space for mu, dmu and delta. */
+ size = ALIGN(size, sizeof(s32));
+ size += (req->ecc.strength + 1) * sizeof(s32) * 3;
+
+ user = kzalloc(size, GFP_KERNEL);
+ if (!user)
+ return ERR_PTR(-ENOMEM);
+
+ user->pmecc = pmecc;
+
+ user->partial_syn = (s16 *)PTR_ALIGN(user + 1, sizeof(u16));
+ user->si = user->partial_syn + ((2 * req->ecc.strength) + 1);
+ user->lmu = user->si + ((2 * req->ecc.strength) + 1);
+ user->smu = user->lmu + (req->ecc.strength + 1);
+ user->mu = (s32 *)PTR_ALIGN(user->smu +
+ (((2 * req->ecc.strength) + 1) *
+ (req->ecc.strength + 2)),
+ sizeof(s32));
+ user->dmu = user->mu + req->ecc.strength + 1;
+ user->delta = user->dmu + req->ecc.strength + 1;
+
+ gf_tables = atmel_pmecc_get_gf_tables(req);
+ if (IS_ERR(gf_tables)) {
+ kfree(user);
+ return ERR_CAST(gf_tables);
+ }
+
+ user->gf_tables = gf_tables;
+
+ user->eccbytes = req->ecc.bytes / req->ecc.nsectors;
+
+ for (strength = 0; strength < pmecc->caps->nstrengths; strength++) {
+ if (pmecc->caps->strengths[strength] == req->ecc.strength)
+ break;
+ }
+
+ user->cache.cfg = PMECC_CFG_BCH_STRENGTH(strength) |
+ PMECC_CFG_NSECTORS(req->ecc.nsectors);
+
+ if (req->ecc.sectorsize == 1024)
+ user->cache.cfg |= PMECC_CFG_SECTOR1024;
+
+ user->cache.sarea = req->oobsize - 1;
+ user->cache.saddr = req->ecc.ooboffset;
+ user->cache.eaddr = req->ecc.ooboffset + req->ecc.bytes - 1;
+
+ return user;
+}
+EXPORT_SYMBOL_GPL(atmel_pmecc_create_user);
+
+void atmel_pmecc_destroy_user(struct atmel_pmecc_user *user)
+{
+ kfree(user);
+}
+EXPORT_SYMBOL_GPL(atmel_pmecc_destroy_user);
+
+static int get_strength(struct atmel_pmecc_user *user)
+{
+ const int *strengths = user->pmecc->caps->strengths;
+
+ return strengths[user->cache.cfg & PMECC_CFG_BCH_STRENGTH_MASK];
+}
+
+static int get_sectorsize(struct atmel_pmecc_user *user)
+{
+ return user->cache.cfg & PMECC_CFG_SECTOR1024 ? 1024 : 512;
+}
+
+static void atmel_pmecc_gen_syndrome(struct atmel_pmecc_user *user, int sector)
+{
+ int strength = get_strength(user);
+ u32 value;
+ int i;
+
+ /* Fill odd syndromes */
+ for (i = 0; i < strength; i++) {
+ value = readl_relaxed(user->pmecc->regs.base +
+ ATMEL_PMECC_REM(sector, i / 2));
+ if (i & 1)
+ value >>= 16;
+
+ user->partial_syn[(2 * i) + 1] = value;
+ }
+}
+
+static void atmel_pmecc_substitute(struct atmel_pmecc_user *user)
+{
+ int degree = get_sectorsize(user) == 512 ? 13 : 14;
+ int cw_len = BIT(degree) - 1;
+ int strength = get_strength(user);
+ s16 *alpha_to = user->gf_tables->alpha_to;
+ s16 *index_of = user->gf_tables->index_of;
+ s16 *partial_syn = user->partial_syn;
+ s16 *si;
+ int i, j;
+
+ /*
+ * si[] is a table that holds the current syndrome value,
+ * an element of that table belongs to the field
+ */
+ si = user->si;
+
+ memset(&si[1], 0, sizeof(s16) * ((2 * strength) - 1));
+
+ /* Computation 2t syndromes based on S(x) */
+ /* Odd syndromes */
+ for (i = 1; i < 2 * strength; i += 2) {
+ for (j = 0; j < degree; j++) {
+ if (partial_syn[i] & BIT(j))
+ si[i] = alpha_to[i * j] ^ si[i];
+ }
+ }
+ /* Even syndrome = (Odd syndrome) ** 2 */
+ for (i = 2, j = 1; j <= strength; i = ++j << 1) {
+ if (si[j] == 0) {
+ si[i] = 0;
+ } else {
+ s16 tmp;
+
+ tmp = index_of[si[j]];
+ tmp = (tmp * 2) % cw_len;
+ si[i] = alpha_to[tmp];
+ }
+ }
+}
+
+static void atmel_pmecc_get_sigma(struct atmel_pmecc_user *user)
+{
+ s16 *lmu = user->lmu;
+ s16 *si = user->si;
+ s32 *mu = user->mu;
+ s32 *dmu = user->dmu;
+ s32 *delta = user->delta;
+ int degree = get_sectorsize(user) == 512 ? 13 : 14;
+ int cw_len = BIT(degree) - 1;
+ int strength = get_strength(user);
+ int num = 2 * strength + 1;
+ s16 *index_of = user->gf_tables->index_of;
+ s16 *alpha_to = user->gf_tables->alpha_to;
+ int i, j, k;
+ u32 dmu_0_count, tmp;
+ s16 *smu = user->smu;
+
+ /* index of largest delta */
+ int ro;
+ int largest;
+ int diff;
+
+ dmu_0_count = 0;
+
+ /* First Row */
+
+ /* Mu */
+ mu[0] = -1;
+
+ memset(smu, 0, sizeof(s16) * num);
+ smu[0] = 1;
+
+ /* discrepancy set to 1 */
+ dmu[0] = 1;
+ /* polynom order set to 0 */
+ lmu[0] = 0;
+ delta[0] = (mu[0] * 2 - lmu[0]) >> 1;
+
+ /* Second Row */
+
+ /* Mu */
+ mu[1] = 0;
+ /* Sigma(x) set to 1 */
+ memset(&smu[num], 0, sizeof(s16) * num);
+ smu[num] = 1;
+
+ /* discrepancy set to S1 */
+ dmu[1] = si[1];
+
+ /* polynom order set to 0 */
+ lmu[1] = 0;
+
+ delta[1] = (mu[1] * 2 - lmu[1]) >> 1;
+
+ /* Init the Sigma(x) last row */
+ memset(&smu[(strength + 1) * num], 0, sizeof(s16) * num);
+
+ for (i = 1; i <= strength; i++) {
+ mu[i + 1] = i << 1;
+ /* Begin Computing Sigma (Mu+1) and L(mu) */
+ /* check if discrepancy is set to 0 */
+ if (dmu[i] == 0) {
+ dmu_0_count++;
+
+ tmp = ((strength - (lmu[i] >> 1) - 1) / 2);
+ if ((strength - (lmu[i] >> 1) - 1) & 0x1)
+ tmp += 2;
+ else
+ tmp += 1;
+
+ if (dmu_0_count == tmp) {
+ for (j = 0; j <= (lmu[i] >> 1) + 1; j++)
+ smu[(strength + 1) * num + j] =
+ smu[i * num + j];
+
+ lmu[strength + 1] = lmu[i];
+ return;
+ }
+
+ /* copy polynom */
+ for (j = 0; j <= lmu[i] >> 1; j++)
+ smu[(i + 1) * num + j] = smu[i * num + j];
+
+ /* copy previous polynom order to the next */
+ lmu[i + 1] = lmu[i];
+ } else {
+ ro = 0;
+ largest = -1;
+ /* find largest delta with dmu != 0 */
+ for (j = 0; j < i; j++) {
+ if ((dmu[j]) && (delta[j] > largest)) {
+ largest = delta[j];
+ ro = j;
+ }
+ }
+
+ /* compute difference */
+ diff = (mu[i] - mu[ro]);
+
+ /* Compute degree of the new smu polynomial */
+ if ((lmu[i] >> 1) > ((lmu[ro] >> 1) + diff))
+ lmu[i + 1] = lmu[i];
+ else
+ lmu[i + 1] = ((lmu[ro] >> 1) + diff) * 2;
+
+ /* Init smu[i+1] with 0 */
+ for (k = 0; k < num; k++)
+ smu[(i + 1) * num + k] = 0;
+
+ /* Compute smu[i+1] */
+ for (k = 0; k <= lmu[ro] >> 1; k++) {
+ s16 a, b, c;
+
+ if (!(smu[ro * num + k] && dmu[i]))
+ continue;
+
+ a = index_of[dmu[i]];
+ b = index_of[dmu[ro]];
+ c = index_of[smu[ro * num + k]];
+ tmp = a + (cw_len - b) + c;
+ a = alpha_to[tmp % cw_len];
+ smu[(i + 1) * num + (k + diff)] = a;
+ }
+
+ for (k = 0; k <= lmu[i] >> 1; k++)
+ smu[(i + 1) * num + k] ^= smu[i * num + k];
+ }
+
+ /* End Computing Sigma (Mu+1) and L(mu) */
+ /* In either case compute delta */
+ delta[i + 1] = (mu[i + 1] * 2 - lmu[i + 1]) >> 1;
+
+ /* Do not compute discrepancy for the last iteration */
+ if (i >= strength)
+ continue;
+
+ for (k = 0; k <= (lmu[i + 1] >> 1); k++) {
+ tmp = 2 * (i - 1);
+ if (k == 0) {
+ dmu[i + 1] = si[tmp + 3];
+ } else if (smu[(i + 1) * num + k] && si[tmp + 3 - k]) {
+ s16 a, b, c;
+
+ a = index_of[smu[(i + 1) * num + k]];
+ b = si[2 * (i - 1) + 3 - k];
+ c = index_of[b];
+ tmp = a + c;
+ tmp %= cw_len;
+ dmu[i + 1] = alpha_to[tmp] ^ dmu[i + 1];
+ }
+ }
+ }
+}
+
+static int atmel_pmecc_err_location(struct atmel_pmecc_user *user)
+{
+ int sector_size = get_sectorsize(user);
+ int degree = sector_size == 512 ? 13 : 14;
+ struct atmel_pmecc *pmecc = user->pmecc;
+ int strength = get_strength(user);
+ int ret, roots_nbr, i, err_nbr = 0;
+ int num = (2 * strength) + 1;
+ s16 *smu = user->smu;
+ u32 val;
+
+ writel(PMERRLOC_DISABLE, pmecc->regs.errloc + ATMEL_PMERRLOC_ELDIS);
+
+ for (i = 0; i <= user->lmu[strength + 1] >> 1; i++) {
+ writel_relaxed(smu[(strength + 1) * num + i],
+ pmecc->regs.errloc + ATMEL_PMERRLOC_SIGMA(i));
+ err_nbr++;
+ }
+
+ val = (err_nbr - 1) << 16;
+ if (sector_size == 1024)
+ val |= 1;
+
+ writel(val, pmecc->regs.errloc + ATMEL_PMERRLOC_ELCFG);
+ writel((sector_size * 8) + (degree * strength),
+ pmecc->regs.errloc + ATMEL_PMERRLOC_ELEN);
+
+ ret = readl_relaxed_poll_timeout(pmecc->regs.errloc +
+ ATMEL_PMERRLOC_ELISR,
+ val, val & PMERRLOC_CALC_DONE, 0,
+ PMECC_MAX_TIMEOUT_MS * 1000);
+ if (ret) {
+ dev_err(pmecc->dev,
+ "PMECC: Timeout to calculate error location.\n");
+ return ret;
+ }
+
+ roots_nbr = (val & PMERRLOC_ERR_NUM_MASK) >> 8;
+ /* Number of roots == degree of smu hence <= cap */
+ if (roots_nbr == user->lmu[strength + 1] >> 1)
+ return err_nbr - 1;
+
+ /*
+ * Number of roots does not match the degree of smu
+ * unable to correct error.
+ */
+ return -EBADMSG;
+}
+
+int atmel_pmecc_correct_sector(struct atmel_pmecc_user *user, int sector,
+ void *data, void *ecc)
+{
+ struct atmel_pmecc *pmecc = user->pmecc;
+ int sectorsize = get_sectorsize(user);
+ int eccbytes = user->eccbytes;
+ int i, nerrors;
+
+ if (!(user->isr & BIT(sector)))
+ return 0;
+
+ atmel_pmecc_gen_syndrome(user, sector);
+ atmel_pmecc_substitute(user);
+ atmel_pmecc_get_sigma(user);
+
+ nerrors = atmel_pmecc_err_location(user);
+ if (nerrors < 0)
+ return nerrors;
+
+ for (i = 0; i < nerrors; i++) {
+ const char *area;
+ int byte, bit;
+ u32 errpos;
+ u8 *ptr;
+
+ errpos = readl_relaxed(pmecc->regs.errloc +
+ ATMEL_PMERRLOC_EL(pmecc->caps->el_offset, i));
+ errpos--;
+
+ byte = errpos / 8;
+ bit = errpos % 8;
+
+ if (byte < sectorsize) {
+ ptr = data + byte;
+ area = "data";
+ } else if (byte < sectorsize + eccbytes) {
+ ptr = ecc + byte - sectorsize;
+ area = "ECC";
+ } else {
+ dev_dbg(pmecc->dev,
+ "Invalid errpos value (%d, max is %d)\n",
+ errpos, (sectorsize + eccbytes) * 8);
+ return -EINVAL;
+ }
+
+ dev_dbg(pmecc->dev,
+ "Bit flip in %s area, byte %d: 0x%02x -> 0x%02x\n",
+ area, byte, *ptr, (unsigned int)(*ptr ^ BIT(bit)));
+
+ *ptr ^= BIT(bit);
+ }
+
+ return nerrors;
+}
+EXPORT_SYMBOL_GPL(atmel_pmecc_correct_sector);
+
+bool atmel_pmecc_correct_erased_chunks(struct atmel_pmecc_user *user)
+{
+ return user->pmecc->caps->correct_erased_chunks;
+}
+EXPORT_SYMBOL_GPL(atmel_pmecc_correct_erased_chunks);
+
+void atmel_pmecc_get_generated_eccbytes(struct atmel_pmecc_user *user,
+ int sector, void *ecc)
+{
+ struct atmel_pmecc *pmecc = user->pmecc;
+ u8 *ptr = ecc;
+ int i;
+
+ for (i = 0; i < user->eccbytes; i++)
+ ptr[i] = readb_relaxed(pmecc->regs.base +
+ ATMEL_PMECC_ECC(sector, i));
+}
+EXPORT_SYMBOL_GPL(atmel_pmecc_get_generated_eccbytes);
+
+void atmel_pmecc_reset(struct atmel_pmecc *pmecc)
+{
+ writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
+ writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
+}
+EXPORT_SYMBOL_GPL(atmel_pmecc_reset);
+
+int atmel_pmecc_enable(struct atmel_pmecc_user *user, int op)
+{
+ struct atmel_pmecc *pmecc = user->pmecc;
+ u32 cfg;
+
+ if (op != NAND_ECC_READ && op != NAND_ECC_WRITE) {
+ dev_err(pmecc->dev, "Bad ECC operation!");
+ return -EINVAL;
+ }
+
+ mutex_lock(&user->pmecc->lock);
+
+ cfg = user->cache.cfg;
+ if (op == NAND_ECC_WRITE)
+ cfg |= PMECC_CFG_WRITE_OP;
+ else
+ cfg |= PMECC_CFG_AUTO_ENABLE;
+
+ writel(cfg, pmecc->regs.base + ATMEL_PMECC_CFG);
+ writel(user->cache.sarea, pmecc->regs.base + ATMEL_PMECC_SAREA);
+ writel(user->cache.saddr, pmecc->regs.base + ATMEL_PMECC_SADDR);
+ writel(user->cache.eaddr, pmecc->regs.base + ATMEL_PMECC_EADDR);
+
+ writel(PMECC_CTRL_ENABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
+ writel(PMECC_CTRL_DATA, pmecc->regs.base + ATMEL_PMECC_CTRL);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(atmel_pmecc_enable);
+
+void atmel_pmecc_disable(struct atmel_pmecc_user *user)
+{
+ atmel_pmecc_reset(user->pmecc);
+ mutex_unlock(&user->pmecc->lock);
+}
+EXPORT_SYMBOL_GPL(atmel_pmecc_disable);
+
+int atmel_pmecc_wait_rdy(struct atmel_pmecc_user *user)
+{
+ struct atmel_pmecc *pmecc = user->pmecc;
+ u32 status;
+ int ret;
+
+ ret = readl_relaxed_poll_timeout(pmecc->regs.base +
+ ATMEL_PMECC_SR,
+ status, !(status & PMECC_SR_BUSY), 0,
+ PMECC_MAX_TIMEOUT_MS * 1000);
+ if (ret) {
+ dev_err(pmecc->dev,
+ "Timeout while waiting for PMECC ready.\n");
+ return ret;
+ }
+
+ user->isr = readl_relaxed(pmecc->regs.base + ATMEL_PMECC_ISR);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(atmel_pmecc_wait_rdy);
+
+static struct atmel_pmecc *atmel_pmecc_create(struct platform_device *pdev,
+ const struct atmel_pmecc_caps *caps,
+ int pmecc_res_idx, int errloc_res_idx)
+{
+ struct device *dev = &pdev->dev;
+ struct atmel_pmecc *pmecc;
+ struct resource *res;
+
+ pmecc = devm_kzalloc(dev, sizeof(*pmecc), GFP_KERNEL);
+ if (!pmecc)
+ return ERR_PTR(-ENOMEM);
+
+ pmecc->caps = caps;
+ pmecc->dev = dev;
+ mutex_init(&pmecc->lock);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, pmecc_res_idx);
+ pmecc->regs.base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(pmecc->regs.base))
+ return ERR_CAST(pmecc->regs.base);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, errloc_res_idx);
+ pmecc->regs.errloc = devm_ioremap_resource(dev, res);
+ if (IS_ERR(pmecc->regs.errloc))
+ return ERR_CAST(pmecc->regs.errloc);
+
+ /* Disable all interrupts before registering the PMECC handler. */
+ writel(0xffffffff, pmecc->regs.base + ATMEL_PMECC_IDR);
+ atmel_pmecc_reset(pmecc);
+
+ return pmecc;
+}
+
+static void devm_atmel_pmecc_put(struct device *dev, void *res)
+{
+ struct atmel_pmecc **pmecc = res;
+
+ put_device((*pmecc)->dev);
+}
+
+static struct atmel_pmecc *atmel_pmecc_get_by_node(struct device *userdev,
+ struct device_node *np)
+{
+ struct platform_device *pdev;
+ struct atmel_pmecc *pmecc, **ptr;
+
+ pdev = of_find_device_by_node(np);
+ if (!pdev || !platform_get_drvdata(pdev))
+ return ERR_PTR(-EPROBE_DEFER);
+
+ ptr = devres_alloc(devm_atmel_pmecc_put, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ get_device(&pdev->dev);
+ pmecc = platform_get_drvdata(pdev);
+
+ *ptr = pmecc;
+
+ devres_add(userdev, ptr);
+
+ return pmecc;
+}
+
+static const int atmel_pmecc_strengths[] = { 2, 4, 8, 12, 24, 32 };
+
+static struct atmel_pmecc_caps at91sam9g45_caps = {
+ .strengths = atmel_pmecc_strengths,
+ .nstrengths = 5,
+ .el_offset = 0x8c,
+};
+
+static struct atmel_pmecc_caps sama5d4_caps = {
+ .strengths = atmel_pmecc_strengths,
+ .nstrengths = 5,
+ .el_offset = 0x8c,
+ .correct_erased_chunks = true,
+};
+
+static struct atmel_pmecc_caps sama5d2_caps = {
+ .strengths = atmel_pmecc_strengths,
+ .nstrengths = 6,
+ .el_offset = 0xac,
+ .correct_erased_chunks = true,
+};
+
+static const struct of_device_id atmel_pmecc_legacy_match[] = {
+ { .compatible = "atmel,sama5d4-nand", &sama5d4_caps },
+ { .compatible = "atmel,sama5d2-nand", &sama5d2_caps },
+ { /* sentinel */ }
+};
+
+struct atmel_pmecc *devm_atmel_pmecc_get(struct device *userdev)
+{
+ struct atmel_pmecc *pmecc;
+ struct device_node *np;
+
+ if (!userdev)
+ return ERR_PTR(-EINVAL);
+
+ if (!userdev->of_node)
+ return NULL;
+
+ np = of_parse_phandle(userdev->of_node, "ecc-engine", 0);
+ if (np) {
+ pmecc = atmel_pmecc_get_by_node(userdev, np);
+ of_node_put(np);
+ } else {
+ /*
+ * Support old DT bindings: in this case the PMECC iomem
+ * resources are directly defined in the user pdev at position
+ * 1 and 2. Extract all relevant information from there.
+ */
+ struct platform_device *pdev = to_platform_device(userdev);
+ const struct atmel_pmecc_caps *caps;
+ const struct of_device_id *match;
+
+ /* No PMECC engine available. */
+ if (!of_property_read_bool(userdev->of_node,
+ "atmel,has-pmecc"))
+ return NULL;
+
+ caps = &at91sam9g45_caps;
+
+ /* Find the caps associated to the NAND dev node. */
+ match = of_match_node(atmel_pmecc_legacy_match,
+ userdev->of_node);
+ if (match && match->data)
+ caps = match->data;
+
+ pmecc = atmel_pmecc_create(pdev, caps, 1, 2);
+ }
+
+ return pmecc;
+}
+EXPORT_SYMBOL(devm_atmel_pmecc_get);
+
+static const struct of_device_id atmel_pmecc_match[] = {
+ { .compatible = "atmel,at91sam9g45-pmecc", &at91sam9g45_caps },
+ { .compatible = "atmel,sama5d4-pmecc", &sama5d4_caps },
+ { .compatible = "atmel,sama5d2-pmecc", &sama5d2_caps },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, atmel_pmecc_match);
+
+static int atmel_pmecc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct atmel_pmecc_caps *caps;
+ struct atmel_pmecc *pmecc;
+
+ caps = of_device_get_match_data(&pdev->dev);
+ if (!caps) {
+ dev_err(dev, "Invalid caps\n");
+ return -EINVAL;
+ }
+
+ pmecc = atmel_pmecc_create(pdev, caps, 0, 1);
+ if (IS_ERR(pmecc))
+ return PTR_ERR(pmecc);
+
+ platform_set_drvdata(pdev, pmecc);
+
+ return 0;
+}
+
+static struct platform_driver atmel_pmecc_driver = {
+ .driver = {
+ .name = "atmel-pmecc",
+ .of_match_table = of_match_ptr(atmel_pmecc_match),
+ },
+ .probe = atmel_pmecc_probe,
+};
+module_platform_driver(atmel_pmecc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
+MODULE_DESCRIPTION("PMECC engine driver");
+MODULE_ALIAS("platform:atmel_pmecc");
diff --git a/drivers/mtd/nand/raw/atmel/pmecc.h b/drivers/mtd/nand/raw/atmel/pmecc.h
new file mode 100644
index 000000000000..808f1be0d6ad
--- /dev/null
+++ b/drivers/mtd/nand/raw/atmel/pmecc.h
@@ -0,0 +1,74 @@
+/*
+ * © Copyright 2016 ATMEL
+ * © Copyright 2016 Free Electrons
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
+ *
+ * Derived from the atmel_nand.c driver which contained the following
+ * copyrights:
+ *
+ * Copyright © 2003 Rick Bronson
+ *
+ * Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8)
+ * Copyright © 2001 Thomas Gleixner (gleixner@autronix.de)
+ *
+ * Derived from drivers/mtd/spia.c (removed in v3.8)
+ * Copyright © 2000 Steven J. Hill (sjhill@cotw.com)
+ *
+ *
+ * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263
+ * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright © 2007
+ *
+ * Derived from Das U-Boot source code
+ * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c)
+ * © Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas
+ *
+ * Add Programmable Multibit ECC support for various AT91 SoC
+ * © Copyright 2012 ATMEL, Hong Xu
+ *
+ * Add Nand Flash Controller support for SAMA5 SoC
+ * © Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.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.
+ *
+ */
+
+#ifndef ATMEL_PMECC_H
+#define ATMEL_PMECC_H
+
+#define ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH 0
+#define ATMEL_PMECC_SECTOR_SIZE_AUTO 0
+#define ATMEL_PMECC_OOBOFFSET_AUTO -1
+
+struct atmel_pmecc_user_req {
+ int pagesize;
+ int oobsize;
+ struct {
+ int strength;
+ int bytes;
+ int sectorsize;
+ int nsectors;
+ int ooboffset;
+ } ecc;
+};
+
+struct atmel_pmecc *devm_atmel_pmecc_get(struct device *dev);
+
+struct atmel_pmecc_user *
+atmel_pmecc_create_user(struct atmel_pmecc *pmecc,
+ struct atmel_pmecc_user_req *req);
+void atmel_pmecc_destroy_user(struct atmel_pmecc_user *user);
+
+void atmel_pmecc_reset(struct atmel_pmecc *pmecc);
+int atmel_pmecc_enable(struct atmel_pmecc_user *user, int op);
+void atmel_pmecc_disable(struct atmel_pmecc_user *user);
+int atmel_pmecc_wait_rdy(struct atmel_pmecc_user *user);
+int atmel_pmecc_correct_sector(struct atmel_pmecc_user *user, int sector,
+ void *data, void *ecc);
+bool atmel_pmecc_correct_erased_chunks(struct atmel_pmecc_user *user);
+void atmel_pmecc_get_generated_eccbytes(struct atmel_pmecc_user *user,
+ int sector, void *ecc);
+
+#endif /* ATMEL_PMECC_H */
diff --git a/drivers/mtd/nand/raw/au1550nd.c b/drivers/mtd/nand/raw/au1550nd.c
new file mode 100644
index 000000000000..df0ef1f1e2f5
--- /dev/null
+++ b/drivers/mtd/nand/raw/au1550nd.c
@@ -0,0 +1,515 @@
+/*
+ * Copyright (C) 2004 Embedded Edge, LLC
+ *
+ * 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.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/platform_device.h>
+#include <asm/io.h>
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/au1550nd.h>
+
+
+struct au1550nd_ctx {
+ struct nand_chip chip;
+
+ int cs;
+ void __iomem *base;
+ void (*write_byte)(struct mtd_info *, u_char);
+};
+
+/**
+ * au_read_byte - read one byte from the chip
+ * @mtd: MTD device structure
+ *
+ * read function for 8bit buswidth
+ */
+static u_char au_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ u_char ret = readb(this->IO_ADDR_R);
+ wmb(); /* drain writebuffer */
+ return ret;
+}
+
+/**
+ * au_write_byte - write one byte to the chip
+ * @mtd: MTD device structure
+ * @byte: pointer to data byte to write
+ *
+ * write function for 8it buswidth
+ */
+static void au_write_byte(struct mtd_info *mtd, u_char byte)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ writeb(byte, this->IO_ADDR_W);
+ wmb(); /* drain writebuffer */
+}
+
+/**
+ * au_read_byte16 - read one byte endianness aware from the chip
+ * @mtd: MTD device structure
+ *
+ * read function for 16bit buswidth with endianness conversion
+ */
+static u_char au_read_byte16(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ u_char ret = (u_char) cpu_to_le16(readw(this->IO_ADDR_R));
+ wmb(); /* drain writebuffer */
+ return ret;
+}
+
+/**
+ * au_write_byte16 - write one byte endianness aware to the chip
+ * @mtd: MTD device structure
+ * @byte: pointer to data byte to write
+ *
+ * write function for 16bit buswidth with endianness conversion
+ */
+static void au_write_byte16(struct mtd_info *mtd, u_char byte)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ writew(le16_to_cpu((u16) byte), this->IO_ADDR_W);
+ wmb(); /* drain writebuffer */
+}
+
+/**
+ * au_read_word - read one word from the chip
+ * @mtd: MTD device structure
+ *
+ * read function for 16bit buswidth without endianness conversion
+ */
+static u16 au_read_word(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ u16 ret = readw(this->IO_ADDR_R);
+ wmb(); /* drain writebuffer */
+ return ret;
+}
+
+/**
+ * au_write_buf - write buffer to chip
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ *
+ * write function for 8bit buswidth
+ */
+static void au_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+ int i;
+ struct nand_chip *this = mtd_to_nand(mtd);
+
+ for (i = 0; i < len; i++) {
+ writeb(buf[i], this->IO_ADDR_W);
+ wmb(); /* drain writebuffer */
+ }
+}
+
+/**
+ * au_read_buf - read chip data into buffer
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
+ *
+ * read function for 8bit buswidth
+ */
+static void au_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+ int i;
+ struct nand_chip *this = mtd_to_nand(mtd);
+
+ for (i = 0; i < len; i++) {
+ buf[i] = readb(this->IO_ADDR_R);
+ wmb(); /* drain writebuffer */
+ }
+}
+
+/**
+ * au_write_buf16 - write buffer to chip
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ *
+ * write function for 16bit buswidth
+ */
+static void au_write_buf16(struct mtd_info *mtd, const u_char *buf, int len)
+{
+ int i;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ u16 *p = (u16 *) buf;
+ len >>= 1;
+
+ for (i = 0; i < len; i++) {
+ writew(p[i], this->IO_ADDR_W);
+ wmb(); /* drain writebuffer */
+ }
+
+}
+
+/**
+ * au_read_buf16 - read chip data into buffer
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
+ *
+ * read function for 16bit buswidth
+ */
+static void au_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
+{
+ int i;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ u16 *p = (u16 *) buf;
+ len >>= 1;
+
+ for (i = 0; i < len; i++) {
+ p[i] = readw(this->IO_ADDR_R);
+ wmb(); /* drain writebuffer */
+ }
+}
+
+/* Select the chip by setting nCE to low */
+#define NAND_CTL_SETNCE 1
+/* Deselect the chip by setting nCE to high */
+#define NAND_CTL_CLRNCE 2
+/* Select the command latch by setting CLE to high */
+#define NAND_CTL_SETCLE 3
+/* Deselect the command latch by setting CLE to low */
+#define NAND_CTL_CLRCLE 4
+/* Select the address latch by setting ALE to high */
+#define NAND_CTL_SETALE 5
+/* Deselect the address latch by setting ALE to low */
+#define NAND_CTL_CLRALE 6
+
+static void au1550_hwcontrol(struct mtd_info *mtd, int cmd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct au1550nd_ctx *ctx = container_of(this, struct au1550nd_ctx,
+ chip);
+
+ switch (cmd) {
+
+ case NAND_CTL_SETCLE:
+ this->IO_ADDR_W = ctx->base + MEM_STNAND_CMD;
+ break;
+
+ case NAND_CTL_CLRCLE:
+ this->IO_ADDR_W = ctx->base + MEM_STNAND_DATA;
+ break;
+
+ case NAND_CTL_SETALE:
+ this->IO_ADDR_W = ctx->base + MEM_STNAND_ADDR;
+ break;
+
+ case NAND_CTL_CLRALE:
+ this->IO_ADDR_W = ctx->base + MEM_STNAND_DATA;
+ /* FIXME: Nobody knows why this is necessary,
+ * but it works only that way */
+ udelay(1);
+ break;
+
+ case NAND_CTL_SETNCE:
+ /* assert (force assert) chip enable */
+ alchemy_wrsmem((1 << (4 + ctx->cs)), AU1000_MEM_STNDCTL);
+ break;
+
+ case NAND_CTL_CLRNCE:
+ /* deassert chip enable */
+ alchemy_wrsmem(0, AU1000_MEM_STNDCTL);
+ break;
+ }
+
+ this->IO_ADDR_R = this->IO_ADDR_W;
+
+ wmb(); /* Drain the writebuffer */
+}
+
+int au1550_device_ready(struct mtd_info *mtd)
+{
+ return (alchemy_rdsmem(AU1000_MEM_STSTAT) & 0x1) ? 1 : 0;
+}
+
+/**
+ * au1550_select_chip - control -CE line
+ * Forbid driving -CE manually permitting the NAND controller to do this.
+ * Keeping -CE asserted during the whole sector reads interferes with the
+ * NOR flash and PCMCIA drivers as it causes contention on the static bus.
+ * We only have to hold -CE low for the NAND read commands since the flash
+ * chip needs it to be asserted during chip not ready time but the NAND
+ * controller keeps it released.
+ *
+ * @mtd: MTD device structure
+ * @chip: chipnumber to select, -1 for deselect
+ */
+static void au1550_select_chip(struct mtd_info *mtd, int chip)
+{
+}
+
+/**
+ * au1550_command - Send command to NAND device
+ * @mtd: MTD device structure
+ * @command: the command to be sent
+ * @column: the column address for this command, -1 if none
+ * @page_addr: the page address for this command, -1 if none
+ */
+static void au1550_command(struct mtd_info *mtd, unsigned command, int column, int page_addr)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct au1550nd_ctx *ctx = container_of(this, struct au1550nd_ctx,
+ chip);
+ int ce_override = 0, i;
+ unsigned long flags = 0;
+
+ /* Begin command latch cycle */
+ au1550_hwcontrol(mtd, NAND_CTL_SETCLE);
+ /*
+ * Write out the command to the device.
+ */
+ if (command == NAND_CMD_SEQIN) {
+ int readcmd;
+
+ if (column >= mtd->writesize) {
+ /* OOB area */
+ column -= mtd->writesize;
+ readcmd = NAND_CMD_READOOB;
+ } else if (column < 256) {
+ /* First 256 bytes --> READ0 */
+ readcmd = NAND_CMD_READ0;
+ } else {
+ column -= 256;
+ readcmd = NAND_CMD_READ1;
+ }
+ ctx->write_byte(mtd, readcmd);
+ }
+ ctx->write_byte(mtd, command);
+
+ /* Set ALE and clear CLE to start address cycle */
+ au1550_hwcontrol(mtd, NAND_CTL_CLRCLE);
+
+ if (column != -1 || page_addr != -1) {
+ au1550_hwcontrol(mtd, NAND_CTL_SETALE);
+
+ /* Serially input address */
+ if (column != -1) {
+ /* Adjust columns for 16 bit buswidth */
+ if (this->options & NAND_BUSWIDTH_16 &&
+ !nand_opcode_8bits(command))
+ column >>= 1;
+ ctx->write_byte(mtd, column);
+ }
+ if (page_addr != -1) {
+ ctx->write_byte(mtd, (u8)(page_addr & 0xff));
+
+ if (command == NAND_CMD_READ0 ||
+ command == NAND_CMD_READ1 ||
+ command == NAND_CMD_READOOB) {
+ /*
+ * NAND controller will release -CE after
+ * the last address byte is written, so we'll
+ * have to forcibly assert it. No interrupts
+ * are allowed while we do this as we don't
+ * want the NOR flash or PCMCIA drivers to
+ * steal our precious bytes of data...
+ */
+ ce_override = 1;
+ local_irq_save(flags);
+ au1550_hwcontrol(mtd, NAND_CTL_SETNCE);
+ }
+
+ ctx->write_byte(mtd, (u8)(page_addr >> 8));
+
+ if (this->options & NAND_ROW_ADDR_3)
+ ctx->write_byte(mtd,
+ ((page_addr >> 16) & 0x0f));
+ }
+ /* Latch in address */
+ au1550_hwcontrol(mtd, NAND_CTL_CLRALE);
+ }
+
+ /*
+ * Program and erase have their own busy handlers.
+ * Status and sequential in need no delay.
+ */
+ switch (command) {
+
+ case NAND_CMD_PAGEPROG:
+ case NAND_CMD_ERASE1:
+ case NAND_CMD_ERASE2:
+ case NAND_CMD_SEQIN:
+ case NAND_CMD_STATUS:
+ return;
+
+ case NAND_CMD_RESET:
+ break;
+
+ case NAND_CMD_READ0:
+ case NAND_CMD_READ1:
+ case NAND_CMD_READOOB:
+ /* Check if we're really driving -CE low (just in case) */
+ if (unlikely(!ce_override))
+ break;
+
+ /* Apply a short delay always to ensure that we do wait tWB. */
+ ndelay(100);
+ /* Wait for a chip to become ready... */
+ for (i = this->chip_delay; !this->dev_ready(mtd) && i > 0; --i)
+ udelay(1);
+
+ /* Release -CE and re-enable interrupts. */
+ au1550_hwcontrol(mtd, NAND_CTL_CLRNCE);
+ local_irq_restore(flags);
+ return;
+ }
+ /* Apply this short delay always to ensure that we do wait tWB. */
+ ndelay(100);
+
+ while(!this->dev_ready(mtd));
+}
+
+static int find_nand_cs(unsigned long nand_base)
+{
+ void __iomem *base =
+ (void __iomem *)KSEG1ADDR(AU1000_STATIC_MEM_PHYS_ADDR);
+ unsigned long addr, staddr, start, mask, end;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ addr = 0x1000 + (i * 0x10); /* CSx */
+ staddr = __raw_readl(base + addr + 0x08); /* STADDRx */
+ /* figure out the decoded range of this CS */
+ start = (staddr << 4) & 0xfffc0000;
+ mask = (staddr << 18) & 0xfffc0000;
+ end = (start | (start - 1)) & ~(start ^ mask);
+ if ((nand_base >= start) && (nand_base < end))
+ return i;
+ }
+
+ return -ENODEV;
+}
+
+static int au1550nd_probe(struct platform_device *pdev)
+{
+ struct au1550nd_platdata *pd;
+ struct au1550nd_ctx *ctx;
+ struct nand_chip *this;
+ struct mtd_info *mtd;
+ struct resource *r;
+ int ret, cs;
+
+ pd = dev_get_platdata(&pdev->dev);
+ if (!pd) {
+ dev_err(&pdev->dev, "missing platform data\n");
+ return -ENODEV;
+ }
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "no NAND memory resource\n");
+ ret = -ENODEV;
+ goto out1;
+ }
+ if (request_mem_region(r->start, resource_size(r), "au1550-nand")) {
+ dev_err(&pdev->dev, "cannot claim NAND memory area\n");
+ ret = -ENOMEM;
+ goto out1;
+ }
+
+ ctx->base = ioremap_nocache(r->start, 0x1000);
+ if (!ctx->base) {
+ dev_err(&pdev->dev, "cannot remap NAND memory area\n");
+ ret = -ENODEV;
+ goto out2;
+ }
+
+ this = &ctx->chip;
+ mtd = nand_to_mtd(this);
+ mtd->dev.parent = &pdev->dev;
+
+ /* figure out which CS# r->start belongs to */
+ cs = find_nand_cs(r->start);
+ if (cs < 0) {
+ dev_err(&pdev->dev, "cannot detect NAND chipselect\n");
+ ret = -ENODEV;
+ goto out3;
+ }
+ ctx->cs = cs;
+
+ this->dev_ready = au1550_device_ready;
+ this->select_chip = au1550_select_chip;
+ this->cmdfunc = au1550_command;
+
+ /* 30 us command delay time */
+ this->chip_delay = 30;
+ this->ecc.mode = NAND_ECC_SOFT;
+ this->ecc.algo = NAND_ECC_HAMMING;
+
+ if (pd->devwidth)
+ this->options |= NAND_BUSWIDTH_16;
+
+ this->read_byte = (pd->devwidth) ? au_read_byte16 : au_read_byte;
+ ctx->write_byte = (pd->devwidth) ? au_write_byte16 : au_write_byte;
+ this->read_word = au_read_word;
+ this->write_buf = (pd->devwidth) ? au_write_buf16 : au_write_buf;
+ this->read_buf = (pd->devwidth) ? au_read_buf16 : au_read_buf;
+
+ ret = nand_scan(mtd, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "NAND scan failed with %d\n", ret);
+ goto out3;
+ }
+
+ mtd_device_register(mtd, pd->parts, pd->num_parts);
+
+ platform_set_drvdata(pdev, ctx);
+
+ return 0;
+
+out3:
+ iounmap(ctx->base);
+out2:
+ release_mem_region(r->start, resource_size(r));
+out1:
+ kfree(ctx);
+ return ret;
+}
+
+static int au1550nd_remove(struct platform_device *pdev)
+{
+ struct au1550nd_ctx *ctx = platform_get_drvdata(pdev);
+ struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ nand_release(nand_to_mtd(&ctx->chip));
+ iounmap(ctx->base);
+ release_mem_region(r->start, 0x1000);
+ kfree(ctx);
+ return 0;
+}
+
+static struct platform_driver au1550nd_driver = {
+ .driver = {
+ .name = "au1550-nand",
+ },
+ .probe = au1550nd_probe,
+ .remove = au1550nd_remove,
+};
+
+module_platform_driver(au1550nd_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Embedded Edge, LLC");
+MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on Pb1550 board");
diff --git a/drivers/mtd/nand/bcm47xxnflash/Makefile b/drivers/mtd/nand/raw/bcm47xxnflash/Makefile
index f05b119e134b..f05b119e134b 100644
--- a/drivers/mtd/nand/bcm47xxnflash/Makefile
+++ b/drivers/mtd/nand/raw/bcm47xxnflash/Makefile
diff --git a/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h b/drivers/mtd/nand/raw/bcm47xxnflash/bcm47xxnflash.h
index 201b9baa52a0..201b9baa52a0 100644
--- a/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h
+++ b/drivers/mtd/nand/raw/bcm47xxnflash/bcm47xxnflash.h
diff --git a/drivers/mtd/nand/bcm47xxnflash/main.c b/drivers/mtd/nand/raw/bcm47xxnflash/main.c
index fb31429b70a9..fb31429b70a9 100644
--- a/drivers/mtd/nand/bcm47xxnflash/main.c
+++ b/drivers/mtd/nand/raw/bcm47xxnflash/main.c
diff --git a/drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c b/drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c
new file mode 100644
index 000000000000..60874de430eb
--- /dev/null
+++ b/drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c
@@ -0,0 +1,456 @@
+/*
+ * BCM47XX NAND flash driver
+ *
+ * Copyright (C) 2012 Rafał Miłecki <zajec5@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.
+ *
+ */
+
+#include "bcm47xxnflash.h"
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/bcma/bcma.h>
+
+/* Broadcom uses 1'000'000 but it seems to be too many. Tests on WNDR4500 has
+ * shown ~1000 retries as maxiumum. */
+#define NFLASH_READY_RETRIES 10000
+
+#define NFLASH_SECTOR_SIZE 512
+
+#define NCTL_CMD0 0x00010000
+#define NCTL_COL 0x00020000 /* Update column with value from BCMA_CC_NFLASH_COL_ADDR */
+#define NCTL_ROW 0x00040000 /* Update row (page) with value from BCMA_CC_NFLASH_ROW_ADDR */
+#define NCTL_CMD1W 0x00080000
+#define NCTL_READ 0x00100000
+#define NCTL_WRITE 0x00200000
+#define NCTL_SPECADDR 0x01000000
+#define NCTL_READY 0x04000000
+#define NCTL_ERR 0x08000000
+#define NCTL_CSA 0x40000000
+#define NCTL_START 0x80000000
+
+/**************************************************
+ * Various helpers
+ **************************************************/
+
+static inline u8 bcm47xxnflash_ops_bcm4706_ns_to_cycle(u16 ns, u16 clock)
+{
+ return ((ns * 1000 * clock) / 1000000) + 1;
+}
+
+static int bcm47xxnflash_ops_bcm4706_ctl_cmd(struct bcma_drv_cc *cc, u32 code)
+{
+ int i = 0;
+
+ bcma_cc_write32(cc, BCMA_CC_NFLASH_CTL, NCTL_START | code);
+ for (i = 0; i < NFLASH_READY_RETRIES; i++) {
+ if (!(bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) & NCTL_START)) {
+ i = 0;
+ break;
+ }
+ }
+ if (i) {
+ pr_err("NFLASH control command not ready!\n");
+ return -EBUSY;
+ }
+ return 0;
+}
+
+static int bcm47xxnflash_ops_bcm4706_poll(struct bcma_drv_cc *cc)
+{
+ int i;
+
+ for (i = 0; i < NFLASH_READY_RETRIES; i++) {
+ if (bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) & NCTL_READY) {
+ if (bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) &
+ BCMA_CC_NFLASH_CTL_ERR) {
+ pr_err("Error on polling\n");
+ return -EBUSY;
+ } else {
+ return 0;
+ }
+ }
+ }
+
+ pr_err("Polling timeout!\n");
+ return -EBUSY;
+}
+
+/**************************************************
+ * R/W
+ **************************************************/
+
+static void bcm47xxnflash_ops_bcm4706_read(struct mtd_info *mtd, uint8_t *buf,
+ int len)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
+
+ u32 ctlcode;
+ u32 *dest = (u32 *)buf;
+ int i;
+ int toread;
+
+ BUG_ON(b47n->curr_page_addr & ~nand_chip->pagemask);
+ /* Don't validate column using nand_chip->page_shift, it may be bigger
+ * when accessing OOB */
+
+ while (len) {
+ /* We can read maximum of 0x200 bytes at once */
+ toread = min(len, 0x200);
+
+ /* Set page and column */
+ bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_COL_ADDR,
+ b47n->curr_column);
+ bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_ROW_ADDR,
+ b47n->curr_page_addr);
+
+ /* Prepare to read */
+ ctlcode = NCTL_CSA | NCTL_CMD1W | NCTL_ROW | NCTL_COL |
+ NCTL_CMD0;
+ ctlcode |= NAND_CMD_READSTART << 8;
+ if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, ctlcode))
+ return;
+ if (bcm47xxnflash_ops_bcm4706_poll(b47n->cc))
+ return;
+
+ /* Eventually read some data :) */
+ for (i = 0; i < toread; i += 4, dest++) {
+ ctlcode = NCTL_CSA | 0x30000000 | NCTL_READ;
+ if (i == toread - 4) /* Last read goes without that */
+ ctlcode &= ~NCTL_CSA;
+ if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc,
+ ctlcode))
+ return;
+ *dest = bcma_cc_read32(b47n->cc, BCMA_CC_NFLASH_DATA);
+ }
+
+ b47n->curr_column += toread;
+ len -= toread;
+ }
+}
+
+static void bcm47xxnflash_ops_bcm4706_write(struct mtd_info *mtd,
+ const uint8_t *buf, int len)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
+ struct bcma_drv_cc *cc = b47n->cc;
+
+ u32 ctlcode;
+ const u32 *data = (u32 *)buf;
+ int i;
+
+ BUG_ON(b47n->curr_page_addr & ~nand_chip->pagemask);
+ /* Don't validate column using nand_chip->page_shift, it may be bigger
+ * when accessing OOB */
+
+ for (i = 0; i < len; i += 4, data++) {
+ bcma_cc_write32(cc, BCMA_CC_NFLASH_DATA, *data);
+
+ ctlcode = NCTL_CSA | 0x30000000 | NCTL_WRITE;
+ if (i == len - 4) /* Last read goes without that */
+ ctlcode &= ~NCTL_CSA;
+ if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode)) {
+ pr_err("%s ctl_cmd didn't work!\n", __func__);
+ return;
+ }
+ }
+
+ b47n->curr_column += len;
+}
+
+/**************************************************
+ * NAND chip ops
+ **************************************************/
+
+static void bcm47xxnflash_ops_bcm4706_cmd_ctrl(struct mtd_info *mtd, int cmd,
+ unsigned int ctrl)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
+ u32 code = 0;
+
+ if (cmd == NAND_CMD_NONE)
+ return;
+
+ if (cmd & NAND_CTRL_CLE)
+ code = cmd | NCTL_CMD0;
+
+ /* nCS is not needed for reset command */
+ if (cmd != NAND_CMD_RESET)
+ code |= NCTL_CSA;
+
+ bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, code);
+}
+
+/* Default nand_select_chip calls cmd_ctrl, which is not used in BCM4706 */
+static void bcm47xxnflash_ops_bcm4706_select_chip(struct mtd_info *mtd,
+ int chip)
+{
+ return;
+}
+
+static int bcm47xxnflash_ops_bcm4706_dev_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
+
+ return !!(bcma_cc_read32(b47n->cc, BCMA_CC_NFLASH_CTL) & NCTL_READY);
+}
+
+/*
+ * Default nand_command and nand_command_lp don't match BCM4706 hardware layout.
+ * For example, reading chip id is performed in a non-standard way.
+ * Setting column and page is also handled differently, we use a special
+ * registers of ChipCommon core. Hacking cmd_ctrl to understand and convert
+ * standard commands would be much more complicated.
+ */
+static void bcm47xxnflash_ops_bcm4706_cmdfunc(struct mtd_info *mtd,
+ unsigned command, int column,
+ int page_addr)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
+ struct bcma_drv_cc *cc = b47n->cc;
+ u32 ctlcode;
+ int i;
+
+ if (column != -1)
+ b47n->curr_column = column;
+ if (page_addr != -1)
+ b47n->curr_page_addr = page_addr;
+
+ switch (command) {
+ case NAND_CMD_RESET:
+ nand_chip->cmd_ctrl(mtd, command, NAND_CTRL_CLE);
+
+ ndelay(100);
+ nand_wait_ready(mtd);
+ break;
+ case NAND_CMD_READID:
+ ctlcode = NCTL_CSA | 0x01000000 | NCTL_CMD1W | NCTL_CMD0;
+ ctlcode |= NAND_CMD_READID;
+ if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, ctlcode)) {
+ pr_err("READID error\n");
+ break;
+ }
+
+ /*
+ * Reading is specific, last one has to go without NCTL_CSA
+ * bit. We don't know how many reads NAND subsystem is going
+ * to perform, so cache everything.
+ */
+ for (i = 0; i < ARRAY_SIZE(b47n->id_data); i++) {
+ ctlcode = NCTL_CSA | NCTL_READ;
+ if (i == ARRAY_SIZE(b47n->id_data) - 1)
+ ctlcode &= ~NCTL_CSA;
+ if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc,
+ ctlcode)) {
+ pr_err("READID error\n");
+ break;
+ }
+ b47n->id_data[i] =
+ bcma_cc_read32(b47n->cc, BCMA_CC_NFLASH_DATA)
+ & 0xFF;
+ }
+
+ break;
+ case NAND_CMD_STATUS:
+ ctlcode = NCTL_CSA | NCTL_CMD0 | NAND_CMD_STATUS;
+ if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode))
+ pr_err("STATUS command error\n");
+ break;
+ case NAND_CMD_READ0:
+ break;
+ case NAND_CMD_READOOB:
+ if (page_addr != -1)
+ b47n->curr_column += mtd->writesize;
+ break;
+ case NAND_CMD_ERASE1:
+ bcma_cc_write32(cc, BCMA_CC_NFLASH_ROW_ADDR,
+ b47n->curr_page_addr);
+ ctlcode = NCTL_ROW | NCTL_CMD1W | NCTL_CMD0 |
+ NAND_CMD_ERASE1 | (NAND_CMD_ERASE2 << 8);
+ if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode))
+ pr_err("ERASE1 failed\n");
+ break;
+ case NAND_CMD_ERASE2:
+ break;
+ case NAND_CMD_SEQIN:
+ /* Set page and column */
+ bcma_cc_write32(cc, BCMA_CC_NFLASH_COL_ADDR,
+ b47n->curr_column);
+ bcma_cc_write32(cc, BCMA_CC_NFLASH_ROW_ADDR,
+ b47n->curr_page_addr);
+
+ /* Prepare to write */
+ ctlcode = 0x40000000 | NCTL_ROW | NCTL_COL | NCTL_CMD0;
+ ctlcode |= NAND_CMD_SEQIN;
+ if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode))
+ pr_err("SEQIN failed\n");
+ break;
+ case NAND_CMD_PAGEPROG:
+ if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, NCTL_CMD0 |
+ NAND_CMD_PAGEPROG))
+ pr_err("PAGEPROG failed\n");
+ if (bcm47xxnflash_ops_bcm4706_poll(cc))
+ pr_err("PAGEPROG not ready\n");
+ break;
+ default:
+ pr_err("Command 0x%X unsupported\n", command);
+ break;
+ }
+ b47n->curr_command = command;
+}
+
+static u8 bcm47xxnflash_ops_bcm4706_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
+ struct bcma_drv_cc *cc = b47n->cc;
+ u32 tmp = 0;
+
+ switch (b47n->curr_command) {
+ case NAND_CMD_READID:
+ if (b47n->curr_column >= ARRAY_SIZE(b47n->id_data)) {
+ pr_err("Requested invalid id_data: %d\n",
+ b47n->curr_column);
+ return 0;
+ }
+ return b47n->id_data[b47n->curr_column++];
+ case NAND_CMD_STATUS:
+ if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, NCTL_READ))
+ return 0;
+ return bcma_cc_read32(cc, BCMA_CC_NFLASH_DATA) & 0xff;
+ case NAND_CMD_READOOB:
+ bcm47xxnflash_ops_bcm4706_read(mtd, (u8 *)&tmp, 4);
+ return tmp & 0xFF;
+ }
+
+ pr_err("Invalid command for byte read: 0x%X\n", b47n->curr_command);
+ return 0;
+}
+
+static void bcm47xxnflash_ops_bcm4706_read_buf(struct mtd_info *mtd,
+ uint8_t *buf, int len)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
+
+ switch (b47n->curr_command) {
+ case NAND_CMD_READ0:
+ case NAND_CMD_READOOB:
+ bcm47xxnflash_ops_bcm4706_read(mtd, buf, len);
+ return;
+ }
+
+ pr_err("Invalid command for buf read: 0x%X\n", b47n->curr_command);
+}
+
+static void bcm47xxnflash_ops_bcm4706_write_buf(struct mtd_info *mtd,
+ const uint8_t *buf, int len)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
+
+ switch (b47n->curr_command) {
+ case NAND_CMD_SEQIN:
+ bcm47xxnflash_ops_bcm4706_write(mtd, buf, len);
+ return;
+ }
+
+ pr_err("Invalid command for buf write: 0x%X\n", b47n->curr_command);
+}
+
+/**************************************************
+ * Init
+ **************************************************/
+
+int bcm47xxnflash_ops_bcm4706_init(struct bcm47xxnflash *b47n)
+{
+ struct nand_chip *nand_chip = (struct nand_chip *)&b47n->nand_chip;
+ int err;
+ u32 freq;
+ u16 clock;
+ u8 w0, w1, w2, w3, w4;
+
+ unsigned long chipsize; /* MiB */
+ u8 tbits, col_bits, col_size, row_bits, row_bsize;
+ u32 val;
+
+ b47n->nand_chip.select_chip = bcm47xxnflash_ops_bcm4706_select_chip;
+ nand_chip->cmd_ctrl = bcm47xxnflash_ops_bcm4706_cmd_ctrl;
+ nand_chip->dev_ready = bcm47xxnflash_ops_bcm4706_dev_ready;
+ b47n->nand_chip.cmdfunc = bcm47xxnflash_ops_bcm4706_cmdfunc;
+ b47n->nand_chip.read_byte = bcm47xxnflash_ops_bcm4706_read_byte;
+ b47n->nand_chip.read_buf = bcm47xxnflash_ops_bcm4706_read_buf;
+ b47n->nand_chip.write_buf = bcm47xxnflash_ops_bcm4706_write_buf;
+ b47n->nand_chip.set_features = nand_get_set_features_notsupp;
+ b47n->nand_chip.get_features = nand_get_set_features_notsupp;
+
+ nand_chip->chip_delay = 50;
+ b47n->nand_chip.bbt_options = NAND_BBT_USE_FLASH;
+ b47n->nand_chip.ecc.mode = NAND_ECC_NONE; /* TODO: implement ECC */
+
+ /* Enable NAND flash access */
+ bcma_cc_set32(b47n->cc, BCMA_CC_4706_FLASHSCFG,
+ BCMA_CC_4706_FLASHSCFG_NF1);
+
+ /* Configure wait counters */
+ if (b47n->cc->status & BCMA_CC_CHIPST_4706_PKG_OPTION) {
+ /* 400 MHz */
+ freq = 400000000 / 4;
+ } else {
+ freq = bcma_chipco_pll_read(b47n->cc, 4);
+ freq = (freq & 0xFFF) >> 3;
+ /* Fixed reference clock 25 MHz and m = 2 */
+ freq = (freq * 25000000 / 2) / 4;
+ }
+ clock = freq / 1000000;
+ w0 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(15, clock);
+ w1 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(20, clock);
+ w2 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(10, clock);
+ w3 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(10, clock);
+ w4 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(100, clock);
+ bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_WAITCNT0,
+ (w4 << 24 | w3 << 18 | w2 << 12 | w1 << 6 | w0));
+
+ /* Scan NAND */
+ err = nand_scan(nand_to_mtd(&b47n->nand_chip), 1);
+ if (err) {
+ pr_err("Could not scan NAND flash: %d\n", err);
+ goto exit;
+ }
+
+ /* Configure FLASH */
+ chipsize = b47n->nand_chip.chipsize >> 20;
+ tbits = ffs(chipsize); /* find first bit set */
+ if (!tbits || tbits != fls(chipsize)) {
+ pr_err("Invalid flash size: 0x%lX\n", chipsize);
+ err = -ENOTSUPP;
+ goto exit;
+ }
+ tbits += 19; /* Broadcom increases *index* by 20, we increase *pos* */
+
+ col_bits = b47n->nand_chip.page_shift + 1;
+ col_size = (col_bits + 7) / 8;
+
+ row_bits = tbits - col_bits + 1;
+ row_bsize = (row_bits + 7) / 8;
+
+ val = ((row_bsize - 1) << 6) | ((col_size - 1) << 4) | 2;
+ bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_CONF, val);
+
+exit:
+ if (err)
+ bcma_cc_mask32(b47n->cc, BCMA_CC_4706_FLASHSCFG,
+ ~BCMA_CC_4706_FLASHSCFG_NF1);
+ return err;
+}
diff --git a/drivers/mtd/nand/brcmnand/Makefile b/drivers/mtd/nand/raw/brcmnand/Makefile
index 195b845e48b8..195b845e48b8 100644
--- a/drivers/mtd/nand/brcmnand/Makefile
+++ b/drivers/mtd/nand/raw/brcmnand/Makefile
diff --git a/drivers/mtd/nand/brcmnand/bcm63138_nand.c b/drivers/mtd/nand/raw/brcmnand/bcm63138_nand.c
index 59444b3a697d..59444b3a697d 100644
--- a/drivers/mtd/nand/brcmnand/bcm63138_nand.c
+++ b/drivers/mtd/nand/raw/brcmnand/bcm63138_nand.c
diff --git a/drivers/mtd/nand/brcmnand/bcm6368_nand.c b/drivers/mtd/nand/raw/brcmnand/bcm6368_nand.c
index 34c91b0e1e69..34c91b0e1e69 100644
--- a/drivers/mtd/nand/brcmnand/bcm6368_nand.c
+++ b/drivers/mtd/nand/raw/brcmnand/bcm6368_nand.c
diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c
new file mode 100644
index 000000000000..1306aaa7a8bf
--- /dev/null
+++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c
@@ -0,0 +1,2624 @@
+/*
+ * Copyright © 2010-2015 Broadcom Corporation
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/dma-mapping.h>
+#include <linux/ioport.h>
+#include <linux/bug.h>
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/mm.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/log2.h>
+
+#include "brcmnand.h"
+
+/*
+ * This flag controls if WP stays on between erase/write commands to mitigate
+ * flash corruption due to power glitches. Values:
+ * 0: NAND_WP is not used or not available
+ * 1: NAND_WP is set by default, cleared for erase/write operations
+ * 2: NAND_WP is always cleared
+ */
+static int wp_on = 1;
+module_param(wp_on, int, 0444);
+
+/***********************************************************************
+ * Definitions
+ ***********************************************************************/
+
+#define DRV_NAME "brcmnand"
+
+#define CMD_NULL 0x00
+#define CMD_PAGE_READ 0x01
+#define CMD_SPARE_AREA_READ 0x02
+#define CMD_STATUS_READ 0x03
+#define CMD_PROGRAM_PAGE 0x04
+#define CMD_PROGRAM_SPARE_AREA 0x05
+#define CMD_COPY_BACK 0x06
+#define CMD_DEVICE_ID_READ 0x07
+#define CMD_BLOCK_ERASE 0x08
+#define CMD_FLASH_RESET 0x09
+#define CMD_BLOCKS_LOCK 0x0a
+#define CMD_BLOCKS_LOCK_DOWN 0x0b
+#define CMD_BLOCKS_UNLOCK 0x0c
+#define CMD_READ_BLOCKS_LOCK_STATUS 0x0d
+#define CMD_PARAMETER_READ 0x0e
+#define CMD_PARAMETER_CHANGE_COL 0x0f
+#define CMD_LOW_LEVEL_OP 0x10
+
+struct brcm_nand_dma_desc {
+ u32 next_desc;
+ u32 next_desc_ext;
+ u32 cmd_irq;
+ u32 dram_addr;
+ u32 dram_addr_ext;
+ u32 tfr_len;
+ u32 total_len;
+ u32 flash_addr;
+ u32 flash_addr_ext;
+ u32 cs;
+ u32 pad2[5];
+ u32 status_valid;
+} __packed;
+
+/* Bitfields for brcm_nand_dma_desc::status_valid */
+#define FLASH_DMA_ECC_ERROR (1 << 8)
+#define FLASH_DMA_CORR_ERROR (1 << 9)
+
+/* 512B flash cache in the NAND controller HW */
+#define FC_SHIFT 9U
+#define FC_BYTES 512U
+#define FC_WORDS (FC_BYTES >> 2)
+
+#define BRCMNAND_MIN_PAGESIZE 512
+#define BRCMNAND_MIN_BLOCKSIZE (8 * 1024)
+#define BRCMNAND_MIN_DEVSIZE (4ULL * 1024 * 1024)
+
+#define NAND_CTRL_RDY (INTFC_CTLR_READY | INTFC_FLASH_READY)
+#define NAND_POLL_STATUS_TIMEOUT_MS 100
+
+/* Controller feature flags */
+enum {
+ BRCMNAND_HAS_1K_SECTORS = BIT(0),
+ BRCMNAND_HAS_PREFETCH = BIT(1),
+ BRCMNAND_HAS_CACHE_MODE = BIT(2),
+ BRCMNAND_HAS_WP = BIT(3),
+};
+
+struct brcmnand_controller {
+ struct device *dev;
+ struct nand_hw_control controller;
+ void __iomem *nand_base;
+ void __iomem *nand_fc; /* flash cache */
+ void __iomem *flash_dma_base;
+ unsigned int irq;
+ unsigned int dma_irq;
+ int nand_version;
+
+ /* Some SoCs provide custom interrupt status register(s) */
+ struct brcmnand_soc *soc;
+
+ /* Some SoCs have a gateable clock for the controller */
+ struct clk *clk;
+
+ int cmd_pending;
+ bool dma_pending;
+ struct completion done;
+ struct completion dma_done;
+
+ /* List of NAND hosts (one for each chip-select) */
+ struct list_head host_list;
+
+ struct brcm_nand_dma_desc *dma_desc;
+ dma_addr_t dma_pa;
+
+ /* in-memory cache of the FLASH_CACHE, used only for some commands */
+ u8 flash_cache[FC_BYTES];
+
+ /* Controller revision details */
+ const u16 *reg_offsets;
+ unsigned int reg_spacing; /* between CS1, CS2, ... regs */
+ const u8 *cs_offsets; /* within each chip-select */
+ const u8 *cs0_offsets; /* within CS0, if different */
+ unsigned int max_block_size;
+ const unsigned int *block_sizes;
+ unsigned int max_page_size;
+ const unsigned int *page_sizes;
+ unsigned int max_oob;
+ u32 features;
+
+ /* for low-power standby/resume only */
+ u32 nand_cs_nand_select;
+ u32 nand_cs_nand_xor;
+ u32 corr_stat_threshold;
+ u32 flash_dma_mode;
+};
+
+struct brcmnand_cfg {
+ u64 device_size;
+ unsigned int block_size;
+ unsigned int page_size;
+ unsigned int spare_area_size;
+ unsigned int device_width;
+ unsigned int col_adr_bytes;
+ unsigned int blk_adr_bytes;
+ unsigned int ful_adr_bytes;
+ unsigned int sector_size_1k;
+ unsigned int ecc_level;
+ /* use for low-power standby/resume only */
+ u32 acc_control;
+ u32 config;
+ u32 config_ext;
+ u32 timing_1;
+ u32 timing_2;
+};
+
+struct brcmnand_host {
+ struct list_head node;
+
+ struct nand_chip chip;
+ struct platform_device *pdev;
+ int cs;
+
+ unsigned int last_cmd;
+ unsigned int last_byte;
+ u64 last_addr;
+ struct brcmnand_cfg hwcfg;
+ struct brcmnand_controller *ctrl;
+};
+
+enum brcmnand_reg {
+ BRCMNAND_CMD_START = 0,
+ BRCMNAND_CMD_EXT_ADDRESS,
+ BRCMNAND_CMD_ADDRESS,
+ BRCMNAND_INTFC_STATUS,
+ BRCMNAND_CS_SELECT,
+ BRCMNAND_CS_XOR,
+ BRCMNAND_LL_OP,
+ BRCMNAND_CS0_BASE,
+ BRCMNAND_CS1_BASE, /* CS1 regs, if non-contiguous */
+ BRCMNAND_CORR_THRESHOLD,
+ BRCMNAND_CORR_THRESHOLD_EXT,
+ BRCMNAND_UNCORR_COUNT,
+ BRCMNAND_CORR_COUNT,
+ BRCMNAND_CORR_EXT_ADDR,
+ BRCMNAND_CORR_ADDR,
+ BRCMNAND_UNCORR_EXT_ADDR,
+ BRCMNAND_UNCORR_ADDR,
+ BRCMNAND_SEMAPHORE,
+ BRCMNAND_ID,
+ BRCMNAND_ID_EXT,
+ BRCMNAND_LL_RDATA,
+ BRCMNAND_OOB_READ_BASE,
+ BRCMNAND_OOB_READ_10_BASE, /* offset 0x10, if non-contiguous */
+ BRCMNAND_OOB_WRITE_BASE,
+ BRCMNAND_OOB_WRITE_10_BASE, /* offset 0x10, if non-contiguous */
+ BRCMNAND_FC_BASE,
+};
+
+/* BRCMNAND v4.0 */
+static const u16 brcmnand_regs_v40[] = {
+ [BRCMNAND_CMD_START] = 0x04,
+ [BRCMNAND_CMD_EXT_ADDRESS] = 0x08,
+ [BRCMNAND_CMD_ADDRESS] = 0x0c,
+ [BRCMNAND_INTFC_STATUS] = 0x6c,
+ [BRCMNAND_CS_SELECT] = 0x14,
+ [BRCMNAND_CS_XOR] = 0x18,
+ [BRCMNAND_LL_OP] = 0x178,
+ [BRCMNAND_CS0_BASE] = 0x40,
+ [BRCMNAND_CS1_BASE] = 0xd0,
+ [BRCMNAND_CORR_THRESHOLD] = 0x84,
+ [BRCMNAND_CORR_THRESHOLD_EXT] = 0,
+ [BRCMNAND_UNCORR_COUNT] = 0,
+ [BRCMNAND_CORR_COUNT] = 0,
+ [BRCMNAND_CORR_EXT_ADDR] = 0x70,
+ [BRCMNAND_CORR_ADDR] = 0x74,
+ [BRCMNAND_UNCORR_EXT_ADDR] = 0x78,
+ [BRCMNAND_UNCORR_ADDR] = 0x7c,
+ [BRCMNAND_SEMAPHORE] = 0x58,
+ [BRCMNAND_ID] = 0x60,
+ [BRCMNAND_ID_EXT] = 0x64,
+ [BRCMNAND_LL_RDATA] = 0x17c,
+ [BRCMNAND_OOB_READ_BASE] = 0x20,
+ [BRCMNAND_OOB_READ_10_BASE] = 0x130,
+ [BRCMNAND_OOB_WRITE_BASE] = 0x30,
+ [BRCMNAND_OOB_WRITE_10_BASE] = 0,
+ [BRCMNAND_FC_BASE] = 0x200,
+};
+
+/* BRCMNAND v5.0 */
+static const u16 brcmnand_regs_v50[] = {
+ [BRCMNAND_CMD_START] = 0x04,
+ [BRCMNAND_CMD_EXT_ADDRESS] = 0x08,
+ [BRCMNAND_CMD_ADDRESS] = 0x0c,
+ [BRCMNAND_INTFC_STATUS] = 0x6c,
+ [BRCMNAND_CS_SELECT] = 0x14,
+ [BRCMNAND_CS_XOR] = 0x18,
+ [BRCMNAND_LL_OP] = 0x178,
+ [BRCMNAND_CS0_BASE] = 0x40,
+ [BRCMNAND_CS1_BASE] = 0xd0,
+ [BRCMNAND_CORR_THRESHOLD] = 0x84,
+ [BRCMNAND_CORR_THRESHOLD_EXT] = 0,
+ [BRCMNAND_UNCORR_COUNT] = 0,
+ [BRCMNAND_CORR_COUNT] = 0,
+ [BRCMNAND_CORR_EXT_ADDR] = 0x70,
+ [BRCMNAND_CORR_ADDR] = 0x74,
+ [BRCMNAND_UNCORR_EXT_ADDR] = 0x78,
+ [BRCMNAND_UNCORR_ADDR] = 0x7c,
+ [BRCMNAND_SEMAPHORE] = 0x58,
+ [BRCMNAND_ID] = 0x60,
+ [BRCMNAND_ID_EXT] = 0x64,
+ [BRCMNAND_LL_RDATA] = 0x17c,
+ [BRCMNAND_OOB_READ_BASE] = 0x20,
+ [BRCMNAND_OOB_READ_10_BASE] = 0x130,
+ [BRCMNAND_OOB_WRITE_BASE] = 0x30,
+ [BRCMNAND_OOB_WRITE_10_BASE] = 0x140,
+ [BRCMNAND_FC_BASE] = 0x200,
+};
+
+/* BRCMNAND v6.0 - v7.1 */
+static const u16 brcmnand_regs_v60[] = {
+ [BRCMNAND_CMD_START] = 0x04,
+ [BRCMNAND_CMD_EXT_ADDRESS] = 0x08,
+ [BRCMNAND_CMD_ADDRESS] = 0x0c,
+ [BRCMNAND_INTFC_STATUS] = 0x14,
+ [BRCMNAND_CS_SELECT] = 0x18,
+ [BRCMNAND_CS_XOR] = 0x1c,
+ [BRCMNAND_LL_OP] = 0x20,
+ [BRCMNAND_CS0_BASE] = 0x50,
+ [BRCMNAND_CS1_BASE] = 0,
+ [BRCMNAND_CORR_THRESHOLD] = 0xc0,
+ [BRCMNAND_CORR_THRESHOLD_EXT] = 0xc4,
+ [BRCMNAND_UNCORR_COUNT] = 0xfc,
+ [BRCMNAND_CORR_COUNT] = 0x100,
+ [BRCMNAND_CORR_EXT_ADDR] = 0x10c,
+ [BRCMNAND_CORR_ADDR] = 0x110,
+ [BRCMNAND_UNCORR_EXT_ADDR] = 0x114,
+ [BRCMNAND_UNCORR_ADDR] = 0x118,
+ [BRCMNAND_SEMAPHORE] = 0x150,
+ [BRCMNAND_ID] = 0x194,
+ [BRCMNAND_ID_EXT] = 0x198,
+ [BRCMNAND_LL_RDATA] = 0x19c,
+ [BRCMNAND_OOB_READ_BASE] = 0x200,
+ [BRCMNAND_OOB_READ_10_BASE] = 0,
+ [BRCMNAND_OOB_WRITE_BASE] = 0x280,
+ [BRCMNAND_OOB_WRITE_10_BASE] = 0,
+ [BRCMNAND_FC_BASE] = 0x400,
+};
+
+/* BRCMNAND v7.1 */
+static const u16 brcmnand_regs_v71[] = {
+ [BRCMNAND_CMD_START] = 0x04,
+ [BRCMNAND_CMD_EXT_ADDRESS] = 0x08,
+ [BRCMNAND_CMD_ADDRESS] = 0x0c,
+ [BRCMNAND_INTFC_STATUS] = 0x14,
+ [BRCMNAND_CS_SELECT] = 0x18,
+ [BRCMNAND_CS_XOR] = 0x1c,
+ [BRCMNAND_LL_OP] = 0x20,
+ [BRCMNAND_CS0_BASE] = 0x50,
+ [BRCMNAND_CS1_BASE] = 0,
+ [BRCMNAND_CORR_THRESHOLD] = 0xdc,
+ [BRCMNAND_CORR_THRESHOLD_EXT] = 0xe0,
+ [BRCMNAND_UNCORR_COUNT] = 0xfc,
+ [BRCMNAND_CORR_COUNT] = 0x100,
+ [BRCMNAND_CORR_EXT_ADDR] = 0x10c,
+ [BRCMNAND_CORR_ADDR] = 0x110,
+ [BRCMNAND_UNCORR_EXT_ADDR] = 0x114,
+ [BRCMNAND_UNCORR_ADDR] = 0x118,
+ [BRCMNAND_SEMAPHORE] = 0x150,
+ [BRCMNAND_ID] = 0x194,
+ [BRCMNAND_ID_EXT] = 0x198,
+ [BRCMNAND_LL_RDATA] = 0x19c,
+ [BRCMNAND_OOB_READ_BASE] = 0x200,
+ [BRCMNAND_OOB_READ_10_BASE] = 0,
+ [BRCMNAND_OOB_WRITE_BASE] = 0x280,
+ [BRCMNAND_OOB_WRITE_10_BASE] = 0,
+ [BRCMNAND_FC_BASE] = 0x400,
+};
+
+/* BRCMNAND v7.2 */
+static const u16 brcmnand_regs_v72[] = {
+ [BRCMNAND_CMD_START] = 0x04,
+ [BRCMNAND_CMD_EXT_ADDRESS] = 0x08,
+ [BRCMNAND_CMD_ADDRESS] = 0x0c,
+ [BRCMNAND_INTFC_STATUS] = 0x14,
+ [BRCMNAND_CS_SELECT] = 0x18,
+ [BRCMNAND_CS_XOR] = 0x1c,
+ [BRCMNAND_LL_OP] = 0x20,
+ [BRCMNAND_CS0_BASE] = 0x50,
+ [BRCMNAND_CS1_BASE] = 0,
+ [BRCMNAND_CORR_THRESHOLD] = 0xdc,
+ [BRCMNAND_CORR_THRESHOLD_EXT] = 0xe0,
+ [BRCMNAND_UNCORR_COUNT] = 0xfc,
+ [BRCMNAND_CORR_COUNT] = 0x100,
+ [BRCMNAND_CORR_EXT_ADDR] = 0x10c,
+ [BRCMNAND_CORR_ADDR] = 0x110,
+ [BRCMNAND_UNCORR_EXT_ADDR] = 0x114,
+ [BRCMNAND_UNCORR_ADDR] = 0x118,
+ [BRCMNAND_SEMAPHORE] = 0x150,
+ [BRCMNAND_ID] = 0x194,
+ [BRCMNAND_ID_EXT] = 0x198,
+ [BRCMNAND_LL_RDATA] = 0x19c,
+ [BRCMNAND_OOB_READ_BASE] = 0x200,
+ [BRCMNAND_OOB_READ_10_BASE] = 0,
+ [BRCMNAND_OOB_WRITE_BASE] = 0x400,
+ [BRCMNAND_OOB_WRITE_10_BASE] = 0,
+ [BRCMNAND_FC_BASE] = 0x600,
+};
+
+enum brcmnand_cs_reg {
+ BRCMNAND_CS_CFG_EXT = 0,
+ BRCMNAND_CS_CFG,
+ BRCMNAND_CS_ACC_CONTROL,
+ BRCMNAND_CS_TIMING1,
+ BRCMNAND_CS_TIMING2,
+};
+
+/* Per chip-select offsets for v7.1 */
+static const u8 brcmnand_cs_offsets_v71[] = {
+ [BRCMNAND_CS_ACC_CONTROL] = 0x00,
+ [BRCMNAND_CS_CFG_EXT] = 0x04,
+ [BRCMNAND_CS_CFG] = 0x08,
+ [BRCMNAND_CS_TIMING1] = 0x0c,
+ [BRCMNAND_CS_TIMING2] = 0x10,
+};
+
+/* Per chip-select offsets for pre v7.1, except CS0 on <= v5.0 */
+static const u8 brcmnand_cs_offsets[] = {
+ [BRCMNAND_CS_ACC_CONTROL] = 0x00,
+ [BRCMNAND_CS_CFG_EXT] = 0x04,
+ [BRCMNAND_CS_CFG] = 0x04,
+ [BRCMNAND_CS_TIMING1] = 0x08,
+ [BRCMNAND_CS_TIMING2] = 0x0c,
+};
+
+/* Per chip-select offset for <= v5.0 on CS0 only */
+static const u8 brcmnand_cs_offsets_cs0[] = {
+ [BRCMNAND_CS_ACC_CONTROL] = 0x00,
+ [BRCMNAND_CS_CFG_EXT] = 0x08,
+ [BRCMNAND_CS_CFG] = 0x08,
+ [BRCMNAND_CS_TIMING1] = 0x10,
+ [BRCMNAND_CS_TIMING2] = 0x14,
+};
+
+/*
+ * Bitfields for the CFG and CFG_EXT registers. Pre-v7.1 controllers only had
+ * one config register, but once the bitfields overflowed, newer controllers
+ * (v7.1 and newer) added a CFG_EXT register and shuffled a few fields around.
+ */
+enum {
+ CFG_BLK_ADR_BYTES_SHIFT = 8,
+ CFG_COL_ADR_BYTES_SHIFT = 12,
+ CFG_FUL_ADR_BYTES_SHIFT = 16,
+ CFG_BUS_WIDTH_SHIFT = 23,
+ CFG_BUS_WIDTH = BIT(CFG_BUS_WIDTH_SHIFT),
+ CFG_DEVICE_SIZE_SHIFT = 24,
+
+ /* Only for pre-v7.1 (with no CFG_EXT register) */
+ CFG_PAGE_SIZE_SHIFT = 20,
+ CFG_BLK_SIZE_SHIFT = 28,
+
+ /* Only for v7.1+ (with CFG_EXT register) */
+ CFG_EXT_PAGE_SIZE_SHIFT = 0,
+ CFG_EXT_BLK_SIZE_SHIFT = 4,
+};
+
+/* BRCMNAND_INTFC_STATUS */
+enum {
+ INTFC_FLASH_STATUS = GENMASK(7, 0),
+
+ INTFC_ERASED = BIT(27),
+ INTFC_OOB_VALID = BIT(28),
+ INTFC_CACHE_VALID = BIT(29),
+ INTFC_FLASH_READY = BIT(30),
+ INTFC_CTLR_READY = BIT(31),
+};
+
+static inline u32 nand_readreg(struct brcmnand_controller *ctrl, u32 offs)
+{
+ return brcmnand_readl(ctrl->nand_base + offs);
+}
+
+static inline void nand_writereg(struct brcmnand_controller *ctrl, u32 offs,
+ u32 val)
+{
+ brcmnand_writel(val, ctrl->nand_base + offs);
+}
+
+static int brcmnand_revision_init(struct brcmnand_controller *ctrl)
+{
+ static const unsigned int block_sizes_v6[] = { 8, 16, 128, 256, 512, 1024, 2048, 0 };
+ static const unsigned int block_sizes_v4[] = { 16, 128, 8, 512, 256, 1024, 2048, 0 };
+ static const unsigned int page_sizes[] = { 512, 2048, 4096, 8192, 0 };
+
+ ctrl->nand_version = nand_readreg(ctrl, 0) & 0xffff;
+
+ /* Only support v4.0+? */
+ if (ctrl->nand_version < 0x0400) {
+ dev_err(ctrl->dev, "version %#x not supported\n",
+ ctrl->nand_version);
+ return -ENODEV;
+ }
+
+ /* Register offsets */
+ if (ctrl->nand_version >= 0x0702)
+ ctrl->reg_offsets = brcmnand_regs_v72;
+ else if (ctrl->nand_version >= 0x0701)
+ ctrl->reg_offsets = brcmnand_regs_v71;
+ else if (ctrl->nand_version >= 0x0600)
+ ctrl->reg_offsets = brcmnand_regs_v60;
+ else if (ctrl->nand_version >= 0x0500)
+ ctrl->reg_offsets = brcmnand_regs_v50;
+ else if (ctrl->nand_version >= 0x0400)
+ ctrl->reg_offsets = brcmnand_regs_v40;
+
+ /* Chip-select stride */
+ if (ctrl->nand_version >= 0x0701)
+ ctrl->reg_spacing = 0x14;
+ else
+ ctrl->reg_spacing = 0x10;
+
+ /* Per chip-select registers */
+ if (ctrl->nand_version >= 0x0701) {
+ ctrl->cs_offsets = brcmnand_cs_offsets_v71;
+ } else {
+ ctrl->cs_offsets = brcmnand_cs_offsets;
+
+ /* v5.0 and earlier has a different CS0 offset layout */
+ if (ctrl->nand_version <= 0x0500)
+ ctrl->cs0_offsets = brcmnand_cs_offsets_cs0;
+ }
+
+ /* Page / block sizes */
+ if (ctrl->nand_version >= 0x0701) {
+ /* >= v7.1 use nice power-of-2 values! */
+ ctrl->max_page_size = 16 * 1024;
+ ctrl->max_block_size = 2 * 1024 * 1024;
+ } else {
+ ctrl->page_sizes = page_sizes;
+ if (ctrl->nand_version >= 0x0600)
+ ctrl->block_sizes = block_sizes_v6;
+ else
+ ctrl->block_sizes = block_sizes_v4;
+
+ if (ctrl->nand_version < 0x0400) {
+ ctrl->max_page_size = 4096;
+ ctrl->max_block_size = 512 * 1024;
+ }
+ }
+
+ /* Maximum spare area sector size (per 512B) */
+ if (ctrl->nand_version >= 0x0702)
+ ctrl->max_oob = 128;
+ else if (ctrl->nand_version >= 0x0600)
+ ctrl->max_oob = 64;
+ else if (ctrl->nand_version >= 0x0500)
+ ctrl->max_oob = 32;
+ else
+ ctrl->max_oob = 16;
+
+ /* v6.0 and newer (except v6.1) have prefetch support */
+ if (ctrl->nand_version >= 0x0600 && ctrl->nand_version != 0x0601)
+ ctrl->features |= BRCMNAND_HAS_PREFETCH;
+
+ /*
+ * v6.x has cache mode, but it's implemented differently. Ignore it for
+ * now.
+ */
+ if (ctrl->nand_version >= 0x0700)
+ ctrl->features |= BRCMNAND_HAS_CACHE_MODE;
+
+ if (ctrl->nand_version >= 0x0500)
+ ctrl->features |= BRCMNAND_HAS_1K_SECTORS;
+
+ if (ctrl->nand_version >= 0x0700)
+ ctrl->features |= BRCMNAND_HAS_WP;
+ else if (of_property_read_bool(ctrl->dev->of_node, "brcm,nand-has-wp"))
+ ctrl->features |= BRCMNAND_HAS_WP;
+
+ return 0;
+}
+
+static inline u32 brcmnand_read_reg(struct brcmnand_controller *ctrl,
+ enum brcmnand_reg reg)
+{
+ u16 offs = ctrl->reg_offsets[reg];
+
+ if (offs)
+ return nand_readreg(ctrl, offs);
+ else
+ return 0;
+}
+
+static inline void brcmnand_write_reg(struct brcmnand_controller *ctrl,
+ enum brcmnand_reg reg, u32 val)
+{
+ u16 offs = ctrl->reg_offsets[reg];
+
+ if (offs)
+ nand_writereg(ctrl, offs, val);
+}
+
+static inline void brcmnand_rmw_reg(struct brcmnand_controller *ctrl,
+ enum brcmnand_reg reg, u32 mask, unsigned
+ int shift, u32 val)
+{
+ u32 tmp = brcmnand_read_reg(ctrl, reg);
+
+ tmp &= ~mask;
+ tmp |= val << shift;
+ brcmnand_write_reg(ctrl, reg, tmp);
+}
+
+static inline u32 brcmnand_read_fc(struct brcmnand_controller *ctrl, int word)
+{
+ return __raw_readl(ctrl->nand_fc + word * 4);
+}
+
+static inline void brcmnand_write_fc(struct brcmnand_controller *ctrl,
+ int word, u32 val)
+{
+ __raw_writel(val, ctrl->nand_fc + word * 4);
+}
+
+static inline u16 brcmnand_cs_offset(struct brcmnand_controller *ctrl, int cs,
+ enum brcmnand_cs_reg reg)
+{
+ u16 offs_cs0 = ctrl->reg_offsets[BRCMNAND_CS0_BASE];
+ u16 offs_cs1 = ctrl->reg_offsets[BRCMNAND_CS1_BASE];
+ u8 cs_offs;
+
+ if (cs == 0 && ctrl->cs0_offsets)
+ cs_offs = ctrl->cs0_offsets[reg];
+ else
+ cs_offs = ctrl->cs_offsets[reg];
+
+ if (cs && offs_cs1)
+ return offs_cs1 + (cs - 1) * ctrl->reg_spacing + cs_offs;
+
+ return offs_cs0 + cs * ctrl->reg_spacing + cs_offs;
+}
+
+static inline u32 brcmnand_count_corrected(struct brcmnand_controller *ctrl)
+{
+ if (ctrl->nand_version < 0x0600)
+ return 1;
+ return brcmnand_read_reg(ctrl, BRCMNAND_CORR_COUNT);
+}
+
+static void brcmnand_wr_corr_thresh(struct brcmnand_host *host, u8 val)
+{
+ struct brcmnand_controller *ctrl = host->ctrl;
+ unsigned int shift = 0, bits;
+ enum brcmnand_reg reg = BRCMNAND_CORR_THRESHOLD;
+ int cs = host->cs;
+
+ if (ctrl->nand_version >= 0x0702)
+ bits = 7;
+ else if (ctrl->nand_version >= 0x0600)
+ bits = 6;
+ else if (ctrl->nand_version >= 0x0500)
+ bits = 5;
+ else
+ bits = 4;
+
+ if (ctrl->nand_version >= 0x0702) {
+ if (cs >= 4)
+ reg = BRCMNAND_CORR_THRESHOLD_EXT;
+ shift = (cs % 4) * bits;
+ } else if (ctrl->nand_version >= 0x0600) {
+ if (cs >= 5)
+ reg = BRCMNAND_CORR_THRESHOLD_EXT;
+ shift = (cs % 5) * bits;
+ }
+ brcmnand_rmw_reg(ctrl, reg, (bits - 1) << shift, shift, val);
+}
+
+static inline int brcmnand_cmd_shift(struct brcmnand_controller *ctrl)
+{
+ if (ctrl->nand_version < 0x0602)
+ return 24;
+ return 0;
+}
+
+/***********************************************************************
+ * NAND ACC CONTROL bitfield
+ *
+ * Some bits have remained constant throughout hardware revision, while
+ * others have shifted around.
+ ***********************************************************************/
+
+/* Constant for all versions (where supported) */
+enum {
+ /* See BRCMNAND_HAS_CACHE_MODE */
+ ACC_CONTROL_CACHE_MODE = BIT(22),
+
+ /* See BRCMNAND_HAS_PREFETCH */
+ ACC_CONTROL_PREFETCH = BIT(23),
+
+ ACC_CONTROL_PAGE_HIT = BIT(24),
+ ACC_CONTROL_WR_PREEMPT = BIT(25),
+ ACC_CONTROL_PARTIAL_PAGE = BIT(26),
+ ACC_CONTROL_RD_ERASED = BIT(27),
+ ACC_CONTROL_FAST_PGM_RDIN = BIT(28),
+ ACC_CONTROL_WR_ECC = BIT(30),
+ ACC_CONTROL_RD_ECC = BIT(31),
+};
+
+static inline u32 brcmnand_spare_area_mask(struct brcmnand_controller *ctrl)
+{
+ if (ctrl->nand_version >= 0x0702)
+ return GENMASK(7, 0);
+ else if (ctrl->nand_version >= 0x0600)
+ return GENMASK(6, 0);
+ else
+ return GENMASK(5, 0);
+}
+
+#define NAND_ACC_CONTROL_ECC_SHIFT 16
+#define NAND_ACC_CONTROL_ECC_EXT_SHIFT 13
+
+static inline u32 brcmnand_ecc_level_mask(struct brcmnand_controller *ctrl)
+{
+ u32 mask = (ctrl->nand_version >= 0x0600) ? 0x1f : 0x0f;
+
+ mask <<= NAND_ACC_CONTROL_ECC_SHIFT;
+
+ /* v7.2 includes additional ECC levels */
+ if (ctrl->nand_version >= 0x0702)
+ mask |= 0x7 << NAND_ACC_CONTROL_ECC_EXT_SHIFT;
+
+ return mask;
+}
+
+static void brcmnand_set_ecc_enabled(struct brcmnand_host *host, int en)
+{
+ struct brcmnand_controller *ctrl = host->ctrl;
+ u16 offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL);
+ u32 acc_control = nand_readreg(ctrl, offs);
+ u32 ecc_flags = ACC_CONTROL_WR_ECC | ACC_CONTROL_RD_ECC;
+
+ if (en) {
+ acc_control |= ecc_flags; /* enable RD/WR ECC */
+ acc_control |= host->hwcfg.ecc_level
+ << NAND_ACC_CONTROL_ECC_SHIFT;
+ } else {
+ acc_control &= ~ecc_flags; /* disable RD/WR ECC */
+ acc_control &= ~brcmnand_ecc_level_mask(ctrl);
+ }
+
+ nand_writereg(ctrl, offs, acc_control);
+}
+
+static inline int brcmnand_sector_1k_shift(struct brcmnand_controller *ctrl)
+{
+ if (ctrl->nand_version >= 0x0702)
+ return 9;
+ else if (ctrl->nand_version >= 0x0600)
+ return 7;
+ else if (ctrl->nand_version >= 0x0500)
+ return 6;
+ else
+ return -1;
+}
+
+static int brcmnand_get_sector_size_1k(struct brcmnand_host *host)
+{
+ struct brcmnand_controller *ctrl = host->ctrl;
+ int shift = brcmnand_sector_1k_shift(ctrl);
+ u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
+ BRCMNAND_CS_ACC_CONTROL);
+
+ if (shift < 0)
+ return 0;
+
+ return (nand_readreg(ctrl, acc_control_offs) >> shift) & 0x1;
+}
+
+static void brcmnand_set_sector_size_1k(struct brcmnand_host *host, int val)
+{
+ struct brcmnand_controller *ctrl = host->ctrl;
+ int shift = brcmnand_sector_1k_shift(ctrl);
+ u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
+ BRCMNAND_CS_ACC_CONTROL);
+ u32 tmp;
+
+ if (shift < 0)
+ return;
+
+ tmp = nand_readreg(ctrl, acc_control_offs);
+ tmp &= ~(1 << shift);
+ tmp |= (!!val) << shift;
+ nand_writereg(ctrl, acc_control_offs, tmp);
+}
+
+/***********************************************************************
+ * CS_NAND_SELECT
+ ***********************************************************************/
+
+enum {
+ CS_SELECT_NAND_WP = BIT(29),
+ CS_SELECT_AUTO_DEVICE_ID_CFG = BIT(30),
+};
+
+static int bcmnand_ctrl_poll_status(struct brcmnand_controller *ctrl,
+ u32 mask, u32 expected_val,
+ unsigned long timeout_ms)
+{
+ unsigned long limit;
+ u32 val;
+
+ if (!timeout_ms)
+ timeout_ms = NAND_POLL_STATUS_TIMEOUT_MS;
+
+ limit = jiffies + msecs_to_jiffies(timeout_ms);
+ do {
+ val = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS);
+ if ((val & mask) == expected_val)
+ return 0;
+
+ cpu_relax();
+ } while (time_after(limit, jiffies));
+
+ dev_warn(ctrl->dev, "timeout on status poll (expected %x got %x)\n",
+ expected_val, val & mask);
+
+ return -ETIMEDOUT;
+}
+
+static inline void brcmnand_set_wp(struct brcmnand_controller *ctrl, bool en)
+{
+ u32 val = en ? CS_SELECT_NAND_WP : 0;
+
+ brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT, CS_SELECT_NAND_WP, 0, val);
+}
+
+/***********************************************************************
+ * Flash DMA
+ ***********************************************************************/
+
+enum flash_dma_reg {
+ FLASH_DMA_REVISION = 0x00,
+ FLASH_DMA_FIRST_DESC = 0x04,
+ FLASH_DMA_FIRST_DESC_EXT = 0x08,
+ FLASH_DMA_CTRL = 0x0c,
+ FLASH_DMA_MODE = 0x10,
+ FLASH_DMA_STATUS = 0x14,
+ FLASH_DMA_INTERRUPT_DESC = 0x18,
+ FLASH_DMA_INTERRUPT_DESC_EXT = 0x1c,
+ FLASH_DMA_ERROR_STATUS = 0x20,
+ FLASH_DMA_CURRENT_DESC = 0x24,
+ FLASH_DMA_CURRENT_DESC_EXT = 0x28,
+};
+
+static inline bool has_flash_dma(struct brcmnand_controller *ctrl)
+{
+ return ctrl->flash_dma_base;
+}
+
+static inline bool flash_dma_buf_ok(const void *buf)
+{
+ return buf && !is_vmalloc_addr(buf) &&
+ likely(IS_ALIGNED((uintptr_t)buf, 4));
+}
+
+static inline void flash_dma_writel(struct brcmnand_controller *ctrl, u8 offs,
+ u32 val)
+{
+ brcmnand_writel(val, ctrl->flash_dma_base + offs);
+}
+
+static inline u32 flash_dma_readl(struct brcmnand_controller *ctrl, u8 offs)
+{
+ return brcmnand_readl(ctrl->flash_dma_base + offs);
+}
+
+/* Low-level operation types: command, address, write, or read */
+enum brcmnand_llop_type {
+ LL_OP_CMD,
+ LL_OP_ADDR,
+ LL_OP_WR,
+ LL_OP_RD,
+};
+
+/***********************************************************************
+ * Internal support functions
+ ***********************************************************************/
+
+static inline bool is_hamming_ecc(struct brcmnand_controller *ctrl,
+ struct brcmnand_cfg *cfg)
+{
+ if (ctrl->nand_version <= 0x0701)
+ return cfg->sector_size_1k == 0 && cfg->spare_area_size == 16 &&
+ cfg->ecc_level == 15;
+ else
+ return cfg->sector_size_1k == 0 && ((cfg->spare_area_size == 16 &&
+ cfg->ecc_level == 15) ||
+ (cfg->spare_area_size == 28 && cfg->ecc_level == 16));
+}
+
+/*
+ * Set mtd->ooblayout to the appropriate mtd_ooblayout_ops given
+ * the layout/configuration.
+ * Returns -ERRCODE on failure.
+ */
+static int brcmnand_hamming_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct brcmnand_host *host = nand_get_controller_data(chip);
+ struct brcmnand_cfg *cfg = &host->hwcfg;
+ int sas = cfg->spare_area_size << cfg->sector_size_1k;
+ int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
+
+ if (section >= sectors)
+ return -ERANGE;
+
+ oobregion->offset = (section * sas) + 6;
+ oobregion->length = 3;
+
+ return 0;
+}
+
+static int brcmnand_hamming_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct brcmnand_host *host = nand_get_controller_data(chip);
+ struct brcmnand_cfg *cfg = &host->hwcfg;
+ int sas = cfg->spare_area_size << cfg->sector_size_1k;
+ int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
+
+ if (section >= sectors * 2)
+ return -ERANGE;
+
+ oobregion->offset = (section / 2) * sas;
+
+ if (section & 1) {
+ oobregion->offset += 9;
+ oobregion->length = 7;
+ } else {
+ oobregion->length = 6;
+
+ /* First sector of each page may have BBI */
+ if (!section) {
+ /*
+ * Small-page NAND use byte 6 for BBI while large-page
+ * NAND use byte 0.
+ */
+ if (cfg->page_size > 512)
+ oobregion->offset++;
+ oobregion->length--;
+ }
+ }
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops brcmnand_hamming_ooblayout_ops = {
+ .ecc = brcmnand_hamming_ooblayout_ecc,
+ .free = brcmnand_hamming_ooblayout_free,
+};
+
+static int brcmnand_bch_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct brcmnand_host *host = nand_get_controller_data(chip);
+ struct brcmnand_cfg *cfg = &host->hwcfg;
+ int sas = cfg->spare_area_size << cfg->sector_size_1k;
+ int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
+
+ if (section >= sectors)
+ return -ERANGE;
+
+ oobregion->offset = (section * (sas + 1)) - chip->ecc.bytes;
+ oobregion->length = chip->ecc.bytes;
+
+ return 0;
+}
+
+static int brcmnand_bch_ooblayout_free_lp(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct brcmnand_host *host = nand_get_controller_data(chip);
+ struct brcmnand_cfg *cfg = &host->hwcfg;
+ int sas = cfg->spare_area_size << cfg->sector_size_1k;
+ int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
+
+ if (section >= sectors)
+ return -ERANGE;
+
+ if (sas <= chip->ecc.bytes)
+ return 0;
+
+ oobregion->offset = section * sas;
+ oobregion->length = sas - chip->ecc.bytes;
+
+ if (!section) {
+ oobregion->offset++;
+ oobregion->length--;
+ }
+
+ return 0;
+}
+
+static int brcmnand_bch_ooblayout_free_sp(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct brcmnand_host *host = nand_get_controller_data(chip);
+ struct brcmnand_cfg *cfg = &host->hwcfg;
+ int sas = cfg->spare_area_size << cfg->sector_size_1k;
+
+ if (section > 1 || sas - chip->ecc.bytes < 6 ||
+ (section && sas - chip->ecc.bytes == 6))
+ return -ERANGE;
+
+ if (!section) {
+ oobregion->offset = 0;
+ oobregion->length = 5;
+ } else {
+ oobregion->offset = 6;
+ oobregion->length = sas - chip->ecc.bytes - 6;
+ }
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops brcmnand_bch_lp_ooblayout_ops = {
+ .ecc = brcmnand_bch_ooblayout_ecc,
+ .free = brcmnand_bch_ooblayout_free_lp,
+};
+
+static const struct mtd_ooblayout_ops brcmnand_bch_sp_ooblayout_ops = {
+ .ecc = brcmnand_bch_ooblayout_ecc,
+ .free = brcmnand_bch_ooblayout_free_sp,
+};
+
+static int brcmstb_choose_ecc_layout(struct brcmnand_host *host)
+{
+ struct brcmnand_cfg *p = &host->hwcfg;
+ struct mtd_info *mtd = nand_to_mtd(&host->chip);
+ struct nand_ecc_ctrl *ecc = &host->chip.ecc;
+ unsigned int ecc_level = p->ecc_level;
+ int sas = p->spare_area_size << p->sector_size_1k;
+ int sectors = p->page_size / (512 << p->sector_size_1k);
+
+ if (p->sector_size_1k)
+ ecc_level <<= 1;
+
+ if (is_hamming_ecc(host->ctrl, p)) {
+ ecc->bytes = 3 * sectors;
+ mtd_set_ooblayout(mtd, &brcmnand_hamming_ooblayout_ops);
+ return 0;
+ }
+
+ /*
+ * CONTROLLER_VERSION:
+ * < v5.0: ECC_REQ = ceil(BCH_T * 13/8)
+ * >= v5.0: ECC_REQ = ceil(BCH_T * 14/8)
+ * But we will just be conservative.
+ */
+ ecc->bytes = DIV_ROUND_UP(ecc_level * 14, 8);
+ if (p->page_size == 512)
+ mtd_set_ooblayout(mtd, &brcmnand_bch_sp_ooblayout_ops);
+ else
+ mtd_set_ooblayout(mtd, &brcmnand_bch_lp_ooblayout_ops);
+
+ if (ecc->bytes >= sas) {
+ dev_err(&host->pdev->dev,
+ "error: ECC too large for OOB (ECC bytes %d, spare sector %d)\n",
+ ecc->bytes, sas);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void brcmnand_wp(struct mtd_info *mtd, int wp)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct brcmnand_host *host = nand_get_controller_data(chip);
+ struct brcmnand_controller *ctrl = host->ctrl;
+
+ if ((ctrl->features & BRCMNAND_HAS_WP) && wp_on == 1) {
+ static int old_wp = -1;
+ int ret;
+
+ if (old_wp != wp) {
+ dev_dbg(ctrl->dev, "WP %s\n", wp ? "on" : "off");
+ old_wp = wp;
+ }
+
+ /*
+ * make sure ctrl/flash ready before and after
+ * changing state of #WP pin
+ */
+ ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY |
+ NAND_STATUS_READY,
+ NAND_CTRL_RDY |
+ NAND_STATUS_READY, 0);
+ if (ret)
+ return;
+
+ brcmnand_set_wp(ctrl, wp);
+ nand_status_op(chip, NULL);
+ /* NAND_STATUS_WP 0x00 = protected, 0x80 = not protected */
+ ret = bcmnand_ctrl_poll_status(ctrl,
+ NAND_CTRL_RDY |
+ NAND_STATUS_READY |
+ NAND_STATUS_WP,
+ NAND_CTRL_RDY |
+ NAND_STATUS_READY |
+ (wp ? 0 : NAND_STATUS_WP), 0);
+
+ if (ret)
+ dev_err_ratelimited(&host->pdev->dev,
+ "nand #WP expected %s\n",
+ wp ? "on" : "off");
+ }
+}
+
+/* Helper functions for reading and writing OOB registers */
+static inline u8 oob_reg_read(struct brcmnand_controller *ctrl, u32 offs)
+{
+ u16 offset0, offset10, reg_offs;
+
+ offset0 = ctrl->reg_offsets[BRCMNAND_OOB_READ_BASE];
+ offset10 = ctrl->reg_offsets[BRCMNAND_OOB_READ_10_BASE];
+
+ if (offs >= ctrl->max_oob)
+ return 0x77;
+
+ if (offs >= 16 && offset10)
+ reg_offs = offset10 + ((offs - 0x10) & ~0x03);
+ else
+ reg_offs = offset0 + (offs & ~0x03);
+
+ return nand_readreg(ctrl, reg_offs) >> (24 - ((offs & 0x03) << 3));
+}
+
+static inline void oob_reg_write(struct brcmnand_controller *ctrl, u32 offs,
+ u32 data)
+{
+ u16 offset0, offset10, reg_offs;
+
+ offset0 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_BASE];
+ offset10 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_10_BASE];
+
+ if (offs >= ctrl->max_oob)
+ return;
+
+ if (offs >= 16 && offset10)
+ reg_offs = offset10 + ((offs - 0x10) & ~0x03);
+ else
+ reg_offs = offset0 + (offs & ~0x03);
+
+ nand_writereg(ctrl, reg_offs, data);
+}
+
+/*
+ * read_oob_from_regs - read data from OOB registers
+ * @ctrl: NAND controller
+ * @i: sub-page sector index
+ * @oob: buffer to read to
+ * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE)
+ * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal
+ */
+static int read_oob_from_regs(struct brcmnand_controller *ctrl, int i, u8 *oob,
+ int sas, int sector_1k)
+{
+ int tbytes = sas << sector_1k;
+ int j;
+
+ /* Adjust OOB values for 1K sector size */
+ if (sector_1k && (i & 0x01))
+ tbytes = max(0, tbytes - (int)ctrl->max_oob);
+ tbytes = min_t(int, tbytes, ctrl->max_oob);
+
+ for (j = 0; j < tbytes; j++)
+ oob[j] = oob_reg_read(ctrl, j);
+ return tbytes;
+}
+
+/*
+ * write_oob_to_regs - write data to OOB registers
+ * @i: sub-page sector index
+ * @oob: buffer to write from
+ * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE)
+ * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal
+ */
+static int write_oob_to_regs(struct brcmnand_controller *ctrl, int i,
+ const u8 *oob, int sas, int sector_1k)
+{
+ int tbytes = sas << sector_1k;
+ int j;
+
+ /* Adjust OOB values for 1K sector size */
+ if (sector_1k && (i & 0x01))
+ tbytes = max(0, tbytes - (int)ctrl->max_oob);
+ tbytes = min_t(int, tbytes, ctrl->max_oob);
+
+ for (j = 0; j < tbytes; j += 4)
+ oob_reg_write(ctrl, j,
+ (oob[j + 0] << 24) |
+ (oob[j + 1] << 16) |
+ (oob[j + 2] << 8) |
+ (oob[j + 3] << 0));
+ return tbytes;
+}
+
+static irqreturn_t brcmnand_ctlrdy_irq(int irq, void *data)
+{
+ struct brcmnand_controller *ctrl = data;
+
+ /* Discard all NAND_CTLRDY interrupts during DMA */
+ if (ctrl->dma_pending)
+ return IRQ_HANDLED;
+
+ complete(&ctrl->done);
+ return IRQ_HANDLED;
+}
+
+/* Handle SoC-specific interrupt hardware */
+static irqreturn_t brcmnand_irq(int irq, void *data)
+{
+ struct brcmnand_controller *ctrl = data;
+
+ if (ctrl->soc->ctlrdy_ack(ctrl->soc))
+ return brcmnand_ctlrdy_irq(irq, data);
+
+ return IRQ_NONE;
+}
+
+static irqreturn_t brcmnand_dma_irq(int irq, void *data)
+{
+ struct brcmnand_controller *ctrl = data;
+
+ complete(&ctrl->dma_done);
+
+ return IRQ_HANDLED;
+}
+
+static void brcmnand_send_cmd(struct brcmnand_host *host, int cmd)
+{
+ struct brcmnand_controller *ctrl = host->ctrl;
+ int ret;
+
+ dev_dbg(ctrl->dev, "send native cmd %d addr_lo 0x%x\n", cmd,
+ brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS));
+ BUG_ON(ctrl->cmd_pending != 0);
+ ctrl->cmd_pending = cmd;
+
+ ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY, NAND_CTRL_RDY, 0);
+ WARN_ON(ret);
+
+ mb(); /* flush previous writes */
+ brcmnand_write_reg(ctrl, BRCMNAND_CMD_START,
+ cmd << brcmnand_cmd_shift(ctrl));
+}
+
+/***********************************************************************
+ * NAND MTD API: read/program/erase
+ ***********************************************************************/
+
+static void brcmnand_cmd_ctrl(struct mtd_info *mtd, int dat,
+ unsigned int ctrl)
+{
+ /* intentionally left blank */
+}
+
+static int brcmnand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct brcmnand_host *host = nand_get_controller_data(chip);
+ struct brcmnand_controller *ctrl = host->ctrl;
+ unsigned long timeo = msecs_to_jiffies(100);
+
+ dev_dbg(ctrl->dev, "wait on native cmd %d\n", ctrl->cmd_pending);
+ if (ctrl->cmd_pending &&
+ wait_for_completion_timeout(&ctrl->done, timeo) <= 0) {
+ u32 cmd = brcmnand_read_reg(ctrl, BRCMNAND_CMD_START)
+ >> brcmnand_cmd_shift(ctrl);
+
+ dev_err_ratelimited(ctrl->dev,
+ "timeout waiting for command %#02x\n", cmd);
+ dev_err_ratelimited(ctrl->dev, "intfc status %08x\n",
+ brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS));
+ }
+ ctrl->cmd_pending = 0;
+ return brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) &
+ INTFC_FLASH_STATUS;
+}
+
+enum {
+ LLOP_RE = BIT(16),
+ LLOP_WE = BIT(17),
+ LLOP_ALE = BIT(18),
+ LLOP_CLE = BIT(19),
+ LLOP_RETURN_IDLE = BIT(31),
+
+ LLOP_DATA_MASK = GENMASK(15, 0),
+};
+
+static int brcmnand_low_level_op(struct brcmnand_host *host,
+ enum brcmnand_llop_type type, u32 data,
+ bool last_op)
+{
+ struct mtd_info *mtd = nand_to_mtd(&host->chip);
+ struct nand_chip *chip = &host->chip;
+ struct brcmnand_controller *ctrl = host->ctrl;
+ u32 tmp;
+
+ tmp = data & LLOP_DATA_MASK;
+ switch (type) {
+ case LL_OP_CMD:
+ tmp |= LLOP_WE | LLOP_CLE;
+ break;
+ case LL_OP_ADDR:
+ /* WE | ALE */
+ tmp |= LLOP_WE | LLOP_ALE;
+ break;
+ case LL_OP_WR:
+ /* WE */
+ tmp |= LLOP_WE;
+ break;
+ case LL_OP_RD:
+ /* RE */
+ tmp |= LLOP_RE;
+ break;
+ }
+ if (last_op)
+ /* RETURN_IDLE */
+ tmp |= LLOP_RETURN_IDLE;
+
+ dev_dbg(ctrl->dev, "ll_op cmd %#x\n", tmp);
+
+ brcmnand_write_reg(ctrl, BRCMNAND_LL_OP, tmp);
+ (void)brcmnand_read_reg(ctrl, BRCMNAND_LL_OP);
+
+ brcmnand_send_cmd(host, CMD_LOW_LEVEL_OP);
+ return brcmnand_waitfunc(mtd, chip);
+}
+
+static void brcmnand_cmdfunc(struct mtd_info *mtd, unsigned command,
+ int column, int page_addr)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct brcmnand_host *host = nand_get_controller_data(chip);
+ struct brcmnand_controller *ctrl = host->ctrl;
+ u64 addr = (u64)page_addr << chip->page_shift;
+ int native_cmd = 0;
+
+ if (command == NAND_CMD_READID || command == NAND_CMD_PARAM ||
+ command == NAND_CMD_RNDOUT)
+ addr = (u64)column;
+ /* Avoid propagating a negative, don't-care address */
+ else if (page_addr < 0)
+ addr = 0;
+
+ dev_dbg(ctrl->dev, "cmd 0x%x addr 0x%llx\n", command,
+ (unsigned long long)addr);
+
+ host->last_cmd = command;
+ host->last_byte = 0;
+ host->last_addr = addr;
+
+ switch (command) {
+ case NAND_CMD_RESET:
+ native_cmd = CMD_FLASH_RESET;
+ break;
+ case NAND_CMD_STATUS:
+ native_cmd = CMD_STATUS_READ;
+ break;
+ case NAND_CMD_READID:
+ native_cmd = CMD_DEVICE_ID_READ;
+ break;
+ case NAND_CMD_READOOB:
+ native_cmd = CMD_SPARE_AREA_READ;
+ break;
+ case NAND_CMD_ERASE1:
+ native_cmd = CMD_BLOCK_ERASE;
+ brcmnand_wp(mtd, 0);
+ break;
+ case NAND_CMD_PARAM:
+ native_cmd = CMD_PARAMETER_READ;
+ break;
+ case NAND_CMD_SET_FEATURES:
+ case NAND_CMD_GET_FEATURES:
+ brcmnand_low_level_op(host, LL_OP_CMD, command, false);
+ brcmnand_low_level_op(host, LL_OP_ADDR, column, false);
+ break;
+ case NAND_CMD_RNDOUT:
+ native_cmd = CMD_PARAMETER_CHANGE_COL;
+ addr &= ~((u64)(FC_BYTES - 1));
+ /*
+ * HW quirk: PARAMETER_CHANGE_COL requires SECTOR_SIZE_1K=0
+ * NB: hwcfg.sector_size_1k may not be initialized yet
+ */
+ if (brcmnand_get_sector_size_1k(host)) {
+ host->hwcfg.sector_size_1k =
+ brcmnand_get_sector_size_1k(host);
+ brcmnand_set_sector_size_1k(host, 0);
+ }
+ break;
+ }
+
+ if (!native_cmd)
+ return;
+
+ brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS,
+ (host->cs << 16) | ((addr >> 32) & 0xffff));
+ (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS);
+ brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, lower_32_bits(addr));
+ (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS);
+
+ brcmnand_send_cmd(host, native_cmd);
+ brcmnand_waitfunc(mtd, chip);
+
+ if (native_cmd == CMD_PARAMETER_READ ||
+ native_cmd == CMD_PARAMETER_CHANGE_COL) {
+ /* Copy flash cache word-wise */
+ u32 *flash_cache = (u32 *)ctrl->flash_cache;
+ int i;
+
+ brcmnand_soc_data_bus_prepare(ctrl->soc, true);
+
+ /*
+ * Must cache the FLASH_CACHE now, since changes in
+ * SECTOR_SIZE_1K may invalidate it
+ */
+ for (i = 0; i < FC_WORDS; i++)
+ /*
+ * Flash cache is big endian for parameter pages, at
+ * least on STB SoCs
+ */
+ flash_cache[i] = be32_to_cpu(brcmnand_read_fc(ctrl, i));
+
+ brcmnand_soc_data_bus_unprepare(ctrl->soc, true);
+
+ /* Cleanup from HW quirk: restore SECTOR_SIZE_1K */
+ if (host->hwcfg.sector_size_1k)
+ brcmnand_set_sector_size_1k(host,
+ host->hwcfg.sector_size_1k);
+ }
+
+ /* Re-enable protection is necessary only after erase */
+ if (command == NAND_CMD_ERASE1)
+ brcmnand_wp(mtd, 1);
+}
+
+static uint8_t brcmnand_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct brcmnand_host *host = nand_get_controller_data(chip);
+ struct brcmnand_controller *ctrl = host->ctrl;
+ uint8_t ret = 0;
+ int addr, offs;
+
+ switch (host->last_cmd) {
+ case NAND_CMD_READID:
+ if (host->last_byte < 4)
+ ret = brcmnand_read_reg(ctrl, BRCMNAND_ID) >>
+ (24 - (host->last_byte << 3));
+ else if (host->last_byte < 8)
+ ret = brcmnand_read_reg(ctrl, BRCMNAND_ID_EXT) >>
+ (56 - (host->last_byte << 3));
+ break;
+
+ case NAND_CMD_READOOB:
+ ret = oob_reg_read(ctrl, host->last_byte);
+ break;
+
+ case NAND_CMD_STATUS:
+ ret = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) &
+ INTFC_FLASH_STATUS;
+ if (wp_on) /* hide WP status */
+ ret |= NAND_STATUS_WP;
+ break;
+
+ case NAND_CMD_PARAM:
+ case NAND_CMD_RNDOUT:
+ addr = host->last_addr + host->last_byte;
+ offs = addr & (FC_BYTES - 1);
+
+ /* At FC_BYTES boundary, switch to next column */
+ if (host->last_byte > 0 && offs == 0)
+ nand_change_read_column_op(chip, addr, NULL, 0, false);
+
+ ret = ctrl->flash_cache[offs];
+ break;
+ case NAND_CMD_GET_FEATURES:
+ if (host->last_byte >= ONFI_SUBFEATURE_PARAM_LEN) {
+ ret = 0;
+ } else {
+ bool last = host->last_byte ==
+ ONFI_SUBFEATURE_PARAM_LEN - 1;
+ brcmnand_low_level_op(host, LL_OP_RD, 0, last);
+ ret = brcmnand_read_reg(ctrl, BRCMNAND_LL_RDATA) & 0xff;
+ }
+ }
+
+ dev_dbg(ctrl->dev, "read byte = 0x%02x\n", ret);
+ host->last_byte++;
+
+ return ret;
+}
+
+static void brcmnand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++, buf++)
+ *buf = brcmnand_read_byte(mtd);
+}
+
+static void brcmnand_write_buf(struct mtd_info *mtd, const uint8_t *buf,
+ int len)
+{
+ int i;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct brcmnand_host *host = nand_get_controller_data(chip);
+
+ switch (host->last_cmd) {
+ case NAND_CMD_SET_FEATURES:
+ for (i = 0; i < len; i++)
+ brcmnand_low_level_op(host, LL_OP_WR, buf[i],
+ (i + 1) == len);
+ break;
+ default:
+ BUG();
+ break;
+ }
+}
+
+/**
+ * Construct a FLASH_DMA descriptor as part of a linked list. You must know the
+ * following ahead of time:
+ * - Is this descriptor the beginning or end of a linked list?
+ * - What is the (DMA) address of the next descriptor in the linked list?
+ */
+static int brcmnand_fill_dma_desc(struct brcmnand_host *host,
+ struct brcm_nand_dma_desc *desc, u64 addr,
+ dma_addr_t buf, u32 len, u8 dma_cmd,
+ bool begin, bool end,
+ dma_addr_t next_desc)
+{
+ memset(desc, 0, sizeof(*desc));
+ /* Descriptors are written in native byte order (wordwise) */
+ desc->next_desc = lower_32_bits(next_desc);
+ desc->next_desc_ext = upper_32_bits(next_desc);
+ desc->cmd_irq = (dma_cmd << 24) |
+ (end ? (0x03 << 8) : 0) | /* IRQ | STOP */
+ (!!begin) | ((!!end) << 1); /* head, tail */
+#ifdef CONFIG_CPU_BIG_ENDIAN
+ desc->cmd_irq |= 0x01 << 12;
+#endif
+ desc->dram_addr = lower_32_bits(buf);
+ desc->dram_addr_ext = upper_32_bits(buf);
+ desc->tfr_len = len;
+ desc->total_len = len;
+ desc->flash_addr = lower_32_bits(addr);
+ desc->flash_addr_ext = upper_32_bits(addr);
+ desc->cs = host->cs;
+ desc->status_valid = 0x01;
+ return 0;
+}
+
+/**
+ * Kick the FLASH_DMA engine, with a given DMA descriptor
+ */
+static void brcmnand_dma_run(struct brcmnand_host *host, dma_addr_t desc)
+{
+ struct brcmnand_controller *ctrl = host->ctrl;
+ unsigned long timeo = msecs_to_jiffies(100);
+
+ flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC, lower_32_bits(desc));
+ (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC);
+ flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC_EXT, upper_32_bits(desc));
+ (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC_EXT);
+
+ /* Start FLASH_DMA engine */
+ ctrl->dma_pending = true;
+ mb(); /* flush previous writes */
+ flash_dma_writel(ctrl, FLASH_DMA_CTRL, 0x03); /* wake | run */
+
+ if (wait_for_completion_timeout(&ctrl->dma_done, timeo) <= 0) {
+ dev_err(ctrl->dev,
+ "timeout waiting for DMA; status %#x, error status %#x\n",
+ flash_dma_readl(ctrl, FLASH_DMA_STATUS),
+ flash_dma_readl(ctrl, FLASH_DMA_ERROR_STATUS));
+ }
+ ctrl->dma_pending = false;
+ flash_dma_writel(ctrl, FLASH_DMA_CTRL, 0); /* force stop */
+}
+
+static int brcmnand_dma_trans(struct brcmnand_host *host, u64 addr, u32 *buf,
+ u32 len, u8 dma_cmd)
+{
+ struct brcmnand_controller *ctrl = host->ctrl;
+ dma_addr_t buf_pa;
+ int dir = dma_cmd == CMD_PAGE_READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+ buf_pa = dma_map_single(ctrl->dev, buf, len, dir);
+ if (dma_mapping_error(ctrl->dev, buf_pa)) {
+ dev_err(ctrl->dev, "unable to map buffer for DMA\n");
+ return -ENOMEM;
+ }
+
+ brcmnand_fill_dma_desc(host, ctrl->dma_desc, addr, buf_pa, len,
+ dma_cmd, true, true, 0);
+
+ brcmnand_dma_run(host, ctrl->dma_pa);
+
+ dma_unmap_single(ctrl->dev, buf_pa, len, dir);
+
+ if (ctrl->dma_desc->status_valid & FLASH_DMA_ECC_ERROR)
+ return -EBADMSG;
+ else if (ctrl->dma_desc->status_valid & FLASH_DMA_CORR_ERROR)
+ return -EUCLEAN;
+
+ return 0;
+}
+
+/*
+ * Assumes proper CS is already set
+ */
+static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip,
+ u64 addr, unsigned int trans, u32 *buf,
+ u8 *oob, u64 *err_addr)
+{
+ struct brcmnand_host *host = nand_get_controller_data(chip);
+ struct brcmnand_controller *ctrl = host->ctrl;
+ int i, j, ret = 0;
+
+ /* Clear error addresses */
+ brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_ADDR, 0);
+ brcmnand_write_reg(ctrl, BRCMNAND_CORR_ADDR, 0);
+ brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_EXT_ADDR, 0);
+ brcmnand_write_reg(ctrl, BRCMNAND_CORR_EXT_ADDR, 0);
+
+ brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS,
+ (host->cs << 16) | ((addr >> 32) & 0xffff));
+ (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS);
+
+ for (i = 0; i < trans; i++, addr += FC_BYTES) {
+ brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS,
+ lower_32_bits(addr));
+ (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS);
+ /* SPARE_AREA_READ does not use ECC, so just use PAGE_READ */
+ brcmnand_send_cmd(host, CMD_PAGE_READ);
+ brcmnand_waitfunc(mtd, chip);
+
+ if (likely(buf)) {
+ brcmnand_soc_data_bus_prepare(ctrl->soc, false);
+
+ for (j = 0; j < FC_WORDS; j++, buf++)
+ *buf = brcmnand_read_fc(ctrl, j);
+
+ brcmnand_soc_data_bus_unprepare(ctrl->soc, false);
+ }
+
+ if (oob)
+ oob += read_oob_from_regs(ctrl, i, oob,
+ mtd->oobsize / trans,
+ host->hwcfg.sector_size_1k);
+
+ if (!ret) {
+ *err_addr = brcmnand_read_reg(ctrl,
+ BRCMNAND_UNCORR_ADDR) |
+ ((u64)(brcmnand_read_reg(ctrl,
+ BRCMNAND_UNCORR_EXT_ADDR)
+ & 0xffff) << 32);
+ if (*err_addr)
+ ret = -EBADMSG;
+ }
+
+ if (!ret) {
+ *err_addr = brcmnand_read_reg(ctrl,
+ BRCMNAND_CORR_ADDR) |
+ ((u64)(brcmnand_read_reg(ctrl,
+ BRCMNAND_CORR_EXT_ADDR)
+ & 0xffff) << 32);
+ if (*err_addr)
+ ret = -EUCLEAN;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * Check a page to see if it is erased (w/ bitflips) after an uncorrectable ECC
+ * error
+ *
+ * Because the HW ECC signals an ECC error if an erase paged has even a single
+ * bitflip, we must check each ECC error to see if it is actually an erased
+ * page with bitflips, not a truly corrupted page.
+ *
+ * On a real error, return a negative error code (-EBADMSG for ECC error), and
+ * buf will contain raw data.
+ * Otherwise, buf gets filled with 0xffs and return the maximum number of
+ * bitflips-per-ECC-sector to the caller.
+ *
+ */
+static int brcmstb_nand_verify_erased_page(struct mtd_info *mtd,
+ struct nand_chip *chip, void *buf, u64 addr)
+{
+ int i, sas;
+ void *oob = chip->oob_poi;
+ int bitflips = 0;
+ int page = addr >> chip->page_shift;
+ int ret;
+
+ if (!buf) {
+ buf = chip->data_buf;
+ /* Invalidate page cache */
+ chip->pagebuf = -1;
+ }
+
+ sas = mtd->oobsize / chip->ecc.steps;
+
+ /* read without ecc for verification */
+ ret = chip->ecc.read_page_raw(mtd, chip, buf, true, page);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < chip->ecc.steps; i++, oob += sas) {
+ ret = nand_check_erased_ecc_chunk(buf, chip->ecc.size,
+ oob, sas, NULL, 0,
+ chip->ecc.strength);
+ if (ret < 0)
+ return ret;
+
+ bitflips = max(bitflips, ret);
+ }
+
+ return bitflips;
+}
+
+static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip,
+ u64 addr, unsigned int trans, u32 *buf, u8 *oob)
+{
+ struct brcmnand_host *host = nand_get_controller_data(chip);
+ struct brcmnand_controller *ctrl = host->ctrl;
+ u64 err_addr = 0;
+ int err;
+ bool retry = true;
+
+ dev_dbg(ctrl->dev, "read %llx -> %p\n", (unsigned long long)addr, buf);
+
+try_dmaread:
+ brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_COUNT, 0);
+
+ if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) {
+ err = brcmnand_dma_trans(host, addr, buf, trans * FC_BYTES,
+ CMD_PAGE_READ);
+ if (err) {
+ if (mtd_is_bitflip_or_eccerr(err))
+ err_addr = addr;
+ else
+ return -EIO;
+ }
+ } else {
+ if (oob)
+ memset(oob, 0x99, mtd->oobsize);
+
+ err = brcmnand_read_by_pio(mtd, chip, addr, trans, buf,
+ oob, &err_addr);
+ }
+
+ if (mtd_is_eccerr(err)) {
+ /*
+ * On controller version and 7.0, 7.1 , DMA read after a
+ * prior PIO read that reported uncorrectable error,
+ * the DMA engine captures this error following DMA read
+ * cleared only on subsequent DMA read, so just retry once
+ * to clear a possible false error reported for current DMA
+ * read
+ */
+ if ((ctrl->nand_version == 0x0700) ||
+ (ctrl->nand_version == 0x0701)) {
+ if (retry) {
+ retry = false;
+ goto try_dmaread;
+ }
+ }
+
+ /*
+ * Controller version 7.2 has hw encoder to detect erased page
+ * bitflips, apply sw verification for older controllers only
+ */
+ if (ctrl->nand_version < 0x0702) {
+ err = brcmstb_nand_verify_erased_page(mtd, chip, buf,
+ addr);
+ /* erased page bitflips corrected */
+ if (err >= 0)
+ return err;
+ }
+
+ dev_dbg(ctrl->dev, "uncorrectable error at 0x%llx\n",
+ (unsigned long long)err_addr);
+ mtd->ecc_stats.failed++;
+ /* NAND layer expects zero on ECC errors */
+ return 0;
+ }
+
+ if (mtd_is_bitflip(err)) {
+ unsigned int corrected = brcmnand_count_corrected(ctrl);
+
+ dev_dbg(ctrl->dev, "corrected error at 0x%llx\n",
+ (unsigned long long)err_addr);
+ mtd->ecc_stats.corrected += corrected;
+ /* Always exceed the software-imposed threshold */
+ return max(mtd->bitflip_threshold, corrected);
+ }
+
+ return 0;
+}
+
+static int brcmnand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ struct brcmnand_host *host = nand_get_controller_data(chip);
+ u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL;
+
+ nand_read_page_op(chip, page, 0, NULL, 0);
+
+ return brcmnand_read(mtd, chip, host->last_addr,
+ mtd->writesize >> FC_SHIFT, (u32 *)buf, oob);
+}
+
+static int brcmnand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ struct brcmnand_host *host = nand_get_controller_data(chip);
+ u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL;
+ int ret;
+
+ nand_read_page_op(chip, page, 0, NULL, 0);
+
+ brcmnand_set_ecc_enabled(host, 0);
+ ret = brcmnand_read(mtd, chip, host->last_addr,
+ mtd->writesize >> FC_SHIFT, (u32 *)buf, oob);
+ brcmnand_set_ecc_enabled(host, 1);
+ return ret;
+}
+
+static int brcmnand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ return brcmnand_read(mtd, chip, (u64)page << chip->page_shift,
+ mtd->writesize >> FC_SHIFT,
+ NULL, (u8 *)chip->oob_poi);
+}
+
+static int brcmnand_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ struct brcmnand_host *host = nand_get_controller_data(chip);
+
+ brcmnand_set_ecc_enabled(host, 0);
+ brcmnand_read(mtd, chip, (u64)page << chip->page_shift,
+ mtd->writesize >> FC_SHIFT,
+ NULL, (u8 *)chip->oob_poi);
+ brcmnand_set_ecc_enabled(host, 1);
+ return 0;
+}
+
+static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip,
+ u64 addr, const u32 *buf, u8 *oob)
+{
+ struct brcmnand_host *host = nand_get_controller_data(chip);
+ struct brcmnand_controller *ctrl = host->ctrl;
+ unsigned int i, j, trans = mtd->writesize >> FC_SHIFT;
+ int status, ret = 0;
+
+ dev_dbg(ctrl->dev, "write %llx <- %p\n", (unsigned long long)addr, buf);
+
+ if (unlikely((unsigned long)buf & 0x03)) {
+ dev_warn(ctrl->dev, "unaligned buffer: %p\n", buf);
+ buf = (u32 *)((unsigned long)buf & ~0x03);
+ }
+
+ brcmnand_wp(mtd, 0);
+
+ for (i = 0; i < ctrl->max_oob; i += 4)
+ oob_reg_write(ctrl, i, 0xffffffff);
+
+ if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) {
+ if (brcmnand_dma_trans(host, addr, (u32 *)buf,
+ mtd->writesize, CMD_PROGRAM_PAGE))
+ ret = -EIO;
+ goto out;
+ }
+
+ brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS,
+ (host->cs << 16) | ((addr >> 32) & 0xffff));
+ (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS);
+
+ for (i = 0; i < trans; i++, addr += FC_BYTES) {
+ /* full address MUST be set before populating FC */
+ brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS,
+ lower_32_bits(addr));
+ (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS);
+
+ if (buf) {
+ brcmnand_soc_data_bus_prepare(ctrl->soc, false);
+
+ for (j = 0; j < FC_WORDS; j++, buf++)
+ brcmnand_write_fc(ctrl, j, *buf);
+
+ brcmnand_soc_data_bus_unprepare(ctrl->soc, false);
+ } else if (oob) {
+ for (j = 0; j < FC_WORDS; j++)
+ brcmnand_write_fc(ctrl, j, 0xffffffff);
+ }
+
+ if (oob) {
+ oob += write_oob_to_regs(ctrl, i, oob,
+ mtd->oobsize / trans,
+ host->hwcfg.sector_size_1k);
+ }
+
+ /* we cannot use SPARE_AREA_PROGRAM when PARTIAL_PAGE_EN=0 */
+ brcmnand_send_cmd(host, CMD_PROGRAM_PAGE);
+ status = brcmnand_waitfunc(mtd, chip);
+
+ if (status & NAND_STATUS_FAIL) {
+ dev_info(ctrl->dev, "program failed at %llx\n",
+ (unsigned long long)addr);
+ ret = -EIO;
+ goto out;
+ }
+ }
+out:
+ brcmnand_wp(mtd, 1);
+ return ret;
+}
+
+static int brcmnand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required, int page)
+{
+ struct brcmnand_host *host = nand_get_controller_data(chip);
+ void *oob = oob_required ? chip->oob_poi : NULL;
+
+ nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+ brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob);
+
+ return nand_prog_page_end_op(chip);
+}
+
+static int brcmnand_write_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf,
+ int oob_required, int page)
+{
+ struct brcmnand_host *host = nand_get_controller_data(chip);
+ void *oob = oob_required ? chip->oob_poi : NULL;
+
+ nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+ brcmnand_set_ecc_enabled(host, 0);
+ brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob);
+ brcmnand_set_ecc_enabled(host, 1);
+
+ return nand_prog_page_end_op(chip);
+}
+
+static int brcmnand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ return brcmnand_write(mtd, chip, (u64)page << chip->page_shift,
+ NULL, chip->oob_poi);
+}
+
+static int brcmnand_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ struct brcmnand_host *host = nand_get_controller_data(chip);
+ int ret;
+
+ brcmnand_set_ecc_enabled(host, 0);
+ ret = brcmnand_write(mtd, chip, (u64)page << chip->page_shift, NULL,
+ (u8 *)chip->oob_poi);
+ brcmnand_set_ecc_enabled(host, 1);
+
+ return ret;
+}
+
+/***********************************************************************
+ * Per-CS setup (1 NAND device)
+ ***********************************************************************/
+
+static int brcmnand_set_cfg(struct brcmnand_host *host,
+ struct brcmnand_cfg *cfg)
+{
+ struct brcmnand_controller *ctrl = host->ctrl;
+ struct nand_chip *chip = &host->chip;
+ u16 cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG);
+ u16 cfg_ext_offs = brcmnand_cs_offset(ctrl, host->cs,
+ BRCMNAND_CS_CFG_EXT);
+ u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
+ BRCMNAND_CS_ACC_CONTROL);
+ u8 block_size = 0, page_size = 0, device_size = 0;
+ u32 tmp;
+
+ if (ctrl->block_sizes) {
+ int i, found;
+
+ for (i = 0, found = 0; ctrl->block_sizes[i]; i++)
+ if (ctrl->block_sizes[i] * 1024 == cfg->block_size) {
+ block_size = i;
+ found = 1;
+ }
+ if (!found) {
+ dev_warn(ctrl->dev, "invalid block size %u\n",
+ cfg->block_size);
+ return -EINVAL;
+ }
+ } else {
+ block_size = ffs(cfg->block_size) - ffs(BRCMNAND_MIN_BLOCKSIZE);
+ }
+
+ if (cfg->block_size < BRCMNAND_MIN_BLOCKSIZE || (ctrl->max_block_size &&
+ cfg->block_size > ctrl->max_block_size)) {
+ dev_warn(ctrl->dev, "invalid block size %u\n",
+ cfg->block_size);
+ block_size = 0;
+ }
+
+ if (ctrl->page_sizes) {
+ int i, found;
+
+ for (i = 0, found = 0; ctrl->page_sizes[i]; i++)
+ if (ctrl->page_sizes[i] == cfg->page_size) {
+ page_size = i;
+ found = 1;
+ }
+ if (!found) {
+ dev_warn(ctrl->dev, "invalid page size %u\n",
+ cfg->page_size);
+ return -EINVAL;
+ }
+ } else {
+ page_size = ffs(cfg->page_size) - ffs(BRCMNAND_MIN_PAGESIZE);
+ }
+
+ if (cfg->page_size < BRCMNAND_MIN_PAGESIZE || (ctrl->max_page_size &&
+ cfg->page_size > ctrl->max_page_size)) {
+ dev_warn(ctrl->dev, "invalid page size %u\n", cfg->page_size);
+ return -EINVAL;
+ }
+
+ if (fls64(cfg->device_size) < fls64(BRCMNAND_MIN_DEVSIZE)) {
+ dev_warn(ctrl->dev, "invalid device size 0x%llx\n",
+ (unsigned long long)cfg->device_size);
+ return -EINVAL;
+ }
+ device_size = fls64(cfg->device_size) - fls64(BRCMNAND_MIN_DEVSIZE);
+
+ tmp = (cfg->blk_adr_bytes << CFG_BLK_ADR_BYTES_SHIFT) |
+ (cfg->col_adr_bytes << CFG_COL_ADR_BYTES_SHIFT) |
+ (cfg->ful_adr_bytes << CFG_FUL_ADR_BYTES_SHIFT) |
+ (!!(cfg->device_width == 16) << CFG_BUS_WIDTH_SHIFT) |
+ (device_size << CFG_DEVICE_SIZE_SHIFT);
+ if (cfg_offs == cfg_ext_offs) {
+ tmp |= (page_size << CFG_PAGE_SIZE_SHIFT) |
+ (block_size << CFG_BLK_SIZE_SHIFT);
+ nand_writereg(ctrl, cfg_offs, tmp);
+ } else {
+ nand_writereg(ctrl, cfg_offs, tmp);
+ tmp = (page_size << CFG_EXT_PAGE_SIZE_SHIFT) |
+ (block_size << CFG_EXT_BLK_SIZE_SHIFT);
+ nand_writereg(ctrl, cfg_ext_offs, tmp);
+ }
+
+ tmp = nand_readreg(ctrl, acc_control_offs);
+ tmp &= ~brcmnand_ecc_level_mask(ctrl);
+ tmp |= cfg->ecc_level << NAND_ACC_CONTROL_ECC_SHIFT;
+ tmp &= ~brcmnand_spare_area_mask(ctrl);
+ tmp |= cfg->spare_area_size;
+ nand_writereg(ctrl, acc_control_offs, tmp);
+
+ brcmnand_set_sector_size_1k(host, cfg->sector_size_1k);
+
+ /* threshold = ceil(BCH-level * 0.75) */
+ brcmnand_wr_corr_thresh(host, DIV_ROUND_UP(chip->ecc.strength * 3, 4));
+
+ return 0;
+}
+
+static void brcmnand_print_cfg(struct brcmnand_host *host,
+ char *buf, struct brcmnand_cfg *cfg)
+{
+ buf += sprintf(buf,
+ "%lluMiB total, %uKiB blocks, %u%s pages, %uB OOB, %u-bit",
+ (unsigned long long)cfg->device_size >> 20,
+ cfg->block_size >> 10,
+ cfg->page_size >= 1024 ? cfg->page_size >> 10 : cfg->page_size,
+ cfg->page_size >= 1024 ? "KiB" : "B",
+ cfg->spare_area_size, cfg->device_width);
+
+ /* Account for Hamming ECC and for BCH 512B vs 1KiB sectors */
+ if (is_hamming_ecc(host->ctrl, cfg))
+ sprintf(buf, ", Hamming ECC");
+ else if (cfg->sector_size_1k)
+ sprintf(buf, ", BCH-%u (1KiB sector)", cfg->ecc_level << 1);
+ else
+ sprintf(buf, ", BCH-%u", cfg->ecc_level);
+}
+
+/*
+ * Minimum number of bytes to address a page. Calculated as:
+ * roundup(log2(size / page-size) / 8)
+ *
+ * NB: the following does not "round up" for non-power-of-2 'size'; but this is
+ * OK because many other things will break if 'size' is irregular...
+ */
+static inline int get_blk_adr_bytes(u64 size, u32 writesize)
+{
+ return ALIGN(ilog2(size) - ilog2(writesize), 8) >> 3;
+}
+
+static int brcmnand_setup_dev(struct brcmnand_host *host)
+{
+ struct mtd_info *mtd = nand_to_mtd(&host->chip);
+ struct nand_chip *chip = &host->chip;
+ struct brcmnand_controller *ctrl = host->ctrl;
+ struct brcmnand_cfg *cfg = &host->hwcfg;
+ char msg[128];
+ u32 offs, tmp, oob_sector;
+ int ret;
+
+ memset(cfg, 0, sizeof(*cfg));
+
+ ret = of_property_read_u32(nand_get_flash_node(chip),
+ "brcm,nand-oob-sector-size",
+ &oob_sector);
+ if (ret) {
+ /* Use detected size */
+ cfg->spare_area_size = mtd->oobsize /
+ (mtd->writesize >> FC_SHIFT);
+ } else {
+ cfg->spare_area_size = oob_sector;
+ }
+ if (cfg->spare_area_size > ctrl->max_oob)
+ cfg->spare_area_size = ctrl->max_oob;
+ /*
+ * Set oobsize to be consistent with controller's spare_area_size, as
+ * the rest is inaccessible.
+ */
+ mtd->oobsize = cfg->spare_area_size * (mtd->writesize >> FC_SHIFT);
+
+ cfg->device_size = mtd->size;
+ cfg->block_size = mtd->erasesize;
+ cfg->page_size = mtd->writesize;
+ cfg->device_width = (chip->options & NAND_BUSWIDTH_16) ? 16 : 8;
+ cfg->col_adr_bytes = 2;
+ cfg->blk_adr_bytes = get_blk_adr_bytes(mtd->size, mtd->writesize);
+
+ if (chip->ecc.mode != NAND_ECC_HW) {
+ dev_err(ctrl->dev, "only HW ECC supported; selected: %d\n",
+ chip->ecc.mode);
+ return -EINVAL;
+ }
+
+ if (chip->ecc.algo == NAND_ECC_UNKNOWN) {
+ if (chip->ecc.strength == 1 && chip->ecc.size == 512)
+ /* Default to Hamming for 1-bit ECC, if unspecified */
+ chip->ecc.algo = NAND_ECC_HAMMING;
+ else
+ /* Otherwise, BCH */
+ chip->ecc.algo = NAND_ECC_BCH;
+ }
+
+ if (chip->ecc.algo == NAND_ECC_HAMMING && (chip->ecc.strength != 1 ||
+ chip->ecc.size != 512)) {
+ dev_err(ctrl->dev, "invalid Hamming params: %d bits per %d bytes\n",
+ chip->ecc.strength, chip->ecc.size);
+ return -EINVAL;
+ }
+
+ switch (chip->ecc.size) {
+ case 512:
+ if (chip->ecc.algo == NAND_ECC_HAMMING)
+ cfg->ecc_level = 15;
+ else
+ cfg->ecc_level = chip->ecc.strength;
+ cfg->sector_size_1k = 0;
+ break;
+ case 1024:
+ if (!(ctrl->features & BRCMNAND_HAS_1K_SECTORS)) {
+ dev_err(ctrl->dev, "1KB sectors not supported\n");
+ return -EINVAL;
+ }
+ if (chip->ecc.strength & 0x1) {
+ dev_err(ctrl->dev,
+ "odd ECC not supported with 1KB sectors\n");
+ return -EINVAL;
+ }
+
+ cfg->ecc_level = chip->ecc.strength >> 1;
+ cfg->sector_size_1k = 1;
+ break;
+ default:
+ dev_err(ctrl->dev, "unsupported ECC size: %d\n",
+ chip->ecc.size);
+ return -EINVAL;
+ }
+
+ cfg->ful_adr_bytes = cfg->blk_adr_bytes;
+ if (mtd->writesize > 512)
+ cfg->ful_adr_bytes += cfg->col_adr_bytes;
+ else
+ cfg->ful_adr_bytes += 1;
+
+ ret = brcmnand_set_cfg(host, cfg);
+ if (ret)
+ return ret;
+
+ brcmnand_set_ecc_enabled(host, 1);
+
+ brcmnand_print_cfg(host, msg, cfg);
+ dev_info(ctrl->dev, "detected %s\n", msg);
+
+ /* Configure ACC_CONTROL */
+ offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL);
+ tmp = nand_readreg(ctrl, offs);
+ tmp &= ~ACC_CONTROL_PARTIAL_PAGE;
+ tmp &= ~ACC_CONTROL_RD_ERASED;
+
+ /* We need to turn on Read from erased paged protected by ECC */
+ if (ctrl->nand_version >= 0x0702)
+ tmp |= ACC_CONTROL_RD_ERASED;
+ tmp &= ~ACC_CONTROL_FAST_PGM_RDIN;
+ if (ctrl->features & BRCMNAND_HAS_PREFETCH)
+ tmp &= ~ACC_CONTROL_PREFETCH;
+
+ nand_writereg(ctrl, offs, tmp);
+
+ return 0;
+}
+
+static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn)
+{
+ struct brcmnand_controller *ctrl = host->ctrl;
+ struct platform_device *pdev = host->pdev;
+ struct mtd_info *mtd;
+ struct nand_chip *chip;
+ int ret;
+ u16 cfg_offs;
+
+ ret = of_property_read_u32(dn, "reg", &host->cs);
+ if (ret) {
+ dev_err(&pdev->dev, "can't get chip-select\n");
+ return -ENXIO;
+ }
+
+ mtd = nand_to_mtd(&host->chip);
+ chip = &host->chip;
+
+ nand_set_flash_node(chip, dn);
+ nand_set_controller_data(chip, host);
+ mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "brcmnand.%d",
+ host->cs);
+ if (!mtd->name)
+ return -ENOMEM;
+
+ mtd->owner = THIS_MODULE;
+ mtd->dev.parent = &pdev->dev;
+
+ chip->IO_ADDR_R = (void __iomem *)0xdeadbeef;
+ chip->IO_ADDR_W = (void __iomem *)0xdeadbeef;
+
+ chip->cmd_ctrl = brcmnand_cmd_ctrl;
+ chip->cmdfunc = brcmnand_cmdfunc;
+ chip->waitfunc = brcmnand_waitfunc;
+ chip->read_byte = brcmnand_read_byte;
+ chip->read_buf = brcmnand_read_buf;
+ chip->write_buf = brcmnand_write_buf;
+
+ chip->ecc.mode = NAND_ECC_HW;
+ chip->ecc.read_page = brcmnand_read_page;
+ chip->ecc.write_page = brcmnand_write_page;
+ chip->ecc.read_page_raw = brcmnand_read_page_raw;
+ chip->ecc.write_page_raw = brcmnand_write_page_raw;
+ chip->ecc.write_oob_raw = brcmnand_write_oob_raw;
+ chip->ecc.read_oob_raw = brcmnand_read_oob_raw;
+ chip->ecc.read_oob = brcmnand_read_oob;
+ chip->ecc.write_oob = brcmnand_write_oob;
+
+ chip->controller = &ctrl->controller;
+
+ /*
+ * The bootloader might have configured 16bit mode but
+ * NAND READID command only works in 8bit mode. We force
+ * 8bit mode here to ensure that NAND READID commands works.
+ */
+ cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG);
+ nand_writereg(ctrl, cfg_offs,
+ nand_readreg(ctrl, cfg_offs) & ~CFG_BUS_WIDTH);
+
+ ret = nand_scan_ident(mtd, 1, NULL);
+ if (ret)
+ return ret;
+
+ chip->options |= NAND_NO_SUBPAGE_WRITE;
+ /*
+ * Avoid (for instance) kmap()'d buffers from JFFS2, which we can't DMA
+ * to/from, and have nand_base pass us a bounce buffer instead, as
+ * needed.
+ */
+ chip->options |= NAND_USE_BOUNCE_BUFFER;
+
+ if (chip->bbt_options & NAND_BBT_USE_FLASH)
+ chip->bbt_options |= NAND_BBT_NO_OOB;
+
+ if (brcmnand_setup_dev(host))
+ return -ENXIO;
+
+ chip->ecc.size = host->hwcfg.sector_size_1k ? 1024 : 512;
+ /* only use our internal HW threshold */
+ mtd->bitflip_threshold = 1;
+
+ ret = brcmstb_choose_ecc_layout(host);
+ if (ret)
+ return ret;
+
+ ret = nand_scan_tail(mtd);
+ if (ret)
+ return ret;
+
+ ret = mtd_device_register(mtd, NULL, 0);
+ if (ret)
+ nand_cleanup(chip);
+
+ return ret;
+}
+
+static void brcmnand_save_restore_cs_config(struct brcmnand_host *host,
+ int restore)
+{
+ struct brcmnand_controller *ctrl = host->ctrl;
+ u16 cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG);
+ u16 cfg_ext_offs = brcmnand_cs_offset(ctrl, host->cs,
+ BRCMNAND_CS_CFG_EXT);
+ u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
+ BRCMNAND_CS_ACC_CONTROL);
+ u16 t1_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING1);
+ u16 t2_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING2);
+
+ if (restore) {
+ nand_writereg(ctrl, cfg_offs, host->hwcfg.config);
+ if (cfg_offs != cfg_ext_offs)
+ nand_writereg(ctrl, cfg_ext_offs,
+ host->hwcfg.config_ext);
+ nand_writereg(ctrl, acc_control_offs, host->hwcfg.acc_control);
+ nand_writereg(ctrl, t1_offs, host->hwcfg.timing_1);
+ nand_writereg(ctrl, t2_offs, host->hwcfg.timing_2);
+ } else {
+ host->hwcfg.config = nand_readreg(ctrl, cfg_offs);
+ if (cfg_offs != cfg_ext_offs)
+ host->hwcfg.config_ext =
+ nand_readreg(ctrl, cfg_ext_offs);
+ host->hwcfg.acc_control = nand_readreg(ctrl, acc_control_offs);
+ host->hwcfg.timing_1 = nand_readreg(ctrl, t1_offs);
+ host->hwcfg.timing_2 = nand_readreg(ctrl, t2_offs);
+ }
+}
+
+static int brcmnand_suspend(struct device *dev)
+{
+ struct brcmnand_controller *ctrl = dev_get_drvdata(dev);
+ struct brcmnand_host *host;
+
+ list_for_each_entry(host, &ctrl->host_list, node)
+ brcmnand_save_restore_cs_config(host, 0);
+
+ ctrl->nand_cs_nand_select = brcmnand_read_reg(ctrl, BRCMNAND_CS_SELECT);
+ ctrl->nand_cs_nand_xor = brcmnand_read_reg(ctrl, BRCMNAND_CS_XOR);
+ ctrl->corr_stat_threshold =
+ brcmnand_read_reg(ctrl, BRCMNAND_CORR_THRESHOLD);
+
+ if (has_flash_dma(ctrl))
+ ctrl->flash_dma_mode = flash_dma_readl(ctrl, FLASH_DMA_MODE);
+
+ return 0;
+}
+
+static int brcmnand_resume(struct device *dev)
+{
+ struct brcmnand_controller *ctrl = dev_get_drvdata(dev);
+ struct brcmnand_host *host;
+
+ if (has_flash_dma(ctrl)) {
+ flash_dma_writel(ctrl, FLASH_DMA_MODE, ctrl->flash_dma_mode);
+ flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0);
+ }
+
+ brcmnand_write_reg(ctrl, BRCMNAND_CS_SELECT, ctrl->nand_cs_nand_select);
+ brcmnand_write_reg(ctrl, BRCMNAND_CS_XOR, ctrl->nand_cs_nand_xor);
+ brcmnand_write_reg(ctrl, BRCMNAND_CORR_THRESHOLD,
+ ctrl->corr_stat_threshold);
+ if (ctrl->soc) {
+ /* Clear/re-enable interrupt */
+ ctrl->soc->ctlrdy_ack(ctrl->soc);
+ ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true);
+ }
+
+ list_for_each_entry(host, &ctrl->host_list, node) {
+ struct nand_chip *chip = &host->chip;
+
+ brcmnand_save_restore_cs_config(host, 1);
+
+ /* Reset the chip, required by some chips after power-up */
+ nand_reset_op(chip);
+ }
+
+ return 0;
+}
+
+const struct dev_pm_ops brcmnand_pm_ops = {
+ .suspend = brcmnand_suspend,
+ .resume = brcmnand_resume,
+};
+EXPORT_SYMBOL_GPL(brcmnand_pm_ops);
+
+static const struct of_device_id brcmnand_of_match[] = {
+ { .compatible = "brcm,brcmnand-v4.0" },
+ { .compatible = "brcm,brcmnand-v5.0" },
+ { .compatible = "brcm,brcmnand-v6.0" },
+ { .compatible = "brcm,brcmnand-v6.1" },
+ { .compatible = "brcm,brcmnand-v6.2" },
+ { .compatible = "brcm,brcmnand-v7.0" },
+ { .compatible = "brcm,brcmnand-v7.1" },
+ { .compatible = "brcm,brcmnand-v7.2" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, brcmnand_of_match);
+
+/***********************************************************************
+ * Platform driver setup (per controller)
+ ***********************************************************************/
+
+int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *dn = dev->of_node, *child;
+ struct brcmnand_controller *ctrl;
+ struct resource *res;
+ int ret;
+
+ /* We only support device-tree instantiation */
+ if (!dn)
+ return -ENODEV;
+
+ if (!of_match_node(brcmnand_of_match, dn))
+ return -ENODEV;
+
+ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, ctrl);
+ ctrl->dev = dev;
+
+ init_completion(&ctrl->done);
+ init_completion(&ctrl->dma_done);
+ nand_hw_control_init(&ctrl->controller);
+ INIT_LIST_HEAD(&ctrl->host_list);
+
+ /* NAND register range */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ctrl->nand_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ctrl->nand_base))
+ return PTR_ERR(ctrl->nand_base);
+
+ /* Enable clock before using NAND registers */
+ ctrl->clk = devm_clk_get(dev, "nand");
+ if (!IS_ERR(ctrl->clk)) {
+ ret = clk_prepare_enable(ctrl->clk);
+ if (ret)
+ return ret;
+ } else {
+ ret = PTR_ERR(ctrl->clk);
+ if (ret == -EPROBE_DEFER)
+ return ret;
+
+ ctrl->clk = NULL;
+ }
+
+ /* Initialize NAND revision */
+ ret = brcmnand_revision_init(ctrl);
+ if (ret)
+ goto err;
+
+ /*
+ * Most chips have this cache at a fixed offset within 'nand' block.
+ * Some must specify this region separately.
+ */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand-cache");
+ if (res) {
+ ctrl->nand_fc = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ctrl->nand_fc)) {
+ ret = PTR_ERR(ctrl->nand_fc);
+ goto err;
+ }
+ } else {
+ ctrl->nand_fc = ctrl->nand_base +
+ ctrl->reg_offsets[BRCMNAND_FC_BASE];
+ }
+
+ /* FLASH_DMA */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "flash-dma");
+ if (res) {
+ ctrl->flash_dma_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ctrl->flash_dma_base)) {
+ ret = PTR_ERR(ctrl->flash_dma_base);
+ goto err;
+ }
+
+ flash_dma_writel(ctrl, FLASH_DMA_MODE, 1); /* linked-list */
+ flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0);
+
+ /* Allocate descriptor(s) */
+ ctrl->dma_desc = dmam_alloc_coherent(dev,
+ sizeof(*ctrl->dma_desc),
+ &ctrl->dma_pa, GFP_KERNEL);
+ if (!ctrl->dma_desc) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ctrl->dma_irq = platform_get_irq(pdev, 1);
+ if ((int)ctrl->dma_irq < 0) {
+ dev_err(dev, "missing FLASH_DMA IRQ\n");
+ ret = -ENODEV;
+ goto err;
+ }
+
+ ret = devm_request_irq(dev, ctrl->dma_irq,
+ brcmnand_dma_irq, 0, DRV_NAME,
+ ctrl);
+ if (ret < 0) {
+ dev_err(dev, "can't allocate IRQ %d: error %d\n",
+ ctrl->dma_irq, ret);
+ goto err;
+ }
+
+ dev_info(dev, "enabling FLASH_DMA\n");
+ }
+
+ /* Disable automatic device ID config, direct addressing */
+ brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT,
+ CS_SELECT_AUTO_DEVICE_ID_CFG | 0xff, 0, 0);
+ /* Disable XOR addressing */
+ brcmnand_rmw_reg(ctrl, BRCMNAND_CS_XOR, 0xff, 0, 0);
+
+ if (ctrl->features & BRCMNAND_HAS_WP) {
+ /* Permanently disable write protection */
+ if (wp_on == 2)
+ brcmnand_set_wp(ctrl, false);
+ } else {
+ wp_on = 0;
+ }
+
+ /* IRQ */
+ ctrl->irq = platform_get_irq(pdev, 0);
+ if ((int)ctrl->irq < 0) {
+ dev_err(dev, "no IRQ defined\n");
+ ret = -ENODEV;
+ goto err;
+ }
+
+ /*
+ * Some SoCs integrate this controller (e.g., its interrupt bits) in
+ * interesting ways
+ */
+ if (soc) {
+ ctrl->soc = soc;
+
+ ret = devm_request_irq(dev, ctrl->irq, brcmnand_irq, 0,
+ DRV_NAME, ctrl);
+
+ /* Enable interrupt */
+ ctrl->soc->ctlrdy_ack(ctrl->soc);
+ ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true);
+ } else {
+ /* Use standard interrupt infrastructure */
+ ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0,
+ DRV_NAME, ctrl);
+ }
+ if (ret < 0) {
+ dev_err(dev, "can't allocate IRQ %d: error %d\n",
+ ctrl->irq, ret);
+ goto err;
+ }
+
+ for_each_available_child_of_node(dn, child) {
+ if (of_device_is_compatible(child, "brcm,nandcs")) {
+ struct brcmnand_host *host;
+
+ host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
+ if (!host) {
+ of_node_put(child);
+ ret = -ENOMEM;
+ goto err;
+ }
+ host->pdev = pdev;
+ host->ctrl = ctrl;
+
+ ret = brcmnand_init_cs(host, child);
+ if (ret) {
+ devm_kfree(dev, host);
+ continue; /* Try all chip-selects */
+ }
+
+ list_add_tail(&host->node, &ctrl->host_list);
+ }
+ }
+
+ /* No chip-selects could initialize properly */
+ if (list_empty(&ctrl->host_list)) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ return 0;
+
+err:
+ clk_disable_unprepare(ctrl->clk);
+ return ret;
+
+}
+EXPORT_SYMBOL_GPL(brcmnand_probe);
+
+int brcmnand_remove(struct platform_device *pdev)
+{
+ struct brcmnand_controller *ctrl = dev_get_drvdata(&pdev->dev);
+ struct brcmnand_host *host;
+
+ list_for_each_entry(host, &ctrl->host_list, node)
+ nand_release(nand_to_mtd(&host->chip));
+
+ clk_disable_unprepare(ctrl->clk);
+
+ dev_set_drvdata(&pdev->dev, NULL);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(brcmnand_remove);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Kevin Cernekee");
+MODULE_AUTHOR("Brian Norris");
+MODULE_DESCRIPTION("NAND driver for Broadcom chips");
+MODULE_ALIAS("platform:brcmnand");
diff --git a/drivers/mtd/nand/brcmnand/brcmnand.h b/drivers/mtd/nand/raw/brcmnand/brcmnand.h
index 5c44cd4aba87..5c44cd4aba87 100644
--- a/drivers/mtd/nand/brcmnand/brcmnand.h
+++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.h
diff --git a/drivers/mtd/nand/brcmnand/brcmstb_nand.c b/drivers/mtd/nand/raw/brcmnand/brcmstb_nand.c
index 5c271077ac87..5c271077ac87 100644
--- a/drivers/mtd/nand/brcmnand/brcmstb_nand.c
+++ b/drivers/mtd/nand/raw/brcmnand/brcmstb_nand.c
diff --git a/drivers/mtd/nand/brcmnand/iproc_nand.c b/drivers/mtd/nand/raw/brcmnand/iproc_nand.c
index 4c6ae113664d..4c6ae113664d 100644
--- a/drivers/mtd/nand/brcmnand/iproc_nand.c
+++ b/drivers/mtd/nand/raw/brcmnand/iproc_nand.c
diff --git a/drivers/mtd/nand/raw/cafe_nand.c b/drivers/mtd/nand/raw/cafe_nand.c
new file mode 100644
index 000000000000..d8c8c9d1e640
--- /dev/null
+++ b/drivers/mtd/nand/raw/cafe_nand.c
@@ -0,0 +1,875 @@
+/*
+ * Driver for One Laptop Per Child ‘CAFÉ’ controller, aka Marvell 88ALP01
+ *
+ * The data sheet for this device can be found at:
+ * http://wiki.laptop.org/go/Datasheets
+ *
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2006 David Woodhouse <dwmw2@infradead.org>
+ */
+
+#define DEBUG
+
+#include <linux/device.h>
+#undef DEBUG
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/rslib.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <asm/io.h>
+
+#define CAFE_NAND_CTRL1 0x00
+#define CAFE_NAND_CTRL2 0x04
+#define CAFE_NAND_CTRL3 0x08
+#define CAFE_NAND_STATUS 0x0c
+#define CAFE_NAND_IRQ 0x10
+#define CAFE_NAND_IRQ_MASK 0x14
+#define CAFE_NAND_DATA_LEN 0x18
+#define CAFE_NAND_ADDR1 0x1c
+#define CAFE_NAND_ADDR2 0x20
+#define CAFE_NAND_TIMING1 0x24
+#define CAFE_NAND_TIMING2 0x28
+#define CAFE_NAND_TIMING3 0x2c
+#define CAFE_NAND_NONMEM 0x30
+#define CAFE_NAND_ECC_RESULT 0x3C
+#define CAFE_NAND_DMA_CTRL 0x40
+#define CAFE_NAND_DMA_ADDR0 0x44
+#define CAFE_NAND_DMA_ADDR1 0x48
+#define CAFE_NAND_ECC_SYN01 0x50
+#define CAFE_NAND_ECC_SYN23 0x54
+#define CAFE_NAND_ECC_SYN45 0x58
+#define CAFE_NAND_ECC_SYN67 0x5c
+#define CAFE_NAND_READ_DATA 0x1000
+#define CAFE_NAND_WRITE_DATA 0x2000
+
+#define CAFE_GLOBAL_CTRL 0x3004
+#define CAFE_GLOBAL_IRQ 0x3008
+#define CAFE_GLOBAL_IRQ_MASK 0x300c
+#define CAFE_NAND_RESET 0x3034
+
+/* Missing from the datasheet: bit 19 of CTRL1 sets CE0 vs. CE1 */
+#define CTRL1_CHIPSELECT (1<<19)
+
+struct cafe_priv {
+ struct nand_chip nand;
+ struct pci_dev *pdev;
+ void __iomem *mmio;
+ struct rs_control *rs;
+ uint32_t ctl1;
+ uint32_t ctl2;
+ int datalen;
+ int nr_data;
+ int data_pos;
+ int page_addr;
+ dma_addr_t dmaaddr;
+ unsigned char *dmabuf;
+};
+
+static int usedma = 1;
+module_param(usedma, int, 0644);
+
+static int skipbbt = 0;
+module_param(skipbbt, int, 0644);
+
+static int debug = 0;
+module_param(debug, int, 0644);
+
+static int regdebug = 0;
+module_param(regdebug, int, 0644);
+
+static int checkecc = 1;
+module_param(checkecc, int, 0644);
+
+static unsigned int numtimings;
+static int timing[3];
+module_param_array(timing, int, &numtimings, 0644);
+
+static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL };
+
+/* Hrm. Why isn't this already conditional on something in the struct device? */
+#define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0)
+
+/* Make it easier to switch to PIO if we need to */
+#define cafe_readl(cafe, addr) readl((cafe)->mmio + CAFE_##addr)
+#define cafe_writel(cafe, datum, addr) writel(datum, (cafe)->mmio + CAFE_##addr)
+
+static int cafe_device_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct cafe_priv *cafe = nand_get_controller_data(chip);
+ int result = !!(cafe_readl(cafe, NAND_STATUS) & 0x40000000);
+ uint32_t irqs = cafe_readl(cafe, NAND_IRQ);
+
+ cafe_writel(cafe, irqs, NAND_IRQ);
+
+ cafe_dev_dbg(&cafe->pdev->dev, "NAND device is%s ready, IRQ %x (%x) (%x,%x)\n",
+ result?"":" not", irqs, cafe_readl(cafe, NAND_IRQ),
+ cafe_readl(cafe, GLOBAL_IRQ), cafe_readl(cafe, GLOBAL_IRQ_MASK));
+
+ return result;
+}
+
+
+static void cafe_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct cafe_priv *cafe = nand_get_controller_data(chip);
+
+ if (usedma)
+ memcpy(cafe->dmabuf + cafe->datalen, buf, len);
+ else
+ memcpy_toio(cafe->mmio + CAFE_NAND_WRITE_DATA + cafe->datalen, buf, len);
+
+ cafe->datalen += len;
+
+ cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes to write buffer. datalen 0x%x\n",
+ len, cafe->datalen);
+}
+
+static void cafe_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct cafe_priv *cafe = nand_get_controller_data(chip);
+
+ if (usedma)
+ memcpy(buf, cafe->dmabuf + cafe->datalen, len);
+ else
+ memcpy_fromio(buf, cafe->mmio + CAFE_NAND_READ_DATA + cafe->datalen, len);
+
+ cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes from position 0x%x in read buffer.\n",
+ len, cafe->datalen);
+ cafe->datalen += len;
+}
+
+static uint8_t cafe_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct cafe_priv *cafe = nand_get_controller_data(chip);
+ uint8_t d;
+
+ cafe_read_buf(mtd, &d, 1);
+ cafe_dev_dbg(&cafe->pdev->dev, "Read %02x\n", d);
+
+ return d;
+}
+
+static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
+ int column, int page_addr)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct cafe_priv *cafe = nand_get_controller_data(chip);
+ int adrbytes = 0;
+ uint32_t ctl1;
+ uint32_t doneint = 0x80000000;
+
+ cafe_dev_dbg(&cafe->pdev->dev, "cmdfunc %02x, 0x%x, 0x%x\n",
+ command, column, page_addr);
+
+ if (command == NAND_CMD_ERASE2 || command == NAND_CMD_PAGEPROG) {
+ /* Second half of a command we already calculated */
+ cafe_writel(cafe, cafe->ctl2 | 0x100 | command, NAND_CTRL2);
+ ctl1 = cafe->ctl1;
+ cafe->ctl2 &= ~(1<<30);
+ cafe_dev_dbg(&cafe->pdev->dev, "Continue command, ctl1 %08x, #data %d\n",
+ cafe->ctl1, cafe->nr_data);
+ goto do_command;
+ }
+ /* Reset ECC engine */
+ cafe_writel(cafe, 0, NAND_CTRL2);
+
+ /* Emulate NAND_CMD_READOOB on large-page chips */
+ if (mtd->writesize > 512 &&
+ command == NAND_CMD_READOOB) {
+ column += mtd->writesize;
+ command = NAND_CMD_READ0;
+ }
+
+ /* FIXME: Do we need to send read command before sending data
+ for small-page chips, to position the buffer correctly? */
+
+ if (column != -1) {
+ cafe_writel(cafe, column, NAND_ADDR1);
+ adrbytes = 2;
+ if (page_addr != -1)
+ goto write_adr2;
+ } else if (page_addr != -1) {
+ cafe_writel(cafe, page_addr & 0xffff, NAND_ADDR1);
+ page_addr >>= 16;
+ write_adr2:
+ cafe_writel(cafe, page_addr, NAND_ADDR2);
+ adrbytes += 2;
+ if (mtd->size > mtd->writesize << 16)
+ adrbytes++;
+ }
+
+ cafe->data_pos = cafe->datalen = 0;
+
+ /* Set command valid bit, mask in the chip select bit */
+ ctl1 = 0x80000000 | command | (cafe->ctl1 & CTRL1_CHIPSELECT);
+
+ /* Set RD or WR bits as appropriate */
+ if (command == NAND_CMD_READID || command == NAND_CMD_STATUS) {
+ ctl1 |= (1<<26); /* rd */
+ /* Always 5 bytes, for now */
+ cafe->datalen = 4;
+ /* And one address cycle -- even for STATUS, since the controller doesn't work without */
+ adrbytes = 1;
+ } else if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 ||
+ command == NAND_CMD_READOOB || command == NAND_CMD_RNDOUT) {
+ ctl1 |= 1<<26; /* rd */
+ /* For now, assume just read to end of page */
+ cafe->datalen = mtd->writesize + mtd->oobsize - column;
+ } else if (command == NAND_CMD_SEQIN)
+ ctl1 |= 1<<25; /* wr */
+
+ /* Set number of address bytes */
+ if (adrbytes)
+ ctl1 |= ((adrbytes-1)|8) << 27;
+
+ if (command == NAND_CMD_SEQIN || command == NAND_CMD_ERASE1) {
+ /* Ignore the first command of a pair; the hardware
+ deals with them both at once, later */
+ cafe->ctl1 = ctl1;
+ cafe_dev_dbg(&cafe->pdev->dev, "Setup for delayed command, ctl1 %08x, dlen %x\n",
+ cafe->ctl1, cafe->datalen);
+ return;
+ }
+ /* RNDOUT and READ0 commands need a following byte */
+ if (command == NAND_CMD_RNDOUT)
+ cafe_writel(cafe, cafe->ctl2 | 0x100 | NAND_CMD_RNDOUTSTART, NAND_CTRL2);
+ else if (command == NAND_CMD_READ0 && mtd->writesize > 512)
+ cafe_writel(cafe, cafe->ctl2 | 0x100 | NAND_CMD_READSTART, NAND_CTRL2);
+
+ do_command:
+ cafe_dev_dbg(&cafe->pdev->dev, "dlen %x, ctl1 %x, ctl2 %x\n",
+ cafe->datalen, ctl1, cafe_readl(cafe, NAND_CTRL2));
+
+ /* NB: The datasheet lies -- we really should be subtracting 1 here */
+ cafe_writel(cafe, cafe->datalen, NAND_DATA_LEN);
+ cafe_writel(cafe, 0x90000000, NAND_IRQ);
+ if (usedma && (ctl1 & (3<<25))) {
+ uint32_t dmactl = 0xc0000000 + cafe->datalen;
+ /* If WR or RD bits set, set up DMA */
+ if (ctl1 & (1<<26)) {
+ /* It's a read */
+ dmactl |= (1<<29);
+ /* ... so it's done when the DMA is done, not just
+ the command. */
+ doneint = 0x10000000;
+ }
+ cafe_writel(cafe, dmactl, NAND_DMA_CTRL);
+ }
+ cafe->datalen = 0;
+
+ if (unlikely(regdebug)) {
+ int i;
+ printk("About to write command %08x to register 0\n", ctl1);
+ for (i=4; i< 0x5c; i+=4)
+ printk("Register %x: %08x\n", i, readl(cafe->mmio + i));
+ }
+
+ cafe_writel(cafe, ctl1, NAND_CTRL1);
+ /* Apply this short delay always to ensure that we do wait tWB in
+ * any case on any machine. */
+ ndelay(100);
+
+ if (1) {
+ int c;
+ uint32_t irqs;
+
+ for (c = 500000; c != 0; c--) {
+ irqs = cafe_readl(cafe, NAND_IRQ);
+ if (irqs & doneint)
+ break;
+ udelay(1);
+ if (!(c % 100000))
+ cafe_dev_dbg(&cafe->pdev->dev, "Wait for ready, IRQ %x\n", irqs);
+ cpu_relax();
+ }
+ cafe_writel(cafe, doneint, NAND_IRQ);
+ cafe_dev_dbg(&cafe->pdev->dev, "Command %x completed after %d usec, irqs %x (%x)\n",
+ command, 500000-c, irqs, cafe_readl(cafe, NAND_IRQ));
+ }
+
+ WARN_ON(cafe->ctl2 & (1<<30));
+
+ switch (command) {
+
+ case NAND_CMD_CACHEDPROG:
+ case NAND_CMD_PAGEPROG:
+ case NAND_CMD_ERASE1:
+ case NAND_CMD_ERASE2:
+ case NAND_CMD_SEQIN:
+ case NAND_CMD_RNDIN:
+ case NAND_CMD_STATUS:
+ case NAND_CMD_RNDOUT:
+ cafe_writel(cafe, cafe->ctl2, NAND_CTRL2);
+ return;
+ }
+ nand_wait_ready(mtd);
+ cafe_writel(cafe, cafe->ctl2, NAND_CTRL2);
+}
+
+static void cafe_select_chip(struct mtd_info *mtd, int chipnr)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct cafe_priv *cafe = nand_get_controller_data(chip);
+
+ cafe_dev_dbg(&cafe->pdev->dev, "select_chip %d\n", chipnr);
+
+ /* Mask the appropriate bit into the stored value of ctl1
+ which will be used by cafe_nand_cmdfunc() */
+ if (chipnr)
+ cafe->ctl1 |= CTRL1_CHIPSELECT;
+ else
+ cafe->ctl1 &= ~CTRL1_CHIPSELECT;
+}
+
+static irqreturn_t cafe_nand_interrupt(int irq, void *id)
+{
+ struct mtd_info *mtd = id;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct cafe_priv *cafe = nand_get_controller_data(chip);
+ uint32_t irqs = cafe_readl(cafe, NAND_IRQ);
+ cafe_writel(cafe, irqs & ~0x90000000, NAND_IRQ);
+ if (!irqs)
+ return IRQ_NONE;
+
+ cafe_dev_dbg(&cafe->pdev->dev, "irq, bits %x (%x)\n", irqs, cafe_readl(cafe, NAND_IRQ));
+ return IRQ_HANDLED;
+}
+
+static void cafe_nand_bug(struct mtd_info *mtd)
+{
+ BUG();
+}
+
+static int cafe_nand_write_oob(struct mtd_info *mtd,
+ struct nand_chip *chip, int page)
+{
+ return nand_prog_page_op(chip, page, mtd->writesize, chip->oob_poi,
+ mtd->oobsize);
+}
+
+/* Don't use -- use nand_read_oob_std for now */
+static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ return nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize);
+}
+/**
+ * cafe_nand_read_page_syndrome - [REPLACEABLE] hardware ecc syndrome based page read
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller expects OOB data read to chip->oob_poi
+ *
+ * The hw generator calculates the error syndrome automatically. Therefore
+ * we need a special oob layout and handling.
+ */
+static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ struct cafe_priv *cafe = nand_get_controller_data(chip);
+ unsigned int max_bitflips = 0;
+
+ cafe_dev_dbg(&cafe->pdev->dev, "ECC result %08x SYN1,2 %08x\n",
+ cafe_readl(cafe, NAND_ECC_RESULT),
+ cafe_readl(cafe, NAND_ECC_SYN01));
+
+ nand_read_page_op(chip, page, 0, buf, mtd->writesize);
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ if (checkecc && cafe_readl(cafe, NAND_ECC_RESULT) & (1<<18)) {
+ unsigned short syn[8], pat[4];
+ int pos[4];
+ u8 *oob = chip->oob_poi;
+ int i, n;
+
+ for (i=0; i<8; i+=2) {
+ uint32_t tmp = cafe_readl(cafe, NAND_ECC_SYN01 + (i*2));
+ syn[i] = cafe->rs->index_of[tmp & 0xfff];
+ syn[i+1] = cafe->rs->index_of[(tmp >> 16) & 0xfff];
+ }
+
+ n = decode_rs16(cafe->rs, NULL, NULL, 1367, syn, 0, pos, 0,
+ pat);
+
+ for (i = 0; i < n; i++) {
+ int p = pos[i];
+
+ /* The 12-bit symbols are mapped to bytes here */
+
+ if (p > 1374) {
+ /* out of range */
+ n = -1374;
+ } else if (p == 0) {
+ /* high four bits do not correspond to data */
+ if (pat[i] > 0xff)
+ n = -2048;
+ else
+ buf[0] ^= pat[i];
+ } else if (p == 1365) {
+ buf[2047] ^= pat[i] >> 4;
+ oob[0] ^= pat[i] << 4;
+ } else if (p > 1365) {
+ if ((p & 1) == 1) {
+ oob[3*p/2 - 2048] ^= pat[i] >> 4;
+ oob[3*p/2 - 2047] ^= pat[i] << 4;
+ } else {
+ oob[3*p/2 - 2049] ^= pat[i] >> 8;
+ oob[3*p/2 - 2048] ^= pat[i];
+ }
+ } else if ((p & 1) == 1) {
+ buf[3*p/2] ^= pat[i] >> 4;
+ buf[3*p/2 + 1] ^= pat[i] << 4;
+ } else {
+ buf[3*p/2 - 1] ^= pat[i] >> 8;
+ buf[3*p/2] ^= pat[i];
+ }
+ }
+
+ if (n < 0) {
+ dev_dbg(&cafe->pdev->dev, "Failed to correct ECC at %08x\n",
+ cafe_readl(cafe, NAND_ADDR2) * 2048);
+ for (i = 0; i < 0x5c; i += 4)
+ printk("Register %x: %08x\n", i, readl(cafe->mmio + i));
+ mtd->ecc_stats.failed++;
+ } else {
+ dev_dbg(&cafe->pdev->dev, "Corrected %d symbol errors\n", n);
+ mtd->ecc_stats.corrected += n;
+ max_bitflips = max_t(unsigned int, max_bitflips, n);
+ }
+ }
+
+ return max_bitflips;
+}
+
+static int cafe_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (section)
+ return -ERANGE;
+
+ oobregion->offset = 0;
+ oobregion->length = chip->ecc.total;
+
+ return 0;
+}
+
+static int cafe_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (section)
+ return -ERANGE;
+
+ oobregion->offset = chip->ecc.total;
+ oobregion->length = mtd->oobsize - chip->ecc.total;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops cafe_ooblayout_ops = {
+ .ecc = cafe_ooblayout_ecc,
+ .free = cafe_ooblayout_free,
+};
+
+/* Ick. The BBT code really ought to be able to work this bit out
+ for itself from the above, at least for the 2KiB case */
+static uint8_t cafe_bbt_pattern_2048[] = { 'B', 'b', 't', '0' };
+static uint8_t cafe_mirror_pattern_2048[] = { '1', 't', 'b', 'B' };
+
+static uint8_t cafe_bbt_pattern_512[] = { 0xBB };
+static uint8_t cafe_mirror_pattern_512[] = { 0xBC };
+
+
+static struct nand_bbt_descr cafe_bbt_main_descr_2048 = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION,
+ .offs = 14,
+ .len = 4,
+ .veroffs = 18,
+ .maxblocks = 4,
+ .pattern = cafe_bbt_pattern_2048
+};
+
+static struct nand_bbt_descr cafe_bbt_mirror_descr_2048 = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION,
+ .offs = 14,
+ .len = 4,
+ .veroffs = 18,
+ .maxblocks = 4,
+ .pattern = cafe_mirror_pattern_2048
+};
+
+static struct nand_bbt_descr cafe_bbt_main_descr_512 = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION,
+ .offs = 14,
+ .len = 1,
+ .veroffs = 15,
+ .maxblocks = 4,
+ .pattern = cafe_bbt_pattern_512
+};
+
+static struct nand_bbt_descr cafe_bbt_mirror_descr_512 = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION,
+ .offs = 14,
+ .len = 1,
+ .veroffs = 15,
+ .maxblocks = 4,
+ .pattern = cafe_mirror_pattern_512
+};
+
+
+static int cafe_nand_write_page_lowlevel(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ const uint8_t *buf, int oob_required,
+ int page)
+{
+ struct cafe_priv *cafe = nand_get_controller_data(chip);
+
+ nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);
+ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ /* Set up ECC autogeneration */
+ cafe->ctl2 |= (1<<30);
+
+ return nand_prog_page_end_op(chip);
+}
+
+static int cafe_nand_block_bad(struct mtd_info *mtd, loff_t ofs)
+{
+ return 0;
+}
+
+/* F_2[X]/(X**6+X+1) */
+static unsigned short gf64_mul(u8 a, u8 b)
+{
+ u8 c;
+ unsigned int i;
+
+ c = 0;
+ for (i = 0; i < 6; i++) {
+ if (a & 1)
+ c ^= b;
+ a >>= 1;
+ b <<= 1;
+ if ((b & 0x40) != 0)
+ b ^= 0x43;
+ }
+
+ return c;
+}
+
+/* F_64[X]/(X**2+X+A**-1) with A the generator of F_64[X] */
+static u16 gf4096_mul(u16 a, u16 b)
+{
+ u8 ah, al, bh, bl, ch, cl;
+
+ ah = a >> 6;
+ al = a & 0x3f;
+ bh = b >> 6;
+ bl = b & 0x3f;
+
+ ch = gf64_mul(ah ^ al, bh ^ bl) ^ gf64_mul(al, bl);
+ cl = gf64_mul(gf64_mul(ah, bh), 0x21) ^ gf64_mul(al, bl);
+
+ return (ch << 6) ^ cl;
+}
+
+static int cafe_mul(int x)
+{
+ if (x == 0)
+ return 1;
+ return gf4096_mul(x, 0xe01);
+}
+
+static int cafe_nand_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct mtd_info *mtd;
+ struct cafe_priv *cafe;
+ uint32_t ctrl;
+ int err = 0;
+ int old_dma;
+
+ /* Very old versions shared the same PCI ident for all three
+ functions on the chip. Verify the class too... */
+ if ((pdev->class >> 8) != PCI_CLASS_MEMORY_FLASH)
+ return -ENODEV;
+
+ err = pci_enable_device(pdev);
+ if (err)
+ return err;
+
+ pci_set_master(pdev);
+
+ cafe = kzalloc(sizeof(*cafe), GFP_KERNEL);
+ if (!cafe)
+ return -ENOMEM;
+
+ mtd = nand_to_mtd(&cafe->nand);
+ mtd->dev.parent = &pdev->dev;
+ nand_set_controller_data(&cafe->nand, cafe);
+
+ cafe->pdev = pdev;
+ cafe->mmio = pci_iomap(pdev, 0, 0);
+ if (!cafe->mmio) {
+ dev_warn(&pdev->dev, "failed to iomap\n");
+ err = -ENOMEM;
+ goto out_free_mtd;
+ }
+
+ cafe->rs = init_rs_non_canonical(12, &cafe_mul, 0, 1, 8);
+ if (!cafe->rs) {
+ err = -ENOMEM;
+ goto out_ior;
+ }
+
+ cafe->nand.cmdfunc = cafe_nand_cmdfunc;
+ cafe->nand.dev_ready = cafe_device_ready;
+ cafe->nand.read_byte = cafe_read_byte;
+ cafe->nand.read_buf = cafe_read_buf;
+ cafe->nand.write_buf = cafe_write_buf;
+ cafe->nand.select_chip = cafe_select_chip;
+ cafe->nand.set_features = nand_get_set_features_notsupp;
+ cafe->nand.get_features = nand_get_set_features_notsupp;
+
+ cafe->nand.chip_delay = 0;
+
+ /* Enable the following for a flash based bad block table */
+ cafe->nand.bbt_options = NAND_BBT_USE_FLASH;
+
+ if (skipbbt) {
+ cafe->nand.options |= NAND_SKIP_BBTSCAN;
+ cafe->nand.block_bad = cafe_nand_block_bad;
+ }
+
+ if (numtimings && numtimings != 3) {
+ dev_warn(&cafe->pdev->dev, "%d timing register values ignored; precisely three are required\n", numtimings);
+ }
+
+ if (numtimings == 3) {
+ cafe_dev_dbg(&cafe->pdev->dev, "Using provided timings (%08x %08x %08x)\n",
+ timing[0], timing[1], timing[2]);
+ } else {
+ timing[0] = cafe_readl(cafe, NAND_TIMING1);
+ timing[1] = cafe_readl(cafe, NAND_TIMING2);
+ timing[2] = cafe_readl(cafe, NAND_TIMING3);
+
+ if (timing[0] | timing[1] | timing[2]) {
+ cafe_dev_dbg(&cafe->pdev->dev, "Timing registers already set (%08x %08x %08x)\n",
+ timing[0], timing[1], timing[2]);
+ } else {
+ dev_warn(&cafe->pdev->dev, "Timing registers unset; using most conservative defaults\n");
+ timing[0] = timing[1] = timing[2] = 0xffffffff;
+ }
+ }
+
+ /* Start off by resetting the NAND controller completely */
+ cafe_writel(cafe, 1, NAND_RESET);
+ cafe_writel(cafe, 0, NAND_RESET);
+
+ cafe_writel(cafe, timing[0], NAND_TIMING1);
+ cafe_writel(cafe, timing[1], NAND_TIMING2);
+ cafe_writel(cafe, timing[2], NAND_TIMING3);
+
+ cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK);
+ err = request_irq(pdev->irq, &cafe_nand_interrupt, IRQF_SHARED,
+ "CAFE NAND", mtd);
+ if (err) {
+ dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq);
+ goto out_ior;
+ }
+
+ /* Disable master reset, enable NAND clock */
+ ctrl = cafe_readl(cafe, GLOBAL_CTRL);
+ ctrl &= 0xffffeff0;
+ ctrl |= 0x00007000;
+ cafe_writel(cafe, ctrl | 0x05, GLOBAL_CTRL);
+ cafe_writel(cafe, ctrl | 0x0a, GLOBAL_CTRL);
+ cafe_writel(cafe, 0, NAND_DMA_CTRL);
+
+ cafe_writel(cafe, 0x7006, GLOBAL_CTRL);
+ cafe_writel(cafe, 0x700a, GLOBAL_CTRL);
+
+ /* Enable NAND IRQ in global IRQ mask register */
+ cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK);
+ cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n",
+ cafe_readl(cafe, GLOBAL_CTRL),
+ cafe_readl(cafe, GLOBAL_IRQ_MASK));
+
+ /* Do not use the DMA for the nand_scan_ident() */
+ old_dma = usedma;
+ usedma = 0;
+
+ /* Scan to find existence of the device */
+ err = nand_scan_ident(mtd, 2, NULL);
+ if (err)
+ goto out_irq;
+
+ cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev, 2112,
+ &cafe->dmaaddr, GFP_KERNEL);
+ if (!cafe->dmabuf) {
+ err = -ENOMEM;
+ goto out_irq;
+ }
+
+ /* Set up DMA address */
+ cafe_writel(cafe, lower_32_bits(cafe->dmaaddr), NAND_DMA_ADDR0);
+ cafe_writel(cafe, upper_32_bits(cafe->dmaaddr), NAND_DMA_ADDR1);
+
+ cafe_dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n",
+ cafe_readl(cafe, NAND_DMA_ADDR0), cafe->dmabuf);
+
+ /* Restore the DMA flag */
+ usedma = old_dma;
+
+ cafe->ctl2 = 1<<27; /* Reed-Solomon ECC */
+ if (mtd->writesize == 2048)
+ cafe->ctl2 |= 1<<29; /* 2KiB page size */
+
+ /* Set up ECC according to the type of chip we found */
+ mtd_set_ooblayout(mtd, &cafe_ooblayout_ops);
+ if (mtd->writesize == 2048) {
+ cafe->nand.bbt_td = &cafe_bbt_main_descr_2048;
+ cafe->nand.bbt_md = &cafe_bbt_mirror_descr_2048;
+ } else if (mtd->writesize == 512) {
+ cafe->nand.bbt_td = &cafe_bbt_main_descr_512;
+ cafe->nand.bbt_md = &cafe_bbt_mirror_descr_512;
+ } else {
+ pr_warn("Unexpected NAND flash writesize %d. Aborting\n",
+ mtd->writesize);
+ goto out_free_dma;
+ }
+ cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME;
+ cafe->nand.ecc.size = mtd->writesize;
+ cafe->nand.ecc.bytes = 14;
+ cafe->nand.ecc.strength = 4;
+ cafe->nand.ecc.hwctl = (void *)cafe_nand_bug;
+ cafe->nand.ecc.calculate = (void *)cafe_nand_bug;
+ cafe->nand.ecc.correct = (void *)cafe_nand_bug;
+ cafe->nand.ecc.write_page = cafe_nand_write_page_lowlevel;
+ cafe->nand.ecc.write_oob = cafe_nand_write_oob;
+ cafe->nand.ecc.read_page = cafe_nand_read_page;
+ cafe->nand.ecc.read_oob = cafe_nand_read_oob;
+
+ err = nand_scan_tail(mtd);
+ if (err)
+ goto out_free_dma;
+
+ pci_set_drvdata(pdev, mtd);
+
+ mtd->name = "cafe_nand";
+ err = mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0);
+ if (err)
+ goto out_cleanup_nand;
+
+ goto out;
+
+ out_cleanup_nand:
+ nand_cleanup(&cafe->nand);
+ out_free_dma:
+ dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
+ out_irq:
+ /* Disable NAND IRQ in global IRQ mask register */
+ cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK);
+ free_irq(pdev->irq, mtd);
+ out_ior:
+ pci_iounmap(pdev, cafe->mmio);
+ out_free_mtd:
+ kfree(cafe);
+ out:
+ return err;
+}
+
+static void cafe_nand_remove(struct pci_dev *pdev)
+{
+ struct mtd_info *mtd = pci_get_drvdata(pdev);
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct cafe_priv *cafe = nand_get_controller_data(chip);
+
+ /* Disable NAND IRQ in global IRQ mask register */
+ cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK);
+ free_irq(pdev->irq, mtd);
+ nand_release(mtd);
+ free_rs(cafe->rs);
+ pci_iounmap(pdev, cafe->mmio);
+ dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
+ kfree(cafe);
+}
+
+static const struct pci_device_id cafe_nand_tbl[] = {
+ { PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_88ALP01_NAND,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { }
+};
+
+MODULE_DEVICE_TABLE(pci, cafe_nand_tbl);
+
+static int cafe_nand_resume(struct pci_dev *pdev)
+{
+ uint32_t ctrl;
+ struct mtd_info *mtd = pci_get_drvdata(pdev);
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct cafe_priv *cafe = nand_get_controller_data(chip);
+
+ /* Start off by resetting the NAND controller completely */
+ cafe_writel(cafe, 1, NAND_RESET);
+ cafe_writel(cafe, 0, NAND_RESET);
+ cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK);
+
+ /* Restore timing configuration */
+ cafe_writel(cafe, timing[0], NAND_TIMING1);
+ cafe_writel(cafe, timing[1], NAND_TIMING2);
+ cafe_writel(cafe, timing[2], NAND_TIMING3);
+
+ /* Disable master reset, enable NAND clock */
+ ctrl = cafe_readl(cafe, GLOBAL_CTRL);
+ ctrl &= 0xffffeff0;
+ ctrl |= 0x00007000;
+ cafe_writel(cafe, ctrl | 0x05, GLOBAL_CTRL);
+ cafe_writel(cafe, ctrl | 0x0a, GLOBAL_CTRL);
+ cafe_writel(cafe, 0, NAND_DMA_CTRL);
+ cafe_writel(cafe, 0x7006, GLOBAL_CTRL);
+ cafe_writel(cafe, 0x700a, GLOBAL_CTRL);
+
+ /* Set up DMA address */
+ cafe_writel(cafe, cafe->dmaaddr & 0xffffffff, NAND_DMA_ADDR0);
+ if (sizeof(cafe->dmaaddr) > 4)
+ /* Shift in two parts to shut the compiler up */
+ cafe_writel(cafe, (cafe->dmaaddr >> 16) >> 16, NAND_DMA_ADDR1);
+ else
+ cafe_writel(cafe, 0, NAND_DMA_ADDR1);
+
+ /* Enable NAND IRQ in global IRQ mask register */
+ cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK);
+ return 0;
+}
+
+static struct pci_driver cafe_nand_pci_driver = {
+ .name = "CAFÉ NAND",
+ .id_table = cafe_nand_tbl,
+ .probe = cafe_nand_probe,
+ .remove = cafe_nand_remove,
+ .resume = cafe_nand_resume,
+};
+
+module_pci_driver(cafe_nand_pci_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
+MODULE_DESCRIPTION("NAND flash driver for OLPC CAFÉ chip");
diff --git a/drivers/mtd/nand/raw/cmx270_nand.c b/drivers/mtd/nand/raw/cmx270_nand.c
new file mode 100644
index 000000000000..02d6751e9efe
--- /dev/null
+++ b/drivers/mtd/nand/raw/cmx270_nand.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2006 Compulab, Ltd.
+ * Mike Rapoport <mike@compulab.co.il>
+ *
+ * Derived from drivers/mtd/nand/h1910.c (removed in v3.10)
+ * Copyright (C) 2002 Marius Gröger (mag@sysgo.de)
+ * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
+ *
+ *
+ * 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.
+ *
+ * Overview:
+ * This is a device driver for the NAND flash device found on the
+ * CM-X270 board.
+ */
+
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/mach-types.h>
+
+#include <mach/pxa2xx-regs.h>
+
+#define GPIO_NAND_CS (11)
+#define GPIO_NAND_RB (89)
+
+/* MTD structure for CM-X270 board */
+static struct mtd_info *cmx270_nand_mtd;
+
+/* remaped IO address of the device */
+static void __iomem *cmx270_nand_io;
+
+/*
+ * Define static partitions for flash device
+ */
+static const struct mtd_partition partition_info[] = {
+ [0] = {
+ .name = "cmx270-0",
+ .offset = 0,
+ .size = MTDPART_SIZ_FULL
+ }
+};
+#define NUM_PARTITIONS (ARRAY_SIZE(partition_info))
+
+static u_char cmx270_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+
+ return (readl(this->IO_ADDR_R) >> 16);
+}
+
+static void cmx270_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+ int i;
+ struct nand_chip *this = mtd_to_nand(mtd);
+
+ for (i=0; i<len; i++)
+ writel((*buf++ << 16), this->IO_ADDR_W);
+}
+
+static void cmx270_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+ int i;
+ struct nand_chip *this = mtd_to_nand(mtd);
+
+ for (i=0; i<len; i++)
+ *buf++ = readl(this->IO_ADDR_R) >> 16;
+}
+
+static inline void nand_cs_on(void)
+{
+ gpio_set_value(GPIO_NAND_CS, 0);
+}
+
+static void nand_cs_off(void)
+{
+ dsb();
+
+ gpio_set_value(GPIO_NAND_CS, 1);
+}
+
+/*
+ * hardware specific access to control-lines
+ */
+static void cmx270_hwcontrol(struct mtd_info *mtd, int dat,
+ unsigned int ctrl)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ unsigned int nandaddr = (unsigned int)this->IO_ADDR_W;
+
+ dsb();
+
+ if (ctrl & NAND_CTRL_CHANGE) {
+ if ( ctrl & NAND_ALE )
+ nandaddr |= (1 << 3);
+ else
+ nandaddr &= ~(1 << 3);
+ if ( ctrl & NAND_CLE )
+ nandaddr |= (1 << 2);
+ else
+ nandaddr &= ~(1 << 2);
+ if ( ctrl & NAND_NCE )
+ nand_cs_on();
+ else
+ nand_cs_off();
+ }
+
+ dsb();
+ this->IO_ADDR_W = (void __iomem*)nandaddr;
+ if (dat != NAND_CMD_NONE)
+ writel((dat << 16), this->IO_ADDR_W);
+
+ dsb();
+}
+
+/*
+ * read device ready pin
+ */
+static int cmx270_device_ready(struct mtd_info *mtd)
+{
+ dsb();
+
+ return (gpio_get_value(GPIO_NAND_RB));
+}
+
+/*
+ * Main initialization routine
+ */
+static int __init cmx270_init(void)
+{
+ struct nand_chip *this;
+ int ret;
+
+ if (!(machine_is_armcore() && cpu_is_pxa27x()))
+ return -ENODEV;
+
+ ret = gpio_request(GPIO_NAND_CS, "NAND CS");
+ if (ret) {
+ pr_warn("CM-X270: failed to request NAND CS gpio\n");
+ return ret;
+ }
+
+ gpio_direction_output(GPIO_NAND_CS, 1);
+
+ ret = gpio_request(GPIO_NAND_RB, "NAND R/B");
+ if (ret) {
+ pr_warn("CM-X270: failed to request NAND R/B gpio\n");
+ goto err_gpio_request;
+ }
+
+ gpio_direction_input(GPIO_NAND_RB);
+
+ /* Allocate memory for MTD device structure and private data */
+ this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
+ if (!this) {
+ ret = -ENOMEM;
+ goto err_kzalloc;
+ }
+
+ cmx270_nand_io = ioremap(PXA_CS1_PHYS, 12);
+ if (!cmx270_nand_io) {
+ pr_debug("Unable to ioremap NAND device\n");
+ ret = -EINVAL;
+ goto err_ioremap;
+ }
+
+ cmx270_nand_mtd = nand_to_mtd(this);
+
+ /* Link the private data with the MTD structure */
+ cmx270_nand_mtd->owner = THIS_MODULE;
+
+ /* insert callbacks */
+ this->IO_ADDR_R = cmx270_nand_io;
+ this->IO_ADDR_W = cmx270_nand_io;
+ this->cmd_ctrl = cmx270_hwcontrol;
+ this->dev_ready = cmx270_device_ready;
+
+ /* 15 us command delay time */
+ this->chip_delay = 20;
+ this->ecc.mode = NAND_ECC_SOFT;
+ this->ecc.algo = NAND_ECC_HAMMING;
+
+ /* read/write functions */
+ this->read_byte = cmx270_read_byte;
+ this->read_buf = cmx270_read_buf;
+ this->write_buf = cmx270_write_buf;
+
+ /* Scan to find existence of the device */
+ ret = nand_scan(cmx270_nand_mtd, 1);
+ if (ret) {
+ pr_notice("No NAND device\n");
+ goto err_scan;
+ }
+
+ /* Register the partitions */
+ ret = mtd_device_parse_register(cmx270_nand_mtd, NULL, NULL,
+ partition_info, NUM_PARTITIONS);
+ if (ret)
+ goto err_scan;
+
+ /* Return happy */
+ return 0;
+
+err_scan:
+ iounmap(cmx270_nand_io);
+err_ioremap:
+ kfree(this);
+err_kzalloc:
+ gpio_free(GPIO_NAND_RB);
+err_gpio_request:
+ gpio_free(GPIO_NAND_CS);
+
+ return ret;
+
+}
+module_init(cmx270_init);
+
+/*
+ * Clean up routine
+ */
+static void __exit cmx270_cleanup(void)
+{
+ /* Release resources, unregister device */
+ nand_release(cmx270_nand_mtd);
+
+ gpio_free(GPIO_NAND_RB);
+ gpio_free(GPIO_NAND_CS);
+
+ iounmap(cmx270_nand_io);
+
+ kfree(mtd_to_nand(cmx270_nand_mtd));
+}
+module_exit(cmx270_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
+MODULE_DESCRIPTION("NAND flash driver for Compulab CM-X270 Module");
diff --git a/drivers/mtd/nand/raw/cs553x_nand.c b/drivers/mtd/nand/raw/cs553x_nand.c
new file mode 100644
index 000000000000..82269fde9e66
--- /dev/null
+++ b/drivers/mtd/nand/raw/cs553x_nand.c
@@ -0,0 +1,356 @@
+/*
+ * (C) 2005, 2006 Red Hat Inc.
+ *
+ * Author: David Woodhouse <dwmw2@infradead.org>
+ * Tom Sylla <tom.sylla@amd.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.
+ *
+ * Overview:
+ * This is a device driver for the NAND flash controller found on
+ * the AMD CS5535/CS5536 companion chipsets for the Geode processor.
+ * mtd-id for command line partitioning is cs553x_nand_cs[0-3]
+ * where 0-3 reflects the chip select for NAND.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/msr.h>
+#include <asm/io.h>
+
+#define NR_CS553X_CONTROLLERS 4
+
+#define MSR_DIVIL_GLD_CAP 0x51400000 /* DIVIL capabilitiies */
+#define CAP_CS5535 0x2df000ULL
+#define CAP_CS5536 0x5df500ULL
+
+/* NAND Timing MSRs */
+#define MSR_NANDF_DATA 0x5140001b /* NAND Flash Data Timing MSR */
+#define MSR_NANDF_CTL 0x5140001c /* NAND Flash Control Timing */
+#define MSR_NANDF_RSVD 0x5140001d /* Reserved */
+
+/* NAND BAR MSRs */
+#define MSR_DIVIL_LBAR_FLSH0 0x51400010 /* Flash Chip Select 0 */
+#define MSR_DIVIL_LBAR_FLSH1 0x51400011 /* Flash Chip Select 1 */
+#define MSR_DIVIL_LBAR_FLSH2 0x51400012 /* Flash Chip Select 2 */
+#define MSR_DIVIL_LBAR_FLSH3 0x51400013 /* Flash Chip Select 3 */
+ /* Each made up of... */
+#define FLSH_LBAR_EN (1ULL<<32)
+#define FLSH_NOR_NAND (1ULL<<33) /* 1 for NAND */
+#define FLSH_MEM_IO (1ULL<<34) /* 1 for MMIO */
+ /* I/O BARs have BASE_ADDR in bits 15:4, IO_MASK in 47:36 */
+ /* MMIO BARs have BASE_ADDR in bits 31:12, MEM_MASK in 63:44 */
+
+/* Pin function selection MSR (IDE vs. flash on the IDE pins) */
+#define MSR_DIVIL_BALL_OPTS 0x51400015
+#define PIN_OPT_IDE (1<<0) /* 0 for flash, 1 for IDE */
+
+/* Registers within the NAND flash controller BAR -- memory mapped */
+#define MM_NAND_DATA 0x00 /* 0 to 0x7ff, in fact */
+#define MM_NAND_CTL 0x800 /* Any even address 0x800-0x80e */
+#define MM_NAND_IO 0x801 /* Any odd address 0x801-0x80f */
+#define MM_NAND_STS 0x810
+#define MM_NAND_ECC_LSB 0x811
+#define MM_NAND_ECC_MSB 0x812
+#define MM_NAND_ECC_COL 0x813
+#define MM_NAND_LAC 0x814
+#define MM_NAND_ECC_CTL 0x815
+
+/* Registers within the NAND flash controller BAR -- I/O mapped */
+#define IO_NAND_DATA 0x00 /* 0 to 3, in fact */
+#define IO_NAND_CTL 0x04
+#define IO_NAND_IO 0x05
+#define IO_NAND_STS 0x06
+#define IO_NAND_ECC_CTL 0x08
+#define IO_NAND_ECC_LSB 0x09
+#define IO_NAND_ECC_MSB 0x0a
+#define IO_NAND_ECC_COL 0x0b
+#define IO_NAND_LAC 0x0c
+
+#define CS_NAND_CTL_DIST_EN (1<<4) /* Enable NAND Distract interrupt */
+#define CS_NAND_CTL_RDY_INT_MASK (1<<3) /* Enable RDY/BUSY# interrupt */
+#define CS_NAND_CTL_ALE (1<<2)
+#define CS_NAND_CTL_CLE (1<<1)
+#define CS_NAND_CTL_CE (1<<0) /* Keep low; 1 to reset */
+
+#define CS_NAND_STS_FLASH_RDY (1<<3)
+#define CS_NAND_CTLR_BUSY (1<<2)
+#define CS_NAND_CMD_COMP (1<<1)
+#define CS_NAND_DIST_ST (1<<0)
+
+#define CS_NAND_ECC_PARITY (1<<2)
+#define CS_NAND_ECC_CLRECC (1<<1)
+#define CS_NAND_ECC_ENECC (1<<0)
+
+static void cs553x_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+
+ while (unlikely(len > 0x800)) {
+ memcpy_fromio(buf, this->IO_ADDR_R, 0x800);
+ buf += 0x800;
+ len -= 0x800;
+ }
+ memcpy_fromio(buf, this->IO_ADDR_R, len);
+}
+
+static void cs553x_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+
+ while (unlikely(len > 0x800)) {
+ memcpy_toio(this->IO_ADDR_R, buf, 0x800);
+ buf += 0x800;
+ len -= 0x800;
+ }
+ memcpy_toio(this->IO_ADDR_R, buf, len);
+}
+
+static unsigned char cs553x_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ return readb(this->IO_ADDR_R);
+}
+
+static void cs553x_write_byte(struct mtd_info *mtd, u_char byte)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ int i = 100000;
+
+ while (i && readb(this->IO_ADDR_R + MM_NAND_STS) & CS_NAND_CTLR_BUSY) {
+ udelay(1);
+ i--;
+ }
+ writeb(byte, this->IO_ADDR_W + 0x801);
+}
+
+static void cs553x_hwcontrol(struct mtd_info *mtd, int cmd,
+ unsigned int ctrl)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ void __iomem *mmio_base = this->IO_ADDR_R;
+ if (ctrl & NAND_CTRL_CHANGE) {
+ unsigned char ctl = (ctrl & ~NAND_CTRL_CHANGE ) ^ 0x01;
+ writeb(ctl, mmio_base + MM_NAND_CTL);
+ }
+ if (cmd != NAND_CMD_NONE)
+ cs553x_write_byte(mtd, cmd);
+}
+
+static int cs553x_device_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ void __iomem *mmio_base = this->IO_ADDR_R;
+ unsigned char foo = readb(mmio_base + MM_NAND_STS);
+
+ return (foo & CS_NAND_STS_FLASH_RDY) && !(foo & CS_NAND_CTLR_BUSY);
+}
+
+static void cs_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ void __iomem *mmio_base = this->IO_ADDR_R;
+
+ writeb(0x07, mmio_base + MM_NAND_ECC_CTL);
+}
+
+static int cs_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
+{
+ uint32_t ecc;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ void __iomem *mmio_base = this->IO_ADDR_R;
+
+ ecc = readl(mmio_base + MM_NAND_STS);
+
+ ecc_code[1] = ecc >> 8;
+ ecc_code[0] = ecc >> 16;
+ ecc_code[2] = ecc >> 24;
+ return 0;
+}
+
+static struct mtd_info *cs553x_mtd[4];
+
+static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
+{
+ int err = 0;
+ struct nand_chip *this;
+ struct mtd_info *new_mtd;
+
+ pr_notice("Probing CS553x NAND controller CS#%d at %sIO 0x%08lx\n",
+ cs, mmio ? "MM" : "P", adr);
+
+ if (!mmio) {
+ pr_notice("PIO mode not yet implemented for CS553X NAND controller\n");
+ return -ENXIO;
+ }
+
+ /* Allocate memory for MTD device structure and private data */
+ this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
+ if (!this) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ new_mtd = nand_to_mtd(this);
+
+ /* Link the private data with the MTD structure */
+ new_mtd->owner = THIS_MODULE;
+
+ /* map physical address */
+ this->IO_ADDR_R = this->IO_ADDR_W = ioremap(adr, 4096);
+ if (!this->IO_ADDR_R) {
+ pr_warn("ioremap cs553x NAND @0x%08lx failed\n", adr);
+ err = -EIO;
+ goto out_mtd;
+ }
+
+ this->cmd_ctrl = cs553x_hwcontrol;
+ this->dev_ready = cs553x_device_ready;
+ this->read_byte = cs553x_read_byte;
+ this->read_buf = cs553x_read_buf;
+ this->write_buf = cs553x_write_buf;
+
+ this->chip_delay = 0;
+
+ this->ecc.mode = NAND_ECC_HW;
+ this->ecc.size = 256;
+ this->ecc.bytes = 3;
+ this->ecc.hwctl = cs_enable_hwecc;
+ this->ecc.calculate = cs_calculate_ecc;
+ this->ecc.correct = nand_correct_data;
+ this->ecc.strength = 1;
+
+ /* Enable the following for a flash based bad block table */
+ this->bbt_options = NAND_BBT_USE_FLASH;
+
+ new_mtd->name = kasprintf(GFP_KERNEL, "cs553x_nand_cs%d", cs);
+ if (!new_mtd->name) {
+ err = -ENOMEM;
+ goto out_ior;
+ }
+
+ /* Scan to find existence of the device */
+ err = nand_scan(new_mtd, 1);
+ if (err)
+ goto out_free;
+
+ cs553x_mtd[cs] = new_mtd;
+ goto out;
+
+out_free:
+ kfree(new_mtd->name);
+out_ior:
+ iounmap(this->IO_ADDR_R);
+out_mtd:
+ kfree(this);
+out:
+ return err;
+}
+
+static int is_geode(void)
+{
+ /* These are the CPUs which will have a CS553[56] companion chip */
+ if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD &&
+ boot_cpu_data.x86 == 5 &&
+ boot_cpu_data.x86_model == 10)
+ return 1; /* Geode LX */
+
+ if ((boot_cpu_data.x86_vendor == X86_VENDOR_NSC ||
+ boot_cpu_data.x86_vendor == X86_VENDOR_CYRIX) &&
+ boot_cpu_data.x86 == 5 &&
+ boot_cpu_data.x86_model == 5)
+ return 1; /* Geode GX (née GX2) */
+
+ return 0;
+}
+
+static int __init cs553x_init(void)
+{
+ int err = -ENXIO;
+ int i;
+ uint64_t val;
+
+ /* If the CPU isn't a Geode GX or LX, abort */
+ if (!is_geode())
+ return -ENXIO;
+
+ /* If it doesn't have the CS553[56], abort */
+ rdmsrl(MSR_DIVIL_GLD_CAP, val);
+ val &= ~0xFFULL;
+ if (val != CAP_CS5535 && val != CAP_CS5536)
+ return -ENXIO;
+
+ /* If it doesn't have the NAND controller enabled, abort */
+ rdmsrl(MSR_DIVIL_BALL_OPTS, val);
+ if (val & PIN_OPT_IDE) {
+ pr_info("CS553x NAND controller: Flash I/O not enabled in MSR_DIVIL_BALL_OPTS.\n");
+ return -ENXIO;
+ }
+
+ for (i = 0; i < NR_CS553X_CONTROLLERS; i++) {
+ rdmsrl(MSR_DIVIL_LBAR_FLSH0 + i, val);
+
+ if ((val & (FLSH_LBAR_EN|FLSH_NOR_NAND)) == (FLSH_LBAR_EN|FLSH_NOR_NAND))
+ err = cs553x_init_one(i, !!(val & FLSH_MEM_IO), val & 0xFFFFFFFF);
+ }
+
+ /* Register all devices together here. This means we can easily hack it to
+ do mtdconcat etc. if we want to. */
+ for (i = 0; i < NR_CS553X_CONTROLLERS; i++) {
+ if (cs553x_mtd[i]) {
+ /* If any devices registered, return success. Else the last error. */
+ mtd_device_parse_register(cs553x_mtd[i], NULL, NULL,
+ NULL, 0);
+ err = 0;
+ }
+ }
+
+ return err;
+}
+
+module_init(cs553x_init);
+
+static void __exit cs553x_cleanup(void)
+{
+ int i;
+
+ for (i = 0; i < NR_CS553X_CONTROLLERS; i++) {
+ struct mtd_info *mtd = cs553x_mtd[i];
+ struct nand_chip *this;
+ void __iomem *mmio_base;
+
+ if (!mtd)
+ continue;
+
+ this = mtd_to_nand(mtd);
+ mmio_base = this->IO_ADDR_R;
+
+ /* Release resources, unregister device */
+ nand_release(mtd);
+ kfree(mtd->name);
+ cs553x_mtd[i] = NULL;
+
+ /* unmap physical address */
+ iounmap(mmio_base);
+
+ /* Free the MTD device structure */
+ kfree(this);
+ }
+}
+
+module_exit(cs553x_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
+MODULE_DESCRIPTION("NAND controller driver for AMD CS5535/CS5536 companion chip");
diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c
new file mode 100644
index 000000000000..0f09518d980f
--- /dev/null
+++ b/drivers/mtd/nand/raw/davinci_nand.c
@@ -0,0 +1,882 @@
+/*
+ * davinci_nand.c - NAND Flash Driver for DaVinci family chips
+ *
+ * Copyright © 2006 Texas Instruments.
+ *
+ * Port to 2.6.23 Copyright © 2008 by:
+ * Sander Huijsen <Shuijsen@optelecom-nkf.com>
+ * Troy Kisky <troy.kisky@boundarydevices.com>
+ * Dirk Behme <Dirk.Behme@gmail.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/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+
+#include <linux/platform_data/mtd-davinci.h>
+#include <linux/platform_data/mtd-davinci-aemif.h>
+
+/*
+ * This is a device driver for the NAND flash controller found on the
+ * various DaVinci family chips. It handles up to four SoC chipselects,
+ * and some flavors of secondary chipselect (e.g. based on A12) as used
+ * with multichip packages.
+ *
+ * The 1-bit ECC hardware is supported, as well as the newer 4-bit ECC
+ * available on chips like the DM355 and OMAP-L137 and needed with the
+ * more error-prone MLC NAND chips.
+ *
+ * This driver assumes EM_WAIT connects all the NAND devices' RDY/nBUSY
+ * outputs in a "wire-AND" configuration, with no per-chip signals.
+ */
+struct davinci_nand_info {
+ struct nand_chip chip;
+
+ struct device *dev;
+ struct clk *clk;
+
+ bool is_readmode;
+
+ void __iomem *base;
+ void __iomem *vaddr;
+
+ uint32_t ioaddr;
+ uint32_t current_cs;
+
+ uint32_t mask_chipsel;
+ uint32_t mask_ale;
+ uint32_t mask_cle;
+
+ uint32_t core_chipsel;
+
+ struct davinci_aemif_timing *timing;
+};
+
+static DEFINE_SPINLOCK(davinci_nand_lock);
+static bool ecc4_busy;
+
+static inline struct davinci_nand_info *to_davinci_nand(struct mtd_info *mtd)
+{
+ return container_of(mtd_to_nand(mtd), struct davinci_nand_info, chip);
+}
+
+static inline unsigned int davinci_nand_readl(struct davinci_nand_info *info,
+ int offset)
+{
+ return __raw_readl(info->base + offset);
+}
+
+static inline void davinci_nand_writel(struct davinci_nand_info *info,
+ int offset, unsigned long value)
+{
+ __raw_writel(value, info->base + offset);
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Access to hardware control lines: ALE, CLE, secondary chipselect.
+ */
+
+static void nand_davinci_hwcontrol(struct mtd_info *mtd, int cmd,
+ unsigned int ctrl)
+{
+ struct davinci_nand_info *info = to_davinci_nand(mtd);
+ uint32_t addr = info->current_cs;
+ struct nand_chip *nand = mtd_to_nand(mtd);
+
+ /* Did the control lines change? */
+ if (ctrl & NAND_CTRL_CHANGE) {
+ if ((ctrl & NAND_CTRL_CLE) == NAND_CTRL_CLE)
+ addr |= info->mask_cle;
+ else if ((ctrl & NAND_CTRL_ALE) == NAND_CTRL_ALE)
+ addr |= info->mask_ale;
+
+ nand->IO_ADDR_W = (void __iomem __force *)addr;
+ }
+
+ if (cmd != NAND_CMD_NONE)
+ iowrite8(cmd, nand->IO_ADDR_W);
+}
+
+static void nand_davinci_select_chip(struct mtd_info *mtd, int chip)
+{
+ struct davinci_nand_info *info = to_davinci_nand(mtd);
+ uint32_t addr = info->ioaddr;
+
+ /* maybe kick in a second chipselect */
+ if (chip > 0)
+ addr |= info->mask_chipsel;
+ info->current_cs = addr;
+
+ info->chip.IO_ADDR_W = (void __iomem __force *)addr;
+ info->chip.IO_ADDR_R = info->chip.IO_ADDR_W;
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * 1-bit hardware ECC ... context maintained for each core chipselect
+ */
+
+static inline uint32_t nand_davinci_readecc_1bit(struct mtd_info *mtd)
+{
+ struct davinci_nand_info *info = to_davinci_nand(mtd);
+
+ return davinci_nand_readl(info, NANDF1ECC_OFFSET
+ + 4 * info->core_chipsel);
+}
+
+static void nand_davinci_hwctl_1bit(struct mtd_info *mtd, int mode)
+{
+ struct davinci_nand_info *info;
+ uint32_t nandcfr;
+ unsigned long flags;
+
+ info = to_davinci_nand(mtd);
+
+ /* Reset ECC hardware */
+ nand_davinci_readecc_1bit(mtd);
+
+ spin_lock_irqsave(&davinci_nand_lock, flags);
+
+ /* Restart ECC hardware */
+ nandcfr = davinci_nand_readl(info, NANDFCR_OFFSET);
+ nandcfr |= BIT(8 + info->core_chipsel);
+ davinci_nand_writel(info, NANDFCR_OFFSET, nandcfr);
+
+ spin_unlock_irqrestore(&davinci_nand_lock, flags);
+}
+
+/*
+ * Read hardware ECC value and pack into three bytes
+ */
+static int nand_davinci_calculate_1bit(struct mtd_info *mtd,
+ const u_char *dat, u_char *ecc_code)
+{
+ unsigned int ecc_val = nand_davinci_readecc_1bit(mtd);
+ unsigned int ecc24 = (ecc_val & 0x0fff) | ((ecc_val & 0x0fff0000) >> 4);
+
+ /* invert so that erased block ecc is correct */
+ ecc24 = ~ecc24;
+ ecc_code[0] = (u_char)(ecc24);
+ ecc_code[1] = (u_char)(ecc24 >> 8);
+ ecc_code[2] = (u_char)(ecc24 >> 16);
+
+ return 0;
+}
+
+static int nand_davinci_correct_1bit(struct mtd_info *mtd, u_char *dat,
+ u_char *read_ecc, u_char *calc_ecc)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ uint32_t eccNand = read_ecc[0] | (read_ecc[1] << 8) |
+ (read_ecc[2] << 16);
+ uint32_t eccCalc = calc_ecc[0] | (calc_ecc[1] << 8) |
+ (calc_ecc[2] << 16);
+ uint32_t diff = eccCalc ^ eccNand;
+
+ if (diff) {
+ if ((((diff >> 12) ^ diff) & 0xfff) == 0xfff) {
+ /* Correctable error */
+ if ((diff >> (12 + 3)) < chip->ecc.size) {
+ dat[diff >> (12 + 3)] ^= BIT((diff >> 12) & 7);
+ return 1;
+ } else {
+ return -EBADMSG;
+ }
+ } else if (!(diff & (diff - 1))) {
+ /* Single bit ECC error in the ECC itself,
+ * nothing to fix */
+ return 1;
+ } else {
+ /* Uncorrectable error */
+ return -EBADMSG;
+ }
+
+ }
+ return 0;
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * 4-bit hardware ECC ... context maintained over entire AEMIF
+ *
+ * This is a syndrome engine, but we avoid NAND_ECC_HW_SYNDROME
+ * since that forces use of a problematic "infix OOB" layout.
+ * Among other things, it trashes manufacturer bad block markers.
+ * Also, and specific to this hardware, it ECC-protects the "prepad"
+ * in the OOB ... while having ECC protection for parts of OOB would
+ * seem useful, the current MTD stack sometimes wants to update the
+ * OOB without recomputing ECC.
+ */
+
+static void nand_davinci_hwctl_4bit(struct mtd_info *mtd, int mode)
+{
+ struct davinci_nand_info *info = to_davinci_nand(mtd);
+ unsigned long flags;
+ u32 val;
+
+ /* Reset ECC hardware */
+ davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET);
+
+ spin_lock_irqsave(&davinci_nand_lock, flags);
+
+ /* Start 4-bit ECC calculation for read/write */
+ val = davinci_nand_readl(info, NANDFCR_OFFSET);
+ val &= ~(0x03 << 4);
+ val |= (info->core_chipsel << 4) | BIT(12);
+ davinci_nand_writel(info, NANDFCR_OFFSET, val);
+
+ info->is_readmode = (mode == NAND_ECC_READ);
+
+ spin_unlock_irqrestore(&davinci_nand_lock, flags);
+}
+
+/* Read raw ECC code after writing to NAND. */
+static void
+nand_davinci_readecc_4bit(struct davinci_nand_info *info, u32 code[4])
+{
+ const u32 mask = 0x03ff03ff;
+
+ code[0] = davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET) & mask;
+ code[1] = davinci_nand_readl(info, NAND_4BIT_ECC2_OFFSET) & mask;
+ code[2] = davinci_nand_readl(info, NAND_4BIT_ECC3_OFFSET) & mask;
+ code[3] = davinci_nand_readl(info, NAND_4BIT_ECC4_OFFSET) & mask;
+}
+
+/* Terminate read ECC; or return ECC (as bytes) of data written to NAND. */
+static int nand_davinci_calculate_4bit(struct mtd_info *mtd,
+ const u_char *dat, u_char *ecc_code)
+{
+ struct davinci_nand_info *info = to_davinci_nand(mtd);
+ u32 raw_ecc[4], *p;
+ unsigned i;
+
+ /* After a read, terminate ECC calculation by a dummy read
+ * of some 4-bit ECC register. ECC covers everything that
+ * was read; correct() just uses the hardware state, so
+ * ecc_code is not needed.
+ */
+ if (info->is_readmode) {
+ davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET);
+ return 0;
+ }
+
+ /* Pack eight raw 10-bit ecc values into ten bytes, making
+ * two passes which each convert four values (in upper and
+ * lower halves of two 32-bit words) into five bytes. The
+ * ROM boot loader uses this same packing scheme.
+ */
+ nand_davinci_readecc_4bit(info, raw_ecc);
+ for (i = 0, p = raw_ecc; i < 2; i++, p += 2) {
+ *ecc_code++ = p[0] & 0xff;
+ *ecc_code++ = ((p[0] >> 8) & 0x03) | ((p[0] >> 14) & 0xfc);
+ *ecc_code++ = ((p[0] >> 22) & 0x0f) | ((p[1] << 4) & 0xf0);
+ *ecc_code++ = ((p[1] >> 4) & 0x3f) | ((p[1] >> 10) & 0xc0);
+ *ecc_code++ = (p[1] >> 18) & 0xff;
+ }
+
+ return 0;
+}
+
+/* Correct up to 4 bits in data we just read, using state left in the
+ * hardware plus the ecc_code computed when it was first written.
+ */
+static int nand_davinci_correct_4bit(struct mtd_info *mtd,
+ u_char *data, u_char *ecc_code, u_char *null)
+{
+ int i;
+ struct davinci_nand_info *info = to_davinci_nand(mtd);
+ unsigned short ecc10[8];
+ unsigned short *ecc16;
+ u32 syndrome[4];
+ u32 ecc_state;
+ unsigned num_errors, corrected;
+ unsigned long timeo;
+
+ /* Unpack ten bytes into eight 10 bit values. We know we're
+ * little-endian, and use type punning for less shifting/masking.
+ */
+ if (WARN_ON(0x01 & (unsigned) ecc_code))
+ return -EINVAL;
+ ecc16 = (unsigned short *)ecc_code;
+
+ ecc10[0] = (ecc16[0] >> 0) & 0x3ff;
+ ecc10[1] = ((ecc16[0] >> 10) & 0x3f) | ((ecc16[1] << 6) & 0x3c0);
+ ecc10[2] = (ecc16[1] >> 4) & 0x3ff;
+ ecc10[3] = ((ecc16[1] >> 14) & 0x3) | ((ecc16[2] << 2) & 0x3fc);
+ ecc10[4] = (ecc16[2] >> 8) | ((ecc16[3] << 8) & 0x300);
+ ecc10[5] = (ecc16[3] >> 2) & 0x3ff;
+ ecc10[6] = ((ecc16[3] >> 12) & 0xf) | ((ecc16[4] << 4) & 0x3f0);
+ ecc10[7] = (ecc16[4] >> 6) & 0x3ff;
+
+ /* Tell ECC controller about the expected ECC codes. */
+ for (i = 7; i >= 0; i--)
+ davinci_nand_writel(info, NAND_4BIT_ECC_LOAD_OFFSET, ecc10[i]);
+
+ /* Allow time for syndrome calculation ... then read it.
+ * A syndrome of all zeroes 0 means no detected errors.
+ */
+ davinci_nand_readl(info, NANDFSR_OFFSET);
+ nand_davinci_readecc_4bit(info, syndrome);
+ if (!(syndrome[0] | syndrome[1] | syndrome[2] | syndrome[3]))
+ return 0;
+
+ /*
+ * Clear any previous address calculation by doing a dummy read of an
+ * error address register.
+ */
+ davinci_nand_readl(info, NAND_ERR_ADD1_OFFSET);
+
+ /* Start address calculation, and wait for it to complete.
+ * We _could_ start reading more data while this is working,
+ * to speed up the overall page read.
+ */
+ davinci_nand_writel(info, NANDFCR_OFFSET,
+ davinci_nand_readl(info, NANDFCR_OFFSET) | BIT(13));
+
+ /*
+ * ECC_STATE field reads 0x3 (Error correction complete) immediately
+ * after setting the 4BITECC_ADD_CALC_START bit. So if you immediately
+ * begin trying to poll for the state, you may fall right out of your
+ * loop without any of the correction calculations having taken place.
+ * The recommendation from the hardware team is to initially delay as
+ * long as ECC_STATE reads less than 4. After that, ECC HW has entered
+ * correction state.
+ */
+ timeo = jiffies + usecs_to_jiffies(100);
+ do {
+ ecc_state = (davinci_nand_readl(info,
+ NANDFSR_OFFSET) >> 8) & 0x0f;
+ cpu_relax();
+ } while ((ecc_state < 4) && time_before(jiffies, timeo));
+
+ for (;;) {
+ u32 fsr = davinci_nand_readl(info, NANDFSR_OFFSET);
+
+ switch ((fsr >> 8) & 0x0f) {
+ case 0: /* no error, should not happen */
+ davinci_nand_readl(info, NAND_ERR_ERRVAL1_OFFSET);
+ return 0;
+ case 1: /* five or more errors detected */
+ davinci_nand_readl(info, NAND_ERR_ERRVAL1_OFFSET);
+ return -EBADMSG;
+ case 2: /* error addresses computed */
+ case 3:
+ num_errors = 1 + ((fsr >> 16) & 0x03);
+ goto correct;
+ default: /* still working on it */
+ cpu_relax();
+ continue;
+ }
+ }
+
+correct:
+ /* correct each error */
+ for (i = 0, corrected = 0; i < num_errors; i++) {
+ int error_address, error_value;
+
+ if (i > 1) {
+ error_address = davinci_nand_readl(info,
+ NAND_ERR_ADD2_OFFSET);
+ error_value = davinci_nand_readl(info,
+ NAND_ERR_ERRVAL2_OFFSET);
+ } else {
+ error_address = davinci_nand_readl(info,
+ NAND_ERR_ADD1_OFFSET);
+ error_value = davinci_nand_readl(info,
+ NAND_ERR_ERRVAL1_OFFSET);
+ }
+
+ if (i & 1) {
+ error_address >>= 16;
+ error_value >>= 16;
+ }
+ error_address &= 0x3ff;
+ error_address = (512 + 7) - error_address;
+
+ if (error_address < 512) {
+ data[error_address] ^= error_value;
+ corrected++;
+ }
+ }
+
+ return corrected;
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * NOTE: NAND boot requires ALE == EM_A[1], CLE == EM_A[2], so that's
+ * how these chips are normally wired. This translates to both 8 and 16
+ * bit busses using ALE == BIT(3) in byte addresses, and CLE == BIT(4).
+ *
+ * For now we assume that configuration, or any other one which ignores
+ * the two LSBs for NAND access ... so we can issue 32-bit reads/writes
+ * and have that transparently morphed into multiple NAND operations.
+ */
+static void nand_davinci_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if ((0x03 & ((unsigned)buf)) == 0 && (0x03 & len) == 0)
+ ioread32_rep(chip->IO_ADDR_R, buf, len >> 2);
+ else if ((0x01 & ((unsigned)buf)) == 0 && (0x01 & len) == 0)
+ ioread16_rep(chip->IO_ADDR_R, buf, len >> 1);
+ else
+ ioread8_rep(chip->IO_ADDR_R, buf, len);
+}
+
+static void nand_davinci_write_buf(struct mtd_info *mtd,
+ const uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if ((0x03 & ((unsigned)buf)) == 0 && (0x03 & len) == 0)
+ iowrite32_rep(chip->IO_ADDR_R, buf, len >> 2);
+ else if ((0x01 & ((unsigned)buf)) == 0 && (0x01 & len) == 0)
+ iowrite16_rep(chip->IO_ADDR_R, buf, len >> 1);
+ else
+ iowrite8_rep(chip->IO_ADDR_R, buf, len);
+}
+
+/*
+ * Check hardware register for wait status. Returns 1 if device is ready,
+ * 0 if it is still busy.
+ */
+static int nand_davinci_dev_ready(struct mtd_info *mtd)
+{
+ struct davinci_nand_info *info = to_davinci_nand(mtd);
+
+ return davinci_nand_readl(info, NANDFSR_OFFSET) & BIT(0);
+}
+
+/*----------------------------------------------------------------------*/
+
+/* An ECC layout for using 4-bit ECC with small-page flash, storing
+ * ten ECC bytes plus the manufacturer's bad block marker byte, and
+ * and not overlapping the default BBT markers.
+ */
+static int hwecc4_ooblayout_small_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ if (section > 2)
+ return -ERANGE;
+
+ if (!section) {
+ oobregion->offset = 0;
+ oobregion->length = 5;
+ } else if (section == 1) {
+ oobregion->offset = 6;
+ oobregion->length = 2;
+ } else {
+ oobregion->offset = 13;
+ oobregion->length = 3;
+ }
+
+ return 0;
+}
+
+static int hwecc4_ooblayout_small_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ if (section > 1)
+ return -ERANGE;
+
+ if (!section) {
+ oobregion->offset = 8;
+ oobregion->length = 5;
+ } else {
+ oobregion->offset = 16;
+ oobregion->length = mtd->oobsize - 16;
+ }
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops hwecc4_small_ooblayout_ops = {
+ .ecc = hwecc4_ooblayout_small_ecc,
+ .free = hwecc4_ooblayout_small_free,
+};
+
+#if defined(CONFIG_OF)
+static const struct of_device_id davinci_nand_of_match[] = {
+ {.compatible = "ti,davinci-nand", },
+ {.compatible = "ti,keystone-nand", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, davinci_nand_of_match);
+
+static struct davinci_nand_pdata
+ *nand_davinci_get_pdata(struct platform_device *pdev)
+{
+ if (!dev_get_platdata(&pdev->dev) && pdev->dev.of_node) {
+ struct davinci_nand_pdata *pdata;
+ const char *mode;
+ u32 prop;
+
+ pdata = devm_kzalloc(&pdev->dev,
+ sizeof(struct davinci_nand_pdata),
+ GFP_KERNEL);
+ pdev->dev.platform_data = pdata;
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+ if (!of_property_read_u32(pdev->dev.of_node,
+ "ti,davinci-chipselect", &prop))
+ pdev->id = prop;
+ else
+ return ERR_PTR(-EINVAL);
+
+ if (!of_property_read_u32(pdev->dev.of_node,
+ "ti,davinci-mask-ale", &prop))
+ pdata->mask_ale = prop;
+ if (!of_property_read_u32(pdev->dev.of_node,
+ "ti,davinci-mask-cle", &prop))
+ pdata->mask_cle = prop;
+ if (!of_property_read_u32(pdev->dev.of_node,
+ "ti,davinci-mask-chipsel", &prop))
+ pdata->mask_chipsel = prop;
+ if (!of_property_read_string(pdev->dev.of_node,
+ "ti,davinci-ecc-mode", &mode)) {
+ if (!strncmp("none", mode, 4))
+ pdata->ecc_mode = NAND_ECC_NONE;
+ if (!strncmp("soft", mode, 4))
+ pdata->ecc_mode = NAND_ECC_SOFT;
+ if (!strncmp("hw", mode, 2))
+ pdata->ecc_mode = NAND_ECC_HW;
+ }
+ if (!of_property_read_u32(pdev->dev.of_node,
+ "ti,davinci-ecc-bits", &prop))
+ pdata->ecc_bits = prop;
+
+ if (!of_property_read_u32(pdev->dev.of_node,
+ "ti,davinci-nand-buswidth", &prop) && prop == 16)
+ pdata->options |= NAND_BUSWIDTH_16;
+
+ if (of_property_read_bool(pdev->dev.of_node,
+ "ti,davinci-nand-use-bbt"))
+ pdata->bbt_options = NAND_BBT_USE_FLASH;
+
+ /*
+ * Since kernel v4.8, this driver has been fixed to enable
+ * use of 4-bit hardware ECC with subpages and verified on
+ * TI's keystone EVMs (K2L, K2HK and K2E).
+ * However, in the interest of not breaking systems using
+ * existing UBI partitions, sub-page writes are not being
+ * (re)enabled. If you want to use subpage writes on Keystone
+ * platforms (i.e. do not have any existing UBI partitions),
+ * then use "ti,davinci-nand" as the compatible in your
+ * device-tree file.
+ */
+ if (of_device_is_compatible(pdev->dev.of_node,
+ "ti,keystone-nand")) {
+ pdata->options |= NAND_NO_SUBPAGE_WRITE;
+ }
+ }
+
+ return dev_get_platdata(&pdev->dev);
+}
+#else
+static struct davinci_nand_pdata
+ *nand_davinci_get_pdata(struct platform_device *pdev)
+{
+ return dev_get_platdata(&pdev->dev);
+}
+#endif
+
+static int nand_davinci_probe(struct platform_device *pdev)
+{
+ struct davinci_nand_pdata *pdata;
+ struct davinci_nand_info *info;
+ struct resource *res1;
+ struct resource *res2;
+ void __iomem *vaddr;
+ void __iomem *base;
+ int ret;
+ uint32_t val;
+ struct mtd_info *mtd;
+
+ pdata = nand_davinci_get_pdata(pdev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+
+ /* insist on board-specific configuration */
+ if (!pdata)
+ return -ENODEV;
+
+ /* which external chipselect will we be managing? */
+ if (pdev->id < 0 || pdev->id > 3)
+ return -ENODEV;
+
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, info);
+
+ res1 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res1 || !res2) {
+ dev_err(&pdev->dev, "resource missing\n");
+ return -EINVAL;
+ }
+
+ vaddr = devm_ioremap_resource(&pdev->dev, res1);
+ if (IS_ERR(vaddr))
+ return PTR_ERR(vaddr);
+
+ /*
+ * This registers range is used to setup NAND settings. In case with
+ * TI AEMIF driver, the same memory address range is requested already
+ * by AEMIF, so we cannot request it twice, just ioremap.
+ * The AEMIF and NAND drivers not use the same registers in this range.
+ */
+ base = devm_ioremap(&pdev->dev, res2->start, resource_size(res2));
+ if (!base) {
+ dev_err(&pdev->dev, "ioremap failed for resource %pR\n", res2);
+ return -EADDRNOTAVAIL;
+ }
+
+ info->dev = &pdev->dev;
+ info->base = base;
+ info->vaddr = vaddr;
+
+ mtd = nand_to_mtd(&info->chip);
+ mtd->dev.parent = &pdev->dev;
+ nand_set_flash_node(&info->chip, pdev->dev.of_node);
+
+ info->chip.IO_ADDR_R = vaddr;
+ info->chip.IO_ADDR_W = vaddr;
+ info->chip.chip_delay = 0;
+ info->chip.select_chip = nand_davinci_select_chip;
+
+ /* options such as NAND_BBT_USE_FLASH */
+ info->chip.bbt_options = pdata->bbt_options;
+ /* options such as 16-bit widths */
+ info->chip.options = pdata->options;
+ info->chip.bbt_td = pdata->bbt_td;
+ info->chip.bbt_md = pdata->bbt_md;
+ info->timing = pdata->timing;
+
+ info->ioaddr = (uint32_t __force) vaddr;
+
+ info->current_cs = info->ioaddr;
+ info->core_chipsel = pdev->id;
+ info->mask_chipsel = pdata->mask_chipsel;
+
+ /* use nandboot-capable ALE/CLE masks by default */
+ info->mask_ale = pdata->mask_ale ? : MASK_ALE;
+ info->mask_cle = pdata->mask_cle ? : MASK_CLE;
+
+ /* Set address of hardware control function */
+ info->chip.cmd_ctrl = nand_davinci_hwcontrol;
+ info->chip.dev_ready = nand_davinci_dev_ready;
+
+ /* Speed up buffer I/O */
+ info->chip.read_buf = nand_davinci_read_buf;
+ info->chip.write_buf = nand_davinci_write_buf;
+
+ /* Use board-specific ECC config */
+ info->chip.ecc.mode = pdata->ecc_mode;
+
+ ret = -EINVAL;
+
+ info->clk = devm_clk_get(&pdev->dev, "aemif");
+ if (IS_ERR(info->clk)) {
+ ret = PTR_ERR(info->clk);
+ dev_dbg(&pdev->dev, "unable to get AEMIF clock, err %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(info->clk);
+ if (ret < 0) {
+ dev_dbg(&pdev->dev, "unable to enable AEMIF clock, err %d\n",
+ ret);
+ goto err_clk_enable;
+ }
+
+ spin_lock_irq(&davinci_nand_lock);
+
+ /* put CSxNAND into NAND mode */
+ val = davinci_nand_readl(info, NANDFCR_OFFSET);
+ val |= BIT(info->core_chipsel);
+ davinci_nand_writel(info, NANDFCR_OFFSET, val);
+
+ spin_unlock_irq(&davinci_nand_lock);
+
+ /* Scan to find existence of the device(s) */
+ ret = nand_scan_ident(mtd, pdata->mask_chipsel ? 2 : 1, NULL);
+ if (ret < 0) {
+ dev_dbg(&pdev->dev, "no NAND chip(s) found\n");
+ goto err;
+ }
+
+ switch (info->chip.ecc.mode) {
+ case NAND_ECC_NONE:
+ pdata->ecc_bits = 0;
+ break;
+ case NAND_ECC_SOFT:
+ pdata->ecc_bits = 0;
+ /*
+ * This driver expects Hamming based ECC when ecc_mode is set
+ * to NAND_ECC_SOFT. Force ecc.algo to NAND_ECC_HAMMING to
+ * avoid adding an extra ->ecc_algo field to
+ * davinci_nand_pdata.
+ */
+ info->chip.ecc.algo = NAND_ECC_HAMMING;
+ break;
+ case NAND_ECC_HW:
+ if (pdata->ecc_bits == 4) {
+ /* No sanity checks: CPUs must support this,
+ * and the chips may not use NAND_BUSWIDTH_16.
+ */
+
+ /* No sharing 4-bit hardware between chipselects yet */
+ spin_lock_irq(&davinci_nand_lock);
+ if (ecc4_busy)
+ ret = -EBUSY;
+ else
+ ecc4_busy = true;
+ spin_unlock_irq(&davinci_nand_lock);
+
+ if (ret == -EBUSY)
+ return ret;
+
+ info->chip.ecc.calculate = nand_davinci_calculate_4bit;
+ info->chip.ecc.correct = nand_davinci_correct_4bit;
+ info->chip.ecc.hwctl = nand_davinci_hwctl_4bit;
+ info->chip.ecc.bytes = 10;
+ info->chip.ecc.options = NAND_ECC_GENERIC_ERASED_CHECK;
+ info->chip.ecc.algo = NAND_ECC_BCH;
+ } else {
+ /* 1bit ecc hamming */
+ info->chip.ecc.calculate = nand_davinci_calculate_1bit;
+ info->chip.ecc.correct = nand_davinci_correct_1bit;
+ info->chip.ecc.hwctl = nand_davinci_hwctl_1bit;
+ info->chip.ecc.bytes = 3;
+ info->chip.ecc.algo = NAND_ECC_HAMMING;
+ }
+ info->chip.ecc.size = 512;
+ info->chip.ecc.strength = pdata->ecc_bits;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Update ECC layout if needed ... for 1-bit HW ECC, the default
+ * is OK, but it allocates 6 bytes when only 3 are needed (for
+ * each 512 bytes). For the 4-bit HW ECC, that default is not
+ * usable: 10 bytes are needed, not 6.
+ */
+ if (pdata->ecc_bits == 4) {
+ int chunks = mtd->writesize / 512;
+
+ if (!chunks || mtd->oobsize < 16) {
+ dev_dbg(&pdev->dev, "too small\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* For small page chips, preserve the manufacturer's
+ * badblock marking data ... and make sure a flash BBT
+ * table marker fits in the free bytes.
+ */
+ if (chunks == 1) {
+ mtd_set_ooblayout(mtd, &hwecc4_small_ooblayout_ops);
+ } else if (chunks == 4 || chunks == 8) {
+ mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
+ info->chip.ecc.mode = NAND_ECC_HW_OOB_FIRST;
+ } else {
+ ret = -EIO;
+ goto err;
+ }
+ }
+
+ ret = nand_scan_tail(mtd);
+ if (ret < 0)
+ goto err;
+
+ if (pdata->parts)
+ ret = mtd_device_parse_register(mtd, NULL, NULL,
+ pdata->parts, pdata->nr_parts);
+ else
+ ret = mtd_device_register(mtd, NULL, 0);
+ if (ret < 0)
+ goto err_cleanup_nand;
+
+ val = davinci_nand_readl(info, NRCSR_OFFSET);
+ dev_info(&pdev->dev, "controller rev. %d.%d\n",
+ (val >> 8) & 0xff, val & 0xff);
+
+ return 0;
+
+err_cleanup_nand:
+ nand_cleanup(&info->chip);
+
+err:
+ clk_disable_unprepare(info->clk);
+
+err_clk_enable:
+ spin_lock_irq(&davinci_nand_lock);
+ if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME)
+ ecc4_busy = false;
+ spin_unlock_irq(&davinci_nand_lock);
+ return ret;
+}
+
+static int nand_davinci_remove(struct platform_device *pdev)
+{
+ struct davinci_nand_info *info = platform_get_drvdata(pdev);
+
+ spin_lock_irq(&davinci_nand_lock);
+ if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME)
+ ecc4_busy = false;
+ spin_unlock_irq(&davinci_nand_lock);
+
+ nand_release(nand_to_mtd(&info->chip));
+
+ clk_disable_unprepare(info->clk);
+
+ return 0;
+}
+
+static struct platform_driver nand_davinci_driver = {
+ .probe = nand_davinci_probe,
+ .remove = nand_davinci_remove,
+ .driver = {
+ .name = "davinci_nand",
+ .of_match_table = of_match_ptr(davinci_nand_of_match),
+ },
+};
+MODULE_ALIAS("platform:davinci_nand");
+
+module_platform_driver(nand_davinci_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("Davinci NAND flash driver");
+
diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c
new file mode 100644
index 000000000000..2a302a1d1430
--- /dev/null
+++ b/drivers/mtd/nand/raw/denali.c
@@ -0,0 +1,1410 @@
+/*
+ * NAND Flash Controller Device Driver
+ * Copyright © 2009-2010, Intel Corporation and its suppliers.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions 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.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/completion.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "denali.h"
+
+MODULE_LICENSE("GPL");
+
+#define DENALI_NAND_NAME "denali-nand"
+
+/* for Indexed Addressing */
+#define DENALI_INDEXED_CTRL 0x00
+#define DENALI_INDEXED_DATA 0x10
+
+#define DENALI_MAP00 (0 << 26) /* direct access to buffer */
+#define DENALI_MAP01 (1 << 26) /* read/write pages in PIO */
+#define DENALI_MAP10 (2 << 26) /* high-level control plane */
+#define DENALI_MAP11 (3 << 26) /* direct controller access */
+
+/* MAP11 access cycle type */
+#define DENALI_MAP11_CMD ((DENALI_MAP11) | 0) /* command cycle */
+#define DENALI_MAP11_ADDR ((DENALI_MAP11) | 1) /* address cycle */
+#define DENALI_MAP11_DATA ((DENALI_MAP11) | 2) /* data cycle */
+
+/* MAP10 commands */
+#define DENALI_ERASE 0x01
+
+#define DENALI_BANK(denali) ((denali)->active_bank << 24)
+
+#define DENALI_INVALID_BANK -1
+#define DENALI_NR_BANKS 4
+
+/*
+ * The bus interface clock, clk_x, is phase aligned with the core clock. The
+ * clk_x is an integral multiple N of the core clk. The value N is configured
+ * at IP delivery time, and its available value is 4, 5, or 6. We need to align
+ * to the largest value to make it work with any possible configuration.
+ */
+#define DENALI_CLK_X_MULT 6
+
+static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd)
+{
+ return container_of(mtd_to_nand(mtd), struct denali_nand_info, nand);
+}
+
+/*
+ * Direct Addressing - the slave address forms the control information (command
+ * type, bank, block, and page address). The slave data is the actual data to
+ * be transferred. This mode requires 28 bits of address region allocated.
+ */
+static u32 denali_direct_read(struct denali_nand_info *denali, u32 addr)
+{
+ return ioread32(denali->host + addr);
+}
+
+static void denali_direct_write(struct denali_nand_info *denali, u32 addr,
+ u32 data)
+{
+ iowrite32(data, denali->host + addr);
+}
+
+/*
+ * Indexed Addressing - address translation module intervenes in passing the
+ * control information. This mode reduces the required address range. The
+ * control information and transferred data are latched by the registers in
+ * the translation module.
+ */
+static u32 denali_indexed_read(struct denali_nand_info *denali, u32 addr)
+{
+ iowrite32(addr, denali->host + DENALI_INDEXED_CTRL);
+ return ioread32(denali->host + DENALI_INDEXED_DATA);
+}
+
+static void denali_indexed_write(struct denali_nand_info *denali, u32 addr,
+ u32 data)
+{
+ iowrite32(addr, denali->host + DENALI_INDEXED_CTRL);
+ iowrite32(data, denali->host + DENALI_INDEXED_DATA);
+}
+
+/*
+ * Use the configuration feature register to determine the maximum number of
+ * banks that the hardware supports.
+ */
+static void denali_detect_max_banks(struct denali_nand_info *denali)
+{
+ uint32_t features = ioread32(denali->reg + FEATURES);
+
+ denali->max_banks = 1 << FIELD_GET(FEATURES__N_BANKS, features);
+
+ /* the encoding changed from rev 5.0 to 5.1 */
+ if (denali->revision < 0x0501)
+ denali->max_banks <<= 1;
+}
+
+static void denali_enable_irq(struct denali_nand_info *denali)
+{
+ int i;
+
+ for (i = 0; i < DENALI_NR_BANKS; i++)
+ iowrite32(U32_MAX, denali->reg + INTR_EN(i));
+ iowrite32(GLOBAL_INT_EN_FLAG, denali->reg + GLOBAL_INT_ENABLE);
+}
+
+static void denali_disable_irq(struct denali_nand_info *denali)
+{
+ int i;
+
+ for (i = 0; i < DENALI_NR_BANKS; i++)
+ iowrite32(0, denali->reg + INTR_EN(i));
+ iowrite32(0, denali->reg + GLOBAL_INT_ENABLE);
+}
+
+static void denali_clear_irq(struct denali_nand_info *denali,
+ int bank, uint32_t irq_status)
+{
+ /* write one to clear bits */
+ iowrite32(irq_status, denali->reg + INTR_STATUS(bank));
+}
+
+static void denali_clear_irq_all(struct denali_nand_info *denali)
+{
+ int i;
+
+ for (i = 0; i < DENALI_NR_BANKS; i++)
+ denali_clear_irq(denali, i, U32_MAX);
+}
+
+static irqreturn_t denali_isr(int irq, void *dev_id)
+{
+ struct denali_nand_info *denali = dev_id;
+ irqreturn_t ret = IRQ_NONE;
+ uint32_t irq_status;
+ int i;
+
+ spin_lock(&denali->irq_lock);
+
+ for (i = 0; i < DENALI_NR_BANKS; i++) {
+ irq_status = ioread32(denali->reg + INTR_STATUS(i));
+ if (irq_status)
+ ret = IRQ_HANDLED;
+
+ denali_clear_irq(denali, i, irq_status);
+
+ if (i != denali->active_bank)
+ continue;
+
+ denali->irq_status |= irq_status;
+
+ if (denali->irq_status & denali->irq_mask)
+ complete(&denali->complete);
+ }
+
+ spin_unlock(&denali->irq_lock);
+
+ return ret;
+}
+
+static void denali_reset_irq(struct denali_nand_info *denali)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&denali->irq_lock, flags);
+ denali->irq_status = 0;
+ denali->irq_mask = 0;
+ spin_unlock_irqrestore(&denali->irq_lock, flags);
+}
+
+static uint32_t denali_wait_for_irq(struct denali_nand_info *denali,
+ uint32_t irq_mask)
+{
+ unsigned long time_left, flags;
+ uint32_t irq_status;
+
+ spin_lock_irqsave(&denali->irq_lock, flags);
+
+ irq_status = denali->irq_status;
+
+ if (irq_mask & irq_status) {
+ /* return immediately if the IRQ has already happened. */
+ spin_unlock_irqrestore(&denali->irq_lock, flags);
+ return irq_status;
+ }
+
+ denali->irq_mask = irq_mask;
+ reinit_completion(&denali->complete);
+ spin_unlock_irqrestore(&denali->irq_lock, flags);
+
+ time_left = wait_for_completion_timeout(&denali->complete,
+ msecs_to_jiffies(1000));
+ if (!time_left) {
+ dev_err(denali->dev, "timeout while waiting for irq 0x%x\n",
+ irq_mask);
+ return 0;
+ }
+
+ return denali->irq_status;
+}
+
+static uint32_t denali_check_irq(struct denali_nand_info *denali)
+{
+ unsigned long flags;
+ uint32_t irq_status;
+
+ spin_lock_irqsave(&denali->irq_lock, flags);
+ irq_status = denali->irq_status;
+ spin_unlock_irqrestore(&denali->irq_lock, flags);
+
+ return irq_status;
+}
+
+static void denali_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali);
+ int i;
+
+ for (i = 0; i < len; i++)
+ buf[i] = denali->host_read(denali, addr);
+}
+
+static void denali_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali);
+ int i;
+
+ for (i = 0; i < len; i++)
+ denali->host_write(denali, addr, buf[i]);
+}
+
+static void denali_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali);
+ uint16_t *buf16 = (uint16_t *)buf;
+ int i;
+
+ for (i = 0; i < len / 2; i++)
+ buf16[i] = denali->host_read(denali, addr);
+}
+
+static void denali_write_buf16(struct mtd_info *mtd, const uint8_t *buf,
+ int len)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali);
+ const uint16_t *buf16 = (const uint16_t *)buf;
+ int i;
+
+ for (i = 0; i < len / 2; i++)
+ denali->host_write(denali, addr, buf16[i]);
+}
+
+static uint8_t denali_read_byte(struct mtd_info *mtd)
+{
+ uint8_t byte;
+
+ denali_read_buf(mtd, &byte, 1);
+
+ return byte;
+}
+
+static void denali_write_byte(struct mtd_info *mtd, uint8_t byte)
+{
+ denali_write_buf(mtd, &byte, 1);
+}
+
+static uint16_t denali_read_word(struct mtd_info *mtd)
+{
+ uint16_t word;
+
+ denali_read_buf16(mtd, (uint8_t *)&word, 2);
+
+ return word;
+}
+
+static void denali_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ uint32_t type;
+
+ if (ctrl & NAND_CLE)
+ type = DENALI_MAP11_CMD;
+ else if (ctrl & NAND_ALE)
+ type = DENALI_MAP11_ADDR;
+ else
+ return;
+
+ /*
+ * Some commands are followed by chip->dev_ready or chip->waitfunc.
+ * irq_status must be cleared here to catch the R/B# interrupt later.
+ */
+ if (ctrl & NAND_CTRL_CHANGE)
+ denali_reset_irq(denali);
+
+ denali->host_write(denali, DENALI_BANK(denali) | type, dat);
+}
+
+static int denali_dev_ready(struct mtd_info *mtd)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+
+ return !!(denali_check_irq(denali) & INTR__INT_ACT);
+}
+
+static int denali_check_erased_page(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf,
+ unsigned long uncor_ecc_flags,
+ unsigned int max_bitflips)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ uint8_t *ecc_code = chip->oob_poi + denali->oob_skip_bytes;
+ int ecc_steps = chip->ecc.steps;
+ int ecc_size = chip->ecc.size;
+ int ecc_bytes = chip->ecc.bytes;
+ int i, stat;
+
+ for (i = 0; i < ecc_steps; i++) {
+ if (!(uncor_ecc_flags & BIT(i)))
+ continue;
+
+ stat = nand_check_erased_ecc_chunk(buf, ecc_size,
+ ecc_code, ecc_bytes,
+ NULL, 0,
+ chip->ecc.strength);
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ }
+
+ buf += ecc_size;
+ ecc_code += ecc_bytes;
+ }
+
+ return max_bitflips;
+}
+
+static int denali_hw_ecc_fixup(struct mtd_info *mtd,
+ struct denali_nand_info *denali,
+ unsigned long *uncor_ecc_flags)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int bank = denali->active_bank;
+ uint32_t ecc_cor;
+ unsigned int max_bitflips;
+
+ ecc_cor = ioread32(denali->reg + ECC_COR_INFO(bank));
+ ecc_cor >>= ECC_COR_INFO__SHIFT(bank);
+
+ if (ecc_cor & ECC_COR_INFO__UNCOR_ERR) {
+ /*
+ * This flag is set when uncorrectable error occurs at least in
+ * one ECC sector. We can not know "how many sectors", or
+ * "which sector(s)". We need erase-page check for all sectors.
+ */
+ *uncor_ecc_flags = GENMASK(chip->ecc.steps - 1, 0);
+ return 0;
+ }
+
+ max_bitflips = FIELD_GET(ECC_COR_INFO__MAX_ERRORS, ecc_cor);
+
+ /*
+ * The register holds the maximum of per-sector corrected bitflips.
+ * This is suitable for the return value of the ->read_page() callback.
+ * Unfortunately, we can not know the total number of corrected bits in
+ * the page. Increase the stats by max_bitflips. (compromised solution)
+ */
+ mtd->ecc_stats.corrected += max_bitflips;
+
+ return max_bitflips;
+}
+
+static int denali_sw_ecc_fixup(struct mtd_info *mtd,
+ struct denali_nand_info *denali,
+ unsigned long *uncor_ecc_flags, uint8_t *buf)
+{
+ unsigned int ecc_size = denali->nand.ecc.size;
+ unsigned int bitflips = 0;
+ unsigned int max_bitflips = 0;
+ uint32_t err_addr, err_cor_info;
+ unsigned int err_byte, err_sector, err_device;
+ uint8_t err_cor_value;
+ unsigned int prev_sector = 0;
+ uint32_t irq_status;
+
+ denali_reset_irq(denali);
+
+ do {
+ err_addr = ioread32(denali->reg + ECC_ERROR_ADDRESS);
+ err_sector = FIELD_GET(ECC_ERROR_ADDRESS__SECTOR, err_addr);
+ err_byte = FIELD_GET(ECC_ERROR_ADDRESS__OFFSET, err_addr);
+
+ err_cor_info = ioread32(denali->reg + ERR_CORRECTION_INFO);
+ err_cor_value = FIELD_GET(ERR_CORRECTION_INFO__BYTE,
+ err_cor_info);
+ err_device = FIELD_GET(ERR_CORRECTION_INFO__DEVICE,
+ err_cor_info);
+
+ /* reset the bitflip counter when crossing ECC sector */
+ if (err_sector != prev_sector)
+ bitflips = 0;
+
+ if (err_cor_info & ERR_CORRECTION_INFO__UNCOR) {
+ /*
+ * Check later if this is a real ECC error, or
+ * an erased sector.
+ */
+ *uncor_ecc_flags |= BIT(err_sector);
+ } else if (err_byte < ecc_size) {
+ /*
+ * If err_byte is larger than ecc_size, means error
+ * happened in OOB, so we ignore it. It's no need for
+ * us to correct it err_device is represented the NAND
+ * error bits are happened in if there are more than
+ * one NAND connected.
+ */
+ int offset;
+ unsigned int flips_in_byte;
+
+ offset = (err_sector * ecc_size + err_byte) *
+ denali->devs_per_cs + err_device;
+
+ /* correct the ECC error */
+ flips_in_byte = hweight8(buf[offset] ^ err_cor_value);
+ buf[offset] ^= err_cor_value;
+ mtd->ecc_stats.corrected += flips_in_byte;
+ bitflips += flips_in_byte;
+
+ max_bitflips = max(max_bitflips, bitflips);
+ }
+
+ prev_sector = err_sector;
+ } while (!(err_cor_info & ERR_CORRECTION_INFO__LAST_ERR));
+
+ /*
+ * Once handle all ECC errors, controller will trigger an
+ * ECC_TRANSACTION_DONE interrupt.
+ */
+ irq_status = denali_wait_for_irq(denali, INTR__ECC_TRANSACTION_DONE);
+ if (!(irq_status & INTR__ECC_TRANSACTION_DONE))
+ return -EIO;
+
+ return max_bitflips;
+}
+
+static void denali_setup_dma64(struct denali_nand_info *denali,
+ dma_addr_t dma_addr, int page, int write)
+{
+ uint32_t mode;
+ const int page_count = 1;
+
+ mode = DENALI_MAP10 | DENALI_BANK(denali) | page;
+
+ /* DMA is a three step process */
+
+ /*
+ * 1. setup transfer type, interrupt when complete,
+ * burst len = 64 bytes, the number of pages
+ */
+ denali->host_write(denali, mode,
+ 0x01002000 | (64 << 16) | (write << 8) | page_count);
+
+ /* 2. set memory low address */
+ denali->host_write(denali, mode, lower_32_bits(dma_addr));
+
+ /* 3. set memory high address */
+ denali->host_write(denali, mode, upper_32_bits(dma_addr));
+}
+
+static void denali_setup_dma32(struct denali_nand_info *denali,
+ dma_addr_t dma_addr, int page, int write)
+{
+ uint32_t mode;
+ const int page_count = 1;
+
+ mode = DENALI_MAP10 | DENALI_BANK(denali);
+
+ /* DMA is a four step process */
+
+ /* 1. setup transfer type and # of pages */
+ denali->host_write(denali, mode | page,
+ 0x2000 | (write << 8) | page_count);
+
+ /* 2. set memory high address bits 23:8 */
+ denali->host_write(denali, mode | ((dma_addr >> 16) << 8), 0x2200);
+
+ /* 3. set memory low address bits 23:8 */
+ denali->host_write(denali, mode | ((dma_addr & 0xffff) << 8), 0x2300);
+
+ /* 4. interrupt when complete, burst len = 64 bytes */
+ denali->host_write(denali, mode | 0x14000, 0x2400);
+}
+
+static int denali_pio_read(struct denali_nand_info *denali, void *buf,
+ size_t size, int page, int raw)
+{
+ u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page;
+ uint32_t *buf32 = (uint32_t *)buf;
+ uint32_t irq_status, ecc_err_mask;
+ int i;
+
+ if (denali->caps & DENALI_CAP_HW_ECC_FIXUP)
+ ecc_err_mask = INTR__ECC_UNCOR_ERR;
+ else
+ ecc_err_mask = INTR__ECC_ERR;
+
+ denali_reset_irq(denali);
+
+ for (i = 0; i < size / 4; i++)
+ *buf32++ = denali->host_read(denali, addr);
+
+ irq_status = denali_wait_for_irq(denali, INTR__PAGE_XFER_INC);
+ if (!(irq_status & INTR__PAGE_XFER_INC))
+ return -EIO;
+
+ if (irq_status & INTR__ERASED_PAGE)
+ memset(buf, 0xff, size);
+
+ return irq_status & ecc_err_mask ? -EBADMSG : 0;
+}
+
+static int denali_pio_write(struct denali_nand_info *denali,
+ const void *buf, size_t size, int page, int raw)
+{
+ u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page;
+ const uint32_t *buf32 = (uint32_t *)buf;
+ uint32_t irq_status;
+ int i;
+
+ denali_reset_irq(denali);
+
+ for (i = 0; i < size / 4; i++)
+ denali->host_write(denali, addr, *buf32++);
+
+ irq_status = denali_wait_for_irq(denali,
+ INTR__PROGRAM_COMP | INTR__PROGRAM_FAIL);
+ if (!(irq_status & INTR__PROGRAM_COMP))
+ return -EIO;
+
+ return 0;
+}
+
+static int denali_pio_xfer(struct denali_nand_info *denali, void *buf,
+ size_t size, int page, int raw, int write)
+{
+ if (write)
+ return denali_pio_write(denali, buf, size, page, raw);
+ else
+ return denali_pio_read(denali, buf, size, page, raw);
+}
+
+static int denali_dma_xfer(struct denali_nand_info *denali, void *buf,
+ size_t size, int page, int raw, int write)
+{
+ dma_addr_t dma_addr;
+ uint32_t irq_mask, irq_status, ecc_err_mask;
+ enum dma_data_direction dir = write ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+ int ret = 0;
+
+ dma_addr = dma_map_single(denali->dev, buf, size, dir);
+ if (dma_mapping_error(denali->dev, dma_addr)) {
+ dev_dbg(denali->dev, "Failed to DMA-map buffer. Trying PIO.\n");
+ return denali_pio_xfer(denali, buf, size, page, raw, write);
+ }
+
+ if (write) {
+ /*
+ * INTR__PROGRAM_COMP is never asserted for the DMA transfer.
+ * We can use INTR__DMA_CMD_COMP instead. This flag is asserted
+ * when the page program is completed.
+ */
+ irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL;
+ ecc_err_mask = 0;
+ } else if (denali->caps & DENALI_CAP_HW_ECC_FIXUP) {
+ irq_mask = INTR__DMA_CMD_COMP;
+ ecc_err_mask = INTR__ECC_UNCOR_ERR;
+ } else {
+ irq_mask = INTR__DMA_CMD_COMP;
+ ecc_err_mask = INTR__ECC_ERR;
+ }
+
+ iowrite32(DMA_ENABLE__FLAG, denali->reg + DMA_ENABLE);
+
+ denali_reset_irq(denali);
+ denali->setup_dma(denali, dma_addr, page, write);
+
+ irq_status = denali_wait_for_irq(denali, irq_mask);
+ if (!(irq_status & INTR__DMA_CMD_COMP))
+ ret = -EIO;
+ else if (irq_status & ecc_err_mask)
+ ret = -EBADMSG;
+
+ iowrite32(0, denali->reg + DMA_ENABLE);
+
+ dma_unmap_single(denali->dev, dma_addr, size, dir);
+
+ if (irq_status & INTR__ERASED_PAGE)
+ memset(buf, 0xff, size);
+
+ return ret;
+}
+
+static int denali_data_xfer(struct denali_nand_info *denali, void *buf,
+ size_t size, int page, int raw, int write)
+{
+ iowrite32(raw ? 0 : ECC_ENABLE__FLAG, denali->reg + ECC_ENABLE);
+ iowrite32(raw ? TRANSFER_SPARE_REG__FLAG : 0,
+ denali->reg + TRANSFER_SPARE_REG);
+
+ if (denali->dma_avail)
+ return denali_dma_xfer(denali, buf, size, page, raw, write);
+ else
+ return denali_pio_xfer(denali, buf, size, page, raw, write);
+}
+
+static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip,
+ int page, int write)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ int writesize = mtd->writesize;
+ int oobsize = mtd->oobsize;
+ uint8_t *bufpoi = chip->oob_poi;
+ int ecc_steps = chip->ecc.steps;
+ int ecc_size = chip->ecc.size;
+ int ecc_bytes = chip->ecc.bytes;
+ int oob_skip = denali->oob_skip_bytes;
+ size_t size = writesize + oobsize;
+ int i, pos, len;
+
+ /* BBM at the beginning of the OOB area */
+ if (write)
+ nand_prog_page_begin_op(chip, page, writesize, bufpoi,
+ oob_skip);
+ else
+ nand_read_page_op(chip, page, writesize, bufpoi, oob_skip);
+ bufpoi += oob_skip;
+
+ /* OOB ECC */
+ for (i = 0; i < ecc_steps; i++) {
+ pos = ecc_size + i * (ecc_size + ecc_bytes);
+ len = ecc_bytes;
+
+ if (pos >= writesize)
+ pos += oob_skip;
+ else if (pos + len > writesize)
+ len = writesize - pos;
+
+ if (write)
+ nand_change_write_column_op(chip, pos, bufpoi, len,
+ false);
+ else
+ nand_change_read_column_op(chip, pos, bufpoi, len,
+ false);
+ bufpoi += len;
+ if (len < ecc_bytes) {
+ len = ecc_bytes - len;
+ if (write)
+ nand_change_write_column_op(chip, writesize +
+ oob_skip, bufpoi,
+ len, false);
+ else
+ nand_change_read_column_op(chip, writesize +
+ oob_skip, bufpoi,
+ len, false);
+ bufpoi += len;
+ }
+ }
+
+ /* OOB free */
+ len = oobsize - (bufpoi - chip->oob_poi);
+ if (write)
+ nand_change_write_column_op(chip, size - len, bufpoi, len,
+ false);
+ else
+ nand_change_read_column_op(chip, size - len, bufpoi, len,
+ false);
+}
+
+static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ int writesize = mtd->writesize;
+ int oobsize = mtd->oobsize;
+ int ecc_steps = chip->ecc.steps;
+ int ecc_size = chip->ecc.size;
+ int ecc_bytes = chip->ecc.bytes;
+ void *tmp_buf = denali->buf;
+ int oob_skip = denali->oob_skip_bytes;
+ size_t size = writesize + oobsize;
+ int ret, i, pos, len;
+
+ ret = denali_data_xfer(denali, tmp_buf, size, page, 1, 0);
+ if (ret)
+ return ret;
+
+ /* Arrange the buffer for syndrome payload/ecc layout */
+ if (buf) {
+ for (i = 0; i < ecc_steps; i++) {
+ pos = i * (ecc_size + ecc_bytes);
+ len = ecc_size;
+
+ if (pos >= writesize)
+ pos += oob_skip;
+ else if (pos + len > writesize)
+ len = writesize - pos;
+
+ memcpy(buf, tmp_buf + pos, len);
+ buf += len;
+ if (len < ecc_size) {
+ len = ecc_size - len;
+ memcpy(buf, tmp_buf + writesize + oob_skip,
+ len);
+ buf += len;
+ }
+ }
+ }
+
+ if (oob_required) {
+ uint8_t *oob = chip->oob_poi;
+
+ /* BBM at the beginning of the OOB area */
+ memcpy(oob, tmp_buf + writesize, oob_skip);
+ oob += oob_skip;
+
+ /* OOB ECC */
+ for (i = 0; i < ecc_steps; i++) {
+ pos = ecc_size + i * (ecc_size + ecc_bytes);
+ len = ecc_bytes;
+
+ if (pos >= writesize)
+ pos += oob_skip;
+ else if (pos + len > writesize)
+ len = writesize - pos;
+
+ memcpy(oob, tmp_buf + pos, len);
+ oob += len;
+ if (len < ecc_bytes) {
+ len = ecc_bytes - len;
+ memcpy(oob, tmp_buf + writesize + oob_skip,
+ len);
+ oob += len;
+ }
+ }
+
+ /* OOB free */
+ len = oobsize - (oob - chip->oob_poi);
+ memcpy(oob, tmp_buf + size - len, len);
+ }
+
+ return 0;
+}
+
+static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ denali_oob_xfer(mtd, chip, page, 0);
+
+ return 0;
+}
+
+static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+
+ denali_reset_irq(denali);
+
+ denali_oob_xfer(mtd, chip, page, 1);
+
+ return nand_prog_page_end_op(chip);
+}
+
+static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ unsigned long uncor_ecc_flags = 0;
+ int stat = 0;
+ int ret;
+
+ ret = denali_data_xfer(denali, buf, mtd->writesize, page, 0, 0);
+ if (ret && ret != -EBADMSG)
+ return ret;
+
+ if (denali->caps & DENALI_CAP_HW_ECC_FIXUP)
+ stat = denali_hw_ecc_fixup(mtd, denali, &uncor_ecc_flags);
+ else if (ret == -EBADMSG)
+ stat = denali_sw_ecc_fixup(mtd, denali, &uncor_ecc_flags, buf);
+
+ if (stat < 0)
+ return stat;
+
+ if (uncor_ecc_flags) {
+ ret = denali_read_oob(mtd, chip, page);
+ if (ret)
+ return ret;
+
+ stat = denali_check_erased_page(mtd, chip, buf,
+ uncor_ecc_flags, stat);
+ }
+
+ return stat;
+}
+
+static int denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required, int page)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ int writesize = mtd->writesize;
+ int oobsize = mtd->oobsize;
+ int ecc_steps = chip->ecc.steps;
+ int ecc_size = chip->ecc.size;
+ int ecc_bytes = chip->ecc.bytes;
+ void *tmp_buf = denali->buf;
+ int oob_skip = denali->oob_skip_bytes;
+ size_t size = writesize + oobsize;
+ int i, pos, len;
+
+ /*
+ * Fill the buffer with 0xff first except the full page transfer.
+ * This simplifies the logic.
+ */
+ if (!buf || !oob_required)
+ memset(tmp_buf, 0xff, size);
+
+ /* Arrange the buffer for syndrome payload/ecc layout */
+ if (buf) {
+ for (i = 0; i < ecc_steps; i++) {
+ pos = i * (ecc_size + ecc_bytes);
+ len = ecc_size;
+
+ if (pos >= writesize)
+ pos += oob_skip;
+ else if (pos + len > writesize)
+ len = writesize - pos;
+
+ memcpy(tmp_buf + pos, buf, len);
+ buf += len;
+ if (len < ecc_size) {
+ len = ecc_size - len;
+ memcpy(tmp_buf + writesize + oob_skip, buf,
+ len);
+ buf += len;
+ }
+ }
+ }
+
+ if (oob_required) {
+ const uint8_t *oob = chip->oob_poi;
+
+ /* BBM at the beginning of the OOB area */
+ memcpy(tmp_buf + writesize, oob, oob_skip);
+ oob += oob_skip;
+
+ /* OOB ECC */
+ for (i = 0; i < ecc_steps; i++) {
+ pos = ecc_size + i * (ecc_size + ecc_bytes);
+ len = ecc_bytes;
+
+ if (pos >= writesize)
+ pos += oob_skip;
+ else if (pos + len > writesize)
+ len = writesize - pos;
+
+ memcpy(tmp_buf + pos, oob, len);
+ oob += len;
+ if (len < ecc_bytes) {
+ len = ecc_bytes - len;
+ memcpy(tmp_buf + writesize + oob_skip, oob,
+ len);
+ oob += len;
+ }
+ }
+
+ /* OOB free */
+ len = oobsize - (oob - chip->oob_poi);
+ memcpy(tmp_buf + size - len, oob, len);
+ }
+
+ return denali_data_xfer(denali, tmp_buf, size, page, 1, 1);
+}
+
+static int denali_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required, int page)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+
+ return denali_data_xfer(denali, (void *)buf, mtd->writesize,
+ page, 0, 1);
+}
+
+static void denali_select_chip(struct mtd_info *mtd, int chip)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+
+ denali->active_bank = chip;
+}
+
+static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ uint32_t irq_status;
+
+ /* R/B# pin transitioned from low to high? */
+ irq_status = denali_wait_for_irq(denali, INTR__INT_ACT);
+
+ return irq_status & INTR__INT_ACT ? 0 : NAND_STATUS_FAIL;
+}
+
+static int denali_erase(struct mtd_info *mtd, int page)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ uint32_t irq_status;
+
+ denali_reset_irq(denali);
+
+ denali->host_write(denali, DENALI_MAP10 | DENALI_BANK(denali) | page,
+ DENALI_ERASE);
+
+ /* wait for erase to complete or failure to occur */
+ irq_status = denali_wait_for_irq(denali,
+ INTR__ERASE_COMP | INTR__ERASE_FAIL);
+
+ return irq_status & INTR__ERASE_COMP ? 0 : -EIO;
+}
+
+static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,
+ const struct nand_data_interface *conf)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ const struct nand_sdr_timings *timings;
+ unsigned long t_clk;
+ int acc_clks, re_2_we, re_2_re, we_2_re, addr_2_data;
+ int rdwr_en_lo, rdwr_en_hi, rdwr_en_lo_hi, cs_setup;
+ int addr_2_data_mask;
+ uint32_t tmp;
+
+ timings = nand_get_sdr_timings(conf);
+ if (IS_ERR(timings))
+ return PTR_ERR(timings);
+
+ /* clk_x period in picoseconds */
+ t_clk = DIV_ROUND_DOWN_ULL(1000000000000ULL, denali->clk_x_rate);
+ if (!t_clk)
+ return -EINVAL;
+
+ if (chipnr == NAND_DATA_IFACE_CHECK_ONLY)
+ return 0;
+
+ /* tREA -> ACC_CLKS */
+ acc_clks = DIV_ROUND_UP(timings->tREA_max, t_clk);
+ acc_clks = min_t(int, acc_clks, ACC_CLKS__VALUE);
+
+ tmp = ioread32(denali->reg + ACC_CLKS);
+ tmp &= ~ACC_CLKS__VALUE;
+ tmp |= FIELD_PREP(ACC_CLKS__VALUE, acc_clks);
+ iowrite32(tmp, denali->reg + ACC_CLKS);
+
+ /* tRWH -> RE_2_WE */
+ re_2_we = DIV_ROUND_UP(timings->tRHW_min, t_clk);
+ re_2_we = min_t(int, re_2_we, RE_2_WE__VALUE);
+
+ tmp = ioread32(denali->reg + RE_2_WE);
+ tmp &= ~RE_2_WE__VALUE;
+ tmp |= FIELD_PREP(RE_2_WE__VALUE, re_2_we);
+ iowrite32(tmp, denali->reg + RE_2_WE);
+
+ /* tRHZ -> RE_2_RE */
+ re_2_re = DIV_ROUND_UP(timings->tRHZ_max, t_clk);
+ re_2_re = min_t(int, re_2_re, RE_2_RE__VALUE);
+
+ tmp = ioread32(denali->reg + RE_2_RE);
+ tmp &= ~RE_2_RE__VALUE;
+ tmp |= FIELD_PREP(RE_2_RE__VALUE, re_2_re);
+ iowrite32(tmp, denali->reg + RE_2_RE);
+
+ /*
+ * tCCS, tWHR -> WE_2_RE
+ *
+ * With WE_2_RE properly set, the Denali controller automatically takes
+ * care of the delay; the driver need not set NAND_WAIT_TCCS.
+ */
+ we_2_re = DIV_ROUND_UP(max(timings->tCCS_min, timings->tWHR_min),
+ t_clk);
+ we_2_re = min_t(int, we_2_re, TWHR2_AND_WE_2_RE__WE_2_RE);
+
+ tmp = ioread32(denali->reg + TWHR2_AND_WE_2_RE);
+ tmp &= ~TWHR2_AND_WE_2_RE__WE_2_RE;
+ tmp |= FIELD_PREP(TWHR2_AND_WE_2_RE__WE_2_RE, we_2_re);
+ iowrite32(tmp, denali->reg + TWHR2_AND_WE_2_RE);
+
+ /* tADL -> ADDR_2_DATA */
+
+ /* for older versions, ADDR_2_DATA is only 6 bit wide */
+ addr_2_data_mask = TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA;
+ if (denali->revision < 0x0501)
+ addr_2_data_mask >>= 1;
+
+ addr_2_data = DIV_ROUND_UP(timings->tADL_min, t_clk);
+ addr_2_data = min_t(int, addr_2_data, addr_2_data_mask);
+
+ tmp = ioread32(denali->reg + TCWAW_AND_ADDR_2_DATA);
+ tmp &= ~TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA;
+ tmp |= FIELD_PREP(TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA, addr_2_data);
+ iowrite32(tmp, denali->reg + TCWAW_AND_ADDR_2_DATA);
+
+ /* tREH, tWH -> RDWR_EN_HI_CNT */
+ rdwr_en_hi = DIV_ROUND_UP(max(timings->tREH_min, timings->tWH_min),
+ t_clk);
+ rdwr_en_hi = min_t(int, rdwr_en_hi, RDWR_EN_HI_CNT__VALUE);
+
+ tmp = ioread32(denali->reg + RDWR_EN_HI_CNT);
+ tmp &= ~RDWR_EN_HI_CNT__VALUE;
+ tmp |= FIELD_PREP(RDWR_EN_HI_CNT__VALUE, rdwr_en_hi);
+ iowrite32(tmp, denali->reg + RDWR_EN_HI_CNT);
+
+ /* tRP, tWP -> RDWR_EN_LO_CNT */
+ rdwr_en_lo = DIV_ROUND_UP(max(timings->tRP_min, timings->tWP_min),
+ t_clk);
+ rdwr_en_lo_hi = DIV_ROUND_UP(max(timings->tRC_min, timings->tWC_min),
+ t_clk);
+ rdwr_en_lo_hi = max(rdwr_en_lo_hi, DENALI_CLK_X_MULT);
+ rdwr_en_lo = max(rdwr_en_lo, rdwr_en_lo_hi - rdwr_en_hi);
+ rdwr_en_lo = min_t(int, rdwr_en_lo, RDWR_EN_LO_CNT__VALUE);
+
+ tmp = ioread32(denali->reg + RDWR_EN_LO_CNT);
+ tmp &= ~RDWR_EN_LO_CNT__VALUE;
+ tmp |= FIELD_PREP(RDWR_EN_LO_CNT__VALUE, rdwr_en_lo);
+ iowrite32(tmp, denali->reg + RDWR_EN_LO_CNT);
+
+ /* tCS, tCEA -> CS_SETUP_CNT */
+ cs_setup = max3((int)DIV_ROUND_UP(timings->tCS_min, t_clk) - rdwr_en_lo,
+ (int)DIV_ROUND_UP(timings->tCEA_max, t_clk) - acc_clks,
+ 0);
+ cs_setup = min_t(int, cs_setup, CS_SETUP_CNT__VALUE);
+
+ tmp = ioread32(denali->reg + CS_SETUP_CNT);
+ tmp &= ~CS_SETUP_CNT__VALUE;
+ tmp |= FIELD_PREP(CS_SETUP_CNT__VALUE, cs_setup);
+ iowrite32(tmp, denali->reg + CS_SETUP_CNT);
+
+ return 0;
+}
+
+static void denali_reset_banks(struct denali_nand_info *denali)
+{
+ u32 irq_status;
+ int i;
+
+ for (i = 0; i < denali->max_banks; i++) {
+ denali->active_bank = i;
+
+ denali_reset_irq(denali);
+
+ iowrite32(DEVICE_RESET__BANK(i),
+ denali->reg + DEVICE_RESET);
+
+ irq_status = denali_wait_for_irq(denali,
+ INTR__RST_COMP | INTR__INT_ACT | INTR__TIME_OUT);
+ if (!(irq_status & INTR__INT_ACT))
+ break;
+ }
+
+ dev_dbg(denali->dev, "%d chips connected\n", i);
+ denali->max_banks = i;
+}
+
+static void denali_hw_init(struct denali_nand_info *denali)
+{
+ /*
+ * The REVISION register may not be reliable. Platforms are allowed to
+ * override it.
+ */
+ if (!denali->revision)
+ denali->revision = swab16(ioread32(denali->reg + REVISION));
+
+ /*
+ * tell driver how many bit controller will skip before
+ * writing ECC code in OOB, this register may be already
+ * set by firmware. So we read this value out.
+ * if this value is 0, just let it be.
+ */
+ denali->oob_skip_bytes = ioread32(denali->reg + SPARE_AREA_SKIP_BYTES);
+ denali_detect_max_banks(denali);
+ iowrite32(0x0F, denali->reg + RB_PIN_ENABLED);
+ iowrite32(CHIP_EN_DONT_CARE__FLAG, denali->reg + CHIP_ENABLE_DONT_CARE);
+
+ iowrite32(0xffff, denali->reg + SPARE_AREA_MARKER);
+}
+
+int denali_calc_ecc_bytes(int step_size, int strength)
+{
+ /* BCH code. Denali requires ecc.bytes to be multiple of 2 */
+ return DIV_ROUND_UP(strength * fls(step_size * 8), 16) * 2;
+}
+EXPORT_SYMBOL(denali_calc_ecc_bytes);
+
+static int denali_ecc_setup(struct mtd_info *mtd, struct nand_chip *chip,
+ struct denali_nand_info *denali)
+{
+ int oobavail = mtd->oobsize - denali->oob_skip_bytes;
+ int ret;
+
+ /*
+ * If .size and .strength are already set (usually by DT),
+ * check if they are supported by this controller.
+ */
+ if (chip->ecc.size && chip->ecc.strength)
+ return nand_check_ecc_caps(chip, denali->ecc_caps, oobavail);
+
+ /*
+ * We want .size and .strength closest to the chip's requirement
+ * unless NAND_ECC_MAXIMIZE is requested.
+ */
+ if (!(chip->ecc.options & NAND_ECC_MAXIMIZE)) {
+ ret = nand_match_ecc_req(chip, denali->ecc_caps, oobavail);
+ if (!ret)
+ return 0;
+ }
+
+ /* Max ECC strength is the last thing we can do */
+ return nand_maximize_ecc(chip, denali->ecc_caps, oobavail);
+}
+
+static int denali_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (section)
+ return -ERANGE;
+
+ oobregion->offset = denali->oob_skip_bytes;
+ oobregion->length = chip->ecc.total;
+
+ return 0;
+}
+
+static int denali_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (section)
+ return -ERANGE;
+
+ oobregion->offset = chip->ecc.total + denali->oob_skip_bytes;
+ oobregion->length = mtd->oobsize - oobregion->offset;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops denali_ooblayout_ops = {
+ .ecc = denali_ooblayout_ecc,
+ .free = denali_ooblayout_free,
+};
+
+static int denali_multidev_fixup(struct denali_nand_info *denali)
+{
+ struct nand_chip *chip = &denali->nand;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ /*
+ * Support for multi device:
+ * When the IP configuration is x16 capable and two x8 chips are
+ * connected in parallel, DEVICES_CONNECTED should be set to 2.
+ * In this case, the core framework knows nothing about this fact,
+ * so we should tell it the _logical_ pagesize and anything necessary.
+ */
+ denali->devs_per_cs = ioread32(denali->reg + DEVICES_CONNECTED);
+
+ /*
+ * On some SoCs, DEVICES_CONNECTED is not auto-detected.
+ * For those, DEVICES_CONNECTED is left to 0. Set 1 if it is the case.
+ */
+ if (denali->devs_per_cs == 0) {
+ denali->devs_per_cs = 1;
+ iowrite32(1, denali->reg + DEVICES_CONNECTED);
+ }
+
+ if (denali->devs_per_cs == 1)
+ return 0;
+
+ if (denali->devs_per_cs != 2) {
+ dev_err(denali->dev, "unsupported number of devices %d\n",
+ denali->devs_per_cs);
+ return -EINVAL;
+ }
+
+ /* 2 chips in parallel */
+ mtd->size <<= 1;
+ mtd->erasesize <<= 1;
+ mtd->writesize <<= 1;
+ mtd->oobsize <<= 1;
+ chip->chipsize <<= 1;
+ chip->page_shift += 1;
+ chip->phys_erase_shift += 1;
+ chip->bbt_erase_shift += 1;
+ chip->chip_shift += 1;
+ chip->pagemask <<= 1;
+ chip->ecc.size <<= 1;
+ chip->ecc.bytes <<= 1;
+ chip->ecc.strength <<= 1;
+ denali->oob_skip_bytes <<= 1;
+
+ return 0;
+}
+
+int denali_init(struct denali_nand_info *denali)
+{
+ struct nand_chip *chip = &denali->nand;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ u32 features = ioread32(denali->reg + FEATURES);
+ int ret;
+
+ mtd->dev.parent = denali->dev;
+ denali_hw_init(denali);
+
+ init_completion(&denali->complete);
+ spin_lock_init(&denali->irq_lock);
+
+ denali_clear_irq_all(denali);
+
+ ret = devm_request_irq(denali->dev, denali->irq, denali_isr,
+ IRQF_SHARED, DENALI_NAND_NAME, denali);
+ if (ret) {
+ dev_err(denali->dev, "Unable to request IRQ\n");
+ return ret;
+ }
+
+ denali_enable_irq(denali);
+ denali_reset_banks(denali);
+
+ denali->active_bank = DENALI_INVALID_BANK;
+
+ nand_set_flash_node(chip, denali->dev->of_node);
+ /* Fallback to the default name if DT did not give "label" property */
+ if (!mtd->name)
+ mtd->name = "denali-nand";
+
+ chip->select_chip = denali_select_chip;
+ chip->read_byte = denali_read_byte;
+ chip->write_byte = denali_write_byte;
+ chip->read_word = denali_read_word;
+ chip->cmd_ctrl = denali_cmd_ctrl;
+ chip->dev_ready = denali_dev_ready;
+ chip->waitfunc = denali_waitfunc;
+
+ if (features & FEATURES__INDEX_ADDR) {
+ denali->host_read = denali_indexed_read;
+ denali->host_write = denali_indexed_write;
+ } else {
+ denali->host_read = denali_direct_read;
+ denali->host_write = denali_direct_write;
+ }
+
+ /* clk rate info is needed for setup_data_interface */
+ if (denali->clk_x_rate)
+ chip->setup_data_interface = denali_setup_data_interface;
+
+ ret = nand_scan_ident(mtd, denali->max_banks, NULL);
+ if (ret)
+ goto disable_irq;
+
+ if (ioread32(denali->reg + FEATURES) & FEATURES__DMA)
+ denali->dma_avail = 1;
+
+ if (denali->dma_avail) {
+ int dma_bit = denali->caps & DENALI_CAP_DMA_64BIT ? 64 : 32;
+
+ ret = dma_set_mask(denali->dev, DMA_BIT_MASK(dma_bit));
+ if (ret) {
+ dev_info(denali->dev,
+ "Failed to set DMA mask. Disabling DMA.\n");
+ denali->dma_avail = 0;
+ }
+ }
+
+ if (denali->dma_avail) {
+ chip->options |= NAND_USE_BOUNCE_BUFFER;
+ chip->buf_align = 16;
+ if (denali->caps & DENALI_CAP_DMA_64BIT)
+ denali->setup_dma = denali_setup_dma64;
+ else
+ denali->setup_dma = denali_setup_dma32;
+ }
+
+ chip->bbt_options |= NAND_BBT_USE_FLASH;
+ chip->bbt_options |= NAND_BBT_NO_OOB;
+ chip->ecc.mode = NAND_ECC_HW_SYNDROME;
+ chip->options |= NAND_NO_SUBPAGE_WRITE;
+
+ ret = denali_ecc_setup(mtd, chip, denali);
+ if (ret) {
+ dev_err(denali->dev, "Failed to setup ECC settings.\n");
+ goto disable_irq;
+ }
+
+ dev_dbg(denali->dev,
+ "chosen ECC settings: step=%d, strength=%d, bytes=%d\n",
+ chip->ecc.size, chip->ecc.strength, chip->ecc.bytes);
+
+ iowrite32(FIELD_PREP(ECC_CORRECTION__ERASE_THRESHOLD, 1) |
+ FIELD_PREP(ECC_CORRECTION__VALUE, chip->ecc.strength),
+ denali->reg + ECC_CORRECTION);
+ iowrite32(mtd->erasesize / mtd->writesize,
+ denali->reg + PAGES_PER_BLOCK);
+ iowrite32(chip->options & NAND_BUSWIDTH_16 ? 1 : 0,
+ denali->reg + DEVICE_WIDTH);
+ iowrite32(chip->options & NAND_ROW_ADDR_3 ? 0 : TWO_ROW_ADDR_CYCLES__FLAG,
+ denali->reg + TWO_ROW_ADDR_CYCLES);
+ iowrite32(mtd->writesize, denali->reg + DEVICE_MAIN_AREA_SIZE);
+ iowrite32(mtd->oobsize, denali->reg + DEVICE_SPARE_AREA_SIZE);
+
+ iowrite32(chip->ecc.size, denali->reg + CFG_DATA_BLOCK_SIZE);
+ iowrite32(chip->ecc.size, denali->reg + CFG_LAST_DATA_BLOCK_SIZE);
+ /* chip->ecc.steps is set by nand_scan_tail(); not available here */
+ iowrite32(mtd->writesize / chip->ecc.size,
+ denali->reg + CFG_NUM_DATA_BLOCKS);
+
+ mtd_set_ooblayout(mtd, &denali_ooblayout_ops);
+
+ if (chip->options & NAND_BUSWIDTH_16) {
+ chip->read_buf = denali_read_buf16;
+ chip->write_buf = denali_write_buf16;
+ } else {
+ chip->read_buf = denali_read_buf;
+ chip->write_buf = denali_write_buf;
+ }
+ chip->ecc.read_page = denali_read_page;
+ chip->ecc.read_page_raw = denali_read_page_raw;
+ chip->ecc.write_page = denali_write_page;
+ chip->ecc.write_page_raw = denali_write_page_raw;
+ chip->ecc.read_oob = denali_read_oob;
+ chip->ecc.write_oob = denali_write_oob;
+ chip->erase = denali_erase;
+
+ ret = denali_multidev_fixup(denali);
+ if (ret)
+ goto disable_irq;
+
+ /*
+ * This buffer is DMA-mapped by denali_{read,write}_page_raw. Do not
+ * use devm_kmalloc() because the memory allocated by devm_ does not
+ * guarantee DMA-safe alignment.
+ */
+ denali->buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
+ if (!denali->buf) {
+ ret = -ENOMEM;
+ goto disable_irq;
+ }
+
+ ret = nand_scan_tail(mtd);
+ if (ret)
+ goto free_buf;
+
+ ret = mtd_device_register(mtd, NULL, 0);
+ if (ret) {
+ dev_err(denali->dev, "Failed to register MTD: %d\n", ret);
+ goto cleanup_nand;
+ }
+ return 0;
+
+cleanup_nand:
+ nand_cleanup(chip);
+free_buf:
+ kfree(denali->buf);
+disable_irq:
+ denali_disable_irq(denali);
+
+ return ret;
+}
+EXPORT_SYMBOL(denali_init);
+
+void denali_remove(struct denali_nand_info *denali)
+{
+ struct mtd_info *mtd = nand_to_mtd(&denali->nand);
+
+ nand_release(mtd);
+ kfree(denali->buf);
+ denali_disable_irq(denali);
+}
+EXPORT_SYMBOL(denali_remove);
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/raw/denali.h
index 9ad33d237378..9ad33d237378 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/raw/denali.h
diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/raw/denali_dt.c
index cfd33e6ca77f..cfd33e6ca77f 100644
--- a/drivers/mtd/nand/denali_dt.c
+++ b/drivers/mtd/nand/raw/denali_dt.c
diff --git a/drivers/mtd/nand/denali_pci.c b/drivers/mtd/nand/raw/denali_pci.c
index 49cb3e1f8bd0..49cb3e1f8bd0 100644
--- a/drivers/mtd/nand/denali_pci.c
+++ b/drivers/mtd/nand/raw/denali_pci.c
diff --git a/drivers/mtd/nand/raw/diskonchip.c b/drivers/mtd/nand/raw/diskonchip.c
new file mode 100644
index 000000000000..86a258de0b75
--- /dev/null
+++ b/drivers/mtd/nand/raw/diskonchip.c
@@ -0,0 +1,1713 @@
+/*
+ * (C) 2003 Red Hat, Inc.
+ * (C) 2004 Dan Brown <dan_brown@ieee.org>
+ * (C) 2004 Kalev Lember <kalev@smartlink.ee>
+ *
+ * Author: David Woodhouse <dwmw2@infradead.org>
+ * Additional Diskonchip 2000 and Millennium support by Dan Brown <dan_brown@ieee.org>
+ * Diskonchip Millennium Plus support by Kalev Lember <kalev@smartlink.ee>
+ *
+ * Error correction code lifted from the old docecc code
+ * Author: Fabrice Bellard (fabrice.bellard@netgem.com)
+ * Copyright (C) 2000 Netgem S.A.
+ * converted to the generic Reed-Solomon library by Thomas Gleixner <tglx@linutronix.de>
+ *
+ * Interface to generic NAND code for M-Systems DiskOnChip devices
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/rslib.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/doc2000.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/inftl.h>
+#include <linux/module.h>
+
+/* Where to look for the devices? */
+#ifndef CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS
+#define CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS 0
+#endif
+
+static unsigned long doc_locations[] __initdata = {
+#if defined (__alpha__) || defined(__i386__) || defined(__x86_64__)
+#ifdef CONFIG_MTD_NAND_DISKONCHIP_PROBE_HIGH
+ 0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000,
+ 0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000,
+ 0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000,
+ 0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000,
+ 0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000,
+#else
+ 0xc8000, 0xca000, 0xcc000, 0xce000,
+ 0xd0000, 0xd2000, 0xd4000, 0xd6000,
+ 0xd8000, 0xda000, 0xdc000, 0xde000,
+ 0xe0000, 0xe2000, 0xe4000, 0xe6000,
+ 0xe8000, 0xea000, 0xec000, 0xee000,
+#endif
+#endif
+ 0xffffffff };
+
+static struct mtd_info *doclist = NULL;
+
+struct doc_priv {
+ void __iomem *virtadr;
+ unsigned long physadr;
+ u_char ChipID;
+ u_char CDSNControl;
+ int chips_per_floor; /* The number of chips detected on each floor */
+ int curfloor;
+ int curchip;
+ int mh0_page;
+ int mh1_page;
+ struct mtd_info *nextdoc;
+
+ /* Handle the last stage of initialization (BBT scan, partitioning) */
+ int (*late_init)(struct mtd_info *mtd);
+};
+
+/* This is the ecc value computed by the HW ecc generator upon writing an empty
+ page, one with all 0xff for data. */
+static u_char empty_write_ecc[6] = { 0x4b, 0x00, 0xe2, 0x0e, 0x93, 0xf7 };
+
+#define INFTL_BBT_RESERVED_BLOCKS 4
+
+#define DoC_is_MillenniumPlus(doc) ((doc)->ChipID == DOC_ChipID_DocMilPlus16 || (doc)->ChipID == DOC_ChipID_DocMilPlus32)
+#define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil)
+#define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k)
+
+static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd,
+ unsigned int bitmask);
+static void doc200x_select_chip(struct mtd_info *mtd, int chip);
+
+static int debug = 0;
+module_param(debug, int, 0);
+
+static int try_dword = 1;
+module_param(try_dword, int, 0);
+
+static int no_ecc_failures = 0;
+module_param(no_ecc_failures, int, 0);
+
+static int no_autopart = 0;
+module_param(no_autopart, int, 0);
+
+static int show_firmware_partition = 0;
+module_param(show_firmware_partition, int, 0);
+
+#ifdef CONFIG_MTD_NAND_DISKONCHIP_BBTWRITE
+static int inftl_bbt_write = 1;
+#else
+static int inftl_bbt_write = 0;
+#endif
+module_param(inftl_bbt_write, int, 0);
+
+static unsigned long doc_config_location = CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS;
+module_param(doc_config_location, ulong, 0);
+MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip");
+
+/* Sector size for HW ECC */
+#define SECTOR_SIZE 512
+/* The sector bytes are packed into NB_DATA 10 bit words */
+#define NB_DATA (((SECTOR_SIZE + 1) * 8 + 6) / 10)
+/* Number of roots */
+#define NROOTS 4
+/* First consective root */
+#define FCR 510
+/* Number of symbols */
+#define NN 1023
+
+/* the Reed Solomon control structure */
+static struct rs_control *rs_decoder;
+
+/*
+ * The HW decoder in the DoC ASIC's provides us a error syndrome,
+ * which we must convert to a standard syndrome usable by the generic
+ * Reed-Solomon library code.
+ *
+ * Fabrice Bellard figured this out in the old docecc code. I added
+ * some comments, improved a minor bit and converted it to make use
+ * of the generic Reed-Solomon library. tglx
+ */
+static int doc_ecc_decode(struct rs_control *rs, uint8_t *data, uint8_t *ecc)
+{
+ int i, j, nerr, errpos[8];
+ uint8_t parity;
+ uint16_t ds[4], s[5], tmp, errval[8], syn[4];
+
+ memset(syn, 0, sizeof(syn));
+ /* Convert the ecc bytes into words */
+ ds[0] = ((ecc[4] & 0xff) >> 0) | ((ecc[5] & 0x03) << 8);
+ ds[1] = ((ecc[5] & 0xfc) >> 2) | ((ecc[2] & 0x0f) << 6);
+ ds[2] = ((ecc[2] & 0xf0) >> 4) | ((ecc[3] & 0x3f) << 4);
+ ds[3] = ((ecc[3] & 0xc0) >> 6) | ((ecc[0] & 0xff) << 2);
+ parity = ecc[1];
+
+ /* Initialize the syndrome buffer */
+ for (i = 0; i < NROOTS; i++)
+ s[i] = ds[0];
+ /*
+ * Evaluate
+ * s[i] = ds[3]x^3 + ds[2]x^2 + ds[1]x^1 + ds[0]
+ * where x = alpha^(FCR + i)
+ */
+ for (j = 1; j < NROOTS; j++) {
+ if (ds[j] == 0)
+ continue;
+ tmp = rs->index_of[ds[j]];
+ for (i = 0; i < NROOTS; i++)
+ s[i] ^= rs->alpha_to[rs_modnn(rs, tmp + (FCR + i) * j)];
+ }
+
+ /* Calc syn[i] = s[i] / alpha^(v + i) */
+ for (i = 0; i < NROOTS; i++) {
+ if (s[i])
+ syn[i] = rs_modnn(rs, rs->index_of[s[i]] + (NN - FCR - i));
+ }
+ /* Call the decoder library */
+ nerr = decode_rs16(rs, NULL, NULL, 1019, syn, 0, errpos, 0, errval);
+
+ /* Incorrectable errors ? */
+ if (nerr < 0)
+ return nerr;
+
+ /*
+ * Correct the errors. The bitpositions are a bit of magic,
+ * but they are given by the design of the de/encoder circuit
+ * in the DoC ASIC's.
+ */
+ for (i = 0; i < nerr; i++) {
+ int index, bitpos, pos = 1015 - errpos[i];
+ uint8_t val;
+ if (pos >= NB_DATA && pos < 1019)
+ continue;
+ if (pos < NB_DATA) {
+ /* extract bit position (MSB first) */
+ pos = 10 * (NB_DATA - 1 - pos) - 6;
+ /* now correct the following 10 bits. At most two bytes
+ can be modified since pos is even */
+ index = (pos >> 3) ^ 1;
+ bitpos = pos & 7;
+ if ((index >= 0 && index < SECTOR_SIZE) || index == (SECTOR_SIZE + 1)) {
+ val = (uint8_t) (errval[i] >> (2 + bitpos));
+ parity ^= val;
+ if (index < SECTOR_SIZE)
+ data[index] ^= val;
+ }
+ index = ((pos >> 3) + 1) ^ 1;
+ bitpos = (bitpos + 10) & 7;
+ if (bitpos == 0)
+ bitpos = 8;
+ if ((index >= 0 && index < SECTOR_SIZE) || index == (SECTOR_SIZE + 1)) {
+ val = (uint8_t) (errval[i] << (8 - bitpos));
+ parity ^= val;
+ if (index < SECTOR_SIZE)
+ data[index] ^= val;
+ }
+ }
+ }
+ /* If the parity is wrong, no rescue possible */
+ return parity ? -EBADMSG : nerr;
+}
+
+static void DoC_Delay(struct doc_priv *doc, unsigned short cycles)
+{
+ volatile char dummy;
+ int i;
+
+ for (i = 0; i < cycles; i++) {
+ if (DoC_is_Millennium(doc))
+ dummy = ReadDOC(doc->virtadr, NOP);
+ else if (DoC_is_MillenniumPlus(doc))
+ dummy = ReadDOC(doc->virtadr, Mplus_NOP);
+ else
+ dummy = ReadDOC(doc->virtadr, DOCStatus);
+ }
+
+}
+
+#define CDSN_CTRL_FR_B_MASK (CDSN_CTRL_FR_B0 | CDSN_CTRL_FR_B1)
+
+/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */
+static int _DoC_WaitReady(struct doc_priv *doc)
+{
+ void __iomem *docptr = doc->virtadr;
+ unsigned long timeo = jiffies + (HZ * 10);
+
+ if (debug)
+ printk("_DoC_WaitReady...\n");
+ /* Out-of-line routine to wait for chip response */
+ if (DoC_is_MillenniumPlus(doc)) {
+ while ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) {
+ if (time_after(jiffies, timeo)) {
+ printk("_DoC_WaitReady timed out.\n");
+ return -EIO;
+ }
+ udelay(1);
+ cond_resched();
+ }
+ } else {
+ while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) {
+ if (time_after(jiffies, timeo)) {
+ printk("_DoC_WaitReady timed out.\n");
+ return -EIO;
+ }
+ udelay(1);
+ cond_resched();
+ }
+ }
+
+ return 0;
+}
+
+static inline int DoC_WaitReady(struct doc_priv *doc)
+{
+ void __iomem *docptr = doc->virtadr;
+ int ret = 0;
+
+ if (DoC_is_MillenniumPlus(doc)) {
+ DoC_Delay(doc, 4);
+
+ if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK)
+ /* Call the out-of-line routine to wait */
+ ret = _DoC_WaitReady(doc);
+ } else {
+ DoC_Delay(doc, 4);
+
+ if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B))
+ /* Call the out-of-line routine to wait */
+ ret = _DoC_WaitReady(doc);
+ DoC_Delay(doc, 2);
+ }
+
+ if (debug)
+ printk("DoC_WaitReady OK\n");
+ return ret;
+}
+
+static void doc2000_write_byte(struct mtd_info *mtd, u_char datum)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+ void __iomem *docptr = doc->virtadr;
+
+ if (debug)
+ printk("write_byte %02x\n", datum);
+ WriteDOC(datum, docptr, CDSNSlowIO);
+ WriteDOC(datum, docptr, 2k_CDSN_IO);
+}
+
+static u_char doc2000_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+ void __iomem *docptr = doc->virtadr;
+ u_char ret;
+
+ ReadDOC(docptr, CDSNSlowIO);
+ DoC_Delay(doc, 2);
+ ret = ReadDOC(docptr, 2k_CDSN_IO);
+ if (debug)
+ printk("read_byte returns %02x\n", ret);
+ return ret;
+}
+
+static void doc2000_writebuf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+ void __iomem *docptr = doc->virtadr;
+ int i;
+ if (debug)
+ printk("writebuf of %d bytes: ", len);
+ for (i = 0; i < len; i++) {
+ WriteDOC_(buf[i], docptr, DoC_2k_CDSN_IO + i);
+ if (debug && i < 16)
+ printk("%02x ", buf[i]);
+ }
+ if (debug)
+ printk("\n");
+}
+
+static void doc2000_readbuf(struct mtd_info *mtd, u_char *buf, int len)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+ void __iomem *docptr = doc->virtadr;
+ int i;
+
+ if (debug)
+ printk("readbuf of %d bytes: ", len);
+
+ for (i = 0; i < len; i++) {
+ buf[i] = ReadDOC(docptr, 2k_CDSN_IO + i);
+ }
+}
+
+static void doc2000_readbuf_dword(struct mtd_info *mtd, u_char *buf, int len)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+ void __iomem *docptr = doc->virtadr;
+ int i;
+
+ if (debug)
+ printk("readbuf_dword of %d bytes: ", len);
+
+ if (unlikely((((unsigned long)buf) | len) & 3)) {
+ for (i = 0; i < len; i++) {
+ *(uint8_t *) (&buf[i]) = ReadDOC(docptr, 2k_CDSN_IO + i);
+ }
+ } else {
+ for (i = 0; i < len; i += 4) {
+ *(uint32_t *) (&buf[i]) = readl(docptr + DoC_2k_CDSN_IO + i);
+ }
+ }
+}
+
+static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+ uint16_t ret;
+
+ doc200x_select_chip(mtd, nr);
+ doc200x_hwcontrol(mtd, NAND_CMD_READID,
+ NAND_CTRL_CLE | NAND_CTRL_CHANGE);
+ doc200x_hwcontrol(mtd, 0, NAND_CTRL_ALE | NAND_CTRL_CHANGE);
+ doc200x_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+
+ /* We can't use dev_ready here, but at least we wait for the
+ * command to complete
+ */
+ udelay(50);
+
+ ret = this->read_byte(mtd) << 8;
+ ret |= this->read_byte(mtd);
+
+ if (doc->ChipID == DOC_ChipID_Doc2k && try_dword && !nr) {
+ /* First chip probe. See if we get same results by 32-bit access */
+ union {
+ uint32_t dword;
+ uint8_t byte[4];
+ } ident;
+ void __iomem *docptr = doc->virtadr;
+
+ doc200x_hwcontrol(mtd, NAND_CMD_READID,
+ NAND_CTRL_CLE | NAND_CTRL_CHANGE);
+ doc200x_hwcontrol(mtd, 0, NAND_CTRL_ALE | NAND_CTRL_CHANGE);
+ doc200x_hwcontrol(mtd, NAND_CMD_NONE,
+ NAND_NCE | NAND_CTRL_CHANGE);
+
+ udelay(50);
+
+ ident.dword = readl(docptr + DoC_2k_CDSN_IO);
+ if (((ident.byte[0] << 8) | ident.byte[1]) == ret) {
+ pr_info("DiskOnChip 2000 responds to DWORD access\n");
+ this->read_buf = &doc2000_readbuf_dword;
+ }
+ }
+
+ return ret;
+}
+
+static void __init doc2000_count_chips(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+ uint16_t mfrid;
+ int i;
+
+ /* Max 4 chips per floor on DiskOnChip 2000 */
+ doc->chips_per_floor = 4;
+
+ /* Find out what the first chip is */
+ mfrid = doc200x_ident_chip(mtd, 0);
+
+ /* Find how many chips in each floor. */
+ for (i = 1; i < 4; i++) {
+ if (doc200x_ident_chip(mtd, i) != mfrid)
+ break;
+ }
+ doc->chips_per_floor = i;
+ pr_debug("Detected %d chips per floor.\n", i);
+}
+
+static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this)
+{
+ struct doc_priv *doc = nand_get_controller_data(this);
+
+ int status;
+
+ DoC_WaitReady(doc);
+ nand_status_op(this, NULL);
+ DoC_WaitReady(doc);
+ status = (int)this->read_byte(mtd);
+
+ return status;
+}
+
+static void doc2001_write_byte(struct mtd_info *mtd, u_char datum)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+ void __iomem *docptr = doc->virtadr;
+
+ WriteDOC(datum, docptr, CDSNSlowIO);
+ WriteDOC(datum, docptr, Mil_CDSN_IO);
+ WriteDOC(datum, docptr, WritePipeTerm);
+}
+
+static u_char doc2001_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+ void __iomem *docptr = doc->virtadr;
+
+ //ReadDOC(docptr, CDSNSlowIO);
+ /* 11.4.5 -- delay twice to allow extended length cycle */
+ DoC_Delay(doc, 2);
+ ReadDOC(docptr, ReadPipeInit);
+ //return ReadDOC(docptr, Mil_CDSN_IO);
+ return ReadDOC(docptr, LastDataRead);
+}
+
+static void doc2001_writebuf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+ void __iomem *docptr = doc->virtadr;
+ int i;
+
+ for (i = 0; i < len; i++)
+ WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i);
+ /* Terminate write pipeline */
+ WriteDOC(0x00, docptr, WritePipeTerm);
+}
+
+static void doc2001_readbuf(struct mtd_info *mtd, u_char *buf, int len)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+ void __iomem *docptr = doc->virtadr;
+ int i;
+
+ /* Start read pipeline */
+ ReadDOC(docptr, ReadPipeInit);
+
+ for (i = 0; i < len - 1; i++)
+ buf[i] = ReadDOC(docptr, Mil_CDSN_IO + (i & 0xff));
+
+ /* Terminate read pipeline */
+ buf[i] = ReadDOC(docptr, LastDataRead);
+}
+
+static u_char doc2001plus_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+ void __iomem *docptr = doc->virtadr;
+ u_char ret;
+
+ ReadDOC(docptr, Mplus_ReadPipeInit);
+ ReadDOC(docptr, Mplus_ReadPipeInit);
+ ret = ReadDOC(docptr, Mplus_LastDataRead);
+ if (debug)
+ printk("read_byte returns %02x\n", ret);
+ return ret;
+}
+
+static void doc2001plus_writebuf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+ void __iomem *docptr = doc->virtadr;
+ int i;
+
+ if (debug)
+ printk("writebuf of %d bytes: ", len);
+ for (i = 0; i < len; i++) {
+ WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i);
+ if (debug && i < 16)
+ printk("%02x ", buf[i]);
+ }
+ if (debug)
+ printk("\n");
+}
+
+static void doc2001plus_readbuf(struct mtd_info *mtd, u_char *buf, int len)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+ void __iomem *docptr = doc->virtadr;
+ int i;
+
+ if (debug)
+ printk("readbuf of %d bytes: ", len);
+
+ /* Start read pipeline */
+ ReadDOC(docptr, Mplus_ReadPipeInit);
+ ReadDOC(docptr, Mplus_ReadPipeInit);
+
+ for (i = 0; i < len - 2; i++) {
+ buf[i] = ReadDOC(docptr, Mil_CDSN_IO);
+ if (debug && i < 16)
+ printk("%02x ", buf[i]);
+ }
+
+ /* Terminate read pipeline */
+ buf[len - 2] = ReadDOC(docptr, Mplus_LastDataRead);
+ if (debug && i < 16)
+ printk("%02x ", buf[len - 2]);
+ buf[len - 1] = ReadDOC(docptr, Mplus_LastDataRead);
+ if (debug && i < 16)
+ printk("%02x ", buf[len - 1]);
+ if (debug)
+ printk("\n");
+}
+
+static void doc2001plus_select_chip(struct mtd_info *mtd, int chip)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+ void __iomem *docptr = doc->virtadr;
+ int floor = 0;
+
+ if (debug)
+ printk("select chip (%d)\n", chip);
+
+ if (chip == -1) {
+ /* Disable flash internally */
+ WriteDOC(0, docptr, Mplus_FlashSelect);
+ return;
+ }
+
+ floor = chip / doc->chips_per_floor;
+ chip -= (floor * doc->chips_per_floor);
+
+ /* Assert ChipEnable and deassert WriteProtect */
+ WriteDOC((DOC_FLASH_CE), docptr, Mplus_FlashSelect);
+ nand_reset_op(this);
+
+ doc->curchip = chip;
+ doc->curfloor = floor;
+}
+
+static void doc200x_select_chip(struct mtd_info *mtd, int chip)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+ void __iomem *docptr = doc->virtadr;
+ int floor = 0;
+
+ if (debug)
+ printk("select chip (%d)\n", chip);
+
+ if (chip == -1)
+ return;
+
+ floor = chip / doc->chips_per_floor;
+ chip -= (floor * doc->chips_per_floor);
+
+ /* 11.4.4 -- deassert CE before changing chip */
+ doc200x_hwcontrol(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
+
+ WriteDOC(floor, docptr, FloorSelect);
+ WriteDOC(chip, docptr, CDSNDeviceSelect);
+
+ doc200x_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+
+ doc->curchip = chip;
+ doc->curfloor = floor;
+}
+
+#define CDSN_CTRL_MSK (CDSN_CTRL_CE | CDSN_CTRL_CLE | CDSN_CTRL_ALE)
+
+static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd,
+ unsigned int ctrl)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+ void __iomem *docptr = doc->virtadr;
+
+ if (ctrl & NAND_CTRL_CHANGE) {
+ doc->CDSNControl &= ~CDSN_CTRL_MSK;
+ doc->CDSNControl |= ctrl & CDSN_CTRL_MSK;
+ if (debug)
+ printk("hwcontrol(%d): %02x\n", cmd, doc->CDSNControl);
+ WriteDOC(doc->CDSNControl, docptr, CDSNControl);
+ /* 11.4.3 -- 4 NOPs after CSDNControl write */
+ DoC_Delay(doc, 4);
+ }
+ if (cmd != NAND_CMD_NONE) {
+ if (DoC_is_2000(doc))
+ doc2000_write_byte(mtd, cmd);
+ else
+ doc2001_write_byte(mtd, cmd);
+ }
+}
+
+static void doc2001plus_command(struct mtd_info *mtd, unsigned command, int column, int page_addr)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+ void __iomem *docptr = doc->virtadr;
+
+ /*
+ * Must terminate write pipeline before sending any commands
+ * to the device.
+ */
+ if (command == NAND_CMD_PAGEPROG) {
+ WriteDOC(0x00, docptr, Mplus_WritePipeTerm);
+ WriteDOC(0x00, docptr, Mplus_WritePipeTerm);
+ }
+
+ /*
+ * Write out the command to the device.
+ */
+ if (command == NAND_CMD_SEQIN) {
+ int readcmd;
+
+ if (column >= mtd->writesize) {
+ /* OOB area */
+ column -= mtd->writesize;
+ readcmd = NAND_CMD_READOOB;
+ } else if (column < 256) {
+ /* First 256 bytes --> READ0 */
+ readcmd = NAND_CMD_READ0;
+ } else {
+ column -= 256;
+ readcmd = NAND_CMD_READ1;
+ }
+ WriteDOC(readcmd, docptr, Mplus_FlashCmd);
+ }
+ WriteDOC(command, docptr, Mplus_FlashCmd);
+ WriteDOC(0, docptr, Mplus_WritePipeTerm);
+ WriteDOC(0, docptr, Mplus_WritePipeTerm);
+
+ if (column != -1 || page_addr != -1) {
+ /* Serially input address */
+ if (column != -1) {
+ /* Adjust columns for 16 bit buswidth */
+ if (this->options & NAND_BUSWIDTH_16 &&
+ !nand_opcode_8bits(command))
+ column >>= 1;
+ WriteDOC(column, docptr, Mplus_FlashAddress);
+ }
+ if (page_addr != -1) {
+ WriteDOC((unsigned char)(page_addr & 0xff), docptr, Mplus_FlashAddress);
+ WriteDOC((unsigned char)((page_addr >> 8) & 0xff), docptr, Mplus_FlashAddress);
+ if (this->options & NAND_ROW_ADDR_3) {
+ WriteDOC((unsigned char)((page_addr >> 16) & 0x0f), docptr, Mplus_FlashAddress);
+ printk("high density\n");
+ }
+ }
+ WriteDOC(0, docptr, Mplus_WritePipeTerm);
+ WriteDOC(0, docptr, Mplus_WritePipeTerm);
+ /* deassert ALE */
+ if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 ||
+ command == NAND_CMD_READOOB || command == NAND_CMD_READID)
+ WriteDOC(0, docptr, Mplus_FlashControl);
+ }
+
+ /*
+ * program and erase have their own busy handlers
+ * status and sequential in needs no delay
+ */
+ switch (command) {
+
+ case NAND_CMD_PAGEPROG:
+ case NAND_CMD_ERASE1:
+ case NAND_CMD_ERASE2:
+ case NAND_CMD_SEQIN:
+ case NAND_CMD_STATUS:
+ return;
+
+ case NAND_CMD_RESET:
+ if (this->dev_ready)
+ break;
+ udelay(this->chip_delay);
+ WriteDOC(NAND_CMD_STATUS, docptr, Mplus_FlashCmd);
+ WriteDOC(0, docptr, Mplus_WritePipeTerm);
+ WriteDOC(0, docptr, Mplus_WritePipeTerm);
+ while (!(this->read_byte(mtd) & 0x40)) ;
+ return;
+
+ /* This applies to read commands */
+ default:
+ /*
+ * If we don't have access to the busy pin, we apply the given
+ * command delay
+ */
+ if (!this->dev_ready) {
+ udelay(this->chip_delay);
+ return;
+ }
+ }
+
+ /* Apply this short delay always to ensure that we do wait tWB in
+ * any case on any machine. */
+ ndelay(100);
+ /* wait until command is processed */
+ while (!this->dev_ready(mtd)) ;
+}
+
+static int doc200x_dev_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+ void __iomem *docptr = doc->virtadr;
+
+ if (DoC_is_MillenniumPlus(doc)) {
+ /* 11.4.2 -- must NOP four times before checking FR/B# */
+ DoC_Delay(doc, 4);
+ if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) {
+ if (debug)
+ printk("not ready\n");
+ return 0;
+ }
+ if (debug)
+ printk("was ready\n");
+ return 1;
+ } else {
+ /* 11.4.2 -- must NOP four times before checking FR/B# */
+ DoC_Delay(doc, 4);
+ if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) {
+ if (debug)
+ printk("not ready\n");
+ return 0;
+ }
+ /* 11.4.2 -- Must NOP twice if it's ready */
+ DoC_Delay(doc, 2);
+ if (debug)
+ printk("was ready\n");
+ return 1;
+ }
+}
+
+static int doc200x_block_bad(struct mtd_info *mtd, loff_t ofs)
+{
+ /* This is our last resort if we couldn't find or create a BBT. Just
+ pretend all blocks are good. */
+ return 0;
+}
+
+static void doc200x_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+ void __iomem *docptr = doc->virtadr;
+
+ /* Prime the ECC engine */
+ switch (mode) {
+ case NAND_ECC_READ:
+ WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
+ WriteDOC(DOC_ECC_EN, docptr, ECCConf);
+ break;
+ case NAND_ECC_WRITE:
+ WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
+ WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf);
+ break;
+ }
+}
+
+static void doc2001plus_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+ void __iomem *docptr = doc->virtadr;
+
+ /* Prime the ECC engine */
+ switch (mode) {
+ case NAND_ECC_READ:
+ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);
+ WriteDOC(DOC_ECC_EN, docptr, Mplus_ECCConf);
+ break;
+ case NAND_ECC_WRITE:
+ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);
+ WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, Mplus_ECCConf);
+ break;
+ }
+}
+
+/* This code is only called on write */
+static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat, unsigned char *ecc_code)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+ void __iomem *docptr = doc->virtadr;
+ int i;
+ int emptymatch = 1;
+
+ /* flush the pipeline */
+ if (DoC_is_2000(doc)) {
+ WriteDOC(doc->CDSNControl & ~CDSN_CTRL_FLASH_IO, docptr, CDSNControl);
+ WriteDOC(0, docptr, 2k_CDSN_IO);
+ WriteDOC(0, docptr, 2k_CDSN_IO);
+ WriteDOC(0, docptr, 2k_CDSN_IO);
+ WriteDOC(doc->CDSNControl, docptr, CDSNControl);
+ } else if (DoC_is_MillenniumPlus(doc)) {
+ WriteDOC(0, docptr, Mplus_NOP);
+ WriteDOC(0, docptr, Mplus_NOP);
+ WriteDOC(0, docptr, Mplus_NOP);
+ } else {
+ WriteDOC(0, docptr, NOP);
+ WriteDOC(0, docptr, NOP);
+ WriteDOC(0, docptr, NOP);
+ }
+
+ for (i = 0; i < 6; i++) {
+ if (DoC_is_MillenniumPlus(doc))
+ ecc_code[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i);
+ else
+ ecc_code[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i);
+ if (ecc_code[i] != empty_write_ecc[i])
+ emptymatch = 0;
+ }
+ if (DoC_is_MillenniumPlus(doc))
+ WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf);
+ else
+ WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
+#if 0
+ /* If emptymatch=1, we might have an all-0xff data buffer. Check. */
+ if (emptymatch) {
+ /* Note: this somewhat expensive test should not be triggered
+ often. It could be optimized away by examining the data in
+ the writebuf routine, and remembering the result. */
+ for (i = 0; i < 512; i++) {
+ if (dat[i] == 0xff)
+ continue;
+ emptymatch = 0;
+ break;
+ }
+ }
+ /* If emptymatch still =1, we do have an all-0xff data buffer.
+ Return all-0xff ecc value instead of the computed one, so
+ it'll look just like a freshly-erased page. */
+ if (emptymatch)
+ memset(ecc_code, 0xff, 6);
+#endif
+ return 0;
+}
+
+static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat,
+ u_char *read_ecc, u_char *isnull)
+{
+ int i, ret = 0;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+ void __iomem *docptr = doc->virtadr;
+ uint8_t calc_ecc[6];
+ volatile u_char dummy;
+
+ /* flush the pipeline */
+ if (DoC_is_2000(doc)) {
+ dummy = ReadDOC(docptr, 2k_ECCStatus);
+ dummy = ReadDOC(docptr, 2k_ECCStatus);
+ dummy = ReadDOC(docptr, 2k_ECCStatus);
+ } else if (DoC_is_MillenniumPlus(doc)) {
+ dummy = ReadDOC(docptr, Mplus_ECCConf);
+ dummy = ReadDOC(docptr, Mplus_ECCConf);
+ dummy = ReadDOC(docptr, Mplus_ECCConf);
+ } else {
+ dummy = ReadDOC(docptr, ECCConf);
+ dummy = ReadDOC(docptr, ECCConf);
+ dummy = ReadDOC(docptr, ECCConf);
+ }
+
+ /* Error occurred ? */
+ if (dummy & 0x80) {
+ for (i = 0; i < 6; i++) {
+ if (DoC_is_MillenniumPlus(doc))
+ calc_ecc[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i);
+ else
+ calc_ecc[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i);
+ }
+
+ ret = doc_ecc_decode(rs_decoder, dat, calc_ecc);
+ if (ret > 0)
+ pr_err("doc200x_correct_data corrected %d errors\n",
+ ret);
+ }
+ if (DoC_is_MillenniumPlus(doc))
+ WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf);
+ else
+ WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
+ if (no_ecc_failures && mtd_is_eccerr(ret)) {
+ pr_err("suppressing ECC failure\n");
+ ret = 0;
+ }
+ return ret;
+}
+
+//u_char mydatabuf[528];
+
+static int doc200x_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ if (section)
+ return -ERANGE;
+
+ oobregion->offset = 0;
+ oobregion->length = 6;
+
+ return 0;
+}
+
+static int doc200x_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ if (section > 1)
+ return -ERANGE;
+
+ /*
+ * The strange out-of-order free bytes definition is a (possibly
+ * unneeded) attempt to retain compatibility. It used to read:
+ * .oobfree = { {8, 8} }
+ * Since that leaves two bytes unusable, it was changed. But the
+ * following scheme might affect existing jffs2 installs by moving the
+ * cleanmarker:
+ * .oobfree = { {6, 10} }
+ * jffs2 seems to handle the above gracefully, but the current scheme
+ * seems safer. The only problem with it is that any code retrieving
+ * free bytes position must be able to handle out-of-order segments.
+ */
+ if (!section) {
+ oobregion->offset = 8;
+ oobregion->length = 8;
+ } else {
+ oobregion->offset = 6;
+ oobregion->length = 2;
+ }
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops doc200x_ooblayout_ops = {
+ .ecc = doc200x_ooblayout_ecc,
+ .free = doc200x_ooblayout_free,
+};
+
+/* Find the (I)NFTL Media Header, and optionally also the mirror media header.
+ On successful return, buf will contain a copy of the media header for
+ further processing. id is the string to scan for, and will presumably be
+ either "ANAND" or "BNAND". If findmirror=1, also look for the mirror media
+ header. The page #s of the found media headers are placed in mh0_page and
+ mh1_page in the DOC private structure. */
+static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, const char *id, int findmirror)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+ unsigned offs;
+ int ret;
+ size_t retlen;
+
+ for (offs = 0; offs < mtd->size; offs += mtd->erasesize) {
+ ret = mtd_read(mtd, offs, mtd->writesize, &retlen, buf);
+ if (retlen != mtd->writesize)
+ continue;
+ if (ret) {
+ pr_warn("ECC error scanning DOC at 0x%x\n", offs);
+ }
+ if (memcmp(buf, id, 6))
+ continue;
+ pr_info("Found DiskOnChip %s Media Header at 0x%x\n", id, offs);
+ if (doc->mh0_page == -1) {
+ doc->mh0_page = offs >> this->page_shift;
+ if (!findmirror)
+ return 1;
+ continue;
+ }
+ doc->mh1_page = offs >> this->page_shift;
+ return 2;
+ }
+ if (doc->mh0_page == -1) {
+ pr_warn("DiskOnChip %s Media Header not found.\n", id);
+ return 0;
+ }
+ /* Only one mediaheader was found. We want buf to contain a
+ mediaheader on return, so we'll have to re-read the one we found. */
+ offs = doc->mh0_page << this->page_shift;
+ ret = mtd_read(mtd, offs, mtd->writesize, &retlen, buf);
+ if (retlen != mtd->writesize) {
+ /* Insanity. Give up. */
+ pr_err("Read DiskOnChip Media Header once, but can't reread it???\n");
+ return 0;
+ }
+ return 1;
+}
+
+static inline int __init nftl_partscan(struct mtd_info *mtd, struct mtd_partition *parts)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+ int ret = 0;
+ u_char *buf;
+ struct NFTLMediaHeader *mh;
+ const unsigned psize = 1 << this->page_shift;
+ int numparts = 0;
+ unsigned blocks, maxblocks;
+ int offs, numheaders;
+
+ buf = kmalloc(mtd->writesize, GFP_KERNEL);
+ if (!buf) {
+ return 0;
+ }
+ if (!(numheaders = find_media_headers(mtd, buf, "ANAND", 1)))
+ goto out;
+ mh = (struct NFTLMediaHeader *)buf;
+
+ le16_to_cpus(&mh->NumEraseUnits);
+ le16_to_cpus(&mh->FirstPhysicalEUN);
+ le32_to_cpus(&mh->FormattedSize);
+
+ pr_info(" DataOrgID = %s\n"
+ " NumEraseUnits = %d\n"
+ " FirstPhysicalEUN = %d\n"
+ " FormattedSize = %d\n"
+ " UnitSizeFactor = %d\n",
+ mh->DataOrgID, mh->NumEraseUnits,
+ mh->FirstPhysicalEUN, mh->FormattedSize,
+ mh->UnitSizeFactor);
+
+ blocks = mtd->size >> this->phys_erase_shift;
+ maxblocks = min(32768U, mtd->erasesize - psize);
+
+ if (mh->UnitSizeFactor == 0x00) {
+ /* Auto-determine UnitSizeFactor. The constraints are:
+ - There can be at most 32768 virtual blocks.
+ - There can be at most (virtual block size - page size)
+ virtual blocks (because MediaHeader+BBT must fit in 1).
+ */
+ mh->UnitSizeFactor = 0xff;
+ while (blocks > maxblocks) {
+ blocks >>= 1;
+ maxblocks = min(32768U, (maxblocks << 1) + psize);
+ mh->UnitSizeFactor--;
+ }
+ pr_warn("UnitSizeFactor=0x00 detected. Correct value is assumed to be 0x%02x.\n", mh->UnitSizeFactor);
+ }
+
+ /* NOTE: The lines below modify internal variables of the NAND and MTD
+ layers; variables with have already been configured by nand_scan.
+ Unfortunately, we didn't know before this point what these values
+ should be. Thus, this code is somewhat dependent on the exact
+ implementation of the NAND layer. */
+ if (mh->UnitSizeFactor != 0xff) {
+ this->bbt_erase_shift += (0xff - mh->UnitSizeFactor);
+ mtd->erasesize <<= (0xff - mh->UnitSizeFactor);
+ pr_info("Setting virtual erase size to %d\n", mtd->erasesize);
+ blocks = mtd->size >> this->bbt_erase_shift;
+ maxblocks = min(32768U, mtd->erasesize - psize);
+ }
+
+ if (blocks > maxblocks) {
+ pr_err("UnitSizeFactor of 0x%02x is inconsistent with device size. Aborting.\n", mh->UnitSizeFactor);
+ goto out;
+ }
+
+ /* Skip past the media headers. */
+ offs = max(doc->mh0_page, doc->mh1_page);
+ offs <<= this->page_shift;
+ offs += mtd->erasesize;
+
+ if (show_firmware_partition == 1) {
+ parts[0].name = " DiskOnChip Firmware / Media Header partition";
+ parts[0].offset = 0;
+ parts[0].size = offs;
+ numparts = 1;
+ }
+
+ parts[numparts].name = " DiskOnChip BDTL partition";
+ parts[numparts].offset = offs;
+ parts[numparts].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift;
+
+ offs += parts[numparts].size;
+ numparts++;
+
+ if (offs < mtd->size) {
+ parts[numparts].name = " DiskOnChip Remainder partition";
+ parts[numparts].offset = offs;
+ parts[numparts].size = mtd->size - offs;
+ numparts++;
+ }
+
+ ret = numparts;
+ out:
+ kfree(buf);
+ return ret;
+}
+
+/* This is a stripped-down copy of the code in inftlmount.c */
+static inline int __init inftl_partscan(struct mtd_info *mtd, struct mtd_partition *parts)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+ int ret = 0;
+ u_char *buf;
+ struct INFTLMediaHeader *mh;
+ struct INFTLPartition *ip;
+ int numparts = 0;
+ int blocks;
+ int vshift, lastvunit = 0;
+ int i;
+ int end = mtd->size;
+
+ if (inftl_bbt_write)
+ end -= (INFTL_BBT_RESERVED_BLOCKS << this->phys_erase_shift);
+
+ buf = kmalloc(mtd->writesize, GFP_KERNEL);
+ if (!buf) {
+ return 0;
+ }
+
+ if (!find_media_headers(mtd, buf, "BNAND", 0))
+ goto out;
+ doc->mh1_page = doc->mh0_page + (4096 >> this->page_shift);
+ mh = (struct INFTLMediaHeader *)buf;
+
+ le32_to_cpus(&mh->NoOfBootImageBlocks);
+ le32_to_cpus(&mh->NoOfBinaryPartitions);
+ le32_to_cpus(&mh->NoOfBDTLPartitions);
+ le32_to_cpus(&mh->BlockMultiplierBits);
+ le32_to_cpus(&mh->FormatFlags);
+ le32_to_cpus(&mh->PercentUsed);
+
+ pr_info(" bootRecordID = %s\n"
+ " NoOfBootImageBlocks = %d\n"
+ " NoOfBinaryPartitions = %d\n"
+ " NoOfBDTLPartitions = %d\n"
+ " BlockMultiplerBits = %d\n"
+ " FormatFlgs = %d\n"
+ " OsakVersion = %d.%d.%d.%d\n"
+ " PercentUsed = %d\n",
+ mh->bootRecordID, mh->NoOfBootImageBlocks,
+ mh->NoOfBinaryPartitions,
+ mh->NoOfBDTLPartitions,
+ mh->BlockMultiplierBits, mh->FormatFlags,
+ ((unsigned char *) &mh->OsakVersion)[0] & 0xf,
+ ((unsigned char *) &mh->OsakVersion)[1] & 0xf,
+ ((unsigned char *) &mh->OsakVersion)[2] & 0xf,
+ ((unsigned char *) &mh->OsakVersion)[3] & 0xf,
+ mh->PercentUsed);
+
+ vshift = this->phys_erase_shift + mh->BlockMultiplierBits;
+
+ blocks = mtd->size >> vshift;
+ if (blocks > 32768) {
+ pr_err("BlockMultiplierBits=%d is inconsistent with device size. Aborting.\n", mh->BlockMultiplierBits);
+ goto out;
+ }
+
+ blocks = doc->chips_per_floor << (this->chip_shift - this->phys_erase_shift);
+ if (inftl_bbt_write && (blocks > mtd->erasesize)) {
+ pr_err("Writeable BBTs spanning more than one erase block are not yet supported. FIX ME!\n");
+ goto out;
+ }
+
+ /* Scan the partitions */
+ for (i = 0; (i < 4); i++) {
+ ip = &(mh->Partitions[i]);
+ le32_to_cpus(&ip->virtualUnits);
+ le32_to_cpus(&ip->firstUnit);
+ le32_to_cpus(&ip->lastUnit);
+ le32_to_cpus(&ip->flags);
+ le32_to_cpus(&ip->spareUnits);
+ le32_to_cpus(&ip->Reserved0);
+
+ pr_info(" PARTITION[%d] ->\n"
+ " virtualUnits = %d\n"
+ " firstUnit = %d\n"
+ " lastUnit = %d\n"
+ " flags = 0x%x\n"
+ " spareUnits = %d\n",
+ i, ip->virtualUnits, ip->firstUnit,
+ ip->lastUnit, ip->flags,
+ ip->spareUnits);
+
+ if ((show_firmware_partition == 1) &&
+ (i == 0) && (ip->firstUnit > 0)) {
+ parts[0].name = " DiskOnChip IPL / Media Header partition";
+ parts[0].offset = 0;
+ parts[0].size = mtd->erasesize * ip->firstUnit;
+ numparts = 1;
+ }
+
+ if (ip->flags & INFTL_BINARY)
+ parts[numparts].name = " DiskOnChip BDK partition";
+ else
+ parts[numparts].name = " DiskOnChip BDTL partition";
+ parts[numparts].offset = ip->firstUnit << vshift;
+ parts[numparts].size = (1 + ip->lastUnit - ip->firstUnit) << vshift;
+ numparts++;
+ if (ip->lastUnit > lastvunit)
+ lastvunit = ip->lastUnit;
+ if (ip->flags & INFTL_LAST)
+ break;
+ }
+ lastvunit++;
+ if ((lastvunit << vshift) < end) {
+ parts[numparts].name = " DiskOnChip Remainder partition";
+ parts[numparts].offset = lastvunit << vshift;
+ parts[numparts].size = end - parts[numparts].offset;
+ numparts++;
+ }
+ ret = numparts;
+ out:
+ kfree(buf);
+ return ret;
+}
+
+static int __init nftl_scan_bbt(struct mtd_info *mtd)
+{
+ int ret, numparts;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+ struct mtd_partition parts[2];
+
+ memset((char *)parts, 0, sizeof(parts));
+ /* On NFTL, we have to find the media headers before we can read the
+ BBTs, since they're stored in the media header eraseblocks. */
+ numparts = nftl_partscan(mtd, parts);
+ if (!numparts)
+ return -EIO;
+ this->bbt_td->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT |
+ NAND_BBT_SAVECONTENT | NAND_BBT_WRITE |
+ NAND_BBT_VERSION;
+ this->bbt_td->veroffs = 7;
+ this->bbt_td->pages[0] = doc->mh0_page + 1;
+ if (doc->mh1_page != -1) {
+ this->bbt_md->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT |
+ NAND_BBT_SAVECONTENT | NAND_BBT_WRITE |
+ NAND_BBT_VERSION;
+ this->bbt_md->veroffs = 7;
+ this->bbt_md->pages[0] = doc->mh1_page + 1;
+ } else {
+ this->bbt_md = NULL;
+ }
+
+ ret = this->scan_bbt(mtd);
+ if (ret)
+ return ret;
+
+ return mtd_device_register(mtd, parts, no_autopart ? 0 : numparts);
+}
+
+static int __init inftl_scan_bbt(struct mtd_info *mtd)
+{
+ int ret, numparts;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+ struct mtd_partition parts[5];
+
+ if (this->numchips > doc->chips_per_floor) {
+ pr_err("Multi-floor INFTL devices not yet supported.\n");
+ return -EIO;
+ }
+
+ if (DoC_is_MillenniumPlus(doc)) {
+ this->bbt_td->options = NAND_BBT_2BIT | NAND_BBT_ABSPAGE;
+ if (inftl_bbt_write)
+ this->bbt_td->options |= NAND_BBT_WRITE;
+ this->bbt_td->pages[0] = 2;
+ this->bbt_md = NULL;
+ } else {
+ this->bbt_td->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | NAND_BBT_VERSION;
+ if (inftl_bbt_write)
+ this->bbt_td->options |= NAND_BBT_WRITE;
+ this->bbt_td->offs = 8;
+ this->bbt_td->len = 8;
+ this->bbt_td->veroffs = 7;
+ this->bbt_td->maxblocks = INFTL_BBT_RESERVED_BLOCKS;
+ this->bbt_td->reserved_block_code = 0x01;
+ this->bbt_td->pattern = "MSYS_BBT";
+
+ this->bbt_md->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | NAND_BBT_VERSION;
+ if (inftl_bbt_write)
+ this->bbt_md->options |= NAND_BBT_WRITE;
+ this->bbt_md->offs = 8;
+ this->bbt_md->len = 8;
+ this->bbt_md->veroffs = 7;
+ this->bbt_md->maxblocks = INFTL_BBT_RESERVED_BLOCKS;
+ this->bbt_md->reserved_block_code = 0x01;
+ this->bbt_md->pattern = "TBB_SYSM";
+ }
+
+ ret = this->scan_bbt(mtd);
+ if (ret)
+ return ret;
+
+ memset((char *)parts, 0, sizeof(parts));
+ numparts = inftl_partscan(mtd, parts);
+ /* At least for now, require the INFTL Media Header. We could probably
+ do without it for non-INFTL use, since all it gives us is
+ autopartitioning, but I want to give it more thought. */
+ if (!numparts)
+ return -EIO;
+ return mtd_device_register(mtd, parts, no_autopart ? 0 : numparts);
+}
+
+static inline int __init doc2000_init(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+
+ this->read_byte = doc2000_read_byte;
+ this->write_buf = doc2000_writebuf;
+ this->read_buf = doc2000_readbuf;
+ doc->late_init = nftl_scan_bbt;
+
+ doc->CDSNControl = CDSN_CTRL_FLASH_IO | CDSN_CTRL_ECC_IO;
+ doc2000_count_chips(mtd);
+ mtd->name = "DiskOnChip 2000 (NFTL Model)";
+ return (4 * doc->chips_per_floor);
+}
+
+static inline int __init doc2001_init(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+
+ this->read_byte = doc2001_read_byte;
+ this->write_buf = doc2001_writebuf;
+ this->read_buf = doc2001_readbuf;
+
+ ReadDOC(doc->virtadr, ChipID);
+ ReadDOC(doc->virtadr, ChipID);
+ ReadDOC(doc->virtadr, ChipID);
+ if (ReadDOC(doc->virtadr, ChipID) != DOC_ChipID_DocMil) {
+ /* It's not a Millennium; it's one of the newer
+ DiskOnChip 2000 units with a similar ASIC.
+ Treat it like a Millennium, except that it
+ can have multiple chips. */
+ doc2000_count_chips(mtd);
+ mtd->name = "DiskOnChip 2000 (INFTL Model)";
+ doc->late_init = inftl_scan_bbt;
+ return (4 * doc->chips_per_floor);
+ } else {
+ /* Bog-standard Millennium */
+ doc->chips_per_floor = 1;
+ mtd->name = "DiskOnChip Millennium";
+ doc->late_init = nftl_scan_bbt;
+ return 1;
+ }
+}
+
+static inline int __init doc2001plus_init(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
+
+ this->read_byte = doc2001plus_read_byte;
+ this->write_buf = doc2001plus_writebuf;
+ this->read_buf = doc2001plus_readbuf;
+ doc->late_init = inftl_scan_bbt;
+ this->cmd_ctrl = NULL;
+ this->select_chip = doc2001plus_select_chip;
+ this->cmdfunc = doc2001plus_command;
+ this->ecc.hwctl = doc2001plus_enable_hwecc;
+
+ doc->chips_per_floor = 1;
+ mtd->name = "DiskOnChip Millennium Plus";
+
+ return 1;
+}
+
+static int __init doc_probe(unsigned long physadr)
+{
+ unsigned char ChipID;
+ struct mtd_info *mtd;
+ struct nand_chip *nand;
+ struct doc_priv *doc;
+ void __iomem *virtadr;
+ unsigned char save_control;
+ unsigned char tmp, tmpb, tmpc;
+ int reg, len, numchips;
+ int ret = 0;
+
+ if (!request_mem_region(physadr, DOC_IOREMAP_LEN, "DiskOnChip"))
+ return -EBUSY;
+ virtadr = ioremap(physadr, DOC_IOREMAP_LEN);
+ if (!virtadr) {
+ pr_err("Diskonchip ioremap failed: 0x%x bytes at 0x%lx\n",
+ DOC_IOREMAP_LEN, physadr);
+ ret = -EIO;
+ goto error_ioremap;
+ }
+
+ /* It's not possible to cleanly detect the DiskOnChip - the
+ * bootup procedure will put the device into reset mode, and
+ * it's not possible to talk to it without actually writing
+ * to the DOCControl register. So we store the current contents
+ * of the DOCControl register's location, in case we later decide
+ * that it's not a DiskOnChip, and want to put it back how we
+ * found it.
+ */
+ save_control = ReadDOC(virtadr, DOCControl);
+
+ /* Reset the DiskOnChip ASIC */
+ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, virtadr, DOCControl);
+ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, virtadr, DOCControl);
+
+ /* Enable the DiskOnChip ASIC */
+ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, virtadr, DOCControl);
+ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, virtadr, DOCControl);
+
+ ChipID = ReadDOC(virtadr, ChipID);
+
+ switch (ChipID) {
+ case DOC_ChipID_Doc2k:
+ reg = DoC_2k_ECCStatus;
+ break;
+ case DOC_ChipID_DocMil:
+ reg = DoC_ECCConf;
+ break;
+ case DOC_ChipID_DocMilPlus16:
+ case DOC_ChipID_DocMilPlus32:
+ case 0:
+ /* Possible Millennium Plus, need to do more checks */
+ /* Possibly release from power down mode */
+ for (tmp = 0; (tmp < 4); tmp++)
+ ReadDOC(virtadr, Mplus_Power);
+
+ /* Reset the Millennium Plus ASIC */
+ tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT;
+ WriteDOC(tmp, virtadr, Mplus_DOCControl);
+ WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm);
+
+ mdelay(1);
+ /* Enable the Millennium Plus ASIC */
+ tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT;
+ WriteDOC(tmp, virtadr, Mplus_DOCControl);
+ WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm);
+ mdelay(1);
+
+ ChipID = ReadDOC(virtadr, ChipID);
+
+ switch (ChipID) {
+ case DOC_ChipID_DocMilPlus16:
+ reg = DoC_Mplus_Toggle;
+ break;
+ case DOC_ChipID_DocMilPlus32:
+ pr_err("DiskOnChip Millennium Plus 32MB is not supported, ignoring.\n");
+ default:
+ ret = -ENODEV;
+ goto notfound;
+ }
+ break;
+
+ default:
+ ret = -ENODEV;
+ goto notfound;
+ }
+ /* Check the TOGGLE bit in the ECC register */
+ tmp = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
+ tmpb = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
+ tmpc = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
+ if ((tmp == tmpb) || (tmp != tmpc)) {
+ pr_warn("Possible DiskOnChip at 0x%lx failed TOGGLE test, dropping.\n", physadr);
+ ret = -ENODEV;
+ goto notfound;
+ }
+
+ for (mtd = doclist; mtd; mtd = doc->nextdoc) {
+ unsigned char oldval;
+ unsigned char newval;
+ nand = mtd_to_nand(mtd);
+ doc = nand_get_controller_data(nand);
+ /* Use the alias resolution register to determine if this is
+ in fact the same DOC aliased to a new address. If writes
+ to one chip's alias resolution register change the value on
+ the other chip, they're the same chip. */
+ if (ChipID == DOC_ChipID_DocMilPlus16) {
+ oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution);
+ newval = ReadDOC(virtadr, Mplus_AliasResolution);
+ } else {
+ oldval = ReadDOC(doc->virtadr, AliasResolution);
+ newval = ReadDOC(virtadr, AliasResolution);
+ }
+ if (oldval != newval)
+ continue;
+ if (ChipID == DOC_ChipID_DocMilPlus16) {
+ WriteDOC(~newval, virtadr, Mplus_AliasResolution);
+ oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution);
+ WriteDOC(newval, virtadr, Mplus_AliasResolution); // restore it
+ } else {
+ WriteDOC(~newval, virtadr, AliasResolution);
+ oldval = ReadDOC(doc->virtadr, AliasResolution);
+ WriteDOC(newval, virtadr, AliasResolution); // restore it
+ }
+ newval = ~newval;
+ if (oldval == newval) {
+ pr_debug("Found alias of DOC at 0x%lx to 0x%lx\n",
+ doc->physadr, physadr);
+ goto notfound;
+ }
+ }
+
+ pr_notice("DiskOnChip found at 0x%lx\n", physadr);
+
+ len = sizeof(struct nand_chip) + sizeof(struct doc_priv) +
+ (2 * sizeof(struct nand_bbt_descr));
+ nand = kzalloc(len, GFP_KERNEL);
+ if (!nand) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ mtd = nand_to_mtd(nand);
+ doc = (struct doc_priv *) (nand + 1);
+ nand->bbt_td = (struct nand_bbt_descr *) (doc + 1);
+ nand->bbt_md = nand->bbt_td + 1;
+
+ mtd->owner = THIS_MODULE;
+ mtd_set_ooblayout(mtd, &doc200x_ooblayout_ops);
+
+ nand_set_controller_data(nand, doc);
+ nand->select_chip = doc200x_select_chip;
+ nand->cmd_ctrl = doc200x_hwcontrol;
+ nand->dev_ready = doc200x_dev_ready;
+ nand->waitfunc = doc200x_wait;
+ nand->block_bad = doc200x_block_bad;
+ nand->ecc.hwctl = doc200x_enable_hwecc;
+ nand->ecc.calculate = doc200x_calculate_ecc;
+ nand->ecc.correct = doc200x_correct_data;
+
+ nand->ecc.mode = NAND_ECC_HW_SYNDROME;
+ nand->ecc.size = 512;
+ nand->ecc.bytes = 6;
+ nand->ecc.strength = 2;
+ nand->ecc.options = NAND_ECC_GENERIC_ERASED_CHECK;
+ nand->bbt_options = NAND_BBT_USE_FLASH;
+ /* Skip the automatic BBT scan so we can run it manually */
+ nand->options |= NAND_SKIP_BBTSCAN;
+
+ doc->physadr = physadr;
+ doc->virtadr = virtadr;
+ doc->ChipID = ChipID;
+ doc->curfloor = -1;
+ doc->curchip = -1;
+ doc->mh0_page = -1;
+ doc->mh1_page = -1;
+ doc->nextdoc = doclist;
+
+ if (ChipID == DOC_ChipID_Doc2k)
+ numchips = doc2000_init(mtd);
+ else if (ChipID == DOC_ChipID_DocMilPlus16)
+ numchips = doc2001plus_init(mtd);
+ else
+ numchips = doc2001_init(mtd);
+
+ if ((ret = nand_scan(mtd, numchips)) || (ret = doc->late_init(mtd))) {
+ /* DBB note: i believe nand_release is necessary here, as
+ buffers may have been allocated in nand_base. Check with
+ Thomas. FIX ME! */
+ /* nand_release will call mtd_device_unregister, but we
+ haven't yet added it. This is handled without incident by
+ mtd_device_unregister, as far as I can tell. */
+ nand_release(mtd);
+ kfree(nand);
+ goto fail;
+ }
+
+ /* Success! */
+ doclist = mtd;
+ return 0;
+
+ notfound:
+ /* Put back the contents of the DOCControl register, in case it's not
+ actually a DiskOnChip. */
+ WriteDOC(save_control, virtadr, DOCControl);
+ fail:
+ iounmap(virtadr);
+
+error_ioremap:
+ release_mem_region(physadr, DOC_IOREMAP_LEN);
+
+ return ret;
+}
+
+static void release_nanddoc(void)
+{
+ struct mtd_info *mtd, *nextmtd;
+ struct nand_chip *nand;
+ struct doc_priv *doc;
+
+ for (mtd = doclist; mtd; mtd = nextmtd) {
+ nand = mtd_to_nand(mtd);
+ doc = nand_get_controller_data(nand);
+
+ nextmtd = doc->nextdoc;
+ nand_release(mtd);
+ iounmap(doc->virtadr);
+ release_mem_region(doc->physadr, DOC_IOREMAP_LEN);
+ kfree(nand);
+ }
+}
+
+static int __init init_nanddoc(void)
+{
+ int i, ret = 0;
+
+ /* We could create the decoder on demand, if memory is a concern.
+ * This way we have it handy, if an error happens
+ *
+ * Symbolsize is 10 (bits)
+ * Primitve polynomial is x^10+x^3+1
+ * first consecutive root is 510
+ * primitve element to generate roots = 1
+ * generator polinomial degree = 4
+ */
+ rs_decoder = init_rs(10, 0x409, FCR, 1, NROOTS);
+ if (!rs_decoder) {
+ pr_err("DiskOnChip: Could not create a RS decoder\n");
+ return -ENOMEM;
+ }
+
+ if (doc_config_location) {
+ pr_info("Using configured DiskOnChip probe address 0x%lx\n",
+ doc_config_location);
+ ret = doc_probe(doc_config_location);
+ if (ret < 0)
+ goto outerr;
+ } else {
+ for (i = 0; (doc_locations[i] != 0xffffffff); i++) {
+ doc_probe(doc_locations[i]);
+ }
+ }
+ /* No banner message any more. Print a message if no DiskOnChip
+ found, so the user knows we at least tried. */
+ if (!doclist) {
+ pr_info("No valid DiskOnChip devices found\n");
+ ret = -ENODEV;
+ goto outerr;
+ }
+ return 0;
+ outerr:
+ free_rs(rs_decoder);
+ return ret;
+}
+
+static void __exit cleanup_nanddoc(void)
+{
+ /* Cleanup the nand/DoC resources */
+ release_nanddoc();
+
+ /* Free the reed solomon resources */
+ if (rs_decoder) {
+ free_rs(rs_decoder);
+ }
+}
+
+module_init(init_nanddoc);
+module_exit(cleanup_nanddoc);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
+MODULE_DESCRIPTION("M-Systems DiskOnChip 2000, Millennium and Millennium Plus device driver");
diff --git a/drivers/mtd/nand/raw/docg4.c b/drivers/mtd/nand/raw/docg4.c
new file mode 100644
index 000000000000..1314aa99b9ab
--- /dev/null
+++ b/drivers/mtd/nand/raw/docg4.c
@@ -0,0 +1,1421 @@
+/*
+ * Copyright © 2012 Mike Dunn <mikedunn@newsguy.com>
+ *
+ * mtd nand driver for M-Systems DiskOnChip G4
+ *
+ * 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.
+ *
+ * Tested on the Palm Treo 680. The G4 is also present on Toshiba Portege, Asus
+ * P526, some HTC smartphones (Wizard, Prophet, ...), O2 XDA Zinc, maybe others.
+ * Should work on these as well. Let me know!
+ *
+ * TODO:
+ *
+ * Mechanism for management of password-protected areas
+ *
+ * Hamming ecc when reading oob only
+ *
+ * According to the M-Sys documentation, this device is also available in a
+ * "dual-die" configuration having a 256MB capacity, but no mechanism for
+ * detecting this variant is documented. Currently this driver assumes 128MB
+ * capacity.
+ *
+ * Support for multiple cascaded devices ("floors"). Not sure which gadgets
+ * contain multiple G4s in a cascaded configuration, if any.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/bitops.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/bch.h>
+#include <linux/bitrev.h>
+#include <linux/jiffies.h>
+
+/*
+ * In "reliable mode" consecutive 2k pages are used in parallel (in some
+ * fashion) to store the same data. The data can be read back from the
+ * even-numbered pages in the normal manner; odd-numbered pages will appear to
+ * contain junk. Systems that boot from the docg4 typically write the secondary
+ * program loader (SPL) code in this mode. The SPL is loaded by the initial
+ * program loader (IPL, stored in the docg4's 2k NOR-like region that is mapped
+ * to the reset vector address). This module parameter enables you to use this
+ * driver to write the SPL. When in this mode, no more than 2k of data can be
+ * written at a time, because the addresses do not increment in the normal
+ * manner, and the starting offset must be within an even-numbered 2k region;
+ * i.e., invalid starting offsets are 0x800, 0xa00, 0xc00, 0xe00, 0x1800,
+ * 0x1a00, ... Reliable mode is a special case and should not be used unless
+ * you know what you're doing.
+ */
+static bool reliable_mode;
+module_param(reliable_mode, bool, 0);
+MODULE_PARM_DESC(reliable_mode, "pages are programmed in reliable mode");
+
+/*
+ * You'll want to ignore badblocks if you're reading a partition that contains
+ * data written by the TrueFFS library (i.e., by PalmOS, Windows, etc), since
+ * it does not use mtd nand's method for marking bad blocks (using oob area).
+ * This will also skip the check of the "page written" flag.
+ */
+static bool ignore_badblocks;
+module_param(ignore_badblocks, bool, 0);
+MODULE_PARM_DESC(ignore_badblocks, "no badblock checking performed");
+
+struct docg4_priv {
+ struct mtd_info *mtd;
+ struct device *dev;
+ void __iomem *virtadr;
+ int status;
+ struct {
+ unsigned int command;
+ int column;
+ int page;
+ } last_command;
+ uint8_t oob_buf[16];
+ uint8_t ecc_buf[7];
+ int oob_page;
+ struct bch_control *bch;
+};
+
+/*
+ * Defines prefixed with DOCG4 are unique to the diskonchip G4. All others are
+ * shared with other diskonchip devices (P3, G3 at least).
+ *
+ * Functions with names prefixed with docg4_ are mtd / nand interface functions
+ * (though they may also be called internally). All others are internal.
+ */
+
+#define DOC_IOSPACE_DATA 0x0800
+
+/* register offsets */
+#define DOC_CHIPID 0x1000
+#define DOC_DEVICESELECT 0x100a
+#define DOC_ASICMODE 0x100c
+#define DOC_DATAEND 0x101e
+#define DOC_NOP 0x103e
+
+#define DOC_FLASHSEQUENCE 0x1032
+#define DOC_FLASHCOMMAND 0x1034
+#define DOC_FLASHADDRESS 0x1036
+#define DOC_FLASHCONTROL 0x1038
+#define DOC_ECCCONF0 0x1040
+#define DOC_ECCCONF1 0x1042
+#define DOC_HAMMINGPARITY 0x1046
+#define DOC_BCH_SYNDROM(idx) (0x1048 + idx)
+
+#define DOC_ASICMODECONFIRM 0x1072
+#define DOC_CHIPID_INV 0x1074
+#define DOC_POWERMODE 0x107c
+
+#define DOCG4_MYSTERY_REG 0x1050
+
+/* apparently used only to write oob bytes 6 and 7 */
+#define DOCG4_OOB_6_7 0x1052
+
+/* DOC_FLASHSEQUENCE register commands */
+#define DOC_SEQ_RESET 0x00
+#define DOCG4_SEQ_PAGE_READ 0x03
+#define DOCG4_SEQ_FLUSH 0x29
+#define DOCG4_SEQ_PAGEWRITE 0x16
+#define DOCG4_SEQ_PAGEPROG 0x1e
+#define DOCG4_SEQ_BLOCKERASE 0x24
+#define DOCG4_SEQ_SETMODE 0x45
+
+/* DOC_FLASHCOMMAND register commands */
+#define DOCG4_CMD_PAGE_READ 0x00
+#define DOC_CMD_ERASECYCLE2 0xd0
+#define DOCG4_CMD_FLUSH 0x70
+#define DOCG4_CMD_READ2 0x30
+#define DOC_CMD_PROG_BLOCK_ADDR 0x60
+#define DOCG4_CMD_PAGEWRITE 0x80
+#define DOC_CMD_PROG_CYCLE2 0x10
+#define DOCG4_CMD_FAST_MODE 0xa3 /* functionality guessed */
+#define DOC_CMD_RELIABLE_MODE 0x22
+#define DOC_CMD_RESET 0xff
+
+/* DOC_POWERMODE register bits */
+#define DOC_POWERDOWN_READY 0x80
+
+/* DOC_FLASHCONTROL register bits */
+#define DOC_CTRL_CE 0x10
+#define DOC_CTRL_UNKNOWN 0x40
+#define DOC_CTRL_FLASHREADY 0x01
+
+/* DOC_ECCCONF0 register bits */
+#define DOC_ECCCONF0_READ_MODE 0x8000
+#define DOC_ECCCONF0_UNKNOWN 0x2000
+#define DOC_ECCCONF0_ECC_ENABLE 0x1000
+#define DOC_ECCCONF0_DATA_BYTES_MASK 0x07ff
+
+/* DOC_ECCCONF1 register bits */
+#define DOC_ECCCONF1_BCH_SYNDROM_ERR 0x80
+#define DOC_ECCCONF1_ECC_ENABLE 0x07
+#define DOC_ECCCONF1_PAGE_IS_WRITTEN 0x20
+
+/* DOC_ASICMODE register bits */
+#define DOC_ASICMODE_RESET 0x00
+#define DOC_ASICMODE_NORMAL 0x01
+#define DOC_ASICMODE_POWERDOWN 0x02
+#define DOC_ASICMODE_MDWREN 0x04
+#define DOC_ASICMODE_BDETCT_RESET 0x08
+#define DOC_ASICMODE_RSTIN_RESET 0x10
+#define DOC_ASICMODE_RAM_WE 0x20
+
+/* good status values read after read/write/erase operations */
+#define DOCG4_PROGSTATUS_GOOD 0x51
+#define DOCG4_PROGSTATUS_GOOD_2 0xe0
+
+/*
+ * On read operations (page and oob-only), the first byte read from I/O reg is a
+ * status. On error, it reads 0x73; otherwise, it reads either 0x71 (first read
+ * after reset only) or 0x51, so bit 1 is presumed to be an error indicator.
+ */
+#define DOCG4_READ_ERROR 0x02 /* bit 1 indicates read error */
+
+/* anatomy of the device */
+#define DOCG4_CHIP_SIZE 0x8000000
+#define DOCG4_PAGE_SIZE 0x200
+#define DOCG4_PAGES_PER_BLOCK 0x200
+#define DOCG4_BLOCK_SIZE (DOCG4_PAGES_PER_BLOCK * DOCG4_PAGE_SIZE)
+#define DOCG4_NUMBLOCKS (DOCG4_CHIP_SIZE / DOCG4_BLOCK_SIZE)
+#define DOCG4_OOB_SIZE 0x10
+#define DOCG4_CHIP_SHIFT 27 /* log_2(DOCG4_CHIP_SIZE) */
+#define DOCG4_PAGE_SHIFT 9 /* log_2(DOCG4_PAGE_SIZE) */
+#define DOCG4_ERASE_SHIFT 18 /* log_2(DOCG4_BLOCK_SIZE) */
+
+/* all but the last byte is included in ecc calculation */
+#define DOCG4_BCH_SIZE (DOCG4_PAGE_SIZE + DOCG4_OOB_SIZE - 1)
+
+#define DOCG4_USERDATA_LEN 520 /* 512 byte page plus 8 oob avail to user */
+
+/* expected values from the ID registers */
+#define DOCG4_IDREG1_VALUE 0x0400
+#define DOCG4_IDREG2_VALUE 0xfbff
+
+/* primitive polynomial used to build the Galois field used by hw ecc gen */
+#define DOCG4_PRIMITIVE_POLY 0x4443
+
+#define DOCG4_M 14 /* Galois field is of order 2^14 */
+#define DOCG4_T 4 /* BCH alg corrects up to 4 bit errors */
+
+#define DOCG4_FACTORY_BBT_PAGE 16 /* page where read-only factory bbt lives */
+#define DOCG4_REDUNDANT_BBT_PAGE 24 /* page where redundant factory bbt lives */
+
+/*
+ * Bytes 0, 1 are used as badblock marker.
+ * Bytes 2 - 6 are available to the user.
+ * Byte 7 is hamming ecc for first 7 oob bytes only.
+ * Bytes 8 - 14 are hw-generated ecc covering entire page + oob bytes 0 - 14.
+ * Byte 15 (the last) is used by the driver as a "page written" flag.
+ */
+static int docg4_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ if (section)
+ return -ERANGE;
+
+ oobregion->offset = 7;
+ oobregion->length = 9;
+
+ return 0;
+}
+
+static int docg4_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ if (section)
+ return -ERANGE;
+
+ oobregion->offset = 2;
+ oobregion->length = 5;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops docg4_ooblayout_ops = {
+ .ecc = docg4_ooblayout_ecc,
+ .free = docg4_ooblayout_free,
+};
+
+/*
+ * The device has a nop register which M-Sys claims is for the purpose of
+ * inserting precise delays. But beware; at least some operations fail if the
+ * nop writes are replaced with a generic delay!
+ */
+static inline void write_nop(void __iomem *docptr)
+{
+ writew(0, docptr + DOC_NOP);
+}
+
+static void docg4_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ int i;
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ uint16_t *p = (uint16_t *) buf;
+ len >>= 1;
+
+ for (i = 0; i < len; i++)
+ p[i] = readw(nand->IO_ADDR_R);
+}
+
+static void docg4_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ int i;
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ uint16_t *p = (uint16_t *) buf;
+ len >>= 1;
+
+ for (i = 0; i < len; i++)
+ writew(p[i], nand->IO_ADDR_W);
+}
+
+static int poll_status(struct docg4_priv *doc)
+{
+ /*
+ * Busy-wait for the FLASHREADY bit to be set in the FLASHCONTROL
+ * register. Operations known to take a long time (e.g., block erase)
+ * should sleep for a while before calling this.
+ */
+
+ uint16_t flash_status;
+ unsigned long timeo;
+ void __iomem *docptr = doc->virtadr;
+
+ dev_dbg(doc->dev, "%s...\n", __func__);
+
+ /* hardware quirk requires reading twice initially */
+ flash_status = readw(docptr + DOC_FLASHCONTROL);
+
+ timeo = jiffies + msecs_to_jiffies(200); /* generous timeout */
+ do {
+ cpu_relax();
+ flash_status = readb(docptr + DOC_FLASHCONTROL);
+ } while (!(flash_status & DOC_CTRL_FLASHREADY) &&
+ time_before(jiffies, timeo));
+
+ if (unlikely(!(flash_status & DOC_CTRL_FLASHREADY))) {
+ dev_err(doc->dev, "%s: timed out!\n", __func__);
+ return NAND_STATUS_FAIL;
+ }
+
+ return 0;
+}
+
+
+static int docg4_wait(struct mtd_info *mtd, struct nand_chip *nand)
+{
+
+ struct docg4_priv *doc = nand_get_controller_data(nand);
+ int status = NAND_STATUS_WP; /* inverse logic?? */
+ dev_dbg(doc->dev, "%s...\n", __func__);
+
+ /* report any previously unreported error */
+ if (doc->status) {
+ status |= doc->status;
+ doc->status = 0;
+ return status;
+ }
+
+ status |= poll_status(doc);
+ return status;
+}
+
+static void docg4_select_chip(struct mtd_info *mtd, int chip)
+{
+ /*
+ * Select among multiple cascaded chips ("floors"). Multiple floors are
+ * not yet supported, so the only valid non-negative value is 0.
+ */
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct docg4_priv *doc = nand_get_controller_data(nand);
+ void __iomem *docptr = doc->virtadr;
+
+ dev_dbg(doc->dev, "%s: chip %d\n", __func__, chip);
+
+ if (chip < 0)
+ return; /* deselected */
+
+ if (chip > 0)
+ dev_warn(doc->dev, "multiple floors currently unsupported\n");
+
+ writew(0, docptr + DOC_DEVICESELECT);
+}
+
+static void reset(struct mtd_info *mtd)
+{
+ /* full device reset */
+
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct docg4_priv *doc = nand_get_controller_data(nand);
+ void __iomem *docptr = doc->virtadr;
+
+ writew(DOC_ASICMODE_RESET | DOC_ASICMODE_MDWREN,
+ docptr + DOC_ASICMODE);
+ writew(~(DOC_ASICMODE_RESET | DOC_ASICMODE_MDWREN),
+ docptr + DOC_ASICMODECONFIRM);
+ write_nop(docptr);
+
+ writew(DOC_ASICMODE_NORMAL | DOC_ASICMODE_MDWREN,
+ docptr + DOC_ASICMODE);
+ writew(~(DOC_ASICMODE_NORMAL | DOC_ASICMODE_MDWREN),
+ docptr + DOC_ASICMODECONFIRM);
+
+ writew(DOC_ECCCONF1_ECC_ENABLE, docptr + DOC_ECCCONF1);
+
+ poll_status(doc);
+}
+
+static void read_hw_ecc(void __iomem *docptr, uint8_t *ecc_buf)
+{
+ /* read the 7 hw-generated ecc bytes */
+
+ int i;
+ for (i = 0; i < 7; i++) { /* hw quirk; read twice */
+ ecc_buf[i] = readb(docptr + DOC_BCH_SYNDROM(i));
+ ecc_buf[i] = readb(docptr + DOC_BCH_SYNDROM(i));
+ }
+}
+
+static int correct_data(struct mtd_info *mtd, uint8_t *buf, int page)
+{
+ /*
+ * Called after a page read when hardware reports bitflips.
+ * Up to four bitflips can be corrected.
+ */
+
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct docg4_priv *doc = nand_get_controller_data(nand);
+ void __iomem *docptr = doc->virtadr;
+ int i, numerrs, errpos[4];
+ const uint8_t blank_read_hwecc[8] = {
+ 0xcf, 0x72, 0xfc, 0x1b, 0xa9, 0xc7, 0xb9, 0 };
+
+ read_hw_ecc(docptr, doc->ecc_buf); /* read 7 hw-generated ecc bytes */
+
+ /* check if read error is due to a blank page */
+ if (!memcmp(doc->ecc_buf, blank_read_hwecc, 7))
+ return 0; /* yes */
+
+ /* skip additional check of "written flag" if ignore_badblocks */
+ if (ignore_badblocks == false) {
+
+ /*
+ * If the hw ecc bytes are not those of a blank page, there's
+ * still a chance that the page is blank, but was read with
+ * errors. Check the "written flag" in last oob byte, which
+ * is set to zero when a page is written. If more than half
+ * the bits are set, assume a blank page. Unfortunately, the
+ * bit flips(s) are not reported in stats.
+ */
+
+ if (nand->oob_poi[15]) {
+ int bit, numsetbits = 0;
+ unsigned long written_flag = nand->oob_poi[15];
+ for_each_set_bit(bit, &written_flag, 8)
+ numsetbits++;
+ if (numsetbits > 4) { /* assume blank */
+ dev_warn(doc->dev,
+ "error(s) in blank page "
+ "at offset %08x\n",
+ page * DOCG4_PAGE_SIZE);
+ return 0;
+ }
+ }
+ }
+
+ /*
+ * The hardware ecc unit produces oob_ecc ^ calc_ecc. The kernel's bch
+ * algorithm is used to decode this. However the hw operates on page
+ * data in a bit order that is the reverse of that of the bch alg,
+ * requiring that the bits be reversed on the result. Thanks to Ivan
+ * Djelic for his analysis!
+ */
+ for (i = 0; i < 7; i++)
+ doc->ecc_buf[i] = bitrev8(doc->ecc_buf[i]);
+
+ numerrs = decode_bch(doc->bch, NULL, DOCG4_USERDATA_LEN, NULL,
+ doc->ecc_buf, NULL, errpos);
+
+ if (numerrs == -EBADMSG) {
+ dev_warn(doc->dev, "uncorrectable errors at offset %08x\n",
+ page * DOCG4_PAGE_SIZE);
+ return -EBADMSG;
+ }
+
+ BUG_ON(numerrs < 0); /* -EINVAL, or anything other than -EBADMSG */
+
+ /* undo last step in BCH alg (modulo mirroring not needed) */
+ for (i = 0; i < numerrs; i++)
+ errpos[i] = (errpos[i] & ~7)|(7-(errpos[i] & 7));
+
+ /* fix the errors */
+ for (i = 0; i < numerrs; i++) {
+
+ /* ignore if error within oob ecc bytes */
+ if (errpos[i] > DOCG4_USERDATA_LEN * 8)
+ continue;
+
+ /* if error within oob area preceeding ecc bytes... */
+ if (errpos[i] > DOCG4_PAGE_SIZE * 8)
+ change_bit(errpos[i] - DOCG4_PAGE_SIZE * 8,
+ (unsigned long *)nand->oob_poi);
+
+ else /* error in page data */
+ change_bit(errpos[i], (unsigned long *)buf);
+ }
+
+ dev_notice(doc->dev, "%d error(s) corrected at offset %08x\n",
+ numerrs, page * DOCG4_PAGE_SIZE);
+
+ return numerrs;
+}
+
+static uint8_t docg4_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct docg4_priv *doc = nand_get_controller_data(nand);
+
+ dev_dbg(doc->dev, "%s\n", __func__);
+
+ if (doc->last_command.command == NAND_CMD_STATUS) {
+ int status;
+
+ /*
+ * Previous nand command was status request, so nand
+ * infrastructure code expects to read the status here. If an
+ * error occurred in a previous operation, report it.
+ */
+ doc->last_command.command = 0;
+
+ if (doc->status) {
+ status = doc->status;
+ doc->status = 0;
+ }
+
+ /* why is NAND_STATUS_WP inverse logic?? */
+ else
+ status = NAND_STATUS_WP | NAND_STATUS_READY;
+
+ return status;
+ }
+
+ dev_warn(doc->dev, "unexpected call to read_byte()\n");
+
+ return 0;
+}
+
+static void write_addr(struct docg4_priv *doc, uint32_t docg4_addr)
+{
+ /* write the four address bytes packed in docg4_addr to the device */
+
+ void __iomem *docptr = doc->virtadr;
+ writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS);
+ docg4_addr >>= 8;
+ writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS);
+ docg4_addr >>= 8;
+ writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS);
+ docg4_addr >>= 8;
+ writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS);
+}
+
+static int read_progstatus(struct docg4_priv *doc)
+{
+ /*
+ * This apparently checks the status of programming. Done after an
+ * erasure, and after page data is written. On error, the status is
+ * saved, to be later retrieved by the nand infrastructure code.
+ */
+ void __iomem *docptr = doc->virtadr;
+
+ /* status is read from the I/O reg */
+ uint16_t status1 = readw(docptr + DOC_IOSPACE_DATA);
+ uint16_t status2 = readw(docptr + DOC_IOSPACE_DATA);
+ uint16_t status3 = readw(docptr + DOCG4_MYSTERY_REG);
+
+ dev_dbg(doc->dev, "docg4: %s: %02x %02x %02x\n",
+ __func__, status1, status2, status3);
+
+ if (status1 != DOCG4_PROGSTATUS_GOOD
+ || status2 != DOCG4_PROGSTATUS_GOOD_2
+ || status3 != DOCG4_PROGSTATUS_GOOD_2) {
+ doc->status = NAND_STATUS_FAIL;
+ dev_warn(doc->dev, "read_progstatus failed: "
+ "%02x, %02x, %02x\n", status1, status2, status3);
+ return -EIO;
+ }
+ return 0;
+}
+
+static int pageprog(struct mtd_info *mtd)
+{
+ /*
+ * Final step in writing a page. Writes the contents of its
+ * internal buffer out to the flash array, or some such.
+ */
+
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct docg4_priv *doc = nand_get_controller_data(nand);
+ void __iomem *docptr = doc->virtadr;
+ int retval = 0;
+
+ dev_dbg(doc->dev, "docg4: %s\n", __func__);
+
+ writew(DOCG4_SEQ_PAGEPROG, docptr + DOC_FLASHSEQUENCE);
+ writew(DOC_CMD_PROG_CYCLE2, docptr + DOC_FLASHCOMMAND);
+ write_nop(docptr);
+ write_nop(docptr);
+
+ /* Just busy-wait; usleep_range() slows things down noticeably. */
+ poll_status(doc);
+
+ writew(DOCG4_SEQ_FLUSH, docptr + DOC_FLASHSEQUENCE);
+ writew(DOCG4_CMD_FLUSH, docptr + DOC_FLASHCOMMAND);
+ writew(DOC_ECCCONF0_READ_MODE | 4, docptr + DOC_ECCCONF0);
+ write_nop(docptr);
+ write_nop(docptr);
+ write_nop(docptr);
+ write_nop(docptr);
+ write_nop(docptr);
+
+ retval = read_progstatus(doc);
+ writew(0, docptr + DOC_DATAEND);
+ write_nop(docptr);
+ poll_status(doc);
+ write_nop(docptr);
+
+ return retval;
+}
+
+static void sequence_reset(struct mtd_info *mtd)
+{
+ /* common starting sequence for all operations */
+
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct docg4_priv *doc = nand_get_controller_data(nand);
+ void __iomem *docptr = doc->virtadr;
+
+ writew(DOC_CTRL_UNKNOWN | DOC_CTRL_CE, docptr + DOC_FLASHCONTROL);
+ writew(DOC_SEQ_RESET, docptr + DOC_FLASHSEQUENCE);
+ writew(DOC_CMD_RESET, docptr + DOC_FLASHCOMMAND);
+ write_nop(docptr);
+ write_nop(docptr);
+ poll_status(doc);
+ write_nop(docptr);
+}
+
+static void read_page_prologue(struct mtd_info *mtd, uint32_t docg4_addr)
+{
+ /* first step in reading a page */
+
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct docg4_priv *doc = nand_get_controller_data(nand);
+ void __iomem *docptr = doc->virtadr;
+
+ dev_dbg(doc->dev,
+ "docg4: %s: g4 page %08x\n", __func__, docg4_addr);
+
+ sequence_reset(mtd);
+
+ writew(DOCG4_SEQ_PAGE_READ, docptr + DOC_FLASHSEQUENCE);
+ writew(DOCG4_CMD_PAGE_READ, docptr + DOC_FLASHCOMMAND);
+ write_nop(docptr);
+
+ write_addr(doc, docg4_addr);
+
+ write_nop(docptr);
+ writew(DOCG4_CMD_READ2, docptr + DOC_FLASHCOMMAND);
+ write_nop(docptr);
+ write_nop(docptr);
+
+ poll_status(doc);
+}
+
+static void write_page_prologue(struct mtd_info *mtd, uint32_t docg4_addr)
+{
+ /* first step in writing a page */
+
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct docg4_priv *doc = nand_get_controller_data(nand);
+ void __iomem *docptr = doc->virtadr;
+
+ dev_dbg(doc->dev,
+ "docg4: %s: g4 addr: %x\n", __func__, docg4_addr);
+ sequence_reset(mtd);
+
+ if (unlikely(reliable_mode)) {
+ writew(DOCG4_SEQ_SETMODE, docptr + DOC_FLASHSEQUENCE);
+ writew(DOCG4_CMD_FAST_MODE, docptr + DOC_FLASHCOMMAND);
+ writew(DOC_CMD_RELIABLE_MODE, docptr + DOC_FLASHCOMMAND);
+ write_nop(docptr);
+ }
+
+ writew(DOCG4_SEQ_PAGEWRITE, docptr + DOC_FLASHSEQUENCE);
+ writew(DOCG4_CMD_PAGEWRITE, docptr + DOC_FLASHCOMMAND);
+ write_nop(docptr);
+ write_addr(doc, docg4_addr);
+ write_nop(docptr);
+ write_nop(docptr);
+ poll_status(doc);
+}
+
+static uint32_t mtd_to_docg4_address(int page, int column)
+{
+ /*
+ * Convert mtd address to format used by the device, 32 bit packed.
+ *
+ * Some notes on G4 addressing... The M-Sys documentation on this device
+ * claims that pages are 2K in length, and indeed, the format of the
+ * address used by the device reflects that. But within each page are
+ * four 512 byte "sub-pages", each with its own oob data that is
+ * read/written immediately after the 512 bytes of page data. This oob
+ * data contains the ecc bytes for the preceeding 512 bytes.
+ *
+ * Rather than tell the mtd nand infrastructure that page size is 2k,
+ * with four sub-pages each, we engage in a little subterfuge and tell
+ * the infrastructure code that pages are 512 bytes in size. This is
+ * done because during the course of reverse-engineering the device, I
+ * never observed an instance where an entire 2K "page" was read or
+ * written as a unit. Each "sub-page" is always addressed individually,
+ * its data read/written, and ecc handled before the next "sub-page" is
+ * addressed.
+ *
+ * This requires us to convert addresses passed by the mtd nand
+ * infrastructure code to those used by the device.
+ *
+ * The address that is written to the device consists of four bytes: the
+ * first two are the 2k page number, and the second is the index into
+ * the page. The index is in terms of 16-bit half-words and includes
+ * the preceeding oob data, so e.g., the index into the second
+ * "sub-page" is 0x108, and the full device address of the start of mtd
+ * page 0x201 is 0x00800108.
+ */
+ int g4_page = page / 4; /* device's 2K page */
+ int g4_index = (page % 4) * 0x108 + column/2; /* offset into page */
+ return (g4_page << 16) | g4_index; /* pack */
+}
+
+static void docg4_command(struct mtd_info *mtd, unsigned command, int column,
+ int page_addr)
+{
+ /* handle standard nand commands */
+
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct docg4_priv *doc = nand_get_controller_data(nand);
+ uint32_t g4_addr = mtd_to_docg4_address(page_addr, column);
+
+ dev_dbg(doc->dev, "%s %x, page_addr=%x, column=%x\n",
+ __func__, command, page_addr, column);
+
+ /*
+ * Save the command and its arguments. This enables emulation of
+ * standard flash devices, and also some optimizations.
+ */
+ doc->last_command.command = command;
+ doc->last_command.column = column;
+ doc->last_command.page = page_addr;
+
+ switch (command) {
+
+ case NAND_CMD_RESET:
+ reset(mtd);
+ break;
+
+ case NAND_CMD_READ0:
+ read_page_prologue(mtd, g4_addr);
+ break;
+
+ case NAND_CMD_STATUS:
+ /* next call to read_byte() will expect a status */
+ break;
+
+ case NAND_CMD_SEQIN:
+ if (unlikely(reliable_mode)) {
+ uint16_t g4_page = g4_addr >> 16;
+
+ /* writes to odd-numbered 2k pages are invalid */
+ if (g4_page & 0x01)
+ dev_warn(doc->dev,
+ "invalid reliable mode address\n");
+ }
+
+ write_page_prologue(mtd, g4_addr);
+
+ /* hack for deferred write of oob bytes */
+ if (doc->oob_page == page_addr)
+ memcpy(nand->oob_poi, doc->oob_buf, 16);
+ break;
+
+ case NAND_CMD_PAGEPROG:
+ pageprog(mtd);
+ break;
+
+ /* we don't expect these, based on review of nand_base.c */
+ case NAND_CMD_READOOB:
+ case NAND_CMD_READID:
+ case NAND_CMD_ERASE1:
+ case NAND_CMD_ERASE2:
+ dev_warn(doc->dev, "docg4_command: "
+ "unexpected nand command 0x%x\n", command);
+ break;
+
+ }
+}
+
+static int read_page(struct mtd_info *mtd, struct nand_chip *nand,
+ uint8_t *buf, int page, bool use_ecc)
+{
+ struct docg4_priv *doc = nand_get_controller_data(nand);
+ void __iomem *docptr = doc->virtadr;
+ uint16_t status, edc_err, *buf16;
+ int bits_corrected = 0;
+
+ dev_dbg(doc->dev, "%s: page %08x\n", __func__, page);
+
+ nand_read_page_op(nand, page, 0, NULL, 0);
+
+ writew(DOC_ECCCONF0_READ_MODE |
+ DOC_ECCCONF0_ECC_ENABLE |
+ DOC_ECCCONF0_UNKNOWN |
+ DOCG4_BCH_SIZE,
+ docptr + DOC_ECCCONF0);
+ write_nop(docptr);
+ write_nop(docptr);
+ write_nop(docptr);
+ write_nop(docptr);
+ write_nop(docptr);
+
+ /* the 1st byte from the I/O reg is a status; the rest is page data */
+ status = readw(docptr + DOC_IOSPACE_DATA);
+ if (status & DOCG4_READ_ERROR) {
+ dev_err(doc->dev,
+ "docg4_read_page: bad status: 0x%02x\n", status);
+ writew(0, docptr + DOC_DATAEND);
+ return -EIO;
+ }
+
+ dev_dbg(doc->dev, "%s: status = 0x%x\n", __func__, status);
+
+ docg4_read_buf(mtd, buf, DOCG4_PAGE_SIZE); /* read the page data */
+
+ /* this device always reads oob after page data */
+ /* first 14 oob bytes read from I/O reg */
+ docg4_read_buf(mtd, nand->oob_poi, 14);
+
+ /* last 2 read from another reg */
+ buf16 = (uint16_t *)(nand->oob_poi + 14);
+ *buf16 = readw(docptr + DOCG4_MYSTERY_REG);
+
+ write_nop(docptr);
+
+ if (likely(use_ecc == true)) {
+
+ /* read the register that tells us if bitflip(s) detected */
+ edc_err = readw(docptr + DOC_ECCCONF1);
+ edc_err = readw(docptr + DOC_ECCCONF1);
+ dev_dbg(doc->dev, "%s: edc_err = 0x%02x\n", __func__, edc_err);
+
+ /* If bitflips are reported, attempt to correct with ecc */
+ if (edc_err & DOC_ECCCONF1_BCH_SYNDROM_ERR) {
+ bits_corrected = correct_data(mtd, buf, page);
+ if (bits_corrected == -EBADMSG)
+ mtd->ecc_stats.failed++;
+ else
+ mtd->ecc_stats.corrected += bits_corrected;
+ }
+ }
+
+ writew(0, docptr + DOC_DATAEND);
+ if (bits_corrected == -EBADMSG) /* uncorrectable errors */
+ return 0;
+ return bits_corrected;
+}
+
+
+static int docg4_read_page_raw(struct mtd_info *mtd, struct nand_chip *nand,
+ uint8_t *buf, int oob_required, int page)
+{
+ return read_page(mtd, nand, buf, page, false);
+}
+
+static int docg4_read_page(struct mtd_info *mtd, struct nand_chip *nand,
+ uint8_t *buf, int oob_required, int page)
+{
+ return read_page(mtd, nand, buf, page, true);
+}
+
+static int docg4_read_oob(struct mtd_info *mtd, struct nand_chip *nand,
+ int page)
+{
+ struct docg4_priv *doc = nand_get_controller_data(nand);
+ void __iomem *docptr = doc->virtadr;
+ uint16_t status;
+
+ dev_dbg(doc->dev, "%s: page %x\n", __func__, page);
+
+ nand_read_page_op(nand, page, nand->ecc.size, NULL, 0);
+
+ writew(DOC_ECCCONF0_READ_MODE | DOCG4_OOB_SIZE, docptr + DOC_ECCCONF0);
+ write_nop(docptr);
+ write_nop(docptr);
+ write_nop(docptr);
+ write_nop(docptr);
+ write_nop(docptr);
+
+ /* the 1st byte from the I/O reg is a status; the rest is oob data */
+ status = readw(docptr + DOC_IOSPACE_DATA);
+ if (status & DOCG4_READ_ERROR) {
+ dev_warn(doc->dev,
+ "docg4_read_oob failed: status = 0x%02x\n", status);
+ return -EIO;
+ }
+
+ dev_dbg(doc->dev, "%s: status = 0x%x\n", __func__, status);
+
+ docg4_read_buf(mtd, nand->oob_poi, 16);
+
+ write_nop(docptr);
+ write_nop(docptr);
+ write_nop(docptr);
+ writew(0, docptr + DOC_DATAEND);
+ write_nop(docptr);
+
+ return 0;
+}
+
+static int docg4_erase_block(struct mtd_info *mtd, int page)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct docg4_priv *doc = nand_get_controller_data(nand);
+ void __iomem *docptr = doc->virtadr;
+ uint16_t g4_page;
+ int status;
+
+ dev_dbg(doc->dev, "%s: page %04x\n", __func__, page);
+
+ sequence_reset(mtd);
+
+ writew(DOCG4_SEQ_BLOCKERASE, docptr + DOC_FLASHSEQUENCE);
+ writew(DOC_CMD_PROG_BLOCK_ADDR, docptr + DOC_FLASHCOMMAND);
+ write_nop(docptr);
+
+ /* only 2 bytes of address are written to specify erase block */
+ g4_page = (uint16_t)(page / 4); /* to g4's 2k page addressing */
+ writeb(g4_page & 0xff, docptr + DOC_FLASHADDRESS);
+ g4_page >>= 8;
+ writeb(g4_page & 0xff, docptr + DOC_FLASHADDRESS);
+ write_nop(docptr);
+
+ /* start the erasure */
+ writew(DOC_CMD_ERASECYCLE2, docptr + DOC_FLASHCOMMAND);
+ write_nop(docptr);
+ write_nop(docptr);
+
+ usleep_range(500, 1000); /* erasure is long; take a snooze */
+ poll_status(doc);
+ writew(DOCG4_SEQ_FLUSH, docptr + DOC_FLASHSEQUENCE);
+ writew(DOCG4_CMD_FLUSH, docptr + DOC_FLASHCOMMAND);
+ writew(DOC_ECCCONF0_READ_MODE | 4, docptr + DOC_ECCCONF0);
+ write_nop(docptr);
+ write_nop(docptr);
+ write_nop(docptr);
+ write_nop(docptr);
+ write_nop(docptr);
+
+ read_progstatus(doc);
+
+ writew(0, docptr + DOC_DATAEND);
+ write_nop(docptr);
+ poll_status(doc);
+ write_nop(docptr);
+
+ status = nand->waitfunc(mtd, nand);
+ if (status < 0)
+ return status;
+
+ return status & NAND_STATUS_FAIL ? -EIO : 0;
+}
+
+static int write_page(struct mtd_info *mtd, struct nand_chip *nand,
+ const uint8_t *buf, int page, bool use_ecc)
+{
+ struct docg4_priv *doc = nand_get_controller_data(nand);
+ void __iomem *docptr = doc->virtadr;
+ uint8_t ecc_buf[8];
+
+ dev_dbg(doc->dev, "%s...\n", __func__);
+
+ nand_prog_page_begin_op(nand, page, 0, NULL, 0);
+
+ writew(DOC_ECCCONF0_ECC_ENABLE |
+ DOC_ECCCONF0_UNKNOWN |
+ DOCG4_BCH_SIZE,
+ docptr + DOC_ECCCONF0);
+ write_nop(docptr);
+
+ /* write the page data */
+ docg4_write_buf16(mtd, buf, DOCG4_PAGE_SIZE);
+
+ /* oob bytes 0 through 5 are written to I/O reg */
+ docg4_write_buf16(mtd, nand->oob_poi, 6);
+
+ /* oob byte 6 written to a separate reg */
+ writew(nand->oob_poi[6], docptr + DOCG4_OOB_6_7);
+
+ write_nop(docptr);
+ write_nop(docptr);
+
+ /* write hw-generated ecc bytes to oob */
+ if (likely(use_ecc == true)) {
+ /* oob byte 7 is hamming code */
+ uint8_t hamming = readb(docptr + DOC_HAMMINGPARITY);
+ hamming = readb(docptr + DOC_HAMMINGPARITY); /* 2nd read */
+ writew(hamming, docptr + DOCG4_OOB_6_7);
+ write_nop(docptr);
+
+ /* read the 7 bch bytes from ecc regs */
+ read_hw_ecc(docptr, ecc_buf);
+ ecc_buf[7] = 0; /* clear the "page written" flag */
+ }
+
+ /* write user-supplied bytes to oob */
+ else {
+ writew(nand->oob_poi[7], docptr + DOCG4_OOB_6_7);
+ write_nop(docptr);
+ memcpy(ecc_buf, &nand->oob_poi[8], 8);
+ }
+
+ docg4_write_buf16(mtd, ecc_buf, 8);
+ write_nop(docptr);
+ write_nop(docptr);
+ writew(0, docptr + DOC_DATAEND);
+ write_nop(docptr);
+
+ return nand_prog_page_end_op(nand);
+}
+
+static int docg4_write_page_raw(struct mtd_info *mtd, struct nand_chip *nand,
+ const uint8_t *buf, int oob_required, int page)
+{
+ return write_page(mtd, nand, buf, page, false);
+}
+
+static int docg4_write_page(struct mtd_info *mtd, struct nand_chip *nand,
+ const uint8_t *buf, int oob_required, int page)
+{
+ return write_page(mtd, nand, buf, page, true);
+}
+
+static int docg4_write_oob(struct mtd_info *mtd, struct nand_chip *nand,
+ int page)
+{
+ /*
+ * Writing oob-only is not really supported, because MLC nand must write
+ * oob bytes at the same time as page data. Nonetheless, we save the
+ * oob buffer contents here, and then write it along with the page data
+ * if the same page is subsequently written. This allows user space
+ * utilities that write the oob data prior to the page data to work
+ * (e.g., nandwrite). The disdvantage is that, if the intention was to
+ * write oob only, the operation is quietly ignored. Also, oob can get
+ * corrupted if two concurrent processes are running nandwrite.
+ */
+
+ /* note that bytes 7..14 are hw generated hamming/ecc and overwritten */
+ struct docg4_priv *doc = nand_get_controller_data(nand);
+ doc->oob_page = page;
+ memcpy(doc->oob_buf, nand->oob_poi, 16);
+ return 0;
+}
+
+static int __init read_factory_bbt(struct mtd_info *mtd)
+{
+ /*
+ * The device contains a read-only factory bad block table. Read it and
+ * update the memory-based bbt accordingly.
+ */
+
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct docg4_priv *doc = nand_get_controller_data(nand);
+ uint32_t g4_addr = mtd_to_docg4_address(DOCG4_FACTORY_BBT_PAGE, 0);
+ uint8_t *buf;
+ int i, block;
+ __u32 eccfailed_stats = mtd->ecc_stats.failed;
+
+ buf = kzalloc(DOCG4_PAGE_SIZE, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ read_page_prologue(mtd, g4_addr);
+ docg4_read_page(mtd, nand, buf, 0, DOCG4_FACTORY_BBT_PAGE);
+
+ /*
+ * If no memory-based bbt was created, exit. This will happen if module
+ * parameter ignore_badblocks is set. Then why even call this function?
+ * For an unknown reason, block erase always fails if it's the first
+ * operation after device power-up. The above read ensures it never is.
+ * Ugly, I know.
+ */
+ if (nand->bbt == NULL) /* no memory-based bbt */
+ goto exit;
+
+ if (mtd->ecc_stats.failed > eccfailed_stats) {
+ /*
+ * Whoops, an ecc failure ocurred reading the factory bbt.
+ * It is stored redundantly, so we get another chance.
+ */
+ eccfailed_stats = mtd->ecc_stats.failed;
+ docg4_read_page(mtd, nand, buf, 0, DOCG4_REDUNDANT_BBT_PAGE);
+ if (mtd->ecc_stats.failed > eccfailed_stats) {
+ dev_warn(doc->dev,
+ "The factory bbt could not be read!\n");
+ goto exit;
+ }
+ }
+
+ /*
+ * Parse factory bbt and update memory-based bbt. Factory bbt format is
+ * simple: one bit per block, block numbers increase left to right (msb
+ * to lsb). Bit clear means bad block.
+ */
+ for (i = block = 0; block < DOCG4_NUMBLOCKS; block += 8, i++) {
+ int bitnum;
+ unsigned long bits = ~buf[i];
+ for_each_set_bit(bitnum, &bits, 8) {
+ int badblock = block + 7 - bitnum;
+ nand->bbt[badblock / 4] |=
+ 0x03 << ((badblock % 4) * 2);
+ mtd->ecc_stats.badblocks++;
+ dev_notice(doc->dev, "factory-marked bad block: %d\n",
+ badblock);
+ }
+ }
+ exit:
+ kfree(buf);
+ return 0;
+}
+
+static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ /*
+ * Mark a block as bad. Bad blocks are marked in the oob area of the
+ * first page of the block. The default scan_bbt() in the nand
+ * infrastructure code works fine for building the memory-based bbt
+ * during initialization, as does the nand infrastructure function that
+ * checks if a block is bad by reading the bbt. This function replaces
+ * the nand default because writes to oob-only are not supported.
+ */
+
+ int ret, i;
+ uint8_t *buf;
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct docg4_priv *doc = nand_get_controller_data(nand);
+ struct nand_bbt_descr *bbtd = nand->badblock_pattern;
+ int page = (int)(ofs >> nand->page_shift);
+ uint32_t g4_addr = mtd_to_docg4_address(page, 0);
+
+ dev_dbg(doc->dev, "%s: %08llx\n", __func__, ofs);
+
+ if (unlikely(ofs & (DOCG4_BLOCK_SIZE - 1)))
+ dev_warn(doc->dev, "%s: ofs %llx not start of block!\n",
+ __func__, ofs);
+
+ /* allocate blank buffer for page data */
+ buf = kzalloc(DOCG4_PAGE_SIZE, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ /* write bit-wise negation of pattern to oob buffer */
+ memset(nand->oob_poi, 0xff, mtd->oobsize);
+ for (i = 0; i < bbtd->len; i++)
+ nand->oob_poi[bbtd->offs + i] = ~bbtd->pattern[i];
+
+ /* write first page of block */
+ write_page_prologue(mtd, g4_addr);
+ docg4_write_page(mtd, nand, buf, 1, page);
+ ret = pageprog(mtd);
+
+ kfree(buf);
+
+ return ret;
+}
+
+static int docg4_block_neverbad(struct mtd_info *mtd, loff_t ofs)
+{
+ /* only called when module_param ignore_badblocks is set */
+ return 0;
+}
+
+static int docg4_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ /*
+ * Put the device into "deep power-down" mode. Note that CE# must be
+ * deasserted for this to take effect. The xscale, e.g., can be
+ * configured to float this signal when the processor enters power-down,
+ * and a suitable pull-up ensures its deassertion.
+ */
+
+ int i;
+ uint8_t pwr_down;
+ struct docg4_priv *doc = platform_get_drvdata(pdev);
+ void __iomem *docptr = doc->virtadr;
+
+ dev_dbg(doc->dev, "%s...\n", __func__);
+
+ /* poll the register that tells us we're ready to go to sleep */
+ for (i = 0; i < 10; i++) {
+ pwr_down = readb(docptr + DOC_POWERMODE);
+ if (pwr_down & DOC_POWERDOWN_READY)
+ break;
+ usleep_range(1000, 4000);
+ }
+
+ if (pwr_down & DOC_POWERDOWN_READY) {
+ dev_err(doc->dev, "suspend failed; "
+ "timeout polling DOC_POWERDOWN_READY\n");
+ return -EIO;
+ }
+
+ writew(DOC_ASICMODE_POWERDOWN | DOC_ASICMODE_MDWREN,
+ docptr + DOC_ASICMODE);
+ writew(~(DOC_ASICMODE_POWERDOWN | DOC_ASICMODE_MDWREN),
+ docptr + DOC_ASICMODECONFIRM);
+
+ write_nop(docptr);
+
+ return 0;
+}
+
+static int docg4_resume(struct platform_device *pdev)
+{
+
+ /*
+ * Exit power-down. Twelve consecutive reads of the address below
+ * accomplishes this, assuming CE# has been asserted.
+ */
+
+ struct docg4_priv *doc = platform_get_drvdata(pdev);
+ void __iomem *docptr = doc->virtadr;
+ int i;
+
+ dev_dbg(doc->dev, "%s...\n", __func__);
+
+ for (i = 0; i < 12; i++)
+ readb(docptr + 0x1fff);
+
+ return 0;
+}
+
+static void __init init_mtd_structs(struct mtd_info *mtd)
+{
+ /* initialize mtd and nand data structures */
+
+ /*
+ * Note that some of the following initializations are not usually
+ * required within a nand driver because they are performed by the nand
+ * infrastructure code as part of nand_scan(). In this case they need
+ * to be initialized here because we skip call to nand_scan_ident() (the
+ * first half of nand_scan()). The call to nand_scan_ident() is skipped
+ * because for this device the chip id is not read in the manner of a
+ * standard nand device. Unfortunately, nand_scan_ident() does other
+ * things as well, such as call nand_set_defaults().
+ */
+
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct docg4_priv *doc = nand_get_controller_data(nand);
+
+ mtd->size = DOCG4_CHIP_SIZE;
+ mtd->name = "Msys_Diskonchip_G4";
+ mtd->writesize = DOCG4_PAGE_SIZE;
+ mtd->erasesize = DOCG4_BLOCK_SIZE;
+ mtd->oobsize = DOCG4_OOB_SIZE;
+ mtd_set_ooblayout(mtd, &docg4_ooblayout_ops);
+ nand->chipsize = DOCG4_CHIP_SIZE;
+ nand->chip_shift = DOCG4_CHIP_SHIFT;
+ nand->bbt_erase_shift = nand->phys_erase_shift = DOCG4_ERASE_SHIFT;
+ nand->chip_delay = 20;
+ nand->page_shift = DOCG4_PAGE_SHIFT;
+ nand->pagemask = 0x3ffff;
+ nand->badblockpos = NAND_LARGE_BADBLOCK_POS;
+ nand->badblockbits = 8;
+ nand->ecc.mode = NAND_ECC_HW_SYNDROME;
+ nand->ecc.size = DOCG4_PAGE_SIZE;
+ nand->ecc.prepad = 8;
+ nand->ecc.bytes = 8;
+ nand->ecc.strength = DOCG4_T;
+ nand->options = NAND_BUSWIDTH_16 | NAND_NO_SUBPAGE_WRITE;
+ nand->IO_ADDR_R = nand->IO_ADDR_W = doc->virtadr + DOC_IOSPACE_DATA;
+ nand->controller = &nand->hwcontrol;
+ nand_hw_control_init(nand->controller);
+
+ /* methods */
+ nand->cmdfunc = docg4_command;
+ nand->waitfunc = docg4_wait;
+ nand->select_chip = docg4_select_chip;
+ nand->read_byte = docg4_read_byte;
+ nand->block_markbad = docg4_block_markbad;
+ nand->read_buf = docg4_read_buf;
+ nand->write_buf = docg4_write_buf16;
+ nand->erase = docg4_erase_block;
+ nand->set_features = nand_get_set_features_notsupp;
+ nand->get_features = nand_get_set_features_notsupp;
+ nand->ecc.read_page = docg4_read_page;
+ nand->ecc.write_page = docg4_write_page;
+ nand->ecc.read_page_raw = docg4_read_page_raw;
+ nand->ecc.write_page_raw = docg4_write_page_raw;
+ nand->ecc.read_oob = docg4_read_oob;
+ nand->ecc.write_oob = docg4_write_oob;
+
+ /*
+ * The way the nand infrastructure code is written, a memory-based bbt
+ * is not created if NAND_SKIP_BBTSCAN is set. With no memory bbt,
+ * nand->block_bad() is used. So when ignoring bad blocks, we skip the
+ * scan and define a dummy block_bad() which always returns 0.
+ */
+ if (ignore_badblocks) {
+ nand->options |= NAND_SKIP_BBTSCAN;
+ nand->block_bad = docg4_block_neverbad;
+ }
+
+}
+
+static int __init read_id_reg(struct mtd_info *mtd)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct docg4_priv *doc = nand_get_controller_data(nand);
+ void __iomem *docptr = doc->virtadr;
+ uint16_t id1, id2;
+
+ /* check for presence of g4 chip by reading id registers */
+ id1 = readw(docptr + DOC_CHIPID);
+ id1 = readw(docptr + DOCG4_MYSTERY_REG);
+ id2 = readw(docptr + DOC_CHIPID_INV);
+ id2 = readw(docptr + DOCG4_MYSTERY_REG);
+
+ if (id1 == DOCG4_IDREG1_VALUE && id2 == DOCG4_IDREG2_VALUE) {
+ dev_info(doc->dev,
+ "NAND device: 128MiB Diskonchip G4 detected\n");
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static char const *part_probes[] = { "cmdlinepart", "saftlpart", NULL };
+
+static int __init probe_docg4(struct platform_device *pdev)
+{
+ struct mtd_info *mtd;
+ struct nand_chip *nand;
+ void __iomem *virtadr;
+ struct docg4_priv *doc;
+ int len, retval;
+ struct resource *r;
+ struct device *dev = &pdev->dev;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (r == NULL) {
+ dev_err(dev, "no io memory resource defined!\n");
+ return -ENODEV;
+ }
+
+ virtadr = ioremap(r->start, resource_size(r));
+ if (!virtadr) {
+ dev_err(dev, "Diskonchip ioremap failed: %pR\n", r);
+ return -EIO;
+ }
+
+ len = sizeof(struct nand_chip) + sizeof(struct docg4_priv);
+ nand = kzalloc(len, GFP_KERNEL);
+ if (nand == NULL) {
+ retval = -ENOMEM;
+ goto fail_unmap;
+ }
+
+ mtd = nand_to_mtd(nand);
+ doc = (struct docg4_priv *) (nand + 1);
+ nand_set_controller_data(nand, doc);
+ mtd->dev.parent = &pdev->dev;
+ doc->virtadr = virtadr;
+ doc->dev = dev;
+
+ init_mtd_structs(mtd);
+
+ /* initialize kernel bch algorithm */
+ doc->bch = init_bch(DOCG4_M, DOCG4_T, DOCG4_PRIMITIVE_POLY);
+ if (doc->bch == NULL) {
+ retval = -EINVAL;
+ goto fail;
+ }
+
+ platform_set_drvdata(pdev, doc);
+
+ reset(mtd);
+ retval = read_id_reg(mtd);
+ if (retval == -ENODEV) {
+ dev_warn(dev, "No diskonchip G4 device found.\n");
+ goto fail;
+ }
+
+ retval = nand_scan_tail(mtd);
+ if (retval)
+ goto fail;
+
+ retval = read_factory_bbt(mtd);
+ if (retval)
+ goto fail;
+
+ retval = mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0);
+ if (retval)
+ goto fail;
+
+ doc->mtd = mtd;
+ return 0;
+
+fail:
+ nand_release(mtd); /* deletes partitions and mtd devices */
+ free_bch(doc->bch);
+ kfree(nand);
+
+fail_unmap:
+ iounmap(virtadr);
+
+ return retval;
+}
+
+static int __exit cleanup_docg4(struct platform_device *pdev)
+{
+ struct docg4_priv *doc = platform_get_drvdata(pdev);
+ nand_release(doc->mtd);
+ free_bch(doc->bch);
+ kfree(mtd_to_nand(doc->mtd));
+ iounmap(doc->virtadr);
+ return 0;
+}
+
+static struct platform_driver docg4_driver = {
+ .driver = {
+ .name = "docg4",
+ },
+ .suspend = docg4_suspend,
+ .resume = docg4_resume,
+ .remove = __exit_p(cleanup_docg4),
+};
+
+module_platform_driver_probe(docg4_driver, probe_docg4);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mike Dunn");
+MODULE_DESCRIPTION("M-Systems DiskOnChip G4 device driver");
diff --git a/drivers/mtd/nand/raw/fsl_elbc_nand.c b/drivers/mtd/nand/raw/fsl_elbc_nand.c
new file mode 100644
index 000000000000..d28df991c73c
--- /dev/null
+++ b/drivers/mtd/nand/raw/fsl_elbc_nand.c
@@ -0,0 +1,979 @@
+/* Freescale Enhanced Local Bus Controller NAND driver
+ *
+ * Copyright © 2006-2007, 2010 Freescale Semiconductor
+ *
+ * Authors: Nick Spence <nick.spence@freescale.com>,
+ * Scott Wood <scottwood@freescale.com>
+ * Jack Lan <jack.lan@freescale.com>
+ * Roy Zang <tie-fei.zang@freescale.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+#include <asm/fsl_lbc.h>
+
+#define MAX_BANKS 8
+#define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */
+#define FCM_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait for FCM */
+
+/* mtd information per set */
+
+struct fsl_elbc_mtd {
+ struct nand_chip chip;
+ struct fsl_lbc_ctrl *ctrl;
+
+ struct device *dev;
+ int bank; /* Chip select bank number */
+ u8 __iomem *vbase; /* Chip select base virtual address */
+ int page_size; /* NAND page size (0=512, 1=2048) */
+ unsigned int fmr; /* FCM Flash Mode Register value */
+};
+
+/* Freescale eLBC FCM controller information */
+
+struct fsl_elbc_fcm_ctrl {
+ struct nand_hw_control controller;
+ struct fsl_elbc_mtd *chips[MAX_BANKS];
+
+ u8 __iomem *addr; /* Address of assigned FCM buffer */
+ unsigned int page; /* Last page written to / read from */
+ unsigned int read_bytes; /* Number of bytes read during command */
+ unsigned int column; /* Saved column from SEQIN */
+ unsigned int index; /* Pointer to next byte to 'read' */
+ unsigned int status; /* status read from LTESR after last op */
+ unsigned int mdr; /* UPM/FCM Data Register value */
+ unsigned int use_mdr; /* Non zero if the MDR is to be set */
+ unsigned int oob; /* Non zero if operating on OOB data */
+ unsigned int counter; /* counter for the initializations */
+ unsigned int max_bitflips; /* Saved during READ0 cmd */
+};
+
+/* These map to the positions used by the FCM hardware ECC generator */
+
+static int fsl_elbc_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
+
+ if (section >= chip->ecc.steps)
+ return -ERANGE;
+
+ oobregion->offset = (16 * section) + 6;
+ if (priv->fmr & FMR_ECCM)
+ oobregion->offset += 2;
+
+ oobregion->length = chip->ecc.bytes;
+
+ return 0;
+}
+
+static int fsl_elbc_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
+
+ if (section > chip->ecc.steps)
+ return -ERANGE;
+
+ if (!section) {
+ oobregion->offset = 0;
+ if (mtd->writesize > 512)
+ oobregion->offset++;
+ oobregion->length = (priv->fmr & FMR_ECCM) ? 7 : 5;
+ } else {
+ oobregion->offset = (16 * section) -
+ ((priv->fmr & FMR_ECCM) ? 5 : 7);
+ if (section < chip->ecc.steps)
+ oobregion->length = 13;
+ else
+ oobregion->length = mtd->oobsize - oobregion->offset;
+ }
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops fsl_elbc_ooblayout_ops = {
+ .ecc = fsl_elbc_ooblayout_ecc,
+ .free = fsl_elbc_ooblayout_free,
+};
+
+/*
+ * ELBC may use HW ECC, so that OOB offsets, that NAND core uses for bbt,
+ * interfere with ECC positions, that's why we implement our own descriptors.
+ * OOB {11, 5}, works for both SP and LP chips, with ECCM = 1 and ECCM = 0.
+ */
+static u8 bbt_pattern[] = {'B', 'b', 't', '0' };
+static u8 mirror_pattern[] = {'1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
+ NAND_BBT_2BIT | NAND_BBT_VERSION,
+ .offs = 11,
+ .len = 4,
+ .veroffs = 15,
+ .maxblocks = 4,
+ .pattern = bbt_pattern,
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
+ NAND_BBT_2BIT | NAND_BBT_VERSION,
+ .offs = 11,
+ .len = 4,
+ .veroffs = 15,
+ .maxblocks = 4,
+ .pattern = mirror_pattern,
+};
+
+/*=================================*/
+
+/*
+ * Set up the FCM hardware block and page address fields, and the fcm
+ * structure addr field to point to the correct FCM buffer in memory
+ */
+static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_lbc_ctrl *ctrl = priv->ctrl;
+ struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
+ struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
+ int buf_num;
+
+ elbc_fcm_ctrl->page = page_addr;
+
+ if (priv->page_size) {
+ /*
+ * large page size chip : FPAR[PI] save the lowest 6 bits,
+ * FBAR[BLK] save the other bits.
+ */
+ out_be32(&lbc->fbar, page_addr >> 6);
+ out_be32(&lbc->fpar,
+ ((page_addr << FPAR_LP_PI_SHIFT) & FPAR_LP_PI) |
+ (oob ? FPAR_LP_MS : 0) | column);
+ buf_num = (page_addr & 1) << 2;
+ } else {
+ /*
+ * small page size chip : FPAR[PI] save the lowest 5 bits,
+ * FBAR[BLK] save the other bits.
+ */
+ out_be32(&lbc->fbar, page_addr >> 5);
+ out_be32(&lbc->fpar,
+ ((page_addr << FPAR_SP_PI_SHIFT) & FPAR_SP_PI) |
+ (oob ? FPAR_SP_MS : 0) | column);
+ buf_num = page_addr & 7;
+ }
+
+ elbc_fcm_ctrl->addr = priv->vbase + buf_num * 1024;
+ elbc_fcm_ctrl->index = column;
+
+ /* for OOB data point to the second half of the buffer */
+ if (oob)
+ elbc_fcm_ctrl->index += priv->page_size ? 2048 : 512;
+
+ dev_vdbg(priv->dev, "set_addr: bank=%d, "
+ "elbc_fcm_ctrl->addr=0x%p (0x%p), "
+ "index %x, pes %d ps %d\n",
+ buf_num, elbc_fcm_ctrl->addr, priv->vbase,
+ elbc_fcm_ctrl->index,
+ chip->phys_erase_shift, chip->page_shift);
+}
+
+/*
+ * execute FCM command and wait for it to complete
+ */
+static int fsl_elbc_run_command(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_lbc_ctrl *ctrl = priv->ctrl;
+ struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
+ struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
+
+ /* Setup the FMR[OP] to execute without write protection */
+ out_be32(&lbc->fmr, priv->fmr | 3);
+ if (elbc_fcm_ctrl->use_mdr)
+ out_be32(&lbc->mdr, elbc_fcm_ctrl->mdr);
+
+ dev_vdbg(priv->dev,
+ "fsl_elbc_run_command: fmr=%08x fir=%08x fcr=%08x\n",
+ in_be32(&lbc->fmr), in_be32(&lbc->fir), in_be32(&lbc->fcr));
+ dev_vdbg(priv->dev,
+ "fsl_elbc_run_command: fbar=%08x fpar=%08x "
+ "fbcr=%08x bank=%d\n",
+ in_be32(&lbc->fbar), in_be32(&lbc->fpar),
+ in_be32(&lbc->fbcr), priv->bank);
+
+ ctrl->irq_status = 0;
+ /* execute special operation */
+ out_be32(&lbc->lsor, priv->bank);
+
+ /* wait for FCM complete flag or timeout */
+ wait_event_timeout(ctrl->irq_wait, ctrl->irq_status,
+ FCM_TIMEOUT_MSECS * HZ/1000);
+ elbc_fcm_ctrl->status = ctrl->irq_status;
+ /* store mdr value in case it was needed */
+ if (elbc_fcm_ctrl->use_mdr)
+ elbc_fcm_ctrl->mdr = in_be32(&lbc->mdr);
+
+ elbc_fcm_ctrl->use_mdr = 0;
+
+ if (elbc_fcm_ctrl->status != LTESR_CC) {
+ dev_info(priv->dev,
+ "command failed: fir %x fcr %x status %x mdr %x\n",
+ in_be32(&lbc->fir), in_be32(&lbc->fcr),
+ elbc_fcm_ctrl->status, elbc_fcm_ctrl->mdr);
+ return -EIO;
+ }
+
+ if (chip->ecc.mode != NAND_ECC_HW)
+ return 0;
+
+ elbc_fcm_ctrl->max_bitflips = 0;
+
+ if (elbc_fcm_ctrl->read_bytes == mtd->writesize + mtd->oobsize) {
+ uint32_t lteccr = in_be32(&lbc->lteccr);
+ /*
+ * if command was a full page read and the ELBC
+ * has the LTECCR register, then bits 12-15 (ppc order) of
+ * LTECCR indicates which 512 byte sub-pages had fixed errors.
+ * bits 28-31 are uncorrectable errors, marked elsewhere.
+ * for small page nand only 1 bit is used.
+ * if the ELBC doesn't have the lteccr register it reads 0
+ * FIXME: 4 bits can be corrected on NANDs with 2k pages, so
+ * count the number of sub-pages with bitflips and update
+ * ecc_stats.corrected accordingly.
+ */
+ if (lteccr & 0x000F000F)
+ out_be32(&lbc->lteccr, 0x000F000F); /* clear lteccr */
+ if (lteccr & 0x000F0000) {
+ mtd->ecc_stats.corrected++;
+ elbc_fcm_ctrl->max_bitflips = 1;
+ }
+ }
+
+ return 0;
+}
+
+static void fsl_elbc_do_read(struct nand_chip *chip, int oob)
+{
+ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_lbc_ctrl *ctrl = priv->ctrl;
+ struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
+
+ if (priv->page_size) {
+ out_be32(&lbc->fir,
+ (FIR_OP_CM0 << FIR_OP0_SHIFT) |
+ (FIR_OP_CA << FIR_OP1_SHIFT) |
+ (FIR_OP_PA << FIR_OP2_SHIFT) |
+ (FIR_OP_CM1 << FIR_OP3_SHIFT) |
+ (FIR_OP_RBW << FIR_OP4_SHIFT));
+
+ out_be32(&lbc->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) |
+ (NAND_CMD_READSTART << FCR_CMD1_SHIFT));
+ } else {
+ out_be32(&lbc->fir,
+ (FIR_OP_CM0 << FIR_OP0_SHIFT) |
+ (FIR_OP_CA << FIR_OP1_SHIFT) |
+ (FIR_OP_PA << FIR_OP2_SHIFT) |
+ (FIR_OP_RBW << FIR_OP3_SHIFT));
+
+ if (oob)
+ out_be32(&lbc->fcr, NAND_CMD_READOOB << FCR_CMD0_SHIFT);
+ else
+ out_be32(&lbc->fcr, NAND_CMD_READ0 << FCR_CMD0_SHIFT);
+ }
+}
+
+/* cmdfunc send commands to the FCM */
+static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_lbc_ctrl *ctrl = priv->ctrl;
+ struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
+ struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
+
+ elbc_fcm_ctrl->use_mdr = 0;
+
+ /* clear the read buffer */
+ elbc_fcm_ctrl->read_bytes = 0;
+ if (command != NAND_CMD_PAGEPROG)
+ elbc_fcm_ctrl->index = 0;
+
+ switch (command) {
+ /* READ0 and READ1 read the entire buffer to use hardware ECC. */
+ case NAND_CMD_READ1:
+ column += 256;
+
+ /* fall-through */
+ case NAND_CMD_READ0:
+ dev_dbg(priv->dev,
+ "fsl_elbc_cmdfunc: NAND_CMD_READ0, page_addr:"
+ " 0x%x, column: 0x%x.\n", page_addr, column);
+
+
+ out_be32(&lbc->fbcr, 0); /* read entire page to enable ECC */
+ set_addr(mtd, 0, page_addr, 0);
+
+ elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize;
+ elbc_fcm_ctrl->index += column;
+
+ fsl_elbc_do_read(chip, 0);
+ fsl_elbc_run_command(mtd);
+ return;
+
+ /* READOOB reads only the OOB because no ECC is performed. */
+ case NAND_CMD_READOOB:
+ dev_vdbg(priv->dev,
+ "fsl_elbc_cmdfunc: NAND_CMD_READOOB, page_addr:"
+ " 0x%x, column: 0x%x.\n", page_addr, column);
+
+ out_be32(&lbc->fbcr, mtd->oobsize - column);
+ set_addr(mtd, column, page_addr, 1);
+
+ elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize;
+
+ fsl_elbc_do_read(chip, 1);
+ fsl_elbc_run_command(mtd);
+ return;
+
+ case NAND_CMD_READID:
+ case NAND_CMD_PARAM:
+ dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD %x\n", command);
+
+ out_be32(&lbc->fir, (FIR_OP_CM0 << FIR_OP0_SHIFT) |
+ (FIR_OP_UA << FIR_OP1_SHIFT) |
+ (FIR_OP_RBW << FIR_OP2_SHIFT));
+ out_be32(&lbc->fcr, command << FCR_CMD0_SHIFT);
+ /*
+ * although currently it's 8 bytes for READID, we always read
+ * the maximum 256 bytes(for PARAM)
+ */
+ out_be32(&lbc->fbcr, 256);
+ elbc_fcm_ctrl->read_bytes = 256;
+ elbc_fcm_ctrl->use_mdr = 1;
+ elbc_fcm_ctrl->mdr = column;
+ set_addr(mtd, 0, 0, 0);
+ fsl_elbc_run_command(mtd);
+ return;
+
+ /* ERASE1 stores the block and page address */
+ case NAND_CMD_ERASE1:
+ dev_vdbg(priv->dev,
+ "fsl_elbc_cmdfunc: NAND_CMD_ERASE1, "
+ "page_addr: 0x%x.\n", page_addr);
+ set_addr(mtd, 0, page_addr, 0);
+ return;
+
+ /* ERASE2 uses the block and page address from ERASE1 */
+ case NAND_CMD_ERASE2:
+ dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n");
+
+ out_be32(&lbc->fir,
+ (FIR_OP_CM0 << FIR_OP0_SHIFT) |
+ (FIR_OP_PA << FIR_OP1_SHIFT) |
+ (FIR_OP_CM2 << FIR_OP2_SHIFT) |
+ (FIR_OP_CW1 << FIR_OP3_SHIFT) |
+ (FIR_OP_RS << FIR_OP4_SHIFT));
+
+ out_be32(&lbc->fcr,
+ (NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) |
+ (NAND_CMD_STATUS << FCR_CMD1_SHIFT) |
+ (NAND_CMD_ERASE2 << FCR_CMD2_SHIFT));
+
+ out_be32(&lbc->fbcr, 0);
+ elbc_fcm_ctrl->read_bytes = 0;
+ elbc_fcm_ctrl->use_mdr = 1;
+
+ fsl_elbc_run_command(mtd);
+ return;
+
+ /* SEQIN sets up the addr buffer and all registers except the length */
+ case NAND_CMD_SEQIN: {
+ __be32 fcr;
+ dev_vdbg(priv->dev,
+ "fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, "
+ "page_addr: 0x%x, column: 0x%x.\n",
+ page_addr, column);
+
+ elbc_fcm_ctrl->column = column;
+ elbc_fcm_ctrl->use_mdr = 1;
+
+ if (column >= mtd->writesize) {
+ /* OOB area */
+ column -= mtd->writesize;
+ elbc_fcm_ctrl->oob = 1;
+ } else {
+ WARN_ON(column != 0);
+ elbc_fcm_ctrl->oob = 0;
+ }
+
+ fcr = (NAND_CMD_STATUS << FCR_CMD1_SHIFT) |
+ (NAND_CMD_SEQIN << FCR_CMD2_SHIFT) |
+ (NAND_CMD_PAGEPROG << FCR_CMD3_SHIFT);
+
+ if (priv->page_size) {
+ out_be32(&lbc->fir,
+ (FIR_OP_CM2 << FIR_OP0_SHIFT) |
+ (FIR_OP_CA << FIR_OP1_SHIFT) |
+ (FIR_OP_PA << FIR_OP2_SHIFT) |
+ (FIR_OP_WB << FIR_OP3_SHIFT) |
+ (FIR_OP_CM3 << FIR_OP4_SHIFT) |
+ (FIR_OP_CW1 << FIR_OP5_SHIFT) |
+ (FIR_OP_RS << FIR_OP6_SHIFT));
+ } else {
+ out_be32(&lbc->fir,
+ (FIR_OP_CM0 << FIR_OP0_SHIFT) |
+ (FIR_OP_CM2 << FIR_OP1_SHIFT) |
+ (FIR_OP_CA << FIR_OP2_SHIFT) |
+ (FIR_OP_PA << FIR_OP3_SHIFT) |
+ (FIR_OP_WB << FIR_OP4_SHIFT) |
+ (FIR_OP_CM3 << FIR_OP5_SHIFT) |
+ (FIR_OP_CW1 << FIR_OP6_SHIFT) |
+ (FIR_OP_RS << FIR_OP7_SHIFT));
+
+ if (elbc_fcm_ctrl->oob)
+ /* OOB area --> READOOB */
+ fcr |= NAND_CMD_READOOB << FCR_CMD0_SHIFT;
+ else
+ /* First 256 bytes --> READ0 */
+ fcr |= NAND_CMD_READ0 << FCR_CMD0_SHIFT;
+ }
+
+ out_be32(&lbc->fcr, fcr);
+ set_addr(mtd, column, page_addr, elbc_fcm_ctrl->oob);
+ return;
+ }
+
+ /* PAGEPROG reuses all of the setup from SEQIN and adds the length */
+ case NAND_CMD_PAGEPROG: {
+ dev_vdbg(priv->dev,
+ "fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG "
+ "writing %d bytes.\n", elbc_fcm_ctrl->index);
+
+ /* if the write did not start at 0 or is not a full page
+ * then set the exact length, otherwise use a full page
+ * write so the HW generates the ECC.
+ */
+ if (elbc_fcm_ctrl->oob || elbc_fcm_ctrl->column != 0 ||
+ elbc_fcm_ctrl->index != mtd->writesize + mtd->oobsize)
+ out_be32(&lbc->fbcr,
+ elbc_fcm_ctrl->index - elbc_fcm_ctrl->column);
+ else
+ out_be32(&lbc->fbcr, 0);
+
+ fsl_elbc_run_command(mtd);
+ return;
+ }
+
+ /* CMD_STATUS must read the status byte while CEB is active */
+ /* Note - it does not wait for the ready line */
+ case NAND_CMD_STATUS:
+ out_be32(&lbc->fir,
+ (FIR_OP_CM0 << FIR_OP0_SHIFT) |
+ (FIR_OP_RBW << FIR_OP1_SHIFT));
+ out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT);
+ out_be32(&lbc->fbcr, 1);
+ set_addr(mtd, 0, 0, 0);
+ elbc_fcm_ctrl->read_bytes = 1;
+
+ fsl_elbc_run_command(mtd);
+
+ /* The chip always seems to report that it is
+ * write-protected, even when it is not.
+ */
+ setbits8(elbc_fcm_ctrl->addr, NAND_STATUS_WP);
+ return;
+
+ /* RESET without waiting for the ready line */
+ case NAND_CMD_RESET:
+ dev_dbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_RESET.\n");
+ out_be32(&lbc->fir, FIR_OP_CM0 << FIR_OP0_SHIFT);
+ out_be32(&lbc->fcr, NAND_CMD_RESET << FCR_CMD0_SHIFT);
+ fsl_elbc_run_command(mtd);
+ return;
+
+ default:
+ dev_err(priv->dev,
+ "fsl_elbc_cmdfunc: error, unsupported command 0x%x.\n",
+ command);
+ }
+}
+
+static void fsl_elbc_select_chip(struct mtd_info *mtd, int chip)
+{
+ /* The hardware does not seem to support multiple
+ * chips per bank.
+ */
+}
+
+/*
+ * Write buf to the FCM Controller Data Buffer
+ */
+static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
+ unsigned int bufsize = mtd->writesize + mtd->oobsize;
+
+ if (len <= 0) {
+ dev_err(priv->dev, "write_buf of %d bytes", len);
+ elbc_fcm_ctrl->status = 0;
+ return;
+ }
+
+ if ((unsigned int)len > bufsize - elbc_fcm_ctrl->index) {
+ dev_err(priv->dev,
+ "write_buf beyond end of buffer "
+ "(%d requested, %u available)\n",
+ len, bufsize - elbc_fcm_ctrl->index);
+ len = bufsize - elbc_fcm_ctrl->index;
+ }
+
+ memcpy_toio(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], buf, len);
+ /*
+ * This is workaround for the weird elbc hangs during nand write,
+ * Scott Wood says: "...perhaps difference in how long it takes a
+ * write to make it through the localbus compared to a write to IMMR
+ * is causing problems, and sync isn't helping for some reason."
+ * Reading back the last byte helps though.
+ */
+ in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index] + len - 1);
+
+ elbc_fcm_ctrl->index += len;
+}
+
+/*
+ * read a byte from either the FCM hardware buffer if it has any data left
+ * otherwise issue a command to read a single byte.
+ */
+static u8 fsl_elbc_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
+
+ /* If there are still bytes in the FCM, then use the next byte. */
+ if (elbc_fcm_ctrl->index < elbc_fcm_ctrl->read_bytes)
+ return in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index++]);
+
+ dev_err(priv->dev, "read_byte beyond end of buffer\n");
+ return ERR_BYTE;
+}
+
+/*
+ * Read from the FCM Controller Data Buffer
+ */
+static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
+ int avail;
+
+ if (len < 0)
+ return;
+
+ avail = min((unsigned int)len,
+ elbc_fcm_ctrl->read_bytes - elbc_fcm_ctrl->index);
+ memcpy_fromio(buf, &elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], avail);
+ elbc_fcm_ctrl->index += avail;
+
+ if (len > avail)
+ dev_err(priv->dev,
+ "read_buf beyond end of buffer "
+ "(%d requested, %d available)\n",
+ len, avail);
+}
+
+/* This function is called after Program and Erase Operations to
+ * check for success or failure.
+ */
+static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip)
+{
+ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
+
+ if (elbc_fcm_ctrl->status != LTESR_CC)
+ return NAND_STATUS_FAIL;
+
+ /* The chip always seems to report that it is
+ * write-protected, even when it is not.
+ */
+ return (elbc_fcm_ctrl->mdr & 0xff) | NAND_STATUS_WP;
+}
+
+static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_lbc_ctrl *ctrl = priv->ctrl;
+ struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
+ unsigned int al;
+
+ /* calculate FMR Address Length field */
+ al = 0;
+ if (chip->pagemask & 0xffff0000)
+ al++;
+ if (chip->pagemask & 0xff000000)
+ al++;
+
+ priv->fmr |= al << FMR_AL_SHIFT;
+
+ dev_dbg(priv->dev, "fsl_elbc_init: nand->numchips = %d\n",
+ chip->numchips);
+ dev_dbg(priv->dev, "fsl_elbc_init: nand->chipsize = %lld\n",
+ chip->chipsize);
+ dev_dbg(priv->dev, "fsl_elbc_init: nand->pagemask = %8x\n",
+ chip->pagemask);
+ dev_dbg(priv->dev, "fsl_elbc_init: nand->chip_delay = %d\n",
+ chip->chip_delay);
+ dev_dbg(priv->dev, "fsl_elbc_init: nand->badblockpos = %d\n",
+ chip->badblockpos);
+ dev_dbg(priv->dev, "fsl_elbc_init: nand->chip_shift = %d\n",
+ chip->chip_shift);
+ dev_dbg(priv->dev, "fsl_elbc_init: nand->page_shift = %d\n",
+ chip->page_shift);
+ dev_dbg(priv->dev, "fsl_elbc_init: nand->phys_erase_shift = %d\n",
+ chip->phys_erase_shift);
+ dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.mode = %d\n",
+ chip->ecc.mode);
+ dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.steps = %d\n",
+ chip->ecc.steps);
+ dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.bytes = %d\n",
+ chip->ecc.bytes);
+ dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.total = %d\n",
+ chip->ecc.total);
+ dev_dbg(priv->dev, "fsl_elbc_init: mtd->ooblayout = %p\n",
+ mtd->ooblayout);
+ dev_dbg(priv->dev, "fsl_elbc_init: mtd->flags = %08x\n", mtd->flags);
+ dev_dbg(priv->dev, "fsl_elbc_init: mtd->size = %lld\n", mtd->size);
+ dev_dbg(priv->dev, "fsl_elbc_init: mtd->erasesize = %d\n",
+ mtd->erasesize);
+ dev_dbg(priv->dev, "fsl_elbc_init: mtd->writesize = %d\n",
+ mtd->writesize);
+ dev_dbg(priv->dev, "fsl_elbc_init: mtd->oobsize = %d\n",
+ mtd->oobsize);
+
+ /* adjust Option Register and ECC to match Flash page size */
+ if (mtd->writesize == 512) {
+ priv->page_size = 0;
+ clrbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS);
+ } else if (mtd->writesize == 2048) {
+ priv->page_size = 1;
+ setbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS);
+ } else {
+ dev_err(priv->dev,
+ "fsl_elbc_init: page size %d is not supported\n",
+ mtd->writesize);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_lbc_ctrl *ctrl = priv->ctrl;
+ struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
+
+ nand_read_page_op(chip, page, 0, buf, mtd->writesize);
+ if (oob_required)
+ fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ if (fsl_elbc_wait(mtd, chip) & NAND_STATUS_FAIL)
+ mtd->ecc_stats.failed++;
+
+ return elbc_fcm_ctrl->max_bitflips;
+}
+
+/* ECC will be calculated automatically, and errors will be detected in
+ * waitfunc.
+ */
+static int fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required, int page)
+{
+ nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);
+ fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ return nand_prog_page_end_op(chip);
+}
+
+/* ECC will be calculated automatically, and errors will be detected in
+ * waitfunc.
+ */
+static int fsl_elbc_write_subpage(struct mtd_info *mtd, struct nand_chip *chip,
+ uint32_t offset, uint32_t data_len,
+ const uint8_t *buf, int oob_required, int page)
+{
+ nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+ fsl_elbc_write_buf(mtd, buf, mtd->writesize);
+ fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
+ return nand_prog_page_end_op(chip);
+}
+
+static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
+{
+ struct fsl_lbc_ctrl *ctrl = priv->ctrl;
+ struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
+ struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
+ struct nand_chip *chip = &priv->chip;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ dev_dbg(priv->dev, "eLBC Set Information for bank %d\n", priv->bank);
+
+ /* Fill in fsl_elbc_mtd structure */
+ mtd->dev.parent = priv->dev;
+ nand_set_flash_node(chip, priv->dev->of_node);
+
+ /* set timeout to maximum */
+ priv->fmr = 15 << FMR_CWTO_SHIFT;
+ if (in_be32(&lbc->bank[priv->bank].or) & OR_FCM_PGS)
+ priv->fmr |= FMR_ECCM;
+
+ /* fill in nand_chip structure */
+ /* set up function call table */
+ chip->read_byte = fsl_elbc_read_byte;
+ chip->write_buf = fsl_elbc_write_buf;
+ chip->read_buf = fsl_elbc_read_buf;
+ chip->select_chip = fsl_elbc_select_chip;
+ chip->cmdfunc = fsl_elbc_cmdfunc;
+ chip->waitfunc = fsl_elbc_wait;
+ chip->set_features = nand_get_set_features_notsupp;
+ chip->get_features = nand_get_set_features_notsupp;
+
+ chip->bbt_td = &bbt_main_descr;
+ chip->bbt_md = &bbt_mirror_descr;
+
+ /* set up nand options */
+ chip->bbt_options = NAND_BBT_USE_FLASH;
+
+ chip->controller = &elbc_fcm_ctrl->controller;
+ nand_set_controller_data(chip, priv);
+
+ chip->ecc.read_page = fsl_elbc_read_page;
+ chip->ecc.write_page = fsl_elbc_write_page;
+ chip->ecc.write_subpage = fsl_elbc_write_subpage;
+
+ /* If CS Base Register selects full hardware ECC then use it */
+ if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) ==
+ BR_DECC_CHK_GEN) {
+ chip->ecc.mode = NAND_ECC_HW;
+ mtd_set_ooblayout(mtd, &fsl_elbc_ooblayout_ops);
+ chip->ecc.size = 512;
+ chip->ecc.bytes = 3;
+ chip->ecc.strength = 1;
+ } else {
+ /* otherwise fall back to default software ECC */
+ chip->ecc.mode = NAND_ECC_SOFT;
+ chip->ecc.algo = NAND_ECC_HAMMING;
+ }
+
+ return 0;
+}
+
+static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv)
+{
+ struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
+ struct mtd_info *mtd = nand_to_mtd(&priv->chip);
+
+ nand_release(mtd);
+
+ kfree(mtd->name);
+
+ if (priv->vbase)
+ iounmap(priv->vbase);
+
+ elbc_fcm_ctrl->chips[priv->bank] = NULL;
+ kfree(priv);
+ return 0;
+}
+
+static DEFINE_MUTEX(fsl_elbc_nand_mutex);
+
+static int fsl_elbc_nand_probe(struct platform_device *pdev)
+{
+ struct fsl_lbc_regs __iomem *lbc;
+ struct fsl_elbc_mtd *priv;
+ struct resource res;
+ struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl;
+ static const char *part_probe_types[]
+ = { "cmdlinepart", "RedBoot", "ofpart", NULL };
+ int ret;
+ int bank;
+ struct device *dev;
+ struct device_node *node = pdev->dev.of_node;
+ struct mtd_info *mtd;
+
+ if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
+ return -ENODEV;
+ lbc = fsl_lbc_ctrl_dev->regs;
+ dev = fsl_lbc_ctrl_dev->dev;
+
+ /* get, allocate and map the memory resource */
+ ret = of_address_to_resource(node, 0, &res);
+ if (ret) {
+ dev_err(dev, "failed to get resource\n");
+ return ret;
+ }
+
+ /* find which chip select it is connected to */
+ for (bank = 0; bank < MAX_BANKS; bank++)
+ if ((in_be32(&lbc->bank[bank].br) & BR_V) &&
+ (in_be32(&lbc->bank[bank].br) & BR_MSEL) == BR_MS_FCM &&
+ (in_be32(&lbc->bank[bank].br) &
+ in_be32(&lbc->bank[bank].or) & BR_BA)
+ == fsl_lbc_addr(res.start))
+ break;
+
+ if (bank >= MAX_BANKS) {
+ dev_err(dev, "address did not match any chip selects\n");
+ return -ENODEV;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ mutex_lock(&fsl_elbc_nand_mutex);
+ if (!fsl_lbc_ctrl_dev->nand) {
+ elbc_fcm_ctrl = kzalloc(sizeof(*elbc_fcm_ctrl), GFP_KERNEL);
+ if (!elbc_fcm_ctrl) {
+ mutex_unlock(&fsl_elbc_nand_mutex);
+ ret = -ENOMEM;
+ goto err;
+ }
+ elbc_fcm_ctrl->counter++;
+
+ nand_hw_control_init(&elbc_fcm_ctrl->controller);
+ fsl_lbc_ctrl_dev->nand = elbc_fcm_ctrl;
+ } else {
+ elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand;
+ }
+ mutex_unlock(&fsl_elbc_nand_mutex);
+
+ elbc_fcm_ctrl->chips[bank] = priv;
+ priv->bank = bank;
+ priv->ctrl = fsl_lbc_ctrl_dev;
+ priv->dev = &pdev->dev;
+ dev_set_drvdata(priv->dev, priv);
+
+ priv->vbase = ioremap(res.start, resource_size(&res));
+ if (!priv->vbase) {
+ dev_err(dev, "failed to map chip region\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ mtd = nand_to_mtd(&priv->chip);
+ mtd->name = kasprintf(GFP_KERNEL, "%llx.flash", (u64)res.start);
+ if (!nand_to_mtd(&priv->chip)->name) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = fsl_elbc_chip_init(priv);
+ if (ret)
+ goto err;
+
+ ret = nand_scan_ident(mtd, 1, NULL);
+ if (ret)
+ goto err;
+
+ ret = fsl_elbc_chip_init_tail(mtd);
+ if (ret)
+ goto err;
+
+ ret = nand_scan_tail(mtd);
+ if (ret)
+ goto err;
+
+ /* First look for RedBoot table or partitions on the command
+ * line, these take precedence over device tree information */
+ mtd_device_parse_register(mtd, part_probe_types, NULL,
+ NULL, 0);
+
+ pr_info("eLBC NAND device at 0x%llx, bank %d\n",
+ (unsigned long long)res.start, priv->bank);
+ return 0;
+
+err:
+ fsl_elbc_chip_remove(priv);
+ return ret;
+}
+
+static int fsl_elbc_nand_remove(struct platform_device *pdev)
+{
+ struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand;
+ struct fsl_elbc_mtd *priv = dev_get_drvdata(&pdev->dev);
+
+ fsl_elbc_chip_remove(priv);
+
+ mutex_lock(&fsl_elbc_nand_mutex);
+ elbc_fcm_ctrl->counter--;
+ if (!elbc_fcm_ctrl->counter) {
+ fsl_lbc_ctrl_dev->nand = NULL;
+ kfree(elbc_fcm_ctrl);
+ }
+ mutex_unlock(&fsl_elbc_nand_mutex);
+
+ return 0;
+
+}
+
+static const struct of_device_id fsl_elbc_nand_match[] = {
+ { .compatible = "fsl,elbc-fcm-nand", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, fsl_elbc_nand_match);
+
+static struct platform_driver fsl_elbc_nand_driver = {
+ .driver = {
+ .name = "fsl,elbc-fcm-nand",
+ .of_match_table = fsl_elbc_nand_match,
+ },
+ .probe = fsl_elbc_nand_probe,
+ .remove = fsl_elbc_nand_remove,
+};
+
+module_platform_driver(fsl_elbc_nand_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Freescale");
+MODULE_DESCRIPTION("Freescale Enhanced Local Bus Controller MTD NAND driver");
diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c
new file mode 100644
index 000000000000..61aae0224078
--- /dev/null
+++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c
@@ -0,0 +1,1111 @@
+/*
+ * Freescale Integrated Flash Controller NAND driver
+ *
+ * Copyright 2011-2012 Freescale Semiconductor, Inc
+ *
+ * Author: Dipen Dudhat <Dipen.Dudhat@freescale.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/fsl_ifc.h>
+
+#define ERR_BYTE 0xFF /* Value returned for read
+ bytes when read failed */
+#define IFC_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait
+ for IFC NAND Machine */
+
+struct fsl_ifc_ctrl;
+
+/* mtd information per set */
+struct fsl_ifc_mtd {
+ struct nand_chip chip;
+ struct fsl_ifc_ctrl *ctrl;
+
+ struct device *dev;
+ int bank; /* Chip select bank number */
+ unsigned int bufnum_mask; /* bufnum = page & bufnum_mask */
+ u8 __iomem *vbase; /* Chip select base virtual address */
+};
+
+/* overview of the fsl ifc controller */
+struct fsl_ifc_nand_ctrl {
+ struct nand_hw_control controller;
+ struct fsl_ifc_mtd *chips[FSL_IFC_BANK_COUNT];
+
+ void __iomem *addr; /* Address of assigned IFC buffer */
+ unsigned int page; /* Last page written to / read from */
+ unsigned int read_bytes;/* Number of bytes read during command */
+ unsigned int column; /* Saved column from SEQIN */
+ unsigned int index; /* Pointer to next byte to 'read' */
+ unsigned int oob; /* Non zero if operating on OOB data */
+ unsigned int eccread; /* Non zero for a full-page ECC read */
+ unsigned int counter; /* counter for the initializations */
+ unsigned int max_bitflips; /* Saved during READ0 cmd */
+};
+
+static struct fsl_ifc_nand_ctrl *ifc_nand_ctrl;
+
+/*
+ * Generic flash bbt descriptors
+ */
+static u8 bbt_pattern[] = {'B', 'b', 't', '0' };
+static u8 mirror_pattern[] = {'1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
+ NAND_BBT_2BIT | NAND_BBT_VERSION,
+ .offs = 2, /* 0 on 8-bit small page */
+ .len = 4,
+ .veroffs = 6,
+ .maxblocks = 4,
+ .pattern = bbt_pattern,
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
+ NAND_BBT_2BIT | NAND_BBT_VERSION,
+ .offs = 2, /* 0 on 8-bit small page */
+ .len = 4,
+ .veroffs = 6,
+ .maxblocks = 4,
+ .pattern = mirror_pattern,
+};
+
+static int fsl_ifc_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (section)
+ return -ERANGE;
+
+ oobregion->offset = 8;
+ oobregion->length = chip->ecc.total;
+
+ return 0;
+}
+
+static int fsl_ifc_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (section > 1)
+ return -ERANGE;
+
+ if (mtd->writesize == 512 &&
+ !(chip->options & NAND_BUSWIDTH_16)) {
+ if (!section) {
+ oobregion->offset = 0;
+ oobregion->length = 5;
+ } else {
+ oobregion->offset = 6;
+ oobregion->length = 2;
+ }
+
+ return 0;
+ }
+
+ if (!section) {
+ oobregion->offset = 2;
+ oobregion->length = 6;
+ } else {
+ oobregion->offset = chip->ecc.total + 8;
+ oobregion->length = mtd->oobsize - oobregion->offset;
+ }
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops fsl_ifc_ooblayout_ops = {
+ .ecc = fsl_ifc_ooblayout_ecc,
+ .free = fsl_ifc_ooblayout_free,
+};
+
+/*
+ * Set up the IFC hardware block and page address fields, and the ifc nand
+ * structure addr field to point to the correct IFC buffer in memory
+ */
+static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_ifc_ctrl *ctrl = priv->ctrl;
+ struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
+ int buf_num;
+
+ ifc_nand_ctrl->page = page_addr;
+ /* Program ROW0/COL0 */
+ ifc_out32(page_addr, &ifc->ifc_nand.row0);
+ ifc_out32((oob ? IFC_NAND_COL_MS : 0) | column, &ifc->ifc_nand.col0);
+
+ buf_num = page_addr & priv->bufnum_mask;
+
+ ifc_nand_ctrl->addr = priv->vbase + buf_num * (mtd->writesize * 2);
+ ifc_nand_ctrl->index = column;
+
+ /* for OOB data point to the second half of the buffer */
+ if (oob)
+ ifc_nand_ctrl->index += mtd->writesize;
+}
+
+/* returns nonzero if entire page is blank */
+static int check_read_ecc(struct mtd_info *mtd, struct fsl_ifc_ctrl *ctrl,
+ u32 eccstat, unsigned int bufnum)
+{
+ return (eccstat >> ((3 - bufnum % 4) * 8)) & 15;
+}
+
+/*
+ * execute IFC NAND command and wait for it to complete
+ */
+static void fsl_ifc_run_command(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_ifc_ctrl *ctrl = priv->ctrl;
+ struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl;
+ struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
+ u32 eccstat;
+ int i;
+
+ /* set the chip select for NAND Transaction */
+ ifc_out32(priv->bank << IFC_NAND_CSEL_SHIFT,
+ &ifc->ifc_nand.nand_csel);
+
+ dev_vdbg(priv->dev,
+ "%s: fir0=%08x fcr0=%08x\n",
+ __func__,
+ ifc_in32(&ifc->ifc_nand.nand_fir0),
+ ifc_in32(&ifc->ifc_nand.nand_fcr0));
+
+ ctrl->nand_stat = 0;
+
+ /* start read/write seq */
+ ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt);
+
+ /* wait for command complete flag or timeout */
+ wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat,
+ msecs_to_jiffies(IFC_TIMEOUT_MSECS));
+
+ /* ctrl->nand_stat will be updated from IRQ context */
+ if (!ctrl->nand_stat)
+ dev_err(priv->dev, "Controller is not responding\n");
+ if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_FTOER)
+ dev_err(priv->dev, "NAND Flash Timeout Error\n");
+ if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_WPER)
+ dev_err(priv->dev, "NAND Flash Write Protect Error\n");
+
+ nctrl->max_bitflips = 0;
+
+ if (nctrl->eccread) {
+ int errors;
+ int bufnum = nctrl->page & priv->bufnum_mask;
+ int sector_start = bufnum * chip->ecc.steps;
+ int sector_end = sector_start + chip->ecc.steps - 1;
+ __be32 *eccstat_regs;
+
+ eccstat_regs = ifc->ifc_nand.nand_eccstat;
+ eccstat = ifc_in32(&eccstat_regs[sector_start / 4]);
+
+ for (i = sector_start; i <= sector_end; i++) {
+ if (i != sector_start && !(i % 4))
+ eccstat = ifc_in32(&eccstat_regs[i / 4]);
+
+ errors = check_read_ecc(mtd, ctrl, eccstat, i);
+
+ if (errors == 15) {
+ /*
+ * Uncorrectable error.
+ * We'll check for blank pages later.
+ *
+ * We disable ECCER reporting due to...
+ * erratum IFC-A002770 -- so report it now if we
+ * see an uncorrectable error in ECCSTAT.
+ */
+ ctrl->nand_stat |= IFC_NAND_EVTER_STAT_ECCER;
+ continue;
+ }
+
+ mtd->ecc_stats.corrected += errors;
+ nctrl->max_bitflips = max_t(unsigned int,
+ nctrl->max_bitflips,
+ errors);
+ }
+
+ nctrl->eccread = 0;
+ }
+}
+
+static void fsl_ifc_do_read(struct nand_chip *chip,
+ int oob,
+ struct mtd_info *mtd)
+{
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_ifc_ctrl *ctrl = priv->ctrl;
+ struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
+
+ /* Program FIR/IFC_NAND_FCR0 for Small/Large page */
+ if (mtd->writesize > 512) {
+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) |
+ (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) |
+ (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) |
+ (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP4_SHIFT),
+ &ifc->ifc_nand.nand_fir0);
+ ifc_out32(0x0, &ifc->ifc_nand.nand_fir1);
+
+ ifc_out32((NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) |
+ (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT),
+ &ifc->ifc_nand.nand_fcr0);
+ } else {
+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) |
+ (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) |
+ (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP3_SHIFT),
+ &ifc->ifc_nand.nand_fir0);
+ ifc_out32(0x0, &ifc->ifc_nand.nand_fir1);
+
+ if (oob)
+ ifc_out32(NAND_CMD_READOOB <<
+ IFC_NAND_FCR0_CMD0_SHIFT,
+ &ifc->ifc_nand.nand_fcr0);
+ else
+ ifc_out32(NAND_CMD_READ0 <<
+ IFC_NAND_FCR0_CMD0_SHIFT,
+ &ifc->ifc_nand.nand_fcr0);
+ }
+}
+
+/* cmdfunc send commands to the IFC NAND Machine */
+static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr) {
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_ifc_ctrl *ctrl = priv->ctrl;
+ struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
+
+ /* clear the read buffer */
+ ifc_nand_ctrl->read_bytes = 0;
+ if (command != NAND_CMD_PAGEPROG)
+ ifc_nand_ctrl->index = 0;
+
+ switch (command) {
+ /* READ0 read the entire buffer to use hardware ECC. */
+ case NAND_CMD_READ0:
+ ifc_out32(0, &ifc->ifc_nand.nand_fbcr);
+ set_addr(mtd, 0, page_addr, 0);
+
+ ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize;
+ ifc_nand_ctrl->index += column;
+
+ if (chip->ecc.mode == NAND_ECC_HW)
+ ifc_nand_ctrl->eccread = 1;
+
+ fsl_ifc_do_read(chip, 0, mtd);
+ fsl_ifc_run_command(mtd);
+ return;
+
+ /* READOOB reads only the OOB because no ECC is performed. */
+ case NAND_CMD_READOOB:
+ ifc_out32(mtd->oobsize - column, &ifc->ifc_nand.nand_fbcr);
+ set_addr(mtd, column, page_addr, 1);
+
+ ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize;
+
+ fsl_ifc_do_read(chip, 1, mtd);
+ fsl_ifc_run_command(mtd);
+
+ return;
+
+ case NAND_CMD_READID:
+ case NAND_CMD_PARAM: {
+ int timing = IFC_FIR_OP_RB;
+ if (command == NAND_CMD_PARAM)
+ timing = IFC_FIR_OP_RBCD;
+
+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) |
+ (timing << IFC_NAND_FIR0_OP2_SHIFT),
+ &ifc->ifc_nand.nand_fir0);
+ ifc_out32(command << IFC_NAND_FCR0_CMD0_SHIFT,
+ &ifc->ifc_nand.nand_fcr0);
+ ifc_out32(column, &ifc->ifc_nand.row3);
+
+ /*
+ * although currently it's 8 bytes for READID, we always read
+ * the maximum 256 bytes(for PARAM)
+ */
+ ifc_out32(256, &ifc->ifc_nand.nand_fbcr);
+ ifc_nand_ctrl->read_bytes = 256;
+
+ set_addr(mtd, 0, 0, 0);
+ fsl_ifc_run_command(mtd);
+ return;
+ }
+
+ /* ERASE1 stores the block and page address */
+ case NAND_CMD_ERASE1:
+ set_addr(mtd, 0, page_addr, 0);
+ return;
+
+ /* ERASE2 uses the block and page address from ERASE1 */
+ case NAND_CMD_ERASE2:
+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP1_SHIFT) |
+ (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP2_SHIFT),
+ &ifc->ifc_nand.nand_fir0);
+
+ ifc_out32((NAND_CMD_ERASE1 << IFC_NAND_FCR0_CMD0_SHIFT) |
+ (NAND_CMD_ERASE2 << IFC_NAND_FCR0_CMD1_SHIFT),
+ &ifc->ifc_nand.nand_fcr0);
+
+ ifc_out32(0, &ifc->ifc_nand.nand_fbcr);
+ ifc_nand_ctrl->read_bytes = 0;
+ fsl_ifc_run_command(mtd);
+ return;
+
+ /* SEQIN sets up the addr buffer and all registers except the length */
+ case NAND_CMD_SEQIN: {
+ u32 nand_fcr0;
+ ifc_nand_ctrl->column = column;
+ ifc_nand_ctrl->oob = 0;
+
+ if (mtd->writesize > 512) {
+ nand_fcr0 =
+ (NAND_CMD_SEQIN << IFC_NAND_FCR0_CMD0_SHIFT) |
+ (NAND_CMD_STATUS << IFC_NAND_FCR0_CMD1_SHIFT) |
+ (NAND_CMD_PAGEPROG << IFC_NAND_FCR0_CMD2_SHIFT);
+
+ ifc_out32(
+ (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) |
+ (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) |
+ (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP3_SHIFT) |
+ (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP4_SHIFT),
+ &ifc->ifc_nand.nand_fir0);
+ ifc_out32(
+ (IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT) |
+ (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR1_OP6_SHIFT) |
+ (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP7_SHIFT),
+ &ifc->ifc_nand.nand_fir1);
+ } else {
+ nand_fcr0 = ((NAND_CMD_PAGEPROG <<
+ IFC_NAND_FCR0_CMD1_SHIFT) |
+ (NAND_CMD_SEQIN <<
+ IFC_NAND_FCR0_CMD2_SHIFT) |
+ (NAND_CMD_STATUS <<
+ IFC_NAND_FCR0_CMD3_SHIFT));
+
+ ifc_out32(
+ (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP1_SHIFT) |
+ (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP2_SHIFT) |
+ (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP3_SHIFT) |
+ (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP4_SHIFT),
+ &ifc->ifc_nand.nand_fir0);
+ ifc_out32(
+ (IFC_FIR_OP_CMD1 << IFC_NAND_FIR1_OP5_SHIFT) |
+ (IFC_FIR_OP_CW3 << IFC_NAND_FIR1_OP6_SHIFT) |
+ (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR1_OP7_SHIFT) |
+ (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP8_SHIFT),
+ &ifc->ifc_nand.nand_fir1);
+
+ if (column >= mtd->writesize)
+ nand_fcr0 |=
+ NAND_CMD_READOOB << IFC_NAND_FCR0_CMD0_SHIFT;
+ else
+ nand_fcr0 |=
+ NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT;
+ }
+
+ if (column >= mtd->writesize) {
+ /* OOB area --> READOOB */
+ column -= mtd->writesize;
+ ifc_nand_ctrl->oob = 1;
+ }
+ ifc_out32(nand_fcr0, &ifc->ifc_nand.nand_fcr0);
+ set_addr(mtd, column, page_addr, ifc_nand_ctrl->oob);
+ return;
+ }
+
+ /* PAGEPROG reuses all of the setup from SEQIN and adds the length */
+ case NAND_CMD_PAGEPROG: {
+ if (ifc_nand_ctrl->oob) {
+ ifc_out32(ifc_nand_ctrl->index -
+ ifc_nand_ctrl->column,
+ &ifc->ifc_nand.nand_fbcr);
+ } else {
+ ifc_out32(0, &ifc->ifc_nand.nand_fbcr);
+ }
+
+ fsl_ifc_run_command(mtd);
+ return;
+ }
+
+ case NAND_CMD_STATUS: {
+ void __iomem *addr;
+
+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP1_SHIFT),
+ &ifc->ifc_nand.nand_fir0);
+ ifc_out32(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT,
+ &ifc->ifc_nand.nand_fcr0);
+ ifc_out32(1, &ifc->ifc_nand.nand_fbcr);
+ set_addr(mtd, 0, 0, 0);
+ ifc_nand_ctrl->read_bytes = 1;
+
+ fsl_ifc_run_command(mtd);
+
+ /*
+ * The chip always seems to report that it is
+ * write-protected, even when it is not.
+ */
+ addr = ifc_nand_ctrl->addr;
+ if (chip->options & NAND_BUSWIDTH_16)
+ ifc_out16(ifc_in16(addr) | (NAND_STATUS_WP), addr);
+ else
+ ifc_out8(ifc_in8(addr) | (NAND_STATUS_WP), addr);
+ return;
+ }
+
+ case NAND_CMD_RESET:
+ ifc_out32(IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT,
+ &ifc->ifc_nand.nand_fir0);
+ ifc_out32(NAND_CMD_RESET << IFC_NAND_FCR0_CMD0_SHIFT,
+ &ifc->ifc_nand.nand_fcr0);
+ fsl_ifc_run_command(mtd);
+ return;
+
+ default:
+ dev_err(priv->dev, "%s: error, unsupported command 0x%x.\n",
+ __func__, command);
+ }
+}
+
+static void fsl_ifc_select_chip(struct mtd_info *mtd, int chip)
+{
+ /* The hardware does not seem to support multiple
+ * chips per bank.
+ */
+}
+
+/*
+ * Write buf to the IFC NAND Controller Data Buffer
+ */
+static void fsl_ifc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
+ unsigned int bufsize = mtd->writesize + mtd->oobsize;
+
+ if (len <= 0) {
+ dev_err(priv->dev, "%s: len %d bytes", __func__, len);
+ return;
+ }
+
+ if ((unsigned int)len > bufsize - ifc_nand_ctrl->index) {
+ dev_err(priv->dev,
+ "%s: beyond end of buffer (%d requested, %u available)\n",
+ __func__, len, bufsize - ifc_nand_ctrl->index);
+ len = bufsize - ifc_nand_ctrl->index;
+ }
+
+ memcpy_toio(ifc_nand_ctrl->addr + ifc_nand_ctrl->index, buf, len);
+ ifc_nand_ctrl->index += len;
+}
+
+/*
+ * Read a byte from either the IFC hardware buffer
+ * read function for 8-bit buswidth
+ */
+static uint8_t fsl_ifc_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
+ unsigned int offset;
+
+ /*
+ * If there are still bytes in the IFC buffer, then use the
+ * next byte.
+ */
+ if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) {
+ offset = ifc_nand_ctrl->index++;
+ return ifc_in8(ifc_nand_ctrl->addr + offset);
+ }
+
+ dev_err(priv->dev, "%s: beyond end of buffer\n", __func__);
+ return ERR_BYTE;
+}
+
+/*
+ * Read two bytes from the IFC hardware buffer
+ * read function for 16-bit buswith
+ */
+static uint8_t fsl_ifc_read_byte16(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
+ uint16_t data;
+
+ /*
+ * If there are still bytes in the IFC buffer, then use the
+ * next byte.
+ */
+ if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) {
+ data = ifc_in16(ifc_nand_ctrl->addr + ifc_nand_ctrl->index);
+ ifc_nand_ctrl->index += 2;
+ return (uint8_t) data;
+ }
+
+ dev_err(priv->dev, "%s: beyond end of buffer\n", __func__);
+ return ERR_BYTE;
+}
+
+/*
+ * Read from the IFC Controller Data Buffer
+ */
+static void fsl_ifc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
+ int avail;
+
+ if (len < 0) {
+ dev_err(priv->dev, "%s: len %d bytes", __func__, len);
+ return;
+ }
+
+ avail = min((unsigned int)len,
+ ifc_nand_ctrl->read_bytes - ifc_nand_ctrl->index);
+ memcpy_fromio(buf, ifc_nand_ctrl->addr + ifc_nand_ctrl->index, avail);
+ ifc_nand_ctrl->index += avail;
+
+ if (len > avail)
+ dev_err(priv->dev,
+ "%s: beyond end of buffer (%d requested, %d available)\n",
+ __func__, len, avail);
+}
+
+/*
+ * This function is called after Program and Erase Operations to
+ * check for success or failure.
+ */
+static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip)
+{
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_ifc_ctrl *ctrl = priv->ctrl;
+ struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
+ u32 nand_fsr;
+ int status;
+
+ /* Use READ_STATUS command, but wait for the device to be ready */
+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR0_OP1_SHIFT),
+ &ifc->ifc_nand.nand_fir0);
+ ifc_out32(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT,
+ &ifc->ifc_nand.nand_fcr0);
+ ifc_out32(1, &ifc->ifc_nand.nand_fbcr);
+ set_addr(mtd, 0, 0, 0);
+ ifc_nand_ctrl->read_bytes = 1;
+
+ fsl_ifc_run_command(mtd);
+
+ nand_fsr = ifc_in32(&ifc->ifc_nand.nand_fsr);
+ status = nand_fsr >> 24;
+ /*
+ * The chip always seems to report that it is
+ * write-protected, even when it is not.
+ */
+ return status | NAND_STATUS_WP;
+}
+
+/*
+ * The controller does not check for bitflips in erased pages,
+ * therefore software must check instead.
+ */
+static int check_erased_page(struct nand_chip *chip, u8 *buf)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ u8 *ecc = chip->oob_poi;
+ const int ecc_size = chip->ecc.bytes;
+ const int pkt_size = chip->ecc.size;
+ int i, res, bitflips = 0;
+ struct mtd_oob_region oobregion = { };
+
+ mtd_ooblayout_ecc(mtd, 0, &oobregion);
+ ecc += oobregion.offset;
+
+ for (i = 0; i < chip->ecc.steps; ++i) {
+ res = nand_check_erased_ecc_chunk(buf, pkt_size, ecc, ecc_size,
+ NULL, 0,
+ chip->ecc.strength);
+ if (res < 0)
+ mtd->ecc_stats.failed++;
+ else
+ mtd->ecc_stats.corrected += res;
+
+ bitflips = max(res, bitflips);
+ buf += pkt_size;
+ ecc += ecc_size;
+ }
+
+ return bitflips;
+}
+
+static int fsl_ifc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_ifc_ctrl *ctrl = priv->ctrl;
+ struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl;
+
+ nand_read_page_op(chip, page, 0, buf, mtd->writesize);
+ if (oob_required)
+ fsl_ifc_read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_ECCER) {
+ if (!oob_required)
+ fsl_ifc_read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ return check_erased_page(chip, buf);
+ }
+
+ if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC)
+ mtd->ecc_stats.failed++;
+
+ return nctrl->max_bitflips;
+}
+
+/* ECC will be calculated automatically, and errors will be detected in
+ * waitfunc.
+ */
+static int fsl_ifc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required, int page)
+{
+ nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);
+ fsl_ifc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ return nand_prog_page_end_op(chip);
+}
+
+static int fsl_ifc_chip_init_tail(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
+
+ dev_dbg(priv->dev, "%s: nand->numchips = %d\n", __func__,
+ chip->numchips);
+ dev_dbg(priv->dev, "%s: nand->chipsize = %lld\n", __func__,
+ chip->chipsize);
+ dev_dbg(priv->dev, "%s: nand->pagemask = %8x\n", __func__,
+ chip->pagemask);
+ dev_dbg(priv->dev, "%s: nand->chip_delay = %d\n", __func__,
+ chip->chip_delay);
+ dev_dbg(priv->dev, "%s: nand->badblockpos = %d\n", __func__,
+ chip->badblockpos);
+ dev_dbg(priv->dev, "%s: nand->chip_shift = %d\n", __func__,
+ chip->chip_shift);
+ dev_dbg(priv->dev, "%s: nand->page_shift = %d\n", __func__,
+ chip->page_shift);
+ dev_dbg(priv->dev, "%s: nand->phys_erase_shift = %d\n", __func__,
+ chip->phys_erase_shift);
+ dev_dbg(priv->dev, "%s: nand->ecc.mode = %d\n", __func__,
+ chip->ecc.mode);
+ dev_dbg(priv->dev, "%s: nand->ecc.steps = %d\n", __func__,
+ chip->ecc.steps);
+ dev_dbg(priv->dev, "%s: nand->ecc.bytes = %d\n", __func__,
+ chip->ecc.bytes);
+ dev_dbg(priv->dev, "%s: nand->ecc.total = %d\n", __func__,
+ chip->ecc.total);
+ dev_dbg(priv->dev, "%s: mtd->ooblayout = %p\n", __func__,
+ mtd->ooblayout);
+ dev_dbg(priv->dev, "%s: mtd->flags = %08x\n", __func__, mtd->flags);
+ dev_dbg(priv->dev, "%s: mtd->size = %lld\n", __func__, mtd->size);
+ dev_dbg(priv->dev, "%s: mtd->erasesize = %d\n", __func__,
+ mtd->erasesize);
+ dev_dbg(priv->dev, "%s: mtd->writesize = %d\n", __func__,
+ mtd->writesize);
+ dev_dbg(priv->dev, "%s: mtd->oobsize = %d\n", __func__,
+ mtd->oobsize);
+
+ return 0;
+}
+
+static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv)
+{
+ struct fsl_ifc_ctrl *ctrl = priv->ctrl;
+ struct fsl_ifc_runtime __iomem *ifc_runtime = ctrl->rregs;
+ struct fsl_ifc_global __iomem *ifc_global = ctrl->gregs;
+ uint32_t csor = 0, csor_8k = 0, csor_ext = 0;
+ uint32_t cs = priv->bank;
+
+ /* Save CSOR and CSOR_ext */
+ csor = ifc_in32(&ifc_global->csor_cs[cs].csor);
+ csor_ext = ifc_in32(&ifc_global->csor_cs[cs].csor_ext);
+
+ /* chage PageSize 8K and SpareSize 1K*/
+ csor_8k = (csor & ~(CSOR_NAND_PGS_MASK)) | 0x0018C000;
+ ifc_out32(csor_8k, &ifc_global->csor_cs[cs].csor);
+ ifc_out32(0x0000400, &ifc_global->csor_cs[cs].csor_ext);
+
+ /* READID */
+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) |
+ (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT),
+ &ifc_runtime->ifc_nand.nand_fir0);
+ ifc_out32(NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT,
+ &ifc_runtime->ifc_nand.nand_fcr0);
+ ifc_out32(0x0, &ifc_runtime->ifc_nand.row3);
+
+ ifc_out32(0x0, &ifc_runtime->ifc_nand.nand_fbcr);
+
+ /* Program ROW0/COL0 */
+ ifc_out32(0x0, &ifc_runtime->ifc_nand.row0);
+ ifc_out32(0x0, &ifc_runtime->ifc_nand.col0);
+
+ /* set the chip select for NAND Transaction */
+ ifc_out32(cs << IFC_NAND_CSEL_SHIFT,
+ &ifc_runtime->ifc_nand.nand_csel);
+
+ /* start read seq */
+ ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT,
+ &ifc_runtime->ifc_nand.nandseq_strt);
+
+ /* wait for command complete flag or timeout */
+ wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat,
+ msecs_to_jiffies(IFC_TIMEOUT_MSECS));
+
+ if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC)
+ pr_err("fsl-ifc: Failed to Initialise SRAM\n");
+
+ /* Restore CSOR and CSOR_ext */
+ ifc_out32(csor, &ifc_global->csor_cs[cs].csor);
+ ifc_out32(csor_ext, &ifc_global->csor_cs[cs].csor_ext);
+}
+
+static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
+{
+ struct fsl_ifc_ctrl *ctrl = priv->ctrl;
+ struct fsl_ifc_global __iomem *ifc_global = ctrl->gregs;
+ struct fsl_ifc_runtime __iomem *ifc_runtime = ctrl->rregs;
+ struct nand_chip *chip = &priv->chip;
+ struct mtd_info *mtd = nand_to_mtd(&priv->chip);
+ u32 csor;
+
+ /* Fill in fsl_ifc_mtd structure */
+ mtd->dev.parent = priv->dev;
+ nand_set_flash_node(chip, priv->dev->of_node);
+
+ /* fill in nand_chip structure */
+ /* set up function call table */
+ if ((ifc_in32(&ifc_global->cspr_cs[priv->bank].cspr))
+ & CSPR_PORT_SIZE_16)
+ chip->read_byte = fsl_ifc_read_byte16;
+ else
+ chip->read_byte = fsl_ifc_read_byte;
+
+ chip->write_buf = fsl_ifc_write_buf;
+ chip->read_buf = fsl_ifc_read_buf;
+ chip->select_chip = fsl_ifc_select_chip;
+ chip->cmdfunc = fsl_ifc_cmdfunc;
+ chip->waitfunc = fsl_ifc_wait;
+ chip->set_features = nand_get_set_features_notsupp;
+ chip->get_features = nand_get_set_features_notsupp;
+
+ chip->bbt_td = &bbt_main_descr;
+ chip->bbt_md = &bbt_mirror_descr;
+
+ ifc_out32(0x0, &ifc_runtime->ifc_nand.ncfgr);
+
+ /* set up nand options */
+ chip->bbt_options = NAND_BBT_USE_FLASH;
+ chip->options = NAND_NO_SUBPAGE_WRITE;
+
+ if (ifc_in32(&ifc_global->cspr_cs[priv->bank].cspr)
+ & CSPR_PORT_SIZE_16) {
+ chip->read_byte = fsl_ifc_read_byte16;
+ chip->options |= NAND_BUSWIDTH_16;
+ } else {
+ chip->read_byte = fsl_ifc_read_byte;
+ }
+
+ chip->controller = &ifc_nand_ctrl->controller;
+ nand_set_controller_data(chip, priv);
+
+ chip->ecc.read_page = fsl_ifc_read_page;
+ chip->ecc.write_page = fsl_ifc_write_page;
+
+ csor = ifc_in32(&ifc_global->csor_cs[priv->bank].csor);
+
+ switch (csor & CSOR_NAND_PGS_MASK) {
+ case CSOR_NAND_PGS_512:
+ if (!(chip->options & NAND_BUSWIDTH_16)) {
+ /* Avoid conflict with bad block marker */
+ bbt_main_descr.offs = 0;
+ bbt_mirror_descr.offs = 0;
+ }
+
+ priv->bufnum_mask = 15;
+ break;
+
+ case CSOR_NAND_PGS_2K:
+ priv->bufnum_mask = 3;
+ break;
+
+ case CSOR_NAND_PGS_4K:
+ priv->bufnum_mask = 1;
+ break;
+
+ case CSOR_NAND_PGS_8K:
+ priv->bufnum_mask = 0;
+ break;
+
+ default:
+ dev_err(priv->dev, "bad csor %#x: bad page size\n", csor);
+ return -ENODEV;
+ }
+
+ /* Must also set CSOR_NAND_ECC_ENC_EN if DEC_EN set */
+ if (csor & CSOR_NAND_ECC_DEC_EN) {
+ chip->ecc.mode = NAND_ECC_HW;
+ mtd_set_ooblayout(mtd, &fsl_ifc_ooblayout_ops);
+
+ /* Hardware generates ECC per 512 Bytes */
+ chip->ecc.size = 512;
+ if ((csor & CSOR_NAND_ECC_MODE_MASK) == CSOR_NAND_ECC_MODE_4) {
+ chip->ecc.bytes = 8;
+ chip->ecc.strength = 4;
+ } else {
+ chip->ecc.bytes = 16;
+ chip->ecc.strength = 8;
+ }
+ } else {
+ chip->ecc.mode = NAND_ECC_SOFT;
+ chip->ecc.algo = NAND_ECC_HAMMING;
+ }
+
+ if (ctrl->version >= FSL_IFC_VERSION_1_1_0)
+ fsl_ifc_sram_init(priv);
+
+ /*
+ * As IFC version 2.0.0 has 16KB of internal SRAM as compared to older
+ * versions which had 8KB. Hence bufnum mask needs to be updated.
+ */
+ if (ctrl->version >= FSL_IFC_VERSION_2_0_0)
+ priv->bufnum_mask = (priv->bufnum_mask * 2) + 1;
+
+ return 0;
+}
+
+static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv)
+{
+ struct mtd_info *mtd = nand_to_mtd(&priv->chip);
+
+ nand_release(mtd);
+
+ kfree(mtd->name);
+
+ if (priv->vbase)
+ iounmap(priv->vbase);
+
+ ifc_nand_ctrl->chips[priv->bank] = NULL;
+
+ return 0;
+}
+
+static int match_bank(struct fsl_ifc_global __iomem *ifc_global, int bank,
+ phys_addr_t addr)
+{
+ u32 cspr = ifc_in32(&ifc_global->cspr_cs[bank].cspr);
+
+ if (!(cspr & CSPR_V))
+ return 0;
+ if ((cspr & CSPR_MSEL) != CSPR_MSEL_NAND)
+ return 0;
+
+ return (cspr & CSPR_BA) == convert_ifc_address(addr);
+}
+
+static DEFINE_MUTEX(fsl_ifc_nand_mutex);
+
+static int fsl_ifc_nand_probe(struct platform_device *dev)
+{
+ struct fsl_ifc_runtime __iomem *ifc;
+ struct fsl_ifc_mtd *priv;
+ struct resource res;
+ static const char *part_probe_types[]
+ = { "cmdlinepart", "RedBoot", "ofpart", NULL };
+ int ret;
+ int bank;
+ struct device_node *node = dev->dev.of_node;
+ struct mtd_info *mtd;
+
+ if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->rregs)
+ return -ENODEV;
+ ifc = fsl_ifc_ctrl_dev->rregs;
+
+ /* get, allocate and map the memory resource */
+ ret = of_address_to_resource(node, 0, &res);
+ if (ret) {
+ dev_err(&dev->dev, "%s: failed to get resource\n", __func__);
+ return ret;
+ }
+
+ /* find which chip select it is connected to */
+ for (bank = 0; bank < fsl_ifc_ctrl_dev->banks; bank++) {
+ if (match_bank(fsl_ifc_ctrl_dev->gregs, bank, res.start))
+ break;
+ }
+
+ if (bank >= fsl_ifc_ctrl_dev->banks) {
+ dev_err(&dev->dev, "%s: address did not match any chip selects\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ mutex_lock(&fsl_ifc_nand_mutex);
+ if (!fsl_ifc_ctrl_dev->nand) {
+ ifc_nand_ctrl = kzalloc(sizeof(*ifc_nand_ctrl), GFP_KERNEL);
+ if (!ifc_nand_ctrl) {
+ mutex_unlock(&fsl_ifc_nand_mutex);
+ return -ENOMEM;
+ }
+
+ ifc_nand_ctrl->read_bytes = 0;
+ ifc_nand_ctrl->index = 0;
+ ifc_nand_ctrl->addr = NULL;
+ fsl_ifc_ctrl_dev->nand = ifc_nand_ctrl;
+
+ nand_hw_control_init(&ifc_nand_ctrl->controller);
+ } else {
+ ifc_nand_ctrl = fsl_ifc_ctrl_dev->nand;
+ }
+ mutex_unlock(&fsl_ifc_nand_mutex);
+
+ ifc_nand_ctrl->chips[bank] = priv;
+ priv->bank = bank;
+ priv->ctrl = fsl_ifc_ctrl_dev;
+ priv->dev = &dev->dev;
+
+ priv->vbase = ioremap(res.start, resource_size(&res));
+ if (!priv->vbase) {
+ dev_err(priv->dev, "%s: failed to map chip region\n", __func__);
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ dev_set_drvdata(priv->dev, priv);
+
+ ifc_out32(IFC_NAND_EVTER_EN_OPC_EN |
+ IFC_NAND_EVTER_EN_FTOER_EN |
+ IFC_NAND_EVTER_EN_WPER_EN,
+ &ifc->ifc_nand.nand_evter_en);
+
+ /* enable NAND Machine Interrupts */
+ ifc_out32(IFC_NAND_EVTER_INTR_OPCIR_EN |
+ IFC_NAND_EVTER_INTR_FTOERIR_EN |
+ IFC_NAND_EVTER_INTR_WPERIR_EN,
+ &ifc->ifc_nand.nand_evter_intr_en);
+
+ mtd = nand_to_mtd(&priv->chip);
+ mtd->name = kasprintf(GFP_KERNEL, "%llx.flash", (u64)res.start);
+ if (!mtd->name) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = fsl_ifc_chip_init(priv);
+ if (ret)
+ goto err;
+
+ ret = nand_scan_ident(mtd, 1, NULL);
+ if (ret)
+ goto err;
+
+ ret = fsl_ifc_chip_init_tail(mtd);
+ if (ret)
+ goto err;
+
+ ret = nand_scan_tail(mtd);
+ if (ret)
+ goto err;
+
+ /* First look for RedBoot table or partitions on the command
+ * line, these take precedence over device tree information */
+ mtd_device_parse_register(mtd, part_probe_types, NULL, NULL, 0);
+
+ dev_info(priv->dev, "IFC NAND device at 0x%llx, bank %d\n",
+ (unsigned long long)res.start, priv->bank);
+ return 0;
+
+err:
+ fsl_ifc_chip_remove(priv);
+ return ret;
+}
+
+static int fsl_ifc_nand_remove(struct platform_device *dev)
+{
+ struct fsl_ifc_mtd *priv = dev_get_drvdata(&dev->dev);
+
+ fsl_ifc_chip_remove(priv);
+
+ mutex_lock(&fsl_ifc_nand_mutex);
+ ifc_nand_ctrl->counter--;
+ if (!ifc_nand_ctrl->counter) {
+ fsl_ifc_ctrl_dev->nand = NULL;
+ kfree(ifc_nand_ctrl);
+ }
+ mutex_unlock(&fsl_ifc_nand_mutex);
+
+ return 0;
+}
+
+static const struct of_device_id fsl_ifc_nand_match[] = {
+ {
+ .compatible = "fsl,ifc-nand",
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, fsl_ifc_nand_match);
+
+static struct platform_driver fsl_ifc_nand_driver = {
+ .driver = {
+ .name = "fsl,ifc-nand",
+ .of_match_table = fsl_ifc_nand_match,
+ },
+ .probe = fsl_ifc_nand_probe,
+ .remove = fsl_ifc_nand_remove,
+};
+
+module_platform_driver(fsl_ifc_nand_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Freescale");
+MODULE_DESCRIPTION("Freescale Integrated Flash Controller MTD NAND driver");
diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/raw/fsl_upm.c
index a88e2cf66e0f..a88e2cf66e0f 100644
--- a/drivers/mtd/nand/fsl_upm.c
+++ b/drivers/mtd/nand/raw/fsl_upm.c
diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c
new file mode 100644
index 000000000000..28c48dcc514e
--- /dev/null
+++ b/drivers/mtd/nand/raw/fsmc_nand.c
@@ -0,0 +1,1207 @@
+/*
+ * ST Microelectronics
+ * Flexible Static Memory Controller (FSMC)
+ * Driver for NAND portions
+ *
+ * Copyright © 2010 ST Microelectronics
+ * Vipin Kumar <vipin.kumar@st.com>
+ * Ashish Priyadarshi
+ *
+ * Based on drivers/mtd/nand/nomadik_nand.c (removed in v3.8)
+ * Copyright © 2007 STMicroelectronics Pvt. Ltd.
+ * Copyright © 2009 Alessandro Rubini
+ *
+ * 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/completion.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-direction.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/resource.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/mtd/partitions.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/amba/bus.h>
+#include <mtd/mtd-abi.h>
+
+/* fsmc controller registers for NOR flash */
+#define CTRL 0x0
+ /* ctrl register definitions */
+ #define BANK_ENABLE (1 << 0)
+ #define MUXED (1 << 1)
+ #define NOR_DEV (2 << 2)
+ #define WIDTH_8 (0 << 4)
+ #define WIDTH_16 (1 << 4)
+ #define RSTPWRDWN (1 << 6)
+ #define WPROT (1 << 7)
+ #define WRT_ENABLE (1 << 12)
+ #define WAIT_ENB (1 << 13)
+
+#define CTRL_TIM 0x4
+ /* ctrl_tim register definitions */
+
+#define FSMC_NOR_BANK_SZ 0x8
+#define FSMC_NOR_REG_SIZE 0x40
+
+#define FSMC_NOR_REG(base, bank, reg) (base + \
+ FSMC_NOR_BANK_SZ * (bank) + \
+ reg)
+
+/* fsmc controller registers for NAND flash */
+#define PC 0x00
+ /* pc register definitions */
+ #define FSMC_RESET (1 << 0)
+ #define FSMC_WAITON (1 << 1)
+ #define FSMC_ENABLE (1 << 2)
+ #define FSMC_DEVTYPE_NAND (1 << 3)
+ #define FSMC_DEVWID_8 (0 << 4)
+ #define FSMC_DEVWID_16 (1 << 4)
+ #define FSMC_ECCEN (1 << 6)
+ #define FSMC_ECCPLEN_512 (0 << 7)
+ #define FSMC_ECCPLEN_256 (1 << 7)
+ #define FSMC_TCLR_1 (1)
+ #define FSMC_TCLR_SHIFT (9)
+ #define FSMC_TCLR_MASK (0xF)
+ #define FSMC_TAR_1 (1)
+ #define FSMC_TAR_SHIFT (13)
+ #define FSMC_TAR_MASK (0xF)
+#define STS 0x04
+ /* sts register definitions */
+ #define FSMC_CODE_RDY (1 << 15)
+#define COMM 0x08
+ /* comm register definitions */
+ #define FSMC_TSET_0 0
+ #define FSMC_TSET_SHIFT 0
+ #define FSMC_TSET_MASK 0xFF
+ #define FSMC_TWAIT_6 6
+ #define FSMC_TWAIT_SHIFT 8
+ #define FSMC_TWAIT_MASK 0xFF
+ #define FSMC_THOLD_4 4
+ #define FSMC_THOLD_SHIFT 16
+ #define FSMC_THOLD_MASK 0xFF
+ #define FSMC_THIZ_1 1
+ #define FSMC_THIZ_SHIFT 24
+ #define FSMC_THIZ_MASK 0xFF
+#define ATTRIB 0x0C
+#define IOATA 0x10
+#define ECC1 0x14
+#define ECC2 0x18
+#define ECC3 0x1C
+#define FSMC_NAND_BANK_SZ 0x20
+
+#define FSMC_BUSY_WAIT_TIMEOUT (1 * HZ)
+
+struct fsmc_nand_timings {
+ uint8_t tclr;
+ uint8_t tar;
+ uint8_t thiz;
+ uint8_t thold;
+ uint8_t twait;
+ uint8_t tset;
+};
+
+enum access_mode {
+ USE_DMA_ACCESS = 1,
+ USE_WORD_ACCESS,
+};
+
+/**
+ * struct fsmc_nand_data - structure for FSMC NAND device state
+ *
+ * @pid: Part ID on the AMBA PrimeCell format
+ * @mtd: MTD info for a NAND flash.
+ * @nand: Chip related info for a NAND flash.
+ * @partitions: Partition info for a NAND Flash.
+ * @nr_partitions: Total number of partition of a NAND flash.
+ *
+ * @bank: Bank number for probed device.
+ * @clk: Clock structure for FSMC.
+ *
+ * @read_dma_chan: DMA channel for read access
+ * @write_dma_chan: DMA channel for write access to NAND
+ * @dma_access_complete: Completion structure
+ *
+ * @data_pa: NAND Physical port for Data.
+ * @data_va: NAND port for Data.
+ * @cmd_va: NAND port for Command.
+ * @addr_va: NAND port for Address.
+ * @regs_va: Registers base address for a given bank.
+ */
+struct fsmc_nand_data {
+ u32 pid;
+ struct nand_chip nand;
+
+ unsigned int bank;
+ struct device *dev;
+ enum access_mode mode;
+ struct clk *clk;
+
+ /* DMA related objects */
+ struct dma_chan *read_dma_chan;
+ struct dma_chan *write_dma_chan;
+ struct completion dma_access_complete;
+
+ struct fsmc_nand_timings *dev_timings;
+
+ dma_addr_t data_pa;
+ void __iomem *data_va;
+ void __iomem *cmd_va;
+ void __iomem *addr_va;
+ void __iomem *regs_va;
+};
+
+static int fsmc_ecc1_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (section >= chip->ecc.steps)
+ return -ERANGE;
+
+ oobregion->offset = (section * 16) + 2;
+ oobregion->length = 3;
+
+ return 0;
+}
+
+static int fsmc_ecc1_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (section >= chip->ecc.steps)
+ return -ERANGE;
+
+ oobregion->offset = (section * 16) + 8;
+
+ if (section < chip->ecc.steps - 1)
+ oobregion->length = 8;
+ else
+ oobregion->length = mtd->oobsize - oobregion->offset;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops fsmc_ecc1_ooblayout_ops = {
+ .ecc = fsmc_ecc1_ooblayout_ecc,
+ .free = fsmc_ecc1_ooblayout_free,
+};
+
+/*
+ * ECC placement definitions in oobfree type format.
+ * There are 13 bytes of ecc for every 512 byte block and it has to be read
+ * consecutively and immediately after the 512 byte data block for hardware to
+ * generate the error bit offsets in 512 byte data.
+ */
+static int fsmc_ecc4_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (section >= chip->ecc.steps)
+ return -ERANGE;
+
+ oobregion->length = chip->ecc.bytes;
+
+ if (!section && mtd->writesize <= 512)
+ oobregion->offset = 0;
+ else
+ oobregion->offset = (section * 16) + 2;
+
+ return 0;
+}
+
+static int fsmc_ecc4_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (section >= chip->ecc.steps)
+ return -ERANGE;
+
+ oobregion->offset = (section * 16) + 15;
+
+ if (section < chip->ecc.steps - 1)
+ oobregion->length = 3;
+ else
+ oobregion->length = mtd->oobsize - oobregion->offset;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops fsmc_ecc4_ooblayout_ops = {
+ .ecc = fsmc_ecc4_ooblayout_ecc,
+ .free = fsmc_ecc4_ooblayout_free,
+};
+
+static inline struct fsmc_nand_data *mtd_to_fsmc(struct mtd_info *mtd)
+{
+ return container_of(mtd_to_nand(mtd), struct fsmc_nand_data, nand);
+}
+
+/*
+ * fsmc_nand_setup - FSMC (Flexible Static Memory Controller) init routine
+ *
+ * This routine initializes timing parameters related to NAND memory access in
+ * FSMC registers
+ */
+static void fsmc_nand_setup(struct fsmc_nand_data *host,
+ struct fsmc_nand_timings *tims)
+{
+ uint32_t value = FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON;
+ uint32_t tclr, tar, thiz, thold, twait, tset;
+
+ tclr = (tims->tclr & FSMC_TCLR_MASK) << FSMC_TCLR_SHIFT;
+ tar = (tims->tar & FSMC_TAR_MASK) << FSMC_TAR_SHIFT;
+ thiz = (tims->thiz & FSMC_THIZ_MASK) << FSMC_THIZ_SHIFT;
+ thold = (tims->thold & FSMC_THOLD_MASK) << FSMC_THOLD_SHIFT;
+ twait = (tims->twait & FSMC_TWAIT_MASK) << FSMC_TWAIT_SHIFT;
+ tset = (tims->tset & FSMC_TSET_MASK) << FSMC_TSET_SHIFT;
+
+ if (host->nand.options & NAND_BUSWIDTH_16)
+ writel_relaxed(value | FSMC_DEVWID_16, host->regs_va + PC);
+ else
+ writel_relaxed(value | FSMC_DEVWID_8, host->regs_va + PC);
+
+ writel_relaxed(readl(host->regs_va + PC) | tclr | tar,
+ host->regs_va + PC);
+ writel_relaxed(thiz | thold | twait | tset, host->regs_va + COMM);
+ writel_relaxed(thiz | thold | twait | tset, host->regs_va + ATTRIB);
+}
+
+static int fsmc_calc_timings(struct fsmc_nand_data *host,
+ const struct nand_sdr_timings *sdrt,
+ struct fsmc_nand_timings *tims)
+{
+ unsigned long hclk = clk_get_rate(host->clk);
+ unsigned long hclkn = NSEC_PER_SEC / hclk;
+ uint32_t thiz, thold, twait, tset;
+
+ if (sdrt->tRC_min < 30000)
+ return -EOPNOTSUPP;
+
+ tims->tar = DIV_ROUND_UP(sdrt->tAR_min / 1000, hclkn) - 1;
+ if (tims->tar > FSMC_TAR_MASK)
+ tims->tar = FSMC_TAR_MASK;
+ tims->tclr = DIV_ROUND_UP(sdrt->tCLR_min / 1000, hclkn) - 1;
+ if (tims->tclr > FSMC_TCLR_MASK)
+ tims->tclr = FSMC_TCLR_MASK;
+
+ thiz = sdrt->tCS_min - sdrt->tWP_min;
+ tims->thiz = DIV_ROUND_UP(thiz / 1000, hclkn);
+
+ thold = sdrt->tDH_min;
+ if (thold < sdrt->tCH_min)
+ thold = sdrt->tCH_min;
+ if (thold < sdrt->tCLH_min)
+ thold = sdrt->tCLH_min;
+ if (thold < sdrt->tWH_min)
+ thold = sdrt->tWH_min;
+ if (thold < sdrt->tALH_min)
+ thold = sdrt->tALH_min;
+ if (thold < sdrt->tREH_min)
+ thold = sdrt->tREH_min;
+ tims->thold = DIV_ROUND_UP(thold / 1000, hclkn);
+ if (tims->thold == 0)
+ tims->thold = 1;
+ else if (tims->thold > FSMC_THOLD_MASK)
+ tims->thold = FSMC_THOLD_MASK;
+
+ twait = max(sdrt->tRP_min, sdrt->tWP_min);
+ tims->twait = DIV_ROUND_UP(twait / 1000, hclkn) - 1;
+ if (tims->twait == 0)
+ tims->twait = 1;
+ else if (tims->twait > FSMC_TWAIT_MASK)
+ tims->twait = FSMC_TWAIT_MASK;
+
+ tset = max(sdrt->tCS_min - sdrt->tWP_min,
+ sdrt->tCEA_max - sdrt->tREA_max);
+ tims->tset = DIV_ROUND_UP(tset / 1000, hclkn) - 1;
+ if (tims->tset == 0)
+ tims->tset = 1;
+ else if (tims->tset > FSMC_TSET_MASK)
+ tims->tset = FSMC_TSET_MASK;
+
+ return 0;
+}
+
+static int fsmc_setup_data_interface(struct mtd_info *mtd, int csline,
+ const struct nand_data_interface *conf)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct fsmc_nand_data *host = nand_get_controller_data(nand);
+ struct fsmc_nand_timings tims;
+ const struct nand_sdr_timings *sdrt;
+ int ret;
+
+ sdrt = nand_get_sdr_timings(conf);
+ if (IS_ERR(sdrt))
+ return PTR_ERR(sdrt);
+
+ ret = fsmc_calc_timings(host, sdrt, &tims);
+ if (ret)
+ return ret;
+
+ if (csline == NAND_DATA_IFACE_CHECK_ONLY)
+ return 0;
+
+ fsmc_nand_setup(host, &tims);
+
+ return 0;
+}
+
+/*
+ * fsmc_enable_hwecc - Enables Hardware ECC through FSMC registers
+ */
+static void fsmc_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+ struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
+
+ writel_relaxed(readl(host->regs_va + PC) & ~FSMC_ECCPLEN_256,
+ host->regs_va + PC);
+ writel_relaxed(readl(host->regs_va + PC) & ~FSMC_ECCEN,
+ host->regs_va + PC);
+ writel_relaxed(readl(host->regs_va + PC) | FSMC_ECCEN,
+ host->regs_va + PC);
+}
+
+/*
+ * fsmc_read_hwecc_ecc4 - Hardware ECC calculator for ecc4 option supported by
+ * FSMC. ECC is 13 bytes for 512 bytes of data (supports error correction up to
+ * max of 8-bits)
+ */
+static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data,
+ uint8_t *ecc)
+{
+ struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
+ uint32_t ecc_tmp;
+ unsigned long deadline = jiffies + FSMC_BUSY_WAIT_TIMEOUT;
+
+ do {
+ if (readl_relaxed(host->regs_va + STS) & FSMC_CODE_RDY)
+ break;
+ else
+ cond_resched();
+ } while (!time_after_eq(jiffies, deadline));
+
+ if (time_after_eq(jiffies, deadline)) {
+ dev_err(host->dev, "calculate ecc timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ ecc_tmp = readl_relaxed(host->regs_va + ECC1);
+ ecc[0] = (uint8_t) (ecc_tmp >> 0);
+ ecc[1] = (uint8_t) (ecc_tmp >> 8);
+ ecc[2] = (uint8_t) (ecc_tmp >> 16);
+ ecc[3] = (uint8_t) (ecc_tmp >> 24);
+
+ ecc_tmp = readl_relaxed(host->regs_va + ECC2);
+ ecc[4] = (uint8_t) (ecc_tmp >> 0);
+ ecc[5] = (uint8_t) (ecc_tmp >> 8);
+ ecc[6] = (uint8_t) (ecc_tmp >> 16);
+ ecc[7] = (uint8_t) (ecc_tmp >> 24);
+
+ ecc_tmp = readl_relaxed(host->regs_va + ECC3);
+ ecc[8] = (uint8_t) (ecc_tmp >> 0);
+ ecc[9] = (uint8_t) (ecc_tmp >> 8);
+ ecc[10] = (uint8_t) (ecc_tmp >> 16);
+ ecc[11] = (uint8_t) (ecc_tmp >> 24);
+
+ ecc_tmp = readl_relaxed(host->regs_va + STS);
+ ecc[12] = (uint8_t) (ecc_tmp >> 16);
+
+ return 0;
+}
+
+/*
+ * fsmc_read_hwecc_ecc1 - Hardware ECC calculator for ecc1 option supported by
+ * FSMC. ECC is 3 bytes for 512 bytes of data (supports error correction up to
+ * max of 1-bit)
+ */
+static int fsmc_read_hwecc_ecc1(struct mtd_info *mtd, const uint8_t *data,
+ uint8_t *ecc)
+{
+ struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
+ uint32_t ecc_tmp;
+
+ ecc_tmp = readl_relaxed(host->regs_va + ECC1);
+ ecc[0] = (uint8_t) (ecc_tmp >> 0);
+ ecc[1] = (uint8_t) (ecc_tmp >> 8);
+ ecc[2] = (uint8_t) (ecc_tmp >> 16);
+
+ return 0;
+}
+
+/* Count the number of 0's in buff upto a max of max_bits */
+static int count_written_bits(uint8_t *buff, int size, int max_bits)
+{
+ int k, written_bits = 0;
+
+ for (k = 0; k < size; k++) {
+ written_bits += hweight8(~buff[k]);
+ if (written_bits > max_bits)
+ break;
+ }
+
+ return written_bits;
+}
+
+static void dma_complete(void *param)
+{
+ struct fsmc_nand_data *host = param;
+
+ complete(&host->dma_access_complete);
+}
+
+static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len,
+ enum dma_data_direction direction)
+{
+ struct dma_chan *chan;
+ struct dma_device *dma_dev;
+ struct dma_async_tx_descriptor *tx;
+ dma_addr_t dma_dst, dma_src, dma_addr;
+ dma_cookie_t cookie;
+ unsigned long flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
+ int ret;
+ unsigned long time_left;
+
+ if (direction == DMA_TO_DEVICE)
+ chan = host->write_dma_chan;
+ else if (direction == DMA_FROM_DEVICE)
+ chan = host->read_dma_chan;
+ else
+ return -EINVAL;
+
+ dma_dev = chan->device;
+ dma_addr = dma_map_single(dma_dev->dev, buffer, len, direction);
+
+ if (direction == DMA_TO_DEVICE) {
+ dma_src = dma_addr;
+ dma_dst = host->data_pa;
+ } else {
+ dma_src = host->data_pa;
+ dma_dst = dma_addr;
+ }
+
+ tx = dma_dev->device_prep_dma_memcpy(chan, dma_dst, dma_src,
+ len, flags);
+ if (!tx) {
+ dev_err(host->dev, "device_prep_dma_memcpy error\n");
+ ret = -EIO;
+ goto unmap_dma;
+ }
+
+ tx->callback = dma_complete;
+ tx->callback_param = host;
+ cookie = tx->tx_submit(tx);
+
+ ret = dma_submit_error(cookie);
+ if (ret) {
+ dev_err(host->dev, "dma_submit_error %d\n", cookie);
+ goto unmap_dma;
+ }
+
+ dma_async_issue_pending(chan);
+
+ time_left =
+ wait_for_completion_timeout(&host->dma_access_complete,
+ msecs_to_jiffies(3000));
+ if (time_left == 0) {
+ dmaengine_terminate_all(chan);
+ dev_err(host->dev, "wait_for_completion_timeout\n");
+ ret = -ETIMEDOUT;
+ goto unmap_dma;
+ }
+
+ ret = 0;
+
+unmap_dma:
+ dma_unmap_single(dma_dev->dev, dma_addr, len, direction);
+
+ return ret;
+}
+
+/*
+ * fsmc_write_buf - write buffer to chip
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ */
+static void fsmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
+ int i;
+
+ if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) &&
+ IS_ALIGNED(len, sizeof(uint32_t))) {
+ uint32_t *p = (uint32_t *)buf;
+ len = len >> 2;
+ for (i = 0; i < len; i++)
+ writel_relaxed(p[i], host->data_va);
+ } else {
+ for (i = 0; i < len; i++)
+ writeb_relaxed(buf[i], host->data_va);
+ }
+}
+
+/*
+ * fsmc_read_buf - read chip data into buffer
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
+ */
+static void fsmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
+ int i;
+
+ if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) &&
+ IS_ALIGNED(len, sizeof(uint32_t))) {
+ uint32_t *p = (uint32_t *)buf;
+ len = len >> 2;
+ for (i = 0; i < len; i++)
+ p[i] = readl_relaxed(host->data_va);
+ } else {
+ for (i = 0; i < len; i++)
+ buf[i] = readb_relaxed(host->data_va);
+ }
+}
+
+/*
+ * fsmc_read_buf_dma - read chip data into buffer
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
+ */
+static void fsmc_read_buf_dma(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
+
+ dma_xfer(host, buf, len, DMA_FROM_DEVICE);
+}
+
+/*
+ * fsmc_write_buf_dma - write buffer to chip
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ */
+static void fsmc_write_buf_dma(struct mtd_info *mtd, const uint8_t *buf,
+ int len)
+{
+ struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
+
+ dma_xfer(host, (void *)buf, len, DMA_TO_DEVICE);
+}
+
+/* fsmc_select_chip - assert or deassert nCE */
+static void fsmc_select_chip(struct mtd_info *mtd, int chipnr)
+{
+ struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
+ u32 pc;
+
+ /* Support only one CS */
+ if (chipnr > 0)
+ return;
+
+ pc = readl(host->regs_va + PC);
+ if (chipnr < 0)
+ writel_relaxed(pc & ~FSMC_ENABLE, host->regs_va + PC);
+ else
+ writel_relaxed(pc | FSMC_ENABLE, host->regs_va + PC);
+
+ /* nCE line must be asserted before starting any operation */
+ mb();
+}
+
+/*
+ * fsmc_exec_op - hook called by the core to execute NAND operations
+ *
+ * This controller is simple enough and thus does not need to use the parser
+ * provided by the core, instead, handle every situation here.
+ */
+static int fsmc_exec_op(struct nand_chip *chip, const struct nand_operation *op,
+ bool check_only)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
+ const struct nand_op_instr *instr = NULL;
+ int ret = 0;
+ unsigned int op_id;
+ int i;
+
+ pr_debug("Executing operation [%d instructions]:\n", op->ninstrs);
+ for (op_id = 0; op_id < op->ninstrs; op_id++) {
+ instr = &op->instrs[op_id];
+
+ switch (instr->type) {
+ case NAND_OP_CMD_INSTR:
+ pr_debug(" ->CMD [0x%02x]\n",
+ instr->ctx.cmd.opcode);
+
+ writeb_relaxed(instr->ctx.cmd.opcode, host->cmd_va);
+ break;
+
+ case NAND_OP_ADDR_INSTR:
+ pr_debug(" ->ADDR [%d cyc]",
+ instr->ctx.addr.naddrs);
+
+ for (i = 0; i < instr->ctx.addr.naddrs; i++)
+ writeb_relaxed(instr->ctx.addr.addrs[i],
+ host->addr_va);
+ break;
+
+ case NAND_OP_DATA_IN_INSTR:
+ pr_debug(" ->DATA_IN [%d B%s]\n", instr->ctx.data.len,
+ instr->ctx.data.force_8bit ?
+ ", force 8-bit" : "");
+
+ if (host->mode == USE_DMA_ACCESS)
+ fsmc_read_buf_dma(mtd, instr->ctx.data.buf.in,
+ instr->ctx.data.len);
+ else
+ fsmc_read_buf(mtd, instr->ctx.data.buf.in,
+ instr->ctx.data.len);
+ break;
+
+ case NAND_OP_DATA_OUT_INSTR:
+ pr_debug(" ->DATA_OUT [%d B%s]\n", instr->ctx.data.len,
+ instr->ctx.data.force_8bit ?
+ ", force 8-bit" : "");
+
+ if (host->mode == USE_DMA_ACCESS)
+ fsmc_write_buf_dma(mtd, instr->ctx.data.buf.out,
+ instr->ctx.data.len);
+ else
+ fsmc_write_buf(mtd, instr->ctx.data.buf.out,
+ instr->ctx.data.len);
+ break;
+
+ case NAND_OP_WAITRDY_INSTR:
+ pr_debug(" ->WAITRDY [max %d ms]\n",
+ instr->ctx.waitrdy.timeout_ms);
+
+ ret = nand_soft_waitrdy(chip,
+ instr->ctx.waitrdy.timeout_ms);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * fsmc_read_page_hwecc
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller expects OOB data read to chip->oob_poi
+ * @page: page number to read
+ *
+ * This routine is needed for fsmc version 8 as reading from NAND chip has to be
+ * performed in a strict sequence as follows:
+ * data(512 byte) -> ecc(13 byte)
+ * After this read, fsmc hardware generates and reports error data bits(up to a
+ * max of 8 bits)
+ */
+static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ int i, j, s, stat, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *p = buf;
+ uint8_t *ecc_calc = chip->ecc.calc_buf;
+ uint8_t *ecc_code = chip->ecc.code_buf;
+ int off, len, group = 0;
+ /*
+ * ecc_oob is intentionally taken as uint16_t. In 16bit devices, we
+ * end up reading 14 bytes (7 words) from oob. The local array is
+ * to maintain word alignment
+ */
+ uint16_t ecc_oob[7];
+ uint8_t *oob = (uint8_t *)&ecc_oob[0];
+ unsigned int max_bitflips = 0;
+
+ for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) {
+ nand_read_page_op(chip, page, s * eccsize, NULL, 0);
+ chip->ecc.hwctl(mtd, NAND_ECC_READ);
+ chip->read_buf(mtd, p, eccsize);
+
+ for (j = 0; j < eccbytes;) {
+ struct mtd_oob_region oobregion;
+ int ret;
+
+ ret = mtd_ooblayout_ecc(mtd, group++, &oobregion);
+ if (ret)
+ return ret;
+
+ off = oobregion.offset;
+ len = oobregion.length;
+
+ /*
+ * length is intentionally kept a higher multiple of 2
+ * to read at least 13 bytes even in case of 16 bit NAND
+ * devices
+ */
+ if (chip->options & NAND_BUSWIDTH_16)
+ len = roundup(len, 2);
+
+ nand_read_oob_op(chip, page, off, oob + j, len);
+ j += len;
+ }
+
+ memcpy(&ecc_code[i], oob, chip->ecc.bytes);
+ chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+
+ stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ }
+ }
+
+ return max_bitflips;
+}
+
+/*
+ * fsmc_bch8_correct_data
+ * @mtd: mtd info structure
+ * @dat: buffer of read data
+ * @read_ecc: ecc read from device spare area
+ * @calc_ecc: ecc calculated from read data
+ *
+ * calc_ecc is a 104 bit information containing maximum of 8 error
+ * offset informations of 13 bits each in 512 bytes of read data.
+ */
+static int fsmc_bch8_correct_data(struct mtd_info *mtd, uint8_t *dat,
+ uint8_t *read_ecc, uint8_t *calc_ecc)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
+ uint32_t err_idx[8];
+ uint32_t num_err, i;
+ uint32_t ecc1, ecc2, ecc3, ecc4;
+
+ num_err = (readl_relaxed(host->regs_va + STS) >> 10) & 0xF;
+
+ /* no bit flipping */
+ if (likely(num_err == 0))
+ return 0;
+
+ /* too many errors */
+ if (unlikely(num_err > 8)) {
+ /*
+ * This is a temporary erase check. A newly erased page read
+ * would result in an ecc error because the oob data is also
+ * erased to FF and the calculated ecc for an FF data is not
+ * FF..FF.
+ * This is a workaround to skip performing correction in case
+ * data is FF..FF
+ *
+ * Logic:
+ * For every page, each bit written as 0 is counted until these
+ * number of bits are greater than 8 (the maximum correction
+ * capability of FSMC for each 512 + 13 bytes)
+ */
+
+ int bits_ecc = count_written_bits(read_ecc, chip->ecc.bytes, 8);
+ int bits_data = count_written_bits(dat, chip->ecc.size, 8);
+
+ if ((bits_ecc + bits_data) <= 8) {
+ if (bits_data)
+ memset(dat, 0xff, chip->ecc.size);
+ return bits_data;
+ }
+
+ return -EBADMSG;
+ }
+
+ /*
+ * ------------------- calc_ecc[] bit wise -----------|--13 bits--|
+ * |---idx[7]--|--.....-----|---idx[2]--||---idx[1]--||---idx[0]--|
+ *
+ * calc_ecc is a 104 bit information containing maximum of 8 error
+ * offset informations of 13 bits each. calc_ecc is copied into a
+ * uint64_t array and error offset indexes are populated in err_idx
+ * array
+ */
+ ecc1 = readl_relaxed(host->regs_va + ECC1);
+ ecc2 = readl_relaxed(host->regs_va + ECC2);
+ ecc3 = readl_relaxed(host->regs_va + ECC3);
+ ecc4 = readl_relaxed(host->regs_va + STS);
+
+ err_idx[0] = (ecc1 >> 0) & 0x1FFF;
+ err_idx[1] = (ecc1 >> 13) & 0x1FFF;
+ err_idx[2] = (((ecc2 >> 0) & 0x7F) << 6) | ((ecc1 >> 26) & 0x3F);
+ err_idx[3] = (ecc2 >> 7) & 0x1FFF;
+ err_idx[4] = (((ecc3 >> 0) & 0x1) << 12) | ((ecc2 >> 20) & 0xFFF);
+ err_idx[5] = (ecc3 >> 1) & 0x1FFF;
+ err_idx[6] = (ecc3 >> 14) & 0x1FFF;
+ err_idx[7] = (((ecc4 >> 16) & 0xFF) << 5) | ((ecc3 >> 27) & 0x1F);
+
+ i = 0;
+ while (num_err--) {
+ change_bit(0, (unsigned long *)&err_idx[i]);
+ change_bit(1, (unsigned long *)&err_idx[i]);
+
+ if (err_idx[i] < chip->ecc.size * 8) {
+ change_bit(err_idx[i], (unsigned long *)dat);
+ i++;
+ }
+ }
+ return i;
+}
+
+static bool filter(struct dma_chan *chan, void *slave)
+{
+ chan->private = slave;
+ return true;
+}
+
+static int fsmc_nand_probe_config_dt(struct platform_device *pdev,
+ struct fsmc_nand_data *host,
+ struct nand_chip *nand)
+{
+ struct device_node *np = pdev->dev.of_node;
+ u32 val;
+ int ret;
+
+ nand->options = 0;
+
+ if (!of_property_read_u32(np, "bank-width", &val)) {
+ if (val == 2) {
+ nand->options |= NAND_BUSWIDTH_16;
+ } else if (val != 1) {
+ dev_err(&pdev->dev, "invalid bank-width %u\n", val);
+ return -EINVAL;
+ }
+ }
+
+ if (of_get_property(np, "nand-skip-bbtscan", NULL))
+ nand->options |= NAND_SKIP_BBTSCAN;
+
+ host->dev_timings = devm_kzalloc(&pdev->dev,
+ sizeof(*host->dev_timings), GFP_KERNEL);
+ if (!host->dev_timings)
+ return -ENOMEM;
+ ret = of_property_read_u8_array(np, "timings", (u8 *)host->dev_timings,
+ sizeof(*host->dev_timings));
+ if (ret)
+ host->dev_timings = NULL;
+
+ /* Set default NAND bank to 0 */
+ host->bank = 0;
+ if (!of_property_read_u32(np, "bank", &val)) {
+ if (val > 3) {
+ dev_err(&pdev->dev, "invalid bank %u\n", val);
+ return -EINVAL;
+ }
+ host->bank = val;
+ }
+ return 0;
+}
+
+/*
+ * fsmc_nand_probe - Probe function
+ * @pdev: platform device structure
+ */
+static int __init fsmc_nand_probe(struct platform_device *pdev)
+{
+ struct fsmc_nand_data *host;
+ struct mtd_info *mtd;
+ struct nand_chip *nand;
+ struct resource *res;
+ void __iomem *base;
+ dma_cap_mask_t mask;
+ int ret = 0;
+ u32 pid;
+ int i;
+
+ /* Allocate memory for the device structure (and zero it) */
+ host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
+ if (!host)
+ return -ENOMEM;
+
+ nand = &host->nand;
+
+ ret = fsmc_nand_probe_config_dt(pdev, host, nand);
+ if (ret)
+ return ret;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data");
+ host->data_va = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(host->data_va))
+ return PTR_ERR(host->data_va);
+
+ host->data_pa = (dma_addr_t)res->start;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_addr");
+ host->addr_va = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(host->addr_va))
+ return PTR_ERR(host->addr_va);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_cmd");
+ host->cmd_va = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(host->cmd_va))
+ return PTR_ERR(host->cmd_va);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fsmc_regs");
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ host->regs_va = base + FSMC_NOR_REG_SIZE +
+ (host->bank * FSMC_NAND_BANK_SZ);
+
+ host->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(host->clk)) {
+ dev_err(&pdev->dev, "failed to fetch block clock\n");
+ return PTR_ERR(host->clk);
+ }
+
+ ret = clk_prepare_enable(host->clk);
+ if (ret)
+ return ret;
+
+ /*
+ * This device ID is actually a common AMBA ID as used on the
+ * AMBA PrimeCell bus. However it is not a PrimeCell.
+ */
+ for (pid = 0, i = 0; i < 4; i++)
+ pid |= (readl(base + resource_size(res) - 0x20 + 4 * i) & 255) << (i * 8);
+ host->pid = pid;
+ dev_info(&pdev->dev, "FSMC device partno %03x, manufacturer %02x, "
+ "revision %02x, config %02x\n",
+ AMBA_PART_BITS(pid), AMBA_MANF_BITS(pid),
+ AMBA_REV_BITS(pid), AMBA_CONFIG_BITS(pid));
+
+ host->dev = &pdev->dev;
+
+ if (host->mode == USE_DMA_ACCESS)
+ init_completion(&host->dma_access_complete);
+
+ /* Link all private pointers */
+ mtd = nand_to_mtd(&host->nand);
+ nand_set_controller_data(nand, host);
+ nand_set_flash_node(nand, pdev->dev.of_node);
+
+ mtd->dev.parent = &pdev->dev;
+ nand->exec_op = fsmc_exec_op;
+ nand->select_chip = fsmc_select_chip;
+ nand->chip_delay = 30;
+
+ /*
+ * Setup default ECC mode. nand_dt_init() called from nand_scan_ident()
+ * can overwrite this value if the DT provides a different value.
+ */
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.hwctl = fsmc_enable_hwecc;
+ nand->ecc.size = 512;
+ nand->badblockbits = 7;
+
+ if (host->mode == USE_DMA_ACCESS) {
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+ host->read_dma_chan = dma_request_channel(mask, filter, NULL);
+ if (!host->read_dma_chan) {
+ dev_err(&pdev->dev, "Unable to get read dma channel\n");
+ goto err_req_read_chnl;
+ }
+ host->write_dma_chan = dma_request_channel(mask, filter, NULL);
+ if (!host->write_dma_chan) {
+ dev_err(&pdev->dev, "Unable to get write dma channel\n");
+ goto err_req_write_chnl;
+ }
+ }
+
+ if (host->dev_timings)
+ fsmc_nand_setup(host, host->dev_timings);
+ else
+ nand->setup_data_interface = fsmc_setup_data_interface;
+
+ if (AMBA_REV_BITS(host->pid) >= 8) {
+ nand->ecc.read_page = fsmc_read_page_hwecc;
+ nand->ecc.calculate = fsmc_read_hwecc_ecc4;
+ nand->ecc.correct = fsmc_bch8_correct_data;
+ nand->ecc.bytes = 13;
+ nand->ecc.strength = 8;
+ }
+
+ /*
+ * Scan to find existence of the device
+ */
+ ret = nand_scan_ident(mtd, 1, NULL);
+ if (ret) {
+ dev_err(&pdev->dev, "No NAND Device found!\n");
+ goto err_scan_ident;
+ }
+
+ if (AMBA_REV_BITS(host->pid) >= 8) {
+ switch (mtd->oobsize) {
+ case 16:
+ case 64:
+ case 128:
+ case 224:
+ case 256:
+ break;
+ default:
+ dev_warn(&pdev->dev, "No oob scheme defined for oobsize %d\n",
+ mtd->oobsize);
+ ret = -EINVAL;
+ goto err_probe;
+ }
+
+ mtd_set_ooblayout(mtd, &fsmc_ecc4_ooblayout_ops);
+ } else {
+ switch (nand->ecc.mode) {
+ case NAND_ECC_HW:
+ dev_info(&pdev->dev, "Using 1-bit HW ECC scheme\n");
+ nand->ecc.calculate = fsmc_read_hwecc_ecc1;
+ nand->ecc.correct = nand_correct_data;
+ nand->ecc.bytes = 3;
+ nand->ecc.strength = 1;
+ break;
+
+ case NAND_ECC_SOFT:
+ if (nand->ecc.algo == NAND_ECC_BCH) {
+ dev_info(&pdev->dev, "Using 4-bit SW BCH ECC scheme\n");
+ break;
+ }
+
+ case NAND_ECC_ON_DIE:
+ break;
+
+ default:
+ dev_err(&pdev->dev, "Unsupported ECC mode!\n");
+ goto err_probe;
+ }
+
+ /*
+ * Don't set layout for BCH4 SW ECC. This will be
+ * generated later in nand_bch_init() later.
+ */
+ if (nand->ecc.mode == NAND_ECC_HW) {
+ switch (mtd->oobsize) {
+ case 16:
+ case 64:
+ case 128:
+ mtd_set_ooblayout(mtd,
+ &fsmc_ecc1_ooblayout_ops);
+ break;
+ default:
+ dev_warn(&pdev->dev,
+ "No oob scheme defined for oobsize %d\n",
+ mtd->oobsize);
+ ret = -EINVAL;
+ goto err_probe;
+ }
+ }
+ }
+
+ /* Second stage of scan to fill MTD data-structures */
+ ret = nand_scan_tail(mtd);
+ if (ret)
+ goto err_probe;
+
+ mtd->name = "nand";
+ ret = mtd_device_register(mtd, NULL, 0);
+ if (ret)
+ goto err_probe;
+
+ platform_set_drvdata(pdev, host);
+ dev_info(&pdev->dev, "FSMC NAND driver registration successful\n");
+ return 0;
+
+err_probe:
+err_scan_ident:
+ if (host->mode == USE_DMA_ACCESS)
+ dma_release_channel(host->write_dma_chan);
+err_req_write_chnl:
+ if (host->mode == USE_DMA_ACCESS)
+ dma_release_channel(host->read_dma_chan);
+err_req_read_chnl:
+ clk_disable_unprepare(host->clk);
+ return ret;
+}
+
+/*
+ * Clean up routine
+ */
+static int fsmc_nand_remove(struct platform_device *pdev)
+{
+ struct fsmc_nand_data *host = platform_get_drvdata(pdev);
+
+ if (host) {
+ nand_release(nand_to_mtd(&host->nand));
+
+ if (host->mode == USE_DMA_ACCESS) {
+ dma_release_channel(host->write_dma_chan);
+ dma_release_channel(host->read_dma_chan);
+ }
+ clk_disable_unprepare(host->clk);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int fsmc_nand_suspend(struct device *dev)
+{
+ struct fsmc_nand_data *host = dev_get_drvdata(dev);
+ if (host)
+ clk_disable_unprepare(host->clk);
+ return 0;
+}
+
+static int fsmc_nand_resume(struct device *dev)
+{
+ struct fsmc_nand_data *host = dev_get_drvdata(dev);
+ if (host) {
+ clk_prepare_enable(host->clk);
+ if (host->dev_timings)
+ fsmc_nand_setup(host, host->dev_timings);
+ }
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(fsmc_nand_pm_ops, fsmc_nand_suspend, fsmc_nand_resume);
+
+static const struct of_device_id fsmc_nand_id_table[] = {
+ { .compatible = "st,spear600-fsmc-nand" },
+ { .compatible = "stericsson,fsmc-nand" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, fsmc_nand_id_table);
+
+static struct platform_driver fsmc_nand_driver = {
+ .remove = fsmc_nand_remove,
+ .driver = {
+ .name = "fsmc-nand",
+ .of_match_table = fsmc_nand_id_table,
+ .pm = &fsmc_nand_pm_ops,
+ },
+};
+
+module_platform_driver_probe(fsmc_nand_driver, fsmc_nand_probe);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vipin Kumar <vipin.kumar@st.com>, Ashish Priyadarshi");
+MODULE_DESCRIPTION("NAND driver for SPEAr Platforms");
diff --git a/drivers/mtd/nand/raw/gpio.c b/drivers/mtd/nand/raw/gpio.c
new file mode 100644
index 000000000000..2780af26d9ab
--- /dev/null
+++ b/drivers/mtd/nand/raw/gpio.c
@@ -0,0 +1,327 @@
+/*
+ * Updated, and converted to generic GPIO based driver by Russell King.
+ *
+ * Written by Ben Dooks <ben@simtec.co.uk>
+ * Based on 2.4 version by Mark Whittaker
+ *
+ * © 2004 Simtec Electronics
+ *
+ * Device driver for NAND flash that uses a memory mapped interface to
+ * read/write the NAND commands and data, and GPIO pins for control signals
+ * (the DT binding refers to this as "GPIO assisted NAND flash")
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/io.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/nand-gpio.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+struct gpiomtd {
+ void __iomem *io_sync;
+ struct nand_chip nand_chip;
+ struct gpio_nand_platdata plat;
+ struct gpio_desc *nce; /* Optional chip enable */
+ struct gpio_desc *cle;
+ struct gpio_desc *ale;
+ struct gpio_desc *rdy;
+ struct gpio_desc *nwp; /* Optional write protection */
+};
+
+static inline struct gpiomtd *gpio_nand_getpriv(struct mtd_info *mtd)
+{
+ return container_of(mtd_to_nand(mtd), struct gpiomtd, nand_chip);
+}
+
+
+#ifdef CONFIG_ARM
+/* gpio_nand_dosync()
+ *
+ * Make sure the GPIO state changes occur in-order with writes to NAND
+ * memory region.
+ * Needed on PXA due to bus-reordering within the SoC itself (see section on
+ * I/O ordering in PXA manual (section 2.3, p35)
+ */
+static void gpio_nand_dosync(struct gpiomtd *gpiomtd)
+{
+ unsigned long tmp;
+
+ if (gpiomtd->io_sync) {
+ /*
+ * Linux memory barriers don't cater for what's required here.
+ * What's required is what's here - a read from a separate
+ * region with a dependency on that read.
+ */
+ tmp = readl(gpiomtd->io_sync);
+ asm volatile("mov %1, %0\n" : "=r" (tmp) : "r" (tmp));
+ }
+}
+#else
+static inline void gpio_nand_dosync(struct gpiomtd *gpiomtd) {}
+#endif
+
+static void gpio_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+ struct gpiomtd *gpiomtd = gpio_nand_getpriv(mtd);
+
+ gpio_nand_dosync(gpiomtd);
+
+ if (ctrl & NAND_CTRL_CHANGE) {
+ if (gpiomtd->nce)
+ gpiod_set_value(gpiomtd->nce, !(ctrl & NAND_NCE));
+ gpiod_set_value(gpiomtd->cle, !!(ctrl & NAND_CLE));
+ gpiod_set_value(gpiomtd->ale, !!(ctrl & NAND_ALE));
+ gpio_nand_dosync(gpiomtd);
+ }
+ if (cmd == NAND_CMD_NONE)
+ return;
+
+ writeb(cmd, gpiomtd->nand_chip.IO_ADDR_W);
+ gpio_nand_dosync(gpiomtd);
+}
+
+static int gpio_nand_devready(struct mtd_info *mtd)
+{
+ struct gpiomtd *gpiomtd = gpio_nand_getpriv(mtd);
+
+ return gpiod_get_value(gpiomtd->rdy);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id gpio_nand_id_table[] = {
+ { .compatible = "gpio-control-nand" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, gpio_nand_id_table);
+
+static int gpio_nand_get_config_of(const struct device *dev,
+ struct gpio_nand_platdata *plat)
+{
+ u32 val;
+
+ if (!dev->of_node)
+ return -ENODEV;
+
+ if (!of_property_read_u32(dev->of_node, "bank-width", &val)) {
+ if (val == 2) {
+ plat->options |= NAND_BUSWIDTH_16;
+ } else if (val != 1) {
+ dev_err(dev, "invalid bank-width %u\n", val);
+ return -EINVAL;
+ }
+ }
+
+ if (!of_property_read_u32(dev->of_node, "chip-delay", &val))
+ plat->chip_delay = val;
+
+ return 0;
+}
+
+static struct resource *gpio_nand_get_io_sync_of(struct platform_device *pdev)
+{
+ struct resource *r;
+ u64 addr;
+
+ if (of_property_read_u64(pdev->dev.of_node,
+ "gpio-control-nand,io-sync-reg", &addr))
+ return NULL;
+
+ r = devm_kzalloc(&pdev->dev, sizeof(*r), GFP_KERNEL);
+ if (!r)
+ return NULL;
+
+ r->start = addr;
+ r->end = r->start + 0x3;
+ r->flags = IORESOURCE_MEM;
+
+ return r;
+}
+#else /* CONFIG_OF */
+static inline int gpio_nand_get_config_of(const struct device *dev,
+ struct gpio_nand_platdata *plat)
+{
+ return -ENOSYS;
+}
+
+static inline struct resource *
+gpio_nand_get_io_sync_of(struct platform_device *pdev)
+{
+ return NULL;
+}
+#endif /* CONFIG_OF */
+
+static inline int gpio_nand_get_config(const struct device *dev,
+ struct gpio_nand_platdata *plat)
+{
+ int ret = gpio_nand_get_config_of(dev, plat);
+
+ if (!ret)
+ return ret;
+
+ if (dev_get_platdata(dev)) {
+ memcpy(plat, dev_get_platdata(dev), sizeof(*plat));
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static inline struct resource *
+gpio_nand_get_io_sync(struct platform_device *pdev)
+{
+ struct resource *r = gpio_nand_get_io_sync_of(pdev);
+
+ if (r)
+ return r;
+
+ return platform_get_resource(pdev, IORESOURCE_MEM, 1);
+}
+
+static int gpio_nand_remove(struct platform_device *pdev)
+{
+ struct gpiomtd *gpiomtd = platform_get_drvdata(pdev);
+
+ nand_release(nand_to_mtd(&gpiomtd->nand_chip));
+
+ /* Enable write protection and disable the chip */
+ if (gpiomtd->nwp && !IS_ERR(gpiomtd->nwp))
+ gpiod_set_value(gpiomtd->nwp, 0);
+ if (gpiomtd->nce && !IS_ERR(gpiomtd->nce))
+ gpiod_set_value(gpiomtd->nce, 0);
+
+ return 0;
+}
+
+static int gpio_nand_probe(struct platform_device *pdev)
+{
+ struct gpiomtd *gpiomtd;
+ struct nand_chip *chip;
+ struct mtd_info *mtd;
+ struct resource *res;
+ struct device *dev = &pdev->dev;
+ int ret = 0;
+
+ if (!dev->of_node && !dev_get_platdata(dev))
+ return -EINVAL;
+
+ gpiomtd = devm_kzalloc(dev, sizeof(*gpiomtd), GFP_KERNEL);
+ if (!gpiomtd)
+ return -ENOMEM;
+
+ chip = &gpiomtd->nand_chip;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ chip->IO_ADDR_R = devm_ioremap_resource(dev, res);
+ if (IS_ERR(chip->IO_ADDR_R))
+ return PTR_ERR(chip->IO_ADDR_R);
+
+ res = gpio_nand_get_io_sync(pdev);
+ if (res) {
+ gpiomtd->io_sync = devm_ioremap_resource(dev, res);
+ if (IS_ERR(gpiomtd->io_sync))
+ return PTR_ERR(gpiomtd->io_sync);
+ }
+
+ ret = gpio_nand_get_config(dev, &gpiomtd->plat);
+ if (ret)
+ return ret;
+
+ /* Just enable the chip */
+ gpiomtd->nce = devm_gpiod_get_optional(dev, "nce", GPIOD_OUT_HIGH);
+ if (IS_ERR(gpiomtd->nce))
+ return PTR_ERR(gpiomtd->nce);
+
+ /* We disable write protection once we know probe() will succeed */
+ gpiomtd->nwp = devm_gpiod_get_optional(dev, "nwp", GPIOD_OUT_LOW);
+ if (IS_ERR(gpiomtd->nwp)) {
+ ret = PTR_ERR(gpiomtd->nwp);
+ goto out_ce;
+ }
+
+ gpiomtd->ale = devm_gpiod_get(dev, "ale", GPIOD_OUT_LOW);
+ if (IS_ERR(gpiomtd->ale)) {
+ ret = PTR_ERR(gpiomtd->ale);
+ goto out_ce;
+ }
+
+ gpiomtd->cle = devm_gpiod_get(dev, "cle", GPIOD_OUT_LOW);
+ if (IS_ERR(gpiomtd->cle)) {
+ ret = PTR_ERR(gpiomtd->cle);
+ goto out_ce;
+ }
+
+ gpiomtd->rdy = devm_gpiod_get_optional(dev, "rdy", GPIOD_IN);
+ if (IS_ERR(gpiomtd->rdy)) {
+ ret = PTR_ERR(gpiomtd->rdy);
+ goto out_ce;
+ }
+ /* Using RDY pin */
+ if (gpiomtd->rdy)
+ chip->dev_ready = gpio_nand_devready;
+
+ nand_set_flash_node(chip, pdev->dev.of_node);
+ chip->IO_ADDR_W = chip->IO_ADDR_R;
+ chip->ecc.mode = NAND_ECC_SOFT;
+ chip->ecc.algo = NAND_ECC_HAMMING;
+ chip->options = gpiomtd->plat.options;
+ chip->chip_delay = gpiomtd->plat.chip_delay;
+ chip->cmd_ctrl = gpio_nand_cmd_ctrl;
+
+ mtd = nand_to_mtd(chip);
+ mtd->dev.parent = dev;
+
+ platform_set_drvdata(pdev, gpiomtd);
+
+ /* Disable write protection, if wired up */
+ if (gpiomtd->nwp && !IS_ERR(gpiomtd->nwp))
+ gpiod_direction_output(gpiomtd->nwp, 1);
+
+ ret = nand_scan(mtd, 1);
+ if (ret)
+ goto err_wp;
+
+ if (gpiomtd->plat.adjust_parts)
+ gpiomtd->plat.adjust_parts(&gpiomtd->plat, mtd->size);
+
+ ret = mtd_device_register(mtd, gpiomtd->plat.parts,
+ gpiomtd->plat.num_parts);
+ if (!ret)
+ return 0;
+
+err_wp:
+ if (gpiomtd->nwp && !IS_ERR(gpiomtd->nwp))
+ gpiod_set_value(gpiomtd->nwp, 0);
+out_ce:
+ if (gpiomtd->nce && !IS_ERR(gpiomtd->nce))
+ gpiod_set_value(gpiomtd->nce, 0);
+
+ return ret;
+}
+
+static struct platform_driver gpio_nand_driver = {
+ .probe = gpio_nand_probe,
+ .remove = gpio_nand_remove,
+ .driver = {
+ .name = "gpio-nand",
+ .of_match_table = of_match_ptr(gpio_nand_id_table),
+ },
+};
+
+module_platform_driver(gpio_nand_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
+MODULE_DESCRIPTION("GPIO NAND Driver");
diff --git a/drivers/mtd/nand/gpmi-nand/Makefile b/drivers/mtd/nand/raw/gpmi-nand/Makefile
index 3a462487c35e..3a462487c35e 100644
--- a/drivers/mtd/nand/gpmi-nand/Makefile
+++ b/drivers/mtd/nand/raw/gpmi-nand/Makefile
diff --git a/drivers/mtd/nand/gpmi-nand/bch-regs.h b/drivers/mtd/nand/raw/gpmi-nand/bch-regs.h
index 05bb91f2f4c4..05bb91f2f4c4 100644
--- a/drivers/mtd/nand/gpmi-nand/bch-regs.h
+++ b/drivers/mtd/nand/raw/gpmi-nand/bch-regs.h
diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c
new file mode 100644
index 000000000000..e94556705dc7
--- /dev/null
+++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c
@@ -0,0 +1,943 @@
+/*
+ * Freescale GPMI NAND Flash Driver
+ *
+ * Copyright (C) 2008-2011 Freescale Semiconductor, Inc.
+ * Copyright (C) 2008 Embedded Alley Solutions, Inc.
+ *
+ * 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/delay.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+
+#include "gpmi-nand.h"
+#include "gpmi-regs.h"
+#include "bch-regs.h"
+
+/* Converts time to clock cycles */
+#define TO_CYCLES(duration, period) DIV_ROUND_UP_ULL(duration, period)
+
+#define MXS_SET_ADDR 0x4
+#define MXS_CLR_ADDR 0x8
+/*
+ * Clear the bit and poll it cleared. This is usually called with
+ * a reset address and mask being either SFTRST(bit 31) or CLKGATE
+ * (bit 30).
+ */
+static int clear_poll_bit(void __iomem *addr, u32 mask)
+{
+ int timeout = 0x400;
+
+ /* clear the bit */
+ writel(mask, addr + MXS_CLR_ADDR);
+
+ /*
+ * SFTRST needs 3 GPMI clocks to settle, the reference manual
+ * recommends to wait 1us.
+ */
+ udelay(1);
+
+ /* poll the bit becoming clear */
+ while ((readl(addr) & mask) && --timeout)
+ /* nothing */;
+
+ return !timeout;
+}
+
+#define MODULE_CLKGATE (1 << 30)
+#define MODULE_SFTRST (1 << 31)
+/*
+ * The current mxs_reset_block() will do two things:
+ * [1] enable the module.
+ * [2] reset the module.
+ *
+ * In most of the cases, it's ok.
+ * But in MX23, there is a hardware bug in the BCH block (see erratum #2847).
+ * If you try to soft reset the BCH block, it becomes unusable until
+ * the next hard reset. This case occurs in the NAND boot mode. When the board
+ * boots by NAND, the ROM of the chip will initialize the BCH blocks itself.
+ * So If the driver tries to reset the BCH again, the BCH will not work anymore.
+ * You will see a DMA timeout in this case. The bug has been fixed
+ * in the following chips, such as MX28.
+ *
+ * To avoid this bug, just add a new parameter `just_enable` for
+ * the mxs_reset_block(), and rewrite it here.
+ */
+static int gpmi_reset_block(void __iomem *reset_addr, bool just_enable)
+{
+ int ret;
+ int timeout = 0x400;
+
+ /* clear and poll SFTRST */
+ ret = clear_poll_bit(reset_addr, MODULE_SFTRST);
+ if (unlikely(ret))
+ goto error;
+
+ /* clear CLKGATE */
+ writel(MODULE_CLKGATE, reset_addr + MXS_CLR_ADDR);
+
+ if (!just_enable) {
+ /* set SFTRST to reset the block */
+ writel(MODULE_SFTRST, reset_addr + MXS_SET_ADDR);
+ udelay(1);
+
+ /* poll CLKGATE becoming set */
+ while ((!(readl(reset_addr) & MODULE_CLKGATE)) && --timeout)
+ /* nothing */;
+ if (unlikely(!timeout))
+ goto error;
+ }
+
+ /* clear and poll SFTRST */
+ ret = clear_poll_bit(reset_addr, MODULE_SFTRST);
+ if (unlikely(ret))
+ goto error;
+
+ /* clear and poll CLKGATE */
+ ret = clear_poll_bit(reset_addr, MODULE_CLKGATE);
+ if (unlikely(ret))
+ goto error;
+
+ return 0;
+
+error:
+ pr_err("%s(%p): module reset timeout\n", __func__, reset_addr);
+ return -ETIMEDOUT;
+}
+
+static int __gpmi_enable_clk(struct gpmi_nand_data *this, bool v)
+{
+ struct clk *clk;
+ int ret;
+ int i;
+
+ for (i = 0; i < GPMI_CLK_MAX; i++) {
+ clk = this->resources.clock[i];
+ if (!clk)
+ break;
+
+ if (v) {
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ goto err_clk;
+ } else {
+ clk_disable_unprepare(clk);
+ }
+ }
+ return 0;
+
+err_clk:
+ for (; i > 0; i--)
+ clk_disable_unprepare(this->resources.clock[i - 1]);
+ return ret;
+}
+
+int gpmi_enable_clk(struct gpmi_nand_data *this)
+{
+ return __gpmi_enable_clk(this, true);
+}
+
+int gpmi_disable_clk(struct gpmi_nand_data *this)
+{
+ return __gpmi_enable_clk(this, false);
+}
+
+int gpmi_init(struct gpmi_nand_data *this)
+{
+ struct resources *r = &this->resources;
+ int ret;
+
+ ret = gpmi_enable_clk(this);
+ if (ret)
+ return ret;
+ ret = gpmi_reset_block(r->gpmi_regs, false);
+ if (ret)
+ goto err_out;
+
+ /*
+ * Reset BCH here, too. We got failures otherwise :(
+ * See later BCH reset for explanation of MX23 handling
+ */
+ ret = gpmi_reset_block(r->bch_regs, GPMI_IS_MX23(this));
+ if (ret)
+ goto err_out;
+
+ /* Choose NAND mode. */
+ writel(BM_GPMI_CTRL1_GPMI_MODE, r->gpmi_regs + HW_GPMI_CTRL1_CLR);
+
+ /* Set the IRQ polarity. */
+ writel(BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY,
+ r->gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ /* Disable Write-Protection. */
+ writel(BM_GPMI_CTRL1_DEV_RESET, r->gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ /* Select BCH ECC. */
+ writel(BM_GPMI_CTRL1_BCH_MODE, r->gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ /*
+ * Decouple the chip select from dma channel. We use dma0 for all
+ * the chips.
+ */
+ writel(BM_GPMI_CTRL1_DECOUPLE_CS, r->gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ gpmi_disable_clk(this);
+ return 0;
+err_out:
+ gpmi_disable_clk(this);
+ return ret;
+}
+
+/* This function is very useful. It is called only when the bug occur. */
+void gpmi_dump_info(struct gpmi_nand_data *this)
+{
+ struct resources *r = &this->resources;
+ struct bch_geometry *geo = &this->bch_geometry;
+ u32 reg;
+ int i;
+
+ dev_err(this->dev, "Show GPMI registers :\n");
+ for (i = 0; i <= HW_GPMI_DEBUG / 0x10 + 1; i++) {
+ reg = readl(r->gpmi_regs + i * 0x10);
+ dev_err(this->dev, "offset 0x%.3x : 0x%.8x\n", i * 0x10, reg);
+ }
+
+ /* start to print out the BCH info */
+ dev_err(this->dev, "Show BCH registers :\n");
+ for (i = 0; i <= HW_BCH_VERSION / 0x10 + 1; i++) {
+ reg = readl(r->bch_regs + i * 0x10);
+ dev_err(this->dev, "offset 0x%.3x : 0x%.8x\n", i * 0x10, reg);
+ }
+ dev_err(this->dev, "BCH Geometry :\n"
+ "GF length : %u\n"
+ "ECC Strength : %u\n"
+ "Page Size in Bytes : %u\n"
+ "Metadata Size in Bytes : %u\n"
+ "ECC Chunk Size in Bytes: %u\n"
+ "ECC Chunk Count : %u\n"
+ "Payload Size in Bytes : %u\n"
+ "Auxiliary Size in Bytes: %u\n"
+ "Auxiliary Status Offset: %u\n"
+ "Block Mark Byte Offset : %u\n"
+ "Block Mark Bit Offset : %u\n",
+ geo->gf_len,
+ geo->ecc_strength,
+ geo->page_size,
+ geo->metadata_size,
+ geo->ecc_chunk_size,
+ geo->ecc_chunk_count,
+ geo->payload_size,
+ geo->auxiliary_size,
+ geo->auxiliary_status_offset,
+ geo->block_mark_byte_offset,
+ geo->block_mark_bit_offset);
+}
+
+/* Configures the geometry for BCH. */
+int bch_set_geometry(struct gpmi_nand_data *this)
+{
+ struct resources *r = &this->resources;
+ struct bch_geometry *bch_geo = &this->bch_geometry;
+ unsigned int block_count;
+ unsigned int block_size;
+ unsigned int metadata_size;
+ unsigned int ecc_strength;
+ unsigned int page_size;
+ unsigned int gf_len;
+ int ret;
+
+ if (common_nfc_set_geometry(this))
+ return !0;
+
+ block_count = bch_geo->ecc_chunk_count - 1;
+ block_size = bch_geo->ecc_chunk_size;
+ metadata_size = bch_geo->metadata_size;
+ ecc_strength = bch_geo->ecc_strength >> 1;
+ page_size = bch_geo->page_size;
+ gf_len = bch_geo->gf_len;
+
+ ret = gpmi_enable_clk(this);
+ if (ret)
+ return ret;
+
+ /*
+ * Due to erratum #2847 of the MX23, the BCH cannot be soft reset on this
+ * chip, otherwise it will lock up. So we skip resetting BCH on the MX23.
+ * On the other hand, the MX28 needs the reset, because one case has been
+ * seen where the BCH produced ECC errors constantly after 10000
+ * consecutive reboots. The latter case has not been seen on the MX23
+ * yet, still we don't know if it could happen there as well.
+ */
+ ret = gpmi_reset_block(r->bch_regs, GPMI_IS_MX23(this));
+ if (ret)
+ goto err_out;
+
+ /* Configure layout 0. */
+ writel(BF_BCH_FLASH0LAYOUT0_NBLOCKS(block_count)
+ | BF_BCH_FLASH0LAYOUT0_META_SIZE(metadata_size)
+ | BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this)
+ | BF_BCH_FLASH0LAYOUT0_GF(gf_len, this)
+ | BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size, this),
+ r->bch_regs + HW_BCH_FLASH0LAYOUT0);
+
+ writel(BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size)
+ | BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this)
+ | BF_BCH_FLASH0LAYOUT1_GF(gf_len, this)
+ | BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size, this),
+ r->bch_regs + HW_BCH_FLASH0LAYOUT1);
+
+ /* Set *all* chip selects to use layout 0. */
+ writel(0, r->bch_regs + HW_BCH_LAYOUTSELECT);
+
+ /* Enable interrupts. */
+ writel(BM_BCH_CTRL_COMPLETE_IRQ_EN,
+ r->bch_regs + HW_BCH_CTRL_SET);
+
+ gpmi_disable_clk(this);
+ return 0;
+err_out:
+ gpmi_disable_clk(this);
+ return ret;
+}
+
+/*
+ * <1> Firstly, we should know what's the GPMI-clock means.
+ * The GPMI-clock is the internal clock in the gpmi nand controller.
+ * If you set 100MHz to gpmi nand controller, the GPMI-clock's period
+ * is 10ns. Mark the GPMI-clock's period as GPMI-clock-period.
+ *
+ * <2> Secondly, we should know what's the frequency on the nand chip pins.
+ * The frequency on the nand chip pins is derived from the GPMI-clock.
+ * We can get it from the following equation:
+ *
+ * F = G / (DS + DH)
+ *
+ * F : the frequency on the nand chip pins.
+ * G : the GPMI clock, such as 100MHz.
+ * DS : GPMI_HW_GPMI_TIMING0:DATA_SETUP
+ * DH : GPMI_HW_GPMI_TIMING0:DATA_HOLD
+ *
+ * <3> Thirdly, when the frequency on the nand chip pins is above 33MHz,
+ * the nand EDO(extended Data Out) timing could be applied.
+ * The GPMI implements a feedback read strobe to sample the read data.
+ * The feedback read strobe can be delayed to support the nand EDO timing
+ * where the read strobe may deasserts before the read data is valid, and
+ * read data is valid for some time after read strobe.
+ *
+ * The following figure illustrates some aspects of a NAND Flash read:
+ *
+ * |<---tREA---->|
+ * | |
+ * | | |
+ * |<--tRP-->| |
+ * | | |
+ * __ ___|__________________________________
+ * RDN \________/ |
+ * |
+ * /---------\
+ * Read Data --------------< >---------
+ * \---------/
+ * | |
+ * |<-D->|
+ * FeedbackRDN ________ ____________
+ * \___________/
+ *
+ * D stands for delay, set in the HW_GPMI_CTRL1:RDN_DELAY.
+ *
+ *
+ * <4> Now, we begin to describe how to compute the right RDN_DELAY.
+ *
+ * 4.1) From the aspect of the nand chip pins:
+ * Delay = (tREA + C - tRP) {1}
+ *
+ * tREA : the maximum read access time.
+ * C : a constant to adjust the delay. default is 4000ps.
+ * tRP : the read pulse width, which is exactly:
+ * tRP = (GPMI-clock-period) * DATA_SETUP
+ *
+ * 4.2) From the aspect of the GPMI nand controller:
+ * Delay = RDN_DELAY * 0.125 * RP {2}
+ *
+ * RP : the DLL reference period.
+ * if (GPMI-clock-period > DLL_THRETHOLD)
+ * RP = GPMI-clock-period / 2;
+ * else
+ * RP = GPMI-clock-period;
+ *
+ * Set the HW_GPMI_CTRL1:HALF_PERIOD if GPMI-clock-period
+ * is greater DLL_THRETHOLD. In other SOCs, the DLL_THRETHOLD
+ * is 16000ps, but in mx6q, we use 12000ps.
+ *
+ * 4.3) since {1} equals {2}, we get:
+ *
+ * (tREA + 4000 - tRP) * 8
+ * RDN_DELAY = ----------------------- {3}
+ * RP
+ */
+static void gpmi_nfc_compute_timings(struct gpmi_nand_data *this,
+ const struct nand_sdr_timings *sdr)
+{
+ struct gpmi_nfc_hardware_timing *hw = &this->hw;
+ unsigned int dll_threshold_ps = this->devdata->max_chain_delay;
+ unsigned int period_ps, reference_period_ps;
+ unsigned int data_setup_cycles, data_hold_cycles, addr_setup_cycles;
+ unsigned int tRP_ps;
+ bool use_half_period;
+ int sample_delay_ps, sample_delay_factor;
+ u16 busy_timeout_cycles;
+ u8 wrn_dly_sel;
+
+ if (sdr->tRC_min >= 30000) {
+ /* ONFI non-EDO modes [0-3] */
+ hw->clk_rate = 22000000;
+ wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS;
+ } else if (sdr->tRC_min >= 25000) {
+ /* ONFI EDO mode 4 */
+ hw->clk_rate = 80000000;
+ wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY;
+ } else {
+ /* ONFI EDO mode 5 */
+ hw->clk_rate = 100000000;
+ wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY;
+ }
+
+ /* SDR core timings are given in picoseconds */
+ period_ps = div_u64((u64)NSEC_PER_SEC * 1000, hw->clk_rate);
+
+ addr_setup_cycles = TO_CYCLES(sdr->tALS_min, period_ps);
+ data_setup_cycles = TO_CYCLES(sdr->tDS_min, period_ps);
+ data_hold_cycles = TO_CYCLES(sdr->tDH_min, period_ps);
+ busy_timeout_cycles = TO_CYCLES(sdr->tWB_max + sdr->tR_max, period_ps);
+
+ hw->timing0 = BF_GPMI_TIMING0_ADDRESS_SETUP(addr_setup_cycles) |
+ BF_GPMI_TIMING0_DATA_HOLD(data_hold_cycles) |
+ BF_GPMI_TIMING0_DATA_SETUP(data_setup_cycles);
+ hw->timing1 = BF_GPMI_TIMING1_BUSY_TIMEOUT(busy_timeout_cycles * 4096);
+
+ /*
+ * Derive NFC ideal delay from {3}:
+ *
+ * (tREA + 4000 - tRP) * 8
+ * RDN_DELAY = -----------------------
+ * RP
+ */
+ if (period_ps > dll_threshold_ps) {
+ use_half_period = true;
+ reference_period_ps = period_ps / 2;
+ } else {
+ use_half_period = false;
+ reference_period_ps = period_ps;
+ }
+
+ tRP_ps = data_setup_cycles * period_ps;
+ sample_delay_ps = (sdr->tREA_max + 4000 - tRP_ps) * 8;
+ if (sample_delay_ps > 0)
+ sample_delay_factor = sample_delay_ps / reference_period_ps;
+ else
+ sample_delay_factor = 0;
+
+ hw->ctrl1n = BF_GPMI_CTRL1_WRN_DLY_SEL(wrn_dly_sel);
+ if (sample_delay_factor)
+ hw->ctrl1n |= BF_GPMI_CTRL1_RDN_DELAY(sample_delay_factor) |
+ BM_GPMI_CTRL1_DLL_ENABLE |
+ (use_half_period ? BM_GPMI_CTRL1_HALF_PERIOD : 0);
+}
+
+void gpmi_nfc_apply_timings(struct gpmi_nand_data *this)
+{
+ struct gpmi_nfc_hardware_timing *hw = &this->hw;
+ struct resources *r = &this->resources;
+ void __iomem *gpmi_regs = r->gpmi_regs;
+ unsigned int dll_wait_time_us;
+
+ clk_set_rate(r->clock[0], hw->clk_rate);
+
+ writel(hw->timing0, gpmi_regs + HW_GPMI_TIMING0);
+ writel(hw->timing1, gpmi_regs + HW_GPMI_TIMING1);
+
+ /*
+ * Clear several CTRL1 fields, DLL must be disabled when setting
+ * RDN_DELAY or HALF_PERIOD.
+ */
+ writel(BM_GPMI_CTRL1_CLEAR_MASK, gpmi_regs + HW_GPMI_CTRL1_CLR);
+ writel(hw->ctrl1n, gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ /* Wait 64 clock cycles before using the GPMI after enabling the DLL */
+ dll_wait_time_us = USEC_PER_SEC / hw->clk_rate * 64;
+ if (!dll_wait_time_us)
+ dll_wait_time_us = 1;
+
+ /* Wait for the DLL to settle. */
+ udelay(dll_wait_time_us);
+}
+
+int gpmi_setup_data_interface(struct mtd_info *mtd, int chipnr,
+ const struct nand_data_interface *conf)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
+ const struct nand_sdr_timings *sdr;
+
+ /* Retrieve required NAND timings */
+ sdr = nand_get_sdr_timings(conf);
+ if (IS_ERR(sdr))
+ return PTR_ERR(sdr);
+
+ /* Only MX6 GPMI controller can reach EDO timings */
+ if (sdr->tRC_min <= 25000 && !GPMI_IS_MX6(this))
+ return -ENOTSUPP;
+
+ /* Stop here if this call was just a check */
+ if (chipnr < 0)
+ return 0;
+
+ /* Do the actual derivation of the controller timings */
+ gpmi_nfc_compute_timings(this, sdr);
+
+ this->hw.must_apply_timings = true;
+
+ return 0;
+}
+
+/* Clears a BCH interrupt. */
+void gpmi_clear_bch(struct gpmi_nand_data *this)
+{
+ struct resources *r = &this->resources;
+ writel(BM_BCH_CTRL_COMPLETE_IRQ, r->bch_regs + HW_BCH_CTRL_CLR);
+}
+
+/* Returns the Ready/Busy status of the given chip. */
+int gpmi_is_ready(struct gpmi_nand_data *this, unsigned chip)
+{
+ struct resources *r = &this->resources;
+ uint32_t mask = 0;
+ uint32_t reg = 0;
+
+ if (GPMI_IS_MX23(this)) {
+ mask = MX23_BM_GPMI_DEBUG_READY0 << chip;
+ reg = readl(r->gpmi_regs + HW_GPMI_DEBUG);
+ } else if (GPMI_IS_MX28(this) || GPMI_IS_MX6(this)) {
+ /*
+ * In the imx6, all the ready/busy pins are bound
+ * together. So we only need to check chip 0.
+ */
+ if (GPMI_IS_MX6(this))
+ chip = 0;
+
+ /* MX28 shares the same R/B register as MX6Q. */
+ mask = MX28_BF_GPMI_STAT_READY_BUSY(1 << chip);
+ reg = readl(r->gpmi_regs + HW_GPMI_STAT);
+ } else
+ dev_err(this->dev, "unknown arch.\n");
+ return reg & mask;
+}
+
+static inline void set_dma_type(struct gpmi_nand_data *this,
+ enum dma_ops_type type)
+{
+ this->last_dma_type = this->dma_type;
+ this->dma_type = type;
+}
+
+int gpmi_send_command(struct gpmi_nand_data *this)
+{
+ struct dma_chan *channel = get_dma_chan(this);
+ struct dma_async_tx_descriptor *desc;
+ struct scatterlist *sgl;
+ int chip = this->current_chip;
+ u32 pio[3];
+
+ /* [1] send out the PIO words */
+ pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__WRITE)
+ | BM_GPMI_CTRL0_WORD_LENGTH
+ | BF_GPMI_CTRL0_CS(chip, this)
+ | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
+ | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_CLE)
+ | BM_GPMI_CTRL0_ADDRESS_INCREMENT
+ | BF_GPMI_CTRL0_XFER_COUNT(this->command_length);
+ pio[1] = pio[2] = 0;
+ desc = dmaengine_prep_slave_sg(channel,
+ (struct scatterlist *)pio,
+ ARRAY_SIZE(pio), DMA_TRANS_NONE, 0);
+ if (!desc)
+ return -EINVAL;
+
+ /* [2] send out the COMMAND + ADDRESS string stored in @buffer */
+ sgl = &this->cmd_sgl;
+
+ sg_init_one(sgl, this->cmd_buffer, this->command_length);
+ dma_map_sg(this->dev, sgl, 1, DMA_TO_DEVICE);
+ desc = dmaengine_prep_slave_sg(channel,
+ sgl, 1, DMA_MEM_TO_DEV,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc)
+ return -EINVAL;
+
+ /* [3] submit the DMA */
+ set_dma_type(this, DMA_FOR_COMMAND);
+ return start_dma_without_bch_irq(this, desc);
+}
+
+int gpmi_send_data(struct gpmi_nand_data *this)
+{
+ struct dma_async_tx_descriptor *desc;
+ struct dma_chan *channel = get_dma_chan(this);
+ int chip = this->current_chip;
+ uint32_t command_mode;
+ uint32_t address;
+ u32 pio[2];
+
+ /* [1] PIO */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+
+ pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode)
+ | BM_GPMI_CTRL0_WORD_LENGTH
+ | BF_GPMI_CTRL0_CS(chip, this)
+ | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
+ | BF_GPMI_CTRL0_ADDRESS(address)
+ | BF_GPMI_CTRL0_XFER_COUNT(this->upper_len);
+ pio[1] = 0;
+ desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio,
+ ARRAY_SIZE(pio), DMA_TRANS_NONE, 0);
+ if (!desc)
+ return -EINVAL;
+
+ /* [2] send DMA request */
+ prepare_data_dma(this, DMA_TO_DEVICE);
+ desc = dmaengine_prep_slave_sg(channel, &this->data_sgl,
+ 1, DMA_MEM_TO_DEV,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc)
+ return -EINVAL;
+
+ /* [3] submit the DMA */
+ set_dma_type(this, DMA_FOR_WRITE_DATA);
+ return start_dma_without_bch_irq(this, desc);
+}
+
+int gpmi_read_data(struct gpmi_nand_data *this)
+{
+ struct dma_async_tx_descriptor *desc;
+ struct dma_chan *channel = get_dma_chan(this);
+ int chip = this->current_chip;
+ u32 pio[2];
+
+ /* [1] : send PIO */
+ pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__READ)
+ | BM_GPMI_CTRL0_WORD_LENGTH
+ | BF_GPMI_CTRL0_CS(chip, this)
+ | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
+ | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA)
+ | BF_GPMI_CTRL0_XFER_COUNT(this->upper_len);
+ pio[1] = 0;
+ desc = dmaengine_prep_slave_sg(channel,
+ (struct scatterlist *)pio,
+ ARRAY_SIZE(pio), DMA_TRANS_NONE, 0);
+ if (!desc)
+ return -EINVAL;
+
+ /* [2] : send DMA request */
+ prepare_data_dma(this, DMA_FROM_DEVICE);
+ desc = dmaengine_prep_slave_sg(channel, &this->data_sgl,
+ 1, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc)
+ return -EINVAL;
+
+ /* [3] : submit the DMA */
+ set_dma_type(this, DMA_FOR_READ_DATA);
+ return start_dma_without_bch_irq(this, desc);
+}
+
+int gpmi_send_page(struct gpmi_nand_data *this,
+ dma_addr_t payload, dma_addr_t auxiliary)
+{
+ struct bch_geometry *geo = &this->bch_geometry;
+ uint32_t command_mode;
+ uint32_t address;
+ uint32_t ecc_command;
+ uint32_t buffer_mask;
+ struct dma_async_tx_descriptor *desc;
+ struct dma_chan *channel = get_dma_chan(this);
+ int chip = this->current_chip;
+ u32 pio[6];
+
+ /* A DMA descriptor that does an ECC page read. */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+ ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE;
+ buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE |
+ BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY;
+
+ pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode)
+ | BM_GPMI_CTRL0_WORD_LENGTH
+ | BF_GPMI_CTRL0_CS(chip, this)
+ | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
+ | BF_GPMI_CTRL0_ADDRESS(address)
+ | BF_GPMI_CTRL0_XFER_COUNT(0);
+ pio[1] = 0;
+ pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC
+ | BF_GPMI_ECCCTRL_ECC_CMD(ecc_command)
+ | BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask);
+ pio[3] = geo->page_size;
+ pio[4] = payload;
+ pio[5] = auxiliary;
+
+ desc = dmaengine_prep_slave_sg(channel,
+ (struct scatterlist *)pio,
+ ARRAY_SIZE(pio), DMA_TRANS_NONE,
+ DMA_CTRL_ACK);
+ if (!desc)
+ return -EINVAL;
+
+ set_dma_type(this, DMA_FOR_WRITE_ECC_PAGE);
+ return start_dma_with_bch_irq(this, desc);
+}
+
+int gpmi_read_page(struct gpmi_nand_data *this,
+ dma_addr_t payload, dma_addr_t auxiliary)
+{
+ struct bch_geometry *geo = &this->bch_geometry;
+ uint32_t command_mode;
+ uint32_t address;
+ uint32_t ecc_command;
+ uint32_t buffer_mask;
+ struct dma_async_tx_descriptor *desc;
+ struct dma_chan *channel = get_dma_chan(this);
+ int chip = this->current_chip;
+ u32 pio[6];
+
+ /* [1] Wait for the chip to report ready. */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+
+ pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode)
+ | BM_GPMI_CTRL0_WORD_LENGTH
+ | BF_GPMI_CTRL0_CS(chip, this)
+ | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
+ | BF_GPMI_CTRL0_ADDRESS(address)
+ | BF_GPMI_CTRL0_XFER_COUNT(0);
+ pio[1] = 0;
+ desc = dmaengine_prep_slave_sg(channel,
+ (struct scatterlist *)pio, 2,
+ DMA_TRANS_NONE, 0);
+ if (!desc)
+ return -EINVAL;
+
+ /* [2] Enable the BCH block and read. */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__READ;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+ ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE;
+ buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE
+ | BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY;
+
+ pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode)
+ | BM_GPMI_CTRL0_WORD_LENGTH
+ | BF_GPMI_CTRL0_CS(chip, this)
+ | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
+ | BF_GPMI_CTRL0_ADDRESS(address)
+ | BF_GPMI_CTRL0_XFER_COUNT(geo->page_size);
+
+ pio[1] = 0;
+ pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC
+ | BF_GPMI_ECCCTRL_ECC_CMD(ecc_command)
+ | BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask);
+ pio[3] = geo->page_size;
+ pio[4] = payload;
+ pio[5] = auxiliary;
+ desc = dmaengine_prep_slave_sg(channel,
+ (struct scatterlist *)pio,
+ ARRAY_SIZE(pio), DMA_TRANS_NONE,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc)
+ return -EINVAL;
+
+ /* [3] Disable the BCH block */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+
+ pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode)
+ | BM_GPMI_CTRL0_WORD_LENGTH
+ | BF_GPMI_CTRL0_CS(chip, this)
+ | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
+ | BF_GPMI_CTRL0_ADDRESS(address)
+ | BF_GPMI_CTRL0_XFER_COUNT(geo->page_size);
+ pio[1] = 0;
+ pio[2] = 0; /* clear GPMI_HW_GPMI_ECCCTRL, disable the BCH. */
+ desc = dmaengine_prep_slave_sg(channel,
+ (struct scatterlist *)pio, 3,
+ DMA_TRANS_NONE,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc)
+ return -EINVAL;
+
+ /* [4] submit the DMA */
+ set_dma_type(this, DMA_FOR_READ_ECC_PAGE);
+ return start_dma_with_bch_irq(this, desc);
+}
+
+/**
+ * gpmi_copy_bits - copy bits from one memory region to another
+ * @dst: destination buffer
+ * @dst_bit_off: bit offset we're starting to write at
+ * @src: source buffer
+ * @src_bit_off: bit offset we're starting to read from
+ * @nbits: number of bits to copy
+ *
+ * This functions copies bits from one memory region to another, and is used by
+ * the GPMI driver to copy ECC sections which are not guaranteed to be byte
+ * aligned.
+ *
+ * src and dst should not overlap.
+ *
+ */
+void gpmi_copy_bits(u8 *dst, size_t dst_bit_off,
+ const u8 *src, size_t src_bit_off,
+ size_t nbits)
+{
+ size_t i;
+ size_t nbytes;
+ u32 src_buffer = 0;
+ size_t bits_in_src_buffer = 0;
+
+ if (!nbits)
+ return;
+
+ /*
+ * Move src and dst pointers to the closest byte pointer and store bit
+ * offsets within a byte.
+ */
+ src += src_bit_off / 8;
+ src_bit_off %= 8;
+
+ dst += dst_bit_off / 8;
+ dst_bit_off %= 8;
+
+ /*
+ * Initialize the src_buffer value with bits available in the first
+ * byte of data so that we end up with a byte aligned src pointer.
+ */
+ if (src_bit_off) {
+ src_buffer = src[0] >> src_bit_off;
+ if (nbits >= (8 - src_bit_off)) {
+ bits_in_src_buffer += 8 - src_bit_off;
+ } else {
+ src_buffer &= GENMASK(nbits - 1, 0);
+ bits_in_src_buffer += nbits;
+ }
+ nbits -= bits_in_src_buffer;
+ src++;
+ }
+
+ /* Calculate the number of bytes that can be copied from src to dst. */
+ nbytes = nbits / 8;
+
+ /* Try to align dst to a byte boundary. */
+ if (dst_bit_off) {
+ if (bits_in_src_buffer < (8 - dst_bit_off) && nbytes) {
+ src_buffer |= src[0] << bits_in_src_buffer;
+ bits_in_src_buffer += 8;
+ src++;
+ nbytes--;
+ }
+
+ if (bits_in_src_buffer >= (8 - dst_bit_off)) {
+ dst[0] &= GENMASK(dst_bit_off - 1, 0);
+ dst[0] |= src_buffer << dst_bit_off;
+ src_buffer >>= (8 - dst_bit_off);
+ bits_in_src_buffer -= (8 - dst_bit_off);
+ dst_bit_off = 0;
+ dst++;
+ if (bits_in_src_buffer > 7) {
+ bits_in_src_buffer -= 8;
+ dst[0] = src_buffer;
+ dst++;
+ src_buffer >>= 8;
+ }
+ }
+ }
+
+ if (!bits_in_src_buffer && !dst_bit_off) {
+ /*
+ * Both src and dst pointers are byte aligned, thus we can
+ * just use the optimized memcpy function.
+ */
+ if (nbytes)
+ memcpy(dst, src, nbytes);
+ } else {
+ /*
+ * src buffer is not byte aligned, hence we have to copy each
+ * src byte to the src_buffer variable before extracting a byte
+ * to store in dst.
+ */
+ for (i = 0; i < nbytes; i++) {
+ src_buffer |= src[i] << bits_in_src_buffer;
+ dst[i] = src_buffer;
+ src_buffer >>= 8;
+ }
+ }
+ /* Update dst and src pointers */
+ dst += nbytes;
+ src += nbytes;
+
+ /*
+ * nbits is the number of remaining bits. It should not exceed 8 as
+ * we've already copied as much bytes as possible.
+ */
+ nbits %= 8;
+
+ /*
+ * If there's no more bits to copy to the destination and src buffer
+ * was already byte aligned, then we're done.
+ */
+ if (!nbits && !bits_in_src_buffer)
+ return;
+
+ /* Copy the remaining bits to src_buffer */
+ if (nbits)
+ src_buffer |= (*src & GENMASK(nbits - 1, 0)) <<
+ bits_in_src_buffer;
+ bits_in_src_buffer += nbits;
+
+ /*
+ * In case there were not enough bits to get a byte aligned dst buffer
+ * prepare the src_buffer variable to match the dst organization (shift
+ * src_buffer by dst_bit_off and retrieve the least significant bits
+ * from dst).
+ */
+ if (dst_bit_off)
+ src_buffer = (src_buffer << dst_bit_off) |
+ (*dst & GENMASK(dst_bit_off - 1, 0));
+ bits_in_src_buffer += dst_bit_off;
+
+ /*
+ * Keep most significant bits from dst if we end up with an unaligned
+ * number of bits.
+ */
+ nbytes = bits_in_src_buffer / 8;
+ if (bits_in_src_buffer % 8) {
+ src_buffer |= (dst[nbytes] &
+ GENMASK(7, bits_in_src_buffer % 8)) <<
+ (nbytes * 8);
+ nbytes++;
+ }
+
+ /* Copy the remaining bytes to dst */
+ for (i = 0; i < nbytes; i++) {
+ dst[i] = src_buffer;
+ src_buffer >>= 8;
+ }
+}
diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c
new file mode 100644
index 000000000000..c2597c8107a0
--- /dev/null
+++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c
@@ -0,0 +1,2164 @@
+/*
+ * Freescale GPMI NAND Flash Driver
+ *
+ * Copyright (C) 2010-2015 Freescale Semiconductor, Inc.
+ * Copyright (C) 2008 Embedded Alley Solutions, Inc.
+ *
+ * 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/clk.h>
+#include <linux/slab.h>
+#include <linux/sched/task_stack.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/mtd/partitions.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include "gpmi-nand.h"
+#include "bch-regs.h"
+
+/* Resource names for the GPMI NAND driver. */
+#define GPMI_NAND_GPMI_REGS_ADDR_RES_NAME "gpmi-nand"
+#define GPMI_NAND_BCH_REGS_ADDR_RES_NAME "bch"
+#define GPMI_NAND_BCH_INTERRUPT_RES_NAME "bch"
+
+/* add our owner bbt descriptor */
+static uint8_t scan_ff_pattern[] = { 0xff };
+static struct nand_bbt_descr gpmi_bbt_descr = {
+ .options = 0,
+ .offs = 0,
+ .len = 1,
+ .pattern = scan_ff_pattern
+};
+
+/*
+ * We may change the layout if we can get the ECC info from the datasheet,
+ * else we will use all the (page + OOB).
+ */
+static int gpmi_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
+ struct bch_geometry *geo = &this->bch_geometry;
+
+ if (section)
+ return -ERANGE;
+
+ oobregion->offset = 0;
+ oobregion->length = geo->page_size - mtd->writesize;
+
+ return 0;
+}
+
+static int gpmi_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
+ struct bch_geometry *geo = &this->bch_geometry;
+
+ if (section)
+ return -ERANGE;
+
+ /* The available oob size we have. */
+ if (geo->page_size < mtd->writesize + mtd->oobsize) {
+ oobregion->offset = geo->page_size - mtd->writesize;
+ oobregion->length = mtd->oobsize - oobregion->offset;
+ }
+
+ return 0;
+}
+
+static const char * const gpmi_clks_for_mx2x[] = {
+ "gpmi_io",
+};
+
+static const struct mtd_ooblayout_ops gpmi_ooblayout_ops = {
+ .ecc = gpmi_ooblayout_ecc,
+ .free = gpmi_ooblayout_free,
+};
+
+static const struct gpmi_devdata gpmi_devdata_imx23 = {
+ .type = IS_MX23,
+ .bch_max_ecc_strength = 20,
+ .max_chain_delay = 16000,
+ .clks = gpmi_clks_for_mx2x,
+ .clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x),
+};
+
+static const struct gpmi_devdata gpmi_devdata_imx28 = {
+ .type = IS_MX28,
+ .bch_max_ecc_strength = 20,
+ .max_chain_delay = 16000,
+ .clks = gpmi_clks_for_mx2x,
+ .clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x),
+};
+
+static const char * const gpmi_clks_for_mx6[] = {
+ "gpmi_io", "gpmi_apb", "gpmi_bch", "gpmi_bch_apb", "per1_bch",
+};
+
+static const struct gpmi_devdata gpmi_devdata_imx6q = {
+ .type = IS_MX6Q,
+ .bch_max_ecc_strength = 40,
+ .max_chain_delay = 12000,
+ .clks = gpmi_clks_for_mx6,
+ .clks_count = ARRAY_SIZE(gpmi_clks_for_mx6),
+};
+
+static const struct gpmi_devdata gpmi_devdata_imx6sx = {
+ .type = IS_MX6SX,
+ .bch_max_ecc_strength = 62,
+ .max_chain_delay = 12000,
+ .clks = gpmi_clks_for_mx6,
+ .clks_count = ARRAY_SIZE(gpmi_clks_for_mx6),
+};
+
+static const char * const gpmi_clks_for_mx7d[] = {
+ "gpmi_io", "gpmi_bch_apb",
+};
+
+static const struct gpmi_devdata gpmi_devdata_imx7d = {
+ .type = IS_MX7D,
+ .bch_max_ecc_strength = 62,
+ .max_chain_delay = 12000,
+ .clks = gpmi_clks_for_mx7d,
+ .clks_count = ARRAY_SIZE(gpmi_clks_for_mx7d),
+};
+
+static irqreturn_t bch_irq(int irq, void *cookie)
+{
+ struct gpmi_nand_data *this = cookie;
+
+ gpmi_clear_bch(this);
+ complete(&this->bch_done);
+ return IRQ_HANDLED;
+}
+
+/*
+ * Calculate the ECC strength by hand:
+ * E : The ECC strength.
+ * G : the length of Galois Field.
+ * N : The chunk count of per page.
+ * O : the oobsize of the NAND chip.
+ * M : the metasize of per page.
+ *
+ * The formula is :
+ * E * G * N
+ * ------------ <= (O - M)
+ * 8
+ *
+ * So, we get E by:
+ * (O - M) * 8
+ * E <= -------------
+ * G * N
+ */
+static inline int get_ecc_strength(struct gpmi_nand_data *this)
+{
+ struct bch_geometry *geo = &this->bch_geometry;
+ struct mtd_info *mtd = nand_to_mtd(&this->nand);
+ int ecc_strength;
+
+ ecc_strength = ((mtd->oobsize - geo->metadata_size) * 8)
+ / (geo->gf_len * geo->ecc_chunk_count);
+
+ /* We need the minor even number. */
+ return round_down(ecc_strength, 2);
+}
+
+static inline bool gpmi_check_ecc(struct gpmi_nand_data *this)
+{
+ struct bch_geometry *geo = &this->bch_geometry;
+
+ /* Do the sanity check. */
+ if (GPMI_IS_MX23(this) || GPMI_IS_MX28(this)) {
+ /* The mx23/mx28 only support the GF13. */
+ if (geo->gf_len == 14)
+ return false;
+ }
+ return geo->ecc_strength <= this->devdata->bch_max_ecc_strength;
+}
+
+/*
+ * If we can get the ECC information from the nand chip, we do not
+ * need to calculate them ourselves.
+ *
+ * We may have available oob space in this case.
+ */
+static int set_geometry_by_ecc_info(struct gpmi_nand_data *this)
+{
+ struct bch_geometry *geo = &this->bch_geometry;
+ struct nand_chip *chip = &this->nand;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ unsigned int block_mark_bit_offset;
+
+ if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0))
+ return -EINVAL;
+
+ switch (chip->ecc_step_ds) {
+ case SZ_512:
+ geo->gf_len = 13;
+ break;
+ case SZ_1K:
+ geo->gf_len = 14;
+ break;
+ default:
+ dev_err(this->dev,
+ "unsupported nand chip. ecc bits : %d, ecc size : %d\n",
+ chip->ecc_strength_ds, chip->ecc_step_ds);
+ return -EINVAL;
+ }
+ geo->ecc_chunk_size = chip->ecc_step_ds;
+ geo->ecc_strength = round_up(chip->ecc_strength_ds, 2);
+ if (!gpmi_check_ecc(this))
+ return -EINVAL;
+
+ /* Keep the C >= O */
+ if (geo->ecc_chunk_size < mtd->oobsize) {
+ dev_err(this->dev,
+ "unsupported nand chip. ecc size: %d, oob size : %d\n",
+ chip->ecc_step_ds, mtd->oobsize);
+ return -EINVAL;
+ }
+
+ /* The default value, see comment in the legacy_set_geometry(). */
+ geo->metadata_size = 10;
+
+ geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size;
+
+ /*
+ * Now, the NAND chip with 2K page(data chunk is 512byte) shows below:
+ *
+ * | P |
+ * |<----------------------------------------------------->|
+ * | |
+ * | (Block Mark) |
+ * | P' | | | |
+ * |<-------------------------------------------->| D | | O' |
+ * | |<---->| |<--->|
+ * V V V V V
+ * +---+----------+-+----------+-+----------+-+----------+-+-----+
+ * | M | data |E| data |E| data |E| data |E| |
+ * +---+----------+-+----------+-+----------+-+----------+-+-----+
+ * ^ ^
+ * | O |
+ * |<------------>|
+ * | |
+ *
+ * P : the page size for BCH module.
+ * E : The ECC strength.
+ * G : the length of Galois Field.
+ * N : The chunk count of per page.
+ * M : the metasize of per page.
+ * C : the ecc chunk size, aka the "data" above.
+ * P': the nand chip's page size.
+ * O : the nand chip's oob size.
+ * O': the free oob.
+ *
+ * The formula for P is :
+ *
+ * E * G * N
+ * P = ------------ + P' + M
+ * 8
+ *
+ * The position of block mark moves forward in the ECC-based view
+ * of page, and the delta is:
+ *
+ * E * G * (N - 1)
+ * D = (---------------- + M)
+ * 8
+ *
+ * Please see the comment in legacy_set_geometry().
+ * With the condition C >= O , we still can get same result.
+ * So the bit position of the physical block mark within the ECC-based
+ * view of the page is :
+ * (P' - D) * 8
+ */
+ geo->page_size = mtd->writesize + geo->metadata_size +
+ (geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8;
+
+ geo->payload_size = mtd->writesize;
+
+ geo->auxiliary_status_offset = ALIGN(geo->metadata_size, 4);
+ geo->auxiliary_size = ALIGN(geo->metadata_size, 4)
+ + ALIGN(geo->ecc_chunk_count, 4);
+
+ if (!this->swap_block_mark)
+ return 0;
+
+ /* For bit swap. */
+ block_mark_bit_offset = mtd->writesize * 8 -
+ (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - 1)
+ + geo->metadata_size * 8);
+
+ geo->block_mark_byte_offset = block_mark_bit_offset / 8;
+ geo->block_mark_bit_offset = block_mark_bit_offset % 8;
+ return 0;
+}
+
+static int legacy_set_geometry(struct gpmi_nand_data *this)
+{
+ struct bch_geometry *geo = &this->bch_geometry;
+ struct mtd_info *mtd = nand_to_mtd(&this->nand);
+ unsigned int metadata_size;
+ unsigned int status_size;
+ unsigned int block_mark_bit_offset;
+
+ /*
+ * The size of the metadata can be changed, though we set it to 10
+ * bytes now. But it can't be too large, because we have to save
+ * enough space for BCH.
+ */
+ geo->metadata_size = 10;
+
+ /* The default for the length of Galois Field. */
+ geo->gf_len = 13;
+
+ /* The default for chunk size. */
+ geo->ecc_chunk_size = 512;
+ while (geo->ecc_chunk_size < mtd->oobsize) {
+ geo->ecc_chunk_size *= 2; /* keep C >= O */
+ geo->gf_len = 14;
+ }
+
+ geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size;
+
+ /* We use the same ECC strength for all chunks. */
+ geo->ecc_strength = get_ecc_strength(this);
+ if (!gpmi_check_ecc(this)) {
+ dev_err(this->dev,
+ "ecc strength: %d cannot be supported by the controller (%d)\n"
+ "try to use minimum ecc strength that NAND chip required\n",
+ geo->ecc_strength,
+ this->devdata->bch_max_ecc_strength);
+ return -EINVAL;
+ }
+
+ geo->page_size = mtd->writesize + geo->metadata_size +
+ (geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8;
+ geo->payload_size = mtd->writesize;
+
+ /*
+ * The auxiliary buffer contains the metadata and the ECC status. The
+ * metadata is padded to the nearest 32-bit boundary. The ECC status
+ * contains one byte for every ECC chunk, and is also padded to the
+ * nearest 32-bit boundary.
+ */
+ metadata_size = ALIGN(geo->metadata_size, 4);
+ status_size = ALIGN(geo->ecc_chunk_count, 4);
+
+ geo->auxiliary_size = metadata_size + status_size;
+ geo->auxiliary_status_offset = metadata_size;
+
+ if (!this->swap_block_mark)
+ return 0;
+
+ /*
+ * We need to compute the byte and bit offsets of
+ * the physical block mark within the ECC-based view of the page.
+ *
+ * NAND chip with 2K page shows below:
+ * (Block Mark)
+ * | |
+ * | D |
+ * |<---->|
+ * V V
+ * +---+----------+-+----------+-+----------+-+----------+-+
+ * | M | data |E| data |E| data |E| data |E|
+ * +---+----------+-+----------+-+----------+-+----------+-+
+ *
+ * The position of block mark moves forward in the ECC-based view
+ * of page, and the delta is:
+ *
+ * E * G * (N - 1)
+ * D = (---------------- + M)
+ * 8
+ *
+ * With the formula to compute the ECC strength, and the condition
+ * : C >= O (C is the ecc chunk size)
+ *
+ * It's easy to deduce to the following result:
+ *
+ * E * G (O - M) C - M C - M
+ * ----------- <= ------- <= -------- < ---------
+ * 8 N N (N - 1)
+ *
+ * So, we get:
+ *
+ * E * G * (N - 1)
+ * D = (---------------- + M) < C
+ * 8
+ *
+ * The above inequality means the position of block mark
+ * within the ECC-based view of the page is still in the data chunk,
+ * and it's NOT in the ECC bits of the chunk.
+ *
+ * Use the following to compute the bit position of the
+ * physical block mark within the ECC-based view of the page:
+ * (page_size - D) * 8
+ *
+ * --Huang Shijie
+ */
+ block_mark_bit_offset = mtd->writesize * 8 -
+ (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - 1)
+ + geo->metadata_size * 8);
+
+ geo->block_mark_byte_offset = block_mark_bit_offset / 8;
+ geo->block_mark_bit_offset = block_mark_bit_offset % 8;
+ return 0;
+}
+
+int common_nfc_set_geometry(struct gpmi_nand_data *this)
+{
+ if ((of_property_read_bool(this->dev->of_node, "fsl,use-minimum-ecc"))
+ || legacy_set_geometry(this))
+ return set_geometry_by_ecc_info(this);
+
+ return 0;
+}
+
+struct dma_chan *get_dma_chan(struct gpmi_nand_data *this)
+{
+ /* We use the DMA channel 0 to access all the nand chips. */
+ return this->dma_chans[0];
+}
+
+/* Can we use the upper's buffer directly for DMA? */
+void prepare_data_dma(struct gpmi_nand_data *this, enum dma_data_direction dr)
+{
+ struct scatterlist *sgl = &this->data_sgl;
+ int ret;
+
+ /* first try to map the upper buffer directly */
+ if (virt_addr_valid(this->upper_buf) &&
+ !object_is_on_stack(this->upper_buf)) {
+ sg_init_one(sgl, this->upper_buf, this->upper_len);
+ ret = dma_map_sg(this->dev, sgl, 1, dr);
+ if (ret == 0)
+ goto map_fail;
+
+ this->direct_dma_map_ok = true;
+ return;
+ }
+
+map_fail:
+ /* We have to use our own DMA buffer. */
+ sg_init_one(sgl, this->data_buffer_dma, this->upper_len);
+
+ if (dr == DMA_TO_DEVICE)
+ memcpy(this->data_buffer_dma, this->upper_buf, this->upper_len);
+
+ dma_map_sg(this->dev, sgl, 1, dr);
+
+ this->direct_dma_map_ok = false;
+}
+
+/* This will be called after the DMA operation is finished. */
+static void dma_irq_callback(void *param)
+{
+ struct gpmi_nand_data *this = param;
+ struct completion *dma_c = &this->dma_done;
+
+ switch (this->dma_type) {
+ case DMA_FOR_COMMAND:
+ dma_unmap_sg(this->dev, &this->cmd_sgl, 1, DMA_TO_DEVICE);
+ break;
+
+ case DMA_FOR_READ_DATA:
+ dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_FROM_DEVICE);
+ if (this->direct_dma_map_ok == false)
+ memcpy(this->upper_buf, this->data_buffer_dma,
+ this->upper_len);
+ break;
+
+ case DMA_FOR_WRITE_DATA:
+ dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_TO_DEVICE);
+ break;
+
+ case DMA_FOR_READ_ECC_PAGE:
+ case DMA_FOR_WRITE_ECC_PAGE:
+ /* We have to wait the BCH interrupt to finish. */
+ break;
+
+ default:
+ dev_err(this->dev, "in wrong DMA operation.\n");
+ }
+
+ complete(dma_c);
+}
+
+int start_dma_without_bch_irq(struct gpmi_nand_data *this,
+ struct dma_async_tx_descriptor *desc)
+{
+ struct completion *dma_c = &this->dma_done;
+ unsigned long timeout;
+
+ init_completion(dma_c);
+
+ desc->callback = dma_irq_callback;
+ desc->callback_param = this;
+ dmaengine_submit(desc);
+ dma_async_issue_pending(get_dma_chan(this));
+
+ /* Wait for the interrupt from the DMA block. */
+ timeout = wait_for_completion_timeout(dma_c, msecs_to_jiffies(1000));
+ if (!timeout) {
+ dev_err(this->dev, "DMA timeout, last DMA :%d\n",
+ this->last_dma_type);
+ gpmi_dump_info(this);
+ return -ETIMEDOUT;
+ }
+ return 0;
+}
+
+/*
+ * This function is used in BCH reading or BCH writing pages.
+ * It will wait for the BCH interrupt as long as ONE second.
+ * Actually, we must wait for two interrupts :
+ * [1] firstly the DMA interrupt and
+ * [2] secondly the BCH interrupt.
+ */
+int start_dma_with_bch_irq(struct gpmi_nand_data *this,
+ struct dma_async_tx_descriptor *desc)
+{
+ struct completion *bch_c = &this->bch_done;
+ unsigned long timeout;
+
+ /* Prepare to receive an interrupt from the BCH block. */
+ init_completion(bch_c);
+
+ /* start the DMA */
+ start_dma_without_bch_irq(this, desc);
+
+ /* Wait for the interrupt from the BCH block. */
+ timeout = wait_for_completion_timeout(bch_c, msecs_to_jiffies(1000));
+ if (!timeout) {
+ dev_err(this->dev, "BCH timeout, last DMA :%d\n",
+ this->last_dma_type);
+ gpmi_dump_info(this);
+ return -ETIMEDOUT;
+ }
+ return 0;
+}
+
+static int acquire_register_block(struct gpmi_nand_data *this,
+ const char *res_name)
+{
+ struct platform_device *pdev = this->pdev;
+ struct resources *res = &this->resources;
+ struct resource *r;
+ void __iomem *p;
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res_name);
+ p = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(p))
+ return PTR_ERR(p);
+
+ if (!strcmp(res_name, GPMI_NAND_GPMI_REGS_ADDR_RES_NAME))
+ res->gpmi_regs = p;
+ else if (!strcmp(res_name, GPMI_NAND_BCH_REGS_ADDR_RES_NAME))
+ res->bch_regs = p;
+ else
+ dev_err(this->dev, "unknown resource name : %s\n", res_name);
+
+ return 0;
+}
+
+static int acquire_bch_irq(struct gpmi_nand_data *this, irq_handler_t irq_h)
+{
+ struct platform_device *pdev = this->pdev;
+ const char *res_name = GPMI_NAND_BCH_INTERRUPT_RES_NAME;
+ struct resource *r;
+ int err;
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, res_name);
+ if (!r) {
+ dev_err(this->dev, "Can't get resource for %s\n", res_name);
+ return -ENODEV;
+ }
+
+ err = devm_request_irq(this->dev, r->start, irq_h, 0, res_name, this);
+ if (err)
+ dev_err(this->dev, "error requesting BCH IRQ\n");
+
+ return err;
+}
+
+static void release_dma_channels(struct gpmi_nand_data *this)
+{
+ unsigned int i;
+ for (i = 0; i < DMA_CHANS; i++)
+ if (this->dma_chans[i]) {
+ dma_release_channel(this->dma_chans[i]);
+ this->dma_chans[i] = NULL;
+ }
+}
+
+static int acquire_dma_channels(struct gpmi_nand_data *this)
+{
+ struct platform_device *pdev = this->pdev;
+ struct dma_chan *dma_chan;
+
+ /* request dma channel */
+ dma_chan = dma_request_slave_channel(&pdev->dev, "rx-tx");
+ if (!dma_chan) {
+ dev_err(this->dev, "Failed to request DMA channel.\n");
+ goto acquire_err;
+ }
+
+ this->dma_chans[0] = dma_chan;
+ return 0;
+
+acquire_err:
+ release_dma_channels(this);
+ return -EINVAL;
+}
+
+static int gpmi_get_clks(struct gpmi_nand_data *this)
+{
+ struct resources *r = &this->resources;
+ struct clk *clk;
+ int err, i;
+
+ for (i = 0; i < this->devdata->clks_count; i++) {
+ clk = devm_clk_get(this->dev, this->devdata->clks[i]);
+ if (IS_ERR(clk)) {
+ err = PTR_ERR(clk);
+ goto err_clock;
+ }
+
+ r->clock[i] = clk;
+ }
+
+ if (GPMI_IS_MX6(this))
+ /*
+ * Set the default value for the gpmi clock.
+ *
+ * If you want to use the ONFI nand which is in the
+ * Synchronous Mode, you should change the clock as you need.
+ */
+ clk_set_rate(r->clock[0], 22000000);
+
+ return 0;
+
+err_clock:
+ dev_dbg(this->dev, "failed in finding the clocks.\n");
+ return err;
+}
+
+static int acquire_resources(struct gpmi_nand_data *this)
+{
+ int ret;
+
+ ret = acquire_register_block(this, GPMI_NAND_GPMI_REGS_ADDR_RES_NAME);
+ if (ret)
+ goto exit_regs;
+
+ ret = acquire_register_block(this, GPMI_NAND_BCH_REGS_ADDR_RES_NAME);
+ if (ret)
+ goto exit_regs;
+
+ ret = acquire_bch_irq(this, bch_irq);
+ if (ret)
+ goto exit_regs;
+
+ ret = acquire_dma_channels(this);
+ if (ret)
+ goto exit_regs;
+
+ ret = gpmi_get_clks(this);
+ if (ret)
+ goto exit_clock;
+ return 0;
+
+exit_clock:
+ release_dma_channels(this);
+exit_regs:
+ return ret;
+}
+
+static void release_resources(struct gpmi_nand_data *this)
+{
+ release_dma_channels(this);
+}
+
+static int read_page_prepare(struct gpmi_nand_data *this,
+ void *destination, unsigned length,
+ void *alt_virt, dma_addr_t alt_phys, unsigned alt_size,
+ void **use_virt, dma_addr_t *use_phys)
+{
+ struct device *dev = this->dev;
+
+ if (virt_addr_valid(destination)) {
+ dma_addr_t dest_phys;
+
+ dest_phys = dma_map_single(dev, destination,
+ length, DMA_FROM_DEVICE);
+ if (dma_mapping_error(dev, dest_phys)) {
+ if (alt_size < length) {
+ dev_err(dev, "Alternate buffer is too small\n");
+ return -ENOMEM;
+ }
+ goto map_failed;
+ }
+ *use_virt = destination;
+ *use_phys = dest_phys;
+ this->direct_dma_map_ok = true;
+ return 0;
+ }
+
+map_failed:
+ *use_virt = alt_virt;
+ *use_phys = alt_phys;
+ this->direct_dma_map_ok = false;
+ return 0;
+}
+
+static inline void read_page_end(struct gpmi_nand_data *this,
+ void *destination, unsigned length,
+ void *alt_virt, dma_addr_t alt_phys, unsigned alt_size,
+ void *used_virt, dma_addr_t used_phys)
+{
+ if (this->direct_dma_map_ok)
+ dma_unmap_single(this->dev, used_phys, length, DMA_FROM_DEVICE);
+}
+
+static inline void read_page_swap_end(struct gpmi_nand_data *this,
+ void *destination, unsigned length,
+ void *alt_virt, dma_addr_t alt_phys, unsigned alt_size,
+ void *used_virt, dma_addr_t used_phys)
+{
+ if (!this->direct_dma_map_ok)
+ memcpy(destination, alt_virt, length);
+}
+
+static int send_page_prepare(struct gpmi_nand_data *this,
+ const void *source, unsigned length,
+ void *alt_virt, dma_addr_t alt_phys, unsigned alt_size,
+ const void **use_virt, dma_addr_t *use_phys)
+{
+ struct device *dev = this->dev;
+
+ if (virt_addr_valid(source)) {
+ dma_addr_t source_phys;
+
+ source_phys = dma_map_single(dev, (void *)source, length,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, source_phys)) {
+ if (alt_size < length) {
+ dev_err(dev, "Alternate buffer is too small\n");
+ return -ENOMEM;
+ }
+ goto map_failed;
+ }
+ *use_virt = source;
+ *use_phys = source_phys;
+ return 0;
+ }
+map_failed:
+ /*
+ * Copy the content of the source buffer into the alternate
+ * buffer and set up the return values accordingly.
+ */
+ memcpy(alt_virt, source, length);
+
+ *use_virt = alt_virt;
+ *use_phys = alt_phys;
+ return 0;
+}
+
+static void send_page_end(struct gpmi_nand_data *this,
+ const void *source, unsigned length,
+ void *alt_virt, dma_addr_t alt_phys, unsigned alt_size,
+ const void *used_virt, dma_addr_t used_phys)
+{
+ struct device *dev = this->dev;
+ if (used_virt == source)
+ dma_unmap_single(dev, used_phys, length, DMA_TO_DEVICE);
+}
+
+static void gpmi_free_dma_buffer(struct gpmi_nand_data *this)
+{
+ struct device *dev = this->dev;
+
+ if (this->page_buffer_virt && virt_addr_valid(this->page_buffer_virt))
+ dma_free_coherent(dev, this->page_buffer_size,
+ this->page_buffer_virt,
+ this->page_buffer_phys);
+ kfree(this->cmd_buffer);
+ kfree(this->data_buffer_dma);
+ kfree(this->raw_buffer);
+
+ this->cmd_buffer = NULL;
+ this->data_buffer_dma = NULL;
+ this->raw_buffer = NULL;
+ this->page_buffer_virt = NULL;
+ this->page_buffer_size = 0;
+}
+
+/* Allocate the DMA buffers */
+static int gpmi_alloc_dma_buffer(struct gpmi_nand_data *this)
+{
+ struct bch_geometry *geo = &this->bch_geometry;
+ struct device *dev = this->dev;
+ struct mtd_info *mtd = nand_to_mtd(&this->nand);
+
+ /* [1] Allocate a command buffer. PAGE_SIZE is enough. */
+ this->cmd_buffer = kzalloc(PAGE_SIZE, GFP_DMA | GFP_KERNEL);
+ if (this->cmd_buffer == NULL)
+ goto error_alloc;
+
+ /*
+ * [2] Allocate a read/write data buffer.
+ * The gpmi_alloc_dma_buffer can be called twice.
+ * We allocate a PAGE_SIZE length buffer if gpmi_alloc_dma_buffer
+ * is called before the nand_scan_ident; and we allocate a buffer
+ * of the real NAND page size when the gpmi_alloc_dma_buffer is
+ * called after the nand_scan_ident.
+ */
+ this->data_buffer_dma = kzalloc(mtd->writesize ?: PAGE_SIZE,
+ GFP_DMA | GFP_KERNEL);
+ if (this->data_buffer_dma == NULL)
+ goto error_alloc;
+
+ /*
+ * [3] Allocate the page buffer.
+ *
+ * Both the payload buffer and the auxiliary buffer must appear on
+ * 32-bit boundaries. We presume the size of the payload buffer is a
+ * power of two and is much larger than four, which guarantees the
+ * auxiliary buffer will appear on a 32-bit boundary.
+ */
+ this->page_buffer_size = geo->payload_size + geo->auxiliary_size;
+ this->page_buffer_virt = dma_alloc_coherent(dev, this->page_buffer_size,
+ &this->page_buffer_phys, GFP_DMA);
+ if (!this->page_buffer_virt)
+ goto error_alloc;
+
+ this->raw_buffer = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
+ if (!this->raw_buffer)
+ goto error_alloc;
+
+ /* Slice up the page buffer. */
+ this->payload_virt = this->page_buffer_virt;
+ this->payload_phys = this->page_buffer_phys;
+ this->auxiliary_virt = this->payload_virt + geo->payload_size;
+ this->auxiliary_phys = this->payload_phys + geo->payload_size;
+ return 0;
+
+error_alloc:
+ gpmi_free_dma_buffer(this);
+ return -ENOMEM;
+}
+
+static void gpmi_cmd_ctrl(struct mtd_info *mtd, int data, unsigned int ctrl)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
+ int ret;
+
+ /*
+ * Every operation begins with a command byte and a series of zero or
+ * more address bytes. These are distinguished by either the Address
+ * Latch Enable (ALE) or Command Latch Enable (CLE) signals being
+ * asserted. When MTD is ready to execute the command, it will deassert
+ * both latch enables.
+ *
+ * Rather than run a separate DMA operation for every single byte, we
+ * queue them up and run a single DMA operation for the entire series
+ * of command and data bytes. NAND_CMD_NONE means the END of the queue.
+ */
+ if ((ctrl & (NAND_ALE | NAND_CLE))) {
+ if (data != NAND_CMD_NONE)
+ this->cmd_buffer[this->command_length++] = data;
+ return;
+ }
+
+ if (!this->command_length)
+ return;
+
+ ret = gpmi_send_command(this);
+ if (ret)
+ dev_err(this->dev, "Chip: %u, Error %d\n",
+ this->current_chip, ret);
+
+ this->command_length = 0;
+}
+
+static int gpmi_dev_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
+
+ return gpmi_is_ready(this, this->current_chip);
+}
+
+static void gpmi_select_chip(struct mtd_info *mtd, int chipnr)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
+ int ret;
+
+ /*
+ * For power consumption matters, disable/enable the clock each time a
+ * die is selected/unselected.
+ */
+ if (this->current_chip < 0 && chipnr >= 0) {
+ ret = gpmi_enable_clk(this);
+ if (ret)
+ dev_err(this->dev, "Failed to enable the clock\n");
+ } else if (this->current_chip >= 0 && chipnr < 0) {
+ ret = gpmi_disable_clk(this);
+ if (ret)
+ dev_err(this->dev, "Failed to disable the clock\n");
+ }
+
+ /*
+ * This driver currently supports only one NAND chip. Plus, dies share
+ * the same configuration. So once timings have been applied on the
+ * controller side, they will not change anymore. When the time will
+ * come, the check on must_apply_timings will have to be dropped.
+ */
+ if (chipnr >= 0 && this->hw.must_apply_timings) {
+ this->hw.must_apply_timings = false;
+ gpmi_nfc_apply_timings(this);
+ }
+
+ this->current_chip = chipnr;
+}
+
+static void gpmi_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
+
+ dev_dbg(this->dev, "len is %d\n", len);
+ this->upper_buf = buf;
+ this->upper_len = len;
+
+ gpmi_read_data(this);
+}
+
+static void gpmi_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
+
+ dev_dbg(this->dev, "len is %d\n", len);
+ this->upper_buf = (uint8_t *)buf;
+ this->upper_len = len;
+
+ gpmi_send_data(this);
+}
+
+static uint8_t gpmi_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
+ uint8_t *buf = this->data_buffer_dma;
+
+ gpmi_read_buf(mtd, buf, 1);
+ return buf[0];
+}
+
+/*
+ * Handles block mark swapping.
+ * It can be called in swapping the block mark, or swapping it back,
+ * because the the operations are the same.
+ */
+static void block_mark_swapping(struct gpmi_nand_data *this,
+ void *payload, void *auxiliary)
+{
+ struct bch_geometry *nfc_geo = &this->bch_geometry;
+ unsigned char *p;
+ unsigned char *a;
+ unsigned int bit;
+ unsigned char mask;
+ unsigned char from_data;
+ unsigned char from_oob;
+
+ if (!this->swap_block_mark)
+ return;
+
+ /*
+ * If control arrives here, we're swapping. Make some convenience
+ * variables.
+ */
+ bit = nfc_geo->block_mark_bit_offset;
+ p = payload + nfc_geo->block_mark_byte_offset;
+ a = auxiliary;
+
+ /*
+ * Get the byte from the data area that overlays the block mark. Since
+ * the ECC engine applies its own view to the bits in the page, the
+ * physical block mark won't (in general) appear on a byte boundary in
+ * the data.
+ */
+ from_data = (p[0] >> bit) | (p[1] << (8 - bit));
+
+ /* Get the byte from the OOB. */
+ from_oob = a[0];
+
+ /* Swap them. */
+ a[0] = from_data;
+
+ mask = (0x1 << bit) - 1;
+ p[0] = (p[0] & mask) | (from_oob << bit);
+
+ mask = ~0 << bit;
+ p[1] = (p[1] & mask) | (from_oob >> (8 - bit));
+}
+
+static int gpmi_ecc_read_page_data(struct nand_chip *chip,
+ uint8_t *buf, int oob_required,
+ int page)
+{
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
+ struct bch_geometry *nfc_geo = &this->bch_geometry;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ void *payload_virt;
+ dma_addr_t payload_phys;
+ void *auxiliary_virt;
+ dma_addr_t auxiliary_phys;
+ unsigned int i;
+ unsigned char *status;
+ unsigned int max_bitflips = 0;
+ int ret;
+
+ dev_dbg(this->dev, "page number is : %d\n", page);
+ ret = read_page_prepare(this, buf, nfc_geo->payload_size,
+ this->payload_virt, this->payload_phys,
+ nfc_geo->payload_size,
+ &payload_virt, &payload_phys);
+ if (ret) {
+ dev_err(this->dev, "Inadequate DMA buffer\n");
+ ret = -ENOMEM;
+ return ret;
+ }
+ auxiliary_virt = this->auxiliary_virt;
+ auxiliary_phys = this->auxiliary_phys;
+
+ /* go! */
+ ret = gpmi_read_page(this, payload_phys, auxiliary_phys);
+ read_page_end(this, buf, nfc_geo->payload_size,
+ this->payload_virt, this->payload_phys,
+ nfc_geo->payload_size,
+ payload_virt, payload_phys);
+ if (ret) {
+ dev_err(this->dev, "Error in ECC-based read: %d\n", ret);
+ return ret;
+ }
+
+ /* Loop over status bytes, accumulating ECC status. */
+ status = auxiliary_virt + nfc_geo->auxiliary_status_offset;
+
+ read_page_swap_end(this, buf, nfc_geo->payload_size,
+ this->payload_virt, this->payload_phys,
+ nfc_geo->payload_size,
+ payload_virt, payload_phys);
+
+ for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) {
+ if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED))
+ continue;
+
+ if (*status == STATUS_UNCORRECTABLE) {
+ int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len;
+ u8 *eccbuf = this->raw_buffer;
+ int offset, bitoffset;
+ int eccbytes;
+ int flips;
+
+ /* Read ECC bytes into our internal raw_buffer */
+ offset = nfc_geo->metadata_size * 8;
+ offset += ((8 * nfc_geo->ecc_chunk_size) + eccbits) * (i + 1);
+ offset -= eccbits;
+ bitoffset = offset % 8;
+ eccbytes = DIV_ROUND_UP(offset + eccbits, 8);
+ offset /= 8;
+ eccbytes -= offset;
+ nand_change_read_column_op(chip, offset, eccbuf,
+ eccbytes, false);
+
+ /*
+ * ECC data are not byte aligned and we may have
+ * in-band data in the first and last byte of
+ * eccbuf. Set non-eccbits to one so that
+ * nand_check_erased_ecc_chunk() does not count them
+ * as bitflips.
+ */
+ if (bitoffset)
+ eccbuf[0] |= GENMASK(bitoffset - 1, 0);
+
+ bitoffset = (bitoffset + eccbits) % 8;
+ if (bitoffset)
+ eccbuf[eccbytes - 1] |= GENMASK(7, bitoffset);
+
+ /*
+ * The ECC hardware has an uncorrectable ECC status
+ * code in case we have bitflips in an erased page. As
+ * nothing was written into this subpage the ECC is
+ * obviously wrong and we can not trust it. We assume
+ * at this point that we are reading an erased page and
+ * try to correct the bitflips in buffer up to
+ * ecc_strength bitflips. If this is a page with random
+ * data, we exceed this number of bitflips and have a
+ * ECC failure. Otherwise we use the corrected buffer.
+ */
+ if (i == 0) {
+ /* The first block includes metadata */
+ flips = nand_check_erased_ecc_chunk(
+ buf + i * nfc_geo->ecc_chunk_size,
+ nfc_geo->ecc_chunk_size,
+ eccbuf, eccbytes,
+ auxiliary_virt,
+ nfc_geo->metadata_size,
+ nfc_geo->ecc_strength);
+ } else {
+ flips = nand_check_erased_ecc_chunk(
+ buf + i * nfc_geo->ecc_chunk_size,
+ nfc_geo->ecc_chunk_size,
+ eccbuf, eccbytes,
+ NULL, 0,
+ nfc_geo->ecc_strength);
+ }
+
+ if (flips > 0) {
+ max_bitflips = max_t(unsigned int, max_bitflips,
+ flips);
+ mtd->ecc_stats.corrected += flips;
+ continue;
+ }
+
+ mtd->ecc_stats.failed++;
+ continue;
+ }
+
+ mtd->ecc_stats.corrected += *status;
+ max_bitflips = max_t(unsigned int, max_bitflips, *status);
+ }
+
+ /* handle the block mark swapping */
+ block_mark_swapping(this, buf, auxiliary_virt);
+
+ if (oob_required) {
+ /*
+ * It's time to deliver the OOB bytes. See gpmi_ecc_read_oob()
+ * for details about our policy for delivering the OOB.
+ *
+ * We fill the caller's buffer with set bits, and then copy the
+ * block mark to th caller's buffer. Note that, if block mark
+ * swapping was necessary, it has already been done, so we can
+ * rely on the first byte of the auxiliary buffer to contain
+ * the block mark.
+ */
+ memset(chip->oob_poi, ~0, mtd->oobsize);
+ chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0];
+ }
+
+ return max_bitflips;
+}
+
+static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ nand_read_page_op(chip, page, 0, NULL, 0);
+
+ return gpmi_ecc_read_page_data(chip, buf, oob_required, page);
+}
+
+/* Fake a virtual small page for the subpage read */
+static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
+ uint32_t offs, uint32_t len, uint8_t *buf, int page)
+{
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
+ void __iomem *bch_regs = this->resources.bch_regs;
+ struct bch_geometry old_geo = this->bch_geometry;
+ struct bch_geometry *geo = &this->bch_geometry;
+ int size = chip->ecc.size; /* ECC chunk size */
+ int meta, n, page_size;
+ u32 r1_old, r2_old, r1_new, r2_new;
+ unsigned int max_bitflips;
+ int first, last, marker_pos;
+ int ecc_parity_size;
+ int col = 0;
+ int old_swap_block_mark = this->swap_block_mark;
+
+ /* The size of ECC parity */
+ ecc_parity_size = geo->gf_len * geo->ecc_strength / 8;
+
+ /* Align it with the chunk size */
+ first = offs / size;
+ last = (offs + len - 1) / size;
+
+ if (this->swap_block_mark) {
+ /*
+ * Find the chunk which contains the Block Marker.
+ * If this chunk is in the range of [first, last],
+ * we have to read out the whole page.
+ * Why? since we had swapped the data at the position of Block
+ * Marker to the metadata which is bound with the chunk 0.
+ */
+ marker_pos = geo->block_mark_byte_offset / size;
+ if (last >= marker_pos && first <= marker_pos) {
+ dev_dbg(this->dev,
+ "page:%d, first:%d, last:%d, marker at:%d\n",
+ page, first, last, marker_pos);
+ return gpmi_ecc_read_page(mtd, chip, buf, 0, page);
+ }
+ }
+
+ meta = geo->metadata_size;
+ if (first) {
+ col = meta + (size + ecc_parity_size) * first;
+ meta = 0;
+ buf = buf + first * size;
+ }
+
+ nand_read_page_op(chip, page, col, NULL, 0);
+
+ /* Save the old environment */
+ r1_old = r1_new = readl(bch_regs + HW_BCH_FLASH0LAYOUT0);
+ r2_old = r2_new = readl(bch_regs + HW_BCH_FLASH0LAYOUT1);
+
+ /* change the BCH registers and bch_geometry{} */
+ n = last - first + 1;
+ page_size = meta + (size + ecc_parity_size) * n;
+
+ r1_new &= ~(BM_BCH_FLASH0LAYOUT0_NBLOCKS |
+ BM_BCH_FLASH0LAYOUT0_META_SIZE);
+ r1_new |= BF_BCH_FLASH0LAYOUT0_NBLOCKS(n - 1)
+ | BF_BCH_FLASH0LAYOUT0_META_SIZE(meta);
+ writel(r1_new, bch_regs + HW_BCH_FLASH0LAYOUT0);
+
+ r2_new &= ~BM_BCH_FLASH0LAYOUT1_PAGE_SIZE;
+ r2_new |= BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size);
+ writel(r2_new, bch_regs + HW_BCH_FLASH0LAYOUT1);
+
+ geo->ecc_chunk_count = n;
+ geo->payload_size = n * size;
+ geo->page_size = page_size;
+ geo->auxiliary_status_offset = ALIGN(meta, 4);
+
+ dev_dbg(this->dev, "page:%d(%d:%d)%d, chunk:(%d:%d), BCH PG size:%d\n",
+ page, offs, len, col, first, n, page_size);
+
+ /* Read the subpage now */
+ this->swap_block_mark = false;
+ max_bitflips = gpmi_ecc_read_page_data(chip, buf, 0, page);
+
+ /* Restore */
+ writel(r1_old, bch_regs + HW_BCH_FLASH0LAYOUT0);
+ writel(r2_old, bch_regs + HW_BCH_FLASH0LAYOUT1);
+ this->bch_geometry = old_geo;
+ this->swap_block_mark = old_swap_block_mark;
+
+ return max_bitflips;
+}
+
+static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required, int page)
+{
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
+ struct bch_geometry *nfc_geo = &this->bch_geometry;
+ const void *payload_virt;
+ dma_addr_t payload_phys;
+ const void *auxiliary_virt;
+ dma_addr_t auxiliary_phys;
+ int ret;
+
+ dev_dbg(this->dev, "ecc write page.\n");
+
+ nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+
+ if (this->swap_block_mark) {
+ /*
+ * If control arrives here, we're doing block mark swapping.
+ * Since we can't modify the caller's buffers, we must copy them
+ * into our own.
+ */
+ memcpy(this->payload_virt, buf, mtd->writesize);
+ payload_virt = this->payload_virt;
+ payload_phys = this->payload_phys;
+
+ memcpy(this->auxiliary_virt, chip->oob_poi,
+ nfc_geo->auxiliary_size);
+ auxiliary_virt = this->auxiliary_virt;
+ auxiliary_phys = this->auxiliary_phys;
+
+ /* Handle block mark swapping. */
+ block_mark_swapping(this,
+ (void *)payload_virt, (void *)auxiliary_virt);
+ } else {
+ /*
+ * If control arrives here, we're not doing block mark swapping,
+ * so we can to try and use the caller's buffers.
+ */
+ ret = send_page_prepare(this,
+ buf, mtd->writesize,
+ this->payload_virt, this->payload_phys,
+ nfc_geo->payload_size,
+ &payload_virt, &payload_phys);
+ if (ret) {
+ dev_err(this->dev, "Inadequate payload DMA buffer\n");
+ return 0;
+ }
+
+ ret = send_page_prepare(this,
+ chip->oob_poi, mtd->oobsize,
+ this->auxiliary_virt, this->auxiliary_phys,
+ nfc_geo->auxiliary_size,
+ &auxiliary_virt, &auxiliary_phys);
+ if (ret) {
+ dev_err(this->dev, "Inadequate auxiliary DMA buffer\n");
+ goto exit_auxiliary;
+ }
+ }
+
+ /* Ask the NFC. */
+ ret = gpmi_send_page(this, payload_phys, auxiliary_phys);
+ if (ret)
+ dev_err(this->dev, "Error in ECC-based write: %d\n", ret);
+
+ if (!this->swap_block_mark) {
+ send_page_end(this, chip->oob_poi, mtd->oobsize,
+ this->auxiliary_virt, this->auxiliary_phys,
+ nfc_geo->auxiliary_size,
+ auxiliary_virt, auxiliary_phys);
+exit_auxiliary:
+ send_page_end(this, buf, mtd->writesize,
+ this->payload_virt, this->payload_phys,
+ nfc_geo->payload_size,
+ payload_virt, payload_phys);
+ }
+
+ if (ret)
+ return ret;
+
+ return nand_prog_page_end_op(chip);
+}
+
+/*
+ * There are several places in this driver where we have to handle the OOB and
+ * block marks. This is the function where things are the most complicated, so
+ * this is where we try to explain it all. All the other places refer back to
+ * here.
+ *
+ * These are the rules, in order of decreasing importance:
+ *
+ * 1) Nothing the caller does can be allowed to imperil the block mark.
+ *
+ * 2) In read operations, the first byte of the OOB we return must reflect the
+ * true state of the block mark, no matter where that block mark appears in
+ * the physical page.
+ *
+ * 3) ECC-based read operations return an OOB full of set bits (since we never
+ * allow ECC-based writes to the OOB, it doesn't matter what ECC-based reads
+ * return).
+ *
+ * 4) "Raw" read operations return a direct view of the physical bytes in the
+ * page, using the conventional definition of which bytes are data and which
+ * are OOB. This gives the caller a way to see the actual, physical bytes
+ * in the page, without the distortions applied by our ECC engine.
+ *
+ *
+ * What we do for this specific read operation depends on two questions:
+ *
+ * 1) Are we doing a "raw" read, or an ECC-based read?
+ *
+ * 2) Are we using block mark swapping or transcription?
+ *
+ * There are four cases, illustrated by the following Karnaugh map:
+ *
+ * | Raw | ECC-based |
+ * -------------+-------------------------+-------------------------+
+ * | Read the conventional | |
+ * | OOB at the end of the | |
+ * Swapping | page and return it. It | |
+ * | contains exactly what | |
+ * | we want. | Read the block mark and |
+ * -------------+-------------------------+ return it in a buffer |
+ * | Read the conventional | full of set bits. |
+ * | OOB at the end of the | |
+ * | page and also the block | |
+ * Transcribing | mark in the metadata. | |
+ * | Copy the block mark | |
+ * | into the first byte of | |
+ * | the OOB. | |
+ * -------------+-------------------------+-------------------------+
+ *
+ * Note that we break rule #4 in the Transcribing/Raw case because we're not
+ * giving an accurate view of the actual, physical bytes in the page (we're
+ * overwriting the block mark). That's OK because it's more important to follow
+ * rule #2.
+ *
+ * It turns out that knowing whether we want an "ECC-based" or "raw" read is not
+ * easy. When reading a page, for example, the NAND Flash MTD code calls our
+ * ecc.read_page or ecc.read_page_raw function. Thus, the fact that MTD wants an
+ * ECC-based or raw view of the page is implicit in which function it calls
+ * (there is a similar pair of ECC-based/raw functions for writing).
+ */
+static int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
+
+ dev_dbg(this->dev, "page number is %d\n", page);
+ /* clear the OOB buffer */
+ memset(chip->oob_poi, ~0, mtd->oobsize);
+
+ /* Read out the conventional OOB. */
+ nand_read_page_op(chip, page, mtd->writesize, NULL, 0);
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ /*
+ * Now, we want to make sure the block mark is correct. In the
+ * non-transcribing case (!GPMI_IS_MX23()), we already have it.
+ * Otherwise, we need to explicitly read it.
+ */
+ if (GPMI_IS_MX23(this)) {
+ /* Read the block mark into the first byte of the OOB buffer. */
+ nand_read_page_op(chip, page, 0, NULL, 0);
+ chip->oob_poi[0] = chip->read_byte(mtd);
+ }
+
+ return 0;
+}
+
+static int
+gpmi_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page)
+{
+ struct mtd_oob_region of = { };
+
+ /* Do we have available oob area? */
+ mtd_ooblayout_free(mtd, 0, &of);
+ if (!of.length)
+ return -EPERM;
+
+ if (!nand_is_slc(chip))
+ return -EPERM;
+
+ return nand_prog_page_op(chip, page, mtd->writesize + of.offset,
+ chip->oob_poi + of.offset, of.length);
+}
+
+/*
+ * This function reads a NAND page without involving the ECC engine (no HW
+ * ECC correction).
+ * The tricky part in the GPMI/BCH controller is that it stores ECC bits
+ * inline (interleaved with payload DATA), and do not align data chunk on
+ * byte boundaries.
+ * We thus need to take care moving the payload data and ECC bits stored in the
+ * page into the provided buffers, which is why we're using gpmi_copy_bits.
+ *
+ * See set_geometry_by_ecc_info inline comments to have a full description
+ * of the layout used by the GPMI controller.
+ */
+static int gpmi_ecc_read_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf,
+ int oob_required, int page)
+{
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
+ struct bch_geometry *nfc_geo = &this->bch_geometry;
+ int eccsize = nfc_geo->ecc_chunk_size;
+ int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len;
+ u8 *tmp_buf = this->raw_buffer;
+ size_t src_bit_off;
+ size_t oob_bit_off;
+ size_t oob_byte_off;
+ uint8_t *oob = chip->oob_poi;
+ int step;
+
+ nand_read_page_op(chip, page, 0, tmp_buf,
+ mtd->writesize + mtd->oobsize);
+
+ /*
+ * If required, swap the bad block marker and the data stored in the
+ * metadata section, so that we don't wrongly consider a block as bad.
+ *
+ * See the layout description for a detailed explanation on why this
+ * is needed.
+ */
+ if (this->swap_block_mark)
+ swap(tmp_buf[0], tmp_buf[mtd->writesize]);
+
+ /*
+ * Copy the metadata section into the oob buffer (this section is
+ * guaranteed to be aligned on a byte boundary).
+ */
+ if (oob_required)
+ memcpy(oob, tmp_buf, nfc_geo->metadata_size);
+
+ oob_bit_off = nfc_geo->metadata_size * 8;
+ src_bit_off = oob_bit_off;
+
+ /* Extract interleaved payload data and ECC bits */
+ for (step = 0; step < nfc_geo->ecc_chunk_count; step++) {
+ if (buf)
+ gpmi_copy_bits(buf, step * eccsize * 8,
+ tmp_buf, src_bit_off,
+ eccsize * 8);
+ src_bit_off += eccsize * 8;
+
+ /* Align last ECC block to align a byte boundary */
+ if (step == nfc_geo->ecc_chunk_count - 1 &&
+ (oob_bit_off + eccbits) % 8)
+ eccbits += 8 - ((oob_bit_off + eccbits) % 8);
+
+ if (oob_required)
+ gpmi_copy_bits(oob, oob_bit_off,
+ tmp_buf, src_bit_off,
+ eccbits);
+
+ src_bit_off += eccbits;
+ oob_bit_off += eccbits;
+ }
+
+ if (oob_required) {
+ oob_byte_off = oob_bit_off / 8;
+
+ if (oob_byte_off < mtd->oobsize)
+ memcpy(oob + oob_byte_off,
+ tmp_buf + mtd->writesize + oob_byte_off,
+ mtd->oobsize - oob_byte_off);
+ }
+
+ return 0;
+}
+
+/*
+ * This function writes a NAND page without involving the ECC engine (no HW
+ * ECC generation).
+ * The tricky part in the GPMI/BCH controller is that it stores ECC bits
+ * inline (interleaved with payload DATA), and do not align data chunk on
+ * byte boundaries.
+ * We thus need to take care moving the OOB area at the right place in the
+ * final page, which is why we're using gpmi_copy_bits.
+ *
+ * See set_geometry_by_ecc_info inline comments to have a full description
+ * of the layout used by the GPMI controller.
+ */
+static int gpmi_ecc_write_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ const uint8_t *buf,
+ int oob_required, int page)
+{
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
+ struct bch_geometry *nfc_geo = &this->bch_geometry;
+ int eccsize = nfc_geo->ecc_chunk_size;
+ int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len;
+ u8 *tmp_buf = this->raw_buffer;
+ uint8_t *oob = chip->oob_poi;
+ size_t dst_bit_off;
+ size_t oob_bit_off;
+ size_t oob_byte_off;
+ int step;
+
+ /*
+ * Initialize all bits to 1 in case we don't have a buffer for the
+ * payload or oob data in order to leave unspecified bits of data
+ * to their initial state.
+ */
+ if (!buf || !oob_required)
+ memset(tmp_buf, 0xff, mtd->writesize + mtd->oobsize);
+
+ /*
+ * First copy the metadata section (stored in oob buffer) at the
+ * beginning of the page, as imposed by the GPMI layout.
+ */
+ memcpy(tmp_buf, oob, nfc_geo->metadata_size);
+ oob_bit_off = nfc_geo->metadata_size * 8;
+ dst_bit_off = oob_bit_off;
+
+ /* Interleave payload data and ECC bits */
+ for (step = 0; step < nfc_geo->ecc_chunk_count; step++) {
+ if (buf)
+ gpmi_copy_bits(tmp_buf, dst_bit_off,
+ buf, step * eccsize * 8, eccsize * 8);
+ dst_bit_off += eccsize * 8;
+
+ /* Align last ECC block to align a byte boundary */
+ if (step == nfc_geo->ecc_chunk_count - 1 &&
+ (oob_bit_off + eccbits) % 8)
+ eccbits += 8 - ((oob_bit_off + eccbits) % 8);
+
+ if (oob_required)
+ gpmi_copy_bits(tmp_buf, dst_bit_off,
+ oob, oob_bit_off, eccbits);
+
+ dst_bit_off += eccbits;
+ oob_bit_off += eccbits;
+ }
+
+ oob_byte_off = oob_bit_off / 8;
+
+ if (oob_required && oob_byte_off < mtd->oobsize)
+ memcpy(tmp_buf + mtd->writesize + oob_byte_off,
+ oob + oob_byte_off, mtd->oobsize - oob_byte_off);
+
+ /*
+ * If required, swap the bad block marker and the first byte of the
+ * metadata section, so that we don't modify the bad block marker.
+ *
+ * See the layout description for a detailed explanation on why this
+ * is needed.
+ */
+ if (this->swap_block_mark)
+ swap(tmp_buf[0], tmp_buf[mtd->writesize]);
+
+ return nand_prog_page_op(chip, page, 0, tmp_buf,
+ mtd->writesize + mtd->oobsize);
+}
+
+static int gpmi_ecc_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ return gpmi_ecc_read_page_raw(mtd, chip, NULL, 1, page);
+}
+
+static int gpmi_ecc_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ return gpmi_ecc_write_page_raw(mtd, chip, NULL, 1, page);
+}
+
+static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
+ int ret = 0;
+ uint8_t *block_mark;
+ int column, page, chipnr;
+
+ chipnr = (int)(ofs >> chip->chip_shift);
+ chip->select_chip(mtd, chipnr);
+
+ column = !GPMI_IS_MX23(this) ? mtd->writesize : 0;
+
+ /* Write the block mark. */
+ block_mark = this->data_buffer_dma;
+ block_mark[0] = 0; /* bad block marker */
+
+ /* Shift to get page */
+ page = (int)(ofs >> chip->page_shift);
+
+ ret = nand_prog_page_op(chip, page, column, block_mark, 1);
+
+ chip->select_chip(mtd, -1);
+
+ return ret;
+}
+
+static int nand_boot_set_geometry(struct gpmi_nand_data *this)
+{
+ struct boot_rom_geometry *geometry = &this->rom_geometry;
+
+ /*
+ * Set the boot block stride size.
+ *
+ * In principle, we should be reading this from the OTP bits, since
+ * that's where the ROM is going to get it. In fact, we don't have any
+ * way to read the OTP bits, so we go with the default and hope for the
+ * best.
+ */
+ geometry->stride_size_in_pages = 64;
+
+ /*
+ * Set the search area stride exponent.
+ *
+ * In principle, we should be reading this from the OTP bits, since
+ * that's where the ROM is going to get it. In fact, we don't have any
+ * way to read the OTP bits, so we go with the default and hope for the
+ * best.
+ */
+ geometry->search_area_stride_exponent = 2;
+ return 0;
+}
+
+static const char *fingerprint = "STMP";
+static int mx23_check_transcription_stamp(struct gpmi_nand_data *this)
+{
+ struct boot_rom_geometry *rom_geo = &this->rom_geometry;
+ struct device *dev = this->dev;
+ struct nand_chip *chip = &this->nand;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ unsigned int search_area_size_in_strides;
+ unsigned int stride;
+ unsigned int page;
+ uint8_t *buffer = chip->data_buf;
+ int saved_chip_number;
+ int found_an_ncb_fingerprint = false;
+
+ /* Compute the number of strides in a search area. */
+ search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent;
+
+ saved_chip_number = this->current_chip;
+ chip->select_chip(mtd, 0);
+
+ /*
+ * Loop through the first search area, looking for the NCB fingerprint.
+ */
+ dev_dbg(dev, "Scanning for an NCB fingerprint...\n");
+
+ for (stride = 0; stride < search_area_size_in_strides; stride++) {
+ /* Compute the page addresses. */
+ page = stride * rom_geo->stride_size_in_pages;
+
+ dev_dbg(dev, "Looking for a fingerprint in page 0x%x\n", page);
+
+ /*
+ * Read the NCB fingerprint. The fingerprint is four bytes long
+ * and starts in the 12th byte of the page.
+ */
+ nand_read_page_op(chip, page, 12, NULL, 0);
+ chip->read_buf(mtd, buffer, strlen(fingerprint));
+
+ /* Look for the fingerprint. */
+ if (!memcmp(buffer, fingerprint, strlen(fingerprint))) {
+ found_an_ncb_fingerprint = true;
+ break;
+ }
+
+ }
+
+ chip->select_chip(mtd, saved_chip_number);
+
+ if (found_an_ncb_fingerprint)
+ dev_dbg(dev, "\tFound a fingerprint\n");
+ else
+ dev_dbg(dev, "\tNo fingerprint found\n");
+ return found_an_ncb_fingerprint;
+}
+
+/* Writes a transcription stamp. */
+static int mx23_write_transcription_stamp(struct gpmi_nand_data *this)
+{
+ struct device *dev = this->dev;
+ struct boot_rom_geometry *rom_geo = &this->rom_geometry;
+ struct nand_chip *chip = &this->nand;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ unsigned int block_size_in_pages;
+ unsigned int search_area_size_in_strides;
+ unsigned int search_area_size_in_pages;
+ unsigned int search_area_size_in_blocks;
+ unsigned int block;
+ unsigned int stride;
+ unsigned int page;
+ uint8_t *buffer = chip->data_buf;
+ int saved_chip_number;
+ int status;
+
+ /* Compute the search area geometry. */
+ block_size_in_pages = mtd->erasesize / mtd->writesize;
+ search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent;
+ search_area_size_in_pages = search_area_size_in_strides *
+ rom_geo->stride_size_in_pages;
+ search_area_size_in_blocks =
+ (search_area_size_in_pages + (block_size_in_pages - 1)) /
+ block_size_in_pages;
+
+ dev_dbg(dev, "Search Area Geometry :\n");
+ dev_dbg(dev, "\tin Blocks : %u\n", search_area_size_in_blocks);
+ dev_dbg(dev, "\tin Strides: %u\n", search_area_size_in_strides);
+ dev_dbg(dev, "\tin Pages : %u\n", search_area_size_in_pages);
+
+ /* Select chip 0. */
+ saved_chip_number = this->current_chip;
+ chip->select_chip(mtd, 0);
+
+ /* Loop over blocks in the first search area, erasing them. */
+ dev_dbg(dev, "Erasing the search area...\n");
+
+ for (block = 0; block < search_area_size_in_blocks; block++) {
+ /* Erase this block. */
+ dev_dbg(dev, "\tErasing block 0x%x\n", block);
+ status = nand_erase_op(chip, block);
+ if (status)
+ dev_err(dev, "[%s] Erase failed.\n", __func__);
+ }
+
+ /* Write the NCB fingerprint into the page buffer. */
+ memset(buffer, ~0, mtd->writesize);
+ memcpy(buffer + 12, fingerprint, strlen(fingerprint));
+
+ /* Loop through the first search area, writing NCB fingerprints. */
+ dev_dbg(dev, "Writing NCB fingerprints...\n");
+ for (stride = 0; stride < search_area_size_in_strides; stride++) {
+ /* Compute the page addresses. */
+ page = stride * rom_geo->stride_size_in_pages;
+
+ /* Write the first page of the current stride. */
+ dev_dbg(dev, "Writing an NCB fingerprint in page 0x%x\n", page);
+
+ status = chip->ecc.write_page_raw(mtd, chip, buffer, 0, page);
+ if (status)
+ dev_err(dev, "[%s] Write failed.\n", __func__);
+ }
+
+ /* Deselect chip 0. */
+ chip->select_chip(mtd, saved_chip_number);
+ return 0;
+}
+
+static int mx23_boot_init(struct gpmi_nand_data *this)
+{
+ struct device *dev = this->dev;
+ struct nand_chip *chip = &this->nand;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ unsigned int block_count;
+ unsigned int block;
+ int chipnr;
+ int page;
+ loff_t byte;
+ uint8_t block_mark;
+ int ret = 0;
+
+ /*
+ * If control arrives here, we can't use block mark swapping, which
+ * means we're forced to use transcription. First, scan for the
+ * transcription stamp. If we find it, then we don't have to do
+ * anything -- the block marks are already transcribed.
+ */
+ if (mx23_check_transcription_stamp(this))
+ return 0;
+
+ /*
+ * If control arrives here, we couldn't find a transcription stamp, so
+ * so we presume the block marks are in the conventional location.
+ */
+ dev_dbg(dev, "Transcribing bad block marks...\n");
+
+ /* Compute the number of blocks in the entire medium. */
+ block_count = chip->chipsize >> chip->phys_erase_shift;
+
+ /*
+ * Loop over all the blocks in the medium, transcribing block marks as
+ * we go.
+ */
+ for (block = 0; block < block_count; block++) {
+ /*
+ * Compute the chip, page and byte addresses for this block's
+ * conventional mark.
+ */
+ chipnr = block >> (chip->chip_shift - chip->phys_erase_shift);
+ page = block << (chip->phys_erase_shift - chip->page_shift);
+ byte = block << chip->phys_erase_shift;
+
+ /* Send the command to read the conventional block mark. */
+ chip->select_chip(mtd, chipnr);
+ nand_read_page_op(chip, page, mtd->writesize, NULL, 0);
+ block_mark = chip->read_byte(mtd);
+ chip->select_chip(mtd, -1);
+
+ /*
+ * Check if the block is marked bad. If so, we need to mark it
+ * again, but this time the result will be a mark in the
+ * location where we transcribe block marks.
+ */
+ if (block_mark != 0xff) {
+ dev_dbg(dev, "Transcribing mark in block %u\n", block);
+ ret = chip->block_markbad(mtd, byte);
+ if (ret)
+ dev_err(dev,
+ "Failed to mark block bad with ret %d\n",
+ ret);
+ }
+ }
+
+ /* Write the stamp that indicates we've transcribed the block marks. */
+ mx23_write_transcription_stamp(this);
+ return 0;
+}
+
+static int nand_boot_init(struct gpmi_nand_data *this)
+{
+ nand_boot_set_geometry(this);
+
+ /* This is ROM arch-specific initilization before the BBT scanning. */
+ if (GPMI_IS_MX23(this))
+ return mx23_boot_init(this);
+ return 0;
+}
+
+static int gpmi_set_geometry(struct gpmi_nand_data *this)
+{
+ int ret;
+
+ /* Free the temporary DMA memory for reading ID. */
+ gpmi_free_dma_buffer(this);
+
+ /* Set up the NFC geometry which is used by BCH. */
+ ret = bch_set_geometry(this);
+ if (ret) {
+ dev_err(this->dev, "Error setting BCH geometry : %d\n", ret);
+ return ret;
+ }
+
+ /* Alloc the new DMA buffers according to the pagesize and oobsize */
+ return gpmi_alloc_dma_buffer(this);
+}
+
+static int gpmi_init_last(struct gpmi_nand_data *this)
+{
+ struct nand_chip *chip = &this->nand;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ struct bch_geometry *bch_geo = &this->bch_geometry;
+ int ret;
+
+ /* Set up the medium geometry */
+ ret = gpmi_set_geometry(this);
+ if (ret)
+ return ret;
+
+ /* Init the nand_ecc_ctrl{} */
+ ecc->read_page = gpmi_ecc_read_page;
+ ecc->write_page = gpmi_ecc_write_page;
+ ecc->read_oob = gpmi_ecc_read_oob;
+ ecc->write_oob = gpmi_ecc_write_oob;
+ ecc->read_page_raw = gpmi_ecc_read_page_raw;
+ ecc->write_page_raw = gpmi_ecc_write_page_raw;
+ ecc->read_oob_raw = gpmi_ecc_read_oob_raw;
+ ecc->write_oob_raw = gpmi_ecc_write_oob_raw;
+ ecc->mode = NAND_ECC_HW;
+ ecc->size = bch_geo->ecc_chunk_size;
+ ecc->strength = bch_geo->ecc_strength;
+ mtd_set_ooblayout(mtd, &gpmi_ooblayout_ops);
+
+ /*
+ * We only enable the subpage read when:
+ * (1) the chip is imx6, and
+ * (2) the size of the ECC parity is byte aligned.
+ */
+ if (GPMI_IS_MX6(this) &&
+ ((bch_geo->gf_len * bch_geo->ecc_strength) % 8) == 0) {
+ ecc->read_subpage = gpmi_ecc_read_subpage;
+ chip->options |= NAND_SUBPAGE_READ;
+ }
+
+ return 0;
+}
+
+static int gpmi_nand_init(struct gpmi_nand_data *this)
+{
+ struct nand_chip *chip = &this->nand;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int ret;
+
+ /* init current chip */
+ this->current_chip = -1;
+
+ /* init the MTD data structures */
+ mtd->name = "gpmi-nand";
+ mtd->dev.parent = this->dev;
+
+ /* init the nand_chip{}, we don't support a 16-bit NAND Flash bus. */
+ nand_set_controller_data(chip, this);
+ nand_set_flash_node(chip, this->pdev->dev.of_node);
+ chip->select_chip = gpmi_select_chip;
+ chip->setup_data_interface = gpmi_setup_data_interface;
+ chip->cmd_ctrl = gpmi_cmd_ctrl;
+ chip->dev_ready = gpmi_dev_ready;
+ chip->read_byte = gpmi_read_byte;
+ chip->read_buf = gpmi_read_buf;
+ chip->write_buf = gpmi_write_buf;
+ chip->badblock_pattern = &gpmi_bbt_descr;
+ chip->block_markbad = gpmi_block_markbad;
+ chip->options |= NAND_NO_SUBPAGE_WRITE;
+
+ /* Set up swap_block_mark, must be set before the gpmi_set_geometry() */
+ this->swap_block_mark = !GPMI_IS_MX23(this);
+
+ /*
+ * Allocate a temporary DMA buffer for reading ID in the
+ * nand_scan_ident().
+ */
+ this->bch_geometry.payload_size = 1024;
+ this->bch_geometry.auxiliary_size = 128;
+ ret = gpmi_alloc_dma_buffer(this);
+ if (ret)
+ goto err_out;
+
+ ret = nand_scan_ident(mtd, GPMI_IS_MX6(this) ? 2 : 1, NULL);
+ if (ret)
+ goto err_out;
+
+ if (chip->bbt_options & NAND_BBT_USE_FLASH) {
+ chip->bbt_options |= NAND_BBT_NO_OOB;
+
+ if (of_property_read_bool(this->dev->of_node,
+ "fsl,no-blockmark-swap"))
+ this->swap_block_mark = false;
+ }
+ dev_dbg(this->dev, "Blockmark swapping %sabled\n",
+ this->swap_block_mark ? "en" : "dis");
+
+ ret = gpmi_init_last(this);
+ if (ret)
+ goto err_out;
+
+ chip->options |= NAND_SKIP_BBTSCAN;
+ ret = nand_scan_tail(mtd);
+ if (ret)
+ goto err_out;
+
+ ret = nand_boot_init(this);
+ if (ret)
+ goto err_nand_cleanup;
+ ret = chip->scan_bbt(mtd);
+ if (ret)
+ goto err_nand_cleanup;
+
+ ret = mtd_device_register(mtd, NULL, 0);
+ if (ret)
+ goto err_nand_cleanup;
+ return 0;
+
+err_nand_cleanup:
+ nand_cleanup(chip);
+err_out:
+ gpmi_free_dma_buffer(this);
+ return ret;
+}
+
+static const struct of_device_id gpmi_nand_id_table[] = {
+ {
+ .compatible = "fsl,imx23-gpmi-nand",
+ .data = &gpmi_devdata_imx23,
+ }, {
+ .compatible = "fsl,imx28-gpmi-nand",
+ .data = &gpmi_devdata_imx28,
+ }, {
+ .compatible = "fsl,imx6q-gpmi-nand",
+ .data = &gpmi_devdata_imx6q,
+ }, {
+ .compatible = "fsl,imx6sx-gpmi-nand",
+ .data = &gpmi_devdata_imx6sx,
+ }, {
+ .compatible = "fsl,imx7d-gpmi-nand",
+ .data = &gpmi_devdata_imx7d,
+ }, {}
+};
+MODULE_DEVICE_TABLE(of, gpmi_nand_id_table);
+
+static int gpmi_nand_probe(struct platform_device *pdev)
+{
+ struct gpmi_nand_data *this;
+ const struct of_device_id *of_id;
+ int ret;
+
+ this = devm_kzalloc(&pdev->dev, sizeof(*this), GFP_KERNEL);
+ if (!this)
+ return -ENOMEM;
+
+ of_id = of_match_device(gpmi_nand_id_table, &pdev->dev);
+ if (of_id) {
+ this->devdata = of_id->data;
+ } else {
+ dev_err(&pdev->dev, "Failed to find the right device id.\n");
+ return -ENODEV;
+ }
+
+ platform_set_drvdata(pdev, this);
+ this->pdev = pdev;
+ this->dev = &pdev->dev;
+
+ ret = acquire_resources(this);
+ if (ret)
+ goto exit_acquire_resources;
+
+ ret = gpmi_init(this);
+ if (ret)
+ goto exit_nfc_init;
+
+ ret = gpmi_nand_init(this);
+ if (ret)
+ goto exit_nfc_init;
+
+ dev_info(this->dev, "driver registered.\n");
+
+ return 0;
+
+exit_nfc_init:
+ release_resources(this);
+exit_acquire_resources:
+
+ return ret;
+}
+
+static int gpmi_nand_remove(struct platform_device *pdev)
+{
+ struct gpmi_nand_data *this = platform_get_drvdata(pdev);
+
+ nand_release(nand_to_mtd(&this->nand));
+ gpmi_free_dma_buffer(this);
+ release_resources(this);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int gpmi_pm_suspend(struct device *dev)
+{
+ struct gpmi_nand_data *this = dev_get_drvdata(dev);
+
+ release_dma_channels(this);
+ return 0;
+}
+
+static int gpmi_pm_resume(struct device *dev)
+{
+ struct gpmi_nand_data *this = dev_get_drvdata(dev);
+ int ret;
+
+ ret = acquire_dma_channels(this);
+ if (ret < 0)
+ return ret;
+
+ /* re-init the GPMI registers */
+ ret = gpmi_init(this);
+ if (ret) {
+ dev_err(this->dev, "Error setting GPMI : %d\n", ret);
+ return ret;
+ }
+
+ /* re-init the BCH registers */
+ ret = bch_set_geometry(this);
+ if (ret) {
+ dev_err(this->dev, "Error setting BCH : %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops gpmi_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(gpmi_pm_suspend, gpmi_pm_resume)
+};
+
+static struct platform_driver gpmi_nand_driver = {
+ .driver = {
+ .name = "gpmi-nand",
+ .pm = &gpmi_pm_ops,
+ .of_match_table = gpmi_nand_id_table,
+ },
+ .probe = gpmi_nand_probe,
+ .remove = gpmi_nand_remove,
+};
+module_platform_driver(gpmi_nand_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("i.MX GPMI NAND Flash Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h
new file mode 100644
index 000000000000..62fde59b995f
--- /dev/null
+++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h
@@ -0,0 +1,236 @@
+/*
+ * Freescale GPMI NAND Flash Driver
+ *
+ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
+ * Copyright (C) 2008 Embedded Alley Solutions, Inc.
+ *
+ * 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.
+ */
+#ifndef __DRIVERS_MTD_NAND_GPMI_NAND_H
+#define __DRIVERS_MTD_NAND_GPMI_NAND_H
+
+#include <linux/mtd/rawnand.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+
+#define GPMI_CLK_MAX 5 /* MX6Q needs five clocks */
+struct resources {
+ void __iomem *gpmi_regs;
+ void __iomem *bch_regs;
+ unsigned int dma_low_channel;
+ unsigned int dma_high_channel;
+ struct clk *clock[GPMI_CLK_MAX];
+};
+
+/**
+ * struct bch_geometry - BCH geometry description.
+ * @gf_len: The length of Galois Field. (e.g., 13 or 14)
+ * @ecc_strength: A number that describes the strength of the ECC
+ * algorithm.
+ * @page_size: The size, in bytes, of a physical page, including
+ * both data and OOB.
+ * @metadata_size: The size, in bytes, of the metadata.
+ * @ecc_chunk_size: The size, in bytes, of a single ECC chunk. Note
+ * the first chunk in the page includes both data and
+ * metadata, so it's a bit larger than this value.
+ * @ecc_chunk_count: The number of ECC chunks in the page,
+ * @payload_size: The size, in bytes, of the payload buffer.
+ * @auxiliary_size: The size, in bytes, of the auxiliary buffer.
+ * @auxiliary_status_offset: The offset into the auxiliary buffer at which
+ * the ECC status appears.
+ * @block_mark_byte_offset: The byte offset in the ECC-based page view at
+ * which the underlying physical block mark appears.
+ * @block_mark_bit_offset: The bit offset into the ECC-based page view at
+ * which the underlying physical block mark appears.
+ */
+struct bch_geometry {
+ unsigned int gf_len;
+ unsigned int ecc_strength;
+ unsigned int page_size;
+ unsigned int metadata_size;
+ unsigned int ecc_chunk_size;
+ unsigned int ecc_chunk_count;
+ unsigned int payload_size;
+ unsigned int auxiliary_size;
+ unsigned int auxiliary_status_offset;
+ unsigned int block_mark_byte_offset;
+ unsigned int block_mark_bit_offset;
+};
+
+/**
+ * struct boot_rom_geometry - Boot ROM geometry description.
+ * @stride_size_in_pages: The size of a boot block stride, in pages.
+ * @search_area_stride_exponent: The logarithm to base 2 of the size of a
+ * search area in boot block strides.
+ */
+struct boot_rom_geometry {
+ unsigned int stride_size_in_pages;
+ unsigned int search_area_stride_exponent;
+};
+
+/* DMA operations types */
+enum dma_ops_type {
+ DMA_FOR_COMMAND = 1,
+ DMA_FOR_READ_DATA,
+ DMA_FOR_WRITE_DATA,
+ DMA_FOR_READ_ECC_PAGE,
+ DMA_FOR_WRITE_ECC_PAGE
+};
+
+enum gpmi_type {
+ IS_MX23,
+ IS_MX28,
+ IS_MX6Q,
+ IS_MX6SX,
+ IS_MX7D,
+};
+
+struct gpmi_devdata {
+ enum gpmi_type type;
+ int bch_max_ecc_strength;
+ int max_chain_delay; /* See the async EDO mode */
+ const char * const *clks;
+ const int clks_count;
+};
+
+/**
+ * struct gpmi_nfc_hardware_timing - GPMI hardware timing parameters.
+ * @must_apply_timings: Whether controller timings have already been
+ * applied or not (useful only while there is
+ * support for only one chip select)
+ * @clk_rate: The clock rate that must be used to derive the
+ * following parameters
+ * @timing0: HW_GPMI_TIMING0 register
+ * @timing1: HW_GPMI_TIMING1 register
+ * @ctrl1n: HW_GPMI_CTRL1n register
+ */
+struct gpmi_nfc_hardware_timing {
+ bool must_apply_timings;
+ unsigned long int clk_rate;
+ u32 timing0;
+ u32 timing1;
+ u32 ctrl1n;
+};
+
+struct gpmi_nand_data {
+ /* Devdata */
+ const struct gpmi_devdata *devdata;
+
+ /* System Interface */
+ struct device *dev;
+ struct platform_device *pdev;
+
+ /* Resources */
+ struct resources resources;
+
+ /* Flash Hardware */
+ struct gpmi_nfc_hardware_timing hw;
+
+ /* BCH */
+ struct bch_geometry bch_geometry;
+ struct completion bch_done;
+
+ /* NAND Boot issue */
+ bool swap_block_mark;
+ struct boot_rom_geometry rom_geometry;
+
+ /* MTD / NAND */
+ struct nand_chip nand;
+
+ /* General-use Variables */
+ int current_chip;
+ unsigned int command_length;
+
+ /* passed from upper layer */
+ uint8_t *upper_buf;
+ int upper_len;
+
+ /* for DMA operations */
+ bool direct_dma_map_ok;
+
+ struct scatterlist cmd_sgl;
+ char *cmd_buffer;
+
+ struct scatterlist data_sgl;
+ char *data_buffer_dma;
+
+ void *page_buffer_virt;
+ dma_addr_t page_buffer_phys;
+ unsigned int page_buffer_size;
+
+ void *payload_virt;
+ dma_addr_t payload_phys;
+
+ void *auxiliary_virt;
+ dma_addr_t auxiliary_phys;
+
+ void *raw_buffer;
+
+ /* DMA channels */
+#define DMA_CHANS 8
+ struct dma_chan *dma_chans[DMA_CHANS];
+ enum dma_ops_type last_dma_type;
+ enum dma_ops_type dma_type;
+ struct completion dma_done;
+
+ /* private */
+ void *private;
+};
+
+/* Common Services */
+int common_nfc_set_geometry(struct gpmi_nand_data *);
+struct dma_chan *get_dma_chan(struct gpmi_nand_data *);
+void prepare_data_dma(struct gpmi_nand_data *,
+ enum dma_data_direction dr);
+int start_dma_without_bch_irq(struct gpmi_nand_data *,
+ struct dma_async_tx_descriptor *);
+int start_dma_with_bch_irq(struct gpmi_nand_data *,
+ struct dma_async_tx_descriptor *);
+
+/* GPMI-NAND helper function library */
+int gpmi_init(struct gpmi_nand_data *);
+void gpmi_clear_bch(struct gpmi_nand_data *);
+void gpmi_dump_info(struct gpmi_nand_data *);
+int bch_set_geometry(struct gpmi_nand_data *);
+int gpmi_is_ready(struct gpmi_nand_data *, unsigned chip);
+int gpmi_send_command(struct gpmi_nand_data *);
+int gpmi_enable_clk(struct gpmi_nand_data *this);
+int gpmi_disable_clk(struct gpmi_nand_data *this);
+int gpmi_setup_data_interface(struct mtd_info *mtd, int chipnr,
+ const struct nand_data_interface *conf);
+void gpmi_nfc_apply_timings(struct gpmi_nand_data *this);
+int gpmi_read_data(struct gpmi_nand_data *);
+int gpmi_send_data(struct gpmi_nand_data *);
+int gpmi_send_page(struct gpmi_nand_data *,
+ dma_addr_t payload, dma_addr_t auxiliary);
+int gpmi_read_page(struct gpmi_nand_data *,
+ dma_addr_t payload, dma_addr_t auxiliary);
+
+void gpmi_copy_bits(u8 *dst, size_t dst_bit_off,
+ const u8 *src, size_t src_bit_off,
+ size_t nbits);
+
+/* BCH : Status Block Completion Codes */
+#define STATUS_GOOD 0x00
+#define STATUS_ERASED 0xff
+#define STATUS_UNCORRECTABLE 0xfe
+
+/* Use the devdata to distinguish different Archs. */
+#define GPMI_IS_MX23(x) ((x)->devdata->type == IS_MX23)
+#define GPMI_IS_MX28(x) ((x)->devdata->type == IS_MX28)
+#define GPMI_IS_MX6Q(x) ((x)->devdata->type == IS_MX6Q)
+#define GPMI_IS_MX6SX(x) ((x)->devdata->type == IS_MX6SX)
+#define GPMI_IS_MX7D(x) ((x)->devdata->type == IS_MX7D)
+
+#define GPMI_IS_MX6(x) (GPMI_IS_MX6Q(x) || GPMI_IS_MX6SX(x) || \
+ GPMI_IS_MX7D(x))
+#endif
diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-regs.h b/drivers/mtd/nand/raw/gpmi-nand/gpmi-regs.h
new file mode 100644
index 000000000000..d92bf32221ca
--- /dev/null
+++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-regs.h
@@ -0,0 +1,192 @@
+/*
+ * Freescale GPMI NAND Flash Driver
+ *
+ * Copyright 2008-2011 Freescale Semiconductor, Inc.
+ * Copyright 2008 Embedded Alley Solutions, Inc.
+ *
+ * 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.
+ */
+#ifndef __GPMI_NAND_GPMI_REGS_H
+#define __GPMI_NAND_GPMI_REGS_H
+
+#define HW_GPMI_CTRL0 0x00000000
+#define HW_GPMI_CTRL0_SET 0x00000004
+#define HW_GPMI_CTRL0_CLR 0x00000008
+#define HW_GPMI_CTRL0_TOG 0x0000000c
+
+#define BP_GPMI_CTRL0_COMMAND_MODE 24
+#define BM_GPMI_CTRL0_COMMAND_MODE (3 << BP_GPMI_CTRL0_COMMAND_MODE)
+#define BF_GPMI_CTRL0_COMMAND_MODE(v) \
+ (((v) << BP_GPMI_CTRL0_COMMAND_MODE) & BM_GPMI_CTRL0_COMMAND_MODE)
+#define BV_GPMI_CTRL0_COMMAND_MODE__WRITE 0x0
+#define BV_GPMI_CTRL0_COMMAND_MODE__READ 0x1
+#define BV_GPMI_CTRL0_COMMAND_MODE__READ_AND_COMPARE 0x2
+#define BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY 0x3
+
+#define BM_GPMI_CTRL0_WORD_LENGTH (1 << 23)
+#define BV_GPMI_CTRL0_WORD_LENGTH__16_BIT 0x0
+#define BV_GPMI_CTRL0_WORD_LENGTH__8_BIT 0x1
+
+/*
+ * Difference in LOCK_CS between imx23 and imx28 :
+ * This bit may impact the _POWER_ consumption. So some chips
+ * do not set it.
+ */
+#define MX23_BP_GPMI_CTRL0_LOCK_CS 22
+#define MX28_BP_GPMI_CTRL0_LOCK_CS 27
+#define LOCK_CS_ENABLE 0x1
+#define BF_GPMI_CTRL0_LOCK_CS(v, x) 0x0
+
+/* Difference in CS between imx23 and imx28 */
+#define BP_GPMI_CTRL0_CS 20
+#define MX23_BM_GPMI_CTRL0_CS (3 << BP_GPMI_CTRL0_CS)
+#define MX28_BM_GPMI_CTRL0_CS (7 << BP_GPMI_CTRL0_CS)
+#define BF_GPMI_CTRL0_CS(v, x) (((v) << BP_GPMI_CTRL0_CS) & \
+ (GPMI_IS_MX23((x)) \
+ ? MX23_BM_GPMI_CTRL0_CS \
+ : MX28_BM_GPMI_CTRL0_CS))
+
+#define BP_GPMI_CTRL0_ADDRESS 17
+#define BM_GPMI_CTRL0_ADDRESS (3 << BP_GPMI_CTRL0_ADDRESS)
+#define BF_GPMI_CTRL0_ADDRESS(v) \
+ (((v) << BP_GPMI_CTRL0_ADDRESS) & BM_GPMI_CTRL0_ADDRESS)
+#define BV_GPMI_CTRL0_ADDRESS__NAND_DATA 0x0
+#define BV_GPMI_CTRL0_ADDRESS__NAND_CLE 0x1
+#define BV_GPMI_CTRL0_ADDRESS__NAND_ALE 0x2
+
+#define BM_GPMI_CTRL0_ADDRESS_INCREMENT (1 << 16)
+#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__DISABLED 0x0
+#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__ENABLED 0x1
+
+#define BP_GPMI_CTRL0_XFER_COUNT 0
+#define BM_GPMI_CTRL0_XFER_COUNT (0xffff << BP_GPMI_CTRL0_XFER_COUNT)
+#define BF_GPMI_CTRL0_XFER_COUNT(v) \
+ (((v) << BP_GPMI_CTRL0_XFER_COUNT) & BM_GPMI_CTRL0_XFER_COUNT)
+
+#define HW_GPMI_COMPARE 0x00000010
+
+#define HW_GPMI_ECCCTRL 0x00000020
+#define HW_GPMI_ECCCTRL_SET 0x00000024
+#define HW_GPMI_ECCCTRL_CLR 0x00000028
+#define HW_GPMI_ECCCTRL_TOG 0x0000002c
+
+#define BP_GPMI_ECCCTRL_ECC_CMD 13
+#define BM_GPMI_ECCCTRL_ECC_CMD (3 << BP_GPMI_ECCCTRL_ECC_CMD)
+#define BF_GPMI_ECCCTRL_ECC_CMD(v) \
+ (((v) << BP_GPMI_ECCCTRL_ECC_CMD) & BM_GPMI_ECCCTRL_ECC_CMD)
+#define BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE 0x0
+#define BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE 0x1
+
+#define BM_GPMI_ECCCTRL_ENABLE_ECC (1 << 12)
+#define BV_GPMI_ECCCTRL_ENABLE_ECC__ENABLE 0x1
+#define BV_GPMI_ECCCTRL_ENABLE_ECC__DISABLE 0x0
+
+#define BP_GPMI_ECCCTRL_BUFFER_MASK 0
+#define BM_GPMI_ECCCTRL_BUFFER_MASK (0x1ff << BP_GPMI_ECCCTRL_BUFFER_MASK)
+#define BF_GPMI_ECCCTRL_BUFFER_MASK(v) \
+ (((v) << BP_GPMI_ECCCTRL_BUFFER_MASK) & BM_GPMI_ECCCTRL_BUFFER_MASK)
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY 0x100
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE 0x1FF
+
+#define HW_GPMI_ECCCOUNT 0x00000030
+#define HW_GPMI_PAYLOAD 0x00000040
+#define HW_GPMI_AUXILIARY 0x00000050
+#define HW_GPMI_CTRL1 0x00000060
+#define HW_GPMI_CTRL1_SET 0x00000064
+#define HW_GPMI_CTRL1_CLR 0x00000068
+#define HW_GPMI_CTRL1_TOG 0x0000006c
+
+#define BP_GPMI_CTRL1_DECOUPLE_CS 24
+#define BM_GPMI_CTRL1_DECOUPLE_CS (1 << BP_GPMI_CTRL1_DECOUPLE_CS)
+
+#define BP_GPMI_CTRL1_WRN_DLY_SEL 22
+#define BM_GPMI_CTRL1_WRN_DLY_SEL (0x3 << BP_GPMI_CTRL1_WRN_DLY_SEL)
+#define BF_GPMI_CTRL1_WRN_DLY_SEL(v) \
+ (((v) << BP_GPMI_CTRL1_WRN_DLY_SEL) & BM_GPMI_CTRL1_WRN_DLY_SEL)
+#define BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS 0x0
+#define BV_GPMI_CTRL1_WRN_DLY_SEL_6_TO_10NS 0x1
+#define BV_GPMI_CTRL1_WRN_DLY_SEL_7_TO_12NS 0x2
+#define BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY 0x3
+
+#define BM_GPMI_CTRL1_BCH_MODE (1 << 18)
+
+#define BP_GPMI_CTRL1_DLL_ENABLE 17
+#define BM_GPMI_CTRL1_DLL_ENABLE (1 << BP_GPMI_CTRL1_DLL_ENABLE)
+
+#define BP_GPMI_CTRL1_HALF_PERIOD 16
+#define BM_GPMI_CTRL1_HALF_PERIOD (1 << BP_GPMI_CTRL1_HALF_PERIOD)
+
+#define BP_GPMI_CTRL1_RDN_DELAY 12
+#define BM_GPMI_CTRL1_RDN_DELAY (0xf << BP_GPMI_CTRL1_RDN_DELAY)
+#define BF_GPMI_CTRL1_RDN_DELAY(v) \
+ (((v) << BP_GPMI_CTRL1_RDN_DELAY) & BM_GPMI_CTRL1_RDN_DELAY)
+
+#define BM_GPMI_CTRL1_DEV_RESET (1 << 3)
+#define BV_GPMI_CTRL1_DEV_RESET__ENABLED 0x0
+#define BV_GPMI_CTRL1_DEV_RESET__DISABLED 0x1
+
+#define BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY (1 << 2)
+#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVELOW 0x0
+#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVEHIGH 0x1
+
+#define BM_GPMI_CTRL1_CAMERA_MODE (1 << 1)
+#define BV_GPMI_CTRL1_GPMI_MODE__NAND 0x0
+#define BV_GPMI_CTRL1_GPMI_MODE__ATA 0x1
+
+#define BM_GPMI_CTRL1_GPMI_MODE (1 << 0)
+
+#define BM_GPMI_CTRL1_CLEAR_MASK (BM_GPMI_CTRL1_WRN_DLY_SEL | \
+ BM_GPMI_CTRL1_DLL_ENABLE | \
+ BM_GPMI_CTRL1_RDN_DELAY | \
+ BM_GPMI_CTRL1_HALF_PERIOD)
+
+#define HW_GPMI_TIMING0 0x00000070
+
+#define BP_GPMI_TIMING0_ADDRESS_SETUP 16
+#define BM_GPMI_TIMING0_ADDRESS_SETUP (0xff << BP_GPMI_TIMING0_ADDRESS_SETUP)
+#define BF_GPMI_TIMING0_ADDRESS_SETUP(v) \
+ (((v) << BP_GPMI_TIMING0_ADDRESS_SETUP) & BM_GPMI_TIMING0_ADDRESS_SETUP)
+
+#define BP_GPMI_TIMING0_DATA_HOLD 8
+#define BM_GPMI_TIMING0_DATA_HOLD (0xff << BP_GPMI_TIMING0_DATA_HOLD)
+#define BF_GPMI_TIMING0_DATA_HOLD(v) \
+ (((v) << BP_GPMI_TIMING0_DATA_HOLD) & BM_GPMI_TIMING0_DATA_HOLD)
+
+#define BP_GPMI_TIMING0_DATA_SETUP 0
+#define BM_GPMI_TIMING0_DATA_SETUP (0xff << BP_GPMI_TIMING0_DATA_SETUP)
+#define BF_GPMI_TIMING0_DATA_SETUP(v) \
+ (((v) << BP_GPMI_TIMING0_DATA_SETUP) & BM_GPMI_TIMING0_DATA_SETUP)
+
+#define HW_GPMI_TIMING1 0x00000080
+#define BP_GPMI_TIMING1_BUSY_TIMEOUT 16
+#define BM_GPMI_TIMING1_BUSY_TIMEOUT (0xffff << BP_GPMI_TIMING1_BUSY_TIMEOUT)
+#define BF_GPMI_TIMING1_BUSY_TIMEOUT(v) \
+ (((v) << BP_GPMI_TIMING1_BUSY_TIMEOUT) & BM_GPMI_TIMING1_BUSY_TIMEOUT)
+
+#define HW_GPMI_TIMING2 0x00000090
+#define HW_GPMI_DATA 0x000000a0
+
+/* MX28 uses this to detect READY. */
+#define HW_GPMI_STAT 0x000000b0
+#define MX28_BP_GPMI_STAT_READY_BUSY 24
+#define MX28_BM_GPMI_STAT_READY_BUSY (0xff << MX28_BP_GPMI_STAT_READY_BUSY)
+#define MX28_BF_GPMI_STAT_READY_BUSY(v) \
+ (((v) << MX28_BP_GPMI_STAT_READY_BUSY) & MX28_BM_GPMI_STAT_READY_BUSY)
+
+/* MX23 uses this to detect READY. */
+#define HW_GPMI_DEBUG 0x000000c0
+#define MX23_BP_GPMI_DEBUG_READY0 28
+#define MX23_BM_GPMI_DEBUG_READY0 (1 << MX23_BP_GPMI_DEBUG_READY0)
+#endif
diff --git a/drivers/mtd/nand/raw/hisi504_nand.c b/drivers/mtd/nand/raw/hisi504_nand.c
new file mode 100644
index 000000000000..27558a67fa41
--- /dev/null
+++ b/drivers/mtd/nand/raw/hisi504_nand.c
@@ -0,0 +1,896 @@
+/*
+ * Hisilicon NAND Flash controller driver
+ *
+ * Copyright © 2012-2014 HiSilicon Technologies Co., Ltd.
+ * http://www.hisilicon.com
+ *
+ * Author: Zhou Wang <wangzhou.bry@gmail.com>
+ * The initial developer of the original code is Zhiyong Cai
+ * <caizhiyong@huawei.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.
+ */
+#include <linux/of.h>
+#include <linux/mtd/mtd.h>
+#include <linux/sizes.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/partitions.h>
+
+#define HINFC504_MAX_CHIP (4)
+#define HINFC504_W_LATCH (5)
+#define HINFC504_R_LATCH (7)
+#define HINFC504_RW_LATCH (3)
+
+#define HINFC504_NFC_TIMEOUT (2 * HZ)
+#define HINFC504_NFC_PM_TIMEOUT (1 * HZ)
+#define HINFC504_NFC_DMA_TIMEOUT (5 * HZ)
+#define HINFC504_CHIP_DELAY (25)
+
+#define HINFC504_REG_BASE_ADDRESS_LEN (0x100)
+#define HINFC504_BUFFER_BASE_ADDRESS_LEN (2048 + 128)
+
+#define HINFC504_ADDR_CYCLE_MASK 0x4
+
+#define HINFC504_CON 0x00
+#define HINFC504_CON_OP_MODE_NORMAL BIT(0)
+#define HINFC504_CON_PAGEISZE_SHIFT (1)
+#define HINFC504_CON_PAGESIZE_MASK (0x07)
+#define HINFC504_CON_BUS_WIDTH BIT(4)
+#define HINFC504_CON_READY_BUSY_SEL BIT(8)
+#define HINFC504_CON_ECCTYPE_SHIFT (9)
+#define HINFC504_CON_ECCTYPE_MASK (0x07)
+
+#define HINFC504_PWIDTH 0x04
+#define SET_HINFC504_PWIDTH(_w_lcnt, _r_lcnt, _rw_hcnt) \
+ ((_w_lcnt) | (((_r_lcnt) & 0x0F) << 4) | (((_rw_hcnt) & 0x0F) << 8))
+
+#define HINFC504_CMD 0x0C
+#define HINFC504_ADDRL 0x10
+#define HINFC504_ADDRH 0x14
+#define HINFC504_DATA_NUM 0x18
+
+#define HINFC504_OP 0x1C
+#define HINFC504_OP_READ_DATA_EN BIT(1)
+#define HINFC504_OP_WAIT_READY_EN BIT(2)
+#define HINFC504_OP_CMD2_EN BIT(3)
+#define HINFC504_OP_WRITE_DATA_EN BIT(4)
+#define HINFC504_OP_ADDR_EN BIT(5)
+#define HINFC504_OP_CMD1_EN BIT(6)
+#define HINFC504_OP_NF_CS_SHIFT (7)
+#define HINFC504_OP_NF_CS_MASK (3)
+#define HINFC504_OP_ADDR_CYCLE_SHIFT (9)
+#define HINFC504_OP_ADDR_CYCLE_MASK (7)
+
+#define HINFC504_STATUS 0x20
+#define HINFC504_READY BIT(0)
+
+#define HINFC504_INTEN 0x24
+#define HINFC504_INTEN_DMA BIT(9)
+#define HINFC504_INTEN_UE BIT(6)
+#define HINFC504_INTEN_CE BIT(5)
+
+#define HINFC504_INTS 0x28
+#define HINFC504_INTS_DMA BIT(9)
+#define HINFC504_INTS_UE BIT(6)
+#define HINFC504_INTS_CE BIT(5)
+
+#define HINFC504_INTCLR 0x2C
+#define HINFC504_INTCLR_DMA BIT(9)
+#define HINFC504_INTCLR_UE BIT(6)
+#define HINFC504_INTCLR_CE BIT(5)
+
+#define HINFC504_ECC_STATUS 0x5C
+#define HINFC504_ECC_16_BIT_SHIFT 12
+
+#define HINFC504_DMA_CTRL 0x60
+#define HINFC504_DMA_CTRL_DMA_START BIT(0)
+#define HINFC504_DMA_CTRL_WE BIT(1)
+#define HINFC504_DMA_CTRL_DATA_AREA_EN BIT(2)
+#define HINFC504_DMA_CTRL_OOB_AREA_EN BIT(3)
+#define HINFC504_DMA_CTRL_BURST4_EN BIT(4)
+#define HINFC504_DMA_CTRL_BURST8_EN BIT(5)
+#define HINFC504_DMA_CTRL_BURST16_EN BIT(6)
+#define HINFC504_DMA_CTRL_ADDR_NUM_SHIFT (7)
+#define HINFC504_DMA_CTRL_ADDR_NUM_MASK (1)
+#define HINFC504_DMA_CTRL_CS_SHIFT (8)
+#define HINFC504_DMA_CTRL_CS_MASK (0x03)
+
+#define HINFC504_DMA_ADDR_DATA 0x64
+#define HINFC504_DMA_ADDR_OOB 0x68
+
+#define HINFC504_DMA_LEN 0x6C
+#define HINFC504_DMA_LEN_OOB_SHIFT (16)
+#define HINFC504_DMA_LEN_OOB_MASK (0xFFF)
+
+#define HINFC504_DMA_PARA 0x70
+#define HINFC504_DMA_PARA_DATA_RW_EN BIT(0)
+#define HINFC504_DMA_PARA_OOB_RW_EN BIT(1)
+#define HINFC504_DMA_PARA_DATA_EDC_EN BIT(2)
+#define HINFC504_DMA_PARA_OOB_EDC_EN BIT(3)
+#define HINFC504_DMA_PARA_DATA_ECC_EN BIT(4)
+#define HINFC504_DMA_PARA_OOB_ECC_EN BIT(5)
+
+#define HINFC_VERSION 0x74
+#define HINFC504_LOG_READ_ADDR 0x7C
+#define HINFC504_LOG_READ_LEN 0x80
+
+#define HINFC504_NANDINFO_LEN 0x10
+
+struct hinfc_host {
+ struct nand_chip chip;
+ struct device *dev;
+ void __iomem *iobase;
+ void __iomem *mmio;
+ struct completion cmd_complete;
+ unsigned int offset;
+ unsigned int command;
+ int chipselect;
+ unsigned int addr_cycle;
+ u32 addr_value[2];
+ u32 cache_addr_value[2];
+ char *buffer;
+ dma_addr_t dma_buffer;
+ dma_addr_t dma_oob;
+ int version;
+ unsigned int irq_status; /* interrupt status */
+};
+
+static inline unsigned int hinfc_read(struct hinfc_host *host, unsigned int reg)
+{
+ return readl(host->iobase + reg);
+}
+
+static inline void hinfc_write(struct hinfc_host *host, unsigned int value,
+ unsigned int reg)
+{
+ writel(value, host->iobase + reg);
+}
+
+static void wait_controller_finished(struct hinfc_host *host)
+{
+ unsigned long timeout = jiffies + HINFC504_NFC_TIMEOUT;
+ int val;
+
+ while (time_before(jiffies, timeout)) {
+ val = hinfc_read(host, HINFC504_STATUS);
+ if (host->command == NAND_CMD_ERASE2) {
+ /* nfc is ready */
+ while (!(val & HINFC504_READY)) {
+ usleep_range(500, 1000);
+ val = hinfc_read(host, HINFC504_STATUS);
+ }
+ return;
+ }
+
+ if (val & HINFC504_READY)
+ return;
+ }
+
+ /* wait cmd timeout */
+ dev_err(host->dev, "Wait NAND controller exec cmd timeout.\n");
+}
+
+static void hisi_nfc_dma_transfer(struct hinfc_host *host, int todev)
+{
+ struct nand_chip *chip = &host->chip;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ unsigned long val;
+ int ret;
+
+ hinfc_write(host, host->dma_buffer, HINFC504_DMA_ADDR_DATA);
+ hinfc_write(host, host->dma_oob, HINFC504_DMA_ADDR_OOB);
+
+ if (chip->ecc.mode == NAND_ECC_NONE) {
+ hinfc_write(host, ((mtd->oobsize & HINFC504_DMA_LEN_OOB_MASK)
+ << HINFC504_DMA_LEN_OOB_SHIFT), HINFC504_DMA_LEN);
+
+ hinfc_write(host, HINFC504_DMA_PARA_DATA_RW_EN
+ | HINFC504_DMA_PARA_OOB_RW_EN, HINFC504_DMA_PARA);
+ } else {
+ if (host->command == NAND_CMD_READOOB)
+ hinfc_write(host, HINFC504_DMA_PARA_OOB_RW_EN
+ | HINFC504_DMA_PARA_OOB_EDC_EN
+ | HINFC504_DMA_PARA_OOB_ECC_EN, HINFC504_DMA_PARA);
+ else
+ hinfc_write(host, HINFC504_DMA_PARA_DATA_RW_EN
+ | HINFC504_DMA_PARA_OOB_RW_EN
+ | HINFC504_DMA_PARA_DATA_EDC_EN
+ | HINFC504_DMA_PARA_OOB_EDC_EN
+ | HINFC504_DMA_PARA_DATA_ECC_EN
+ | HINFC504_DMA_PARA_OOB_ECC_EN, HINFC504_DMA_PARA);
+
+ }
+
+ val = (HINFC504_DMA_CTRL_DMA_START | HINFC504_DMA_CTRL_BURST4_EN
+ | HINFC504_DMA_CTRL_BURST8_EN | HINFC504_DMA_CTRL_BURST16_EN
+ | HINFC504_DMA_CTRL_DATA_AREA_EN | HINFC504_DMA_CTRL_OOB_AREA_EN
+ | ((host->addr_cycle == 4 ? 1 : 0)
+ << HINFC504_DMA_CTRL_ADDR_NUM_SHIFT)
+ | ((host->chipselect & HINFC504_DMA_CTRL_CS_MASK)
+ << HINFC504_DMA_CTRL_CS_SHIFT));
+
+ if (todev)
+ val |= HINFC504_DMA_CTRL_WE;
+
+ init_completion(&host->cmd_complete);
+
+ hinfc_write(host, val, HINFC504_DMA_CTRL);
+ ret = wait_for_completion_timeout(&host->cmd_complete,
+ HINFC504_NFC_DMA_TIMEOUT);
+
+ if (!ret) {
+ dev_err(host->dev, "DMA operation(irq) timeout!\n");
+ /* sanity check */
+ val = hinfc_read(host, HINFC504_DMA_CTRL);
+ if (!(val & HINFC504_DMA_CTRL_DMA_START))
+ dev_err(host->dev, "DMA is already done but without irq ACK!\n");
+ else
+ dev_err(host->dev, "DMA is really timeout!\n");
+ }
+}
+
+static int hisi_nfc_send_cmd_pageprog(struct hinfc_host *host)
+{
+ host->addr_value[0] &= 0xffff0000;
+
+ hinfc_write(host, host->addr_value[0], HINFC504_ADDRL);
+ hinfc_write(host, host->addr_value[1], HINFC504_ADDRH);
+ hinfc_write(host, NAND_CMD_PAGEPROG << 8 | NAND_CMD_SEQIN,
+ HINFC504_CMD);
+
+ hisi_nfc_dma_transfer(host, 1);
+
+ return 0;
+}
+
+static int hisi_nfc_send_cmd_readstart(struct hinfc_host *host)
+{
+ struct mtd_info *mtd = nand_to_mtd(&host->chip);
+
+ if ((host->addr_value[0] == host->cache_addr_value[0]) &&
+ (host->addr_value[1] == host->cache_addr_value[1]))
+ return 0;
+
+ host->addr_value[0] &= 0xffff0000;
+
+ hinfc_write(host, host->addr_value[0], HINFC504_ADDRL);
+ hinfc_write(host, host->addr_value[1], HINFC504_ADDRH);
+ hinfc_write(host, NAND_CMD_READSTART << 8 | NAND_CMD_READ0,
+ HINFC504_CMD);
+
+ hinfc_write(host, 0, HINFC504_LOG_READ_ADDR);
+ hinfc_write(host, mtd->writesize + mtd->oobsize,
+ HINFC504_LOG_READ_LEN);
+
+ hisi_nfc_dma_transfer(host, 0);
+
+ host->cache_addr_value[0] = host->addr_value[0];
+ host->cache_addr_value[1] = host->addr_value[1];
+
+ return 0;
+}
+
+static int hisi_nfc_send_cmd_erase(struct hinfc_host *host)
+{
+ hinfc_write(host, host->addr_value[0], HINFC504_ADDRL);
+ hinfc_write(host, (NAND_CMD_ERASE2 << 8) | NAND_CMD_ERASE1,
+ HINFC504_CMD);
+
+ hinfc_write(host, HINFC504_OP_WAIT_READY_EN
+ | HINFC504_OP_CMD2_EN
+ | HINFC504_OP_CMD1_EN
+ | HINFC504_OP_ADDR_EN
+ | ((host->chipselect & HINFC504_OP_NF_CS_MASK)
+ << HINFC504_OP_NF_CS_SHIFT)
+ | ((host->addr_cycle & HINFC504_OP_ADDR_CYCLE_MASK)
+ << HINFC504_OP_ADDR_CYCLE_SHIFT),
+ HINFC504_OP);
+
+ wait_controller_finished(host);
+
+ return 0;
+}
+
+static int hisi_nfc_send_cmd_readid(struct hinfc_host *host)
+{
+ hinfc_write(host, HINFC504_NANDINFO_LEN, HINFC504_DATA_NUM);
+ hinfc_write(host, NAND_CMD_READID, HINFC504_CMD);
+ hinfc_write(host, 0, HINFC504_ADDRL);
+
+ hinfc_write(host, HINFC504_OP_CMD1_EN | HINFC504_OP_ADDR_EN
+ | HINFC504_OP_READ_DATA_EN
+ | ((host->chipselect & HINFC504_OP_NF_CS_MASK)
+ << HINFC504_OP_NF_CS_SHIFT)
+ | 1 << HINFC504_OP_ADDR_CYCLE_SHIFT, HINFC504_OP);
+
+ wait_controller_finished(host);
+
+ return 0;
+}
+
+static int hisi_nfc_send_cmd_status(struct hinfc_host *host)
+{
+ hinfc_write(host, HINFC504_NANDINFO_LEN, HINFC504_DATA_NUM);
+ hinfc_write(host, NAND_CMD_STATUS, HINFC504_CMD);
+ hinfc_write(host, HINFC504_OP_CMD1_EN
+ | HINFC504_OP_READ_DATA_EN
+ | ((host->chipselect & HINFC504_OP_NF_CS_MASK)
+ << HINFC504_OP_NF_CS_SHIFT),
+ HINFC504_OP);
+
+ wait_controller_finished(host);
+
+ return 0;
+}
+
+static int hisi_nfc_send_cmd_reset(struct hinfc_host *host, int chipselect)
+{
+ hinfc_write(host, NAND_CMD_RESET, HINFC504_CMD);
+
+ hinfc_write(host, HINFC504_OP_CMD1_EN
+ | ((chipselect & HINFC504_OP_NF_CS_MASK)
+ << HINFC504_OP_NF_CS_SHIFT)
+ | HINFC504_OP_WAIT_READY_EN,
+ HINFC504_OP);
+
+ wait_controller_finished(host);
+
+ return 0;
+}
+
+static void hisi_nfc_select_chip(struct mtd_info *mtd, int chipselect)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct hinfc_host *host = nand_get_controller_data(chip);
+
+ if (chipselect < 0)
+ return;
+
+ host->chipselect = chipselect;
+}
+
+static uint8_t hisi_nfc_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct hinfc_host *host = nand_get_controller_data(chip);
+
+ if (host->command == NAND_CMD_STATUS)
+ return *(uint8_t *)(host->mmio);
+
+ host->offset++;
+
+ if (host->command == NAND_CMD_READID)
+ return *(uint8_t *)(host->mmio + host->offset - 1);
+
+ return *(uint8_t *)(host->buffer + host->offset - 1);
+}
+
+static u16 hisi_nfc_read_word(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct hinfc_host *host = nand_get_controller_data(chip);
+
+ host->offset += 2;
+ return *(u16 *)(host->buffer + host->offset - 2);
+}
+
+static void
+hisi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct hinfc_host *host = nand_get_controller_data(chip);
+
+ memcpy(host->buffer + host->offset, buf, len);
+ host->offset += len;
+}
+
+static void hisi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct hinfc_host *host = nand_get_controller_data(chip);
+
+ memcpy(buf, host->buffer + host->offset, len);
+ host->offset += len;
+}
+
+static void set_addr(struct mtd_info *mtd, int column, int page_addr)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct hinfc_host *host = nand_get_controller_data(chip);
+ unsigned int command = host->command;
+
+ host->addr_cycle = 0;
+ host->addr_value[0] = 0;
+ host->addr_value[1] = 0;
+
+ /* Serially input address */
+ if (column != -1) {
+ /* Adjust columns for 16 bit buswidth */
+ if (chip->options & NAND_BUSWIDTH_16 &&
+ !nand_opcode_8bits(command))
+ column >>= 1;
+
+ host->addr_value[0] = column & 0xffff;
+ host->addr_cycle = 2;
+ }
+ if (page_addr != -1) {
+ host->addr_value[0] |= (page_addr & 0xffff)
+ << (host->addr_cycle * 8);
+ host->addr_cycle += 2;
+ if (chip->options & NAND_ROW_ADDR_3) {
+ host->addr_cycle += 1;
+ if (host->command == NAND_CMD_ERASE1)
+ host->addr_value[0] |= ((page_addr >> 16) & 0xff) << 16;
+ else
+ host->addr_value[1] |= ((page_addr >> 16) & 0xff);
+ }
+ }
+}
+
+static void hisi_nfc_cmdfunc(struct mtd_info *mtd, unsigned command, int column,
+ int page_addr)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct hinfc_host *host = nand_get_controller_data(chip);
+ int is_cache_invalid = 1;
+ unsigned int flag = 0;
+
+ host->command = command;
+
+ switch (command) {
+ case NAND_CMD_READ0:
+ case NAND_CMD_READOOB:
+ if (command == NAND_CMD_READ0)
+ host->offset = column;
+ else
+ host->offset = column + mtd->writesize;
+
+ is_cache_invalid = 0;
+ set_addr(mtd, column, page_addr);
+ hisi_nfc_send_cmd_readstart(host);
+ break;
+
+ case NAND_CMD_SEQIN:
+ host->offset = column;
+ set_addr(mtd, column, page_addr);
+ break;
+
+ case NAND_CMD_ERASE1:
+ set_addr(mtd, column, page_addr);
+ break;
+
+ case NAND_CMD_PAGEPROG:
+ hisi_nfc_send_cmd_pageprog(host);
+ break;
+
+ case NAND_CMD_ERASE2:
+ hisi_nfc_send_cmd_erase(host);
+ break;
+
+ case NAND_CMD_READID:
+ host->offset = column;
+ memset(host->mmio, 0, 0x10);
+ hisi_nfc_send_cmd_readid(host);
+ break;
+
+ case NAND_CMD_STATUS:
+ flag = hinfc_read(host, HINFC504_CON);
+ if (chip->ecc.mode == NAND_ECC_HW)
+ hinfc_write(host,
+ flag & ~(HINFC504_CON_ECCTYPE_MASK <<
+ HINFC504_CON_ECCTYPE_SHIFT), HINFC504_CON);
+
+ host->offset = 0;
+ memset(host->mmio, 0, 0x10);
+ hisi_nfc_send_cmd_status(host);
+ hinfc_write(host, flag, HINFC504_CON);
+ break;
+
+ case NAND_CMD_RESET:
+ hisi_nfc_send_cmd_reset(host, host->chipselect);
+ break;
+
+ default:
+ dev_err(host->dev, "Error: unsupported cmd(cmd=%x, col=%x, page=%x)\n",
+ command, column, page_addr);
+ }
+
+ if (is_cache_invalid) {
+ host->cache_addr_value[0] = ~0;
+ host->cache_addr_value[1] = ~0;
+ }
+}
+
+static irqreturn_t hinfc_irq_handle(int irq, void *devid)
+{
+ struct hinfc_host *host = devid;
+ unsigned int flag;
+
+ flag = hinfc_read(host, HINFC504_INTS);
+ /* store interrupts state */
+ host->irq_status |= flag;
+
+ if (flag & HINFC504_INTS_DMA) {
+ hinfc_write(host, HINFC504_INTCLR_DMA, HINFC504_INTCLR);
+ complete(&host->cmd_complete);
+ } else if (flag & HINFC504_INTS_CE) {
+ hinfc_write(host, HINFC504_INTCLR_CE, HINFC504_INTCLR);
+ } else if (flag & HINFC504_INTS_UE) {
+ hinfc_write(host, HINFC504_INTCLR_UE, HINFC504_INTCLR);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int hisi_nand_read_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
+{
+ struct hinfc_host *host = nand_get_controller_data(chip);
+ int max_bitflips = 0, stat = 0, stat_max = 0, status_ecc;
+ int stat_1, stat_2;
+
+ nand_read_page_op(chip, page, 0, buf, mtd->writesize);
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ /* errors which can not be corrected by ECC */
+ if (host->irq_status & HINFC504_INTS_UE) {
+ mtd->ecc_stats.failed++;
+ } else if (host->irq_status & HINFC504_INTS_CE) {
+ /* TODO: need add other ECC modes! */
+ switch (chip->ecc.strength) {
+ case 16:
+ status_ecc = hinfc_read(host, HINFC504_ECC_STATUS) >>
+ HINFC504_ECC_16_BIT_SHIFT & 0x0fff;
+ stat_2 = status_ecc & 0x3f;
+ stat_1 = status_ecc >> 6 & 0x3f;
+ stat = stat_1 + stat_2;
+ stat_max = max_t(int, stat_1, stat_2);
+ }
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max_t(int, max_bitflips, stat_max);
+ }
+ host->irq_status = 0;
+
+ return max_bitflips;
+}
+
+static int hisi_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ struct hinfc_host *host = nand_get_controller_data(chip);
+
+ nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize);
+
+ if (host->irq_status & HINFC504_INTS_UE) {
+ host->irq_status = 0;
+ return -EBADMSG;
+ }
+
+ host->irq_status = 0;
+ return 0;
+}
+
+static int hisi_nand_write_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf, int oob_required,
+ int page)
+{
+ nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);
+ if (oob_required)
+ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ return nand_prog_page_end_op(chip);
+}
+
+static void hisi_nfc_host_init(struct hinfc_host *host)
+{
+ struct nand_chip *chip = &host->chip;
+ unsigned int flag = 0;
+
+ host->version = hinfc_read(host, HINFC_VERSION);
+ host->addr_cycle = 0;
+ host->addr_value[0] = 0;
+ host->addr_value[1] = 0;
+ host->cache_addr_value[0] = ~0;
+ host->cache_addr_value[1] = ~0;
+ host->chipselect = 0;
+
+ /* default page size: 2K, ecc_none. need modify */
+ flag = HINFC504_CON_OP_MODE_NORMAL | HINFC504_CON_READY_BUSY_SEL
+ | ((0x001 & HINFC504_CON_PAGESIZE_MASK)
+ << HINFC504_CON_PAGEISZE_SHIFT)
+ | ((0x0 & HINFC504_CON_ECCTYPE_MASK)
+ << HINFC504_CON_ECCTYPE_SHIFT)
+ | ((chip->options & NAND_BUSWIDTH_16) ?
+ HINFC504_CON_BUS_WIDTH : 0);
+ hinfc_write(host, flag, HINFC504_CON);
+
+ memset(host->mmio, 0xff, HINFC504_BUFFER_BASE_ADDRESS_LEN);
+
+ hinfc_write(host, SET_HINFC504_PWIDTH(HINFC504_W_LATCH,
+ HINFC504_R_LATCH, HINFC504_RW_LATCH), HINFC504_PWIDTH);
+
+ /* enable DMA irq */
+ hinfc_write(host, HINFC504_INTEN_DMA, HINFC504_INTEN);
+}
+
+static int hisi_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ /* FIXME: add ECC bytes position */
+ return -ENOTSUPP;
+}
+
+static int hisi_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ if (section)
+ return -ERANGE;
+
+ oobregion->offset = 2;
+ oobregion->length = 6;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops hisi_ooblayout_ops = {
+ .ecc = hisi_ooblayout_ecc,
+ .free = hisi_ooblayout_free,
+};
+
+static int hisi_nfc_ecc_probe(struct hinfc_host *host)
+{
+ unsigned int flag;
+ int size, strength, ecc_bits;
+ struct device *dev = host->dev;
+ struct nand_chip *chip = &host->chip;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ size = chip->ecc.size;
+ strength = chip->ecc.strength;
+ if (size != 1024) {
+ dev_err(dev, "error ecc size: %d\n", size);
+ return -EINVAL;
+ }
+
+ if ((size == 1024) && ((strength != 8) && (strength != 16) &&
+ (strength != 24) && (strength != 40))) {
+ dev_err(dev, "ecc size and strength do not match\n");
+ return -EINVAL;
+ }
+
+ chip->ecc.size = size;
+ chip->ecc.strength = strength;
+
+ chip->ecc.read_page = hisi_nand_read_page_hwecc;
+ chip->ecc.read_oob = hisi_nand_read_oob;
+ chip->ecc.write_page = hisi_nand_write_page_hwecc;
+
+ switch (chip->ecc.strength) {
+ case 16:
+ ecc_bits = 6;
+ if (mtd->writesize == 2048)
+ mtd_set_ooblayout(mtd, &hisi_ooblayout_ops);
+
+ /* TODO: add more page size support */
+ break;
+
+ /* TODO: add more ecc strength support */
+ default:
+ dev_err(dev, "not support strength: %d\n", chip->ecc.strength);
+ return -EINVAL;
+ }
+
+ flag = hinfc_read(host, HINFC504_CON);
+ /* add ecc type configure */
+ flag |= ((ecc_bits & HINFC504_CON_ECCTYPE_MASK)
+ << HINFC504_CON_ECCTYPE_SHIFT);
+ hinfc_write(host, flag, HINFC504_CON);
+
+ /* enable ecc irq */
+ flag = hinfc_read(host, HINFC504_INTEN) & 0xfff;
+ hinfc_write(host, flag | HINFC504_INTEN_UE | HINFC504_INTEN_CE,
+ HINFC504_INTEN);
+
+ return 0;
+}
+
+static int hisi_nfc_probe(struct platform_device *pdev)
+{
+ int ret = 0, irq, flag, max_chips = HINFC504_MAX_CHIP;
+ struct device *dev = &pdev->dev;
+ struct hinfc_host *host;
+ struct nand_chip *chip;
+ struct mtd_info *mtd;
+ struct resource *res;
+ struct device_node *np = dev->of_node;
+
+ host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
+ if (!host)
+ return -ENOMEM;
+ host->dev = dev;
+
+ platform_set_drvdata(pdev, host);
+ chip = &host->chip;
+ mtd = nand_to_mtd(chip);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(dev, "no IRQ resource defined\n");
+ ret = -ENXIO;
+ goto err_res;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ host->iobase = devm_ioremap_resource(dev, res);
+ if (IS_ERR(host->iobase)) {
+ ret = PTR_ERR(host->iobase);
+ goto err_res;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ host->mmio = devm_ioremap_resource(dev, res);
+ if (IS_ERR(host->mmio)) {
+ ret = PTR_ERR(host->mmio);
+ dev_err(dev, "devm_ioremap_resource[1] fail\n");
+ goto err_res;
+ }
+
+ mtd->name = "hisi_nand";
+ mtd->dev.parent = &pdev->dev;
+
+ nand_set_controller_data(chip, host);
+ nand_set_flash_node(chip, np);
+ chip->cmdfunc = hisi_nfc_cmdfunc;
+ chip->select_chip = hisi_nfc_select_chip;
+ chip->read_byte = hisi_nfc_read_byte;
+ chip->read_word = hisi_nfc_read_word;
+ chip->write_buf = hisi_nfc_write_buf;
+ chip->read_buf = hisi_nfc_read_buf;
+ chip->chip_delay = HINFC504_CHIP_DELAY;
+ chip->set_features = nand_get_set_features_notsupp;
+ chip->get_features = nand_get_set_features_notsupp;
+
+ hisi_nfc_host_init(host);
+
+ ret = devm_request_irq(dev, irq, hinfc_irq_handle, 0x0, "nandc", host);
+ if (ret) {
+ dev_err(dev, "failed to request IRQ\n");
+ goto err_res;
+ }
+
+ ret = nand_scan_ident(mtd, max_chips, NULL);
+ if (ret)
+ goto err_res;
+
+ host->buffer = dmam_alloc_coherent(dev, mtd->writesize + mtd->oobsize,
+ &host->dma_buffer, GFP_KERNEL);
+ if (!host->buffer) {
+ ret = -ENOMEM;
+ goto err_res;
+ }
+
+ host->dma_oob = host->dma_buffer + mtd->writesize;
+ memset(host->buffer, 0xff, mtd->writesize + mtd->oobsize);
+
+ flag = hinfc_read(host, HINFC504_CON);
+ flag &= ~(HINFC504_CON_PAGESIZE_MASK << HINFC504_CON_PAGEISZE_SHIFT);
+ switch (mtd->writesize) {
+ case 2048:
+ flag |= (0x001 << HINFC504_CON_PAGEISZE_SHIFT); break;
+ /*
+ * TODO: add more pagesize support,
+ * default pagesize has been set in hisi_nfc_host_init
+ */
+ default:
+ dev_err(dev, "NON-2KB page size nand flash\n");
+ ret = -EINVAL;
+ goto err_res;
+ }
+ hinfc_write(host, flag, HINFC504_CON);
+
+ if (chip->ecc.mode == NAND_ECC_HW)
+ hisi_nfc_ecc_probe(host);
+
+ ret = nand_scan_tail(mtd);
+ if (ret) {
+ dev_err(dev, "nand_scan_tail failed: %d\n", ret);
+ goto err_res;
+ }
+
+ ret = mtd_device_register(mtd, NULL, 0);
+ if (ret) {
+ dev_err(dev, "Err MTD partition=%d\n", ret);
+ goto err_mtd;
+ }
+
+ return 0;
+
+err_mtd:
+ nand_release(mtd);
+err_res:
+ return ret;
+}
+
+static int hisi_nfc_remove(struct platform_device *pdev)
+{
+ struct hinfc_host *host = platform_get_drvdata(pdev);
+ struct mtd_info *mtd = nand_to_mtd(&host->chip);
+
+ nand_release(mtd);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int hisi_nfc_suspend(struct device *dev)
+{
+ struct hinfc_host *host = dev_get_drvdata(dev);
+ unsigned long timeout = jiffies + HINFC504_NFC_PM_TIMEOUT;
+
+ while (time_before(jiffies, timeout)) {
+ if (((hinfc_read(host, HINFC504_STATUS) & 0x1) == 0x0) &&
+ (hinfc_read(host, HINFC504_DMA_CTRL) &
+ HINFC504_DMA_CTRL_DMA_START)) {
+ cond_resched();
+ return 0;
+ }
+ }
+
+ dev_err(host->dev, "nand controller suspend timeout.\n");
+
+ return -EAGAIN;
+}
+
+static int hisi_nfc_resume(struct device *dev)
+{
+ int cs;
+ struct hinfc_host *host = dev_get_drvdata(dev);
+ struct nand_chip *chip = &host->chip;
+
+ for (cs = 0; cs < chip->numchips; cs++)
+ hisi_nfc_send_cmd_reset(host, cs);
+ hinfc_write(host, SET_HINFC504_PWIDTH(HINFC504_W_LATCH,
+ HINFC504_R_LATCH, HINFC504_RW_LATCH), HINFC504_PWIDTH);
+
+ return 0;
+}
+#endif
+static SIMPLE_DEV_PM_OPS(hisi_nfc_pm_ops, hisi_nfc_suspend, hisi_nfc_resume);
+
+static const struct of_device_id nfc_id_table[] = {
+ { .compatible = "hisilicon,504-nfc" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, nfc_id_table);
+
+static struct platform_driver hisi_nfc_driver = {
+ .driver = {
+ .name = "hisi_nand",
+ .of_match_table = nfc_id_table,
+ .pm = &hisi_nfc_pm_ops,
+ },
+ .probe = hisi_nfc_probe,
+ .remove = hisi_nfc_remove,
+};
+
+module_platform_driver(hisi_nfc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Zhou Wang");
+MODULE_AUTHOR("Zhiyong Cai");
+MODULE_DESCRIPTION("Hisilicon Nand Flash Controller Driver");
diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/raw/jz4740_nand.c
index 613b00a9604b..613b00a9604b 100644
--- a/drivers/mtd/nand/jz4740_nand.c
+++ b/drivers/mtd/nand/raw/jz4740_nand.c
diff --git a/drivers/mtd/nand/jz4780_bch.c b/drivers/mtd/nand/raw/jz4780_bch.c
index 731c6051d91e..731c6051d91e 100644
--- a/drivers/mtd/nand/jz4780_bch.c
+++ b/drivers/mtd/nand/raw/jz4780_bch.c
diff --git a/drivers/mtd/nand/jz4780_bch.h b/drivers/mtd/nand/raw/jz4780_bch.h
index bf4718088a3a..bf4718088a3a 100644
--- a/drivers/mtd/nand/jz4780_bch.h
+++ b/drivers/mtd/nand/raw/jz4780_bch.h
diff --git a/drivers/mtd/nand/jz4780_nand.c b/drivers/mtd/nand/raw/jz4780_nand.c
index e69f6ae4c539..e69f6ae4c539 100644
--- a/drivers/mtd/nand/jz4780_nand.c
+++ b/drivers/mtd/nand/raw/jz4780_nand.c
diff --git a/drivers/mtd/nand/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c
index e357948a7505..e357948a7505 100644
--- a/drivers/mtd/nand/lpc32xx_mlc.c
+++ b/drivers/mtd/nand/raw/lpc32xx_mlc.c
diff --git a/drivers/mtd/nand/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c
index 5f7cc6da0a7f..5f7cc6da0a7f 100644
--- a/drivers/mtd/nand/lpc32xx_slc.c
+++ b/drivers/mtd/nand/raw/lpc32xx_slc.c
diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c
new file mode 100644
index 000000000000..1d779a35ac8e
--- /dev/null
+++ b/drivers/mtd/nand/raw/marvell_nand.c
@@ -0,0 +1,2927 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Marvell NAND flash controller driver
+ *
+ * Copyright (C) 2017 Marvell
+ * Author: Miquel RAYNAL <miquel.raynal@free-electrons.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/of_platform.h>
+#include <linux/iopoll.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <asm/unaligned.h>
+
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/dma/pxa-dma.h>
+#include <linux/platform_data/mtd-nand-pxa3xx.h>
+
+/* Data FIFO granularity, FIFO reads/writes must be a multiple of this length */
+#define FIFO_DEPTH 8
+#define FIFO_REP(x) (x / sizeof(u32))
+#define BCH_SEQ_READS (32 / FIFO_DEPTH)
+/* NFC does not support transfers of larger chunks at a time */
+#define MAX_CHUNK_SIZE 2112
+/* NFCv1 cannot read more that 7 bytes of ID */
+#define NFCV1_READID_LEN 7
+/* Polling is done at a pace of POLL_PERIOD us until POLL_TIMEOUT is reached */
+#define POLL_PERIOD 0
+#define POLL_TIMEOUT 100000
+/* Interrupt maximum wait period in ms */
+#define IRQ_TIMEOUT 1000
+/* Latency in clock cycles between SoC pins and NFC logic */
+#define MIN_RD_DEL_CNT 3
+/* Maximum number of contiguous address cycles */
+#define MAX_ADDRESS_CYC_NFCV1 5
+#define MAX_ADDRESS_CYC_NFCV2 7
+/* System control registers/bits to enable the NAND controller on some SoCs */
+#define GENCONF_SOC_DEVICE_MUX 0x208
+#define GENCONF_SOC_DEVICE_MUX_NFC_EN BIT(0)
+#define GENCONF_SOC_DEVICE_MUX_ECC_CLK_RST BIT(20)
+#define GENCONF_SOC_DEVICE_MUX_ECC_CORE_RST BIT(21)
+#define GENCONF_SOC_DEVICE_MUX_NFC_INT_EN BIT(25)
+#define GENCONF_CLK_GATING_CTRL 0x220
+#define GENCONF_CLK_GATING_CTRL_ND_GATE BIT(2)
+#define GENCONF_ND_CLK_CTRL 0x700
+#define GENCONF_ND_CLK_CTRL_EN BIT(0)
+
+/* NAND controller data flash control register */
+#define NDCR 0x00
+#define NDCR_ALL_INT GENMASK(11, 0)
+#define NDCR_CS1_CMDDM BIT(7)
+#define NDCR_CS0_CMDDM BIT(8)
+#define NDCR_RDYM BIT(11)
+#define NDCR_ND_ARB_EN BIT(12)
+#define NDCR_RA_START BIT(15)
+#define NDCR_RD_ID_CNT(x) (min_t(unsigned int, x, 0x7) << 16)
+#define NDCR_PAGE_SZ(x) (x >= 2048 ? BIT(24) : 0)
+#define NDCR_DWIDTH_M BIT(26)
+#define NDCR_DWIDTH_C BIT(27)
+#define NDCR_ND_RUN BIT(28)
+#define NDCR_DMA_EN BIT(29)
+#define NDCR_ECC_EN BIT(30)
+#define NDCR_SPARE_EN BIT(31)
+#define NDCR_GENERIC_FIELDS_MASK (~(NDCR_RA_START | NDCR_PAGE_SZ(2048) | \
+ NDCR_DWIDTH_M | NDCR_DWIDTH_C))
+
+/* NAND interface timing parameter 0 register */
+#define NDTR0 0x04
+#define NDTR0_TRP(x) ((min_t(unsigned int, x, 0xF) & 0x7) << 0)
+#define NDTR0_TRH(x) (min_t(unsigned int, x, 0x7) << 3)
+#define NDTR0_ETRP(x) ((min_t(unsigned int, x, 0xF) & 0x8) << 3)
+#define NDTR0_SEL_NRE_EDGE BIT(7)
+#define NDTR0_TWP(x) (min_t(unsigned int, x, 0x7) << 8)
+#define NDTR0_TWH(x) (min_t(unsigned int, x, 0x7) << 11)
+#define NDTR0_TCS(x) (min_t(unsigned int, x, 0x7) << 16)
+#define NDTR0_TCH(x) (min_t(unsigned int, x, 0x7) << 19)
+#define NDTR0_RD_CNT_DEL(x) (min_t(unsigned int, x, 0xF) << 22)
+#define NDTR0_SELCNTR BIT(26)
+#define NDTR0_TADL(x) (min_t(unsigned int, x, 0x1F) << 27)
+
+/* NAND interface timing parameter 1 register */
+#define NDTR1 0x0C
+#define NDTR1_TAR(x) (min_t(unsigned int, x, 0xF) << 0)
+#define NDTR1_TWHR(x) (min_t(unsigned int, x, 0xF) << 4)
+#define NDTR1_TRHW(x) (min_t(unsigned int, x / 16, 0x3) << 8)
+#define NDTR1_PRESCALE BIT(14)
+#define NDTR1_WAIT_MODE BIT(15)
+#define NDTR1_TR(x) (min_t(unsigned int, x, 0xFFFF) << 16)
+
+/* NAND controller status register */
+#define NDSR 0x14
+#define NDSR_WRCMDREQ BIT(0)
+#define NDSR_RDDREQ BIT(1)
+#define NDSR_WRDREQ BIT(2)
+#define NDSR_CORERR BIT(3)
+#define NDSR_UNCERR BIT(4)
+#define NDSR_CMDD(cs) BIT(8 - cs)
+#define NDSR_RDY(rb) BIT(11 + rb)
+#define NDSR_ERRCNT(x) ((x >> 16) & 0x1F)
+
+/* NAND ECC control register */
+#define NDECCCTRL 0x28
+#define NDECCCTRL_BCH_EN BIT(0)
+
+/* NAND controller data buffer register */
+#define NDDB 0x40
+
+/* NAND controller command buffer 0 register */
+#define NDCB0 0x48
+#define NDCB0_CMD1(x) ((x & 0xFF) << 0)
+#define NDCB0_CMD2(x) ((x & 0xFF) << 8)
+#define NDCB0_ADDR_CYC(x) ((x & 0x7) << 16)
+#define NDCB0_ADDR_GET_NUM_CYC(x) (((x) >> 16) & 0x7)
+#define NDCB0_DBC BIT(19)
+#define NDCB0_CMD_TYPE(x) ((x & 0x7) << 21)
+#define NDCB0_CSEL BIT(24)
+#define NDCB0_RDY_BYP BIT(27)
+#define NDCB0_LEN_OVRD BIT(28)
+#define NDCB0_CMD_XTYPE(x) ((x & 0x7) << 29)
+
+/* NAND controller command buffer 1 register */
+#define NDCB1 0x4C
+#define NDCB1_COLS(x) ((x & 0xFFFF) << 0)
+#define NDCB1_ADDRS_PAGE(x) (x << 16)
+
+/* NAND controller command buffer 2 register */
+#define NDCB2 0x50
+#define NDCB2_ADDR5_PAGE(x) (((x >> 16) & 0xFF) << 0)
+#define NDCB2_ADDR5_CYC(x) ((x & 0xFF) << 0)
+
+/* NAND controller command buffer 3 register */
+#define NDCB3 0x54
+#define NDCB3_ADDR6_CYC(x) ((x & 0xFF) << 16)
+#define NDCB3_ADDR7_CYC(x) ((x & 0xFF) << 24)
+
+/* NAND controller command buffer 0 register 'type' and 'xtype' fields */
+#define TYPE_READ 0
+#define TYPE_WRITE 1
+#define TYPE_ERASE 2
+#define TYPE_READ_ID 3
+#define TYPE_STATUS 4
+#define TYPE_RESET 5
+#define TYPE_NAKED_CMD 6
+#define TYPE_NAKED_ADDR 7
+#define TYPE_MASK 7
+#define XTYPE_MONOLITHIC_RW 0
+#define XTYPE_LAST_NAKED_RW 1
+#define XTYPE_FINAL_COMMAND 3
+#define XTYPE_READ 4
+#define XTYPE_WRITE_DISPATCH 4
+#define XTYPE_NAKED_RW 5
+#define XTYPE_COMMAND_DISPATCH 6
+#define XTYPE_MASK 7
+
+/**
+ * Marvell ECC engine works differently than the others, in order to limit the
+ * size of the IP, hardware engineers chose to set a fixed strength at 16 bits
+ * per subpage, and depending on a the desired strength needed by the NAND chip,
+ * a particular layout mixing data/spare/ecc is defined, with a possible last
+ * chunk smaller that the others.
+ *
+ * @writesize: Full page size on which the layout applies
+ * @chunk: Desired ECC chunk size on which the layout applies
+ * @strength: Desired ECC strength (per chunk size bytes) on which the
+ * layout applies
+ * @nchunks: Total number of chunks
+ * @full_chunk_cnt: Number of full-sized chunks, which is the number of
+ * repetitions of the pattern:
+ * (data_bytes + spare_bytes + ecc_bytes).
+ * @data_bytes: Number of data bytes per chunk
+ * @spare_bytes: Number of spare bytes per chunk
+ * @ecc_bytes: Number of ecc bytes per chunk
+ * @last_data_bytes: Number of data bytes in the last chunk
+ * @last_spare_bytes: Number of spare bytes in the last chunk
+ * @last_ecc_bytes: Number of ecc bytes in the last chunk
+ */
+struct marvell_hw_ecc_layout {
+ /* Constraints */
+ int writesize;
+ int chunk;
+ int strength;
+ /* Corresponding layout */
+ int nchunks;
+ int full_chunk_cnt;
+ int data_bytes;
+ int spare_bytes;
+ int ecc_bytes;
+ int last_data_bytes;
+ int last_spare_bytes;
+ int last_ecc_bytes;
+};
+
+#define MARVELL_LAYOUT(ws, dc, ds, nc, fcc, db, sb, eb, ldb, lsb, leb) \
+ { \
+ .writesize = ws, \
+ .chunk = dc, \
+ .strength = ds, \
+ .nchunks = nc, \
+ .full_chunk_cnt = fcc, \
+ .data_bytes = db, \
+ .spare_bytes = sb, \
+ .ecc_bytes = eb, \
+ .last_data_bytes = ldb, \
+ .last_spare_bytes = lsb, \
+ .last_ecc_bytes = leb, \
+ }
+
+/* Layouts explained in AN-379_Marvell_SoC_NFC_ECC */
+static const struct marvell_hw_ecc_layout marvell_nfc_layouts[] = {
+ MARVELL_LAYOUT( 512, 512, 1, 1, 1, 512, 8, 8, 0, 0, 0),
+ MARVELL_LAYOUT( 2048, 512, 1, 1, 1, 2048, 40, 24, 0, 0, 0),
+ MARVELL_LAYOUT( 2048, 512, 4, 1, 1, 2048, 32, 30, 0, 0, 0),
+ MARVELL_LAYOUT( 4096, 512, 4, 2, 2, 2048, 32, 30, 0, 0, 0),
+ MARVELL_LAYOUT( 4096, 512, 8, 5, 4, 1024, 0, 30, 0, 64, 30),
+};
+
+/**
+ * The Nand Flash Controller has up to 4 CE and 2 RB pins. The CE selection
+ * is made by a field in NDCB0 register, and in another field in NDCB2 register.
+ * The datasheet describes the logic with an error: ADDR5 field is once
+ * declared at the beginning of NDCB2, and another time at its end. Because the
+ * ADDR5 field of NDCB2 may be used by other bytes, it would be more logical
+ * to use the last bit of this field instead of the first ones.
+ *
+ * @cs: Wanted CE lane.
+ * @ndcb0_csel: Value of the NDCB0 register with or without the flag
+ * selecting the wanted CE lane. This is set once when
+ * the Device Tree is probed.
+ * @rb: Ready/Busy pin for the flash chip
+ */
+struct marvell_nand_chip_sel {
+ unsigned int cs;
+ u32 ndcb0_csel;
+ unsigned int rb;
+};
+
+/**
+ * NAND chip structure: stores NAND chip device related information
+ *
+ * @chip: Base NAND chip structure
+ * @node: Used to store NAND chips into a list
+ * @layout NAND layout when using hardware ECC
+ * @ndcr: Controller register value for this NAND chip
+ * @ndtr0: Timing registers 0 value for this NAND chip
+ * @ndtr1: Timing registers 1 value for this NAND chip
+ * @selected_die: Current active CS
+ * @nsels: Number of CS lines required by the NAND chip
+ * @sels: Array of CS lines descriptions
+ */
+struct marvell_nand_chip {
+ struct nand_chip chip;
+ struct list_head node;
+ const struct marvell_hw_ecc_layout *layout;
+ u32 ndcr;
+ u32 ndtr0;
+ u32 ndtr1;
+ int addr_cyc;
+ int selected_die;
+ unsigned int nsels;
+ struct marvell_nand_chip_sel sels[0];
+};
+
+static inline struct marvell_nand_chip *to_marvell_nand(struct nand_chip *chip)
+{
+ return container_of(chip, struct marvell_nand_chip, chip);
+}
+
+static inline struct marvell_nand_chip_sel *to_nand_sel(struct marvell_nand_chip
+ *nand)
+{
+ return &nand->sels[nand->selected_die];
+}
+
+/**
+ * NAND controller capabilities for distinction between compatible strings
+ *
+ * @max_cs_nb: Number of Chip Select lines available
+ * @max_rb_nb: Number of Ready/Busy lines available
+ * @need_system_controller: Indicates if the SoC needs to have access to the
+ * system controller (ie. to enable the NAND controller)
+ * @legacy_of_bindings: Indicates if DT parsing must be done using the old
+ * fashion way
+ * @is_nfcv2: NFCv2 has numerous enhancements compared to NFCv1, ie.
+ * BCH error detection and correction algorithm,
+ * NDCB3 register has been added
+ * @use_dma: Use dma for data transfers
+ */
+struct marvell_nfc_caps {
+ unsigned int max_cs_nb;
+ unsigned int max_rb_nb;
+ bool need_system_controller;
+ bool legacy_of_bindings;
+ bool is_nfcv2;
+ bool use_dma;
+};
+
+/**
+ * NAND controller structure: stores Marvell NAND controller information
+ *
+ * @controller: Base controller structure
+ * @dev: Parent device (used to print error messages)
+ * @regs: NAND controller registers
+ * @core_clk: Core clock
+ * @reg_clk: Regiters clock
+ * @complete: Completion object to wait for NAND controller events
+ * @assigned_cs: Bitmask describing already assigned CS lines
+ * @chips: List containing all the NAND chips attached to
+ * this NAND controller
+ * @caps: NAND controller capabilities for each compatible string
+ * @dma_chan: DMA channel (NFCv1 only)
+ * @dma_buf: 32-bit aligned buffer for DMA transfers (NFCv1 only)
+ */
+struct marvell_nfc {
+ struct nand_hw_control controller;
+ struct device *dev;
+ void __iomem *regs;
+ struct clk *core_clk;
+ struct clk *reg_clk;
+ struct completion complete;
+ unsigned long assigned_cs;
+ struct list_head chips;
+ struct nand_chip *selected_chip;
+ const struct marvell_nfc_caps *caps;
+
+ /* DMA (NFCv1 only) */
+ bool use_dma;
+ struct dma_chan *dma_chan;
+ u8 *dma_buf;
+};
+
+static inline struct marvell_nfc *to_marvell_nfc(struct nand_hw_control *ctrl)
+{
+ return container_of(ctrl, struct marvell_nfc, controller);
+}
+
+/**
+ * NAND controller timings expressed in NAND Controller clock cycles
+ *
+ * @tRP: ND_nRE pulse width
+ * @tRH: ND_nRE high duration
+ * @tWP: ND_nWE pulse time
+ * @tWH: ND_nWE high duration
+ * @tCS: Enable signal setup time
+ * @tCH: Enable signal hold time
+ * @tADL: Address to write data delay
+ * @tAR: ND_ALE low to ND_nRE low delay
+ * @tWHR: ND_nWE high to ND_nRE low for status read
+ * @tRHW: ND_nRE high duration, read to write delay
+ * @tR: ND_nWE high to ND_nRE low for read
+ */
+struct marvell_nfc_timings {
+ /* NDTR0 fields */
+ unsigned int tRP;
+ unsigned int tRH;
+ unsigned int tWP;
+ unsigned int tWH;
+ unsigned int tCS;
+ unsigned int tCH;
+ unsigned int tADL;
+ /* NDTR1 fields */
+ unsigned int tAR;
+ unsigned int tWHR;
+ unsigned int tRHW;
+ unsigned int tR;
+};
+
+/**
+ * Derives a duration in numbers of clock cycles.
+ *
+ * @ps: Duration in pico-seconds
+ * @period_ns: Clock period in nano-seconds
+ *
+ * Convert the duration in nano-seconds, then divide by the period and
+ * return the number of clock periods.
+ */
+#define TO_CYCLES(ps, period_ns) (DIV_ROUND_UP(ps / 1000, period_ns))
+#define TO_CYCLES64(ps, period_ns) (DIV_ROUND_UP_ULL(div_u64(ps, 1000), \
+ period_ns))
+
+/**
+ * NAND driver structure filled during the parsing of the ->exec_op() subop
+ * subset of instructions.
+ *
+ * @ndcb: Array of values written to NDCBx registers
+ * @cle_ale_delay_ns: Optional delay after the last CMD or ADDR cycle
+ * @rdy_timeout_ms: Timeout for waits on Ready/Busy pin
+ * @rdy_delay_ns: Optional delay after waiting for the RB pin
+ * @data_delay_ns: Optional delay after the data xfer
+ * @data_instr_idx: Index of the data instruction in the subop
+ * @data_instr: Pointer to the data instruction in the subop
+ */
+struct marvell_nfc_op {
+ u32 ndcb[4];
+ unsigned int cle_ale_delay_ns;
+ unsigned int rdy_timeout_ms;
+ unsigned int rdy_delay_ns;
+ unsigned int data_delay_ns;
+ unsigned int data_instr_idx;
+ const struct nand_op_instr *data_instr;
+};
+
+/*
+ * Internal helper to conditionnally apply a delay (from the above structure,
+ * most of the time).
+ */
+static void cond_delay(unsigned int ns)
+{
+ if (!ns)
+ return;
+
+ if (ns < 10000)
+ ndelay(ns);
+ else
+ udelay(DIV_ROUND_UP(ns, 1000));
+}
+
+/*
+ * The controller has many flags that could generate interrupts, most of them
+ * are disabled and polling is used. For the very slow signals, using interrupts
+ * may relax the CPU charge.
+ */
+static void marvell_nfc_disable_int(struct marvell_nfc *nfc, u32 int_mask)
+{
+ u32 reg;
+
+ /* Writing 1 disables the interrupt */
+ reg = readl_relaxed(nfc->regs + NDCR);
+ writel_relaxed(reg | int_mask, nfc->regs + NDCR);
+}
+
+static void marvell_nfc_enable_int(struct marvell_nfc *nfc, u32 int_mask)
+{
+ u32 reg;
+
+ /* Writing 0 enables the interrupt */
+ reg = readl_relaxed(nfc->regs + NDCR);
+ writel_relaxed(reg & ~int_mask, nfc->regs + NDCR);
+}
+
+static void marvell_nfc_clear_int(struct marvell_nfc *nfc, u32 int_mask)
+{
+ writel_relaxed(int_mask, nfc->regs + NDSR);
+}
+
+static void marvell_nfc_force_byte_access(struct nand_chip *chip,
+ bool force_8bit)
+{
+ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+ u32 ndcr;
+
+ /*
+ * Callers of this function do not verify if the NAND is using a 16-bit
+ * an 8-bit bus for normal operations, so we need to take care of that
+ * here by leaving the configuration unchanged if the NAND does not have
+ * the NAND_BUSWIDTH_16 flag set.
+ */
+ if (!(chip->options & NAND_BUSWIDTH_16))
+ return;
+
+ ndcr = readl_relaxed(nfc->regs + NDCR);
+
+ if (force_8bit)
+ ndcr &= ~(NDCR_DWIDTH_M | NDCR_DWIDTH_C);
+ else
+ ndcr |= NDCR_DWIDTH_M | NDCR_DWIDTH_C;
+
+ writel_relaxed(ndcr, nfc->regs + NDCR);
+}
+
+static int marvell_nfc_wait_ndrun(struct nand_chip *chip)
+{
+ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+ u32 val;
+ int ret;
+
+ /*
+ * The command is being processed, wait for the ND_RUN bit to be
+ * cleared by the NFC. If not, we must clear it by hand.
+ */
+ ret = readl_relaxed_poll_timeout(nfc->regs + NDCR, val,
+ (val & NDCR_ND_RUN) == 0,
+ POLL_PERIOD, POLL_TIMEOUT);
+ if (ret) {
+ dev_err(nfc->dev, "Timeout on NAND controller run mode\n");
+ writel_relaxed(readl(nfc->regs + NDCR) & ~NDCR_ND_RUN,
+ nfc->regs + NDCR);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Any time a command has to be sent to the controller, the following sequence
+ * has to be followed:
+ * - call marvell_nfc_prepare_cmd()
+ * -> activate the ND_RUN bit that will kind of 'start a job'
+ * -> wait the signal indicating the NFC is waiting for a command
+ * - send the command (cmd and address cycles)
+ * - enventually send or receive the data
+ * - call marvell_nfc_end_cmd() with the corresponding flag
+ * -> wait the flag to be triggered or cancel the job with a timeout
+ *
+ * The following helpers are here to factorize the code a bit so that
+ * specialized functions responsible for executing the actual NAND
+ * operations do not have to replicate the same code blocks.
+ */
+static int marvell_nfc_prepare_cmd(struct nand_chip *chip)
+{
+ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+ u32 ndcr, val;
+ int ret;
+
+ /* Poll ND_RUN and clear NDSR before issuing any command */
+ ret = marvell_nfc_wait_ndrun(chip);
+ if (ret) {
+ dev_err(nfc->dev, "Last operation did not succeed\n");
+ return ret;
+ }
+
+ ndcr = readl_relaxed(nfc->regs + NDCR);
+ writel_relaxed(readl(nfc->regs + NDSR), nfc->regs + NDSR);
+
+ /* Assert ND_RUN bit and wait the NFC to be ready */
+ writel_relaxed(ndcr | NDCR_ND_RUN, nfc->regs + NDCR);
+ ret = readl_relaxed_poll_timeout(nfc->regs + NDSR, val,
+ val & NDSR_WRCMDREQ,
+ POLL_PERIOD, POLL_TIMEOUT);
+ if (ret) {
+ dev_err(nfc->dev, "Timeout on WRCMDRE\n");
+ return -ETIMEDOUT;
+ }
+
+ /* Command may be written, clear WRCMDREQ status bit */
+ writel_relaxed(NDSR_WRCMDREQ, nfc->regs + NDSR);
+
+ return 0;
+}
+
+static void marvell_nfc_send_cmd(struct nand_chip *chip,
+ struct marvell_nfc_op *nfc_op)
+{
+ struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
+ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+
+ dev_dbg(nfc->dev, "\nNDCR: 0x%08x\n"
+ "NDCB0: 0x%08x\nNDCB1: 0x%08x\nNDCB2: 0x%08x\nNDCB3: 0x%08x\n",
+ (u32)readl_relaxed(nfc->regs + NDCR), nfc_op->ndcb[0],
+ nfc_op->ndcb[1], nfc_op->ndcb[2], nfc_op->ndcb[3]);
+
+ writel_relaxed(to_nand_sel(marvell_nand)->ndcb0_csel | nfc_op->ndcb[0],
+ nfc->regs + NDCB0);
+ writel_relaxed(nfc_op->ndcb[1], nfc->regs + NDCB0);
+ writel(nfc_op->ndcb[2], nfc->regs + NDCB0);
+
+ /*
+ * Write NDCB0 four times only if LEN_OVRD is set or if ADDR6 or ADDR7
+ * fields are used (only available on NFCv2).
+ */
+ if (nfc_op->ndcb[0] & NDCB0_LEN_OVRD ||
+ NDCB0_ADDR_GET_NUM_CYC(nfc_op->ndcb[0]) >= 6) {
+ if (!WARN_ON_ONCE(!nfc->caps->is_nfcv2))
+ writel(nfc_op->ndcb[3], nfc->regs + NDCB0);
+ }
+}
+
+static int marvell_nfc_end_cmd(struct nand_chip *chip, int flag,
+ const char *label)
+{
+ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+ u32 val;
+ int ret;
+
+ ret = readl_relaxed_poll_timeout(nfc->regs + NDSR, val,
+ val & flag,
+ POLL_PERIOD, POLL_TIMEOUT);
+
+ if (ret) {
+ dev_err(nfc->dev, "Timeout on %s (NDSR: 0x%08x)\n",
+ label, val);
+ if (nfc->dma_chan)
+ dmaengine_terminate_all(nfc->dma_chan);
+ return ret;
+ }
+
+ /*
+ * DMA function uses this helper to poll on CMDD bits without wanting
+ * them to be cleared.
+ */
+ if (nfc->use_dma && (readl_relaxed(nfc->regs + NDCR) & NDCR_DMA_EN))
+ return 0;
+
+ writel_relaxed(flag, nfc->regs + NDSR);
+
+ return 0;
+}
+
+static int marvell_nfc_wait_cmdd(struct nand_chip *chip)
+{
+ struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
+ int cs_flag = NDSR_CMDD(to_nand_sel(marvell_nand)->ndcb0_csel);
+
+ return marvell_nfc_end_cmd(chip, cs_flag, "CMDD");
+}
+
+static int marvell_nfc_wait_op(struct nand_chip *chip, unsigned int timeout_ms)
+{
+ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+ int ret;
+
+ /* Timeout is expressed in ms */
+ if (!timeout_ms)
+ timeout_ms = IRQ_TIMEOUT;
+
+ init_completion(&nfc->complete);
+
+ marvell_nfc_enable_int(nfc, NDCR_RDYM);
+ ret = wait_for_completion_timeout(&nfc->complete,
+ msecs_to_jiffies(timeout_ms));
+ marvell_nfc_disable_int(nfc, NDCR_RDYM);
+ marvell_nfc_clear_int(nfc, NDSR_RDY(0) | NDSR_RDY(1));
+ if (!ret) {
+ dev_err(nfc->dev, "Timeout waiting for RB signal\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static void marvell_nfc_select_chip(struct mtd_info *mtd, int die_nr)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
+ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+ u32 ndcr_generic;
+
+ if (chip == nfc->selected_chip && die_nr == marvell_nand->selected_die)
+ return;
+
+ if (die_nr < 0 || die_nr >= marvell_nand->nsels) {
+ nfc->selected_chip = NULL;
+ marvell_nand->selected_die = -1;
+ return;
+ }
+
+ /*
+ * Do not change the timing registers when using the DT property
+ * marvell,nand-keep-config; in that case ->ndtr0 and ->ndtr1 from the
+ * marvell_nand structure are supposedly empty.
+ */
+ writel_relaxed(marvell_nand->ndtr0, nfc->regs + NDTR0);
+ writel_relaxed(marvell_nand->ndtr1, nfc->regs + NDTR1);
+
+ /*
+ * Reset the NDCR register to a clean state for this particular chip,
+ * also clear ND_RUN bit.
+ */
+ ndcr_generic = readl_relaxed(nfc->regs + NDCR) &
+ NDCR_GENERIC_FIELDS_MASK & ~NDCR_ND_RUN;
+ writel_relaxed(ndcr_generic | marvell_nand->ndcr, nfc->regs + NDCR);
+
+ /* Also reset the interrupt status register */
+ marvell_nfc_clear_int(nfc, NDCR_ALL_INT);
+
+ nfc->selected_chip = chip;
+ marvell_nand->selected_die = die_nr;
+}
+
+static irqreturn_t marvell_nfc_isr(int irq, void *dev_id)
+{
+ struct marvell_nfc *nfc = dev_id;
+ u32 st = readl_relaxed(nfc->regs + NDSR);
+ u32 ien = (~readl_relaxed(nfc->regs + NDCR)) & NDCR_ALL_INT;
+
+ /*
+ * RDY interrupt mask is one bit in NDCR while there are two status
+ * bit in NDSR (RDY[cs0/cs2] and RDY[cs1/cs3]).
+ */
+ if (st & NDSR_RDY(1))
+ st |= NDSR_RDY(0);
+
+ if (!(st & ien))
+ return IRQ_NONE;
+
+ marvell_nfc_disable_int(nfc, st & NDCR_ALL_INT);
+
+ if (!(st & (NDSR_RDDREQ | NDSR_WRDREQ | NDSR_WRCMDREQ)))
+ complete(&nfc->complete);
+
+ return IRQ_HANDLED;
+}
+
+/* HW ECC related functions */
+static void marvell_nfc_enable_hw_ecc(struct nand_chip *chip)
+{
+ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+ u32 ndcr = readl_relaxed(nfc->regs + NDCR);
+
+ if (!(ndcr & NDCR_ECC_EN)) {
+ writel_relaxed(ndcr | NDCR_ECC_EN, nfc->regs + NDCR);
+
+ /*
+ * When enabling BCH, set threshold to 0 to always know the
+ * number of corrected bitflips.
+ */
+ if (chip->ecc.algo == NAND_ECC_BCH)
+ writel_relaxed(NDECCCTRL_BCH_EN, nfc->regs + NDECCCTRL);
+ }
+}
+
+static void marvell_nfc_disable_hw_ecc(struct nand_chip *chip)
+{
+ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+ u32 ndcr = readl_relaxed(nfc->regs + NDCR);
+
+ if (ndcr & NDCR_ECC_EN) {
+ writel_relaxed(ndcr & ~NDCR_ECC_EN, nfc->regs + NDCR);
+ if (chip->ecc.algo == NAND_ECC_BCH)
+ writel_relaxed(0, nfc->regs + NDECCCTRL);
+ }
+}
+
+/* DMA related helpers */
+static void marvell_nfc_enable_dma(struct marvell_nfc *nfc)
+{
+ u32 reg;
+
+ reg = readl_relaxed(nfc->regs + NDCR);
+ writel_relaxed(reg | NDCR_DMA_EN, nfc->regs + NDCR);
+}
+
+static void marvell_nfc_disable_dma(struct marvell_nfc *nfc)
+{
+ u32 reg;
+
+ reg = readl_relaxed(nfc->regs + NDCR);
+ writel_relaxed(reg & ~NDCR_DMA_EN, nfc->regs + NDCR);
+}
+
+/* Read/write PIO/DMA accessors */
+static int marvell_nfc_xfer_data_dma(struct marvell_nfc *nfc,
+ enum dma_data_direction direction,
+ unsigned int len)
+{
+ unsigned int dma_len = min_t(int, ALIGN(len, 32), MAX_CHUNK_SIZE);
+ struct dma_async_tx_descriptor *tx;
+ struct scatterlist sg;
+ dma_cookie_t cookie;
+ int ret;
+
+ marvell_nfc_enable_dma(nfc);
+ /* Prepare the DMA transfer */
+ sg_init_one(&sg, nfc->dma_buf, dma_len);
+ dma_map_sg(nfc->dma_chan->device->dev, &sg, 1, direction);
+ tx = dmaengine_prep_slave_sg(nfc->dma_chan, &sg, 1,
+ direction == DMA_FROM_DEVICE ?
+ DMA_DEV_TO_MEM : DMA_MEM_TO_DEV,
+ DMA_PREP_INTERRUPT);
+ if (!tx) {
+ dev_err(nfc->dev, "Could not prepare DMA S/G list\n");
+ return -ENXIO;
+ }
+
+ /* Do the task and wait for it to finish */
+ cookie = dmaengine_submit(tx);
+ ret = dma_submit_error(cookie);
+ if (ret)
+ return -EIO;
+
+ dma_async_issue_pending(nfc->dma_chan);
+ ret = marvell_nfc_wait_cmdd(nfc->selected_chip);
+ dma_unmap_sg(nfc->dma_chan->device->dev, &sg, 1, direction);
+ marvell_nfc_disable_dma(nfc);
+ if (ret) {
+ dev_err(nfc->dev, "Timeout waiting for DMA (status: %d)\n",
+ dmaengine_tx_status(nfc->dma_chan, cookie, NULL));
+ dmaengine_terminate_all(nfc->dma_chan);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int marvell_nfc_xfer_data_in_pio(struct marvell_nfc *nfc, u8 *in,
+ unsigned int len)
+{
+ unsigned int last_len = len % FIFO_DEPTH;
+ unsigned int last_full_offset = round_down(len, FIFO_DEPTH);
+ int i;
+
+ for (i = 0; i < last_full_offset; i += FIFO_DEPTH)
+ ioread32_rep(nfc->regs + NDDB, in + i, FIFO_REP(FIFO_DEPTH));
+
+ if (last_len) {
+ u8 tmp_buf[FIFO_DEPTH];
+
+ ioread32_rep(nfc->regs + NDDB, tmp_buf, FIFO_REP(FIFO_DEPTH));
+ memcpy(in + last_full_offset, tmp_buf, last_len);
+ }
+
+ return 0;
+}
+
+static int marvell_nfc_xfer_data_out_pio(struct marvell_nfc *nfc, const u8 *out,
+ unsigned int len)
+{
+ unsigned int last_len = len % FIFO_DEPTH;
+ unsigned int last_full_offset = round_down(len, FIFO_DEPTH);
+ int i;
+
+ for (i = 0; i < last_full_offset; i += FIFO_DEPTH)
+ iowrite32_rep(nfc->regs + NDDB, out + i, FIFO_REP(FIFO_DEPTH));
+
+ if (last_len) {
+ u8 tmp_buf[FIFO_DEPTH];
+
+ memcpy(tmp_buf, out + last_full_offset, last_len);
+ iowrite32_rep(nfc->regs + NDDB, tmp_buf, FIFO_REP(FIFO_DEPTH));
+ }
+
+ return 0;
+}
+
+static void marvell_nfc_check_empty_chunk(struct nand_chip *chip,
+ u8 *data, int data_len,
+ u8 *spare, int spare_len,
+ u8 *ecc, int ecc_len,
+ unsigned int *max_bitflips)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int bf;
+
+ /*
+ * Blank pages (all 0xFF) that have not been written may be recognized
+ * as bad if bitflips occur, so whenever an uncorrectable error occurs,
+ * check if the entire page (with ECC bytes) is actually blank or not.
+ */
+ if (!data)
+ data_len = 0;
+ if (!spare)
+ spare_len = 0;
+ if (!ecc)
+ ecc_len = 0;
+
+ bf = nand_check_erased_ecc_chunk(data, data_len, ecc, ecc_len,
+ spare, spare_len, chip->ecc.strength);
+ if (bf < 0) {
+ mtd->ecc_stats.failed++;
+ return;
+ }
+
+ /* Update the stats and max_bitflips */
+ mtd->ecc_stats.corrected += bf;
+ *max_bitflips = max_t(unsigned int, *max_bitflips, bf);
+}
+
+/*
+ * Check a chunk is correct or not according to hardware ECC engine.
+ * mtd->ecc_stats.corrected is updated, as well as max_bitflips, however
+ * mtd->ecc_stats.failure is not, the function will instead return a non-zero
+ * value indicating that a check on the emptyness of the subpage must be
+ * performed before declaring the subpage corrupted.
+ */
+static int marvell_nfc_hw_ecc_correct(struct nand_chip *chip,
+ unsigned int *max_bitflips)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+ int bf = 0;
+ u32 ndsr;
+
+ ndsr = readl_relaxed(nfc->regs + NDSR);
+
+ /* Check uncorrectable error flag */
+ if (ndsr & NDSR_UNCERR) {
+ writel_relaxed(ndsr, nfc->regs + NDSR);
+
+ /*
+ * Do not increment ->ecc_stats.failed now, instead, return a
+ * non-zero value to indicate that this chunk was apparently
+ * bad, and it should be check to see if it empty or not. If
+ * the chunk (with ECC bytes) is not declared empty, the calling
+ * function must increment the failure count.
+ */
+ return -EBADMSG;
+ }
+
+ /* Check correctable error flag */
+ if (ndsr & NDSR_CORERR) {
+ writel_relaxed(ndsr, nfc->regs + NDSR);
+
+ if (chip->ecc.algo == NAND_ECC_BCH)
+ bf = NDSR_ERRCNT(ndsr);
+ else
+ bf = 1;
+ }
+
+ /* Update the stats and max_bitflips */
+ mtd->ecc_stats.corrected += bf;
+ *max_bitflips = max_t(unsigned int, *max_bitflips, bf);
+
+ return 0;
+}
+
+/* Hamming read helpers */
+static int marvell_nfc_hw_ecc_hmg_do_read_page(struct nand_chip *chip,
+ u8 *data_buf, u8 *oob_buf,
+ bool raw, int page)
+{
+ struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
+ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+ const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
+ struct marvell_nfc_op nfc_op = {
+ .ndcb[0] = NDCB0_CMD_TYPE(TYPE_READ) |
+ NDCB0_ADDR_CYC(marvell_nand->addr_cyc) |
+ NDCB0_DBC |
+ NDCB0_CMD1(NAND_CMD_READ0) |
+ NDCB0_CMD2(NAND_CMD_READSTART),
+ .ndcb[1] = NDCB1_ADDRS_PAGE(page),
+ .ndcb[2] = NDCB2_ADDR5_PAGE(page),
+ };
+ unsigned int oob_bytes = lt->spare_bytes + (raw ? lt->ecc_bytes : 0);
+ int ret;
+
+ /* NFCv2 needs more information about the operation being executed */
+ if (nfc->caps->is_nfcv2)
+ nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW);
+
+ ret = marvell_nfc_prepare_cmd(chip);
+ if (ret)
+ return ret;
+
+ marvell_nfc_send_cmd(chip, &nfc_op);
+ ret = marvell_nfc_end_cmd(chip, NDSR_RDDREQ,
+ "RDDREQ while draining FIFO (data/oob)");
+ if (ret)
+ return ret;
+
+ /*
+ * Read the page then the OOB area. Unlike what is shown in current
+ * documentation, spare bytes are protected by the ECC engine, and must
+ * be at the beginning of the OOB area or running this driver on legacy
+ * systems will prevent the discovery of the BBM/BBT.
+ */
+ if (nfc->use_dma) {
+ marvell_nfc_xfer_data_dma(nfc, DMA_FROM_DEVICE,
+ lt->data_bytes + oob_bytes);
+ memcpy(data_buf, nfc->dma_buf, lt->data_bytes);
+ memcpy(oob_buf, nfc->dma_buf + lt->data_bytes, oob_bytes);
+ } else {
+ marvell_nfc_xfer_data_in_pio(nfc, data_buf, lt->data_bytes);
+ marvell_nfc_xfer_data_in_pio(nfc, oob_buf, oob_bytes);
+ }
+
+ ret = marvell_nfc_wait_cmdd(chip);
+
+ return ret;
+}
+
+static int marvell_nfc_hw_ecc_hmg_read_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, u8 *buf,
+ int oob_required, int page)
+{
+ return marvell_nfc_hw_ecc_hmg_do_read_page(chip, buf, chip->oob_poi,
+ true, page);
+}
+
+static int marvell_nfc_hw_ecc_hmg_read_page(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ u8 *buf, int oob_required,
+ int page)
+{
+ const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
+ unsigned int full_sz = lt->data_bytes + lt->spare_bytes + lt->ecc_bytes;
+ int max_bitflips = 0, ret;
+ u8 *raw_buf;
+
+ marvell_nfc_enable_hw_ecc(chip);
+ marvell_nfc_hw_ecc_hmg_do_read_page(chip, buf, chip->oob_poi, false,
+ page);
+ ret = marvell_nfc_hw_ecc_correct(chip, &max_bitflips);
+ marvell_nfc_disable_hw_ecc(chip);
+
+ if (!ret)
+ return max_bitflips;
+
+ /*
+ * When ECC failures are detected, check if the full page has been
+ * written or not. Ignore the failure if it is actually empty.
+ */
+ raw_buf = kmalloc(full_sz, GFP_KERNEL);
+ if (!raw_buf)
+ return -ENOMEM;
+
+ marvell_nfc_hw_ecc_hmg_do_read_page(chip, raw_buf, raw_buf +
+ lt->data_bytes, true, page);
+ marvell_nfc_check_empty_chunk(chip, raw_buf, full_sz, NULL, 0, NULL, 0,
+ &max_bitflips);
+ kfree(raw_buf);
+
+ return max_bitflips;
+}
+
+/*
+ * Spare area in Hamming layouts is not protected by the ECC engine (even if
+ * it appears before the ECC bytes when reading), the ->read_oob_raw() function
+ * also stands for ->read_oob().
+ */
+static int marvell_nfc_hw_ecc_hmg_read_oob_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, int page)
+{
+ /* Invalidate page cache */
+ chip->pagebuf = -1;
+
+ return marvell_nfc_hw_ecc_hmg_do_read_page(chip, chip->data_buf,
+ chip->oob_poi, true, page);
+}
+
+/* Hamming write helpers */
+static int marvell_nfc_hw_ecc_hmg_do_write_page(struct nand_chip *chip,
+ const u8 *data_buf,
+ const u8 *oob_buf, bool raw,
+ int page)
+{
+ struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
+ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+ const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
+ struct marvell_nfc_op nfc_op = {
+ .ndcb[0] = NDCB0_CMD_TYPE(TYPE_WRITE) |
+ NDCB0_ADDR_CYC(marvell_nand->addr_cyc) |
+ NDCB0_CMD1(NAND_CMD_SEQIN) |
+ NDCB0_CMD2(NAND_CMD_PAGEPROG) |
+ NDCB0_DBC,
+ .ndcb[1] = NDCB1_ADDRS_PAGE(page),
+ .ndcb[2] = NDCB2_ADDR5_PAGE(page),
+ };
+ unsigned int oob_bytes = lt->spare_bytes + (raw ? lt->ecc_bytes : 0);
+ int ret;
+
+ /* NFCv2 needs more information about the operation being executed */
+ if (nfc->caps->is_nfcv2)
+ nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW);
+
+ ret = marvell_nfc_prepare_cmd(chip);
+ if (ret)
+ return ret;
+
+ marvell_nfc_send_cmd(chip, &nfc_op);
+ ret = marvell_nfc_end_cmd(chip, NDSR_WRDREQ,
+ "WRDREQ while loading FIFO (data)");
+ if (ret)
+ return ret;
+
+ /* Write the page then the OOB area */
+ if (nfc->use_dma) {
+ memcpy(nfc->dma_buf, data_buf, lt->data_bytes);
+ memcpy(nfc->dma_buf + lt->data_bytes, oob_buf, oob_bytes);
+ marvell_nfc_xfer_data_dma(nfc, DMA_TO_DEVICE, lt->data_bytes +
+ lt->ecc_bytes + lt->spare_bytes);
+ } else {
+ marvell_nfc_xfer_data_out_pio(nfc, data_buf, lt->data_bytes);
+ marvell_nfc_xfer_data_out_pio(nfc, oob_buf, oob_bytes);
+ }
+
+ ret = marvell_nfc_wait_cmdd(chip);
+ if (ret)
+ return ret;
+
+ ret = marvell_nfc_wait_op(chip,
+ chip->data_interface.timings.sdr.tPROG_max);
+ return ret;
+}
+
+static int marvell_nfc_hw_ecc_hmg_write_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ const u8 *buf,
+ int oob_required, int page)
+{
+ return marvell_nfc_hw_ecc_hmg_do_write_page(chip, buf, chip->oob_poi,
+ true, page);
+}
+
+static int marvell_nfc_hw_ecc_hmg_write_page(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ const u8 *buf,
+ int oob_required, int page)
+{
+ int ret;
+
+ marvell_nfc_enable_hw_ecc(chip);
+ ret = marvell_nfc_hw_ecc_hmg_do_write_page(chip, buf, chip->oob_poi,
+ false, page);
+ marvell_nfc_disable_hw_ecc(chip);
+
+ return ret;
+}
+
+/*
+ * Spare area in Hamming layouts is not protected by the ECC engine (even if
+ * it appears before the ECC bytes when reading), the ->write_oob_raw() function
+ * also stands for ->write_oob().
+ */
+static int marvell_nfc_hw_ecc_hmg_write_oob_raw(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ int page)
+{
+ /* Invalidate page cache */
+ chip->pagebuf = -1;
+
+ memset(chip->data_buf, 0xFF, mtd->writesize);
+
+ return marvell_nfc_hw_ecc_hmg_do_write_page(chip, chip->data_buf,
+ chip->oob_poi, true, page);
+}
+
+/* BCH read helpers */
+static int marvell_nfc_hw_ecc_bch_read_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, u8 *buf,
+ int oob_required, int page)
+{
+ const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
+ u8 *oob = chip->oob_poi;
+ int chunk_size = lt->data_bytes + lt->spare_bytes + lt->ecc_bytes;
+ int ecc_offset = (lt->full_chunk_cnt * lt->spare_bytes) +
+ lt->last_spare_bytes;
+ int data_len = lt->data_bytes;
+ int spare_len = lt->spare_bytes;
+ int ecc_len = lt->ecc_bytes;
+ int chunk;
+
+ if (oob_required)
+ memset(chip->oob_poi, 0xFF, mtd->oobsize);
+
+ nand_read_page_op(chip, page, 0, NULL, 0);
+
+ for (chunk = 0; chunk < lt->nchunks; chunk++) {
+ /* Update last chunk length */
+ if (chunk >= lt->full_chunk_cnt) {
+ data_len = lt->last_data_bytes;
+ spare_len = lt->last_spare_bytes;
+ ecc_len = lt->last_ecc_bytes;
+ }
+
+ /* Read data bytes*/
+ nand_change_read_column_op(chip, chunk * chunk_size,
+ buf + (lt->data_bytes * chunk),
+ data_len, false);
+
+ /* Read spare bytes */
+ nand_read_data_op(chip, oob + (lt->spare_bytes * chunk),
+ spare_len, false);
+
+ /* Read ECC bytes */
+ nand_read_data_op(chip, oob + ecc_offset +
+ (ALIGN(lt->ecc_bytes, 32) * chunk),
+ ecc_len, false);
+ }
+
+ return 0;
+}
+
+static void marvell_nfc_hw_ecc_bch_read_chunk(struct nand_chip *chip, int chunk,
+ u8 *data, unsigned int data_len,
+ u8 *spare, unsigned int spare_len,
+ int page)
+{
+ struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
+ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+ const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
+ int i, ret;
+ struct marvell_nfc_op nfc_op = {
+ .ndcb[0] = NDCB0_CMD_TYPE(TYPE_READ) |
+ NDCB0_ADDR_CYC(marvell_nand->addr_cyc) |
+ NDCB0_LEN_OVRD,
+ .ndcb[1] = NDCB1_ADDRS_PAGE(page),
+ .ndcb[2] = NDCB2_ADDR5_PAGE(page),
+ .ndcb[3] = data_len + spare_len,
+ };
+
+ ret = marvell_nfc_prepare_cmd(chip);
+ if (ret)
+ return;
+
+ if (chunk == 0)
+ nfc_op.ndcb[0] |= NDCB0_DBC |
+ NDCB0_CMD1(NAND_CMD_READ0) |
+ NDCB0_CMD2(NAND_CMD_READSTART);
+
+ /*
+ * Trigger the naked read operation only on the last chunk.
+ * Otherwise, use monolithic read.
+ */
+ if (lt->nchunks == 1 || (chunk < lt->nchunks - 1))
+ nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW);
+ else
+ nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_LAST_NAKED_RW);
+
+ marvell_nfc_send_cmd(chip, &nfc_op);
+
+ /*
+ * According to the datasheet, when reading from NDDB
+ * with BCH enabled, after each 32 bytes reads, we
+ * have to make sure that the NDSR.RDDREQ bit is set.
+ *
+ * Drain the FIFO, 8 32-bit reads at a time, and skip
+ * the polling on the last read.
+ *
+ * Length is a multiple of 32 bytes, hence it is a multiple of 8 too.
+ */
+ for (i = 0; i < data_len; i += FIFO_DEPTH * BCH_SEQ_READS) {
+ marvell_nfc_end_cmd(chip, NDSR_RDDREQ,
+ "RDDREQ while draining FIFO (data)");
+ marvell_nfc_xfer_data_in_pio(nfc, data,
+ FIFO_DEPTH * BCH_SEQ_READS);
+ data += FIFO_DEPTH * BCH_SEQ_READS;
+ }
+
+ for (i = 0; i < spare_len; i += FIFO_DEPTH * BCH_SEQ_READS) {
+ marvell_nfc_end_cmd(chip, NDSR_RDDREQ,
+ "RDDREQ while draining FIFO (OOB)");
+ marvell_nfc_xfer_data_in_pio(nfc, spare,
+ FIFO_DEPTH * BCH_SEQ_READS);
+ spare += FIFO_DEPTH * BCH_SEQ_READS;
+ }
+}
+
+static int marvell_nfc_hw_ecc_bch_read_page(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ u8 *buf, int oob_required,
+ int page)
+{
+ const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
+ int data_len = lt->data_bytes, spare_len = lt->spare_bytes, ecc_len;
+ u8 *data = buf, *spare = chip->oob_poi, *ecc;
+ int max_bitflips = 0;
+ u32 failure_mask = 0;
+ int chunk, ecc_offset_in_page, ret;
+
+ /*
+ * With BCH, OOB is not fully used (and thus not read entirely), not
+ * expected bytes could show up at the end of the OOB buffer if not
+ * explicitly erased.
+ */
+ if (oob_required)
+ memset(chip->oob_poi, 0xFF, mtd->oobsize);
+
+ marvell_nfc_enable_hw_ecc(chip);
+
+ for (chunk = 0; chunk < lt->nchunks; chunk++) {
+ /* Update length for the last chunk */
+ if (chunk >= lt->full_chunk_cnt) {
+ data_len = lt->last_data_bytes;
+ spare_len = lt->last_spare_bytes;
+ }
+
+ /* Read the chunk and detect number of bitflips */
+ marvell_nfc_hw_ecc_bch_read_chunk(chip, chunk, data, data_len,
+ spare, spare_len, page);
+ ret = marvell_nfc_hw_ecc_correct(chip, &max_bitflips);
+ if (ret)
+ failure_mask |= BIT(chunk);
+
+ data += data_len;
+ spare += spare_len;
+ }
+
+ marvell_nfc_disable_hw_ecc(chip);
+
+ if (!failure_mask)
+ return max_bitflips;
+
+ /*
+ * Please note that dumping the ECC bytes during a normal read with OOB
+ * area would add a significant overhead as ECC bytes are "consumed" by
+ * the controller in normal mode and must be re-read in raw mode. To
+ * avoid dropping the performances, we prefer not to include them. The
+ * user should re-read the page in raw mode if ECC bytes are required.
+ *
+ * However, for any subpage read error reported by ->correct(), the ECC
+ * bytes must be read in raw mode and the full subpage must be checked
+ * to see if it is entirely empty of if there was an actual error.
+ */
+ for (chunk = 0; chunk < lt->nchunks; chunk++) {
+ /* No failure reported for this chunk, move to the next one */
+ if (!(failure_mask & BIT(chunk)))
+ continue;
+
+ /* Derive ECC bytes positions (in page/buffer) and length */
+ ecc = chip->oob_poi +
+ (lt->full_chunk_cnt * lt->spare_bytes) +
+ lt->last_spare_bytes +
+ (chunk * ALIGN(lt->ecc_bytes, 32));
+ ecc_offset_in_page =
+ (chunk * (lt->data_bytes + lt->spare_bytes +
+ lt->ecc_bytes)) +
+ (chunk < lt->full_chunk_cnt ?
+ lt->data_bytes + lt->spare_bytes :
+ lt->last_data_bytes + lt->last_spare_bytes);
+ ecc_len = chunk < lt->full_chunk_cnt ?
+ lt->ecc_bytes : lt->last_ecc_bytes;
+
+ /* Do the actual raw read of the ECC bytes */
+ nand_change_read_column_op(chip, ecc_offset_in_page,
+ ecc, ecc_len, false);
+
+ /* Derive data/spare bytes positions (in buffer) and length */
+ data = buf + (chunk * lt->data_bytes);
+ data_len = chunk < lt->full_chunk_cnt ?
+ lt->data_bytes : lt->last_data_bytes;
+ spare = chip->oob_poi + (chunk * (lt->spare_bytes +
+ lt->ecc_bytes));
+ spare_len = chunk < lt->full_chunk_cnt ?
+ lt->spare_bytes : lt->last_spare_bytes;
+
+ /* Check the entire chunk (data + spare + ecc) for emptyness */
+ marvell_nfc_check_empty_chunk(chip, data, data_len, spare,
+ spare_len, ecc, ecc_len,
+ &max_bitflips);
+ }
+
+ return max_bitflips;
+}
+
+static int marvell_nfc_hw_ecc_bch_read_oob_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, int page)
+{
+ /* Invalidate page cache */
+ chip->pagebuf = -1;
+
+ return chip->ecc.read_page_raw(mtd, chip, chip->data_buf, true, page);
+}
+
+static int marvell_nfc_hw_ecc_bch_read_oob(struct mtd_info *mtd,
+ struct nand_chip *chip, int page)
+{
+ /* Invalidate page cache */
+ chip->pagebuf = -1;
+
+ return chip->ecc.read_page(mtd, chip, chip->data_buf, true, page);
+}
+
+/* BCH write helpers */
+static int marvell_nfc_hw_ecc_bch_write_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ const u8 *buf,
+ int oob_required, int page)
+{
+ const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
+ int full_chunk_size = lt->data_bytes + lt->spare_bytes + lt->ecc_bytes;
+ int data_len = lt->data_bytes;
+ int spare_len = lt->spare_bytes;
+ int ecc_len = lt->ecc_bytes;
+ int spare_offset = 0;
+ int ecc_offset = (lt->full_chunk_cnt * lt->spare_bytes) +
+ lt->last_spare_bytes;
+ int chunk;
+
+ nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+
+ for (chunk = 0; chunk < lt->nchunks; chunk++) {
+ if (chunk >= lt->full_chunk_cnt) {
+ data_len = lt->last_data_bytes;
+ spare_len = lt->last_spare_bytes;
+ ecc_len = lt->last_ecc_bytes;
+ }
+
+ /* Point to the column of the next chunk */
+ nand_change_write_column_op(chip, chunk * full_chunk_size,
+ NULL, 0, false);
+
+ /* Write the data */
+ nand_write_data_op(chip, buf + (chunk * lt->data_bytes),
+ data_len, false);
+
+ if (!oob_required)
+ continue;
+
+ /* Write the spare bytes */
+ if (spare_len)
+ nand_write_data_op(chip, chip->oob_poi + spare_offset,
+ spare_len, false);
+
+ /* Write the ECC bytes */
+ if (ecc_len)
+ nand_write_data_op(chip, chip->oob_poi + ecc_offset,
+ ecc_len, false);
+
+ spare_offset += spare_len;
+ ecc_offset += ALIGN(ecc_len, 32);
+ }
+
+ return nand_prog_page_end_op(chip);
+}
+
+static int
+marvell_nfc_hw_ecc_bch_write_chunk(struct nand_chip *chip, int chunk,
+ const u8 *data, unsigned int data_len,
+ const u8 *spare, unsigned int spare_len,
+ int page)
+{
+ struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
+ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+ const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
+ int ret;
+ struct marvell_nfc_op nfc_op = {
+ .ndcb[0] = NDCB0_CMD_TYPE(TYPE_WRITE) | NDCB0_LEN_OVRD,
+ .ndcb[3] = data_len + spare_len,
+ };
+
+ /*
+ * First operation dispatches the CMD_SEQIN command, issue the address
+ * cycles and asks for the first chunk of data.
+ * All operations in the middle (if any) will issue a naked write and
+ * also ask for data.
+ * Last operation (if any) asks for the last chunk of data through a
+ * last naked write.
+ */
+ if (chunk == 0) {
+ nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_WRITE_DISPATCH) |
+ NDCB0_ADDR_CYC(marvell_nand->addr_cyc) |
+ NDCB0_CMD1(NAND_CMD_SEQIN);
+ nfc_op.ndcb[1] |= NDCB1_ADDRS_PAGE(page);
+ nfc_op.ndcb[2] |= NDCB2_ADDR5_PAGE(page);
+ } else if (chunk < lt->nchunks - 1) {
+ nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_NAKED_RW);
+ } else {
+ nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_LAST_NAKED_RW);
+ }
+
+ /* Always dispatch the PAGEPROG command on the last chunk */
+ if (chunk == lt->nchunks - 1)
+ nfc_op.ndcb[0] |= NDCB0_CMD2(NAND_CMD_PAGEPROG) | NDCB0_DBC;
+
+ ret = marvell_nfc_prepare_cmd(chip);
+ if (ret)
+ return ret;
+
+ marvell_nfc_send_cmd(chip, &nfc_op);
+ ret = marvell_nfc_end_cmd(chip, NDSR_WRDREQ,
+ "WRDREQ while loading FIFO (data)");
+ if (ret)
+ return ret;
+
+ /* Transfer the contents */
+ iowrite32_rep(nfc->regs + NDDB, data, FIFO_REP(data_len));
+ iowrite32_rep(nfc->regs + NDDB, spare, FIFO_REP(spare_len));
+
+ return 0;
+}
+
+static int marvell_nfc_hw_ecc_bch_write_page(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ const u8 *buf,
+ int oob_required, int page)
+{
+ const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
+ const u8 *data = buf;
+ const u8 *spare = chip->oob_poi;
+ int data_len = lt->data_bytes;
+ int spare_len = lt->spare_bytes;
+ int chunk, ret;
+
+ /* Spare data will be written anyway, so clear it to avoid garbage */
+ if (!oob_required)
+ memset(chip->oob_poi, 0xFF, mtd->oobsize);
+
+ marvell_nfc_enable_hw_ecc(chip);
+
+ for (chunk = 0; chunk < lt->nchunks; chunk++) {
+ if (chunk >= lt->full_chunk_cnt) {
+ data_len = lt->last_data_bytes;
+ spare_len = lt->last_spare_bytes;
+ }
+
+ marvell_nfc_hw_ecc_bch_write_chunk(chip, chunk, data, data_len,
+ spare, spare_len, page);
+ data += data_len;
+ spare += spare_len;
+
+ /*
+ * Waiting only for CMDD or PAGED is not enough, ECC are
+ * partially written. No flag is set once the operation is
+ * really finished but the ND_RUN bit is cleared, so wait for it
+ * before stepping into the next command.
+ */
+ marvell_nfc_wait_ndrun(chip);
+ }
+
+ ret = marvell_nfc_wait_op(chip,
+ chip->data_interface.timings.sdr.tPROG_max);
+
+ marvell_nfc_disable_hw_ecc(chip);
+
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int marvell_nfc_hw_ecc_bch_write_oob_raw(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ int page)
+{
+ /* Invalidate page cache */
+ chip->pagebuf = -1;
+
+ memset(chip->data_buf, 0xFF, mtd->writesize);
+
+ return chip->ecc.write_page_raw(mtd, chip, chip->data_buf, true, page);
+}
+
+static int marvell_nfc_hw_ecc_bch_write_oob(struct mtd_info *mtd,
+ struct nand_chip *chip, int page)
+{
+ /* Invalidate page cache */
+ chip->pagebuf = -1;
+
+ memset(chip->data_buf, 0xFF, mtd->writesize);
+
+ return chip->ecc.write_page(mtd, chip, chip->data_buf, true, page);
+}
+
+/* NAND framework ->exec_op() hooks and related helpers */
+static void marvell_nfc_parse_instructions(struct nand_chip *chip,
+ const struct nand_subop *subop,
+ struct marvell_nfc_op *nfc_op)
+{
+ const struct nand_op_instr *instr = NULL;
+ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+ bool first_cmd = true;
+ unsigned int op_id;
+ int i;
+
+ /* Reset the input structure as most of its fields will be OR'ed */
+ memset(nfc_op, 0, sizeof(struct marvell_nfc_op));
+
+ for (op_id = 0; op_id < subop->ninstrs; op_id++) {
+ unsigned int offset, naddrs;
+ const u8 *addrs;
+ int len = nand_subop_get_data_len(subop, op_id);
+
+ instr = &subop->instrs[op_id];
+
+ switch (instr->type) {
+ case NAND_OP_CMD_INSTR:
+ if (first_cmd)
+ nfc_op->ndcb[0] |=
+ NDCB0_CMD1(instr->ctx.cmd.opcode);
+ else
+ nfc_op->ndcb[0] |=
+ NDCB0_CMD2(instr->ctx.cmd.opcode) |
+ NDCB0_DBC;
+
+ nfc_op->cle_ale_delay_ns = instr->delay_ns;
+ first_cmd = false;
+ break;
+
+ case NAND_OP_ADDR_INSTR:
+ offset = nand_subop_get_addr_start_off(subop, op_id);
+ naddrs = nand_subop_get_num_addr_cyc(subop, op_id);
+ addrs = &instr->ctx.addr.addrs[offset];
+
+ nfc_op->ndcb[0] |= NDCB0_ADDR_CYC(naddrs);
+
+ for (i = 0; i < min_t(unsigned int, 4, naddrs); i++)
+ nfc_op->ndcb[1] |= addrs[i] << (8 * i);
+
+ if (naddrs >= 5)
+ nfc_op->ndcb[2] |= NDCB2_ADDR5_CYC(addrs[4]);
+ if (naddrs >= 6)
+ nfc_op->ndcb[3] |= NDCB3_ADDR6_CYC(addrs[5]);
+ if (naddrs == 7)
+ nfc_op->ndcb[3] |= NDCB3_ADDR7_CYC(addrs[6]);
+
+ nfc_op->cle_ale_delay_ns = instr->delay_ns;
+ break;
+
+ case NAND_OP_DATA_IN_INSTR:
+ nfc_op->data_instr = instr;
+ nfc_op->data_instr_idx = op_id;
+ nfc_op->ndcb[0] |= NDCB0_CMD_TYPE(TYPE_READ);
+ if (nfc->caps->is_nfcv2) {
+ nfc_op->ndcb[0] |=
+ NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW) |
+ NDCB0_LEN_OVRD;
+ nfc_op->ndcb[3] |= round_up(len, FIFO_DEPTH);
+ }
+ nfc_op->data_delay_ns = instr->delay_ns;
+ break;
+
+ case NAND_OP_DATA_OUT_INSTR:
+ nfc_op->data_instr = instr;
+ nfc_op->data_instr_idx = op_id;
+ nfc_op->ndcb[0] |= NDCB0_CMD_TYPE(TYPE_WRITE);
+ if (nfc->caps->is_nfcv2) {
+ nfc_op->ndcb[0] |=
+ NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW) |
+ NDCB0_LEN_OVRD;
+ nfc_op->ndcb[3] |= round_up(len, FIFO_DEPTH);
+ }
+ nfc_op->data_delay_ns = instr->delay_ns;
+ break;
+
+ case NAND_OP_WAITRDY_INSTR:
+ nfc_op->rdy_timeout_ms = instr->ctx.waitrdy.timeout_ms;
+ nfc_op->rdy_delay_ns = instr->delay_ns;
+ break;
+ }
+ }
+}
+
+static int marvell_nfc_xfer_data_pio(struct nand_chip *chip,
+ const struct nand_subop *subop,
+ struct marvell_nfc_op *nfc_op)
+{
+ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+ const struct nand_op_instr *instr = nfc_op->data_instr;
+ unsigned int op_id = nfc_op->data_instr_idx;
+ unsigned int len = nand_subop_get_data_len(subop, op_id);
+ unsigned int offset = nand_subop_get_data_start_off(subop, op_id);
+ bool reading = (instr->type == NAND_OP_DATA_IN_INSTR);
+ int ret;
+
+ if (instr->ctx.data.force_8bit)
+ marvell_nfc_force_byte_access(chip, true);
+
+ if (reading) {
+ u8 *in = instr->ctx.data.buf.in + offset;
+
+ ret = marvell_nfc_xfer_data_in_pio(nfc, in, len);
+ } else {
+ const u8 *out = instr->ctx.data.buf.out + offset;
+
+ ret = marvell_nfc_xfer_data_out_pio(nfc, out, len);
+ }
+
+ if (instr->ctx.data.force_8bit)
+ marvell_nfc_force_byte_access(chip, false);
+
+ return ret;
+}
+
+static int marvell_nfc_monolithic_access_exec(struct nand_chip *chip,
+ const struct nand_subop *subop)
+{
+ struct marvell_nfc_op nfc_op;
+ bool reading;
+ int ret;
+
+ marvell_nfc_parse_instructions(chip, subop, &nfc_op);
+ reading = (nfc_op.data_instr->type == NAND_OP_DATA_IN_INSTR);
+
+ ret = marvell_nfc_prepare_cmd(chip);
+ if (ret)
+ return ret;
+
+ marvell_nfc_send_cmd(chip, &nfc_op);
+ ret = marvell_nfc_end_cmd(chip, NDSR_RDDREQ | NDSR_WRDREQ,
+ "RDDREQ/WRDREQ while draining raw data");
+ if (ret)
+ return ret;
+
+ cond_delay(nfc_op.cle_ale_delay_ns);
+
+ if (reading) {
+ if (nfc_op.rdy_timeout_ms) {
+ ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms);
+ if (ret)
+ return ret;
+ }
+
+ cond_delay(nfc_op.rdy_delay_ns);
+ }
+
+ marvell_nfc_xfer_data_pio(chip, subop, &nfc_op);
+ ret = marvell_nfc_wait_cmdd(chip);
+ if (ret)
+ return ret;
+
+ cond_delay(nfc_op.data_delay_ns);
+
+ if (!reading) {
+ if (nfc_op.rdy_timeout_ms) {
+ ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms);
+ if (ret)
+ return ret;
+ }
+
+ cond_delay(nfc_op.rdy_delay_ns);
+ }
+
+ /*
+ * NDCR ND_RUN bit should be cleared automatically at the end of each
+ * operation but experience shows that the behavior is buggy when it
+ * comes to writes (with LEN_OVRD). Clear it by hand in this case.
+ */
+ if (!reading) {
+ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+
+ writel_relaxed(readl(nfc->regs + NDCR) & ~NDCR_ND_RUN,
+ nfc->regs + NDCR);
+ }
+
+ return 0;
+}
+
+static int marvell_nfc_naked_access_exec(struct nand_chip *chip,
+ const struct nand_subop *subop)
+{
+ struct marvell_nfc_op nfc_op;
+ int ret;
+
+ marvell_nfc_parse_instructions(chip, subop, &nfc_op);
+
+ /*
+ * Naked access are different in that they need to be flagged as naked
+ * by the controller. Reset the controller registers fields that inform
+ * on the type and refill them according to the ongoing operation.
+ */
+ nfc_op.ndcb[0] &= ~(NDCB0_CMD_TYPE(TYPE_MASK) |
+ NDCB0_CMD_XTYPE(XTYPE_MASK));
+ switch (subop->instrs[0].type) {
+ case NAND_OP_CMD_INSTR:
+ nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_NAKED_CMD);
+ break;
+ case NAND_OP_ADDR_INSTR:
+ nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_NAKED_ADDR);
+ break;
+ case NAND_OP_DATA_IN_INSTR:
+ nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_READ) |
+ NDCB0_CMD_XTYPE(XTYPE_LAST_NAKED_RW);
+ break;
+ case NAND_OP_DATA_OUT_INSTR:
+ nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_WRITE) |
+ NDCB0_CMD_XTYPE(XTYPE_LAST_NAKED_RW);
+ break;
+ default:
+ /* This should never happen */
+ break;
+ }
+
+ ret = marvell_nfc_prepare_cmd(chip);
+ if (ret)
+ return ret;
+
+ marvell_nfc_send_cmd(chip, &nfc_op);
+
+ if (!nfc_op.data_instr) {
+ ret = marvell_nfc_wait_cmdd(chip);
+ cond_delay(nfc_op.cle_ale_delay_ns);
+ return ret;
+ }
+
+ ret = marvell_nfc_end_cmd(chip, NDSR_RDDREQ | NDSR_WRDREQ,
+ "RDDREQ/WRDREQ while draining raw data");
+ if (ret)
+ return ret;
+
+ marvell_nfc_xfer_data_pio(chip, subop, &nfc_op);
+ ret = marvell_nfc_wait_cmdd(chip);
+ if (ret)
+ return ret;
+
+ /*
+ * NDCR ND_RUN bit should be cleared automatically at the end of each
+ * operation but experience shows that the behavior is buggy when it
+ * comes to writes (with LEN_OVRD). Clear it by hand in this case.
+ */
+ if (subop->instrs[0].type == NAND_OP_DATA_OUT_INSTR) {
+ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+
+ writel_relaxed(readl(nfc->regs + NDCR) & ~NDCR_ND_RUN,
+ nfc->regs + NDCR);
+ }
+
+ return 0;
+}
+
+static int marvell_nfc_naked_waitrdy_exec(struct nand_chip *chip,
+ const struct nand_subop *subop)
+{
+ struct marvell_nfc_op nfc_op;
+ int ret;
+
+ marvell_nfc_parse_instructions(chip, subop, &nfc_op);
+
+ ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms);
+ cond_delay(nfc_op.rdy_delay_ns);
+
+ return ret;
+}
+
+static int marvell_nfc_read_id_type_exec(struct nand_chip *chip,
+ const struct nand_subop *subop)
+{
+ struct marvell_nfc_op nfc_op;
+ int ret;
+
+ marvell_nfc_parse_instructions(chip, subop, &nfc_op);
+ nfc_op.ndcb[0] &= ~NDCB0_CMD_TYPE(TYPE_READ);
+ nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_READ_ID);
+
+ ret = marvell_nfc_prepare_cmd(chip);
+ if (ret)
+ return ret;
+
+ marvell_nfc_send_cmd(chip, &nfc_op);
+ ret = marvell_nfc_end_cmd(chip, NDSR_RDDREQ,
+ "RDDREQ while reading ID");
+ if (ret)
+ return ret;
+
+ cond_delay(nfc_op.cle_ale_delay_ns);
+
+ if (nfc_op.rdy_timeout_ms) {
+ ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms);
+ if (ret)
+ return ret;
+ }
+
+ cond_delay(nfc_op.rdy_delay_ns);
+
+ marvell_nfc_xfer_data_pio(chip, subop, &nfc_op);
+ ret = marvell_nfc_wait_cmdd(chip);
+ if (ret)
+ return ret;
+
+ cond_delay(nfc_op.data_delay_ns);
+
+ return 0;
+}
+
+static int marvell_nfc_read_status_exec(struct nand_chip *chip,
+ const struct nand_subop *subop)
+{
+ struct marvell_nfc_op nfc_op;
+ int ret;
+
+ marvell_nfc_parse_instructions(chip, subop, &nfc_op);
+ nfc_op.ndcb[0] &= ~NDCB0_CMD_TYPE(TYPE_READ);
+ nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_STATUS);
+
+ ret = marvell_nfc_prepare_cmd(chip);
+ if (ret)
+ return ret;
+
+ marvell_nfc_send_cmd(chip, &nfc_op);
+ ret = marvell_nfc_end_cmd(chip, NDSR_RDDREQ,
+ "RDDREQ while reading status");
+ if (ret)
+ return ret;
+
+ cond_delay(nfc_op.cle_ale_delay_ns);
+
+ if (nfc_op.rdy_timeout_ms) {
+ ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms);
+ if (ret)
+ return ret;
+ }
+
+ cond_delay(nfc_op.rdy_delay_ns);
+
+ marvell_nfc_xfer_data_pio(chip, subop, &nfc_op);
+ ret = marvell_nfc_wait_cmdd(chip);
+ if (ret)
+ return ret;
+
+ cond_delay(nfc_op.data_delay_ns);
+
+ return 0;
+}
+
+static int marvell_nfc_reset_cmd_type_exec(struct nand_chip *chip,
+ const struct nand_subop *subop)
+{
+ struct marvell_nfc_op nfc_op;
+ int ret;
+
+ marvell_nfc_parse_instructions(chip, subop, &nfc_op);
+ nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_RESET);
+
+ ret = marvell_nfc_prepare_cmd(chip);
+ if (ret)
+ return ret;
+
+ marvell_nfc_send_cmd(chip, &nfc_op);
+ ret = marvell_nfc_wait_cmdd(chip);
+ if (ret)
+ return ret;
+
+ cond_delay(nfc_op.cle_ale_delay_ns);
+
+ ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms);
+ if (ret)
+ return ret;
+
+ cond_delay(nfc_op.rdy_delay_ns);
+
+ return 0;
+}
+
+static int marvell_nfc_erase_cmd_type_exec(struct nand_chip *chip,
+ const struct nand_subop *subop)
+{
+ struct marvell_nfc_op nfc_op;
+ int ret;
+
+ marvell_nfc_parse_instructions(chip, subop, &nfc_op);
+ nfc_op.ndcb[0] |= NDCB0_CMD_TYPE(TYPE_ERASE);
+
+ ret = marvell_nfc_prepare_cmd(chip);
+ if (ret)
+ return ret;
+
+ marvell_nfc_send_cmd(chip, &nfc_op);
+ ret = marvell_nfc_wait_cmdd(chip);
+ if (ret)
+ return ret;
+
+ cond_delay(nfc_op.cle_ale_delay_ns);
+
+ ret = marvell_nfc_wait_op(chip, nfc_op.rdy_timeout_ms);
+ if (ret)
+ return ret;
+
+ cond_delay(nfc_op.rdy_delay_ns);
+
+ return 0;
+}
+
+static const struct nand_op_parser marvell_nfcv2_op_parser = NAND_OP_PARSER(
+ /* Monolithic reads/writes */
+ NAND_OP_PARSER_PATTERN(
+ marvell_nfc_monolithic_access_exec,
+ NAND_OP_PARSER_PAT_CMD_ELEM(false),
+ NAND_OP_PARSER_PAT_ADDR_ELEM(true, MAX_ADDRESS_CYC_NFCV2),
+ NAND_OP_PARSER_PAT_CMD_ELEM(true),
+ NAND_OP_PARSER_PAT_WAITRDY_ELEM(true),
+ NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, MAX_CHUNK_SIZE)),
+ NAND_OP_PARSER_PATTERN(
+ marvell_nfc_monolithic_access_exec,
+ NAND_OP_PARSER_PAT_CMD_ELEM(false),
+ NAND_OP_PARSER_PAT_ADDR_ELEM(false, MAX_ADDRESS_CYC_NFCV2),
+ NAND_OP_PARSER_PAT_DATA_OUT_ELEM(false, MAX_CHUNK_SIZE),
+ NAND_OP_PARSER_PAT_CMD_ELEM(true),
+ NAND_OP_PARSER_PAT_WAITRDY_ELEM(true)),
+ /* Naked commands */
+ NAND_OP_PARSER_PATTERN(
+ marvell_nfc_naked_access_exec,
+ NAND_OP_PARSER_PAT_CMD_ELEM(false)),
+ NAND_OP_PARSER_PATTERN(
+ marvell_nfc_naked_access_exec,
+ NAND_OP_PARSER_PAT_ADDR_ELEM(false, MAX_ADDRESS_CYC_NFCV2)),
+ NAND_OP_PARSER_PATTERN(
+ marvell_nfc_naked_access_exec,
+ NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, MAX_CHUNK_SIZE)),
+ NAND_OP_PARSER_PATTERN(
+ marvell_nfc_naked_access_exec,
+ NAND_OP_PARSER_PAT_DATA_OUT_ELEM(false, MAX_CHUNK_SIZE)),
+ NAND_OP_PARSER_PATTERN(
+ marvell_nfc_naked_waitrdy_exec,
+ NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)),
+ );
+
+static const struct nand_op_parser marvell_nfcv1_op_parser = NAND_OP_PARSER(
+ /* Naked commands not supported, use a function for each pattern */
+ NAND_OP_PARSER_PATTERN(
+ marvell_nfc_read_id_type_exec,
+ NAND_OP_PARSER_PAT_CMD_ELEM(false),
+ NAND_OP_PARSER_PAT_ADDR_ELEM(false, MAX_ADDRESS_CYC_NFCV1),
+ NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, 8)),
+ NAND_OP_PARSER_PATTERN(
+ marvell_nfc_erase_cmd_type_exec,
+ NAND_OP_PARSER_PAT_CMD_ELEM(false),
+ NAND_OP_PARSER_PAT_ADDR_ELEM(false, MAX_ADDRESS_CYC_NFCV1),
+ NAND_OP_PARSER_PAT_CMD_ELEM(false),
+ NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)),
+ NAND_OP_PARSER_PATTERN(
+ marvell_nfc_read_status_exec,
+ NAND_OP_PARSER_PAT_CMD_ELEM(false),
+ NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, 1)),
+ NAND_OP_PARSER_PATTERN(
+ marvell_nfc_reset_cmd_type_exec,
+ NAND_OP_PARSER_PAT_CMD_ELEM(false),
+ NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)),
+ NAND_OP_PARSER_PATTERN(
+ marvell_nfc_naked_waitrdy_exec,
+ NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)),
+ );
+
+static int marvell_nfc_exec_op(struct nand_chip *chip,
+ const struct nand_operation *op,
+ bool check_only)
+{
+ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+
+ if (nfc->caps->is_nfcv2)
+ return nand_op_parser_exec_op(chip, &marvell_nfcv2_op_parser,
+ op, check_only);
+ else
+ return nand_op_parser_exec_op(chip, &marvell_nfcv1_op_parser,
+ op, check_only);
+}
+
+/*
+ * Layouts were broken in old pxa3xx_nand driver, these are supposed to be
+ * usable.
+ */
+static int marvell_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
+
+ if (section)
+ return -ERANGE;
+
+ oobregion->length = (lt->full_chunk_cnt * lt->ecc_bytes) +
+ lt->last_ecc_bytes;
+ oobregion->offset = mtd->oobsize - oobregion->length;
+
+ return 0;
+}
+
+static int marvell_nand_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
+
+ if (section)
+ return -ERANGE;
+
+ /*
+ * Bootrom looks in bytes 0 & 5 for bad blocks for the
+ * 4KB page / 4bit BCH combination.
+ */
+ if (mtd->writesize == SZ_4K && lt->data_bytes == SZ_2K)
+ oobregion->offset = 6;
+ else
+ oobregion->offset = 2;
+
+ oobregion->length = (lt->full_chunk_cnt * lt->spare_bytes) +
+ lt->last_spare_bytes - oobregion->offset;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops marvell_nand_ooblayout_ops = {
+ .ecc = marvell_nand_ooblayout_ecc,
+ .free = marvell_nand_ooblayout_free,
+};
+
+static int marvell_nand_hw_ecc_ctrl_init(struct mtd_info *mtd,
+ struct nand_ecc_ctrl *ecc)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+ const struct marvell_hw_ecc_layout *l;
+ int i;
+
+ if (!nfc->caps->is_nfcv2 &&
+ (mtd->writesize + mtd->oobsize > MAX_CHUNK_SIZE)) {
+ dev_err(nfc->dev,
+ "NFCv1: writesize (%d) cannot be bigger than a chunk (%d)\n",
+ mtd->writesize, MAX_CHUNK_SIZE - mtd->oobsize);
+ return -ENOTSUPP;
+ }
+
+ to_marvell_nand(chip)->layout = NULL;
+ for (i = 0; i < ARRAY_SIZE(marvell_nfc_layouts); i++) {
+ l = &marvell_nfc_layouts[i];
+ if (mtd->writesize == l->writesize &&
+ ecc->size == l->chunk && ecc->strength == l->strength) {
+ to_marvell_nand(chip)->layout = l;
+ break;
+ }
+ }
+
+ if (!to_marvell_nand(chip)->layout ||
+ (!nfc->caps->is_nfcv2 && ecc->strength > 1)) {
+ dev_err(nfc->dev,
+ "ECC strength %d at page size %d is not supported\n",
+ ecc->strength, mtd->writesize);
+ return -ENOTSUPP;
+ }
+
+ mtd_set_ooblayout(mtd, &marvell_nand_ooblayout_ops);
+ ecc->steps = l->nchunks;
+ ecc->size = l->data_bytes;
+
+ if (ecc->strength == 1) {
+ chip->ecc.algo = NAND_ECC_HAMMING;
+ ecc->read_page_raw = marvell_nfc_hw_ecc_hmg_read_page_raw;
+ ecc->read_page = marvell_nfc_hw_ecc_hmg_read_page;
+ ecc->read_oob_raw = marvell_nfc_hw_ecc_hmg_read_oob_raw;
+ ecc->read_oob = ecc->read_oob_raw;
+ ecc->write_page_raw = marvell_nfc_hw_ecc_hmg_write_page_raw;
+ ecc->write_page = marvell_nfc_hw_ecc_hmg_write_page;
+ ecc->write_oob_raw = marvell_nfc_hw_ecc_hmg_write_oob_raw;
+ ecc->write_oob = ecc->write_oob_raw;
+ } else {
+ chip->ecc.algo = NAND_ECC_BCH;
+ ecc->strength = 16;
+ ecc->read_page_raw = marvell_nfc_hw_ecc_bch_read_page_raw;
+ ecc->read_page = marvell_nfc_hw_ecc_bch_read_page;
+ ecc->read_oob_raw = marvell_nfc_hw_ecc_bch_read_oob_raw;
+ ecc->read_oob = marvell_nfc_hw_ecc_bch_read_oob;
+ ecc->write_page_raw = marvell_nfc_hw_ecc_bch_write_page_raw;
+ ecc->write_page = marvell_nfc_hw_ecc_bch_write_page;
+ ecc->write_oob_raw = marvell_nfc_hw_ecc_bch_write_oob_raw;
+ ecc->write_oob = marvell_nfc_hw_ecc_bch_write_oob;
+ }
+
+ return 0;
+}
+
+static int marvell_nand_ecc_init(struct mtd_info *mtd,
+ struct nand_ecc_ctrl *ecc)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+ int ret;
+
+ if (ecc->mode != NAND_ECC_NONE && (!ecc->size || !ecc->strength)) {
+ if (chip->ecc_step_ds && chip->ecc_strength_ds) {
+ ecc->size = chip->ecc_step_ds;
+ ecc->strength = chip->ecc_strength_ds;
+ } else {
+ dev_info(nfc->dev,
+ "No minimum ECC strength, using 1b/512B\n");
+ ecc->size = 512;
+ ecc->strength = 1;
+ }
+ }
+
+ switch (ecc->mode) {
+ case NAND_ECC_HW:
+ ret = marvell_nand_hw_ecc_ctrl_init(mtd, ecc);
+ if (ret)
+ return ret;
+ break;
+ case NAND_ECC_NONE:
+ case NAND_ECC_SOFT:
+ if (!nfc->caps->is_nfcv2 && mtd->writesize != SZ_512 &&
+ mtd->writesize != SZ_2K) {
+ dev_err(nfc->dev, "NFCv1 cannot write %d bytes pages\n",
+ mtd->writesize);
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static u8 bbt_pattern[] = {'M', 'V', 'B', 'b', 't', '0' };
+static u8 bbt_mirror_pattern[] = {'1', 't', 'b', 'B', 'V', 'M' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
+ NAND_BBT_2BIT | NAND_BBT_VERSION,
+ .offs = 8,
+ .len = 6,
+ .veroffs = 14,
+ .maxblocks = 8, /* Last 8 blocks in each chip */
+ .pattern = bbt_pattern
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
+ NAND_BBT_2BIT | NAND_BBT_VERSION,
+ .offs = 8,
+ .len = 6,
+ .veroffs = 14,
+ .maxblocks = 8, /* Last 8 blocks in each chip */
+ .pattern = bbt_mirror_pattern
+};
+
+static int marvell_nfc_setup_data_interface(struct mtd_info *mtd, int chipnr,
+ const struct nand_data_interface
+ *conf)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
+ struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+ unsigned int period_ns = 1000000000 / clk_get_rate(nfc->core_clk) * 2;
+ const struct nand_sdr_timings *sdr;
+ struct marvell_nfc_timings nfc_tmg;
+ int read_delay;
+
+ sdr = nand_get_sdr_timings(conf);
+ if (IS_ERR(sdr))
+ return PTR_ERR(sdr);
+
+ /*
+ * SDR timings are given in pico-seconds while NFC timings must be
+ * expressed in NAND controller clock cycles, which is half of the
+ * frequency of the accessible ECC clock retrieved by clk_get_rate().
+ * This is not written anywhere in the datasheet but was observed
+ * with an oscilloscope.
+ *
+ * NFC datasheet gives equations from which thoses calculations
+ * are derived, they tend to be slightly more restrictives than the
+ * given core timings and may improve the overall speed.
+ */
+ nfc_tmg.tRP = TO_CYCLES(DIV_ROUND_UP(sdr->tRC_min, 2), period_ns) - 1;
+ nfc_tmg.tRH = nfc_tmg.tRP;
+ nfc_tmg.tWP = TO_CYCLES(DIV_ROUND_UP(sdr->tWC_min, 2), period_ns) - 1;
+ nfc_tmg.tWH = nfc_tmg.tWP;
+ nfc_tmg.tCS = TO_CYCLES(sdr->tCS_min, period_ns);
+ nfc_tmg.tCH = TO_CYCLES(sdr->tCH_min, period_ns) - 1;
+ nfc_tmg.tADL = TO_CYCLES(sdr->tADL_min, period_ns);
+ /*
+ * Read delay is the time of propagation from SoC pins to NFC internal
+ * logic. With non-EDO timings, this is MIN_RD_DEL_CNT clock cycles. In
+ * EDO mode, an additional delay of tRH must be taken into account so
+ * the data is sampled on the falling edge instead of the rising edge.
+ */
+ read_delay = sdr->tRC_min >= 30000 ?
+ MIN_RD_DEL_CNT : MIN_RD_DEL_CNT + nfc_tmg.tRH;
+
+ nfc_tmg.tAR = TO_CYCLES(sdr->tAR_min, period_ns);
+ /*
+ * tWHR and tRHW are supposed to be read to write delays (and vice
+ * versa) but in some cases, ie. when doing a change column, they must
+ * be greater than that to be sure tCCS delay is respected.
+ */
+ nfc_tmg.tWHR = TO_CYCLES(max_t(int, sdr->tWHR_min, sdr->tCCS_min),
+ period_ns) - 2,
+ nfc_tmg.tRHW = TO_CYCLES(max_t(int, sdr->tRHW_min, sdr->tCCS_min),
+ period_ns);
+
+ /*
+ * NFCv2: Use WAIT_MODE (wait for RB line), do not rely only on delays.
+ * NFCv1: No WAIT_MODE, tR must be maximal.
+ */
+ if (nfc->caps->is_nfcv2) {
+ nfc_tmg.tR = TO_CYCLES(sdr->tWB_max, period_ns);
+ } else {
+ nfc_tmg.tR = TO_CYCLES64(sdr->tWB_max + sdr->tR_max,
+ period_ns);
+ if (nfc_tmg.tR + 3 > nfc_tmg.tCH)
+ nfc_tmg.tR = nfc_tmg.tCH - 3;
+ else
+ nfc_tmg.tR = 0;
+ }
+
+ if (chipnr < 0)
+ return 0;
+
+ marvell_nand->ndtr0 =
+ NDTR0_TRP(nfc_tmg.tRP) |
+ NDTR0_TRH(nfc_tmg.tRH) |
+ NDTR0_ETRP(nfc_tmg.tRP) |
+ NDTR0_TWP(nfc_tmg.tWP) |
+ NDTR0_TWH(nfc_tmg.tWH) |
+ NDTR0_TCS(nfc_tmg.tCS) |
+ NDTR0_TCH(nfc_tmg.tCH);
+
+ marvell_nand->ndtr1 =
+ NDTR1_TAR(nfc_tmg.tAR) |
+ NDTR1_TWHR(nfc_tmg.tWHR) |
+ NDTR1_TR(nfc_tmg.tR);
+
+ if (nfc->caps->is_nfcv2) {
+ marvell_nand->ndtr0 |=
+ NDTR0_RD_CNT_DEL(read_delay) |
+ NDTR0_SELCNTR |
+ NDTR0_TADL(nfc_tmg.tADL);
+
+ marvell_nand->ndtr1 |=
+ NDTR1_TRHW(nfc_tmg.tRHW) |
+ NDTR1_WAIT_MODE;
+ }
+
+ return 0;
+}
+
+static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc,
+ struct device_node *np)
+{
+ struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(dev);
+ struct marvell_nand_chip *marvell_nand;
+ struct mtd_info *mtd;
+ struct nand_chip *chip;
+ int nsels, ret, i;
+ u32 cs, rb;
+
+ /*
+ * The legacy "num-cs" property indicates the number of CS on the only
+ * chip connected to the controller (legacy bindings does not support
+ * more than one chip). The CS and RB pins are always the #0.
+ *
+ * When not using legacy bindings, a couple of "reg" and "nand-rb"
+ * properties must be filled. For each chip, expressed as a subnode,
+ * "reg" points to the CS lines and "nand-rb" to the RB line.
+ */
+ if (pdata || nfc->caps->legacy_of_bindings) {
+ nsels = 1;
+ } else {
+ nsels = of_property_count_elems_of_size(np, "reg", sizeof(u32));
+ if (nsels <= 0) {
+ dev_err(dev, "missing/invalid reg property\n");
+ return -EINVAL;
+ }
+ }
+
+ /* Alloc the nand chip structure */
+ marvell_nand = devm_kzalloc(dev, sizeof(*marvell_nand) +
+ (nsels *
+ sizeof(struct marvell_nand_chip_sel)),
+ GFP_KERNEL);
+ if (!marvell_nand) {
+ dev_err(dev, "could not allocate chip structure\n");
+ return -ENOMEM;
+ }
+
+ marvell_nand->nsels = nsels;
+ marvell_nand->selected_die = -1;
+
+ for (i = 0; i < nsels; i++) {
+ if (pdata || nfc->caps->legacy_of_bindings) {
+ /*
+ * Legacy bindings use the CS lines in natural
+ * order (0, 1, ...)
+ */
+ cs = i;
+ } else {
+ /* Retrieve CS id */
+ ret = of_property_read_u32_index(np, "reg", i, &cs);
+ if (ret) {
+ dev_err(dev, "could not retrieve reg property: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ if (cs >= nfc->caps->max_cs_nb) {
+ dev_err(dev, "invalid reg value: %u (max CS = %d)\n",
+ cs, nfc->caps->max_cs_nb);
+ return -EINVAL;
+ }
+
+ if (test_and_set_bit(cs, &nfc->assigned_cs)) {
+ dev_err(dev, "CS %d already assigned\n", cs);
+ return -EINVAL;
+ }
+
+ /*
+ * The cs variable represents the chip select id, which must be
+ * converted in bit fields for NDCB0 and NDCB2 to select the
+ * right chip. Unfortunately, due to a lack of information on
+ * the subject and incoherent documentation, the user should not
+ * use CS1 and CS3 at all as asserting them is not supported in
+ * a reliable way (due to multiplexing inside ADDR5 field).
+ */
+ marvell_nand->sels[i].cs = cs;
+ switch (cs) {
+ case 0:
+ case 2:
+ marvell_nand->sels[i].ndcb0_csel = 0;
+ break;
+ case 1:
+ case 3:
+ marvell_nand->sels[i].ndcb0_csel = NDCB0_CSEL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Retrieve RB id */
+ if (pdata || nfc->caps->legacy_of_bindings) {
+ /* Legacy bindings always use RB #0 */
+ rb = 0;
+ } else {
+ ret = of_property_read_u32_index(np, "nand-rb", i,
+ &rb);
+ if (ret) {
+ dev_err(dev,
+ "could not retrieve RB property: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ if (rb >= nfc->caps->max_rb_nb) {
+ dev_err(dev, "invalid reg value: %u (max RB = %d)\n",
+ rb, nfc->caps->max_rb_nb);
+ return -EINVAL;
+ }
+
+ marvell_nand->sels[i].rb = rb;
+ }
+
+ chip = &marvell_nand->chip;
+ chip->controller = &nfc->controller;
+ nand_set_flash_node(chip, np);
+
+ chip->exec_op = marvell_nfc_exec_op;
+ chip->select_chip = marvell_nfc_select_chip;
+ if (!of_property_read_bool(np, "marvell,nand-keep-config"))
+ chip->setup_data_interface = marvell_nfc_setup_data_interface;
+
+ mtd = nand_to_mtd(chip);
+ mtd->dev.parent = dev;
+
+ /*
+ * Default to HW ECC engine mode. If the nand-ecc-mode property is given
+ * in the DT node, this entry will be overwritten in nand_scan_ident().
+ */
+ chip->ecc.mode = NAND_ECC_HW;
+
+ /*
+ * Save a reference value for timing registers before
+ * ->setup_data_interface() is called.
+ */
+ marvell_nand->ndtr0 = readl_relaxed(nfc->regs + NDTR0);
+ marvell_nand->ndtr1 = readl_relaxed(nfc->regs + NDTR1);
+
+ chip->options |= NAND_BUSWIDTH_AUTO;
+ ret = nand_scan_ident(mtd, marvell_nand->nsels, NULL);
+ if (ret) {
+ dev_err(dev, "could not identify the nand chip\n");
+ return ret;
+ }
+
+ if (pdata && pdata->flash_bbt)
+ chip->bbt_options |= NAND_BBT_USE_FLASH;
+
+ if (chip->bbt_options & NAND_BBT_USE_FLASH) {
+ /*
+ * We'll use a bad block table stored in-flash and don't
+ * allow writing the bad block marker to the flash.
+ */
+ chip->bbt_options |= NAND_BBT_NO_OOB_BBM;
+ chip->bbt_td = &bbt_main_descr;
+ chip->bbt_md = &bbt_mirror_descr;
+ }
+
+ /* Save the chip-specific fields of NDCR */
+ marvell_nand->ndcr = NDCR_PAGE_SZ(mtd->writesize);
+ if (chip->options & NAND_BUSWIDTH_16)
+ marvell_nand->ndcr |= NDCR_DWIDTH_M | NDCR_DWIDTH_C;
+
+ /*
+ * On small page NANDs, only one cycle is needed to pass the
+ * column address.
+ */
+ if (mtd->writesize <= 512) {
+ marvell_nand->addr_cyc = 1;
+ } else {
+ marvell_nand->addr_cyc = 2;
+ marvell_nand->ndcr |= NDCR_RA_START;
+ }
+
+ /*
+ * Now add the number of cycles needed to pass the row
+ * address.
+ *
+ * Addressing a chip using CS 2 or 3 should also need the third row
+ * cycle but due to inconsistance in the documentation and lack of
+ * hardware to test this situation, this case is not supported.
+ */
+ if (chip->options & NAND_ROW_ADDR_3)
+ marvell_nand->addr_cyc += 3;
+ else
+ marvell_nand->addr_cyc += 2;
+
+ if (pdata) {
+ chip->ecc.size = pdata->ecc_step_size;
+ chip->ecc.strength = pdata->ecc_strength;
+ }
+
+ ret = marvell_nand_ecc_init(mtd, &chip->ecc);
+ if (ret) {
+ dev_err(dev, "ECC init failed: %d\n", ret);
+ return ret;
+ }
+
+ if (chip->ecc.mode == NAND_ECC_HW) {
+ /*
+ * Subpage write not available with hardware ECC, prohibit also
+ * subpage read as in userspace subpage access would still be
+ * allowed and subpage write, if used, would lead to numerous
+ * uncorrectable ECC errors.
+ */
+ chip->options |= NAND_NO_SUBPAGE_WRITE;
+ }
+
+ if (pdata || nfc->caps->legacy_of_bindings) {
+ /*
+ * We keep the MTD name unchanged to avoid breaking platforms
+ * where the MTD cmdline parser is used and the bootloader
+ * has not been updated to use the new naming scheme.
+ */
+ mtd->name = "pxa3xx_nand-0";
+ } else if (!mtd->name) {
+ /*
+ * If the new bindings are used and the bootloader has not been
+ * updated to pass a new mtdparts parameter on the cmdline, you
+ * should define the following property in your NAND node, ie:
+ *
+ * label = "main-storage";
+ *
+ * This way, mtd->name will be set by the core when
+ * nand_set_flash_node() is called.
+ */
+ mtd->name = devm_kasprintf(nfc->dev, GFP_KERNEL,
+ "%s:nand.%d", dev_name(nfc->dev),
+ marvell_nand->sels[0].cs);
+ if (!mtd->name) {
+ dev_err(nfc->dev, "Failed to allocate mtd->name\n");
+ return -ENOMEM;
+ }
+ }
+
+ ret = nand_scan_tail(mtd);
+ if (ret) {
+ dev_err(dev, "nand_scan_tail failed: %d\n", ret);
+ return ret;
+ }
+
+ if (pdata)
+ /* Legacy bindings support only one chip */
+ ret = mtd_device_register(mtd, pdata->parts, pdata->nr_parts);
+ else
+ ret = mtd_device_register(mtd, NULL, 0);
+ if (ret) {
+ dev_err(dev, "failed to register mtd device: %d\n", ret);
+ nand_release(mtd);
+ return ret;
+ }
+
+ list_add_tail(&marvell_nand->node, &nfc->chips);
+
+ return 0;
+}
+
+static int marvell_nand_chips_init(struct device *dev, struct marvell_nfc *nfc)
+{
+ struct device_node *np = dev->of_node;
+ struct device_node *nand_np;
+ int max_cs = nfc->caps->max_cs_nb;
+ int nchips;
+ int ret;
+
+ if (!np)
+ nchips = 1;
+ else
+ nchips = of_get_child_count(np);
+
+ if (nchips > max_cs) {
+ dev_err(dev, "too many NAND chips: %d (max = %d CS)\n", nchips,
+ max_cs);
+ return -EINVAL;
+ }
+
+ /*
+ * Legacy bindings do not use child nodes to exhibit NAND chip
+ * properties and layout. Instead, NAND properties are mixed with the
+ * controller ones, and partitions are defined as direct subnodes of the
+ * NAND controller node.
+ */
+ if (nfc->caps->legacy_of_bindings) {
+ ret = marvell_nand_chip_init(dev, nfc, np);
+ return ret;
+ }
+
+ for_each_child_of_node(np, nand_np) {
+ ret = marvell_nand_chip_init(dev, nfc, nand_np);
+ if (ret) {
+ of_node_put(nand_np);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void marvell_nand_chips_cleanup(struct marvell_nfc *nfc)
+{
+ struct marvell_nand_chip *entry, *temp;
+
+ list_for_each_entry_safe(entry, temp, &nfc->chips, node) {
+ nand_release(nand_to_mtd(&entry->chip));
+ list_del(&entry->node);
+ }
+}
+
+static int marvell_nfc_init_dma(struct marvell_nfc *nfc)
+{
+ struct platform_device *pdev = container_of(nfc->dev,
+ struct platform_device,
+ dev);
+ struct dma_slave_config config = {};
+ struct resource *r;
+ dma_cap_mask_t mask;
+ struct pxad_param param;
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_PXA_DMA)) {
+ dev_warn(nfc->dev,
+ "DMA not enabled in configuration\n");
+ return -ENOTSUPP;
+ }
+
+ ret = dma_set_mask_and_coherent(nfc->dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!r) {
+ dev_err(nfc->dev, "No resource defined for data DMA\n");
+ return -ENXIO;
+ }
+
+ param.drcmr = r->start;
+ param.prio = PXAD_PRIO_LOWEST;
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ nfc->dma_chan =
+ dma_request_slave_channel_compat(mask, pxad_filter_fn,
+ &param, nfc->dev,
+ "data");
+ if (!nfc->dma_chan) {
+ dev_err(nfc->dev,
+ "Unable to request data DMA channel\n");
+ return -ENODEV;
+ }
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r)
+ return -ENXIO;
+
+ config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ config.src_addr = r->start + NDDB;
+ config.dst_addr = r->start + NDDB;
+ config.src_maxburst = 32;
+ config.dst_maxburst = 32;
+ ret = dmaengine_slave_config(nfc->dma_chan, &config);
+ if (ret < 0) {
+ dev_err(nfc->dev, "Failed to configure DMA channel\n");
+ return ret;
+ }
+
+ /*
+ * DMA must act on length multiple of 32 and this length may be
+ * bigger than the destination buffer. Use this buffer instead
+ * for DMA transfers and then copy the desired amount of data to
+ * the provided buffer.
+ */
+ nfc->dma_buf = kmalloc(MAX_CHUNK_SIZE, GFP_KERNEL | GFP_DMA);
+ if (!nfc->dma_buf)
+ return -ENOMEM;
+
+ nfc->use_dma = true;
+
+ return 0;
+}
+
+static int marvell_nfc_init(struct marvell_nfc *nfc)
+{
+ struct device_node *np = nfc->dev->of_node;
+
+ /*
+ * Some SoCs like A7k/A8k need to enable manually the NAND
+ * controller, gated clocks and reset bits to avoid being bootloader
+ * dependent. This is done through the use of the System Functions
+ * registers.
+ */
+ if (nfc->caps->need_system_controller) {
+ struct regmap *sysctrl_base =
+ syscon_regmap_lookup_by_phandle(np,
+ "marvell,system-controller");
+ u32 reg;
+
+ if (IS_ERR(sysctrl_base))
+ return PTR_ERR(sysctrl_base);
+
+ reg = GENCONF_SOC_DEVICE_MUX_NFC_EN |
+ GENCONF_SOC_DEVICE_MUX_ECC_CLK_RST |
+ GENCONF_SOC_DEVICE_MUX_ECC_CORE_RST |
+ GENCONF_SOC_DEVICE_MUX_NFC_INT_EN;
+ regmap_write(sysctrl_base, GENCONF_SOC_DEVICE_MUX, reg);
+
+ regmap_read(sysctrl_base, GENCONF_CLK_GATING_CTRL, &reg);
+ reg |= GENCONF_CLK_GATING_CTRL_ND_GATE;
+ regmap_write(sysctrl_base, GENCONF_CLK_GATING_CTRL, reg);
+
+ regmap_read(sysctrl_base, GENCONF_ND_CLK_CTRL, &reg);
+ reg |= GENCONF_ND_CLK_CTRL_EN;
+ regmap_write(sysctrl_base, GENCONF_ND_CLK_CTRL, reg);
+ }
+
+ /* Configure the DMA if appropriate */
+ if (!nfc->caps->is_nfcv2)
+ marvell_nfc_init_dma(nfc);
+
+ /*
+ * ECC operations and interruptions are only enabled when specifically
+ * needed. ECC shall not be activated in the early stages (fails probe).
+ * Arbiter flag, even if marked as "reserved", must be set (empirical).
+ * SPARE_EN bit must always be set or ECC bytes will not be at the same
+ * offset in the read page and this will fail the protection.
+ */
+ writel_relaxed(NDCR_ALL_INT | NDCR_ND_ARB_EN | NDCR_SPARE_EN |
+ NDCR_RD_ID_CNT(NFCV1_READID_LEN), nfc->regs + NDCR);
+ writel_relaxed(0xFFFFFFFF, nfc->regs + NDSR);
+ writel_relaxed(0, nfc->regs + NDECCCTRL);
+
+ return 0;
+}
+
+static int marvell_nfc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *r;
+ struct marvell_nfc *nfc;
+ int ret;
+ int irq;
+
+ nfc = devm_kzalloc(&pdev->dev, sizeof(struct marvell_nfc),
+ GFP_KERNEL);
+ if (!nfc)
+ return -ENOMEM;
+
+ nfc->dev = dev;
+ nand_hw_control_init(&nfc->controller);
+ INIT_LIST_HEAD(&nfc->chips);
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ nfc->regs = devm_ioremap_resource(dev, r);
+ if (IS_ERR(nfc->regs))
+ return PTR_ERR(nfc->regs);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(dev, "failed to retrieve irq\n");
+ return irq;
+ }
+
+ nfc->core_clk = devm_clk_get(&pdev->dev, "core");
+
+ /* Managed the legacy case (when the first clock was not named) */
+ if (nfc->core_clk == ERR_PTR(-ENOENT))
+ nfc->core_clk = devm_clk_get(&pdev->dev, NULL);
+
+ if (IS_ERR(nfc->core_clk))
+ return PTR_ERR(nfc->core_clk);
+
+ ret = clk_prepare_enable(nfc->core_clk);
+ if (ret)
+ return ret;
+
+ nfc->reg_clk = devm_clk_get(&pdev->dev, "reg");
+ if (PTR_ERR(nfc->reg_clk) != -ENOENT) {
+ if (!IS_ERR(nfc->reg_clk)) {
+ ret = clk_prepare_enable(nfc->reg_clk);
+ if (ret)
+ goto unprepare_core_clk;
+ } else {
+ ret = PTR_ERR(nfc->reg_clk);
+ goto unprepare_core_clk;
+ }
+ }
+
+ marvell_nfc_disable_int(nfc, NDCR_ALL_INT);
+ marvell_nfc_clear_int(nfc, NDCR_ALL_INT);
+ ret = devm_request_irq(dev, irq, marvell_nfc_isr,
+ 0, "marvell-nfc", nfc);
+ if (ret)
+ goto unprepare_reg_clk;
+
+ /* Get NAND controller capabilities */
+ if (pdev->id_entry)
+ nfc->caps = (void *)pdev->id_entry->driver_data;
+ else
+ nfc->caps = of_device_get_match_data(&pdev->dev);
+
+ if (!nfc->caps) {
+ dev_err(dev, "Could not retrieve NFC caps\n");
+ ret = -EINVAL;
+ goto unprepare_reg_clk;
+ }
+
+ /* Init the controller and then probe the chips */
+ ret = marvell_nfc_init(nfc);
+ if (ret)
+ goto unprepare_reg_clk;
+
+ platform_set_drvdata(pdev, nfc);
+
+ ret = marvell_nand_chips_init(dev, nfc);
+ if (ret)
+ goto unprepare_reg_clk;
+
+ return 0;
+
+unprepare_reg_clk:
+ clk_disable_unprepare(nfc->reg_clk);
+unprepare_core_clk:
+ clk_disable_unprepare(nfc->core_clk);
+
+ return ret;
+}
+
+static int marvell_nfc_remove(struct platform_device *pdev)
+{
+ struct marvell_nfc *nfc = platform_get_drvdata(pdev);
+
+ marvell_nand_chips_cleanup(nfc);
+
+ if (nfc->use_dma) {
+ dmaengine_terminate_all(nfc->dma_chan);
+ dma_release_channel(nfc->dma_chan);
+ }
+
+ clk_disable_unprepare(nfc->reg_clk);
+ clk_disable_unprepare(nfc->core_clk);
+
+ return 0;
+}
+
+static const struct marvell_nfc_caps marvell_armada_8k_nfc_caps = {
+ .max_cs_nb = 4,
+ .max_rb_nb = 2,
+ .need_system_controller = true,
+ .is_nfcv2 = true,
+};
+
+static const struct marvell_nfc_caps marvell_armada370_nfc_caps = {
+ .max_cs_nb = 4,
+ .max_rb_nb = 2,
+ .is_nfcv2 = true,
+};
+
+static const struct marvell_nfc_caps marvell_pxa3xx_nfc_caps = {
+ .max_cs_nb = 2,
+ .max_rb_nb = 1,
+ .use_dma = true,
+};
+
+static const struct marvell_nfc_caps marvell_armada_8k_nfc_legacy_caps = {
+ .max_cs_nb = 4,
+ .max_rb_nb = 2,
+ .need_system_controller = true,
+ .legacy_of_bindings = true,
+ .is_nfcv2 = true,
+};
+
+static const struct marvell_nfc_caps marvell_armada370_nfc_legacy_caps = {
+ .max_cs_nb = 4,
+ .max_rb_nb = 2,
+ .legacy_of_bindings = true,
+ .is_nfcv2 = true,
+};
+
+static const struct marvell_nfc_caps marvell_pxa3xx_nfc_legacy_caps = {
+ .max_cs_nb = 2,
+ .max_rb_nb = 1,
+ .legacy_of_bindings = true,
+ .use_dma = true,
+};
+
+static const struct platform_device_id marvell_nfc_platform_ids[] = {
+ {
+ .name = "pxa3xx-nand",
+ .driver_data = (kernel_ulong_t)&marvell_pxa3xx_nfc_legacy_caps,
+ },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(platform, marvell_nfc_platform_ids);
+
+static const struct of_device_id marvell_nfc_of_ids[] = {
+ {
+ .compatible = "marvell,armada-8k-nand-controller",
+ .data = &marvell_armada_8k_nfc_caps,
+ },
+ {
+ .compatible = "marvell,armada370-nand-controller",
+ .data = &marvell_armada370_nfc_caps,
+ },
+ {
+ .compatible = "marvell,pxa3xx-nand-controller",
+ .data = &marvell_pxa3xx_nfc_caps,
+ },
+ /* Support for old/deprecated bindings: */
+ {
+ .compatible = "marvell,armada-8k-nand",
+ .data = &marvell_armada_8k_nfc_legacy_caps,
+ },
+ {
+ .compatible = "marvell,armada370-nand",
+ .data = &marvell_armada370_nfc_legacy_caps,
+ },
+ {
+ .compatible = "marvell,pxa3xx-nand",
+ .data = &marvell_pxa3xx_nfc_legacy_caps,
+ },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, marvell_nfc_of_ids);
+
+static struct platform_driver marvell_nfc_driver = {
+ .driver = {
+ .name = "marvell-nfc",
+ .of_match_table = marvell_nfc_of_ids,
+ },
+ .id_table = marvell_nfc_platform_ids,
+ .probe = marvell_nfc_probe,
+ .remove = marvell_nfc_remove,
+};
+module_platform_driver(marvell_nfc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Marvell NAND controller driver");
diff --git a/drivers/mtd/nand/raw/mpc5121_nfc.c b/drivers/mtd/nand/raw/mpc5121_nfc.c
new file mode 100644
index 000000000000..6d1740d54e0d
--- /dev/null
+++ b/drivers/mtd/nand/raw/mpc5121_nfc.c
@@ -0,0 +1,856 @@
+/*
+ * Copyright 2004-2008 Freescale Semiconductor, Inc.
+ * Copyright 2009 Semihalf.
+ *
+ * Approved as OSADL project by a majority of OSADL members and funded
+ * by OSADL membership fees in 2009; for details see www.osadl.org.
+ *
+ * Based on original driver from Freescale Semiconductor
+ * written by John Rigby <jrigby@freescale.com> on basis of mxc_nand.c.
+ * Reworked and extended by Piotr Ziecik <kosmo@semihalf.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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/gfp.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+
+#include <asm/mpc5121.h>
+
+/* Addresses for NFC MAIN RAM BUFFER areas */
+#define NFC_MAIN_AREA(n) ((n) * 0x200)
+
+/* Addresses for NFC SPARE BUFFER areas */
+#define NFC_SPARE_BUFFERS 8
+#define NFC_SPARE_LEN 0x40
+#define NFC_SPARE_AREA(n) (0x1000 + ((n) * NFC_SPARE_LEN))
+
+/* MPC5121 NFC registers */
+#define NFC_BUF_ADDR 0x1E04
+#define NFC_FLASH_ADDR 0x1E06
+#define NFC_FLASH_CMD 0x1E08
+#define NFC_CONFIG 0x1E0A
+#define NFC_ECC_STATUS1 0x1E0C
+#define NFC_ECC_STATUS2 0x1E0E
+#define NFC_SPAS 0x1E10
+#define NFC_WRPROT 0x1E12
+#define NFC_NF_WRPRST 0x1E18
+#define NFC_CONFIG1 0x1E1A
+#define NFC_CONFIG2 0x1E1C
+#define NFC_UNLOCKSTART_BLK0 0x1E20
+#define NFC_UNLOCKEND_BLK0 0x1E22
+#define NFC_UNLOCKSTART_BLK1 0x1E24
+#define NFC_UNLOCKEND_BLK1 0x1E26
+#define NFC_UNLOCKSTART_BLK2 0x1E28
+#define NFC_UNLOCKEND_BLK2 0x1E2A
+#define NFC_UNLOCKSTART_BLK3 0x1E2C
+#define NFC_UNLOCKEND_BLK3 0x1E2E
+
+/* Bit Definitions: NFC_BUF_ADDR */
+#define NFC_RBA_MASK (7 << 0)
+#define NFC_ACTIVE_CS_SHIFT 5
+#define NFC_ACTIVE_CS_MASK (3 << NFC_ACTIVE_CS_SHIFT)
+
+/* Bit Definitions: NFC_CONFIG */
+#define NFC_BLS_UNLOCKED (1 << 1)
+
+/* Bit Definitions: NFC_CONFIG1 */
+#define NFC_ECC_4BIT (1 << 0)
+#define NFC_FULL_PAGE_DMA (1 << 1)
+#define NFC_SPARE_ONLY (1 << 2)
+#define NFC_ECC_ENABLE (1 << 3)
+#define NFC_INT_MASK (1 << 4)
+#define NFC_BIG_ENDIAN (1 << 5)
+#define NFC_RESET (1 << 6)
+#define NFC_CE (1 << 7)
+#define NFC_ONE_CYCLE (1 << 8)
+#define NFC_PPB_32 (0 << 9)
+#define NFC_PPB_64 (1 << 9)
+#define NFC_PPB_128 (2 << 9)
+#define NFC_PPB_256 (3 << 9)
+#define NFC_PPB_MASK (3 << 9)
+#define NFC_FULL_PAGE_INT (1 << 11)
+
+/* Bit Definitions: NFC_CONFIG2 */
+#define NFC_COMMAND (1 << 0)
+#define NFC_ADDRESS (1 << 1)
+#define NFC_INPUT (1 << 2)
+#define NFC_OUTPUT (1 << 3)
+#define NFC_ID (1 << 4)
+#define NFC_STATUS (1 << 5)
+#define NFC_CMD_FAIL (1 << 15)
+#define NFC_INT (1 << 15)
+
+/* Bit Definitions: NFC_WRPROT */
+#define NFC_WPC_LOCK_TIGHT (1 << 0)
+#define NFC_WPC_LOCK (1 << 1)
+#define NFC_WPC_UNLOCK (1 << 2)
+
+#define DRV_NAME "mpc5121_nfc"
+
+/* Timeouts */
+#define NFC_RESET_TIMEOUT 1000 /* 1 ms */
+#define NFC_TIMEOUT (HZ / 10) /* 1/10 s */
+
+struct mpc5121_nfc_prv {
+ struct nand_chip chip;
+ int irq;
+ void __iomem *regs;
+ struct clk *clk;
+ wait_queue_head_t irq_waitq;
+ uint column;
+ int spareonly;
+ void __iomem *csreg;
+ struct device *dev;
+};
+
+static void mpc5121_nfc_done(struct mtd_info *mtd);
+
+/* Read NFC register */
+static inline u16 nfc_read(struct mtd_info *mtd, uint reg)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
+
+ return in_be16(prv->regs + reg);
+}
+
+/* Write NFC register */
+static inline void nfc_write(struct mtd_info *mtd, uint reg, u16 val)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
+
+ out_be16(prv->regs + reg, val);
+}
+
+/* Set bits in NFC register */
+static inline void nfc_set(struct mtd_info *mtd, uint reg, u16 bits)
+{
+ nfc_write(mtd, reg, nfc_read(mtd, reg) | bits);
+}
+
+/* Clear bits in NFC register */
+static inline void nfc_clear(struct mtd_info *mtd, uint reg, u16 bits)
+{
+ nfc_write(mtd, reg, nfc_read(mtd, reg) & ~bits);
+}
+
+/* Invoke address cycle */
+static inline void mpc5121_nfc_send_addr(struct mtd_info *mtd, u16 addr)
+{
+ nfc_write(mtd, NFC_FLASH_ADDR, addr);
+ nfc_write(mtd, NFC_CONFIG2, NFC_ADDRESS);
+ mpc5121_nfc_done(mtd);
+}
+
+/* Invoke command cycle */
+static inline void mpc5121_nfc_send_cmd(struct mtd_info *mtd, u16 cmd)
+{
+ nfc_write(mtd, NFC_FLASH_CMD, cmd);
+ nfc_write(mtd, NFC_CONFIG2, NFC_COMMAND);
+ mpc5121_nfc_done(mtd);
+}
+
+/* Send data from NFC buffers to NAND flash */
+static inline void mpc5121_nfc_send_prog_page(struct mtd_info *mtd)
+{
+ nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK);
+ nfc_write(mtd, NFC_CONFIG2, NFC_INPUT);
+ mpc5121_nfc_done(mtd);
+}
+
+/* Receive data from NAND flash */
+static inline void mpc5121_nfc_send_read_page(struct mtd_info *mtd)
+{
+ nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK);
+ nfc_write(mtd, NFC_CONFIG2, NFC_OUTPUT);
+ mpc5121_nfc_done(mtd);
+}
+
+/* Receive ID from NAND flash */
+static inline void mpc5121_nfc_send_read_id(struct mtd_info *mtd)
+{
+ nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK);
+ nfc_write(mtd, NFC_CONFIG2, NFC_ID);
+ mpc5121_nfc_done(mtd);
+}
+
+/* Receive status from NAND flash */
+static inline void mpc5121_nfc_send_read_status(struct mtd_info *mtd)
+{
+ nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK);
+ nfc_write(mtd, NFC_CONFIG2, NFC_STATUS);
+ mpc5121_nfc_done(mtd);
+}
+
+/* NFC interrupt handler */
+static irqreturn_t mpc5121_nfc_irq(int irq, void *data)
+{
+ struct mtd_info *mtd = data;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
+
+ nfc_set(mtd, NFC_CONFIG1, NFC_INT_MASK);
+ wake_up(&prv->irq_waitq);
+
+ return IRQ_HANDLED;
+}
+
+/* Wait for operation complete */
+static void mpc5121_nfc_done(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
+ int rv;
+
+ if ((nfc_read(mtd, NFC_CONFIG2) & NFC_INT) == 0) {
+ nfc_clear(mtd, NFC_CONFIG1, NFC_INT_MASK);
+ rv = wait_event_timeout(prv->irq_waitq,
+ (nfc_read(mtd, NFC_CONFIG2) & NFC_INT), NFC_TIMEOUT);
+
+ if (!rv)
+ dev_warn(prv->dev,
+ "Timeout while waiting for interrupt.\n");
+ }
+
+ nfc_clear(mtd, NFC_CONFIG2, NFC_INT);
+}
+
+/* Do address cycle(s) */
+static void mpc5121_nfc_addr_cycle(struct mtd_info *mtd, int column, int page)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ u32 pagemask = chip->pagemask;
+
+ if (column != -1) {
+ mpc5121_nfc_send_addr(mtd, column);
+ if (mtd->writesize > 512)
+ mpc5121_nfc_send_addr(mtd, column >> 8);
+ }
+
+ if (page != -1) {
+ do {
+ mpc5121_nfc_send_addr(mtd, page & 0xFF);
+ page >>= 8;
+ pagemask >>= 8;
+ } while (pagemask);
+ }
+}
+
+/* Control chip select signals */
+static void mpc5121_nfc_select_chip(struct mtd_info *mtd, int chip)
+{
+ if (chip < 0) {
+ nfc_clear(mtd, NFC_CONFIG1, NFC_CE);
+ return;
+ }
+
+ nfc_clear(mtd, NFC_BUF_ADDR, NFC_ACTIVE_CS_MASK);
+ nfc_set(mtd, NFC_BUF_ADDR, (chip << NFC_ACTIVE_CS_SHIFT) &
+ NFC_ACTIVE_CS_MASK);
+ nfc_set(mtd, NFC_CONFIG1, NFC_CE);
+}
+
+/* Init external chip select logic on ADS5121 board */
+static int ads5121_chipselect_init(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
+ struct device_node *dn;
+
+ dn = of_find_compatible_node(NULL, NULL, "fsl,mpc5121ads-cpld");
+ if (dn) {
+ prv->csreg = of_iomap(dn, 0);
+ of_node_put(dn);
+ if (!prv->csreg)
+ return -ENOMEM;
+
+ /* CPLD Register 9 controls NAND /CE Lines */
+ prv->csreg += 9;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+/* Control chips select signal on ADS5121 board */
+static void ads5121_select_chip(struct mtd_info *mtd, int chip)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct mpc5121_nfc_prv *prv = nand_get_controller_data(nand);
+ u8 v;
+
+ v = in_8(prv->csreg);
+ v |= 0x0F;
+
+ if (chip >= 0) {
+ mpc5121_nfc_select_chip(mtd, 0);
+ v &= ~(1 << chip);
+ } else
+ mpc5121_nfc_select_chip(mtd, -1);
+
+ out_8(prv->csreg, v);
+}
+
+/* Read NAND Ready/Busy signal */
+static int mpc5121_nfc_dev_ready(struct mtd_info *mtd)
+{
+ /*
+ * NFC handles ready/busy signal internally. Therefore, this function
+ * always returns status as ready.
+ */
+ return 1;
+}
+
+/* Write command to NAND flash */
+static void mpc5121_nfc_command(struct mtd_info *mtd, unsigned command,
+ int column, int page)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
+
+ prv->column = (column >= 0) ? column : 0;
+ prv->spareonly = 0;
+
+ switch (command) {
+ case NAND_CMD_PAGEPROG:
+ mpc5121_nfc_send_prog_page(mtd);
+ break;
+ /*
+ * NFC does not support sub-page reads and writes,
+ * so emulate them using full page transfers.
+ */
+ case NAND_CMD_READ0:
+ column = 0;
+ break;
+
+ case NAND_CMD_READ1:
+ prv->column += 256;
+ command = NAND_CMD_READ0;
+ column = 0;
+ break;
+
+ case NAND_CMD_READOOB:
+ prv->spareonly = 1;
+ command = NAND_CMD_READ0;
+ column = 0;
+ break;
+
+ case NAND_CMD_SEQIN:
+ mpc5121_nfc_command(mtd, NAND_CMD_READ0, column, page);
+ column = 0;
+ break;
+
+ case NAND_CMD_ERASE1:
+ case NAND_CMD_ERASE2:
+ case NAND_CMD_READID:
+ case NAND_CMD_STATUS:
+ break;
+
+ default:
+ return;
+ }
+
+ mpc5121_nfc_send_cmd(mtd, command);
+ mpc5121_nfc_addr_cycle(mtd, column, page);
+
+ switch (command) {
+ case NAND_CMD_READ0:
+ if (mtd->writesize > 512)
+ mpc5121_nfc_send_cmd(mtd, NAND_CMD_READSTART);
+ mpc5121_nfc_send_read_page(mtd);
+ break;
+
+ case NAND_CMD_READID:
+ mpc5121_nfc_send_read_id(mtd);
+ break;
+
+ case NAND_CMD_STATUS:
+ mpc5121_nfc_send_read_status(mtd);
+ if (chip->options & NAND_BUSWIDTH_16)
+ prv->column = 1;
+ else
+ prv->column = 0;
+ break;
+ }
+}
+
+/* Copy data from/to NFC spare buffers. */
+static void mpc5121_nfc_copy_spare(struct mtd_info *mtd, uint offset,
+ u8 *buffer, uint size, int wr)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct mpc5121_nfc_prv *prv = nand_get_controller_data(nand);
+ uint o, s, sbsize, blksize;
+
+ /*
+ * NAND spare area is available through NFC spare buffers.
+ * The NFC divides spare area into (page_size / 512) chunks.
+ * Each chunk is placed into separate spare memory area, using
+ * first (spare_size / num_of_chunks) bytes of the buffer.
+ *
+ * For NAND device in which the spare area is not divided fully
+ * by the number of chunks, number of used bytes in each spare
+ * buffer is rounded down to the nearest even number of bytes,
+ * and all remaining bytes are added to the last used spare area.
+ *
+ * For more information read section 26.6.10 of MPC5121e
+ * Microcontroller Reference Manual, Rev. 3.
+ */
+
+ /* Calculate number of valid bytes in each spare buffer */
+ sbsize = (mtd->oobsize / (mtd->writesize / 512)) & ~1;
+
+ while (size) {
+ /* Calculate spare buffer number */
+ s = offset / sbsize;
+ if (s > NFC_SPARE_BUFFERS - 1)
+ s = NFC_SPARE_BUFFERS - 1;
+
+ /*
+ * Calculate offset to requested data block in selected spare
+ * buffer and its size.
+ */
+ o = offset - (s * sbsize);
+ blksize = min(sbsize - o, size);
+
+ if (wr)
+ memcpy_toio(prv->regs + NFC_SPARE_AREA(s) + o,
+ buffer, blksize);
+ else
+ memcpy_fromio(buffer,
+ prv->regs + NFC_SPARE_AREA(s) + o, blksize);
+
+ buffer += blksize;
+ offset += blksize;
+ size -= blksize;
+ };
+}
+
+/* Copy data from/to NFC main and spare buffers */
+static void mpc5121_nfc_buf_copy(struct mtd_info *mtd, u_char *buf, int len,
+ int wr)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
+ uint c = prv->column;
+ uint l;
+
+ /* Handle spare area access */
+ if (prv->spareonly || c >= mtd->writesize) {
+ /* Calculate offset from beginning of spare area */
+ if (c >= mtd->writesize)
+ c -= mtd->writesize;
+
+ prv->column += len;
+ mpc5121_nfc_copy_spare(mtd, c, buf, len, wr);
+ return;
+ }
+
+ /*
+ * Handle main area access - limit copy length to prevent
+ * crossing main/spare boundary.
+ */
+ l = min((uint)len, mtd->writesize - c);
+ prv->column += l;
+
+ if (wr)
+ memcpy_toio(prv->regs + NFC_MAIN_AREA(0) + c, buf, l);
+ else
+ memcpy_fromio(buf, prv->regs + NFC_MAIN_AREA(0) + c, l);
+
+ /* Handle crossing main/spare boundary */
+ if (l != len) {
+ buf += l;
+ len -= l;
+ mpc5121_nfc_buf_copy(mtd, buf, len, wr);
+ }
+}
+
+/* Read data from NFC buffers */
+static void mpc5121_nfc_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+ mpc5121_nfc_buf_copy(mtd, buf, len, 0);
+}
+
+/* Write data to NFC buffers */
+static void mpc5121_nfc_write_buf(struct mtd_info *mtd,
+ const u_char *buf, int len)
+{
+ mpc5121_nfc_buf_copy(mtd, (u_char *)buf, len, 1);
+}
+
+/* Read byte from NFC buffers */
+static u8 mpc5121_nfc_read_byte(struct mtd_info *mtd)
+{
+ u8 tmp;
+
+ mpc5121_nfc_read_buf(mtd, &tmp, sizeof(tmp));
+
+ return tmp;
+}
+
+/* Read word from NFC buffers */
+static u16 mpc5121_nfc_read_word(struct mtd_info *mtd)
+{
+ u16 tmp;
+
+ mpc5121_nfc_read_buf(mtd, (u_char *)&tmp, sizeof(tmp));
+
+ return tmp;
+}
+
+/*
+ * Read NFC configuration from Reset Config Word
+ *
+ * NFC is configured during reset in basis of information stored
+ * in Reset Config Word. There is no other way to set NAND block
+ * size, spare size and bus width.
+ */
+static int mpc5121_nfc_read_hw_config(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
+ struct mpc512x_reset_module *rm;
+ struct device_node *rmnode;
+ uint rcw_pagesize = 0;
+ uint rcw_sparesize = 0;
+ uint rcw_width;
+ uint rcwh;
+ uint romloc, ps;
+ int ret = 0;
+
+ rmnode = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-reset");
+ if (!rmnode) {
+ dev_err(prv->dev, "Missing 'fsl,mpc5121-reset' "
+ "node in device tree!\n");
+ return -ENODEV;
+ }
+
+ rm = of_iomap(rmnode, 0);
+ if (!rm) {
+ dev_err(prv->dev, "Error mapping reset module node!\n");
+ ret = -EBUSY;
+ goto out;
+ }
+
+ rcwh = in_be32(&rm->rcwhr);
+
+ /* Bit 6: NFC bus width */
+ rcw_width = ((rcwh >> 6) & 0x1) ? 2 : 1;
+
+ /* Bit 7: NFC Page/Spare size */
+ ps = (rcwh >> 7) & 0x1;
+
+ /* Bits [22:21]: ROM Location */
+ romloc = (rcwh >> 21) & 0x3;
+
+ /* Decode RCW bits */
+ switch ((ps << 2) | romloc) {
+ case 0x00:
+ case 0x01:
+ rcw_pagesize = 512;
+ rcw_sparesize = 16;
+ break;
+ case 0x02:
+ case 0x03:
+ rcw_pagesize = 4096;
+ rcw_sparesize = 128;
+ break;
+ case 0x04:
+ case 0x05:
+ rcw_pagesize = 2048;
+ rcw_sparesize = 64;
+ break;
+ case 0x06:
+ case 0x07:
+ rcw_pagesize = 4096;
+ rcw_sparesize = 218;
+ break;
+ }
+
+ mtd->writesize = rcw_pagesize;
+ mtd->oobsize = rcw_sparesize;
+ if (rcw_width == 2)
+ chip->options |= NAND_BUSWIDTH_16;
+
+ dev_notice(prv->dev, "Configured for "
+ "%u-bit NAND, page size %u "
+ "with %u spare.\n",
+ rcw_width * 8, rcw_pagesize,
+ rcw_sparesize);
+ iounmap(rm);
+out:
+ of_node_put(rmnode);
+ return ret;
+}
+
+/* Free driver resources */
+static void mpc5121_nfc_free(struct device *dev, struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
+
+ if (prv->clk)
+ clk_disable_unprepare(prv->clk);
+
+ if (prv->csreg)
+ iounmap(prv->csreg);
+}
+
+static int mpc5121_nfc_probe(struct platform_device *op)
+{
+ struct device_node *dn = op->dev.of_node;
+ struct clk *clk;
+ struct device *dev = &op->dev;
+ struct mpc5121_nfc_prv *prv;
+ struct resource res;
+ struct mtd_info *mtd;
+ struct nand_chip *chip;
+ unsigned long regs_paddr, regs_size;
+ const __be32 *chips_no;
+ int resettime = 0;
+ int retval = 0;
+ int rev, len;
+
+ /*
+ * Check SoC revision. This driver supports only NFC
+ * in MPC5121 revision 2 and MPC5123 revision 3.
+ */
+ rev = (mfspr(SPRN_SVR) >> 4) & 0xF;
+ if ((rev != 2) && (rev != 3)) {
+ dev_err(dev, "SoC revision %u is not supported!\n", rev);
+ return -ENXIO;
+ }
+
+ prv = devm_kzalloc(dev, sizeof(*prv), GFP_KERNEL);
+ if (!prv)
+ return -ENOMEM;
+
+ chip = &prv->chip;
+ mtd = nand_to_mtd(chip);
+
+ mtd->dev.parent = dev;
+ nand_set_controller_data(chip, prv);
+ nand_set_flash_node(chip, dn);
+ prv->dev = dev;
+
+ /* Read NFC configuration from Reset Config Word */
+ retval = mpc5121_nfc_read_hw_config(mtd);
+ if (retval) {
+ dev_err(dev, "Unable to read NFC config!\n");
+ return retval;
+ }
+
+ prv->irq = irq_of_parse_and_map(dn, 0);
+ if (prv->irq == NO_IRQ) {
+ dev_err(dev, "Error mapping IRQ!\n");
+ return -EINVAL;
+ }
+
+ retval = of_address_to_resource(dn, 0, &res);
+ if (retval) {
+ dev_err(dev, "Error parsing memory region!\n");
+ return retval;
+ }
+
+ chips_no = of_get_property(dn, "chips", &len);
+ if (!chips_no || len != sizeof(*chips_no)) {
+ dev_err(dev, "Invalid/missing 'chips' property!\n");
+ return -EINVAL;
+ }
+
+ regs_paddr = res.start;
+ regs_size = resource_size(&res);
+
+ if (!devm_request_mem_region(dev, regs_paddr, regs_size, DRV_NAME)) {
+ dev_err(dev, "Error requesting memory region!\n");
+ return -EBUSY;
+ }
+
+ prv->regs = devm_ioremap(dev, regs_paddr, regs_size);
+ if (!prv->regs) {
+ dev_err(dev, "Error mapping memory region!\n");
+ return -ENOMEM;
+ }
+
+ mtd->name = "MPC5121 NAND";
+ chip->dev_ready = mpc5121_nfc_dev_ready;
+ chip->cmdfunc = mpc5121_nfc_command;
+ chip->read_byte = mpc5121_nfc_read_byte;
+ chip->read_word = mpc5121_nfc_read_word;
+ chip->read_buf = mpc5121_nfc_read_buf;
+ chip->write_buf = mpc5121_nfc_write_buf;
+ chip->select_chip = mpc5121_nfc_select_chip;
+ chip->set_features = nand_get_set_features_notsupp;
+ chip->get_features = nand_get_set_features_notsupp;
+ chip->bbt_options = NAND_BBT_USE_FLASH;
+ chip->ecc.mode = NAND_ECC_SOFT;
+ chip->ecc.algo = NAND_ECC_HAMMING;
+
+ /* Support external chip-select logic on ADS5121 board */
+ if (of_machine_is_compatible("fsl,mpc5121ads")) {
+ retval = ads5121_chipselect_init(mtd);
+ if (retval) {
+ dev_err(dev, "Chipselect init error!\n");
+ return retval;
+ }
+
+ chip->select_chip = ads5121_select_chip;
+ }
+
+ /* Enable NFC clock */
+ clk = devm_clk_get(dev, "ipg");
+ if (IS_ERR(clk)) {
+ dev_err(dev, "Unable to acquire NFC clock!\n");
+ retval = PTR_ERR(clk);
+ goto error;
+ }
+ retval = clk_prepare_enable(clk);
+ if (retval) {
+ dev_err(dev, "Unable to enable NFC clock!\n");
+ goto error;
+ }
+ prv->clk = clk;
+
+ /* Reset NAND Flash controller */
+ nfc_set(mtd, NFC_CONFIG1, NFC_RESET);
+ while (nfc_read(mtd, NFC_CONFIG1) & NFC_RESET) {
+ if (resettime++ >= NFC_RESET_TIMEOUT) {
+ dev_err(dev, "Timeout while resetting NFC!\n");
+ retval = -EINVAL;
+ goto error;
+ }
+
+ udelay(1);
+ }
+
+ /* Enable write to NFC memory */
+ nfc_write(mtd, NFC_CONFIG, NFC_BLS_UNLOCKED);
+
+ /* Enable write to all NAND pages */
+ nfc_write(mtd, NFC_UNLOCKSTART_BLK0, 0x0000);
+ nfc_write(mtd, NFC_UNLOCKEND_BLK0, 0xFFFF);
+ nfc_write(mtd, NFC_WRPROT, NFC_WPC_UNLOCK);
+
+ /*
+ * Setup NFC:
+ * - Big Endian transfers,
+ * - Interrupt after full page read/write.
+ */
+ nfc_write(mtd, NFC_CONFIG1, NFC_BIG_ENDIAN | NFC_INT_MASK |
+ NFC_FULL_PAGE_INT);
+
+ /* Set spare area size */
+ nfc_write(mtd, NFC_SPAS, mtd->oobsize >> 1);
+
+ init_waitqueue_head(&prv->irq_waitq);
+ retval = devm_request_irq(dev, prv->irq, &mpc5121_nfc_irq, 0, DRV_NAME,
+ mtd);
+ if (retval) {
+ dev_err(dev, "Error requesting IRQ!\n");
+ goto error;
+ }
+
+ /* Detect NAND chips */
+ retval = nand_scan(mtd, be32_to_cpup(chips_no));
+ if (retval) {
+ dev_err(dev, "NAND Flash not found !\n");
+ goto error;
+ }
+
+ /* Set erase block size */
+ switch (mtd->erasesize / mtd->writesize) {
+ case 32:
+ nfc_set(mtd, NFC_CONFIG1, NFC_PPB_32);
+ break;
+
+ case 64:
+ nfc_set(mtd, NFC_CONFIG1, NFC_PPB_64);
+ break;
+
+ case 128:
+ nfc_set(mtd, NFC_CONFIG1, NFC_PPB_128);
+ break;
+
+ case 256:
+ nfc_set(mtd, NFC_CONFIG1, NFC_PPB_256);
+ break;
+
+ default:
+ dev_err(dev, "Unsupported NAND flash!\n");
+ retval = -ENXIO;
+ goto error;
+ }
+
+ dev_set_drvdata(dev, mtd);
+
+ /* Register device in MTD */
+ retval = mtd_device_register(mtd, NULL, 0);
+ if (retval) {
+ dev_err(dev, "Error adding MTD device!\n");
+ goto error;
+ }
+
+ return 0;
+error:
+ mpc5121_nfc_free(dev, mtd);
+ return retval;
+}
+
+static int mpc5121_nfc_remove(struct platform_device *op)
+{
+ struct device *dev = &op->dev;
+ struct mtd_info *mtd = dev_get_drvdata(dev);
+
+ nand_release(mtd);
+ mpc5121_nfc_free(dev, mtd);
+
+ return 0;
+}
+
+static const struct of_device_id mpc5121_nfc_match[] = {
+ { .compatible = "fsl,mpc5121-nfc", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mpc5121_nfc_match);
+
+static struct platform_driver mpc5121_nfc_driver = {
+ .probe = mpc5121_nfc_probe,
+ .remove = mpc5121_nfc_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = mpc5121_nfc_match,
+ },
+};
+
+module_platform_driver(mpc5121_nfc_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MPC5121 NAND MTD driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/nand/mtk_ecc.c b/drivers/mtd/nand/raw/mtk_ecc.c
index 40d86a861a70..40d86a861a70 100644
--- a/drivers/mtd/nand/mtk_ecc.c
+++ b/drivers/mtd/nand/raw/mtk_ecc.c
diff --git a/drivers/mtd/nand/mtk_ecc.h b/drivers/mtd/nand/raw/mtk_ecc.h
index a455df080952..a455df080952 100644
--- a/drivers/mtd/nand/mtk_ecc.h
+++ b/drivers/mtd/nand/raw/mtk_ecc.h
diff --git a/drivers/mtd/nand/mtk_nand.c b/drivers/mtd/nand/raw/mtk_nand.c
index 6977da3a26aa..6977da3a26aa 100644
--- a/drivers/mtd/nand/mtk_nand.c
+++ b/drivers/mtd/nand/raw/mtk_nand.c
diff --git a/drivers/mtd/nand/raw/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c
new file mode 100644
index 000000000000..45786e707b7b
--- /dev/null
+++ b/drivers/mtd/nand/raw/mxc_nand.c
@@ -0,0 +1,1957 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Sascha Hauer, kernel@pengutronix.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 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/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/completion.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include <asm/mach/flash.h>
+#include <linux/platform_data/mtd-mxc_nand.h>
+
+#define DRIVER_NAME "mxc_nand"
+
+/* Addresses for NFC registers */
+#define NFC_V1_V2_BUF_SIZE (host->regs + 0x00)
+#define NFC_V1_V2_BUF_ADDR (host->regs + 0x04)
+#define NFC_V1_V2_FLASH_ADDR (host->regs + 0x06)
+#define NFC_V1_V2_FLASH_CMD (host->regs + 0x08)
+#define NFC_V1_V2_CONFIG (host->regs + 0x0a)
+#define NFC_V1_V2_ECC_STATUS_RESULT (host->regs + 0x0c)
+#define NFC_V1_V2_RSLTMAIN_AREA (host->regs + 0x0e)
+#define NFC_V1_V2_RSLTSPARE_AREA (host->regs + 0x10)
+#define NFC_V1_V2_WRPROT (host->regs + 0x12)
+#define NFC_V1_UNLOCKSTART_BLKADDR (host->regs + 0x14)
+#define NFC_V1_UNLOCKEND_BLKADDR (host->regs + 0x16)
+#define NFC_V21_UNLOCKSTART_BLKADDR0 (host->regs + 0x20)
+#define NFC_V21_UNLOCKSTART_BLKADDR1 (host->regs + 0x24)
+#define NFC_V21_UNLOCKSTART_BLKADDR2 (host->regs + 0x28)
+#define NFC_V21_UNLOCKSTART_BLKADDR3 (host->regs + 0x2c)
+#define NFC_V21_UNLOCKEND_BLKADDR0 (host->regs + 0x22)
+#define NFC_V21_UNLOCKEND_BLKADDR1 (host->regs + 0x26)
+#define NFC_V21_UNLOCKEND_BLKADDR2 (host->regs + 0x2a)
+#define NFC_V21_UNLOCKEND_BLKADDR3 (host->regs + 0x2e)
+#define NFC_V1_V2_NF_WRPRST (host->regs + 0x18)
+#define NFC_V1_V2_CONFIG1 (host->regs + 0x1a)
+#define NFC_V1_V2_CONFIG2 (host->regs + 0x1c)
+
+#define NFC_V2_CONFIG1_ECC_MODE_4 (1 << 0)
+#define NFC_V1_V2_CONFIG1_SP_EN (1 << 2)
+#define NFC_V1_V2_CONFIG1_ECC_EN (1 << 3)
+#define NFC_V1_V2_CONFIG1_INT_MSK (1 << 4)
+#define NFC_V1_V2_CONFIG1_BIG (1 << 5)
+#define NFC_V1_V2_CONFIG1_RST (1 << 6)
+#define NFC_V1_V2_CONFIG1_CE (1 << 7)
+#define NFC_V2_CONFIG1_ONE_CYCLE (1 << 8)
+#define NFC_V2_CONFIG1_PPB(x) (((x) & 0x3) << 9)
+#define NFC_V2_CONFIG1_FP_INT (1 << 11)
+
+#define NFC_V1_V2_CONFIG2_INT (1 << 15)
+
+/*
+ * Operation modes for the NFC. Valid for v1, v2 and v3
+ * type controllers.
+ */
+#define NFC_CMD (1 << 0)
+#define NFC_ADDR (1 << 1)
+#define NFC_INPUT (1 << 2)
+#define NFC_OUTPUT (1 << 3)
+#define NFC_ID (1 << 4)
+#define NFC_STATUS (1 << 5)
+
+#define NFC_V3_FLASH_CMD (host->regs_axi + 0x00)
+#define NFC_V3_FLASH_ADDR0 (host->regs_axi + 0x04)
+
+#define NFC_V3_CONFIG1 (host->regs_axi + 0x34)
+#define NFC_V3_CONFIG1_SP_EN (1 << 0)
+#define NFC_V3_CONFIG1_RBA(x) (((x) & 0x7 ) << 4)
+
+#define NFC_V3_ECC_STATUS_RESULT (host->regs_axi + 0x38)
+
+#define NFC_V3_LAUNCH (host->regs_axi + 0x40)
+
+#define NFC_V3_WRPROT (host->regs_ip + 0x0)
+#define NFC_V3_WRPROT_LOCK_TIGHT (1 << 0)
+#define NFC_V3_WRPROT_LOCK (1 << 1)
+#define NFC_V3_WRPROT_UNLOCK (1 << 2)
+#define NFC_V3_WRPROT_BLS_UNLOCK (2 << 6)
+
+#define NFC_V3_WRPROT_UNLOCK_BLK_ADD0 (host->regs_ip + 0x04)
+
+#define NFC_V3_CONFIG2 (host->regs_ip + 0x24)
+#define NFC_V3_CONFIG2_PS_512 (0 << 0)
+#define NFC_V3_CONFIG2_PS_2048 (1 << 0)
+#define NFC_V3_CONFIG2_PS_4096 (2 << 0)
+#define NFC_V3_CONFIG2_ONE_CYCLE (1 << 2)
+#define NFC_V3_CONFIG2_ECC_EN (1 << 3)
+#define NFC_V3_CONFIG2_2CMD_PHASES (1 << 4)
+#define NFC_V3_CONFIG2_NUM_ADDR_PHASE0 (1 << 5)
+#define NFC_V3_CONFIG2_ECC_MODE_8 (1 << 6)
+#define NFC_V3_CONFIG2_PPB(x, shift) (((x) & 0x3) << shift)
+#define NFC_V3_CONFIG2_NUM_ADDR_PHASE1(x) (((x) & 0x3) << 12)
+#define NFC_V3_CONFIG2_INT_MSK (1 << 15)
+#define NFC_V3_CONFIG2_ST_CMD(x) (((x) & 0xff) << 24)
+#define NFC_V3_CONFIG2_SPAS(x) (((x) & 0xff) << 16)
+
+#define NFC_V3_CONFIG3 (host->regs_ip + 0x28)
+#define NFC_V3_CONFIG3_ADD_OP(x) (((x) & 0x3) << 0)
+#define NFC_V3_CONFIG3_FW8 (1 << 3)
+#define NFC_V3_CONFIG3_SBB(x) (((x) & 0x7) << 8)
+#define NFC_V3_CONFIG3_NUM_OF_DEVICES(x) (((x) & 0x7) << 12)
+#define NFC_V3_CONFIG3_RBB_MODE (1 << 15)
+#define NFC_V3_CONFIG3_NO_SDMA (1 << 20)
+
+#define NFC_V3_IPC (host->regs_ip + 0x2C)
+#define NFC_V3_IPC_CREQ (1 << 0)
+#define NFC_V3_IPC_INT (1 << 31)
+
+#define NFC_V3_DELAY_LINE (host->regs_ip + 0x34)
+
+struct mxc_nand_host;
+
+struct mxc_nand_devtype_data {
+ void (*preset)(struct mtd_info *);
+ int (*read_page)(struct nand_chip *chip, void *buf, void *oob, bool ecc,
+ int page);
+ void (*send_cmd)(struct mxc_nand_host *, uint16_t, int);
+ void (*send_addr)(struct mxc_nand_host *, uint16_t, int);
+ void (*send_page)(struct mtd_info *, unsigned int);
+ void (*send_read_id)(struct mxc_nand_host *);
+ uint16_t (*get_dev_status)(struct mxc_nand_host *);
+ int (*check_int)(struct mxc_nand_host *);
+ void (*irq_control)(struct mxc_nand_host *, int);
+ u32 (*get_ecc_status)(struct mxc_nand_host *);
+ const struct mtd_ooblayout_ops *ooblayout;
+ void (*select_chip)(struct mtd_info *mtd, int chip);
+ int (*setup_data_interface)(struct mtd_info *mtd, int csline,
+ const struct nand_data_interface *conf);
+ void (*enable_hwecc)(struct nand_chip *chip, bool enable);
+
+ /*
+ * On i.MX21 the CONFIG2:INT bit cannot be read if interrupts are masked
+ * (CONFIG1:INT_MSK is set). To handle this the driver uses
+ * enable_irq/disable_irq_nosync instead of CONFIG1:INT_MSK
+ */
+ int irqpending_quirk;
+ int needs_ip;
+
+ size_t regs_offset;
+ size_t spare0_offset;
+ size_t axi_offset;
+
+ int spare_len;
+ int eccbytes;
+ int eccsize;
+ int ppb_shift;
+};
+
+struct mxc_nand_host {
+ struct nand_chip nand;
+ struct device *dev;
+
+ void __iomem *spare0;
+ void __iomem *main_area0;
+
+ void __iomem *base;
+ void __iomem *regs;
+ void __iomem *regs_axi;
+ void __iomem *regs_ip;
+ int status_request;
+ struct clk *clk;
+ int clk_act;
+ int irq;
+ int eccsize;
+ int used_oobsize;
+ int active_cs;
+
+ struct completion op_completion;
+
+ uint8_t *data_buf;
+ unsigned int buf_start;
+
+ const struct mxc_nand_devtype_data *devtype_data;
+ struct mxc_nand_platform_data pdata;
+};
+
+static const char * const part_probes[] = {
+ "cmdlinepart", "RedBoot", "ofpart", NULL };
+
+static void memcpy32_fromio(void *trg, const void __iomem *src, size_t size)
+{
+ int i;
+ u32 *t = trg;
+ const __iomem u32 *s = src;
+
+ for (i = 0; i < (size >> 2); i++)
+ *t++ = __raw_readl(s++);
+}
+
+static void memcpy16_fromio(void *trg, const void __iomem *src, size_t size)
+{
+ int i;
+ u16 *t = trg;
+ const __iomem u16 *s = src;
+
+ /* We assume that src (IO) is always 32bit aligned */
+ if (PTR_ALIGN(trg, 4) == trg && IS_ALIGNED(size, 4)) {
+ memcpy32_fromio(trg, src, size);
+ return;
+ }
+
+ for (i = 0; i < (size >> 1); i++)
+ *t++ = __raw_readw(s++);
+}
+
+static inline void memcpy32_toio(void __iomem *trg, const void *src, int size)
+{
+ /* __iowrite32_copy use 32bit size values so divide by 4 */
+ __iowrite32_copy(trg, src, size / 4);
+}
+
+static void memcpy16_toio(void __iomem *trg, const void *src, int size)
+{
+ int i;
+ __iomem u16 *t = trg;
+ const u16 *s = src;
+
+ /* We assume that trg (IO) is always 32bit aligned */
+ if (PTR_ALIGN(src, 4) == src && IS_ALIGNED(size, 4)) {
+ memcpy32_toio(trg, src, size);
+ return;
+ }
+
+ for (i = 0; i < (size >> 1); i++)
+ __raw_writew(*s++, t++);
+}
+
+/*
+ * The controller splits a page into data chunks of 512 bytes + partial oob.
+ * There are writesize / 512 such chunks, the size of the partial oob parts is
+ * oobsize / #chunks rounded down to a multiple of 2. The last oob chunk then
+ * contains additionally the byte lost by rounding (if any).
+ * This function handles the needed shuffling between host->data_buf (which
+ * holds a page in natural order, i.e. writesize bytes data + oobsize bytes
+ * spare) and the NFC buffer.
+ */
+static void copy_spare(struct mtd_info *mtd, bool bfrom, void *buf)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(this);
+ u16 i, oob_chunk_size;
+ u16 num_chunks = mtd->writesize / 512;
+
+ u8 *d = buf;
+ u8 __iomem *s = host->spare0;
+ u16 sparebuf_size = host->devtype_data->spare_len;
+
+ /* size of oob chunk for all but possibly the last one */
+ oob_chunk_size = (host->used_oobsize / num_chunks) & ~1;
+
+ if (bfrom) {
+ for (i = 0; i < num_chunks - 1; i++)
+ memcpy16_fromio(d + i * oob_chunk_size,
+ s + i * sparebuf_size,
+ oob_chunk_size);
+
+ /* the last chunk */
+ memcpy16_fromio(d + i * oob_chunk_size,
+ s + i * sparebuf_size,
+ host->used_oobsize - i * oob_chunk_size);
+ } else {
+ for (i = 0; i < num_chunks - 1; i++)
+ memcpy16_toio(&s[i * sparebuf_size],
+ &d[i * oob_chunk_size],
+ oob_chunk_size);
+
+ /* the last chunk */
+ memcpy16_toio(&s[i * sparebuf_size],
+ &d[i * oob_chunk_size],
+ host->used_oobsize - i * oob_chunk_size);
+ }
+}
+
+/*
+ * MXC NANDFC can only perform full page+spare or spare-only read/write. When
+ * the upper layers perform a read/write buf operation, the saved column address
+ * is used to index into the full page. So usually this function is called with
+ * column == 0 (unless no column cycle is needed indicated by column == -1)
+ */
+static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+
+ /* Write out column address, if necessary */
+ if (column != -1) {
+ host->devtype_data->send_addr(host, column & 0xff,
+ page_addr == -1);
+ if (mtd->writesize > 512)
+ /* another col addr cycle for 2k page */
+ host->devtype_data->send_addr(host,
+ (column >> 8) & 0xff,
+ false);
+ }
+
+ /* Write out page address, if necessary */
+ if (page_addr != -1) {
+ /* paddr_0 - p_addr_7 */
+ host->devtype_data->send_addr(host, (page_addr & 0xff), false);
+
+ if (mtd->writesize > 512) {
+ if (mtd->size >= 0x10000000) {
+ /* paddr_8 - paddr_15 */
+ host->devtype_data->send_addr(host,
+ (page_addr >> 8) & 0xff,
+ false);
+ host->devtype_data->send_addr(host,
+ (page_addr >> 16) & 0xff,
+ true);
+ } else
+ /* paddr_8 - paddr_15 */
+ host->devtype_data->send_addr(host,
+ (page_addr >> 8) & 0xff, true);
+ } else {
+ if (nand_chip->options & NAND_ROW_ADDR_3) {
+ /* paddr_8 - paddr_15 */
+ host->devtype_data->send_addr(host,
+ (page_addr >> 8) & 0xff,
+ false);
+ host->devtype_data->send_addr(host,
+ (page_addr >> 16) & 0xff,
+ true);
+ } else
+ /* paddr_8 - paddr_15 */
+ host->devtype_data->send_addr(host,
+ (page_addr >> 8) & 0xff, true);
+ }
+ }
+}
+
+static int check_int_v3(struct mxc_nand_host *host)
+{
+ uint32_t tmp;
+
+ tmp = readl(NFC_V3_IPC);
+ if (!(tmp & NFC_V3_IPC_INT))
+ return 0;
+
+ tmp &= ~NFC_V3_IPC_INT;
+ writel(tmp, NFC_V3_IPC);
+
+ return 1;
+}
+
+static int check_int_v1_v2(struct mxc_nand_host *host)
+{
+ uint32_t tmp;
+
+ tmp = readw(NFC_V1_V2_CONFIG2);
+ if (!(tmp & NFC_V1_V2_CONFIG2_INT))
+ return 0;
+
+ if (!host->devtype_data->irqpending_quirk)
+ writew(tmp & ~NFC_V1_V2_CONFIG2_INT, NFC_V1_V2_CONFIG2);
+
+ return 1;
+}
+
+static void irq_control_v1_v2(struct mxc_nand_host *host, int activate)
+{
+ uint16_t tmp;
+
+ tmp = readw(NFC_V1_V2_CONFIG1);
+
+ if (activate)
+ tmp &= ~NFC_V1_V2_CONFIG1_INT_MSK;
+ else
+ tmp |= NFC_V1_V2_CONFIG1_INT_MSK;
+
+ writew(tmp, NFC_V1_V2_CONFIG1);
+}
+
+static void irq_control_v3(struct mxc_nand_host *host, int activate)
+{
+ uint32_t tmp;
+
+ tmp = readl(NFC_V3_CONFIG2);
+
+ if (activate)
+ tmp &= ~NFC_V3_CONFIG2_INT_MSK;
+ else
+ tmp |= NFC_V3_CONFIG2_INT_MSK;
+
+ writel(tmp, NFC_V3_CONFIG2);
+}
+
+static void irq_control(struct mxc_nand_host *host, int activate)
+{
+ if (host->devtype_data->irqpending_quirk) {
+ if (activate)
+ enable_irq(host->irq);
+ else
+ disable_irq_nosync(host->irq);
+ } else {
+ host->devtype_data->irq_control(host, activate);
+ }
+}
+
+static u32 get_ecc_status_v1(struct mxc_nand_host *host)
+{
+ return readw(NFC_V1_V2_ECC_STATUS_RESULT);
+}
+
+static u32 get_ecc_status_v2(struct mxc_nand_host *host)
+{
+ return readl(NFC_V1_V2_ECC_STATUS_RESULT);
+}
+
+static u32 get_ecc_status_v3(struct mxc_nand_host *host)
+{
+ return readl(NFC_V3_ECC_STATUS_RESULT);
+}
+
+static irqreturn_t mxc_nfc_irq(int irq, void *dev_id)
+{
+ struct mxc_nand_host *host = dev_id;
+
+ if (!host->devtype_data->check_int(host))
+ return IRQ_NONE;
+
+ irq_control(host, 0);
+
+ complete(&host->op_completion);
+
+ return IRQ_HANDLED;
+}
+
+/* This function polls the NANDFC to wait for the basic operation to
+ * complete by checking the INT bit of config2 register.
+ */
+static int wait_op_done(struct mxc_nand_host *host, int useirq)
+{
+ int ret = 0;
+
+ /*
+ * If operation is already complete, don't bother to setup an irq or a
+ * loop.
+ */
+ if (host->devtype_data->check_int(host))
+ return 0;
+
+ if (useirq) {
+ unsigned long timeout;
+
+ reinit_completion(&host->op_completion);
+
+ irq_control(host, 1);
+
+ timeout = wait_for_completion_timeout(&host->op_completion, HZ);
+ if (!timeout && !host->devtype_data->check_int(host)) {
+ dev_dbg(host->dev, "timeout waiting for irq\n");
+ ret = -ETIMEDOUT;
+ }
+ } else {
+ int max_retries = 8000;
+ int done;
+
+ do {
+ udelay(1);
+
+ done = host->devtype_data->check_int(host);
+ if (done)
+ break;
+
+ } while (--max_retries);
+
+ if (!done) {
+ dev_dbg(host->dev, "timeout polling for completion\n");
+ ret = -ETIMEDOUT;
+ }
+ }
+
+ WARN_ONCE(ret < 0, "timeout! useirq=%d\n", useirq);
+
+ return ret;
+}
+
+static void send_cmd_v3(struct mxc_nand_host *host, uint16_t cmd, int useirq)
+{
+ /* fill command */
+ writel(cmd, NFC_V3_FLASH_CMD);
+
+ /* send out command */
+ writel(NFC_CMD, NFC_V3_LAUNCH);
+
+ /* Wait for operation to complete */
+ wait_op_done(host, useirq);
+}
+
+/* This function issues the specified command to the NAND device and
+ * waits for completion. */
+static void send_cmd_v1_v2(struct mxc_nand_host *host, uint16_t cmd, int useirq)
+{
+ dev_dbg(host->dev, "send_cmd(host, 0x%x, %d)\n", cmd, useirq);
+
+ writew(cmd, NFC_V1_V2_FLASH_CMD);
+ writew(NFC_CMD, NFC_V1_V2_CONFIG2);
+
+ if (host->devtype_data->irqpending_quirk && (cmd == NAND_CMD_RESET)) {
+ int max_retries = 100;
+ /* Reset completion is indicated by NFC_CONFIG2 */
+ /* being set to 0 */
+ while (max_retries-- > 0) {
+ if (readw(NFC_V1_V2_CONFIG2) == 0) {
+ break;
+ }
+ udelay(1);
+ }
+ if (max_retries < 0)
+ dev_dbg(host->dev, "%s: RESET failed\n", __func__);
+ } else {
+ /* Wait for operation to complete */
+ wait_op_done(host, useirq);
+ }
+}
+
+static void send_addr_v3(struct mxc_nand_host *host, uint16_t addr, int islast)
+{
+ /* fill address */
+ writel(addr, NFC_V3_FLASH_ADDR0);
+
+ /* send out address */
+ writel(NFC_ADDR, NFC_V3_LAUNCH);
+
+ wait_op_done(host, 0);
+}
+
+/* This function sends an address (or partial address) to the
+ * NAND device. The address is used to select the source/destination for
+ * a NAND command. */
+static void send_addr_v1_v2(struct mxc_nand_host *host, uint16_t addr, int islast)
+{
+ dev_dbg(host->dev, "send_addr(host, 0x%x %d)\n", addr, islast);
+
+ writew(addr, NFC_V1_V2_FLASH_ADDR);
+ writew(NFC_ADDR, NFC_V1_V2_CONFIG2);
+
+ /* Wait for operation to complete */
+ wait_op_done(host, islast);
+}
+
+static void send_page_v3(struct mtd_info *mtd, unsigned int ops)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+ uint32_t tmp;
+
+ tmp = readl(NFC_V3_CONFIG1);
+ tmp &= ~(7 << 4);
+ writel(tmp, NFC_V3_CONFIG1);
+
+ /* transfer data from NFC ram to nand */
+ writel(ops, NFC_V3_LAUNCH);
+
+ wait_op_done(host, false);
+}
+
+static void send_page_v2(struct mtd_info *mtd, unsigned int ops)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+
+ /* NANDFC buffer 0 is used for page read/write */
+ writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR);
+
+ writew(ops, NFC_V1_V2_CONFIG2);
+
+ /* Wait for operation to complete */
+ wait_op_done(host, true);
+}
+
+static void send_page_v1(struct mtd_info *mtd, unsigned int ops)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+ int bufs, i;
+
+ if (mtd->writesize > 512)
+ bufs = 4;
+ else
+ bufs = 1;
+
+ for (i = 0; i < bufs; i++) {
+
+ /* NANDFC buffer 0 is used for page read/write */
+ writew((host->active_cs << 4) | i, NFC_V1_V2_BUF_ADDR);
+
+ writew(ops, NFC_V1_V2_CONFIG2);
+
+ /* Wait for operation to complete */
+ wait_op_done(host, true);
+ }
+}
+
+static void send_read_id_v3(struct mxc_nand_host *host)
+{
+ /* Read ID into main buffer */
+ writel(NFC_ID, NFC_V3_LAUNCH);
+
+ wait_op_done(host, true);
+
+ memcpy32_fromio(host->data_buf, host->main_area0, 16);
+}
+
+/* Request the NANDFC to perform a read of the NAND device ID. */
+static void send_read_id_v1_v2(struct mxc_nand_host *host)
+{
+ /* NANDFC buffer 0 is used for device ID output */
+ writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR);
+
+ writew(NFC_ID, NFC_V1_V2_CONFIG2);
+
+ /* Wait for operation to complete */
+ wait_op_done(host, true);
+
+ memcpy32_fromio(host->data_buf, host->main_area0, 16);
+}
+
+static uint16_t get_dev_status_v3(struct mxc_nand_host *host)
+{
+ writew(NFC_STATUS, NFC_V3_LAUNCH);
+ wait_op_done(host, true);
+
+ return readl(NFC_V3_CONFIG1) >> 16;
+}
+
+/* This function requests the NANDFC to perform a read of the
+ * NAND device status and returns the current status. */
+static uint16_t get_dev_status_v1_v2(struct mxc_nand_host *host)
+{
+ void __iomem *main_buf = host->main_area0;
+ uint32_t store;
+ uint16_t ret;
+
+ writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR);
+
+ /*
+ * The device status is stored in main_area0. To
+ * prevent corruption of the buffer save the value
+ * and restore it afterwards.
+ */
+ store = readl(main_buf);
+
+ writew(NFC_STATUS, NFC_V1_V2_CONFIG2);
+ wait_op_done(host, true);
+
+ ret = readw(main_buf);
+
+ writel(store, main_buf);
+
+ return ret;
+}
+
+static void mxc_nand_enable_hwecc_v1_v2(struct nand_chip *chip, bool enable)
+{
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ uint16_t config1;
+
+ if (chip->ecc.mode != NAND_ECC_HW)
+ return;
+
+ config1 = readw(NFC_V1_V2_CONFIG1);
+
+ if (enable)
+ config1 |= NFC_V1_V2_CONFIG1_ECC_EN;
+ else
+ config1 &= ~NFC_V1_V2_CONFIG1_ECC_EN;
+
+ writew(config1, NFC_V1_V2_CONFIG1);
+}
+
+static void mxc_nand_enable_hwecc_v3(struct nand_chip *chip, bool enable)
+{
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ uint32_t config2;
+
+ if (chip->ecc.mode != NAND_ECC_HW)
+ return;
+
+ config2 = readl(NFC_V3_CONFIG2);
+
+ if (enable)
+ config2 |= NFC_V3_CONFIG2_ECC_EN;
+ else
+ config2 &= ~NFC_V3_CONFIG2_ECC_EN;
+
+ writel(config2, NFC_V3_CONFIG2);
+}
+
+/* This functions is used by upper layer to checks if device is ready */
+static int mxc_nand_dev_ready(struct mtd_info *mtd)
+{
+ /*
+ * NFC handles R/B internally. Therefore, this function
+ * always returns status as ready.
+ */
+ return 1;
+}
+
+static int mxc_nand_read_page_v1(struct nand_chip *chip, void *buf, void *oob,
+ bool ecc, int page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ unsigned int bitflips_corrected = 0;
+ int no_subpages;
+ int i;
+
+ host->devtype_data->enable_hwecc(chip, ecc);
+
+ host->devtype_data->send_cmd(host, NAND_CMD_READ0, false);
+ mxc_do_addr_cycle(mtd, 0, page);
+
+ if (mtd->writesize > 512)
+ host->devtype_data->send_cmd(host, NAND_CMD_READSTART, true);
+
+ no_subpages = mtd->writesize >> 9;
+
+ for (i = 0; i < no_subpages; i++) {
+ uint16_t ecc_stats;
+
+ /* NANDFC buffer 0 is used for page read/write */
+ writew((host->active_cs << 4) | i, NFC_V1_V2_BUF_ADDR);
+
+ writew(NFC_OUTPUT, NFC_V1_V2_CONFIG2);
+
+ /* Wait for operation to complete */
+ wait_op_done(host, true);
+
+ ecc_stats = get_ecc_status_v1(host);
+
+ ecc_stats >>= 2;
+
+ if (buf && ecc) {
+ switch (ecc_stats & 0x3) {
+ case 0:
+ default:
+ break;
+ case 1:
+ mtd->ecc_stats.corrected++;
+ bitflips_corrected = 1;
+ break;
+ case 2:
+ mtd->ecc_stats.failed++;
+ break;
+ }
+ }
+ }
+
+ if (buf)
+ memcpy32_fromio(buf, host->main_area0, mtd->writesize);
+ if (oob)
+ copy_spare(mtd, true, oob);
+
+ return bitflips_corrected;
+}
+
+static int mxc_nand_read_page_v2_v3(struct nand_chip *chip, void *buf,
+ void *oob, bool ecc, int page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ unsigned int max_bitflips = 0;
+ u32 ecc_stat, err;
+ int no_subpages;
+ u8 ecc_bit_mask, err_limit;
+
+ host->devtype_data->enable_hwecc(chip, ecc);
+
+ host->devtype_data->send_cmd(host, NAND_CMD_READ0, false);
+ mxc_do_addr_cycle(mtd, 0, page);
+
+ if (mtd->writesize > 512)
+ host->devtype_data->send_cmd(host,
+ NAND_CMD_READSTART, true);
+
+ host->devtype_data->send_page(mtd, NFC_OUTPUT);
+
+ if (buf)
+ memcpy32_fromio(buf, host->main_area0, mtd->writesize);
+ if (oob)
+ copy_spare(mtd, true, oob);
+
+ ecc_bit_mask = (host->eccsize == 4) ? 0x7 : 0xf;
+ err_limit = (host->eccsize == 4) ? 0x4 : 0x8;
+
+ no_subpages = mtd->writesize >> 9;
+
+ ecc_stat = host->devtype_data->get_ecc_status(host);
+
+ do {
+ err = ecc_stat & ecc_bit_mask;
+ if (err > err_limit) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += err;
+ max_bitflips = max_t(unsigned int, max_bitflips, err);
+ }
+
+ ecc_stat >>= 4;
+ } while (--no_subpages);
+
+ return max_bitflips;
+}
+
+static int mxc_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ void *oob_buf;
+
+ if (oob_required)
+ oob_buf = chip->oob_poi;
+ else
+ oob_buf = NULL;
+
+ return host->devtype_data->read_page(chip, buf, oob_buf, 1, page);
+}
+
+static int mxc_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ void *oob_buf;
+
+ if (oob_required)
+ oob_buf = chip->oob_poi;
+ else
+ oob_buf = NULL;
+
+ return host->devtype_data->read_page(chip, buf, oob_buf, 0, page);
+}
+
+static int mxc_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+
+ return host->devtype_data->read_page(chip, NULL, chip->oob_poi, 0,
+ page);
+}
+
+static int mxc_nand_write_page(struct nand_chip *chip, const uint8_t *buf,
+ bool ecc, int page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+
+ host->devtype_data->enable_hwecc(chip, ecc);
+
+ host->devtype_data->send_cmd(host, NAND_CMD_SEQIN, false);
+ mxc_do_addr_cycle(mtd, 0, page);
+
+ memcpy32_toio(host->main_area0, buf, mtd->writesize);
+ copy_spare(mtd, false, chip->oob_poi);
+
+ host->devtype_data->send_page(mtd, NFC_INPUT);
+ host->devtype_data->send_cmd(host, NAND_CMD_PAGEPROG, true);
+ mxc_do_addr_cycle(mtd, 0, page);
+
+ return 0;
+}
+
+static int mxc_nand_write_page_ecc(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required,
+ int page)
+{
+ return mxc_nand_write_page(chip, buf, true, page);
+}
+
+static int mxc_nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required, int page)
+{
+ return mxc_nand_write_page(chip, buf, false, page);
+}
+
+static int mxc_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+
+ memset(host->data_buf, 0xff, mtd->writesize);
+
+ return mxc_nand_write_page(chip, host->data_buf, false, page);
+}
+
+static u_char mxc_nand_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+ uint8_t ret;
+
+ /* Check for status request */
+ if (host->status_request)
+ return host->devtype_data->get_dev_status(host) & 0xFF;
+
+ if (nand_chip->options & NAND_BUSWIDTH_16) {
+ /* only take the lower byte of each word */
+ ret = *(uint16_t *)(host->data_buf + host->buf_start);
+
+ host->buf_start += 2;
+ } else {
+ ret = *(uint8_t *)(host->data_buf + host->buf_start);
+ host->buf_start++;
+ }
+
+ dev_dbg(host->dev, "%s: ret=0x%hhx (start=%u)\n", __func__, ret, host->buf_start);
+ return ret;
+}
+
+static uint16_t mxc_nand_read_word(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+ uint16_t ret;
+
+ ret = *(uint16_t *)(host->data_buf + host->buf_start);
+ host->buf_start += 2;
+
+ return ret;
+}
+
+/* Write data of length len to buffer buf. The data to be
+ * written on NAND Flash is first copied to RAMbuffer. After the Data Input
+ * Operation by the NFC, the data is written to NAND Flash */
+static void mxc_nand_write_buf(struct mtd_info *mtd,
+ const u_char *buf, int len)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+ u16 col = host->buf_start;
+ int n = mtd->oobsize + mtd->writesize - col;
+
+ n = min(n, len);
+
+ memcpy(host->data_buf + col, buf, n);
+
+ host->buf_start += n;
+}
+
+/* Read the data buffer from the NAND Flash. To read the data from NAND
+ * Flash first the data output cycle is initiated by the NFC, which copies
+ * the data to RAMbuffer. This data of length len is then copied to buffer buf.
+ */
+static void mxc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+ u16 col = host->buf_start;
+ int n = mtd->oobsize + mtd->writesize - col;
+
+ n = min(n, len);
+
+ memcpy(buf, host->data_buf + col, n);
+
+ host->buf_start += n;
+}
+
+/* This function is used by upper layer for select and
+ * deselect of the NAND chip */
+static void mxc_nand_select_chip_v1_v3(struct mtd_info *mtd, int chip)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+
+ if (chip == -1) {
+ /* Disable the NFC clock */
+ if (host->clk_act) {
+ clk_disable_unprepare(host->clk);
+ host->clk_act = 0;
+ }
+ return;
+ }
+
+ if (!host->clk_act) {
+ /* Enable the NFC clock */
+ clk_prepare_enable(host->clk);
+ host->clk_act = 1;
+ }
+}
+
+static void mxc_nand_select_chip_v2(struct mtd_info *mtd, int chip)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+
+ if (chip == -1) {
+ /* Disable the NFC clock */
+ if (host->clk_act) {
+ clk_disable_unprepare(host->clk);
+ host->clk_act = 0;
+ }
+ return;
+ }
+
+ if (!host->clk_act) {
+ /* Enable the NFC clock */
+ clk_prepare_enable(host->clk);
+ host->clk_act = 1;
+ }
+
+ host->active_cs = chip;
+ writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR);
+}
+
+#define MXC_V1_ECCBYTES 5
+
+static int mxc_v1_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+
+ if (section >= nand_chip->ecc.steps)
+ return -ERANGE;
+
+ oobregion->offset = (section * 16) + 6;
+ oobregion->length = MXC_V1_ECCBYTES;
+
+ return 0;
+}
+
+static int mxc_v1_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+
+ if (section > nand_chip->ecc.steps)
+ return -ERANGE;
+
+ if (!section) {
+ if (mtd->writesize <= 512) {
+ oobregion->offset = 0;
+ oobregion->length = 5;
+ } else {
+ oobregion->offset = 2;
+ oobregion->length = 4;
+ }
+ } else {
+ oobregion->offset = ((section - 1) * 16) + MXC_V1_ECCBYTES + 6;
+ if (section < nand_chip->ecc.steps)
+ oobregion->length = (section * 16) + 6 -
+ oobregion->offset;
+ else
+ oobregion->length = mtd->oobsize - oobregion->offset;
+ }
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops mxc_v1_ooblayout_ops = {
+ .ecc = mxc_v1_ooblayout_ecc,
+ .free = mxc_v1_ooblayout_free,
+};
+
+static int mxc_v2_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ int stepsize = nand_chip->ecc.bytes == 9 ? 16 : 26;
+
+ if (section >= nand_chip->ecc.steps)
+ return -ERANGE;
+
+ oobregion->offset = (section * stepsize) + 7;
+ oobregion->length = nand_chip->ecc.bytes;
+
+ return 0;
+}
+
+static int mxc_v2_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ int stepsize = nand_chip->ecc.bytes == 9 ? 16 : 26;
+
+ if (section >= nand_chip->ecc.steps)
+ return -ERANGE;
+
+ if (!section) {
+ if (mtd->writesize <= 512) {
+ oobregion->offset = 0;
+ oobregion->length = 5;
+ } else {
+ oobregion->offset = 2;
+ oobregion->length = 4;
+ }
+ } else {
+ oobregion->offset = section * stepsize;
+ oobregion->length = 7;
+ }
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops mxc_v2_ooblayout_ops = {
+ .ecc = mxc_v2_ooblayout_ecc,
+ .free = mxc_v2_ooblayout_free,
+};
+
+/*
+ * v2 and v3 type controllers can do 4bit or 8bit ecc depending
+ * on how much oob the nand chip has. For 8bit ecc we need at least
+ * 26 bytes of oob data per 512 byte block.
+ */
+static int get_eccsize(struct mtd_info *mtd)
+{
+ int oobbytes_per_512 = 0;
+
+ oobbytes_per_512 = mtd->oobsize * 512 / mtd->writesize;
+
+ if (oobbytes_per_512 < 26)
+ return 4;
+ else
+ return 8;
+}
+
+static void preset_v1(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+ uint16_t config1 = 0;
+
+ if (nand_chip->ecc.mode == NAND_ECC_HW && mtd->writesize)
+ config1 |= NFC_V1_V2_CONFIG1_ECC_EN;
+
+ if (!host->devtype_data->irqpending_quirk)
+ config1 |= NFC_V1_V2_CONFIG1_INT_MSK;
+
+ host->eccsize = 1;
+
+ writew(config1, NFC_V1_V2_CONFIG1);
+ /* preset operation */
+
+ /* Unlock the internal RAM Buffer */
+ writew(0x2, NFC_V1_V2_CONFIG);
+
+ /* Blocks to be unlocked */
+ writew(0x0, NFC_V1_UNLOCKSTART_BLKADDR);
+ writew(0xffff, NFC_V1_UNLOCKEND_BLKADDR);
+
+ /* Unlock Block Command for given address range */
+ writew(0x4, NFC_V1_V2_WRPROT);
+}
+
+static int mxc_nand_v2_setup_data_interface(struct mtd_info *mtd, int csline,
+ const struct nand_data_interface *conf)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+ int tRC_min_ns, tRC_ps, ret;
+ unsigned long rate, rate_round;
+ const struct nand_sdr_timings *timings;
+ u16 config1;
+
+ timings = nand_get_sdr_timings(conf);
+ if (IS_ERR(timings))
+ return -ENOTSUPP;
+
+ config1 = readw(NFC_V1_V2_CONFIG1);
+
+ tRC_min_ns = timings->tRC_min / 1000;
+ rate = 1000000000 / tRC_min_ns;
+
+ /*
+ * For tRC < 30ns we have to use EDO mode. In this case the controller
+ * does one access per clock cycle. Otherwise the controller does one
+ * access in two clock cycles, thus we have to double the rate to the
+ * controller.
+ */
+ if (tRC_min_ns < 30) {
+ rate_round = clk_round_rate(host->clk, rate);
+ config1 |= NFC_V2_CONFIG1_ONE_CYCLE;
+ tRC_ps = 1000000000 / (rate_round / 1000);
+ } else {
+ rate *= 2;
+ rate_round = clk_round_rate(host->clk, rate);
+ config1 &= ~NFC_V2_CONFIG1_ONE_CYCLE;
+ tRC_ps = 1000000000 / (rate_round / 1000 / 2);
+ }
+
+ /*
+ * The timing values compared against are from the i.MX25 Automotive
+ * datasheet, Table 50. NFC Timing Parameters
+ */
+ if (timings->tCLS_min > tRC_ps - 1000 ||
+ timings->tCLH_min > tRC_ps - 2000 ||
+ timings->tCS_min > tRC_ps - 1000 ||
+ timings->tCH_min > tRC_ps - 2000 ||
+ timings->tWP_min > tRC_ps - 1500 ||
+ timings->tALS_min > tRC_ps ||
+ timings->tALH_min > tRC_ps - 3000 ||
+ timings->tDS_min > tRC_ps ||
+ timings->tDH_min > tRC_ps - 5000 ||
+ timings->tWC_min > 2 * tRC_ps ||
+ timings->tWH_min > tRC_ps - 2500 ||
+ timings->tRR_min > 6 * tRC_ps ||
+ timings->tRP_min > 3 * tRC_ps / 2 ||
+ timings->tRC_min > 2 * tRC_ps ||
+ timings->tREH_min > (tRC_ps / 2) - 2500) {
+ dev_dbg(host->dev, "Timing out of bounds\n");
+ return -EINVAL;
+ }
+
+ if (csline == NAND_DATA_IFACE_CHECK_ONLY)
+ return 0;
+
+ ret = clk_set_rate(host->clk, rate);
+ if (ret)
+ return ret;
+
+ writew(config1, NFC_V1_V2_CONFIG1);
+
+ dev_dbg(host->dev, "Setting rate to %ldHz, %s mode\n", rate_round,
+ config1 & NFC_V2_CONFIG1_ONE_CYCLE ? "One cycle (EDO)" :
+ "normal");
+
+ return 0;
+}
+
+static void preset_v2(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+ uint16_t config1 = 0;
+
+ config1 |= NFC_V2_CONFIG1_FP_INT;
+
+ if (!host->devtype_data->irqpending_quirk)
+ config1 |= NFC_V1_V2_CONFIG1_INT_MSK;
+
+ if (mtd->writesize) {
+ uint16_t pages_per_block = mtd->erasesize / mtd->writesize;
+
+ if (nand_chip->ecc.mode == NAND_ECC_HW)
+ config1 |= NFC_V1_V2_CONFIG1_ECC_EN;
+
+ host->eccsize = get_eccsize(mtd);
+ if (host->eccsize == 4)
+ config1 |= NFC_V2_CONFIG1_ECC_MODE_4;
+
+ config1 |= NFC_V2_CONFIG1_PPB(ffs(pages_per_block) - 6);
+ } else {
+ host->eccsize = 1;
+ }
+
+ writew(config1, NFC_V1_V2_CONFIG1);
+ /* preset operation */
+
+ /* Unlock the internal RAM Buffer */
+ writew(0x2, NFC_V1_V2_CONFIG);
+
+ /* Blocks to be unlocked */
+ writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR0);
+ writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR1);
+ writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR2);
+ writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR3);
+ writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR0);
+ writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR1);
+ writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR2);
+ writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR3);
+
+ /* Unlock Block Command for given address range */
+ writew(0x4, NFC_V1_V2_WRPROT);
+}
+
+static void preset_v3(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ uint32_t config2, config3;
+ int i, addr_phases;
+
+ writel(NFC_V3_CONFIG1_RBA(0), NFC_V3_CONFIG1);
+ writel(NFC_V3_IPC_CREQ, NFC_V3_IPC);
+
+ /* Unlock the internal RAM Buffer */
+ writel(NFC_V3_WRPROT_BLS_UNLOCK | NFC_V3_WRPROT_UNLOCK,
+ NFC_V3_WRPROT);
+
+ /* Blocks to be unlocked */
+ for (i = 0; i < NAND_MAX_CHIPS; i++)
+ writel(0xffff << 16, NFC_V3_WRPROT_UNLOCK_BLK_ADD0 + (i << 2));
+
+ writel(0, NFC_V3_IPC);
+
+ config2 = NFC_V3_CONFIG2_ONE_CYCLE |
+ NFC_V3_CONFIG2_2CMD_PHASES |
+ NFC_V3_CONFIG2_SPAS(mtd->oobsize >> 1) |
+ NFC_V3_CONFIG2_ST_CMD(0x70) |
+ NFC_V3_CONFIG2_INT_MSK |
+ NFC_V3_CONFIG2_NUM_ADDR_PHASE0;
+
+ addr_phases = fls(chip->pagemask) >> 3;
+
+ if (mtd->writesize == 2048) {
+ config2 |= NFC_V3_CONFIG2_PS_2048;
+ config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases);
+ } else if (mtd->writesize == 4096) {
+ config2 |= NFC_V3_CONFIG2_PS_4096;
+ config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases);
+ } else {
+ config2 |= NFC_V3_CONFIG2_PS_512;
+ config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases - 1);
+ }
+
+ if (mtd->writesize) {
+ if (chip->ecc.mode == NAND_ECC_HW)
+ config2 |= NFC_V3_CONFIG2_ECC_EN;
+
+ config2 |= NFC_V3_CONFIG2_PPB(
+ ffs(mtd->erasesize / mtd->writesize) - 6,
+ host->devtype_data->ppb_shift);
+ host->eccsize = get_eccsize(mtd);
+ if (host->eccsize == 8)
+ config2 |= NFC_V3_CONFIG2_ECC_MODE_8;
+ }
+
+ writel(config2, NFC_V3_CONFIG2);
+
+ config3 = NFC_V3_CONFIG3_NUM_OF_DEVICES(0) |
+ NFC_V3_CONFIG3_NO_SDMA |
+ NFC_V3_CONFIG3_RBB_MODE |
+ NFC_V3_CONFIG3_SBB(6) | /* Reset default */
+ NFC_V3_CONFIG3_ADD_OP(0);
+
+ if (!(chip->options & NAND_BUSWIDTH_16))
+ config3 |= NFC_V3_CONFIG3_FW8;
+
+ writel(config3, NFC_V3_CONFIG3);
+
+ writel(0, NFC_V3_DELAY_LINE);
+}
+
+/* Used by the upper layer to write command to NAND Flash for
+ * different operations to be carried out on NAND Flash */
+static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
+ int column, int page_addr)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+
+ dev_dbg(host->dev, "mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n",
+ command, column, page_addr);
+
+ /* Reset command state information */
+ host->status_request = false;
+
+ /* Command pre-processing step */
+ switch (command) {
+ case NAND_CMD_RESET:
+ host->devtype_data->preset(mtd);
+ host->devtype_data->send_cmd(host, command, false);
+ break;
+
+ case NAND_CMD_STATUS:
+ host->buf_start = 0;
+ host->status_request = true;
+
+ host->devtype_data->send_cmd(host, command, true);
+ WARN_ONCE(column != -1 || page_addr != -1,
+ "Unexpected column/row value (cmd=%u, col=%d, row=%d)\n",
+ command, column, page_addr);
+ mxc_do_addr_cycle(mtd, column, page_addr);
+ break;
+
+ case NAND_CMD_READID:
+ host->devtype_data->send_cmd(host, command, true);
+ mxc_do_addr_cycle(mtd, column, page_addr);
+ host->devtype_data->send_read_id(host);
+ host->buf_start = 0;
+ break;
+
+ case NAND_CMD_ERASE1:
+ case NAND_CMD_ERASE2:
+ host->devtype_data->send_cmd(host, command, false);
+ WARN_ONCE(column != -1,
+ "Unexpected column value (cmd=%u, col=%d)\n",
+ command, column);
+ mxc_do_addr_cycle(mtd, column, page_addr);
+
+ break;
+ case NAND_CMD_PARAM:
+ host->devtype_data->send_cmd(host, command, false);
+ mxc_do_addr_cycle(mtd, column, page_addr);
+ host->devtype_data->send_page(mtd, NFC_OUTPUT);
+ memcpy32_fromio(host->data_buf, host->main_area0, 512);
+ host->buf_start = 0;
+ break;
+ default:
+ WARN_ONCE(1, "Unimplemented command (cmd=%u)\n",
+ command);
+ break;
+ }
+}
+
+static int mxc_nand_set_features(struct mtd_info *mtd, struct nand_chip *chip,
+ int addr, u8 *subfeature_param)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+ int i;
+
+ host->buf_start = 0;
+
+ for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
+ chip->write_byte(mtd, subfeature_param[i]);
+
+ memcpy32_toio(host->main_area0, host->data_buf, mtd->writesize);
+ host->devtype_data->send_cmd(host, NAND_CMD_SET_FEATURES, false);
+ mxc_do_addr_cycle(mtd, addr, -1);
+ host->devtype_data->send_page(mtd, NFC_INPUT);
+
+ return 0;
+}
+
+static int mxc_nand_get_features(struct mtd_info *mtd, struct nand_chip *chip,
+ int addr, u8 *subfeature_param)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+ int i;
+
+ host->devtype_data->send_cmd(host, NAND_CMD_GET_FEATURES, false);
+ mxc_do_addr_cycle(mtd, addr, -1);
+ host->devtype_data->send_page(mtd, NFC_OUTPUT);
+ memcpy32_fromio(host->data_buf, host->main_area0, 512);
+ host->buf_start = 0;
+
+ for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
+ *subfeature_param++ = chip->read_byte(mtd);
+
+ return 0;
+}
+
+/*
+ * The generic flash bbt decriptors overlap with our ecc
+ * hardware, so define some i.MX specific ones.
+ */
+static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' };
+static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 0,
+ .len = 4,
+ .veroffs = 4,
+ .maxblocks = 4,
+ .pattern = bbt_pattern,
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 0,
+ .len = 4,
+ .veroffs = 4,
+ .maxblocks = 4,
+ .pattern = mirror_pattern,
+};
+
+/* v1 + irqpending_quirk: i.MX21 */
+static const struct mxc_nand_devtype_data imx21_nand_devtype_data = {
+ .preset = preset_v1,
+ .read_page = mxc_nand_read_page_v1,
+ .send_cmd = send_cmd_v1_v2,
+ .send_addr = send_addr_v1_v2,
+ .send_page = send_page_v1,
+ .send_read_id = send_read_id_v1_v2,
+ .get_dev_status = get_dev_status_v1_v2,
+ .check_int = check_int_v1_v2,
+ .irq_control = irq_control_v1_v2,
+ .get_ecc_status = get_ecc_status_v1,
+ .ooblayout = &mxc_v1_ooblayout_ops,
+ .select_chip = mxc_nand_select_chip_v1_v3,
+ .enable_hwecc = mxc_nand_enable_hwecc_v1_v2,
+ .irqpending_quirk = 1,
+ .needs_ip = 0,
+ .regs_offset = 0xe00,
+ .spare0_offset = 0x800,
+ .spare_len = 16,
+ .eccbytes = 3,
+ .eccsize = 1,
+};
+
+/* v1 + !irqpending_quirk: i.MX27, i.MX31 */
+static const struct mxc_nand_devtype_data imx27_nand_devtype_data = {
+ .preset = preset_v1,
+ .read_page = mxc_nand_read_page_v1,
+ .send_cmd = send_cmd_v1_v2,
+ .send_addr = send_addr_v1_v2,
+ .send_page = send_page_v1,
+ .send_read_id = send_read_id_v1_v2,
+ .get_dev_status = get_dev_status_v1_v2,
+ .check_int = check_int_v1_v2,
+ .irq_control = irq_control_v1_v2,
+ .get_ecc_status = get_ecc_status_v1,
+ .ooblayout = &mxc_v1_ooblayout_ops,
+ .select_chip = mxc_nand_select_chip_v1_v3,
+ .enable_hwecc = mxc_nand_enable_hwecc_v1_v2,
+ .irqpending_quirk = 0,
+ .needs_ip = 0,
+ .regs_offset = 0xe00,
+ .spare0_offset = 0x800,
+ .axi_offset = 0,
+ .spare_len = 16,
+ .eccbytes = 3,
+ .eccsize = 1,
+};
+
+/* v21: i.MX25, i.MX35 */
+static const struct mxc_nand_devtype_data imx25_nand_devtype_data = {
+ .preset = preset_v2,
+ .read_page = mxc_nand_read_page_v2_v3,
+ .send_cmd = send_cmd_v1_v2,
+ .send_addr = send_addr_v1_v2,
+ .send_page = send_page_v2,
+ .send_read_id = send_read_id_v1_v2,
+ .get_dev_status = get_dev_status_v1_v2,
+ .check_int = check_int_v1_v2,
+ .irq_control = irq_control_v1_v2,
+ .get_ecc_status = get_ecc_status_v2,
+ .ooblayout = &mxc_v2_ooblayout_ops,
+ .select_chip = mxc_nand_select_chip_v2,
+ .setup_data_interface = mxc_nand_v2_setup_data_interface,
+ .enable_hwecc = mxc_nand_enable_hwecc_v1_v2,
+ .irqpending_quirk = 0,
+ .needs_ip = 0,
+ .regs_offset = 0x1e00,
+ .spare0_offset = 0x1000,
+ .axi_offset = 0,
+ .spare_len = 64,
+ .eccbytes = 9,
+ .eccsize = 0,
+};
+
+/* v3.2a: i.MX51 */
+static const struct mxc_nand_devtype_data imx51_nand_devtype_data = {
+ .preset = preset_v3,
+ .read_page = mxc_nand_read_page_v2_v3,
+ .send_cmd = send_cmd_v3,
+ .send_addr = send_addr_v3,
+ .send_page = send_page_v3,
+ .send_read_id = send_read_id_v3,
+ .get_dev_status = get_dev_status_v3,
+ .check_int = check_int_v3,
+ .irq_control = irq_control_v3,
+ .get_ecc_status = get_ecc_status_v3,
+ .ooblayout = &mxc_v2_ooblayout_ops,
+ .select_chip = mxc_nand_select_chip_v1_v3,
+ .enable_hwecc = mxc_nand_enable_hwecc_v3,
+ .irqpending_quirk = 0,
+ .needs_ip = 1,
+ .regs_offset = 0,
+ .spare0_offset = 0x1000,
+ .axi_offset = 0x1e00,
+ .spare_len = 64,
+ .eccbytes = 0,
+ .eccsize = 0,
+ .ppb_shift = 7,
+};
+
+/* v3.2b: i.MX53 */
+static const struct mxc_nand_devtype_data imx53_nand_devtype_data = {
+ .preset = preset_v3,
+ .read_page = mxc_nand_read_page_v2_v3,
+ .send_cmd = send_cmd_v3,
+ .send_addr = send_addr_v3,
+ .send_page = send_page_v3,
+ .send_read_id = send_read_id_v3,
+ .get_dev_status = get_dev_status_v3,
+ .check_int = check_int_v3,
+ .irq_control = irq_control_v3,
+ .get_ecc_status = get_ecc_status_v3,
+ .ooblayout = &mxc_v2_ooblayout_ops,
+ .select_chip = mxc_nand_select_chip_v1_v3,
+ .enable_hwecc = mxc_nand_enable_hwecc_v3,
+ .irqpending_quirk = 0,
+ .needs_ip = 1,
+ .regs_offset = 0,
+ .spare0_offset = 0x1000,
+ .axi_offset = 0x1e00,
+ .spare_len = 64,
+ .eccbytes = 0,
+ .eccsize = 0,
+ .ppb_shift = 8,
+};
+
+static inline int is_imx21_nfc(struct mxc_nand_host *host)
+{
+ return host->devtype_data == &imx21_nand_devtype_data;
+}
+
+static inline int is_imx27_nfc(struct mxc_nand_host *host)
+{
+ return host->devtype_data == &imx27_nand_devtype_data;
+}
+
+static inline int is_imx25_nfc(struct mxc_nand_host *host)
+{
+ return host->devtype_data == &imx25_nand_devtype_data;
+}
+
+static inline int is_imx51_nfc(struct mxc_nand_host *host)
+{
+ return host->devtype_data == &imx51_nand_devtype_data;
+}
+
+static inline int is_imx53_nfc(struct mxc_nand_host *host)
+{
+ return host->devtype_data == &imx53_nand_devtype_data;
+}
+
+static const struct platform_device_id mxcnd_devtype[] = {
+ {
+ .name = "imx21-nand",
+ .driver_data = (kernel_ulong_t) &imx21_nand_devtype_data,
+ }, {
+ .name = "imx27-nand",
+ .driver_data = (kernel_ulong_t) &imx27_nand_devtype_data,
+ }, {
+ .name = "imx25-nand",
+ .driver_data = (kernel_ulong_t) &imx25_nand_devtype_data,
+ }, {
+ .name = "imx51-nand",
+ .driver_data = (kernel_ulong_t) &imx51_nand_devtype_data,
+ }, {
+ .name = "imx53-nand",
+ .driver_data = (kernel_ulong_t) &imx53_nand_devtype_data,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(platform, mxcnd_devtype);
+
+#ifdef CONFIG_OF
+static const struct of_device_id mxcnd_dt_ids[] = {
+ {
+ .compatible = "fsl,imx21-nand",
+ .data = &imx21_nand_devtype_data,
+ }, {
+ .compatible = "fsl,imx27-nand",
+ .data = &imx27_nand_devtype_data,
+ }, {
+ .compatible = "fsl,imx25-nand",
+ .data = &imx25_nand_devtype_data,
+ }, {
+ .compatible = "fsl,imx51-nand",
+ .data = &imx51_nand_devtype_data,
+ }, {
+ .compatible = "fsl,imx53-nand",
+ .data = &imx53_nand_devtype_data,
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mxcnd_dt_ids);
+
+static int __init mxcnd_probe_dt(struct mxc_nand_host *host)
+{
+ struct device_node *np = host->dev->of_node;
+ const struct of_device_id *of_id =
+ of_match_device(mxcnd_dt_ids, host->dev);
+
+ if (!np)
+ return 1;
+
+ host->devtype_data = of_id->data;
+
+ return 0;
+}
+#else
+static int __init mxcnd_probe_dt(struct mxc_nand_host *host)
+{
+ return 1;
+}
+#endif
+
+static int mxcnd_probe(struct platform_device *pdev)
+{
+ struct nand_chip *this;
+ struct mtd_info *mtd;
+ struct mxc_nand_host *host;
+ struct resource *res;
+ int err = 0;
+
+ /* Allocate memory for MTD device structure and private data */
+ host = devm_kzalloc(&pdev->dev, sizeof(struct mxc_nand_host),
+ GFP_KERNEL);
+ if (!host)
+ return -ENOMEM;
+
+ /* allocate a temporary buffer for the nand_scan_ident() */
+ host->data_buf = devm_kzalloc(&pdev->dev, PAGE_SIZE, GFP_KERNEL);
+ if (!host->data_buf)
+ return -ENOMEM;
+
+ host->dev = &pdev->dev;
+ /* structures must be linked */
+ this = &host->nand;
+ mtd = nand_to_mtd(this);
+ mtd->dev.parent = &pdev->dev;
+ mtd->name = DRIVER_NAME;
+
+ /* 50 us command delay time */
+ this->chip_delay = 5;
+
+ nand_set_controller_data(this, host);
+ nand_set_flash_node(this, pdev->dev.of_node),
+ this->dev_ready = mxc_nand_dev_ready;
+ this->cmdfunc = mxc_nand_command;
+ this->read_byte = mxc_nand_read_byte;
+ this->read_word = mxc_nand_read_word;
+ this->write_buf = mxc_nand_write_buf;
+ this->read_buf = mxc_nand_read_buf;
+ this->set_features = mxc_nand_set_features;
+ this->get_features = mxc_nand_get_features;
+
+ host->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(host->clk))
+ return PTR_ERR(host->clk);
+
+ err = mxcnd_probe_dt(host);
+ if (err > 0) {
+ struct mxc_nand_platform_data *pdata =
+ dev_get_platdata(&pdev->dev);
+ if (pdata) {
+ host->pdata = *pdata;
+ host->devtype_data = (struct mxc_nand_devtype_data *)
+ pdev->id_entry->driver_data;
+ } else {
+ err = -ENODEV;
+ }
+ }
+ if (err < 0)
+ return err;
+
+ this->setup_data_interface = host->devtype_data->setup_data_interface;
+
+ if (host->devtype_data->needs_ip) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ host->regs_ip = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(host->regs_ip))
+ return PTR_ERR(host->regs_ip);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ } else {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ }
+
+ host->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(host->base))
+ return PTR_ERR(host->base);
+
+ host->main_area0 = host->base;
+
+ if (host->devtype_data->regs_offset)
+ host->regs = host->base + host->devtype_data->regs_offset;
+ host->spare0 = host->base + host->devtype_data->spare0_offset;
+ if (host->devtype_data->axi_offset)
+ host->regs_axi = host->base + host->devtype_data->axi_offset;
+
+ this->ecc.bytes = host->devtype_data->eccbytes;
+ host->eccsize = host->devtype_data->eccsize;
+
+ this->select_chip = host->devtype_data->select_chip;
+ this->ecc.size = 512;
+ mtd_set_ooblayout(mtd, host->devtype_data->ooblayout);
+
+ if (host->pdata.hw_ecc) {
+ this->ecc.mode = NAND_ECC_HW;
+ } else {
+ this->ecc.mode = NAND_ECC_SOFT;
+ this->ecc.algo = NAND_ECC_HAMMING;
+ }
+
+ /* NAND bus width determines access functions used by upper layer */
+ if (host->pdata.width == 2)
+ this->options |= NAND_BUSWIDTH_16;
+
+ /* update flash based bbt */
+ if (host->pdata.flash_bbt)
+ this->bbt_options |= NAND_BBT_USE_FLASH;
+
+ init_completion(&host->op_completion);
+
+ host->irq = platform_get_irq(pdev, 0);
+ if (host->irq < 0)
+ return host->irq;
+
+ /*
+ * Use host->devtype_data->irq_control() here instead of irq_control()
+ * because we must not disable_irq_nosync without having requested the
+ * irq.
+ */
+ host->devtype_data->irq_control(host, 0);
+
+ err = devm_request_irq(&pdev->dev, host->irq, mxc_nfc_irq,
+ 0, DRIVER_NAME, host);
+ if (err)
+ return err;
+
+ err = clk_prepare_enable(host->clk);
+ if (err)
+ return err;
+ host->clk_act = 1;
+
+ /*
+ * Now that we "own" the interrupt make sure the interrupt mask bit is
+ * cleared on i.MX21. Otherwise we can't read the interrupt status bit
+ * on this machine.
+ */
+ if (host->devtype_data->irqpending_quirk) {
+ disable_irq_nosync(host->irq);
+ host->devtype_data->irq_control(host, 1);
+ }
+
+ /* first scan to find the device and get the page size */
+ err = nand_scan_ident(mtd, is_imx25_nfc(host) ? 4 : 1, NULL);
+ if (err)
+ goto escan;
+
+ switch (this->ecc.mode) {
+ case NAND_ECC_HW:
+ this->ecc.read_page = mxc_nand_read_page;
+ this->ecc.read_page_raw = mxc_nand_read_page_raw;
+ this->ecc.read_oob = mxc_nand_read_oob;
+ this->ecc.write_page = mxc_nand_write_page_ecc;
+ this->ecc.write_page_raw = mxc_nand_write_page_raw;
+ this->ecc.write_oob = mxc_nand_write_oob;
+ break;
+
+ case NAND_ECC_SOFT:
+ break;
+
+ default:
+ err = -EINVAL;
+ goto escan;
+ }
+
+ if (this->bbt_options & NAND_BBT_USE_FLASH) {
+ this->bbt_td = &bbt_main_descr;
+ this->bbt_md = &bbt_mirror_descr;
+ }
+
+ /* allocate the right size buffer now */
+ devm_kfree(&pdev->dev, (void *)host->data_buf);
+ host->data_buf = devm_kzalloc(&pdev->dev, mtd->writesize + mtd->oobsize,
+ GFP_KERNEL);
+ if (!host->data_buf) {
+ err = -ENOMEM;
+ goto escan;
+ }
+
+ /* Call preset again, with correct writesize this time */
+ host->devtype_data->preset(mtd);
+
+ if (!this->ecc.bytes) {
+ if (host->eccsize == 8)
+ this->ecc.bytes = 18;
+ else if (host->eccsize == 4)
+ this->ecc.bytes = 9;
+ }
+
+ /*
+ * Experimentation shows that i.MX NFC can only handle up to 218 oob
+ * bytes. Limit used_oobsize to 218 so as to not confuse copy_spare()
+ * into copying invalid data to/from the spare IO buffer, as this
+ * might cause ECC data corruption when doing sub-page write to a
+ * partially written page.
+ */
+ host->used_oobsize = min(mtd->oobsize, 218U);
+
+ if (this->ecc.mode == NAND_ECC_HW) {
+ if (is_imx21_nfc(host) || is_imx27_nfc(host))
+ this->ecc.strength = 1;
+ else
+ this->ecc.strength = (host->eccsize == 4) ? 4 : 8;
+ }
+
+ /* second phase scan */
+ err = nand_scan_tail(mtd);
+ if (err)
+ goto escan;
+
+ /* Register the partitions */
+ err = mtd_device_parse_register(mtd, part_probes, NULL,
+ host->pdata.parts,
+ host->pdata.nr_parts);
+ if (err)
+ goto cleanup_nand;
+
+ platform_set_drvdata(pdev, host);
+
+ return 0;
+
+cleanup_nand:
+ nand_cleanup(this);
+escan:
+ if (host->clk_act)
+ clk_disable_unprepare(host->clk);
+
+ return err;
+}
+
+static int mxcnd_remove(struct platform_device *pdev)
+{
+ struct mxc_nand_host *host = platform_get_drvdata(pdev);
+
+ nand_release(nand_to_mtd(&host->nand));
+ if (host->clk_act)
+ clk_disable_unprepare(host->clk);
+
+ return 0;
+}
+
+static struct platform_driver mxcnd_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = of_match_ptr(mxcnd_dt_ids),
+ },
+ .id_table = mxcnd_devtype,
+ .probe = mxcnd_probe,
+ .remove = mxcnd_remove,
+};
+module_platform_driver(mxcnd_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC NAND MTD driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/nand/nand_amd.c b/drivers/mtd/nand/raw/nand_amd.c
index 22f060f38123..22f060f38123 100644
--- a/drivers/mtd/nand/nand_amd.c
+++ b/drivers/mtd/nand/raw/nand_amd.c
diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
new file mode 100644
index 000000000000..72f3a89da513
--- /dev/null
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -0,0 +1,6693 @@
+/*
+ * Overview:
+ * This is the generic MTD driver for NAND flash devices. It should be
+ * capable of working with almost all NAND chips currently available.
+ *
+ * Additional technical information is available on
+ * http://www.linux-mtd.infradead.org/doc/nand.html
+ *
+ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
+ * 2002-2006 Thomas Gleixner (tglx@linutronix.de)
+ *
+ * Credits:
+ * David Woodhouse for adding multichip support
+ *
+ * Aleph One Ltd. and Toby Churchill Ltd. for supporting the
+ * rework for 2K page size chips
+ *
+ * TODO:
+ * Enable cached programming for 2k page size chips
+ * Check, if mtd->ecctype should be set to MTD_ECC_HW
+ * if we have HW ECC support.
+ * BBT table is not serialized, has to be fixed
+ *
+ * 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.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/nmi.h>
+#include <linux/types.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/nand_bch.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/mtd/partitions.h>
+#include <linux/of.h>
+
+static int nand_get_device(struct mtd_info *mtd, int new_state);
+
+static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops);
+
+/* Define default oob placement schemes for large and small page devices */
+static int nand_ooblayout_ecc_sp(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+
+ if (section > 1)
+ return -ERANGE;
+
+ if (!section) {
+ oobregion->offset = 0;
+ if (mtd->oobsize == 16)
+ oobregion->length = 4;
+ else
+ oobregion->length = 3;
+ } else {
+ if (mtd->oobsize == 8)
+ return -ERANGE;
+
+ oobregion->offset = 6;
+ oobregion->length = ecc->total - 4;
+ }
+
+ return 0;
+}
+
+static int nand_ooblayout_free_sp(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ if (section > 1)
+ return -ERANGE;
+
+ if (mtd->oobsize == 16) {
+ if (section)
+ return -ERANGE;
+
+ oobregion->length = 8;
+ oobregion->offset = 8;
+ } else {
+ oobregion->length = 2;
+ if (!section)
+ oobregion->offset = 3;
+ else
+ oobregion->offset = 6;
+ }
+
+ return 0;
+}
+
+const struct mtd_ooblayout_ops nand_ooblayout_sp_ops = {
+ .ecc = nand_ooblayout_ecc_sp,
+ .free = nand_ooblayout_free_sp,
+};
+EXPORT_SYMBOL_GPL(nand_ooblayout_sp_ops);
+
+static int nand_ooblayout_ecc_lp(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+
+ if (section || !ecc->total)
+ return -ERANGE;
+
+ oobregion->length = ecc->total;
+ oobregion->offset = mtd->oobsize - oobregion->length;
+
+ return 0;
+}
+
+static int nand_ooblayout_free_lp(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+
+ if (section)
+ return -ERANGE;
+
+ oobregion->length = mtd->oobsize - ecc->total - 2;
+ oobregion->offset = 2;
+
+ return 0;
+}
+
+const struct mtd_ooblayout_ops nand_ooblayout_lp_ops = {
+ .ecc = nand_ooblayout_ecc_lp,
+ .free = nand_ooblayout_free_lp,
+};
+EXPORT_SYMBOL_GPL(nand_ooblayout_lp_ops);
+
+/*
+ * Support the old "large page" layout used for 1-bit Hamming ECC where ECC
+ * are placed at a fixed offset.
+ */
+static int nand_ooblayout_ecc_lp_hamming(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+
+ if (section)
+ return -ERANGE;
+
+ switch (mtd->oobsize) {
+ case 64:
+ oobregion->offset = 40;
+ break;
+ case 128:
+ oobregion->offset = 80;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ oobregion->length = ecc->total;
+ if (oobregion->offset + oobregion->length > mtd->oobsize)
+ return -ERANGE;
+
+ return 0;
+}
+
+static int nand_ooblayout_free_lp_hamming(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ int ecc_offset = 0;
+
+ if (section < 0 || section > 1)
+ return -ERANGE;
+
+ switch (mtd->oobsize) {
+ case 64:
+ ecc_offset = 40;
+ break;
+ case 128:
+ ecc_offset = 80;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (section == 0) {
+ oobregion->offset = 2;
+ oobregion->length = ecc_offset - 2;
+ } else {
+ oobregion->offset = ecc_offset + ecc->total;
+ oobregion->length = mtd->oobsize - oobregion->offset;
+ }
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops nand_ooblayout_lp_hamming_ops = {
+ .ecc = nand_ooblayout_ecc_lp_hamming,
+ .free = nand_ooblayout_free_lp_hamming,
+};
+
+static int check_offs_len(struct mtd_info *mtd,
+ loff_t ofs, uint64_t len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int ret = 0;
+
+ /* Start address must align on block boundary */
+ if (ofs & ((1ULL << chip->phys_erase_shift) - 1)) {
+ pr_debug("%s: unaligned address\n", __func__);
+ ret = -EINVAL;
+ }
+
+ /* Length must align on block boundary */
+ if (len & ((1ULL << chip->phys_erase_shift) - 1)) {
+ pr_debug("%s: length not block aligned\n", __func__);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+/**
+ * nand_release_device - [GENERIC] release chip
+ * @mtd: MTD device structure
+ *
+ * Release chip lock and wake up anyone waiting on the device.
+ */
+static void nand_release_device(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ /* Release the controller and the chip */
+ spin_lock(&chip->controller->lock);
+ chip->controller->active = NULL;
+ chip->state = FL_READY;
+ wake_up(&chip->controller->wq);
+ spin_unlock(&chip->controller->lock);
+}
+
+/**
+ * nand_read_byte - [DEFAULT] read one byte from the chip
+ * @mtd: MTD device structure
+ *
+ * Default read function for 8bit buswidth
+ */
+static uint8_t nand_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ return readb(chip->IO_ADDR_R);
+}
+
+/**
+ * nand_read_byte16 - [DEFAULT] read one byte endianness aware from the chip
+ * @mtd: MTD device structure
+ *
+ * Default read function for 16bit buswidth with endianness conversion.
+ *
+ */
+static uint8_t nand_read_byte16(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ return (uint8_t) cpu_to_le16(readw(chip->IO_ADDR_R));
+}
+
+/**
+ * nand_read_word - [DEFAULT] read one word from the chip
+ * @mtd: MTD device structure
+ *
+ * Default read function for 16bit buswidth without endianness conversion.
+ */
+static u16 nand_read_word(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ return readw(chip->IO_ADDR_R);
+}
+
+/**
+ * nand_select_chip - [DEFAULT] control CE line
+ * @mtd: MTD device structure
+ * @chipnr: chipnumber to select, -1 for deselect
+ *
+ * Default select function for 1 chip devices.
+ */
+static void nand_select_chip(struct mtd_info *mtd, int chipnr)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ switch (chipnr) {
+ case -1:
+ chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
+ break;
+ case 0:
+ break;
+
+ default:
+ BUG();
+ }
+}
+
+/**
+ * nand_write_byte - [DEFAULT] write single byte to chip
+ * @mtd: MTD device structure
+ * @byte: value to write
+ *
+ * Default function to write a byte to I/O[7:0]
+ */
+static void nand_write_byte(struct mtd_info *mtd, uint8_t byte)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ chip->write_buf(mtd, &byte, 1);
+}
+
+/**
+ * nand_write_byte16 - [DEFAULT] write single byte to a chip with width 16
+ * @mtd: MTD device structure
+ * @byte: value to write
+ *
+ * Default function to write a byte to I/O[7:0] on a 16-bit wide chip.
+ */
+static void nand_write_byte16(struct mtd_info *mtd, uint8_t byte)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ uint16_t word = byte;
+
+ /*
+ * It's not entirely clear what should happen to I/O[15:8] when writing
+ * a byte. The ONFi spec (Revision 3.1; 2012-09-19, Section 2.16) reads:
+ *
+ * When the host supports a 16-bit bus width, only data is
+ * transferred at the 16-bit width. All address and command line
+ * transfers shall use only the lower 8-bits of the data bus. During
+ * command transfers, the host may place any value on the upper
+ * 8-bits of the data bus. During address transfers, the host shall
+ * set the upper 8-bits of the data bus to 00h.
+ *
+ * One user of the write_byte callback is nand_set_features. The
+ * four parameters are specified to be written to I/O[7:0], but this is
+ * neither an address nor a command transfer. Let's assume a 0 on the
+ * upper I/O lines is OK.
+ */
+ chip->write_buf(mtd, (uint8_t *)&word, 2);
+}
+
+/**
+ * nand_write_buf - [DEFAULT] write buffer to chip
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ *
+ * Default write function for 8bit buswidth.
+ */
+static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ iowrite8_rep(chip->IO_ADDR_W, buf, len);
+}
+
+/**
+ * nand_read_buf - [DEFAULT] read chip data into buffer
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
+ *
+ * Default read function for 8bit buswidth.
+ */
+static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ ioread8_rep(chip->IO_ADDR_R, buf, len);
+}
+
+/**
+ * nand_write_buf16 - [DEFAULT] write buffer to chip
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ *
+ * Default write function for 16bit buswidth.
+ */
+static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ u16 *p = (u16 *) buf;
+
+ iowrite16_rep(chip->IO_ADDR_W, p, len >> 1);
+}
+
+/**
+ * nand_read_buf16 - [DEFAULT] read chip data into buffer
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
+ *
+ * Default read function for 16bit buswidth.
+ */
+static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ u16 *p = (u16 *) buf;
+
+ ioread16_rep(chip->IO_ADDR_R, p, len >> 1);
+}
+
+/**
+ * nand_block_bad - [DEFAULT] Read bad block marker from the chip
+ * @mtd: MTD device structure
+ * @ofs: offset from device start
+ *
+ * Check, if the block is bad.
+ */
+static int nand_block_bad(struct mtd_info *mtd, loff_t ofs)
+{
+ int page, page_end, res;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ u8 bad;
+
+ if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
+ ofs += mtd->erasesize - mtd->writesize;
+
+ page = (int)(ofs >> chip->page_shift) & chip->pagemask;
+ page_end = page + (chip->bbt_options & NAND_BBT_SCAN2NDPAGE ? 2 : 1);
+
+ for (; page < page_end; page++) {
+ res = chip->ecc.read_oob(mtd, chip, page);
+ if (res)
+ return res;
+
+ bad = chip->oob_poi[chip->badblockpos];
+
+ if (likely(chip->badblockbits == 8))
+ res = bad != 0xFF;
+ else
+ res = hweight8(bad) < chip->badblockbits;
+ if (res)
+ return res;
+ }
+
+ return 0;
+}
+
+/**
+ * nand_default_block_markbad - [DEFAULT] mark a block bad via bad block marker
+ * @mtd: MTD device structure
+ * @ofs: offset from device start
+ *
+ * This is the default implementation, which can be overridden by a hardware
+ * specific driver. It provides the details for writing a bad block marker to a
+ * block.
+ */
+static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mtd_oob_ops ops;
+ uint8_t buf[2] = { 0, 0 };
+ int ret = 0, res, i = 0;
+
+ memset(&ops, 0, sizeof(ops));
+ ops.oobbuf = buf;
+ ops.ooboffs = chip->badblockpos;
+ if (chip->options & NAND_BUSWIDTH_16) {
+ ops.ooboffs &= ~0x01;
+ ops.len = ops.ooblen = 2;
+ } else {
+ ops.len = ops.ooblen = 1;
+ }
+ ops.mode = MTD_OPS_PLACE_OOB;
+
+ /* Write to first/last page(s) if necessary */
+ if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
+ ofs += mtd->erasesize - mtd->writesize;
+ do {
+ res = nand_do_write_oob(mtd, ofs, &ops);
+ if (!ret)
+ ret = res;
+
+ i++;
+ ofs += mtd->writesize;
+ } while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
+
+ return ret;
+}
+
+/**
+ * nand_block_markbad_lowlevel - mark a block bad
+ * @mtd: MTD device structure
+ * @ofs: offset from device start
+ *
+ * This function performs the generic NAND bad block marking steps (i.e., bad
+ * block table(s) and/or marker(s)). We only allow the hardware driver to
+ * specify how to write bad block markers to OOB (chip->block_markbad).
+ *
+ * We try operations in the following order:
+ *
+ * (1) erase the affected block, to allow OOB marker to be written cleanly
+ * (2) write bad block marker to OOB area of affected block (unless flag
+ * NAND_BBT_NO_OOB_BBM is present)
+ * (3) update the BBT
+ *
+ * Note that we retain the first error encountered in (2) or (3), finish the
+ * procedures, and dump the error in the end.
+*/
+static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int res, ret = 0;
+
+ if (!(chip->bbt_options & NAND_BBT_NO_OOB_BBM)) {
+ struct erase_info einfo;
+
+ /* Attempt erase before marking OOB */
+ memset(&einfo, 0, sizeof(einfo));
+ einfo.addr = ofs;
+ einfo.len = 1ULL << chip->phys_erase_shift;
+ nand_erase_nand(mtd, &einfo, 0);
+
+ /* Write bad block marker to OOB */
+ nand_get_device(mtd, FL_WRITING);
+ ret = chip->block_markbad(mtd, ofs);
+ nand_release_device(mtd);
+ }
+
+ /* Mark block bad in BBT */
+ if (chip->bbt) {
+ res = nand_markbad_bbt(mtd, ofs);
+ if (!ret)
+ ret = res;
+ }
+
+ if (!ret)
+ mtd->ecc_stats.badblocks++;
+
+ return ret;
+}
+
+/**
+ * nand_check_wp - [GENERIC] check if the chip is write protected
+ * @mtd: MTD device structure
+ *
+ * Check, if the device is write protected. The function expects, that the
+ * device is already selected.
+ */
+static int nand_check_wp(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ u8 status;
+ int ret;
+
+ /* Broken xD cards report WP despite being writable */
+ if (chip->options & NAND_BROKEN_XD)
+ return 0;
+
+ /* Check the WP bit */
+ ret = nand_status_op(chip, &status);
+ if (ret)
+ return ret;
+
+ return status & NAND_STATUS_WP ? 0 : 1;
+}
+
+/**
+ * nand_block_isreserved - [GENERIC] Check if a block is marked reserved.
+ * @mtd: MTD device structure
+ * @ofs: offset from device start
+ *
+ * Check if the block is marked as reserved.
+ */
+static int nand_block_isreserved(struct mtd_info *mtd, loff_t ofs)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (!chip->bbt)
+ return 0;
+ /* Return info from the table */
+ return nand_isreserved_bbt(mtd, ofs);
+}
+
+/**
+ * nand_block_checkbad - [GENERIC] Check if a block is marked bad
+ * @mtd: MTD device structure
+ * @ofs: offset from device start
+ * @allowbbt: 1, if its allowed to access the bbt area
+ *
+ * Check, if the block is bad. Either by reading the bad block table or
+ * calling of the scan function.
+ */
+static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int allowbbt)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (!chip->bbt)
+ return chip->block_bad(mtd, ofs);
+
+ /* Return info from the table */
+ return nand_isbad_bbt(mtd, ofs, allowbbt);
+}
+
+/**
+ * panic_nand_wait_ready - [GENERIC] Wait for the ready pin after commands.
+ * @mtd: MTD device structure
+ * @timeo: Timeout
+ *
+ * Helper function for nand_wait_ready used when needing to wait in interrupt
+ * context.
+ */
+static void panic_nand_wait_ready(struct mtd_info *mtd, unsigned long timeo)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int i;
+
+ /* Wait for the device to get ready */
+ for (i = 0; i < timeo; i++) {
+ if (chip->dev_ready(mtd))
+ break;
+ touch_softlockup_watchdog();
+ mdelay(1);
+ }
+}
+
+/**
+ * nand_wait_ready - [GENERIC] Wait for the ready pin after commands.
+ * @mtd: MTD device structure
+ *
+ * Wait for the ready pin after a command, and warn if a timeout occurs.
+ */
+void nand_wait_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ unsigned long timeo = 400;
+
+ if (in_interrupt() || oops_in_progress)
+ return panic_nand_wait_ready(mtd, timeo);
+
+ /* Wait until command is processed or timeout occurs */
+ timeo = jiffies + msecs_to_jiffies(timeo);
+ do {
+ if (chip->dev_ready(mtd))
+ return;
+ cond_resched();
+ } while (time_before(jiffies, timeo));
+
+ if (!chip->dev_ready(mtd))
+ pr_warn_ratelimited("timeout while waiting for chip to become ready\n");
+}
+EXPORT_SYMBOL_GPL(nand_wait_ready);
+
+/**
+ * nand_wait_status_ready - [GENERIC] Wait for the ready status after commands.
+ * @mtd: MTD device structure
+ * @timeo: Timeout in ms
+ *
+ * Wait for status ready (i.e. command done) or timeout.
+ */
+static void nand_wait_status_ready(struct mtd_info *mtd, unsigned long timeo)
+{
+ register struct nand_chip *chip = mtd_to_nand(mtd);
+ int ret;
+
+ timeo = jiffies + msecs_to_jiffies(timeo);
+ do {
+ u8 status;
+
+ ret = nand_read_data_op(chip, &status, sizeof(status), true);
+ if (ret)
+ return;
+
+ if (status & NAND_STATUS_READY)
+ break;
+ touch_softlockup_watchdog();
+ } while (time_before(jiffies, timeo));
+};
+
+/**
+ * nand_soft_waitrdy - Poll STATUS reg until RDY bit is set to 1
+ * @chip: NAND chip structure
+ * @timeout_ms: Timeout in ms
+ *
+ * Poll the STATUS register using ->exec_op() until the RDY bit becomes 1.
+ * If that does not happen whitin the specified timeout, -ETIMEDOUT is
+ * returned.
+ *
+ * This helper is intended to be used when the controller does not have access
+ * to the NAND R/B pin.
+ *
+ * Be aware that calling this helper from an ->exec_op() implementation means
+ * ->exec_op() must be re-entrant.
+ *
+ * Return 0 if the NAND chip is ready, a negative error otherwise.
+ */
+int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms)
+{
+ u8 status = 0;
+ int ret;
+
+ if (!chip->exec_op)
+ return -ENOTSUPP;
+
+ ret = nand_status_op(chip, NULL);
+ if (ret)
+ return ret;
+
+ timeout_ms = jiffies + msecs_to_jiffies(timeout_ms);
+ do {
+ ret = nand_read_data_op(chip, &status, sizeof(status), true);
+ if (ret)
+ break;
+
+ if (status & NAND_STATUS_READY)
+ break;
+
+ /*
+ * Typical lowest execution time for a tR on most NANDs is 10us,
+ * use this as polling delay before doing something smarter (ie.
+ * deriving a delay from the timeout value, timeout_ms/ratio).
+ */
+ udelay(10);
+ } while (time_before(jiffies, timeout_ms));
+
+ /*
+ * We have to exit READ_STATUS mode in order to read real data on the
+ * bus in case the WAITRDY instruction is preceding a DATA_IN
+ * instruction.
+ */
+ nand_exit_status_op(chip);
+
+ if (ret)
+ return ret;
+
+ return status & NAND_STATUS_READY ? 0 : -ETIMEDOUT;
+};
+EXPORT_SYMBOL_GPL(nand_soft_waitrdy);
+
+/**
+ * nand_command - [DEFAULT] Send command to NAND device
+ * @mtd: MTD device structure
+ * @command: the command to be sent
+ * @column: the column address for this command, -1 if none
+ * @page_addr: the page address for this command, -1 if none
+ *
+ * Send command to NAND device. This function is used for small page devices
+ * (512 Bytes per page).
+ */
+static void nand_command(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+{
+ register struct nand_chip *chip = mtd_to_nand(mtd);
+ int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE;
+
+ /* Write out the command to the device */
+ if (command == NAND_CMD_SEQIN) {
+ int readcmd;
+
+ if (column >= mtd->writesize) {
+ /* OOB area */
+ column -= mtd->writesize;
+ readcmd = NAND_CMD_READOOB;
+ } else if (column < 256) {
+ /* First 256 bytes --> READ0 */
+ readcmd = NAND_CMD_READ0;
+ } else {
+ column -= 256;
+ readcmd = NAND_CMD_READ1;
+ }
+ chip->cmd_ctrl(mtd, readcmd, ctrl);
+ ctrl &= ~NAND_CTRL_CHANGE;
+ }
+ if (command != NAND_CMD_NONE)
+ chip->cmd_ctrl(mtd, command, ctrl);
+
+ /* Address cycle, when necessary */
+ ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE;
+ /* Serially input address */
+ if (column != -1) {
+ /* Adjust columns for 16 bit buswidth */
+ if (chip->options & NAND_BUSWIDTH_16 &&
+ !nand_opcode_8bits(command))
+ column >>= 1;
+ chip->cmd_ctrl(mtd, column, ctrl);
+ ctrl &= ~NAND_CTRL_CHANGE;
+ }
+ if (page_addr != -1) {
+ chip->cmd_ctrl(mtd, page_addr, ctrl);
+ ctrl &= ~NAND_CTRL_CHANGE;
+ chip->cmd_ctrl(mtd, page_addr >> 8, ctrl);
+ if (chip->options & NAND_ROW_ADDR_3)
+ chip->cmd_ctrl(mtd, page_addr >> 16, ctrl);
+ }
+ chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+
+ /*
+ * Program and erase have their own busy handlers status and sequential
+ * in needs no delay
+ */
+ switch (command) {
+
+ case NAND_CMD_NONE:
+ case NAND_CMD_PAGEPROG:
+ case NAND_CMD_ERASE1:
+ case NAND_CMD_ERASE2:
+ case NAND_CMD_SEQIN:
+ case NAND_CMD_STATUS:
+ case NAND_CMD_READID:
+ case NAND_CMD_SET_FEATURES:
+ return;
+
+ case NAND_CMD_RESET:
+ if (chip->dev_ready)
+ break;
+ udelay(chip->chip_delay);
+ chip->cmd_ctrl(mtd, NAND_CMD_STATUS,
+ NAND_CTRL_CLE | NAND_CTRL_CHANGE);
+ chip->cmd_ctrl(mtd,
+ NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+ /* EZ-NAND can take upto 250ms as per ONFi v4.0 */
+ nand_wait_status_ready(mtd, 250);
+ return;
+
+ /* This applies to read commands */
+ case NAND_CMD_READ0:
+ /*
+ * READ0 is sometimes used to exit GET STATUS mode. When this
+ * is the case no address cycles are requested, and we can use
+ * this information to detect that we should not wait for the
+ * device to be ready.
+ */
+ if (column == -1 && page_addr == -1)
+ return;
+
+ default:
+ /*
+ * If we don't have access to the busy pin, we apply the given
+ * command delay
+ */
+ if (!chip->dev_ready) {
+ udelay(chip->chip_delay);
+ return;
+ }
+ }
+ /*
+ * Apply this short delay always to ensure that we do wait tWB in
+ * any case on any machine.
+ */
+ ndelay(100);
+
+ nand_wait_ready(mtd);
+}
+
+static void nand_ccs_delay(struct nand_chip *chip)
+{
+ /*
+ * The controller already takes care of waiting for tCCS when the RNDIN
+ * or RNDOUT command is sent, return directly.
+ */
+ if (!(chip->options & NAND_WAIT_TCCS))
+ return;
+
+ /*
+ * Wait tCCS_min if it is correctly defined, otherwise wait 500ns
+ * (which should be safe for all NANDs).
+ */
+ if (chip->setup_data_interface)
+ ndelay(chip->data_interface.timings.sdr.tCCS_min / 1000);
+ else
+ ndelay(500);
+}
+
+/**
+ * nand_command_lp - [DEFAULT] Send command to NAND large page device
+ * @mtd: MTD device structure
+ * @command: the command to be sent
+ * @column: the column address for this command, -1 if none
+ * @page_addr: the page address for this command, -1 if none
+ *
+ * Send command to NAND device. This is the version for the new large page
+ * devices. We don't have the separate regions as we have in the small page
+ * devices. We must emulate NAND_CMD_READOOB to keep the code compatible.
+ */
+static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+{
+ register struct nand_chip *chip = mtd_to_nand(mtd);
+
+ /* Emulate NAND_CMD_READOOB */
+ if (command == NAND_CMD_READOOB) {
+ column += mtd->writesize;
+ command = NAND_CMD_READ0;
+ }
+
+ /* Command latch cycle */
+ if (command != NAND_CMD_NONE)
+ chip->cmd_ctrl(mtd, command,
+ NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+
+ if (column != -1 || page_addr != -1) {
+ int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
+
+ /* Serially input address */
+ if (column != -1) {
+ /* Adjust columns for 16 bit buswidth */
+ if (chip->options & NAND_BUSWIDTH_16 &&
+ !nand_opcode_8bits(command))
+ column >>= 1;
+ chip->cmd_ctrl(mtd, column, ctrl);
+ ctrl &= ~NAND_CTRL_CHANGE;
+
+ /* Only output a single addr cycle for 8bits opcodes. */
+ if (!nand_opcode_8bits(command))
+ chip->cmd_ctrl(mtd, column >> 8, ctrl);
+ }
+ if (page_addr != -1) {
+ chip->cmd_ctrl(mtd, page_addr, ctrl);
+ chip->cmd_ctrl(mtd, page_addr >> 8,
+ NAND_NCE | NAND_ALE);
+ if (chip->options & NAND_ROW_ADDR_3)
+ chip->cmd_ctrl(mtd, page_addr >> 16,
+ NAND_NCE | NAND_ALE);
+ }
+ }
+ chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+
+ /*
+ * Program and erase have their own busy handlers status, sequential
+ * in and status need no delay.
+ */
+ switch (command) {
+
+ case NAND_CMD_NONE:
+ case NAND_CMD_CACHEDPROG:
+ case NAND_CMD_PAGEPROG:
+ case NAND_CMD_ERASE1:
+ case NAND_CMD_ERASE2:
+ case NAND_CMD_SEQIN:
+ case NAND_CMD_STATUS:
+ case NAND_CMD_READID:
+ case NAND_CMD_SET_FEATURES:
+ return;
+
+ case NAND_CMD_RNDIN:
+ nand_ccs_delay(chip);
+ return;
+
+ case NAND_CMD_RESET:
+ if (chip->dev_ready)
+ break;
+ udelay(chip->chip_delay);
+ chip->cmd_ctrl(mtd, NAND_CMD_STATUS,
+ NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+ chip->cmd_ctrl(mtd, NAND_CMD_NONE,
+ NAND_NCE | NAND_CTRL_CHANGE);
+ /* EZ-NAND can take upto 250ms as per ONFi v4.0 */
+ nand_wait_status_ready(mtd, 250);
+ return;
+
+ case NAND_CMD_RNDOUT:
+ /* No ready / busy check necessary */
+ chip->cmd_ctrl(mtd, NAND_CMD_RNDOUTSTART,
+ NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+ chip->cmd_ctrl(mtd, NAND_CMD_NONE,
+ NAND_NCE | NAND_CTRL_CHANGE);
+
+ nand_ccs_delay(chip);
+ return;
+
+ case NAND_CMD_READ0:
+ /*
+ * READ0 is sometimes used to exit GET STATUS mode. When this
+ * is the case no address cycles are requested, and we can use
+ * this information to detect that READSTART should not be
+ * issued.
+ */
+ if (column == -1 && page_addr == -1)
+ return;
+
+ chip->cmd_ctrl(mtd, NAND_CMD_READSTART,
+ NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+ chip->cmd_ctrl(mtd, NAND_CMD_NONE,
+ NAND_NCE | NAND_CTRL_CHANGE);
+
+ /* This applies to read commands */
+ default:
+ /*
+ * If we don't have access to the busy pin, we apply the given
+ * command delay.
+ */
+ if (!chip->dev_ready) {
+ udelay(chip->chip_delay);
+ return;
+ }
+ }
+
+ /*
+ * Apply this short delay always to ensure that we do wait tWB in
+ * any case on any machine.
+ */
+ ndelay(100);
+
+ nand_wait_ready(mtd);
+}
+
+/**
+ * panic_nand_get_device - [GENERIC] Get chip for selected access
+ * @chip: the nand chip descriptor
+ * @mtd: MTD device structure
+ * @new_state: the state which is requested
+ *
+ * Used when in panic, no locks are taken.
+ */
+static void panic_nand_get_device(struct nand_chip *chip,
+ struct mtd_info *mtd, int new_state)
+{
+ /* Hardware controller shared among independent devices */
+ chip->controller->active = chip;
+ chip->state = new_state;
+}
+
+/**
+ * nand_get_device - [GENERIC] Get chip for selected access
+ * @mtd: MTD device structure
+ * @new_state: the state which is requested
+ *
+ * Get the device and lock it for exclusive access
+ */
+static int
+nand_get_device(struct mtd_info *mtd, int new_state)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ spinlock_t *lock = &chip->controller->lock;
+ wait_queue_head_t *wq = &chip->controller->wq;
+ DECLARE_WAITQUEUE(wait, current);
+retry:
+ spin_lock(lock);
+
+ /* Hardware controller shared among independent devices */
+ if (!chip->controller->active)
+ chip->controller->active = chip;
+
+ if (chip->controller->active == chip && chip->state == FL_READY) {
+ chip->state = new_state;
+ spin_unlock(lock);
+ return 0;
+ }
+ if (new_state == FL_PM_SUSPENDED) {
+ if (chip->controller->active->state == FL_PM_SUSPENDED) {
+ chip->state = FL_PM_SUSPENDED;
+ spin_unlock(lock);
+ return 0;
+ }
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ add_wait_queue(wq, &wait);
+ spin_unlock(lock);
+ schedule();
+ remove_wait_queue(wq, &wait);
+ goto retry;
+}
+
+/**
+ * panic_nand_wait - [GENERIC] wait until the command is done
+ * @mtd: MTD device structure
+ * @chip: NAND chip structure
+ * @timeo: timeout
+ *
+ * Wait for command done. This is a helper function for nand_wait used when
+ * we are in interrupt context. May happen when in panic and trying to write
+ * an oops through mtdoops.
+ */
+static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip,
+ unsigned long timeo)
+{
+ int i;
+ for (i = 0; i < timeo; i++) {
+ if (chip->dev_ready) {
+ if (chip->dev_ready(mtd))
+ break;
+ } else {
+ int ret;
+ u8 status;
+
+ ret = nand_read_data_op(chip, &status, sizeof(status),
+ true);
+ if (ret)
+ return;
+
+ if (status & NAND_STATUS_READY)
+ break;
+ }
+ mdelay(1);
+ }
+}
+
+/**
+ * nand_wait - [DEFAULT] wait until the command is done
+ * @mtd: MTD device structure
+ * @chip: NAND chip structure
+ *
+ * Wait for command done. This applies to erase and program only.
+ */
+static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
+{
+
+ unsigned long timeo = 400;
+ u8 status;
+ int ret;
+
+ /*
+ * Apply this short delay always to ensure that we do wait tWB in any
+ * case on any machine.
+ */
+ ndelay(100);
+
+ ret = nand_status_op(chip, NULL);
+ if (ret)
+ return ret;
+
+ if (in_interrupt() || oops_in_progress)
+ panic_nand_wait(mtd, chip, timeo);
+ else {
+ timeo = jiffies + msecs_to_jiffies(timeo);
+ do {
+ if (chip->dev_ready) {
+ if (chip->dev_ready(mtd))
+ break;
+ } else {
+ ret = nand_read_data_op(chip, &status,
+ sizeof(status), true);
+ if (ret)
+ return ret;
+
+ if (status & NAND_STATUS_READY)
+ break;
+ }
+ cond_resched();
+ } while (time_before(jiffies, timeo));
+ }
+
+ ret = nand_read_data_op(chip, &status, sizeof(status), true);
+ if (ret)
+ return ret;
+
+ /* This can happen if in case of timeout or buggy dev_ready */
+ WARN_ON(!(status & NAND_STATUS_READY));
+ return status;
+}
+
+static bool nand_supports_get_features(struct nand_chip *chip, int addr)
+{
+ return (chip->parameters.supports_set_get_features &&
+ test_bit(addr, chip->parameters.get_feature_list));
+}
+
+static bool nand_supports_set_features(struct nand_chip *chip, int addr)
+{
+ return (chip->parameters.supports_set_get_features &&
+ test_bit(addr, chip->parameters.set_feature_list));
+}
+
+/**
+ * nand_get_features - wrapper to perform a GET_FEATURE
+ * @chip: NAND chip info structure
+ * @addr: feature address
+ * @subfeature_param: the subfeature parameters, a four bytes array
+ *
+ * Returns 0 for success, a negative error otherwise. Returns -ENOTSUPP if the
+ * operation cannot be handled.
+ */
+int nand_get_features(struct nand_chip *chip, int addr,
+ u8 *subfeature_param)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ if (!nand_supports_get_features(chip, addr))
+ return -ENOTSUPP;
+
+ return chip->get_features(mtd, chip, addr, subfeature_param);
+}
+EXPORT_SYMBOL_GPL(nand_get_features);
+
+/**
+ * nand_set_features - wrapper to perform a SET_FEATURE
+ * @chip: NAND chip info structure
+ * @addr: feature address
+ * @subfeature_param: the subfeature parameters, a four bytes array
+ *
+ * Returns 0 for success, a negative error otherwise. Returns -ENOTSUPP if the
+ * operation cannot be handled.
+ */
+int nand_set_features(struct nand_chip *chip, int addr,
+ u8 *subfeature_param)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ if (!nand_supports_set_features(chip, addr))
+ return -ENOTSUPP;
+
+ return chip->set_features(mtd, chip, addr, subfeature_param);
+}
+EXPORT_SYMBOL_GPL(nand_set_features);
+
+/**
+ * nand_reset_data_interface - Reset data interface and timings
+ * @chip: The NAND chip
+ * @chipnr: Internal die id
+ *
+ * Reset the Data interface and timings to ONFI mode 0.
+ *
+ * Returns 0 for success or negative error code otherwise.
+ */
+static int nand_reset_data_interface(struct nand_chip *chip, int chipnr)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int ret;
+
+ if (!chip->setup_data_interface)
+ return 0;
+
+ /*
+ * The ONFI specification says:
+ * "
+ * To transition from NV-DDR or NV-DDR2 to the SDR data
+ * interface, the host shall use the Reset (FFh) command
+ * using SDR timing mode 0. A device in any timing mode is
+ * required to recognize Reset (FFh) command issued in SDR
+ * timing mode 0.
+ * "
+ *
+ * Configure the data interface in SDR mode and set the
+ * timings to timing mode 0.
+ */
+
+ onfi_fill_data_interface(chip, NAND_SDR_IFACE, 0);
+ ret = chip->setup_data_interface(mtd, chipnr, &chip->data_interface);
+ if (ret)
+ pr_err("Failed to configure data interface to SDR timing mode 0\n");
+
+ return ret;
+}
+
+/**
+ * nand_setup_data_interface - Setup the best data interface and timings
+ * @chip: The NAND chip
+ * @chipnr: Internal die id
+ *
+ * Find and configure the best data interface and NAND timings supported by
+ * the chip and the driver.
+ * First tries to retrieve supported timing modes from ONFI information,
+ * and if the NAND chip does not support ONFI, relies on the
+ * ->onfi_timing_mode_default specified in the nand_ids table.
+ *
+ * Returns 0 for success or negative error code otherwise.
+ */
+static int nand_setup_data_interface(struct nand_chip *chip, int chipnr)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = {
+ chip->onfi_timing_mode_default,
+ };
+ int ret;
+
+ if (!chip->setup_data_interface)
+ return 0;
+
+ /* Change the mode on the chip side (if supported by the NAND chip) */
+ if (nand_supports_set_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE)) {
+ chip->select_chip(mtd, chipnr);
+ ret = nand_set_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE,
+ tmode_param);
+ chip->select_chip(mtd, -1);
+ if (ret)
+ return ret;
+ }
+
+ /* Change the mode on the controller side */
+ ret = chip->setup_data_interface(mtd, chipnr, &chip->data_interface);
+ if (ret)
+ return ret;
+
+ /* Check the mode has been accepted by the chip, if supported */
+ if (!nand_supports_get_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE))
+ return 0;
+
+ memset(tmode_param, 0, ONFI_SUBFEATURE_PARAM_LEN);
+ chip->select_chip(mtd, chipnr);
+ ret = nand_get_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE,
+ tmode_param);
+ chip->select_chip(mtd, -1);
+ if (ret)
+ goto err_reset_chip;
+
+ if (tmode_param[0] != chip->onfi_timing_mode_default) {
+ pr_warn("timing mode %d not acknowledged by the NAND chip\n",
+ chip->onfi_timing_mode_default);
+ goto err_reset_chip;
+ }
+
+ return 0;
+
+err_reset_chip:
+ /*
+ * Fallback to mode 0 if the chip explicitly did not ack the chosen
+ * timing mode.
+ */
+ nand_reset_data_interface(chip, chipnr);
+ chip->select_chip(mtd, chipnr);
+ nand_reset_op(chip);
+ chip->select_chip(mtd, -1);
+
+ return ret;
+}
+
+/**
+ * nand_init_data_interface - find the best data interface and timings
+ * @chip: The NAND chip
+ *
+ * Find the best data interface and NAND timings supported by the chip
+ * and the driver.
+ * First tries to retrieve supported timing modes from ONFI information,
+ * and if the NAND chip does not support ONFI, relies on the
+ * ->onfi_timing_mode_default specified in the nand_ids table. After this
+ * function nand_chip->data_interface is initialized with the best timing mode
+ * available.
+ *
+ * Returns 0 for success or negative error code otherwise.
+ */
+static int nand_init_data_interface(struct nand_chip *chip)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int modes, mode, ret;
+
+ if (!chip->setup_data_interface)
+ return 0;
+
+ /*
+ * First try to identify the best timings from ONFI parameters and
+ * if the NAND does not support ONFI, fallback to the default ONFI
+ * timing mode.
+ */
+ modes = onfi_get_async_timing_mode(chip);
+ if (modes == ONFI_TIMING_MODE_UNKNOWN) {
+ if (!chip->onfi_timing_mode_default)
+ return 0;
+
+ modes = GENMASK(chip->onfi_timing_mode_default, 0);
+ }
+
+
+ for (mode = fls(modes) - 1; mode >= 0; mode--) {
+ ret = onfi_fill_data_interface(chip, NAND_SDR_IFACE, mode);
+ if (ret)
+ continue;
+
+ /*
+ * Pass NAND_DATA_IFACE_CHECK_ONLY to only check if the
+ * controller supports the requested timings.
+ */
+ ret = chip->setup_data_interface(mtd,
+ NAND_DATA_IFACE_CHECK_ONLY,
+ &chip->data_interface);
+ if (!ret) {
+ chip->onfi_timing_mode_default = mode;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * nand_fill_column_cycles - fill the column cycles of an address
+ * @chip: The NAND chip
+ * @addrs: Array of address cycles to fill
+ * @offset_in_page: The offset in the page
+ *
+ * Fills the first or the first two bytes of the @addrs field depending
+ * on the NAND bus width and the page size.
+ *
+ * Returns the number of cycles needed to encode the column, or a negative
+ * error code in case one of the arguments is invalid.
+ */
+static int nand_fill_column_cycles(struct nand_chip *chip, u8 *addrs,
+ unsigned int offset_in_page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ /* Make sure the offset is less than the actual page size. */
+ if (offset_in_page > mtd->writesize + mtd->oobsize)
+ return -EINVAL;
+
+ /*
+ * On small page NANDs, there's a dedicated command to access the OOB
+ * area, and the column address is relative to the start of the OOB
+ * area, not the start of the page. Asjust the address accordingly.
+ */
+ if (mtd->writesize <= 512 && offset_in_page >= mtd->writesize)
+ offset_in_page -= mtd->writesize;
+
+ /*
+ * The offset in page is expressed in bytes, if the NAND bus is 16-bit
+ * wide, then it must be divided by 2.
+ */
+ if (chip->options & NAND_BUSWIDTH_16) {
+ if (WARN_ON(offset_in_page % 2))
+ return -EINVAL;
+
+ offset_in_page /= 2;
+ }
+
+ addrs[0] = offset_in_page;
+
+ /*
+ * Small page NANDs use 1 cycle for the columns, while large page NANDs
+ * need 2
+ */
+ if (mtd->writesize <= 512)
+ return 1;
+
+ addrs[1] = offset_in_page >> 8;
+
+ return 2;
+}
+
+static int nand_sp_exec_read_page_op(struct nand_chip *chip, unsigned int page,
+ unsigned int offset_in_page, void *buf,
+ unsigned int len)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ const struct nand_sdr_timings *sdr =
+ nand_get_sdr_timings(&chip->data_interface);
+ u8 addrs[4];
+ struct nand_op_instr instrs[] = {
+ NAND_OP_CMD(NAND_CMD_READ0, 0),
+ NAND_OP_ADDR(3, addrs, PSEC_TO_NSEC(sdr->tWB_max)),
+ NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tR_max),
+ PSEC_TO_NSEC(sdr->tRR_min)),
+ NAND_OP_DATA_IN(len, buf, 0),
+ };
+ struct nand_operation op = NAND_OPERATION(instrs);
+ int ret;
+
+ /* Drop the DATA_IN instruction if len is set to 0. */
+ if (!len)
+ op.ninstrs--;
+
+ if (offset_in_page >= mtd->writesize)
+ instrs[0].ctx.cmd.opcode = NAND_CMD_READOOB;
+ else if (offset_in_page >= 256 &&
+ !(chip->options & NAND_BUSWIDTH_16))
+ instrs[0].ctx.cmd.opcode = NAND_CMD_READ1;
+
+ ret = nand_fill_column_cycles(chip, addrs, offset_in_page);
+ if (ret < 0)
+ return ret;
+
+ addrs[1] = page;
+ addrs[2] = page >> 8;
+
+ if (chip->options & NAND_ROW_ADDR_3) {
+ addrs[3] = page >> 16;
+ instrs[1].ctx.addr.naddrs++;
+ }
+
+ return nand_exec_op(chip, &op);
+}
+
+static int nand_lp_exec_read_page_op(struct nand_chip *chip, unsigned int page,
+ unsigned int offset_in_page, void *buf,
+ unsigned int len)
+{
+ const struct nand_sdr_timings *sdr =
+ nand_get_sdr_timings(&chip->data_interface);
+ u8 addrs[5];
+ struct nand_op_instr instrs[] = {
+ NAND_OP_CMD(NAND_CMD_READ0, 0),
+ NAND_OP_ADDR(4, addrs, 0),
+ NAND_OP_CMD(NAND_CMD_READSTART, PSEC_TO_NSEC(sdr->tWB_max)),
+ NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tR_max),
+ PSEC_TO_NSEC(sdr->tRR_min)),
+ NAND_OP_DATA_IN(len, buf, 0),
+ };
+ struct nand_operation op = NAND_OPERATION(instrs);
+ int ret;
+
+ /* Drop the DATA_IN instruction if len is set to 0. */
+ if (!len)
+ op.ninstrs--;
+
+ ret = nand_fill_column_cycles(chip, addrs, offset_in_page);
+ if (ret < 0)
+ return ret;
+
+ addrs[2] = page;
+ addrs[3] = page >> 8;
+
+ if (chip->options & NAND_ROW_ADDR_3) {
+ addrs[4] = page >> 16;
+ instrs[1].ctx.addr.naddrs++;
+ }
+
+ return nand_exec_op(chip, &op);
+}
+
+/**
+ * nand_read_page_op - Do a READ PAGE operation
+ * @chip: The NAND chip
+ * @page: page to read
+ * @offset_in_page: offset within the page
+ * @buf: buffer used to store the data
+ * @len: length of the buffer
+ *
+ * This function issues a READ PAGE operation.
+ * This function does not select/unselect the CS line.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int nand_read_page_op(struct nand_chip *chip, unsigned int page,
+ unsigned int offset_in_page, void *buf, unsigned int len)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ if (len && !buf)
+ return -EINVAL;
+
+ if (offset_in_page + len > mtd->writesize + mtd->oobsize)
+ return -EINVAL;
+
+ if (chip->exec_op) {
+ if (mtd->writesize > 512)
+ return nand_lp_exec_read_page_op(chip, page,
+ offset_in_page, buf,
+ len);
+
+ return nand_sp_exec_read_page_op(chip, page, offset_in_page,
+ buf, len);
+ }
+
+ chip->cmdfunc(mtd, NAND_CMD_READ0, offset_in_page, page);
+ if (len)
+ chip->read_buf(mtd, buf, len);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nand_read_page_op);
+
+/**
+ * nand_read_param_page_op - Do a READ PARAMETER PAGE operation
+ * @chip: The NAND chip
+ * @page: parameter page to read
+ * @buf: buffer used to store the data
+ * @len: length of the buffer
+ *
+ * This function issues a READ PARAMETER PAGE operation.
+ * This function does not select/unselect the CS line.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+static int nand_read_param_page_op(struct nand_chip *chip, u8 page, void *buf,
+ unsigned int len)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ unsigned int i;
+ u8 *p = buf;
+
+ if (len && !buf)
+ return -EINVAL;
+
+ if (chip->exec_op) {
+ const struct nand_sdr_timings *sdr =
+ nand_get_sdr_timings(&chip->data_interface);
+ struct nand_op_instr instrs[] = {
+ NAND_OP_CMD(NAND_CMD_PARAM, 0),
+ NAND_OP_ADDR(1, &page, PSEC_TO_NSEC(sdr->tWB_max)),
+ NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tR_max),
+ PSEC_TO_NSEC(sdr->tRR_min)),
+ NAND_OP_8BIT_DATA_IN(len, buf, 0),
+ };
+ struct nand_operation op = NAND_OPERATION(instrs);
+
+ /* Drop the DATA_IN instruction if len is set to 0. */
+ if (!len)
+ op.ninstrs--;
+
+ return nand_exec_op(chip, &op);
+ }
+
+ chip->cmdfunc(mtd, NAND_CMD_PARAM, page, -1);
+ for (i = 0; i < len; i++)
+ p[i] = chip->read_byte(mtd);
+
+ return 0;
+}
+
+/**
+ * nand_change_read_column_op - Do a CHANGE READ COLUMN operation
+ * @chip: The NAND chip
+ * @offset_in_page: offset within the page
+ * @buf: buffer used to store the data
+ * @len: length of the buffer
+ * @force_8bit: force 8-bit bus access
+ *
+ * This function issues a CHANGE READ COLUMN operation.
+ * This function does not select/unselect the CS line.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int nand_change_read_column_op(struct nand_chip *chip,
+ unsigned int offset_in_page, void *buf,
+ unsigned int len, bool force_8bit)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ if (len && !buf)
+ return -EINVAL;
+
+ if (offset_in_page + len > mtd->writesize + mtd->oobsize)
+ return -EINVAL;
+
+ /* Small page NANDs do not support column change. */
+ if (mtd->writesize <= 512)
+ return -ENOTSUPP;
+
+ if (chip->exec_op) {
+ const struct nand_sdr_timings *sdr =
+ nand_get_sdr_timings(&chip->data_interface);
+ u8 addrs[2] = {};
+ struct nand_op_instr instrs[] = {
+ NAND_OP_CMD(NAND_CMD_RNDOUT, 0),
+ NAND_OP_ADDR(2, addrs, 0),
+ NAND_OP_CMD(NAND_CMD_RNDOUTSTART,
+ PSEC_TO_NSEC(sdr->tCCS_min)),
+ NAND_OP_DATA_IN(len, buf, 0),
+ };
+ struct nand_operation op = NAND_OPERATION(instrs);
+ int ret;
+
+ ret = nand_fill_column_cycles(chip, addrs, offset_in_page);
+ if (ret < 0)
+ return ret;
+
+ /* Drop the DATA_IN instruction if len is set to 0. */
+ if (!len)
+ op.ninstrs--;
+
+ instrs[3].ctx.data.force_8bit = force_8bit;
+
+ return nand_exec_op(chip, &op);
+ }
+
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset_in_page, -1);
+ if (len)
+ chip->read_buf(mtd, buf, len);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nand_change_read_column_op);
+
+/**
+ * nand_read_oob_op - Do a READ OOB operation
+ * @chip: The NAND chip
+ * @page: page to read
+ * @offset_in_oob: offset within the OOB area
+ * @buf: buffer used to store the data
+ * @len: length of the buffer
+ *
+ * This function issues a READ OOB operation.
+ * This function does not select/unselect the CS line.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int nand_read_oob_op(struct nand_chip *chip, unsigned int page,
+ unsigned int offset_in_oob, void *buf, unsigned int len)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ if (len && !buf)
+ return -EINVAL;
+
+ if (offset_in_oob + len > mtd->oobsize)
+ return -EINVAL;
+
+ if (chip->exec_op)
+ return nand_read_page_op(chip, page,
+ mtd->writesize + offset_in_oob,
+ buf, len);
+
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, offset_in_oob, page);
+ if (len)
+ chip->read_buf(mtd, buf, len);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nand_read_oob_op);
+
+static int nand_exec_prog_page_op(struct nand_chip *chip, unsigned int page,
+ unsigned int offset_in_page, const void *buf,
+ unsigned int len, bool prog)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ const struct nand_sdr_timings *sdr =
+ nand_get_sdr_timings(&chip->data_interface);
+ u8 addrs[5] = {};
+ struct nand_op_instr instrs[] = {
+ /*
+ * The first instruction will be dropped if we're dealing
+ * with a large page NAND and adjusted if we're dealing
+ * with a small page NAND and the page offset is > 255.
+ */
+ NAND_OP_CMD(NAND_CMD_READ0, 0),
+ NAND_OP_CMD(NAND_CMD_SEQIN, 0),
+ NAND_OP_ADDR(0, addrs, PSEC_TO_NSEC(sdr->tADL_min)),
+ NAND_OP_DATA_OUT(len, buf, 0),
+ NAND_OP_CMD(NAND_CMD_PAGEPROG, PSEC_TO_NSEC(sdr->tWB_max)),
+ NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tPROG_max), 0),
+ };
+ struct nand_operation op = NAND_OPERATION(instrs);
+ int naddrs = nand_fill_column_cycles(chip, addrs, offset_in_page);
+ int ret;
+ u8 status;
+
+ if (naddrs < 0)
+ return naddrs;
+
+ addrs[naddrs++] = page;
+ addrs[naddrs++] = page >> 8;
+ if (chip->options & NAND_ROW_ADDR_3)
+ addrs[naddrs++] = page >> 16;
+
+ instrs[2].ctx.addr.naddrs = naddrs;
+
+ /* Drop the last two instructions if we're not programming the page. */
+ if (!prog) {
+ op.ninstrs -= 2;
+ /* Also drop the DATA_OUT instruction if empty. */
+ if (!len)
+ op.ninstrs--;
+ }
+
+ if (mtd->writesize <= 512) {
+ /*
+ * Small pages need some more tweaking: we have to adjust the
+ * first instruction depending on the page offset we're trying
+ * to access.
+ */
+ if (offset_in_page >= mtd->writesize)
+ instrs[0].ctx.cmd.opcode = NAND_CMD_READOOB;
+ else if (offset_in_page >= 256 &&
+ !(chip->options & NAND_BUSWIDTH_16))
+ instrs[0].ctx.cmd.opcode = NAND_CMD_READ1;
+ } else {
+ /*
+ * Drop the first command if we're dealing with a large page
+ * NAND.
+ */
+ op.instrs++;
+ op.ninstrs--;
+ }
+
+ ret = nand_exec_op(chip, &op);
+ if (!prog || ret)
+ return ret;
+
+ ret = nand_status_op(chip, &status);
+ if (ret)
+ return ret;
+
+ return status;
+}
+
+/**
+ * nand_prog_page_begin_op - starts a PROG PAGE operation
+ * @chip: The NAND chip
+ * @page: page to write
+ * @offset_in_page: offset within the page
+ * @buf: buffer containing the data to write to the page
+ * @len: length of the buffer
+ *
+ * This function issues the first half of a PROG PAGE operation.
+ * This function does not select/unselect the CS line.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int nand_prog_page_begin_op(struct nand_chip *chip, unsigned int page,
+ unsigned int offset_in_page, const void *buf,
+ unsigned int len)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ if (len && !buf)
+ return -EINVAL;
+
+ if (offset_in_page + len > mtd->writesize + mtd->oobsize)
+ return -EINVAL;
+
+ if (chip->exec_op)
+ return nand_exec_prog_page_op(chip, page, offset_in_page, buf,
+ len, false);
+
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, offset_in_page, page);
+
+ if (buf)
+ chip->write_buf(mtd, buf, len);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nand_prog_page_begin_op);
+
+/**
+ * nand_prog_page_end_op - ends a PROG PAGE operation
+ * @chip: The NAND chip
+ *
+ * This function issues the second half of a PROG PAGE operation.
+ * This function does not select/unselect the CS line.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int nand_prog_page_end_op(struct nand_chip *chip)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int ret;
+ u8 status;
+
+ if (chip->exec_op) {
+ const struct nand_sdr_timings *sdr =
+ nand_get_sdr_timings(&chip->data_interface);
+ struct nand_op_instr instrs[] = {
+ NAND_OP_CMD(NAND_CMD_PAGEPROG,
+ PSEC_TO_NSEC(sdr->tWB_max)),
+ NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tPROG_max), 0),
+ };
+ struct nand_operation op = NAND_OPERATION(instrs);
+
+ ret = nand_exec_op(chip, &op);
+ if (ret)
+ return ret;
+
+ ret = nand_status_op(chip, &status);
+ if (ret)
+ return ret;
+ } else {
+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+ ret = chip->waitfunc(mtd, chip);
+ if (ret < 0)
+ return ret;
+
+ status = ret;
+ }
+
+ if (status & NAND_STATUS_FAIL)
+ return -EIO;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nand_prog_page_end_op);
+
+/**
+ * nand_prog_page_op - Do a full PROG PAGE operation
+ * @chip: The NAND chip
+ * @page: page to write
+ * @offset_in_page: offset within the page
+ * @buf: buffer containing the data to write to the page
+ * @len: length of the buffer
+ *
+ * This function issues a full PROG PAGE operation.
+ * This function does not select/unselect the CS line.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int nand_prog_page_op(struct nand_chip *chip, unsigned int page,
+ unsigned int offset_in_page, const void *buf,
+ unsigned int len)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int status;
+
+ if (!len || !buf)
+ return -EINVAL;
+
+ if (offset_in_page + len > mtd->writesize + mtd->oobsize)
+ return -EINVAL;
+
+ if (chip->exec_op) {
+ status = nand_exec_prog_page_op(chip, page, offset_in_page, buf,
+ len, true);
+ } else {
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, offset_in_page, page);
+ chip->write_buf(mtd, buf, len);
+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+ status = chip->waitfunc(mtd, chip);
+ }
+
+ if (status & NAND_STATUS_FAIL)
+ return -EIO;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nand_prog_page_op);
+
+/**
+ * nand_change_write_column_op - Do a CHANGE WRITE COLUMN operation
+ * @chip: The NAND chip
+ * @offset_in_page: offset within the page
+ * @buf: buffer containing the data to send to the NAND
+ * @len: length of the buffer
+ * @force_8bit: force 8-bit bus access
+ *
+ * This function issues a CHANGE WRITE COLUMN operation.
+ * This function does not select/unselect the CS line.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int nand_change_write_column_op(struct nand_chip *chip,
+ unsigned int offset_in_page,
+ const void *buf, unsigned int len,
+ bool force_8bit)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ if (len && !buf)
+ return -EINVAL;
+
+ if (offset_in_page + len > mtd->writesize + mtd->oobsize)
+ return -EINVAL;
+
+ /* Small page NANDs do not support column change. */
+ if (mtd->writesize <= 512)
+ return -ENOTSUPP;
+
+ if (chip->exec_op) {
+ const struct nand_sdr_timings *sdr =
+ nand_get_sdr_timings(&chip->data_interface);
+ u8 addrs[2];
+ struct nand_op_instr instrs[] = {
+ NAND_OP_CMD(NAND_CMD_RNDIN, 0),
+ NAND_OP_ADDR(2, addrs, PSEC_TO_NSEC(sdr->tCCS_min)),
+ NAND_OP_DATA_OUT(len, buf, 0),
+ };
+ struct nand_operation op = NAND_OPERATION(instrs);
+ int ret;
+
+ ret = nand_fill_column_cycles(chip, addrs, offset_in_page);
+ if (ret < 0)
+ return ret;
+
+ instrs[2].ctx.data.force_8bit = force_8bit;
+
+ /* Drop the DATA_OUT instruction if len is set to 0. */
+ if (!len)
+ op.ninstrs--;
+
+ return nand_exec_op(chip, &op);
+ }
+
+ chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset_in_page, -1);
+ if (len)
+ chip->write_buf(mtd, buf, len);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nand_change_write_column_op);
+
+/**
+ * nand_readid_op - Do a READID operation
+ * @chip: The NAND chip
+ * @addr: address cycle to pass after the READID command
+ * @buf: buffer used to store the ID
+ * @len: length of the buffer
+ *
+ * This function sends a READID command and reads back the ID returned by the
+ * NAND.
+ * This function does not select/unselect the CS line.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int nand_readid_op(struct nand_chip *chip, u8 addr, void *buf,
+ unsigned int len)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ unsigned int i;
+ u8 *id = buf;
+
+ if (len && !buf)
+ return -EINVAL;
+
+ if (chip->exec_op) {
+ const struct nand_sdr_timings *sdr =
+ nand_get_sdr_timings(&chip->data_interface);
+ struct nand_op_instr instrs[] = {
+ NAND_OP_CMD(NAND_CMD_READID, 0),
+ NAND_OP_ADDR(1, &addr, PSEC_TO_NSEC(sdr->tADL_min)),
+ NAND_OP_8BIT_DATA_IN(len, buf, 0),
+ };
+ struct nand_operation op = NAND_OPERATION(instrs);
+
+ /* Drop the DATA_IN instruction if len is set to 0. */
+ if (!len)
+ op.ninstrs--;
+
+ return nand_exec_op(chip, &op);
+ }
+
+ chip->cmdfunc(mtd, NAND_CMD_READID, addr, -1);
+
+ for (i = 0; i < len; i++)
+ id[i] = chip->read_byte(mtd);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nand_readid_op);
+
+/**
+ * nand_status_op - Do a STATUS operation
+ * @chip: The NAND chip
+ * @status: out variable to store the NAND status
+ *
+ * This function sends a STATUS command and reads back the status returned by
+ * the NAND.
+ * This function does not select/unselect the CS line.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int nand_status_op(struct nand_chip *chip, u8 *status)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ if (chip->exec_op) {
+ const struct nand_sdr_timings *sdr =
+ nand_get_sdr_timings(&chip->data_interface);
+ struct nand_op_instr instrs[] = {
+ NAND_OP_CMD(NAND_CMD_STATUS,
+ PSEC_TO_NSEC(sdr->tADL_min)),
+ NAND_OP_8BIT_DATA_IN(1, status, 0),
+ };
+ struct nand_operation op = NAND_OPERATION(instrs);
+
+ if (!status)
+ op.ninstrs--;
+
+ return nand_exec_op(chip, &op);
+ }
+
+ chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
+ if (status)
+ *status = chip->read_byte(mtd);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nand_status_op);
+
+/**
+ * nand_exit_status_op - Exit a STATUS operation
+ * @chip: The NAND chip
+ *
+ * This function sends a READ0 command to cancel the effect of the STATUS
+ * command to avoid reading only the status until a new read command is sent.
+ *
+ * This function does not select/unselect the CS line.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int nand_exit_status_op(struct nand_chip *chip)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ if (chip->exec_op) {
+ struct nand_op_instr instrs[] = {
+ NAND_OP_CMD(NAND_CMD_READ0, 0),
+ };
+ struct nand_operation op = NAND_OPERATION(instrs);
+
+ return nand_exec_op(chip, &op);
+ }
+
+ chip->cmdfunc(mtd, NAND_CMD_READ0, -1, -1);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nand_exit_status_op);
+
+/**
+ * nand_erase_op - Do an erase operation
+ * @chip: The NAND chip
+ * @eraseblock: block to erase
+ *
+ * This function sends an ERASE command and waits for the NAND to be ready
+ * before returning.
+ * This function does not select/unselect the CS line.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ unsigned int page = eraseblock <<
+ (chip->phys_erase_shift - chip->page_shift);
+ int ret;
+ u8 status;
+
+ if (chip->exec_op) {
+ const struct nand_sdr_timings *sdr =
+ nand_get_sdr_timings(&chip->data_interface);
+ u8 addrs[3] = { page, page >> 8, page >> 16 };
+ struct nand_op_instr instrs[] = {
+ NAND_OP_CMD(NAND_CMD_ERASE1, 0),
+ NAND_OP_ADDR(2, addrs, 0),
+ NAND_OP_CMD(NAND_CMD_ERASE2,
+ PSEC_TO_MSEC(sdr->tWB_max)),
+ NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tBERS_max), 0),
+ };
+ struct nand_operation op = NAND_OPERATION(instrs);
+
+ if (chip->options & NAND_ROW_ADDR_3)
+ instrs[1].ctx.addr.naddrs++;
+
+ ret = nand_exec_op(chip, &op);
+ if (ret)
+ return ret;
+
+ ret = nand_status_op(chip, &status);
+ if (ret)
+ return ret;
+ } else {
+ chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
+ chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
+
+ ret = chip->waitfunc(mtd, chip);
+ if (ret < 0)
+ return ret;
+
+ status = ret;
+ }
+
+ if (status & NAND_STATUS_FAIL)
+ return -EIO;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nand_erase_op);
+
+/**
+ * nand_set_features_op - Do a SET FEATURES operation
+ * @chip: The NAND chip
+ * @feature: feature id
+ * @data: 4 bytes of data
+ *
+ * This function sends a SET FEATURES command and waits for the NAND to be
+ * ready before returning.
+ * This function does not select/unselect the CS line.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+static int nand_set_features_op(struct nand_chip *chip, u8 feature,
+ const void *data)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ const u8 *params = data;
+ int i, ret;
+ u8 status;
+
+ if (chip->exec_op) {
+ const struct nand_sdr_timings *sdr =
+ nand_get_sdr_timings(&chip->data_interface);
+ struct nand_op_instr instrs[] = {
+ NAND_OP_CMD(NAND_CMD_SET_FEATURES, 0),
+ NAND_OP_ADDR(1, &feature, PSEC_TO_NSEC(sdr->tADL_min)),
+ NAND_OP_8BIT_DATA_OUT(ONFI_SUBFEATURE_PARAM_LEN, data,
+ PSEC_TO_NSEC(sdr->tWB_max)),
+ NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tFEAT_max), 0),
+ };
+ struct nand_operation op = NAND_OPERATION(instrs);
+
+ ret = nand_exec_op(chip, &op);
+ if (ret)
+ return ret;
+
+ ret = nand_status_op(chip, &status);
+ if (ret)
+ return ret;
+ } else {
+ chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, feature, -1);
+ for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
+ chip->write_byte(mtd, params[i]);
+
+ ret = chip->waitfunc(mtd, chip);
+ if (ret < 0)
+ return ret;
+
+ status = ret;
+ }
+
+ if (status & NAND_STATUS_FAIL)
+ return -EIO;
+
+ return 0;
+}
+
+/**
+ * nand_get_features_op - Do a GET FEATURES operation
+ * @chip: The NAND chip
+ * @feature: feature id
+ * @data: 4 bytes of data
+ *
+ * This function sends a GET FEATURES command and waits for the NAND to be
+ * ready before returning.
+ * This function does not select/unselect the CS line.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+static int nand_get_features_op(struct nand_chip *chip, u8 feature,
+ void *data)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ u8 *params = data;
+ int i;
+
+ if (chip->exec_op) {
+ const struct nand_sdr_timings *sdr =
+ nand_get_sdr_timings(&chip->data_interface);
+ struct nand_op_instr instrs[] = {
+ NAND_OP_CMD(NAND_CMD_GET_FEATURES, 0),
+ NAND_OP_ADDR(1, &feature, PSEC_TO_NSEC(sdr->tWB_max)),
+ NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tFEAT_max),
+ PSEC_TO_NSEC(sdr->tRR_min)),
+ NAND_OP_8BIT_DATA_IN(ONFI_SUBFEATURE_PARAM_LEN,
+ data, 0),
+ };
+ struct nand_operation op = NAND_OPERATION(instrs);
+
+ return nand_exec_op(chip, &op);
+ }
+
+ chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, feature, -1);
+ for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
+ params[i] = chip->read_byte(mtd);
+
+ return 0;
+}
+
+/**
+ * nand_reset_op - Do a reset operation
+ * @chip: The NAND chip
+ *
+ * This function sends a RESET command and waits for the NAND to be ready
+ * before returning.
+ * This function does not select/unselect the CS line.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int nand_reset_op(struct nand_chip *chip)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ if (chip->exec_op) {
+ const struct nand_sdr_timings *sdr =
+ nand_get_sdr_timings(&chip->data_interface);
+ struct nand_op_instr instrs[] = {
+ NAND_OP_CMD(NAND_CMD_RESET, PSEC_TO_NSEC(sdr->tWB_max)),
+ NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tRST_max), 0),
+ };
+ struct nand_operation op = NAND_OPERATION(instrs);
+
+ return nand_exec_op(chip, &op);
+ }
+
+ chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nand_reset_op);
+
+/**
+ * nand_read_data_op - Read data from the NAND
+ * @chip: The NAND chip
+ * @buf: buffer used to store the data
+ * @len: length of the buffer
+ * @force_8bit: force 8-bit bus access
+ *
+ * This function does a raw data read on the bus. Usually used after launching
+ * another NAND operation like nand_read_page_op().
+ * This function does not select/unselect the CS line.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int nand_read_data_op(struct nand_chip *chip, void *buf, unsigned int len,
+ bool force_8bit)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ if (!len || !buf)
+ return -EINVAL;
+
+ if (chip->exec_op) {
+ struct nand_op_instr instrs[] = {
+ NAND_OP_DATA_IN(len, buf, 0),
+ };
+ struct nand_operation op = NAND_OPERATION(instrs);
+
+ instrs[0].ctx.data.force_8bit = force_8bit;
+
+ return nand_exec_op(chip, &op);
+ }
+
+ if (force_8bit) {
+ u8 *p = buf;
+ unsigned int i;
+
+ for (i = 0; i < len; i++)
+ p[i] = chip->read_byte(mtd);
+ } else {
+ chip->read_buf(mtd, buf, len);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nand_read_data_op);
+
+/**
+ * nand_write_data_op - Write data from the NAND
+ * @chip: The NAND chip
+ * @buf: buffer containing the data to send on the bus
+ * @len: length of the buffer
+ * @force_8bit: force 8-bit bus access
+ *
+ * This function does a raw data write on the bus. Usually used after launching
+ * another NAND operation like nand_write_page_begin_op().
+ * This function does not select/unselect the CS line.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int nand_write_data_op(struct nand_chip *chip, const void *buf,
+ unsigned int len, bool force_8bit)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ if (!len || !buf)
+ return -EINVAL;
+
+ if (chip->exec_op) {
+ struct nand_op_instr instrs[] = {
+ NAND_OP_DATA_OUT(len, buf, 0),
+ };
+ struct nand_operation op = NAND_OPERATION(instrs);
+
+ instrs[0].ctx.data.force_8bit = force_8bit;
+
+ return nand_exec_op(chip, &op);
+ }
+
+ if (force_8bit) {
+ const u8 *p = buf;
+ unsigned int i;
+
+ for (i = 0; i < len; i++)
+ chip->write_byte(mtd, p[i]);
+ } else {
+ chip->write_buf(mtd, buf, len);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nand_write_data_op);
+
+/**
+ * struct nand_op_parser_ctx - Context used by the parser
+ * @instrs: array of all the instructions that must be addressed
+ * @ninstrs: length of the @instrs array
+ * @subop: Sub-operation to be passed to the NAND controller
+ *
+ * This structure is used by the core to split NAND operations into
+ * sub-operations that can be handled by the NAND controller.
+ */
+struct nand_op_parser_ctx {
+ const struct nand_op_instr *instrs;
+ unsigned int ninstrs;
+ struct nand_subop subop;
+};
+
+/**
+ * nand_op_parser_must_split_instr - Checks if an instruction must be split
+ * @pat: the parser pattern element that matches @instr
+ * @instr: pointer to the instruction to check
+ * @start_offset: this is an in/out parameter. If @instr has already been
+ * split, then @start_offset is the offset from which to start
+ * (either an address cycle or an offset in the data buffer).
+ * Conversely, if the function returns true (ie. instr must be
+ * split), this parameter is updated to point to the first
+ * data/address cycle that has not been taken care of.
+ *
+ * Some NAND controllers are limited and cannot send X address cycles with a
+ * unique operation, or cannot read/write more than Y bytes at the same time.
+ * In this case, split the instruction that does not fit in a single
+ * controller-operation into two or more chunks.
+ *
+ * Returns true if the instruction must be split, false otherwise.
+ * The @start_offset parameter is also updated to the offset at which the next
+ * bundle of instruction must start (if an address or a data instruction).
+ */
+static bool
+nand_op_parser_must_split_instr(const struct nand_op_parser_pattern_elem *pat,
+ const struct nand_op_instr *instr,
+ unsigned int *start_offset)
+{
+ switch (pat->type) {
+ case NAND_OP_ADDR_INSTR:
+ if (!pat->ctx.addr.maxcycles)
+ break;
+
+ if (instr->ctx.addr.naddrs - *start_offset >
+ pat->ctx.addr.maxcycles) {
+ *start_offset += pat->ctx.addr.maxcycles;
+ return true;
+ }
+ break;
+
+ case NAND_OP_DATA_IN_INSTR:
+ case NAND_OP_DATA_OUT_INSTR:
+ if (!pat->ctx.data.maxlen)
+ break;
+
+ if (instr->ctx.data.len - *start_offset >
+ pat->ctx.data.maxlen) {
+ *start_offset += pat->ctx.data.maxlen;
+ return true;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+/**
+ * nand_op_parser_match_pat - Checks if a pattern matches the instructions
+ * remaining in the parser context
+ * @pat: the pattern to test
+ * @ctx: the parser context structure to match with the pattern @pat
+ *
+ * Check if @pat matches the set or a sub-set of instructions remaining in @ctx.
+ * Returns true if this is the case, false ortherwise. When true is returned,
+ * @ctx->subop is updated with the set of instructions to be passed to the
+ * controller driver.
+ */
+static bool
+nand_op_parser_match_pat(const struct nand_op_parser_pattern *pat,
+ struct nand_op_parser_ctx *ctx)
+{
+ unsigned int instr_offset = ctx->subop.first_instr_start_off;
+ const struct nand_op_instr *end = ctx->instrs + ctx->ninstrs;
+ const struct nand_op_instr *instr = ctx->subop.instrs;
+ unsigned int i, ninstrs;
+
+ for (i = 0, ninstrs = 0; i < pat->nelems && instr < end; i++) {
+ /*
+ * The pattern instruction does not match the operation
+ * instruction. If the instruction is marked optional in the
+ * pattern definition, we skip the pattern element and continue
+ * to the next one. If the element is mandatory, there's no
+ * match and we can return false directly.
+ */
+ if (instr->type != pat->elems[i].type) {
+ if (!pat->elems[i].optional)
+ return false;
+
+ continue;
+ }
+
+ /*
+ * Now check the pattern element constraints. If the pattern is
+ * not able to handle the whole instruction in a single step,
+ * we have to split it.
+ * The last_instr_end_off value comes back updated to point to
+ * the position where we have to split the instruction (the
+ * start of the next subop chunk).
+ */
+ if (nand_op_parser_must_split_instr(&pat->elems[i], instr,
+ &instr_offset)) {
+ ninstrs++;
+ i++;
+ break;
+ }
+
+ instr++;
+ ninstrs++;
+ instr_offset = 0;
+ }
+
+ /*
+ * This can happen if all instructions of a pattern are optional.
+ * Still, if there's not at least one instruction handled by this
+ * pattern, this is not a match, and we should try the next one (if
+ * any).
+ */
+ if (!ninstrs)
+ return false;
+
+ /*
+ * We had a match on the pattern head, but the pattern may be longer
+ * than the instructions we're asked to execute. We need to make sure
+ * there's no mandatory elements in the pattern tail.
+ */
+ for (; i < pat->nelems; i++) {
+ if (!pat->elems[i].optional)
+ return false;
+ }
+
+ /*
+ * We have a match: update the subop structure accordingly and return
+ * true.
+ */
+ ctx->subop.ninstrs = ninstrs;
+ ctx->subop.last_instr_end_off = instr_offset;
+
+ return true;
+}
+
+#if IS_ENABLED(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG)
+static void nand_op_parser_trace(const struct nand_op_parser_ctx *ctx)
+{
+ const struct nand_op_instr *instr;
+ char *prefix = " ";
+ unsigned int i;
+
+ pr_debug("executing subop:\n");
+
+ for (i = 0; i < ctx->ninstrs; i++) {
+ instr = &ctx->instrs[i];
+
+ if (instr == &ctx->subop.instrs[0])
+ prefix = " ->";
+
+ switch (instr->type) {
+ case NAND_OP_CMD_INSTR:
+ pr_debug("%sCMD [0x%02x]\n", prefix,
+ instr->ctx.cmd.opcode);
+ break;
+ case NAND_OP_ADDR_INSTR:
+ pr_debug("%sADDR [%d cyc: %*ph]\n", prefix,
+ instr->ctx.addr.naddrs,
+ instr->ctx.addr.naddrs < 64 ?
+ instr->ctx.addr.naddrs : 64,
+ instr->ctx.addr.addrs);
+ break;
+ case NAND_OP_DATA_IN_INSTR:
+ pr_debug("%sDATA_IN [%d B%s]\n", prefix,
+ instr->ctx.data.len,
+ instr->ctx.data.force_8bit ?
+ ", force 8-bit" : "");
+ break;
+ case NAND_OP_DATA_OUT_INSTR:
+ pr_debug("%sDATA_OUT [%d B%s]\n", prefix,
+ instr->ctx.data.len,
+ instr->ctx.data.force_8bit ?
+ ", force 8-bit" : "");
+ break;
+ case NAND_OP_WAITRDY_INSTR:
+ pr_debug("%sWAITRDY [max %d ms]\n", prefix,
+ instr->ctx.waitrdy.timeout_ms);
+ break;
+ }
+
+ if (instr == &ctx->subop.instrs[ctx->subop.ninstrs - 1])
+ prefix = " ";
+ }
+}
+#else
+static void nand_op_parser_trace(const struct nand_op_parser_ctx *ctx)
+{
+ /* NOP */
+}
+#endif
+
+/**
+ * nand_op_parser_exec_op - exec_op parser
+ * @chip: the NAND chip
+ * @parser: patterns description provided by the controller driver
+ * @op: the NAND operation to address
+ * @check_only: when true, the function only checks if @op can be handled but
+ * does not execute the operation
+ *
+ * Helper function designed to ease integration of NAND controller drivers that
+ * only support a limited set of instruction sequences. The supported sequences
+ * are described in @parser, and the framework takes care of splitting @op into
+ * multiple sub-operations (if required) and pass them back to the ->exec()
+ * callback of the matching pattern if @check_only is set to false.
+ *
+ * NAND controller drivers should call this function from their own ->exec_op()
+ * implementation.
+ *
+ * Returns 0 on success, a negative error code otherwise. A failure can be
+ * caused by an unsupported operation (none of the supported patterns is able
+ * to handle the requested operation), or an error returned by one of the
+ * matching pattern->exec() hook.
+ */
+int nand_op_parser_exec_op(struct nand_chip *chip,
+ const struct nand_op_parser *parser,
+ const struct nand_operation *op, bool check_only)
+{
+ struct nand_op_parser_ctx ctx = {
+ .subop.instrs = op->instrs,
+ .instrs = op->instrs,
+ .ninstrs = op->ninstrs,
+ };
+ unsigned int i;
+
+ while (ctx.subop.instrs < op->instrs + op->ninstrs) {
+ int ret;
+
+ for (i = 0; i < parser->npatterns; i++) {
+ const struct nand_op_parser_pattern *pattern;
+
+ pattern = &parser->patterns[i];
+ if (!nand_op_parser_match_pat(pattern, &ctx))
+ continue;
+
+ nand_op_parser_trace(&ctx);
+
+ if (check_only)
+ break;
+
+ ret = pattern->exec(chip, &ctx.subop);
+ if (ret)
+ return ret;
+
+ break;
+ }
+
+ if (i == parser->npatterns) {
+ pr_debug("->exec_op() parser: pattern not found!\n");
+ return -ENOTSUPP;
+ }
+
+ /*
+ * Update the context structure by pointing to the start of the
+ * next subop.
+ */
+ ctx.subop.instrs = ctx.subop.instrs + ctx.subop.ninstrs;
+ if (ctx.subop.last_instr_end_off)
+ ctx.subop.instrs -= 1;
+
+ ctx.subop.first_instr_start_off = ctx.subop.last_instr_end_off;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nand_op_parser_exec_op);
+
+static bool nand_instr_is_data(const struct nand_op_instr *instr)
+{
+ return instr && (instr->type == NAND_OP_DATA_IN_INSTR ||
+ instr->type == NAND_OP_DATA_OUT_INSTR);
+}
+
+static bool nand_subop_instr_is_valid(const struct nand_subop *subop,
+ unsigned int instr_idx)
+{
+ return subop && instr_idx < subop->ninstrs;
+}
+
+static int nand_subop_get_start_off(const struct nand_subop *subop,
+ unsigned int instr_idx)
+{
+ if (instr_idx)
+ return 0;
+
+ return subop->first_instr_start_off;
+}
+
+/**
+ * nand_subop_get_addr_start_off - Get the start offset in an address array
+ * @subop: The entire sub-operation
+ * @instr_idx: Index of the instruction inside the sub-operation
+ *
+ * During driver development, one could be tempted to directly use the
+ * ->addr.addrs field of address instructions. This is wrong as address
+ * instructions might be split.
+ *
+ * Given an address instruction, returns the offset of the first cycle to issue.
+ */
+int nand_subop_get_addr_start_off(const struct nand_subop *subop,
+ unsigned int instr_idx)
+{
+ if (!nand_subop_instr_is_valid(subop, instr_idx) ||
+ subop->instrs[instr_idx].type != NAND_OP_ADDR_INSTR)
+ return -EINVAL;
+
+ return nand_subop_get_start_off(subop, instr_idx);
+}
+EXPORT_SYMBOL_GPL(nand_subop_get_addr_start_off);
+
+/**
+ * nand_subop_get_num_addr_cyc - Get the remaining address cycles to assert
+ * @subop: The entire sub-operation
+ * @instr_idx: Index of the instruction inside the sub-operation
+ *
+ * During driver development, one could be tempted to directly use the
+ * ->addr->naddrs field of a data instruction. This is wrong as instructions
+ * might be split.
+ *
+ * Given an address instruction, returns the number of address cycle to issue.
+ */
+int nand_subop_get_num_addr_cyc(const struct nand_subop *subop,
+ unsigned int instr_idx)
+{
+ int start_off, end_off;
+
+ if (!nand_subop_instr_is_valid(subop, instr_idx) ||
+ subop->instrs[instr_idx].type != NAND_OP_ADDR_INSTR)
+ return -EINVAL;
+
+ start_off = nand_subop_get_addr_start_off(subop, instr_idx);
+
+ if (instr_idx == subop->ninstrs - 1 &&
+ subop->last_instr_end_off)
+ end_off = subop->last_instr_end_off;
+ else
+ end_off = subop->instrs[instr_idx].ctx.addr.naddrs;
+
+ return end_off - start_off;
+}
+EXPORT_SYMBOL_GPL(nand_subop_get_num_addr_cyc);
+
+/**
+ * nand_subop_get_data_start_off - Get the start offset in a data array
+ * @subop: The entire sub-operation
+ * @instr_idx: Index of the instruction inside the sub-operation
+ *
+ * During driver development, one could be tempted to directly use the
+ * ->data->buf.{in,out} field of data instructions. This is wrong as data
+ * instructions might be split.
+ *
+ * Given a data instruction, returns the offset to start from.
+ */
+int nand_subop_get_data_start_off(const struct nand_subop *subop,
+ unsigned int instr_idx)
+{
+ if (!nand_subop_instr_is_valid(subop, instr_idx) ||
+ !nand_instr_is_data(&subop->instrs[instr_idx]))
+ return -EINVAL;
+
+ return nand_subop_get_start_off(subop, instr_idx);
+}
+EXPORT_SYMBOL_GPL(nand_subop_get_data_start_off);
+
+/**
+ * nand_subop_get_data_len - Get the number of bytes to retrieve
+ * @subop: The entire sub-operation
+ * @instr_idx: Index of the instruction inside the sub-operation
+ *
+ * During driver development, one could be tempted to directly use the
+ * ->data->len field of a data instruction. This is wrong as data instructions
+ * might be split.
+ *
+ * Returns the length of the chunk of data to send/receive.
+ */
+int nand_subop_get_data_len(const struct nand_subop *subop,
+ unsigned int instr_idx)
+{
+ int start_off = 0, end_off;
+
+ if (!nand_subop_instr_is_valid(subop, instr_idx) ||
+ !nand_instr_is_data(&subop->instrs[instr_idx]))
+ return -EINVAL;
+
+ start_off = nand_subop_get_data_start_off(subop, instr_idx);
+
+ if (instr_idx == subop->ninstrs - 1 &&
+ subop->last_instr_end_off)
+ end_off = subop->last_instr_end_off;
+ else
+ end_off = subop->instrs[instr_idx].ctx.data.len;
+
+ return end_off - start_off;
+}
+EXPORT_SYMBOL_GPL(nand_subop_get_data_len);
+
+/**
+ * nand_reset - Reset and initialize a NAND device
+ * @chip: The NAND chip
+ * @chipnr: Internal die id
+ *
+ * Save the timings data structure, then apply SDR timings mode 0 (see
+ * nand_reset_data_interface for details), do the reset operation, and
+ * apply back the previous timings.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int nand_reset(struct nand_chip *chip, int chipnr)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct nand_data_interface saved_data_intf = chip->data_interface;
+ int ret;
+
+ ret = nand_reset_data_interface(chip, chipnr);
+ if (ret)
+ return ret;
+
+ /*
+ * The CS line has to be released before we can apply the new NAND
+ * interface settings, hence this weird ->select_chip() dance.
+ */
+ chip->select_chip(mtd, chipnr);
+ ret = nand_reset_op(chip);
+ chip->select_chip(mtd, -1);
+ if (ret)
+ return ret;
+
+ /*
+ * A nand_reset_data_interface() put both the NAND chip and the NAND
+ * controller in timings mode 0. If the default mode for this chip is
+ * also 0, no need to proceed to the change again. Plus, at probe time,
+ * nand_setup_data_interface() uses ->set/get_features() which would
+ * fail anyway as the parameter page is not available yet.
+ */
+ if (!chip->onfi_timing_mode_default)
+ return 0;
+
+ chip->data_interface = saved_data_intf;
+ ret = nand_setup_data_interface(chip, chipnr);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nand_reset);
+
+/**
+ * nand_check_erased_buf - check if a buffer contains (almost) only 0xff data
+ * @buf: buffer to test
+ * @len: buffer length
+ * @bitflips_threshold: maximum number of bitflips
+ *
+ * Check if a buffer contains only 0xff, which means the underlying region
+ * has been erased and is ready to be programmed.
+ * The bitflips_threshold specify the maximum number of bitflips before
+ * considering the region is not erased.
+ * Note: The logic of this function has been extracted from the memweight
+ * implementation, except that nand_check_erased_buf function exit before
+ * testing the whole buffer if the number of bitflips exceed the
+ * bitflips_threshold value.
+ *
+ * Returns a positive number of bitflips less than or equal to
+ * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the
+ * threshold.
+ */
+static int nand_check_erased_buf(void *buf, int len, int bitflips_threshold)
+{
+ const unsigned char *bitmap = buf;
+ int bitflips = 0;
+ int weight;
+
+ for (; len && ((uintptr_t)bitmap) % sizeof(long);
+ len--, bitmap++) {
+ weight = hweight8(*bitmap);
+ bitflips += BITS_PER_BYTE - weight;
+ if (unlikely(bitflips > bitflips_threshold))
+ return -EBADMSG;
+ }
+
+ for (; len >= sizeof(long);
+ len -= sizeof(long), bitmap += sizeof(long)) {
+ unsigned long d = *((unsigned long *)bitmap);
+ if (d == ~0UL)
+ continue;
+ weight = hweight_long(d);
+ bitflips += BITS_PER_LONG - weight;
+ if (unlikely(bitflips > bitflips_threshold))
+ return -EBADMSG;
+ }
+
+ for (; len > 0; len--, bitmap++) {
+ weight = hweight8(*bitmap);
+ bitflips += BITS_PER_BYTE - weight;
+ if (unlikely(bitflips > bitflips_threshold))
+ return -EBADMSG;
+ }
+
+ return bitflips;
+}
+
+/**
+ * nand_check_erased_ecc_chunk - check if an ECC chunk contains (almost) only
+ * 0xff data
+ * @data: data buffer to test
+ * @datalen: data length
+ * @ecc: ECC buffer
+ * @ecclen: ECC length
+ * @extraoob: extra OOB buffer
+ * @extraooblen: extra OOB length
+ * @bitflips_threshold: maximum number of bitflips
+ *
+ * Check if a data buffer and its associated ECC and OOB data contains only
+ * 0xff pattern, which means the underlying region has been erased and is
+ * ready to be programmed.
+ * The bitflips_threshold specify the maximum number of bitflips before
+ * considering the region as not erased.
+ *
+ * Note:
+ * 1/ ECC algorithms are working on pre-defined block sizes which are usually
+ * different from the NAND page size. When fixing bitflips, ECC engines will
+ * report the number of errors per chunk, and the NAND core infrastructure
+ * expect you to return the maximum number of bitflips for the whole page.
+ * This is why you should always use this function on a single chunk and
+ * not on the whole page. After checking each chunk you should update your
+ * max_bitflips value accordingly.
+ * 2/ When checking for bitflips in erased pages you should not only check
+ * the payload data but also their associated ECC data, because a user might
+ * have programmed almost all bits to 1 but a few. In this case, we
+ * shouldn't consider the chunk as erased, and checking ECC bytes prevent
+ * this case.
+ * 3/ The extraoob argument is optional, and should be used if some of your OOB
+ * data are protected by the ECC engine.
+ * It could also be used if you support subpages and want to attach some
+ * extra OOB data to an ECC chunk.
+ *
+ * Returns a positive number of bitflips less than or equal to
+ * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the
+ * threshold. In case of success, the passed buffers are filled with 0xff.
+ */
+int nand_check_erased_ecc_chunk(void *data, int datalen,
+ void *ecc, int ecclen,
+ void *extraoob, int extraooblen,
+ int bitflips_threshold)
+{
+ int data_bitflips = 0, ecc_bitflips = 0, extraoob_bitflips = 0;
+
+ data_bitflips = nand_check_erased_buf(data, datalen,
+ bitflips_threshold);
+ if (data_bitflips < 0)
+ return data_bitflips;
+
+ bitflips_threshold -= data_bitflips;
+
+ ecc_bitflips = nand_check_erased_buf(ecc, ecclen, bitflips_threshold);
+ if (ecc_bitflips < 0)
+ return ecc_bitflips;
+
+ bitflips_threshold -= ecc_bitflips;
+
+ extraoob_bitflips = nand_check_erased_buf(extraoob, extraooblen,
+ bitflips_threshold);
+ if (extraoob_bitflips < 0)
+ return extraoob_bitflips;
+
+ if (data_bitflips)
+ memset(data, 0xff, datalen);
+
+ if (ecc_bitflips)
+ memset(ecc, 0xff, ecclen);
+
+ if (extraoob_bitflips)
+ memset(extraoob, 0xff, extraooblen);
+
+ return data_bitflips + ecc_bitflips + extraoob_bitflips;
+}
+EXPORT_SYMBOL(nand_check_erased_ecc_chunk);
+
+/**
+ * nand_read_page_raw - [INTERN] read raw page data without ecc
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
+ *
+ * Not for syndrome calculating ECC controllers, which use a special oob layout.
+ */
+int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ int ret;
+
+ ret = nand_read_page_op(chip, page, 0, buf, mtd->writesize);
+ if (ret)
+ return ret;
+
+ if (oob_required) {
+ ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize,
+ false);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(nand_read_page_raw);
+
+/**
+ * nand_read_page_raw_syndrome - [INTERN] read raw page data without ecc
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
+ *
+ * We need a special oob layout and handling even when OOB isn't used.
+ */
+static int nand_read_page_raw_syndrome(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf,
+ int oob_required, int page)
+{
+ int eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ uint8_t *oob = chip->oob_poi;
+ int steps, size, ret;
+
+ ret = nand_read_page_op(chip, page, 0, NULL, 0);
+ if (ret)
+ return ret;
+
+ for (steps = chip->ecc.steps; steps > 0; steps--) {
+ ret = nand_read_data_op(chip, buf, eccsize, false);
+ if (ret)
+ return ret;
+
+ buf += eccsize;
+
+ if (chip->ecc.prepad) {
+ ret = nand_read_data_op(chip, oob, chip->ecc.prepad,
+ false);
+ if (ret)
+ return ret;
+
+ oob += chip->ecc.prepad;
+ }
+
+ ret = nand_read_data_op(chip, oob, eccbytes, false);
+ if (ret)
+ return ret;
+
+ oob += eccbytes;
+
+ if (chip->ecc.postpad) {
+ ret = nand_read_data_op(chip, oob, chip->ecc.postpad,
+ false);
+ if (ret)
+ return ret;
+
+ oob += chip->ecc.postpad;
+ }
+ }
+
+ size = mtd->oobsize - (oob - chip->oob_poi);
+ if (size) {
+ ret = nand_read_data_op(chip, oob, size, false);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * nand_read_page_swecc - [REPLACEABLE] software ECC based page read function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
+ */
+static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ int i, eccsize = chip->ecc.size, ret;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *p = buf;
+ uint8_t *ecc_calc = chip->ecc.calc_buf;
+ uint8_t *ecc_code = chip->ecc.code_buf;
+ unsigned int max_bitflips = 0;
+
+ chip->ecc.read_page_raw(mtd, chip, buf, 1, page);
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
+ chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+
+ ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
+ chip->ecc.total);
+ if (ret)
+ return ret;
+
+ eccsteps = chip->ecc.steps;
+ p = buf;
+
+ for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ int stat;
+
+ stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ }
+ }
+ return max_bitflips;
+}
+
+/**
+ * nand_read_subpage - [REPLACEABLE] ECC based sub-page read function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @data_offs: offset of requested data within the page
+ * @readlen: data length
+ * @bufpoi: buffer to store read data
+ * @page: page number to read
+ */
+static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
+ uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi,
+ int page)
+{
+ int start_step, end_step, num_steps, ret;
+ uint8_t *p;
+ int data_col_addr, i, gaps = 0;
+ int datafrag_len, eccfrag_len, aligned_len, aligned_pos;
+ int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;
+ int index, section = 0;
+ unsigned int max_bitflips = 0;
+ struct mtd_oob_region oobregion = { };
+
+ /* Column address within the page aligned to ECC size (256bytes) */
+ start_step = data_offs / chip->ecc.size;
+ end_step = (data_offs + readlen - 1) / chip->ecc.size;
+ num_steps = end_step - start_step + 1;
+ index = start_step * chip->ecc.bytes;
+
+ /* Data size aligned to ECC ecc.size */
+ datafrag_len = num_steps * chip->ecc.size;
+ eccfrag_len = num_steps * chip->ecc.bytes;
+
+ data_col_addr = start_step * chip->ecc.size;
+ /* If we read not a page aligned data */
+ p = bufpoi + data_col_addr;
+ ret = nand_read_page_op(chip, page, data_col_addr, p, datafrag_len);
+ if (ret)
+ return ret;
+
+ /* Calculate ECC */
+ for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size)
+ chip->ecc.calculate(mtd, p, &chip->ecc.calc_buf[i]);
+
+ /*
+ * The performance is faster if we position offsets according to
+ * ecc.pos. Let's make sure that there are no gaps in ECC positions.
+ */
+ ret = mtd_ooblayout_find_eccregion(mtd, index, &section, &oobregion);
+ if (ret)
+ return ret;
+
+ if (oobregion.length < eccfrag_len)
+ gaps = 1;
+
+ if (gaps) {
+ ret = nand_change_read_column_op(chip, mtd->writesize,
+ chip->oob_poi, mtd->oobsize,
+ false);
+ if (ret)
+ return ret;
+ } else {
+ /*
+ * Send the command to read the particular ECC bytes take care
+ * about buswidth alignment in read_buf.
+ */
+ aligned_pos = oobregion.offset & ~(busw - 1);
+ aligned_len = eccfrag_len;
+ if (oobregion.offset & (busw - 1))
+ aligned_len++;
+ if ((oobregion.offset + (num_steps * chip->ecc.bytes)) &
+ (busw - 1))
+ aligned_len++;
+
+ ret = nand_change_read_column_op(chip,
+ mtd->writesize + aligned_pos,
+ &chip->oob_poi[aligned_pos],
+ aligned_len, false);
+ if (ret)
+ return ret;
+ }
+
+ ret = mtd_ooblayout_get_eccbytes(mtd, chip->ecc.code_buf,
+ chip->oob_poi, index, eccfrag_len);
+ if (ret)
+ return ret;
+
+ p = bufpoi + data_col_addr;
+ for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) {
+ int stat;
+
+ stat = chip->ecc.correct(mtd, p, &chip->ecc.code_buf[i],
+ &chip->ecc.calc_buf[i]);
+ if (stat == -EBADMSG &&
+ (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) {
+ /* check for empty pages with bitflips */
+ stat = nand_check_erased_ecc_chunk(p, chip->ecc.size,
+ &chip->ecc.code_buf[i],
+ chip->ecc.bytes,
+ NULL, 0,
+ chip->ecc.strength);
+ }
+
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ }
+ }
+ return max_bitflips;
+}
+
+/**
+ * nand_read_page_hwecc - [REPLACEABLE] hardware ECC based page read function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
+ *
+ * Not for syndrome calculating ECC controllers which need a special oob layout.
+ */
+static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ int i, eccsize = chip->ecc.size, ret;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *p = buf;
+ uint8_t *ecc_calc = chip->ecc.calc_buf;
+ uint8_t *ecc_code = chip->ecc.code_buf;
+ unsigned int max_bitflips = 0;
+
+ ret = nand_read_page_op(chip, page, 0, NULL, 0);
+ if (ret)
+ return ret;
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ chip->ecc.hwctl(mtd, NAND_ECC_READ);
+
+ ret = nand_read_data_op(chip, p, eccsize, false);
+ if (ret)
+ return ret;
+
+ chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+ }
+
+ ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, false);
+ if (ret)
+ return ret;
+
+ ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
+ chip->ecc.total);
+ if (ret)
+ return ret;
+
+ eccsteps = chip->ecc.steps;
+ p = buf;
+
+ for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ int stat;
+
+ stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
+ if (stat == -EBADMSG &&
+ (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) {
+ /* check for empty pages with bitflips */
+ stat = nand_check_erased_ecc_chunk(p, eccsize,
+ &ecc_code[i], eccbytes,
+ NULL, 0,
+ chip->ecc.strength);
+ }
+
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ }
+ }
+ return max_bitflips;
+}
+
+/**
+ * nand_read_page_hwecc_oob_first - [REPLACEABLE] hw ecc, read oob first
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
+ *
+ * Hardware ECC for large page chips, require OOB to be read first. For this
+ * ECC mode, the write_page method is re-used from ECC_HW. These methods
+ * read/write ECC from the OOB area, unlike the ECC_HW_SYNDROME support with
+ * multiple ECC steps, follows the "infix ECC" scheme and reads/writes ECC from
+ * the data area, by overwriting the NAND manufacturer bad block markings.
+ */
+static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
+{
+ int i, eccsize = chip->ecc.size, ret;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *p = buf;
+ uint8_t *ecc_code = chip->ecc.code_buf;
+ uint8_t *ecc_calc = chip->ecc.calc_buf;
+ unsigned int max_bitflips = 0;
+
+ /* Read the OOB area first */
+ ret = nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize);
+ if (ret)
+ return ret;
+
+ ret = nand_read_page_op(chip, page, 0, NULL, 0);
+ if (ret)
+ return ret;
+
+ ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
+ chip->ecc.total);
+ if (ret)
+ return ret;
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ int stat;
+
+ chip->ecc.hwctl(mtd, NAND_ECC_READ);
+
+ ret = nand_read_data_op(chip, p, eccsize, false);
+ if (ret)
+ return ret;
+
+ chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+
+ stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL);
+ if (stat == -EBADMSG &&
+ (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) {
+ /* check for empty pages with bitflips */
+ stat = nand_check_erased_ecc_chunk(p, eccsize,
+ &ecc_code[i], eccbytes,
+ NULL, 0,
+ chip->ecc.strength);
+ }
+
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ }
+ }
+ return max_bitflips;
+}
+
+/**
+ * nand_read_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page read
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
+ *
+ * The hw generator calculates the error syndrome automatically. Therefore we
+ * need a special oob layout and handling.
+ */
+static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ int ret, i, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ int eccpadbytes = eccbytes + chip->ecc.prepad + chip->ecc.postpad;
+ uint8_t *p = buf;
+ uint8_t *oob = chip->oob_poi;
+ unsigned int max_bitflips = 0;
+
+ ret = nand_read_page_op(chip, page, 0, NULL, 0);
+ if (ret)
+ return ret;
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ int stat;
+
+ chip->ecc.hwctl(mtd, NAND_ECC_READ);
+
+ ret = nand_read_data_op(chip, p, eccsize, false);
+ if (ret)
+ return ret;
+
+ if (chip->ecc.prepad) {
+ ret = nand_read_data_op(chip, oob, chip->ecc.prepad,
+ false);
+ if (ret)
+ return ret;
+
+ oob += chip->ecc.prepad;
+ }
+
+ chip->ecc.hwctl(mtd, NAND_ECC_READSYN);
+
+ ret = nand_read_data_op(chip, oob, eccbytes, false);
+ if (ret)
+ return ret;
+
+ stat = chip->ecc.correct(mtd, p, oob, NULL);
+
+ oob += eccbytes;
+
+ if (chip->ecc.postpad) {
+ ret = nand_read_data_op(chip, oob, chip->ecc.postpad,
+ false);
+ if (ret)
+ return ret;
+
+ oob += chip->ecc.postpad;
+ }
+
+ if (stat == -EBADMSG &&
+ (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) {
+ /* check for empty pages with bitflips */
+ stat = nand_check_erased_ecc_chunk(p, chip->ecc.size,
+ oob - eccpadbytes,
+ eccpadbytes,
+ NULL, 0,
+ chip->ecc.strength);
+ }
+
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ }
+ }
+
+ /* Calculate remaining oob bytes */
+ i = mtd->oobsize - (oob - chip->oob_poi);
+ if (i) {
+ ret = nand_read_data_op(chip, oob, i, false);
+ if (ret)
+ return ret;
+ }
+
+ return max_bitflips;
+}
+
+/**
+ * nand_transfer_oob - [INTERN] Transfer oob to client buffer
+ * @mtd: mtd info structure
+ * @oob: oob destination address
+ * @ops: oob ops structure
+ * @len: size of oob to transfer
+ */
+static uint8_t *nand_transfer_oob(struct mtd_info *mtd, uint8_t *oob,
+ struct mtd_oob_ops *ops, size_t len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int ret;
+
+ switch (ops->mode) {
+
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_RAW:
+ memcpy(oob, chip->oob_poi + ops->ooboffs, len);
+ return oob + len;
+
+ case MTD_OPS_AUTO_OOB:
+ ret = mtd_ooblayout_get_databytes(mtd, oob, chip->oob_poi,
+ ops->ooboffs, len);
+ BUG_ON(ret);
+ return oob + len;
+
+ default:
+ BUG();
+ }
+ return NULL;
+}
+
+/**
+ * nand_setup_read_retry - [INTERN] Set the READ RETRY mode
+ * @mtd: MTD device structure
+ * @retry_mode: the retry mode to use
+ *
+ * Some vendors supply a special command to shift the Vt threshold, to be used
+ * when there are too many bitflips in a page (i.e., ECC error). After setting
+ * a new threshold, the host should retry reading the page.
+ */
+static int nand_setup_read_retry(struct mtd_info *mtd, int retry_mode)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ pr_debug("setting READ RETRY mode %d\n", retry_mode);
+
+ if (retry_mode >= chip->read_retries)
+ return -EINVAL;
+
+ if (!chip->setup_read_retry)
+ return -EOPNOTSUPP;
+
+ return chip->setup_read_retry(mtd, retry_mode);
+}
+
+/**
+ * nand_do_read_ops - [INTERN] Read data with ECC
+ * @mtd: MTD device structure
+ * @from: offset to read from
+ * @ops: oob ops structure
+ *
+ * Internal function. Called with chip held.
+ */
+static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops)
+{
+ int chipnr, page, realpage, col, bytes, aligned, oob_required;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int ret = 0;
+ uint32_t readlen = ops->len;
+ uint32_t oobreadlen = ops->ooblen;
+ uint32_t max_oobsize = mtd_oobavail(mtd, ops);
+
+ uint8_t *bufpoi, *oob, *buf;
+ int use_bufpoi;
+ unsigned int max_bitflips = 0;
+ int retry_mode = 0;
+ bool ecc_fail = false;
+
+ chipnr = (int)(from >> chip->chip_shift);
+ chip->select_chip(mtd, chipnr);
+
+ realpage = (int)(from >> chip->page_shift);
+ page = realpage & chip->pagemask;
+
+ col = (int)(from & (mtd->writesize - 1));
+
+ buf = ops->datbuf;
+ oob = ops->oobbuf;
+ oob_required = oob ? 1 : 0;
+
+ while (1) {
+ unsigned int ecc_failures = mtd->ecc_stats.failed;
+
+ bytes = min(mtd->writesize - col, readlen);
+ aligned = (bytes == mtd->writesize);
+
+ if (!aligned)
+ use_bufpoi = 1;
+ else if (chip->options & NAND_USE_BOUNCE_BUFFER)
+ use_bufpoi = !virt_addr_valid(buf) ||
+ !IS_ALIGNED((unsigned long)buf,
+ chip->buf_align);
+ else
+ use_bufpoi = 0;
+
+ /* Is the current page in the buffer? */
+ if (realpage != chip->pagebuf || oob) {
+ bufpoi = use_bufpoi ? chip->data_buf : buf;
+
+ if (use_bufpoi && aligned)
+ pr_debug("%s: using read bounce buffer for buf@%p\n",
+ __func__, buf);
+
+read_retry:
+ /*
+ * Now read the page into the buffer. Absent an error,
+ * the read methods return max bitflips per ecc step.
+ */
+ if (unlikely(ops->mode == MTD_OPS_RAW))
+ ret = chip->ecc.read_page_raw(mtd, chip, bufpoi,
+ oob_required,
+ page);
+ else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) &&
+ !oob)
+ ret = chip->ecc.read_subpage(mtd, chip,
+ col, bytes, bufpoi,
+ page);
+ else
+ ret = chip->ecc.read_page(mtd, chip, bufpoi,
+ oob_required, page);
+ if (ret < 0) {
+ if (use_bufpoi)
+ /* Invalidate page cache */
+ chip->pagebuf = -1;
+ break;
+ }
+
+ /* Transfer not aligned data */
+ if (use_bufpoi) {
+ if (!NAND_HAS_SUBPAGE_READ(chip) && !oob &&
+ !(mtd->ecc_stats.failed - ecc_failures) &&
+ (ops->mode != MTD_OPS_RAW)) {
+ chip->pagebuf = realpage;
+ chip->pagebuf_bitflips = ret;
+ } else {
+ /* Invalidate page cache */
+ chip->pagebuf = -1;
+ }
+ memcpy(buf, chip->data_buf + col, bytes);
+ }
+
+ if (unlikely(oob)) {
+ int toread = min(oobreadlen, max_oobsize);
+
+ if (toread) {
+ oob = nand_transfer_oob(mtd,
+ oob, ops, toread);
+ oobreadlen -= toread;
+ }
+ }
+
+ if (chip->options & NAND_NEED_READRDY) {
+ /* Apply delay or wait for ready/busy pin */
+ if (!chip->dev_ready)
+ udelay(chip->chip_delay);
+ else
+ nand_wait_ready(mtd);
+ }
+
+ if (mtd->ecc_stats.failed - ecc_failures) {
+ if (retry_mode + 1 < chip->read_retries) {
+ retry_mode++;
+ ret = nand_setup_read_retry(mtd,
+ retry_mode);
+ if (ret < 0)
+ break;
+
+ /* Reset failures; retry */
+ mtd->ecc_stats.failed = ecc_failures;
+ goto read_retry;
+ } else {
+ /* No more retry modes; real failure */
+ ecc_fail = true;
+ }
+ }
+
+ buf += bytes;
+ max_bitflips = max_t(unsigned int, max_bitflips, ret);
+ } else {
+ memcpy(buf, chip->data_buf + col, bytes);
+ buf += bytes;
+ max_bitflips = max_t(unsigned int, max_bitflips,
+ chip->pagebuf_bitflips);
+ }
+
+ readlen -= bytes;
+
+ /* Reset to retry mode 0 */
+ if (retry_mode) {
+ ret = nand_setup_read_retry(mtd, 0);
+ if (ret < 0)
+ break;
+ retry_mode = 0;
+ }
+
+ if (!readlen)
+ break;
+
+ /* For subsequent reads align to page boundary */
+ col = 0;
+ /* Increment page address */
+ realpage++;
+
+ page = realpage & chip->pagemask;
+ /* Check, if we cross a chip boundary */
+ if (!page) {
+ chipnr++;
+ chip->select_chip(mtd, -1);
+ chip->select_chip(mtd, chipnr);
+ }
+ }
+ chip->select_chip(mtd, -1);
+
+ ops->retlen = ops->len - (size_t) readlen;
+ if (oob)
+ ops->oobretlen = ops->ooblen - oobreadlen;
+
+ if (ret < 0)
+ return ret;
+
+ if (ecc_fail)
+ return -EBADMSG;
+
+ return max_bitflips;
+}
+
+/**
+ * nand_read_oob_std - [REPLACEABLE] the most common OOB data read function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @page: page number to read
+ */
+int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page)
+{
+ return nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize);
+}
+EXPORT_SYMBOL(nand_read_oob_std);
+
+/**
+ * nand_read_oob_syndrome - [REPLACEABLE] OOB data read function for HW ECC
+ * with syndromes
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @page: page number to read
+ */
+int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ int length = mtd->oobsize;
+ int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
+ int eccsize = chip->ecc.size;
+ uint8_t *bufpoi = chip->oob_poi;
+ int i, toread, sndrnd = 0, pos, ret;
+
+ ret = nand_read_page_op(chip, page, chip->ecc.size, NULL, 0);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < chip->ecc.steps; i++) {
+ if (sndrnd) {
+ int ret;
+
+ pos = eccsize + i * (eccsize + chunk);
+ if (mtd->writesize > 512)
+ ret = nand_change_read_column_op(chip, pos,
+ NULL, 0,
+ false);
+ else
+ ret = nand_read_page_op(chip, page, pos, NULL,
+ 0);
+
+ if (ret)
+ return ret;
+ } else
+ sndrnd = 1;
+ toread = min_t(int, length, chunk);
+
+ ret = nand_read_data_op(chip, bufpoi, toread, false);
+ if (ret)
+ return ret;
+
+ bufpoi += toread;
+ length -= toread;
+ }
+ if (length > 0) {
+ ret = nand_read_data_op(chip, bufpoi, length, false);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(nand_read_oob_syndrome);
+
+/**
+ * nand_write_oob_std - [REPLACEABLE] the most common OOB data write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @page: page number to write
+ */
+int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page)
+{
+ return nand_prog_page_op(chip, page, mtd->writesize, chip->oob_poi,
+ mtd->oobsize);
+}
+EXPORT_SYMBOL(nand_write_oob_std);
+
+/**
+ * nand_write_oob_syndrome - [REPLACEABLE] OOB data write function for HW ECC
+ * with syndrome - only for large page flash
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @page: page number to write
+ */
+int nand_write_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
+ int eccsize = chip->ecc.size, length = mtd->oobsize;
+ int ret, i, len, pos, sndcmd = 0, steps = chip->ecc.steps;
+ const uint8_t *bufpoi = chip->oob_poi;
+
+ /*
+ * data-ecc-data-ecc ... ecc-oob
+ * or
+ * data-pad-ecc-pad-data-pad .... ecc-pad-oob
+ */
+ if (!chip->ecc.prepad && !chip->ecc.postpad) {
+ pos = steps * (eccsize + chunk);
+ steps = 0;
+ } else
+ pos = eccsize;
+
+ ret = nand_prog_page_begin_op(chip, page, pos, NULL, 0);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < steps; i++) {
+ if (sndcmd) {
+ if (mtd->writesize <= 512) {
+ uint32_t fill = 0xFFFFFFFF;
+
+ len = eccsize;
+ while (len > 0) {
+ int num = min_t(int, len, 4);
+
+ ret = nand_write_data_op(chip, &fill,
+ num, false);
+ if (ret)
+ return ret;
+
+ len -= num;
+ }
+ } else {
+ pos = eccsize + i * (eccsize + chunk);
+ ret = nand_change_write_column_op(chip, pos,
+ NULL, 0,
+ false);
+ if (ret)
+ return ret;
+ }
+ } else
+ sndcmd = 1;
+ len = min_t(int, length, chunk);
+
+ ret = nand_write_data_op(chip, bufpoi, len, false);
+ if (ret)
+ return ret;
+
+ bufpoi += len;
+ length -= len;
+ }
+ if (length > 0) {
+ ret = nand_write_data_op(chip, bufpoi, length, false);
+ if (ret)
+ return ret;
+ }
+
+ return nand_prog_page_end_op(chip);
+}
+EXPORT_SYMBOL(nand_write_oob_syndrome);
+
+/**
+ * nand_do_read_oob - [INTERN] NAND read out-of-band
+ * @mtd: MTD device structure
+ * @from: offset to read from
+ * @ops: oob operations description structure
+ *
+ * NAND read out-of-band data from the spare area.
+ */
+static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops)
+{
+ unsigned int max_bitflips = 0;
+ int page, realpage, chipnr;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mtd_ecc_stats stats;
+ int readlen = ops->ooblen;
+ int len;
+ uint8_t *buf = ops->oobbuf;
+ int ret = 0;
+
+ pr_debug("%s: from = 0x%08Lx, len = %i\n",
+ __func__, (unsigned long long)from, readlen);
+
+ stats = mtd->ecc_stats;
+
+ len = mtd_oobavail(mtd, ops);
+
+ chipnr = (int)(from >> chip->chip_shift);
+ chip->select_chip(mtd, chipnr);
+
+ /* Shift to get page */
+ realpage = (int)(from >> chip->page_shift);
+ page = realpage & chip->pagemask;
+
+ while (1) {
+ if (ops->mode == MTD_OPS_RAW)
+ ret = chip->ecc.read_oob_raw(mtd, chip, page);
+ else
+ ret = chip->ecc.read_oob(mtd, chip, page);
+
+ if (ret < 0)
+ break;
+
+ len = min(len, readlen);
+ buf = nand_transfer_oob(mtd, buf, ops, len);
+
+ if (chip->options & NAND_NEED_READRDY) {
+ /* Apply delay or wait for ready/busy pin */
+ if (!chip->dev_ready)
+ udelay(chip->chip_delay);
+ else
+ nand_wait_ready(mtd);
+ }
+
+ max_bitflips = max_t(unsigned int, max_bitflips, ret);
+
+ readlen -= len;
+ if (!readlen)
+ break;
+
+ /* Increment page address */
+ realpage++;
+
+ page = realpage & chip->pagemask;
+ /* Check, if we cross a chip boundary */
+ if (!page) {
+ chipnr++;
+ chip->select_chip(mtd, -1);
+ chip->select_chip(mtd, chipnr);
+ }
+ }
+ chip->select_chip(mtd, -1);
+
+ ops->oobretlen = ops->ooblen - readlen;
+
+ if (ret < 0)
+ return ret;
+
+ if (mtd->ecc_stats.failed - stats.failed)
+ return -EBADMSG;
+
+ return max_bitflips;
+}
+
+/**
+ * nand_read_oob - [MTD Interface] NAND read data and/or out-of-band
+ * @mtd: MTD device structure
+ * @from: offset to read from
+ * @ops: oob operation description structure
+ *
+ * NAND read data and/or out-of-band data.
+ */
+static int nand_read_oob(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops)
+{
+ int ret;
+
+ ops->retlen = 0;
+
+ if (ops->mode != MTD_OPS_PLACE_OOB &&
+ ops->mode != MTD_OPS_AUTO_OOB &&
+ ops->mode != MTD_OPS_RAW)
+ return -ENOTSUPP;
+
+ nand_get_device(mtd, FL_READING);
+
+ if (!ops->datbuf)
+ ret = nand_do_read_oob(mtd, from, ops);
+ else
+ ret = nand_do_read_ops(mtd, from, ops);
+
+ nand_release_device(mtd);
+ return ret;
+}
+
+
+/**
+ * nand_write_page_raw - [INTERN] raw page write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ * @oob_required: must write chip->oob_poi to OOB
+ * @page: page number to write
+ *
+ * Not for syndrome calculating ECC controllers, which use a special oob layout.
+ */
+int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required, int page)
+{
+ int ret;
+
+ ret = nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);
+ if (ret)
+ return ret;
+
+ if (oob_required) {
+ ret = nand_write_data_op(chip, chip->oob_poi, mtd->oobsize,
+ false);
+ if (ret)
+ return ret;
+ }
+
+ return nand_prog_page_end_op(chip);
+}
+EXPORT_SYMBOL(nand_write_page_raw);
+
+/**
+ * nand_write_page_raw_syndrome - [INTERN] raw page write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ * @oob_required: must write chip->oob_poi to OOB
+ * @page: page number to write
+ *
+ * We need a special oob layout and handling even when ECC isn't checked.
+ */
+static int nand_write_page_raw_syndrome(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ const uint8_t *buf, int oob_required,
+ int page)
+{
+ int eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ uint8_t *oob = chip->oob_poi;
+ int steps, size, ret;
+
+ ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+ if (ret)
+ return ret;
+
+ for (steps = chip->ecc.steps; steps > 0; steps--) {
+ ret = nand_write_data_op(chip, buf, eccsize, false);
+ if (ret)
+ return ret;
+
+ buf += eccsize;
+
+ if (chip->ecc.prepad) {
+ ret = nand_write_data_op(chip, oob, chip->ecc.prepad,
+ false);
+ if (ret)
+ return ret;
+
+ oob += chip->ecc.prepad;
+ }
+
+ ret = nand_write_data_op(chip, oob, eccbytes, false);
+ if (ret)
+ return ret;
+
+ oob += eccbytes;
+
+ if (chip->ecc.postpad) {
+ ret = nand_write_data_op(chip, oob, chip->ecc.postpad,
+ false);
+ if (ret)
+ return ret;
+
+ oob += chip->ecc.postpad;
+ }
+ }
+
+ size = mtd->oobsize - (oob - chip->oob_poi);
+ if (size) {
+ ret = nand_write_data_op(chip, oob, size, false);
+ if (ret)
+ return ret;
+ }
+
+ return nand_prog_page_end_op(chip);
+}
+/**
+ * nand_write_page_swecc - [REPLACEABLE] software ECC based page write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ * @oob_required: must write chip->oob_poi to OOB
+ * @page: page number to write
+ */
+static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required,
+ int page)
+{
+ int i, eccsize = chip->ecc.size, ret;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *ecc_calc = chip->ecc.calc_buf;
+ const uint8_t *p = buf;
+
+ /* Software ECC calculation */
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
+ chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+
+ ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
+ chip->ecc.total);
+ if (ret)
+ return ret;
+
+ return chip->ecc.write_page_raw(mtd, chip, buf, 1, page);
+}
+
+/**
+ * nand_write_page_hwecc - [REPLACEABLE] hardware ECC based page write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ * @oob_required: must write chip->oob_poi to OOB
+ * @page: page number to write
+ */
+static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required,
+ int page)
+{
+ int i, eccsize = chip->ecc.size, ret;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *ecc_calc = chip->ecc.calc_buf;
+ const uint8_t *p = buf;
+
+ ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+ if (ret)
+ return ret;
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
+
+ ret = nand_write_data_op(chip, p, eccsize, false);
+ if (ret)
+ return ret;
+
+ chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+ }
+
+ ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
+ chip->ecc.total);
+ if (ret)
+ return ret;
+
+ ret = nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, false);
+ if (ret)
+ return ret;
+
+ return nand_prog_page_end_op(chip);
+}
+
+
+/**
+ * nand_write_subpage_hwecc - [REPLACEABLE] hardware ECC based subpage write
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @offset: column address of subpage within the page
+ * @data_len: data length
+ * @buf: data buffer
+ * @oob_required: must write chip->oob_poi to OOB
+ * @page: page number to write
+ */
+static int nand_write_subpage_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, uint32_t offset,
+ uint32_t data_len, const uint8_t *buf,
+ int oob_required, int page)
+{
+ uint8_t *oob_buf = chip->oob_poi;
+ uint8_t *ecc_calc = chip->ecc.calc_buf;
+ int ecc_size = chip->ecc.size;
+ int ecc_bytes = chip->ecc.bytes;
+ int ecc_steps = chip->ecc.steps;
+ uint32_t start_step = offset / ecc_size;
+ uint32_t end_step = (offset + data_len - 1) / ecc_size;
+ int oob_bytes = mtd->oobsize / ecc_steps;
+ int step, ret;
+
+ ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+ if (ret)
+ return ret;
+
+ for (step = 0; step < ecc_steps; step++) {
+ /* configure controller for WRITE access */
+ chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
+
+ /* write data (untouched subpages already masked by 0xFF) */
+ ret = nand_write_data_op(chip, buf, ecc_size, false);
+ if (ret)
+ return ret;
+
+ /* mask ECC of un-touched subpages by padding 0xFF */
+ if ((step < start_step) || (step > end_step))
+ memset(ecc_calc, 0xff, ecc_bytes);
+ else
+ chip->ecc.calculate(mtd, buf, ecc_calc);
+
+ /* mask OOB of un-touched subpages by padding 0xFF */
+ /* if oob_required, preserve OOB metadata of written subpage */
+ if (!oob_required || (step < start_step) || (step > end_step))
+ memset(oob_buf, 0xff, oob_bytes);
+
+ buf += ecc_size;
+ ecc_calc += ecc_bytes;
+ oob_buf += oob_bytes;
+ }
+
+ /* copy calculated ECC for whole page to chip->buffer->oob */
+ /* this include masked-value(0xFF) for unwritten subpages */
+ ecc_calc = chip->ecc.calc_buf;
+ ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
+ chip->ecc.total);
+ if (ret)
+ return ret;
+
+ /* write OOB buffer to NAND device */
+ ret = nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, false);
+ if (ret)
+ return ret;
+
+ return nand_prog_page_end_op(chip);
+}
+
+
+/**
+ * nand_write_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page write
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ * @oob_required: must write chip->oob_poi to OOB
+ * @page: page number to write
+ *
+ * The hw generator calculates the error syndrome automatically. Therefore we
+ * need a special oob layout and handling.
+ */
+static int nand_write_page_syndrome(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ const uint8_t *buf, int oob_required,
+ int page)
+{
+ int i, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ const uint8_t *p = buf;
+ uint8_t *oob = chip->oob_poi;
+ int ret;
+
+ ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+ if (ret)
+ return ret;
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
+
+ ret = nand_write_data_op(chip, p, eccsize, false);
+ if (ret)
+ return ret;
+
+ if (chip->ecc.prepad) {
+ ret = nand_write_data_op(chip, oob, chip->ecc.prepad,
+ false);
+ if (ret)
+ return ret;
+
+ oob += chip->ecc.prepad;
+ }
+
+ chip->ecc.calculate(mtd, p, oob);
+
+ ret = nand_write_data_op(chip, oob, eccbytes, false);
+ if (ret)
+ return ret;
+
+ oob += eccbytes;
+
+ if (chip->ecc.postpad) {
+ ret = nand_write_data_op(chip, oob, chip->ecc.postpad,
+ false);
+ if (ret)
+ return ret;
+
+ oob += chip->ecc.postpad;
+ }
+ }
+
+ /* Calculate remaining oob bytes */
+ i = mtd->oobsize - (oob - chip->oob_poi);
+ if (i) {
+ ret = nand_write_data_op(chip, oob, i, false);
+ if (ret)
+ return ret;
+ }
+
+ return nand_prog_page_end_op(chip);
+}
+
+/**
+ * nand_write_page - write one page
+ * @mtd: MTD device structure
+ * @chip: NAND chip descriptor
+ * @offset: address offset within the page
+ * @data_len: length of actual data to be written
+ * @buf: the data to write
+ * @oob_required: must write chip->oob_poi to OOB
+ * @page: page number to write
+ * @raw: use _raw version of write_page
+ */
+static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint32_t offset, int data_len, const uint8_t *buf,
+ int oob_required, int page, int raw)
+{
+ int status, subpage;
+
+ if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&
+ chip->ecc.write_subpage)
+ subpage = offset || (data_len < mtd->writesize);
+ else
+ subpage = 0;
+
+ if (unlikely(raw))
+ status = chip->ecc.write_page_raw(mtd, chip, buf,
+ oob_required, page);
+ else if (subpage)
+ status = chip->ecc.write_subpage(mtd, chip, offset, data_len,
+ buf, oob_required, page);
+ else
+ status = chip->ecc.write_page(mtd, chip, buf, oob_required,
+ page);
+
+ if (status < 0)
+ return status;
+
+ return 0;
+}
+
+/**
+ * nand_fill_oob - [INTERN] Transfer client buffer to oob
+ * @mtd: MTD device structure
+ * @oob: oob data buffer
+ * @len: oob data write length
+ * @ops: oob ops structure
+ */
+static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len,
+ struct mtd_oob_ops *ops)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int ret;
+
+ /*
+ * Initialise to all 0xFF, to avoid the possibility of left over OOB
+ * data from a previous OOB read.
+ */
+ memset(chip->oob_poi, 0xff, mtd->oobsize);
+
+ switch (ops->mode) {
+
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_RAW:
+ memcpy(chip->oob_poi + ops->ooboffs, oob, len);
+ return oob + len;
+
+ case MTD_OPS_AUTO_OOB:
+ ret = mtd_ooblayout_set_databytes(mtd, oob, chip->oob_poi,
+ ops->ooboffs, len);
+ BUG_ON(ret);
+ return oob + len;
+
+ default:
+ BUG();
+ }
+ return NULL;
+}
+
+#define NOTALIGNED(x) ((x & (chip->subpagesize - 1)) != 0)
+
+/**
+ * nand_do_write_ops - [INTERN] NAND write with ECC
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @ops: oob operations description structure
+ *
+ * NAND write with ECC.
+ */
+static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+{
+ int chipnr, realpage, page, column;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ uint32_t writelen = ops->len;
+
+ uint32_t oobwritelen = ops->ooblen;
+ uint32_t oobmaxlen = mtd_oobavail(mtd, ops);
+
+ uint8_t *oob = ops->oobbuf;
+ uint8_t *buf = ops->datbuf;
+ int ret;
+ int oob_required = oob ? 1 : 0;
+
+ ops->retlen = 0;
+ if (!writelen)
+ return 0;
+
+ /* Reject writes, which are not page aligned */
+ if (NOTALIGNED(to) || NOTALIGNED(ops->len)) {
+ pr_notice("%s: attempt to write non page aligned data\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ column = to & (mtd->writesize - 1);
+
+ chipnr = (int)(to >> chip->chip_shift);
+ chip->select_chip(mtd, chipnr);
+
+ /* Check, if it is write protected */
+ if (nand_check_wp(mtd)) {
+ ret = -EIO;
+ goto err_out;
+ }
+
+ realpage = (int)(to >> chip->page_shift);
+ page = realpage & chip->pagemask;
+
+ /* Invalidate the page cache, when we write to the cached page */
+ if (to <= ((loff_t)chip->pagebuf << chip->page_shift) &&
+ ((loff_t)chip->pagebuf << chip->page_shift) < (to + ops->len))
+ chip->pagebuf = -1;
+
+ /* Don't allow multipage oob writes with offset */
+ if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen)) {
+ ret = -EINVAL;
+ goto err_out;
+ }
+
+ while (1) {
+ int bytes = mtd->writesize;
+ uint8_t *wbuf = buf;
+ int use_bufpoi;
+ int part_pagewr = (column || writelen < mtd->writesize);
+
+ if (part_pagewr)
+ use_bufpoi = 1;
+ else if (chip->options & NAND_USE_BOUNCE_BUFFER)
+ use_bufpoi = !virt_addr_valid(buf) ||
+ !IS_ALIGNED((unsigned long)buf,
+ chip->buf_align);
+ else
+ use_bufpoi = 0;
+
+ /* Partial page write?, or need to use bounce buffer */
+ if (use_bufpoi) {
+ pr_debug("%s: using write bounce buffer for buf@%p\n",
+ __func__, buf);
+ if (part_pagewr)
+ bytes = min_t(int, bytes - column, writelen);
+ chip->pagebuf = -1;
+ memset(chip->data_buf, 0xff, mtd->writesize);
+ memcpy(&chip->data_buf[column], buf, bytes);
+ wbuf = chip->data_buf;
+ }
+
+ if (unlikely(oob)) {
+ size_t len = min(oobwritelen, oobmaxlen);
+ oob = nand_fill_oob(mtd, oob, len, ops);
+ oobwritelen -= len;
+ } else {
+ /* We still need to erase leftover OOB data */
+ memset(chip->oob_poi, 0xff, mtd->oobsize);
+ }
+
+ ret = nand_write_page(mtd, chip, column, bytes, wbuf,
+ oob_required, page,
+ (ops->mode == MTD_OPS_RAW));
+ if (ret)
+ break;
+
+ writelen -= bytes;
+ if (!writelen)
+ break;
+
+ column = 0;
+ buf += bytes;
+ realpage++;
+
+ page = realpage & chip->pagemask;
+ /* Check, if we cross a chip boundary */
+ if (!page) {
+ chipnr++;
+ chip->select_chip(mtd, -1);
+ chip->select_chip(mtd, chipnr);
+ }
+ }
+
+ ops->retlen = ops->len - writelen;
+ if (unlikely(oob))
+ ops->oobretlen = ops->ooblen;
+
+err_out:
+ chip->select_chip(mtd, -1);
+ return ret;
+}
+
+/**
+ * panic_nand_write - [MTD Interface] NAND write with ECC
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @len: number of bytes to write
+ * @retlen: pointer to variable to store the number of written bytes
+ * @buf: the data to write
+ *
+ * NAND write with ECC. Used when performing writes in interrupt context, this
+ * may for example be called by mtdoops when writing an oops while in panic.
+ */
+static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const uint8_t *buf)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int chipnr = (int)(to >> chip->chip_shift);
+ struct mtd_oob_ops ops;
+ int ret;
+
+ /* Grab the device */
+ panic_nand_get_device(chip, mtd, FL_WRITING);
+
+ chip->select_chip(mtd, chipnr);
+
+ /* Wait for the device to get ready */
+ panic_nand_wait(mtd, chip, 400);
+
+ memset(&ops, 0, sizeof(ops));
+ ops.len = len;
+ ops.datbuf = (uint8_t *)buf;
+ ops.mode = MTD_OPS_PLACE_OOB;
+
+ ret = nand_do_write_ops(mtd, to, &ops);
+
+ *retlen = ops.retlen;
+ return ret;
+}
+
+/**
+ * nand_do_write_oob - [MTD Interface] NAND write out-of-band
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @ops: oob operation description structure
+ *
+ * NAND write out-of-band.
+ */
+static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+{
+ int chipnr, page, status, len;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ pr_debug("%s: to = 0x%08x, len = %i\n",
+ __func__, (unsigned int)to, (int)ops->ooblen);
+
+ len = mtd_oobavail(mtd, ops);
+
+ /* Do not allow write past end of page */
+ if ((ops->ooboffs + ops->ooblen) > len) {
+ pr_debug("%s: attempt to write past end of page\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ chipnr = (int)(to >> chip->chip_shift);
+
+ /*
+ * Reset the chip. Some chips (like the Toshiba TC5832DC found in one
+ * of my DiskOnChip 2000 test units) will clear the whole data page too
+ * if we don't do this. I have no clue why, but I seem to have 'fixed'
+ * it in the doc2000 driver in August 1999. dwmw2.
+ */
+ nand_reset(chip, chipnr);
+
+ chip->select_chip(mtd, chipnr);
+
+ /* Shift to get page */
+ page = (int)(to >> chip->page_shift);
+
+ /* Check, if it is write protected */
+ if (nand_check_wp(mtd)) {
+ chip->select_chip(mtd, -1);
+ return -EROFS;
+ }
+
+ /* Invalidate the page cache, if we write to the cached page */
+ if (page == chip->pagebuf)
+ chip->pagebuf = -1;
+
+ nand_fill_oob(mtd, ops->oobbuf, ops->ooblen, ops);
+
+ if (ops->mode == MTD_OPS_RAW)
+ status = chip->ecc.write_oob_raw(mtd, chip, page & chip->pagemask);
+ else
+ status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
+
+ chip->select_chip(mtd, -1);
+
+ if (status)
+ return status;
+
+ ops->oobretlen = ops->ooblen;
+
+ return 0;
+}
+
+/**
+ * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @ops: oob operation description structure
+ */
+static int nand_write_oob(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+{
+ int ret = -ENOTSUPP;
+
+ ops->retlen = 0;
+
+ nand_get_device(mtd, FL_WRITING);
+
+ switch (ops->mode) {
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_AUTO_OOB:
+ case MTD_OPS_RAW:
+ break;
+
+ default:
+ goto out;
+ }
+
+ if (!ops->datbuf)
+ ret = nand_do_write_oob(mtd, to, ops);
+ else
+ ret = nand_do_write_ops(mtd, to, ops);
+
+out:
+ nand_release_device(mtd);
+ return ret;
+}
+
+/**
+ * single_erase - [GENERIC] NAND standard block erase command function
+ * @mtd: MTD device structure
+ * @page: the page address of the block which will be erased
+ *
+ * Standard erase command for NAND chips. Returns NAND status.
+ */
+static int single_erase(struct mtd_info *mtd, int page)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ unsigned int eraseblock;
+
+ /* Send commands to erase a block */
+ eraseblock = page >> (chip->phys_erase_shift - chip->page_shift);
+
+ return nand_erase_op(chip, eraseblock);
+}
+
+/**
+ * nand_erase - [MTD Interface] erase block(s)
+ * @mtd: MTD device structure
+ * @instr: erase instruction
+ *
+ * Erase one ore more blocks.
+ */
+static int nand_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+ return nand_erase_nand(mtd, instr, 0);
+}
+
+/**
+ * nand_erase_nand - [INTERN] erase block(s)
+ * @mtd: MTD device structure
+ * @instr: erase instruction
+ * @allowbbt: allow erasing the bbt area
+ *
+ * Erase one ore more blocks.
+ */
+int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
+ int allowbbt)
+{
+ int page, status, pages_per_block, ret, chipnr;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ loff_t len;
+
+ pr_debug("%s: start = 0x%012llx, len = %llu\n",
+ __func__, (unsigned long long)instr->addr,
+ (unsigned long long)instr->len);
+
+ if (check_offs_len(mtd, instr->addr, instr->len))
+ return -EINVAL;
+
+ /* Grab the lock and see if the device is available */
+ nand_get_device(mtd, FL_ERASING);
+
+ /* Shift to get first page */
+ page = (int)(instr->addr >> chip->page_shift);
+ chipnr = (int)(instr->addr >> chip->chip_shift);
+
+ /* Calculate pages in each block */
+ pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
+
+ /* Select the NAND device */
+ chip->select_chip(mtd, chipnr);
+
+ /* Check, if it is write protected */
+ if (nand_check_wp(mtd)) {
+ pr_debug("%s: device is write protected!\n",
+ __func__);
+ ret = -EIO;
+ goto erase_exit;
+ }
+
+ /* Loop through the pages */
+ len = instr->len;
+
+ while (len) {
+ /* Check if we have a bad block, we do not erase bad blocks! */
+ if (nand_block_checkbad(mtd, ((loff_t) page) <<
+ chip->page_shift, allowbbt)) {
+ pr_warn("%s: attempt to erase a bad block at page 0x%08x\n",
+ __func__, page);
+ ret = -EIO;
+ goto erase_exit;
+ }
+
+ /*
+ * Invalidate the page cache, if we erase the block which
+ * contains the current cached page.
+ */
+ if (page <= chip->pagebuf && chip->pagebuf <
+ (page + pages_per_block))
+ chip->pagebuf = -1;
+
+ status = chip->erase(mtd, page & chip->pagemask);
+
+ /* See if block erase succeeded */
+ if (status) {
+ pr_debug("%s: failed erase, page 0x%08x\n",
+ __func__, page);
+ ret = -EIO;
+ instr->fail_addr =
+ ((loff_t)page << chip->page_shift);
+ goto erase_exit;
+ }
+
+ /* Increment page address and decrement length */
+ len -= (1ULL << chip->phys_erase_shift);
+ page += pages_per_block;
+
+ /* Check, if we cross a chip boundary */
+ if (len && !(page & chip->pagemask)) {
+ chipnr++;
+ chip->select_chip(mtd, -1);
+ chip->select_chip(mtd, chipnr);
+ }
+ }
+
+ ret = 0;
+erase_exit:
+
+ /* Deselect and wake up anyone waiting on the device */
+ chip->select_chip(mtd, -1);
+ nand_release_device(mtd);
+
+ /* Return more or less happy */
+ return ret;
+}
+
+/**
+ * nand_sync - [MTD Interface] sync
+ * @mtd: MTD device structure
+ *
+ * Sync is actually a wait for chip ready function.
+ */
+static void nand_sync(struct mtd_info *mtd)
+{
+ pr_debug("%s: called\n", __func__);
+
+ /* Grab the lock and see if the device is available */
+ nand_get_device(mtd, FL_SYNCING);
+ /* Release it and go back */
+ nand_release_device(mtd);
+}
+
+/**
+ * nand_block_isbad - [MTD Interface] Check if block at offset is bad
+ * @mtd: MTD device structure
+ * @offs: offset relative to mtd start
+ */
+static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int chipnr = (int)(offs >> chip->chip_shift);
+ int ret;
+
+ /* Select the NAND device */
+ nand_get_device(mtd, FL_READING);
+ chip->select_chip(mtd, chipnr);
+
+ ret = nand_block_checkbad(mtd, offs, 0);
+
+ chip->select_chip(mtd, -1);
+ nand_release_device(mtd);
+
+ return ret;
+}
+
+/**
+ * nand_block_markbad - [MTD Interface] Mark block at the given offset as bad
+ * @mtd: MTD device structure
+ * @ofs: offset relative to mtd start
+ */
+static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ int ret;
+
+ ret = nand_block_isbad(mtd, ofs);
+ if (ret) {
+ /* If it was bad already, return success and do nothing */
+ if (ret > 0)
+ return 0;
+ return ret;
+ }
+
+ return nand_block_markbad_lowlevel(mtd, ofs);
+}
+
+/**
+ * nand_max_bad_blocks - [MTD Interface] Max number of bad blocks for an mtd
+ * @mtd: MTD device structure
+ * @ofs: offset relative to mtd start
+ * @len: length of mtd
+ */
+static int nand_max_bad_blocks(struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ u32 part_start_block;
+ u32 part_end_block;
+ u32 part_start_die;
+ u32 part_end_die;
+
+ /*
+ * max_bb_per_die and blocks_per_die used to determine
+ * the maximum bad block count.
+ */
+ if (!chip->max_bb_per_die || !chip->blocks_per_die)
+ return -ENOTSUPP;
+
+ /* Get the start and end of the partition in erase blocks. */
+ part_start_block = mtd_div_by_eb(ofs, mtd);
+ part_end_block = mtd_div_by_eb(len, mtd) + part_start_block - 1;
+
+ /* Get the start and end LUNs of the partition. */
+ part_start_die = part_start_block / chip->blocks_per_die;
+ part_end_die = part_end_block / chip->blocks_per_die;
+
+ /*
+ * Look up the bad blocks per unit and multiply by the number of units
+ * that the partition spans.
+ */
+ return chip->max_bb_per_die * (part_end_die - part_start_die + 1);
+}
+
+/**
+ * nand_default_set_features- [REPLACEABLE] set NAND chip features
+ * @mtd: MTD device structure
+ * @chip: nand chip info structure
+ * @addr: feature address.
+ * @subfeature_param: the subfeature parameters, a four bytes array.
+ */
+static int nand_default_set_features(struct mtd_info *mtd,
+ struct nand_chip *chip, int addr,
+ uint8_t *subfeature_param)
+{
+ return nand_set_features_op(chip, addr, subfeature_param);
+}
+
+/**
+ * nand_default_get_features- [REPLACEABLE] get NAND chip features
+ * @mtd: MTD device structure
+ * @chip: nand chip info structure
+ * @addr: feature address.
+ * @subfeature_param: the subfeature parameters, a four bytes array.
+ */
+static int nand_default_get_features(struct mtd_info *mtd,
+ struct nand_chip *chip, int addr,
+ uint8_t *subfeature_param)
+{
+ return nand_get_features_op(chip, addr, subfeature_param);
+}
+
+/**
+ * nand_get_set_features_notsupp - set/get features stub returning -ENOTSUPP
+ * @mtd: MTD device structure
+ * @chip: nand chip info structure
+ * @addr: feature address.
+ * @subfeature_param: the subfeature parameters, a four bytes array.
+ *
+ * Should be used by NAND controller drivers that do not support the SET/GET
+ * FEATURES operations.
+ */
+int nand_get_set_features_notsupp(struct mtd_info *mtd, struct nand_chip *chip,
+ int addr, u8 *subfeature_param)
+{
+ return -ENOTSUPP;
+}
+EXPORT_SYMBOL(nand_get_set_features_notsupp);
+
+/**
+ * nand_suspend - [MTD Interface] Suspend the NAND flash
+ * @mtd: MTD device structure
+ */
+static int nand_suspend(struct mtd_info *mtd)
+{
+ return nand_get_device(mtd, FL_PM_SUSPENDED);
+}
+
+/**
+ * nand_resume - [MTD Interface] Resume the NAND flash
+ * @mtd: MTD device structure
+ */
+static void nand_resume(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (chip->state == FL_PM_SUSPENDED)
+ nand_release_device(mtd);
+ else
+ pr_err("%s called for a chip which is not in suspended state\n",
+ __func__);
+}
+
+/**
+ * nand_shutdown - [MTD Interface] Finish the current NAND operation and
+ * prevent further operations
+ * @mtd: MTD device structure
+ */
+static void nand_shutdown(struct mtd_info *mtd)
+{
+ nand_get_device(mtd, FL_PM_SUSPENDED);
+}
+
+/* Set default functions */
+static void nand_set_defaults(struct nand_chip *chip)
+{
+ unsigned int busw = chip->options & NAND_BUSWIDTH_16;
+
+ /* check for proper chip_delay setup, set 20us if not */
+ if (!chip->chip_delay)
+ chip->chip_delay = 20;
+
+ /* check, if a user supplied command function given */
+ if (!chip->cmdfunc && !chip->exec_op)
+ chip->cmdfunc = nand_command;
+
+ /* check, if a user supplied wait function given */
+ if (chip->waitfunc == NULL)
+ chip->waitfunc = nand_wait;
+
+ if (!chip->select_chip)
+ chip->select_chip = nand_select_chip;
+
+ /* set for ONFI nand */
+ if (!chip->set_features)
+ chip->set_features = nand_default_set_features;
+ if (!chip->get_features)
+ chip->get_features = nand_default_get_features;
+
+ /* If called twice, pointers that depend on busw may need to be reset */
+ if (!chip->read_byte || chip->read_byte == nand_read_byte)
+ chip->read_byte = busw ? nand_read_byte16 : nand_read_byte;
+ if (!chip->read_word)
+ chip->read_word = nand_read_word;
+ if (!chip->block_bad)
+ chip->block_bad = nand_block_bad;
+ if (!chip->block_markbad)
+ chip->block_markbad = nand_default_block_markbad;
+ if (!chip->write_buf || chip->write_buf == nand_write_buf)
+ chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;
+ if (!chip->write_byte || chip->write_byte == nand_write_byte)
+ chip->write_byte = busw ? nand_write_byte16 : nand_write_byte;
+ if (!chip->read_buf || chip->read_buf == nand_read_buf)
+ chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;
+ if (!chip->scan_bbt)
+ chip->scan_bbt = nand_default_bbt;
+
+ if (!chip->controller) {
+ chip->controller = &chip->hwcontrol;
+ nand_hw_control_init(chip->controller);
+ }
+
+ if (!chip->buf_align)
+ chip->buf_align = 1;
+}
+
+/* Sanitize ONFI strings so we can safely print them */
+static void sanitize_string(uint8_t *s, size_t len)
+{
+ ssize_t i;
+
+ /* Null terminate */
+ s[len - 1] = 0;
+
+ /* Remove non printable chars */
+ for (i = 0; i < len - 1; i++) {
+ if (s[i] < ' ' || s[i] > 127)
+ s[i] = '?';
+ }
+
+ /* Remove trailing spaces */
+ strim(s);
+}
+
+static u16 onfi_crc16(u16 crc, u8 const *p, size_t len)
+{
+ int i;
+ while (len--) {
+ crc ^= *p++ << 8;
+ for (i = 0; i < 8; i++)
+ crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0);
+ }
+
+ return crc;
+}
+
+/* Parse the Extended Parameter Page. */
+static int nand_flash_detect_ext_param_page(struct nand_chip *chip,
+ struct nand_onfi_params *p)
+{
+ struct onfi_ext_param_page *ep;
+ struct onfi_ext_section *s;
+ struct onfi_ext_ecc_info *ecc;
+ uint8_t *cursor;
+ int ret;
+ int len;
+ int i;
+
+ len = le16_to_cpu(p->ext_param_page_length) * 16;
+ ep = kmalloc(len, GFP_KERNEL);
+ if (!ep)
+ return -ENOMEM;
+
+ /* Send our own NAND_CMD_PARAM. */
+ ret = nand_read_param_page_op(chip, 0, NULL, 0);
+ if (ret)
+ goto ext_out;
+
+ /* Use the Change Read Column command to skip the ONFI param pages. */
+ ret = nand_change_read_column_op(chip,
+ sizeof(*p) * p->num_of_param_pages,
+ ep, len, true);
+ if (ret)
+ goto ext_out;
+
+ ret = -EINVAL;
+ if ((onfi_crc16(ONFI_CRC_BASE, ((uint8_t *)ep) + 2, len - 2)
+ != le16_to_cpu(ep->crc))) {
+ pr_debug("fail in the CRC.\n");
+ goto ext_out;
+ }
+
+ /*
+ * Check the signature.
+ * Do not strictly follow the ONFI spec, maybe changed in future.
+ */
+ if (strncmp(ep->sig, "EPPS", 4)) {
+ pr_debug("The signature is invalid.\n");
+ goto ext_out;
+ }
+
+ /* find the ECC section. */
+ cursor = (uint8_t *)(ep + 1);
+ for (i = 0; i < ONFI_EXT_SECTION_MAX; i++) {
+ s = ep->sections + i;
+ if (s->type == ONFI_SECTION_TYPE_2)
+ break;
+ cursor += s->length * 16;
+ }
+ if (i == ONFI_EXT_SECTION_MAX) {
+ pr_debug("We can not find the ECC section.\n");
+ goto ext_out;
+ }
+
+ /* get the info we want. */
+ ecc = (struct onfi_ext_ecc_info *)cursor;
+
+ if (!ecc->codeword_size) {
+ pr_debug("Invalid codeword size\n");
+ goto ext_out;
+ }
+
+ chip->ecc_strength_ds = ecc->ecc_bits;
+ chip->ecc_step_ds = 1 << ecc->codeword_size;
+ ret = 0;
+
+ext_out:
+ kfree(ep);
+ return ret;
+}
+
+/*
+ * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.
+ */
+static int nand_flash_detect_onfi(struct nand_chip *chip)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct nand_onfi_params *p;
+ char id[4];
+ int i, ret, val;
+
+ /* Try ONFI for unknown chip or LP */
+ ret = nand_readid_op(chip, 0x20, id, sizeof(id));
+ if (ret || strncmp(id, "ONFI", 4))
+ return 0;
+
+ /* ONFI chip: allocate a buffer to hold its parameter page */
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ ret = nand_read_param_page_op(chip, 0, NULL, 0);
+ if (ret) {
+ ret = 0;
+ goto free_onfi_param_page;
+ }
+
+ for (i = 0; i < 3; i++) {
+ ret = nand_read_data_op(chip, p, sizeof(*p), true);
+ if (ret) {
+ ret = 0;
+ goto free_onfi_param_page;
+ }
+
+ if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
+ le16_to_cpu(p->crc)) {
+ break;
+ }
+ }
+
+ if (i == 3) {
+ pr_err("Could not find valid ONFI parameter page; aborting\n");
+ goto free_onfi_param_page;
+ }
+
+ /* Check version */
+ val = le16_to_cpu(p->revision);
+ if (val & (1 << 5))
+ chip->parameters.onfi.version = 23;
+ else if (val & (1 << 4))
+ chip->parameters.onfi.version = 22;
+ else if (val & (1 << 3))
+ chip->parameters.onfi.version = 21;
+ else if (val & (1 << 2))
+ chip->parameters.onfi.version = 20;
+ else if (val & (1 << 1))
+ chip->parameters.onfi.version = 10;
+
+ if (!chip->parameters.onfi.version) {
+ pr_info("unsupported ONFI version: %d\n", val);
+ goto free_onfi_param_page;
+ } else {
+ ret = 1;
+ }
+
+ sanitize_string(p->manufacturer, sizeof(p->manufacturer));
+ sanitize_string(p->model, sizeof(p->model));
+ strncpy(chip->parameters.model, p->model,
+ sizeof(chip->parameters.model) - 1);
+
+ mtd->writesize = le32_to_cpu(p->byte_per_page);
+
+ /*
+ * pages_per_block and blocks_per_lun may not be a power-of-2 size
+ * (don't ask me who thought of this...). MTD assumes that these
+ * dimensions will be power-of-2, so just truncate the remaining area.
+ */
+ mtd->erasesize = 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1);
+ mtd->erasesize *= mtd->writesize;
+
+ mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
+
+ /* See erasesize comment */
+ chip->chipsize = 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1);
+ chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count;
+ chip->bits_per_cell = p->bits_per_cell;
+
+ chip->max_bb_per_die = le16_to_cpu(p->bb_per_lun);
+ chip->blocks_per_die = le32_to_cpu(p->blocks_per_lun);
+
+ if (le16_to_cpu(p->features) & ONFI_FEATURE_16_BIT_BUS)
+ chip->options |= NAND_BUSWIDTH_16;
+
+ if (p->ecc_bits != 0xff) {
+ chip->ecc_strength_ds = p->ecc_bits;
+ chip->ecc_step_ds = 512;
+ } else if (chip->parameters.onfi.version >= 21 &&
+ (le16_to_cpu(p->features) & ONFI_FEATURE_EXT_PARAM_PAGE)) {
+
+ /*
+ * The nand_flash_detect_ext_param_page() uses the
+ * Change Read Column command which maybe not supported
+ * by the chip->cmdfunc. So try to update the chip->cmdfunc
+ * now. We do not replace user supplied command function.
+ */
+ if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
+ chip->cmdfunc = nand_command_lp;
+
+ /* The Extended Parameter Page is supported since ONFI 2.1. */
+ if (nand_flash_detect_ext_param_page(chip, p))
+ pr_warn("Failed to detect ONFI extended param page\n");
+ } else {
+ pr_warn("Could not retrieve ONFI ECC requirements\n");
+ }
+
+ /* Save some parameters from the parameter page for future use */
+ if (le16_to_cpu(p->opt_cmd) & ONFI_OPT_CMD_SET_GET_FEATURES) {
+ chip->parameters.supports_set_get_features = true;
+ bitmap_set(chip->parameters.get_feature_list,
+ ONFI_FEATURE_ADDR_TIMING_MODE, 1);
+ bitmap_set(chip->parameters.set_feature_list,
+ ONFI_FEATURE_ADDR_TIMING_MODE, 1);
+ }
+ chip->parameters.onfi.tPROG = le16_to_cpu(p->t_prog);
+ chip->parameters.onfi.tBERS = le16_to_cpu(p->t_bers);
+ chip->parameters.onfi.tR = le16_to_cpu(p->t_r);
+ chip->parameters.onfi.tCCS = le16_to_cpu(p->t_ccs);
+ chip->parameters.onfi.async_timing_mode =
+ le16_to_cpu(p->async_timing_mode);
+ chip->parameters.onfi.vendor_revision =
+ le16_to_cpu(p->vendor_revision);
+ memcpy(chip->parameters.onfi.vendor, p->vendor,
+ sizeof(p->vendor));
+
+free_onfi_param_page:
+ kfree(p);
+ return ret;
+}
+
+/*
+ * Check if the NAND chip is JEDEC compliant, returns 1 if it is, 0 otherwise.
+ */
+static int nand_flash_detect_jedec(struct nand_chip *chip)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct nand_jedec_params *p;
+ struct jedec_ecc_info *ecc;
+ int jedec_version = 0;
+ char id[5];
+ int i, val, ret;
+
+ /* Try JEDEC for unknown chip or LP */
+ ret = nand_readid_op(chip, 0x40, id, sizeof(id));
+ if (ret || strncmp(id, "JEDEC", sizeof(id)))
+ return 0;
+
+ /* JEDEC chip: allocate a buffer to hold its parameter page */
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ ret = nand_read_param_page_op(chip, 0x40, NULL, 0);
+ if (ret) {
+ ret = 0;
+ goto free_jedec_param_page;
+ }
+
+ for (i = 0; i < 3; i++) {
+ ret = nand_read_data_op(chip, p, sizeof(*p), true);
+ if (ret) {
+ ret = 0;
+ goto free_jedec_param_page;
+ }
+
+ if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 510) ==
+ le16_to_cpu(p->crc))
+ break;
+ }
+
+ if (i == 3) {
+ pr_err("Could not find valid JEDEC parameter page; aborting\n");
+ goto free_jedec_param_page;
+ }
+
+ /* Check version */
+ val = le16_to_cpu(p->revision);
+ if (val & (1 << 2))
+ jedec_version = 10;
+ else if (val & (1 << 1))
+ jedec_version = 1; /* vendor specific version */
+
+ if (!jedec_version) {
+ pr_info("unsupported JEDEC version: %d\n", val);
+ goto free_jedec_param_page;
+ }
+
+ sanitize_string(p->manufacturer, sizeof(p->manufacturer));
+ sanitize_string(p->model, sizeof(p->model));
+ strncpy(chip->parameters.model, p->model,
+ sizeof(chip->parameters.model) - 1);
+
+ mtd->writesize = le32_to_cpu(p->byte_per_page);
+
+ /* Please reference to the comment for nand_flash_detect_onfi. */
+ mtd->erasesize = 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1);
+ mtd->erasesize *= mtd->writesize;
+
+ mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
+
+ /* Please reference to the comment for nand_flash_detect_onfi. */
+ chip->chipsize = 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1);
+ chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count;
+ chip->bits_per_cell = p->bits_per_cell;
+
+ if (le16_to_cpu(p->features) & JEDEC_FEATURE_16_BIT_BUS)
+ chip->options |= NAND_BUSWIDTH_16;
+
+ /* ECC info */
+ ecc = &p->ecc_info[0];
+
+ if (ecc->codeword_size >= 9) {
+ chip->ecc_strength_ds = ecc->ecc_bits;
+ chip->ecc_step_ds = 1 << ecc->codeword_size;
+ } else {
+ pr_warn("Invalid codeword size\n");
+ }
+
+free_jedec_param_page:
+ kfree(p);
+ return ret;
+}
+
+/*
+ * nand_id_has_period - Check if an ID string has a given wraparound period
+ * @id_data: the ID string
+ * @arrlen: the length of the @id_data array
+ * @period: the period of repitition
+ *
+ * Check if an ID string is repeated within a given sequence of bytes at
+ * specific repetition interval period (e.g., {0x20,0x01,0x7F,0x20} has a
+ * period of 3). This is a helper function for nand_id_len(). Returns non-zero
+ * if the repetition has a period of @period; otherwise, returns zero.
+ */
+static int nand_id_has_period(u8 *id_data, int arrlen, int period)
+{
+ int i, j;
+ for (i = 0; i < period; i++)
+ for (j = i + period; j < arrlen; j += period)
+ if (id_data[i] != id_data[j])
+ return 0;
+ return 1;
+}
+
+/*
+ * nand_id_len - Get the length of an ID string returned by CMD_READID
+ * @id_data: the ID string
+ * @arrlen: the length of the @id_data array
+
+ * Returns the length of the ID string, according to known wraparound/trailing
+ * zero patterns. If no pattern exists, returns the length of the array.
+ */
+static int nand_id_len(u8 *id_data, int arrlen)
+{
+ int last_nonzero, period;
+
+ /* Find last non-zero byte */
+ for (last_nonzero = arrlen - 1; last_nonzero >= 0; last_nonzero--)
+ if (id_data[last_nonzero])
+ break;
+
+ /* All zeros */
+ if (last_nonzero < 0)
+ return 0;
+
+ /* Calculate wraparound period */
+ for (period = 1; period < arrlen; period++)
+ if (nand_id_has_period(id_data, arrlen, period))
+ break;
+
+ /* There's a repeated pattern */
+ if (period < arrlen)
+ return period;
+
+ /* There are trailing zeros */
+ if (last_nonzero < arrlen - 1)
+ return last_nonzero + 1;
+
+ /* No pattern detected */
+ return arrlen;
+}
+
+/* Extract the bits of per cell from the 3rd byte of the extended ID */
+static int nand_get_bits_per_cell(u8 cellinfo)
+{
+ int bits;
+
+ bits = cellinfo & NAND_CI_CELLTYPE_MSK;
+ bits >>= NAND_CI_CELLTYPE_SHIFT;
+ return bits + 1;
+}
+
+/*
+ * Many new NAND share similar device ID codes, which represent the size of the
+ * chip. The rest of the parameters must be decoded according to generic or
+ * manufacturer-specific "extended ID" decoding patterns.
+ */
+void nand_decode_ext_id(struct nand_chip *chip)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int extid;
+ u8 *id_data = chip->id.data;
+ /* The 3rd id byte holds MLC / multichip data */
+ chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]);
+ /* The 4th id byte is the important one */
+ extid = id_data[3];
+
+ /* Calc pagesize */
+ mtd->writesize = 1024 << (extid & 0x03);
+ extid >>= 2;
+ /* Calc oobsize */
+ mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9);
+ extid >>= 2;
+ /* Calc blocksize. Blocksize is multiples of 64KiB */
+ mtd->erasesize = (64 * 1024) << (extid & 0x03);
+ extid >>= 2;
+ /* Get buswidth information */
+ if (extid & 0x1)
+ chip->options |= NAND_BUSWIDTH_16;
+}
+EXPORT_SYMBOL_GPL(nand_decode_ext_id);
+
+/*
+ * Old devices have chip data hardcoded in the device ID table. nand_decode_id
+ * decodes a matching ID table entry and assigns the MTD size parameters for
+ * the chip.
+ */
+static void nand_decode_id(struct nand_chip *chip, struct nand_flash_dev *type)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ mtd->erasesize = type->erasesize;
+ mtd->writesize = type->pagesize;
+ mtd->oobsize = mtd->writesize / 32;
+
+ /* All legacy ID NAND are small-page, SLC */
+ chip->bits_per_cell = 1;
+}
+
+/*
+ * Set the bad block marker/indicator (BBM/BBI) patterns according to some
+ * heuristic patterns using various detected parameters (e.g., manufacturer,
+ * page size, cell-type information).
+ */
+static void nand_decode_bbm_options(struct nand_chip *chip)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ /* Set the bad block position */
+ if (mtd->writesize > 512 || (chip->options & NAND_BUSWIDTH_16))
+ chip->badblockpos = NAND_LARGE_BADBLOCK_POS;
+ else
+ chip->badblockpos = NAND_SMALL_BADBLOCK_POS;
+}
+
+static inline bool is_full_id_nand(struct nand_flash_dev *type)
+{
+ return type->id_len;
+}
+
+static bool find_full_id_nand(struct nand_chip *chip,
+ struct nand_flash_dev *type)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ u8 *id_data = chip->id.data;
+
+ if (!strncmp(type->id, id_data, type->id_len)) {
+ mtd->writesize = type->pagesize;
+ mtd->erasesize = type->erasesize;
+ mtd->oobsize = type->oobsize;
+
+ chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]);
+ chip->chipsize = (uint64_t)type->chipsize << 20;
+ chip->options |= type->options;
+ chip->ecc_strength_ds = NAND_ECC_STRENGTH(type);
+ chip->ecc_step_ds = NAND_ECC_STEP(type);
+ chip->onfi_timing_mode_default =
+ type->onfi_timing_mode_default;
+
+ strncpy(chip->parameters.model, type->name,
+ sizeof(chip->parameters.model) - 1);
+
+ return true;
+ }
+ return false;
+}
+
+/*
+ * Manufacturer detection. Only used when the NAND is not ONFI or JEDEC
+ * compliant and does not have a full-id or legacy-id entry in the nand_ids
+ * table.
+ */
+static void nand_manufacturer_detect(struct nand_chip *chip)
+{
+ /*
+ * Try manufacturer detection if available and use
+ * nand_decode_ext_id() otherwise.
+ */
+ if (chip->manufacturer.desc && chip->manufacturer.desc->ops &&
+ chip->manufacturer.desc->ops->detect) {
+ /* The 3rd id byte holds MLC / multichip data */
+ chip->bits_per_cell = nand_get_bits_per_cell(chip->id.data[2]);
+ chip->manufacturer.desc->ops->detect(chip);
+ } else {
+ nand_decode_ext_id(chip);
+ }
+}
+
+/*
+ * Manufacturer initialization. This function is called for all NANDs including
+ * ONFI and JEDEC compliant ones.
+ * Manufacturer drivers should put all their specific initialization code in
+ * their ->init() hook.
+ */
+static int nand_manufacturer_init(struct nand_chip *chip)
+{
+ if (!chip->manufacturer.desc || !chip->manufacturer.desc->ops ||
+ !chip->manufacturer.desc->ops->init)
+ return 0;
+
+ return chip->manufacturer.desc->ops->init(chip);
+}
+
+/*
+ * Manufacturer cleanup. This function is called for all NANDs including
+ * ONFI and JEDEC compliant ones.
+ * Manufacturer drivers should put all their specific cleanup code in their
+ * ->cleanup() hook.
+ */
+static void nand_manufacturer_cleanup(struct nand_chip *chip)
+{
+ /* Release manufacturer private data */
+ if (chip->manufacturer.desc && chip->manufacturer.desc->ops &&
+ chip->manufacturer.desc->ops->cleanup)
+ chip->manufacturer.desc->ops->cleanup(chip);
+}
+
+/*
+ * Get the flash and manufacturer id and lookup if the type is supported.
+ */
+static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type)
+{
+ const struct nand_manufacturer *manufacturer;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int busw, ret;
+ u8 *id_data = chip->id.data;
+ u8 maf_id, dev_id;
+
+ /*
+ * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
+ * after power-up.
+ */
+ ret = nand_reset(chip, 0);
+ if (ret)
+ return ret;
+
+ /* Select the device */
+ chip->select_chip(mtd, 0);
+
+ /* Send the command for reading device ID */
+ ret = nand_readid_op(chip, 0, id_data, 2);
+ if (ret)
+ return ret;
+
+ /* Read manufacturer and device IDs */
+ maf_id = id_data[0];
+ dev_id = id_data[1];
+
+ /*
+ * Try again to make sure, as some systems the bus-hold or other
+ * interface concerns can cause random data which looks like a
+ * possibly credible NAND flash to appear. If the two results do
+ * not match, ignore the device completely.
+ */
+
+ /* Read entire ID string */
+ ret = nand_readid_op(chip, 0, id_data, sizeof(chip->id.data));
+ if (ret)
+ return ret;
+
+ if (id_data[0] != maf_id || id_data[1] != dev_id) {
+ pr_info("second ID read did not match %02x,%02x against %02x,%02x\n",
+ maf_id, dev_id, id_data[0], id_data[1]);
+ return -ENODEV;
+ }
+
+ chip->id.len = nand_id_len(id_data, ARRAY_SIZE(chip->id.data));
+
+ /* Try to identify manufacturer */
+ manufacturer = nand_get_manufacturer(maf_id);
+ chip->manufacturer.desc = manufacturer;
+
+ if (!type)
+ type = nand_flash_ids;
+
+ /*
+ * Save the NAND_BUSWIDTH_16 flag before letting auto-detection logic
+ * override it.
+ * This is required to make sure initial NAND bus width set by the
+ * NAND controller driver is coherent with the real NAND bus width
+ * (extracted by auto-detection code).
+ */
+ busw = chip->options & NAND_BUSWIDTH_16;
+
+ /*
+ * The flag is only set (never cleared), reset it to its default value
+ * before starting auto-detection.
+ */
+ chip->options &= ~NAND_BUSWIDTH_16;
+
+ for (; type->name != NULL; type++) {
+ if (is_full_id_nand(type)) {
+ if (find_full_id_nand(chip, type))
+ goto ident_done;
+ } else if (dev_id == type->dev_id) {
+ break;
+ }
+ }
+
+ chip->parameters.onfi.version = 0;
+ if (!type->name || !type->pagesize) {
+ /* Check if the chip is ONFI compliant */
+ ret = nand_flash_detect_onfi(chip);
+ if (ret < 0)
+ return ret;
+ else if (ret)
+ goto ident_done;
+
+ /* Check if the chip is JEDEC compliant */
+ ret = nand_flash_detect_jedec(chip);
+ if (ret < 0)
+ return ret;
+ else if (ret)
+ goto ident_done;
+ }
+
+ if (!type->name)
+ return -ENODEV;
+
+ strncpy(chip->parameters.model, type->name,
+ sizeof(chip->parameters.model) - 1);
+
+ chip->chipsize = (uint64_t)type->chipsize << 20;
+
+ if (!type->pagesize)
+ nand_manufacturer_detect(chip);
+ else
+ nand_decode_id(chip, type);
+
+ /* Get chip options */
+ chip->options |= type->options;
+
+ident_done:
+ if (!mtd->name)
+ mtd->name = chip->parameters.model;
+
+ if (chip->options & NAND_BUSWIDTH_AUTO) {
+ WARN_ON(busw & NAND_BUSWIDTH_16);
+ nand_set_defaults(chip);
+ } else if (busw != (chip->options & NAND_BUSWIDTH_16)) {
+ /*
+ * Check, if buswidth is correct. Hardware drivers should set
+ * chip correct!
+ */
+ pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
+ maf_id, dev_id);
+ pr_info("%s %s\n", nand_manufacturer_name(manufacturer),
+ mtd->name);
+ pr_warn("bus width %d instead of %d bits\n", busw ? 16 : 8,
+ (chip->options & NAND_BUSWIDTH_16) ? 16 : 8);
+ return -EINVAL;
+ }
+
+ nand_decode_bbm_options(chip);
+
+ /* Calculate the address shift from the page size */
+ chip->page_shift = ffs(mtd->writesize) - 1;
+ /* Convert chipsize to number of pages per chip -1 */
+ chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;
+
+ chip->bbt_erase_shift = chip->phys_erase_shift =
+ ffs(mtd->erasesize) - 1;
+ if (chip->chipsize & 0xffffffff)
+ chip->chip_shift = ffs((unsigned)chip->chipsize) - 1;
+ else {
+ chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32));
+ chip->chip_shift += 32 - 1;
+ }
+
+ if (chip->chip_shift - chip->page_shift > 16)
+ chip->options |= NAND_ROW_ADDR_3;
+
+ chip->badblockbits = 8;
+ chip->erase = single_erase;
+
+ /* Do not replace user supplied command function! */
+ if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
+ chip->cmdfunc = nand_command_lp;
+
+ pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
+ maf_id, dev_id);
+ pr_info("%s %s\n", nand_manufacturer_name(manufacturer),
+ chip->parameters.model);
+ pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, OOB size: %d\n",
+ (int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC",
+ mtd->erasesize >> 10, mtd->writesize, mtd->oobsize);
+ return 0;
+}
+
+static const char * const nand_ecc_modes[] = {
+ [NAND_ECC_NONE] = "none",
+ [NAND_ECC_SOFT] = "soft",
+ [NAND_ECC_HW] = "hw",
+ [NAND_ECC_HW_SYNDROME] = "hw_syndrome",
+ [NAND_ECC_HW_OOB_FIRST] = "hw_oob_first",
+ [NAND_ECC_ON_DIE] = "on-die",
+};
+
+static int of_get_nand_ecc_mode(struct device_node *np)
+{
+ const char *pm;
+ int err, i;
+
+ err = of_property_read_string(np, "nand-ecc-mode", &pm);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < ARRAY_SIZE(nand_ecc_modes); i++)
+ if (!strcasecmp(pm, nand_ecc_modes[i]))
+ return i;
+
+ /*
+ * For backward compatibility we support few obsoleted values that don't
+ * have their mappings into nand_ecc_modes_t anymore (they were merged
+ * with other enums).
+ */
+ if (!strcasecmp(pm, "soft_bch"))
+ return NAND_ECC_SOFT;
+
+ return -ENODEV;
+}
+
+static const char * const nand_ecc_algos[] = {
+ [NAND_ECC_HAMMING] = "hamming",
+ [NAND_ECC_BCH] = "bch",
+};
+
+static int of_get_nand_ecc_algo(struct device_node *np)
+{
+ const char *pm;
+ int err, i;
+
+ err = of_property_read_string(np, "nand-ecc-algo", &pm);
+ if (!err) {
+ for (i = NAND_ECC_HAMMING; i < ARRAY_SIZE(nand_ecc_algos); i++)
+ if (!strcasecmp(pm, nand_ecc_algos[i]))
+ return i;
+ return -ENODEV;
+ }
+
+ /*
+ * For backward compatibility we also read "nand-ecc-mode" checking
+ * for some obsoleted values that were specifying ECC algorithm.
+ */
+ err = of_property_read_string(np, "nand-ecc-mode", &pm);
+ if (err < 0)
+ return err;
+
+ if (!strcasecmp(pm, "soft"))
+ return NAND_ECC_HAMMING;
+ else if (!strcasecmp(pm, "soft_bch"))
+ return NAND_ECC_BCH;
+
+ return -ENODEV;
+}
+
+static int of_get_nand_ecc_step_size(struct device_node *np)
+{
+ int ret;
+ u32 val;
+
+ ret = of_property_read_u32(np, "nand-ecc-step-size", &val);
+ return ret ? ret : val;
+}
+
+static int of_get_nand_ecc_strength(struct device_node *np)
+{
+ int ret;
+ u32 val;
+
+ ret = of_property_read_u32(np, "nand-ecc-strength", &val);
+ return ret ? ret : val;
+}
+
+static int of_get_nand_bus_width(struct device_node *np)
+{
+ u32 val;
+
+ if (of_property_read_u32(np, "nand-bus-width", &val))
+ return 8;
+
+ switch (val) {
+ case 8:
+ case 16:
+ return val;
+ default:
+ return -EIO;
+ }
+}
+
+static bool of_get_nand_on_flash_bbt(struct device_node *np)
+{
+ return of_property_read_bool(np, "nand-on-flash-bbt");
+}
+
+static int nand_dt_init(struct nand_chip *chip)
+{
+ struct device_node *dn = nand_get_flash_node(chip);
+ int ecc_mode, ecc_algo, ecc_strength, ecc_step;
+
+ if (!dn)
+ return 0;
+
+ if (of_get_nand_bus_width(dn) == 16)
+ chip->options |= NAND_BUSWIDTH_16;
+
+ if (of_get_nand_on_flash_bbt(dn))
+ chip->bbt_options |= NAND_BBT_USE_FLASH;
+
+ ecc_mode = of_get_nand_ecc_mode(dn);
+ ecc_algo = of_get_nand_ecc_algo(dn);
+ ecc_strength = of_get_nand_ecc_strength(dn);
+ ecc_step = of_get_nand_ecc_step_size(dn);
+
+ if (ecc_mode >= 0)
+ chip->ecc.mode = ecc_mode;
+
+ if (ecc_algo >= 0)
+ chip->ecc.algo = ecc_algo;
+
+ if (ecc_strength >= 0)
+ chip->ecc.strength = ecc_strength;
+
+ if (ecc_step > 0)
+ chip->ecc.size = ecc_step;
+
+ if (of_property_read_bool(dn, "nand-ecc-maximize"))
+ chip->ecc.options |= NAND_ECC_MAXIMIZE;
+
+ return 0;
+}
+
+/**
+ * nand_scan_ident - [NAND Interface] Scan for the NAND device
+ * @mtd: MTD device structure
+ * @maxchips: number of chips to scan for
+ * @table: alternative NAND ID table
+ *
+ * This is the first phase of the normal nand_scan() function. It reads the
+ * flash ID and sets up MTD fields accordingly.
+ *
+ */
+int nand_scan_ident(struct mtd_info *mtd, int maxchips,
+ struct nand_flash_dev *table)
+{
+ int i, nand_maf_id, nand_dev_id;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int ret;
+
+ /* Enforce the right timings for reset/detection */
+ onfi_fill_data_interface(chip, NAND_SDR_IFACE, 0);
+
+ ret = nand_dt_init(chip);
+ if (ret)
+ return ret;
+
+ if (!mtd->name && mtd->dev.parent)
+ mtd->name = dev_name(mtd->dev.parent);
+
+ /*
+ * ->cmdfunc() is legacy and will only be used if ->exec_op() is not
+ * populated.
+ */
+ if (!chip->exec_op) {
+ /*
+ * Default functions assigned for ->cmdfunc() and
+ * ->select_chip() both expect ->cmd_ctrl() to be populated.
+ */
+ if ((!chip->cmdfunc || !chip->select_chip) && !chip->cmd_ctrl) {
+ pr_err("->cmd_ctrl() should be provided\n");
+ return -EINVAL;
+ }
+ }
+
+ /* Set the default functions */
+ nand_set_defaults(chip);
+
+ /* Read the flash type */
+ ret = nand_detect(chip, table);
+ if (ret) {
+ if (!(chip->options & NAND_SCAN_SILENT_NODEV))
+ pr_warn("No NAND device found\n");
+ chip->select_chip(mtd, -1);
+ return ret;
+ }
+
+ nand_maf_id = chip->id.data[0];
+ nand_dev_id = chip->id.data[1];
+
+ chip->select_chip(mtd, -1);
+
+ /* Check for a chip array */
+ for (i = 1; i < maxchips; i++) {
+ u8 id[2];
+
+ /* See comment in nand_get_flash_type for reset */
+ nand_reset(chip, i);
+
+ chip->select_chip(mtd, i);
+ /* Send the command for reading device ID */
+ nand_readid_op(chip, 0, id, sizeof(id));
+ /* Read manufacturer and device IDs */
+ if (nand_maf_id != id[0] || nand_dev_id != id[1]) {
+ chip->select_chip(mtd, -1);
+ break;
+ }
+ chip->select_chip(mtd, -1);
+ }
+ if (i > 1)
+ pr_info("%d chips detected\n", i);
+
+ /* Store the number of chips and calc total size for mtd */
+ chip->numchips = i;
+ mtd->size = i * chip->chipsize;
+
+ return 0;
+}
+EXPORT_SYMBOL(nand_scan_ident);
+
+static int nand_set_ecc_soft_ops(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+
+ if (WARN_ON(ecc->mode != NAND_ECC_SOFT))
+ return -EINVAL;
+
+ switch (ecc->algo) {
+ case NAND_ECC_HAMMING:
+ ecc->calculate = nand_calculate_ecc;
+ ecc->correct = nand_correct_data;
+ ecc->read_page = nand_read_page_swecc;
+ ecc->read_subpage = nand_read_subpage;
+ ecc->write_page = nand_write_page_swecc;
+ ecc->read_page_raw = nand_read_page_raw;
+ ecc->write_page_raw = nand_write_page_raw;
+ ecc->read_oob = nand_read_oob_std;
+ ecc->write_oob = nand_write_oob_std;
+ if (!ecc->size)
+ ecc->size = 256;
+ ecc->bytes = 3;
+ ecc->strength = 1;
+ return 0;
+ case NAND_ECC_BCH:
+ if (!mtd_nand_has_bch()) {
+ WARN(1, "CONFIG_MTD_NAND_ECC_BCH not enabled\n");
+ return -EINVAL;
+ }
+ ecc->calculate = nand_bch_calculate_ecc;
+ ecc->correct = nand_bch_correct_data;
+ ecc->read_page = nand_read_page_swecc;
+ ecc->read_subpage = nand_read_subpage;
+ ecc->write_page = nand_write_page_swecc;
+ ecc->read_page_raw = nand_read_page_raw;
+ ecc->write_page_raw = nand_write_page_raw;
+ ecc->read_oob = nand_read_oob_std;
+ ecc->write_oob = nand_write_oob_std;
+
+ /*
+ * Board driver should supply ecc.size and ecc.strength
+ * values to select how many bits are correctable.
+ * Otherwise, default to 4 bits for large page devices.
+ */
+ if (!ecc->size && (mtd->oobsize >= 64)) {
+ ecc->size = 512;
+ ecc->strength = 4;
+ }
+
+ /*
+ * if no ecc placement scheme was provided pickup the default
+ * large page one.
+ */
+ if (!mtd->ooblayout) {
+ /* handle large page devices only */
+ if (mtd->oobsize < 64) {
+ WARN(1, "OOB layout is required when using software BCH on small pages\n");
+ return -EINVAL;
+ }
+
+ mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
+
+ }
+
+ /*
+ * We can only maximize ECC config when the default layout is
+ * used, otherwise we don't know how many bytes can really be
+ * used.
+ */
+ if (mtd->ooblayout == &nand_ooblayout_lp_ops &&
+ ecc->options & NAND_ECC_MAXIMIZE) {
+ int steps, bytes;
+
+ /* Always prefer 1k blocks over 512bytes ones */
+ ecc->size = 1024;
+ steps = mtd->writesize / ecc->size;
+
+ /* Reserve 2 bytes for the BBM */
+ bytes = (mtd->oobsize - 2) / steps;
+ ecc->strength = bytes * 8 / fls(8 * ecc->size);
+ }
+
+ /* See nand_bch_init() for details. */
+ ecc->bytes = 0;
+ ecc->priv = nand_bch_init(mtd);
+ if (!ecc->priv) {
+ WARN(1, "BCH ECC initialization failed!\n");
+ return -EINVAL;
+ }
+ return 0;
+ default:
+ WARN(1, "Unsupported ECC algorithm!\n");
+ return -EINVAL;
+ }
+}
+
+/**
+ * nand_check_ecc_caps - check the sanity of preset ECC settings
+ * @chip: nand chip info structure
+ * @caps: ECC caps info structure
+ * @oobavail: OOB size that the ECC engine can use
+ *
+ * When ECC step size and strength are already set, check if they are supported
+ * by the controller and the calculated ECC bytes fit within the chip's OOB.
+ * On success, the calculated ECC bytes is set.
+ */
+int nand_check_ecc_caps(struct nand_chip *chip,
+ const struct nand_ecc_caps *caps, int oobavail)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ const struct nand_ecc_step_info *stepinfo;
+ int preset_step = chip->ecc.size;
+ int preset_strength = chip->ecc.strength;
+ int nsteps, ecc_bytes;
+ int i, j;
+
+ if (WARN_ON(oobavail < 0))
+ return -EINVAL;
+
+ if (!preset_step || !preset_strength)
+ return -ENODATA;
+
+ nsteps = mtd->writesize / preset_step;
+
+ for (i = 0; i < caps->nstepinfos; i++) {
+ stepinfo = &caps->stepinfos[i];
+
+ if (stepinfo->stepsize != preset_step)
+ continue;
+
+ for (j = 0; j < stepinfo->nstrengths; j++) {
+ if (stepinfo->strengths[j] != preset_strength)
+ continue;
+
+ ecc_bytes = caps->calc_ecc_bytes(preset_step,
+ preset_strength);
+ if (WARN_ON_ONCE(ecc_bytes < 0))
+ return ecc_bytes;
+
+ if (ecc_bytes * nsteps > oobavail) {
+ pr_err("ECC (step, strength) = (%d, %d) does not fit in OOB",
+ preset_step, preset_strength);
+ return -ENOSPC;
+ }
+
+ chip->ecc.bytes = ecc_bytes;
+
+ return 0;
+ }
+ }
+
+ pr_err("ECC (step, strength) = (%d, %d) not supported on this controller",
+ preset_step, preset_strength);
+
+ return -ENOTSUPP;
+}
+EXPORT_SYMBOL_GPL(nand_check_ecc_caps);
+
+/**
+ * nand_match_ecc_req - meet the chip's requirement with least ECC bytes
+ * @chip: nand chip info structure
+ * @caps: ECC engine caps info structure
+ * @oobavail: OOB size that the ECC engine can use
+ *
+ * If a chip's ECC requirement is provided, try to meet it with the least
+ * number of ECC bytes (i.e. with the largest number of OOB-free bytes).
+ * On success, the chosen ECC settings are set.
+ */
+int nand_match_ecc_req(struct nand_chip *chip,
+ const struct nand_ecc_caps *caps, int oobavail)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ const struct nand_ecc_step_info *stepinfo;
+ int req_step = chip->ecc_step_ds;
+ int req_strength = chip->ecc_strength_ds;
+ int req_corr, step_size, strength, nsteps, ecc_bytes, ecc_bytes_total;
+ int best_step, best_strength, best_ecc_bytes;
+ int best_ecc_bytes_total = INT_MAX;
+ int i, j;
+
+ if (WARN_ON(oobavail < 0))
+ return -EINVAL;
+
+ /* No information provided by the NAND chip */
+ if (!req_step || !req_strength)
+ return -ENOTSUPP;
+
+ /* number of correctable bits the chip requires in a page */
+ req_corr = mtd->writesize / req_step * req_strength;
+
+ for (i = 0; i < caps->nstepinfos; i++) {
+ stepinfo = &caps->stepinfos[i];
+ step_size = stepinfo->stepsize;
+
+ for (j = 0; j < stepinfo->nstrengths; j++) {
+ strength = stepinfo->strengths[j];
+
+ /*
+ * If both step size and strength are smaller than the
+ * chip's requirement, it is not easy to compare the
+ * resulted reliability.
+ */
+ if (step_size < req_step && strength < req_strength)
+ continue;
+
+ if (mtd->writesize % step_size)
+ continue;
+
+ nsteps = mtd->writesize / step_size;
+
+ ecc_bytes = caps->calc_ecc_bytes(step_size, strength);
+ if (WARN_ON_ONCE(ecc_bytes < 0))
+ continue;
+ ecc_bytes_total = ecc_bytes * nsteps;
+
+ if (ecc_bytes_total > oobavail ||
+ strength * nsteps < req_corr)
+ continue;
+
+ /*
+ * We assume the best is to meet the chip's requrement
+ * with the least number of ECC bytes.
+ */
+ if (ecc_bytes_total < best_ecc_bytes_total) {
+ best_ecc_bytes_total = ecc_bytes_total;
+ best_step = step_size;
+ best_strength = strength;
+ best_ecc_bytes = ecc_bytes;
+ }
+ }
+ }
+
+ if (best_ecc_bytes_total == INT_MAX)
+ return -ENOTSUPP;
+
+ chip->ecc.size = best_step;
+ chip->ecc.strength = best_strength;
+ chip->ecc.bytes = best_ecc_bytes;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nand_match_ecc_req);
+
+/**
+ * nand_maximize_ecc - choose the max ECC strength available
+ * @chip: nand chip info structure
+ * @caps: ECC engine caps info structure
+ * @oobavail: OOB size that the ECC engine can use
+ *
+ * Choose the max ECC strength that is supported on the controller, and can fit
+ * within the chip's OOB. On success, the chosen ECC settings are set.
+ */
+int nand_maximize_ecc(struct nand_chip *chip,
+ const struct nand_ecc_caps *caps, int oobavail)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ const struct nand_ecc_step_info *stepinfo;
+ int step_size, strength, nsteps, ecc_bytes, corr;
+ int best_corr = 0;
+ int best_step = 0;
+ int best_strength, best_ecc_bytes;
+ int i, j;
+
+ if (WARN_ON(oobavail < 0))
+ return -EINVAL;
+
+ for (i = 0; i < caps->nstepinfos; i++) {
+ stepinfo = &caps->stepinfos[i];
+ step_size = stepinfo->stepsize;
+
+ /* If chip->ecc.size is already set, respect it */
+ if (chip->ecc.size && step_size != chip->ecc.size)
+ continue;
+
+ for (j = 0; j < stepinfo->nstrengths; j++) {
+ strength = stepinfo->strengths[j];
+
+ if (mtd->writesize % step_size)
+ continue;
+
+ nsteps = mtd->writesize / step_size;
+
+ ecc_bytes = caps->calc_ecc_bytes(step_size, strength);
+ if (WARN_ON_ONCE(ecc_bytes < 0))
+ continue;
+
+ if (ecc_bytes * nsteps > oobavail)
+ continue;
+
+ corr = strength * nsteps;
+
+ /*
+ * If the number of correctable bits is the same,
+ * bigger step_size has more reliability.
+ */
+ if (corr > best_corr ||
+ (corr == best_corr && step_size > best_step)) {
+ best_corr = corr;
+ best_step = step_size;
+ best_strength = strength;
+ best_ecc_bytes = ecc_bytes;
+ }
+ }
+ }
+
+ if (!best_corr)
+ return -ENOTSUPP;
+
+ chip->ecc.size = best_step;
+ chip->ecc.strength = best_strength;
+ chip->ecc.bytes = best_ecc_bytes;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nand_maximize_ecc);
+
+/*
+ * Check if the chip configuration meet the datasheet requirements.
+
+ * If our configuration corrects A bits per B bytes and the minimum
+ * required correction level is X bits per Y bytes, then we must ensure
+ * both of the following are true:
+ *
+ * (1) A / B >= X / Y
+ * (2) A >= X
+ *
+ * Requirement (1) ensures we can correct for the required bitflip density.
+ * Requirement (2) ensures we can correct even when all bitflips are clumped
+ * in the same sector.
+ */
+static bool nand_ecc_strength_good(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ int corr, ds_corr;
+
+ if (ecc->size == 0 || chip->ecc_step_ds == 0)
+ /* Not enough information */
+ return true;
+
+ /*
+ * We get the number of corrected bits per page to compare
+ * the correction density.
+ */
+ corr = (mtd->writesize * ecc->strength) / ecc->size;
+ ds_corr = (mtd->writesize * chip->ecc_strength_ds) / chip->ecc_step_ds;
+
+ return corr >= ds_corr && ecc->strength >= chip->ecc_strength_ds;
+}
+
+/**
+ * nand_scan_tail - [NAND Interface] Scan for the NAND device
+ * @mtd: MTD device structure
+ *
+ * This is the second phase of the normal nand_scan() function. It fills out
+ * all the uninitialized function pointers with the defaults and scans for a
+ * bad block table if appropriate.
+ */
+int nand_scan_tail(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ int ret, i;
+
+ /* New bad blocks should be marked in OOB, flash-based BBT, or both */
+ if (WARN_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
+ !(chip->bbt_options & NAND_BBT_USE_FLASH))) {
+ return -EINVAL;
+ }
+
+ chip->data_buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
+ if (!chip->data_buf)
+ return -ENOMEM;
+
+ /*
+ * FIXME: some NAND manufacturer drivers expect the first die to be
+ * selected when manufacturer->init() is called. They should be fixed
+ * to explictly select the relevant die when interacting with the NAND
+ * chip.
+ */
+ chip->select_chip(mtd, 0);
+ ret = nand_manufacturer_init(chip);
+ chip->select_chip(mtd, -1);
+ if (ret)
+ goto err_free_buf;
+
+ /* Set the internal oob buffer location, just after the page data */
+ chip->oob_poi = chip->data_buf + mtd->writesize;
+
+ /*
+ * If no default placement scheme is given, select an appropriate one.
+ */
+ if (!mtd->ooblayout &&
+ !(ecc->mode == NAND_ECC_SOFT && ecc->algo == NAND_ECC_BCH)) {
+ switch (mtd->oobsize) {
+ case 8:
+ case 16:
+ mtd_set_ooblayout(mtd, &nand_ooblayout_sp_ops);
+ break;
+ case 64:
+ case 128:
+ mtd_set_ooblayout(mtd, &nand_ooblayout_lp_hamming_ops);
+ break;
+ default:
+ /*
+ * Expose the whole OOB area to users if ECC_NONE
+ * is passed. We could do that for all kind of
+ * ->oobsize, but we must keep the old large/small
+ * page with ECC layout when ->oobsize <= 128 for
+ * compatibility reasons.
+ */
+ if (ecc->mode == NAND_ECC_NONE) {
+ mtd_set_ooblayout(mtd,
+ &nand_ooblayout_lp_ops);
+ break;
+ }
+
+ WARN(1, "No oob scheme defined for oobsize %d\n",
+ mtd->oobsize);
+ ret = -EINVAL;
+ goto err_nand_manuf_cleanup;
+ }
+ }
+
+ /*
+ * Check ECC mode, default to software if 3byte/512byte hardware ECC is
+ * selected and we have 256 byte pagesize fallback to software ECC
+ */
+
+ switch (ecc->mode) {
+ case NAND_ECC_HW_OOB_FIRST:
+ /* Similar to NAND_ECC_HW, but a separate read_page handle */
+ if (!ecc->calculate || !ecc->correct || !ecc->hwctl) {
+ WARN(1, "No ECC functions supplied; hardware ECC not possible\n");
+ ret = -EINVAL;
+ goto err_nand_manuf_cleanup;
+ }
+ if (!ecc->read_page)
+ ecc->read_page = nand_read_page_hwecc_oob_first;
+
+ case NAND_ECC_HW:
+ /* Use standard hwecc read page function? */
+ if (!ecc->read_page)
+ ecc->read_page = nand_read_page_hwecc;
+ if (!ecc->write_page)
+ ecc->write_page = nand_write_page_hwecc;
+ if (!ecc->read_page_raw)
+ ecc->read_page_raw = nand_read_page_raw;
+ if (!ecc->write_page_raw)
+ ecc->write_page_raw = nand_write_page_raw;
+ if (!ecc->read_oob)
+ ecc->read_oob = nand_read_oob_std;
+ if (!ecc->write_oob)
+ ecc->write_oob = nand_write_oob_std;
+ if (!ecc->read_subpage)
+ ecc->read_subpage = nand_read_subpage;
+ if (!ecc->write_subpage && ecc->hwctl && ecc->calculate)
+ ecc->write_subpage = nand_write_subpage_hwecc;
+
+ case NAND_ECC_HW_SYNDROME:
+ if ((!ecc->calculate || !ecc->correct || !ecc->hwctl) &&
+ (!ecc->read_page ||
+ ecc->read_page == nand_read_page_hwecc ||
+ !ecc->write_page ||
+ ecc->write_page == nand_write_page_hwecc)) {
+ WARN(1, "No ECC functions supplied; hardware ECC not possible\n");
+ ret = -EINVAL;
+ goto err_nand_manuf_cleanup;
+ }
+ /* Use standard syndrome read/write page function? */
+ if (!ecc->read_page)
+ ecc->read_page = nand_read_page_syndrome;
+ if (!ecc->write_page)
+ ecc->write_page = nand_write_page_syndrome;
+ if (!ecc->read_page_raw)
+ ecc->read_page_raw = nand_read_page_raw_syndrome;
+ if (!ecc->write_page_raw)
+ ecc->write_page_raw = nand_write_page_raw_syndrome;
+ if (!ecc->read_oob)
+ ecc->read_oob = nand_read_oob_syndrome;
+ if (!ecc->write_oob)
+ ecc->write_oob = nand_write_oob_syndrome;
+
+ if (mtd->writesize >= ecc->size) {
+ if (!ecc->strength) {
+ WARN(1, "Driver must set ecc.strength when using hardware ECC\n");
+ ret = -EINVAL;
+ goto err_nand_manuf_cleanup;
+ }
+ break;
+ }
+ pr_warn("%d byte HW ECC not possible on %d byte page size, fallback to SW ECC\n",
+ ecc->size, mtd->writesize);
+ ecc->mode = NAND_ECC_SOFT;
+ ecc->algo = NAND_ECC_HAMMING;
+
+ case NAND_ECC_SOFT:
+ ret = nand_set_ecc_soft_ops(mtd);
+ if (ret) {
+ ret = -EINVAL;
+ goto err_nand_manuf_cleanup;
+ }
+ break;
+
+ case NAND_ECC_ON_DIE:
+ if (!ecc->read_page || !ecc->write_page) {
+ WARN(1, "No ECC functions supplied; on-die ECC not possible\n");
+ ret = -EINVAL;
+ goto err_nand_manuf_cleanup;
+ }
+ if (!ecc->read_oob)
+ ecc->read_oob = nand_read_oob_std;
+ if (!ecc->write_oob)
+ ecc->write_oob = nand_write_oob_std;
+ break;
+
+ case NAND_ECC_NONE:
+ pr_warn("NAND_ECC_NONE selected by board driver. This is not recommended!\n");
+ ecc->read_page = nand_read_page_raw;
+ ecc->write_page = nand_write_page_raw;
+ ecc->read_oob = nand_read_oob_std;
+ ecc->read_page_raw = nand_read_page_raw;
+ ecc->write_page_raw = nand_write_page_raw;
+ ecc->write_oob = nand_write_oob_std;
+ ecc->size = mtd->writesize;
+ ecc->bytes = 0;
+ ecc->strength = 0;
+ break;
+
+ default:
+ WARN(1, "Invalid NAND_ECC_MODE %d\n", ecc->mode);
+ ret = -EINVAL;
+ goto err_nand_manuf_cleanup;
+ }
+
+ if (ecc->correct || ecc->calculate) {
+ ecc->calc_buf = kmalloc(mtd->oobsize, GFP_KERNEL);
+ ecc->code_buf = kmalloc(mtd->oobsize, GFP_KERNEL);
+ if (!ecc->calc_buf || !ecc->code_buf) {
+ ret = -ENOMEM;
+ goto err_nand_manuf_cleanup;
+ }
+ }
+
+ /* For many systems, the standard OOB write also works for raw */
+ if (!ecc->read_oob_raw)
+ ecc->read_oob_raw = ecc->read_oob;
+ if (!ecc->write_oob_raw)
+ ecc->write_oob_raw = ecc->write_oob;
+
+ /* propagate ecc info to mtd_info */
+ mtd->ecc_strength = ecc->strength;
+ mtd->ecc_step_size = ecc->size;
+
+ /*
+ * Set the number of read / write steps for one page depending on ECC
+ * mode.
+ */
+ ecc->steps = mtd->writesize / ecc->size;
+ if (ecc->steps * ecc->size != mtd->writesize) {
+ WARN(1, "Invalid ECC parameters\n");
+ ret = -EINVAL;
+ goto err_nand_manuf_cleanup;
+ }
+ ecc->total = ecc->steps * ecc->bytes;
+ if (ecc->total > mtd->oobsize) {
+ WARN(1, "Total number of ECC bytes exceeded oobsize\n");
+ ret = -EINVAL;
+ goto err_nand_manuf_cleanup;
+ }
+
+ /*
+ * The number of bytes available for a client to place data into
+ * the out of band area.
+ */
+ ret = mtd_ooblayout_count_freebytes(mtd);
+ if (ret < 0)
+ ret = 0;
+
+ mtd->oobavail = ret;
+
+ /* ECC sanity check: warn if it's too weak */
+ if (!nand_ecc_strength_good(mtd))
+ pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n",
+ mtd->name);
+
+ /* Allow subpage writes up to ecc.steps. Not possible for MLC flash */
+ if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) {
+ switch (ecc->steps) {
+ case 2:
+ mtd->subpage_sft = 1;
+ break;
+ case 4:
+ case 8:
+ case 16:
+ mtd->subpage_sft = 2;
+ break;
+ }
+ }
+ chip->subpagesize = mtd->writesize >> mtd->subpage_sft;
+
+ /* Initialize state */
+ chip->state = FL_READY;
+
+ /* Invalidate the pagebuffer reference */
+ chip->pagebuf = -1;
+
+ /* Large page NAND with SOFT_ECC should support subpage reads */
+ switch (ecc->mode) {
+ case NAND_ECC_SOFT:
+ if (chip->page_shift > 9)
+ chip->options |= NAND_SUBPAGE_READ;
+ break;
+
+ default:
+ break;
+ }
+
+ /* Fill in remaining MTD driver data */
+ mtd->type = nand_is_slc(chip) ? MTD_NANDFLASH : MTD_MLCNANDFLASH;
+ mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM :
+ MTD_CAP_NANDFLASH;
+ mtd->_erase = nand_erase;
+ mtd->_point = NULL;
+ mtd->_unpoint = NULL;
+ mtd->_panic_write = panic_nand_write;
+ mtd->_read_oob = nand_read_oob;
+ mtd->_write_oob = nand_write_oob;
+ mtd->_sync = nand_sync;
+ mtd->_lock = NULL;
+ mtd->_unlock = NULL;
+ mtd->_suspend = nand_suspend;
+ mtd->_resume = nand_resume;
+ mtd->_reboot = nand_shutdown;
+ mtd->_block_isreserved = nand_block_isreserved;
+ mtd->_block_isbad = nand_block_isbad;
+ mtd->_block_markbad = nand_block_markbad;
+ mtd->_max_bad_blocks = nand_max_bad_blocks;
+ mtd->writebufsize = mtd->writesize;
+
+ /*
+ * Initialize bitflip_threshold to its default prior scan_bbt() call.
+ * scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be
+ * properly set.
+ */
+ if (!mtd->bitflip_threshold)
+ mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3, 4);
+
+ /* Initialize the ->data_interface field. */
+ ret = nand_init_data_interface(chip);
+ if (ret)
+ goto err_nand_manuf_cleanup;
+
+ /* Enter fastest possible mode on all dies. */
+ for (i = 0; i < chip->numchips; i++) {
+ ret = nand_setup_data_interface(chip, i);
+ if (ret)
+ goto err_nand_manuf_cleanup;
+ }
+
+ /* Check, if we should skip the bad block table scan */
+ if (chip->options & NAND_SKIP_BBTSCAN)
+ return 0;
+
+ /* Build bad block table */
+ ret = chip->scan_bbt(mtd);
+ if (ret)
+ goto err_nand_manuf_cleanup;
+
+ return 0;
+
+
+err_nand_manuf_cleanup:
+ nand_manufacturer_cleanup(chip);
+
+err_free_buf:
+ kfree(chip->data_buf);
+ kfree(ecc->code_buf);
+ kfree(ecc->calc_buf);
+
+ return ret;
+}
+EXPORT_SYMBOL(nand_scan_tail);
+
+/*
+ * is_module_text_address() isn't exported, and it's mostly a pointless
+ * test if this is a module _anyway_ -- they'd have to try _really_ hard
+ * to call us from in-kernel code if the core NAND support is modular.
+ */
+#ifdef MODULE
+#define caller_is_module() (1)
+#else
+#define caller_is_module() \
+ is_module_text_address((unsigned long)__builtin_return_address(0))
+#endif
+
+/**
+ * nand_scan - [NAND Interface] Scan for the NAND device
+ * @mtd: MTD device structure
+ * @maxchips: number of chips to scan for
+ *
+ * This fills out all the uninitialized function pointers with the defaults.
+ * The flash ID is read and the mtd/chip structures are filled with the
+ * appropriate values.
+ */
+int nand_scan(struct mtd_info *mtd, int maxchips)
+{
+ int ret;
+
+ ret = nand_scan_ident(mtd, maxchips, NULL);
+ if (!ret)
+ ret = nand_scan_tail(mtd);
+ return ret;
+}
+EXPORT_SYMBOL(nand_scan);
+
+/**
+ * nand_cleanup - [NAND Interface] Free resources held by the NAND device
+ * @chip: NAND chip object
+ */
+void nand_cleanup(struct nand_chip *chip)
+{
+ if (chip->ecc.mode == NAND_ECC_SOFT &&
+ chip->ecc.algo == NAND_ECC_BCH)
+ nand_bch_free((struct nand_bch_control *)chip->ecc.priv);
+
+ /* Free bad block table memory */
+ kfree(chip->bbt);
+ kfree(chip->data_buf);
+ kfree(chip->ecc.code_buf);
+ kfree(chip->ecc.calc_buf);
+
+ /* Free bad block descriptor memory */
+ if (chip->badblock_pattern && chip->badblock_pattern->options
+ & NAND_BBT_DYNAMICSTRUCT)
+ kfree(chip->badblock_pattern);
+
+ /* Free manufacturer priv data. */
+ nand_manufacturer_cleanup(chip);
+}
+EXPORT_SYMBOL_GPL(nand_cleanup);
+
+/**
+ * nand_release - [NAND Interface] Unregister the MTD device and free resources
+ * held by the NAND device
+ * @mtd: MTD device structure
+ */
+void nand_release(struct mtd_info *mtd)
+{
+ mtd_device_unregister(mtd);
+ nand_cleanup(mtd_to_nand(mtd));
+}
+EXPORT_SYMBOL_GPL(nand_release);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com>");
+MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
+MODULE_DESCRIPTION("Generic NAND flash driver code");
diff --git a/drivers/mtd/nand/raw/nand_bbt.c b/drivers/mtd/nand/raw/nand_bbt.c
new file mode 100644
index 000000000000..d9f4ceff2568
--- /dev/null
+++ b/drivers/mtd/nand/raw/nand_bbt.c
@@ -0,0 +1,1451 @@
+/*
+ * Overview:
+ * Bad block table support for the NAND driver
+ *
+ * Copyright © 2004 Thomas Gleixner (tglx@linutronix.de)
+ *
+ * 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.
+ *
+ * Description:
+ *
+ * When nand_scan_bbt is called, then it tries to find the bad block table
+ * depending on the options in the BBT descriptor(s). If no flash based BBT
+ * (NAND_BBT_USE_FLASH) is specified then the device is scanned for factory
+ * marked good / bad blocks. This information is used to create a memory BBT.
+ * Once a new bad block is discovered then the "factory" information is updated
+ * on the device.
+ * If a flash based BBT is specified then the function first tries to find the
+ * BBT on flash. If a BBT is found then the contents are read and the memory
+ * based BBT is created. If a mirrored BBT is selected then the mirror is
+ * searched too and the versions are compared. If the mirror has a greater
+ * version number, then the mirror BBT is used to build the memory based BBT.
+ * If the tables are not versioned, then we "or" the bad block information.
+ * If one of the BBTs is out of date or does not exist it is (re)created.
+ * If no BBT exists at all then the device is scanned for factory marked
+ * good / bad blocks and the bad block tables are created.
+ *
+ * For manufacturer created BBTs like the one found on M-SYS DOC devices
+ * the BBT is searched and read but never created
+ *
+ * The auto generated bad block table is located in the last good blocks
+ * of the device. The table is mirrored, so it can be updated eventually.
+ * The table is marked in the OOB area with an ident pattern and a version
+ * number which indicates which of both tables is more up to date. If the NAND
+ * controller needs the complete OOB area for the ECC information then the
+ * option NAND_BBT_NO_OOB should be used (along with NAND_BBT_USE_FLASH, of
+ * course): it moves the ident pattern and the version byte into the data area
+ * and the OOB area will remain untouched.
+ *
+ * The table uses 2 bits per block
+ * 11b: block is good
+ * 00b: block is factory marked bad
+ * 01b, 10b: block is marked bad due to wear
+ *
+ * The memory bad block table uses the following scheme:
+ * 00b: block is good
+ * 01b: block is marked bad due to wear
+ * 10b: block is reserved (to protect the bbt area)
+ * 11b: block is factory marked bad
+ *
+ * Multichip devices like DOC store the bad block info per floor.
+ *
+ * Following assumptions are made:
+ * - bbts start at a page boundary, if autolocated on a block boundary
+ * - the space necessary for a bbt in FLASH does not exceed a block boundary
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/bbm.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/vmalloc.h>
+#include <linux/export.h>
+#include <linux/string.h>
+
+#define BBT_BLOCK_GOOD 0x00
+#define BBT_BLOCK_WORN 0x01
+#define BBT_BLOCK_RESERVED 0x02
+#define BBT_BLOCK_FACTORY_BAD 0x03
+
+#define BBT_ENTRY_MASK 0x03
+#define BBT_ENTRY_SHIFT 2
+
+static int nand_update_bbt(struct mtd_info *mtd, loff_t offs);
+
+static inline uint8_t bbt_get_entry(struct nand_chip *chip, int block)
+{
+ uint8_t entry = chip->bbt[block >> BBT_ENTRY_SHIFT];
+ entry >>= (block & BBT_ENTRY_MASK) * 2;
+ return entry & BBT_ENTRY_MASK;
+}
+
+static inline void bbt_mark_entry(struct nand_chip *chip, int block,
+ uint8_t mark)
+{
+ uint8_t msk = (mark & BBT_ENTRY_MASK) << ((block & BBT_ENTRY_MASK) * 2);
+ chip->bbt[block >> BBT_ENTRY_SHIFT] |= msk;
+}
+
+static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td)
+{
+ if (memcmp(buf, td->pattern, td->len))
+ return -1;
+ return 0;
+}
+
+/**
+ * check_pattern - [GENERIC] check if a pattern is in the buffer
+ * @buf: the buffer to search
+ * @len: the length of buffer to search
+ * @paglen: the pagelength
+ * @td: search pattern descriptor
+ *
+ * Check for a pattern at the given place. Used to search bad block tables and
+ * good / bad block identifiers.
+ */
+static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
+{
+ if (td->options & NAND_BBT_NO_OOB)
+ return check_pattern_no_oob(buf, td);
+
+ /* Compare the pattern */
+ if (memcmp(buf + paglen + td->offs, td->pattern, td->len))
+ return -1;
+
+ return 0;
+}
+
+/**
+ * check_short_pattern - [GENERIC] check if a pattern is in the buffer
+ * @buf: the buffer to search
+ * @td: search pattern descriptor
+ *
+ * Check for a pattern at the given place. Used to search bad block tables and
+ * good / bad block identifiers. Same as check_pattern, but no optional empty
+ * check.
+ */
+static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td)
+{
+ /* Compare the pattern */
+ if (memcmp(buf + td->offs, td->pattern, td->len))
+ return -1;
+ return 0;
+}
+
+/**
+ * add_marker_len - compute the length of the marker in data area
+ * @td: BBT descriptor used for computation
+ *
+ * The length will be 0 if the marker is located in OOB area.
+ */
+static u32 add_marker_len(struct nand_bbt_descr *td)
+{
+ u32 len;
+
+ if (!(td->options & NAND_BBT_NO_OOB))
+ return 0;
+
+ len = td->len;
+ if (td->options & NAND_BBT_VERSION)
+ len++;
+ return len;
+}
+
+/**
+ * read_bbt - [GENERIC] Read the bad block table starting from page
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @page: the starting page
+ * @num: the number of bbt descriptors to read
+ * @td: the bbt describtion table
+ * @offs: block number offset in the table
+ *
+ * Read the bad block table starting from page.
+ */
+static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
+ struct nand_bbt_descr *td, int offs)
+{
+ int res, ret = 0, i, j, act = 0;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ size_t retlen, len, totlen;
+ loff_t from;
+ int bits = td->options & NAND_BBT_NRBITS_MSK;
+ uint8_t msk = (uint8_t)((1 << bits) - 1);
+ u32 marker_len;
+ int reserved_block_code = td->reserved_block_code;
+
+ totlen = (num * bits) >> 3;
+ marker_len = add_marker_len(td);
+ from = ((loff_t)page) << this->page_shift;
+
+ while (totlen) {
+ len = min(totlen, (size_t)(1 << this->bbt_erase_shift));
+ if (marker_len) {
+ /*
+ * In case the BBT marker is not in the OOB area it
+ * will be just in the first page.
+ */
+ len -= marker_len;
+ from += marker_len;
+ marker_len = 0;
+ }
+ res = mtd_read(mtd, from, len, &retlen, buf);
+ if (res < 0) {
+ if (mtd_is_eccerr(res)) {
+ pr_info("nand_bbt: ECC error in BBT at 0x%012llx\n",
+ from & ~mtd->writesize);
+ return res;
+ } else if (mtd_is_bitflip(res)) {
+ pr_info("nand_bbt: corrected error in BBT at 0x%012llx\n",
+ from & ~mtd->writesize);
+ ret = res;
+ } else {
+ pr_info("nand_bbt: error reading BBT\n");
+ return res;
+ }
+ }
+
+ /* Analyse data */
+ for (i = 0; i < len; i++) {
+ uint8_t dat = buf[i];
+ for (j = 0; j < 8; j += bits, act++) {
+ uint8_t tmp = (dat >> j) & msk;
+ if (tmp == msk)
+ continue;
+ if (reserved_block_code && (tmp == reserved_block_code)) {
+ pr_info("nand_read_bbt: reserved block at 0x%012llx\n",
+ (loff_t)(offs + act) <<
+ this->bbt_erase_shift);
+ bbt_mark_entry(this, offs + act,
+ BBT_BLOCK_RESERVED);
+ mtd->ecc_stats.bbtblocks++;
+ continue;
+ }
+ /*
+ * Leave it for now, if it's matured we can
+ * move this message to pr_debug.
+ */
+ pr_info("nand_read_bbt: bad block at 0x%012llx\n",
+ (loff_t)(offs + act) <<
+ this->bbt_erase_shift);
+ /* Factory marked bad or worn out? */
+ if (tmp == 0)
+ bbt_mark_entry(this, offs + act,
+ BBT_BLOCK_FACTORY_BAD);
+ else
+ bbt_mark_entry(this, offs + act,
+ BBT_BLOCK_WORN);
+ mtd->ecc_stats.badblocks++;
+ }
+ }
+ totlen -= len;
+ from += len;
+ }
+ return ret;
+}
+
+/**
+ * read_abs_bbt - [GENERIC] Read the bad block table starting at a given page
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ * @chip: read the table for a specific chip, -1 read all chips; applies only if
+ * NAND_BBT_PERCHIP option is set
+ *
+ * Read the bad block table for all chips starting at a given page. We assume
+ * that the bbt bits are in consecutive order.
+ */
+static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ int res = 0, i;
+
+ if (td->options & NAND_BBT_PERCHIP) {
+ int offs = 0;
+ for (i = 0; i < this->numchips; i++) {
+ if (chip == -1 || chip == i)
+ res = read_bbt(mtd, buf, td->pages[i],
+ this->chipsize >> this->bbt_erase_shift,
+ td, offs);
+ if (res)
+ return res;
+ offs += this->chipsize >> this->bbt_erase_shift;
+ }
+ } else {
+ res = read_bbt(mtd, buf, td->pages[0],
+ mtd->size >> this->bbt_erase_shift, td, 0);
+ if (res)
+ return res;
+ }
+ return 0;
+}
+
+/* BBT marker is in the first page, no OOB */
+static int scan_read_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
+ struct nand_bbt_descr *td)
+{
+ size_t retlen;
+ size_t len;
+
+ len = td->len;
+ if (td->options & NAND_BBT_VERSION)
+ len++;
+
+ return mtd_read(mtd, offs, len, &retlen, buf);
+}
+
+/**
+ * scan_read_oob - [GENERIC] Scan data+OOB region to buffer
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @offs: offset at which to scan
+ * @len: length of data region to read
+ *
+ * Scan read data from data+OOB. May traverse multiple pages, interleaving
+ * page,OOB,page,OOB,... in buf. Completes transfer and returns the "strongest"
+ * ECC condition (error or bitflip). May quit on the first (non-ECC) error.
+ */
+static int scan_read_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
+ size_t len)
+{
+ struct mtd_oob_ops ops;
+ int res, ret = 0;
+
+ ops.mode = MTD_OPS_PLACE_OOB;
+ ops.ooboffs = 0;
+ ops.ooblen = mtd->oobsize;
+
+ while (len > 0) {
+ ops.datbuf = buf;
+ ops.len = min(len, (size_t)mtd->writesize);
+ ops.oobbuf = buf + ops.len;
+
+ res = mtd_read_oob(mtd, offs, &ops);
+ if (res) {
+ if (!mtd_is_bitflip_or_eccerr(res))
+ return res;
+ else if (mtd_is_eccerr(res) || !ret)
+ ret = res;
+ }
+
+ buf += mtd->oobsize + mtd->writesize;
+ len -= mtd->writesize;
+ offs += mtd->writesize;
+ }
+ return ret;
+}
+
+static int scan_read(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
+ size_t len, struct nand_bbt_descr *td)
+{
+ if (td->options & NAND_BBT_NO_OOB)
+ return scan_read_data(mtd, buf, offs, td);
+ else
+ return scan_read_oob(mtd, buf, offs, len);
+}
+
+/* Scan write data with oob to flash */
+static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len,
+ uint8_t *buf, uint8_t *oob)
+{
+ struct mtd_oob_ops ops;
+
+ ops.mode = MTD_OPS_PLACE_OOB;
+ ops.ooboffs = 0;
+ ops.ooblen = mtd->oobsize;
+ ops.datbuf = buf;
+ ops.oobbuf = oob;
+ ops.len = len;
+
+ return mtd_write_oob(mtd, offs, &ops);
+}
+
+static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td)
+{
+ u32 ver_offs = td->veroffs;
+
+ if (!(td->options & NAND_BBT_NO_OOB))
+ ver_offs += mtd->writesize;
+ return ver_offs;
+}
+
+/**
+ * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ * @md: descriptor for the bad block table mirror
+ *
+ * Read the bad block table(s) for all chips starting at a given page. We
+ * assume that the bbt bits are in consecutive order.
+ */
+static void read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
+ struct nand_bbt_descr *td, struct nand_bbt_descr *md)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+
+ /* Read the primary version, if available */
+ if (td->options & NAND_BBT_VERSION) {
+ scan_read(mtd, buf, (loff_t)td->pages[0] << this->page_shift,
+ mtd->writesize, td);
+ td->version[0] = buf[bbt_get_ver_offs(mtd, td)];
+ pr_info("Bad block table at page %d, version 0x%02X\n",
+ td->pages[0], td->version[0]);
+ }
+
+ /* Read the mirror version, if available */
+ if (md && (md->options & NAND_BBT_VERSION)) {
+ scan_read(mtd, buf, (loff_t)md->pages[0] << this->page_shift,
+ mtd->writesize, md);
+ md->version[0] = buf[bbt_get_ver_offs(mtd, md)];
+ pr_info("Bad block table at page %d, version 0x%02X\n",
+ md->pages[0], md->version[0]);
+ }
+}
+
+/* Scan a given block partially */
+static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd,
+ loff_t offs, uint8_t *buf, int numpages)
+{
+ struct mtd_oob_ops ops;
+ int j, ret;
+
+ ops.ooblen = mtd->oobsize;
+ ops.oobbuf = buf;
+ ops.ooboffs = 0;
+ ops.datbuf = NULL;
+ ops.mode = MTD_OPS_PLACE_OOB;
+
+ for (j = 0; j < numpages; j++) {
+ /*
+ * Read the full oob until read_oob is fixed to handle single
+ * byte reads for 16 bit buswidth.
+ */
+ ret = mtd_read_oob(mtd, offs, &ops);
+ /* Ignore ECC errors when checking for BBM */
+ if (ret && !mtd_is_bitflip_or_eccerr(ret))
+ return ret;
+
+ if (check_short_pattern(buf, bd))
+ return 1;
+
+ offs += mtd->writesize;
+ }
+ return 0;
+}
+
+/**
+ * create_bbt - [GENERIC] Create a bad block table by scanning the device
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @bd: descriptor for the good/bad block search pattern
+ * @chip: create the table for a specific chip, -1 read all chips; applies only
+ * if NAND_BBT_PERCHIP option is set
+ *
+ * Create a bad block table by scanning the device for the given good/bad block
+ * identify pattern.
+ */
+static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
+ struct nand_bbt_descr *bd, int chip)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ int i, numblocks, numpages;
+ int startblock;
+ loff_t from;
+
+ pr_info("Scanning device for bad blocks\n");
+
+ if (bd->options & NAND_BBT_SCAN2NDPAGE)
+ numpages = 2;
+ else
+ numpages = 1;
+
+ if (chip == -1) {
+ numblocks = mtd->size >> this->bbt_erase_shift;
+ startblock = 0;
+ from = 0;
+ } else {
+ if (chip >= this->numchips) {
+ pr_warn("create_bbt(): chipnr (%d) > available chips (%d)\n",
+ chip + 1, this->numchips);
+ return -EINVAL;
+ }
+ numblocks = this->chipsize >> this->bbt_erase_shift;
+ startblock = chip * numblocks;
+ numblocks += startblock;
+ from = (loff_t)startblock << this->bbt_erase_shift;
+ }
+
+ if (this->bbt_options & NAND_BBT_SCANLASTPAGE)
+ from += mtd->erasesize - (mtd->writesize * numpages);
+
+ for (i = startblock; i < numblocks; i++) {
+ int ret;
+
+ BUG_ON(bd->options & NAND_BBT_NO_OOB);
+
+ ret = scan_block_fast(mtd, bd, from, buf, numpages);
+ if (ret < 0)
+ return ret;
+
+ if (ret) {
+ bbt_mark_entry(this, i, BBT_BLOCK_FACTORY_BAD);
+ pr_warn("Bad eraseblock %d at 0x%012llx\n",
+ i, (unsigned long long)from);
+ mtd->ecc_stats.badblocks++;
+ }
+
+ from += (1 << this->bbt_erase_shift);
+ }
+ return 0;
+}
+
+/**
+ * search_bbt - [GENERIC] scan the device for a specific bad block table
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ *
+ * Read the bad block table by searching for a given ident pattern. Search is
+ * preformed either from the beginning up or from the end of the device
+ * downwards. The search starts always at the start of a block. If the option
+ * NAND_BBT_PERCHIP is given, each chip is searched for a bbt, which contains
+ * the bad block information of this chip. This is necessary to provide support
+ * for certain DOC devices.
+ *
+ * The bbt ident pattern resides in the oob area of the first page in a block.
+ */
+static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ int i, chips;
+ int startblock, block, dir;
+ int scanlen = mtd->writesize + mtd->oobsize;
+ int bbtblocks;
+ int blocktopage = this->bbt_erase_shift - this->page_shift;
+
+ /* Search direction top -> down? */
+ if (td->options & NAND_BBT_LASTBLOCK) {
+ startblock = (mtd->size >> this->bbt_erase_shift) - 1;
+ dir = -1;
+ } else {
+ startblock = 0;
+ dir = 1;
+ }
+
+ /* Do we have a bbt per chip? */
+ if (td->options & NAND_BBT_PERCHIP) {
+ chips = this->numchips;
+ bbtblocks = this->chipsize >> this->bbt_erase_shift;
+ startblock &= bbtblocks - 1;
+ } else {
+ chips = 1;
+ bbtblocks = mtd->size >> this->bbt_erase_shift;
+ }
+
+ for (i = 0; i < chips; i++) {
+ /* Reset version information */
+ td->version[i] = 0;
+ td->pages[i] = -1;
+ /* Scan the maximum number of blocks */
+ for (block = 0; block < td->maxblocks; block++) {
+
+ int actblock = startblock + dir * block;
+ loff_t offs = (loff_t)actblock << this->bbt_erase_shift;
+
+ /* Read first page */
+ scan_read(mtd, buf, offs, mtd->writesize, td);
+ if (!check_pattern(buf, scanlen, mtd->writesize, td)) {
+ td->pages[i] = actblock << blocktopage;
+ if (td->options & NAND_BBT_VERSION) {
+ offs = bbt_get_ver_offs(mtd, td);
+ td->version[i] = buf[offs];
+ }
+ break;
+ }
+ }
+ startblock += this->chipsize >> this->bbt_erase_shift;
+ }
+ /* Check, if we found a bbt for each requested chip */
+ for (i = 0; i < chips; i++) {
+ if (td->pages[i] == -1)
+ pr_warn("Bad block table not found for chip %d\n", i);
+ else
+ pr_info("Bad block table found at page %d, version 0x%02X\n",
+ td->pages[i], td->version[i]);
+ }
+ return 0;
+}
+
+/**
+ * search_read_bbts - [GENERIC] scan the device for bad block table(s)
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ * @md: descriptor for the bad block table mirror
+ *
+ * Search and read the bad block table(s).
+ */
+static void search_read_bbts(struct mtd_info *mtd, uint8_t *buf,
+ struct nand_bbt_descr *td,
+ struct nand_bbt_descr *md)
+{
+ /* Search the primary table */
+ search_bbt(mtd, buf, td);
+
+ /* Search the mirror table */
+ if (md)
+ search_bbt(mtd, buf, md);
+}
+
+/**
+ * get_bbt_block - Get the first valid eraseblock suitable to store a BBT
+ * @this: the NAND device
+ * @td: the BBT description
+ * @md: the mirror BBT descriptor
+ * @chip: the CHIP selector
+ *
+ * This functions returns a positive block number pointing a valid eraseblock
+ * suitable to store a BBT (i.e. in the range reserved for BBT), or -ENOSPC if
+ * all blocks are already used of marked bad. If td->pages[chip] was already
+ * pointing to a valid block we re-use it, otherwise we search for the next
+ * valid one.
+ */
+static int get_bbt_block(struct nand_chip *this, struct nand_bbt_descr *td,
+ struct nand_bbt_descr *md, int chip)
+{
+ int startblock, dir, page, numblocks, i;
+
+ /*
+ * There was already a version of the table, reuse the page. This
+ * applies for absolute placement too, as we have the page number in
+ * td->pages.
+ */
+ if (td->pages[chip] != -1)
+ return td->pages[chip] >>
+ (this->bbt_erase_shift - this->page_shift);
+
+ numblocks = (int)(this->chipsize >> this->bbt_erase_shift);
+ if (!(td->options & NAND_BBT_PERCHIP))
+ numblocks *= this->numchips;
+
+ /*
+ * Automatic placement of the bad block table. Search direction
+ * top -> down?
+ */
+ if (td->options & NAND_BBT_LASTBLOCK) {
+ startblock = numblocks * (chip + 1) - 1;
+ dir = -1;
+ } else {
+ startblock = chip * numblocks;
+ dir = 1;
+ }
+
+ for (i = 0; i < td->maxblocks; i++) {
+ int block = startblock + dir * i;
+
+ /* Check, if the block is bad */
+ switch (bbt_get_entry(this, block)) {
+ case BBT_BLOCK_WORN:
+ case BBT_BLOCK_FACTORY_BAD:
+ continue;
+ }
+
+ page = block << (this->bbt_erase_shift - this->page_shift);
+
+ /* Check, if the block is used by the mirror table */
+ if (!md || md->pages[chip] != page)
+ return block;
+ }
+
+ return -ENOSPC;
+}
+
+/**
+ * mark_bbt_block_bad - Mark one of the block reserved for BBT bad
+ * @this: the NAND device
+ * @td: the BBT description
+ * @chip: the CHIP selector
+ * @block: the BBT block to mark
+ *
+ * Blocks reserved for BBT can become bad. This functions is an helper to mark
+ * such blocks as bad. It takes care of updating the in-memory BBT, marking the
+ * block as bad using a bad block marker and invalidating the associated
+ * td->pages[] entry.
+ */
+static void mark_bbt_block_bad(struct nand_chip *this,
+ struct nand_bbt_descr *td,
+ int chip, int block)
+{
+ struct mtd_info *mtd = nand_to_mtd(this);
+ loff_t to;
+ int res;
+
+ bbt_mark_entry(this, block, BBT_BLOCK_WORN);
+
+ to = (loff_t)block << this->bbt_erase_shift;
+ res = this->block_markbad(mtd, to);
+ if (res)
+ pr_warn("nand_bbt: error %d while marking block %d bad\n",
+ res, block);
+
+ td->pages[chip] = -1;
+}
+
+/**
+ * write_bbt - [GENERIC] (Re)write the bad block table
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ * @md: descriptor for the bad block table mirror
+ * @chipsel: selector for a specific chip, -1 for all
+ *
+ * (Re)write the bad block table.
+ */
+static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
+ struct nand_bbt_descr *td, struct nand_bbt_descr *md,
+ int chipsel)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct erase_info einfo;
+ int i, res, chip = 0;
+ int bits, page, offs, numblocks, sft, sftmsk;
+ int nrchips, pageoffs, ooboffs;
+ uint8_t msk[4];
+ uint8_t rcode = td->reserved_block_code;
+ size_t retlen, len = 0;
+ loff_t to;
+ struct mtd_oob_ops ops;
+
+ ops.ooblen = mtd->oobsize;
+ ops.ooboffs = 0;
+ ops.datbuf = NULL;
+ ops.mode = MTD_OPS_PLACE_OOB;
+
+ if (!rcode)
+ rcode = 0xff;
+ /* Write bad block table per chip rather than per device? */
+ if (td->options & NAND_BBT_PERCHIP) {
+ numblocks = (int)(this->chipsize >> this->bbt_erase_shift);
+ /* Full device write or specific chip? */
+ if (chipsel == -1) {
+ nrchips = this->numchips;
+ } else {
+ nrchips = chipsel + 1;
+ chip = chipsel;
+ }
+ } else {
+ numblocks = (int)(mtd->size >> this->bbt_erase_shift);
+ nrchips = 1;
+ }
+
+ /* Loop through the chips */
+ while (chip < nrchips) {
+ int block;
+
+ block = get_bbt_block(this, td, md, chip);
+ if (block < 0) {
+ pr_err("No space left to write bad block table\n");
+ res = block;
+ goto outerr;
+ }
+
+ /*
+ * get_bbt_block() returns a block number, shift the value to
+ * get a page number.
+ */
+ page = block << (this->bbt_erase_shift - this->page_shift);
+
+ /* Set up shift count and masks for the flash table */
+ bits = td->options & NAND_BBT_NRBITS_MSK;
+ msk[2] = ~rcode;
+ switch (bits) {
+ case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01;
+ msk[3] = 0x01;
+ break;
+ case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01;
+ msk[3] = 0x03;
+ break;
+ case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C;
+ msk[3] = 0x0f;
+ break;
+ case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F;
+ msk[3] = 0xff;
+ break;
+ default: return -EINVAL;
+ }
+
+ to = ((loff_t)page) << this->page_shift;
+
+ /* Must we save the block contents? */
+ if (td->options & NAND_BBT_SAVECONTENT) {
+ /* Make it block aligned */
+ to &= ~(((loff_t)1 << this->bbt_erase_shift) - 1);
+ len = 1 << this->bbt_erase_shift;
+ res = mtd_read(mtd, to, len, &retlen, buf);
+ if (res < 0) {
+ if (retlen != len) {
+ pr_info("nand_bbt: error reading block for writing the bad block table\n");
+ return res;
+ }
+ pr_warn("nand_bbt: ECC error while reading block for writing bad block table\n");
+ }
+ /* Read oob data */
+ ops.ooblen = (len >> this->page_shift) * mtd->oobsize;
+ ops.oobbuf = &buf[len];
+ res = mtd_read_oob(mtd, to + mtd->writesize, &ops);
+ if (res < 0 || ops.oobretlen != ops.ooblen)
+ goto outerr;
+
+ /* Calc the byte offset in the buffer */
+ pageoffs = page - (int)(to >> this->page_shift);
+ offs = pageoffs << this->page_shift;
+ /* Preset the bbt area with 0xff */
+ memset(&buf[offs], 0xff, (size_t)(numblocks >> sft));
+ ooboffs = len + (pageoffs * mtd->oobsize);
+
+ } else if (td->options & NAND_BBT_NO_OOB) {
+ ooboffs = 0;
+ offs = td->len;
+ /* The version byte */
+ if (td->options & NAND_BBT_VERSION)
+ offs++;
+ /* Calc length */
+ len = (size_t)(numblocks >> sft);
+ len += offs;
+ /* Make it page aligned! */
+ len = ALIGN(len, mtd->writesize);
+ /* Preset the buffer with 0xff */
+ memset(buf, 0xff, len);
+ /* Pattern is located at the begin of first page */
+ memcpy(buf, td->pattern, td->len);
+ } else {
+ /* Calc length */
+ len = (size_t)(numblocks >> sft);
+ /* Make it page aligned! */
+ len = ALIGN(len, mtd->writesize);
+ /* Preset the buffer with 0xff */
+ memset(buf, 0xff, len +
+ (len >> this->page_shift)* mtd->oobsize);
+ offs = 0;
+ ooboffs = len;
+ /* Pattern is located in oob area of first page */
+ memcpy(&buf[ooboffs + td->offs], td->pattern, td->len);
+ }
+
+ if (td->options & NAND_BBT_VERSION)
+ buf[ooboffs + td->veroffs] = td->version[chip];
+
+ /* Walk through the memory table */
+ for (i = 0; i < numblocks; i++) {
+ uint8_t dat;
+ int sftcnt = (i << (3 - sft)) & sftmsk;
+ dat = bbt_get_entry(this, chip * numblocks + i);
+ /* Do not store the reserved bbt blocks! */
+ buf[offs + (i >> sft)] &= ~(msk[dat] << sftcnt);
+ }
+
+ memset(&einfo, 0, sizeof(einfo));
+ einfo.addr = to;
+ einfo.len = 1 << this->bbt_erase_shift;
+ res = nand_erase_nand(mtd, &einfo, 1);
+ if (res < 0) {
+ pr_warn("nand_bbt: error while erasing BBT block %d\n",
+ res);
+ mark_bbt_block_bad(this, td, chip, block);
+ continue;
+ }
+
+ res = scan_write_bbt(mtd, to, len, buf,
+ td->options & NAND_BBT_NO_OOB ? NULL :
+ &buf[len]);
+ if (res < 0) {
+ pr_warn("nand_bbt: error while writing BBT block %d\n",
+ res);
+ mark_bbt_block_bad(this, td, chip, block);
+ continue;
+ }
+
+ pr_info("Bad block table written to 0x%012llx, version 0x%02X\n",
+ (unsigned long long)to, td->version[chip]);
+
+ /* Mark it as used */
+ td->pages[chip++] = page;
+ }
+ return 0;
+
+ outerr:
+ pr_warn("nand_bbt: error while writing bad block table %d\n", res);
+ return res;
+}
+
+/**
+ * nand_memory_bbt - [GENERIC] create a memory based bad block table
+ * @mtd: MTD device structure
+ * @bd: descriptor for the good/bad block search pattern
+ *
+ * The function creates a memory based bbt by scanning the device for
+ * manufacturer / software marked good / bad blocks.
+ */
+static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+
+ return create_bbt(mtd, this->data_buf, bd, -1);
+}
+
+/**
+ * check_create - [GENERIC] create and write bbt(s) if necessary
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @bd: descriptor for the good/bad block search pattern
+ *
+ * The function checks the results of the previous call to read_bbt and creates
+ * / updates the bbt(s) if necessary. Creation is necessary if no bbt was found
+ * for the chip/device. Update is necessary if one of the tables is missing or
+ * the version nr. of one table is less than the other.
+ */
+static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd)
+{
+ int i, chips, writeops, create, chipsel, res, res2;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct nand_bbt_descr *td = this->bbt_td;
+ struct nand_bbt_descr *md = this->bbt_md;
+ struct nand_bbt_descr *rd, *rd2;
+
+ /* Do we have a bbt per chip? */
+ if (td->options & NAND_BBT_PERCHIP)
+ chips = this->numchips;
+ else
+ chips = 1;
+
+ for (i = 0; i < chips; i++) {
+ writeops = 0;
+ create = 0;
+ rd = NULL;
+ rd2 = NULL;
+ res = res2 = 0;
+ /* Per chip or per device? */
+ chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1;
+ /* Mirrored table available? */
+ if (md) {
+ if (td->pages[i] == -1 && md->pages[i] == -1) {
+ create = 1;
+ writeops = 0x03;
+ } else if (td->pages[i] == -1) {
+ rd = md;
+ writeops = 0x01;
+ } else if (md->pages[i] == -1) {
+ rd = td;
+ writeops = 0x02;
+ } else if (td->version[i] == md->version[i]) {
+ rd = td;
+ if (!(td->options & NAND_BBT_VERSION))
+ rd2 = md;
+ } else if (((int8_t)(td->version[i] - md->version[i])) > 0) {
+ rd = td;
+ writeops = 0x02;
+ } else {
+ rd = md;
+ writeops = 0x01;
+ }
+ } else {
+ if (td->pages[i] == -1) {
+ create = 1;
+ writeops = 0x01;
+ } else {
+ rd = td;
+ }
+ }
+
+ if (create) {
+ /* Create the bad block table by scanning the device? */
+ if (!(td->options & NAND_BBT_CREATE))
+ continue;
+
+ /* Create the table in memory by scanning the chip(s) */
+ if (!(this->bbt_options & NAND_BBT_CREATE_EMPTY))
+ create_bbt(mtd, buf, bd, chipsel);
+
+ td->version[i] = 1;
+ if (md)
+ md->version[i] = 1;
+ }
+
+ /* Read back first? */
+ if (rd) {
+ res = read_abs_bbt(mtd, buf, rd, chipsel);
+ if (mtd_is_eccerr(res)) {
+ /* Mark table as invalid */
+ rd->pages[i] = -1;
+ rd->version[i] = 0;
+ i--;
+ continue;
+ }
+ }
+ /* If they weren't versioned, read both */
+ if (rd2) {
+ res2 = read_abs_bbt(mtd, buf, rd2, chipsel);
+ if (mtd_is_eccerr(res2)) {
+ /* Mark table as invalid */
+ rd2->pages[i] = -1;
+ rd2->version[i] = 0;
+ i--;
+ continue;
+ }
+ }
+
+ /* Scrub the flash table(s)? */
+ if (mtd_is_bitflip(res) || mtd_is_bitflip(res2))
+ writeops = 0x03;
+
+ /* Update version numbers before writing */
+ if (md) {
+ td->version[i] = max(td->version[i], md->version[i]);
+ md->version[i] = td->version[i];
+ }
+
+ /* Write the bad block table to the device? */
+ if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
+ res = write_bbt(mtd, buf, td, md, chipsel);
+ if (res < 0)
+ return res;
+ }
+
+ /* Write the mirror bad block table to the device? */
+ if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
+ res = write_bbt(mtd, buf, md, td, chipsel);
+ if (res < 0)
+ return res;
+ }
+ }
+ return 0;
+}
+
+/**
+ * mark_bbt_regions - [GENERIC] mark the bad block table regions
+ * @mtd: MTD device structure
+ * @td: bad block table descriptor
+ *
+ * The bad block table regions are marked as "bad" to prevent accidental
+ * erasures / writes. The regions are identified by the mark 0x02.
+ */
+static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ int i, j, chips, block, nrblocks, update;
+ uint8_t oldval;
+
+ /* Do we have a bbt per chip? */
+ if (td->options & NAND_BBT_PERCHIP) {
+ chips = this->numchips;
+ nrblocks = (int)(this->chipsize >> this->bbt_erase_shift);
+ } else {
+ chips = 1;
+ nrblocks = (int)(mtd->size >> this->bbt_erase_shift);
+ }
+
+ for (i = 0; i < chips; i++) {
+ if ((td->options & NAND_BBT_ABSPAGE) ||
+ !(td->options & NAND_BBT_WRITE)) {
+ if (td->pages[i] == -1)
+ continue;
+ block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift);
+ oldval = bbt_get_entry(this, block);
+ bbt_mark_entry(this, block, BBT_BLOCK_RESERVED);
+ if ((oldval != BBT_BLOCK_RESERVED) &&
+ td->reserved_block_code)
+ nand_update_bbt(mtd, (loff_t)block <<
+ this->bbt_erase_shift);
+ continue;
+ }
+ update = 0;
+ if (td->options & NAND_BBT_LASTBLOCK)
+ block = ((i + 1) * nrblocks) - td->maxblocks;
+ else
+ block = i * nrblocks;
+ for (j = 0; j < td->maxblocks; j++) {
+ oldval = bbt_get_entry(this, block);
+ bbt_mark_entry(this, block, BBT_BLOCK_RESERVED);
+ if (oldval != BBT_BLOCK_RESERVED)
+ update = 1;
+ block++;
+ }
+ /*
+ * If we want reserved blocks to be recorded to flash, and some
+ * new ones have been marked, then we need to update the stored
+ * bbts. This should only happen once.
+ */
+ if (update && td->reserved_block_code)
+ nand_update_bbt(mtd, (loff_t)(block - 1) <<
+ this->bbt_erase_shift);
+ }
+}
+
+/**
+ * verify_bbt_descr - verify the bad block description
+ * @mtd: MTD device structure
+ * @bd: the table to verify
+ *
+ * This functions performs a few sanity checks on the bad block description
+ * table.
+ */
+static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ u32 pattern_len;
+ u32 bits;
+ u32 table_size;
+
+ if (!bd)
+ return;
+
+ pattern_len = bd->len;
+ bits = bd->options & NAND_BBT_NRBITS_MSK;
+
+ BUG_ON((this->bbt_options & NAND_BBT_NO_OOB) &&
+ !(this->bbt_options & NAND_BBT_USE_FLASH));
+ BUG_ON(!bits);
+
+ if (bd->options & NAND_BBT_VERSION)
+ pattern_len++;
+
+ if (bd->options & NAND_BBT_NO_OOB) {
+ BUG_ON(!(this->bbt_options & NAND_BBT_USE_FLASH));
+ BUG_ON(!(this->bbt_options & NAND_BBT_NO_OOB));
+ BUG_ON(bd->offs);
+ if (bd->options & NAND_BBT_VERSION)
+ BUG_ON(bd->veroffs != bd->len);
+ BUG_ON(bd->options & NAND_BBT_SAVECONTENT);
+ }
+
+ if (bd->options & NAND_BBT_PERCHIP)
+ table_size = this->chipsize >> this->bbt_erase_shift;
+ else
+ table_size = mtd->size >> this->bbt_erase_shift;
+ table_size >>= 3;
+ table_size *= bits;
+ if (bd->options & NAND_BBT_NO_OOB)
+ table_size += pattern_len;
+ BUG_ON(table_size > (1 << this->bbt_erase_shift));
+}
+
+/**
+ * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s)
+ * @mtd: MTD device structure
+ * @bd: descriptor for the good/bad block search pattern
+ *
+ * The function checks, if a bad block table(s) is/are already available. If
+ * not it scans the device for manufacturer marked good / bad blocks and writes
+ * the bad block table(s) to the selected place.
+ *
+ * The bad block table memory is allocated here. It must be freed by calling
+ * the nand_free_bbt function.
+ */
+static int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ int len, res;
+ uint8_t *buf;
+ struct nand_bbt_descr *td = this->bbt_td;
+ struct nand_bbt_descr *md = this->bbt_md;
+
+ len = (mtd->size >> (this->bbt_erase_shift + 2)) ? : 1;
+ /*
+ * Allocate memory (2bit per block) and clear the memory bad block
+ * table.
+ */
+ this->bbt = kzalloc(len, GFP_KERNEL);
+ if (!this->bbt)
+ return -ENOMEM;
+
+ /*
+ * If no primary table decriptor is given, scan the device to build a
+ * memory based bad block table.
+ */
+ if (!td) {
+ if ((res = nand_memory_bbt(mtd, bd))) {
+ pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n");
+ goto err;
+ }
+ return 0;
+ }
+ verify_bbt_descr(mtd, td);
+ verify_bbt_descr(mtd, md);
+
+ /* Allocate a temporary buffer for one eraseblock incl. oob */
+ len = (1 << this->bbt_erase_shift);
+ len += (len >> this->page_shift) * mtd->oobsize;
+ buf = vmalloc(len);
+ if (!buf) {
+ res = -ENOMEM;
+ goto err;
+ }
+
+ /* Is the bbt at a given page? */
+ if (td->options & NAND_BBT_ABSPAGE) {
+ read_abs_bbts(mtd, buf, td, md);
+ } else {
+ /* Search the bad block table using a pattern in oob */
+ search_read_bbts(mtd, buf, td, md);
+ }
+
+ res = check_create(mtd, buf, bd);
+ if (res)
+ goto err;
+
+ /* Prevent the bbt regions from erasing / writing */
+ mark_bbt_region(mtd, td);
+ if (md)
+ mark_bbt_region(mtd, md);
+
+ vfree(buf);
+ return 0;
+
+err:
+ kfree(this->bbt);
+ this->bbt = NULL;
+ return res;
+}
+
+/**
+ * nand_update_bbt - update bad block table(s)
+ * @mtd: MTD device structure
+ * @offs: the offset of the newly marked block
+ *
+ * The function updates the bad block table(s).
+ */
+static int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ int len, res = 0;
+ int chip, chipsel;
+ uint8_t *buf;
+ struct nand_bbt_descr *td = this->bbt_td;
+ struct nand_bbt_descr *md = this->bbt_md;
+
+ if (!this->bbt || !td)
+ return -EINVAL;
+
+ /* Allocate a temporary buffer for one eraseblock incl. oob */
+ len = (1 << this->bbt_erase_shift);
+ len += (len >> this->page_shift) * mtd->oobsize;
+ buf = kmalloc(len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ /* Do we have a bbt per chip? */
+ if (td->options & NAND_BBT_PERCHIP) {
+ chip = (int)(offs >> this->chip_shift);
+ chipsel = chip;
+ } else {
+ chip = 0;
+ chipsel = -1;
+ }
+
+ td->version[chip]++;
+ if (md)
+ md->version[chip]++;
+
+ /* Write the bad block table to the device? */
+ if (td->options & NAND_BBT_WRITE) {
+ res = write_bbt(mtd, buf, td, md, chipsel);
+ if (res < 0)
+ goto out;
+ }
+ /* Write the mirror bad block table to the device? */
+ if (md && (md->options & NAND_BBT_WRITE)) {
+ res = write_bbt(mtd, buf, md, td, chipsel);
+ }
+
+ out:
+ kfree(buf);
+ return res;
+}
+
+/*
+ * Define some generic bad / good block scan pattern which are used
+ * while scanning a device for factory marked good / bad blocks.
+ */
+static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
+
+/* Generic flash bbt descriptors */
+static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
+static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 8,
+ .len = 4,
+ .veroffs = 12,
+ .maxblocks = NAND_BBT_SCAN_MAXBLOCKS,
+ .pattern = bbt_pattern
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 8,
+ .len = 4,
+ .veroffs = 12,
+ .maxblocks = NAND_BBT_SCAN_MAXBLOCKS,
+ .pattern = mirror_pattern
+};
+
+static struct nand_bbt_descr bbt_main_no_oob_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP
+ | NAND_BBT_NO_OOB,
+ .len = 4,
+ .veroffs = 4,
+ .maxblocks = NAND_BBT_SCAN_MAXBLOCKS,
+ .pattern = bbt_pattern
+};
+
+static struct nand_bbt_descr bbt_mirror_no_oob_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP
+ | NAND_BBT_NO_OOB,
+ .len = 4,
+ .veroffs = 4,
+ .maxblocks = NAND_BBT_SCAN_MAXBLOCKS,
+ .pattern = mirror_pattern
+};
+
+#define BADBLOCK_SCAN_MASK (~NAND_BBT_NO_OOB)
+/**
+ * nand_create_badblock_pattern - [INTERN] Creates a BBT descriptor structure
+ * @this: NAND chip to create descriptor for
+ *
+ * This function allocates and initializes a nand_bbt_descr for BBM detection
+ * based on the properties of @this. The new descriptor is stored in
+ * this->badblock_pattern. Thus, this->badblock_pattern should be NULL when
+ * passed to this function.
+ */
+static int nand_create_badblock_pattern(struct nand_chip *this)
+{
+ struct nand_bbt_descr *bd;
+ if (this->badblock_pattern) {
+ pr_warn("Bad block pattern already allocated; not replacing\n");
+ return -EINVAL;
+ }
+ bd = kzalloc(sizeof(*bd), GFP_KERNEL);
+ if (!bd)
+ return -ENOMEM;
+ bd->options = this->bbt_options & BADBLOCK_SCAN_MASK;
+ bd->offs = this->badblockpos;
+ bd->len = (this->options & NAND_BUSWIDTH_16) ? 2 : 1;
+ bd->pattern = scan_ff_pattern;
+ bd->options |= NAND_BBT_DYNAMICSTRUCT;
+ this->badblock_pattern = bd;
+ return 0;
+}
+
+/**
+ * nand_default_bbt - [NAND Interface] Select a default bad block table for the device
+ * @mtd: MTD device structure
+ *
+ * This function selects the default bad block table support for the device and
+ * calls the nand_scan_bbt function.
+ */
+int nand_default_bbt(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ int ret;
+
+ /* Is a flash based bad block table requested? */
+ if (this->bbt_options & NAND_BBT_USE_FLASH) {
+ /* Use the default pattern descriptors */
+ if (!this->bbt_td) {
+ if (this->bbt_options & NAND_BBT_NO_OOB) {
+ this->bbt_td = &bbt_main_no_oob_descr;
+ this->bbt_md = &bbt_mirror_no_oob_descr;
+ } else {
+ this->bbt_td = &bbt_main_descr;
+ this->bbt_md = &bbt_mirror_descr;
+ }
+ }
+ } else {
+ this->bbt_td = NULL;
+ this->bbt_md = NULL;
+ }
+
+ if (!this->badblock_pattern) {
+ ret = nand_create_badblock_pattern(this);
+ if (ret)
+ return ret;
+ }
+
+ return nand_scan_bbt(mtd, this->badblock_pattern);
+}
+
+/**
+ * nand_isreserved_bbt - [NAND Interface] Check if a block is reserved
+ * @mtd: MTD device structure
+ * @offs: offset in the device
+ */
+int nand_isreserved_bbt(struct mtd_info *mtd, loff_t offs)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ int block;
+
+ block = (int)(offs >> this->bbt_erase_shift);
+ return bbt_get_entry(this, block) == BBT_BLOCK_RESERVED;
+}
+
+/**
+ * nand_isbad_bbt - [NAND Interface] Check if a block is bad
+ * @mtd: MTD device structure
+ * @offs: offset in the device
+ * @allowbbt: allow access to bad block table region
+ */
+int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ int block, res;
+
+ block = (int)(offs >> this->bbt_erase_shift);
+ res = bbt_get_entry(this, block);
+
+ pr_debug("nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n",
+ (unsigned int)offs, block, res);
+
+ switch (res) {
+ case BBT_BLOCK_GOOD:
+ return 0;
+ case BBT_BLOCK_WORN:
+ return 1;
+ case BBT_BLOCK_RESERVED:
+ return allowbbt ? 0 : 1;
+ }
+ return 1;
+}
+
+/**
+ * nand_markbad_bbt - [NAND Interface] Mark a block bad in the BBT
+ * @mtd: MTD device structure
+ * @offs: offset of the bad block
+ */
+int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ int block, ret = 0;
+
+ block = (int)(offs >> this->bbt_erase_shift);
+
+ /* Mark bad block in memory */
+ bbt_mark_entry(this, block, BBT_BLOCK_WORN);
+
+ /* Update flash-based bad block table */
+ if (this->bbt_options & NAND_BBT_USE_FLASH)
+ ret = nand_update_bbt(mtd, offs);
+
+ return ret;
+}
diff --git a/drivers/mtd/nand/raw/nand_bch.c b/drivers/mtd/nand/raw/nand_bch.c
new file mode 100644
index 000000000000..7f11b68f6db1
--- /dev/null
+++ b/drivers/mtd/nand/raw/nand_bch.c
@@ -0,0 +1,234 @@
+/*
+ * This file provides ECC correction for more than 1 bit per block of data,
+ * using binary BCH codes. It relies on the generic BCH library lib/bch.c.
+ *
+ * Copyright © 2011 Ivan Djelic <ivan.djelic@parrot.com>
+ *
+ * This file 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 file 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 file; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/nand_bch.h>
+#include <linux/bch.h>
+
+/**
+ * struct nand_bch_control - private NAND BCH control structure
+ * @bch: BCH control structure
+ * @errloc: error location array
+ * @eccmask: XOR ecc mask, allows erased pages to be decoded as valid
+ */
+struct nand_bch_control {
+ struct bch_control *bch;
+ unsigned int *errloc;
+ unsigned char *eccmask;
+};
+
+/**
+ * nand_bch_calculate_ecc - [NAND Interface] Calculate ECC for data block
+ * @mtd: MTD block structure
+ * @buf: input buffer with raw data
+ * @code: output buffer with ECC
+ */
+int nand_bch_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf,
+ unsigned char *code)
+{
+ const struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_bch_control *nbc = chip->ecc.priv;
+ unsigned int i;
+
+ memset(code, 0, chip->ecc.bytes);
+ encode_bch(nbc->bch, buf, chip->ecc.size, code);
+
+ /* apply mask so that an erased page is a valid codeword */
+ for (i = 0; i < chip->ecc.bytes; i++)
+ code[i] ^= nbc->eccmask[i];
+
+ return 0;
+}
+EXPORT_SYMBOL(nand_bch_calculate_ecc);
+
+/**
+ * nand_bch_correct_data - [NAND Interface] Detect and correct bit error(s)
+ * @mtd: MTD block structure
+ * @buf: raw data read from the chip
+ * @read_ecc: ECC from the chip
+ * @calc_ecc: the ECC calculated from raw data
+ *
+ * Detect and correct bit errors for a data byte block
+ */
+int nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf,
+ unsigned char *read_ecc, unsigned char *calc_ecc)
+{
+ const struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_bch_control *nbc = chip->ecc.priv;
+ unsigned int *errloc = nbc->errloc;
+ int i, count;
+
+ count = decode_bch(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc,
+ NULL, errloc);
+ if (count > 0) {
+ for (i = 0; i < count; i++) {
+ if (errloc[i] < (chip->ecc.size*8))
+ /* error is located in data, correct it */
+ buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7));
+ /* else error in ecc, no action needed */
+
+ pr_debug("%s: corrected bitflip %u\n", __func__,
+ errloc[i]);
+ }
+ } else if (count < 0) {
+ pr_err("ecc unrecoverable error\n");
+ count = -EBADMSG;
+ }
+ return count;
+}
+EXPORT_SYMBOL(nand_bch_correct_data);
+
+/**
+ * nand_bch_init - [NAND Interface] Initialize NAND BCH error correction
+ * @mtd: MTD block structure
+ *
+ * Returns:
+ * a pointer to a new NAND BCH control structure, or NULL upon failure
+ *
+ * Initialize NAND BCH error correction. Parameters @eccsize and @eccbytes
+ * are used to compute BCH parameters m (Galois field order) and t (error
+ * correction capability). @eccbytes should be equal to the number of bytes
+ * required to store m*t bits, where m is such that 2^m-1 > @eccsize*8.
+ *
+ * Example: to configure 4 bit correction per 512 bytes, you should pass
+ * @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8)
+ * @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits)
+ */
+struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ unsigned int m, t, eccsteps, i;
+ struct nand_bch_control *nbc = NULL;
+ unsigned char *erased_page;
+ unsigned int eccsize = nand->ecc.size;
+ unsigned int eccbytes = nand->ecc.bytes;
+ unsigned int eccstrength = nand->ecc.strength;
+
+ if (!eccbytes && eccstrength) {
+ eccbytes = DIV_ROUND_UP(eccstrength * fls(8 * eccsize), 8);
+ nand->ecc.bytes = eccbytes;
+ }
+
+ if (!eccsize || !eccbytes) {
+ pr_warn("ecc parameters not supplied\n");
+ goto fail;
+ }
+
+ m = fls(1+8*eccsize);
+ t = (eccbytes*8)/m;
+
+ nbc = kzalloc(sizeof(*nbc), GFP_KERNEL);
+ if (!nbc)
+ goto fail;
+
+ nbc->bch = init_bch(m, t, 0);
+ if (!nbc->bch)
+ goto fail;
+
+ /* verify that eccbytes has the expected value */
+ if (nbc->bch->ecc_bytes != eccbytes) {
+ pr_warn("invalid eccbytes %u, should be %u\n",
+ eccbytes, nbc->bch->ecc_bytes);
+ goto fail;
+ }
+
+ eccsteps = mtd->writesize/eccsize;
+
+ /* Check that we have an oob layout description. */
+ if (!mtd->ooblayout) {
+ pr_warn("missing oob scheme");
+ goto fail;
+ }
+
+ /* sanity checks */
+ if (8*(eccsize+eccbytes) >= (1 << m)) {
+ pr_warn("eccsize %u is too large\n", eccsize);
+ goto fail;
+ }
+
+ /*
+ * ecc->steps and ecc->total might be used by mtd->ooblayout->ecc(),
+ * which is called by mtd_ooblayout_count_eccbytes().
+ * Make sure they are properly initialized before calling
+ * mtd_ooblayout_count_eccbytes().
+ * FIXME: we should probably rework the sequencing in nand_scan_tail()
+ * to avoid setting those fields twice.
+ */
+ nand->ecc.steps = eccsteps;
+ nand->ecc.total = eccsteps * eccbytes;
+ if (mtd_ooblayout_count_eccbytes(mtd) != (eccsteps*eccbytes)) {
+ pr_warn("invalid ecc layout\n");
+ goto fail;
+ }
+
+ nbc->eccmask = kmalloc(eccbytes, GFP_KERNEL);
+ nbc->errloc = kmalloc(t*sizeof(*nbc->errloc), GFP_KERNEL);
+ if (!nbc->eccmask || !nbc->errloc)
+ goto fail;
+ /*
+ * compute and store the inverted ecc of an erased ecc block
+ */
+ erased_page = kmalloc(eccsize, GFP_KERNEL);
+ if (!erased_page)
+ goto fail;
+
+ memset(erased_page, 0xff, eccsize);
+ memset(nbc->eccmask, 0, eccbytes);
+ encode_bch(nbc->bch, erased_page, eccsize, nbc->eccmask);
+ kfree(erased_page);
+
+ for (i = 0; i < eccbytes; i++)
+ nbc->eccmask[i] ^= 0xff;
+
+ if (!eccstrength)
+ nand->ecc.strength = (eccbytes * 8) / fls(8 * eccsize);
+
+ return nbc;
+fail:
+ nand_bch_free(nbc);
+ return NULL;
+}
+EXPORT_SYMBOL(nand_bch_init);
+
+/**
+ * nand_bch_free - [NAND Interface] Release NAND BCH ECC resources
+ * @nbc: NAND BCH control structure
+ */
+void nand_bch_free(struct nand_bch_control *nbc)
+{
+ if (nbc) {
+ free_bch(nbc->bch);
+ kfree(nbc->errloc);
+ kfree(nbc->eccmask);
+ kfree(nbc);
+ }
+}
+EXPORT_SYMBOL(nand_bch_free);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ivan Djelic <ivan.djelic@parrot.com>");
+MODULE_DESCRIPTION("NAND software BCH ECC support");
diff --git a/drivers/mtd/nand/raw/nand_ecc.c b/drivers/mtd/nand/raw/nand_ecc.c
new file mode 100644
index 000000000000..8e132edbc5ce
--- /dev/null
+++ b/drivers/mtd/nand/raw/nand_ecc.c
@@ -0,0 +1,511 @@
+/*
+ * This file contains an ECC algorithm that detects and corrects 1 bit
+ * errors in a 256 byte block of data.
+ *
+ * Copyright © 2008 Koninklijke Philips Electronics NV.
+ * Author: Frans Meulenbroeks
+ *
+ * Completely replaces the previous ECC implementation which was written by:
+ * Steven J. Hill (sjhill@realitydiluted.com)
+ * Thomas Gleixner (tglx@linutronix.de)
+ *
+ * Information on how this algorithm works and how it was developed
+ * can be found in Documentation/mtd/nand_ecc.txt
+ *
+ * This file 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 file 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 file; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <asm/byteorder.h>
+
+/*
+ * invparity is a 256 byte table that contains the odd parity
+ * for each byte. So if the number of bits in a byte is even,
+ * the array element is 1, and when the number of bits is odd
+ * the array eleemnt is 0.
+ */
+static const char invparity[256] = {
+ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
+ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
+ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
+ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
+ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
+ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
+ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
+ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1
+};
+
+/*
+ * bitsperbyte contains the number of bits per byte
+ * this is only used for testing and repairing parity
+ * (a precalculated value slightly improves performance)
+ */
+static const char bitsperbyte[256] = {
+ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8,
+};
+
+/*
+ * addressbits is a lookup table to filter out the bits from the xor-ed
+ * ECC data that identify the faulty location.
+ * this is only used for repairing parity
+ * see the comments in nand_correct_data for more details
+ */
+static const char addressbits[256] = {
+ 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03,
+ 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03,
+ 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05,
+ 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07,
+ 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05,
+ 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07,
+ 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03,
+ 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
+ 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03,
+ 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05,
+ 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07,
+ 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05,
+ 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07,
+ 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09,
+ 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b,
+ 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09,
+ 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b,
+ 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d,
+ 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f,
+ 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d,
+ 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f,
+ 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09,
+ 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b,
+ 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09,
+ 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b,
+ 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d,
+ 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f,
+ 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d,
+ 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f
+};
+
+/**
+ * __nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte
+ * block
+ * @buf: input buffer with raw data
+ * @eccsize: data bytes per ECC step (256 or 512)
+ * @code: output buffer with ECC
+ */
+void __nand_calculate_ecc(const unsigned char *buf, unsigned int eccsize,
+ unsigned char *code)
+{
+ int i;
+ const uint32_t *bp = (uint32_t *)buf;
+ /* 256 or 512 bytes/ecc */
+ const uint32_t eccsize_mult = eccsize >> 8;
+ uint32_t cur; /* current value in buffer */
+ /* rp0..rp15..rp17 are the various accumulated parities (per byte) */
+ uint32_t rp0, rp1, rp2, rp3, rp4, rp5, rp6, rp7;
+ uint32_t rp8, rp9, rp10, rp11, rp12, rp13, rp14, rp15, rp16;
+ uint32_t uninitialized_var(rp17); /* to make compiler happy */
+ uint32_t par; /* the cumulative parity for all data */
+ uint32_t tmppar; /* the cumulative parity for this iteration;
+ for rp12, rp14 and rp16 at the end of the
+ loop */
+
+ par = 0;
+ rp4 = 0;
+ rp6 = 0;
+ rp8 = 0;
+ rp10 = 0;
+ rp12 = 0;
+ rp14 = 0;
+ rp16 = 0;
+
+ /*
+ * The loop is unrolled a number of times;
+ * This avoids if statements to decide on which rp value to update
+ * Also we process the data by longwords.
+ * Note: passing unaligned data might give a performance penalty.
+ * It is assumed that the buffers are aligned.
+ * tmppar is the cumulative sum of this iteration.
+ * needed for calculating rp12, rp14, rp16 and par
+ * also used as a performance improvement for rp6, rp8 and rp10
+ */
+ for (i = 0; i < eccsize_mult << 2; i++) {
+ cur = *bp++;
+ tmppar = cur;
+ rp4 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp6 ^= tmppar;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp4 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp8 ^= tmppar;
+
+ cur = *bp++;
+ tmppar ^= cur;
+ rp4 ^= cur;
+ rp6 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp6 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp4 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp10 ^= tmppar;
+
+ cur = *bp++;
+ tmppar ^= cur;
+ rp4 ^= cur;
+ rp6 ^= cur;
+ rp8 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp6 ^= cur;
+ rp8 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp4 ^= cur;
+ rp8 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp8 ^= cur;
+
+ cur = *bp++;
+ tmppar ^= cur;
+ rp4 ^= cur;
+ rp6 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp6 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+ rp4 ^= cur;
+ cur = *bp++;
+ tmppar ^= cur;
+
+ par ^= tmppar;
+ if ((i & 0x1) == 0)
+ rp12 ^= tmppar;
+ if ((i & 0x2) == 0)
+ rp14 ^= tmppar;
+ if (eccsize_mult == 2 && (i & 0x4) == 0)
+ rp16 ^= tmppar;
+ }
+
+ /*
+ * handle the fact that we use longword operations
+ * we'll bring rp4..rp14..rp16 back to single byte entities by
+ * shifting and xoring first fold the upper and lower 16 bits,
+ * then the upper and lower 8 bits.
+ */
+ rp4 ^= (rp4 >> 16);
+ rp4 ^= (rp4 >> 8);
+ rp4 &= 0xff;
+ rp6 ^= (rp6 >> 16);
+ rp6 ^= (rp6 >> 8);
+ rp6 &= 0xff;
+ rp8 ^= (rp8 >> 16);
+ rp8 ^= (rp8 >> 8);
+ rp8 &= 0xff;
+ rp10 ^= (rp10 >> 16);
+ rp10 ^= (rp10 >> 8);
+ rp10 &= 0xff;
+ rp12 ^= (rp12 >> 16);
+ rp12 ^= (rp12 >> 8);
+ rp12 &= 0xff;
+ rp14 ^= (rp14 >> 16);
+ rp14 ^= (rp14 >> 8);
+ rp14 &= 0xff;
+ if (eccsize_mult == 2) {
+ rp16 ^= (rp16 >> 16);
+ rp16 ^= (rp16 >> 8);
+ rp16 &= 0xff;
+ }
+
+ /*
+ * we also need to calculate the row parity for rp0..rp3
+ * This is present in par, because par is now
+ * rp3 rp3 rp2 rp2 in little endian and
+ * rp2 rp2 rp3 rp3 in big endian
+ * as well as
+ * rp1 rp0 rp1 rp0 in little endian and
+ * rp0 rp1 rp0 rp1 in big endian
+ * First calculate rp2 and rp3
+ */
+#ifdef __BIG_ENDIAN
+ rp2 = (par >> 16);
+ rp2 ^= (rp2 >> 8);
+ rp2 &= 0xff;
+ rp3 = par & 0xffff;
+ rp3 ^= (rp3 >> 8);
+ rp3 &= 0xff;
+#else
+ rp3 = (par >> 16);
+ rp3 ^= (rp3 >> 8);
+ rp3 &= 0xff;
+ rp2 = par & 0xffff;
+ rp2 ^= (rp2 >> 8);
+ rp2 &= 0xff;
+#endif
+
+ /* reduce par to 16 bits then calculate rp1 and rp0 */
+ par ^= (par >> 16);
+#ifdef __BIG_ENDIAN
+ rp0 = (par >> 8) & 0xff;
+ rp1 = (par & 0xff);
+#else
+ rp1 = (par >> 8) & 0xff;
+ rp0 = (par & 0xff);
+#endif
+
+ /* finally reduce par to 8 bits */
+ par ^= (par >> 8);
+ par &= 0xff;
+
+ /*
+ * and calculate rp5..rp15..rp17
+ * note that par = rp4 ^ rp5 and due to the commutative property
+ * of the ^ operator we can say:
+ * rp5 = (par ^ rp4);
+ * The & 0xff seems superfluous, but benchmarking learned that
+ * leaving it out gives slightly worse results. No idea why, probably
+ * it has to do with the way the pipeline in pentium is organized.
+ */
+ rp5 = (par ^ rp4) & 0xff;
+ rp7 = (par ^ rp6) & 0xff;
+ rp9 = (par ^ rp8) & 0xff;
+ rp11 = (par ^ rp10) & 0xff;
+ rp13 = (par ^ rp12) & 0xff;
+ rp15 = (par ^ rp14) & 0xff;
+ if (eccsize_mult == 2)
+ rp17 = (par ^ rp16) & 0xff;
+
+ /*
+ * Finally calculate the ECC bits.
+ * Again here it might seem that there are performance optimisations
+ * possible, but benchmarks showed that on the system this is developed
+ * the code below is the fastest
+ */
+#ifdef CONFIG_MTD_NAND_ECC_SMC
+ code[0] =
+ (invparity[rp7] << 7) |
+ (invparity[rp6] << 6) |
+ (invparity[rp5] << 5) |
+ (invparity[rp4] << 4) |
+ (invparity[rp3] << 3) |
+ (invparity[rp2] << 2) |
+ (invparity[rp1] << 1) |
+ (invparity[rp0]);
+ code[1] =
+ (invparity[rp15] << 7) |
+ (invparity[rp14] << 6) |
+ (invparity[rp13] << 5) |
+ (invparity[rp12] << 4) |
+ (invparity[rp11] << 3) |
+ (invparity[rp10] << 2) |
+ (invparity[rp9] << 1) |
+ (invparity[rp8]);
+#else
+ code[1] =
+ (invparity[rp7] << 7) |
+ (invparity[rp6] << 6) |
+ (invparity[rp5] << 5) |
+ (invparity[rp4] << 4) |
+ (invparity[rp3] << 3) |
+ (invparity[rp2] << 2) |
+ (invparity[rp1] << 1) |
+ (invparity[rp0]);
+ code[0] =
+ (invparity[rp15] << 7) |
+ (invparity[rp14] << 6) |
+ (invparity[rp13] << 5) |
+ (invparity[rp12] << 4) |
+ (invparity[rp11] << 3) |
+ (invparity[rp10] << 2) |
+ (invparity[rp9] << 1) |
+ (invparity[rp8]);
+#endif
+ if (eccsize_mult == 1)
+ code[2] =
+ (invparity[par & 0xf0] << 7) |
+ (invparity[par & 0x0f] << 6) |
+ (invparity[par & 0xcc] << 5) |
+ (invparity[par & 0x33] << 4) |
+ (invparity[par & 0xaa] << 3) |
+ (invparity[par & 0x55] << 2) |
+ 3;
+ else
+ code[2] =
+ (invparity[par & 0xf0] << 7) |
+ (invparity[par & 0x0f] << 6) |
+ (invparity[par & 0xcc] << 5) |
+ (invparity[par & 0x33] << 4) |
+ (invparity[par & 0xaa] << 3) |
+ (invparity[par & 0x55] << 2) |
+ (invparity[rp17] << 1) |
+ (invparity[rp16] << 0);
+}
+EXPORT_SYMBOL(__nand_calculate_ecc);
+
+/**
+ * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte
+ * block
+ * @mtd: MTD block structure
+ * @buf: input buffer with raw data
+ * @code: output buffer with ECC
+ */
+int nand_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf,
+ unsigned char *code)
+{
+ __nand_calculate_ecc(buf,
+ mtd_to_nand(mtd)->ecc.size, code);
+
+ return 0;
+}
+EXPORT_SYMBOL(nand_calculate_ecc);
+
+/**
+ * __nand_correct_data - [NAND Interface] Detect and correct bit error(s)
+ * @buf: raw data read from the chip
+ * @read_ecc: ECC from the chip
+ * @calc_ecc: the ECC calculated from raw data
+ * @eccsize: data bytes per ECC step (256 or 512)
+ *
+ * Detect and correct a 1 bit error for eccsize byte block
+ */
+int __nand_correct_data(unsigned char *buf,
+ unsigned char *read_ecc, unsigned char *calc_ecc,
+ unsigned int eccsize)
+{
+ unsigned char b0, b1, b2, bit_addr;
+ unsigned int byte_addr;
+ /* 256 or 512 bytes/ecc */
+ const uint32_t eccsize_mult = eccsize >> 8;
+
+ /*
+ * b0 to b2 indicate which bit is faulty (if any)
+ * we might need the xor result more than once,
+ * so keep them in a local var
+ */
+#ifdef CONFIG_MTD_NAND_ECC_SMC
+ b0 = read_ecc[0] ^ calc_ecc[0];
+ b1 = read_ecc[1] ^ calc_ecc[1];
+#else
+ b0 = read_ecc[1] ^ calc_ecc[1];
+ b1 = read_ecc[0] ^ calc_ecc[0];
+#endif
+ b2 = read_ecc[2] ^ calc_ecc[2];
+
+ /* check if there are any bitfaults */
+
+ /* repeated if statements are slightly more efficient than switch ... */
+ /* ordered in order of likelihood */
+
+ if ((b0 | b1 | b2) == 0)
+ return 0; /* no error */
+
+ if ((((b0 ^ (b0 >> 1)) & 0x55) == 0x55) &&
+ (((b1 ^ (b1 >> 1)) & 0x55) == 0x55) &&
+ ((eccsize_mult == 1 && ((b2 ^ (b2 >> 1)) & 0x54) == 0x54) ||
+ (eccsize_mult == 2 && ((b2 ^ (b2 >> 1)) & 0x55) == 0x55))) {
+ /* single bit error */
+ /*
+ * rp17/rp15/13/11/9/7/5/3/1 indicate which byte is the faulty
+ * byte, cp 5/3/1 indicate the faulty bit.
+ * A lookup table (called addressbits) is used to filter
+ * the bits from the byte they are in.
+ * A marginal optimisation is possible by having three
+ * different lookup tables.
+ * One as we have now (for b0), one for b2
+ * (that would avoid the >> 1), and one for b1 (with all values
+ * << 4). However it was felt that introducing two more tables
+ * hardly justify the gain.
+ *
+ * The b2 shift is there to get rid of the lowest two bits.
+ * We could also do addressbits[b2] >> 1 but for the
+ * performance it does not make any difference
+ */
+ if (eccsize_mult == 1)
+ byte_addr = (addressbits[b1] << 4) + addressbits[b0];
+ else
+ byte_addr = (addressbits[b2 & 0x3] << 8) +
+ (addressbits[b1] << 4) + addressbits[b0];
+ bit_addr = addressbits[b2 >> 2];
+ /* flip the bit */
+ buf[byte_addr] ^= (1 << bit_addr);
+ return 1;
+
+ }
+ /* count nr of bits; use table lookup, faster than calculating it */
+ if ((bitsperbyte[b0] + bitsperbyte[b1] + bitsperbyte[b2]) == 1)
+ return 1; /* error in ECC data; no action needed */
+
+ pr_err("%s: uncorrectable ECC error\n", __func__);
+ return -EBADMSG;
+}
+EXPORT_SYMBOL(__nand_correct_data);
+
+/**
+ * nand_correct_data - [NAND Interface] Detect and correct bit error(s)
+ * @mtd: MTD block structure
+ * @buf: raw data read from the chip
+ * @read_ecc: ECC from the chip
+ * @calc_ecc: the ECC calculated from raw data
+ *
+ * Detect and correct a 1 bit error for 256/512 byte block
+ */
+int nand_correct_data(struct mtd_info *mtd, unsigned char *buf,
+ unsigned char *read_ecc, unsigned char *calc_ecc)
+{
+ return __nand_correct_data(buf, read_ecc, calc_ecc,
+ mtd_to_nand(mtd)->ecc.size);
+}
+EXPORT_SYMBOL(nand_correct_data);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Frans Meulenbroeks <fransmeulenbroeks@gmail.com>");
+MODULE_DESCRIPTION("Generic NAND ECC support");
diff --git a/drivers/mtd/nand/nand_hynix.c b/drivers/mtd/nand/raw/nand_hynix.c
index d542908a0ebb..d542908a0ebb 100644
--- a/drivers/mtd/nand/nand_hynix.c
+++ b/drivers/mtd/nand/raw/nand_hynix.c
diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/raw/nand_ids.c
index 5423c3bb388e..5423c3bb388e 100644
--- a/drivers/mtd/nand/nand_ids.c
+++ b/drivers/mtd/nand/raw/nand_ids.c
diff --git a/drivers/mtd/nand/raw/nand_macronix.c b/drivers/mtd/nand/raw/nand_macronix.c
new file mode 100644
index 000000000000..7ed1f87e742a
--- /dev/null
+++ b/drivers/mtd/nand/raw/nand_macronix.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 Free Electrons
+ * Copyright (C) 2017 NextThing Co
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.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.
+ */
+
+#include <linux/mtd/rawnand.h>
+
+static int macronix_nand_init(struct nand_chip *chip)
+{
+ if (nand_is_slc(chip))
+ chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
+
+ /*
+ * MX30LF2G18AC chip does not support using SET/GET_FEATURES to change
+ * the timings unlike what is declared in the parameter page. Unflag
+ * this feature to avoid unnecessary downturns.
+ */
+ if (chip->parameters.supports_set_get_features &&
+ !strcmp("MX30LF2G18AC", chip->parameters.model)) {
+ bitmap_clear(chip->parameters.get_feature_list,
+ ONFI_FEATURE_ADDR_TIMING_MODE, 1);
+ bitmap_clear(chip->parameters.set_feature_list,
+ ONFI_FEATURE_ADDR_TIMING_MODE, 1);
+ }
+
+ return 0;
+}
+
+const struct nand_manufacturer_ops macronix_nand_manuf_ops = {
+ .init = macronix_nand_init,
+};
diff --git a/drivers/mtd/nand/raw/nand_micron.c b/drivers/mtd/nand/raw/nand_micron.c
new file mode 100644
index 000000000000..0af45b134c0c
--- /dev/null
+++ b/drivers/mtd/nand/raw/nand_micron.c
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2017 Free Electrons
+ * Copyright (C) 2017 NextThing Co
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.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.
+ */
+
+#include <linux/mtd/rawnand.h>
+
+/*
+ * Special Micron status bit that indicates when the block has been
+ * corrected by on-die ECC and should be rewritten
+ */
+#define NAND_STATUS_WRITE_RECOMMENDED BIT(3)
+
+struct nand_onfi_vendor_micron {
+ u8 two_plane_read;
+ u8 read_cache;
+ u8 read_unique_id;
+ u8 dq_imped;
+ u8 dq_imped_num_settings;
+ u8 dq_imped_feat_addr;
+ u8 rb_pulldown_strength;
+ u8 rb_pulldown_strength_feat_addr;
+ u8 rb_pulldown_strength_num_settings;
+ u8 otp_mode;
+ u8 otp_page_start;
+ u8 otp_data_prot_addr;
+ u8 otp_num_pages;
+ u8 otp_feat_addr;
+ u8 read_retry_options;
+ u8 reserved[72];
+ u8 param_revision;
+} __packed;
+
+static int micron_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = {retry_mode};
+
+ return nand_set_features(chip, ONFI_FEATURE_ADDR_READ_RETRY, feature);
+}
+
+/*
+ * Configure chip properties from Micron vendor-specific ONFI table
+ */
+static int micron_nand_onfi_init(struct nand_chip *chip)
+{
+ struct nand_parameters *p = &chip->parameters;
+ struct nand_onfi_vendor_micron *micron = (void *)p->onfi.vendor;
+
+ if (chip->parameters.onfi.version && p->onfi.vendor_revision) {
+ chip->read_retries = micron->read_retry_options;
+ chip->setup_read_retry = micron_nand_setup_read_retry;
+ }
+
+ if (p->supports_set_get_features) {
+ set_bit(ONFI_FEATURE_ADDR_READ_RETRY, p->set_feature_list);
+ set_bit(ONFI_FEATURE_ADDR_READ_RETRY, p->get_feature_list);
+ }
+
+ return 0;
+}
+
+static int micron_nand_on_die_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ if (section >= 4)
+ return -ERANGE;
+
+ oobregion->offset = (section * 16) + 8;
+ oobregion->length = 8;
+
+ return 0;
+}
+
+static int micron_nand_on_die_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ if (section >= 4)
+ return -ERANGE;
+
+ oobregion->offset = (section * 16) + 2;
+ oobregion->length = 6;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops micron_nand_on_die_ooblayout_ops = {
+ .ecc = micron_nand_on_die_ooblayout_ecc,
+ .free = micron_nand_on_die_ooblayout_free,
+};
+
+static int micron_nand_on_die_ecc_setup(struct nand_chip *chip, bool enable)
+{
+ u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = { 0, };
+
+ if (enable)
+ feature[0] |= ONFI_FEATURE_ON_DIE_ECC_EN;
+
+ return nand_set_features(chip, ONFI_FEATURE_ON_DIE_ECC, feature);
+}
+
+static int
+micron_nand_read_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required,
+ int page)
+{
+ u8 status;
+ int ret, max_bitflips = 0;
+
+ ret = micron_nand_on_die_ecc_setup(chip, true);
+ if (ret)
+ return ret;
+
+ ret = nand_read_page_op(chip, page, 0, NULL, 0);
+ if (ret)
+ goto out;
+
+ ret = nand_status_op(chip, &status);
+ if (ret)
+ goto out;
+
+ ret = nand_exit_status_op(chip);
+ if (ret)
+ goto out;
+
+ if (status & NAND_STATUS_FAIL)
+ mtd->ecc_stats.failed++;
+
+ /*
+ * The internal ECC doesn't tell us the number of bitflips
+ * that have been corrected, but tells us if it recommends to
+ * rewrite the block. If it's the case, then we pretend we had
+ * a number of bitflips equal to the ECC strength, which will
+ * hint the NAND core to rewrite the block.
+ */
+ else if (status & NAND_STATUS_WRITE_RECOMMENDED)
+ max_bitflips = chip->ecc.strength;
+
+ ret = nand_read_data_op(chip, buf, mtd->writesize, false);
+ if (!ret && oob_required)
+ ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize,
+ false);
+
+out:
+ micron_nand_on_die_ecc_setup(chip, false);
+
+ return ret ? ret : max_bitflips;
+}
+
+static int
+micron_nand_write_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required,
+ int page)
+{
+ int ret;
+
+ ret = micron_nand_on_die_ecc_setup(chip, true);
+ if (ret)
+ return ret;
+
+ ret = nand_write_page_raw(mtd, chip, buf, oob_required, page);
+ micron_nand_on_die_ecc_setup(chip, false);
+
+ return ret;
+}
+
+enum {
+ /* The NAND flash doesn't support on-die ECC */
+ MICRON_ON_DIE_UNSUPPORTED,
+
+ /*
+ * The NAND flash supports on-die ECC and it can be
+ * enabled/disabled by a set features command.
+ */
+ MICRON_ON_DIE_SUPPORTED,
+
+ /*
+ * The NAND flash supports on-die ECC, and it cannot be
+ * disabled.
+ */
+ MICRON_ON_DIE_MANDATORY,
+};
+
+/*
+ * Try to detect if the NAND support on-die ECC. To do this, we enable
+ * the feature, and read back if it has been enabled as expected. We
+ * also check if it can be disabled, because some Micron NANDs do not
+ * allow disabling the on-die ECC and we don't support such NANDs for
+ * now.
+ *
+ * This function also has the side effect of disabling on-die ECC if
+ * it had been left enabled by the firmware/bootloader.
+ */
+static int micron_supports_on_die_ecc(struct nand_chip *chip)
+{
+ u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = { 0, };
+ int ret;
+
+ if (!chip->parameters.onfi.version)
+ return MICRON_ON_DIE_UNSUPPORTED;
+
+ if (chip->bits_per_cell != 1)
+ return MICRON_ON_DIE_UNSUPPORTED;
+
+ ret = micron_nand_on_die_ecc_setup(chip, true);
+ if (ret)
+ return MICRON_ON_DIE_UNSUPPORTED;
+
+ ret = nand_get_features(chip, ONFI_FEATURE_ON_DIE_ECC, feature);
+ if (ret < 0)
+ return ret;
+
+ if ((feature[0] & ONFI_FEATURE_ON_DIE_ECC_EN) == 0)
+ return MICRON_ON_DIE_UNSUPPORTED;
+
+ ret = micron_nand_on_die_ecc_setup(chip, false);
+ if (ret)
+ return MICRON_ON_DIE_UNSUPPORTED;
+
+ ret = nand_get_features(chip, ONFI_FEATURE_ON_DIE_ECC, feature);
+ if (ret < 0)
+ return ret;
+
+ if (feature[0] & ONFI_FEATURE_ON_DIE_ECC_EN)
+ return MICRON_ON_DIE_MANDATORY;
+
+ /*
+ * Some Micron NANDs have an on-die ECC of 4/512, some other
+ * 8/512. We only support the former.
+ */
+ if (chip->ecc_strength_ds != 4)
+ return MICRON_ON_DIE_UNSUPPORTED;
+
+ return MICRON_ON_DIE_SUPPORTED;
+}
+
+static int micron_nand_init(struct nand_chip *chip)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int ondie;
+ int ret;
+
+ ret = micron_nand_onfi_init(chip);
+ if (ret)
+ return ret;
+
+ if (mtd->writesize == 2048)
+ chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
+
+ ondie = micron_supports_on_die_ecc(chip);
+
+ if (ondie == MICRON_ON_DIE_MANDATORY) {
+ pr_err("On-die ECC forcefully enabled, not supported\n");
+ return -EINVAL;
+ }
+
+ if (chip->ecc.mode == NAND_ECC_ON_DIE) {
+ if (ondie == MICRON_ON_DIE_UNSUPPORTED) {
+ pr_err("On-die ECC selected but not supported\n");
+ return -EINVAL;
+ }
+
+ chip->ecc.bytes = 8;
+ chip->ecc.size = 512;
+ chip->ecc.strength = 4;
+ chip->ecc.algo = NAND_ECC_BCH;
+ chip->ecc.read_page = micron_nand_read_page_on_die_ecc;
+ chip->ecc.write_page = micron_nand_write_page_on_die_ecc;
+ chip->ecc.read_page_raw = nand_read_page_raw;
+ chip->ecc.write_page_raw = nand_write_page_raw;
+
+ mtd_set_ooblayout(mtd, &micron_nand_on_die_ooblayout_ops);
+ }
+
+ return 0;
+}
+
+const struct nand_manufacturer_ops micron_nand_manuf_ops = {
+ .init = micron_nand_init,
+};
diff --git a/drivers/mtd/nand/nand_samsung.c b/drivers/mtd/nand/raw/nand_samsung.c
index ef022f62f74c..ef022f62f74c 100644
--- a/drivers/mtd/nand/nand_samsung.c
+++ b/drivers/mtd/nand/raw/nand_samsung.c
diff --git a/drivers/mtd/nand/raw/nand_timings.c b/drivers/mtd/nand/raw/nand_timings.c
new file mode 100644
index 000000000000..7c4e4a371bbc
--- /dev/null
+++ b/drivers/mtd/nand/raw/nand_timings.c
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2014 Free Electrons
+ *
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/mtd/rawnand.h>
+
+static const struct nand_data_interface onfi_sdr_timings[] = {
+ /* Mode 0 */
+ {
+ .type = NAND_SDR_IFACE,
+ .timings.sdr = {
+ .tCCS_min = 500000,
+ .tR_max = 200000000,
+ .tADL_min = 400000,
+ .tALH_min = 20000,
+ .tALS_min = 50000,
+ .tAR_min = 25000,
+ .tCEA_max = 100000,
+ .tCEH_min = 20000,
+ .tCH_min = 20000,
+ .tCHZ_max = 100000,
+ .tCLH_min = 20000,
+ .tCLR_min = 20000,
+ .tCLS_min = 50000,
+ .tCOH_min = 0,
+ .tCS_min = 70000,
+ .tDH_min = 20000,
+ .tDS_min = 40000,
+ .tFEAT_max = 1000000,
+ .tIR_min = 10000,
+ .tITC_max = 1000000,
+ .tRC_min = 100000,
+ .tREA_max = 40000,
+ .tREH_min = 30000,
+ .tRHOH_min = 0,
+ .tRHW_min = 200000,
+ .tRHZ_max = 200000,
+ .tRLOH_min = 0,
+ .tRP_min = 50000,
+ .tRR_min = 40000,
+ .tRST_max = 250000000000ULL,
+ .tWB_max = 200000,
+ .tWC_min = 100000,
+ .tWH_min = 30000,
+ .tWHR_min = 120000,
+ .tWP_min = 50000,
+ .tWW_min = 100000,
+ },
+ },
+ /* Mode 1 */
+ {
+ .type = NAND_SDR_IFACE,
+ .timings.sdr = {
+ .tCCS_min = 500000,
+ .tR_max = 200000000,
+ .tADL_min = 400000,
+ .tALH_min = 10000,
+ .tALS_min = 25000,
+ .tAR_min = 10000,
+ .tCEA_max = 45000,
+ .tCEH_min = 20000,
+ .tCH_min = 10000,
+ .tCHZ_max = 50000,
+ .tCLH_min = 10000,
+ .tCLR_min = 10000,
+ .tCLS_min = 25000,
+ .tCOH_min = 15000,
+ .tCS_min = 35000,
+ .tDH_min = 10000,
+ .tDS_min = 20000,
+ .tFEAT_max = 1000000,
+ .tIR_min = 0,
+ .tITC_max = 1000000,
+ .tRC_min = 50000,
+ .tREA_max = 30000,
+ .tREH_min = 15000,
+ .tRHOH_min = 15000,
+ .tRHW_min = 100000,
+ .tRHZ_max = 100000,
+ .tRLOH_min = 0,
+ .tRP_min = 25000,
+ .tRR_min = 20000,
+ .tRST_max = 500000000,
+ .tWB_max = 100000,
+ .tWC_min = 45000,
+ .tWH_min = 15000,
+ .tWHR_min = 80000,
+ .tWP_min = 25000,
+ .tWW_min = 100000,
+ },
+ },
+ /* Mode 2 */
+ {
+ .type = NAND_SDR_IFACE,
+ .timings.sdr = {
+ .tCCS_min = 500000,
+ .tR_max = 200000000,
+ .tADL_min = 400000,
+ .tALH_min = 10000,
+ .tALS_min = 15000,
+ .tAR_min = 10000,
+ .tCEA_max = 30000,
+ .tCEH_min = 20000,
+ .tCH_min = 10000,
+ .tCHZ_max = 50000,
+ .tCLH_min = 10000,
+ .tCLR_min = 10000,
+ .tCLS_min = 15000,
+ .tCOH_min = 15000,
+ .tCS_min = 25000,
+ .tDH_min = 5000,
+ .tDS_min = 15000,
+ .tFEAT_max = 1000000,
+ .tIR_min = 0,
+ .tITC_max = 1000000,
+ .tRC_min = 35000,
+ .tREA_max = 25000,
+ .tREH_min = 15000,
+ .tRHOH_min = 15000,
+ .tRHW_min = 100000,
+ .tRHZ_max = 100000,
+ .tRLOH_min = 0,
+ .tRR_min = 20000,
+ .tRST_max = 500000000,
+ .tWB_max = 100000,
+ .tRP_min = 17000,
+ .tWC_min = 35000,
+ .tWH_min = 15000,
+ .tWHR_min = 80000,
+ .tWP_min = 17000,
+ .tWW_min = 100000,
+ },
+ },
+ /* Mode 3 */
+ {
+ .type = NAND_SDR_IFACE,
+ .timings.sdr = {
+ .tCCS_min = 500000,
+ .tR_max = 200000000,
+ .tADL_min = 400000,
+ .tALH_min = 5000,
+ .tALS_min = 10000,
+ .tAR_min = 10000,
+ .tCEA_max = 25000,
+ .tCEH_min = 20000,
+ .tCH_min = 5000,
+ .tCHZ_max = 50000,
+ .tCLH_min = 5000,
+ .tCLR_min = 10000,
+ .tCLS_min = 10000,
+ .tCOH_min = 15000,
+ .tCS_min = 25000,
+ .tDH_min = 5000,
+ .tDS_min = 10000,
+ .tFEAT_max = 1000000,
+ .tIR_min = 0,
+ .tITC_max = 1000000,
+ .tRC_min = 30000,
+ .tREA_max = 20000,
+ .tREH_min = 10000,
+ .tRHOH_min = 15000,
+ .tRHW_min = 100000,
+ .tRHZ_max = 100000,
+ .tRLOH_min = 0,
+ .tRP_min = 15000,
+ .tRR_min = 20000,
+ .tRST_max = 500000000,
+ .tWB_max = 100000,
+ .tWC_min = 30000,
+ .tWH_min = 10000,
+ .tWHR_min = 80000,
+ .tWP_min = 15000,
+ .tWW_min = 100000,
+ },
+ },
+ /* Mode 4 */
+ {
+ .type = NAND_SDR_IFACE,
+ .timings.sdr = {
+ .tCCS_min = 500000,
+ .tR_max = 200000000,
+ .tADL_min = 400000,
+ .tALH_min = 5000,
+ .tALS_min = 10000,
+ .tAR_min = 10000,
+ .tCEA_max = 25000,
+ .tCEH_min = 20000,
+ .tCH_min = 5000,
+ .tCHZ_max = 30000,
+ .tCLH_min = 5000,
+ .tCLR_min = 10000,
+ .tCLS_min = 10000,
+ .tCOH_min = 15000,
+ .tCS_min = 20000,
+ .tDH_min = 5000,
+ .tDS_min = 10000,
+ .tFEAT_max = 1000000,
+ .tIR_min = 0,
+ .tITC_max = 1000000,
+ .tRC_min = 25000,
+ .tREA_max = 20000,
+ .tREH_min = 10000,
+ .tRHOH_min = 15000,
+ .tRHW_min = 100000,
+ .tRHZ_max = 100000,
+ .tRLOH_min = 5000,
+ .tRP_min = 12000,
+ .tRR_min = 20000,
+ .tRST_max = 500000000,
+ .tWB_max = 100000,
+ .tWC_min = 25000,
+ .tWH_min = 10000,
+ .tWHR_min = 80000,
+ .tWP_min = 12000,
+ .tWW_min = 100000,
+ },
+ },
+ /* Mode 5 */
+ {
+ .type = NAND_SDR_IFACE,
+ .timings.sdr = {
+ .tCCS_min = 500000,
+ .tR_max = 200000000,
+ .tADL_min = 400000,
+ .tALH_min = 5000,
+ .tALS_min = 10000,
+ .tAR_min = 10000,
+ .tCEA_max = 25000,
+ .tCEH_min = 20000,
+ .tCH_min = 5000,
+ .tCHZ_max = 30000,
+ .tCLH_min = 5000,
+ .tCLR_min = 10000,
+ .tCLS_min = 10000,
+ .tCOH_min = 15000,
+ .tCS_min = 15000,
+ .tDH_min = 5000,
+ .tDS_min = 7000,
+ .tFEAT_max = 1000000,
+ .tIR_min = 0,
+ .tITC_max = 1000000,
+ .tRC_min = 20000,
+ .tREA_max = 16000,
+ .tREH_min = 7000,
+ .tRHOH_min = 15000,
+ .tRHW_min = 100000,
+ .tRHZ_max = 100000,
+ .tRLOH_min = 5000,
+ .tRP_min = 10000,
+ .tRR_min = 20000,
+ .tRST_max = 500000000,
+ .tWB_max = 100000,
+ .tWC_min = 20000,
+ .tWH_min = 7000,
+ .tWHR_min = 80000,
+ .tWP_min = 10000,
+ .tWW_min = 100000,
+ },
+ },
+};
+
+/**
+ * onfi_async_timing_mode_to_sdr_timings - [NAND Interface] Retrieve NAND
+ * timings according to the given ONFI timing mode
+ * @mode: ONFI timing mode
+ */
+const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode)
+{
+ if (mode < 0 || mode >= ARRAY_SIZE(onfi_sdr_timings))
+ return ERR_PTR(-EINVAL);
+
+ return &onfi_sdr_timings[mode].timings.sdr;
+}
+EXPORT_SYMBOL(onfi_async_timing_mode_to_sdr_timings);
+
+/**
+ * onfi_fill_data_interface - [NAND Interface] Initialize a data interface from
+ * given ONFI mode
+ * @mode: The ONFI timing mode
+ */
+int onfi_fill_data_interface(struct nand_chip *chip,
+ enum nand_data_interface_type type,
+ int timing_mode)
+{
+ struct nand_data_interface *iface = &chip->data_interface;
+
+ if (type != NAND_SDR_IFACE)
+ return -EINVAL;
+
+ if (timing_mode < 0 || timing_mode >= ARRAY_SIZE(onfi_sdr_timings))
+ return -EINVAL;
+
+ *iface = onfi_sdr_timings[timing_mode];
+
+ /*
+ * Initialize timings that cannot be deduced from timing mode:
+ * tR, tPROG, tCCS, ...
+ * These information are part of the ONFI parameter page.
+ */
+ if (chip->parameters.onfi.version) {
+ struct nand_parameters *params = &chip->parameters;
+ struct nand_sdr_timings *timings = &iface->timings.sdr;
+
+ /* microseconds -> picoseconds */
+ timings->tPROG_max = 1000000ULL * params->onfi.tPROG;
+ timings->tBERS_max = 1000000ULL * params->onfi.tBERS;
+ timings->tR_max = 1000000ULL * params->onfi.tR;
+
+ /* nanoseconds -> picoseconds */
+ timings->tCCS_min = 1000UL * params->onfi.tCCS;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(onfi_fill_data_interface);
diff --git a/drivers/mtd/nand/raw/nand_toshiba.c b/drivers/mtd/nand/raw/nand_toshiba.c
new file mode 100644
index 000000000000..ab43f027cd23
--- /dev/null
+++ b/drivers/mtd/nand/raw/nand_toshiba.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2017 Free Electrons
+ * Copyright (C) 2017 NextThing Co
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.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.
+ */
+
+#include <linux/mtd/rawnand.h>
+
+static void toshiba_nand_decode_id(struct nand_chip *chip)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ nand_decode_ext_id(chip);
+
+ /*
+ * Toshiba 24nm raw SLC (i.e., not BENAND) have 32B OOB per
+ * 512B page. For Toshiba SLC, we decode the 5th/6th byte as
+ * follows:
+ * - ID byte 6, bits[2:0]: 100b -> 43nm, 101b -> 32nm,
+ * 110b -> 24nm
+ * - ID byte 5, bit[7]: 1 -> BENAND, 0 -> raw SLC
+ */
+ if (chip->id.len >= 6 && nand_is_slc(chip) &&
+ (chip->id.data[5] & 0x7) == 0x6 /* 24nm */ &&
+ !(chip->id.data[4] & 0x80) /* !BENAND */)
+ mtd->oobsize = 32 * mtd->writesize >> 9;
+
+ /*
+ * Extract ECC requirements from 6th id byte.
+ * For Toshiba SLC, ecc requrements are as follows:
+ * - 43nm: 1 bit ECC for each 512Byte is required.
+ * - 32nm: 4 bit ECC for each 512Byte is required.
+ * - 24nm: 8 bit ECC for each 512Byte is required.
+ */
+ if (chip->id.len >= 6 && nand_is_slc(chip)) {
+ chip->ecc_step_ds = 512;
+ switch (chip->id.data[5] & 0x7) {
+ case 0x4:
+ chip->ecc_strength_ds = 1;
+ break;
+ case 0x5:
+ chip->ecc_strength_ds = 4;
+ break;
+ case 0x6:
+ chip->ecc_strength_ds = 8;
+ break;
+ default:
+ WARN(1, "Could not get ECC info");
+ chip->ecc_step_ds = 0;
+ break;
+ }
+ }
+}
+
+static int toshiba_nand_init(struct nand_chip *chip)
+{
+ if (nand_is_slc(chip))
+ chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
+
+ return 0;
+}
+
+const struct nand_manufacturer_ops toshiba_nand_manuf_ops = {
+ .detect = toshiba_nand_decode_id,
+ .init = toshiba_nand_init,
+};
diff --git a/drivers/mtd/nand/raw/nandsim.c b/drivers/mtd/nand/raw/nandsim.c
new file mode 100644
index 000000000000..e027c6f9d327
--- /dev/null
+++ b/drivers/mtd/nand/raw/nandsim.c
@@ -0,0 +1,2391 @@
+/*
+ * NAND flash simulator.
+ *
+ * Author: Artem B. Bityuckiy <dedekind@oktetlabs.ru>, <dedekind@infradead.org>
+ *
+ * Copyright (C) 2004 Nokia Corporation
+ *
+ * Note: NS means "NAND Simulator".
+ * Note: Input means input TO flash chip, output means output FROM 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#define pr_fmt(fmt) "[nandsim]" fmt
+
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/vmalloc.h>
+#include <linux/math64.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/nand_bch.h>
+#include <linux/mtd/partitions.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/random.h>
+#include <linux/sched.h>
+#include <linux/sched/mm.h>
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+
+/* Default simulator parameters values */
+#if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE) || \
+ !defined(CONFIG_NANDSIM_SECOND_ID_BYTE) || \
+ !defined(CONFIG_NANDSIM_THIRD_ID_BYTE) || \
+ !defined(CONFIG_NANDSIM_FOURTH_ID_BYTE)
+#define CONFIG_NANDSIM_FIRST_ID_BYTE 0x98
+#define CONFIG_NANDSIM_SECOND_ID_BYTE 0x39
+#define CONFIG_NANDSIM_THIRD_ID_BYTE 0xFF /* No byte */
+#define CONFIG_NANDSIM_FOURTH_ID_BYTE 0xFF /* No byte */
+#endif
+
+#ifndef CONFIG_NANDSIM_ACCESS_DELAY
+#define CONFIG_NANDSIM_ACCESS_DELAY 25
+#endif
+#ifndef CONFIG_NANDSIM_PROGRAMM_DELAY
+#define CONFIG_NANDSIM_PROGRAMM_DELAY 200
+#endif
+#ifndef CONFIG_NANDSIM_ERASE_DELAY
+#define CONFIG_NANDSIM_ERASE_DELAY 2
+#endif
+#ifndef CONFIG_NANDSIM_OUTPUT_CYCLE
+#define CONFIG_NANDSIM_OUTPUT_CYCLE 40
+#endif
+#ifndef CONFIG_NANDSIM_INPUT_CYCLE
+#define CONFIG_NANDSIM_INPUT_CYCLE 50
+#endif
+#ifndef CONFIG_NANDSIM_BUS_WIDTH
+#define CONFIG_NANDSIM_BUS_WIDTH 8
+#endif
+#ifndef CONFIG_NANDSIM_DO_DELAYS
+#define CONFIG_NANDSIM_DO_DELAYS 0
+#endif
+#ifndef CONFIG_NANDSIM_LOG
+#define CONFIG_NANDSIM_LOG 0
+#endif
+#ifndef CONFIG_NANDSIM_DBG
+#define CONFIG_NANDSIM_DBG 0
+#endif
+#ifndef CONFIG_NANDSIM_MAX_PARTS
+#define CONFIG_NANDSIM_MAX_PARTS 32
+#endif
+
+static uint access_delay = CONFIG_NANDSIM_ACCESS_DELAY;
+static uint programm_delay = CONFIG_NANDSIM_PROGRAMM_DELAY;
+static uint erase_delay = CONFIG_NANDSIM_ERASE_DELAY;
+static uint output_cycle = CONFIG_NANDSIM_OUTPUT_CYCLE;
+static uint input_cycle = CONFIG_NANDSIM_INPUT_CYCLE;
+static uint bus_width = CONFIG_NANDSIM_BUS_WIDTH;
+static uint do_delays = CONFIG_NANDSIM_DO_DELAYS;
+static uint log = CONFIG_NANDSIM_LOG;
+static uint dbg = CONFIG_NANDSIM_DBG;
+static unsigned long parts[CONFIG_NANDSIM_MAX_PARTS];
+static unsigned int parts_num;
+static char *badblocks = NULL;
+static char *weakblocks = NULL;
+static char *weakpages = NULL;
+static unsigned int bitflips = 0;
+static char *gravepages = NULL;
+static unsigned int overridesize = 0;
+static char *cache_file = NULL;
+static unsigned int bbt;
+static unsigned int bch;
+static u_char id_bytes[8] = {
+ [0] = CONFIG_NANDSIM_FIRST_ID_BYTE,
+ [1] = CONFIG_NANDSIM_SECOND_ID_BYTE,
+ [2] = CONFIG_NANDSIM_THIRD_ID_BYTE,
+ [3] = CONFIG_NANDSIM_FOURTH_ID_BYTE,
+ [4 ... 7] = 0xFF,
+};
+
+module_param_array(id_bytes, byte, NULL, 0400);
+module_param_named(first_id_byte, id_bytes[0], byte, 0400);
+module_param_named(second_id_byte, id_bytes[1], byte, 0400);
+module_param_named(third_id_byte, id_bytes[2], byte, 0400);
+module_param_named(fourth_id_byte, id_bytes[3], byte, 0400);
+module_param(access_delay, uint, 0400);
+module_param(programm_delay, uint, 0400);
+module_param(erase_delay, uint, 0400);
+module_param(output_cycle, uint, 0400);
+module_param(input_cycle, uint, 0400);
+module_param(bus_width, uint, 0400);
+module_param(do_delays, uint, 0400);
+module_param(log, uint, 0400);
+module_param(dbg, uint, 0400);
+module_param_array(parts, ulong, &parts_num, 0400);
+module_param(badblocks, charp, 0400);
+module_param(weakblocks, charp, 0400);
+module_param(weakpages, charp, 0400);
+module_param(bitflips, uint, 0400);
+module_param(gravepages, charp, 0400);
+module_param(overridesize, uint, 0400);
+module_param(cache_file, charp, 0400);
+module_param(bbt, uint, 0400);
+module_param(bch, uint, 0400);
+
+MODULE_PARM_DESC(id_bytes, "The ID bytes returned by NAND Flash 'read ID' command");
+MODULE_PARM_DESC(first_id_byte, "The first byte returned by NAND Flash 'read ID' command (manufacturer ID) (obsolete)");
+MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID) (obsolete)");
+MODULE_PARM_DESC(third_id_byte, "The third byte returned by NAND Flash 'read ID' command (obsolete)");
+MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read ID' command (obsolete)");
+MODULE_PARM_DESC(access_delay, "Initial page access delay (microseconds)");
+MODULE_PARM_DESC(programm_delay, "Page programm delay (microseconds");
+MODULE_PARM_DESC(erase_delay, "Sector erase delay (milliseconds)");
+MODULE_PARM_DESC(output_cycle, "Word output (from flash) time (nanoseconds)");
+MODULE_PARM_DESC(input_cycle, "Word input (to flash) time (nanoseconds)");
+MODULE_PARM_DESC(bus_width, "Chip's bus width (8- or 16-bit)");
+MODULE_PARM_DESC(do_delays, "Simulate NAND delays using busy-waits if not zero");
+MODULE_PARM_DESC(log, "Perform logging if not zero");
+MODULE_PARM_DESC(dbg, "Output debug information if not zero");
+MODULE_PARM_DESC(parts, "Partition sizes (in erase blocks) separated by commas");
+/* Page and erase block positions for the following parameters are independent of any partitions */
+MODULE_PARM_DESC(badblocks, "Erase blocks that are initially marked bad, separated by commas");
+MODULE_PARM_DESC(weakblocks, "Weak erase blocks [: remaining erase cycles (defaults to 3)]"
+ " separated by commas e.g. 113:2 means eb 113"
+ " can be erased only twice before failing");
+MODULE_PARM_DESC(weakpages, "Weak pages [: maximum writes (defaults to 3)]"
+ " separated by commas e.g. 1401:2 means page 1401"
+ " can be written only twice before failing");
+MODULE_PARM_DESC(bitflips, "Maximum number of random bit flips per page (zero by default)");
+MODULE_PARM_DESC(gravepages, "Pages that lose data [: maximum reads (defaults to 3)]"
+ " separated by commas e.g. 1401:2 means page 1401"
+ " can be read only twice before failing");
+MODULE_PARM_DESC(overridesize, "Specifies the NAND Flash size overriding the ID bytes. "
+ "The size is specified in erase blocks and as the exponent of a power of two"
+ " e.g. 5 means a size of 32 erase blocks");
+MODULE_PARM_DESC(cache_file, "File to use to cache nand pages instead of memory");
+MODULE_PARM_DESC(bbt, "0 OOB, 1 BBT with marker in OOB, 2 BBT with marker in data area");
+MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits should "
+ "be correctable in 512-byte blocks");
+
+/* The largest possible page size */
+#define NS_LARGEST_PAGE_SIZE 4096
+
+/* Simulator's output macros (logging, debugging, warning, error) */
+#define NS_LOG(args...) \
+ do { if (log) pr_debug(" log: " args); } while(0)
+#define NS_DBG(args...) \
+ do { if (dbg) pr_debug(" debug: " args); } while(0)
+#define NS_WARN(args...) \
+ do { pr_warn(" warning: " args); } while(0)
+#define NS_ERR(args...) \
+ do { pr_err(" error: " args); } while(0)
+#define NS_INFO(args...) \
+ do { pr_info(" " args); } while(0)
+
+/* Busy-wait delay macros (microseconds, milliseconds) */
+#define NS_UDELAY(us) \
+ do { if (do_delays) udelay(us); } while(0)
+#define NS_MDELAY(us) \
+ do { if (do_delays) mdelay(us); } while(0)
+
+/* Is the nandsim structure initialized ? */
+#define NS_IS_INITIALIZED(ns) ((ns)->geom.totsz != 0)
+
+/* Good operation completion status */
+#define NS_STATUS_OK(ns) (NAND_STATUS_READY | (NAND_STATUS_WP * ((ns)->lines.wp == 0)))
+
+/* Operation failed completion status */
+#define NS_STATUS_FAILED(ns) (NAND_STATUS_FAIL | NS_STATUS_OK(ns))
+
+/* Calculate the page offset in flash RAM image by (row, column) address */
+#define NS_RAW_OFFSET(ns) \
+ (((ns)->regs.row * (ns)->geom.pgszoob) + (ns)->regs.column)
+
+/* Calculate the OOB offset in flash RAM image by (row, column) address */
+#define NS_RAW_OFFSET_OOB(ns) (NS_RAW_OFFSET(ns) + ns->geom.pgsz)
+
+/* After a command is input, the simulator goes to one of the following states */
+#define STATE_CMD_READ0 0x00000001 /* read data from the beginning of page */
+#define STATE_CMD_READ1 0x00000002 /* read data from the second half of page */
+#define STATE_CMD_READSTART 0x00000003 /* read data second command (large page devices) */
+#define STATE_CMD_PAGEPROG 0x00000004 /* start page program */
+#define STATE_CMD_READOOB 0x00000005 /* read OOB area */
+#define STATE_CMD_ERASE1 0x00000006 /* sector erase first command */
+#define STATE_CMD_STATUS 0x00000007 /* read status */
+#define STATE_CMD_SEQIN 0x00000009 /* sequential data input */
+#define STATE_CMD_READID 0x0000000A /* read ID */
+#define STATE_CMD_ERASE2 0x0000000B /* sector erase second command */
+#define STATE_CMD_RESET 0x0000000C /* reset */
+#define STATE_CMD_RNDOUT 0x0000000D /* random output command */
+#define STATE_CMD_RNDOUTSTART 0x0000000E /* random output start command */
+#define STATE_CMD_MASK 0x0000000F /* command states mask */
+
+/* After an address is input, the simulator goes to one of these states */
+#define STATE_ADDR_PAGE 0x00000010 /* full (row, column) address is accepted */
+#define STATE_ADDR_SEC 0x00000020 /* sector address was accepted */
+#define STATE_ADDR_COLUMN 0x00000030 /* column address was accepted */
+#define STATE_ADDR_ZERO 0x00000040 /* one byte zero address was accepted */
+#define STATE_ADDR_MASK 0x00000070 /* address states mask */
+
+/* During data input/output the simulator is in these states */
+#define STATE_DATAIN 0x00000100 /* waiting for data input */
+#define STATE_DATAIN_MASK 0x00000100 /* data input states mask */
+
+#define STATE_DATAOUT 0x00001000 /* waiting for page data output */
+#define STATE_DATAOUT_ID 0x00002000 /* waiting for ID bytes output */
+#define STATE_DATAOUT_STATUS 0x00003000 /* waiting for status output */
+#define STATE_DATAOUT_MASK 0x00007000 /* data output states mask */
+
+/* Previous operation is done, ready to accept new requests */
+#define STATE_READY 0x00000000
+
+/* This state is used to mark that the next state isn't known yet */
+#define STATE_UNKNOWN 0x10000000
+
+/* Simulator's actions bit masks */
+#define ACTION_CPY 0x00100000 /* copy page/OOB to the internal buffer */
+#define ACTION_PRGPAGE 0x00200000 /* program the internal buffer to flash */
+#define ACTION_SECERASE 0x00300000 /* erase sector */
+#define ACTION_ZEROOFF 0x00400000 /* don't add any offset to address */
+#define ACTION_HALFOFF 0x00500000 /* add to address half of page */
+#define ACTION_OOBOFF 0x00600000 /* add to address OOB offset */
+#define ACTION_MASK 0x00700000 /* action mask */
+
+#define NS_OPER_NUM 13 /* Number of operations supported by the simulator */
+#define NS_OPER_STATES 6 /* Maximum number of states in operation */
+
+#define OPT_ANY 0xFFFFFFFF /* any chip supports this operation */
+#define OPT_PAGE512 0x00000002 /* 512-byte page chips */
+#define OPT_PAGE2048 0x00000008 /* 2048-byte page chips */
+#define OPT_PAGE512_8BIT 0x00000040 /* 512-byte page chips with 8-bit bus width */
+#define OPT_PAGE4096 0x00000080 /* 4096-byte page chips */
+#define OPT_LARGEPAGE (OPT_PAGE2048 | OPT_PAGE4096) /* 2048 & 4096-byte page chips */
+#define OPT_SMALLPAGE (OPT_PAGE512) /* 512-byte page chips */
+
+/* Remove action bits from state */
+#define NS_STATE(x) ((x) & ~ACTION_MASK)
+
+/*
+ * Maximum previous states which need to be saved. Currently saving is
+ * only needed for page program operation with preceded read command
+ * (which is only valid for 512-byte pages).
+ */
+#define NS_MAX_PREVSTATES 1
+
+/* Maximum page cache pages needed to read or write a NAND page to the cache_file */
+#define NS_MAX_HELD_PAGES 16
+
+/*
+ * A union to represent flash memory contents and flash buffer.
+ */
+union ns_mem {
+ u_char *byte; /* for byte access */
+ uint16_t *word; /* for 16-bit word access */
+};
+
+/*
+ * The structure which describes all the internal simulator data.
+ */
+struct nandsim {
+ struct mtd_partition partitions[CONFIG_NANDSIM_MAX_PARTS];
+ unsigned int nbparts;
+
+ uint busw; /* flash chip bus width (8 or 16) */
+ u_char ids[8]; /* chip's ID bytes */
+ uint32_t options; /* chip's characteristic bits */
+ uint32_t state; /* current chip state */
+ uint32_t nxstate; /* next expected state */
+
+ uint32_t *op; /* current operation, NULL operations isn't known yet */
+ uint32_t pstates[NS_MAX_PREVSTATES]; /* previous states */
+ uint16_t npstates; /* number of previous states saved */
+ uint16_t stateidx; /* current state index */
+
+ /* The simulated NAND flash pages array */
+ union ns_mem *pages;
+
+ /* Slab allocator for nand pages */
+ struct kmem_cache *nand_pages_slab;
+
+ /* Internal buffer of page + OOB size bytes */
+ union ns_mem buf;
+
+ /* NAND flash "geometry" */
+ struct {
+ uint64_t totsz; /* total flash size, bytes */
+ uint32_t secsz; /* flash sector (erase block) size, bytes */
+ uint pgsz; /* NAND flash page size, bytes */
+ uint oobsz; /* page OOB area size, bytes */
+ uint64_t totszoob; /* total flash size including OOB, bytes */
+ uint pgszoob; /* page size including OOB , bytes*/
+ uint secszoob; /* sector size including OOB, bytes */
+ uint pgnum; /* total number of pages */
+ uint pgsec; /* number of pages per sector */
+ uint secshift; /* bits number in sector size */
+ uint pgshift; /* bits number in page size */
+ uint pgaddrbytes; /* bytes per page address */
+ uint secaddrbytes; /* bytes per sector address */
+ uint idbytes; /* the number ID bytes that this chip outputs */
+ } geom;
+
+ /* NAND flash internal registers */
+ struct {
+ unsigned command; /* the command register */
+ u_char status; /* the status register */
+ uint row; /* the page number */
+ uint column; /* the offset within page */
+ uint count; /* internal counter */
+ uint num; /* number of bytes which must be processed */
+ uint off; /* fixed page offset */
+ } regs;
+
+ /* NAND flash lines state */
+ struct {
+ int ce; /* chip Enable */
+ int cle; /* command Latch Enable */
+ int ale; /* address Latch Enable */
+ int wp; /* write Protect */
+ } lines;
+
+ /* Fields needed when using a cache file */
+ struct file *cfile; /* Open file */
+ unsigned long *pages_written; /* Which pages have been written */
+ void *file_buf;
+ struct page *held_pages[NS_MAX_HELD_PAGES];
+ int held_cnt;
+};
+
+/*
+ * Operations array. To perform any operation the simulator must pass
+ * through the correspondent states chain.
+ */
+static struct nandsim_operations {
+ uint32_t reqopts; /* options which are required to perform the operation */
+ uint32_t states[NS_OPER_STATES]; /* operation's states */
+} ops[NS_OPER_NUM] = {
+ /* Read page + OOB from the beginning */
+ {OPT_SMALLPAGE, {STATE_CMD_READ0 | ACTION_ZEROOFF, STATE_ADDR_PAGE | ACTION_CPY,
+ STATE_DATAOUT, STATE_READY}},
+ /* Read page + OOB from the second half */
+ {OPT_PAGE512_8BIT, {STATE_CMD_READ1 | ACTION_HALFOFF, STATE_ADDR_PAGE | ACTION_CPY,
+ STATE_DATAOUT, STATE_READY}},
+ /* Read OOB */
+ {OPT_SMALLPAGE, {STATE_CMD_READOOB | ACTION_OOBOFF, STATE_ADDR_PAGE | ACTION_CPY,
+ STATE_DATAOUT, STATE_READY}},
+ /* Program page starting from the beginning */
+ {OPT_ANY, {STATE_CMD_SEQIN, STATE_ADDR_PAGE, STATE_DATAIN,
+ STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}},
+ /* Program page starting from the beginning */
+ {OPT_SMALLPAGE, {STATE_CMD_READ0, STATE_CMD_SEQIN | ACTION_ZEROOFF, STATE_ADDR_PAGE,
+ STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}},
+ /* Program page starting from the second half */
+ {OPT_PAGE512, {STATE_CMD_READ1, STATE_CMD_SEQIN | ACTION_HALFOFF, STATE_ADDR_PAGE,
+ STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}},
+ /* Program OOB */
+ {OPT_SMALLPAGE, {STATE_CMD_READOOB, STATE_CMD_SEQIN | ACTION_OOBOFF, STATE_ADDR_PAGE,
+ STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}},
+ /* Erase sector */
+ {OPT_ANY, {STATE_CMD_ERASE1, STATE_ADDR_SEC, STATE_CMD_ERASE2 | ACTION_SECERASE, STATE_READY}},
+ /* Read status */
+ {OPT_ANY, {STATE_CMD_STATUS, STATE_DATAOUT_STATUS, STATE_READY}},
+ /* Read ID */
+ {OPT_ANY, {STATE_CMD_READID, STATE_ADDR_ZERO, STATE_DATAOUT_ID, STATE_READY}},
+ /* Large page devices read page */
+ {OPT_LARGEPAGE, {STATE_CMD_READ0, STATE_ADDR_PAGE, STATE_CMD_READSTART | ACTION_CPY,
+ STATE_DATAOUT, STATE_READY}},
+ /* Large page devices random page read */
+ {OPT_LARGEPAGE, {STATE_CMD_RNDOUT, STATE_ADDR_COLUMN, STATE_CMD_RNDOUTSTART | ACTION_CPY,
+ STATE_DATAOUT, STATE_READY}},
+};
+
+struct weak_block {
+ struct list_head list;
+ unsigned int erase_block_no;
+ unsigned int max_erases;
+ unsigned int erases_done;
+};
+
+static LIST_HEAD(weak_blocks);
+
+struct weak_page {
+ struct list_head list;
+ unsigned int page_no;
+ unsigned int max_writes;
+ unsigned int writes_done;
+};
+
+static LIST_HEAD(weak_pages);
+
+struct grave_page {
+ struct list_head list;
+ unsigned int page_no;
+ unsigned int max_reads;
+ unsigned int reads_done;
+};
+
+static LIST_HEAD(grave_pages);
+
+static unsigned long *erase_block_wear = NULL;
+static unsigned int wear_eb_count = 0;
+static unsigned long total_wear = 0;
+
+/* MTD structure for NAND controller */
+static struct mtd_info *nsmtd;
+
+static int nandsim_debugfs_show(struct seq_file *m, void *private)
+{
+ unsigned long wmin = -1, wmax = 0, avg;
+ unsigned long deciles[10], decile_max[10], tot = 0;
+ unsigned int i;
+
+ /* Calc wear stats */
+ for (i = 0; i < wear_eb_count; ++i) {
+ unsigned long wear = erase_block_wear[i];
+ if (wear < wmin)
+ wmin = wear;
+ if (wear > wmax)
+ wmax = wear;
+ tot += wear;
+ }
+
+ for (i = 0; i < 9; ++i) {
+ deciles[i] = 0;
+ decile_max[i] = (wmax * (i + 1) + 5) / 10;
+ }
+ deciles[9] = 0;
+ decile_max[9] = wmax;
+ for (i = 0; i < wear_eb_count; ++i) {
+ int d;
+ unsigned long wear = erase_block_wear[i];
+ for (d = 0; d < 10; ++d)
+ if (wear <= decile_max[d]) {
+ deciles[d] += 1;
+ break;
+ }
+ }
+ avg = tot / wear_eb_count;
+
+ /* Output wear report */
+ seq_printf(m, "Total numbers of erases: %lu\n", tot);
+ seq_printf(m, "Number of erase blocks: %u\n", wear_eb_count);
+ seq_printf(m, "Average number of erases: %lu\n", avg);
+ seq_printf(m, "Maximum number of erases: %lu\n", wmax);
+ seq_printf(m, "Minimum number of erases: %lu\n", wmin);
+ for (i = 0; i < 10; ++i) {
+ unsigned long from = (i ? decile_max[i - 1] + 1 : 0);
+ if (from > decile_max[i])
+ continue;
+ seq_printf(m, "Number of ebs with erase counts from %lu to %lu : %lu\n",
+ from,
+ decile_max[i],
+ deciles[i]);
+ }
+
+ return 0;
+}
+
+static int nandsim_debugfs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, nandsim_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations dfs_fops = {
+ .open = nandsim_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/**
+ * nandsim_debugfs_create - initialize debugfs
+ * @dev: nandsim device description object
+ *
+ * This function creates all debugfs files for UBI device @ubi. Returns zero in
+ * case of success and a negative error code in case of failure.
+ */
+static int nandsim_debugfs_create(struct nandsim *dev)
+{
+ struct dentry *root = nsmtd->dbg.dfs_dir;
+ struct dentry *dent;
+
+ /*
+ * Just skip debugfs initialization when the debugfs directory is
+ * missing.
+ */
+ if (IS_ERR_OR_NULL(root)) {
+ if (IS_ENABLED(CONFIG_DEBUG_FS) &&
+ !IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER))
+ NS_WARN("CONFIG_MTD_PARTITIONED_MASTER must be enabled to expose debugfs stuff\n");
+ return 0;
+ }
+
+ dent = debugfs_create_file("nandsim_wear_report", S_IRUSR,
+ root, dev, &dfs_fops);
+ if (IS_ERR_OR_NULL(dent)) {
+ NS_ERR("cannot create \"nandsim_wear_report\" debugfs entry\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Allocate array of page pointers, create slab allocation for an array
+ * and initialize the array by NULL pointers.
+ *
+ * RETURNS: 0 if success, -ENOMEM if memory alloc fails.
+ */
+static int __init alloc_device(struct nandsim *ns)
+{
+ struct file *cfile;
+ int i, err;
+
+ if (cache_file) {
+ cfile = filp_open(cache_file, O_CREAT | O_RDWR | O_LARGEFILE, 0600);
+ if (IS_ERR(cfile))
+ return PTR_ERR(cfile);
+ if (!(cfile->f_mode & FMODE_CAN_READ)) {
+ NS_ERR("alloc_device: cache file not readable\n");
+ err = -EINVAL;
+ goto err_close;
+ }
+ if (!(cfile->f_mode & FMODE_CAN_WRITE)) {
+ NS_ERR("alloc_device: cache file not writeable\n");
+ err = -EINVAL;
+ goto err_close;
+ }
+ ns->pages_written = vzalloc(BITS_TO_LONGS(ns->geom.pgnum) *
+ sizeof(unsigned long));
+ if (!ns->pages_written) {
+ NS_ERR("alloc_device: unable to allocate pages written array\n");
+ err = -ENOMEM;
+ goto err_close;
+ }
+ ns->file_buf = kmalloc(ns->geom.pgszoob, GFP_KERNEL);
+ if (!ns->file_buf) {
+ NS_ERR("alloc_device: unable to allocate file buf\n");
+ err = -ENOMEM;
+ goto err_free;
+ }
+ ns->cfile = cfile;
+ return 0;
+ }
+
+ ns->pages = vmalloc(ns->geom.pgnum * sizeof(union ns_mem));
+ if (!ns->pages) {
+ NS_ERR("alloc_device: unable to allocate page array\n");
+ return -ENOMEM;
+ }
+ for (i = 0; i < ns->geom.pgnum; i++) {
+ ns->pages[i].byte = NULL;
+ }
+ ns->nand_pages_slab = kmem_cache_create("nandsim",
+ ns->geom.pgszoob, 0, 0, NULL);
+ if (!ns->nand_pages_slab) {
+ NS_ERR("cache_create: unable to create kmem_cache\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+
+err_free:
+ vfree(ns->pages_written);
+err_close:
+ filp_close(cfile, NULL);
+ return err;
+}
+
+/*
+ * Free any allocated pages, and free the array of page pointers.
+ */
+static void free_device(struct nandsim *ns)
+{
+ int i;
+
+ if (ns->cfile) {
+ kfree(ns->file_buf);
+ vfree(ns->pages_written);
+ filp_close(ns->cfile, NULL);
+ return;
+ }
+
+ if (ns->pages) {
+ for (i = 0; i < ns->geom.pgnum; i++) {
+ if (ns->pages[i].byte)
+ kmem_cache_free(ns->nand_pages_slab,
+ ns->pages[i].byte);
+ }
+ kmem_cache_destroy(ns->nand_pages_slab);
+ vfree(ns->pages);
+ }
+}
+
+static char __init *get_partition_name(int i)
+{
+ return kasprintf(GFP_KERNEL, "NAND simulator partition %d", i);
+}
+
+/*
+ * Initialize the nandsim structure.
+ *
+ * RETURNS: 0 if success, -ERRNO if failure.
+ */
+static int __init init_nandsim(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nandsim *ns = nand_get_controller_data(chip);
+ int i, ret = 0;
+ uint64_t remains;
+ uint64_t next_offset;
+
+ if (NS_IS_INITIALIZED(ns)) {
+ NS_ERR("init_nandsim: nandsim is already initialized\n");
+ return -EIO;
+ }
+
+ /* Force mtd to not do delays */
+ chip->chip_delay = 0;
+
+ /* Initialize the NAND flash parameters */
+ ns->busw = chip->options & NAND_BUSWIDTH_16 ? 16 : 8;
+ ns->geom.totsz = mtd->size;
+ ns->geom.pgsz = mtd->writesize;
+ ns->geom.oobsz = mtd->oobsize;
+ ns->geom.secsz = mtd->erasesize;
+ ns->geom.pgszoob = ns->geom.pgsz + ns->geom.oobsz;
+ ns->geom.pgnum = div_u64(ns->geom.totsz, ns->geom.pgsz);
+ ns->geom.totszoob = ns->geom.totsz + (uint64_t)ns->geom.pgnum * ns->geom.oobsz;
+ ns->geom.secshift = ffs(ns->geom.secsz) - 1;
+ ns->geom.pgshift = chip->page_shift;
+ ns->geom.pgsec = ns->geom.secsz / ns->geom.pgsz;
+ ns->geom.secszoob = ns->geom.secsz + ns->geom.oobsz * ns->geom.pgsec;
+ ns->options = 0;
+
+ if (ns->geom.pgsz == 512) {
+ ns->options |= OPT_PAGE512;
+ if (ns->busw == 8)
+ ns->options |= OPT_PAGE512_8BIT;
+ } else if (ns->geom.pgsz == 2048) {
+ ns->options |= OPT_PAGE2048;
+ } else if (ns->geom.pgsz == 4096) {
+ ns->options |= OPT_PAGE4096;
+ } else {
+ NS_ERR("init_nandsim: unknown page size %u\n", ns->geom.pgsz);
+ return -EIO;
+ }
+
+ if (ns->options & OPT_SMALLPAGE) {
+ if (ns->geom.totsz <= (32 << 20)) {
+ ns->geom.pgaddrbytes = 3;
+ ns->geom.secaddrbytes = 2;
+ } else {
+ ns->geom.pgaddrbytes = 4;
+ ns->geom.secaddrbytes = 3;
+ }
+ } else {
+ if (ns->geom.totsz <= (128 << 20)) {
+ ns->geom.pgaddrbytes = 4;
+ ns->geom.secaddrbytes = 2;
+ } else {
+ ns->geom.pgaddrbytes = 5;
+ ns->geom.secaddrbytes = 3;
+ }
+ }
+
+ /* Fill the partition_info structure */
+ if (parts_num > ARRAY_SIZE(ns->partitions)) {
+ NS_ERR("too many partitions.\n");
+ return -EINVAL;
+ }
+ remains = ns->geom.totsz;
+ next_offset = 0;
+ for (i = 0; i < parts_num; ++i) {
+ uint64_t part_sz = (uint64_t)parts[i] * ns->geom.secsz;
+
+ if (!part_sz || part_sz > remains) {
+ NS_ERR("bad partition size.\n");
+ return -EINVAL;
+ }
+ ns->partitions[i].name = get_partition_name(i);
+ if (!ns->partitions[i].name) {
+ NS_ERR("unable to allocate memory.\n");
+ return -ENOMEM;
+ }
+ ns->partitions[i].offset = next_offset;
+ ns->partitions[i].size = part_sz;
+ next_offset += ns->partitions[i].size;
+ remains -= ns->partitions[i].size;
+ }
+ ns->nbparts = parts_num;
+ if (remains) {
+ if (parts_num + 1 > ARRAY_SIZE(ns->partitions)) {
+ NS_ERR("too many partitions.\n");
+ return -EINVAL;
+ }
+ ns->partitions[i].name = get_partition_name(i);
+ if (!ns->partitions[i].name) {
+ NS_ERR("unable to allocate memory.\n");
+ return -ENOMEM;
+ }
+ ns->partitions[i].offset = next_offset;
+ ns->partitions[i].size = remains;
+ ns->nbparts += 1;
+ }
+
+ if (ns->busw == 16)
+ NS_WARN("16-bit flashes support wasn't tested\n");
+
+ printk("flash size: %llu MiB\n",
+ (unsigned long long)ns->geom.totsz >> 20);
+ printk("page size: %u bytes\n", ns->geom.pgsz);
+ printk("OOB area size: %u bytes\n", ns->geom.oobsz);
+ printk("sector size: %u KiB\n", ns->geom.secsz >> 10);
+ printk("pages number: %u\n", ns->geom.pgnum);
+ printk("pages per sector: %u\n", ns->geom.pgsec);
+ printk("bus width: %u\n", ns->busw);
+ printk("bits in sector size: %u\n", ns->geom.secshift);
+ printk("bits in page size: %u\n", ns->geom.pgshift);
+ printk("bits in OOB size: %u\n", ffs(ns->geom.oobsz) - 1);
+ printk("flash size with OOB: %llu KiB\n",
+ (unsigned long long)ns->geom.totszoob >> 10);
+ printk("page address bytes: %u\n", ns->geom.pgaddrbytes);
+ printk("sector address bytes: %u\n", ns->geom.secaddrbytes);
+ printk("options: %#x\n", ns->options);
+
+ if ((ret = alloc_device(ns)) != 0)
+ return ret;
+
+ /* Allocate / initialize the internal buffer */
+ ns->buf.byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL);
+ if (!ns->buf.byte) {
+ NS_ERR("init_nandsim: unable to allocate %u bytes for the internal buffer\n",
+ ns->geom.pgszoob);
+ return -ENOMEM;
+ }
+ memset(ns->buf.byte, 0xFF, ns->geom.pgszoob);
+
+ return 0;
+}
+
+/*
+ * Free the nandsim structure.
+ */
+static void free_nandsim(struct nandsim *ns)
+{
+ kfree(ns->buf.byte);
+ free_device(ns);
+
+ return;
+}
+
+static int parse_badblocks(struct nandsim *ns, struct mtd_info *mtd)
+{
+ char *w;
+ int zero_ok;
+ unsigned int erase_block_no;
+ loff_t offset;
+
+ if (!badblocks)
+ return 0;
+ w = badblocks;
+ do {
+ zero_ok = (*w == '0' ? 1 : 0);
+ erase_block_no = simple_strtoul(w, &w, 0);
+ if (!zero_ok && !erase_block_no) {
+ NS_ERR("invalid badblocks.\n");
+ return -EINVAL;
+ }
+ offset = (loff_t)erase_block_no * ns->geom.secsz;
+ if (mtd_block_markbad(mtd, offset)) {
+ NS_ERR("invalid badblocks.\n");
+ return -EINVAL;
+ }
+ if (*w == ',')
+ w += 1;
+ } while (*w);
+ return 0;
+}
+
+static int parse_weakblocks(void)
+{
+ char *w;
+ int zero_ok;
+ unsigned int erase_block_no;
+ unsigned int max_erases;
+ struct weak_block *wb;
+
+ if (!weakblocks)
+ return 0;
+ w = weakblocks;
+ do {
+ zero_ok = (*w == '0' ? 1 : 0);
+ erase_block_no = simple_strtoul(w, &w, 0);
+ if (!zero_ok && !erase_block_no) {
+ NS_ERR("invalid weakblocks.\n");
+ return -EINVAL;
+ }
+ max_erases = 3;
+ if (*w == ':') {
+ w += 1;
+ max_erases = simple_strtoul(w, &w, 0);
+ }
+ if (*w == ',')
+ w += 1;
+ wb = kzalloc(sizeof(*wb), GFP_KERNEL);
+ if (!wb) {
+ NS_ERR("unable to allocate memory.\n");
+ return -ENOMEM;
+ }
+ wb->erase_block_no = erase_block_no;
+ wb->max_erases = max_erases;
+ list_add(&wb->list, &weak_blocks);
+ } while (*w);
+ return 0;
+}
+
+static int erase_error(unsigned int erase_block_no)
+{
+ struct weak_block *wb;
+
+ list_for_each_entry(wb, &weak_blocks, list)
+ if (wb->erase_block_no == erase_block_no) {
+ if (wb->erases_done >= wb->max_erases)
+ return 1;
+ wb->erases_done += 1;
+ return 0;
+ }
+ return 0;
+}
+
+static int parse_weakpages(void)
+{
+ char *w;
+ int zero_ok;
+ unsigned int page_no;
+ unsigned int max_writes;
+ struct weak_page *wp;
+
+ if (!weakpages)
+ return 0;
+ w = weakpages;
+ do {
+ zero_ok = (*w == '0' ? 1 : 0);
+ page_no = simple_strtoul(w, &w, 0);
+ if (!zero_ok && !page_no) {
+ NS_ERR("invalid weakpages.\n");
+ return -EINVAL;
+ }
+ max_writes = 3;
+ if (*w == ':') {
+ w += 1;
+ max_writes = simple_strtoul(w, &w, 0);
+ }
+ if (*w == ',')
+ w += 1;
+ wp = kzalloc(sizeof(*wp), GFP_KERNEL);
+ if (!wp) {
+ NS_ERR("unable to allocate memory.\n");
+ return -ENOMEM;
+ }
+ wp->page_no = page_no;
+ wp->max_writes = max_writes;
+ list_add(&wp->list, &weak_pages);
+ } while (*w);
+ return 0;
+}
+
+static int write_error(unsigned int page_no)
+{
+ struct weak_page *wp;
+
+ list_for_each_entry(wp, &weak_pages, list)
+ if (wp->page_no == page_no) {
+ if (wp->writes_done >= wp->max_writes)
+ return 1;
+ wp->writes_done += 1;
+ return 0;
+ }
+ return 0;
+}
+
+static int parse_gravepages(void)
+{
+ char *g;
+ int zero_ok;
+ unsigned int page_no;
+ unsigned int max_reads;
+ struct grave_page *gp;
+
+ if (!gravepages)
+ return 0;
+ g = gravepages;
+ do {
+ zero_ok = (*g == '0' ? 1 : 0);
+ page_no = simple_strtoul(g, &g, 0);
+ if (!zero_ok && !page_no) {
+ NS_ERR("invalid gravepagess.\n");
+ return -EINVAL;
+ }
+ max_reads = 3;
+ if (*g == ':') {
+ g += 1;
+ max_reads = simple_strtoul(g, &g, 0);
+ }
+ if (*g == ',')
+ g += 1;
+ gp = kzalloc(sizeof(*gp), GFP_KERNEL);
+ if (!gp) {
+ NS_ERR("unable to allocate memory.\n");
+ return -ENOMEM;
+ }
+ gp->page_no = page_no;
+ gp->max_reads = max_reads;
+ list_add(&gp->list, &grave_pages);
+ } while (*g);
+ return 0;
+}
+
+static int read_error(unsigned int page_no)
+{
+ struct grave_page *gp;
+
+ list_for_each_entry(gp, &grave_pages, list)
+ if (gp->page_no == page_no) {
+ if (gp->reads_done >= gp->max_reads)
+ return 1;
+ gp->reads_done += 1;
+ return 0;
+ }
+ return 0;
+}
+
+static void free_lists(void)
+{
+ struct list_head *pos, *n;
+ list_for_each_safe(pos, n, &weak_blocks) {
+ list_del(pos);
+ kfree(list_entry(pos, struct weak_block, list));
+ }
+ list_for_each_safe(pos, n, &weak_pages) {
+ list_del(pos);
+ kfree(list_entry(pos, struct weak_page, list));
+ }
+ list_for_each_safe(pos, n, &grave_pages) {
+ list_del(pos);
+ kfree(list_entry(pos, struct grave_page, list));
+ }
+ kfree(erase_block_wear);
+}
+
+static int setup_wear_reporting(struct mtd_info *mtd)
+{
+ size_t mem;
+
+ wear_eb_count = div_u64(mtd->size, mtd->erasesize);
+ mem = wear_eb_count * sizeof(unsigned long);
+ if (mem / sizeof(unsigned long) != wear_eb_count) {
+ NS_ERR("Too many erase blocks for wear reporting\n");
+ return -ENOMEM;
+ }
+ erase_block_wear = kzalloc(mem, GFP_KERNEL);
+ if (!erase_block_wear) {
+ NS_ERR("Too many erase blocks for wear reporting\n");
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void update_wear(unsigned int erase_block_no)
+{
+ if (!erase_block_wear)
+ return;
+ total_wear += 1;
+ /*
+ * TODO: Notify this through a debugfs entry,
+ * instead of showing an error message.
+ */
+ if (total_wear == 0)
+ NS_ERR("Erase counter total overflow\n");
+ erase_block_wear[erase_block_no] += 1;
+ if (erase_block_wear[erase_block_no] == 0)
+ NS_ERR("Erase counter overflow for erase block %u\n", erase_block_no);
+}
+
+/*
+ * Returns the string representation of 'state' state.
+ */
+static char *get_state_name(uint32_t state)
+{
+ switch (NS_STATE(state)) {
+ case STATE_CMD_READ0:
+ return "STATE_CMD_READ0";
+ case STATE_CMD_READ1:
+ return "STATE_CMD_READ1";
+ case STATE_CMD_PAGEPROG:
+ return "STATE_CMD_PAGEPROG";
+ case STATE_CMD_READOOB:
+ return "STATE_CMD_READOOB";
+ case STATE_CMD_READSTART:
+ return "STATE_CMD_READSTART";
+ case STATE_CMD_ERASE1:
+ return "STATE_CMD_ERASE1";
+ case STATE_CMD_STATUS:
+ return "STATE_CMD_STATUS";
+ case STATE_CMD_SEQIN:
+ return "STATE_CMD_SEQIN";
+ case STATE_CMD_READID:
+ return "STATE_CMD_READID";
+ case STATE_CMD_ERASE2:
+ return "STATE_CMD_ERASE2";
+ case STATE_CMD_RESET:
+ return "STATE_CMD_RESET";
+ case STATE_CMD_RNDOUT:
+ return "STATE_CMD_RNDOUT";
+ case STATE_CMD_RNDOUTSTART:
+ return "STATE_CMD_RNDOUTSTART";
+ case STATE_ADDR_PAGE:
+ return "STATE_ADDR_PAGE";
+ case STATE_ADDR_SEC:
+ return "STATE_ADDR_SEC";
+ case STATE_ADDR_ZERO:
+ return "STATE_ADDR_ZERO";
+ case STATE_ADDR_COLUMN:
+ return "STATE_ADDR_COLUMN";
+ case STATE_DATAIN:
+ return "STATE_DATAIN";
+ case STATE_DATAOUT:
+ return "STATE_DATAOUT";
+ case STATE_DATAOUT_ID:
+ return "STATE_DATAOUT_ID";
+ case STATE_DATAOUT_STATUS:
+ return "STATE_DATAOUT_STATUS";
+ case STATE_READY:
+ return "STATE_READY";
+ case STATE_UNKNOWN:
+ return "STATE_UNKNOWN";
+ }
+
+ NS_ERR("get_state_name: unknown state, BUG\n");
+ return NULL;
+}
+
+/*
+ * Check if command is valid.
+ *
+ * RETURNS: 1 if wrong command, 0 if right.
+ */
+static int check_command(int cmd)
+{
+ switch (cmd) {
+
+ case NAND_CMD_READ0:
+ case NAND_CMD_READ1:
+ case NAND_CMD_READSTART:
+ case NAND_CMD_PAGEPROG:
+ case NAND_CMD_READOOB:
+ case NAND_CMD_ERASE1:
+ case NAND_CMD_STATUS:
+ case NAND_CMD_SEQIN:
+ case NAND_CMD_READID:
+ case NAND_CMD_ERASE2:
+ case NAND_CMD_RESET:
+ case NAND_CMD_RNDOUT:
+ case NAND_CMD_RNDOUTSTART:
+ return 0;
+
+ default:
+ return 1;
+ }
+}
+
+/*
+ * Returns state after command is accepted by command number.
+ */
+static uint32_t get_state_by_command(unsigned command)
+{
+ switch (command) {
+ case NAND_CMD_READ0:
+ return STATE_CMD_READ0;
+ case NAND_CMD_READ1:
+ return STATE_CMD_READ1;
+ case NAND_CMD_PAGEPROG:
+ return STATE_CMD_PAGEPROG;
+ case NAND_CMD_READSTART:
+ return STATE_CMD_READSTART;
+ case NAND_CMD_READOOB:
+ return STATE_CMD_READOOB;
+ case NAND_CMD_ERASE1:
+ return STATE_CMD_ERASE1;
+ case NAND_CMD_STATUS:
+ return STATE_CMD_STATUS;
+ case NAND_CMD_SEQIN:
+ return STATE_CMD_SEQIN;
+ case NAND_CMD_READID:
+ return STATE_CMD_READID;
+ case NAND_CMD_ERASE2:
+ return STATE_CMD_ERASE2;
+ case NAND_CMD_RESET:
+ return STATE_CMD_RESET;
+ case NAND_CMD_RNDOUT:
+ return STATE_CMD_RNDOUT;
+ case NAND_CMD_RNDOUTSTART:
+ return STATE_CMD_RNDOUTSTART;
+ }
+
+ NS_ERR("get_state_by_command: unknown command, BUG\n");
+ return 0;
+}
+
+/*
+ * Move an address byte to the correspondent internal register.
+ */
+static inline void accept_addr_byte(struct nandsim *ns, u_char bt)
+{
+ uint byte = (uint)bt;
+
+ if (ns->regs.count < (ns->geom.pgaddrbytes - ns->geom.secaddrbytes))
+ ns->regs.column |= (byte << 8 * ns->regs.count);
+ else {
+ ns->regs.row |= (byte << 8 * (ns->regs.count -
+ ns->geom.pgaddrbytes +
+ ns->geom.secaddrbytes));
+ }
+
+ return;
+}
+
+/*
+ * Switch to STATE_READY state.
+ */
+static inline void switch_to_ready_state(struct nandsim *ns, u_char status)
+{
+ NS_DBG("switch_to_ready_state: switch to %s state\n", get_state_name(STATE_READY));
+
+ ns->state = STATE_READY;
+ ns->nxstate = STATE_UNKNOWN;
+ ns->op = NULL;
+ ns->npstates = 0;
+ ns->stateidx = 0;
+ ns->regs.num = 0;
+ ns->regs.count = 0;
+ ns->regs.off = 0;
+ ns->regs.row = 0;
+ ns->regs.column = 0;
+ ns->regs.status = status;
+}
+
+/*
+ * If the operation isn't known yet, try to find it in the global array
+ * of supported operations.
+ *
+ * Operation can be unknown because of the following.
+ * 1. New command was accepted and this is the first call to find the
+ * correspondent states chain. In this case ns->npstates = 0;
+ * 2. There are several operations which begin with the same command(s)
+ * (for example program from the second half and read from the
+ * second half operations both begin with the READ1 command). In this
+ * case the ns->pstates[] array contains previous states.
+ *
+ * Thus, the function tries to find operation containing the following
+ * states (if the 'flag' parameter is 0):
+ * ns->pstates[0], ... ns->pstates[ns->npstates], ns->state
+ *
+ * If (one and only one) matching operation is found, it is accepted (
+ * ns->ops, ns->state, ns->nxstate are initialized, ns->npstate is
+ * zeroed).
+ *
+ * If there are several matches, the current state is pushed to the
+ * ns->pstates.
+ *
+ * The operation can be unknown only while commands are input to the chip.
+ * As soon as address command is accepted, the operation must be known.
+ * In such situation the function is called with 'flag' != 0, and the
+ * operation is searched using the following pattern:
+ * ns->pstates[0], ... ns->pstates[ns->npstates], <address input>
+ *
+ * It is supposed that this pattern must either match one operation or
+ * none. There can't be ambiguity in that case.
+ *
+ * If no matches found, the function does the following:
+ * 1. if there are saved states present, try to ignore them and search
+ * again only using the last command. If nothing was found, switch
+ * to the STATE_READY state.
+ * 2. if there are no saved states, switch to the STATE_READY state.
+ *
+ * RETURNS: -2 - no matched operations found.
+ * -1 - several matches.
+ * 0 - operation is found.
+ */
+static int find_operation(struct nandsim *ns, uint32_t flag)
+{
+ int opsfound = 0;
+ int i, j, idx = 0;
+
+ for (i = 0; i < NS_OPER_NUM; i++) {
+
+ int found = 1;
+
+ if (!(ns->options & ops[i].reqopts))
+ /* Ignore operations we can't perform */
+ continue;
+
+ if (flag) {
+ if (!(ops[i].states[ns->npstates] & STATE_ADDR_MASK))
+ continue;
+ } else {
+ if (NS_STATE(ns->state) != NS_STATE(ops[i].states[ns->npstates]))
+ continue;
+ }
+
+ for (j = 0; j < ns->npstates; j++)
+ if (NS_STATE(ops[i].states[j]) != NS_STATE(ns->pstates[j])
+ && (ns->options & ops[idx].reqopts)) {
+ found = 0;
+ break;
+ }
+
+ if (found) {
+ idx = i;
+ opsfound += 1;
+ }
+ }
+
+ if (opsfound == 1) {
+ /* Exact match */
+ ns->op = &ops[idx].states[0];
+ if (flag) {
+ /*
+ * In this case the find_operation function was
+ * called when address has just began input. But it isn't
+ * yet fully input and the current state must
+ * not be one of STATE_ADDR_*, but the STATE_ADDR_*
+ * state must be the next state (ns->nxstate).
+ */
+ ns->stateidx = ns->npstates - 1;
+ } else {
+ ns->stateidx = ns->npstates;
+ }
+ ns->npstates = 0;
+ ns->state = ns->op[ns->stateidx];
+ ns->nxstate = ns->op[ns->stateidx + 1];
+ NS_DBG("find_operation: operation found, index: %d, state: %s, nxstate %s\n",
+ idx, get_state_name(ns->state), get_state_name(ns->nxstate));
+ return 0;
+ }
+
+ if (opsfound == 0) {
+ /* Nothing was found. Try to ignore previous commands (if any) and search again */
+ if (ns->npstates != 0) {
+ NS_DBG("find_operation: no operation found, try again with state %s\n",
+ get_state_name(ns->state));
+ ns->npstates = 0;
+ return find_operation(ns, 0);
+
+ }
+ NS_DBG("find_operation: no operations found\n");
+ switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+ return -2;
+ }
+
+ if (flag) {
+ /* This shouldn't happen */
+ NS_DBG("find_operation: BUG, operation must be known if address is input\n");
+ return -2;
+ }
+
+ NS_DBG("find_operation: there is still ambiguity\n");
+
+ ns->pstates[ns->npstates++] = ns->state;
+
+ return -1;
+}
+
+static void put_pages(struct nandsim *ns)
+{
+ int i;
+
+ for (i = 0; i < ns->held_cnt; i++)
+ put_page(ns->held_pages[i]);
+}
+
+/* Get page cache pages in advance to provide NOFS memory allocation */
+static int get_pages(struct nandsim *ns, struct file *file, size_t count, loff_t pos)
+{
+ pgoff_t index, start_index, end_index;
+ struct page *page;
+ struct address_space *mapping = file->f_mapping;
+
+ start_index = pos >> PAGE_SHIFT;
+ end_index = (pos + count - 1) >> PAGE_SHIFT;
+ if (end_index - start_index + 1 > NS_MAX_HELD_PAGES)
+ return -EINVAL;
+ ns->held_cnt = 0;
+ for (index = start_index; index <= end_index; index++) {
+ page = find_get_page(mapping, index);
+ if (page == NULL) {
+ page = find_or_create_page(mapping, index, GFP_NOFS);
+ if (page == NULL) {
+ write_inode_now(mapping->host, 1);
+ page = find_or_create_page(mapping, index, GFP_NOFS);
+ }
+ if (page == NULL) {
+ put_pages(ns);
+ return -ENOMEM;
+ }
+ unlock_page(page);
+ }
+ ns->held_pages[ns->held_cnt++] = page;
+ }
+ return 0;
+}
+
+static ssize_t read_file(struct nandsim *ns, struct file *file, void *buf, size_t count, loff_t pos)
+{
+ ssize_t tx;
+ int err;
+ unsigned int noreclaim_flag;
+
+ err = get_pages(ns, file, count, pos);
+ if (err)
+ return err;
+ noreclaim_flag = memalloc_noreclaim_save();
+ tx = kernel_read(file, buf, count, &pos);
+ memalloc_noreclaim_restore(noreclaim_flag);
+ put_pages(ns);
+ return tx;
+}
+
+static ssize_t write_file(struct nandsim *ns, struct file *file, void *buf, size_t count, loff_t pos)
+{
+ ssize_t tx;
+ int err;
+ unsigned int noreclaim_flag;
+
+ err = get_pages(ns, file, count, pos);
+ if (err)
+ return err;
+ noreclaim_flag = memalloc_noreclaim_save();
+ tx = kernel_write(file, buf, count, &pos);
+ memalloc_noreclaim_restore(noreclaim_flag);
+ put_pages(ns);
+ return tx;
+}
+
+/*
+ * Returns a pointer to the current page.
+ */
+static inline union ns_mem *NS_GET_PAGE(struct nandsim *ns)
+{
+ return &(ns->pages[ns->regs.row]);
+}
+
+/*
+ * Retuns a pointer to the current byte, within the current page.
+ */
+static inline u_char *NS_PAGE_BYTE_OFF(struct nandsim *ns)
+{
+ return NS_GET_PAGE(ns)->byte + ns->regs.column + ns->regs.off;
+}
+
+static int do_read_error(struct nandsim *ns, int num)
+{
+ unsigned int page_no = ns->regs.row;
+
+ if (read_error(page_no)) {
+ prandom_bytes(ns->buf.byte, num);
+ NS_WARN("simulating read error in page %u\n", page_no);
+ return 1;
+ }
+ return 0;
+}
+
+static void do_bit_flips(struct nandsim *ns, int num)
+{
+ if (bitflips && prandom_u32() < (1 << 22)) {
+ int flips = 1;
+ if (bitflips > 1)
+ flips = (prandom_u32() % (int) bitflips) + 1;
+ while (flips--) {
+ int pos = prandom_u32() % (num * 8);
+ ns->buf.byte[pos / 8] ^= (1 << (pos % 8));
+ NS_WARN("read_page: flipping bit %d in page %d "
+ "reading from %d ecc: corrected=%u failed=%u\n",
+ pos, ns->regs.row, ns->regs.column + ns->regs.off,
+ nsmtd->ecc_stats.corrected, nsmtd->ecc_stats.failed);
+ }
+ }
+}
+
+/*
+ * Fill the NAND buffer with data read from the specified page.
+ */
+static void read_page(struct nandsim *ns, int num)
+{
+ union ns_mem *mypage;
+
+ if (ns->cfile) {
+ if (!test_bit(ns->regs.row, ns->pages_written)) {
+ NS_DBG("read_page: page %d not written\n", ns->regs.row);
+ memset(ns->buf.byte, 0xFF, num);
+ } else {
+ loff_t pos;
+ ssize_t tx;
+
+ NS_DBG("read_page: page %d written, reading from %d\n",
+ ns->regs.row, ns->regs.column + ns->regs.off);
+ if (do_read_error(ns, num))
+ return;
+ pos = (loff_t)NS_RAW_OFFSET(ns) + ns->regs.off;
+ tx = read_file(ns, ns->cfile, ns->buf.byte, num, pos);
+ if (tx != num) {
+ NS_ERR("read_page: read error for page %d ret %ld\n", ns->regs.row, (long)tx);
+ return;
+ }
+ do_bit_flips(ns, num);
+ }
+ return;
+ }
+
+ mypage = NS_GET_PAGE(ns);
+ if (mypage->byte == NULL) {
+ NS_DBG("read_page: page %d not allocated\n", ns->regs.row);
+ memset(ns->buf.byte, 0xFF, num);
+ } else {
+ NS_DBG("read_page: page %d allocated, reading from %d\n",
+ ns->regs.row, ns->regs.column + ns->regs.off);
+ if (do_read_error(ns, num))
+ return;
+ memcpy(ns->buf.byte, NS_PAGE_BYTE_OFF(ns), num);
+ do_bit_flips(ns, num);
+ }
+}
+
+/*
+ * Erase all pages in the specified sector.
+ */
+static void erase_sector(struct nandsim *ns)
+{
+ union ns_mem *mypage;
+ int i;
+
+ if (ns->cfile) {
+ for (i = 0; i < ns->geom.pgsec; i++)
+ if (__test_and_clear_bit(ns->regs.row + i,
+ ns->pages_written)) {
+ NS_DBG("erase_sector: freeing page %d\n", ns->regs.row + i);
+ }
+ return;
+ }
+
+ mypage = NS_GET_PAGE(ns);
+ for (i = 0; i < ns->geom.pgsec; i++) {
+ if (mypage->byte != NULL) {
+ NS_DBG("erase_sector: freeing page %d\n", ns->regs.row+i);
+ kmem_cache_free(ns->nand_pages_slab, mypage->byte);
+ mypage->byte = NULL;
+ }
+ mypage++;
+ }
+}
+
+/*
+ * Program the specified page with the contents from the NAND buffer.
+ */
+static int prog_page(struct nandsim *ns, int num)
+{
+ int i;
+ union ns_mem *mypage;
+ u_char *pg_off;
+
+ if (ns->cfile) {
+ loff_t off;
+ ssize_t tx;
+ int all;
+
+ NS_DBG("prog_page: writing page %d\n", ns->regs.row);
+ pg_off = ns->file_buf + ns->regs.column + ns->regs.off;
+ off = (loff_t)NS_RAW_OFFSET(ns) + ns->regs.off;
+ if (!test_bit(ns->regs.row, ns->pages_written)) {
+ all = 1;
+ memset(ns->file_buf, 0xff, ns->geom.pgszoob);
+ } else {
+ all = 0;
+ tx = read_file(ns, ns->cfile, pg_off, num, off);
+ if (tx != num) {
+ NS_ERR("prog_page: read error for page %d ret %ld\n", ns->regs.row, (long)tx);
+ return -1;
+ }
+ }
+ for (i = 0; i < num; i++)
+ pg_off[i] &= ns->buf.byte[i];
+ if (all) {
+ loff_t pos = (loff_t)ns->regs.row * ns->geom.pgszoob;
+ tx = write_file(ns, ns->cfile, ns->file_buf, ns->geom.pgszoob, pos);
+ if (tx != ns->geom.pgszoob) {
+ NS_ERR("prog_page: write error for page %d ret %ld\n", ns->regs.row, (long)tx);
+ return -1;
+ }
+ __set_bit(ns->regs.row, ns->pages_written);
+ } else {
+ tx = write_file(ns, ns->cfile, pg_off, num, off);
+ if (tx != num) {
+ NS_ERR("prog_page: write error for page %d ret %ld\n", ns->regs.row, (long)tx);
+ return -1;
+ }
+ }
+ return 0;
+ }
+
+ mypage = NS_GET_PAGE(ns);
+ if (mypage->byte == NULL) {
+ NS_DBG("prog_page: allocating page %d\n", ns->regs.row);
+ /*
+ * We allocate memory with GFP_NOFS because a flash FS may
+ * utilize this. If it is holding an FS lock, then gets here,
+ * then kernel memory alloc runs writeback which goes to the FS
+ * again and deadlocks. This was seen in practice.
+ */
+ mypage->byte = kmem_cache_alloc(ns->nand_pages_slab, GFP_NOFS);
+ if (mypage->byte == NULL) {
+ NS_ERR("prog_page: error allocating memory for page %d\n", ns->regs.row);
+ return -1;
+ }
+ memset(mypage->byte, 0xFF, ns->geom.pgszoob);
+ }
+
+ pg_off = NS_PAGE_BYTE_OFF(ns);
+ for (i = 0; i < num; i++)
+ pg_off[i] &= ns->buf.byte[i];
+
+ return 0;
+}
+
+/*
+ * If state has any action bit, perform this action.
+ *
+ * RETURNS: 0 if success, -1 if error.
+ */
+static int do_state_action(struct nandsim *ns, uint32_t action)
+{
+ int num;
+ int busdiv = ns->busw == 8 ? 1 : 2;
+ unsigned int erase_block_no, page_no;
+
+ action &= ACTION_MASK;
+
+ /* Check that page address input is correct */
+ if (action != ACTION_SECERASE && ns->regs.row >= ns->geom.pgnum) {
+ NS_WARN("do_state_action: wrong page number (%#x)\n", ns->regs.row);
+ return -1;
+ }
+
+ switch (action) {
+
+ case ACTION_CPY:
+ /*
+ * Copy page data to the internal buffer.
+ */
+
+ /* Column shouldn't be very large */
+ if (ns->regs.column >= (ns->geom.pgszoob - ns->regs.off)) {
+ NS_ERR("do_state_action: column number is too large\n");
+ break;
+ }
+ num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
+ read_page(ns, num);
+
+ NS_DBG("do_state_action: (ACTION_CPY:) copy %d bytes to int buf, raw offset %d\n",
+ num, NS_RAW_OFFSET(ns) + ns->regs.off);
+
+ if (ns->regs.off == 0)
+ NS_LOG("read page %d\n", ns->regs.row);
+ else if (ns->regs.off < ns->geom.pgsz)
+ NS_LOG("read page %d (second half)\n", ns->regs.row);
+ else
+ NS_LOG("read OOB of page %d\n", ns->regs.row);
+
+ NS_UDELAY(access_delay);
+ NS_UDELAY(input_cycle * ns->geom.pgsz / 1000 / busdiv);
+
+ break;
+
+ case ACTION_SECERASE:
+ /*
+ * Erase sector.
+ */
+
+ if (ns->lines.wp) {
+ NS_ERR("do_state_action: device is write-protected, ignore sector erase\n");
+ return -1;
+ }
+
+ if (ns->regs.row >= ns->geom.pgnum - ns->geom.pgsec
+ || (ns->regs.row & ~(ns->geom.secsz - 1))) {
+ NS_ERR("do_state_action: wrong sector address (%#x)\n", ns->regs.row);
+ return -1;
+ }
+
+ ns->regs.row = (ns->regs.row <<
+ 8 * (ns->geom.pgaddrbytes - ns->geom.secaddrbytes)) | ns->regs.column;
+ ns->regs.column = 0;
+
+ erase_block_no = ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift);
+
+ NS_DBG("do_state_action: erase sector at address %#x, off = %d\n",
+ ns->regs.row, NS_RAW_OFFSET(ns));
+ NS_LOG("erase sector %u\n", erase_block_no);
+
+ erase_sector(ns);
+
+ NS_MDELAY(erase_delay);
+
+ if (erase_block_wear)
+ update_wear(erase_block_no);
+
+ if (erase_error(erase_block_no)) {
+ NS_WARN("simulating erase failure in erase block %u\n", erase_block_no);
+ return -1;
+ }
+
+ break;
+
+ case ACTION_PRGPAGE:
+ /*
+ * Program page - move internal buffer data to the page.
+ */
+
+ if (ns->lines.wp) {
+ NS_WARN("do_state_action: device is write-protected, programm\n");
+ return -1;
+ }
+
+ num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
+ if (num != ns->regs.count) {
+ NS_ERR("do_state_action: too few bytes were input (%d instead of %d)\n",
+ ns->regs.count, num);
+ return -1;
+ }
+
+ if (prog_page(ns, num) == -1)
+ return -1;
+
+ page_no = ns->regs.row;
+
+ NS_DBG("do_state_action: copy %d bytes from int buf to (%#x, %#x), raw off = %d\n",
+ num, ns->regs.row, ns->regs.column, NS_RAW_OFFSET(ns) + ns->regs.off);
+ NS_LOG("programm page %d\n", ns->regs.row);
+
+ NS_UDELAY(programm_delay);
+ NS_UDELAY(output_cycle * ns->geom.pgsz / 1000 / busdiv);
+
+ if (write_error(page_no)) {
+ NS_WARN("simulating write failure in page %u\n", page_no);
+ return -1;
+ }
+
+ break;
+
+ case ACTION_ZEROOFF:
+ NS_DBG("do_state_action: set internal offset to 0\n");
+ ns->regs.off = 0;
+ break;
+
+ case ACTION_HALFOFF:
+ if (!(ns->options & OPT_PAGE512_8BIT)) {
+ NS_ERR("do_state_action: BUG! can't skip half of page for non-512"
+ "byte page size 8x chips\n");
+ return -1;
+ }
+ NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz/2);
+ ns->regs.off = ns->geom.pgsz/2;
+ break;
+
+ case ACTION_OOBOFF:
+ NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz);
+ ns->regs.off = ns->geom.pgsz;
+ break;
+
+ default:
+ NS_DBG("do_state_action: BUG! unknown action\n");
+ }
+
+ return 0;
+}
+
+/*
+ * Switch simulator's state.
+ */
+static void switch_state(struct nandsim *ns)
+{
+ if (ns->op) {
+ /*
+ * The current operation have already been identified.
+ * Just follow the states chain.
+ */
+
+ ns->stateidx += 1;
+ ns->state = ns->nxstate;
+ ns->nxstate = ns->op[ns->stateidx + 1];
+
+ NS_DBG("switch_state: operation is known, switch to the next state, "
+ "state: %s, nxstate: %s\n",
+ get_state_name(ns->state), get_state_name(ns->nxstate));
+
+ /* See, whether we need to do some action */
+ if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) {
+ switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+ return;
+ }
+
+ } else {
+ /*
+ * We don't yet know which operation we perform.
+ * Try to identify it.
+ */
+
+ /*
+ * The only event causing the switch_state function to
+ * be called with yet unknown operation is new command.
+ */
+ ns->state = get_state_by_command(ns->regs.command);
+
+ NS_DBG("switch_state: operation is unknown, try to find it\n");
+
+ if (find_operation(ns, 0) != 0)
+ return;
+
+ if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) {
+ switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+ return;
+ }
+ }
+
+ /* For 16x devices column means the page offset in words */
+ if ((ns->nxstate & STATE_ADDR_MASK) && ns->busw == 16) {
+ NS_DBG("switch_state: double the column number for 16x device\n");
+ ns->regs.column <<= 1;
+ }
+
+ if (NS_STATE(ns->nxstate) == STATE_READY) {
+ /*
+ * The current state is the last. Return to STATE_READY
+ */
+
+ u_char status = NS_STATUS_OK(ns);
+
+ /* In case of data states, see if all bytes were input/output */
+ if ((ns->state & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK))
+ && ns->regs.count != ns->regs.num) {
+ NS_WARN("switch_state: not all bytes were processed, %d left\n",
+ ns->regs.num - ns->regs.count);
+ status = NS_STATUS_FAILED(ns);
+ }
+
+ NS_DBG("switch_state: operation complete, switch to STATE_READY state\n");
+
+ switch_to_ready_state(ns, status);
+
+ return;
+ } else if (ns->nxstate & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK)) {
+ /*
+ * If the next state is data input/output, switch to it now
+ */
+
+ ns->state = ns->nxstate;
+ ns->nxstate = ns->op[++ns->stateidx + 1];
+ ns->regs.num = ns->regs.count = 0;
+
+ NS_DBG("switch_state: the next state is data I/O, switch, "
+ "state: %s, nxstate: %s\n",
+ get_state_name(ns->state), get_state_name(ns->nxstate));
+
+ /*
+ * Set the internal register to the count of bytes which
+ * are expected to be input or output
+ */
+ switch (NS_STATE(ns->state)) {
+ case STATE_DATAIN:
+ case STATE_DATAOUT:
+ ns->regs.num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
+ break;
+
+ case STATE_DATAOUT_ID:
+ ns->regs.num = ns->geom.idbytes;
+ break;
+
+ case STATE_DATAOUT_STATUS:
+ ns->regs.count = ns->regs.num = 0;
+ break;
+
+ default:
+ NS_ERR("switch_state: BUG! unknown data state\n");
+ }
+
+ } else if (ns->nxstate & STATE_ADDR_MASK) {
+ /*
+ * If the next state is address input, set the internal
+ * register to the number of expected address bytes
+ */
+
+ ns->regs.count = 0;
+
+ switch (NS_STATE(ns->nxstate)) {
+ case STATE_ADDR_PAGE:
+ ns->regs.num = ns->geom.pgaddrbytes;
+
+ break;
+ case STATE_ADDR_SEC:
+ ns->regs.num = ns->geom.secaddrbytes;
+ break;
+
+ case STATE_ADDR_ZERO:
+ ns->regs.num = 1;
+ break;
+
+ case STATE_ADDR_COLUMN:
+ /* Column address is always 2 bytes */
+ ns->regs.num = ns->geom.pgaddrbytes - ns->geom.secaddrbytes;
+ break;
+
+ default:
+ NS_ERR("switch_state: BUG! unknown address state\n");
+ }
+ } else {
+ /*
+ * Just reset internal counters.
+ */
+
+ ns->regs.num = 0;
+ ns->regs.count = 0;
+ }
+}
+
+static u_char ns_nand_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nandsim *ns = nand_get_controller_data(chip);
+ u_char outb = 0x00;
+
+ /* Sanity and correctness checks */
+ if (!ns->lines.ce) {
+ NS_ERR("read_byte: chip is disabled, return %#x\n", (uint)outb);
+ return outb;
+ }
+ if (ns->lines.ale || ns->lines.cle) {
+ NS_ERR("read_byte: ALE or CLE pin is high, return %#x\n", (uint)outb);
+ return outb;
+ }
+ if (!(ns->state & STATE_DATAOUT_MASK)) {
+ NS_WARN("read_byte: unexpected data output cycle, state is %s "
+ "return %#x\n", get_state_name(ns->state), (uint)outb);
+ return outb;
+ }
+
+ /* Status register may be read as many times as it is wanted */
+ if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS) {
+ NS_DBG("read_byte: return %#x status\n", ns->regs.status);
+ return ns->regs.status;
+ }
+
+ /* Check if there is any data in the internal buffer which may be read */
+ if (ns->regs.count == ns->regs.num) {
+ NS_WARN("read_byte: no more data to output, return %#x\n", (uint)outb);
+ return outb;
+ }
+
+ switch (NS_STATE(ns->state)) {
+ case STATE_DATAOUT:
+ if (ns->busw == 8) {
+ outb = ns->buf.byte[ns->regs.count];
+ ns->regs.count += 1;
+ } else {
+ outb = (u_char)cpu_to_le16(ns->buf.word[ns->regs.count >> 1]);
+ ns->regs.count += 2;
+ }
+ break;
+ case STATE_DATAOUT_ID:
+ NS_DBG("read_byte: read ID byte %d, total = %d\n", ns->regs.count, ns->regs.num);
+ outb = ns->ids[ns->regs.count];
+ ns->regs.count += 1;
+ break;
+ default:
+ BUG();
+ }
+
+ if (ns->regs.count == ns->regs.num) {
+ NS_DBG("read_byte: all bytes were read\n");
+
+ if (NS_STATE(ns->nxstate) == STATE_READY)
+ switch_state(ns);
+ }
+
+ return outb;
+}
+
+static void ns_nand_write_byte(struct mtd_info *mtd, u_char byte)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nandsim *ns = nand_get_controller_data(chip);
+
+ /* Sanity and correctness checks */
+ if (!ns->lines.ce) {
+ NS_ERR("write_byte: chip is disabled, ignore write\n");
+ return;
+ }
+ if (ns->lines.ale && ns->lines.cle) {
+ NS_ERR("write_byte: ALE and CLE pins are high simultaneously, ignore write\n");
+ return;
+ }
+
+ if (ns->lines.cle == 1) {
+ /*
+ * The byte written is a command.
+ */
+
+ if (byte == NAND_CMD_RESET) {
+ NS_LOG("reset chip\n");
+ switch_to_ready_state(ns, NS_STATUS_OK(ns));
+ return;
+ }
+
+ /* Check that the command byte is correct */
+ if (check_command(byte)) {
+ NS_ERR("write_byte: unknown command %#x\n", (uint)byte);
+ return;
+ }
+
+ if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS
+ || NS_STATE(ns->state) == STATE_DATAOUT) {
+ int row = ns->regs.row;
+
+ switch_state(ns);
+ if (byte == NAND_CMD_RNDOUT)
+ ns->regs.row = row;
+ }
+
+ /* Check if chip is expecting command */
+ if (NS_STATE(ns->nxstate) != STATE_UNKNOWN && !(ns->nxstate & STATE_CMD_MASK)) {
+ /* Do not warn if only 2 id bytes are read */
+ if (!(ns->regs.command == NAND_CMD_READID &&
+ NS_STATE(ns->state) == STATE_DATAOUT_ID && ns->regs.count == 2)) {
+ /*
+ * We are in situation when something else (not command)
+ * was expected but command was input. In this case ignore
+ * previous command(s)/state(s) and accept the last one.
+ */
+ NS_WARN("write_byte: command (%#x) wasn't expected, expected state is %s, "
+ "ignore previous states\n", (uint)byte, get_state_name(ns->nxstate));
+ }
+ switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+ }
+
+ NS_DBG("command byte corresponding to %s state accepted\n",
+ get_state_name(get_state_by_command(byte)));
+ ns->regs.command = byte;
+ switch_state(ns);
+
+ } else if (ns->lines.ale == 1) {
+ /*
+ * The byte written is an address.
+ */
+
+ if (NS_STATE(ns->nxstate) == STATE_UNKNOWN) {
+
+ NS_DBG("write_byte: operation isn't known yet, identify it\n");
+
+ if (find_operation(ns, 1) < 0)
+ return;
+
+ if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) {
+ switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+ return;
+ }
+
+ ns->regs.count = 0;
+ switch (NS_STATE(ns->nxstate)) {
+ case STATE_ADDR_PAGE:
+ ns->regs.num = ns->geom.pgaddrbytes;
+ break;
+ case STATE_ADDR_SEC:
+ ns->regs.num = ns->geom.secaddrbytes;
+ break;
+ case STATE_ADDR_ZERO:
+ ns->regs.num = 1;
+ break;
+ default:
+ BUG();
+ }
+ }
+
+ /* Check that chip is expecting address */
+ if (!(ns->nxstate & STATE_ADDR_MASK)) {
+ NS_ERR("write_byte: address (%#x) isn't expected, expected state is %s, "
+ "switch to STATE_READY\n", (uint)byte, get_state_name(ns->nxstate));
+ switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+ return;
+ }
+
+ /* Check if this is expected byte */
+ if (ns->regs.count == ns->regs.num) {
+ NS_ERR("write_byte: no more address bytes expected\n");
+ switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+ return;
+ }
+
+ accept_addr_byte(ns, byte);
+
+ ns->regs.count += 1;
+
+ NS_DBG("write_byte: address byte %#x was accepted (%d bytes input, %d expected)\n",
+ (uint)byte, ns->regs.count, ns->regs.num);
+
+ if (ns->regs.count == ns->regs.num) {
+ NS_DBG("address (%#x, %#x) is accepted\n", ns->regs.row, ns->regs.column);
+ switch_state(ns);
+ }
+
+ } else {
+ /*
+ * The byte written is an input data.
+ */
+
+ /* Check that chip is expecting data input */
+ if (!(ns->state & STATE_DATAIN_MASK)) {
+ NS_ERR("write_byte: data input (%#x) isn't expected, state is %s, "
+ "switch to %s\n", (uint)byte,
+ get_state_name(ns->state), get_state_name(STATE_READY));
+ switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+ return;
+ }
+
+ /* Check if this is expected byte */
+ if (ns->regs.count == ns->regs.num) {
+ NS_WARN("write_byte: %u input bytes has already been accepted, ignore write\n",
+ ns->regs.num);
+ return;
+ }
+
+ if (ns->busw == 8) {
+ ns->buf.byte[ns->regs.count] = byte;
+ ns->regs.count += 1;
+ } else {
+ ns->buf.word[ns->regs.count >> 1] = cpu_to_le16((uint16_t)byte);
+ ns->regs.count += 2;
+ }
+ }
+
+ return;
+}
+
+static void ns_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int bitmask)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nandsim *ns = nand_get_controller_data(chip);
+
+ ns->lines.cle = bitmask & NAND_CLE ? 1 : 0;
+ ns->lines.ale = bitmask & NAND_ALE ? 1 : 0;
+ ns->lines.ce = bitmask & NAND_NCE ? 1 : 0;
+
+ if (cmd != NAND_CMD_NONE)
+ ns_nand_write_byte(mtd, cmd);
+}
+
+static int ns_device_ready(struct mtd_info *mtd)
+{
+ NS_DBG("device_ready\n");
+ return 1;
+}
+
+static uint16_t ns_nand_read_word(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ NS_DBG("read_word\n");
+
+ return chip->read_byte(mtd) | (chip->read_byte(mtd) << 8);
+}
+
+static void ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nandsim *ns = nand_get_controller_data(chip);
+
+ /* Check that chip is expecting data input */
+ if (!(ns->state & STATE_DATAIN_MASK)) {
+ NS_ERR("write_buf: data input isn't expected, state is %s, "
+ "switch to STATE_READY\n", get_state_name(ns->state));
+ switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+ return;
+ }
+
+ /* Check if these are expected bytes */
+ if (ns->regs.count + len > ns->regs.num) {
+ NS_ERR("write_buf: too many input bytes\n");
+ switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+ return;
+ }
+
+ memcpy(ns->buf.byte + ns->regs.count, buf, len);
+ ns->regs.count += len;
+
+ if (ns->regs.count == ns->regs.num) {
+ NS_DBG("write_buf: %d bytes were written\n", ns->regs.count);
+ }
+}
+
+static void ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nandsim *ns = nand_get_controller_data(chip);
+
+ /* Sanity and correctness checks */
+ if (!ns->lines.ce) {
+ NS_ERR("read_buf: chip is disabled\n");
+ return;
+ }
+ if (ns->lines.ale || ns->lines.cle) {
+ NS_ERR("read_buf: ALE or CLE pin is high\n");
+ return;
+ }
+ if (!(ns->state & STATE_DATAOUT_MASK)) {
+ NS_WARN("read_buf: unexpected data output cycle, current state is %s\n",
+ get_state_name(ns->state));
+ return;
+ }
+
+ if (NS_STATE(ns->state) != STATE_DATAOUT) {
+ int i;
+
+ for (i = 0; i < len; i++)
+ buf[i] = mtd_to_nand(mtd)->read_byte(mtd);
+
+ return;
+ }
+
+ /* Check if these are expected bytes */
+ if (ns->regs.count + len > ns->regs.num) {
+ NS_ERR("read_buf: too many bytes to read\n");
+ switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+ return;
+ }
+
+ memcpy(buf, ns->buf.byte + ns->regs.count, len);
+ ns->regs.count += len;
+
+ if (ns->regs.count == ns->regs.num) {
+ if (NS_STATE(ns->nxstate) == STATE_READY)
+ switch_state(ns);
+ }
+
+ return;
+}
+
+/*
+ * Module initialization function
+ */
+static int __init ns_init_module(void)
+{
+ struct nand_chip *chip;
+ struct nandsim *nand;
+ int retval = -ENOMEM, i;
+
+ if (bus_width != 8 && bus_width != 16) {
+ NS_ERR("wrong bus width (%d), use only 8 or 16\n", bus_width);
+ return -EINVAL;
+ }
+
+ /* Allocate and initialize mtd_info, nand_chip and nandsim structures */
+ chip = kzalloc(sizeof(struct nand_chip) + sizeof(struct nandsim),
+ GFP_KERNEL);
+ if (!chip) {
+ NS_ERR("unable to allocate core structures.\n");
+ return -ENOMEM;
+ }
+ nsmtd = nand_to_mtd(chip);
+ nand = (struct nandsim *)(chip + 1);
+ nand_set_controller_data(chip, (void *)nand);
+
+ /*
+ * Register simulator's callbacks.
+ */
+ chip->cmd_ctrl = ns_hwcontrol;
+ chip->read_byte = ns_nand_read_byte;
+ chip->dev_ready = ns_device_ready;
+ chip->write_buf = ns_nand_write_buf;
+ chip->read_buf = ns_nand_read_buf;
+ chip->read_word = ns_nand_read_word;
+ chip->ecc.mode = NAND_ECC_SOFT;
+ chip->ecc.algo = NAND_ECC_HAMMING;
+ /* The NAND_SKIP_BBTSCAN option is necessary for 'overridesize' */
+ /* and 'badblocks' parameters to work */
+ chip->options |= NAND_SKIP_BBTSCAN;
+
+ switch (bbt) {
+ case 2:
+ chip->bbt_options |= NAND_BBT_NO_OOB;
+ case 1:
+ chip->bbt_options |= NAND_BBT_USE_FLASH;
+ case 0:
+ break;
+ default:
+ NS_ERR("bbt has to be 0..2\n");
+ retval = -EINVAL;
+ goto error;
+ }
+ /*
+ * Perform minimum nandsim structure initialization to handle
+ * the initial ID read command correctly
+ */
+ if (id_bytes[6] != 0xFF || id_bytes[7] != 0xFF)
+ nand->geom.idbytes = 8;
+ else if (id_bytes[4] != 0xFF || id_bytes[5] != 0xFF)
+ nand->geom.idbytes = 6;
+ else if (id_bytes[2] != 0xFF || id_bytes[3] != 0xFF)
+ nand->geom.idbytes = 4;
+ else
+ nand->geom.idbytes = 2;
+ nand->regs.status = NS_STATUS_OK(nand);
+ nand->nxstate = STATE_UNKNOWN;
+ nand->options |= OPT_PAGE512; /* temporary value */
+ memcpy(nand->ids, id_bytes, sizeof(nand->ids));
+ if (bus_width == 16) {
+ nand->busw = 16;
+ chip->options |= NAND_BUSWIDTH_16;
+ }
+
+ nsmtd->owner = THIS_MODULE;
+
+ if ((retval = parse_weakblocks()) != 0)
+ goto error;
+
+ if ((retval = parse_weakpages()) != 0)
+ goto error;
+
+ if ((retval = parse_gravepages()) != 0)
+ goto error;
+
+ retval = nand_scan_ident(nsmtd, 1, NULL);
+ if (retval) {
+ NS_ERR("cannot scan NAND Simulator device\n");
+ goto error;
+ }
+
+ if (bch) {
+ unsigned int eccsteps, eccbytes;
+ if (!mtd_nand_has_bch()) {
+ NS_ERR("BCH ECC support is disabled\n");
+ retval = -EINVAL;
+ goto error;
+ }
+ /* use 512-byte ecc blocks */
+ eccsteps = nsmtd->writesize/512;
+ eccbytes = (bch*13+7)/8;
+ /* do not bother supporting small page devices */
+ if ((nsmtd->oobsize < 64) || !eccsteps) {
+ NS_ERR("bch not available on small page devices\n");
+ retval = -EINVAL;
+ goto error;
+ }
+ if ((eccbytes*eccsteps+2) > nsmtd->oobsize) {
+ NS_ERR("invalid bch value %u\n", bch);
+ retval = -EINVAL;
+ goto error;
+ }
+ chip->ecc.mode = NAND_ECC_SOFT;
+ chip->ecc.algo = NAND_ECC_BCH;
+ chip->ecc.size = 512;
+ chip->ecc.strength = bch;
+ chip->ecc.bytes = eccbytes;
+ NS_INFO("using %u-bit/%u bytes BCH ECC\n", bch, chip->ecc.size);
+ }
+
+ retval = nand_scan_tail(nsmtd);
+ if (retval) {
+ NS_ERR("can't register NAND Simulator\n");
+ goto error;
+ }
+
+ if (overridesize) {
+ uint64_t new_size = (uint64_t)nsmtd->erasesize << overridesize;
+ if (new_size >> overridesize != nsmtd->erasesize) {
+ NS_ERR("overridesize is too big\n");
+ retval = -EINVAL;
+ goto err_exit;
+ }
+ /* N.B. This relies on nand_scan not doing anything with the size before we change it */
+ nsmtd->size = new_size;
+ chip->chipsize = new_size;
+ chip->chip_shift = ffs(nsmtd->erasesize) + overridesize - 1;
+ chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;
+ }
+
+ if ((retval = setup_wear_reporting(nsmtd)) != 0)
+ goto err_exit;
+
+ if ((retval = init_nandsim(nsmtd)) != 0)
+ goto err_exit;
+
+ if ((retval = chip->scan_bbt(nsmtd)) != 0)
+ goto err_exit;
+
+ if ((retval = parse_badblocks(nand, nsmtd)) != 0)
+ goto err_exit;
+
+ /* Register NAND partitions */
+ retval = mtd_device_register(nsmtd, &nand->partitions[0],
+ nand->nbparts);
+ if (retval != 0)
+ goto err_exit;
+
+ if ((retval = nandsim_debugfs_create(nand)) != 0)
+ goto err_exit;
+
+ return 0;
+
+err_exit:
+ free_nandsim(nand);
+ nand_release(nsmtd);
+ for (i = 0;i < ARRAY_SIZE(nand->partitions); ++i)
+ kfree(nand->partitions[i].name);
+error:
+ kfree(chip);
+ free_lists();
+
+ return retval;
+}
+
+module_init(ns_init_module);
+
+/*
+ * Module clean-up function
+ */
+static void __exit ns_cleanup_module(void)
+{
+ struct nand_chip *chip = mtd_to_nand(nsmtd);
+ struct nandsim *ns = nand_get_controller_data(chip);
+ int i;
+
+ free_nandsim(ns); /* Free nandsim private resources */
+ nand_release(nsmtd); /* Unregister driver */
+ for (i = 0;i < ARRAY_SIZE(ns->partitions); ++i)
+ kfree(ns->partitions[i].name);
+ kfree(mtd_to_nand(nsmtd)); /* Free other structures */
+ free_lists();
+}
+
+module_exit(ns_cleanup_module);
+
+MODULE_LICENSE ("GPL");
+MODULE_AUTHOR ("Artem B. Bityuckiy");
+MODULE_DESCRIPTION ("The NAND flash simulator");
diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/raw/ndfc.c
index d8a806894937..d8a806894937 100644
--- a/drivers/mtd/nand/ndfc.c
+++ b/drivers/mtd/nand/raw/ndfc.c
diff --git a/drivers/mtd/nand/nuc900_nand.c b/drivers/mtd/nand/raw/nuc900_nand.c
index af5b32c9a791..af5b32c9a791 100644
--- a/drivers/mtd/nand/nuc900_nand.c
+++ b/drivers/mtd/nand/raw/nuc900_nand.c
diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c
new file mode 100644
index 000000000000..e50c64adc3c8
--- /dev/null
+++ b/drivers/mtd/nand/raw/omap2.c
@@ -0,0 +1,2319 @@
+/*
+ * Copyright © 2004 Texas Instruments, Jian Zhang <jzhang@ti.com>
+ * Copyright © 2004 Micron Technology Inc.
+ * Copyright © 2004 David Brownell
+ *
+ * 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.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/omap-dma.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include <linux/mtd/nand_bch.h>
+#include <linux/platform_data/elm.h>
+
+#include <linux/omap-gpmc.h>
+#include <linux/platform_data/mtd-nand-omap2.h>
+
+#define DRIVER_NAME "omap2-nand"
+#define OMAP_NAND_TIMEOUT_MS 5000
+
+#define NAND_Ecc_P1e (1 << 0)
+#define NAND_Ecc_P2e (1 << 1)
+#define NAND_Ecc_P4e (1 << 2)
+#define NAND_Ecc_P8e (1 << 3)
+#define NAND_Ecc_P16e (1 << 4)
+#define NAND_Ecc_P32e (1 << 5)
+#define NAND_Ecc_P64e (1 << 6)
+#define NAND_Ecc_P128e (1 << 7)
+#define NAND_Ecc_P256e (1 << 8)
+#define NAND_Ecc_P512e (1 << 9)
+#define NAND_Ecc_P1024e (1 << 10)
+#define NAND_Ecc_P2048e (1 << 11)
+
+#define NAND_Ecc_P1o (1 << 16)
+#define NAND_Ecc_P2o (1 << 17)
+#define NAND_Ecc_P4o (1 << 18)
+#define NAND_Ecc_P8o (1 << 19)
+#define NAND_Ecc_P16o (1 << 20)
+#define NAND_Ecc_P32o (1 << 21)
+#define NAND_Ecc_P64o (1 << 22)
+#define NAND_Ecc_P128o (1 << 23)
+#define NAND_Ecc_P256o (1 << 24)
+#define NAND_Ecc_P512o (1 << 25)
+#define NAND_Ecc_P1024o (1 << 26)
+#define NAND_Ecc_P2048o (1 << 27)
+
+#define TF(value) (value ? 1 : 0)
+
+#define P2048e(a) (TF(a & NAND_Ecc_P2048e) << 0)
+#define P2048o(a) (TF(a & NAND_Ecc_P2048o) << 1)
+#define P1e(a) (TF(a & NAND_Ecc_P1e) << 2)
+#define P1o(a) (TF(a & NAND_Ecc_P1o) << 3)
+#define P2e(a) (TF(a & NAND_Ecc_P2e) << 4)
+#define P2o(a) (TF(a & NAND_Ecc_P2o) << 5)
+#define P4e(a) (TF(a & NAND_Ecc_P4e) << 6)
+#define P4o(a) (TF(a & NAND_Ecc_P4o) << 7)
+
+#define P8e(a) (TF(a & NAND_Ecc_P8e) << 0)
+#define P8o(a) (TF(a & NAND_Ecc_P8o) << 1)
+#define P16e(a) (TF(a & NAND_Ecc_P16e) << 2)
+#define P16o(a) (TF(a & NAND_Ecc_P16o) << 3)
+#define P32e(a) (TF(a & NAND_Ecc_P32e) << 4)
+#define P32o(a) (TF(a & NAND_Ecc_P32o) << 5)
+#define P64e(a) (TF(a & NAND_Ecc_P64e) << 6)
+#define P64o(a) (TF(a & NAND_Ecc_P64o) << 7)
+
+#define P128e(a) (TF(a & NAND_Ecc_P128e) << 0)
+#define P128o(a) (TF(a & NAND_Ecc_P128o) << 1)
+#define P256e(a) (TF(a & NAND_Ecc_P256e) << 2)
+#define P256o(a) (TF(a & NAND_Ecc_P256o) << 3)
+#define P512e(a) (TF(a & NAND_Ecc_P512e) << 4)
+#define P512o(a) (TF(a & NAND_Ecc_P512o) << 5)
+#define P1024e(a) (TF(a & NAND_Ecc_P1024e) << 6)
+#define P1024o(a) (TF(a & NAND_Ecc_P1024o) << 7)
+
+#define P8e_s(a) (TF(a & NAND_Ecc_P8e) << 0)
+#define P8o_s(a) (TF(a & NAND_Ecc_P8o) << 1)
+#define P16e_s(a) (TF(a & NAND_Ecc_P16e) << 2)
+#define P16o_s(a) (TF(a & NAND_Ecc_P16o) << 3)
+#define P1e_s(a) (TF(a & NAND_Ecc_P1e) << 4)
+#define P1o_s(a) (TF(a & NAND_Ecc_P1o) << 5)
+#define P2e_s(a) (TF(a & NAND_Ecc_P2e) << 6)
+#define P2o_s(a) (TF(a & NAND_Ecc_P2o) << 7)
+
+#define P4e_s(a) (TF(a & NAND_Ecc_P4e) << 0)
+#define P4o_s(a) (TF(a & NAND_Ecc_P4o) << 1)
+
+#define PREFETCH_CONFIG1_CS_SHIFT 24
+#define ECC_CONFIG_CS_SHIFT 1
+#define CS_MASK 0x7
+#define ENABLE_PREFETCH (0x1 << 7)
+#define DMA_MPU_MODE_SHIFT 2
+#define ECCSIZE0_SHIFT 12
+#define ECCSIZE1_SHIFT 22
+#define ECC1RESULTSIZE 0x1
+#define ECCCLEAR 0x100
+#define ECC1 0x1
+#define PREFETCH_FIFOTHRESHOLD_MAX 0x40
+#define PREFETCH_FIFOTHRESHOLD(val) ((val) << 8)
+#define PREFETCH_STATUS_COUNT(val) (val & 0x00003fff)
+#define PREFETCH_STATUS_FIFO_CNT(val) ((val >> 24) & 0x7F)
+#define STATUS_BUFF_EMPTY 0x00000001
+
+#define SECTOR_BYTES 512
+/* 4 bit padding to make byte aligned, 56 = 52 + 4 */
+#define BCH4_BIT_PAD 4
+
+/* GPMC ecc engine settings for read */
+#define BCH_WRAPMODE_1 1 /* BCH wrap mode 1 */
+#define BCH8R_ECC_SIZE0 0x1a /* ecc_size0 = 26 */
+#define BCH8R_ECC_SIZE1 0x2 /* ecc_size1 = 2 */
+#define BCH4R_ECC_SIZE0 0xd /* ecc_size0 = 13 */
+#define BCH4R_ECC_SIZE1 0x3 /* ecc_size1 = 3 */
+
+/* GPMC ecc engine settings for write */
+#define BCH_WRAPMODE_6 6 /* BCH wrap mode 6 */
+#define BCH_ECC_SIZE0 0x0 /* ecc_size0 = 0, no oob protection */
+#define BCH_ECC_SIZE1 0x20 /* ecc_size1 = 32 */
+
+#define BADBLOCK_MARKER_LENGTH 2
+
+static u_char bch16_vector[] = {0xf5, 0x24, 0x1c, 0xd0, 0x61, 0xb3, 0xf1, 0x55,
+ 0x2e, 0x2c, 0x86, 0xa3, 0xed, 0x36, 0x1b, 0x78,
+ 0x48, 0x76, 0xa9, 0x3b, 0x97, 0xd1, 0x7a, 0x93,
+ 0x07, 0x0e};
+static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc,
+ 0xac, 0x6b, 0xff, 0x99, 0x7b};
+static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10};
+
+/* Shared among all NAND instances to synchronize access to the ECC Engine */
+static struct nand_hw_control omap_gpmc_controller = {
+ .lock = __SPIN_LOCK_UNLOCKED(omap_gpmc_controller.lock),
+ .wq = __WAIT_QUEUE_HEAD_INITIALIZER(omap_gpmc_controller.wq),
+};
+
+struct omap_nand_info {
+ struct nand_chip nand;
+ struct platform_device *pdev;
+
+ int gpmc_cs;
+ bool dev_ready;
+ enum nand_io xfer_type;
+ int devsize;
+ enum omap_ecc ecc_opt;
+ struct device_node *elm_of_node;
+
+ unsigned long phys_base;
+ struct completion comp;
+ struct dma_chan *dma;
+ int gpmc_irq_fifo;
+ int gpmc_irq_count;
+ enum {
+ OMAP_NAND_IO_READ = 0, /* read */
+ OMAP_NAND_IO_WRITE, /* write */
+ } iomode;
+ u_char *buf;
+ int buf_len;
+ /* Interface to GPMC */
+ struct gpmc_nand_regs reg;
+ struct gpmc_nand_ops *ops;
+ bool flash_bbt;
+ /* fields specific for BCHx_HW ECC scheme */
+ struct device *elm_dev;
+ /* NAND ready gpio */
+ struct gpio_desc *ready_gpiod;
+};
+
+static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd)
+{
+ return container_of(mtd_to_nand(mtd), struct omap_nand_info, nand);
+}
+
+/**
+ * omap_prefetch_enable - configures and starts prefetch transfer
+ * @cs: cs (chip select) number
+ * @fifo_th: fifo threshold to be used for read/ write
+ * @dma_mode: dma mode enable (1) or disable (0)
+ * @u32_count: number of bytes to be transferred
+ * @is_write: prefetch read(0) or write post(1) mode
+ */
+static int omap_prefetch_enable(int cs, int fifo_th, int dma_mode,
+ unsigned int u32_count, int is_write, struct omap_nand_info *info)
+{
+ u32 val;
+
+ if (fifo_th > PREFETCH_FIFOTHRESHOLD_MAX)
+ return -1;
+
+ if (readl(info->reg.gpmc_prefetch_control))
+ return -EBUSY;
+
+ /* Set the amount of bytes to be prefetched */
+ writel(u32_count, info->reg.gpmc_prefetch_config2);
+
+ /* Set dma/mpu mode, the prefetch read / post write and
+ * enable the engine. Set which cs is has requested for.
+ */
+ val = ((cs << PREFETCH_CONFIG1_CS_SHIFT) |
+ PREFETCH_FIFOTHRESHOLD(fifo_th) | ENABLE_PREFETCH |
+ (dma_mode << DMA_MPU_MODE_SHIFT) | (is_write & 0x1));
+ writel(val, info->reg.gpmc_prefetch_config1);
+
+ /* Start the prefetch engine */
+ writel(0x1, info->reg.gpmc_prefetch_control);
+
+ return 0;
+}
+
+/**
+ * omap_prefetch_reset - disables and stops the prefetch engine
+ */
+static int omap_prefetch_reset(int cs, struct omap_nand_info *info)
+{
+ u32 config1;
+
+ /* check if the same module/cs is trying to reset */
+ config1 = readl(info->reg.gpmc_prefetch_config1);
+ if (((config1 >> PREFETCH_CONFIG1_CS_SHIFT) & CS_MASK) != cs)
+ return -EINVAL;
+
+ /* Stop the PFPW engine */
+ writel(0x0, info->reg.gpmc_prefetch_control);
+
+ /* Reset/disable the PFPW engine */
+ writel(0x0, info->reg.gpmc_prefetch_config1);
+
+ return 0;
+}
+
+/**
+ * omap_hwcontrol - hardware specific access to control-lines
+ * @mtd: MTD device structure
+ * @cmd: command to device
+ * @ctrl:
+ * NAND_NCE: bit 0 -> don't care
+ * NAND_CLE: bit 1 -> Command Latch
+ * NAND_ALE: bit 2 -> Address Latch
+ *
+ * NOTE: boards may use different bits for these!!
+ */
+static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+ struct omap_nand_info *info = mtd_to_omap(mtd);
+
+ if (cmd != NAND_CMD_NONE) {
+ if (ctrl & NAND_CLE)
+ writeb(cmd, info->reg.gpmc_nand_command);
+
+ else if (ctrl & NAND_ALE)
+ writeb(cmd, info->reg.gpmc_nand_address);
+
+ else /* NAND_NCE */
+ writeb(cmd, info->reg.gpmc_nand_data);
+ }
+}
+
+/**
+ * omap_read_buf8 - read data from NAND controller into buffer
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
+ */
+static void omap_read_buf8(struct mtd_info *mtd, u_char *buf, int len)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+
+ ioread8_rep(nand->IO_ADDR_R, buf, len);
+}
+
+/**
+ * omap_write_buf8 - write buffer to NAND controller
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ */
+static void omap_write_buf8(struct mtd_info *mtd, const u_char *buf, int len)
+{
+ struct omap_nand_info *info = mtd_to_omap(mtd);
+ u_char *p = (u_char *)buf;
+ bool status;
+
+ while (len--) {
+ iowrite8(*p++, info->nand.IO_ADDR_W);
+ /* wait until buffer is available for write */
+ do {
+ status = info->ops->nand_writebuffer_empty();
+ } while (!status);
+ }
+}
+
+/**
+ * omap_read_buf16 - read data from NAND controller into buffer
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
+ */
+static void omap_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+
+ ioread16_rep(nand->IO_ADDR_R, buf, len / 2);
+}
+
+/**
+ * omap_write_buf16 - write buffer to NAND controller
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ */
+static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len)
+{
+ struct omap_nand_info *info = mtd_to_omap(mtd);
+ u16 *p = (u16 *) buf;
+ bool status;
+ /* FIXME try bursts of writesw() or DMA ... */
+ len >>= 1;
+
+ while (len--) {
+ iowrite16(*p++, info->nand.IO_ADDR_W);
+ /* wait until buffer is available for write */
+ do {
+ status = info->ops->nand_writebuffer_empty();
+ } while (!status);
+ }
+}
+
+/**
+ * omap_read_buf_pref - read data from NAND controller into buffer
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
+ */
+static void omap_read_buf_pref(struct mtd_info *mtd, u_char *buf, int len)
+{
+ struct omap_nand_info *info = mtd_to_omap(mtd);
+ uint32_t r_count = 0;
+ int ret = 0;
+ u32 *p = (u32 *)buf;
+
+ /* take care of subpage reads */
+ if (len % 4) {
+ if (info->nand.options & NAND_BUSWIDTH_16)
+ omap_read_buf16(mtd, buf, len % 4);
+ else
+ omap_read_buf8(mtd, buf, len % 4);
+ p = (u32 *) (buf + len % 4);
+ len -= len % 4;
+ }
+
+ /* configure and start prefetch transfer */
+ ret = omap_prefetch_enable(info->gpmc_cs,
+ PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x0, info);
+ if (ret) {
+ /* PFPW engine is busy, use cpu copy method */
+ if (info->nand.options & NAND_BUSWIDTH_16)
+ omap_read_buf16(mtd, (u_char *)p, len);
+ else
+ omap_read_buf8(mtd, (u_char *)p, len);
+ } else {
+ do {
+ r_count = readl(info->reg.gpmc_prefetch_status);
+ r_count = PREFETCH_STATUS_FIFO_CNT(r_count);
+ r_count = r_count >> 2;
+ ioread32_rep(info->nand.IO_ADDR_R, p, r_count);
+ p += r_count;
+ len -= r_count << 2;
+ } while (len);
+ /* disable and stop the PFPW engine */
+ omap_prefetch_reset(info->gpmc_cs, info);
+ }
+}
+
+/**
+ * omap_write_buf_pref - write buffer to NAND controller
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ */
+static void omap_write_buf_pref(struct mtd_info *mtd,
+ const u_char *buf, int len)
+{
+ struct omap_nand_info *info = mtd_to_omap(mtd);
+ uint32_t w_count = 0;
+ int i = 0, ret = 0;
+ u16 *p = (u16 *)buf;
+ unsigned long tim, limit;
+ u32 val;
+
+ /* take care of subpage writes */
+ if (len % 2 != 0) {
+ writeb(*buf, info->nand.IO_ADDR_W);
+ p = (u16 *)(buf + 1);
+ len--;
+ }
+
+ /* configure and start prefetch transfer */
+ ret = omap_prefetch_enable(info->gpmc_cs,
+ PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x1, info);
+ if (ret) {
+ /* PFPW engine is busy, use cpu copy method */
+ if (info->nand.options & NAND_BUSWIDTH_16)
+ omap_write_buf16(mtd, (u_char *)p, len);
+ else
+ omap_write_buf8(mtd, (u_char *)p, len);
+ } else {
+ while (len) {
+ w_count = readl(info->reg.gpmc_prefetch_status);
+ w_count = PREFETCH_STATUS_FIFO_CNT(w_count);
+ w_count = w_count >> 1;
+ for (i = 0; (i < w_count) && len; i++, len -= 2)
+ iowrite16(*p++, info->nand.IO_ADDR_W);
+ }
+ /* wait for data to flushed-out before reset the prefetch */
+ tim = 0;
+ limit = (loops_per_jiffy *
+ msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS));
+ do {
+ cpu_relax();
+ val = readl(info->reg.gpmc_prefetch_status);
+ val = PREFETCH_STATUS_COUNT(val);
+ } while (val && (tim++ < limit));
+
+ /* disable and stop the PFPW engine */
+ omap_prefetch_reset(info->gpmc_cs, info);
+ }
+}
+
+/*
+ * omap_nand_dma_callback: callback on the completion of dma transfer
+ * @data: pointer to completion data structure
+ */
+static void omap_nand_dma_callback(void *data)
+{
+ complete((struct completion *) data);
+}
+
+/*
+ * omap_nand_dma_transfer: configure and start dma transfer
+ * @mtd: MTD device structure
+ * @addr: virtual address in RAM of source/destination
+ * @len: number of data bytes to be transferred
+ * @is_write: flag for read/write operation
+ */
+static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
+ unsigned int len, int is_write)
+{
+ struct omap_nand_info *info = mtd_to_omap(mtd);
+ struct dma_async_tx_descriptor *tx;
+ enum dma_data_direction dir = is_write ? DMA_TO_DEVICE :
+ DMA_FROM_DEVICE;
+ struct scatterlist sg;
+ unsigned long tim, limit;
+ unsigned n;
+ int ret;
+ u32 val;
+
+ if (!virt_addr_valid(addr))
+ goto out_copy;
+
+ sg_init_one(&sg, addr, len);
+ n = dma_map_sg(info->dma->device->dev, &sg, 1, dir);
+ if (n == 0) {
+ dev_err(&info->pdev->dev,
+ "Couldn't DMA map a %d byte buffer\n", len);
+ goto out_copy;
+ }
+
+ tx = dmaengine_prep_slave_sg(info->dma, &sg, n,
+ is_write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!tx)
+ goto out_copy_unmap;
+
+ tx->callback = omap_nand_dma_callback;
+ tx->callback_param = &info->comp;
+ dmaengine_submit(tx);
+
+ init_completion(&info->comp);
+
+ /* setup and start DMA using dma_addr */
+ dma_async_issue_pending(info->dma);
+
+ /* configure and start prefetch transfer */
+ ret = omap_prefetch_enable(info->gpmc_cs,
+ PREFETCH_FIFOTHRESHOLD_MAX, 0x1, len, is_write, info);
+ if (ret)
+ /* PFPW engine is busy, use cpu copy method */
+ goto out_copy_unmap;
+
+ wait_for_completion(&info->comp);
+ tim = 0;
+ limit = (loops_per_jiffy * msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS));
+
+ do {
+ cpu_relax();
+ val = readl(info->reg.gpmc_prefetch_status);
+ val = PREFETCH_STATUS_COUNT(val);
+ } while (val && (tim++ < limit));
+
+ /* disable and stop the PFPW engine */
+ omap_prefetch_reset(info->gpmc_cs, info);
+
+ dma_unmap_sg(info->dma->device->dev, &sg, 1, dir);
+ return 0;
+
+out_copy_unmap:
+ dma_unmap_sg(info->dma->device->dev, &sg, 1, dir);
+out_copy:
+ if (info->nand.options & NAND_BUSWIDTH_16)
+ is_write == 0 ? omap_read_buf16(mtd, (u_char *) addr, len)
+ : omap_write_buf16(mtd, (u_char *) addr, len);
+ else
+ is_write == 0 ? omap_read_buf8(mtd, (u_char *) addr, len)
+ : omap_write_buf8(mtd, (u_char *) addr, len);
+ return 0;
+}
+
+/**
+ * omap_read_buf_dma_pref - read data from NAND controller into buffer
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
+ */
+static void omap_read_buf_dma_pref(struct mtd_info *mtd, u_char *buf, int len)
+{
+ if (len <= mtd->oobsize)
+ omap_read_buf_pref(mtd, buf, len);
+ else
+ /* start transfer in DMA mode */
+ omap_nand_dma_transfer(mtd, buf, len, 0x0);
+}
+
+/**
+ * omap_write_buf_dma_pref - write buffer to NAND controller
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ */
+static void omap_write_buf_dma_pref(struct mtd_info *mtd,
+ const u_char *buf, int len)
+{
+ if (len <= mtd->oobsize)
+ omap_write_buf_pref(mtd, buf, len);
+ else
+ /* start transfer in DMA mode */
+ omap_nand_dma_transfer(mtd, (u_char *) buf, len, 0x1);
+}
+
+/*
+ * omap_nand_irq - GPMC irq handler
+ * @this_irq: gpmc irq number
+ * @dev: omap_nand_info structure pointer is passed here
+ */
+static irqreturn_t omap_nand_irq(int this_irq, void *dev)
+{
+ struct omap_nand_info *info = (struct omap_nand_info *) dev;
+ u32 bytes;
+
+ bytes = readl(info->reg.gpmc_prefetch_status);
+ bytes = PREFETCH_STATUS_FIFO_CNT(bytes);
+ bytes = bytes & 0xFFFC; /* io in multiple of 4 bytes */
+ if (info->iomode == OMAP_NAND_IO_WRITE) { /* checks for write io */
+ if (this_irq == info->gpmc_irq_count)
+ goto done;
+
+ if (info->buf_len && (info->buf_len < bytes))
+ bytes = info->buf_len;
+ else if (!info->buf_len)
+ bytes = 0;
+ iowrite32_rep(info->nand.IO_ADDR_W,
+ (u32 *)info->buf, bytes >> 2);
+ info->buf = info->buf + bytes;
+ info->buf_len -= bytes;
+
+ } else {
+ ioread32_rep(info->nand.IO_ADDR_R,
+ (u32 *)info->buf, bytes >> 2);
+ info->buf = info->buf + bytes;
+
+ if (this_irq == info->gpmc_irq_count)
+ goto done;
+ }
+
+ return IRQ_HANDLED;
+
+done:
+ complete(&info->comp);
+
+ disable_irq_nosync(info->gpmc_irq_fifo);
+ disable_irq_nosync(info->gpmc_irq_count);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * omap_read_buf_irq_pref - read data from NAND controller into buffer
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
+ */
+static void omap_read_buf_irq_pref(struct mtd_info *mtd, u_char *buf, int len)
+{
+ struct omap_nand_info *info = mtd_to_omap(mtd);
+ int ret = 0;
+
+ if (len <= mtd->oobsize) {
+ omap_read_buf_pref(mtd, buf, len);
+ return;
+ }
+
+ info->iomode = OMAP_NAND_IO_READ;
+ info->buf = buf;
+ init_completion(&info->comp);
+
+ /* configure and start prefetch transfer */
+ ret = omap_prefetch_enable(info->gpmc_cs,
+ PREFETCH_FIFOTHRESHOLD_MAX/2, 0x0, len, 0x0, info);
+ if (ret)
+ /* PFPW engine is busy, use cpu copy method */
+ goto out_copy;
+
+ info->buf_len = len;
+
+ enable_irq(info->gpmc_irq_count);
+ enable_irq(info->gpmc_irq_fifo);
+
+ /* waiting for read to complete */
+ wait_for_completion(&info->comp);
+
+ /* disable and stop the PFPW engine */
+ omap_prefetch_reset(info->gpmc_cs, info);
+ return;
+
+out_copy:
+ if (info->nand.options & NAND_BUSWIDTH_16)
+ omap_read_buf16(mtd, buf, len);
+ else
+ omap_read_buf8(mtd, buf, len);
+}
+
+/*
+ * omap_write_buf_irq_pref - write buffer to NAND controller
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ */
+static void omap_write_buf_irq_pref(struct mtd_info *mtd,
+ const u_char *buf, int len)
+{
+ struct omap_nand_info *info = mtd_to_omap(mtd);
+ int ret = 0;
+ unsigned long tim, limit;
+ u32 val;
+
+ if (len <= mtd->oobsize) {
+ omap_write_buf_pref(mtd, buf, len);
+ return;
+ }
+
+ info->iomode = OMAP_NAND_IO_WRITE;
+ info->buf = (u_char *) buf;
+ init_completion(&info->comp);
+
+ /* configure and start prefetch transfer : size=24 */
+ ret = omap_prefetch_enable(info->gpmc_cs,
+ (PREFETCH_FIFOTHRESHOLD_MAX * 3) / 8, 0x0, len, 0x1, info);
+ if (ret)
+ /* PFPW engine is busy, use cpu copy method */
+ goto out_copy;
+
+ info->buf_len = len;
+
+ enable_irq(info->gpmc_irq_count);
+ enable_irq(info->gpmc_irq_fifo);
+
+ /* waiting for write to complete */
+ wait_for_completion(&info->comp);
+
+ /* wait for data to flushed-out before reset the prefetch */
+ tim = 0;
+ limit = (loops_per_jiffy * msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS));
+ do {
+ val = readl(info->reg.gpmc_prefetch_status);
+ val = PREFETCH_STATUS_COUNT(val);
+ cpu_relax();
+ } while (val && (tim++ < limit));
+
+ /* disable and stop the PFPW engine */
+ omap_prefetch_reset(info->gpmc_cs, info);
+ return;
+
+out_copy:
+ if (info->nand.options & NAND_BUSWIDTH_16)
+ omap_write_buf16(mtd, buf, len);
+ else
+ omap_write_buf8(mtd, buf, len);
+}
+
+/**
+ * gen_true_ecc - This function will generate true ECC value
+ * @ecc_buf: buffer to store ecc code
+ *
+ * This generated true ECC value can be used when correcting
+ * data read from NAND flash memory core
+ */
+static void gen_true_ecc(u8 *ecc_buf)
+{
+ u32 tmp = ecc_buf[0] | (ecc_buf[1] << 16) |
+ ((ecc_buf[2] & 0xF0) << 20) | ((ecc_buf[2] & 0x0F) << 8);
+
+ ecc_buf[0] = ~(P64o(tmp) | P64e(tmp) | P32o(tmp) | P32e(tmp) |
+ P16o(tmp) | P16e(tmp) | P8o(tmp) | P8e(tmp));
+ ecc_buf[1] = ~(P1024o(tmp) | P1024e(tmp) | P512o(tmp) | P512e(tmp) |
+ P256o(tmp) | P256e(tmp) | P128o(tmp) | P128e(tmp));
+ ecc_buf[2] = ~(P4o(tmp) | P4e(tmp) | P2o(tmp) | P2e(tmp) | P1o(tmp) |
+ P1e(tmp) | P2048o(tmp) | P2048e(tmp));
+}
+
+/**
+ * omap_compare_ecc - Detect (2 bits) and correct (1 bit) error in data
+ * @ecc_data1: ecc code from nand spare area
+ * @ecc_data2: ecc code from hardware register obtained from hardware ecc
+ * @page_data: page data
+ *
+ * This function compares two ECC's and indicates if there is an error.
+ * If the error can be corrected it will be corrected to the buffer.
+ * If there is no error, %0 is returned. If there is an error but it
+ * was corrected, %1 is returned. Otherwise, %-1 is returned.
+ */
+static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */
+ u8 *ecc_data2, /* read from register */
+ u8 *page_data)
+{
+ uint i;
+ u8 tmp0_bit[8], tmp1_bit[8], tmp2_bit[8];
+ u8 comp0_bit[8], comp1_bit[8], comp2_bit[8];
+ u8 ecc_bit[24];
+ u8 ecc_sum = 0;
+ u8 find_bit = 0;
+ uint find_byte = 0;
+ int isEccFF;
+
+ isEccFF = ((*(u32 *)ecc_data1 & 0xFFFFFF) == 0xFFFFFF);
+
+ gen_true_ecc(ecc_data1);
+ gen_true_ecc(ecc_data2);
+
+ for (i = 0; i <= 2; i++) {
+ *(ecc_data1 + i) = ~(*(ecc_data1 + i));
+ *(ecc_data2 + i) = ~(*(ecc_data2 + i));
+ }
+
+ for (i = 0; i < 8; i++) {
+ tmp0_bit[i] = *ecc_data1 % 2;
+ *ecc_data1 = *ecc_data1 / 2;
+ }
+
+ for (i = 0; i < 8; i++) {
+ tmp1_bit[i] = *(ecc_data1 + 1) % 2;
+ *(ecc_data1 + 1) = *(ecc_data1 + 1) / 2;
+ }
+
+ for (i = 0; i < 8; i++) {
+ tmp2_bit[i] = *(ecc_data1 + 2) % 2;
+ *(ecc_data1 + 2) = *(ecc_data1 + 2) / 2;
+ }
+
+ for (i = 0; i < 8; i++) {
+ comp0_bit[i] = *ecc_data2 % 2;
+ *ecc_data2 = *ecc_data2 / 2;
+ }
+
+ for (i = 0; i < 8; i++) {
+ comp1_bit[i] = *(ecc_data2 + 1) % 2;
+ *(ecc_data2 + 1) = *(ecc_data2 + 1) / 2;
+ }
+
+ for (i = 0; i < 8; i++) {
+ comp2_bit[i] = *(ecc_data2 + 2) % 2;
+ *(ecc_data2 + 2) = *(ecc_data2 + 2) / 2;
+ }
+
+ for (i = 0; i < 6; i++)
+ ecc_bit[i] = tmp2_bit[i + 2] ^ comp2_bit[i + 2];
+
+ for (i = 0; i < 8; i++)
+ ecc_bit[i + 6] = tmp0_bit[i] ^ comp0_bit[i];
+
+ for (i = 0; i < 8; i++)
+ ecc_bit[i + 14] = tmp1_bit[i] ^ comp1_bit[i];
+
+ ecc_bit[22] = tmp2_bit[0] ^ comp2_bit[0];
+ ecc_bit[23] = tmp2_bit[1] ^ comp2_bit[1];
+
+ for (i = 0; i < 24; i++)
+ ecc_sum += ecc_bit[i];
+
+ switch (ecc_sum) {
+ case 0:
+ /* Not reached because this function is not called if
+ * ECC values are equal
+ */
+ return 0;
+
+ case 1:
+ /* Uncorrectable error */
+ pr_debug("ECC UNCORRECTED_ERROR 1\n");
+ return -EBADMSG;
+
+ case 11:
+ /* UN-Correctable error */
+ pr_debug("ECC UNCORRECTED_ERROR B\n");
+ return -EBADMSG;
+
+ case 12:
+ /* Correctable error */
+ find_byte = (ecc_bit[23] << 8) +
+ (ecc_bit[21] << 7) +
+ (ecc_bit[19] << 6) +
+ (ecc_bit[17] << 5) +
+ (ecc_bit[15] << 4) +
+ (ecc_bit[13] << 3) +
+ (ecc_bit[11] << 2) +
+ (ecc_bit[9] << 1) +
+ ecc_bit[7];
+
+ find_bit = (ecc_bit[5] << 2) + (ecc_bit[3] << 1) + ecc_bit[1];
+
+ pr_debug("Correcting single bit ECC error at offset: "
+ "%d, bit: %d\n", find_byte, find_bit);
+
+ page_data[find_byte] ^= (1 << find_bit);
+
+ return 1;
+ default:
+ if (isEccFF) {
+ if (ecc_data2[0] == 0 &&
+ ecc_data2[1] == 0 &&
+ ecc_data2[2] == 0)
+ return 0;
+ }
+ pr_debug("UNCORRECTED_ERROR default\n");
+ return -EBADMSG;
+ }
+}
+
+/**
+ * omap_correct_data - Compares the ECC read with HW generated ECC
+ * @mtd: MTD device structure
+ * @dat: page data
+ * @read_ecc: ecc read from nand flash
+ * @calc_ecc: ecc read from HW ECC registers
+ *
+ * Compares the ecc read from nand spare area with ECC registers values
+ * and if ECC's mismatched, it will call 'omap_compare_ecc' for error
+ * detection and correction. If there are no errors, %0 is returned. If
+ * there were errors and all of the errors were corrected, the number of
+ * corrected errors is returned. If uncorrectable errors exist, %-1 is
+ * returned.
+ */
+static int omap_correct_data(struct mtd_info *mtd, u_char *dat,
+ u_char *read_ecc, u_char *calc_ecc)
+{
+ struct omap_nand_info *info = mtd_to_omap(mtd);
+ int blockCnt = 0, i = 0, ret = 0;
+ int stat = 0;
+
+ /* Ex NAND_ECC_HW12_2048 */
+ if ((info->nand.ecc.mode == NAND_ECC_HW) &&
+ (info->nand.ecc.size == 2048))
+ blockCnt = 4;
+ else
+ blockCnt = 1;
+
+ for (i = 0; i < blockCnt; i++) {
+ if (memcmp(read_ecc, calc_ecc, 3) != 0) {
+ ret = omap_compare_ecc(read_ecc, calc_ecc, dat);
+ if (ret < 0)
+ return ret;
+ /* keep track of the number of corrected errors */
+ stat += ret;
+ }
+ read_ecc += 3;
+ calc_ecc += 3;
+ dat += 512;
+ }
+ return stat;
+}
+
+/**
+ * omap_calcuate_ecc - Generate non-inverted ECC bytes.
+ * @mtd: MTD device structure
+ * @dat: The pointer to data on which ecc is computed
+ * @ecc_code: The ecc_code buffer
+ *
+ * Using noninverted ECC can be considered ugly since writing a blank
+ * page ie. padding will clear the ECC bytes. This is no problem as long
+ * nobody is trying to write data on the seemingly unused page. Reading
+ * an erased page will produce an ECC mismatch between generated and read
+ * ECC bytes that has to be dealt with separately.
+ */
+static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
+ u_char *ecc_code)
+{
+ struct omap_nand_info *info = mtd_to_omap(mtd);
+ u32 val;
+
+ val = readl(info->reg.gpmc_ecc_config);
+ if (((val >> ECC_CONFIG_CS_SHIFT) & CS_MASK) != info->gpmc_cs)
+ return -EINVAL;
+
+ /* read ecc result */
+ val = readl(info->reg.gpmc_ecc1_result);
+ *ecc_code++ = val; /* P128e, ..., P1e */
+ *ecc_code++ = val >> 16; /* P128o, ..., P1o */
+ /* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */
+ *ecc_code++ = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0);
+
+ return 0;
+}
+
+/**
+ * omap_enable_hwecc - This function enables the hardware ecc functionality
+ * @mtd: MTD device structure
+ * @mode: Read/Write mode
+ */
+static void omap_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+ struct omap_nand_info *info = mtd_to_omap(mtd);
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ unsigned int dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0;
+ u32 val;
+
+ /* clear ecc and enable bits */
+ val = ECCCLEAR | ECC1;
+ writel(val, info->reg.gpmc_ecc_control);
+
+ /* program ecc and result sizes */
+ val = ((((info->nand.ecc.size >> 1) - 1) << ECCSIZE1_SHIFT) |
+ ECC1RESULTSIZE);
+ writel(val, info->reg.gpmc_ecc_size_config);
+
+ switch (mode) {
+ case NAND_ECC_READ:
+ case NAND_ECC_WRITE:
+ writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control);
+ break;
+ case NAND_ECC_READSYN:
+ writel(ECCCLEAR, info->reg.gpmc_ecc_control);
+ break;
+ default:
+ dev_info(&info->pdev->dev,
+ "error: unrecognized Mode[%d]!\n", mode);
+ break;
+ }
+
+ /* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */
+ val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1);
+ writel(val, info->reg.gpmc_ecc_config);
+}
+
+/**
+ * omap_wait - wait until the command is done
+ * @mtd: MTD device structure
+ * @chip: NAND Chip structure
+ *
+ * Wait function is called during Program and erase operations and
+ * the way it is called from MTD layer, we should wait till the NAND
+ * chip is ready after the programming/erase operation has completed.
+ *
+ * Erase can take up to 400ms and program up to 20ms according to
+ * general NAND and SmartMedia specs
+ */
+static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct omap_nand_info *info = mtd_to_omap(mtd);
+ unsigned long timeo = jiffies;
+ int status, state = this->state;
+
+ if (state == FL_ERASING)
+ timeo += msecs_to_jiffies(400);
+ else
+ timeo += msecs_to_jiffies(20);
+
+ writeb(NAND_CMD_STATUS & 0xFF, info->reg.gpmc_nand_command);
+ while (time_before(jiffies, timeo)) {
+ status = readb(info->reg.gpmc_nand_data);
+ if (status & NAND_STATUS_READY)
+ break;
+ cond_resched();
+ }
+
+ status = readb(info->reg.gpmc_nand_data);
+ return status;
+}
+
+/**
+ * omap_dev_ready - checks the NAND Ready GPIO line
+ * @mtd: MTD device structure
+ *
+ * Returns true if ready and false if busy.
+ */
+static int omap_dev_ready(struct mtd_info *mtd)
+{
+ struct omap_nand_info *info = mtd_to_omap(mtd);
+
+ return gpiod_get_value(info->ready_gpiod);
+}
+
+/**
+ * omap_enable_hwecc_bch - Program GPMC to perform BCH ECC calculation
+ * @mtd: MTD device structure
+ * @mode: Read/Write mode
+ *
+ * When using BCH with SW correction (i.e. no ELM), sector size is set
+ * to 512 bytes and we use BCH_WRAPMODE_6 wrapping mode
+ * for both reading and writing with:
+ * eccsize0 = 0 (no additional protected byte in spare area)
+ * eccsize1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area)
+ */
+static void __maybe_unused omap_enable_hwecc_bch(struct mtd_info *mtd, int mode)
+{
+ unsigned int bch_type;
+ unsigned int dev_width, nsectors;
+ struct omap_nand_info *info = mtd_to_omap(mtd);
+ enum omap_ecc ecc_opt = info->ecc_opt;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ u32 val, wr_mode;
+ unsigned int ecc_size1, ecc_size0;
+
+ /* GPMC configurations for calculating ECC */
+ switch (ecc_opt) {
+ case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
+ bch_type = 0;
+ nsectors = 1;
+ wr_mode = BCH_WRAPMODE_6;
+ ecc_size0 = BCH_ECC_SIZE0;
+ ecc_size1 = BCH_ECC_SIZE1;
+ break;
+ case OMAP_ECC_BCH4_CODE_HW:
+ bch_type = 0;
+ nsectors = chip->ecc.steps;
+ if (mode == NAND_ECC_READ) {
+ wr_mode = BCH_WRAPMODE_1;
+ ecc_size0 = BCH4R_ECC_SIZE0;
+ ecc_size1 = BCH4R_ECC_SIZE1;
+ } else {
+ wr_mode = BCH_WRAPMODE_6;
+ ecc_size0 = BCH_ECC_SIZE0;
+ ecc_size1 = BCH_ECC_SIZE1;
+ }
+ break;
+ case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+ bch_type = 1;
+ nsectors = 1;
+ wr_mode = BCH_WRAPMODE_6;
+ ecc_size0 = BCH_ECC_SIZE0;
+ ecc_size1 = BCH_ECC_SIZE1;
+ break;
+ case OMAP_ECC_BCH8_CODE_HW:
+ bch_type = 1;
+ nsectors = chip->ecc.steps;
+ if (mode == NAND_ECC_READ) {
+ wr_mode = BCH_WRAPMODE_1;
+ ecc_size0 = BCH8R_ECC_SIZE0;
+ ecc_size1 = BCH8R_ECC_SIZE1;
+ } else {
+ wr_mode = BCH_WRAPMODE_6;
+ ecc_size0 = BCH_ECC_SIZE0;
+ ecc_size1 = BCH_ECC_SIZE1;
+ }
+ break;
+ case OMAP_ECC_BCH16_CODE_HW:
+ bch_type = 0x2;
+ nsectors = chip->ecc.steps;
+ if (mode == NAND_ECC_READ) {
+ wr_mode = 0x01;
+ ecc_size0 = 52; /* ECC bits in nibbles per sector */
+ ecc_size1 = 0; /* non-ECC bits in nibbles per sector */
+ } else {
+ wr_mode = 0x01;
+ ecc_size0 = 0; /* extra bits in nibbles per sector */
+ ecc_size1 = 52; /* OOB bits in nibbles per sector */
+ }
+ break;
+ default:
+ return;
+ }
+
+ writel(ECC1, info->reg.gpmc_ecc_control);
+
+ /* Configure ecc size for BCH */
+ val = (ecc_size1 << ECCSIZE1_SHIFT) | (ecc_size0 << ECCSIZE0_SHIFT);
+ writel(val, info->reg.gpmc_ecc_size_config);
+
+ dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0;
+
+ /* BCH configuration */
+ val = ((1 << 16) | /* enable BCH */
+ (bch_type << 12) | /* BCH4/BCH8/BCH16 */
+ (wr_mode << 8) | /* wrap mode */
+ (dev_width << 7) | /* bus width */
+ (((nsectors-1) & 0x7) << 4) | /* number of sectors */
+ (info->gpmc_cs << 1) | /* ECC CS */
+ (0x1)); /* enable ECC */
+
+ writel(val, info->reg.gpmc_ecc_config);
+
+ /* Clear ecc and enable bits */
+ writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control);
+}
+
+static u8 bch4_polynomial[] = {0x28, 0x13, 0xcc, 0x39, 0x96, 0xac, 0x7f};
+static u8 bch8_polynomial[] = {0xef, 0x51, 0x2e, 0x09, 0xed, 0x93, 0x9a, 0xc2,
+ 0x97, 0x79, 0xe5, 0x24, 0xb5};
+
+/**
+ * _omap_calculate_ecc_bch - Generate ECC bytes for one sector
+ * @mtd: MTD device structure
+ * @dat: The pointer to data on which ecc is computed
+ * @ecc_code: The ecc_code buffer
+ * @i: The sector number (for a multi sector page)
+ *
+ * Support calculating of BCH4/8/16 ECC vectors for one sector
+ * within a page. Sector number is in @i.
+ */
+static int _omap_calculate_ecc_bch(struct mtd_info *mtd,
+ const u_char *dat, u_char *ecc_calc, int i)
+{
+ struct omap_nand_info *info = mtd_to_omap(mtd);
+ int eccbytes = info->nand.ecc.bytes;
+ struct gpmc_nand_regs *gpmc_regs = &info->reg;
+ u8 *ecc_code;
+ unsigned long bch_val1, bch_val2, bch_val3, bch_val4;
+ u32 val;
+ int j;
+
+ ecc_code = ecc_calc;
+ switch (info->ecc_opt) {
+ case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+ case OMAP_ECC_BCH8_CODE_HW:
+ bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]);
+ bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]);
+ bch_val3 = readl(gpmc_regs->gpmc_bch_result2[i]);
+ bch_val4 = readl(gpmc_regs->gpmc_bch_result3[i]);
+ *ecc_code++ = (bch_val4 & 0xFF);
+ *ecc_code++ = ((bch_val3 >> 24) & 0xFF);
+ *ecc_code++ = ((bch_val3 >> 16) & 0xFF);
+ *ecc_code++ = ((bch_val3 >> 8) & 0xFF);
+ *ecc_code++ = (bch_val3 & 0xFF);
+ *ecc_code++ = ((bch_val2 >> 24) & 0xFF);
+ *ecc_code++ = ((bch_val2 >> 16) & 0xFF);
+ *ecc_code++ = ((bch_val2 >> 8) & 0xFF);
+ *ecc_code++ = (bch_val2 & 0xFF);
+ *ecc_code++ = ((bch_val1 >> 24) & 0xFF);
+ *ecc_code++ = ((bch_val1 >> 16) & 0xFF);
+ *ecc_code++ = ((bch_val1 >> 8) & 0xFF);
+ *ecc_code++ = (bch_val1 & 0xFF);
+ break;
+ case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
+ case OMAP_ECC_BCH4_CODE_HW:
+ bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]);
+ bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]);
+ *ecc_code++ = ((bch_val2 >> 12) & 0xFF);
+ *ecc_code++ = ((bch_val2 >> 4) & 0xFF);
+ *ecc_code++ = ((bch_val2 & 0xF) << 4) |
+ ((bch_val1 >> 28) & 0xF);
+ *ecc_code++ = ((bch_val1 >> 20) & 0xFF);
+ *ecc_code++ = ((bch_val1 >> 12) & 0xFF);
+ *ecc_code++ = ((bch_val1 >> 4) & 0xFF);
+ *ecc_code++ = ((bch_val1 & 0xF) << 4);
+ break;
+ case OMAP_ECC_BCH16_CODE_HW:
+ val = readl(gpmc_regs->gpmc_bch_result6[i]);
+ ecc_code[0] = ((val >> 8) & 0xFF);
+ ecc_code[1] = ((val >> 0) & 0xFF);
+ val = readl(gpmc_regs->gpmc_bch_result5[i]);
+ ecc_code[2] = ((val >> 24) & 0xFF);
+ ecc_code[3] = ((val >> 16) & 0xFF);
+ ecc_code[4] = ((val >> 8) & 0xFF);
+ ecc_code[5] = ((val >> 0) & 0xFF);
+ val = readl(gpmc_regs->gpmc_bch_result4[i]);
+ ecc_code[6] = ((val >> 24) & 0xFF);
+ ecc_code[7] = ((val >> 16) & 0xFF);
+ ecc_code[8] = ((val >> 8) & 0xFF);
+ ecc_code[9] = ((val >> 0) & 0xFF);
+ val = readl(gpmc_regs->gpmc_bch_result3[i]);
+ ecc_code[10] = ((val >> 24) & 0xFF);
+ ecc_code[11] = ((val >> 16) & 0xFF);
+ ecc_code[12] = ((val >> 8) & 0xFF);
+ ecc_code[13] = ((val >> 0) & 0xFF);
+ val = readl(gpmc_regs->gpmc_bch_result2[i]);
+ ecc_code[14] = ((val >> 24) & 0xFF);
+ ecc_code[15] = ((val >> 16) & 0xFF);
+ ecc_code[16] = ((val >> 8) & 0xFF);
+ ecc_code[17] = ((val >> 0) & 0xFF);
+ val = readl(gpmc_regs->gpmc_bch_result1[i]);
+ ecc_code[18] = ((val >> 24) & 0xFF);
+ ecc_code[19] = ((val >> 16) & 0xFF);
+ ecc_code[20] = ((val >> 8) & 0xFF);
+ ecc_code[21] = ((val >> 0) & 0xFF);
+ val = readl(gpmc_regs->gpmc_bch_result0[i]);
+ ecc_code[22] = ((val >> 24) & 0xFF);
+ ecc_code[23] = ((val >> 16) & 0xFF);
+ ecc_code[24] = ((val >> 8) & 0xFF);
+ ecc_code[25] = ((val >> 0) & 0xFF);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* ECC scheme specific syndrome customizations */
+ switch (info->ecc_opt) {
+ case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
+ /* Add constant polynomial to remainder, so that
+ * ECC of blank pages results in 0x0 on reading back
+ */
+ for (j = 0; j < eccbytes; j++)
+ ecc_calc[j] ^= bch4_polynomial[j];
+ break;
+ case OMAP_ECC_BCH4_CODE_HW:
+ /* Set 8th ECC byte as 0x0 for ROM compatibility */
+ ecc_calc[eccbytes - 1] = 0x0;
+ break;
+ case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+ /* Add constant polynomial to remainder, so that
+ * ECC of blank pages results in 0x0 on reading back
+ */
+ for (j = 0; j < eccbytes; j++)
+ ecc_calc[j] ^= bch8_polynomial[j];
+ break;
+ case OMAP_ECC_BCH8_CODE_HW:
+ /* Set 14th ECC byte as 0x0 for ROM compatibility */
+ ecc_calc[eccbytes - 1] = 0x0;
+ break;
+ case OMAP_ECC_BCH16_CODE_HW:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * omap_calculate_ecc_bch_sw - ECC generator for sector for SW based correction
+ * @mtd: MTD device structure
+ * @dat: The pointer to data on which ecc is computed
+ * @ecc_code: The ecc_code buffer
+ *
+ * Support calculating of BCH4/8/16 ECC vectors for one sector. This is used
+ * when SW based correction is required as ECC is required for one sector
+ * at a time.
+ */
+static int omap_calculate_ecc_bch_sw(struct mtd_info *mtd,
+ const u_char *dat, u_char *ecc_calc)
+{
+ return _omap_calculate_ecc_bch(mtd, dat, ecc_calc, 0);
+}
+
+/**
+ * omap_calculate_ecc_bch_multi - Generate ECC for multiple sectors
+ * @mtd: MTD device structure
+ * @dat: The pointer to data on which ecc is computed
+ * @ecc_code: The ecc_code buffer
+ *
+ * Support calculating of BCH4/8/16 ecc vectors for the entire page in one go.
+ */
+static int omap_calculate_ecc_bch_multi(struct mtd_info *mtd,
+ const u_char *dat, u_char *ecc_calc)
+{
+ struct omap_nand_info *info = mtd_to_omap(mtd);
+ int eccbytes = info->nand.ecc.bytes;
+ unsigned long nsectors;
+ int i, ret;
+
+ nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1;
+ for (i = 0; i < nsectors; i++) {
+ ret = _omap_calculate_ecc_bch(mtd, dat, ecc_calc, i);
+ if (ret)
+ return ret;
+
+ ecc_calc += eccbytes;
+ }
+
+ return 0;
+}
+
+/**
+ * erased_sector_bitflips - count bit flips
+ * @data: data sector buffer
+ * @oob: oob buffer
+ * @info: omap_nand_info
+ *
+ * Check the bit flips in erased page falls below correctable level.
+ * If falls below, report the page as erased with correctable bit
+ * flip, else report as uncorrectable page.
+ */
+static int erased_sector_bitflips(u_char *data, u_char *oob,
+ struct omap_nand_info *info)
+{
+ int flip_bits = 0, i;
+
+ for (i = 0; i < info->nand.ecc.size; i++) {
+ flip_bits += hweight8(~data[i]);
+ if (flip_bits > info->nand.ecc.strength)
+ return 0;
+ }
+
+ for (i = 0; i < info->nand.ecc.bytes - 1; i++) {
+ flip_bits += hweight8(~oob[i]);
+ if (flip_bits > info->nand.ecc.strength)
+ return 0;
+ }
+
+ /*
+ * Bit flips falls in correctable level.
+ * Fill data area with 0xFF
+ */
+ if (flip_bits) {
+ memset(data, 0xFF, info->nand.ecc.size);
+ memset(oob, 0xFF, info->nand.ecc.bytes);
+ }
+
+ return flip_bits;
+}
+
+/**
+ * omap_elm_correct_data - corrects page data area in case error reported
+ * @mtd: MTD device structure
+ * @data: page data
+ * @read_ecc: ecc read from nand flash
+ * @calc_ecc: ecc read from HW ECC registers
+ *
+ * Calculated ecc vector reported as zero in case of non-error pages.
+ * In case of non-zero ecc vector, first filter out erased-pages, and
+ * then process data via ELM to detect bit-flips.
+ */
+static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
+ u_char *read_ecc, u_char *calc_ecc)
+{
+ struct omap_nand_info *info = mtd_to_omap(mtd);
+ struct nand_ecc_ctrl *ecc = &info->nand.ecc;
+ int eccsteps = info->nand.ecc.steps;
+ int i , j, stat = 0;
+ int eccflag, actual_eccbytes;
+ struct elm_errorvec err_vec[ERROR_VECTOR_MAX];
+ u_char *ecc_vec = calc_ecc;
+ u_char *spare_ecc = read_ecc;
+ u_char *erased_ecc_vec;
+ u_char *buf;
+ int bitflip_count;
+ bool is_error_reported = false;
+ u32 bit_pos, byte_pos, error_max, pos;
+ int err;
+
+ switch (info->ecc_opt) {
+ case OMAP_ECC_BCH4_CODE_HW:
+ /* omit 7th ECC byte reserved for ROM code compatibility */
+ actual_eccbytes = ecc->bytes - 1;
+ erased_ecc_vec = bch4_vector;
+ break;
+ case OMAP_ECC_BCH8_CODE_HW:
+ /* omit 14th ECC byte reserved for ROM code compatibility */
+ actual_eccbytes = ecc->bytes - 1;
+ erased_ecc_vec = bch8_vector;
+ break;
+ case OMAP_ECC_BCH16_CODE_HW:
+ actual_eccbytes = ecc->bytes;
+ erased_ecc_vec = bch16_vector;
+ break;
+ default:
+ dev_err(&info->pdev->dev, "invalid driver configuration\n");
+ return -EINVAL;
+ }
+
+ /* Initialize elm error vector to zero */
+ memset(err_vec, 0, sizeof(err_vec));
+
+ for (i = 0; i < eccsteps ; i++) {
+ eccflag = 0; /* initialize eccflag */
+
+ /*
+ * Check any error reported,
+ * In case of error, non zero ecc reported.
+ */
+ for (j = 0; j < actual_eccbytes; j++) {
+ if (calc_ecc[j] != 0) {
+ eccflag = 1; /* non zero ecc, error present */
+ break;
+ }
+ }
+
+ if (eccflag == 1) {
+ if (memcmp(calc_ecc, erased_ecc_vec,
+ actual_eccbytes) == 0) {
+ /*
+ * calc_ecc[] matches pattern for ECC(all 0xff)
+ * so this is definitely an erased-page
+ */
+ } else {
+ buf = &data[info->nand.ecc.size * i];
+ /*
+ * count number of 0-bits in read_buf.
+ * This check can be removed once a similar
+ * check is introduced in generic NAND driver
+ */
+ bitflip_count = erased_sector_bitflips(
+ buf, read_ecc, info);
+ if (bitflip_count) {
+ /*
+ * number of 0-bits within ECC limits
+ * So this may be an erased-page
+ */
+ stat += bitflip_count;
+ } else {
+ /*
+ * Too many 0-bits. It may be a
+ * - programmed-page, OR
+ * - erased-page with many bit-flips
+ * So this page requires check by ELM
+ */
+ err_vec[i].error_reported = true;
+ is_error_reported = true;
+ }
+ }
+ }
+
+ /* Update the ecc vector */
+ calc_ecc += ecc->bytes;
+ read_ecc += ecc->bytes;
+ }
+
+ /* Check if any error reported */
+ if (!is_error_reported)
+ return stat;
+
+ /* Decode BCH error using ELM module */
+ elm_decode_bch_error_page(info->elm_dev, ecc_vec, err_vec);
+
+ err = 0;
+ for (i = 0; i < eccsteps; i++) {
+ if (err_vec[i].error_uncorrectable) {
+ dev_err(&info->pdev->dev,
+ "uncorrectable bit-flips found\n");
+ err = -EBADMSG;
+ } else if (err_vec[i].error_reported) {
+ for (j = 0; j < err_vec[i].error_count; j++) {
+ switch (info->ecc_opt) {
+ case OMAP_ECC_BCH4_CODE_HW:
+ /* Add 4 bits to take care of padding */
+ pos = err_vec[i].error_loc[j] +
+ BCH4_BIT_PAD;
+ break;
+ case OMAP_ECC_BCH8_CODE_HW:
+ case OMAP_ECC_BCH16_CODE_HW:
+ pos = err_vec[i].error_loc[j];
+ break;
+ default:
+ return -EINVAL;
+ }
+ error_max = (ecc->size + actual_eccbytes) * 8;
+ /* Calculate bit position of error */
+ bit_pos = pos % 8;
+
+ /* Calculate byte position of error */
+ byte_pos = (error_max - pos - 1) / 8;
+
+ if (pos < error_max) {
+ if (byte_pos < 512) {
+ pr_debug("bitflip@dat[%d]=%x\n",
+ byte_pos, data[byte_pos]);
+ data[byte_pos] ^= 1 << bit_pos;
+ } else {
+ pr_debug("bitflip@oob[%d]=%x\n",
+ (byte_pos - 512),
+ spare_ecc[byte_pos - 512]);
+ spare_ecc[byte_pos - 512] ^=
+ 1 << bit_pos;
+ }
+ } else {
+ dev_err(&info->pdev->dev,
+ "invalid bit-flip @ %d:%d\n",
+ byte_pos, bit_pos);
+ err = -EBADMSG;
+ }
+ }
+ }
+
+ /* Update number of correctable errors */
+ stat += err_vec[i].error_count;
+
+ /* Update page data with sector size */
+ data += ecc->size;
+ spare_ecc += ecc->bytes;
+ }
+
+ return (err) ? err : stat;
+}
+
+/**
+ * omap_write_page_bch - BCH ecc based write page function for entire page
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ * @oob_required: must write chip->oob_poi to OOB
+ * @page: page
+ *
+ * Custom write page method evolved to support multi sector writing in one shot
+ */
+static int omap_write_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required, int page)
+{
+ int ret;
+ uint8_t *ecc_calc = chip->ecc.calc_buf;
+
+ nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+
+ /* Enable GPMC ecc engine */
+ chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
+
+ /* Write data */
+ chip->write_buf(mtd, buf, mtd->writesize);
+
+ /* Update ecc vector from GPMC result registers */
+ omap_calculate_ecc_bch_multi(mtd, buf, &ecc_calc[0]);
+
+ ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
+ chip->ecc.total);
+ if (ret)
+ return ret;
+
+ /* Write ecc vector to OOB area */
+ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ return nand_prog_page_end_op(chip);
+}
+
+/**
+ * omap_write_subpage_bch - BCH hardware ECC based subpage write
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @offset: column address of subpage within the page
+ * @data_len: data length
+ * @buf: data buffer
+ * @oob_required: must write chip->oob_poi to OOB
+ * @page: page number to write
+ *
+ * OMAP optimized subpage write method.
+ */
+static int omap_write_subpage_bch(struct mtd_info *mtd,
+ struct nand_chip *chip, u32 offset,
+ u32 data_len, const u8 *buf,
+ int oob_required, int page)
+{
+ u8 *ecc_calc = chip->ecc.calc_buf;
+ int ecc_size = chip->ecc.size;
+ int ecc_bytes = chip->ecc.bytes;
+ int ecc_steps = chip->ecc.steps;
+ u32 start_step = offset / ecc_size;
+ u32 end_step = (offset + data_len - 1) / ecc_size;
+ int step, ret = 0;
+
+ /*
+ * Write entire page at one go as it would be optimal
+ * as ECC is calculated by hardware.
+ * ECC is calculated for all subpages but we choose
+ * only what we want.
+ */
+ nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+
+ /* Enable GPMC ECC engine */
+ chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
+
+ /* Write data */
+ chip->write_buf(mtd, buf, mtd->writesize);
+
+ for (step = 0; step < ecc_steps; step++) {
+ /* mask ECC of un-touched subpages by padding 0xFF */
+ if (step < start_step || step > end_step)
+ memset(ecc_calc, 0xff, ecc_bytes);
+ else
+ ret = _omap_calculate_ecc_bch(mtd, buf, ecc_calc, step);
+
+ if (ret)
+ return ret;
+
+ buf += ecc_size;
+ ecc_calc += ecc_bytes;
+ }
+
+ /* copy calculated ECC for whole page to chip->buffer->oob */
+ /* this include masked-value(0xFF) for unwritten subpages */
+ ecc_calc = chip->ecc.calc_buf;
+ ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
+ chip->ecc.total);
+ if (ret)
+ return ret;
+
+ /* write OOB buffer to NAND device */
+ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ return nand_prog_page_end_op(chip);
+}
+
+/**
+ * omap_read_page_bch - BCH ecc based page read function for entire page
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
+ *
+ * For BCH ecc scheme, GPMC used for syndrome calculation and ELM module
+ * used for error correction.
+ * Custom method evolved to support ELM error correction & multi sector
+ * reading. On reading page data area is read along with OOB data with
+ * ecc engine enabled. ecc vector updated after read of OOB data.
+ * For non error pages ecc vector reported as zero.
+ */
+static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ uint8_t *ecc_calc = chip->ecc.calc_buf;
+ uint8_t *ecc_code = chip->ecc.code_buf;
+ int stat, ret;
+ unsigned int max_bitflips = 0;
+
+ nand_read_page_op(chip, page, 0, NULL, 0);
+
+ /* Enable GPMC ecc engine */
+ chip->ecc.hwctl(mtd, NAND_ECC_READ);
+
+ /* Read data */
+ chip->read_buf(mtd, buf, mtd->writesize);
+
+ /* Read oob bytes */
+ nand_change_read_column_op(chip,
+ mtd->writesize + BADBLOCK_MARKER_LENGTH,
+ chip->oob_poi + BADBLOCK_MARKER_LENGTH,
+ chip->ecc.total, false);
+
+ /* Calculate ecc bytes */
+ omap_calculate_ecc_bch_multi(mtd, buf, ecc_calc);
+
+ ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
+ chip->ecc.total);
+ if (ret)
+ return ret;
+
+ stat = chip->ecc.correct(mtd, buf, ecc_code, ecc_calc);
+
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ }
+
+ return max_bitflips;
+}
+
+/**
+ * is_elm_present - checks for presence of ELM module by scanning DT nodes
+ * @omap_nand_info: NAND device structure containing platform data
+ */
+static bool is_elm_present(struct omap_nand_info *info,
+ struct device_node *elm_node)
+{
+ struct platform_device *pdev;
+
+ /* check whether elm-id is passed via DT */
+ if (!elm_node) {
+ dev_err(&info->pdev->dev, "ELM devicetree node not found\n");
+ return false;
+ }
+ pdev = of_find_device_by_node(elm_node);
+ /* check whether ELM device is registered */
+ if (!pdev) {
+ dev_err(&info->pdev->dev, "ELM device not found\n");
+ return false;
+ }
+ /* ELM module available, now configure it */
+ info->elm_dev = &pdev->dev;
+ return true;
+}
+
+static bool omap2_nand_ecc_check(struct omap_nand_info *info)
+{
+ bool ecc_needs_bch, ecc_needs_omap_bch, ecc_needs_elm;
+
+ switch (info->ecc_opt) {
+ case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
+ case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+ ecc_needs_omap_bch = false;
+ ecc_needs_bch = true;
+ ecc_needs_elm = false;
+ break;
+ case OMAP_ECC_BCH4_CODE_HW:
+ case OMAP_ECC_BCH8_CODE_HW:
+ case OMAP_ECC_BCH16_CODE_HW:
+ ecc_needs_omap_bch = true;
+ ecc_needs_bch = false;
+ ecc_needs_elm = true;
+ break;
+ default:
+ ecc_needs_omap_bch = false;
+ ecc_needs_bch = false;
+ ecc_needs_elm = false;
+ break;
+ }
+
+ if (ecc_needs_bch && !IS_ENABLED(CONFIG_MTD_NAND_ECC_BCH)) {
+ dev_err(&info->pdev->dev,
+ "CONFIG_MTD_NAND_ECC_BCH not enabled\n");
+ return false;
+ }
+ if (ecc_needs_omap_bch && !IS_ENABLED(CONFIG_MTD_NAND_OMAP_BCH)) {
+ dev_err(&info->pdev->dev,
+ "CONFIG_MTD_NAND_OMAP_BCH not enabled\n");
+ return false;
+ }
+ if (ecc_needs_elm && !is_elm_present(info, info->elm_of_node)) {
+ dev_err(&info->pdev->dev, "ELM not available\n");
+ return false;
+ }
+
+ return true;
+}
+
+static const char * const nand_xfer_types[] = {
+ [NAND_OMAP_PREFETCH_POLLED] = "prefetch-polled",
+ [NAND_OMAP_POLLED] = "polled",
+ [NAND_OMAP_PREFETCH_DMA] = "prefetch-dma",
+ [NAND_OMAP_PREFETCH_IRQ] = "prefetch-irq",
+};
+
+static int omap_get_dt_info(struct device *dev, struct omap_nand_info *info)
+{
+ struct device_node *child = dev->of_node;
+ int i;
+ const char *s;
+ u32 cs;
+
+ if (of_property_read_u32(child, "reg", &cs) < 0) {
+ dev_err(dev, "reg not found in DT\n");
+ return -EINVAL;
+ }
+
+ info->gpmc_cs = cs;
+
+ /* detect availability of ELM module. Won't be present pre-OMAP4 */
+ info->elm_of_node = of_parse_phandle(child, "ti,elm-id", 0);
+ if (!info->elm_of_node) {
+ info->elm_of_node = of_parse_phandle(child, "elm_id", 0);
+ if (!info->elm_of_node)
+ dev_dbg(dev, "ti,elm-id not in DT\n");
+ }
+
+ /* select ecc-scheme for NAND */
+ if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) {
+ dev_err(dev, "ti,nand-ecc-opt not found\n");
+ return -EINVAL;
+ }
+
+ if (!strcmp(s, "sw")) {
+ info->ecc_opt = OMAP_ECC_HAM1_CODE_SW;
+ } else if (!strcmp(s, "ham1") ||
+ !strcmp(s, "hw") || !strcmp(s, "hw-romcode")) {
+ info->ecc_opt = OMAP_ECC_HAM1_CODE_HW;
+ } else if (!strcmp(s, "bch4")) {
+ if (info->elm_of_node)
+ info->ecc_opt = OMAP_ECC_BCH4_CODE_HW;
+ else
+ info->ecc_opt = OMAP_ECC_BCH4_CODE_HW_DETECTION_SW;
+ } else if (!strcmp(s, "bch8")) {
+ if (info->elm_of_node)
+ info->ecc_opt = OMAP_ECC_BCH8_CODE_HW;
+ else
+ info->ecc_opt = OMAP_ECC_BCH8_CODE_HW_DETECTION_SW;
+ } else if (!strcmp(s, "bch16")) {
+ info->ecc_opt = OMAP_ECC_BCH16_CODE_HW;
+ } else {
+ dev_err(dev, "unrecognized value for ti,nand-ecc-opt\n");
+ return -EINVAL;
+ }
+
+ /* select data transfer mode */
+ if (!of_property_read_string(child, "ti,nand-xfer-type", &s)) {
+ for (i = 0; i < ARRAY_SIZE(nand_xfer_types); i++) {
+ if (!strcasecmp(s, nand_xfer_types[i])) {
+ info->xfer_type = i;
+ return 0;
+ }
+ }
+
+ dev_err(dev, "unrecognized value for ti,nand-xfer-type\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int omap_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct omap_nand_info *info = mtd_to_omap(mtd);
+ struct nand_chip *chip = &info->nand;
+ int off = BADBLOCK_MARKER_LENGTH;
+
+ if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW &&
+ !(chip->options & NAND_BUSWIDTH_16))
+ off = 1;
+
+ if (section)
+ return -ERANGE;
+
+ oobregion->offset = off;
+ oobregion->length = chip->ecc.total;
+
+ return 0;
+}
+
+static int omap_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct omap_nand_info *info = mtd_to_omap(mtd);
+ struct nand_chip *chip = &info->nand;
+ int off = BADBLOCK_MARKER_LENGTH;
+
+ if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW &&
+ !(chip->options & NAND_BUSWIDTH_16))
+ off = 1;
+
+ if (section)
+ return -ERANGE;
+
+ off += chip->ecc.total;
+ if (off >= mtd->oobsize)
+ return -ERANGE;
+
+ oobregion->offset = off;
+ oobregion->length = mtd->oobsize - off;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops omap_ooblayout_ops = {
+ .ecc = omap_ooblayout_ecc,
+ .free = omap_ooblayout_free,
+};
+
+static int omap_sw_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int off = BADBLOCK_MARKER_LENGTH;
+
+ if (section >= chip->ecc.steps)
+ return -ERANGE;
+
+ /*
+ * When SW correction is employed, one OMAP specific marker byte is
+ * reserved after each ECC step.
+ */
+ oobregion->offset = off + (section * (chip->ecc.bytes + 1));
+ oobregion->length = chip->ecc.bytes;
+
+ return 0;
+}
+
+static int omap_sw_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int off = BADBLOCK_MARKER_LENGTH;
+
+ if (section)
+ return -ERANGE;
+
+ /*
+ * When SW correction is employed, one OMAP specific marker byte is
+ * reserved after each ECC step.
+ */
+ off += ((chip->ecc.bytes + 1) * chip->ecc.steps);
+ if (off >= mtd->oobsize)
+ return -ERANGE;
+
+ oobregion->offset = off;
+ oobregion->length = mtd->oobsize - off;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops omap_sw_ooblayout_ops = {
+ .ecc = omap_sw_ooblayout_ecc,
+ .free = omap_sw_ooblayout_free,
+};
+
+static int omap_nand_probe(struct platform_device *pdev)
+{
+ struct omap_nand_info *info;
+ struct mtd_info *mtd;
+ struct nand_chip *nand_chip;
+ int err;
+ dma_cap_mask_t mask;
+ struct resource *res;
+ struct device *dev = &pdev->dev;
+ int min_oobbytes = BADBLOCK_MARKER_LENGTH;
+ int oobbytes_per_step;
+
+ info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info),
+ GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->pdev = pdev;
+
+ err = omap_get_dt_info(dev, info);
+ if (err)
+ return err;
+
+ info->ops = gpmc_omap_get_nand_ops(&info->reg, info->gpmc_cs);
+ if (!info->ops) {
+ dev_err(&pdev->dev, "Failed to get GPMC->NAND interface\n");
+ return -ENODEV;
+ }
+
+ nand_chip = &info->nand;
+ mtd = nand_to_mtd(nand_chip);
+ mtd->dev.parent = &pdev->dev;
+ nand_chip->ecc.priv = NULL;
+ nand_set_flash_node(nand_chip, dev->of_node);
+
+ if (!mtd->name) {
+ mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
+ "omap2-nand.%d", info->gpmc_cs);
+ if (!mtd->name) {
+ dev_err(&pdev->dev, "Failed to set MTD name\n");
+ return -ENOMEM;
+ }
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(nand_chip->IO_ADDR_R))
+ return PTR_ERR(nand_chip->IO_ADDR_R);
+
+ info->phys_base = res->start;
+
+ nand_chip->controller = &omap_gpmc_controller;
+
+ nand_chip->IO_ADDR_W = nand_chip->IO_ADDR_R;
+ nand_chip->cmd_ctrl = omap_hwcontrol;
+
+ info->ready_gpiod = devm_gpiod_get_optional(&pdev->dev, "rb",
+ GPIOD_IN);
+ if (IS_ERR(info->ready_gpiod)) {
+ dev_err(dev, "failed to get ready gpio\n");
+ return PTR_ERR(info->ready_gpiod);
+ }
+
+ /*
+ * If RDY/BSY line is connected to OMAP then use the omap ready
+ * function and the generic nand_wait function which reads the status
+ * register after monitoring the RDY/BSY line. Otherwise use a standard
+ * chip delay which is slightly more than tR (AC Timing) of the NAND
+ * device and read status register until you get a failure or success
+ */
+ if (info->ready_gpiod) {
+ nand_chip->dev_ready = omap_dev_ready;
+ nand_chip->chip_delay = 0;
+ } else {
+ nand_chip->waitfunc = omap_wait;
+ nand_chip->chip_delay = 50;
+ }
+
+ if (info->flash_bbt)
+ nand_chip->bbt_options |= NAND_BBT_USE_FLASH;
+
+ /* scan NAND device connected to chip controller */
+ nand_chip->options |= info->devsize & NAND_BUSWIDTH_16;
+ err = nand_scan_ident(mtd, 1, NULL);
+ if (err) {
+ dev_err(&info->pdev->dev,
+ "scan failed, may be bus-width mismatch\n");
+ goto return_error;
+ }
+
+ if (nand_chip->bbt_options & NAND_BBT_USE_FLASH)
+ nand_chip->bbt_options |= NAND_BBT_NO_OOB;
+ else
+ nand_chip->options |= NAND_SKIP_BBTSCAN;
+
+ /* re-populate low-level callbacks based on xfer modes */
+ switch (info->xfer_type) {
+ case NAND_OMAP_PREFETCH_POLLED:
+ nand_chip->read_buf = omap_read_buf_pref;
+ nand_chip->write_buf = omap_write_buf_pref;
+ break;
+
+ case NAND_OMAP_POLLED:
+ /* Use nand_base defaults for {read,write}_buf */
+ break;
+
+ case NAND_OMAP_PREFETCH_DMA:
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ info->dma = dma_request_chan(pdev->dev.parent, "rxtx");
+
+ if (IS_ERR(info->dma)) {
+ dev_err(&pdev->dev, "DMA engine request failed\n");
+ err = PTR_ERR(info->dma);
+ goto return_error;
+ } else {
+ struct dma_slave_config cfg;
+
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.src_addr = info->phys_base;
+ cfg.dst_addr = info->phys_base;
+ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ cfg.src_maxburst = 16;
+ cfg.dst_maxburst = 16;
+ err = dmaengine_slave_config(info->dma, &cfg);
+ if (err) {
+ dev_err(&pdev->dev, "DMA engine slave config failed: %d\n",
+ err);
+ goto return_error;
+ }
+ nand_chip->read_buf = omap_read_buf_dma_pref;
+ nand_chip->write_buf = omap_write_buf_dma_pref;
+ }
+ break;
+
+ case NAND_OMAP_PREFETCH_IRQ:
+ info->gpmc_irq_fifo = platform_get_irq(pdev, 0);
+ if (info->gpmc_irq_fifo <= 0) {
+ dev_err(&pdev->dev, "error getting fifo irq\n");
+ err = -ENODEV;
+ goto return_error;
+ }
+ err = devm_request_irq(&pdev->dev, info->gpmc_irq_fifo,
+ omap_nand_irq, IRQF_SHARED,
+ "gpmc-nand-fifo", info);
+ if (err) {
+ dev_err(&pdev->dev, "requesting irq(%d) error:%d",
+ info->gpmc_irq_fifo, err);
+ info->gpmc_irq_fifo = 0;
+ goto return_error;
+ }
+
+ info->gpmc_irq_count = platform_get_irq(pdev, 1);
+ if (info->gpmc_irq_count <= 0) {
+ dev_err(&pdev->dev, "error getting count irq\n");
+ err = -ENODEV;
+ goto return_error;
+ }
+ err = devm_request_irq(&pdev->dev, info->gpmc_irq_count,
+ omap_nand_irq, IRQF_SHARED,
+ "gpmc-nand-count", info);
+ if (err) {
+ dev_err(&pdev->dev, "requesting irq(%d) error:%d",
+ info->gpmc_irq_count, err);
+ info->gpmc_irq_count = 0;
+ goto return_error;
+ }
+
+ nand_chip->read_buf = omap_read_buf_irq_pref;
+ nand_chip->write_buf = omap_write_buf_irq_pref;
+
+ break;
+
+ default:
+ dev_err(&pdev->dev,
+ "xfer_type(%d) not supported!\n", info->xfer_type);
+ err = -EINVAL;
+ goto return_error;
+ }
+
+ if (!omap2_nand_ecc_check(info)) {
+ err = -EINVAL;
+ goto return_error;
+ }
+
+ /*
+ * Bail out earlier to let NAND_ECC_SOFT code create its own
+ * ooblayout instead of using ours.
+ */
+ if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) {
+ nand_chip->ecc.mode = NAND_ECC_SOFT;
+ nand_chip->ecc.algo = NAND_ECC_HAMMING;
+ goto scan_tail;
+ }
+
+ /* populate MTD interface based on ECC scheme */
+ switch (info->ecc_opt) {
+ case OMAP_ECC_HAM1_CODE_HW:
+ pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n");
+ nand_chip->ecc.mode = NAND_ECC_HW;
+ nand_chip->ecc.bytes = 3;
+ nand_chip->ecc.size = 512;
+ nand_chip->ecc.strength = 1;
+ nand_chip->ecc.calculate = omap_calculate_ecc;
+ nand_chip->ecc.hwctl = omap_enable_hwecc;
+ nand_chip->ecc.correct = omap_correct_data;
+ mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
+ oobbytes_per_step = nand_chip->ecc.bytes;
+
+ if (!(nand_chip->options & NAND_BUSWIDTH_16))
+ min_oobbytes = 1;
+
+ break;
+
+ case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
+ pr_info("nand: using OMAP_ECC_BCH4_CODE_HW_DETECTION_SW\n");
+ nand_chip->ecc.mode = NAND_ECC_HW;
+ nand_chip->ecc.size = 512;
+ nand_chip->ecc.bytes = 7;
+ nand_chip->ecc.strength = 4;
+ nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
+ nand_chip->ecc.correct = nand_bch_correct_data;
+ nand_chip->ecc.calculate = omap_calculate_ecc_bch_sw;
+ mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
+ /* Reserve one byte for the OMAP marker */
+ oobbytes_per_step = nand_chip->ecc.bytes + 1;
+ /* software bch library is used for locating errors */
+ nand_chip->ecc.priv = nand_bch_init(mtd);
+ if (!nand_chip->ecc.priv) {
+ dev_err(&info->pdev->dev, "unable to use BCH library\n");
+ err = -EINVAL;
+ goto return_error;
+ }
+ break;
+
+ case OMAP_ECC_BCH4_CODE_HW:
+ pr_info("nand: using OMAP_ECC_BCH4_CODE_HW ECC scheme\n");
+ nand_chip->ecc.mode = NAND_ECC_HW;
+ nand_chip->ecc.size = 512;
+ /* 14th bit is kept reserved for ROM-code compatibility */
+ nand_chip->ecc.bytes = 7 + 1;
+ nand_chip->ecc.strength = 4;
+ nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
+ nand_chip->ecc.correct = omap_elm_correct_data;
+ nand_chip->ecc.read_page = omap_read_page_bch;
+ nand_chip->ecc.write_page = omap_write_page_bch;
+ nand_chip->ecc.write_subpage = omap_write_subpage_bch;
+ mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
+ oobbytes_per_step = nand_chip->ecc.bytes;
+
+ err = elm_config(info->elm_dev, BCH4_ECC,
+ mtd->writesize / nand_chip->ecc.size,
+ nand_chip->ecc.size, nand_chip->ecc.bytes);
+ if (err < 0)
+ goto return_error;
+ break;
+
+ case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+ pr_info("nand: using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n");
+ nand_chip->ecc.mode = NAND_ECC_HW;
+ nand_chip->ecc.size = 512;
+ nand_chip->ecc.bytes = 13;
+ nand_chip->ecc.strength = 8;
+ nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
+ nand_chip->ecc.correct = nand_bch_correct_data;
+ nand_chip->ecc.calculate = omap_calculate_ecc_bch_sw;
+ mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
+ /* Reserve one byte for the OMAP marker */
+ oobbytes_per_step = nand_chip->ecc.bytes + 1;
+ /* software bch library is used for locating errors */
+ nand_chip->ecc.priv = nand_bch_init(mtd);
+ if (!nand_chip->ecc.priv) {
+ dev_err(&info->pdev->dev, "unable to use BCH library\n");
+ err = -EINVAL;
+ goto return_error;
+ }
+ break;
+
+ case OMAP_ECC_BCH8_CODE_HW:
+ pr_info("nand: using OMAP_ECC_BCH8_CODE_HW ECC scheme\n");
+ nand_chip->ecc.mode = NAND_ECC_HW;
+ nand_chip->ecc.size = 512;
+ /* 14th bit is kept reserved for ROM-code compatibility */
+ nand_chip->ecc.bytes = 13 + 1;
+ nand_chip->ecc.strength = 8;
+ nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
+ nand_chip->ecc.correct = omap_elm_correct_data;
+ nand_chip->ecc.read_page = omap_read_page_bch;
+ nand_chip->ecc.write_page = omap_write_page_bch;
+ nand_chip->ecc.write_subpage = omap_write_subpage_bch;
+ mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
+ oobbytes_per_step = nand_chip->ecc.bytes;
+
+ err = elm_config(info->elm_dev, BCH8_ECC,
+ mtd->writesize / nand_chip->ecc.size,
+ nand_chip->ecc.size, nand_chip->ecc.bytes);
+ if (err < 0)
+ goto return_error;
+
+ break;
+
+ case OMAP_ECC_BCH16_CODE_HW:
+ pr_info("using OMAP_ECC_BCH16_CODE_HW ECC scheme\n");
+ nand_chip->ecc.mode = NAND_ECC_HW;
+ nand_chip->ecc.size = 512;
+ nand_chip->ecc.bytes = 26;
+ nand_chip->ecc.strength = 16;
+ nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
+ nand_chip->ecc.correct = omap_elm_correct_data;
+ nand_chip->ecc.read_page = omap_read_page_bch;
+ nand_chip->ecc.write_page = omap_write_page_bch;
+ nand_chip->ecc.write_subpage = omap_write_subpage_bch;
+ mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
+ oobbytes_per_step = nand_chip->ecc.bytes;
+
+ err = elm_config(info->elm_dev, BCH16_ECC,
+ mtd->writesize / nand_chip->ecc.size,
+ nand_chip->ecc.size, nand_chip->ecc.bytes);
+ if (err < 0)
+ goto return_error;
+
+ break;
+ default:
+ dev_err(&info->pdev->dev, "invalid or unsupported ECC scheme\n");
+ err = -EINVAL;
+ goto return_error;
+ }
+
+ /* check if NAND device's OOB is enough to store ECC signatures */
+ min_oobbytes += (oobbytes_per_step *
+ (mtd->writesize / nand_chip->ecc.size));
+ if (mtd->oobsize < min_oobbytes) {
+ dev_err(&info->pdev->dev,
+ "not enough OOB bytes required = %d, available=%d\n",
+ min_oobbytes, mtd->oobsize);
+ err = -EINVAL;
+ goto return_error;
+ }
+
+scan_tail:
+ /* second phase scan */
+ err = nand_scan_tail(mtd);
+ if (err)
+ goto return_error;
+
+ err = mtd_device_register(mtd, NULL, 0);
+ if (err)
+ goto cleanup_nand;
+
+ platform_set_drvdata(pdev, mtd);
+
+ return 0;
+
+cleanup_nand:
+ nand_cleanup(nand_chip);
+
+return_error:
+ if (!IS_ERR_OR_NULL(info->dma))
+ dma_release_channel(info->dma);
+ if (nand_chip->ecc.priv) {
+ nand_bch_free(nand_chip->ecc.priv);
+ nand_chip->ecc.priv = NULL;
+ }
+ return err;
+}
+
+static int omap_nand_remove(struct platform_device *pdev)
+{
+ struct mtd_info *mtd = platform_get_drvdata(pdev);
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct omap_nand_info *info = mtd_to_omap(mtd);
+ if (nand_chip->ecc.priv) {
+ nand_bch_free(nand_chip->ecc.priv);
+ nand_chip->ecc.priv = NULL;
+ }
+ if (info->dma)
+ dma_release_channel(info->dma);
+ nand_release(mtd);
+ return 0;
+}
+
+static const struct of_device_id omap_nand_ids[] = {
+ { .compatible = "ti,omap2-nand", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, omap_nand_ids);
+
+static struct platform_driver omap_nand_driver = {
+ .probe = omap_nand_probe,
+ .remove = omap_nand_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = of_match_ptr(omap_nand_ids),
+ },
+};
+
+module_platform_driver(omap_nand_driver);
+
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Glue layer for NAND flash on TI OMAP boards");
diff --git a/drivers/mtd/nand/omap_elm.c b/drivers/mtd/nand/raw/omap_elm.c
index a3f32f939cc1..a3f32f939cc1 100644
--- a/drivers/mtd/nand/omap_elm.c
+++ b/drivers/mtd/nand/raw/omap_elm.c
diff --git a/drivers/mtd/nand/raw/orion_nand.c b/drivers/mtd/nand/raw/orion_nand.c
new file mode 100644
index 000000000000..7825fd3ce66b
--- /dev/null
+++ b/drivers/mtd/nand/raw/orion_nand.c
@@ -0,0 +1,232 @@
+/*
+ * NAND support for Marvell Orion SoC platforms
+ *
+ * Tzachi Perelstein <tzachi@marvell.com>
+ *
+ * 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/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <asm/sizes.h>
+#include <linux/platform_data/mtd-orion_nand.h>
+
+struct orion_nand_info {
+ struct nand_chip chip;
+ struct clk *clk;
+};
+
+static void orion_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+ struct nand_chip *nc = mtd_to_nand(mtd);
+ struct orion_nand_data *board = nand_get_controller_data(nc);
+ u32 offs;
+
+ if (cmd == NAND_CMD_NONE)
+ return;
+
+ if (ctrl & NAND_CLE)
+ offs = (1 << board->cle);
+ else if (ctrl & NAND_ALE)
+ offs = (1 << board->ale);
+ else
+ return;
+
+ if (nc->options & NAND_BUSWIDTH_16)
+ offs <<= 1;
+
+ writeb(cmd, nc->IO_ADDR_W + offs);
+}
+
+static void orion_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ void __iomem *io_base = chip->IO_ADDR_R;
+#if __LINUX_ARM_ARCH__ >= 5
+ uint64_t *buf64;
+#endif
+ int i = 0;
+
+ while (len && (unsigned long)buf & 7) {
+ *buf++ = readb(io_base);
+ len--;
+ }
+#if __LINUX_ARM_ARCH__ >= 5
+ buf64 = (uint64_t *)buf;
+ while (i < len/8) {
+ /*
+ * Since GCC has no proper constraint (PR 43518)
+ * force x variable to r2/r3 registers as ldrd instruction
+ * requires first register to be even.
+ */
+ register uint64_t x asm ("r2");
+
+ asm volatile ("ldrd\t%0, [%1]" : "=&r" (x) : "r" (io_base));
+ buf64[i++] = x;
+ }
+ i *= 8;
+#else
+ readsl(io_base, buf, len/4);
+ i = len / 4 * 4;
+#endif
+ while (i < len)
+ buf[i++] = readb(io_base);
+}
+
+static int __init orion_nand_probe(struct platform_device *pdev)
+{
+ struct orion_nand_info *info;
+ struct mtd_info *mtd;
+ struct nand_chip *nc;
+ struct orion_nand_data *board;
+ struct resource *res;
+ void __iomem *io_base;
+ int ret = 0;
+ u32 val = 0;
+
+ info = devm_kzalloc(&pdev->dev,
+ sizeof(struct orion_nand_info),
+ GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ nc = &info->chip;
+ mtd = nand_to_mtd(nc);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ io_base = devm_ioremap_resource(&pdev->dev, res);
+
+ if (IS_ERR(io_base))
+ return PTR_ERR(io_base);
+
+ if (pdev->dev.of_node) {
+ board = devm_kzalloc(&pdev->dev, sizeof(struct orion_nand_data),
+ GFP_KERNEL);
+ if (!board)
+ return -ENOMEM;
+ if (!of_property_read_u32(pdev->dev.of_node, "cle", &val))
+ board->cle = (u8)val;
+ else
+ board->cle = 0;
+ if (!of_property_read_u32(pdev->dev.of_node, "ale", &val))
+ board->ale = (u8)val;
+ else
+ board->ale = 1;
+ if (!of_property_read_u32(pdev->dev.of_node,
+ "bank-width", &val))
+ board->width = (u8)val * 8;
+ else
+ board->width = 8;
+ if (!of_property_read_u32(pdev->dev.of_node,
+ "chip-delay", &val))
+ board->chip_delay = (u8)val;
+ } else {
+ board = dev_get_platdata(&pdev->dev);
+ }
+
+ mtd->dev.parent = &pdev->dev;
+
+ nand_set_controller_data(nc, board);
+ nand_set_flash_node(nc, pdev->dev.of_node);
+ nc->IO_ADDR_R = nc->IO_ADDR_W = io_base;
+ nc->cmd_ctrl = orion_nand_cmd_ctrl;
+ nc->read_buf = orion_nand_read_buf;
+ nc->ecc.mode = NAND_ECC_SOFT;
+ nc->ecc.algo = NAND_ECC_HAMMING;
+
+ if (board->chip_delay)
+ nc->chip_delay = board->chip_delay;
+
+ WARN(board->width > 16,
+ "%d bit bus width out of range",
+ board->width);
+
+ if (board->width == 16)
+ nc->options |= NAND_BUSWIDTH_16;
+
+ if (board->dev_ready)
+ nc->dev_ready = board->dev_ready;
+
+ platform_set_drvdata(pdev, info);
+
+ /* Not all platforms can gate the clock, so it is not
+ an error if the clock does not exists. */
+ info->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(info->clk)) {
+ ret = PTR_ERR(info->clk);
+ if (ret == -ENOENT) {
+ info->clk = NULL;
+ } else {
+ dev_err(&pdev->dev, "failed to get clock!\n");
+ return ret;
+ }
+ }
+
+ ret = clk_prepare_enable(info->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to prepare clock!\n");
+ return ret;
+ }
+
+ ret = nand_scan(mtd, 1);
+ if (ret)
+ goto no_dev;
+
+ mtd->name = "orion_nand";
+ ret = mtd_device_register(mtd, board->parts, board->nr_parts);
+ if (ret) {
+ nand_release(mtd);
+ goto no_dev;
+ }
+
+ return 0;
+
+no_dev:
+ clk_disable_unprepare(info->clk);
+ return ret;
+}
+
+static int orion_nand_remove(struct platform_device *pdev)
+{
+ struct orion_nand_info *info = platform_get_drvdata(pdev);
+ struct nand_chip *chip = &info->chip;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ nand_release(mtd);
+
+ clk_disable_unprepare(info->clk);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id orion_nand_of_match_table[] = {
+ { .compatible = "marvell,orion-nand", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, orion_nand_of_match_table);
+#endif
+
+static struct platform_driver orion_nand_driver = {
+ .remove = orion_nand_remove,
+ .driver = {
+ .name = "orion_nand",
+ .of_match_table = of_match_ptr(orion_nand_of_match_table),
+ },
+};
+
+module_platform_driver_probe(orion_nand_driver, orion_nand_probe);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Tzachi Perelstein");
+MODULE_DESCRIPTION("NAND glue for Orion platforms");
+MODULE_ALIAS("platform:orion_nand");
diff --git a/drivers/mtd/nand/oxnas_nand.c b/drivers/mtd/nand/raw/oxnas_nand.c
index d649d5944826..d649d5944826 100644
--- a/drivers/mtd/nand/oxnas_nand.c
+++ b/drivers/mtd/nand/raw/oxnas_nand.c
diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/raw/pasemi_nand.c
index a47a7e4bd25a..a47a7e4bd25a 100644
--- a/drivers/mtd/nand/pasemi_nand.c
+++ b/drivers/mtd/nand/raw/pasemi_nand.c
diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/raw/plat_nand.c
index 925a1323604d..925a1323604d 100644
--- a/drivers/mtd/nand/plat_nand.c
+++ b/drivers/mtd/nand/raw/plat_nand.c
diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c
new file mode 100644
index 000000000000..b554fb6e609c
--- /dev/null
+++ b/drivers/mtd/nand/raw/qcom_nandc.c
@@ -0,0 +1,2921 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/module.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/delay.h>
+#include <linux/dma/qcom_bam_dma.h>
+#include <linux/dma-direct.h> /* XXX: drivers shall never use this directly! */
+
+/* NANDc reg offsets */
+#define NAND_FLASH_CMD 0x00
+#define NAND_ADDR0 0x04
+#define NAND_ADDR1 0x08
+#define NAND_FLASH_CHIP_SELECT 0x0c
+#define NAND_EXEC_CMD 0x10
+#define NAND_FLASH_STATUS 0x14
+#define NAND_BUFFER_STATUS 0x18
+#define NAND_DEV0_CFG0 0x20
+#define NAND_DEV0_CFG1 0x24
+#define NAND_DEV0_ECC_CFG 0x28
+#define NAND_DEV1_ECC_CFG 0x2c
+#define NAND_DEV1_CFG0 0x30
+#define NAND_DEV1_CFG1 0x34
+#define NAND_READ_ID 0x40
+#define NAND_READ_STATUS 0x44
+#define NAND_DEV_CMD0 0xa0
+#define NAND_DEV_CMD1 0xa4
+#define NAND_DEV_CMD2 0xa8
+#define NAND_DEV_CMD_VLD 0xac
+#define SFLASHC_BURST_CFG 0xe0
+#define NAND_ERASED_CW_DETECT_CFG 0xe8
+#define NAND_ERASED_CW_DETECT_STATUS 0xec
+#define NAND_EBI2_ECC_BUF_CFG 0xf0
+#define FLASH_BUF_ACC 0x100
+
+#define NAND_CTRL 0xf00
+#define NAND_VERSION 0xf08
+#define NAND_READ_LOCATION_0 0xf20
+#define NAND_READ_LOCATION_1 0xf24
+#define NAND_READ_LOCATION_2 0xf28
+#define NAND_READ_LOCATION_3 0xf2c
+
+/* dummy register offsets, used by write_reg_dma */
+#define NAND_DEV_CMD1_RESTORE 0xdead
+#define NAND_DEV_CMD_VLD_RESTORE 0xbeef
+
+/* NAND_FLASH_CMD bits */
+#define PAGE_ACC BIT(4)
+#define LAST_PAGE BIT(5)
+
+/* NAND_FLASH_CHIP_SELECT bits */
+#define NAND_DEV_SEL 0
+#define DM_EN BIT(2)
+
+/* NAND_FLASH_STATUS bits */
+#define FS_OP_ERR BIT(4)
+#define FS_READY_BSY_N BIT(5)
+#define FS_MPU_ERR BIT(8)
+#define FS_DEVICE_STS_ERR BIT(16)
+#define FS_DEVICE_WP BIT(23)
+
+/* NAND_BUFFER_STATUS bits */
+#define BS_UNCORRECTABLE_BIT BIT(8)
+#define BS_CORRECTABLE_ERR_MSK 0x1f
+
+/* NAND_DEVn_CFG0 bits */
+#define DISABLE_STATUS_AFTER_WRITE 4
+#define CW_PER_PAGE 6
+#define UD_SIZE_BYTES 9
+#define ECC_PARITY_SIZE_BYTES_RS 19
+#define SPARE_SIZE_BYTES 23
+#define NUM_ADDR_CYCLES 27
+#define STATUS_BFR_READ 30
+#define SET_RD_MODE_AFTER_STATUS 31
+
+/* NAND_DEVn_CFG0 bits */
+#define DEV0_CFG1_ECC_DISABLE 0
+#define WIDE_FLASH 1
+#define NAND_RECOVERY_CYCLES 2
+#define CS_ACTIVE_BSY 5
+#define BAD_BLOCK_BYTE_NUM 6
+#define BAD_BLOCK_IN_SPARE_AREA 16
+#define WR_RD_BSY_GAP 17
+#define ENABLE_BCH_ECC 27
+
+/* NAND_DEV0_ECC_CFG bits */
+#define ECC_CFG_ECC_DISABLE 0
+#define ECC_SW_RESET 1
+#define ECC_MODE 4
+#define ECC_PARITY_SIZE_BYTES_BCH 8
+#define ECC_NUM_DATA_BYTES 16
+#define ECC_FORCE_CLK_OPEN 30
+
+/* NAND_DEV_CMD1 bits */
+#define READ_ADDR 0
+
+/* NAND_DEV_CMD_VLD bits */
+#define READ_START_VLD BIT(0)
+#define READ_STOP_VLD BIT(1)
+#define WRITE_START_VLD BIT(2)
+#define ERASE_START_VLD BIT(3)
+#define SEQ_READ_START_VLD BIT(4)
+
+/* NAND_EBI2_ECC_BUF_CFG bits */
+#define NUM_STEPS 0
+
+/* NAND_ERASED_CW_DETECT_CFG bits */
+#define ERASED_CW_ECC_MASK 1
+#define AUTO_DETECT_RES 0
+#define MASK_ECC (1 << ERASED_CW_ECC_MASK)
+#define RESET_ERASED_DET (1 << AUTO_DETECT_RES)
+#define ACTIVE_ERASED_DET (0 << AUTO_DETECT_RES)
+#define CLR_ERASED_PAGE_DET (RESET_ERASED_DET | MASK_ECC)
+#define SET_ERASED_PAGE_DET (ACTIVE_ERASED_DET | MASK_ECC)
+
+/* NAND_ERASED_CW_DETECT_STATUS bits */
+#define PAGE_ALL_ERASED BIT(7)
+#define CODEWORD_ALL_ERASED BIT(6)
+#define PAGE_ERASED BIT(5)
+#define CODEWORD_ERASED BIT(4)
+#define ERASED_PAGE (PAGE_ALL_ERASED | PAGE_ERASED)
+#define ERASED_CW (CODEWORD_ALL_ERASED | CODEWORD_ERASED)
+
+/* NAND_READ_LOCATION_n bits */
+#define READ_LOCATION_OFFSET 0
+#define READ_LOCATION_SIZE 16
+#define READ_LOCATION_LAST 31
+
+/* Version Mask */
+#define NAND_VERSION_MAJOR_MASK 0xf0000000
+#define NAND_VERSION_MAJOR_SHIFT 28
+#define NAND_VERSION_MINOR_MASK 0x0fff0000
+#define NAND_VERSION_MINOR_SHIFT 16
+
+/* NAND OP_CMDs */
+#define PAGE_READ 0x2
+#define PAGE_READ_WITH_ECC 0x3
+#define PAGE_READ_WITH_ECC_SPARE 0x4
+#define PROGRAM_PAGE 0x6
+#define PAGE_PROGRAM_WITH_ECC 0x7
+#define PROGRAM_PAGE_SPARE 0x9
+#define BLOCK_ERASE 0xa
+#define FETCH_ID 0xb
+#define RESET_DEVICE 0xd
+
+/* Default Value for NAND_DEV_CMD_VLD */
+#define NAND_DEV_CMD_VLD_VAL (READ_START_VLD | WRITE_START_VLD | \
+ ERASE_START_VLD | SEQ_READ_START_VLD)
+
+/* NAND_CTRL bits */
+#define BAM_MODE_EN BIT(0)
+
+/*
+ * the NAND controller performs reads/writes with ECC in 516 byte chunks.
+ * the driver calls the chunks 'step' or 'codeword' interchangeably
+ */
+#define NANDC_STEP_SIZE 512
+
+/*
+ * the largest page size we support is 8K, this will have 16 steps/codewords
+ * of 512 bytes each
+ */
+#define MAX_NUM_STEPS (SZ_8K / NANDC_STEP_SIZE)
+
+/* we read at most 3 registers per codeword scan */
+#define MAX_REG_RD (3 * MAX_NUM_STEPS)
+
+/* ECC modes supported by the controller */
+#define ECC_NONE BIT(0)
+#define ECC_RS_4BIT BIT(1)
+#define ECC_BCH_4BIT BIT(2)
+#define ECC_BCH_8BIT BIT(3)
+
+#define nandc_set_read_loc(nandc, reg, offset, size, is_last) \
+nandc_set_reg(nandc, NAND_READ_LOCATION_##reg, \
+ ((offset) << READ_LOCATION_OFFSET) | \
+ ((size) << READ_LOCATION_SIZE) | \
+ ((is_last) << READ_LOCATION_LAST))
+
+/*
+ * Returns the actual register address for all NAND_DEV_ registers
+ * (i.e. NAND_DEV_CMD0, NAND_DEV_CMD1, NAND_DEV_CMD2 and NAND_DEV_CMD_VLD)
+ */
+#define dev_cmd_reg_addr(nandc, reg) ((nandc)->props->dev_cmd_reg_start + (reg))
+
+/* Returns the NAND register physical address */
+#define nandc_reg_phys(chip, offset) ((chip)->base_phys + (offset))
+
+/* Returns the dma address for reg read buffer */
+#define reg_buf_dma_addr(chip, vaddr) \
+ ((chip)->reg_read_dma + \
+ ((uint8_t *)(vaddr) - (uint8_t *)(chip)->reg_read_buf))
+
+#define QPIC_PER_CW_CMD_ELEMENTS 32
+#define QPIC_PER_CW_CMD_SGL 32
+#define QPIC_PER_CW_DATA_SGL 8
+
+/*
+ * Flags used in DMA descriptor preparation helper functions
+ * (i.e. read_reg_dma/write_reg_dma/read_data_dma/write_data_dma)
+ */
+/* Don't set the EOT in current tx BAM sgl */
+#define NAND_BAM_NO_EOT BIT(0)
+/* Set the NWD flag in current BAM sgl */
+#define NAND_BAM_NWD BIT(1)
+/* Finish writing in the current BAM sgl and start writing in another BAM sgl */
+#define NAND_BAM_NEXT_SGL BIT(2)
+/*
+ * Erased codeword status is being used two times in single transfer so this
+ * flag will determine the current value of erased codeword status register
+ */
+#define NAND_ERASED_CW_SET BIT(4)
+
+/*
+ * This data type corresponds to the BAM transaction which will be used for all
+ * NAND transfers.
+ * @bam_ce - the array of BAM command elements
+ * @cmd_sgl - sgl for NAND BAM command pipe
+ * @data_sgl - sgl for NAND BAM consumer/producer pipe
+ * @bam_ce_pos - the index in bam_ce which is available for next sgl
+ * @bam_ce_start - the index in bam_ce which marks the start position ce
+ * for current sgl. It will be used for size calculation
+ * for current sgl
+ * @cmd_sgl_pos - current index in command sgl.
+ * @cmd_sgl_start - start index in command sgl.
+ * @tx_sgl_pos - current index in data sgl for tx.
+ * @tx_sgl_start - start index in data sgl for tx.
+ * @rx_sgl_pos - current index in data sgl for rx.
+ * @rx_sgl_start - start index in data sgl for rx.
+ */
+struct bam_transaction {
+ struct bam_cmd_element *bam_ce;
+ struct scatterlist *cmd_sgl;
+ struct scatterlist *data_sgl;
+ u32 bam_ce_pos;
+ u32 bam_ce_start;
+ u32 cmd_sgl_pos;
+ u32 cmd_sgl_start;
+ u32 tx_sgl_pos;
+ u32 tx_sgl_start;
+ u32 rx_sgl_pos;
+ u32 rx_sgl_start;
+};
+
+/*
+ * This data type corresponds to the nand dma descriptor
+ * @list - list for desc_info
+ * @dir - DMA transfer direction
+ * @adm_sgl - sgl which will be used for single sgl dma descriptor. Only used by
+ * ADM
+ * @bam_sgl - sgl which will be used for dma descriptor. Only used by BAM
+ * @sgl_cnt - number of SGL in bam_sgl. Only used by BAM
+ * @dma_desc - low level DMA engine descriptor
+ */
+struct desc_info {
+ struct list_head node;
+
+ enum dma_data_direction dir;
+ union {
+ struct scatterlist adm_sgl;
+ struct {
+ struct scatterlist *bam_sgl;
+ int sgl_cnt;
+ };
+ };
+ struct dma_async_tx_descriptor *dma_desc;
+};
+
+/*
+ * holds the current register values that we want to write. acts as a contiguous
+ * chunk of memory which we use to write the controller registers through DMA.
+ */
+struct nandc_regs {
+ __le32 cmd;
+ __le32 addr0;
+ __le32 addr1;
+ __le32 chip_sel;
+ __le32 exec;
+
+ __le32 cfg0;
+ __le32 cfg1;
+ __le32 ecc_bch_cfg;
+
+ __le32 clrflashstatus;
+ __le32 clrreadstatus;
+
+ __le32 cmd1;
+ __le32 vld;
+
+ __le32 orig_cmd1;
+ __le32 orig_vld;
+
+ __le32 ecc_buf_cfg;
+ __le32 read_location0;
+ __le32 read_location1;
+ __le32 read_location2;
+ __le32 read_location3;
+
+ __le32 erased_cw_detect_cfg_clr;
+ __le32 erased_cw_detect_cfg_set;
+};
+
+/*
+ * NAND controller data struct
+ *
+ * @controller: base controller structure
+ * @host_list: list containing all the chips attached to the
+ * controller
+ * @dev: parent device
+ * @base: MMIO base
+ * @base_phys: physical base address of controller registers
+ * @base_dma: dma base address of controller registers
+ * @core_clk: controller clock
+ * @aon_clk: another controller clock
+ *
+ * @chan: dma channel
+ * @cmd_crci: ADM DMA CRCI for command flow control
+ * @data_crci: ADM DMA CRCI for data flow control
+ * @desc_list: DMA descriptor list (list of desc_infos)
+ *
+ * @data_buffer: our local DMA buffer for page read/writes,
+ * used when we can't use the buffer provided
+ * by upper layers directly
+ * @buf_size/count/start: markers for chip->read_buf/write_buf functions
+ * @reg_read_buf: local buffer for reading back registers via DMA
+ * @reg_read_dma: contains dma address for register read buffer
+ * @reg_read_pos: marker for data read in reg_read_buf
+ *
+ * @regs: a contiguous chunk of memory for DMA register
+ * writes. contains the register values to be
+ * written to controller
+ * @cmd1/vld: some fixed controller register values
+ * @props: properties of current NAND controller,
+ * initialized via DT match data
+ * @max_cwperpage: maximum QPIC codewords required. calculated
+ * from all connected NAND devices pagesize
+ */
+struct qcom_nand_controller {
+ struct nand_hw_control controller;
+ struct list_head host_list;
+
+ struct device *dev;
+
+ void __iomem *base;
+ phys_addr_t base_phys;
+ dma_addr_t base_dma;
+
+ struct clk *core_clk;
+ struct clk *aon_clk;
+
+ union {
+ /* will be used only by QPIC for BAM DMA */
+ struct {
+ struct dma_chan *tx_chan;
+ struct dma_chan *rx_chan;
+ struct dma_chan *cmd_chan;
+ };
+
+ /* will be used only by EBI2 for ADM DMA */
+ struct {
+ struct dma_chan *chan;
+ unsigned int cmd_crci;
+ unsigned int data_crci;
+ };
+ };
+
+ struct list_head desc_list;
+ struct bam_transaction *bam_txn;
+
+ u8 *data_buffer;
+ int buf_size;
+ int buf_count;
+ int buf_start;
+ unsigned int max_cwperpage;
+
+ __le32 *reg_read_buf;
+ dma_addr_t reg_read_dma;
+ int reg_read_pos;
+
+ struct nandc_regs *regs;
+
+ u32 cmd1, vld;
+ const struct qcom_nandc_props *props;
+};
+
+/*
+ * NAND chip structure
+ *
+ * @chip: base NAND chip structure
+ * @node: list node to add itself to host_list in
+ * qcom_nand_controller
+ *
+ * @cs: chip select value for this chip
+ * @cw_size: the number of bytes in a single step/codeword
+ * of a page, consisting of all data, ecc, spare
+ * and reserved bytes
+ * @cw_data: the number of bytes within a codeword protected
+ * by ECC
+ * @use_ecc: request the controller to use ECC for the
+ * upcoming read/write
+ * @bch_enabled: flag to tell whether BCH ECC mode is used
+ * @ecc_bytes_hw: ECC bytes used by controller hardware for this
+ * chip
+ * @status: value to be returned if NAND_CMD_STATUS command
+ * is executed
+ * @last_command: keeps track of last command on this chip. used
+ * for reading correct status
+ *
+ * @cfg0, cfg1, cfg0_raw..: NANDc register configurations needed for
+ * ecc/non-ecc mode for the current nand flash
+ * device
+ */
+struct qcom_nand_host {
+ struct nand_chip chip;
+ struct list_head node;
+
+ int cs;
+ int cw_size;
+ int cw_data;
+ bool use_ecc;
+ bool bch_enabled;
+ int ecc_bytes_hw;
+ int spare_bytes;
+ int bbm_size;
+ u8 status;
+ int last_command;
+
+ u32 cfg0, cfg1;
+ u32 cfg0_raw, cfg1_raw;
+ u32 ecc_buf_cfg;
+ u32 ecc_bch_cfg;
+ u32 clrflashstatus;
+ u32 clrreadstatus;
+};
+
+/*
+ * This data type corresponds to the NAND controller properties which varies
+ * among different NAND controllers.
+ * @ecc_modes - ecc mode for NAND
+ * @is_bam - whether NAND controller is using BAM
+ * @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset
+ */
+struct qcom_nandc_props {
+ u32 ecc_modes;
+ bool is_bam;
+ u32 dev_cmd_reg_start;
+};
+
+/* Frees the BAM transaction memory */
+static void free_bam_transaction(struct qcom_nand_controller *nandc)
+{
+ struct bam_transaction *bam_txn = nandc->bam_txn;
+
+ devm_kfree(nandc->dev, bam_txn);
+}
+
+/* Allocates and Initializes the BAM transaction */
+static struct bam_transaction *
+alloc_bam_transaction(struct qcom_nand_controller *nandc)
+{
+ struct bam_transaction *bam_txn;
+ size_t bam_txn_size;
+ unsigned int num_cw = nandc->max_cwperpage;
+ void *bam_txn_buf;
+
+ bam_txn_size =
+ sizeof(*bam_txn) + num_cw *
+ ((sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS) +
+ (sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL) +
+ (sizeof(*bam_txn->data_sgl) * QPIC_PER_CW_DATA_SGL));
+
+ bam_txn_buf = devm_kzalloc(nandc->dev, bam_txn_size, GFP_KERNEL);
+ if (!bam_txn_buf)
+ return NULL;
+
+ bam_txn = bam_txn_buf;
+ bam_txn_buf += sizeof(*bam_txn);
+
+ bam_txn->bam_ce = bam_txn_buf;
+ bam_txn_buf +=
+ sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS * num_cw;
+
+ bam_txn->cmd_sgl = bam_txn_buf;
+ bam_txn_buf +=
+ sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL * num_cw;
+
+ bam_txn->data_sgl = bam_txn_buf;
+
+ return bam_txn;
+}
+
+/* Clears the BAM transaction indexes */
+static void clear_bam_transaction(struct qcom_nand_controller *nandc)
+{
+ struct bam_transaction *bam_txn = nandc->bam_txn;
+
+ if (!nandc->props->is_bam)
+ return;
+
+ bam_txn->bam_ce_pos = 0;
+ bam_txn->bam_ce_start = 0;
+ bam_txn->cmd_sgl_pos = 0;
+ bam_txn->cmd_sgl_start = 0;
+ bam_txn->tx_sgl_pos = 0;
+ bam_txn->tx_sgl_start = 0;
+ bam_txn->rx_sgl_pos = 0;
+ bam_txn->rx_sgl_start = 0;
+
+ sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage *
+ QPIC_PER_CW_CMD_SGL);
+ sg_init_table(bam_txn->data_sgl, nandc->max_cwperpage *
+ QPIC_PER_CW_DATA_SGL);
+}
+
+static inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip)
+{
+ return container_of(chip, struct qcom_nand_host, chip);
+}
+
+static inline struct qcom_nand_controller *
+get_qcom_nand_controller(struct nand_chip *chip)
+{
+ return container_of(chip->controller, struct qcom_nand_controller,
+ controller);
+}
+
+static inline u32 nandc_read(struct qcom_nand_controller *nandc, int offset)
+{
+ return ioread32(nandc->base + offset);
+}
+
+static inline void nandc_write(struct qcom_nand_controller *nandc, int offset,
+ u32 val)
+{
+ iowrite32(val, nandc->base + offset);
+}
+
+static inline void nandc_read_buffer_sync(struct qcom_nand_controller *nandc,
+ bool is_cpu)
+{
+ if (!nandc->props->is_bam)
+ return;
+
+ if (is_cpu)
+ dma_sync_single_for_cpu(nandc->dev, nandc->reg_read_dma,
+ MAX_REG_RD *
+ sizeof(*nandc->reg_read_buf),
+ DMA_FROM_DEVICE);
+ else
+ dma_sync_single_for_device(nandc->dev, nandc->reg_read_dma,
+ MAX_REG_RD *
+ sizeof(*nandc->reg_read_buf),
+ DMA_FROM_DEVICE);
+}
+
+static __le32 *offset_to_nandc_reg(struct nandc_regs *regs, int offset)
+{
+ switch (offset) {
+ case NAND_FLASH_CMD:
+ return &regs->cmd;
+ case NAND_ADDR0:
+ return &regs->addr0;
+ case NAND_ADDR1:
+ return &regs->addr1;
+ case NAND_FLASH_CHIP_SELECT:
+ return &regs->chip_sel;
+ case NAND_EXEC_CMD:
+ return &regs->exec;
+ case NAND_FLASH_STATUS:
+ return &regs->clrflashstatus;
+ case NAND_DEV0_CFG0:
+ return &regs->cfg0;
+ case NAND_DEV0_CFG1:
+ return &regs->cfg1;
+ case NAND_DEV0_ECC_CFG:
+ return &regs->ecc_bch_cfg;
+ case NAND_READ_STATUS:
+ return &regs->clrreadstatus;
+ case NAND_DEV_CMD1:
+ return &regs->cmd1;
+ case NAND_DEV_CMD1_RESTORE:
+ return &regs->orig_cmd1;
+ case NAND_DEV_CMD_VLD:
+ return &regs->vld;
+ case NAND_DEV_CMD_VLD_RESTORE:
+ return &regs->orig_vld;
+ case NAND_EBI2_ECC_BUF_CFG:
+ return &regs->ecc_buf_cfg;
+ case NAND_READ_LOCATION_0:
+ return &regs->read_location0;
+ case NAND_READ_LOCATION_1:
+ return &regs->read_location1;
+ case NAND_READ_LOCATION_2:
+ return &regs->read_location2;
+ case NAND_READ_LOCATION_3:
+ return &regs->read_location3;
+ default:
+ return NULL;
+ }
+}
+
+static void nandc_set_reg(struct qcom_nand_controller *nandc, int offset,
+ u32 val)
+{
+ struct nandc_regs *regs = nandc->regs;
+ __le32 *reg;
+
+ reg = offset_to_nandc_reg(regs, offset);
+
+ if (reg)
+ *reg = cpu_to_le32(val);
+}
+
+/* helper to configure address register values */
+static void set_address(struct qcom_nand_host *host, u16 column, int page)
+{
+ struct nand_chip *chip = &host->chip;
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+
+ if (chip->options & NAND_BUSWIDTH_16)
+ column >>= 1;
+
+ nandc_set_reg(nandc, NAND_ADDR0, page << 16 | column);
+ nandc_set_reg(nandc, NAND_ADDR1, page >> 16 & 0xff);
+}
+
+/*
+ * update_rw_regs: set up read/write register values, these will be
+ * written to the NAND controller registers via DMA
+ *
+ * @num_cw: number of steps for the read/write operation
+ * @read: read or write operation
+ */
+static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read)
+{
+ struct nand_chip *chip = &host->chip;
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+ u32 cmd, cfg0, cfg1, ecc_bch_cfg;
+
+ if (read) {
+ if (host->use_ecc)
+ cmd = PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE;
+ else
+ cmd = PAGE_READ | PAGE_ACC | LAST_PAGE;
+ } else {
+ cmd = PROGRAM_PAGE | PAGE_ACC | LAST_PAGE;
+ }
+
+ if (host->use_ecc) {
+ cfg0 = (host->cfg0 & ~(7U << CW_PER_PAGE)) |
+ (num_cw - 1) << CW_PER_PAGE;
+
+ cfg1 = host->cfg1;
+ ecc_bch_cfg = host->ecc_bch_cfg;
+ } else {
+ cfg0 = (host->cfg0_raw & ~(7U << CW_PER_PAGE)) |
+ (num_cw - 1) << CW_PER_PAGE;
+
+ cfg1 = host->cfg1_raw;
+ ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE;
+ }
+
+ nandc_set_reg(nandc, NAND_FLASH_CMD, cmd);
+ nandc_set_reg(nandc, NAND_DEV0_CFG0, cfg0);
+ nandc_set_reg(nandc, NAND_DEV0_CFG1, cfg1);
+ nandc_set_reg(nandc, NAND_DEV0_ECC_CFG, ecc_bch_cfg);
+ nandc_set_reg(nandc, NAND_EBI2_ECC_BUF_CFG, host->ecc_buf_cfg);
+ nandc_set_reg(nandc, NAND_FLASH_STATUS, host->clrflashstatus);
+ nandc_set_reg(nandc, NAND_READ_STATUS, host->clrreadstatus);
+ nandc_set_reg(nandc, NAND_EXEC_CMD, 1);
+
+ if (read)
+ nandc_set_read_loc(nandc, 0, 0, host->use_ecc ?
+ host->cw_data : host->cw_size, 1);
+}
+
+/*
+ * Maps the scatter gather list for DMA transfer and forms the DMA descriptor
+ * for BAM. This descriptor will be added in the NAND DMA descriptor queue
+ * which will be submitted to DMA engine.
+ */
+static int prepare_bam_async_desc(struct qcom_nand_controller *nandc,
+ struct dma_chan *chan,
+ unsigned long flags)
+{
+ struct desc_info *desc;
+ struct scatterlist *sgl;
+ unsigned int sgl_cnt;
+ int ret;
+ struct bam_transaction *bam_txn = nandc->bam_txn;
+ enum dma_transfer_direction dir_eng;
+ struct dma_async_tx_descriptor *dma_desc;
+
+ desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+ if (!desc)
+ return -ENOMEM;
+
+ if (chan == nandc->cmd_chan) {
+ sgl = &bam_txn->cmd_sgl[bam_txn->cmd_sgl_start];
+ sgl_cnt = bam_txn->cmd_sgl_pos - bam_txn->cmd_sgl_start;
+ bam_txn->cmd_sgl_start = bam_txn->cmd_sgl_pos;
+ dir_eng = DMA_MEM_TO_DEV;
+ desc->dir = DMA_TO_DEVICE;
+ } else if (chan == nandc->tx_chan) {
+ sgl = &bam_txn->data_sgl[bam_txn->tx_sgl_start];
+ sgl_cnt = bam_txn->tx_sgl_pos - bam_txn->tx_sgl_start;
+ bam_txn->tx_sgl_start = bam_txn->tx_sgl_pos;
+ dir_eng = DMA_MEM_TO_DEV;
+ desc->dir = DMA_TO_DEVICE;
+ } else {
+ sgl = &bam_txn->data_sgl[bam_txn->rx_sgl_start];
+ sgl_cnt = bam_txn->rx_sgl_pos - bam_txn->rx_sgl_start;
+ bam_txn->rx_sgl_start = bam_txn->rx_sgl_pos;
+ dir_eng = DMA_DEV_TO_MEM;
+ desc->dir = DMA_FROM_DEVICE;
+ }
+
+ sg_mark_end(sgl + sgl_cnt - 1);
+ ret = dma_map_sg(nandc->dev, sgl, sgl_cnt, desc->dir);
+ if (ret == 0) {
+ dev_err(nandc->dev, "failure in mapping desc\n");
+ kfree(desc);
+ return -ENOMEM;
+ }
+
+ desc->sgl_cnt = sgl_cnt;
+ desc->bam_sgl = sgl;
+
+ dma_desc = dmaengine_prep_slave_sg(chan, sgl, sgl_cnt, dir_eng,
+ flags);
+
+ if (!dma_desc) {
+ dev_err(nandc->dev, "failure in prep desc\n");
+ dma_unmap_sg(nandc->dev, sgl, sgl_cnt, desc->dir);
+ kfree(desc);
+ return -EINVAL;
+ }
+
+ desc->dma_desc = dma_desc;
+
+ list_add_tail(&desc->node, &nandc->desc_list);
+
+ return 0;
+}
+
+/*
+ * Prepares the command descriptor for BAM DMA which will be used for NAND
+ * register reads and writes. The command descriptor requires the command
+ * to be formed in command element type so this function uses the command
+ * element from bam transaction ce array and fills the same with required
+ * data. A single SGL can contain multiple command elements so
+ * NAND_BAM_NEXT_SGL will be used for starting the separate SGL
+ * after the current command element.
+ */
+static int prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc, bool read,
+ int reg_off, const void *vaddr,
+ int size, unsigned int flags)
+{
+ int bam_ce_size;
+ int i, ret;
+ struct bam_cmd_element *bam_ce_buffer;
+ struct bam_transaction *bam_txn = nandc->bam_txn;
+
+ bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_pos];
+
+ /* fill the command desc */
+ for (i = 0; i < size; i++) {
+ if (read)
+ bam_prep_ce(&bam_ce_buffer[i],
+ nandc_reg_phys(nandc, reg_off + 4 * i),
+ BAM_READ_COMMAND,
+ reg_buf_dma_addr(nandc,
+ (__le32 *)vaddr + i));
+ else
+ bam_prep_ce_le32(&bam_ce_buffer[i],
+ nandc_reg_phys(nandc, reg_off + 4 * i),
+ BAM_WRITE_COMMAND,
+ *((__le32 *)vaddr + i));
+ }
+
+ bam_txn->bam_ce_pos += size;
+
+ /* use the separate sgl after this command */
+ if (flags & NAND_BAM_NEXT_SGL) {
+ bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_start];
+ bam_ce_size = (bam_txn->bam_ce_pos -
+ bam_txn->bam_ce_start) *
+ sizeof(struct bam_cmd_element);
+ sg_set_buf(&bam_txn->cmd_sgl[bam_txn->cmd_sgl_pos],
+ bam_ce_buffer, bam_ce_size);
+ bam_txn->cmd_sgl_pos++;
+ bam_txn->bam_ce_start = bam_txn->bam_ce_pos;
+
+ if (flags & NAND_BAM_NWD) {
+ ret = prepare_bam_async_desc(nandc, nandc->cmd_chan,
+ DMA_PREP_FENCE |
+ DMA_PREP_CMD);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Prepares the data descriptor for BAM DMA which will be used for NAND
+ * data reads and writes.
+ */
+static int prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read,
+ const void *vaddr,
+ int size, unsigned int flags)
+{
+ int ret;
+ struct bam_transaction *bam_txn = nandc->bam_txn;
+
+ if (read) {
+ sg_set_buf(&bam_txn->data_sgl[bam_txn->rx_sgl_pos],
+ vaddr, size);
+ bam_txn->rx_sgl_pos++;
+ } else {
+ sg_set_buf(&bam_txn->data_sgl[bam_txn->tx_sgl_pos],
+ vaddr, size);
+ bam_txn->tx_sgl_pos++;
+
+ /*
+ * BAM will only set EOT for DMA_PREP_INTERRUPT so if this flag
+ * is not set, form the DMA descriptor
+ */
+ if (!(flags & NAND_BAM_NO_EOT)) {
+ ret = prepare_bam_async_desc(nandc, nandc->tx_chan,
+ DMA_PREP_INTERRUPT);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read,
+ int reg_off, const void *vaddr, int size,
+ bool flow_control)
+{
+ struct desc_info *desc;
+ struct dma_async_tx_descriptor *dma_desc;
+ struct scatterlist *sgl;
+ struct dma_slave_config slave_conf;
+ enum dma_transfer_direction dir_eng;
+ int ret;
+
+ desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+ if (!desc)
+ return -ENOMEM;
+
+ sgl = &desc->adm_sgl;
+
+ sg_init_one(sgl, vaddr, size);
+
+ if (read) {
+ dir_eng = DMA_DEV_TO_MEM;
+ desc->dir = DMA_FROM_DEVICE;
+ } else {
+ dir_eng = DMA_MEM_TO_DEV;
+ desc->dir = DMA_TO_DEVICE;
+ }
+
+ ret = dma_map_sg(nandc->dev, sgl, 1, desc->dir);
+ if (ret == 0) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ memset(&slave_conf, 0x00, sizeof(slave_conf));
+
+ slave_conf.device_fc = flow_control;
+ if (read) {
+ slave_conf.src_maxburst = 16;
+ slave_conf.src_addr = nandc->base_dma + reg_off;
+ slave_conf.slave_id = nandc->data_crci;
+ } else {
+ slave_conf.dst_maxburst = 16;
+ slave_conf.dst_addr = nandc->base_dma + reg_off;
+ slave_conf.slave_id = nandc->cmd_crci;
+ }
+
+ ret = dmaengine_slave_config(nandc->chan, &slave_conf);
+ if (ret) {
+ dev_err(nandc->dev, "failed to configure dma channel\n");
+ goto err;
+ }
+
+ dma_desc = dmaengine_prep_slave_sg(nandc->chan, sgl, 1, dir_eng, 0);
+ if (!dma_desc) {
+ dev_err(nandc->dev, "failed to prepare desc\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ desc->dma_desc = dma_desc;
+
+ list_add_tail(&desc->node, &nandc->desc_list);
+
+ return 0;
+err:
+ kfree(desc);
+
+ return ret;
+}
+
+/*
+ * read_reg_dma: prepares a descriptor to read a given number of
+ * contiguous registers to the reg_read_buf pointer
+ *
+ * @first: offset of the first register in the contiguous block
+ * @num_regs: number of registers to read
+ * @flags: flags to control DMA descriptor preparation
+ */
+static int read_reg_dma(struct qcom_nand_controller *nandc, int first,
+ int num_regs, unsigned int flags)
+{
+ bool flow_control = false;
+ void *vaddr;
+
+ vaddr = nandc->reg_read_buf + nandc->reg_read_pos;
+ nandc->reg_read_pos += num_regs;
+
+ if (first == NAND_DEV_CMD_VLD || first == NAND_DEV_CMD1)
+ first = dev_cmd_reg_addr(nandc, first);
+
+ if (nandc->props->is_bam)
+ return prep_bam_dma_desc_cmd(nandc, true, first, vaddr,
+ num_regs, flags);
+
+ if (first == NAND_READ_ID || first == NAND_FLASH_STATUS)
+ flow_control = true;
+
+ return prep_adm_dma_desc(nandc, true, first, vaddr,
+ num_regs * sizeof(u32), flow_control);
+}
+
+/*
+ * write_reg_dma: prepares a descriptor to write a given number of
+ * contiguous registers
+ *
+ * @first: offset of the first register in the contiguous block
+ * @num_regs: number of registers to write
+ * @flags: flags to control DMA descriptor preparation
+ */
+static int write_reg_dma(struct qcom_nand_controller *nandc, int first,
+ int num_regs, unsigned int flags)
+{
+ bool flow_control = false;
+ struct nandc_regs *regs = nandc->regs;
+ void *vaddr;
+
+ vaddr = offset_to_nandc_reg(regs, first);
+
+ if (first == NAND_ERASED_CW_DETECT_CFG) {
+ if (flags & NAND_ERASED_CW_SET)
+ vaddr = &regs->erased_cw_detect_cfg_set;
+ else
+ vaddr = &regs->erased_cw_detect_cfg_clr;
+ }
+
+ if (first == NAND_EXEC_CMD)
+ flags |= NAND_BAM_NWD;
+
+ if (first == NAND_DEV_CMD1_RESTORE || first == NAND_DEV_CMD1)
+ first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD1);
+
+ if (first == NAND_DEV_CMD_VLD_RESTORE || first == NAND_DEV_CMD_VLD)
+ first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD);
+
+ if (nandc->props->is_bam)
+ return prep_bam_dma_desc_cmd(nandc, false, first, vaddr,
+ num_regs, flags);
+
+ if (first == NAND_FLASH_CMD)
+ flow_control = true;
+
+ return prep_adm_dma_desc(nandc, false, first, vaddr,
+ num_regs * sizeof(u32), flow_control);
+}
+
+/*
+ * read_data_dma: prepares a DMA descriptor to transfer data from the
+ * controller's internal buffer to the buffer 'vaddr'
+ *
+ * @reg_off: offset within the controller's data buffer
+ * @vaddr: virtual address of the buffer we want to write to
+ * @size: DMA transaction size in bytes
+ * @flags: flags to control DMA descriptor preparation
+ */
+static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off,
+ const u8 *vaddr, int size, unsigned int flags)
+{
+ if (nandc->props->is_bam)
+ return prep_bam_dma_desc_data(nandc, true, vaddr, size, flags);
+
+ return prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false);
+}
+
+/*
+ * write_data_dma: prepares a DMA descriptor to transfer data from
+ * 'vaddr' to the controller's internal buffer
+ *
+ * @reg_off: offset within the controller's data buffer
+ * @vaddr: virtual address of the buffer we want to read from
+ * @size: DMA transaction size in bytes
+ * @flags: flags to control DMA descriptor preparation
+ */
+static int write_data_dma(struct qcom_nand_controller *nandc, int reg_off,
+ const u8 *vaddr, int size, unsigned int flags)
+{
+ if (nandc->props->is_bam)
+ return prep_bam_dma_desc_data(nandc, false, vaddr, size, flags);
+
+ return prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false);
+}
+
+/*
+ * Helper to prepare DMA descriptors for configuring registers
+ * before reading a NAND page.
+ */
+static void config_nand_page_read(struct qcom_nand_controller *nandc)
+{
+ write_reg_dma(nandc, NAND_ADDR0, 2, 0);
+ write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0);
+ write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, 0);
+ write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, 0);
+ write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1,
+ NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL);
+}
+
+/*
+ * Helper to prepare DMA descriptors for configuring registers
+ * before reading each codeword in NAND page.
+ */
+static void config_nand_cw_read(struct qcom_nand_controller *nandc)
+{
+ if (nandc->props->is_bam)
+ write_reg_dma(nandc, NAND_READ_LOCATION_0, 4,
+ NAND_BAM_NEXT_SGL);
+
+ write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
+
+ read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0);
+ read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1,
+ NAND_BAM_NEXT_SGL);
+}
+
+/*
+ * Helper to prepare dma descriptors to configure registers needed for reading a
+ * single codeword in page
+ */
+static void config_nand_single_cw_page_read(struct qcom_nand_controller *nandc)
+{
+ config_nand_page_read(nandc);
+ config_nand_cw_read(nandc);
+}
+
+/*
+ * Helper to prepare DMA descriptors used to configure registers needed for
+ * before writing a NAND page.
+ */
+static void config_nand_page_write(struct qcom_nand_controller *nandc)
+{
+ write_reg_dma(nandc, NAND_ADDR0, 2, 0);
+ write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0);
+ write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1,
+ NAND_BAM_NEXT_SGL);
+}
+
+/*
+ * Helper to prepare DMA descriptors for configuring registers
+ * before writing each codeword in NAND page.
+ */
+static void config_nand_cw_write(struct qcom_nand_controller *nandc)
+{
+ write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
+
+ read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
+
+ write_reg_dma(nandc, NAND_FLASH_STATUS, 1, 0);
+ write_reg_dma(nandc, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL);
+}
+
+/*
+ * the following functions are used within chip->cmdfunc() to perform different
+ * NAND_CMD_* commands
+ */
+
+/* sets up descriptors for NAND_CMD_PARAM */
+static int nandc_param(struct qcom_nand_host *host)
+{
+ struct nand_chip *chip = &host->chip;
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+
+ /*
+ * NAND_CMD_PARAM is called before we know much about the FLASH chip
+ * in use. we configure the controller to perform a raw read of 512
+ * bytes to read onfi params
+ */
+ nandc_set_reg(nandc, NAND_FLASH_CMD, PAGE_READ | PAGE_ACC | LAST_PAGE);
+ nandc_set_reg(nandc, NAND_ADDR0, 0);
+ nandc_set_reg(nandc, NAND_ADDR1, 0);
+ nandc_set_reg(nandc, NAND_DEV0_CFG0, 0 << CW_PER_PAGE
+ | 512 << UD_SIZE_BYTES
+ | 5 << NUM_ADDR_CYCLES
+ | 0 << SPARE_SIZE_BYTES);
+ nandc_set_reg(nandc, NAND_DEV0_CFG1, 7 << NAND_RECOVERY_CYCLES
+ | 0 << CS_ACTIVE_BSY
+ | 17 << BAD_BLOCK_BYTE_NUM
+ | 1 << BAD_BLOCK_IN_SPARE_AREA
+ | 2 << WR_RD_BSY_GAP
+ | 0 << WIDE_FLASH
+ | 1 << DEV0_CFG1_ECC_DISABLE);
+ nandc_set_reg(nandc, NAND_EBI2_ECC_BUF_CFG, 1 << ECC_CFG_ECC_DISABLE);
+
+ /* configure CMD1 and VLD for ONFI param probing */
+ nandc_set_reg(nandc, NAND_DEV_CMD_VLD,
+ (nandc->vld & ~READ_START_VLD));
+ nandc_set_reg(nandc, NAND_DEV_CMD1,
+ (nandc->cmd1 & ~(0xFF << READ_ADDR))
+ | NAND_CMD_PARAM << READ_ADDR);
+
+ nandc_set_reg(nandc, NAND_EXEC_CMD, 1);
+
+ nandc_set_reg(nandc, NAND_DEV_CMD1_RESTORE, nandc->cmd1);
+ nandc_set_reg(nandc, NAND_DEV_CMD_VLD_RESTORE, nandc->vld);
+ nandc_set_read_loc(nandc, 0, 0, 512, 1);
+
+ write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1, 0);
+ write_reg_dma(nandc, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL);
+
+ nandc->buf_count = 512;
+ memset(nandc->data_buffer, 0xff, nandc->buf_count);
+
+ config_nand_single_cw_page_read(nandc);
+
+ read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer,
+ nandc->buf_count, 0);
+
+ /* restore CMD1 and VLD regs */
+ write_reg_dma(nandc, NAND_DEV_CMD1_RESTORE, 1, 0);
+ write_reg_dma(nandc, NAND_DEV_CMD_VLD_RESTORE, 1, NAND_BAM_NEXT_SGL);
+
+ return 0;
+}
+
+/* sets up descriptors for NAND_CMD_ERASE1 */
+static int erase_block(struct qcom_nand_host *host, int page_addr)
+{
+ struct nand_chip *chip = &host->chip;
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+
+ nandc_set_reg(nandc, NAND_FLASH_CMD,
+ BLOCK_ERASE | PAGE_ACC | LAST_PAGE);
+ nandc_set_reg(nandc, NAND_ADDR0, page_addr);
+ nandc_set_reg(nandc, NAND_ADDR1, 0);
+ nandc_set_reg(nandc, NAND_DEV0_CFG0,
+ host->cfg0_raw & ~(7 << CW_PER_PAGE));
+ nandc_set_reg(nandc, NAND_DEV0_CFG1, host->cfg1_raw);
+ nandc_set_reg(nandc, NAND_EXEC_CMD, 1);
+ nandc_set_reg(nandc, NAND_FLASH_STATUS, host->clrflashstatus);
+ nandc_set_reg(nandc, NAND_READ_STATUS, host->clrreadstatus);
+
+ write_reg_dma(nandc, NAND_FLASH_CMD, 3, NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
+
+ read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
+
+ write_reg_dma(nandc, NAND_FLASH_STATUS, 1, 0);
+ write_reg_dma(nandc, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL);
+
+ return 0;
+}
+
+/* sets up descriptors for NAND_CMD_READID */
+static int read_id(struct qcom_nand_host *host, int column)
+{
+ struct nand_chip *chip = &host->chip;
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+
+ if (column == -1)
+ return 0;
+
+ nandc_set_reg(nandc, NAND_FLASH_CMD, FETCH_ID);
+ nandc_set_reg(nandc, NAND_ADDR0, column);
+ nandc_set_reg(nandc, NAND_ADDR1, 0);
+ nandc_set_reg(nandc, NAND_FLASH_CHIP_SELECT,
+ nandc->props->is_bam ? 0 : DM_EN);
+ nandc_set_reg(nandc, NAND_EXEC_CMD, 1);
+
+ write_reg_dma(nandc, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
+
+ read_reg_dma(nandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL);
+
+ return 0;
+}
+
+/* sets up descriptors for NAND_CMD_RESET */
+static int reset(struct qcom_nand_host *host)
+{
+ struct nand_chip *chip = &host->chip;
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+
+ nandc_set_reg(nandc, NAND_FLASH_CMD, RESET_DEVICE);
+ nandc_set_reg(nandc, NAND_EXEC_CMD, 1);
+
+ write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
+
+ read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
+
+ return 0;
+}
+
+/* helpers to submit/free our list of dma descriptors */
+static int submit_descs(struct qcom_nand_controller *nandc)
+{
+ struct desc_info *desc;
+ dma_cookie_t cookie = 0;
+ struct bam_transaction *bam_txn = nandc->bam_txn;
+ int r;
+
+ if (nandc->props->is_bam) {
+ if (bam_txn->rx_sgl_pos > bam_txn->rx_sgl_start) {
+ r = prepare_bam_async_desc(nandc, nandc->rx_chan, 0);
+ if (r)
+ return r;
+ }
+
+ if (bam_txn->tx_sgl_pos > bam_txn->tx_sgl_start) {
+ r = prepare_bam_async_desc(nandc, nandc->tx_chan,
+ DMA_PREP_INTERRUPT);
+ if (r)
+ return r;
+ }
+
+ if (bam_txn->cmd_sgl_pos > bam_txn->cmd_sgl_start) {
+ r = prepare_bam_async_desc(nandc, nandc->cmd_chan,
+ DMA_PREP_CMD);
+ if (r)
+ return r;
+ }
+ }
+
+ list_for_each_entry(desc, &nandc->desc_list, node)
+ cookie = dmaengine_submit(desc->dma_desc);
+
+ if (nandc->props->is_bam) {
+ dma_async_issue_pending(nandc->tx_chan);
+ dma_async_issue_pending(nandc->rx_chan);
+
+ if (dma_sync_wait(nandc->cmd_chan, cookie) != DMA_COMPLETE)
+ return -ETIMEDOUT;
+ } else {
+ if (dma_sync_wait(nandc->chan, cookie) != DMA_COMPLETE)
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static void free_descs(struct qcom_nand_controller *nandc)
+{
+ struct desc_info *desc, *n;
+
+ list_for_each_entry_safe(desc, n, &nandc->desc_list, node) {
+ list_del(&desc->node);
+
+ if (nandc->props->is_bam)
+ dma_unmap_sg(nandc->dev, desc->bam_sgl,
+ desc->sgl_cnt, desc->dir);
+ else
+ dma_unmap_sg(nandc->dev, &desc->adm_sgl, 1,
+ desc->dir);
+
+ kfree(desc);
+ }
+}
+
+/* reset the register read buffer for next NAND operation */
+static void clear_read_regs(struct qcom_nand_controller *nandc)
+{
+ nandc->reg_read_pos = 0;
+ nandc_read_buffer_sync(nandc, false);
+}
+
+static void pre_command(struct qcom_nand_host *host, int command)
+{
+ struct nand_chip *chip = &host->chip;
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+
+ nandc->buf_count = 0;
+ nandc->buf_start = 0;
+ host->use_ecc = false;
+ host->last_command = command;
+
+ clear_read_regs(nandc);
+
+ if (command == NAND_CMD_RESET || command == NAND_CMD_READID ||
+ command == NAND_CMD_PARAM || command == NAND_CMD_ERASE1)
+ clear_bam_transaction(nandc);
+}
+
+/*
+ * this is called after NAND_CMD_PAGEPROG and NAND_CMD_ERASE1 to set our
+ * privately maintained status byte, this status byte can be read after
+ * NAND_CMD_STATUS is called
+ */
+static void parse_erase_write_errors(struct qcom_nand_host *host, int command)
+{
+ struct nand_chip *chip = &host->chip;
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ int num_cw;
+ int i;
+
+ num_cw = command == NAND_CMD_PAGEPROG ? ecc->steps : 1;
+ nandc_read_buffer_sync(nandc, true);
+
+ for (i = 0; i < num_cw; i++) {
+ u32 flash_status = le32_to_cpu(nandc->reg_read_buf[i]);
+
+ if (flash_status & FS_MPU_ERR)
+ host->status &= ~NAND_STATUS_WP;
+
+ if (flash_status & FS_OP_ERR || (i == (num_cw - 1) &&
+ (flash_status &
+ FS_DEVICE_STS_ERR)))
+ host->status |= NAND_STATUS_FAIL;
+ }
+}
+
+static void post_command(struct qcom_nand_host *host, int command)
+{
+ struct nand_chip *chip = &host->chip;
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+
+ switch (command) {
+ case NAND_CMD_READID:
+ nandc_read_buffer_sync(nandc, true);
+ memcpy(nandc->data_buffer, nandc->reg_read_buf,
+ nandc->buf_count);
+ break;
+ case NAND_CMD_PAGEPROG:
+ case NAND_CMD_ERASE1:
+ parse_erase_write_errors(host, command);
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Implements chip->cmdfunc. It's only used for a limited set of commands.
+ * The rest of the commands wouldn't be called by upper layers. For example,
+ * NAND_CMD_READOOB would never be called because we have our own versions
+ * of read_oob ops for nand_ecc_ctrl.
+ */
+static void qcom_nandc_command(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct qcom_nand_host *host = to_qcom_nand_host(chip);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+ bool wait = false;
+ int ret = 0;
+
+ pre_command(host, command);
+
+ switch (command) {
+ case NAND_CMD_RESET:
+ ret = reset(host);
+ wait = true;
+ break;
+
+ case NAND_CMD_READID:
+ nandc->buf_count = 4;
+ ret = read_id(host, column);
+ wait = true;
+ break;
+
+ case NAND_CMD_PARAM:
+ ret = nandc_param(host);
+ wait = true;
+ break;
+
+ case NAND_CMD_ERASE1:
+ ret = erase_block(host, page_addr);
+ wait = true;
+ break;
+
+ case NAND_CMD_READ0:
+ /* we read the entire page for now */
+ WARN_ON(column != 0);
+
+ host->use_ecc = true;
+ set_address(host, 0, page_addr);
+ update_rw_regs(host, ecc->steps, true);
+ break;
+
+ case NAND_CMD_SEQIN:
+ WARN_ON(column != 0);
+ set_address(host, 0, page_addr);
+ break;
+
+ case NAND_CMD_PAGEPROG:
+ case NAND_CMD_STATUS:
+ case NAND_CMD_NONE:
+ default:
+ break;
+ }
+
+ if (ret) {
+ dev_err(nandc->dev, "failure executing command %d\n",
+ command);
+ free_descs(nandc);
+ return;
+ }
+
+ if (wait) {
+ ret = submit_descs(nandc);
+ if (ret)
+ dev_err(nandc->dev,
+ "failure submitting descs for command %d\n",
+ command);
+ }
+
+ free_descs(nandc);
+
+ post_command(host, command);
+}
+
+/*
+ * when using BCH ECC, the HW flags an error in NAND_FLASH_STATUS if it read
+ * an erased CW, and reports an erased CW in NAND_ERASED_CW_DETECT_STATUS.
+ *
+ * when using RS ECC, the HW reports the same erros when reading an erased CW,
+ * but it notifies that it is an erased CW by placing special characters at
+ * certain offsets in the buffer.
+ *
+ * verify if the page is erased or not, and fix up the page for RS ECC by
+ * replacing the special characters with 0xff.
+ */
+static bool erased_chunk_check_and_fixup(u8 *data_buf, int data_len)
+{
+ u8 empty1, empty2;
+
+ /*
+ * an erased page flags an error in NAND_FLASH_STATUS, check if the page
+ * is erased by looking for 0x54s at offsets 3 and 175 from the
+ * beginning of each codeword
+ */
+
+ empty1 = data_buf[3];
+ empty2 = data_buf[175];
+
+ /*
+ * if the erased codework markers, if they exist override them with
+ * 0xffs
+ */
+ if ((empty1 == 0x54 && empty2 == 0xff) ||
+ (empty1 == 0xff && empty2 == 0x54)) {
+ data_buf[3] = 0xff;
+ data_buf[175] = 0xff;
+ }
+
+ /*
+ * check if the entire chunk contains 0xffs or not. if it doesn't, then
+ * restore the original values at the special offsets
+ */
+ if (memchr_inv(data_buf, 0xff, data_len)) {
+ data_buf[3] = empty1;
+ data_buf[175] = empty2;
+
+ return false;
+ }
+
+ return true;
+}
+
+struct read_stats {
+ __le32 flash;
+ __le32 buffer;
+ __le32 erased_cw;
+};
+
+/*
+ * reads back status registers set by the controller to notify page read
+ * errors. this is equivalent to what 'ecc->correct()' would do.
+ */
+static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf,
+ u8 *oob_buf)
+{
+ struct nand_chip *chip = &host->chip;
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ unsigned int max_bitflips = 0;
+ struct read_stats *buf;
+ int i;
+
+ buf = (struct read_stats *)nandc->reg_read_buf;
+ nandc_read_buffer_sync(nandc, true);
+
+ for (i = 0; i < ecc->steps; i++, buf++) {
+ u32 flash, buffer, erased_cw;
+ int data_len, oob_len;
+
+ if (i == (ecc->steps - 1)) {
+ data_len = ecc->size - ((ecc->steps - 1) << 2);
+ oob_len = ecc->steps << 2;
+ } else {
+ data_len = host->cw_data;
+ oob_len = 0;
+ }
+
+ flash = le32_to_cpu(buf->flash);
+ buffer = le32_to_cpu(buf->buffer);
+ erased_cw = le32_to_cpu(buf->erased_cw);
+
+ if (flash & (FS_OP_ERR | FS_MPU_ERR)) {
+ bool erased;
+
+ /* ignore erased codeword errors */
+ if (host->bch_enabled) {
+ erased = (erased_cw & ERASED_CW) == ERASED_CW ?
+ true : false;
+ } else {
+ erased = erased_chunk_check_and_fixup(data_buf,
+ data_len);
+ }
+
+ if (erased) {
+ data_buf += data_len;
+ if (oob_buf)
+ oob_buf += oob_len + ecc->bytes;
+ continue;
+ }
+
+ if (buffer & BS_UNCORRECTABLE_BIT) {
+ int ret, ecclen, extraooblen;
+ void *eccbuf;
+
+ eccbuf = oob_buf ? oob_buf + oob_len : NULL;
+ ecclen = oob_buf ? host->ecc_bytes_hw : 0;
+ extraooblen = oob_buf ? oob_len : 0;
+
+ /*
+ * make sure it isn't an erased page reported
+ * as not-erased by HW because of a few bitflips
+ */
+ ret = nand_check_erased_ecc_chunk(data_buf,
+ data_len, eccbuf, ecclen, oob_buf,
+ extraooblen, ecc->strength);
+ if (ret < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += ret;
+ max_bitflips =
+ max_t(unsigned int, max_bitflips, ret);
+ }
+ }
+ } else {
+ unsigned int stat;
+
+ stat = buffer & BS_CORRECTABLE_ERR_MSK;
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max(max_bitflips, stat);
+ }
+
+ data_buf += data_len;
+ if (oob_buf)
+ oob_buf += oob_len + ecc->bytes;
+ }
+
+ return max_bitflips;
+}
+
+/*
+ * helper to perform the actual page read operation, used by ecc->read_page(),
+ * ecc->read_oob()
+ */
+static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf,
+ u8 *oob_buf)
+{
+ struct nand_chip *chip = &host->chip;
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ int i, ret;
+
+ config_nand_page_read(nandc);
+
+ /* queue cmd descs for each codeword */
+ for (i = 0; i < ecc->steps; i++) {
+ int data_size, oob_size;
+
+ if (i == (ecc->steps - 1)) {
+ data_size = ecc->size - ((ecc->steps - 1) << 2);
+ oob_size = (ecc->steps << 2) + host->ecc_bytes_hw +
+ host->spare_bytes;
+ } else {
+ data_size = host->cw_data;
+ oob_size = host->ecc_bytes_hw + host->spare_bytes;
+ }
+
+ if (nandc->props->is_bam) {
+ if (data_buf && oob_buf) {
+ nandc_set_read_loc(nandc, 0, 0, data_size, 0);
+ nandc_set_read_loc(nandc, 1, data_size,
+ oob_size, 1);
+ } else if (data_buf) {
+ nandc_set_read_loc(nandc, 0, 0, data_size, 1);
+ } else {
+ nandc_set_read_loc(nandc, 0, data_size,
+ oob_size, 1);
+ }
+ }
+
+ config_nand_cw_read(nandc);
+
+ if (data_buf)
+ read_data_dma(nandc, FLASH_BUF_ACC, data_buf,
+ data_size, 0);
+
+ /*
+ * when ecc is enabled, the controller doesn't read the real
+ * or dummy bad block markers in each chunk. To maintain a
+ * consistent layout across RAW and ECC reads, we just
+ * leave the real/dummy BBM offsets empty (i.e, filled with
+ * 0xffs)
+ */
+ if (oob_buf) {
+ int j;
+
+ for (j = 0; j < host->bbm_size; j++)
+ *oob_buf++ = 0xff;
+
+ read_data_dma(nandc, FLASH_BUF_ACC + data_size,
+ oob_buf, oob_size, 0);
+ }
+
+ if (data_buf)
+ data_buf += data_size;
+ if (oob_buf)
+ oob_buf += oob_size;
+ }
+
+ ret = submit_descs(nandc);
+ if (ret)
+ dev_err(nandc->dev, "failure to read page/oob\n");
+
+ free_descs(nandc);
+
+ return ret;
+}
+
+/*
+ * a helper that copies the last step/codeword of a page (containing free oob)
+ * into our local buffer
+ */
+static int copy_last_cw(struct qcom_nand_host *host, int page)
+{
+ struct nand_chip *chip = &host->chip;
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ int size;
+ int ret;
+
+ clear_read_regs(nandc);
+
+ size = host->use_ecc ? host->cw_data : host->cw_size;
+
+ /* prepare a clean read buffer */
+ memset(nandc->data_buffer, 0xff, size);
+
+ set_address(host, host->cw_size * (ecc->steps - 1), page);
+ update_rw_regs(host, 1, true);
+
+ config_nand_single_cw_page_read(nandc);
+
+ read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0);
+
+ ret = submit_descs(nandc);
+ if (ret)
+ dev_err(nandc->dev, "failed to copy last codeword\n");
+
+ free_descs(nandc);
+
+ return ret;
+}
+
+/* implements ecc->read_page() */
+static int qcom_nandc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ struct qcom_nand_host *host = to_qcom_nand_host(chip);
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+ u8 *data_buf, *oob_buf = NULL;
+ int ret;
+
+ nand_read_page_op(chip, page, 0, NULL, 0);
+ data_buf = buf;
+ oob_buf = oob_required ? chip->oob_poi : NULL;
+
+ clear_bam_transaction(nandc);
+ ret = read_page_ecc(host, data_buf, oob_buf);
+ if (ret) {
+ dev_err(nandc->dev, "failure to read page\n");
+ return ret;
+ }
+
+ return parse_read_errors(host, data_buf, oob_buf);
+}
+
+/* implements ecc->read_page_raw() */
+static int qcom_nandc_read_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf,
+ int oob_required, int page)
+{
+ struct qcom_nand_host *host = to_qcom_nand_host(chip);
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+ u8 *data_buf, *oob_buf;
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ int i, ret;
+ int read_loc;
+
+ nand_read_page_op(chip, page, 0, NULL, 0);
+ data_buf = buf;
+ oob_buf = chip->oob_poi;
+
+ host->use_ecc = false;
+
+ clear_bam_transaction(nandc);
+ update_rw_regs(host, ecc->steps, true);
+ config_nand_page_read(nandc);
+
+ for (i = 0; i < ecc->steps; i++) {
+ int data_size1, data_size2, oob_size1, oob_size2;
+ int reg_off = FLASH_BUF_ACC;
+
+ data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1);
+ oob_size1 = host->bbm_size;
+
+ if (i == (ecc->steps - 1)) {
+ data_size2 = ecc->size - data_size1 -
+ ((ecc->steps - 1) << 2);
+ oob_size2 = (ecc->steps << 2) + host->ecc_bytes_hw +
+ host->spare_bytes;
+ } else {
+ data_size2 = host->cw_data - data_size1;
+ oob_size2 = host->ecc_bytes_hw + host->spare_bytes;
+ }
+
+ if (nandc->props->is_bam) {
+ read_loc = 0;
+ nandc_set_read_loc(nandc, 0, read_loc, data_size1, 0);
+ read_loc += data_size1;
+
+ nandc_set_read_loc(nandc, 1, read_loc, oob_size1, 0);
+ read_loc += oob_size1;
+
+ nandc_set_read_loc(nandc, 2, read_loc, data_size2, 0);
+ read_loc += data_size2;
+
+ nandc_set_read_loc(nandc, 3, read_loc, oob_size2, 1);
+ }
+
+ config_nand_cw_read(nandc);
+
+ read_data_dma(nandc, reg_off, data_buf, data_size1, 0);
+ reg_off += data_size1;
+ data_buf += data_size1;
+
+ read_data_dma(nandc, reg_off, oob_buf, oob_size1, 0);
+ reg_off += oob_size1;
+ oob_buf += oob_size1;
+
+ read_data_dma(nandc, reg_off, data_buf, data_size2, 0);
+ reg_off += data_size2;
+ data_buf += data_size2;
+
+ read_data_dma(nandc, reg_off, oob_buf, oob_size2, 0);
+ oob_buf += oob_size2;
+ }
+
+ ret = submit_descs(nandc);
+ if (ret)
+ dev_err(nandc->dev, "failure to read raw page\n");
+
+ free_descs(nandc);
+
+ return 0;
+}
+
+/* implements ecc->read_oob() */
+static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ struct qcom_nand_host *host = to_qcom_nand_host(chip);
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ int ret;
+
+ clear_read_regs(nandc);
+ clear_bam_transaction(nandc);
+
+ host->use_ecc = true;
+ set_address(host, 0, page);
+ update_rw_regs(host, ecc->steps, true);
+
+ ret = read_page_ecc(host, NULL, chip->oob_poi);
+ if (ret)
+ dev_err(nandc->dev, "failure to read oob\n");
+
+ return ret;
+}
+
+/* implements ecc->write_page() */
+static int qcom_nandc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required, int page)
+{
+ struct qcom_nand_host *host = to_qcom_nand_host(chip);
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ u8 *data_buf, *oob_buf;
+ int i, ret;
+
+ nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+
+ clear_read_regs(nandc);
+ clear_bam_transaction(nandc);
+
+ data_buf = (u8 *)buf;
+ oob_buf = chip->oob_poi;
+
+ host->use_ecc = true;
+ update_rw_regs(host, ecc->steps, false);
+ config_nand_page_write(nandc);
+
+ for (i = 0; i < ecc->steps; i++) {
+ int data_size, oob_size;
+
+ if (i == (ecc->steps - 1)) {
+ data_size = ecc->size - ((ecc->steps - 1) << 2);
+ oob_size = (ecc->steps << 2) + host->ecc_bytes_hw +
+ host->spare_bytes;
+ } else {
+ data_size = host->cw_data;
+ oob_size = ecc->bytes;
+ }
+
+
+ write_data_dma(nandc, FLASH_BUF_ACC, data_buf, data_size,
+ i == (ecc->steps - 1) ? NAND_BAM_NO_EOT : 0);
+
+ /*
+ * when ECC is enabled, we don't really need to write anything
+ * to oob for the first n - 1 codewords since these oob regions
+ * just contain ECC bytes that's written by the controller
+ * itself. For the last codeword, we skip the bbm positions and
+ * write to the free oob area.
+ */
+ if (i == (ecc->steps - 1)) {
+ oob_buf += host->bbm_size;
+
+ write_data_dma(nandc, FLASH_BUF_ACC + data_size,
+ oob_buf, oob_size, 0);
+ }
+
+ config_nand_cw_write(nandc);
+
+ data_buf += data_size;
+ oob_buf += oob_size;
+ }
+
+ ret = submit_descs(nandc);
+ if (ret)
+ dev_err(nandc->dev, "failure to write page\n");
+
+ free_descs(nandc);
+
+ if (!ret)
+ ret = nand_prog_page_end_op(chip);
+
+ return ret;
+}
+
+/* implements ecc->write_page_raw() */
+static int qcom_nandc_write_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf,
+ int oob_required, int page)
+{
+ struct qcom_nand_host *host = to_qcom_nand_host(chip);
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ u8 *data_buf, *oob_buf;
+ int i, ret;
+
+ nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+ clear_read_regs(nandc);
+ clear_bam_transaction(nandc);
+
+ data_buf = (u8 *)buf;
+ oob_buf = chip->oob_poi;
+
+ host->use_ecc = false;
+ update_rw_regs(host, ecc->steps, false);
+ config_nand_page_write(nandc);
+
+ for (i = 0; i < ecc->steps; i++) {
+ int data_size1, data_size2, oob_size1, oob_size2;
+ int reg_off = FLASH_BUF_ACC;
+
+ data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1);
+ oob_size1 = host->bbm_size;
+
+ if (i == (ecc->steps - 1)) {
+ data_size2 = ecc->size - data_size1 -
+ ((ecc->steps - 1) << 2);
+ oob_size2 = (ecc->steps << 2) + host->ecc_bytes_hw +
+ host->spare_bytes;
+ } else {
+ data_size2 = host->cw_data - data_size1;
+ oob_size2 = host->ecc_bytes_hw + host->spare_bytes;
+ }
+
+ write_data_dma(nandc, reg_off, data_buf, data_size1,
+ NAND_BAM_NO_EOT);
+ reg_off += data_size1;
+ data_buf += data_size1;
+
+ write_data_dma(nandc, reg_off, oob_buf, oob_size1,
+ NAND_BAM_NO_EOT);
+ reg_off += oob_size1;
+ oob_buf += oob_size1;
+
+ write_data_dma(nandc, reg_off, data_buf, data_size2,
+ NAND_BAM_NO_EOT);
+ reg_off += data_size2;
+ data_buf += data_size2;
+
+ write_data_dma(nandc, reg_off, oob_buf, oob_size2, 0);
+ oob_buf += oob_size2;
+
+ config_nand_cw_write(nandc);
+ }
+
+ ret = submit_descs(nandc);
+ if (ret)
+ dev_err(nandc->dev, "failure to write raw page\n");
+
+ free_descs(nandc);
+
+ if (!ret)
+ ret = nand_prog_page_end_op(chip);
+
+ return ret;
+}
+
+/*
+ * implements ecc->write_oob()
+ *
+ * the NAND controller cannot write only data or only oob within a codeword,
+ * since ecc is calculated for the combined codeword. we first copy the
+ * entire contents for the last codeword(data + oob), replace the old oob
+ * with the new one in chip->oob_poi, and then write the entire codeword.
+ * this read-copy-write operation results in a slight performance loss.
+ */
+static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ struct qcom_nand_host *host = to_qcom_nand_host(chip);
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ u8 *oob = chip->oob_poi;
+ int data_size, oob_size;
+ int ret;
+
+ host->use_ecc = true;
+
+ clear_bam_transaction(nandc);
+ ret = copy_last_cw(host, page);
+ if (ret)
+ return ret;
+
+ clear_read_regs(nandc);
+ clear_bam_transaction(nandc);
+
+ /* calculate the data and oob size for the last codeword/step */
+ data_size = ecc->size - ((ecc->steps - 1) << 2);
+ oob_size = mtd->oobavail;
+
+ /* override new oob content to last codeword */
+ mtd_ooblayout_get_databytes(mtd, nandc->data_buffer + data_size, oob,
+ 0, mtd->oobavail);
+
+ set_address(host, host->cw_size * (ecc->steps - 1), page);
+ update_rw_regs(host, 1, false);
+
+ config_nand_page_write(nandc);
+ write_data_dma(nandc, FLASH_BUF_ACC,
+ nandc->data_buffer, data_size + oob_size, 0);
+ config_nand_cw_write(nandc);
+
+ ret = submit_descs(nandc);
+
+ free_descs(nandc);
+
+ if (ret) {
+ dev_err(nandc->dev, "failure to write oob\n");
+ return -EIO;
+ }
+
+ return nand_prog_page_end_op(chip);
+}
+
+static int qcom_nandc_block_bad(struct mtd_info *mtd, loff_t ofs)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct qcom_nand_host *host = to_qcom_nand_host(chip);
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ int page, ret, bbpos, bad = 0;
+ u32 flash_status;
+
+ page = (int)(ofs >> chip->page_shift) & chip->pagemask;
+
+ /*
+ * configure registers for a raw sub page read, the address is set to
+ * the beginning of the last codeword, we don't care about reading ecc
+ * portion of oob. we just want the first few bytes from this codeword
+ * that contains the BBM
+ */
+ host->use_ecc = false;
+
+ clear_bam_transaction(nandc);
+ ret = copy_last_cw(host, page);
+ if (ret)
+ goto err;
+
+ flash_status = le32_to_cpu(nandc->reg_read_buf[0]);
+
+ if (flash_status & (FS_OP_ERR | FS_MPU_ERR)) {
+ dev_warn(nandc->dev, "error when trying to read BBM\n");
+ goto err;
+ }
+
+ bbpos = mtd->writesize - host->cw_size * (ecc->steps - 1);
+
+ bad = nandc->data_buffer[bbpos] != 0xff;
+
+ if (chip->options & NAND_BUSWIDTH_16)
+ bad = bad || (nandc->data_buffer[bbpos + 1] != 0xff);
+err:
+ return bad;
+}
+
+static int qcom_nandc_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct qcom_nand_host *host = to_qcom_nand_host(chip);
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ int page, ret;
+
+ clear_read_regs(nandc);
+ clear_bam_transaction(nandc);
+
+ /*
+ * to mark the BBM as bad, we flash the entire last codeword with 0s.
+ * we don't care about the rest of the content in the codeword since
+ * we aren't going to use this block again
+ */
+ memset(nandc->data_buffer, 0x00, host->cw_size);
+
+ page = (int)(ofs >> chip->page_shift) & chip->pagemask;
+
+ /* prepare write */
+ host->use_ecc = false;
+ set_address(host, host->cw_size * (ecc->steps - 1), page);
+ update_rw_regs(host, 1, false);
+
+ config_nand_page_write(nandc);
+ write_data_dma(nandc, FLASH_BUF_ACC,
+ nandc->data_buffer, host->cw_size, 0);
+ config_nand_cw_write(nandc);
+
+ ret = submit_descs(nandc);
+
+ free_descs(nandc);
+
+ if (ret) {
+ dev_err(nandc->dev, "failure to update BBM\n");
+ return -EIO;
+ }
+
+ return nand_prog_page_end_op(chip);
+}
+
+/*
+ * the three functions below implement chip->read_byte(), chip->read_buf()
+ * and chip->write_buf() respectively. these aren't used for
+ * reading/writing page data, they are used for smaller data like reading
+ * id, status etc
+ */
+static uint8_t qcom_nandc_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct qcom_nand_host *host = to_qcom_nand_host(chip);
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+ u8 *buf = nandc->data_buffer;
+ u8 ret = 0x0;
+
+ if (host->last_command == NAND_CMD_STATUS) {
+ ret = host->status;
+
+ host->status = NAND_STATUS_READY | NAND_STATUS_WP;
+
+ return ret;
+ }
+
+ if (nandc->buf_start < nandc->buf_count)
+ ret = buf[nandc->buf_start++];
+
+ return ret;
+}
+
+static void qcom_nandc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+ int real_len = min_t(size_t, len, nandc->buf_count - nandc->buf_start);
+
+ memcpy(buf, nandc->data_buffer + nandc->buf_start, real_len);
+ nandc->buf_start += real_len;
+}
+
+static void qcom_nandc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
+ int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+ int real_len = min_t(size_t, len, nandc->buf_count - nandc->buf_start);
+
+ memcpy(nandc->data_buffer + nandc->buf_start, buf, real_len);
+
+ nandc->buf_start += real_len;
+}
+
+/* we support only one external chip for now */
+static void qcom_nandc_select_chip(struct mtd_info *mtd, int chipnr)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+
+ if (chipnr <= 0)
+ return;
+
+ dev_warn(nandc->dev, "invalid chip select\n");
+}
+
+/*
+ * NAND controller page layout info
+ *
+ * Layout with ECC enabled:
+ *
+ * |----------------------| |---------------------------------|
+ * | xx.......yy| | *********xx.......yy|
+ * | DATA xx..ECC..yy| | DATA **SPARE**xx..ECC..yy|
+ * | (516) xx.......yy| | (516-n*4) **(n*4)**xx.......yy|
+ * | xx.......yy| | *********xx.......yy|
+ * |----------------------| |---------------------------------|
+ * codeword 1,2..n-1 codeword n
+ * <---(528/532 Bytes)--> <-------(528/532 Bytes)--------->
+ *
+ * n = Number of codewords in the page
+ * . = ECC bytes
+ * * = Spare/free bytes
+ * x = Unused byte(s)
+ * y = Reserved byte(s)
+ *
+ * 2K page: n = 4, spare = 16 bytes
+ * 4K page: n = 8, spare = 32 bytes
+ * 8K page: n = 16, spare = 64 bytes
+ *
+ * the qcom nand controller operates at a sub page/codeword level. each
+ * codeword is 528 and 532 bytes for 4 bit and 8 bit ECC modes respectively.
+ * the number of ECC bytes vary based on the ECC strength and the bus width.
+ *
+ * the first n - 1 codewords contains 516 bytes of user data, the remaining
+ * 12/16 bytes consist of ECC and reserved data. The nth codeword contains
+ * both user data and spare(oobavail) bytes that sum up to 516 bytes.
+ *
+ * When we access a page with ECC enabled, the reserved bytes(s) are not
+ * accessible at all. When reading, we fill up these unreadable positions
+ * with 0xffs. When writing, the controller skips writing the inaccessible
+ * bytes.
+ *
+ * Layout with ECC disabled:
+ *
+ * |------------------------------| |---------------------------------------|
+ * | yy xx.......| | bb *********xx.......|
+ * | DATA1 yy DATA2 xx..ECC..| | DATA1 bb DATA2 **SPARE**xx..ECC..|
+ * | (size1) yy (size2) xx.......| | (size1) bb (size2) **(n*4)**xx.......|
+ * | yy xx.......| | bb *********xx.......|
+ * |------------------------------| |---------------------------------------|
+ * codeword 1,2..n-1 codeword n
+ * <-------(528/532 Bytes)------> <-----------(528/532 Bytes)----------->
+ *
+ * n = Number of codewords in the page
+ * . = ECC bytes
+ * * = Spare/free bytes
+ * x = Unused byte(s)
+ * y = Dummy Bad Bock byte(s)
+ * b = Real Bad Block byte(s)
+ * size1/size2 = function of codeword size and 'n'
+ *
+ * when the ECC block is disabled, one reserved byte (or two for 16 bit bus
+ * width) is now accessible. For the first n - 1 codewords, these are dummy Bad
+ * Block Markers. In the last codeword, this position contains the real BBM
+ *
+ * In order to have a consistent layout between RAW and ECC modes, we assume
+ * the following OOB layout arrangement:
+ *
+ * |-----------| |--------------------|
+ * |yyxx.......| |bb*********xx.......|
+ * |yyxx..ECC..| |bb*FREEOOB*xx..ECC..|
+ * |yyxx.......| |bb*********xx.......|
+ * |yyxx.......| |bb*********xx.......|
+ * |-----------| |--------------------|
+ * first n - 1 nth OOB region
+ * OOB regions
+ *
+ * n = Number of codewords in the page
+ * . = ECC bytes
+ * * = FREE OOB bytes
+ * y = Dummy bad block byte(s) (inaccessible when ECC enabled)
+ * x = Unused byte(s)
+ * b = Real bad block byte(s) (inaccessible when ECC enabled)
+ *
+ * This layout is read as is when ECC is disabled. When ECC is enabled, the
+ * inaccessible Bad Block byte(s) are ignored when we write to a page/oob,
+ * and assumed as 0xffs when we read a page/oob. The ECC, unused and
+ * dummy/real bad block bytes are grouped as ecc bytes (i.e, ecc->bytes is
+ * the sum of the three).
+ */
+static int qcom_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct qcom_nand_host *host = to_qcom_nand_host(chip);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+
+ if (section > 1)
+ return -ERANGE;
+
+ if (!section) {
+ oobregion->length = (ecc->bytes * (ecc->steps - 1)) +
+ host->bbm_size;
+ oobregion->offset = 0;
+ } else {
+ oobregion->length = host->ecc_bytes_hw + host->spare_bytes;
+ oobregion->offset = mtd->oobsize - oobregion->length;
+ }
+
+ return 0;
+}
+
+static int qcom_nand_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct qcom_nand_host *host = to_qcom_nand_host(chip);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+
+ if (section)
+ return -ERANGE;
+
+ oobregion->length = ecc->steps * 4;
+ oobregion->offset = ((ecc->steps - 1) * ecc->bytes) + host->bbm_size;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops qcom_nand_ooblayout_ops = {
+ .ecc = qcom_nand_ooblayout_ecc,
+ .free = qcom_nand_ooblayout_free,
+};
+
+static int qcom_nand_host_setup(struct qcom_nand_host *host)
+{
+ struct nand_chip *chip = &host->chip;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+ int cwperpage, bad_block_byte;
+ bool wide_bus;
+ int ecc_mode = 1;
+
+ /*
+ * the controller requires each step consists of 512 bytes of data.
+ * bail out if DT has populated a wrong step size.
+ */
+ if (ecc->size != NANDC_STEP_SIZE) {
+ dev_err(nandc->dev, "invalid ecc size\n");
+ return -EINVAL;
+ }
+
+ wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false;
+
+ if (ecc->strength >= 8) {
+ /* 8 bit ECC defaults to BCH ECC on all platforms */
+ host->bch_enabled = true;
+ ecc_mode = 1;
+
+ if (wide_bus) {
+ host->ecc_bytes_hw = 14;
+ host->spare_bytes = 0;
+ host->bbm_size = 2;
+ } else {
+ host->ecc_bytes_hw = 13;
+ host->spare_bytes = 2;
+ host->bbm_size = 1;
+ }
+ } else {
+ /*
+ * if the controller supports BCH for 4 bit ECC, the controller
+ * uses lesser bytes for ECC. If RS is used, the ECC bytes is
+ * always 10 bytes
+ */
+ if (nandc->props->ecc_modes & ECC_BCH_4BIT) {
+ /* BCH */
+ host->bch_enabled = true;
+ ecc_mode = 0;
+
+ if (wide_bus) {
+ host->ecc_bytes_hw = 8;
+ host->spare_bytes = 2;
+ host->bbm_size = 2;
+ } else {
+ host->ecc_bytes_hw = 7;
+ host->spare_bytes = 4;
+ host->bbm_size = 1;
+ }
+ } else {
+ /* RS */
+ host->ecc_bytes_hw = 10;
+
+ if (wide_bus) {
+ host->spare_bytes = 0;
+ host->bbm_size = 2;
+ } else {
+ host->spare_bytes = 1;
+ host->bbm_size = 1;
+ }
+ }
+ }
+
+ /*
+ * we consider ecc->bytes as the sum of all the non-data content in a
+ * step. It gives us a clean representation of the oob area (even if
+ * all the bytes aren't used for ECC).It is always 16 bytes for 8 bit
+ * ECC and 12 bytes for 4 bit ECC
+ */
+ ecc->bytes = host->ecc_bytes_hw + host->spare_bytes + host->bbm_size;
+
+ ecc->read_page = qcom_nandc_read_page;
+ ecc->read_page_raw = qcom_nandc_read_page_raw;
+ ecc->read_oob = qcom_nandc_read_oob;
+ ecc->write_page = qcom_nandc_write_page;
+ ecc->write_page_raw = qcom_nandc_write_page_raw;
+ ecc->write_oob = qcom_nandc_write_oob;
+
+ ecc->mode = NAND_ECC_HW;
+
+ mtd_set_ooblayout(mtd, &qcom_nand_ooblayout_ops);
+
+ cwperpage = mtd->writesize / ecc->size;
+ nandc->max_cwperpage = max_t(unsigned int, nandc->max_cwperpage,
+ cwperpage);
+
+ /*
+ * DATA_UD_BYTES varies based on whether the read/write command protects
+ * spare data with ECC too. We protect spare data by default, so we set
+ * it to main + spare data, which are 512 and 4 bytes respectively.
+ */
+ host->cw_data = 516;
+
+ /*
+ * total bytes in a step, either 528 bytes for 4 bit ECC, or 532 bytes
+ * for 8 bit ECC
+ */
+ host->cw_size = host->cw_data + ecc->bytes;
+
+ if (ecc->bytes * (mtd->writesize / ecc->size) > mtd->oobsize) {
+ dev_err(nandc->dev, "ecc data doesn't fit in OOB area\n");
+ return -EINVAL;
+ }
+
+ bad_block_byte = mtd->writesize - host->cw_size * (cwperpage - 1) + 1;
+
+ host->cfg0 = (cwperpage - 1) << CW_PER_PAGE
+ | host->cw_data << UD_SIZE_BYTES
+ | 0 << DISABLE_STATUS_AFTER_WRITE
+ | 5 << NUM_ADDR_CYCLES
+ | host->ecc_bytes_hw << ECC_PARITY_SIZE_BYTES_RS
+ | 0 << STATUS_BFR_READ
+ | 1 << SET_RD_MODE_AFTER_STATUS
+ | host->spare_bytes << SPARE_SIZE_BYTES;
+
+ host->cfg1 = 7 << NAND_RECOVERY_CYCLES
+ | 0 << CS_ACTIVE_BSY
+ | bad_block_byte << BAD_BLOCK_BYTE_NUM
+ | 0 << BAD_BLOCK_IN_SPARE_AREA
+ | 2 << WR_RD_BSY_GAP
+ | wide_bus << WIDE_FLASH
+ | host->bch_enabled << ENABLE_BCH_ECC;
+
+ host->cfg0_raw = (cwperpage - 1) << CW_PER_PAGE
+ | host->cw_size << UD_SIZE_BYTES
+ | 5 << NUM_ADDR_CYCLES
+ | 0 << SPARE_SIZE_BYTES;
+
+ host->cfg1_raw = 7 << NAND_RECOVERY_CYCLES
+ | 0 << CS_ACTIVE_BSY
+ | 17 << BAD_BLOCK_BYTE_NUM
+ | 1 << BAD_BLOCK_IN_SPARE_AREA
+ | 2 << WR_RD_BSY_GAP
+ | wide_bus << WIDE_FLASH
+ | 1 << DEV0_CFG1_ECC_DISABLE;
+
+ host->ecc_bch_cfg = !host->bch_enabled << ECC_CFG_ECC_DISABLE
+ | 0 << ECC_SW_RESET
+ | host->cw_data << ECC_NUM_DATA_BYTES
+ | 1 << ECC_FORCE_CLK_OPEN
+ | ecc_mode << ECC_MODE
+ | host->ecc_bytes_hw << ECC_PARITY_SIZE_BYTES_BCH;
+
+ host->ecc_buf_cfg = 0x203 << NUM_STEPS;
+
+ host->clrflashstatus = FS_READY_BSY_N;
+ host->clrreadstatus = 0xc0;
+ nandc->regs->erased_cw_detect_cfg_clr =
+ cpu_to_le32(CLR_ERASED_PAGE_DET);
+ nandc->regs->erased_cw_detect_cfg_set =
+ cpu_to_le32(SET_ERASED_PAGE_DET);
+
+ dev_dbg(nandc->dev,
+ "cfg0 %x cfg1 %x ecc_buf_cfg %x ecc_bch cfg %x cw_size %d cw_data %d strength %d parity_bytes %d steps %d\n",
+ host->cfg0, host->cfg1, host->ecc_buf_cfg, host->ecc_bch_cfg,
+ host->cw_size, host->cw_data, ecc->strength, ecc->bytes,
+ cwperpage);
+
+ return 0;
+}
+
+static int qcom_nandc_alloc(struct qcom_nand_controller *nandc)
+{
+ int ret;
+
+ ret = dma_set_coherent_mask(nandc->dev, DMA_BIT_MASK(32));
+ if (ret) {
+ dev_err(nandc->dev, "failed to set DMA mask\n");
+ return ret;
+ }
+
+ /*
+ * we use the internal buffer for reading ONFI params, reading small
+ * data like ID and status, and preforming read-copy-write operations
+ * when writing to a codeword partially. 532 is the maximum possible
+ * size of a codeword for our nand controller
+ */
+ nandc->buf_size = 532;
+
+ nandc->data_buffer = devm_kzalloc(nandc->dev, nandc->buf_size,
+ GFP_KERNEL);
+ if (!nandc->data_buffer)
+ return -ENOMEM;
+
+ nandc->regs = devm_kzalloc(nandc->dev, sizeof(*nandc->regs),
+ GFP_KERNEL);
+ if (!nandc->regs)
+ return -ENOMEM;
+
+ nandc->reg_read_buf = devm_kzalloc(nandc->dev,
+ MAX_REG_RD * sizeof(*nandc->reg_read_buf),
+ GFP_KERNEL);
+ if (!nandc->reg_read_buf)
+ return -ENOMEM;
+
+ if (nandc->props->is_bam) {
+ nandc->reg_read_dma =
+ dma_map_single(nandc->dev, nandc->reg_read_buf,
+ MAX_REG_RD *
+ sizeof(*nandc->reg_read_buf),
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(nandc->dev, nandc->reg_read_dma)) {
+ dev_err(nandc->dev, "failed to DMA MAP reg buffer\n");
+ return -EIO;
+ }
+
+ nandc->tx_chan = dma_request_slave_channel(nandc->dev, "tx");
+ if (!nandc->tx_chan) {
+ dev_err(nandc->dev, "failed to request tx channel\n");
+ return -ENODEV;
+ }
+
+ nandc->rx_chan = dma_request_slave_channel(nandc->dev, "rx");
+ if (!nandc->rx_chan) {
+ dev_err(nandc->dev, "failed to request rx channel\n");
+ return -ENODEV;
+ }
+
+ nandc->cmd_chan = dma_request_slave_channel(nandc->dev, "cmd");
+ if (!nandc->cmd_chan) {
+ dev_err(nandc->dev, "failed to request cmd channel\n");
+ return -ENODEV;
+ }
+
+ /*
+ * Initially allocate BAM transaction to read ONFI param page.
+ * After detecting all the devices, this BAM transaction will
+ * be freed and the next BAM tranasction will be allocated with
+ * maximum codeword size
+ */
+ nandc->max_cwperpage = 1;
+ nandc->bam_txn = alloc_bam_transaction(nandc);
+ if (!nandc->bam_txn) {
+ dev_err(nandc->dev,
+ "failed to allocate bam transaction\n");
+ return -ENOMEM;
+ }
+ } else {
+ nandc->chan = dma_request_slave_channel(nandc->dev, "rxtx");
+ if (!nandc->chan) {
+ dev_err(nandc->dev,
+ "failed to request slave channel\n");
+ return -ENODEV;
+ }
+ }
+
+ INIT_LIST_HEAD(&nandc->desc_list);
+ INIT_LIST_HEAD(&nandc->host_list);
+
+ nand_hw_control_init(&nandc->controller);
+
+ return 0;
+}
+
+static void qcom_nandc_unalloc(struct qcom_nand_controller *nandc)
+{
+ if (nandc->props->is_bam) {
+ if (!dma_mapping_error(nandc->dev, nandc->reg_read_dma))
+ dma_unmap_single(nandc->dev, nandc->reg_read_dma,
+ MAX_REG_RD *
+ sizeof(*nandc->reg_read_buf),
+ DMA_FROM_DEVICE);
+
+ if (nandc->tx_chan)
+ dma_release_channel(nandc->tx_chan);
+
+ if (nandc->rx_chan)
+ dma_release_channel(nandc->rx_chan);
+
+ if (nandc->cmd_chan)
+ dma_release_channel(nandc->cmd_chan);
+ } else {
+ if (nandc->chan)
+ dma_release_channel(nandc->chan);
+ }
+}
+
+/* one time setup of a few nand controller registers */
+static int qcom_nandc_setup(struct qcom_nand_controller *nandc)
+{
+ u32 nand_ctrl;
+
+ /* kill onenand */
+ nandc_write(nandc, SFLASHC_BURST_CFG, 0);
+ nandc_write(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD),
+ NAND_DEV_CMD_VLD_VAL);
+
+ /* enable ADM or BAM DMA */
+ if (nandc->props->is_bam) {
+ nand_ctrl = nandc_read(nandc, NAND_CTRL);
+ nandc_write(nandc, NAND_CTRL, nand_ctrl | BAM_MODE_EN);
+ } else {
+ nandc_write(nandc, NAND_FLASH_CHIP_SELECT, DM_EN);
+ }
+
+ /* save the original values of these registers */
+ nandc->cmd1 = nandc_read(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD1));
+ nandc->vld = NAND_DEV_CMD_VLD_VAL;
+
+ return 0;
+}
+
+static int qcom_nand_host_init(struct qcom_nand_controller *nandc,
+ struct qcom_nand_host *host,
+ struct device_node *dn)
+{
+ struct nand_chip *chip = &host->chip;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct device *dev = nandc->dev;
+ int ret;
+
+ ret = of_property_read_u32(dn, "reg", &host->cs);
+ if (ret) {
+ dev_err(dev, "can't get chip-select\n");
+ return -ENXIO;
+ }
+
+ nand_set_flash_node(chip, dn);
+ mtd->name = devm_kasprintf(dev, GFP_KERNEL, "qcom_nand.%d", host->cs);
+ if (!mtd->name)
+ return -ENOMEM;
+
+ mtd->owner = THIS_MODULE;
+ mtd->dev.parent = dev;
+
+ chip->cmdfunc = qcom_nandc_command;
+ chip->select_chip = qcom_nandc_select_chip;
+ chip->read_byte = qcom_nandc_read_byte;
+ chip->read_buf = qcom_nandc_read_buf;
+ chip->write_buf = qcom_nandc_write_buf;
+ chip->set_features = nand_get_set_features_notsupp;
+ chip->get_features = nand_get_set_features_notsupp;
+
+ /*
+ * the bad block marker is readable only when we read the last codeword
+ * of a page with ECC disabled. currently, the nand_base and nand_bbt
+ * helpers don't allow us to read BB from a nand chip with ECC
+ * disabled (MTD_OPS_PLACE_OOB is set by default). use the block_bad
+ * and block_markbad helpers until we permanently switch to using
+ * MTD_OPS_RAW for all drivers (with the help of badblockbits)
+ */
+ chip->block_bad = qcom_nandc_block_bad;
+ chip->block_markbad = qcom_nandc_block_markbad;
+
+ chip->controller = &nandc->controller;
+ chip->options |= NAND_NO_SUBPAGE_WRITE | NAND_USE_BOUNCE_BUFFER |
+ NAND_SKIP_BBTSCAN;
+
+ /* set up initial status value */
+ host->status = NAND_STATUS_READY | NAND_STATUS_WP;
+
+ ret = nand_scan_ident(mtd, 1, NULL);
+ if (ret)
+ return ret;
+
+ ret = qcom_nand_host_setup(host);
+
+ return ret;
+}
+
+static int qcom_nand_mtd_register(struct qcom_nand_controller *nandc,
+ struct qcom_nand_host *host,
+ struct device_node *dn)
+{
+ struct nand_chip *chip = &host->chip;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int ret;
+
+ ret = nand_scan_tail(mtd);
+ if (ret)
+ return ret;
+
+ ret = mtd_device_register(mtd, NULL, 0);
+ if (ret)
+ nand_cleanup(mtd_to_nand(mtd));
+
+ return ret;
+}
+
+static int qcom_probe_nand_devices(struct qcom_nand_controller *nandc)
+{
+ struct device *dev = nandc->dev;
+ struct device_node *dn = dev->of_node, *child;
+ struct qcom_nand_host *host, *tmp;
+ int ret;
+
+ for_each_available_child_of_node(dn, child) {
+ host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
+ if (!host) {
+ of_node_put(child);
+ return -ENOMEM;
+ }
+
+ ret = qcom_nand_host_init(nandc, host, child);
+ if (ret) {
+ devm_kfree(dev, host);
+ continue;
+ }
+
+ list_add_tail(&host->node, &nandc->host_list);
+ }
+
+ if (list_empty(&nandc->host_list))
+ return -ENODEV;
+
+ if (nandc->props->is_bam) {
+ free_bam_transaction(nandc);
+ nandc->bam_txn = alloc_bam_transaction(nandc);
+ if (!nandc->bam_txn) {
+ dev_err(nandc->dev,
+ "failed to allocate bam transaction\n");
+ return -ENOMEM;
+ }
+ }
+
+ list_for_each_entry_safe(host, tmp, &nandc->host_list, node) {
+ ret = qcom_nand_mtd_register(nandc, host, child);
+ if (ret) {
+ list_del(&host->node);
+ devm_kfree(dev, host);
+ }
+ }
+
+ if (list_empty(&nandc->host_list))
+ return -ENODEV;
+
+ return 0;
+}
+
+/* parse custom DT properties here */
+static int qcom_nandc_parse_dt(struct platform_device *pdev)
+{
+ struct qcom_nand_controller *nandc = platform_get_drvdata(pdev);
+ struct device_node *np = nandc->dev->of_node;
+ int ret;
+
+ if (!nandc->props->is_bam) {
+ ret = of_property_read_u32(np, "qcom,cmd-crci",
+ &nandc->cmd_crci);
+ if (ret) {
+ dev_err(nandc->dev, "command CRCI unspecified\n");
+ return ret;
+ }
+
+ ret = of_property_read_u32(np, "qcom,data-crci",
+ &nandc->data_crci);
+ if (ret) {
+ dev_err(nandc->dev, "data CRCI unspecified\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int qcom_nandc_probe(struct platform_device *pdev)
+{
+ struct qcom_nand_controller *nandc;
+ const void *dev_data;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ int ret;
+
+ nandc = devm_kzalloc(&pdev->dev, sizeof(*nandc), GFP_KERNEL);
+ if (!nandc)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, nandc);
+ nandc->dev = dev;
+
+ dev_data = of_device_get_match_data(dev);
+ if (!dev_data) {
+ dev_err(&pdev->dev, "failed to get device data\n");
+ return -ENODEV;
+ }
+
+ nandc->props = dev_data;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ nandc->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(nandc->base))
+ return PTR_ERR(nandc->base);
+
+ nandc->base_phys = res->start;
+ nandc->base_dma = phys_to_dma(dev, (phys_addr_t)res->start);
+
+ nandc->core_clk = devm_clk_get(dev, "core");
+ if (IS_ERR(nandc->core_clk))
+ return PTR_ERR(nandc->core_clk);
+
+ nandc->aon_clk = devm_clk_get(dev, "aon");
+ if (IS_ERR(nandc->aon_clk))
+ return PTR_ERR(nandc->aon_clk);
+
+ ret = qcom_nandc_parse_dt(pdev);
+ if (ret)
+ return ret;
+
+ ret = qcom_nandc_alloc(nandc);
+ if (ret)
+ goto err_core_clk;
+
+ ret = clk_prepare_enable(nandc->core_clk);
+ if (ret)
+ goto err_core_clk;
+
+ ret = clk_prepare_enable(nandc->aon_clk);
+ if (ret)
+ goto err_aon_clk;
+
+ ret = qcom_nandc_setup(nandc);
+ if (ret)
+ goto err_setup;
+
+ ret = qcom_probe_nand_devices(nandc);
+ if (ret)
+ goto err_setup;
+
+ return 0;
+
+err_setup:
+ clk_disable_unprepare(nandc->aon_clk);
+err_aon_clk:
+ clk_disable_unprepare(nandc->core_clk);
+err_core_clk:
+ qcom_nandc_unalloc(nandc);
+
+ return ret;
+}
+
+static int qcom_nandc_remove(struct platform_device *pdev)
+{
+ struct qcom_nand_controller *nandc = platform_get_drvdata(pdev);
+ struct qcom_nand_host *host;
+
+ list_for_each_entry(host, &nandc->host_list, node)
+ nand_release(nand_to_mtd(&host->chip));
+
+ qcom_nandc_unalloc(nandc);
+
+ clk_disable_unprepare(nandc->aon_clk);
+ clk_disable_unprepare(nandc->core_clk);
+
+ return 0;
+}
+
+static const struct qcom_nandc_props ipq806x_nandc_props = {
+ .ecc_modes = (ECC_RS_4BIT | ECC_BCH_8BIT),
+ .is_bam = false,
+ .dev_cmd_reg_start = 0x0,
+};
+
+static const struct qcom_nandc_props ipq4019_nandc_props = {
+ .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
+ .is_bam = true,
+ .dev_cmd_reg_start = 0x0,
+};
+
+static const struct qcom_nandc_props ipq8074_nandc_props = {
+ .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
+ .is_bam = true,
+ .dev_cmd_reg_start = 0x7000,
+};
+
+/*
+ * data will hold a struct pointer containing more differences once we support
+ * more controller variants
+ */
+static const struct of_device_id qcom_nandc_of_match[] = {
+ {
+ .compatible = "qcom,ipq806x-nand",
+ .data = &ipq806x_nandc_props,
+ },
+ {
+ .compatible = "qcom,ipq4019-nand",
+ .data = &ipq4019_nandc_props,
+ },
+ {
+ .compatible = "qcom,ipq8074-nand",
+ .data = &ipq8074_nandc_props,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, qcom_nandc_of_match);
+
+static struct platform_driver qcom_nandc_driver = {
+ .driver = {
+ .name = "qcom-nandc",
+ .of_match_table = qcom_nandc_of_match,
+ },
+ .probe = qcom_nandc_probe,
+ .remove = qcom_nandc_remove,
+};
+module_platform_driver(qcom_nandc_driver);
+
+MODULE_AUTHOR("Archit Taneja <architt@codeaurora.org>");
+MODULE_DESCRIPTION("Qualcomm NAND Controller driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mtd/nand/raw/r852.c b/drivers/mtd/nand/raw/r852.c
new file mode 100644
index 000000000000..dcdeb0660e5e
--- /dev/null
+++ b/drivers/mtd/nand/raw/r852.c
@@ -0,0 +1,1082 @@
+/*
+ * Copyright © 2009 - Maxim Levitsky
+ * driver for Ricoh xD readers
+ *
+ * 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.
+ */
+
+#define DRV_NAME "r852"
+#define pr_fmt(fmt) DRV_NAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <asm/byteorder.h>
+#include <linux/sched.h>
+#include "sm_common.h"
+#include "r852.h"
+
+
+static bool r852_enable_dma = 1;
+module_param(r852_enable_dma, bool, S_IRUGO);
+MODULE_PARM_DESC(r852_enable_dma, "Enable usage of the DMA (default)");
+
+static int debug;
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug level (0-2)");
+
+/* read register */
+static inline uint8_t r852_read_reg(struct r852_device *dev, int address)
+{
+ uint8_t reg = readb(dev->mmio + address);
+ return reg;
+}
+
+/* write register */
+static inline void r852_write_reg(struct r852_device *dev,
+ int address, uint8_t value)
+{
+ writeb(value, dev->mmio + address);
+ mmiowb();
+}
+
+
+/* read dword sized register */
+static inline uint32_t r852_read_reg_dword(struct r852_device *dev, int address)
+{
+ uint32_t reg = le32_to_cpu(readl(dev->mmio + address));
+ return reg;
+}
+
+/* write dword sized register */
+static inline void r852_write_reg_dword(struct r852_device *dev,
+ int address, uint32_t value)
+{
+ writel(cpu_to_le32(value), dev->mmio + address);
+ mmiowb();
+}
+
+/* returns pointer to our private structure */
+static inline struct r852_device *r852_get_dev(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ return nand_get_controller_data(chip);
+}
+
+
+/* check if controller supports dma */
+static void r852_dma_test(struct r852_device *dev)
+{
+ dev->dma_usable = (r852_read_reg(dev, R852_DMA_CAP) &
+ (R852_DMA1 | R852_DMA2)) == (R852_DMA1 | R852_DMA2);
+
+ if (!dev->dma_usable)
+ message("Non dma capable device detected, dma disabled");
+
+ if (!r852_enable_dma) {
+ message("disabling dma on user request");
+ dev->dma_usable = 0;
+ }
+}
+
+/*
+ * Enable dma. Enables ether first or second stage of the DMA,
+ * Expects dev->dma_dir and dev->dma_state be set
+ */
+static void r852_dma_enable(struct r852_device *dev)
+{
+ uint8_t dma_reg, dma_irq_reg;
+
+ /* Set up dma settings */
+ dma_reg = r852_read_reg_dword(dev, R852_DMA_SETTINGS);
+ dma_reg &= ~(R852_DMA_READ | R852_DMA_INTERNAL | R852_DMA_MEMORY);
+
+ if (dev->dma_dir)
+ dma_reg |= R852_DMA_READ;
+
+ if (dev->dma_state == DMA_INTERNAL) {
+ dma_reg |= R852_DMA_INTERNAL;
+ /* Precaution to make sure HW doesn't write */
+ /* to random kernel memory */
+ r852_write_reg_dword(dev, R852_DMA_ADDR,
+ cpu_to_le32(dev->phys_bounce_buffer));
+ } else {
+ dma_reg |= R852_DMA_MEMORY;
+ r852_write_reg_dword(dev, R852_DMA_ADDR,
+ cpu_to_le32(dev->phys_dma_addr));
+ }
+
+ /* Precaution: make sure write reached the device */
+ r852_read_reg_dword(dev, R852_DMA_ADDR);
+
+ r852_write_reg_dword(dev, R852_DMA_SETTINGS, dma_reg);
+
+ /* Set dma irq */
+ dma_irq_reg = r852_read_reg_dword(dev, R852_DMA_IRQ_ENABLE);
+ r852_write_reg_dword(dev, R852_DMA_IRQ_ENABLE,
+ dma_irq_reg |
+ R852_DMA_IRQ_INTERNAL |
+ R852_DMA_IRQ_ERROR |
+ R852_DMA_IRQ_MEMORY);
+}
+
+/*
+ * Disable dma, called from the interrupt handler, which specifies
+ * success of the operation via 'error' argument
+ */
+static void r852_dma_done(struct r852_device *dev, int error)
+{
+ WARN_ON(dev->dma_stage == 0);
+
+ r852_write_reg_dword(dev, R852_DMA_IRQ_STA,
+ r852_read_reg_dword(dev, R852_DMA_IRQ_STA));
+
+ r852_write_reg_dword(dev, R852_DMA_SETTINGS, 0);
+ r852_write_reg_dword(dev, R852_DMA_IRQ_ENABLE, 0);
+
+ /* Precaution to make sure HW doesn't write to random kernel memory */
+ r852_write_reg_dword(dev, R852_DMA_ADDR,
+ cpu_to_le32(dev->phys_bounce_buffer));
+ r852_read_reg_dword(dev, R852_DMA_ADDR);
+
+ dev->dma_error = error;
+ dev->dma_stage = 0;
+
+ if (dev->phys_dma_addr && dev->phys_dma_addr != dev->phys_bounce_buffer)
+ pci_unmap_single(dev->pci_dev, dev->phys_dma_addr, R852_DMA_LEN,
+ dev->dma_dir ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE);
+}
+
+/*
+ * Wait, till dma is done, which includes both phases of it
+ */
+static int r852_dma_wait(struct r852_device *dev)
+{
+ long timeout = wait_for_completion_timeout(&dev->dma_done,
+ msecs_to_jiffies(1000));
+ if (!timeout) {
+ dbg("timeout waiting for DMA interrupt");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+/*
+ * Read/Write one page using dma. Only pages can be read (512 bytes)
+*/
+static void r852_do_dma(struct r852_device *dev, uint8_t *buf, int do_read)
+{
+ int bounce = 0;
+ unsigned long flags;
+ int error;
+
+ dev->dma_error = 0;
+
+ /* Set dma direction */
+ dev->dma_dir = do_read;
+ dev->dma_stage = 1;
+ reinit_completion(&dev->dma_done);
+
+ dbg_verbose("doing dma %s ", do_read ? "read" : "write");
+
+ /* Set initial dma state: for reading first fill on board buffer,
+ from device, for writes first fill the buffer from memory*/
+ dev->dma_state = do_read ? DMA_INTERNAL : DMA_MEMORY;
+
+ /* if incoming buffer is not page aligned, we should do bounce */
+ if ((unsigned long)buf & (R852_DMA_LEN-1))
+ bounce = 1;
+
+ if (!bounce) {
+ dev->phys_dma_addr = pci_map_single(dev->pci_dev, (void *)buf,
+ R852_DMA_LEN,
+ (do_read ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE));
+
+ if (pci_dma_mapping_error(dev->pci_dev, dev->phys_dma_addr))
+ bounce = 1;
+ }
+
+ if (bounce) {
+ dbg_verbose("dma: using bounce buffer");
+ dev->phys_dma_addr = dev->phys_bounce_buffer;
+ if (!do_read)
+ memcpy(dev->bounce_buffer, buf, R852_DMA_LEN);
+ }
+
+ /* Enable DMA */
+ spin_lock_irqsave(&dev->irqlock, flags);
+ r852_dma_enable(dev);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+
+ /* Wait till complete */
+ error = r852_dma_wait(dev);
+
+ if (error) {
+ r852_dma_done(dev, error);
+ return;
+ }
+
+ if (do_read && bounce)
+ memcpy((void *)buf, dev->bounce_buffer, R852_DMA_LEN);
+}
+
+/*
+ * Program data lines of the nand chip to send data to it
+ */
+static void r852_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ struct r852_device *dev = r852_get_dev(mtd);
+ uint32_t reg;
+
+ /* Don't allow any access to hardware if we suspect card removal */
+ if (dev->card_unstable)
+ return;
+
+ /* Special case for whole sector read */
+ if (len == R852_DMA_LEN && dev->dma_usable) {
+ r852_do_dma(dev, (uint8_t *)buf, 0);
+ return;
+ }
+
+ /* write DWORD chinks - faster */
+ while (len >= 4) {
+ reg = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24;
+ r852_write_reg_dword(dev, R852_DATALINE, reg);
+ buf += 4;
+ len -= 4;
+
+ }
+
+ /* write rest */
+ while (len > 0) {
+ r852_write_reg(dev, R852_DATALINE, *buf++);
+ len--;
+ }
+}
+
+/*
+ * Read data lines of the nand chip to retrieve data
+ */
+static void r852_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct r852_device *dev = r852_get_dev(mtd);
+ uint32_t reg;
+
+ if (dev->card_unstable) {
+ /* since we can't signal error here, at least, return
+ predictable buffer */
+ memset(buf, 0, len);
+ return;
+ }
+
+ /* special case for whole sector read */
+ if (len == R852_DMA_LEN && dev->dma_usable) {
+ r852_do_dma(dev, buf, 1);
+ return;
+ }
+
+ /* read in dword sized chunks */
+ while (len >= 4) {
+
+ reg = r852_read_reg_dword(dev, R852_DATALINE);
+ *buf++ = reg & 0xFF;
+ *buf++ = (reg >> 8) & 0xFF;
+ *buf++ = (reg >> 16) & 0xFF;
+ *buf++ = (reg >> 24) & 0xFF;
+ len -= 4;
+ }
+
+ /* read the reset by bytes */
+ while (len--)
+ *buf++ = r852_read_reg(dev, R852_DATALINE);
+}
+
+/*
+ * Read one byte from nand chip
+ */
+static uint8_t r852_read_byte(struct mtd_info *mtd)
+{
+ struct r852_device *dev = r852_get_dev(mtd);
+
+ /* Same problem as in r852_read_buf.... */
+ if (dev->card_unstable)
+ return 0;
+
+ return r852_read_reg(dev, R852_DATALINE);
+}
+
+/*
+ * Control several chip lines & send commands
+ */
+static void r852_cmdctl(struct mtd_info *mtd, int dat, unsigned int ctrl)
+{
+ struct r852_device *dev = r852_get_dev(mtd);
+
+ if (dev->card_unstable)
+ return;
+
+ if (ctrl & NAND_CTRL_CHANGE) {
+
+ dev->ctlreg &= ~(R852_CTL_DATA | R852_CTL_COMMAND |
+ R852_CTL_ON | R852_CTL_CARDENABLE);
+
+ if (ctrl & NAND_ALE)
+ dev->ctlreg |= R852_CTL_DATA;
+
+ if (ctrl & NAND_CLE)
+ dev->ctlreg |= R852_CTL_COMMAND;
+
+ if (ctrl & NAND_NCE)
+ dev->ctlreg |= (R852_CTL_CARDENABLE | R852_CTL_ON);
+ else
+ dev->ctlreg &= ~R852_CTL_WRITE;
+
+ /* when write is stareted, enable write access */
+ if (dat == NAND_CMD_ERASE1)
+ dev->ctlreg |= R852_CTL_WRITE;
+
+ r852_write_reg(dev, R852_CTL, dev->ctlreg);
+ }
+
+ /* HACK: NAND_CMD_SEQIN is called without NAND_CTRL_CHANGE, but we need
+ to set write mode */
+ if (dat == NAND_CMD_SEQIN && (dev->ctlreg & R852_CTL_COMMAND)) {
+ dev->ctlreg |= R852_CTL_WRITE;
+ r852_write_reg(dev, R852_CTL, dev->ctlreg);
+ }
+
+ if (dat != NAND_CMD_NONE)
+ r852_write_reg(dev, R852_DATALINE, dat);
+}
+
+/*
+ * Wait till card is ready.
+ * based on nand_wait, but returns errors on DMA error
+ */
+static int r852_wait(struct mtd_info *mtd, struct nand_chip *chip)
+{
+ struct r852_device *dev = nand_get_controller_data(chip);
+
+ unsigned long timeout;
+ u8 status;
+
+ timeout = jiffies + (chip->state == FL_ERASING ?
+ msecs_to_jiffies(400) : msecs_to_jiffies(20));
+
+ while (time_before(jiffies, timeout))
+ if (chip->dev_ready(mtd))
+ break;
+
+ nand_status_op(chip, &status);
+
+ /* Unfortunelly, no way to send detailed error status... */
+ if (dev->dma_error) {
+ status |= NAND_STATUS_FAIL;
+ dev->dma_error = 0;
+ }
+ return status;
+}
+
+/*
+ * Check if card is ready
+ */
+
+static int r852_ready(struct mtd_info *mtd)
+{
+ struct r852_device *dev = r852_get_dev(mtd);
+ return !(r852_read_reg(dev, R852_CARD_STA) & R852_CARD_STA_BUSY);
+}
+
+
+/*
+ * Set ECC engine mode
+*/
+
+static void r852_ecc_hwctl(struct mtd_info *mtd, int mode)
+{
+ struct r852_device *dev = r852_get_dev(mtd);
+
+ if (dev->card_unstable)
+ return;
+
+ switch (mode) {
+ case NAND_ECC_READ:
+ case NAND_ECC_WRITE:
+ /* enable ecc generation/check*/
+ dev->ctlreg |= R852_CTL_ECC_ENABLE;
+
+ /* flush ecc buffer */
+ r852_write_reg(dev, R852_CTL,
+ dev->ctlreg | R852_CTL_ECC_ACCESS);
+
+ r852_read_reg_dword(dev, R852_DATALINE);
+ r852_write_reg(dev, R852_CTL, dev->ctlreg);
+ return;
+
+ case NAND_ECC_READSYN:
+ /* disable ecc generation */
+ dev->ctlreg &= ~R852_CTL_ECC_ENABLE;
+ r852_write_reg(dev, R852_CTL, dev->ctlreg);
+ }
+}
+
+/*
+ * Calculate ECC, only used for writes
+ */
+
+static int r852_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat,
+ uint8_t *ecc_code)
+{
+ struct r852_device *dev = r852_get_dev(mtd);
+ struct sm_oob *oob = (struct sm_oob *)ecc_code;
+ uint32_t ecc1, ecc2;
+
+ if (dev->card_unstable)
+ return 0;
+
+ dev->ctlreg &= ~R852_CTL_ECC_ENABLE;
+ r852_write_reg(dev, R852_CTL, dev->ctlreg | R852_CTL_ECC_ACCESS);
+
+ ecc1 = r852_read_reg_dword(dev, R852_DATALINE);
+ ecc2 = r852_read_reg_dword(dev, R852_DATALINE);
+
+ oob->ecc1[0] = (ecc1) & 0xFF;
+ oob->ecc1[1] = (ecc1 >> 8) & 0xFF;
+ oob->ecc1[2] = (ecc1 >> 16) & 0xFF;
+
+ oob->ecc2[0] = (ecc2) & 0xFF;
+ oob->ecc2[1] = (ecc2 >> 8) & 0xFF;
+ oob->ecc2[2] = (ecc2 >> 16) & 0xFF;
+
+ r852_write_reg(dev, R852_CTL, dev->ctlreg);
+ return 0;
+}
+
+/*
+ * Correct the data using ECC, hw did almost everything for us
+ */
+
+static int r852_ecc_correct(struct mtd_info *mtd, uint8_t *dat,
+ uint8_t *read_ecc, uint8_t *calc_ecc)
+{
+ uint32_t ecc_reg;
+ uint8_t ecc_status, err_byte;
+ int i, error = 0;
+
+ struct r852_device *dev = r852_get_dev(mtd);
+
+ if (dev->card_unstable)
+ return 0;
+
+ if (dev->dma_error) {
+ dev->dma_error = 0;
+ return -EIO;
+ }
+
+ r852_write_reg(dev, R852_CTL, dev->ctlreg | R852_CTL_ECC_ACCESS);
+ ecc_reg = r852_read_reg_dword(dev, R852_DATALINE);
+ r852_write_reg(dev, R852_CTL, dev->ctlreg);
+
+ for (i = 0 ; i <= 1 ; i++) {
+
+ ecc_status = (ecc_reg >> 8) & 0xFF;
+
+ /* ecc uncorrectable error */
+ if (ecc_status & R852_ECC_FAIL) {
+ dbg("ecc: unrecoverable error, in half %d", i);
+ error = -EBADMSG;
+ goto exit;
+ }
+
+ /* correctable error */
+ if (ecc_status & R852_ECC_CORRECTABLE) {
+
+ err_byte = ecc_reg & 0xFF;
+ dbg("ecc: recoverable error, "
+ "in half %d, byte %d, bit %d", i,
+ err_byte, ecc_status & R852_ECC_ERR_BIT_MSK);
+
+ dat[err_byte] ^=
+ 1 << (ecc_status & R852_ECC_ERR_BIT_MSK);
+ error++;
+ }
+
+ dat += 256;
+ ecc_reg >>= 16;
+ }
+exit:
+ return error;
+}
+
+/*
+ * This is copy of nand_read_oob_std
+ * nand_read_oob_syndrome assumes we can send column address - we can't
+ */
+static int r852_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ return nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize);
+}
+
+/*
+ * Start the nand engine
+ */
+
+static void r852_engine_enable(struct r852_device *dev)
+{
+ if (r852_read_reg_dword(dev, R852_HW) & R852_HW_UNKNOWN) {
+ r852_write_reg(dev, R852_CTL, R852_CTL_RESET | R852_CTL_ON);
+ r852_write_reg_dword(dev, R852_HW, R852_HW_ENABLED);
+ } else {
+ r852_write_reg_dword(dev, R852_HW, R852_HW_ENABLED);
+ r852_write_reg(dev, R852_CTL, R852_CTL_RESET | R852_CTL_ON);
+ }
+ msleep(300);
+ r852_write_reg(dev, R852_CTL, 0);
+}
+
+
+/*
+ * Stop the nand engine
+ */
+
+static void r852_engine_disable(struct r852_device *dev)
+{
+ r852_write_reg_dword(dev, R852_HW, 0);
+ r852_write_reg(dev, R852_CTL, R852_CTL_RESET);
+}
+
+/*
+ * Test if card is present
+ */
+
+static void r852_card_update_present(struct r852_device *dev)
+{
+ unsigned long flags;
+ uint8_t reg;
+
+ spin_lock_irqsave(&dev->irqlock, flags);
+ reg = r852_read_reg(dev, R852_CARD_STA);
+ dev->card_detected = !!(reg & R852_CARD_STA_PRESENT);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+}
+
+/*
+ * Update card detection IRQ state according to current card state
+ * which is read in r852_card_update_present
+ */
+static void r852_update_card_detect(struct r852_device *dev)
+{
+ int card_detect_reg = r852_read_reg(dev, R852_CARD_IRQ_ENABLE);
+ dev->card_unstable = 0;
+
+ card_detect_reg &= ~(R852_CARD_IRQ_REMOVE | R852_CARD_IRQ_INSERT);
+ card_detect_reg |= R852_CARD_IRQ_GENABLE;
+
+ card_detect_reg |= dev->card_detected ?
+ R852_CARD_IRQ_REMOVE : R852_CARD_IRQ_INSERT;
+
+ r852_write_reg(dev, R852_CARD_IRQ_ENABLE, card_detect_reg);
+}
+
+static ssize_t r852_media_type_show(struct device *sys_dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mtd_info *mtd = container_of(sys_dev, struct mtd_info, dev);
+ struct r852_device *dev = r852_get_dev(mtd);
+ char *data = dev->sm ? "smartmedia" : "xd";
+
+ strcpy(buf, data);
+ return strlen(data);
+}
+
+static DEVICE_ATTR(media_type, S_IRUGO, r852_media_type_show, NULL);
+
+
+/* Detect properties of card in slot */
+static void r852_update_media_status(struct r852_device *dev)
+{
+ uint8_t reg;
+ unsigned long flags;
+ int readonly;
+
+ spin_lock_irqsave(&dev->irqlock, flags);
+ if (!dev->card_detected) {
+ message("card removed");
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ return ;
+ }
+
+ readonly = r852_read_reg(dev, R852_CARD_STA) & R852_CARD_STA_RO;
+ reg = r852_read_reg(dev, R852_DMA_CAP);
+ dev->sm = (reg & (R852_DMA1 | R852_DMA2)) && (reg & R852_SMBIT);
+
+ message("detected %s %s card in slot",
+ dev->sm ? "SmartMedia" : "xD",
+ readonly ? "readonly" : "writeable");
+
+ dev->readonly = readonly;
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+}
+
+/*
+ * Register the nand device
+ * Called when the card is detected
+ */
+static int r852_register_nand_device(struct r852_device *dev)
+{
+ struct mtd_info *mtd = nand_to_mtd(dev->chip);
+
+ WARN_ON(dev->card_registred);
+
+ mtd->dev.parent = &dev->pci_dev->dev;
+
+ if (dev->readonly)
+ dev->chip->options |= NAND_ROM;
+
+ r852_engine_enable(dev);
+
+ if (sm_register_device(mtd, dev->sm))
+ goto error1;
+
+ if (device_create_file(&mtd->dev, &dev_attr_media_type)) {
+ message("can't create media type sysfs attribute");
+ goto error3;
+ }
+
+ dev->card_registred = 1;
+ return 0;
+error3:
+ nand_release(mtd);
+error1:
+ /* Force card redetect */
+ dev->card_detected = 0;
+ return -1;
+}
+
+/*
+ * Unregister the card
+ */
+
+static void r852_unregister_nand_device(struct r852_device *dev)
+{
+ struct mtd_info *mtd = nand_to_mtd(dev->chip);
+
+ if (!dev->card_registred)
+ return;
+
+ device_remove_file(&mtd->dev, &dev_attr_media_type);
+ nand_release(mtd);
+ r852_engine_disable(dev);
+ dev->card_registred = 0;
+}
+
+/* Card state updater */
+static void r852_card_detect_work(struct work_struct *work)
+{
+ struct r852_device *dev =
+ container_of(work, struct r852_device, card_detect_work.work);
+
+ r852_card_update_present(dev);
+ r852_update_card_detect(dev);
+ dev->card_unstable = 0;
+
+ /* False alarm */
+ if (dev->card_detected == dev->card_registred)
+ goto exit;
+
+ /* Read media properties */
+ r852_update_media_status(dev);
+
+ /* Register the card */
+ if (dev->card_detected)
+ r852_register_nand_device(dev);
+ else
+ r852_unregister_nand_device(dev);
+exit:
+ r852_update_card_detect(dev);
+}
+
+/* Ack + disable IRQ generation */
+static void r852_disable_irqs(struct r852_device *dev)
+{
+ uint8_t reg;
+ reg = r852_read_reg(dev, R852_CARD_IRQ_ENABLE);
+ r852_write_reg(dev, R852_CARD_IRQ_ENABLE, reg & ~R852_CARD_IRQ_MASK);
+
+ reg = r852_read_reg_dword(dev, R852_DMA_IRQ_ENABLE);
+ r852_write_reg_dword(dev, R852_DMA_IRQ_ENABLE,
+ reg & ~R852_DMA_IRQ_MASK);
+
+ r852_write_reg(dev, R852_CARD_IRQ_STA, R852_CARD_IRQ_MASK);
+ r852_write_reg_dword(dev, R852_DMA_IRQ_STA, R852_DMA_IRQ_MASK);
+}
+
+/* Interrupt handler */
+static irqreturn_t r852_irq(int irq, void *data)
+{
+ struct r852_device *dev = (struct r852_device *)data;
+
+ uint8_t card_status, dma_status;
+ unsigned long flags;
+ irqreturn_t ret = IRQ_NONE;
+
+ spin_lock_irqsave(&dev->irqlock, flags);
+
+ /* handle card detection interrupts first */
+ card_status = r852_read_reg(dev, R852_CARD_IRQ_STA);
+ r852_write_reg(dev, R852_CARD_IRQ_STA, card_status);
+
+ if (card_status & (R852_CARD_IRQ_INSERT|R852_CARD_IRQ_REMOVE)) {
+
+ ret = IRQ_HANDLED;
+ dev->card_detected = !!(card_status & R852_CARD_IRQ_INSERT);
+
+ /* we shouldn't receive any interrupts if we wait for card
+ to settle */
+ WARN_ON(dev->card_unstable);
+
+ /* disable irqs while card is unstable */
+ /* this will timeout DMA if active, but better that garbage */
+ r852_disable_irqs(dev);
+
+ if (dev->card_unstable)
+ goto out;
+
+ /* let, card state to settle a bit, and then do the work */
+ dev->card_unstable = 1;
+ queue_delayed_work(dev->card_workqueue,
+ &dev->card_detect_work, msecs_to_jiffies(100));
+ goto out;
+ }
+
+
+ /* Handle dma interrupts */
+ dma_status = r852_read_reg_dword(dev, R852_DMA_IRQ_STA);
+ r852_write_reg_dword(dev, R852_DMA_IRQ_STA, dma_status);
+
+ if (dma_status & R852_DMA_IRQ_MASK) {
+
+ ret = IRQ_HANDLED;
+
+ if (dma_status & R852_DMA_IRQ_ERROR) {
+ dbg("received dma error IRQ");
+ r852_dma_done(dev, -EIO);
+ complete(&dev->dma_done);
+ goto out;
+ }
+
+ /* received DMA interrupt out of nowhere? */
+ WARN_ON_ONCE(dev->dma_stage == 0);
+
+ if (dev->dma_stage == 0)
+ goto out;
+
+ /* done device access */
+ if (dev->dma_state == DMA_INTERNAL &&
+ (dma_status & R852_DMA_IRQ_INTERNAL)) {
+
+ dev->dma_state = DMA_MEMORY;
+ dev->dma_stage++;
+ }
+
+ /* done memory DMA */
+ if (dev->dma_state == DMA_MEMORY &&
+ (dma_status & R852_DMA_IRQ_MEMORY)) {
+ dev->dma_state = DMA_INTERNAL;
+ dev->dma_stage++;
+ }
+
+ /* Enable 2nd half of dma dance */
+ if (dev->dma_stage == 2)
+ r852_dma_enable(dev);
+
+ /* Operation done */
+ if (dev->dma_stage == 3) {
+ r852_dma_done(dev, 0);
+ complete(&dev->dma_done);
+ }
+ goto out;
+ }
+
+ /* Handle unknown interrupts */
+ if (dma_status)
+ dbg("bad dma IRQ status = %x", dma_status);
+
+ if (card_status & ~R852_CARD_STA_CD)
+ dbg("strange card status = %x", card_status);
+
+out:
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ return ret;
+}
+
+static int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
+{
+ int error;
+ struct nand_chip *chip;
+ struct r852_device *dev;
+
+ /* pci initialization */
+ error = pci_enable_device(pci_dev);
+
+ if (error)
+ goto error1;
+
+ pci_set_master(pci_dev);
+
+ error = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32));
+ if (error)
+ goto error2;
+
+ error = pci_request_regions(pci_dev, DRV_NAME);
+
+ if (error)
+ goto error3;
+
+ error = -ENOMEM;
+
+ /* init nand chip, but register it only on card insert */
+ chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
+
+ if (!chip)
+ goto error4;
+
+ /* commands */
+ chip->cmd_ctrl = r852_cmdctl;
+ chip->waitfunc = r852_wait;
+ chip->dev_ready = r852_ready;
+
+ /* I/O */
+ chip->read_byte = r852_read_byte;
+ chip->read_buf = r852_read_buf;
+ chip->write_buf = r852_write_buf;
+
+ /* ecc */
+ chip->ecc.mode = NAND_ECC_HW_SYNDROME;
+ chip->ecc.size = R852_DMA_LEN;
+ chip->ecc.bytes = SM_OOB_SIZE;
+ chip->ecc.strength = 2;
+ chip->ecc.hwctl = r852_ecc_hwctl;
+ chip->ecc.calculate = r852_ecc_calculate;
+ chip->ecc.correct = r852_ecc_correct;
+
+ /* TODO: hack */
+ chip->ecc.read_oob = r852_read_oob;
+
+ /* init our device structure */
+ dev = kzalloc(sizeof(struct r852_device), GFP_KERNEL);
+
+ if (!dev)
+ goto error5;
+
+ nand_set_controller_data(chip, dev);
+ dev->chip = chip;
+ dev->pci_dev = pci_dev;
+ pci_set_drvdata(pci_dev, dev);
+
+ dev->bounce_buffer = pci_alloc_consistent(pci_dev, R852_DMA_LEN,
+ &dev->phys_bounce_buffer);
+
+ if (!dev->bounce_buffer)
+ goto error6;
+
+
+ error = -ENODEV;
+ dev->mmio = pci_ioremap_bar(pci_dev, 0);
+
+ if (!dev->mmio)
+ goto error7;
+
+ error = -ENOMEM;
+ dev->tmp_buffer = kzalloc(SM_SECTOR_SIZE, GFP_KERNEL);
+
+ if (!dev->tmp_buffer)
+ goto error8;
+
+ init_completion(&dev->dma_done);
+
+ dev->card_workqueue = create_freezable_workqueue(DRV_NAME);
+
+ if (!dev->card_workqueue)
+ goto error9;
+
+ INIT_DELAYED_WORK(&dev->card_detect_work, r852_card_detect_work);
+
+ /* shutdown everything - precation */
+ r852_engine_disable(dev);
+ r852_disable_irqs(dev);
+
+ r852_dma_test(dev);
+
+ dev->irq = pci_dev->irq;
+ spin_lock_init(&dev->irqlock);
+
+ dev->card_detected = 0;
+ r852_card_update_present(dev);
+
+ /*register irq handler*/
+ error = -ENODEV;
+ if (request_irq(pci_dev->irq, &r852_irq, IRQF_SHARED,
+ DRV_NAME, dev))
+ goto error10;
+
+ /* kick initial present test */
+ queue_delayed_work(dev->card_workqueue,
+ &dev->card_detect_work, 0);
+
+
+ pr_notice("driver loaded successfully\n");
+ return 0;
+
+error10:
+ destroy_workqueue(dev->card_workqueue);
+error9:
+ kfree(dev->tmp_buffer);
+error8:
+ pci_iounmap(pci_dev, dev->mmio);
+error7:
+ pci_free_consistent(pci_dev, R852_DMA_LEN,
+ dev->bounce_buffer, dev->phys_bounce_buffer);
+error6:
+ kfree(dev);
+error5:
+ kfree(chip);
+error4:
+ pci_release_regions(pci_dev);
+error3:
+error2:
+ pci_disable_device(pci_dev);
+error1:
+ return error;
+}
+
+static void r852_remove(struct pci_dev *pci_dev)
+{
+ struct r852_device *dev = pci_get_drvdata(pci_dev);
+
+ /* Stop detect workqueue -
+ we are going to unregister the device anyway*/
+ cancel_delayed_work_sync(&dev->card_detect_work);
+ destroy_workqueue(dev->card_workqueue);
+
+ /* Unregister the device, this might make more IO */
+ r852_unregister_nand_device(dev);
+
+ /* Stop interrupts */
+ r852_disable_irqs(dev);
+ free_irq(dev->irq, dev);
+
+ /* Cleanup */
+ kfree(dev->tmp_buffer);
+ pci_iounmap(pci_dev, dev->mmio);
+ pci_free_consistent(pci_dev, R852_DMA_LEN,
+ dev->bounce_buffer, dev->phys_bounce_buffer);
+
+ kfree(dev->chip);
+ kfree(dev);
+
+ /* Shutdown the PCI device */
+ pci_release_regions(pci_dev);
+ pci_disable_device(pci_dev);
+}
+
+static void r852_shutdown(struct pci_dev *pci_dev)
+{
+ struct r852_device *dev = pci_get_drvdata(pci_dev);
+
+ cancel_delayed_work_sync(&dev->card_detect_work);
+ r852_disable_irqs(dev);
+ synchronize_irq(dev->irq);
+ pci_disable_device(pci_dev);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int r852_suspend(struct device *device)
+{
+ struct r852_device *dev = pci_get_drvdata(to_pci_dev(device));
+
+ if (dev->ctlreg & R852_CTL_CARDENABLE)
+ return -EBUSY;
+
+ /* First make sure the detect work is gone */
+ cancel_delayed_work_sync(&dev->card_detect_work);
+
+ /* Turn off the interrupts and stop the device */
+ r852_disable_irqs(dev);
+ r852_engine_disable(dev);
+
+ /* If card was pulled off just during the suspend, which is very
+ unlikely, we will remove it on resume, it too late now
+ anyway... */
+ dev->card_unstable = 0;
+ return 0;
+}
+
+static int r852_resume(struct device *device)
+{
+ struct r852_device *dev = pci_get_drvdata(to_pci_dev(device));
+ struct mtd_info *mtd = nand_to_mtd(dev->chip);
+
+ r852_disable_irqs(dev);
+ r852_card_update_present(dev);
+ r852_engine_disable(dev);
+
+
+ /* If card status changed, just do the work */
+ if (dev->card_detected != dev->card_registred) {
+ dbg("card was %s during low power state",
+ dev->card_detected ? "added" : "removed");
+
+ queue_delayed_work(dev->card_workqueue,
+ &dev->card_detect_work, msecs_to_jiffies(1000));
+ return 0;
+ }
+
+ /* Otherwise, initialize the card */
+ if (dev->card_registred) {
+ r852_engine_enable(dev);
+ dev->chip->select_chip(mtd, 0);
+ nand_reset_op(dev->chip);
+ dev->chip->select_chip(mtd, -1);
+ }
+
+ /* Program card detection IRQ */
+ r852_update_card_detect(dev);
+ return 0;
+}
+#endif
+
+static const struct pci_device_id r852_pci_id_tbl[] = {
+
+ { PCI_VDEVICE(RICOH, 0x0852), },
+ { },
+};
+
+MODULE_DEVICE_TABLE(pci, r852_pci_id_tbl);
+
+static SIMPLE_DEV_PM_OPS(r852_pm_ops, r852_suspend, r852_resume);
+
+static struct pci_driver r852_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = r852_pci_id_tbl,
+ .probe = r852_probe,
+ .remove = r852_remove,
+ .shutdown = r852_shutdown,
+ .driver.pm = &r852_pm_ops,
+};
+
+module_pci_driver(r852_pci_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>");
+MODULE_DESCRIPTION("Ricoh 85xx xD/smartmedia card reader driver");
diff --git a/drivers/mtd/nand/raw/r852.h b/drivers/mtd/nand/raw/r852.h
new file mode 100644
index 000000000000..1eed2fc2fa42
--- /dev/null
+++ b/drivers/mtd/nand/raw/r852.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright © 2009 - Maxim Levitsky
+ * driver for Ricoh xD readers
+ *
+ * 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.
+ */
+
+#include <linux/pci.h>
+#include <linux/completion.h>
+#include <linux/workqueue.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/spinlock.h>
+
+
+/* nand interface + ecc
+ byte write/read does one cycle on nand data lines.
+ dword write/read does 4 cycles
+ if R852_CTL_ECC_ACCESS is set in R852_CTL, then dword read reads
+ results of ecc correction, if DMA read was done before.
+ If write was done two dword reads read generated ecc checksums
+*/
+#define R852_DATALINE 0x00
+
+/* control register */
+#define R852_CTL 0x04
+#define R852_CTL_COMMAND 0x01 /* send command (#CLE)*/
+#define R852_CTL_DATA 0x02 /* read/write data (#ALE)*/
+#define R852_CTL_ON 0x04 /* only seem to controls the hd led, */
+ /* but has to be set on start...*/
+#define R852_CTL_RESET 0x08 /* unknown, set only on start once*/
+#define R852_CTL_CARDENABLE 0x10 /* probably (#CE) - always set*/
+#define R852_CTL_ECC_ENABLE 0x20 /* enable ecc engine */
+#define R852_CTL_ECC_ACCESS 0x40 /* read/write ecc via reg #0*/
+#define R852_CTL_WRITE 0x80 /* set when performing writes (#WP) */
+
+/* card detection status */
+#define R852_CARD_STA 0x05
+
+#define R852_CARD_STA_CD 0x01 /* state of #CD line, same as 0x04 */
+#define R852_CARD_STA_RO 0x02 /* card is readonly */
+#define R852_CARD_STA_PRESENT 0x04 /* card is present (#CD) */
+#define R852_CARD_STA_ABSENT 0x08 /* card is absent */
+#define R852_CARD_STA_BUSY 0x80 /* card is busy - (#R/B) */
+
+/* card detection irq status & enable*/
+#define R852_CARD_IRQ_STA 0x06 /* IRQ status */
+#define R852_CARD_IRQ_ENABLE 0x07 /* IRQ enable */
+
+#define R852_CARD_IRQ_CD 0x01 /* fire when #CD lights, same as 0x04*/
+#define R852_CARD_IRQ_REMOVE 0x04 /* detect card removal */
+#define R852_CARD_IRQ_INSERT 0x08 /* detect card insert */
+#define R852_CARD_IRQ_UNK1 0x10 /* unknown */
+#define R852_CARD_IRQ_GENABLE 0x80 /* general enable */
+#define R852_CARD_IRQ_MASK 0x1D
+
+
+
+/* hardware enable */
+#define R852_HW 0x08
+#define R852_HW_ENABLED 0x01 /* hw enabled */
+#define R852_HW_UNKNOWN 0x80
+
+
+/* dma capabilities */
+#define R852_DMA_CAP 0x09
+#define R852_SMBIT 0x20 /* if set with bit #6 or bit #7, then */
+ /* hw is smartmedia */
+#define R852_DMA1 0x40 /* if set w/bit #7, dma is supported */
+#define R852_DMA2 0x80 /* if set w/bit #6, dma is supported */
+
+
+/* physical DMA address - 32 bit value*/
+#define R852_DMA_ADDR 0x0C
+
+
+/* dma settings */
+#define R852_DMA_SETTINGS 0x10
+#define R852_DMA_MEMORY 0x01 /* (memory <-> internal hw buffer) */
+#define R852_DMA_READ 0x02 /* 0 = write, 1 = read */
+#define R852_DMA_INTERNAL 0x04 /* (internal hw buffer <-> card) */
+
+/* dma IRQ status */
+#define R852_DMA_IRQ_STA 0x14
+
+/* dma IRQ enable */
+#define R852_DMA_IRQ_ENABLE 0x18
+
+#define R852_DMA_IRQ_MEMORY 0x01 /* (memory <-> internal hw buffer) */
+#define R852_DMA_IRQ_ERROR 0x02 /* error did happen */
+#define R852_DMA_IRQ_INTERNAL 0x04 /* (internal hw buffer <-> card) */
+#define R852_DMA_IRQ_MASK 0x07 /* mask of all IRQ bits */
+
+
+/* ECC syndrome format - read from reg #0 will return two copies of these for
+ each half of the page.
+ first byte is error byte location, and second, bit location + flags */
+#define R852_ECC_ERR_BIT_MSK 0x07 /* error bit location */
+#define R852_ECC_CORRECT 0x10 /* no errors - (guessed) */
+#define R852_ECC_CORRECTABLE 0x20 /* correctable error exist */
+#define R852_ECC_FAIL 0x40 /* non correctable error detected */
+
+#define R852_DMA_LEN 512
+
+#define DMA_INTERNAL 0
+#define DMA_MEMORY 1
+
+struct r852_device {
+ void __iomem *mmio; /* mmio */
+ struct nand_chip *chip; /* nand chip backpointer */
+ struct pci_dev *pci_dev; /* pci backpointer */
+
+ /* dma area */
+ dma_addr_t phys_dma_addr; /* bus address of buffer*/
+ struct completion dma_done; /* data transfer done */
+
+ dma_addr_t phys_bounce_buffer; /* bus address of bounce buffer */
+ uint8_t *bounce_buffer; /* virtual address of bounce buffer */
+
+ int dma_dir; /* 1 = read, 0 = write */
+ int dma_stage; /* 0 - idle, 1 - first step,
+ 2 - second step */
+
+ int dma_state; /* 0 = internal, 1 = memory */
+ int dma_error; /* dma errors */
+ int dma_usable; /* is it possible to use dma */
+
+ /* card status area */
+ struct delayed_work card_detect_work;
+ struct workqueue_struct *card_workqueue;
+ int card_registred; /* card registered with mtd */
+ int card_detected; /* card detected in slot */
+ int card_unstable; /* whenever the card is inserted,
+ is not known yet */
+ int readonly; /* card is readonly */
+ int sm; /* Is card smartmedia */
+
+ /* interrupt handling */
+ spinlock_t irqlock; /* IRQ protecting lock */
+ int irq; /* irq num */
+ /* misc */
+ void *tmp_buffer; /* temporary buffer */
+ uint8_t ctlreg; /* cached contents of control reg */
+};
+
+#define dbg(format, ...) \
+ if (debug) \
+ pr_debug(format "\n", ## __VA_ARGS__)
+
+#define dbg_verbose(format, ...) \
+ if (debug > 1) \
+ pr_debug(format "\n", ## __VA_ARGS__)
+
+
+#define message(format, ...) \
+ pr_info(format "\n", ## __VA_ARGS__)
diff --git a/drivers/mtd/nand/raw/s3c2410.c b/drivers/mtd/nand/raw/s3c2410.c
new file mode 100644
index 000000000000..1bc0458063d8
--- /dev/null
+++ b/drivers/mtd/nand/raw/s3c2410.c
@@ -0,0 +1,1295 @@
+/*
+ * Copyright © 2004-2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * Samsung S3C2410/S3C2440/S3C2412 NAND driver
+ *
+ * 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
+*/
+
+#define pr_fmt(fmt) "nand-s3c2410: " fmt
+
+#ifdef CONFIG_MTD_NAND_S3C2410_DEBUG
+#define DEBUG
+#endif
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/cpufreq.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/partitions.h>
+
+#include <linux/platform_data/mtd-nand-s3c2410.h>
+
+#define S3C2410_NFREG(x) (x)
+
+#define S3C2410_NFCONF S3C2410_NFREG(0x00)
+#define S3C2410_NFCMD S3C2410_NFREG(0x04)
+#define S3C2410_NFADDR S3C2410_NFREG(0x08)
+#define S3C2410_NFDATA S3C2410_NFREG(0x0C)
+#define S3C2410_NFSTAT S3C2410_NFREG(0x10)
+#define S3C2410_NFECC S3C2410_NFREG(0x14)
+#define S3C2440_NFCONT S3C2410_NFREG(0x04)
+#define S3C2440_NFCMD S3C2410_NFREG(0x08)
+#define S3C2440_NFADDR S3C2410_NFREG(0x0C)
+#define S3C2440_NFDATA S3C2410_NFREG(0x10)
+#define S3C2440_NFSTAT S3C2410_NFREG(0x20)
+#define S3C2440_NFMECC0 S3C2410_NFREG(0x2C)
+#define S3C2412_NFSTAT S3C2410_NFREG(0x28)
+#define S3C2412_NFMECC0 S3C2410_NFREG(0x34)
+#define S3C2410_NFCONF_EN (1<<15)
+#define S3C2410_NFCONF_INITECC (1<<12)
+#define S3C2410_NFCONF_nFCE (1<<11)
+#define S3C2410_NFCONF_TACLS(x) ((x)<<8)
+#define S3C2410_NFCONF_TWRPH0(x) ((x)<<4)
+#define S3C2410_NFCONF_TWRPH1(x) ((x)<<0)
+#define S3C2410_NFSTAT_BUSY (1<<0)
+#define S3C2440_NFCONF_TACLS(x) ((x)<<12)
+#define S3C2440_NFCONF_TWRPH0(x) ((x)<<8)
+#define S3C2440_NFCONF_TWRPH1(x) ((x)<<4)
+#define S3C2440_NFCONT_INITECC (1<<4)
+#define S3C2440_NFCONT_nFCE (1<<1)
+#define S3C2440_NFCONT_ENABLE (1<<0)
+#define S3C2440_NFSTAT_READY (1<<0)
+#define S3C2412_NFCONF_NANDBOOT (1<<31)
+#define S3C2412_NFCONT_INIT_MAIN_ECC (1<<5)
+#define S3C2412_NFCONT_nFCE0 (1<<1)
+#define S3C2412_NFSTAT_READY (1<<0)
+
+/* new oob placement block for use with hardware ecc generation
+ */
+static int s3c2410_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ if (section)
+ return -ERANGE;
+
+ oobregion->offset = 0;
+ oobregion->length = 3;
+
+ return 0;
+}
+
+static int s3c2410_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ if (section)
+ return -ERANGE;
+
+ oobregion->offset = 8;
+ oobregion->length = 8;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops s3c2410_ooblayout_ops = {
+ .ecc = s3c2410_ooblayout_ecc,
+ .free = s3c2410_ooblayout_free,
+};
+
+/* controller and mtd information */
+
+struct s3c2410_nand_info;
+
+/**
+ * struct s3c2410_nand_mtd - driver MTD structure
+ * @mtd: The MTD instance to pass to the MTD layer.
+ * @chip: The NAND chip information.
+ * @set: The platform information supplied for this set of NAND chips.
+ * @info: Link back to the hardware information.
+*/
+struct s3c2410_nand_mtd {
+ struct nand_chip chip;
+ struct s3c2410_nand_set *set;
+ struct s3c2410_nand_info *info;
+};
+
+enum s3c_cpu_type {
+ TYPE_S3C2410,
+ TYPE_S3C2412,
+ TYPE_S3C2440,
+};
+
+enum s3c_nand_clk_state {
+ CLOCK_DISABLE = 0,
+ CLOCK_ENABLE,
+ CLOCK_SUSPEND,
+};
+
+/* overview of the s3c2410 nand state */
+
+/**
+ * struct s3c2410_nand_info - NAND controller state.
+ * @mtds: An array of MTD instances on this controoler.
+ * @platform: The platform data for this board.
+ * @device: The platform device we bound to.
+ * @clk: The clock resource for this controller.
+ * @regs: The area mapped for the hardware registers.
+ * @sel_reg: Pointer to the register controlling the NAND selection.
+ * @sel_bit: The bit in @sel_reg to select the NAND chip.
+ * @mtd_count: The number of MTDs created from this controller.
+ * @save_sel: The contents of @sel_reg to be saved over suspend.
+ * @clk_rate: The clock rate from @clk.
+ * @clk_state: The current clock state.
+ * @cpu_type: The exact type of this controller.
+ */
+struct s3c2410_nand_info {
+ /* mtd info */
+ struct nand_hw_control controller;
+ struct s3c2410_nand_mtd *mtds;
+ struct s3c2410_platform_nand *platform;
+
+ /* device info */
+ struct device *device;
+ struct clk *clk;
+ void __iomem *regs;
+ void __iomem *sel_reg;
+ int sel_bit;
+ int mtd_count;
+ unsigned long save_sel;
+ unsigned long clk_rate;
+ enum s3c_nand_clk_state clk_state;
+
+ enum s3c_cpu_type cpu_type;
+
+#ifdef CONFIG_ARM_S3C24XX_CPUFREQ
+ struct notifier_block freq_transition;
+#endif
+};
+
+struct s3c24XX_nand_devtype_data {
+ enum s3c_cpu_type type;
+};
+
+static const struct s3c24XX_nand_devtype_data s3c2410_nand_devtype_data = {
+ .type = TYPE_S3C2410,
+};
+
+static const struct s3c24XX_nand_devtype_data s3c2412_nand_devtype_data = {
+ .type = TYPE_S3C2412,
+};
+
+static const struct s3c24XX_nand_devtype_data s3c2440_nand_devtype_data = {
+ .type = TYPE_S3C2440,
+};
+
+/* conversion functions */
+
+static struct s3c2410_nand_mtd *s3c2410_nand_mtd_toours(struct mtd_info *mtd)
+{
+ return container_of(mtd_to_nand(mtd), struct s3c2410_nand_mtd,
+ chip);
+}
+
+static struct s3c2410_nand_info *s3c2410_nand_mtd_toinfo(struct mtd_info *mtd)
+{
+ return s3c2410_nand_mtd_toours(mtd)->info;
+}
+
+static struct s3c2410_nand_info *to_nand_info(struct platform_device *dev)
+{
+ return platform_get_drvdata(dev);
+}
+
+static struct s3c2410_platform_nand *to_nand_plat(struct platform_device *dev)
+{
+ return dev_get_platdata(&dev->dev);
+}
+
+static inline int allow_clk_suspend(struct s3c2410_nand_info *info)
+{
+#ifdef CONFIG_MTD_NAND_S3C2410_CLKSTOP
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+/**
+ * s3c2410_nand_clk_set_state - Enable, disable or suspend NAND clock.
+ * @info: The controller instance.
+ * @new_state: State to which clock should be set.
+ */
+static void s3c2410_nand_clk_set_state(struct s3c2410_nand_info *info,
+ enum s3c_nand_clk_state new_state)
+{
+ if (!allow_clk_suspend(info) && new_state == CLOCK_SUSPEND)
+ return;
+
+ if (info->clk_state == CLOCK_ENABLE) {
+ if (new_state != CLOCK_ENABLE)
+ clk_disable_unprepare(info->clk);
+ } else {
+ if (new_state == CLOCK_ENABLE)
+ clk_prepare_enable(info->clk);
+ }
+
+ info->clk_state = new_state;
+}
+
+/* timing calculations */
+
+#define NS_IN_KHZ 1000000
+
+/**
+ * s3c_nand_calc_rate - calculate timing data.
+ * @wanted: The cycle time in nanoseconds.
+ * @clk: The clock rate in kHz.
+ * @max: The maximum divider value.
+ *
+ * Calculate the timing value from the given parameters.
+ */
+static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max)
+{
+ int result;
+
+ result = DIV_ROUND_UP((wanted * clk), NS_IN_KHZ);
+
+ pr_debug("result %d from %ld, %d\n", result, clk, wanted);
+
+ if (result > max) {
+ pr_err("%d ns is too big for current clock rate %ld\n",
+ wanted, clk);
+ return -1;
+ }
+
+ if (result < 1)
+ result = 1;
+
+ return result;
+}
+
+#define to_ns(ticks, clk) (((ticks) * NS_IN_KHZ) / (unsigned int)(clk))
+
+/* controller setup */
+
+/**
+ * s3c2410_nand_setrate - setup controller timing information.
+ * @info: The controller instance.
+ *
+ * Given the information supplied by the platform, calculate and set
+ * the necessary timing registers in the hardware to generate the
+ * necessary timing cycles to the hardware.
+ */
+static int s3c2410_nand_setrate(struct s3c2410_nand_info *info)
+{
+ struct s3c2410_platform_nand *plat = info->platform;
+ int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4;
+ int tacls, twrph0, twrph1;
+ unsigned long clkrate = clk_get_rate(info->clk);
+ unsigned long uninitialized_var(set), cfg, uninitialized_var(mask);
+ unsigned long flags;
+
+ /* calculate the timing information for the controller */
+
+ info->clk_rate = clkrate;
+ clkrate /= 1000; /* turn clock into kHz for ease of use */
+
+ if (plat != NULL) {
+ tacls = s3c_nand_calc_rate(plat->tacls, clkrate, tacls_max);
+ twrph0 = s3c_nand_calc_rate(plat->twrph0, clkrate, 8);
+ twrph1 = s3c_nand_calc_rate(plat->twrph1, clkrate, 8);
+ } else {
+ /* default timings */
+ tacls = tacls_max;
+ twrph0 = 8;
+ twrph1 = 8;
+ }
+
+ if (tacls < 0 || twrph0 < 0 || twrph1 < 0) {
+ dev_err(info->device, "cannot get suitable timings\n");
+ return -EINVAL;
+ }
+
+ dev_info(info->device, "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n",
+ tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate),
+ twrph1, to_ns(twrph1, clkrate));
+
+ switch (info->cpu_type) {
+ case TYPE_S3C2410:
+ mask = (S3C2410_NFCONF_TACLS(3) |
+ S3C2410_NFCONF_TWRPH0(7) |
+ S3C2410_NFCONF_TWRPH1(7));
+ set = S3C2410_NFCONF_EN;
+ set |= S3C2410_NFCONF_TACLS(tacls - 1);
+ set |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
+ set |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
+ break;
+
+ case TYPE_S3C2440:
+ case TYPE_S3C2412:
+ mask = (S3C2440_NFCONF_TACLS(tacls_max - 1) |
+ S3C2440_NFCONF_TWRPH0(7) |
+ S3C2440_NFCONF_TWRPH1(7));
+
+ set = S3C2440_NFCONF_TACLS(tacls - 1);
+ set |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
+ set |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);
+ break;
+
+ default:
+ BUG();
+ }
+
+ local_irq_save(flags);
+
+ cfg = readl(info->regs + S3C2410_NFCONF);
+ cfg &= ~mask;
+ cfg |= set;
+ writel(cfg, info->regs + S3C2410_NFCONF);
+
+ local_irq_restore(flags);
+
+ dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg);
+
+ return 0;
+}
+
+/**
+ * s3c2410_nand_inithw - basic hardware initialisation
+ * @info: The hardware state.
+ *
+ * Do the basic initialisation of the hardware, using s3c2410_nand_setrate()
+ * to setup the hardware access speeds and set the controller to be enabled.
+*/
+static int s3c2410_nand_inithw(struct s3c2410_nand_info *info)
+{
+ int ret;
+
+ ret = s3c2410_nand_setrate(info);
+ if (ret < 0)
+ return ret;
+
+ switch (info->cpu_type) {
+ case TYPE_S3C2410:
+ default:
+ break;
+
+ case TYPE_S3C2440:
+ case TYPE_S3C2412:
+ /* enable the controller and de-assert nFCE */
+
+ writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT);
+ }
+
+ return 0;
+}
+
+/**
+ * s3c2410_nand_select_chip - select the given nand chip
+ * @mtd: The MTD instance for this chip.
+ * @chip: The chip number.
+ *
+ * This is called by the MTD layer to either select a given chip for the
+ * @mtd instance, or to indicate that the access has finished and the
+ * chip can be de-selected.
+ *
+ * The routine ensures that the nFCE line is correctly setup, and any
+ * platform specific selection code is called to route nFCE to the specific
+ * chip.
+ */
+static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
+{
+ struct s3c2410_nand_info *info;
+ struct s3c2410_nand_mtd *nmtd;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ unsigned long cur;
+
+ nmtd = nand_get_controller_data(this);
+ info = nmtd->info;
+
+ if (chip != -1)
+ s3c2410_nand_clk_set_state(info, CLOCK_ENABLE);
+
+ cur = readl(info->sel_reg);
+
+ if (chip == -1) {
+ cur |= info->sel_bit;
+ } else {
+ if (nmtd->set != NULL && chip > nmtd->set->nr_chips) {
+ dev_err(info->device, "invalid chip %d\n", chip);
+ return;
+ }
+
+ if (info->platform != NULL) {
+ if (info->platform->select_chip != NULL)
+ (info->platform->select_chip) (nmtd->set, chip);
+ }
+
+ cur &= ~info->sel_bit;
+ }
+
+ writel(cur, info->sel_reg);
+
+ if (chip == -1)
+ s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND);
+}
+
+/* s3c2410_nand_hwcontrol
+ *
+ * Issue command and address cycles to the chip
+*/
+
+static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd,
+ unsigned int ctrl)
+{
+ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+
+ if (cmd == NAND_CMD_NONE)
+ return;
+
+ if (ctrl & NAND_CLE)
+ writeb(cmd, info->regs + S3C2410_NFCMD);
+ else
+ writeb(cmd, info->regs + S3C2410_NFADDR);
+}
+
+/* command and control functions */
+
+static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd,
+ unsigned int ctrl)
+{
+ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+
+ if (cmd == NAND_CMD_NONE)
+ return;
+
+ if (ctrl & NAND_CLE)
+ writeb(cmd, info->regs + S3C2440_NFCMD);
+ else
+ writeb(cmd, info->regs + S3C2440_NFADDR);
+}
+
+/* s3c2410_nand_devready()
+ *
+ * returns 0 if the nand is busy, 1 if it is ready
+*/
+
+static int s3c2410_nand_devready(struct mtd_info *mtd)
+{
+ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+ return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY;
+}
+
+static int s3c2440_nand_devready(struct mtd_info *mtd)
+{
+ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+ return readb(info->regs + S3C2440_NFSTAT) & S3C2440_NFSTAT_READY;
+}
+
+static int s3c2412_nand_devready(struct mtd_info *mtd)
+{
+ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+ return readb(info->regs + S3C2412_NFSTAT) & S3C2412_NFSTAT_READY;
+}
+
+/* ECC handling functions */
+
+static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
+ u_char *read_ecc, u_char *calc_ecc)
+{
+ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+ unsigned int diff0, diff1, diff2;
+ unsigned int bit, byte;
+
+ pr_debug("%s(%p,%p,%p,%p)\n", __func__, mtd, dat, read_ecc, calc_ecc);
+
+ diff0 = read_ecc[0] ^ calc_ecc[0];
+ diff1 = read_ecc[1] ^ calc_ecc[1];
+ diff2 = read_ecc[2] ^ calc_ecc[2];
+
+ pr_debug("%s: rd %*phN calc %*phN diff %02x%02x%02x\n",
+ __func__, 3, read_ecc, 3, calc_ecc,
+ diff0, diff1, diff2);
+
+ if (diff0 == 0 && diff1 == 0 && diff2 == 0)
+ return 0; /* ECC is ok */
+
+ /* sometimes people do not think about using the ECC, so check
+ * to see if we have an 0xff,0xff,0xff read ECC and then ignore
+ * the error, on the assumption that this is an un-eccd page.
+ */
+ if (read_ecc[0] == 0xff && read_ecc[1] == 0xff && read_ecc[2] == 0xff
+ && info->platform->ignore_unset_ecc)
+ return 0;
+
+ /* Can we correct this ECC (ie, one row and column change).
+ * Note, this is similar to the 256 error code on smartmedia */
+
+ if (((diff0 ^ (diff0 >> 1)) & 0x55) == 0x55 &&
+ ((diff1 ^ (diff1 >> 1)) & 0x55) == 0x55 &&
+ ((diff2 ^ (diff2 >> 1)) & 0x55) == 0x55) {
+ /* calculate the bit position of the error */
+
+ bit = ((diff2 >> 3) & 1) |
+ ((diff2 >> 4) & 2) |
+ ((diff2 >> 5) & 4);
+
+ /* calculate the byte position of the error */
+
+ byte = ((diff2 << 7) & 0x100) |
+ ((diff1 << 0) & 0x80) |
+ ((diff1 << 1) & 0x40) |
+ ((diff1 << 2) & 0x20) |
+ ((diff1 << 3) & 0x10) |
+ ((diff0 >> 4) & 0x08) |
+ ((diff0 >> 3) & 0x04) |
+ ((diff0 >> 2) & 0x02) |
+ ((diff0 >> 1) & 0x01);
+
+ dev_dbg(info->device, "correcting error bit %d, byte %d\n",
+ bit, byte);
+
+ dat[byte] ^= (1 << bit);
+ return 1;
+ }
+
+ /* if there is only one bit difference in the ECC, then
+ * one of only a row or column parity has changed, which
+ * means the error is most probably in the ECC itself */
+
+ diff0 |= (diff1 << 8);
+ diff0 |= (diff2 << 16);
+
+ /* equal to "(diff0 & ~(1 << __ffs(diff0)))" */
+ if ((diff0 & (diff0 - 1)) == 0)
+ return 1;
+
+ return -1;
+}
+
+/* ECC functions
+ *
+ * These allow the s3c2410 and s3c2440 to use the controller's ECC
+ * generator block to ECC the data as it passes through]
+*/
+
+static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+ unsigned long ctrl;
+
+ ctrl = readl(info->regs + S3C2410_NFCONF);
+ ctrl |= S3C2410_NFCONF_INITECC;
+ writel(ctrl, info->regs + S3C2410_NFCONF);
+}
+
+static void s3c2412_nand_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+ unsigned long ctrl;
+
+ ctrl = readl(info->regs + S3C2440_NFCONT);
+ writel(ctrl | S3C2412_NFCONT_INIT_MAIN_ECC,
+ info->regs + S3C2440_NFCONT);
+}
+
+static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+ unsigned long ctrl;
+
+ ctrl = readl(info->regs + S3C2440_NFCONT);
+ writel(ctrl | S3C2440_NFCONT_INITECC, info->regs + S3C2440_NFCONT);
+}
+
+static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
+ u_char *ecc_code)
+{
+ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+
+ ecc_code[0] = readb(info->regs + S3C2410_NFECC + 0);
+ ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1);
+ ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2);
+
+ pr_debug("%s: returning ecc %*phN\n", __func__, 3, ecc_code);
+
+ return 0;
+}
+
+static int s3c2412_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
+ u_char *ecc_code)
+{
+ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+ unsigned long ecc = readl(info->regs + S3C2412_NFMECC0);
+
+ ecc_code[0] = ecc;
+ ecc_code[1] = ecc >> 8;
+ ecc_code[2] = ecc >> 16;
+
+ pr_debug("%s: returning ecc %*phN\n", __func__, 3, ecc_code);
+
+ return 0;
+}
+
+static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
+ u_char *ecc_code)
+{
+ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+ unsigned long ecc = readl(info->regs + S3C2440_NFMECC0);
+
+ ecc_code[0] = ecc;
+ ecc_code[1] = ecc >> 8;
+ ecc_code[2] = ecc >> 16;
+
+ pr_debug("%s: returning ecc %06lx\n", __func__, ecc & 0xffffff);
+
+ return 0;
+}
+
+/* over-ride the standard functions for a little more speed. We can
+ * use read/write block to move the data buffers to/from the controller
+*/
+
+static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ readsb(this->IO_ADDR_R, buf, len);
+}
+
+static void s3c2440_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+
+ readsl(info->regs + S3C2440_NFDATA, buf, len >> 2);
+
+ /* cleanup if we've got less than a word to do */
+ if (len & 3) {
+ buf += len & ~3;
+
+ for (; len & 3; len--)
+ *buf++ = readb(info->regs + S3C2440_NFDATA);
+ }
+}
+
+static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf,
+ int len)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ writesb(this->IO_ADDR_W, buf, len);
+}
+
+static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf,
+ int len)
+{
+ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+
+ writesl(info->regs + S3C2440_NFDATA, buf, len >> 2);
+
+ /* cleanup any fractional write */
+ if (len & 3) {
+ buf += len & ~3;
+
+ for (; len & 3; len--, buf++)
+ writeb(*buf, info->regs + S3C2440_NFDATA);
+ }
+}
+
+/* cpufreq driver support */
+
+#ifdef CONFIG_ARM_S3C24XX_CPUFREQ
+
+static int s3c2410_nand_cpufreq_transition(struct notifier_block *nb,
+ unsigned long val, void *data)
+{
+ struct s3c2410_nand_info *info;
+ unsigned long newclk;
+
+ info = container_of(nb, struct s3c2410_nand_info, freq_transition);
+ newclk = clk_get_rate(info->clk);
+
+ if ((val == CPUFREQ_POSTCHANGE && newclk < info->clk_rate) ||
+ (val == CPUFREQ_PRECHANGE && newclk > info->clk_rate)) {
+ s3c2410_nand_setrate(info);
+ }
+
+ return 0;
+}
+
+static inline int s3c2410_nand_cpufreq_register(struct s3c2410_nand_info *info)
+{
+ info->freq_transition.notifier_call = s3c2410_nand_cpufreq_transition;
+
+ return cpufreq_register_notifier(&info->freq_transition,
+ CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+static inline void
+s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info)
+{
+ cpufreq_unregister_notifier(&info->freq_transition,
+ CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+#else
+static inline int s3c2410_nand_cpufreq_register(struct s3c2410_nand_info *info)
+{
+ return 0;
+}
+
+static inline void
+s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info)
+{
+}
+#endif
+
+/* device management functions */
+
+static int s3c24xx_nand_remove(struct platform_device *pdev)
+{
+ struct s3c2410_nand_info *info = to_nand_info(pdev);
+
+ if (info == NULL)
+ return 0;
+
+ s3c2410_nand_cpufreq_deregister(info);
+
+ /* Release all our mtds and their partitions, then go through
+ * freeing the resources used
+ */
+
+ if (info->mtds != NULL) {
+ struct s3c2410_nand_mtd *ptr = info->mtds;
+ int mtdno;
+
+ for (mtdno = 0; mtdno < info->mtd_count; mtdno++, ptr++) {
+ pr_debug("releasing mtd %d (%p)\n", mtdno, ptr);
+ nand_release(nand_to_mtd(&ptr->chip));
+ }
+ }
+
+ /* free the common resources */
+
+ if (!IS_ERR(info->clk))
+ s3c2410_nand_clk_set_state(info, CLOCK_DISABLE);
+
+ return 0;
+}
+
+static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
+ struct s3c2410_nand_mtd *mtd,
+ struct s3c2410_nand_set *set)
+{
+ if (set) {
+ struct mtd_info *mtdinfo = nand_to_mtd(&mtd->chip);
+
+ mtdinfo->name = set->name;
+
+ return mtd_device_parse_register(mtdinfo, NULL, NULL,
+ set->partitions, set->nr_partitions);
+ }
+
+ return -ENODEV;
+}
+
+static int s3c2410_nand_setup_data_interface(struct mtd_info *mtd, int csline,
+ const struct nand_data_interface *conf)
+{
+ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+ struct s3c2410_platform_nand *pdata = info->platform;
+ const struct nand_sdr_timings *timings;
+ int tacls;
+
+ timings = nand_get_sdr_timings(conf);
+ if (IS_ERR(timings))
+ return -ENOTSUPP;
+
+ tacls = timings->tCLS_min - timings->tWP_min;
+ if (tacls < 0)
+ tacls = 0;
+
+ pdata->tacls = DIV_ROUND_UP(tacls, 1000);
+ pdata->twrph0 = DIV_ROUND_UP(timings->tWP_min, 1000);
+ pdata->twrph1 = DIV_ROUND_UP(timings->tCLH_min, 1000);
+
+ return s3c2410_nand_setrate(info);
+}
+
+/**
+ * s3c2410_nand_init_chip - initialise a single instance of an chip
+ * @info: The base NAND controller the chip is on.
+ * @nmtd: The new controller MTD instance to fill in.
+ * @set: The information passed from the board specific platform data.
+ *
+ * Initialise the given @nmtd from the information in @info and @set. This
+ * readies the structure for use with the MTD layer functions by ensuring
+ * all pointers are setup and the necessary control routines selected.
+ */
+static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
+ struct s3c2410_nand_mtd *nmtd,
+ struct s3c2410_nand_set *set)
+{
+ struct device_node *np = info->device->of_node;
+ struct nand_chip *chip = &nmtd->chip;
+ void __iomem *regs = info->regs;
+
+ nand_set_flash_node(chip, set->of_node);
+
+ chip->write_buf = s3c2410_nand_write_buf;
+ chip->read_buf = s3c2410_nand_read_buf;
+ chip->select_chip = s3c2410_nand_select_chip;
+ chip->chip_delay = 50;
+ nand_set_controller_data(chip, nmtd);
+ chip->options = set->options;
+ chip->controller = &info->controller;
+
+ /*
+ * let's keep behavior unchanged for legacy boards booting via pdata and
+ * auto-detect timings only when booting with a device tree.
+ */
+ if (np)
+ chip->setup_data_interface = s3c2410_nand_setup_data_interface;
+
+ switch (info->cpu_type) {
+ case TYPE_S3C2410:
+ chip->IO_ADDR_W = regs + S3C2410_NFDATA;
+ info->sel_reg = regs + S3C2410_NFCONF;
+ info->sel_bit = S3C2410_NFCONF_nFCE;
+ chip->cmd_ctrl = s3c2410_nand_hwcontrol;
+ chip->dev_ready = s3c2410_nand_devready;
+ break;
+
+ case TYPE_S3C2440:
+ chip->IO_ADDR_W = regs + S3C2440_NFDATA;
+ info->sel_reg = regs + S3C2440_NFCONT;
+ info->sel_bit = S3C2440_NFCONT_nFCE;
+ chip->cmd_ctrl = s3c2440_nand_hwcontrol;
+ chip->dev_ready = s3c2440_nand_devready;
+ chip->read_buf = s3c2440_nand_read_buf;
+ chip->write_buf = s3c2440_nand_write_buf;
+ break;
+
+ case TYPE_S3C2412:
+ chip->IO_ADDR_W = regs + S3C2440_NFDATA;
+ info->sel_reg = regs + S3C2440_NFCONT;
+ info->sel_bit = S3C2412_NFCONT_nFCE0;
+ chip->cmd_ctrl = s3c2440_nand_hwcontrol;
+ chip->dev_ready = s3c2412_nand_devready;
+
+ if (readl(regs + S3C2410_NFCONF) & S3C2412_NFCONF_NANDBOOT)
+ dev_info(info->device, "System booted from NAND\n");
+
+ break;
+ }
+
+ chip->IO_ADDR_R = chip->IO_ADDR_W;
+
+ nmtd->info = info;
+ nmtd->set = set;
+
+ chip->ecc.mode = info->platform->ecc_mode;
+
+ /*
+ * If you use u-boot BBT creation code, specifying this flag will
+ * let the kernel fish out the BBT from the NAND.
+ */
+ if (set->flash_bbt)
+ chip->bbt_options |= NAND_BBT_USE_FLASH;
+}
+
+/**
+ * s3c2410_nand_update_chip - post probe update
+ * @info: The controller instance.
+ * @nmtd: The driver version of the MTD instance.
+ *
+ * This routine is called after the chip probe has successfully completed
+ * and the relevant per-chip information updated. This call ensure that
+ * we update the internal state accordingly.
+ *
+ * The internal state is currently limited to the ECC state information.
+*/
+static int s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
+ struct s3c2410_nand_mtd *nmtd)
+{
+ struct nand_chip *chip = &nmtd->chip;
+
+ switch (chip->ecc.mode) {
+
+ case NAND_ECC_NONE:
+ dev_info(info->device, "ECC disabled\n");
+ break;
+
+ case NAND_ECC_SOFT:
+ /*
+ * This driver expects Hamming based ECC when ecc_mode is set
+ * to NAND_ECC_SOFT. Force ecc.algo to NAND_ECC_HAMMING to
+ * avoid adding an extra ecc_algo field to
+ * s3c2410_platform_nand.
+ */
+ chip->ecc.algo = NAND_ECC_HAMMING;
+ dev_info(info->device, "soft ECC\n");
+ break;
+
+ case NAND_ECC_HW:
+ chip->ecc.calculate = s3c2410_nand_calculate_ecc;
+ chip->ecc.correct = s3c2410_nand_correct_data;
+ chip->ecc.strength = 1;
+
+ switch (info->cpu_type) {
+ case TYPE_S3C2410:
+ chip->ecc.hwctl = s3c2410_nand_enable_hwecc;
+ chip->ecc.calculate = s3c2410_nand_calculate_ecc;
+ break;
+
+ case TYPE_S3C2412:
+ chip->ecc.hwctl = s3c2412_nand_enable_hwecc;
+ chip->ecc.calculate = s3c2412_nand_calculate_ecc;
+ break;
+
+ case TYPE_S3C2440:
+ chip->ecc.hwctl = s3c2440_nand_enable_hwecc;
+ chip->ecc.calculate = s3c2440_nand_calculate_ecc;
+ break;
+ }
+
+ dev_dbg(info->device, "chip %p => page shift %d\n",
+ chip, chip->page_shift);
+
+ /* change the behaviour depending on whether we are using
+ * the large or small page nand device */
+ if (chip->page_shift > 10) {
+ chip->ecc.size = 256;
+ chip->ecc.bytes = 3;
+ } else {
+ chip->ecc.size = 512;
+ chip->ecc.bytes = 3;
+ mtd_set_ooblayout(nand_to_mtd(chip),
+ &s3c2410_ooblayout_ops);
+ }
+
+ dev_info(info->device, "hardware ECC\n");
+ break;
+
+ default:
+ dev_err(info->device, "invalid ECC mode!\n");
+ return -EINVAL;
+ }
+
+ if (chip->bbt_options & NAND_BBT_USE_FLASH)
+ chip->options |= NAND_SKIP_BBTSCAN;
+
+ return 0;
+}
+
+static const struct of_device_id s3c24xx_nand_dt_ids[] = {
+ {
+ .compatible = "samsung,s3c2410-nand",
+ .data = &s3c2410_nand_devtype_data,
+ }, {
+ /* also compatible with s3c6400 */
+ .compatible = "samsung,s3c2412-nand",
+ .data = &s3c2412_nand_devtype_data,
+ }, {
+ .compatible = "samsung,s3c2440-nand",
+ .data = &s3c2440_nand_devtype_data,
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, s3c24xx_nand_dt_ids);
+
+static int s3c24xx_nand_probe_dt(struct platform_device *pdev)
+{
+ const struct s3c24XX_nand_devtype_data *devtype_data;
+ struct s3c2410_platform_nand *pdata;
+ struct s3c2410_nand_info *info = platform_get_drvdata(pdev);
+ struct device_node *np = pdev->dev.of_node, *child;
+ struct s3c2410_nand_set *sets;
+
+ devtype_data = of_device_get_match_data(&pdev->dev);
+ if (!devtype_data)
+ return -ENODEV;
+
+ info->cpu_type = devtype_data->type;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ pdev->dev.platform_data = pdata;
+
+ pdata->nr_sets = of_get_child_count(np);
+ if (!pdata->nr_sets)
+ return 0;
+
+ sets = devm_kzalloc(&pdev->dev, sizeof(*sets) * pdata->nr_sets,
+ GFP_KERNEL);
+ if (!sets)
+ return -ENOMEM;
+
+ pdata->sets = sets;
+
+ for_each_available_child_of_node(np, child) {
+ sets->name = (char *)child->name;
+ sets->of_node = child;
+ sets->nr_chips = 1;
+
+ of_node_get(child);
+
+ sets++;
+ }
+
+ return 0;
+}
+
+static int s3c24xx_nand_probe_pdata(struct platform_device *pdev)
+{
+ struct s3c2410_nand_info *info = platform_get_drvdata(pdev);
+
+ info->cpu_type = platform_get_device_id(pdev)->driver_data;
+
+ return 0;
+}
+
+/* s3c24xx_nand_probe
+ *
+ * called by device layer when it finds a device matching
+ * one our driver can handled. This code checks to see if
+ * it can allocate all necessary resources then calls the
+ * nand layer to look for devices
+*/
+static int s3c24xx_nand_probe(struct platform_device *pdev)
+{
+ struct s3c2410_platform_nand *plat;
+ struct s3c2410_nand_info *info;
+ struct s3c2410_nand_mtd *nmtd;
+ struct s3c2410_nand_set *sets;
+ struct resource *res;
+ int err = 0;
+ int size;
+ int nr_sets;
+ int setno;
+
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+ if (info == NULL) {
+ err = -ENOMEM;
+ goto exit_error;
+ }
+
+ platform_set_drvdata(pdev, info);
+
+ nand_hw_control_init(&info->controller);
+
+ /* get the clock source and enable it */
+
+ info->clk = devm_clk_get(&pdev->dev, "nand");
+ if (IS_ERR(info->clk)) {
+ dev_err(&pdev->dev, "failed to get clock\n");
+ err = -ENOENT;
+ goto exit_error;
+ }
+
+ s3c2410_nand_clk_set_state(info, CLOCK_ENABLE);
+
+ if (pdev->dev.of_node)
+ err = s3c24xx_nand_probe_dt(pdev);
+ else
+ err = s3c24xx_nand_probe_pdata(pdev);
+
+ if (err)
+ goto exit_error;
+
+ plat = to_nand_plat(pdev);
+
+ /* allocate and map the resource */
+
+ /* currently we assume we have the one resource */
+ res = pdev->resource;
+ size = resource_size(res);
+
+ info->device = &pdev->dev;
+ info->platform = plat;
+
+ info->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(info->regs)) {
+ err = PTR_ERR(info->regs);
+ goto exit_error;
+ }
+
+ dev_dbg(&pdev->dev, "mapped registers at %p\n", info->regs);
+
+ sets = (plat != NULL) ? plat->sets : NULL;
+ nr_sets = (plat != NULL) ? plat->nr_sets : 1;
+
+ info->mtd_count = nr_sets;
+
+ /* allocate our information */
+
+ size = nr_sets * sizeof(*info->mtds);
+ info->mtds = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
+ if (info->mtds == NULL) {
+ err = -ENOMEM;
+ goto exit_error;
+ }
+
+ /* initialise all possible chips */
+
+ nmtd = info->mtds;
+
+ for (setno = 0; setno < nr_sets; setno++, nmtd++) {
+ struct mtd_info *mtd = nand_to_mtd(&nmtd->chip);
+
+ pr_debug("initialising set %d (%p, info %p)\n",
+ setno, nmtd, info);
+
+ mtd->dev.parent = &pdev->dev;
+ s3c2410_nand_init_chip(info, nmtd, sets);
+
+ err = nand_scan_ident(mtd, (sets) ? sets->nr_chips : 1, NULL);
+ if (err)
+ goto exit_error;
+
+ err = s3c2410_nand_update_chip(info, nmtd);
+ if (err < 0)
+ goto exit_error;
+
+ err = nand_scan_tail(mtd);
+ if (err)
+ goto exit_error;
+
+ s3c2410_nand_add_partition(info, nmtd, sets);
+
+ if (sets != NULL)
+ sets++;
+ }
+
+ /* initialise the hardware */
+ err = s3c2410_nand_inithw(info);
+ if (err != 0)
+ goto exit_error;
+
+ err = s3c2410_nand_cpufreq_register(info);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to init cpufreq support\n");
+ goto exit_error;
+ }
+
+ if (allow_clk_suspend(info)) {
+ dev_info(&pdev->dev, "clock idle support enabled\n");
+ s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND);
+ }
+
+ return 0;
+
+ exit_error:
+ s3c24xx_nand_remove(pdev);
+
+ if (err == 0)
+ err = -EINVAL;
+ return err;
+}
+
+/* PM Support */
+#ifdef CONFIG_PM
+
+static int s3c24xx_nand_suspend(struct platform_device *dev, pm_message_t pm)
+{
+ struct s3c2410_nand_info *info = platform_get_drvdata(dev);
+
+ if (info) {
+ info->save_sel = readl(info->sel_reg);
+
+ /* For the moment, we must ensure nFCE is high during
+ * the time we are suspended. This really should be
+ * handled by suspending the MTDs we are using, but
+ * that is currently not the case. */
+
+ writel(info->save_sel | info->sel_bit, info->sel_reg);
+
+ s3c2410_nand_clk_set_state(info, CLOCK_DISABLE);
+ }
+
+ return 0;
+}
+
+static int s3c24xx_nand_resume(struct platform_device *dev)
+{
+ struct s3c2410_nand_info *info = platform_get_drvdata(dev);
+ unsigned long sel;
+
+ if (info) {
+ s3c2410_nand_clk_set_state(info, CLOCK_ENABLE);
+ s3c2410_nand_inithw(info);
+
+ /* Restore the state of the nFCE line. */
+
+ sel = readl(info->sel_reg);
+ sel &= ~info->sel_bit;
+ sel |= info->save_sel & info->sel_bit;
+ writel(sel, info->sel_reg);
+
+ s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND);
+ }
+
+ return 0;
+}
+
+#else
+#define s3c24xx_nand_suspend NULL
+#define s3c24xx_nand_resume NULL
+#endif
+
+/* driver device registration */
+
+static const struct platform_device_id s3c24xx_driver_ids[] = {
+ {
+ .name = "s3c2410-nand",
+ .driver_data = TYPE_S3C2410,
+ }, {
+ .name = "s3c2440-nand",
+ .driver_data = TYPE_S3C2440,
+ }, {
+ .name = "s3c2412-nand",
+ .driver_data = TYPE_S3C2412,
+ }, {
+ .name = "s3c6400-nand",
+ .driver_data = TYPE_S3C2412, /* compatible with 2412 */
+ },
+ { }
+};
+
+MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);
+
+static struct platform_driver s3c24xx_nand_driver = {
+ .probe = s3c24xx_nand_probe,
+ .remove = s3c24xx_nand_remove,
+ .suspend = s3c24xx_nand_suspend,
+ .resume = s3c24xx_nand_resume,
+ .id_table = s3c24xx_driver_ids,
+ .driver = {
+ .name = "s3c24xx-nand",
+ .of_match_table = s3c24xx_nand_dt_ids,
+ },
+};
+
+module_platform_driver(s3c24xx_nand_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
+MODULE_DESCRIPTION("S3C24XX MTD NAND driver");
diff --git a/drivers/mtd/nand/raw/sh_flctl.c b/drivers/mtd/nand/raw/sh_flctl.c
new file mode 100644
index 000000000000..c7abceffcc40
--- /dev/null
+++ b/drivers/mtd/nand/raw/sh_flctl.c
@@ -0,0 +1,1254 @@
+/*
+ * SuperH FLCTL nand controller
+ *
+ * Copyright (c) 2008 Renesas Solutions Corp.
+ * Copyright (c) 2008 Atom Create Engineering Co., Ltd.
+ *
+ * Based on fsl_elbc_nand.c, Copyright (c) 2006-2007 Freescale Semiconductor
+ *
+ * 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 of the License.
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/sh_dma.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/sh_flctl.h>
+
+static int flctl_4secc_ooblayout_sp_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (section)
+ return -ERANGE;
+
+ oobregion->offset = 0;
+ oobregion->length = chip->ecc.bytes;
+
+ return 0;
+}
+
+static int flctl_4secc_ooblayout_sp_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ if (section)
+ return -ERANGE;
+
+ oobregion->offset = 12;
+ oobregion->length = 4;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops flctl_4secc_oob_smallpage_ops = {
+ .ecc = flctl_4secc_ooblayout_sp_ecc,
+ .free = flctl_4secc_ooblayout_sp_free,
+};
+
+static int flctl_4secc_ooblayout_lp_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (section >= chip->ecc.steps)
+ return -ERANGE;
+
+ oobregion->offset = (section * 16) + 6;
+ oobregion->length = chip->ecc.bytes;
+
+ return 0;
+}
+
+static int flctl_4secc_ooblayout_lp_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (section >= chip->ecc.steps)
+ return -ERANGE;
+
+ oobregion->offset = section * 16;
+ oobregion->length = 6;
+
+ if (!section) {
+ oobregion->offset += 2;
+ oobregion->length -= 2;
+ }
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops flctl_4secc_oob_largepage_ops = {
+ .ecc = flctl_4secc_ooblayout_lp_ecc,
+ .free = flctl_4secc_ooblayout_lp_free,
+};
+
+static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
+
+static struct nand_bbt_descr flctl_4secc_smallpage = {
+ .options = NAND_BBT_SCAN2NDPAGE,
+ .offs = 11,
+ .len = 1,
+ .pattern = scan_ff_pattern,
+};
+
+static struct nand_bbt_descr flctl_4secc_largepage = {
+ .options = NAND_BBT_SCAN2NDPAGE,
+ .offs = 0,
+ .len = 2,
+ .pattern = scan_ff_pattern,
+};
+
+static void empty_fifo(struct sh_flctl *flctl)
+{
+ writel(flctl->flintdmacr_base | AC1CLR | AC0CLR, FLINTDMACR(flctl));
+ writel(flctl->flintdmacr_base, FLINTDMACR(flctl));
+}
+
+static void start_translation(struct sh_flctl *flctl)
+{
+ writeb(TRSTRT, FLTRCR(flctl));
+}
+
+static void timeout_error(struct sh_flctl *flctl, const char *str)
+{
+ dev_err(&flctl->pdev->dev, "Timeout occurred in %s\n", str);
+}
+
+static void wait_completion(struct sh_flctl *flctl)
+{
+ uint32_t timeout = LOOP_TIMEOUT_MAX;
+
+ while (timeout--) {
+ if (readb(FLTRCR(flctl)) & TREND) {
+ writeb(0x0, FLTRCR(flctl));
+ return;
+ }
+ udelay(1);
+ }
+
+ timeout_error(flctl, __func__);
+ writeb(0x0, FLTRCR(flctl));
+}
+
+static void flctl_dma_complete(void *param)
+{
+ struct sh_flctl *flctl = param;
+
+ complete(&flctl->dma_complete);
+}
+
+static void flctl_release_dma(struct sh_flctl *flctl)
+{
+ if (flctl->chan_fifo0_rx) {
+ dma_release_channel(flctl->chan_fifo0_rx);
+ flctl->chan_fifo0_rx = NULL;
+ }
+ if (flctl->chan_fifo0_tx) {
+ dma_release_channel(flctl->chan_fifo0_tx);
+ flctl->chan_fifo0_tx = NULL;
+ }
+}
+
+static void flctl_setup_dma(struct sh_flctl *flctl)
+{
+ dma_cap_mask_t mask;
+ struct dma_slave_config cfg;
+ struct platform_device *pdev = flctl->pdev;
+ struct sh_flctl_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ int ret;
+
+ if (!pdata)
+ return;
+
+ if (pdata->slave_id_fifo0_tx <= 0 || pdata->slave_id_fifo0_rx <= 0)
+ return;
+
+ /* We can only either use DMA for both Tx and Rx or not use it at all */
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ flctl->chan_fifo0_tx = dma_request_channel(mask, shdma_chan_filter,
+ (void *)(uintptr_t)pdata->slave_id_fifo0_tx);
+ dev_dbg(&pdev->dev, "%s: TX: got channel %p\n", __func__,
+ flctl->chan_fifo0_tx);
+
+ if (!flctl->chan_fifo0_tx)
+ return;
+
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.direction = DMA_MEM_TO_DEV;
+ cfg.dst_addr = flctl->fifo;
+ cfg.src_addr = 0;
+ ret = dmaengine_slave_config(flctl->chan_fifo0_tx, &cfg);
+ if (ret < 0)
+ goto err;
+
+ flctl->chan_fifo0_rx = dma_request_channel(mask, shdma_chan_filter,
+ (void *)(uintptr_t)pdata->slave_id_fifo0_rx);
+ dev_dbg(&pdev->dev, "%s: RX: got channel %p\n", __func__,
+ flctl->chan_fifo0_rx);
+
+ if (!flctl->chan_fifo0_rx)
+ goto err;
+
+ cfg.direction = DMA_DEV_TO_MEM;
+ cfg.dst_addr = 0;
+ cfg.src_addr = flctl->fifo;
+ ret = dmaengine_slave_config(flctl->chan_fifo0_rx, &cfg);
+ if (ret < 0)
+ goto err;
+
+ init_completion(&flctl->dma_complete);
+
+ return;
+
+err:
+ flctl_release_dma(flctl);
+}
+
+static void set_addr(struct mtd_info *mtd, int column, int page_addr)
+{
+ struct sh_flctl *flctl = mtd_to_flctl(mtd);
+ uint32_t addr = 0;
+
+ if (column == -1) {
+ addr = page_addr; /* ERASE1 */
+ } else if (page_addr != -1) {
+ /* SEQIN, READ0, etc.. */
+ if (flctl->chip.options & NAND_BUSWIDTH_16)
+ column >>= 1;
+ if (flctl->page_size) {
+ addr = column & 0x0FFF;
+ addr |= (page_addr & 0xff) << 16;
+ addr |= ((page_addr >> 8) & 0xff) << 24;
+ /* big than 128MB */
+ if (flctl->rw_ADRCNT == ADRCNT2_E) {
+ uint32_t addr2;
+ addr2 = (page_addr >> 16) & 0xff;
+ writel(addr2, FLADR2(flctl));
+ }
+ } else {
+ addr = column;
+ addr |= (page_addr & 0xff) << 8;
+ addr |= ((page_addr >> 8) & 0xff) << 16;
+ addr |= ((page_addr >> 16) & 0xff) << 24;
+ }
+ }
+ writel(addr, FLADR(flctl));
+}
+
+static void wait_rfifo_ready(struct sh_flctl *flctl)
+{
+ uint32_t timeout = LOOP_TIMEOUT_MAX;
+
+ while (timeout--) {
+ uint32_t val;
+ /* check FIFO */
+ val = readl(FLDTCNTR(flctl)) >> 16;
+ if (val & 0xFF)
+ return;
+ udelay(1);
+ }
+ timeout_error(flctl, __func__);
+}
+
+static void wait_wfifo_ready(struct sh_flctl *flctl)
+{
+ uint32_t len, timeout = LOOP_TIMEOUT_MAX;
+
+ while (timeout--) {
+ /* check FIFO */
+ len = (readl(FLDTCNTR(flctl)) >> 16) & 0xFF;
+ if (len >= 4)
+ return;
+ udelay(1);
+ }
+ timeout_error(flctl, __func__);
+}
+
+static enum flctl_ecc_res_t wait_recfifo_ready
+ (struct sh_flctl *flctl, int sector_number)
+{
+ uint32_t timeout = LOOP_TIMEOUT_MAX;
+ void __iomem *ecc_reg[4];
+ int i;
+ int state = FL_SUCCESS;
+ uint32_t data, size;
+
+ /*
+ * First this loops checks in FLDTCNTR if we are ready to read out the
+ * oob data. This is the case if either all went fine without errors or
+ * if the bottom part of the loop corrected the errors or marked them as
+ * uncorrectable and the controller is given time to push the data into
+ * the FIFO.
+ */
+ while (timeout--) {
+ /* check if all is ok and we can read out the OOB */
+ size = readl(FLDTCNTR(flctl)) >> 24;
+ if ((size & 0xFF) == 4)
+ return state;
+
+ /* check if a correction code has been calculated */
+ if (!(readl(FL4ECCCR(flctl)) & _4ECCEND)) {
+ /*
+ * either we wait for the fifo to be filled or a
+ * correction pattern is being generated
+ */
+ udelay(1);
+ continue;
+ }
+
+ /* check for an uncorrectable error */
+ if (readl(FL4ECCCR(flctl)) & _4ECCFA) {
+ /* check if we face a non-empty page */
+ for (i = 0; i < 512; i++) {
+ if (flctl->done_buff[i] != 0xff) {
+ state = FL_ERROR; /* can't correct */
+ break;
+ }
+ }
+
+ if (state == FL_SUCCESS)
+ dev_dbg(&flctl->pdev->dev,
+ "reading empty sector %d, ecc error ignored\n",
+ sector_number);
+
+ writel(0, FL4ECCCR(flctl));
+ continue;
+ }
+
+ /* start error correction */
+ ecc_reg[0] = FL4ECCRESULT0(flctl);
+ ecc_reg[1] = FL4ECCRESULT1(flctl);
+ ecc_reg[2] = FL4ECCRESULT2(flctl);
+ ecc_reg[3] = FL4ECCRESULT3(flctl);
+
+ for (i = 0; i < 3; i++) {
+ uint8_t org;
+ unsigned int index;
+
+ data = readl(ecc_reg[i]);
+
+ if (flctl->page_size)
+ index = (512 * sector_number) +
+ (data >> 16);
+ else
+ index = data >> 16;
+
+ org = flctl->done_buff[index];
+ flctl->done_buff[index] = org ^ (data & 0xFF);
+ }
+ state = FL_REPAIRABLE;
+ writel(0, FL4ECCCR(flctl));
+ }
+
+ timeout_error(flctl, __func__);
+ return FL_TIMEOUT; /* timeout */
+}
+
+static void wait_wecfifo_ready(struct sh_flctl *flctl)
+{
+ uint32_t timeout = LOOP_TIMEOUT_MAX;
+ uint32_t len;
+
+ while (timeout--) {
+ /* check FLECFIFO */
+ len = (readl(FLDTCNTR(flctl)) >> 24) & 0xFF;
+ if (len >= 4)
+ return;
+ udelay(1);
+ }
+ timeout_error(flctl, __func__);
+}
+
+static int flctl_dma_fifo0_transfer(struct sh_flctl *flctl, unsigned long *buf,
+ int len, enum dma_data_direction dir)
+{
+ struct dma_async_tx_descriptor *desc = NULL;
+ struct dma_chan *chan;
+ enum dma_transfer_direction tr_dir;
+ dma_addr_t dma_addr;
+ dma_cookie_t cookie;
+ uint32_t reg;
+ int ret;
+
+ if (dir == DMA_FROM_DEVICE) {
+ chan = flctl->chan_fifo0_rx;
+ tr_dir = DMA_DEV_TO_MEM;
+ } else {
+ chan = flctl->chan_fifo0_tx;
+ tr_dir = DMA_MEM_TO_DEV;
+ }
+
+ dma_addr = dma_map_single(chan->device->dev, buf, len, dir);
+
+ if (!dma_mapping_error(chan->device->dev, dma_addr))
+ desc = dmaengine_prep_slave_single(chan, dma_addr, len,
+ tr_dir, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+
+ if (desc) {
+ reg = readl(FLINTDMACR(flctl));
+ reg |= DREQ0EN;
+ writel(reg, FLINTDMACR(flctl));
+
+ desc->callback = flctl_dma_complete;
+ desc->callback_param = flctl;
+ cookie = dmaengine_submit(desc);
+ if (dma_submit_error(cookie)) {
+ ret = dma_submit_error(cookie);
+ dev_warn(&flctl->pdev->dev,
+ "DMA submit failed, falling back to PIO\n");
+ goto out;
+ }
+
+ dma_async_issue_pending(chan);
+ } else {
+ /* DMA failed, fall back to PIO */
+ flctl_release_dma(flctl);
+ dev_warn(&flctl->pdev->dev,
+ "DMA failed, falling back to PIO\n");
+ ret = -EIO;
+ goto out;
+ }
+
+ ret =
+ wait_for_completion_timeout(&flctl->dma_complete,
+ msecs_to_jiffies(3000));
+
+ if (ret <= 0) {
+ dmaengine_terminate_all(chan);
+ dev_err(&flctl->pdev->dev, "wait_for_completion_timeout\n");
+ }
+
+out:
+ reg = readl(FLINTDMACR(flctl));
+ reg &= ~DREQ0EN;
+ writel(reg, FLINTDMACR(flctl));
+
+ dma_unmap_single(chan->device->dev, dma_addr, len, dir);
+
+ /* ret > 0 is success */
+ return ret;
+}
+
+static void read_datareg(struct sh_flctl *flctl, int offset)
+{
+ unsigned long data;
+ unsigned long *buf = (unsigned long *)&flctl->done_buff[offset];
+
+ wait_completion(flctl);
+
+ data = readl(FLDATAR(flctl));
+ *buf = le32_to_cpu(data);
+}
+
+static void read_fiforeg(struct sh_flctl *flctl, int rlen, int offset)
+{
+ int i, len_4align;
+ unsigned long *buf = (unsigned long *)&flctl->done_buff[offset];
+
+ len_4align = (rlen + 3) / 4;
+
+ /* initiate DMA transfer */
+ if (flctl->chan_fifo0_rx && rlen >= 32 &&
+ flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_DEV_TO_MEM) > 0)
+ goto convert; /* DMA success */
+
+ /* do polling transfer */
+ for (i = 0; i < len_4align; i++) {
+ wait_rfifo_ready(flctl);
+ buf[i] = readl(FLDTFIFO(flctl));
+ }
+
+convert:
+ for (i = 0; i < len_4align; i++)
+ buf[i] = be32_to_cpu(buf[i]);
+}
+
+static enum flctl_ecc_res_t read_ecfiforeg
+ (struct sh_flctl *flctl, uint8_t *buff, int sector)
+{
+ int i;
+ enum flctl_ecc_res_t res;
+ unsigned long *ecc_buf = (unsigned long *)buff;
+
+ res = wait_recfifo_ready(flctl , sector);
+
+ if (res != FL_ERROR) {
+ for (i = 0; i < 4; i++) {
+ ecc_buf[i] = readl(FLECFIFO(flctl));
+ ecc_buf[i] = be32_to_cpu(ecc_buf[i]);
+ }
+ }
+
+ return res;
+}
+
+static void write_fiforeg(struct sh_flctl *flctl, int rlen,
+ unsigned int offset)
+{
+ int i, len_4align;
+ unsigned long *buf = (unsigned long *)&flctl->done_buff[offset];
+
+ len_4align = (rlen + 3) / 4;
+ for (i = 0; i < len_4align; i++) {
+ wait_wfifo_ready(flctl);
+ writel(cpu_to_be32(buf[i]), FLDTFIFO(flctl));
+ }
+}
+
+static void write_ec_fiforeg(struct sh_flctl *flctl, int rlen,
+ unsigned int offset)
+{
+ int i, len_4align;
+ unsigned long *buf = (unsigned long *)&flctl->done_buff[offset];
+
+ len_4align = (rlen + 3) / 4;
+
+ for (i = 0; i < len_4align; i++)
+ buf[i] = cpu_to_be32(buf[i]);
+
+ /* initiate DMA transfer */
+ if (flctl->chan_fifo0_tx && rlen >= 32 &&
+ flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_MEM_TO_DEV) > 0)
+ return; /* DMA success */
+
+ /* do polling transfer */
+ for (i = 0; i < len_4align; i++) {
+ wait_wecfifo_ready(flctl);
+ writel(buf[i], FLECFIFO(flctl));
+ }
+}
+
+static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_val)
+{
+ struct sh_flctl *flctl = mtd_to_flctl(mtd);
+ uint32_t flcmncr_val = flctl->flcmncr_base & ~SEL_16BIT;
+ uint32_t flcmdcr_val, addr_len_bytes = 0;
+
+ /* Set SNAND bit if page size is 2048byte */
+ if (flctl->page_size)
+ flcmncr_val |= SNAND_E;
+ else
+ flcmncr_val &= ~SNAND_E;
+
+ /* default FLCMDCR val */
+ flcmdcr_val = DOCMD1_E | DOADR_E;
+
+ /* Set for FLCMDCR */
+ switch (cmd) {
+ case NAND_CMD_ERASE1:
+ addr_len_bytes = flctl->erase_ADRCNT;
+ flcmdcr_val |= DOCMD2_E;
+ break;
+ case NAND_CMD_READ0:
+ case NAND_CMD_READOOB:
+ case NAND_CMD_RNDOUT:
+ addr_len_bytes = flctl->rw_ADRCNT;
+ flcmdcr_val |= CDSRC_E;
+ if (flctl->chip.options & NAND_BUSWIDTH_16)
+ flcmncr_val |= SEL_16BIT;
+ break;
+ case NAND_CMD_SEQIN:
+ /* This case is that cmd is READ0 or READ1 or READ00 */
+ flcmdcr_val &= ~DOADR_E; /* ONLY execute 1st cmd */
+ break;
+ case NAND_CMD_PAGEPROG:
+ addr_len_bytes = flctl->rw_ADRCNT;
+ flcmdcr_val |= DOCMD2_E | CDSRC_E | SELRW;
+ if (flctl->chip.options & NAND_BUSWIDTH_16)
+ flcmncr_val |= SEL_16BIT;
+ break;
+ case NAND_CMD_READID:
+ flcmncr_val &= ~SNAND_E;
+ flcmdcr_val |= CDSRC_E;
+ addr_len_bytes = ADRCNT_1;
+ break;
+ case NAND_CMD_STATUS:
+ case NAND_CMD_RESET:
+ flcmncr_val &= ~SNAND_E;
+ flcmdcr_val &= ~(DOADR_E | DOSR_E);
+ break;
+ default:
+ break;
+ }
+
+ /* Set address bytes parameter */
+ flcmdcr_val |= addr_len_bytes;
+
+ /* Now actually write */
+ writel(flcmncr_val, FLCMNCR(flctl));
+ writel(flcmdcr_val, FLCMDCR(flctl));
+ writel(flcmcdr_val, FLCMCDR(flctl));
+}
+
+static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ nand_read_page_op(chip, page, 0, buf, mtd->writesize);
+ if (oob_required)
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ return 0;
+}
+
+static int flctl_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required,
+ int page)
+{
+ nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);
+ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+ return nand_prog_page_end_op(chip);
+}
+
+static void execmd_read_page_sector(struct mtd_info *mtd, int page_addr)
+{
+ struct sh_flctl *flctl = mtd_to_flctl(mtd);
+ int sector, page_sectors;
+ enum flctl_ecc_res_t ecc_result;
+
+ page_sectors = flctl->page_size ? 4 : 1;
+
+ set_cmd_regs(mtd, NAND_CMD_READ0,
+ (NAND_CMD_READSTART << 8) | NAND_CMD_READ0);
+
+ writel(readl(FLCMNCR(flctl)) | ACM_SACCES_MODE | _4ECCCORRECT,
+ FLCMNCR(flctl));
+ writel(readl(FLCMDCR(flctl)) | page_sectors, FLCMDCR(flctl));
+ writel(page_addr << 2, FLADR(flctl));
+
+ empty_fifo(flctl);
+ start_translation(flctl);
+
+ for (sector = 0; sector < page_sectors; sector++) {
+ read_fiforeg(flctl, 512, 512 * sector);
+
+ ecc_result = read_ecfiforeg(flctl,
+ &flctl->done_buff[mtd->writesize + 16 * sector],
+ sector);
+
+ switch (ecc_result) {
+ case FL_REPAIRABLE:
+ dev_info(&flctl->pdev->dev,
+ "applied ecc on page 0x%x", page_addr);
+ mtd->ecc_stats.corrected++;
+ break;
+ case FL_ERROR:
+ dev_warn(&flctl->pdev->dev,
+ "page 0x%x contains corrupted data\n",
+ page_addr);
+ mtd->ecc_stats.failed++;
+ break;
+ default:
+ ;
+ }
+ }
+
+ wait_completion(flctl);
+
+ writel(readl(FLCMNCR(flctl)) & ~(ACM_SACCES_MODE | _4ECCCORRECT),
+ FLCMNCR(flctl));
+}
+
+static void execmd_read_oob(struct mtd_info *mtd, int page_addr)
+{
+ struct sh_flctl *flctl = mtd_to_flctl(mtd);
+ int page_sectors = flctl->page_size ? 4 : 1;
+ int i;
+
+ set_cmd_regs(mtd, NAND_CMD_READ0,
+ (NAND_CMD_READSTART << 8) | NAND_CMD_READ0);
+
+ empty_fifo(flctl);
+
+ for (i = 0; i < page_sectors; i++) {
+ set_addr(mtd, (512 + 16) * i + 512 , page_addr);
+ writel(16, FLDTCNTR(flctl));
+
+ start_translation(flctl);
+ read_fiforeg(flctl, 16, 16 * i);
+ wait_completion(flctl);
+ }
+}
+
+static void execmd_write_page_sector(struct mtd_info *mtd)
+{
+ struct sh_flctl *flctl = mtd_to_flctl(mtd);
+ int page_addr = flctl->seqin_page_addr;
+ int sector, page_sectors;
+
+ page_sectors = flctl->page_size ? 4 : 1;
+
+ set_cmd_regs(mtd, NAND_CMD_PAGEPROG,
+ (NAND_CMD_PAGEPROG << 8) | NAND_CMD_SEQIN);
+
+ empty_fifo(flctl);
+ writel(readl(FLCMNCR(flctl)) | ACM_SACCES_MODE, FLCMNCR(flctl));
+ writel(readl(FLCMDCR(flctl)) | page_sectors, FLCMDCR(flctl));
+ writel(page_addr << 2, FLADR(flctl));
+ start_translation(flctl);
+
+ for (sector = 0; sector < page_sectors; sector++) {
+ write_fiforeg(flctl, 512, 512 * sector);
+ write_ec_fiforeg(flctl, 16, mtd->writesize + 16 * sector);
+ }
+
+ wait_completion(flctl);
+ writel(readl(FLCMNCR(flctl)) & ~ACM_SACCES_MODE, FLCMNCR(flctl));
+}
+
+static void execmd_write_oob(struct mtd_info *mtd)
+{
+ struct sh_flctl *flctl = mtd_to_flctl(mtd);
+ int page_addr = flctl->seqin_page_addr;
+ int sector, page_sectors;
+
+ page_sectors = flctl->page_size ? 4 : 1;
+
+ set_cmd_regs(mtd, NAND_CMD_PAGEPROG,
+ (NAND_CMD_PAGEPROG << 8) | NAND_CMD_SEQIN);
+
+ for (sector = 0; sector < page_sectors; sector++) {
+ empty_fifo(flctl);
+ set_addr(mtd, sector * 528 + 512, page_addr);
+ writel(16, FLDTCNTR(flctl)); /* set read size */
+
+ start_translation(flctl);
+ write_fiforeg(flctl, 16, 16 * sector);
+ wait_completion(flctl);
+ }
+}
+
+static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+{
+ struct sh_flctl *flctl = mtd_to_flctl(mtd);
+ uint32_t read_cmd = 0;
+
+ pm_runtime_get_sync(&flctl->pdev->dev);
+
+ flctl->read_bytes = 0;
+ if (command != NAND_CMD_PAGEPROG)
+ flctl->index = 0;
+
+ switch (command) {
+ case NAND_CMD_READ1:
+ case NAND_CMD_READ0:
+ if (flctl->hwecc) {
+ /* read page with hwecc */
+ execmd_read_page_sector(mtd, page_addr);
+ break;
+ }
+ if (flctl->page_size)
+ set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8)
+ | command);
+ else
+ set_cmd_regs(mtd, command, command);
+
+ set_addr(mtd, 0, page_addr);
+
+ flctl->read_bytes = mtd->writesize + mtd->oobsize;
+ if (flctl->chip.options & NAND_BUSWIDTH_16)
+ column >>= 1;
+ flctl->index += column;
+ goto read_normal_exit;
+
+ case NAND_CMD_READOOB:
+ if (flctl->hwecc) {
+ /* read page with hwecc */
+ execmd_read_oob(mtd, page_addr);
+ break;
+ }
+
+ if (flctl->page_size) {
+ set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8)
+ | NAND_CMD_READ0);
+ set_addr(mtd, mtd->writesize, page_addr);
+ } else {
+ set_cmd_regs(mtd, command, command);
+ set_addr(mtd, 0, page_addr);
+ }
+ flctl->read_bytes = mtd->oobsize;
+ goto read_normal_exit;
+
+ case NAND_CMD_RNDOUT:
+ if (flctl->hwecc)
+ break;
+
+ if (flctl->page_size)
+ set_cmd_regs(mtd, command, (NAND_CMD_RNDOUTSTART << 8)
+ | command);
+ else
+ set_cmd_regs(mtd, command, command);
+
+ set_addr(mtd, column, 0);
+
+ flctl->read_bytes = mtd->writesize + mtd->oobsize - column;
+ goto read_normal_exit;
+
+ case NAND_CMD_READID:
+ set_cmd_regs(mtd, command, command);
+
+ /* READID is always performed using an 8-bit bus */
+ if (flctl->chip.options & NAND_BUSWIDTH_16)
+ column <<= 1;
+ set_addr(mtd, column, 0);
+
+ flctl->read_bytes = 8;
+ writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */
+ empty_fifo(flctl);
+ start_translation(flctl);
+ read_fiforeg(flctl, flctl->read_bytes, 0);
+ wait_completion(flctl);
+ break;
+
+ case NAND_CMD_ERASE1:
+ flctl->erase1_page_addr = page_addr;
+ break;
+
+ case NAND_CMD_ERASE2:
+ set_cmd_regs(mtd, NAND_CMD_ERASE1,
+ (command << 8) | NAND_CMD_ERASE1);
+ set_addr(mtd, -1, flctl->erase1_page_addr);
+ start_translation(flctl);
+ wait_completion(flctl);
+ break;
+
+ case NAND_CMD_SEQIN:
+ if (!flctl->page_size) {
+ /* output read command */
+ if (column >= mtd->writesize) {
+ column -= mtd->writesize;
+ read_cmd = NAND_CMD_READOOB;
+ } else if (column < 256) {
+ read_cmd = NAND_CMD_READ0;
+ } else {
+ column -= 256;
+ read_cmd = NAND_CMD_READ1;
+ }
+ }
+ flctl->seqin_column = column;
+ flctl->seqin_page_addr = page_addr;
+ flctl->seqin_read_cmd = read_cmd;
+ break;
+
+ case NAND_CMD_PAGEPROG:
+ empty_fifo(flctl);
+ if (!flctl->page_size) {
+ set_cmd_regs(mtd, NAND_CMD_SEQIN,
+ flctl->seqin_read_cmd);
+ set_addr(mtd, -1, -1);
+ writel(0, FLDTCNTR(flctl)); /* set 0 size */
+ start_translation(flctl);
+ wait_completion(flctl);
+ }
+ if (flctl->hwecc) {
+ /* write page with hwecc */
+ if (flctl->seqin_column == mtd->writesize)
+ execmd_write_oob(mtd);
+ else if (!flctl->seqin_column)
+ execmd_write_page_sector(mtd);
+ else
+ pr_err("Invalid address !?\n");
+ break;
+ }
+ set_cmd_regs(mtd, command, (command << 8) | NAND_CMD_SEQIN);
+ set_addr(mtd, flctl->seqin_column, flctl->seqin_page_addr);
+ writel(flctl->index, FLDTCNTR(flctl)); /* set write size */
+ start_translation(flctl);
+ write_fiforeg(flctl, flctl->index, 0);
+ wait_completion(flctl);
+ break;
+
+ case NAND_CMD_STATUS:
+ set_cmd_regs(mtd, command, command);
+ set_addr(mtd, -1, -1);
+
+ flctl->read_bytes = 1;
+ writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */
+ start_translation(flctl);
+ read_datareg(flctl, 0); /* read and end */
+ break;
+
+ case NAND_CMD_RESET:
+ set_cmd_regs(mtd, command, command);
+ set_addr(mtd, -1, -1);
+
+ writel(0, FLDTCNTR(flctl)); /* set 0 size */
+ start_translation(flctl);
+ wait_completion(flctl);
+ break;
+
+ default:
+ break;
+ }
+ goto runtime_exit;
+
+read_normal_exit:
+ writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */
+ empty_fifo(flctl);
+ start_translation(flctl);
+ read_fiforeg(flctl, flctl->read_bytes, 0);
+ wait_completion(flctl);
+runtime_exit:
+ pm_runtime_put_sync(&flctl->pdev->dev);
+ return;
+}
+
+static void flctl_select_chip(struct mtd_info *mtd, int chipnr)
+{
+ struct sh_flctl *flctl = mtd_to_flctl(mtd);
+ int ret;
+
+ switch (chipnr) {
+ case -1:
+ flctl->flcmncr_base &= ~CE0_ENABLE;
+
+ pm_runtime_get_sync(&flctl->pdev->dev);
+ writel(flctl->flcmncr_base, FLCMNCR(flctl));
+
+ if (flctl->qos_request) {
+ dev_pm_qos_remove_request(&flctl->pm_qos);
+ flctl->qos_request = 0;
+ }
+
+ pm_runtime_put_sync(&flctl->pdev->dev);
+ break;
+ case 0:
+ flctl->flcmncr_base |= CE0_ENABLE;
+
+ if (!flctl->qos_request) {
+ ret = dev_pm_qos_add_request(&flctl->pdev->dev,
+ &flctl->pm_qos,
+ DEV_PM_QOS_RESUME_LATENCY,
+ 100);
+ if (ret < 0)
+ dev_err(&flctl->pdev->dev,
+ "PM QoS request failed: %d\n", ret);
+ flctl->qos_request = 1;
+ }
+
+ if (flctl->holden) {
+ pm_runtime_get_sync(&flctl->pdev->dev);
+ writel(HOLDEN, FLHOLDCR(flctl));
+ pm_runtime_put_sync(&flctl->pdev->dev);
+ }
+ break;
+ default:
+ BUG();
+ }
+}
+
+static void flctl_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ struct sh_flctl *flctl = mtd_to_flctl(mtd);
+
+ memcpy(&flctl->done_buff[flctl->index], buf, len);
+ flctl->index += len;
+}
+
+static uint8_t flctl_read_byte(struct mtd_info *mtd)
+{
+ struct sh_flctl *flctl = mtd_to_flctl(mtd);
+ uint8_t data;
+
+ data = flctl->done_buff[flctl->index];
+ flctl->index++;
+ return data;
+}
+
+static uint16_t flctl_read_word(struct mtd_info *mtd)
+{
+ struct sh_flctl *flctl = mtd_to_flctl(mtd);
+ uint16_t *buf = (uint16_t *)&flctl->done_buff[flctl->index];
+
+ flctl->index += 2;
+ return *buf;
+}
+
+static void flctl_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct sh_flctl *flctl = mtd_to_flctl(mtd);
+
+ memcpy(buf, &flctl->done_buff[flctl->index], len);
+ flctl->index += len;
+}
+
+static int flctl_chip_init_tail(struct mtd_info *mtd)
+{
+ struct sh_flctl *flctl = mtd_to_flctl(mtd);
+ struct nand_chip *chip = &flctl->chip;
+
+ if (mtd->writesize == 512) {
+ flctl->page_size = 0;
+ if (chip->chipsize > (32 << 20)) {
+ /* big than 32MB */
+ flctl->rw_ADRCNT = ADRCNT_4;
+ flctl->erase_ADRCNT = ADRCNT_3;
+ } else if (chip->chipsize > (2 << 16)) {
+ /* big than 128KB */
+ flctl->rw_ADRCNT = ADRCNT_3;
+ flctl->erase_ADRCNT = ADRCNT_2;
+ } else {
+ flctl->rw_ADRCNT = ADRCNT_2;
+ flctl->erase_ADRCNT = ADRCNT_1;
+ }
+ } else {
+ flctl->page_size = 1;
+ if (chip->chipsize > (128 << 20)) {
+ /* big than 128MB */
+ flctl->rw_ADRCNT = ADRCNT2_E;
+ flctl->erase_ADRCNT = ADRCNT_3;
+ } else if (chip->chipsize > (8 << 16)) {
+ /* big than 512KB */
+ flctl->rw_ADRCNT = ADRCNT_4;
+ flctl->erase_ADRCNT = ADRCNT_2;
+ } else {
+ flctl->rw_ADRCNT = ADRCNT_3;
+ flctl->erase_ADRCNT = ADRCNT_1;
+ }
+ }
+
+ if (flctl->hwecc) {
+ if (mtd->writesize == 512) {
+ mtd_set_ooblayout(mtd, &flctl_4secc_oob_smallpage_ops);
+ chip->badblock_pattern = &flctl_4secc_smallpage;
+ } else {
+ mtd_set_ooblayout(mtd, &flctl_4secc_oob_largepage_ops);
+ chip->badblock_pattern = &flctl_4secc_largepage;
+ }
+
+ chip->ecc.size = 512;
+ chip->ecc.bytes = 10;
+ chip->ecc.strength = 4;
+ chip->ecc.read_page = flctl_read_page_hwecc;
+ chip->ecc.write_page = flctl_write_page_hwecc;
+ chip->ecc.mode = NAND_ECC_HW;
+
+ /* 4 symbols ECC enabled */
+ flctl->flcmncr_base |= _4ECCEN;
+ } else {
+ chip->ecc.mode = NAND_ECC_SOFT;
+ chip->ecc.algo = NAND_ECC_HAMMING;
+ }
+
+ return 0;
+}
+
+static irqreturn_t flctl_handle_flste(int irq, void *dev_id)
+{
+ struct sh_flctl *flctl = dev_id;
+
+ dev_err(&flctl->pdev->dev, "flste irq: %x\n", readl(FLINTDMACR(flctl)));
+ writel(flctl->flintdmacr_base, FLINTDMACR(flctl));
+
+ return IRQ_HANDLED;
+}
+
+struct flctl_soc_config {
+ unsigned long flcmncr_val;
+ unsigned has_hwecc:1;
+ unsigned use_holden:1;
+};
+
+static struct flctl_soc_config flctl_sh7372_config = {
+ .flcmncr_val = CLK_16B_12L_4H | TYPESEL_SET | SHBUSSEL,
+ .has_hwecc = 1,
+ .use_holden = 1,
+};
+
+static const struct of_device_id of_flctl_match[] = {
+ { .compatible = "renesas,shmobile-flctl-sh7372",
+ .data = &flctl_sh7372_config },
+ {},
+};
+MODULE_DEVICE_TABLE(of, of_flctl_match);
+
+static struct sh_flctl_platform_data *flctl_parse_dt(struct device *dev)
+{
+ const struct flctl_soc_config *config;
+ struct sh_flctl_platform_data *pdata;
+
+ config = of_device_get_match_data(dev);
+ if (!config) {
+ dev_err(dev, "%s: no OF configuration attached\n", __func__);
+ return NULL;
+ }
+
+ pdata = devm_kzalloc(dev, sizeof(struct sh_flctl_platform_data),
+ GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+
+ /* set SoC specific options */
+ pdata->flcmncr_val = config->flcmncr_val;
+ pdata->has_hwecc = config->has_hwecc;
+ pdata->use_holden = config->use_holden;
+
+ return pdata;
+}
+
+static int flctl_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct sh_flctl *flctl;
+ struct mtd_info *flctl_mtd;
+ struct nand_chip *nand;
+ struct sh_flctl_platform_data *pdata;
+ int ret;
+ int irq;
+
+ flctl = devm_kzalloc(&pdev->dev, sizeof(struct sh_flctl), GFP_KERNEL);
+ if (!flctl)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ flctl->reg = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(flctl->reg))
+ return PTR_ERR(flctl->reg);
+ flctl->fifo = res->start + 0x24; /* FLDTFIFO */
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "failed to get flste irq data: %d\n", irq);
+ return irq;
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq, flctl_handle_flste, IRQF_SHARED,
+ "flste", flctl);
+ if (ret) {
+ dev_err(&pdev->dev, "request interrupt failed.\n");
+ return ret;
+ }
+
+ if (pdev->dev.of_node)
+ pdata = flctl_parse_dt(&pdev->dev);
+ else
+ pdata = dev_get_platdata(&pdev->dev);
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "no setup data defined\n");
+ return -EINVAL;
+ }
+
+ platform_set_drvdata(pdev, flctl);
+ nand = &flctl->chip;
+ flctl_mtd = nand_to_mtd(nand);
+ nand_set_flash_node(nand, pdev->dev.of_node);
+ flctl_mtd->dev.parent = &pdev->dev;
+ flctl->pdev = pdev;
+ flctl->hwecc = pdata->has_hwecc;
+ flctl->holden = pdata->use_holden;
+ flctl->flcmncr_base = pdata->flcmncr_val;
+ flctl->flintdmacr_base = flctl->hwecc ? (STERINTE | ECERB) : STERINTE;
+
+ /* Set address of hardware control function */
+ /* 20 us command delay time */
+ nand->chip_delay = 20;
+
+ nand->read_byte = flctl_read_byte;
+ nand->read_word = flctl_read_word;
+ nand->write_buf = flctl_write_buf;
+ nand->read_buf = flctl_read_buf;
+ nand->select_chip = flctl_select_chip;
+ nand->cmdfunc = flctl_cmdfunc;
+ nand->set_features = nand_get_set_features_notsupp;
+ nand->get_features = nand_get_set_features_notsupp;
+
+ if (pdata->flcmncr_val & SEL_16BIT)
+ nand->options |= NAND_BUSWIDTH_16;
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_resume(&pdev->dev);
+
+ flctl_setup_dma(flctl);
+
+ ret = nand_scan_ident(flctl_mtd, 1, NULL);
+ if (ret)
+ goto err_chip;
+
+ if (nand->options & NAND_BUSWIDTH_16) {
+ /*
+ * NAND_BUSWIDTH_16 may have been set by nand_scan_ident().
+ * Add the SEL_16BIT flag in pdata->flcmncr_val and re-assign
+ * flctl->flcmncr_base to pdata->flcmncr_val.
+ */
+ pdata->flcmncr_val |= SEL_16BIT;
+ flctl->flcmncr_base = pdata->flcmncr_val;
+ }
+
+ ret = flctl_chip_init_tail(flctl_mtd);
+ if (ret)
+ goto err_chip;
+
+ ret = nand_scan_tail(flctl_mtd);
+ if (ret)
+ goto err_chip;
+
+ ret = mtd_device_register(flctl_mtd, pdata->parts, pdata->nr_parts);
+ if (ret)
+ goto cleanup_nand;
+
+ return 0;
+
+cleanup_nand:
+ nand_cleanup(nand);
+err_chip:
+ flctl_release_dma(flctl);
+ pm_runtime_disable(&pdev->dev);
+ return ret;
+}
+
+static int flctl_remove(struct platform_device *pdev)
+{
+ struct sh_flctl *flctl = platform_get_drvdata(pdev);
+
+ flctl_release_dma(flctl);
+ nand_release(nand_to_mtd(&flctl->chip));
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver flctl_driver = {
+ .remove = flctl_remove,
+ .driver = {
+ .name = "sh_flctl",
+ .of_match_table = of_match_ptr(of_flctl_match),
+ },
+};
+
+module_platform_driver_probe(flctl_driver, flctl_probe);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Yoshihiro Shimoda");
+MODULE_DESCRIPTION("SuperH FLCTL driver");
+MODULE_ALIAS("platform:sh_flctl");
diff --git a/drivers/mtd/nand/raw/sharpsl.c b/drivers/mtd/nand/raw/sharpsl.c
new file mode 100644
index 000000000000..e93df02c825e
--- /dev/null
+++ b/drivers/mtd/nand/raw/sharpsl.c
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2004 Richard Purdie
+ * Copyright (C) 2008 Dmitry Baryshkov
+ *
+ * Based on Sharp's NAND driver sharp_sl.c
+ *
+ * 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.
+ *
+ */
+
+#include <linux/genhd.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/sharpsl.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+#include <mach/hardware.h>
+#include <asm/mach-types.h>
+
+struct sharpsl_nand {
+ struct nand_chip chip;
+
+ void __iomem *io;
+};
+
+static inline struct sharpsl_nand *mtd_to_sharpsl(struct mtd_info *mtd)
+{
+ return container_of(mtd_to_nand(mtd), struct sharpsl_nand, chip);
+}
+
+/* register offset */
+#define ECCLPLB 0x00 /* line parity 7 - 0 bit */
+#define ECCLPUB 0x04 /* line parity 15 - 8 bit */
+#define ECCCP 0x08 /* column parity 5 - 0 bit */
+#define ECCCNTR 0x0C /* ECC byte counter */
+#define ECCCLRR 0x10 /* cleare ECC */
+#define FLASHIO 0x14 /* Flash I/O */
+#define FLASHCTL 0x18 /* Flash Control */
+
+/* Flash control bit */
+#define FLRYBY (1 << 5)
+#define FLCE1 (1 << 4)
+#define FLWP (1 << 3)
+#define FLALE (1 << 2)
+#define FLCLE (1 << 1)
+#define FLCE0 (1 << 0)
+
+/*
+ * hardware specific access to control-lines
+ * ctrl:
+ * NAND_CNE: bit 0 -> ! bit 0 & 4
+ * NAND_CLE: bit 1 -> bit 1
+ * NAND_ALE: bit 2 -> bit 2
+ *
+ */
+static void sharpsl_nand_hwcontrol(struct mtd_info *mtd, int cmd,
+ unsigned int ctrl)
+{
+ struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd);
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (ctrl & NAND_CTRL_CHANGE) {
+ unsigned char bits = ctrl & 0x07;
+
+ bits |= (ctrl & 0x01) << 4;
+
+ bits ^= 0x11;
+
+ writeb((readb(sharpsl->io + FLASHCTL) & ~0x17) | bits, sharpsl->io + FLASHCTL);
+ }
+
+ if (cmd != NAND_CMD_NONE)
+ writeb(cmd, chip->IO_ADDR_W);
+}
+
+static int sharpsl_nand_dev_ready(struct mtd_info *mtd)
+{
+ struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd);
+ return !((readb(sharpsl->io + FLASHCTL) & FLRYBY) == 0);
+}
+
+static void sharpsl_nand_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+ struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd);
+ writeb(0, sharpsl->io + ECCCLRR);
+}
+
+static int sharpsl_nand_calculate_ecc(struct mtd_info *mtd, const u_char * dat, u_char * ecc_code)
+{
+ struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd);
+ ecc_code[0] = ~readb(sharpsl->io + ECCLPUB);
+ ecc_code[1] = ~readb(sharpsl->io + ECCLPLB);
+ ecc_code[2] = (~readb(sharpsl->io + ECCCP) << 2) | 0x03;
+ return readb(sharpsl->io + ECCCNTR) != 0;
+}
+
+/*
+ * Main initialization routine
+ */
+static int sharpsl_nand_probe(struct platform_device *pdev)
+{
+ struct nand_chip *this;
+ struct mtd_info *mtd;
+ struct resource *r;
+ int err = 0;
+ struct sharpsl_nand *sharpsl;
+ struct sharpsl_nand_platform_data *data = dev_get_platdata(&pdev->dev);
+
+ if (!data) {
+ dev_err(&pdev->dev, "no platform data!\n");
+ return -EINVAL;
+ }
+
+ /* Allocate memory for MTD device structure and private data */
+ sharpsl = kzalloc(sizeof(struct sharpsl_nand), GFP_KERNEL);
+ if (!sharpsl)
+ return -ENOMEM;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "no io memory resource defined!\n");
+ err = -ENODEV;
+ goto err_get_res;
+ }
+
+ /* map physical address */
+ sharpsl->io = ioremap(r->start, resource_size(r));
+ if (!sharpsl->io) {
+ dev_err(&pdev->dev, "ioremap to access Sharp SL NAND chip failed\n");
+ err = -EIO;
+ goto err_ioremap;
+ }
+
+ /* Get pointer to private data */
+ this = (struct nand_chip *)(&sharpsl->chip);
+
+ /* Link the private data with the MTD structure */
+ mtd = nand_to_mtd(this);
+ mtd->dev.parent = &pdev->dev;
+ mtd_set_ooblayout(mtd, data->ecc_layout);
+
+ platform_set_drvdata(pdev, sharpsl);
+
+ /*
+ * PXA initialize
+ */
+ writeb(readb(sharpsl->io + FLASHCTL) | FLWP, sharpsl->io + FLASHCTL);
+
+ /* Set address of NAND IO lines */
+ this->IO_ADDR_R = sharpsl->io + FLASHIO;
+ this->IO_ADDR_W = sharpsl->io + FLASHIO;
+ /* Set address of hardware control function */
+ this->cmd_ctrl = sharpsl_nand_hwcontrol;
+ this->dev_ready = sharpsl_nand_dev_ready;
+ /* 15 us command delay time */
+ this->chip_delay = 15;
+ /* set eccmode using hardware ECC */
+ this->ecc.mode = NAND_ECC_HW;
+ this->ecc.size = 256;
+ this->ecc.bytes = 3;
+ this->ecc.strength = 1;
+ this->badblock_pattern = data->badblock_pattern;
+ this->ecc.hwctl = sharpsl_nand_enable_hwecc;
+ this->ecc.calculate = sharpsl_nand_calculate_ecc;
+ this->ecc.correct = nand_correct_data;
+
+ /* Scan to find existence of the device */
+ err = nand_scan(mtd, 1);
+ if (err)
+ goto err_scan;
+
+ /* Register the partitions */
+ mtd->name = "sharpsl-nand";
+
+ err = mtd_device_parse_register(mtd, data->part_parsers, NULL,
+ data->partitions, data->nr_partitions);
+ if (err)
+ goto err_add;
+
+ /* Return happy */
+ return 0;
+
+err_add:
+ nand_release(mtd);
+
+err_scan:
+ iounmap(sharpsl->io);
+err_ioremap:
+err_get_res:
+ kfree(sharpsl);
+ return err;
+}
+
+/*
+ * Clean up routine
+ */
+static int sharpsl_nand_remove(struct platform_device *pdev)
+{
+ struct sharpsl_nand *sharpsl = platform_get_drvdata(pdev);
+
+ /* Release resources, unregister device */
+ nand_release(nand_to_mtd(&sharpsl->chip));
+
+ iounmap(sharpsl->io);
+
+ /* Free the MTD device structure */
+ kfree(sharpsl);
+
+ return 0;
+}
+
+static struct platform_driver sharpsl_nand_driver = {
+ .driver = {
+ .name = "sharpsl-nand",
+ },
+ .probe = sharpsl_nand_probe,
+ .remove = sharpsl_nand_remove,
+};
+
+module_platform_driver(sharpsl_nand_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
+MODULE_DESCRIPTION("Device specific logic for NAND flash on Sharp SL-C7xx Series");
diff --git a/drivers/mtd/nand/raw/sm_common.c b/drivers/mtd/nand/raw/sm_common.c
new file mode 100644
index 000000000000..7f5044a79f01
--- /dev/null
+++ b/drivers/mtd/nand/raw/sm_common.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright © 2009 - Maxim Levitsky
+ * Common routines & support for xD format
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/module.h>
+#include <linux/sizes.h>
+#include "sm_common.h"
+
+static int oob_sm_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ if (section > 1)
+ return -ERANGE;
+
+ oobregion->length = 3;
+ oobregion->offset = ((section + 1) * 8) - 3;
+
+ return 0;
+}
+
+static int oob_sm_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ switch (section) {
+ case 0:
+ /* reserved */
+ oobregion->offset = 0;
+ oobregion->length = 4;
+ break;
+ case 1:
+ /* LBA1 */
+ oobregion->offset = 6;
+ oobregion->length = 2;
+ break;
+ case 2:
+ /* LBA2 */
+ oobregion->offset = 11;
+ oobregion->length = 2;
+ break;
+ default:
+ return -ERANGE;
+ }
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops oob_sm_ops = {
+ .ecc = oob_sm_ooblayout_ecc,
+ .free = oob_sm_ooblayout_free,
+};
+
+/* NOTE: This layout is is not compatabable with SmartMedia, */
+/* because the 256 byte devices have page depenent oob layout */
+/* However it does preserve the bad block markers */
+/* If you use smftl, it will bypass this and work correctly */
+/* If you not, then you break SmartMedia compliance anyway */
+
+static int oob_sm_small_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ if (section)
+ return -ERANGE;
+
+ oobregion->length = 3;
+ oobregion->offset = 0;
+
+ return 0;
+}
+
+static int oob_sm_small_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ switch (section) {
+ case 0:
+ /* reserved */
+ oobregion->offset = 3;
+ oobregion->length = 2;
+ break;
+ case 1:
+ /* LBA1 */
+ oobregion->offset = 6;
+ oobregion->length = 2;
+ break;
+ default:
+ return -ERANGE;
+ }
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops oob_sm_small_ops = {
+ .ecc = oob_sm_small_ooblayout_ecc,
+ .free = oob_sm_small_ooblayout_free,
+};
+
+static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ struct mtd_oob_ops ops;
+ struct sm_oob oob;
+ int ret;
+
+ memset(&oob, -1, SM_OOB_SIZE);
+ oob.block_status = 0x0F;
+
+ /* As long as this function is called on erase block boundaries
+ it will work correctly for 256 byte nand */
+ ops.mode = MTD_OPS_PLACE_OOB;
+ ops.ooboffs = 0;
+ ops.ooblen = mtd->oobsize;
+ ops.oobbuf = (void *)&oob;
+ ops.datbuf = NULL;
+
+
+ ret = mtd_write_oob(mtd, ofs, &ops);
+ if (ret < 0 || ops.oobretlen != SM_OOB_SIZE) {
+ pr_notice("sm_common: can't mark sector at %i as bad\n",
+ (int)ofs);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static struct nand_flash_dev nand_smartmedia_flash_ids[] = {
+ LEGACY_ID_NAND("SmartMedia 2MiB 3,3V ROM", 0x5d, 2, SZ_8K, NAND_ROM),
+ LEGACY_ID_NAND("SmartMedia 4MiB 3,3V", 0xe3, 4, SZ_8K, 0),
+ LEGACY_ID_NAND("SmartMedia 4MiB 3,3/5V", 0xe5, 4, SZ_8K, 0),
+ LEGACY_ID_NAND("SmartMedia 4MiB 5V", 0x6b, 4, SZ_8K, 0),
+ LEGACY_ID_NAND("SmartMedia 4MiB 3,3V ROM", 0xd5, 4, SZ_8K, NAND_ROM),
+ LEGACY_ID_NAND("SmartMedia 8MiB 3,3V", 0xe6, 8, SZ_8K, 0),
+ LEGACY_ID_NAND("SmartMedia 8MiB 3,3V ROM", 0xd6, 8, SZ_8K, NAND_ROM),
+ LEGACY_ID_NAND("SmartMedia 16MiB 3,3V", 0x73, 16, SZ_16K, 0),
+ LEGACY_ID_NAND("SmartMedia 16MiB 3,3V ROM", 0x57, 16, SZ_16K, NAND_ROM),
+ LEGACY_ID_NAND("SmartMedia 32MiB 3,3V", 0x75, 32, SZ_16K, 0),
+ LEGACY_ID_NAND("SmartMedia 32MiB 3,3V ROM", 0x58, 32, SZ_16K, NAND_ROM),
+ LEGACY_ID_NAND("SmartMedia 64MiB 3,3V", 0x76, 64, SZ_16K, 0),
+ LEGACY_ID_NAND("SmartMedia 64MiB 3,3V ROM", 0xd9, 64, SZ_16K, NAND_ROM),
+ LEGACY_ID_NAND("SmartMedia 128MiB 3,3V", 0x79, 128, SZ_16K, 0),
+ LEGACY_ID_NAND("SmartMedia 128MiB 3,3V ROM", 0xda, 128, SZ_16K, NAND_ROM),
+ LEGACY_ID_NAND("SmartMedia 256MiB 3, 3V", 0x71, 256, SZ_16K, 0),
+ LEGACY_ID_NAND("SmartMedia 256MiB 3,3V ROM", 0x5b, 256, SZ_16K, NAND_ROM),
+ {NULL}
+};
+
+static struct nand_flash_dev nand_xd_flash_ids[] = {
+ LEGACY_ID_NAND("xD 16MiB 3,3V", 0x73, 16, SZ_16K, 0),
+ LEGACY_ID_NAND("xD 32MiB 3,3V", 0x75, 32, SZ_16K, 0),
+ LEGACY_ID_NAND("xD 64MiB 3,3V", 0x76, 64, SZ_16K, 0),
+ LEGACY_ID_NAND("xD 128MiB 3,3V", 0x79, 128, SZ_16K, 0),
+ LEGACY_ID_NAND("xD 256MiB 3,3V", 0x71, 256, SZ_16K, NAND_BROKEN_XD),
+ LEGACY_ID_NAND("xD 512MiB 3,3V", 0xdc, 512, SZ_16K, NAND_BROKEN_XD),
+ LEGACY_ID_NAND("xD 1GiB 3,3V", 0xd3, 1024, SZ_16K, NAND_BROKEN_XD),
+ LEGACY_ID_NAND("xD 2GiB 3,3V", 0xd5, 2048, SZ_16K, NAND_BROKEN_XD),
+ {NULL}
+};
+
+int sm_register_device(struct mtd_info *mtd, int smartmedia)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int ret;
+
+ chip->options |= NAND_SKIP_BBTSCAN;
+
+ /* Scan for card properties */
+ ret = nand_scan_ident(mtd, 1, smartmedia ?
+ nand_smartmedia_flash_ids : nand_xd_flash_ids);
+
+ if (ret)
+ return ret;
+
+ /* Bad block marker position */
+ chip->badblockpos = 0x05;
+ chip->badblockbits = 7;
+ chip->block_markbad = sm_block_markbad;
+
+ /* ECC layout */
+ if (mtd->writesize == SM_SECTOR_SIZE)
+ mtd_set_ooblayout(mtd, &oob_sm_ops);
+ else if (mtd->writesize == SM_SMALL_PAGE)
+ mtd_set_ooblayout(mtd, &oob_sm_small_ops);
+ else
+ return -ENODEV;
+
+ ret = nand_scan_tail(mtd);
+
+ if (ret)
+ return ret;
+
+ return mtd_device_register(mtd, NULL, 0);
+}
+EXPORT_SYMBOL_GPL(sm_register_device);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>");
+MODULE_DESCRIPTION("Common SmartMedia/xD functions");
diff --git a/drivers/mtd/nand/sm_common.h b/drivers/mtd/nand/raw/sm_common.h
index 1581671b05ae..1581671b05ae 100644
--- a/drivers/mtd/nand/sm_common.h
+++ b/drivers/mtd/nand/raw/sm_common.h
diff --git a/drivers/mtd/nand/raw/socrates_nand.c b/drivers/mtd/nand/raw/socrates_nand.c
new file mode 100644
index 000000000000..9824a9923583
--- /dev/null
+++ b/drivers/mtd/nand/raw/socrates_nand.c
@@ -0,0 +1,241 @@
+/*
+ * Copyright © 2008 Ilya Yanok, Emcraft Systems
+ *
+ *
+ * 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.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/io.h>
+
+#define FPGA_NAND_CMD_MASK (0x7 << 28)
+#define FPGA_NAND_CMD_COMMAND (0x0 << 28)
+#define FPGA_NAND_CMD_ADDR (0x1 << 28)
+#define FPGA_NAND_CMD_READ (0x2 << 28)
+#define FPGA_NAND_CMD_WRITE (0x3 << 28)
+#define FPGA_NAND_BUSY (0x1 << 15)
+#define FPGA_NAND_ENABLE (0x1 << 31)
+#define FPGA_NAND_DATA_SHIFT 16
+
+struct socrates_nand_host {
+ struct nand_chip nand_chip;
+ void __iomem *io_base;
+ struct device *dev;
+};
+
+/**
+ * socrates_nand_write_buf - write buffer to chip
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ */
+static void socrates_nand_write_buf(struct mtd_info *mtd,
+ const uint8_t *buf, int len)
+{
+ int i;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct socrates_nand_host *host = nand_get_controller_data(this);
+
+ for (i = 0; i < len; i++) {
+ out_be32(host->io_base, FPGA_NAND_ENABLE |
+ FPGA_NAND_CMD_WRITE |
+ (buf[i] << FPGA_NAND_DATA_SHIFT));
+ }
+}
+
+/**
+ * socrates_nand_read_buf - read chip data into buffer
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
+ */
+static void socrates_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ int i;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct socrates_nand_host *host = nand_get_controller_data(this);
+ uint32_t val;
+
+ val = FPGA_NAND_ENABLE | FPGA_NAND_CMD_READ;
+
+ out_be32(host->io_base, val);
+ for (i = 0; i < len; i++) {
+ buf[i] = (in_be32(host->io_base) >>
+ FPGA_NAND_DATA_SHIFT) & 0xff;
+ }
+}
+
+/**
+ * socrates_nand_read_byte - read one byte from the chip
+ * @mtd: MTD device structure
+ */
+static uint8_t socrates_nand_read_byte(struct mtd_info *mtd)
+{
+ uint8_t byte;
+ socrates_nand_read_buf(mtd, &byte, sizeof(byte));
+ return byte;
+}
+
+/**
+ * socrates_nand_read_word - read one word from the chip
+ * @mtd: MTD device structure
+ */
+static uint16_t socrates_nand_read_word(struct mtd_info *mtd)
+{
+ uint16_t word;
+ socrates_nand_read_buf(mtd, (uint8_t *)&word, sizeof(word));
+ return word;
+}
+
+/*
+ * Hardware specific access to control-lines
+ */
+static void socrates_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
+ unsigned int ctrl)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct socrates_nand_host *host = nand_get_controller_data(nand_chip);
+ uint32_t val;
+
+ if (cmd == NAND_CMD_NONE)
+ return;
+
+ if (ctrl & NAND_CLE)
+ val = FPGA_NAND_CMD_COMMAND;
+ else
+ val = FPGA_NAND_CMD_ADDR;
+
+ if (ctrl & NAND_NCE)
+ val |= FPGA_NAND_ENABLE;
+
+ val |= (cmd & 0xff) << FPGA_NAND_DATA_SHIFT;
+
+ out_be32(host->io_base, val);
+}
+
+/*
+ * Read the Device Ready pin.
+ */
+static int socrates_nand_device_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct socrates_nand_host *host = nand_get_controller_data(nand_chip);
+
+ if (in_be32(host->io_base) & FPGA_NAND_BUSY)
+ return 0; /* busy */
+ return 1;
+}
+
+/*
+ * Probe for the NAND device.
+ */
+static int socrates_nand_probe(struct platform_device *ofdev)
+{
+ struct socrates_nand_host *host;
+ struct mtd_info *mtd;
+ struct nand_chip *nand_chip;
+ int res;
+
+ /* Allocate memory for the device structure (and zero it) */
+ host = devm_kzalloc(&ofdev->dev, sizeof(*host), GFP_KERNEL);
+ if (!host)
+ return -ENOMEM;
+
+ host->io_base = of_iomap(ofdev->dev.of_node, 0);
+ if (host->io_base == NULL) {
+ dev_err(&ofdev->dev, "ioremap failed\n");
+ return -EIO;
+ }
+
+ nand_chip = &host->nand_chip;
+ mtd = nand_to_mtd(nand_chip);
+ host->dev = &ofdev->dev;
+
+ /* link the private data structures */
+ nand_set_controller_data(nand_chip, host);
+ nand_set_flash_node(nand_chip, ofdev->dev.of_node);
+ mtd->name = "socrates_nand";
+ mtd->dev.parent = &ofdev->dev;
+
+ /*should never be accessed directly */
+ nand_chip->IO_ADDR_R = (void *)0xdeadbeef;
+ nand_chip->IO_ADDR_W = (void *)0xdeadbeef;
+
+ nand_chip->cmd_ctrl = socrates_nand_cmd_ctrl;
+ nand_chip->read_byte = socrates_nand_read_byte;
+ nand_chip->read_word = socrates_nand_read_word;
+ nand_chip->write_buf = socrates_nand_write_buf;
+ nand_chip->read_buf = socrates_nand_read_buf;
+ nand_chip->dev_ready = socrates_nand_device_ready;
+
+ nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */
+ nand_chip->ecc.algo = NAND_ECC_HAMMING;
+
+ /* TODO: I have no idea what real delay is. */
+ nand_chip->chip_delay = 20; /* 20us command delay time */
+
+ dev_set_drvdata(&ofdev->dev, host);
+
+ res = nand_scan(mtd, 1);
+ if (res)
+ goto out;
+
+ res = mtd_device_register(mtd, NULL, 0);
+ if (!res)
+ return res;
+
+ nand_release(mtd);
+
+out:
+ iounmap(host->io_base);
+ return res;
+}
+
+/*
+ * Remove a NAND device.
+ */
+static int socrates_nand_remove(struct platform_device *ofdev)
+{
+ struct socrates_nand_host *host = dev_get_drvdata(&ofdev->dev);
+ struct mtd_info *mtd = nand_to_mtd(&host->nand_chip);
+
+ nand_release(mtd);
+
+ iounmap(host->io_base);
+
+ return 0;
+}
+
+static const struct of_device_id socrates_nand_match[] =
+{
+ {
+ .compatible = "abb,socrates-nand",
+ },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, socrates_nand_match);
+
+static struct platform_driver socrates_nand_driver = {
+ .driver = {
+ .name = "socrates_nand",
+ .of_match_table = socrates_nand_match,
+ },
+ .probe = socrates_nand_probe,
+ .remove = socrates_nand_remove,
+};
+
+module_platform_driver(socrates_nand_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ilya Yanok");
+MODULE_DESCRIPTION("NAND driver for Socrates board");
diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c
new file mode 100644
index 000000000000..aad42812a353
--- /dev/null
+++ b/drivers/mtd/nand/raw/sunxi_nand.c
@@ -0,0 +1,2206 @@
+/*
+ * Copyright (C) 2013 Boris BREZILLON <b.brezillon.dev@gmail.com>
+ *
+ * Derived from:
+ * https://github.com/yuq/sunxi-nfc-mtd
+ * Copyright (C) 2013 Qiang Yu <yuq825@gmail.com>
+ *
+ * https://github.com/hno/Allwinner-Info
+ * Copyright (C) 2013 Henrik Nordström <Henrik Nordström>
+ *
+ * Copyright (C) 2013 Dmitriy B. <rzk333@gmail.com>
+ * Copyright (C) 2013 Sergey Lapin <slapin@ossfans.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.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/reset.h>
+
+#define NFC_REG_CTL 0x0000
+#define NFC_REG_ST 0x0004
+#define NFC_REG_INT 0x0008
+#define NFC_REG_TIMING_CTL 0x000C
+#define NFC_REG_TIMING_CFG 0x0010
+#define NFC_REG_ADDR_LOW 0x0014
+#define NFC_REG_ADDR_HIGH 0x0018
+#define NFC_REG_SECTOR_NUM 0x001C
+#define NFC_REG_CNT 0x0020
+#define NFC_REG_CMD 0x0024
+#define NFC_REG_RCMD_SET 0x0028
+#define NFC_REG_WCMD_SET 0x002C
+#define NFC_REG_IO_DATA 0x0030
+#define NFC_REG_ECC_CTL 0x0034
+#define NFC_REG_ECC_ST 0x0038
+#define NFC_REG_DEBUG 0x003C
+#define NFC_REG_ECC_ERR_CNT(x) ((0x0040 + (x)) & ~0x3)
+#define NFC_REG_USER_DATA(x) (0x0050 + ((x) * 4))
+#define NFC_REG_SPARE_AREA 0x00A0
+#define NFC_REG_PAT_ID 0x00A4
+#define NFC_RAM0_BASE 0x0400
+#define NFC_RAM1_BASE 0x0800
+
+/* define bit use in NFC_CTL */
+#define NFC_EN BIT(0)
+#define NFC_RESET BIT(1)
+#define NFC_BUS_WIDTH_MSK BIT(2)
+#define NFC_BUS_WIDTH_8 (0 << 2)
+#define NFC_BUS_WIDTH_16 (1 << 2)
+#define NFC_RB_SEL_MSK BIT(3)
+#define NFC_RB_SEL(x) ((x) << 3)
+#define NFC_CE_SEL_MSK GENMASK(26, 24)
+#define NFC_CE_SEL(x) ((x) << 24)
+#define NFC_CE_CTL BIT(6)
+#define NFC_PAGE_SHIFT_MSK GENMASK(11, 8)
+#define NFC_PAGE_SHIFT(x) (((x) < 10 ? 0 : (x) - 10) << 8)
+#define NFC_SAM BIT(12)
+#define NFC_RAM_METHOD BIT(14)
+#define NFC_DEBUG_CTL BIT(31)
+
+/* define bit use in NFC_ST */
+#define NFC_RB_B2R BIT(0)
+#define NFC_CMD_INT_FLAG BIT(1)
+#define NFC_DMA_INT_FLAG BIT(2)
+#define NFC_CMD_FIFO_STATUS BIT(3)
+#define NFC_STA BIT(4)
+#define NFC_NATCH_INT_FLAG BIT(5)
+#define NFC_RB_STATE(x) BIT(x + 8)
+
+/* define bit use in NFC_INT */
+#define NFC_B2R_INT_ENABLE BIT(0)
+#define NFC_CMD_INT_ENABLE BIT(1)
+#define NFC_DMA_INT_ENABLE BIT(2)
+#define NFC_INT_MASK (NFC_B2R_INT_ENABLE | \
+ NFC_CMD_INT_ENABLE | \
+ NFC_DMA_INT_ENABLE)
+
+/* define bit use in NFC_TIMING_CTL */
+#define NFC_TIMING_CTL_EDO BIT(8)
+
+/* define NFC_TIMING_CFG register layout */
+#define NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD) \
+ (((tWB) & 0x3) | (((tADL) & 0x3) << 2) | \
+ (((tWHR) & 0x3) << 4) | (((tRHW) & 0x3) << 6) | \
+ (((tCAD) & 0x7) << 8))
+
+/* define bit use in NFC_CMD */
+#define NFC_CMD_LOW_BYTE_MSK GENMASK(7, 0)
+#define NFC_CMD_HIGH_BYTE_MSK GENMASK(15, 8)
+#define NFC_CMD(x) (x)
+#define NFC_ADR_NUM_MSK GENMASK(18, 16)
+#define NFC_ADR_NUM(x) (((x) - 1) << 16)
+#define NFC_SEND_ADR BIT(19)
+#define NFC_ACCESS_DIR BIT(20)
+#define NFC_DATA_TRANS BIT(21)
+#define NFC_SEND_CMD1 BIT(22)
+#define NFC_WAIT_FLAG BIT(23)
+#define NFC_SEND_CMD2 BIT(24)
+#define NFC_SEQ BIT(25)
+#define NFC_DATA_SWAP_METHOD BIT(26)
+#define NFC_ROW_AUTO_INC BIT(27)
+#define NFC_SEND_CMD3 BIT(28)
+#define NFC_SEND_CMD4 BIT(29)
+#define NFC_CMD_TYPE_MSK GENMASK(31, 30)
+#define NFC_NORMAL_OP (0 << 30)
+#define NFC_ECC_OP (1 << 30)
+#define NFC_PAGE_OP (2 << 30)
+
+/* define bit use in NFC_RCMD_SET */
+#define NFC_READ_CMD_MSK GENMASK(7, 0)
+#define NFC_RND_READ_CMD0_MSK GENMASK(15, 8)
+#define NFC_RND_READ_CMD1_MSK GENMASK(23, 16)
+
+/* define bit use in NFC_WCMD_SET */
+#define NFC_PROGRAM_CMD_MSK GENMASK(7, 0)
+#define NFC_RND_WRITE_CMD_MSK GENMASK(15, 8)
+#define NFC_READ_CMD0_MSK GENMASK(23, 16)
+#define NFC_READ_CMD1_MSK GENMASK(31, 24)
+
+/* define bit use in NFC_ECC_CTL */
+#define NFC_ECC_EN BIT(0)
+#define NFC_ECC_PIPELINE BIT(3)
+#define NFC_ECC_EXCEPTION BIT(4)
+#define NFC_ECC_BLOCK_SIZE_MSK BIT(5)
+#define NFC_ECC_BLOCK_512 BIT(5)
+#define NFC_RANDOM_EN BIT(9)
+#define NFC_RANDOM_DIRECTION BIT(10)
+#define NFC_ECC_MODE_MSK GENMASK(15, 12)
+#define NFC_ECC_MODE(x) ((x) << 12)
+#define NFC_RANDOM_SEED_MSK GENMASK(30, 16)
+#define NFC_RANDOM_SEED(x) ((x) << 16)
+
+/* define bit use in NFC_ECC_ST */
+#define NFC_ECC_ERR(x) BIT(x)
+#define NFC_ECC_ERR_MSK GENMASK(15, 0)
+#define NFC_ECC_PAT_FOUND(x) BIT(x + 16)
+#define NFC_ECC_ERR_CNT(b, x) (((x) >> (((b) % 4) * 8)) & 0xff)
+
+#define NFC_DEFAULT_TIMEOUT_MS 1000
+
+#define NFC_SRAM_SIZE 1024
+
+#define NFC_MAX_CS 7
+
+/*
+ * Ready/Busy detection type: describes the Ready/Busy detection modes
+ *
+ * @RB_NONE: no external detection available, rely on STATUS command
+ * and software timeouts
+ * @RB_NATIVE: use sunxi NAND controller Ready/Busy support. The Ready/Busy
+ * pin of the NAND flash chip must be connected to one of the
+ * native NAND R/B pins (those which can be muxed to the NAND
+ * Controller)
+ * @RB_GPIO: use a simple GPIO to handle Ready/Busy status. The Ready/Busy
+ * pin of the NAND flash chip must be connected to a GPIO capable
+ * pin.
+ */
+enum sunxi_nand_rb_type {
+ RB_NONE,
+ RB_NATIVE,
+ RB_GPIO,
+};
+
+/*
+ * Ready/Busy structure: stores information related to Ready/Busy detection
+ *
+ * @type: the Ready/Busy detection mode
+ * @info: information related to the R/B detection mode. Either a gpio
+ * id or a native R/B id (those supported by the NAND controller).
+ */
+struct sunxi_nand_rb {
+ enum sunxi_nand_rb_type type;
+ union {
+ int gpio;
+ int nativeid;
+ } info;
+};
+
+/*
+ * Chip Select structure: stores information related to NAND Chip Select
+ *
+ * @cs: the NAND CS id used to communicate with a NAND Chip
+ * @rb: the Ready/Busy description
+ */
+struct sunxi_nand_chip_sel {
+ u8 cs;
+ struct sunxi_nand_rb rb;
+};
+
+/*
+ * sunxi HW ECC infos: stores information related to HW ECC support
+ *
+ * @mode: the sunxi ECC mode field deduced from ECC requirements
+ */
+struct sunxi_nand_hw_ecc {
+ int mode;
+};
+
+/*
+ * NAND chip structure: stores NAND chip device related information
+ *
+ * @node: used to store NAND chips into a list
+ * @nand: base NAND chip structure
+ * @mtd: base MTD structure
+ * @clk_rate: clk_rate required for this NAND chip
+ * @timing_cfg TIMING_CFG register value for this NAND chip
+ * @selected: current active CS
+ * @nsels: number of CS lines required by the NAND chip
+ * @sels: array of CS lines descriptions
+ */
+struct sunxi_nand_chip {
+ struct list_head node;
+ struct nand_chip nand;
+ unsigned long clk_rate;
+ u32 timing_cfg;
+ u32 timing_ctl;
+ int selected;
+ int addr_cycles;
+ u32 addr[2];
+ int cmd_cycles;
+ u8 cmd[2];
+ int nsels;
+ struct sunxi_nand_chip_sel sels[0];
+};
+
+static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
+{
+ return container_of(nand, struct sunxi_nand_chip, nand);
+}
+
+/*
+ * NAND Controller structure: stores sunxi NAND controller information
+ *
+ * @controller: base controller structure
+ * @dev: parent device (used to print error messages)
+ * @regs: NAND controller registers
+ * @ahb_clk: NAND Controller AHB clock
+ * @mod_clk: NAND Controller mod clock
+ * @assigned_cs: bitmask describing already assigned CS lines
+ * @clk_rate: NAND controller current clock rate
+ * @chips: a list containing all the NAND chips attached to
+ * this NAND controller
+ * @complete: a completion object used to wait for NAND
+ * controller events
+ */
+struct sunxi_nfc {
+ struct nand_hw_control controller;
+ struct device *dev;
+ void __iomem *regs;
+ struct clk *ahb_clk;
+ struct clk *mod_clk;
+ struct reset_control *reset;
+ unsigned long assigned_cs;
+ unsigned long clk_rate;
+ struct list_head chips;
+ struct completion complete;
+ struct dma_chan *dmac;
+};
+
+static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_hw_control *ctrl)
+{
+ return container_of(ctrl, struct sunxi_nfc, controller);
+}
+
+static irqreturn_t sunxi_nfc_interrupt(int irq, void *dev_id)
+{
+ struct sunxi_nfc *nfc = dev_id;
+ u32 st = readl(nfc->regs + NFC_REG_ST);
+ u32 ien = readl(nfc->regs + NFC_REG_INT);
+
+ if (!(ien & st))
+ return IRQ_NONE;
+
+ if ((ien & st) == ien)
+ complete(&nfc->complete);
+
+ writel(st & NFC_INT_MASK, nfc->regs + NFC_REG_ST);
+ writel(~st & ien & NFC_INT_MASK, nfc->regs + NFC_REG_INT);
+
+ return IRQ_HANDLED;
+}
+
+static int sunxi_nfc_wait_events(struct sunxi_nfc *nfc, u32 events,
+ bool use_polling, unsigned int timeout_ms)
+{
+ int ret;
+
+ if (events & ~NFC_INT_MASK)
+ return -EINVAL;
+
+ if (!timeout_ms)
+ timeout_ms = NFC_DEFAULT_TIMEOUT_MS;
+
+ if (!use_polling) {
+ init_completion(&nfc->complete);
+
+ writel(events, nfc->regs + NFC_REG_INT);
+
+ ret = wait_for_completion_timeout(&nfc->complete,
+ msecs_to_jiffies(timeout_ms));
+ if (!ret)
+ ret = -ETIMEDOUT;
+ else
+ ret = 0;
+
+ writel(0, nfc->regs + NFC_REG_INT);
+ } else {
+ u32 status;
+
+ ret = readl_poll_timeout(nfc->regs + NFC_REG_ST, status,
+ (status & events) == events, 1,
+ timeout_ms * 1000);
+ }
+
+ writel(events & NFC_INT_MASK, nfc->regs + NFC_REG_ST);
+
+ if (ret)
+ dev_err(nfc->dev, "wait interrupt timedout\n");
+
+ return ret;
+}
+
+static int sunxi_nfc_wait_cmd_fifo_empty(struct sunxi_nfc *nfc)
+{
+ u32 status;
+ int ret;
+
+ ret = readl_poll_timeout(nfc->regs + NFC_REG_ST, status,
+ !(status & NFC_CMD_FIFO_STATUS), 1,
+ NFC_DEFAULT_TIMEOUT_MS * 1000);
+ if (ret)
+ dev_err(nfc->dev, "wait for empty cmd FIFO timedout\n");
+
+ return ret;
+}
+
+static int sunxi_nfc_rst(struct sunxi_nfc *nfc)
+{
+ u32 ctl;
+ int ret;
+
+ writel(0, nfc->regs + NFC_REG_ECC_CTL);
+ writel(NFC_RESET, nfc->regs + NFC_REG_CTL);
+
+ ret = readl_poll_timeout(nfc->regs + NFC_REG_CTL, ctl,
+ !(ctl & NFC_RESET), 1,
+ NFC_DEFAULT_TIMEOUT_MS * 1000);
+ if (ret)
+ dev_err(nfc->dev, "wait for NAND controller reset timedout\n");
+
+ return ret;
+}
+
+static int sunxi_nfc_dma_op_prepare(struct mtd_info *mtd, const void *buf,
+ int chunksize, int nchunks,
+ enum dma_data_direction ddir,
+ struct scatterlist *sg)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+ struct dma_async_tx_descriptor *dmad;
+ enum dma_transfer_direction tdir;
+ dma_cookie_t dmat;
+ int ret;
+
+ if (ddir == DMA_FROM_DEVICE)
+ tdir = DMA_DEV_TO_MEM;
+ else
+ tdir = DMA_MEM_TO_DEV;
+
+ sg_init_one(sg, buf, nchunks * chunksize);
+ ret = dma_map_sg(nfc->dev, sg, 1, ddir);
+ if (!ret)
+ return -ENOMEM;
+
+ dmad = dmaengine_prep_slave_sg(nfc->dmac, sg, 1, tdir, DMA_CTRL_ACK);
+ if (!dmad) {
+ ret = -EINVAL;
+ goto err_unmap_buf;
+ }
+
+ writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RAM_METHOD,
+ nfc->regs + NFC_REG_CTL);
+ writel(nchunks, nfc->regs + NFC_REG_SECTOR_NUM);
+ writel(chunksize, nfc->regs + NFC_REG_CNT);
+ dmat = dmaengine_submit(dmad);
+
+ ret = dma_submit_error(dmat);
+ if (ret)
+ goto err_clr_dma_flag;
+
+ return 0;
+
+err_clr_dma_flag:
+ writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD,
+ nfc->regs + NFC_REG_CTL);
+
+err_unmap_buf:
+ dma_unmap_sg(nfc->dev, sg, 1, ddir);
+ return ret;
+}
+
+static void sunxi_nfc_dma_op_cleanup(struct mtd_info *mtd,
+ enum dma_data_direction ddir,
+ struct scatterlist *sg)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+
+ dma_unmap_sg(nfc->dev, sg, 1, ddir);
+ writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD,
+ nfc->regs + NFC_REG_CTL);
+}
+
+static int sunxi_nfc_dev_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+ struct sunxi_nand_rb *rb;
+ int ret;
+
+ if (sunxi_nand->selected < 0)
+ return 0;
+
+ rb = &sunxi_nand->sels[sunxi_nand->selected].rb;
+
+ switch (rb->type) {
+ case RB_NATIVE:
+ ret = !!(readl(nfc->regs + NFC_REG_ST) &
+ NFC_RB_STATE(rb->info.nativeid));
+ break;
+ case RB_GPIO:
+ ret = gpio_get_value(rb->info.gpio);
+ break;
+ case RB_NONE:
+ default:
+ ret = 0;
+ dev_err(nfc->dev, "cannot check R/B NAND status!\n");
+ break;
+ }
+
+ return ret;
+}
+
+static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+ struct sunxi_nand_chip_sel *sel;
+ u32 ctl;
+
+ if (chip > 0 && chip >= sunxi_nand->nsels)
+ return;
+
+ if (chip == sunxi_nand->selected)
+ return;
+
+ ctl = readl(nfc->regs + NFC_REG_CTL) &
+ ~(NFC_PAGE_SHIFT_MSK | NFC_CE_SEL_MSK | NFC_RB_SEL_MSK | NFC_EN);
+
+ if (chip >= 0) {
+ sel = &sunxi_nand->sels[chip];
+
+ ctl |= NFC_CE_SEL(sel->cs) | NFC_EN |
+ NFC_PAGE_SHIFT(nand->page_shift);
+ if (sel->rb.type == RB_NONE) {
+ nand->dev_ready = NULL;
+ } else {
+ nand->dev_ready = sunxi_nfc_dev_ready;
+ if (sel->rb.type == RB_NATIVE)
+ ctl |= NFC_RB_SEL(sel->rb.info.nativeid);
+ }
+
+ writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA);
+
+ if (nfc->clk_rate != sunxi_nand->clk_rate) {
+ clk_set_rate(nfc->mod_clk, sunxi_nand->clk_rate);
+ nfc->clk_rate = sunxi_nand->clk_rate;
+ }
+ }
+
+ writel(sunxi_nand->timing_ctl, nfc->regs + NFC_REG_TIMING_CTL);
+ writel(sunxi_nand->timing_cfg, nfc->regs + NFC_REG_TIMING_CFG);
+ writel(ctl, nfc->regs + NFC_REG_CTL);
+
+ sunxi_nand->selected = chip;
+}
+
+static void sunxi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+ int ret;
+ int cnt;
+ int offs = 0;
+ u32 tmp;
+
+ while (len > offs) {
+ bool poll = false;
+
+ cnt = min(len - offs, NFC_SRAM_SIZE);
+
+ ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
+ if (ret)
+ break;
+
+ writel(cnt, nfc->regs + NFC_REG_CNT);
+ tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD;
+ writel(tmp, nfc->regs + NFC_REG_CMD);
+
+ /* Arbitrary limit for polling mode */
+ if (cnt < 64)
+ poll = true;
+
+ ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, poll, 0);
+ if (ret)
+ break;
+
+ if (buf)
+ memcpy_fromio(buf + offs, nfc->regs + NFC_RAM0_BASE,
+ cnt);
+ offs += cnt;
+ }
+}
+
+static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
+ int len)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+ int ret;
+ int cnt;
+ int offs = 0;
+ u32 tmp;
+
+ while (len > offs) {
+ bool poll = false;
+
+ cnt = min(len - offs, NFC_SRAM_SIZE);
+
+ ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
+ if (ret)
+ break;
+
+ writel(cnt, nfc->regs + NFC_REG_CNT);
+ memcpy_toio(nfc->regs + NFC_RAM0_BASE, buf + offs, cnt);
+ tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
+ NFC_ACCESS_DIR;
+ writel(tmp, nfc->regs + NFC_REG_CMD);
+
+ /* Arbitrary limit for polling mode */
+ if (cnt < 64)
+ poll = true;
+
+ ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, poll, 0);
+ if (ret)
+ break;
+
+ offs += cnt;
+ }
+}
+
+static uint8_t sunxi_nfc_read_byte(struct mtd_info *mtd)
+{
+ uint8_t ret;
+
+ sunxi_nfc_read_buf(mtd, &ret, 1);
+
+ return ret;
+}
+
+static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat,
+ unsigned int ctrl)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+ int ret;
+
+ if (dat == NAND_CMD_NONE && (ctrl & NAND_NCE) &&
+ !(ctrl & (NAND_CLE | NAND_ALE))) {
+ u32 cmd = 0;
+
+ if (!sunxi_nand->addr_cycles && !sunxi_nand->cmd_cycles)
+ return;
+
+ if (sunxi_nand->cmd_cycles--)
+ cmd |= NFC_SEND_CMD1 | sunxi_nand->cmd[0];
+
+ if (sunxi_nand->cmd_cycles--) {
+ cmd |= NFC_SEND_CMD2;
+ writel(sunxi_nand->cmd[1],
+ nfc->regs + NFC_REG_RCMD_SET);
+ }
+
+ sunxi_nand->cmd_cycles = 0;
+
+ if (sunxi_nand->addr_cycles) {
+ cmd |= NFC_SEND_ADR |
+ NFC_ADR_NUM(sunxi_nand->addr_cycles);
+ writel(sunxi_nand->addr[0],
+ nfc->regs + NFC_REG_ADDR_LOW);
+ }
+
+ if (sunxi_nand->addr_cycles > 4)
+ writel(sunxi_nand->addr[1],
+ nfc->regs + NFC_REG_ADDR_HIGH);
+
+ ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
+ if (ret)
+ return;
+
+ writel(cmd, nfc->regs + NFC_REG_CMD);
+ sunxi_nand->addr[0] = 0;
+ sunxi_nand->addr[1] = 0;
+ sunxi_nand->addr_cycles = 0;
+ sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0);
+ }
+
+ if (ctrl & NAND_CLE) {
+ sunxi_nand->cmd[sunxi_nand->cmd_cycles++] = dat;
+ } else if (ctrl & NAND_ALE) {
+ sunxi_nand->addr[sunxi_nand->addr_cycles / 4] |=
+ dat << ((sunxi_nand->addr_cycles % 4) * 8);
+ sunxi_nand->addr_cycles++;
+ }
+}
+
+/* These seed values have been extracted from Allwinner's BSP */
+static const u16 sunxi_nfc_randomizer_page_seeds[] = {
+ 0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72,
+ 0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436,
+ 0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d,
+ 0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130,
+ 0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56,
+ 0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55,
+ 0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb,
+ 0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17,
+ 0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62,
+ 0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064,
+ 0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126,
+ 0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e,
+ 0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3,
+ 0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b,
+ 0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d,
+ 0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db,
+};
+
+/*
+ * sunxi_nfc_randomizer_ecc512_seeds and sunxi_nfc_randomizer_ecc1024_seeds
+ * have been generated using
+ * sunxi_nfc_randomizer_step(seed, (step_size * 8) + 15), which is what
+ * the randomizer engine does internally before de/scrambling OOB data.
+ *
+ * Those tables are statically defined to avoid calculating randomizer state
+ * at runtime.
+ */
+static const u16 sunxi_nfc_randomizer_ecc512_seeds[] = {
+ 0x3346, 0x367f, 0x1f18, 0x769a, 0x4f64, 0x068c, 0x2ef1, 0x6b64,
+ 0x28a9, 0x15d7, 0x30f8, 0x3659, 0x53db, 0x7c5f, 0x71d4, 0x4409,
+ 0x26eb, 0x03cc, 0x655d, 0x47d4, 0x4daa, 0x0877, 0x712d, 0x3617,
+ 0x3264, 0x49aa, 0x7f9e, 0x588e, 0x4fbc, 0x7176, 0x7f91, 0x6c6d,
+ 0x4b95, 0x5fb7, 0x3844, 0x4037, 0x0184, 0x081b, 0x0ee8, 0x5b91,
+ 0x293d, 0x1f71, 0x0e6f, 0x402b, 0x5122, 0x1e52, 0x22be, 0x3d2d,
+ 0x75bc, 0x7c60, 0x6291, 0x1a2f, 0x61d4, 0x74aa, 0x4140, 0x29ab,
+ 0x472d, 0x2852, 0x017e, 0x15e8, 0x5ec2, 0x17cf, 0x7d0f, 0x06b8,
+ 0x117a, 0x6b94, 0x789b, 0x3126, 0x6ac5, 0x5be7, 0x150f, 0x51f8,
+ 0x7889, 0x0aa5, 0x663d, 0x77e8, 0x0b87, 0x3dcb, 0x360d, 0x218b,
+ 0x512f, 0x7dc9, 0x6a4d, 0x630a, 0x3547, 0x1dd2, 0x5aea, 0x69a5,
+ 0x7bfa, 0x5e4f, 0x1519, 0x6430, 0x3a0e, 0x5eb3, 0x5425, 0x0c7a,
+ 0x5540, 0x3670, 0x63c1, 0x31e9, 0x5a39, 0x2de7, 0x5979, 0x2891,
+ 0x1562, 0x014b, 0x5b05, 0x2756, 0x5a34, 0x13aa, 0x6cb5, 0x2c36,
+ 0x5e72, 0x1306, 0x0861, 0x15ef, 0x1ee8, 0x5a37, 0x7ac4, 0x45dd,
+ 0x44c4, 0x7266, 0x2f41, 0x3ccc, 0x045e, 0x7d40, 0x7c66, 0x0fa0,
+};
+
+static const u16 sunxi_nfc_randomizer_ecc1024_seeds[] = {
+ 0x2cf5, 0x35f1, 0x63a4, 0x5274, 0x2bd2, 0x778b, 0x7285, 0x32b6,
+ 0x6a5c, 0x70d6, 0x757d, 0x6769, 0x5375, 0x1e81, 0x0cf3, 0x3982,
+ 0x6787, 0x042a, 0x6c49, 0x1925, 0x56a8, 0x40a9, 0x063e, 0x7bd9,
+ 0x4dbf, 0x55ec, 0x672e, 0x7334, 0x5185, 0x4d00, 0x232a, 0x7e07,
+ 0x445d, 0x6b92, 0x528f, 0x4255, 0x53ba, 0x7d82, 0x2a2e, 0x3a4e,
+ 0x75eb, 0x450c, 0x6844, 0x1b5d, 0x581a, 0x4cc6, 0x0379, 0x37b2,
+ 0x419f, 0x0e92, 0x6b27, 0x5624, 0x01e3, 0x07c1, 0x44a5, 0x130c,
+ 0x13e8, 0x5910, 0x0876, 0x60c5, 0x54e3, 0x5b7f, 0x2269, 0x509f,
+ 0x7665, 0x36fd, 0x3e9a, 0x0579, 0x6295, 0x14ef, 0x0a81, 0x1bcc,
+ 0x4b16, 0x64db, 0x0514, 0x4f07, 0x0591, 0x3576, 0x6853, 0x0d9e,
+ 0x259f, 0x38b7, 0x64fb, 0x3094, 0x4693, 0x6ddd, 0x29bb, 0x0bc8,
+ 0x3f47, 0x490e, 0x0c0e, 0x7933, 0x3c9e, 0x5840, 0x398d, 0x3e68,
+ 0x4af1, 0x71f5, 0x57cf, 0x1121, 0x64eb, 0x3579, 0x15ac, 0x584d,
+ 0x5f2a, 0x47e2, 0x6528, 0x6eac, 0x196e, 0x6b96, 0x0450, 0x0179,
+ 0x609c, 0x06e1, 0x4626, 0x42c7, 0x273e, 0x486f, 0x0705, 0x1601,
+ 0x145b, 0x407e, 0x062b, 0x57a5, 0x53f9, 0x5659, 0x4410, 0x3ccd,
+};
+
+static u16 sunxi_nfc_randomizer_step(u16 state, int count)
+{
+ state &= 0x7fff;
+
+ /*
+ * This loop is just a simple implementation of a Fibonacci LFSR using
+ * the x16 + x15 + 1 polynomial.
+ */
+ while (count--)
+ state = ((state >> 1) |
+ (((state ^ (state >> 1)) & 1) << 14)) & 0x7fff;
+
+ return state;
+}
+
+static u16 sunxi_nfc_randomizer_state(struct mtd_info *mtd, int page, bool ecc)
+{
+ const u16 *seeds = sunxi_nfc_randomizer_page_seeds;
+ int mod = mtd_div_by_ws(mtd->erasesize, mtd);
+
+ if (mod > ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds))
+ mod = ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds);
+
+ if (ecc) {
+ if (mtd->ecc_step_size == 512)
+ seeds = sunxi_nfc_randomizer_ecc512_seeds;
+ else
+ seeds = sunxi_nfc_randomizer_ecc1024_seeds;
+ }
+
+ return seeds[page % mod];
+}
+
+static void sunxi_nfc_randomizer_config(struct mtd_info *mtd,
+ int page, bool ecc)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+ u32 ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
+ u16 state;
+
+ if (!(nand->options & NAND_NEED_SCRAMBLING))
+ return;
+
+ ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
+ state = sunxi_nfc_randomizer_state(mtd, page, ecc);
+ ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_SEED_MSK;
+ writel(ecc_ctl | NFC_RANDOM_SEED(state), nfc->regs + NFC_REG_ECC_CTL);
+}
+
+static void sunxi_nfc_randomizer_enable(struct mtd_info *mtd)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+
+ if (!(nand->options & NAND_NEED_SCRAMBLING))
+ return;
+
+ writel(readl(nfc->regs + NFC_REG_ECC_CTL) | NFC_RANDOM_EN,
+ nfc->regs + NFC_REG_ECC_CTL);
+}
+
+static void sunxi_nfc_randomizer_disable(struct mtd_info *mtd)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+
+ if (!(nand->options & NAND_NEED_SCRAMBLING))
+ return;
+
+ writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN,
+ nfc->regs + NFC_REG_ECC_CTL);
+}
+
+static void sunxi_nfc_randomize_bbm(struct mtd_info *mtd, int page, u8 *bbm)
+{
+ u16 state = sunxi_nfc_randomizer_state(mtd, page, true);
+
+ bbm[0] ^= state;
+ bbm[1] ^= sunxi_nfc_randomizer_step(state, 8);
+}
+
+static void sunxi_nfc_randomizer_write_buf(struct mtd_info *mtd,
+ const uint8_t *buf, int len,
+ bool ecc, int page)
+{
+ sunxi_nfc_randomizer_config(mtd, page, ecc);
+ sunxi_nfc_randomizer_enable(mtd);
+ sunxi_nfc_write_buf(mtd, buf, len);
+ sunxi_nfc_randomizer_disable(mtd);
+}
+
+static void sunxi_nfc_randomizer_read_buf(struct mtd_info *mtd, uint8_t *buf,
+ int len, bool ecc, int page)
+{
+ sunxi_nfc_randomizer_config(mtd, page, ecc);
+ sunxi_nfc_randomizer_enable(mtd);
+ sunxi_nfc_read_buf(mtd, buf, len);
+ sunxi_nfc_randomizer_disable(mtd);
+}
+
+static void sunxi_nfc_hw_ecc_enable(struct mtd_info *mtd)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+ struct sunxi_nand_hw_ecc *data = nand->ecc.priv;
+ u32 ecc_ctl;
+
+ ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
+ ecc_ctl &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE |
+ NFC_ECC_BLOCK_SIZE_MSK);
+ ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION |
+ NFC_ECC_PIPELINE;
+
+ if (nand->ecc.size == 512)
+ ecc_ctl |= NFC_ECC_BLOCK_512;
+
+ writel(ecc_ctl, nfc->regs + NFC_REG_ECC_CTL);
+}
+
+static void sunxi_nfc_hw_ecc_disable(struct mtd_info *mtd)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+
+ writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_ECC_EN,
+ nfc->regs + NFC_REG_ECC_CTL);
+}
+
+static inline void sunxi_nfc_user_data_to_buf(u32 user_data, u8 *buf)
+{
+ buf[0] = user_data;
+ buf[1] = user_data >> 8;
+ buf[2] = user_data >> 16;
+ buf[3] = user_data >> 24;
+}
+
+static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf)
+{
+ return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+}
+
+static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct mtd_info *mtd, u8 *oob,
+ int step, bool bbm, int page)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+
+ sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(step)),
+ oob);
+
+ /* De-randomize the Bad Block Marker. */
+ if (bbm && (nand->options & NAND_NEED_SCRAMBLING))
+ sunxi_nfc_randomize_bbm(mtd, page, oob);
+}
+
+static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct mtd_info *mtd,
+ const u8 *oob, int step,
+ bool bbm, int page)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+ u8 user_data[4];
+
+ /* Randomize the Bad Block Marker. */
+ if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) {
+ memcpy(user_data, oob, sizeof(user_data));
+ sunxi_nfc_randomize_bbm(mtd, page, user_data);
+ oob = user_data;
+ }
+
+ writel(sunxi_nfc_buf_to_user_data(oob),
+ nfc->regs + NFC_REG_USER_DATA(step));
+}
+
+static void sunxi_nfc_hw_ecc_update_stats(struct mtd_info *mtd,
+ unsigned int *max_bitflips, int ret)
+{
+ if (ret < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += ret;
+ *max_bitflips = max_t(unsigned int, *max_bitflips, ret);
+ }
+}
+
+static int sunxi_nfc_hw_ecc_correct(struct mtd_info *mtd, u8 *data, u8 *oob,
+ int step, u32 status, bool *erased)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+ struct nand_ecc_ctrl *ecc = &nand->ecc;
+ u32 tmp;
+
+ *erased = false;
+
+ if (status & NFC_ECC_ERR(step))
+ return -EBADMSG;
+
+ if (status & NFC_ECC_PAT_FOUND(step)) {
+ u8 pattern;
+
+ if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1))) {
+ pattern = 0x0;
+ } else {
+ pattern = 0xff;
+ *erased = true;
+ }
+
+ if (data)
+ memset(data, pattern, ecc->size);
+
+ if (oob)
+ memset(oob, pattern, ecc->bytes + 4);
+
+ return 0;
+ }
+
+ tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(step));
+
+ return NFC_ECC_ERR_CNT(step, tmp);
+}
+
+static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
+ u8 *data, int data_off,
+ u8 *oob, int oob_off,
+ int *cur_off,
+ unsigned int *max_bitflips,
+ bool bbm, bool oob_required, int page)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+ struct nand_ecc_ctrl *ecc = &nand->ecc;
+ int raw_mode = 0;
+ bool erased;
+ int ret;
+
+ if (*cur_off != data_off)
+ nand_change_read_column_op(nand, data_off, NULL, 0, false);
+
+ sunxi_nfc_randomizer_read_buf(mtd, NULL, ecc->size, false, page);
+
+ if (data_off + ecc->size != oob_off)
+ nand_change_read_column_op(nand, oob_off, NULL, 0, false);
+
+ ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
+ if (ret)
+ return ret;
+
+ sunxi_nfc_randomizer_enable(mtd);
+ writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP,
+ nfc->regs + NFC_REG_CMD);
+
+ ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0);
+ sunxi_nfc_randomizer_disable(mtd);
+ if (ret)
+ return ret;
+
+ *cur_off = oob_off + ecc->bytes + 4;
+
+ ret = sunxi_nfc_hw_ecc_correct(mtd, data, oob_required ? oob : NULL, 0,
+ readl(nfc->regs + NFC_REG_ECC_ST),
+ &erased);
+ if (erased)
+ return 1;
+
+ if (ret < 0) {
+ /*
+ * Re-read the data with the randomizer disabled to identify
+ * bitflips in erased pages.
+ */
+ if (nand->options & NAND_NEED_SCRAMBLING)
+ nand_change_read_column_op(nand, data_off, data,
+ ecc->size, false);
+ else
+ memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE,
+ ecc->size);
+
+ nand_change_read_column_op(nand, oob_off, oob, ecc->bytes + 4,
+ false);
+
+ ret = nand_check_erased_ecc_chunk(data, ecc->size,
+ oob, ecc->bytes + 4,
+ NULL, 0, ecc->strength);
+ if (ret >= 0)
+ raw_mode = 1;
+ } else {
+ memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size);
+
+ if (oob_required) {
+ nand_change_read_column_op(nand, oob_off, NULL, 0,
+ false);
+ sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + 4,
+ true, page);
+
+ sunxi_nfc_hw_ecc_get_prot_oob_bytes(mtd, oob, 0,
+ bbm, page);
+ }
+ }
+
+ sunxi_nfc_hw_ecc_update_stats(mtd, max_bitflips, ret);
+
+ return raw_mode;
+}
+
+static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd,
+ u8 *oob, int *cur_off,
+ bool randomize, int page)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &nand->ecc;
+ int offset = ((ecc->bytes + 4) * ecc->steps);
+ int len = mtd->oobsize - offset;
+
+ if (len <= 0)
+ return;
+
+ if (!cur_off || *cur_off != offset)
+ nand_change_read_column_op(nand, mtd->writesize, NULL, 0,
+ false);
+
+ if (!randomize)
+ sunxi_nfc_read_buf(mtd, oob + offset, len);
+ else
+ sunxi_nfc_randomizer_read_buf(mtd, oob + offset, len,
+ false, page);
+
+ if (cur_off)
+ *cur_off = mtd->oobsize + mtd->writesize;
+}
+
+static int sunxi_nfc_hw_ecc_read_chunks_dma(struct mtd_info *mtd, uint8_t *buf,
+ int oob_required, int page,
+ int nchunks)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ bool randomized = nand->options & NAND_NEED_SCRAMBLING;
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+ struct nand_ecc_ctrl *ecc = &nand->ecc;
+ unsigned int max_bitflips = 0;
+ int ret, i, raw_mode = 0;
+ struct scatterlist sg;
+ u32 status;
+
+ ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
+ if (ret)
+ return ret;
+
+ ret = sunxi_nfc_dma_op_prepare(mtd, buf, ecc->size, nchunks,
+ DMA_FROM_DEVICE, &sg);
+ if (ret)
+ return ret;
+
+ sunxi_nfc_hw_ecc_enable(mtd);
+ sunxi_nfc_randomizer_config(mtd, page, false);
+ sunxi_nfc_randomizer_enable(mtd);
+
+ writel((NAND_CMD_RNDOUTSTART << 16) | (NAND_CMD_RNDOUT << 8) |
+ NAND_CMD_READSTART, nfc->regs + NFC_REG_RCMD_SET);
+
+ dma_async_issue_pending(nfc->dmac);
+
+ writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD | NFC_DATA_TRANS,
+ nfc->regs + NFC_REG_CMD);
+
+ ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0);
+ if (ret)
+ dmaengine_terminate_all(nfc->dmac);
+
+ sunxi_nfc_randomizer_disable(mtd);
+ sunxi_nfc_hw_ecc_disable(mtd);
+
+ sunxi_nfc_dma_op_cleanup(mtd, DMA_FROM_DEVICE, &sg);
+
+ if (ret)
+ return ret;
+
+ status = readl(nfc->regs + NFC_REG_ECC_ST);
+
+ for (i = 0; i < nchunks; i++) {
+ int data_off = i * ecc->size;
+ int oob_off = i * (ecc->bytes + 4);
+ u8 *data = buf + data_off;
+ u8 *oob = nand->oob_poi + oob_off;
+ bool erased;
+
+ ret = sunxi_nfc_hw_ecc_correct(mtd, randomized ? data : NULL,
+ oob_required ? oob : NULL,
+ i, status, &erased);
+
+ /* ECC errors are handled in the second loop. */
+ if (ret < 0)
+ continue;
+
+ if (oob_required && !erased) {
+ /* TODO: use DMA to retrieve OOB */
+ nand_change_read_column_op(nand,
+ mtd->writesize + oob_off,
+ oob, ecc->bytes + 4, false);
+
+ sunxi_nfc_hw_ecc_get_prot_oob_bytes(mtd, oob, i,
+ !i, page);
+ }
+
+ if (erased)
+ raw_mode = 1;
+
+ sunxi_nfc_hw_ecc_update_stats(mtd, &max_bitflips, ret);
+ }
+
+ if (status & NFC_ECC_ERR_MSK) {
+ for (i = 0; i < nchunks; i++) {
+ int data_off = i * ecc->size;
+ int oob_off = i * (ecc->bytes + 4);
+ u8 *data = buf + data_off;
+ u8 *oob = nand->oob_poi + oob_off;
+
+ if (!(status & NFC_ECC_ERR(i)))
+ continue;
+
+ /*
+ * Re-read the data with the randomizer disabled to
+ * identify bitflips in erased pages.
+ * TODO: use DMA to read page in raw mode
+ */
+ if (randomized)
+ nand_change_read_column_op(nand, data_off,
+ data, ecc->size,
+ false);
+
+ /* TODO: use DMA to retrieve OOB */
+ nand_change_read_column_op(nand,
+ mtd->writesize + oob_off,
+ oob, ecc->bytes + 4, false);
+
+ ret = nand_check_erased_ecc_chunk(data, ecc->size,
+ oob, ecc->bytes + 4,
+ NULL, 0,
+ ecc->strength);
+ if (ret >= 0)
+ raw_mode = 1;
+
+ sunxi_nfc_hw_ecc_update_stats(mtd, &max_bitflips, ret);
+ }
+ }
+
+ if (oob_required)
+ sunxi_nfc_hw_ecc_read_extra_oob(mtd, nand->oob_poi,
+ NULL, !raw_mode,
+ page);
+
+ return max_bitflips;
+}
+
+static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
+ const u8 *data, int data_off,
+ const u8 *oob, int oob_off,
+ int *cur_off, bool bbm,
+ int page)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+ struct nand_ecc_ctrl *ecc = &nand->ecc;
+ int ret;
+
+ if (data_off != *cur_off)
+ nand_change_write_column_op(nand, data_off, NULL, 0, false);
+
+ sunxi_nfc_randomizer_write_buf(mtd, data, ecc->size, false, page);
+
+ if (data_off + ecc->size != oob_off)
+ nand_change_write_column_op(nand, oob_off, NULL, 0, false);
+
+ ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
+ if (ret)
+ return ret;
+
+ sunxi_nfc_randomizer_enable(mtd);
+ sunxi_nfc_hw_ecc_set_prot_oob_bytes(mtd, oob, 0, bbm, page);
+
+ writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
+ NFC_ACCESS_DIR | NFC_ECC_OP,
+ nfc->regs + NFC_REG_CMD);
+
+ ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0);
+ sunxi_nfc_randomizer_disable(mtd);
+ if (ret)
+ return ret;
+
+ *cur_off = oob_off + ecc->bytes + 4;
+
+ return 0;
+}
+
+static void sunxi_nfc_hw_ecc_write_extra_oob(struct mtd_info *mtd,
+ u8 *oob, int *cur_off,
+ int page)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &nand->ecc;
+ int offset = ((ecc->bytes + 4) * ecc->steps);
+ int len = mtd->oobsize - offset;
+
+ if (len <= 0)
+ return;
+
+ if (!cur_off || *cur_off != offset)
+ nand_change_write_column_op(nand, offset + mtd->writesize,
+ NULL, 0, false);
+
+ sunxi_nfc_randomizer_write_buf(mtd, oob + offset, len, false, page);
+
+ if (cur_off)
+ *cur_off = mtd->oobsize + mtd->writesize;
+}
+
+static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf,
+ int oob_required, int page)
+{
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ unsigned int max_bitflips = 0;
+ int ret, i, cur_off = 0;
+ bool raw_mode = false;
+
+ nand_read_page_op(chip, page, 0, NULL, 0);
+
+ sunxi_nfc_hw_ecc_enable(mtd);
+
+ for (i = 0; i < ecc->steps; i++) {
+ int data_off = i * ecc->size;
+ int oob_off = i * (ecc->bytes + 4);
+ u8 *data = buf + data_off;
+ u8 *oob = chip->oob_poi + oob_off;
+
+ ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob,
+ oob_off + mtd->writesize,
+ &cur_off, &max_bitflips,
+ !i, oob_required, page);
+ if (ret < 0)
+ return ret;
+ else if (ret)
+ raw_mode = true;
+ }
+
+ if (oob_required)
+ sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off,
+ !raw_mode, page);
+
+ sunxi_nfc_hw_ecc_disable(mtd);
+
+ return max_bitflips;
+}
+
+static int sunxi_nfc_hw_ecc_read_page_dma(struct mtd_info *mtd,
+ struct nand_chip *chip, u8 *buf,
+ int oob_required, int page)
+{
+ int ret;
+
+ nand_read_page_op(chip, page, 0, NULL, 0);
+
+ ret = sunxi_nfc_hw_ecc_read_chunks_dma(mtd, buf, oob_required, page,
+ chip->ecc.steps);
+ if (ret >= 0)
+ return ret;
+
+ /* Fallback to PIO mode */
+ return sunxi_nfc_hw_ecc_read_page(mtd, chip, buf, oob_required, page);
+}
+
+static int sunxi_nfc_hw_ecc_read_subpage(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ u32 data_offs, u32 readlen,
+ u8 *bufpoi, int page)
+{
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ int ret, i, cur_off = 0;
+ unsigned int max_bitflips = 0;
+
+ nand_read_page_op(chip, page, 0, NULL, 0);
+
+ sunxi_nfc_hw_ecc_enable(mtd);
+
+ for (i = data_offs / ecc->size;
+ i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) {
+ int data_off = i * ecc->size;
+ int oob_off = i * (ecc->bytes + 4);
+ u8 *data = bufpoi + data_off;
+ u8 *oob = chip->oob_poi + oob_off;
+
+ ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off,
+ oob,
+ oob_off + mtd->writesize,
+ &cur_off, &max_bitflips, !i,
+ false, page);
+ if (ret < 0)
+ return ret;
+ }
+
+ sunxi_nfc_hw_ecc_disable(mtd);
+
+ return max_bitflips;
+}
+
+static int sunxi_nfc_hw_ecc_read_subpage_dma(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ u32 data_offs, u32 readlen,
+ u8 *buf, int page)
+{
+ int nchunks = DIV_ROUND_UP(data_offs + readlen, chip->ecc.size);
+ int ret;
+
+ nand_read_page_op(chip, page, 0, NULL, 0);
+
+ ret = sunxi_nfc_hw_ecc_read_chunks_dma(mtd, buf, false, page, nchunks);
+ if (ret >= 0)
+ return ret;
+
+ /* Fallback to PIO mode */
+ return sunxi_nfc_hw_ecc_read_subpage(mtd, chip, data_offs, readlen,
+ buf, page);
+}
+
+static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ const uint8_t *buf, int oob_required,
+ int page)
+{
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ int ret, i, cur_off = 0;
+
+ nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+
+ sunxi_nfc_hw_ecc_enable(mtd);
+
+ for (i = 0; i < ecc->steps; i++) {
+ int data_off = i * ecc->size;
+ int oob_off = i * (ecc->bytes + 4);
+ const u8 *data = buf + data_off;
+ const u8 *oob = chip->oob_poi + oob_off;
+
+ ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob,
+ oob_off + mtd->writesize,
+ &cur_off, !i, page);
+ if (ret)
+ return ret;
+ }
+
+ if (oob_required || (chip->options & NAND_NEED_SCRAMBLING))
+ sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi,
+ &cur_off, page);
+
+ sunxi_nfc_hw_ecc_disable(mtd);
+
+ return nand_prog_page_end_op(chip);
+}
+
+static int sunxi_nfc_hw_ecc_write_subpage(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ u32 data_offs, u32 data_len,
+ const u8 *buf, int oob_required,
+ int page)
+{
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ int ret, i, cur_off = 0;
+
+ nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+
+ sunxi_nfc_hw_ecc_enable(mtd);
+
+ for (i = data_offs / ecc->size;
+ i < DIV_ROUND_UP(data_offs + data_len, ecc->size); i++) {
+ int data_off = i * ecc->size;
+ int oob_off = i * (ecc->bytes + 4);
+ const u8 *data = buf + data_off;
+ const u8 *oob = chip->oob_poi + oob_off;
+
+ ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob,
+ oob_off + mtd->writesize,
+ &cur_off, !i, page);
+ if (ret)
+ return ret;
+ }
+
+ sunxi_nfc_hw_ecc_disable(mtd);
+
+ return nand_prog_page_end_op(chip);
+}
+
+static int sunxi_nfc_hw_ecc_write_page_dma(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ const u8 *buf,
+ int oob_required,
+ int page)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+ struct nand_ecc_ctrl *ecc = &nand->ecc;
+ struct scatterlist sg;
+ int ret, i;
+
+ ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
+ if (ret)
+ return ret;
+
+ ret = sunxi_nfc_dma_op_prepare(mtd, buf, ecc->size, ecc->steps,
+ DMA_TO_DEVICE, &sg);
+ if (ret)
+ goto pio_fallback;
+
+ for (i = 0; i < ecc->steps; i++) {
+ const u8 *oob = nand->oob_poi + (i * (ecc->bytes + 4));
+
+ sunxi_nfc_hw_ecc_set_prot_oob_bytes(mtd, oob, i, !i, page);
+ }
+
+ nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+
+ sunxi_nfc_hw_ecc_enable(mtd);
+ sunxi_nfc_randomizer_config(mtd, page, false);
+ sunxi_nfc_randomizer_enable(mtd);
+
+ writel((NAND_CMD_RNDIN << 8) | NAND_CMD_PAGEPROG,
+ nfc->regs + NFC_REG_RCMD_SET);
+
+ dma_async_issue_pending(nfc->dmac);
+
+ writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD |
+ NFC_DATA_TRANS | NFC_ACCESS_DIR,
+ nfc->regs + NFC_REG_CMD);
+
+ ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0);
+ if (ret)
+ dmaengine_terminate_all(nfc->dmac);
+
+ sunxi_nfc_randomizer_disable(mtd);
+ sunxi_nfc_hw_ecc_disable(mtd);
+
+ sunxi_nfc_dma_op_cleanup(mtd, DMA_TO_DEVICE, &sg);
+
+ if (ret)
+ return ret;
+
+ if (oob_required || (chip->options & NAND_NEED_SCRAMBLING))
+ /* TODO: use DMA to transfer extra OOB bytes ? */
+ sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi,
+ NULL, page);
+
+ return nand_prog_page_end_op(chip);
+
+pio_fallback:
+ return sunxi_nfc_hw_ecc_write_page(mtd, chip, buf, oob_required, page);
+}
+
+static int sunxi_nfc_hw_ecc_read_oob(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ int page)
+{
+ chip->pagebuf = -1;
+
+ return chip->ecc.read_page(mtd, chip, chip->data_buf, 1, page);
+}
+
+static int sunxi_nfc_hw_ecc_write_oob(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ int page)
+{
+ int ret;
+
+ chip->pagebuf = -1;
+
+ memset(chip->data_buf, 0xff, mtd->writesize);
+ ret = chip->ecc.write_page(mtd, chip, chip->data_buf, 1, page);
+ if (ret)
+ return ret;
+
+ /* Send command to program the OOB data */
+ return nand_prog_page_end_op(chip);
+}
+
+static const s32 tWB_lut[] = {6, 12, 16, 20};
+static const s32 tRHW_lut[] = {4, 8, 12, 20};
+
+static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration,
+ u32 clk_period)
+{
+ u32 clk_cycles = DIV_ROUND_UP(duration, clk_period);
+ int i;
+
+ for (i = 0; i < lut_size; i++) {
+ if (clk_cycles <= lut[i])
+ return i;
+ }
+
+ /* Doesn't fit */
+ return -EINVAL;
+}
+
+#define sunxi_nand_lookup_timing(l, p, c) \
+ _sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c)
+
+static int sunxi_nfc_setup_data_interface(struct mtd_info *mtd, int csline,
+ const struct nand_data_interface *conf)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nand_chip *chip = to_sunxi_nand(nand);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(chip->nand.controller);
+ const struct nand_sdr_timings *timings;
+ u32 min_clk_period = 0;
+ s32 tWB, tADL, tWHR, tRHW, tCAD;
+ long real_clk_rate;
+
+ timings = nand_get_sdr_timings(conf);
+ if (IS_ERR(timings))
+ return -ENOTSUPP;
+
+ /* T1 <=> tCLS */
+ if (timings->tCLS_min > min_clk_period)
+ min_clk_period = timings->tCLS_min;
+
+ /* T2 <=> tCLH */
+ if (timings->tCLH_min > min_clk_period)
+ min_clk_period = timings->tCLH_min;
+
+ /* T3 <=> tCS */
+ if (timings->tCS_min > min_clk_period)
+ min_clk_period = timings->tCS_min;
+
+ /* T4 <=> tCH */
+ if (timings->tCH_min > min_clk_period)
+ min_clk_period = timings->tCH_min;
+
+ /* T5 <=> tWP */
+ if (timings->tWP_min > min_clk_period)
+ min_clk_period = timings->tWP_min;
+
+ /* T6 <=> tWH */
+ if (timings->tWH_min > min_clk_period)
+ min_clk_period = timings->tWH_min;
+
+ /* T7 <=> tALS */
+ if (timings->tALS_min > min_clk_period)
+ min_clk_period = timings->tALS_min;
+
+ /* T8 <=> tDS */
+ if (timings->tDS_min > min_clk_period)
+ min_clk_period = timings->tDS_min;
+
+ /* T9 <=> tDH */
+ if (timings->tDH_min > min_clk_period)
+ min_clk_period = timings->tDH_min;
+
+ /* T10 <=> tRR */
+ if (timings->tRR_min > (min_clk_period * 3))
+ min_clk_period = DIV_ROUND_UP(timings->tRR_min, 3);
+
+ /* T11 <=> tALH */
+ if (timings->tALH_min > min_clk_period)
+ min_clk_period = timings->tALH_min;
+
+ /* T12 <=> tRP */
+ if (timings->tRP_min > min_clk_period)
+ min_clk_period = timings->tRP_min;
+
+ /* T13 <=> tREH */
+ if (timings->tREH_min > min_clk_period)
+ min_clk_period = timings->tREH_min;
+
+ /* T14 <=> tRC */
+ if (timings->tRC_min > (min_clk_period * 2))
+ min_clk_period = DIV_ROUND_UP(timings->tRC_min, 2);
+
+ /* T15 <=> tWC */
+ if (timings->tWC_min > (min_clk_period * 2))
+ min_clk_period = DIV_ROUND_UP(timings->tWC_min, 2);
+
+ /* T16 - T19 + tCAD */
+ if (timings->tWB_max > (min_clk_period * 20))
+ min_clk_period = DIV_ROUND_UP(timings->tWB_max, 20);
+
+ if (timings->tADL_min > (min_clk_period * 32))
+ min_clk_period = DIV_ROUND_UP(timings->tADL_min, 32);
+
+ if (timings->tWHR_min > (min_clk_period * 32))
+ min_clk_period = DIV_ROUND_UP(timings->tWHR_min, 32);
+
+ if (timings->tRHW_min > (min_clk_period * 20))
+ min_clk_period = DIV_ROUND_UP(timings->tRHW_min, 20);
+
+ tWB = sunxi_nand_lookup_timing(tWB_lut, timings->tWB_max,
+ min_clk_period);
+ if (tWB < 0) {
+ dev_err(nfc->dev, "unsupported tWB\n");
+ return tWB;
+ }
+
+ tADL = DIV_ROUND_UP(timings->tADL_min, min_clk_period) >> 3;
+ if (tADL > 3) {
+ dev_err(nfc->dev, "unsupported tADL\n");
+ return -EINVAL;
+ }
+
+ tWHR = DIV_ROUND_UP(timings->tWHR_min, min_clk_period) >> 3;
+ if (tWHR > 3) {
+ dev_err(nfc->dev, "unsupported tWHR\n");
+ return -EINVAL;
+ }
+
+ tRHW = sunxi_nand_lookup_timing(tRHW_lut, timings->tRHW_min,
+ min_clk_period);
+ if (tRHW < 0) {
+ dev_err(nfc->dev, "unsupported tRHW\n");
+ return tRHW;
+ }
+
+ if (csline == NAND_DATA_IFACE_CHECK_ONLY)
+ return 0;
+
+ /*
+ * TODO: according to ONFI specs this value only applies for DDR NAND,
+ * but Allwinner seems to set this to 0x7. Mimic them for now.
+ */
+ tCAD = 0x7;
+
+ /* TODO: A83 has some more bits for CDQSS, CS, CLHZ, CCS, WC */
+ chip->timing_cfg = NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD);
+
+ /* Convert min_clk_period from picoseconds to nanoseconds */
+ min_clk_period = DIV_ROUND_UP(min_clk_period, 1000);
+
+ /*
+ * Unlike what is stated in Allwinner datasheet, the clk_rate should
+ * be set to (1 / min_clk_period), and not (2 / min_clk_period).
+ * This new formula was verified with a scope and validated by
+ * Allwinner engineers.
+ */
+ chip->clk_rate = NSEC_PER_SEC / min_clk_period;
+ real_clk_rate = clk_round_rate(nfc->mod_clk, chip->clk_rate);
+ if (real_clk_rate <= 0) {
+ dev_err(nfc->dev, "Unable to round clk %lu\n", chip->clk_rate);
+ return -EINVAL;
+ }
+
+ /*
+ * ONFI specification 3.1, paragraph 4.15.2 dictates that EDO data
+ * output cycle timings shall be used if the host drives tRC less than
+ * 30 ns.
+ */
+ min_clk_period = NSEC_PER_SEC / real_clk_rate;
+ chip->timing_ctl = ((min_clk_period * 2) < 30) ?
+ NFC_TIMING_CTL_EDO : 0;
+
+ return 0;
+}
+
+static int sunxi_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &nand->ecc;
+
+ if (section >= ecc->steps)
+ return -ERANGE;
+
+ oobregion->offset = section * (ecc->bytes + 4) + 4;
+ oobregion->length = ecc->bytes;
+
+ return 0;
+}
+
+static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &nand->ecc;
+
+ if (section > ecc->steps)
+ return -ERANGE;
+
+ /*
+ * The first 2 bytes are used for BB markers, hence we
+ * only have 2 bytes available in the first user data
+ * section.
+ */
+ if (!section && ecc->mode == NAND_ECC_HW) {
+ oobregion->offset = 2;
+ oobregion->length = 2;
+
+ return 0;
+ }
+
+ oobregion->offset = section * (ecc->bytes + 4);
+
+ if (section < ecc->steps)
+ oobregion->length = 4;
+ else
+ oobregion->offset = mtd->oobsize - oobregion->offset;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops sunxi_nand_ooblayout_ops = {
+ .ecc = sunxi_nand_ooblayout_ecc,
+ .free = sunxi_nand_ooblayout_free,
+};
+
+static void sunxi_nand_hw_ecc_ctrl_cleanup(struct nand_ecc_ctrl *ecc)
+{
+ kfree(ecc->priv);
+}
+
+static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd,
+ struct nand_ecc_ctrl *ecc,
+ struct device_node *np)
+{
+ static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 };
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+ struct sunxi_nand_hw_ecc *data;
+ int nsectors;
+ int ret;
+ int i;
+
+ if (ecc->options & NAND_ECC_MAXIMIZE) {
+ int bytes;
+
+ ecc->size = 1024;
+ nsectors = mtd->writesize / ecc->size;
+
+ /* Reserve 2 bytes for the BBM */
+ bytes = (mtd->oobsize - 2) / nsectors;
+
+ /* 4 non-ECC bytes are added before each ECC bytes section */
+ bytes -= 4;
+
+ /* and bytes has to be even. */
+ if (bytes % 2)
+ bytes--;
+
+ ecc->strength = bytes * 8 / fls(8 * ecc->size);
+
+ for (i = 0; i < ARRAY_SIZE(strengths); i++) {
+ if (strengths[i] > ecc->strength)
+ break;
+ }
+
+ if (!i)
+ ecc->strength = 0;
+ else
+ ecc->strength = strengths[i - 1];
+ }
+
+ if (ecc->size != 512 && ecc->size != 1024)
+ return -EINVAL;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ /* Prefer 1k ECC chunk over 512 ones */
+ if (ecc->size == 512 && mtd->writesize > 512) {
+ ecc->size = 1024;
+ ecc->strength *= 2;
+ }
+
+ /* Add ECC info retrieval from DT */
+ for (i = 0; i < ARRAY_SIZE(strengths); i++) {
+ if (ecc->strength <= strengths[i]) {
+ /*
+ * Update ecc->strength value with the actual strength
+ * that will be used by the ECC engine.
+ */
+ ecc->strength = strengths[i];
+ break;
+ }
+ }
+
+ if (i >= ARRAY_SIZE(strengths)) {
+ dev_err(nfc->dev, "unsupported strength\n");
+ ret = -ENOTSUPP;
+ goto err;
+ }
+
+ data->mode = i;
+
+ /* HW ECC always request ECC bytes for 1024 bytes blocks */
+ ecc->bytes = DIV_ROUND_UP(ecc->strength * fls(8 * 1024), 8);
+
+ /* HW ECC always work with even numbers of ECC bytes */
+ ecc->bytes = ALIGN(ecc->bytes, 2);
+
+ nsectors = mtd->writesize / ecc->size;
+
+ if (mtd->oobsize < ((ecc->bytes + 4) * nsectors)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ecc->read_oob = sunxi_nfc_hw_ecc_read_oob;
+ ecc->write_oob = sunxi_nfc_hw_ecc_write_oob;
+ mtd_set_ooblayout(mtd, &sunxi_nand_ooblayout_ops);
+ ecc->priv = data;
+
+ if (nfc->dmac) {
+ ecc->read_page = sunxi_nfc_hw_ecc_read_page_dma;
+ ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage_dma;
+ ecc->write_page = sunxi_nfc_hw_ecc_write_page_dma;
+ nand->options |= NAND_USE_BOUNCE_BUFFER;
+ } else {
+ ecc->read_page = sunxi_nfc_hw_ecc_read_page;
+ ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage;
+ ecc->write_page = sunxi_nfc_hw_ecc_write_page;
+ }
+
+ /* TODO: support DMA for raw accesses and subpage write */
+ ecc->write_subpage = sunxi_nfc_hw_ecc_write_subpage;
+ ecc->read_oob_raw = nand_read_oob_std;
+ ecc->write_oob_raw = nand_write_oob_std;
+
+ return 0;
+
+err:
+ kfree(data);
+
+ return ret;
+}
+
+static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc)
+{
+ switch (ecc->mode) {
+ case NAND_ECC_HW:
+ sunxi_nand_hw_ecc_ctrl_cleanup(ecc);
+ break;
+ case NAND_ECC_NONE:
+ default:
+ break;
+ }
+}
+
+static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc,
+ struct device_node *np)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ int ret;
+
+ if (!ecc->size) {
+ ecc->size = nand->ecc_step_ds;
+ ecc->strength = nand->ecc_strength_ds;
+ }
+
+ if (!ecc->size || !ecc->strength)
+ return -EINVAL;
+
+ switch (ecc->mode) {
+ case NAND_ECC_HW:
+ ret = sunxi_nand_hw_ecc_ctrl_init(mtd, ecc, np);
+ if (ret)
+ return ret;
+ break;
+ case NAND_ECC_NONE:
+ case NAND_ECC_SOFT:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
+ struct device_node *np)
+{
+ struct sunxi_nand_chip *chip;
+ struct mtd_info *mtd;
+ struct nand_chip *nand;
+ int nsels;
+ int ret;
+ int i;
+ u32 tmp;
+
+ if (!of_get_property(np, "reg", &nsels))
+ return -EINVAL;
+
+ nsels /= sizeof(u32);
+ if (!nsels) {
+ dev_err(dev, "invalid reg property size\n");
+ return -EINVAL;
+ }
+
+ chip = devm_kzalloc(dev,
+ sizeof(*chip) +
+ (nsels * sizeof(struct sunxi_nand_chip_sel)),
+ GFP_KERNEL);
+ if (!chip) {
+ dev_err(dev, "could not allocate chip\n");
+ return -ENOMEM;
+ }
+
+ chip->nsels = nsels;
+ chip->selected = -1;
+
+ for (i = 0; i < nsels; i++) {
+ ret = of_property_read_u32_index(np, "reg", i, &tmp);
+ if (ret) {
+ dev_err(dev, "could not retrieve reg property: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (tmp > NFC_MAX_CS) {
+ dev_err(dev,
+ "invalid reg value: %u (max CS = 7)\n",
+ tmp);
+ return -EINVAL;
+ }
+
+ if (test_and_set_bit(tmp, &nfc->assigned_cs)) {
+ dev_err(dev, "CS %d already assigned\n", tmp);
+ return -EINVAL;
+ }
+
+ chip->sels[i].cs = tmp;
+
+ if (!of_property_read_u32_index(np, "allwinner,rb", i, &tmp) &&
+ tmp < 2) {
+ chip->sels[i].rb.type = RB_NATIVE;
+ chip->sels[i].rb.info.nativeid = tmp;
+ } else {
+ ret = of_get_named_gpio(np, "rb-gpios", i);
+ if (ret >= 0) {
+ tmp = ret;
+ chip->sels[i].rb.type = RB_GPIO;
+ chip->sels[i].rb.info.gpio = tmp;
+ ret = devm_gpio_request(dev, tmp, "nand-rb");
+ if (ret)
+ return ret;
+
+ ret = gpio_direction_input(tmp);
+ if (ret)
+ return ret;
+ } else {
+ chip->sels[i].rb.type = RB_NONE;
+ }
+ }
+ }
+
+ nand = &chip->nand;
+ /* Default tR value specified in the ONFI spec (chapter 4.15.1) */
+ nand->chip_delay = 200;
+ nand->controller = &nfc->controller;
+ /*
+ * Set the ECC mode to the default value in case nothing is specified
+ * in the DT.
+ */
+ nand->ecc.mode = NAND_ECC_HW;
+ nand_set_flash_node(nand, np);
+ nand->select_chip = sunxi_nfc_select_chip;
+ nand->cmd_ctrl = sunxi_nfc_cmd_ctrl;
+ nand->read_buf = sunxi_nfc_read_buf;
+ nand->write_buf = sunxi_nfc_write_buf;
+ nand->read_byte = sunxi_nfc_read_byte;
+ nand->setup_data_interface = sunxi_nfc_setup_data_interface;
+
+ mtd = nand_to_mtd(nand);
+ mtd->dev.parent = dev;
+
+ ret = nand_scan_ident(mtd, nsels, NULL);
+ if (ret)
+ return ret;
+
+ if (nand->bbt_options & NAND_BBT_USE_FLASH)
+ nand->bbt_options |= NAND_BBT_NO_OOB;
+
+ if (nand->options & NAND_NEED_SCRAMBLING)
+ nand->options |= NAND_NO_SUBPAGE_WRITE;
+
+ nand->options |= NAND_SUBPAGE_READ;
+
+ ret = sunxi_nand_ecc_init(mtd, &nand->ecc, np);
+ if (ret) {
+ dev_err(dev, "ECC init failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = nand_scan_tail(mtd);
+ if (ret) {
+ dev_err(dev, "nand_scan_tail failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = mtd_device_register(mtd, NULL, 0);
+ if (ret) {
+ dev_err(dev, "failed to register mtd device: %d\n", ret);
+ nand_release(mtd);
+ return ret;
+ }
+
+ list_add_tail(&chip->node, &nfc->chips);
+
+ return 0;
+}
+
+static int sunxi_nand_chips_init(struct device *dev, struct sunxi_nfc *nfc)
+{
+ struct device_node *np = dev->of_node;
+ struct device_node *nand_np;
+ int nchips = of_get_child_count(np);
+ int ret;
+
+ if (nchips > 8) {
+ dev_err(dev, "too many NAND chips: %d (max = 8)\n", nchips);
+ return -EINVAL;
+ }
+
+ for_each_child_of_node(np, nand_np) {
+ ret = sunxi_nand_chip_init(dev, nfc, nand_np);
+ if (ret) {
+ of_node_put(nand_np);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc)
+{
+ struct sunxi_nand_chip *chip;
+
+ while (!list_empty(&nfc->chips)) {
+ chip = list_first_entry(&nfc->chips, struct sunxi_nand_chip,
+ node);
+ nand_release(nand_to_mtd(&chip->nand));
+ sunxi_nand_ecc_cleanup(&chip->nand.ecc);
+ list_del(&chip->node);
+ }
+}
+
+static int sunxi_nfc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *r;
+ struct sunxi_nfc *nfc;
+ int irq;
+ int ret;
+
+ nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL);
+ if (!nfc)
+ return -ENOMEM;
+
+ nfc->dev = dev;
+ nand_hw_control_init(&nfc->controller);
+ INIT_LIST_HEAD(&nfc->chips);
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ nfc->regs = devm_ioremap_resource(dev, r);
+ if (IS_ERR(nfc->regs))
+ return PTR_ERR(nfc->regs);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(dev, "failed to retrieve irq\n");
+ return irq;
+ }
+
+ nfc->ahb_clk = devm_clk_get(dev, "ahb");
+ if (IS_ERR(nfc->ahb_clk)) {
+ dev_err(dev, "failed to retrieve ahb clk\n");
+ return PTR_ERR(nfc->ahb_clk);
+ }
+
+ ret = clk_prepare_enable(nfc->ahb_clk);
+ if (ret)
+ return ret;
+
+ nfc->mod_clk = devm_clk_get(dev, "mod");
+ if (IS_ERR(nfc->mod_clk)) {
+ dev_err(dev, "failed to retrieve mod clk\n");
+ ret = PTR_ERR(nfc->mod_clk);
+ goto out_ahb_clk_unprepare;
+ }
+
+ ret = clk_prepare_enable(nfc->mod_clk);
+ if (ret)
+ goto out_ahb_clk_unprepare;
+
+ nfc->reset = devm_reset_control_get_optional_exclusive(dev, "ahb");
+ if (IS_ERR(nfc->reset)) {
+ ret = PTR_ERR(nfc->reset);
+ goto out_mod_clk_unprepare;
+ }
+
+ ret = reset_control_deassert(nfc->reset);
+ if (ret) {
+ dev_err(dev, "reset err %d\n", ret);
+ goto out_mod_clk_unprepare;
+ }
+
+ ret = sunxi_nfc_rst(nfc);
+ if (ret)
+ goto out_ahb_reset_reassert;
+
+ writel(0, nfc->regs + NFC_REG_INT);
+ ret = devm_request_irq(dev, irq, sunxi_nfc_interrupt,
+ 0, "sunxi-nand", nfc);
+ if (ret)
+ goto out_ahb_reset_reassert;
+
+ nfc->dmac = dma_request_slave_channel(dev, "rxtx");
+ if (nfc->dmac) {
+ struct dma_slave_config dmac_cfg = { };
+
+ dmac_cfg.src_addr = r->start + NFC_REG_IO_DATA;
+ dmac_cfg.dst_addr = dmac_cfg.src_addr;
+ dmac_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ dmac_cfg.dst_addr_width = dmac_cfg.src_addr_width;
+ dmac_cfg.src_maxburst = 4;
+ dmac_cfg.dst_maxburst = 4;
+ dmaengine_slave_config(nfc->dmac, &dmac_cfg);
+ } else {
+ dev_warn(dev, "failed to request rxtx DMA channel\n");
+ }
+
+ platform_set_drvdata(pdev, nfc);
+
+ ret = sunxi_nand_chips_init(dev, nfc);
+ if (ret) {
+ dev_err(dev, "failed to init nand chips\n");
+ goto out_release_dmac;
+ }
+
+ return 0;
+
+out_release_dmac:
+ if (nfc->dmac)
+ dma_release_channel(nfc->dmac);
+out_ahb_reset_reassert:
+ reset_control_assert(nfc->reset);
+out_mod_clk_unprepare:
+ clk_disable_unprepare(nfc->mod_clk);
+out_ahb_clk_unprepare:
+ clk_disable_unprepare(nfc->ahb_clk);
+
+ return ret;
+}
+
+static int sunxi_nfc_remove(struct platform_device *pdev)
+{
+ struct sunxi_nfc *nfc = platform_get_drvdata(pdev);
+
+ sunxi_nand_chips_cleanup(nfc);
+
+ reset_control_assert(nfc->reset);
+
+ if (nfc->dmac)
+ dma_release_channel(nfc->dmac);
+ clk_disable_unprepare(nfc->mod_clk);
+ clk_disable_unprepare(nfc->ahb_clk);
+
+ return 0;
+}
+
+static const struct of_device_id sunxi_nfc_ids[] = {
+ { .compatible = "allwinner,sun4i-a10-nand" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sunxi_nfc_ids);
+
+static struct platform_driver sunxi_nfc_driver = {
+ .driver = {
+ .name = "sunxi_nand",
+ .of_match_table = sunxi_nfc_ids,
+ },
+ .probe = sunxi_nfc_probe,
+ .remove = sunxi_nfc_remove,
+};
+module_platform_driver(sunxi_nfc_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Boris BREZILLON");
+MODULE_DESCRIPTION("Allwinner NAND Flash Controller driver");
+MODULE_ALIAS("platform:sunxi_nand");
diff --git a/drivers/mtd/nand/raw/tango_nand.c b/drivers/mtd/nand/raw/tango_nand.c
new file mode 100644
index 000000000000..f2052fae21c7
--- /dev/null
+++ b/drivers/mtd/nand/raw/tango_nand.c
@@ -0,0 +1,690 @@
+/*
+ * Copyright (C) 2016 Sigma Designs
+ *
+ * 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.
+ */
+
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+
+/* Offsets relative to chip->base */
+#define PBUS_CMD 0
+#define PBUS_ADDR 4
+#define PBUS_DATA 8
+
+/* Offsets relative to reg_base */
+#define NFC_STATUS 0x00
+#define NFC_FLASH_CMD 0x04
+#define NFC_DEVICE_CFG 0x08
+#define NFC_TIMING1 0x0c
+#define NFC_TIMING2 0x10
+#define NFC_XFER_CFG 0x14
+#define NFC_PKT_0_CFG 0x18
+#define NFC_PKT_N_CFG 0x1c
+#define NFC_BB_CFG 0x20
+#define NFC_ADDR_PAGE 0x24
+#define NFC_ADDR_OFFSET 0x28
+#define NFC_XFER_STATUS 0x2c
+
+/* NFC_STATUS values */
+#define CMD_READY BIT(31)
+
+/* NFC_FLASH_CMD values */
+#define NFC_READ 1
+#define NFC_WRITE 2
+
+/* NFC_XFER_STATUS values */
+#define PAGE_IS_EMPTY BIT(16)
+
+/* Offsets relative to mem_base */
+#define METADATA 0x000
+#define ERROR_REPORT 0x1c0
+
+/*
+ * Error reports are split in two bytes:
+ * byte 0 for the first packet in the page (PKT_0)
+ * byte 1 for other packets in the page (PKT_N, for N > 0)
+ * ERR_COUNT_PKT_N is the max error count over all but the first packet.
+ */
+#define ERR_COUNT_PKT_0(v) (((v) >> 0) & 0x3f)
+#define ERR_COUNT_PKT_N(v) (((v) >> 8) & 0x3f)
+#define DECODE_FAIL_PKT_0(v) (((v) & BIT(7)) == 0)
+#define DECODE_FAIL_PKT_N(v) (((v) & BIT(15)) == 0)
+
+/* Offsets relative to pbus_base */
+#define PBUS_CS_CTRL 0x83c
+#define PBUS_PAD_MODE 0x8f0
+
+/* PBUS_CS_CTRL values */
+#define PBUS_IORDY BIT(31)
+
+/*
+ * PBUS_PAD_MODE values
+ * In raw mode, the driver communicates directly with the NAND chips.
+ * In NFC mode, the NAND Flash controller manages the communication.
+ * We use NFC mode for read and write; raw mode for everything else.
+ */
+#define MODE_RAW 0
+#define MODE_NFC BIT(31)
+
+#define METADATA_SIZE 4
+#define BBM_SIZE 6
+#define FIELD_ORDER 15
+
+#define MAX_CS 4
+
+struct tango_nfc {
+ struct nand_hw_control hw;
+ void __iomem *reg_base;
+ void __iomem *mem_base;
+ void __iomem *pbus_base;
+ struct tango_chip *chips[MAX_CS];
+ struct dma_chan *chan;
+ int freq_kHz;
+};
+
+#define to_tango_nfc(ptr) container_of(ptr, struct tango_nfc, hw)
+
+struct tango_chip {
+ struct nand_chip nand_chip;
+ void __iomem *base;
+ u32 timing1;
+ u32 timing2;
+ u32 xfer_cfg;
+ u32 pkt_0_cfg;
+ u32 pkt_n_cfg;
+ u32 bb_cfg;
+};
+
+#define to_tango_chip(ptr) container_of(ptr, struct tango_chip, nand_chip)
+
+#define XFER_CFG(cs, page_count, steps, metadata_size) \
+ ((cs) << 24 | (page_count) << 16 | (steps) << 8 | (metadata_size))
+
+#define PKT_CFG(size, strength) ((size) << 16 | (strength))
+
+#define BB_CFG(bb_offset, bb_size) ((bb_offset) << 16 | (bb_size))
+
+#define TIMING(t0, t1, t2, t3) ((t0) << 24 | (t1) << 16 | (t2) << 8 | (t3))
+
+static void tango_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
+{
+ struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd));
+
+ if (ctrl & NAND_CLE)
+ writeb_relaxed(dat, tchip->base + PBUS_CMD);
+
+ if (ctrl & NAND_ALE)
+ writeb_relaxed(dat, tchip->base + PBUS_ADDR);
+}
+
+static int tango_dev_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct tango_nfc *nfc = to_tango_nfc(chip->controller);
+
+ return readl_relaxed(nfc->pbus_base + PBUS_CS_CTRL) & PBUS_IORDY;
+}
+
+static u8 tango_read_byte(struct mtd_info *mtd)
+{
+ struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd));
+
+ return readb_relaxed(tchip->base + PBUS_DATA);
+}
+
+static void tango_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+ struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd));
+
+ ioread8_rep(tchip->base + PBUS_DATA, buf, len);
+}
+
+static void tango_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd));
+
+ iowrite8_rep(tchip->base + PBUS_DATA, buf, len);
+}
+
+static void tango_select_chip(struct mtd_info *mtd, int idx)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct tango_nfc *nfc = to_tango_nfc(chip->controller);
+ struct tango_chip *tchip = to_tango_chip(chip);
+
+ if (idx < 0)
+ return; /* No "chip unselect" function */
+
+ writel_relaxed(tchip->timing1, nfc->reg_base + NFC_TIMING1);
+ writel_relaxed(tchip->timing2, nfc->reg_base + NFC_TIMING2);
+ writel_relaxed(tchip->xfer_cfg, nfc->reg_base + NFC_XFER_CFG);
+ writel_relaxed(tchip->pkt_0_cfg, nfc->reg_base + NFC_PKT_0_CFG);
+ writel_relaxed(tchip->pkt_n_cfg, nfc->reg_base + NFC_PKT_N_CFG);
+ writel_relaxed(tchip->bb_cfg, nfc->reg_base + NFC_BB_CFG);
+}
+
+/*
+ * The controller does not check for bitflips in erased pages,
+ * therefore software must check instead.
+ */
+static int check_erased_page(struct nand_chip *chip, u8 *buf)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ u8 *meta = chip->oob_poi + BBM_SIZE;
+ u8 *ecc = chip->oob_poi + BBM_SIZE + METADATA_SIZE;
+ const int ecc_size = chip->ecc.bytes;
+ const int pkt_size = chip->ecc.size;
+ int i, res, meta_len, bitflips = 0;
+
+ for (i = 0; i < chip->ecc.steps; ++i) {
+ meta_len = i ? 0 : METADATA_SIZE;
+ res = nand_check_erased_ecc_chunk(buf, pkt_size, ecc, ecc_size,
+ meta, meta_len,
+ chip->ecc.strength);
+ if (res < 0)
+ mtd->ecc_stats.failed++;
+ else
+ mtd->ecc_stats.corrected += res;
+
+ bitflips = max(res, bitflips);
+ buf += pkt_size;
+ ecc += ecc_size;
+ }
+
+ return bitflips;
+}
+
+static int decode_error_report(struct nand_chip *chip)
+{
+ u32 status, res;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct tango_nfc *nfc = to_tango_nfc(chip->controller);
+
+ status = readl_relaxed(nfc->reg_base + NFC_XFER_STATUS);
+ if (status & PAGE_IS_EMPTY)
+ return 0;
+
+ res = readl_relaxed(nfc->mem_base + ERROR_REPORT);
+
+ if (DECODE_FAIL_PKT_0(res) || DECODE_FAIL_PKT_N(res))
+ return -EBADMSG;
+
+ /* ERR_COUNT_PKT_N is max, not sum, but that's all we have */
+ mtd->ecc_stats.corrected +=
+ ERR_COUNT_PKT_0(res) + ERR_COUNT_PKT_N(res);
+
+ return max(ERR_COUNT_PKT_0(res), ERR_COUNT_PKT_N(res));
+}
+
+static void tango_dma_callback(void *arg)
+{
+ complete(arg);
+}
+
+static int do_dma(struct tango_nfc *nfc, enum dma_data_direction dir, int cmd,
+ const void *buf, int len, int page)
+{
+ void __iomem *addr = nfc->reg_base + NFC_STATUS;
+ struct dma_chan *chan = nfc->chan;
+ struct dma_async_tx_descriptor *desc;
+ enum dma_transfer_direction tdir;
+ struct scatterlist sg;
+ struct completion tx_done;
+ int err = -EIO;
+ u32 res, val;
+
+ sg_init_one(&sg, buf, len);
+ if (dma_map_sg(chan->device->dev, &sg, 1, dir) != 1)
+ return -EIO;
+
+ tdir = dir == DMA_TO_DEVICE ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
+ desc = dmaengine_prep_slave_sg(chan, &sg, 1, tdir, DMA_PREP_INTERRUPT);
+ if (!desc)
+ goto dma_unmap;
+
+ desc->callback = tango_dma_callback;
+ desc->callback_param = &tx_done;
+ init_completion(&tx_done);
+
+ writel_relaxed(MODE_NFC, nfc->pbus_base + PBUS_PAD_MODE);
+
+ writel_relaxed(page, nfc->reg_base + NFC_ADDR_PAGE);
+ writel_relaxed(0, nfc->reg_base + NFC_ADDR_OFFSET);
+ writel_relaxed(cmd, nfc->reg_base + NFC_FLASH_CMD);
+
+ dmaengine_submit(desc);
+ dma_async_issue_pending(chan);
+
+ res = wait_for_completion_timeout(&tx_done, HZ);
+ if (res > 0)
+ err = readl_poll_timeout(addr, val, val & CMD_READY, 0, 1000);
+
+ writel_relaxed(MODE_RAW, nfc->pbus_base + PBUS_PAD_MODE);
+
+dma_unmap:
+ dma_unmap_sg(chan->device->dev, &sg, 1, dir);
+
+ return err;
+}
+
+static int tango_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+ u8 *buf, int oob_required, int page)
+{
+ struct tango_nfc *nfc = to_tango_nfc(chip->controller);
+ int err, res, len = mtd->writesize;
+
+ if (oob_required)
+ chip->ecc.read_oob(mtd, chip, page);
+
+ err = do_dma(nfc, DMA_FROM_DEVICE, NFC_READ, buf, len, page);
+ if (err)
+ return err;
+
+ res = decode_error_report(chip);
+ if (res < 0) {
+ chip->ecc.read_oob_raw(mtd, chip, page);
+ res = check_erased_page(chip, buf);
+ }
+
+ return res;
+}
+
+static int tango_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ const u8 *buf, int oob_required, int page)
+{
+ struct tango_nfc *nfc = to_tango_nfc(chip->controller);
+ int err, status, len = mtd->writesize;
+
+ /* Calling tango_write_oob() would send PAGEPROG twice */
+ if (oob_required)
+ return -ENOTSUPP;
+
+ writel_relaxed(0xffffffff, nfc->mem_base + METADATA);
+ err = do_dma(nfc, DMA_TO_DEVICE, NFC_WRITE, buf, len, page);
+ if (err)
+ return err;
+
+ status = chip->waitfunc(mtd, chip);
+ if (status & NAND_STATUS_FAIL)
+ return -EIO;
+
+ return 0;
+}
+
+static void aux_read(struct nand_chip *chip, u8 **buf, int len, int *pos)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ *pos += len;
+
+ if (!*buf) {
+ /* skip over "len" bytes */
+ nand_change_read_column_op(chip, *pos, NULL, 0, false);
+ } else {
+ tango_read_buf(mtd, *buf, len);
+ *buf += len;
+ }
+}
+
+static void aux_write(struct nand_chip *chip, const u8 **buf, int len, int *pos)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ *pos += len;
+
+ if (!*buf) {
+ /* skip over "len" bytes */
+ nand_change_write_column_op(chip, *pos, NULL, 0, false);
+ } else {
+ tango_write_buf(mtd, *buf, len);
+ *buf += len;
+ }
+}
+
+/*
+ * Physical page layout (not drawn to scale)
+ *
+ * NB: Bad Block Marker area splits PKT_N in two (N1, N2).
+ *
+ * +---+-----------------+-------+-----+-----------+-----+----+-------+
+ * | M | PKT_0 | ECC_0 | ... | N1 | BBM | N2 | ECC_N |
+ * +---+-----------------+-------+-----+-----------+-----+----+-------+
+ *
+ * Logical page layout:
+ *
+ * +-----+---+-------+-----+-------+
+ * oob = | BBM | M | ECC_0 | ... | ECC_N |
+ * +-----+---+-------+-----+-------+
+ *
+ * +-----------------+-----+-----------------+
+ * buf = | PKT_0 | ... | PKT_N |
+ * +-----------------+-----+-----------------+
+ */
+static void raw_read(struct nand_chip *chip, u8 *buf, u8 *oob)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ u8 *oob_orig = oob;
+ const int page_size = mtd->writesize;
+ const int ecc_size = chip->ecc.bytes;
+ const int pkt_size = chip->ecc.size;
+ int pos = 0; /* position within physical page */
+ int rem = page_size; /* bytes remaining until BBM area */
+
+ if (oob)
+ oob += BBM_SIZE;
+
+ aux_read(chip, &oob, METADATA_SIZE, &pos);
+
+ while (rem > pkt_size) {
+ aux_read(chip, &buf, pkt_size, &pos);
+ aux_read(chip, &oob, ecc_size, &pos);
+ rem = page_size - pos;
+ }
+
+ aux_read(chip, &buf, rem, &pos);
+ aux_read(chip, &oob_orig, BBM_SIZE, &pos);
+ aux_read(chip, &buf, pkt_size - rem, &pos);
+ aux_read(chip, &oob, ecc_size, &pos);
+}
+
+static void raw_write(struct nand_chip *chip, const u8 *buf, const u8 *oob)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ const u8 *oob_orig = oob;
+ const int page_size = mtd->writesize;
+ const int ecc_size = chip->ecc.bytes;
+ const int pkt_size = chip->ecc.size;
+ int pos = 0; /* position within physical page */
+ int rem = page_size; /* bytes remaining until BBM area */
+
+ if (oob)
+ oob += BBM_SIZE;
+
+ aux_write(chip, &oob, METADATA_SIZE, &pos);
+
+ while (rem > pkt_size) {
+ aux_write(chip, &buf, pkt_size, &pos);
+ aux_write(chip, &oob, ecc_size, &pos);
+ rem = page_size - pos;
+ }
+
+ aux_write(chip, &buf, rem, &pos);
+ aux_write(chip, &oob_orig, BBM_SIZE, &pos);
+ aux_write(chip, &buf, pkt_size - rem, &pos);
+ aux_write(chip, &oob, ecc_size, &pos);
+}
+
+static int tango_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ u8 *buf, int oob_required, int page)
+{
+ nand_read_page_op(chip, page, 0, NULL, 0);
+ raw_read(chip, buf, chip->oob_poi);
+ return 0;
+}
+
+static int tango_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ const u8 *buf, int oob_required, int page)
+{
+ nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+ raw_write(chip, buf, chip->oob_poi);
+ return nand_prog_page_end_op(chip);
+}
+
+static int tango_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ nand_read_page_op(chip, page, 0, NULL, 0);
+ raw_read(chip, NULL, chip->oob_poi);
+ return 0;
+}
+
+static int tango_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+ raw_write(chip, NULL, chip->oob_poi);
+ return nand_prog_page_end_op(chip);
+}
+
+static int oob_ecc(struct mtd_info *mtd, int idx, struct mtd_oob_region *res)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+
+ if (idx >= ecc->steps)
+ return -ERANGE;
+
+ res->offset = BBM_SIZE + METADATA_SIZE + ecc->bytes * idx;
+ res->length = ecc->bytes;
+
+ return 0;
+}
+
+static int oob_free(struct mtd_info *mtd, int idx, struct mtd_oob_region *res)
+{
+ return -ERANGE; /* no free space in spare area */
+}
+
+static const struct mtd_ooblayout_ops tango_nand_ooblayout_ops = {
+ .ecc = oob_ecc,
+ .free = oob_free,
+};
+
+static u32 to_ticks(int kHz, int ps)
+{
+ return DIV_ROUND_UP_ULL((u64)kHz * ps, NSEC_PER_SEC);
+}
+
+static int tango_set_timings(struct mtd_info *mtd, int csline,
+ const struct nand_data_interface *conf)
+{
+ const struct nand_sdr_timings *sdr = nand_get_sdr_timings(conf);
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct tango_nfc *nfc = to_tango_nfc(chip->controller);
+ struct tango_chip *tchip = to_tango_chip(chip);
+ u32 Trdy, Textw, Twc, Twpw, Tacc, Thold, Trpw, Textr;
+ int kHz = nfc->freq_kHz;
+
+ if (IS_ERR(sdr))
+ return PTR_ERR(sdr);
+
+ if (csline == NAND_DATA_IFACE_CHECK_ONLY)
+ return 0;
+
+ Trdy = to_ticks(kHz, sdr->tCEA_max - sdr->tREA_max);
+ Textw = to_ticks(kHz, sdr->tWB_max);
+ Twc = to_ticks(kHz, sdr->tWC_min);
+ Twpw = to_ticks(kHz, sdr->tWC_min - sdr->tWP_min);
+
+ Tacc = to_ticks(kHz, sdr->tREA_max);
+ Thold = to_ticks(kHz, sdr->tREH_min);
+ Trpw = to_ticks(kHz, sdr->tRC_min - sdr->tREH_min);
+ Textr = to_ticks(kHz, sdr->tRHZ_max);
+
+ tchip->timing1 = TIMING(Trdy, Textw, Twc, Twpw);
+ tchip->timing2 = TIMING(Tacc, Thold, Trpw, Textr);
+
+ return 0;
+}
+
+static int chip_init(struct device *dev, struct device_node *np)
+{
+ u32 cs;
+ int err, res;
+ struct mtd_info *mtd;
+ struct nand_chip *chip;
+ struct tango_chip *tchip;
+ struct nand_ecc_ctrl *ecc;
+ struct tango_nfc *nfc = dev_get_drvdata(dev);
+
+ tchip = devm_kzalloc(dev, sizeof(*tchip), GFP_KERNEL);
+ if (!tchip)
+ return -ENOMEM;
+
+ res = of_property_count_u32_elems(np, "reg");
+ if (res < 0)
+ return res;
+
+ if (res != 1)
+ return -ENOTSUPP; /* Multi-CS chips are not supported */
+
+ err = of_property_read_u32_index(np, "reg", 0, &cs);
+ if (err)
+ return err;
+
+ if (cs >= MAX_CS)
+ return -EINVAL;
+
+ chip = &tchip->nand_chip;
+ ecc = &chip->ecc;
+ mtd = nand_to_mtd(chip);
+
+ chip->read_byte = tango_read_byte;
+ chip->write_buf = tango_write_buf;
+ chip->read_buf = tango_read_buf;
+ chip->select_chip = tango_select_chip;
+ chip->cmd_ctrl = tango_cmd_ctrl;
+ chip->dev_ready = tango_dev_ready;
+ chip->setup_data_interface = tango_set_timings;
+ chip->options = NAND_USE_BOUNCE_BUFFER |
+ NAND_NO_SUBPAGE_WRITE |
+ NAND_WAIT_TCCS;
+ chip->controller = &nfc->hw;
+ tchip->base = nfc->pbus_base + (cs * 256);
+
+ nand_set_flash_node(chip, np);
+ mtd_set_ooblayout(mtd, &tango_nand_ooblayout_ops);
+ mtd->dev.parent = dev;
+
+ err = nand_scan_ident(mtd, 1, NULL);
+ if (err)
+ return err;
+
+ ecc->mode = NAND_ECC_HW;
+ ecc->algo = NAND_ECC_BCH;
+ ecc->bytes = DIV_ROUND_UP(ecc->strength * FIELD_ORDER, BITS_PER_BYTE);
+
+ ecc->read_page_raw = tango_read_page_raw;
+ ecc->write_page_raw = tango_write_page_raw;
+ ecc->read_page = tango_read_page;
+ ecc->write_page = tango_write_page;
+ ecc->read_oob = tango_read_oob;
+ ecc->write_oob = tango_write_oob;
+
+ err = nand_scan_tail(mtd);
+ if (err)
+ return err;
+
+ tchip->xfer_cfg = XFER_CFG(cs, 1, ecc->steps, METADATA_SIZE);
+ tchip->pkt_0_cfg = PKT_CFG(ecc->size + METADATA_SIZE, ecc->strength);
+ tchip->pkt_n_cfg = PKT_CFG(ecc->size, ecc->strength);
+ tchip->bb_cfg = BB_CFG(mtd->writesize, BBM_SIZE);
+
+ err = mtd_device_register(mtd, NULL, 0);
+ if (err) {
+ nand_cleanup(chip);
+ return err;
+ }
+
+ nfc->chips[cs] = tchip;
+
+ return 0;
+}
+
+static int tango_nand_remove(struct platform_device *pdev)
+{
+ int cs;
+ struct tango_nfc *nfc = platform_get_drvdata(pdev);
+
+ dma_release_channel(nfc->chan);
+
+ for (cs = 0; cs < MAX_CS; ++cs) {
+ if (nfc->chips[cs])
+ nand_release(nand_to_mtd(&nfc->chips[cs]->nand_chip));
+ }
+
+ return 0;
+}
+
+static int tango_nand_probe(struct platform_device *pdev)
+{
+ int err;
+ struct clk *clk;
+ struct resource *res;
+ struct tango_nfc *nfc;
+ struct device_node *np;
+
+ nfc = devm_kzalloc(&pdev->dev, sizeof(*nfc), GFP_KERNEL);
+ if (!nfc)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ nfc->reg_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(nfc->reg_base))
+ return PTR_ERR(nfc->reg_base);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ nfc->mem_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(nfc->mem_base))
+ return PTR_ERR(nfc->mem_base);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ nfc->pbus_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(nfc->pbus_base))
+ return PTR_ERR(nfc->pbus_base);
+
+ writel_relaxed(MODE_RAW, nfc->pbus_base + PBUS_PAD_MODE);
+
+ clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ nfc->chan = dma_request_chan(&pdev->dev, "rxtx");
+ if (IS_ERR(nfc->chan))
+ return PTR_ERR(nfc->chan);
+
+ platform_set_drvdata(pdev, nfc);
+ nand_hw_control_init(&nfc->hw);
+ nfc->freq_kHz = clk_get_rate(clk) / 1000;
+
+ for_each_child_of_node(pdev->dev.of_node, np) {
+ err = chip_init(&pdev->dev, np);
+ if (err) {
+ tango_nand_remove(pdev);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static const struct of_device_id tango_nand_ids[] = {
+ { .compatible = "sigma,smp8758-nand" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, tango_nand_ids);
+
+static struct platform_driver tango_nand_driver = {
+ .probe = tango_nand_probe,
+ .remove = tango_nand_remove,
+ .driver = {
+ .name = "tango-nand",
+ .of_match_table = tango_nand_ids,
+ },
+};
+
+module_platform_driver(tango_nand_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sigma Designs");
+MODULE_DESCRIPTION("Tango4 NAND Flash controller driver");
diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/raw/tmio_nand.c
index dcaa924502de..dcaa924502de 100644
--- a/drivers/mtd/nand/tmio_nand.c
+++ b/drivers/mtd/nand/raw/tmio_nand.c
diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/raw/txx9ndfmc.c
index b567d212fe7d..b567d212fe7d 100644
--- a/drivers/mtd/nand/txx9ndfmc.c
+++ b/drivers/mtd/nand/raw/txx9ndfmc.c
diff --git a/drivers/mtd/nand/raw/vf610_nfc.c b/drivers/mtd/nand/raw/vf610_nfc.c
new file mode 100644
index 000000000000..d5a22fc96878
--- /dev/null
+++ b/drivers/mtd/nand/raw/vf610_nfc.c
@@ -0,0 +1,962 @@
+/*
+ * Copyright 2009-2015 Freescale Semiconductor, Inc. and others
+ *
+ * Description: MPC5125, VF610, MCF54418 and Kinetis K70 Nand driver.
+ * Jason ported to M54418TWR and MVFA5 (VF610).
+ * Authors: Stefan Agner <stefan.agner@toradex.com>
+ * Bill Pringlemeir <bpringlemeir@nbsps.com>
+ * Shaohui Xie <b21989@freescale.com>
+ * Jason Jin <Jason.jin@freescale.com>
+ *
+ * Based on original driver mpc5121_nfc.c.
+ *
+ * This 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.
+ *
+ * Limitations:
+ * - Untested on MPC5125 and M54418.
+ * - DMA and pipelining not used.
+ * - 2K pages or less.
+ * - HW ECC: Only 2K page with 64+ OOB.
+ * - HW ECC: Only 24 and 32-bit error correction implemented.
+ */
+
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/swab.h>
+
+#define DRV_NAME "vf610_nfc"
+
+/* Register Offsets */
+#define NFC_FLASH_CMD1 0x3F00
+#define NFC_FLASH_CMD2 0x3F04
+#define NFC_COL_ADDR 0x3F08
+#define NFC_ROW_ADDR 0x3F0c
+#define NFC_ROW_ADDR_INC 0x3F14
+#define NFC_FLASH_STATUS1 0x3F18
+#define NFC_FLASH_STATUS2 0x3F1c
+#define NFC_CACHE_SWAP 0x3F28
+#define NFC_SECTOR_SIZE 0x3F2c
+#define NFC_FLASH_CONFIG 0x3F30
+#define NFC_IRQ_STATUS 0x3F38
+
+/* Addresses for NFC MAIN RAM BUFFER areas */
+#define NFC_MAIN_AREA(n) ((n) * 0x1000)
+
+#define PAGE_2K 0x0800
+#define OOB_64 0x0040
+#define OOB_MAX 0x0100
+
+/* NFC_CMD2[CODE] controller cycle bit masks */
+#define COMMAND_CMD_BYTE1 BIT(14)
+#define COMMAND_CAR_BYTE1 BIT(13)
+#define COMMAND_CAR_BYTE2 BIT(12)
+#define COMMAND_RAR_BYTE1 BIT(11)
+#define COMMAND_RAR_BYTE2 BIT(10)
+#define COMMAND_RAR_BYTE3 BIT(9)
+#define COMMAND_NADDR_BYTES(x) GENMASK(13, 13 - (x) + 1)
+#define COMMAND_WRITE_DATA BIT(8)
+#define COMMAND_CMD_BYTE2 BIT(7)
+#define COMMAND_RB_HANDSHAKE BIT(6)
+#define COMMAND_READ_DATA BIT(5)
+#define COMMAND_CMD_BYTE3 BIT(4)
+#define COMMAND_READ_STATUS BIT(3)
+#define COMMAND_READ_ID BIT(2)
+
+/* NFC ECC mode define */
+#define ECC_BYPASS 0
+#define ECC_45_BYTE 6
+#define ECC_60_BYTE 7
+
+/*** Register Mask and bit definitions */
+
+/* NFC_FLASH_CMD1 Field */
+#define CMD_BYTE2_MASK 0xFF000000
+#define CMD_BYTE2_SHIFT 24
+
+/* NFC_FLASH_CM2 Field */
+#define CMD_BYTE1_MASK 0xFF000000
+#define CMD_BYTE1_SHIFT 24
+#define CMD_CODE_MASK 0x00FFFF00
+#define CMD_CODE_SHIFT 8
+#define BUFNO_MASK 0x00000006
+#define BUFNO_SHIFT 1
+#define START_BIT BIT(0)
+
+/* NFC_COL_ADDR Field */
+#define COL_ADDR_MASK 0x0000FFFF
+#define COL_ADDR_SHIFT 0
+#define COL_ADDR(pos, val) (((val) & 0xFF) << (8 * (pos)))
+
+/* NFC_ROW_ADDR Field */
+#define ROW_ADDR_MASK 0x00FFFFFF
+#define ROW_ADDR_SHIFT 0
+#define ROW_ADDR(pos, val) (((val) & 0xFF) << (8 * (pos)))
+
+#define ROW_ADDR_CHIP_SEL_RB_MASK 0xF0000000
+#define ROW_ADDR_CHIP_SEL_RB_SHIFT 28
+#define ROW_ADDR_CHIP_SEL_MASK 0x0F000000
+#define ROW_ADDR_CHIP_SEL_SHIFT 24
+
+/* NFC_FLASH_STATUS2 Field */
+#define STATUS_BYTE1_MASK 0x000000FF
+
+/* NFC_FLASH_CONFIG Field */
+#define CONFIG_ECC_SRAM_ADDR_MASK 0x7FC00000
+#define CONFIG_ECC_SRAM_ADDR_SHIFT 22
+#define CONFIG_ECC_SRAM_REQ_BIT BIT(21)
+#define CONFIG_DMA_REQ_BIT BIT(20)
+#define CONFIG_ECC_MODE_MASK 0x000E0000
+#define CONFIG_ECC_MODE_SHIFT 17
+#define CONFIG_FAST_FLASH_BIT BIT(16)
+#define CONFIG_16BIT BIT(7)
+#define CONFIG_BOOT_MODE_BIT BIT(6)
+#define CONFIG_ADDR_AUTO_INCR_BIT BIT(5)
+#define CONFIG_BUFNO_AUTO_INCR_BIT BIT(4)
+#define CONFIG_PAGE_CNT_MASK 0xF
+#define CONFIG_PAGE_CNT_SHIFT 0
+
+/* NFC_IRQ_STATUS Field */
+#define IDLE_IRQ_BIT BIT(29)
+#define IDLE_EN_BIT BIT(20)
+#define CMD_DONE_CLEAR_BIT BIT(18)
+#define IDLE_CLEAR_BIT BIT(17)
+
+/*
+ * ECC status - seems to consume 8 bytes (double word). The documented
+ * status byte is located in the lowest byte of the second word (which is
+ * the 4th or 7th byte depending on endianness).
+ * Calculate an offset to store the ECC status at the end of the buffer.
+ */
+#define ECC_SRAM_ADDR (PAGE_2K + OOB_MAX - 8)
+
+#define ECC_STATUS 0x4
+#define ECC_STATUS_MASK 0x80
+#define ECC_STATUS_ERR_COUNT 0x3F
+
+enum vf610_nfc_variant {
+ NFC_VFC610 = 1,
+};
+
+struct vf610_nfc {
+ struct nand_chip chip;
+ struct device *dev;
+ void __iomem *regs;
+ struct completion cmd_done;
+ /* Status and ID are in alternate locations. */
+ enum vf610_nfc_variant variant;
+ struct clk *clk;
+ /*
+ * Indicate that user data is accessed (full page/oob). This is
+ * useful to indicate the driver whether to swap byte endianness.
+ * See comments in vf610_nfc_rd_from_sram/vf610_nfc_wr_to_sram.
+ */
+ bool data_access;
+ u32 ecc_mode;
+};
+
+static inline struct vf610_nfc *mtd_to_nfc(struct mtd_info *mtd)
+{
+ return container_of(mtd_to_nand(mtd), struct vf610_nfc, chip);
+}
+
+static inline struct vf610_nfc *chip_to_nfc(struct nand_chip *chip)
+{
+ return container_of(chip, struct vf610_nfc, chip);
+}
+
+static inline u32 vf610_nfc_read(struct vf610_nfc *nfc, uint reg)
+{
+ return readl(nfc->regs + reg);
+}
+
+static inline void vf610_nfc_write(struct vf610_nfc *nfc, uint reg, u32 val)
+{
+ writel(val, nfc->regs + reg);
+}
+
+static inline void vf610_nfc_set(struct vf610_nfc *nfc, uint reg, u32 bits)
+{
+ vf610_nfc_write(nfc, reg, vf610_nfc_read(nfc, reg) | bits);
+}
+
+static inline void vf610_nfc_clear(struct vf610_nfc *nfc, uint reg, u32 bits)
+{
+ vf610_nfc_write(nfc, reg, vf610_nfc_read(nfc, reg) & ~bits);
+}
+
+static inline void vf610_nfc_set_field(struct vf610_nfc *nfc, u32 reg,
+ u32 mask, u32 shift, u32 val)
+{
+ vf610_nfc_write(nfc, reg,
+ (vf610_nfc_read(nfc, reg) & (~mask)) | val << shift);
+}
+
+static inline bool vf610_nfc_kernel_is_little_endian(void)
+{
+#ifdef __LITTLE_ENDIAN
+ return true;
+#else
+ return false;
+#endif
+}
+
+/**
+ * Read accessor for internal SRAM buffer
+ * @dst: destination address in regular memory
+ * @src: source address in SRAM buffer
+ * @len: bytes to copy
+ * @fix_endian: Fix endianness if required
+ *
+ * Use this accessor for the internal SRAM buffers. On the ARM
+ * Freescale Vybrid SoC it's known that the driver can treat
+ * the SRAM buffer as if it's memory. Other platform might need
+ * to treat the buffers differently.
+ *
+ * The controller stores bytes from the NAND chip internally in big
+ * endianness. On little endian platforms such as Vybrid this leads
+ * to reversed byte order.
+ * For performance reason (and earlier probably due to unawareness)
+ * the driver avoids correcting endianness where it has control over
+ * write and read side (e.g. page wise data access).
+ */
+static inline void vf610_nfc_rd_from_sram(void *dst, const void __iomem *src,
+ size_t len, bool fix_endian)
+{
+ if (vf610_nfc_kernel_is_little_endian() && fix_endian) {
+ unsigned int i;
+
+ for (i = 0; i < len; i += 4) {
+ u32 val = swab32(__raw_readl(src + i));
+
+ memcpy(dst + i, &val, min(sizeof(val), len - i));
+ }
+ } else {
+ memcpy_fromio(dst, src, len);
+ }
+}
+
+/**
+ * Write accessor for internal SRAM buffer
+ * @dst: destination address in SRAM buffer
+ * @src: source address in regular memory
+ * @len: bytes to copy
+ * @fix_endian: Fix endianness if required
+ *
+ * Use this accessor for the internal SRAM buffers. On the ARM
+ * Freescale Vybrid SoC it's known that the driver can treat
+ * the SRAM buffer as if it's memory. Other platform might need
+ * to treat the buffers differently.
+ *
+ * The controller stores bytes from the NAND chip internally in big
+ * endianness. On little endian platforms such as Vybrid this leads
+ * to reversed byte order.
+ * For performance reason (and earlier probably due to unawareness)
+ * the driver avoids correcting endianness where it has control over
+ * write and read side (e.g. page wise data access).
+ */
+static inline void vf610_nfc_wr_to_sram(void __iomem *dst, const void *src,
+ size_t len, bool fix_endian)
+{
+ if (vf610_nfc_kernel_is_little_endian() && fix_endian) {
+ unsigned int i;
+
+ for (i = 0; i < len; i += 4) {
+ u32 val;
+
+ memcpy(&val, src + i, min(sizeof(val), len - i));
+ __raw_writel(swab32(val), dst + i);
+ }
+ } else {
+ memcpy_toio(dst, src, len);
+ }
+}
+
+/* Clear flags for upcoming command */
+static inline void vf610_nfc_clear_status(struct vf610_nfc *nfc)
+{
+ u32 tmp = vf610_nfc_read(nfc, NFC_IRQ_STATUS);
+
+ tmp |= CMD_DONE_CLEAR_BIT | IDLE_CLEAR_BIT;
+ vf610_nfc_write(nfc, NFC_IRQ_STATUS, tmp);
+}
+
+static void vf610_nfc_done(struct vf610_nfc *nfc)
+{
+ unsigned long timeout = msecs_to_jiffies(100);
+
+ /*
+ * Barrier is needed after this write. This write need
+ * to be done before reading the next register the first
+ * time.
+ * vf610_nfc_set implicates such a barrier by using writel
+ * to write to the register.
+ */
+ vf610_nfc_set(nfc, NFC_IRQ_STATUS, IDLE_EN_BIT);
+ vf610_nfc_set(nfc, NFC_FLASH_CMD2, START_BIT);
+
+ if (!wait_for_completion_timeout(&nfc->cmd_done, timeout))
+ dev_warn(nfc->dev, "Timeout while waiting for BUSY.\n");
+
+ vf610_nfc_clear_status(nfc);
+}
+
+static irqreturn_t vf610_nfc_irq(int irq, void *data)
+{
+ struct mtd_info *mtd = data;
+ struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+
+ vf610_nfc_clear(nfc, NFC_IRQ_STATUS, IDLE_EN_BIT);
+ complete(&nfc->cmd_done);
+
+ return IRQ_HANDLED;
+}
+
+static inline void vf610_nfc_ecc_mode(struct vf610_nfc *nfc, int ecc_mode)
+{
+ vf610_nfc_set_field(nfc, NFC_FLASH_CONFIG,
+ CONFIG_ECC_MODE_MASK,
+ CONFIG_ECC_MODE_SHIFT, ecc_mode);
+}
+
+static inline void vf610_nfc_transfer_size(struct vf610_nfc *nfc, int size)
+{
+ vf610_nfc_write(nfc, NFC_SECTOR_SIZE, size);
+}
+
+static inline void vf610_nfc_run(struct vf610_nfc *nfc, u32 col, u32 row,
+ u32 cmd1, u32 cmd2, u32 trfr_sz)
+{
+ vf610_nfc_set_field(nfc, NFC_COL_ADDR, COL_ADDR_MASK,
+ COL_ADDR_SHIFT, col);
+
+ vf610_nfc_set_field(nfc, NFC_ROW_ADDR, ROW_ADDR_MASK,
+ ROW_ADDR_SHIFT, row);
+
+ vf610_nfc_write(nfc, NFC_SECTOR_SIZE, trfr_sz);
+ vf610_nfc_write(nfc, NFC_FLASH_CMD1, cmd1);
+ vf610_nfc_write(nfc, NFC_FLASH_CMD2, cmd2);
+
+ dev_dbg(nfc->dev,
+ "col 0x%04x, row 0x%08x, cmd1 0x%08x, cmd2 0x%08x, len %d\n",
+ col, row, cmd1, cmd2, trfr_sz);
+
+ vf610_nfc_done(nfc);
+}
+
+static inline const struct nand_op_instr *
+vf610_get_next_instr(const struct nand_subop *subop, int *op_id)
+{
+ if (*op_id + 1 >= subop->ninstrs)
+ return NULL;
+
+ (*op_id)++;
+
+ return &subop->instrs[*op_id];
+}
+
+static int vf610_nfc_cmd(struct nand_chip *chip,
+ const struct nand_subop *subop)
+{
+ const struct nand_op_instr *instr;
+ struct vf610_nfc *nfc = chip_to_nfc(chip);
+ int op_id = -1, trfr_sz = 0, offset;
+ u32 col = 0, row = 0, cmd1 = 0, cmd2 = 0, code = 0;
+ bool force8bit = false;
+
+ /*
+ * Some ops are optional, but the hardware requires the operations
+ * to be in this exact order.
+ * The op parser enforces the order and makes sure that there isn't
+ * a read and write element in a single operation.
+ */
+ instr = vf610_get_next_instr(subop, &op_id);
+ if (!instr)
+ return -EINVAL;
+
+ if (instr && instr->type == NAND_OP_CMD_INSTR) {
+ cmd2 |= instr->ctx.cmd.opcode << CMD_BYTE1_SHIFT;
+ code |= COMMAND_CMD_BYTE1;
+
+ instr = vf610_get_next_instr(subop, &op_id);
+ }
+
+ if (instr && instr->type == NAND_OP_ADDR_INSTR) {
+ int naddrs = nand_subop_get_num_addr_cyc(subop, op_id);
+ int i = nand_subop_get_addr_start_off(subop, op_id);
+
+ for (; i < naddrs; i++) {
+ u8 val = instr->ctx.addr.addrs[i];
+
+ if (i < 2)
+ col |= COL_ADDR(i, val);
+ else
+ row |= ROW_ADDR(i - 2, val);
+ }
+ code |= COMMAND_NADDR_BYTES(naddrs);
+
+ instr = vf610_get_next_instr(subop, &op_id);
+ }
+
+ if (instr && instr->type == NAND_OP_DATA_OUT_INSTR) {
+ trfr_sz = nand_subop_get_data_len(subop, op_id);
+ offset = nand_subop_get_data_start_off(subop, op_id);
+ force8bit = instr->ctx.data.force_8bit;
+
+ /*
+ * Don't fix endianness on page access for historical reasons.
+ * See comment in vf610_nfc_wr_to_sram
+ */
+ vf610_nfc_wr_to_sram(nfc->regs + NFC_MAIN_AREA(0) + offset,
+ instr->ctx.data.buf.out + offset,
+ trfr_sz, !nfc->data_access);
+ code |= COMMAND_WRITE_DATA;
+
+ instr = vf610_get_next_instr(subop, &op_id);
+ }
+
+ if (instr && instr->type == NAND_OP_CMD_INSTR) {
+ cmd1 |= instr->ctx.cmd.opcode << CMD_BYTE2_SHIFT;
+ code |= COMMAND_CMD_BYTE2;
+
+ instr = vf610_get_next_instr(subop, &op_id);
+ }
+
+ if (instr && instr->type == NAND_OP_WAITRDY_INSTR) {
+ code |= COMMAND_RB_HANDSHAKE;
+
+ instr = vf610_get_next_instr(subop, &op_id);
+ }
+
+ if (instr && instr->type == NAND_OP_DATA_IN_INSTR) {
+ trfr_sz = nand_subop_get_data_len(subop, op_id);
+ offset = nand_subop_get_data_start_off(subop, op_id);
+ force8bit = instr->ctx.data.force_8bit;
+
+ code |= COMMAND_READ_DATA;
+ }
+
+ if (force8bit && (chip->options & NAND_BUSWIDTH_16))
+ vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_16BIT);
+
+ cmd2 |= code << CMD_CODE_SHIFT;
+
+ vf610_nfc_run(nfc, col, row, cmd1, cmd2, trfr_sz);
+
+ if (instr && instr->type == NAND_OP_DATA_IN_INSTR) {
+ /*
+ * Don't fix endianness on page access for historical reasons.
+ * See comment in vf610_nfc_rd_from_sram
+ */
+ vf610_nfc_rd_from_sram(instr->ctx.data.buf.in + offset,
+ nfc->regs + NFC_MAIN_AREA(0) + offset,
+ trfr_sz, !nfc->data_access);
+ }
+
+ if (force8bit && (chip->options & NAND_BUSWIDTH_16))
+ vf610_nfc_set(nfc, NFC_FLASH_CONFIG, CONFIG_16BIT);
+
+ return 0;
+}
+
+static const struct nand_op_parser vf610_nfc_op_parser = NAND_OP_PARSER(
+ NAND_OP_PARSER_PATTERN(vf610_nfc_cmd,
+ NAND_OP_PARSER_PAT_CMD_ELEM(true),
+ NAND_OP_PARSER_PAT_ADDR_ELEM(true, 5),
+ NAND_OP_PARSER_PAT_DATA_OUT_ELEM(true, PAGE_2K + OOB_MAX),
+ NAND_OP_PARSER_PAT_CMD_ELEM(true),
+ NAND_OP_PARSER_PAT_WAITRDY_ELEM(true)),
+ NAND_OP_PARSER_PATTERN(vf610_nfc_cmd,
+ NAND_OP_PARSER_PAT_CMD_ELEM(true),
+ NAND_OP_PARSER_PAT_ADDR_ELEM(true, 5),
+ NAND_OP_PARSER_PAT_CMD_ELEM(true),
+ NAND_OP_PARSER_PAT_WAITRDY_ELEM(true),
+ NAND_OP_PARSER_PAT_DATA_IN_ELEM(true, PAGE_2K + OOB_MAX)),
+ );
+
+static int vf610_nfc_exec_op(struct nand_chip *chip,
+ const struct nand_operation *op,
+ bool check_only)
+{
+ return nand_op_parser_exec_op(chip, &vf610_nfc_op_parser, op,
+ check_only);
+}
+
+/*
+ * This function supports Vybrid only (MPC5125 would have full RB and four CS)
+ */
+static void vf610_nfc_select_chip(struct mtd_info *mtd, int chip)
+{
+ struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+ u32 tmp = vf610_nfc_read(nfc, NFC_ROW_ADDR);
+
+ /* Vybrid only (MPC5125 would have full RB and four CS) */
+ if (nfc->variant != NFC_VFC610)
+ return;
+
+ tmp &= ~(ROW_ADDR_CHIP_SEL_RB_MASK | ROW_ADDR_CHIP_SEL_MASK);
+
+ if (chip >= 0) {
+ tmp |= 1 << ROW_ADDR_CHIP_SEL_RB_SHIFT;
+ tmp |= BIT(chip) << ROW_ADDR_CHIP_SEL_SHIFT;
+ }
+
+ vf610_nfc_write(nfc, NFC_ROW_ADDR, tmp);
+}
+
+static inline int vf610_nfc_correct_data(struct mtd_info *mtd, uint8_t *dat,
+ uint8_t *oob, int page)
+{
+ struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+ u32 ecc_status_off = NFC_MAIN_AREA(0) + ECC_SRAM_ADDR + ECC_STATUS;
+ u8 ecc_status;
+ u8 ecc_count;
+ int flips_threshold = nfc->chip.ecc.strength / 2;
+
+ ecc_status = vf610_nfc_read(nfc, ecc_status_off) & 0xff;
+ ecc_count = ecc_status & ECC_STATUS_ERR_COUNT;
+
+ if (!(ecc_status & ECC_STATUS_MASK))
+ return ecc_count;
+
+ nfc->data_access = true;
+ nand_read_oob_op(&nfc->chip, page, 0, oob, mtd->oobsize);
+ nfc->data_access = false;
+
+ /*
+ * On an erased page, bit count (including OOB) should be zero or
+ * at least less then half of the ECC strength.
+ */
+ return nand_check_erased_ecc_chunk(dat, nfc->chip.ecc.size, oob,
+ mtd->oobsize, NULL, 0,
+ flips_threshold);
+}
+
+static void vf610_nfc_fill_row(struct nand_chip *chip, int page, u32 *code,
+ u32 *row)
+{
+ *row = ROW_ADDR(0, page & 0xff) | ROW_ADDR(1, page >> 8);
+ *code |= COMMAND_RAR_BYTE1 | COMMAND_RAR_BYTE2;
+
+ if (chip->options & NAND_ROW_ADDR_3) {
+ *row |= ROW_ADDR(2, page >> 16);
+ *code |= COMMAND_RAR_BYTE3;
+ }
+}
+
+static int vf610_nfc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+ int trfr_sz = mtd->writesize + mtd->oobsize;
+ u32 row = 0, cmd1 = 0, cmd2 = 0, code = 0;
+ int stat;
+
+ cmd2 |= NAND_CMD_READ0 << CMD_BYTE1_SHIFT;
+ code |= COMMAND_CMD_BYTE1 | COMMAND_CAR_BYTE1 | COMMAND_CAR_BYTE2;
+
+ vf610_nfc_fill_row(chip, page, &code, &row);
+
+ cmd1 |= NAND_CMD_READSTART << CMD_BYTE2_SHIFT;
+ code |= COMMAND_CMD_BYTE2 | COMMAND_RB_HANDSHAKE | COMMAND_READ_DATA;
+
+ cmd2 |= code << CMD_CODE_SHIFT;
+
+ vf610_nfc_ecc_mode(nfc, nfc->ecc_mode);
+ vf610_nfc_run(nfc, 0, row, cmd1, cmd2, trfr_sz);
+ vf610_nfc_ecc_mode(nfc, ECC_BYPASS);
+
+ /*
+ * Don't fix endianness on page access for historical reasons.
+ * See comment in vf610_nfc_rd_from_sram
+ */
+ vf610_nfc_rd_from_sram(buf, nfc->regs + NFC_MAIN_AREA(0),
+ mtd->writesize, false);
+ if (oob_required)
+ vf610_nfc_rd_from_sram(chip->oob_poi,
+ nfc->regs + NFC_MAIN_AREA(0) +
+ mtd->writesize,
+ mtd->oobsize, false);
+
+ stat = vf610_nfc_correct_data(mtd, buf, chip->oob_poi, page);
+
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ return 0;
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ return stat;
+ }
+}
+
+static int vf610_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required, int page)
+{
+ struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+ int trfr_sz = mtd->writesize + mtd->oobsize;
+ u32 row = 0, cmd1 = 0, cmd2 = 0, code = 0;
+ u8 status;
+ int ret;
+
+ cmd2 |= NAND_CMD_SEQIN << CMD_BYTE1_SHIFT;
+ code |= COMMAND_CMD_BYTE1 | COMMAND_CAR_BYTE1 | COMMAND_CAR_BYTE2;
+
+ vf610_nfc_fill_row(chip, page, &code, &row);
+
+ cmd1 |= NAND_CMD_PAGEPROG << CMD_BYTE2_SHIFT;
+ code |= COMMAND_CMD_BYTE2 | COMMAND_WRITE_DATA;
+
+ /*
+ * Don't fix endianness on page access for historical reasons.
+ * See comment in vf610_nfc_wr_to_sram
+ */
+ vf610_nfc_wr_to_sram(nfc->regs + NFC_MAIN_AREA(0), buf,
+ mtd->writesize, false);
+
+ code |= COMMAND_RB_HANDSHAKE;
+ cmd2 |= code << CMD_CODE_SHIFT;
+
+ vf610_nfc_ecc_mode(nfc, nfc->ecc_mode);
+ vf610_nfc_run(nfc, 0, row, cmd1, cmd2, trfr_sz);
+ vf610_nfc_ecc_mode(nfc, ECC_BYPASS);
+
+ ret = nand_status_op(chip, &status);
+ if (ret)
+ return ret;
+
+ if (status & NAND_STATUS_FAIL)
+ return -EIO;
+
+ return 0;
+}
+
+static int vf610_nfc_read_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, u8 *buf,
+ int oob_required, int page)
+{
+ struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+ int ret;
+
+ nfc->data_access = true;
+ ret = nand_read_page_raw(mtd, chip, buf, oob_required, page);
+ nfc->data_access = false;
+
+ return ret;
+}
+
+static int vf610_nfc_write_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, const u8 *buf,
+ int oob_required, int page)
+{
+ struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+ int ret;
+
+ nfc->data_access = true;
+ ret = nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);
+ if (!ret && oob_required)
+ ret = nand_write_data_op(chip, chip->oob_poi, mtd->oobsize,
+ false);
+ nfc->data_access = false;
+
+ if (ret)
+ return ret;
+
+ return nand_prog_page_end_op(chip);
+}
+
+static int vf610_nfc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+ int ret;
+
+ nfc->data_access = true;
+ ret = nand_read_oob_std(mtd, chip, page);
+ nfc->data_access = false;
+
+ return ret;
+}
+
+static int vf610_nfc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+ int ret;
+
+ nfc->data_access = true;
+ ret = nand_prog_page_begin_op(chip, page, mtd->writesize,
+ chip->oob_poi, mtd->oobsize);
+ nfc->data_access = false;
+
+ if (ret)
+ return ret;
+
+ return nand_prog_page_end_op(chip);
+}
+
+static const struct of_device_id vf610_nfc_dt_ids[] = {
+ { .compatible = "fsl,vf610-nfc", .data = (void *)NFC_VFC610 },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, vf610_nfc_dt_ids);
+
+static void vf610_nfc_preinit_controller(struct vf610_nfc *nfc)
+{
+ vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_16BIT);
+ vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_ADDR_AUTO_INCR_BIT);
+ vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_BUFNO_AUTO_INCR_BIT);
+ vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_BOOT_MODE_BIT);
+ vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_DMA_REQ_BIT);
+ vf610_nfc_set(nfc, NFC_FLASH_CONFIG, CONFIG_FAST_FLASH_BIT);
+ vf610_nfc_ecc_mode(nfc, ECC_BYPASS);
+
+ /* Disable virtual pages, only one elementary transfer unit */
+ vf610_nfc_set_field(nfc, NFC_FLASH_CONFIG, CONFIG_PAGE_CNT_MASK,
+ CONFIG_PAGE_CNT_SHIFT, 1);
+}
+
+static void vf610_nfc_init_controller(struct vf610_nfc *nfc)
+{
+ if (nfc->chip.options & NAND_BUSWIDTH_16)
+ vf610_nfc_set(nfc, NFC_FLASH_CONFIG, CONFIG_16BIT);
+ else
+ vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_16BIT);
+
+ if (nfc->chip.ecc.mode == NAND_ECC_HW) {
+ /* Set ECC status offset in SRAM */
+ vf610_nfc_set_field(nfc, NFC_FLASH_CONFIG,
+ CONFIG_ECC_SRAM_ADDR_MASK,
+ CONFIG_ECC_SRAM_ADDR_SHIFT,
+ ECC_SRAM_ADDR >> 3);
+
+ /* Enable ECC status in SRAM */
+ vf610_nfc_set(nfc, NFC_FLASH_CONFIG, CONFIG_ECC_SRAM_REQ_BIT);
+ }
+}
+
+static int vf610_nfc_probe(struct platform_device *pdev)
+{
+ struct vf610_nfc *nfc;
+ struct resource *res;
+ struct mtd_info *mtd;
+ struct nand_chip *chip;
+ struct device_node *child;
+ const struct of_device_id *of_id;
+ int err;
+ int irq;
+
+ nfc = devm_kzalloc(&pdev->dev, sizeof(*nfc), GFP_KERNEL);
+ if (!nfc)
+ return -ENOMEM;
+
+ nfc->dev = &pdev->dev;
+ chip = &nfc->chip;
+ mtd = nand_to_mtd(chip);
+
+ mtd->owner = THIS_MODULE;
+ mtd->dev.parent = nfc->dev;
+ mtd->name = DRV_NAME;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0)
+ return -EINVAL;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ nfc->regs = devm_ioremap_resource(nfc->dev, res);
+ if (IS_ERR(nfc->regs))
+ return PTR_ERR(nfc->regs);
+
+ nfc->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(nfc->clk))
+ return PTR_ERR(nfc->clk);
+
+ err = clk_prepare_enable(nfc->clk);
+ if (err) {
+ dev_err(nfc->dev, "Unable to enable clock!\n");
+ return err;
+ }
+
+ of_id = of_match_device(vf610_nfc_dt_ids, &pdev->dev);
+ nfc->variant = (enum vf610_nfc_variant)of_id->data;
+
+ for_each_available_child_of_node(nfc->dev->of_node, child) {
+ if (of_device_is_compatible(child, "fsl,vf610-nfc-nandcs")) {
+
+ if (nand_get_flash_node(chip)) {
+ dev_err(nfc->dev,
+ "Only one NAND chip supported!\n");
+ err = -EINVAL;
+ goto err_disable_clk;
+ }
+
+ nand_set_flash_node(chip, child);
+ }
+ }
+
+ if (!nand_get_flash_node(chip)) {
+ dev_err(nfc->dev, "NAND chip sub-node missing!\n");
+ err = -ENODEV;
+ goto err_disable_clk;
+ }
+
+ chip->exec_op = vf610_nfc_exec_op;
+ chip->select_chip = vf610_nfc_select_chip;
+
+ chip->options |= NAND_NO_SUBPAGE_WRITE;
+
+ init_completion(&nfc->cmd_done);
+
+ err = devm_request_irq(nfc->dev, irq, vf610_nfc_irq, 0, DRV_NAME, mtd);
+ if (err) {
+ dev_err(nfc->dev, "Error requesting IRQ!\n");
+ goto err_disable_clk;
+ }
+
+ vf610_nfc_preinit_controller(nfc);
+
+ /* first scan to find the device and get the page size */
+ err = nand_scan_ident(mtd, 1, NULL);
+ if (err)
+ goto err_disable_clk;
+
+ vf610_nfc_init_controller(nfc);
+
+ /* Bad block options. */
+ if (chip->bbt_options & NAND_BBT_USE_FLASH)
+ chip->bbt_options |= NAND_BBT_NO_OOB;
+
+ /* Single buffer only, max 256 OOB minus ECC status */
+ if (mtd->writesize + mtd->oobsize > PAGE_2K + OOB_MAX - 8) {
+ dev_err(nfc->dev, "Unsupported flash page size\n");
+ err = -ENXIO;
+ goto err_disable_clk;
+ }
+
+ if (chip->ecc.mode == NAND_ECC_HW) {
+ if (mtd->writesize != PAGE_2K && mtd->oobsize < 64) {
+ dev_err(nfc->dev, "Unsupported flash with hwecc\n");
+ err = -ENXIO;
+ goto err_disable_clk;
+ }
+
+ if (chip->ecc.size != mtd->writesize) {
+ dev_err(nfc->dev, "Step size needs to be page size\n");
+ err = -ENXIO;
+ goto err_disable_clk;
+ }
+
+ /* Only 64 byte ECC layouts known */
+ if (mtd->oobsize > 64)
+ mtd->oobsize = 64;
+
+ /* Use default large page ECC layout defined in NAND core */
+ mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
+ if (chip->ecc.strength == 32) {
+ nfc->ecc_mode = ECC_60_BYTE;
+ chip->ecc.bytes = 60;
+ } else if (chip->ecc.strength == 24) {
+ nfc->ecc_mode = ECC_45_BYTE;
+ chip->ecc.bytes = 45;
+ } else {
+ dev_err(nfc->dev, "Unsupported ECC strength\n");
+ err = -ENXIO;
+ goto err_disable_clk;
+ }
+
+ chip->ecc.read_page = vf610_nfc_read_page;
+ chip->ecc.write_page = vf610_nfc_write_page;
+ chip->ecc.read_page_raw = vf610_nfc_read_page_raw;
+ chip->ecc.write_page_raw = vf610_nfc_write_page_raw;
+ chip->ecc.read_oob = vf610_nfc_read_oob;
+ chip->ecc.write_oob = vf610_nfc_write_oob;
+
+ chip->ecc.size = PAGE_2K;
+ }
+
+ /* second phase scan */
+ err = nand_scan_tail(mtd);
+ if (err)
+ goto err_disable_clk;
+
+ platform_set_drvdata(pdev, mtd);
+
+ /* Register device in MTD */
+ err = mtd_device_register(mtd, NULL, 0);
+ if (err)
+ goto err_cleanup_nand;
+ return 0;
+
+err_cleanup_nand:
+ nand_cleanup(chip);
+err_disable_clk:
+ clk_disable_unprepare(nfc->clk);
+ return err;
+}
+
+static int vf610_nfc_remove(struct platform_device *pdev)
+{
+ struct mtd_info *mtd = platform_get_drvdata(pdev);
+ struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+
+ nand_release(mtd);
+ clk_disable_unprepare(nfc->clk);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int vf610_nfc_suspend(struct device *dev)
+{
+ struct mtd_info *mtd = dev_get_drvdata(dev);
+ struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+
+ clk_disable_unprepare(nfc->clk);
+ return 0;
+}
+
+static int vf610_nfc_resume(struct device *dev)
+{
+ int err;
+
+ struct mtd_info *mtd = dev_get_drvdata(dev);
+ struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+
+ err = clk_prepare_enable(nfc->clk);
+ if (err)
+ return err;
+
+ vf610_nfc_preinit_controller(nfc);
+ vf610_nfc_init_controller(nfc);
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(vf610_nfc_pm_ops, vf610_nfc_suspend, vf610_nfc_resume);
+
+static struct platform_driver vf610_nfc_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = vf610_nfc_dt_ids,
+ .pm = &vf610_nfc_pm_ops,
+ },
+ .probe = vf610_nfc_probe,
+ .remove = vf610_nfc_remove,
+};
+
+module_platform_driver(vf610_nfc_driver);
+
+MODULE_AUTHOR("Stefan Agner <stefan.agner@toradex.com>");
+MODULE_DESCRIPTION("Freescale VF610/MPC5125 NFC MTD NAND driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/nand/xway_nand.c b/drivers/mtd/nand/raw/xway_nand.c
index 9926b4e3d69d..9926b4e3d69d 100644
--- a/drivers/mtd/nand/xway_nand.c
+++ b/drivers/mtd/nand/raw/xway_nand.c
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
deleted file mode 100644
index 4c383eeec6f6..000000000000
--- a/drivers/mtd/nand/s3c2410.c
+++ /dev/null
@@ -1,1296 +0,0 @@
-/* linux/drivers/mtd/nand/s3c2410.c
- *
- * Copyright © 2004-2008 Simtec Electronics
- * http://armlinux.simtec.co.uk/
- * Ben Dooks <ben@simtec.co.uk>
- *
- * Samsung S3C2410/S3C2440/S3C2412 NAND driver
- *
- * 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
-*/
-
-#define pr_fmt(fmt) "nand-s3c2410: " fmt
-
-#ifdef CONFIG_MTD_NAND_S3C2410_DEBUG
-#define DEBUG
-#endif
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/io.h>
-#include <linux/ioport.h>
-#include <linux/platform_device.h>
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/slab.h>
-#include <linux/clk.h>
-#include <linux/cpufreq.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand_ecc.h>
-#include <linux/mtd/partitions.h>
-
-#include <linux/platform_data/mtd-nand-s3c2410.h>
-
-#define S3C2410_NFREG(x) (x)
-
-#define S3C2410_NFCONF S3C2410_NFREG(0x00)
-#define S3C2410_NFCMD S3C2410_NFREG(0x04)
-#define S3C2410_NFADDR S3C2410_NFREG(0x08)
-#define S3C2410_NFDATA S3C2410_NFREG(0x0C)
-#define S3C2410_NFSTAT S3C2410_NFREG(0x10)
-#define S3C2410_NFECC S3C2410_NFREG(0x14)
-#define S3C2440_NFCONT S3C2410_NFREG(0x04)
-#define S3C2440_NFCMD S3C2410_NFREG(0x08)
-#define S3C2440_NFADDR S3C2410_NFREG(0x0C)
-#define S3C2440_NFDATA S3C2410_NFREG(0x10)
-#define S3C2440_NFSTAT S3C2410_NFREG(0x20)
-#define S3C2440_NFMECC0 S3C2410_NFREG(0x2C)
-#define S3C2412_NFSTAT S3C2410_NFREG(0x28)
-#define S3C2412_NFMECC0 S3C2410_NFREG(0x34)
-#define S3C2410_NFCONF_EN (1<<15)
-#define S3C2410_NFCONF_INITECC (1<<12)
-#define S3C2410_NFCONF_nFCE (1<<11)
-#define S3C2410_NFCONF_TACLS(x) ((x)<<8)
-#define S3C2410_NFCONF_TWRPH0(x) ((x)<<4)
-#define S3C2410_NFCONF_TWRPH1(x) ((x)<<0)
-#define S3C2410_NFSTAT_BUSY (1<<0)
-#define S3C2440_NFCONF_TACLS(x) ((x)<<12)
-#define S3C2440_NFCONF_TWRPH0(x) ((x)<<8)
-#define S3C2440_NFCONF_TWRPH1(x) ((x)<<4)
-#define S3C2440_NFCONT_INITECC (1<<4)
-#define S3C2440_NFCONT_nFCE (1<<1)
-#define S3C2440_NFCONT_ENABLE (1<<0)
-#define S3C2440_NFSTAT_READY (1<<0)
-#define S3C2412_NFCONF_NANDBOOT (1<<31)
-#define S3C2412_NFCONT_INIT_MAIN_ECC (1<<5)
-#define S3C2412_NFCONT_nFCE0 (1<<1)
-#define S3C2412_NFSTAT_READY (1<<0)
-
-/* new oob placement block for use with hardware ecc generation
- */
-static int s3c2410_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- if (section)
- return -ERANGE;
-
- oobregion->offset = 0;
- oobregion->length = 3;
-
- return 0;
-}
-
-static int s3c2410_ooblayout_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- if (section)
- return -ERANGE;
-
- oobregion->offset = 8;
- oobregion->length = 8;
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops s3c2410_ooblayout_ops = {
- .ecc = s3c2410_ooblayout_ecc,
- .free = s3c2410_ooblayout_free,
-};
-
-/* controller and mtd information */
-
-struct s3c2410_nand_info;
-
-/**
- * struct s3c2410_nand_mtd - driver MTD structure
- * @mtd: The MTD instance to pass to the MTD layer.
- * @chip: The NAND chip information.
- * @set: The platform information supplied for this set of NAND chips.
- * @info: Link back to the hardware information.
- * @scan_res: The result from calling nand_scan_ident().
-*/
-struct s3c2410_nand_mtd {
- struct nand_chip chip;
- struct s3c2410_nand_set *set;
- struct s3c2410_nand_info *info;
- int scan_res;
-};
-
-enum s3c_cpu_type {
- TYPE_S3C2410,
- TYPE_S3C2412,
- TYPE_S3C2440,
-};
-
-enum s3c_nand_clk_state {
- CLOCK_DISABLE = 0,
- CLOCK_ENABLE,
- CLOCK_SUSPEND,
-};
-
-/* overview of the s3c2410 nand state */
-
-/**
- * struct s3c2410_nand_info - NAND controller state.
- * @mtds: An array of MTD instances on this controoler.
- * @platform: The platform data for this board.
- * @device: The platform device we bound to.
- * @clk: The clock resource for this controller.
- * @regs: The area mapped for the hardware registers.
- * @sel_reg: Pointer to the register controlling the NAND selection.
- * @sel_bit: The bit in @sel_reg to select the NAND chip.
- * @mtd_count: The number of MTDs created from this controller.
- * @save_sel: The contents of @sel_reg to be saved over suspend.
- * @clk_rate: The clock rate from @clk.
- * @clk_state: The current clock state.
- * @cpu_type: The exact type of this controller.
- */
-struct s3c2410_nand_info {
- /* mtd info */
- struct nand_hw_control controller;
- struct s3c2410_nand_mtd *mtds;
- struct s3c2410_platform_nand *platform;
-
- /* device info */
- struct device *device;
- struct clk *clk;
- void __iomem *regs;
- void __iomem *sel_reg;
- int sel_bit;
- int mtd_count;
- unsigned long save_sel;
- unsigned long clk_rate;
- enum s3c_nand_clk_state clk_state;
-
- enum s3c_cpu_type cpu_type;
-
-#ifdef CONFIG_ARM_S3C24XX_CPUFREQ
- struct notifier_block freq_transition;
-#endif
-};
-
-struct s3c24XX_nand_devtype_data {
- enum s3c_cpu_type type;
-};
-
-static const struct s3c24XX_nand_devtype_data s3c2410_nand_devtype_data = {
- .type = TYPE_S3C2410,
-};
-
-static const struct s3c24XX_nand_devtype_data s3c2412_nand_devtype_data = {
- .type = TYPE_S3C2412,
-};
-
-static const struct s3c24XX_nand_devtype_data s3c2440_nand_devtype_data = {
- .type = TYPE_S3C2440,
-};
-
-/* conversion functions */
-
-static struct s3c2410_nand_mtd *s3c2410_nand_mtd_toours(struct mtd_info *mtd)
-{
- return container_of(mtd_to_nand(mtd), struct s3c2410_nand_mtd,
- chip);
-}
-
-static struct s3c2410_nand_info *s3c2410_nand_mtd_toinfo(struct mtd_info *mtd)
-{
- return s3c2410_nand_mtd_toours(mtd)->info;
-}
-
-static struct s3c2410_nand_info *to_nand_info(struct platform_device *dev)
-{
- return platform_get_drvdata(dev);
-}
-
-static struct s3c2410_platform_nand *to_nand_plat(struct platform_device *dev)
-{
- return dev_get_platdata(&dev->dev);
-}
-
-static inline int allow_clk_suspend(struct s3c2410_nand_info *info)
-{
-#ifdef CONFIG_MTD_NAND_S3C2410_CLKSTOP
- return 1;
-#else
- return 0;
-#endif
-}
-
-/**
- * s3c2410_nand_clk_set_state - Enable, disable or suspend NAND clock.
- * @info: The controller instance.
- * @new_state: State to which clock should be set.
- */
-static void s3c2410_nand_clk_set_state(struct s3c2410_nand_info *info,
- enum s3c_nand_clk_state new_state)
-{
- if (!allow_clk_suspend(info) && new_state == CLOCK_SUSPEND)
- return;
-
- if (info->clk_state == CLOCK_ENABLE) {
- if (new_state != CLOCK_ENABLE)
- clk_disable_unprepare(info->clk);
- } else {
- if (new_state == CLOCK_ENABLE)
- clk_prepare_enable(info->clk);
- }
-
- info->clk_state = new_state;
-}
-
-/* timing calculations */
-
-#define NS_IN_KHZ 1000000
-
-/**
- * s3c_nand_calc_rate - calculate timing data.
- * @wanted: The cycle time in nanoseconds.
- * @clk: The clock rate in kHz.
- * @max: The maximum divider value.
- *
- * Calculate the timing value from the given parameters.
- */
-static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max)
-{
- int result;
-
- result = DIV_ROUND_UP((wanted * clk), NS_IN_KHZ);
-
- pr_debug("result %d from %ld, %d\n", result, clk, wanted);
-
- if (result > max) {
- pr_err("%d ns is too big for current clock rate %ld\n",
- wanted, clk);
- return -1;
- }
-
- if (result < 1)
- result = 1;
-
- return result;
-}
-
-#define to_ns(ticks, clk) (((ticks) * NS_IN_KHZ) / (unsigned int)(clk))
-
-/* controller setup */
-
-/**
- * s3c2410_nand_setrate - setup controller timing information.
- * @info: The controller instance.
- *
- * Given the information supplied by the platform, calculate and set
- * the necessary timing registers in the hardware to generate the
- * necessary timing cycles to the hardware.
- */
-static int s3c2410_nand_setrate(struct s3c2410_nand_info *info)
-{
- struct s3c2410_platform_nand *plat = info->platform;
- int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4;
- int tacls, twrph0, twrph1;
- unsigned long clkrate = clk_get_rate(info->clk);
- unsigned long uninitialized_var(set), cfg, uninitialized_var(mask);
- unsigned long flags;
-
- /* calculate the timing information for the controller */
-
- info->clk_rate = clkrate;
- clkrate /= 1000; /* turn clock into kHz for ease of use */
-
- if (plat != NULL) {
- tacls = s3c_nand_calc_rate(plat->tacls, clkrate, tacls_max);
- twrph0 = s3c_nand_calc_rate(plat->twrph0, clkrate, 8);
- twrph1 = s3c_nand_calc_rate(plat->twrph1, clkrate, 8);
- } else {
- /* default timings */
- tacls = tacls_max;
- twrph0 = 8;
- twrph1 = 8;
- }
-
- if (tacls < 0 || twrph0 < 0 || twrph1 < 0) {
- dev_err(info->device, "cannot get suitable timings\n");
- return -EINVAL;
- }
-
- dev_info(info->device, "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n",
- tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate),
- twrph1, to_ns(twrph1, clkrate));
-
- switch (info->cpu_type) {
- case TYPE_S3C2410:
- mask = (S3C2410_NFCONF_TACLS(3) |
- S3C2410_NFCONF_TWRPH0(7) |
- S3C2410_NFCONF_TWRPH1(7));
- set = S3C2410_NFCONF_EN;
- set |= S3C2410_NFCONF_TACLS(tacls - 1);
- set |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
- set |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
- break;
-
- case TYPE_S3C2440:
- case TYPE_S3C2412:
- mask = (S3C2440_NFCONF_TACLS(tacls_max - 1) |
- S3C2440_NFCONF_TWRPH0(7) |
- S3C2440_NFCONF_TWRPH1(7));
-
- set = S3C2440_NFCONF_TACLS(tacls - 1);
- set |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
- set |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);
- break;
-
- default:
- BUG();
- }
-
- local_irq_save(flags);
-
- cfg = readl(info->regs + S3C2410_NFCONF);
- cfg &= ~mask;
- cfg |= set;
- writel(cfg, info->regs + S3C2410_NFCONF);
-
- local_irq_restore(flags);
-
- dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg);
-
- return 0;
-}
-
-/**
- * s3c2410_nand_inithw - basic hardware initialisation
- * @info: The hardware state.
- *
- * Do the basic initialisation of the hardware, using s3c2410_nand_setrate()
- * to setup the hardware access speeds and set the controller to be enabled.
-*/
-static int s3c2410_nand_inithw(struct s3c2410_nand_info *info)
-{
- int ret;
-
- ret = s3c2410_nand_setrate(info);
- if (ret < 0)
- return ret;
-
- switch (info->cpu_type) {
- case TYPE_S3C2410:
- default:
- break;
-
- case TYPE_S3C2440:
- case TYPE_S3C2412:
- /* enable the controller and de-assert nFCE */
-
- writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT);
- }
-
- return 0;
-}
-
-/**
- * s3c2410_nand_select_chip - select the given nand chip
- * @mtd: The MTD instance for this chip.
- * @chip: The chip number.
- *
- * This is called by the MTD layer to either select a given chip for the
- * @mtd instance, or to indicate that the access has finished and the
- * chip can be de-selected.
- *
- * The routine ensures that the nFCE line is correctly setup, and any
- * platform specific selection code is called to route nFCE to the specific
- * chip.
- */
-static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
-{
- struct s3c2410_nand_info *info;
- struct s3c2410_nand_mtd *nmtd;
- struct nand_chip *this = mtd_to_nand(mtd);
- unsigned long cur;
-
- nmtd = nand_get_controller_data(this);
- info = nmtd->info;
-
- if (chip != -1)
- s3c2410_nand_clk_set_state(info, CLOCK_ENABLE);
-
- cur = readl(info->sel_reg);
-
- if (chip == -1) {
- cur |= info->sel_bit;
- } else {
- if (nmtd->set != NULL && chip > nmtd->set->nr_chips) {
- dev_err(info->device, "invalid chip %d\n", chip);
- return;
- }
-
- if (info->platform != NULL) {
- if (info->platform->select_chip != NULL)
- (info->platform->select_chip) (nmtd->set, chip);
- }
-
- cur &= ~info->sel_bit;
- }
-
- writel(cur, info->sel_reg);
-
- if (chip == -1)
- s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND);
-}
-
-/* s3c2410_nand_hwcontrol
- *
- * Issue command and address cycles to the chip
-*/
-
-static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd,
- unsigned int ctrl)
-{
- struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
-
- if (cmd == NAND_CMD_NONE)
- return;
-
- if (ctrl & NAND_CLE)
- writeb(cmd, info->regs + S3C2410_NFCMD);
- else
- writeb(cmd, info->regs + S3C2410_NFADDR);
-}
-
-/* command and control functions */
-
-static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd,
- unsigned int ctrl)
-{
- struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
-
- if (cmd == NAND_CMD_NONE)
- return;
-
- if (ctrl & NAND_CLE)
- writeb(cmd, info->regs + S3C2440_NFCMD);
- else
- writeb(cmd, info->regs + S3C2440_NFADDR);
-}
-
-/* s3c2410_nand_devready()
- *
- * returns 0 if the nand is busy, 1 if it is ready
-*/
-
-static int s3c2410_nand_devready(struct mtd_info *mtd)
-{
- struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
- return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY;
-}
-
-static int s3c2440_nand_devready(struct mtd_info *mtd)
-{
- struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
- return readb(info->regs + S3C2440_NFSTAT) & S3C2440_NFSTAT_READY;
-}
-
-static int s3c2412_nand_devready(struct mtd_info *mtd)
-{
- struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
- return readb(info->regs + S3C2412_NFSTAT) & S3C2412_NFSTAT_READY;
-}
-
-/* ECC handling functions */
-
-static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
- u_char *read_ecc, u_char *calc_ecc)
-{
- struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
- unsigned int diff0, diff1, diff2;
- unsigned int bit, byte;
-
- pr_debug("%s(%p,%p,%p,%p)\n", __func__, mtd, dat, read_ecc, calc_ecc);
-
- diff0 = read_ecc[0] ^ calc_ecc[0];
- diff1 = read_ecc[1] ^ calc_ecc[1];
- diff2 = read_ecc[2] ^ calc_ecc[2];
-
- pr_debug("%s: rd %*phN calc %*phN diff %02x%02x%02x\n",
- __func__, 3, read_ecc, 3, calc_ecc,
- diff0, diff1, diff2);
-
- if (diff0 == 0 && diff1 == 0 && diff2 == 0)
- return 0; /* ECC is ok */
-
- /* sometimes people do not think about using the ECC, so check
- * to see if we have an 0xff,0xff,0xff read ECC and then ignore
- * the error, on the assumption that this is an un-eccd page.
- */
- if (read_ecc[0] == 0xff && read_ecc[1] == 0xff && read_ecc[2] == 0xff
- && info->platform->ignore_unset_ecc)
- return 0;
-
- /* Can we correct this ECC (ie, one row and column change).
- * Note, this is similar to the 256 error code on smartmedia */
-
- if (((diff0 ^ (diff0 >> 1)) & 0x55) == 0x55 &&
- ((diff1 ^ (diff1 >> 1)) & 0x55) == 0x55 &&
- ((diff2 ^ (diff2 >> 1)) & 0x55) == 0x55) {
- /* calculate the bit position of the error */
-
- bit = ((diff2 >> 3) & 1) |
- ((diff2 >> 4) & 2) |
- ((diff2 >> 5) & 4);
-
- /* calculate the byte position of the error */
-
- byte = ((diff2 << 7) & 0x100) |
- ((diff1 << 0) & 0x80) |
- ((diff1 << 1) & 0x40) |
- ((diff1 << 2) & 0x20) |
- ((diff1 << 3) & 0x10) |
- ((diff0 >> 4) & 0x08) |
- ((diff0 >> 3) & 0x04) |
- ((diff0 >> 2) & 0x02) |
- ((diff0 >> 1) & 0x01);
-
- dev_dbg(info->device, "correcting error bit %d, byte %d\n",
- bit, byte);
-
- dat[byte] ^= (1 << bit);
- return 1;
- }
-
- /* if there is only one bit difference in the ECC, then
- * one of only a row or column parity has changed, which
- * means the error is most probably in the ECC itself */
-
- diff0 |= (diff1 << 8);
- diff0 |= (diff2 << 16);
-
- /* equal to "(diff0 & ~(1 << __ffs(diff0)))" */
- if ((diff0 & (diff0 - 1)) == 0)
- return 1;
-
- return -1;
-}
-
-/* ECC functions
- *
- * These allow the s3c2410 and s3c2440 to use the controller's ECC
- * generator block to ECC the data as it passes through]
-*/
-
-static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
-{
- struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
- unsigned long ctrl;
-
- ctrl = readl(info->regs + S3C2410_NFCONF);
- ctrl |= S3C2410_NFCONF_INITECC;
- writel(ctrl, info->regs + S3C2410_NFCONF);
-}
-
-static void s3c2412_nand_enable_hwecc(struct mtd_info *mtd, int mode)
-{
- struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
- unsigned long ctrl;
-
- ctrl = readl(info->regs + S3C2440_NFCONT);
- writel(ctrl | S3C2412_NFCONT_INIT_MAIN_ECC,
- info->regs + S3C2440_NFCONT);
-}
-
-static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode)
-{
- struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
- unsigned long ctrl;
-
- ctrl = readl(info->regs + S3C2440_NFCONT);
- writel(ctrl | S3C2440_NFCONT_INITECC, info->regs + S3C2440_NFCONT);
-}
-
-static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
- u_char *ecc_code)
-{
- struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
-
- ecc_code[0] = readb(info->regs + S3C2410_NFECC + 0);
- ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1);
- ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2);
-
- pr_debug("%s: returning ecc %*phN\n", __func__, 3, ecc_code);
-
- return 0;
-}
-
-static int s3c2412_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
- u_char *ecc_code)
-{
- struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
- unsigned long ecc = readl(info->regs + S3C2412_NFMECC0);
-
- ecc_code[0] = ecc;
- ecc_code[1] = ecc >> 8;
- ecc_code[2] = ecc >> 16;
-
- pr_debug("%s: returning ecc %*phN\n", __func__, 3, ecc_code);
-
- return 0;
-}
-
-static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
- u_char *ecc_code)
-{
- struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
- unsigned long ecc = readl(info->regs + S3C2440_NFMECC0);
-
- ecc_code[0] = ecc;
- ecc_code[1] = ecc >> 8;
- ecc_code[2] = ecc >> 16;
-
- pr_debug("%s: returning ecc %06lx\n", __func__, ecc & 0xffffff);
-
- return 0;
-}
-
-/* over-ride the standard functions for a little more speed. We can
- * use read/write block to move the data buffers to/from the controller
-*/
-
-static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- readsb(this->IO_ADDR_R, buf, len);
-}
-
-static void s3c2440_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
-{
- struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
-
- readsl(info->regs + S3C2440_NFDATA, buf, len >> 2);
-
- /* cleanup if we've got less than a word to do */
- if (len & 3) {
- buf += len & ~3;
-
- for (; len & 3; len--)
- *buf++ = readb(info->regs + S3C2440_NFDATA);
- }
-}
-
-static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf,
- int len)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- writesb(this->IO_ADDR_W, buf, len);
-}
-
-static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf,
- int len)
-{
- struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
-
- writesl(info->regs + S3C2440_NFDATA, buf, len >> 2);
-
- /* cleanup any fractional write */
- if (len & 3) {
- buf += len & ~3;
-
- for (; len & 3; len--, buf++)
- writeb(*buf, info->regs + S3C2440_NFDATA);
- }
-}
-
-/* cpufreq driver support */
-
-#ifdef CONFIG_ARM_S3C24XX_CPUFREQ
-
-static int s3c2410_nand_cpufreq_transition(struct notifier_block *nb,
- unsigned long val, void *data)
-{
- struct s3c2410_nand_info *info;
- unsigned long newclk;
-
- info = container_of(nb, struct s3c2410_nand_info, freq_transition);
- newclk = clk_get_rate(info->clk);
-
- if ((val == CPUFREQ_POSTCHANGE && newclk < info->clk_rate) ||
- (val == CPUFREQ_PRECHANGE && newclk > info->clk_rate)) {
- s3c2410_nand_setrate(info);
- }
-
- return 0;
-}
-
-static inline int s3c2410_nand_cpufreq_register(struct s3c2410_nand_info *info)
-{
- info->freq_transition.notifier_call = s3c2410_nand_cpufreq_transition;
-
- return cpufreq_register_notifier(&info->freq_transition,
- CPUFREQ_TRANSITION_NOTIFIER);
-}
-
-static inline void
-s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info)
-{
- cpufreq_unregister_notifier(&info->freq_transition,
- CPUFREQ_TRANSITION_NOTIFIER);
-}
-
-#else
-static inline int s3c2410_nand_cpufreq_register(struct s3c2410_nand_info *info)
-{
- return 0;
-}
-
-static inline void
-s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info)
-{
-}
-#endif
-
-/* device management functions */
-
-static int s3c24xx_nand_remove(struct platform_device *pdev)
-{
- struct s3c2410_nand_info *info = to_nand_info(pdev);
-
- if (info == NULL)
- return 0;
-
- s3c2410_nand_cpufreq_deregister(info);
-
- /* Release all our mtds and their partitions, then go through
- * freeing the resources used
- */
-
- if (info->mtds != NULL) {
- struct s3c2410_nand_mtd *ptr = info->mtds;
- int mtdno;
-
- for (mtdno = 0; mtdno < info->mtd_count; mtdno++, ptr++) {
- pr_debug("releasing mtd %d (%p)\n", mtdno, ptr);
- nand_release(nand_to_mtd(&ptr->chip));
- }
- }
-
- /* free the common resources */
-
- if (!IS_ERR(info->clk))
- s3c2410_nand_clk_set_state(info, CLOCK_DISABLE);
-
- return 0;
-}
-
-static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
- struct s3c2410_nand_mtd *mtd,
- struct s3c2410_nand_set *set)
-{
- if (set) {
- struct mtd_info *mtdinfo = nand_to_mtd(&mtd->chip);
-
- mtdinfo->name = set->name;
-
- return mtd_device_parse_register(mtdinfo, NULL, NULL,
- set->partitions, set->nr_partitions);
- }
-
- return -ENODEV;
-}
-
-static int s3c2410_nand_setup_data_interface(struct mtd_info *mtd, int csline,
- const struct nand_data_interface *conf)
-{
- struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
- struct s3c2410_platform_nand *pdata = info->platform;
- const struct nand_sdr_timings *timings;
- int tacls;
-
- timings = nand_get_sdr_timings(conf);
- if (IS_ERR(timings))
- return -ENOTSUPP;
-
- tacls = timings->tCLS_min - timings->tWP_min;
- if (tacls < 0)
- tacls = 0;
-
- pdata->tacls = DIV_ROUND_UP(tacls, 1000);
- pdata->twrph0 = DIV_ROUND_UP(timings->tWP_min, 1000);
- pdata->twrph1 = DIV_ROUND_UP(timings->tCLH_min, 1000);
-
- return s3c2410_nand_setrate(info);
-}
-
-/**
- * s3c2410_nand_init_chip - initialise a single instance of an chip
- * @info: The base NAND controller the chip is on.
- * @nmtd: The new controller MTD instance to fill in.
- * @set: The information passed from the board specific platform data.
- *
- * Initialise the given @nmtd from the information in @info and @set. This
- * readies the structure for use with the MTD layer functions by ensuring
- * all pointers are setup and the necessary control routines selected.
- */
-static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
- struct s3c2410_nand_mtd *nmtd,
- struct s3c2410_nand_set *set)
-{
- struct device_node *np = info->device->of_node;
- struct nand_chip *chip = &nmtd->chip;
- void __iomem *regs = info->regs;
-
- nand_set_flash_node(chip, set->of_node);
-
- chip->write_buf = s3c2410_nand_write_buf;
- chip->read_buf = s3c2410_nand_read_buf;
- chip->select_chip = s3c2410_nand_select_chip;
- chip->chip_delay = 50;
- nand_set_controller_data(chip, nmtd);
- chip->options = set->options;
- chip->controller = &info->controller;
-
- /*
- * let's keep behavior unchanged for legacy boards booting via pdata and
- * auto-detect timings only when booting with a device tree.
- */
- if (np)
- chip->setup_data_interface = s3c2410_nand_setup_data_interface;
-
- switch (info->cpu_type) {
- case TYPE_S3C2410:
- chip->IO_ADDR_W = regs + S3C2410_NFDATA;
- info->sel_reg = regs + S3C2410_NFCONF;
- info->sel_bit = S3C2410_NFCONF_nFCE;
- chip->cmd_ctrl = s3c2410_nand_hwcontrol;
- chip->dev_ready = s3c2410_nand_devready;
- break;
-
- case TYPE_S3C2440:
- chip->IO_ADDR_W = regs + S3C2440_NFDATA;
- info->sel_reg = regs + S3C2440_NFCONT;
- info->sel_bit = S3C2440_NFCONT_nFCE;
- chip->cmd_ctrl = s3c2440_nand_hwcontrol;
- chip->dev_ready = s3c2440_nand_devready;
- chip->read_buf = s3c2440_nand_read_buf;
- chip->write_buf = s3c2440_nand_write_buf;
- break;
-
- case TYPE_S3C2412:
- chip->IO_ADDR_W = regs + S3C2440_NFDATA;
- info->sel_reg = regs + S3C2440_NFCONT;
- info->sel_bit = S3C2412_NFCONT_nFCE0;
- chip->cmd_ctrl = s3c2440_nand_hwcontrol;
- chip->dev_ready = s3c2412_nand_devready;
-
- if (readl(regs + S3C2410_NFCONF) & S3C2412_NFCONF_NANDBOOT)
- dev_info(info->device, "System booted from NAND\n");
-
- break;
- }
-
- chip->IO_ADDR_R = chip->IO_ADDR_W;
-
- nmtd->info = info;
- nmtd->set = set;
-
- chip->ecc.mode = info->platform->ecc_mode;
-
- /*
- * If you use u-boot BBT creation code, specifying this flag will
- * let the kernel fish out the BBT from the NAND.
- */
- if (set->flash_bbt)
- chip->bbt_options |= NAND_BBT_USE_FLASH;
-}
-
-/**
- * s3c2410_nand_update_chip - post probe update
- * @info: The controller instance.
- * @nmtd: The driver version of the MTD instance.
- *
- * This routine is called after the chip probe has successfully completed
- * and the relevant per-chip information updated. This call ensure that
- * we update the internal state accordingly.
- *
- * The internal state is currently limited to the ECC state information.
-*/
-static int s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
- struct s3c2410_nand_mtd *nmtd)
-{
- struct nand_chip *chip = &nmtd->chip;
-
- switch (chip->ecc.mode) {
-
- case NAND_ECC_NONE:
- dev_info(info->device, "ECC disabled\n");
- break;
-
- case NAND_ECC_SOFT:
- /*
- * This driver expects Hamming based ECC when ecc_mode is set
- * to NAND_ECC_SOFT. Force ecc.algo to NAND_ECC_HAMMING to
- * avoid adding an extra ecc_algo field to
- * s3c2410_platform_nand.
- */
- chip->ecc.algo = NAND_ECC_HAMMING;
- dev_info(info->device, "soft ECC\n");
- break;
-
- case NAND_ECC_HW:
- chip->ecc.calculate = s3c2410_nand_calculate_ecc;
- chip->ecc.correct = s3c2410_nand_correct_data;
- chip->ecc.strength = 1;
-
- switch (info->cpu_type) {
- case TYPE_S3C2410:
- chip->ecc.hwctl = s3c2410_nand_enable_hwecc;
- chip->ecc.calculate = s3c2410_nand_calculate_ecc;
- break;
-
- case TYPE_S3C2412:
- chip->ecc.hwctl = s3c2412_nand_enable_hwecc;
- chip->ecc.calculate = s3c2412_nand_calculate_ecc;
- break;
-
- case TYPE_S3C2440:
- chip->ecc.hwctl = s3c2440_nand_enable_hwecc;
- chip->ecc.calculate = s3c2440_nand_calculate_ecc;
- break;
- }
-
- dev_dbg(info->device, "chip %p => page shift %d\n",
- chip, chip->page_shift);
-
- /* change the behaviour depending on whether we are using
- * the large or small page nand device */
- if (chip->page_shift > 10) {
- chip->ecc.size = 256;
- chip->ecc.bytes = 3;
- } else {
- chip->ecc.size = 512;
- chip->ecc.bytes = 3;
- mtd_set_ooblayout(nand_to_mtd(chip),
- &s3c2410_ooblayout_ops);
- }
-
- dev_info(info->device, "hardware ECC\n");
- break;
-
- default:
- dev_err(info->device, "invalid ECC mode!\n");
- return -EINVAL;
- }
-
- if (chip->bbt_options & NAND_BBT_USE_FLASH)
- chip->options |= NAND_SKIP_BBTSCAN;
-
- return 0;
-}
-
-static const struct of_device_id s3c24xx_nand_dt_ids[] = {
- {
- .compatible = "samsung,s3c2410-nand",
- .data = &s3c2410_nand_devtype_data,
- }, {
- /* also compatible with s3c6400 */
- .compatible = "samsung,s3c2412-nand",
- .data = &s3c2412_nand_devtype_data,
- }, {
- .compatible = "samsung,s3c2440-nand",
- .data = &s3c2440_nand_devtype_data,
- },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, s3c24xx_nand_dt_ids);
-
-static int s3c24xx_nand_probe_dt(struct platform_device *pdev)
-{
- const struct s3c24XX_nand_devtype_data *devtype_data;
- struct s3c2410_platform_nand *pdata;
- struct s3c2410_nand_info *info = platform_get_drvdata(pdev);
- struct device_node *np = pdev->dev.of_node, *child;
- struct s3c2410_nand_set *sets;
-
- devtype_data = of_device_get_match_data(&pdev->dev);
- if (!devtype_data)
- return -ENODEV;
-
- info->cpu_type = devtype_data->type;
-
- pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
-
- pdev->dev.platform_data = pdata;
-
- pdata->nr_sets = of_get_child_count(np);
- if (!pdata->nr_sets)
- return 0;
-
- sets = devm_kzalloc(&pdev->dev, sizeof(*sets) * pdata->nr_sets,
- GFP_KERNEL);
- if (!sets)
- return -ENOMEM;
-
- pdata->sets = sets;
-
- for_each_available_child_of_node(np, child) {
- sets->name = (char *)child->name;
- sets->of_node = child;
- sets->nr_chips = 1;
-
- of_node_get(child);
-
- sets++;
- }
-
- return 0;
-}
-
-static int s3c24xx_nand_probe_pdata(struct platform_device *pdev)
-{
- struct s3c2410_nand_info *info = platform_get_drvdata(pdev);
-
- info->cpu_type = platform_get_device_id(pdev)->driver_data;
-
- return 0;
-}
-
-/* s3c24xx_nand_probe
- *
- * called by device layer when it finds a device matching
- * one our driver can handled. This code checks to see if
- * it can allocate all necessary resources then calls the
- * nand layer to look for devices
-*/
-static int s3c24xx_nand_probe(struct platform_device *pdev)
-{
- struct s3c2410_platform_nand *plat;
- struct s3c2410_nand_info *info;
- struct s3c2410_nand_mtd *nmtd;
- struct s3c2410_nand_set *sets;
- struct resource *res;
- int err = 0;
- int size;
- int nr_sets;
- int setno;
-
- info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
- if (info == NULL) {
- err = -ENOMEM;
- goto exit_error;
- }
-
- platform_set_drvdata(pdev, info);
-
- nand_hw_control_init(&info->controller);
-
- /* get the clock source and enable it */
-
- info->clk = devm_clk_get(&pdev->dev, "nand");
- if (IS_ERR(info->clk)) {
- dev_err(&pdev->dev, "failed to get clock\n");
- err = -ENOENT;
- goto exit_error;
- }
-
- s3c2410_nand_clk_set_state(info, CLOCK_ENABLE);
-
- if (pdev->dev.of_node)
- err = s3c24xx_nand_probe_dt(pdev);
- else
- err = s3c24xx_nand_probe_pdata(pdev);
-
- if (err)
- goto exit_error;
-
- plat = to_nand_plat(pdev);
-
- /* allocate and map the resource */
-
- /* currently we assume we have the one resource */
- res = pdev->resource;
- size = resource_size(res);
-
- info->device = &pdev->dev;
- info->platform = plat;
-
- info->regs = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(info->regs)) {
- err = PTR_ERR(info->regs);
- goto exit_error;
- }
-
- dev_dbg(&pdev->dev, "mapped registers at %p\n", info->regs);
-
- sets = (plat != NULL) ? plat->sets : NULL;
- nr_sets = (plat != NULL) ? plat->nr_sets : 1;
-
- info->mtd_count = nr_sets;
-
- /* allocate our information */
-
- size = nr_sets * sizeof(*info->mtds);
- info->mtds = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
- if (info->mtds == NULL) {
- err = -ENOMEM;
- goto exit_error;
- }
-
- /* initialise all possible chips */
-
- nmtd = info->mtds;
-
- for (setno = 0; setno < nr_sets; setno++, nmtd++) {
- struct mtd_info *mtd = nand_to_mtd(&nmtd->chip);
-
- pr_debug("initialising set %d (%p, info %p)\n",
- setno, nmtd, info);
-
- mtd->dev.parent = &pdev->dev;
- s3c2410_nand_init_chip(info, nmtd, sets);
-
- nmtd->scan_res = nand_scan_ident(mtd,
- (sets) ? sets->nr_chips : 1,
- NULL);
-
- if (nmtd->scan_res == 0) {
- err = s3c2410_nand_update_chip(info, nmtd);
- if (err < 0)
- goto exit_error;
- nand_scan_tail(mtd);
- s3c2410_nand_add_partition(info, nmtd, sets);
- }
-
- if (sets != NULL)
- sets++;
- }
-
- /* initialise the hardware */
- err = s3c2410_nand_inithw(info);
- if (err != 0)
- goto exit_error;
-
- err = s3c2410_nand_cpufreq_register(info);
- if (err < 0) {
- dev_err(&pdev->dev, "failed to init cpufreq support\n");
- goto exit_error;
- }
-
- if (allow_clk_suspend(info)) {
- dev_info(&pdev->dev, "clock idle support enabled\n");
- s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND);
- }
-
- return 0;
-
- exit_error:
- s3c24xx_nand_remove(pdev);
-
- if (err == 0)
- err = -EINVAL;
- return err;
-}
-
-/* PM Support */
-#ifdef CONFIG_PM
-
-static int s3c24xx_nand_suspend(struct platform_device *dev, pm_message_t pm)
-{
- struct s3c2410_nand_info *info = platform_get_drvdata(dev);
-
- if (info) {
- info->save_sel = readl(info->sel_reg);
-
- /* For the moment, we must ensure nFCE is high during
- * the time we are suspended. This really should be
- * handled by suspending the MTDs we are using, but
- * that is currently not the case. */
-
- writel(info->save_sel | info->sel_bit, info->sel_reg);
-
- s3c2410_nand_clk_set_state(info, CLOCK_DISABLE);
- }
-
- return 0;
-}
-
-static int s3c24xx_nand_resume(struct platform_device *dev)
-{
- struct s3c2410_nand_info *info = platform_get_drvdata(dev);
- unsigned long sel;
-
- if (info) {
- s3c2410_nand_clk_set_state(info, CLOCK_ENABLE);
- s3c2410_nand_inithw(info);
-
- /* Restore the state of the nFCE line. */
-
- sel = readl(info->sel_reg);
- sel &= ~info->sel_bit;
- sel |= info->save_sel & info->sel_bit;
- writel(sel, info->sel_reg);
-
- s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND);
- }
-
- return 0;
-}
-
-#else
-#define s3c24xx_nand_suspend NULL
-#define s3c24xx_nand_resume NULL
-#endif
-
-/* driver device registration */
-
-static const struct platform_device_id s3c24xx_driver_ids[] = {
- {
- .name = "s3c2410-nand",
- .driver_data = TYPE_S3C2410,
- }, {
- .name = "s3c2440-nand",
- .driver_data = TYPE_S3C2440,
- }, {
- .name = "s3c2412-nand",
- .driver_data = TYPE_S3C2412,
- }, {
- .name = "s3c6400-nand",
- .driver_data = TYPE_S3C2412, /* compatible with 2412 */
- },
- { }
-};
-
-MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);
-
-static struct platform_driver s3c24xx_nand_driver = {
- .probe = s3c24xx_nand_probe,
- .remove = s3c24xx_nand_remove,
- .suspend = s3c24xx_nand_suspend,
- .resume = s3c24xx_nand_resume,
- .id_table = s3c24xx_driver_ids,
- .driver = {
- .name = "s3c24xx-nand",
- .of_match_table = s3c24xx_nand_dt_ids,
- },
-};
-
-module_platform_driver(s3c24xx_nand_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
-MODULE_DESCRIPTION("S3C24XX MTD NAND driver");
diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c
deleted file mode 100644
index c4e7755448e6..000000000000
--- a/drivers/mtd/nand/sh_flctl.c
+++ /dev/null
@@ -1,1250 +0,0 @@
-/*
- * SuperH FLCTL nand controller
- *
- * Copyright (c) 2008 Renesas Solutions Corp.
- * Copyright (c) 2008 Atom Create Engineering Co., Ltd.
- *
- * Based on fsl_elbc_nand.c, Copyright (c) 2006-2007 Freescale Semiconductor
- *
- * 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 of the License.
- *
- * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/completion.h>
-#include <linux/delay.h>
-#include <linux/dmaengine.h>
-#include <linux/dma-mapping.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/sh_dma.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/partitions.h>
-#include <linux/mtd/sh_flctl.h>
-
-static int flctl_4secc_ooblayout_sp_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- if (section)
- return -ERANGE;
-
- oobregion->offset = 0;
- oobregion->length = chip->ecc.bytes;
-
- return 0;
-}
-
-static int flctl_4secc_ooblayout_sp_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- if (section)
- return -ERANGE;
-
- oobregion->offset = 12;
- oobregion->length = 4;
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops flctl_4secc_oob_smallpage_ops = {
- .ecc = flctl_4secc_ooblayout_sp_ecc,
- .free = flctl_4secc_ooblayout_sp_free,
-};
-
-static int flctl_4secc_ooblayout_lp_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- if (section >= chip->ecc.steps)
- return -ERANGE;
-
- oobregion->offset = (section * 16) + 6;
- oobregion->length = chip->ecc.bytes;
-
- return 0;
-}
-
-static int flctl_4secc_ooblayout_lp_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- if (section >= chip->ecc.steps)
- return -ERANGE;
-
- oobregion->offset = section * 16;
- oobregion->length = 6;
-
- if (!section) {
- oobregion->offset += 2;
- oobregion->length -= 2;
- }
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops flctl_4secc_oob_largepage_ops = {
- .ecc = flctl_4secc_ooblayout_lp_ecc,
- .free = flctl_4secc_ooblayout_lp_free,
-};
-
-static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
-
-static struct nand_bbt_descr flctl_4secc_smallpage = {
- .options = NAND_BBT_SCAN2NDPAGE,
- .offs = 11,
- .len = 1,
- .pattern = scan_ff_pattern,
-};
-
-static struct nand_bbt_descr flctl_4secc_largepage = {
- .options = NAND_BBT_SCAN2NDPAGE,
- .offs = 0,
- .len = 2,
- .pattern = scan_ff_pattern,
-};
-
-static void empty_fifo(struct sh_flctl *flctl)
-{
- writel(flctl->flintdmacr_base | AC1CLR | AC0CLR, FLINTDMACR(flctl));
- writel(flctl->flintdmacr_base, FLINTDMACR(flctl));
-}
-
-static void start_translation(struct sh_flctl *flctl)
-{
- writeb(TRSTRT, FLTRCR(flctl));
-}
-
-static void timeout_error(struct sh_flctl *flctl, const char *str)
-{
- dev_err(&flctl->pdev->dev, "Timeout occurred in %s\n", str);
-}
-
-static void wait_completion(struct sh_flctl *flctl)
-{
- uint32_t timeout = LOOP_TIMEOUT_MAX;
-
- while (timeout--) {
- if (readb(FLTRCR(flctl)) & TREND) {
- writeb(0x0, FLTRCR(flctl));
- return;
- }
- udelay(1);
- }
-
- timeout_error(flctl, __func__);
- writeb(0x0, FLTRCR(flctl));
-}
-
-static void flctl_dma_complete(void *param)
-{
- struct sh_flctl *flctl = param;
-
- complete(&flctl->dma_complete);
-}
-
-static void flctl_release_dma(struct sh_flctl *flctl)
-{
- if (flctl->chan_fifo0_rx) {
- dma_release_channel(flctl->chan_fifo0_rx);
- flctl->chan_fifo0_rx = NULL;
- }
- if (flctl->chan_fifo0_tx) {
- dma_release_channel(flctl->chan_fifo0_tx);
- flctl->chan_fifo0_tx = NULL;
- }
-}
-
-static void flctl_setup_dma(struct sh_flctl *flctl)
-{
- dma_cap_mask_t mask;
- struct dma_slave_config cfg;
- struct platform_device *pdev = flctl->pdev;
- struct sh_flctl_platform_data *pdata = dev_get_platdata(&pdev->dev);
- int ret;
-
- if (!pdata)
- return;
-
- if (pdata->slave_id_fifo0_tx <= 0 || pdata->slave_id_fifo0_rx <= 0)
- return;
-
- /* We can only either use DMA for both Tx and Rx or not use it at all */
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
-
- flctl->chan_fifo0_tx = dma_request_channel(mask, shdma_chan_filter,
- (void *)(uintptr_t)pdata->slave_id_fifo0_tx);
- dev_dbg(&pdev->dev, "%s: TX: got channel %p\n", __func__,
- flctl->chan_fifo0_tx);
-
- if (!flctl->chan_fifo0_tx)
- return;
-
- memset(&cfg, 0, sizeof(cfg));
- cfg.direction = DMA_MEM_TO_DEV;
- cfg.dst_addr = flctl->fifo;
- cfg.src_addr = 0;
- ret = dmaengine_slave_config(flctl->chan_fifo0_tx, &cfg);
- if (ret < 0)
- goto err;
-
- flctl->chan_fifo0_rx = dma_request_channel(mask, shdma_chan_filter,
- (void *)(uintptr_t)pdata->slave_id_fifo0_rx);
- dev_dbg(&pdev->dev, "%s: RX: got channel %p\n", __func__,
- flctl->chan_fifo0_rx);
-
- if (!flctl->chan_fifo0_rx)
- goto err;
-
- cfg.direction = DMA_DEV_TO_MEM;
- cfg.dst_addr = 0;
- cfg.src_addr = flctl->fifo;
- ret = dmaengine_slave_config(flctl->chan_fifo0_rx, &cfg);
- if (ret < 0)
- goto err;
-
- init_completion(&flctl->dma_complete);
-
- return;
-
-err:
- flctl_release_dma(flctl);
-}
-
-static void set_addr(struct mtd_info *mtd, int column, int page_addr)
-{
- struct sh_flctl *flctl = mtd_to_flctl(mtd);
- uint32_t addr = 0;
-
- if (column == -1) {
- addr = page_addr; /* ERASE1 */
- } else if (page_addr != -1) {
- /* SEQIN, READ0, etc.. */
- if (flctl->chip.options & NAND_BUSWIDTH_16)
- column >>= 1;
- if (flctl->page_size) {
- addr = column & 0x0FFF;
- addr |= (page_addr & 0xff) << 16;
- addr |= ((page_addr >> 8) & 0xff) << 24;
- /* big than 128MB */
- if (flctl->rw_ADRCNT == ADRCNT2_E) {
- uint32_t addr2;
- addr2 = (page_addr >> 16) & 0xff;
- writel(addr2, FLADR2(flctl));
- }
- } else {
- addr = column;
- addr |= (page_addr & 0xff) << 8;
- addr |= ((page_addr >> 8) & 0xff) << 16;
- addr |= ((page_addr >> 16) & 0xff) << 24;
- }
- }
- writel(addr, FLADR(flctl));
-}
-
-static void wait_rfifo_ready(struct sh_flctl *flctl)
-{
- uint32_t timeout = LOOP_TIMEOUT_MAX;
-
- while (timeout--) {
- uint32_t val;
- /* check FIFO */
- val = readl(FLDTCNTR(flctl)) >> 16;
- if (val & 0xFF)
- return;
- udelay(1);
- }
- timeout_error(flctl, __func__);
-}
-
-static void wait_wfifo_ready(struct sh_flctl *flctl)
-{
- uint32_t len, timeout = LOOP_TIMEOUT_MAX;
-
- while (timeout--) {
- /* check FIFO */
- len = (readl(FLDTCNTR(flctl)) >> 16) & 0xFF;
- if (len >= 4)
- return;
- udelay(1);
- }
- timeout_error(flctl, __func__);
-}
-
-static enum flctl_ecc_res_t wait_recfifo_ready
- (struct sh_flctl *flctl, int sector_number)
-{
- uint32_t timeout = LOOP_TIMEOUT_MAX;
- void __iomem *ecc_reg[4];
- int i;
- int state = FL_SUCCESS;
- uint32_t data, size;
-
- /*
- * First this loops checks in FLDTCNTR if we are ready to read out the
- * oob data. This is the case if either all went fine without errors or
- * if the bottom part of the loop corrected the errors or marked them as
- * uncorrectable and the controller is given time to push the data into
- * the FIFO.
- */
- while (timeout--) {
- /* check if all is ok and we can read out the OOB */
- size = readl(FLDTCNTR(flctl)) >> 24;
- if ((size & 0xFF) == 4)
- return state;
-
- /* check if a correction code has been calculated */
- if (!(readl(FL4ECCCR(flctl)) & _4ECCEND)) {
- /*
- * either we wait for the fifo to be filled or a
- * correction pattern is being generated
- */
- udelay(1);
- continue;
- }
-
- /* check for an uncorrectable error */
- if (readl(FL4ECCCR(flctl)) & _4ECCFA) {
- /* check if we face a non-empty page */
- for (i = 0; i < 512; i++) {
- if (flctl->done_buff[i] != 0xff) {
- state = FL_ERROR; /* can't correct */
- break;
- }
- }
-
- if (state == FL_SUCCESS)
- dev_dbg(&flctl->pdev->dev,
- "reading empty sector %d, ecc error ignored\n",
- sector_number);
-
- writel(0, FL4ECCCR(flctl));
- continue;
- }
-
- /* start error correction */
- ecc_reg[0] = FL4ECCRESULT0(flctl);
- ecc_reg[1] = FL4ECCRESULT1(flctl);
- ecc_reg[2] = FL4ECCRESULT2(flctl);
- ecc_reg[3] = FL4ECCRESULT3(flctl);
-
- for (i = 0; i < 3; i++) {
- uint8_t org;
- unsigned int index;
-
- data = readl(ecc_reg[i]);
-
- if (flctl->page_size)
- index = (512 * sector_number) +
- (data >> 16);
- else
- index = data >> 16;
-
- org = flctl->done_buff[index];
- flctl->done_buff[index] = org ^ (data & 0xFF);
- }
- state = FL_REPAIRABLE;
- writel(0, FL4ECCCR(flctl));
- }
-
- timeout_error(flctl, __func__);
- return FL_TIMEOUT; /* timeout */
-}
-
-static void wait_wecfifo_ready(struct sh_flctl *flctl)
-{
- uint32_t timeout = LOOP_TIMEOUT_MAX;
- uint32_t len;
-
- while (timeout--) {
- /* check FLECFIFO */
- len = (readl(FLDTCNTR(flctl)) >> 24) & 0xFF;
- if (len >= 4)
- return;
- udelay(1);
- }
- timeout_error(flctl, __func__);
-}
-
-static int flctl_dma_fifo0_transfer(struct sh_flctl *flctl, unsigned long *buf,
- int len, enum dma_data_direction dir)
-{
- struct dma_async_tx_descriptor *desc = NULL;
- struct dma_chan *chan;
- enum dma_transfer_direction tr_dir;
- dma_addr_t dma_addr;
- dma_cookie_t cookie;
- uint32_t reg;
- int ret;
-
- if (dir == DMA_FROM_DEVICE) {
- chan = flctl->chan_fifo0_rx;
- tr_dir = DMA_DEV_TO_MEM;
- } else {
- chan = flctl->chan_fifo0_tx;
- tr_dir = DMA_MEM_TO_DEV;
- }
-
- dma_addr = dma_map_single(chan->device->dev, buf, len, dir);
-
- if (!dma_mapping_error(chan->device->dev, dma_addr))
- desc = dmaengine_prep_slave_single(chan, dma_addr, len,
- tr_dir, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
-
- if (desc) {
- reg = readl(FLINTDMACR(flctl));
- reg |= DREQ0EN;
- writel(reg, FLINTDMACR(flctl));
-
- desc->callback = flctl_dma_complete;
- desc->callback_param = flctl;
- cookie = dmaengine_submit(desc);
- if (dma_submit_error(cookie)) {
- ret = dma_submit_error(cookie);
- dev_warn(&flctl->pdev->dev,
- "DMA submit failed, falling back to PIO\n");
- goto out;
- }
-
- dma_async_issue_pending(chan);
- } else {
- /* DMA failed, fall back to PIO */
- flctl_release_dma(flctl);
- dev_warn(&flctl->pdev->dev,
- "DMA failed, falling back to PIO\n");
- ret = -EIO;
- goto out;
- }
-
- ret =
- wait_for_completion_timeout(&flctl->dma_complete,
- msecs_to_jiffies(3000));
-
- if (ret <= 0) {
- dmaengine_terminate_all(chan);
- dev_err(&flctl->pdev->dev, "wait_for_completion_timeout\n");
- }
-
-out:
- reg = readl(FLINTDMACR(flctl));
- reg &= ~DREQ0EN;
- writel(reg, FLINTDMACR(flctl));
-
- dma_unmap_single(chan->device->dev, dma_addr, len, dir);
-
- /* ret > 0 is success */
- return ret;
-}
-
-static void read_datareg(struct sh_flctl *flctl, int offset)
-{
- unsigned long data;
- unsigned long *buf = (unsigned long *)&flctl->done_buff[offset];
-
- wait_completion(flctl);
-
- data = readl(FLDATAR(flctl));
- *buf = le32_to_cpu(data);
-}
-
-static void read_fiforeg(struct sh_flctl *flctl, int rlen, int offset)
-{
- int i, len_4align;
- unsigned long *buf = (unsigned long *)&flctl->done_buff[offset];
-
- len_4align = (rlen + 3) / 4;
-
- /* initiate DMA transfer */
- if (flctl->chan_fifo0_rx && rlen >= 32 &&
- flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_DEV_TO_MEM) > 0)
- goto convert; /* DMA success */
-
- /* do polling transfer */
- for (i = 0; i < len_4align; i++) {
- wait_rfifo_ready(flctl);
- buf[i] = readl(FLDTFIFO(flctl));
- }
-
-convert:
- for (i = 0; i < len_4align; i++)
- buf[i] = be32_to_cpu(buf[i]);
-}
-
-static enum flctl_ecc_res_t read_ecfiforeg
- (struct sh_flctl *flctl, uint8_t *buff, int sector)
-{
- int i;
- enum flctl_ecc_res_t res;
- unsigned long *ecc_buf = (unsigned long *)buff;
-
- res = wait_recfifo_ready(flctl , sector);
-
- if (res != FL_ERROR) {
- for (i = 0; i < 4; i++) {
- ecc_buf[i] = readl(FLECFIFO(flctl));
- ecc_buf[i] = be32_to_cpu(ecc_buf[i]);
- }
- }
-
- return res;
-}
-
-static void write_fiforeg(struct sh_flctl *flctl, int rlen,
- unsigned int offset)
-{
- int i, len_4align;
- unsigned long *buf = (unsigned long *)&flctl->done_buff[offset];
-
- len_4align = (rlen + 3) / 4;
- for (i = 0; i < len_4align; i++) {
- wait_wfifo_ready(flctl);
- writel(cpu_to_be32(buf[i]), FLDTFIFO(flctl));
- }
-}
-
-static void write_ec_fiforeg(struct sh_flctl *flctl, int rlen,
- unsigned int offset)
-{
- int i, len_4align;
- unsigned long *buf = (unsigned long *)&flctl->done_buff[offset];
-
- len_4align = (rlen + 3) / 4;
-
- for (i = 0; i < len_4align; i++)
- buf[i] = cpu_to_be32(buf[i]);
-
- /* initiate DMA transfer */
- if (flctl->chan_fifo0_tx && rlen >= 32 &&
- flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_MEM_TO_DEV) > 0)
- return; /* DMA success */
-
- /* do polling transfer */
- for (i = 0; i < len_4align; i++) {
- wait_wecfifo_ready(flctl);
- writel(buf[i], FLECFIFO(flctl));
- }
-}
-
-static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_val)
-{
- struct sh_flctl *flctl = mtd_to_flctl(mtd);
- uint32_t flcmncr_val = flctl->flcmncr_base & ~SEL_16BIT;
- uint32_t flcmdcr_val, addr_len_bytes = 0;
-
- /* Set SNAND bit if page size is 2048byte */
- if (flctl->page_size)
- flcmncr_val |= SNAND_E;
- else
- flcmncr_val &= ~SNAND_E;
-
- /* default FLCMDCR val */
- flcmdcr_val = DOCMD1_E | DOADR_E;
-
- /* Set for FLCMDCR */
- switch (cmd) {
- case NAND_CMD_ERASE1:
- addr_len_bytes = flctl->erase_ADRCNT;
- flcmdcr_val |= DOCMD2_E;
- break;
- case NAND_CMD_READ0:
- case NAND_CMD_READOOB:
- case NAND_CMD_RNDOUT:
- addr_len_bytes = flctl->rw_ADRCNT;
- flcmdcr_val |= CDSRC_E;
- if (flctl->chip.options & NAND_BUSWIDTH_16)
- flcmncr_val |= SEL_16BIT;
- break;
- case NAND_CMD_SEQIN:
- /* This case is that cmd is READ0 or READ1 or READ00 */
- flcmdcr_val &= ~DOADR_E; /* ONLY execute 1st cmd */
- break;
- case NAND_CMD_PAGEPROG:
- addr_len_bytes = flctl->rw_ADRCNT;
- flcmdcr_val |= DOCMD2_E | CDSRC_E | SELRW;
- if (flctl->chip.options & NAND_BUSWIDTH_16)
- flcmncr_val |= SEL_16BIT;
- break;
- case NAND_CMD_READID:
- flcmncr_val &= ~SNAND_E;
- flcmdcr_val |= CDSRC_E;
- addr_len_bytes = ADRCNT_1;
- break;
- case NAND_CMD_STATUS:
- case NAND_CMD_RESET:
- flcmncr_val &= ~SNAND_E;
- flcmdcr_val &= ~(DOADR_E | DOSR_E);
- break;
- default:
- break;
- }
-
- /* Set address bytes parameter */
- flcmdcr_val |= addr_len_bytes;
-
- /* Now actually write */
- writel(flcmncr_val, FLCMNCR(flctl));
- writel(flcmdcr_val, FLCMDCR(flctl));
- writel(flcmcdr_val, FLCMCDR(flctl));
-}
-
-static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page)
-{
- nand_read_page_op(chip, page, 0, buf, mtd->writesize);
- if (oob_required)
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
- return 0;
-}
-
-static int flctl_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf, int oob_required,
- int page)
-{
- nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);
- chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
- return nand_prog_page_end_op(chip);
-}
-
-static void execmd_read_page_sector(struct mtd_info *mtd, int page_addr)
-{
- struct sh_flctl *flctl = mtd_to_flctl(mtd);
- int sector, page_sectors;
- enum flctl_ecc_res_t ecc_result;
-
- page_sectors = flctl->page_size ? 4 : 1;
-
- set_cmd_regs(mtd, NAND_CMD_READ0,
- (NAND_CMD_READSTART << 8) | NAND_CMD_READ0);
-
- writel(readl(FLCMNCR(flctl)) | ACM_SACCES_MODE | _4ECCCORRECT,
- FLCMNCR(flctl));
- writel(readl(FLCMDCR(flctl)) | page_sectors, FLCMDCR(flctl));
- writel(page_addr << 2, FLADR(flctl));
-
- empty_fifo(flctl);
- start_translation(flctl);
-
- for (sector = 0; sector < page_sectors; sector++) {
- read_fiforeg(flctl, 512, 512 * sector);
-
- ecc_result = read_ecfiforeg(flctl,
- &flctl->done_buff[mtd->writesize + 16 * sector],
- sector);
-
- switch (ecc_result) {
- case FL_REPAIRABLE:
- dev_info(&flctl->pdev->dev,
- "applied ecc on page 0x%x", page_addr);
- mtd->ecc_stats.corrected++;
- break;
- case FL_ERROR:
- dev_warn(&flctl->pdev->dev,
- "page 0x%x contains corrupted data\n",
- page_addr);
- mtd->ecc_stats.failed++;
- break;
- default:
- ;
- }
- }
-
- wait_completion(flctl);
-
- writel(readl(FLCMNCR(flctl)) & ~(ACM_SACCES_MODE | _4ECCCORRECT),
- FLCMNCR(flctl));
-}
-
-static void execmd_read_oob(struct mtd_info *mtd, int page_addr)
-{
- struct sh_flctl *flctl = mtd_to_flctl(mtd);
- int page_sectors = flctl->page_size ? 4 : 1;
- int i;
-
- set_cmd_regs(mtd, NAND_CMD_READ0,
- (NAND_CMD_READSTART << 8) | NAND_CMD_READ0);
-
- empty_fifo(flctl);
-
- for (i = 0; i < page_sectors; i++) {
- set_addr(mtd, (512 + 16) * i + 512 , page_addr);
- writel(16, FLDTCNTR(flctl));
-
- start_translation(flctl);
- read_fiforeg(flctl, 16, 16 * i);
- wait_completion(flctl);
- }
-}
-
-static void execmd_write_page_sector(struct mtd_info *mtd)
-{
- struct sh_flctl *flctl = mtd_to_flctl(mtd);
- int page_addr = flctl->seqin_page_addr;
- int sector, page_sectors;
-
- page_sectors = flctl->page_size ? 4 : 1;
-
- set_cmd_regs(mtd, NAND_CMD_PAGEPROG,
- (NAND_CMD_PAGEPROG << 8) | NAND_CMD_SEQIN);
-
- empty_fifo(flctl);
- writel(readl(FLCMNCR(flctl)) | ACM_SACCES_MODE, FLCMNCR(flctl));
- writel(readl(FLCMDCR(flctl)) | page_sectors, FLCMDCR(flctl));
- writel(page_addr << 2, FLADR(flctl));
- start_translation(flctl);
-
- for (sector = 0; sector < page_sectors; sector++) {
- write_fiforeg(flctl, 512, 512 * sector);
- write_ec_fiforeg(flctl, 16, mtd->writesize + 16 * sector);
- }
-
- wait_completion(flctl);
- writel(readl(FLCMNCR(flctl)) & ~ACM_SACCES_MODE, FLCMNCR(flctl));
-}
-
-static void execmd_write_oob(struct mtd_info *mtd)
-{
- struct sh_flctl *flctl = mtd_to_flctl(mtd);
- int page_addr = flctl->seqin_page_addr;
- int sector, page_sectors;
-
- page_sectors = flctl->page_size ? 4 : 1;
-
- set_cmd_regs(mtd, NAND_CMD_PAGEPROG,
- (NAND_CMD_PAGEPROG << 8) | NAND_CMD_SEQIN);
-
- for (sector = 0; sector < page_sectors; sector++) {
- empty_fifo(flctl);
- set_addr(mtd, sector * 528 + 512, page_addr);
- writel(16, FLDTCNTR(flctl)); /* set read size */
-
- start_translation(flctl);
- write_fiforeg(flctl, 16, 16 * sector);
- wait_completion(flctl);
- }
-}
-
-static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command,
- int column, int page_addr)
-{
- struct sh_flctl *flctl = mtd_to_flctl(mtd);
- uint32_t read_cmd = 0;
-
- pm_runtime_get_sync(&flctl->pdev->dev);
-
- flctl->read_bytes = 0;
- if (command != NAND_CMD_PAGEPROG)
- flctl->index = 0;
-
- switch (command) {
- case NAND_CMD_READ1:
- case NAND_CMD_READ0:
- if (flctl->hwecc) {
- /* read page with hwecc */
- execmd_read_page_sector(mtd, page_addr);
- break;
- }
- if (flctl->page_size)
- set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8)
- | command);
- else
- set_cmd_regs(mtd, command, command);
-
- set_addr(mtd, 0, page_addr);
-
- flctl->read_bytes = mtd->writesize + mtd->oobsize;
- if (flctl->chip.options & NAND_BUSWIDTH_16)
- column >>= 1;
- flctl->index += column;
- goto read_normal_exit;
-
- case NAND_CMD_READOOB:
- if (flctl->hwecc) {
- /* read page with hwecc */
- execmd_read_oob(mtd, page_addr);
- break;
- }
-
- if (flctl->page_size) {
- set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8)
- | NAND_CMD_READ0);
- set_addr(mtd, mtd->writesize, page_addr);
- } else {
- set_cmd_regs(mtd, command, command);
- set_addr(mtd, 0, page_addr);
- }
- flctl->read_bytes = mtd->oobsize;
- goto read_normal_exit;
-
- case NAND_CMD_RNDOUT:
- if (flctl->hwecc)
- break;
-
- if (flctl->page_size)
- set_cmd_regs(mtd, command, (NAND_CMD_RNDOUTSTART << 8)
- | command);
- else
- set_cmd_regs(mtd, command, command);
-
- set_addr(mtd, column, 0);
-
- flctl->read_bytes = mtd->writesize + mtd->oobsize - column;
- goto read_normal_exit;
-
- case NAND_CMD_READID:
- set_cmd_regs(mtd, command, command);
-
- /* READID is always performed using an 8-bit bus */
- if (flctl->chip.options & NAND_BUSWIDTH_16)
- column <<= 1;
- set_addr(mtd, column, 0);
-
- flctl->read_bytes = 8;
- writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */
- empty_fifo(flctl);
- start_translation(flctl);
- read_fiforeg(flctl, flctl->read_bytes, 0);
- wait_completion(flctl);
- break;
-
- case NAND_CMD_ERASE1:
- flctl->erase1_page_addr = page_addr;
- break;
-
- case NAND_CMD_ERASE2:
- set_cmd_regs(mtd, NAND_CMD_ERASE1,
- (command << 8) | NAND_CMD_ERASE1);
- set_addr(mtd, -1, flctl->erase1_page_addr);
- start_translation(flctl);
- wait_completion(flctl);
- break;
-
- case NAND_CMD_SEQIN:
- if (!flctl->page_size) {
- /* output read command */
- if (column >= mtd->writesize) {
- column -= mtd->writesize;
- read_cmd = NAND_CMD_READOOB;
- } else if (column < 256) {
- read_cmd = NAND_CMD_READ0;
- } else {
- column -= 256;
- read_cmd = NAND_CMD_READ1;
- }
- }
- flctl->seqin_column = column;
- flctl->seqin_page_addr = page_addr;
- flctl->seqin_read_cmd = read_cmd;
- break;
-
- case NAND_CMD_PAGEPROG:
- empty_fifo(flctl);
- if (!flctl->page_size) {
- set_cmd_regs(mtd, NAND_CMD_SEQIN,
- flctl->seqin_read_cmd);
- set_addr(mtd, -1, -1);
- writel(0, FLDTCNTR(flctl)); /* set 0 size */
- start_translation(flctl);
- wait_completion(flctl);
- }
- if (flctl->hwecc) {
- /* write page with hwecc */
- if (flctl->seqin_column == mtd->writesize)
- execmd_write_oob(mtd);
- else if (!flctl->seqin_column)
- execmd_write_page_sector(mtd);
- else
- printk(KERN_ERR "Invalid address !?\n");
- break;
- }
- set_cmd_regs(mtd, command, (command << 8) | NAND_CMD_SEQIN);
- set_addr(mtd, flctl->seqin_column, flctl->seqin_page_addr);
- writel(flctl->index, FLDTCNTR(flctl)); /* set write size */
- start_translation(flctl);
- write_fiforeg(flctl, flctl->index, 0);
- wait_completion(flctl);
- break;
-
- case NAND_CMD_STATUS:
- set_cmd_regs(mtd, command, command);
- set_addr(mtd, -1, -1);
-
- flctl->read_bytes = 1;
- writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */
- start_translation(flctl);
- read_datareg(flctl, 0); /* read and end */
- break;
-
- case NAND_CMD_RESET:
- set_cmd_regs(mtd, command, command);
- set_addr(mtd, -1, -1);
-
- writel(0, FLDTCNTR(flctl)); /* set 0 size */
- start_translation(flctl);
- wait_completion(flctl);
- break;
-
- default:
- break;
- }
- goto runtime_exit;
-
-read_normal_exit:
- writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */
- empty_fifo(flctl);
- start_translation(flctl);
- read_fiforeg(flctl, flctl->read_bytes, 0);
- wait_completion(flctl);
-runtime_exit:
- pm_runtime_put_sync(&flctl->pdev->dev);
- return;
-}
-
-static void flctl_select_chip(struct mtd_info *mtd, int chipnr)
-{
- struct sh_flctl *flctl = mtd_to_flctl(mtd);
- int ret;
-
- switch (chipnr) {
- case -1:
- flctl->flcmncr_base &= ~CE0_ENABLE;
-
- pm_runtime_get_sync(&flctl->pdev->dev);
- writel(flctl->flcmncr_base, FLCMNCR(flctl));
-
- if (flctl->qos_request) {
- dev_pm_qos_remove_request(&flctl->pm_qos);
- flctl->qos_request = 0;
- }
-
- pm_runtime_put_sync(&flctl->pdev->dev);
- break;
- case 0:
- flctl->flcmncr_base |= CE0_ENABLE;
-
- if (!flctl->qos_request) {
- ret = dev_pm_qos_add_request(&flctl->pdev->dev,
- &flctl->pm_qos,
- DEV_PM_QOS_RESUME_LATENCY,
- 100);
- if (ret < 0)
- dev_err(&flctl->pdev->dev,
- "PM QoS request failed: %d\n", ret);
- flctl->qos_request = 1;
- }
-
- if (flctl->holden) {
- pm_runtime_get_sync(&flctl->pdev->dev);
- writel(HOLDEN, FLHOLDCR(flctl));
- pm_runtime_put_sync(&flctl->pdev->dev);
- }
- break;
- default:
- BUG();
- }
-}
-
-static void flctl_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
-{
- struct sh_flctl *flctl = mtd_to_flctl(mtd);
-
- memcpy(&flctl->done_buff[flctl->index], buf, len);
- flctl->index += len;
-}
-
-static uint8_t flctl_read_byte(struct mtd_info *mtd)
-{
- struct sh_flctl *flctl = mtd_to_flctl(mtd);
- uint8_t data;
-
- data = flctl->done_buff[flctl->index];
- flctl->index++;
- return data;
-}
-
-static uint16_t flctl_read_word(struct mtd_info *mtd)
-{
- struct sh_flctl *flctl = mtd_to_flctl(mtd);
- uint16_t *buf = (uint16_t *)&flctl->done_buff[flctl->index];
-
- flctl->index += 2;
- return *buf;
-}
-
-static void flctl_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
-{
- struct sh_flctl *flctl = mtd_to_flctl(mtd);
-
- memcpy(buf, &flctl->done_buff[flctl->index], len);
- flctl->index += len;
-}
-
-static int flctl_chip_init_tail(struct mtd_info *mtd)
-{
- struct sh_flctl *flctl = mtd_to_flctl(mtd);
- struct nand_chip *chip = &flctl->chip;
-
- if (mtd->writesize == 512) {
- flctl->page_size = 0;
- if (chip->chipsize > (32 << 20)) {
- /* big than 32MB */
- flctl->rw_ADRCNT = ADRCNT_4;
- flctl->erase_ADRCNT = ADRCNT_3;
- } else if (chip->chipsize > (2 << 16)) {
- /* big than 128KB */
- flctl->rw_ADRCNT = ADRCNT_3;
- flctl->erase_ADRCNT = ADRCNT_2;
- } else {
- flctl->rw_ADRCNT = ADRCNT_2;
- flctl->erase_ADRCNT = ADRCNT_1;
- }
- } else {
- flctl->page_size = 1;
- if (chip->chipsize > (128 << 20)) {
- /* big than 128MB */
- flctl->rw_ADRCNT = ADRCNT2_E;
- flctl->erase_ADRCNT = ADRCNT_3;
- } else if (chip->chipsize > (8 << 16)) {
- /* big than 512KB */
- flctl->rw_ADRCNT = ADRCNT_4;
- flctl->erase_ADRCNT = ADRCNT_2;
- } else {
- flctl->rw_ADRCNT = ADRCNT_3;
- flctl->erase_ADRCNT = ADRCNT_1;
- }
- }
-
- if (flctl->hwecc) {
- if (mtd->writesize == 512) {
- mtd_set_ooblayout(mtd, &flctl_4secc_oob_smallpage_ops);
- chip->badblock_pattern = &flctl_4secc_smallpage;
- } else {
- mtd_set_ooblayout(mtd, &flctl_4secc_oob_largepage_ops);
- chip->badblock_pattern = &flctl_4secc_largepage;
- }
-
- chip->ecc.size = 512;
- chip->ecc.bytes = 10;
- chip->ecc.strength = 4;
- chip->ecc.read_page = flctl_read_page_hwecc;
- chip->ecc.write_page = flctl_write_page_hwecc;
- chip->ecc.mode = NAND_ECC_HW;
-
- /* 4 symbols ECC enabled */
- flctl->flcmncr_base |= _4ECCEN;
- } else {
- chip->ecc.mode = NAND_ECC_SOFT;
- chip->ecc.algo = NAND_ECC_HAMMING;
- }
-
- return 0;
-}
-
-static irqreturn_t flctl_handle_flste(int irq, void *dev_id)
-{
- struct sh_flctl *flctl = dev_id;
-
- dev_err(&flctl->pdev->dev, "flste irq: %x\n", readl(FLINTDMACR(flctl)));
- writel(flctl->flintdmacr_base, FLINTDMACR(flctl));
-
- return IRQ_HANDLED;
-}
-
-struct flctl_soc_config {
- unsigned long flcmncr_val;
- unsigned has_hwecc:1;
- unsigned use_holden:1;
-};
-
-static struct flctl_soc_config flctl_sh7372_config = {
- .flcmncr_val = CLK_16B_12L_4H | TYPESEL_SET | SHBUSSEL,
- .has_hwecc = 1,
- .use_holden = 1,
-};
-
-static const struct of_device_id of_flctl_match[] = {
- { .compatible = "renesas,shmobile-flctl-sh7372",
- .data = &flctl_sh7372_config },
- {},
-};
-MODULE_DEVICE_TABLE(of, of_flctl_match);
-
-static struct sh_flctl_platform_data *flctl_parse_dt(struct device *dev)
-{
- const struct flctl_soc_config *config;
- struct sh_flctl_platform_data *pdata;
-
- config = of_device_get_match_data(dev);
- if (!config) {
- dev_err(dev, "%s: no OF configuration attached\n", __func__);
- return NULL;
- }
-
- pdata = devm_kzalloc(dev, sizeof(struct sh_flctl_platform_data),
- GFP_KERNEL);
- if (!pdata)
- return NULL;
-
- /* set SoC specific options */
- pdata->flcmncr_val = config->flcmncr_val;
- pdata->has_hwecc = config->has_hwecc;
- pdata->use_holden = config->use_holden;
-
- return pdata;
-}
-
-static int flctl_probe(struct platform_device *pdev)
-{
- struct resource *res;
- struct sh_flctl *flctl;
- struct mtd_info *flctl_mtd;
- struct nand_chip *nand;
- struct sh_flctl_platform_data *pdata;
- int ret;
- int irq;
-
- flctl = devm_kzalloc(&pdev->dev, sizeof(struct sh_flctl), GFP_KERNEL);
- if (!flctl)
- return -ENOMEM;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- flctl->reg = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(flctl->reg))
- return PTR_ERR(flctl->reg);
- flctl->fifo = res->start + 0x24; /* FLDTFIFO */
-
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "failed to get flste irq data: %d\n", irq);
- return irq;
- }
-
- ret = devm_request_irq(&pdev->dev, irq, flctl_handle_flste, IRQF_SHARED,
- "flste", flctl);
- if (ret) {
- dev_err(&pdev->dev, "request interrupt failed.\n");
- return ret;
- }
-
- if (pdev->dev.of_node)
- pdata = flctl_parse_dt(&pdev->dev);
- else
- pdata = dev_get_platdata(&pdev->dev);
-
- if (!pdata) {
- dev_err(&pdev->dev, "no setup data defined\n");
- return -EINVAL;
- }
-
- platform_set_drvdata(pdev, flctl);
- nand = &flctl->chip;
- flctl_mtd = nand_to_mtd(nand);
- nand_set_flash_node(nand, pdev->dev.of_node);
- flctl_mtd->dev.parent = &pdev->dev;
- flctl->pdev = pdev;
- flctl->hwecc = pdata->has_hwecc;
- flctl->holden = pdata->use_holden;
- flctl->flcmncr_base = pdata->flcmncr_val;
- flctl->flintdmacr_base = flctl->hwecc ? (STERINTE | ECERB) : STERINTE;
-
- /* Set address of hardware control function */
- /* 20 us command delay time */
- nand->chip_delay = 20;
-
- nand->read_byte = flctl_read_byte;
- nand->read_word = flctl_read_word;
- nand->write_buf = flctl_write_buf;
- nand->read_buf = flctl_read_buf;
- nand->select_chip = flctl_select_chip;
- nand->cmdfunc = flctl_cmdfunc;
- nand->onfi_set_features = nand_onfi_get_set_features_notsupp;
- nand->onfi_get_features = nand_onfi_get_set_features_notsupp;
-
- if (pdata->flcmncr_val & SEL_16BIT)
- nand->options |= NAND_BUSWIDTH_16;
-
- pm_runtime_enable(&pdev->dev);
- pm_runtime_resume(&pdev->dev);
-
- flctl_setup_dma(flctl);
-
- ret = nand_scan_ident(flctl_mtd, 1, NULL);
- if (ret)
- goto err_chip;
-
- if (nand->options & NAND_BUSWIDTH_16) {
- /*
- * NAND_BUSWIDTH_16 may have been set by nand_scan_ident().
- * Add the SEL_16BIT flag in pdata->flcmncr_val and re-assign
- * flctl->flcmncr_base to pdata->flcmncr_val.
- */
- pdata->flcmncr_val |= SEL_16BIT;
- flctl->flcmncr_base = pdata->flcmncr_val;
- }
-
- ret = flctl_chip_init_tail(flctl_mtd);
- if (ret)
- goto err_chip;
-
- ret = nand_scan_tail(flctl_mtd);
- if (ret)
- goto err_chip;
-
- ret = mtd_device_register(flctl_mtd, pdata->parts, pdata->nr_parts);
-
- return 0;
-
-err_chip:
- flctl_release_dma(flctl);
- pm_runtime_disable(&pdev->dev);
- return ret;
-}
-
-static int flctl_remove(struct platform_device *pdev)
-{
- struct sh_flctl *flctl = platform_get_drvdata(pdev);
-
- flctl_release_dma(flctl);
- nand_release(nand_to_mtd(&flctl->chip));
- pm_runtime_disable(&pdev->dev);
-
- return 0;
-}
-
-static struct platform_driver flctl_driver = {
- .remove = flctl_remove,
- .driver = {
- .name = "sh_flctl",
- .of_match_table = of_match_ptr(of_flctl_match),
- },
-};
-
-module_platform_driver_probe(flctl_driver, flctl_probe);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Yoshihiro Shimoda");
-MODULE_DESCRIPTION("SuperH FLCTL driver");
-MODULE_ALIAS("platform:sh_flctl");
diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c
deleted file mode 100644
index f59c455d9f51..000000000000
--- a/drivers/mtd/nand/sharpsl.c
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * drivers/mtd/nand/sharpsl.c
- *
- * Copyright (C) 2004 Richard Purdie
- * Copyright (C) 2008 Dmitry Baryshkov
- *
- * Based on Sharp's NAND driver sharp_sl.c
- *
- * 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.
- *
- */
-
-#include <linux/genhd.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand_ecc.h>
-#include <linux/mtd/partitions.h>
-#include <linux/mtd/sharpsl.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-
-#include <asm/io.h>
-#include <mach/hardware.h>
-#include <asm/mach-types.h>
-
-struct sharpsl_nand {
- struct nand_chip chip;
-
- void __iomem *io;
-};
-
-static inline struct sharpsl_nand *mtd_to_sharpsl(struct mtd_info *mtd)
-{
- return container_of(mtd_to_nand(mtd), struct sharpsl_nand, chip);
-}
-
-/* register offset */
-#define ECCLPLB 0x00 /* line parity 7 - 0 bit */
-#define ECCLPUB 0x04 /* line parity 15 - 8 bit */
-#define ECCCP 0x08 /* column parity 5 - 0 bit */
-#define ECCCNTR 0x0C /* ECC byte counter */
-#define ECCCLRR 0x10 /* cleare ECC */
-#define FLASHIO 0x14 /* Flash I/O */
-#define FLASHCTL 0x18 /* Flash Control */
-
-/* Flash control bit */
-#define FLRYBY (1 << 5)
-#define FLCE1 (1 << 4)
-#define FLWP (1 << 3)
-#define FLALE (1 << 2)
-#define FLCLE (1 << 1)
-#define FLCE0 (1 << 0)
-
-/*
- * hardware specific access to control-lines
- * ctrl:
- * NAND_CNE: bit 0 -> ! bit 0 & 4
- * NAND_CLE: bit 1 -> bit 1
- * NAND_ALE: bit 2 -> bit 2
- *
- */
-static void sharpsl_nand_hwcontrol(struct mtd_info *mtd, int cmd,
- unsigned int ctrl)
-{
- struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd);
- struct nand_chip *chip = mtd_to_nand(mtd);
-
- if (ctrl & NAND_CTRL_CHANGE) {
- unsigned char bits = ctrl & 0x07;
-
- bits |= (ctrl & 0x01) << 4;
-
- bits ^= 0x11;
-
- writeb((readb(sharpsl->io + FLASHCTL) & ~0x17) | bits, sharpsl->io + FLASHCTL);
- }
-
- if (cmd != NAND_CMD_NONE)
- writeb(cmd, chip->IO_ADDR_W);
-}
-
-static int sharpsl_nand_dev_ready(struct mtd_info *mtd)
-{
- struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd);
- return !((readb(sharpsl->io + FLASHCTL) & FLRYBY) == 0);
-}
-
-static void sharpsl_nand_enable_hwecc(struct mtd_info *mtd, int mode)
-{
- struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd);
- writeb(0, sharpsl->io + ECCCLRR);
-}
-
-static int sharpsl_nand_calculate_ecc(struct mtd_info *mtd, const u_char * dat, u_char * ecc_code)
-{
- struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd);
- ecc_code[0] = ~readb(sharpsl->io + ECCLPUB);
- ecc_code[1] = ~readb(sharpsl->io + ECCLPLB);
- ecc_code[2] = (~readb(sharpsl->io + ECCCP) << 2) | 0x03;
- return readb(sharpsl->io + ECCCNTR) != 0;
-}
-
-/*
- * Main initialization routine
- */
-static int sharpsl_nand_probe(struct platform_device *pdev)
-{
- struct nand_chip *this;
- struct mtd_info *mtd;
- struct resource *r;
- int err = 0;
- struct sharpsl_nand *sharpsl;
- struct sharpsl_nand_platform_data *data = dev_get_platdata(&pdev->dev);
-
- if (!data) {
- dev_err(&pdev->dev, "no platform data!\n");
- return -EINVAL;
- }
-
- /* Allocate memory for MTD device structure and private data */
- sharpsl = kzalloc(sizeof(struct sharpsl_nand), GFP_KERNEL);
- if (!sharpsl)
- return -ENOMEM;
-
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!r) {
- dev_err(&pdev->dev, "no io memory resource defined!\n");
- err = -ENODEV;
- goto err_get_res;
- }
-
- /* map physical address */
- sharpsl->io = ioremap(r->start, resource_size(r));
- if (!sharpsl->io) {
- dev_err(&pdev->dev, "ioremap to access Sharp SL NAND chip failed\n");
- err = -EIO;
- goto err_ioremap;
- }
-
- /* Get pointer to private data */
- this = (struct nand_chip *)(&sharpsl->chip);
-
- /* Link the private data with the MTD structure */
- mtd = nand_to_mtd(this);
- mtd->dev.parent = &pdev->dev;
- mtd_set_ooblayout(mtd, data->ecc_layout);
-
- platform_set_drvdata(pdev, sharpsl);
-
- /*
- * PXA initialize
- */
- writeb(readb(sharpsl->io + FLASHCTL) | FLWP, sharpsl->io + FLASHCTL);
-
- /* Set address of NAND IO lines */
- this->IO_ADDR_R = sharpsl->io + FLASHIO;
- this->IO_ADDR_W = sharpsl->io + FLASHIO;
- /* Set address of hardware control function */
- this->cmd_ctrl = sharpsl_nand_hwcontrol;
- this->dev_ready = sharpsl_nand_dev_ready;
- /* 15 us command delay time */
- this->chip_delay = 15;
- /* set eccmode using hardware ECC */
- this->ecc.mode = NAND_ECC_HW;
- this->ecc.size = 256;
- this->ecc.bytes = 3;
- this->ecc.strength = 1;
- this->badblock_pattern = data->badblock_pattern;
- this->ecc.hwctl = sharpsl_nand_enable_hwecc;
- this->ecc.calculate = sharpsl_nand_calculate_ecc;
- this->ecc.correct = nand_correct_data;
-
- /* Scan to find existence of the device */
- err = nand_scan(mtd, 1);
- if (err)
- goto err_scan;
-
- /* Register the partitions */
- mtd->name = "sharpsl-nand";
-
- err = mtd_device_parse_register(mtd, data->part_parsers, NULL,
- data->partitions, data->nr_partitions);
- if (err)
- goto err_add;
-
- /* Return happy */
- return 0;
-
-err_add:
- nand_release(mtd);
-
-err_scan:
- iounmap(sharpsl->io);
-err_ioremap:
-err_get_res:
- kfree(sharpsl);
- return err;
-}
-
-/*
- * Clean up routine
- */
-static int sharpsl_nand_remove(struct platform_device *pdev)
-{
- struct sharpsl_nand *sharpsl = platform_get_drvdata(pdev);
-
- /* Release resources, unregister device */
- nand_release(nand_to_mtd(&sharpsl->chip));
-
- iounmap(sharpsl->io);
-
- /* Free the MTD device structure */
- kfree(sharpsl);
-
- return 0;
-}
-
-static struct platform_driver sharpsl_nand_driver = {
- .driver = {
- .name = "sharpsl-nand",
- },
- .probe = sharpsl_nand_probe,
- .remove = sharpsl_nand_remove,
-};
-
-module_platform_driver(sharpsl_nand_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
-MODULE_DESCRIPTION("Device specific logic for NAND flash on Sharp SL-C7xx Series");
diff --git a/drivers/mtd/nand/sm_common.c b/drivers/mtd/nand/sm_common.c
deleted file mode 100644
index c378705c6e2b..000000000000
--- a/drivers/mtd/nand/sm_common.c
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright © 2009 - Maxim Levitsky
- * Common routines & support for xD format
- *
- * 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.
- */
-#include <linux/kernel.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/module.h>
-#include <linux/sizes.h>
-#include "sm_common.h"
-
-static int oob_sm_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- if (section > 1)
- return -ERANGE;
-
- oobregion->length = 3;
- oobregion->offset = ((section + 1) * 8) - 3;
-
- return 0;
-}
-
-static int oob_sm_ooblayout_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- switch (section) {
- case 0:
- /* reserved */
- oobregion->offset = 0;
- oobregion->length = 4;
- break;
- case 1:
- /* LBA1 */
- oobregion->offset = 6;
- oobregion->length = 2;
- break;
- case 2:
- /* LBA2 */
- oobregion->offset = 11;
- oobregion->length = 2;
- break;
- default:
- return -ERANGE;
- }
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops oob_sm_ops = {
- .ecc = oob_sm_ooblayout_ecc,
- .free = oob_sm_ooblayout_free,
-};
-
-/* NOTE: This layout is is not compatabable with SmartMedia, */
-/* because the 256 byte devices have page depenent oob layout */
-/* However it does preserve the bad block markers */
-/* If you use smftl, it will bypass this and work correctly */
-/* If you not, then you break SmartMedia compliance anyway */
-
-static int oob_sm_small_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- if (section)
- return -ERANGE;
-
- oobregion->length = 3;
- oobregion->offset = 0;
-
- return 0;
-}
-
-static int oob_sm_small_ooblayout_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- switch (section) {
- case 0:
- /* reserved */
- oobregion->offset = 3;
- oobregion->length = 2;
- break;
- case 1:
- /* LBA1 */
- oobregion->offset = 6;
- oobregion->length = 2;
- break;
- default:
- return -ERANGE;
- }
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops oob_sm_small_ops = {
- .ecc = oob_sm_small_ooblayout_ecc,
- .free = oob_sm_small_ooblayout_free,
-};
-
-static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs)
-{
- struct mtd_oob_ops ops;
- struct sm_oob oob;
- int ret;
-
- memset(&oob, -1, SM_OOB_SIZE);
- oob.block_status = 0x0F;
-
- /* As long as this function is called on erase block boundaries
- it will work correctly for 256 byte nand */
- ops.mode = MTD_OPS_PLACE_OOB;
- ops.ooboffs = 0;
- ops.ooblen = mtd->oobsize;
- ops.oobbuf = (void *)&oob;
- ops.datbuf = NULL;
-
-
- ret = mtd_write_oob(mtd, ofs, &ops);
- if (ret < 0 || ops.oobretlen != SM_OOB_SIZE) {
- printk(KERN_NOTICE
- "sm_common: can't mark sector at %i as bad\n",
- (int)ofs);
- return -EIO;
- }
-
- return 0;
-}
-
-static struct nand_flash_dev nand_smartmedia_flash_ids[] = {
- LEGACY_ID_NAND("SmartMedia 2MiB 3,3V ROM", 0x5d, 2, SZ_8K, NAND_ROM),
- LEGACY_ID_NAND("SmartMedia 4MiB 3,3V", 0xe3, 4, SZ_8K, 0),
- LEGACY_ID_NAND("SmartMedia 4MiB 3,3/5V", 0xe5, 4, SZ_8K, 0),
- LEGACY_ID_NAND("SmartMedia 4MiB 5V", 0x6b, 4, SZ_8K, 0),
- LEGACY_ID_NAND("SmartMedia 4MiB 3,3V ROM", 0xd5, 4, SZ_8K, NAND_ROM),
- LEGACY_ID_NAND("SmartMedia 8MiB 3,3V", 0xe6, 8, SZ_8K, 0),
- LEGACY_ID_NAND("SmartMedia 8MiB 3,3V ROM", 0xd6, 8, SZ_8K, NAND_ROM),
- LEGACY_ID_NAND("SmartMedia 16MiB 3,3V", 0x73, 16, SZ_16K, 0),
- LEGACY_ID_NAND("SmartMedia 16MiB 3,3V ROM", 0x57, 16, SZ_16K, NAND_ROM),
- LEGACY_ID_NAND("SmartMedia 32MiB 3,3V", 0x75, 32, SZ_16K, 0),
- LEGACY_ID_NAND("SmartMedia 32MiB 3,3V ROM", 0x58, 32, SZ_16K, NAND_ROM),
- LEGACY_ID_NAND("SmartMedia 64MiB 3,3V", 0x76, 64, SZ_16K, 0),
- LEGACY_ID_NAND("SmartMedia 64MiB 3,3V ROM", 0xd9, 64, SZ_16K, NAND_ROM),
- LEGACY_ID_NAND("SmartMedia 128MiB 3,3V", 0x79, 128, SZ_16K, 0),
- LEGACY_ID_NAND("SmartMedia 128MiB 3,3V ROM", 0xda, 128, SZ_16K, NAND_ROM),
- LEGACY_ID_NAND("SmartMedia 256MiB 3, 3V", 0x71, 256, SZ_16K, 0),
- LEGACY_ID_NAND("SmartMedia 256MiB 3,3V ROM", 0x5b, 256, SZ_16K, NAND_ROM),
- {NULL}
-};
-
-static struct nand_flash_dev nand_xd_flash_ids[] = {
- LEGACY_ID_NAND("xD 16MiB 3,3V", 0x73, 16, SZ_16K, 0),
- LEGACY_ID_NAND("xD 32MiB 3,3V", 0x75, 32, SZ_16K, 0),
- LEGACY_ID_NAND("xD 64MiB 3,3V", 0x76, 64, SZ_16K, 0),
- LEGACY_ID_NAND("xD 128MiB 3,3V", 0x79, 128, SZ_16K, 0),
- LEGACY_ID_NAND("xD 256MiB 3,3V", 0x71, 256, SZ_16K, NAND_BROKEN_XD),
- LEGACY_ID_NAND("xD 512MiB 3,3V", 0xdc, 512, SZ_16K, NAND_BROKEN_XD),
- LEGACY_ID_NAND("xD 1GiB 3,3V", 0xd3, 1024, SZ_16K, NAND_BROKEN_XD),
- LEGACY_ID_NAND("xD 2GiB 3,3V", 0xd5, 2048, SZ_16K, NAND_BROKEN_XD),
- {NULL}
-};
-
-int sm_register_device(struct mtd_info *mtd, int smartmedia)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- int ret;
-
- chip->options |= NAND_SKIP_BBTSCAN;
-
- /* Scan for card properties */
- ret = nand_scan_ident(mtd, 1, smartmedia ?
- nand_smartmedia_flash_ids : nand_xd_flash_ids);
-
- if (ret)
- return ret;
-
- /* Bad block marker position */
- chip->badblockpos = 0x05;
- chip->badblockbits = 7;
- chip->block_markbad = sm_block_markbad;
-
- /* ECC layout */
- if (mtd->writesize == SM_SECTOR_SIZE)
- mtd_set_ooblayout(mtd, &oob_sm_ops);
- else if (mtd->writesize == SM_SMALL_PAGE)
- mtd_set_ooblayout(mtd, &oob_sm_small_ops);
- else
- return -ENODEV;
-
- ret = nand_scan_tail(mtd);
-
- if (ret)
- return ret;
-
- return mtd_device_register(mtd, NULL, 0);
-}
-EXPORT_SYMBOL_GPL(sm_register_device);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>");
-MODULE_DESCRIPTION("Common SmartMedia/xD functions");
diff --git a/drivers/mtd/nand/socrates_nand.c b/drivers/mtd/nand/socrates_nand.c
deleted file mode 100644
index 575997d0ef8a..000000000000
--- a/drivers/mtd/nand/socrates_nand.c
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * drivers/mtd/nand/socrates_nand.c
- *
- * Copyright © 2008 Ilya Yanok, Emcraft Systems
- *
- *
- * 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.
- *
- */
-
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/partitions.h>
-#include <linux/of_address.h>
-#include <linux/of_platform.h>
-#include <linux/io.h>
-
-#define FPGA_NAND_CMD_MASK (0x7 << 28)
-#define FPGA_NAND_CMD_COMMAND (0x0 << 28)
-#define FPGA_NAND_CMD_ADDR (0x1 << 28)
-#define FPGA_NAND_CMD_READ (0x2 << 28)
-#define FPGA_NAND_CMD_WRITE (0x3 << 28)
-#define FPGA_NAND_BUSY (0x1 << 15)
-#define FPGA_NAND_ENABLE (0x1 << 31)
-#define FPGA_NAND_DATA_SHIFT 16
-
-struct socrates_nand_host {
- struct nand_chip nand_chip;
- void __iomem *io_base;
- struct device *dev;
-};
-
-/**
- * socrates_nand_write_buf - write buffer to chip
- * @mtd: MTD device structure
- * @buf: data buffer
- * @len: number of bytes to write
- */
-static void socrates_nand_write_buf(struct mtd_info *mtd,
- const uint8_t *buf, int len)
-{
- int i;
- struct nand_chip *this = mtd_to_nand(mtd);
- struct socrates_nand_host *host = nand_get_controller_data(this);
-
- for (i = 0; i < len; i++) {
- out_be32(host->io_base, FPGA_NAND_ENABLE |
- FPGA_NAND_CMD_WRITE |
- (buf[i] << FPGA_NAND_DATA_SHIFT));
- }
-}
-
-/**
- * socrates_nand_read_buf - read chip data into buffer
- * @mtd: MTD device structure
- * @buf: buffer to store date
- * @len: number of bytes to read
- */
-static void socrates_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
-{
- int i;
- struct nand_chip *this = mtd_to_nand(mtd);
- struct socrates_nand_host *host = nand_get_controller_data(this);
- uint32_t val;
-
- val = FPGA_NAND_ENABLE | FPGA_NAND_CMD_READ;
-
- out_be32(host->io_base, val);
- for (i = 0; i < len; i++) {
- buf[i] = (in_be32(host->io_base) >>
- FPGA_NAND_DATA_SHIFT) & 0xff;
- }
-}
-
-/**
- * socrates_nand_read_byte - read one byte from the chip
- * @mtd: MTD device structure
- */
-static uint8_t socrates_nand_read_byte(struct mtd_info *mtd)
-{
- uint8_t byte;
- socrates_nand_read_buf(mtd, &byte, sizeof(byte));
- return byte;
-}
-
-/**
- * socrates_nand_read_word - read one word from the chip
- * @mtd: MTD device structure
- */
-static uint16_t socrates_nand_read_word(struct mtd_info *mtd)
-{
- uint16_t word;
- socrates_nand_read_buf(mtd, (uint8_t *)&word, sizeof(word));
- return word;
-}
-
-/*
- * Hardware specific access to control-lines
- */
-static void socrates_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
- unsigned int ctrl)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct socrates_nand_host *host = nand_get_controller_data(nand_chip);
- uint32_t val;
-
- if (cmd == NAND_CMD_NONE)
- return;
-
- if (ctrl & NAND_CLE)
- val = FPGA_NAND_CMD_COMMAND;
- else
- val = FPGA_NAND_CMD_ADDR;
-
- if (ctrl & NAND_NCE)
- val |= FPGA_NAND_ENABLE;
-
- val |= (cmd & 0xff) << FPGA_NAND_DATA_SHIFT;
-
- out_be32(host->io_base, val);
-}
-
-/*
- * Read the Device Ready pin.
- */
-static int socrates_nand_device_ready(struct mtd_info *mtd)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct socrates_nand_host *host = nand_get_controller_data(nand_chip);
-
- if (in_be32(host->io_base) & FPGA_NAND_BUSY)
- return 0; /* busy */
- return 1;
-}
-
-/*
- * Probe for the NAND device.
- */
-static int socrates_nand_probe(struct platform_device *ofdev)
-{
- struct socrates_nand_host *host;
- struct mtd_info *mtd;
- struct nand_chip *nand_chip;
- int res;
-
- /* Allocate memory for the device structure (and zero it) */
- host = devm_kzalloc(&ofdev->dev, sizeof(*host), GFP_KERNEL);
- if (!host)
- return -ENOMEM;
-
- host->io_base = of_iomap(ofdev->dev.of_node, 0);
- if (host->io_base == NULL) {
- dev_err(&ofdev->dev, "ioremap failed\n");
- return -EIO;
- }
-
- nand_chip = &host->nand_chip;
- mtd = nand_to_mtd(nand_chip);
- host->dev = &ofdev->dev;
-
- /* link the private data structures */
- nand_set_controller_data(nand_chip, host);
- nand_set_flash_node(nand_chip, ofdev->dev.of_node);
- mtd->name = "socrates_nand";
- mtd->dev.parent = &ofdev->dev;
-
- /*should never be accessed directly */
- nand_chip->IO_ADDR_R = (void *)0xdeadbeef;
- nand_chip->IO_ADDR_W = (void *)0xdeadbeef;
-
- nand_chip->cmd_ctrl = socrates_nand_cmd_ctrl;
- nand_chip->read_byte = socrates_nand_read_byte;
- nand_chip->read_word = socrates_nand_read_word;
- nand_chip->write_buf = socrates_nand_write_buf;
- nand_chip->read_buf = socrates_nand_read_buf;
- nand_chip->dev_ready = socrates_nand_device_ready;
-
- nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */
- nand_chip->ecc.algo = NAND_ECC_HAMMING;
-
- /* TODO: I have no idea what real delay is. */
- nand_chip->chip_delay = 20; /* 20us command delay time */
-
- dev_set_drvdata(&ofdev->dev, host);
-
- res = nand_scan(mtd, 1);
- if (res)
- goto out;
-
- res = mtd_device_register(mtd, NULL, 0);
- if (!res)
- return res;
-
- nand_release(mtd);
-
-out:
- iounmap(host->io_base);
- return res;
-}
-
-/*
- * Remove a NAND device.
- */
-static int socrates_nand_remove(struct platform_device *ofdev)
-{
- struct socrates_nand_host *host = dev_get_drvdata(&ofdev->dev);
- struct mtd_info *mtd = nand_to_mtd(&host->nand_chip);
-
- nand_release(mtd);
-
- iounmap(host->io_base);
-
- return 0;
-}
-
-static const struct of_device_id socrates_nand_match[] =
-{
- {
- .compatible = "abb,socrates-nand",
- },
- {},
-};
-
-MODULE_DEVICE_TABLE(of, socrates_nand_match);
-
-static struct platform_driver socrates_nand_driver = {
- .driver = {
- .name = "socrates_nand",
- .of_match_table = socrates_nand_match,
- },
- .probe = socrates_nand_probe,
- .remove = socrates_nand_remove,
-};
-
-module_platform_driver(socrates_nand_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Ilya Yanok");
-MODULE_DESCRIPTION("NAND driver for Socrates board");
diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
deleted file mode 100644
index f5a55c63935c..000000000000
--- a/drivers/mtd/nand/sunxi_nand.c
+++ /dev/null
@@ -1,2321 +0,0 @@
-/*
- * Copyright (C) 2013 Boris BREZILLON <b.brezillon.dev@gmail.com>
- *
- * Derived from:
- * https://github.com/yuq/sunxi-nfc-mtd
- * Copyright (C) 2013 Qiang Yu <yuq825@gmail.com>
- *
- * https://github.com/hno/Allwinner-Info
- * Copyright (C) 2013 Henrik Nordström <Henrik Nordström>
- *
- * Copyright (C) 2013 Dmitriy B. <rzk333@gmail.com>
- * Copyright (C) 2013 Sergey Lapin <slapin@ossfans.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.
- */
-
-#include <linux/dma-mapping.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/platform_device.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/of_gpio.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/partitions.h>
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/dmaengine.h>
-#include <linux/gpio.h>
-#include <linux/interrupt.h>
-#include <linux/iopoll.h>
-#include <linux/reset.h>
-
-#define NFC_REG_CTL 0x0000
-#define NFC_REG_ST 0x0004
-#define NFC_REG_INT 0x0008
-#define NFC_REG_TIMING_CTL 0x000C
-#define NFC_REG_TIMING_CFG 0x0010
-#define NFC_REG_ADDR_LOW 0x0014
-#define NFC_REG_ADDR_HIGH 0x0018
-#define NFC_REG_SECTOR_NUM 0x001C
-#define NFC_REG_CNT 0x0020
-#define NFC_REG_CMD 0x0024
-#define NFC_REG_RCMD_SET 0x0028
-#define NFC_REG_WCMD_SET 0x002C
-#define NFC_REG_IO_DATA 0x0030
-#define NFC_REG_ECC_CTL 0x0034
-#define NFC_REG_ECC_ST 0x0038
-#define NFC_REG_DEBUG 0x003C
-#define NFC_REG_ECC_ERR_CNT(x) ((0x0040 + (x)) & ~0x3)
-#define NFC_REG_USER_DATA(x) (0x0050 + ((x) * 4))
-#define NFC_REG_SPARE_AREA 0x00A0
-#define NFC_REG_PAT_ID 0x00A4
-#define NFC_RAM0_BASE 0x0400
-#define NFC_RAM1_BASE 0x0800
-
-/* define bit use in NFC_CTL */
-#define NFC_EN BIT(0)
-#define NFC_RESET BIT(1)
-#define NFC_BUS_WIDTH_MSK BIT(2)
-#define NFC_BUS_WIDTH_8 (0 << 2)
-#define NFC_BUS_WIDTH_16 (1 << 2)
-#define NFC_RB_SEL_MSK BIT(3)
-#define NFC_RB_SEL(x) ((x) << 3)
-#define NFC_CE_SEL_MSK GENMASK(26, 24)
-#define NFC_CE_SEL(x) ((x) << 24)
-#define NFC_CE_CTL BIT(6)
-#define NFC_PAGE_SHIFT_MSK GENMASK(11, 8)
-#define NFC_PAGE_SHIFT(x) (((x) < 10 ? 0 : (x) - 10) << 8)
-#define NFC_SAM BIT(12)
-#define NFC_RAM_METHOD BIT(14)
-#define NFC_DEBUG_CTL BIT(31)
-
-/* define bit use in NFC_ST */
-#define NFC_RB_B2R BIT(0)
-#define NFC_CMD_INT_FLAG BIT(1)
-#define NFC_DMA_INT_FLAG BIT(2)
-#define NFC_CMD_FIFO_STATUS BIT(3)
-#define NFC_STA BIT(4)
-#define NFC_NATCH_INT_FLAG BIT(5)
-#define NFC_RB_STATE(x) BIT(x + 8)
-
-/* define bit use in NFC_INT */
-#define NFC_B2R_INT_ENABLE BIT(0)
-#define NFC_CMD_INT_ENABLE BIT(1)
-#define NFC_DMA_INT_ENABLE BIT(2)
-#define NFC_INT_MASK (NFC_B2R_INT_ENABLE | \
- NFC_CMD_INT_ENABLE | \
- NFC_DMA_INT_ENABLE)
-
-/* define bit use in NFC_TIMING_CTL */
-#define NFC_TIMING_CTL_EDO BIT(8)
-
-/* define NFC_TIMING_CFG register layout */
-#define NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD) \
- (((tWB) & 0x3) | (((tADL) & 0x3) << 2) | \
- (((tWHR) & 0x3) << 4) | (((tRHW) & 0x3) << 6) | \
- (((tCAD) & 0x7) << 8))
-
-/* define bit use in NFC_CMD */
-#define NFC_CMD_LOW_BYTE_MSK GENMASK(7, 0)
-#define NFC_CMD_HIGH_BYTE_MSK GENMASK(15, 8)
-#define NFC_CMD(x) (x)
-#define NFC_ADR_NUM_MSK GENMASK(18, 16)
-#define NFC_ADR_NUM(x) (((x) - 1) << 16)
-#define NFC_SEND_ADR BIT(19)
-#define NFC_ACCESS_DIR BIT(20)
-#define NFC_DATA_TRANS BIT(21)
-#define NFC_SEND_CMD1 BIT(22)
-#define NFC_WAIT_FLAG BIT(23)
-#define NFC_SEND_CMD2 BIT(24)
-#define NFC_SEQ BIT(25)
-#define NFC_DATA_SWAP_METHOD BIT(26)
-#define NFC_ROW_AUTO_INC BIT(27)
-#define NFC_SEND_CMD3 BIT(28)
-#define NFC_SEND_CMD4 BIT(29)
-#define NFC_CMD_TYPE_MSK GENMASK(31, 30)
-#define NFC_NORMAL_OP (0 << 30)
-#define NFC_ECC_OP (1 << 30)
-#define NFC_PAGE_OP (2 << 30)
-
-/* define bit use in NFC_RCMD_SET */
-#define NFC_READ_CMD_MSK GENMASK(7, 0)
-#define NFC_RND_READ_CMD0_MSK GENMASK(15, 8)
-#define NFC_RND_READ_CMD1_MSK GENMASK(23, 16)
-
-/* define bit use in NFC_WCMD_SET */
-#define NFC_PROGRAM_CMD_MSK GENMASK(7, 0)
-#define NFC_RND_WRITE_CMD_MSK GENMASK(15, 8)
-#define NFC_READ_CMD0_MSK GENMASK(23, 16)
-#define NFC_READ_CMD1_MSK GENMASK(31, 24)
-
-/* define bit use in NFC_ECC_CTL */
-#define NFC_ECC_EN BIT(0)
-#define NFC_ECC_PIPELINE BIT(3)
-#define NFC_ECC_EXCEPTION BIT(4)
-#define NFC_ECC_BLOCK_SIZE_MSK BIT(5)
-#define NFC_ECC_BLOCK_512 BIT(5)
-#define NFC_RANDOM_EN BIT(9)
-#define NFC_RANDOM_DIRECTION BIT(10)
-#define NFC_ECC_MODE_MSK GENMASK(15, 12)
-#define NFC_ECC_MODE(x) ((x) << 12)
-#define NFC_RANDOM_SEED_MSK GENMASK(30, 16)
-#define NFC_RANDOM_SEED(x) ((x) << 16)
-
-/* define bit use in NFC_ECC_ST */
-#define NFC_ECC_ERR(x) BIT(x)
-#define NFC_ECC_ERR_MSK GENMASK(15, 0)
-#define NFC_ECC_PAT_FOUND(x) BIT(x + 16)
-#define NFC_ECC_ERR_CNT(b, x) (((x) >> (((b) % 4) * 8)) & 0xff)
-
-#define NFC_DEFAULT_TIMEOUT_MS 1000
-
-#define NFC_SRAM_SIZE 1024
-
-#define NFC_MAX_CS 7
-
-/*
- * Ready/Busy detection type: describes the Ready/Busy detection modes
- *
- * @RB_NONE: no external detection available, rely on STATUS command
- * and software timeouts
- * @RB_NATIVE: use sunxi NAND controller Ready/Busy support. The Ready/Busy
- * pin of the NAND flash chip must be connected to one of the
- * native NAND R/B pins (those which can be muxed to the NAND
- * Controller)
- * @RB_GPIO: use a simple GPIO to handle Ready/Busy status. The Ready/Busy
- * pin of the NAND flash chip must be connected to a GPIO capable
- * pin.
- */
-enum sunxi_nand_rb_type {
- RB_NONE,
- RB_NATIVE,
- RB_GPIO,
-};
-
-/*
- * Ready/Busy structure: stores information related to Ready/Busy detection
- *
- * @type: the Ready/Busy detection mode
- * @info: information related to the R/B detection mode. Either a gpio
- * id or a native R/B id (those supported by the NAND controller).
- */
-struct sunxi_nand_rb {
- enum sunxi_nand_rb_type type;
- union {
- int gpio;
- int nativeid;
- } info;
-};
-
-/*
- * Chip Select structure: stores information related to NAND Chip Select
- *
- * @cs: the NAND CS id used to communicate with a NAND Chip
- * @rb: the Ready/Busy description
- */
-struct sunxi_nand_chip_sel {
- u8 cs;
- struct sunxi_nand_rb rb;
-};
-
-/*
- * sunxi HW ECC infos: stores information related to HW ECC support
- *
- * @mode: the sunxi ECC mode field deduced from ECC requirements
- */
-struct sunxi_nand_hw_ecc {
- int mode;
-};
-
-/*
- * NAND chip structure: stores NAND chip device related information
- *
- * @node: used to store NAND chips into a list
- * @nand: base NAND chip structure
- * @mtd: base MTD structure
- * @clk_rate: clk_rate required for this NAND chip
- * @timing_cfg TIMING_CFG register value for this NAND chip
- * @selected: current active CS
- * @nsels: number of CS lines required by the NAND chip
- * @sels: array of CS lines descriptions
- */
-struct sunxi_nand_chip {
- struct list_head node;
- struct nand_chip nand;
- unsigned long clk_rate;
- u32 timing_cfg;
- u32 timing_ctl;
- int selected;
- int addr_cycles;
- u32 addr[2];
- int cmd_cycles;
- u8 cmd[2];
- int nsels;
- struct sunxi_nand_chip_sel sels[0];
-};
-
-static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
-{
- return container_of(nand, struct sunxi_nand_chip, nand);
-}
-
-/*
- * NAND Controller structure: stores sunxi NAND controller information
- *
- * @controller: base controller structure
- * @dev: parent device (used to print error messages)
- * @regs: NAND controller registers
- * @ahb_clk: NAND Controller AHB clock
- * @mod_clk: NAND Controller mod clock
- * @assigned_cs: bitmask describing already assigned CS lines
- * @clk_rate: NAND controller current clock rate
- * @chips: a list containing all the NAND chips attached to
- * this NAND controller
- * @complete: a completion object used to wait for NAND
- * controller events
- */
-struct sunxi_nfc {
- struct nand_hw_control controller;
- struct device *dev;
- void __iomem *regs;
- struct clk *ahb_clk;
- struct clk *mod_clk;
- struct reset_control *reset;
- unsigned long assigned_cs;
- unsigned long clk_rate;
- struct list_head chips;
- struct completion complete;
- struct dma_chan *dmac;
-};
-
-static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_hw_control *ctrl)
-{
- return container_of(ctrl, struct sunxi_nfc, controller);
-}
-
-static irqreturn_t sunxi_nfc_interrupt(int irq, void *dev_id)
-{
- struct sunxi_nfc *nfc = dev_id;
- u32 st = readl(nfc->regs + NFC_REG_ST);
- u32 ien = readl(nfc->regs + NFC_REG_INT);
-
- if (!(ien & st))
- return IRQ_NONE;
-
- if ((ien & st) == ien)
- complete(&nfc->complete);
-
- writel(st & NFC_INT_MASK, nfc->regs + NFC_REG_ST);
- writel(~st & ien & NFC_INT_MASK, nfc->regs + NFC_REG_INT);
-
- return IRQ_HANDLED;
-}
-
-static int sunxi_nfc_wait_events(struct sunxi_nfc *nfc, u32 events,
- bool use_polling, unsigned int timeout_ms)
-{
- int ret;
-
- if (events & ~NFC_INT_MASK)
- return -EINVAL;
-
- if (!timeout_ms)
- timeout_ms = NFC_DEFAULT_TIMEOUT_MS;
-
- if (!use_polling) {
- init_completion(&nfc->complete);
-
- writel(events, nfc->regs + NFC_REG_INT);
-
- ret = wait_for_completion_timeout(&nfc->complete,
- msecs_to_jiffies(timeout_ms));
- if (!ret)
- ret = -ETIMEDOUT;
- else
- ret = 0;
-
- writel(0, nfc->regs + NFC_REG_INT);
- } else {
- u32 status;
-
- ret = readl_poll_timeout(nfc->regs + NFC_REG_ST, status,
- (status & events) == events, 1,
- timeout_ms * 1000);
- }
-
- writel(events & NFC_INT_MASK, nfc->regs + NFC_REG_ST);
-
- if (ret)
- dev_err(nfc->dev, "wait interrupt timedout\n");
-
- return ret;
-}
-
-static int sunxi_nfc_wait_cmd_fifo_empty(struct sunxi_nfc *nfc)
-{
- u32 status;
- int ret;
-
- ret = readl_poll_timeout(nfc->regs + NFC_REG_ST, status,
- !(status & NFC_CMD_FIFO_STATUS), 1,
- NFC_DEFAULT_TIMEOUT_MS * 1000);
- if (ret)
- dev_err(nfc->dev, "wait for empty cmd FIFO timedout\n");
-
- return ret;
-}
-
-static int sunxi_nfc_rst(struct sunxi_nfc *nfc)
-{
- u32 ctl;
- int ret;
-
- writel(0, nfc->regs + NFC_REG_ECC_CTL);
- writel(NFC_RESET, nfc->regs + NFC_REG_CTL);
-
- ret = readl_poll_timeout(nfc->regs + NFC_REG_CTL, ctl,
- !(ctl & NFC_RESET), 1,
- NFC_DEFAULT_TIMEOUT_MS * 1000);
- if (ret)
- dev_err(nfc->dev, "wait for NAND controller reset timedout\n");
-
- return ret;
-}
-
-static int sunxi_nfc_dma_op_prepare(struct mtd_info *mtd, const void *buf,
- int chunksize, int nchunks,
- enum dma_data_direction ddir,
- struct scatterlist *sg)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
- struct dma_async_tx_descriptor *dmad;
- enum dma_transfer_direction tdir;
- dma_cookie_t dmat;
- int ret;
-
- if (ddir == DMA_FROM_DEVICE)
- tdir = DMA_DEV_TO_MEM;
- else
- tdir = DMA_MEM_TO_DEV;
-
- sg_init_one(sg, buf, nchunks * chunksize);
- ret = dma_map_sg(nfc->dev, sg, 1, ddir);
- if (!ret)
- return -ENOMEM;
-
- dmad = dmaengine_prep_slave_sg(nfc->dmac, sg, 1, tdir, DMA_CTRL_ACK);
- if (!dmad) {
- ret = -EINVAL;
- goto err_unmap_buf;
- }
-
- writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RAM_METHOD,
- nfc->regs + NFC_REG_CTL);
- writel(nchunks, nfc->regs + NFC_REG_SECTOR_NUM);
- writel(chunksize, nfc->regs + NFC_REG_CNT);
- dmat = dmaengine_submit(dmad);
-
- ret = dma_submit_error(dmat);
- if (ret)
- goto err_clr_dma_flag;
-
- return 0;
-
-err_clr_dma_flag:
- writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD,
- nfc->regs + NFC_REG_CTL);
-
-err_unmap_buf:
- dma_unmap_sg(nfc->dev, sg, 1, ddir);
- return ret;
-}
-
-static void sunxi_nfc_dma_op_cleanup(struct mtd_info *mtd,
- enum dma_data_direction ddir,
- struct scatterlist *sg)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
-
- dma_unmap_sg(nfc->dev, sg, 1, ddir);
- writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD,
- nfc->regs + NFC_REG_CTL);
-}
-
-static int sunxi_nfc_dev_ready(struct mtd_info *mtd)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
- struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
- struct sunxi_nand_rb *rb;
- int ret;
-
- if (sunxi_nand->selected < 0)
- return 0;
-
- rb = &sunxi_nand->sels[sunxi_nand->selected].rb;
-
- switch (rb->type) {
- case RB_NATIVE:
- ret = !!(readl(nfc->regs + NFC_REG_ST) &
- NFC_RB_STATE(rb->info.nativeid));
- break;
- case RB_GPIO:
- ret = gpio_get_value(rb->info.gpio);
- break;
- case RB_NONE:
- default:
- ret = 0;
- dev_err(nfc->dev, "cannot check R/B NAND status!\n");
- break;
- }
-
- return ret;
-}
-
-static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
- struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
- struct sunxi_nand_chip_sel *sel;
- u32 ctl;
-
- if (chip > 0 && chip >= sunxi_nand->nsels)
- return;
-
- if (chip == sunxi_nand->selected)
- return;
-
- ctl = readl(nfc->regs + NFC_REG_CTL) &
- ~(NFC_PAGE_SHIFT_MSK | NFC_CE_SEL_MSK | NFC_RB_SEL_MSK | NFC_EN);
-
- if (chip >= 0) {
- sel = &sunxi_nand->sels[chip];
-
- ctl |= NFC_CE_SEL(sel->cs) | NFC_EN |
- NFC_PAGE_SHIFT(nand->page_shift);
- if (sel->rb.type == RB_NONE) {
- nand->dev_ready = NULL;
- } else {
- nand->dev_ready = sunxi_nfc_dev_ready;
- if (sel->rb.type == RB_NATIVE)
- ctl |= NFC_RB_SEL(sel->rb.info.nativeid);
- }
-
- writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA);
-
- if (nfc->clk_rate != sunxi_nand->clk_rate) {
- clk_set_rate(nfc->mod_clk, sunxi_nand->clk_rate);
- nfc->clk_rate = sunxi_nand->clk_rate;
- }
- }
-
- writel(sunxi_nand->timing_ctl, nfc->regs + NFC_REG_TIMING_CTL);
- writel(sunxi_nand->timing_cfg, nfc->regs + NFC_REG_TIMING_CFG);
- writel(ctl, nfc->regs + NFC_REG_CTL);
-
- sunxi_nand->selected = chip;
-}
-
-static void sunxi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
- struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
- int ret;
- int cnt;
- int offs = 0;
- u32 tmp;
-
- while (len > offs) {
- bool poll = false;
-
- cnt = min(len - offs, NFC_SRAM_SIZE);
-
- ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
- if (ret)
- break;
-
- writel(cnt, nfc->regs + NFC_REG_CNT);
- tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD;
- writel(tmp, nfc->regs + NFC_REG_CMD);
-
- /* Arbitrary limit for polling mode */
- if (cnt < 64)
- poll = true;
-
- ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, poll, 0);
- if (ret)
- break;
-
- if (buf)
- memcpy_fromio(buf + offs, nfc->regs + NFC_RAM0_BASE,
- cnt);
- offs += cnt;
- }
-}
-
-static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
- int len)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
- struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
- int ret;
- int cnt;
- int offs = 0;
- u32 tmp;
-
- while (len > offs) {
- bool poll = false;
-
- cnt = min(len - offs, NFC_SRAM_SIZE);
-
- ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
- if (ret)
- break;
-
- writel(cnt, nfc->regs + NFC_REG_CNT);
- memcpy_toio(nfc->regs + NFC_RAM0_BASE, buf + offs, cnt);
- tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
- NFC_ACCESS_DIR;
- writel(tmp, nfc->regs + NFC_REG_CMD);
-
- /* Arbitrary limit for polling mode */
- if (cnt < 64)
- poll = true;
-
- ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, poll, 0);
- if (ret)
- break;
-
- offs += cnt;
- }
-}
-
-static uint8_t sunxi_nfc_read_byte(struct mtd_info *mtd)
-{
- uint8_t ret;
-
- sunxi_nfc_read_buf(mtd, &ret, 1);
-
- return ret;
-}
-
-static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat,
- unsigned int ctrl)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
- struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
- int ret;
-
- if (dat == NAND_CMD_NONE && (ctrl & NAND_NCE) &&
- !(ctrl & (NAND_CLE | NAND_ALE))) {
- u32 cmd = 0;
-
- if (!sunxi_nand->addr_cycles && !sunxi_nand->cmd_cycles)
- return;
-
- if (sunxi_nand->cmd_cycles--)
- cmd |= NFC_SEND_CMD1 | sunxi_nand->cmd[0];
-
- if (sunxi_nand->cmd_cycles--) {
- cmd |= NFC_SEND_CMD2;
- writel(sunxi_nand->cmd[1],
- nfc->regs + NFC_REG_RCMD_SET);
- }
-
- sunxi_nand->cmd_cycles = 0;
-
- if (sunxi_nand->addr_cycles) {
- cmd |= NFC_SEND_ADR |
- NFC_ADR_NUM(sunxi_nand->addr_cycles);
- writel(sunxi_nand->addr[0],
- nfc->regs + NFC_REG_ADDR_LOW);
- }
-
- if (sunxi_nand->addr_cycles > 4)
- writel(sunxi_nand->addr[1],
- nfc->regs + NFC_REG_ADDR_HIGH);
-
- ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
- if (ret)
- return;
-
- writel(cmd, nfc->regs + NFC_REG_CMD);
- sunxi_nand->addr[0] = 0;
- sunxi_nand->addr[1] = 0;
- sunxi_nand->addr_cycles = 0;
- sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0);
- }
-
- if (ctrl & NAND_CLE) {
- sunxi_nand->cmd[sunxi_nand->cmd_cycles++] = dat;
- } else if (ctrl & NAND_ALE) {
- sunxi_nand->addr[sunxi_nand->addr_cycles / 4] |=
- dat << ((sunxi_nand->addr_cycles % 4) * 8);
- sunxi_nand->addr_cycles++;
- }
-}
-
-/* These seed values have been extracted from Allwinner's BSP */
-static const u16 sunxi_nfc_randomizer_page_seeds[] = {
- 0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72,
- 0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436,
- 0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d,
- 0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130,
- 0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56,
- 0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55,
- 0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb,
- 0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17,
- 0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62,
- 0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064,
- 0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126,
- 0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e,
- 0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3,
- 0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b,
- 0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d,
- 0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db,
-};
-
-/*
- * sunxi_nfc_randomizer_ecc512_seeds and sunxi_nfc_randomizer_ecc1024_seeds
- * have been generated using
- * sunxi_nfc_randomizer_step(seed, (step_size * 8) + 15), which is what
- * the randomizer engine does internally before de/scrambling OOB data.
- *
- * Those tables are statically defined to avoid calculating randomizer state
- * at runtime.
- */
-static const u16 sunxi_nfc_randomizer_ecc512_seeds[] = {
- 0x3346, 0x367f, 0x1f18, 0x769a, 0x4f64, 0x068c, 0x2ef1, 0x6b64,
- 0x28a9, 0x15d7, 0x30f8, 0x3659, 0x53db, 0x7c5f, 0x71d4, 0x4409,
- 0x26eb, 0x03cc, 0x655d, 0x47d4, 0x4daa, 0x0877, 0x712d, 0x3617,
- 0x3264, 0x49aa, 0x7f9e, 0x588e, 0x4fbc, 0x7176, 0x7f91, 0x6c6d,
- 0x4b95, 0x5fb7, 0x3844, 0x4037, 0x0184, 0x081b, 0x0ee8, 0x5b91,
- 0x293d, 0x1f71, 0x0e6f, 0x402b, 0x5122, 0x1e52, 0x22be, 0x3d2d,
- 0x75bc, 0x7c60, 0x6291, 0x1a2f, 0x61d4, 0x74aa, 0x4140, 0x29ab,
- 0x472d, 0x2852, 0x017e, 0x15e8, 0x5ec2, 0x17cf, 0x7d0f, 0x06b8,
- 0x117a, 0x6b94, 0x789b, 0x3126, 0x6ac5, 0x5be7, 0x150f, 0x51f8,
- 0x7889, 0x0aa5, 0x663d, 0x77e8, 0x0b87, 0x3dcb, 0x360d, 0x218b,
- 0x512f, 0x7dc9, 0x6a4d, 0x630a, 0x3547, 0x1dd2, 0x5aea, 0x69a5,
- 0x7bfa, 0x5e4f, 0x1519, 0x6430, 0x3a0e, 0x5eb3, 0x5425, 0x0c7a,
- 0x5540, 0x3670, 0x63c1, 0x31e9, 0x5a39, 0x2de7, 0x5979, 0x2891,
- 0x1562, 0x014b, 0x5b05, 0x2756, 0x5a34, 0x13aa, 0x6cb5, 0x2c36,
- 0x5e72, 0x1306, 0x0861, 0x15ef, 0x1ee8, 0x5a37, 0x7ac4, 0x45dd,
- 0x44c4, 0x7266, 0x2f41, 0x3ccc, 0x045e, 0x7d40, 0x7c66, 0x0fa0,
-};
-
-static const u16 sunxi_nfc_randomizer_ecc1024_seeds[] = {
- 0x2cf5, 0x35f1, 0x63a4, 0x5274, 0x2bd2, 0x778b, 0x7285, 0x32b6,
- 0x6a5c, 0x70d6, 0x757d, 0x6769, 0x5375, 0x1e81, 0x0cf3, 0x3982,
- 0x6787, 0x042a, 0x6c49, 0x1925, 0x56a8, 0x40a9, 0x063e, 0x7bd9,
- 0x4dbf, 0x55ec, 0x672e, 0x7334, 0x5185, 0x4d00, 0x232a, 0x7e07,
- 0x445d, 0x6b92, 0x528f, 0x4255, 0x53ba, 0x7d82, 0x2a2e, 0x3a4e,
- 0x75eb, 0x450c, 0x6844, 0x1b5d, 0x581a, 0x4cc6, 0x0379, 0x37b2,
- 0x419f, 0x0e92, 0x6b27, 0x5624, 0x01e3, 0x07c1, 0x44a5, 0x130c,
- 0x13e8, 0x5910, 0x0876, 0x60c5, 0x54e3, 0x5b7f, 0x2269, 0x509f,
- 0x7665, 0x36fd, 0x3e9a, 0x0579, 0x6295, 0x14ef, 0x0a81, 0x1bcc,
- 0x4b16, 0x64db, 0x0514, 0x4f07, 0x0591, 0x3576, 0x6853, 0x0d9e,
- 0x259f, 0x38b7, 0x64fb, 0x3094, 0x4693, 0x6ddd, 0x29bb, 0x0bc8,
- 0x3f47, 0x490e, 0x0c0e, 0x7933, 0x3c9e, 0x5840, 0x398d, 0x3e68,
- 0x4af1, 0x71f5, 0x57cf, 0x1121, 0x64eb, 0x3579, 0x15ac, 0x584d,
- 0x5f2a, 0x47e2, 0x6528, 0x6eac, 0x196e, 0x6b96, 0x0450, 0x0179,
- 0x609c, 0x06e1, 0x4626, 0x42c7, 0x273e, 0x486f, 0x0705, 0x1601,
- 0x145b, 0x407e, 0x062b, 0x57a5, 0x53f9, 0x5659, 0x4410, 0x3ccd,
-};
-
-static u16 sunxi_nfc_randomizer_step(u16 state, int count)
-{
- state &= 0x7fff;
-
- /*
- * This loop is just a simple implementation of a Fibonacci LFSR using
- * the x16 + x15 + 1 polynomial.
- */
- while (count--)
- state = ((state >> 1) |
- (((state ^ (state >> 1)) & 1) << 14)) & 0x7fff;
-
- return state;
-}
-
-static u16 sunxi_nfc_randomizer_state(struct mtd_info *mtd, int page, bool ecc)
-{
- const u16 *seeds = sunxi_nfc_randomizer_page_seeds;
- int mod = mtd_div_by_ws(mtd->erasesize, mtd);
-
- if (mod > ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds))
- mod = ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds);
-
- if (ecc) {
- if (mtd->ecc_step_size == 512)
- seeds = sunxi_nfc_randomizer_ecc512_seeds;
- else
- seeds = sunxi_nfc_randomizer_ecc1024_seeds;
- }
-
- return seeds[page % mod];
-}
-
-static void sunxi_nfc_randomizer_config(struct mtd_info *mtd,
- int page, bool ecc)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
- u32 ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
- u16 state;
-
- if (!(nand->options & NAND_NEED_SCRAMBLING))
- return;
-
- ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
- state = sunxi_nfc_randomizer_state(mtd, page, ecc);
- ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_SEED_MSK;
- writel(ecc_ctl | NFC_RANDOM_SEED(state), nfc->regs + NFC_REG_ECC_CTL);
-}
-
-static void sunxi_nfc_randomizer_enable(struct mtd_info *mtd)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
-
- if (!(nand->options & NAND_NEED_SCRAMBLING))
- return;
-
- writel(readl(nfc->regs + NFC_REG_ECC_CTL) | NFC_RANDOM_EN,
- nfc->regs + NFC_REG_ECC_CTL);
-}
-
-static void sunxi_nfc_randomizer_disable(struct mtd_info *mtd)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
-
- if (!(nand->options & NAND_NEED_SCRAMBLING))
- return;
-
- writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN,
- nfc->regs + NFC_REG_ECC_CTL);
-}
-
-static void sunxi_nfc_randomize_bbm(struct mtd_info *mtd, int page, u8 *bbm)
-{
- u16 state = sunxi_nfc_randomizer_state(mtd, page, true);
-
- bbm[0] ^= state;
- bbm[1] ^= sunxi_nfc_randomizer_step(state, 8);
-}
-
-static void sunxi_nfc_randomizer_write_buf(struct mtd_info *mtd,
- const uint8_t *buf, int len,
- bool ecc, int page)
-{
- sunxi_nfc_randomizer_config(mtd, page, ecc);
- sunxi_nfc_randomizer_enable(mtd);
- sunxi_nfc_write_buf(mtd, buf, len);
- sunxi_nfc_randomizer_disable(mtd);
-}
-
-static void sunxi_nfc_randomizer_read_buf(struct mtd_info *mtd, uint8_t *buf,
- int len, bool ecc, int page)
-{
- sunxi_nfc_randomizer_config(mtd, page, ecc);
- sunxi_nfc_randomizer_enable(mtd);
- sunxi_nfc_read_buf(mtd, buf, len);
- sunxi_nfc_randomizer_disable(mtd);
-}
-
-static void sunxi_nfc_hw_ecc_enable(struct mtd_info *mtd)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
- struct sunxi_nand_hw_ecc *data = nand->ecc.priv;
- u32 ecc_ctl;
-
- ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
- ecc_ctl &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE |
- NFC_ECC_BLOCK_SIZE_MSK);
- ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION |
- NFC_ECC_PIPELINE;
-
- if (nand->ecc.size == 512)
- ecc_ctl |= NFC_ECC_BLOCK_512;
-
- writel(ecc_ctl, nfc->regs + NFC_REG_ECC_CTL);
-}
-
-static void sunxi_nfc_hw_ecc_disable(struct mtd_info *mtd)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
-
- writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_ECC_EN,
- nfc->regs + NFC_REG_ECC_CTL);
-}
-
-static inline void sunxi_nfc_user_data_to_buf(u32 user_data, u8 *buf)
-{
- buf[0] = user_data;
- buf[1] = user_data >> 8;
- buf[2] = user_data >> 16;
- buf[3] = user_data >> 24;
-}
-
-static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf)
-{
- return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
-}
-
-static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct mtd_info *mtd, u8 *oob,
- int step, bool bbm, int page)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
-
- sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(step)),
- oob);
-
- /* De-randomize the Bad Block Marker. */
- if (bbm && (nand->options & NAND_NEED_SCRAMBLING))
- sunxi_nfc_randomize_bbm(mtd, page, oob);
-}
-
-static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct mtd_info *mtd,
- const u8 *oob, int step,
- bool bbm, int page)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
- u8 user_data[4];
-
- /* Randomize the Bad Block Marker. */
- if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) {
- memcpy(user_data, oob, sizeof(user_data));
- sunxi_nfc_randomize_bbm(mtd, page, user_data);
- oob = user_data;
- }
-
- writel(sunxi_nfc_buf_to_user_data(oob),
- nfc->regs + NFC_REG_USER_DATA(step));
-}
-
-static void sunxi_nfc_hw_ecc_update_stats(struct mtd_info *mtd,
- unsigned int *max_bitflips, int ret)
-{
- if (ret < 0) {
- mtd->ecc_stats.failed++;
- } else {
- mtd->ecc_stats.corrected += ret;
- *max_bitflips = max_t(unsigned int, *max_bitflips, ret);
- }
-}
-
-static int sunxi_nfc_hw_ecc_correct(struct mtd_info *mtd, u8 *data, u8 *oob,
- int step, u32 status, bool *erased)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
- struct nand_ecc_ctrl *ecc = &nand->ecc;
- u32 tmp;
-
- *erased = false;
-
- if (status & NFC_ECC_ERR(step))
- return -EBADMSG;
-
- if (status & NFC_ECC_PAT_FOUND(step)) {
- u8 pattern;
-
- if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1))) {
- pattern = 0x0;
- } else {
- pattern = 0xff;
- *erased = true;
- }
-
- if (data)
- memset(data, pattern, ecc->size);
-
- if (oob)
- memset(oob, pattern, ecc->bytes + 4);
-
- return 0;
- }
-
- tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(step));
-
- return NFC_ECC_ERR_CNT(step, tmp);
-}
-
-static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
- u8 *data, int data_off,
- u8 *oob, int oob_off,
- int *cur_off,
- unsigned int *max_bitflips,
- bool bbm, bool oob_required, int page)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
- struct nand_ecc_ctrl *ecc = &nand->ecc;
- int raw_mode = 0;
- bool erased;
- int ret;
-
- if (*cur_off != data_off)
- nand_change_read_column_op(nand, data_off, NULL, 0, false);
-
- sunxi_nfc_randomizer_read_buf(mtd, NULL, ecc->size, false, page);
-
- if (data_off + ecc->size != oob_off)
- nand_change_read_column_op(nand, oob_off, NULL, 0, false);
-
- ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
- if (ret)
- return ret;
-
- sunxi_nfc_randomizer_enable(mtd);
- writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP,
- nfc->regs + NFC_REG_CMD);
-
- ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0);
- sunxi_nfc_randomizer_disable(mtd);
- if (ret)
- return ret;
-
- *cur_off = oob_off + ecc->bytes + 4;
-
- ret = sunxi_nfc_hw_ecc_correct(mtd, data, oob_required ? oob : NULL, 0,
- readl(nfc->regs + NFC_REG_ECC_ST),
- &erased);
- if (erased)
- return 1;
-
- if (ret < 0) {
- /*
- * Re-read the data with the randomizer disabled to identify
- * bitflips in erased pages.
- */
- if (nand->options & NAND_NEED_SCRAMBLING)
- nand_change_read_column_op(nand, data_off, data,
- ecc->size, false);
- else
- memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE,
- ecc->size);
-
- nand_change_read_column_op(nand, oob_off, oob, ecc->bytes + 4,
- false);
-
- ret = nand_check_erased_ecc_chunk(data, ecc->size,
- oob, ecc->bytes + 4,
- NULL, 0, ecc->strength);
- if (ret >= 0)
- raw_mode = 1;
- } else {
- memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size);
-
- if (oob_required) {
- nand_change_read_column_op(nand, oob_off, NULL, 0,
- false);
- sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + 4,
- true, page);
-
- sunxi_nfc_hw_ecc_get_prot_oob_bytes(mtd, oob, 0,
- bbm, page);
- }
- }
-
- sunxi_nfc_hw_ecc_update_stats(mtd, max_bitflips, ret);
-
- return raw_mode;
-}
-
-static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd,
- u8 *oob, int *cur_off,
- bool randomize, int page)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct nand_ecc_ctrl *ecc = &nand->ecc;
- int offset = ((ecc->bytes + 4) * ecc->steps);
- int len = mtd->oobsize - offset;
-
- if (len <= 0)
- return;
-
- if (!cur_off || *cur_off != offset)
- nand_change_read_column_op(nand, mtd->writesize, NULL, 0,
- false);
-
- if (!randomize)
- sunxi_nfc_read_buf(mtd, oob + offset, len);
- else
- sunxi_nfc_randomizer_read_buf(mtd, oob + offset, len,
- false, page);
-
- if (cur_off)
- *cur_off = mtd->oobsize + mtd->writesize;
-}
-
-static int sunxi_nfc_hw_ecc_read_chunks_dma(struct mtd_info *mtd, uint8_t *buf,
- int oob_required, int page,
- int nchunks)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- bool randomized = nand->options & NAND_NEED_SCRAMBLING;
- struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
- struct nand_ecc_ctrl *ecc = &nand->ecc;
- unsigned int max_bitflips = 0;
- int ret, i, raw_mode = 0;
- struct scatterlist sg;
- u32 status;
-
- ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
- if (ret)
- return ret;
-
- ret = sunxi_nfc_dma_op_prepare(mtd, buf, ecc->size, nchunks,
- DMA_FROM_DEVICE, &sg);
- if (ret)
- return ret;
-
- sunxi_nfc_hw_ecc_enable(mtd);
- sunxi_nfc_randomizer_config(mtd, page, false);
- sunxi_nfc_randomizer_enable(mtd);
-
- writel((NAND_CMD_RNDOUTSTART << 16) | (NAND_CMD_RNDOUT << 8) |
- NAND_CMD_READSTART, nfc->regs + NFC_REG_RCMD_SET);
-
- dma_async_issue_pending(nfc->dmac);
-
- writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD | NFC_DATA_TRANS,
- nfc->regs + NFC_REG_CMD);
-
- ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0);
- if (ret)
- dmaengine_terminate_all(nfc->dmac);
-
- sunxi_nfc_randomizer_disable(mtd);
- sunxi_nfc_hw_ecc_disable(mtd);
-
- sunxi_nfc_dma_op_cleanup(mtd, DMA_FROM_DEVICE, &sg);
-
- if (ret)
- return ret;
-
- status = readl(nfc->regs + NFC_REG_ECC_ST);
-
- for (i = 0; i < nchunks; i++) {
- int data_off = i * ecc->size;
- int oob_off = i * (ecc->bytes + 4);
- u8 *data = buf + data_off;
- u8 *oob = nand->oob_poi + oob_off;
- bool erased;
-
- ret = sunxi_nfc_hw_ecc_correct(mtd, randomized ? data : NULL,
- oob_required ? oob : NULL,
- i, status, &erased);
-
- /* ECC errors are handled in the second loop. */
- if (ret < 0)
- continue;
-
- if (oob_required && !erased) {
- /* TODO: use DMA to retrieve OOB */
- nand_change_read_column_op(nand,
- mtd->writesize + oob_off,
- oob, ecc->bytes + 4, false);
-
- sunxi_nfc_hw_ecc_get_prot_oob_bytes(mtd, oob, i,
- !i, page);
- }
-
- if (erased)
- raw_mode = 1;
-
- sunxi_nfc_hw_ecc_update_stats(mtd, &max_bitflips, ret);
- }
-
- if (status & NFC_ECC_ERR_MSK) {
- for (i = 0; i < nchunks; i++) {
- int data_off = i * ecc->size;
- int oob_off = i * (ecc->bytes + 4);
- u8 *data = buf + data_off;
- u8 *oob = nand->oob_poi + oob_off;
-
- if (!(status & NFC_ECC_ERR(i)))
- continue;
-
- /*
- * Re-read the data with the randomizer disabled to
- * identify bitflips in erased pages.
- * TODO: use DMA to read page in raw mode
- */
- if (randomized)
- nand_change_read_column_op(nand, data_off,
- data, ecc->size,
- false);
-
- /* TODO: use DMA to retrieve OOB */
- nand_change_read_column_op(nand,
- mtd->writesize + oob_off,
- oob, ecc->bytes + 4, false);
-
- ret = nand_check_erased_ecc_chunk(data, ecc->size,
- oob, ecc->bytes + 4,
- NULL, 0,
- ecc->strength);
- if (ret >= 0)
- raw_mode = 1;
-
- sunxi_nfc_hw_ecc_update_stats(mtd, &max_bitflips, ret);
- }
- }
-
- if (oob_required)
- sunxi_nfc_hw_ecc_read_extra_oob(mtd, nand->oob_poi,
- NULL, !raw_mode,
- page);
-
- return max_bitflips;
-}
-
-static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
- const u8 *data, int data_off,
- const u8 *oob, int oob_off,
- int *cur_off, bool bbm,
- int page)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
- struct nand_ecc_ctrl *ecc = &nand->ecc;
- int ret;
-
- if (data_off != *cur_off)
- nand_change_write_column_op(nand, data_off, NULL, 0, false);
-
- sunxi_nfc_randomizer_write_buf(mtd, data, ecc->size, false, page);
-
- if (data_off + ecc->size != oob_off)
- nand_change_write_column_op(nand, oob_off, NULL, 0, false);
-
- ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
- if (ret)
- return ret;
-
- sunxi_nfc_randomizer_enable(mtd);
- sunxi_nfc_hw_ecc_set_prot_oob_bytes(mtd, oob, 0, bbm, page);
-
- writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
- NFC_ACCESS_DIR | NFC_ECC_OP,
- nfc->regs + NFC_REG_CMD);
-
- ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0);
- sunxi_nfc_randomizer_disable(mtd);
- if (ret)
- return ret;
-
- *cur_off = oob_off + ecc->bytes + 4;
-
- return 0;
-}
-
-static void sunxi_nfc_hw_ecc_write_extra_oob(struct mtd_info *mtd,
- u8 *oob, int *cur_off,
- int page)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct nand_ecc_ctrl *ecc = &nand->ecc;
- int offset = ((ecc->bytes + 4) * ecc->steps);
- int len = mtd->oobsize - offset;
-
- if (len <= 0)
- return;
-
- if (!cur_off || *cur_off != offset)
- nand_change_write_column_op(nand, offset + mtd->writesize,
- NULL, 0, false);
-
- sunxi_nfc_randomizer_write_buf(mtd, oob + offset, len, false, page);
-
- if (cur_off)
- *cur_off = mtd->oobsize + mtd->writesize;
-}
-
-static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
- struct nand_chip *chip, uint8_t *buf,
- int oob_required, int page)
-{
- struct nand_ecc_ctrl *ecc = &chip->ecc;
- unsigned int max_bitflips = 0;
- int ret, i, cur_off = 0;
- bool raw_mode = false;
-
- nand_read_page_op(chip, page, 0, NULL, 0);
-
- sunxi_nfc_hw_ecc_enable(mtd);
-
- for (i = 0; i < ecc->steps; i++) {
- int data_off = i * ecc->size;
- int oob_off = i * (ecc->bytes + 4);
- u8 *data = buf + data_off;
- u8 *oob = chip->oob_poi + oob_off;
-
- ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob,
- oob_off + mtd->writesize,
- &cur_off, &max_bitflips,
- !i, oob_required, page);
- if (ret < 0)
- return ret;
- else if (ret)
- raw_mode = true;
- }
-
- if (oob_required)
- sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off,
- !raw_mode, page);
-
- sunxi_nfc_hw_ecc_disable(mtd);
-
- return max_bitflips;
-}
-
-static int sunxi_nfc_hw_ecc_read_page_dma(struct mtd_info *mtd,
- struct nand_chip *chip, u8 *buf,
- int oob_required, int page)
-{
- int ret;
-
- nand_read_page_op(chip, page, 0, NULL, 0);
-
- ret = sunxi_nfc_hw_ecc_read_chunks_dma(mtd, buf, oob_required, page,
- chip->ecc.steps);
- if (ret >= 0)
- return ret;
-
- /* Fallback to PIO mode */
- return sunxi_nfc_hw_ecc_read_page(mtd, chip, buf, oob_required, page);
-}
-
-static int sunxi_nfc_hw_ecc_read_subpage(struct mtd_info *mtd,
- struct nand_chip *chip,
- u32 data_offs, u32 readlen,
- u8 *bufpoi, int page)
-{
- struct nand_ecc_ctrl *ecc = &chip->ecc;
- int ret, i, cur_off = 0;
- unsigned int max_bitflips = 0;
-
- nand_read_page_op(chip, page, 0, NULL, 0);
-
- sunxi_nfc_hw_ecc_enable(mtd);
-
- for (i = data_offs / ecc->size;
- i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) {
- int data_off = i * ecc->size;
- int oob_off = i * (ecc->bytes + 4);
- u8 *data = bufpoi + data_off;
- u8 *oob = chip->oob_poi + oob_off;
-
- ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off,
- oob,
- oob_off + mtd->writesize,
- &cur_off, &max_bitflips, !i,
- false, page);
- if (ret < 0)
- return ret;
- }
-
- sunxi_nfc_hw_ecc_disable(mtd);
-
- return max_bitflips;
-}
-
-static int sunxi_nfc_hw_ecc_read_subpage_dma(struct mtd_info *mtd,
- struct nand_chip *chip,
- u32 data_offs, u32 readlen,
- u8 *buf, int page)
-{
- int nchunks = DIV_ROUND_UP(data_offs + readlen, chip->ecc.size);
- int ret;
-
- nand_read_page_op(chip, page, 0, NULL, 0);
-
- ret = sunxi_nfc_hw_ecc_read_chunks_dma(mtd, buf, false, page, nchunks);
- if (ret >= 0)
- return ret;
-
- /* Fallback to PIO mode */
- return sunxi_nfc_hw_ecc_read_subpage(mtd, chip, data_offs, readlen,
- buf, page);
-}
-
-static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
- struct nand_chip *chip,
- const uint8_t *buf, int oob_required,
- int page)
-{
- struct nand_ecc_ctrl *ecc = &chip->ecc;
- int ret, i, cur_off = 0;
-
- nand_prog_page_begin_op(chip, page, 0, NULL, 0);
-
- sunxi_nfc_hw_ecc_enable(mtd);
-
- for (i = 0; i < ecc->steps; i++) {
- int data_off = i * ecc->size;
- int oob_off = i * (ecc->bytes + 4);
- const u8 *data = buf + data_off;
- const u8 *oob = chip->oob_poi + oob_off;
-
- ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob,
- oob_off + mtd->writesize,
- &cur_off, !i, page);
- if (ret)
- return ret;
- }
-
- if (oob_required || (chip->options & NAND_NEED_SCRAMBLING))
- sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi,
- &cur_off, page);
-
- sunxi_nfc_hw_ecc_disable(mtd);
-
- return nand_prog_page_end_op(chip);
-}
-
-static int sunxi_nfc_hw_ecc_write_subpage(struct mtd_info *mtd,
- struct nand_chip *chip,
- u32 data_offs, u32 data_len,
- const u8 *buf, int oob_required,
- int page)
-{
- struct nand_ecc_ctrl *ecc = &chip->ecc;
- int ret, i, cur_off = 0;
-
- nand_prog_page_begin_op(chip, page, 0, NULL, 0);
-
- sunxi_nfc_hw_ecc_enable(mtd);
-
- for (i = data_offs / ecc->size;
- i < DIV_ROUND_UP(data_offs + data_len, ecc->size); i++) {
- int data_off = i * ecc->size;
- int oob_off = i * (ecc->bytes + 4);
- const u8 *data = buf + data_off;
- const u8 *oob = chip->oob_poi + oob_off;
-
- ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob,
- oob_off + mtd->writesize,
- &cur_off, !i, page);
- if (ret)
- return ret;
- }
-
- sunxi_nfc_hw_ecc_disable(mtd);
-
- return nand_prog_page_end_op(chip);
-}
-
-static int sunxi_nfc_hw_ecc_write_page_dma(struct mtd_info *mtd,
- struct nand_chip *chip,
- const u8 *buf,
- int oob_required,
- int page)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
- struct nand_ecc_ctrl *ecc = &nand->ecc;
- struct scatterlist sg;
- int ret, i;
-
- ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
- if (ret)
- return ret;
-
- ret = sunxi_nfc_dma_op_prepare(mtd, buf, ecc->size, ecc->steps,
- DMA_TO_DEVICE, &sg);
- if (ret)
- goto pio_fallback;
-
- for (i = 0; i < ecc->steps; i++) {
- const u8 *oob = nand->oob_poi + (i * (ecc->bytes + 4));
-
- sunxi_nfc_hw_ecc_set_prot_oob_bytes(mtd, oob, i, !i, page);
- }
-
- nand_prog_page_begin_op(chip, page, 0, NULL, 0);
-
- sunxi_nfc_hw_ecc_enable(mtd);
- sunxi_nfc_randomizer_config(mtd, page, false);
- sunxi_nfc_randomizer_enable(mtd);
-
- writel((NAND_CMD_RNDIN << 8) | NAND_CMD_PAGEPROG,
- nfc->regs + NFC_REG_RCMD_SET);
-
- dma_async_issue_pending(nfc->dmac);
-
- writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD |
- NFC_DATA_TRANS | NFC_ACCESS_DIR,
- nfc->regs + NFC_REG_CMD);
-
- ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0);
- if (ret)
- dmaengine_terminate_all(nfc->dmac);
-
- sunxi_nfc_randomizer_disable(mtd);
- sunxi_nfc_hw_ecc_disable(mtd);
-
- sunxi_nfc_dma_op_cleanup(mtd, DMA_TO_DEVICE, &sg);
-
- if (ret)
- return ret;
-
- if (oob_required || (chip->options & NAND_NEED_SCRAMBLING))
- /* TODO: use DMA to transfer extra OOB bytes ? */
- sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi,
- NULL, page);
-
- return nand_prog_page_end_op(chip);
-
-pio_fallback:
- return sunxi_nfc_hw_ecc_write_page(mtd, chip, buf, oob_required, page);
-}
-
-static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd,
- struct nand_chip *chip,
- uint8_t *buf, int oob_required,
- int page)
-{
- struct nand_ecc_ctrl *ecc = &chip->ecc;
- unsigned int max_bitflips = 0;
- int ret, i, cur_off = 0;
- bool raw_mode = false;
-
- nand_read_page_op(chip, page, 0, NULL, 0);
-
- sunxi_nfc_hw_ecc_enable(mtd);
-
- for (i = 0; i < ecc->steps; i++) {
- int data_off = i * (ecc->size + ecc->bytes + 4);
- int oob_off = data_off + ecc->size;
- u8 *data = buf + (i * ecc->size);
- u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4));
-
- ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob,
- oob_off, &cur_off,
- &max_bitflips, !i,
- oob_required,
- page);
- if (ret < 0)
- return ret;
- else if (ret)
- raw_mode = true;
- }
-
- if (oob_required)
- sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off,
- !raw_mode, page);
-
- sunxi_nfc_hw_ecc_disable(mtd);
-
- return max_bitflips;
-}
-
-static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
- struct nand_chip *chip,
- const uint8_t *buf,
- int oob_required, int page)
-{
- struct nand_ecc_ctrl *ecc = &chip->ecc;
- int ret, i, cur_off = 0;
-
- nand_prog_page_begin_op(chip, page, 0, NULL, 0);
-
- sunxi_nfc_hw_ecc_enable(mtd);
-
- for (i = 0; i < ecc->steps; i++) {
- int data_off = i * (ecc->size + ecc->bytes + 4);
- int oob_off = data_off + ecc->size;
- const u8 *data = buf + (i * ecc->size);
- const u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4));
-
- ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off,
- oob, oob_off, &cur_off,
- false, page);
- if (ret)
- return ret;
- }
-
- if (oob_required || (chip->options & NAND_NEED_SCRAMBLING))
- sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi,
- &cur_off, page);
-
- sunxi_nfc_hw_ecc_disable(mtd);
-
- return nand_prog_page_end_op(chip);
-}
-
-static int sunxi_nfc_hw_common_ecc_read_oob(struct mtd_info *mtd,
- struct nand_chip *chip,
- int page)
-{
- chip->pagebuf = -1;
-
- return chip->ecc.read_page(mtd, chip, chip->data_buf, 1, page);
-}
-
-static int sunxi_nfc_hw_common_ecc_write_oob(struct mtd_info *mtd,
- struct nand_chip *chip,
- int page)
-{
- int ret;
-
- chip->pagebuf = -1;
-
- memset(chip->data_buf, 0xff, mtd->writesize);
- ret = chip->ecc.write_page(mtd, chip, chip->data_buf, 1, page);
- if (ret)
- return ret;
-
- /* Send command to program the OOB data */
- return nand_prog_page_end_op(chip);
-}
-
-static const s32 tWB_lut[] = {6, 12, 16, 20};
-static const s32 tRHW_lut[] = {4, 8, 12, 20};
-
-static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration,
- u32 clk_period)
-{
- u32 clk_cycles = DIV_ROUND_UP(duration, clk_period);
- int i;
-
- for (i = 0; i < lut_size; i++) {
- if (clk_cycles <= lut[i])
- return i;
- }
-
- /* Doesn't fit */
- return -EINVAL;
-}
-
-#define sunxi_nand_lookup_timing(l, p, c) \
- _sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c)
-
-static int sunxi_nfc_setup_data_interface(struct mtd_info *mtd, int csline,
- const struct nand_data_interface *conf)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct sunxi_nand_chip *chip = to_sunxi_nand(nand);
- struct sunxi_nfc *nfc = to_sunxi_nfc(chip->nand.controller);
- const struct nand_sdr_timings *timings;
- u32 min_clk_period = 0;
- s32 tWB, tADL, tWHR, tRHW, tCAD;
- long real_clk_rate;
-
- timings = nand_get_sdr_timings(conf);
- if (IS_ERR(timings))
- return -ENOTSUPP;
-
- /* T1 <=> tCLS */
- if (timings->tCLS_min > min_clk_period)
- min_clk_period = timings->tCLS_min;
-
- /* T2 <=> tCLH */
- if (timings->tCLH_min > min_clk_period)
- min_clk_period = timings->tCLH_min;
-
- /* T3 <=> tCS */
- if (timings->tCS_min > min_clk_period)
- min_clk_period = timings->tCS_min;
-
- /* T4 <=> tCH */
- if (timings->tCH_min > min_clk_period)
- min_clk_period = timings->tCH_min;
-
- /* T5 <=> tWP */
- if (timings->tWP_min > min_clk_period)
- min_clk_period = timings->tWP_min;
-
- /* T6 <=> tWH */
- if (timings->tWH_min > min_clk_period)
- min_clk_period = timings->tWH_min;
-
- /* T7 <=> tALS */
- if (timings->tALS_min > min_clk_period)
- min_clk_period = timings->tALS_min;
-
- /* T8 <=> tDS */
- if (timings->tDS_min > min_clk_period)
- min_clk_period = timings->tDS_min;
-
- /* T9 <=> tDH */
- if (timings->tDH_min > min_clk_period)
- min_clk_period = timings->tDH_min;
-
- /* T10 <=> tRR */
- if (timings->tRR_min > (min_clk_period * 3))
- min_clk_period = DIV_ROUND_UP(timings->tRR_min, 3);
-
- /* T11 <=> tALH */
- if (timings->tALH_min > min_clk_period)
- min_clk_period = timings->tALH_min;
-
- /* T12 <=> tRP */
- if (timings->tRP_min > min_clk_period)
- min_clk_period = timings->tRP_min;
-
- /* T13 <=> tREH */
- if (timings->tREH_min > min_clk_period)
- min_clk_period = timings->tREH_min;
-
- /* T14 <=> tRC */
- if (timings->tRC_min > (min_clk_period * 2))
- min_clk_period = DIV_ROUND_UP(timings->tRC_min, 2);
-
- /* T15 <=> tWC */
- if (timings->tWC_min > (min_clk_period * 2))
- min_clk_period = DIV_ROUND_UP(timings->tWC_min, 2);
-
- /* T16 - T19 + tCAD */
- if (timings->tWB_max > (min_clk_period * 20))
- min_clk_period = DIV_ROUND_UP(timings->tWB_max, 20);
-
- if (timings->tADL_min > (min_clk_period * 32))
- min_clk_period = DIV_ROUND_UP(timings->tADL_min, 32);
-
- if (timings->tWHR_min > (min_clk_period * 32))
- min_clk_period = DIV_ROUND_UP(timings->tWHR_min, 32);
-
- if (timings->tRHW_min > (min_clk_period * 20))
- min_clk_period = DIV_ROUND_UP(timings->tRHW_min, 20);
-
- tWB = sunxi_nand_lookup_timing(tWB_lut, timings->tWB_max,
- min_clk_period);
- if (tWB < 0) {
- dev_err(nfc->dev, "unsupported tWB\n");
- return tWB;
- }
-
- tADL = DIV_ROUND_UP(timings->tADL_min, min_clk_period) >> 3;
- if (tADL > 3) {
- dev_err(nfc->dev, "unsupported tADL\n");
- return -EINVAL;
- }
-
- tWHR = DIV_ROUND_UP(timings->tWHR_min, min_clk_period) >> 3;
- if (tWHR > 3) {
- dev_err(nfc->dev, "unsupported tWHR\n");
- return -EINVAL;
- }
-
- tRHW = sunxi_nand_lookup_timing(tRHW_lut, timings->tRHW_min,
- min_clk_period);
- if (tRHW < 0) {
- dev_err(nfc->dev, "unsupported tRHW\n");
- return tRHW;
- }
-
- if (csline == NAND_DATA_IFACE_CHECK_ONLY)
- return 0;
-
- /*
- * TODO: according to ONFI specs this value only applies for DDR NAND,
- * but Allwinner seems to set this to 0x7. Mimic them for now.
- */
- tCAD = 0x7;
-
- /* TODO: A83 has some more bits for CDQSS, CS, CLHZ, CCS, WC */
- chip->timing_cfg = NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD);
-
- /* Convert min_clk_period from picoseconds to nanoseconds */
- min_clk_period = DIV_ROUND_UP(min_clk_period, 1000);
-
- /*
- * Unlike what is stated in Allwinner datasheet, the clk_rate should
- * be set to (1 / min_clk_period), and not (2 / min_clk_period).
- * This new formula was verified with a scope and validated by
- * Allwinner engineers.
- */
- chip->clk_rate = NSEC_PER_SEC / min_clk_period;
- real_clk_rate = clk_round_rate(nfc->mod_clk, chip->clk_rate);
- if (real_clk_rate <= 0) {
- dev_err(nfc->dev, "Unable to round clk %lu\n", chip->clk_rate);
- return -EINVAL;
- }
-
- /*
- * ONFI specification 3.1, paragraph 4.15.2 dictates that EDO data
- * output cycle timings shall be used if the host drives tRC less than
- * 30 ns.
- */
- min_clk_period = NSEC_PER_SEC / real_clk_rate;
- chip->timing_ctl = ((min_clk_period * 2) < 30) ?
- NFC_TIMING_CTL_EDO : 0;
-
- return 0;
-}
-
-static int sunxi_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct nand_ecc_ctrl *ecc = &nand->ecc;
-
- if (section >= ecc->steps)
- return -ERANGE;
-
- oobregion->offset = section * (ecc->bytes + 4) + 4;
- oobregion->length = ecc->bytes;
-
- return 0;
-}
-
-static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct nand_ecc_ctrl *ecc = &nand->ecc;
-
- if (section > ecc->steps)
- return -ERANGE;
-
- /*
- * The first 2 bytes are used for BB markers, hence we
- * only have 2 bytes available in the first user data
- * section.
- */
- if (!section && ecc->mode == NAND_ECC_HW) {
- oobregion->offset = 2;
- oobregion->length = 2;
-
- return 0;
- }
-
- oobregion->offset = section * (ecc->bytes + 4);
-
- if (section < ecc->steps)
- oobregion->length = 4;
- else
- oobregion->offset = mtd->oobsize - oobregion->offset;
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops sunxi_nand_ooblayout_ops = {
- .ecc = sunxi_nand_ooblayout_ecc,
- .free = sunxi_nand_ooblayout_free,
-};
-
-static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd,
- struct nand_ecc_ctrl *ecc,
- struct device_node *np)
-{
- static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 };
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
- struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
- struct sunxi_nand_hw_ecc *data;
- int nsectors;
- int ret;
- int i;
-
- if (ecc->options & NAND_ECC_MAXIMIZE) {
- int bytes;
-
- ecc->size = 1024;
- nsectors = mtd->writesize / ecc->size;
-
- /* Reserve 2 bytes for the BBM */
- bytes = (mtd->oobsize - 2) / nsectors;
-
- /* 4 non-ECC bytes are added before each ECC bytes section */
- bytes -= 4;
-
- /* and bytes has to be even. */
- if (bytes % 2)
- bytes--;
-
- ecc->strength = bytes * 8 / fls(8 * ecc->size);
-
- for (i = 0; i < ARRAY_SIZE(strengths); i++) {
- if (strengths[i] > ecc->strength)
- break;
- }
-
- if (!i)
- ecc->strength = 0;
- else
- ecc->strength = strengths[i - 1];
- }
-
- if (ecc->size != 512 && ecc->size != 1024)
- return -EINVAL;
-
- data = kzalloc(sizeof(*data), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- /* Prefer 1k ECC chunk over 512 ones */
- if (ecc->size == 512 && mtd->writesize > 512) {
- ecc->size = 1024;
- ecc->strength *= 2;
- }
-
- /* Add ECC info retrieval from DT */
- for (i = 0; i < ARRAY_SIZE(strengths); i++) {
- if (ecc->strength <= strengths[i]) {
- /*
- * Update ecc->strength value with the actual strength
- * that will be used by the ECC engine.
- */
- ecc->strength = strengths[i];
- break;
- }
- }
-
- if (i >= ARRAY_SIZE(strengths)) {
- dev_err(nfc->dev, "unsupported strength\n");
- ret = -ENOTSUPP;
- goto err;
- }
-
- data->mode = i;
-
- /* HW ECC always request ECC bytes for 1024 bytes blocks */
- ecc->bytes = DIV_ROUND_UP(ecc->strength * fls(8 * 1024), 8);
-
- /* HW ECC always work with even numbers of ECC bytes */
- ecc->bytes = ALIGN(ecc->bytes, 2);
-
- nsectors = mtd->writesize / ecc->size;
-
- if (mtd->oobsize < ((ecc->bytes + 4) * nsectors)) {
- ret = -EINVAL;
- goto err;
- }
-
- ecc->read_oob = sunxi_nfc_hw_common_ecc_read_oob;
- ecc->write_oob = sunxi_nfc_hw_common_ecc_write_oob;
- mtd_set_ooblayout(mtd, &sunxi_nand_ooblayout_ops);
- ecc->priv = data;
-
- return 0;
-
-err:
- kfree(data);
-
- return ret;
-}
-
-static void sunxi_nand_hw_common_ecc_ctrl_cleanup(struct nand_ecc_ctrl *ecc)
-{
- kfree(ecc->priv);
-}
-
-static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd,
- struct nand_ecc_ctrl *ecc,
- struct device_node *np)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
- struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
- int ret;
-
- ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc, np);
- if (ret)
- return ret;
-
- if (nfc->dmac) {
- ecc->read_page = sunxi_nfc_hw_ecc_read_page_dma;
- ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage_dma;
- ecc->write_page = sunxi_nfc_hw_ecc_write_page_dma;
- nand->options |= NAND_USE_BOUNCE_BUFFER;
- } else {
- ecc->read_page = sunxi_nfc_hw_ecc_read_page;
- ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage;
- ecc->write_page = sunxi_nfc_hw_ecc_write_page;
- }
-
- /* TODO: support DMA for raw accesses and subpage write */
- ecc->write_subpage = sunxi_nfc_hw_ecc_write_subpage;
- ecc->read_oob_raw = nand_read_oob_std;
- ecc->write_oob_raw = nand_write_oob_std;
-
- return 0;
-}
-
-static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd,
- struct nand_ecc_ctrl *ecc,
- struct device_node *np)
-{
- int ret;
-
- ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc, np);
- if (ret)
- return ret;
-
- ecc->prepad = 4;
- ecc->read_page = sunxi_nfc_hw_syndrome_ecc_read_page;
- ecc->write_page = sunxi_nfc_hw_syndrome_ecc_write_page;
- ecc->read_oob_raw = nand_read_oob_syndrome;
- ecc->write_oob_raw = nand_write_oob_syndrome;
-
- return 0;
-}
-
-static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc)
-{
- switch (ecc->mode) {
- case NAND_ECC_HW:
- case NAND_ECC_HW_SYNDROME:
- sunxi_nand_hw_common_ecc_ctrl_cleanup(ecc);
- break;
- case NAND_ECC_NONE:
- default:
- break;
- }
-}
-
-static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc,
- struct device_node *np)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- int ret;
-
- if (!ecc->size) {
- ecc->size = nand->ecc_step_ds;
- ecc->strength = nand->ecc_strength_ds;
- }
-
- if (!ecc->size || !ecc->strength)
- return -EINVAL;
-
- switch (ecc->mode) {
- case NAND_ECC_HW:
- ret = sunxi_nand_hw_ecc_ctrl_init(mtd, ecc, np);
- if (ret)
- return ret;
- break;
- case NAND_ECC_HW_SYNDROME:
- ret = sunxi_nand_hw_syndrome_ecc_ctrl_init(mtd, ecc, np);
- if (ret)
- return ret;
- break;
- case NAND_ECC_NONE:
- case NAND_ECC_SOFT:
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
- struct device_node *np)
-{
- struct sunxi_nand_chip *chip;
- struct mtd_info *mtd;
- struct nand_chip *nand;
- int nsels;
- int ret;
- int i;
- u32 tmp;
-
- if (!of_get_property(np, "reg", &nsels))
- return -EINVAL;
-
- nsels /= sizeof(u32);
- if (!nsels) {
- dev_err(dev, "invalid reg property size\n");
- return -EINVAL;
- }
-
- chip = devm_kzalloc(dev,
- sizeof(*chip) +
- (nsels * sizeof(struct sunxi_nand_chip_sel)),
- GFP_KERNEL);
- if (!chip) {
- dev_err(dev, "could not allocate chip\n");
- return -ENOMEM;
- }
-
- chip->nsels = nsels;
- chip->selected = -1;
-
- for (i = 0; i < nsels; i++) {
- ret = of_property_read_u32_index(np, "reg", i, &tmp);
- if (ret) {
- dev_err(dev, "could not retrieve reg property: %d\n",
- ret);
- return ret;
- }
-
- if (tmp > NFC_MAX_CS) {
- dev_err(dev,
- "invalid reg value: %u (max CS = 7)\n",
- tmp);
- return -EINVAL;
- }
-
- if (test_and_set_bit(tmp, &nfc->assigned_cs)) {
- dev_err(dev, "CS %d already assigned\n", tmp);
- return -EINVAL;
- }
-
- chip->sels[i].cs = tmp;
-
- if (!of_property_read_u32_index(np, "allwinner,rb", i, &tmp) &&
- tmp < 2) {
- chip->sels[i].rb.type = RB_NATIVE;
- chip->sels[i].rb.info.nativeid = tmp;
- } else {
- ret = of_get_named_gpio(np, "rb-gpios", i);
- if (ret >= 0) {
- tmp = ret;
- chip->sels[i].rb.type = RB_GPIO;
- chip->sels[i].rb.info.gpio = tmp;
- ret = devm_gpio_request(dev, tmp, "nand-rb");
- if (ret)
- return ret;
-
- ret = gpio_direction_input(tmp);
- if (ret)
- return ret;
- } else {
- chip->sels[i].rb.type = RB_NONE;
- }
- }
- }
-
- nand = &chip->nand;
- /* Default tR value specified in the ONFI spec (chapter 4.15.1) */
- nand->chip_delay = 200;
- nand->controller = &nfc->controller;
- /*
- * Set the ECC mode to the default value in case nothing is specified
- * in the DT.
- */
- nand->ecc.mode = NAND_ECC_HW;
- nand_set_flash_node(nand, np);
- nand->select_chip = sunxi_nfc_select_chip;
- nand->cmd_ctrl = sunxi_nfc_cmd_ctrl;
- nand->read_buf = sunxi_nfc_read_buf;
- nand->write_buf = sunxi_nfc_write_buf;
- nand->read_byte = sunxi_nfc_read_byte;
- nand->setup_data_interface = sunxi_nfc_setup_data_interface;
-
- mtd = nand_to_mtd(nand);
- mtd->dev.parent = dev;
-
- ret = nand_scan_ident(mtd, nsels, NULL);
- if (ret)
- return ret;
-
- if (nand->bbt_options & NAND_BBT_USE_FLASH)
- nand->bbt_options |= NAND_BBT_NO_OOB;
-
- if (nand->options & NAND_NEED_SCRAMBLING)
- nand->options |= NAND_NO_SUBPAGE_WRITE;
-
- nand->options |= NAND_SUBPAGE_READ;
-
- ret = sunxi_nand_ecc_init(mtd, &nand->ecc, np);
- if (ret) {
- dev_err(dev, "ECC init failed: %d\n", ret);
- return ret;
- }
-
- ret = nand_scan_tail(mtd);
- if (ret) {
- dev_err(dev, "nand_scan_tail failed: %d\n", ret);
- return ret;
- }
-
- ret = mtd_device_register(mtd, NULL, 0);
- if (ret) {
- dev_err(dev, "failed to register mtd device: %d\n", ret);
- nand_release(mtd);
- return ret;
- }
-
- list_add_tail(&chip->node, &nfc->chips);
-
- return 0;
-}
-
-static int sunxi_nand_chips_init(struct device *dev, struct sunxi_nfc *nfc)
-{
- struct device_node *np = dev->of_node;
- struct device_node *nand_np;
- int nchips = of_get_child_count(np);
- int ret;
-
- if (nchips > 8) {
- dev_err(dev, "too many NAND chips: %d (max = 8)\n", nchips);
- return -EINVAL;
- }
-
- for_each_child_of_node(np, nand_np) {
- ret = sunxi_nand_chip_init(dev, nfc, nand_np);
- if (ret) {
- of_node_put(nand_np);
- return ret;
- }
- }
-
- return 0;
-}
-
-static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc)
-{
- struct sunxi_nand_chip *chip;
-
- while (!list_empty(&nfc->chips)) {
- chip = list_first_entry(&nfc->chips, struct sunxi_nand_chip,
- node);
- nand_release(nand_to_mtd(&chip->nand));
- sunxi_nand_ecc_cleanup(&chip->nand.ecc);
- list_del(&chip->node);
- }
-}
-
-static int sunxi_nfc_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct resource *r;
- struct sunxi_nfc *nfc;
- int irq;
- int ret;
-
- nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL);
- if (!nfc)
- return -ENOMEM;
-
- nfc->dev = dev;
- nand_hw_control_init(&nfc->controller);
- INIT_LIST_HEAD(&nfc->chips);
-
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- nfc->regs = devm_ioremap_resource(dev, r);
- if (IS_ERR(nfc->regs))
- return PTR_ERR(nfc->regs);
-
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(dev, "failed to retrieve irq\n");
- return irq;
- }
-
- nfc->ahb_clk = devm_clk_get(dev, "ahb");
- if (IS_ERR(nfc->ahb_clk)) {
- dev_err(dev, "failed to retrieve ahb clk\n");
- return PTR_ERR(nfc->ahb_clk);
- }
-
- ret = clk_prepare_enable(nfc->ahb_clk);
- if (ret)
- return ret;
-
- nfc->mod_clk = devm_clk_get(dev, "mod");
- if (IS_ERR(nfc->mod_clk)) {
- dev_err(dev, "failed to retrieve mod clk\n");
- ret = PTR_ERR(nfc->mod_clk);
- goto out_ahb_clk_unprepare;
- }
-
- ret = clk_prepare_enable(nfc->mod_clk);
- if (ret)
- goto out_ahb_clk_unprepare;
-
- nfc->reset = devm_reset_control_get_optional_exclusive(dev, "ahb");
- if (IS_ERR(nfc->reset)) {
- ret = PTR_ERR(nfc->reset);
- goto out_mod_clk_unprepare;
- }
-
- ret = reset_control_deassert(nfc->reset);
- if (ret) {
- dev_err(dev, "reset err %d\n", ret);
- goto out_mod_clk_unprepare;
- }
-
- ret = sunxi_nfc_rst(nfc);
- if (ret)
- goto out_ahb_reset_reassert;
-
- writel(0, nfc->regs + NFC_REG_INT);
- ret = devm_request_irq(dev, irq, sunxi_nfc_interrupt,
- 0, "sunxi-nand", nfc);
- if (ret)
- goto out_ahb_reset_reassert;
-
- nfc->dmac = dma_request_slave_channel(dev, "rxtx");
- if (nfc->dmac) {
- struct dma_slave_config dmac_cfg = { };
-
- dmac_cfg.src_addr = r->start + NFC_REG_IO_DATA;
- dmac_cfg.dst_addr = dmac_cfg.src_addr;
- dmac_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- dmac_cfg.dst_addr_width = dmac_cfg.src_addr_width;
- dmac_cfg.src_maxburst = 4;
- dmac_cfg.dst_maxburst = 4;
- dmaengine_slave_config(nfc->dmac, &dmac_cfg);
- } else {
- dev_warn(dev, "failed to request rxtx DMA channel\n");
- }
-
- platform_set_drvdata(pdev, nfc);
-
- ret = sunxi_nand_chips_init(dev, nfc);
- if (ret) {
- dev_err(dev, "failed to init nand chips\n");
- goto out_release_dmac;
- }
-
- return 0;
-
-out_release_dmac:
- if (nfc->dmac)
- dma_release_channel(nfc->dmac);
-out_ahb_reset_reassert:
- reset_control_assert(nfc->reset);
-out_mod_clk_unprepare:
- clk_disable_unprepare(nfc->mod_clk);
-out_ahb_clk_unprepare:
- clk_disable_unprepare(nfc->ahb_clk);
-
- return ret;
-}
-
-static int sunxi_nfc_remove(struct platform_device *pdev)
-{
- struct sunxi_nfc *nfc = platform_get_drvdata(pdev);
-
- sunxi_nand_chips_cleanup(nfc);
-
- reset_control_assert(nfc->reset);
-
- if (nfc->dmac)
- dma_release_channel(nfc->dmac);
- clk_disable_unprepare(nfc->mod_clk);
- clk_disable_unprepare(nfc->ahb_clk);
-
- return 0;
-}
-
-static const struct of_device_id sunxi_nfc_ids[] = {
- { .compatible = "allwinner,sun4i-a10-nand" },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, sunxi_nfc_ids);
-
-static struct platform_driver sunxi_nfc_driver = {
- .driver = {
- .name = "sunxi_nand",
- .of_match_table = sunxi_nfc_ids,
- },
- .probe = sunxi_nfc_probe,
- .remove = sunxi_nfc_remove,
-};
-module_platform_driver(sunxi_nfc_driver);
-
-MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Boris BREZILLON");
-MODULE_DESCRIPTION("Allwinner NAND Flash Controller driver");
-MODULE_ALIAS("platform:sunxi_nand");
diff --git a/drivers/mtd/nand/tango_nand.c b/drivers/mtd/nand/tango_nand.c
deleted file mode 100644
index c5bee00b7f5e..000000000000
--- a/drivers/mtd/nand/tango_nand.c
+++ /dev/null
@@ -1,688 +0,0 @@
-/*
- * Copyright (C) 2016 Sigma Designs
- *
- * 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.
- */
-
-#include <linux/io.h>
-#include <linux/of.h>
-#include <linux/clk.h>
-#include <linux/iopoll.h>
-#include <linux/module.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/dmaengine.h>
-#include <linux/dma-mapping.h>
-#include <linux/platform_device.h>
-
-/* Offsets relative to chip->base */
-#define PBUS_CMD 0
-#define PBUS_ADDR 4
-#define PBUS_DATA 8
-
-/* Offsets relative to reg_base */
-#define NFC_STATUS 0x00
-#define NFC_FLASH_CMD 0x04
-#define NFC_DEVICE_CFG 0x08
-#define NFC_TIMING1 0x0c
-#define NFC_TIMING2 0x10
-#define NFC_XFER_CFG 0x14
-#define NFC_PKT_0_CFG 0x18
-#define NFC_PKT_N_CFG 0x1c
-#define NFC_BB_CFG 0x20
-#define NFC_ADDR_PAGE 0x24
-#define NFC_ADDR_OFFSET 0x28
-#define NFC_XFER_STATUS 0x2c
-
-/* NFC_STATUS values */
-#define CMD_READY BIT(31)
-
-/* NFC_FLASH_CMD values */
-#define NFC_READ 1
-#define NFC_WRITE 2
-
-/* NFC_XFER_STATUS values */
-#define PAGE_IS_EMPTY BIT(16)
-
-/* Offsets relative to mem_base */
-#define METADATA 0x000
-#define ERROR_REPORT 0x1c0
-
-/*
- * Error reports are split in two bytes:
- * byte 0 for the first packet in the page (PKT_0)
- * byte 1 for other packets in the page (PKT_N, for N > 0)
- * ERR_COUNT_PKT_N is the max error count over all but the first packet.
- */
-#define ERR_COUNT_PKT_0(v) (((v) >> 0) & 0x3f)
-#define ERR_COUNT_PKT_N(v) (((v) >> 8) & 0x3f)
-#define DECODE_FAIL_PKT_0(v) (((v) & BIT(7)) == 0)
-#define DECODE_FAIL_PKT_N(v) (((v) & BIT(15)) == 0)
-
-/* Offsets relative to pbus_base */
-#define PBUS_CS_CTRL 0x83c
-#define PBUS_PAD_MODE 0x8f0
-
-/* PBUS_CS_CTRL values */
-#define PBUS_IORDY BIT(31)
-
-/*
- * PBUS_PAD_MODE values
- * In raw mode, the driver communicates directly with the NAND chips.
- * In NFC mode, the NAND Flash controller manages the communication.
- * We use NFC mode for read and write; raw mode for everything else.
- */
-#define MODE_RAW 0
-#define MODE_NFC BIT(31)
-
-#define METADATA_SIZE 4
-#define BBM_SIZE 6
-#define FIELD_ORDER 15
-
-#define MAX_CS 4
-
-struct tango_nfc {
- struct nand_hw_control hw;
- void __iomem *reg_base;
- void __iomem *mem_base;
- void __iomem *pbus_base;
- struct tango_chip *chips[MAX_CS];
- struct dma_chan *chan;
- int freq_kHz;
-};
-
-#define to_tango_nfc(ptr) container_of(ptr, struct tango_nfc, hw)
-
-struct tango_chip {
- struct nand_chip nand_chip;
- void __iomem *base;
- u32 timing1;
- u32 timing2;
- u32 xfer_cfg;
- u32 pkt_0_cfg;
- u32 pkt_n_cfg;
- u32 bb_cfg;
-};
-
-#define to_tango_chip(ptr) container_of(ptr, struct tango_chip, nand_chip)
-
-#define XFER_CFG(cs, page_count, steps, metadata_size) \
- ((cs) << 24 | (page_count) << 16 | (steps) << 8 | (metadata_size))
-
-#define PKT_CFG(size, strength) ((size) << 16 | (strength))
-
-#define BB_CFG(bb_offset, bb_size) ((bb_offset) << 16 | (bb_size))
-
-#define TIMING(t0, t1, t2, t3) ((t0) << 24 | (t1) << 16 | (t2) << 8 | (t3))
-
-static void tango_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
-{
- struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd));
-
- if (ctrl & NAND_CLE)
- writeb_relaxed(dat, tchip->base + PBUS_CMD);
-
- if (ctrl & NAND_ALE)
- writeb_relaxed(dat, tchip->base + PBUS_ADDR);
-}
-
-static int tango_dev_ready(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct tango_nfc *nfc = to_tango_nfc(chip->controller);
-
- return readl_relaxed(nfc->pbus_base + PBUS_CS_CTRL) & PBUS_IORDY;
-}
-
-static u8 tango_read_byte(struct mtd_info *mtd)
-{
- struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd));
-
- return readb_relaxed(tchip->base + PBUS_DATA);
-}
-
-static void tango_read_buf(struct mtd_info *mtd, u8 *buf, int len)
-{
- struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd));
-
- ioread8_rep(tchip->base + PBUS_DATA, buf, len);
-}
-
-static void tango_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
-{
- struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd));
-
- iowrite8_rep(tchip->base + PBUS_DATA, buf, len);
-}
-
-static void tango_select_chip(struct mtd_info *mtd, int idx)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct tango_nfc *nfc = to_tango_nfc(chip->controller);
- struct tango_chip *tchip = to_tango_chip(chip);
-
- if (idx < 0)
- return; /* No "chip unselect" function */
-
- writel_relaxed(tchip->timing1, nfc->reg_base + NFC_TIMING1);
- writel_relaxed(tchip->timing2, nfc->reg_base + NFC_TIMING2);
- writel_relaxed(tchip->xfer_cfg, nfc->reg_base + NFC_XFER_CFG);
- writel_relaxed(tchip->pkt_0_cfg, nfc->reg_base + NFC_PKT_0_CFG);
- writel_relaxed(tchip->pkt_n_cfg, nfc->reg_base + NFC_PKT_N_CFG);
- writel_relaxed(tchip->bb_cfg, nfc->reg_base + NFC_BB_CFG);
-}
-
-/*
- * The controller does not check for bitflips in erased pages,
- * therefore software must check instead.
- */
-static int check_erased_page(struct nand_chip *chip, u8 *buf)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- u8 *meta = chip->oob_poi + BBM_SIZE;
- u8 *ecc = chip->oob_poi + BBM_SIZE + METADATA_SIZE;
- const int ecc_size = chip->ecc.bytes;
- const int pkt_size = chip->ecc.size;
- int i, res, meta_len, bitflips = 0;
-
- for (i = 0; i < chip->ecc.steps; ++i) {
- meta_len = i ? 0 : METADATA_SIZE;
- res = nand_check_erased_ecc_chunk(buf, pkt_size, ecc, ecc_size,
- meta, meta_len,
- chip->ecc.strength);
- if (res < 0)
- mtd->ecc_stats.failed++;
- else
- mtd->ecc_stats.corrected += res;
-
- bitflips = max(res, bitflips);
- buf += pkt_size;
- ecc += ecc_size;
- }
-
- return bitflips;
-}
-
-static int decode_error_report(struct nand_chip *chip)
-{
- u32 status, res;
- struct mtd_info *mtd = nand_to_mtd(chip);
- struct tango_nfc *nfc = to_tango_nfc(chip->controller);
-
- status = readl_relaxed(nfc->reg_base + NFC_XFER_STATUS);
- if (status & PAGE_IS_EMPTY)
- return 0;
-
- res = readl_relaxed(nfc->mem_base + ERROR_REPORT);
-
- if (DECODE_FAIL_PKT_0(res) || DECODE_FAIL_PKT_N(res))
- return -EBADMSG;
-
- /* ERR_COUNT_PKT_N is max, not sum, but that's all we have */
- mtd->ecc_stats.corrected +=
- ERR_COUNT_PKT_0(res) + ERR_COUNT_PKT_N(res);
-
- return max(ERR_COUNT_PKT_0(res), ERR_COUNT_PKT_N(res));
-}
-
-static void tango_dma_callback(void *arg)
-{
- complete(arg);
-}
-
-static int do_dma(struct tango_nfc *nfc, enum dma_data_direction dir, int cmd,
- const void *buf, int len, int page)
-{
- void __iomem *addr = nfc->reg_base + NFC_STATUS;
- struct dma_chan *chan = nfc->chan;
- struct dma_async_tx_descriptor *desc;
- enum dma_transfer_direction tdir;
- struct scatterlist sg;
- struct completion tx_done;
- int err = -EIO;
- u32 res, val;
-
- sg_init_one(&sg, buf, len);
- if (dma_map_sg(chan->device->dev, &sg, 1, dir) != 1)
- return -EIO;
-
- tdir = dir == DMA_TO_DEVICE ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
- desc = dmaengine_prep_slave_sg(chan, &sg, 1, tdir, DMA_PREP_INTERRUPT);
- if (!desc)
- goto dma_unmap;
-
- desc->callback = tango_dma_callback;
- desc->callback_param = &tx_done;
- init_completion(&tx_done);
-
- writel_relaxed(MODE_NFC, nfc->pbus_base + PBUS_PAD_MODE);
-
- writel_relaxed(page, nfc->reg_base + NFC_ADDR_PAGE);
- writel_relaxed(0, nfc->reg_base + NFC_ADDR_OFFSET);
- writel_relaxed(cmd, nfc->reg_base + NFC_FLASH_CMD);
-
- dmaengine_submit(desc);
- dma_async_issue_pending(chan);
-
- res = wait_for_completion_timeout(&tx_done, HZ);
- if (res > 0)
- err = readl_poll_timeout(addr, val, val & CMD_READY, 0, 1000);
-
- writel_relaxed(MODE_RAW, nfc->pbus_base + PBUS_PAD_MODE);
-
-dma_unmap:
- dma_unmap_sg(chan->device->dev, &sg, 1, dir);
-
- return err;
-}
-
-static int tango_read_page(struct mtd_info *mtd, struct nand_chip *chip,
- u8 *buf, int oob_required, int page)
-{
- struct tango_nfc *nfc = to_tango_nfc(chip->controller);
- int err, res, len = mtd->writesize;
-
- if (oob_required)
- chip->ecc.read_oob(mtd, chip, page);
-
- err = do_dma(nfc, DMA_FROM_DEVICE, NFC_READ, buf, len, page);
- if (err)
- return err;
-
- res = decode_error_report(chip);
- if (res < 0) {
- chip->ecc.read_oob_raw(mtd, chip, page);
- res = check_erased_page(chip, buf);
- }
-
- return res;
-}
-
-static int tango_write_page(struct mtd_info *mtd, struct nand_chip *chip,
- const u8 *buf, int oob_required, int page)
-{
- struct tango_nfc *nfc = to_tango_nfc(chip->controller);
- int err, status, len = mtd->writesize;
-
- /* Calling tango_write_oob() would send PAGEPROG twice */
- if (oob_required)
- return -ENOTSUPP;
-
- writel_relaxed(0xffffffff, nfc->mem_base + METADATA);
- err = do_dma(nfc, DMA_TO_DEVICE, NFC_WRITE, buf, len, page);
- if (err)
- return err;
-
- status = chip->waitfunc(mtd, chip);
- if (status & NAND_STATUS_FAIL)
- return -EIO;
-
- return 0;
-}
-
-static void aux_read(struct nand_chip *chip, u8 **buf, int len, int *pos)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
-
- *pos += len;
-
- if (!*buf) {
- /* skip over "len" bytes */
- nand_change_read_column_op(chip, *pos, NULL, 0, false);
- } else {
- tango_read_buf(mtd, *buf, len);
- *buf += len;
- }
-}
-
-static void aux_write(struct nand_chip *chip, const u8 **buf, int len, int *pos)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
-
- *pos += len;
-
- if (!*buf) {
- /* skip over "len" bytes */
- nand_change_write_column_op(chip, *pos, NULL, 0, false);
- } else {
- tango_write_buf(mtd, *buf, len);
- *buf += len;
- }
-}
-
-/*
- * Physical page layout (not drawn to scale)
- *
- * NB: Bad Block Marker area splits PKT_N in two (N1, N2).
- *
- * +---+-----------------+-------+-----+-----------+-----+----+-------+
- * | M | PKT_0 | ECC_0 | ... | N1 | BBM | N2 | ECC_N |
- * +---+-----------------+-------+-----+-----------+-----+----+-------+
- *
- * Logical page layout:
- *
- * +-----+---+-------+-----+-------+
- * oob = | BBM | M | ECC_0 | ... | ECC_N |
- * +-----+---+-------+-----+-------+
- *
- * +-----------------+-----+-----------------+
- * buf = | PKT_0 | ... | PKT_N |
- * +-----------------+-----+-----------------+
- */
-static void raw_read(struct nand_chip *chip, u8 *buf, u8 *oob)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- u8 *oob_orig = oob;
- const int page_size = mtd->writesize;
- const int ecc_size = chip->ecc.bytes;
- const int pkt_size = chip->ecc.size;
- int pos = 0; /* position within physical page */
- int rem = page_size; /* bytes remaining until BBM area */
-
- if (oob)
- oob += BBM_SIZE;
-
- aux_read(chip, &oob, METADATA_SIZE, &pos);
-
- while (rem > pkt_size) {
- aux_read(chip, &buf, pkt_size, &pos);
- aux_read(chip, &oob, ecc_size, &pos);
- rem = page_size - pos;
- }
-
- aux_read(chip, &buf, rem, &pos);
- aux_read(chip, &oob_orig, BBM_SIZE, &pos);
- aux_read(chip, &buf, pkt_size - rem, &pos);
- aux_read(chip, &oob, ecc_size, &pos);
-}
-
-static void raw_write(struct nand_chip *chip, const u8 *buf, const u8 *oob)
-{
- struct mtd_info *mtd = nand_to_mtd(chip);
- const u8 *oob_orig = oob;
- const int page_size = mtd->writesize;
- const int ecc_size = chip->ecc.bytes;
- const int pkt_size = chip->ecc.size;
- int pos = 0; /* position within physical page */
- int rem = page_size; /* bytes remaining until BBM area */
-
- if (oob)
- oob += BBM_SIZE;
-
- aux_write(chip, &oob, METADATA_SIZE, &pos);
-
- while (rem > pkt_size) {
- aux_write(chip, &buf, pkt_size, &pos);
- aux_write(chip, &oob, ecc_size, &pos);
- rem = page_size - pos;
- }
-
- aux_write(chip, &buf, rem, &pos);
- aux_write(chip, &oob_orig, BBM_SIZE, &pos);
- aux_write(chip, &buf, pkt_size - rem, &pos);
- aux_write(chip, &oob, ecc_size, &pos);
-}
-
-static int tango_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
- u8 *buf, int oob_required, int page)
-{
- nand_read_page_op(chip, page, 0, NULL, 0);
- raw_read(chip, buf, chip->oob_poi);
- return 0;
-}
-
-static int tango_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
- const u8 *buf, int oob_required, int page)
-{
- nand_prog_page_begin_op(chip, page, 0, NULL, 0);
- raw_write(chip, buf, chip->oob_poi);
- return nand_prog_page_end_op(chip);
-}
-
-static int tango_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
- int page)
-{
- nand_read_page_op(chip, page, 0, NULL, 0);
- raw_read(chip, NULL, chip->oob_poi);
- return 0;
-}
-
-static int tango_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
- int page)
-{
- nand_prog_page_begin_op(chip, page, 0, NULL, 0);
- raw_write(chip, NULL, chip->oob_poi);
- return nand_prog_page_end_op(chip);
-}
-
-static int oob_ecc(struct mtd_info *mtd, int idx, struct mtd_oob_region *res)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct nand_ecc_ctrl *ecc = &chip->ecc;
-
- if (idx >= ecc->steps)
- return -ERANGE;
-
- res->offset = BBM_SIZE + METADATA_SIZE + ecc->bytes * idx;
- res->length = ecc->bytes;
-
- return 0;
-}
-
-static int oob_free(struct mtd_info *mtd, int idx, struct mtd_oob_region *res)
-{
- return -ERANGE; /* no free space in spare area */
-}
-
-static const struct mtd_ooblayout_ops tango_nand_ooblayout_ops = {
- .ecc = oob_ecc,
- .free = oob_free,
-};
-
-static u32 to_ticks(int kHz, int ps)
-{
- return DIV_ROUND_UP_ULL((u64)kHz * ps, NSEC_PER_SEC);
-}
-
-static int tango_set_timings(struct mtd_info *mtd, int csline,
- const struct nand_data_interface *conf)
-{
- const struct nand_sdr_timings *sdr = nand_get_sdr_timings(conf);
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct tango_nfc *nfc = to_tango_nfc(chip->controller);
- struct tango_chip *tchip = to_tango_chip(chip);
- u32 Trdy, Textw, Twc, Twpw, Tacc, Thold, Trpw, Textr;
- int kHz = nfc->freq_kHz;
-
- if (IS_ERR(sdr))
- return PTR_ERR(sdr);
-
- if (csline == NAND_DATA_IFACE_CHECK_ONLY)
- return 0;
-
- Trdy = to_ticks(kHz, sdr->tCEA_max - sdr->tREA_max);
- Textw = to_ticks(kHz, sdr->tWB_max);
- Twc = to_ticks(kHz, sdr->tWC_min);
- Twpw = to_ticks(kHz, sdr->tWC_min - sdr->tWP_min);
-
- Tacc = to_ticks(kHz, sdr->tREA_max);
- Thold = to_ticks(kHz, sdr->tREH_min);
- Trpw = to_ticks(kHz, sdr->tRC_min - sdr->tREH_min);
- Textr = to_ticks(kHz, sdr->tRHZ_max);
-
- tchip->timing1 = TIMING(Trdy, Textw, Twc, Twpw);
- tchip->timing2 = TIMING(Tacc, Thold, Trpw, Textr);
-
- return 0;
-}
-
-static int chip_init(struct device *dev, struct device_node *np)
-{
- u32 cs;
- int err, res;
- struct mtd_info *mtd;
- struct nand_chip *chip;
- struct tango_chip *tchip;
- struct nand_ecc_ctrl *ecc;
- struct tango_nfc *nfc = dev_get_drvdata(dev);
-
- tchip = devm_kzalloc(dev, sizeof(*tchip), GFP_KERNEL);
- if (!tchip)
- return -ENOMEM;
-
- res = of_property_count_u32_elems(np, "reg");
- if (res < 0)
- return res;
-
- if (res != 1)
- return -ENOTSUPP; /* Multi-CS chips are not supported */
-
- err = of_property_read_u32_index(np, "reg", 0, &cs);
- if (err)
- return err;
-
- if (cs >= MAX_CS)
- return -EINVAL;
-
- chip = &tchip->nand_chip;
- ecc = &chip->ecc;
- mtd = nand_to_mtd(chip);
-
- chip->read_byte = tango_read_byte;
- chip->write_buf = tango_write_buf;
- chip->read_buf = tango_read_buf;
- chip->select_chip = tango_select_chip;
- chip->cmd_ctrl = tango_cmd_ctrl;
- chip->dev_ready = tango_dev_ready;
- chip->setup_data_interface = tango_set_timings;
- chip->options = NAND_USE_BOUNCE_BUFFER |
- NAND_NO_SUBPAGE_WRITE |
- NAND_WAIT_TCCS;
- chip->controller = &nfc->hw;
- tchip->base = nfc->pbus_base + (cs * 256);
-
- nand_set_flash_node(chip, np);
- mtd_set_ooblayout(mtd, &tango_nand_ooblayout_ops);
- mtd->dev.parent = dev;
-
- err = nand_scan_ident(mtd, 1, NULL);
- if (err)
- return err;
-
- ecc->mode = NAND_ECC_HW;
- ecc->algo = NAND_ECC_BCH;
- ecc->bytes = DIV_ROUND_UP(ecc->strength * FIELD_ORDER, BITS_PER_BYTE);
-
- ecc->read_page_raw = tango_read_page_raw;
- ecc->write_page_raw = tango_write_page_raw;
- ecc->read_page = tango_read_page;
- ecc->write_page = tango_write_page;
- ecc->read_oob = tango_read_oob;
- ecc->write_oob = tango_write_oob;
-
- err = nand_scan_tail(mtd);
- if (err)
- return err;
-
- tchip->xfer_cfg = XFER_CFG(cs, 1, ecc->steps, METADATA_SIZE);
- tchip->pkt_0_cfg = PKT_CFG(ecc->size + METADATA_SIZE, ecc->strength);
- tchip->pkt_n_cfg = PKT_CFG(ecc->size, ecc->strength);
- tchip->bb_cfg = BB_CFG(mtd->writesize, BBM_SIZE);
-
- err = mtd_device_register(mtd, NULL, 0);
- if (err)
- return err;
-
- nfc->chips[cs] = tchip;
-
- return 0;
-}
-
-static int tango_nand_remove(struct platform_device *pdev)
-{
- int cs;
- struct tango_nfc *nfc = platform_get_drvdata(pdev);
-
- dma_release_channel(nfc->chan);
-
- for (cs = 0; cs < MAX_CS; ++cs) {
- if (nfc->chips[cs])
- nand_release(nand_to_mtd(&nfc->chips[cs]->nand_chip));
- }
-
- return 0;
-}
-
-static int tango_nand_probe(struct platform_device *pdev)
-{
- int err;
- struct clk *clk;
- struct resource *res;
- struct tango_nfc *nfc;
- struct device_node *np;
-
- nfc = devm_kzalloc(&pdev->dev, sizeof(*nfc), GFP_KERNEL);
- if (!nfc)
- return -ENOMEM;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- nfc->reg_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(nfc->reg_base))
- return PTR_ERR(nfc->reg_base);
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- nfc->mem_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(nfc->mem_base))
- return PTR_ERR(nfc->mem_base);
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
- nfc->pbus_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(nfc->pbus_base))
- return PTR_ERR(nfc->pbus_base);
-
- writel_relaxed(MODE_RAW, nfc->pbus_base + PBUS_PAD_MODE);
-
- clk = clk_get(&pdev->dev, NULL);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
-
- nfc->chan = dma_request_chan(&pdev->dev, "rxtx");
- if (IS_ERR(nfc->chan))
- return PTR_ERR(nfc->chan);
-
- platform_set_drvdata(pdev, nfc);
- nand_hw_control_init(&nfc->hw);
- nfc->freq_kHz = clk_get_rate(clk) / 1000;
-
- for_each_child_of_node(pdev->dev.of_node, np) {
- err = chip_init(&pdev->dev, np);
- if (err) {
- tango_nand_remove(pdev);
- return err;
- }
- }
-
- return 0;
-}
-
-static const struct of_device_id tango_nand_ids[] = {
- { .compatible = "sigma,smp8758-nand" },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, tango_nand_ids);
-
-static struct platform_driver tango_nand_driver = {
- .probe = tango_nand_probe,
- .remove = tango_nand_remove,
- .driver = {
- .name = "tango-nand",
- .of_match_table = tango_nand_ids,
- },
-};
-
-module_platform_driver(tango_nand_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Sigma Designs");
-MODULE_DESCRIPTION("Tango4 NAND Flash controller driver");
diff --git a/drivers/mtd/nand/vf610_nfc.c b/drivers/mtd/nand/vf610_nfc.c
deleted file mode 100644
index f367144f3c6f..000000000000
--- a/drivers/mtd/nand/vf610_nfc.c
+++ /dev/null
@@ -1,845 +0,0 @@
-/*
- * Copyright 2009-2015 Freescale Semiconductor, Inc. and others
- *
- * Description: MPC5125, VF610, MCF54418 and Kinetis K70 Nand driver.
- * Jason ported to M54418TWR and MVFA5 (VF610).
- * Authors: Stefan Agner <stefan.agner@toradex.com>
- * Bill Pringlemeir <bpringlemeir@nbsps.com>
- * Shaohui Xie <b21989@freescale.com>
- * Jason Jin <Jason.jin@freescale.com>
- *
- * Based on original driver mpc5121_nfc.c.
- *
- * This 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.
- *
- * Limitations:
- * - Untested on MPC5125 and M54418.
- * - DMA and pipelining not used.
- * - 2K pages or less.
- * - HW ECC: Only 2K page with 64+ OOB.
- * - HW ECC: Only 24 and 32-bit error correction implemented.
- */
-
-#include <linux/module.h>
-#include <linux/bitops.h>
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/partitions.h>
-#include <linux/of_device.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-
-#define DRV_NAME "vf610_nfc"
-
-/* Register Offsets */
-#define NFC_FLASH_CMD1 0x3F00
-#define NFC_FLASH_CMD2 0x3F04
-#define NFC_COL_ADDR 0x3F08
-#define NFC_ROW_ADDR 0x3F0c
-#define NFC_ROW_ADDR_INC 0x3F14
-#define NFC_FLASH_STATUS1 0x3F18
-#define NFC_FLASH_STATUS2 0x3F1c
-#define NFC_CACHE_SWAP 0x3F28
-#define NFC_SECTOR_SIZE 0x3F2c
-#define NFC_FLASH_CONFIG 0x3F30
-#define NFC_IRQ_STATUS 0x3F38
-
-/* Addresses for NFC MAIN RAM BUFFER areas */
-#define NFC_MAIN_AREA(n) ((n) * 0x1000)
-
-#define PAGE_2K 0x0800
-#define OOB_64 0x0040
-#define OOB_MAX 0x0100
-
-/*
- * NFC_CMD2[CODE] values. See section:
- * - 31.4.7 Flash Command Code Description, Vybrid manual
- * - 23.8.6 Flash Command Sequencer, MPC5125 manual
- *
- * Briefly these are bitmasks of controller cycles.
- */
-#define READ_PAGE_CMD_CODE 0x7EE0
-#define READ_ONFI_PARAM_CMD_CODE 0x4860
-#define PROGRAM_PAGE_CMD_CODE 0x7FC0
-#define ERASE_CMD_CODE 0x4EC0
-#define READ_ID_CMD_CODE 0x4804
-#define RESET_CMD_CODE 0x4040
-#define STATUS_READ_CMD_CODE 0x4068
-
-/* NFC ECC mode define */
-#define ECC_BYPASS 0
-#define ECC_45_BYTE 6
-#define ECC_60_BYTE 7
-
-/*** Register Mask and bit definitions */
-
-/* NFC_FLASH_CMD1 Field */
-#define CMD_BYTE2_MASK 0xFF000000
-#define CMD_BYTE2_SHIFT 24
-
-/* NFC_FLASH_CM2 Field */
-#define CMD_BYTE1_MASK 0xFF000000
-#define CMD_BYTE1_SHIFT 24
-#define CMD_CODE_MASK 0x00FFFF00
-#define CMD_CODE_SHIFT 8
-#define BUFNO_MASK 0x00000006
-#define BUFNO_SHIFT 1
-#define START_BIT BIT(0)
-
-/* NFC_COL_ADDR Field */
-#define COL_ADDR_MASK 0x0000FFFF
-#define COL_ADDR_SHIFT 0
-
-/* NFC_ROW_ADDR Field */
-#define ROW_ADDR_MASK 0x00FFFFFF
-#define ROW_ADDR_SHIFT 0
-#define ROW_ADDR_CHIP_SEL_RB_MASK 0xF0000000
-#define ROW_ADDR_CHIP_SEL_RB_SHIFT 28
-#define ROW_ADDR_CHIP_SEL_MASK 0x0F000000
-#define ROW_ADDR_CHIP_SEL_SHIFT 24
-
-/* NFC_FLASH_STATUS2 Field */
-#define STATUS_BYTE1_MASK 0x000000FF
-
-/* NFC_FLASH_CONFIG Field */
-#define CONFIG_ECC_SRAM_ADDR_MASK 0x7FC00000
-#define CONFIG_ECC_SRAM_ADDR_SHIFT 22
-#define CONFIG_ECC_SRAM_REQ_BIT BIT(21)
-#define CONFIG_DMA_REQ_BIT BIT(20)
-#define CONFIG_ECC_MODE_MASK 0x000E0000
-#define CONFIG_ECC_MODE_SHIFT 17
-#define CONFIG_FAST_FLASH_BIT BIT(16)
-#define CONFIG_16BIT BIT(7)
-#define CONFIG_BOOT_MODE_BIT BIT(6)
-#define CONFIG_ADDR_AUTO_INCR_BIT BIT(5)
-#define CONFIG_BUFNO_AUTO_INCR_BIT BIT(4)
-#define CONFIG_PAGE_CNT_MASK 0xF
-#define CONFIG_PAGE_CNT_SHIFT 0
-
-/* NFC_IRQ_STATUS Field */
-#define IDLE_IRQ_BIT BIT(29)
-#define IDLE_EN_BIT BIT(20)
-#define CMD_DONE_CLEAR_BIT BIT(18)
-#define IDLE_CLEAR_BIT BIT(17)
-
-/*
- * ECC status - seems to consume 8 bytes (double word). The documented
- * status byte is located in the lowest byte of the second word (which is
- * the 4th or 7th byte depending on endianness).
- * Calculate an offset to store the ECC status at the end of the buffer.
- */
-#define ECC_SRAM_ADDR (PAGE_2K + OOB_MAX - 8)
-
-#define ECC_STATUS 0x4
-#define ECC_STATUS_MASK 0x80
-#define ECC_STATUS_ERR_COUNT 0x3F
-
-enum vf610_nfc_alt_buf {
- ALT_BUF_DATA = 0,
- ALT_BUF_ID = 1,
- ALT_BUF_STAT = 2,
- ALT_BUF_ONFI = 3,
-};
-
-enum vf610_nfc_variant {
- NFC_VFC610 = 1,
-};
-
-struct vf610_nfc {
- struct nand_chip chip;
- struct device *dev;
- void __iomem *regs;
- struct completion cmd_done;
- uint buf_offset;
- int write_sz;
- /* Status and ID are in alternate locations. */
- enum vf610_nfc_alt_buf alt_buf;
- enum vf610_nfc_variant variant;
- struct clk *clk;
- bool use_hw_ecc;
- u32 ecc_mode;
-};
-
-static inline struct vf610_nfc *mtd_to_nfc(struct mtd_info *mtd)
-{
- return container_of(mtd_to_nand(mtd), struct vf610_nfc, chip);
-}
-
-static inline u32 vf610_nfc_read(struct vf610_nfc *nfc, uint reg)
-{
- return readl(nfc->regs + reg);
-}
-
-static inline void vf610_nfc_write(struct vf610_nfc *nfc, uint reg, u32 val)
-{
- writel(val, nfc->regs + reg);
-}
-
-static inline void vf610_nfc_set(struct vf610_nfc *nfc, uint reg, u32 bits)
-{
- vf610_nfc_write(nfc, reg, vf610_nfc_read(nfc, reg) | bits);
-}
-
-static inline void vf610_nfc_clear(struct vf610_nfc *nfc, uint reg, u32 bits)
-{
- vf610_nfc_write(nfc, reg, vf610_nfc_read(nfc, reg) & ~bits);
-}
-
-static inline void vf610_nfc_set_field(struct vf610_nfc *nfc, u32 reg,
- u32 mask, u32 shift, u32 val)
-{
- vf610_nfc_write(nfc, reg,
- (vf610_nfc_read(nfc, reg) & (~mask)) | val << shift);
-}
-
-static inline void vf610_nfc_memcpy(void *dst, const void __iomem *src,
- size_t n)
-{
- /*
- * Use this accessor for the internal SRAM buffers. On the ARM
- * Freescale Vybrid SoC it's known that the driver can treat
- * the SRAM buffer as if it's memory. Other platform might need
- * to treat the buffers differently.
- *
- * For the time being, use memcpy
- */
- memcpy(dst, src, n);
-}
-
-/* Clear flags for upcoming command */
-static inline void vf610_nfc_clear_status(struct vf610_nfc *nfc)
-{
- u32 tmp = vf610_nfc_read(nfc, NFC_IRQ_STATUS);
-
- tmp |= CMD_DONE_CLEAR_BIT | IDLE_CLEAR_BIT;
- vf610_nfc_write(nfc, NFC_IRQ_STATUS, tmp);
-}
-
-static void vf610_nfc_done(struct vf610_nfc *nfc)
-{
- unsigned long timeout = msecs_to_jiffies(100);
-
- /*
- * Barrier is needed after this write. This write need
- * to be done before reading the next register the first
- * time.
- * vf610_nfc_set implicates such a barrier by using writel
- * to write to the register.
- */
- vf610_nfc_set(nfc, NFC_IRQ_STATUS, IDLE_EN_BIT);
- vf610_nfc_set(nfc, NFC_FLASH_CMD2, START_BIT);
-
- if (!wait_for_completion_timeout(&nfc->cmd_done, timeout))
- dev_warn(nfc->dev, "Timeout while waiting for BUSY.\n");
-
- vf610_nfc_clear_status(nfc);
-}
-
-static u8 vf610_nfc_get_id(struct vf610_nfc *nfc, int col)
-{
- u32 flash_id;
-
- if (col < 4) {
- flash_id = vf610_nfc_read(nfc, NFC_FLASH_STATUS1);
- flash_id >>= (3 - col) * 8;
- } else {
- flash_id = vf610_nfc_read(nfc, NFC_FLASH_STATUS2);
- flash_id >>= 24;
- }
-
- return flash_id & 0xff;
-}
-
-static u8 vf610_nfc_get_status(struct vf610_nfc *nfc)
-{
- return vf610_nfc_read(nfc, NFC_FLASH_STATUS2) & STATUS_BYTE1_MASK;
-}
-
-static void vf610_nfc_send_command(struct vf610_nfc *nfc, u32 cmd_byte1,
- u32 cmd_code)
-{
- u32 tmp;
-
- vf610_nfc_clear_status(nfc);
-
- tmp = vf610_nfc_read(nfc, NFC_FLASH_CMD2);
- tmp &= ~(CMD_BYTE1_MASK | CMD_CODE_MASK | BUFNO_MASK);
- tmp |= cmd_byte1 << CMD_BYTE1_SHIFT;
- tmp |= cmd_code << CMD_CODE_SHIFT;
- vf610_nfc_write(nfc, NFC_FLASH_CMD2, tmp);
-}
-
-static void vf610_nfc_send_commands(struct vf610_nfc *nfc, u32 cmd_byte1,
- u32 cmd_byte2, u32 cmd_code)
-{
- u32 tmp;
-
- vf610_nfc_send_command(nfc, cmd_byte1, cmd_code);
-
- tmp = vf610_nfc_read(nfc, NFC_FLASH_CMD1);
- tmp &= ~CMD_BYTE2_MASK;
- tmp |= cmd_byte2 << CMD_BYTE2_SHIFT;
- vf610_nfc_write(nfc, NFC_FLASH_CMD1, tmp);
-}
-
-static irqreturn_t vf610_nfc_irq(int irq, void *data)
-{
- struct mtd_info *mtd = data;
- struct vf610_nfc *nfc = mtd_to_nfc(mtd);
-
- vf610_nfc_clear(nfc, NFC_IRQ_STATUS, IDLE_EN_BIT);
- complete(&nfc->cmd_done);
-
- return IRQ_HANDLED;
-}
-
-static void vf610_nfc_addr_cycle(struct vf610_nfc *nfc, int column, int page)
-{
- if (column != -1) {
- if (nfc->chip.options & NAND_BUSWIDTH_16)
- column = column / 2;
- vf610_nfc_set_field(nfc, NFC_COL_ADDR, COL_ADDR_MASK,
- COL_ADDR_SHIFT, column);
- }
- if (page != -1)
- vf610_nfc_set_field(nfc, NFC_ROW_ADDR, ROW_ADDR_MASK,
- ROW_ADDR_SHIFT, page);
-}
-
-static inline void vf610_nfc_ecc_mode(struct vf610_nfc *nfc, int ecc_mode)
-{
- vf610_nfc_set_field(nfc, NFC_FLASH_CONFIG,
- CONFIG_ECC_MODE_MASK,
- CONFIG_ECC_MODE_SHIFT, ecc_mode);
-}
-
-static inline void vf610_nfc_transfer_size(struct vf610_nfc *nfc, int size)
-{
- vf610_nfc_write(nfc, NFC_SECTOR_SIZE, size);
-}
-
-static void vf610_nfc_command(struct mtd_info *mtd, unsigned command,
- int column, int page)
-{
- struct vf610_nfc *nfc = mtd_to_nfc(mtd);
- int trfr_sz = nfc->chip.options & NAND_BUSWIDTH_16 ? 1 : 0;
-
- nfc->buf_offset = max(column, 0);
- nfc->alt_buf = ALT_BUF_DATA;
-
- switch (command) {
- case NAND_CMD_SEQIN:
- /* Use valid column/page from preread... */
- vf610_nfc_addr_cycle(nfc, column, page);
- nfc->buf_offset = 0;
-
- /*
- * SEQIN => data => PAGEPROG sequence is done by the controller
- * hence we do not need to issue the command here...
- */
- return;
- case NAND_CMD_PAGEPROG:
- trfr_sz += nfc->write_sz;
- vf610_nfc_transfer_size(nfc, trfr_sz);
- vf610_nfc_send_commands(nfc, NAND_CMD_SEQIN,
- command, PROGRAM_PAGE_CMD_CODE);
- if (nfc->use_hw_ecc)
- vf610_nfc_ecc_mode(nfc, nfc->ecc_mode);
- else
- vf610_nfc_ecc_mode(nfc, ECC_BYPASS);
- break;
-
- case NAND_CMD_RESET:
- vf610_nfc_transfer_size(nfc, 0);
- vf610_nfc_send_command(nfc, command, RESET_CMD_CODE);
- break;
-
- case NAND_CMD_READOOB:
- trfr_sz += mtd->oobsize;
- column = mtd->writesize;
- vf610_nfc_transfer_size(nfc, trfr_sz);
- vf610_nfc_send_commands(nfc, NAND_CMD_READ0,
- NAND_CMD_READSTART, READ_PAGE_CMD_CODE);
- vf610_nfc_addr_cycle(nfc, column, page);
- vf610_nfc_ecc_mode(nfc, ECC_BYPASS);
- break;
-
- case NAND_CMD_READ0:
- trfr_sz += mtd->writesize + mtd->oobsize;
- vf610_nfc_transfer_size(nfc, trfr_sz);
- vf610_nfc_send_commands(nfc, NAND_CMD_READ0,
- NAND_CMD_READSTART, READ_PAGE_CMD_CODE);
- vf610_nfc_addr_cycle(nfc, column, page);
- vf610_nfc_ecc_mode(nfc, nfc->ecc_mode);
- break;
-
- case NAND_CMD_PARAM:
- nfc->alt_buf = ALT_BUF_ONFI;
- trfr_sz = 3 * sizeof(struct nand_onfi_params);
- vf610_nfc_transfer_size(nfc, trfr_sz);
- vf610_nfc_send_command(nfc, command, READ_ONFI_PARAM_CMD_CODE);
- vf610_nfc_addr_cycle(nfc, -1, column);
- vf610_nfc_ecc_mode(nfc, ECC_BYPASS);
- break;
-
- case NAND_CMD_ERASE1:
- vf610_nfc_transfer_size(nfc, 0);
- vf610_nfc_send_commands(nfc, command,
- NAND_CMD_ERASE2, ERASE_CMD_CODE);
- vf610_nfc_addr_cycle(nfc, column, page);
- break;
-
- case NAND_CMD_READID:
- nfc->alt_buf = ALT_BUF_ID;
- nfc->buf_offset = 0;
- vf610_nfc_transfer_size(nfc, 0);
- vf610_nfc_send_command(nfc, command, READ_ID_CMD_CODE);
- vf610_nfc_addr_cycle(nfc, -1, column);
- break;
-
- case NAND_CMD_STATUS:
- nfc->alt_buf = ALT_BUF_STAT;
- vf610_nfc_transfer_size(nfc, 0);
- vf610_nfc_send_command(nfc, command, STATUS_READ_CMD_CODE);
- break;
- default:
- return;
- }
-
- vf610_nfc_done(nfc);
-
- nfc->use_hw_ecc = false;
- nfc->write_sz = 0;
-}
-
-static void vf610_nfc_read_buf(struct mtd_info *mtd, u_char *buf, int len)
-{
- struct vf610_nfc *nfc = mtd_to_nfc(mtd);
- uint c = nfc->buf_offset;
-
- /* Alternate buffers are only supported through read_byte */
- WARN_ON(nfc->alt_buf);
-
- vf610_nfc_memcpy(buf, nfc->regs + NFC_MAIN_AREA(0) + c, len);
-
- nfc->buf_offset += len;
-}
-
-static void vf610_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
- int len)
-{
- struct vf610_nfc *nfc = mtd_to_nfc(mtd);
- uint c = nfc->buf_offset;
- uint l;
-
- l = min_t(uint, len, mtd->writesize + mtd->oobsize - c);
- vf610_nfc_memcpy(nfc->regs + NFC_MAIN_AREA(0) + c, buf, l);
-
- nfc->write_sz += l;
- nfc->buf_offset += l;
-}
-
-static uint8_t vf610_nfc_read_byte(struct mtd_info *mtd)
-{
- struct vf610_nfc *nfc = mtd_to_nfc(mtd);
- u8 tmp;
- uint c = nfc->buf_offset;
-
- switch (nfc->alt_buf) {
- case ALT_BUF_ID:
- tmp = vf610_nfc_get_id(nfc, c);
- break;
- case ALT_BUF_STAT:
- tmp = vf610_nfc_get_status(nfc);
- break;
-#ifdef __LITTLE_ENDIAN
- case ALT_BUF_ONFI:
- /* Reverse byte since the controller uses big endianness */
- c = nfc->buf_offset ^ 0x3;
- /* fall-through */
-#endif
- default:
- tmp = *((u8 *)(nfc->regs + NFC_MAIN_AREA(0) + c));
- break;
- }
- nfc->buf_offset++;
- return tmp;
-}
-
-static u16 vf610_nfc_read_word(struct mtd_info *mtd)
-{
- u16 tmp;
-
- vf610_nfc_read_buf(mtd, (u_char *)&tmp, sizeof(tmp));
- return tmp;
-}
-
-/* If not provided, upper layers apply a fixed delay. */
-static int vf610_nfc_dev_ready(struct mtd_info *mtd)
-{
- /* NFC handles R/B internally; always ready. */
- return 1;
-}
-
-/*
- * This function supports Vybrid only (MPC5125 would have full RB and four CS)
- */
-static void vf610_nfc_select_chip(struct mtd_info *mtd, int chip)
-{
- struct vf610_nfc *nfc = mtd_to_nfc(mtd);
- u32 tmp = vf610_nfc_read(nfc, NFC_ROW_ADDR);
-
- /* Vybrid only (MPC5125 would have full RB and four CS) */
- if (nfc->variant != NFC_VFC610)
- return;
-
- tmp &= ~(ROW_ADDR_CHIP_SEL_RB_MASK | ROW_ADDR_CHIP_SEL_MASK);
-
- if (chip >= 0) {
- tmp |= 1 << ROW_ADDR_CHIP_SEL_RB_SHIFT;
- tmp |= BIT(chip) << ROW_ADDR_CHIP_SEL_SHIFT;
- }
-
- vf610_nfc_write(nfc, NFC_ROW_ADDR, tmp);
-}
-
-/* Count the number of 0's in buff up to max_bits */
-static inline int count_written_bits(uint8_t *buff, int size, int max_bits)
-{
- uint32_t *buff32 = (uint32_t *)buff;
- int k, written_bits = 0;
-
- for (k = 0; k < (size / 4); k++) {
- written_bits += hweight32(~buff32[k]);
- if (unlikely(written_bits > max_bits))
- break;
- }
-
- return written_bits;
-}
-
-static inline int vf610_nfc_correct_data(struct mtd_info *mtd, uint8_t *dat,
- uint8_t *oob, int page)
-{
- struct vf610_nfc *nfc = mtd_to_nfc(mtd);
- u32 ecc_status_off = NFC_MAIN_AREA(0) + ECC_SRAM_ADDR + ECC_STATUS;
- u8 ecc_status;
- u8 ecc_count;
- int flips_threshold = nfc->chip.ecc.strength / 2;
-
- ecc_status = vf610_nfc_read(nfc, ecc_status_off) & 0xff;
- ecc_count = ecc_status & ECC_STATUS_ERR_COUNT;
-
- if (!(ecc_status & ECC_STATUS_MASK))
- return ecc_count;
-
- /* Read OOB without ECC unit enabled */
- vf610_nfc_command(mtd, NAND_CMD_READOOB, 0, page);
- vf610_nfc_read_buf(mtd, oob, mtd->oobsize);
-
- /*
- * On an erased page, bit count (including OOB) should be zero or
- * at least less then half of the ECC strength.
- */
- return nand_check_erased_ecc_chunk(dat, nfc->chip.ecc.size, oob,
- mtd->oobsize, NULL, 0,
- flips_threshold);
-}
-
-static int vf610_nfc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page)
-{
- int eccsize = chip->ecc.size;
- int stat;
-
- nand_read_page_op(chip, page, 0, buf, eccsize);
- if (oob_required)
- vf610_nfc_read_buf(mtd, chip->oob_poi, mtd->oobsize);
-
- stat = vf610_nfc_correct_data(mtd, buf, chip->oob_poi, page);
-
- if (stat < 0) {
- mtd->ecc_stats.failed++;
- return 0;
- } else {
- mtd->ecc_stats.corrected += stat;
- return stat;
- }
-}
-
-static int vf610_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf, int oob_required, int page)
-{
- struct vf610_nfc *nfc = mtd_to_nfc(mtd);
-
- nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);
- if (oob_required)
- vf610_nfc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
-
- /* Always write whole page including OOB due to HW ECC */
- nfc->use_hw_ecc = true;
- nfc->write_sz = mtd->writesize + mtd->oobsize;
-
- return nand_prog_page_end_op(chip);
-}
-
-static const struct of_device_id vf610_nfc_dt_ids[] = {
- { .compatible = "fsl,vf610-nfc", .data = (void *)NFC_VFC610 },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, vf610_nfc_dt_ids);
-
-static void vf610_nfc_preinit_controller(struct vf610_nfc *nfc)
-{
- vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_16BIT);
- vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_ADDR_AUTO_INCR_BIT);
- vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_BUFNO_AUTO_INCR_BIT);
- vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_BOOT_MODE_BIT);
- vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_DMA_REQ_BIT);
- vf610_nfc_set(nfc, NFC_FLASH_CONFIG, CONFIG_FAST_FLASH_BIT);
-
- /* Disable virtual pages, only one elementary transfer unit */
- vf610_nfc_set_field(nfc, NFC_FLASH_CONFIG, CONFIG_PAGE_CNT_MASK,
- CONFIG_PAGE_CNT_SHIFT, 1);
-}
-
-static void vf610_nfc_init_controller(struct vf610_nfc *nfc)
-{
- if (nfc->chip.options & NAND_BUSWIDTH_16)
- vf610_nfc_set(nfc, NFC_FLASH_CONFIG, CONFIG_16BIT);
- else
- vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_16BIT);
-
- if (nfc->chip.ecc.mode == NAND_ECC_HW) {
- /* Set ECC status offset in SRAM */
- vf610_nfc_set_field(nfc, NFC_FLASH_CONFIG,
- CONFIG_ECC_SRAM_ADDR_MASK,
- CONFIG_ECC_SRAM_ADDR_SHIFT,
- ECC_SRAM_ADDR >> 3);
-
- /* Enable ECC status in SRAM */
- vf610_nfc_set(nfc, NFC_FLASH_CONFIG, CONFIG_ECC_SRAM_REQ_BIT);
- }
-}
-
-static int vf610_nfc_probe(struct platform_device *pdev)
-{
- struct vf610_nfc *nfc;
- struct resource *res;
- struct mtd_info *mtd;
- struct nand_chip *chip;
- struct device_node *child;
- const struct of_device_id *of_id;
- int err;
- int irq;
-
- nfc = devm_kzalloc(&pdev->dev, sizeof(*nfc), GFP_KERNEL);
- if (!nfc)
- return -ENOMEM;
-
- nfc->dev = &pdev->dev;
- chip = &nfc->chip;
- mtd = nand_to_mtd(chip);
-
- mtd->owner = THIS_MODULE;
- mtd->dev.parent = nfc->dev;
- mtd->name = DRV_NAME;
-
- irq = platform_get_irq(pdev, 0);
- if (irq <= 0)
- return -EINVAL;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- nfc->regs = devm_ioremap_resource(nfc->dev, res);
- if (IS_ERR(nfc->regs))
- return PTR_ERR(nfc->regs);
-
- nfc->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(nfc->clk))
- return PTR_ERR(nfc->clk);
-
- err = clk_prepare_enable(nfc->clk);
- if (err) {
- dev_err(nfc->dev, "Unable to enable clock!\n");
- return err;
- }
-
- of_id = of_match_device(vf610_nfc_dt_ids, &pdev->dev);
- nfc->variant = (enum vf610_nfc_variant)of_id->data;
-
- for_each_available_child_of_node(nfc->dev->of_node, child) {
- if (of_device_is_compatible(child, "fsl,vf610-nfc-nandcs")) {
-
- if (nand_get_flash_node(chip)) {
- dev_err(nfc->dev,
- "Only one NAND chip supported!\n");
- err = -EINVAL;
- goto error;
- }
-
- nand_set_flash_node(chip, child);
- }
- }
-
- if (!nand_get_flash_node(chip)) {
- dev_err(nfc->dev, "NAND chip sub-node missing!\n");
- err = -ENODEV;
- goto err_clk;
- }
-
- chip->dev_ready = vf610_nfc_dev_ready;
- chip->cmdfunc = vf610_nfc_command;
- chip->read_byte = vf610_nfc_read_byte;
- chip->read_word = vf610_nfc_read_word;
- chip->read_buf = vf610_nfc_read_buf;
- chip->write_buf = vf610_nfc_write_buf;
- chip->select_chip = vf610_nfc_select_chip;
- chip->onfi_set_features = nand_onfi_get_set_features_notsupp;
- chip->onfi_get_features = nand_onfi_get_set_features_notsupp;
-
- chip->options |= NAND_NO_SUBPAGE_WRITE;
-
- init_completion(&nfc->cmd_done);
-
- err = devm_request_irq(nfc->dev, irq, vf610_nfc_irq, 0, DRV_NAME, mtd);
- if (err) {
- dev_err(nfc->dev, "Error requesting IRQ!\n");
- goto error;
- }
-
- vf610_nfc_preinit_controller(nfc);
-
- /* first scan to find the device and get the page size */
- err = nand_scan_ident(mtd, 1, NULL);
- if (err)
- goto error;
-
- vf610_nfc_init_controller(nfc);
-
- /* Bad block options. */
- if (chip->bbt_options & NAND_BBT_USE_FLASH)
- chip->bbt_options |= NAND_BBT_NO_OOB;
-
- /* Single buffer only, max 256 OOB minus ECC status */
- if (mtd->writesize + mtd->oobsize > PAGE_2K + OOB_MAX - 8) {
- dev_err(nfc->dev, "Unsupported flash page size\n");
- err = -ENXIO;
- goto error;
- }
-
- if (chip->ecc.mode == NAND_ECC_HW) {
- if (mtd->writesize != PAGE_2K && mtd->oobsize < 64) {
- dev_err(nfc->dev, "Unsupported flash with hwecc\n");
- err = -ENXIO;
- goto error;
- }
-
- if (chip->ecc.size != mtd->writesize) {
- dev_err(nfc->dev, "Step size needs to be page size\n");
- err = -ENXIO;
- goto error;
- }
-
- /* Only 64 byte ECC layouts known */
- if (mtd->oobsize > 64)
- mtd->oobsize = 64;
-
- /* Use default large page ECC layout defined in NAND core */
- mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
- if (chip->ecc.strength == 32) {
- nfc->ecc_mode = ECC_60_BYTE;
- chip->ecc.bytes = 60;
- } else if (chip->ecc.strength == 24) {
- nfc->ecc_mode = ECC_45_BYTE;
- chip->ecc.bytes = 45;
- } else {
- dev_err(nfc->dev, "Unsupported ECC strength\n");
- err = -ENXIO;
- goto error;
- }
-
- chip->ecc.read_page = vf610_nfc_read_page;
- chip->ecc.write_page = vf610_nfc_write_page;
-
- chip->ecc.size = PAGE_2K;
- }
-
- /* second phase scan */
- err = nand_scan_tail(mtd);
- if (err)
- goto error;
-
- platform_set_drvdata(pdev, mtd);
-
- /* Register device in MTD */
- return mtd_device_register(mtd, NULL, 0);
-
-error:
- of_node_put(nand_get_flash_node(chip));
-err_clk:
- clk_disable_unprepare(nfc->clk);
- return err;
-}
-
-static int vf610_nfc_remove(struct platform_device *pdev)
-{
- struct mtd_info *mtd = platform_get_drvdata(pdev);
- struct vf610_nfc *nfc = mtd_to_nfc(mtd);
-
- nand_release(mtd);
- clk_disable_unprepare(nfc->clk);
- return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int vf610_nfc_suspend(struct device *dev)
-{
- struct mtd_info *mtd = dev_get_drvdata(dev);
- struct vf610_nfc *nfc = mtd_to_nfc(mtd);
-
- clk_disable_unprepare(nfc->clk);
- return 0;
-}
-
-static int vf610_nfc_resume(struct device *dev)
-{
- int err;
-
- struct mtd_info *mtd = dev_get_drvdata(dev);
- struct vf610_nfc *nfc = mtd_to_nfc(mtd);
-
- err = clk_prepare_enable(nfc->clk);
- if (err)
- return err;
-
- vf610_nfc_preinit_controller(nfc);
- vf610_nfc_init_controller(nfc);
- return 0;
-}
-#endif
-
-static SIMPLE_DEV_PM_OPS(vf610_nfc_pm_ops, vf610_nfc_suspend, vf610_nfc_resume);
-
-static struct platform_driver vf610_nfc_driver = {
- .driver = {
- .name = DRV_NAME,
- .of_match_table = vf610_nfc_dt_ids,
- .pm = &vf610_nfc_pm_ops,
- },
- .probe = vf610_nfc_probe,
- .remove = vf610_nfc_remove,
-};
-
-module_platform_driver(vf610_nfc_driver);
-
-MODULE_AUTHOR("Stefan Agner <stefan.agner@toradex.com>");
-MODULE_DESCRIPTION("Freescale VF610/MPC5125 NFC MTD NAND driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c
index 184c8fbfe465..a6fbfa4e5799 100644
--- a/drivers/mtd/nftlmount.c
+++ b/drivers/mtd/nftlmount.c
@@ -122,8 +122,7 @@ static int find_boot_record(struct NFTLrecord *nftl)
if (memcmp(buf, "ANAND", 6)) {
printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but went away on reread!\n",
block * nftl->EraseSize, nftl->mbd.mtd->index);
- printk(KERN_NOTICE "New data are: %02x %02x %02x %02x %02x %02x\n",
- buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
+ printk(KERN_NOTICE "New data are: %6ph\n", buf);
continue;
}
#endif
@@ -328,12 +327,9 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block)
memset(instr, 0, sizeof(struct erase_info));
/* XXX: use async erase interface, XXX: test return code */
- instr->mtd = nftl->mbd.mtd;
instr->addr = block * nftl->EraseSize;
instr->len = nftl->EraseSize;
- mtd_erase(mtd, instr);
-
- if (instr->state == MTD_ERASE_FAILED) {
+ if (mtd_erase(mtd, instr)) {
printk("Error while formatting block %d\n", block);
goto fail;
}
diff --git a/drivers/mtd/ofpart.c b/drivers/mtd/ofpart.c
index 6bdf4e525677..615f8c173162 100644
--- a/drivers/mtd/ofpart.c
+++ b/drivers/mtd/ofpart.c
@@ -25,9 +25,9 @@ static bool node_has_compatible(struct device_node *pp)
return of_get_property(pp, "compatible", NULL);
}
-static int parse_ofpart_partitions(struct mtd_info *master,
- const struct mtd_partition **pparts,
- struct mtd_part_parser_data *data)
+static int parse_fixed_partitions(struct mtd_info *master,
+ const struct mtd_partition **pparts,
+ struct mtd_part_parser_data *data)
{
struct mtd_partition *parts;
struct device_node *mtd_node;
@@ -140,9 +140,16 @@ ofpart_none:
return ret;
}
+static const struct of_device_id parse_ofpart_match_table[] = {
+ { .compatible = "fixed-partitions" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, parse_ofpart_match_table);
+
static struct mtd_part_parser ofpart_parser = {
- .parse_fn = parse_ofpart_partitions,
- .name = "ofpart",
+ .parse_fn = parse_fixed_partitions,
+ .name = "fixed-partitions",
+ .of_match_table = parse_ofpart_match_table,
};
static int parse_ofoldpart_partitions(struct mtd_info *master,
@@ -229,4 +236,5 @@ MODULE_AUTHOR("Vitaly Wool, David Gibson");
* with the same name. Since we provide the ofoldpart parser, we should have
* the corresponding alias.
*/
+MODULE_ALIAS("fixed-partitions");
MODULE_ALIAS("ofoldpart");
diff --git a/drivers/mtd/onenand/generic.c b/drivers/mtd/onenand/generic.c
deleted file mode 100644
index 125da34d8ff9..000000000000
--- a/drivers/mtd/onenand/generic.c
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * linux/drivers/mtd/onenand/generic.c
- *
- * Copyright (c) 2005 Samsung Electronics
- * Kyungmin Park <kyungmin.park@samsung.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.
- *
- * Overview:
- * This is a device driver for the OneNAND flash for generic boards.
- */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/platform_device.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/onenand.h>
-#include <linux/mtd/partitions.h>
-#include <linux/io.h>
-
-/*
- * Note: Driver name and platform data format have been updated!
- *
- * This version of the driver is named "onenand-flash" and takes struct
- * onenand_platform_data as platform data. The old ARM-specific version
- * with the name "onenand" used to take struct flash_platform_data.
- */
-#define DRIVER_NAME "onenand-flash"
-
-struct onenand_info {
- struct mtd_info mtd;
- struct onenand_chip onenand;
-};
-
-static int generic_onenand_probe(struct platform_device *pdev)
-{
- struct onenand_info *info;
- struct onenand_platform_data *pdata = dev_get_platdata(&pdev->dev);
- struct resource *res = pdev->resource;
- unsigned long size = resource_size(res);
- int err;
-
- info = kzalloc(sizeof(struct onenand_info), GFP_KERNEL);
- if (!info)
- return -ENOMEM;
-
- if (!request_mem_region(res->start, size, dev_name(&pdev->dev))) {
- err = -EBUSY;
- goto out_free_info;
- }
-
- info->onenand.base = ioremap(res->start, size);
- if (!info->onenand.base) {
- err = -ENOMEM;
- goto out_release_mem_region;
- }
-
- info->onenand.mmcontrol = pdata ? pdata->mmcontrol : NULL;
- info->onenand.irq = platform_get_irq(pdev, 0);
-
- info->mtd.dev.parent = &pdev->dev;
- info->mtd.priv = &info->onenand;
-
- if (onenand_scan(&info->mtd, 1)) {
- err = -ENXIO;
- goto out_iounmap;
- }
-
- err = mtd_device_parse_register(&info->mtd, NULL, NULL,
- pdata ? pdata->parts : NULL,
- pdata ? pdata->nr_parts : 0);
-
- platform_set_drvdata(pdev, info);
-
- return 0;
-
-out_iounmap:
- iounmap(info->onenand.base);
-out_release_mem_region:
- release_mem_region(res->start, size);
-out_free_info:
- kfree(info);
-
- return err;
-}
-
-static int generic_onenand_remove(struct platform_device *pdev)
-{
- struct onenand_info *info = platform_get_drvdata(pdev);
- struct resource *res = pdev->resource;
- unsigned long size = resource_size(res);
-
- if (info) {
- onenand_release(&info->mtd);
- release_mem_region(res->start, size);
- iounmap(info->onenand.base);
- kfree(info);
- }
-
- return 0;
-}
-
-static struct platform_driver generic_onenand_driver = {
- .driver = {
- .name = DRIVER_NAME,
- },
- .probe = generic_onenand_probe,
- .remove = generic_onenand_remove,
-};
-
-module_platform_driver(generic_onenand_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>");
-MODULE_DESCRIPTION("Glue layer for OneNAND flash on generic boards");
-MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c
deleted file mode 100644
index 87c34f607a75..000000000000
--- a/drivers/mtd/onenand/omap2.c
+++ /dev/null
@@ -1,662 +0,0 @@
-/*
- * linux/drivers/mtd/onenand/omap2.c
- *
- * OneNAND driver for OMAP2 / OMAP3
- *
- * Copyright © 2005-2006 Nokia Corporation
- *
- * Author: Jarkko Lavinen <jarkko.lavinen@nokia.com> and Juha Yrjölä
- * IRQ and DMA support written by Timo Teras
- *
- * 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.
- *
- * 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; see the file COPYING. If not, write to the Free Software
- * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- */
-
-#include <linux/device.h>
-#include <linux/module.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/onenand.h>
-#include <linux/mtd/partitions.h>
-#include <linux/of_device.h>
-#include <linux/omap-gpmc.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/dma-mapping.h>
-#include <linux/dmaengine.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-#include <linux/gpio/consumer.h>
-
-#include <asm/mach/flash.h>
-
-#define DRIVER_NAME "omap2-onenand"
-
-#define ONENAND_BUFRAM_SIZE (1024 * 5)
-
-struct omap2_onenand {
- struct platform_device *pdev;
- int gpmc_cs;
- unsigned long phys_base;
- struct gpio_desc *int_gpiod;
- struct mtd_info mtd;
- struct onenand_chip onenand;
- struct completion irq_done;
- struct completion dma_done;
- struct dma_chan *dma_chan;
-};
-
-static void omap2_onenand_dma_complete_func(void *completion)
-{
- complete(completion);
-}
-
-static irqreturn_t omap2_onenand_interrupt(int irq, void *dev_id)
-{
- struct omap2_onenand *c = dev_id;
-
- complete(&c->irq_done);
-
- return IRQ_HANDLED;
-}
-
-static inline unsigned short read_reg(struct omap2_onenand *c, int reg)
-{
- return readw(c->onenand.base + reg);
-}
-
-static inline void write_reg(struct omap2_onenand *c, unsigned short value,
- int reg)
-{
- writew(value, c->onenand.base + reg);
-}
-
-static int omap2_onenand_set_cfg(struct omap2_onenand *c,
- bool sr, bool sw,
- int latency, int burst_len)
-{
- unsigned short reg = ONENAND_SYS_CFG1_RDY | ONENAND_SYS_CFG1_INT;
-
- reg |= latency << ONENAND_SYS_CFG1_BRL_SHIFT;
-
- switch (burst_len) {
- case 0: /* continuous */
- break;
- case 4:
- reg |= ONENAND_SYS_CFG1_BL_4;
- break;
- case 8:
- reg |= ONENAND_SYS_CFG1_BL_8;
- break;
- case 16:
- reg |= ONENAND_SYS_CFG1_BL_16;
- break;
- case 32:
- reg |= ONENAND_SYS_CFG1_BL_32;
- break;
- default:
- return -EINVAL;
- }
-
- if (latency > 5)
- reg |= ONENAND_SYS_CFG1_HF;
- if (latency > 7)
- reg |= ONENAND_SYS_CFG1_VHF;
- if (sr)
- reg |= ONENAND_SYS_CFG1_SYNC_READ;
- if (sw)
- reg |= ONENAND_SYS_CFG1_SYNC_WRITE;
-
- write_reg(c, reg, ONENAND_REG_SYS_CFG1);
-
- return 0;
-}
-
-static int omap2_onenand_get_freq(int ver)
-{
- switch ((ver >> 4) & 0xf) {
- case 0:
- return 40;
- case 1:
- return 54;
- case 2:
- return 66;
- case 3:
- return 83;
- case 4:
- return 104;
- }
-
- return -EINVAL;
-}
-
-static void wait_err(char *msg, int state, unsigned int ctrl, unsigned int intr)
-{
- printk(KERN_ERR "onenand_wait: %s! state %d ctrl 0x%04x intr 0x%04x\n",
- msg, state, ctrl, intr);
-}
-
-static void wait_warn(char *msg, int state, unsigned int ctrl,
- unsigned int intr)
-{
- printk(KERN_WARNING "onenand_wait: %s! state %d ctrl 0x%04x "
- "intr 0x%04x\n", msg, state, ctrl, intr);
-}
-
-static int omap2_onenand_wait(struct mtd_info *mtd, int state)
-{
- struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd);
- struct onenand_chip *this = mtd->priv;
- unsigned int intr = 0;
- unsigned int ctrl, ctrl_mask;
- unsigned long timeout;
- u32 syscfg;
-
- if (state == FL_RESETING || state == FL_PREPARING_ERASE ||
- state == FL_VERIFYING_ERASE) {
- int i = 21;
- unsigned int intr_flags = ONENAND_INT_MASTER;
-
- switch (state) {
- case FL_RESETING:
- intr_flags |= ONENAND_INT_RESET;
- break;
- case FL_PREPARING_ERASE:
- intr_flags |= ONENAND_INT_ERASE;
- break;
- case FL_VERIFYING_ERASE:
- i = 101;
- break;
- }
-
- while (--i) {
- udelay(1);
- intr = read_reg(c, ONENAND_REG_INTERRUPT);
- if (intr & ONENAND_INT_MASTER)
- break;
- }
- ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS);
- if (ctrl & ONENAND_CTRL_ERROR) {
- wait_err("controller error", state, ctrl, intr);
- return -EIO;
- }
- if ((intr & intr_flags) == intr_flags)
- return 0;
- /* Continue in wait for interrupt branch */
- }
-
- if (state != FL_READING) {
- int result;
-
- /* Turn interrupts on */
- syscfg = read_reg(c, ONENAND_REG_SYS_CFG1);
- if (!(syscfg & ONENAND_SYS_CFG1_IOBE)) {
- syscfg |= ONENAND_SYS_CFG1_IOBE;
- write_reg(c, syscfg, ONENAND_REG_SYS_CFG1);
- /* Add a delay to let GPIO settle */
- syscfg = read_reg(c, ONENAND_REG_SYS_CFG1);
- }
-
- reinit_completion(&c->irq_done);
- result = gpiod_get_value(c->int_gpiod);
- if (result < 0) {
- ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS);
- intr = read_reg(c, ONENAND_REG_INTERRUPT);
- wait_err("gpio error", state, ctrl, intr);
- return result;
- } else if (result == 0) {
- int retry_cnt = 0;
-retry:
- if (!wait_for_completion_io_timeout(&c->irq_done,
- msecs_to_jiffies(20))) {
- /* Timeout after 20ms */
- ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS);
- if (ctrl & ONENAND_CTRL_ONGO &&
- !this->ongoing) {
- /*
- * The operation seems to be still going
- * so give it some more time.
- */
- retry_cnt += 1;
- if (retry_cnt < 3)
- goto retry;
- intr = read_reg(c,
- ONENAND_REG_INTERRUPT);
- wait_err("timeout", state, ctrl, intr);
- return -EIO;
- }
- intr = read_reg(c, ONENAND_REG_INTERRUPT);
- if ((intr & ONENAND_INT_MASTER) == 0)
- wait_warn("timeout", state, ctrl, intr);
- }
- }
- } else {
- int retry_cnt = 0;
-
- /* Turn interrupts off */
- syscfg = read_reg(c, ONENAND_REG_SYS_CFG1);
- syscfg &= ~ONENAND_SYS_CFG1_IOBE;
- write_reg(c, syscfg, ONENAND_REG_SYS_CFG1);
-
- timeout = jiffies + msecs_to_jiffies(20);
- while (1) {
- if (time_before(jiffies, timeout)) {
- intr = read_reg(c, ONENAND_REG_INTERRUPT);
- if (intr & ONENAND_INT_MASTER)
- break;
- } else {
- /* Timeout after 20ms */
- ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS);
- if (ctrl & ONENAND_CTRL_ONGO) {
- /*
- * The operation seems to be still going
- * so give it some more time.
- */
- retry_cnt += 1;
- if (retry_cnt < 3) {
- timeout = jiffies +
- msecs_to_jiffies(20);
- continue;
- }
- }
- break;
- }
- }
- }
-
- intr = read_reg(c, ONENAND_REG_INTERRUPT);
- ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS);
-
- if (intr & ONENAND_INT_READ) {
- int ecc = read_reg(c, ONENAND_REG_ECC_STATUS);
-
- if (ecc) {
- unsigned int addr1, addr8;
-
- addr1 = read_reg(c, ONENAND_REG_START_ADDRESS1);
- addr8 = read_reg(c, ONENAND_REG_START_ADDRESS8);
- if (ecc & ONENAND_ECC_2BIT_ALL) {
- printk(KERN_ERR "onenand_wait: ECC error = "
- "0x%04x, addr1 %#x, addr8 %#x\n",
- ecc, addr1, addr8);
- mtd->ecc_stats.failed++;
- return -EBADMSG;
- } else if (ecc & ONENAND_ECC_1BIT_ALL) {
- printk(KERN_NOTICE "onenand_wait: correctable "
- "ECC error = 0x%04x, addr1 %#x, "
- "addr8 %#x\n", ecc, addr1, addr8);
- mtd->ecc_stats.corrected++;
- }
- }
- } else if (state == FL_READING) {
- wait_err("timeout", state, ctrl, intr);
- return -EIO;
- }
-
- if (ctrl & ONENAND_CTRL_ERROR) {
- wait_err("controller error", state, ctrl, intr);
- if (ctrl & ONENAND_CTRL_LOCK)
- printk(KERN_ERR "onenand_wait: "
- "Device is write protected!!!\n");
- return -EIO;
- }
-
- ctrl_mask = 0xFE9F;
- if (this->ongoing)
- ctrl_mask &= ~0x8000;
-
- if (ctrl & ctrl_mask)
- wait_warn("unexpected controller status", state, ctrl, intr);
-
- return 0;
-}
-
-static inline int omap2_onenand_bufferram_offset(struct mtd_info *mtd, int area)
-{
- struct onenand_chip *this = mtd->priv;
-
- if (ONENAND_CURRENT_BUFFERRAM(this)) {
- if (area == ONENAND_DATARAM)
- return this->writesize;
- if (area == ONENAND_SPARERAM)
- return mtd->oobsize;
- }
-
- return 0;
-}
-
-static inline int omap2_onenand_dma_transfer(struct omap2_onenand *c,
- dma_addr_t src, dma_addr_t dst,
- size_t count)
-{
- struct dma_async_tx_descriptor *tx;
- dma_cookie_t cookie;
-
- tx = dmaengine_prep_dma_memcpy(c->dma_chan, dst, src, count, 0);
- if (!tx) {
- dev_err(&c->pdev->dev, "Failed to prepare DMA memcpy\n");
- return -EIO;
- }
-
- reinit_completion(&c->dma_done);
-
- tx->callback = omap2_onenand_dma_complete_func;
- tx->callback_param = &c->dma_done;
-
- cookie = tx->tx_submit(tx);
- if (dma_submit_error(cookie)) {
- dev_err(&c->pdev->dev, "Failed to do DMA tx_submit\n");
- return -EIO;
- }
-
- dma_async_issue_pending(c->dma_chan);
-
- if (!wait_for_completion_io_timeout(&c->dma_done,
- msecs_to_jiffies(20))) {
- dmaengine_terminate_sync(c->dma_chan);
- return -ETIMEDOUT;
- }
-
- return 0;
-}
-
-static int omap2_onenand_read_bufferram(struct mtd_info *mtd, int area,
- unsigned char *buffer, int offset,
- size_t count)
-{
- struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd);
- struct onenand_chip *this = mtd->priv;
- dma_addr_t dma_src, dma_dst;
- int bram_offset;
- void *buf = (void *)buffer;
- size_t xtra;
- int ret;
-
- bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset;
- if (bram_offset & 3 || (size_t)buf & 3 || count < 384)
- goto out_copy;
-
- /* panic_write() may be in an interrupt context */
- if (in_interrupt() || oops_in_progress)
- goto out_copy;
-
- if (buf >= high_memory) {
- struct page *p1;
-
- if (((size_t)buf & PAGE_MASK) !=
- ((size_t)(buf + count - 1) & PAGE_MASK))
- goto out_copy;
- p1 = vmalloc_to_page(buf);
- if (!p1)
- goto out_copy;
- buf = page_address(p1) + ((size_t)buf & ~PAGE_MASK);
- }
-
- xtra = count & 3;
- if (xtra) {
- count -= xtra;
- memcpy(buf + count, this->base + bram_offset + count, xtra);
- }
-
- dma_src = c->phys_base + bram_offset;
- dma_dst = dma_map_single(&c->pdev->dev, buf, count, DMA_FROM_DEVICE);
- if (dma_mapping_error(&c->pdev->dev, dma_dst)) {
- dev_err(&c->pdev->dev,
- "Couldn't DMA map a %d byte buffer\n",
- count);
- goto out_copy;
- }
-
- ret = omap2_onenand_dma_transfer(c, dma_src, dma_dst, count);
- dma_unmap_single(&c->pdev->dev, dma_dst, count, DMA_FROM_DEVICE);
-
- if (ret) {
- dev_err(&c->pdev->dev, "timeout waiting for DMA\n");
- goto out_copy;
- }
-
- return 0;
-
-out_copy:
- memcpy(buf, this->base + bram_offset, count);
- return 0;
-}
-
-static int omap2_onenand_write_bufferram(struct mtd_info *mtd, int area,
- const unsigned char *buffer,
- int offset, size_t count)
-{
- struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd);
- struct onenand_chip *this = mtd->priv;
- dma_addr_t dma_src, dma_dst;
- int bram_offset;
- void *buf = (void *)buffer;
- int ret;
-
- bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset;
- if (bram_offset & 3 || (size_t)buf & 3 || count < 384)
- goto out_copy;
-
- /* panic_write() may be in an interrupt context */
- if (in_interrupt() || oops_in_progress)
- goto out_copy;
-
- if (buf >= high_memory) {
- struct page *p1;
-
- if (((size_t)buf & PAGE_MASK) !=
- ((size_t)(buf + count - 1) & PAGE_MASK))
- goto out_copy;
- p1 = vmalloc_to_page(buf);
- if (!p1)
- goto out_copy;
- buf = page_address(p1) + ((size_t)buf & ~PAGE_MASK);
- }
-
- dma_src = dma_map_single(&c->pdev->dev, buf, count, DMA_TO_DEVICE);
- dma_dst = c->phys_base + bram_offset;
- if (dma_mapping_error(&c->pdev->dev, dma_src)) {
- dev_err(&c->pdev->dev,
- "Couldn't DMA map a %d byte buffer\n",
- count);
- return -1;
- }
-
- ret = omap2_onenand_dma_transfer(c, dma_src, dma_dst, count);
- dma_unmap_single(&c->pdev->dev, dma_src, count, DMA_TO_DEVICE);
-
- if (ret) {
- dev_err(&c->pdev->dev, "timeout waiting for DMA\n");
- goto out_copy;
- }
-
- return 0;
-
-out_copy:
- memcpy(this->base + bram_offset, buf, count);
- return 0;
-}
-
-static void omap2_onenand_shutdown(struct platform_device *pdev)
-{
- struct omap2_onenand *c = dev_get_drvdata(&pdev->dev);
-
- /* With certain content in the buffer RAM, the OMAP boot ROM code
- * can recognize the flash chip incorrectly. Zero it out before
- * soft reset.
- */
- memset((__force void *)c->onenand.base, 0, ONENAND_BUFRAM_SIZE);
-}
-
-static int omap2_onenand_probe(struct platform_device *pdev)
-{
- u32 val;
- dma_cap_mask_t mask;
- int freq, latency, r;
- struct resource *res;
- struct omap2_onenand *c;
- struct gpmc_onenand_info info;
- struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(dev, "error getting memory resource\n");
- return -EINVAL;
- }
-
- r = of_property_read_u32(np, "reg", &val);
- if (r) {
- dev_err(dev, "reg not found in DT\n");
- return r;
- }
-
- c = devm_kzalloc(dev, sizeof(struct omap2_onenand), GFP_KERNEL);
- if (!c)
- return -ENOMEM;
-
- init_completion(&c->irq_done);
- init_completion(&c->dma_done);
- c->gpmc_cs = val;
- c->phys_base = res->start;
-
- c->onenand.base = devm_ioremap_resource(dev, res);
- if (IS_ERR(c->onenand.base))
- return PTR_ERR(c->onenand.base);
-
- c->int_gpiod = devm_gpiod_get_optional(dev, "int", GPIOD_IN);
- if (IS_ERR(c->int_gpiod)) {
- r = PTR_ERR(c->int_gpiod);
- /* Just try again if this happens */
- if (r != -EPROBE_DEFER)
- dev_err(dev, "error getting gpio: %d\n", r);
- return r;
- }
-
- if (c->int_gpiod) {
- r = devm_request_irq(dev, gpiod_to_irq(c->int_gpiod),
- omap2_onenand_interrupt,
- IRQF_TRIGGER_RISING, "onenand", c);
- if (r)
- return r;
-
- c->onenand.wait = omap2_onenand_wait;
- }
-
- dma_cap_zero(mask);
- dma_cap_set(DMA_MEMCPY, mask);
-
- c->dma_chan = dma_request_channel(mask, NULL, NULL);
- if (c->dma_chan) {
- c->onenand.read_bufferram = omap2_onenand_read_bufferram;
- c->onenand.write_bufferram = omap2_onenand_write_bufferram;
- }
-
- c->pdev = pdev;
- c->mtd.priv = &c->onenand;
- c->mtd.dev.parent = dev;
- mtd_set_of_node(&c->mtd, dev->of_node);
-
- dev_info(dev, "initializing on CS%d (0x%08lx), va %p, %s mode\n",
- c->gpmc_cs, c->phys_base, c->onenand.base,
- c->dma_chan ? "DMA" : "PIO");
-
- if ((r = onenand_scan(&c->mtd, 1)) < 0)
- goto err_release_dma;
-
- freq = omap2_onenand_get_freq(c->onenand.version_id);
- if (freq > 0) {
- switch (freq) {
- case 104:
- latency = 7;
- break;
- case 83:
- latency = 6;
- break;
- case 66:
- latency = 5;
- break;
- case 56:
- latency = 4;
- break;
- default: /* 40 MHz or lower */
- latency = 3;
- break;
- }
-
- r = gpmc_omap_onenand_set_timings(dev, c->gpmc_cs,
- freq, latency, &info);
- if (r)
- goto err_release_onenand;
-
- r = omap2_onenand_set_cfg(c, info.sync_read, info.sync_write,
- latency, info.burst_len);
- if (r)
- goto err_release_onenand;
-
- if (info.sync_read || info.sync_write)
- dev_info(dev, "optimized timings for %d MHz\n", freq);
- }
-
- r = mtd_device_register(&c->mtd, NULL, 0);
- if (r)
- goto err_release_onenand;
-
- platform_set_drvdata(pdev, c);
-
- return 0;
-
-err_release_onenand:
- onenand_release(&c->mtd);
-err_release_dma:
- if (c->dma_chan)
- dma_release_channel(c->dma_chan);
-
- return r;
-}
-
-static int omap2_onenand_remove(struct platform_device *pdev)
-{
- struct omap2_onenand *c = dev_get_drvdata(&pdev->dev);
-
- onenand_release(&c->mtd);
- if (c->dma_chan)
- dma_release_channel(c->dma_chan);
- omap2_onenand_shutdown(pdev);
-
- return 0;
-}
-
-static const struct of_device_id omap2_onenand_id_table[] = {
- { .compatible = "ti,omap2-onenand", },
- {},
-};
-MODULE_DEVICE_TABLE(of, omap2_onenand_id_table);
-
-static struct platform_driver omap2_onenand_driver = {
- .probe = omap2_onenand_probe,
- .remove = omap2_onenand_remove,
- .shutdown = omap2_onenand_shutdown,
- .driver = {
- .name = DRIVER_NAME,
- .of_match_table = omap2_onenand_id_table,
- },
-};
-
-module_platform_driver(omap2_onenand_driver);
-
-MODULE_ALIAS("platform:" DRIVER_NAME);
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Jarkko Lavinen <jarkko.lavinen@nokia.com>");
-MODULE_DESCRIPTION("Glue layer for OneNAND flash on OMAP2 / OMAP3");
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
deleted file mode 100644
index 979f4031f23c..000000000000
--- a/drivers/mtd/onenand/onenand_base.c
+++ /dev/null
@@ -1,4033 +0,0 @@
-/*
- * linux/drivers/mtd/onenand/onenand_base.c
- *
- * Copyright © 2005-2009 Samsung Electronics
- * Copyright © 2007 Nokia Corporation
- *
- * Kyungmin Park <kyungmin.park@samsung.com>
- *
- * Credits:
- * Adrian Hunter <ext-adrian.hunter@nokia.com>:
- * auto-placement support, read-while load support, various fixes
- *
- * Vishak G <vishak.g at samsung.com>, Rohit Hagargundgi <h.rohit at samsung.com>
- * Flex-OneNAND support
- * Amul Kumar Saha <amul.saha at samsung.com>
- * OTP support
- *
- * 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.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/jiffies.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/onenand.h>
-#include <linux/mtd/partitions.h>
-
-#include <asm/io.h>
-
-/*
- * Multiblock erase if number of blocks to erase is 2 or more.
- * Maximum number of blocks for simultaneous erase is 64.
- */
-#define MB_ERASE_MIN_BLK_COUNT 2
-#define MB_ERASE_MAX_BLK_COUNT 64
-
-/* Default Flex-OneNAND boundary and lock respectively */
-static int flex_bdry[MAX_DIES * 2] = { -1, 0, -1, 0 };
-
-module_param_array(flex_bdry, int, NULL, 0400);
-MODULE_PARM_DESC(flex_bdry, "SLC Boundary information for Flex-OneNAND"
- "Syntax:flex_bdry=DIE_BDRY,LOCK,..."
- "DIE_BDRY: SLC boundary of the die"
- "LOCK: Locking information for SLC boundary"
- " : 0->Set boundary in unlocked status"
- " : 1->Set boundary in locked status");
-
-/* Default OneNAND/Flex-OneNAND OTP options*/
-static int otp;
-
-module_param(otp, int, 0400);
-MODULE_PARM_DESC(otp, "Corresponding behaviour of OneNAND in OTP"
- "Syntax : otp=LOCK_TYPE"
- "LOCK_TYPE : Keys issued, for specific OTP Lock type"
- " : 0 -> Default (No Blocks Locked)"
- " : 1 -> OTP Block lock"
- " : 2 -> 1st Block lock"
- " : 3 -> BOTH OTP Block and 1st Block lock");
-
-/*
- * flexonenand_oob_128 - oob info for Flex-Onenand with 4KB page
- * For now, we expose only 64 out of 80 ecc bytes
- */
-static int flexonenand_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- if (section > 7)
- return -ERANGE;
-
- oobregion->offset = (section * 16) + 6;
- oobregion->length = 10;
-
- return 0;
-}
-
-static int flexonenand_ooblayout_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- if (section > 7)
- return -ERANGE;
-
- oobregion->offset = (section * 16) + 2;
- oobregion->length = 4;
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops flexonenand_ooblayout_ops = {
- .ecc = flexonenand_ooblayout_ecc,
- .free = flexonenand_ooblayout_free,
-};
-
-/*
- * onenand_oob_128 - oob info for OneNAND with 4KB page
- *
- * Based on specification:
- * 4Gb M-die OneNAND Flash (KFM4G16Q4M, KFN8G16Q4M). Rev. 1.3, Apr. 2010
- *
- */
-static int onenand_ooblayout_128_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- if (section > 7)
- return -ERANGE;
-
- oobregion->offset = (section * 16) + 7;
- oobregion->length = 9;
-
- return 0;
-}
-
-static int onenand_ooblayout_128_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- if (section >= 8)
- return -ERANGE;
-
- /*
- * free bytes are using the spare area fields marked as
- * "Managed by internal ECC logic for Logical Sector Number area"
- */
- oobregion->offset = (section * 16) + 2;
- oobregion->length = 3;
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops onenand_oob_128_ooblayout_ops = {
- .ecc = onenand_ooblayout_128_ecc,
- .free = onenand_ooblayout_128_free,
-};
-
-/**
- * onenand_oob_32_64 - oob info for large (2KB) page
- */
-static int onenand_ooblayout_32_64_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- if (section > 3)
- return -ERANGE;
-
- oobregion->offset = (section * 16) + 8;
- oobregion->length = 5;
-
- return 0;
-}
-
-static int onenand_ooblayout_32_64_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- int sections = (mtd->oobsize / 32) * 2;
-
- if (section >= sections)
- return -ERANGE;
-
- if (section & 1) {
- oobregion->offset = ((section - 1) * 16) + 14;
- oobregion->length = 2;
- } else {
- oobregion->offset = (section * 16) + 2;
- oobregion->length = 3;
- }
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops onenand_oob_32_64_ooblayout_ops = {
- .ecc = onenand_ooblayout_32_64_ecc,
- .free = onenand_ooblayout_32_64_free,
-};
-
-static const unsigned char ffchars[] = {
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 16 */
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 32 */
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 48 */
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 64 */
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 80 */
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 96 */
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 112 */
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128 */
-};
-
-/**
- * onenand_readw - [OneNAND Interface] Read OneNAND register
- * @param addr address to read
- *
- * Read OneNAND register
- */
-static unsigned short onenand_readw(void __iomem *addr)
-{
- return readw(addr);
-}
-
-/**
- * onenand_writew - [OneNAND Interface] Write OneNAND register with value
- * @param value value to write
- * @param addr address to write
- *
- * Write OneNAND register with value
- */
-static void onenand_writew(unsigned short value, void __iomem *addr)
-{
- writew(value, addr);
-}
-
-/**
- * onenand_block_address - [DEFAULT] Get block address
- * @param this onenand chip data structure
- * @param block the block
- * @return translated block address if DDP, otherwise same
- *
- * Setup Start Address 1 Register (F100h)
- */
-static int onenand_block_address(struct onenand_chip *this, int block)
-{
- /* Device Flash Core select, NAND Flash Block Address */
- if (block & this->density_mask)
- return ONENAND_DDP_CHIP1 | (block ^ this->density_mask);
-
- return block;
-}
-
-/**
- * onenand_bufferram_address - [DEFAULT] Get bufferram address
- * @param this onenand chip data structure
- * @param block the block
- * @return set DBS value if DDP, otherwise 0
- *
- * Setup Start Address 2 Register (F101h) for DDP
- */
-static int onenand_bufferram_address(struct onenand_chip *this, int block)
-{
- /* Device BufferRAM Select */
- if (block & this->density_mask)
- return ONENAND_DDP_CHIP1;
-
- return ONENAND_DDP_CHIP0;
-}
-
-/**
- * onenand_page_address - [DEFAULT] Get page address
- * @param page the page address
- * @param sector the sector address
- * @return combined page and sector address
- *
- * Setup Start Address 8 Register (F107h)
- */
-static int onenand_page_address(int page, int sector)
-{
- /* Flash Page Address, Flash Sector Address */
- int fpa, fsa;
-
- fpa = page & ONENAND_FPA_MASK;
- fsa = sector & ONENAND_FSA_MASK;
-
- return ((fpa << ONENAND_FPA_SHIFT) | fsa);
-}
-
-/**
- * onenand_buffer_address - [DEFAULT] Get buffer address
- * @param dataram1 DataRAM index
- * @param sectors the sector address
- * @param count the number of sectors
- * @return the start buffer value
- *
- * Setup Start Buffer Register (F200h)
- */
-static int onenand_buffer_address(int dataram1, int sectors, int count)
-{
- int bsa, bsc;
-
- /* BufferRAM Sector Address */
- bsa = sectors & ONENAND_BSA_MASK;
-
- if (dataram1)
- bsa |= ONENAND_BSA_DATARAM1; /* DataRAM1 */
- else
- bsa |= ONENAND_BSA_DATARAM0; /* DataRAM0 */
-
- /* BufferRAM Sector Count */
- bsc = count & ONENAND_BSC_MASK;
-
- return ((bsa << ONENAND_BSA_SHIFT) | bsc);
-}
-
-/**
- * flexonenand_block- For given address return block number
- * @param this - OneNAND device structure
- * @param addr - Address for which block number is needed
- */
-static unsigned flexonenand_block(struct onenand_chip *this, loff_t addr)
-{
- unsigned boundary, blk, die = 0;
-
- if (ONENAND_IS_DDP(this) && addr >= this->diesize[0]) {
- die = 1;
- addr -= this->diesize[0];
- }
-
- boundary = this->boundary[die];
-
- blk = addr >> (this->erase_shift - 1);
- if (blk > boundary)
- blk = (blk + boundary + 1) >> 1;
-
- blk += die ? this->density_mask : 0;
- return blk;
-}
-
-inline unsigned onenand_block(struct onenand_chip *this, loff_t addr)
-{
- if (!FLEXONENAND(this))
- return addr >> this->erase_shift;
- return flexonenand_block(this, addr);
-}
-
-/**
- * flexonenand_addr - Return address of the block
- * @this: OneNAND device structure
- * @block: Block number on Flex-OneNAND
- *
- * Return address of the block
- */
-static loff_t flexonenand_addr(struct onenand_chip *this, int block)
-{
- loff_t ofs = 0;
- int die = 0, boundary;
-
- if (ONENAND_IS_DDP(this) && block >= this->density_mask) {
- block -= this->density_mask;
- die = 1;
- ofs = this->diesize[0];
- }
-
- boundary = this->boundary[die];
- ofs += (loff_t)block << (this->erase_shift - 1);
- if (block > (boundary + 1))
- ofs += (loff_t)(block - boundary - 1) << (this->erase_shift - 1);
- return ofs;
-}
-
-loff_t onenand_addr(struct onenand_chip *this, int block)
-{
- if (!FLEXONENAND(this))
- return (loff_t)block << this->erase_shift;
- return flexonenand_addr(this, block);
-}
-EXPORT_SYMBOL(onenand_addr);
-
-/**
- * onenand_get_density - [DEFAULT] Get OneNAND density
- * @param dev_id OneNAND device ID
- *
- * Get OneNAND density from device ID
- */
-static inline int onenand_get_density(int dev_id)
-{
- int density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
- return (density & ONENAND_DEVICE_DENSITY_MASK);
-}
-
-/**
- * flexonenand_region - [Flex-OneNAND] Return erase region of addr
- * @param mtd MTD device structure
- * @param addr address whose erase region needs to be identified
- */
-int flexonenand_region(struct mtd_info *mtd, loff_t addr)
-{
- int i;
-
- for (i = 0; i < mtd->numeraseregions; i++)
- if (addr < mtd->eraseregions[i].offset)
- break;
- return i - 1;
-}
-EXPORT_SYMBOL(flexonenand_region);
-
-/**
- * onenand_command - [DEFAULT] Send command to OneNAND device
- * @param mtd MTD device structure
- * @param cmd the command to be sent
- * @param addr offset to read from or write to
- * @param len number of bytes to read or write
- *
- * Send command to OneNAND device. This function is used for middle/large page
- * devices (1KB/2KB Bytes per page)
- */
-static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t len)
-{
- struct onenand_chip *this = mtd->priv;
- int value, block, page;
-
- /* Address translation */
- switch (cmd) {
- case ONENAND_CMD_UNLOCK:
- case ONENAND_CMD_LOCK:
- case ONENAND_CMD_LOCK_TIGHT:
- case ONENAND_CMD_UNLOCK_ALL:
- block = -1;
- page = -1;
- break;
-
- case FLEXONENAND_CMD_PI_ACCESS:
- /* addr contains die index */
- block = addr * this->density_mask;
- page = -1;
- break;
-
- case ONENAND_CMD_ERASE:
- case ONENAND_CMD_MULTIBLOCK_ERASE:
- case ONENAND_CMD_ERASE_VERIFY:
- case ONENAND_CMD_BUFFERRAM:
- case ONENAND_CMD_OTP_ACCESS:
- block = onenand_block(this, addr);
- page = -1;
- break;
-
- case FLEXONENAND_CMD_READ_PI:
- cmd = ONENAND_CMD_READ;
- block = addr * this->density_mask;
- page = 0;
- break;
-
- default:
- block = onenand_block(this, addr);
- if (FLEXONENAND(this))
- page = (int) (addr - onenand_addr(this, block))>>\
- this->page_shift;
- else
- page = (int) (addr >> this->page_shift);
- if (ONENAND_IS_2PLANE(this)) {
- /* Make the even block number */
- block &= ~1;
- /* Is it the odd plane? */
- if (addr & this->writesize)
- block++;
- page >>= 1;
- }
- page &= this->page_mask;
- break;
- }
-
- /* NOTE: The setting order of the registers is very important! */
- if (cmd == ONENAND_CMD_BUFFERRAM) {
- /* Select DataRAM for DDP */
- value = onenand_bufferram_address(this, block);
- this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
-
- if (ONENAND_IS_2PLANE(this) || ONENAND_IS_4KB_PAGE(this))
- /* It is always BufferRAM0 */
- ONENAND_SET_BUFFERRAM0(this);
- else
- /* Switch to the next data buffer */
- ONENAND_SET_NEXT_BUFFERRAM(this);
-
- return 0;
- }
-
- if (block != -1) {
- /* Write 'DFS, FBA' of Flash */
- value = onenand_block_address(this, block);
- this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
-
- /* Select DataRAM for DDP */
- value = onenand_bufferram_address(this, block);
- this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
- }
-
- if (page != -1) {
- /* Now we use page size operation */
- int sectors = 0, count = 0;
- int dataram;
-
- switch (cmd) {
- case FLEXONENAND_CMD_RECOVER_LSB:
- case ONENAND_CMD_READ:
- case ONENAND_CMD_READOOB:
- if (ONENAND_IS_4KB_PAGE(this))
- /* It is always BufferRAM0 */
- dataram = ONENAND_SET_BUFFERRAM0(this);
- else
- dataram = ONENAND_SET_NEXT_BUFFERRAM(this);
- break;
-
- default:
- if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG)
- cmd = ONENAND_CMD_2X_PROG;
- dataram = ONENAND_CURRENT_BUFFERRAM(this);
- break;
- }
-
- /* Write 'FPA, FSA' of Flash */
- value = onenand_page_address(page, sectors);
- this->write_word(value, this->base + ONENAND_REG_START_ADDRESS8);
-
- /* Write 'BSA, BSC' of DataRAM */
- value = onenand_buffer_address(dataram, sectors, count);
- this->write_word(value, this->base + ONENAND_REG_START_BUFFER);
- }
-
- /* Interrupt clear */
- this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT);
-
- /* Write command */
- this->write_word(cmd, this->base + ONENAND_REG_COMMAND);
-
- return 0;
-}
-
-/**
- * onenand_read_ecc - return ecc status
- * @param this onenand chip structure
- */
-static inline int onenand_read_ecc(struct onenand_chip *this)
-{
- int ecc, i, result = 0;
-
- if (!FLEXONENAND(this) && !ONENAND_IS_4KB_PAGE(this))
- return this->read_word(this->base + ONENAND_REG_ECC_STATUS);
-
- for (i = 0; i < 4; i++) {
- ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS + i*2);
- if (likely(!ecc))
- continue;
- if (ecc & FLEXONENAND_UNCORRECTABLE_ERROR)
- return ONENAND_ECC_2BIT_ALL;
- else
- result = ONENAND_ECC_1BIT_ALL;
- }
-
- return result;
-}
-
-/**
- * onenand_wait - [DEFAULT] wait until the command is done
- * @param mtd MTD device structure
- * @param state state to select the max. timeout value
- *
- * Wait for command done. This applies to all OneNAND command
- * Read can take up to 30us, erase up to 2ms and program up to 350us
- * according to general OneNAND specs
- */
-static int onenand_wait(struct mtd_info *mtd, int state)
-{
- struct onenand_chip * this = mtd->priv;
- unsigned long timeout;
- unsigned int flags = ONENAND_INT_MASTER;
- unsigned int interrupt = 0;
- unsigned int ctrl;
-
- /* The 20 msec is enough */
- timeout = jiffies + msecs_to_jiffies(20);
- while (time_before(jiffies, timeout)) {
- interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
-
- if (interrupt & flags)
- break;
-
- if (state != FL_READING && state != FL_PREPARING_ERASE)
- cond_resched();
- }
- /* To get correct interrupt status in timeout case */
- interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
-
- ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
-
- /*
- * In the Spec. it checks the controller status first
- * However if you get the correct information in case of
- * power off recovery (POR) test, it should read ECC status first
- */
- if (interrupt & ONENAND_INT_READ) {
- int ecc = onenand_read_ecc(this);
- if (ecc) {
- if (ecc & ONENAND_ECC_2BIT_ALL) {
- printk(KERN_ERR "%s: ECC error = 0x%04x\n",
- __func__, ecc);
- mtd->ecc_stats.failed++;
- return -EBADMSG;
- } else if (ecc & ONENAND_ECC_1BIT_ALL) {
- printk(KERN_DEBUG "%s: correctable ECC error = 0x%04x\n",
- __func__, ecc);
- mtd->ecc_stats.corrected++;
- }
- }
- } else if (state == FL_READING) {
- printk(KERN_ERR "%s: read timeout! ctrl=0x%04x intr=0x%04x\n",
- __func__, ctrl, interrupt);
- return -EIO;
- }
-
- if (state == FL_PREPARING_ERASE && !(interrupt & ONENAND_INT_ERASE)) {
- printk(KERN_ERR "%s: mb erase timeout! ctrl=0x%04x intr=0x%04x\n",
- __func__, ctrl, interrupt);
- return -EIO;
- }
-
- if (!(interrupt & ONENAND_INT_MASTER)) {
- printk(KERN_ERR "%s: timeout! ctrl=0x%04x intr=0x%04x\n",
- __func__, ctrl, interrupt);
- return -EIO;
- }
-
- /* If there's controller error, it's a real error */
- if (ctrl & ONENAND_CTRL_ERROR) {
- printk(KERN_ERR "%s: controller error = 0x%04x\n",
- __func__, ctrl);
- if (ctrl & ONENAND_CTRL_LOCK)
- printk(KERN_ERR "%s: it's locked error.\n", __func__);
- return -EIO;
- }
-
- return 0;
-}
-
-/*
- * onenand_interrupt - [DEFAULT] onenand interrupt handler
- * @param irq onenand interrupt number
- * @param dev_id interrupt data
- *
- * complete the work
- */
-static irqreturn_t onenand_interrupt(int irq, void *data)
-{
- struct onenand_chip *this = data;
-
- /* To handle shared interrupt */
- if (!this->complete.done)
- complete(&this->complete);
-
- return IRQ_HANDLED;
-}
-
-/*
- * onenand_interrupt_wait - [DEFAULT] wait until the command is done
- * @param mtd MTD device structure
- * @param state state to select the max. timeout value
- *
- * Wait for command done.
- */
-static int onenand_interrupt_wait(struct mtd_info *mtd, int state)
-{
- struct onenand_chip *this = mtd->priv;
-
- wait_for_completion(&this->complete);
-
- return onenand_wait(mtd, state);
-}
-
-/*
- * onenand_try_interrupt_wait - [DEFAULT] try interrupt wait
- * @param mtd MTD device structure
- * @param state state to select the max. timeout value
- *
- * Try interrupt based wait (It is used one-time)
- */
-static int onenand_try_interrupt_wait(struct mtd_info *mtd, int state)
-{
- struct onenand_chip *this = mtd->priv;
- unsigned long remain, timeout;
-
- /* We use interrupt wait first */
- this->wait = onenand_interrupt_wait;
-
- timeout = msecs_to_jiffies(100);
- remain = wait_for_completion_timeout(&this->complete, timeout);
- if (!remain) {
- printk(KERN_INFO "OneNAND: There's no interrupt. "
- "We use the normal wait\n");
-
- /* Release the irq */
- free_irq(this->irq, this);
-
- this->wait = onenand_wait;
- }
-
- return onenand_wait(mtd, state);
-}
-
-/*
- * onenand_setup_wait - [OneNAND Interface] setup onenand wait method
- * @param mtd MTD device structure
- *
- * There's two method to wait onenand work
- * 1. polling - read interrupt status register
- * 2. interrupt - use the kernel interrupt method
- */
-static void onenand_setup_wait(struct mtd_info *mtd)
-{
- struct onenand_chip *this = mtd->priv;
- int syscfg;
-
- init_completion(&this->complete);
-
- if (this->irq <= 0) {
- this->wait = onenand_wait;
- return;
- }
-
- if (request_irq(this->irq, &onenand_interrupt,
- IRQF_SHARED, "onenand", this)) {
- /* If we can't get irq, use the normal wait */
- this->wait = onenand_wait;
- return;
- }
-
- /* Enable interrupt */
- syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
- syscfg |= ONENAND_SYS_CFG1_IOBE;
- this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);
-
- this->wait = onenand_try_interrupt_wait;
-}
-
-/**
- * onenand_bufferram_offset - [DEFAULT] BufferRAM offset
- * @param mtd MTD data structure
- * @param area BufferRAM area
- * @return offset given area
- *
- * Return BufferRAM offset given area
- */
-static inline int onenand_bufferram_offset(struct mtd_info *mtd, int area)
-{
- struct onenand_chip *this = mtd->priv;
-
- if (ONENAND_CURRENT_BUFFERRAM(this)) {
- /* Note: the 'this->writesize' is a real page size */
- if (area == ONENAND_DATARAM)
- return this->writesize;
- if (area == ONENAND_SPARERAM)
- return mtd->oobsize;
- }
-
- return 0;
-}
-
-/**
- * onenand_read_bufferram - [OneNAND Interface] Read the bufferram area
- * @param mtd MTD data structure
- * @param area BufferRAM area
- * @param buffer the databuffer to put/get data
- * @param offset offset to read from or write to
- * @param count number of bytes to read/write
- *
- * Read the BufferRAM area
- */
-static int onenand_read_bufferram(struct mtd_info *mtd, int area,
- unsigned char *buffer, int offset, size_t count)
-{
- struct onenand_chip *this = mtd->priv;
- void __iomem *bufferram;
-
- bufferram = this->base + area;
-
- bufferram += onenand_bufferram_offset(mtd, area);
-
- if (ONENAND_CHECK_BYTE_ACCESS(count)) {
- unsigned short word;
-
- /* Align with word(16-bit) size */
- count--;
-
- /* Read word and save byte */
- word = this->read_word(bufferram + offset + count);
- buffer[count] = (word & 0xff);
- }
-
- memcpy(buffer, bufferram + offset, count);
-
- return 0;
-}
-
-/**
- * onenand_sync_read_bufferram - [OneNAND Interface] Read the bufferram area with Sync. Burst mode
- * @param mtd MTD data structure
- * @param area BufferRAM area
- * @param buffer the databuffer to put/get data
- * @param offset offset to read from or write to
- * @param count number of bytes to read/write
- *
- * Read the BufferRAM area with Sync. Burst Mode
- */
-static int onenand_sync_read_bufferram(struct mtd_info *mtd, int area,
- unsigned char *buffer, int offset, size_t count)
-{
- struct onenand_chip *this = mtd->priv;
- void __iomem *bufferram;
-
- bufferram = this->base + area;
-
- bufferram += onenand_bufferram_offset(mtd, area);
-
- this->mmcontrol(mtd, ONENAND_SYS_CFG1_SYNC_READ);
-
- if (ONENAND_CHECK_BYTE_ACCESS(count)) {
- unsigned short word;
-
- /* Align with word(16-bit) size */
- count--;
-
- /* Read word and save byte */
- word = this->read_word(bufferram + offset + count);
- buffer[count] = (word & 0xff);
- }
-
- memcpy(buffer, bufferram + offset, count);
-
- this->mmcontrol(mtd, 0);
-
- return 0;
-}
-
-/**
- * onenand_write_bufferram - [OneNAND Interface] Write the bufferram area
- * @param mtd MTD data structure
- * @param area BufferRAM area
- * @param buffer the databuffer to put/get data
- * @param offset offset to read from or write to
- * @param count number of bytes to read/write
- *
- * Write the BufferRAM area
- */
-static int onenand_write_bufferram(struct mtd_info *mtd, int area,
- const unsigned char *buffer, int offset, size_t count)
-{
- struct onenand_chip *this = mtd->priv;
- void __iomem *bufferram;
-
- bufferram = this->base + area;
-
- bufferram += onenand_bufferram_offset(mtd, area);
-
- if (ONENAND_CHECK_BYTE_ACCESS(count)) {
- unsigned short word;
- int byte_offset;
-
- /* Align with word(16-bit) size */
- count--;
-
- /* Calculate byte access offset */
- byte_offset = offset + count;
-
- /* Read word and save byte */
- word = this->read_word(bufferram + byte_offset);
- word = (word & ~0xff) | buffer[count];
- this->write_word(word, bufferram + byte_offset);
- }
-
- memcpy(bufferram + offset, buffer, count);
-
- return 0;
-}
-
-/**
- * onenand_get_2x_blockpage - [GENERIC] Get blockpage at 2x program mode
- * @param mtd MTD data structure
- * @param addr address to check
- * @return blockpage address
- *
- * Get blockpage address at 2x program mode
- */
-static int onenand_get_2x_blockpage(struct mtd_info *mtd, loff_t addr)
-{
- struct onenand_chip *this = mtd->priv;
- int blockpage, block, page;
-
- /* Calculate the even block number */
- block = (int) (addr >> this->erase_shift) & ~1;
- /* Is it the odd plane? */
- if (addr & this->writesize)
- block++;
- page = (int) (addr >> (this->page_shift + 1)) & this->page_mask;
- blockpage = (block << 7) | page;
-
- return blockpage;
-}
-
-/**
- * onenand_check_bufferram - [GENERIC] Check BufferRAM information
- * @param mtd MTD data structure
- * @param addr address to check
- * @return 1 if there are valid data, otherwise 0
- *
- * Check bufferram if there is data we required
- */
-static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr)
-{
- struct onenand_chip *this = mtd->priv;
- int blockpage, found = 0;
- unsigned int i;
-
- if (ONENAND_IS_2PLANE(this))
- blockpage = onenand_get_2x_blockpage(mtd, addr);
- else
- blockpage = (int) (addr >> this->page_shift);
-
- /* Is there valid data? */
- i = ONENAND_CURRENT_BUFFERRAM(this);
- if (this->bufferram[i].blockpage == blockpage)
- found = 1;
- else {
- /* Check another BufferRAM */
- i = ONENAND_NEXT_BUFFERRAM(this);
- if (this->bufferram[i].blockpage == blockpage) {
- ONENAND_SET_NEXT_BUFFERRAM(this);
- found = 1;
- }
- }
-
- if (found && ONENAND_IS_DDP(this)) {
- /* Select DataRAM for DDP */
- int block = onenand_block(this, addr);
- int value = onenand_bufferram_address(this, block);
- this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
- }
-
- return found;
-}
-
-/**
- * onenand_update_bufferram - [GENERIC] Update BufferRAM information
- * @param mtd MTD data structure
- * @param addr address to update
- * @param valid valid flag
- *
- * Update BufferRAM information
- */
-static void onenand_update_bufferram(struct mtd_info *mtd, loff_t addr,
- int valid)
-{
- struct onenand_chip *this = mtd->priv;
- int blockpage;
- unsigned int i;
-
- if (ONENAND_IS_2PLANE(this))
- blockpage = onenand_get_2x_blockpage(mtd, addr);
- else
- blockpage = (int) (addr >> this->page_shift);
-
- /* Invalidate another BufferRAM */
- i = ONENAND_NEXT_BUFFERRAM(this);
- if (this->bufferram[i].blockpage == blockpage)
- this->bufferram[i].blockpage = -1;
-
- /* Update BufferRAM */
- i = ONENAND_CURRENT_BUFFERRAM(this);
- if (valid)
- this->bufferram[i].blockpage = blockpage;
- else
- this->bufferram[i].blockpage = -1;
-}
-
-/**
- * onenand_invalidate_bufferram - [GENERIC] Invalidate BufferRAM information
- * @param mtd MTD data structure
- * @param addr start address to invalidate
- * @param len length to invalidate
- *
- * Invalidate BufferRAM information
- */
-static void onenand_invalidate_bufferram(struct mtd_info *mtd, loff_t addr,
- unsigned int len)
-{
- struct onenand_chip *this = mtd->priv;
- int i;
- loff_t end_addr = addr + len;
-
- /* Invalidate BufferRAM */
- for (i = 0; i < MAX_BUFFERRAM; i++) {
- loff_t buf_addr = this->bufferram[i].blockpage << this->page_shift;
- if (buf_addr >= addr && buf_addr < end_addr)
- this->bufferram[i].blockpage = -1;
- }
-}
-
-/**
- * onenand_get_device - [GENERIC] Get chip for selected access
- * @param mtd MTD device structure
- * @param new_state the state which is requested
- *
- * Get the device and lock it for exclusive access
- */
-static int onenand_get_device(struct mtd_info *mtd, int new_state)
-{
- struct onenand_chip *this = mtd->priv;
- DECLARE_WAITQUEUE(wait, current);
-
- /*
- * Grab the lock and see if the device is available
- */
- while (1) {
- spin_lock(&this->chip_lock);
- if (this->state == FL_READY) {
- this->state = new_state;
- spin_unlock(&this->chip_lock);
- if (new_state != FL_PM_SUSPENDED && this->enable)
- this->enable(mtd);
- break;
- }
- if (new_state == FL_PM_SUSPENDED) {
- spin_unlock(&this->chip_lock);
- return (this->state == FL_PM_SUSPENDED) ? 0 : -EAGAIN;
- }
- set_current_state(TASK_UNINTERRUPTIBLE);
- add_wait_queue(&this->wq, &wait);
- spin_unlock(&this->chip_lock);
- schedule();
- remove_wait_queue(&this->wq, &wait);
- }
-
- return 0;
-}
-
-/**
- * onenand_release_device - [GENERIC] release chip
- * @param mtd MTD device structure
- *
- * Deselect, release chip lock and wake up anyone waiting on the device
- */
-static void onenand_release_device(struct mtd_info *mtd)
-{
- struct onenand_chip *this = mtd->priv;
-
- if (this->state != FL_PM_SUSPENDED && this->disable)
- this->disable(mtd);
- /* Release the chip */
- spin_lock(&this->chip_lock);
- this->state = FL_READY;
- wake_up(&this->wq);
- spin_unlock(&this->chip_lock);
-}
-
-/**
- * onenand_transfer_auto_oob - [INTERN] oob auto-placement transfer
- * @param mtd MTD device structure
- * @param buf destination address
- * @param column oob offset to read from
- * @param thislen oob length to read
- */
-static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int column,
- int thislen)
-{
- struct onenand_chip *this = mtd->priv;
- int ret;
-
- this->read_bufferram(mtd, ONENAND_SPARERAM, this->oob_buf, 0,
- mtd->oobsize);
- ret = mtd_ooblayout_get_databytes(mtd, buf, this->oob_buf,
- column, thislen);
- if (ret)
- return ret;
-
- return 0;
-}
-
-/**
- * onenand_recover_lsb - [Flex-OneNAND] Recover LSB page data
- * @param mtd MTD device structure
- * @param addr address to recover
- * @param status return value from onenand_wait / onenand_bbt_wait
- *
- * MLC NAND Flash cell has paired pages - LSB page and MSB page. LSB page has
- * lower page address and MSB page has higher page address in paired pages.
- * If power off occurs during MSB page program, the paired LSB page data can
- * become corrupt. LSB page recovery read is a way to read LSB page though page
- * data are corrupted. When uncorrectable error occurs as a result of LSB page
- * read after power up, issue LSB page recovery read.
- */
-static int onenand_recover_lsb(struct mtd_info *mtd, loff_t addr, int status)
-{
- struct onenand_chip *this = mtd->priv;
- int i;
-
- /* Recovery is only for Flex-OneNAND */
- if (!FLEXONENAND(this))
- return status;
-
- /* check if we failed due to uncorrectable error */
- if (!mtd_is_eccerr(status) && status != ONENAND_BBT_READ_ECC_ERROR)
- return status;
-
- /* check if address lies in MLC region */
- i = flexonenand_region(mtd, addr);
- if (mtd->eraseregions[i].erasesize < (1 << this->erase_shift))
- return status;
-
- /* We are attempting to reread, so decrement stats.failed
- * which was incremented by onenand_wait due to read failure
- */
- printk(KERN_INFO "%s: Attempting to recover from uncorrectable read\n",
- __func__);
- mtd->ecc_stats.failed--;
-
- /* Issue the LSB page recovery command */
- this->command(mtd, FLEXONENAND_CMD_RECOVER_LSB, addr, this->writesize);
- return this->wait(mtd, FL_READING);
-}
-
-/**
- * onenand_mlc_read_ops_nolock - MLC OneNAND read main and/or out-of-band
- * @param mtd MTD device structure
- * @param from offset to read from
- * @param ops: oob operation description structure
- *
- * MLC OneNAND / Flex-OneNAND has 4KB page size and 4KB dataram.
- * So, read-while-load is not present.
- */
-static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from,
- struct mtd_oob_ops *ops)
-{
- struct onenand_chip *this = mtd->priv;
- struct mtd_ecc_stats stats;
- size_t len = ops->len;
- size_t ooblen = ops->ooblen;
- u_char *buf = ops->datbuf;
- u_char *oobbuf = ops->oobbuf;
- int read = 0, column, thislen;
- int oobread = 0, oobcolumn, thisooblen, oobsize;
- int ret = 0;
- int writesize = this->writesize;
-
- pr_debug("%s: from = 0x%08x, len = %i\n", __func__, (unsigned int)from,
- (int)len);
-
- oobsize = mtd_oobavail(mtd, ops);
- oobcolumn = from & (mtd->oobsize - 1);
-
- /* Do not allow reads past end of device */
- if (from + len > mtd->size) {
- printk(KERN_ERR "%s: Attempt read beyond end of device\n",
- __func__);
- ops->retlen = 0;
- ops->oobretlen = 0;
- return -EINVAL;
- }
-
- stats = mtd->ecc_stats;
-
- while (read < len) {
- cond_resched();
-
- thislen = min_t(int, writesize, len - read);
-
- column = from & (writesize - 1);
- if (column + thislen > writesize)
- thislen = writesize - column;
-
- if (!onenand_check_bufferram(mtd, from)) {
- this->command(mtd, ONENAND_CMD_READ, from, writesize);
-
- ret = this->wait(mtd, FL_READING);
- if (unlikely(ret))
- ret = onenand_recover_lsb(mtd, from, ret);
- onenand_update_bufferram(mtd, from, !ret);
- if (mtd_is_eccerr(ret))
- ret = 0;
- if (ret)
- break;
- }
-
- this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen);
- if (oobbuf) {
- thisooblen = oobsize - oobcolumn;
- thisooblen = min_t(int, thisooblen, ooblen - oobread);
-
- if (ops->mode == MTD_OPS_AUTO_OOB)
- onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen);
- else
- this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen);
- oobread += thisooblen;
- oobbuf += thisooblen;
- oobcolumn = 0;
- }
-
- read += thislen;
- if (read == len)
- break;
-
- from += thislen;
- buf += thislen;
- }
-
- /*
- * Return success, if no ECC failures, else -EBADMSG
- * fs driver will take care of that, because
- * retlen == desired len and result == -EBADMSG
- */
- ops->retlen = read;
- ops->oobretlen = oobread;
-
- if (ret)
- return ret;
-
- if (mtd->ecc_stats.failed - stats.failed)
- return -EBADMSG;
-
- /* return max bitflips per ecc step; ONENANDs correct 1 bit only */
- return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0;
-}
-
-/**
- * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band
- * @param mtd MTD device structure
- * @param from offset to read from
- * @param ops: oob operation description structure
- *
- * OneNAND read main and/or out-of-band data
- */
-static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
- struct mtd_oob_ops *ops)
-{
- struct onenand_chip *this = mtd->priv;
- struct mtd_ecc_stats stats;
- size_t len = ops->len;
- size_t ooblen = ops->ooblen;
- u_char *buf = ops->datbuf;
- u_char *oobbuf = ops->oobbuf;
- int read = 0, column, thislen;
- int oobread = 0, oobcolumn, thisooblen, oobsize;
- int ret = 0, boundary = 0;
- int writesize = this->writesize;
-
- pr_debug("%s: from = 0x%08x, len = %i\n", __func__, (unsigned int)from,
- (int)len);
-
- oobsize = mtd_oobavail(mtd, ops);
- oobcolumn = from & (mtd->oobsize - 1);
-
- /* Do not allow reads past end of device */
- if ((from + len) > mtd->size) {
- printk(KERN_ERR "%s: Attempt read beyond end of device\n",
- __func__);
- ops->retlen = 0;
- ops->oobretlen = 0;
- return -EINVAL;
- }
-
- stats = mtd->ecc_stats;
-
- /* Read-while-load method */
-
- /* Do first load to bufferRAM */
- if (read < len) {
- if (!onenand_check_bufferram(mtd, from)) {
- this->command(mtd, ONENAND_CMD_READ, from, writesize);
- ret = this->wait(mtd, FL_READING);
- onenand_update_bufferram(mtd, from, !ret);
- if (mtd_is_eccerr(ret))
- ret = 0;
- }
- }
-
- thislen = min_t(int, writesize, len - read);
- column = from & (writesize - 1);
- if (column + thislen > writesize)
- thislen = writesize - column;
-
- while (!ret) {
- /* If there is more to load then start next load */
- from += thislen;
- if (read + thislen < len) {
- this->command(mtd, ONENAND_CMD_READ, from, writesize);
- /*
- * Chip boundary handling in DDP
- * Now we issued chip 1 read and pointed chip 1
- * bufferram so we have to point chip 0 bufferram.
- */
- if (ONENAND_IS_DDP(this) &&
- unlikely(from == (this->chipsize >> 1))) {
- this->write_word(ONENAND_DDP_CHIP0, this->base + ONENAND_REG_START_ADDRESS2);
- boundary = 1;
- } else
- boundary = 0;
- ONENAND_SET_PREV_BUFFERRAM(this);
- }
- /* While load is going, read from last bufferRAM */
- this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen);
-
- /* Read oob area if needed */
- if (oobbuf) {
- thisooblen = oobsize - oobcolumn;
- thisooblen = min_t(int, thisooblen, ooblen - oobread);
-
- if (ops->mode == MTD_OPS_AUTO_OOB)
- onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen);
- else
- this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen);
- oobread += thisooblen;
- oobbuf += thisooblen;
- oobcolumn = 0;
- }
-
- /* See if we are done */
- read += thislen;
- if (read == len)
- break;
- /* Set up for next read from bufferRAM */
- if (unlikely(boundary))
- this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2);
- ONENAND_SET_NEXT_BUFFERRAM(this);
- buf += thislen;
- thislen = min_t(int, writesize, len - read);
- column = 0;
- cond_resched();
- /* Now wait for load */
- ret = this->wait(mtd, FL_READING);
- onenand_update_bufferram(mtd, from, !ret);
- if (mtd_is_eccerr(ret))
- ret = 0;
- }
-
- /*
- * Return success, if no ECC failures, else -EBADMSG
- * fs driver will take care of that, because
- * retlen == desired len and result == -EBADMSG
- */
- ops->retlen = read;
- ops->oobretlen = oobread;
-
- if (ret)
- return ret;
-
- if (mtd->ecc_stats.failed - stats.failed)
- return -EBADMSG;
-
- /* return max bitflips per ecc step; ONENANDs correct 1 bit only */
- return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0;
-}
-
-/**
- * onenand_read_oob_nolock - [MTD Interface] OneNAND read out-of-band
- * @param mtd MTD device structure
- * @param from offset to read from
- * @param ops: oob operation description structure
- *
- * OneNAND read out-of-band data from the spare area
- */
-static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
- struct mtd_oob_ops *ops)
-{
- struct onenand_chip *this = mtd->priv;
- struct mtd_ecc_stats stats;
- int read = 0, thislen, column, oobsize;
- size_t len = ops->ooblen;
- unsigned int mode = ops->mode;
- u_char *buf = ops->oobbuf;
- int ret = 0, readcmd;
-
- from += ops->ooboffs;
-
- pr_debug("%s: from = 0x%08x, len = %i\n", __func__, (unsigned int)from,
- (int)len);
-
- /* Initialize return length value */
- ops->oobretlen = 0;
-
- if (mode == MTD_OPS_AUTO_OOB)
- oobsize = mtd->oobavail;
- else
- oobsize = mtd->oobsize;
-
- column = from & (mtd->oobsize - 1);
-
- if (unlikely(column >= oobsize)) {
- printk(KERN_ERR "%s: Attempted to start read outside oob\n",
- __func__);
- return -EINVAL;
- }
-
- stats = mtd->ecc_stats;
-
- readcmd = ONENAND_IS_4KB_PAGE(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB;
-
- while (read < len) {
- cond_resched();
-
- thislen = oobsize - column;
- thislen = min_t(int, thislen, len);
-
- this->command(mtd, readcmd, from, mtd->oobsize);
-
- onenand_update_bufferram(mtd, from, 0);
-
- ret = this->wait(mtd, FL_READING);
- if (unlikely(ret))
- ret = onenand_recover_lsb(mtd, from, ret);
-
- if (ret && !mtd_is_eccerr(ret)) {
- printk(KERN_ERR "%s: read failed = 0x%x\n",
- __func__, ret);
- break;
- }
-
- if (mode == MTD_OPS_AUTO_OOB)
- onenand_transfer_auto_oob(mtd, buf, column, thislen);
- else
- this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
-
- read += thislen;
-
- if (read == len)
- break;
-
- buf += thislen;
-
- /* Read more? */
- if (read < len) {
- /* Page size */
- from += mtd->writesize;
- column = 0;
- }
- }
-
- ops->oobretlen = read;
-
- if (ret)
- return ret;
-
- if (mtd->ecc_stats.failed - stats.failed)
- return -EBADMSG;
-
- return 0;
-}
-
-/**
- * onenand_read_oob - [MTD Interface] Read main and/or out-of-band
- * @param mtd: MTD device structure
- * @param from: offset to read from
- * @param ops: oob operation description structure
-
- * Read main and/or out-of-band
- */
-static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
- struct mtd_oob_ops *ops)
-{
- struct onenand_chip *this = mtd->priv;
- int ret;
-
- switch (ops->mode) {
- case MTD_OPS_PLACE_OOB:
- case MTD_OPS_AUTO_OOB:
- break;
- case MTD_OPS_RAW:
- /* Not implemented yet */
- default:
- return -EINVAL;
- }
-
- onenand_get_device(mtd, FL_READING);
- if (ops->datbuf)
- ret = ONENAND_IS_4KB_PAGE(this) ?
- onenand_mlc_read_ops_nolock(mtd, from, ops) :
- onenand_read_ops_nolock(mtd, from, ops);
- else
- ret = onenand_read_oob_nolock(mtd, from, ops);
- onenand_release_device(mtd);
-
- return ret;
-}
-
-/**
- * onenand_bbt_wait - [DEFAULT] wait until the command is done
- * @param mtd MTD device structure
- * @param state state to select the max. timeout value
- *
- * Wait for command done.
- */
-static int onenand_bbt_wait(struct mtd_info *mtd, int state)
-{
- struct onenand_chip *this = mtd->priv;
- unsigned long timeout;
- unsigned int interrupt, ctrl, ecc, addr1, addr8;
-
- /* The 20 msec is enough */
- timeout = jiffies + msecs_to_jiffies(20);
- while (time_before(jiffies, timeout)) {
- interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
- if (interrupt & ONENAND_INT_MASTER)
- break;
- }
- /* To get correct interrupt status in timeout case */
- interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
- ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
- addr1 = this->read_word(this->base + ONENAND_REG_START_ADDRESS1);
- addr8 = this->read_word(this->base + ONENAND_REG_START_ADDRESS8);
-
- if (interrupt & ONENAND_INT_READ) {
- ecc = onenand_read_ecc(this);
- if (ecc & ONENAND_ECC_2BIT_ALL) {
- printk(KERN_DEBUG "%s: ecc 0x%04x ctrl 0x%04x "
- "intr 0x%04x addr1 %#x addr8 %#x\n",
- __func__, ecc, ctrl, interrupt, addr1, addr8);
- return ONENAND_BBT_READ_ECC_ERROR;
- }
- } else {
- printk(KERN_ERR "%s: read timeout! ctrl 0x%04x "
- "intr 0x%04x addr1 %#x addr8 %#x\n",
- __func__, ctrl, interrupt, addr1, addr8);
- return ONENAND_BBT_READ_FATAL_ERROR;
- }
-
- /* Initial bad block case: 0x2400 or 0x0400 */
- if (ctrl & ONENAND_CTRL_ERROR) {
- printk(KERN_DEBUG "%s: ctrl 0x%04x intr 0x%04x addr1 %#x "
- "addr8 %#x\n", __func__, ctrl, interrupt, addr1, addr8);
- return ONENAND_BBT_READ_ERROR;
- }
-
- return 0;
-}
-
-/**
- * onenand_bbt_read_oob - [MTD Interface] OneNAND read out-of-band for bbt scan
- * @param mtd MTD device structure
- * @param from offset to read from
- * @param ops oob operation description structure
- *
- * OneNAND read out-of-band data from the spare area for bbt scan
- */
-int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
- struct mtd_oob_ops *ops)
-{
- struct onenand_chip *this = mtd->priv;
- int read = 0, thislen, column;
- int ret = 0, readcmd;
- size_t len = ops->ooblen;
- u_char *buf = ops->oobbuf;
-
- pr_debug("%s: from = 0x%08x, len = %zi\n", __func__, (unsigned int)from,
- len);
-
- /* Initialize return value */
- ops->oobretlen = 0;
-
- /* Do not allow reads past end of device */
- if (unlikely((from + len) > mtd->size)) {
- printk(KERN_ERR "%s: Attempt read beyond end of device\n",
- __func__);
- return ONENAND_BBT_READ_FATAL_ERROR;
- }
-
- /* Grab the lock and see if the device is available */
- onenand_get_device(mtd, FL_READING);
-
- column = from & (mtd->oobsize - 1);
-
- readcmd = ONENAND_IS_4KB_PAGE(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB;
-
- while (read < len) {
- cond_resched();
-
- thislen = mtd->oobsize - column;
- thislen = min_t(int, thislen, len);
-
- this->command(mtd, readcmd, from, mtd->oobsize);
-
- onenand_update_bufferram(mtd, from, 0);
-
- ret = this->bbt_wait(mtd, FL_READING);
- if (unlikely(ret))
- ret = onenand_recover_lsb(mtd, from, ret);
-
- if (ret)
- break;
-
- this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
- read += thislen;
- if (read == len)
- break;
-
- buf += thislen;
-
- /* Read more? */
- if (read < len) {
- /* Update Page size */
- from += this->writesize;
- column = 0;
- }
- }
-
- /* Deselect and wake up anyone waiting on the device */
- onenand_release_device(mtd);
-
- ops->oobretlen = read;
- return ret;
-}
-
-#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
-/**
- * onenand_verify_oob - [GENERIC] verify the oob contents after a write
- * @param mtd MTD device structure
- * @param buf the databuffer to verify
- * @param to offset to read from
- */
-static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to)
-{
- struct onenand_chip *this = mtd->priv;
- u_char *oob_buf = this->oob_buf;
- int status, i, readcmd;
-
- readcmd = ONENAND_IS_4KB_PAGE(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB;
-
- this->command(mtd, readcmd, to, mtd->oobsize);
- onenand_update_bufferram(mtd, to, 0);
- status = this->wait(mtd, FL_READING);
- if (status)
- return status;
-
- this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize);
- for (i = 0; i < mtd->oobsize; i++)
- if (buf[i] != 0xFF && buf[i] != oob_buf[i])
- return -EBADMSG;
-
- return 0;
-}
-
-/**
- * onenand_verify - [GENERIC] verify the chip contents after a write
- * @param mtd MTD device structure
- * @param buf the databuffer to verify
- * @param addr offset to read from
- * @param len number of bytes to read and compare
- */
-static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len)
-{
- struct onenand_chip *this = mtd->priv;
- int ret = 0;
- int thislen, column;
-
- column = addr & (this->writesize - 1);
-
- while (len != 0) {
- thislen = min_t(int, this->writesize - column, len);
-
- this->command(mtd, ONENAND_CMD_READ, addr, this->writesize);
-
- onenand_update_bufferram(mtd, addr, 0);
-
- ret = this->wait(mtd, FL_READING);
- if (ret)
- return ret;
-
- onenand_update_bufferram(mtd, addr, 1);
-
- this->read_bufferram(mtd, ONENAND_DATARAM, this->verify_buf, 0, mtd->writesize);
-
- if (memcmp(buf, this->verify_buf + column, thislen))
- return -EBADMSG;
-
- len -= thislen;
- buf += thislen;
- addr += thislen;
- column = 0;
- }
-
- return 0;
-}
-#else
-#define onenand_verify(...) (0)
-#define onenand_verify_oob(...) (0)
-#endif
-
-#define NOTALIGNED(x) ((x & (this->subpagesize - 1)) != 0)
-
-static void onenand_panic_wait(struct mtd_info *mtd)
-{
- struct onenand_chip *this = mtd->priv;
- unsigned int interrupt;
- int i;
-
- for (i = 0; i < 2000; i++) {
- interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
- if (interrupt & ONENAND_INT_MASTER)
- break;
- udelay(10);
- }
-}
-
-/**
- * onenand_panic_write - [MTD Interface] write buffer to FLASH in a panic context
- * @param mtd MTD device structure
- * @param to offset to write to
- * @param len number of bytes to write
- * @param retlen pointer to variable to store the number of written bytes
- * @param buf the data to write
- *
- * Write with ECC
- */
-static int onenand_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
- size_t *retlen, const u_char *buf)
-{
- struct onenand_chip *this = mtd->priv;
- int column, subpage;
- int written = 0;
-
- if (this->state == FL_PM_SUSPENDED)
- return -EBUSY;
-
- /* Wait for any existing operation to clear */
- onenand_panic_wait(mtd);
-
- pr_debug("%s: to = 0x%08x, len = %i\n", __func__, (unsigned int)to,
- (int)len);
-
- /* Reject writes, which are not page aligned */
- if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) {
- printk(KERN_ERR "%s: Attempt to write not page aligned data\n",
- __func__);
- return -EINVAL;
- }
-
- column = to & (mtd->writesize - 1);
-
- /* Loop until all data write */
- while (written < len) {
- int thislen = min_t(int, mtd->writesize - column, len - written);
- u_char *wbuf = (u_char *) buf;
-
- this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen);
-
- /* Partial page write */
- subpage = thislen < mtd->writesize;
- if (subpage) {
- memset(this->page_buf, 0xff, mtd->writesize);
- memcpy(this->page_buf + column, buf, thislen);
- wbuf = this->page_buf;
- }
-
- this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize);
- this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize);
-
- this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);
-
- onenand_panic_wait(mtd);
-
- /* In partial page write we don't update bufferram */
- onenand_update_bufferram(mtd, to, !subpage);
- if (ONENAND_IS_2PLANE(this)) {
- ONENAND_SET_BUFFERRAM1(this);
- onenand_update_bufferram(mtd, to + this->writesize, !subpage);
- }
-
- written += thislen;
-
- if (written == len)
- break;
-
- column = 0;
- to += thislen;
- buf += thislen;
- }
-
- *retlen = written;
- return 0;
-}
-
-/**
- * onenand_fill_auto_oob - [INTERN] oob auto-placement transfer
- * @param mtd MTD device structure
- * @param oob_buf oob buffer
- * @param buf source address
- * @param column oob offset to write to
- * @param thislen oob length to write
- */
-static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
- const u_char *buf, int column, int thislen)
-{
- return mtd_ooblayout_set_databytes(mtd, buf, oob_buf, column, thislen);
-}
-
-/**
- * onenand_write_ops_nolock - [OneNAND Interface] write main and/or out-of-band
- * @param mtd MTD device structure
- * @param to offset to write to
- * @param ops oob operation description structure
- *
- * Write main and/or oob with ECC
- */
-static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
- struct mtd_oob_ops *ops)
-{
- struct onenand_chip *this = mtd->priv;
- int written = 0, column, thislen = 0, subpage = 0;
- int prev = 0, prevlen = 0, prev_subpage = 0, first = 1;
- int oobwritten = 0, oobcolumn, thisooblen, oobsize;
- size_t len = ops->len;
- size_t ooblen = ops->ooblen;
- const u_char *buf = ops->datbuf;
- const u_char *oob = ops->oobbuf;
- u_char *oobbuf;
- int ret = 0, cmd;
-
- pr_debug("%s: to = 0x%08x, len = %i\n", __func__, (unsigned int)to,
- (int)len);
-
- /* Initialize retlen, in case of early exit */
- ops->retlen = 0;
- ops->oobretlen = 0;
-
- /* Reject writes, which are not page aligned */
- if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) {
- printk(KERN_ERR "%s: Attempt to write not page aligned data\n",
- __func__);
- return -EINVAL;
- }
-
- /* Check zero length */
- if (!len)
- return 0;
- oobsize = mtd_oobavail(mtd, ops);
- oobcolumn = to & (mtd->oobsize - 1);
-
- column = to & (mtd->writesize - 1);
-
- /* Loop until all data write */
- while (1) {
- if (written < len) {
- u_char *wbuf = (u_char *) buf;
-
- thislen = min_t(int, mtd->writesize - column, len - written);
- thisooblen = min_t(int, oobsize - oobcolumn, ooblen - oobwritten);
-
- cond_resched();
-
- this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen);
-
- /* Partial page write */
- subpage = thislen < mtd->writesize;
- if (subpage) {
- memset(this->page_buf, 0xff, mtd->writesize);
- memcpy(this->page_buf + column, buf, thislen);
- wbuf = this->page_buf;
- }
-
- this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize);
-
- if (oob) {
- oobbuf = this->oob_buf;
-
- /* We send data to spare ram with oobsize
- * to prevent byte access */
- memset(oobbuf, 0xff, mtd->oobsize);
- if (ops->mode == MTD_OPS_AUTO_OOB)
- onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen);
- else
- memcpy(oobbuf + oobcolumn, oob, thisooblen);
-
- oobwritten += thisooblen;
- oob += thisooblen;
- oobcolumn = 0;
- } else
- oobbuf = (u_char *) ffchars;
-
- this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
- } else
- ONENAND_SET_NEXT_BUFFERRAM(this);
-
- /*
- * 2 PLANE, MLC, and Flex-OneNAND do not support
- * write-while-program feature.
- */
- if (!ONENAND_IS_2PLANE(this) && !ONENAND_IS_4KB_PAGE(this) && !first) {
- ONENAND_SET_PREV_BUFFERRAM(this);
-
- ret = this->wait(mtd, FL_WRITING);
-
- /* In partial page write we don't update bufferram */
- onenand_update_bufferram(mtd, prev, !ret && !prev_subpage);
- if (ret) {
- written -= prevlen;
- printk(KERN_ERR "%s: write failed %d\n",
- __func__, ret);
- break;
- }
-
- if (written == len) {
- /* Only check verify write turn on */
- ret = onenand_verify(mtd, buf - len, to - len, len);
- if (ret)
- printk(KERN_ERR "%s: verify failed %d\n",
- __func__, ret);
- break;
- }
-
- ONENAND_SET_NEXT_BUFFERRAM(this);
- }
-
- this->ongoing = 0;
- cmd = ONENAND_CMD_PROG;
-
- /* Exclude 1st OTP and OTP blocks for cache program feature */
- if (ONENAND_IS_CACHE_PROGRAM(this) &&
- likely(onenand_block(this, to) != 0) &&
- ONENAND_IS_4KB_PAGE(this) &&
- ((written + thislen) < len)) {
- cmd = ONENAND_CMD_2X_CACHE_PROG;
- this->ongoing = 1;
- }
-
- this->command(mtd, cmd, to, mtd->writesize);
-
- /*
- * 2 PLANE, MLC, and Flex-OneNAND wait here
- */
- if (ONENAND_IS_2PLANE(this) || ONENAND_IS_4KB_PAGE(this)) {
- ret = this->wait(mtd, FL_WRITING);
-
- /* In partial page write we don't update bufferram */
- onenand_update_bufferram(mtd, to, !ret && !subpage);
- if (ret) {
- printk(KERN_ERR "%s: write failed %d\n",
- __func__, ret);
- break;
- }
-
- /* Only check verify write turn on */
- ret = onenand_verify(mtd, buf, to, thislen);
- if (ret) {
- printk(KERN_ERR "%s: verify failed %d\n",
- __func__, ret);
- break;
- }
-
- written += thislen;
-
- if (written == len)
- break;
-
- } else
- written += thislen;
-
- column = 0;
- prev_subpage = subpage;
- prev = to;
- prevlen = thislen;
- to += thislen;
- buf += thislen;
- first = 0;
- }
-
- /* In error case, clear all bufferrams */
- if (written != len)
- onenand_invalidate_bufferram(mtd, 0, -1);
-
- ops->retlen = written;
- ops->oobretlen = oobwritten;
-
- return ret;
-}
-
-
-/**
- * onenand_write_oob_nolock - [INTERN] OneNAND write out-of-band
- * @param mtd MTD device structure
- * @param to offset to write to
- * @param len number of bytes to write
- * @param retlen pointer to variable to store the number of written bytes
- * @param buf the data to write
- * @param mode operation mode
- *
- * OneNAND write out-of-band
- */
-static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
- struct mtd_oob_ops *ops)
-{
- struct onenand_chip *this = mtd->priv;
- int column, ret = 0, oobsize;
- int written = 0, oobcmd;
- u_char *oobbuf;
- size_t len = ops->ooblen;
- const u_char *buf = ops->oobbuf;
- unsigned int mode = ops->mode;
-
- to += ops->ooboffs;
-
- pr_debug("%s: to = 0x%08x, len = %i\n", __func__, (unsigned int)to,
- (int)len);
-
- /* Initialize retlen, in case of early exit */
- ops->oobretlen = 0;
-
- if (mode == MTD_OPS_AUTO_OOB)
- oobsize = mtd->oobavail;
- else
- oobsize = mtd->oobsize;
-
- column = to & (mtd->oobsize - 1);
-
- if (unlikely(column >= oobsize)) {
- printk(KERN_ERR "%s: Attempted to start write outside oob\n",
- __func__);
- return -EINVAL;
- }
-
- /* For compatibility with NAND: Do not allow write past end of page */
- if (unlikely(column + len > oobsize)) {
- printk(KERN_ERR "%s: Attempt to write past end of page\n",
- __func__);
- return -EINVAL;
- }
-
- oobbuf = this->oob_buf;
-
- oobcmd = ONENAND_IS_4KB_PAGE(this) ? ONENAND_CMD_PROG : ONENAND_CMD_PROGOOB;
-
- /* Loop until all data write */
- while (written < len) {
- int thislen = min_t(int, oobsize, len - written);
-
- cond_resched();
-
- this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize);
-
- /* We send data to spare ram with oobsize
- * to prevent byte access */
- memset(oobbuf, 0xff, mtd->oobsize);
- if (mode == MTD_OPS_AUTO_OOB)
- onenand_fill_auto_oob(mtd, oobbuf, buf, column, thislen);
- else
- memcpy(oobbuf + column, buf, thislen);
- this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
-
- if (ONENAND_IS_4KB_PAGE(this)) {
- /* Set main area of DataRAM to 0xff*/
- memset(this->page_buf, 0xff, mtd->writesize);
- this->write_bufferram(mtd, ONENAND_DATARAM,
- this->page_buf, 0, mtd->writesize);
- }
-
- this->command(mtd, oobcmd, to, mtd->oobsize);
-
- onenand_update_bufferram(mtd, to, 0);
- if (ONENAND_IS_2PLANE(this)) {
- ONENAND_SET_BUFFERRAM1(this);
- onenand_update_bufferram(mtd, to + this->writesize, 0);
- }
-
- ret = this->wait(mtd, FL_WRITING);
- if (ret) {
- printk(KERN_ERR "%s: write failed %d\n", __func__, ret);
- break;
- }
-
- ret = onenand_verify_oob(mtd, oobbuf, to);
- if (ret) {
- printk(KERN_ERR "%s: verify failed %d\n",
- __func__, ret);
- break;
- }
-
- written += thislen;
- if (written == len)
- break;
-
- to += mtd->writesize;
- buf += thislen;
- column = 0;
- }
-
- ops->oobretlen = written;
-
- return ret;
-}
-
-/**
- * onenand_write_oob - [MTD Interface] NAND write data and/or out-of-band
- * @param mtd: MTD device structure
- * @param to: offset to write
- * @param ops: oob operation description structure
- */
-static int onenand_write_oob(struct mtd_info *mtd, loff_t to,
- struct mtd_oob_ops *ops)
-{
- int ret;
-
- switch (ops->mode) {
- case MTD_OPS_PLACE_OOB:
- case MTD_OPS_AUTO_OOB:
- break;
- case MTD_OPS_RAW:
- /* Not implemented yet */
- default:
- return -EINVAL;
- }
-
- onenand_get_device(mtd, FL_WRITING);
- if (ops->datbuf)
- ret = onenand_write_ops_nolock(mtd, to, ops);
- else
- ret = onenand_write_oob_nolock(mtd, to, ops);
- onenand_release_device(mtd);
-
- return ret;
-}
-
-/**
- * onenand_block_isbad_nolock - [GENERIC] Check if a block is marked bad
- * @param mtd MTD device structure
- * @param ofs offset from device start
- * @param allowbbt 1, if its allowed to access the bbt area
- *
- * Check, if the block is bad. Either by reading the bad block table or
- * calling of the scan function.
- */
-static int onenand_block_isbad_nolock(struct mtd_info *mtd, loff_t ofs, int allowbbt)
-{
- struct onenand_chip *this = mtd->priv;
- struct bbm_info *bbm = this->bbm;
-
- /* Return info from the table */
- return bbm->isbad_bbt(mtd, ofs, allowbbt);
-}
-
-
-static int onenand_multiblock_erase_verify(struct mtd_info *mtd,
- struct erase_info *instr)
-{
- struct onenand_chip *this = mtd->priv;
- loff_t addr = instr->addr;
- int len = instr->len;
- unsigned int block_size = (1 << this->erase_shift);
- int ret = 0;
-
- while (len) {
- this->command(mtd, ONENAND_CMD_ERASE_VERIFY, addr, block_size);
- ret = this->wait(mtd, FL_VERIFYING_ERASE);
- if (ret) {
- printk(KERN_ERR "%s: Failed verify, block %d\n",
- __func__, onenand_block(this, addr));
- instr->state = MTD_ERASE_FAILED;
- instr->fail_addr = addr;
- return -1;
- }
- len -= block_size;
- addr += block_size;
- }
- return 0;
-}
-
-/**
- * onenand_multiblock_erase - [INTERN] erase block(s) using multiblock erase
- * @param mtd MTD device structure
- * @param instr erase instruction
- * @param region erase region
- *
- * Erase one or more blocks up to 64 block at a time
- */
-static int onenand_multiblock_erase(struct mtd_info *mtd,
- struct erase_info *instr,
- unsigned int block_size)
-{
- struct onenand_chip *this = mtd->priv;
- loff_t addr = instr->addr;
- int len = instr->len;
- int eb_count = 0;
- int ret = 0;
- int bdry_block = 0;
-
- instr->state = MTD_ERASING;
-
- if (ONENAND_IS_DDP(this)) {
- loff_t bdry_addr = this->chipsize >> 1;
- if (addr < bdry_addr && (addr + len) > bdry_addr)
- bdry_block = bdry_addr >> this->erase_shift;
- }
-
- /* Pre-check bbs */
- while (len) {
- /* Check if we have a bad block, we do not erase bad blocks */
- if (onenand_block_isbad_nolock(mtd, addr, 0)) {
- printk(KERN_WARNING "%s: attempt to erase a bad block "
- "at addr 0x%012llx\n",
- __func__, (unsigned long long) addr);
- instr->state = MTD_ERASE_FAILED;
- return -EIO;
- }
- len -= block_size;
- addr += block_size;
- }
-
- len = instr->len;
- addr = instr->addr;
-
- /* loop over 64 eb batches */
- while (len) {
- struct erase_info verify_instr = *instr;
- int max_eb_count = MB_ERASE_MAX_BLK_COUNT;
-
- verify_instr.addr = addr;
- verify_instr.len = 0;
-
- /* do not cross chip boundary */
- if (bdry_block) {
- int this_block = (addr >> this->erase_shift);
-
- if (this_block < bdry_block) {
- max_eb_count = min(max_eb_count,
- (bdry_block - this_block));
- }
- }
-
- eb_count = 0;
-
- while (len > block_size && eb_count < (max_eb_count - 1)) {
- this->command(mtd, ONENAND_CMD_MULTIBLOCK_ERASE,
- addr, block_size);
- onenand_invalidate_bufferram(mtd, addr, block_size);
-
- ret = this->wait(mtd, FL_PREPARING_ERASE);
- if (ret) {
- printk(KERN_ERR "%s: Failed multiblock erase, "
- "block %d\n", __func__,
- onenand_block(this, addr));
- instr->state = MTD_ERASE_FAILED;
- instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
- return -EIO;
- }
-
- len -= block_size;
- addr += block_size;
- eb_count++;
- }
-
- /* last block of 64-eb series */
- cond_resched();
- this->command(mtd, ONENAND_CMD_ERASE, addr, block_size);
- onenand_invalidate_bufferram(mtd, addr, block_size);
-
- ret = this->wait(mtd, FL_ERASING);
- /* Check if it is write protected */
- if (ret) {
- printk(KERN_ERR "%s: Failed erase, block %d\n",
- __func__, onenand_block(this, addr));
- instr->state = MTD_ERASE_FAILED;
- instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
- return -EIO;
- }
-
- len -= block_size;
- addr += block_size;
- eb_count++;
-
- /* verify */
- verify_instr.len = eb_count * block_size;
- if (onenand_multiblock_erase_verify(mtd, &verify_instr)) {
- instr->state = verify_instr.state;
- instr->fail_addr = verify_instr.fail_addr;
- return -EIO;
- }
-
- }
- return 0;
-}
-
-
-/**
- * onenand_block_by_block_erase - [INTERN] erase block(s) using regular erase
- * @param mtd MTD device structure
- * @param instr erase instruction
- * @param region erase region
- * @param block_size erase block size
- *
- * Erase one or more blocks one block at a time
- */
-static int onenand_block_by_block_erase(struct mtd_info *mtd,
- struct erase_info *instr,
- struct mtd_erase_region_info *region,
- unsigned int block_size)
-{
- struct onenand_chip *this = mtd->priv;
- loff_t addr = instr->addr;
- int len = instr->len;
- loff_t region_end = 0;
- int ret = 0;
-
- if (region) {
- /* region is set for Flex-OneNAND */
- region_end = region->offset + region->erasesize * region->numblocks;
- }
-
- instr->state = MTD_ERASING;
-
- /* Loop through the blocks */
- while (len) {
- cond_resched();
-
- /* Check if we have a bad block, we do not erase bad blocks */
- if (onenand_block_isbad_nolock(mtd, addr, 0)) {
- printk(KERN_WARNING "%s: attempt to erase a bad block "
- "at addr 0x%012llx\n",
- __func__, (unsigned long long) addr);
- instr->state = MTD_ERASE_FAILED;
- return -EIO;
- }
-
- this->command(mtd, ONENAND_CMD_ERASE, addr, block_size);
-
- onenand_invalidate_bufferram(mtd, addr, block_size);
-
- ret = this->wait(mtd, FL_ERASING);
- /* Check, if it is write protected */
- if (ret) {
- printk(KERN_ERR "%s: Failed erase, block %d\n",
- __func__, onenand_block(this, addr));
- instr->state = MTD_ERASE_FAILED;
- instr->fail_addr = addr;
- return -EIO;
- }
-
- len -= block_size;
- addr += block_size;
-
- if (region && addr == region_end) {
- if (!len)
- break;
- region++;
-
- block_size = region->erasesize;
- region_end = region->offset + region->erasesize * region->numblocks;
-
- if (len & (block_size - 1)) {
- /* FIXME: This should be handled at MTD partitioning level. */
- printk(KERN_ERR "%s: Unaligned address\n",
- __func__);
- return -EIO;
- }
- }
- }
- return 0;
-}
-
-/**
- * onenand_erase - [MTD Interface] erase block(s)
- * @param mtd MTD device structure
- * @param instr erase instruction
- *
- * Erase one or more blocks
- */
-static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
-{
- struct onenand_chip *this = mtd->priv;
- unsigned int block_size;
- loff_t addr = instr->addr;
- loff_t len = instr->len;
- int ret = 0;
- struct mtd_erase_region_info *region = NULL;
- loff_t region_offset = 0;
-
- pr_debug("%s: start=0x%012llx, len=%llu\n", __func__,
- (unsigned long long)instr->addr,
- (unsigned long long)instr->len);
-
- if (FLEXONENAND(this)) {
- /* Find the eraseregion of this address */
- int i = flexonenand_region(mtd, addr);
-
- region = &mtd->eraseregions[i];
- block_size = region->erasesize;
-
- /* Start address within region must align on block boundary.
- * Erase region's start offset is always block start address.
- */
- region_offset = region->offset;
- } else
- block_size = 1 << this->erase_shift;
-
- /* Start address must align on block boundary */
- if (unlikely((addr - region_offset) & (block_size - 1))) {
- printk(KERN_ERR "%s: Unaligned address\n", __func__);
- return -EINVAL;
- }
-
- /* Length must align on block boundary */
- if (unlikely(len & (block_size - 1))) {
- printk(KERN_ERR "%s: Length not block aligned\n", __func__);
- return -EINVAL;
- }
-
- /* Grab the lock and see if the device is available */
- onenand_get_device(mtd, FL_ERASING);
-
- if (ONENAND_IS_4KB_PAGE(this) || region ||
- instr->len < MB_ERASE_MIN_BLK_COUNT * block_size) {
- /* region is set for Flex-OneNAND (no mb erase) */
- ret = onenand_block_by_block_erase(mtd, instr,
- region, block_size);
- } else {
- ret = onenand_multiblock_erase(mtd, instr, block_size);
- }
-
- /* Deselect and wake up anyone waiting on the device */
- onenand_release_device(mtd);
-
- /* Do call back function */
- if (!ret) {
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
- }
-
- return ret;
-}
-
-/**
- * onenand_sync - [MTD Interface] sync
- * @param mtd MTD device structure
- *
- * Sync is actually a wait for chip ready function
- */
-static void onenand_sync(struct mtd_info *mtd)
-{
- pr_debug("%s: called\n", __func__);
-
- /* Grab the lock and see if the device is available */
- onenand_get_device(mtd, FL_SYNCING);
-
- /* Release it and go back */
- onenand_release_device(mtd);
-}
-
-/**
- * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad
- * @param mtd MTD device structure
- * @param ofs offset relative to mtd start
- *
- * Check whether the block is bad
- */
-static int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs)
-{
- int ret;
-
- onenand_get_device(mtd, FL_READING);
- ret = onenand_block_isbad_nolock(mtd, ofs, 0);
- onenand_release_device(mtd);
- return ret;
-}
-
-/**
- * onenand_default_block_markbad - [DEFAULT] mark a block bad
- * @param mtd MTD device structure
- * @param ofs offset from device start
- *
- * This is the default implementation, which can be overridden by
- * a hardware specific driver.
- */
-static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
-{
- struct onenand_chip *this = mtd->priv;
- struct bbm_info *bbm = this->bbm;
- u_char buf[2] = {0, 0};
- struct mtd_oob_ops ops = {
- .mode = MTD_OPS_PLACE_OOB,
- .ooblen = 2,
- .oobbuf = buf,
- .ooboffs = 0,
- };
- int block;
-
- /* Get block number */
- block = onenand_block(this, ofs);
- if (bbm->bbt)
- bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
-
- /* We write two bytes, so we don't have to mess with 16-bit access */
- ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
- /* FIXME : What to do when marking SLC block in partition
- * with MLC erasesize? For now, it is not advisable to
- * create partitions containing both SLC and MLC regions.
- */
- return onenand_write_oob_nolock(mtd, ofs, &ops);
-}
-
-/**
- * onenand_block_markbad - [MTD Interface] Mark the block at the given offset as bad
- * @param mtd MTD device structure
- * @param ofs offset relative to mtd start
- *
- * Mark the block as bad
- */
-static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
-{
- struct onenand_chip *this = mtd->priv;
- int ret;
-
- ret = onenand_block_isbad(mtd, ofs);
- if (ret) {
- /* If it was bad already, return success and do nothing */
- if (ret > 0)
- return 0;
- return ret;
- }
-
- onenand_get_device(mtd, FL_WRITING);
- ret = this->block_markbad(mtd, ofs);
- onenand_release_device(mtd);
- return ret;
-}
-
-/**
- * onenand_do_lock_cmd - [OneNAND Interface] Lock or unlock block(s)
- * @param mtd MTD device structure
- * @param ofs offset relative to mtd start
- * @param len number of bytes to lock or unlock
- * @param cmd lock or unlock command
- *
- * Lock or unlock one or more blocks
- */
-static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int cmd)
-{
- struct onenand_chip *this = mtd->priv;
- int start, end, block, value, status;
- int wp_status_mask;
-
- start = onenand_block(this, ofs);
- end = onenand_block(this, ofs + len) - 1;
-
- if (cmd == ONENAND_CMD_LOCK)
- wp_status_mask = ONENAND_WP_LS;
- else
- wp_status_mask = ONENAND_WP_US;
-
- /* Continuous lock scheme */
- if (this->options & ONENAND_HAS_CONT_LOCK) {
- /* Set start block address */
- this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
- /* Set end block address */
- this->write_word(end, this->base + ONENAND_REG_END_BLOCK_ADDRESS);
- /* Write lock command */
- this->command(mtd, cmd, 0, 0);
-
- /* There's no return value */
- this->wait(mtd, FL_LOCKING);
-
- /* Sanity check */
- while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
- & ONENAND_CTRL_ONGO)
- continue;
-
- /* Check lock status */
- status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
- if (!(status & wp_status_mask))
- printk(KERN_ERR "%s: wp status = 0x%x\n",
- __func__, status);
-
- return 0;
- }
-
- /* Block lock scheme */
- for (block = start; block < end + 1; block++) {
- /* Set block address */
- value = onenand_block_address(this, block);
- this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
- /* Select DataRAM for DDP */
- value = onenand_bufferram_address(this, block);
- this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
- /* Set start block address */
- this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
- /* Write lock command */
- this->command(mtd, cmd, 0, 0);
-
- /* There's no return value */
- this->wait(mtd, FL_LOCKING);
-
- /* Sanity check */
- while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
- & ONENAND_CTRL_ONGO)
- continue;
-
- /* Check lock status */
- status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
- if (!(status & wp_status_mask))
- printk(KERN_ERR "%s: block = %d, wp status = 0x%x\n",
- __func__, block, status);
- }
-
- return 0;
-}
-
-/**
- * onenand_lock - [MTD Interface] Lock block(s)
- * @param mtd MTD device structure
- * @param ofs offset relative to mtd start
- * @param len number of bytes to unlock
- *
- * Lock one or more blocks
- */
-static int onenand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
-{
- int ret;
-
- onenand_get_device(mtd, FL_LOCKING);
- ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_LOCK);
- onenand_release_device(mtd);
- return ret;
-}
-
-/**
- * onenand_unlock - [MTD Interface] Unlock block(s)
- * @param mtd MTD device structure
- * @param ofs offset relative to mtd start
- * @param len number of bytes to unlock
- *
- * Unlock one or more blocks
- */
-static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
-{
- int ret;
-
- onenand_get_device(mtd, FL_LOCKING);
- ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK);
- onenand_release_device(mtd);
- return ret;
-}
-
-/**
- * onenand_check_lock_status - [OneNAND Interface] Check lock status
- * @param this onenand chip data structure
- *
- * Check lock status
- */
-static int onenand_check_lock_status(struct onenand_chip *this)
-{
- unsigned int value, block, status;
- unsigned int end;
-
- end = this->chipsize >> this->erase_shift;
- for (block = 0; block < end; block++) {
- /* Set block address */
- value = onenand_block_address(this, block);
- this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
- /* Select DataRAM for DDP */
- value = onenand_bufferram_address(this, block);
- this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
- /* Set start block address */
- this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
-
- /* Check lock status */
- status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
- if (!(status & ONENAND_WP_US)) {
- printk(KERN_ERR "%s: block = %d, wp status = 0x%x\n",
- __func__, block, status);
- return 0;
- }
- }
-
- return 1;
-}
-
-/**
- * onenand_unlock_all - [OneNAND Interface] unlock all blocks
- * @param mtd MTD device structure
- *
- * Unlock all blocks
- */
-static void onenand_unlock_all(struct mtd_info *mtd)
-{
- struct onenand_chip *this = mtd->priv;
- loff_t ofs = 0;
- loff_t len = mtd->size;
-
- if (this->options & ONENAND_HAS_UNLOCK_ALL) {
- /* Set start block address */
- this->write_word(0, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
- /* Write unlock command */
- this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0);
-
- /* There's no return value */
- this->wait(mtd, FL_LOCKING);
-
- /* Sanity check */
- while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
- & ONENAND_CTRL_ONGO)
- continue;
-
- /* Don't check lock status */
- if (this->options & ONENAND_SKIP_UNLOCK_CHECK)
- return;
-
- /* Check lock status */
- if (onenand_check_lock_status(this))
- return;
-
- /* Workaround for all block unlock in DDP */
- if (ONENAND_IS_DDP(this) && !FLEXONENAND(this)) {
- /* All blocks on another chip */
- ofs = this->chipsize >> 1;
- len = this->chipsize >> 1;
- }
- }
-
- onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK);
-}
-
-#ifdef CONFIG_MTD_ONENAND_OTP
-
-/**
- * onenand_otp_command - Send OTP specific command to OneNAND device
- * @param mtd MTD device structure
- * @param cmd the command to be sent
- * @param addr offset to read from or write to
- * @param len number of bytes to read or write
- */
-static int onenand_otp_command(struct mtd_info *mtd, int cmd, loff_t addr,
- size_t len)
-{
- struct onenand_chip *this = mtd->priv;
- int value, block, page;
-
- /* Address translation */
- switch (cmd) {
- case ONENAND_CMD_OTP_ACCESS:
- block = (int) (addr >> this->erase_shift);
- page = -1;
- break;
-
- default:
- block = (int) (addr >> this->erase_shift);
- page = (int) (addr >> this->page_shift);
-
- if (ONENAND_IS_2PLANE(this)) {
- /* Make the even block number */
- block &= ~1;
- /* Is it the odd plane? */
- if (addr & this->writesize)
- block++;
- page >>= 1;
- }
- page &= this->page_mask;
- break;
- }
-
- if (block != -1) {
- /* Write 'DFS, FBA' of Flash */
- value = onenand_block_address(this, block);
- this->write_word(value, this->base +
- ONENAND_REG_START_ADDRESS1);
- }
-
- if (page != -1) {
- /* Now we use page size operation */
- int sectors = 4, count = 4;
- int dataram;
-
- switch (cmd) {
- default:
- if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG)
- cmd = ONENAND_CMD_2X_PROG;
- dataram = ONENAND_CURRENT_BUFFERRAM(this);
- break;
- }
-
- /* Write 'FPA, FSA' of Flash */
- value = onenand_page_address(page, sectors);
- this->write_word(value, this->base +
- ONENAND_REG_START_ADDRESS8);
-
- /* Write 'BSA, BSC' of DataRAM */
- value = onenand_buffer_address(dataram, sectors, count);
- this->write_word(value, this->base + ONENAND_REG_START_BUFFER);
- }
-
- /* Interrupt clear */
- this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT);
-
- /* Write command */
- this->write_word(cmd, this->base + ONENAND_REG_COMMAND);
-
- return 0;
-}
-
-/**
- * onenand_otp_write_oob_nolock - [INTERN] OneNAND write out-of-band, specific to OTP
- * @param mtd MTD device structure
- * @param to offset to write to
- * @param len number of bytes to write
- * @param retlen pointer to variable to store the number of written bytes
- * @param buf the data to write
- *
- * OneNAND write out-of-band only for OTP
- */
-static int onenand_otp_write_oob_nolock(struct mtd_info *mtd, loff_t to,
- struct mtd_oob_ops *ops)
-{
- struct onenand_chip *this = mtd->priv;
- int column, ret = 0, oobsize;
- int written = 0;
- u_char *oobbuf;
- size_t len = ops->ooblen;
- const u_char *buf = ops->oobbuf;
- int block, value, status;
-
- to += ops->ooboffs;
-
- /* Initialize retlen, in case of early exit */
- ops->oobretlen = 0;
-
- oobsize = mtd->oobsize;
-
- column = to & (mtd->oobsize - 1);
-
- oobbuf = this->oob_buf;
-
- /* Loop until all data write */
- while (written < len) {
- int thislen = min_t(int, oobsize, len - written);
-
- cond_resched();
-
- block = (int) (to >> this->erase_shift);
- /*
- * Write 'DFS, FBA' of Flash
- * Add: F100h DQ=DFS, FBA
- */
-
- value = onenand_block_address(this, block);
- this->write_word(value, this->base +
- ONENAND_REG_START_ADDRESS1);
-
- /*
- * Select DataRAM for DDP
- * Add: F101h DQ=DBS
- */
-
- value = onenand_bufferram_address(this, block);
- this->write_word(value, this->base +
- ONENAND_REG_START_ADDRESS2);
- ONENAND_SET_NEXT_BUFFERRAM(this);
-
- /*
- * Enter OTP access mode
- */
- this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
- this->wait(mtd, FL_OTPING);
-
- /* We send data to spare ram with oobsize
- * to prevent byte access */
- memcpy(oobbuf + column, buf, thislen);
-
- /*
- * Write Data into DataRAM
- * Add: 8th Word
- * in sector0/spare/page0
- * DQ=XXFCh
- */
- this->write_bufferram(mtd, ONENAND_SPARERAM,
- oobbuf, 0, mtd->oobsize);
-
- onenand_otp_command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize);
- onenand_update_bufferram(mtd, to, 0);
- if (ONENAND_IS_2PLANE(this)) {
- ONENAND_SET_BUFFERRAM1(this);
- onenand_update_bufferram(mtd, to + this->writesize, 0);
- }
-
- ret = this->wait(mtd, FL_WRITING);
- if (ret) {
- printk(KERN_ERR "%s: write failed %d\n", __func__, ret);
- break;
- }
-
- /* Exit OTP access mode */
- this->command(mtd, ONENAND_CMD_RESET, 0, 0);
- this->wait(mtd, FL_RESETING);
-
- status = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
- status &= 0x60;
-
- if (status == 0x60) {
- printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
- printk(KERN_DEBUG "1st Block\tLOCKED\n");
- printk(KERN_DEBUG "OTP Block\tLOCKED\n");
- } else if (status == 0x20) {
- printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
- printk(KERN_DEBUG "1st Block\tLOCKED\n");
- printk(KERN_DEBUG "OTP Block\tUN-LOCKED\n");
- } else if (status == 0x40) {
- printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
- printk(KERN_DEBUG "1st Block\tUN-LOCKED\n");
- printk(KERN_DEBUG "OTP Block\tLOCKED\n");
- } else {
- printk(KERN_DEBUG "Reboot to check\n");
- }
-
- written += thislen;
- if (written == len)
- break;
-
- to += mtd->writesize;
- buf += thislen;
- column = 0;
- }
-
- ops->oobretlen = written;
-
- return ret;
-}
-
-/* Internal OTP operation */
-typedef int (*otp_op_t)(struct mtd_info *mtd, loff_t form, size_t len,
- size_t *retlen, u_char *buf);
-
-/**
- * do_otp_read - [DEFAULT] Read OTP block area
- * @param mtd MTD device structure
- * @param from The offset to read
- * @param len number of bytes to read
- * @param retlen pointer to variable to store the number of readbytes
- * @param buf the databuffer to put/get data
- *
- * Read OTP block area.
- */
-static int do_otp_read(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf)
-{
- struct onenand_chip *this = mtd->priv;
- struct mtd_oob_ops ops = {
- .len = len,
- .ooblen = 0,
- .datbuf = buf,
- .oobbuf = NULL,
- };
- int ret;
-
- /* Enter OTP access mode */
- this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
- this->wait(mtd, FL_OTPING);
-
- ret = ONENAND_IS_4KB_PAGE(this) ?
- onenand_mlc_read_ops_nolock(mtd, from, &ops) :
- onenand_read_ops_nolock(mtd, from, &ops);
-
- /* Exit OTP access mode */
- this->command(mtd, ONENAND_CMD_RESET, 0, 0);
- this->wait(mtd, FL_RESETING);
-
- return ret;
-}
-
-/**
- * do_otp_write - [DEFAULT] Write OTP block area
- * @param mtd MTD device structure
- * @param to The offset to write
- * @param len number of bytes to write
- * @param retlen pointer to variable to store the number of write bytes
- * @param buf the databuffer to put/get data
- *
- * Write OTP block area.
- */
-static int do_otp_write(struct mtd_info *mtd, loff_t to, size_t len,
- size_t *retlen, u_char *buf)
-{
- struct onenand_chip *this = mtd->priv;
- unsigned char *pbuf = buf;
- int ret;
- struct mtd_oob_ops ops;
-
- /* Force buffer page aligned */
- if (len < mtd->writesize) {
- memcpy(this->page_buf, buf, len);
- memset(this->page_buf + len, 0xff, mtd->writesize - len);
- pbuf = this->page_buf;
- len = mtd->writesize;
- }
-
- /* Enter OTP access mode */
- this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
- this->wait(mtd, FL_OTPING);
-
- ops.len = len;
- ops.ooblen = 0;
- ops.datbuf = pbuf;
- ops.oobbuf = NULL;
- ret = onenand_write_ops_nolock(mtd, to, &ops);
- *retlen = ops.retlen;
-
- /* Exit OTP access mode */
- this->command(mtd, ONENAND_CMD_RESET, 0, 0);
- this->wait(mtd, FL_RESETING);
-
- return ret;
-}
-
-/**
- * do_otp_lock - [DEFAULT] Lock OTP block area
- * @param mtd MTD device structure
- * @param from The offset to lock
- * @param len number of bytes to lock
- * @param retlen pointer to variable to store the number of lock bytes
- * @param buf the databuffer to put/get data
- *
- * Lock OTP block area.
- */
-static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf)
-{
- struct onenand_chip *this = mtd->priv;
- struct mtd_oob_ops ops;
- int ret;
-
- if (FLEXONENAND(this)) {
-
- /* Enter OTP access mode */
- this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
- this->wait(mtd, FL_OTPING);
- /*
- * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
- * main area of page 49.
- */
- ops.len = mtd->writesize;
- ops.ooblen = 0;
- ops.datbuf = buf;
- ops.oobbuf = NULL;
- ret = onenand_write_ops_nolock(mtd, mtd->writesize * 49, &ops);
- *retlen = ops.retlen;
-
- /* Exit OTP access mode */
- this->command(mtd, ONENAND_CMD_RESET, 0, 0);
- this->wait(mtd, FL_RESETING);
- } else {
- ops.mode = MTD_OPS_PLACE_OOB;
- ops.ooblen = len;
- ops.oobbuf = buf;
- ops.ooboffs = 0;
- ret = onenand_otp_write_oob_nolock(mtd, from, &ops);
- *retlen = ops.oobretlen;
- }
-
- return ret;
-}
-
-/**
- * onenand_otp_walk - [DEFAULT] Handle OTP operation
- * @param mtd MTD device structure
- * @param from The offset to read/write
- * @param len number of bytes to read/write
- * @param retlen pointer to variable to store the number of read bytes
- * @param buf the databuffer to put/get data
- * @param action do given action
- * @param mode specify user and factory
- *
- * Handle OTP operation.
- */
-static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf,
- otp_op_t action, int mode)
-{
- struct onenand_chip *this = mtd->priv;
- int otp_pages;
- int density;
- int ret = 0;
-
- *retlen = 0;
-
- density = onenand_get_density(this->device_id);
- if (density < ONENAND_DEVICE_DENSITY_512Mb)
- otp_pages = 20;
- else
- otp_pages = 50;
-
- if (mode == MTD_OTP_FACTORY) {
- from += mtd->writesize * otp_pages;
- otp_pages = ONENAND_PAGES_PER_BLOCK - otp_pages;
- }
-
- /* Check User/Factory boundary */
- if (mode == MTD_OTP_USER) {
- if (mtd->writesize * otp_pages < from + len)
- return 0;
- } else {
- if (mtd->writesize * otp_pages < len)
- return 0;
- }
-
- onenand_get_device(mtd, FL_OTPING);
- while (len > 0 && otp_pages > 0) {
- if (!action) { /* OTP Info functions */
- struct otp_info *otpinfo;
-
- len -= sizeof(struct otp_info);
- if (len <= 0) {
- ret = -ENOSPC;
- break;
- }
-
- otpinfo = (struct otp_info *) buf;
- otpinfo->start = from;
- otpinfo->length = mtd->writesize;
- otpinfo->locked = 0;
-
- from += mtd->writesize;
- buf += sizeof(struct otp_info);
- *retlen += sizeof(struct otp_info);
- } else {
- size_t tmp_retlen;
-
- ret = action(mtd, from, len, &tmp_retlen, buf);
- if (ret)
- break;
-
- buf += tmp_retlen;
- len -= tmp_retlen;
- *retlen += tmp_retlen;
-
- }
- otp_pages--;
- }
- onenand_release_device(mtd);
-
- return ret;
-}
-
-/**
- * onenand_get_fact_prot_info - [MTD Interface] Read factory OTP info
- * @param mtd MTD device structure
- * @param len number of bytes to read
- * @param retlen pointer to variable to store the number of read bytes
- * @param buf the databuffer to put/get data
- *
- * Read factory OTP info.
- */
-static int onenand_get_fact_prot_info(struct mtd_info *mtd, size_t len,
- size_t *retlen, struct otp_info *buf)
-{
- return onenand_otp_walk(mtd, 0, len, retlen, (u_char *) buf, NULL,
- MTD_OTP_FACTORY);
-}
-
-/**
- * onenand_read_fact_prot_reg - [MTD Interface] Read factory OTP area
- * @param mtd MTD device structure
- * @param from The offset to read
- * @param len number of bytes to read
- * @param retlen pointer to variable to store the number of read bytes
- * @param buf the databuffer to put/get data
- *
- * Read factory OTP area.
- */
-static int onenand_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
- size_t len, size_t *retlen, u_char *buf)
-{
- return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_read, MTD_OTP_FACTORY);
-}
-
-/**
- * onenand_get_user_prot_info - [MTD Interface] Read user OTP info
- * @param mtd MTD device structure
- * @param retlen pointer to variable to store the number of read bytes
- * @param len number of bytes to read
- * @param buf the databuffer to put/get data
- *
- * Read user OTP info.
- */
-static int onenand_get_user_prot_info(struct mtd_info *mtd, size_t len,
- size_t *retlen, struct otp_info *buf)
-{
- return onenand_otp_walk(mtd, 0, len, retlen, (u_char *) buf, NULL,
- MTD_OTP_USER);
-}
-
-/**
- * onenand_read_user_prot_reg - [MTD Interface] Read user OTP area
- * @param mtd MTD device structure
- * @param from The offset to read
- * @param len number of bytes to read
- * @param retlen pointer to variable to store the number of read bytes
- * @param buf the databuffer to put/get data
- *
- * Read user OTP area.
- */
-static int onenand_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
- size_t len, size_t *retlen, u_char *buf)
-{
- return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_read, MTD_OTP_USER);
-}
-
-/**
- * onenand_write_user_prot_reg - [MTD Interface] Write user OTP area
- * @param mtd MTD device structure
- * @param from The offset to write
- * @param len number of bytes to write
- * @param retlen pointer to variable to store the number of write bytes
- * @param buf the databuffer to put/get data
- *
- * Write user OTP area.
- */
-static int onenand_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
- size_t len, size_t *retlen, u_char *buf)
-{
- return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_write, MTD_OTP_USER);
-}
-
-/**
- * onenand_lock_user_prot_reg - [MTD Interface] Lock user OTP area
- * @param mtd MTD device structure
- * @param from The offset to lock
- * @param len number of bytes to unlock
- *
- * Write lock mark on spare area in page 0 in OTP block
- */
-static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
- size_t len)
-{
- struct onenand_chip *this = mtd->priv;
- u_char *buf = FLEXONENAND(this) ? this->page_buf : this->oob_buf;
- size_t retlen;
- int ret;
- unsigned int otp_lock_offset = ONENAND_OTP_LOCK_OFFSET;
-
- memset(buf, 0xff, FLEXONENAND(this) ? this->writesize
- : mtd->oobsize);
- /*
- * Write lock mark to 8th word of sector0 of page0 of the spare0.
- * We write 16 bytes spare area instead of 2 bytes.
- * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
- * main area of page 49.
- */
-
- from = 0;
- len = FLEXONENAND(this) ? mtd->writesize : 16;
-
- /*
- * Note: OTP lock operation
- * OTP block : 0xXXFC XX 1111 1100
- * 1st block : 0xXXF3 (If chip support) XX 1111 0011
- * Both : 0xXXF0 (If chip support) XX 1111 0000
- */
- if (FLEXONENAND(this))
- otp_lock_offset = FLEXONENAND_OTP_LOCK_OFFSET;
-
- /* ONENAND_OTP_AREA | ONENAND_OTP_BLOCK0 | ONENAND_OTP_AREA_BLOCK0 */
- if (otp == 1)
- buf[otp_lock_offset] = 0xFC;
- else if (otp == 2)
- buf[otp_lock_offset] = 0xF3;
- else if (otp == 3)
- buf[otp_lock_offset] = 0xF0;
- else if (otp != 0)
- printk(KERN_DEBUG "[OneNAND] Invalid option selected for OTP\n");
-
- ret = onenand_otp_walk(mtd, from, len, &retlen, buf, do_otp_lock, MTD_OTP_USER);
-
- return ret ? : retlen;
-}
-
-#endif /* CONFIG_MTD_ONENAND_OTP */
-
-/**
- * onenand_check_features - Check and set OneNAND features
- * @param mtd MTD data structure
- *
- * Check and set OneNAND features
- * - lock scheme
- * - two plane
- */
-static void onenand_check_features(struct mtd_info *mtd)
-{
- struct onenand_chip *this = mtd->priv;
- unsigned int density, process, numbufs;
-
- /* Lock scheme depends on density and process */
- density = onenand_get_density(this->device_id);
- process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT;
- numbufs = this->read_word(this->base + ONENAND_REG_NUM_BUFFERS) >> 8;
-
- /* Lock scheme */
- switch (density) {
- case ONENAND_DEVICE_DENSITY_4Gb:
- if (ONENAND_IS_DDP(this))
- this->options |= ONENAND_HAS_2PLANE;
- else if (numbufs == 1) {
- this->options |= ONENAND_HAS_4KB_PAGE;
- this->options |= ONENAND_HAS_CACHE_PROGRAM;
- /*
- * There are two different 4KiB pagesize chips
- * and no way to detect it by H/W config values.
- *
- * To detect the correct NOP for each chips,
- * It should check the version ID as workaround.
- *
- * Now it has as following
- * KFM4G16Q4M has NOP 4 with version ID 0x0131
- * KFM4G16Q5M has NOP 1 with versoin ID 0x013e
- */
- if ((this->version_id & 0xf) == 0xe)
- this->options |= ONENAND_HAS_NOP_1;
- }
-
- case ONENAND_DEVICE_DENSITY_2Gb:
- /* 2Gb DDP does not have 2 plane */
- if (!ONENAND_IS_DDP(this))
- this->options |= ONENAND_HAS_2PLANE;
- this->options |= ONENAND_HAS_UNLOCK_ALL;
-
- case ONENAND_DEVICE_DENSITY_1Gb:
- /* A-Die has all block unlock */
- if (process)
- this->options |= ONENAND_HAS_UNLOCK_ALL;
- break;
-
- default:
- /* Some OneNAND has continuous lock scheme */
- if (!process)
- this->options |= ONENAND_HAS_CONT_LOCK;
- break;
- }
-
- /* The MLC has 4KiB pagesize. */
- if (ONENAND_IS_MLC(this))
- this->options |= ONENAND_HAS_4KB_PAGE;
-
- if (ONENAND_IS_4KB_PAGE(this))
- this->options &= ~ONENAND_HAS_2PLANE;
-
- if (FLEXONENAND(this)) {
- this->options &= ~ONENAND_HAS_CONT_LOCK;
- this->options |= ONENAND_HAS_UNLOCK_ALL;
- }
-
- if (this->options & ONENAND_HAS_CONT_LOCK)
- printk(KERN_DEBUG "Lock scheme is Continuous Lock\n");
- if (this->options & ONENAND_HAS_UNLOCK_ALL)
- printk(KERN_DEBUG "Chip support all block unlock\n");
- if (this->options & ONENAND_HAS_2PLANE)
- printk(KERN_DEBUG "Chip has 2 plane\n");
- if (this->options & ONENAND_HAS_4KB_PAGE)
- printk(KERN_DEBUG "Chip has 4KiB pagesize\n");
- if (this->options & ONENAND_HAS_CACHE_PROGRAM)
- printk(KERN_DEBUG "Chip has cache program feature\n");
-}
-
-/**
- * onenand_print_device_info - Print device & version ID
- * @param device device ID
- * @param version version ID
- *
- * Print device & version ID
- */
-static void onenand_print_device_info(int device, int version)
-{
- int vcc, demuxed, ddp, density, flexonenand;
-
- vcc = device & ONENAND_DEVICE_VCC_MASK;
- demuxed = device & ONENAND_DEVICE_IS_DEMUX;
- ddp = device & ONENAND_DEVICE_IS_DDP;
- density = onenand_get_density(device);
- flexonenand = device & DEVICE_IS_FLEXONENAND;
- printk(KERN_INFO "%s%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n",
- demuxed ? "" : "Muxed ",
- flexonenand ? "Flex-" : "",
- ddp ? "(DDP)" : "",
- (16 << density),
- vcc ? "2.65/3.3" : "1.8",
- device);
- printk(KERN_INFO "OneNAND version = 0x%04x\n", version);
-}
-
-static const struct onenand_manufacturers onenand_manuf_ids[] = {
- {ONENAND_MFR_SAMSUNG, "Samsung"},
- {ONENAND_MFR_NUMONYX, "Numonyx"},
-};
-
-/**
- * onenand_check_maf - Check manufacturer ID
- * @param manuf manufacturer ID
- *
- * Check manufacturer ID
- */
-static int onenand_check_maf(int manuf)
-{
- int size = ARRAY_SIZE(onenand_manuf_ids);
- char *name;
- int i;
-
- for (i = 0; i < size; i++)
- if (manuf == onenand_manuf_ids[i].id)
- break;
-
- if (i < size)
- name = onenand_manuf_ids[i].name;
- else
- name = "Unknown";
-
- printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n", name, manuf);
-
- return (i == size);
-}
-
-/**
-* flexonenand_get_boundary - Reads the SLC boundary
-* @param onenand_info - onenand info structure
-**/
-static int flexonenand_get_boundary(struct mtd_info *mtd)
-{
- struct onenand_chip *this = mtd->priv;
- unsigned die, bdry;
- int syscfg, locked;
-
- /* Disable ECC */
- syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
- this->write_word((syscfg | 0x0100), this->base + ONENAND_REG_SYS_CFG1);
-
- for (die = 0; die < this->dies; die++) {
- this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0);
- this->wait(mtd, FL_SYNCING);
-
- this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0);
- this->wait(mtd, FL_READING);
-
- bdry = this->read_word(this->base + ONENAND_DATARAM);
- if ((bdry >> FLEXONENAND_PI_UNLOCK_SHIFT) == 3)
- locked = 0;
- else
- locked = 1;
- this->boundary[die] = bdry & FLEXONENAND_PI_MASK;
-
- this->command(mtd, ONENAND_CMD_RESET, 0, 0);
- this->wait(mtd, FL_RESETING);
-
- printk(KERN_INFO "Die %d boundary: %d%s\n", die,
- this->boundary[die], locked ? "(Locked)" : "(Unlocked)");
- }
-
- /* Enable ECC */
- this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);
- return 0;
-}
-
-/**
- * flexonenand_get_size - Fill up fields in onenand_chip and mtd_info
- * boundary[], diesize[], mtd->size, mtd->erasesize
- * @param mtd - MTD device structure
- */
-static void flexonenand_get_size(struct mtd_info *mtd)
-{
- struct onenand_chip *this = mtd->priv;
- int die, i, eraseshift, density;
- int blksperdie, maxbdry;
- loff_t ofs;
-
- density = onenand_get_density(this->device_id);
- blksperdie = ((loff_t)(16 << density) << 20) >> (this->erase_shift);
- blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0;
- maxbdry = blksperdie - 1;
- eraseshift = this->erase_shift - 1;
-
- mtd->numeraseregions = this->dies << 1;
-
- /* This fills up the device boundary */
- flexonenand_get_boundary(mtd);
- die = ofs = 0;
- i = -1;
- for (; die < this->dies; die++) {
- if (!die || this->boundary[die-1] != maxbdry) {
- i++;
- mtd->eraseregions[i].offset = ofs;
- mtd->eraseregions[i].erasesize = 1 << eraseshift;
- mtd->eraseregions[i].numblocks =
- this->boundary[die] + 1;
- ofs += mtd->eraseregions[i].numblocks << eraseshift;
- eraseshift++;
- } else {
- mtd->numeraseregions -= 1;
- mtd->eraseregions[i].numblocks +=
- this->boundary[die] + 1;
- ofs += (this->boundary[die] + 1) << (eraseshift - 1);
- }
- if (this->boundary[die] != maxbdry) {
- i++;
- mtd->eraseregions[i].offset = ofs;
- mtd->eraseregions[i].erasesize = 1 << eraseshift;
- mtd->eraseregions[i].numblocks = maxbdry ^
- this->boundary[die];
- ofs += mtd->eraseregions[i].numblocks << eraseshift;
- eraseshift--;
- } else
- mtd->numeraseregions -= 1;
- }
-
- /* Expose MLC erase size except when all blocks are SLC */
- mtd->erasesize = 1 << this->erase_shift;
- if (mtd->numeraseregions == 1)
- mtd->erasesize >>= 1;
-
- printk(KERN_INFO "Device has %d eraseregions\n", mtd->numeraseregions);
- for (i = 0; i < mtd->numeraseregions; i++)
- printk(KERN_INFO "[offset: 0x%08x, erasesize: 0x%05x,"
- " numblocks: %04u]\n",
- (unsigned int) mtd->eraseregions[i].offset,
- mtd->eraseregions[i].erasesize,
- mtd->eraseregions[i].numblocks);
-
- for (die = 0, mtd->size = 0; die < this->dies; die++) {
- this->diesize[die] = (loff_t)blksperdie << this->erase_shift;
- this->diesize[die] -= (loff_t)(this->boundary[die] + 1)
- << (this->erase_shift - 1);
- mtd->size += this->diesize[die];
- }
-}
-
-/**
- * flexonenand_check_blocks_erased - Check if blocks are erased
- * @param mtd_info - mtd info structure
- * @param start - first erase block to check
- * @param end - last erase block to check
- *
- * Converting an unerased block from MLC to SLC
- * causes byte values to change. Since both data and its ECC
- * have changed, reads on the block give uncorrectable error.
- * This might lead to the block being detected as bad.
- *
- * Avoid this by ensuring that the block to be converted is
- * erased.
- */
-static int flexonenand_check_blocks_erased(struct mtd_info *mtd, int start, int end)
-{
- struct onenand_chip *this = mtd->priv;
- int i, ret;
- int block;
- struct mtd_oob_ops ops = {
- .mode = MTD_OPS_PLACE_OOB,
- .ooboffs = 0,
- .ooblen = mtd->oobsize,
- .datbuf = NULL,
- .oobbuf = this->oob_buf,
- };
- loff_t addr;
-
- printk(KERN_DEBUG "Check blocks from %d to %d\n", start, end);
-
- for (block = start; block <= end; block++) {
- addr = flexonenand_addr(this, block);
- if (onenand_block_isbad_nolock(mtd, addr, 0))
- continue;
-
- /*
- * Since main area write results in ECC write to spare,
- * it is sufficient to check only ECC bytes for change.
- */
- ret = onenand_read_oob_nolock(mtd, addr, &ops);
- if (ret)
- return ret;
-
- for (i = 0; i < mtd->oobsize; i++)
- if (this->oob_buf[i] != 0xff)
- break;
-
- if (i != mtd->oobsize) {
- printk(KERN_WARNING "%s: Block %d not erased.\n",
- __func__, block);
- return 1;
- }
- }
-
- return 0;
-}
-
-/**
- * flexonenand_set_boundary - Writes the SLC boundary
- * @param mtd - mtd info structure
- */
-static int flexonenand_set_boundary(struct mtd_info *mtd, int die,
- int boundary, int lock)
-{
- struct onenand_chip *this = mtd->priv;
- int ret, density, blksperdie, old, new, thisboundary;
- loff_t addr;
-
- /* Change only once for SDP Flex-OneNAND */
- if (die && (!ONENAND_IS_DDP(this)))
- return 0;
-
- /* boundary value of -1 indicates no required change */
- if (boundary < 0 || boundary == this->boundary[die])
- return 0;
-
- density = onenand_get_density(this->device_id);
- blksperdie = ((16 << density) << 20) >> this->erase_shift;
- blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0;
-
- if (boundary >= blksperdie) {
- printk(KERN_ERR "%s: Invalid boundary value. "
- "Boundary not changed.\n", __func__);
- return -EINVAL;
- }
-
- /* Check if converting blocks are erased */
- old = this->boundary[die] + (die * this->density_mask);
- new = boundary + (die * this->density_mask);
- ret = flexonenand_check_blocks_erased(mtd, min(old, new) + 1, max(old, new));
- if (ret) {
- printk(KERN_ERR "%s: Please erase blocks "
- "before boundary change\n", __func__);
- return ret;
- }
-
- this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0);
- this->wait(mtd, FL_SYNCING);
-
- /* Check is boundary is locked */
- this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0);
- this->wait(mtd, FL_READING);
-
- thisboundary = this->read_word(this->base + ONENAND_DATARAM);
- if ((thisboundary >> FLEXONENAND_PI_UNLOCK_SHIFT) != 3) {
- printk(KERN_ERR "%s: boundary locked\n", __func__);
- ret = 1;
- goto out;
- }
-
- printk(KERN_INFO "Changing die %d boundary: %d%s\n",
- die, boundary, lock ? "(Locked)" : "(Unlocked)");
-
- addr = die ? this->diesize[0] : 0;
-
- boundary &= FLEXONENAND_PI_MASK;
- boundary |= lock ? 0 : (3 << FLEXONENAND_PI_UNLOCK_SHIFT);
-
- this->command(mtd, ONENAND_CMD_ERASE, addr, 0);
- ret = this->wait(mtd, FL_ERASING);
- if (ret) {
- printk(KERN_ERR "%s: Failed PI erase for Die %d\n",
- __func__, die);
- goto out;
- }
-
- this->write_word(boundary, this->base + ONENAND_DATARAM);
- this->command(mtd, ONENAND_CMD_PROG, addr, 0);
- ret = this->wait(mtd, FL_WRITING);
- if (ret) {
- printk(KERN_ERR "%s: Failed PI write for Die %d\n",
- __func__, die);
- goto out;
- }
-
- this->command(mtd, FLEXONENAND_CMD_PI_UPDATE, die, 0);
- ret = this->wait(mtd, FL_WRITING);
-out:
- this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_REG_COMMAND);
- this->wait(mtd, FL_RESETING);
- if (!ret)
- /* Recalculate device size on boundary change*/
- flexonenand_get_size(mtd);
-
- return ret;
-}
-
-/**
- * onenand_chip_probe - [OneNAND Interface] The generic chip probe
- * @param mtd MTD device structure
- *
- * OneNAND detection method:
- * Compare the values from command with ones from register
- */
-static int onenand_chip_probe(struct mtd_info *mtd)
-{
- struct onenand_chip *this = mtd->priv;
- int bram_maf_id, bram_dev_id, maf_id, dev_id;
- int syscfg;
-
- /* Save system configuration 1 */
- syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
- /* Clear Sync. Burst Read mode to read BootRAM */
- this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ & ~ONENAND_SYS_CFG1_SYNC_WRITE), this->base + ONENAND_REG_SYS_CFG1);
-
- /* Send the command for reading device ID from BootRAM */
- this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM);
-
- /* Read manufacturer and device IDs from BootRAM */
- bram_maf_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x0);
- bram_dev_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x2);
-
- /* Reset OneNAND to read default register values */
- this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_BOOTRAM);
- /* Wait reset */
- this->wait(mtd, FL_RESETING);
-
- /* Restore system configuration 1 */
- this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);
-
- /* Check manufacturer ID */
- if (onenand_check_maf(bram_maf_id))
- return -ENXIO;
-
- /* Read manufacturer and device IDs from Register */
- maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID);
- dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID);
-
- /* Check OneNAND device */
- if (maf_id != bram_maf_id || dev_id != bram_dev_id)
- return -ENXIO;
-
- return 0;
-}
-
-/**
- * onenand_probe - [OneNAND Interface] Probe the OneNAND device
- * @param mtd MTD device structure
- */
-static int onenand_probe(struct mtd_info *mtd)
-{
- struct onenand_chip *this = mtd->priv;
- int dev_id, ver_id;
- int density;
- int ret;
-
- ret = this->chip_probe(mtd);
- if (ret)
- return ret;
-
- /* Device and version IDs from Register */
- dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID);
- ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID);
- this->technology = this->read_word(this->base + ONENAND_REG_TECHNOLOGY);
-
- /* Flash device information */
- onenand_print_device_info(dev_id, ver_id);
- this->device_id = dev_id;
- this->version_id = ver_id;
-
- /* Check OneNAND features */
- onenand_check_features(mtd);
-
- density = onenand_get_density(dev_id);
- if (FLEXONENAND(this)) {
- this->dies = ONENAND_IS_DDP(this) ? 2 : 1;
- /* Maximum possible erase regions */
- mtd->numeraseregions = this->dies << 1;
- mtd->eraseregions = kzalloc(sizeof(struct mtd_erase_region_info)
- * (this->dies << 1), GFP_KERNEL);
- if (!mtd->eraseregions)
- return -ENOMEM;
- }
-
- /*
- * For Flex-OneNAND, chipsize represents maximum possible device size.
- * mtd->size represents the actual device size.
- */
- this->chipsize = (16 << density) << 20;
-
- /* OneNAND page size & block size */
- /* The data buffer size is equal to page size */
- mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE);
- /* We use the full BufferRAM */
- if (ONENAND_IS_4KB_PAGE(this))
- mtd->writesize <<= 1;
-
- mtd->oobsize = mtd->writesize >> 5;
- /* Pages per a block are always 64 in OneNAND */
- mtd->erasesize = mtd->writesize << 6;
- /*
- * Flex-OneNAND SLC area has 64 pages per block.
- * Flex-OneNAND MLC area has 128 pages per block.
- * Expose MLC erase size to find erase_shift and page_mask.
- */
- if (FLEXONENAND(this))
- mtd->erasesize <<= 1;
-
- this->erase_shift = ffs(mtd->erasesize) - 1;
- this->page_shift = ffs(mtd->writesize) - 1;
- this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1;
- /* Set density mask. it is used for DDP */
- if (ONENAND_IS_DDP(this))
- this->density_mask = this->chipsize >> (this->erase_shift + 1);
- /* It's real page size */
- this->writesize = mtd->writesize;
-
- /* REVISIT: Multichip handling */
-
- if (FLEXONENAND(this))
- flexonenand_get_size(mtd);
- else
- mtd->size = this->chipsize;
-
- /*
- * We emulate the 4KiB page and 256KiB erase block size
- * But oobsize is still 64 bytes.
- * It is only valid if you turn on 2X program support,
- * Otherwise it will be ignored by compiler.
- */
- if (ONENAND_IS_2PLANE(this)) {
- mtd->writesize <<= 1;
- mtd->erasesize <<= 1;
- }
-
- return 0;
-}
-
-/**
- * onenand_suspend - [MTD Interface] Suspend the OneNAND flash
- * @param mtd MTD device structure
- */
-static int onenand_suspend(struct mtd_info *mtd)
-{
- return onenand_get_device(mtd, FL_PM_SUSPENDED);
-}
-
-/**
- * onenand_resume - [MTD Interface] Resume the OneNAND flash
- * @param mtd MTD device structure
- */
-static void onenand_resume(struct mtd_info *mtd)
-{
- struct onenand_chip *this = mtd->priv;
-
- if (this->state == FL_PM_SUSPENDED)
- onenand_release_device(mtd);
- else
- printk(KERN_ERR "%s: resume() called for the chip which is not "
- "in suspended state\n", __func__);
-}
-
-/**
- * onenand_scan - [OneNAND Interface] Scan for the OneNAND device
- * @param mtd MTD device structure
- * @param maxchips Number of chips to scan for
- *
- * This fills out all the not initialized function pointers
- * with the defaults.
- * The flash ID is read and the mtd/chip structures are
- * filled with the appropriate values.
- */
-int onenand_scan(struct mtd_info *mtd, int maxchips)
-{
- int i, ret;
- struct onenand_chip *this = mtd->priv;
-
- if (!this->read_word)
- this->read_word = onenand_readw;
- if (!this->write_word)
- this->write_word = onenand_writew;
-
- if (!this->command)
- this->command = onenand_command;
- if (!this->wait)
- onenand_setup_wait(mtd);
- if (!this->bbt_wait)
- this->bbt_wait = onenand_bbt_wait;
- if (!this->unlock_all)
- this->unlock_all = onenand_unlock_all;
-
- if (!this->chip_probe)
- this->chip_probe = onenand_chip_probe;
-
- if (!this->read_bufferram)
- this->read_bufferram = onenand_read_bufferram;
- if (!this->write_bufferram)
- this->write_bufferram = onenand_write_bufferram;
-
- if (!this->block_markbad)
- this->block_markbad = onenand_default_block_markbad;
- if (!this->scan_bbt)
- this->scan_bbt = onenand_default_bbt;
-
- if (onenand_probe(mtd))
- return -ENXIO;
-
- /* Set Sync. Burst Read after probing */
- if (this->mmcontrol) {
- printk(KERN_INFO "OneNAND Sync. Burst Read support\n");
- this->read_bufferram = onenand_sync_read_bufferram;
- }
-
- /* Allocate buffers, if necessary */
- if (!this->page_buf) {
- this->page_buf = kzalloc(mtd->writesize, GFP_KERNEL);
- if (!this->page_buf)
- return -ENOMEM;
-#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
- this->verify_buf = kzalloc(mtd->writesize, GFP_KERNEL);
- if (!this->verify_buf) {
- kfree(this->page_buf);
- return -ENOMEM;
- }
-#endif
- this->options |= ONENAND_PAGEBUF_ALLOC;
- }
- if (!this->oob_buf) {
- this->oob_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
- if (!this->oob_buf) {
- if (this->options & ONENAND_PAGEBUF_ALLOC) {
- this->options &= ~ONENAND_PAGEBUF_ALLOC;
- kfree(this->page_buf);
- }
- return -ENOMEM;
- }
- this->options |= ONENAND_OOBBUF_ALLOC;
- }
-
- this->state = FL_READY;
- init_waitqueue_head(&this->wq);
- spin_lock_init(&this->chip_lock);
-
- /*
- * Allow subpage writes up to oobsize.
- */
- switch (mtd->oobsize) {
- case 128:
- if (FLEXONENAND(this)) {
- mtd_set_ooblayout(mtd, &flexonenand_ooblayout_ops);
- mtd->subpage_sft = 0;
- } else {
- mtd_set_ooblayout(mtd, &onenand_oob_128_ooblayout_ops);
- mtd->subpage_sft = 2;
- }
- if (ONENAND_IS_NOP_1(this))
- mtd->subpage_sft = 0;
- break;
- case 64:
- mtd_set_ooblayout(mtd, &onenand_oob_32_64_ooblayout_ops);
- mtd->subpage_sft = 2;
- break;
-
- case 32:
- mtd_set_ooblayout(mtd, &onenand_oob_32_64_ooblayout_ops);
- mtd->subpage_sft = 1;
- break;
-
- default:
- printk(KERN_WARNING "%s: No OOB scheme defined for oobsize %d\n",
- __func__, mtd->oobsize);
- mtd->subpage_sft = 0;
- /* To prevent kernel oops */
- mtd_set_ooblayout(mtd, &onenand_oob_32_64_ooblayout_ops);
- break;
- }
-
- this->subpagesize = mtd->writesize >> mtd->subpage_sft;
-
- /*
- * The number of bytes available for a client to place data into
- * the out of band area
- */
- ret = mtd_ooblayout_count_freebytes(mtd);
- if (ret < 0)
- ret = 0;
-
- mtd->oobavail = ret;
-
- mtd->ecc_strength = 1;
-
- /* Fill in remaining MTD driver data */
- mtd->type = ONENAND_IS_MLC(this) ? MTD_MLCNANDFLASH : MTD_NANDFLASH;
- mtd->flags = MTD_CAP_NANDFLASH;
- mtd->_erase = onenand_erase;
- mtd->_point = NULL;
- mtd->_unpoint = NULL;
- mtd->_read_oob = onenand_read_oob;
- mtd->_write_oob = onenand_write_oob;
- mtd->_panic_write = onenand_panic_write;
-#ifdef CONFIG_MTD_ONENAND_OTP
- mtd->_get_fact_prot_info = onenand_get_fact_prot_info;
- mtd->_read_fact_prot_reg = onenand_read_fact_prot_reg;
- mtd->_get_user_prot_info = onenand_get_user_prot_info;
- mtd->_read_user_prot_reg = onenand_read_user_prot_reg;
- mtd->_write_user_prot_reg = onenand_write_user_prot_reg;
- mtd->_lock_user_prot_reg = onenand_lock_user_prot_reg;
-#endif
- mtd->_sync = onenand_sync;
- mtd->_lock = onenand_lock;
- mtd->_unlock = onenand_unlock;
- mtd->_suspend = onenand_suspend;
- mtd->_resume = onenand_resume;
- mtd->_block_isbad = onenand_block_isbad;
- mtd->_block_markbad = onenand_block_markbad;
- mtd->owner = THIS_MODULE;
- mtd->writebufsize = mtd->writesize;
-
- /* Unlock whole block */
- if (!(this->options & ONENAND_SKIP_INITIAL_UNLOCKING))
- this->unlock_all(mtd);
-
- ret = this->scan_bbt(mtd);
- if ((!FLEXONENAND(this)) || ret)
- return ret;
-
- /* Change Flex-OneNAND boundaries if required */
- for (i = 0; i < MAX_DIES; i++)
- flexonenand_set_boundary(mtd, i, flex_bdry[2 * i],
- flex_bdry[(2 * i) + 1]);
-
- return 0;
-}
-
-/**
- * onenand_release - [OneNAND Interface] Free resources held by the OneNAND device
- * @param mtd MTD device structure
- */
-void onenand_release(struct mtd_info *mtd)
-{
- struct onenand_chip *this = mtd->priv;
-
- /* Deregister partitions */
- mtd_device_unregister(mtd);
-
- /* Free bad block table memory, if allocated */
- if (this->bbm) {
- struct bbm_info *bbm = this->bbm;
- kfree(bbm->bbt);
- kfree(this->bbm);
- }
- /* Buffers allocated by onenand_scan */
- if (this->options & ONENAND_PAGEBUF_ALLOC) {
- kfree(this->page_buf);
-#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
- kfree(this->verify_buf);
-#endif
- }
- if (this->options & ONENAND_OOBBUF_ALLOC)
- kfree(this->oob_buf);
- kfree(mtd->eraseregions);
-}
-
-EXPORT_SYMBOL_GPL(onenand_scan);
-EXPORT_SYMBOL_GPL(onenand_release);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>");
-MODULE_DESCRIPTION("Generic OneNAND flash driver code");
diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c
deleted file mode 100644
index 420260c25ca0..000000000000
--- a/drivers/mtd/onenand/onenand_bbt.c
+++ /dev/null
@@ -1,250 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * linux/drivers/mtd/onenand/onenand_bbt.c
- *
- * Bad Block Table support for the OneNAND driver
- *
- * Copyright(c) 2005 Samsung Electronics
- * Kyungmin Park <kyungmin.park@samsung.com>
- *
- * Derived from nand_bbt.c
- *
- * TODO:
- * Split BBT core and chip specific BBT.
- */
-
-#include <linux/slab.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/onenand.h>
-#include <linux/export.h>
-
-/**
- * check_short_pattern - [GENERIC] check if a pattern is in the buffer
- * @param buf the buffer to search
- * @param len the length of buffer to search
- * @param paglen the pagelength
- * @param td search pattern descriptor
- *
- * Check for a pattern at the given place. Used to search bad block
- * tables and good / bad block identifiers. Same as check_pattern, but
- * no optional empty check and the pattern is expected to start
- * at offset 0.
- *
- */
-static int check_short_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
-{
- int i;
- uint8_t *p = buf;
-
- /* Compare the pattern */
- for (i = 0; i < td->len; i++) {
- if (p[i] != td->pattern[i])
- return -1;
- }
- return 0;
-}
-
-/**
- * create_bbt - [GENERIC] Create a bad block table by scanning the device
- * @param mtd MTD device structure
- * @param buf temporary buffer
- * @param bd descriptor for the good/bad block search pattern
- * @param chip create the table for a specific chip, -1 read all chips.
- * Applies only if NAND_BBT_PERCHIP option is set
- *
- * Create a bad block table by scanning the device
- * for the given good/bad block identify pattern
- */
-static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip)
-{
- struct onenand_chip *this = mtd->priv;
- struct bbm_info *bbm = this->bbm;
- int i, j, numblocks, len, scanlen;
- int startblock;
- loff_t from;
- size_t readlen, ooblen;
- struct mtd_oob_ops ops;
- int rgn;
-
- printk(KERN_INFO "Scanning device for bad blocks\n");
-
- len = 2;
-
- /* We need only read few bytes from the OOB area */
- scanlen = ooblen = 0;
- readlen = bd->len;
-
- /* chip == -1 case only */
- /* Note that numblocks is 2 * (real numblocks) here;
- * see i += 2 below as it makses shifting and masking less painful
- */
- numblocks = this->chipsize >> (bbm->bbt_erase_shift - 1);
- startblock = 0;
- from = 0;
-
- ops.mode = MTD_OPS_PLACE_OOB;
- ops.ooblen = readlen;
- ops.oobbuf = buf;
- ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
-
- for (i = startblock; i < numblocks; ) {
- int ret;
-
- for (j = 0; j < len; j++) {
- /* No need to read pages fully,
- * just read required OOB bytes */
- ret = onenand_bbt_read_oob(mtd,
- from + j * this->writesize + bd->offs, &ops);
-
- /* If it is a initial bad block, just ignore it */
- if (ret == ONENAND_BBT_READ_FATAL_ERROR)
- return -EIO;
-
- if (ret || check_short_pattern(&buf[j * scanlen],
- scanlen, this->writesize, bd)) {
- bbm->bbt[i >> 3] |= 0x03 << (i & 0x6);
- printk(KERN_INFO "OneNAND eraseblock %d is an "
- "initial bad block\n", i >> 1);
- mtd->ecc_stats.badblocks++;
- break;
- }
- }
- i += 2;
-
- if (FLEXONENAND(this)) {
- rgn = flexonenand_region(mtd, from);
- from += mtd->eraseregions[rgn].erasesize;
- } else
- from += (1 << bbm->bbt_erase_shift);
- }
-
- return 0;
-}
-
-
-/**
- * onenand_memory_bbt - [GENERIC] create a memory based bad block table
- * @param mtd MTD device structure
- * @param bd descriptor for the good/bad block search pattern
- *
- * The function creates a memory based bbt by scanning the device
- * for manufacturer / software marked good / bad blocks
- */
-static inline int onenand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
-{
- struct onenand_chip *this = mtd->priv;
-
- return create_bbt(mtd, this->page_buf, bd, -1);
-}
-
-/**
- * onenand_isbad_bbt - [OneNAND Interface] Check if a block is bad
- * @param mtd MTD device structure
- * @param offs offset in the device
- * @param allowbbt allow access to bad block table region
- */
-static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
-{
- struct onenand_chip *this = mtd->priv;
- struct bbm_info *bbm = this->bbm;
- int block;
- uint8_t res;
-
- /* Get block number * 2 */
- block = (int) (onenand_block(this, offs) << 1);
- res = (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03;
-
- pr_debug("onenand_isbad_bbt: bbt info for offs 0x%08x: (block %d) 0x%02x\n",
- (unsigned int) offs, block >> 1, res);
-
- switch ((int) res) {
- case 0x00: return 0;
- case 0x01: return 1;
- case 0x02: return allowbbt ? 0 : 1;
- }
-
- return 1;
-}
-
-/**
- * onenand_scan_bbt - [OneNAND Interface] scan, find, read and maybe create bad block table(s)
- * @param mtd MTD device structure
- * @param bd descriptor for the good/bad block search pattern
- *
- * The function checks, if a bad block table(s) is/are already
- * available. If not it scans the device for manufacturer
- * marked good / bad blocks and writes the bad block table(s) to
- * the selected place.
- *
- * The bad block table memory is allocated here. It is freed
- * by the onenand_release function.
- *
- */
-static int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
-{
- struct onenand_chip *this = mtd->priv;
- struct bbm_info *bbm = this->bbm;
- int len, ret = 0;
-
- len = this->chipsize >> (this->erase_shift + 2);
- /* Allocate memory (2bit per block) and clear the memory bad block table */
- bbm->bbt = kzalloc(len, GFP_KERNEL);
- if (!bbm->bbt)
- return -ENOMEM;
-
- /* Set the bad block position */
- bbm->badblockpos = ONENAND_BADBLOCK_POS;
-
- /* Set erase shift */
- bbm->bbt_erase_shift = this->erase_shift;
-
- if (!bbm->isbad_bbt)
- bbm->isbad_bbt = onenand_isbad_bbt;
-
- /* Scan the device to build a memory based bad block table */
- if ((ret = onenand_memory_bbt(mtd, bd))) {
- printk(KERN_ERR "onenand_scan_bbt: Can't scan flash and build the RAM-based BBT\n");
- kfree(bbm->bbt);
- bbm->bbt = NULL;
- }
-
- return ret;
-}
-
-/*
- * Define some generic bad / good block scan pattern which are used
- * while scanning a device for factory marked good / bad blocks.
- */
-static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
-
-static struct nand_bbt_descr largepage_memorybased = {
- .options = 0,
- .offs = 0,
- .len = 2,
- .pattern = scan_ff_pattern,
-};
-
-/**
- * onenand_default_bbt - [OneNAND Interface] Select a default bad block table for the device
- * @param mtd MTD device structure
- *
- * This function selects the default bad block table
- * support for the device and calls the onenand_scan_bbt function
- */
-int onenand_default_bbt(struct mtd_info *mtd)
-{
- struct onenand_chip *this = mtd->priv;
- struct bbm_info *bbm;
-
- this->bbm = kzalloc(sizeof(struct bbm_info), GFP_KERNEL);
- if (!this->bbm)
- return -ENOMEM;
-
- bbm = this->bbm;
-
- /* 1KB page has same configuration as 2KB page */
- if (!bbm->badblock_pattern)
- bbm->badblock_pattern = &largepage_memorybased;
-
- return onenand_scan_bbt(mtd, bbm->badblock_pattern);
-}
diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c
index d1cbf26db2c0..df27f24ce0fa 100644
--- a/drivers/mtd/rfd_ftl.c
+++ b/drivers/mtd/rfd_ftl.c
@@ -266,91 +266,54 @@ static int rfd_ftl_readsect(struct mtd_blktrans_dev *dev, u_long sector, char *b
return 0;
}
-static void erase_callback(struct erase_info *erase)
-{
- struct partition *part;
- u16 magic;
- int i, rc;
- size_t retlen;
-
- part = (struct partition*)erase->priv;
-
- i = (u32)erase->addr / part->block_size;
- if (i >= part->total_blocks || part->blocks[i].offset != erase->addr ||
- erase->addr > UINT_MAX) {
- printk(KERN_ERR PREFIX "erase callback for unknown offset %llx "
- "on '%s'\n", (unsigned long long)erase->addr, part->mbd.mtd->name);
- return;
- }
-
- if (erase->state != MTD_ERASE_DONE) {
- printk(KERN_WARNING PREFIX "erase failed at 0x%llx on '%s', "
- "state %d\n", (unsigned long long)erase->addr,
- part->mbd.mtd->name, erase->state);
-
- part->blocks[i].state = BLOCK_FAILED;
- part->blocks[i].free_sectors = 0;
- part->blocks[i].used_sectors = 0;
-
- kfree(erase);
-
- return;
- }
-
- magic = cpu_to_le16(RFD_MAGIC);
-
- part->blocks[i].state = BLOCK_ERASED;
- part->blocks[i].free_sectors = part->data_sectors_per_block;
- part->blocks[i].used_sectors = 0;
- part->blocks[i].erases++;
-
- rc = mtd_write(part->mbd.mtd, part->blocks[i].offset, sizeof(magic),
- &retlen, (u_char *)&magic);
-
- if (!rc && retlen != sizeof(magic))
- rc = -EIO;
-
- if (rc) {
- printk(KERN_ERR PREFIX "'%s': unable to write RFD "
- "header at 0x%lx\n",
- part->mbd.mtd->name,
- part->blocks[i].offset);
- part->blocks[i].state = BLOCK_FAILED;
- }
- else
- part->blocks[i].state = BLOCK_OK;
-
- kfree(erase);
-}
-
static int erase_block(struct partition *part, int block)
{
struct erase_info *erase;
- int rc = -ENOMEM;
+ int rc;
erase = kmalloc(sizeof(struct erase_info), GFP_KERNEL);
if (!erase)
- goto err;
+ return -ENOMEM;
- erase->mtd = part->mbd.mtd;
- erase->callback = erase_callback;
erase->addr = part->blocks[block].offset;
erase->len = part->block_size;
- erase->priv = (u_long)part;
part->blocks[block].state = BLOCK_ERASING;
part->blocks[block].free_sectors = 0;
rc = mtd_erase(part->mbd.mtd, erase);
-
if (rc) {
printk(KERN_ERR PREFIX "erase of region %llx,%llx on '%s' "
"failed\n", (unsigned long long)erase->addr,
(unsigned long long)erase->len, part->mbd.mtd->name);
- kfree(erase);
+ part->blocks[block].state = BLOCK_FAILED;
+ part->blocks[block].free_sectors = 0;
+ part->blocks[block].used_sectors = 0;
+ } else {
+ u16 magic = cpu_to_le16(RFD_MAGIC);
+ size_t retlen;
+
+ part->blocks[block].state = BLOCK_ERASED;
+ part->blocks[block].free_sectors = part->data_sectors_per_block;
+ part->blocks[block].used_sectors = 0;
+ part->blocks[block].erases++;
+
+ rc = mtd_write(part->mbd.mtd, part->blocks[block].offset,
+ sizeof(magic), &retlen, (u_char *)&magic);
+ if (!rc && retlen != sizeof(magic))
+ rc = -EIO;
+
+ if (rc) {
+ pr_err(PREFIX "'%s': unable to write RFD header at 0x%lx\n",
+ part->mbd.mtd->name, part->blocks[block].offset);
+ part->blocks[block].state = BLOCK_FAILED;
+ } else {
+ part->blocks[block].state = BLOCK_OK;
+ }
}
-err:
+ kfree(erase);
+
return rc;
}
diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c
index 4237c7cebf02..79636349df96 100644
--- a/drivers/mtd/sm_ftl.c
+++ b/drivers/mtd/sm_ftl.c
@@ -17,7 +17,7 @@
#include <linux/bitops.h>
#include <linux/slab.h>
#include <linux/mtd/nand_ecc.h>
-#include "nand/sm_common.h"
+#include "nand/raw/sm_common.h"
#include "sm_ftl.h"
@@ -460,11 +460,8 @@ static int sm_erase_block(struct sm_ftl *ftl, int zone_num, uint16_t block,
struct mtd_info *mtd = ftl->trans->mtd;
struct erase_info erase;
- erase.mtd = mtd;
- erase.callback = sm_erase_callback;
erase.addr = sm_mkoffset(ftl, zone_num, block, 0);
erase.len = ftl->block_size;
- erase.priv = (u_long)ftl;
if (ftl->unstable)
return -EIO;
@@ -482,15 +479,6 @@ static int sm_erase_block(struct sm_ftl *ftl, int zone_num, uint16_t block,
goto error;
}
- if (erase.state == MTD_ERASE_PENDING)
- wait_for_completion(&ftl->erase_completion);
-
- if (erase.state != MTD_ERASE_DONE) {
- sm_printk("erase of block %d in zone %d failed after wait",
- block, zone_num);
- goto error;
- }
-
if (put_free)
kfifo_in(&zone->free_sectors,
(const unsigned char *)&block, sizeof(block));
@@ -501,12 +489,6 @@ error:
return -EIO;
}
-static void sm_erase_callback(struct erase_info *self)
-{
- struct sm_ftl *ftl = (struct sm_ftl *)self->priv;
- complete(&ftl->erase_completion);
-}
-
/* Thoroughly test that block is valid. */
static int sm_check_block(struct sm_ftl *ftl, int zone, int block)
{
@@ -1141,7 +1123,6 @@ static void sm_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
mutex_init(&ftl->mutex);
timer_setup(&ftl->timer, sm_cache_flush_timer, 0);
INIT_WORK(&ftl->flush_work, sm_cache_flush_work);
- init_completion(&ftl->erase_completion);
/* Read media information */
if (sm_get_media_info(ftl, mtd)) {
diff --git a/drivers/mtd/sm_ftl.h b/drivers/mtd/sm_ftl.h
index 43bb7300785b..0a46d75cdc6a 100644
--- a/drivers/mtd/sm_ftl.h
+++ b/drivers/mtd/sm_ftl.h
@@ -53,9 +53,6 @@ struct sm_ftl {
struct work_struct flush_work;
struct timer_list timer;
- /* Async erase stuff */
- struct completion erase_completion;
-
/* Geometry stuff */
int heads;
int sectors;
@@ -86,7 +83,6 @@ struct chs_entry {
printk(KERN_DEBUG "sm_ftl" ": " format "\n", ## __VA_ARGS__)
-static void sm_erase_callback(struct erase_info *self);
static int sm_erase_block(struct sm_ftl *ftl, int zone_num, uint16_t block,
int put_free);
static void sm_mark_block_bad(struct sm_ftl *ftl, int zone_num, int block);
diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c
index 4b8e9183489a..5872f31eaa60 100644
--- a/drivers/mtd/spi-nor/cadence-quadspi.c
+++ b/drivers/mtd/spi-nor/cadence-quadspi.c
@@ -501,7 +501,9 @@ static int cqspi_indirect_read_execute(struct spi_nor *nor, u8 *rxbuf,
void __iomem *reg_base = cqspi->iobase;
void __iomem *ahb_base = cqspi->ahb_base;
unsigned int remaining = n_rx;
+ unsigned int mod_bytes = n_rx % 4;
unsigned int bytes_to_read = 0;
+ u8 *rxbuf_end = rxbuf + n_rx;
int ret = 0;
writel(from_addr, reg_base + CQSPI_REG_INDIRECTRDSTARTADDR);
@@ -530,11 +532,24 @@ static int cqspi_indirect_read_execute(struct spi_nor *nor, u8 *rxbuf,
}
while (bytes_to_read != 0) {
+ unsigned int word_remain = round_down(remaining, 4);
+
bytes_to_read *= cqspi->fifo_width;
bytes_to_read = bytes_to_read > remaining ?
remaining : bytes_to_read;
- ioread32_rep(ahb_base, rxbuf,
- DIV_ROUND_UP(bytes_to_read, 4));
+ bytes_to_read = round_down(bytes_to_read, 4);
+ /* Read 4 byte word chunks then single bytes */
+ if (bytes_to_read) {
+ ioread32_rep(ahb_base, rxbuf,
+ (bytes_to_read / 4));
+ } else if (!word_remain && mod_bytes) {
+ unsigned int temp = ioread32(ahb_base);
+
+ bytes_to_read = mod_bytes;
+ memcpy(rxbuf, &temp, min((unsigned int)
+ (rxbuf_end - rxbuf),
+ bytes_to_read));
+ }
rxbuf += bytes_to_read;
remaining -= bytes_to_read;
bytes_to_read = cqspi_get_rd_sram_level(cqspi);
diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
index 2901c7bd9e30..3e3c0bbc45c0 100644
--- a/drivers/mtd/spi-nor/fsl-quadspi.c
+++ b/drivers/mtd/spi-nor/fsl-quadspi.c
@@ -1051,6 +1051,24 @@ static int fsl_qspi_probe(struct platform_device *pdev)
spi_nor_set_flash_node(nor, np);
nor->priv = q;
+ if (q->nor_num > 1 && !mtd->name) {
+ int spiflash_idx;
+
+ ret = of_property_read_u32(np, "reg", &spiflash_idx);
+ if (!ret) {
+ mtd->name = devm_kasprintf(dev, GFP_KERNEL,
+ "%s-%d",
+ dev_name(dev),
+ spiflash_idx);
+ if (!mtd->name) {
+ ret = -ENOMEM;
+ goto mutex_failed;
+ }
+ } else {
+ dev_warn(dev, "reg property is missing\n");
+ }
+ }
+
/* fill the hooks */
nor->read_reg = fsl_qspi_read_reg;
nor->write_reg = fsl_qspi_write_reg;
@@ -1174,7 +1192,6 @@ static int fsl_qspi_resume(struct platform_device *pdev)
static struct platform_driver fsl_qspi_driver = {
.driver = {
.name = "fsl-quadspi",
- .bus = &platform_bus_type,
.of_match_table = fsl_qspi_dt_ids,
},
.probe = fsl_qspi_probe,
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index d445a4d3b770..5bfa36e95f35 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -560,9 +560,6 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
erase_err:
spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE);
- instr->state = ret ? MTD_ERASE_FAILED : MTD_ERASE_DONE;
- mtd_erase_callback(instr);
-
return ret;
}
diff --git a/drivers/mtd/tests/mtd_test.c b/drivers/mtd/tests/mtd_test.c
index 3d0b8b5c1a53..c84250beffdc 100644
--- a/drivers/mtd/tests/mtd_test.c
+++ b/drivers/mtd/tests/mtd_test.c
@@ -14,7 +14,6 @@ int mtdtest_erase_eraseblock(struct mtd_info *mtd, unsigned int ebnum)
loff_t addr = (loff_t)ebnum * mtd->erasesize;
memset(&ei, 0, sizeof(struct erase_info));
- ei.mtd = mtd;
ei.addr = addr;
ei.len = mtd->erasesize;
@@ -24,10 +23,6 @@ int mtdtest_erase_eraseblock(struct mtd_info *mtd, unsigned int ebnum)
return err;
}
- if (ei.state == MTD_ERASE_FAILED) {
- pr_info("some erase error occurred at EB %d\n", ebnum);
- return -EIO;
- }
return 0;
}
diff --git a/drivers/mtd/tests/pagetest.c b/drivers/mtd/tests/pagetest.c
index ff1e0565b020..bc303cac9f43 100644
--- a/drivers/mtd/tests/pagetest.c
+++ b/drivers/mtd/tests/pagetest.c
@@ -435,9 +435,13 @@ static int __init mtd_pagetest_init(void)
if (err)
goto out;
- err = erasecrosstest();
- if (err)
- goto out;
+ if (ebcnt > 1) {
+ err = erasecrosstest();
+ if (err)
+ goto out;
+ } else {
+ pr_info("skipping erasecrosstest, 2 erase blocks needed\n");
+ }
err = erasetest();
if (err)
diff --git a/drivers/mtd/tests/speedtest.c b/drivers/mtd/tests/speedtest.c
index 0b89418a0888..20edb3b49c77 100644
--- a/drivers/mtd/tests/speedtest.c
+++ b/drivers/mtd/tests/speedtest.c
@@ -59,7 +59,6 @@ static int multiblock_erase(int ebnum, int blocks)
loff_t addr = (loff_t)ebnum * mtd->erasesize;
memset(&ei, 0, sizeof(struct erase_info));
- ei.mtd = mtd;
ei.addr = addr;
ei.len = mtd->erasesize * blocks;
@@ -70,12 +69,6 @@ static int multiblock_erase(int ebnum, int blocks)
return err;
}
- if (ei.state == MTD_ERASE_FAILED) {
- pr_err("some erase error occurred at EB %d,"
- "blocks %d\n", ebnum, blocks);
- return -EIO;
- }
-
return 0;
}
diff --git a/drivers/mtd/ubi/block.c b/drivers/mtd/ubi/block.c
index b1fc28f63882..d0b63bbf46a7 100644
--- a/drivers/mtd/ubi/block.c
+++ b/drivers/mtd/ubi/block.c
@@ -244,7 +244,7 @@ static int ubiblock_open(struct block_device *bdev, fmode_t mode)
* in any case.
*/
if (mode & FMODE_WRITE) {
- ret = -EPERM;
+ ret = -EROFS;
goto out_unlock;
}
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index e941395de3ae..753494e042d5 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -854,6 +854,17 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num,
return -EINVAL;
}
+ /*
+ * Both UBI and UBIFS have been designed for SLC NAND and NOR flashes.
+ * MLC NAND is different and needs special care, otherwise UBI or UBIFS
+ * will die soon and you will lose all your data.
+ */
+ if (mtd->type == MTD_MLCNANDFLASH) {
+ pr_err("ubi: refuse attaching mtd%d - MLC NAND is not supported\n",
+ mtd->index);
+ return -EINVAL;
+ }
+
if (ubi_num == UBI_DEV_NUM_AUTO) {
/* Search for an empty slot in the @ubi_devices array */
for (ubi_num = 0; ubi_num < UBI_MAX_DEVICES; ubi_num++)
diff --git a/drivers/mtd/ubi/fastmap-wl.c b/drivers/mtd/ubi/fastmap-wl.c
index 590d967011bb..98f7d6be8d1f 100644
--- a/drivers/mtd/ubi/fastmap-wl.c
+++ b/drivers/mtd/ubi/fastmap-wl.c
@@ -362,7 +362,6 @@ static void ubi_fastmap_close(struct ubi_device *ubi)
{
int i;
- flush_work(&ubi->fm_work);
return_unused_pool_pebs(ubi, &ubi->fm_pool);
return_unused_pool_pebs(ubi, &ubi->fm_wl_pool);
diff --git a/drivers/mtd/ubi/gluebi.c b/drivers/mtd/ubi/gluebi.c
index 1cb287ec32ad..6b655a53113b 100644
--- a/drivers/mtd/ubi/gluebi.c
+++ b/drivers/mtd/ubi/gluebi.c
@@ -272,12 +272,9 @@ static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr)
if (err)
goto out_err;
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
return 0;
out_err:
- instr->state = MTD_ERASE_FAILED;
instr->fail_addr = (long long)lnum * mtd->erasesize;
return err;
}
diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c
index 8290432017ce..0e3a76a9e2f8 100644
--- a/drivers/mtd/ubi/io.c
+++ b/drivers/mtd/ubi/io.c
@@ -309,18 +309,6 @@ int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset,
}
/**
- * erase_callback - MTD erasure call-back.
- * @ei: MTD erase information object.
- *
- * Note, even though MTD erase interface is asynchronous, all the current
- * implementations are synchronous anyway.
- */
-static void erase_callback(struct erase_info *ei)
-{
- wake_up_interruptible((wait_queue_head_t *)ei->priv);
-}
-
-/**
* do_sync_erase - synchronously erase a physical eraseblock.
* @ubi: UBI device description object
* @pnum: the physical eraseblock number to erase
@@ -333,7 +321,6 @@ static int do_sync_erase(struct ubi_device *ubi, int pnum)
{
int err, retries = 0;
struct erase_info ei;
- wait_queue_head_t wq;
dbg_io("erase PEB %d", pnum);
ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
@@ -344,14 +331,10 @@ static int do_sync_erase(struct ubi_device *ubi, int pnum)
}
retry:
- init_waitqueue_head(&wq);
memset(&ei, 0, sizeof(struct erase_info));
- ei.mtd = ubi->mtd;
ei.addr = (loff_t)pnum * ubi->peb_size;
ei.len = ubi->peb_size;
- ei.callback = erase_callback;
- ei.priv = (unsigned long)&wq;
err = mtd_erase(ubi->mtd, &ei);
if (err) {
@@ -366,25 +349,6 @@ retry:
return err;
}
- err = wait_event_interruptible(wq, ei.state == MTD_ERASE_DONE ||
- ei.state == MTD_ERASE_FAILED);
- if (err) {
- ubi_err(ubi, "interrupted PEB %d erasure", pnum);
- return -EINTR;
- }
-
- if (ei.state == MTD_ERASE_FAILED) {
- if (retries++ < UBI_IO_RETRIES) {
- ubi_warn(ubi, "error while erasing PEB %d, retry",
- pnum);
- yield();
- goto retry;
- }
- ubi_err(ubi, "cannot erase PEB %d", pnum);
- dump_stack();
- return -EIO;
- }
-
err = ubi_self_check_all_ff(ubi, pnum, 0, ubi->peb_size);
if (err)
return err;
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 944ec3c9282c..891846655000 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -149,9 +149,9 @@ config MACVTAP
config IPVLAN
tristate "IP-VLAN support"
depends on INET
- depends on IPV6
+ depends on IPV6 || !IPV6
depends on NETFILTER
- depends on NET_L3_MASTER_DEV
+ select NET_L3_MASTER_DEV
---help---
This allows one to create virtual devices off of a main interface
and packets will be delivered based on the dest L3 (IPv6/IPv4 addr)
@@ -500,6 +500,7 @@ source "drivers/net/hyperv/Kconfig"
config NETDEVSIM
tristate "Simulated networking device"
depends on DEBUG_FS
+ depends on MAY_USE_DEVLINK
help
This driver is a developer testing tool and software model that can
be used to test various control path networking APIs, especially
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 04c3b747812c..91e67e375dd4 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -40,7 +40,6 @@ obj-$(CONFIG_ARCNET) += arcnet/
obj-$(CONFIG_DEV_APPLETALK) += appletalk/
obj-$(CONFIG_CAIF) += caif/
obj-$(CONFIG_CAN) += can/
-obj-$(CONFIG_ETRAX_ETHERNET) += cris/
obj-$(CONFIG_NET_DSA) += dsa/
obj-$(CONFIG_ETHERNET) += ethernet/
obj-$(CONFIG_FDDI) += fddi/
diff --git a/drivers/net/Space.c b/drivers/net/Space.c
index 11fe71278f40..3afda6561434 100644
--- a/drivers/net/Space.c
+++ b/drivers/net/Space.c
@@ -114,12 +114,6 @@ static struct devprobe2 m68k_probes[] __initdata = {
#ifdef CONFIG_MVME147_NET /* MVME147 internal Ethernet */
{mvme147lance_probe, 0},
#endif
-#ifdef CONFIG_MAC8390 /* NuBus NS8390-based cards */
- {mac8390_probe, 0},
-#endif
-#ifdef CONFIG_MAC89x0
- {mac89x0_probe, 0},
-#endif
{NULL, 0},
};
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index c669554d70bb..718e4914e3a0 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1528,39 +1528,6 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,
goto err_close;
}
- /* If the mode uses primary, then the following is handled by
- * bond_change_active_slave().
- */
- if (!bond_uses_primary(bond)) {
- /* set promiscuity level to new slave */
- if (bond_dev->flags & IFF_PROMISC) {
- res = dev_set_promiscuity(slave_dev, 1);
- if (res)
- goto err_close;
- }
-
- /* set allmulti level to new slave */
- if (bond_dev->flags & IFF_ALLMULTI) {
- res = dev_set_allmulti(slave_dev, 1);
- if (res)
- goto err_close;
- }
-
- netif_addr_lock_bh(bond_dev);
-
- dev_mc_sync_multiple(slave_dev, bond_dev);
- dev_uc_sync_multiple(slave_dev, bond_dev);
-
- netif_addr_unlock_bh(bond_dev);
- }
-
- if (BOND_MODE(bond) == BOND_MODE_8023AD) {
- /* add lacpdu mc addr to mc list */
- u8 lacpdu_multicast[ETH_ALEN] = MULTICAST_LACPDU_ADDR;
-
- dev_mc_add(slave_dev, lacpdu_multicast);
- }
-
res = vlan_vids_add_by_dev(slave_dev, bond_dev);
if (res) {
netdev_err(bond_dev, "Couldn't add bond vlan ids to %s\n",
@@ -1693,8 +1660,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,
} /* switch(bond_mode) */
#ifdef CONFIG_NET_POLL_CONTROLLER
- slave_dev->npinfo = bond->dev->npinfo;
- if (slave_dev->npinfo) {
+ if (bond->dev->npinfo) {
if (slave_enable_netpoll(new_slave)) {
netdev_info(bond_dev, "master_dev is using netpoll, but new slave device does not support netpoll\n");
res = -EBUSY;
@@ -1725,6 +1691,40 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,
goto err_upper_unlink;
}
+ /* If the mode uses primary, then the following is handled by
+ * bond_change_active_slave().
+ */
+ if (!bond_uses_primary(bond)) {
+ /* set promiscuity level to new slave */
+ if (bond_dev->flags & IFF_PROMISC) {
+ res = dev_set_promiscuity(slave_dev, 1);
+ if (res)
+ goto err_sysfs_del;
+ }
+
+ /* set allmulti level to new slave */
+ if (bond_dev->flags & IFF_ALLMULTI) {
+ res = dev_set_allmulti(slave_dev, 1);
+ if (res) {
+ if (bond_dev->flags & IFF_PROMISC)
+ dev_set_promiscuity(slave_dev, -1);
+ goto err_sysfs_del;
+ }
+ }
+
+ netif_addr_lock_bh(bond_dev);
+ dev_mc_sync_multiple(slave_dev, bond_dev);
+ dev_uc_sync_multiple(slave_dev, bond_dev);
+ netif_addr_unlock_bh(bond_dev);
+
+ if (BOND_MODE(bond) == BOND_MODE_8023AD) {
+ /* add lacpdu mc addr to mc list */
+ u8 lacpdu_multicast[ETH_ALEN] = MULTICAST_LACPDU_ADDR;
+
+ dev_mc_add(slave_dev, lacpdu_multicast);
+ }
+ }
+
bond->slave_cnt++;
bond_compute_features(bond);
bond_set_carrier(bond);
@@ -1748,6 +1748,9 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,
return 0;
/* Undo stages on error */
+err_sysfs_del:
+ bond_sysfs_slave_del(new_slave);
+
err_upper_unlink:
bond_upper_dev_unlink(bond, new_slave);
@@ -1755,9 +1758,6 @@ err_unregister:
netdev_rx_handler_unregister(slave_dev);
err_detach:
- if (!bond_uses_primary(bond))
- bond_hw_addr_flush(bond_dev, slave_dev);
-
vlan_vids_del_by_dev(slave_dev, bond_dev);
if (rcu_access_pointer(bond->primary_slave) == new_slave)
RCU_INIT_POINTER(bond->primary_slave, NULL);
diff --git a/drivers/net/bonding/bond_procfs.c b/drivers/net/bonding/bond_procfs.c
index f7799321dffb..01059f1a7bca 100644
--- a/drivers/net/bonding/bond_procfs.c
+++ b/drivers/net/bonding/bond_procfs.c
@@ -287,7 +287,7 @@ void bond_create_proc_entry(struct bonding *bond)
if (bn->proc_dir) {
bond->proc_entry = proc_create_data(bond_dev->name,
- S_IRUGO, bn->proc_dir,
+ 0444, bn->proc_dir,
&bond_info_fops, bond);
if (bond->proc_entry == NULL)
netdev_warn(bond_dev, "Cannot create /proc/net/%s/%s\n",
diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c
index 040b493f60ae..6096440e96ea 100644
--- a/drivers/net/bonding/bond_sysfs.c
+++ b/drivers/net/bonding/bond_sysfs.c
@@ -147,7 +147,7 @@ err_no_cmd:
static const struct class_attribute class_attr_bonding_masters = {
.attr = {
.name = "bonding_masters",
- .mode = S_IWUSR | S_IRUGO,
+ .mode = 0644,
},
.show = bonding_show_bonds,
.store = bonding_store_bonds,
@@ -202,7 +202,7 @@ static ssize_t bonding_show_slaves(struct device *d,
return res;
}
-static DEVICE_ATTR(slaves, S_IRUGO | S_IWUSR, bonding_show_slaves,
+static DEVICE_ATTR(slaves, 0644, bonding_show_slaves,
bonding_sysfs_store_option);
/* Show the bonding mode. */
@@ -216,8 +216,7 @@ static ssize_t bonding_show_mode(struct device *d,
return sprintf(buf, "%s %d\n", val->string, BOND_MODE(bond));
}
-static DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
- bonding_show_mode, bonding_sysfs_store_option);
+static DEVICE_ATTR(mode, 0644, bonding_show_mode, bonding_sysfs_store_option);
/* Show the bonding transmit hash method. */
static ssize_t bonding_show_xmit_hash(struct device *d,
@@ -231,7 +230,7 @@ static ssize_t bonding_show_xmit_hash(struct device *d,
return sprintf(buf, "%s %d\n", val->string, bond->params.xmit_policy);
}
-static DEVICE_ATTR(xmit_hash_policy, S_IRUGO | S_IWUSR,
+static DEVICE_ATTR(xmit_hash_policy, 0644,
bonding_show_xmit_hash, bonding_sysfs_store_option);
/* Show arp_validate. */
@@ -247,7 +246,7 @@ static ssize_t bonding_show_arp_validate(struct device *d,
return sprintf(buf, "%s %d\n", val->string, bond->params.arp_validate);
}
-static DEVICE_ATTR(arp_validate, S_IRUGO | S_IWUSR, bonding_show_arp_validate,
+static DEVICE_ATTR(arp_validate, 0644, bonding_show_arp_validate,
bonding_sysfs_store_option);
/* Show arp_all_targets. */
@@ -263,7 +262,7 @@ static ssize_t bonding_show_arp_all_targets(struct device *d,
return sprintf(buf, "%s %d\n",
val->string, bond->params.arp_all_targets);
}
-static DEVICE_ATTR(arp_all_targets, S_IRUGO | S_IWUSR,
+static DEVICE_ATTR(arp_all_targets, 0644,
bonding_show_arp_all_targets, bonding_sysfs_store_option);
/* Show fail_over_mac. */
@@ -279,7 +278,7 @@ static ssize_t bonding_show_fail_over_mac(struct device *d,
return sprintf(buf, "%s %d\n", val->string, bond->params.fail_over_mac);
}
-static DEVICE_ATTR(fail_over_mac, S_IRUGO | S_IWUSR,
+static DEVICE_ATTR(fail_over_mac, 0644,
bonding_show_fail_over_mac, bonding_sysfs_store_option);
/* Show the arp timer interval. */
@@ -291,7 +290,7 @@ static ssize_t bonding_show_arp_interval(struct device *d,
return sprintf(buf, "%d\n", bond->params.arp_interval);
}
-static DEVICE_ATTR(arp_interval, S_IRUGO | S_IWUSR,
+static DEVICE_ATTR(arp_interval, 0644,
bonding_show_arp_interval, bonding_sysfs_store_option);
/* Show the arp targets. */
@@ -312,7 +311,7 @@ static ssize_t bonding_show_arp_targets(struct device *d,
return res;
}
-static DEVICE_ATTR(arp_ip_target, S_IRUGO | S_IWUSR,
+static DEVICE_ATTR(arp_ip_target, 0644,
bonding_show_arp_targets, bonding_sysfs_store_option);
/* Show the up and down delays. */
@@ -324,7 +323,7 @@ static ssize_t bonding_show_downdelay(struct device *d,
return sprintf(buf, "%d\n", bond->params.downdelay * bond->params.miimon);
}
-static DEVICE_ATTR(downdelay, S_IRUGO | S_IWUSR,
+static DEVICE_ATTR(downdelay, 0644,
bonding_show_downdelay, bonding_sysfs_store_option);
static ssize_t bonding_show_updelay(struct device *d,
@@ -336,7 +335,7 @@ static ssize_t bonding_show_updelay(struct device *d,
return sprintf(buf, "%d\n", bond->params.updelay * bond->params.miimon);
}
-static DEVICE_ATTR(updelay, S_IRUGO | S_IWUSR,
+static DEVICE_ATTR(updelay, 0644,
bonding_show_updelay, bonding_sysfs_store_option);
/* Show the LACP interval. */
@@ -351,7 +350,7 @@ static ssize_t bonding_show_lacp(struct device *d,
return sprintf(buf, "%s %d\n", val->string, bond->params.lacp_fast);
}
-static DEVICE_ATTR(lacp_rate, S_IRUGO | S_IWUSR,
+static DEVICE_ATTR(lacp_rate, 0644,
bonding_show_lacp, bonding_sysfs_store_option);
static ssize_t bonding_show_min_links(struct device *d,
@@ -362,7 +361,7 @@ static ssize_t bonding_show_min_links(struct device *d,
return sprintf(buf, "%u\n", bond->params.min_links);
}
-static DEVICE_ATTR(min_links, S_IRUGO | S_IWUSR,
+static DEVICE_ATTR(min_links, 0644,
bonding_show_min_links, bonding_sysfs_store_option);
static ssize_t bonding_show_ad_select(struct device *d,
@@ -376,7 +375,7 @@ static ssize_t bonding_show_ad_select(struct device *d,
return sprintf(buf, "%s %d\n", val->string, bond->params.ad_select);
}
-static DEVICE_ATTR(ad_select, S_IRUGO | S_IWUSR,
+static DEVICE_ATTR(ad_select, 0644,
bonding_show_ad_select, bonding_sysfs_store_option);
/* Show the number of peer notifications to send after a failover event. */
@@ -387,9 +386,9 @@ static ssize_t bonding_show_num_peer_notif(struct device *d,
struct bonding *bond = to_bond(d);
return sprintf(buf, "%d\n", bond->params.num_peer_notif);
}
-static DEVICE_ATTR(num_grat_arp, S_IRUGO | S_IWUSR,
+static DEVICE_ATTR(num_grat_arp, 0644,
bonding_show_num_peer_notif, bonding_sysfs_store_option);
-static DEVICE_ATTR(num_unsol_na, S_IRUGO | S_IWUSR,
+static DEVICE_ATTR(num_unsol_na, 0644,
bonding_show_num_peer_notif, bonding_sysfs_store_option);
/* Show the MII monitor interval. */
@@ -401,7 +400,7 @@ static ssize_t bonding_show_miimon(struct device *d,
return sprintf(buf, "%d\n", bond->params.miimon);
}
-static DEVICE_ATTR(miimon, S_IRUGO | S_IWUSR,
+static DEVICE_ATTR(miimon, 0644,
bonding_show_miimon, bonding_sysfs_store_option);
/* Show the primary slave. */
@@ -421,7 +420,7 @@ static ssize_t bonding_show_primary(struct device *d,
return count;
}
-static DEVICE_ATTR(primary, S_IRUGO | S_IWUSR,
+static DEVICE_ATTR(primary, 0644,
bonding_show_primary, bonding_sysfs_store_option);
/* Show the primary_reselect flag. */
@@ -438,7 +437,7 @@ static ssize_t bonding_show_primary_reselect(struct device *d,
return sprintf(buf, "%s %d\n",
val->string, bond->params.primary_reselect);
}
-static DEVICE_ATTR(primary_reselect, S_IRUGO | S_IWUSR,
+static DEVICE_ATTR(primary_reselect, 0644,
bonding_show_primary_reselect, bonding_sysfs_store_option);
/* Show the use_carrier flag. */
@@ -450,7 +449,7 @@ static ssize_t bonding_show_carrier(struct device *d,
return sprintf(buf, "%d\n", bond->params.use_carrier);
}
-static DEVICE_ATTR(use_carrier, S_IRUGO | S_IWUSR,
+static DEVICE_ATTR(use_carrier, 0644,
bonding_show_carrier, bonding_sysfs_store_option);
@@ -471,7 +470,7 @@ static ssize_t bonding_show_active_slave(struct device *d,
return count;
}
-static DEVICE_ATTR(active_slave, S_IRUGO | S_IWUSR,
+static DEVICE_ATTR(active_slave, 0644,
bonding_show_active_slave, bonding_sysfs_store_option);
/* Show link status of the bond interface. */
@@ -484,7 +483,7 @@ static ssize_t bonding_show_mii_status(struct device *d,
return sprintf(buf, "%s\n", active ? "up" : "down");
}
-static DEVICE_ATTR(mii_status, S_IRUGO, bonding_show_mii_status, NULL);
+static DEVICE_ATTR(mii_status, 0444, bonding_show_mii_status, NULL);
/* Show current 802.3ad aggregator ID. */
static ssize_t bonding_show_ad_aggregator(struct device *d,
@@ -503,7 +502,7 @@ static ssize_t bonding_show_ad_aggregator(struct device *d,
return count;
}
-static DEVICE_ATTR(ad_aggregator, S_IRUGO, bonding_show_ad_aggregator, NULL);
+static DEVICE_ATTR(ad_aggregator, 0444, bonding_show_ad_aggregator, NULL);
/* Show number of active 802.3ad ports. */
@@ -523,7 +522,7 @@ static ssize_t bonding_show_ad_num_ports(struct device *d,
return count;
}
-static DEVICE_ATTR(ad_num_ports, S_IRUGO, bonding_show_ad_num_ports, NULL);
+static DEVICE_ATTR(ad_num_ports, 0444, bonding_show_ad_num_ports, NULL);
/* Show current 802.3ad actor key. */
@@ -543,7 +542,7 @@ static ssize_t bonding_show_ad_actor_key(struct device *d,
return count;
}
-static DEVICE_ATTR(ad_actor_key, S_IRUGO, bonding_show_ad_actor_key, NULL);
+static DEVICE_ATTR(ad_actor_key, 0444, bonding_show_ad_actor_key, NULL);
/* Show current 802.3ad partner key. */
@@ -563,7 +562,7 @@ static ssize_t bonding_show_ad_partner_key(struct device *d,
return count;
}
-static DEVICE_ATTR(ad_partner_key, S_IRUGO, bonding_show_ad_partner_key, NULL);
+static DEVICE_ATTR(ad_partner_key, 0444, bonding_show_ad_partner_key, NULL);
/* Show current 802.3ad partner mac. */
@@ -582,7 +581,7 @@ static ssize_t bonding_show_ad_partner_mac(struct device *d,
return count;
}
-static DEVICE_ATTR(ad_partner_mac, S_IRUGO, bonding_show_ad_partner_mac, NULL);
+static DEVICE_ATTR(ad_partner_mac, 0444, bonding_show_ad_partner_mac, NULL);
/* Show the queue_ids of the slaves in the current bond. */
static ssize_t bonding_show_queue_id(struct device *d,
@@ -615,7 +614,7 @@ static ssize_t bonding_show_queue_id(struct device *d,
return res;
}
-static DEVICE_ATTR(queue_id, S_IRUGO | S_IWUSR, bonding_show_queue_id,
+static DEVICE_ATTR(queue_id, 0644, bonding_show_queue_id,
bonding_sysfs_store_option);
@@ -628,7 +627,7 @@ static ssize_t bonding_show_slaves_active(struct device *d,
return sprintf(buf, "%d\n", bond->params.all_slaves_active);
}
-static DEVICE_ATTR(all_slaves_active, S_IRUGO | S_IWUSR,
+static DEVICE_ATTR(all_slaves_active, 0644,
bonding_show_slaves_active, bonding_sysfs_store_option);
/* Show the number of IGMP membership reports to send on link failure */
@@ -640,7 +639,7 @@ static ssize_t bonding_show_resend_igmp(struct device *d,
return sprintf(buf, "%d\n", bond->params.resend_igmp);
}
-static DEVICE_ATTR(resend_igmp, S_IRUGO | S_IWUSR,
+static DEVICE_ATTR(resend_igmp, 0644,
bonding_show_resend_igmp, bonding_sysfs_store_option);
@@ -652,7 +651,7 @@ static ssize_t bonding_show_lp_interval(struct device *d,
return sprintf(buf, "%d\n", bond->params.lp_interval);
}
-static DEVICE_ATTR(lp_interval, S_IRUGO | S_IWUSR,
+static DEVICE_ATTR(lp_interval, 0644,
bonding_show_lp_interval, bonding_sysfs_store_option);
static ssize_t bonding_show_tlb_dynamic_lb(struct device *d,
@@ -662,7 +661,7 @@ static ssize_t bonding_show_tlb_dynamic_lb(struct device *d,
struct bonding *bond = to_bond(d);
return sprintf(buf, "%d\n", bond->params.tlb_dynamic_lb);
}
-static DEVICE_ATTR(tlb_dynamic_lb, S_IRUGO | S_IWUSR,
+static DEVICE_ATTR(tlb_dynamic_lb, 0644,
bonding_show_tlb_dynamic_lb, bonding_sysfs_store_option);
static ssize_t bonding_show_packets_per_slave(struct device *d,
@@ -674,7 +673,7 @@ static ssize_t bonding_show_packets_per_slave(struct device *d,
return sprintf(buf, "%u\n", packets_per_slave);
}
-static DEVICE_ATTR(packets_per_slave, S_IRUGO | S_IWUSR,
+static DEVICE_ATTR(packets_per_slave, 0644,
bonding_show_packets_per_slave, bonding_sysfs_store_option);
static ssize_t bonding_show_ad_actor_sys_prio(struct device *d,
@@ -688,7 +687,7 @@ static ssize_t bonding_show_ad_actor_sys_prio(struct device *d,
return 0;
}
-static DEVICE_ATTR(ad_actor_sys_prio, S_IRUGO | S_IWUSR,
+static DEVICE_ATTR(ad_actor_sys_prio, 0644,
bonding_show_ad_actor_sys_prio, bonding_sysfs_store_option);
static ssize_t bonding_show_ad_actor_system(struct device *d,
@@ -703,7 +702,7 @@ static ssize_t bonding_show_ad_actor_system(struct device *d,
return 0;
}
-static DEVICE_ATTR(ad_actor_system, S_IRUGO | S_IWUSR,
+static DEVICE_ATTR(ad_actor_system, 0644,
bonding_show_ad_actor_system, bonding_sysfs_store_option);
static ssize_t bonding_show_ad_user_port_key(struct device *d,
@@ -717,7 +716,7 @@ static ssize_t bonding_show_ad_user_port_key(struct device *d,
return 0;
}
-static DEVICE_ATTR(ad_user_port_key, S_IRUGO | S_IWUSR,
+static DEVICE_ATTR(ad_user_port_key, 0644,
bonding_show_ad_user_port_key, bonding_sysfs_store_option);
static struct attribute *per_bond_attrs[] = {
diff --git a/drivers/net/bonding/bond_sysfs_slave.c b/drivers/net/bonding/bond_sysfs_slave.c
index 7d16c51e6913..2f120b2ffef0 100644
--- a/drivers/net/bonding/bond_sysfs_slave.c
+++ b/drivers/net/bonding/bond_sysfs_slave.c
@@ -25,8 +25,8 @@ const struct slave_attribute slave_attr_##_name = { \
.mode = _mode }, \
.show = _show, \
};
-#define SLAVE_ATTR_RO(_name) \
- SLAVE_ATTR(_name, S_IRUGO, _name##_show)
+#define SLAVE_ATTR_RO(_name) \
+ SLAVE_ATTR(_name, 0444, _name##_show)
static ssize_t state_show(struct slave *slave, char *buf)
{
diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c
index 709838e4c062..a0f954f36c09 100644
--- a/drivers/net/caif/caif_serial.c
+++ b/drivers/net/caif/caif_serial.c
@@ -40,20 +40,20 @@ static LIST_HEAD(ser_list);
static LIST_HEAD(ser_release_list);
static bool ser_loop;
-module_param(ser_loop, bool, S_IRUGO);
+module_param(ser_loop, bool, 0444);
MODULE_PARM_DESC(ser_loop, "Run in simulated loopback mode.");
static bool ser_use_stx = true;
-module_param(ser_use_stx, bool, S_IRUGO);
+module_param(ser_use_stx, bool, 0444);
MODULE_PARM_DESC(ser_use_stx, "STX enabled or not.");
static bool ser_use_fcs = true;
-module_param(ser_use_fcs, bool, S_IRUGO);
+module_param(ser_use_fcs, bool, 0444);
MODULE_PARM_DESC(ser_use_fcs, "FCS enabled or not.");
static int ser_write_chunk = MAX_WRITE_CHUNK;
-module_param(ser_write_chunk, int, S_IRUGO);
+module_param(ser_write_chunk, int, 0444);
MODULE_PARM_DESC(ser_write_chunk, "Maximum size of data written to UART.");
@@ -97,21 +97,21 @@ static inline void debugfs_init(struct ser_device *ser, struct tty_struct *tty)
ser->debugfs_tty_dir =
debugfs_create_dir(tty->name, debugfsdir);
if (!IS_ERR(ser->debugfs_tty_dir)) {
- debugfs_create_blob("last_tx_msg", S_IRUSR,
- ser->debugfs_tty_dir,
- &ser->tx_blob);
+ debugfs_create_blob("last_tx_msg", 0400,
+ ser->debugfs_tty_dir,
+ &ser->tx_blob);
- debugfs_create_blob("last_rx_msg", S_IRUSR,
- ser->debugfs_tty_dir,
- &ser->rx_blob);
+ debugfs_create_blob("last_rx_msg", 0400,
+ ser->debugfs_tty_dir,
+ &ser->rx_blob);
- debugfs_create_x32("ser_state", S_IRUSR,
- ser->debugfs_tty_dir,
- (u32 *)&ser->state);
+ debugfs_create_x32("ser_state", 0400,
+ ser->debugfs_tty_dir,
+ (u32 *)&ser->state);
- debugfs_create_x8("tty_status", S_IRUSR,
- ser->debugfs_tty_dir,
- &ser->tty_status);
+ debugfs_create_x8("tty_status", 0400,
+ ser->debugfs_tty_dir,
+ &ser->tty_status);
}
ser->tx_blob.data = ser->tx_data;
diff --git a/drivers/net/caif/caif_spi.c b/drivers/net/caif/caif_spi.c
index 980eace53d44..d28a1398c091 100644
--- a/drivers/net/caif/caif_spi.c
+++ b/drivers/net/caif/caif_spi.c
@@ -35,27 +35,27 @@ MODULE_DESCRIPTION("CAIF SPI driver");
#define PAD_POW2(x, pow) ((((x)&((pow)-1))==0) ? 0 : (((pow)-((x)&((pow)-1)))))
static bool spi_loop;
-module_param(spi_loop, bool, S_IRUGO);
+module_param(spi_loop, bool, 0444);
MODULE_PARM_DESC(spi_loop, "SPI running in loopback mode.");
/* SPI frame alignment. */
-module_param(spi_frm_align, int, S_IRUGO);
+module_param(spi_frm_align, int, 0444);
MODULE_PARM_DESC(spi_frm_align, "SPI frame alignment.");
/*
* SPI padding options.
* Warning: must be a base of 2 (& operation used) and can not be zero !
*/
-module_param(spi_up_head_align, int, S_IRUGO);
+module_param(spi_up_head_align, int, 0444);
MODULE_PARM_DESC(spi_up_head_align, "SPI uplink head alignment.");
-module_param(spi_up_tail_align, int, S_IRUGO);
+module_param(spi_up_tail_align, int, 0444);
MODULE_PARM_DESC(spi_up_tail_align, "SPI uplink tail alignment.");
-module_param(spi_down_head_align, int, S_IRUGO);
+module_param(spi_down_head_align, int, 0444);
MODULE_PARM_DESC(spi_down_head_align, "SPI downlink head alignment.");
-module_param(spi_down_tail_align, int, S_IRUGO);
+module_param(spi_down_tail_align, int, 0444);
MODULE_PARM_DESC(spi_down_tail_align, "SPI downlink tail alignment.");
#ifdef CONFIG_ARM
@@ -250,10 +250,10 @@ static const struct file_operations dbgfs_frame_fops = {
static inline void dev_debugfs_add(struct cfspi *cfspi)
{
cfspi->dbgfs_dir = debugfs_create_dir(cfspi->pdev->name, dbgfs_root);
- cfspi->dbgfs_state = debugfs_create_file("state", S_IRUGO,
+ cfspi->dbgfs_state = debugfs_create_file("state", 0444,
cfspi->dbgfs_dir, cfspi,
&dbgfs_state_fops);
- cfspi->dbgfs_frame = debugfs_create_file("frame", S_IRUGO,
+ cfspi->dbgfs_frame = debugfs_create_file("frame", 0444,
cfspi->dbgfs_dir, cfspi,
&dbgfs_frame_fops);
}
diff --git a/drivers/net/caif/caif_virtio.c b/drivers/net/caif/caif_virtio.c
index c3d104feee13..2814e0dee4bb 100644
--- a/drivers/net/caif/caif_virtio.c
+++ b/drivers/net/caif/caif_virtio.c
@@ -629,21 +629,21 @@ static inline void debugfs_init(struct cfv_info *cfv)
if (IS_ERR(cfv->debugfs))
return;
- debugfs_create_u32("rx-napi-complete", S_IRUSR, cfv->debugfs,
+ debugfs_create_u32("rx-napi-complete", 0400, cfv->debugfs,
&cfv->stats.rx_napi_complete);
- debugfs_create_u32("rx-napi-resched", S_IRUSR, cfv->debugfs,
+ debugfs_create_u32("rx-napi-resched", 0400, cfv->debugfs,
&cfv->stats.rx_napi_resched);
- debugfs_create_u32("rx-nomem", S_IRUSR, cfv->debugfs,
+ debugfs_create_u32("rx-nomem", 0400, cfv->debugfs,
&cfv->stats.rx_nomem);
- debugfs_create_u32("rx-kicks", S_IRUSR, cfv->debugfs,
+ debugfs_create_u32("rx-kicks", 0400, cfv->debugfs,
&cfv->stats.rx_kicks);
- debugfs_create_u32("tx-full-ring", S_IRUSR, cfv->debugfs,
+ debugfs_create_u32("tx-full-ring", 0400, cfv->debugfs,
&cfv->stats.tx_full_ring);
- debugfs_create_u32("tx-no-mem", S_IRUSR, cfv->debugfs,
+ debugfs_create_u32("tx-no-mem", 0400, cfv->debugfs,
&cfv->stats.tx_no_mem);
- debugfs_create_u32("tx-kicks", S_IRUSR, cfv->debugfs,
+ debugfs_create_u32("tx-kicks", 0400, cfv->debugfs,
&cfv->stats.tx_kicks);
- debugfs_create_u32("tx-flow-on", S_IRUSR, cfv->debugfs,
+ debugfs_create_u32("tx-flow-on", 0400, cfv->debugfs,
&cfv->stats.tx_flow_on);
}
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index ac4ff394bc56..2cb75988b328 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -88,15 +88,6 @@ config CAN_AT91
This is a driver for the SoC CAN controller in Atmel's AT91SAM9263
and AT91SAM9X5 processors.
-config CAN_BFIN
- depends on BF534 || BF536 || BF537 || BF538 || BF539 || BF54x
- tristate "Analog Devices Blackfin on-chip CAN"
- ---help---
- Driver for the Analog Devices Blackfin on-chip CAN controllers
-
- To compile this driver as a module, choose M here: the
- module will be called bfin_can.
-
config CAN_FLEXCAN
tristate "Support for Freescale FLEXCAN based chips"
depends on ARM || PPC
diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
index 02b8ed794564..44922bf29b6a 100644
--- a/drivers/net/can/Makefile
+++ b/drivers/net/can/Makefile
@@ -19,7 +19,6 @@ obj-y += usb/
obj-y += softing/
obj-$(CONFIG_CAN_AT91) += at91_can.o
-obj-$(CONFIG_CAN_BFIN) += bfin_can.o
obj-$(CONFIG_CAN_CC770) += cc770/
obj-$(CONFIG_CAN_C_CAN) += c_can/
obj-$(CONFIG_CAN_FLEXCAN) += flexcan.o
diff --git a/drivers/net/can/at91_can.c b/drivers/net/can/at91_can.c
index f37ce0e1b603..d98c69045b17 100644
--- a/drivers/net/can/at91_can.c
+++ b/drivers/net/can/at91_can.c
@@ -1224,8 +1224,7 @@ static ssize_t at91_sysfs_set_mb0_id(struct device *dev,
return ret;
}
-static DEVICE_ATTR(mb0_id, S_IWUSR | S_IRUGO,
- at91_sysfs_show_mb0_id, at91_sysfs_set_mb0_id);
+static DEVICE_ATTR(mb0_id, 0644, at91_sysfs_show_mb0_id, at91_sysfs_set_mb0_id);
static struct attribute *at91_sysfs_attrs[] = {
&dev_attr_mb0_id.attr,
diff --git a/drivers/net/can/bfin_can.c b/drivers/net/can/bfin_can.c
deleted file mode 100644
index 1deb8ff90a89..000000000000
--- a/drivers/net/can/bfin_can.c
+++ /dev/null
@@ -1,784 +0,0 @@
-/*
- * Blackfin On-Chip CAN Driver
- *
- * Copyright 2004-2009 Analog Devices Inc.
- *
- * Enter bugs at http://blackfin.uclinux.org/
- *
- * Licensed under the GPL-2 or later.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/bitops.h>
-#include <linux/interrupt.h>
-#include <linux/errno.h>
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <linux/platform_device.h>
-
-#include <linux/can/dev.h>
-#include <linux/can/error.h>
-
-#include <asm/portmux.h>
-
-#define DRV_NAME "bfin_can"
-#define BFIN_CAN_TIMEOUT 100
-#define TX_ECHO_SKB_MAX 1
-
-/* transmit and receive channels */
-#define TRANSMIT_CHL 24
-#define RECEIVE_STD_CHL 0
-#define RECEIVE_EXT_CHL 4
-#define RECEIVE_RTR_CHL 8
-#define RECEIVE_EXT_RTR_CHL 12
-#define MAX_CHL_NUMBER 32
-
-/* All Blackfin system MMRs are padded to 32bits even if the register
- * itself is only 16bits. So use a helper macro to streamline this
- */
-#define __BFP(m) u16 m; u16 __pad_##m
-
-/* bfin can registers layout */
-struct bfin_can_mask_regs {
- __BFP(aml);
- __BFP(amh);
-};
-
-struct bfin_can_channel_regs {
- /* data[0,2,4,6] -> data{0,1,2,3} while data[1,3,5,7] is padding */
- u16 data[8];
- __BFP(dlc);
- __BFP(tsv);
- __BFP(id0);
- __BFP(id1);
-};
-
-struct bfin_can_regs {
- /* global control and status registers */
- __BFP(mc1); /* offset 0x00 */
- __BFP(md1); /* offset 0x04 */
- __BFP(trs1); /* offset 0x08 */
- __BFP(trr1); /* offset 0x0c */
- __BFP(ta1); /* offset 0x10 */
- __BFP(aa1); /* offset 0x14 */
- __BFP(rmp1); /* offset 0x18 */
- __BFP(rml1); /* offset 0x1c */
- __BFP(mbtif1); /* offset 0x20 */
- __BFP(mbrif1); /* offset 0x24 */
- __BFP(mbim1); /* offset 0x28 */
- __BFP(rfh1); /* offset 0x2c */
- __BFP(opss1); /* offset 0x30 */
- u32 __pad1[3];
- __BFP(mc2); /* offset 0x40 */
- __BFP(md2); /* offset 0x44 */
- __BFP(trs2); /* offset 0x48 */
- __BFP(trr2); /* offset 0x4c */
- __BFP(ta2); /* offset 0x50 */
- __BFP(aa2); /* offset 0x54 */
- __BFP(rmp2); /* offset 0x58 */
- __BFP(rml2); /* offset 0x5c */
- __BFP(mbtif2); /* offset 0x60 */
- __BFP(mbrif2); /* offset 0x64 */
- __BFP(mbim2); /* offset 0x68 */
- __BFP(rfh2); /* offset 0x6c */
- __BFP(opss2); /* offset 0x70 */
- u32 __pad2[3];
- __BFP(clock); /* offset 0x80 */
- __BFP(timing); /* offset 0x84 */
- __BFP(debug); /* offset 0x88 */
- __BFP(status); /* offset 0x8c */
- __BFP(cec); /* offset 0x90 */
- __BFP(gis); /* offset 0x94 */
- __BFP(gim); /* offset 0x98 */
- __BFP(gif); /* offset 0x9c */
- __BFP(control); /* offset 0xa0 */
- __BFP(intr); /* offset 0xa4 */
- __BFP(version); /* offset 0xa8 */
- __BFP(mbtd); /* offset 0xac */
- __BFP(ewr); /* offset 0xb0 */
- __BFP(esr); /* offset 0xb4 */
- u32 __pad3[2];
- __BFP(ucreg); /* offset 0xc0 */
- __BFP(uccnt); /* offset 0xc4 */
- __BFP(ucrc); /* offset 0xc8 */
- __BFP(uccnf); /* offset 0xcc */
- u32 __pad4[1];
- __BFP(version2); /* offset 0xd4 */
- u32 __pad5[10];
-
- /* channel(mailbox) mask and message registers */
- struct bfin_can_mask_regs msk[MAX_CHL_NUMBER]; /* offset 0x100 */
- struct bfin_can_channel_regs chl[MAX_CHL_NUMBER]; /* offset 0x200 */
-};
-
-#undef __BFP
-
-#define SRS 0x0001 /* Software Reset */
-#define SER 0x0008 /* Stuff Error */
-#define BOIM 0x0008 /* Enable Bus Off Interrupt */
-#define CCR 0x0080 /* CAN Configuration Mode Request */
-#define CCA 0x0080 /* Configuration Mode Acknowledge */
-#define SAM 0x0080 /* Sampling */
-#define AME 0x8000 /* Acceptance Mask Enable */
-#define RMLIM 0x0080 /* Enable RX Message Lost Interrupt */
-#define RMLIS 0x0080 /* RX Message Lost IRQ Status */
-#define RTR 0x4000 /* Remote Frame Transmission Request */
-#define BOIS 0x0008 /* Bus Off IRQ Status */
-#define IDE 0x2000 /* Identifier Extension */
-#define EPIS 0x0004 /* Error-Passive Mode IRQ Status */
-#define EPIM 0x0004 /* Enable Error-Passive Mode Interrupt */
-#define EWTIS 0x0001 /* TX Error Count IRQ Status */
-#define EWRIS 0x0002 /* RX Error Count IRQ Status */
-#define BEF 0x0040 /* Bit Error Flag */
-#define FER 0x0080 /* Form Error Flag */
-#define SMR 0x0020 /* Sleep Mode Request */
-#define SMACK 0x0008 /* Sleep Mode Acknowledge */
-
-/*
- * bfin can private data
- */
-struct bfin_can_priv {
- struct can_priv can; /* must be the first member */
- struct net_device *dev;
- void __iomem *membase;
- int rx_irq;
- int tx_irq;
- int err_irq;
- unsigned short *pin_list;
-};
-
-/*
- * bfin can timing parameters
- */
-static const struct can_bittiming_const bfin_can_bittiming_const = {
- .name = DRV_NAME,
- .tseg1_min = 1,
- .tseg1_max = 16,
- .tseg2_min = 1,
- .tseg2_max = 8,
- .sjw_max = 4,
- /*
- * Although the BRP field can be set to any value, it is recommended
- * that the value be greater than or equal to 4, as restrictions
- * apply to the bit timing configuration when BRP is less than 4.
- */
- .brp_min = 4,
- .brp_max = 1024,
- .brp_inc = 1,
-};
-
-static int bfin_can_set_bittiming(struct net_device *dev)
-{
- struct bfin_can_priv *priv = netdev_priv(dev);
- struct bfin_can_regs __iomem *reg = priv->membase;
- struct can_bittiming *bt = &priv->can.bittiming;
- u16 clk, timing;
-
- clk = bt->brp - 1;
- timing = ((bt->sjw - 1) << 8) | (bt->prop_seg + bt->phase_seg1 - 1) |
- ((bt->phase_seg2 - 1) << 4);
-
- /*
- * If the SAM bit is set, the input signal is oversampled three times
- * at the SCLK rate.
- */
- if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
- timing |= SAM;
-
- writew(clk, &reg->clock);
- writew(timing, &reg->timing);
-
- netdev_info(dev, "setting CLOCK=0x%04x TIMING=0x%04x\n", clk, timing);
-
- return 0;
-}
-
-static void bfin_can_set_reset_mode(struct net_device *dev)
-{
- struct bfin_can_priv *priv = netdev_priv(dev);
- struct bfin_can_regs __iomem *reg = priv->membase;
- int timeout = BFIN_CAN_TIMEOUT;
- int i;
-
- /* disable interrupts */
- writew(0, &reg->mbim1);
- writew(0, &reg->mbim2);
- writew(0, &reg->gim);
-
- /* reset can and enter configuration mode */
- writew(SRS | CCR, &reg->control);
- writew(CCR, &reg->control);
- while (!(readw(&reg->control) & CCA)) {
- udelay(10);
- if (--timeout == 0) {
- netdev_err(dev, "fail to enter configuration mode\n");
- BUG();
- }
- }
-
- /*
- * All mailbox configurations are marked as inactive
- * by writing to CAN Mailbox Configuration Registers 1 and 2
- * For all bits: 0 - Mailbox disabled, 1 - Mailbox enabled
- */
- writew(0, &reg->mc1);
- writew(0, &reg->mc2);
-
- /* Set Mailbox Direction */
- writew(0xFFFF, &reg->md1); /* mailbox 1-16 are RX */
- writew(0, &reg->md2); /* mailbox 17-32 are TX */
-
- /* RECEIVE_STD_CHL */
- for (i = 0; i < 2; i++) {
- writew(0, &reg->chl[RECEIVE_STD_CHL + i].id0);
- writew(AME, &reg->chl[RECEIVE_STD_CHL + i].id1);
- writew(0, &reg->chl[RECEIVE_STD_CHL + i].dlc);
- writew(0x1FFF, &reg->msk[RECEIVE_STD_CHL + i].amh);
- writew(0xFFFF, &reg->msk[RECEIVE_STD_CHL + i].aml);
- }
-
- /* RECEIVE_EXT_CHL */
- for (i = 0; i < 2; i++) {
- writew(0, &reg->chl[RECEIVE_EXT_CHL + i].id0);
- writew(AME | IDE, &reg->chl[RECEIVE_EXT_CHL + i].id1);
- writew(0, &reg->chl[RECEIVE_EXT_CHL + i].dlc);
- writew(0x1FFF, &reg->msk[RECEIVE_EXT_CHL + i].amh);
- writew(0xFFFF, &reg->msk[RECEIVE_EXT_CHL + i].aml);
- }
-
- writew(BIT(TRANSMIT_CHL - 16), &reg->mc2);
- writew(BIT(RECEIVE_STD_CHL) + BIT(RECEIVE_EXT_CHL), &reg->mc1);
-
- priv->can.state = CAN_STATE_STOPPED;
-}
-
-static void bfin_can_set_normal_mode(struct net_device *dev)
-{
- struct bfin_can_priv *priv = netdev_priv(dev);
- struct bfin_can_regs __iomem *reg = priv->membase;
- int timeout = BFIN_CAN_TIMEOUT;
-
- /*
- * leave configuration mode
- */
- writew(readw(&reg->control) & ~CCR, &reg->control);
-
- while (readw(&reg->status) & CCA) {
- udelay(10);
- if (--timeout == 0) {
- netdev_err(dev, "fail to leave configuration mode\n");
- BUG();
- }
- }
-
- /*
- * clear _All_ tx and rx interrupts
- */
- writew(0xFFFF, &reg->mbtif1);
- writew(0xFFFF, &reg->mbtif2);
- writew(0xFFFF, &reg->mbrif1);
- writew(0xFFFF, &reg->mbrif2);
-
- /*
- * clear global interrupt status register
- */
- writew(0x7FF, &reg->gis); /* overwrites with '1' */
-
- /*
- * Initialize Interrupts
- * - set bits in the mailbox interrupt mask register
- * - global interrupt mask
- */
- writew(BIT(RECEIVE_STD_CHL) + BIT(RECEIVE_EXT_CHL), &reg->mbim1);
- writew(BIT(TRANSMIT_CHL - 16), &reg->mbim2);
-
- writew(EPIM | BOIM | RMLIM, &reg->gim);
-}
-
-static void bfin_can_start(struct net_device *dev)
-{
- struct bfin_can_priv *priv = netdev_priv(dev);
-
- /* enter reset mode */
- if (priv->can.state != CAN_STATE_STOPPED)
- bfin_can_set_reset_mode(dev);
-
- /* leave reset mode */
- bfin_can_set_normal_mode(dev);
-}
-
-static int bfin_can_set_mode(struct net_device *dev, enum can_mode mode)
-{
- switch (mode) {
- case CAN_MODE_START:
- bfin_can_start(dev);
- if (netif_queue_stopped(dev))
- netif_wake_queue(dev);
- break;
-
- default:
- return -EOPNOTSUPP;
- }
-
- return 0;
-}
-
-static int bfin_can_get_berr_counter(const struct net_device *dev,
- struct can_berr_counter *bec)
-{
- struct bfin_can_priv *priv = netdev_priv(dev);
- struct bfin_can_regs __iomem *reg = priv->membase;
-
- u16 cec = readw(&reg->cec);
-
- bec->txerr = cec >> 8;
- bec->rxerr = cec;
-
- return 0;
-}
-
-static int bfin_can_start_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct bfin_can_priv *priv = netdev_priv(dev);
- struct bfin_can_regs __iomem *reg = priv->membase;
- struct can_frame *cf = (struct can_frame *)skb->data;
- u8 dlc = cf->can_dlc;
- canid_t id = cf->can_id;
- u8 *data = cf->data;
- u16 val;
- int i;
-
- if (can_dropped_invalid_skb(dev, skb))
- return NETDEV_TX_OK;
-
- netif_stop_queue(dev);
-
- /* fill id */
- if (id & CAN_EFF_FLAG) {
- writew(id, &reg->chl[TRANSMIT_CHL].id0);
- val = ((id & 0x1FFF0000) >> 16) | IDE;
- } else
- val = (id << 2);
- if (id & CAN_RTR_FLAG)
- val |= RTR;
- writew(val | AME, &reg->chl[TRANSMIT_CHL].id1);
-
- /* fill payload */
- for (i = 0; i < 8; i += 2) {
- val = ((7 - i) < dlc ? (data[7 - i]) : 0) +
- ((6 - i) < dlc ? (data[6 - i] << 8) : 0);
- writew(val, &reg->chl[TRANSMIT_CHL].data[i]);
- }
-
- /* fill data length code */
- writew(dlc, &reg->chl[TRANSMIT_CHL].dlc);
-
- can_put_echo_skb(skb, dev, 0);
-
- /* set transmit request */
- writew(BIT(TRANSMIT_CHL - 16), &reg->trs2);
-
- return 0;
-}
-
-static void bfin_can_rx(struct net_device *dev, u16 isrc)
-{
- struct bfin_can_priv *priv = netdev_priv(dev);
- struct net_device_stats *stats = &dev->stats;
- struct bfin_can_regs __iomem *reg = priv->membase;
- struct can_frame *cf;
- struct sk_buff *skb;
- int obj;
- int i;
- u16 val;
-
- skb = alloc_can_skb(dev, &cf);
- if (skb == NULL)
- return;
-
- /* get id */
- if (isrc & BIT(RECEIVE_EXT_CHL)) {
- /* extended frame format (EFF) */
- cf->can_id = ((readw(&reg->chl[RECEIVE_EXT_CHL].id1)
- & 0x1FFF) << 16)
- + readw(&reg->chl[RECEIVE_EXT_CHL].id0);
- cf->can_id |= CAN_EFF_FLAG;
- obj = RECEIVE_EXT_CHL;
- } else {
- /* standard frame format (SFF) */
- cf->can_id = (readw(&reg->chl[RECEIVE_STD_CHL].id1)
- & 0x1ffc) >> 2;
- obj = RECEIVE_STD_CHL;
- }
- if (readw(&reg->chl[obj].id1) & RTR)
- cf->can_id |= CAN_RTR_FLAG;
-
- /* get data length code */
- cf->can_dlc = get_can_dlc(readw(&reg->chl[obj].dlc) & 0xF);
-
- /* get payload */
- for (i = 0; i < 8; i += 2) {
- val = readw(&reg->chl[obj].data[i]);
- cf->data[7 - i] = (7 - i) < cf->can_dlc ? val : 0;
- cf->data[6 - i] = (6 - i) < cf->can_dlc ? (val >> 8) : 0;
- }
-
- stats->rx_packets++;
- stats->rx_bytes += cf->can_dlc;
- netif_rx(skb);
-}
-
-static int bfin_can_err(struct net_device *dev, u16 isrc, u16 status)
-{
- struct bfin_can_priv *priv = netdev_priv(dev);
- struct bfin_can_regs __iomem *reg = priv->membase;
- struct net_device_stats *stats = &dev->stats;
- struct can_frame *cf;
- struct sk_buff *skb;
- enum can_state state = priv->can.state;
-
- skb = alloc_can_err_skb(dev, &cf);
- if (skb == NULL)
- return -ENOMEM;
-
- if (isrc & RMLIS) {
- /* data overrun interrupt */
- netdev_dbg(dev, "data overrun interrupt\n");
- cf->can_id |= CAN_ERR_CRTL;
- cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
- stats->rx_over_errors++;
- stats->rx_errors++;
- }
-
- if (isrc & BOIS) {
- netdev_dbg(dev, "bus-off mode interrupt\n");
- state = CAN_STATE_BUS_OFF;
- cf->can_id |= CAN_ERR_BUSOFF;
- priv->can.can_stats.bus_off++;
- can_bus_off(dev);
- }
-
- if (isrc & EPIS) {
- /* error passive interrupt */
- netdev_dbg(dev, "error passive interrupt\n");
- state = CAN_STATE_ERROR_PASSIVE;
- }
-
- if ((isrc & EWTIS) || (isrc & EWRIS)) {
- netdev_dbg(dev, "Error Warning Transmit/Receive Interrupt\n");
- state = CAN_STATE_ERROR_WARNING;
- }
-
- if (state != priv->can.state && (state == CAN_STATE_ERROR_WARNING ||
- state == CAN_STATE_ERROR_PASSIVE)) {
- u16 cec = readw(&reg->cec);
- u8 rxerr = cec;
- u8 txerr = cec >> 8;
-
- cf->can_id |= CAN_ERR_CRTL;
- if (state == CAN_STATE_ERROR_WARNING) {
- priv->can.can_stats.error_warning++;
- cf->data[1] = (txerr > rxerr) ?
- CAN_ERR_CRTL_TX_WARNING :
- CAN_ERR_CRTL_RX_WARNING;
- } else {
- priv->can.can_stats.error_passive++;
- cf->data[1] = (txerr > rxerr) ?
- CAN_ERR_CRTL_TX_PASSIVE :
- CAN_ERR_CRTL_RX_PASSIVE;
- }
- }
-
- if (status) {
- priv->can.can_stats.bus_error++;
-
- cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
-
- if (status & BEF)
- cf->data[2] |= CAN_ERR_PROT_BIT;
- else if (status & FER)
- cf->data[2] |= CAN_ERR_PROT_FORM;
- else if (status & SER)
- cf->data[2] |= CAN_ERR_PROT_STUFF;
- }
-
- priv->can.state = state;
-
- stats->rx_packets++;
- stats->rx_bytes += cf->can_dlc;
- netif_rx(skb);
-
- return 0;
-}
-
-static irqreturn_t bfin_can_interrupt(int irq, void *dev_id)
-{
- struct net_device *dev = dev_id;
- struct bfin_can_priv *priv = netdev_priv(dev);
- struct bfin_can_regs __iomem *reg = priv->membase;
- struct net_device_stats *stats = &dev->stats;
- u16 status, isrc;
-
- if ((irq == priv->tx_irq) && readw(&reg->mbtif2)) {
- /* transmission complete interrupt */
- writew(0xFFFF, &reg->mbtif2);
- stats->tx_packets++;
- stats->tx_bytes += readw(&reg->chl[TRANSMIT_CHL].dlc);
- can_get_echo_skb(dev, 0);
- netif_wake_queue(dev);
- } else if ((irq == priv->rx_irq) && readw(&reg->mbrif1)) {
- /* receive interrupt */
- isrc = readw(&reg->mbrif1);
- writew(0xFFFF, &reg->mbrif1);
- bfin_can_rx(dev, isrc);
- } else if ((irq == priv->err_irq) && readw(&reg->gis)) {
- /* error interrupt */
- isrc = readw(&reg->gis);
- status = readw(&reg->esr);
- writew(0x7FF, &reg->gis);
- bfin_can_err(dev, isrc, status);
- } else {
- return IRQ_NONE;
- }
-
- return IRQ_HANDLED;
-}
-
-static int bfin_can_open(struct net_device *dev)
-{
- struct bfin_can_priv *priv = netdev_priv(dev);
- int err;
-
- /* set chip into reset mode */
- bfin_can_set_reset_mode(dev);
-
- /* common open */
- err = open_candev(dev);
- if (err)
- goto exit_open;
-
- /* register interrupt handler */
- err = request_irq(priv->rx_irq, &bfin_can_interrupt, 0,
- "bfin-can-rx", dev);
- if (err)
- goto exit_rx_irq;
- err = request_irq(priv->tx_irq, &bfin_can_interrupt, 0,
- "bfin-can-tx", dev);
- if (err)
- goto exit_tx_irq;
- err = request_irq(priv->err_irq, &bfin_can_interrupt, 0,
- "bfin-can-err", dev);
- if (err)
- goto exit_err_irq;
-
- bfin_can_start(dev);
-
- netif_start_queue(dev);
-
- return 0;
-
-exit_err_irq:
- free_irq(priv->tx_irq, dev);
-exit_tx_irq:
- free_irq(priv->rx_irq, dev);
-exit_rx_irq:
- close_candev(dev);
-exit_open:
- return err;
-}
-
-static int bfin_can_close(struct net_device *dev)
-{
- struct bfin_can_priv *priv = netdev_priv(dev);
-
- netif_stop_queue(dev);
- bfin_can_set_reset_mode(dev);
-
- close_candev(dev);
-
- free_irq(priv->rx_irq, dev);
- free_irq(priv->tx_irq, dev);
- free_irq(priv->err_irq, dev);
-
- return 0;
-}
-
-static struct net_device *alloc_bfin_candev(void)
-{
- struct net_device *dev;
- struct bfin_can_priv *priv;
-
- dev = alloc_candev(sizeof(*priv), TX_ECHO_SKB_MAX);
- if (!dev)
- return NULL;
-
- priv = netdev_priv(dev);
-
- priv->dev = dev;
- priv->can.bittiming_const = &bfin_can_bittiming_const;
- priv->can.do_set_bittiming = bfin_can_set_bittiming;
- priv->can.do_set_mode = bfin_can_set_mode;
- priv->can.do_get_berr_counter = bfin_can_get_berr_counter;
- priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;
-
- return dev;
-}
-
-static const struct net_device_ops bfin_can_netdev_ops = {
- .ndo_open = bfin_can_open,
- .ndo_stop = bfin_can_close,
- .ndo_start_xmit = bfin_can_start_xmit,
- .ndo_change_mtu = can_change_mtu,
-};
-
-static int bfin_can_probe(struct platform_device *pdev)
-{
- int err;
- struct net_device *dev;
- struct bfin_can_priv *priv;
- struct resource *res_mem, *rx_irq, *tx_irq, *err_irq;
- unsigned short *pdata;
-
- pdata = dev_get_platdata(&pdev->dev);
- if (!pdata) {
- dev_err(&pdev->dev, "No platform data provided!\n");
- err = -EINVAL;
- goto exit;
- }
-
- res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- rx_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- tx_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
- err_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 2);
- if (!res_mem || !rx_irq || !tx_irq || !err_irq) {
- err = -EINVAL;
- goto exit;
- }
-
- /* request peripheral pins */
- err = peripheral_request_list(pdata, dev_name(&pdev->dev));
- if (err)
- goto exit;
-
- dev = alloc_bfin_candev();
- if (!dev) {
- err = -ENOMEM;
- goto exit_peri_pin_free;
- }
-
- priv = netdev_priv(dev);
-
- priv->membase = devm_ioremap_resource(&pdev->dev, res_mem);
- if (IS_ERR(priv->membase)) {
- err = PTR_ERR(priv->membase);
- goto exit_peri_pin_free;
- }
-
- priv->rx_irq = rx_irq->start;
- priv->tx_irq = tx_irq->start;
- priv->err_irq = err_irq->start;
- priv->pin_list = pdata;
- priv->can.clock.freq = get_sclk();
-
- platform_set_drvdata(pdev, dev);
- SET_NETDEV_DEV(dev, &pdev->dev);
-
- dev->flags |= IFF_ECHO; /* we support local echo */
- dev->netdev_ops = &bfin_can_netdev_ops;
-
- bfin_can_set_reset_mode(dev);
-
- err = register_candev(dev);
- if (err) {
- dev_err(&pdev->dev, "registering failed (err=%d)\n", err);
- goto exit_candev_free;
- }
-
- dev_info(&pdev->dev,
- "%s device registered"
- "(&reg_base=%p, rx_irq=%d, tx_irq=%d, err_irq=%d, sclk=%d)\n",
- DRV_NAME, priv->membase, priv->rx_irq,
- priv->tx_irq, priv->err_irq, priv->can.clock.freq);
- return 0;
-
-exit_candev_free:
- free_candev(dev);
-exit_peri_pin_free:
- peripheral_free_list(pdata);
-exit:
- return err;
-}
-
-static int bfin_can_remove(struct platform_device *pdev)
-{
- struct net_device *dev = platform_get_drvdata(pdev);
- struct bfin_can_priv *priv = netdev_priv(dev);
-
- bfin_can_set_reset_mode(dev);
-
- unregister_candev(dev);
-
- peripheral_free_list(priv->pin_list);
-
- free_candev(dev);
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int bfin_can_suspend(struct platform_device *pdev, pm_message_t mesg)
-{
- struct net_device *dev = platform_get_drvdata(pdev);
- struct bfin_can_priv *priv = netdev_priv(dev);
- struct bfin_can_regs __iomem *reg = priv->membase;
- int timeout = BFIN_CAN_TIMEOUT;
-
- if (netif_running(dev)) {
- /* enter sleep mode */
- writew(readw(&reg->control) | SMR, &reg->control);
- while (!(readw(&reg->intr) & SMACK)) {
- udelay(10);
- if (--timeout == 0) {
- netdev_err(dev, "fail to enter sleep mode\n");
- BUG();
- }
- }
- }
-
- return 0;
-}
-
-static int bfin_can_resume(struct platform_device *pdev)
-{
- struct net_device *dev = platform_get_drvdata(pdev);
- struct bfin_can_priv *priv = netdev_priv(dev);
- struct bfin_can_regs __iomem *reg = priv->membase;
-
- if (netif_running(dev)) {
- /* leave sleep mode */
- writew(0, &reg->intr);
- }
-
- return 0;
-}
-#else
-#define bfin_can_suspend NULL
-#define bfin_can_resume NULL
-#endif /* CONFIG_PM */
-
-static struct platform_driver bfin_can_driver = {
- .probe = bfin_can_probe,
- .remove = bfin_can_remove,
- .suspend = bfin_can_suspend,
- .resume = bfin_can_resume,
- .driver = {
- .name = DRV_NAME,
- },
-};
-
-module_platform_driver(bfin_can_driver);
-
-MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Blackfin on-chip CAN netdevice driver");
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/net/can/cc770/cc770.c b/drivers/net/can/cc770/cc770.c
index 6da69af103e6..d4dd4da23997 100644
--- a/drivers/net/can/cc770/cc770.c
+++ b/drivers/net/can/cc770/cc770.c
@@ -67,12 +67,12 @@ MODULE_DESCRIPTION(KBUILD_MODNAME "CAN netdevice driver");
* otherwise 11 bit SFF messages.
*/
static int msgobj15_eff;
-module_param(msgobj15_eff, int, S_IRUGO);
+module_param(msgobj15_eff, int, 0444);
MODULE_PARM_DESC(msgobj15_eff, "Extended 29-bit frames for message object 15 "
"(default: 11-bit standard frames)");
static int i82527_compat;
-module_param(i82527_compat, int, S_IRUGO);
+module_param(i82527_compat, int, 0444);
MODULE_PARM_DESC(i82527_compat, "Strict Intel 82527 comptibility mode "
"without using additional functions");
diff --git a/drivers/net/can/cc770/cc770_isa.c b/drivers/net/can/cc770/cc770_isa.c
index 3a30fd3b4498..fcd34698074f 100644
--- a/drivers/net/can/cc770/cc770_isa.c
+++ b/drivers/net/can/cc770/cc770_isa.c
@@ -82,29 +82,29 @@ static u8 cor[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
static u8 bcr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
static int indirect[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1};
-module_param_hw_array(port, ulong, ioport, NULL, S_IRUGO);
+module_param_hw_array(port, ulong, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "I/O port number");
-module_param_hw_array(mem, ulong, iomem, NULL, S_IRUGO);
+module_param_hw_array(mem, ulong, iomem, NULL, 0444);
MODULE_PARM_DESC(mem, "I/O memory address");
-module_param_hw_array(indirect, int, ioport, NULL, S_IRUGO);
+module_param_hw_array(indirect, int, ioport, NULL, 0444);
MODULE_PARM_DESC(indirect, "Indirect access via address and data port");
-module_param_hw_array(irq, int, irq, NULL, S_IRUGO);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ number");
-module_param_array(clk, int, NULL, S_IRUGO);
+module_param_array(clk, int, NULL, 0444);
MODULE_PARM_DESC(clk, "External oscillator clock frequency "
"(default=16000000 [16 MHz])");
-module_param_array(cir, byte, NULL, S_IRUGO);
+module_param_array(cir, byte, NULL, 0444);
MODULE_PARM_DESC(cir, "CPU interface register (default=0x40 [DSC])");
-module_param_array(cor, byte, NULL, S_IRUGO);
+module_param_array(cor, byte, NULL, 0444);
MODULE_PARM_DESC(cor, "Clockout register (default=0x00)");
-module_param_array(bcr, byte, NULL, S_IRUGO);
+module_param_array(bcr, byte, NULL, 0444);
MODULE_PARM_DESC(bcr, "Bus configuration register (default=0x40 [CBY])");
#define CC770_IOSIZE 0x20
diff --git a/drivers/net/can/grcan.c b/drivers/net/can/grcan.c
index 897c6b113d3f..2d3046afa80d 100644
--- a/drivers/net/can/grcan.c
+++ b/drivers/net/can/grcan.c
@@ -1484,7 +1484,7 @@ static netdev_tx_t grcan_start_xmit(struct sk_buff *skb,
} \
} \
module_param_named(name, grcan_module_config.name, \
- mtype, S_IRUGO); \
+ mtype, 0444); \
MODULE_PARM_DESC(name, desc)
#define GRCAN_CONFIG_ATTR(name, desc) \
@@ -1513,7 +1513,7 @@ static netdev_tx_t grcan_start_xmit(struct sk_buff *skb,
struct grcan_priv *priv = netdev_priv(dev); \
return sprintf(buf, "%d\n", priv->config.name); \
} \
- static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, \
+ static DEVICE_ATTR(name, 0644, \
grcan_show_##name, \
grcan_store_##name); \
GRCAN_MODULE_PARAM(name, ushort, GRCAN_NOT_BOOL, desc)
diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c
index 12a53c8e8e1d..adfdb66a486e 100644
--- a/drivers/net/can/janz-ican3.c
+++ b/drivers/net/can/janz-ican3.c
@@ -1865,9 +1865,9 @@ static ssize_t ican3_sysfs_show_fwinfo(struct device *dev,
return scnprintf(buf, PAGE_SIZE, "%s\n", mod->fwinfo);
}
-static DEVICE_ATTR(termination, S_IWUSR | S_IRUGO, ican3_sysfs_show_term,
- ican3_sysfs_set_term);
-static DEVICE_ATTR(fwinfo, S_IRUSR | S_IRUGO, ican3_sysfs_show_fwinfo, NULL);
+static DEVICE_ATTR(termination, 0644, ican3_sysfs_show_term,
+ ican3_sysfs_set_term);
+static DEVICE_ATTR(fwinfo, 0444, ican3_sysfs_show_fwinfo, NULL);
static struct attribute *ican3_sysfs_attrs[] = {
&dev_attr_termination.attr,
diff --git a/drivers/net/can/sja1000/sja1000_isa.c b/drivers/net/can/sja1000/sja1000_isa.c
index a89c1e92554d..1a2ae6ce8d87 100644
--- a/drivers/net/can/sja1000/sja1000_isa.c
+++ b/drivers/net/can/sja1000/sja1000_isa.c
@@ -48,27 +48,27 @@ static unsigned char ocr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
static int indirect[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1};
static spinlock_t indirect_lock[MAXDEV]; /* lock for indirect access mode */
-module_param_hw_array(port, ulong, ioport, NULL, S_IRUGO);
+module_param_hw_array(port, ulong, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "I/O port number");
-module_param_hw_array(mem, ulong, iomem, NULL, S_IRUGO);
+module_param_hw_array(mem, ulong, iomem, NULL, 0444);
MODULE_PARM_DESC(mem, "I/O memory address");
-module_param_hw_array(indirect, int, ioport, NULL, S_IRUGO);
+module_param_hw_array(indirect, int, ioport, NULL, 0444);
MODULE_PARM_DESC(indirect, "Indirect access via address and data port");
-module_param_hw_array(irq, int, irq, NULL, S_IRUGO);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ number");
-module_param_array(clk, int, NULL, S_IRUGO);
+module_param_array(clk, int, NULL, 0444);
MODULE_PARM_DESC(clk, "External oscillator clock frequency "
"(default=16000000 [16 MHz])");
-module_param_array(cdr, byte, NULL, S_IRUGO);
+module_param_array(cdr, byte, NULL, 0444);
MODULE_PARM_DESC(cdr, "Clock divider register "
"(default=0x48 [CDR_CBP | CDR_CLK_OFF])");
-module_param_array(ocr, byte, NULL, S_IRUGO);
+module_param_array(ocr, byte, NULL, 0444);
MODULE_PARM_DESC(ocr, "Output control register "
"(default=0x18 [OCR_TX0_PUSHPULL])");
diff --git a/drivers/net/can/softing/softing_main.c b/drivers/net/can/softing/softing_main.c
index 5f64deec9f6c..e22696190583 100644
--- a/drivers/net/can/softing/softing_main.c
+++ b/drivers/net/can/softing/softing_main.c
@@ -601,8 +601,8 @@ static ssize_t store_output(struct device *dev, struct device_attribute *attr,
return count;
}
-static const DEVICE_ATTR(chip, S_IRUGO, show_chip, NULL);
-static const DEVICE_ATTR(output, S_IRUGO | S_IWUSR, show_output, store_output);
+static const DEVICE_ATTR(chip, 0444, show_chip, NULL);
+static const DEVICE_ATTR(output, 0644, show_output, store_output);
static const struct attribute *const netdev_sysfs_attrs[] = {
&dev_attr_chip.attr,
diff --git a/drivers/net/can/spi/mcp251x.c b/drivers/net/can/spi/mcp251x.c
index 98d118b3aaf4..e90817608645 100644
--- a/drivers/net/can/spi/mcp251x.c
+++ b/drivers/net/can/spi/mcp251x.c
@@ -220,7 +220,7 @@
#define DEVICE_NAME "mcp251x"
static int mcp251x_enable_dma; /* Enable SPI DMA. Default: 0 (Off) */
-module_param(mcp251x_enable_dma, int, S_IRUGO);
+module_param(mcp251x_enable_dma, int, 0444);
MODULE_PARM_DESC(mcp251x_enable_dma, "Enable SPI DMA. Default: 0 (Off)");
static const struct can_bittiming_const mcp251x_bittiming_const = {
diff --git a/drivers/net/can/usb/esd_usb2.c b/drivers/net/can/usb/esd_usb2.c
index c6dcf93675c0..5820fd5b69b5 100644
--- a/drivers/net/can/usb/esd_usb2.c
+++ b/drivers/net/can/usb/esd_usb2.c
@@ -496,7 +496,7 @@ static ssize_t show_firmware(struct device *d,
(dev->version >> 8) & 0xf,
dev->version & 0xff);
}
-static DEVICE_ATTR(firmware, S_IRUGO, show_firmware, NULL);
+static DEVICE_ATTR(firmware, 0444, show_firmware, NULL);
static ssize_t show_hardware(struct device *d,
struct device_attribute *attr, char *buf)
@@ -509,7 +509,7 @@ static ssize_t show_hardware(struct device *d,
(dev->version >> 24) & 0xf,
(dev->version >> 16) & 0xff);
}
-static DEVICE_ATTR(hardware, S_IRUGO, show_hardware, NULL);
+static DEVICE_ATTR(hardware, 0444, show_hardware, NULL);
static ssize_t show_nets(struct device *d,
struct device_attribute *attr, char *buf)
@@ -519,7 +519,7 @@ static ssize_t show_nets(struct device *d,
return sprintf(buf, "%d", dev->net_count);
}
-static DEVICE_ATTR(nets, S_IRUGO, show_nets, NULL);
+static DEVICE_ATTR(nets, 0444, show_nets, NULL);
static int esd_usb2_send_msg(struct esd_usb2 *dev, struct esd_usb2_msg *msg)
{
diff --git a/drivers/net/can/vcan.c b/drivers/net/can/vcan.c
index c2b04f505e16..d200a5b0651c 100644
--- a/drivers/net/can/vcan.c
+++ b/drivers/net/can/vcan.c
@@ -65,7 +65,7 @@ MODULE_ALIAS_RTNL_LINK(DRV_NAME);
*/
static bool echo; /* echo testing. Default: 0 (Off) */
-module_param(echo, bool, S_IRUGO);
+module_param(echo, bool, 0444);
MODULE_PARM_DESC(echo, "Echo sent frames (for testing). Default: 0 (Off)");
diff --git a/drivers/net/cris/Makefile b/drivers/net/cris/Makefile
deleted file mode 100644
index b4e8932227b6..000000000000
--- a/drivers/net/cris/Makefile
+++ /dev/null
@@ -1 +0,0 @@
-obj-$(CONFIG_ETRAX_ARCH_V10) += eth_v10.o
diff --git a/drivers/net/cris/eth_v10.c b/drivers/net/cris/eth_v10.c
deleted file mode 100644
index 8b1a859f5140..000000000000
--- a/drivers/net/cris/eth_v10.c
+++ /dev/null
@@ -1,1742 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * e100net.c: A network driver for the ETRAX 100LX network controller.
- *
- * Copyright (c) 1998-2002 Axis Communications AB.
- *
- * The outline of this driver comes from skeleton.c.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/string.h>
-#include <linux/spinlock.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/bitops.h>
-
-#include <linux/if.h>
-#include <linux/mii.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/ethtool.h>
-
-#include <arch/svinto.h>/* DMA and register descriptions */
-#include <asm/io.h> /* CRIS_LED_* I/O functions */
-#include <asm/irq.h>
-#include <asm/dma.h>
-#include <asm/ethernet.h>
-#include <asm/cache.h>
-#include <arch/io_interface_mux.h>
-
-//#define ETHDEBUG
-#define D(x)
-
-/*
- * The name of the card. Is used for messages and in the requests for
- * io regions, irqs and dma channels
- */
-
-static const char* cardname = "ETRAX 100LX built-in ethernet controller";
-
-/* A default ethernet address. Highlevel SW will set the real one later */
-
-static struct sockaddr default_mac = {
- 0,
- { 0x00, 0x40, 0x8C, 0xCD, 0x00, 0x00 }
-};
-
-/* Information that need to be kept for each board. */
-struct net_local {
- struct mii_if_info mii_if;
-
- /* Tx control lock. This protects the transmit buffer ring
- * state along with the "tx full" state of the driver. This
- * means all netif_queue flow control actions are protected
- * by this lock as well.
- */
- spinlock_t lock;
-
- spinlock_t led_lock; /* Protect LED state */
- spinlock_t transceiver_lock; /* Protect transceiver state. */
-};
-
-typedef struct etrax_eth_descr
-{
- etrax_dma_descr descr;
- struct sk_buff* skb;
-} etrax_eth_descr;
-
-/* Some transceivers requires special handling */
-struct transceiver_ops
-{
- unsigned int oui;
- void (*check_speed)(struct net_device* dev);
- void (*check_duplex)(struct net_device* dev);
-};
-
-/* Duplex settings */
-enum duplex
-{
- half,
- full,
- autoneg
-};
-
-/* Dma descriptors etc. */
-
-#define MAX_MEDIA_DATA_SIZE 1522
-
-#define MIN_PACKET_LEN 46
-#define ETHER_HEAD_LEN 14
-
-/*
-** MDIO constants.
-*/
-#define MDIO_START 0x1
-#define MDIO_READ 0x2
-#define MDIO_WRITE 0x1
-#define MDIO_PREAMBLE 0xfffffffful
-
-/* Broadcom specific */
-#define MDIO_AUX_CTRL_STATUS_REG 0x18
-#define MDIO_BC_FULL_DUPLEX_IND 0x1
-#define MDIO_BC_SPEED 0x2
-
-/* TDK specific */
-#define MDIO_TDK_DIAGNOSTIC_REG 18
-#define MDIO_TDK_DIAGNOSTIC_RATE 0x400
-#define MDIO_TDK_DIAGNOSTIC_DPLX 0x800
-
-/*Intel LXT972A specific*/
-#define MDIO_INT_STATUS_REG_2 0x0011
-#define MDIO_INT_FULL_DUPLEX_IND (1 << 9)
-#define MDIO_INT_SPEED (1 << 14)
-
-/* Network flash constants */
-#define NET_FLASH_TIME (HZ/50) /* 20 ms */
-#define NET_FLASH_PAUSE (HZ/100) /* 10 ms */
-#define NET_LINK_UP_CHECK_INTERVAL (2*HZ) /* 2 s */
-#define NET_DUPLEX_CHECK_INTERVAL (2*HZ) /* 2 s */
-
-#define NO_NETWORK_ACTIVITY 0
-#define NETWORK_ACTIVITY 1
-
-#define NBR_OF_RX_DESC 32
-#define NBR_OF_TX_DESC 16
-
-/* Large packets are sent directly to upper layers while small packets are */
-/* copied (to reduce memory waste). The following constant decides the breakpoint */
-#define RX_COPYBREAK 256
-
-/* Due to a chip bug we need to flush the cache when descriptors are returned */
-/* to the DMA. To decrease performance impact we return descriptors in chunks. */
-/* The following constant determines the number of descriptors to return. */
-#define RX_QUEUE_THRESHOLD NBR_OF_RX_DESC/2
-
-#define GET_BIT(bit,val) (((val) >> (bit)) & 0x01)
-
-/* Define some macros to access ETRAX 100 registers */
-#define SETF(var, reg, field, val) var = (var & ~IO_MASK_(reg##_, field##_)) | \
- IO_FIELD_(reg##_, field##_, val)
-#define SETS(var, reg, field, val) var = (var & ~IO_MASK_(reg##_, field##_)) | \
- IO_STATE_(reg##_, field##_, _##val)
-
-static etrax_eth_descr *myNextRxDesc; /* Points to the next descriptor to
- to be processed */
-static etrax_eth_descr *myLastRxDesc; /* The last processed descriptor */
-
-static etrax_eth_descr RxDescList[NBR_OF_RX_DESC] __attribute__ ((aligned(32)));
-
-static etrax_eth_descr* myFirstTxDesc; /* First packet not yet sent */
-static etrax_eth_descr* myLastTxDesc; /* End of send queue */
-static etrax_eth_descr* myNextTxDesc; /* Next descriptor to use */
-static etrax_eth_descr TxDescList[NBR_OF_TX_DESC] __attribute__ ((aligned(32)));
-
-static unsigned int network_rec_config_shadow = 0;
-
-static unsigned int network_tr_ctrl_shadow = 0;
-
-/* Timers */
-static void e100_check_speed(struct timer_list *unused);
-static void e100_clear_network_leds(struct timer_list *unused);
-static void e100_check_duplex(struct timer_list *unused);
-static DEFINE_TIMER(speed_timer, e100_check_speed);
-static DEFINE_TIMER(clear_led_timer, e100_clear_network_leds);
-static DEFINE_TIMER(duplex_timer, e100_check_duplex);
-static struct net_device *timer_dev;
-
-/* Network speed indication. */
-static int current_speed; /* Speed read from transceiver */
-static int current_speed_selection; /* Speed selected by user */
-static unsigned long led_next_time;
-static int led_active;
-static int rx_queue_len;
-
-/* Duplex */
-static int full_duplex;
-static enum duplex current_duplex;
-
-/* Index to functions, as function prototypes. */
-
-static int etrax_ethernet_init(void);
-
-static int e100_open(struct net_device *dev);
-static int e100_set_mac_address(struct net_device *dev, void *addr);
-static int e100_send_packet(struct sk_buff *skb, struct net_device *dev);
-static irqreturn_t e100rxtx_interrupt(int irq, void *dev_id);
-static irqreturn_t e100nw_interrupt(int irq, void *dev_id);
-static void e100_rx(struct net_device *dev);
-static int e100_close(struct net_device *dev);
-static int e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
-static int e100_set_config(struct net_device* dev, struct ifmap* map);
-static void e100_tx_timeout(struct net_device *dev);
-static struct net_device_stats *e100_get_stats(struct net_device *dev);
-static void set_multicast_list(struct net_device *dev);
-static void e100_hardware_send_packet(struct net_local* np, char *buf, int length);
-static void update_rx_stats(struct net_device_stats *);
-static void update_tx_stats(struct net_device_stats *);
-static int e100_probe_transceiver(struct net_device* dev);
-
-static void e100_set_speed(struct net_device* dev, unsigned long speed);
-static void e100_set_duplex(struct net_device* dev, enum duplex);
-static void e100_negotiate(struct net_device* dev);
-
-static int e100_get_mdio_reg(struct net_device *dev, int phy_id, int location);
-static void e100_set_mdio_reg(struct net_device *dev, int phy_id, int location, int value);
-
-static void e100_send_mdio_cmd(unsigned short cmd, int write_cmd);
-static void e100_send_mdio_bit(unsigned char bit);
-static unsigned char e100_receive_mdio_bit(void);
-static void e100_reset_transceiver(struct net_device* net);
-
-static void e100_set_network_leds(int active);
-
-static const struct ethtool_ops e100_ethtool_ops;
-#if defined(CONFIG_ETRAX_NO_PHY)
-static void dummy_check_speed(struct net_device* dev);
-static void dummy_check_duplex(struct net_device* dev);
-#else
-static void broadcom_check_speed(struct net_device* dev);
-static void broadcom_check_duplex(struct net_device* dev);
-static void tdk_check_speed(struct net_device* dev);
-static void tdk_check_duplex(struct net_device* dev);
-static void intel_check_speed(struct net_device* dev);
-static void intel_check_duplex(struct net_device* dev);
-static void generic_check_speed(struct net_device* dev);
-static void generic_check_duplex(struct net_device* dev);
-#endif
-#ifdef CONFIG_NET_POLL_CONTROLLER
-static void e100_netpoll(struct net_device* dev);
-#endif
-
-static int autoneg_normal = 1;
-
-struct transceiver_ops transceivers[] =
-{
-#if defined(CONFIG_ETRAX_NO_PHY)
- {0x0000, dummy_check_speed, dummy_check_duplex} /* Dummy */
-#else
- {0x1018, broadcom_check_speed, broadcom_check_duplex}, /* Broadcom */
- {0xC039, tdk_check_speed, tdk_check_duplex}, /* TDK 2120 */
- {0x039C, tdk_check_speed, tdk_check_duplex}, /* TDK 2120C */
- {0x04de, intel_check_speed, intel_check_duplex}, /* Intel LXT972A*/
- {0x0000, generic_check_speed, generic_check_duplex} /* Generic, must be last */
-#endif
-};
-
-struct transceiver_ops* transceiver = &transceivers[0];
-
-static const struct net_device_ops e100_netdev_ops = {
- .ndo_open = e100_open,
- .ndo_stop = e100_close,
- .ndo_start_xmit = e100_send_packet,
- .ndo_tx_timeout = e100_tx_timeout,
- .ndo_get_stats = e100_get_stats,
- .ndo_set_rx_mode = set_multicast_list,
- .ndo_do_ioctl = e100_ioctl,
- .ndo_set_mac_address = e100_set_mac_address,
- .ndo_validate_addr = eth_validate_addr,
- .ndo_set_config = e100_set_config,
-#ifdef CONFIG_NET_POLL_CONTROLLER
- .ndo_poll_controller = e100_netpoll,
-#endif
-};
-
-#define tx_done(dev) (*R_DMA_CH0_CMD == 0)
-
-/*
- * Check for a network adaptor of this type, and return '0' if one exists.
- * If dev->base_addr == 0, probe all likely locations.
- * If dev->base_addr == 1, always return failure.
- * If dev->base_addr == 2, allocate space for the device and return success
- * (detachable devices only).
- */
-
-static int __init
-etrax_ethernet_init(void)
-{
- struct net_device *dev;
- struct net_local* np;
- int i, err;
-
- printk(KERN_INFO
- "ETRAX 100LX 10/100MBit ethernet v2.0 (c) 1998-2007 Axis Communications AB\n");
-
- if (cris_request_io_interface(if_eth, cardname)) {
- printk(KERN_CRIT "etrax_ethernet_init failed to get IO interface\n");
- return -EBUSY;
- }
-
- dev = alloc_etherdev(sizeof(struct net_local));
- if (!dev)
- return -ENOMEM;
-
- np = netdev_priv(dev);
-
- /* we do our own locking */
- dev->features |= NETIF_F_LLTX;
-
- dev->base_addr = (unsigned int)R_NETWORK_SA_0; /* just to have something to show */
-
- /* now setup our etrax specific stuff */
-
- dev->irq = NETWORK_DMA_RX_IRQ_NBR; /* we really use DMATX as well... */
- dev->dma = NETWORK_RX_DMA_NBR;
-
- /* fill in our handlers so the network layer can talk to us in the future */
-
- dev->ethtool_ops = &e100_ethtool_ops;
- dev->netdev_ops = &e100_netdev_ops;
-
- spin_lock_init(&np->lock);
- spin_lock_init(&np->led_lock);
- spin_lock_init(&np->transceiver_lock);
-
- /* Initialise the list of Etrax DMA-descriptors */
-
- /* Initialise receive descriptors */
-
- for (i = 0; i < NBR_OF_RX_DESC; i++) {
- /* Allocate two extra cachelines to make sure that buffer used
- * by DMA does not share cacheline with any other data (to
- * avoid cache bug)
- */
- RxDescList[i].skb = dev_alloc_skb(MAX_MEDIA_DATA_SIZE + 2 * L1_CACHE_BYTES);
- if (!RxDescList[i].skb)
- return -ENOMEM;
- RxDescList[i].descr.ctrl = 0;
- RxDescList[i].descr.sw_len = MAX_MEDIA_DATA_SIZE;
- RxDescList[i].descr.next = virt_to_phys(&RxDescList[i + 1]);
- RxDescList[i].descr.buf = L1_CACHE_ALIGN(virt_to_phys(RxDescList[i].skb->data));
- RxDescList[i].descr.status = 0;
- RxDescList[i].descr.hw_len = 0;
- prepare_rx_descriptor(&RxDescList[i].descr);
- }
-
- RxDescList[NBR_OF_RX_DESC - 1].descr.ctrl = d_eol;
- RxDescList[NBR_OF_RX_DESC - 1].descr.next = virt_to_phys(&RxDescList[0]);
- rx_queue_len = 0;
-
- /* Initialize transmit descriptors */
- for (i = 0; i < NBR_OF_TX_DESC; i++) {
- TxDescList[i].descr.ctrl = 0;
- TxDescList[i].descr.sw_len = 0;
- TxDescList[i].descr.next = virt_to_phys(&TxDescList[i + 1].descr);
- TxDescList[i].descr.buf = 0;
- TxDescList[i].descr.status = 0;
- TxDescList[i].descr.hw_len = 0;
- TxDescList[i].skb = 0;
- }
-
- TxDescList[NBR_OF_TX_DESC - 1].descr.ctrl = d_eol;
- TxDescList[NBR_OF_TX_DESC - 1].descr.next = virt_to_phys(&TxDescList[0].descr);
-
- /* Initialise initial pointers */
-
- myNextRxDesc = &RxDescList[0];
- myLastRxDesc = &RxDescList[NBR_OF_RX_DESC - 1];
- myFirstTxDesc = &TxDescList[0];
- myNextTxDesc = &TxDescList[0];
- myLastTxDesc = &TxDescList[NBR_OF_TX_DESC - 1];
-
- /* Register device */
- err = register_netdev(dev);
- if (err) {
- free_netdev(dev);
- return err;
- }
-
- /* set the default MAC address */
-
- e100_set_mac_address(dev, &default_mac);
-
- /* Initialize speed indicator stuff. */
-
- current_speed = 10;
- current_speed_selection = 0; /* Auto */
- speed_timer.expires = jiffies + NET_LINK_UP_CHECK_INTERVAL;
-
- full_duplex = 0;
- current_duplex = autoneg;
- duplex_timer.expires = jiffies + NET_DUPLEX_CHECK_INTERVAL;
-
- timer_dev = dev;
-
- /* Initialize mii interface */
- np->mii_if.phy_id_mask = 0x1f;
- np->mii_if.reg_num_mask = 0x1f;
- np->mii_if.dev = dev;
- np->mii_if.mdio_read = e100_get_mdio_reg;
- np->mii_if.mdio_write = e100_set_mdio_reg;
-
- /* Initialize group address registers to make sure that no */
- /* unwanted addresses are matched */
- *R_NETWORK_GA_0 = 0x00000000;
- *R_NETWORK_GA_1 = 0x00000000;
-
- /* Initialize next time the led can flash */
- led_next_time = jiffies;
- return 0;
-}
-device_initcall(etrax_ethernet_init)
-
-/* set MAC address of the interface. called from the core after a
- * SIOCSIFADDR ioctl, and from the bootup above.
- */
-
-static int
-e100_set_mac_address(struct net_device *dev, void *p)
-{
- struct net_local *np = netdev_priv(dev);
- struct sockaddr *addr = p;
-
- spin_lock(&np->lock); /* preemption protection */
-
- /* remember it */
-
- memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
-
- /* Write it to the hardware.
- * Note the way the address is wrapped:
- * *R_NETWORK_SA_0 = a0_0 | (a0_1 << 8) | (a0_2 << 16) | (a0_3 << 24);
- * *R_NETWORK_SA_1 = a0_4 | (a0_5 << 8);
- */
-
- *R_NETWORK_SA_0 = dev->dev_addr[0] | (dev->dev_addr[1] << 8) |
- (dev->dev_addr[2] << 16) | (dev->dev_addr[3] << 24);
- *R_NETWORK_SA_1 = dev->dev_addr[4] | (dev->dev_addr[5] << 8);
- *R_NETWORK_SA_2 = 0;
-
- /* show it in the log as well */
-
- printk(KERN_INFO "%s: changed MAC to %pM\n", dev->name, dev->dev_addr);
-
- spin_unlock(&np->lock);
-
- return 0;
-}
-
-/*
- * Open/initialize the board. This is called (in the current kernel)
- * sometime after booting when the 'ifconfig' program is run.
- *
- * This routine should set everything up anew at each open, even
- * registers that "should" only need to be set once at boot, so that
- * there is non-reboot way to recover if something goes wrong.
- */
-
-static int
-e100_open(struct net_device *dev)
-{
- unsigned long flags;
-
- /* enable the MDIO output pin */
-
- *R_NETWORK_MGM_CTRL = IO_STATE(R_NETWORK_MGM_CTRL, mdoe, enable);
-
- *R_IRQ_MASK0_CLR =
- IO_STATE(R_IRQ_MASK0_CLR, overrun, clr) |
- IO_STATE(R_IRQ_MASK0_CLR, underrun, clr) |
- IO_STATE(R_IRQ_MASK0_CLR, excessive_col, clr);
-
- /* clear dma0 and 1 eop and descr irq masks */
- *R_IRQ_MASK2_CLR =
- IO_STATE(R_IRQ_MASK2_CLR, dma0_descr, clr) |
- IO_STATE(R_IRQ_MASK2_CLR, dma0_eop, clr) |
- IO_STATE(R_IRQ_MASK2_CLR, dma1_descr, clr) |
- IO_STATE(R_IRQ_MASK2_CLR, dma1_eop, clr);
-
- /* Reset and wait for the DMA channels */
-
- RESET_DMA(NETWORK_TX_DMA_NBR);
- RESET_DMA(NETWORK_RX_DMA_NBR);
- WAIT_DMA(NETWORK_TX_DMA_NBR);
- WAIT_DMA(NETWORK_RX_DMA_NBR);
-
- /* Initialise the etrax network controller */
-
- /* allocate the irq corresponding to the receiving DMA */
-
- if (request_irq(NETWORK_DMA_RX_IRQ_NBR, e100rxtx_interrupt, 0, cardname,
- (void *)dev)) {
- goto grace_exit0;
- }
-
- /* allocate the irq corresponding to the transmitting DMA */
-
- if (request_irq(NETWORK_DMA_TX_IRQ_NBR, e100rxtx_interrupt, 0,
- cardname, (void *)dev)) {
- goto grace_exit1;
- }
-
- /* allocate the irq corresponding to the network errors etc */
-
- if (request_irq(NETWORK_STATUS_IRQ_NBR, e100nw_interrupt, 0,
- cardname, (void *)dev)) {
- goto grace_exit2;
- }
-
- /*
- * Always allocate the DMA channels after the IRQ,
- * and clean up on failure.
- */
-
- if (cris_request_dma(NETWORK_TX_DMA_NBR,
- cardname,
- DMA_VERBOSE_ON_ERROR,
- dma_eth)) {
- goto grace_exit3;
- }
-
- if (cris_request_dma(NETWORK_RX_DMA_NBR,
- cardname,
- DMA_VERBOSE_ON_ERROR,
- dma_eth)) {
- goto grace_exit4;
- }
-
- /* give the HW an idea of what MAC address we want */
-
- *R_NETWORK_SA_0 = dev->dev_addr[0] | (dev->dev_addr[1] << 8) |
- (dev->dev_addr[2] << 16) | (dev->dev_addr[3] << 24);
- *R_NETWORK_SA_1 = dev->dev_addr[4] | (dev->dev_addr[5] << 8);
- *R_NETWORK_SA_2 = 0;
-
-#if 0
- /* use promiscuous mode for testing */
- *R_NETWORK_GA_0 = 0xffffffff;
- *R_NETWORK_GA_1 = 0xffffffff;
-
- *R_NETWORK_REC_CONFIG = 0xd; /* broadcast rec, individ. rec, ma0 enabled */
-#else
- SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, max_size, size1522);
- SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, broadcast, receive);
- SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, ma0, enable);
- SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex);
- *R_NETWORK_REC_CONFIG = network_rec_config_shadow;
-#endif
-
- *R_NETWORK_GEN_CONFIG =
- IO_STATE(R_NETWORK_GEN_CONFIG, phy, mii_clk) |
- IO_STATE(R_NETWORK_GEN_CONFIG, enable, on);
-
- SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, clr_error, clr);
- SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, delay, none);
- SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, cancel, dont);
- SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, cd, enable);
- SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, retry, enable);
- SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, pad, enable);
- SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, crc, enable);
- *R_NETWORK_TR_CTRL = network_tr_ctrl_shadow;
-
- local_irq_save(flags);
-
- /* enable the irq's for ethernet DMA */
-
- *R_IRQ_MASK2_SET =
- IO_STATE(R_IRQ_MASK2_SET, dma0_eop, set) |
- IO_STATE(R_IRQ_MASK2_SET, dma1_eop, set);
-
- *R_IRQ_MASK0_SET =
- IO_STATE(R_IRQ_MASK0_SET, overrun, set) |
- IO_STATE(R_IRQ_MASK0_SET, underrun, set) |
- IO_STATE(R_IRQ_MASK0_SET, excessive_col, set);
-
- /* make sure the irqs are cleared */
-
- *R_DMA_CH0_CLR_INTR = IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do);
- *R_DMA_CH1_CLR_INTR = IO_STATE(R_DMA_CH1_CLR_INTR, clr_eop, do);
-
- /* make sure the rec and transmit error counters are cleared */
-
- (void)*R_REC_COUNTERS; /* dummy read */
- (void)*R_TR_COUNTERS; /* dummy read */
-
- /* start the receiving DMA channel so we can receive packets from now on */
-
- *R_DMA_CH1_FIRST = virt_to_phys(myNextRxDesc);
- *R_DMA_CH1_CMD = IO_STATE(R_DMA_CH1_CMD, cmd, start);
-
- /* Set up transmit DMA channel so it can be restarted later */
-
- *R_DMA_CH0_FIRST = 0;
- *R_DMA_CH0_DESCR = virt_to_phys(myLastTxDesc);
- netif_start_queue(dev);
-
- local_irq_restore(flags);
-
- /* Probe for transceiver */
- if (e100_probe_transceiver(dev))
- goto grace_exit5;
-
- /* Start duplex/speed timers */
- add_timer(&speed_timer);
- add_timer(&duplex_timer);
-
- /* We are now ready to accept transmit requeusts from
- * the queueing layer of the networking.
- */
- netif_carrier_on(dev);
-
- return 0;
-
-grace_exit5:
- cris_free_dma(NETWORK_RX_DMA_NBR, cardname);
-grace_exit4:
- cris_free_dma(NETWORK_TX_DMA_NBR, cardname);
-grace_exit3:
- free_irq(NETWORK_STATUS_IRQ_NBR, (void *)dev);
-grace_exit2:
- free_irq(NETWORK_DMA_TX_IRQ_NBR, (void *)dev);
-grace_exit1:
- free_irq(NETWORK_DMA_RX_IRQ_NBR, (void *)dev);
-grace_exit0:
- return -EAGAIN;
-}
-
-#if defined(CONFIG_ETRAX_NO_PHY)
-static void
-dummy_check_speed(struct net_device* dev)
-{
- current_speed = 100;
-}
-#else
-static void
-generic_check_speed(struct net_device* dev)
-{
- unsigned long data;
- struct net_local *np = netdev_priv(dev);
-
- data = e100_get_mdio_reg(dev, np->mii_if.phy_id, MII_ADVERTISE);
- if ((data & ADVERTISE_100FULL) ||
- (data & ADVERTISE_100HALF))
- current_speed = 100;
- else
- current_speed = 10;
-}
-
-static void
-tdk_check_speed(struct net_device* dev)
-{
- unsigned long data;
- struct net_local *np = netdev_priv(dev);
-
- data = e100_get_mdio_reg(dev, np->mii_if.phy_id,
- MDIO_TDK_DIAGNOSTIC_REG);
- current_speed = (data & MDIO_TDK_DIAGNOSTIC_RATE ? 100 : 10);
-}
-
-static void
-broadcom_check_speed(struct net_device* dev)
-{
- unsigned long data;
- struct net_local *np = netdev_priv(dev);
-
- data = e100_get_mdio_reg(dev, np->mii_if.phy_id,
- MDIO_AUX_CTRL_STATUS_REG);
- current_speed = (data & MDIO_BC_SPEED ? 100 : 10);
-}
-
-static void
-intel_check_speed(struct net_device* dev)
-{
- unsigned long data;
- struct net_local *np = netdev_priv(dev);
-
- data = e100_get_mdio_reg(dev, np->mii_if.phy_id,
- MDIO_INT_STATUS_REG_2);
- current_speed = (data & MDIO_INT_SPEED ? 100 : 10);
-}
-#endif
-static void
-e100_check_speed(struct timer_list *unused)
-{
- struct net_device* dev = timer_dev;
- struct net_local *np = netdev_priv(dev);
- static int led_initiated = 0;
- unsigned long data;
- int old_speed = current_speed;
-
- spin_lock(&np->transceiver_lock);
-
- data = e100_get_mdio_reg(dev, np->mii_if.phy_id, MII_BMSR);
- if (!(data & BMSR_LSTATUS)) {
- current_speed = 0;
- } else {
- transceiver->check_speed(dev);
- }
-
- spin_lock(&np->led_lock);
- if ((old_speed != current_speed) || !led_initiated) {
- led_initiated = 1;
- e100_set_network_leds(NO_NETWORK_ACTIVITY);
- if (current_speed)
- netif_carrier_on(dev);
- else
- netif_carrier_off(dev);
- }
- spin_unlock(&np->led_lock);
-
- /* Reinitialize the timer. */
- speed_timer.expires = jiffies + NET_LINK_UP_CHECK_INTERVAL;
- add_timer(&speed_timer);
-
- spin_unlock(&np->transceiver_lock);
-}
-
-static void
-e100_negotiate(struct net_device* dev)
-{
- struct net_local *np = netdev_priv(dev);
- unsigned short data = e100_get_mdio_reg(dev, np->mii_if.phy_id,
- MII_ADVERTISE);
-
- /* Discard old speed and duplex settings */
- data &= ~(ADVERTISE_100HALF | ADVERTISE_100FULL |
- ADVERTISE_10HALF | ADVERTISE_10FULL);
-
- switch (current_speed_selection) {
- case 10:
- if (current_duplex == full)
- data |= ADVERTISE_10FULL;
- else if (current_duplex == half)
- data |= ADVERTISE_10HALF;
- else
- data |= ADVERTISE_10HALF | ADVERTISE_10FULL;
- break;
-
- case 100:
- if (current_duplex == full)
- data |= ADVERTISE_100FULL;
- else if (current_duplex == half)
- data |= ADVERTISE_100HALF;
- else
- data |= ADVERTISE_100HALF | ADVERTISE_100FULL;
- break;
-
- case 0: /* Auto */
- if (current_duplex == full)
- data |= ADVERTISE_100FULL | ADVERTISE_10FULL;
- else if (current_duplex == half)
- data |= ADVERTISE_100HALF | ADVERTISE_10HALF;
- else
- data |= ADVERTISE_10HALF | ADVERTISE_10FULL |
- ADVERTISE_100HALF | ADVERTISE_100FULL;
- break;
-
- default: /* assume autoneg speed and duplex */
- data |= ADVERTISE_10HALF | ADVERTISE_10FULL |
- ADVERTISE_100HALF | ADVERTISE_100FULL;
- break;
- }
-
- e100_set_mdio_reg(dev, np->mii_if.phy_id, MII_ADVERTISE, data);
-
- data = e100_get_mdio_reg(dev, np->mii_if.phy_id, MII_BMCR);
- if (autoneg_normal) {
- /* Renegotiate with link partner */
- data |= BMCR_ANENABLE | BMCR_ANRESTART;
- } else {
- /* Don't negotiate speed or duplex */
- data &= ~(BMCR_ANENABLE | BMCR_ANRESTART);
-
- /* Set speed and duplex static */
- if (current_speed_selection == 10)
- data &= ~BMCR_SPEED100;
- else
- data |= BMCR_SPEED100;
-
- if (current_duplex != full)
- data &= ~BMCR_FULLDPLX;
- else
- data |= BMCR_FULLDPLX;
- }
- e100_set_mdio_reg(dev, np->mii_if.phy_id, MII_BMCR, data);
-}
-
-static void
-e100_set_speed(struct net_device* dev, unsigned long speed)
-{
- struct net_local *np = netdev_priv(dev);
-
- spin_lock(&np->transceiver_lock);
- if (speed != current_speed_selection) {
- current_speed_selection = speed;
- e100_negotiate(dev);
- }
- spin_unlock(&np->transceiver_lock);
-}
-
-static void
-e100_check_duplex(struct timer_list *unused)
-{
- struct net_device *dev = timer_dev;
- struct net_local *np = netdev_priv(dev);
- int old_duplex;
-
- spin_lock(&np->transceiver_lock);
- old_duplex = full_duplex;
- transceiver->check_duplex(dev);
- if (old_duplex != full_duplex) {
- /* Duplex changed */
- SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex);
- *R_NETWORK_REC_CONFIG = network_rec_config_shadow;
- }
-
- /* Reinitialize the timer. */
- duplex_timer.expires = jiffies + NET_DUPLEX_CHECK_INTERVAL;
- add_timer(&duplex_timer);
- np->mii_if.full_duplex = full_duplex;
- spin_unlock(&np->transceiver_lock);
-}
-#if defined(CONFIG_ETRAX_NO_PHY)
-static void
-dummy_check_duplex(struct net_device* dev)
-{
- full_duplex = 1;
-}
-#else
-static void
-generic_check_duplex(struct net_device* dev)
-{
- unsigned long data;
- struct net_local *np = netdev_priv(dev);
-
- data = e100_get_mdio_reg(dev, np->mii_if.phy_id, MII_ADVERTISE);
- if ((data & ADVERTISE_10FULL) ||
- (data & ADVERTISE_100FULL))
- full_duplex = 1;
- else
- full_duplex = 0;
-}
-
-static void
-tdk_check_duplex(struct net_device* dev)
-{
- unsigned long data;
- struct net_local *np = netdev_priv(dev);
-
- data = e100_get_mdio_reg(dev, np->mii_if.phy_id,
- MDIO_TDK_DIAGNOSTIC_REG);
- full_duplex = (data & MDIO_TDK_DIAGNOSTIC_DPLX) ? 1 : 0;
-}
-
-static void
-broadcom_check_duplex(struct net_device* dev)
-{
- unsigned long data;
- struct net_local *np = netdev_priv(dev);
-
- data = e100_get_mdio_reg(dev, np->mii_if.phy_id,
- MDIO_AUX_CTRL_STATUS_REG);
- full_duplex = (data & MDIO_BC_FULL_DUPLEX_IND) ? 1 : 0;
-}
-
-static void
-intel_check_duplex(struct net_device* dev)
-{
- unsigned long data;
- struct net_local *np = netdev_priv(dev);
-
- data = e100_get_mdio_reg(dev, np->mii_if.phy_id,
- MDIO_INT_STATUS_REG_2);
- full_duplex = (data & MDIO_INT_FULL_DUPLEX_IND) ? 1 : 0;
-}
-#endif
-static void
-e100_set_duplex(struct net_device* dev, enum duplex new_duplex)
-{
- struct net_local *np = netdev_priv(dev);
-
- spin_lock(&np->transceiver_lock);
- if (new_duplex != current_duplex) {
- current_duplex = new_duplex;
- e100_negotiate(dev);
- }
- spin_unlock(&np->transceiver_lock);
-}
-
-static int
-e100_probe_transceiver(struct net_device* dev)
-{
- int ret = 0;
-
-#if !defined(CONFIG_ETRAX_NO_PHY)
- unsigned int phyid_high;
- unsigned int phyid_low;
- unsigned int oui;
- struct transceiver_ops* ops = NULL;
- struct net_local *np = netdev_priv(dev);
-
- spin_lock(&np->transceiver_lock);
-
- /* Probe MDIO physical address */
- for (np->mii_if.phy_id = 0; np->mii_if.phy_id <= 31;
- np->mii_if.phy_id++) {
- if (e100_get_mdio_reg(dev,
- np->mii_if.phy_id, MII_BMSR) != 0xffff)
- break;
- }
- if (np->mii_if.phy_id == 32) {
- ret = -ENODEV;
- goto out;
- }
-
- /* Get manufacturer */
- phyid_high = e100_get_mdio_reg(dev, np->mii_if.phy_id, MII_PHYSID1);
- phyid_low = e100_get_mdio_reg(dev, np->mii_if.phy_id, MII_PHYSID2);
- oui = (phyid_high << 6) | (phyid_low >> 10);
-
- for (ops = &transceivers[0]; ops->oui; ops++) {
- if (ops->oui == oui)
- break;
- }
- transceiver = ops;
-out:
- spin_unlock(&np->transceiver_lock);
-#endif
- return ret;
-}
-
-static int
-e100_get_mdio_reg(struct net_device *dev, int phy_id, int location)
-{
- unsigned short cmd; /* Data to be sent on MDIO port */
- int data; /* Data read from MDIO */
- int bitCounter;
-
- /* Start of frame, OP Code, Physical Address, Register Address */
- cmd = (MDIO_START << 14) | (MDIO_READ << 12) | (phy_id << 7) |
- (location << 2);
-
- e100_send_mdio_cmd(cmd, 0);
-
- data = 0;
-
- /* Data... */
- for (bitCounter=15; bitCounter>=0 ; bitCounter--) {
- data |= (e100_receive_mdio_bit() << bitCounter);
- }
-
- return data;
-}
-
-static void
-e100_set_mdio_reg(struct net_device *dev, int phy_id, int location, int value)
-{
- int bitCounter;
- unsigned short cmd;
-
- cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (phy_id << 7) |
- (location << 2);
-
- e100_send_mdio_cmd(cmd, 1);
-
- /* Data... */
- for (bitCounter=15; bitCounter>=0 ; bitCounter--) {
- e100_send_mdio_bit(GET_BIT(bitCounter, value));
- }
-
-}
-
-static void
-e100_send_mdio_cmd(unsigned short cmd, int write_cmd)
-{
- int bitCounter;
- unsigned char data = 0x2;
-
- /* Preamble */
- for (bitCounter = 31; bitCounter>= 0; bitCounter--)
- e100_send_mdio_bit(GET_BIT(bitCounter, MDIO_PREAMBLE));
-
- for (bitCounter = 15; bitCounter >= 2; bitCounter--)
- e100_send_mdio_bit(GET_BIT(bitCounter, cmd));
-
- /* Turnaround */
- for (bitCounter = 1; bitCounter >= 0 ; bitCounter--)
- if (write_cmd)
- e100_send_mdio_bit(GET_BIT(bitCounter, data));
- else
- e100_receive_mdio_bit();
-}
-
-static void
-e100_send_mdio_bit(unsigned char bit)
-{
- *R_NETWORK_MGM_CTRL =
- IO_STATE(R_NETWORK_MGM_CTRL, mdoe, enable) |
- IO_FIELD(R_NETWORK_MGM_CTRL, mdio, bit);
- udelay(1);
- *R_NETWORK_MGM_CTRL =
- IO_STATE(R_NETWORK_MGM_CTRL, mdoe, enable) |
- IO_MASK(R_NETWORK_MGM_CTRL, mdck) |
- IO_FIELD(R_NETWORK_MGM_CTRL, mdio, bit);
- udelay(1);
-}
-
-static unsigned char
-e100_receive_mdio_bit(void)
-{
- unsigned char bit;
- *R_NETWORK_MGM_CTRL = 0;
- bit = IO_EXTRACT(R_NETWORK_STAT, mdio, *R_NETWORK_STAT);
- udelay(1);
- *R_NETWORK_MGM_CTRL = IO_MASK(R_NETWORK_MGM_CTRL, mdck);
- udelay(1);
- return bit;
-}
-
-static void
-e100_reset_transceiver(struct net_device* dev)
-{
- struct net_local *np = netdev_priv(dev);
- unsigned short cmd;
- unsigned short data;
- int bitCounter;
-
- data = e100_get_mdio_reg(dev, np->mii_if.phy_id, MII_BMCR);
-
- cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (np->mii_if.phy_id << 7) | (MII_BMCR << 2);
-
- e100_send_mdio_cmd(cmd, 1);
-
- data |= 0x8000;
-
- for (bitCounter = 15; bitCounter >= 0 ; bitCounter--) {
- e100_send_mdio_bit(GET_BIT(bitCounter, data));
- }
-}
-
-/* Called by upper layers if they decide it took too long to complete
- * sending a packet - we need to reset and stuff.
- */
-
-static void
-e100_tx_timeout(struct net_device *dev)
-{
- struct net_local *np = netdev_priv(dev);
- unsigned long flags;
-
- spin_lock_irqsave(&np->lock, flags);
-
- printk(KERN_WARNING "%s: transmit timed out, %s?\n", dev->name,
- tx_done(dev) ? "IRQ problem" : "network cable problem");
-
- /* remember we got an error */
-
- dev->stats.tx_errors++;
-
- /* reset the TX DMA in case it has hung on something */
-
- RESET_DMA(NETWORK_TX_DMA_NBR);
- WAIT_DMA(NETWORK_TX_DMA_NBR);
-
- /* Reset the transceiver. */
-
- e100_reset_transceiver(dev);
-
- /* and get rid of the packets that never got an interrupt */
- while (myFirstTxDesc != myNextTxDesc) {
- dev_kfree_skb(myFirstTxDesc->skb);
- myFirstTxDesc->skb = 0;
- myFirstTxDesc = phys_to_virt(myFirstTxDesc->descr.next);
- }
-
- /* Set up transmit DMA channel so it can be restarted later */
- *R_DMA_CH0_FIRST = 0;
- *R_DMA_CH0_DESCR = virt_to_phys(myLastTxDesc);
-
- /* tell the upper layers we're ok again */
-
- netif_wake_queue(dev);
- spin_unlock_irqrestore(&np->lock, flags);
-}
-
-
-/* This will only be invoked if the driver is _not_ in XOFF state.
- * What this means is that we need not check it, and that this
- * invariant will hold if we make sure that the netif_*_queue()
- * calls are done at the proper times.
- */
-
-static int
-e100_send_packet(struct sk_buff *skb, struct net_device *dev)
-{
- struct net_local *np = netdev_priv(dev);
- unsigned char *buf = skb->data;
- unsigned long flags;
-
-#ifdef ETHDEBUG
- printk("send packet len %d\n", length);
-#endif
- spin_lock_irqsave(&np->lock, flags); /* protect from tx_interrupt and ourself */
-
- myNextTxDesc->skb = skb;
-
- netif_trans_update(dev); /* NETIF_F_LLTX driver :( */
-
- e100_hardware_send_packet(np, buf, skb->len);
-
- myNextTxDesc = phys_to_virt(myNextTxDesc->descr.next);
-
- /* Stop queue if full */
- if (myNextTxDesc == myFirstTxDesc) {
- netif_stop_queue(dev);
- }
-
- spin_unlock_irqrestore(&np->lock, flags);
-
- return NETDEV_TX_OK;
-}
-
-/*
- * The typical workload of the driver:
- * Handle the network interface interrupts.
- */
-
-static irqreturn_t
-e100rxtx_interrupt(int irq, void *dev_id)
-{
- struct net_device *dev = (struct net_device *)dev_id;
- unsigned long irqbits;
-
- /*
- * Note that both rx and tx interrupts are blocked at this point,
- * regardless of which got us here.
- */
-
- irqbits = *R_IRQ_MASK2_RD;
-
- /* Handle received packets */
- if (irqbits & IO_STATE(R_IRQ_MASK2_RD, dma1_eop, active)) {
- /* acknowledge the eop interrupt */
-
- *R_DMA_CH1_CLR_INTR = IO_STATE(R_DMA_CH1_CLR_INTR, clr_eop, do);
-
- /* check if one or more complete packets were indeed received */
-
- while ((*R_DMA_CH1_FIRST != virt_to_phys(myNextRxDesc)) &&
- (myNextRxDesc != myLastRxDesc)) {
- /* Take out the buffer and give it to the OS, then
- * allocate a new buffer to put a packet in.
- */
- e100_rx(dev);
- dev->stats.rx_packets++;
- /* restart/continue on the channel, for safety */
- *R_DMA_CH1_CMD = IO_STATE(R_DMA_CH1_CMD, cmd, restart);
- /* clear dma channel 1 eop/descr irq bits */
- *R_DMA_CH1_CLR_INTR =
- IO_STATE(R_DMA_CH1_CLR_INTR, clr_eop, do) |
- IO_STATE(R_DMA_CH1_CLR_INTR, clr_descr, do);
-
- /* now, we might have gotten another packet
- so we have to loop back and check if so */
- }
- }
-
- /* Report any packets that have been sent */
- while (virt_to_phys(myFirstTxDesc) != *R_DMA_CH0_FIRST &&
- (netif_queue_stopped(dev) || myFirstTxDesc != myNextTxDesc)) {
- dev->stats.tx_bytes += myFirstTxDesc->skb->len;
- dev->stats.tx_packets++;
-
- /* dma is ready with the transmission of the data in tx_skb, so now
- we can release the skb memory */
- dev_kfree_skb_irq(myFirstTxDesc->skb);
- myFirstTxDesc->skb = 0;
- myFirstTxDesc = phys_to_virt(myFirstTxDesc->descr.next);
- /* Wake up queue. */
- netif_wake_queue(dev);
- }
-
- if (irqbits & IO_STATE(R_IRQ_MASK2_RD, dma0_eop, active)) {
- /* acknowledge the eop interrupt. */
- *R_DMA_CH0_CLR_INTR = IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do);
- }
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t
-e100nw_interrupt(int irq, void *dev_id)
-{
- struct net_device *dev = (struct net_device *)dev_id;
- unsigned long irqbits = *R_IRQ_MASK0_RD;
-
- /* check for underrun irq */
- if (irqbits & IO_STATE(R_IRQ_MASK0_RD, underrun, active)) {
- SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, clr_error, clr);
- *R_NETWORK_TR_CTRL = network_tr_ctrl_shadow;
- SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, clr_error, nop);
- dev->stats.tx_errors++;
- D(printk("ethernet receiver underrun!\n"));
- }
-
- /* check for overrun irq */
- if (irqbits & IO_STATE(R_IRQ_MASK0_RD, overrun, active)) {
- update_rx_stats(&dev->stats); /* this will ack the irq */
- D(printk("ethernet receiver overrun!\n"));
- }
- /* check for excessive collision irq */
- if (irqbits & IO_STATE(R_IRQ_MASK0_RD, excessive_col, active)) {
- SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, clr_error, clr);
- *R_NETWORK_TR_CTRL = network_tr_ctrl_shadow;
- SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, clr_error, nop);
- dev->stats.tx_errors++;
- D(printk("ethernet excessive collisions!\n"));
- }
- return IRQ_HANDLED;
-}
-
-/* We have a good packet(s), get it/them out of the buffers. */
-static void
-e100_rx(struct net_device *dev)
-{
- struct sk_buff *skb;
- int length = 0;
- struct net_local *np = netdev_priv(dev);
- unsigned char *skb_data_ptr;
-#ifdef ETHDEBUG
- int i;
-#endif
- etrax_eth_descr *prevRxDesc; /* The descriptor right before myNextRxDesc */
- spin_lock(&np->led_lock);
- if (!led_active && time_after(jiffies, led_next_time)) {
- /* light the network leds depending on the current speed. */
- e100_set_network_leds(NETWORK_ACTIVITY);
-
- /* Set the earliest time we may clear the LED */
- led_next_time = jiffies + NET_FLASH_TIME;
- led_active = 1;
- mod_timer(&clear_led_timer, jiffies + HZ/10);
- }
- spin_unlock(&np->led_lock);
-
- length = myNextRxDesc->descr.hw_len - 4;
- dev->stats.rx_bytes += length;
-
-#ifdef ETHDEBUG
- printk("Got a packet of length %d:\n", length);
- /* dump the first bytes in the packet */
- skb_data_ptr = (unsigned char *)phys_to_virt(myNextRxDesc->descr.buf);
- for (i = 0; i < 8; i++) {
- printk("%d: %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x\n", i * 8,
- skb_data_ptr[0],skb_data_ptr[1],skb_data_ptr[2],skb_data_ptr[3],
- skb_data_ptr[4],skb_data_ptr[5],skb_data_ptr[6],skb_data_ptr[7]);
- skb_data_ptr += 8;
- }
-#endif
-
- if (length < RX_COPYBREAK) {
- /* Small packet, copy data */
- skb = dev_alloc_skb(length - ETHER_HEAD_LEN);
- if (!skb) {
- dev->stats.rx_errors++;
- printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name);
- goto update_nextrxdesc;
- }
-
- skb_put(skb, length - ETHER_HEAD_LEN); /* allocate room for the packet body */
- skb_data_ptr = skb_push(skb, ETHER_HEAD_LEN); /* allocate room for the header */
-
-#ifdef ETHDEBUG
- printk("head = 0x%x, data = 0x%x, tail = 0x%x, end = 0x%x\n",
- skb->head, skb->data, skb_tail_pointer(skb),
- skb_end_pointer(skb));
- printk("copying packet to 0x%x.\n", skb_data_ptr);
-#endif
-
- memcpy(skb_data_ptr, phys_to_virt(myNextRxDesc->descr.buf), length);
- }
- else {
- /* Large packet, send directly to upper layers and allocate new
- * memory (aligned to cache line boundary to avoid bug).
- * Before sending the skb to upper layers we must make sure
- * that skb->data points to the aligned start of the packet.
- */
- int align;
- struct sk_buff *new_skb = dev_alloc_skb(MAX_MEDIA_DATA_SIZE + 2 * L1_CACHE_BYTES);
- if (!new_skb) {
- dev->stats.rx_errors++;
- printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name);
- goto update_nextrxdesc;
- }
- skb = myNextRxDesc->skb;
- align = (int)phys_to_virt(myNextRxDesc->descr.buf) - (int)skb->data;
- skb_put(skb, length + align);
- skb_pull(skb, align); /* Remove alignment bytes */
- myNextRxDesc->skb = new_skb;
- myNextRxDesc->descr.buf = L1_CACHE_ALIGN(virt_to_phys(myNextRxDesc->skb->data));
- }
-
- skb->protocol = eth_type_trans(skb, dev);
-
- /* Send the packet to the upper layers */
- netif_rx(skb);
-
- update_nextrxdesc:
- /* Prepare for next packet */
- myNextRxDesc->descr.status = 0;
- prevRxDesc = myNextRxDesc;
- myNextRxDesc = phys_to_virt(myNextRxDesc->descr.next);
-
- rx_queue_len++;
-
- /* Check if descriptors should be returned */
- if (rx_queue_len == RX_QUEUE_THRESHOLD) {
- flush_etrax_cache();
- prevRxDesc->descr.ctrl |= d_eol;
- myLastRxDesc->descr.ctrl &= ~d_eol;
- myLastRxDesc = prevRxDesc;
- rx_queue_len = 0;
- }
-}
-
-/* The inverse routine to net_open(). */
-static int
-e100_close(struct net_device *dev)
-{
- printk(KERN_INFO "Closing %s.\n", dev->name);
-
- netif_stop_queue(dev);
-
- *R_IRQ_MASK0_CLR =
- IO_STATE(R_IRQ_MASK0_CLR, overrun, clr) |
- IO_STATE(R_IRQ_MASK0_CLR, underrun, clr) |
- IO_STATE(R_IRQ_MASK0_CLR, excessive_col, clr);
-
- *R_IRQ_MASK2_CLR =
- IO_STATE(R_IRQ_MASK2_CLR, dma0_descr, clr) |
- IO_STATE(R_IRQ_MASK2_CLR, dma0_eop, clr) |
- IO_STATE(R_IRQ_MASK2_CLR, dma1_descr, clr) |
- IO_STATE(R_IRQ_MASK2_CLR, dma1_eop, clr);
-
- /* Stop the receiver and the transmitter */
-
- RESET_DMA(NETWORK_TX_DMA_NBR);
- RESET_DMA(NETWORK_RX_DMA_NBR);
-
- /* Flush the Tx and disable Rx here. */
-
- free_irq(NETWORK_DMA_RX_IRQ_NBR, (void *)dev);
- free_irq(NETWORK_DMA_TX_IRQ_NBR, (void *)dev);
- free_irq(NETWORK_STATUS_IRQ_NBR, (void *)dev);
-
- cris_free_dma(NETWORK_TX_DMA_NBR, cardname);
- cris_free_dma(NETWORK_RX_DMA_NBR, cardname);
-
- /* Update the statistics here. */
-
- update_rx_stats(&dev->stats);
- update_tx_stats(&dev->stats);
-
- /* Stop speed/duplex timers */
- del_timer(&speed_timer);
- del_timer(&duplex_timer);
-
- return 0;
-}
-
-static int
-e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
-{
- struct mii_ioctl_data *data = if_mii(ifr);
- struct net_local *np = netdev_priv(dev);
- int rc = 0;
- int old_autoneg;
-
- spin_lock(&np->lock); /* Preempt protection */
- switch (cmd) {
- /* The ioctls below should be considered obsolete but are */
- /* still present for compatibility with old scripts/apps */
- case SET_ETH_SPEED_10: /* 10 Mbps */
- e100_set_speed(dev, 10);
- break;
- case SET_ETH_SPEED_100: /* 100 Mbps */
- e100_set_speed(dev, 100);
- break;
- case SET_ETH_SPEED_AUTO: /* Auto-negotiate speed */
- e100_set_speed(dev, 0);
- break;
- case SET_ETH_DUPLEX_HALF: /* Half duplex */
- e100_set_duplex(dev, half);
- break;
- case SET_ETH_DUPLEX_FULL: /* Full duplex */
- e100_set_duplex(dev, full);
- break;
- case SET_ETH_DUPLEX_AUTO: /* Auto-negotiate duplex */
- e100_set_duplex(dev, autoneg);
- break;
- case SET_ETH_AUTONEG:
- old_autoneg = autoneg_normal;
- autoneg_normal = *(int*)data;
- if (autoneg_normal != old_autoneg)
- e100_negotiate(dev);
- break;
- default:
- rc = generic_mii_ioctl(&np->mii_if, if_mii(ifr),
- cmd, NULL);
- break;
- }
- spin_unlock(&np->lock);
- return rc;
-}
-
-static int e100_get_link_ksettings(struct net_device *dev,
- struct ethtool_link_ksettings *cmd)
-{
- struct net_local *np = netdev_priv(dev);
- u32 supported;
-
- spin_lock_irq(&np->lock);
- mii_ethtool_get_link_ksettings(&np->mii_if, cmd);
- spin_unlock_irq(&np->lock);
-
- /* The PHY may support 1000baseT, but the Etrax100 does not. */
- ethtool_convert_link_mode_to_legacy_u32(&supported,
- cmd->link_modes.supported);
-
- supported &= ~(SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full);
-
- ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
- supported);
-
- return 0;
-}
-
-static int e100_set_link_ksettings(struct net_device *dev,
- const struct ethtool_link_ksettings *ecmd)
-{
- if (ecmd->base.autoneg == AUTONEG_ENABLE) {
- e100_set_duplex(dev, autoneg);
- e100_set_speed(dev, 0);
- } else {
- e100_set_duplex(dev, ecmd->base.duplex == DUPLEX_HALF ?
- half : full);
- e100_set_speed(dev, ecmd->base.speed == SPEED_10 ? 10 : 100);
- }
-
- return 0;
-}
-
-static void e100_get_drvinfo(struct net_device *dev,
- struct ethtool_drvinfo *info)
-{
- strlcpy(info->driver, "ETRAX 100LX", sizeof(info->driver));
- strlcpy(info->version, "$Revision: 1.31 $", sizeof(info->version));
- strlcpy(info->fw_version, "N/A", sizeof(info->fw_version));
- strlcpy(info->bus_info, "N/A", sizeof(info->bus_info));
-}
-
-static int e100_nway_reset(struct net_device *dev)
-{
- if (current_duplex == autoneg && current_speed_selection == 0)
- e100_negotiate(dev);
- return 0;
-}
-
-static const struct ethtool_ops e100_ethtool_ops = {
- .get_drvinfo = e100_get_drvinfo,
- .nway_reset = e100_nway_reset,
- .get_link = ethtool_op_get_link,
- .get_link_ksettings = e100_get_link_ksettings,
- .set_link_ksettings = e100_set_link_ksettings,
-};
-
-static int
-e100_set_config(struct net_device *dev, struct ifmap *map)
-{
- struct net_local *np = netdev_priv(dev);
-
- spin_lock(&np->lock); /* Preempt protection */
-
- switch(map->port) {
- case IF_PORT_UNKNOWN:
- /* Use autoneg */
- e100_set_speed(dev, 0);
- e100_set_duplex(dev, autoneg);
- break;
- case IF_PORT_10BASET:
- e100_set_speed(dev, 10);
- e100_set_duplex(dev, autoneg);
- break;
- case IF_PORT_100BASET:
- case IF_PORT_100BASETX:
- e100_set_speed(dev, 100);
- e100_set_duplex(dev, autoneg);
- break;
- case IF_PORT_100BASEFX:
- case IF_PORT_10BASE2:
- case IF_PORT_AUI:
- spin_unlock(&np->lock);
- return -EOPNOTSUPP;
- default:
- printk(KERN_ERR "%s: Invalid media selected", dev->name);
- spin_unlock(&np->lock);
- return -EINVAL;
- }
- spin_unlock(&np->lock);
- return 0;
-}
-
-static void
-update_rx_stats(struct net_device_stats *es)
-{
- unsigned long r = *R_REC_COUNTERS;
- /* update stats relevant to reception errors */
- es->rx_fifo_errors += IO_EXTRACT(R_REC_COUNTERS, congestion, r);
- es->rx_crc_errors += IO_EXTRACT(R_REC_COUNTERS, crc_error, r);
- es->rx_frame_errors += IO_EXTRACT(R_REC_COUNTERS, alignment_error, r);
- es->rx_length_errors += IO_EXTRACT(R_REC_COUNTERS, oversize, r);
-}
-
-static void
-update_tx_stats(struct net_device_stats *es)
-{
- unsigned long r = *R_TR_COUNTERS;
- /* update stats relevant to transmission errors */
- es->collisions +=
- IO_EXTRACT(R_TR_COUNTERS, single_col, r) +
- IO_EXTRACT(R_TR_COUNTERS, multiple_col, r);
-}
-
-/*
- * Get the current statistics.
- * This may be called with the card open or closed.
- */
-static struct net_device_stats *
-e100_get_stats(struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
- unsigned long flags;
-
- spin_lock_irqsave(&lp->lock, flags);
-
- update_rx_stats(&dev->stats);
- update_tx_stats(&dev->stats);
-
- spin_unlock_irqrestore(&lp->lock, flags);
- return &dev->stats;
-}
-
-/*
- * Set or clear the multicast filter for this adaptor.
- * num_addrs == -1 Promiscuous mode, receive all packets
- * num_addrs == 0 Normal mode, clear multicast list
- * num_addrs > 0 Multicast mode, receive normal and MC packets,
- * and do best-effort filtering.
- */
-static void
-set_multicast_list(struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
- int num_addr = netdev_mc_count(dev);
- unsigned long int lo_bits;
- unsigned long int hi_bits;
-
- spin_lock(&lp->lock);
- if (dev->flags & IFF_PROMISC) {
- /* promiscuous mode */
- lo_bits = 0xfffffffful;
- hi_bits = 0xfffffffful;
-
- /* Enable individual receive */
- SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, receive);
- *R_NETWORK_REC_CONFIG = network_rec_config_shadow;
- } else if (dev->flags & IFF_ALLMULTI) {
- /* enable all multicasts */
- lo_bits = 0xfffffffful;
- hi_bits = 0xfffffffful;
-
- /* Disable individual receive */
- SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, discard);
- *R_NETWORK_REC_CONFIG = network_rec_config_shadow;
- } else if (num_addr == 0) {
- /* Normal, clear the mc list */
- lo_bits = 0x00000000ul;
- hi_bits = 0x00000000ul;
-
- /* Disable individual receive */
- SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, discard);
- *R_NETWORK_REC_CONFIG = network_rec_config_shadow;
- } else {
- /* MC mode, receive normal and MC packets */
- char hash_ix;
- struct netdev_hw_addr *ha;
- char *baddr;
-
- lo_bits = 0x00000000ul;
- hi_bits = 0x00000000ul;
- netdev_for_each_mc_addr(ha, dev) {
- /* Calculate the hash index for the GA registers */
-
- hash_ix = 0;
- baddr = ha->addr;
- hash_ix ^= (*baddr) & 0x3f;
- hash_ix ^= ((*baddr) >> 6) & 0x03;
- ++baddr;
- hash_ix ^= ((*baddr) << 2) & 0x03c;
- hash_ix ^= ((*baddr) >> 4) & 0xf;
- ++baddr;
- hash_ix ^= ((*baddr) << 4) & 0x30;
- hash_ix ^= ((*baddr) >> 2) & 0x3f;
- ++baddr;
- hash_ix ^= (*baddr) & 0x3f;
- hash_ix ^= ((*baddr) >> 6) & 0x03;
- ++baddr;
- hash_ix ^= ((*baddr) << 2) & 0x03c;
- hash_ix ^= ((*baddr) >> 4) & 0xf;
- ++baddr;
- hash_ix ^= ((*baddr) << 4) & 0x30;
- hash_ix ^= ((*baddr) >> 2) & 0x3f;
-
- hash_ix &= 0x3f;
-
- if (hash_ix >= 32) {
- hi_bits |= (1 << (hash_ix-32));
- } else {
- lo_bits |= (1 << hash_ix);
- }
- }
- /* Disable individual receive */
- SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, discard);
- *R_NETWORK_REC_CONFIG = network_rec_config_shadow;
- }
- *R_NETWORK_GA_0 = lo_bits;
- *R_NETWORK_GA_1 = hi_bits;
- spin_unlock(&lp->lock);
-}
-
-void
-e100_hardware_send_packet(struct net_local *np, char *buf, int length)
-{
- D(printk("e100 send pack, buf 0x%x len %d\n", buf, length));
-
- spin_lock(&np->led_lock);
- if (!led_active && time_after(jiffies, led_next_time)) {
- /* light the network leds depending on the current speed. */
- e100_set_network_leds(NETWORK_ACTIVITY);
-
- /* Set the earliest time we may clear the LED */
- led_next_time = jiffies + NET_FLASH_TIME;
- led_active = 1;
- mod_timer(&clear_led_timer, jiffies + HZ/10);
- }
- spin_unlock(&np->led_lock);
-
- /* configure the tx dma descriptor */
- myNextTxDesc->descr.sw_len = length;
- myNextTxDesc->descr.ctrl = d_eop | d_eol | d_wait;
- myNextTxDesc->descr.buf = virt_to_phys(buf);
-
- /* Move end of list */
- myLastTxDesc->descr.ctrl &= ~d_eol;
- myLastTxDesc = myNextTxDesc;
-
- /* Restart DMA channel */
- *R_DMA_CH0_CMD = IO_STATE(R_DMA_CH0_CMD, cmd, restart);
-}
-
-static void
-e100_clear_network_leds(struct timer_list *unused)
-{
- struct net_device *dev = timer_dev;
- struct net_local *np = netdev_priv(dev);
-
- spin_lock(&np->led_lock);
-
- if (led_active && time_after(jiffies, led_next_time)) {
- e100_set_network_leds(NO_NETWORK_ACTIVITY);
-
- /* Set the earliest time we may set the LED */
- led_next_time = jiffies + NET_FLASH_PAUSE;
- led_active = 0;
- }
-
- spin_unlock(&np->led_lock);
-}
-
-static void
-e100_set_network_leds(int active)
-{
-#if defined(CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK)
- int light_leds = (active == NO_NETWORK_ACTIVITY);
-#elif defined(CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY)
- int light_leds = (active == NETWORK_ACTIVITY);
-#else
-#error "Define either CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK or CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY"
-#endif
-
- if (!current_speed) {
- /* Make LED red, link is down */
- CRIS_LED_NETWORK_SET(CRIS_LED_OFF);
- } else if (light_leds) {
- if (current_speed == 10) {
- CRIS_LED_NETWORK_SET(CRIS_LED_ORANGE);
- } else {
- CRIS_LED_NETWORK_SET(CRIS_LED_GREEN);
- }
- } else {
- CRIS_LED_NETWORK_SET(CRIS_LED_OFF);
- }
-}
-
-#ifdef CONFIG_NET_POLL_CONTROLLER
-static void
-e100_netpoll(struct net_device* netdev)
-{
- e100rxtx_interrupt(NETWORK_DMA_TX_IRQ_NBR, netdev);
-}
-#endif
-
-
-static int __init
-e100_boot_setup(char* str)
-{
- struct sockaddr sa = {0};
- int i;
-
- /* Parse the colon separated Ethernet station address */
- for (i = 0; i < ETH_ALEN; i++) {
- unsigned int tmp;
- if (sscanf(str + 3*i, "%2x", &tmp) != 1) {
- printk(KERN_WARNING "Malformed station address");
- return 0;
- }
- sa.sa_data[i] = (char)tmp;
- }
-
- default_mac = sa;
- return 1;
-}
-
-__setup("etrax100_eth=", e100_boot_setup);
diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c
index 63e02a54d537..78616787f2a3 100644
--- a/drivers/net/dsa/b53/b53_common.c
+++ b/drivers/net/dsa/b53/b53_common.c
@@ -852,7 +852,7 @@ void b53_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
}
EXPORT_SYMBOL(b53_get_ethtool_stats);
-int b53_get_sset_count(struct dsa_switch *ds)
+int b53_get_sset_count(struct dsa_switch *ds, int port)
{
struct b53_device *dev = ds->priv;
diff --git a/drivers/net/dsa/b53/b53_mmap.c b/drivers/net/dsa/b53/b53_mmap.c
index ef63d24fef81..c628d0980c0b 100644
--- a/drivers/net/dsa/b53/b53_mmap.c
+++ b/drivers/net/dsa/b53/b53_mmap.c
@@ -30,7 +30,8 @@ struct b53_mmap_priv {
static int b53_mmap_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
{
- u8 __iomem *regs = dev->priv;
+ struct b53_mmap_priv *priv = dev->priv;
+ void __iomem *regs = priv->regs;
*val = readb(regs + (page << 8) + reg);
@@ -39,7 +40,8 @@ static int b53_mmap_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
static int b53_mmap_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
{
- u8 __iomem *regs = dev->priv;
+ struct b53_mmap_priv *priv = dev->priv;
+ void __iomem *regs = priv->regs;
if (WARN_ON(reg % 2))
return -EINVAL;
@@ -54,7 +56,8 @@ static int b53_mmap_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
static int b53_mmap_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
{
- u8 __iomem *regs = dev->priv;
+ struct b53_mmap_priv *priv = dev->priv;
+ void __iomem *regs = priv->regs;
if (WARN_ON(reg % 4))
return -EINVAL;
@@ -69,7 +72,8 @@ static int b53_mmap_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
static int b53_mmap_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
{
- u8 __iomem *regs = dev->priv;
+ struct b53_mmap_priv *priv = dev->priv;
+ void __iomem *regs = priv->regs;
if (WARN_ON(reg % 2))
return -EINVAL;
@@ -107,7 +111,8 @@ static int b53_mmap_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
static int b53_mmap_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
{
- u8 __iomem *regs = dev->priv;
+ struct b53_mmap_priv *priv = dev->priv;
+ void __iomem *regs = priv->regs;
u32 hi, lo;
if (WARN_ON(reg % 4))
@@ -128,7 +133,8 @@ static int b53_mmap_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
static int b53_mmap_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
{
- u8 __iomem *regs = dev->priv;
+ struct b53_mmap_priv *priv = dev->priv;
+ void __iomem *regs = priv->regs;
writeb(value, regs + (page << 8) + reg);
@@ -138,7 +144,8 @@ static int b53_mmap_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
static int b53_mmap_write16(struct b53_device *dev, u8 page, u8 reg,
u16 value)
{
- u8 __iomem *regs = dev->priv;
+ struct b53_mmap_priv *priv = dev->priv;
+ void __iomem *regs = priv->regs;
if (WARN_ON(reg % 2))
return -EINVAL;
@@ -154,7 +161,8 @@ static int b53_mmap_write16(struct b53_device *dev, u8 page, u8 reg,
static int b53_mmap_write32(struct b53_device *dev, u8 page, u8 reg,
u32 value)
{
- u8 __iomem *regs = dev->priv;
+ struct b53_mmap_priv *priv = dev->priv;
+ void __iomem *regs = priv->regs;
if (WARN_ON(reg % 4))
return -EINVAL;
@@ -223,12 +231,19 @@ static const struct b53_io_ops b53_mmap_ops = {
static int b53_mmap_probe(struct platform_device *pdev)
{
struct b53_platform_data *pdata = pdev->dev.platform_data;
+ struct b53_mmap_priv *priv;
struct b53_device *dev;
if (!pdata)
return -EINVAL;
- dev = b53_switch_alloc(&pdev->dev, &b53_mmap_ops, pdata->regs);
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->regs = pdata->regs;
+
+ dev = b53_switch_alloc(&pdev->dev, &b53_mmap_ops, priv);
if (!dev)
return -ENOMEM;
diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h
index d954cf36ecd8..1187ebd79287 100644
--- a/drivers/net/dsa/b53/b53_priv.h
+++ b/drivers/net/dsa/b53/b53_priv.h
@@ -288,7 +288,7 @@ void b53_imp_vlan_setup(struct dsa_switch *ds, int cpu_port);
int b53_configure_vlan(struct dsa_switch *ds);
void b53_get_strings(struct dsa_switch *ds, int port, uint8_t *data);
void b53_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data);
-int b53_get_sset_count(struct dsa_switch *ds);
+int b53_get_sset_count(struct dsa_switch *ds, int port);
int b53_br_join(struct dsa_switch *ds, int port, struct net_device *bridge);
void b53_br_leave(struct dsa_switch *ds, int port, struct net_device *bridge);
void b53_br_set_stp_state(struct dsa_switch *ds, int port, u8 state);
diff --git a/drivers/net/dsa/dsa_loop.c b/drivers/net/dsa/dsa_loop.c
index 7aa84ee4e771..f77be9f85cb3 100644
--- a/drivers/net/dsa/dsa_loop.c
+++ b/drivers/net/dsa/dsa_loop.c
@@ -86,7 +86,7 @@ static int dsa_loop_setup(struct dsa_switch *ds)
return 0;
}
-static int dsa_loop_get_sset_count(struct dsa_switch *ds)
+static int dsa_loop_get_sset_count(struct dsa_switch *ds, int port)
{
return __DSA_LOOP_CNT_MAX;
}
diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c
index 6171c0853ff1..fefa454f3e56 100644
--- a/drivers/net/dsa/lan9303-core.c
+++ b/drivers/net/dsa/lan9303-core.c
@@ -1007,7 +1007,7 @@ static void lan9303_get_ethtool_stats(struct dsa_switch *ds, int port,
}
}
-static int lan9303_get_sset_count(struct dsa_switch *ds)
+static int lan9303_get_sset_count(struct dsa_switch *ds, int port)
{
return ARRAY_SIZE(lan9303_mib);
}
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index 663b0d5b982b..bcb3e6c734f2 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -439,7 +439,7 @@ static void ksz_disable_port(struct dsa_switch *ds, int port,
ksz_port_cfg(dev, port, REG_PORT_CTRL_0, PORT_MAC_LOOPBACK, true);
}
-static int ksz_sset_count(struct dsa_switch *ds)
+static int ksz_sset_count(struct dsa_switch *ds, int port)
{
return TOTAL_SWITCH_COUNTER_NUM;
}
diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c
index 8a0bb000d056..80a4dbc3a499 100644
--- a/drivers/net/dsa/mt7530.c
+++ b/drivers/net/dsa/mt7530.c
@@ -604,7 +604,7 @@ mt7530_get_ethtool_stats(struct dsa_switch *ds, int port,
}
static int
-mt7530_get_sset_count(struct dsa_switch *ds)
+mt7530_get_sset_count(struct dsa_switch *ds, int port)
{
return ARRAY_SIZE(mt7530_mib);
}
@@ -917,7 +917,7 @@ mt7530_port_fdb_add(struct dsa_switch *ds, int port,
mutex_lock(&priv->reg_mutex);
mt7530_fdb_write(priv, vid, port_mask, addr, -1, STATIC_ENT);
- ret = mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0);
+ ret = mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, NULL);
mutex_unlock(&priv->reg_mutex);
return ret;
@@ -933,7 +933,7 @@ mt7530_port_fdb_del(struct dsa_switch *ds, int port,
mutex_lock(&priv->reg_mutex);
mt7530_fdb_write(priv, vid, port_mask, addr, -1, STATIC_EMP);
- ret = mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0);
+ ret = mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, NULL);
mutex_unlock(&priv->reg_mutex);
return ret;
@@ -1293,7 +1293,7 @@ mt7530_setup(struct dsa_switch *ds)
}
/* Flush the FDB table */
- ret = mt7530_fdb_cmd(priv, MT7530_FDB_FLUSH, 0);
+ ret = mt7530_fdb_cmd(priv, MT7530_FDB_FLUSH, NULL);
if (ret < 0)
return ret;
@@ -1409,6 +1409,7 @@ static const struct of_device_id mt7530_of_match[] = {
{ .compatible = "mediatek,mt7530" },
{ /* sentinel */ },
};
+MODULE_DEVICE_TABLE(of, mt7530_of_match);
static struct mdio_driver mt7530_mdio_driver = {
.probe = mt7530_probe,
@@ -1424,4 +1425,3 @@ mdio_module_driver(mt7530_mdio_driver);
MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
MODULE_DESCRIPTION("Driver for Mediatek MT7530 Switch");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:mediatek-mt7530");
diff --git a/drivers/net/dsa/mv88e6xxx/Kconfig b/drivers/net/dsa/mv88e6xxx/Kconfig
index 1aaa7a95ebc4..ae9e7f7cb31c 100644
--- a/drivers/net/dsa/mv88e6xxx/Kconfig
+++ b/drivers/net/dsa/mv88e6xxx/Kconfig
@@ -18,3 +18,13 @@ config NET_DSA_MV88E6XXX_GLOBAL2
It is required on most chips. If the chip you compile the support for
doesn't have such registers set, say N here. In doubt, say Y.
+
+config NET_DSA_MV88E6XXX_PTP
+ bool "PTP support for Marvell 88E6xxx"
+ default n
+ depends on NET_DSA_MV88E6XXX_GLOBAL2
+ imply NETWORK_PHY_TIMESTAMPING
+ imply PTP_1588_CLOCK
+ help
+ Say Y to enable PTP hardware timestamping on Marvell 88E6xxx switch
+ chips that support it.
diff --git a/drivers/net/dsa/mv88e6xxx/Makefile b/drivers/net/dsa/mv88e6xxx/Makefile
index 58a4a0014e59..50de304abe2f 100644
--- a/drivers/net/dsa/mv88e6xxx/Makefile
+++ b/drivers/net/dsa/mv88e6xxx/Makefile
@@ -5,6 +5,10 @@ mv88e6xxx-objs += global1.o
mv88e6xxx-objs += global1_atu.o
mv88e6xxx-objs += global1_vtu.o
mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_GLOBAL2) += global2.o
+mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_GLOBAL2) += global2_avb.o
+mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_GLOBAL2) += global2_scratch.o
+mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += hwtstamp.o
mv88e6xxx-objs += phy.o
mv88e6xxx-objs += port.o
+mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += ptp.o
mv88e6xxx-objs += serdes.o
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index eb328bade225..3d2091099f7f 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -36,8 +36,10 @@
#include "chip.h"
#include "global1.h"
#include "global2.h"
+#include "hwtstamp.h"
#include "phy.h"
#include "port.h"
+#include "ptp.h"
#include "serdes.h"
static void assert_reg_lock(struct mv88e6xxx_chip *chip)
@@ -251,9 +253,8 @@ static void mv88e6xxx_g1_irq_unmask(struct irq_data *d)
chip->g1_irq.masked &= ~(1 << n);
}
-static irqreturn_t mv88e6xxx_g1_irq_thread_fn(int irq, void *dev_id)
+static irqreturn_t mv88e6xxx_g1_irq_thread_work(struct mv88e6xxx_chip *chip)
{
- struct mv88e6xxx_chip *chip = dev_id;
unsigned int nhandled = 0;
unsigned int sub_irq;
unsigned int n;
@@ -278,6 +279,13 @@ out:
return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE);
}
+static irqreturn_t mv88e6xxx_g1_irq_thread_fn(int irq, void *dev_id)
+{
+ struct mv88e6xxx_chip *chip = dev_id;
+
+ return mv88e6xxx_g1_irq_thread_work(chip);
+}
+
static void mv88e6xxx_g1_irq_bus_lock(struct irq_data *d)
{
struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
@@ -333,7 +341,7 @@ static const struct irq_domain_ops mv88e6xxx_g1_irq_domain_ops = {
.xlate = irq_domain_xlate_twocell,
};
-static void mv88e6xxx_g1_irq_free(struct mv88e6xxx_chip *chip)
+static void mv88e6xxx_g1_irq_free_common(struct mv88e6xxx_chip *chip)
{
int irq, virq;
u16 mask;
@@ -342,8 +350,6 @@ static void mv88e6xxx_g1_irq_free(struct mv88e6xxx_chip *chip)
mask &= ~GENMASK(chip->g1_irq.nirqs, 0);
mv88e6xxx_g1_write(chip, MV88E6XXX_G1_CTL1, mask);
- free_irq(chip->irq, chip);
-
for (irq = 0; irq < chip->g1_irq.nirqs; irq++) {
virq = irq_find_mapping(chip->g1_irq.domain, irq);
irq_dispose_mapping(virq);
@@ -352,7 +358,14 @@ static void mv88e6xxx_g1_irq_free(struct mv88e6xxx_chip *chip)
irq_domain_remove(chip->g1_irq.domain);
}
-static int mv88e6xxx_g1_irq_setup(struct mv88e6xxx_chip *chip)
+static void mv88e6xxx_g1_irq_free(struct mv88e6xxx_chip *chip)
+{
+ mv88e6xxx_g1_irq_free_common(chip);
+
+ free_irq(chip->irq, chip);
+}
+
+static int mv88e6xxx_g1_irq_setup_common(struct mv88e6xxx_chip *chip)
{
int err, irq, virq;
u16 reg, mask;
@@ -385,13 +398,6 @@ static int mv88e6xxx_g1_irq_setup(struct mv88e6xxx_chip *chip)
if (err)
goto out_disable;
- err = request_threaded_irq(chip->irq, NULL,
- mv88e6xxx_g1_irq_thread_fn,
- IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
- dev_name(chip->dev), chip);
- if (err)
- goto out_disable;
-
return 0;
out_disable:
@@ -409,6 +415,64 @@ out_mapping:
return err;
}
+static int mv88e6xxx_g1_irq_setup(struct mv88e6xxx_chip *chip)
+{
+ int err;
+
+ err = mv88e6xxx_g1_irq_setup_common(chip);
+ if (err)
+ return err;
+
+ err = request_threaded_irq(chip->irq, NULL,
+ mv88e6xxx_g1_irq_thread_fn,
+ IRQF_ONESHOT,
+ dev_name(chip->dev), chip);
+ if (err)
+ mv88e6xxx_g1_irq_free_common(chip);
+
+ return err;
+}
+
+static void mv88e6xxx_irq_poll(struct kthread_work *work)
+{
+ struct mv88e6xxx_chip *chip = container_of(work,
+ struct mv88e6xxx_chip,
+ irq_poll_work.work);
+ mv88e6xxx_g1_irq_thread_work(chip);
+
+ kthread_queue_delayed_work(chip->kworker, &chip->irq_poll_work,
+ msecs_to_jiffies(100));
+}
+
+static int mv88e6xxx_irq_poll_setup(struct mv88e6xxx_chip *chip)
+{
+ int err;
+
+ err = mv88e6xxx_g1_irq_setup_common(chip);
+ if (err)
+ return err;
+
+ kthread_init_delayed_work(&chip->irq_poll_work,
+ mv88e6xxx_irq_poll);
+
+ chip->kworker = kthread_create_worker(0, dev_name(chip->dev));
+ if (IS_ERR(chip->kworker))
+ return PTR_ERR(chip->kworker);
+
+ kthread_queue_delayed_work(chip->kworker, &chip->irq_poll_work,
+ msecs_to_jiffies(100));
+
+ return 0;
+}
+
+static void mv88e6xxx_irq_poll_free(struct mv88e6xxx_chip *chip)
+{
+ mv88e6xxx_g1_irq_free_common(chip);
+
+ kthread_cancel_delayed_work_sync(&chip->irq_poll_work);
+ kthread_destroy_worker(chip->kworker);
+}
+
int mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int addr, int reg, u16 mask)
{
int i;
@@ -604,7 +668,7 @@ static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip,
return UINT64_MAX;
low = reg;
- if (s->sizeof_stat == 4) {
+ if (s->size == 4) {
err = mv88e6xxx_port_read(chip, port, s->reg + 1, &reg);
if (err)
return UINT64_MAX;
@@ -617,7 +681,7 @@ static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip,
case STATS_TYPE_BANK0:
reg |= s->reg | histogram;
mv88e6xxx_g1_stats_read(chip, reg, &low);
- if (s->sizeof_stat == 8)
+ if (s->size == 8)
mv88e6xxx_g1_stats_read(chip, reg + 1, &high);
break;
default:
@@ -627,8 +691,8 @@ static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip,
return value;
}
-static void mv88e6xxx_stats_get_strings(struct mv88e6xxx_chip *chip,
- uint8_t *data, int types)
+static int mv88e6xxx_stats_get_strings(struct mv88e6xxx_chip *chip,
+ uint8_t *data, int types)
{
struct mv88e6xxx_hw_stat *stat;
int i, j;
@@ -641,29 +705,62 @@ static void mv88e6xxx_stats_get_strings(struct mv88e6xxx_chip *chip,
j++;
}
}
+
+ return j;
}
-static void mv88e6095_stats_get_strings(struct mv88e6xxx_chip *chip,
- uint8_t *data)
+static int mv88e6095_stats_get_strings(struct mv88e6xxx_chip *chip,
+ uint8_t *data)
{
- mv88e6xxx_stats_get_strings(chip, data,
- STATS_TYPE_BANK0 | STATS_TYPE_PORT);
+ return mv88e6xxx_stats_get_strings(chip, data,
+ STATS_TYPE_BANK0 | STATS_TYPE_PORT);
}
-static void mv88e6320_stats_get_strings(struct mv88e6xxx_chip *chip,
- uint8_t *data)
+static int mv88e6320_stats_get_strings(struct mv88e6xxx_chip *chip,
+ uint8_t *data)
{
- mv88e6xxx_stats_get_strings(chip, data,
- STATS_TYPE_BANK0 | STATS_TYPE_BANK1);
+ return mv88e6xxx_stats_get_strings(chip, data,
+ STATS_TYPE_BANK0 | STATS_TYPE_BANK1);
+}
+
+static const uint8_t *mv88e6xxx_atu_vtu_stats_strings[] = {
+ "atu_member_violation",
+ "atu_miss_violation",
+ "atu_full_violation",
+ "vtu_member_violation",
+ "vtu_miss_violation",
+};
+
+static void mv88e6xxx_atu_vtu_get_strings(uint8_t *data)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(mv88e6xxx_atu_vtu_stats_strings); i++)
+ strlcpy(data + i * ETH_GSTRING_LEN,
+ mv88e6xxx_atu_vtu_stats_strings[i],
+ ETH_GSTRING_LEN);
}
static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port,
uint8_t *data)
{
struct mv88e6xxx_chip *chip = ds->priv;
+ int count = 0;
+
+ mutex_lock(&chip->reg_lock);
if (chip->info->ops->stats_get_strings)
- chip->info->ops->stats_get_strings(chip, data);
+ count = chip->info->ops->stats_get_strings(chip, data);
+
+ if (chip->info->ops->serdes_get_strings) {
+ data += count * ETH_GSTRING_LEN;
+ count = chip->info->ops->serdes_get_strings(chip, port, data);
+ }
+
+ data += count * ETH_GSTRING_LEN;
+ mv88e6xxx_atu_vtu_get_strings(data);
+
+ mutex_unlock(&chip->reg_lock);
}
static int mv88e6xxx_stats_get_sset_count(struct mv88e6xxx_chip *chip,
@@ -692,19 +789,37 @@ static int mv88e6320_stats_get_sset_count(struct mv88e6xxx_chip *chip)
STATS_TYPE_BANK1);
}
-static int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
+static int mv88e6xxx_get_sset_count(struct dsa_switch *ds, int port)
{
struct mv88e6xxx_chip *chip = ds->priv;
+ int serdes_count = 0;
+ int count = 0;
+ mutex_lock(&chip->reg_lock);
if (chip->info->ops->stats_get_sset_count)
- return chip->info->ops->stats_get_sset_count(chip);
+ count = chip->info->ops->stats_get_sset_count(chip);
+ if (count < 0)
+ goto out;
- return 0;
+ if (chip->info->ops->serdes_get_sset_count)
+ serdes_count = chip->info->ops->serdes_get_sset_count(chip,
+ port);
+ if (serdes_count < 0) {
+ count = serdes_count;
+ goto out;
+ }
+ count += serdes_count;
+ count += ARRAY_SIZE(mv88e6xxx_atu_vtu_stats_strings);
+
+out:
+ mutex_unlock(&chip->reg_lock);
+
+ return count;
}
-static void mv88e6xxx_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
- uint64_t *data, int types,
- u16 bank1_select, u16 histogram)
+static int mv88e6xxx_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
+ uint64_t *data, int types,
+ u16 bank1_select, u16 histogram)
{
struct mv88e6xxx_hw_stat *stat;
int i, j;
@@ -712,24 +827,28 @@ static void mv88e6xxx_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
stat = &mv88e6xxx_hw_stats[i];
if (stat->type & types) {
+ mutex_lock(&chip->reg_lock);
data[j] = _mv88e6xxx_get_ethtool_stat(chip, stat, port,
bank1_select,
histogram);
+ mutex_unlock(&chip->reg_lock);
+
j++;
}
}
+ return j;
}
-static void mv88e6095_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
- uint64_t *data)
+static int mv88e6095_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
+ uint64_t *data)
{
return mv88e6xxx_stats_get_stats(chip, port, data,
STATS_TYPE_BANK0 | STATS_TYPE_PORT,
0, MV88E6XXX_G1_STATS_OP_HIST_RX_TX);
}
-static void mv88e6320_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
- uint64_t *data)
+static int mv88e6320_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
+ uint64_t *data)
{
return mv88e6xxx_stats_get_stats(chip, port, data,
STATS_TYPE_BANK0 | STATS_TYPE_BANK1,
@@ -737,8 +856,8 @@ static void mv88e6320_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
MV88E6XXX_G1_STATS_OP_HIST_RX_TX);
}
-static void mv88e6390_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
- uint64_t *data)
+static int mv88e6390_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
+ uint64_t *data)
{
return mv88e6xxx_stats_get_stats(chip, port, data,
STATS_TYPE_BANK0 | STATS_TYPE_BANK1,
@@ -746,11 +865,32 @@ static void mv88e6390_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
0);
}
+static void mv88e6xxx_atu_vtu_get_stats(struct mv88e6xxx_chip *chip, int port,
+ uint64_t *data)
+{
+ *data++ = chip->ports[port].atu_member_violation;
+ *data++ = chip->ports[port].atu_miss_violation;
+ *data++ = chip->ports[port].atu_full_violation;
+ *data++ = chip->ports[port].vtu_member_violation;
+ *data++ = chip->ports[port].vtu_miss_violation;
+}
+
static void mv88e6xxx_get_stats(struct mv88e6xxx_chip *chip, int port,
uint64_t *data)
{
+ int count = 0;
+
if (chip->info->ops->stats_get_stats)
- chip->info->ops->stats_get_stats(chip, port, data);
+ count = chip->info->ops->stats_get_stats(chip, port, data);
+
+ mutex_lock(&chip->reg_lock);
+ if (chip->info->ops->serdes_get_stats) {
+ data += count;
+ count = chip->info->ops->serdes_get_stats(chip, port, data);
+ }
+ data += count;
+ mv88e6xxx_atu_vtu_get_stats(chip, port, data);
+ mutex_unlock(&chip->reg_lock);
}
static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
@@ -762,14 +902,13 @@ static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
mutex_lock(&chip->reg_lock);
ret = mv88e6xxx_stats_snapshot(chip, port);
- if (ret < 0) {
- mutex_unlock(&chip->reg_lock);
+ mutex_unlock(&chip->reg_lock);
+
+ if (ret < 0)
return;
- }
mv88e6xxx_get_stats(chip, port, data);
- mutex_unlock(&chip->reg_lock);
}
static int mv88e6xxx_stats_set_histogram(struct mv88e6xxx_chip *chip)
@@ -1433,7 +1572,9 @@ static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip,
eth_broadcast_addr(addr.mac);
do {
+ mutex_lock(&chip->reg_lock);
err = mv88e6xxx_g1_atu_getnext(chip, fid, &addr);
+ mutex_unlock(&chip->reg_lock);
if (err)
return err;
@@ -1466,7 +1607,10 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port,
int err;
/* Dump port's default Filtering Information Database (VLAN ID 0) */
+ mutex_lock(&chip->reg_lock);
err = mv88e6xxx_port_get_fid(chip, port, &fid);
+ mutex_unlock(&chip->reg_lock);
+
if (err)
return err;
@@ -1476,7 +1620,9 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port,
/* Dump VLANs' Filtering Information Databases */
do {
+ mutex_lock(&chip->reg_lock);
err = mv88e6xxx_vtu_getnext(chip, &vlan);
+ mutex_unlock(&chip->reg_lock);
if (err)
return err;
@@ -1496,13 +1642,8 @@ static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
dsa_fdb_dump_cb_t *cb, void *data)
{
struct mv88e6xxx_chip *chip = ds->priv;
- int err;
-
- mutex_lock(&chip->reg_lock);
- err = mv88e6xxx_port_db_dump(chip, port, cb, data);
- mutex_unlock(&chip->reg_lock);
- return err;
+ return mv88e6xxx_port_db_dump(chip, port, cb, data);
}
static int mv88e6xxx_bridge_map(struct mv88e6xxx_chip *chip,
@@ -2092,6 +2233,17 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)
if (err)
goto unlock;
+ /* Setup PTP Hardware Clock and timestamping */
+ if (chip->info->ptp_support) {
+ err = mv88e6xxx_ptp_setup(chip);
+ if (err)
+ goto unlock;
+
+ err = mv88e6xxx_hwtstamp_setup(chip);
+ if (err)
+ goto unlock;
+ }
+
unlock:
mutex_unlock(&chip->reg_lock);
@@ -2148,6 +2300,15 @@ static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip,
struct mii_bus *bus;
int err;
+ if (external) {
+ mutex_lock(&chip->reg_lock);
+ err = mv88e6xxx_g2_scratch_gpio_set_smi(chip, true);
+ mutex_unlock(&chip->reg_lock);
+
+ if (err)
+ return err;
+ }
+
bus = devm_mdiobus_alloc_size(chip->dev, sizeof(*mdio_bus));
if (!bus)
return -ENOMEM;
@@ -2170,12 +2331,19 @@ static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip,
bus->write = mv88e6xxx_mdio_write;
bus->parent = chip->dev;
+ if (!external) {
+ err = mv88e6xxx_g2_irq_mdio_setup(chip, bus);
+ if (err)
+ return err;
+ }
+
if (np)
err = of_mdiobus_register(bus, np);
else
err = mdiobus_register(bus);
if (err) {
dev_err(chip->dev, "Cannot register MDIO bus (%d)\n", err);
+ mv88e6xxx_g2_irq_mdio_free(chip, bus);
return err;
}
@@ -2202,6 +2370,9 @@ static void mv88e6xxx_mdios_unregister(struct mv88e6xxx_chip *chip)
list_for_each_entry(mdio_bus, &chip->mdios, list) {
bus = mdio_bus->bus;
+ if (!mdio_bus->external)
+ mv88e6xxx_g2_irq_mdio_free(chip, bus);
+
mdiobus_unregister(bus);
}
}
@@ -2472,6 +2643,7 @@ static const struct mv88e6xxx_ops mv88e6141_ops = {
.reset = mv88e6352_g1_reset,
.vtu_getnext = mv88e6352_g1_vtu_getnext,
.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
+ .gpio_ops = &mv88e6352_gpio_ops,
};
static const struct mv88e6xxx_ops mv88e6161_ops = {
@@ -2602,6 +2774,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = {
.vtu_getnext = mv88e6352_g1_vtu_getnext,
.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
.serdes_power = mv88e6352_serdes_power,
+ .gpio_ops = &mv88e6352_gpio_ops,
};
static const struct mv88e6xxx_ops mv88e6175_ops = {
@@ -2673,6 +2846,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = {
.vtu_getnext = mv88e6352_g1_vtu_getnext,
.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
.serdes_power = mv88e6352_serdes_power,
+ .gpio_ops = &mv88e6352_gpio_ops,
};
static const struct mv88e6xxx_ops mv88e6185_ops = {
@@ -2736,6 +2910,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = {
.vtu_getnext = mv88e6390_g1_vtu_getnext,
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
.serdes_power = mv88e6390_serdes_power,
+ .gpio_ops = &mv88e6352_gpio_ops,
};
static const struct mv88e6xxx_ops mv88e6190x_ops = {
@@ -2771,6 +2946,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = {
.vtu_getnext = mv88e6390_g1_vtu_getnext,
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
.serdes_power = mv88e6390_serdes_power,
+ .gpio_ops = &mv88e6352_gpio_ops,
};
static const struct mv88e6xxx_ops mv88e6191_ops = {
@@ -2843,6 +3019,8 @@ static const struct mv88e6xxx_ops mv88e6240_ops = {
.vtu_getnext = mv88e6352_g1_vtu_getnext,
.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
.serdes_power = mv88e6352_serdes_power,
+ .gpio_ops = &mv88e6352_gpio_ops,
+ .avb_ops = &mv88e6352_avb_ops,
};
static const struct mv88e6xxx_ops mv88e6290_ops = {
@@ -2879,6 +3057,8 @@ static const struct mv88e6xxx_ops mv88e6290_ops = {
.vtu_getnext = mv88e6390_g1_vtu_getnext,
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
.serdes_power = mv88e6390_serdes_power,
+ .gpio_ops = &mv88e6352_gpio_ops,
+ .avb_ops = &mv88e6390_avb_ops,
};
static const struct mv88e6xxx_ops mv88e6320_ops = {
@@ -2913,6 +3093,8 @@ static const struct mv88e6xxx_ops mv88e6320_ops = {
.reset = mv88e6352_g1_reset,
.vtu_getnext = mv88e6185_g1_vtu_getnext,
.vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
+ .gpio_ops = &mv88e6352_gpio_ops,
+ .avb_ops = &mv88e6352_avb_ops,
};
static const struct mv88e6xxx_ops mv88e6321_ops = {
@@ -2945,6 +3127,8 @@ static const struct mv88e6xxx_ops mv88e6321_ops = {
.reset = mv88e6352_g1_reset,
.vtu_getnext = mv88e6185_g1_vtu_getnext,
.vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
+ .gpio_ops = &mv88e6352_gpio_ops,
+ .avb_ops = &mv88e6352_avb_ops,
};
static const struct mv88e6xxx_ops mv88e6341_ops = {
@@ -2981,6 +3165,8 @@ static const struct mv88e6xxx_ops mv88e6341_ops = {
.reset = mv88e6352_g1_reset,
.vtu_getnext = mv88e6352_g1_vtu_getnext,
.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
+ .gpio_ops = &mv88e6352_gpio_ops,
+ .avb_ops = &mv88e6390_avb_ops,
};
static const struct mv88e6xxx_ops mv88e6350_ops = {
@@ -3049,6 +3235,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = {
.reset = mv88e6352_g1_reset,
.vtu_getnext = mv88e6352_g1_vtu_getnext,
.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
+ .avb_ops = &mv88e6352_avb_ops,
};
static const struct mv88e6xxx_ops mv88e6352_ops = {
@@ -3086,6 +3273,11 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {
.vtu_getnext = mv88e6352_g1_vtu_getnext,
.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
.serdes_power = mv88e6352_serdes_power,
+ .gpio_ops = &mv88e6352_gpio_ops,
+ .avb_ops = &mv88e6352_avb_ops,
+ .serdes_get_sset_count = mv88e6352_serdes_get_sset_count,
+ .serdes_get_strings = mv88e6352_serdes_get_strings,
+ .serdes_get_stats = mv88e6352_serdes_get_stats,
};
static const struct mv88e6xxx_ops mv88e6390_ops = {
@@ -3124,6 +3316,8 @@ static const struct mv88e6xxx_ops mv88e6390_ops = {
.vtu_getnext = mv88e6390_g1_vtu_getnext,
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
.serdes_power = mv88e6390_serdes_power,
+ .gpio_ops = &mv88e6352_gpio_ops,
+ .avb_ops = &mv88e6390_avb_ops,
};
static const struct mv88e6xxx_ops mv88e6390x_ops = {
@@ -3162,6 +3356,8 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = {
.vtu_getnext = mv88e6390_g1_vtu_getnext,
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
.serdes_power = mv88e6390_serdes_power,
+ .gpio_ops = &mv88e6352_gpio_ops,
+ .avb_ops = &mv88e6390_avb_ops,
};
static const struct mv88e6xxx_info mv88e6xxx_table[] = {
@@ -3171,6 +3367,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.name = "Marvell 88E6085",
.num_databases = 4096,
.num_ports = 10,
+ .num_internal_phys = 5,
.max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
@@ -3191,6 +3388,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.name = "Marvell 88E6095/88E6095F",
.num_databases = 256,
.num_ports = 11,
+ .num_internal_phys = 0,
.max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
@@ -3209,6 +3407,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.name = "Marvell 88E6097/88E6097F",
.num_databases = 4096,
.num_ports = 11,
+ .num_internal_phys = 8,
.max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
@@ -3229,6 +3428,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.name = "Marvell 88E6123",
.num_databases = 4096,
.num_ports = 3,
+ .num_internal_phys = 5,
.max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
@@ -3249,6 +3449,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.name = "Marvell 88E6131",
.num_databases = 256,
.num_ports = 8,
+ .num_internal_phys = 0,
.max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
@@ -3264,15 +3465,18 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
[MV88E6141] = {
.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6141,
.family = MV88E6XXX_FAMILY_6341,
- .name = "Marvell 88E6341",
+ .name = "Marvell 88E6141",
.num_databases = 4096,
.num_ports = 6,
+ .num_internal_phys = 5,
+ .num_gpio = 11,
.max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
.global2_addr = 0x1c,
.age_time_coeff = 3750,
.atu_move_port_mask = 0x1f,
+ .g1_irqs = 9,
.g2_irqs = 10,
.pvt = true,
.multi_chip = true,
@@ -3286,6 +3490,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.name = "Marvell 88E6161",
.num_databases = 4096,
.num_ports = 6,
+ .num_internal_phys = 5,
.max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
@@ -3306,6 +3511,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.name = "Marvell 88E6165",
.num_databases = 4096,
.num_ports = 6,
+ .num_internal_phys = 0,
.max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
@@ -3326,6 +3532,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.name = "Marvell 88E6171",
.num_databases = 4096,
.num_ports = 7,
+ .num_internal_phys = 5,
.max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
@@ -3346,6 +3553,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.name = "Marvell 88E6172",
.num_databases = 4096,
.num_ports = 7,
+ .num_internal_phys = 5,
+ .num_gpio = 15,
.max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
@@ -3366,6 +3575,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.name = "Marvell 88E6175",
.num_databases = 4096,
.num_ports = 7,
+ .num_internal_phys = 5,
.max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
@@ -3386,6 +3596,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.name = "Marvell 88E6176",
.num_databases = 4096,
.num_ports = 7,
+ .num_internal_phys = 5,
+ .num_gpio = 15,
.max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
@@ -3406,6 +3618,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.name = "Marvell 88E6185",
.num_databases = 256,
.num_ports = 10,
+ .num_internal_phys = 0,
.max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
@@ -3424,6 +3637,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.name = "Marvell 88E6190",
.num_databases = 4096,
.num_ports = 11, /* 10 + Z80 */
+ .num_internal_phys = 11,
+ .num_gpio = 16,
.max_vid = 8191,
.port_base_addr = 0x0,
.global1_addr = 0x1b,
@@ -3444,6 +3659,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.name = "Marvell 88E6190X",
.num_databases = 4096,
.num_ports = 11, /* 10 + Z80 */
+ .num_internal_phys = 11,
+ .num_gpio = 16,
.max_vid = 8191,
.port_base_addr = 0x0,
.global1_addr = 0x1b,
@@ -3464,6 +3681,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.name = "Marvell 88E6191",
.num_databases = 4096,
.num_ports = 11, /* 10 + Z80 */
+ .num_internal_phys = 11,
.max_vid = 8191,
.port_base_addr = 0x0,
.global1_addr = 0x1b,
@@ -3475,6 +3693,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.pvt = true,
.multi_chip = true,
.tag_protocol = DSA_TAG_PROTO_DSA,
+ .ptp_support = true,
.ops = &mv88e6191_ops,
},
@@ -3484,6 +3703,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.name = "Marvell 88E6240",
.num_databases = 4096,
.num_ports = 7,
+ .num_internal_phys = 5,
+ .num_gpio = 15,
.max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
@@ -3495,6 +3716,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.pvt = true,
.multi_chip = true,
.tag_protocol = DSA_TAG_PROTO_EDSA,
+ .ptp_support = true,
.ops = &mv88e6240_ops,
},
@@ -3504,6 +3726,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.name = "Marvell 88E6290",
.num_databases = 4096,
.num_ports = 11, /* 10 + Z80 */
+ .num_internal_phys = 11,
+ .num_gpio = 16,
.max_vid = 8191,
.port_base_addr = 0x0,
.global1_addr = 0x1b,
@@ -3515,6 +3739,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.pvt = true,
.multi_chip = true,
.tag_protocol = DSA_TAG_PROTO_DSA,
+ .ptp_support = true,
.ops = &mv88e6290_ops,
},
@@ -3524,16 +3749,20 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.name = "Marvell 88E6320",
.num_databases = 4096,
.num_ports = 7,
+ .num_internal_phys = 5,
+ .num_gpio = 15,
.max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
.global2_addr = 0x1c,
.age_time_coeff = 15000,
.g1_irqs = 8,
+ .g2_irqs = 10,
.atu_move_port_mask = 0xf,
.pvt = true,
.multi_chip = true,
.tag_protocol = DSA_TAG_PROTO_EDSA,
+ .ptp_support = true,
.ops = &mv88e6320_ops,
},
@@ -3543,15 +3772,19 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.name = "Marvell 88E6321",
.num_databases = 4096,
.num_ports = 7,
+ .num_internal_phys = 5,
+ .num_gpio = 15,
.max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
.global2_addr = 0x1c,
.age_time_coeff = 15000,
.g1_irqs = 8,
+ .g2_irqs = 10,
.atu_move_port_mask = 0xf,
.multi_chip = true,
.tag_protocol = DSA_TAG_PROTO_EDSA,
+ .ptp_support = true,
.ops = &mv88e6321_ops,
},
@@ -3560,17 +3793,21 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.family = MV88E6XXX_FAMILY_6341,
.name = "Marvell 88E6341",
.num_databases = 4096,
+ .num_internal_phys = 5,
.num_ports = 6,
+ .num_gpio = 11,
.max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
.global2_addr = 0x1c,
.age_time_coeff = 3750,
.atu_move_port_mask = 0x1f,
+ .g1_irqs = 9,
.g2_irqs = 10,
.pvt = true,
.multi_chip = true,
.tag_protocol = DSA_TAG_PROTO_EDSA,
+ .ptp_support = true,
.ops = &mv88e6341_ops,
},
@@ -3580,6 +3817,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.name = "Marvell 88E6350",
.num_databases = 4096,
.num_ports = 7,
+ .num_internal_phys = 5,
.max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
@@ -3600,6 +3838,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.name = "Marvell 88E6351",
.num_databases = 4096,
.num_ports = 7,
+ .num_internal_phys = 5,
.max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
@@ -3620,6 +3859,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.name = "Marvell 88E6352",
.num_databases = 4096,
.num_ports = 7,
+ .num_internal_phys = 5,
+ .num_gpio = 15,
.max_vid = 4095,
.port_base_addr = 0x10,
.global1_addr = 0x1b,
@@ -3631,6 +3872,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.pvt = true,
.multi_chip = true,
.tag_protocol = DSA_TAG_PROTO_EDSA,
+ .ptp_support = true,
.ops = &mv88e6352_ops,
},
[MV88E6390] = {
@@ -3639,6 +3881,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.name = "Marvell 88E6390",
.num_databases = 4096,
.num_ports = 11, /* 10 + Z80 */
+ .num_internal_phys = 11,
+ .num_gpio = 16,
.max_vid = 8191,
.port_base_addr = 0x0,
.global1_addr = 0x1b,
@@ -3650,6 +3894,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.pvt = true,
.multi_chip = true,
.tag_protocol = DSA_TAG_PROTO_DSA,
+ .ptp_support = true,
.ops = &mv88e6390_ops,
},
[MV88E6390X] = {
@@ -3658,6 +3903,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.name = "Marvell 88E6390X",
.num_databases = 4096,
.num_ports = 11, /* 10 + Z80 */
+ .num_internal_phys = 11,
+ .num_gpio = 16,
.max_vid = 8191,
.port_base_addr = 0x0,
.global1_addr = 0x1b,
@@ -3669,6 +3916,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.pvt = true,
.multi_chip = true,
.tag_protocol = DSA_TAG_PROTO_DSA,
+ .ptp_support = true,
.ops = &mv88e6390x_ops,
},
};
@@ -3880,6 +4128,11 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
.port_mdb_del = mv88e6xxx_port_mdb_del,
.crosschip_bridge_join = mv88e6xxx_crosschip_bridge_join,
.crosschip_bridge_leave = mv88e6xxx_crosschip_bridge_leave,
+ .port_hwtstamp_set = mv88e6xxx_port_hwtstamp_set,
+ .port_hwtstamp_get = mv88e6xxx_port_hwtstamp_get,
+ .port_txtstamp = mv88e6xxx_port_txtstamp,
+ .port_rxtstamp = mv88e6xxx_port_rxtstamp,
+ .get_ts_info = mv88e6xxx_get_ts_info,
};
static struct dsa_switch_driver mv88e6xxx_switch_drv = {
@@ -3959,33 +4212,34 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev)
goto out;
}
- if (chip->irq > 0) {
- /* Has to be performed before the MDIO bus is created,
- * because the PHYs will link there interrupts to these
- * interrupt controllers
- */
- mutex_lock(&chip->reg_lock);
+ /* Has to be performed before the MDIO bus is created, because
+ * the PHYs will link their interrupts to these interrupt
+ * controllers
+ */
+ mutex_lock(&chip->reg_lock);
+ if (chip->irq > 0)
err = mv88e6xxx_g1_irq_setup(chip);
- mutex_unlock(&chip->reg_lock);
-
- if (err)
- goto out;
-
- if (chip->info->g2_irqs > 0) {
- err = mv88e6xxx_g2_irq_setup(chip);
- if (err)
- goto out_g1_irq;
- }
+ else
+ err = mv88e6xxx_irq_poll_setup(chip);
+ mutex_unlock(&chip->reg_lock);
- err = mv88e6xxx_g1_atu_prob_irq_setup(chip);
- if (err)
- goto out_g2_irq;
+ if (err)
+ goto out;
- err = mv88e6xxx_g1_vtu_prob_irq_setup(chip);
+ if (chip->info->g2_irqs > 0) {
+ err = mv88e6xxx_g2_irq_setup(chip);
if (err)
- goto out_g1_atu_prob_irq;
+ goto out_g1_irq;
}
+ err = mv88e6xxx_g1_atu_prob_irq_setup(chip);
+ if (err)
+ goto out_g2_irq;
+
+ err = mv88e6xxx_g1_vtu_prob_irq_setup(chip);
+ if (err)
+ goto out_g1_atu_prob_irq;
+
err = mv88e6xxx_mdios_register(chip, np);
if (err)
goto out_g1_vtu_prob_irq;
@@ -3999,20 +4253,19 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev)
out_mdio:
mv88e6xxx_mdios_unregister(chip);
out_g1_vtu_prob_irq:
- if (chip->irq > 0)
- mv88e6xxx_g1_vtu_prob_irq_free(chip);
+ mv88e6xxx_g1_vtu_prob_irq_free(chip);
out_g1_atu_prob_irq:
- if (chip->irq > 0)
- mv88e6xxx_g1_atu_prob_irq_free(chip);
+ mv88e6xxx_g1_atu_prob_irq_free(chip);
out_g2_irq:
- if (chip->info->g2_irqs > 0 && chip->irq > 0)
+ if (chip->info->g2_irqs > 0)
mv88e6xxx_g2_irq_free(chip);
out_g1_irq:
- if (chip->irq > 0) {
- mutex_lock(&chip->reg_lock);
+ mutex_lock(&chip->reg_lock);
+ if (chip->irq > 0)
mv88e6xxx_g1_irq_free(chip);
- mutex_unlock(&chip->reg_lock);
- }
+ else
+ mv88e6xxx_irq_poll_free(chip);
+ mutex_unlock(&chip->reg_lock);
out:
return err;
}
@@ -4022,19 +4275,27 @@ static void mv88e6xxx_remove(struct mdio_device *mdiodev)
struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
struct mv88e6xxx_chip *chip = ds->priv;
+ if (chip->info->ptp_support) {
+ mv88e6xxx_hwtstamp_free(chip);
+ mv88e6xxx_ptp_free(chip);
+ }
+
mv88e6xxx_phy_destroy(chip);
mv88e6xxx_unregister_switch(chip);
mv88e6xxx_mdios_unregister(chip);
- if (chip->irq > 0) {
- mv88e6xxx_g1_vtu_prob_irq_free(chip);
- mv88e6xxx_g1_atu_prob_irq_free(chip);
- if (chip->info->g2_irqs > 0)
- mv88e6xxx_g2_irq_free(chip);
- mutex_lock(&chip->reg_lock);
+ mv88e6xxx_g1_vtu_prob_irq_free(chip);
+ mv88e6xxx_g1_atu_prob_irq_free(chip);
+
+ if (chip->info->g2_irqs > 0)
+ mv88e6xxx_g2_irq_free(chip);
+
+ mutex_lock(&chip->reg_lock);
+ if (chip->irq > 0)
mv88e6xxx_g1_irq_free(chip);
- mutex_unlock(&chip->reg_lock);
- }
+ else
+ mv88e6xxx_irq_poll_free(chip);
+ mutex_unlock(&chip->reg_lock);
}
static const struct of_device_id mv88e6xxx_of_match[] = {
diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
index 3dba6e90adcf..80490f66bc06 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.h
+++ b/drivers/net/dsa/mv88e6xxx/chip.h
@@ -15,7 +15,10 @@
#include <linux/if_vlan.h>
#include <linux/irq.h>
#include <linux/gpio/consumer.h>
+#include <linux/kthread.h>
#include <linux/phy.h>
+#include <linux/ptp_clock_kernel.h>
+#include <linux/timecounter.h>
#include <net/dsa.h>
#ifndef UINT64_MAX
@@ -39,6 +42,8 @@
#define MV88E6XXX_MAX_PVT_SWITCHES 32
#define MV88E6XXX_MAX_PVT_PORTS 16
+#define MV88E6XXX_MAX_GPIO 16
+
enum mv88e6xxx_egress_mode {
MV88E6XXX_EGRESS_MODE_UNMODIFIED,
MV88E6XXX_EGRESS_MODE_UNTAGGED,
@@ -105,6 +110,8 @@ struct mv88e6xxx_info {
const char *name;
unsigned int num_databases;
unsigned int num_ports;
+ unsigned int num_internal_phys;
+ unsigned int num_gpio;
unsigned int max_vid;
unsigned int port_base_addr;
unsigned int global1_addr;
@@ -126,6 +133,9 @@ struct mv88e6xxx_info {
*/
u8 atu_move_port_mask;
const struct mv88e6xxx_ops *ops;
+
+ /* Supports PTP */
+ bool ptp_support;
};
struct mv88e6xxx_atu_entry {
@@ -146,6 +156,8 @@ struct mv88e6xxx_vtu_entry {
struct mv88e6xxx_bus_ops;
struct mv88e6xxx_irq_ops;
+struct mv88e6xxx_gpio_ops;
+struct mv88e6xxx_avb_ops;
struct mv88e6xxx_irq {
u16 masked;
@@ -154,6 +166,41 @@ struct mv88e6xxx_irq {
unsigned int nirqs;
};
+/* state flags for mv88e6xxx_port_hwtstamp::state */
+enum {
+ MV88E6XXX_HWTSTAMP_ENABLED,
+ MV88E6XXX_HWTSTAMP_TX_IN_PROGRESS,
+};
+
+struct mv88e6xxx_port_hwtstamp {
+ /* Port index */
+ int port_id;
+
+ /* Timestamping state */
+ unsigned long state;
+
+ /* Resources for receive timestamping */
+ struct sk_buff_head rx_queue;
+ struct sk_buff_head rx_queue2;
+
+ /* Resources for transmit timestamping */
+ unsigned long tx_tstamp_start;
+ struct sk_buff *tx_skb;
+ u16 tx_seq_id;
+
+ /* Current timestamp configuration */
+ struct hwtstamp_config tstamp_config;
+};
+
+struct mv88e6xxx_port {
+ u64 serdes_stats[2];
+ u64 atu_member_violation;
+ u64 atu_miss_violation;
+ u64 atu_full_violation;
+ u64 vtu_member_violation;
+ u64 vtu_miss_violation;
+};
+
struct mv88e6xxx_chip {
const struct mv88e6xxx_info *info;
@@ -207,8 +254,34 @@ struct mv88e6xxx_chip {
int irq;
int device_irq;
int watchdog_irq;
+
int atu_prob_irq;
int vtu_prob_irq;
+ struct kthread_worker *kworker;
+ struct kthread_delayed_work irq_poll_work;
+
+ /* GPIO resources */
+ u8 gpio_data[2];
+
+ /* This cyclecounter abstracts the switch PTP time.
+ * reg_lock must be held for any operation that read()s.
+ */
+ struct cyclecounter tstamp_cc;
+ struct timecounter tstamp_tc;
+ struct delayed_work overflow_work;
+
+ struct ptp_clock *ptp_clock;
+ struct ptp_clock_info ptp_clock_info;
+ struct delayed_work tai_event_work;
+ struct ptp_pin_desc pin_config[MV88E6XXX_MAX_GPIO];
+ u16 trig_config;
+ u16 evcap_config;
+
+ /* Per-port timestamping resources. */
+ struct mv88e6xxx_port_hwtstamp port_hwtstamp[DSA_MAX_PORTS];
+
+ /* Array of port structures. */
+ struct mv88e6xxx_port ports[DSA_MAX_PORTS];
};
struct mv88e6xxx_bus_ops {
@@ -327,9 +400,9 @@ struct mv88e6xxx_ops {
/* Return the number of strings describing statistics */
int (*stats_get_sset_count)(struct mv88e6xxx_chip *chip);
- void (*stats_get_strings)(struct mv88e6xxx_chip *chip, uint8_t *data);
- void (*stats_get_stats)(struct mv88e6xxx_chip *chip, int port,
- uint64_t *data);
+ int (*stats_get_strings)(struct mv88e6xxx_chip *chip, uint8_t *data);
+ int (*stats_get_stats)(struct mv88e6xxx_chip *chip, int port,
+ uint64_t *data);
int (*set_cpu_port)(struct mv88e6xxx_chip *chip, int port);
int (*set_egress_port)(struct mv88e6xxx_chip *chip, int port);
const struct mv88e6xxx_irq_ops *watchdog_ops;
@@ -339,11 +412,24 @@ struct mv88e6xxx_ops {
/* Power on/off a SERDES interface */
int (*serdes_power)(struct mv88e6xxx_chip *chip, int port, bool on);
+ /* Statistics from the SERDES interface */
+ int (*serdes_get_sset_count)(struct mv88e6xxx_chip *chip, int port);
+ int (*serdes_get_strings)(struct mv88e6xxx_chip *chip, int port,
+ uint8_t *data);
+ int (*serdes_get_stats)(struct mv88e6xxx_chip *chip, int port,
+ uint64_t *data);
+
/* VLAN Translation Unit operations */
int (*vtu_getnext)(struct mv88e6xxx_chip *chip,
struct mv88e6xxx_vtu_entry *entry);
int (*vtu_loadpurge)(struct mv88e6xxx_chip *chip,
struct mv88e6xxx_vtu_entry *entry);
+
+ /* GPIO operations */
+ const struct mv88e6xxx_gpio_ops *gpio_ops;
+
+ /* Interface to the AVB/PTP registers */
+ const struct mv88e6xxx_avb_ops *avb_ops;
};
struct mv88e6xxx_irq_ops {
@@ -355,13 +441,49 @@ struct mv88e6xxx_irq_ops {
void (*irq_free)(struct mv88e6xxx_chip *chip);
};
+struct mv88e6xxx_gpio_ops {
+ /* Get/set data on GPIO pin */
+ int (*get_data)(struct mv88e6xxx_chip *chip, unsigned int pin);
+ int (*set_data)(struct mv88e6xxx_chip *chip, unsigned int pin,
+ int value);
+
+ /* get/set GPIO direction */
+ int (*get_dir)(struct mv88e6xxx_chip *chip, unsigned int pin);
+ int (*set_dir)(struct mv88e6xxx_chip *chip, unsigned int pin,
+ bool input);
+
+ /* get/set GPIO pin control */
+ int (*get_pctl)(struct mv88e6xxx_chip *chip, unsigned int pin,
+ int *func);
+ int (*set_pctl)(struct mv88e6xxx_chip *chip, unsigned int pin,
+ int func);
+};
+
+struct mv88e6xxx_avb_ops {
+ /* Access port-scoped Precision Time Protocol registers */
+ int (*port_ptp_read)(struct mv88e6xxx_chip *chip, int port, int addr,
+ u16 *data, int len);
+ int (*port_ptp_write)(struct mv88e6xxx_chip *chip, int port, int addr,
+ u16 data);
+
+ /* Access global Precision Time Protocol registers */
+ int (*ptp_read)(struct mv88e6xxx_chip *chip, int addr, u16 *data,
+ int len);
+ int (*ptp_write)(struct mv88e6xxx_chip *chip, int addr, u16 data);
+
+ /* Access global Time Application Interface registers */
+ int (*tai_read)(struct mv88e6xxx_chip *chip, int addr, u16 *data,
+ int len);
+ int (*tai_write)(struct mv88e6xxx_chip *chip, int addr, u16 data);
+};
+
#define STATS_TYPE_PORT BIT(0)
#define STATS_TYPE_BANK0 BIT(1)
#define STATS_TYPE_BANK1 BIT(2)
struct mv88e6xxx_hw_stat {
char string[ETH_GSTRING_LEN];
- int sizeof_stat;
+ size_t size;
int reg;
int type;
};
@@ -386,6 +508,11 @@ static inline u16 mv88e6xxx_port_mask(struct mv88e6xxx_chip *chip)
return GENMASK(mv88e6xxx_num_ports(chip) - 1, 0);
}
+static inline unsigned int mv88e6xxx_num_gpio(struct mv88e6xxx_chip *chip)
+{
+ return chip->info->num_gpio;
+}
+
int mv88e6xxx_read(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val);
int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val);
int mv88e6xxx_update(struct mv88e6xxx_chip *chip, int addr, int reg,
diff --git a/drivers/net/dsa/mv88e6xxx/global1_atu.c b/drivers/net/dsa/mv88e6xxx/global1_atu.c
index 20d941f4273b..307410898fc9 100644
--- a/drivers/net/dsa/mv88e6xxx/global1_atu.c
+++ b/drivers/net/dsa/mv88e6xxx/global1_atu.c
@@ -336,8 +336,6 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
if (err)
goto out;
- mutex_unlock(&chip->reg_lock);
-
if (val & MV88E6XXX_G1_ATU_OP_AGE_OUT_VIOLATION) {
dev_err_ratelimited(chip->dev,
"ATU age out violation for %pM\n",
@@ -348,17 +346,23 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
dev_err_ratelimited(chip->dev,
"ATU member violation for %pM portvec %x\n",
entry.mac, entry.portvec);
+ chip->ports[entry.portvec].atu_member_violation++;
}
- if (val & MV88E6XXX_G1_ATU_OP_MEMBER_VIOLATION)
+ if (val & MV88E6XXX_G1_ATU_OP_MEMBER_VIOLATION) {
dev_err_ratelimited(chip->dev,
"ATU miss violation for %pM portvec %x\n",
entry.mac, entry.portvec);
+ chip->ports[entry.portvec].atu_miss_violation++;
+ }
- if (val & MV88E6XXX_G1_ATU_OP_FULL_VIOLATION)
+ if (val & MV88E6XXX_G1_ATU_OP_FULL_VIOLATION) {
dev_err_ratelimited(chip->dev,
"ATU full violation for %pM portvec %x\n",
entry.mac, entry.portvec);
+ chip->ports[entry.portvec].atu_full_violation++;
+ }
+ mutex_unlock(&chip->reg_lock);
return IRQ_HANDLED;
diff --git a/drivers/net/dsa/mv88e6xxx/global1_vtu.c b/drivers/net/dsa/mv88e6xxx/global1_vtu.c
index 7997961647de..058326924f3e 100644
--- a/drivers/net/dsa/mv88e6xxx/global1_vtu.c
+++ b/drivers/net/dsa/mv88e6xxx/global1_vtu.c
@@ -539,18 +539,21 @@ static irqreturn_t mv88e6xxx_g1_vtu_prob_irq_thread_fn(int irq, void *dev_id)
if (err)
goto out;
- mutex_unlock(&chip->reg_lock);
-
spid = val & MV88E6XXX_G1_VTU_OP_SPID_MASK;
if (val & MV88E6XXX_G1_VTU_OP_MEMBER_VIOLATION) {
dev_err_ratelimited(chip->dev, "VTU member violation for vid %d, source port %d\n",
entry.vid, spid);
+ chip->ports[spid].vtu_member_violation++;
}
- if (val & MV88E6XXX_G1_VTU_OP_MISS_VIOLATION)
- dev_err_ratelimited(chip->dev, "VTU miss violation for vid %d, source port %d\n",
+ if (val & MV88E6XXX_G1_VTU_OP_MISS_VIOLATION) {
+ dev_dbg_ratelimited(chip->dev, "VTU miss violation for vid %d, source port %d\n",
entry.vid, spid);
+ chip->ports[spid].vtu_miss_violation++;
+ }
+
+ mutex_unlock(&chip->reg_lock);
return IRQ_HANDLED;
diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c
index af0727877825..0ce627fded48 100644
--- a/drivers/net/dsa/mv88e6xxx/global2.c
+++ b/drivers/net/dsa/mv88e6xxx/global2.c
@@ -20,22 +20,22 @@
#include "global1.h" /* for MV88E6XXX_G1_STS_IRQ_DEVICE */
#include "global2.h"
-static int mv88e6xxx_g2_read(struct mv88e6xxx_chip *chip, int reg, u16 *val)
+int mv88e6xxx_g2_read(struct mv88e6xxx_chip *chip, int reg, u16 *val)
{
return mv88e6xxx_read(chip, chip->info->global2_addr, reg, val);
}
-static int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val)
+int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val)
{
return mv88e6xxx_write(chip, chip->info->global2_addr, reg, val);
}
-static int mv88e6xxx_g2_update(struct mv88e6xxx_chip *chip, int reg, u16 update)
+int mv88e6xxx_g2_update(struct mv88e6xxx_chip *chip, int reg, u16 update)
{
return mv88e6xxx_update(chip, chip->info->global2_addr, reg, update);
}
-static int mv88e6xxx_g2_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask)
+int mv88e6xxx_g2_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask)
{
return mv88e6xxx_wait(chip, chip->info->global2_addr, reg, mask);
}
@@ -798,6 +798,7 @@ int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
val);
}
+/* Offset 0x1B: Watchdog Control */
static int mv88e6097_watchdog_action(struct mv88e6xxx_chip *chip, int irq)
{
u16 reg;
@@ -1089,7 +1090,7 @@ int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip)
err = request_threaded_irq(chip->device_irq, NULL,
mv88e6xxx_g2_irq_thread_fn,
- IRQF_ONESHOT, "mv88e6xxx-g1", chip);
+ IRQF_ONESHOT, "mv88e6xxx-g2", chip);
if (err)
goto out;
@@ -1106,6 +1107,38 @@ out:
return err;
}
+int mv88e6xxx_g2_irq_mdio_setup(struct mv88e6xxx_chip *chip,
+ struct mii_bus *bus)
+{
+ int phy, irq, err, err_phy;
+
+ for (phy = 0; phy < chip->info->num_internal_phys; phy++) {
+ irq = irq_find_mapping(chip->g2_irq.domain, phy);
+ if (irq < 0) {
+ err = irq;
+ goto out;
+ }
+ bus->irq[chip->info->port_base_addr + phy] = irq;
+ }
+ return 0;
+out:
+ err_phy = phy;
+
+ for (phy = 0; phy < err_phy; phy++)
+ irq_dispose_mapping(bus->irq[phy]);
+
+ return err;
+}
+
+void mv88e6xxx_g2_irq_mdio_free(struct mv88e6xxx_chip *chip,
+ struct mii_bus *bus)
+{
+ int phy;
+
+ for (phy = 0; phy < chip->info->num_internal_phys; phy++)
+ irq_dispose_mapping(bus->irq[phy]);
+}
+
int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip)
{
u16 reg;
diff --git a/drivers/net/dsa/mv88e6xxx/global2.h b/drivers/net/dsa/mv88e6xxx/global2.h
index 669f59017b12..520ec70d32e8 100644
--- a/drivers/net/dsa/mv88e6xxx/global2.h
+++ b/drivers/net/dsa/mv88e6xxx/global2.h
@@ -149,7 +149,26 @@
#define MV88E6390_G2_EEPROM_ADDR_MASK 0xffff
/* Offset 0x16: AVB Command Register */
-#define MV88E6352_G2_AVB_CMD 0x16
+#define MV88E6352_G2_AVB_CMD 0x16
+#define MV88E6352_G2_AVB_CMD_BUSY 0x8000
+#define MV88E6352_G2_AVB_CMD_OP_READ 0x4000
+#define MV88E6352_G2_AVB_CMD_OP_READ_INCR 0x6000
+#define MV88E6352_G2_AVB_CMD_OP_WRITE 0x3000
+#define MV88E6390_G2_AVB_CMD_OP_READ 0x0000
+#define MV88E6390_G2_AVB_CMD_OP_READ_INCR 0x4000
+#define MV88E6390_G2_AVB_CMD_OP_WRITE 0x6000
+#define MV88E6352_G2_AVB_CMD_PORT_MASK 0x0f00
+#define MV88E6352_G2_AVB_CMD_PORT_TAIGLOBAL 0xe
+#define MV88E6352_G2_AVB_CMD_PORT_PTPGLOBAL 0xf
+#define MV88E6390_G2_AVB_CMD_PORT_MASK 0x1f00
+#define MV88E6390_G2_AVB_CMD_PORT_TAIGLOBAL 0x1e
+#define MV88E6390_G2_AVB_CMD_PORT_PTPGLOBAL 0x1f
+#define MV88E6352_G2_AVB_CMD_BLOCK_PTP 0
+#define MV88E6352_G2_AVB_CMD_BLOCK_AVB 1
+#define MV88E6352_G2_AVB_CMD_BLOCK_QAV 2
+#define MV88E6352_G2_AVB_CMD_BLOCK_QVB 3
+#define MV88E6352_G2_AVB_CMD_BLOCK_MASK 0x00e0
+#define MV88E6352_G2_AVB_CMD_ADDR_MASK 0x001f
/* Offset 0x17: AVB Data Register */
#define MV88E6352_G2_AVB_DATA 0x17
@@ -223,6 +242,40 @@
#define MV88E6352_G2_NOEGR_POLICY 0x2000
#define MV88E6390_G2_LAG_ID_4 0x2000
+/* Scratch/Misc registers accessed through MV88E6XXX_G2_SCRATCH_MISC */
+/* Offset 0x02: Misc Configuration */
+#define MV88E6352_G2_SCRATCH_MISC_CFG 0x02
+#define MV88E6352_G2_SCRATCH_MISC_CFG_NORMALSMI 0x80
+/* Offset 0x60-0x61: GPIO Configuration */
+#define MV88E6352_G2_SCRATCH_GPIO_CFG0 0x60
+#define MV88E6352_G2_SCRATCH_GPIO_CFG1 0x61
+/* Offset 0x62-0x63: GPIO Direction */
+#define MV88E6352_G2_SCRATCH_GPIO_DIR0 0x62
+#define MV88E6352_G2_SCRATCH_GPIO_DIR1 0x63
+#define MV88E6352_G2_SCRATCH_GPIO_DIR_OUT 0
+#define MV88E6352_G2_SCRATCH_GPIO_DIR_IN 1
+/* Offset 0x64-0x65: GPIO Data */
+#define MV88E6352_G2_SCRATCH_GPIO_DATA0 0x64
+#define MV88E6352_G2_SCRATCH_GPIO_DATA1 0x65
+/* Offset 0x68-0x6F: GPIO Pin Control */
+#define MV88E6352_G2_SCRATCH_GPIO_PCTL0 0x68
+#define MV88E6352_G2_SCRATCH_GPIO_PCTL1 0x69
+#define MV88E6352_G2_SCRATCH_GPIO_PCTL2 0x6A
+#define MV88E6352_G2_SCRATCH_GPIO_PCTL3 0x6B
+#define MV88E6352_G2_SCRATCH_GPIO_PCTL4 0x6C
+#define MV88E6352_G2_SCRATCH_GPIO_PCTL5 0x6D
+#define MV88E6352_G2_SCRATCH_GPIO_PCTL6 0x6E
+#define MV88E6352_G2_SCRATCH_GPIO_PCTL7 0x6F
+#define MV88E6352_G2_SCRATCH_CONFIG_DATA0 0x70
+#define MV88E6352_G2_SCRATCH_CONFIG_DATA1 0x71
+#define MV88E6352_G2_SCRATCH_CONFIG_DATA1_NO_CPU BIT(2)
+#define MV88E6352_G2_SCRATCH_CONFIG_DATA2 0x72
+#define MV88E6352_G2_SCRATCH_CONFIG_DATA2_P0_MODE_MASK 0x3
+
+#define MV88E6352_G2_SCRATCH_GPIO_PCTL_GPIO 0
+#define MV88E6352_G2_SCRATCH_GPIO_PCTL_TRIG 1
+#define MV88E6352_G2_SCRATCH_GPIO_PCTL_EVREQ 2
+
#ifdef CONFIG_NET_DSA_MV88E6XXX_GLOBAL2
static inline int mv88e6xxx_g2_require(struct mv88e6xxx_chip *chip)
@@ -230,6 +283,11 @@ static inline int mv88e6xxx_g2_require(struct mv88e6xxx_chip *chip)
return 0;
}
+int mv88e6xxx_g2_read(struct mv88e6xxx_chip *chip, int reg, u16 *val);
+int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val);
+int mv88e6xxx_g2_update(struct mv88e6xxx_chip *chip, int reg, u16 update);
+int mv88e6xxx_g2_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask);
+
int mv88e6352_g2_irl_init_all(struct mv88e6xxx_chip *chip, int port);
int mv88e6390_g2_irl_init_all(struct mv88e6xxx_chip *chip, int port);
@@ -259,6 +317,11 @@ int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip);
int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip);
void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip);
+int mv88e6xxx_g2_irq_mdio_setup(struct mv88e6xxx_chip *chip,
+ struct mii_bus *bus);
+void mv88e6xxx_g2_irq_mdio_free(struct mv88e6xxx_chip *chip,
+ struct mii_bus *bus);
+
int mv88e6185_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip);
int mv88e6352_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip);
@@ -267,6 +330,14 @@ int mv88e6xxx_g2_pot_clear(struct mv88e6xxx_chip *chip);
extern const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops;
extern const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops;
+extern const struct mv88e6xxx_avb_ops mv88e6352_avb_ops;
+extern const struct mv88e6xxx_avb_ops mv88e6390_avb_ops;
+
+extern const struct mv88e6xxx_gpio_ops mv88e6352_gpio_ops;
+
+int mv88e6xxx_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip,
+ bool external);
+
#else /* !CONFIG_NET_DSA_MV88E6XXX_GLOBAL2 */
static inline int mv88e6xxx_g2_require(struct mv88e6xxx_chip *chip)
@@ -279,6 +350,26 @@ static inline int mv88e6xxx_g2_require(struct mv88e6xxx_chip *chip)
return 0;
}
+static inline int mv88e6xxx_g2_read(struct mv88e6xxx_chip *chip, int reg, u16 *val)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int mv88e6xxx_g2_update(struct mv88e6xxx_chip *chip, int reg, u16 update)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int mv88e6xxx_g2_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask)
+{
+ return -EOPNOTSUPP;
+}
+
static inline int mv88e6352_g2_irl_init_all(struct mv88e6xxx_chip *chip,
int port)
{
@@ -364,6 +455,17 @@ static inline void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip)
{
}
+static inline int mv88e6xxx_g2_irq_mdio_setup(struct mv88e6xxx_chip *chip,
+ struct mii_bus *bus)
+{
+ return 0;
+}
+
+static inline void mv88e6xxx_g2_irq_mdio_free(struct mv88e6xxx_chip *chip,
+ struct mii_bus *bus)
+{
+}
+
static inline int mv88e6185_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip)
{
return -EOPNOTSUPP;
@@ -382,6 +484,17 @@ static inline int mv88e6xxx_g2_pot_clear(struct mv88e6xxx_chip *chip)
static const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops = {};
static const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops = {};
+static const struct mv88e6xxx_avb_ops mv88e6352_avb_ops = {};
+static const struct mv88e6xxx_avb_ops mv88e6390_avb_ops = {};
+
+static const struct mv88e6xxx_gpio_ops mv88e6352_gpio_ops = {};
+
+static inline int mv88e6xxx_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip,
+ bool external)
+{
+ return -EOPNOTSUPP;
+}
+
#endif /* CONFIG_NET_DSA_MV88E6XXX_GLOBAL2 */
#endif /* _MV88E6XXX_GLOBAL2_H */
diff --git a/drivers/net/dsa/mv88e6xxx/global2_avb.c b/drivers/net/dsa/mv88e6xxx/global2_avb.c
new file mode 100644
index 000000000000..2e398ccb88ca
--- /dev/null
+++ b/drivers/net/dsa/mv88e6xxx/global2_avb.c
@@ -0,0 +1,193 @@
+/*
+ * Marvell 88E6xxx Switch Global 2 Registers support
+ *
+ * Copyright (c) 2008 Marvell Semiconductor
+ *
+ * Copyright (c) 2016-2017 Savoir-faire Linux Inc.
+ * Vivien Didelot <vivien.didelot@savoirfairelinux.com>
+ *
+ * Copyright (c) 2017 National Instruments
+ * Brandon Streiff <brandon.streiff@ni.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.
+ */
+
+#include "global2.h"
+
+/* Offset 0x16: AVB Command Register
+ * Offset 0x17: AVB Data Register
+ *
+ * There are two different versions of this register interface:
+ * "6352": 3-bit "op" field, 4-bit "port" field.
+ * "6390": 2-bit "op" field, 5-bit "port" field.
+ *
+ * The "op" codes are different between the two, as well as the special
+ * port fields for global PTP and TAI configuration.
+ */
+
+/* mv88e6xxx_g2_avb_read -- Read one or multiple 16-bit words.
+ * The hardware supports snapshotting up to four contiguous registers.
+ */
+static int mv88e6xxx_g2_avb_read(struct mv88e6xxx_chip *chip, u16 readop,
+ u16 *data, int len)
+{
+ int err;
+ int i;
+
+ /* Hardware can only snapshot four words. */
+ if (len > 4)
+ return -E2BIG;
+
+ err = mv88e6xxx_g2_update(chip, MV88E6352_G2_AVB_CMD, readop);
+ if (err)
+ return err;
+
+ for (i = 0; i < len; ++i) {
+ err = mv88e6xxx_g2_read(chip, MV88E6352_G2_AVB_DATA,
+ &data[i]);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+/* mv88e6xxx_g2_avb_write -- Write one 16-bit word. */
+static int mv88e6xxx_g2_avb_write(struct mv88e6xxx_chip *chip, u16 writeop,
+ u16 data)
+{
+ int err;
+
+ err = mv88e6xxx_g2_write(chip, MV88E6352_G2_AVB_DATA, data);
+ if (err)
+ return err;
+
+ return mv88e6xxx_g2_update(chip, MV88E6352_G2_AVB_CMD, writeop);
+}
+
+static int mv88e6352_g2_avb_port_ptp_read(struct mv88e6xxx_chip *chip,
+ int port, int addr, u16 *data,
+ int len)
+{
+ u16 readop = (len == 1 ? MV88E6352_G2_AVB_CMD_OP_READ :
+ MV88E6352_G2_AVB_CMD_OP_READ_INCR) |
+ (port << 8) | (MV88E6352_G2_AVB_CMD_BLOCK_PTP << 5) |
+ addr;
+
+ return mv88e6xxx_g2_avb_read(chip, readop, data, len);
+}
+
+static int mv88e6352_g2_avb_port_ptp_write(struct mv88e6xxx_chip *chip,
+ int port, int addr, u16 data)
+{
+ u16 writeop = MV88E6352_G2_AVB_CMD_OP_WRITE | (port << 8) |
+ (MV88E6352_G2_AVB_CMD_BLOCK_PTP << 5) | addr;
+
+ return mv88e6xxx_g2_avb_write(chip, writeop, data);
+}
+
+static int mv88e6352_g2_avb_ptp_read(struct mv88e6xxx_chip *chip, int addr,
+ u16 *data, int len)
+{
+ return mv88e6352_g2_avb_port_ptp_read(chip,
+ MV88E6352_G2_AVB_CMD_PORT_PTPGLOBAL,
+ addr, data, len);
+}
+
+static int mv88e6352_g2_avb_ptp_write(struct mv88e6xxx_chip *chip, int addr,
+ u16 data)
+{
+ return mv88e6352_g2_avb_port_ptp_write(chip,
+ MV88E6352_G2_AVB_CMD_PORT_PTPGLOBAL,
+ addr, data);
+}
+
+static int mv88e6352_g2_avb_tai_read(struct mv88e6xxx_chip *chip, int addr,
+ u16 *data, int len)
+{
+ return mv88e6352_g2_avb_port_ptp_read(chip,
+ MV88E6352_G2_AVB_CMD_PORT_TAIGLOBAL,
+ addr, data, len);
+}
+
+static int mv88e6352_g2_avb_tai_write(struct mv88e6xxx_chip *chip, int addr,
+ u16 data)
+{
+ return mv88e6352_g2_avb_port_ptp_write(chip,
+ MV88E6352_G2_AVB_CMD_PORT_TAIGLOBAL,
+ addr, data);
+}
+
+const struct mv88e6xxx_avb_ops mv88e6352_avb_ops = {
+ .port_ptp_read = mv88e6352_g2_avb_port_ptp_read,
+ .port_ptp_write = mv88e6352_g2_avb_port_ptp_write,
+ .ptp_read = mv88e6352_g2_avb_ptp_read,
+ .ptp_write = mv88e6352_g2_avb_ptp_write,
+ .tai_read = mv88e6352_g2_avb_tai_read,
+ .tai_write = mv88e6352_g2_avb_tai_write,
+};
+
+static int mv88e6390_g2_avb_port_ptp_read(struct mv88e6xxx_chip *chip,
+ int port, int addr, u16 *data,
+ int len)
+{
+ u16 readop = (len == 1 ? MV88E6390_G2_AVB_CMD_OP_READ :
+ MV88E6390_G2_AVB_CMD_OP_READ_INCR) |
+ (port << 8) | (MV88E6352_G2_AVB_CMD_BLOCK_PTP << 5) |
+ addr;
+
+ return mv88e6xxx_g2_avb_read(chip, readop, data, len);
+}
+
+static int mv88e6390_g2_avb_port_ptp_write(struct mv88e6xxx_chip *chip,
+ int port, int addr, u16 data)
+{
+ u16 writeop = MV88E6390_G2_AVB_CMD_OP_WRITE | (port << 8) |
+ (MV88E6352_G2_AVB_CMD_BLOCK_PTP << 5) | addr;
+
+ return mv88e6xxx_g2_avb_write(chip, writeop, data);
+}
+
+static int mv88e6390_g2_avb_ptp_read(struct mv88e6xxx_chip *chip, int addr,
+ u16 *data, int len)
+{
+ return mv88e6390_g2_avb_port_ptp_read(chip,
+ MV88E6390_G2_AVB_CMD_PORT_PTPGLOBAL,
+ addr, data, len);
+}
+
+static int mv88e6390_g2_avb_ptp_write(struct mv88e6xxx_chip *chip, int addr,
+ u16 data)
+{
+ return mv88e6390_g2_avb_port_ptp_write(chip,
+ MV88E6390_G2_AVB_CMD_PORT_PTPGLOBAL,
+ addr, data);
+}
+
+static int mv88e6390_g2_avb_tai_read(struct mv88e6xxx_chip *chip, int addr,
+ u16 *data, int len)
+{
+ return mv88e6390_g2_avb_port_ptp_read(chip,
+ MV88E6390_G2_AVB_CMD_PORT_TAIGLOBAL,
+ addr, data, len);
+}
+
+static int mv88e6390_g2_avb_tai_write(struct mv88e6xxx_chip *chip, int addr,
+ u16 data)
+{
+ return mv88e6390_g2_avb_port_ptp_write(chip,
+ MV88E6390_G2_AVB_CMD_PORT_TAIGLOBAL,
+ addr, data);
+}
+
+const struct mv88e6xxx_avb_ops mv88e6390_avb_ops = {
+ .port_ptp_read = mv88e6390_g2_avb_port_ptp_read,
+ .port_ptp_write = mv88e6390_g2_avb_port_ptp_write,
+ .ptp_read = mv88e6390_g2_avb_ptp_read,
+ .ptp_write = mv88e6390_g2_avb_ptp_write,
+ .tai_read = mv88e6390_g2_avb_tai_read,
+ .tai_write = mv88e6390_g2_avb_tai_write,
+};
diff --git a/drivers/net/dsa/mv88e6xxx/global2_scratch.c b/drivers/net/dsa/mv88e6xxx/global2_scratch.c
new file mode 100644
index 000000000000..3f92b8892dc7
--- /dev/null
+++ b/drivers/net/dsa/mv88e6xxx/global2_scratch.c
@@ -0,0 +1,291 @@
+/*
+ * Marvell 88E6xxx Switch Global 2 Scratch & Misc Registers support
+ *
+ * Copyright (c) 2008 Marvell Semiconductor
+ *
+ * Copyright (c) 2017 National Instruments
+ * Brandon Streiff <brandon.streiff@ni.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.
+ */
+
+#include "chip.h"
+#include "global2.h"
+
+/* Offset 0x1A: Scratch and Misc. Register */
+static int mv88e6xxx_g2_scratch_read(struct mv88e6xxx_chip *chip, int reg,
+ u8 *data)
+{
+ u16 value;
+ int err;
+
+ err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SCRATCH_MISC_MISC,
+ reg << 8);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_SCRATCH_MISC_MISC, &value);
+ if (err)
+ return err;
+
+ *data = (value & MV88E6XXX_G2_SCRATCH_MISC_DATA_MASK);
+
+ return 0;
+}
+
+static int mv88e6xxx_g2_scratch_write(struct mv88e6xxx_chip *chip, int reg,
+ u8 data)
+{
+ u16 value = (reg << 8) | data;
+
+ return mv88e6xxx_g2_update(chip, MV88E6XXX_G2_SCRATCH_MISC_MISC, value);
+}
+
+/**
+ * mv88e6xxx_g2_scratch_gpio_get_bit - get a bit
+ * @chip: chip private data
+ * @nr: bit index
+ * @set: is bit set?
+ */
+static int mv88e6xxx_g2_scratch_get_bit(struct mv88e6xxx_chip *chip,
+ int base_reg, unsigned int offset,
+ int *set)
+{
+ int reg = base_reg + (offset / 8);
+ u8 mask = (1 << (offset & 0x7));
+ u8 val;
+ int err;
+
+ err = mv88e6xxx_g2_scratch_read(chip, reg, &val);
+ if (err)
+ return err;
+
+ *set = !!(mask & val);
+
+ return 0;
+}
+
+/**
+ * mv88e6xxx_g2_scratch_gpio_set_bit - set (or clear) a bit
+ * @chip: chip private data
+ * @nr: bit index
+ * @set: set if true, clear if false
+ *
+ * Helper function for dealing with the direction and data registers.
+ */
+static int mv88e6xxx_g2_scratch_set_bit(struct mv88e6xxx_chip *chip,
+ int base_reg, unsigned int offset,
+ int set)
+{
+ int reg = base_reg + (offset / 8);
+ u8 mask = (1 << (offset & 0x7));
+ u8 val;
+ int err;
+
+ err = mv88e6xxx_g2_scratch_read(chip, reg, &val);
+ if (err)
+ return err;
+
+ if (set)
+ val |= mask;
+ else
+ val &= ~mask;
+
+ return mv88e6xxx_g2_scratch_write(chip, reg, val);
+}
+
+/**
+ * mv88e6352_g2_scratch_gpio_get_data - get data on gpio pin
+ * @chip: chip private data
+ * @pin: gpio index
+ *
+ * Return: 0 for low, 1 for high, negative error
+ */
+static int mv88e6352_g2_scratch_gpio_get_data(struct mv88e6xxx_chip *chip,
+ unsigned int pin)
+{
+ int val = 0;
+ int err;
+
+ err = mv88e6xxx_g2_scratch_get_bit(chip,
+ MV88E6352_G2_SCRATCH_GPIO_DATA0,
+ pin, &val);
+ if (err)
+ return err;
+
+ return val;
+}
+
+/**
+ * mv88e6352_g2_scratch_gpio_set_data - set data on gpio pin
+ * @chip: chip private data
+ * @pin: gpio index
+ * @value: value to set
+ */
+static int mv88e6352_g2_scratch_gpio_set_data(struct mv88e6xxx_chip *chip,
+ unsigned int pin, int value)
+{
+ u8 mask = (1 << (pin & 0x7));
+ int offset = (pin / 8);
+ int reg;
+
+ reg = MV88E6352_G2_SCRATCH_GPIO_DATA0 + offset;
+
+ if (value)
+ chip->gpio_data[offset] |= mask;
+ else
+ chip->gpio_data[offset] &= ~mask;
+
+ return mv88e6xxx_g2_scratch_write(chip, reg, chip->gpio_data[offset]);
+}
+
+/**
+ * mv88e6352_g2_scratch_gpio_get_dir - get direction of gpio pin
+ * @chip: chip private data
+ * @pin: gpio index
+ *
+ * Return: 0 for output, 1 for input (same as GPIOF_DIR_XXX).
+ */
+static int mv88e6352_g2_scratch_gpio_get_dir(struct mv88e6xxx_chip *chip,
+ unsigned int pin)
+{
+ int val = 0;
+ int err;
+
+ err = mv88e6xxx_g2_scratch_get_bit(chip,
+ MV88E6352_G2_SCRATCH_GPIO_DIR0,
+ pin, &val);
+ if (err)
+ return err;
+
+ return val;
+}
+
+/**
+ * mv88e6352_g2_scratch_gpio_set_dir - set direction of gpio pin
+ * @chip: chip private data
+ * @pin: gpio index
+ */
+static int mv88e6352_g2_scratch_gpio_set_dir(struct mv88e6xxx_chip *chip,
+ unsigned int pin, bool input)
+{
+ int value = (input ? MV88E6352_G2_SCRATCH_GPIO_DIR_IN :
+ MV88E6352_G2_SCRATCH_GPIO_DIR_OUT);
+
+ return mv88e6xxx_g2_scratch_set_bit(chip,
+ MV88E6352_G2_SCRATCH_GPIO_DIR0,
+ pin, value);
+}
+
+/**
+ * mv88e6352_g2_scratch_gpio_get_pctl - get pin control setting
+ * @chip: chip private data
+ * @pin: gpio index
+ * @func: function number
+ *
+ * Note that the function numbers themselves may vary by chipset.
+ */
+static int mv88e6352_g2_scratch_gpio_get_pctl(struct mv88e6xxx_chip *chip,
+ unsigned int pin, int *func)
+{
+ int reg = MV88E6352_G2_SCRATCH_GPIO_PCTL0 + (pin / 2);
+ int offset = (pin & 0x1) ? 4 : 0;
+ u8 mask = (0x7 << offset);
+ int err;
+ u8 val;
+
+ err = mv88e6xxx_g2_scratch_read(chip, reg, &val);
+ if (err)
+ return err;
+
+ *func = (val & mask) >> offset;
+
+ return 0;
+}
+
+/**
+ * mv88e6352_g2_scratch_gpio_set_pctl - set pin control setting
+ * @chip: chip private data
+ * @pin: gpio index
+ * @func: function number
+ */
+static int mv88e6352_g2_scratch_gpio_set_pctl(struct mv88e6xxx_chip *chip,
+ unsigned int pin, int func)
+{
+ int reg = MV88E6352_G2_SCRATCH_GPIO_PCTL0 + (pin / 2);
+ int offset = (pin & 0x1) ? 4 : 0;
+ u8 mask = (0x7 << offset);
+ int err;
+ u8 val;
+
+ err = mv88e6xxx_g2_scratch_read(chip, reg, &val);
+ if (err)
+ return err;
+
+ val = (val & ~mask) | ((func & mask) << offset);
+
+ return mv88e6xxx_g2_scratch_write(chip, reg, val);
+}
+
+const struct mv88e6xxx_gpio_ops mv88e6352_gpio_ops = {
+ .get_data = mv88e6352_g2_scratch_gpio_get_data,
+ .set_data = mv88e6352_g2_scratch_gpio_set_data,
+ .get_dir = mv88e6352_g2_scratch_gpio_get_dir,
+ .set_dir = mv88e6352_g2_scratch_gpio_set_dir,
+ .get_pctl = mv88e6352_g2_scratch_gpio_get_pctl,
+ .set_pctl = mv88e6352_g2_scratch_gpio_set_pctl,
+};
+
+/**
+ * mv88e6xxx_g2_gpio_set_smi - set gpio muxing for external smi
+ * @chip: chip private data
+ * @external: set mux for external smi, or free for gpio usage
+ *
+ * Some mv88e6xxx models have GPIO pins that may be configured as
+ * an external SMI interface, or they may be made free for other
+ * GPIO uses.
+ */
+int mv88e6xxx_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip,
+ bool external)
+{
+ int misc_cfg = MV88E6352_G2_SCRATCH_MISC_CFG;
+ int config_data1 = MV88E6352_G2_SCRATCH_CONFIG_DATA1;
+ int config_data2 = MV88E6352_G2_SCRATCH_CONFIG_DATA2;
+ bool no_cpu;
+ u8 p0_mode;
+ int err;
+ u8 val;
+
+ err = mv88e6xxx_g2_scratch_read(chip, config_data2, &val);
+ if (err)
+ return err;
+
+ p0_mode = val & MV88E6352_G2_SCRATCH_CONFIG_DATA2_P0_MODE_MASK;
+
+ if (p0_mode == 0x01 || p0_mode == 0x02)
+ return -EBUSY;
+
+ err = mv88e6xxx_g2_scratch_read(chip, config_data1, &val);
+ if (err)
+ return err;
+
+ no_cpu = !!(val & MV88E6352_G2_SCRATCH_CONFIG_DATA1_NO_CPU);
+
+ err = mv88e6xxx_g2_scratch_read(chip, misc_cfg, &val);
+ if (err)
+ return err;
+
+ /* NO_CPU being 0 inverts the meaning of the bit */
+ if (!no_cpu)
+ external = !external;
+
+ if (external)
+ val |= MV88E6352_G2_SCRATCH_MISC_CFG_NORMALSMI;
+ else
+ val &= ~MV88E6352_G2_SCRATCH_MISC_CFG_NORMALSMI;
+
+ return mv88e6xxx_g2_scratch_write(chip, misc_cfg, val);
+}
diff --git a/drivers/net/dsa/mv88e6xxx/hwtstamp.c b/drivers/net/dsa/mv88e6xxx/hwtstamp.c
new file mode 100644
index 000000000000..a036c490b7ce
--- /dev/null
+++ b/drivers/net/dsa/mv88e6xxx/hwtstamp.c
@@ -0,0 +1,584 @@
+/*
+ * Marvell 88E6xxx Switch hardware timestamping support
+ *
+ * Copyright (c) 2008 Marvell Semiconductor
+ *
+ * Copyright (c) 2017 National Instruments
+ * Erik Hons <erik.hons@ni.com>
+ * Brandon Streiff <brandon.streiff@ni.com>
+ * Dane Wagner <dane.wagner@ni.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.
+ */
+
+#include "chip.h"
+#include "global2.h"
+#include "hwtstamp.h"
+#include "ptp.h"
+#include <linux/ptp_classify.h>
+
+#define SKB_PTP_TYPE(__skb) (*(unsigned int *)((__skb)->cb))
+
+static int mv88e6xxx_port_ptp_read(struct mv88e6xxx_chip *chip, int port,
+ int addr, u16 *data, int len)
+{
+ if (!chip->info->ops->avb_ops->port_ptp_read)
+ return -EOPNOTSUPP;
+
+ return chip->info->ops->avb_ops->port_ptp_read(chip, port, addr,
+ data, len);
+}
+
+static int mv88e6xxx_port_ptp_write(struct mv88e6xxx_chip *chip, int port,
+ int addr, u16 data)
+{
+ if (!chip->info->ops->avb_ops->port_ptp_write)
+ return -EOPNOTSUPP;
+
+ return chip->info->ops->avb_ops->port_ptp_write(chip, port, addr,
+ data);
+}
+
+static int mv88e6xxx_ptp_write(struct mv88e6xxx_chip *chip, int addr,
+ u16 data)
+{
+ if (!chip->info->ops->avb_ops->ptp_write)
+ return -EOPNOTSUPP;
+
+ return chip->info->ops->avb_ops->ptp_write(chip, addr, data);
+}
+
+/* TX_TSTAMP_TIMEOUT: This limits the time spent polling for a TX
+ * timestamp. When working properly, hardware will produce a timestamp
+ * within 1ms. Software may enounter delays due to MDIO contention, so
+ * the timeout is set accordingly.
+ */
+#define TX_TSTAMP_TIMEOUT msecs_to_jiffies(20)
+
+int mv88e6xxx_get_ts_info(struct dsa_switch *ds, int port,
+ struct ethtool_ts_info *info)
+{
+ struct mv88e6xxx_chip *chip = ds->priv;
+
+ if (!chip->info->ptp_support)
+ return -EOPNOTSUPP;
+
+ info->so_timestamping =
+ SOF_TIMESTAMPING_TX_HARDWARE |
+ SOF_TIMESTAMPING_RX_HARDWARE |
+ SOF_TIMESTAMPING_RAW_HARDWARE;
+ info->phc_index = ptp_clock_index(chip->ptp_clock);
+ info->tx_types =
+ (1 << HWTSTAMP_TX_OFF) |
+ (1 << HWTSTAMP_TX_ON);
+ info->rx_filters =
+ (1 << HWTSTAMP_FILTER_NONE) |
+ (1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT) |
+ (1 << HWTSTAMP_FILTER_PTP_V2_L4_SYNC) |
+ (1 << HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ) |
+ (1 << HWTSTAMP_FILTER_PTP_V2_L2_EVENT) |
+ (1 << HWTSTAMP_FILTER_PTP_V2_L2_SYNC) |
+ (1 << HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ) |
+ (1 << HWTSTAMP_FILTER_PTP_V2_EVENT) |
+ (1 << HWTSTAMP_FILTER_PTP_V2_SYNC) |
+ (1 << HWTSTAMP_FILTER_PTP_V2_DELAY_REQ);
+
+ return 0;
+}
+
+static int mv88e6xxx_set_hwtstamp_config(struct mv88e6xxx_chip *chip, int port,
+ struct hwtstamp_config *config)
+{
+ struct mv88e6xxx_port_hwtstamp *ps = &chip->port_hwtstamp[port];
+ bool tstamp_enable = false;
+ u16 port_config0;
+ int err;
+
+ /* Prevent the TX/RX paths from trying to interact with the
+ * timestamp hardware while we reconfigure it.
+ */
+ clear_bit_unlock(MV88E6XXX_HWTSTAMP_ENABLED, &ps->state);
+
+ /* reserved for future extensions */
+ if (config->flags)
+ return -EINVAL;
+
+ switch (config->tx_type) {
+ case HWTSTAMP_TX_OFF:
+ tstamp_enable = false;
+ break;
+ case HWTSTAMP_TX_ON:
+ tstamp_enable = true;
+ break;
+ default:
+ return -ERANGE;
+ }
+
+ /* The switch supports timestamping both L2 and L4; one cannot be
+ * disabled independently of the other.
+ */
+ switch (config->rx_filter) {
+ case HWTSTAMP_FILTER_NONE:
+ tstamp_enable = false;
+ break;
+ case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
+ case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
+ case HWTSTAMP_FILTER_PTP_V2_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
+ config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
+ break;
+ case HWTSTAMP_FILTER_ALL:
+ default:
+ config->rx_filter = HWTSTAMP_FILTER_NONE;
+ return -ERANGE;
+ }
+
+ if (tstamp_enable) {
+ /* Disable transportSpecific value matching, so that packets
+ * with either 1588 (0) and 802.1AS (1) will be timestamped.
+ */
+ port_config0 = MV88E6XXX_PORT_PTP_CFG0_DISABLE_TSPEC_MATCH;
+ } else {
+ /* Disable PTP. This disables both RX and TX timestamping. */
+ port_config0 = MV88E6XXX_PORT_PTP_CFG0_DISABLE_PTP;
+ }
+
+ mutex_lock(&chip->reg_lock);
+ err = mv88e6xxx_port_ptp_write(chip, port, MV88E6XXX_PORT_PTP_CFG0,
+ port_config0);
+ mutex_unlock(&chip->reg_lock);
+
+ if (err < 0)
+ return err;
+
+ /* Once hardware has been configured, enable timestamp checks
+ * in the RX/TX paths.
+ */
+ if (tstamp_enable)
+ set_bit(MV88E6XXX_HWTSTAMP_ENABLED, &ps->state);
+
+ return 0;
+}
+
+int mv88e6xxx_port_hwtstamp_set(struct dsa_switch *ds, int port,
+ struct ifreq *ifr)
+{
+ struct mv88e6xxx_chip *chip = ds->priv;
+ struct mv88e6xxx_port_hwtstamp *ps = &chip->port_hwtstamp[port];
+ struct hwtstamp_config config;
+ int err;
+
+ if (!chip->info->ptp_support)
+ return -EOPNOTSUPP;
+
+ if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
+ return -EFAULT;
+
+ err = mv88e6xxx_set_hwtstamp_config(chip, port, &config);
+ if (err)
+ return err;
+
+ /* Save the chosen configuration to be returned later. */
+ memcpy(&ps->tstamp_config, &config, sizeof(config));
+
+ return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
+ -EFAULT : 0;
+}
+
+int mv88e6xxx_port_hwtstamp_get(struct dsa_switch *ds, int port,
+ struct ifreq *ifr)
+{
+ struct mv88e6xxx_chip *chip = ds->priv;
+ struct mv88e6xxx_port_hwtstamp *ps = &chip->port_hwtstamp[port];
+ struct hwtstamp_config *config = &ps->tstamp_config;
+
+ if (!chip->info->ptp_support)
+ return -EOPNOTSUPP;
+
+ return copy_to_user(ifr->ifr_data, config, sizeof(*config)) ?
+ -EFAULT : 0;
+}
+
+/* Get the start of the PTP header in this skb */
+static u8 *parse_ptp_header(struct sk_buff *skb, unsigned int type)
+{
+ u8 *data = skb_mac_header(skb);
+ unsigned int offset = 0;
+
+ if (type & PTP_CLASS_VLAN)
+ offset += VLAN_HLEN;
+
+ switch (type & PTP_CLASS_PMASK) {
+ case PTP_CLASS_IPV4:
+ offset += ETH_HLEN + IPV4_HLEN(data + offset) + UDP_HLEN;
+ break;
+ case PTP_CLASS_IPV6:
+ offset += ETH_HLEN + IP6_HLEN + UDP_HLEN;
+ break;
+ case PTP_CLASS_L2:
+ offset += ETH_HLEN;
+ break;
+ default:
+ return NULL;
+ }
+
+ /* Ensure that the entire header is present in this packet. */
+ if (skb->len + ETH_HLEN < offset + 34)
+ return NULL;
+
+ return data + offset;
+}
+
+/* Returns a pointer to the PTP header if the caller should time stamp,
+ * or NULL if the caller should not.
+ */
+static u8 *mv88e6xxx_should_tstamp(struct mv88e6xxx_chip *chip, int port,
+ struct sk_buff *skb, unsigned int type)
+{
+ struct mv88e6xxx_port_hwtstamp *ps = &chip->port_hwtstamp[port];
+ u8 *hdr;
+
+ if (!chip->info->ptp_support)
+ return NULL;
+
+ hdr = parse_ptp_header(skb, type);
+ if (!hdr)
+ return NULL;
+
+ if (!test_bit(MV88E6XXX_HWTSTAMP_ENABLED, &ps->state))
+ return NULL;
+
+ return hdr;
+}
+
+static int mv88e6xxx_ts_valid(u16 status)
+{
+ if (!(status & MV88E6XXX_PTP_TS_VALID))
+ return 0;
+ if (status & MV88E6XXX_PTP_TS_STATUS_MASK)
+ return 0;
+ return 1;
+}
+
+static int seq_match(struct sk_buff *skb, u16 ts_seqid)
+{
+ unsigned int type = SKB_PTP_TYPE(skb);
+ u8 *hdr = parse_ptp_header(skb, type);
+ __be16 *seqid;
+
+ seqid = (__be16 *)(hdr + OFF_PTP_SEQUENCE_ID);
+
+ return ts_seqid == ntohs(*seqid);
+}
+
+static void mv88e6xxx_get_rxts(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_port_hwtstamp *ps,
+ struct sk_buff *skb, u16 reg,
+ struct sk_buff_head *rxq)
+{
+ u16 buf[4] = { 0 }, status, seq_id;
+ struct skb_shared_hwtstamps *shwt;
+ struct sk_buff_head received;
+ u64 ns, timelo, timehi;
+ unsigned long flags;
+ int err;
+
+ /* The latched timestamp belongs to one of the received frames. */
+ __skb_queue_head_init(&received);
+ spin_lock_irqsave(&rxq->lock, flags);
+ skb_queue_splice_tail_init(rxq, &received);
+ spin_unlock_irqrestore(&rxq->lock, flags);
+
+ mutex_lock(&chip->reg_lock);
+ err = mv88e6xxx_port_ptp_read(chip, ps->port_id,
+ reg, buf, ARRAY_SIZE(buf));
+ mutex_unlock(&chip->reg_lock);
+ if (err)
+ pr_err("failed to get the receive time stamp\n");
+
+ status = buf[0];
+ timelo = buf[1];
+ timehi = buf[2];
+ seq_id = buf[3];
+
+ if (status & MV88E6XXX_PTP_TS_VALID) {
+ mutex_lock(&chip->reg_lock);
+ err = mv88e6xxx_port_ptp_write(chip, ps->port_id, reg, 0);
+ mutex_unlock(&chip->reg_lock);
+ if (err)
+ pr_err("failed to clear the receive status\n");
+ }
+ /* Since the device can only handle one time stamp at a time,
+ * we purge any extra frames from the queue.
+ */
+ for ( ; skb; skb = __skb_dequeue(&received)) {
+ if (mv88e6xxx_ts_valid(status) && seq_match(skb, seq_id)) {
+ ns = timehi << 16 | timelo;
+
+ mutex_lock(&chip->reg_lock);
+ ns = timecounter_cyc2time(&chip->tstamp_tc, ns);
+ mutex_unlock(&chip->reg_lock);
+ shwt = skb_hwtstamps(skb);
+ memset(shwt, 0, sizeof(*shwt));
+ shwt->hwtstamp = ns_to_ktime(ns);
+ status &= ~MV88E6XXX_PTP_TS_VALID;
+ }
+ netif_rx_ni(skb);
+ }
+}
+
+static void mv88e6xxx_rxtstamp_work(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_port_hwtstamp *ps)
+{
+ struct sk_buff *skb;
+
+ skb = skb_dequeue(&ps->rx_queue);
+
+ if (skb)
+ mv88e6xxx_get_rxts(chip, ps, skb, MV88E6XXX_PORT_PTP_ARR0_STS,
+ &ps->rx_queue);
+
+ skb = skb_dequeue(&ps->rx_queue2);
+ if (skb)
+ mv88e6xxx_get_rxts(chip, ps, skb, MV88E6XXX_PORT_PTP_ARR1_STS,
+ &ps->rx_queue2);
+}
+
+static int is_pdelay_resp(u8 *msgtype)
+{
+ return (*msgtype & 0xf) == 3;
+}
+
+bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, int port,
+ struct sk_buff *skb, unsigned int type)
+{
+ struct mv88e6xxx_port_hwtstamp *ps;
+ struct mv88e6xxx_chip *chip;
+ u8 *hdr;
+
+ chip = ds->priv;
+ ps = &chip->port_hwtstamp[port];
+
+ if (ps->tstamp_config.rx_filter != HWTSTAMP_FILTER_PTP_V2_EVENT)
+ return false;
+
+ hdr = mv88e6xxx_should_tstamp(chip, port, skb, type);
+ if (!hdr)
+ return false;
+
+ SKB_PTP_TYPE(skb) = type;
+
+ if (is_pdelay_resp(hdr))
+ skb_queue_tail(&ps->rx_queue2, skb);
+ else
+ skb_queue_tail(&ps->rx_queue, skb);
+
+ ptp_schedule_worker(chip->ptp_clock, 0);
+
+ return true;
+}
+
+static int mv88e6xxx_txtstamp_work(struct mv88e6xxx_chip *chip,
+ struct mv88e6xxx_port_hwtstamp *ps)
+{
+ struct skb_shared_hwtstamps shhwtstamps;
+ u16 departure_block[4], status;
+ struct sk_buff *tmp_skb;
+ u32 time_raw;
+ int err;
+ u64 ns;
+
+ if (!ps->tx_skb)
+ return 0;
+
+ mutex_lock(&chip->reg_lock);
+ err = mv88e6xxx_port_ptp_read(chip, ps->port_id,
+ MV88E6XXX_PORT_PTP_DEP_STS,
+ departure_block,
+ ARRAY_SIZE(departure_block));
+ mutex_unlock(&chip->reg_lock);
+
+ if (err)
+ goto free_and_clear_skb;
+
+ if (!(departure_block[0] & MV88E6XXX_PTP_TS_VALID)) {
+ if (time_is_before_jiffies(ps->tx_tstamp_start +
+ TX_TSTAMP_TIMEOUT)) {
+ dev_warn(chip->dev, "p%d: clearing tx timestamp hang\n",
+ ps->port_id);
+ goto free_and_clear_skb;
+ }
+ /* The timestamp should be available quickly, while getting it
+ * is high priority and time bounded to only 10ms. A poll is
+ * warranted so restart the work.
+ */
+ return 1;
+ }
+
+ /* We have the timestamp; go ahead and clear valid now */
+ mutex_lock(&chip->reg_lock);
+ mv88e6xxx_port_ptp_write(chip, ps->port_id,
+ MV88E6XXX_PORT_PTP_DEP_STS, 0);
+ mutex_unlock(&chip->reg_lock);
+
+ status = departure_block[0] & MV88E6XXX_PTP_TS_STATUS_MASK;
+ if (status != MV88E6XXX_PTP_TS_STATUS_NORMAL) {
+ dev_warn(chip->dev, "p%d: tx timestamp overrun\n", ps->port_id);
+ goto free_and_clear_skb;
+ }
+
+ if (departure_block[3] != ps->tx_seq_id) {
+ dev_warn(chip->dev, "p%d: unexpected seq. id\n", ps->port_id);
+ goto free_and_clear_skb;
+ }
+
+ memset(&shhwtstamps, 0, sizeof(shhwtstamps));
+ time_raw = ((u32)departure_block[2] << 16) | departure_block[1];
+ mutex_lock(&chip->reg_lock);
+ ns = timecounter_cyc2time(&chip->tstamp_tc, time_raw);
+ mutex_unlock(&chip->reg_lock);
+ shhwtstamps.hwtstamp = ns_to_ktime(ns);
+
+ dev_dbg(chip->dev,
+ "p%d: txtstamp %llx status 0x%04x skb ID 0x%04x hw ID 0x%04x\n",
+ ps->port_id, ktime_to_ns(shhwtstamps.hwtstamp),
+ departure_block[0], ps->tx_seq_id, departure_block[3]);
+
+ /* skb_complete_tx_timestamp() will free up the client to make
+ * another timestamp-able transmit. We have to be ready for it
+ * -- by clearing the ps->tx_skb "flag" -- beforehand.
+ */
+
+ tmp_skb = ps->tx_skb;
+ ps->tx_skb = NULL;
+ clear_bit_unlock(MV88E6XXX_HWTSTAMP_TX_IN_PROGRESS, &ps->state);
+ skb_complete_tx_timestamp(tmp_skb, &shhwtstamps);
+
+ return 0;
+
+free_and_clear_skb:
+ dev_kfree_skb_any(ps->tx_skb);
+ ps->tx_skb = NULL;
+ clear_bit_unlock(MV88E6XXX_HWTSTAMP_TX_IN_PROGRESS, &ps->state);
+
+ return 0;
+}
+
+long mv88e6xxx_hwtstamp_work(struct ptp_clock_info *ptp)
+{
+ struct mv88e6xxx_chip *chip = ptp_to_chip(ptp);
+ struct dsa_switch *ds = chip->ds;
+ struct mv88e6xxx_port_hwtstamp *ps;
+ int i, restart = 0;
+
+ for (i = 0; i < ds->num_ports; i++) {
+ if (!dsa_is_user_port(ds, i))
+ continue;
+
+ ps = &chip->port_hwtstamp[i];
+ if (test_bit(MV88E6XXX_HWTSTAMP_TX_IN_PROGRESS, &ps->state))
+ restart |= mv88e6xxx_txtstamp_work(chip, ps);
+
+ mv88e6xxx_rxtstamp_work(chip, ps);
+ }
+
+ return restart ? 1 : -1;
+}
+
+bool mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port,
+ struct sk_buff *clone, unsigned int type)
+{
+ struct mv88e6xxx_chip *chip = ds->priv;
+ struct mv88e6xxx_port_hwtstamp *ps = &chip->port_hwtstamp[port];
+ __be16 *seq_ptr;
+ u8 *hdr;
+
+ if (!(skb_shinfo(clone)->tx_flags & SKBTX_HW_TSTAMP))
+ return false;
+
+ hdr = mv88e6xxx_should_tstamp(chip, port, clone, type);
+ if (!hdr)
+ return false;
+
+ seq_ptr = (__be16 *)(hdr + OFF_PTP_SEQUENCE_ID);
+
+ if (test_and_set_bit_lock(MV88E6XXX_HWTSTAMP_TX_IN_PROGRESS,
+ &ps->state))
+ return false;
+
+ ps->tx_skb = clone;
+ ps->tx_tstamp_start = jiffies;
+ ps->tx_seq_id = be16_to_cpup(seq_ptr);
+
+ ptp_schedule_worker(chip->ptp_clock, 0);
+ return true;
+}
+
+static int mv88e6xxx_hwtstamp_port_setup(struct mv88e6xxx_chip *chip, int port)
+{
+ struct mv88e6xxx_port_hwtstamp *ps = &chip->port_hwtstamp[port];
+
+ ps->port_id = port;
+
+ skb_queue_head_init(&ps->rx_queue);
+ skb_queue_head_init(&ps->rx_queue2);
+
+ return mv88e6xxx_port_ptp_write(chip, port, MV88E6XXX_PORT_PTP_CFG0,
+ MV88E6XXX_PORT_PTP_CFG0_DISABLE_PTP);
+}
+
+int mv88e6xxx_hwtstamp_setup(struct mv88e6xxx_chip *chip)
+{
+ int err;
+ int i;
+
+ /* Disable timestamping on all ports. */
+ for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
+ err = mv88e6xxx_hwtstamp_port_setup(chip, i);
+ if (err)
+ return err;
+ }
+
+ /* MV88E6XXX_PTP_MSG_TYPE is a mask of PTP message types to
+ * timestamp. This affects all ports that have timestamping enabled,
+ * but the timestamp config is per-port; thus we configure all events
+ * here and only support the HWTSTAMP_FILTER_*_EVENT filter types.
+ */
+ err = mv88e6xxx_ptp_write(chip, MV88E6XXX_PTP_MSGTYPE,
+ MV88E6XXX_PTP_MSGTYPE_ALL_EVENT);
+ if (err)
+ return err;
+
+ /* Use ARRIVAL1 for peer delay response messages. */
+ err = mv88e6xxx_ptp_write(chip, MV88E6XXX_PTP_TS_ARRIVAL_PTR,
+ MV88E6XXX_PTP_MSGTYPE_PDLAY_RES);
+ if (err)
+ return err;
+
+ /* 88E6341 devices default to timestamping at the PHY, but this has
+ * a hardware issue that results in unreliable timestamps. Force
+ * these devices to timestamp at the MAC.
+ */
+ if (chip->info->family == MV88E6XXX_FAMILY_6341) {
+ u16 val = MV88E6341_PTP_CFG_UPDATE |
+ MV88E6341_PTP_CFG_MODE_IDX |
+ MV88E6341_PTP_CFG_MODE_TS_AT_MAC;
+ err = mv88e6xxx_ptp_write(chip, MV88E6341_PTP_CFG, val);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+void mv88e6xxx_hwtstamp_free(struct mv88e6xxx_chip *chip)
+{
+}
diff --git a/drivers/net/dsa/mv88e6xxx/hwtstamp.h b/drivers/net/dsa/mv88e6xxx/hwtstamp.h
new file mode 100644
index 000000000000..bc71c9212a08
--- /dev/null
+++ b/drivers/net/dsa/mv88e6xxx/hwtstamp.h
@@ -0,0 +1,172 @@
+/*
+ * Marvell 88E6xxx Switch hardware timestamping support
+ *
+ * Copyright (c) 2008 Marvell Semiconductor
+ *
+ * Copyright (c) 2017 National Instruments
+ * Erik Hons <erik.hons@ni.com>
+ * Brandon Streiff <brandon.streiff@ni.com>
+ * Dane Wagner <dane.wagner@ni.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.
+ */
+
+#ifndef _MV88E6XXX_HWTSTAMP_H
+#define _MV88E6XXX_HWTSTAMP_H
+
+#include "chip.h"
+
+/* Global PTP registers */
+/* Offset 0x00: PTP EtherType */
+#define MV88E6XXX_PTP_ETHERTYPE 0x00
+
+/* Offset 0x01: Message Type Timestamp Enables */
+#define MV88E6XXX_PTP_MSGTYPE 0x01
+#define MV88E6XXX_PTP_MSGTYPE_SYNC 0x0001
+#define MV88E6XXX_PTP_MSGTYPE_DELAY_REQ 0x0002
+#define MV88E6XXX_PTP_MSGTYPE_PDLAY_REQ 0x0004
+#define MV88E6XXX_PTP_MSGTYPE_PDLAY_RES 0x0008
+#define MV88E6XXX_PTP_MSGTYPE_ALL_EVENT 0x000f
+
+/* Offset 0x02: Timestamp Arrival Capture Pointers */
+#define MV88E6XXX_PTP_TS_ARRIVAL_PTR 0x02
+
+/* Offset 0x07: PTP Global Configuration */
+#define MV88E6341_PTP_CFG 0x07
+#define MV88E6341_PTP_CFG_UPDATE 0x8000
+#define MV88E6341_PTP_CFG_IDX_MASK 0x7f00
+#define MV88E6341_PTP_CFG_DATA_MASK 0x00ff
+#define MV88E6341_PTP_CFG_MODE_IDX 0x0
+#define MV88E6341_PTP_CFG_MODE_TS_AT_PHY 0x00
+#define MV88E6341_PTP_CFG_MODE_TS_AT_MAC 0x80
+
+/* Offset 0x08: PTP Interrupt Status */
+#define MV88E6XXX_PTP_IRQ_STATUS 0x08
+
+/* Per-Port PTP Registers */
+/* Offset 0x00: PTP Configuration 0 */
+#define MV88E6XXX_PORT_PTP_CFG0 0x00
+#define MV88E6XXX_PORT_PTP_CFG0_TSPEC_SHIFT 12
+#define MV88E6XXX_PORT_PTP_CFG0_TSPEC_MASK 0xf000
+#define MV88E6XXX_PORT_PTP_CFG0_TSPEC_1588 0x0000
+#define MV88E6XXX_PORT_PTP_CFG0_TSPEC_8021AS 0x1000
+#define MV88E6XXX_PORT_PTP_CFG0_DISABLE_TSPEC_MATCH 0x0800
+#define MV88E6XXX_PORT_PTP_CFG0_DISABLE_OVERWRITE 0x0002
+#define MV88E6XXX_PORT_PTP_CFG0_DISABLE_PTP 0x0001
+
+/* Offset 0x01: PTP Configuration 1 */
+#define MV88E6XXX_PORT_PTP_CFG1 0x01
+
+/* Offset 0x02: PTP Configuration 2 */
+#define MV88E6XXX_PORT_PTP_CFG2 0x02
+#define MV88E6XXX_PORT_PTP_CFG2_EMBED_ARRIVAL 0x1000
+#define MV88E6XXX_PORT_PTP_CFG2_DEP_IRQ_EN 0x0002
+#define MV88E6XXX_PORT_PTP_CFG2_ARR_IRQ_EN 0x0001
+
+/* Offset 0x03: PTP LED Configuration */
+#define MV88E6XXX_PORT_PTP_LED_CFG 0x03
+
+/* Offset 0x08: PTP Arrival 0 Status */
+#define MV88E6XXX_PORT_PTP_ARR0_STS 0x08
+
+/* Offset 0x09/0x0A: PTP Arrival 0 Time */
+#define MV88E6XXX_PORT_PTP_ARR0_TIME_LO 0x09
+#define MV88E6XXX_PORT_PTP_ARR0_TIME_HI 0x0a
+
+/* Offset 0x0B: PTP Arrival 0 Sequence ID */
+#define MV88E6XXX_PORT_PTP_ARR0_SEQID 0x0b
+
+/* Offset 0x0C: PTP Arrival 1 Status */
+#define MV88E6XXX_PORT_PTP_ARR1_STS 0x0c
+
+/* Offset 0x0D/0x0E: PTP Arrival 1 Time */
+#define MV88E6XXX_PORT_PTP_ARR1_TIME_LO 0x0d
+#define MV88E6XXX_PORT_PTP_ARR1_TIME_HI 0x0e
+
+/* Offset 0x0F: PTP Arrival 1 Sequence ID */
+#define MV88E6XXX_PORT_PTP_ARR1_SEQID 0x0f
+
+/* Offset 0x10: PTP Departure Status */
+#define MV88E6XXX_PORT_PTP_DEP_STS 0x10
+
+/* Offset 0x11/0x12: PTP Deperture Time */
+#define MV88E6XXX_PORT_PTP_DEP_TIME_LO 0x11
+#define MV88E6XXX_PORT_PTP_DEP_TIME_HI 0x12
+
+/* Offset 0x13: PTP Departure Sequence ID */
+#define MV88E6XXX_PORT_PTP_DEP_SEQID 0x13
+
+/* Status fields for arrival and depature timestamp status registers */
+#define MV88E6XXX_PTP_TS_STATUS_MASK 0x0006
+#define MV88E6XXX_PTP_TS_STATUS_NORMAL 0x0000
+#define MV88E6XXX_PTP_TS_STATUS_OVERWITTEN 0x0002
+#define MV88E6XXX_PTP_TS_STATUS_DISCARDED 0x0004
+#define MV88E6XXX_PTP_TS_VALID 0x0001
+
+#ifdef CONFIG_NET_DSA_MV88E6XXX_PTP
+
+int mv88e6xxx_port_hwtstamp_set(struct dsa_switch *ds, int port,
+ struct ifreq *ifr);
+int mv88e6xxx_port_hwtstamp_get(struct dsa_switch *ds, int port,
+ struct ifreq *ifr);
+
+bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, int port,
+ struct sk_buff *clone, unsigned int type);
+bool mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port,
+ struct sk_buff *clone, unsigned int type);
+
+int mv88e6xxx_get_ts_info(struct dsa_switch *ds, int port,
+ struct ethtool_ts_info *info);
+
+int mv88e6xxx_hwtstamp_setup(struct mv88e6xxx_chip *chip);
+void mv88e6xxx_hwtstamp_free(struct mv88e6xxx_chip *chip);
+
+#else /* !CONFIG_NET_DSA_MV88E6XXX_PTP */
+
+static inline int mv88e6xxx_port_hwtstamp_set(struct dsa_switch *ds,
+ int port, struct ifreq *ifr)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int mv88e6xxx_port_hwtstamp_get(struct dsa_switch *ds,
+ int port, struct ifreq *ifr)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, int port,
+ struct sk_buff *clone,
+ unsigned int type)
+{
+ return false;
+}
+
+static inline bool mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port,
+ struct sk_buff *clone,
+ unsigned int type)
+{
+ return false;
+}
+
+static inline int mv88e6xxx_get_ts_info(struct dsa_switch *ds, int port,
+ struct ethtool_ts_info *info)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int mv88e6xxx_hwtstamp_setup(struct mv88e6xxx_chip *chip)
+{
+ return 0;
+}
+
+static inline void mv88e6xxx_hwtstamp_free(struct mv88e6xxx_chip *chip)
+{
+}
+
+#endif /* CONFIG_NET_DSA_MV88E6XXX_PTP */
+
+#endif /* _MV88E6XXX_HWTSTAMP_H */
diff --git a/drivers/net/dsa/mv88e6xxx/ptp.c b/drivers/net/dsa/mv88e6xxx/ptp.c
new file mode 100644
index 000000000000..bd85e2c390e1
--- /dev/null
+++ b/drivers/net/dsa/mv88e6xxx/ptp.c
@@ -0,0 +1,381 @@
+/*
+ * Marvell 88E6xxx Switch PTP support
+ *
+ * Copyright (c) 2008 Marvell Semiconductor
+ *
+ * Copyright (c) 2017 National Instruments
+ * Erik Hons <erik.hons@ni.com>
+ * Brandon Streiff <brandon.streiff@ni.com>
+ * Dane Wagner <dane.wagner@ni.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.
+ */
+
+#include "chip.h"
+#include "global2.h"
+#include "ptp.h"
+
+/* Raw timestamps are in units of 8-ns clock periods. */
+#define CC_SHIFT 28
+#define CC_MULT (8 << CC_SHIFT)
+#define CC_MULT_NUM (1 << 9)
+#define CC_MULT_DEM 15625ULL
+
+#define TAI_EVENT_WORK_INTERVAL msecs_to_jiffies(100)
+
+#define cc_to_chip(cc) container_of(cc, struct mv88e6xxx_chip, tstamp_cc)
+#define dw_overflow_to_chip(dw) container_of(dw, struct mv88e6xxx_chip, \
+ overflow_work)
+#define dw_tai_event_to_chip(dw) container_of(dw, struct mv88e6xxx_chip, \
+ tai_event_work)
+
+static int mv88e6xxx_tai_read(struct mv88e6xxx_chip *chip, int addr,
+ u16 *data, int len)
+{
+ if (!chip->info->ops->avb_ops->tai_read)
+ return -EOPNOTSUPP;
+
+ return chip->info->ops->avb_ops->tai_read(chip, addr, data, len);
+}
+
+static int mv88e6xxx_tai_write(struct mv88e6xxx_chip *chip, int addr, u16 data)
+{
+ if (!chip->info->ops->avb_ops->tai_write)
+ return -EOPNOTSUPP;
+
+ return chip->info->ops->avb_ops->tai_write(chip, addr, data);
+}
+
+/* TODO: places where this are called should be using pinctrl */
+static int mv88e6xxx_set_gpio_func(struct mv88e6xxx_chip *chip, int pin,
+ int func, int input)
+{
+ int err;
+
+ if (!chip->info->ops->gpio_ops)
+ return -EOPNOTSUPP;
+
+ err = chip->info->ops->gpio_ops->set_dir(chip, pin, input);
+ if (err)
+ return err;
+
+ return chip->info->ops->gpio_ops->set_pctl(chip, pin, func);
+}
+
+static u64 mv88e6xxx_ptp_clock_read(const struct cyclecounter *cc)
+{
+ struct mv88e6xxx_chip *chip = cc_to_chip(cc);
+ u16 phc_time[2];
+ int err;
+
+ err = mv88e6xxx_tai_read(chip, MV88E6XXX_TAI_TIME_LO, phc_time,
+ ARRAY_SIZE(phc_time));
+ if (err)
+ return 0;
+ else
+ return ((u32)phc_time[1] << 16) | phc_time[0];
+}
+
+/* mv88e6xxx_config_eventcap - configure TAI event capture
+ * @event: PTP_CLOCK_PPS (internal) or PTP_CLOCK_EXTTS (external)
+ * @rising: zero for falling-edge trigger, else rising-edge trigger
+ *
+ * This will also reset the capture sequence counter.
+ */
+static int mv88e6xxx_config_eventcap(struct mv88e6xxx_chip *chip, int event,
+ int rising)
+{
+ u16 global_config;
+ u16 cap_config;
+ int err;
+
+ chip->evcap_config = MV88E6XXX_TAI_CFG_CAP_OVERWRITE |
+ MV88E6XXX_TAI_CFG_CAP_CTR_START;
+ if (!rising)
+ chip->evcap_config |= MV88E6XXX_TAI_CFG_EVREQ_FALLING;
+
+ global_config = (chip->evcap_config | chip->trig_config);
+ err = mv88e6xxx_tai_write(chip, MV88E6XXX_TAI_CFG, global_config);
+ if (err)
+ return err;
+
+ if (event == PTP_CLOCK_PPS) {
+ cap_config = MV88E6XXX_TAI_EVENT_STATUS_CAP_TRIG;
+ } else if (event == PTP_CLOCK_EXTTS) {
+ /* if STATUS_CAP_TRIG is unset we capture PTP_EVREQ events */
+ cap_config = 0;
+ } else {
+ return -EINVAL;
+ }
+
+ /* Write the capture config; this also clears the capture counter */
+ err = mv88e6xxx_tai_write(chip, MV88E6XXX_TAI_EVENT_STATUS,
+ cap_config);
+
+ return err;
+}
+
+static void mv88e6xxx_tai_event_work(struct work_struct *ugly)
+{
+ struct delayed_work *dw = to_delayed_work(ugly);
+ struct mv88e6xxx_chip *chip = dw_tai_event_to_chip(dw);
+ struct ptp_clock_event ev;
+ u16 status[4];
+ u32 raw_ts;
+ int err;
+
+ mutex_lock(&chip->reg_lock);
+ err = mv88e6xxx_tai_read(chip, MV88E6XXX_TAI_EVENT_STATUS,
+ status, ARRAY_SIZE(status));
+ mutex_unlock(&chip->reg_lock);
+
+ if (err) {
+ dev_err(chip->dev, "failed to read TAI status register\n");
+ return;
+ }
+ if (status[0] & MV88E6XXX_TAI_EVENT_STATUS_ERROR) {
+ dev_warn(chip->dev, "missed event capture\n");
+ return;
+ }
+ if (!(status[0] & MV88E6XXX_TAI_EVENT_STATUS_VALID))
+ goto out;
+
+ raw_ts = ((u32)status[2] << 16) | status[1];
+
+ /* Clear the valid bit so the next timestamp can come in */
+ status[0] &= ~MV88E6XXX_TAI_EVENT_STATUS_VALID;
+ mutex_lock(&chip->reg_lock);
+ err = mv88e6xxx_tai_write(chip, MV88E6XXX_TAI_EVENT_STATUS, status[0]);
+ mutex_unlock(&chip->reg_lock);
+
+ /* This is an external timestamp */
+ ev.type = PTP_CLOCK_EXTTS;
+
+ /* We only have one timestamping channel. */
+ ev.index = 0;
+ mutex_lock(&chip->reg_lock);
+ ev.timestamp = timecounter_cyc2time(&chip->tstamp_tc, raw_ts);
+ mutex_unlock(&chip->reg_lock);
+
+ ptp_clock_event(chip->ptp_clock, &ev);
+out:
+ schedule_delayed_work(&chip->tai_event_work, TAI_EVENT_WORK_INTERVAL);
+}
+
+static int mv88e6xxx_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
+{
+ struct mv88e6xxx_chip *chip = ptp_to_chip(ptp);
+ int neg_adj = 0;
+ u32 diff, mult;
+ u64 adj;
+
+ if (scaled_ppm < 0) {
+ neg_adj = 1;
+ scaled_ppm = -scaled_ppm;
+ }
+ mult = CC_MULT;
+ adj = CC_MULT_NUM;
+ adj *= scaled_ppm;
+ diff = div_u64(adj, CC_MULT_DEM);
+
+ mutex_lock(&chip->reg_lock);
+
+ timecounter_read(&chip->tstamp_tc);
+ chip->tstamp_cc.mult = neg_adj ? mult - diff : mult + diff;
+
+ mutex_unlock(&chip->reg_lock);
+
+ return 0;
+}
+
+static int mv88e6xxx_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+ struct mv88e6xxx_chip *chip = ptp_to_chip(ptp);
+
+ mutex_lock(&chip->reg_lock);
+ timecounter_adjtime(&chip->tstamp_tc, delta);
+ mutex_unlock(&chip->reg_lock);
+
+ return 0;
+}
+
+static int mv88e6xxx_ptp_gettime(struct ptp_clock_info *ptp,
+ struct timespec64 *ts)
+{
+ struct mv88e6xxx_chip *chip = ptp_to_chip(ptp);
+ u64 ns;
+
+ mutex_lock(&chip->reg_lock);
+ ns = timecounter_read(&chip->tstamp_tc);
+ mutex_unlock(&chip->reg_lock);
+
+ *ts = ns_to_timespec64(ns);
+
+ return 0;
+}
+
+static int mv88e6xxx_ptp_settime(struct ptp_clock_info *ptp,
+ const struct timespec64 *ts)
+{
+ struct mv88e6xxx_chip *chip = ptp_to_chip(ptp);
+ u64 ns;
+
+ ns = timespec64_to_ns(ts);
+
+ mutex_lock(&chip->reg_lock);
+ timecounter_init(&chip->tstamp_tc, &chip->tstamp_cc, ns);
+ mutex_unlock(&chip->reg_lock);
+
+ return 0;
+}
+
+static int mv88e6xxx_ptp_enable_extts(struct mv88e6xxx_chip *chip,
+ struct ptp_clock_request *rq, int on)
+{
+ int rising = (rq->extts.flags & PTP_RISING_EDGE);
+ int func;
+ int pin;
+ int err;
+
+ pin = ptp_find_pin(chip->ptp_clock, PTP_PF_EXTTS, rq->extts.index);
+
+ if (pin < 0)
+ return -EBUSY;
+
+ mutex_lock(&chip->reg_lock);
+
+ if (on) {
+ func = MV88E6352_G2_SCRATCH_GPIO_PCTL_EVREQ;
+
+ err = mv88e6xxx_set_gpio_func(chip, pin, func, true);
+ if (err)
+ goto out;
+
+ schedule_delayed_work(&chip->tai_event_work,
+ TAI_EVENT_WORK_INTERVAL);
+
+ err = mv88e6xxx_config_eventcap(chip, PTP_CLOCK_EXTTS, rising);
+ } else {
+ func = MV88E6352_G2_SCRATCH_GPIO_PCTL_GPIO;
+
+ err = mv88e6xxx_set_gpio_func(chip, pin, func, true);
+
+ cancel_delayed_work_sync(&chip->tai_event_work);
+ }
+
+out:
+ mutex_unlock(&chip->reg_lock);
+
+ return err;
+}
+
+static int mv88e6xxx_ptp_enable(struct ptp_clock_info *ptp,
+ struct ptp_clock_request *rq, int on)
+{
+ struct mv88e6xxx_chip *chip = ptp_to_chip(ptp);
+
+ switch (rq->type) {
+ case PTP_CLK_REQ_EXTTS:
+ return mv88e6xxx_ptp_enable_extts(chip, rq, on);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int mv88e6xxx_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin,
+ enum ptp_pin_function func, unsigned int chan)
+{
+ switch (func) {
+ case PTP_PF_NONE:
+ case PTP_PF_EXTTS:
+ break;
+ case PTP_PF_PEROUT:
+ case PTP_PF_PHYSYNC:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+/* With a 125MHz input clock, the 32-bit timestamp counter overflows in ~34.3
+ * seconds; this task forces periodic reads so that we don't miss any.
+ */
+#define MV88E6XXX_TAI_OVERFLOW_PERIOD (HZ * 16)
+static void mv88e6xxx_ptp_overflow_check(struct work_struct *work)
+{
+ struct delayed_work *dw = to_delayed_work(work);
+ struct mv88e6xxx_chip *chip = dw_overflow_to_chip(dw);
+ struct timespec64 ts;
+
+ mv88e6xxx_ptp_gettime(&chip->ptp_clock_info, &ts);
+
+ schedule_delayed_work(&chip->overflow_work,
+ MV88E6XXX_TAI_OVERFLOW_PERIOD);
+}
+
+int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip)
+{
+ int i;
+
+ /* Set up the cycle counter */
+ memset(&chip->tstamp_cc, 0, sizeof(chip->tstamp_cc));
+ chip->tstamp_cc.read = mv88e6xxx_ptp_clock_read;
+ chip->tstamp_cc.mask = CYCLECOUNTER_MASK(32);
+ chip->tstamp_cc.mult = CC_MULT;
+ chip->tstamp_cc.shift = CC_SHIFT;
+
+ timecounter_init(&chip->tstamp_tc, &chip->tstamp_cc,
+ ktime_to_ns(ktime_get_real()));
+
+ INIT_DELAYED_WORK(&chip->overflow_work, mv88e6xxx_ptp_overflow_check);
+ INIT_DELAYED_WORK(&chip->tai_event_work, mv88e6xxx_tai_event_work);
+
+ chip->ptp_clock_info.owner = THIS_MODULE;
+ snprintf(chip->ptp_clock_info.name, sizeof(chip->ptp_clock_info.name),
+ dev_name(chip->dev));
+ chip->ptp_clock_info.max_adj = 1000000;
+
+ chip->ptp_clock_info.n_ext_ts = 1;
+ chip->ptp_clock_info.n_per_out = 0;
+ chip->ptp_clock_info.n_pins = mv88e6xxx_num_gpio(chip);
+ chip->ptp_clock_info.pps = 0;
+
+ for (i = 0; i < chip->ptp_clock_info.n_pins; ++i) {
+ struct ptp_pin_desc *ppd = &chip->pin_config[i];
+
+ snprintf(ppd->name, sizeof(ppd->name), "mv88e6xxx_gpio%d", i);
+ ppd->index = i;
+ ppd->func = PTP_PF_NONE;
+ }
+ chip->ptp_clock_info.pin_config = chip->pin_config;
+
+ chip->ptp_clock_info.adjfine = mv88e6xxx_ptp_adjfine;
+ chip->ptp_clock_info.adjtime = mv88e6xxx_ptp_adjtime;
+ chip->ptp_clock_info.gettime64 = mv88e6xxx_ptp_gettime;
+ chip->ptp_clock_info.settime64 = mv88e6xxx_ptp_settime;
+ chip->ptp_clock_info.enable = mv88e6xxx_ptp_enable;
+ chip->ptp_clock_info.verify = mv88e6xxx_ptp_verify;
+ chip->ptp_clock_info.do_aux_work = mv88e6xxx_hwtstamp_work;
+
+ chip->ptp_clock = ptp_clock_register(&chip->ptp_clock_info, chip->dev);
+ if (IS_ERR(chip->ptp_clock))
+ return PTR_ERR(chip->ptp_clock);
+
+ schedule_delayed_work(&chip->overflow_work,
+ MV88E6XXX_TAI_OVERFLOW_PERIOD);
+
+ return 0;
+}
+
+void mv88e6xxx_ptp_free(struct mv88e6xxx_chip *chip)
+{
+ if (chip->ptp_clock) {
+ cancel_delayed_work_sync(&chip->overflow_work);
+ cancel_delayed_work_sync(&chip->tai_event_work);
+
+ ptp_clock_unregister(chip->ptp_clock);
+ chip->ptp_clock = NULL;
+ }
+}
diff --git a/drivers/net/dsa/mv88e6xxx/ptp.h b/drivers/net/dsa/mv88e6xxx/ptp.h
new file mode 100644
index 000000000000..10f271ab650d
--- /dev/null
+++ b/drivers/net/dsa/mv88e6xxx/ptp.h
@@ -0,0 +1,108 @@
+/*
+ * Marvell 88E6xxx Switch PTP support
+ *
+ * Copyright (c) 2008 Marvell Semiconductor
+ *
+ * Copyright (c) 2017 National Instruments
+ * Erik Hons <erik.hons@ni.com>
+ * Brandon Streiff <brandon.streiff@ni.com>
+ * Dane Wagner <dane.wagner@ni.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.
+ */
+
+#ifndef _MV88E6XXX_PTP_H
+#define _MV88E6XXX_PTP_H
+
+#include "chip.h"
+
+/* Offset 0x00: TAI Global Config */
+#define MV88E6XXX_TAI_CFG 0x00
+#define MV88E6XXX_TAI_CFG_CAP_OVERWRITE 0x8000
+#define MV88E6XXX_TAI_CFG_CAP_CTR_START 0x4000
+#define MV88E6XXX_TAI_CFG_EVREQ_FALLING 0x2000
+#define MV88E6XXX_TAI_CFG_TRIG_ACTIVE_LO 0x1000
+#define MV88E6XXX_TAI_CFG_IRL_ENABLE 0x0400
+#define MV88E6XXX_TAI_CFG_TRIG_IRQ_EN 0x0200
+#define MV88E6XXX_TAI_CFG_EVREQ_IRQ_EN 0x0100
+#define MV88E6XXX_TAI_CFG_TRIG_LOCK 0x0080
+#define MV88E6XXX_TAI_CFG_BLOCK_UPDATE 0x0008
+#define MV88E6XXX_TAI_CFG_MULTI_PTP 0x0004
+#define MV88E6XXX_TAI_CFG_TRIG_MODE_ONESHOT 0x0002
+#define MV88E6XXX_TAI_CFG_TRIG_ENABLE 0x0001
+
+/* Offset 0x01: Timestamp Clock Period (ps) */
+#define MV88E6XXX_TAI_CLOCK_PERIOD 0x01
+
+/* Offset 0x02/0x03: Trigger Generation Amount */
+#define MV88E6XXX_TAI_TRIG_GEN_AMOUNT_LO 0x02
+#define MV88E6XXX_TAI_TRIG_GEN_AMOUNT_HI 0x03
+
+/* Offset 0x04: Clock Compensation */
+#define MV88E6XXX_TAI_TRIG_CLOCK_COMP 0x04
+
+/* Offset 0x05: Trigger Configuration */
+#define MV88E6XXX_TAI_TRIG_CFG 0x05
+
+/* Offset 0x06: Ingress Rate Limiter Clock Generation Amount */
+#define MV88E6XXX_TAI_IRL_AMOUNT 0x06
+
+/* Offset 0x07: Ingress Rate Limiter Compensation */
+#define MV88E6XXX_TAI_IRL_COMP 0x07
+
+/* Offset 0x08: Ingress Rate Limiter Compensation */
+#define MV88E6XXX_TAI_IRL_COMP_PS 0x08
+
+/* Offset 0x09: Event Status */
+#define MV88E6XXX_TAI_EVENT_STATUS 0x09
+#define MV88E6XXX_TAI_EVENT_STATUS_CAP_TRIG 0x4000
+#define MV88E6XXX_TAI_EVENT_STATUS_ERROR 0x0200
+#define MV88E6XXX_TAI_EVENT_STATUS_VALID 0x0100
+#define MV88E6XXX_TAI_EVENT_STATUS_CTR_MASK 0x00ff
+
+/* Offset 0x0A/0x0B: Event Time */
+#define MV88E6XXX_TAI_EVENT_TIME_LO 0x0a
+#define MV88E6XXX_TAI_EVENT_TYPE_HI 0x0b
+
+/* Offset 0x0E/0x0F: PTP Global Time */
+#define MV88E6XXX_TAI_TIME_LO 0x0e
+#define MV88E6XXX_TAI_TIME_HI 0x0f
+
+/* Offset 0x10/0x11: Trig Generation Time */
+#define MV88E6XXX_TAI_TRIG_TIME_LO 0x10
+#define MV88E6XXX_TAI_TRIG_TIME_HI 0x11
+
+/* Offset 0x12: Lock Status */
+#define MV88E6XXX_TAI_LOCK_STATUS 0x12
+
+#ifdef CONFIG_NET_DSA_MV88E6XXX_PTP
+
+long mv88e6xxx_hwtstamp_work(struct ptp_clock_info *ptp);
+int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip);
+void mv88e6xxx_ptp_free(struct mv88e6xxx_chip *chip);
+
+#define ptp_to_chip(ptp) container_of(ptp, struct mv88e6xxx_chip, \
+ ptp_clock_info)
+
+#else /* !CONFIG_NET_DSA_MV88E6XXX_PTP */
+
+static inline long mv88e6xxx_hwtstamp_work(struct ptp_clock_info *ptp)
+{
+ return -1;
+}
+
+static inline int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip)
+{
+ return 0;
+}
+
+static inline void mv88e6xxx_ptp_free(struct mv88e6xxx_chip *chip)
+{
+}
+
+#endif /* CONFIG_NET_DSA_MV88E6XXX_PTP */
+
+#endif /* _MV88E6XXX_PTP_H */
diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c
index f3c01119b3d1..fb058fd35c0d 100644
--- a/drivers/net/dsa/mv88e6xxx/serdes.c
+++ b/drivers/net/dsa/mv88e6xxx/serdes.c
@@ -55,18 +55,30 @@ static int mv88e6352_serdes_power_set(struct mv88e6xxx_chip *chip, bool on)
return err;
}
-int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
+static bool mv88e6352_port_has_serdes(struct mv88e6xxx_chip *chip, int port)
{
- int err;
u8 cmode;
+ int err;
err = mv88e6xxx_port_get_cmode(chip, port, &cmode);
- if (err)
- return err;
+ if (err) {
+ dev_err(chip->dev, "failed to read cmode\n");
+ return false;
+ }
if ((cmode == MV88E6XXX_PORT_STS_CMODE_100BASE_X) ||
(cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) ||
- (cmode == MV88E6XXX_PORT_STS_CMODE_SGMII)) {
+ (cmode == MV88E6XXX_PORT_STS_CMODE_SGMII))
+ return true;
+
+ return false;
+}
+
+int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
+{
+ int err;
+
+ if (mv88e6352_port_has_serdes(chip, port)) {
err = mv88e6352_serdes_power_set(chip, on);
if (err < 0)
return err;
@@ -75,6 +87,93 @@ int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
return 0;
}
+struct mv88e6352_serdes_hw_stat {
+ char string[ETH_GSTRING_LEN];
+ int sizeof_stat;
+ int reg;
+};
+
+static struct mv88e6352_serdes_hw_stat mv88e6352_serdes_hw_stats[] = {
+ { "serdes_fibre_rx_error", 16, 21 },
+ { "serdes_PRBS_error", 32, 24 },
+};
+
+int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port)
+{
+ if (mv88e6352_port_has_serdes(chip, port))
+ return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
+
+ return 0;
+}
+
+int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip,
+ int port, uint8_t *data)
+{
+ struct mv88e6352_serdes_hw_stat *stat;
+ int i;
+
+ if (!mv88e6352_port_has_serdes(chip, port))
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) {
+ stat = &mv88e6352_serdes_hw_stats[i];
+ memcpy(data + i * ETH_GSTRING_LEN, stat->string,
+ ETH_GSTRING_LEN);
+ }
+ return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
+}
+
+static uint64_t mv88e6352_serdes_get_stat(struct mv88e6xxx_chip *chip,
+ struct mv88e6352_serdes_hw_stat *stat)
+{
+ u64 val = 0;
+ u16 reg;
+ int err;
+
+ err = mv88e6352_serdes_read(chip, stat->reg, &reg);
+ if (err) {
+ dev_err(chip->dev, "failed to read statistic\n");
+ return 0;
+ }
+
+ val = reg;
+
+ if (stat->sizeof_stat == 32) {
+ err = mv88e6352_serdes_read(chip, stat->reg + 1, &reg);
+ if (err) {
+ dev_err(chip->dev, "failed to read statistic\n");
+ return 0;
+ }
+ val = val << 16 | reg;
+ }
+
+ return val;
+}
+
+int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,
+ uint64_t *data)
+{
+ struct mv88e6xxx_port *mv88e6xxx_port = &chip->ports[port];
+ struct mv88e6352_serdes_hw_stat *stat;
+ u64 value;
+ int i;
+
+ if (!mv88e6352_port_has_serdes(chip, port))
+ return 0;
+
+ BUILD_BUG_ON(ARRAY_SIZE(mv88e6352_serdes_hw_stats) >
+ ARRAY_SIZE(mv88e6xxx_port->serdes_stats));
+
+ for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) {
+ stat = &mv88e6352_serdes_hw_stats[i];
+ value = mv88e6352_serdes_get_stat(chip, stat);
+ mv88e6xxx_port->serdes_stats[i] += value;
+ data[i] = mv88e6xxx_port->serdes_stats[i];
+ }
+
+ return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
+}
+
/* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */
static int mv88e6390_serdes_10g(struct mv88e6xxx_chip *chip, int addr, bool on)
{
diff --git a/drivers/net/dsa/mv88e6xxx/serdes.h b/drivers/net/dsa/mv88e6xxx/serdes.h
index 5c1cd6d8e9a5..1897c01c6e19 100644
--- a/drivers/net/dsa/mv88e6xxx/serdes.h
+++ b/drivers/net/dsa/mv88e6xxx/serdes.h
@@ -44,5 +44,9 @@
int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on);
int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on);
-
+int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port);
+int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip,
+ int port, uint8_t *data);
+int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,
+ uint64_t *data);
#endif
diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
index 9df22ebee822..600d5ad1fbde 100644
--- a/drivers/net/dsa/qca8k.c
+++ b/drivers/net/dsa/qca8k.c
@@ -631,7 +631,7 @@ qca8k_get_ethtool_stats(struct dsa_switch *ds, int port,
}
static int
-qca8k_get_sset_count(struct dsa_switch *ds)
+qca8k_get_sset_count(struct dsa_switch *ds, int port)
{
return ARRAY_SIZE(ar8327_mib);
}
diff --git a/drivers/net/dummy.c b/drivers/net/dummy.c
index 30b1c8512049..0d15a12a4560 100644
--- a/drivers/net/dummy.c
+++ b/drivers/net/dummy.c
@@ -219,6 +219,7 @@ static int __init dummy_init_module(void)
{
int i, err = 0;
+ down_write(&pernet_ops_rwsem);
rtnl_lock();
err = __rtnl_link_register(&dummy_link_ops);
if (err < 0)
@@ -233,6 +234,7 @@ static int __init dummy_init_module(void)
out:
rtnl_unlock();
+ up_write(&pernet_ops_rwsem);
return err;
}
diff --git a/drivers/net/ethernet/8390/Kconfig b/drivers/net/ethernet/8390/Kconfig
index fdc673484add..9fee7c83ef9f 100644
--- a/drivers/net/ethernet/8390/Kconfig
+++ b/drivers/net/ethernet/8390/Kconfig
@@ -87,8 +87,7 @@ config MCF8390
config NE2000
tristate "NE2000/NE1000 support"
- depends on (ISA || (Q40 && m) || M32R || MACH_TX49XX || \
- ATARI_ETHERNEC)
+ depends on (ISA || (Q40 && m) || MACH_TX49XX || ATARI_ETHERNEC)
select CRC32
---help---
If you have a network (Ethernet) card of this type, say Y here.
diff --git a/drivers/net/ethernet/8390/Makefile b/drivers/net/ethernet/8390/Makefile
index f975c2fc88a3..1d650e66cc6e 100644
--- a/drivers/net/ethernet/8390/Makefile
+++ b/drivers/net/ethernet/8390/Makefile
@@ -7,8 +7,8 @@ obj-$(CONFIG_MAC8390) += mac8390.o
obj-$(CONFIG_APNE) += apne.o 8390.o
obj-$(CONFIG_ARM_ETHERH) += etherh.o
obj-$(CONFIG_AX88796) += ax88796.o
-obj-$(CONFIG_HYDRA) += hydra.o 8390.o
-obj-$(CONFIG_MCF8390) += mcf8390.o 8390.o
+obj-$(CONFIG_HYDRA) += hydra.o
+obj-$(CONFIG_MCF8390) += mcf8390.o
obj-$(CONFIG_NE2000) += ne.o 8390p.o
obj-$(CONFIG_NE2K_PCI) += ne2k-pci.o 8390.o
obj-$(CONFIG_PCMCIA_AXNET) += axnet_cs.o 8390.o
@@ -16,4 +16,4 @@ obj-$(CONFIG_PCMCIA_PCNET) += pcnet_cs.o 8390.o
obj-$(CONFIG_STNIC) += stnic.o 8390.o
obj-$(CONFIG_ULTRA) += smc-ultra.o 8390.o
obj-$(CONFIG_WD80x3) += wd.o 8390.o
-obj-$(CONFIG_ZORRO8390) += zorro8390.o 8390.o
+obj-$(CONFIG_ZORRO8390) += zorro8390.o
diff --git a/drivers/net/ethernet/8390/apne.c b/drivers/net/ethernet/8390/apne.c
index c56ac9ebc08f..fe6c834c422e 100644
--- a/drivers/net/ethernet/8390/apne.c
+++ b/drivers/net/ethernet/8390/apne.c
@@ -117,7 +117,7 @@ static const char version[] =
static int apne_owned; /* signal if card already owned */
static u32 apne_msg_enable;
-module_param_named(msg_enable, apne_msg_enable, uint, (S_IRUSR|S_IRGRP|S_IROTH));
+module_param_named(msg_enable, apne_msg_enable, uint, 0444);
MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)");
struct net_device * __init apne_probe(int unit)
diff --git a/drivers/net/ethernet/8390/ax88796.c b/drivers/net/ethernet/8390/ax88796.c
index 245554707163..da61cf3cb3a9 100644
--- a/drivers/net/ethernet/8390/ax88796.c
+++ b/drivers/net/ethernet/8390/ax88796.c
@@ -77,8 +77,6 @@ static unsigned char version[] = "ax88796.c: Copyright 2005,2007 Simtec Electron
#define AX_GPOC_PPDSET BIT(6)
-static u32 ax_msg_enable;
-
/* device private data */
struct ax_device {
@@ -747,7 +745,6 @@ static int ax_init_dev(struct net_device *dev)
ei_local->block_output = &ax_block_output;
ei_local->get_8390_hdr = &ax_get_8390_hdr;
ei_local->priv = 0;
- ei_local->msg_enable = ax_msg_enable;
dev->netdev_ops = &ax_netdev_ops;
dev->ethtool_ops = &ax_ethtool_ops;
diff --git a/drivers/net/ethernet/8390/axnet_cs.c b/drivers/net/ethernet/8390/axnet_cs.c
index 7bddb8efb6d5..d422a124cd7c 100644
--- a/drivers/net/ethernet/8390/axnet_cs.c
+++ b/drivers/net/ethernet/8390/axnet_cs.c
@@ -104,7 +104,6 @@ static void AX88190_init(struct net_device *dev, int startp);
static int ax_open(struct net_device *dev);
static int ax_close(struct net_device *dev);
static irqreturn_t ax_interrupt(int irq, void *dev_id);
-static u32 axnet_msg_enable;
/*====================================================================*/
@@ -151,7 +150,6 @@ static int axnet_probe(struct pcmcia_device *link)
return -ENOMEM;
ei_local = netdev_priv(dev);
- ei_local->msg_enable = axnet_msg_enable;
spin_lock_init(&ei_local->page_lock);
info = PRIV(dev);
diff --git a/drivers/net/ethernet/8390/etherh.c b/drivers/net/ethernet/8390/etherh.c
index 11cbf22ad201..32e9627e3880 100644
--- a/drivers/net/ethernet/8390/etherh.c
+++ b/drivers/net/ethernet/8390/etherh.c
@@ -64,8 +64,6 @@ static char version[] =
#include "lib8390.c"
-static u32 etherh_msg_enable;
-
struct etherh_priv {
void __iomem *ioc_fast;
void __iomem *memc;
@@ -502,18 +500,6 @@ etherh_close(struct net_device *dev)
}
/*
- * Initialisation
- */
-
-static void __init etherh_banner(void)
-{
- static int version_printed;
-
- if ((etherh_msg_enable & NETIF_MSG_DRV) && (version_printed++ == 0))
- pr_info("%s", version);
-}
-
-/*
* Read the ethernet address string from the on board rom.
* This is an ascii string...
*/
@@ -671,8 +657,6 @@ etherh_probe(struct expansion_card *ec, const struct ecard_id *id)
struct etherh_priv *eh;
int ret;
- etherh_banner();
-
ret = ecard_request_resources(ec);
if (ret)
goto out;
@@ -757,7 +741,6 @@ etherh_probe(struct expansion_card *ec, const struct ecard_id *id)
ei_local->block_output = etherh_block_output;
ei_local->get_8390_hdr = etherh_get_header;
ei_local->interface_num = 0;
- ei_local->msg_enable = etherh_msg_enable;
etherh_reset(dev);
__NS8390_init(dev, 0);
diff --git a/drivers/net/ethernet/8390/hydra.c b/drivers/net/ethernet/8390/hydra.c
index 8ae249195301..941754ea78ec 100644
--- a/drivers/net/ethernet/8390/hydra.c
+++ b/drivers/net/ethernet/8390/hydra.c
@@ -66,7 +66,6 @@ static void hydra_block_input(struct net_device *dev, int count,
static void hydra_block_output(struct net_device *dev, int count,
const unsigned char *buf, int start_page);
static void hydra_remove_one(struct zorro_dev *z);
-static u32 hydra_msg_enable;
static struct zorro_device_id hydra_zorro_tbl[] = {
{ ZORRO_PROD_HYDRA_SYSTEMS_AMIGANET },
@@ -119,7 +118,6 @@ static int hydra_init(struct zorro_dev *z)
int start_page, stop_page;
int j;
int err;
- struct ei_device *ei_local;
static u32 hydra_offsets[16] = {
0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e,
@@ -138,8 +136,6 @@ static int hydra_init(struct zorro_dev *z)
start_page = NESM_START_PG;
stop_page = NESM_STOP_PG;
- ei_local = netdev_priv(dev);
- ei_local->msg_enable = hydra_msg_enable;
dev->base_addr = ioaddr;
dev->irq = IRQ_AMIGA_PORTS;
diff --git a/drivers/net/ethernet/8390/lib8390.c b/drivers/net/ethernet/8390/lib8390.c
index 60f8e2c8e726..c9c55c9eab9f 100644
--- a/drivers/net/ethernet/8390/lib8390.c
+++ b/drivers/net/ethernet/8390/lib8390.c
@@ -113,7 +113,7 @@ static void __NS8390_init(struct net_device *dev, int startp);
static unsigned version_printed;
static u32 msg_enable;
-module_param(msg_enable, uint, (S_IRUSR|S_IRGRP|S_IROTH));
+module_param(msg_enable, uint, 0444);
MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)");
/*
@@ -975,6 +975,8 @@ static void ethdev_setup(struct net_device *dev)
ether_setup(dev);
spin_lock_init(&ei_local->page_lock);
+
+ ei_local->msg_enable = msg_enable;
}
/**
diff --git a/drivers/net/ethernet/8390/mac8390.c b/drivers/net/ethernet/8390/mac8390.c
index 2f91ce8dc614..b6d735bf8011 100644
--- a/drivers/net/ethernet/8390/mac8390.c
+++ b/drivers/net/ethernet/8390/mac8390.c
@@ -123,8 +123,7 @@ enum mac8390_access {
};
extern int mac8390_memtest(struct net_device *dev);
-static int mac8390_initdev(struct net_device *dev,
- struct nubus_rsrc *ndev,
+static int mac8390_initdev(struct net_device *dev, struct nubus_board *board,
enum mac8390_type type);
static int mac8390_open(struct net_device *dev);
@@ -168,9 +167,8 @@ static void slow_sane_block_output(struct net_device *dev, int count,
const unsigned char *buf, int start_page);
static void word_memcpy_tocard(unsigned long tp, const void *fp, int count);
static void word_memcpy_fromcard(void *tp, unsigned long fp, int count);
-static u32 mac8390_msg_enable;
-static enum mac8390_type __init mac8390_ident(struct nubus_rsrc *fres)
+static enum mac8390_type mac8390_ident(struct nubus_rsrc *fres)
{
switch (fres->dr_sw) {
case NUBUS_DRSW_3COM:
@@ -236,7 +234,7 @@ static enum mac8390_type __init mac8390_ident(struct nubus_rsrc *fres)
return MAC8390_NONE;
}
-static enum mac8390_access __init mac8390_testio(volatile unsigned long membase)
+static enum mac8390_access mac8390_testio(unsigned long membase)
{
unsigned long outdata = 0xA5A0B5B0;
unsigned long indata = 0x00000000;
@@ -254,7 +252,7 @@ static enum mac8390_access __init mac8390_testio(volatile unsigned long membase)
return ACCESS_UNKNOWN;
}
-static int __init mac8390_memsize(unsigned long membase)
+static int mac8390_memsize(unsigned long membase)
{
unsigned long flags;
int i, j;
@@ -290,36 +288,34 @@ static int __init mac8390_memsize(unsigned long membase)
return i * 0x1000;
}
-static bool __init mac8390_init(struct net_device *dev,
- struct nubus_rsrc *ndev,
- enum mac8390_type cardtype)
+static bool mac8390_rsrc_init(struct net_device *dev,
+ struct nubus_rsrc *fres,
+ enum mac8390_type cardtype)
{
+ struct nubus_board *board = fres->board;
struct nubus_dir dir;
struct nubus_dirent ent;
int offset;
volatile unsigned short *i;
- printk_once(KERN_INFO pr_fmt("%s"), version);
-
- dev->irq = SLOT2IRQ(ndev->board->slot);
+ dev->irq = SLOT2IRQ(board->slot);
/* This is getting to be a habit */
- dev->base_addr = (ndev->board->slot_addr |
- ((ndev->board->slot & 0xf) << 20));
+ dev->base_addr = board->slot_addr | ((board->slot & 0xf) << 20);
/*
* Get some Nubus info - we will trust the card's idea
* of where its memory and registers are.
*/
- if (nubus_get_func_dir(ndev, &dir) == -1) {
- pr_err("%s: Unable to get Nubus functional directory for slot %X!\n",
- dev->name, ndev->board->slot);
+ if (nubus_get_func_dir(fres, &dir) == -1) {
+ dev_err(&board->dev,
+ "Unable to get Nubus functional directory\n");
return false;
}
/* Get the MAC address */
if (nubus_find_rsrc(&dir, NUBUS_RESID_MAC_ADDRESS, &ent) == -1) {
- pr_info("%s: Couldn't get MAC address!\n", dev->name);
+ dev_info(&board->dev, "MAC address resource not found\n");
return false;
}
@@ -329,8 +325,8 @@ static bool __init mac8390_init(struct net_device *dev,
nubus_rewinddir(&dir);
if (nubus_find_rsrc(&dir, NUBUS_RESID_MINOR_BASEOS,
&ent) == -1) {
- pr_err("%s: Memory offset resource for slot %X not found!\n",
- dev->name, ndev->board->slot);
+ dev_err(&board->dev,
+ "Memory offset resource not found\n");
return false;
}
nubus_get_rsrc_mem(&offset, &ent, 4);
@@ -340,8 +336,8 @@ static bool __init mac8390_init(struct net_device *dev,
nubus_rewinddir(&dir);
if (nubus_find_rsrc(&dir, NUBUS_RESID_MINOR_LENGTH,
&ent) == -1) {
- pr_info("%s: Memory length resource for slot %X not found, probing\n",
- dev->name, ndev->board->slot);
+ dev_info(&board->dev,
+ "Memory length resource not found, probing\n");
offset = mac8390_memsize(dev->mem_start);
} else {
nubus_get_rsrc_mem(&offset, &ent, 4);
@@ -351,25 +347,25 @@ static bool __init mac8390_init(struct net_device *dev,
switch (cardtype) {
case MAC8390_KINETICS:
case MAC8390_DAYNA: /* it's the same */
- dev->base_addr = (int)(ndev->board->slot_addr +
+ dev->base_addr = (int)(board->slot_addr +
DAYNA_8390_BASE);
- dev->mem_start = (int)(ndev->board->slot_addr +
+ dev->mem_start = (int)(board->slot_addr +
DAYNA_8390_MEM);
dev->mem_end = dev->mem_start +
mac8390_memsize(dev->mem_start);
break;
case MAC8390_INTERLAN:
- dev->base_addr = (int)(ndev->board->slot_addr +
+ dev->base_addr = (int)(board->slot_addr +
INTERLAN_8390_BASE);
- dev->mem_start = (int)(ndev->board->slot_addr +
+ dev->mem_start = (int)(board->slot_addr +
INTERLAN_8390_MEM);
dev->mem_end = dev->mem_start +
mac8390_memsize(dev->mem_start);
break;
case MAC8390_CABLETRON:
- dev->base_addr = (int)(ndev->board->slot_addr +
+ dev->base_addr = (int)(board->slot_addr +
CABLETRON_8390_BASE);
- dev->mem_start = (int)(ndev->board->slot_addr +
+ dev->mem_start = (int)(board->slot_addr +
CABLETRON_8390_MEM);
/* The base address is unreadable if 0x00
* has been written to the command register
@@ -384,8 +380,8 @@ static bool __init mac8390_init(struct net_device *dev,
break;
default:
- pr_err("Card type %s is unsupported, sorry\n",
- ndev->board->name);
+ dev_err(&board->dev,
+ "No known base address for card type\n");
return false;
}
}
@@ -393,91 +389,83 @@ static bool __init mac8390_init(struct net_device *dev,
return true;
}
-struct net_device * __init mac8390_probe(int unit)
+static int mac8390_device_probe(struct nubus_board *board)
{
struct net_device *dev;
- struct nubus_rsrc *ndev = NULL;
int err = -ENODEV;
- struct ei_device *ei_local;
-
- static unsigned int slots;
-
- enum mac8390_type cardtype;
-
- /* probably should check for Nubus instead */
-
- if (!MACH_IS_MAC)
- return ERR_PTR(-ENODEV);
+ struct nubus_rsrc *fres;
+ enum mac8390_type cardtype = MAC8390_NONE;
dev = ____alloc_ei_netdev(0);
if (!dev)
- return ERR_PTR(-ENOMEM);
-
- if (unit >= 0)
- sprintf(dev->name, "eth%d", unit);
+ return -ENOMEM;
- for_each_func_rsrc(ndev) {
- if (ndev->category != NUBUS_CAT_NETWORK ||
- ndev->type != NUBUS_TYPE_ETHERNET)
- continue;
+ SET_NETDEV_DEV(dev, &board->dev);
- /* Have we seen it already? */
- if (slots & (1 << ndev->board->slot))
+ for_each_board_func_rsrc(board, fres) {
+ if (fres->category != NUBUS_CAT_NETWORK ||
+ fres->type != NUBUS_TYPE_ETHERNET)
continue;
- slots |= 1 << ndev->board->slot;
- cardtype = mac8390_ident(ndev);
+ cardtype = mac8390_ident(fres);
if (cardtype == MAC8390_NONE)
continue;
- if (!mac8390_init(dev, ndev, cardtype))
- continue;
-
- /* Do the nasty 8390 stuff */
- if (!mac8390_initdev(dev, ndev, cardtype))
+ if (mac8390_rsrc_init(dev, fres, cardtype))
break;
}
-
- if (!ndev)
+ if (!fres)
goto out;
- ei_local = netdev_priv(dev);
- ei_local->msg_enable = mac8390_msg_enable;
+ err = mac8390_initdev(dev, board, cardtype);
+ if (err)
+ goto out;
err = register_netdev(dev);
if (err)
goto out;
- return dev;
+
+ nubus_set_drvdata(board, dev);
+ return 0;
out:
free_netdev(dev);
- return ERR_PTR(err);
+ return err;
+}
+
+static int mac8390_device_remove(struct nubus_board *board)
+{
+ struct net_device *dev = nubus_get_drvdata(board);
+
+ unregister_netdev(dev);
+ free_netdev(dev);
+ return 0;
}
-#ifdef MODULE
+static struct nubus_driver mac8390_driver = {
+ .probe = mac8390_device_probe,
+ .remove = mac8390_device_remove,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ }
+};
+
MODULE_AUTHOR("David Huggins-Daines <dhd@debian.org> and others");
MODULE_DESCRIPTION("Macintosh NS8390-based Nubus Ethernet driver");
MODULE_LICENSE("GPL");
-static struct net_device *dev_mac8390;
-
-int __init init_module(void)
+static int __init mac8390_init(void)
{
- dev_mac8390 = mac8390_probe(-1);
- if (IS_ERR(dev_mac8390)) {
- pr_warn("mac8390: No card found\n");
- return PTR_ERR(dev_mac8390);
- }
- return 0;
+ return nubus_driver_register(&mac8390_driver);
}
+module_init(mac8390_init);
-void __exit cleanup_module(void)
+static void __exit mac8390_exit(void)
{
- unregister_netdev(dev_mac8390);
- free_netdev(dev_mac8390);
+ nubus_driver_unregister(&mac8390_driver);
}
-
-#endif /* MODULE */
+module_exit(mac8390_exit);
static const struct net_device_ops mac8390_netdev_ops = {
.ndo_open = mac8390_open,
@@ -493,9 +481,8 @@ static const struct net_device_ops mac8390_netdev_ops = {
#endif
};
-static int __init mac8390_initdev(struct net_device *dev,
- struct nubus_rsrc *ndev,
- enum mac8390_type type)
+static int mac8390_initdev(struct net_device *dev, struct nubus_board *board,
+ enum mac8390_type type)
{
static u32 fwrd4_offsets[16] = {
0, 4, 8, 12,
@@ -546,7 +533,8 @@ static int __init mac8390_initdev(struct net_device *dev,
case MAC8390_APPLE:
switch (mac8390_testio(dev->mem_start)) {
case ACCESS_UNKNOWN:
- pr_err("Don't know how to access card memory!\n");
+ dev_err(&board->dev,
+ "Don't know how to access card memory\n");
return -ENODEV;
case ACCESS_16:
@@ -612,21 +600,18 @@ static int __init mac8390_initdev(struct net_device *dev,
break;
default:
- pr_err("Card type %s is unsupported, sorry\n",
- ndev->board->name);
+ dev_err(&board->dev, "Unsupported card type\n");
return -ENODEV;
}
__NS8390_init(dev, 0);
/* Good, done, now spit out some messages */
- pr_info("%s: %s in slot %X (type %s)\n",
- dev->name, ndev->board->name, ndev->board->slot,
- cardname[type]);
- pr_info("MAC %pM IRQ %d, %d KB shared memory at %#lx, %d-bit access.\n",
- dev->dev_addr, dev->irq,
- (unsigned int)(dev->mem_end - dev->mem_start) >> 10,
- dev->mem_start, access_bitmode ? 32 : 16);
+ dev_info(&board->dev, "%s (type %s)\n", board->name, cardname[type]);
+ dev_info(&board->dev, "MAC %pM, IRQ %d, %d KB shared memory at %#lx, %d-bit access.\n",
+ dev->dev_addr, dev->irq,
+ (unsigned int)(dev->mem_end - dev->mem_start) >> 10,
+ dev->mem_start, access_bitmode ? 32 : 16);
return 0;
}
diff --git a/drivers/net/ethernet/8390/mcf8390.c b/drivers/net/ethernet/8390/mcf8390.c
index 4bb967bc879e..4ad8031ab669 100644
--- a/drivers/net/ethernet/8390/mcf8390.c
+++ b/drivers/net/ethernet/8390/mcf8390.c
@@ -38,7 +38,6 @@ static const char version[] =
#define NESM_START_PG 0x40 /* First page of TX buffer */
#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
-static u32 mcf8390_msg_enable;
#ifdef NE2000_ODDOFFSET
/*
@@ -407,7 +406,6 @@ static int mcf8390_init(struct net_device *dev)
static int mcf8390_probe(struct platform_device *pdev)
{
struct net_device *dev;
- struct ei_device *ei_local;
struct resource *mem, *irq;
resource_size_t msize;
int ret;
@@ -435,8 +433,6 @@ static int mcf8390_probe(struct platform_device *pdev)
SET_NETDEV_DEV(dev, &pdev->dev);
platform_set_drvdata(pdev, dev);
- ei_local = netdev_priv(dev);
- ei_local->msg_enable = mcf8390_msg_enable;
dev->irq = irq->start;
dev->base_addr = mem->start;
diff --git a/drivers/net/ethernet/8390/ne.c b/drivers/net/ethernet/8390/ne.c
index 66f47987e2a2..ac99d089ac72 100644
--- a/drivers/net/ethernet/8390/ne.c
+++ b/drivers/net/ethernet/8390/ne.c
@@ -77,7 +77,7 @@ static u32 ne_msg_enable;
module_param_hw_array(io, int, ioport, NULL, 0);
module_param_hw_array(irq, int, irq, NULL, 0);
module_param_array(bad, int, NULL, 0);
-module_param_named(msg_enable, ne_msg_enable, uint, (S_IRUSR|S_IRGRP|S_IROTH));
+module_param_named(msg_enable, ne_msg_enable, uint, 0444);
MODULE_PARM_DESC(io, "I/O base address(es),required");
MODULE_PARM_DESC(irq, "IRQ number(s)");
MODULE_PARM_DESC(bad, "Accept card(s) with bad signatures");
@@ -99,7 +99,7 @@ MODULE_LICENSE("GPL");
that the ne2k probe is the last 8390 based probe to take place (as it
is at boot) and so the probe will get confused by any other 8390 cards.
ISA device autoprobes on a running machine are not recommended anyway. */
-#if !defined(MODULE) && (defined(CONFIG_ISA) || defined(CONFIG_M32R))
+#if !defined(MODULE) && defined(CONFIG_ISA)
/* Do we need a portlist for the ISA auto-probe ? */
#define NEEDS_PORTLIST
#endif
@@ -164,12 +164,7 @@ bad_clone_list[] __initdata = {
#define NESM_START_PG 0x40 /* First page of TX buffer */
#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
-#if defined(CONFIG_PLAT_MAPPI)
-# define DCR_VAL 0x4b
-#elif defined(CONFIG_PLAT_OAKS32R) || \
- defined(CONFIG_MACH_TX49XX)
-# define DCR_VAL 0x48 /* 8-bit mode */
-#elif defined(CONFIG_ATARI) /* 8-bit mode on Atari, normal on Q40 */
+#if defined(CONFIG_ATARI) /* 8-bit mode on Atari, normal on Q40 */
# define DCR_VAL (MACH_IS_ATARI ? 0x48 : 0x49)
#else
# define DCR_VAL 0x49
@@ -422,12 +417,7 @@ static int __init ne_probe1(struct net_device *dev, unsigned long ioaddr)
stop_page = NE1SM_STOP_PG;
}
-#if defined(CONFIG_PLAT_MAPPI) || defined(CONFIG_PLAT_OAKS32R)
- neX000 = ((SA_prom[14] == 0x57 && SA_prom[15] == 0x57)
- || (SA_prom[14] == 0x42 && SA_prom[15] == 0x42));
-#else
neX000 = (SA_prom[14] == 0x57 && SA_prom[15] == 0x57);
-#endif
ctron = (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d);
copam = (SA_prom[14] == 0x49 && SA_prom[15] == 0x00);
@@ -485,7 +475,7 @@ static int __init ne_probe1(struct net_device *dev, unsigned long ioaddr)
mdelay(10); /* wait 10ms for interrupt to propagate */
outb_p(0x00, ioaddr + EN0_IMR); /* Mask it again. */
dev->irq = probe_irq_off(cookie);
- if (netif_msg_probe(ei_local))
+ if (ne_msg_enable & NETIF_MSG_PROBE)
pr_cont(" autoirq is %d", dev->irq);
} else if (dev->irq == 2)
/* Fixup for users that don't know that IRQ 2 is really IRQ 9,
@@ -508,18 +498,9 @@ static int __init ne_probe1(struct net_device *dev, unsigned long ioaddr)
dev->base_addr = ioaddr;
-#ifdef CONFIG_PLAT_MAPPI
- outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP,
- ioaddr + E8390_CMD); /* 0x61 */
- for (i = 0; i < ETH_ALEN; i++) {
- dev->dev_addr[i] = SA_prom[i]
- = inb_p(ioaddr + EN1_PHYS_SHIFT(i));
- }
-#else
for (i = 0; i < ETH_ALEN; i++) {
dev->dev_addr[i] = SA_prom[i];
}
-#endif
pr_cont("%pM\n", dev->dev_addr);
diff --git a/drivers/net/ethernet/8390/ne2k-pci.c b/drivers/net/ethernet/8390/ne2k-pci.c
index 1bdea746926c..42985a82321a 100644
--- a/drivers/net/ethernet/8390/ne2k-pci.c
+++ b/drivers/net/ethernet/8390/ne2k-pci.c
@@ -76,7 +76,7 @@ MODULE_AUTHOR("Donald Becker / Paul Gortmaker");
MODULE_DESCRIPTION("PCI NE2000 clone driver");
MODULE_LICENSE("GPL");
-module_param_named(msg_enable, ne2k_msg_enable, uint, (S_IRUSR|S_IRGRP|S_IROTH));
+module_param_named(msg_enable, ne2k_msg_enable, uint, 0444);
module_param_array(options, int, NULL, 0);
module_param_array(full_duplex, int, NULL, 0);
MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)");
diff --git a/drivers/net/ethernet/8390/pcnet_cs.c b/drivers/net/ethernet/8390/pcnet_cs.c
index bcad4a7fac9f..61e43802b9a5 100644
--- a/drivers/net/ethernet/8390/pcnet_cs.c
+++ b/drivers/net/ethernet/8390/pcnet_cs.c
@@ -66,7 +66,6 @@
#define PCNET_RDC_TIMEOUT (2*HZ/100) /* Max wait in jiffies for Tx RDC */
static const char *if_names[] = { "auto", "10baseT", "10base2"};
-static u32 pcnet_msg_enable;
/*====================================================================*/
@@ -556,7 +555,6 @@ static int pcnet_config(struct pcmcia_device *link)
int start_pg, stop_pg, cm_offset;
int has_shmem = 0;
struct hw_info *local_hw_info;
- struct ei_device *ei_local;
dev_dbg(&link->dev, "pcnet_config\n");
@@ -606,8 +604,6 @@ static int pcnet_config(struct pcmcia_device *link)
mii_phy_probe(dev);
SET_NETDEV_DEV(dev, &link->dev);
- ei_local = netdev_priv(dev);
- ei_local->msg_enable = pcnet_msg_enable;
if (register_netdev(dev) != 0) {
pr_notice("register_netdev() failed\n");
diff --git a/drivers/net/ethernet/8390/smc-ultra.c b/drivers/net/ethernet/8390/smc-ultra.c
index 4e02f6a23575..3fe3b4dfa7c5 100644
--- a/drivers/net/ethernet/8390/smc-ultra.c
+++ b/drivers/net/ethernet/8390/smc-ultra.c
@@ -563,7 +563,7 @@ static int irq[MAX_ULTRA_CARDS];
module_param_hw_array(io, int, ioport, NULL, 0);
module_param_hw_array(irq, int, irq, NULL, 0);
-module_param_named(msg_enable, ultra_msg_enable, uint, (S_IRUSR|S_IRGRP|S_IROTH));
+module_param_named(msg_enable, ultra_msg_enable, uint, 0444);
MODULE_PARM_DESC(io, "I/O base address(es)");
MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)");
diff --git a/drivers/net/ethernet/8390/stnic.c b/drivers/net/ethernet/8390/stnic.c
index aca957d4e121..1f0670cd3ea3 100644
--- a/drivers/net/ethernet/8390/stnic.c
+++ b/drivers/net/ethernet/8390/stnic.c
@@ -71,7 +71,7 @@ static void stnic_init (struct net_device *dev);
static u32 stnic_msg_enable;
-module_param_named(msg_enable, stnic_msg_enable, uint, (S_IRUSR|S_IRGRP|S_IROTH));
+module_param_named(msg_enable, stnic_msg_enable, uint, 0444);
MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)");
/* SH7750 specific read/write io. */
diff --git a/drivers/net/ethernet/8390/wd.c b/drivers/net/ethernet/8390/wd.c
index 6efa2722f850..c834123560f1 100644
--- a/drivers/net/ethernet/8390/wd.c
+++ b/drivers/net/ethernet/8390/wd.c
@@ -299,7 +299,7 @@ static int __init wd_probe1(struct net_device *dev, int ioaddr)
outb_p(0x00, nic_addr+EN0_IMR); /* Mask all intrs. again. */
- if (netif_msg_drv(ei_local))
+ if (wd_msg_enable & NETIF_MSG_PROBE)
pr_cont(" autoirq is %d", dev->irq);
if (dev->irq < 2)
dev->irq = word16 ? 10 : 5;
@@ -507,7 +507,7 @@ module_param_hw_array(io, int, ioport, NULL, 0);
module_param_hw_array(irq, int, irq, NULL, 0);
module_param_hw_array(mem, int, iomem, NULL, 0);
module_param_hw_array(mem_end, int, iomem, NULL, 0);
-module_param_named(msg_enable, wd_msg_enable, uint, (S_IRUSR|S_IRGRP|S_IROTH));
+module_param_named(msg_enable, wd_msg_enable, uint, 0444);
MODULE_PARM_DESC(io, "I/O base address(es)");
MODULE_PARM_DESC(irq, "IRQ number(s) (ignored for PureData boards)");
MODULE_PARM_DESC(mem, "memory base address(es)(ignored for PureData boards)");
diff --git a/drivers/net/ethernet/8390/zorro8390.c b/drivers/net/ethernet/8390/zorro8390.c
index 6d93956b293b..35a500a21521 100644
--- a/drivers/net/ethernet/8390/zorro8390.c
+++ b/drivers/net/ethernet/8390/zorro8390.c
@@ -44,8 +44,6 @@
static const char version[] =
"8390.c:v1.10cvs 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
-static u32 zorro8390_msg_enable;
-
#include "lib8390.c"
#define DRV_NAME "zorro8390"
@@ -296,7 +294,6 @@ static int zorro8390_init(struct net_device *dev, unsigned long board,
int err;
unsigned char SA_prom[32];
int start_page, stop_page;
- struct ei_device *ei_local = netdev_priv(dev);
static u32 zorro8390_offsets[16] = {
0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e,
0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
@@ -388,8 +385,6 @@ static int zorro8390_init(struct net_device *dev, unsigned long board,
dev->netdev_ops = &zorro8390_netdev_ops;
__NS8390_init(dev, 0);
- ei_local->msg_enable = zorro8390_msg_enable;
-
err = register_netdev(dev);
if (err) {
free_irq(IRQ_AMIGA_PORTS, dev);
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index b6cf4b6962f5..603a5704dab8 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -34,7 +34,6 @@ source "drivers/net/ethernet/arc/Kconfig"
source "drivers/net/ethernet/atheros/Kconfig"
source "drivers/net/ethernet/aurora/Kconfig"
source "drivers/net/ethernet/cadence/Kconfig"
-source "drivers/net/ethernet/adi/Kconfig"
source "drivers/net/ethernet/broadcom/Kconfig"
source "drivers/net/ethernet/brocade/Kconfig"
source "drivers/net/ethernet/calxeda/Kconfig"
@@ -129,6 +128,7 @@ config FEALNX
source "drivers/net/ethernet/natsemi/Kconfig"
source "drivers/net/ethernet/netronome/Kconfig"
+source "drivers/net/ethernet/ni/Kconfig"
source "drivers/net/ethernet/8390/Kconfig"
config NET_NETX
@@ -176,7 +176,6 @@ source "drivers/net/ethernet/stmicro/Kconfig"
source "drivers/net/ethernet/sun/Kconfig"
source "drivers/net/ethernet/tehuti/Kconfig"
source "drivers/net/ethernet/ti/Kconfig"
-source "drivers/net/ethernet/tile/Kconfig"
source "drivers/net/ethernet/toshiba/Kconfig"
source "drivers/net/ethernet/tundra/Kconfig"
source "drivers/net/ethernet/via/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index 3cdf01e96e0b..2bfd2eea50bf 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -21,7 +21,6 @@ obj-$(CONFIG_NET_VENDOR_ARC) += arc/
obj-$(CONFIG_NET_VENDOR_ATHEROS) += atheros/
obj-$(CONFIG_NET_VENDOR_AURORA) += aurora/
obj-$(CONFIG_NET_CADENCE) += cadence/
-obj-$(CONFIG_NET_BFIN) += adi/
obj-$(CONFIG_NET_VENDOR_BROADCOM) += broadcom/
obj-$(CONFIG_NET_VENDOR_BROCADE) += brocade/
obj-$(CONFIG_NET_CALXEDA_XGMAC) += calxeda/
@@ -61,6 +60,7 @@ obj-$(CONFIG_NET_VENDOR_MYRI) += myricom/
obj-$(CONFIG_FEALNX) += fealnx.o
obj-$(CONFIG_NET_VENDOR_NATSEMI) += natsemi/
obj-$(CONFIG_NET_VENDOR_NETRONOME) += netronome/
+obj-$(CONFIG_NET_VENDOR_NI) += ni/
obj-$(CONFIG_NET_NETX) += netx-eth.o
obj-$(CONFIG_NET_VENDOR_NUVOTON) += nuvoton/
obj-$(CONFIG_NET_VENDOR_NVIDIA) += nvidia/
@@ -88,7 +88,6 @@ obj-$(CONFIG_NET_VENDOR_STMICRO) += stmicro/
obj-$(CONFIG_NET_VENDOR_SUN) += sun/
obj-$(CONFIG_NET_VENDOR_TEHUTI) += tehuti/
obj-$(CONFIG_NET_VENDOR_TI) += ti/
-obj-$(CONFIG_TILE_NET) += tile/
obj-$(CONFIG_NET_VENDOR_TOSHIBA) += toshiba/
obj-$(CONFIG_NET_VENDOR_TUNDRA) += tundra/
obj-$(CONFIG_NET_VENDOR_VIA) += via/
diff --git a/drivers/net/ethernet/adi/Kconfig b/drivers/net/ethernet/adi/Kconfig
deleted file mode 100644
index 98cc8f535021..000000000000
--- a/drivers/net/ethernet/adi/Kconfig
+++ /dev/null
@@ -1,66 +0,0 @@
-#
-# Blackfin device configuration
-#
-
-config NET_BFIN
- bool "Blackfin devices"
- depends on BF516 || BF518 || BF526 || BF527 || BF536 || BF537
- ---help---
- If you have a network (Ethernet) card belonging to this class, say Y.
-
- If unsure, say Y.
-
- Note that the answer to this question doesn't directly affect the
- kernel: saying N will just cause the configurator to skip all
- the remaining Blackfin card questions. If you say Y, you will be
- asked for your specific card in the following questions.
-
-if NET_BFIN
-
-config BFIN_MAC
- tristate "Blackfin on-chip MAC support"
- depends on (BF516 || BF518 || BF526 || BF527 || BF536 || BF537)
- select CRC32
- select MII
- select PHYLIB
- select BFIN_MAC_USE_L1 if DMA_UNCACHED_NONE
- ---help---
- This is the driver for Blackfin on-chip mac device. Say Y if you want
- it compiled into the kernel. This driver is also available as a
- module ( = code which can be inserted in and removed from the running
- kernel whenever you want). The module will be called bfin_mac.
-
-config BFIN_MAC_USE_L1
- bool "Use L1 memory for rx/tx packets"
- depends on BFIN_MAC && (BF527 || BF537)
- default y
- ---help---
- To get maximum network performance, you should use L1 memory as rx/tx
- buffers. Say N here if you want to reserve L1 memory for other uses.
-
-config BFIN_TX_DESC_NUM
- int "Number of transmit buffer packets"
- depends on BFIN_MAC
- range 6 10 if BFIN_MAC_USE_L1
- range 10 100
- default "10"
- ---help---
- Set the number of buffer packets used in driver.
-
-config BFIN_RX_DESC_NUM
- int "Number of receive buffer packets"
- depends on BFIN_MAC
- range 20 64
- default "20"
- ---help---
- Set the number of buffer packets used in driver.
-
-config BFIN_MAC_USE_HWSTAMP
- bool "Use IEEE 1588 hwstamp"
- depends on BFIN_MAC && BF518
- imply PTP_1588_CLOCK
- default y
- ---help---
- To support the IEEE 1588 Precision Time Protocol (PTP), select y here
-
-endif # NET_BFIN
diff --git a/drivers/net/ethernet/adi/Makefile b/drivers/net/ethernet/adi/Makefile
deleted file mode 100644
index b1fbe195d0e8..000000000000
--- a/drivers/net/ethernet/adi/Makefile
+++ /dev/null
@@ -1,5 +0,0 @@
-#
-# Makefile for the Blackfin device drivers.
-#
-
-obj-$(CONFIG_BFIN_MAC) += bfin_mac.o
diff --git a/drivers/net/ethernet/adi/bfin_mac.c b/drivers/net/ethernet/adi/bfin_mac.c
deleted file mode 100644
index 7120f2b9c6ef..000000000000
--- a/drivers/net/ethernet/adi/bfin_mac.c
+++ /dev/null
@@ -1,1881 +0,0 @@
-/*
- * Blackfin On-Chip MAC Driver
- *
- * Copyright 2004-2010 Analog Devices Inc.
- *
- * Enter bugs at http://blackfin.uclinux.org/
- *
- * Licensed under the GPL-2 or later.
- */
-
-#define DRV_VERSION "1.1"
-#define DRV_DESC "Blackfin on-chip Ethernet MAC driver"
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/timer.h>
-#include <linux/errno.h>
-#include <linux/irq.h>
-#include <linux/io.h>
-#include <linux/ioport.h>
-#include <linux/crc32.h>
-#include <linux/device.h>
-#include <linux/spinlock.h>
-#include <linux/mii.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/ethtool.h>
-#include <linux/skbuff.h>
-#include <linux/platform_device.h>
-
-#include <asm/dma.h>
-#include <linux/dma-mapping.h>
-
-#include <asm/div64.h>
-#include <asm/dpmc.h>
-#include <asm/blackfin.h>
-#include <asm/cacheflush.h>
-#include <asm/portmux.h>
-#include <mach/pll.h>
-
-#include "bfin_mac.h"
-
-MODULE_AUTHOR("Bryan Wu, Luke Yang");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION(DRV_DESC);
-MODULE_ALIAS("platform:bfin_mac");
-
-#if defined(CONFIG_BFIN_MAC_USE_L1)
-# define bfin_mac_alloc(dma_handle, size, num) l1_data_sram_zalloc(size*num)
-# define bfin_mac_free(dma_handle, ptr, num) l1_data_sram_free(ptr)
-#else
-# define bfin_mac_alloc(dma_handle, size, num) \
- dma_alloc_coherent(NULL, size*num, dma_handle, GFP_KERNEL)
-# define bfin_mac_free(dma_handle, ptr, num) \
- dma_free_coherent(NULL, sizeof(*ptr)*num, ptr, dma_handle)
-#endif
-
-#define PKT_BUF_SZ 1580
-
-#define MAX_TIMEOUT_CNT 500
-
-/* pointers to maintain transmit list */
-static struct net_dma_desc_tx *tx_list_head;
-static struct net_dma_desc_tx *tx_list_tail;
-static struct net_dma_desc_rx *rx_list_head;
-static struct net_dma_desc_rx *rx_list_tail;
-static struct net_dma_desc_rx *current_rx_ptr;
-static struct net_dma_desc_tx *current_tx_ptr;
-static struct net_dma_desc_tx *tx_desc;
-static struct net_dma_desc_rx *rx_desc;
-
-static void desc_list_free(void)
-{
- struct net_dma_desc_rx *r;
- struct net_dma_desc_tx *t;
- int i;
-#if !defined(CONFIG_BFIN_MAC_USE_L1)
- dma_addr_t dma_handle = 0;
-#endif
-
- if (tx_desc) {
- t = tx_list_head;
- for (i = 0; i < CONFIG_BFIN_TX_DESC_NUM; i++) {
- if (t) {
- if (t->skb) {
- dev_kfree_skb(t->skb);
- t->skb = NULL;
- }
- t = t->next;
- }
- }
- bfin_mac_free(dma_handle, tx_desc, CONFIG_BFIN_TX_DESC_NUM);
- }
-
- if (rx_desc) {
- r = rx_list_head;
- for (i = 0; i < CONFIG_BFIN_RX_DESC_NUM; i++) {
- if (r) {
- if (r->skb) {
- dev_kfree_skb(r->skb);
- r->skb = NULL;
- }
- r = r->next;
- }
- }
- bfin_mac_free(dma_handle, rx_desc, CONFIG_BFIN_RX_DESC_NUM);
- }
-}
-
-static int desc_list_init(struct net_device *dev)
-{
- int i;
- struct sk_buff *new_skb;
-#if !defined(CONFIG_BFIN_MAC_USE_L1)
- /*
- * This dma_handle is useless in Blackfin dma_alloc_coherent().
- * The real dma handler is the return value of dma_alloc_coherent().
- */
- dma_addr_t dma_handle;
-#endif
-
- tx_desc = bfin_mac_alloc(&dma_handle,
- sizeof(struct net_dma_desc_tx),
- CONFIG_BFIN_TX_DESC_NUM);
- if (tx_desc == NULL)
- goto init_error;
-
- rx_desc = bfin_mac_alloc(&dma_handle,
- sizeof(struct net_dma_desc_rx),
- CONFIG_BFIN_RX_DESC_NUM);
- if (rx_desc == NULL)
- goto init_error;
-
- /* init tx_list */
- tx_list_head = tx_list_tail = tx_desc;
-
- for (i = 0; i < CONFIG_BFIN_TX_DESC_NUM; i++) {
- struct net_dma_desc_tx *t = tx_desc + i;
- struct dma_descriptor *a = &(t->desc_a);
- struct dma_descriptor *b = &(t->desc_b);
-
- /*
- * disable DMA
- * read from memory WNR = 0
- * wordsize is 32 bits
- * 6 half words is desc size
- * large desc flow
- */
- a->config = WDSIZE_32 | NDSIZE_6 | DMAFLOW_LARGE;
- a->start_addr = (unsigned long)t->packet;
- a->x_count = 0;
- a->next_dma_desc = b;
-
- /*
- * enabled DMA
- * write to memory WNR = 1
- * wordsize is 32 bits
- * disable interrupt
- * 6 half words is desc size
- * large desc flow
- */
- b->config = DMAEN | WNR | WDSIZE_32 | NDSIZE_6 | DMAFLOW_LARGE;
- b->start_addr = (unsigned long)(&(t->status));
- b->x_count = 0;
-
- t->skb = NULL;
- tx_list_tail->desc_b.next_dma_desc = a;
- tx_list_tail->next = t;
- tx_list_tail = t;
- }
- tx_list_tail->next = tx_list_head; /* tx_list is a circle */
- tx_list_tail->desc_b.next_dma_desc = &(tx_list_head->desc_a);
- current_tx_ptr = tx_list_head;
-
- /* init rx_list */
- rx_list_head = rx_list_tail = rx_desc;
-
- for (i = 0; i < CONFIG_BFIN_RX_DESC_NUM; i++) {
- struct net_dma_desc_rx *r = rx_desc + i;
- struct dma_descriptor *a = &(r->desc_a);
- struct dma_descriptor *b = &(r->desc_b);
-
- /* allocate a new skb for next time receive */
- new_skb = netdev_alloc_skb(dev, PKT_BUF_SZ + NET_IP_ALIGN);
- if (!new_skb)
- goto init_error;
-
- skb_reserve(new_skb, NET_IP_ALIGN);
- /* Invalidate the data cache of skb->data range when it is write back
- * cache. It will prevent overwriting the new data from DMA
- */
- blackfin_dcache_invalidate_range((unsigned long)new_skb->head,
- (unsigned long)new_skb->end);
- r->skb = new_skb;
-
- /*
- * enabled DMA
- * write to memory WNR = 1
- * wordsize is 32 bits
- * disable interrupt
- * 6 half words is desc size
- * large desc flow
- */
- a->config = DMAEN | WNR | WDSIZE_32 | NDSIZE_6 | DMAFLOW_LARGE;
- /* since RXDWA is enabled */
- a->start_addr = (unsigned long)new_skb->data - 2;
- a->x_count = 0;
- a->next_dma_desc = b;
-
- /*
- * enabled DMA
- * write to memory WNR = 1
- * wordsize is 32 bits
- * enable interrupt
- * 6 half words is desc size
- * large desc flow
- */
- b->config = DMAEN | WNR | WDSIZE_32 | DI_EN |
- NDSIZE_6 | DMAFLOW_LARGE;
- b->start_addr = (unsigned long)(&(r->status));
- b->x_count = 0;
-
- rx_list_tail->desc_b.next_dma_desc = a;
- rx_list_tail->next = r;
- rx_list_tail = r;
- }
- rx_list_tail->next = rx_list_head; /* rx_list is a circle */
- rx_list_tail->desc_b.next_dma_desc = &(rx_list_head->desc_a);
- current_rx_ptr = rx_list_head;
-
- return 0;
-
-init_error:
- desc_list_free();
- pr_err("kmalloc failed\n");
- return -ENOMEM;
-}
-
-
-/*---PHY CONTROL AND CONFIGURATION-----------------------------------------*/
-
-/*
- * MII operations
- */
-/* Wait until the previous MDC/MDIO transaction has completed */
-static int bfin_mdio_poll(void)
-{
- int timeout_cnt = MAX_TIMEOUT_CNT;
-
- /* poll the STABUSY bit */
- while ((bfin_read_EMAC_STAADD()) & STABUSY) {
- udelay(1);
- if (timeout_cnt-- < 0) {
- pr_err("wait MDC/MDIO transaction to complete timeout\n");
- return -ETIMEDOUT;
- }
- }
-
- return 0;
-}
-
-/* Read an off-chip register in a PHY through the MDC/MDIO port */
-static int bfin_mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum)
-{
- int ret;
-
- ret = bfin_mdio_poll();
- if (ret)
- return ret;
-
- /* read mode */
- bfin_write_EMAC_STAADD(SET_PHYAD((u16) phy_addr) |
- SET_REGAD((u16) regnum) |
- STABUSY);
-
- ret = bfin_mdio_poll();
- if (ret)
- return ret;
-
- return (int) bfin_read_EMAC_STADAT();
-}
-
-/* Write an off-chip register in a PHY through the MDC/MDIO port */
-static int bfin_mdiobus_write(struct mii_bus *bus, int phy_addr, int regnum,
- u16 value)
-{
- int ret;
-
- ret = bfin_mdio_poll();
- if (ret)
- return ret;
-
- bfin_write_EMAC_STADAT((u32) value);
-
- /* write mode */
- bfin_write_EMAC_STAADD(SET_PHYAD((u16) phy_addr) |
- SET_REGAD((u16) regnum) |
- STAOP |
- STABUSY);
-
- return bfin_mdio_poll();
-}
-
-static void bfin_mac_adjust_link(struct net_device *dev)
-{
- struct bfin_mac_local *lp = netdev_priv(dev);
- struct phy_device *phydev = dev->phydev;
- unsigned long flags;
- int new_state = 0;
-
- spin_lock_irqsave(&lp->lock, flags);
- if (phydev->link) {
- /* Now we make sure that we can be in full duplex mode.
- * If not, we operate in half-duplex mode. */
- if (phydev->duplex != lp->old_duplex) {
- u32 opmode = bfin_read_EMAC_OPMODE();
- new_state = 1;
-
- if (phydev->duplex)
- opmode |= FDMODE;
- else
- opmode &= ~(FDMODE);
-
- bfin_write_EMAC_OPMODE(opmode);
- lp->old_duplex = phydev->duplex;
- }
-
- if (phydev->speed != lp->old_speed) {
- if (phydev->interface == PHY_INTERFACE_MODE_RMII) {
- u32 opmode = bfin_read_EMAC_OPMODE();
- switch (phydev->speed) {
- case 10:
- opmode |= RMII_10;
- break;
- case 100:
- opmode &= ~RMII_10;
- break;
- default:
- netdev_warn(dev,
- "Ack! Speed (%d) is not 10/100!\n",
- phydev->speed);
- break;
- }
- bfin_write_EMAC_OPMODE(opmode);
- }
-
- new_state = 1;
- lp->old_speed = phydev->speed;
- }
-
- if (!lp->old_link) {
- new_state = 1;
- lp->old_link = 1;
- }
- } else if (lp->old_link) {
- new_state = 1;
- lp->old_link = 0;
- lp->old_speed = 0;
- lp->old_duplex = -1;
- }
-
- if (new_state) {
- u32 opmode = bfin_read_EMAC_OPMODE();
- phy_print_status(phydev);
- pr_debug("EMAC_OPMODE = 0x%08x\n", opmode);
- }
-
- spin_unlock_irqrestore(&lp->lock, flags);
-}
-
-/* MDC = 2.5 MHz */
-#define MDC_CLK 2500000
-
-static int mii_probe(struct net_device *dev, int phy_mode)
-{
- struct bfin_mac_local *lp = netdev_priv(dev);
- struct phy_device *phydev;
- unsigned short sysctl;
- u32 sclk, mdc_div;
-
- /* Enable PHY output early */
- if (!(bfin_read_VR_CTL() & CLKBUFOE))
- bfin_write_VR_CTL(bfin_read_VR_CTL() | CLKBUFOE);
-
- sclk = get_sclk();
- mdc_div = ((sclk / MDC_CLK) / 2) - 1;
-
- sysctl = bfin_read_EMAC_SYSCTL();
- sysctl = (sysctl & ~MDCDIV) | SET_MDCDIV(mdc_div);
- bfin_write_EMAC_SYSCTL(sysctl);
-
- phydev = phy_find_first(lp->mii_bus);
- if (!phydev) {
- netdev_err(dev, "no phy device found\n");
- return -ENODEV;
- }
-
- if (phy_mode != PHY_INTERFACE_MODE_RMII &&
- phy_mode != PHY_INTERFACE_MODE_MII) {
- netdev_err(dev, "invalid phy interface mode\n");
- return -EINVAL;
- }
-
- phydev = phy_connect(dev, phydev_name(phydev),
- &bfin_mac_adjust_link, phy_mode);
-
- if (IS_ERR(phydev)) {
- netdev_err(dev, "could not attach PHY\n");
- return PTR_ERR(phydev);
- }
-
- /* mask with MAC supported features */
- phydev->supported &= (SUPPORTED_10baseT_Half
- | SUPPORTED_10baseT_Full
- | SUPPORTED_100baseT_Half
- | SUPPORTED_100baseT_Full
- | SUPPORTED_Autoneg
- | SUPPORTED_Pause | SUPPORTED_Asym_Pause
- | SUPPORTED_MII
- | SUPPORTED_TP);
-
- phydev->advertising = phydev->supported;
-
- lp->old_link = 0;
- lp->old_speed = 0;
- lp->old_duplex = -1;
-
- phy_attached_print(phydev, "mdc_clk=%dHz(mdc_div=%d)@sclk=%dMHz)\n",
- MDC_CLK, mdc_div, sclk / 1000000);
-
- return 0;
-}
-
-/*
- * Ethtool support
- */
-
-/*
- * interrupt routine for magic packet wakeup
- */
-static irqreturn_t bfin_mac_wake_interrupt(int irq, void *dev_id)
-{
- return IRQ_HANDLED;
-}
-
-static void bfin_mac_ethtool_getdrvinfo(struct net_device *dev,
- struct ethtool_drvinfo *info)
-{
- strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
- strlcpy(info->version, DRV_VERSION, sizeof(info->version));
- strlcpy(info->fw_version, "N/A", sizeof(info->fw_version));
- strlcpy(info->bus_info, dev_name(&dev->dev), sizeof(info->bus_info));
-}
-
-static void bfin_mac_ethtool_getwol(struct net_device *dev,
- struct ethtool_wolinfo *wolinfo)
-{
- struct bfin_mac_local *lp = netdev_priv(dev);
-
- wolinfo->supported = WAKE_MAGIC;
- wolinfo->wolopts = lp->wol;
-}
-
-static int bfin_mac_ethtool_setwol(struct net_device *dev,
- struct ethtool_wolinfo *wolinfo)
-{
- struct bfin_mac_local *lp = netdev_priv(dev);
- int rc;
-
- if (wolinfo->wolopts & (WAKE_MAGICSECURE |
- WAKE_UCAST |
- WAKE_MCAST |
- WAKE_BCAST |
- WAKE_ARP))
- return -EOPNOTSUPP;
-
- lp->wol = wolinfo->wolopts;
-
- if (lp->wol && !lp->irq_wake_requested) {
- /* register wake irq handler */
- rc = request_irq(IRQ_MAC_WAKEDET, bfin_mac_wake_interrupt,
- 0, "EMAC_WAKE", dev);
- if (rc)
- return rc;
- lp->irq_wake_requested = true;
- }
-
- if (!lp->wol && lp->irq_wake_requested) {
- free_irq(IRQ_MAC_WAKEDET, dev);
- lp->irq_wake_requested = false;
- }
-
- /* Make sure the PHY driver doesn't suspend */
- device_init_wakeup(&dev->dev, lp->wol);
-
- return 0;
-}
-
-#ifdef CONFIG_BFIN_MAC_USE_HWSTAMP
-static int bfin_mac_ethtool_get_ts_info(struct net_device *dev,
- struct ethtool_ts_info *info)
-{
- struct bfin_mac_local *lp = netdev_priv(dev);
-
- info->so_timestamping =
- SOF_TIMESTAMPING_TX_HARDWARE |
- SOF_TIMESTAMPING_RX_HARDWARE |
- SOF_TIMESTAMPING_RAW_HARDWARE;
- info->phc_index = lp->phc_index;
- info->tx_types =
- (1 << HWTSTAMP_TX_OFF) |
- (1 << HWTSTAMP_TX_ON);
- info->rx_filters =
- (1 << HWTSTAMP_FILTER_NONE) |
- (1 << HWTSTAMP_FILTER_PTP_V1_L4_EVENT) |
- (1 << HWTSTAMP_FILTER_PTP_V2_L2_EVENT) |
- (1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT);
- return 0;
-}
-#endif
-
-static const struct ethtool_ops bfin_mac_ethtool_ops = {
- .get_link = ethtool_op_get_link,
- .get_drvinfo = bfin_mac_ethtool_getdrvinfo,
- .get_wol = bfin_mac_ethtool_getwol,
- .set_wol = bfin_mac_ethtool_setwol,
-#ifdef CONFIG_BFIN_MAC_USE_HWSTAMP
- .get_ts_info = bfin_mac_ethtool_get_ts_info,
-#endif
- .get_link_ksettings = phy_ethtool_get_link_ksettings,
- .set_link_ksettings = phy_ethtool_set_link_ksettings,
-};
-
-/**************************************************************************/
-static void setup_system_regs(struct net_device *dev)
-{
- struct bfin_mac_local *lp = netdev_priv(dev);
- int i;
- unsigned short sysctl;
-
- /*
- * Odd word alignment for Receive Frame DMA word
- * Configure checksum support and rcve frame word alignment
- */
- sysctl = bfin_read_EMAC_SYSCTL();
- /*
- * check if interrupt is requested for any PHY,
- * enable PHY interrupt only if needed
- */
- for (i = 0; i < PHY_MAX_ADDR; ++i)
- if (lp->mii_bus->irq[i] != PHY_POLL)
- break;
- if (i < PHY_MAX_ADDR)
- sysctl |= PHYIE;
- sysctl |= RXDWA;
-#if defined(BFIN_MAC_CSUM_OFFLOAD)
- sysctl |= RXCKS;
-#else
- sysctl &= ~RXCKS;
-#endif
- bfin_write_EMAC_SYSCTL(sysctl);
-
- bfin_write_EMAC_MMC_CTL(RSTC | CROLL);
-
- /* Set vlan regs to let 1522 bytes long packets pass through */
- bfin_write_EMAC_VLAN1(lp->vlan1_mask);
- bfin_write_EMAC_VLAN2(lp->vlan2_mask);
-
- /* Initialize the TX DMA channel registers */
- bfin_write_DMA2_X_COUNT(0);
- bfin_write_DMA2_X_MODIFY(4);
- bfin_write_DMA2_Y_COUNT(0);
- bfin_write_DMA2_Y_MODIFY(0);
-
- /* Initialize the RX DMA channel registers */
- bfin_write_DMA1_X_COUNT(0);
- bfin_write_DMA1_X_MODIFY(4);
- bfin_write_DMA1_Y_COUNT(0);
- bfin_write_DMA1_Y_MODIFY(0);
-}
-
-static void setup_mac_addr(u8 *mac_addr)
-{
- u32 addr_low = le32_to_cpu(*(__le32 *) & mac_addr[0]);
- u16 addr_hi = le16_to_cpu(*(__le16 *) & mac_addr[4]);
-
- /* this depends on a little-endian machine */
- bfin_write_EMAC_ADDRLO(addr_low);
- bfin_write_EMAC_ADDRHI(addr_hi);
-}
-
-static int bfin_mac_set_mac_address(struct net_device *dev, void *p)
-{
- struct sockaddr *addr = p;
- if (netif_running(dev))
- return -EBUSY;
- memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
- setup_mac_addr(dev->dev_addr);
- return 0;
-}
-
-#ifdef CONFIG_BFIN_MAC_USE_HWSTAMP
-#define bfin_mac_hwtstamp_is_none(cfg) ((cfg) == HWTSTAMP_FILTER_NONE)
-
-static u32 bfin_select_phc_clock(u32 input_clk, unsigned int *shift_result)
-{
- u32 ipn = 1000000000UL / input_clk;
- u32 ppn = 1;
- unsigned int shift = 0;
-
- while (ppn <= ipn) {
- ppn <<= 1;
- shift++;
- }
- *shift_result = shift;
- return 1000000000UL / ppn;
-}
-
-static int bfin_mac_hwtstamp_set(struct net_device *netdev,
- struct ifreq *ifr)
-{
- struct hwtstamp_config config;
- struct bfin_mac_local *lp = netdev_priv(netdev);
- u16 ptpctl;
- u32 ptpfv1, ptpfv2, ptpfv3, ptpfoff;
-
- if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
- return -EFAULT;
-
- pr_debug("%s config flag:0x%x, tx_type:0x%x, rx_filter:0x%x\n",
- __func__, config.flags, config.tx_type, config.rx_filter);
-
- /* reserved for future extensions */
- if (config.flags)
- return -EINVAL;
-
- if ((config.tx_type != HWTSTAMP_TX_OFF) &&
- (config.tx_type != HWTSTAMP_TX_ON))
- return -ERANGE;
-
- ptpctl = bfin_read_EMAC_PTP_CTL();
-
- switch (config.rx_filter) {
- case HWTSTAMP_FILTER_NONE:
- /*
- * Dont allow any timestamping
- */
- ptpfv3 = 0xFFFFFFFF;
- bfin_write_EMAC_PTP_FV3(ptpfv3);
- break;
- case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
- case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
- case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
- /*
- * Clear the five comparison mask bits (bits[12:8]) in EMAC_PTP_CTL)
- * to enable all the field matches.
- */
- ptpctl &= ~0x1F00;
- bfin_write_EMAC_PTP_CTL(ptpctl);
- /*
- * Keep the default values of the EMAC_PTP_FOFF register.
- */
- ptpfoff = 0x4A24170C;
- bfin_write_EMAC_PTP_FOFF(ptpfoff);
- /*
- * Keep the default values of the EMAC_PTP_FV1 and EMAC_PTP_FV2
- * registers.
- */
- ptpfv1 = 0x11040800;
- bfin_write_EMAC_PTP_FV1(ptpfv1);
- ptpfv2 = 0x0140013F;
- bfin_write_EMAC_PTP_FV2(ptpfv2);
- /*
- * The default value (0xFFFC) allows the timestamping of both
- * received Sync messages and Delay_Req messages.
- */
- ptpfv3 = 0xFFFFFFFC;
- bfin_write_EMAC_PTP_FV3(ptpfv3);
-
- config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
- break;
- case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
- case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
- case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
- /* Clear all five comparison mask bits (bits[12:8]) in the
- * EMAC_PTP_CTL register to enable all the field matches.
- */
- ptpctl &= ~0x1F00;
- bfin_write_EMAC_PTP_CTL(ptpctl);
- /*
- * Keep the default values of the EMAC_PTP_FOFF register, except set
- * the PTPCOF field to 0x2A.
- */
- ptpfoff = 0x2A24170C;
- bfin_write_EMAC_PTP_FOFF(ptpfoff);
- /*
- * Keep the default values of the EMAC_PTP_FV1 and EMAC_PTP_FV2
- * registers.
- */
- ptpfv1 = 0x11040800;
- bfin_write_EMAC_PTP_FV1(ptpfv1);
- ptpfv2 = 0x0140013F;
- bfin_write_EMAC_PTP_FV2(ptpfv2);
- /*
- * To allow the timestamping of Pdelay_Req and Pdelay_Resp, set
- * the value to 0xFFF0.
- */
- ptpfv3 = 0xFFFFFFF0;
- bfin_write_EMAC_PTP_FV3(ptpfv3);
-
- config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
- break;
- case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
- case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
- case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
- /*
- * Clear bits 8 and 12 of the EMAC_PTP_CTL register to enable only the
- * EFTM and PTPCM field comparison.
- */
- ptpctl &= ~0x1100;
- bfin_write_EMAC_PTP_CTL(ptpctl);
- /*
- * Keep the default values of all the fields of the EMAC_PTP_FOFF
- * register, except set the PTPCOF field to 0x0E.
- */
- ptpfoff = 0x0E24170C;
- bfin_write_EMAC_PTP_FOFF(ptpfoff);
- /*
- * Program bits [15:0] of the EMAC_PTP_FV1 register to 0x88F7, which
- * corresponds to PTP messages on the MAC layer.
- */
- ptpfv1 = 0x110488F7;
- bfin_write_EMAC_PTP_FV1(ptpfv1);
- ptpfv2 = 0x0140013F;
- bfin_write_EMAC_PTP_FV2(ptpfv2);
- /*
- * To allow the timestamping of Pdelay_Req and Pdelay_Resp
- * messages, set the value to 0xFFF0.
- */
- ptpfv3 = 0xFFFFFFF0;
- bfin_write_EMAC_PTP_FV3(ptpfv3);
-
- config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
- break;
- default:
- return -ERANGE;
- }
-
- if (config.tx_type == HWTSTAMP_TX_OFF &&
- bfin_mac_hwtstamp_is_none(config.rx_filter)) {
- ptpctl &= ~PTP_EN;
- bfin_write_EMAC_PTP_CTL(ptpctl);
-
- SSYNC();
- } else {
- ptpctl |= PTP_EN;
- bfin_write_EMAC_PTP_CTL(ptpctl);
-
- /*
- * clear any existing timestamp
- */
- bfin_read_EMAC_PTP_RXSNAPLO();
- bfin_read_EMAC_PTP_RXSNAPHI();
-
- bfin_read_EMAC_PTP_TXSNAPLO();
- bfin_read_EMAC_PTP_TXSNAPHI();
-
- SSYNC();
- }
-
- lp->stamp_cfg = config;
- return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
- -EFAULT : 0;
-}
-
-static int bfin_mac_hwtstamp_get(struct net_device *netdev,
- struct ifreq *ifr)
-{
- struct bfin_mac_local *lp = netdev_priv(netdev);
-
- return copy_to_user(ifr->ifr_data, &lp->stamp_cfg,
- sizeof(lp->stamp_cfg)) ?
- -EFAULT : 0;
-}
-
-static void bfin_tx_hwtstamp(struct net_device *netdev, struct sk_buff *skb)
-{
- struct bfin_mac_local *lp = netdev_priv(netdev);
-
- if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) {
- int timeout_cnt = MAX_TIMEOUT_CNT;
-
- /* When doing time stamping, keep the connection to the socket
- * a while longer
- */
- skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
-
- /*
- * The timestamping is done at the EMAC module's MII/RMII interface
- * when the module sees the Start of Frame of an event message packet. This
- * interface is the closest possible place to the physical Ethernet transmission
- * medium, providing the best timing accuracy.
- */
- while ((!(bfin_read_EMAC_PTP_ISTAT() & TXTL)) && (--timeout_cnt))
- udelay(1);
- if (timeout_cnt == 0)
- netdev_err(netdev, "timestamp the TX packet failed\n");
- else {
- struct skb_shared_hwtstamps shhwtstamps;
- u64 ns;
- u64 regval;
-
- regval = bfin_read_EMAC_PTP_TXSNAPLO();
- regval |= (u64)bfin_read_EMAC_PTP_TXSNAPHI() << 32;
- memset(&shhwtstamps, 0, sizeof(shhwtstamps));
- ns = regval << lp->shift;
- shhwtstamps.hwtstamp = ns_to_ktime(ns);
- skb_tstamp_tx(skb, &shhwtstamps);
- }
- }
-}
-
-static void bfin_rx_hwtstamp(struct net_device *netdev, struct sk_buff *skb)
-{
- struct bfin_mac_local *lp = netdev_priv(netdev);
- u32 valid;
- u64 regval, ns;
- struct skb_shared_hwtstamps *shhwtstamps;
-
- if (bfin_mac_hwtstamp_is_none(lp->stamp_cfg.rx_filter))
- return;
-
- valid = bfin_read_EMAC_PTP_ISTAT() & RXEL;
- if (!valid)
- return;
-
- shhwtstamps = skb_hwtstamps(skb);
-
- regval = bfin_read_EMAC_PTP_RXSNAPLO();
- regval |= (u64)bfin_read_EMAC_PTP_RXSNAPHI() << 32;
- ns = regval << lp->shift;
- memset(shhwtstamps, 0, sizeof(*shhwtstamps));
- shhwtstamps->hwtstamp = ns_to_ktime(ns);
-}
-
-static void bfin_mac_hwtstamp_init(struct net_device *netdev)
-{
- struct bfin_mac_local *lp = netdev_priv(netdev);
- u64 addend, ppb;
- u32 input_clk, phc_clk;
-
- /* Initialize hardware timer */
- input_clk = get_sclk();
- phc_clk = bfin_select_phc_clock(input_clk, &lp->shift);
- addend = phc_clk * (1ULL << 32);
- do_div(addend, input_clk);
- bfin_write_EMAC_PTP_ADDEND((u32)addend);
-
- lp->addend = addend;
- ppb = 1000000000ULL * input_clk;
- do_div(ppb, phc_clk);
- lp->max_ppb = ppb - 1000000000ULL - 1ULL;
-
- /* Initialize hwstamp config */
- lp->stamp_cfg.rx_filter = HWTSTAMP_FILTER_NONE;
- lp->stamp_cfg.tx_type = HWTSTAMP_TX_OFF;
-}
-
-static u64 bfin_ptp_time_read(struct bfin_mac_local *lp)
-{
- u64 ns;
- u32 lo, hi;
-
- lo = bfin_read_EMAC_PTP_TIMELO();
- hi = bfin_read_EMAC_PTP_TIMEHI();
-
- ns = ((u64) hi) << 32;
- ns |= lo;
- ns <<= lp->shift;
-
- return ns;
-}
-
-static void bfin_ptp_time_write(struct bfin_mac_local *lp, u64 ns)
-{
- u32 hi, lo;
-
- ns >>= lp->shift;
- hi = ns >> 32;
- lo = ns & 0xffffffff;
-
- bfin_write_EMAC_PTP_TIMELO(lo);
- bfin_write_EMAC_PTP_TIMEHI(hi);
-}
-
-/* PTP Hardware Clock operations */
-
-static int bfin_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
-{
- u64 adj;
- u32 diff, addend;
- int neg_adj = 0;
- struct bfin_mac_local *lp =
- container_of(ptp, struct bfin_mac_local, caps);
-
- if (ppb < 0) {
- neg_adj = 1;
- ppb = -ppb;
- }
- addend = lp->addend;
- adj = addend;
- adj *= ppb;
- diff = div_u64(adj, 1000000000ULL);
-
- addend = neg_adj ? addend - diff : addend + diff;
-
- bfin_write_EMAC_PTP_ADDEND(addend);
-
- return 0;
-}
-
-static int bfin_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
-{
- s64 now;
- unsigned long flags;
- struct bfin_mac_local *lp =
- container_of(ptp, struct bfin_mac_local, caps);
-
- spin_lock_irqsave(&lp->phc_lock, flags);
-
- now = bfin_ptp_time_read(lp);
- now += delta;
- bfin_ptp_time_write(lp, now);
-
- spin_unlock_irqrestore(&lp->phc_lock, flags);
-
- return 0;
-}
-
-static int bfin_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
-{
- u64 ns;
- unsigned long flags;
- struct bfin_mac_local *lp =
- container_of(ptp, struct bfin_mac_local, caps);
-
- spin_lock_irqsave(&lp->phc_lock, flags);
-
- ns = bfin_ptp_time_read(lp);
-
- spin_unlock_irqrestore(&lp->phc_lock, flags);
-
- *ts = ns_to_timespec64(ns);
-
- return 0;
-}
-
-static int bfin_ptp_settime(struct ptp_clock_info *ptp,
- const struct timespec64 *ts)
-{
- u64 ns;
- unsigned long flags;
- struct bfin_mac_local *lp =
- container_of(ptp, struct bfin_mac_local, caps);
-
- ns = timespec64_to_ns(ts);
-
- spin_lock_irqsave(&lp->phc_lock, flags);
-
- bfin_ptp_time_write(lp, ns);
-
- spin_unlock_irqrestore(&lp->phc_lock, flags);
-
- return 0;
-}
-
-static int bfin_ptp_enable(struct ptp_clock_info *ptp,
- struct ptp_clock_request *rq, int on)
-{
- return -EOPNOTSUPP;
-}
-
-static const struct ptp_clock_info bfin_ptp_caps = {
- .owner = THIS_MODULE,
- .name = "BF518 clock",
- .max_adj = 0,
- .n_alarm = 0,
- .n_ext_ts = 0,
- .n_per_out = 0,
- .n_pins = 0,
- .pps = 0,
- .adjfreq = bfin_ptp_adjfreq,
- .adjtime = bfin_ptp_adjtime,
- .gettime64 = bfin_ptp_gettime,
- .settime64 = bfin_ptp_settime,
- .enable = bfin_ptp_enable,
-};
-
-static int bfin_phc_init(struct net_device *netdev, struct device *dev)
-{
- struct bfin_mac_local *lp = netdev_priv(netdev);
-
- lp->caps = bfin_ptp_caps;
- lp->caps.max_adj = lp->max_ppb;
- lp->clock = ptp_clock_register(&lp->caps, dev);
- if (IS_ERR(lp->clock))
- return PTR_ERR(lp->clock);
-
- lp->phc_index = ptp_clock_index(lp->clock);
- spin_lock_init(&lp->phc_lock);
-
- return 0;
-}
-
-static void bfin_phc_release(struct bfin_mac_local *lp)
-{
- ptp_clock_unregister(lp->clock);
-}
-
-#else
-# define bfin_mac_hwtstamp_is_none(cfg) 0
-# define bfin_mac_hwtstamp_init(dev)
-# define bfin_mac_hwtstamp_set(dev, ifr) (-EOPNOTSUPP)
-# define bfin_mac_hwtstamp_get(dev, ifr) (-EOPNOTSUPP)
-# define bfin_rx_hwtstamp(dev, skb)
-# define bfin_tx_hwtstamp(dev, skb)
-# define bfin_phc_init(netdev, dev) 0
-# define bfin_phc_release(lp)
-#endif
-
-static inline void _tx_reclaim_skb(void)
-{
- do {
- tx_list_head->desc_a.config &= ~DMAEN;
- tx_list_head->status.status_word = 0;
- if (tx_list_head->skb) {
- dev_consume_skb_any(tx_list_head->skb);
- tx_list_head->skb = NULL;
- }
- tx_list_head = tx_list_head->next;
-
- } while (tx_list_head->status.status_word != 0);
-}
-
-static void tx_reclaim_skb(struct bfin_mac_local *lp)
-{
- int timeout_cnt = MAX_TIMEOUT_CNT;
-
- if (tx_list_head->status.status_word != 0)
- _tx_reclaim_skb();
-
- if (current_tx_ptr->next == tx_list_head) {
- while (tx_list_head->status.status_word == 0) {
- /* slow down polling to avoid too many queue stop. */
- udelay(10);
- /* reclaim skb if DMA is not running. */
- if (!(bfin_read_DMA2_IRQ_STATUS() & DMA_RUN))
- break;
- if (timeout_cnt-- < 0)
- break;
- }
-
- if (timeout_cnt >= 0)
- _tx_reclaim_skb();
- else
- netif_stop_queue(lp->ndev);
- }
-
- if (current_tx_ptr->next != tx_list_head &&
- netif_queue_stopped(lp->ndev))
- netif_wake_queue(lp->ndev);
-
- if (tx_list_head != current_tx_ptr) {
- /* shorten the timer interval if tx queue is stopped */
- if (netif_queue_stopped(lp->ndev))
- lp->tx_reclaim_timer.expires =
- jiffies + (TX_RECLAIM_JIFFIES >> 4);
- else
- lp->tx_reclaim_timer.expires =
- jiffies + TX_RECLAIM_JIFFIES;
-
- mod_timer(&lp->tx_reclaim_timer,
- lp->tx_reclaim_timer.expires);
- }
-
- return;
-}
-
-static void tx_reclaim_skb_timeout(struct timer_list *t)
-{
- struct bfin_mac_local *lp = from_timer(lp, t, tx_reclaim_timer);
-
- tx_reclaim_skb(lp);
-}
-
-static int bfin_mac_hard_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
-{
- struct bfin_mac_local *lp = netdev_priv(dev);
- u16 *data;
- u32 data_align = (unsigned long)(skb->data) & 0x3;
-
- current_tx_ptr->skb = skb;
-
- if (data_align == 0x2) {
- /* move skb->data to current_tx_ptr payload */
- data = (u16 *)(skb->data) - 1;
- *data = (u16)(skb->len);
- /*
- * When transmitting an Ethernet packet, the PTP_TSYNC module requires
- * a DMA_Length_Word field associated with the packet. The lower 12 bits
- * of this field are the length of the packet payload in bytes and the higher
- * 4 bits are the timestamping enable field.
- */
- if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)
- *data |= 0x1000;
-
- current_tx_ptr->desc_a.start_addr = (u32)data;
- /* this is important! */
- blackfin_dcache_flush_range((u32)data,
- (u32)((u8 *)data + skb->len + 4));
- } else {
- *((u16 *)(current_tx_ptr->packet)) = (u16)(skb->len);
- /* enable timestamping for the sent packet */
- if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)
- *((u16 *)(current_tx_ptr->packet)) |= 0x1000;
- memcpy((u8 *)(current_tx_ptr->packet + 2), skb->data,
- skb->len);
- current_tx_ptr->desc_a.start_addr =
- (u32)current_tx_ptr->packet;
- blackfin_dcache_flush_range(
- (u32)current_tx_ptr->packet,
- (u32)(current_tx_ptr->packet + skb->len + 2));
- }
-
- /* make sure the internal data buffers in the core are drained
- * so that the DMA descriptors are completely written when the
- * DMA engine goes to fetch them below
- */
- SSYNC();
-
- /* always clear status buffer before start tx dma */
- current_tx_ptr->status.status_word = 0;
-
- /* enable this packet's dma */
- current_tx_ptr->desc_a.config |= DMAEN;
-
- /* tx dma is running, just return */
- if (bfin_read_DMA2_IRQ_STATUS() & DMA_RUN)
- goto out;
-
- /* tx dma is not running */
- bfin_write_DMA2_NEXT_DESC_PTR(&(current_tx_ptr->desc_a));
- /* dma enabled, read from memory, size is 6 */
- bfin_write_DMA2_CONFIG(current_tx_ptr->desc_a.config);
- /* Turn on the EMAC tx */
- bfin_write_EMAC_OPMODE(bfin_read_EMAC_OPMODE() | TE);
-
-out:
- bfin_tx_hwtstamp(dev, skb);
-
- current_tx_ptr = current_tx_ptr->next;
- dev->stats.tx_packets++;
- dev->stats.tx_bytes += (skb->len);
-
- tx_reclaim_skb(lp);
-
- return NETDEV_TX_OK;
-}
-
-#define IP_HEADER_OFF 0
-#define RX_ERROR_MASK (RX_LONG | RX_ALIGN | RX_CRC | RX_LEN | \
- RX_FRAG | RX_ADDR | RX_DMAO | RX_PHY | RX_LATE | RX_RANGE)
-
-static void bfin_mac_rx(struct bfin_mac_local *lp)
-{
- struct net_device *dev = lp->ndev;
- struct sk_buff *skb, *new_skb;
- unsigned short len;
-#if defined(BFIN_MAC_CSUM_OFFLOAD)
- unsigned int i;
- unsigned char fcs[ETH_FCS_LEN + 1];
-#endif
-
- /* check if frame status word reports an error condition
- * we which case we simply drop the packet
- */
- if (current_rx_ptr->status.status_word & RX_ERROR_MASK) {
- netdev_notice(dev, "rx: receive error - packet dropped\n");
- dev->stats.rx_dropped++;
- goto out;
- }
-
- /* allocate a new skb for next time receive */
- skb = current_rx_ptr->skb;
-
- new_skb = netdev_alloc_skb(dev, PKT_BUF_SZ + NET_IP_ALIGN);
- if (!new_skb) {
- dev->stats.rx_dropped++;
- goto out;
- }
- /* reserve 2 bytes for RXDWA padding */
- skb_reserve(new_skb, NET_IP_ALIGN);
- /* Invalidate the data cache of skb->data range when it is write back
- * cache. It will prevent overwriting the new data from DMA
- */
- blackfin_dcache_invalidate_range((unsigned long)new_skb->head,
- (unsigned long)new_skb->end);
-
- current_rx_ptr->skb = new_skb;
- current_rx_ptr->desc_a.start_addr = (unsigned long)new_skb->data - 2;
-
- len = (unsigned short)(current_rx_ptr->status.status_word & RX_FRLEN);
- /* Deduce Ethernet FCS length from Ethernet payload length */
- len -= ETH_FCS_LEN;
- skb_put(skb, len);
-
- skb->protocol = eth_type_trans(skb, dev);
-
- bfin_rx_hwtstamp(dev, skb);
-
-#if defined(BFIN_MAC_CSUM_OFFLOAD)
- /* Checksum offloading only works for IPv4 packets with the standard IP header
- * length of 20 bytes, because the blackfin MAC checksum calculation is
- * based on that assumption. We must NOT use the calculated checksum if our
- * IP version or header break that assumption.
- */
- if (skb->data[IP_HEADER_OFF] == 0x45) {
- skb->csum = current_rx_ptr->status.ip_payload_csum;
- /*
- * Deduce Ethernet FCS from hardware generated IP payload checksum.
- * IP checksum is based on 16-bit one's complement algorithm.
- * To deduce a value from checksum is equal to add its inversion.
- * If the IP payload len is odd, the inversed FCS should also
- * begin from odd address and leave first byte zero.
- */
- if (skb->len % 2) {
- fcs[0] = 0;
- for (i = 0; i < ETH_FCS_LEN; i++)
- fcs[i + 1] = ~skb->data[skb->len + i];
- skb->csum = csum_partial(fcs, ETH_FCS_LEN + 1, skb->csum);
- } else {
- for (i = 0; i < ETH_FCS_LEN; i++)
- fcs[i] = ~skb->data[skb->len + i];
- skb->csum = csum_partial(fcs, ETH_FCS_LEN, skb->csum);
- }
- skb->ip_summed = CHECKSUM_COMPLETE;
- }
-#endif
-
- napi_gro_receive(&lp->napi, skb);
-
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += len;
-out:
- current_rx_ptr->status.status_word = 0x00000000;
- current_rx_ptr = current_rx_ptr->next;
-}
-
-static int bfin_mac_poll(struct napi_struct *napi, int budget)
-{
- int i = 0;
- struct bfin_mac_local *lp = container_of(napi,
- struct bfin_mac_local,
- napi);
-
- while (current_rx_ptr->status.status_word != 0 && i < budget) {
- bfin_mac_rx(lp);
- i++;
- }
-
- if (i < budget) {
- napi_complete_done(napi, i);
- if (test_and_clear_bit(BFIN_MAC_RX_IRQ_DISABLED, &lp->flags))
- enable_irq(IRQ_MAC_RX);
- }
-
- return i;
-}
-
-/* interrupt routine to handle rx and error signal */
-static irqreturn_t bfin_mac_interrupt(int irq, void *dev_id)
-{
- struct bfin_mac_local *lp = netdev_priv(dev_id);
- u32 status;
-
- status = bfin_read_DMA1_IRQ_STATUS();
-
- bfin_write_DMA1_IRQ_STATUS(status | DMA_DONE | DMA_ERR);
- if (status & DMA_DONE) {
- disable_irq_nosync(IRQ_MAC_RX);
- set_bit(BFIN_MAC_RX_IRQ_DISABLED, &lp->flags);
- napi_schedule(&lp->napi);
- }
-
- return IRQ_HANDLED;
-}
-
-#ifdef CONFIG_NET_POLL_CONTROLLER
-static void bfin_mac_poll_controller(struct net_device *dev)
-{
- struct bfin_mac_local *lp = netdev_priv(dev);
-
- bfin_mac_interrupt(IRQ_MAC_RX, dev);
- tx_reclaim_skb(lp);
-}
-#endif /* CONFIG_NET_POLL_CONTROLLER */
-
-static void bfin_mac_disable(void)
-{
- unsigned int opmode;
-
- opmode = bfin_read_EMAC_OPMODE();
- opmode &= (~RE);
- opmode &= (~TE);
- /* Turn off the EMAC */
- bfin_write_EMAC_OPMODE(opmode);
-}
-
-/*
- * Enable Interrupts, Receive, and Transmit
- */
-static int bfin_mac_enable(struct phy_device *phydev)
-{
- int ret;
- u32 opmode;
-
- pr_debug("%s\n", __func__);
-
- /* Set RX DMA */
- bfin_write_DMA1_NEXT_DESC_PTR(&(rx_list_head->desc_a));
- bfin_write_DMA1_CONFIG(rx_list_head->desc_a.config);
-
- /* Wait MII done */
- ret = bfin_mdio_poll();
- if (ret)
- return ret;
-
- /* We enable only RX here */
- /* ASTP : Enable Automatic Pad Stripping
- PR : Promiscuous Mode for test
- PSF : Receive frames with total length less than 64 bytes.
- FDMODE : Full Duplex Mode
- LB : Internal Loopback for test
- RE : Receiver Enable */
- opmode = bfin_read_EMAC_OPMODE();
- if (opmode & FDMODE)
- opmode |= PSF;
- else
- opmode |= DRO | DC | PSF;
- opmode |= RE;
-
- if (phydev->interface == PHY_INTERFACE_MODE_RMII) {
- opmode |= RMII; /* For Now only 100MBit are supported */
-#if defined(CONFIG_BF537) || defined(CONFIG_BF536)
- if (__SILICON_REVISION__ < 3) {
- /*
- * This isn't publicly documented (fun times!), but in
- * silicon <=0.2, the RX and TX pins are clocked together.
- * So in order to recv, we must enable the transmit side
- * as well. This will cause a spurious TX interrupt too,
- * but we can easily consume that.
- */
- opmode |= TE;
- }
-#endif
- }
-
- /* Turn on the EMAC rx */
- bfin_write_EMAC_OPMODE(opmode);
-
- return 0;
-}
-
-/* Our watchdog timed out. Called by the networking layer */
-static void bfin_mac_timeout(struct net_device *dev)
-{
- struct bfin_mac_local *lp = netdev_priv(dev);
-
- pr_debug("%s: %s\n", dev->name, __func__);
-
- bfin_mac_disable();
-
- del_timer(&lp->tx_reclaim_timer);
-
- /* reset tx queue and free skb */
- while (tx_list_head != current_tx_ptr) {
- tx_list_head->desc_a.config &= ~DMAEN;
- tx_list_head->status.status_word = 0;
- if (tx_list_head->skb) {
- dev_kfree_skb(tx_list_head->skb);
- tx_list_head->skb = NULL;
- }
- tx_list_head = tx_list_head->next;
- }
-
- if (netif_queue_stopped(dev))
- netif_wake_queue(dev);
-
- bfin_mac_enable(dev->phydev);
-
- /* We can accept TX packets again */
- netif_trans_update(dev); /* prevent tx timeout */
-}
-
-static void bfin_mac_multicast_hash(struct net_device *dev)
-{
- u32 emac_hashhi, emac_hashlo;
- struct netdev_hw_addr *ha;
- u32 crc;
-
- emac_hashhi = emac_hashlo = 0;
-
- netdev_for_each_mc_addr(ha, dev) {
- crc = ether_crc(ETH_ALEN, ha->addr);
- crc >>= 26;
-
- if (crc & 0x20)
- emac_hashhi |= 1 << (crc & 0x1f);
- else
- emac_hashlo |= 1 << (crc & 0x1f);
- }
-
- bfin_write_EMAC_HASHHI(emac_hashhi);
- bfin_write_EMAC_HASHLO(emac_hashlo);
-}
-
-/*
- * This routine will, depending on the values passed to it,
- * either make it accept multicast packets, go into
- * promiscuous mode (for TCPDUMP and cousins) or accept
- * a select set of multicast packets
- */
-static void bfin_mac_set_multicast_list(struct net_device *dev)
-{
- u32 sysctl;
-
- if (dev->flags & IFF_PROMISC) {
- netdev_info(dev, "set promisc mode\n");
- sysctl = bfin_read_EMAC_OPMODE();
- sysctl |= PR;
- bfin_write_EMAC_OPMODE(sysctl);
- } else if (dev->flags & IFF_ALLMULTI) {
- /* accept all multicast */
- sysctl = bfin_read_EMAC_OPMODE();
- sysctl |= PAM;
- bfin_write_EMAC_OPMODE(sysctl);
- } else if (!netdev_mc_empty(dev)) {
- /* set up multicast hash table */
- sysctl = bfin_read_EMAC_OPMODE();
- sysctl |= HM;
- bfin_write_EMAC_OPMODE(sysctl);
- bfin_mac_multicast_hash(dev);
- } else {
- /* clear promisc or multicast mode */
- sysctl = bfin_read_EMAC_OPMODE();
- sysctl &= ~(RAF | PAM);
- bfin_write_EMAC_OPMODE(sysctl);
- }
-}
-
-static int bfin_mac_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
-{
- if (!netif_running(netdev))
- return -EINVAL;
-
- switch (cmd) {
- case SIOCSHWTSTAMP:
- return bfin_mac_hwtstamp_set(netdev, ifr);
- case SIOCGHWTSTAMP:
- return bfin_mac_hwtstamp_get(netdev, ifr);
- default:
- if (netdev->phydev)
- return phy_mii_ioctl(netdev->phydev, ifr, cmd);
- else
- return -EOPNOTSUPP;
- }
-}
-
-/*
- * this puts the device in an inactive state
- */
-static void bfin_mac_shutdown(struct net_device *dev)
-{
- /* Turn off the EMAC */
- bfin_write_EMAC_OPMODE(0x00000000);
- /* Turn off the EMAC RX DMA */
- bfin_write_DMA1_CONFIG(0x0000);
- bfin_write_DMA2_CONFIG(0x0000);
-}
-
-/*
- * Open and Initialize the interface
- *
- * Set up everything, reset the card, etc..
- */
-static int bfin_mac_open(struct net_device *dev)
-{
- struct bfin_mac_local *lp = netdev_priv(dev);
- int ret;
- pr_debug("%s: %s\n", dev->name, __func__);
-
- /*
- * Check that the address is valid. If its not, refuse
- * to bring the device up. The user must specify an
- * address using ifconfig eth0 hw ether xx:xx:xx:xx:xx:xx
- */
- if (!is_valid_ether_addr(dev->dev_addr)) {
- netdev_warn(dev, "no valid ethernet hw addr\n");
- return -EINVAL;
- }
-
- /* initial rx and tx list */
- ret = desc_list_init(dev);
- if (ret)
- return ret;
-
- phy_start(dev->phydev);
- setup_system_regs(dev);
- setup_mac_addr(dev->dev_addr);
-
- bfin_mac_disable();
- ret = bfin_mac_enable(dev->phydev);
- if (ret)
- return ret;
- pr_debug("hardware init finished\n");
-
- napi_enable(&lp->napi);
- netif_start_queue(dev);
- netif_carrier_on(dev);
-
- return 0;
-}
-
-/*
- * this makes the board clean up everything that it can
- * and not talk to the outside world. Caused by
- * an 'ifconfig ethX down'
- */
-static int bfin_mac_close(struct net_device *dev)
-{
- struct bfin_mac_local *lp = netdev_priv(dev);
- pr_debug("%s: %s\n", dev->name, __func__);
-
- netif_stop_queue(dev);
- napi_disable(&lp->napi);
- netif_carrier_off(dev);
-
- phy_stop(dev->phydev);
- phy_write(dev->phydev, MII_BMCR, BMCR_PDOWN);
-
- /* clear everything */
- bfin_mac_shutdown(dev);
-
- /* free the rx/tx buffers */
- desc_list_free();
-
- return 0;
-}
-
-static const struct net_device_ops bfin_mac_netdev_ops = {
- .ndo_open = bfin_mac_open,
- .ndo_stop = bfin_mac_close,
- .ndo_start_xmit = bfin_mac_hard_start_xmit,
- .ndo_set_mac_address = bfin_mac_set_mac_address,
- .ndo_tx_timeout = bfin_mac_timeout,
- .ndo_set_rx_mode = bfin_mac_set_multicast_list,
- .ndo_do_ioctl = bfin_mac_ioctl,
- .ndo_validate_addr = eth_validate_addr,
-#ifdef CONFIG_NET_POLL_CONTROLLER
- .ndo_poll_controller = bfin_mac_poll_controller,
-#endif
-};
-
-static int bfin_mac_probe(struct platform_device *pdev)
-{
- struct net_device *ndev;
- struct bfin_mac_local *lp;
- struct platform_device *pd;
- struct bfin_mii_bus_platform_data *mii_bus_data;
- int rc;
-
- ndev = alloc_etherdev(sizeof(struct bfin_mac_local));
- if (!ndev)
- return -ENOMEM;
-
- SET_NETDEV_DEV(ndev, &pdev->dev);
- platform_set_drvdata(pdev, ndev);
- lp = netdev_priv(ndev);
- lp->ndev = ndev;
-
- /* Grab the MAC address in the MAC */
- *(__le32 *) (&(ndev->dev_addr[0])) = cpu_to_le32(bfin_read_EMAC_ADDRLO());
- *(__le16 *) (&(ndev->dev_addr[4])) = cpu_to_le16((u16) bfin_read_EMAC_ADDRHI());
-
- /* probe mac */
- /*todo: how to probe? which is revision_register */
- bfin_write_EMAC_ADDRLO(0x12345678);
- if (bfin_read_EMAC_ADDRLO() != 0x12345678) {
- dev_err(&pdev->dev, "Cannot detect Blackfin on-chip ethernet MAC controller!\n");
- rc = -ENODEV;
- goto out_err_probe_mac;
- }
-
-
- /*
- * Is it valid? (Did bootloader initialize it?)
- * Grab the MAC from the board somehow
- * this is done in the arch/blackfin/mach-bfxxx/boards/eth_mac.c
- */
- if (!is_valid_ether_addr(ndev->dev_addr)) {
- if (bfin_get_ether_addr(ndev->dev_addr) ||
- !is_valid_ether_addr(ndev->dev_addr)) {
- /* Still not valid, get a random one */
- netdev_warn(ndev, "Setting Ethernet MAC to a random one\n");
- eth_hw_addr_random(ndev);
- }
- }
-
- setup_mac_addr(ndev->dev_addr);
-
- if (!dev_get_platdata(&pdev->dev)) {
- dev_err(&pdev->dev, "Cannot get platform device bfin_mii_bus!\n");
- rc = -ENODEV;
- goto out_err_probe_mac;
- }
- pd = dev_get_platdata(&pdev->dev);
- lp->mii_bus = platform_get_drvdata(pd);
- if (!lp->mii_bus) {
- dev_err(&pdev->dev, "Cannot get mii_bus!\n");
- rc = -ENODEV;
- goto out_err_probe_mac;
- }
- lp->mii_bus->priv = ndev;
- mii_bus_data = dev_get_platdata(&pd->dev);
-
- rc = mii_probe(ndev, mii_bus_data->phy_mode);
- if (rc) {
- dev_err(&pdev->dev, "MII Probe failed!\n");
- goto out_err_mii_probe;
- }
-
- lp->vlan1_mask = ETH_P_8021Q | mii_bus_data->vlan1_mask;
- lp->vlan2_mask = ETH_P_8021Q | mii_bus_data->vlan2_mask;
-
- ndev->netdev_ops = &bfin_mac_netdev_ops;
- ndev->ethtool_ops = &bfin_mac_ethtool_ops;
-
- timer_setup(&lp->tx_reclaim_timer, tx_reclaim_skb_timeout, 0);
-
- lp->flags = 0;
- netif_napi_add(ndev, &lp->napi, bfin_mac_poll, CONFIG_BFIN_RX_DESC_NUM);
-
- spin_lock_init(&lp->lock);
-
- /* now, enable interrupts */
- /* register irq handler */
- rc = request_irq(IRQ_MAC_RX, bfin_mac_interrupt,
- 0, "EMAC_RX", ndev);
- if (rc) {
- dev_err(&pdev->dev, "Cannot request Blackfin MAC RX IRQ!\n");
- rc = -EBUSY;
- goto out_err_request_irq;
- }
-
- rc = register_netdev(ndev);
- if (rc) {
- dev_err(&pdev->dev, "Cannot register net device!\n");
- goto out_err_reg_ndev;
- }
-
- bfin_mac_hwtstamp_init(ndev);
- rc = bfin_phc_init(ndev, &pdev->dev);
- if (rc) {
- dev_err(&pdev->dev, "Cannot register PHC device!\n");
- goto out_err_phc;
- }
-
- /* now, print out the card info, in a short format.. */
- netdev_info(ndev, "%s, Version %s\n", DRV_DESC, DRV_VERSION);
-
- return 0;
-
-out_err_phc:
-out_err_reg_ndev:
- free_irq(IRQ_MAC_RX, ndev);
-out_err_request_irq:
- netif_napi_del(&lp->napi);
-out_err_mii_probe:
- mdiobus_unregister(lp->mii_bus);
- mdiobus_free(lp->mii_bus);
-out_err_probe_mac:
- free_netdev(ndev);
-
- return rc;
-}
-
-static int bfin_mac_remove(struct platform_device *pdev)
-{
- struct net_device *ndev = platform_get_drvdata(pdev);
- struct bfin_mac_local *lp = netdev_priv(ndev);
-
- bfin_phc_release(lp);
-
- lp->mii_bus->priv = NULL;
-
- unregister_netdev(ndev);
-
- netif_napi_del(&lp->napi);
-
- free_irq(IRQ_MAC_RX, ndev);
-
- free_netdev(ndev);
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int bfin_mac_suspend(struct platform_device *pdev, pm_message_t mesg)
-{
- struct net_device *net_dev = platform_get_drvdata(pdev);
- struct bfin_mac_local *lp = netdev_priv(net_dev);
-
- if (lp->wol) {
- bfin_write_EMAC_OPMODE((bfin_read_EMAC_OPMODE() & ~TE) | RE);
- bfin_write_EMAC_WKUP_CTL(MPKE);
- enable_irq_wake(IRQ_MAC_WAKEDET);
- } else {
- if (netif_running(net_dev))
- bfin_mac_close(net_dev);
- }
-
- return 0;
-}
-
-static int bfin_mac_resume(struct platform_device *pdev)
-{
- struct net_device *net_dev = platform_get_drvdata(pdev);
- struct bfin_mac_local *lp = netdev_priv(net_dev);
-
- if (lp->wol) {
- bfin_write_EMAC_OPMODE(bfin_read_EMAC_OPMODE() | TE);
- bfin_write_EMAC_WKUP_CTL(0);
- disable_irq_wake(IRQ_MAC_WAKEDET);
- } else {
- if (netif_running(net_dev))
- bfin_mac_open(net_dev);
- }
-
- return 0;
-}
-#else
-#define bfin_mac_suspend NULL
-#define bfin_mac_resume NULL
-#endif /* CONFIG_PM */
-
-static int bfin_mii_bus_probe(struct platform_device *pdev)
-{
- struct mii_bus *miibus;
- struct bfin_mii_bus_platform_data *mii_bus_pd;
- const unsigned short *pin_req;
- int rc, i;
-
- mii_bus_pd = dev_get_platdata(&pdev->dev);
- if (!mii_bus_pd) {
- dev_err(&pdev->dev, "No peripherals in platform data!\n");
- return -EINVAL;
- }
-
- /*
- * We are setting up a network card,
- * so set the GPIO pins to Ethernet mode
- */
- pin_req = mii_bus_pd->mac_peripherals;
- rc = peripheral_request_list(pin_req, KBUILD_MODNAME);
- if (rc) {
- dev_err(&pdev->dev, "Requesting peripherals failed!\n");
- return rc;
- }
-
- rc = -ENOMEM;
- miibus = mdiobus_alloc();
- if (miibus == NULL)
- goto out_err_alloc;
- miibus->read = bfin_mdiobus_read;
- miibus->write = bfin_mdiobus_write;
-
- miibus->parent = &pdev->dev;
- miibus->name = "bfin_mii_bus";
- miibus->phy_mask = mii_bus_pd->phy_mask;
-
- snprintf(miibus->id, MII_BUS_ID_SIZE, "%s-%x",
- pdev->name, pdev->id);
-
- rc = clamp(mii_bus_pd->phydev_number, 0, PHY_MAX_ADDR);
- if (rc != mii_bus_pd->phydev_number)
- dev_err(&pdev->dev, "Invalid number (%i) of phydevs\n",
- mii_bus_pd->phydev_number);
- for (i = 0; i < rc; ++i) {
- unsigned short phyaddr = mii_bus_pd->phydev_data[i].addr;
- if (phyaddr < PHY_MAX_ADDR)
- miibus->irq[phyaddr] = mii_bus_pd->phydev_data[i].irq;
- else
- dev_err(&pdev->dev,
- "Invalid PHY address %i for phydev %i\n",
- phyaddr, i);
- }
-
- rc = mdiobus_register(miibus);
- if (rc) {
- dev_err(&pdev->dev, "Cannot register MDIO bus!\n");
- goto out_err_irq_alloc;
- }
-
- platform_set_drvdata(pdev, miibus);
- return 0;
-
-out_err_irq_alloc:
- mdiobus_free(miibus);
-out_err_alloc:
- peripheral_free_list(pin_req);
-
- return rc;
-}
-
-static int bfin_mii_bus_remove(struct platform_device *pdev)
-{
- struct mii_bus *miibus = platform_get_drvdata(pdev);
- struct bfin_mii_bus_platform_data *mii_bus_pd =
- dev_get_platdata(&pdev->dev);
-
- mdiobus_unregister(miibus);
- mdiobus_free(miibus);
- peripheral_free_list(mii_bus_pd->mac_peripherals);
-
- return 0;
-}
-
-static struct platform_driver bfin_mii_bus_driver = {
- .probe = bfin_mii_bus_probe,
- .remove = bfin_mii_bus_remove,
- .driver = {
- .name = "bfin_mii_bus",
- },
-};
-
-static struct platform_driver bfin_mac_driver = {
- .probe = bfin_mac_probe,
- .remove = bfin_mac_remove,
- .resume = bfin_mac_resume,
- .suspend = bfin_mac_suspend,
- .driver = {
- .name = KBUILD_MODNAME,
- },
-};
-
-static struct platform_driver * const drivers[] = {
- &bfin_mii_bus_driver,
- &bfin_mac_driver,
-};
-
-static int __init bfin_mac_init(void)
-{
- return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
-}
-
-module_init(bfin_mac_init);
-
-static void __exit bfin_mac_cleanup(void)
-{
- platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
-}
-
-module_exit(bfin_mac_cleanup);
-
diff --git a/drivers/net/ethernet/adi/bfin_mac.h b/drivers/net/ethernet/adi/bfin_mac.h
deleted file mode 100644
index 4ad5b9be3f84..000000000000
--- a/drivers/net/ethernet/adi/bfin_mac.h
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Blackfin On-Chip MAC Driver
- *
- * Copyright 2004-2007 Analog Devices Inc.
- *
- * Enter bugs at http://blackfin.uclinux.org/
- *
- * Licensed under the GPL-2 or later.
- */
-#ifndef _BFIN_MAC_H_
-#define _BFIN_MAC_H_
-
-#include <linux/net_tstamp.h>
-#include <linux/ptp_clock_kernel.h>
-#include <linux/timer.h>
-#include <linux/etherdevice.h>
-#include <linux/bfin_mac.h>
-
-/*
- * Disable hardware checksum for bug #5600 if writeback cache is
- * enabled. Otherwize, corrupted RX packet will be sent up stack
- * without error mark.
- */
-#ifndef CONFIG_BFIN_EXTMEM_WRITEBACK
-#define BFIN_MAC_CSUM_OFFLOAD
-#endif
-
-#define TX_RECLAIM_JIFFIES (HZ / 5)
-#define BFIN_MAC_RX_IRQ_DISABLED 1
-
-struct dma_descriptor {
- struct dma_descriptor *next_dma_desc;
- unsigned long start_addr;
- unsigned short config;
- unsigned short x_count;
-};
-
-struct status_area_rx {
-#if defined(BFIN_MAC_CSUM_OFFLOAD)
- unsigned short ip_hdr_csum; /* ip header checksum */
- /* ip payload(udp or tcp or others) checksum */
- unsigned short ip_payload_csum;
-#endif
- unsigned long status_word; /* the frame status word */
-};
-
-struct status_area_tx {
- unsigned long status_word; /* the frame status word */
-};
-
-/* use two descriptors for a packet */
-struct net_dma_desc_rx {
- struct net_dma_desc_rx *next;
- struct sk_buff *skb;
- struct dma_descriptor desc_a;
- struct dma_descriptor desc_b;
- struct status_area_rx status;
-};
-
-/* use two descriptors for a packet */
-struct net_dma_desc_tx {
- struct net_dma_desc_tx *next;
- struct sk_buff *skb;
- struct dma_descriptor desc_a;
- struct dma_descriptor desc_b;
- unsigned char packet[1560];
- struct status_area_tx status;
-};
-
-struct bfin_mac_local {
- spinlock_t lock;
-
- int wol; /* Wake On Lan */
- int irq_wake_requested;
- struct timer_list tx_reclaim_timer;
- struct net_device *ndev;
- struct napi_struct napi;
- unsigned long flags;
-
- /* Data for EMAC_VLAN1 regs */
- u16 vlan1_mask, vlan2_mask;
-
- /* MII and PHY stuffs */
- int old_link; /* used by bf537_adjust_link */
- int old_speed;
- int old_duplex;
-
- struct mii_bus *mii_bus;
-
-#if defined(CONFIG_BFIN_MAC_USE_HWSTAMP)
- u32 addend;
- unsigned int shift;
- s32 max_ppb;
- struct hwtstamp_config stamp_cfg;
- struct ptp_clock_info caps;
- struct ptp_clock *clock;
- int phc_index;
- spinlock_t phc_lock; /* protects time lo/hi registers */
-#endif
-};
-
-int bfin_get_ether_addr(char *addr);
-
-#endif
diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c
index 527908c7e384..baca8f704a45 100644
--- a/drivers/net/ethernet/altera/altera_tse_main.c
+++ b/drivers/net/ethernet/altera/altera_tse_main.c
@@ -56,7 +56,7 @@
static atomic_t instance_count = ATOMIC_INIT(~0);
/* Module parameters */
static int debug = -1;
-module_param(debug, int, S_IRUGO | S_IWUSR);
+module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Message Level (-1: default, 0: no output, 16: all)");
static const u32 default_msg_level = (NETIF_MSG_DRV | NETIF_MSG_PROBE |
@@ -65,12 +65,12 @@ static const u32 default_msg_level = (NETIF_MSG_DRV | NETIF_MSG_PROBE |
#define RX_DESCRIPTORS 64
static int dma_rx_num = RX_DESCRIPTORS;
-module_param(dma_rx_num, int, S_IRUGO | S_IWUSR);
+module_param(dma_rx_num, int, 0644);
MODULE_PARM_DESC(dma_rx_num, "Number of descriptors in the RX list");
#define TX_DESCRIPTORS 64
static int dma_tx_num = TX_DESCRIPTORS;
-module_param(dma_tx_num, int, S_IRUGO | S_IWUSR);
+module_param(dma_tx_num, int, 0644);
MODULE_PARM_DESC(dma_tx_num, "Number of descriptors in the TX list");
diff --git a/drivers/net/ethernet/amazon/ena/ena_com.c b/drivers/net/ethernet/amazon/ena/ena_com.c
index bf2de5298005..1b9d3130af4d 100644
--- a/drivers/net/ethernet/amazon/ena/ena_com.c
+++ b/drivers/net/ethernet/amazon/ena/ena_com.c
@@ -631,8 +631,10 @@ static u32 ena_com_reg_bar_read32(struct ena_com_dev *ena_dev, u16 offset)
*/
wmb();
- writel(mmio_read_reg, ena_dev->reg_bar + ENA_REGS_MMIO_REG_READ_OFF);
+ writel_relaxed(mmio_read_reg,
+ ena_dev->reg_bar + ENA_REGS_MMIO_REG_READ_OFF);
+ mmiowb();
for (i = 0; i < timeout; i++) {
if (read_resp->req_id == mmio_read->seq_num)
break;
@@ -1826,7 +1828,9 @@ void ena_com_aenq_intr_handler(struct ena_com_dev *dev, void *data)
/* write the aenq doorbell after all AENQ descriptors were read */
mb();
- writel((u32)aenq->head, dev->reg_bar + ENA_REGS_AENQ_HEAD_DB_OFF);
+ writel_relaxed((u32)aenq->head,
+ dev->reg_bar + ENA_REGS_AENQ_HEAD_DB_OFF);
+ mmiowb();
}
int ena_com_dev_reset(struct ena_com_dev *ena_dev,
diff --git a/drivers/net/ethernet/amazon/ena/ena_eth_com.h b/drivers/net/ethernet/amazon/ena/ena_eth_com.h
index 2f7657227cfe..6fdc753d9483 100644
--- a/drivers/net/ethernet/amazon/ena/ena_eth_com.h
+++ b/drivers/net/ethernet/amazon/ena/ena_eth_com.h
@@ -107,7 +107,8 @@ static inline int ena_com_sq_empty_space(struct ena_com_io_sq *io_sq)
return io_sq->q_depth - 1 - cnt;
}
-static inline int ena_com_write_sq_doorbell(struct ena_com_io_sq *io_sq)
+static inline int ena_com_write_sq_doorbell(struct ena_com_io_sq *io_sq,
+ bool relaxed)
{
u16 tail;
@@ -116,7 +117,10 @@ static inline int ena_com_write_sq_doorbell(struct ena_com_io_sq *io_sq)
pr_debug("write submission queue doorbell for queue: %d tail: %d\n",
io_sq->qid, tail);
- writel(tail, io_sq->db_addr);
+ if (relaxed)
+ writel_relaxed(tail, io_sq->db_addr);
+ else
+ writel(tail, io_sq->db_addr);
return 0;
}
diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.c b/drivers/net/ethernet/amazon/ena/ena_netdev.c
index 6975150d144e..a822e70c2af3 100644
--- a/drivers/net/ethernet/amazon/ena/ena_netdev.c
+++ b/drivers/net/ethernet/amazon/ena/ena_netdev.c
@@ -556,7 +556,8 @@ static int ena_refill_rx_bufs(struct ena_ring *rx_ring, u32 num)
* issue a doorbell
*/
wmb();
- ena_com_write_sq_doorbell(rx_ring->ena_com_io_sq);
+ ena_com_write_sq_doorbell(rx_ring->ena_com_io_sq, true);
+ mmiowb();
}
rx_ring->next_to_use = next_to_use;
@@ -2151,7 +2152,7 @@ static netdev_tx_t ena_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (netif_xmit_stopped(txq) || !skb->xmit_more) {
/* trigger the dma engine */
- ena_com_write_sq_doorbell(tx_ring->ena_com_io_sq);
+ ena_com_write_sq_doorbell(tx_ring->ena_com_io_sq, false);
u64_stats_update_begin(&tx_ring->syncp);
tx_ring->tx_stats.doorbells++;
u64_stats_update_end(&tx_ring->syncp);
diff --git a/drivers/net/ethernet/amd/amd8111e.c b/drivers/net/ethernet/amd/amd8111e.c
index 358f7ab77c70..c99e3e845ac0 100644
--- a/drivers/net/ethernet/amd/amd8111e.c
+++ b/drivers/net/ethernet/amd/amd8111e.c
@@ -649,7 +649,7 @@ static void amd8111e_free_ring(struct amd8111e_priv *lp)
static int amd8111e_tx(struct net_device *dev)
{
struct amd8111e_priv *lp = netdev_priv(dev);
- int tx_index = lp->tx_complete_idx & TX_RING_DR_MOD_MASK;
+ int tx_index;
int status;
/* Complete all the transmit packet */
while (lp->tx_complete_idx != lp->tx_idx){
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-common.h b/drivers/net/ethernet/amd/xgbe/xgbe-common.h
index 7ea72ef11a55..d272dc6984ac 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-common.h
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-common.h
@@ -1321,6 +1321,10 @@
#define MDIO_VEND2_AN_STAT 0x8002
#endif
+#ifndef MDIO_VEND2_PMA_CDR_CONTROL
+#define MDIO_VEND2_PMA_CDR_CONTROL 0x8056
+#endif
+
#ifndef MDIO_CTRL1_SPEED1G
#define MDIO_CTRL1_SPEED1G (MDIO_CTRL1_SPEED10G & ~BMCR_SPEED100)
#endif
@@ -1369,6 +1373,10 @@
#define XGBE_AN_CL37_TX_CONFIG_MASK 0x08
#define XGBE_AN_CL37_MII_CTRL_8BIT 0x0100
+#define XGBE_PMA_CDR_TRACK_EN_MASK 0x01
+#define XGBE_PMA_CDR_TRACK_EN_OFF 0x00
+#define XGBE_PMA_CDR_TRACK_EN_ON 0x01
+
/* Bit setting and getting macros
* The get macro will extract the current bit field value from within
* the variable
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c b/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c
index 7d128be61310..b91143947ed2 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c
@@ -519,6 +519,22 @@ void xgbe_debugfs_init(struct xgbe_prv_data *pdata)
"debugfs_create_file failed\n");
}
+ if (pdata->vdata->an_cdr_workaround) {
+ pfile = debugfs_create_bool("an_cdr_workaround", 0600,
+ pdata->xgbe_debugfs,
+ &pdata->debugfs_an_cdr_workaround);
+ if (!pfile)
+ netdev_err(pdata->netdev,
+ "debugfs_create_bool failed\n");
+
+ pfile = debugfs_create_bool("an_cdr_track_early", 0600,
+ pdata->xgbe_debugfs,
+ &pdata->debugfs_an_cdr_track_early);
+ if (!pfile)
+ netdev_err(pdata->netdev,
+ "debugfs_create_bool failed\n");
+ }
+
kfree(buf);
}
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
index 100adee778df..7c204f05b418 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
@@ -137,21 +137,21 @@ static unsigned int ecc_ded_period = 600;
#ifdef CONFIG_AMD_XGBE_HAVE_ECC
/* Only expose the ECC parameters if supported */
-module_param(ecc_sec_info_threshold, uint, S_IWUSR | S_IRUGO);
+module_param(ecc_sec_info_threshold, uint, 0644);
MODULE_PARM_DESC(ecc_sec_info_threshold,
" ECC corrected error informational threshold setting");
-module_param(ecc_sec_warn_threshold, uint, S_IWUSR | S_IRUGO);
+module_param(ecc_sec_warn_threshold, uint, 0644);
MODULE_PARM_DESC(ecc_sec_warn_threshold,
" ECC corrected error warning threshold setting");
-module_param(ecc_sec_period, uint, S_IWUSR | S_IRUGO);
+module_param(ecc_sec_period, uint, 0644);
MODULE_PARM_DESC(ecc_sec_period, " ECC corrected error period (in seconds)");
-module_param(ecc_ded_threshold, uint, S_IWUSR | S_IRUGO);
+module_param(ecc_ded_threshold, uint, 0644);
MODULE_PARM_DESC(ecc_ded_threshold, " ECC detected error threshold setting");
-module_param(ecc_ded_period, uint, S_IWUSR | S_IRUGO);
+module_param(ecc_ded_period, uint, 0644);
MODULE_PARM_DESC(ecc_ded_period, " ECC detected error period (in seconds)");
#endif
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c
index d91fa595be98..441d0973957b 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c
@@ -131,7 +131,7 @@ MODULE_VERSION(XGBE_DRV_VERSION);
MODULE_DESCRIPTION(XGBE_DRV_DESC);
static int debug = -1;
-module_param(debug, int, S_IWUSR | S_IRUGO);
+module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, " Network interface message level setting");
static const u32 default_msg_level = (NETIF_MSG_LINK | NETIF_MSG_IFDOWN |
@@ -349,6 +349,7 @@ int xgbe_config_netdev(struct xgbe_prv_data *pdata)
XGMAC_SET_BITS(pdata->rss_options, MAC_RSSCR, UDP4TE, 1);
/* Call MDIO/PHY initialization routine */
+ pdata->debugfs_an_cdr_workaround = pdata->vdata->an_cdr_workaround;
ret = pdata->phy_if.phy_init(pdata);
if (ret)
return ret;
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
index 072b9f664597..1b45cd73a258 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
@@ -432,11 +432,16 @@ static void xgbe_an73_disable(struct xgbe_prv_data *pdata)
xgbe_an73_set(pdata, false, false);
xgbe_an73_disable_interrupts(pdata);
+ pdata->an_start = 0;
+
netif_dbg(pdata, link, pdata->netdev, "CL73 AN disabled\n");
}
static void xgbe_an_restart(struct xgbe_prv_data *pdata)
{
+ if (pdata->phy_if.phy_impl.an_pre)
+ pdata->phy_if.phy_impl.an_pre(pdata);
+
switch (pdata->an_mode) {
case XGBE_AN_MODE_CL73:
case XGBE_AN_MODE_CL73_REDRV:
@@ -453,6 +458,9 @@ static void xgbe_an_restart(struct xgbe_prv_data *pdata)
static void xgbe_an_disable(struct xgbe_prv_data *pdata)
{
+ if (pdata->phy_if.phy_impl.an_post)
+ pdata->phy_if.phy_impl.an_post(pdata);
+
switch (pdata->an_mode) {
case XGBE_AN_MODE_CL73:
case XGBE_AN_MODE_CL73_REDRV:
@@ -505,11 +513,11 @@ static enum xgbe_an xgbe_an73_tx_training(struct xgbe_prv_data *pdata,
XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL,
reg);
- if (pdata->phy_if.phy_impl.kr_training_post)
- pdata->phy_if.phy_impl.kr_training_post(pdata);
-
netif_dbg(pdata, link, pdata->netdev,
"KR training initiated\n");
+
+ if (pdata->phy_if.phy_impl.kr_training_post)
+ pdata->phy_if.phy_impl.kr_training_post(pdata);
}
return XGBE_AN_PAGE_RECEIVED;
@@ -637,11 +645,11 @@ static enum xgbe_an xgbe_an73_incompat_link(struct xgbe_prv_data *pdata)
return XGBE_AN_NO_LINK;
}
- xgbe_an73_disable(pdata);
+ xgbe_an_disable(pdata);
xgbe_switch_mode(pdata);
- xgbe_an73_restart(pdata);
+ xgbe_an_restart(pdata);
return XGBE_AN_INCOMPAT_LINK;
}
@@ -820,6 +828,9 @@ static void xgbe_an37_state_machine(struct xgbe_prv_data *pdata)
pdata->an_result = pdata->an_state;
pdata->an_state = XGBE_AN_READY;
+ if (pdata->phy_if.phy_impl.an_post)
+ pdata->phy_if.phy_impl.an_post(pdata);
+
netif_dbg(pdata, link, pdata->netdev, "CL37 AN result: %s\n",
xgbe_state_as_string(pdata->an_result));
}
@@ -903,6 +914,9 @@ again:
pdata->kx_state = XGBE_RX_BPA;
pdata->an_start = 0;
+ if (pdata->phy_if.phy_impl.an_post)
+ pdata->phy_if.phy_impl.an_post(pdata);
+
netif_dbg(pdata, link, pdata->netdev, "CL73 AN result: %s\n",
xgbe_state_as_string(pdata->an_result));
}
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-pci.c b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c
index eb23f9ba1a9a..82d1f416ee2a 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-pci.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c
@@ -456,6 +456,7 @@ static const struct xgbe_version_data xgbe_v2a = {
.irq_reissue_support = 1,
.tx_desc_prefetch = 5,
.rx_desc_prefetch = 5,
+ .an_cdr_workaround = 1,
};
static const struct xgbe_version_data xgbe_v2b = {
@@ -470,6 +471,7 @@ static const struct xgbe_version_data xgbe_v2b = {
.irq_reissue_support = 1,
.tx_desc_prefetch = 5,
.rx_desc_prefetch = 5,
+ .an_cdr_workaround = 1,
};
static const struct pci_device_id xgbe_pci_table[] = {
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c
index 3304a291aa96..aac884314000 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c
@@ -147,6 +147,14 @@
/* Rate-change complete wait/retry count */
#define XGBE_RATECHANGE_COUNT 500
+/* CDR delay values for KR support (in usec) */
+#define XGBE_CDR_DELAY_INIT 10000
+#define XGBE_CDR_DELAY_INC 10000
+#define XGBE_CDR_DELAY_MAX 100000
+
+/* RRC frequency during link status check */
+#define XGBE_RRC_FREQUENCY 10
+
enum xgbe_port_mode {
XGBE_PORT_MODE_RSVD = 0,
XGBE_PORT_MODE_BACKPLANE,
@@ -245,6 +253,10 @@ enum xgbe_sfp_speed {
#define XGBE_SFP_BASE_VENDOR_SN 4
#define XGBE_SFP_BASE_VENDOR_SN_LEN 16
+#define XGBE_SFP_EXTD_OPT1 1
+#define XGBE_SFP_EXTD_OPT1_RX_LOS BIT(1)
+#define XGBE_SFP_EXTD_OPT1_TX_FAULT BIT(3)
+
#define XGBE_SFP_EXTD_DIAG 28
#define XGBE_SFP_EXTD_DIAG_ADDR_CHANGE BIT(2)
@@ -324,6 +336,7 @@ struct xgbe_phy_data {
unsigned int sfp_gpio_address;
unsigned int sfp_gpio_mask;
+ unsigned int sfp_gpio_inputs;
unsigned int sfp_gpio_rx_los;
unsigned int sfp_gpio_tx_fault;
unsigned int sfp_gpio_mod_absent;
@@ -355,6 +368,10 @@ struct xgbe_phy_data {
unsigned int redrv_addr;
unsigned int redrv_lane;
unsigned int redrv_model;
+
+ /* KR AN support */
+ unsigned int phy_cdr_notrack;
+ unsigned int phy_cdr_delay;
};
/* I2C, MDIO and GPIO lines are muxed, so only one device at a time */
@@ -974,6 +991,49 @@ static void xgbe_phy_sfp_external_phy(struct xgbe_prv_data *pdata)
phy_data->sfp_phy_avail = 1;
}
+static bool xgbe_phy_check_sfp_rx_los(struct xgbe_phy_data *phy_data)
+{
+ u8 *sfp_extd = phy_data->sfp_eeprom.extd;
+
+ if (!(sfp_extd[XGBE_SFP_EXTD_OPT1] & XGBE_SFP_EXTD_OPT1_RX_LOS))
+ return false;
+
+ if (phy_data->sfp_gpio_mask & XGBE_GPIO_NO_RX_LOS)
+ return false;
+
+ if (phy_data->sfp_gpio_inputs & (1 << phy_data->sfp_gpio_rx_los))
+ return true;
+
+ return false;
+}
+
+static bool xgbe_phy_check_sfp_tx_fault(struct xgbe_phy_data *phy_data)
+{
+ u8 *sfp_extd = phy_data->sfp_eeprom.extd;
+
+ if (!(sfp_extd[XGBE_SFP_EXTD_OPT1] & XGBE_SFP_EXTD_OPT1_TX_FAULT))
+ return false;
+
+ if (phy_data->sfp_gpio_mask & XGBE_GPIO_NO_TX_FAULT)
+ return false;
+
+ if (phy_data->sfp_gpio_inputs & (1 << phy_data->sfp_gpio_tx_fault))
+ return true;
+
+ return false;
+}
+
+static bool xgbe_phy_check_sfp_mod_absent(struct xgbe_phy_data *phy_data)
+{
+ if (phy_data->sfp_gpio_mask & XGBE_GPIO_NO_MOD_ABSENT)
+ return false;
+
+ if (phy_data->sfp_gpio_inputs & (1 << phy_data->sfp_gpio_mod_absent))
+ return true;
+
+ return false;
+}
+
static bool xgbe_phy_belfuse_parse_quirks(struct xgbe_prv_data *pdata)
{
struct xgbe_phy_data *phy_data = pdata->phy_data;
@@ -1019,6 +1079,10 @@ static void xgbe_phy_sfp_parse_eeprom(struct xgbe_prv_data *pdata)
if (sfp_base[XGBE_SFP_BASE_EXT_ID] != XGBE_SFP_EXT_ID_SFP)
return;
+ /* Update transceiver signals (eeprom extd/options) */
+ phy_data->sfp_tx_fault = xgbe_phy_check_sfp_tx_fault(phy_data);
+ phy_data->sfp_rx_los = xgbe_phy_check_sfp_rx_los(phy_data);
+
if (xgbe_phy_sfp_parse_quirks(pdata))
return;
@@ -1184,7 +1248,6 @@ put:
static void xgbe_phy_sfp_signals(struct xgbe_prv_data *pdata)
{
struct xgbe_phy_data *phy_data = pdata->phy_data;
- unsigned int gpio_input;
u8 gpio_reg, gpio_ports[2];
int ret;
@@ -1199,23 +1262,9 @@ static void xgbe_phy_sfp_signals(struct xgbe_prv_data *pdata)
return;
}
- gpio_input = (gpio_ports[1] << 8) | gpio_ports[0];
-
- if (phy_data->sfp_gpio_mask & XGBE_GPIO_NO_MOD_ABSENT) {
- /* No GPIO, just assume the module is present for now */
- phy_data->sfp_mod_absent = 0;
- } else {
- if (!(gpio_input & (1 << phy_data->sfp_gpio_mod_absent)))
- phy_data->sfp_mod_absent = 0;
- }
-
- if (!(phy_data->sfp_gpio_mask & XGBE_GPIO_NO_RX_LOS) &&
- (gpio_input & (1 << phy_data->sfp_gpio_rx_los)))
- phy_data->sfp_rx_los = 1;
+ phy_data->sfp_gpio_inputs = (gpio_ports[1] << 8) | gpio_ports[0];
- if (!(phy_data->sfp_gpio_mask & XGBE_GPIO_NO_TX_FAULT) &&
- (gpio_input & (1 << phy_data->sfp_gpio_tx_fault)))
- phy_data->sfp_tx_fault = 1;
+ phy_data->sfp_mod_absent = xgbe_phy_check_sfp_mod_absent(phy_data);
}
static void xgbe_phy_sfp_mod_absent(struct xgbe_prv_data *pdata)
@@ -2361,7 +2410,7 @@ static int xgbe_phy_link_status(struct xgbe_prv_data *pdata, int *an_restart)
return 1;
/* No link, attempt a receiver reset cycle */
- if (phy_data->rrc_count++) {
+ if (phy_data->rrc_count++ > XGBE_RRC_FREQUENCY) {
phy_data->rrc_count = 0;
xgbe_phy_rrc(pdata);
}
@@ -2669,6 +2718,103 @@ static bool xgbe_phy_port_enabled(struct xgbe_prv_data *pdata)
return true;
}
+static void xgbe_phy_cdr_track(struct xgbe_prv_data *pdata)
+{
+ struct xgbe_phy_data *phy_data = pdata->phy_data;
+
+ if (!pdata->debugfs_an_cdr_workaround)
+ return;
+
+ if (!phy_data->phy_cdr_notrack)
+ return;
+
+ usleep_range(phy_data->phy_cdr_delay,
+ phy_data->phy_cdr_delay + 500);
+
+ XMDIO_WRITE_BITS(pdata, MDIO_MMD_PMAPMD, MDIO_VEND2_PMA_CDR_CONTROL,
+ XGBE_PMA_CDR_TRACK_EN_MASK,
+ XGBE_PMA_CDR_TRACK_EN_ON);
+
+ phy_data->phy_cdr_notrack = 0;
+}
+
+static void xgbe_phy_cdr_notrack(struct xgbe_prv_data *pdata)
+{
+ struct xgbe_phy_data *phy_data = pdata->phy_data;
+
+ if (!pdata->debugfs_an_cdr_workaround)
+ return;
+
+ if (phy_data->phy_cdr_notrack)
+ return;
+
+ XMDIO_WRITE_BITS(pdata, MDIO_MMD_PMAPMD, MDIO_VEND2_PMA_CDR_CONTROL,
+ XGBE_PMA_CDR_TRACK_EN_MASK,
+ XGBE_PMA_CDR_TRACK_EN_OFF);
+
+ xgbe_phy_rrc(pdata);
+
+ phy_data->phy_cdr_notrack = 1;
+}
+
+static void xgbe_phy_kr_training_post(struct xgbe_prv_data *pdata)
+{
+ if (!pdata->debugfs_an_cdr_track_early)
+ xgbe_phy_cdr_track(pdata);
+}
+
+static void xgbe_phy_kr_training_pre(struct xgbe_prv_data *pdata)
+{
+ if (pdata->debugfs_an_cdr_track_early)
+ xgbe_phy_cdr_track(pdata);
+}
+
+static void xgbe_phy_an_post(struct xgbe_prv_data *pdata)
+{
+ struct xgbe_phy_data *phy_data = pdata->phy_data;
+
+ switch (pdata->an_mode) {
+ case XGBE_AN_MODE_CL73:
+ case XGBE_AN_MODE_CL73_REDRV:
+ if (phy_data->cur_mode != XGBE_MODE_KR)
+ break;
+
+ xgbe_phy_cdr_track(pdata);
+
+ switch (pdata->an_result) {
+ case XGBE_AN_READY:
+ case XGBE_AN_COMPLETE:
+ break;
+ default:
+ if (phy_data->phy_cdr_delay < XGBE_CDR_DELAY_MAX)
+ phy_data->phy_cdr_delay += XGBE_CDR_DELAY_INC;
+ else
+ phy_data->phy_cdr_delay = XGBE_CDR_DELAY_INIT;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void xgbe_phy_an_pre(struct xgbe_prv_data *pdata)
+{
+ struct xgbe_phy_data *phy_data = pdata->phy_data;
+
+ switch (pdata->an_mode) {
+ case XGBE_AN_MODE_CL73:
+ case XGBE_AN_MODE_CL73_REDRV:
+ if (phy_data->cur_mode != XGBE_MODE_KR)
+ break;
+
+ xgbe_phy_cdr_notrack(pdata);
+ break;
+ default:
+ break;
+ }
+}
+
static void xgbe_phy_stop(struct xgbe_prv_data *pdata)
{
struct xgbe_phy_data *phy_data = pdata->phy_data;
@@ -2680,6 +2826,9 @@ static void xgbe_phy_stop(struct xgbe_prv_data *pdata)
xgbe_phy_sfp_reset(phy_data);
xgbe_phy_sfp_mod_absent(pdata);
+ /* Reset CDR support */
+ xgbe_phy_cdr_track(pdata);
+
/* Power off the PHY */
xgbe_phy_power_off(pdata);
@@ -2712,6 +2861,9 @@ static int xgbe_phy_start(struct xgbe_prv_data *pdata)
/* Start in highest supported mode */
xgbe_phy_set_mode(pdata, phy_data->start_mode);
+ /* Reset CDR support */
+ xgbe_phy_cdr_track(pdata);
+
/* After starting the I2C controller, we can check for an SFP */
switch (phy_data->port_mode) {
case XGBE_PORT_MODE_SFP:
@@ -3019,6 +3171,8 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata)
}
}
+ phy_data->phy_cdr_delay = XGBE_CDR_DELAY_INIT;
+
/* Register for driving external PHYs */
mii = devm_mdiobus_alloc(pdata->dev);
if (!mii) {
@@ -3071,4 +3225,10 @@ void xgbe_init_function_ptrs_phy_v2(struct xgbe_phy_if *phy_if)
phy_impl->an_advertising = xgbe_phy_an_advertising;
phy_impl->an_outcome = xgbe_phy_an_outcome;
+
+ phy_impl->an_pre = xgbe_phy_an_pre;
+ phy_impl->an_post = xgbe_phy_an_post;
+
+ phy_impl->kr_training_pre = xgbe_phy_kr_training_pre;
+ phy_impl->kr_training_post = xgbe_phy_kr_training_post;
}
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h
index ad102c8bac7b..95d4b56448c6 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe.h
+++ b/drivers/net/ethernet/amd/xgbe/xgbe.h
@@ -833,6 +833,7 @@ struct xgbe_hw_if {
/* This structure represents implementation specific routines for an
* implementation of a PHY. All routines are required unless noted below.
* Optional routines:
+ * an_pre, an_post
* kr_training_pre, kr_training_post
*/
struct xgbe_phy_impl_if {
@@ -875,6 +876,10 @@ struct xgbe_phy_impl_if {
/* Process results of auto-negotiation */
enum xgbe_mode (*an_outcome)(struct xgbe_prv_data *);
+ /* Pre/Post auto-negotiation support */
+ void (*an_pre)(struct xgbe_prv_data *);
+ void (*an_post)(struct xgbe_prv_data *);
+
/* Pre/Post KR training enablement support */
void (*kr_training_pre)(struct xgbe_prv_data *);
void (*kr_training_post)(struct xgbe_prv_data *);
@@ -989,6 +994,7 @@ struct xgbe_version_data {
unsigned int irq_reissue_support;
unsigned int tx_desc_prefetch;
unsigned int rx_desc_prefetch;
+ unsigned int an_cdr_workaround;
};
struct xgbe_vxlan_data {
@@ -1257,6 +1263,9 @@ struct xgbe_prv_data {
unsigned int debugfs_xprop_reg;
unsigned int debugfs_xi2c_reg;
+
+ bool debugfs_an_cdr_workaround;
+ bool debugfs_an_cdr_track_early;
};
/* Function prototypes*/
diff --git a/drivers/net/ethernet/apple/macmace.c b/drivers/net/ethernet/apple/macmace.c
index f17a160dbff2..137cbb470af2 100644
--- a/drivers/net/ethernet/apple/macmace.c
+++ b/drivers/net/ethernet/apple/macmace.c
@@ -247,8 +247,8 @@ static int mace_probe(struct platform_device *pdev)
dev->netdev_ops = &mace_netdev_ops;
dev->watchdog_timeo = TX_TIMEOUT;
- printk(KERN_INFO "%s: 68K MACE, hardware address %pM\n",
- dev->name, dev->dev_addr);
+ pr_info("Onboard MACE, hardware address %pM, chip revision 0x%04X\n",
+ dev->dev_addr, mp->chipid);
err = register_netdev(dev);
if (!err)
@@ -589,7 +589,6 @@ static irqreturn_t mace_interrupt(int irq, void *dev_id)
else if (fs & (UFLO|LCOL|RTRY)) {
++dev->stats.tx_aborted_errors;
if (mb->xmtfs & UFLO) {
- printk(KERN_ERR "%s: DMA underrun.\n", dev->name);
dev->stats.tx_fifo_errors++;
mace_txdma_reset(dev);
}
@@ -644,10 +643,8 @@ static void mace_dma_rx_frame(struct net_device *dev, struct mace_frame *mf)
if (frame_status & (RS_OFLO | RS_CLSN | RS_FRAMERR | RS_FCSERR)) {
dev->stats.rx_errors++;
- if (frame_status & RS_OFLO) {
- printk(KERN_DEBUG "%s: fifo overflow.\n", dev->name);
+ if (frame_status & RS_OFLO)
dev->stats.rx_fifo_errors++;
- }
if (frame_status & RS_CLSN)
dev->stats.collisions++;
if (frame_status & RS_FRAMERR)
@@ -770,18 +767,4 @@ static struct platform_driver mac_mace_driver = {
},
};
-static int __init mac_mace_init_module(void)
-{
- if (!MACH_IS_MAC)
- return -ENODEV;
-
- return platform_driver_register(&mac_mace_driver);
-}
-
-static void __exit mac_mace_cleanup_module(void)
-{
- platform_driver_unregister(&mac_mace_driver);
-}
-
-module_init(mac_mace_init_module);
-module_exit(mac_mace_cleanup_module);
+module_platform_driver(mac_mace_driver);
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c
index c96a92118b8b..32f6d2e24d66 100644
--- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c
+++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c
@@ -951,9 +951,11 @@ void aq_nic_shutdown(struct aq_nic_s *self)
netif_device_detach(self->ndev);
- err = aq_nic_stop(self);
- if (err < 0)
- goto err_exit;
+ if (netif_running(self->ndev)) {
+ err = aq_nic_stop(self);
+ if (err < 0)
+ goto err_exit;
+ }
aq_nic_deinit(self);
err_exit:
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/Makefile b/drivers/net/ethernet/aquantia/atlantic/hw_atl/Makefile
new file mode 100644
index 000000000000..805fa28f391a
--- /dev/null
+++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+# kbuild requires Makefile in a directory to build individual objects
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c
index d3b847ec7465..e652d86b87d4 100644
--- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c
+++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c
@@ -48,6 +48,8 @@
#define FORCE_FLASHLESS 0
static int hw_atl_utils_ver_match(u32 ver_expected, u32 ver_actual);
+static int hw_atl_utils_mpi_set_state(struct aq_hw_s *self,
+ enum hal_atl_utils_fw_state_e state);
int hw_atl_utils_initfw(struct aq_hw_s *self, const struct aq_fw_ops **fw_ops)
{
@@ -247,6 +249,20 @@ int hw_atl_utils_soft_reset(struct aq_hw_s *self)
self->rbl_enabled = (boot_exit_code != 0);
+ /* FW 1.x may bootup in an invalid POWER state (WOL feature).
+ * We should work around this by forcing its state back to DEINIT
+ */
+ if (!hw_atl_utils_ver_match(HW_ATL_FW_VER_1X,
+ aq_hw_read_reg(self,
+ HW_ATL_MPI_FW_VERSION))) {
+ int err = 0;
+
+ hw_atl_utils_mpi_set_state(self, MPI_DEINIT);
+ AQ_HW_WAIT_FOR((aq_hw_read_reg(self, HW_ATL_MPI_STATE_ADR) &
+ HW_ATL_MPI_STATE_MSK) == MPI_DEINIT,
+ 10, 1000U);
+ }
+
if (self->rbl_enabled)
return hw_atl_utils_soft_reset_rbl(self);
else
@@ -505,7 +521,7 @@ void hw_atl_utils_mpi_read_stats(struct aq_hw_s *self,
err_exit:;
}
-int hw_atl_utils_mpi_set_speed(struct aq_hw_s *self, u32 speed)
+static int hw_atl_utils_mpi_set_speed(struct aq_hw_s *self, u32 speed)
{
u32 val = aq_hw_read_reg(self, HW_ATL_MPI_CONTROL_ADR);
diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c
index 3fc549b88c43..f9a3c1a76d5d 100644
--- a/drivers/net/ethernet/broadcom/bcmsysport.c
+++ b/drivers/net/ethernet/broadcom/bcmsysport.c
@@ -574,6 +574,34 @@ static int bcm_sysport_set_wol(struct net_device *dev,
return 0;
}
+static void bcm_sysport_set_rx_coalesce(struct bcm_sysport_priv *priv,
+ u32 usecs, u32 pkts)
+{
+ u32 reg;
+
+ reg = rdma_readl(priv, RDMA_MBDONE_INTR);
+ reg &= ~(RDMA_INTR_THRESH_MASK |
+ RDMA_TIMEOUT_MASK << RDMA_TIMEOUT_SHIFT);
+ reg |= pkts;
+ reg |= DIV_ROUND_UP(usecs * 1000, 8192) << RDMA_TIMEOUT_SHIFT;
+ rdma_writel(priv, reg, RDMA_MBDONE_INTR);
+}
+
+static void bcm_sysport_set_tx_coalesce(struct bcm_sysport_tx_ring *ring,
+ struct ethtool_coalesce *ec)
+{
+ struct bcm_sysport_priv *priv = ring->priv;
+ u32 reg;
+
+ reg = tdma_readl(priv, TDMA_DESC_RING_INTR_CONTROL(ring->index));
+ reg &= ~(RING_INTR_THRESH_MASK |
+ RING_TIMEOUT_MASK << RING_TIMEOUT_SHIFT);
+ reg |= ec->tx_max_coalesced_frames;
+ reg |= DIV_ROUND_UP(ec->tx_coalesce_usecs * 1000, 8192) <<
+ RING_TIMEOUT_SHIFT;
+ tdma_writel(priv, reg, TDMA_DESC_RING_INTR_CONTROL(ring->index));
+}
+
static int bcm_sysport_get_coalesce(struct net_device *dev,
struct ethtool_coalesce *ec)
{
@@ -589,6 +617,7 @@ static int bcm_sysport_get_coalesce(struct net_device *dev,
ec->rx_coalesce_usecs = (reg >> RDMA_TIMEOUT_SHIFT) * 8192 / 1000;
ec->rx_max_coalesced_frames = reg & RDMA_INTR_THRESH_MASK;
+ ec->use_adaptive_rx_coalesce = priv->dim.use_dim;
return 0;
}
@@ -597,8 +626,9 @@ static int bcm_sysport_set_coalesce(struct net_device *dev,
struct ethtool_coalesce *ec)
{
struct bcm_sysport_priv *priv = netdev_priv(dev);
+ struct net_dim_cq_moder moder;
+ u32 usecs, pkts;
unsigned int i;
- u32 reg;
/* Base system clock is 125Mhz, DMA timeout is this reference clock
* divided by 1024, which yield roughly 8.192 us, our maximum value has
@@ -611,26 +641,28 @@ static int bcm_sysport_set_coalesce(struct net_device *dev,
return -EINVAL;
if ((ec->tx_coalesce_usecs == 0 && ec->tx_max_coalesced_frames == 0) ||
- (ec->rx_coalesce_usecs == 0 && ec->rx_max_coalesced_frames == 0))
+ (ec->rx_coalesce_usecs == 0 && ec->rx_max_coalesced_frames == 0) ||
+ ec->use_adaptive_tx_coalesce)
return -EINVAL;
- for (i = 0; i < dev->num_tx_queues; i++) {
- reg = tdma_readl(priv, TDMA_DESC_RING_INTR_CONTROL(i));
- reg &= ~(RING_INTR_THRESH_MASK |
- RING_TIMEOUT_MASK << RING_TIMEOUT_SHIFT);
- reg |= ec->tx_max_coalesced_frames;
- reg |= DIV_ROUND_UP(ec->tx_coalesce_usecs * 1000, 8192) <<
- RING_TIMEOUT_SHIFT;
- tdma_writel(priv, reg, TDMA_DESC_RING_INTR_CONTROL(i));
+ for (i = 0; i < dev->num_tx_queues; i++)
+ bcm_sysport_set_tx_coalesce(&priv->tx_rings[i], ec);
+
+ priv->rx_coalesce_usecs = ec->rx_coalesce_usecs;
+ priv->rx_max_coalesced_frames = ec->rx_max_coalesced_frames;
+ usecs = priv->rx_coalesce_usecs;
+ pkts = priv->rx_max_coalesced_frames;
+
+ if (ec->use_adaptive_rx_coalesce && !priv->dim.use_dim) {
+ moder = net_dim_get_def_profile(priv->dim.dim.mode);
+ usecs = moder.usec;
+ pkts = moder.pkts;
}
- reg = rdma_readl(priv, RDMA_MBDONE_INTR);
- reg &= ~(RDMA_INTR_THRESH_MASK |
- RDMA_TIMEOUT_MASK << RDMA_TIMEOUT_SHIFT);
- reg |= ec->rx_max_coalesced_frames;
- reg |= DIV_ROUND_UP(ec->rx_coalesce_usecs * 1000, 8192) <<
- RDMA_TIMEOUT_SHIFT;
- rdma_writel(priv, reg, RDMA_MBDONE_INTR);
+ priv->dim.use_dim = ec->use_adaptive_rx_coalesce;
+
+ /* Apply desired coalescing parameters */
+ bcm_sysport_set_rx_coalesce(priv, usecs, pkts);
return 0;
}
@@ -709,6 +741,7 @@ static unsigned int bcm_sysport_desc_rx(struct bcm_sysport_priv *priv,
struct bcm_sysport_stats64 *stats64 = &priv->stats64;
struct net_device *ndev = priv->netdev;
unsigned int processed = 0, to_process;
+ unsigned int processed_bytes = 0;
struct bcm_sysport_cb *cb;
struct sk_buff *skb;
unsigned int p_index;
@@ -800,6 +833,7 @@ static unsigned int bcm_sysport_desc_rx(struct bcm_sysport_priv *priv,
*/
skb_pull(skb, sizeof(*rsb) + 2);
len -= (sizeof(*rsb) + 2);
+ processed_bytes += len;
/* UniMAC may forward CRC */
if (priv->crc_fwd) {
@@ -824,6 +858,9 @@ next:
priv->rx_read_ptr = 0;
}
+ priv->dim.packets = processed;
+ priv->dim.bytes = processed_bytes;
+
return processed;
}
@@ -972,6 +1009,7 @@ static int bcm_sysport_poll(struct napi_struct *napi, int budget)
{
struct bcm_sysport_priv *priv =
container_of(napi, struct bcm_sysport_priv, napi);
+ struct net_dim_sample dim_sample;
unsigned int work_done = 0;
work_done = bcm_sysport_desc_rx(priv, budget);
@@ -994,6 +1032,12 @@ static int bcm_sysport_poll(struct napi_struct *napi, int budget)
intrl2_0_mask_clear(priv, INTRL2_0_RDMA_MBDONE);
}
+ if (priv->dim.use_dim) {
+ net_dim_sample(priv->dim.event_ctr, priv->dim.packets,
+ priv->dim.bytes, &dim_sample);
+ net_dim(&priv->dim.dim, dim_sample);
+ }
+
return work_done;
}
@@ -1012,6 +1056,20 @@ static void bcm_sysport_resume_from_wol(struct bcm_sysport_priv *priv)
netif_dbg(priv, wol, priv->netdev, "resumed from WOL\n");
}
+static void bcm_sysport_dim_work(struct work_struct *work)
+{
+ struct net_dim *dim = container_of(work, struct net_dim, work);
+ struct bcm_sysport_net_dim *ndim =
+ container_of(dim, struct bcm_sysport_net_dim, dim);
+ struct bcm_sysport_priv *priv =
+ container_of(ndim, struct bcm_sysport_priv, dim);
+ struct net_dim_cq_moder cur_profile =
+ net_dim_get_profile(dim->mode, dim->profile_ix);
+
+ bcm_sysport_set_rx_coalesce(priv, cur_profile.usec, cur_profile.pkts);
+ dim->state = NET_DIM_START_MEASURE;
+}
+
/* RX and misc interrupt routine */
static irqreturn_t bcm_sysport_rx_isr(int irq, void *dev_id)
{
@@ -1030,6 +1088,7 @@ static irqreturn_t bcm_sysport_rx_isr(int irq, void *dev_id)
}
if (priv->irq0_stat & INTRL2_0_RDMA_MBDONE) {
+ priv->dim.event_ctr++;
if (likely(napi_schedule_prep(&priv->napi))) {
/* disable RX interrupts */
intrl2_0_mask_set(priv, INTRL2_0_RDMA_MBDONE);
@@ -1133,7 +1192,7 @@ static struct sk_buff *bcm_sysport_insert_tsb(struct sk_buff *skb,
u32 csum_info;
u8 ip_proto;
u16 csum_start;
- u16 ip_ver;
+ __be16 ip_ver;
/* Re-allocate SKB if needed */
if (unlikely(skb_headroom(skb) < sizeof(*tsb))) {
@@ -1152,12 +1211,12 @@ static struct sk_buff *bcm_sysport_insert_tsb(struct sk_buff *skb,
memset(tsb, 0, sizeof(*tsb));
if (skb->ip_summed == CHECKSUM_PARTIAL) {
- ip_ver = htons(skb->protocol);
+ ip_ver = skb->protocol;
switch (ip_ver) {
- case ETH_P_IP:
+ case htons(ETH_P_IP):
ip_proto = ip_hdr(skb)->protocol;
break;
- case ETH_P_IPV6:
+ case htons(ETH_P_IPV6):
ip_proto = ipv6_hdr(skb)->nexthdr;
break;
default:
@@ -1171,7 +1230,8 @@ static struct sk_buff *bcm_sysport_insert_tsb(struct sk_buff *skb,
if (ip_proto == IPPROTO_TCP || ip_proto == IPPROTO_UDP) {
csum_info |= L4_LENGTH_VALID;
- if (ip_proto == IPPROTO_UDP && ip_ver == ETH_P_IP)
+ if (ip_proto == IPPROTO_UDP &&
+ ip_ver == htons(ETH_P_IP))
csum_info |= L4_UDP;
} else {
csum_info = 0;
@@ -1354,6 +1414,37 @@ out:
phy_print_status(phydev);
}
+static void bcm_sysport_init_dim(struct bcm_sysport_priv *priv,
+ void (*cb)(struct work_struct *work))
+{
+ struct bcm_sysport_net_dim *dim = &priv->dim;
+
+ INIT_WORK(&dim->dim.work, cb);
+ dim->dim.mode = NET_DIM_CQ_PERIOD_MODE_START_FROM_EQE;
+ dim->event_ctr = 0;
+ dim->packets = 0;
+ dim->bytes = 0;
+}
+
+static void bcm_sysport_init_rx_coalesce(struct bcm_sysport_priv *priv)
+{
+ struct bcm_sysport_net_dim *dim = &priv->dim;
+ struct net_dim_cq_moder moder;
+ u32 usecs, pkts;
+
+ usecs = priv->rx_coalesce_usecs;
+ pkts = priv->rx_max_coalesced_frames;
+
+ /* If DIM was enabled, re-apply default parameters */
+ if (dim->use_dim) {
+ moder = net_dim_get_def_profile(dim->dim.mode);
+ usecs = moder.usec;
+ pkts = moder.pkts;
+ }
+
+ bcm_sysport_set_rx_coalesce(priv, usecs, pkts);
+}
+
static int bcm_sysport_init_tx_ring(struct bcm_sysport_priv *priv,
unsigned int index)
{
@@ -1594,8 +1685,6 @@ static int bcm_sysport_init_rx_ring(struct bcm_sysport_priv *priv)
rdma_writel(priv, 0, RDMA_END_ADDR_HI);
rdma_writel(priv, priv->num_rx_desc_words - 1, RDMA_END_ADDR_LO);
- rdma_writel(priv, 1, RDMA_MBDONE_INTR);
-
netif_dbg(priv, hw, priv->netdev,
"RDMA cfg, num_rx_bds=%d, rx_bds=%p\n",
priv->num_rx_bds, priv->rx_bds);
@@ -1763,6 +1852,8 @@ static void bcm_sysport_netif_start(struct net_device *dev)
struct bcm_sysport_priv *priv = netdev_priv(dev);
/* Enable NAPI */
+ bcm_sysport_init_dim(priv, bcm_sysport_dim_work);
+ bcm_sysport_init_rx_coalesce(priv);
napi_enable(&priv->napi);
/* Enable RX interrupt and TX ring full interrupt */
@@ -1948,6 +2039,7 @@ static void bcm_sysport_netif_stop(struct net_device *dev)
/* stop all software from updating hardware */
netif_tx_stop_all_queues(dev);
napi_disable(&priv->napi);
+ cancel_work_sync(&priv->dim.dim.work);
phy_stop(dev->phydev);
/* mask all interrupts */
@@ -2267,6 +2359,7 @@ static int bcm_sysport_probe(struct platform_device *pdev)
/* libphy will adjust the link state accordingly */
netif_carrier_off(dev);
+ priv->rx_max_coalesced_frames = 1;
u64_stats_init(&priv->syncp);
priv->dsa_notifier.notifier_call = bcm_sysport_dsa_notifier;
diff --git a/drivers/net/ethernet/broadcom/bcmsysport.h b/drivers/net/ethernet/broadcom/bcmsysport.h
index 19c91c76e327..d6e5d0cbf3a3 100644
--- a/drivers/net/ethernet/broadcom/bcmsysport.h
+++ b/drivers/net/ethernet/broadcom/bcmsysport.h
@@ -12,6 +12,7 @@
#define __BCM_SYSPORT_H
#include <linux/if_vlan.h>
+#include <linux/net_dim.h>
/* Receive/transmit descriptor format */
#define DESC_ADDR_HI_STATUS_LEN 0x00
@@ -695,6 +696,14 @@ struct bcm_sysport_hw_params {
unsigned int num_rx_desc_words;
};
+struct bcm_sysport_net_dim {
+ u16 use_dim;
+ u16 event_ctr;
+ unsigned long packets;
+ unsigned long bytes;
+ struct net_dim dim;
+};
+
/* Software view of the TX ring */
struct bcm_sysport_tx_ring {
spinlock_t lock; /* Ring lock for tx reclaim/xmit */
@@ -743,6 +752,10 @@ struct bcm_sysport_priv {
unsigned int rx_read_ptr;
unsigned int rx_c_index;
+ struct bcm_sysport_net_dim dim;
+ u32 rx_max_coalesced_frames;
+ u32 rx_coalesce_usecs;
+
/* PHY device */
struct device_node *phy_dn;
phy_interface_t phy_interface;
diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c
index 8eef9fb6b1fe..e6ea8e61f96d 100644
--- a/drivers/net/ethernet/broadcom/bgmac.c
+++ b/drivers/net/ethernet/broadcom/bgmac.c
@@ -533,7 +533,8 @@ static void bgmac_dma_tx_ring_free(struct bgmac *bgmac,
int i;
for (i = 0; i < BGMAC_TX_RING_SLOTS; i++) {
- int len = dma_desc[i].ctl1 & BGMAC_DESC_CTL1_LEN;
+ u32 ctl1 = le32_to_cpu(dma_desc[i].ctl1);
+ unsigned int len = ctl1 & BGMAC_DESC_CTL1_LEN;
slot = &ring->slots[i];
dev_kfree_skb(slot->skb);
@@ -1190,7 +1191,7 @@ static int bgmac_open(struct net_device *net_dev)
bgmac_chip_init(bgmac);
err = request_irq(bgmac->irq, bgmac_interrupt, IRQF_SHARED,
- KBUILD_MODNAME, net_dev);
+ net_dev->name, net_dev);
if (err < 0) {
dev_err(bgmac->dev, "IRQ request error: %d!\n", err);
bgmac_dma_cleanup(bgmac);
@@ -1492,6 +1493,8 @@ int bgmac_enet_probe(struct bgmac *bgmac)
struct net_device *net_dev = bgmac->net_dev;
int err;
+ bgmac_chip_intrs_off(bgmac);
+
net_dev->irq = bgmac->irq;
SET_NETDEV_DEV(net_dev, bgmac->dev);
dev_set_drvdata(bgmac->dev, bgmac);
diff --git a/drivers/net/ethernet/broadcom/bgmac.h b/drivers/net/ethernet/broadcom/bgmac.h
index 4040d846da8e..40d02fec2747 100644
--- a/drivers/net/ethernet/broadcom/bgmac.h
+++ b/drivers/net/ethernet/broadcom/bgmac.h
@@ -479,9 +479,9 @@ struct bgmac_rx_header {
struct bgmac {
union {
struct {
- void *base;
- void *idm_base;
- void *nicpm_base;
+ void __iomem *base;
+ void __iomem *idm_base;
+ void __iomem *nicpm_base;
} plat;
struct {
struct bcma_device *core;
diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c
index 5e34b34f7740..9ffc4a8c5fc7 100644
--- a/drivers/net/ethernet/broadcom/bnx2.c
+++ b/drivers/net/ethernet/broadcom/bnx2.c
@@ -87,7 +87,7 @@ MODULE_FIRMWARE(FW_RV2P_FILE_09_Ax);
static int disable_msi = 0;
-module_param(disable_msi, int, S_IRUGO);
+module_param(disable_msi, int, 0444);
MODULE_PARM_DESC(disable_msi, "Disable Message Signaled Interrupt (MSI)");
typedef enum {
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
index 352beff796ae..d847e1b9c37b 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
@@ -166,6 +166,12 @@ do { \
#define REG_RD8(bp, offset) readb(REG_ADDR(bp, offset))
#define REG_RD16(bp, offset) readw(REG_ADDR(bp, offset))
+#define REG_WR_RELAXED(bp, offset, val) \
+ writel_relaxed((u32)val, REG_ADDR(bp, offset))
+
+#define REG_WR16_RELAXED(bp, offset, val) \
+ writew_relaxed((u16)val, REG_ADDR(bp, offset))
+
#define REG_WR(bp, offset, val) writel((u32)val, REG_ADDR(bp, offset))
#define REG_WR8(bp, offset, val) writeb((u8)val, REG_ADDR(bp, offset))
#define REG_WR16(bp, offset, val) writew((u16)val, REG_ADDR(bp, offset))
@@ -758,10 +764,8 @@ struct bnx2x_fastpath {
#if (BNX2X_DB_SHIFT < BNX2X_DB_MIN_SHIFT)
#error "Min DB doorbell stride is 8"
#endif
-#define DOORBELL(bp, cid, val) \
- do { \
- writel((u32)(val), bp->doorbells + (bp->db_size * (cid))); \
- } while (0)
+#define DOORBELL_RELAXED(bp, cid, val) \
+ writel_relaxed((u32)(val), (bp)->doorbells + ((bp)->db_size * (cid)))
/* TX CSUM helpers */
#define SKB_CS_OFF(skb) (offsetof(struct tcphdr, check) - \
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
index d7c98e807ca8..95871576ab92 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
@@ -4153,9 +4153,10 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
wmb();
txdata->tx_db.data.prod += nbd;
- barrier();
+ /* make sure descriptor update is observed by HW */
+ wmb();
- DOORBELL(bp, txdata->cid, txdata->tx_db.raw);
+ DOORBELL_RELAXED(bp, txdata->cid, txdata->tx_db.raw);
mmiowb();
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h
index a5265e1344f1..a8ce5c55bbb0 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h
@@ -522,8 +522,8 @@ static inline void bnx2x_update_rx_prod(struct bnx2x *bp,
wmb();
for (i = 0; i < sizeof(rx_prods)/4; i++)
- REG_WR(bp, fp->ustorm_rx_prods_offset + i*4,
- ((u32 *)&rx_prods)[i]);
+ REG_WR_RELAXED(bp, fp->ustorm_rx_prods_offset + i * 4,
+ ((u32 *)&rx_prods)[i]);
mmiowb(); /* keep prod updates ordered */
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
index 1e33abde4a3e..da18aa239acb 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
@@ -2591,8 +2591,9 @@ static int bnx2x_run_loopback(struct bnx2x *bp, int loopback_mode)
wmb();
txdata->tx_db.data.prod += 2;
- barrier();
- DOORBELL(bp, txdata->cid, txdata->tx_db.raw);
+ /* make sure descriptor update is observed by the HW */
+ wmb();
+ DOORBELL_RELAXED(bp, txdata->cid, txdata->tx_db.raw);
mmiowb();
barrier();
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
index b8388e93520a..c766ae23bc74 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
@@ -97,29 +97,29 @@ MODULE_FIRMWARE(FW_FILE_NAME_E1H);
MODULE_FIRMWARE(FW_FILE_NAME_E2);
int bnx2x_num_queues;
-module_param_named(num_queues, bnx2x_num_queues, int, S_IRUGO);
+module_param_named(num_queues, bnx2x_num_queues, int, 0444);
MODULE_PARM_DESC(num_queues,
" Set number of queues (default is as a number of CPUs)");
static int disable_tpa;
-module_param(disable_tpa, int, S_IRUGO);
+module_param(disable_tpa, int, 0444);
MODULE_PARM_DESC(disable_tpa, " Disable the TPA (LRO) feature");
static int int_mode;
-module_param(int_mode, int, S_IRUGO);
+module_param(int_mode, int, 0444);
MODULE_PARM_DESC(int_mode, " Force interrupt mode other than MSI-X "
"(1 INT#x; 2 MSI)");
static int dropless_fc;
-module_param(dropless_fc, int, S_IRUGO);
+module_param(dropless_fc, int, 0444);
MODULE_PARM_DESC(dropless_fc, " Pause on exhausted host ring");
static int mrrs = -1;
-module_param(mrrs, int, S_IRUGO);
+module_param(mrrs, int, 0444);
MODULE_PARM_DESC(mrrs, " Force Max Read Req Size (0..3) (for debug)");
static int debug;
-module_param(debug, int, S_IRUGO);
+module_param(debug, int, 0444);
MODULE_PARM_DESC(debug, " Default debug msglevel");
static struct workqueue_struct *bnx2x_wq;
@@ -3817,8 +3817,8 @@ static void bnx2x_sp_prod_update(struct bnx2x *bp)
*/
mb();
- REG_WR16(bp, BAR_XSTRORM_INTMEM + XSTORM_SPQ_PROD_OFFSET(func),
- bp->spq_prod_idx);
+ REG_WR16_RELAXED(bp, BAR_XSTRORM_INTMEM + XSTORM_SPQ_PROD_OFFSET(func),
+ bp->spq_prod_idx);
mmiowb();
}
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c
index 76a4668c50fe..8e0a317b31f7 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c
@@ -170,7 +170,9 @@ static int bnx2x_send_msg2pf(struct bnx2x *bp, u8 *done, dma_addr_t msg_mapping)
wmb();
/* Trigger the PF FW */
- writeb(1, &zone_data->trigger.vf_pf_channel.addr_valid);
+ writeb_relaxed(1, &zone_data->trigger.vf_pf_channel.addr_valid);
+
+ mmiowb();
/* Wait for PF to complete */
while ((tout >= 0) && (!*done)) {
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index c7e5e6f09647..f83769d8047b 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -1922,7 +1922,7 @@ static int bnxt_poll_work(struct bnxt *bp, struct bnxt_napi *bnapi, int budget)
/* Sync BD data before updating doorbell */
wmb();
- bnxt_db_write(bp, db, DB_KEY_TX | prod);
+ bnxt_db_write_relaxed(bp, db, DB_KEY_TX | prod);
}
cpr->cp_raw_cons = raw_cons;
@@ -2317,6 +2317,7 @@ static int bnxt_alloc_rx_rings(struct bnxt *bp)
if (rc)
return rc;
+ ring->grp_idx = i;
rxr->rx_agg_bmap_size = bp->rx_agg_ring_mask + 1;
mem_size = rxr->rx_agg_bmap_size / 8;
rxr->rx_agg_bmap = kzalloc(mem_size, GFP_KERNEL);
@@ -2389,6 +2390,7 @@ static int bnxt_alloc_tx_rings(struct bnxt *bp)
if (rc)
return rc;
+ ring->grp_idx = txr->bnapi->index;
if (bp->tx_push_size) {
dma_addr_t mapping;
@@ -2442,8 +2444,10 @@ static void bnxt_free_cp_rings(struct bnxt *bp)
static int bnxt_alloc_cp_rings(struct bnxt *bp)
{
- int i, rc;
+ int i, rc, ulp_base_vec, ulp_msix;
+ ulp_msix = bnxt_get_ulp_msix_num(bp);
+ ulp_base_vec = bnxt_get_ulp_msix_base(bp);
for (i = 0; i < bp->cp_nr_rings; i++) {
struct bnxt_napi *bnapi = bp->bnapi[i];
struct bnxt_cp_ring_info *cpr;
@@ -2458,6 +2462,11 @@ static int bnxt_alloc_cp_rings(struct bnxt *bp)
rc = bnxt_alloc_ring(bp, ring);
if (rc)
return rc;
+
+ if (ulp_msix && i >= ulp_base_vec)
+ ring->map_idx = i + ulp_msix;
+ else
+ ring->map_idx = i;
}
return 0;
}
@@ -3059,12 +3068,21 @@ static void bnxt_free_stats(struct bnxt *bp)
u32 size, i;
struct pci_dev *pdev = bp->pdev;
+ bp->flags &= ~BNXT_FLAG_PORT_STATS;
+ bp->flags &= ~BNXT_FLAG_PORT_STATS_EXT;
+
if (bp->hw_rx_port_stats) {
dma_free_coherent(&pdev->dev, bp->hw_port_stats_size,
bp->hw_rx_port_stats,
bp->hw_rx_port_stats_map);
bp->hw_rx_port_stats = NULL;
- bp->flags &= ~BNXT_FLAG_PORT_STATS;
+ }
+
+ if (bp->hw_rx_port_stats_ext) {
+ dma_free_coherent(&pdev->dev, sizeof(struct rx_port_stats_ext),
+ bp->hw_rx_port_stats_ext,
+ bp->hw_rx_port_stats_ext_map);
+ bp->hw_rx_port_stats_ext = NULL;
}
if (!bp->bnapi)
@@ -3120,6 +3138,21 @@ static int bnxt_alloc_stats(struct bnxt *bp)
bp->hw_tx_port_stats_map = bp->hw_rx_port_stats_map +
sizeof(struct rx_port_stats) + 512;
bp->flags |= BNXT_FLAG_PORT_STATS;
+
+ /* Display extended statistics only if FW supports it */
+ if (bp->hwrm_spec_code < 0x10804 ||
+ bp->hwrm_spec_code == 0x10900)
+ return 0;
+
+ bp->hw_rx_port_stats_ext =
+ dma_zalloc_coherent(&pdev->dev,
+ sizeof(struct rx_port_stats_ext),
+ &bp->hw_rx_port_stats_ext_map,
+ GFP_KERNEL);
+ if (!bp->hw_rx_port_stats_ext)
+ return 0;
+
+ bp->flags |= BNXT_FLAG_PORT_STATS_EXT;
}
return 0;
}
@@ -3357,6 +3390,15 @@ static void bnxt_disable_int(struct bnxt *bp)
}
}
+static int bnxt_cp_num_to_irq_num(struct bnxt *bp, int n)
+{
+ struct bnxt_napi *bnapi = bp->bnapi[n];
+ struct bnxt_cp_ring_info *cpr;
+
+ cpr = &bnapi->cp_ring;
+ return cpr->cp_ring_struct.map_idx;
+}
+
static void bnxt_disable_int_sync(struct bnxt *bp)
{
int i;
@@ -3364,8 +3406,11 @@ static void bnxt_disable_int_sync(struct bnxt *bp)
atomic_inc(&bp->intr_sem);
bnxt_disable_int(bp);
- for (i = 0; i < bp->cp_nr_rings; i++)
- synchronize_irq(bp->irq_tbl[i].vector);
+ for (i = 0; i < bp->cp_nr_rings; i++) {
+ int map_idx = bnxt_cp_num_to_irq_num(bp, i);
+
+ synchronize_irq(bp->irq_tbl[map_idx].vector);
+ }
}
static void bnxt_enable_int(struct bnxt *bp)
@@ -3398,7 +3443,8 @@ static int bnxt_hwrm_do_send_msg(struct bnxt *bp, void *msg, u32 msg_len,
int i, intr_process, rc, tmo_count;
struct input *req = msg;
u32 *data = msg;
- __le32 *resp_len, *valid;
+ __le32 *resp_len;
+ u8 *valid;
u16 cp_ring_id, len = 0;
struct hwrm_err_output *resp = bp->hwrm_cmd_resp_addr;
u16 max_req_len = BNXT_HWRM_MAX_REQ_LEN;
@@ -3450,6 +3496,7 @@ static int bnxt_hwrm_do_send_msg(struct bnxt *bp, void *msg, u32 msg_len,
i = 0;
tmo_count = timeout * 40;
+ resp_len = bp->hwrm_cmd_resp_addr + HWRM_RESP_LEN_OFFSET;
if (intr_process) {
/* Wait until hwrm response cmpl interrupt is processed */
while (bp->hwrm_intr_seq_id != HWRM_SEQ_ID_INVALID &&
@@ -3462,9 +3509,11 @@ static int bnxt_hwrm_do_send_msg(struct bnxt *bp, void *msg, u32 msg_len,
le16_to_cpu(req->req_type));
return -1;
}
+ len = (le32_to_cpu(*resp_len) & HWRM_RESP_LEN_MASK) >>
+ HWRM_RESP_LEN_SFT;
+ valid = bp->hwrm_cmd_resp_addr + len - 1;
} else {
/* Check if response len is updated */
- resp_len = bp->hwrm_cmd_resp_addr + HWRM_RESP_LEN_OFFSET;
for (i = 0; i < tmo_count; i++) {
len = (le32_to_cpu(*resp_len) & HWRM_RESP_LEN_MASK) >>
HWRM_RESP_LEN_SFT;
@@ -3480,10 +3529,12 @@ static int bnxt_hwrm_do_send_msg(struct bnxt *bp, void *msg, u32 msg_len,
return -1;
}
- /* Last word of resp contains valid bit */
- valid = bp->hwrm_cmd_resp_addr + len - 4;
+ /* Last byte of resp contains valid bit */
+ valid = bp->hwrm_cmd_resp_addr + len - 1;
for (i = 0; i < 5; i++) {
- if (le32_to_cpu(*valid) & HWRM_RESP_VALID_MASK)
+ /* make sure we read from updated DMA memory */
+ dma_rmb();
+ if (*valid)
break;
udelay(1);
}
@@ -3496,6 +3547,11 @@ static int bnxt_hwrm_do_send_msg(struct bnxt *bp, void *msg, u32 msg_len,
}
}
+ /* Zero valid bit for compatibility. Valid bit in an older spec
+ * may become a new field in a newer spec. We must make sure that
+ * a new field not implemented by old spec will read zero.
+ */
+ *valid = 0;
rc = le16_to_cpu(resp->error_code);
if (rc && !silent)
netdev_err(bp->dev, "hwrm req_type 0x%x seq id 0x%x error 0x%x\n",
@@ -3577,9 +3633,13 @@ static int bnxt_hwrm_func_drv_rgtr(struct bnxt *bp)
FUNC_DRV_RGTR_REQ_ENABLES_VER);
req.os_type = cpu_to_le16(FUNC_DRV_RGTR_REQ_OS_TYPE_LINUX);
- req.ver_maj = DRV_VER_MAJ;
- req.ver_min = DRV_VER_MIN;
- req.ver_upd = DRV_VER_UPD;
+ req.flags = cpu_to_le32(FUNC_DRV_RGTR_REQ_FLAGS_16BIT_VER_MODE);
+ req.ver_maj_8b = DRV_VER_MAJ;
+ req.ver_min_8b = DRV_VER_MIN;
+ req.ver_upd_8b = DRV_VER_UPD;
+ req.ver_maj = cpu_to_le16(DRV_VER_MAJ);
+ req.ver_min = cpu_to_le16(DRV_VER_MIN);
+ req.ver_upd = cpu_to_le16(DRV_VER_UPD);
if (BNXT_PF(bp)) {
u32 data[8];
@@ -3998,6 +4058,13 @@ static int bnxt_hwrm_vnic_ctx_alloc(struct bnxt *bp, u16 vnic_id, u16 ctx_idx)
return rc;
}
+static u32 bnxt_get_roce_vnic_mode(struct bnxt *bp)
+{
+ if (bp->flags & BNXT_FLAG_ROCE_MIRROR_CAP)
+ return VNIC_CFG_REQ_FLAGS_ROCE_MIRRORING_CAPABLE_VNIC_MODE;
+ return VNIC_CFG_REQ_FLAGS_ROCE_DUAL_VNIC_MODE;
+}
+
int bnxt_hwrm_vnic_cfg(struct bnxt *bp, u16 vnic_id)
{
unsigned int ring = 0, grp_idx;
@@ -4053,8 +4120,7 @@ int bnxt_hwrm_vnic_cfg(struct bnxt *bp, u16 vnic_id)
if ((bp->flags & BNXT_FLAG_STRIP_VLAN) || def_vlan)
req.flags |= cpu_to_le32(VNIC_CFG_REQ_FLAGS_VLAN_STRIP_MODE);
if (!vnic_id && bnxt_ulp_registered(bp->edev, BNXT_ROCE_ULP))
- req.flags |=
- cpu_to_le32(VNIC_CFG_REQ_FLAGS_ROCE_DUAL_VNIC_MODE);
+ req.flags |= cpu_to_le32(bnxt_get_roce_vnic_mode(bp));
return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
}
@@ -4135,9 +4201,13 @@ static int bnxt_hwrm_vnic_qcaps(struct bnxt *bp)
mutex_lock(&bp->hwrm_cmd_lock);
rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
if (!rc) {
- if (resp->flags &
- cpu_to_le32(VNIC_QCAPS_RESP_FLAGS_RSS_DFLT_CR_CAP))
+ u32 flags = le32_to_cpu(resp->flags);
+
+ if (flags & VNIC_QCAPS_RESP_FLAGS_RSS_DFLT_CR_CAP)
bp->flags |= BNXT_FLAG_NEW_RSS_CAP;
+ if (flags &
+ VNIC_QCAPS_RESP_FLAGS_ROCE_MIRRORING_CAPABLE_VNIC_CAP)
+ bp->flags |= BNXT_FLAG_ROCE_MIRROR_CAP;
}
mutex_unlock(&bp->hwrm_cmd_lock);
return rc;
@@ -4204,12 +4274,12 @@ static int bnxt_hwrm_ring_grp_free(struct bnxt *bp)
static int hwrm_ring_alloc_send_msg(struct bnxt *bp,
struct bnxt_ring_struct *ring,
- u32 ring_type, u32 map_index,
- u32 stats_ctx_id)
+ u32 ring_type, u32 map_index)
{
int rc = 0, err = 0;
struct hwrm_ring_alloc_input req = {0};
struct hwrm_ring_alloc_output *resp = bp->hwrm_cmd_resp_addr;
+ struct bnxt_ring_grp_info *grp_info;
u16 ring_id;
bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_RING_ALLOC, -1, -1);
@@ -4231,10 +4301,10 @@ static int hwrm_ring_alloc_send_msg(struct bnxt *bp,
case HWRM_RING_ALLOC_TX:
req.ring_type = RING_ALLOC_REQ_RING_TYPE_TX;
/* Association of transmit ring with completion ring */
- req.cmpl_ring_id =
- cpu_to_le16(bp->grp_info[map_index].cp_fw_ring_id);
+ grp_info = &bp->grp_info[ring->grp_idx];
+ req.cmpl_ring_id = cpu_to_le16(grp_info->cp_fw_ring_id);
req.length = cpu_to_le32(bp->tx_ring_mask + 1);
- req.stat_ctx_id = cpu_to_le32(stats_ctx_id);
+ req.stat_ctx_id = cpu_to_le32(grp_info->fw_stats_ctx);
req.queue_id = cpu_to_le16(ring->queue_id);
break;
case HWRM_RING_ALLOC_RX:
@@ -4321,10 +4391,11 @@ static int bnxt_hwrm_ring_alloc(struct bnxt *bp)
struct bnxt_napi *bnapi = bp->bnapi[i];
struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
struct bnxt_ring_struct *ring = &cpr->cp_ring_struct;
+ u32 map_idx = ring->map_idx;
- cpr->cp_doorbell = bp->bar1 + i * 0x80;
- rc = hwrm_ring_alloc_send_msg(bp, ring, HWRM_RING_ALLOC_CMPL, i,
- INVALID_STATS_CTX_ID);
+ cpr->cp_doorbell = bp->bar1 + map_idx * 0x80;
+ rc = hwrm_ring_alloc_send_msg(bp, ring, HWRM_RING_ALLOC_CMPL,
+ map_idx);
if (rc)
goto err_out;
BNXT_CP_DB(cpr->cp_doorbell, cpr->cp_raw_cons);
@@ -4340,11 +4411,10 @@ static int bnxt_hwrm_ring_alloc(struct bnxt *bp)
for (i = 0; i < bp->tx_nr_rings; i++) {
struct bnxt_tx_ring_info *txr = &bp->tx_ring[i];
struct bnxt_ring_struct *ring = &txr->tx_ring_struct;
- u32 map_idx = txr->bnapi->index;
- u16 fw_stats_ctx = bp->grp_info[map_idx].fw_stats_ctx;
+ u32 map_idx = i;
rc = hwrm_ring_alloc_send_msg(bp, ring, HWRM_RING_ALLOC_TX,
- map_idx, fw_stats_ctx);
+ map_idx);
if (rc)
goto err_out;
txr->tx_doorbell = bp->bar1 + map_idx * 0x80;
@@ -4356,7 +4426,7 @@ static int bnxt_hwrm_ring_alloc(struct bnxt *bp)
u32 map_idx = rxr->bnapi->index;
rc = hwrm_ring_alloc_send_msg(bp, ring, HWRM_RING_ALLOC_RX,
- map_idx, INVALID_STATS_CTX_ID);
+ map_idx);
if (rc)
goto err_out;
rxr->rx_doorbell = bp->bar1 + map_idx * 0x80;
@@ -4369,13 +4439,12 @@ static int bnxt_hwrm_ring_alloc(struct bnxt *bp)
struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i];
struct bnxt_ring_struct *ring =
&rxr->rx_agg_ring_struct;
- u32 grp_idx = rxr->bnapi->index;
+ u32 grp_idx = ring->grp_idx;
u32 map_idx = grp_idx + bp->rx_nr_rings;
rc = hwrm_ring_alloc_send_msg(bp, ring,
HWRM_RING_ALLOC_AGG,
- map_idx,
- INVALID_STATS_CTX_ID);
+ map_idx);
if (rc)
goto err_out;
@@ -4669,20 +4738,59 @@ static int bnxt_hwrm_reserve_rings(struct bnxt *bp, int tx, int rx, int grp,
return bnxt_hwrm_reserve_vf_rings(bp, tx, rx, grp, cp, vnic);
}
+static int bnxt_cp_rings_in_use(struct bnxt *bp)
+{
+ int cp = bp->cp_nr_rings;
+ int ulp_msix, ulp_base;
+
+ ulp_msix = bnxt_get_ulp_msix_num(bp);
+ if (ulp_msix) {
+ ulp_base = bnxt_get_ulp_msix_base(bp);
+ cp += ulp_msix;
+ if ((ulp_base + ulp_msix) > cp)
+ cp = ulp_base + ulp_msix;
+ }
+ return cp;
+}
+
+static bool bnxt_need_reserve_rings(struct bnxt *bp)
+{
+ struct bnxt_hw_resc *hw_resc = &bp->hw_resc;
+ int cp = bnxt_cp_rings_in_use(bp);
+ int rx = bp->rx_nr_rings;
+ int vnic = 1, grp = rx;
+
+ if (bp->hwrm_spec_code < 0x10601)
+ return false;
+
+ if (hw_resc->resv_tx_rings != bp->tx_nr_rings)
+ return true;
+
+ if (bp->flags & BNXT_FLAG_RFS)
+ vnic = rx + 1;
+ if (bp->flags & BNXT_FLAG_AGG_RINGS)
+ rx <<= 1;
+ if ((bp->flags & BNXT_FLAG_NEW_RM) &&
+ (hw_resc->resv_rx_rings != rx || hw_resc->resv_cp_rings != cp ||
+ hw_resc->resv_hw_ring_grps != grp || hw_resc->resv_vnics != vnic))
+ return true;
+ return false;
+}
+
static int bnxt_trim_rings(struct bnxt *bp, int *rx, int *tx, int max,
bool shared);
static int __bnxt_reserve_rings(struct bnxt *bp)
{
struct bnxt_hw_resc *hw_resc = &bp->hw_resc;
+ int cp = bnxt_cp_rings_in_use(bp);
int tx = bp->tx_nr_rings;
int rx = bp->rx_nr_rings;
- int cp = bp->cp_nr_rings;
int grp, rx_rings, rc;
bool sh = false;
int vnic = 1;
- if (bp->hwrm_spec_code < 0x10601)
+ if (!bnxt_need_reserve_rings(bp))
return 0;
if (bp->flags & BNXT_FLAG_SHARED_RINGS)
@@ -4691,14 +4799,7 @@ static int __bnxt_reserve_rings(struct bnxt *bp)
vnic = rx + 1;
if (bp->flags & BNXT_FLAG_AGG_RINGS)
rx <<= 1;
-
grp = bp->rx_nr_rings;
- if (tx == hw_resc->resv_tx_rings &&
- (!(bp->flags & BNXT_FLAG_NEW_RM) ||
- (rx == hw_resc->resv_rx_rings &&
- grp == hw_resc->resv_hw_ring_grps &&
- cp == hw_resc->resv_cp_rings && vnic == hw_resc->resv_vnics)))
- return 0;
rc = bnxt_hwrm_reserve_rings(bp, tx, rx, grp, cp, vnic);
if (rc)
@@ -4742,30 +4843,6 @@ static int __bnxt_reserve_rings(struct bnxt *bp)
return rc;
}
-static bool bnxt_need_reserve_rings(struct bnxt *bp)
-{
- struct bnxt_hw_resc *hw_resc = &bp->hw_resc;
- int rx = bp->rx_nr_rings;
- int vnic = 1;
-
- if (bp->hwrm_spec_code < 0x10601)
- return false;
-
- if (hw_resc->resv_tx_rings != bp->tx_nr_rings)
- return true;
-
- if (bp->flags & BNXT_FLAG_RFS)
- vnic = rx + 1;
- if (bp->flags & BNXT_FLAG_AGG_RINGS)
- rx <<= 1;
- if ((bp->flags & BNXT_FLAG_NEW_RM) &&
- (hw_resc->resv_rx_rings != rx ||
- hw_resc->resv_cp_rings != bp->cp_nr_rings ||
- hw_resc->resv_vnics != vnic))
- return true;
- return false;
-}
-
static int bnxt_hwrm_check_vf_rings(struct bnxt *bp, int tx_rings, int rx_rings,
int ring_grps, int cp_rings, int vnics)
{
@@ -5055,7 +5132,7 @@ func_qcfg_exit:
return rc;
}
-static int bnxt_hwrm_func_resc_qcaps(struct bnxt *bp)
+int bnxt_hwrm_func_resc_qcaps(struct bnxt *bp, bool all)
{
struct hwrm_func_resource_qcaps_output *resp = bp->hwrm_cmd_resp_addr;
struct hwrm_func_resource_qcaps_input req = {0};
@@ -5072,6 +5149,10 @@ static int bnxt_hwrm_func_resc_qcaps(struct bnxt *bp)
goto hwrm_func_resc_qcaps_exit;
}
+ hw_resc->max_tx_sch_inputs = le16_to_cpu(resp->max_tx_scheduler_inputs);
+ if (!all)
+ goto hwrm_func_resc_qcaps_exit;
+
hw_resc->min_rsscos_ctxs = le16_to_cpu(resp->min_rsscos_ctx);
hw_resc->max_rsscos_ctxs = le16_to_cpu(resp->max_rsscos_ctx);
hw_resc->min_cp_rings = le16_to_cpu(resp->min_cmpl_rings);
@@ -5178,7 +5259,7 @@ static int bnxt_hwrm_func_qcaps(struct bnxt *bp)
if (rc)
return rc;
if (bp->hwrm_spec_code >= 0x10803) {
- rc = bnxt_hwrm_func_resc_qcaps(bp);
+ rc = bnxt_hwrm_func_resc_qcaps(bp, true);
if (!rc)
bp->flags |= BNXT_FLAG_NEW_RM;
}
@@ -5326,6 +5407,21 @@ static int bnxt_hwrm_port_qstats(struct bnxt *bp)
return rc;
}
+static int bnxt_hwrm_port_qstats_ext(struct bnxt *bp)
+{
+ struct hwrm_port_qstats_ext_input req = {0};
+ struct bnxt_pf_info *pf = &bp->pf;
+
+ if (!(bp->flags & BNXT_FLAG_PORT_STATS_EXT))
+ return 0;
+
+ bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_PORT_QSTATS_EXT, -1, -1);
+ req.port_id = cpu_to_le16(pf->port_id);
+ req.rx_stat_size = cpu_to_le16(sizeof(struct rx_port_stats_ext));
+ req.rx_stat_host_addr = cpu_to_le64(bp->hw_rx_port_stats_ext_map);
+ return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+}
+
static void bnxt_hwrm_free_tunnel_ports(struct bnxt *bp)
{
if (bp->vxlan_port_cnt) {
@@ -5418,10 +5514,9 @@ static int bnxt_hwrm_set_cache_line_size(struct bnxt *bp, int size)
bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_CFG, -1, -1);
req.fid = cpu_to_le16(0xffff);
req.enables = cpu_to_le32(FUNC_CFG_REQ_ENABLES_CACHE_LINESIZE);
- req.cache_linesize = FUNC_QCFG_RESP_CACHE_LINESIZE_CACHE_LINESIZE_64;
+ req.options = FUNC_CFG_REQ_OPTIONS_CACHE_LINESIZE_SIZE_64;
if (size == 128)
- req.cache_linesize =
- FUNC_QCFG_RESP_CACHE_LINESIZE_CACHE_LINESIZE_128;
+ req.options = FUNC_CFG_REQ_OPTIONS_CACHE_LINESIZE_SIZE_128;
rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
if (rc)
@@ -5740,6 +5835,7 @@ static void bnxt_setup_msix(struct bnxt *bp)
}
for (i = 0; i < bp->cp_nr_rings; i++) {
+ int map_idx = bnxt_cp_num_to_irq_num(bp, i);
char *attr;
if (bp->flags & BNXT_FLAG_SHARED_RINGS)
@@ -5749,9 +5845,9 @@ static void bnxt_setup_msix(struct bnxt *bp)
else
attr = "tx";
- snprintf(bp->irq_tbl[i].name, len, "%s-%s-%d", dev->name, attr,
- i);
- bp->irq_tbl[i].handler = bnxt_msix;
+ snprintf(bp->irq_tbl[map_idx].name, len, "%s-%s-%d", dev->name,
+ attr, i);
+ bp->irq_tbl[map_idx].handler = bnxt_msix;
}
}
@@ -5812,7 +5908,7 @@ void bnxt_set_max_func_cp_rings(struct bnxt *bp, unsigned int max)
bp->hw_resc.max_cp_rings = max;
}
-static unsigned int bnxt_get_max_func_irqs(struct bnxt *bp)
+unsigned int bnxt_get_max_func_irqs(struct bnxt *bp)
{
struct bnxt_hw_resc *hw_resc = &bp->hw_resc;
@@ -5824,12 +5920,44 @@ void bnxt_set_max_func_irqs(struct bnxt *bp, unsigned int max_irqs)
bp->hw_resc.max_irqs = max_irqs;
}
+int bnxt_get_avail_msix(struct bnxt *bp, int num)
+{
+ int max_cp = bnxt_get_max_func_cp_rings(bp);
+ int max_irq = bnxt_get_max_func_irqs(bp);
+ int total_req = bp->cp_nr_rings + num;
+ int max_idx, avail_msix;
+
+ max_idx = min_t(int, bp->total_irqs, max_cp);
+ avail_msix = max_idx - bp->cp_nr_rings;
+ if (!(bp->flags & BNXT_FLAG_NEW_RM) || avail_msix >= num)
+ return avail_msix;
+
+ if (max_irq < total_req) {
+ num = max_irq - bp->cp_nr_rings;
+ if (num <= 0)
+ return 0;
+ }
+ return num;
+}
+
+static int bnxt_get_num_msix(struct bnxt *bp)
+{
+ if (!(bp->flags & BNXT_FLAG_NEW_RM))
+ return bnxt_get_max_func_irqs(bp);
+
+ return bnxt_cp_rings_in_use(bp);
+}
+
static int bnxt_init_msix(struct bnxt *bp)
{
- int i, total_vecs, rc = 0, min = 1;
+ int i, total_vecs, max, rc = 0, min = 1, ulp_msix;
struct msix_entry *msix_ent;
- total_vecs = bnxt_get_max_func_irqs(bp);
+ total_vecs = bnxt_get_num_msix(bp);
+ max = bnxt_get_max_func_irqs(bp);
+ if (total_vecs > max)
+ total_vecs = max;
+
msix_ent = kcalloc(total_vecs, sizeof(struct msix_entry), GFP_KERNEL);
if (!msix_ent)
return -ENOMEM;
@@ -5843,7 +5971,8 @@ static int bnxt_init_msix(struct bnxt *bp)
min = 2;
total_vecs = pci_enable_msix_range(bp->pdev, msix_ent, min, total_vecs);
- if (total_vecs < 0) {
+ ulp_msix = bnxt_get_ulp_msix_num(bp);
+ if (total_vecs < 0 || total_vecs < ulp_msix) {
rc = -ENODEV;
goto msix_setup_exit;
}
@@ -5856,7 +5985,7 @@ static int bnxt_init_msix(struct bnxt *bp)
bp->total_irqs = total_vecs;
/* Trim rings based upon num of vectors allocated */
rc = bnxt_trim_rings(bp, &bp->rx_nr_rings, &bp->tx_nr_rings,
- total_vecs, min == 1);
+ total_vecs - ulp_msix, min == 1);
if (rc)
goto msix_setup_exit;
@@ -5920,9 +6049,8 @@ static void bnxt_clear_int_mode(struct bnxt *bp)
bp->flags &= ~BNXT_FLAG_USING_MSIX;
}
-static int bnxt_reserve_rings(struct bnxt *bp)
+int bnxt_reserve_rings(struct bnxt *bp)
{
- int orig_cp = bp->hw_resc.resv_cp_rings;
int tcs = netdev_get_num_tc(bp->dev);
int rc;
@@ -5934,9 +6062,12 @@ static int bnxt_reserve_rings(struct bnxt *bp)
netdev_err(bp->dev, "ring reservation failure rc: %d\n", rc);
return rc;
}
- if ((bp->flags & BNXT_FLAG_NEW_RM) && bp->cp_nr_rings > orig_cp) {
+ if ((bp->flags & BNXT_FLAG_NEW_RM) &&
+ (bnxt_get_num_msix(bp) != bp->total_irqs)) {
+ bnxt_ulp_irq_stop(bp);
bnxt_clear_int_mode(bp);
rc = bnxt_init_int_mode(bp);
+ bnxt_ulp_irq_restart(bp, rc);
if (rc)
return rc;
}
@@ -5959,11 +6090,13 @@ static void bnxt_free_irq(struct bnxt *bp)
free_irq_cpu_rmap(bp->dev->rx_cpu_rmap);
bp->dev->rx_cpu_rmap = NULL;
#endif
- if (!bp->irq_tbl)
+ if (!bp->irq_tbl || !bp->bnapi)
return;
for (i = 0; i < bp->cp_nr_rings; i++) {
- irq = &bp->irq_tbl[i];
+ int map_idx = bnxt_cp_num_to_irq_num(bp, i);
+
+ irq = &bp->irq_tbl[map_idx];
if (irq->requested) {
if (irq->have_cpumask) {
irq_set_affinity_hint(irq->vector, NULL);
@@ -5982,14 +6115,25 @@ static int bnxt_request_irq(struct bnxt *bp)
int i, j, rc = 0;
unsigned long flags = 0;
#ifdef CONFIG_RFS_ACCEL
- struct cpu_rmap *rmap = bp->dev->rx_cpu_rmap;
+ struct cpu_rmap *rmap;
#endif
+ rc = bnxt_setup_int_mode(bp);
+ if (rc) {
+ netdev_err(bp->dev, "bnxt_setup_int_mode err: %x\n",
+ rc);
+ return rc;
+ }
+#ifdef CONFIG_RFS_ACCEL
+ rmap = bp->dev->rx_cpu_rmap;
+#endif
if (!(bp->flags & BNXT_FLAG_USING_MSIX))
flags = IRQF_SHARED;
for (i = 0, j = 0; i < bp->cp_nr_rings; i++) {
- struct bnxt_irq *irq = &bp->irq_tbl[i];
+ int map_idx = bnxt_cp_num_to_irq_num(bp, i);
+ struct bnxt_irq *irq = &bp->irq_tbl[map_idx];
+
#ifdef CONFIG_RFS_ACCEL
if (rmap && bp->bnapi[i]->rx_ring) {
rc = irq_cpu_rmap_add(rmap, irq->vector);
@@ -6709,13 +6853,6 @@ static int __bnxt_open_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init)
rc = bnxt_reserve_rings(bp);
if (rc)
return rc;
-
- rc = bnxt_setup_int_mode(bp);
- if (rc) {
- netdev_err(bp->dev, "bnxt_setup_int_mode err: %x\n",
- rc);
- return rc;
- }
}
if ((bp->flags & BNXT_FLAG_RFS) &&
!(bp->flags & BNXT_FLAG_USING_MSIX)) {
@@ -7478,8 +7615,10 @@ static void bnxt_sp_task(struct work_struct *work)
bnxt_hwrm_tunnel_dst_port_free(
bp, TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_GENEVE);
}
- if (test_and_clear_bit(BNXT_PERIODIC_STATS_SP_EVENT, &bp->sp_event))
+ if (test_and_clear_bit(BNXT_PERIODIC_STATS_SP_EVENT, &bp->sp_event)) {
bnxt_hwrm_port_qstats(bp);
+ bnxt_hwrm_port_qstats_ext(bp);
+ }
if (test_and_clear_bit(BNXT_LINK_CHNG_SP_EVENT, &bp->sp_event)) {
int rc;
@@ -7547,6 +7686,8 @@ int bnxt_check_rings(struct bnxt *bp, int tx, int rx, bool sh, int tcs,
if (bp->flags & BNXT_FLAG_AGG_RINGS)
rx_rings <<= 1;
cp = sh ? max_t(int, tx_rings_needed, rx) : tx_rings_needed + rx;
+ if (bp->flags & BNXT_FLAG_NEW_RM)
+ cp += bnxt_get_ulp_msix_num(bp);
return bnxt_hwrm_check_rings(bp, tx_rings_needed, rx_rings, rx, cp,
vnics);
}
@@ -8193,6 +8334,7 @@ static const struct net_device_ops bnxt_netdev_ops = {
.ndo_set_vf_rate = bnxt_set_vf_bw,
.ndo_set_vf_link_state = bnxt_set_vf_link_state,
.ndo_set_vf_spoofchk = bnxt_set_vf_spoofchk,
+ .ndo_set_vf_trust = bnxt_set_vf_trust,
#endif
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = bnxt_poll_controller,
@@ -8390,9 +8532,15 @@ static int bnxt_set_dflt_rings(struct bnxt *bp, bool sh)
if (sh)
bp->flags |= BNXT_FLAG_SHARED_RINGS;
dflt_rings = netif_get_num_default_rss_queues();
- /* Reduce default rings to reduce memory usage on multi-port cards */
- if (bp->port_count > 1)
- dflt_rings = min_t(int, dflt_rings, 4);
+ /* Reduce default rings on multi-port cards so that total default
+ * rings do not exceed CPU count.
+ */
+ if (bp->port_count > 1) {
+ int max_rings =
+ max_t(int, num_online_cpus() / bp->port_count, 1);
+
+ dflt_rings = min_t(int, dflt_rings, max_rings);
+ }
rc = bnxt_get_dflt_rings(bp, &max_rx_rings, &max_tx_rings, sh);
if (rc)
return rc;
@@ -8431,16 +8579,15 @@ int bnxt_restore_pf_fw_resources(struct bnxt *bp)
int rc;
ASSERT_RTNL();
- if (bnxt_ulp_registered(bp->edev, BNXT_ROCE_ULP))
- return 0;
-
bnxt_hwrm_func_qcaps(bp);
if (netif_running(bp->dev))
__bnxt_close_nic(bp, true, false);
+ bnxt_ulp_irq_stop(bp);
bnxt_clear_int_mode(bp);
rc = bnxt_init_int_mode(bp);
+ bnxt_ulp_irq_restart(bp, rc);
if (netif_running(bp->dev)) {
if (rc)
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
index 5e3d62189cab..3d55d3b56865 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
@@ -12,11 +12,11 @@
#define BNXT_H
#define DRV_MODULE_NAME "bnxt_en"
-#define DRV_MODULE_VERSION "1.9.0"
+#define DRV_MODULE_VERSION "1.9.1"
#define DRV_VER_MAJ 1
#define DRV_VER_MIN 9
-#define DRV_VER_UPD 0
+#define DRV_VER_UPD 1
#include <linux/interrupt.h>
#include <linux/rhashtable.h>
@@ -573,6 +573,10 @@ struct bnxt_ring_struct {
void **vmem;
u16 fw_ring_id; /* Ring id filled by Chimp FW */
+ union {
+ u16 grp_idx;
+ u16 map_idx; /* Used by cmpl rings */
+ };
u8 queue_id;
};
@@ -786,6 +790,7 @@ struct bnxt_hw_resc {
u16 min_tx_rings;
u16 max_tx_rings;
u16 resv_tx_rings;
+ u16 max_tx_sch_inputs;
u16 min_rx_rings;
u16 max_rx_rings;
u16 resv_rx_rings;
@@ -815,6 +820,7 @@ struct bnxt_vf_info {
#define BNXT_VF_SPOOFCHK 0x2
#define BNXT_VF_LINK_FORCED 0x4
#define BNXT_VF_LINK_UP 0x8
+#define BNXT_VF_TRUST 0x10
u32 func_flags; /* func cfg flags */
u32 min_tx_rate;
u32 max_tx_rate;
@@ -1151,7 +1157,9 @@ struct bnxt {
#define BNXT_FLAG_FW_DCBX_AGENT 0x800000
#define BNXT_FLAG_CHIP_NITRO_A0 0x1000000
#define BNXT_FLAG_DIM 0x2000000
+ #define BNXT_FLAG_ROCE_MIRROR_CAP 0x4000000
#define BNXT_FLAG_NEW_RM 0x8000000
+ #define BNXT_FLAG_PORT_STATS_EXT 0x10000000
#define BNXT_FLAG_ALL_CONFIG_FEATS (BNXT_FLAG_TPA | \
BNXT_FLAG_RFS | \
@@ -1271,8 +1279,10 @@ struct bnxt {
struct rx_port_stats *hw_rx_port_stats;
struct tx_port_stats *hw_tx_port_stats;
+ struct rx_port_stats_ext *hw_rx_port_stats_ext;
dma_addr_t hw_rx_port_stats_map;
dma_addr_t hw_tx_port_stats_map;
+ dma_addr_t hw_rx_port_stats_ext_map;
int hw_port_stats_size;
u16 hwrm_max_req_len;
@@ -1383,6 +1393,9 @@ struct bnxt {
((offsetof(struct tx_port_stats, counter) + \
sizeof(struct rx_port_stats) + 512) / 8)
+#define BNXT_RX_STATS_EXT_OFFSET(counter) \
+ (offsetof(struct rx_port_stats_ext, counter) / 8)
+
#define I2C_DEV_ADDR_A0 0xa0
#define I2C_DEV_ADDR_A2 0xa2
#define SFP_EEPROM_SFF_8472_COMP_ADDR 0x5e
@@ -1402,6 +1415,15 @@ static inline u32 bnxt_tx_avail(struct bnxt *bp, struct bnxt_tx_ring_info *txr)
((txr->tx_prod - txr->tx_cons) & bp->tx_ring_mask);
}
+/* For TX and RX ring doorbells with no ordering guarantee*/
+static inline void bnxt_db_write_relaxed(struct bnxt *bp, void __iomem *db,
+ u32 val)
+{
+ writel_relaxed(val, db);
+ if (bp->flags & BNXT_FLAG_DOUBLE_DB)
+ writel_relaxed(val, db);
+}
+
/* For TX and RX ring doorbells */
static inline void bnxt_db_write(struct bnxt *bp, void __iomem *db, u32 val)
{
@@ -1432,13 +1454,17 @@ unsigned int bnxt_get_max_func_stat_ctxs(struct bnxt *bp);
void bnxt_set_max_func_stat_ctxs(struct bnxt *bp, unsigned int max);
unsigned int bnxt_get_max_func_cp_rings(struct bnxt *bp);
void bnxt_set_max_func_cp_rings(struct bnxt *bp, unsigned int max);
+unsigned int bnxt_get_max_func_irqs(struct bnxt *bp);
void bnxt_set_max_func_irqs(struct bnxt *bp, unsigned int max);
+int bnxt_get_avail_msix(struct bnxt *bp, int num);
+int bnxt_reserve_rings(struct bnxt *bp);
void bnxt_tx_disable(struct bnxt *bp);
void bnxt_tx_enable(struct bnxt *bp);
int bnxt_hwrm_set_pause(struct bnxt *);
int bnxt_hwrm_set_link_setting(struct bnxt *, bool, bool);
int bnxt_hwrm_alloc_wol_fltr(struct bnxt *bp);
int bnxt_hwrm_free_wol_fltr(struct bnxt *bp);
+int bnxt_hwrm_func_resc_qcaps(struct bnxt *bp, bool all);
int bnxt_hwrm_fw_set_time(struct bnxt *);
int bnxt_open_nic(struct bnxt *, bool, bool);
int bnxt_half_open_nic(struct bnxt *bp);
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.h
index d2e0af960bf5..69efde785f23 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.h
@@ -1,7 +1,7 @@
/* Broadcom NetXtreme-C/E network driver.
*
* Copyright (c) 2014-2016 Broadcom Corporation
- * Copyright (c) 2016-2017 Broadcom Limited
+ * Copyright (c) 2016-2018 Broadcom Limited
*
* 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
@@ -34,7 +34,8 @@ struct bnxt_cos2bw_cfg {
};
#define BNXT_LLQ(q_profile) \
- ((q_profile) == QUEUE_QPORTCFG_RESP_QUEUE_ID0_SERVICE_PROFILE_LOSSLESS)
+ ((q_profile) == \
+ QUEUE_QPORTCFG_RESP_QUEUE_ID0_SERVICE_PROFILE_LOSSLESS_ROCE)
#define HWRM_STRUCT_DATA_SUBTYPE_HOST_OPERATIONAL 0x0300
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
index 1801582076be..8ba14ae00e8f 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
@@ -137,6 +137,9 @@ reset_coalesce:
#define BNXT_TX_STATS_ENTRY(counter) \
{ BNXT_TX_STATS_OFFSET(counter), __stringify(counter) }
+#define BNXT_RX_STATS_EXT_ENTRY(counter) \
+ { BNXT_RX_STATS_EXT_OFFSET(counter), __stringify(counter) }
+
static const struct {
long offset;
char string[ETH_GSTRING_LEN];
@@ -181,6 +184,8 @@ static const struct {
BNXT_RX_STATS_ENTRY(rx_bytes),
BNXT_RX_STATS_ENTRY(rx_runt_bytes),
BNXT_RX_STATS_ENTRY(rx_runt_frames),
+ BNXT_RX_STATS_ENTRY(rx_stat_discard),
+ BNXT_RX_STATS_ENTRY(rx_stat_err),
BNXT_TX_STATS_ENTRY(tx_64b_frames),
BNXT_TX_STATS_ENTRY(tx_65b_127b_frames),
@@ -216,9 +221,24 @@ static const struct {
BNXT_TX_STATS_ENTRY(tx_eee_lpi_duration),
BNXT_TX_STATS_ENTRY(tx_total_collisions),
BNXT_TX_STATS_ENTRY(tx_bytes),
+ BNXT_TX_STATS_ENTRY(tx_xthol_frames),
+ BNXT_TX_STATS_ENTRY(tx_stat_discard),
+ BNXT_TX_STATS_ENTRY(tx_stat_error),
+};
+
+static const struct {
+ long offset;
+ char string[ETH_GSTRING_LEN];
+} bnxt_port_stats_ext_arr[] = {
+ BNXT_RX_STATS_EXT_ENTRY(link_down_events),
+ BNXT_RX_STATS_EXT_ENTRY(continuous_pause_events),
+ BNXT_RX_STATS_EXT_ENTRY(resume_pause_events),
+ BNXT_RX_STATS_EXT_ENTRY(continuous_roce_pause_events),
+ BNXT_RX_STATS_EXT_ENTRY(resume_roce_pause_events),
};
#define BNXT_NUM_PORT_STATS ARRAY_SIZE(bnxt_port_stats_arr)
+#define BNXT_NUM_PORT_STATS_EXT ARRAY_SIZE(bnxt_port_stats_ext_arr)
static int bnxt_get_num_stats(struct bnxt *bp)
{
@@ -227,6 +247,9 @@ static int bnxt_get_num_stats(struct bnxt *bp)
if (bp->flags & BNXT_FLAG_PORT_STATS)
num_stats += BNXT_NUM_PORT_STATS;
+ if (bp->flags & BNXT_FLAG_PORT_STATS_EXT)
+ num_stats += BNXT_NUM_PORT_STATS_EXT;
+
return num_stats;
}
@@ -274,6 +297,14 @@ static void bnxt_get_ethtool_stats(struct net_device *dev,
bnxt_port_stats_arr[i].offset));
}
}
+ if (bp->flags & BNXT_FLAG_PORT_STATS_EXT) {
+ __le64 *port_stats_ext = (__le64 *)bp->hw_rx_port_stats_ext;
+
+ for (i = 0; i < BNXT_NUM_PORT_STATS_EXT; i++, j++) {
+ buf[j] = le64_to_cpu(*(port_stats_ext +
+ bnxt_port_stats_ext_arr[i].offset));
+ }
+ }
}
static void bnxt_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
@@ -334,6 +365,12 @@ static void bnxt_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
buf += ETH_GSTRING_LEN;
}
}
+ if (bp->flags & BNXT_FLAG_PORT_STATS_EXT) {
+ for (i = 0; i < BNXT_NUM_PORT_STATS_EXT; i++) {
+ strcpy(buf, bnxt_port_stats_ext_arr[i].string);
+ buf += ETH_GSTRING_LEN;
+ }
+ }
break;
case ETH_SS_TEST:
if (bp->num_tests)
@@ -388,15 +425,26 @@ static void bnxt_get_channels(struct net_device *dev,
struct ethtool_channels *channel)
{
struct bnxt *bp = netdev_priv(dev);
+ struct bnxt_hw_resc *hw_resc = &bp->hw_resc;
int max_rx_rings, max_tx_rings, tcs;
+ int max_tx_sch_inputs;
+
+ /* Get the most up-to-date max_tx_sch_inputs. */
+ if (bp->flags & BNXT_FLAG_NEW_RM)
+ bnxt_hwrm_func_resc_qcaps(bp, false);
+ max_tx_sch_inputs = hw_resc->max_tx_sch_inputs;
bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, true);
+ if (max_tx_sch_inputs)
+ max_tx_rings = min_t(int, max_tx_rings, max_tx_sch_inputs);
channel->max_combined = min_t(int, max_rx_rings, max_tx_rings);
if (bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, false)) {
max_rx_rings = 0;
max_tx_rings = 0;
}
+ if (max_tx_sch_inputs)
+ max_tx_rings = min_t(int, max_tx_rings, max_tx_sch_inputs);
tcs = netdev_get_num_tc(dev);
if (tcs > 1)
@@ -822,17 +870,22 @@ static int bnxt_get_rxfh(struct net_device *dev, u32 *indir, u8 *key,
u8 *hfunc)
{
struct bnxt *bp = netdev_priv(dev);
- struct bnxt_vnic_info *vnic = &bp->vnic_info[0];
+ struct bnxt_vnic_info *vnic;
int i = 0;
if (hfunc)
*hfunc = ETH_RSS_HASH_TOP;
- if (indir)
+ if (!bp->vnic_info)
+ return 0;
+
+ vnic = &bp->vnic_info[0];
+ if (indir && vnic->rss_table) {
for (i = 0; i < HW_HASH_INDEX_SIZE; i++)
indir[i] = le16_to_cpu(vnic->rss_table[i]);
+ }
- if (key)
+ if (key && vnic->rss_hash_key)
memcpy(key, vnic->rss_hash_key, HW_HASH_KEY_SIZE);
return 0;
@@ -1874,22 +1927,39 @@ static char *bnxt_parse_pkglog(int desired_field, u8 *data, size_t datalen)
return retval;
}
-static char *bnxt_get_pkgver(struct net_device *dev, char *buf, size_t buflen)
+static void bnxt_get_pkgver(struct net_device *dev)
{
+ struct bnxt *bp = netdev_priv(dev);
u16 index = 0;
- u32 datalen;
+ char *pkgver;
+ u32 pkglen;
+ u8 *pkgbuf;
+ int len;
if (bnxt_find_nvram_item(dev, BNX_DIR_TYPE_PKG_LOG,
BNX_DIR_ORDINAL_FIRST, BNX_DIR_EXT_NONE,
- &index, NULL, &datalen) != 0)
- return NULL;
+ &index, NULL, &pkglen) != 0)
+ return;
- memset(buf, 0, buflen);
- if (bnxt_get_nvram_item(dev, index, 0, datalen, buf) != 0)
- return NULL;
+ pkgbuf = kzalloc(pkglen, GFP_KERNEL);
+ if (!pkgbuf) {
+ dev_err(&bp->pdev->dev, "Unable to allocate memory for pkg version, length = %u\n",
+ pkglen);
+ return;
+ }
+
+ if (bnxt_get_nvram_item(dev, index, 0, pkglen, pkgbuf))
+ goto err;
- return bnxt_parse_pkglog(BNX_PKG_LOG_FIELD_IDX_PKG_VERSION, buf,
- datalen);
+ pkgver = bnxt_parse_pkglog(BNX_PKG_LOG_FIELD_IDX_PKG_VERSION, pkgbuf,
+ pkglen);
+ if (pkgver && *pkgver != 0 && isdigit(*pkgver)) {
+ len = strlen(bp->fw_ver_str);
+ snprintf(bp->fw_ver_str + len, FW_VER_STR_LEN - len - 1,
+ "/pkg %s", pkgver);
+ }
+err:
+ kfree(pkgbuf);
}
static int bnxt_get_eeprom(struct net_device *dev,
@@ -2535,16 +2605,20 @@ static int bnxt_reset(struct net_device *dev, u32 *flags)
return -EOPNOTSUPP;
rc = bnxt_firmware_reset(dev, BNXT_FW_RESET_CHIP);
- if (!rc)
+ if (!rc) {
netdev_info(dev, "Reset request successful. Reload driver to complete reset\n");
+ *flags = 0;
+ }
} else if (*flags == ETH_RESET_AP) {
/* This feature is not supported in older firmware versions */
if (bp->hwrm_spec_code < 0x10803)
return -EOPNOTSUPP;
rc = bnxt_firmware_reset(dev, BNXT_FW_RESET_AP);
- if (!rc)
+ if (!rc) {
netdev_info(dev, "Reset Application Processor request successful.\n");
+ *flags = 0;
+ }
} else {
rc = -EINVAL;
}
@@ -2558,22 +2632,10 @@ void bnxt_ethtool_init(struct bnxt *bp)
struct hwrm_selftest_qlist_input req = {0};
struct bnxt_test_info *test_info;
struct net_device *dev = bp->dev;
- char *pkglog;
int i, rc;
- pkglog = kzalloc(BNX_PKG_LOG_MAX_LENGTH, GFP_KERNEL);
- if (pkglog) {
- char *pkgver;
- int len;
+ bnxt_get_pkgver(dev);
- pkgver = bnxt_get_pkgver(dev, pkglog, BNX_PKG_LOG_MAX_LENGTH);
- if (pkgver && *pkgver != 0 && isdigit(*pkgver)) {
- len = strlen(bp->fw_ver_str);
- snprintf(bp->fw_ver_str + len, FW_VER_STR_LEN - len - 1,
- "/pkg %s", pkgver);
- }
- kfree(pkglog);
- }
if (bp->hwrm_spec_code < 0x10704 || !BNXT_SINGLE_PF(bp))
return;
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h
index 82d17f8cc0db..0fe0ea8dce6c 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h
@@ -188,6 +188,7 @@ struct cmd_nums {
#define HWRM_STAT_CTX_FREE 0xb1UL
#define HWRM_STAT_CTX_QUERY 0xb2UL
#define HWRM_STAT_CTX_CLR_STATS 0xb3UL
+ #define HWRM_PORT_QSTATS_EXT 0xb4UL
#define HWRM_FW_RESET 0xc0UL
#define HWRM_FW_QSTATUS 0xc1UL
#define HWRM_FW_SET_TIME 0xc8UL
@@ -199,6 +200,7 @@ struct cmd_nums {
#define HWRM_REJECT_FWD_RESP 0xd1UL
#define HWRM_FWD_RESP 0xd2UL
#define HWRM_FWD_ASYNC_EVENT_CMPL 0xd3UL
+ #define HWRM_OEM_CMD 0xd4UL
#define HWRM_TEMP_MONITOR_QUERY 0xe0UL
#define HWRM_WOL_FILTER_ALLOC 0xf0UL
#define HWRM_WOL_FILTER_FREE 0xf1UL
@@ -271,6 +273,7 @@ struct cmd_nums {
#define HWRM_SELFTEST_EXEC 0x201UL
#define HWRM_SELFTEST_IRQ 0x202UL
#define HWRM_SELFTEST_RETRIEVE_SERDES_DATA 0x203UL
+ #define HWRM_PCIE_QSTATS 0x204UL
#define HWRM_DBG_READ_DIRECT 0xff10UL
#define HWRM_DBG_READ_INDIRECT 0xff11UL
#define HWRM_DBG_WRITE_DIRECT 0xff12UL
@@ -341,9 +344,9 @@ struct hwrm_err_output {
#define HWRM_RESP_VALID_KEY 1
#define HWRM_VERSION_MAJOR 1
#define HWRM_VERSION_MINOR 9
-#define HWRM_VERSION_UPDATE 0
-#define HWRM_VERSION_RSVD 0
-#define HWRM_VERSION_STR "1.9.0.0"
+#define HWRM_VERSION_UPDATE 1
+#define HWRM_VERSION_RSVD 15
+#define HWRM_VERSION_STR "1.9.1.15"
/* hwrm_ver_get_input (size:192b/24B) */
struct hwrm_ver_get_input {
@@ -616,30 +619,6 @@ struct hwrm_async_event_cmpl_link_speed_cfg_change {
#define ASYNC_EVENT_CMPL_LINK_SPEED_CFG_CHANGE_EVENT_DATA1_ILLEGAL_LINK_SPEED_CFG 0x20000UL
};
-/* hwrm_async_event_cmpl_pf_drvr_unload (size:128b/16B) */
-struct hwrm_async_event_cmpl_pf_drvr_unload {
- __le16 type;
- #define ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_TYPE_MASK 0x3fUL
- #define ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_TYPE_SFT 0
- #define ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_TYPE_HWRM_ASYNC_EVENT 0x2eUL
- #define ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_TYPE_LAST ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_TYPE_HWRM_ASYNC_EVENT
- __le16 event_id;
- #define ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_ID_PF_DRVR_UNLOAD 0x20UL
- #define ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_ID_LAST ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_ID_PF_DRVR_UNLOAD
- __le32 event_data2;
- u8 opaque_v;
- #define ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_V 0x1UL
- #define ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_OPAQUE_MASK 0xfeUL
- #define ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_OPAQUE_SFT 1
- u8 timestamp_lo;
- __le16 timestamp_hi;
- __le32 event_data1;
- #define ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_DATA1_FUNC_ID_MASK 0xffffUL
- #define ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_DATA1_FUNC_ID_SFT 0
- #define ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_DATA1_PORT_MASK 0x70000UL
- #define ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_DATA1_PORT_SFT 16
-};
-
/* hwrm_async_event_cmpl_vf_cfg_change (size:128b/16B) */
struct hwrm_async_event_cmpl_vf_cfg_change {
__le16 type;
@@ -854,6 +833,7 @@ struct hwrm_func_qcaps_output {
#define FUNC_QCAPS_RESP_FLAGS_NVGRE_TUN_FLAGS_SUPPORTED 0x2000UL
#define FUNC_QCAPS_RESP_FLAGS_GRE_TUN_FLAGS_SUPPORTED 0x4000UL
#define FUNC_QCAPS_RESP_FLAGS_MPLS_TUN_FLAGS_SUPPORTED 0x8000UL
+ #define FUNC_QCAPS_RESP_FLAGS_PCIE_STATS_SUPPORTED 0x10000UL
u8 mac_address[6];
__le16 max_rsscos_ctx;
__le16 max_cmpl_rings;
@@ -966,10 +946,14 @@ struct hwrm_func_qcfg_output {
#define FUNC_QCFG_RESP_EVB_MODE_VEB 0x1UL
#define FUNC_QCFG_RESP_EVB_MODE_VEPA 0x2UL
#define FUNC_QCFG_RESP_EVB_MODE_LAST FUNC_QCFG_RESP_EVB_MODE_VEPA
- u8 cache_linesize;
- #define FUNC_QCFG_RESP_CACHE_LINESIZE_CACHE_LINESIZE_64 0x0UL
- #define FUNC_QCFG_RESP_CACHE_LINESIZE_CACHE_LINESIZE_128 0x1UL
- #define FUNC_QCFG_RESP_CACHE_LINESIZE_LAST FUNC_QCFG_RESP_CACHE_LINESIZE_CACHE_LINESIZE_128
+ u8 options;
+ #define FUNC_QCFG_RESP_OPTIONS_CACHE_LINESIZE_MASK 0x3UL
+ #define FUNC_QCFG_RESP_OPTIONS_CACHE_LINESIZE_SFT 0
+ #define FUNC_QCFG_RESP_OPTIONS_CACHE_LINESIZE_SIZE_64 0x0UL
+ #define FUNC_QCFG_RESP_OPTIONS_CACHE_LINESIZE_SIZE_128 0x1UL
+ #define FUNC_QCFG_RESP_OPTIONS_CACHE_LINESIZE_LAST FUNC_QCFG_RESP_OPTIONS_CACHE_LINESIZE_SIZE_128
+ #define FUNC_QCFG_RESP_OPTIONS_RSVD_MASK 0xfcUL
+ #define FUNC_QCFG_RESP_OPTIONS_RSVD_SFT 2
__le16 alloc_vfs;
__le32 alloc_mcast_filters;
__le32 alloc_hw_ring_grps;
@@ -1124,10 +1108,14 @@ struct hwrm_func_cfg_input {
#define FUNC_CFG_REQ_EVB_MODE_VEB 0x1UL
#define FUNC_CFG_REQ_EVB_MODE_VEPA 0x2UL
#define FUNC_CFG_REQ_EVB_MODE_LAST FUNC_CFG_REQ_EVB_MODE_VEPA
- u8 cache_linesize;
- #define FUNC_CFG_REQ_CACHE_LINESIZE_CACHE_LINESIZE_64 0x0UL
- #define FUNC_CFG_REQ_CACHE_LINESIZE_CACHE_LINESIZE_128 0x1UL
- #define FUNC_CFG_REQ_CACHE_LINESIZE_LAST FUNC_CFG_REQ_CACHE_LINESIZE_CACHE_LINESIZE_128
+ u8 options;
+ #define FUNC_CFG_REQ_OPTIONS_CACHE_LINESIZE_MASK 0x3UL
+ #define FUNC_CFG_REQ_OPTIONS_CACHE_LINESIZE_SFT 0
+ #define FUNC_CFG_REQ_OPTIONS_CACHE_LINESIZE_SIZE_64 0x0UL
+ #define FUNC_CFG_REQ_OPTIONS_CACHE_LINESIZE_SIZE_128 0x1UL
+ #define FUNC_CFG_REQ_OPTIONS_CACHE_LINESIZE_LAST FUNC_CFG_REQ_OPTIONS_CACHE_LINESIZE_SIZE_128
+ #define FUNC_CFG_REQ_OPTIONS_RSVD_MASK 0xfcUL
+ #define FUNC_CFG_REQ_OPTIONS_RSVD_SFT 2
__le16 num_mcast_filters;
};
@@ -1248,7 +1236,7 @@ struct hwrm_func_vf_vnic_ids_query_output {
u8 valid;
};
-/* hwrm_func_drv_rgtr_input (size:832b/104B) */
+/* hwrm_func_drv_rgtr_input (size:896b/112B) */
struct hwrm_func_drv_rgtr_input {
__le16 req_type;
__le16 cmpl_ring;
@@ -1256,8 +1244,9 @@ struct hwrm_func_drv_rgtr_input {
__le16 target_id;
__le64 resp_addr;
__le32 flags;
- #define FUNC_DRV_RGTR_REQ_FLAGS_FWD_ALL_MODE 0x1UL
- #define FUNC_DRV_RGTR_REQ_FLAGS_FWD_NONE_MODE 0x2UL
+ #define FUNC_DRV_RGTR_REQ_FLAGS_FWD_ALL_MODE 0x1UL
+ #define FUNC_DRV_RGTR_REQ_FLAGS_FWD_NONE_MODE 0x2UL
+ #define FUNC_DRV_RGTR_REQ_FLAGS_16BIT_VER_MODE 0x4UL
__le32 enables;
#define FUNC_DRV_RGTR_REQ_ENABLES_OS_TYPE 0x1UL
#define FUNC_DRV_RGTR_REQ_ENABLES_VER 0x2UL
@@ -1277,14 +1266,18 @@ struct hwrm_func_drv_rgtr_input {
#define FUNC_DRV_RGTR_REQ_OS_TYPE_WIN2012R2 0x74UL
#define FUNC_DRV_RGTR_REQ_OS_TYPE_UEFI 0x8000UL
#define FUNC_DRV_RGTR_REQ_OS_TYPE_LAST FUNC_DRV_RGTR_REQ_OS_TYPE_UEFI
- u8 ver_maj;
- u8 ver_min;
- u8 ver_upd;
+ u8 ver_maj_8b;
+ u8 ver_min_8b;
+ u8 ver_upd_8b;
u8 unused_0[3];
__le32 timestamp;
u8 unused_1[4];
__le32 vf_req_fwd[8];
__le32 async_event_fwd[8];
+ __le16 ver_maj;
+ __le16 ver_min;
+ __le16 ver_upd;
+ __le16 ver_patch;
};
/* hwrm_func_drv_rgtr_output (size:128b/16B) */
@@ -1379,7 +1372,7 @@ struct hwrm_func_drv_qver_input {
u8 unused_0[2];
};
-/* hwrm_func_drv_qver_output (size:128b/16B) */
+/* hwrm_func_drv_qver_output (size:192b/24B) */
struct hwrm_func_drv_qver_output {
__le16 error_code;
__le16 req_type;
@@ -1398,11 +1391,15 @@ struct hwrm_func_drv_qver_output {
#define FUNC_DRV_QVER_RESP_OS_TYPE_WIN2012R2 0x74UL
#define FUNC_DRV_QVER_RESP_OS_TYPE_UEFI 0x8000UL
#define FUNC_DRV_QVER_RESP_OS_TYPE_LAST FUNC_DRV_QVER_RESP_OS_TYPE_UEFI
- u8 ver_maj;
- u8 ver_min;
- u8 ver_upd;
+ u8 ver_maj_8b;
+ u8 ver_min_8b;
+ u8 ver_upd_8b;
u8 unused_0[2];
u8 valid;
+ __le16 ver_maj;
+ __le16 ver_min;
+ __le16 ver_upd;
+ __le16 ver_patch;
};
/* hwrm_func_resource_qcaps_input (size:192b/24B) */
@@ -1416,7 +1413,7 @@ struct hwrm_func_resource_qcaps_input {
u8 unused_0[6];
};
-/* hwrm_func_resource_qcaps_output (size:384b/48B) */
+/* hwrm_func_resource_qcaps_output (size:448b/56B) */
struct hwrm_func_resource_qcaps_output {
__le16 error_code;
__le16 req_type;
@@ -1425,9 +1422,10 @@ struct hwrm_func_resource_qcaps_output {
__le16 max_vfs;
__le16 max_msix;
__le16 vf_reservation_strategy;
- #define FUNC_RESOURCE_QCAPS_RESP_VF_RESERVATION_STRATEGY_MAXIMAL 0x0UL
- #define FUNC_RESOURCE_QCAPS_RESP_VF_RESERVATION_STRATEGY_MINIMAL 0x1UL
- #define FUNC_RESOURCE_QCAPS_RESP_VF_RESERVATION_STRATEGY_LAST FUNC_RESOURCE_QCAPS_RESP_VF_RESERVATION_STRATEGY_MINIMAL
+ #define FUNC_RESOURCE_QCAPS_RESP_VF_RESERVATION_STRATEGY_MAXIMAL 0x0UL
+ #define FUNC_RESOURCE_QCAPS_RESP_VF_RESERVATION_STRATEGY_MINIMAL 0x1UL
+ #define FUNC_RESOURCE_QCAPS_RESP_VF_RESERVATION_STRATEGY_MINIMAL_STATIC 0x2UL
+ #define FUNC_RESOURCE_QCAPS_RESP_VF_RESERVATION_STRATEGY_LAST FUNC_RESOURCE_QCAPS_RESP_VF_RESERVATION_STRATEGY_MINIMAL_STATIC
__le16 min_rsscos_ctx;
__le16 max_rsscos_ctx;
__le16 min_cmpl_rings;
@@ -1444,7 +1442,8 @@ struct hwrm_func_resource_qcaps_output {
__le16 max_stat_ctx;
__le16 min_hw_ring_grps;
__le16 max_hw_ring_grps;
- u8 unused_0;
+ __le16 max_tx_scheduler_inputs;
+ u8 unused_0[7];
u8 valid;
};
@@ -1627,6 +1626,16 @@ struct hwrm_port_phy_cfg_output {
u8 valid;
};
+/* hwrm_port_phy_cfg_cmd_err (size:64b/8B) */
+struct hwrm_port_phy_cfg_cmd_err {
+ u8 code;
+ #define PORT_PHY_CFG_CMD_ERR_CODE_UNKNOWN 0x0UL
+ #define PORT_PHY_CFG_CMD_ERR_CODE_ILLEGAL_SPEED 0x1UL
+ #define PORT_PHY_CFG_CMD_ERR_CODE_RETRY 0x2UL
+ #define PORT_PHY_CFG_CMD_ERR_CODE_LAST PORT_PHY_CFG_CMD_ERR_CODE_RETRY
+ u8 unused_0[7];
+};
+
/* hwrm_port_phy_qcfg_input (size:192b/24B) */
struct hwrm_port_phy_qcfg_input {
__le16 req_type;
@@ -2030,6 +2039,33 @@ struct hwrm_port_qstats_output {
u8 valid;
};
+/* hwrm_port_qstats_ext_input (size:320b/40B) */
+struct hwrm_port_qstats_ext_input {
+ __le16 req_type;
+ __le16 cmpl_ring;
+ __le16 seq_id;
+ __le16 target_id;
+ __le64 resp_addr;
+ __le16 port_id;
+ __le16 tx_stat_size;
+ __le16 rx_stat_size;
+ u8 unused_0[2];
+ __le64 tx_stat_host_addr;
+ __le64 rx_stat_host_addr;
+};
+
+/* hwrm_port_qstats_ext_output (size:128b/16B) */
+struct hwrm_port_qstats_ext_output {
+ __le16 error_code;
+ __le16 req_type;
+ __le16 seq_id;
+ __le16 resp_len;
+ __le16 tx_stat_size;
+ __le16 rx_stat_size;
+ u8 unused_0[3];
+ u8 valid;
+};
+
/* hwrm_port_lpbk_qstats_input (size:128b/16B) */
struct hwrm_port_lpbk_qstats_input {
__le16 req_type;
@@ -2552,7 +2588,11 @@ struct hwrm_queue_qportcfg_input {
#define QUEUE_QPORTCFG_REQ_FLAGS_PATH_RX 0x1UL
#define QUEUE_QPORTCFG_REQ_FLAGS_PATH_LAST QUEUE_QPORTCFG_REQ_FLAGS_PATH_RX
__le16 port_id;
- u8 unused_0[2];
+ u8 drv_qmap_cap;
+ #define QUEUE_QPORTCFG_REQ_DRV_QMAP_CAP_DISABLED 0x0UL
+ #define QUEUE_QPORTCFG_REQ_DRV_QMAP_CAP_ENABLED 0x1UL
+ #define QUEUE_QPORTCFG_REQ_DRV_QMAP_CAP_LAST QUEUE_QPORTCFG_REQ_DRV_QMAP_CAP_ENABLED
+ u8 unused_0;
};
/* hwrm_queue_qportcfg_output (size:256b/32B) */
@@ -2571,52 +2611,68 @@ struct hwrm_queue_qportcfg_output {
u8 queue_cos2bw_cfg_allowed;
u8 queue_id0;
u8 queue_id0_service_profile;
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID0_SERVICE_PROFILE_LOSSY 0x0UL
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID0_SERVICE_PROFILE_LOSSLESS 0x1UL
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID0_SERVICE_PROFILE_UNKNOWN 0xffUL
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID0_SERVICE_PROFILE_LAST QUEUE_QPORTCFG_RESP_QUEUE_ID0_SERVICE_PROFILE_UNKNOWN
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID0_SERVICE_PROFILE_LOSSY 0x0UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID0_SERVICE_PROFILE_LOSSLESS_ROCE 0x1UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID0_SERVICE_PROFILE_LOSSY_ROCE_CNP 0x2UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID0_SERVICE_PROFILE_LOSSLESS_NIC 0x3UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID0_SERVICE_PROFILE_UNKNOWN 0xffUL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID0_SERVICE_PROFILE_LAST QUEUE_QPORTCFG_RESP_QUEUE_ID0_SERVICE_PROFILE_UNKNOWN
u8 queue_id1;
u8 queue_id1_service_profile;
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID1_SERVICE_PROFILE_LOSSY 0x0UL
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID1_SERVICE_PROFILE_LOSSLESS 0x1UL
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID1_SERVICE_PROFILE_UNKNOWN 0xffUL
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID1_SERVICE_PROFILE_LAST QUEUE_QPORTCFG_RESP_QUEUE_ID1_SERVICE_PROFILE_UNKNOWN
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID1_SERVICE_PROFILE_LOSSY 0x0UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID1_SERVICE_PROFILE_LOSSLESS_ROCE 0x1UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID1_SERVICE_PROFILE_LOSSY_ROCE_CNP 0x2UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID1_SERVICE_PROFILE_LOSSLESS_NIC 0x3UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID1_SERVICE_PROFILE_UNKNOWN 0xffUL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID1_SERVICE_PROFILE_LAST QUEUE_QPORTCFG_RESP_QUEUE_ID1_SERVICE_PROFILE_UNKNOWN
u8 queue_id2;
u8 queue_id2_service_profile;
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID2_SERVICE_PROFILE_LOSSY 0x0UL
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID2_SERVICE_PROFILE_LOSSLESS 0x1UL
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID2_SERVICE_PROFILE_UNKNOWN 0xffUL
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID2_SERVICE_PROFILE_LAST QUEUE_QPORTCFG_RESP_QUEUE_ID2_SERVICE_PROFILE_UNKNOWN
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID2_SERVICE_PROFILE_LOSSY 0x0UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID2_SERVICE_PROFILE_LOSSLESS_ROCE 0x1UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID2_SERVICE_PROFILE_LOSSY_ROCE_CNP 0x2UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID2_SERVICE_PROFILE_LOSSLESS_NIC 0x3UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID2_SERVICE_PROFILE_UNKNOWN 0xffUL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID2_SERVICE_PROFILE_LAST QUEUE_QPORTCFG_RESP_QUEUE_ID2_SERVICE_PROFILE_UNKNOWN
u8 queue_id3;
u8 queue_id3_service_profile;
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID3_SERVICE_PROFILE_LOSSY 0x0UL
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID3_SERVICE_PROFILE_LOSSLESS 0x1UL
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID3_SERVICE_PROFILE_UNKNOWN 0xffUL
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID3_SERVICE_PROFILE_LAST QUEUE_QPORTCFG_RESP_QUEUE_ID3_SERVICE_PROFILE_UNKNOWN
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID3_SERVICE_PROFILE_LOSSY 0x0UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID3_SERVICE_PROFILE_LOSSLESS_ROCE 0x1UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID3_SERVICE_PROFILE_LOSSY_ROCE_CNP 0x2UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID3_SERVICE_PROFILE_LOSSLESS_NIC 0x3UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID3_SERVICE_PROFILE_UNKNOWN 0xffUL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID3_SERVICE_PROFILE_LAST QUEUE_QPORTCFG_RESP_QUEUE_ID3_SERVICE_PROFILE_UNKNOWN
u8 queue_id4;
u8 queue_id4_service_profile;
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID4_SERVICE_PROFILE_LOSSY 0x0UL
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID4_SERVICE_PROFILE_LOSSLESS 0x1UL
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID4_SERVICE_PROFILE_UNKNOWN 0xffUL
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID4_SERVICE_PROFILE_LAST QUEUE_QPORTCFG_RESP_QUEUE_ID4_SERVICE_PROFILE_UNKNOWN
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID4_SERVICE_PROFILE_LOSSY 0x0UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID4_SERVICE_PROFILE_LOSSLESS_ROCE 0x1UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID4_SERVICE_PROFILE_LOSSY_ROCE_CNP 0x2UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID4_SERVICE_PROFILE_LOSSLESS_NIC 0x3UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID4_SERVICE_PROFILE_UNKNOWN 0xffUL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID4_SERVICE_PROFILE_LAST QUEUE_QPORTCFG_RESP_QUEUE_ID4_SERVICE_PROFILE_UNKNOWN
u8 queue_id5;
u8 queue_id5_service_profile;
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID5_SERVICE_PROFILE_LOSSY 0x0UL
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID5_SERVICE_PROFILE_LOSSLESS 0x1UL
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID5_SERVICE_PROFILE_UNKNOWN 0xffUL
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID5_SERVICE_PROFILE_LAST QUEUE_QPORTCFG_RESP_QUEUE_ID5_SERVICE_PROFILE_UNKNOWN
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID5_SERVICE_PROFILE_LOSSY 0x0UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID5_SERVICE_PROFILE_LOSSLESS_ROCE 0x1UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID5_SERVICE_PROFILE_LOSSY_ROCE_CNP 0x2UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID5_SERVICE_PROFILE_LOSSLESS_NIC 0x3UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID5_SERVICE_PROFILE_UNKNOWN 0xffUL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID5_SERVICE_PROFILE_LAST QUEUE_QPORTCFG_RESP_QUEUE_ID5_SERVICE_PROFILE_UNKNOWN
u8 queue_id6;
u8 queue_id6_service_profile;
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID6_SERVICE_PROFILE_LOSSY 0x0UL
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID6_SERVICE_PROFILE_LOSSLESS 0x1UL
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID6_SERVICE_PROFILE_UNKNOWN 0xffUL
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID6_SERVICE_PROFILE_LAST QUEUE_QPORTCFG_RESP_QUEUE_ID6_SERVICE_PROFILE_UNKNOWN
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID6_SERVICE_PROFILE_LOSSY 0x0UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID6_SERVICE_PROFILE_LOSSLESS_ROCE 0x1UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID6_SERVICE_PROFILE_LOSSY_ROCE_CNP 0x2UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID6_SERVICE_PROFILE_LOSSLESS_NIC 0x3UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID6_SERVICE_PROFILE_UNKNOWN 0xffUL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID6_SERVICE_PROFILE_LAST QUEUE_QPORTCFG_RESP_QUEUE_ID6_SERVICE_PROFILE_UNKNOWN
u8 queue_id7;
u8 queue_id7_service_profile;
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID7_SERVICE_PROFILE_LOSSY 0x0UL
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID7_SERVICE_PROFILE_LOSSLESS 0x1UL
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID7_SERVICE_PROFILE_UNKNOWN 0xffUL
- #define QUEUE_QPORTCFG_RESP_QUEUE_ID7_SERVICE_PROFILE_LAST QUEUE_QPORTCFG_RESP_QUEUE_ID7_SERVICE_PROFILE_UNKNOWN
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID7_SERVICE_PROFILE_LOSSY 0x0UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID7_SERVICE_PROFILE_LOSSLESS_ROCE 0x1UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID7_SERVICE_PROFILE_LOSSY_ROCE_CNP 0x2UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID7_SERVICE_PROFILE_LOSSLESS_NIC 0x3UL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID7_SERVICE_PROFILE_UNKNOWN 0xffUL
+ #define QUEUE_QPORTCFG_RESP_QUEUE_ID7_SERVICE_PROFILE_LAST QUEUE_QPORTCFG_RESP_QUEUE_ID7_SERVICE_PROFILE_UNKNOWN
u8 valid;
};
@@ -5180,6 +5236,29 @@ struct hwrm_stat_ctx_clr_stats_output {
u8 valid;
};
+/* hwrm_pcie_qstats_input (size:256b/32B) */
+struct hwrm_pcie_qstats_input {
+ __le16 req_type;
+ __le16 cmpl_ring;
+ __le16 seq_id;
+ __le16 target_id;
+ __le64 resp_addr;
+ __le16 pcie_stat_size;
+ u8 unused_0[6];
+ __le64 pcie_stat_host_addr;
+};
+
+/* hwrm_pcie_qstats_output (size:128b/16B) */
+struct hwrm_pcie_qstats_output {
+ __le16 error_code;
+ __le16 req_type;
+ __le16 seq_id;
+ __le16 resp_len;
+ __le16 pcie_stat_size;
+ u8 unused_0[5];
+ u8 valid;
+};
+
/* tx_port_stats (size:3264b/408B) */
struct tx_port_stats {
__le64 tx_64b_frames;
@@ -5305,6 +5384,30 @@ struct rx_port_stats {
__le64 rx_stat_err;
};
+/* rx_port_stats_ext (size:320b/40B) */
+struct rx_port_stats_ext {
+ __le64 link_down_events;
+ __le64 continuous_pause_events;
+ __le64 resume_pause_events;
+ __le64 continuous_roce_pause_events;
+ __le64 resume_roce_pause_events;
+};
+
+/* pcie_ctx_hw_stats (size:768b/96B) */
+struct pcie_ctx_hw_stats {
+ __le64 pcie_pl_signal_integrity;
+ __le64 pcie_dl_signal_integrity;
+ __le64 pcie_tl_signal_integrity;
+ __le64 pcie_link_integrity;
+ __le64 pcie_tx_traffic_rate;
+ __le64 pcie_rx_traffic_rate;
+ __le64 pcie_tx_dllp_statistics;
+ __le64 pcie_rx_dllp_statistics;
+ __le64 pcie_equalization_time;
+ __le32 pcie_ltssm_histogram[4];
+ __le64 pcie_recovery_histogram;
+};
+
/* hwrm_fw_reset_input (size:192b/24B) */
struct hwrm_fw_reset_input {
__le16 req_type;
@@ -5313,14 +5416,15 @@ struct hwrm_fw_reset_input {
__le16 target_id;
__le64 resp_addr;
u8 embedded_proc_type;
- #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_BOOT 0x0UL
- #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_MGMT 0x1UL
- #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_NETCTRL 0x2UL
- #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_ROCE 0x3UL
- #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_HOST 0x4UL
- #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_AP 0x5UL
- #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_CHIP 0x6UL
- #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_LAST FW_RESET_REQ_EMBEDDED_PROC_TYPE_CHIP
+ #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_BOOT 0x0UL
+ #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_MGMT 0x1UL
+ #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_NETCTRL 0x2UL
+ #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_ROCE 0x3UL
+ #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_HOST 0x4UL
+ #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_AP 0x5UL
+ #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_CHIP 0x6UL
+ #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_HOST_RESOURCE_REINIT 0x7UL
+ #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_LAST FW_RESET_REQ_EMBEDDED_PROC_TYPE_HOST_RESOURCE_REINIT
u8 selfrst_status;
#define FW_RESET_REQ_SELFRST_STATUS_SELFRSTNONE 0x0UL
#define FW_RESET_REQ_SELFRST_STATUS_SELFRSTASAP 0x1UL
@@ -6253,8 +6357,7 @@ struct hwrm_selftest_exec_input {
#define SELFTEST_EXEC_REQ_FLAGS_MEMORY_TEST 0x8UL
#define SELFTEST_EXEC_REQ_FLAGS_PCIE_SERDES_TEST 0x10UL
#define SELFTEST_EXEC_REQ_FLAGS_ETHERNET_SERDES_TEST 0x20UL
- u8 pcie_lane_num;
- u8 unused_0[6];
+ u8 unused_0[7];
};
/* hwrm_selftest_exec_output (size:128b/16B) */
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_nvm_defs.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_nvm_defs.h
index 73f2249555b5..83444811d3c6 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_nvm_defs.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_nvm_defs.h
@@ -59,8 +59,6 @@ enum bnxt_nvm_directory_type {
#define BNX_DIR_ATTR_NO_CHKSUM (1 << 0)
#define BNX_DIR_ATTR_PROP_STREAM (1 << 1)
-#define BNX_PKG_LOG_MAX_LENGTH 4096
-
enum bnxnvm_pkglog_field_index {
BNX_PKG_LOG_FIELD_IDX_INSTALLED_TIMESTAMP = 0,
BNX_PKG_LOG_FIELD_IDX_PKG_DESCRIPTION = 1,
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c
index d87faad901fe..f952963d594e 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c
@@ -1,7 +1,7 @@
/* Broadcom NetXtreme-C/E network driver.
*
* Copyright (c) 2014-2016 Broadcom Corporation
- * Copyright (c) 2016-2017 Broadcom Limited
+ * Copyright (c) 2016-2018 Broadcom Limited
*
* 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
@@ -121,6 +121,23 @@ int bnxt_set_vf_spoofchk(struct net_device *dev, int vf_id, bool setting)
return rc;
}
+int bnxt_set_vf_trust(struct net_device *dev, int vf_id, bool trusted)
+{
+ struct bnxt *bp = netdev_priv(dev);
+ struct bnxt_vf_info *vf;
+
+ if (bnxt_vf_ndo_prep(bp, vf_id))
+ return -EINVAL;
+
+ vf = &bp->pf.vf[vf_id];
+ if (trusted)
+ vf->flags |= BNXT_VF_TRUST;
+ else
+ vf->flags &= ~BNXT_VF_TRUST;
+
+ return 0;
+}
+
int bnxt_get_vf_config(struct net_device *dev, int vf_id,
struct ifla_vf_info *ivi)
{
@@ -147,6 +164,7 @@ int bnxt_get_vf_config(struct net_device *dev, int vf_id,
else
ivi->qos = 0;
ivi->spoofchk = !!(vf->flags & BNXT_VF_SPOOFCHK);
+ ivi->trusted = !!(vf->flags & BNXT_VF_TRUST);
if (!(vf->flags & BNXT_VF_LINK_FORCED))
ivi->linkstate = IFLA_VF_LINK_STATE_AUTO;
else if (vf->flags & BNXT_VF_LINK_UP)
@@ -492,18 +510,16 @@ static int bnxt_hwrm_func_vf_resc_cfg(struct bnxt *bp, int num_vfs)
}
mutex_unlock(&bp->hwrm_cmd_lock);
if (pf->active_vfs) {
- u16 n = 1;
+ u16 n = pf->active_vfs;
- if (pf->vf_resv_strategy != BNXT_VF_RESV_STRATEGY_MINIMAL)
- n = pf->active_vfs;
-
- hw_resc->max_tx_rings -= vf_tx_rings * n;
- hw_resc->max_rx_rings -= vf_rx_rings * n;
- hw_resc->max_hw_ring_grps -= vf_ring_grps * n;
- hw_resc->max_cp_rings -= vf_cp_rings * n;
+ hw_resc->max_tx_rings -= le16_to_cpu(req.min_tx_rings) * n;
+ hw_resc->max_rx_rings -= le16_to_cpu(req.min_rx_rings) * n;
+ hw_resc->max_hw_ring_grps -= le16_to_cpu(req.min_hw_ring_grps) *
+ n;
+ hw_resc->max_cp_rings -= le16_to_cpu(req.min_cmpl_rings) * n;
hw_resc->max_rsscos_ctxs -= pf->active_vfs;
- hw_resc->max_stat_ctxs -= vf_stat_ctx * n;
- hw_resc->max_vnics -= vf_vnics * n;
+ hw_resc->max_stat_ctxs -= le16_to_cpu(req.min_stat_ctx) * n;
+ hw_resc->max_vnics -= le16_to_cpu(req.min_vnics) * n;
rc = pf->active_vfs;
}
@@ -886,18 +902,19 @@ exec_fwd_resp_exit:
return rc;
}
-static int bnxt_vf_store_mac(struct bnxt *bp, struct bnxt_vf_info *vf)
+static int bnxt_vf_configure_mac(struct bnxt *bp, struct bnxt_vf_info *vf)
{
u32 msg_size = sizeof(struct hwrm_func_vf_cfg_input);
struct hwrm_func_vf_cfg_input *req =
(struct hwrm_func_vf_cfg_input *)vf->hwrm_cmd_req_addr;
- /* Only allow VF to set a valid MAC address if the PF assigned MAC
- * address is zero
+ /* Allow VF to set a valid MAC address, if trust is set to on or
+ * if the PF assigned MAC address is zero
*/
if (req->enables & cpu_to_le32(FUNC_VF_CFG_REQ_ENABLES_DFLT_MAC_ADDR)) {
if (is_valid_ether_addr(req->dflt_mac_addr) &&
- !is_valid_ether_addr(vf->mac_addr)) {
+ ((vf->flags & BNXT_VF_TRUST) ||
+ (!is_valid_ether_addr(vf->mac_addr)))) {
ether_addr_copy(vf->vf_mac_addr, req->dflt_mac_addr);
return bnxt_hwrm_exec_fwd_resp(bp, vf, msg_size);
}
@@ -913,11 +930,17 @@ static int bnxt_vf_validate_set_mac(struct bnxt *bp, struct bnxt_vf_info *vf)
(struct hwrm_cfa_l2_filter_alloc_input *)vf->hwrm_cmd_req_addr;
bool mac_ok = false;
- /* VF MAC address must first match PF MAC address, if it is valid.
+ if (!is_valid_ether_addr((const u8 *)req->l2_addr))
+ return bnxt_hwrm_fwd_err_resp(bp, vf, msg_size);
+
+ /* Allow VF to set a valid MAC address, if trust is set to on.
+ * Or VF MAC address must first match MAC address in PF's context.
* Otherwise, it must match the VF MAC address if firmware spec >=
* 1.2.2
*/
- if (is_valid_ether_addr(vf->mac_addr)) {
+ if (vf->flags & BNXT_VF_TRUST) {
+ mac_ok = true;
+ } else if (is_valid_ether_addr(vf->mac_addr)) {
if (ether_addr_equal((const u8 *)req->l2_addr, vf->mac_addr))
mac_ok = true;
} else if (is_valid_ether_addr(vf->vf_mac_addr)) {
@@ -951,7 +974,9 @@ static int bnxt_vf_set_link(struct bnxt *bp, struct bnxt_vf_info *vf)
memcpy(&phy_qcfg_resp, &bp->link_info.phy_qcfg_resp,
sizeof(phy_qcfg_resp));
mutex_unlock(&bp->hwrm_cmd_lock);
+ phy_qcfg_resp.resp_len = cpu_to_le16(sizeof(phy_qcfg_resp));
phy_qcfg_resp.seq_id = phy_qcfg_req->seq_id;
+ phy_qcfg_resp.valid = 1;
if (vf->flags & BNXT_VF_LINK_UP) {
/* if physical link is down, force link up on VF */
@@ -993,7 +1018,7 @@ static int bnxt_vf_req_validate_snd(struct bnxt *bp, struct bnxt_vf_info *vf)
switch (req_type) {
case HWRM_FUNC_VF_CFG:
- rc = bnxt_vf_store_mac(bp, vf);
+ rc = bnxt_vf_configure_mac(bp, vf);
break;
case HWRM_CFA_L2_FILTER_ALLOC:
rc = bnxt_vf_validate_set_mac(bp, vf);
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.h
index dbc8d977fc5a..d10f6f6c7860 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.h
@@ -1,7 +1,7 @@
/* Broadcom NetXtreme-C/E network driver.
*
* Copyright (c) 2014-2016 Broadcom Corporation
- * Copyright (c) 2016-2017 Broadcom Limited
+ * Copyright (c) 2016-2018 Broadcom Limited
*
* 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
@@ -17,6 +17,7 @@ int bnxt_set_vf_vlan(struct net_device *, int, u16, u8, __be16);
int bnxt_set_vf_bw(struct net_device *, int, int, int);
int bnxt_set_vf_link_state(struct net_device *, int, int);
int bnxt_set_vf_spoofchk(struct net_device *, int, bool);
+int bnxt_set_vf_trust(struct net_device *dev, int vf_id, bool trust);
int bnxt_sriov_configure(struct pci_dev *pdev, int num_vfs);
void bnxt_sriov_disable(struct bnxt *);
void bnxt_hwrm_exec_fwd_req(struct bnxt *);
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c
index 65c2cee35766..795f45024c20 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c
@@ -377,6 +377,30 @@ static bool is_wildcard(void *mask, int len)
return true;
}
+static bool is_exactmatch(void *mask, int len)
+{
+ const u8 *p = mask;
+ int i;
+
+ for (i = 0; i < len; i++)
+ if (p[i] != 0xff)
+ return false;
+
+ return true;
+}
+
+static bool bits_set(void *key, int len)
+{
+ const u8 *p = key;
+ int i;
+
+ for (i = 0; i < len; i++)
+ if (p[i] != 0)
+ return true;
+
+ return false;
+}
+
static int bnxt_hwrm_cfa_flow_alloc(struct bnxt *bp, struct bnxt_tc_flow *flow,
__le16 ref_flow_handle,
__le32 tunnel_handle, __le16 *flow_handle)
@@ -764,6 +788,41 @@ static bool bnxt_tc_can_offload(struct bnxt *bp, struct bnxt_tc_flow *flow)
return false;
}
+ /* Currently source/dest MAC cannot be partial wildcard */
+ if (bits_set(&flow->l2_key.smac, sizeof(flow->l2_key.smac)) &&
+ !is_exactmatch(flow->l2_mask.smac, sizeof(flow->l2_mask.smac))) {
+ netdev_info(bp->dev, "Wildcard match unsupported for Source MAC\n");
+ return false;
+ }
+ if (bits_set(&flow->l2_key.dmac, sizeof(flow->l2_key.dmac)) &&
+ !is_exactmatch(&flow->l2_mask.dmac, sizeof(flow->l2_mask.dmac))) {
+ netdev_info(bp->dev, "Wildcard match unsupported for Dest MAC\n");
+ return false;
+ }
+
+ /* Currently VLAN fields cannot be partial wildcard */
+ if (bits_set(&flow->l2_key.inner_vlan_tci,
+ sizeof(flow->l2_key.inner_vlan_tci)) &&
+ !is_exactmatch(&flow->l2_mask.inner_vlan_tci,
+ sizeof(flow->l2_mask.inner_vlan_tci))) {
+ netdev_info(bp->dev, "Wildcard match unsupported for VLAN TCI\n");
+ return false;
+ }
+ if (bits_set(&flow->l2_key.inner_vlan_tpid,
+ sizeof(flow->l2_key.inner_vlan_tpid)) &&
+ !is_exactmatch(&flow->l2_mask.inner_vlan_tpid,
+ sizeof(flow->l2_mask.inner_vlan_tpid))) {
+ netdev_info(bp->dev, "Wildcard match unsupported for VLAN TPID\n");
+ return false;
+ }
+
+ /* Currently Ethertype must be set */
+ if (!is_exactmatch(&flow->l2_mask.ether_type,
+ sizeof(flow->l2_mask.ether_type))) {
+ netdev_info(bp->dev, "Wildcard match unsupported for Ethertype\n");
+ return false;
+ }
+
return true;
}
@@ -992,8 +1051,10 @@ static int bnxt_tc_get_decap_handle(struct bnxt *bp, struct bnxt_tc_flow *flow,
/* Check if there's another flow using the same tunnel decap.
* If not, add this tunnel to the table and resolve the other
- * tunnel header fileds
+ * tunnel header fileds. Ignore src_port in the tunnel_key,
+ * since it is not required for decap filters.
*/
+ decap_key->tp_src = 0;
decap_node = bnxt_tc_get_tunnel_node(bp, &tc_info->decap_table,
&tc_info->decap_ht_params,
decap_key);
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c
index 997e10e8b863..347e4f946eb2 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c
@@ -1,6 +1,6 @@
/* Broadcom NetXtreme-C/E network driver.
*
- * Copyright (c) 2016 Broadcom Limited
+ * Copyright (c) 2016-2018 Broadcom Limited
*
* 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
@@ -101,13 +101,28 @@ static int bnxt_unregister_dev(struct bnxt_en_dev *edev, int ulp_id)
return 0;
}
+static void bnxt_fill_msix_vecs(struct bnxt *bp, struct bnxt_msix_entry *ent)
+{
+ struct bnxt_en_dev *edev = bp->edev;
+ int num_msix, idx, i;
+
+ num_msix = edev->ulp_tbl[BNXT_ROCE_ULP].msix_requested;
+ idx = edev->ulp_tbl[BNXT_ROCE_ULP].msix_base;
+ for (i = 0; i < num_msix; i++) {
+ ent[i].vector = bp->irq_tbl[idx + i].vector;
+ ent[i].ring_idx = idx + i;
+ ent[i].db_offset = (idx + i) * 0x80;
+ }
+}
+
static int bnxt_req_msix_vecs(struct bnxt_en_dev *edev, int ulp_id,
struct bnxt_msix_entry *ent, int num_msix)
{
struct net_device *dev = edev->net;
struct bnxt *bp = netdev_priv(dev);
int max_idx, max_cp_rings;
- int avail_msix, i, idx;
+ int avail_msix, idx;
+ int rc = 0;
ASSERT_RTNL();
if (ulp_id != BNXT_ROCE_ULP)
@@ -116,23 +131,47 @@ static int bnxt_req_msix_vecs(struct bnxt_en_dev *edev, int ulp_id,
if (!(bp->flags & BNXT_FLAG_USING_MSIX))
return -ENODEV;
+ if (edev->ulp_tbl[ulp_id].msix_requested)
+ return -EAGAIN;
+
max_cp_rings = bnxt_get_max_func_cp_rings(bp);
- max_idx = min_t(int, bp->total_irqs, max_cp_rings);
- avail_msix = max_idx - bp->cp_nr_rings;
+ avail_msix = bnxt_get_avail_msix(bp, num_msix);
if (!avail_msix)
return -ENOMEM;
if (avail_msix > num_msix)
avail_msix = num_msix;
- idx = max_idx - avail_msix;
- for (i = 0; i < avail_msix; i++) {
- ent[i].vector = bp->irq_tbl[idx + i].vector;
- ent[i].ring_idx = idx + i;
- ent[i].db_offset = (idx + i) * 0x80;
+ if (bp->flags & BNXT_FLAG_NEW_RM) {
+ idx = bp->cp_nr_rings;
+ } else {
+ max_idx = min_t(int, bp->total_irqs, max_cp_rings);
+ idx = max_idx - avail_msix;
}
- bnxt_set_max_func_irqs(bp, max_idx - avail_msix);
- bnxt_set_max_func_cp_rings(bp, max_cp_rings - avail_msix);
+ edev->ulp_tbl[ulp_id].msix_base = idx;
edev->ulp_tbl[ulp_id].msix_requested = avail_msix;
+ if (bp->total_irqs < (idx + avail_msix)) {
+ if (netif_running(dev)) {
+ bnxt_close_nic(bp, true, false);
+ rc = bnxt_open_nic(bp, true, false);
+ } else {
+ rc = bnxt_reserve_rings(bp);
+ }
+ }
+ if (rc) {
+ edev->ulp_tbl[ulp_id].msix_requested = 0;
+ return -EAGAIN;
+ }
+
+ if (bp->flags & BNXT_FLAG_NEW_RM) {
+ struct bnxt_hw_resc *hw_resc = &bp->hw_resc;
+
+ avail_msix = hw_resc->resv_cp_rings - bp->cp_nr_rings;
+ edev->ulp_tbl[ulp_id].msix_requested = avail_msix;
+ }
+ bnxt_fill_msix_vecs(bp, ent);
+ bnxt_set_max_func_irqs(bp, bnxt_get_max_func_irqs(bp) - avail_msix);
+ bnxt_set_max_func_cp_rings(bp, max_cp_rings - avail_msix);
+ edev->flags |= BNXT_EN_FLAG_MSIX_REQUESTED;
return avail_msix;
}
@@ -146,11 +185,40 @@ static int bnxt_free_msix_vecs(struct bnxt_en_dev *edev, int ulp_id)
if (ulp_id != BNXT_ROCE_ULP)
return -EINVAL;
+ if (!(edev->flags & BNXT_EN_FLAG_MSIX_REQUESTED))
+ return 0;
+
max_cp_rings = bnxt_get_max_func_cp_rings(bp);
msix_requested = edev->ulp_tbl[ulp_id].msix_requested;
bnxt_set_max_func_cp_rings(bp, max_cp_rings + msix_requested);
edev->ulp_tbl[ulp_id].msix_requested = 0;
- bnxt_set_max_func_irqs(bp, bp->total_irqs);
+ bnxt_set_max_func_irqs(bp, bnxt_get_max_func_irqs(bp) + msix_requested);
+ edev->flags &= ~BNXT_EN_FLAG_MSIX_REQUESTED;
+ if (netif_running(dev)) {
+ bnxt_close_nic(bp, true, false);
+ bnxt_open_nic(bp, true, false);
+ }
+ return 0;
+}
+
+int bnxt_get_ulp_msix_num(struct bnxt *bp)
+{
+ if (bnxt_ulp_registered(bp->edev, BNXT_ROCE_ULP)) {
+ struct bnxt_en_dev *edev = bp->edev;
+
+ return edev->ulp_tbl[BNXT_ROCE_ULP].msix_requested;
+ }
+ return 0;
+}
+
+int bnxt_get_ulp_msix_base(struct bnxt *bp)
+{
+ if (bnxt_ulp_registered(bp->edev, BNXT_ROCE_ULP)) {
+ struct bnxt_en_dev *edev = bp->edev;
+
+ if (edev->ulp_tbl[BNXT_ROCE_ULP].msix_requested)
+ return edev->ulp_tbl[BNXT_ROCE_ULP].msix_base;
+ }
return 0;
}
@@ -287,6 +355,58 @@ void bnxt_ulp_shutdown(struct bnxt *bp)
}
}
+void bnxt_ulp_irq_stop(struct bnxt *bp)
+{
+ struct bnxt_en_dev *edev = bp->edev;
+ struct bnxt_ulp_ops *ops;
+
+ if (!edev || !(edev->flags & BNXT_EN_FLAG_MSIX_REQUESTED))
+ return;
+
+ if (bnxt_ulp_registered(bp->edev, BNXT_ROCE_ULP)) {
+ struct bnxt_ulp *ulp = &edev->ulp_tbl[BNXT_ROCE_ULP];
+
+ if (!ulp->msix_requested)
+ return;
+
+ ops = rtnl_dereference(ulp->ulp_ops);
+ if (!ops || !ops->ulp_irq_stop)
+ return;
+ ops->ulp_irq_stop(ulp->handle);
+ }
+}
+
+void bnxt_ulp_irq_restart(struct bnxt *bp, int err)
+{
+ struct bnxt_en_dev *edev = bp->edev;
+ struct bnxt_ulp_ops *ops;
+
+ if (!edev || !(edev->flags & BNXT_EN_FLAG_MSIX_REQUESTED))
+ return;
+
+ if (bnxt_ulp_registered(bp->edev, BNXT_ROCE_ULP)) {
+ struct bnxt_ulp *ulp = &edev->ulp_tbl[BNXT_ROCE_ULP];
+ struct bnxt_msix_entry *ent = NULL;
+
+ if (!ulp->msix_requested)
+ return;
+
+ ops = rtnl_dereference(ulp->ulp_ops);
+ if (!ops || !ops->ulp_irq_restart)
+ return;
+
+ if (!err) {
+ ent = kcalloc(ulp->msix_requested, sizeof(*ent),
+ GFP_KERNEL);
+ if (!ent)
+ return;
+ bnxt_fill_msix_vecs(bp, ent);
+ }
+ ops->ulp_irq_restart(ulp->handle, ent);
+ kfree(ent);
+ }
+}
+
void bnxt_ulp_async_events(struct bnxt *bp, struct hwrm_async_event_cmpl *cmpl)
{
u16 event_id = le16_to_cpu(cmpl->event_id);
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h
index d2471067dc37..df48ac71729f 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h
@@ -1,6 +1,6 @@
/* Broadcom NetXtreme-C/E network driver.
*
- * Copyright (c) 2016 Broadcom Limited
+ * Copyright (c) 2016-2018 Broadcom Limited
*
* 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
@@ -20,6 +20,12 @@
struct hwrm_async_event_cmpl;
struct bnxt;
+struct bnxt_msix_entry {
+ u32 vector;
+ u32 ring_idx;
+ u32 db_offset;
+};
+
struct bnxt_ulp_ops {
/* async_notifier() cannot sleep (in BH context) */
void (*ulp_async_notifier)(void *, struct hwrm_async_event_cmpl *);
@@ -27,12 +33,8 @@ struct bnxt_ulp_ops {
void (*ulp_start)(void *);
void (*ulp_sriov_config)(void *, int);
void (*ulp_shutdown)(void *);
-};
-
-struct bnxt_msix_entry {
- u32 vector;
- u32 ring_idx;
- u32 db_offset;
+ void (*ulp_irq_stop)(void *);
+ void (*ulp_irq_restart)(void *, struct bnxt_msix_entry *);
};
struct bnxt_fw_msg {
@@ -49,6 +51,7 @@ struct bnxt_ulp {
unsigned long *async_events_bmap;
u16 max_async_event_id;
u16 msix_requested;
+ u16 msix_base;
atomic_t ref_count;
};
@@ -60,6 +63,7 @@ struct bnxt_en_dev {
#define BNXT_EN_FLAG_ROCEV2_CAP 0x2
#define BNXT_EN_FLAG_ROCE_CAP (BNXT_EN_FLAG_ROCEV1_CAP | \
BNXT_EN_FLAG_ROCEV2_CAP)
+ #define BNXT_EN_FLAG_MSIX_REQUESTED 0x4
const struct bnxt_en_ops *en_ops;
struct bnxt_ulp ulp_tbl[BNXT_MAX_ULP];
};
@@ -84,11 +88,15 @@ static inline bool bnxt_ulp_registered(struct bnxt_en_dev *edev, int ulp_id)
return false;
}
+int bnxt_get_ulp_msix_num(struct bnxt *bp);
+int bnxt_get_ulp_msix_base(struct bnxt *bp);
void bnxt_subtract_ulp_resources(struct bnxt *bp, int ulp_id);
void bnxt_ulp_stop(struct bnxt *bp);
void bnxt_ulp_start(struct bnxt *bp);
void bnxt_ulp_sriov_cfg(struct bnxt *bp, int num_vfs);
void bnxt_ulp_shutdown(struct bnxt *bp);
+void bnxt_ulp_irq_stop(struct bnxt *bp);
+void bnxt_ulp_irq_restart(struct bnxt *bp, int err);
void bnxt_ulp_async_events(struct bnxt *bp, struct hwrm_async_event_cmpl *cmpl);
struct bnxt_en_dev *bnxt_ulp_probe(struct net_device *dev);
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c
index 26290403f38f..38f635cf8408 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c
@@ -64,6 +64,31 @@ static int hwrm_cfa_vfr_free(struct bnxt *bp, u16 vf_idx)
return rc;
}
+static int bnxt_hwrm_vfr_qcfg(struct bnxt *bp, struct bnxt_vf_rep *vf_rep,
+ u16 *max_mtu)
+{
+ struct hwrm_func_qcfg_output *resp = bp->hwrm_cmd_resp_addr;
+ struct hwrm_func_qcfg_input req = {0};
+ u16 mtu;
+ int rc;
+
+ bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_QCFG, -1, -1);
+ req.fid = cpu_to_le16(bp->pf.vf[vf_rep->vf_idx].fw_fid);
+
+ mutex_lock(&bp->hwrm_cmd_lock);
+
+ rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+ if (!rc) {
+ mtu = le16_to_cpu(resp->max_mtu_configured);
+ if (!mtu)
+ *max_mtu = BNXT_MAX_MTU;
+ else
+ *max_mtu = mtu;
+ }
+ mutex_unlock(&bp->hwrm_cmd_lock);
+ return rc;
+}
+
static int bnxt_vf_rep_open(struct net_device *dev)
{
struct bnxt_vf_rep *vf_rep = netdev_priv(dev);
@@ -365,6 +390,7 @@ static void bnxt_vf_rep_netdev_init(struct bnxt *bp, struct bnxt_vf_rep *vf_rep,
struct net_device *dev)
{
struct net_device *pf_dev = bp->dev;
+ u16 max_mtu;
dev->netdev_ops = &bnxt_vf_rep_netdev_ops;
dev->ethtool_ops = &bnxt_vf_rep_ethtool_ops;
@@ -380,6 +406,10 @@ static void bnxt_vf_rep_netdev_init(struct bnxt *bp, struct bnxt_vf_rep *vf_rep,
bnxt_vf_rep_eth_addr_gen(bp->pf.mac_addr, vf_rep->vf_idx,
dev->perm_addr);
ether_addr_copy(dev->dev_addr, dev->perm_addr);
+ /* Set VF-Rep's max-mtu to the corresponding VF's max-mtu */
+ if (!bnxt_hwrm_vfr_qcfg(bp, vf_rep, &max_mtu))
+ dev->max_mtu = max_mtu;
+ dev->min_mtu = ETH_ZLEN;
}
static int bnxt_pcie_dsn_get(struct bnxt *bp, u8 dsn[])
diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
index b1e35a9accf1..0445f2c0c629 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
@@ -603,6 +603,8 @@ static int bcmgenet_get_coalesce(struct net_device *dev,
struct ethtool_coalesce *ec)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
+ struct bcmgenet_rx_ring *ring;
+ unsigned int i;
ec->tx_max_coalesced_frames =
bcmgenet_tdma_ring_readl(priv, DESC_INDEX,
@@ -613,15 +615,57 @@ static int bcmgenet_get_coalesce(struct net_device *dev,
ec->rx_coalesce_usecs =
bcmgenet_rdma_readl(priv, DMA_RING16_TIMEOUT) * 8192 / 1000;
+ for (i = 0; i < priv->hw_params->rx_queues; i++) {
+ ring = &priv->rx_rings[i];
+ ec->use_adaptive_rx_coalesce |= ring->dim.use_dim;
+ }
+ ring = &priv->rx_rings[DESC_INDEX];
+ ec->use_adaptive_rx_coalesce |= ring->dim.use_dim;
+
return 0;
}
+static void bcmgenet_set_rx_coalesce(struct bcmgenet_rx_ring *ring,
+ u32 usecs, u32 pkts)
+{
+ struct bcmgenet_priv *priv = ring->priv;
+ unsigned int i = ring->index;
+ u32 reg;
+
+ bcmgenet_rdma_ring_writel(priv, i, pkts, DMA_MBUF_DONE_THRESH);
+
+ reg = bcmgenet_rdma_readl(priv, DMA_RING0_TIMEOUT + i);
+ reg &= ~DMA_TIMEOUT_MASK;
+ reg |= DIV_ROUND_UP(usecs * 1000, 8192);
+ bcmgenet_rdma_writel(priv, reg, DMA_RING0_TIMEOUT + i);
+}
+
+static void bcmgenet_set_ring_rx_coalesce(struct bcmgenet_rx_ring *ring,
+ struct ethtool_coalesce *ec)
+{
+ struct net_dim_cq_moder moder;
+ u32 usecs, pkts;
+
+ ring->rx_coalesce_usecs = ec->rx_coalesce_usecs;
+ ring->rx_max_coalesced_frames = ec->rx_max_coalesced_frames;
+ usecs = ring->rx_coalesce_usecs;
+ pkts = ring->rx_max_coalesced_frames;
+
+ if (ec->use_adaptive_rx_coalesce && !ring->dim.use_dim) {
+ moder = net_dim_get_def_profile(ring->dim.dim.mode);
+ usecs = moder.usec;
+ pkts = moder.pkts;
+ }
+
+ ring->dim.use_dim = ec->use_adaptive_rx_coalesce;
+ bcmgenet_set_rx_coalesce(ring, usecs, pkts);
+}
+
static int bcmgenet_set_coalesce(struct net_device *dev,
struct ethtool_coalesce *ec)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
unsigned int i;
- u32 reg;
/* Base system clock is 125Mhz, DMA timeout is this reference clock
* divided by 1024, which yields roughly 8.192us, our maximum value
@@ -641,7 +685,8 @@ static int bcmgenet_set_coalesce(struct net_device *dev,
* transmitted, or when the ring is empty.
*/
if (ec->tx_coalesce_usecs || ec->tx_coalesce_usecs_high ||
- ec->tx_coalesce_usecs_irq || ec->tx_coalesce_usecs_low)
+ ec->tx_coalesce_usecs_irq || ec->tx_coalesce_usecs_low ||
+ ec->use_adaptive_tx_coalesce)
return -EOPNOTSUPP;
/* Program all TX queues with the same values, as there is no
@@ -655,25 +700,9 @@ static int bcmgenet_set_coalesce(struct net_device *dev,
ec->tx_max_coalesced_frames,
DMA_MBUF_DONE_THRESH);
- for (i = 0; i < priv->hw_params->rx_queues; i++) {
- bcmgenet_rdma_ring_writel(priv, i,
- ec->rx_max_coalesced_frames,
- DMA_MBUF_DONE_THRESH);
-
- reg = bcmgenet_rdma_readl(priv, DMA_RING0_TIMEOUT + i);
- reg &= ~DMA_TIMEOUT_MASK;
- reg |= DIV_ROUND_UP(ec->rx_coalesce_usecs * 1000, 8192);
- bcmgenet_rdma_writel(priv, reg, DMA_RING0_TIMEOUT + i);
- }
-
- bcmgenet_rdma_ring_writel(priv, DESC_INDEX,
- ec->rx_max_coalesced_frames,
- DMA_MBUF_DONE_THRESH);
-
- reg = bcmgenet_rdma_readl(priv, DMA_RING16_TIMEOUT);
- reg &= ~DMA_TIMEOUT_MASK;
- reg |= DIV_ROUND_UP(ec->rx_coalesce_usecs * 1000, 8192);
- bcmgenet_rdma_writel(priv, reg, DMA_RING16_TIMEOUT);
+ for (i = 0; i < priv->hw_params->rx_queues; i++)
+ bcmgenet_set_ring_rx_coalesce(&priv->rx_rings[i], ec);
+ bcmgenet_set_ring_rx_coalesce(&priv->rx_rings[DESC_INDEX], ec);
return 0;
}
@@ -1321,7 +1350,7 @@ static struct sk_buff *bcmgenet_free_tx_cb(struct device *dev,
dma_unmap_addr_set(cb, dma_addr, 0);
}
- return 0;
+ return NULL;
}
/* Simple helper to free a receive control block's resources */
@@ -1460,7 +1489,7 @@ static struct sk_buff *bcmgenet_put_tx_csum(struct net_device *dev,
struct sk_buff *new_skb;
u16 offset;
u8 ip_proto;
- u16 ip_ver;
+ __be16 ip_ver;
u32 tx_csum_info;
if (unlikely(skb_headroom(skb) < sizeof(*status))) {
@@ -1480,12 +1509,12 @@ static struct sk_buff *bcmgenet_put_tx_csum(struct net_device *dev,
status = (struct status_64 *)skb->data;
if (skb->ip_summed == CHECKSUM_PARTIAL) {
- ip_ver = htons(skb->protocol);
+ ip_ver = skb->protocol;
switch (ip_ver) {
- case ETH_P_IP:
+ case htons(ETH_P_IP):
ip_proto = ip_hdr(skb)->protocol;
break;
- case ETH_P_IPV6:
+ case htons(ETH_P_IPV6):
ip_proto = ipv6_hdr(skb)->nexthdr;
break;
default:
@@ -1501,7 +1530,8 @@ static struct sk_buff *bcmgenet_put_tx_csum(struct net_device *dev,
*/
if (ip_proto == IPPROTO_TCP || ip_proto == IPPROTO_UDP) {
tx_csum_info |= STATUS_TX_CSUM_LV;
- if (ip_proto == IPPROTO_UDP && ip_ver == ETH_P_IP)
+ if (ip_proto == IPPROTO_UDP &&
+ ip_ver == htons(ETH_P_IP))
tx_csum_info |= STATUS_TX_CSUM_PROTO_UDP;
} else {
tx_csum_info = 0;
@@ -1713,6 +1743,7 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_rx_ring *ring,
unsigned long dma_flag;
int len;
unsigned int rxpktprocessed = 0, rxpkttoprocess;
+ unsigned int bytes_processed = 0;
unsigned int p_index, mask;
unsigned int discards;
unsigned int chksum_ok = 0;
@@ -1832,6 +1863,8 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_rx_ring *ring,
len -= ETH_FCS_LEN;
}
+ bytes_processed += len;
+
/*Finish setting up the received SKB and send it to the kernel*/
skb->protocol = eth_type_trans(skb, priv->dev);
ring->packets++;
@@ -1854,6 +1887,9 @@ next:
bcmgenet_rdma_ring_writel(priv, ring->index, ring->c_index, RDMA_CONS_INDEX);
}
+ ring->dim.bytes = bytes_processed;
+ ring->dim.packets = rxpktprocessed;
+
return rxpktprocessed;
}
@@ -1862,6 +1898,7 @@ static int bcmgenet_rx_poll(struct napi_struct *napi, int budget)
{
struct bcmgenet_rx_ring *ring = container_of(napi,
struct bcmgenet_rx_ring, napi);
+ struct net_dim_sample dim_sample;
unsigned int work_done;
work_done = bcmgenet_desc_rx(ring, budget);
@@ -1871,9 +1908,29 @@ static int bcmgenet_rx_poll(struct napi_struct *napi, int budget)
ring->int_enable(ring);
}
+ if (ring->dim.use_dim) {
+ net_dim_sample(ring->dim.event_ctr, ring->dim.packets,
+ ring->dim.bytes, &dim_sample);
+ net_dim(&ring->dim.dim, dim_sample);
+ }
+
return work_done;
}
+static void bcmgenet_dim_work(struct work_struct *work)
+{
+ struct net_dim *dim = container_of(work, struct net_dim, work);
+ struct bcmgenet_net_dim *ndim =
+ container_of(dim, struct bcmgenet_net_dim, dim);
+ struct bcmgenet_rx_ring *ring =
+ container_of(ndim, struct bcmgenet_rx_ring, dim);
+ struct net_dim_cq_moder cur_profile =
+ net_dim_get_profile(dim->mode, dim->profile_ix);
+
+ bcmgenet_set_rx_coalesce(ring, cur_profile.usec, cur_profile.pkts);
+ dim->state = NET_DIM_START_MEASURE;
+}
+
/* Assign skb to RX DMA descriptor. */
static int bcmgenet_alloc_rx_buffers(struct bcmgenet_priv *priv,
struct bcmgenet_rx_ring *ring)
@@ -2022,6 +2079,37 @@ static void init_umac(struct bcmgenet_priv *priv)
dev_dbg(kdev, "done init umac\n");
}
+static void bcmgenet_init_dim(struct bcmgenet_rx_ring *ring,
+ void (*cb)(struct work_struct *work))
+{
+ struct bcmgenet_net_dim *dim = &ring->dim;
+
+ INIT_WORK(&dim->dim.work, cb);
+ dim->dim.mode = NET_DIM_CQ_PERIOD_MODE_START_FROM_EQE;
+ dim->event_ctr = 0;
+ dim->packets = 0;
+ dim->bytes = 0;
+}
+
+static void bcmgenet_init_rx_coalesce(struct bcmgenet_rx_ring *ring)
+{
+ struct bcmgenet_net_dim *dim = &ring->dim;
+ struct net_dim_cq_moder moder;
+ u32 usecs, pkts;
+
+ usecs = ring->rx_coalesce_usecs;
+ pkts = ring->rx_max_coalesced_frames;
+
+ /* If DIM was enabled, re-apply default parameters */
+ if (dim->use_dim) {
+ moder = net_dim_get_def_profile(dim->dim.mode);
+ usecs = moder.usec;
+ pkts = moder.pkts;
+ }
+
+ bcmgenet_set_rx_coalesce(ring, usecs, pkts);
+}
+
/* Initialize a Tx ring along with corresponding hardware registers */
static void bcmgenet_init_tx_ring(struct bcmgenet_priv *priv,
unsigned int index, unsigned int size,
@@ -2111,13 +2199,15 @@ static int bcmgenet_init_rx_ring(struct bcmgenet_priv *priv,
if (ret)
return ret;
+ bcmgenet_init_dim(ring, bcmgenet_dim_work);
+ bcmgenet_init_rx_coalesce(ring);
+
/* Initialize Rx NAPI */
netif_napi_add(priv->dev, &ring->napi, bcmgenet_rx_poll,
NAPI_POLL_WEIGHT);
bcmgenet_rdma_ring_writel(priv, index, 0, RDMA_PROD_INDEX);
bcmgenet_rdma_ring_writel(priv, index, 0, RDMA_CONS_INDEX);
- bcmgenet_rdma_ring_writel(priv, index, 1, DMA_MBUF_DONE_THRESH);
bcmgenet_rdma_ring_writel(priv, index,
((size << DMA_RING_SIZE_SHIFT) |
RX_BUF_LENGTH), DMA_RING_BUF_SIZE);
@@ -2276,10 +2366,12 @@ static void bcmgenet_disable_rx_napi(struct bcmgenet_priv *priv)
for (i = 0; i < priv->hw_params->rx_queues; ++i) {
ring = &priv->rx_rings[i];
napi_disable(&ring->napi);
+ cancel_work_sync(&ring->dim.dim.work);
}
ring = &priv->rx_rings[DESC_INDEX];
napi_disable(&ring->napi);
+ cancel_work_sync(&ring->dim.dim.work);
}
static void bcmgenet_fini_rx_napi(struct bcmgenet_priv *priv)
@@ -2557,6 +2649,7 @@ static irqreturn_t bcmgenet_isr1(int irq, void *dev_id)
continue;
rx_ring = &priv->rx_rings[index];
+ rx_ring->dim.event_ctr++;
if (likely(napi_schedule_prep(&rx_ring->napi))) {
rx_ring->int_disable(rx_ring);
@@ -2601,6 +2694,7 @@ static irqreturn_t bcmgenet_isr0(int irq, void *dev_id)
if (status & UMAC_IRQ_RXDMA_DONE) {
rx_ring = &priv->rx_rings[DESC_INDEX];
+ rx_ring->dim.event_ctr++;
if (likely(napi_schedule_prep(&rx_ring->napi))) {
rx_ring->int_disable(rx_ring);
@@ -3351,6 +3445,7 @@ static int bcmgenet_probe(struct platform_device *pdev)
struct net_device *dev;
const void *macaddr;
struct resource *r;
+ unsigned int i;
int err = -EIO;
const char *phy_mode_str;
@@ -3479,6 +3574,11 @@ static int bcmgenet_probe(struct platform_device *pdev)
netif_set_real_num_tx_queues(priv->dev, priv->hw_params->tx_queues + 1);
netif_set_real_num_rx_queues(priv->dev, priv->hw_params->rx_queues + 1);
+ /* Set default coalescing parameters */
+ for (i = 0; i < priv->hw_params->rx_queues; i++)
+ priv->rx_rings[i].rx_max_coalesced_frames = 1;
+ priv->rx_rings[DESC_INDEX].rx_max_coalesced_frames = 1;
+
/* libphy will determine the link state */
netif_carrier_off(dev);
diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h
index 3c50431ccd2a..b773bc07edf7 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmgenet.h
+++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h
@@ -16,6 +16,7 @@
#include <linux/mii.h>
#include <linux/if_vlan.h>
#include <linux/phy.h>
+#include <linux/net_dim.h>
/* total number of Buffer Descriptors, same for Rx/Tx */
#define TOTAL_DESC 256
@@ -572,6 +573,14 @@ struct bcmgenet_tx_ring {
struct bcmgenet_priv *priv;
};
+struct bcmgenet_net_dim {
+ u16 use_dim;
+ u16 event_ctr;
+ unsigned long packets;
+ unsigned long bytes;
+ struct net_dim dim;
+};
+
struct bcmgenet_rx_ring {
struct napi_struct napi; /* Rx NAPI struct */
unsigned long bytes;
@@ -586,6 +595,9 @@ struct bcmgenet_rx_ring {
unsigned int cb_ptr; /* Rx ring initial CB ptr */
unsigned int end_ptr; /* Rx ring end CB ptr */
unsigned int old_discards;
+ struct bcmgenet_net_dim dim;
+ u32 rx_max_coalesced_frames;
+ u32 rx_coalesce_usecs;
void (*int_enable)(struct bcmgenet_rx_ring *);
void (*int_disable)(struct bcmgenet_rx_ring *);
struct bcmgenet_priv *priv;
diff --git a/drivers/net/ethernet/broadcom/sb1250-mac.c b/drivers/net/ethernet/broadcom/sb1250-mac.c
index ecdef42f0ae6..ef4a0c326736 100644
--- a/drivers/net/ethernet/broadcom/sb1250-mac.c
+++ b/drivers/net/ethernet/broadcom/sb1250-mac.c
@@ -63,24 +63,24 @@ MODULE_DESCRIPTION("Broadcom SiByte SOC GB Ethernet driver");
/* 1 normal messages, 0 quiet .. 7 verbose. */
static int debug = 1;
-module_param(debug, int, S_IRUGO);
+module_param(debug, int, 0444);
MODULE_PARM_DESC(debug, "Debug messages");
#ifdef CONFIG_SBMAC_COALESCE
static int int_pktcnt_tx = 255;
-module_param(int_pktcnt_tx, int, S_IRUGO);
+module_param(int_pktcnt_tx, int, 0444);
MODULE_PARM_DESC(int_pktcnt_tx, "TX packet count");
static int int_timeout_tx = 255;
-module_param(int_timeout_tx, int, S_IRUGO);
+module_param(int_timeout_tx, int, 0444);
MODULE_PARM_DESC(int_timeout_tx, "TX timeout value");
static int int_pktcnt_rx = 64;
-module_param(int_pktcnt_rx, int, S_IRUGO);
+module_param(int_pktcnt_rx, int, 0444);
MODULE_PARM_DESC(int_pktcnt_rx, "RX packet count");
static int int_timeout_rx = 64;
-module_param(int_timeout_rx, int, S_IRUGO);
+module_param(int_timeout_rx, int, 0444);
MODULE_PARM_DESC(int_timeout_rx, "RX timeout value");
#endif
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index f2593978ae75..08bbb639be1a 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -10799,11 +10799,11 @@ static ssize_t tg3_show_temp(struct device *dev,
}
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, tg3_show_temp, NULL,
+static SENSOR_DEVICE_ATTR(temp1_input, 0444, tg3_show_temp, NULL,
TG3_TEMP_SENSOR_OFFSET);
-static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, tg3_show_temp, NULL,
+static SENSOR_DEVICE_ATTR(temp1_crit, 0444, tg3_show_temp, NULL,
TG3_TEMP_CAUTION_OFFSET);
-static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, tg3_show_temp, NULL,
+static SENSOR_DEVICE_ATTR(temp1_max, 0444, tg3_show_temp, NULL,
TG3_TEMP_MAX_OFFSET);
static struct attribute *tg3_attrs[] = {
diff --git a/drivers/net/ethernet/brocade/bna/bnad.c b/drivers/net/ethernet/brocade/bna/bnad.c
index a843076597ec..69cc3e0119d6 100644
--- a/drivers/net/ethernet/brocade/bna/bnad.c
+++ b/drivers/net/ethernet/brocade/bna/bnad.c
@@ -46,7 +46,7 @@ module_param(bnad_ioc_auto_recover, uint, 0444);
MODULE_PARM_DESC(bnad_ioc_auto_recover, "Enable / Disable auto recovery");
static uint bna_debugfs_enable = 1;
-module_param(bna_debugfs_enable, uint, S_IRUGO | S_IWUSR);
+module_param(bna_debugfs_enable, uint, 0644);
MODULE_PARM_DESC(bna_debugfs_enable, "Enables debugfs feature, default=1,"
" Range[false:0|true:1]");
diff --git a/drivers/net/ethernet/brocade/bna/bnad_debugfs.c b/drivers/net/ethernet/brocade/bna/bnad_debugfs.c
index cebfe3bd086e..933799be0471 100644
--- a/drivers/net/ethernet/brocade/bna/bnad_debugfs.c
+++ b/drivers/net/ethernet/brocade/bna/bnad_debugfs.c
@@ -486,11 +486,11 @@ struct bnad_debugfs_entry {
};
static const struct bnad_debugfs_entry bnad_debugfs_files[] = {
- { "fwtrc", S_IFREG|S_IRUGO, &bnad_debugfs_op_fwtrc, },
- { "fwsave", S_IFREG|S_IRUGO, &bnad_debugfs_op_fwsave, },
- { "regrd", S_IFREG|S_IRUGO|S_IWUSR, &bnad_debugfs_op_regrd, },
- { "regwr", S_IFREG|S_IWUSR, &bnad_debugfs_op_regwr, },
- { "drvinfo", S_IFREG|S_IRUGO, &bnad_debugfs_op_drvinfo, },
+ { "fwtrc", S_IFREG | 0444, &bnad_debugfs_op_fwtrc, },
+ { "fwsave", S_IFREG | 0444, &bnad_debugfs_op_fwsave, },
+ { "regrd", S_IFREG | 0644, &bnad_debugfs_op_regrd, },
+ { "regwr", S_IFREG | 0200, &bnad_debugfs_op_regwr, },
+ { "drvinfo", S_IFREG | 0444, &bnad_debugfs_op_drvinfo, },
};
static struct dentry *bna_debugfs_root;
diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c
index e84afcf1ecb5..b4c9268100bb 100644
--- a/drivers/net/ethernet/cadence/macb_main.c
+++ b/drivers/net/ethernet/cadence/macb_main.c
@@ -472,8 +472,44 @@ static int macb_mii_probe(struct net_device *dev)
struct macb *bp = netdev_priv(dev);
struct macb_platform_data *pdata;
struct phy_device *phydev;
- int phy_irq;
- int ret;
+ struct device_node *np;
+ int phy_irq, ret, i;
+
+ pdata = dev_get_platdata(&bp->pdev->dev);
+ np = bp->pdev->dev.of_node;
+ ret = 0;
+
+ if (np) {
+ if (of_phy_is_fixed_link(np)) {
+ if (of_phy_register_fixed_link(np) < 0) {
+ dev_err(&bp->pdev->dev,
+ "broken fixed-link specification\n");
+ return -ENODEV;
+ }
+ bp->phy_node = of_node_get(np);
+ } else {
+ bp->phy_node = of_parse_phandle(np, "phy-handle", 0);
+ /* fallback to standard phy registration if no
+ * phy-handle was found nor any phy found during
+ * dt phy registration
+ */
+ if (!bp->phy_node && !phy_find_first(bp->mii_bus)) {
+ for (i = 0; i < PHY_MAX_ADDR; i++) {
+ struct phy_device *phydev;
+
+ phydev = mdiobus_scan(bp->mii_bus, i);
+ if (IS_ERR(phydev) &&
+ PTR_ERR(phydev) != -ENODEV) {
+ ret = PTR_ERR(phydev);
+ break;
+ }
+ }
+
+ if (ret)
+ return -ENODEV;
+ }
+ }
+ }
if (bp->phy_node) {
phydev = of_phy_connect(dev, bp->phy_node,
@@ -488,7 +524,6 @@ static int macb_mii_probe(struct net_device *dev)
return -ENXIO;
}
- pdata = dev_get_platdata(&bp->pdev->dev);
if (pdata) {
if (gpio_is_valid(pdata->phy_irq_pin)) {
ret = devm_gpio_request(&bp->pdev->dev,
@@ -533,7 +568,7 @@ static int macb_mii_init(struct macb *bp)
{
struct macb_platform_data *pdata;
struct device_node *np;
- int err = -ENXIO, i;
+ int err;
/* Enable management port */
macb_writel(bp, NCR, MACB_BIT(MPE));
@@ -556,43 +591,10 @@ static int macb_mii_init(struct macb *bp)
dev_set_drvdata(&bp->dev->dev, bp->mii_bus);
np = bp->pdev->dev.of_node;
- if (np) {
- if (of_phy_is_fixed_link(np)) {
- if (of_phy_register_fixed_link(np) < 0) {
- dev_err(&bp->pdev->dev,
- "broken fixed-link specification\n");
- goto err_out_unregister_bus;
- }
- bp->phy_node = of_node_get(np);
-
- err = mdiobus_register(bp->mii_bus);
- } else {
- /* try dt phy registration */
- err = of_mdiobus_register(bp->mii_bus, np);
-
- /* fallback to standard phy registration if no phy were
- * found during dt phy registration
- */
- if (!err && !phy_find_first(bp->mii_bus)) {
- for (i = 0; i < PHY_MAX_ADDR; i++) {
- struct phy_device *phydev;
- phydev = mdiobus_scan(bp->mii_bus, i);
- if (IS_ERR(phydev) &&
- PTR_ERR(phydev) != -ENODEV) {
- err = PTR_ERR(phydev);
- break;
- }
- }
-
- if (err)
- goto err_out_unregister_bus;
- }
- }
+ if (np) {
+ err = of_mdiobus_register(bp->mii_bus, np);
} else {
- for (i = 0; i < PHY_MAX_ADDR; i++)
- bp->mii_bus->irq[i] = PHY_POLL;
-
if (pdata)
bp->mii_bus->phy_mask = pdata->phy_mask;
@@ -610,10 +612,10 @@ static int macb_mii_init(struct macb *bp)
err_out_unregister_bus:
mdiobus_unregister(bp->mii_bus);
-err_out_free_mdiobus:
- of_node_put(bp->phy_node);
if (np && of_phy_is_fixed_link(np))
of_phy_deregister_fixed_link(np);
+err_out_free_mdiobus:
+ of_node_put(bp->phy_node);
mdiobus_free(bp->mii_bus);
err_out:
return err;
@@ -3950,10 +3952,16 @@ static int macb_probe(struct platform_device *pdev)
dev->max_mtu = ETH_DATA_LEN;
mac = of_get_mac_address(np);
- if (mac)
+ if (mac) {
ether_addr_copy(bp->dev->dev_addr, mac);
- else
- macb_get_hwaddr(bp);
+ } else {
+ err = of_get_nvmem_mac_address(np, bp->dev->dev_addr);
+ if (err) {
+ if (err == -EPROBE_DEFER)
+ goto err_out_free_netdev;
+ macb_get_hwaddr(bp);
+ }
+ }
err = of_get_phy_mode(np);
if (err < 0) {
diff --git a/drivers/net/ethernet/cadence/macb_ptp.c b/drivers/net/ethernet/cadence/macb_ptp.c
index 2220c771092b..2220c771092b 100755..100644
--- a/drivers/net/ethernet/cadence/macb_ptp.c
+++ b/drivers/net/ethernet/cadence/macb_ptp.c
diff --git a/drivers/net/ethernet/cavium/common/cavium_ptp.c b/drivers/net/ethernet/cavium/common/cavium_ptp.c
index d59497a7bdce..6aeb1045c302 100644
--- a/drivers/net/ethernet/cavium/common/cavium_ptp.c
+++ b/drivers/net/ethernet/cavium/common/cavium_ptp.c
@@ -336,18 +336,7 @@ static struct pci_driver cavium_ptp_driver = {
.remove = cavium_ptp_remove,
};
-static int __init cavium_ptp_init_module(void)
-{
- return pci_register_driver(&cavium_ptp_driver);
-}
-
-static void __exit cavium_ptp_cleanup_module(void)
-{
- pci_unregister_driver(&cavium_ptp_driver);
-}
-
-module_init(cavium_ptp_init_module);
-module_exit(cavium_ptp_cleanup_module);
+module_pci_driver(cavium_ptp_driver);
MODULE_DESCRIPTION(DRV_NAME);
MODULE_AUTHOR("Cavium Networks <support@cavium.com>");
diff --git a/drivers/net/ethernet/cavium/liquidio/Makefile b/drivers/net/ethernet/cavium/liquidio/Makefile
index e3fc4645cd8a..bc9937502043 100644
--- a/drivers/net/ethernet/cavium/liquidio/Makefile
+++ b/drivers/net/ethernet/cavium/liquidio/Makefile
@@ -2,42 +2,23 @@
#
# Cavium Liquidio ethernet device driver
#
-obj-$(CONFIG_LIQUIDIO) += liquidio.o
-liquidio-$(CONFIG_LIQUIDIO) += lio_ethtool.o \
- lio_core.o \
- request_manager.o \
- response_manager.o \
- octeon_device.o \
- cn66xx_device.o \
- cn68xx_device.o \
- cn23xx_pf_device.o \
- cn23xx_vf_device.o \
- octeon_mailbox.o \
- octeon_mem_ops.o \
- octeon_droq.o \
- octeon_nic.o
+common-objs := lio_ethtool.o \
+ lio_core.o \
+ request_manager.o \
+ response_manager.o \
+ octeon_device.o \
+ cn66xx_device.o \
+ cn68xx_device.o \
+ cn23xx_pf_device.o \
+ cn23xx_vf_device.o \
+ octeon_mailbox.o \
+ octeon_mem_ops.o \
+ octeon_droq.o \
+ octeon_nic.o
-liquidio-objs := lio_main.o octeon_console.o lio_vf_rep.o $(liquidio-y)
+obj-$(CONFIG_LIQUIDIO) += liquidio.o
+liquidio-y := lio_main.o octeon_console.o lio_vf_rep.o $(common-objs)
obj-$(CONFIG_LIQUIDIO_VF) += liquidio_vf.o
-
-ifeq ($(CONFIG_LIQUIDIO)$(CONFIG_LIQUIDIO_VF), yy)
- liquidio_vf-objs := lio_vf_main.o
-else
-liquidio_vf-$(CONFIG_LIQUIDIO_VF) += lio_ethtool.o \
- lio_core.o \
- request_manager.o \
- response_manager.o \
- octeon_device.o \
- cn66xx_device.o \
- cn68xx_device.o \
- cn23xx_pf_device.o \
- cn23xx_vf_device.o \
- octeon_mailbox.o \
- octeon_mem_ops.o \
- octeon_droq.o \
- octeon_nic.o
-
-liquidio_vf-objs := lio_vf_main.o $(liquidio_vf-y)
-endif
+liquidio_vf-y := lio_vf_main.o $(common-objs)
diff --git a/drivers/net/ethernet/cavium/liquidio/lio_core.c b/drivers/net/ethernet/cavium/liquidio/lio_core.c
index 32ae63b6f20e..2a94eee943b2 100644
--- a/drivers/net/ethernet/cavium/liquidio/lio_core.c
+++ b/drivers/net/ethernet/cavium/liquidio/lio_core.c
@@ -164,15 +164,6 @@ void liquidio_link_ctrl_cmd_completion(void *nctrl_ptr)
}
break;
- case OCTNET_CMD_CHANGE_MTU:
- /* If command is successful, change the MTU. */
- netif_info(lio, probe, lio->netdev, "MTU Changed from %d to %d\n",
- netdev->mtu, nctrl->ncmd.s.param1);
- netdev->mtu = nctrl->ncmd.s.param1;
- queue_delayed_work(lio->link_status_wq.wq,
- &lio->link_status_wq.wk.work, 0);
- break;
-
case OCTNET_CMD_GPIO_ACCESS:
netif_info(lio, probe, lio->netdev, "LED Flashing visual identification\n");
@@ -386,20 +377,12 @@ static void lio_update_txq_status(struct octeon_device *oct, int iq_num)
return;
lio = GET_LIO(netdev);
- if (netif_is_multiqueue(netdev)) {
- if (__netif_subqueue_stopped(netdev, iq->q_index) &&
- lio->linfo.link.s.link_up &&
- (!octnet_iq_is_full(oct, iq_num))) {
- netif_wake_subqueue(netdev, iq->q_index);
- INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, iq_num,
- tx_restart, 1);
- }
- } else if (netif_queue_stopped(netdev) &&
- lio->linfo.link.s.link_up &&
- (!octnet_iq_is_full(oct, lio->txq))) {
- INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, lio->txq,
+ if (__netif_subqueue_stopped(netdev, iq->q_index) &&
+ lio->linfo.link.s.link_up &&
+ (!octnet_iq_is_full(oct, iq_num))) {
+ netif_wake_subqueue(netdev, iq->q_index);
+ INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, iq_num,
tx_restart, 1);
- netif_wake_queue(netdev);
}
}
@@ -571,7 +554,8 @@ liquidio_push_packet(u32 octeon_id __attribute__((unused)),
napi_gro_receive(napi, skb);
- droq->stats.rx_bytes_received += len;
+ droq->stats.rx_bytes_received += len -
+ rh->r_dh.len * BYTES_PER_DHLEN_UNIT;
droq->stats.rx_pkts_received++;
} else {
recv_buffer_free(skb);
@@ -635,9 +619,7 @@ static int liquidio_napi_poll(struct napi_struct *napi, int budget)
iq_no = droq->q_no;
/* Handle Droq descriptors */
- work_done = octeon_process_droq_poll_cmd(oct, droq->q_no,
- POLL_EVENT_PROCESS_PKTS,
- budget);
+ work_done = octeon_droq_process_poll_pkts(oct, droq, budget);
/* Flush the instruction queue */
iq = oct->instr_queue[iq_no];
@@ -668,8 +650,7 @@ static int liquidio_napi_poll(struct napi_struct *napi, int budget)
tx_done = 1;
napi_complete_done(napi, work_done);
- octeon_process_droq_poll_cmd(droq->oct_dev, droq->q_no,
- POLL_EVENT_ENABLE_INTR, 0);
+ octeon_enable_irq(droq->oct_dev, droq->q_no);
return 0;
}
@@ -1080,3 +1061,111 @@ int octeon_setup_interrupt(struct octeon_device *oct, u32 num_ioqs)
}
return 0;
}
+
+static void liquidio_change_mtu_completion(struct octeon_device *oct,
+ u32 status, void *buf)
+{
+ struct octeon_soft_command *sc = (struct octeon_soft_command *)buf;
+ struct liquidio_if_cfg_context *ctx;
+
+ ctx = (struct liquidio_if_cfg_context *)sc->ctxptr;
+
+ if (status) {
+ dev_err(&oct->pci_dev->dev, "MTU change failed. Status: %llx\n",
+ CVM_CAST64(status));
+ WRITE_ONCE(ctx->cond, LIO_CHANGE_MTU_FAIL);
+ } else {
+ WRITE_ONCE(ctx->cond, LIO_CHANGE_MTU_SUCCESS);
+ }
+
+ /* This barrier is required to be sure that the response has been
+ * written fully before waking up the handler
+ */
+ wmb();
+
+ wake_up_interruptible(&ctx->wc);
+}
+
+/**
+ * \brief Net device change_mtu
+ * @param netdev network device
+ */
+int liquidio_change_mtu(struct net_device *netdev, int new_mtu)
+{
+ struct lio *lio = GET_LIO(netdev);
+ struct octeon_device *oct = lio->oct_dev;
+ struct liquidio_if_cfg_context *ctx;
+ struct octeon_soft_command *sc;
+ union octnet_cmd *ncmd;
+ int ctx_size;
+ int ret = 0;
+
+ ctx_size = sizeof(struct liquidio_if_cfg_context);
+ sc = (struct octeon_soft_command *)
+ octeon_alloc_soft_command(oct, OCTNET_CMD_SIZE, 16, ctx_size);
+
+ ncmd = (union octnet_cmd *)sc->virtdptr;
+ ctx = (struct liquidio_if_cfg_context *)sc->ctxptr;
+
+ WRITE_ONCE(ctx->cond, 0);
+ ctx->octeon_id = lio_get_device_id(oct);
+ init_waitqueue_head(&ctx->wc);
+
+ ncmd->u64 = 0;
+ ncmd->s.cmd = OCTNET_CMD_CHANGE_MTU;
+ ncmd->s.param1 = new_mtu;
+
+ octeon_swap_8B_data((u64 *)ncmd, (OCTNET_CMD_SIZE >> 3));
+
+ sc->iq_no = lio->linfo.txpciq[0].s.q_no;
+
+ octeon_prepare_soft_command(oct, sc, OPCODE_NIC,
+ OPCODE_NIC_CMD, 0, 0, 0);
+
+ sc->callback = liquidio_change_mtu_completion;
+ sc->callback_arg = sc;
+ sc->wait_time = 100;
+
+ ret = octeon_send_soft_command(oct, sc);
+ if (ret == IQ_SEND_FAILED) {
+ netif_info(lio, rx_err, lio->netdev, "Failed to change MTU\n");
+ return -EINVAL;
+ }
+ /* Sleep on a wait queue till the cond flag indicates that the
+ * response arrived or timed-out.
+ */
+ if (sleep_cond(&ctx->wc, &ctx->cond) == -EINTR ||
+ ctx->cond == LIO_CHANGE_MTU_FAIL) {
+ octeon_free_soft_command(oct, sc);
+ return -EINVAL;
+ }
+
+ netdev->mtu = new_mtu;
+ lio->mtu = new_mtu;
+
+ octeon_free_soft_command(oct, sc);
+ return 0;
+}
+
+int lio_wait_for_clean_oq(struct octeon_device *oct)
+{
+ int retry = 100, pending_pkts = 0;
+ int idx;
+
+ do {
+ pending_pkts = 0;
+
+ for (idx = 0; idx < MAX_OCTEON_OUTPUT_QUEUES(oct); idx++) {
+ if (!(oct->io_qmask.oq & BIT_ULL(idx)))
+ continue;
+ pending_pkts +=
+ atomic_read(&oct->droq[idx]->pkts_pending);
+ }
+
+ if (pending_pkts > 0)
+ schedule_timeout_uninterruptible(1);
+
+ } while (retry-- && pending_pkts);
+
+ return pending_pkts;
+}
diff --git a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c
index a63ddf07f168..550ac29682a5 100644
--- a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c
+++ b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c
@@ -232,10 +232,16 @@ static int lio_get_link_ksettings(struct net_device *netdev,
linfo = &lio->linfo;
- if (linfo->link.s.if_mode == INTERFACE_MODE_XAUI ||
- linfo->link.s.if_mode == INTERFACE_MODE_RXAUI ||
- linfo->link.s.if_mode == INTERFACE_MODE_XLAUI ||
- linfo->link.s.if_mode == INTERFACE_MODE_XFI) {
+ switch (linfo->link.s.phy_type) {
+ case LIO_PHY_PORT_TP:
+ ecmd->base.port = PORT_TP;
+ supported = (SUPPORTED_10000baseT_Full |
+ SUPPORTED_TP | SUPPORTED_Pause);
+ advertising = (ADVERTISED_10000baseT_Full | ADVERTISED_Pause);
+ ecmd->base.autoneg = AUTONEG_DISABLE;
+ break;
+
+ case LIO_PHY_PORT_FIBRE:
ecmd->base.port = PORT_FIBRE;
if (linfo->link.s.speed == SPEED_10000) {
@@ -245,12 +251,18 @@ static int lio_get_link_ksettings(struct net_device *netdev,
supported |= SUPPORTED_FIBRE | SUPPORTED_Pause;
advertising |= ADVERTISED_Pause;
+ ecmd->base.autoneg = AUTONEG_DISABLE;
+ break;
+ }
+
+ if (linfo->link.s.if_mode == INTERFACE_MODE_XAUI ||
+ linfo->link.s.if_mode == INTERFACE_MODE_RXAUI ||
+ linfo->link.s.if_mode == INTERFACE_MODE_XLAUI ||
+ linfo->link.s.if_mode == INTERFACE_MODE_XFI) {
ethtool_convert_legacy_u32_to_link_mode(
ecmd->link_modes.supported, supported);
ethtool_convert_legacy_u32_to_link_mode(
ecmd->link_modes.advertising, advertising);
- ecmd->base.autoneg = AUTONEG_DISABLE;
-
} else {
dev_err(&oct->pci_dev->dev, "Unknown link interface reported %d\n",
linfo->link.s.if_mode);
diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c
index a5eecd895a82..603a144d3d9c 100644
--- a/drivers/net/ethernet/cavium/liquidio/lio_main.c
+++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c
@@ -91,18 +91,9 @@ static int octeon_console_debug_enabled(u32 console)
*/
#define LIO_SYNC_OCTEON_TIME_INTERVAL_MS 60000
-struct liquidio_if_cfg_context {
- int octeon_id;
-
- wait_queue_head_t wc;
-
- int cond;
-};
-
-struct liquidio_if_cfg_resp {
- u64 rh;
- struct liquidio_if_cfg_info cfg_info;
- u64 status;
+struct lio_trusted_vf_ctx {
+ struct completion complete;
+ int status;
};
struct liquidio_rx_ctl_context {
@@ -523,148 +514,30 @@ static void liquidio_deinit_pci(void)
}
/**
- * \brief Stop Tx queues
- * @param netdev network device
- */
-static inline void txqs_stop(struct net_device *netdev)
-{
- if (netif_is_multiqueue(netdev)) {
- int i;
-
- for (i = 0; i < netdev->num_tx_queues; i++)
- netif_stop_subqueue(netdev, i);
- } else {
- netif_stop_queue(netdev);
- }
-}
-
-/**
- * \brief Start Tx queues
- * @param netdev network device
- */
-static inline void txqs_start(struct net_device *netdev)
-{
- if (netif_is_multiqueue(netdev)) {
- int i;
-
- for (i = 0; i < netdev->num_tx_queues; i++)
- netif_start_subqueue(netdev, i);
- } else {
- netif_start_queue(netdev);
- }
-}
-
-/**
- * \brief Wake Tx queues
- * @param netdev network device
- */
-static inline void txqs_wake(struct net_device *netdev)
-{
- struct lio *lio = GET_LIO(netdev);
-
- if (netif_is_multiqueue(netdev)) {
- int i;
-
- for (i = 0; i < netdev->num_tx_queues; i++) {
- int qno = lio->linfo.txpciq[i %
- lio->oct_dev->num_iqs].s.q_no;
-
- if (__netif_subqueue_stopped(netdev, i)) {
- INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, qno,
- tx_restart, 1);
- netif_wake_subqueue(netdev, i);
- }
- }
- } else {
- INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, lio->txq,
- tx_restart, 1);
- netif_wake_queue(netdev);
- }
-}
-
-/**
- * \brief Stop Tx queue
- * @param netdev network device
- */
-static void stop_txq(struct net_device *netdev)
-{
- txqs_stop(netdev);
-}
-
-/**
- * \brief Start Tx queue
- * @param netdev network device
- */
-static void start_txq(struct net_device *netdev)
-{
- struct lio *lio = GET_LIO(netdev);
-
- if (lio->linfo.link.s.link_up) {
- txqs_start(netdev);
- return;
- }
-}
-
-/**
- * \brief Wake a queue
- * @param netdev network device
- * @param q which queue to wake
- */
-static inline void wake_q(struct net_device *netdev, int q)
-{
- if (netif_is_multiqueue(netdev))
- netif_wake_subqueue(netdev, q);
- else
- netif_wake_queue(netdev);
-}
-
-/**
- * \brief Stop a queue
- * @param netdev network device
- * @param q which queue to stop
- */
-static inline void stop_q(struct net_device *netdev, int q)
-{
- if (netif_is_multiqueue(netdev))
- netif_stop_subqueue(netdev, q);
- else
- netif_stop_queue(netdev);
-}
-
-/**
* \brief Check Tx queue status, and take appropriate action
* @param lio per-network private data
* @returns 0 if full, number of queues woken up otherwise
*/
static inline int check_txq_status(struct lio *lio)
{
+ int numqs = lio->netdev->num_tx_queues;
int ret_val = 0;
+ int q, iq;
- if (netif_is_multiqueue(lio->netdev)) {
- int numqs = lio->netdev->num_tx_queues;
- int q, iq = 0;
-
- /* check each sub-queue state */
- for (q = 0; q < numqs; q++) {
- iq = lio->linfo.txpciq[q %
- lio->oct_dev->num_iqs].s.q_no;
- if (octnet_iq_is_full(lio->oct_dev, iq))
- continue;
- if (__netif_subqueue_stopped(lio->netdev, q)) {
- wake_q(lio->netdev, q);
- INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, iq,
- tx_restart, 1);
- ret_val++;
- }
+ /* check each sub-queue state */
+ for (q = 0; q < numqs; q++) {
+ iq = lio->linfo.txpciq[q %
+ lio->oct_dev->num_iqs].s.q_no;
+ if (octnet_iq_is_full(lio->oct_dev, iq))
+ continue;
+ if (__netif_subqueue_stopped(lio->netdev, q)) {
+ netif_wake_subqueue(lio->netdev, q);
+ INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, iq,
+ tx_restart, 1);
+ ret_val++;
}
- } else {
- if (octnet_iq_is_full(lio->oct_dev, lio->txq))
- return 0;
- wake_q(lio->netdev, lio->txq);
- INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, lio->txq,
- tx_restart, 1);
- ret_val = 1;
}
+
return ret_val;
}
@@ -841,8 +714,12 @@ static void octnet_link_status_change(struct work_struct *work)
struct cavium_wk *wk = (struct cavium_wk *)work;
struct lio *lio = (struct lio *)wk->ctxptr;
+ /* lio->linfo.link.s.mtu always contains max MTU of the lio interface.
+ * this API is invoked only when new max-MTU of the interface is
+ * less than current MTU.
+ */
rtnl_lock();
- call_netdevice_notifiers(NETDEV_CHANGEMTU, lio->netdev);
+ dev_set_mtu(lio->netdev, lio->linfo.link.s.mtu);
rtnl_unlock();
}
@@ -891,7 +768,11 @@ static inline void update_link_status(struct net_device *netdev,
{
struct lio *lio = GET_LIO(netdev);
int changed = (lio->linfo.link.u64 != ls->u64);
+ int current_max_mtu = lio->linfo.link.s.mtu;
+ struct octeon_device *oct = lio->oct_dev;
+ dev_dbg(&oct->pci_dev->dev, "%s: lio->linfo.link.u64=%llx, ls->u64=%llx\n",
+ __func__, lio->linfo.link.u64, ls->u64);
lio->linfo.link.u64 = ls->u64;
if ((lio->intf_open) && (changed)) {
@@ -899,11 +780,25 @@ static inline void update_link_status(struct net_device *netdev,
lio->link_changes++;
if (lio->linfo.link.s.link_up) {
+ dev_dbg(&oct->pci_dev->dev, "%s: link_up", __func__);
netif_carrier_on(netdev);
- txqs_wake(netdev);
+ wake_txqs(netdev);
} else {
+ dev_dbg(&oct->pci_dev->dev, "%s: link_off", __func__);
netif_carrier_off(netdev);
- stop_txq(netdev);
+ stop_txqs(netdev);
+ }
+ if (lio->linfo.link.s.mtu != current_max_mtu) {
+ netif_info(lio, probe, lio->netdev, "Max MTU changed from %d to %d\n",
+ current_max_mtu, lio->linfo.link.s.mtu);
+ netdev->max_mtu = lio->linfo.link.s.mtu;
+ }
+ if (lio->linfo.link.s.mtu < netdev->mtu) {
+ dev_warn(&oct->pci_dev->dev,
+ "Current MTU is higher than new max MTU; Reducing the current mtu from %d to %d\n",
+ netdev->mtu, lio->linfo.link.s.mtu);
+ queue_delayed_work(lio->link_status_wq.wq,
+ &lio->link_status_wq.wk.work, 0);
}
}
}
@@ -1739,43 +1634,6 @@ static int octeon_pci_os_setup(struct octeon_device *oct)
return 0;
}
-static inline int skb_iq(struct lio *lio, struct sk_buff *skb)
-{
- int q = 0;
-
- if (netif_is_multiqueue(lio->netdev))
- q = skb->queue_mapping % lio->linfo.num_txpciq;
-
- return q;
-}
-
-/**
- * \brief Check Tx queue state for a given network buffer
- * @param lio per-network private data
- * @param skb network buffer
- */
-static inline int check_txq_state(struct lio *lio, struct sk_buff *skb)
-{
- int q = 0, iq = 0;
-
- if (netif_is_multiqueue(lio->netdev)) {
- q = skb->queue_mapping;
- iq = lio->linfo.txpciq[(q % lio->oct_dev->num_iqs)].s.q_no;
- } else {
- iq = lio->txq;
- q = iq;
- }
-
- if (octnet_iq_is_full(lio->oct_dev, iq))
- return 0;
-
- if (__netif_subqueue_stopped(lio->netdev, q)) {
- INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, iq, tx_restart, 1);
- wake_q(lio->netdev, q);
- }
- return 1;
-}
-
/**
* \brief Unmap and free network buffer
* @param buf buffer
@@ -1793,8 +1651,6 @@ static void free_netbuf(void *buf)
dma_unmap_single(&lio->oct_dev->pci_dev->dev, finfo->dptr, skb->len,
DMA_TO_DEVICE);
- check_txq_state(lio, skb);
-
tx_buffer_free(skb);
}
@@ -1835,8 +1691,6 @@ static void free_netsgbuf(void *buf)
list_add_tail(&g->list, &lio->glist[iq]);
spin_unlock(&lio->glist_lock[iq]);
- check_txq_state(lio, skb); /* mq support: sub-queue state check */
-
tx_buffer_free(skb);
}
@@ -1882,8 +1736,6 @@ static void free_netsgbuf_with_resp(void *buf)
spin_unlock(&lio->glist_lock[iq]);
/* Don't free the skb yet */
-
- check_txq_state(lio, skb);
}
/**
@@ -2211,7 +2063,7 @@ static int liquidio_open(struct net_device *netdev)
return -1;
}
- start_txq(netdev);
+ start_txqs(netdev);
/* tell Octeon to start forwarding packets to host */
send_rx_ctrl_cmd(lio, 1);
@@ -2232,16 +2084,6 @@ static int liquidio_stop(struct net_device *netdev)
struct octeon_device *oct = lio->oct_dev;
struct napi_struct *napi, *n;
- if (oct->props[lio->ifidx].napi_enabled) {
- list_for_each_entry_safe(napi, n, &netdev->napi_list, dev_list)
- napi_disable(napi);
-
- oct->props[lio->ifidx].napi_enabled = 0;
-
- if (OCTEON_CN23XX_PF(oct))
- oct->droq[0]->ops.poll_mode = 0;
- }
-
ifstate_reset(lio, LIO_IFSTATE_RUNNING);
netif_tx_disable(netdev);
@@ -2267,6 +2109,21 @@ static int liquidio_stop(struct net_device *netdev)
lio->ptp_clock = NULL;
}
+ /* Wait for any pending Rx descriptors */
+ if (lio_wait_for_clean_oq(oct))
+ netif_info(lio, rx_err, lio->netdev,
+ "Proceeding with stop interface after partial RX desc processing\n");
+
+ if (oct->props[lio->ifidx].napi_enabled == 1) {
+ list_for_each_entry_safe(napi, n, &netdev->napi_list, dev_list)
+ napi_disable(napi);
+
+ oct->props[lio->ifidx].napi_enabled = 0;
+
+ if (OCTEON_CN23XX_PF(oct))
+ oct->droq[0]->ops.poll_mode = 0;
+ }
+
dev_info(&oct->pci_dev->dev, "%s interface is stopped\n", netdev->name);
return 0;
@@ -2449,38 +2306,6 @@ static struct net_device_stats *liquidio_get_stats(struct net_device *netdev)
}
/**
- * \brief Net device change_mtu
- * @param netdev network device
- */
-static int liquidio_change_mtu(struct net_device *netdev, int new_mtu)
-{
- struct lio *lio = GET_LIO(netdev);
- struct octeon_device *oct = lio->oct_dev;
- struct octnic_ctrl_pkt nctrl;
- int ret = 0;
-
- memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt));
-
- nctrl.ncmd.u64 = 0;
- nctrl.ncmd.s.cmd = OCTNET_CMD_CHANGE_MTU;
- nctrl.ncmd.s.param1 = new_mtu;
- nctrl.iq_no = lio->linfo.txpciq[0].s.q_no;
- nctrl.wait_time = 100;
- nctrl.netpndev = (u64)netdev;
- nctrl.cb_fn = liquidio_link_ctrl_cmd_completion;
-
- ret = octnet_send_nic_ctrl_pkt(lio->oct_dev, &nctrl);
- if (ret < 0) {
- dev_err(&oct->pci_dev->dev, "Failed to set MTU\n");
- return -1;
- }
-
- lio->mtu = new_mtu;
-
- return 0;
-}
-
-/**
* \brief Handler for SIOCSHWTSTAMP ioctl
* @param netdev network device
* @param ifr interface request
@@ -2685,14 +2510,9 @@ static int liquidio_xmit(struct sk_buff *skb, struct net_device *netdev)
lio = GET_LIO(netdev);
oct = lio->oct_dev;
- if (netif_is_multiqueue(netdev)) {
- q_idx = skb->queue_mapping;
- q_idx = (q_idx % (lio->linfo.num_txpciq));
- tag = q_idx;
- iq_no = lio->linfo.txpciq[q_idx].s.q_no;
- } else {
- iq_no = lio->txq;
- }
+ q_idx = skb_iq(lio, skb);
+ tag = q_idx;
+ iq_no = lio->linfo.txpciq[q_idx].s.q_no;
stats = &oct->instr_queue[iq_no]->stats;
@@ -2723,23 +2543,14 @@ static int liquidio_xmit(struct sk_buff *skb, struct net_device *netdev)
ndata.q_no = iq_no;
- if (netif_is_multiqueue(netdev)) {
- if (octnet_iq_is_full(oct, ndata.q_no)) {
- /* defer sending if queue is full */
- netif_info(lio, tx_err, lio->netdev, "Transmit failed iq:%d full\n",
- ndata.q_no);
- stats->tx_iq_busy++;
- return NETDEV_TX_BUSY;
- }
- } else {
- if (octnet_iq_is_full(oct, lio->txq)) {
- /* defer sending if queue is full */
- stats->tx_iq_busy++;
- netif_info(lio, tx_err, lio->netdev, "Transmit failed iq:%d full\n",
- lio->txq);
- return NETDEV_TX_BUSY;
- }
+ if (octnet_iq_is_full(oct, ndata.q_no)) {
+ /* defer sending if queue is full */
+ netif_info(lio, tx_err, lio->netdev, "Transmit failed iq:%d full\n",
+ ndata.q_no);
+ stats->tx_iq_busy++;
+ return NETDEV_TX_BUSY;
}
+
/* pr_info(" XMIT - valid Qs: %d, 1st Q no: %d, cpu: %d, q_no:%d\n",
* lio->linfo.num_txpciq, lio->txq, cpu, ndata.q_no);
*/
@@ -2895,7 +2706,7 @@ static int liquidio_xmit(struct sk_buff *skb, struct net_device *netdev)
netif_info(lio, tx_queued, lio->netdev, "Transmit queued successfully\n");
if (status == IQ_SEND_STOP)
- stop_q(netdev, q_idx);
+ netif_stop_subqueue(netdev, q_idx);
netif_trans_update(netdev);
@@ -2934,7 +2745,7 @@ static void liquidio_tx_timeout(struct net_device *netdev)
"Transmit timeout tx_dropped:%ld, waking up queues now!!\n",
netdev->stats.tx_dropped);
netif_trans_update(netdev);
- txqs_wake(netdev);
+ wake_txqs(netdev);
}
static int liquidio_vlan_rx_add_vid(struct net_device *netdev,
@@ -3289,10 +3100,120 @@ static int liquidio_get_vf_config(struct net_device *netdev, int vfidx,
ether_addr_copy(&ivi->mac[0], macaddr);
ivi->vlan = oct->sriov_info.vf_vlantci[vfidx] & VLAN_VID_MASK;
ivi->qos = oct->sriov_info.vf_vlantci[vfidx] >> VLAN_PRIO_SHIFT;
+ if (oct->sriov_info.trusted_vf.active &&
+ oct->sriov_info.trusted_vf.id == vfidx)
+ ivi->trusted = true;
+ else
+ ivi->trusted = false;
ivi->linkstate = oct->sriov_info.vf_linkstate[vfidx];
return 0;
}
+static void trusted_vf_callback(struct octeon_device *oct_dev,
+ u32 status, void *ptr)
+{
+ struct octeon_soft_command *sc = (struct octeon_soft_command *)ptr;
+ struct lio_trusted_vf_ctx *ctx;
+
+ ctx = (struct lio_trusted_vf_ctx *)sc->ctxptr;
+ ctx->status = status;
+
+ complete(&ctx->complete);
+}
+
+static int liquidio_send_vf_trust_cmd(struct lio *lio, int vfidx, bool trusted)
+{
+ struct octeon_device *oct = lio->oct_dev;
+ struct lio_trusted_vf_ctx *ctx;
+ struct octeon_soft_command *sc;
+ int ctx_size, retval;
+
+ ctx_size = sizeof(struct lio_trusted_vf_ctx);
+ sc = octeon_alloc_soft_command(oct, 0, 0, ctx_size);
+
+ ctx = (struct lio_trusted_vf_ctx *)sc->ctxptr;
+ init_completion(&ctx->complete);
+
+ sc->iq_no = lio->linfo.txpciq[0].s.q_no;
+
+ /* vfidx is 0 based, but vf_num (param1) is 1 based */
+ octeon_prepare_soft_command(oct, sc, OPCODE_NIC,
+ OPCODE_NIC_SET_TRUSTED_VF, 0, vfidx + 1,
+ trusted);
+
+ sc->callback = trusted_vf_callback;
+ sc->callback_arg = sc;
+ sc->wait_time = 1000;
+
+ retval = octeon_send_soft_command(oct, sc);
+ if (retval == IQ_SEND_FAILED) {
+ retval = -1;
+ } else {
+ /* Wait for response or timeout */
+ if (wait_for_completion_timeout(&ctx->complete,
+ msecs_to_jiffies(2000)))
+ retval = ctx->status;
+ else
+ retval = -1;
+ }
+
+ octeon_free_soft_command(oct, sc);
+
+ return retval;
+}
+
+static int liquidio_set_vf_trust(struct net_device *netdev, int vfidx,
+ bool setting)
+{
+ struct lio *lio = GET_LIO(netdev);
+ struct octeon_device *oct = lio->oct_dev;
+
+ if (strcmp(oct->fw_info.liquidio_firmware_version, "1.7.1") < 0) {
+ /* trusted vf is not supported by firmware older than 1.7.1 */
+ return -EOPNOTSUPP;
+ }
+
+ if (vfidx < 0 || vfidx >= oct->sriov_info.num_vfs_alloced) {
+ netif_info(lio, drv, lio->netdev, "Invalid vfidx %d\n", vfidx);
+ return -EINVAL;
+ }
+
+ if (setting) {
+ /* Set */
+
+ if (oct->sriov_info.trusted_vf.active &&
+ oct->sriov_info.trusted_vf.id == vfidx)
+ return 0;
+
+ if (oct->sriov_info.trusted_vf.active) {
+ netif_info(lio, drv, lio->netdev, "More than one trusted VF is not allowed\n");
+ return -EPERM;
+ }
+ } else {
+ /* Clear */
+
+ if (!oct->sriov_info.trusted_vf.active)
+ return 0;
+ }
+
+ if (!liquidio_send_vf_trust_cmd(lio, vfidx, setting)) {
+ if (setting) {
+ oct->sriov_info.trusted_vf.id = vfidx;
+ oct->sriov_info.trusted_vf.active = true;
+ } else {
+ oct->sriov_info.trusted_vf.active = false;
+ }
+
+ netif_info(lio, drv, lio->netdev, "VF %u is %strusted\n", vfidx,
+ setting ? "" : "not ");
+ } else {
+ netif_info(lio, drv, lio->netdev, "Failed to set VF trusted\n");
+ return -1;
+ }
+
+ return 0;
+}
+
static int liquidio_set_vf_link_state(struct net_device *netdev, int vfidx,
int linkstate)
{
@@ -3423,6 +3344,7 @@ static const struct net_device_ops lionetdevops = {
.ndo_set_vf_mac = liquidio_set_vf_mac,
.ndo_set_vf_vlan = liquidio_set_vf_vlan,
.ndo_get_vf_config = liquidio_get_vf_config,
+ .ndo_set_vf_trust = liquidio_set_vf_trust,
.ndo_set_vf_link_state = liquidio_set_vf_link_state,
};
diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c
index fd70a4844e2d..f92dfa411de6 100644
--- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c
+++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c
@@ -40,20 +40,6 @@ MODULE_PARM_DESC(debug, "NETIF_MSG debug bits");
#define DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK)
-struct liquidio_if_cfg_context {
- int octeon_id;
-
- wait_queue_head_t wc;
-
- int cond;
-};
-
-struct liquidio_if_cfg_resp {
- u64 rh;
- struct liquidio_if_cfg_info cfg_info;
- u64 status;
-};
-
struct liquidio_rx_ctl_context {
int octeon_id;
@@ -299,105 +285,6 @@ static struct pci_driver liquidio_vf_pci_driver = {
};
/**
- * \brief Stop Tx queues
- * @param netdev network device
- */
-static void txqs_stop(struct net_device *netdev)
-{
- if (netif_is_multiqueue(netdev)) {
- int i;
-
- for (i = 0; i < netdev->num_tx_queues; i++)
- netif_stop_subqueue(netdev, i);
- } else {
- netif_stop_queue(netdev);
- }
-}
-
-/**
- * \brief Start Tx queues
- * @param netdev network device
- */
-static void txqs_start(struct net_device *netdev)
-{
- if (netif_is_multiqueue(netdev)) {
- int i;
-
- for (i = 0; i < netdev->num_tx_queues; i++)
- netif_start_subqueue(netdev, i);
- } else {
- netif_start_queue(netdev);
- }
-}
-
-/**
- * \brief Wake Tx queues
- * @param netdev network device
- */
-static void txqs_wake(struct net_device *netdev)
-{
- struct lio *lio = GET_LIO(netdev);
-
- if (netif_is_multiqueue(netdev)) {
- int i;
-
- for (i = 0; i < netdev->num_tx_queues; i++) {
- int qno = lio->linfo.txpciq[i % lio->oct_dev->num_iqs]
- .s.q_no;
- if (__netif_subqueue_stopped(netdev, i)) {
- INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, qno,
- tx_restart, 1);
- netif_wake_subqueue(netdev, i);
- }
- }
- } else {
- INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, lio->txq,
- tx_restart, 1);
- netif_wake_queue(netdev);
- }
-}
-
-/**
- * \brief Start Tx queue
- * @param netdev network device
- */
-static void start_txq(struct net_device *netdev)
-{
- struct lio *lio = GET_LIO(netdev);
-
- if (lio->linfo.link.s.link_up) {
- txqs_start(netdev);
- return;
- }
-}
-
-/**
- * \brief Wake a queue
- * @param netdev network device
- * @param q which queue to wake
- */
-static void wake_q(struct net_device *netdev, int q)
-{
- if (netif_is_multiqueue(netdev))
- netif_wake_subqueue(netdev, q);
- else
- netif_wake_queue(netdev);
-}
-
-/**
- * \brief Stop a queue
- * @param netdev network device
- * @param q which queue to stop
- */
-static void stop_q(struct net_device *netdev, int q)
-{
- if (netif_is_multiqueue(netdev))
- netif_stop_subqueue(netdev, q);
- else
- netif_stop_queue(netdev);
-}
-
-/**
* Remove the node at the head of the list. The list would be empty at
* the end of this call if there are no more nodes in the list.
*/
@@ -564,8 +451,12 @@ static void octnet_link_status_change(struct work_struct *work)
struct cavium_wk *wk = (struct cavium_wk *)work;
struct lio *lio = (struct lio *)wk->ctxptr;
+ /* lio->linfo.link.s.mtu always contains max MTU of the lio interface.
+ * this API is invoked only when new max-MTU of the interface is
+ * less than current MTU.
+ */
rtnl_lock();
- call_netdevice_notifiers(NETDEV_CHANGEMTU, lio->netdev);
+ dev_set_mtu(lio->netdev, lio->linfo.link.s.mtu);
rtnl_unlock();
}
@@ -613,6 +504,7 @@ static void update_link_status(struct net_device *netdev,
union oct_link_status *ls)
{
struct lio *lio = GET_LIO(netdev);
+ int current_max_mtu = lio->linfo.link.s.mtu;
struct octeon_device *oct = lio->oct_dev;
if ((lio->intf_open) && (lio->linfo.link.u64 != ls->u64)) {
@@ -623,24 +515,23 @@ static void update_link_status(struct net_device *netdev,
if (lio->linfo.link.s.link_up) {
netif_carrier_on(netdev);
- txqs_wake(netdev);
+ wake_txqs(netdev);
} else {
netif_carrier_off(netdev);
- txqs_stop(netdev);
+ stop_txqs(netdev);
}
- if (lio->linfo.link.s.mtu != netdev->max_mtu) {
- dev_info(&oct->pci_dev->dev, "Max MTU Changed from %d to %d\n",
- netdev->max_mtu, lio->linfo.link.s.mtu);
+ if (lio->linfo.link.s.mtu != current_max_mtu) {
+ dev_info(&oct->pci_dev->dev,
+ "Max MTU Changed from %d to %d\n",
+ current_max_mtu, lio->linfo.link.s.mtu);
netdev->max_mtu = lio->linfo.link.s.mtu;
}
if (lio->linfo.link.s.mtu < netdev->mtu) {
dev_warn(&oct->pci_dev->dev,
- "PF has changed the MTU for gmx port. Reducing the mtu from %d to %d\n",
+ "Current MTU is higher than new max MTU; Reducing the current mtu from %d to %d\n",
netdev->mtu, lio->linfo.link.s.mtu);
- lio->mtu = lio->linfo.link.s.mtu;
- netdev->mtu = lio->linfo.link.s.mtu;
queue_delayed_work(lio->link_status_wq.wq,
&lio->link_status_wq.wk.work, 0);
}
@@ -1062,44 +953,6 @@ static int octeon_pci_os_setup(struct octeon_device *oct)
return 0;
}
-static int skb_iq(struct lio *lio, struct sk_buff *skb)
-{
- int q = 0;
-
- if (netif_is_multiqueue(lio->netdev))
- q = skb->queue_mapping % lio->linfo.num_txpciq;
-
- return q;
-}
-
-/**
- * \brief Check Tx queue state for a given network buffer
- * @param lio per-network private data
- * @param skb network buffer
- */
-static int check_txq_state(struct lio *lio, struct sk_buff *skb)
-{
- int q = 0, iq = 0;
-
- if (netif_is_multiqueue(lio->netdev)) {
- q = skb->queue_mapping;
- iq = lio->linfo.txpciq[q % lio->oct_dev->num_iqs].s.q_no;
- } else {
- iq = lio->txq;
- q = iq;
- }
-
- if (octnet_iq_is_full(lio->oct_dev, iq))
- return 0;
-
- if (__netif_subqueue_stopped(lio->netdev, q)) {
- INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, iq, tx_restart, 1);
- wake_q(lio->netdev, q);
- }
-
- return 1;
-}
-
/**
* \brief Unmap and free network buffer
* @param buf buffer
@@ -1117,8 +970,6 @@ static void free_netbuf(void *buf)
dma_unmap_single(&lio->oct_dev->pci_dev->dev, finfo->dptr, skb->len,
DMA_TO_DEVICE);
- check_txq_state(lio, skb);
-
tx_buffer_free(skb);
}
@@ -1160,8 +1011,6 @@ static void free_netsgbuf(void *buf)
list_add_tail(&g->list, &lio->glist[iq]);
spin_unlock(&lio->glist_lock[iq]);
- check_txq_state(lio, skb); /* mq support: sub-queue state check */
-
tx_buffer_free(skb);
}
@@ -1207,8 +1056,6 @@ static void free_netsgbuf_with_resp(void *buf)
spin_unlock(&lio->glist_lock[iq]);
/* Don't free the skb yet */
-
- check_txq_state(lio, skb);
}
/**
@@ -1268,7 +1115,7 @@ static int liquidio_open(struct net_device *netdev)
lio->intf_open = 1;
netif_info(lio, ifup, lio->netdev, "Interface Open, ready for traffic\n");
- start_txq(netdev);
+ start_txqs(netdev);
/* tell Octeon to start forwarding packets to host */
send_rx_ctrl_cmd(lio, 1);
@@ -1291,15 +1138,6 @@ static int liquidio_stop(struct net_device *netdev)
/* tell Octeon to stop forwarding packets to host */
send_rx_ctrl_cmd(lio, 0);
- if (oct->props[lio->ifidx].napi_enabled) {
- list_for_each_entry_safe(napi, n, &netdev->napi_list, dev_list)
- napi_disable(napi);
-
- oct->props[lio->ifidx].napi_enabled = 0;
-
- oct->droq[0]->ops.poll_mode = 0;
- }
-
netif_info(lio, ifdown, lio->netdev, "Stopping interface!\n");
/* Inform that netif carrier is down */
lio->intf_open = 0;
@@ -1310,7 +1148,21 @@ static int liquidio_stop(struct net_device *netdev)
ifstate_reset(lio, LIO_IFSTATE_RUNNING);
- txqs_stop(netdev);
+ stop_txqs(netdev);
+
+ /* Wait for any pending Rx descriptors */
+ if (lio_wait_for_clean_oq(oct))
+ netif_info(lio, rx_err, lio->netdev,
+ "Proceeding with stop interface after partial RX desc processing\n");
+
+ if (oct->props[lio->ifidx].napi_enabled == 1) {
+ list_for_each_entry_safe(napi, n, &netdev->napi_list, dev_list)
+ napi_disable(napi);
+
+ oct->props[lio->ifidx].napi_enabled = 0;
+
+ oct->droq[0]->ops.poll_mode = 0;
+ }
dev_info(&oct->pci_dev->dev, "%s interface is stopped\n", netdev->name);
@@ -1538,41 +1390,6 @@ static struct net_device_stats *liquidio_get_stats(struct net_device *netdev)
}
/**
- * \brief Net device change_mtu
- * @param netdev network device
- */
-static int liquidio_change_mtu(struct net_device *netdev, int new_mtu)
-{
- struct octnic_ctrl_pkt nctrl;
- struct octeon_device *oct;
- struct lio *lio;
- int ret = 0;
-
- lio = GET_LIO(netdev);
- oct = lio->oct_dev;
-
- memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt));
-
- nctrl.ncmd.u64 = 0;
- nctrl.ncmd.s.cmd = OCTNET_CMD_CHANGE_MTU;
- nctrl.ncmd.s.param1 = new_mtu;
- nctrl.iq_no = lio->linfo.txpciq[0].s.q_no;
- nctrl.wait_time = LIO_CMD_WAIT_TM;
- nctrl.netpndev = (u64)netdev;
- nctrl.cb_fn = liquidio_link_ctrl_cmd_completion;
-
- ret = octnet_send_nic_ctrl_pkt(lio->oct_dev, &nctrl);
- if (ret < 0) {
- dev_err(&oct->pci_dev->dev, "Failed to set MTU\n");
- return -EIO;
- }
-
- lio->mtu = new_mtu;
-
- return 0;
-}
-
-/**
* \brief Handler for SIOCSHWTSTAMP ioctl
* @param netdev network device
* @param ifr interface request
@@ -1763,14 +1580,9 @@ static int liquidio_xmit(struct sk_buff *skb, struct net_device *netdev)
lio = GET_LIO(netdev);
oct = lio->oct_dev;
- if (netif_is_multiqueue(netdev)) {
- q_idx = skb->queue_mapping;
- q_idx = (q_idx % (lio->linfo.num_txpciq));
- tag = q_idx;
- iq_no = lio->linfo.txpciq[q_idx].s.q_no;
- } else {
- iq_no = lio->txq;
- }
+ q_idx = skb_iq(lio, skb);
+ tag = q_idx;
+ iq_no = lio->linfo.txpciq[q_idx].s.q_no;
stats = &oct->instr_queue[iq_no]->stats;
@@ -1799,22 +1611,12 @@ static int liquidio_xmit(struct sk_buff *skb, struct net_device *netdev)
ndata.q_no = iq_no;
- if (netif_is_multiqueue(netdev)) {
- if (octnet_iq_is_full(oct, ndata.q_no)) {
- /* defer sending if queue is full */
- netif_info(lio, tx_err, lio->netdev, "Transmit failed iq:%d full\n",
- ndata.q_no);
- stats->tx_iq_busy++;
- return NETDEV_TX_BUSY;
- }
- } else {
- if (octnet_iq_is_full(oct, lio->txq)) {
- /* defer sending if queue is full */
- stats->tx_iq_busy++;
- netif_info(lio, tx_err, lio->netdev, "Transmit failed iq:%d full\n",
- ndata.q_no);
- return NETDEV_TX_BUSY;
- }
+ if (octnet_iq_is_full(oct, ndata.q_no)) {
+ /* defer sending if queue is full */
+ netif_info(lio, tx_err, lio->netdev, "Transmit failed iq:%d full\n",
+ ndata.q_no);
+ stats->tx_iq_busy++;
+ return NETDEV_TX_BUSY;
}
ndata.datasize = skb->len;
@@ -1956,7 +1758,7 @@ static int liquidio_xmit(struct sk_buff *skb, struct net_device *netdev)
if (status == IQ_SEND_STOP) {
dev_err(&oct->pci_dev->dev, "Rcvd IQ_SEND_STOP signal; stopping IQ-%d\n",
iq_no);
- stop_q(netdev, q_idx);
+ netif_stop_subqueue(netdev, q_idx);
}
netif_trans_update(netdev);
@@ -1996,7 +1798,7 @@ static void liquidio_tx_timeout(struct net_device *netdev)
"Transmit timeout tx_dropped:%ld, waking up queues now!!\n",
netdev->stats.tx_dropped);
netif_trans_update(netdev);
- txqs_wake(netdev);
+ wake_txqs(netdev);
}
static int
diff --git a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h
index 522dcc4dcff7..75eea83c7cc6 100644
--- a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h
+++ b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h
@@ -84,6 +84,7 @@ enum octeon_tag_type {
#define OPCODE_NIC_IF_CFG 0x09
#define OPCODE_NIC_VF_DRV_NOTICE 0x0A
#define OPCODE_NIC_INTRMOD_PARAMS 0x0B
+#define OPCODE_NIC_SET_TRUSTED_VF 0x13
#define OPCODE_NIC_SYNC_OCTEON_TIME 0x14
#define VF_DRV_LOADED 1
#define VF_DRV_REMOVED -1
@@ -192,7 +193,8 @@ static inline void add_sg_size(struct octeon_sg_entry *sg_entry,
#define OCTNET_MAX_FRM_SIZE (16000 + OCTNET_FRM_HEADER_SIZE)
-#define OCTNET_DEFAULT_FRM_SIZE (1500 + OCTNET_FRM_HEADER_SIZE)
+#define OCTNET_DEFAULT_MTU (1500)
+#define OCTNET_DEFAULT_FRM_SIZE (OCTNET_DEFAULT_MTU + OCTNET_FRM_HEADER_SIZE)
/** NIC Commands are sent using this Octeon Input Queue */
#define OCTNET_CMD_Q 0
@@ -675,9 +677,11 @@ union oct_link_status {
u64 if_mode:5;
u64 pause:1;
u64 flashing:1;
- u64 reserved:15;
+ u64 phy_type:5;
+ u64 reserved:10;
#else
- u64 reserved:15;
+ u64 reserved:10;
+ u64 phy_type:5;
u64 flashing:1;
u64 pause:1;
u64 if_mode:5;
@@ -690,6 +694,12 @@ union oct_link_status {
} s;
};
+enum lio_phy_type {
+ LIO_PHY_PORT_TP = 0x0,
+ LIO_PHY_PORT_FIBRE = 0x1,
+ LIO_PHY_PORT_UNKNOWN,
+};
+
/** The txpciq info passed to host from the firmware */
union oct_txpciq {
@@ -702,9 +712,13 @@ union oct_txpciq {
u64 pkind:6;
u64 use_qpg:1;
u64 qpg:11;
- u64 reserved:30;
+ u64 reserved0:10;
+ u64 ctrl_qpg:11;
+ u64 reserved:9;
#else
- u64 reserved:30;
+ u64 reserved:9;
+ u64 ctrl_qpg:11;
+ u64 reserved0:10;
u64 qpg:11;
u64 use_qpg:1;
u64 pkind:6;
@@ -909,6 +923,12 @@ union oct_nic_if_cfg {
} s;
};
+struct lio_trusted_vf {
+ uint64_t active: 1;
+ uint64_t id : 8;
+ uint64_t reserved: 55;
+};
+
struct lio_time {
s64 sec; /* seconds */
s64 nsec; /* nanoseconds */
diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.h b/drivers/net/ethernet/cavium/liquidio/octeon_device.h
index 63b0c758a0a6..91937cc5c1d7 100644
--- a/drivers/net/ethernet/cavium/liquidio/octeon_device.h
+++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.h
@@ -370,6 +370,8 @@ struct octeon_sriov_info {
u32 sriov_enabled;
+ struct lio_trusted_vf trusted_vf;
+
/*lookup table that maps DPI ring number to VF pci_dev struct pointer*/
struct pci_dev *dpiring_to_vfpcidev_lut[MAX_POSSIBLE_VFS];
diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_droq.c b/drivers/net/ethernet/cavium/liquidio/octeon_droq.c
index 3461d65ff4eb..f044718cea52 100644
--- a/drivers/net/ethernet/cavium/liquidio/octeon_droq.c
+++ b/drivers/net/ethernet/cavium/liquidio/octeon_droq.c
@@ -788,7 +788,7 @@ octeon_droq_process_packets(struct octeon_device *oct,
* called before calling this routine.
*/
-static int
+int
octeon_droq_process_poll_pkts(struct octeon_device *oct,
struct octeon_droq *droq, u32 budget)
{
@@ -835,71 +835,46 @@ octeon_droq_process_poll_pkts(struct octeon_device *oct,
return total_pkts_processed;
}
+/* Enable Pkt Interrupt */
int
-octeon_process_droq_poll_cmd(struct octeon_device *oct, u32 q_no, int cmd,
- u32 arg)
+octeon_enable_irq(struct octeon_device *oct, u32 q_no)
{
- struct octeon_droq *droq;
-
- droq = oct->droq[q_no];
+ switch (oct->chip_id) {
+ case OCTEON_CN66XX:
+ case OCTEON_CN68XX: {
+ struct octeon_cn6xxx *cn6xxx =
+ (struct octeon_cn6xxx *)oct->chip;
+ unsigned long flags;
+ u32 value;
- if (cmd == POLL_EVENT_PROCESS_PKTS)
- return octeon_droq_process_poll_pkts(oct, droq, arg);
+ spin_lock_irqsave
+ (&cn6xxx->lock_for_droq_int_enb_reg, flags);
+ value = octeon_read_csr(oct, CN6XXX_SLI_PKT_TIME_INT_ENB);
+ value |= (1 << q_no);
+ octeon_write_csr(oct, CN6XXX_SLI_PKT_TIME_INT_ENB, value);
+ value = octeon_read_csr(oct, CN6XXX_SLI_PKT_CNT_INT_ENB);
+ value |= (1 << q_no);
+ octeon_write_csr(oct, CN6XXX_SLI_PKT_CNT_INT_ENB, value);
- if (cmd == POLL_EVENT_PENDING_PKTS) {
- u32 pkt_cnt = atomic_read(&droq->pkts_pending);
+ /* don't bother flushing the enables */
- return octeon_droq_process_packets(oct, droq, pkt_cnt);
+ spin_unlock_irqrestore
+ (&cn6xxx->lock_for_droq_int_enb_reg, flags);
}
-
- if (cmd == POLL_EVENT_ENABLE_INTR) {
- u32 value;
- unsigned long flags;
-
- /* Enable Pkt Interrupt */
- switch (oct->chip_id) {
- case OCTEON_CN66XX:
- case OCTEON_CN68XX: {
- struct octeon_cn6xxx *cn6xxx =
- (struct octeon_cn6xxx *)oct->chip;
- spin_lock_irqsave
- (&cn6xxx->lock_for_droq_int_enb_reg, flags);
- value =
- octeon_read_csr(oct,
- CN6XXX_SLI_PKT_TIME_INT_ENB);
- value |= (1 << q_no);
- octeon_write_csr(oct,
- CN6XXX_SLI_PKT_TIME_INT_ENB,
- value);
- value =
- octeon_read_csr(oct,
- CN6XXX_SLI_PKT_CNT_INT_ENB);
- value |= (1 << q_no);
- octeon_write_csr(oct,
- CN6XXX_SLI_PKT_CNT_INT_ENB,
- value);
-
- /* don't bother flushing the enables */
-
- spin_unlock_irqrestore
- (&cn6xxx->lock_for_droq_int_enb_reg, flags);
- return 0;
- }
break;
- case OCTEON_CN23XX_PF_VID: {
- lio_enable_irq(oct->droq[q_no], oct->instr_queue[q_no]);
- }
+ case OCTEON_CN23XX_PF_VID:
+ lio_enable_irq(oct->droq[q_no], oct->instr_queue[q_no]);
break;
- case OCTEON_CN23XX_VF_VID:
- lio_enable_irq(oct->droq[q_no], oct->instr_queue[q_no]);
+ case OCTEON_CN23XX_VF_VID:
+ lio_enable_irq(oct->droq[q_no], oct->instr_queue[q_no]);
break;
- }
- return 0;
+ default:
+ dev_err(&oct->pci_dev->dev, "%s Unknown Chip\n", __func__);
+ return 1;
}
- dev_err(&oct->pci_dev->dev, "%s Unknown command: %d\n", __func__, cmd);
- return -EINVAL;
+ return 0;
}
int octeon_register_droq_ops(struct octeon_device *oct, u32 q_no,
diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_droq.h b/drivers/net/ethernet/cavium/liquidio/octeon_droq.h
index 815a9f56fd59..f28f262d4ab6 100644
--- a/drivers/net/ethernet/cavium/liquidio/octeon_droq.h
+++ b/drivers/net/ethernet/cavium/liquidio/octeon_droq.h
@@ -123,11 +123,6 @@ struct oct_droq_stats {
};
-#define POLL_EVENT_INTR_ARRIVED 1
-#define POLL_EVENT_PROCESS_PKTS 2
-#define POLL_EVENT_PENDING_PKTS 3
-#define POLL_EVENT_ENABLE_INTR 4
-
/* The maximum number of buffers that can be dispatched from the
* output/dma queue. Set to 64 assuming 1K buffers in DROQ and the fact that
* max packet size from DROQ is 64K.
@@ -414,8 +409,10 @@ int octeon_droq_process_packets(struct octeon_device *oct,
struct octeon_droq *droq,
u32 budget);
-int octeon_process_droq_poll_cmd(struct octeon_device *oct, u32 q_no,
- int cmd, u32 arg);
+int octeon_droq_process_poll_pkts(struct octeon_device *oct,
+ struct octeon_droq *droq, u32 budget);
+
+int octeon_enable_irq(struct octeon_device *oct, u32 q_no);
void octeon_droq_check_oom(struct octeon_droq *droq);
diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c b/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c
index 57af7df74ced..28e74ee23ff8 100644
--- a/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c
+++ b/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c
@@ -87,7 +87,7 @@ int octeon_mbox_read(struct octeon_mbox *mbox)
}
if (mbox->state & OCTEON_MBOX_STATE_REQUEST_RECEIVING) {
- if (mbox->mbox_req.recv_len < msg.s.len) {
+ if (mbox->mbox_req.recv_len < mbox->mbox_req.msg.s.len) {
ret = 0;
} else {
mbox->state &= ~OCTEON_MBOX_STATE_REQUEST_RECEIVING;
@@ -96,7 +96,8 @@ int octeon_mbox_read(struct octeon_mbox *mbox)
}
} else {
if (mbox->state & OCTEON_MBOX_STATE_RESPONSE_RECEIVING) {
- if (mbox->mbox_resp.recv_len < msg.s.len) {
+ if (mbox->mbox_resp.recv_len <
+ mbox->mbox_resp.msg.s.len) {
ret = 0;
} else {
mbox->state &=
diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_network.h b/drivers/net/ethernet/cavium/liquidio/octeon_network.h
index f2d1a076a038..4069710796a8 100644
--- a/drivers/net/ethernet/cavium/liquidio/octeon_network.h
+++ b/drivers/net/ethernet/cavium/liquidio/octeon_network.h
@@ -35,6 +35,18 @@
#define LIO_IFSTATE_RX_TIMESTAMP_ENABLED 0x08
#define LIO_IFSTATE_RESETTING 0x10
+struct liquidio_if_cfg_context {
+ u32 octeon_id;
+ wait_queue_head_t wc;
+ int cond;
+};
+
+struct liquidio_if_cfg_resp {
+ u64 rh;
+ struct liquidio_if_cfg_info cfg_info;
+ u64 status;
+};
+
struct oct_nic_stats_resp {
u64 rh;
struct oct_link_stats stats;
@@ -178,12 +190,21 @@ irqreturn_t liquidio_msix_intr_handler(int irq __attribute__((unused)),
int octeon_setup_interrupt(struct octeon_device *oct, u32 num_ioqs);
+int lio_wait_for_clean_oq(struct octeon_device *oct);
/**
* \brief Register ethtool operations
* @param netdev pointer to network device
*/
void liquidio_set_ethtool_ops(struct net_device *netdev);
+/**
+ * \brief Net device change_mtu
+ * @param netdev network device
+ */
+int liquidio_change_mtu(struct net_device *netdev, int new_mtu);
+#define LIO_CHANGE_MTU_SUCCESS 1
+#define LIO_CHANGE_MTU_FAIL 2
+
#define SKB_ADJ_MASK 0x3F
#define SKB_ADJ (SKB_ADJ_MASK + 1)
@@ -486,4 +507,56 @@ static inline int wait_for_pending_requests(struct octeon_device *oct)
return 0;
}
+/**
+ * \brief Stop Tx queues
+ * @param netdev network device
+ */
+static inline void stop_txqs(struct net_device *netdev)
+{
+ int i;
+
+ for (i = 0; i < netdev->num_tx_queues; i++)
+ netif_stop_subqueue(netdev, i);
+}
+
+/**
+ * \brief Wake Tx queues
+ * @param netdev network device
+ */
+static inline void wake_txqs(struct net_device *netdev)
+{
+ struct lio *lio = GET_LIO(netdev);
+ int i, qno;
+
+ for (i = 0; i < netdev->num_tx_queues; i++) {
+ qno = lio->linfo.txpciq[i % lio->oct_dev->num_iqs].s.q_no;
+
+ if (__netif_subqueue_stopped(netdev, i)) {
+ INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, qno,
+ tx_restart, 1);
+ netif_wake_subqueue(netdev, i);
+ }
+ }
+}
+
+/**
+ * \brief Start Tx queues
+ * @param netdev network device
+ */
+static inline void start_txqs(struct net_device *netdev)
+{
+ struct lio *lio = GET_LIO(netdev);
+ int i;
+
+ if (lio->linfo.link.s.link_up) {
+ for (i = 0; i < netdev->num_tx_queues; i++)
+ netif_start_subqueue(netdev, i);
+ }
+}
+
+static inline int skb_iq(struct lio *lio, struct sk_buff *skb)
+{
+ return skb->queue_mapping % lio->linfo.num_txpciq;
+}
+
#endif
diff --git a/drivers/net/ethernet/cavium/liquidio/request_manager.c b/drivers/net/ethernet/cavium/liquidio/request_manager.c
index e07d2093b971..b1270355b0b1 100644
--- a/drivers/net/ethernet/cavium/liquidio/request_manager.c
+++ b/drivers/net/ethernet/cavium/liquidio/request_manager.c
@@ -366,6 +366,7 @@ int
lio_process_iq_request_list(struct octeon_device *oct,
struct octeon_instr_queue *iq, u32 napi_budget)
{
+ struct cavium_wq *cwq = &oct->dma_comp_wq;
int reqtype;
void *buf;
u32 old = iq->flush_index;
@@ -450,6 +451,10 @@ lio_process_iq_request_list(struct octeon_device *oct,
bytes_compl);
iq->flush_index = old;
+ if (atomic_read(&oct->response_list
+ [OCTEON_ORDERED_SC_LIST].pending_req_count))
+ queue_delayed_work(cwq->wq, &cwq->wk.work, msecs_to_jiffies(1));
+
return inst_count;
}
@@ -623,7 +628,8 @@ octeon_prepare_soft_command(struct octeon_device *oct,
pki_ih3->tag = LIO_CONTROL;
pki_ih3->tagtype = ATOMIC_TAG;
pki_ih3->qpg =
- oct->instr_queue[sc->iq_no]->txpciq.s.qpg;
+ oct->instr_queue[sc->iq_no]->txpciq.s.ctrl_qpg;
+
pki_ih3->pm = 0x7;
pki_ih3->sl = 8;
diff --git a/drivers/net/ethernet/cavium/liquidio/response_manager.c b/drivers/net/ethernet/cavium/liquidio/response_manager.c
index 3d691c69f74d..fe5b53700576 100644
--- a/drivers/net/ethernet/cavium/liquidio/response_manager.c
+++ b/drivers/net/ethernet/cavium/liquidio/response_manager.c
@@ -49,7 +49,6 @@ int octeon_setup_response_list(struct octeon_device *oct)
INIT_DELAYED_WORK(&cwq->wk.work, oct_poll_req_completion);
cwq->wk.ctxptr = oct;
oct->cmd_resp_state = OCT_DRV_ONLINE;
- queue_delayed_work(cwq->wq, &cwq->wk.work, msecs_to_jiffies(50));
return ret;
}
@@ -164,5 +163,8 @@ static void oct_poll_req_completion(struct work_struct *work)
struct cavium_wq *cwq = &oct->dma_comp_wq;
lio_process_ordered_list(oct, 0);
- queue_delayed_work(cwq->wq, &cwq->wk.work, msecs_to_jiffies(50));
+
+ if (atomic_read(&oct->response_list
+ [OCTEON_ORDERED_SC_LIST].pending_req_count))
+ queue_delayed_work(cwq->wq, &cwq->wk.work, msecs_to_jiffies(1));
}
diff --git a/drivers/net/ethernet/cavium/thunder/nic.h b/drivers/net/ethernet/cavium/thunder/nic.h
index 4cacce5d2b16..448d1fafc827 100644
--- a/drivers/net/ethernet/cavium/thunder/nic.h
+++ b/drivers/net/ethernet/cavium/thunder/nic.h
@@ -265,6 +265,17 @@ struct nicvf_drv_stats {
struct cavium_ptp;
+struct xcast_addr_list {
+ int count;
+ u64 mc[];
+};
+
+struct nicvf_work {
+ struct delayed_work work;
+ u8 mode;
+ struct xcast_addr_list *mc;
+};
+
struct nicvf {
struct nicvf *pnicvf;
struct net_device *netdev;
@@ -313,6 +324,7 @@ struct nicvf {
struct nicvf_pfc pfc;
struct tasklet_struct qs_err_task;
struct work_struct reset_task;
+ struct nicvf_work rx_mode_work;
/* PTP timestamp */
struct cavium_ptp *ptp_clock;
@@ -403,6 +415,9 @@ struct nicvf {
#define NIC_MBOX_MSG_PTP_CFG 0x19 /* HW packet timestamp */
#define NIC_MBOX_MSG_CFG_DONE 0xF0 /* VF configuration done */
#define NIC_MBOX_MSG_SHUTDOWN 0xF1 /* VF is being shutdown */
+#define NIC_MBOX_MSG_RESET_XCAST 0xF2 /* Reset DCAM filtering mode */
+#define NIC_MBOX_MSG_ADD_MCAST 0xF3 /* Add MAC to DCAM filters */
+#define NIC_MBOX_MSG_SET_XCAST 0xF4 /* Set MCAST/BCAST RX mode */
struct nic_cfg_msg {
u8 msg;
@@ -556,6 +571,14 @@ struct set_ptp {
bool enable;
};
+struct xcast {
+ u8 msg;
+ union {
+ u8 mode;
+ u64 mac;
+ } data;
+};
+
/* 128 bit shared memory between PF and each VF */
union nic_mbx {
struct { u8 msg; } msg;
@@ -576,6 +599,7 @@ union nic_mbx {
struct reset_stat_cfg reset_stat;
struct pfc pfc;
struct set_ptp ptp;
+ struct xcast xcast;
};
#define NIC_NODE_ID_MASK 0x03
diff --git a/drivers/net/ethernet/cavium/thunder/nic_main.c b/drivers/net/ethernet/cavium/thunder/nic_main.c
index 7ff66a8194e2..55af04fa03a7 100644
--- a/drivers/net/ethernet/cavium/thunder/nic_main.c
+++ b/drivers/net/ethernet/cavium/thunder/nic_main.c
@@ -21,6 +21,8 @@
#define DRV_NAME "nicpf"
#define DRV_VERSION "1.0"
+#define NIC_VF_PER_MBX_REG 64
+
struct hw_info {
u8 bgx_cnt;
u8 chans_per_lmac;
@@ -1072,6 +1074,40 @@ static void nic_handle_mbx_intr(struct nicpf *nic, int vf)
case NIC_MBOX_MSG_PTP_CFG:
nic_config_timestamp(nic, vf, &mbx.ptp);
break;
+ case NIC_MBOX_MSG_RESET_XCAST:
+ if (vf >= nic->num_vf_en) {
+ ret = -1; /* NACK */
+ break;
+ }
+ bgx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]);
+ lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]);
+ bgx_reset_xcast_mode(nic->node, bgx, lmac,
+ vf < NIC_VF_PER_MBX_REG ? vf :
+ vf - NIC_VF_PER_MBX_REG);
+ break;
+
+ case NIC_MBOX_MSG_ADD_MCAST:
+ if (vf >= nic->num_vf_en) {
+ ret = -1; /* NACK */
+ break;
+ }
+ bgx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]);
+ lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]);
+ bgx_set_dmac_cam_filter(nic->node, bgx, lmac,
+ mbx.xcast.data.mac,
+ vf < NIC_VF_PER_MBX_REG ? vf :
+ vf - NIC_VF_PER_MBX_REG);
+ break;
+
+ case NIC_MBOX_MSG_SET_XCAST:
+ if (vf >= nic->num_vf_en) {
+ ret = -1; /* NACK */
+ break;
+ }
+ bgx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]);
+ lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]);
+ bgx_set_xcast_mode(nic->node, bgx, lmac, mbx.xcast.data.mode);
+ break;
default:
dev_err(&nic->pdev->dev,
"Invalid msg from VF%d, msg 0x%x\n", vf, mbx.msg.msg);
@@ -1094,7 +1130,7 @@ static irqreturn_t nic_mbx_intr_handler(int irq, void *nic_irq)
struct nicpf *nic = (struct nicpf *)nic_irq;
int mbx;
u64 intr;
- u8 vf, vf_per_mbx_reg = 64;
+ u8 vf;
if (irq == pci_irq_vector(nic->pdev, NIC_PF_INTR_ID_MBOX0))
mbx = 0;
@@ -1103,12 +1139,13 @@ static irqreturn_t nic_mbx_intr_handler(int irq, void *nic_irq)
intr = nic_reg_read(nic, NIC_PF_MAILBOX_INT + (mbx << 3));
dev_dbg(&nic->pdev->dev, "PF interrupt Mbox%d 0x%llx\n", mbx, intr);
- for (vf = 0; vf < vf_per_mbx_reg; vf++) {
+ for (vf = 0; vf < NIC_VF_PER_MBX_REG; vf++) {
if (intr & (1ULL << vf)) {
dev_dbg(&nic->pdev->dev, "Intr from VF %d\n",
- vf + (mbx * vf_per_mbx_reg));
+ vf + (mbx * NIC_VF_PER_MBX_REG));
- nic_handle_mbx_intr(nic, vf + (mbx * vf_per_mbx_reg));
+ nic_handle_mbx_intr(nic, vf +
+ (mbx * NIC_VF_PER_MBX_REG));
nic_clear_mbx_intr(nic, vf, mbx);
}
}
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c
index 7d9c5ffbd041..707db3304396 100644
--- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c
@@ -21,6 +21,7 @@
#include <linux/bpf_trace.h>
#include <linux/filter.h>
#include <linux/net_tstamp.h>
+#include <linux/workqueue.h>
#include "nic_reg.h"
#include "nic.h"
@@ -63,10 +64,13 @@ module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Debug message level bitmap");
static int cpi_alg = CPI_ALG_NONE;
-module_param(cpi_alg, int, S_IRUGO);
+module_param(cpi_alg, int, 0444);
MODULE_PARM_DESC(cpi_alg,
"PFC algorithm (0=none, 1=VLAN, 2=VLAN16, 3=IP Diffserv)");
+/* workqueue for handling kernel ndo_set_rx_mode() calls */
+static struct workqueue_struct *nicvf_rx_mode_wq;
+
static inline u8 nicvf_netdev_qidx(struct nicvf *nic, u8 qidx)
{
if (nic->sqs_mode)
@@ -1919,6 +1923,92 @@ static int nicvf_ioctl(struct net_device *netdev, struct ifreq *req, int cmd)
}
}
+static void nicvf_set_rx_mode_task(struct work_struct *work_arg)
+{
+ struct nicvf_work *vf_work = container_of(work_arg, struct nicvf_work,
+ work.work);
+ struct nicvf *nic = container_of(vf_work, struct nicvf, rx_mode_work);
+ union nic_mbx mbx = {};
+ int idx;
+
+ if (!vf_work)
+ return;
+
+ /* From the inside of VM code flow we have only 128 bits memory
+ * available to send message to host's PF, so send all mc addrs
+ * one by one, starting from flush command in case if kernel
+ * requests to configure specific MAC filtering
+ */
+
+ /* flush DMAC filters and reset RX mode */
+ mbx.xcast.msg = NIC_MBOX_MSG_RESET_XCAST;
+ nicvf_send_msg_to_pf(nic, &mbx);
+
+ if (vf_work->mode & BGX_XCAST_MCAST_FILTER) {
+ /* once enabling filtering, we need to signal to PF to add
+ * its' own LMAC to the filter to accept packets for it.
+ */
+ mbx.xcast.msg = NIC_MBOX_MSG_ADD_MCAST;
+ mbx.xcast.data.mac = 0;
+ nicvf_send_msg_to_pf(nic, &mbx);
+ }
+
+ /* check if we have any specific MACs to be added to PF DMAC filter */
+ if (vf_work->mc) {
+ /* now go through kernel list of MACs and add them one by one */
+ for (idx = 0; idx < vf_work->mc->count; idx++) {
+ mbx.xcast.msg = NIC_MBOX_MSG_ADD_MCAST;
+ mbx.xcast.data.mac = vf_work->mc->mc[idx];
+ nicvf_send_msg_to_pf(nic, &mbx);
+ }
+ kfree(vf_work->mc);
+ }
+
+ /* and finally set rx mode for PF accordingly */
+ mbx.xcast.msg = NIC_MBOX_MSG_SET_XCAST;
+ mbx.xcast.data.mode = vf_work->mode;
+
+ nicvf_send_msg_to_pf(nic, &mbx);
+}
+
+static void nicvf_set_rx_mode(struct net_device *netdev)
+{
+ struct nicvf *nic = netdev_priv(netdev);
+ struct netdev_hw_addr *ha;
+ struct xcast_addr_list *mc_list = NULL;
+ u8 mode = 0;
+
+ if (netdev->flags & IFF_PROMISC) {
+ mode = BGX_XCAST_BCAST_ACCEPT | BGX_XCAST_MCAST_ACCEPT;
+ } else {
+ if (netdev->flags & IFF_BROADCAST)
+ mode |= BGX_XCAST_BCAST_ACCEPT;
+
+ if (netdev->flags & IFF_ALLMULTI) {
+ mode |= BGX_XCAST_MCAST_ACCEPT;
+ } else if (netdev->flags & IFF_MULTICAST) {
+ mode |= BGX_XCAST_MCAST_FILTER;
+ /* here we need to copy mc addrs */
+ if (netdev_mc_count(netdev)) {
+ mc_list = kmalloc(offsetof(typeof(*mc_list),
+ mc[netdev_mc_count(netdev)]),
+ GFP_ATOMIC);
+ if (unlikely(!mc_list))
+ return;
+ mc_list->count = 0;
+ netdev_hw_addr_list_for_each(ha, &netdev->mc) {
+ mc_list->mc[mc_list->count] =
+ ether_addr_to_u64(ha->addr);
+ mc_list->count++;
+ }
+ }
+ }
+ }
+ nic->rx_mode_work.mc = mc_list;
+ nic->rx_mode_work.mode = mode;
+ queue_delayed_work(nicvf_rx_mode_wq, &nic->rx_mode_work.work, 2 * HZ);
+}
+
static const struct net_device_ops nicvf_netdev_ops = {
.ndo_open = nicvf_open,
.ndo_stop = nicvf_stop,
@@ -1931,6 +2021,7 @@ static const struct net_device_ops nicvf_netdev_ops = {
.ndo_set_features = nicvf_set_features,
.ndo_bpf = nicvf_xdp,
.ndo_do_ioctl = nicvf_ioctl,
+ .ndo_set_rx_mode = nicvf_set_rx_mode,
};
static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
@@ -2071,6 +2162,8 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
INIT_WORK(&nic->reset_task, nicvf_reset_task);
+ INIT_DELAYED_WORK(&nic->rx_mode_work.work, nicvf_set_rx_mode_task);
+
err = register_netdev(netdev);
if (err) {
dev_err(dev, "Failed to register netdevice\n");
@@ -2109,6 +2202,8 @@ static void nicvf_remove(struct pci_dev *pdev)
nic = netdev_priv(netdev);
pnetdev = nic->pnicvf->netdev;
+ cancel_delayed_work_sync(&nic->rx_mode_work.work);
+
/* Check if this Qset is assigned to different VF.
* If yes, clean primary and all secondary Qsets.
*/
@@ -2140,12 +2235,17 @@ static struct pci_driver nicvf_driver = {
static int __init nicvf_init_module(void)
{
pr_info("%s, ver %s\n", DRV_NAME, DRV_VERSION);
-
+ nicvf_rx_mode_wq = alloc_ordered_workqueue("nicvf_generic",
+ WQ_MEM_RECLAIM);
return pci_register_driver(&nicvf_driver);
}
static void __exit nicvf_cleanup_module(void)
{
+ if (nicvf_rx_mode_wq) {
+ destroy_workqueue(nicvf_rx_mode_wq);
+ nicvf_rx_mode_wq = NULL;
+ }
pci_unregister_driver(&nicvf_driver);
}
diff --git a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c
index 91d34ea40e2c..5d08d2aeb172 100644
--- a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c
+++ b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c
@@ -24,9 +24,31 @@
#define DRV_NAME "thunder_bgx"
#define DRV_VERSION "1.0"
+/* RX_DMAC_CTL configuration */
+enum MCAST_MODE {
+ MCAST_MODE_REJECT = 0x0,
+ MCAST_MODE_ACCEPT = 0x1,
+ MCAST_MODE_CAM_FILTER = 0x2,
+ RSVD = 0x3
+};
+
+#define BCAST_ACCEPT BIT(0)
+#define CAM_ACCEPT BIT(3)
+#define MCAST_MODE_MASK 0x3
+#define BGX_MCAST_MODE(x) (x << 1)
+
+struct dmac_map {
+ u64 vf_map;
+ u64 dmac;
+};
+
struct lmac {
struct bgx *bgx;
- int dmac;
+ /* actual number of DMACs configured */
+ u8 dmacs_cfg;
+ /* overal number of possible DMACs could be configured per LMAC */
+ u8 dmacs_count;
+ struct dmac_map *dmacs; /* DMAC:VFs tracking filter array */
u8 mac[ETH_ALEN];
u8 lmac_type;
u8 lane_to_sds;
@@ -223,6 +245,163 @@ void bgx_set_lmac_mac(int node, int bgx_idx, int lmacid, const u8 *mac)
}
EXPORT_SYMBOL(bgx_set_lmac_mac);
+static void bgx_flush_dmac_cam_filter(struct bgx *bgx, int lmacid)
+{
+ struct lmac *lmac = NULL;
+ u8 idx = 0;
+
+ lmac = &bgx->lmac[lmacid];
+ /* reset CAM filters */
+ for (idx = 0; idx < lmac->dmacs_count; idx++)
+ bgx_reg_write(bgx, 0, BGX_CMR_RX_DMACX_CAM +
+ ((lmacid * lmac->dmacs_count) + idx) *
+ sizeof(u64), 0);
+}
+
+static void bgx_lmac_remove_filters(struct lmac *lmac, u8 vf_id)
+{
+ int i = 0;
+
+ if (!lmac)
+ return;
+
+ /* We've got reset filters request from some of attached VF, while the
+ * others might want to keep their configuration. So in this case lets
+ * iterate over all of configured filters and decrease number of
+ * referencies. if some addresses get zero refs remove them from list
+ */
+ for (i = lmac->dmacs_cfg - 1; i >= 0; i--) {
+ lmac->dmacs[i].vf_map &= ~BIT_ULL(vf_id);
+ if (!lmac->dmacs[i].vf_map) {
+ lmac->dmacs_cfg--;
+ lmac->dmacs[i].dmac = 0;
+ lmac->dmacs[i].vf_map = 0;
+ }
+ }
+}
+
+static int bgx_lmac_save_filter(struct lmac *lmac, u64 dmac, u8 vf_id)
+{
+ u8 i = 0;
+
+ if (!lmac)
+ return -1;
+
+ /* At the same time we could have several VFs 'attached' to some
+ * particular LMAC, and each VF is represented as network interface
+ * for kernel. So from user perspective it should be possible to
+ * manipulate with its' (VF) receive modes. However from PF
+ * driver perspective we need to keep track of filter configurations
+ * for different VFs to prevent filter values dupes
+ */
+ for (i = 0; i < lmac->dmacs_cfg; i++) {
+ if (lmac->dmacs[i].dmac == dmac) {
+ lmac->dmacs[i].vf_map |= BIT_ULL(vf_id);
+ return -1;
+ }
+ }
+
+ if (!(lmac->dmacs_cfg < lmac->dmacs_count))
+ return -1;
+
+ /* keep it for further tracking */
+ lmac->dmacs[lmac->dmacs_cfg].dmac = dmac;
+ lmac->dmacs[lmac->dmacs_cfg].vf_map = BIT_ULL(vf_id);
+ lmac->dmacs_cfg++;
+ return 0;
+}
+
+static int bgx_set_dmac_cam_filter_mac(struct bgx *bgx, int lmacid,
+ u64 cam_dmac, u8 idx)
+{
+ struct lmac *lmac = NULL;
+ u64 cfg = 0;
+
+ /* skip zero addresses as meaningless */
+ if (!cam_dmac || !bgx)
+ return -1;
+
+ lmac = &bgx->lmac[lmacid];
+
+ /* configure DCAM filtering for designated LMAC */
+ cfg = RX_DMACX_CAM_LMACID(lmacid & LMAC_ID_MASK) |
+ RX_DMACX_CAM_EN | cam_dmac;
+ bgx_reg_write(bgx, 0, BGX_CMR_RX_DMACX_CAM +
+ ((lmacid * lmac->dmacs_count) + idx) * sizeof(u64), cfg);
+ return 0;
+}
+
+void bgx_set_dmac_cam_filter(int node, int bgx_idx, int lmacid,
+ u64 cam_dmac, u8 vf_id)
+{
+ struct bgx *bgx = get_bgx(node, bgx_idx);
+ struct lmac *lmac = NULL;
+
+ if (!bgx)
+ return;
+
+ lmac = &bgx->lmac[lmacid];
+
+ if (!cam_dmac)
+ cam_dmac = ether_addr_to_u64(lmac->mac);
+
+ /* since we might have several VFs attached to particular LMAC
+ * and kernel could call mcast config for each of them with the
+ * same MAC, check if requested MAC is already in filtering list and
+ * updare/prepare list of MACs to be applied later to HW filters
+ */
+ bgx_lmac_save_filter(lmac, cam_dmac, vf_id);
+}
+EXPORT_SYMBOL(bgx_set_dmac_cam_filter);
+
+void bgx_set_xcast_mode(int node, int bgx_idx, int lmacid, u8 mode)
+{
+ struct bgx *bgx = get_bgx(node, bgx_idx);
+ struct lmac *lmac = NULL;
+ u64 cfg = 0;
+ u8 i = 0;
+
+ if (!bgx)
+ return;
+
+ lmac = &bgx->lmac[lmacid];
+
+ cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_RX_DMAC_CTL);
+ if (mode & BGX_XCAST_BCAST_ACCEPT)
+ cfg |= BCAST_ACCEPT;
+ else
+ cfg &= ~BCAST_ACCEPT;
+
+ /* disable all MCASTs and DMAC filtering */
+ cfg &= ~(CAM_ACCEPT | BGX_MCAST_MODE(MCAST_MODE_MASK));
+
+ /* check requested bits and set filtergin mode appropriately */
+ if (mode & (BGX_XCAST_MCAST_ACCEPT)) {
+ cfg |= (BGX_MCAST_MODE(MCAST_MODE_ACCEPT));
+ } else if (mode & BGX_XCAST_MCAST_FILTER) {
+ cfg |= (BGX_MCAST_MODE(MCAST_MODE_CAM_FILTER) | CAM_ACCEPT);
+ for (i = 0; i < lmac->dmacs_cfg; i++)
+ bgx_set_dmac_cam_filter_mac(bgx, lmacid,
+ lmac->dmacs[i].dmac, i);
+ }
+ bgx_reg_write(bgx, lmacid, BGX_CMRX_RX_DMAC_CTL, cfg);
+}
+EXPORT_SYMBOL(bgx_set_xcast_mode);
+
+void bgx_reset_xcast_mode(int node, int bgx_idx, int lmacid, u8 vf_id)
+{
+ struct bgx *bgx = get_bgx(node, bgx_idx);
+
+ if (!bgx)
+ return;
+
+ bgx_lmac_remove_filters(&bgx->lmac[lmacid], vf_id);
+ bgx_flush_dmac_cam_filter(bgx, lmacid);
+ bgx_set_xcast_mode(node, bgx_idx, lmacid,
+ (BGX_XCAST_BCAST_ACCEPT | BGX_XCAST_MCAST_ACCEPT));
+}
+EXPORT_SYMBOL(bgx_reset_xcast_mode);
+
void bgx_lmac_rx_tx_enable(int node, int bgx_idx, int lmacid, bool enable)
{
struct bgx *bgx = get_bgx(node, bgx_idx);
@@ -468,18 +647,6 @@ u64 bgx_get_tx_stats(int node, int bgx_idx, int lmac, int idx)
}
EXPORT_SYMBOL(bgx_get_tx_stats);
-static void bgx_flush_dmac_addrs(struct bgx *bgx, int lmac)
-{
- u64 offset;
-
- while (bgx->lmac[lmac].dmac > 0) {
- offset = ((bgx->lmac[lmac].dmac - 1) * sizeof(u64)) +
- (lmac * MAX_DMAC_PER_LMAC * sizeof(u64));
- bgx_reg_write(bgx, 0, BGX_CMR_RX_DMACX_CAM + offset, 0);
- bgx->lmac[lmac].dmac--;
- }
-}
-
/* Configure BGX LMAC in internal loopback mode */
void bgx_lmac_internal_loopback(int node, int bgx_idx,
int lmac_idx, bool enable)
@@ -912,6 +1079,11 @@ static int bgx_lmac_enable(struct bgx *bgx, u8 lmacid)
bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_MIN_PKT, 60 + 4);
}
+ /* actual number of filters available to exact LMAC */
+ lmac->dmacs_count = (RX_DMAC_COUNT / bgx->lmac_count);
+ lmac->dmacs = kcalloc(lmac->dmacs_count, sizeof(*lmac->dmacs),
+ GFP_KERNEL);
+
/* Enable lmac */
bgx_reg_modify(bgx, lmacid, BGX_CMRX_CFG, CMR_EN);
@@ -998,7 +1170,8 @@ static void bgx_lmac_disable(struct bgx *bgx, u8 lmacid)
cfg &= ~CMR_EN;
bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg);
- bgx_flush_dmac_addrs(bgx, lmacid);
+ bgx_flush_dmac_cam_filter(bgx, lmacid);
+ kfree(lmac->dmacs);
if ((lmac->lmac_type != BGX_MODE_XFI) &&
(lmac->lmac_type != BGX_MODE_XLAUI) &&
diff --git a/drivers/net/ethernet/cavium/thunder/thunder_bgx.h b/drivers/net/ethernet/cavium/thunder/thunder_bgx.h
index 5a7567d31138..cbdd20b9ee6f 100644
--- a/drivers/net/ethernet/cavium/thunder/thunder_bgx.h
+++ b/drivers/net/ethernet/cavium/thunder/thunder_bgx.h
@@ -30,6 +30,7 @@
#define DEFAULT_PAUSE_TIME 0xFFFF
#define BGX_ID_MASK 0x3
+#define LMAC_ID_MASK 0x3
#define MAX_DMAC_PER_LMAC_TNS_BYPASS_MODE 2
@@ -57,7 +58,7 @@
#define BGX_CMRX_RX_FIFO_LEN 0x108
#define BGX_CMR_RX_DMACX_CAM 0x200
#define RX_DMACX_CAM_EN BIT_ULL(48)
-#define RX_DMACX_CAM_LMACID(x) (x << 49)
+#define RX_DMACX_CAM_LMACID(x) (((u64)x) << 49)
#define RX_DMAC_COUNT 32
#define BGX_CMR_RX_STREERING 0x300
#define RX_TRAFFIC_STEER_RULE_COUNT 8
@@ -205,17 +206,13 @@
#define LMAC_INTR_LINK_UP BIT(0)
#define LMAC_INTR_LINK_DOWN BIT(1)
-/* RX_DMAC_CTL configuration*/
-enum MCAST_MODE {
- MCAST_MODE_REJECT,
- MCAST_MODE_ACCEPT,
- MCAST_MODE_CAM_FILTER,
- RSVD
-};
-
-#define BCAST_ACCEPT 1
-#define CAM_ACCEPT 1
+#define BGX_XCAST_BCAST_ACCEPT BIT(0)
+#define BGX_XCAST_MCAST_ACCEPT BIT(1)
+#define BGX_XCAST_MCAST_FILTER BIT(2)
+void bgx_set_dmac_cam_filter(int node, int bgx_idx, int lmacid, u64 mac, u8 vf);
+void bgx_reset_xcast_mode(int node, int bgx_idx, int lmacid, u8 vf);
+void bgx_set_xcast_mode(int node, int bgx_idx, int lmacid, u8 mode);
void octeon_mdiobus_force_mod_depencency(void);
void bgx_lmac_rx_tx_enable(int node, int bgx_idx, int lmacid, bool enable);
void bgx_add_dmac_addr(u64 dmac, int node, int bgx_idx, int lmac);
diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c
index 185fe8df7628..2edfdbdaae48 100644
--- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c
@@ -776,11 +776,11 @@ static ssize_t store_nservers(struct device *d, struct device_attribute *attr,
#define CXGB3_ATTR_R(name, val_expr) \
CXGB3_SHOW(name, val_expr) \
-static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL)
+static DEVICE_ATTR(name, 0444, show_##name, NULL)
#define CXGB3_ATTR_RW(name, val_expr, store_method) \
CXGB3_SHOW(name, val_expr) \
-static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, show_##name, store_method)
+static DEVICE_ATTR(name, 0644, show_##name, store_method)
CXGB3_ATTR_R(cam_size, t3_mc5_size(&adap->mc5));
CXGB3_ATTR_RW(nfilters, adap->params.mc5.nfilters, store_nfilters);
@@ -859,7 +859,7 @@ static ssize_t store_##name(struct device *d, struct device_attribute *attr, \
{ \
return tm_attr_store(d, buf, len, sched); \
} \
-static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, show_##name, store_##name)
+static DEVICE_ATTR(name, 0644, show_##name, store_##name)
TM_ATTR(sched0, 0);
TM_ATTR(sched1, 1);
diff --git a/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c b/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c
index a89721fad633..080918af773c 100644
--- a/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c
@@ -681,18 +681,18 @@ int t3_seeprom_wp(struct adapter *adapter, int enable)
return t3_seeprom_write(adapter, EEPROM_STAT_ADDR, enable ? 0xc : 0);
}
-static int vpdstrtouint(char *s, int len, unsigned int base, unsigned int *val)
+static int vpdstrtouint(char *s, u8 len, unsigned int base, unsigned int *val)
{
- char tok[len + 1];
+ char tok[256];
memcpy(tok, s, len);
tok[len] = 0;
return kstrtouint(strim(tok), base, val);
}
-static int vpdstrtou16(char *s, int len, unsigned int base, u16 *val)
+static int vpdstrtou16(char *s, u8 len, unsigned int base, u16 *val)
{
- char tok[len + 1];
+ char tok[256];
memcpy(tok, s, len);
tok[len] = 0;
diff --git a/drivers/net/ethernet/chelsio/cxgb4/Makefile b/drivers/net/ethernet/chelsio/cxgb4/Makefile
index 53b6a02c778e..bea6a059a8f1 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/Makefile
+++ b/drivers/net/ethernet/chelsio/cxgb4/Makefile
@@ -6,7 +6,7 @@
obj-$(CONFIG_CHELSIO_T4) += cxgb4.o
cxgb4-objs := cxgb4_main.o l2t.o smt.o t4_hw.o sge.o clip_tbl.o cxgb4_ethtool.o \
- cxgb4_uld.o sched.o cxgb4_filter.o cxgb4_tc_u32.o \
+ cxgb4_uld.o srq.o sched.o cxgb4_filter.o cxgb4_tc_u32.o \
cxgb4_ptp.o cxgb4_tc_flower.o cxgb4_cudbg.o \
cudbg_common.o cudbg_lib.o cudbg_zlib.o
cxgb4-$(CONFIG_CHELSIO_T4_DCB) += cxgb4_dcb.o
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.c b/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.c
index 00a1d2d13169..9da6f57901a9 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.c
@@ -878,6 +878,86 @@ static int cudbg_get_payload_range(struct adapter *padap, u8 mem_type,
&payload->start, &payload->end);
}
+static int cudbg_memory_read(struct cudbg_init *pdbg_init, int win,
+ int mtype, u32 addr, u32 len, void *hbuf)
+{
+ u32 win_pf, memoffset, mem_aperture, mem_base;
+ struct adapter *adap = pdbg_init->adap;
+ u32 pos, offset, resid;
+ u32 *res_buf;
+ u64 *buf;
+ int ret;
+
+ /* Argument sanity checks ...
+ */
+ if (addr & 0x3 || (uintptr_t)hbuf & 0x3)
+ return -EINVAL;
+
+ buf = (u64 *)hbuf;
+
+ /* Try to do 64-bit reads. Residual will be handled later. */
+ resid = len & 0x7;
+ len -= resid;
+
+ ret = t4_memory_rw_init(adap, win, mtype, &memoffset, &mem_base,
+ &mem_aperture);
+ if (ret)
+ return ret;
+
+ addr = addr + memoffset;
+ win_pf = is_t4(adap->params.chip) ? 0 : PFNUM_V(adap->pf);
+
+ pos = addr & ~(mem_aperture - 1);
+ offset = addr - pos;
+
+ /* Set up initial PCI-E Memory Window to cover the start of our
+ * transfer.
+ */
+ t4_memory_update_win(adap, win, pos | win_pf);
+
+ /* Transfer data from the adapter */
+ while (len > 0) {
+ *buf++ = le64_to_cpu((__force __le64)
+ t4_read_reg64(adap, mem_base + offset));
+ offset += sizeof(u64);
+ len -= sizeof(u64);
+
+ /* If we've reached the end of our current window aperture,
+ * move the PCI-E Memory Window on to the next.
+ */
+ if (offset == mem_aperture) {
+ pos += mem_aperture;
+ offset = 0;
+ t4_memory_update_win(adap, win, pos | win_pf);
+ }
+ }
+
+ res_buf = (u32 *)buf;
+ /* Read residual in 32-bit multiples */
+ while (resid > sizeof(u32)) {
+ *res_buf++ = le32_to_cpu((__force __le32)
+ t4_read_reg(adap, mem_base + offset));
+ offset += sizeof(u32);
+ resid -= sizeof(u32);
+
+ /* If we've reached the end of our current window aperture,
+ * move the PCI-E Memory Window on to the next.
+ */
+ if (offset == mem_aperture) {
+ pos += mem_aperture;
+ offset = 0;
+ t4_memory_update_win(adap, win, pos | win_pf);
+ }
+ }
+
+ /* Transfer residual < 32-bits */
+ if (resid)
+ t4_memory_rw_residual(adap, resid, mem_base + offset,
+ (u8 *)res_buf, T4_MEMORY_READ);
+
+ return 0;
+}
+
#define CUDBG_YIELD_ITERATION 256
static int cudbg_read_fw_mem(struct cudbg_init *pdbg_init,
@@ -937,10 +1017,8 @@ static int cudbg_read_fw_mem(struct cudbg_init *pdbg_init,
goto skip_read;
spin_lock(&padap->win0_lock);
- rc = t4_memory_rw(padap, MEMWIN_NIC, mem_type,
- bytes_read, bytes,
- (__be32 *)temp_buff.data,
- 1);
+ rc = cudbg_memory_read(pdbg_init, MEMWIN_NIC, mem_type,
+ bytes_read, bytes, temp_buff.data);
spin_unlock(&padap->win0_lock);
if (rc) {
cudbg_err->sys_err = rc;
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
index 9040e13ce4b7..688f95440af2 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
@@ -390,6 +390,8 @@ struct adapter_params {
* used by the Port
*/
u8 mps_bg_map[MAX_NPORTS]; /* MPS Buffer Group Map */
+ bool write_w_imm_support; /* FW supports WRITE_WITH_IMMEDIATE */
+ bool write_cmpl_support; /* FW supports WRITE_CMPL */
};
/* State needed to monitor the forward progress of SGE Ingress DMA activities
@@ -831,6 +833,16 @@ struct vf_info {
u16 vlan;
};
+enum {
+ HMA_DMA_MAPPED_FLAG = 1
+};
+
+struct hma_data {
+ unsigned char flags;
+ struct sg_table *sgt;
+ dma_addr_t *phy_addr; /* physical address of the page */
+};
+
struct mbox_list {
struct list_head list;
};
@@ -907,6 +919,7 @@ struct adapter {
struct work_struct tid_release_task;
struct work_struct db_full_task;
struct work_struct db_drop_task;
+ struct work_struct fatal_err_notify_task;
bool tid_release_task_busy;
/* lock for mailbox cmd list */
@@ -946,6 +959,11 @@ struct adapter {
/* Ethtool Dump */
struct ethtool_dump eth_dump;
+
+ /* HMA */
+ struct hma_data hma;
+
+ struct srq_data *srq;
};
/* Support for "sched-class" command to allow a TX Scheduling Class to be
@@ -1488,6 +1506,11 @@ u32 t4_read_pcie_cfg4(struct adapter *adap, int reg);
u32 t4_get_util_window(struct adapter *adap);
void t4_setup_memwin(struct adapter *adap, u32 memwin_base, u32 window);
+int t4_memory_rw_init(struct adapter *adap, int win, int mtype, u32 *mem_off,
+ u32 *mem_base, u32 *mem_aperture);
+void t4_memory_update_win(struct adapter *adap, int win, u32 addr);
+void t4_memory_rw_residual(struct adapter *adap, u32 off, u32 addr, u8 *buf,
+ int dir);
#define T4_MEMORY_WRITE 0
#define T4_MEMORY_READ 1
int t4_memory_rw(struct adapter *adap, int win, int mtype, u32 addr, u32 len,
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
index 2822bbff73e8..251d5bdc972f 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
@@ -2617,7 +2617,7 @@ int mem_open(struct inode *inode, struct file *file)
file->private_data = inode->i_private;
- mem = (uintptr_t)file->private_data & 0x3;
+ mem = (uintptr_t)file->private_data & 0x7;
adap = file->private_data - mem;
(void)t4_fwcache(adap, FW_PARAM_DEV_FWCACHE_FLUSH);
@@ -2630,7 +2630,7 @@ static ssize_t mem_read(struct file *file, char __user *buf, size_t count,
{
loff_t pos = *ppos;
loff_t avail = file_inode(file)->i_size;
- unsigned int mem = (uintptr_t)file->private_data & 3;
+ unsigned int mem = (uintptr_t)file->private_data & 0x7;
struct adapter *adap = file->private_data - mem;
__be32 *data;
int ret;
@@ -2752,7 +2752,7 @@ DEFINE_SIMPLE_DEBUGFS_FILE(tid_info);
static void add_debugfs_mem(struct adapter *adap, const char *name,
unsigned int idx, unsigned int size_mb)
{
- debugfs_create_file_size(name, S_IRUSR, adap->debugfs_root,
+ debugfs_create_file_size(name, 0400, adap->debugfs_root,
(void *)adap + idx, &mem_debugfs_fops,
size_mb << 20);
}
@@ -2947,65 +2947,65 @@ int t4_setup_debugfs(struct adapter *adap)
struct dentry *de;
static struct t4_debugfs_entry t4_debugfs_files[] = {
- { "cim_la", &cim_la_fops, S_IRUSR, 0 },
- { "cim_pif_la", &cim_pif_la_fops, S_IRUSR, 0 },
- { "cim_ma_la", &cim_ma_la_fops, S_IRUSR, 0 },
- { "cim_qcfg", &cim_qcfg_fops, S_IRUSR, 0 },
- { "clk", &clk_debugfs_fops, S_IRUSR, 0 },
- { "devlog", &devlog_fops, S_IRUSR, 0 },
- { "mboxlog", &mboxlog_fops, S_IRUSR, 0 },
- { "mbox0", &mbox_debugfs_fops, S_IRUSR | S_IWUSR, 0 },
- { "mbox1", &mbox_debugfs_fops, S_IRUSR | S_IWUSR, 1 },
- { "mbox2", &mbox_debugfs_fops, S_IRUSR | S_IWUSR, 2 },
- { "mbox3", &mbox_debugfs_fops, S_IRUSR | S_IWUSR, 3 },
- { "mbox4", &mbox_debugfs_fops, S_IRUSR | S_IWUSR, 4 },
- { "mbox5", &mbox_debugfs_fops, S_IRUSR | S_IWUSR, 5 },
- { "mbox6", &mbox_debugfs_fops, S_IRUSR | S_IWUSR, 6 },
- { "mbox7", &mbox_debugfs_fops, S_IRUSR | S_IWUSR, 7 },
- { "trace0", &mps_trc_debugfs_fops, S_IRUSR | S_IWUSR, 0 },
- { "trace1", &mps_trc_debugfs_fops, S_IRUSR | S_IWUSR, 1 },
- { "trace2", &mps_trc_debugfs_fops, S_IRUSR | S_IWUSR, 2 },
- { "trace3", &mps_trc_debugfs_fops, S_IRUSR | S_IWUSR, 3 },
- { "l2t", &t4_l2t_fops, S_IRUSR, 0},
- { "mps_tcam", &mps_tcam_debugfs_fops, S_IRUSR, 0 },
- { "rss", &rss_debugfs_fops, S_IRUSR, 0 },
- { "rss_config", &rss_config_debugfs_fops, S_IRUSR, 0 },
- { "rss_key", &rss_key_debugfs_fops, S_IRUSR, 0 },
- { "rss_pf_config", &rss_pf_config_debugfs_fops, S_IRUSR, 0 },
- { "rss_vf_config", &rss_vf_config_debugfs_fops, S_IRUSR, 0 },
- { "sge_qinfo", &sge_qinfo_debugfs_fops, S_IRUSR, 0 },
- { "ibq_tp0", &cim_ibq_fops, S_IRUSR, 0 },
- { "ibq_tp1", &cim_ibq_fops, S_IRUSR, 1 },
- { "ibq_ulp", &cim_ibq_fops, S_IRUSR, 2 },
- { "ibq_sge0", &cim_ibq_fops, S_IRUSR, 3 },
- { "ibq_sge1", &cim_ibq_fops, S_IRUSR, 4 },
- { "ibq_ncsi", &cim_ibq_fops, S_IRUSR, 5 },
- { "obq_ulp0", &cim_obq_fops, S_IRUSR, 0 },
- { "obq_ulp1", &cim_obq_fops, S_IRUSR, 1 },
- { "obq_ulp2", &cim_obq_fops, S_IRUSR, 2 },
- { "obq_ulp3", &cim_obq_fops, S_IRUSR, 3 },
- { "obq_sge", &cim_obq_fops, S_IRUSR, 4 },
- { "obq_ncsi", &cim_obq_fops, S_IRUSR, 5 },
- { "tp_la", &tp_la_fops, S_IRUSR, 0 },
- { "ulprx_la", &ulprx_la_fops, S_IRUSR, 0 },
- { "sensors", &sensors_debugfs_fops, S_IRUSR, 0 },
- { "pm_stats", &pm_stats_debugfs_fops, S_IRUSR, 0 },
- { "tx_rate", &tx_rate_debugfs_fops, S_IRUSR, 0 },
- { "cctrl", &cctrl_tbl_debugfs_fops, S_IRUSR, 0 },
+ { "cim_la", &cim_la_fops, 0400, 0 },
+ { "cim_pif_la", &cim_pif_la_fops, 0400, 0 },
+ { "cim_ma_la", &cim_ma_la_fops, 0400, 0 },
+ { "cim_qcfg", &cim_qcfg_fops, 0400, 0 },
+ { "clk", &clk_debugfs_fops, 0400, 0 },
+ { "devlog", &devlog_fops, 0400, 0 },
+ { "mboxlog", &mboxlog_fops, 0400, 0 },
+ { "mbox0", &mbox_debugfs_fops, 0600, 0 },
+ { "mbox1", &mbox_debugfs_fops, 0600, 1 },
+ { "mbox2", &mbox_debugfs_fops, 0600, 2 },
+ { "mbox3", &mbox_debugfs_fops, 0600, 3 },
+ { "mbox4", &mbox_debugfs_fops, 0600, 4 },
+ { "mbox5", &mbox_debugfs_fops, 0600, 5 },
+ { "mbox6", &mbox_debugfs_fops, 0600, 6 },
+ { "mbox7", &mbox_debugfs_fops, 0600, 7 },
+ { "trace0", &mps_trc_debugfs_fops, 0600, 0 },
+ { "trace1", &mps_trc_debugfs_fops, 0600, 1 },
+ { "trace2", &mps_trc_debugfs_fops, 0600, 2 },
+ { "trace3", &mps_trc_debugfs_fops, 0600, 3 },
+ { "l2t", &t4_l2t_fops, 0400, 0},
+ { "mps_tcam", &mps_tcam_debugfs_fops, 0400, 0 },
+ { "rss", &rss_debugfs_fops, 0400, 0 },
+ { "rss_config", &rss_config_debugfs_fops, 0400, 0 },
+ { "rss_key", &rss_key_debugfs_fops, 0400, 0 },
+ { "rss_pf_config", &rss_pf_config_debugfs_fops, 0400, 0 },
+ { "rss_vf_config", &rss_vf_config_debugfs_fops, 0400, 0 },
+ { "sge_qinfo", &sge_qinfo_debugfs_fops, 0400, 0 },
+ { "ibq_tp0", &cim_ibq_fops, 0400, 0 },
+ { "ibq_tp1", &cim_ibq_fops, 0400, 1 },
+ { "ibq_ulp", &cim_ibq_fops, 0400, 2 },
+ { "ibq_sge0", &cim_ibq_fops, 0400, 3 },
+ { "ibq_sge1", &cim_ibq_fops, 0400, 4 },
+ { "ibq_ncsi", &cim_ibq_fops, 0400, 5 },
+ { "obq_ulp0", &cim_obq_fops, 0400, 0 },
+ { "obq_ulp1", &cim_obq_fops, 0400, 1 },
+ { "obq_ulp2", &cim_obq_fops, 0400, 2 },
+ { "obq_ulp3", &cim_obq_fops, 0400, 3 },
+ { "obq_sge", &cim_obq_fops, 0400, 4 },
+ { "obq_ncsi", &cim_obq_fops, 0400, 5 },
+ { "tp_la", &tp_la_fops, 0400, 0 },
+ { "ulprx_la", &ulprx_la_fops, 0400, 0 },
+ { "sensors", &sensors_debugfs_fops, 0400, 0 },
+ { "pm_stats", &pm_stats_debugfs_fops, 0400, 0 },
+ { "tx_rate", &tx_rate_debugfs_fops, 0400, 0 },
+ { "cctrl", &cctrl_tbl_debugfs_fops, 0400, 0 },
#if IS_ENABLED(CONFIG_IPV6)
- { "clip_tbl", &clip_tbl_debugfs_fops, S_IRUSR, 0 },
+ { "clip_tbl", &clip_tbl_debugfs_fops, 0400, 0 },
#endif
- { "tids", &tid_info_debugfs_fops, S_IRUSR, 0},
- { "blocked_fl", &blocked_fl_fops, S_IRUSR | S_IWUSR, 0 },
- { "meminfo", &meminfo_fops, S_IRUSR, 0 },
- { "crypto", &chcr_stats_debugfs_fops, S_IRUSR, 0 },
+ { "tids", &tid_info_debugfs_fops, 0400, 0},
+ { "blocked_fl", &blocked_fl_fops, 0600, 0 },
+ { "meminfo", &meminfo_fops, 0400, 0 },
+ { "crypto", &chcr_stats_debugfs_fops, 0400, 0 },
};
/* Debug FS nodes common to all T5 and later adapters.
*/
static struct t4_debugfs_entry t5_debugfs_files[] = {
- { "obq_sge_rx_q0", &cim_obq_fops, S_IRUSR, 6 },
- { "obq_sge_rx_q1", &cim_obq_fops, S_IRUSR, 7 },
+ { "obq_sge_rx_q0", &cim_obq_fops, 0400, 6 },
+ { "obq_sge_rx_q1", &cim_obq_fops, 0400, 7 },
};
add_debugfs_files(adap,
@@ -3042,13 +3042,19 @@ int t4_setup_debugfs(struct adapter *adap)
add_debugfs_mem(adap, "mc", MEM_MC,
EXT_MEM_SIZE_G(size));
}
+
+ if (i & HMA_MUX_F) {
+ size = t4_read_reg(adap, MA_EXT_MEMORY1_BAR_A);
+ add_debugfs_mem(adap, "hma", MEM_HMA,
+ EXT_MEM1_SIZE_G(size));
+ }
}
- de = debugfs_create_file_size("flash", S_IRUSR, adap->debugfs_root, adap,
+ de = debugfs_create_file_size("flash", 0400, adap->debugfs_root, adap,
&flash_debugfs_fops, adap->params.sf_size);
- debugfs_create_bool("use_backdoor", S_IWUSR | S_IRUSR,
+ debugfs_create_bool("use_backdoor", 0600,
adap->debugfs_root, &adap->use_bd);
- debugfs_create_bool("trace_rss", S_IWUSR | S_IRUSR,
+ debugfs_create_bool("trace_rss", 0600,
adap->debugfs_root, &adap->trace_rss);
return 0;
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c
index 7852d98bad75..59d04d73c672 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c
@@ -597,22 +597,22 @@ static void fw_caps_to_lmm(enum fw_port_type port_type,
case FW_PORT_TYPE_KR:
SET_LMM(Backplane);
- SET_LMM(10000baseKR_Full);
+ FW_CAPS_TO_LMM(SPEED_10G, 10000baseKR_Full);
break;
case FW_PORT_TYPE_BP_AP:
SET_LMM(Backplane);
- SET_LMM(10000baseR_FEC);
- SET_LMM(10000baseKR_Full);
- SET_LMM(1000baseKX_Full);
+ FW_CAPS_TO_LMM(SPEED_1G, 1000baseKX_Full);
+ FW_CAPS_TO_LMM(SPEED_10G, 10000baseR_FEC);
+ FW_CAPS_TO_LMM(SPEED_10G, 10000baseKR_Full);
break;
case FW_PORT_TYPE_BP4_AP:
SET_LMM(Backplane);
- SET_LMM(10000baseR_FEC);
- SET_LMM(10000baseKR_Full);
- SET_LMM(1000baseKX_Full);
- SET_LMM(10000baseKX4_Full);
+ FW_CAPS_TO_LMM(SPEED_1G, 1000baseKX_Full);
+ FW_CAPS_TO_LMM(SPEED_10G, 10000baseR_FEC);
+ FW_CAPS_TO_LMM(SPEED_10G, 10000baseKR_Full);
+ FW_CAPS_TO_LMM(SPEED_10G, 10000baseKX4_Full);
break;
case FW_PORT_TYPE_FIBER_XFI:
@@ -628,7 +628,9 @@ static void fw_caps_to_lmm(enum fw_port_type port_type,
case FW_PORT_TYPE_BP40_BA:
case FW_PORT_TYPE_QSFP:
SET_LMM(FIBRE);
- SET_LMM(40000baseSR4_Full);
+ FW_CAPS_TO_LMM(SPEED_1G, 1000baseT_Full);
+ FW_CAPS_TO_LMM(SPEED_10G, 10000baseT_Full);
+ FW_CAPS_TO_LMM(SPEED_40G, 40000baseSR4_Full);
break;
case FW_PORT_TYPE_CR_QSFP:
@@ -655,12 +657,14 @@ static void fw_caps_to_lmm(enum fw_port_type port_type,
case FW_PORT_TYPE_CR2_QSFP:
SET_LMM(FIBRE);
- SET_LMM(50000baseSR2_Full);
+ FW_CAPS_TO_LMM(SPEED_50G, 50000baseSR2_Full);
break;
case FW_PORT_TYPE_KR4_100G:
case FW_PORT_TYPE_CR4_QSFP:
SET_LMM(FIBRE);
+ FW_CAPS_TO_LMM(SPEED_1G, 1000baseT_Full);
+ FW_CAPS_TO_LMM(SPEED_10G, 10000baseSR_Full);
FW_CAPS_TO_LMM(SPEED_40G, 40000baseSR4_Full);
FW_CAPS_TO_LMM(SPEED_25G, 25000baseCR_Full);
FW_CAPS_TO_LMM(SPEED_50G, 50000baseCR2_Full);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
index 3177b0c9bd2d..db92f1858060 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
@@ -1335,12 +1335,6 @@ int __cxgb4_set_filter(struct net_device *dev, int filter_id,
return ret;
}
- /* Clear out any old resources being used by the filter before
- * we start constructing the new filter.
- */
- if (f->valid)
- clear_filter(adapter, f);
-
if (is_t6(adapter->params.chip) && fs->type &&
ipv6_addr_type((const struct in6_addr *)fs->val.lip) !=
IPV6_ADDR_ANY) {
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index 61022b5f6743..24d2865b8806 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -75,6 +75,7 @@
#include "t4fw_api.h"
#include "t4fw_version.h"
#include "cxgb4_dcb.h"
+#include "srq.h"
#include "cxgb4_debugfs.h"
#include "clip_tbl.h"
#include "l2t.h"
@@ -210,6 +211,9 @@ static void link_report(struct net_device *dev)
case 40000:
s = "40Gbps";
break;
+ case 50000:
+ s = "50Gbps";
+ break;
case 100000:
s = "100Gbps";
break;
@@ -583,6 +587,10 @@ static int fwevtq_handler(struct sge_rspq *q, const __be64 *rsp,
const struct cpl_abort_rpl_rss *p = (void *)rsp;
hash_del_filter_rpl(q->adap, p);
+ } else if (opcode == CPL_SRQ_TABLE_RPL) {
+ const struct cpl_srq_table_rpl *p = (void *)rsp;
+
+ do_srq_table_rpl(q->adap, p);
} else
dev_err(q->adap->pdev_dev,
"unexpected CPL %#x on FW event queue\n", opcode);
@@ -833,8 +841,6 @@ static int setup_fw_sge_queues(struct adapter *adap)
err = t4_sge_alloc_rxq(adap, &s->fw_evtq, true, adap->port[0],
adap->msi_idx, NULL, fwevtq_handler, NULL, -1);
- if (err)
- t4_free_sge_resources(adap);
return err;
}
@@ -1733,10 +1739,11 @@ EXPORT_SYMBOL(cxgb4_sync_txq_pidx);
int cxgb4_read_tpte(struct net_device *dev, u32 stag, __be32 *tpte)
{
- struct adapter *adap;
- u32 offset, memtype, memaddr;
u32 edc0_size, edc1_size, mc0_size, mc1_size, size;
u32 edc0_end, edc1_end, mc0_end, mc1_end;
+ u32 offset, memtype, memaddr;
+ struct adapter *adap;
+ u32 hma_size = 0;
int ret;
adap = netdev2adap(dev);
@@ -1756,6 +1763,10 @@ int cxgb4_read_tpte(struct net_device *dev, u32 stag, __be32 *tpte)
size = t4_read_reg(adap, MA_EXT_MEMORY0_BAR_A);
mc0_size = EXT_MEM0_SIZE_G(size) << 20;
+ if (t4_read_reg(adap, MA_TARGET_MEM_ENABLE_A) & HMA_MUX_F) {
+ size = t4_read_reg(adap, MA_EXT_MEMORY1_BAR_A);
+ hma_size = EXT_MEM1_SIZE_G(size) << 20;
+ }
edc0_end = edc0_size;
edc1_end = edc0_end + edc1_size;
mc0_end = edc1_end + mc0_size;
@@ -1767,7 +1778,10 @@ int cxgb4_read_tpte(struct net_device *dev, u32 stag, __be32 *tpte)
memtype = MEM_EDC1;
memaddr = offset - edc0_end;
} else {
- if (offset < mc0_end) {
+ if (hma_size && (offset < (edc1_end + hma_size))) {
+ memtype = MEM_HMA;
+ memaddr = offset - edc1_end;
+ } else if (offset < mc0_end) {
memtype = MEM_MC0;
memaddr = offset - edc1_end;
} else if (is_t5(adap->params.chip)) {
@@ -2681,13 +2695,17 @@ static int cxgb4_mgmt_get_vf_config(struct net_device *dev,
{
struct port_info *pi = netdev_priv(dev);
struct adapter *adap = pi->adapter;
+ struct vf_info *vfinfo;
if (vf >= adap->num_vfs)
return -EINVAL;
+ vfinfo = &adap->vfinfo[vf];
+
ivi->vf = vf;
- ivi->max_tx_rate = adap->vfinfo[vf].tx_rate;
+ ivi->max_tx_rate = vfinfo->tx_rate;
ivi->min_tx_rate = 0;
- ether_addr_copy(ivi->mac, adap->vfinfo[vf].vf_mac_addr);
+ ether_addr_copy(ivi->mac, vfinfo->vf_mac_addr);
+ ivi->vlan = vfinfo->vlan;
return 0;
}
@@ -2870,11 +2888,11 @@ static int cxgb_set_tx_maxrate(struct net_device *dev, int index, u32 rate)
/* Convert from Mbps to Kbps */
req_rate = rate << 10;
- /* Max rate is 10 Gbps */
+ /* Max rate is 100 Gbps */
if (req_rate >= SCHED_MAX_RATE_KBPS) {
dev_err(adap->pdev_dev,
- "Invalid rate %u Mbps, Max rate is %u Gbps\n",
- rate, SCHED_MAX_RATE_KBPS);
+ "Invalid rate %u Mbps, Max rate is %u Mbps\n",
+ rate, SCHED_MAX_RATE_KBPS >> 10);
return -ERANGE;
}
@@ -3244,6 +3262,14 @@ static const struct ethtool_ops cxgb4_mgmt_ethtool_ops = {
.get_drvinfo = cxgb4_mgmt_get_drvinfo,
};
+static void notify_fatal_err(struct work_struct *work)
+{
+ struct adapter *adap;
+
+ adap = container_of(work, struct adapter, fatal_err_notify_task);
+ notify_ulds(adap, CXGB4_STATE_FATAL_ERROR);
+}
+
void t4_fatal_err(struct adapter *adap)
{
int port;
@@ -3268,6 +3294,7 @@ void t4_fatal_err(struct adapter *adap)
netif_carrier_off(dev);
}
dev_alert(adap->pdev_dev, "encountered fatal error, adapter stopped\n");
+ queue_work(adap->workq, &adap->fatal_err_notify_task);
}
static void setup_memwin(struct adapter *adap)
@@ -3298,6 +3325,206 @@ static void setup_memwin_rdma(struct adapter *adap)
}
}
+/* HMA Definitions */
+
+/* The maximum number of address that can be send in a single FW cmd */
+#define HMA_MAX_ADDR_IN_CMD 5
+
+#define HMA_PAGE_SIZE PAGE_SIZE
+
+#define HMA_MAX_NO_FW_ADDRESS (16 << 10) /* FW supports 16K addresses */
+
+#define HMA_PAGE_ORDER \
+ ((HMA_PAGE_SIZE < HMA_MAX_NO_FW_ADDRESS) ? \
+ ilog2(HMA_MAX_NO_FW_ADDRESS / HMA_PAGE_SIZE) : 0)
+
+/* The minimum and maximum possible HMA sizes that can be specified in the FW
+ * configuration(in units of MB).
+ */
+#define HMA_MIN_TOTAL_SIZE 1
+#define HMA_MAX_TOTAL_SIZE \
+ (((HMA_PAGE_SIZE << HMA_PAGE_ORDER) * \
+ HMA_MAX_NO_FW_ADDRESS) >> 20)
+
+static void adap_free_hma_mem(struct adapter *adapter)
+{
+ struct scatterlist *iter;
+ struct page *page;
+ int i;
+
+ if (!adapter->hma.sgt)
+ return;
+
+ if (adapter->hma.flags & HMA_DMA_MAPPED_FLAG) {
+ dma_unmap_sg(adapter->pdev_dev, adapter->hma.sgt->sgl,
+ adapter->hma.sgt->nents, PCI_DMA_BIDIRECTIONAL);
+ adapter->hma.flags &= ~HMA_DMA_MAPPED_FLAG;
+ }
+
+ for_each_sg(adapter->hma.sgt->sgl, iter,
+ adapter->hma.sgt->orig_nents, i) {
+ page = sg_page(iter);
+ if (page)
+ __free_pages(page, HMA_PAGE_ORDER);
+ }
+
+ kfree(adapter->hma.phy_addr);
+ sg_free_table(adapter->hma.sgt);
+ kfree(adapter->hma.sgt);
+ adapter->hma.sgt = NULL;
+}
+
+static int adap_config_hma(struct adapter *adapter)
+{
+ struct scatterlist *sgl, *iter;
+ struct sg_table *sgt;
+ struct page *newpage;
+ unsigned int i, j, k;
+ u32 param, hma_size;
+ unsigned int ncmds;
+ size_t page_size;
+ u32 page_order;
+ int node, ret;
+
+ /* HMA is supported only for T6+ cards.
+ * Avoid initializing HMA in kdump kernels.
+ */
+ if (is_kdump_kernel() ||
+ CHELSIO_CHIP_VERSION(adapter->params.chip) < CHELSIO_T6)
+ return 0;
+
+ /* Get the HMA region size required by fw */
+ param = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) |
+ FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_HMA_SIZE));
+ ret = t4_query_params(adapter, adapter->mbox, adapter->pf, 0,
+ 1, &param, &hma_size);
+ /* An error means card has its own memory or HMA is not supported by
+ * the firmware. Return without any errors.
+ */
+ if (ret || !hma_size)
+ return 0;
+
+ if (hma_size < HMA_MIN_TOTAL_SIZE ||
+ hma_size > HMA_MAX_TOTAL_SIZE) {
+ dev_err(adapter->pdev_dev,
+ "HMA size %uMB beyond bounds(%u-%lu)MB\n",
+ hma_size, HMA_MIN_TOTAL_SIZE, HMA_MAX_TOTAL_SIZE);
+ return -EINVAL;
+ }
+
+ page_size = HMA_PAGE_SIZE;
+ page_order = HMA_PAGE_ORDER;
+ adapter->hma.sgt = kzalloc(sizeof(*adapter->hma.sgt), GFP_KERNEL);
+ if (unlikely(!adapter->hma.sgt)) {
+ dev_err(adapter->pdev_dev, "HMA SG table allocation failed\n");
+ return -ENOMEM;
+ }
+ sgt = adapter->hma.sgt;
+ /* FW returned value will be in MB's
+ */
+ sgt->orig_nents = (hma_size << 20) / (page_size << page_order);
+ if (sg_alloc_table(sgt, sgt->orig_nents, GFP_KERNEL)) {
+ dev_err(adapter->pdev_dev, "HMA SGL allocation failed\n");
+ kfree(adapter->hma.sgt);
+ adapter->hma.sgt = NULL;
+ return -ENOMEM;
+ }
+
+ sgl = adapter->hma.sgt->sgl;
+ node = dev_to_node(adapter->pdev_dev);
+ for_each_sg(sgl, iter, sgt->orig_nents, i) {
+ newpage = alloc_pages_node(node, __GFP_NOWARN | GFP_KERNEL,
+ page_order);
+ if (!newpage) {
+ dev_err(adapter->pdev_dev,
+ "Not enough memory for HMA page allocation\n");
+ ret = -ENOMEM;
+ goto free_hma;
+ }
+ sg_set_page(iter, newpage, page_size << page_order, 0);
+ }
+
+ sgt->nents = dma_map_sg(adapter->pdev_dev, sgl, sgt->orig_nents,
+ DMA_BIDIRECTIONAL);
+ if (!sgt->nents) {
+ dev_err(adapter->pdev_dev,
+ "Not enough memory for HMA DMA mapping");
+ ret = -ENOMEM;
+ goto free_hma;
+ }
+ adapter->hma.flags |= HMA_DMA_MAPPED_FLAG;
+
+ adapter->hma.phy_addr = kcalloc(sgt->nents, sizeof(dma_addr_t),
+ GFP_KERNEL);
+ if (unlikely(!adapter->hma.phy_addr))
+ goto free_hma;
+
+ for_each_sg(sgl, iter, sgt->nents, i) {
+ newpage = sg_page(iter);
+ adapter->hma.phy_addr[i] = sg_dma_address(iter);
+ }
+
+ ncmds = DIV_ROUND_UP(sgt->nents, HMA_MAX_ADDR_IN_CMD);
+ /* Pass on the addresses to firmware */
+ for (i = 0, k = 0; i < ncmds; i++, k += HMA_MAX_ADDR_IN_CMD) {
+ struct fw_hma_cmd hma_cmd;
+ u8 naddr = HMA_MAX_ADDR_IN_CMD;
+ u8 soc = 0, eoc = 0;
+ u8 hma_mode = 1; /* Presently we support only Page table mode */
+
+ soc = (i == 0) ? 1 : 0;
+ eoc = (i == ncmds - 1) ? 1 : 0;
+
+ /* For last cmd, set naddr corresponding to remaining
+ * addresses
+ */
+ if (i == ncmds - 1) {
+ naddr = sgt->nents % HMA_MAX_ADDR_IN_CMD;
+ naddr = naddr ? naddr : HMA_MAX_ADDR_IN_CMD;
+ }
+ memset(&hma_cmd, 0, sizeof(hma_cmd));
+ hma_cmd.op_pkd = htonl(FW_CMD_OP_V(FW_HMA_CMD) |
+ FW_CMD_REQUEST_F | FW_CMD_WRITE_F);
+ hma_cmd.retval_len16 = htonl(FW_LEN16(hma_cmd));
+
+ hma_cmd.mode_to_pcie_params =
+ htonl(FW_HMA_CMD_MODE_V(hma_mode) |
+ FW_HMA_CMD_SOC_V(soc) | FW_HMA_CMD_EOC_V(eoc));
+
+ /* HMA cmd size specified in MB's */
+ hma_cmd.naddr_size =
+ htonl(FW_HMA_CMD_SIZE_V(hma_size) |
+ FW_HMA_CMD_NADDR_V(naddr));
+
+ /* Total Page size specified in units of 4K */
+ hma_cmd.addr_size_pkd =
+ htonl(FW_HMA_CMD_ADDR_SIZE_V
+ ((page_size << page_order) >> 12));
+
+ /* Fill the 5 addresses */
+ for (j = 0; j < naddr; j++) {
+ hma_cmd.phy_address[j] =
+ cpu_to_be64(adapter->hma.phy_addr[j + k]);
+ }
+ ret = t4_wr_mbox(adapter, adapter->mbox, &hma_cmd,
+ sizeof(hma_cmd), &hma_cmd);
+ if (ret) {
+ dev_err(adapter->pdev_dev,
+ "HMA FW command failed with err %d\n", ret);
+ goto free_hma;
+ }
+ }
+
+ if (!ret)
+ dev_info(adapter->pdev_dev,
+ "Reserved %uMB host memory for HMA\n", hma_size);
+ return ret;
+
+free_hma:
+ adap_free_hma_mem(adapter);
+ return ret;
+}
+
static int adap_init1(struct adapter *adap, struct fw_caps_config_cmd *c)
{
u32 v;
@@ -3751,6 +3978,12 @@ static int adap_init0_config(struct adapter *adapter, int reset)
if (ret < 0)
goto bye;
+ /* We will proceed even if HMA init fails. */
+ ret = adap_config_hma(adapter);
+ if (ret)
+ dev_err(adapter->pdev_dev,
+ "HMA configuration failed with error %d\n", ret);
+
/*
* And finally tell the firmware to initialize itself using the
* parameters from the Configuration File.
@@ -3957,6 +4190,11 @@ static int adap_init0(struct adapter *adap)
* effect. Otherwise, it's time to try initializing the adapter.
*/
if (state == DEV_STATE_INIT) {
+ ret = adap_config_hma(adap);
+ if (ret)
+ dev_err(adap->pdev_dev,
+ "HMA configuration failed with error %d\n",
+ ret);
dev_info(adap->pdev_dev, "Coming up as %s: "\
"Adapter already initialized\n",
adap->flags & MASTER_PF ? "MASTER" : "SLAVE");
@@ -4211,7 +4449,8 @@ static int adap_init0(struct adapter *adap)
adap->params.ofldq_wr_cred = val[5];
if (caps_cmd.niccaps & htons(FW_CAPS_CONFIG_NIC_HASHFILTER)) {
- if (init_hash_filter(adap) < 0)
+ ret = init_hash_filter(adap);
+ if (ret < 0)
goto bye;
} else {
adap->params.offload = 1;
@@ -4236,6 +4475,20 @@ static int adap_init0(struct adapter *adap)
adap->vres.pbl.start = val[4];
adap->vres.pbl.size = val[5] - val[4] + 1;
+ params[0] = FW_PARAM_PFVF(SRQ_START);
+ params[1] = FW_PARAM_PFVF(SRQ_END);
+ ret = t4_query_params(adap, adap->mbox, adap->pf, 0, 2,
+ params, val);
+ if (!ret) {
+ adap->vres.srq.start = val[0];
+ adap->vres.srq.size = val[1] - val[0] + 1;
+ }
+ if (adap->vres.srq.size) {
+ adap->srq = t4_init_srq(adap->vres.srq.size);
+ if (!adap->srq)
+ dev_warn(&adap->pdev->dev, "could not allocate SRQ, continuing\n");
+ }
+
params[0] = FW_PARAM_PFVF(SQRQ_START);
params[1] = FW_PARAM_PFVF(SQRQ_END);
params[2] = FW_PARAM_PFVF(CQ_START);
@@ -4269,6 +4522,18 @@ static int adap_init0(struct adapter *adap)
"max_ordird_qp %d max_ird_adapter %d\n",
adap->params.max_ordird_qp,
adap->params.max_ird_adapter);
+
+ /* Enable write_with_immediate if FW supports it */
+ params[0] = FW_PARAM_DEV(RDMA_WRITE_WITH_IMM);
+ ret = t4_query_params(adap, adap->mbox, adap->pf, 0, 1, params,
+ val);
+ adap->params.write_w_imm_support = (ret == 0 && val[0] != 0);
+
+ /* Enable write_cmpl if FW supports it */
+ params[0] = FW_PARAM_DEV(RI_WRITE_CMPL_WR);
+ ret = t4_query_params(adap, adap->mbox, adap->pf, 0, 1, params,
+ val);
+ adap->params.write_cmpl_support = (ret == 0 && val[0] != 0);
adap->num_ofld_uld += 2;
}
if (caps_cmd.iscsicaps) {
@@ -4284,18 +4549,32 @@ static int adap_init0(struct adapter *adap)
adap->num_ofld_uld += 2;
}
if (caps_cmd.cryptocaps) {
- /* Should query params here...TODO */
- params[0] = FW_PARAM_PFVF(NCRYPTO_LOOKASIDE);
- ret = t4_query_params(adap, adap->mbox, adap->pf, 0, 2,
- params, val);
- if (ret < 0) {
- if (ret != -EINVAL)
+ if (ntohs(caps_cmd.cryptocaps) &
+ FW_CAPS_CONFIG_CRYPTO_LOOKASIDE) {
+ params[0] = FW_PARAM_PFVF(NCRYPTO_LOOKASIDE);
+ ret = t4_query_params(adap, adap->mbox, adap->pf, 0,
+ 2, params, val);
+ if (ret < 0) {
+ if (ret != -EINVAL)
+ goto bye;
+ } else {
+ adap->vres.ncrypto_fc = val[0];
+ }
+ adap->num_ofld_uld += 1;
+ }
+ if (ntohs(caps_cmd.cryptocaps) &
+ FW_CAPS_CONFIG_TLS_INLINE) {
+ params[0] = FW_PARAM_PFVF(TLS_START);
+ params[1] = FW_PARAM_PFVF(TLS_END);
+ ret = t4_query_params(adap, adap->mbox, adap->pf, 0,
+ 2, params, val);
+ if (ret < 0)
goto bye;
- } else {
- adap->vres.ncrypto_fc = val[0];
+ adap->vres.key.start = val[0];
+ adap->vres.key.size = val[1] - val[0] + 1;
+ adap->num_uld += 1;
}
adap->params.crypto = ntohs(caps_cmd.cryptocaps);
- adap->num_uld += 1;
}
#undef FW_PARAM_PFVF
#undef FW_PARAM_DEV
@@ -4346,6 +4625,7 @@ static int adap_init0(struct adapter *adap)
* happened to HW/FW, stop issuing commands.
*/
bye:
+ adap_free_hma_mem(adap);
kfree(adap->sge.egr_map);
kfree(adap->sge.ingr_map);
kfree(adap->sge.starving_fl);
@@ -4903,6 +5183,7 @@ static void free_some_resources(struct adapter *adapter)
kvfree(adapter->smt);
kvfree(adapter->l2t);
+ kvfree(adapter->srq);
t4_cleanup_sched(adapter);
kvfree(adapter->tids.tid_tab);
cxgb4_cleanup_tc_flower(adapter);
@@ -5257,6 +5538,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
INIT_WORK(&adapter->tid_release_task, process_tid_release_list);
INIT_WORK(&adapter->db_full_task, process_db_full);
INIT_WORK(&adapter->db_drop_task, process_db_drop);
+ INIT_WORK(&adapter->fatal_err_notify_task, notify_fatal_err);
err = t4_prep_adapter(adapter);
if (err)
@@ -5474,6 +5756,13 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (err)
goto out_free_dev;
+ err = setup_fw_sge_queues(adapter);
+ if (err) {
+ dev_err(adapter->pdev_dev,
+ "FW sge queue allocation failed, err %d", err);
+ goto out_free_dev;
+ }
+
/*
* The card is now ready to go. If any errors occur during device
* registration we do not fail the whole card but rather proceed only
@@ -5522,10 +5811,10 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
cxgb4_ptp_init(adapter);
print_adapter_info(adapter);
- setup_fw_sge_queues(adapter);
return 0;
out_free_dev:
+ t4_free_sge_resources(adapter);
free_some_resources(adapter);
if (adapter->flags & USING_MSIX)
free_msix_info(adapter);
@@ -5574,6 +5863,8 @@ static void remove_one(struct pci_dev *pdev)
t4_uld_clean_up(adapter);
}
+ adap_free_hma_mem(adapter);
+
disable_interrupts(adapter);
for_each_port(adapter, i)
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c
index 6b5fea4532f3..a95cde0fadf7 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c
@@ -342,6 +342,7 @@ static void free_queues_uld(struct adapter *adap, unsigned int uld_type)
{
struct sge_uld_rxq_info *rxq_info = adap->sge.uld_rxq_info[uld_type];
+ adap->sge.uld_rxq_info[uld_type] = NULL;
kfree(rxq_info->rspq_id);
kfree(rxq_info->uldrxq);
kfree(rxq_info);
@@ -665,6 +666,8 @@ static void uld_init(struct adapter *adap, struct cxgb4_lld_info *lld)
lld->ulptx_memwrite_dsgl = adap->params.ulptx_memwrite_dsgl;
lld->nodeid = dev_to_node(adap->pdev_dev);
lld->fr_nsmr_tpte_wr_support = adap->params.fr_nsmr_tpte_wr_support;
+ lld->write_w_imm_support = adap->params.write_w_imm_support;
+ lld->write_cmpl_support = adap->params.write_cmpl_support;
}
static void uld_attach(struct adapter *adap, unsigned int uld)
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h
index a14e8db51cdc..de9ad311dacd 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h
@@ -237,6 +237,7 @@ enum cxgb4_uld {
CXGB4_ULD_ISCSI,
CXGB4_ULD_ISCSIT,
CXGB4_ULD_CRYPTO,
+ CXGB4_ULD_TLS,
CXGB4_ULD_MAX
};
@@ -257,7 +258,8 @@ enum cxgb4_state {
CXGB4_STATE_UP,
CXGB4_STATE_START_RECOVERY,
CXGB4_STATE_DOWN,
- CXGB4_STATE_DETACH
+ CXGB4_STATE_DETACH,
+ CXGB4_STATE_FATAL_ERROR
};
enum cxgb4_control {
@@ -283,10 +285,12 @@ struct cxgb4_virt_res { /* virtualized HW resources */
struct cxgb4_range iscsi;
struct cxgb4_range stag;
struct cxgb4_range rq;
+ struct cxgb4_range srq;
struct cxgb4_range pbl;
struct cxgb4_range qp;
struct cxgb4_range cq;
struct cxgb4_range ocq;
+ struct cxgb4_range key;
unsigned int ncrypto_fc;
};
@@ -298,6 +302,9 @@ struct chcr_stats_debug {
atomic_t error;
atomic_t fallback;
atomic_t ipsec_cnt;
+ atomic_t tls_pdu_tx;
+ atomic_t tls_pdu_rx;
+ atomic_t tls_key;
};
#define OCQ_WIN_OFFSET(pdev, vres) \
@@ -352,6 +359,8 @@ struct cxgb4_lld_info {
void **iscsi_ppm; /* iscsi page pod manager */
int nodeid; /* device numa node id */
bool fr_nsmr_tpte_wr_support; /* FW supports FR_NSMR_TPTE_WR */
+ bool write_w_imm_support; /* FW supports WRITE_WITH_IMMEDIATE */
+ bool write_cmpl_support; /* FW supports WRITE_CMPL WR */
};
struct cxgb4_uld_info {
@@ -378,6 +387,8 @@ struct cxgb4_uld_info {
int cxgb4_register_uld(enum cxgb4_uld type, const struct cxgb4_uld_info *p);
int cxgb4_unregister_uld(enum cxgb4_uld type);
int cxgb4_ofld_send(struct net_device *dev, struct sk_buff *skb);
+int cxgb4_immdata_send(struct net_device *dev, unsigned int idx,
+ const void *src, unsigned int len);
int cxgb4_crypto_send(struct net_device *dev, struct sk_buff *skb);
unsigned int cxgb4_dbfifo_count(const struct net_device *dev, int lpfifo);
unsigned int cxgb4_port_chan(const struct net_device *dev);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/sched.h b/drivers/net/ethernet/chelsio/cxgb4/sched.h
index 77b2b3fd9021..3a49e00a38a1 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/sched.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/sched.h
@@ -42,8 +42,8 @@
#define FW_SCHED_CLS_NONE 0xffffffff
-/* Max rate that can be set to a scheduling class is 10 Gbps */
-#define SCHED_MAX_RATE_KBPS 10000000U
+/* Max rate that can be set to a scheduling class is 100 Gbps */
+#define SCHED_MAX_RATE_KBPS 100000000U
enum {
SCHED_STATE_ACTIVE,
diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c
index 6e310a0da7c9..1a28df137e1f 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/sge.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c
@@ -1019,8 +1019,8 @@ EXPORT_SYMBOL(cxgb4_ring_tx_db);
void cxgb4_inline_tx_skb(const struct sk_buff *skb,
const struct sge_txq *q, void *pos)
{
- u64 *p;
int left = (void *)q->stat - pos;
+ u64 *p;
if (likely(skb->len <= left)) {
if (likely(!skb->data_len))
@@ -1735,15 +1735,13 @@ static void txq_stop_maperr(struct sge_uld_txq *q)
/**
* ofldtxq_stop - stop an offload Tx queue that has become full
* @q: the queue to stop
- * @skb: the packet causing the queue to become full
+ * @wr: the Work Request causing the queue to become full
*
* Stops an offload Tx queue that has become full and modifies the packet
* being written to request a wakeup.
*/
-static void ofldtxq_stop(struct sge_uld_txq *q, struct sk_buff *skb)
+static void ofldtxq_stop(struct sge_uld_txq *q, struct fw_wr_hdr *wr)
{
- struct fw_wr_hdr *wr = (struct fw_wr_hdr *)skb->data;
-
wr->lo |= htonl(FW_WR_EQUEQ_F | FW_WR_EQUIQ_F);
q->q.stops++;
q->full = 1;
@@ -1804,7 +1802,7 @@ static void service_ofldq(struct sge_uld_txq *q)
credits = txq_avail(&q->q) - ndesc;
BUG_ON(credits < 0);
if (unlikely(credits < TXQ_STOP_THRES))
- ofldtxq_stop(q, skb);
+ ofldtxq_stop(q, (struct fw_wr_hdr *)skb->data);
pos = (u64 *)&q->q.desc[q->q.pidx];
if (is_ofld_imm(skb))
@@ -2005,6 +2003,103 @@ int cxgb4_ofld_send(struct net_device *dev, struct sk_buff *skb)
}
EXPORT_SYMBOL(cxgb4_ofld_send);
+static void *inline_tx_header(const void *src,
+ const struct sge_txq *q,
+ void *pos, int length)
+{
+ int left = (void *)q->stat - pos;
+ u64 *p;
+
+ if (likely(length <= left)) {
+ memcpy(pos, src, length);
+ pos += length;
+ } else {
+ memcpy(pos, src, left);
+ memcpy(q->desc, src + left, length - left);
+ pos = (void *)q->desc + (length - left);
+ }
+ /* 0-pad to multiple of 16 */
+ p = PTR_ALIGN(pos, 8);
+ if ((uintptr_t)p & 8) {
+ *p = 0;
+ return p + 1;
+ }
+ return p;
+}
+
+/**
+ * ofld_xmit_direct - copy a WR into offload queue
+ * @q: the Tx offload queue
+ * @src: location of WR
+ * @len: WR length
+ *
+ * Copy an immediate WR into an uncontended SGE offload queue.
+ */
+static int ofld_xmit_direct(struct sge_uld_txq *q, const void *src,
+ unsigned int len)
+{
+ unsigned int ndesc;
+ int credits;
+ u64 *pos;
+
+ /* Use the lower limit as the cut-off */
+ if (len > MAX_IMM_OFLD_TX_DATA_WR_LEN) {
+ WARN_ON(1);
+ return NET_XMIT_DROP;
+ }
+
+ /* Don't return NET_XMIT_CN here as the current
+ * implementation doesn't queue the request
+ * using an skb when the following conditions not met
+ */
+ if (!spin_trylock(&q->sendq.lock))
+ return NET_XMIT_DROP;
+
+ if (q->full || !skb_queue_empty(&q->sendq) ||
+ q->service_ofldq_running) {
+ spin_unlock(&q->sendq.lock);
+ return NET_XMIT_DROP;
+ }
+ ndesc = flits_to_desc(DIV_ROUND_UP(len, 8));
+ credits = txq_avail(&q->q) - ndesc;
+ pos = (u64 *)&q->q.desc[q->q.pidx];
+
+ /* ofldtxq_stop modifies WR header in-situ */
+ inline_tx_header(src, &q->q, pos, len);
+ if (unlikely(credits < TXQ_STOP_THRES))
+ ofldtxq_stop(q, (struct fw_wr_hdr *)pos);
+ txq_advance(&q->q, ndesc);
+ cxgb4_ring_tx_db(q->adap, &q->q, ndesc);
+
+ spin_unlock(&q->sendq.lock);
+ return NET_XMIT_SUCCESS;
+}
+
+int cxgb4_immdata_send(struct net_device *dev, unsigned int idx,
+ const void *src, unsigned int len)
+{
+ struct sge_uld_txq_info *txq_info;
+ struct sge_uld_txq *txq;
+ struct adapter *adap;
+ int ret;
+
+ adap = netdev2adap(dev);
+
+ local_bh_disable();
+ txq_info = adap->sge.uld_txq_info[CXGB4_TX_OFLD];
+ if (unlikely(!txq_info)) {
+ WARN_ON(true);
+ local_bh_enable();
+ return NET_XMIT_DROP;
+ }
+ txq = &txq_info->uldtxq[idx];
+
+ ret = ofld_xmit_direct(txq, src, len);
+ local_bh_enable();
+ return net_xmit_eval(ret);
+}
+EXPORT_SYMBOL(cxgb4_immdata_send);
+
/**
* t4_crypto_send - send crypto packet
* @adap: the adapter
diff --git a/drivers/net/ethernet/chelsio/cxgb4/srq.c b/drivers/net/ethernet/chelsio/cxgb4/srq.c
new file mode 100644
index 000000000000..6228a5708307
--- /dev/null
+++ b/drivers/net/ethernet/chelsio/cxgb4/srq.c
@@ -0,0 +1,138 @@
+/*
+ * This file is part of the Chelsio T6 Ethernet driver for Linux.
+ *
+ * Copyright (c) 2017-2018 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 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
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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 "cxgb4.h"
+#include "t4_msg.h"
+#include "srq.h"
+
+struct srq_data *t4_init_srq(int srq_size)
+{
+ struct srq_data *s;
+
+ s = kvzalloc(sizeof(*s), GFP_KERNEL);
+ if (!s)
+ return NULL;
+
+ s->srq_size = srq_size;
+ init_completion(&s->comp);
+ mutex_init(&s->lock);
+
+ return s;
+}
+
+/* cxgb4_get_srq_entry: read the SRQ table entry
+ * @dev: Pointer to the net_device
+ * @idx: Index to the srq
+ * @entryp: pointer to the srq entry
+ *
+ * Sends CPL_SRQ_TABLE_REQ message for the given index.
+ * Contents will be returned in CPL_SRQ_TABLE_RPL message.
+ *
+ * Returns zero if the read is successful, else a error
+ * number will be returned. Caller should not use the srq
+ * entry if the return value is non-zero.
+ *
+ *
+ */
+int cxgb4_get_srq_entry(struct net_device *dev,
+ int srq_idx, struct srq_entry *entryp)
+{
+ struct cpl_srq_table_req *req;
+ struct adapter *adap;
+ struct sk_buff *skb;
+ struct srq_data *s;
+ int rc = -ENODEV;
+
+ adap = netdev2adap(dev);
+ s = adap->srq;
+
+ if (!(adap->flags & FULL_INIT_DONE) || !s)
+ goto out;
+
+ skb = alloc_skb(sizeof(*req), GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+ req = (struct cpl_srq_table_req *)
+ __skb_put(skb, sizeof(*req));
+ memset(req, 0, sizeof(*req));
+ INIT_TP_WR(req, 0);
+ OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SRQ_TABLE_REQ,
+ TID_TID_V(srq_idx) |
+ TID_QID_V(adap->sge.fw_evtq.abs_id)));
+ req->idx = srq_idx;
+
+ mutex_lock(&s->lock);
+
+ s->entryp = entryp;
+ t4_mgmt_tx(adap, skb);
+
+ rc = wait_for_completion_timeout(&s->comp, SRQ_WAIT_TO);
+ if (rc)
+ rc = 0;
+ else /* !rc means we timed out */
+ rc = -ETIMEDOUT;
+
+ WARN_ON_ONCE(entryp->idx != srq_idx);
+ mutex_unlock(&s->lock);
+out:
+ return rc;
+}
+EXPORT_SYMBOL(cxgb4_get_srq_entry);
+
+void do_srq_table_rpl(struct adapter *adap,
+ const struct cpl_srq_table_rpl *rpl)
+{
+ unsigned int idx = TID_TID_G(GET_TID(rpl));
+ struct srq_data *s = adap->srq;
+ struct srq_entry *e;
+
+ if (unlikely(rpl->status != CPL_CONTAINS_READ_RPL)) {
+ dev_err(adap->pdev_dev,
+ "Unexpected SRQ_TABLE_RPL status %u for entry %u\n",
+ rpl->status, idx);
+ goto out;
+ }
+
+ /* Store the read entry */
+ e = s->entryp;
+ e->valid = 1;
+ e->idx = idx;
+ e->pdid = SRQT_PDID_G(be64_to_cpu(rpl->rsvd_pdid));
+ e->qlen = SRQT_QLEN_G(be32_to_cpu(rpl->qlen_qbase));
+ e->qbase = SRQT_QBASE_G(be32_to_cpu(rpl->qlen_qbase));
+ e->cur_msn = be16_to_cpu(rpl->cur_msn);
+ e->max_msn = be16_to_cpu(rpl->max_msn);
+out:
+ complete(&s->comp);
+}
diff --git a/drivers/net/ethernet/chelsio/cxgb4/srq.h b/drivers/net/ethernet/chelsio/cxgb4/srq.h
new file mode 100644
index 000000000000..ec85cf93865a
--- /dev/null
+++ b/drivers/net/ethernet/chelsio/cxgb4/srq.h
@@ -0,0 +1,65 @@
+/*
+ * This file is part of the Chelsio T6 Ethernet driver for Linux.
+ *
+ * Copyright (c) 2017-2018 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 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
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ */
+
+#ifndef __CXGB4_SRQ_H
+#define __CXGB4_SRQ_H
+
+struct adapter;
+struct cpl_srq_table_rpl;
+
+#define SRQ_WAIT_TO (HZ * 5)
+
+struct srq_entry {
+ u8 valid;
+ u8 idx;
+ u8 qlen;
+ u16 pdid;
+ u16 cur_msn;
+ u16 max_msn;
+ u32 qbase;
+};
+
+struct srq_data {
+ unsigned int srq_size;
+ struct srq_entry *entryp;
+ struct completion comp;
+ struct mutex lock; /* generic mutex for srq data */
+};
+
+struct srq_data *t4_init_srq(int srq_size);
+int cxgb4_get_srq_entry(struct net_device *dev,
+ int srq_idx, struct srq_entry *entryp);
+void do_srq_table_rpl(struct adapter *adap,
+ const struct cpl_srq_table_rpl *rpl);
+#endif /* __CXGB4_SRQ_H */
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
index 920bccd6bc40..7cb3ef466cc7 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
@@ -484,6 +484,117 @@ static int t4_edc_err_read(struct adapter *adap, int idx)
}
/**
+ * t4_memory_rw_init - Get memory window relative offset, base, and size.
+ * @adap: the adapter
+ * @win: PCI-E Memory Window to use
+ * @mtype: memory type: MEM_EDC0, MEM_EDC1, MEM_HMA or MEM_MC
+ * @mem_off: memory relative offset with respect to @mtype.
+ * @mem_base: configured memory base address.
+ * @mem_aperture: configured memory window aperture.
+ *
+ * Get the configured memory window's relative offset, base, and size.
+ */
+int t4_memory_rw_init(struct adapter *adap, int win, int mtype, u32 *mem_off,
+ u32 *mem_base, u32 *mem_aperture)
+{
+ u32 edc_size, mc_size, mem_reg;
+
+ /* Offset into the region of memory which is being accessed
+ * MEM_EDC0 = 0
+ * MEM_EDC1 = 1
+ * MEM_MC = 2 -- MEM_MC for chips with only 1 memory controller
+ * MEM_MC1 = 3 -- for chips with 2 memory controllers (e.g. T5)
+ * MEM_HMA = 4
+ */
+ edc_size = EDRAM0_SIZE_G(t4_read_reg(adap, MA_EDRAM0_BAR_A));
+ if (mtype == MEM_HMA) {
+ *mem_off = 2 * (edc_size * 1024 * 1024);
+ } else if (mtype != MEM_MC1) {
+ *mem_off = (mtype * (edc_size * 1024 * 1024));
+ } else {
+ mc_size = EXT_MEM0_SIZE_G(t4_read_reg(adap,
+ MA_EXT_MEMORY0_BAR_A));
+ *mem_off = (MEM_MC0 * edc_size + mc_size) * 1024 * 1024;
+ }
+
+ /* Each PCI-E Memory Window is programmed with a window size -- or
+ * "aperture" -- which controls the granularity of its mapping onto
+ * adapter memory. We need to grab that aperture in order to know
+ * how to use the specified window. The window is also programmed
+ * with the base address of the Memory Window in BAR0's address
+ * space. For T4 this is an absolute PCI-E Bus Address. For T5
+ * the address is relative to BAR0.
+ */
+ mem_reg = t4_read_reg(adap,
+ PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN_A,
+ win));
+ /* a dead adapter will return 0xffffffff for PIO reads */
+ if (mem_reg == 0xffffffff)
+ return -ENXIO;
+
+ *mem_aperture = 1 << (WINDOW_G(mem_reg) + WINDOW_SHIFT_X);
+ *mem_base = PCIEOFST_G(mem_reg) << PCIEOFST_SHIFT_X;
+ if (is_t4(adap->params.chip))
+ *mem_base -= adap->t4_bar0;
+
+ return 0;
+}
+
+/**
+ * t4_memory_update_win - Move memory window to specified address.
+ * @adap: the adapter
+ * @win: PCI-E Memory Window to use
+ * @addr: location to move.
+ *
+ * Move memory window to specified address.
+ */
+void t4_memory_update_win(struct adapter *adap, int win, u32 addr)
+{
+ t4_write_reg(adap,
+ PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET_A, win),
+ addr);
+ /* Read it back to ensure that changes propagate before we
+ * attempt to use the new value.
+ */
+ t4_read_reg(adap,
+ PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET_A, win));
+}
+
+/**
+ * t4_memory_rw_residual - Read/Write residual data.
+ * @adap: the adapter
+ * @off: relative offset within residual to start read/write.
+ * @addr: address within indicated memory type.
+ * @buf: host memory buffer
+ * @dir: direction of transfer T4_MEMORY_READ (1) or T4_MEMORY_WRITE (0)
+ *
+ * Read/Write residual data less than 32-bits.
+ */
+void t4_memory_rw_residual(struct adapter *adap, u32 off, u32 addr, u8 *buf,
+ int dir)
+{
+ union {
+ u32 word;
+ char byte[4];
+ } last;
+ unsigned char *bp;
+ int i;
+
+ if (dir == T4_MEMORY_READ) {
+ last.word = le32_to_cpu((__force __le32)
+ t4_read_reg(adap, addr));
+ for (bp = (unsigned char *)buf, i = off; i < 4; i++)
+ bp[i] = last.byte[i];
+ } else {
+ last.word = *buf;
+ for (i = off; i < 4; i++)
+ last.byte[i] = 0;
+ t4_write_reg(adap, addr,
+ (__force u32)cpu_to_le32(last.word));
+ }
+}
+
+/**
* t4_memory_rw - read/write EDC 0, EDC 1 or MC via PCIE memory window
* @adap: the adapter
* @win: PCI-E Memory Window to use
@@ -504,8 +615,9 @@ int t4_memory_rw(struct adapter *adap, int win, int mtype, u32 addr,
u32 len, void *hbuf, int dir)
{
u32 pos, offset, resid, memoffset;
- u32 edc_size, mc_size, win_pf, mem_reg, mem_aperture, mem_base;
+ u32 win_pf, mem_aperture, mem_base;
u32 *buf;
+ int ret;
/* Argument sanity checks ...
*/
@@ -521,59 +633,26 @@ int t4_memory_rw(struct adapter *adap, int win, int mtype, u32 addr,
resid = len & 0x3;
len -= resid;
- /* Offset into the region of memory which is being accessed
- * MEM_EDC0 = 0
- * MEM_EDC1 = 1
- * MEM_MC = 2 -- MEM_MC for chips with only 1 memory controller
- * MEM_MC1 = 3 -- for chips with 2 memory controllers (e.g. T5)
- * MEM_HMA = 4
- */
- edc_size = EDRAM0_SIZE_G(t4_read_reg(adap, MA_EDRAM0_BAR_A));
- if (mtype == MEM_HMA) {
- memoffset = 2 * (edc_size * 1024 * 1024);
- } else if (mtype != MEM_MC1) {
- memoffset = (mtype * (edc_size * 1024 * 1024));
- } else {
- mc_size = EXT_MEM0_SIZE_G(t4_read_reg(adap,
- MA_EXT_MEMORY0_BAR_A));
- memoffset = (MEM_MC0 * edc_size + mc_size) * 1024 * 1024;
- }
+ ret = t4_memory_rw_init(adap, win, mtype, &memoffset, &mem_base,
+ &mem_aperture);
+ if (ret)
+ return ret;
/* Determine the PCIE_MEM_ACCESS_OFFSET */
addr = addr + memoffset;
- /* Each PCI-E Memory Window is programmed with a window size -- or
- * "aperture" -- which controls the granularity of its mapping onto
- * adapter memory. We need to grab that aperture in order to know
- * how to use the specified window. The window is also programmed
- * with the base address of the Memory Window in BAR0's address
- * space. For T4 this is an absolute PCI-E Bus Address. For T5
- * the address is relative to BAR0.
- */
- mem_reg = t4_read_reg(adap,
- PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN_A,
- win));
- mem_aperture = 1 << (WINDOW_G(mem_reg) + WINDOW_SHIFT_X);
- mem_base = PCIEOFST_G(mem_reg) << PCIEOFST_SHIFT_X;
- if (is_t4(adap->params.chip))
- mem_base -= adap->t4_bar0;
win_pf = is_t4(adap->params.chip) ? 0 : PFNUM_V(adap->pf);
/* Calculate our initial PCI-E Memory Window Position and Offset into
* that Window.
*/
- pos = addr & ~(mem_aperture-1);
+ pos = addr & ~(mem_aperture - 1);
offset = addr - pos;
/* Set up initial PCI-E Memory Window to cover the start of our
- * transfer. (Read it back to ensure that changes propagate before we
- * attempt to use the new value.)
+ * transfer.
*/
- t4_write_reg(adap,
- PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET_A, win),
- pos | win_pf);
- t4_read_reg(adap,
- PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET_A, win));
+ t4_memory_update_win(adap, win, pos | win_pf);
/* Transfer data to/from the adapter as long as there's an integral
* number of 32-bit transfers to complete.
@@ -628,12 +707,7 @@ int t4_memory_rw(struct adapter *adap, int win, int mtype, u32 addr,
if (offset == mem_aperture) {
pos += mem_aperture;
offset = 0;
- t4_write_reg(adap,
- PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET_A,
- win), pos | win_pf);
- t4_read_reg(adap,
- PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET_A,
- win));
+ t4_memory_update_win(adap, win, pos | win_pf);
}
}
@@ -642,28 +716,9 @@ int t4_memory_rw(struct adapter *adap, int win, int mtype, u32 addr,
* residual amount. The PCI-E Memory Window has already been moved
* above (if necessary) to cover this final transfer.
*/
- if (resid) {
- union {
- u32 word;
- char byte[4];
- } last;
- unsigned char *bp;
- int i;
-
- if (dir == T4_MEMORY_READ) {
- last.word = le32_to_cpu(
- (__force __le32)t4_read_reg(adap,
- mem_base + offset));
- for (bp = (unsigned char *)buf, i = resid; i < 4; i++)
- bp[i] = last.byte[i];
- } else {
- last.word = *buf;
- for (i = resid; i < 4; i++)
- last.byte[i] = 0;
- t4_write_reg(adap, mem_base + offset,
- (__force u32)cpu_to_le32(last.word));
- }
- }
+ if (resid)
+ t4_memory_rw_residual(adap, resid, mem_base + offset,
+ (u8 *)buf, dir);
return 0;
}
@@ -4011,8 +4066,6 @@ int t4_link_l1cfg(struct adapter *adapter, unsigned int mbox,
unsigned int fw_mdi = FW_PORT_CAP32_MDI_V(FW_PORT_CAP32_MDI_AUTO);
fw_port_cap32_t fw_fc, cc_fec, fw_fec, rcap;
- lc->link_ok = 0;
-
/* Convert driver coding of Pause Frame Flow Control settings into the
* Firmware's API.
*/
@@ -6036,6 +6089,7 @@ unsigned int t4_get_tp_ch_map(struct adapter *adap, int pidx)
case CHELSIO_T6:
switch (nports) {
+ case 1:
case 2: return 1 << pidx;
}
break;
@@ -8550,6 +8604,25 @@ static int t4_get_flash_params(struct adapter *adap)
}
break;
}
+ case 0x9d: { /* ISSI -- Integrated Silicon Solution, Inc. */
+ /* This Density -> Size decoding table is taken from ISSI
+ * Data Sheets.
+ */
+ density = (flashid >> 16) & 0xff;
+ switch (density) {
+ case 0x16: /* 32 MB */
+ size = 1 << 25;
+ break;
+ case 0x17: /* 64MB */
+ size = 1 << 26;
+ break;
+ default:
+ dev_err(adap->pdev_dev, "ISSI Flash Part has bad size, ID = %#x, Density code = %#x\n",
+ flashid, density);
+ return -EINVAL;
+ }
+ break;
+ }
case 0xc2: { /* Macronix */
/* This Density -> Size decoding table is taken from Macronix
* Data Sheets.
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h b/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h
index d0db4427b77e..fe2029e993a2 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h
@@ -52,6 +52,7 @@ enum {
CPL_L2T_WRITE_REQ = 0x12,
CPL_SMT_WRITE_REQ = 0x14,
CPL_TID_RELEASE = 0x1A,
+ CPL_SRQ_TABLE_REQ = 0x1C,
CPL_TX_DATA_ISO = 0x1F,
CPL_CLOSE_LISTSRV_RPL = 0x20,
@@ -81,13 +82,15 @@ enum {
CPL_RX_ISCSI_CMP = 0x45,
CPL_TRACE_PKT_T5 = 0x48,
CPL_RX_ISCSI_DDP = 0x49,
+ CPL_RX_TLS_CMP = 0x4E,
CPL_RDMA_READ_REQ = 0x60,
CPL_PASS_OPEN_REQ6 = 0x81,
CPL_ACT_OPEN_REQ6 = 0x83,
- CPL_TX_TLS_PDU = 0x88,
+ CPL_TX_TLS_PDU = 0x88,
+ CPL_TX_TLS_SFO = 0x89,
CPL_TX_SEC_PDU = 0x8A,
CPL_TX_TLS_ACK = 0x8B,
@@ -97,11 +100,13 @@ enum {
CPL_RX_MPS_PKT = 0xAF,
CPL_TRACE_PKT = 0xB0,
+ CPL_TLS_DATA = 0xB1,
CPL_ISCSI_DATA = 0xB2,
CPL_FW4_MSG = 0xC0,
CPL_FW4_PLD = 0xC1,
CPL_FW4_ACK = 0xC3,
+ CPL_SRQ_TABLE_RPL = 0xCC,
CPL_RX_PHYS_DSGL = 0xD0,
@@ -136,6 +141,8 @@ enum CPL_error {
CPL_ERR_KEEPALV_NEG_ADVICE = 37,
CPL_ERR_ABORT_FAILED = 42,
CPL_ERR_IWARP_FLM = 50,
+ CPL_CONTAINS_READ_RPL = 60,
+ CPL_CONTAINS_WRITE_RPL = 61,
};
enum {
@@ -151,6 +158,7 @@ enum {
ULP_MODE_RDMA = 4,
ULP_MODE_TCPDDP = 5,
ULP_MODE_FCOE = 6,
+ ULP_MODE_TLS = 8,
};
enum {
@@ -198,6 +206,7 @@ union opcode_tid {
/* partitioning of TID fields that also carry a queue id */
#define TID_TID_S 0
#define TID_TID_M 0x3fff
+#define TID_TID_V(x) ((x) << TID_TID_S)
#define TID_TID_G(x) (((x) >> TID_TID_S) & TID_TID_M)
#define TID_QID_S 14
@@ -743,6 +752,22 @@ struct cpl_abort_req_rss {
u8 status;
};
+struct cpl_abort_req_rss6 {
+ WR_HDR;
+ union opcode_tid ot;
+ __u32 srqidx_status;
+};
+
+#define ABORT_RSS_STATUS_S 0
+#define ABORT_RSS_STATUS_M 0xff
+#define ABORT_RSS_STATUS_V(x) ((x) << ABORT_RSS_STATUS_S)
+#define ABORT_RSS_STATUS_G(x) (((x) >> ABORT_RSS_STATUS_S) & ABORT_RSS_STATUS_M)
+
+#define ABORT_RSS_SRQIDX_S 8
+#define ABORT_RSS_SRQIDX_M 0xffffff
+#define ABORT_RSS_SRQIDX_V(x) ((x) << ABORT_RSS_SRQIDX_S)
+#define ABORT_RSS_SRQIDX_G(x) (((x) >> ABORT_RSS_SRQIDX_S) & ABORT_RSS_SRQIDX_M)
+
struct cpl_abort_req {
WR_HDR;
union opcode_tid ot;
@@ -758,6 +783,11 @@ struct cpl_abort_rpl_rss {
u8 status;
};
+struct cpl_abort_rpl_rss6 {
+ union opcode_tid ot;
+ __u32 srqidx_status;
+};
+
struct cpl_abort_rpl {
WR_HDR;
union opcode_tid ot;
@@ -1419,6 +1449,14 @@ struct cpl_tx_data {
#define T6_TX_FORCE_V(x) ((x) << T6_TX_FORCE_S)
#define T6_TX_FORCE_F T6_TX_FORCE_V(1U)
+#define TX_SHOVE_S 14
+#define TX_SHOVE_V(x) ((x) << TX_SHOVE_S)
+
+#define TX_ULP_MODE_S 10
+#define TX_ULP_MODE_M 0x7
+#define TX_ULP_MODE_V(x) ((x) << TX_ULP_MODE_S)
+#define TX_ULP_MODE_G(x) (((x) >> TX_ULP_MODE_S) & TX_ULP_MODE_M)
+
enum {
ULP_TX_MEM_READ = 2,
ULP_TX_MEM_WRITE = 3,
@@ -1429,12 +1467,21 @@ enum {
ULP_TX_SC_NOOP = 0x80,
ULP_TX_SC_IMM = 0x81,
ULP_TX_SC_DSGL = 0x82,
- ULP_TX_SC_ISGL = 0x83
+ ULP_TX_SC_ISGL = 0x83,
+ ULP_TX_SC_MEMRD = 0x86
};
#define ULPTX_CMD_S 24
#define ULPTX_CMD_V(x) ((x) << ULPTX_CMD_S)
+#define ULPTX_LEN16_S 0
+#define ULPTX_LEN16_M 0xFF
+#define ULPTX_LEN16_V(x) ((x) << ULPTX_LEN16_S)
+
+#define ULP_TX_SC_MORE_S 23
+#define ULP_TX_SC_MORE_V(x) ((x) << ULP_TX_SC_MORE_S)
+#define ULP_TX_SC_MORE_F ULP_TX_SC_MORE_V(1U)
+
struct ulptx_sge_pair {
__be32 len[2];
__be64 addr[2];
@@ -2112,4 +2159,146 @@ enum {
X_CPL_RX_MPS_PKT_TYPE_QFC = 1 << 2,
X_CPL_RX_MPS_PKT_TYPE_PTP = 1 << 3
};
+
+struct cpl_srq_table_req {
+ WR_HDR;
+ union opcode_tid ot;
+ __u8 status;
+ __u8 rsvd[2];
+ __u8 idx;
+ __be64 rsvd_pdid;
+ __be32 qlen_qbase;
+ __be16 cur_msn;
+ __be16 max_msn;
+};
+
+struct cpl_srq_table_rpl {
+ union opcode_tid ot;
+ __u8 status;
+ __u8 rsvd[2];
+ __u8 idx;
+ __be64 rsvd_pdid;
+ __be32 qlen_qbase;
+ __be16 cur_msn;
+ __be16 max_msn;
+};
+
+/* cpl_srq_table_{req,rpl}.params fields */
+#define SRQT_QLEN_S 28
+#define SRQT_QLEN_M 0xF
+#define SRQT_QLEN_V(x) ((x) << SRQT_QLEN_S)
+#define SRQT_QLEN_G(x) (((x) >> SRQT_QLEN_S) & SRQT_QLEN_M)
+
+#define SRQT_QBASE_S 0
+#define SRQT_QBASE_M 0x3FFFFFF
+#define SRQT_QBASE_V(x) ((x) << SRQT_QBASE_S)
+#define SRQT_QBASE_G(x) (((x) >> SRQT_QBASE_S) & SRQT_QBASE_M)
+
+#define SRQT_PDID_S 0
+#define SRQT_PDID_M 0xFF
+#define SRQT_PDID_V(x) ((x) << SRQT_PDID_S)
+#define SRQT_PDID_G(x) (((x) >> SRQT_PDID_S) & SRQT_PDID_M)
+
+#define SRQT_IDX_S 0
+#define SRQT_IDX_M 0xF
+#define SRQT_IDX_V(x) ((x) << SRQT_IDX_S)
+#define SRQT_IDX_G(x) (((x) >> SRQT_IDX_S) & SRQT_IDX_M)
+
+struct cpl_tx_tls_sfo {
+ __be32 op_to_seg_len;
+ __be32 pld_len;
+ __be32 type_protover;
+ __be32 r1_lo;
+ __be32 seqno_numivs;
+ __be32 ivgen_hdrlen;
+ __be64 scmd1;
+};
+
+/* cpl_tx_tls_sfo macros */
+#define CPL_TX_TLS_SFO_OPCODE_S 24
+#define CPL_TX_TLS_SFO_OPCODE_V(x) ((x) << CPL_TX_TLS_SFO_OPCODE_S)
+
+#define CPL_TX_TLS_SFO_DATA_TYPE_S 20
+#define CPL_TX_TLS_SFO_DATA_TYPE_V(x) ((x) << CPL_TX_TLS_SFO_DATA_TYPE_S)
+
+#define CPL_TX_TLS_SFO_CPL_LEN_S 16
+#define CPL_TX_TLS_SFO_CPL_LEN_V(x) ((x) << CPL_TX_TLS_SFO_CPL_LEN_S)
+
+#define CPL_TX_TLS_SFO_SEG_LEN_S 0
+#define CPL_TX_TLS_SFO_SEG_LEN_M 0xffff
+#define CPL_TX_TLS_SFO_SEG_LEN_V(x) ((x) << CPL_TX_TLS_SFO_SEG_LEN_S)
+#define CPL_TX_TLS_SFO_SEG_LEN_G(x) \
+ (((x) >> CPL_TX_TLS_SFO_SEG_LEN_S) & CPL_TX_TLS_SFO_SEG_LEN_M)
+
+#define CPL_TX_TLS_SFO_TYPE_S 24
+#define CPL_TX_TLS_SFO_TYPE_M 0xff
+#define CPL_TX_TLS_SFO_TYPE_V(x) ((x) << CPL_TX_TLS_SFO_TYPE_S)
+#define CPL_TX_TLS_SFO_TYPE_G(x) \
+ (((x) >> CPL_TX_TLS_SFO_TYPE_S) & CPL_TX_TLS_SFO_TYPE_M)
+
+#define CPL_TX_TLS_SFO_PROTOVER_S 8
+#define CPL_TX_TLS_SFO_PROTOVER_M 0xffff
+#define CPL_TX_TLS_SFO_PROTOVER_V(x) ((x) << CPL_TX_TLS_SFO_PROTOVER_S)
+#define CPL_TX_TLS_SFO_PROTOVER_G(x) \
+ (((x) >> CPL_TX_TLS_SFO_PROTOVER_S) & CPL_TX_TLS_SFO_PROTOVER_M)
+
+struct cpl_tls_data {
+ struct rss_header rsshdr;
+ union opcode_tid ot;
+ __be32 length_pkd;
+ __be32 seq;
+ __be32 r1;
+};
+
+#define CPL_TLS_DATA_OPCODE_S 24
+#define CPL_TLS_DATA_OPCODE_M 0xff
+#define CPL_TLS_DATA_OPCODE_V(x) ((x) << CPL_TLS_DATA_OPCODE_S)
+#define CPL_TLS_DATA_OPCODE_G(x) \
+ (((x) >> CPL_TLS_DATA_OPCODE_S) & CPL_TLS_DATA_OPCODE_M)
+
+#define CPL_TLS_DATA_TID_S 0
+#define CPL_TLS_DATA_TID_M 0xffffff
+#define CPL_TLS_DATA_TID_V(x) ((x) << CPL_TLS_DATA_TID_S)
+#define CPL_TLS_DATA_TID_G(x) \
+ (((x) >> CPL_TLS_DATA_TID_S) & CPL_TLS_DATA_TID_M)
+
+#define CPL_TLS_DATA_LENGTH_S 0
+#define CPL_TLS_DATA_LENGTH_M 0xffff
+#define CPL_TLS_DATA_LENGTH_V(x) ((x) << CPL_TLS_DATA_LENGTH_S)
+#define CPL_TLS_DATA_LENGTH_G(x) \
+ (((x) >> CPL_TLS_DATA_LENGTH_S) & CPL_TLS_DATA_LENGTH_M)
+
+struct cpl_rx_tls_cmp {
+ struct rss_header rsshdr;
+ union opcode_tid ot;
+ __be32 pdulength_length;
+ __be32 seq;
+ __be32 ddp_report;
+ __be32 r;
+ __be32 ddp_valid;
+};
+
+#define CPL_RX_TLS_CMP_OPCODE_S 24
+#define CPL_RX_TLS_CMP_OPCODE_M 0xff
+#define CPL_RX_TLS_CMP_OPCODE_V(x) ((x) << CPL_RX_TLS_CMP_OPCODE_S)
+#define CPL_RX_TLS_CMP_OPCODE_G(x) \
+ (((x) >> CPL_RX_TLS_CMP_OPCODE_S) & CPL_RX_TLS_CMP_OPCODE_M)
+
+#define CPL_RX_TLS_CMP_TID_S 0
+#define CPL_RX_TLS_CMP_TID_M 0xffffff
+#define CPL_RX_TLS_CMP_TID_V(x) ((x) << CPL_RX_TLS_CMP_TID_S)
+#define CPL_RX_TLS_CMP_TID_G(x) \
+ (((x) >> CPL_RX_TLS_CMP_TID_S) & CPL_RX_TLS_CMP_TID_M)
+
+#define CPL_RX_TLS_CMP_PDULENGTH_S 16
+#define CPL_RX_TLS_CMP_PDULENGTH_M 0xffff
+#define CPL_RX_TLS_CMP_PDULENGTH_V(x) ((x) << CPL_RX_TLS_CMP_PDULENGTH_S)
+#define CPL_RX_TLS_CMP_PDULENGTH_G(x) \
+ (((x) >> CPL_RX_TLS_CMP_PDULENGTH_S) & CPL_RX_TLS_CMP_PDULENGTH_M)
+
+#define CPL_RX_TLS_CMP_LENGTH_S 0
+#define CPL_RX_TLS_CMP_LENGTH_M 0xffff
+#define CPL_RX_TLS_CMP_LENGTH_V(x) ((x) << CPL_RX_TLS_CMP_LENGTH_S)
+#define CPL_RX_TLS_CMP_LENGTH_G(x) \
+ (((x) >> CPL_RX_TLS_CMP_LENGTH_S) & CPL_RX_TLS_CMP_LENGTH_M)
#endif /* __T4_MSG_H */
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
index a6df73398d17..276fdf214b75 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
@@ -2775,6 +2775,8 @@
#define ULP_RX_LA_RDPTR_A 0x19240
#define ULP_RX_LA_RDDATA_A 0x19244
#define ULP_RX_LA_WRPTR_A 0x19248
+#define ULP_RX_TLS_KEY_LLIMIT_A 0x192ac
+#define ULP_RX_TLS_KEY_ULIMIT_A 0x192b0
#define HPZ3_S 24
#define HPZ3_V(x) ((x) << HPZ3_S)
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h
index 0d83b4064a78..e3d4751f21ac 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h
@@ -101,9 +101,11 @@ enum fw_wr_opcodes {
FW_RI_BIND_MW_WR = 0x18,
FW_RI_FR_NSMR_WR = 0x19,
FW_RI_FR_NSMR_TPTE_WR = 0x20,
+ FW_RI_RDMA_WRITE_CMPL_WR = 0x21,
FW_RI_INV_LSTAG_WR = 0x1a,
FW_ISCSI_TX_DATA_WR = 0x45,
FW_PTP_TX_PKT_WR = 0x46,
+ FW_TLSTX_DATA_WR = 0x68,
FW_CRYPTO_LOOKASIDE_WR = 0X6d,
FW_LASTC2E_WR = 0x70,
FW_FILTER2_WR = 0x77
@@ -634,6 +636,30 @@ struct fw_ofld_connection_wr {
#define FW_OFLD_CONNECTION_WR_CPLPASSACCEPTRPL_F \
FW_OFLD_CONNECTION_WR_CPLPASSACCEPTRPL_V(1U)
+enum fw_flowc_mnem_tcpstate {
+ FW_FLOWC_MNEM_TCPSTATE_CLOSED = 0, /* illegal */
+ FW_FLOWC_MNEM_TCPSTATE_LISTEN = 1, /* illegal */
+ FW_FLOWC_MNEM_TCPSTATE_SYNSENT = 2, /* illegal */
+ FW_FLOWC_MNEM_TCPSTATE_SYNRECEIVED = 3, /* illegal */
+ FW_FLOWC_MNEM_TCPSTATE_ESTABLISHED = 4, /* default */
+ FW_FLOWC_MNEM_TCPSTATE_CLOSEWAIT = 5, /* got peer close already */
+ FW_FLOWC_MNEM_TCPSTATE_FINWAIT1 = 6, /* haven't gotten ACK for FIN and
+ * will resend FIN - equiv ESTAB
+ */
+ FW_FLOWC_MNEM_TCPSTATE_CLOSING = 7, /* haven't gotten ACK for FIN and
+ * will resend FIN but have
+ * received FIN
+ */
+ FW_FLOWC_MNEM_TCPSTATE_LASTACK = 8, /* haven't gotten ACK for FIN and
+ * will resend FIN but have
+ * received FIN
+ */
+ FW_FLOWC_MNEM_TCPSTATE_FINWAIT2 = 9, /* sent FIN and got FIN + ACK,
+ * waiting for FIN
+ */
+ FW_FLOWC_MNEM_TCPSTATE_TIMEWAIT = 10, /* not expected */
+};
+
enum fw_flowc_mnem {
FW_FLOWC_MNEM_PFNVFN, /* PFN [15:8] VFN [7:0] */
FW_FLOWC_MNEM_CH,
@@ -650,6 +676,8 @@ enum fw_flowc_mnem {
FW_FLOWC_MNEM_DCBPRIO,
FW_FLOWC_MNEM_SND_SCALE,
FW_FLOWC_MNEM_RCV_SCALE,
+ FW_FLOWC_MNEM_ULD_MODE,
+ FW_FLOWC_MNEM_MAX,
};
struct fw_flowc_mnemval {
@@ -674,6 +702,14 @@ struct fw_ofld_tx_data_wr {
__be32 tunnel_to_proxy;
};
+#define FW_OFLD_TX_DATA_WR_ALIGNPLD_S 30
+#define FW_OFLD_TX_DATA_WR_ALIGNPLD_V(x) ((x) << FW_OFLD_TX_DATA_WR_ALIGNPLD_S)
+#define FW_OFLD_TX_DATA_WR_ALIGNPLD_F FW_OFLD_TX_DATA_WR_ALIGNPLD_V(1U)
+
+#define FW_OFLD_TX_DATA_WR_SHOVE_S 29
+#define FW_OFLD_TX_DATA_WR_SHOVE_V(x) ((x) << FW_OFLD_TX_DATA_WR_SHOVE_S)
+#define FW_OFLD_TX_DATA_WR_SHOVE_F FW_OFLD_TX_DATA_WR_SHOVE_V(1U)
+
#define FW_OFLD_TX_DATA_WR_TUNNEL_S 19
#define FW_OFLD_TX_DATA_WR_TUNNEL_V(x) ((x) << FW_OFLD_TX_DATA_WR_TUNNEL_S)
@@ -690,10 +726,6 @@ struct fw_ofld_tx_data_wr {
#define FW_OFLD_TX_DATA_WR_MORE_S 15
#define FW_OFLD_TX_DATA_WR_MORE_V(x) ((x) << FW_OFLD_TX_DATA_WR_MORE_S)
-#define FW_OFLD_TX_DATA_WR_SHOVE_S 14
-#define FW_OFLD_TX_DATA_WR_SHOVE_V(x) ((x) << FW_OFLD_TX_DATA_WR_SHOVE_S)
-#define FW_OFLD_TX_DATA_WR_SHOVE_F FW_OFLD_TX_DATA_WR_SHOVE_V(1U)
-
#define FW_OFLD_TX_DATA_WR_ULPMODE_S 10
#define FW_OFLD_TX_DATA_WR_ULPMODE_V(x) ((x) << FW_OFLD_TX_DATA_WR_ULPMODE_S)
@@ -766,6 +798,7 @@ enum fw_cmd_opcodes {
FW_DEVLOG_CMD = 0x25,
FW_CLIP_CMD = 0x28,
FW_PTP_CMD = 0x3e,
+ FW_HMA_CMD = 0x3f,
FW_LASTC2E_CMD = 0x40,
FW_ERROR_CMD = 0x80,
FW_DEBUG_CMD = 0x81,
@@ -1119,6 +1152,12 @@ enum fw_caps_config_iscsi {
FW_CAPS_CONFIG_ISCSI_TARGET_CNXOFLD = 0x00000008,
};
+enum fw_caps_config_crypto {
+ FW_CAPS_CONFIG_CRYPTO_LOOKASIDE = 0x00000001,
+ FW_CAPS_CONFIG_TLS_INLINE = 0x00000002,
+ FW_CAPS_CONFIG_IPSEC_INLINE = 0x00000004,
+};
+
enum fw_caps_config_fcoe {
FW_CAPS_CONFIG_FCOE_INITIATOR = 0x00000001,
FW_CAPS_CONFIG_FCOE_TARGET = 0x00000002,
@@ -1132,6 +1171,7 @@ enum fw_memtype_cf {
FW_MEMTYPE_CF_FLASH = 0x4,
FW_MEMTYPE_CF_INTERNAL = 0x5,
FW_MEMTYPE_CF_EXTMEM1 = 0x6,
+ FW_MEMTYPE_CF_HMA = 0x7,
};
struct fw_caps_config_cmd {
@@ -1210,6 +1250,9 @@ enum fw_params_param_dev {
FW_PARAMS_PARAM_DEV_RI_FR_NSMR_TPTE_WR = 0x1C,
FW_PARAMS_PARAM_DEV_FILTER2_WR = 0x1D,
FW_PARAMS_PARAM_DEV_MPSBGMAP = 0x1E,
+ FW_PARAMS_PARAM_DEV_HMA_SIZE = 0x20,
+ FW_PARAMS_PARAM_DEV_RDMA_WRITE_WITH_IMM = 0x21,
+ FW_PARAMS_PARAM_DEV_RI_WRITE_CMPL_WR = 0x24,
};
/*
@@ -1241,6 +1284,8 @@ enum fw_params_param_pfvf {
FW_PARAMS_PARAM_PFVF_SQRQ_END = 0x16,
FW_PARAMS_PARAM_PFVF_CQ_START = 0x17,
FW_PARAMS_PARAM_PFVF_CQ_END = 0x18,
+ FW_PARAMS_PARAM_PFVF_SRQ_START = 0x19,
+ FW_PARAMS_PARAM_PFVF_SRQ_END = 0x1A,
FW_PARAMS_PARAM_PFVF_SCHEDCLASS_ETH = 0x20,
FW_PARAMS_PARAM_PFVF_VIID = 0x24,
FW_PARAMS_PARAM_PFVF_CPMASK = 0x25,
@@ -1258,6 +1303,8 @@ enum fw_params_param_pfvf {
FW_PARAMS_PARAM_PFVF_CPLFW4MSG_ENCAP = 0x31,
FW_PARAMS_PARAM_PFVF_HPFILTER_START = 0x32,
FW_PARAMS_PARAM_PFVF_HPFILTER_END = 0x33,
+ FW_PARAMS_PARAM_PFVF_TLS_START = 0x34,
+ FW_PARAMS_PARAM_PFVF_TLS_END = 0x35,
FW_PARAMS_PARAM_PFVF_NCRYPTO_LOOKASIDE = 0x39,
FW_PARAMS_PARAM_PFVF_PORT_CAPS32 = 0x3A,
};
@@ -3435,6 +3482,59 @@ struct fw_debug_cmd {
#define FW_DEBUG_CMD_TYPE_G(x) \
(((x) >> FW_DEBUG_CMD_TYPE_S) & FW_DEBUG_CMD_TYPE_M)
+struct fw_hma_cmd {
+ __be32 op_pkd;
+ __be32 retval_len16;
+ __be32 mode_to_pcie_params;
+ __be32 naddr_size;
+ __be32 addr_size_pkd;
+ __be32 r6;
+ __be64 phy_address[5];
+};
+
+#define FW_HMA_CMD_MODE_S 31
+#define FW_HMA_CMD_MODE_M 0x1
+#define FW_HMA_CMD_MODE_V(x) ((x) << FW_HMA_CMD_MODE_S)
+#define FW_HMA_CMD_MODE_G(x) \
+ (((x) >> FW_HMA_CMD_MODE_S) & FW_HMA_CMD_MODE_M)
+#define FW_HMA_CMD_MODE_F FW_HMA_CMD_MODE_V(1U)
+
+#define FW_HMA_CMD_SOC_S 30
+#define FW_HMA_CMD_SOC_M 0x1
+#define FW_HMA_CMD_SOC_V(x) ((x) << FW_HMA_CMD_SOC_S)
+#define FW_HMA_CMD_SOC_G(x) (((x) >> FW_HMA_CMD_SOC_S) & FW_HMA_CMD_SOC_M)
+#define FW_HMA_CMD_SOC_F FW_HMA_CMD_SOC_V(1U)
+
+#define FW_HMA_CMD_EOC_S 29
+#define FW_HMA_CMD_EOC_M 0x1
+#define FW_HMA_CMD_EOC_V(x) ((x) << FW_HMA_CMD_EOC_S)
+#define FW_HMA_CMD_EOC_G(x) (((x) >> FW_HMA_CMD_EOC_S) & FW_HMA_CMD_EOC_M)
+#define FW_HMA_CMD_EOC_F FW_HMA_CMD_EOC_V(1U)
+
+#define FW_HMA_CMD_PCIE_PARAMS_S 0
+#define FW_HMA_CMD_PCIE_PARAMS_M 0x7ffffff
+#define FW_HMA_CMD_PCIE_PARAMS_V(x) ((x) << FW_HMA_CMD_PCIE_PARAMS_S)
+#define FW_HMA_CMD_PCIE_PARAMS_G(x) \
+ (((x) >> FW_HMA_CMD_PCIE_PARAMS_S) & FW_HMA_CMD_PCIE_PARAMS_M)
+
+#define FW_HMA_CMD_NADDR_S 12
+#define FW_HMA_CMD_NADDR_M 0x3f
+#define FW_HMA_CMD_NADDR_V(x) ((x) << FW_HMA_CMD_NADDR_S)
+#define FW_HMA_CMD_NADDR_G(x) \
+ (((x) >> FW_HMA_CMD_NADDR_S) & FW_HMA_CMD_NADDR_M)
+
+#define FW_HMA_CMD_SIZE_S 0
+#define FW_HMA_CMD_SIZE_M 0xfff
+#define FW_HMA_CMD_SIZE_V(x) ((x) << FW_HMA_CMD_SIZE_S)
+#define FW_HMA_CMD_SIZE_G(x) \
+ (((x) >> FW_HMA_CMD_SIZE_S) & FW_HMA_CMD_SIZE_M)
+
+#define FW_HMA_CMD_ADDR_SIZE_S 11
+#define FW_HMA_CMD_ADDR_SIZE_M 0x1fffff
+#define FW_HMA_CMD_ADDR_SIZE_V(x) ((x) << FW_HMA_CMD_ADDR_SIZE_S)
+#define FW_HMA_CMD_ADDR_SIZE_G(x) \
+ (((x) >> FW_HMA_CMD_ADDR_SIZE_S) & FW_HMA_CMD_ADDR_SIZE_M)
+
enum pcie_fw_eval {
PCIE_FW_EVAL_CRASH = 0,
};
@@ -3778,4 +3878,122 @@ struct fw_crypto_lookaside_wr {
(((x) >> FW_CRYPTO_LOOKASIDE_WR_HASH_SIZE_S) & \
FW_CRYPTO_LOOKASIDE_WR_HASH_SIZE_M)
+struct fw_tlstx_data_wr {
+ __be32 op_to_immdlen;
+ __be32 flowid_len16;
+ __be32 plen;
+ __be32 lsodisable_to_flags;
+ __be32 r5;
+ __be32 ctxloc_to_exp;
+ __be16 mfs;
+ __be16 adjustedplen_pkd;
+ __be16 expinplenmax_pkd;
+ u8 pdusinplenmax_pkd;
+ u8 r10;
+};
+
+#define FW_TLSTX_DATA_WR_OPCODE_S 24
+#define FW_TLSTX_DATA_WR_OPCODE_M 0xff
+#define FW_TLSTX_DATA_WR_OPCODE_V(x) ((x) << FW_TLSTX_DATA_WR_OPCODE_S)
+#define FW_TLSTX_DATA_WR_OPCODE_G(x) \
+ (((x) >> FW_TLSTX_DATA_WR_OPCODE_S) & FW_TLSTX_DATA_WR_OPCODE_M)
+
+#define FW_TLSTX_DATA_WR_COMPL_S 21
+#define FW_TLSTX_DATA_WR_COMPL_M 0x1
+#define FW_TLSTX_DATA_WR_COMPL_V(x) ((x) << FW_TLSTX_DATA_WR_COMPL_S)
+#define FW_TLSTX_DATA_WR_COMPL_G(x) \
+ (((x) >> FW_TLSTX_DATA_WR_COMPL_S) & FW_TLSTX_DATA_WR_COMPL_M)
+#define FW_TLSTX_DATA_WR_COMPL_F FW_TLSTX_DATA_WR_COMPL_V(1U)
+
+#define FW_TLSTX_DATA_WR_IMMDLEN_S 0
+#define FW_TLSTX_DATA_WR_IMMDLEN_M 0xff
+#define FW_TLSTX_DATA_WR_IMMDLEN_V(x) ((x) << FW_TLSTX_DATA_WR_IMMDLEN_S)
+#define FW_TLSTX_DATA_WR_IMMDLEN_G(x) \
+ (((x) >> FW_TLSTX_DATA_WR_IMMDLEN_S) & FW_TLSTX_DATA_WR_IMMDLEN_M)
+
+#define FW_TLSTX_DATA_WR_FLOWID_S 8
+#define FW_TLSTX_DATA_WR_FLOWID_M 0xfffff
+#define FW_TLSTX_DATA_WR_FLOWID_V(x) ((x) << FW_TLSTX_DATA_WR_FLOWID_S)
+#define FW_TLSTX_DATA_WR_FLOWID_G(x) \
+ (((x) >> FW_TLSTX_DATA_WR_FLOWID_S) & FW_TLSTX_DATA_WR_FLOWID_M)
+
+#define FW_TLSTX_DATA_WR_LEN16_S 0
+#define FW_TLSTX_DATA_WR_LEN16_M 0xff
+#define FW_TLSTX_DATA_WR_LEN16_V(x) ((x) << FW_TLSTX_DATA_WR_LEN16_S)
+#define FW_TLSTX_DATA_WR_LEN16_G(x) \
+ (((x) >> FW_TLSTX_DATA_WR_LEN16_S) & FW_TLSTX_DATA_WR_LEN16_M)
+
+#define FW_TLSTX_DATA_WR_LSODISABLE_S 31
+#define FW_TLSTX_DATA_WR_LSODISABLE_M 0x1
+#define FW_TLSTX_DATA_WR_LSODISABLE_V(x) \
+ ((x) << FW_TLSTX_DATA_WR_LSODISABLE_S)
+#define FW_TLSTX_DATA_WR_LSODISABLE_G(x) \
+ (((x) >> FW_TLSTX_DATA_WR_LSODISABLE_S) & FW_TLSTX_DATA_WR_LSODISABLE_M)
+#define FW_TLSTX_DATA_WR_LSODISABLE_F FW_TLSTX_DATA_WR_LSODISABLE_V(1U)
+
+#define FW_TLSTX_DATA_WR_ALIGNPLD_S 30
+#define FW_TLSTX_DATA_WR_ALIGNPLD_M 0x1
+#define FW_TLSTX_DATA_WR_ALIGNPLD_V(x) ((x) << FW_TLSTX_DATA_WR_ALIGNPLD_S)
+#define FW_TLSTX_DATA_WR_ALIGNPLD_G(x) \
+ (((x) >> FW_TLSTX_DATA_WR_ALIGNPLD_S) & FW_TLSTX_DATA_WR_ALIGNPLD_M)
+#define FW_TLSTX_DATA_WR_ALIGNPLD_F FW_TLSTX_DATA_WR_ALIGNPLD_V(1U)
+
+#define FW_TLSTX_DATA_WR_ALIGNPLDSHOVE_S 29
+#define FW_TLSTX_DATA_WR_ALIGNPLDSHOVE_M 0x1
+#define FW_TLSTX_DATA_WR_ALIGNPLDSHOVE_V(x) \
+ ((x) << FW_TLSTX_DATA_WR_ALIGNPLDSHOVE_S)
+#define FW_TLSTX_DATA_WR_ALIGNPLDSHOVE_G(x) \
+ (((x) >> FW_TLSTX_DATA_WR_ALIGNPLDSHOVE_S) & \
+ FW_TLSTX_DATA_WR_ALIGNPLDSHOVE_M)
+#define FW_TLSTX_DATA_WR_ALIGNPLDSHOVE_F FW_TLSTX_DATA_WR_ALIGNPLDSHOVE_V(1U)
+
+#define FW_TLSTX_DATA_WR_FLAGS_S 0
+#define FW_TLSTX_DATA_WR_FLAGS_M 0xfffffff
+#define FW_TLSTX_DATA_WR_FLAGS_V(x) ((x) << FW_TLSTX_DATA_WR_FLAGS_S)
+#define FW_TLSTX_DATA_WR_FLAGS_G(x) \
+ (((x) >> FW_TLSTX_DATA_WR_FLAGS_S) & FW_TLSTX_DATA_WR_FLAGS_M)
+
+#define FW_TLSTX_DATA_WR_CTXLOC_S 30
+#define FW_TLSTX_DATA_WR_CTXLOC_M 0x3
+#define FW_TLSTX_DATA_WR_CTXLOC_V(x) ((x) << FW_TLSTX_DATA_WR_CTXLOC_S)
+#define FW_TLSTX_DATA_WR_CTXLOC_G(x) \
+ (((x) >> FW_TLSTX_DATA_WR_CTXLOC_S) & FW_TLSTX_DATA_WR_CTXLOC_M)
+
+#define FW_TLSTX_DATA_WR_IVDSGL_S 29
+#define FW_TLSTX_DATA_WR_IVDSGL_M 0x1
+#define FW_TLSTX_DATA_WR_IVDSGL_V(x) ((x) << FW_TLSTX_DATA_WR_IVDSGL_S)
+#define FW_TLSTX_DATA_WR_IVDSGL_G(x) \
+ (((x) >> FW_TLSTX_DATA_WR_IVDSGL_S) & FW_TLSTX_DATA_WR_IVDSGL_M)
+#define FW_TLSTX_DATA_WR_IVDSGL_F FW_TLSTX_DATA_WR_IVDSGL_V(1U)
+
+#define FW_TLSTX_DATA_WR_KEYSIZE_S 24
+#define FW_TLSTX_DATA_WR_KEYSIZE_M 0x1f
+#define FW_TLSTX_DATA_WR_KEYSIZE_V(x) ((x) << FW_TLSTX_DATA_WR_KEYSIZE_S)
+#define FW_TLSTX_DATA_WR_KEYSIZE_G(x) \
+ (((x) >> FW_TLSTX_DATA_WR_KEYSIZE_S) & FW_TLSTX_DATA_WR_KEYSIZE_M)
+
+#define FW_TLSTX_DATA_WR_NUMIVS_S 14
+#define FW_TLSTX_DATA_WR_NUMIVS_M 0xff
+#define FW_TLSTX_DATA_WR_NUMIVS_V(x) ((x) << FW_TLSTX_DATA_WR_NUMIVS_S)
+#define FW_TLSTX_DATA_WR_NUMIVS_G(x) \
+ (((x) >> FW_TLSTX_DATA_WR_NUMIVS_S) & FW_TLSTX_DATA_WR_NUMIVS_M)
+
+#define FW_TLSTX_DATA_WR_EXP_S 0
+#define FW_TLSTX_DATA_WR_EXP_M 0x3fff
+#define FW_TLSTX_DATA_WR_EXP_V(x) ((x) << FW_TLSTX_DATA_WR_EXP_S)
+#define FW_TLSTX_DATA_WR_EXP_G(x) \
+ (((x) >> FW_TLSTX_DATA_WR_EXP_S) & FW_TLSTX_DATA_WR_EXP_M)
+
+#define FW_TLSTX_DATA_WR_ADJUSTEDPLEN_S 1
+#define FW_TLSTX_DATA_WR_ADJUSTEDPLEN_V(x) \
+ ((x) << FW_TLSTX_DATA_WR_ADJUSTEDPLEN_S)
+
+#define FW_TLSTX_DATA_WR_EXPINPLENMAX_S 4
+#define FW_TLSTX_DATA_WR_EXPINPLENMAX_V(x) \
+ ((x) << FW_TLSTX_DATA_WR_EXPINPLENMAX_S)
+
+#define FW_TLSTX_DATA_WR_PDUSINPLENMAX_S 2
+#define FW_TLSTX_DATA_WR_PDUSINPLENMAX_V(x) \
+ ((x) << FW_TLSTX_DATA_WR_PDUSINPLENMAX_S)
+
#endif /* _T4FW_INTERFACE_H_ */
diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c
index b7e79e64d2ed..9a81b52307a9 100644
--- a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c
@@ -155,8 +155,6 @@ void t4vf_os_link_changed(struct adapter *adapter, int pidx, int link_ok)
const char *fc;
const struct port_info *pi = netdev_priv(dev);
- netif_carrier_on(dev);
-
switch (pi->link_cfg.speed) {
case 100:
s = "100Mbps";
@@ -202,7 +200,6 @@ void t4vf_os_link_changed(struct adapter *adapter, int pidx, int link_ok)
netdev_info(dev, "link up, %s, full-duplex, %s PAUSE\n", s, fc);
} else {
- netif_carrier_off(dev);
netdev_info(dev, "link down\n");
}
}
@@ -278,6 +275,17 @@ static int link_start(struct net_device *dev)
*/
if (ret == 0)
ret = t4vf_enable_vi(pi->adapter, pi->viid, true, true);
+
+ /* The Virtual Interfaces are connected to an internal switch on the
+ * chip which allows VIs attached to the same port to talk to each
+ * other even when the port link is down. As a result, we generally
+ * want to always report a VI's link as being "up", provided there are
+ * no errors in enabling vi.
+ */
+
+ if (ret == 0)
+ netif_carrier_on(dev);
+
return ret;
}
@@ -1281,22 +1289,22 @@ static void fw_caps_to_lmm(enum fw_port_type port_type,
case FW_PORT_TYPE_KR:
SET_LMM(Backplane);
- SET_LMM(10000baseKR_Full);
+ FW_CAPS_TO_LMM(SPEED_10G, 10000baseKR_Full);
break;
case FW_PORT_TYPE_BP_AP:
SET_LMM(Backplane);
- SET_LMM(10000baseR_FEC);
- SET_LMM(10000baseKR_Full);
- SET_LMM(1000baseKX_Full);
+ FW_CAPS_TO_LMM(SPEED_1G, 1000baseKX_Full);
+ FW_CAPS_TO_LMM(SPEED_10G, 10000baseR_FEC);
+ FW_CAPS_TO_LMM(SPEED_10G, 10000baseKR_Full);
break;
case FW_PORT_TYPE_BP4_AP:
SET_LMM(Backplane);
- SET_LMM(10000baseR_FEC);
- SET_LMM(10000baseKR_Full);
- SET_LMM(1000baseKX_Full);
- SET_LMM(10000baseKX4_Full);
+ FW_CAPS_TO_LMM(SPEED_1G, 1000baseKX_Full);
+ FW_CAPS_TO_LMM(SPEED_10G, 10000baseR_FEC);
+ FW_CAPS_TO_LMM(SPEED_10G, 10000baseKR_Full);
+ FW_CAPS_TO_LMM(SPEED_10G, 10000baseKX4_Full);
break;
case FW_PORT_TYPE_FIBER_XFI:
@@ -1312,18 +1320,24 @@ static void fw_caps_to_lmm(enum fw_port_type port_type,
case FW_PORT_TYPE_BP40_BA:
case FW_PORT_TYPE_QSFP:
SET_LMM(FIBRE);
- SET_LMM(40000baseSR4_Full);
+ FW_CAPS_TO_LMM(SPEED_1G, 1000baseT_Full);
+ FW_CAPS_TO_LMM(SPEED_10G, 10000baseT_Full);
+ FW_CAPS_TO_LMM(SPEED_40G, 40000baseSR4_Full);
break;
case FW_PORT_TYPE_CR_QSFP:
case FW_PORT_TYPE_SFP28:
SET_LMM(FIBRE);
- SET_LMM(25000baseCR_Full);
+ FW_CAPS_TO_LMM(SPEED_1G, 1000baseT_Full);
+ FW_CAPS_TO_LMM(SPEED_10G, 10000baseT_Full);
+ FW_CAPS_TO_LMM(SPEED_25G, 25000baseCR_Full);
break;
case FW_PORT_TYPE_KR_SFP28:
SET_LMM(Backplane);
- SET_LMM(25000baseKR_Full);
+ FW_CAPS_TO_LMM(SPEED_1G, 1000baseT_Full);
+ FW_CAPS_TO_LMM(SPEED_10G, 10000baseKR_Full);
+ FW_CAPS_TO_LMM(SPEED_25G, 25000baseKR_Full);
break;
case FW_PORT_TYPE_KR_XLAUI:
@@ -1335,13 +1349,18 @@ static void fw_caps_to_lmm(enum fw_port_type port_type,
case FW_PORT_TYPE_CR2_QSFP:
SET_LMM(FIBRE);
- SET_LMM(50000baseSR2_Full);
+ FW_CAPS_TO_LMM(SPEED_50G, 50000baseSR2_Full);
break;
case FW_PORT_TYPE_KR4_100G:
case FW_PORT_TYPE_CR4_QSFP:
SET_LMM(FIBRE);
- SET_LMM(100000baseCR4_Full);
+ FW_CAPS_TO_LMM(SPEED_1G, 1000baseT_Full);
+ FW_CAPS_TO_LMM(SPEED_10G, 10000baseSR_Full);
+ FW_CAPS_TO_LMM(SPEED_40G, 40000baseSR4_Full);
+ FW_CAPS_TO_LMM(SPEED_25G, 25000baseCR_Full);
+ FW_CAPS_TO_LMM(SPEED_50G, 50000baseCR2_Full);
+ FW_CAPS_TO_LMM(SPEED_100G, 100000baseCR4_Full);
break;
default:
@@ -2383,11 +2402,11 @@ struct cxgb4vf_debugfs_entry {
};
static struct cxgb4vf_debugfs_entry debugfs_files[] = {
- { "mboxlog", S_IRUGO, &mboxlog_fops },
- { "sge_qinfo", S_IRUGO, &sge_qinfo_debugfs_fops },
- { "sge_qstats", S_IRUGO, &sge_qstats_proc_fops },
- { "resources", S_IRUGO, &resources_proc_fops },
- { "interfaces", S_IRUGO, &interfaces_proc_fops },
+ { "mboxlog", 0444, &mboxlog_fops },
+ { "sge_qinfo", 0444, &sge_qinfo_debugfs_fops },
+ { "sge_qstats", 0444, &sge_qstats_proc_fops },
+ { "resources", 0444, &resources_proc_fops },
+ { "interfaces", 0444, &interfaces_proc_fops },
};
/*
diff --git a/drivers/net/ethernet/cirrus/mac89x0.c b/drivers/net/ethernet/cirrus/mac89x0.c
index 977d4c2c759d..3f8fe8fd79cc 100644
--- a/drivers/net/ethernet/cirrus/mac89x0.c
+++ b/drivers/net/ethernet/cirrus/mac89x0.c
@@ -56,21 +56,11 @@
local_irq_{dis,en}able()
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
static const char version[] =
"cs89x0.c:v1.02 11/26/96 Russell Nelson <nelson@crynwr.com>\n";
-/* ======================= configure the driver here ======================= */
-
-/* use 0 for production, 1 for verification, >2 for debug */
-#ifndef NET_DEBUG
-#define NET_DEBUG 0
-#endif
-
-/* ======================= end of configuration ======================= */
-
-
-/* Always include 'config.h' first in case the user wants to turn on
- or override something. */
#include <linux/module.h>
/*
@@ -93,6 +83,7 @@ static const char version[] =
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/netdevice.h>
+#include <linux/platform_device.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/delay.h>
@@ -105,24 +96,22 @@ static const char version[] =
#include "cs89x0.h"
-static unsigned int net_debug = NET_DEBUG;
+static int debug = -1;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "debug message level");
/* Information that need to be kept for each board. */
struct net_local {
+ int msg_enable;
int chip_type; /* one of: CS8900, CS8920, CS8920M */
char chip_revision; /* revision letter of the chip ('A'...) */
int send_cmd; /* the propercommand used to send a packet. */
int rx_mode;
int curr_rx_cfg;
int send_underrun; /* keep track of how many underruns in a row we get */
- struct sk_buff *skb;
};
/* Index to functions, as function prototypes. */
-
-#if 0
-extern void reset_chip(struct net_device *dev);
-#endif
static int net_open(struct net_device *dev);
static int net_send_packet(struct sk_buff *skb, struct net_device *dev);
static irqreturn_t net_interrupt(int irq, void *dev_id);
@@ -132,10 +121,6 @@ static int net_close(struct net_device *dev);
static struct net_device_stats *net_get_stats(struct net_device *dev);
static int set_mac_address(struct net_device *dev, void *addr);
-
-/* Example routines you must write ;->. */
-#define tx_done(dev) 1
-
/* For reading/writing registers ISA-style */
static inline int
readreg_io(struct net_device *dev, int portno)
@@ -176,12 +161,10 @@ static const struct net_device_ops mac89x0_netdev_ops = {
/* Probe for the CS8900 card in slot E. We won't bother looking
anywhere else until we have a really good reason to do so. */
-struct net_device * __init mac89x0_probe(int unit)
+static int mac89x0_device_probe(struct platform_device *pdev)
{
struct net_device *dev;
- static int once_is_enough;
struct net_local *lp;
- static unsigned version_printed;
int i, slot;
unsigned rev_type = 0;
unsigned long ioaddr;
@@ -189,21 +172,9 @@ struct net_device * __init mac89x0_probe(int unit)
int err = -ENODEV;
struct nubus_rsrc *fres;
- if (!MACH_IS_MAC)
- return ERR_PTR(-ENODEV);
-
dev = alloc_etherdev(sizeof(struct net_local));
if (!dev)
- return ERR_PTR(-ENOMEM);
-
- if (unit >= 0) {
- sprintf(dev->name, "eth%d", unit);
- netdev_boot_setup_check(dev);
- }
-
- if (once_is_enough)
- goto out;
- once_is_enough = 1;
+ return -ENOMEM;
/* We might have to parameterize this later */
slot = 0xE;
@@ -230,9 +201,13 @@ struct net_device * __init mac89x0_probe(int unit)
if (sig != swab16(CHIP_EISA_ID_SIG))
goto out;
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
/* Initialize the net_device structure. */
lp = netdev_priv(dev);
+ lp->msg_enable = netif_msg_init(debug, 0);
+
/* Fill in the 'dev' fields. */
dev->base_addr = ioaddr;
dev->mem_start = (unsigned long)
@@ -255,19 +230,16 @@ struct net_device * __init mac89x0_probe(int unit)
if (lp->chip_type != CS8900 && lp->chip_revision >= 'C')
lp->send_cmd = TX_NOW;
- if (net_debug && version_printed++ == 0)
- printk(version);
+ netif_dbg(lp, drv, dev, "%s", version);
- printk(KERN_INFO "%s: cs89%c0%s rev %c found at %#8lx",
- dev->name,
- lp->chip_type==CS8900?'0':'2',
- lp->chip_type==CS8920M?"M":"",
- lp->chip_revision,
- dev->base_addr);
+ pr_info("cs89%c0%s rev %c found at %#8lx\n",
+ lp->chip_type == CS8900 ? '0' : '2',
+ lp->chip_type == CS8920M ? "M" : "",
+ lp->chip_revision, dev->base_addr);
/* Try to read the MAC address */
if ((readreg(dev, PP_SelfST) & (EEPROM_PRESENT | EEPROM_OK)) == 0) {
- printk("\nmac89x0: No EEPROM, giving up now.\n");
+ pr_info("No EEPROM, giving up now.\n");
goto out1;
} else {
for (i = 0; i < ETH_ALEN; i += 2) {
@@ -282,39 +254,23 @@ struct net_device * __init mac89x0_probe(int unit)
/* print the IRQ and ethernet address. */
- printk(" IRQ %d ADDR %pM\n", dev->irq, dev->dev_addr);
+ pr_info("MAC %pM, IRQ %d\n", dev->dev_addr, dev->irq);
dev->netdev_ops = &mac89x0_netdev_ops;
err = register_netdev(dev);
if (err)
goto out1;
- return NULL;
+
+ platform_set_drvdata(pdev, dev);
+ return 0;
out1:
nubus_writew(0, dev->base_addr + ADD_PORT);
out:
free_netdev(dev);
- return ERR_PTR(err);
+ return err;
}
-#if 0
-/* This is useful for something, but I don't know what yet. */
-void __init reset_chip(struct net_device *dev)
-{
- int reset_start_time;
-
- writereg(dev, PP_SelfCTL, readreg(dev, PP_SelfCTL) | POWER_ON_RESET);
-
- /* wait 30 ms */
- msleep_interruptible(30);
-
- /* Wait until the chip is reset */
- reset_start_time = jiffies;
- while( (readreg(dev, PP_SelfST) & INIT_DONE) == 0 && jiffies - reset_start_time < 2)
- ;
-}
-#endif
-
/* Open/initialize the board. This is called (in the current kernel)
sometime after booting when the 'ifconfig' program is run.
@@ -374,11 +330,9 @@ net_send_packet(struct sk_buff *skb, struct net_device *dev)
struct net_local *lp = netdev_priv(dev);
unsigned long flags;
- if (net_debug > 3)
- printk("%s: sent %d byte packet of type %x\n",
- dev->name, skb->len,
- (skb->data[ETH_ALEN+ETH_ALEN] << 8)
- | skb->data[ETH_ALEN+ETH_ALEN+1]);
+ netif_dbg(lp, tx_queued, dev, "sent %d byte packet of type %x\n",
+ skb->len, skb->data[ETH_ALEN + ETH_ALEN] << 8 |
+ skb->data[ETH_ALEN + ETH_ALEN + 1]);
/* keep the upload from being interrupted, since we
ask the chip to start transmitting before the
@@ -416,11 +370,6 @@ static irqreturn_t net_interrupt(int irq, void *dev_id)
struct net_local *lp;
int ioaddr, status;
- if (dev == NULL) {
- printk ("net_interrupt(): irq %d for unknown device.\n", irq);
- return IRQ_NONE;
- }
-
ioaddr = dev->base_addr;
lp = netdev_priv(dev);
@@ -432,7 +381,7 @@ static irqreturn_t net_interrupt(int irq, void *dev_id)
faster than you can read them off, you're screwed. Hasta la
vista, baby! */
while ((status = swab16(nubus_readw(dev->base_addr + ISQ_PORT)))) {
- if (net_debug > 4)printk("%s: event=%04x\n", dev->name, status);
+ netif_dbg(lp, intr, dev, "status=%04x\n", status);
switch(status & ISQ_EVENT_MASK) {
case ISQ_RECEIVER_EVENT:
/* Got a packet(s). */
@@ -462,7 +411,7 @@ static irqreturn_t net_interrupt(int irq, void *dev_id)
netif_wake_queue(dev);
}
if (status & TX_UNDERRUN) {
- if (net_debug > 0) printk("%s: transmit underrun\n", dev->name);
+ netif_dbg(lp, tx_err, dev, "transmit underrun\n");
lp->send_underrun++;
if (lp->send_underrun == 3) lp->send_cmd = TX_AFTER_381;
else if (lp->send_underrun == 6) lp->send_cmd = TX_AFTER_ALL;
@@ -483,6 +432,7 @@ static irqreturn_t net_interrupt(int irq, void *dev_id)
static void
net_rx(struct net_device *dev)
{
+ struct net_local *lp = netdev_priv(dev);
struct sk_buff *skb;
int status, length;
@@ -506,7 +456,6 @@ net_rx(struct net_device *dev)
/* Malloc up new buffer. */
skb = alloc_skb(length, GFP_ATOMIC);
if (skb == NULL) {
- printk("%s: Memory squeeze, dropping packet.\n", dev->name);
dev->stats.rx_dropped++;
return;
}
@@ -515,10 +464,9 @@ net_rx(struct net_device *dev)
skb_copy_to_linear_data(skb, (void *)(dev->mem_start + PP_RxFrame),
length);
- if (net_debug > 3)printk("%s: received %d byte packet of type %x\n",
- dev->name, length,
- (skb->data[ETH_ALEN+ETH_ALEN] << 8)
- | skb->data[ETH_ALEN+ETH_ALEN+1]);
+ netif_dbg(lp, rx_status, dev, "received %d byte packet of type %x\n",
+ length, skb->data[ETH_ALEN + ETH_ALEN] << 8 |
+ skb->data[ETH_ALEN + ETH_ALEN + 1]);
skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
@@ -594,7 +542,7 @@ static int set_mac_address(struct net_device *dev, void *addr)
return -EADDRNOTAVAIL;
memcpy(dev->dev_addr, saddr->sa_data, ETH_ALEN);
- printk("%s: Setting MAC address to %pM\n", dev->name, dev->dev_addr);
+ netdev_info(dev, "Setting MAC address to %pM\n", dev->dev_addr);
/* set the Ethernet address */
for (i=0; i < ETH_ALEN/2; i++)
@@ -603,32 +551,24 @@ static int set_mac_address(struct net_device *dev, void *addr)
return 0;
}
-#ifdef MODULE
-
-static struct net_device *dev_cs89x0;
-static int debug;
-
-module_param(debug, int, 0);
-MODULE_PARM_DESC(debug, "CS89[02]0 debug level (0-5)");
MODULE_LICENSE("GPL");
-int __init
-init_module(void)
+static int mac89x0_device_remove(struct platform_device *pdev)
{
- net_debug = debug;
- dev_cs89x0 = mac89x0_probe(-1);
- if (IS_ERR(dev_cs89x0)) {
- printk(KERN_WARNING "mac89x0.c: No card found\n");
- return PTR_ERR(dev_cs89x0);
- }
+ struct net_device *dev = platform_get_drvdata(pdev);
+
+ unregister_netdev(dev);
+ nubus_writew(0, dev->base_addr + ADD_PORT);
+ free_netdev(dev);
return 0;
}
-void
-cleanup_module(void)
-{
- unregister_netdev(dev_cs89x0);
- nubus_writew(0, dev_cs89x0->base_addr + ADD_PORT);
- free_netdev(dev_cs89x0);
-}
-#endif /* MODULE */
+static struct platform_driver mac89x0_platform_driver = {
+ .probe = mac89x0_device_probe,
+ .remove = mac89x0_device_remove,
+ .driver = {
+ .name = "mac89x0",
+ },
+};
+
+module_platform_driver(mac89x0_platform_driver);
diff --git a/drivers/net/ethernet/cisco/enic/enic.h b/drivers/net/ethernet/cisco/enic/enic.h
index 9b218f0e5a4c..0dd64acd2a3f 100644
--- a/drivers/net/ethernet/cisco/enic/enic.h
+++ b/drivers/net/ethernet/cisco/enic/enic.h
@@ -33,7 +33,7 @@
#define DRV_NAME "enic"
#define DRV_DESCRIPTION "Cisco VIC Ethernet NIC Driver"
-#define DRV_VERSION "2.3.0.45"
+#define DRV_VERSION "2.3.0.53"
#define DRV_COPYRIGHT "Copyright 2008-2013 Cisco Systems, Inc"
#define ENIC_BARS_MAX 6
@@ -140,6 +140,7 @@ struct enic_rfs_flw_tbl {
struct vxlan_offload {
u16 vxlan_udp_port_number;
u8 patch_level;
+ u8 flags;
};
/* Per-instance private data structure */
diff --git a/drivers/net/ethernet/cisco/enic/enic_ethtool.c b/drivers/net/ethernet/cisco/enic/enic_ethtool.c
index efb9333c7cf8..869006c2002d 100644
--- a/drivers/net/ethernet/cisco/enic/enic_ethtool.c
+++ b/drivers/net/ethernet/cisco/enic/enic_ethtool.c
@@ -474,6 +474,39 @@ static int enic_grxclsrule(struct enic *enic, struct ethtool_rxnfc *cmd)
return 0;
}
+static int enic_get_rx_flow_hash(struct enic *enic, struct ethtool_rxnfc *cmd)
+{
+ cmd->data = 0;
+
+ switch (cmd->flow_type) {
+ case TCP_V6_FLOW:
+ case TCP_V4_FLOW:
+ cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+ /* Fall through */
+ case UDP_V6_FLOW:
+ case UDP_V4_FLOW:
+ if (vnic_dev_capable_udp_rss(enic->vdev))
+ cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+ /* Fall through */
+ case SCTP_V4_FLOW:
+ case AH_ESP_V4_FLOW:
+ case AH_V4_FLOW:
+ case ESP_V4_FLOW:
+ case SCTP_V6_FLOW:
+ case AH_ESP_V6_FLOW:
+ case AH_V6_FLOW:
+ case ESP_V6_FLOW:
+ case IPV4_FLOW:
+ case IPV6_FLOW:
+ cmd->data |= RXH_IP_SRC | RXH_IP_DST;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int enic_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
u32 *rule_locs)
{
@@ -500,6 +533,9 @@ static int enic_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
ret = enic_grxclsrule(enic, cmd);
spin_unlock_bh(&enic->rfs_h.lock);
break;
+ case ETHTOOL_GRXFH:
+ ret = enic_get_rx_flow_hash(enic, cmd);
+ break;
default:
ret = -EOPNOTSUPP;
break;
diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c
index f202ba72a811..81684acf52af 100644
--- a/drivers/net/ethernet/cisco/enic/enic_main.c
+++ b/drivers/net/ethernet/cisco/enic/enic_main.c
@@ -191,8 +191,16 @@ static void enic_udp_tunnel_add(struct net_device *netdev,
goto error;
}
- if (ti->sa_family != AF_INET) {
- netdev_info(netdev, "vxlan: only IPv4 offload supported");
+ switch (ti->sa_family) {
+ case AF_INET6:
+ if (!(enic->vxlan.flags & ENIC_VXLAN_OUTER_IPV6)) {
+ netdev_info(netdev, "vxlan: only IPv4 offload supported");
+ goto error;
+ }
+ /* Fall through */
+ case AF_INET:
+ break;
+ default:
goto error;
}
@@ -204,6 +212,11 @@ static void enic_udp_tunnel_add(struct net_device *netdev,
goto error;
}
+ if ((vnic_dev_get_res_count(enic->vdev, RES_TYPE_WQ) != 1) &&
+ !(enic->vxlan.flags & ENIC_VXLAN_MULTI_WQ)) {
+ netdev_info(netdev, "vxlan: vxlan offload with multi wq not supported on this adapter");
+ goto error;
+ }
err = vnic_dev_overlay_offload_cfg(enic->vdev,
OVERLAY_CFG_VXLAN_PORT_UPDATE,
@@ -238,9 +251,8 @@ static void enic_udp_tunnel_del(struct net_device *netdev,
spin_lock_bh(&enic->devcmd_lock);
- if ((ti->sa_family != AF_INET) ||
- ((ntohs(ti->port) != enic->vxlan.vxlan_udp_port_number)) ||
- (ti->type != UDP_TUNNEL_TYPE_VXLAN)) {
+ if ((ntohs(ti->port) != enic->vxlan.vxlan_udp_port_number) ||
+ ti->type != UDP_TUNNEL_TYPE_VXLAN) {
netdev_info(netdev, "udp_tnl: port:%d, sa_family: %d, type: %d not offloaded",
ntohs(ti->port), ti->sa_family, ti->type);
goto unlock;
@@ -271,22 +283,37 @@ static netdev_features_t enic_features_check(struct sk_buff *skb,
struct enic *enic = netdev_priv(dev);
struct udphdr *udph;
u16 port = 0;
- u16 proto;
+ u8 proto;
if (!skb->encapsulation)
return features;
features = vxlan_features_check(skb, features);
- /* hardware only supports IPv4 vxlan tunnel */
- if (vlan_get_protocol(skb) != htons(ETH_P_IP))
+ switch (vlan_get_protocol(skb)) {
+ case htons(ETH_P_IPV6):
+ if (!(enic->vxlan.flags & ENIC_VXLAN_OUTER_IPV6))
+ goto out;
+ proto = ipv6_hdr(skb)->nexthdr;
+ break;
+ case htons(ETH_P_IP):
+ proto = ip_hdr(skb)->protocol;
+ break;
+ default:
goto out;
+ }
- /* hardware does not support offload of ipv6 inner pkt */
- if (eth->h_proto != ntohs(ETH_P_IP))
+ switch (eth->h_proto) {
+ case ntohs(ETH_P_IPV6):
+ if (!(enic->vxlan.flags & ENIC_VXLAN_INNER_IPV6))
+ goto out;
+ /* Fall through */
+ case ntohs(ETH_P_IP):
+ break;
+ default:
goto out;
+ }
- proto = ip_hdr(skb)->protocol;
if (proto == IPPROTO_UDP) {
udph = udp_hdr(skb);
@@ -635,12 +662,25 @@ static int enic_queue_wq_skb_csum_l4(struct enic *enic, struct vnic_wq *wq,
static void enic_preload_tcp_csum_encap(struct sk_buff *skb)
{
- if (skb->protocol == cpu_to_be16(ETH_P_IP)) {
+ const struct ethhdr *eth = (struct ethhdr *)skb_inner_mac_header(skb);
+
+ switch (eth->h_proto) {
+ case ntohs(ETH_P_IP):
inner_ip_hdr(skb)->check = 0;
inner_tcp_hdr(skb)->check =
~csum_tcpudp_magic(inner_ip_hdr(skb)->saddr,
inner_ip_hdr(skb)->daddr, 0,
IPPROTO_TCP, 0);
+ break;
+ case ntohs(ETH_P_IPV6):
+ inner_tcp_hdr(skb)->check =
+ ~csum_ipv6_magic(&inner_ipv6_hdr(skb)->saddr,
+ &inner_ipv6_hdr(skb)->daddr, 0,
+ IPPROTO_TCP, 0);
+ break;
+ default:
+ WARN_ONCE(1, "Non ipv4/ipv6 inner pkt for encap offload");
+ break;
}
}
@@ -1898,6 +1938,8 @@ static int enic_open(struct net_device *netdev)
}
for (i = 0; i < enic->rq_count; i++) {
+ /* enable rq before updating rq desc */
+ vnic_rq_enable(&enic->rq[i]);
vnic_rq_fill(&enic->rq[i], enic_rq_alloc_buf);
/* Need at least one buffer on ring to get going */
if (vnic_rq_desc_used(&enic->rq[i]) == 0) {
@@ -1909,8 +1951,6 @@ static int enic_open(struct net_device *netdev)
for (i = 0; i < enic->wq_count; i++)
vnic_wq_enable(&enic->wq[i]);
- for (i = 0; i < enic->rq_count; i++)
- vnic_rq_enable(&enic->rq[i]);
if (!enic_is_dynamic(enic) && !enic_is_sriov_vf(enic))
enic_dev_add_station_addr(enic);
@@ -1936,8 +1976,12 @@ static int enic_open(struct net_device *netdev)
return 0;
err_out_free_rq:
- for (i = 0; i < enic->rq_count; i++)
+ for (i = 0; i < enic->rq_count; i++) {
+ err = vnic_rq_disable(&enic->rq[i]);
+ if (err)
+ return err;
vnic_rq_clean(&enic->rq[i], enic_free_rq_buf);
+ }
enic_dev_notify_unset(enic);
err_out_free_intr:
enic_unset_affinity_hint(enic);
@@ -2151,9 +2195,10 @@ static int enic_dev_wait(struct vnic_dev *vdev,
static int enic_dev_open(struct enic *enic)
{
int err;
+ u32 flags = CMD_OPENF_IG_DESCCACHE;
err = enic_dev_wait(enic->vdev, vnic_dev_open,
- vnic_dev_open_done, 0);
+ vnic_dev_open_done, flags);
if (err)
dev_err(enic_get_dev(enic), "vNIC device open failed, err %d\n",
err);
@@ -2275,7 +2320,7 @@ static int enic_set_rss_nic_cfg(struct enic *enic)
{
struct device *dev = enic_get_dev(enic);
const u8 rss_default_cpu = 0;
- const u8 rss_hash_type = NIC_CFG_RSS_HASH_TYPE_IPV4 |
+ u8 rss_hash_type = NIC_CFG_RSS_HASH_TYPE_IPV4 |
NIC_CFG_RSS_HASH_TYPE_TCP_IPV4 |
NIC_CFG_RSS_HASH_TYPE_IPV6 |
NIC_CFG_RSS_HASH_TYPE_TCP_IPV6;
@@ -2283,6 +2328,8 @@ static int enic_set_rss_nic_cfg(struct enic *enic)
const u8 rss_base_cpu = 0;
u8 rss_enable = ENIC_SETTING(enic, RSS) && (enic->rq_count > 1);
+ if (vnic_dev_capable_udp_rss(enic->vdev))
+ rss_hash_type |= NIC_CFG_RSS_HASH_TYPE_UDP;
if (rss_enable) {
if (!enic_set_rsskey(enic)) {
if (enic_set_rsscpu(enic, rss_hash_bits)) {
@@ -2901,9 +2948,11 @@ static int enic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
netdev->hw_features |= NETIF_F_RXCSUM;
if (ENIC_SETTING(enic, VXLAN)) {
u64 patch_level;
+ u64 a1 = 0;
netdev->hw_enc_features |= NETIF_F_RXCSUM |
NETIF_F_TSO |
+ NETIF_F_TSO6 |
NETIF_F_TSO_ECN |
NETIF_F_GSO_UDP_TUNNEL |
NETIF_F_HW_CSUM |
@@ -2922,9 +2971,10 @@ static int enic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
*/
err = vnic_dev_get_supported_feature_ver(enic->vdev,
VIC_FEATURE_VXLAN,
- &patch_level);
+ &patch_level, &a1);
if (err)
patch_level = 0;
+ enic->vxlan.flags = (u8)a1;
/* mask bits that are supported by driver
*/
patch_level &= BIT_ULL(0) | BIT_ULL(2);
diff --git a/drivers/net/ethernet/cisco/enic/vnic_dev.c b/drivers/net/ethernet/cisco/enic/vnic_dev.c
index 39bad67422dd..76cdd4c9d11f 100644
--- a/drivers/net/ethernet/cisco/enic/vnic_dev.c
+++ b/drivers/net/ethernet/cisco/enic/vnic_dev.c
@@ -1269,16 +1269,32 @@ int vnic_dev_overlay_offload_cfg(struct vnic_dev *vdev, u8 overlay,
}
int vnic_dev_get_supported_feature_ver(struct vnic_dev *vdev, u8 feature,
- u64 *supported_versions)
+ u64 *supported_versions, u64 *a1)
{
u64 a0 = feature;
int wait = 1000;
- u64 a1 = 0;
int ret;
- ret = vnic_dev_cmd(vdev, CMD_GET_SUPP_FEATURE_VER, &a0, &a1, wait);
+ ret = vnic_dev_cmd(vdev, CMD_GET_SUPP_FEATURE_VER, &a0, a1, wait);
if (!ret)
*supported_versions = a0;
return ret;
}
+
+bool vnic_dev_capable_udp_rss(struct vnic_dev *vdev)
+{
+ u64 a0 = CMD_NIC_CFG, a1 = 0;
+ u64 rss_hash_type;
+ int wait = 1000;
+ int err;
+
+ err = vnic_dev_cmd(vdev, CMD_CAPABILITY, &a0, &a1, wait);
+ if (err || !a0)
+ return false;
+
+ rss_hash_type = (a1 >> NIC_CFG_RSS_HASH_TYPE_SHIFT) &
+ NIC_CFG_RSS_HASH_TYPE_MASK_FIELD;
+
+ return (rss_hash_type & NIC_CFG_RSS_HASH_TYPE_UDP);
+}
diff --git a/drivers/net/ethernet/cisco/enic/vnic_dev.h b/drivers/net/ethernet/cisco/enic/vnic_dev.h
index 9d43d6bb9907..59d4cc8fbb85 100644
--- a/drivers/net/ethernet/cisco/enic/vnic_dev.h
+++ b/drivers/net/ethernet/cisco/enic/vnic_dev.h
@@ -183,6 +183,7 @@ int vnic_dev_overlay_offload_ctrl(struct vnic_dev *vdev, u8 overlay, u8 config);
int vnic_dev_overlay_offload_cfg(struct vnic_dev *vdev, u8 overlay,
u16 vxlan_udp_port_number);
int vnic_dev_get_supported_feature_ver(struct vnic_dev *vdev, u8 feature,
- u64 *supported_versions);
+ u64 *supported_versions, u64 *a1);
+bool vnic_dev_capable_udp_rss(struct vnic_dev *vdev);
#endif /* _VNIC_DEV_H_ */
diff --git a/drivers/net/ethernet/cisco/enic/vnic_devcmd.h b/drivers/net/ethernet/cisco/enic/vnic_devcmd.h
index d83880b0d468..41de4ba622a1 100644
--- a/drivers/net/ethernet/cisco/enic/vnic_devcmd.h
+++ b/drivers/net/ethernet/cisco/enic/vnic_devcmd.h
@@ -439,6 +439,7 @@ enum vnic_devcmd_cmd {
/* flags for CMD_OPEN */
#define CMD_OPENF_OPROM 0x1 /* open coming from option rom */
+#define CMD_OPENF_IG_DESCCACHE 0x2 /* Do not flush IG DESC cache */
/* flags for CMD_INIT */
#define CMD_INITF_DEFAULT_MAC 0x1 /* init with default mac addr */
@@ -697,6 +698,10 @@ enum overlay_ofld_cmd {
#define OVERLAY_CFG_VXLAN_PORT_UPDATE 0
+#define ENIC_VXLAN_INNER_IPV6 BIT(0)
+#define ENIC_VXLAN_OUTER_IPV6 BIT(1)
+#define ENIC_VXLAN_MULTI_WQ BIT(2)
+
/* Use this enum to get the supported versions for each of these features
* If you need to use the devcmd_get_supported_feature_version(), add
* the new feature into this enum and install function handler in devcmd.c
diff --git a/drivers/net/ethernet/cisco/enic/vnic_nic.h b/drivers/net/ethernet/cisco/enic/vnic_nic.h
index 995a50dd4c99..5a93db0d7afc 100644
--- a/drivers/net/ethernet/cisco/enic/vnic_nic.h
+++ b/drivers/net/ethernet/cisco/enic/vnic_nic.h
@@ -47,6 +47,7 @@
#define NIC_CFG_RSS_HASH_TYPE_TCP_IPV6 (1 << 4)
#define NIC_CFG_RSS_HASH_TYPE_IPV6_EX (1 << 5)
#define NIC_CFG_RSS_HASH_TYPE_TCP_IPV6_EX (1 << 6)
+#define NIC_CFG_RSS_HASH_TYPE_UDP (1 << 7)
static inline void vnic_set_nic_cfg(u32 *nic_cfg,
u8 rss_default_cpu, u8 rss_hash_type,
diff --git a/drivers/net/ethernet/davicom/Kconfig b/drivers/net/ethernet/davicom/Kconfig
index 7ec2d74f94d3..680a6d983f37 100644
--- a/drivers/net/ethernet/davicom/Kconfig
+++ b/drivers/net/ethernet/davicom/Kconfig
@@ -4,7 +4,7 @@
config DM9000
tristate "DM9000 support"
- depends on ARM || BLACKFIN || MIPS || COLDFIRE || NIOS2
+ depends on ARM || MIPS || COLDFIRE || NIOS2
select CRC32
select MII
---help---
diff --git a/drivers/net/ethernet/ec_bhf.c b/drivers/net/ethernet/ec_bhf.c
index 1b79a6defd56..d71cba0842c5 100644
--- a/drivers/net/ethernet/ec_bhf.c
+++ b/drivers/net/ethernet/ec_bhf.c
@@ -602,7 +602,7 @@ static struct pci_driver pci_driver = {
};
module_pci_driver(pci_driver);
-module_param(polling_frequency, long, S_IRUGO);
+module_param(polling_frequency, long, 0444);
MODULE_PARM_DESC(polling_frequency, "Polling timer frequency in ns");
MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c
index 1a49297224ed..ff92ab1daeb8 100644
--- a/drivers/net/ethernet/emulex/benet/be_cmds.c
+++ b/drivers/net/ethernet/emulex/benet/be_cmds.c
@@ -19,7 +19,7 @@
#include "be.h"
#include "be_cmds.h"
-char *be_misconfig_evt_port_state[] = {
+const char * const be_misconfig_evt_port_state[] = {
"Physical Link is functional",
"Optics faulted/incorrectly installed/not installed - Reseat optics. If issue not resolved, replace.",
"Optics of two types installed – Remove one optic or install matching pair of optics.",
diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.h b/drivers/net/ethernet/emulex/benet/be_cmds.h
index 09da2d82c2f0..e8b43cf44b6f 100644
--- a/drivers/net/ethernet/emulex/benet/be_cmds.h
+++ b/drivers/net/ethernet/emulex/benet/be_cmds.h
@@ -201,7 +201,7 @@ enum {
phy_state == BE_PHY_UNQUALIFIED || \
phy_state == BE_PHY_UNCERTIFIED)
-extern char *be_misconfig_evt_port_state[];
+extern const char * const be_misconfig_evt_port_state[];
/* async event indicating misconfigured port */
struct be_async_event_misconfig_port {
diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c
index 5774fb6f8aa0..c697e79e491e 100644
--- a/drivers/net/ethernet/emulex/benet/be_main.c
+++ b/drivers/net/ethernet/emulex/benet/be_main.c
@@ -34,11 +34,11 @@ MODULE_LICENSE("GPL");
* Use sysfs method to enable/disable VFs.
*/
static unsigned int num_vfs;
-module_param(num_vfs, uint, S_IRUGO);
+module_param(num_vfs, uint, 0444);
MODULE_PARM_DESC(num_vfs, "Number of PCI VFs to initialize");
static ushort rx_frag_size = 2048;
-module_param(rx_frag_size, ushort, S_IRUGO);
+module_param(rx_frag_size, ushort, 0444);
MODULE_PARM_DESC(rx_frag_size, "Size of a fragment that holds rcvd data.");
/* Per-module error detection/recovery workq shared across all functions.
@@ -5788,7 +5788,7 @@ static ssize_t be_hwmon_show_temp(struct device *dev,
adapter->hwmon_info.be_on_die_temp * 1000);
}
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
+static SENSOR_DEVICE_ATTR(temp1_input, 0444,
be_hwmon_show_temp, NULL, 1);
static struct attribute *be_hwmon_attrs[] = {
diff --git a/drivers/net/ethernet/faraday/Kconfig b/drivers/net/ethernet/faraday/Kconfig
index 040c7f163325..0fb8df656677 100644
--- a/drivers/net/ethernet/faraday/Kconfig
+++ b/drivers/net/ethernet/faraday/Kconfig
@@ -5,7 +5,7 @@
config NET_VENDOR_FARADAY
bool "Faraday devices"
default y
- depends on ARM
+ depends on ARM || NDS32 || COMPILE_TEST
---help---
If you have a network (Ethernet) card belonging to this class, say Y.
@@ -18,7 +18,8 @@ if NET_VENDOR_FARADAY
config FTMAC100
tristate "Faraday FTMAC100 10/100 Ethernet support"
- depends on ARM
+ depends on ARM || NDS32 || COMPILE_TEST
+ depends on !64BIT || BROKEN
select MII
---help---
This driver supports the FTMAC100 10/100 Ethernet controller
@@ -27,7 +28,8 @@ config FTMAC100
config FTGMAC100
tristate "Faraday FTGMAC100 Gigabit Ethernet support"
- depends on ARM
+ depends on ARM || NDS32 || COMPILE_TEST
+ depends on !64BIT || BROKEN
select PHYLIB
---help---
This driver supports the FTGMAC100 Gigabit Ethernet controller
diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
index e4ec32a9ca15..fd43f98ddbe7 100644
--- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
+++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
@@ -454,6 +454,16 @@ static void dpaa_set_rx_mode(struct net_device *net_dev)
err);
}
+ if (!!(net_dev->flags & IFF_ALLMULTI) != priv->mac_dev->allmulti) {
+ priv->mac_dev->allmulti = !priv->mac_dev->allmulti;
+ err = priv->mac_dev->set_allmulti(priv->mac_dev->fman_mac,
+ priv->mac_dev->allmulti);
+ if (err < 0)
+ netif_err(priv, drv, net_dev,
+ "mac_dev->set_allmulti() = %d\n",
+ err);
+ }
+
err = priv->mac_dev->set_multi(net_dev, priv->mac_dev);
if (err < 0)
netif_err(priv, drv, net_dev, "mac_dev->set_multi() = %d\n",
@@ -1916,8 +1926,10 @@ static int skb_to_sg_fd(struct dpaa_priv *priv,
goto csum_failed;
}
+ /* SGT[0] is used by the linear part */
sgt = (struct qm_sg_entry *)(sgt_buf + priv->tx_headroom);
- qm_sg_entry_set_len(&sgt[0], skb_headlen(skb));
+ frag_len = skb_headlen(skb);
+ qm_sg_entry_set_len(&sgt[0], frag_len);
sgt[0].bpid = FSL_DPAA_BPID_INV;
sgt[0].offset = 0;
addr = dma_map_single(dev, skb->data,
@@ -1930,9 +1942,9 @@ static int skb_to_sg_fd(struct dpaa_priv *priv,
qm_sg_entry_set64(&sgt[0], addr);
/* populate the rest of SGT entries */
- frag = &skb_shinfo(skb)->frags[0];
- frag_len = frag->size;
- for (i = 1; i <= nr_frags; i++, frag++) {
+ for (i = 0; i < nr_frags; i++) {
+ frag = &skb_shinfo(skb)->frags[i];
+ frag_len = frag->size;
WARN_ON(!skb_frag_page(frag));
addr = skb_frag_dma_map(dev, frag, 0,
frag_len, dma_dir);
@@ -1942,15 +1954,16 @@ static int skb_to_sg_fd(struct dpaa_priv *priv,
goto sg_map_failed;
}
- qm_sg_entry_set_len(&sgt[i], frag_len);
- sgt[i].bpid = FSL_DPAA_BPID_INV;
- sgt[i].offset = 0;
+ qm_sg_entry_set_len(&sgt[i + 1], frag_len);
+ sgt[i + 1].bpid = FSL_DPAA_BPID_INV;
+ sgt[i + 1].offset = 0;
/* keep the offset in the address */
- qm_sg_entry_set64(&sgt[i], addr);
- frag_len = frag->size;
+ qm_sg_entry_set64(&sgt[i + 1], addr);
}
- qm_sg_entry_set_f(&sgt[i - 1], frag_len);
+
+ /* Set the final bit in the last used entry of the SGT */
+ qm_sg_entry_set_f(&sgt[nr_frags], frag_len);
qm_fd_set_sg(fd, priv->tx_headroom, skb->len);
@@ -2051,19 +2064,23 @@ static int dpaa_start_xmit(struct sk_buff *skb, struct net_device *net_dev)
/* MAX_SKB_FRAGS is equal or larger than our dpaa_SGT_MAX_ENTRIES;
* make sure we don't feed FMan with more fragments than it supports.
*/
- if (nonlinear &&
- likely(skb_shinfo(skb)->nr_frags < DPAA_SGT_MAX_ENTRIES)) {
- /* Just create a S/G fd based on the skb */
- err = skb_to_sg_fd(priv, skb, &fd);
- percpu_priv->tx_frag_skbuffs++;
- } else {
+ if (unlikely(nonlinear &&
+ (skb_shinfo(skb)->nr_frags >= DPAA_SGT_MAX_ENTRIES))) {
/* If the egress skb contains more fragments than we support
* we have no choice but to linearize it ourselves.
*/
- if (unlikely(nonlinear) && __skb_linearize(skb))
+ if (__skb_linearize(skb))
goto enomem;
- /* Finally, create a contig FD from this skb */
+ nonlinear = skb_is_nonlinear(skb);
+ }
+
+ if (nonlinear) {
+ /* Just create a S/G fd based on the skb */
+ err = skb_to_sg_fd(priv, skb, &fd);
+ percpu_priv->tx_frag_skbuffs++;
+ } else {
+ /* Create a contig FD from this skb */
err = skb_to_contig_fd(priv, skb, &fd, &offset);
}
if (unlikely(err < 0))
@@ -2200,14 +2217,8 @@ static enum qman_cb_dqrr_result rx_error_dqrr(struct qman_portal *portal,
if (dpaa_eth_napi_schedule(percpu_priv, portal))
return qman_cb_dqrr_stop;
- if (dpaa_eth_refill_bpools(priv))
- /* Unable to refill the buffer pool due to insufficient
- * system memory. Just release the frame back into the pool,
- * otherwise we'll soon end up with an empty buffer pool.
- */
- dpaa_fd_release(net_dev, &dq->fd);
- else
- dpaa_rx_error(net_dev, priv, percpu_priv, &dq->fd, fq->fqid);
+ dpaa_eth_refill_bpools(priv);
+ dpaa_rx_error(net_dev, priv, percpu_priv, &dq->fd, fq->fqid);
return qman_cb_dqrr_consume;
}
@@ -2766,7 +2777,7 @@ static int dpaa_eth_probe(struct platform_device *pdev)
priv->channel = (u16)channel;
- /* Start a thread that will walk the CPUs with affine portals
+ /* Walk the CPUs with affine portals
* and add this pool channel to each's dequeue mask.
*/
dpaa_eth_add_channel(priv->channel);
diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c b/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c
index faea674094b9..2f933b6b2f4e 100644
--- a/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c
+++ b/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c
@@ -211,7 +211,7 @@ static int dpaa_set_pauseparam(struct net_device *net_dev,
if (epause->rx_pause)
newadv = ADVERTISED_Pause | ADVERTISED_Asym_Pause;
if (epause->tx_pause)
- newadv |= ADVERTISED_Asym_Pause;
+ newadv ^= ADVERTISED_Asym_Pause;
oldadv = phydev->advertising &
(ADVERTISED_Pause | ADVERTISED_Asym_Pause);
@@ -344,7 +344,7 @@ static void dpaa_get_ethtool_stats(struct net_device *net_dev,
/* gather congestion related counters */
cg_num = 0;
- cg_status = 0;
+ cg_status = false;
cg_time = jiffies_to_msecs(priv->cgr_data.congested_jiffies);
if (qman_query_cgr_congested(&priv->cgr_data.cgr, &cg_status) == 0) {
cg_num = priv->cgr_data.cgr_congested_count;
diff --git a/drivers/net/ethernet/freescale/fman/Kconfig b/drivers/net/ethernet/freescale/fman/Kconfig
index 8870a9a798ca..dc0850b3b517 100644
--- a/drivers/net/ethernet/freescale/fman/Kconfig
+++ b/drivers/net/ethernet/freescale/fman/Kconfig
@@ -2,7 +2,6 @@ config FSL_FMAN
tristate "FMan support"
depends on FSL_SOC || ARCH_LAYERSCAPE || COMPILE_TEST
select GENERIC_ALLOCATOR
- depends on HAS_DMA
select PHYLIB
default n
help
diff --git a/drivers/net/ethernet/freescale/fman/fman_dtsec.c b/drivers/net/ethernet/freescale/fman/fman_dtsec.c
index 7af31ddd093f..57b1e2b47c0a 100644
--- a/drivers/net/ethernet/freescale/fman/fman_dtsec.c
+++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.c
@@ -1117,6 +1117,25 @@ int dtsec_add_hash_mac_address(struct fman_mac *dtsec, enet_addr_t *eth_addr)
return 0;
}
+int dtsec_set_allmulti(struct fman_mac *dtsec, bool enable)
+{
+ u32 tmp;
+ struct dtsec_regs __iomem *regs = dtsec->regs;
+
+ if (!is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ tmp = ioread32be(&regs->rctrl);
+ if (enable)
+ tmp |= RCTRL_MPROM;
+ else
+ tmp &= ~RCTRL_MPROM;
+
+ iowrite32be(tmp, &regs->rctrl);
+
+ return 0;
+}
+
int dtsec_del_hash_mac_address(struct fman_mac *dtsec, enet_addr_t *eth_addr)
{
struct dtsec_regs __iomem *regs = dtsec->regs;
diff --git a/drivers/net/ethernet/freescale/fman/fman_dtsec.h b/drivers/net/ethernet/freescale/fman/fman_dtsec.h
index c4467c072058..1a689adf5a22 100644
--- a/drivers/net/ethernet/freescale/fman/fman_dtsec.h
+++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.h
@@ -55,5 +55,6 @@ int dtsec_set_exception(struct fman_mac *dtsec,
int dtsec_add_hash_mac_address(struct fman_mac *dtsec, enet_addr_t *eth_addr);
int dtsec_del_hash_mac_address(struct fman_mac *dtsec, enet_addr_t *eth_addr);
int dtsec_get_version(struct fman_mac *dtsec, u32 *mac_version);
+int dtsec_set_allmulti(struct fman_mac *dtsec, bool enable);
#endif /* __DTSEC_H */
diff --git a/drivers/net/ethernet/freescale/fman/fman_memac.c b/drivers/net/ethernet/freescale/fman/fman_memac.c
index c0296880feba..446a97b792e3 100644
--- a/drivers/net/ethernet/freescale/fman/fman_memac.c
+++ b/drivers/net/ethernet/freescale/fman/fman_memac.c
@@ -350,6 +350,7 @@ struct fman_mac {
struct fman_rev_info fm_rev_info;
bool basex_if;
struct phy_device *pcsphy;
+ bool allmulti_enabled;
};
static void add_addr_in_paddr(struct memac_regs __iomem *regs, u8 *adr,
@@ -940,6 +941,29 @@ int memac_add_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr)
return 0;
}
+int memac_set_allmulti(struct fman_mac *memac, bool enable)
+{
+ u32 entry;
+ struct memac_regs __iomem *regs = memac->regs;
+
+ if (!is_init_done(memac->memac_drv_param))
+ return -EINVAL;
+
+ if (enable) {
+ for (entry = 0; entry < HASH_TABLE_SIZE; entry++)
+ iowrite32be(entry | HASH_CTRL_MCAST_EN,
+ &regs->hashtable_ctrl);
+ } else {
+ for (entry = 0; entry < HASH_TABLE_SIZE; entry++)
+ iowrite32be(entry & ~HASH_CTRL_MCAST_EN,
+ &regs->hashtable_ctrl);
+ }
+
+ memac->allmulti_enabled = enable;
+
+ return 0;
+}
+
int memac_del_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr)
{
struct memac_regs __iomem *regs = memac->regs;
@@ -963,8 +987,12 @@ int memac_del_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr)
break;
}
}
- if (list_empty(&memac->multicast_addr_hash->lsts[hash]))
- iowrite32be(hash & ~HASH_CTRL_MCAST_EN, &regs->hashtable_ctrl);
+
+ if (!memac->allmulti_enabled) {
+ if (list_empty(&memac->multicast_addr_hash->lsts[hash]))
+ iowrite32be(hash & ~HASH_CTRL_MCAST_EN,
+ &regs->hashtable_ctrl);
+ }
return 0;
}
diff --git a/drivers/net/ethernet/freescale/fman/fman_memac.h b/drivers/net/ethernet/freescale/fman/fman_memac.h
index c4a66469a907..b5a50338ed9a 100644
--- a/drivers/net/ethernet/freescale/fman/fman_memac.h
+++ b/drivers/net/ethernet/freescale/fman/fman_memac.h
@@ -57,5 +57,6 @@ int memac_set_exception(struct fman_mac *memac,
enum fman_mac_exceptions exception, bool enable);
int memac_add_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr);
int memac_del_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr);
+int memac_set_allmulti(struct fman_mac *memac, bool enable);
#endif /* __MEMAC_H */
diff --git a/drivers/net/ethernet/freescale/fman/fman_tgec.c b/drivers/net/ethernet/freescale/fman/fman_tgec.c
index 4b0f3a50b293..284735d4ebe9 100644
--- a/drivers/net/ethernet/freescale/fman/fman_tgec.c
+++ b/drivers/net/ethernet/freescale/fman/fman_tgec.c
@@ -217,6 +217,7 @@ struct fman_mac {
struct tgec_cfg *cfg;
void *fm;
struct fman_rev_info fm_rev_info;
+ bool allmulti_enabled;
};
static void set_mac_address(struct tgec_regs __iomem *regs, u8 *adr)
@@ -564,6 +565,29 @@ int tgec_add_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr)
return 0;
}
+int tgec_set_allmulti(struct fman_mac *tgec, bool enable)
+{
+ u32 entry;
+ struct tgec_regs __iomem *regs = tgec->regs;
+
+ if (!is_init_done(tgec->cfg))
+ return -EINVAL;
+
+ if (enable) {
+ for (entry = 0; entry < TGEC_HASH_TABLE_SIZE; entry++)
+ iowrite32be(entry | TGEC_HASH_MCAST_EN,
+ &regs->hashtable_ctrl);
+ } else {
+ for (entry = 0; entry < TGEC_HASH_TABLE_SIZE; entry++)
+ iowrite32be(entry & ~TGEC_HASH_MCAST_EN,
+ &regs->hashtable_ctrl);
+ }
+
+ tgec->allmulti_enabled = enable;
+
+ return 0;
+}
+
int tgec_del_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr)
{
struct tgec_regs __iomem *regs = tgec->regs;
@@ -591,9 +615,12 @@ int tgec_del_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr)
break;
}
}
- if (list_empty(&tgec->multicast_addr_hash->lsts[hash]))
- iowrite32be((hash & ~TGEC_HASH_MCAST_EN),
- &regs->hashtable_ctrl);
+
+ if (!tgec->allmulti_enabled) {
+ if (list_empty(&tgec->multicast_addr_hash->lsts[hash]))
+ iowrite32be((hash & ~TGEC_HASH_MCAST_EN),
+ &regs->hashtable_ctrl);
+ }
return 0;
}
diff --git a/drivers/net/ethernet/freescale/fman/fman_tgec.h b/drivers/net/ethernet/freescale/fman/fman_tgec.h
index 514bba9f47ce..cbbd3b422a98 100644
--- a/drivers/net/ethernet/freescale/fman/fman_tgec.h
+++ b/drivers/net/ethernet/freescale/fman/fman_tgec.h
@@ -51,5 +51,6 @@ int tgec_set_exception(struct fman_mac *tgec,
int tgec_add_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr);
int tgec_del_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr);
int tgec_get_version(struct fman_mac *tgec, u32 *mac_version);
+int tgec_set_allmulti(struct fman_mac *tgec, bool enable);
#endif /* __TGEC_H */
diff --git a/drivers/net/ethernet/freescale/fman/mac.c b/drivers/net/ethernet/freescale/fman/mac.c
index 88c0a0636b44..7b5b95f52c09 100644
--- a/drivers/net/ethernet/freescale/fman/mac.c
+++ b/drivers/net/ethernet/freescale/fman/mac.c
@@ -470,6 +470,7 @@ static void setup_dtsec(struct mac_device *mac_dev)
mac_dev->set_tx_pause = dtsec_set_tx_pause_frames;
mac_dev->set_rx_pause = dtsec_accept_rx_pause_frames;
mac_dev->set_exception = dtsec_set_exception;
+ mac_dev->set_allmulti = dtsec_set_allmulti;
mac_dev->set_multi = set_multi;
mac_dev->start = start;
mac_dev->stop = stop;
@@ -488,6 +489,7 @@ static void setup_tgec(struct mac_device *mac_dev)
mac_dev->set_tx_pause = tgec_set_tx_pause_frames;
mac_dev->set_rx_pause = tgec_accept_rx_pause_frames;
mac_dev->set_exception = tgec_set_exception;
+ mac_dev->set_allmulti = tgec_set_allmulti;
mac_dev->set_multi = set_multi;
mac_dev->start = start;
mac_dev->stop = stop;
@@ -506,6 +508,7 @@ static void setup_memac(struct mac_device *mac_dev)
mac_dev->set_tx_pause = memac_set_tx_pause_frames;
mac_dev->set_rx_pause = memac_accept_rx_pause_frames;
mac_dev->set_exception = memac_set_exception;
+ mac_dev->set_allmulti = memac_set_allmulti;
mac_dev->set_multi = set_multi;
mac_dev->start = start;
mac_dev->stop = stop;
@@ -564,7 +567,6 @@ static struct platform_device *dpaa_eth_add_device(int fman_id,
}
pdev->dev.parent = priv->dev;
- set_dma_ops(&pdev->dev, get_dma_ops(priv->dev));
ret = platform_device_add_data(pdev, &data, sizeof(data));
if (ret)
diff --git a/drivers/net/ethernet/freescale/fman/mac.h b/drivers/net/ethernet/freescale/fman/mac.h
index eefb3357e304..b520cec120ee 100644
--- a/drivers/net/ethernet/freescale/fman/mac.h
+++ b/drivers/net/ethernet/freescale/fman/mac.h
@@ -59,6 +59,7 @@ struct mac_device {
bool rx_pause_active;
bool tx_pause_active;
bool promisc;
+ bool allmulti;
int (*init)(struct mac_device *mac_dev);
int (*start)(struct mac_device *mac_dev);
@@ -66,6 +67,7 @@ struct mac_device {
void (*adjust_link)(struct mac_device *mac_dev);
int (*set_promisc)(struct fman_mac *mac_dev, bool enable);
int (*change_addr)(struct fman_mac *mac_dev, enet_addr_t *enet_addr);
+ int (*set_allmulti)(struct fman_mac *mac_dev, bool enable);
int (*set_multi)(struct net_device *net_dev,
struct mac_device *mac_dev);
int (*set_rx_pause)(struct fman_mac *mac_dev, bool en);
diff --git a/drivers/net/ethernet/freescale/fsl_pq_mdio.c b/drivers/net/ethernet/freescale/fsl_pq_mdio.c
index 80ad16acf0f1..ac2c3f6a12bc 100644
--- a/drivers/net/ethernet/freescale/fsl_pq_mdio.c
+++ b/drivers/net/ethernet/freescale/fsl_pq_mdio.c
@@ -377,6 +377,38 @@ static const struct of_device_id fsl_pq_mdio_match[] = {
};
MODULE_DEVICE_TABLE(of, fsl_pq_mdio_match);
+static void set_tbipa(const u32 tbipa_val, struct platform_device *pdev,
+ uint32_t __iomem * (*get_tbipa)(void __iomem *),
+ void __iomem *reg_map, struct resource *reg_res)
+{
+ struct device_node *np = pdev->dev.of_node;
+ uint32_t __iomem *tbipa;
+ bool tbipa_mapped;
+
+ tbipa = of_iomap(np, 1);
+ if (tbipa) {
+ tbipa_mapped = true;
+ } else {
+ tbipa_mapped = false;
+ tbipa = (*get_tbipa)(reg_map);
+
+ /*
+ * Add consistency check to make sure TBI is contained within
+ * the mapped range (not because we would get a segfault,
+ * rather to catch bugs in computing TBI address). Print error
+ * message but continue anyway.
+ */
+ if ((void *)tbipa > reg_map + resource_size(reg_res) - 4)
+ dev_err(&pdev->dev, "invalid register map (should be at least 0x%04zx to contain TBI address)\n",
+ ((void *)tbipa - reg_map) + 4);
+ }
+
+ iowrite32be(be32_to_cpu(tbipa_val), tbipa);
+
+ if (tbipa_mapped)
+ iounmap(tbipa);
+}
+
static int fsl_pq_mdio_probe(struct platform_device *pdev)
{
const struct of_device_id *id =
@@ -450,8 +482,6 @@ static int fsl_pq_mdio_probe(struct platform_device *pdev)
if (tbi) {
const u32 *prop = of_get_property(tbi, "reg", NULL);
- uint32_t __iomem *tbipa;
-
if (!prop) {
dev_err(&pdev->dev,
"missing 'reg' property in node %pOF\n",
@@ -459,20 +489,8 @@ static int fsl_pq_mdio_probe(struct platform_device *pdev)
err = -EBUSY;
goto error;
}
-
- tbipa = data->get_tbipa(priv->map);
-
- /*
- * Add consistency check to make sure TBI is contained
- * within the mapped range (not because we would get a
- * segfault, rather to catch bugs in computing TBI
- * address). Print error message but continue anyway.
- */
- if ((void *)tbipa > priv->map + resource_size(&res) - 4)
- dev_err(&pdev->dev, "invalid register map (should be at least 0x%04zx to contain TBI address)\n",
- ((void *)tbipa - priv->map) + 4);
-
- iowrite32be(be32_to_cpup(prop), tbipa);
+ set_tbipa(*prop, pdev,
+ data->get_tbipa, priv->map, &res);
}
}
diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.h b/drivers/net/ethernet/hisilicon/hns/hnae.h
index 3e62692af011..fa5b30f547f6 100644
--- a/drivers/net/ethernet/hisilicon/hns/hnae.h
+++ b/drivers/net/ethernet/hisilicon/hns/hnae.h
@@ -87,7 +87,7 @@ do { \
#define HNAE_AE_REGISTER 0x1
-#define RCB_RING_NAME_LEN 16
+#define RCB_RING_NAME_LEN (IFNAMSIZ + 4)
#define HNAE_LOWEST_LATENCY_COAL_PARAM 30
#define HNAE_LOW_LATENCY_COAL_PARAM 80
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c
index cac86e9ae0dd..9dcc5765f11f 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c
@@ -369,7 +369,6 @@ static int hns_mac_port_config_bc_en(struct hns_mac_cb *mac_cb,
{
int ret;
struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev;
- u8 addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
struct dsaf_drv_mac_single_dest_entry mac_entry;
/* directy return ok in debug network mode */
@@ -377,7 +376,7 @@ static int hns_mac_port_config_bc_en(struct hns_mac_cb *mac_cb,
return 0;
if (!HNS_DSAF_IS_DEBUG(dsaf_dev)) {
- memcpy(mac_entry.addr, addr, sizeof(mac_entry.addr));
+ eth_broadcast_addr(mac_entry.addr);
mac_entry.in_vlan_id = vlan_id;
mac_entry.in_port_num = mac_cb->mac_id;
mac_entry.port_num = port_num;
@@ -404,7 +403,6 @@ int hns_mac_vm_config_bc_en(struct hns_mac_cb *mac_cb, u32 vmid, bool enable)
int ret;
struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev;
u8 port_num;
- u8 addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
struct mac_entry_idx *uc_mac_entry;
struct dsaf_drv_mac_single_dest_entry mac_entry;
@@ -414,7 +412,7 @@ int hns_mac_vm_config_bc_en(struct hns_mac_cb *mac_cb, u32 vmid, bool enable)
uc_mac_entry = &mac_cb->addr_entry_idx[vmid];
if (!HNS_DSAF_IS_DEBUG(dsaf_dev)) {
- memcpy(mac_entry.addr, addr, sizeof(mac_entry.addr));
+ eth_broadcast_addr(mac_entry.addr);
mac_entry.in_vlan_id = uc_mac_entry->vlan_id;
mac_entry.in_port_num = mac_cb->mac_id;
ret = hns_mac_get_inner_port_num(mac_cb, vmid, &port_num);
diff --git a/drivers/net/ethernet/hisilicon/hns3/hclge_mbx.h b/drivers/net/ethernet/hisilicon/hns3/hclge_mbx.h
index 3e9203ea42a6..519e2bd6aa60 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hclge_mbx.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hclge_mbx.h
@@ -11,6 +11,7 @@
enum HCLGE_MBX_OPCODE {
HCLGE_MBX_RESET = 0x01, /* (VF -> PF) assert reset */
+ HCLGE_MBX_ASSERTING_RESET, /* (PF -> VF) PF is asserting reset*/
HCLGE_MBX_SET_UNICAST, /* (VF -> PF) set UC addr */
HCLGE_MBX_SET_MULTICAST, /* (VF -> PF) set MC addr */
HCLGE_MBX_SET_VLAN, /* (VF -> PF) set VLAN */
@@ -57,6 +58,8 @@ enum hclge_mbx_vlan_cfg_subcode {
#define HCLGE_MBX_MAX_MSG_SIZE 16
#define HCLGE_MBX_MAX_RESP_DATA_SIZE 8
+#define HCLGE_MBX_RING_MAP_BASIC_MSG_NUM 3
+#define HCLGE_MBX_RING_NODE_VARIABLE_NUM 3
struct hclgevf_mbx_resp_status {
struct mutex mbx_mutex; /* protects against contending sync cmd resp */
@@ -83,6 +86,21 @@ struct hclge_mbx_pf_to_vf_cmd {
u16 msg[8];
};
+/* used by VF to store the received Async responses from PF */
+struct hclgevf_mbx_arq_ring {
+#define HCLGE_MBX_MAX_ARQ_MSG_SIZE 8
+#define HCLGE_MBX_MAX_ARQ_MSG_NUM 1024
+ struct hclgevf_dev *hdev;
+ u32 head;
+ u32 tail;
+ u32 count;
+ u16 msg_q[HCLGE_MBX_MAX_ARQ_MSG_NUM][HCLGE_MBX_MAX_ARQ_MSG_SIZE];
+};
+
#define hclge_mbx_ring_ptr_move_crq(crq) \
(crq->next_to_use = (crq->next_to_use + 1) % crq->desc_num)
+#define hclge_mbx_tail_ptr_move_arq(arq) \
+ (arq.tail = (arq.tail + 1) % HCLGE_MBX_MAX_ARQ_MSG_SIZE)
+#define hclge_mbx_head_ptr_move_arq(arq) \
+ (arq.head = (arq.head + 1) % HCLGE_MBX_MAX_ARQ_MSG_SIZE)
#endif
diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.h b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
index fd06bc78c58e..37ec1b3286c6 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hnae3.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
@@ -118,6 +118,8 @@ enum hnae3_reset_notify_type {
};
enum hnae3_reset_type {
+ HNAE3_VF_RESET,
+ HNAE3_VF_FULL_RESET,
HNAE3_FUNC_RESET,
HNAE3_CORE_RESET,
HNAE3_GLOBAL_RESET,
@@ -265,6 +267,8 @@ struct hnae3_ae_dev {
* Get tc size of handle
* get_vector()
* Get vector number and vector information
+ * put_vector()
+ * Put the vector in hdev
* map_ring_to_vector()
* Map rings to vector
* unmap_ring_from_vector()
@@ -336,7 +340,8 @@ struct hnae3_ae_ops {
u32 *tx_usecs_high, u32 *rx_usecs_high);
void (*get_mac_addr)(struct hnae3_handle *handle, u8 *p);
- int (*set_mac_addr)(struct hnae3_handle *handle, void *p);
+ int (*set_mac_addr)(struct hnae3_handle *handle, void *p,
+ bool is_first);
int (*add_uc_addr)(struct hnae3_handle *handle,
const unsigned char *addr);
int (*rm_uc_addr)(struct hnae3_handle *handle,
@@ -375,6 +380,7 @@ struct hnae3_ae_ops {
int (*get_vector)(struct hnae3_handle *handle, u16 vector_num,
struct hnae3_vector_info *vector_info);
+ int (*put_vector)(struct hnae3_handle *handle, int vector_num);
int (*map_ring_to_vector)(struct hnae3_handle *handle,
int vector_num,
struct hnae3_ring_chain_node *vr_chain);
@@ -396,8 +402,7 @@ struct hnae3_ae_ops {
int (*set_vf_vlan_filter)(struct hnae3_handle *handle, int vfid,
u16 vlan, u8 qos, __be16 proto);
int (*enable_hw_strip_rxvtag)(struct hnae3_handle *handle, bool enable);
- void (*reset_event)(struct hnae3_handle *handle,
- enum hnae3_reset_type reset);
+ void (*reset_event)(struct hnae3_handle *handle);
void (*get_channels)(struct hnae3_handle *handle,
struct ethtool_channels *ch);
void (*get_tqps_and_rss_info)(struct hnae3_handle *h,
@@ -407,6 +412,10 @@ struct hnae3_ae_ops {
u32 *flowctrl_adv);
int (*set_led_id)(struct hnae3_handle *handle,
enum ethtool_phys_id_state status);
+ void (*get_link_mode)(struct hnae3_handle *handle,
+ unsigned long *supported,
+ unsigned long *advertising);
+ void (*get_port_type)(struct hnae3_handle *handle, u8 *port_type);
};
struct hnae3_dcb_ops {
@@ -487,6 +496,9 @@ struct hnae3_handle {
struct hnae3_ae_algo *ae_algo; /* the class who provides this handle */
u64 flags; /* Indicate the capabilities for this handle*/
+ unsigned long last_reset_time;
+ enum hnae3_reset_type reset_level;
+
union {
struct net_device *netdev; /* first member */
struct hnae3_knic_private_info kinfo;
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
index 601b6295d3f8..8c55965a66ac 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
@@ -168,8 +168,8 @@ void hns3_set_vector_coalesce_rl(struct hns3_enet_tqp_vector *tqp_vector,
* GL and RL(Rate Limiter) are 2 ways to acheive interrupt coalescing
*/
- if (rl_reg > 0 && !tqp_vector->tx_group.gl_adapt_enable &&
- !tqp_vector->rx_group.gl_adapt_enable)
+ if (rl_reg > 0 && !tqp_vector->tx_group.coal.gl_adapt_enable &&
+ !tqp_vector->rx_group.coal.gl_adapt_enable)
/* According to the hardware, the range of rl_reg is
* 0-59 and the unit is 4.
*/
@@ -205,23 +205,30 @@ static void hns3_vector_gl_rl_init(struct hns3_enet_tqp_vector *tqp_vector,
*/
/* Default: enable interrupt coalescing self-adaptive and GL */
- tqp_vector->tx_group.gl_adapt_enable = 1;
- tqp_vector->rx_group.gl_adapt_enable = 1;
+ tqp_vector->tx_group.coal.gl_adapt_enable = 1;
+ tqp_vector->rx_group.coal.gl_adapt_enable = 1;
- tqp_vector->tx_group.int_gl = HNS3_INT_GL_50K;
- tqp_vector->rx_group.int_gl = HNS3_INT_GL_50K;
-
- hns3_set_vector_coalesce_tx_gl(tqp_vector,
- tqp_vector->tx_group.int_gl);
- hns3_set_vector_coalesce_rx_gl(tqp_vector,
- tqp_vector->rx_group.int_gl);
+ tqp_vector->tx_group.coal.int_gl = HNS3_INT_GL_50K;
+ tqp_vector->rx_group.coal.int_gl = HNS3_INT_GL_50K;
/* Default: disable RL */
h->kinfo.int_rl_setting = 0;
- hns3_set_vector_coalesce_rl(tqp_vector, h->kinfo.int_rl_setting);
- tqp_vector->rx_group.flow_level = HNS3_FLOW_LOW;
- tqp_vector->tx_group.flow_level = HNS3_FLOW_LOW;
+ tqp_vector->int_adapt_down = HNS3_INT_ADAPT_DOWN_START;
+ tqp_vector->rx_group.coal.flow_level = HNS3_FLOW_LOW;
+ tqp_vector->tx_group.coal.flow_level = HNS3_FLOW_LOW;
+}
+
+static void hns3_vector_gl_rl_init_hw(struct hns3_enet_tqp_vector *tqp_vector,
+ struct hns3_nic_priv *priv)
+{
+ struct hnae3_handle *h = priv->ae_handle;
+
+ hns3_set_vector_coalesce_tx_gl(tqp_vector,
+ tqp_vector->tx_group.coal.int_gl);
+ hns3_set_vector_coalesce_rx_gl(tqp_vector,
+ tqp_vector->rx_group.coal.int_gl);
+ hns3_set_vector_coalesce_rl(tqp_vector, h->kinfo.int_rl_setting);
}
static int hns3_nic_set_real_num_queue(struct net_device *netdev)
@@ -249,6 +256,16 @@ static int hns3_nic_set_real_num_queue(struct net_device *netdev)
return 0;
}
+static u16 hns3_get_max_available_channels(struct hnae3_handle *h)
+{
+ u16 free_tqps, max_rss_size, max_tqps;
+
+ h->ae_algo->ops->get_tqps_and_rss_info(h, &free_tqps, &max_rss_size);
+ max_tqps = h->kinfo.num_tc * max_rss_size;
+
+ return min_t(u16, max_tqps, (free_tqps + h->kinfo.num_tqps));
+}
+
static int hns3_nic_net_up(struct net_device *netdev)
{
struct hns3_nic_priv *priv = netdev_priv(netdev);
@@ -303,7 +320,7 @@ static int hns3_nic_net_open(struct net_device *netdev)
return ret;
}
- priv->last_reset_time = jiffies;
+ priv->ae_handle->last_reset_time = jiffies;
return 0;
}
@@ -747,7 +764,7 @@ static void hns3_set_txbd_baseinfo(u16 *bdtp_fe_sc_vld_ra_ri, int frag_end)
{
/* Config bd buffer end */
hnae_set_field(*bdtp_fe_sc_vld_ra_ri, HNS3_TXD_BDTYPE_M,
- HNS3_TXD_BDTYPE_M, 0);
+ HNS3_TXD_BDTYPE_S, 0);
hnae_set_bit(*bdtp_fe_sc_vld_ra_ri, HNS3_TXD_FE_B, !!frag_end);
hnae_set_bit(*bdtp_fe_sc_vld_ra_ri, HNS3_TXD_VLD_B, 1);
hnae_set_field(*bdtp_fe_sc_vld_ra_ri, HNS3_TXD_SC_M, HNS3_TXD_SC_S, 0);
@@ -1104,7 +1121,7 @@ static int hns3_nic_net_set_mac_address(struct net_device *netdev, void *p)
if (!mac_addr || !is_valid_ether_addr((const u8 *)mac_addr->sa_data))
return -EADDRNOTAVAIL;
- ret = h->ae_algo->ops->set_mac_addr(h, mac_addr->sa_data);
+ ret = h->ae_algo->ops->set_mac_addr(h, mac_addr->sa_data, false);
if (ret) {
netdev_err(netdev, "set_mac_address fail, ret=%d!\n", ret);
return ret;
@@ -1388,11 +1405,15 @@ static int hns3_vlan_rx_add_vid(struct net_device *netdev,
__be16 proto, u16 vid)
{
struct hnae3_handle *h = hns3_get_handle(netdev);
+ struct hns3_nic_priv *priv = netdev_priv(netdev);
int ret = -EIO;
if (h->ae_algo->ops->set_vlan_filter)
ret = h->ae_algo->ops->set_vlan_filter(h, proto, vid, false);
+ if (!ret)
+ set_bit(vid, priv->active_vlans);
+
return ret;
}
@@ -1400,14 +1421,32 @@ static int hns3_vlan_rx_kill_vid(struct net_device *netdev,
__be16 proto, u16 vid)
{
struct hnae3_handle *h = hns3_get_handle(netdev);
+ struct hns3_nic_priv *priv = netdev_priv(netdev);
int ret = -EIO;
if (h->ae_algo->ops->set_vlan_filter)
ret = h->ae_algo->ops->set_vlan_filter(h, proto, vid, true);
+ if (!ret)
+ clear_bit(vid, priv->active_vlans);
+
return ret;
}
+static void hns3_restore_vlan(struct net_device *netdev)
+{
+ struct hns3_nic_priv *priv = netdev_priv(netdev);
+ u16 vid;
+ int ret;
+
+ for_each_set_bit(vid, priv->active_vlans, VLAN_N_VID) {
+ ret = hns3_vlan_rx_add_vid(netdev, htons(ETH_P_8021Q), vid);
+ if (ret)
+ netdev_warn(netdev, "Restore vlan: %d filter, ret:%d\n",
+ vid, ret);
+ }
+}
+
static int hns3_ndo_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan,
u8 qos, __be16 vlan_proto)
{
@@ -1504,7 +1543,6 @@ static bool hns3_get_tx_timeo_queue_info(struct net_device *ndev)
static void hns3_nic_net_timeout(struct net_device *ndev)
{
struct hns3_nic_priv *priv = netdev_priv(ndev);
- unsigned long last_reset_time = priv->last_reset_time;
struct hnae3_handle *h = priv->ae_handle;
if (!hns3_get_tx_timeo_queue_info(ndev))
@@ -1512,24 +1550,12 @@ static void hns3_nic_net_timeout(struct net_device *ndev)
priv->tx_timeout_count++;
- /* This timeout is far away enough from last timeout,
- * if timeout again,set the reset type to PF reset
- */
- if (time_after(jiffies, (last_reset_time + 20 * HZ)))
- priv->reset_level = HNAE3_FUNC_RESET;
-
- /* Don't do any new action before the next timeout */
- else if (time_before(jiffies, (last_reset_time + ndev->watchdog_timeo)))
+ if (time_before(jiffies, (h->last_reset_time + ndev->watchdog_timeo)))
return;
- priv->last_reset_time = jiffies;
-
+ /* request the reset */
if (h->ae_algo->ops->reset_event)
- h->ae_algo->ops->reset_event(h, priv->reset_level);
-
- priv->reset_level++;
- if (priv->reset_level > HNAE3_GLOBAL_RESET)
- priv->reset_level = HNAE3_GLOBAL_RESET;
+ h->ae_algo->ops->reset_event(h);
}
static const struct net_device_ops hns3_nic_netdev_ops = {
@@ -1588,10 +1614,6 @@ static void hns3_remove(struct pci_dev *pdev)
struct hnae3_ae_dev *ae_dev = pci_get_drvdata(pdev);
hnae3_unregister_ae_dev(ae_dev);
-
- devm_kfree(&pdev->dev, ae_dev);
-
- pci_set_drvdata(pdev, NULL);
}
static struct pci_driver hns3_driver = {
@@ -2064,15 +2086,13 @@ static void hns3_nic_reuse_page(struct sk_buff *skb, int i,
desc = &ring->desc[ring->next_to_clean];
size = le16_to_cpu(desc->rx.size);
- if (twobufs) {
- truesize = hnae_buf_size(ring);
- } else {
- truesize = ALIGN(size, L1_CACHE_BYTES);
+ truesize = hnae_buf_size(ring);
+
+ if (!twobufs)
last_offset = hnae_page_size(ring) - hnae_buf_size(ring);
- }
skb_add_rx_frag(skb, i, desc_cb->priv, desc_cb->page_offset + pull_len,
- size - pull_len, truesize - pull_len);
+ size - pull_len, truesize);
/* Avoid re-using remote pages,flag default unreuse */
if (unlikely(page_to_nid(desc_cb->priv) != numa_node_id()))
@@ -2369,20 +2389,20 @@ out:
static bool hns3_get_new_int_gl(struct hns3_enet_ring_group *ring_group)
{
-#define HNS3_RX_ULTRA_PACKET_RATE 40000
+ struct hns3_enet_tqp_vector *tqp_vector =
+ ring_group->ring->tqp_vector;
enum hns3_flow_level_range new_flow_level;
- struct hns3_enet_tqp_vector *tqp_vector;
- int packets_per_secs;
- int bytes_per_usecs;
+ int packets_per_msecs;
+ int bytes_per_msecs;
+ u32 time_passed_ms;
u16 new_int_gl;
- int usecs;
- if (!ring_group->int_gl)
+ if (!ring_group->coal.int_gl || !tqp_vector->last_jiffies)
return false;
if (ring_group->total_packets == 0) {
- ring_group->int_gl = HNS3_INT_GL_50K;
- ring_group->flow_level = HNS3_FLOW_LOW;
+ ring_group->coal.int_gl = HNS3_INT_GL_50K;
+ ring_group->coal.flow_level = HNS3_FLOW_LOW;
return true;
}
@@ -2392,35 +2412,46 @@ static bool hns3_get_new_int_gl(struct hns3_enet_ring_group *ring_group)
* 20-1249MB/s high (18000 ints/s)
* > 40000pps ultra (8000 ints/s)
*/
- new_flow_level = ring_group->flow_level;
- new_int_gl = ring_group->int_gl;
- tqp_vector = ring_group->ring->tqp_vector;
- usecs = (ring_group->int_gl << 1);
- bytes_per_usecs = ring_group->total_bytes / usecs;
- /* 1000000 microseconds */
- packets_per_secs = ring_group->total_packets * 1000000 / usecs;
+ new_flow_level = ring_group->coal.flow_level;
+ new_int_gl = ring_group->coal.int_gl;
+ time_passed_ms =
+ jiffies_to_msecs(jiffies - tqp_vector->last_jiffies);
+
+ if (!time_passed_ms)
+ return false;
+
+ do_div(ring_group->total_packets, time_passed_ms);
+ packets_per_msecs = ring_group->total_packets;
+
+ do_div(ring_group->total_bytes, time_passed_ms);
+ bytes_per_msecs = ring_group->total_bytes;
+
+#define HNS3_RX_LOW_BYTE_RATE 10000
+#define HNS3_RX_MID_BYTE_RATE 20000
switch (new_flow_level) {
case HNS3_FLOW_LOW:
- if (bytes_per_usecs > 10)
+ if (bytes_per_msecs > HNS3_RX_LOW_BYTE_RATE)
new_flow_level = HNS3_FLOW_MID;
break;
case HNS3_FLOW_MID:
- if (bytes_per_usecs > 20)
+ if (bytes_per_msecs > HNS3_RX_MID_BYTE_RATE)
new_flow_level = HNS3_FLOW_HIGH;
- else if (bytes_per_usecs <= 10)
+ else if (bytes_per_msecs <= HNS3_RX_LOW_BYTE_RATE)
new_flow_level = HNS3_FLOW_LOW;
break;
case HNS3_FLOW_HIGH:
case HNS3_FLOW_ULTRA:
default:
- if (bytes_per_usecs <= 20)
+ if (bytes_per_msecs <= HNS3_RX_MID_BYTE_RATE)
new_flow_level = HNS3_FLOW_MID;
break;
}
- if ((packets_per_secs > HNS3_RX_ULTRA_PACKET_RATE) &&
- (&tqp_vector->rx_group == ring_group))
+#define HNS3_RX_ULTRA_PACKET_RATE 40
+
+ if (packets_per_msecs > HNS3_RX_ULTRA_PACKET_RATE &&
+ &tqp_vector->rx_group == ring_group)
new_flow_level = HNS3_FLOW_ULTRA;
switch (new_flow_level) {
@@ -2442,9 +2473,9 @@ static bool hns3_get_new_int_gl(struct hns3_enet_ring_group *ring_group)
ring_group->total_bytes = 0;
ring_group->total_packets = 0;
- ring_group->flow_level = new_flow_level;
- if (new_int_gl != ring_group->int_gl) {
- ring_group->int_gl = new_int_gl;
+ ring_group->coal.flow_level = new_flow_level;
+ if (new_int_gl != ring_group->coal.int_gl) {
+ ring_group->coal.int_gl = new_int_gl;
return true;
}
return false;
@@ -2456,19 +2487,27 @@ static void hns3_update_new_int_gl(struct hns3_enet_tqp_vector *tqp_vector)
struct hns3_enet_ring_group *tx_group = &tqp_vector->tx_group;
bool rx_update, tx_update;
- if (rx_group->gl_adapt_enable) {
+ if (tqp_vector->int_adapt_down > 0) {
+ tqp_vector->int_adapt_down--;
+ return;
+ }
+
+ if (rx_group->coal.gl_adapt_enable) {
rx_update = hns3_get_new_int_gl(rx_group);
if (rx_update)
hns3_set_vector_coalesce_rx_gl(tqp_vector,
- rx_group->int_gl);
+ rx_group->coal.int_gl);
}
- if (tx_group->gl_adapt_enable) {
+ if (tx_group->coal.gl_adapt_enable) {
tx_update = hns3_get_new_int_gl(&tqp_vector->tx_group);
if (tx_update)
hns3_set_vector_coalesce_tx_gl(tqp_vector,
- tx_group->int_gl);
+ tx_group->coal.int_gl);
}
+
+ tqp_vector->last_jiffies = jiffies;
+ tqp_vector->int_adapt_down = HNS3_INT_ADAPT_DOWN_START;
}
static int hns3_nic_common_poll(struct napi_struct *napi, int budget)
@@ -2615,32 +2654,18 @@ static int hns3_nic_init_vector_data(struct hns3_nic_priv *priv)
struct hnae3_ring_chain_node vector_ring_chain;
struct hnae3_handle *h = priv->ae_handle;
struct hns3_enet_tqp_vector *tqp_vector;
- struct hnae3_vector_info *vector;
- struct pci_dev *pdev = h->pdev;
- u16 tqp_num = h->kinfo.num_tqps;
- u16 vector_num;
int ret = 0;
u16 i;
- /* RSS size, cpu online and vector_num should be the same */
- /* Should consider 2p/4p later */
- vector_num = min_t(u16, num_online_cpus(), tqp_num);
- vector = devm_kcalloc(&pdev->dev, vector_num, sizeof(*vector),
- GFP_KERNEL);
- if (!vector)
- return -ENOMEM;
-
- vector_num = h->ae_algo->ops->get_vector(h, vector_num, vector);
-
- priv->vector_num = vector_num;
- priv->tqp_vector = (struct hns3_enet_tqp_vector *)
- devm_kcalloc(&pdev->dev, vector_num, sizeof(*priv->tqp_vector),
- GFP_KERNEL);
- if (!priv->tqp_vector)
- return -ENOMEM;
+ for (i = 0; i < priv->vector_num; i++) {
+ tqp_vector = &priv->tqp_vector[i];
+ hns3_vector_gl_rl_init_hw(tqp_vector, priv);
+ tqp_vector->num_tqps = 0;
+ }
- for (i = 0; i < tqp_num; i++) {
- u16 vector_i = i % vector_num;
+ for (i = 0; i < h->kinfo.num_tqps; i++) {
+ u16 vector_i = i % priv->vector_num;
+ u16 tqp_num = h->kinfo.num_tqps;
tqp_vector = &priv->tqp_vector[vector_i];
@@ -2650,52 +2675,94 @@ static int hns3_nic_init_vector_data(struct hns3_nic_priv *priv)
hns3_add_ring_to_group(&tqp_vector->rx_group,
priv->ring_data[i + tqp_num].ring);
- tqp_vector->idx = vector_i;
- tqp_vector->mask_addr = vector[vector_i].io_addr;
- tqp_vector->vector_irq = vector[vector_i].vector;
- tqp_vector->num_tqps++;
-
priv->ring_data[i].ring->tqp_vector = tqp_vector;
priv->ring_data[i + tqp_num].ring->tqp_vector = tqp_vector;
+ tqp_vector->num_tqps++;
}
- for (i = 0; i < vector_num; i++) {
+ for (i = 0; i < priv->vector_num; i++) {
tqp_vector = &priv->tqp_vector[i];
tqp_vector->rx_group.total_bytes = 0;
tqp_vector->rx_group.total_packets = 0;
tqp_vector->tx_group.total_bytes = 0;
tqp_vector->tx_group.total_packets = 0;
- hns3_vector_gl_rl_init(tqp_vector, priv);
tqp_vector->handle = h;
ret = hns3_get_vector_ring_chain(tqp_vector,
&vector_ring_chain);
if (ret)
- goto out;
+ return ret;
ret = h->ae_algo->ops->map_ring_to_vector(h,
tqp_vector->vector_irq, &vector_ring_chain);
- if (ret)
- goto out;
hns3_free_vector_ring_chain(tqp_vector, &vector_ring_chain);
+ if (ret)
+ return ret;
+
netif_napi_add(priv->netdev, &tqp_vector->napi,
hns3_nic_common_poll, NAPI_POLL_WEIGHT);
}
+ return 0;
+}
+
+static int hns3_nic_alloc_vector_data(struct hns3_nic_priv *priv)
+{
+ struct hnae3_handle *h = priv->ae_handle;
+ struct hns3_enet_tqp_vector *tqp_vector;
+ struct hnae3_vector_info *vector;
+ struct pci_dev *pdev = h->pdev;
+ u16 tqp_num = h->kinfo.num_tqps;
+ u16 vector_num;
+ int ret = 0;
+ u16 i;
+
+ /* RSS size, cpu online and vector_num should be the same */
+ /* Should consider 2p/4p later */
+ vector_num = min_t(u16, num_online_cpus(), tqp_num);
+ vector = devm_kcalloc(&pdev->dev, vector_num, sizeof(*vector),
+ GFP_KERNEL);
+ if (!vector)
+ return -ENOMEM;
+
+ vector_num = h->ae_algo->ops->get_vector(h, vector_num, vector);
+
+ priv->vector_num = vector_num;
+ priv->tqp_vector = (struct hns3_enet_tqp_vector *)
+ devm_kcalloc(&pdev->dev, vector_num, sizeof(*priv->tqp_vector),
+ GFP_KERNEL);
+ if (!priv->tqp_vector) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < priv->vector_num; i++) {
+ tqp_vector = &priv->tqp_vector[i];
+ tqp_vector->idx = i;
+ tqp_vector->mask_addr = vector[i].io_addr;
+ tqp_vector->vector_irq = vector[i].vector;
+ hns3_vector_gl_rl_init(tqp_vector, priv);
+ }
+
out:
devm_kfree(&pdev->dev, vector);
return ret;
}
+static void hns3_clear_ring_group(struct hns3_enet_ring_group *group)
+{
+ group->ring = NULL;
+ group->count = 0;
+}
+
static int hns3_nic_uninit_vector_data(struct hns3_nic_priv *priv)
{
struct hnae3_ring_chain_node vector_ring_chain;
struct hnae3_handle *h = priv->ae_handle;
struct hns3_enet_tqp_vector *tqp_vector;
- struct pci_dev *pdev = h->pdev;
int i, ret;
for (i = 0; i < priv->vector_num; i++) {
@@ -2711,6 +2778,10 @@ static int hns3_nic_uninit_vector_data(struct hns3_nic_priv *priv)
if (ret)
return ret;
+ ret = h->ae_algo->ops->put_vector(h, tqp_vector->vector_irq);
+ if (ret)
+ return ret;
+
hns3_free_vector_ring_chain(tqp_vector, &vector_ring_chain);
if (priv->tqp_vector[i].irq_init_flag == HNS3_VECTOR_INITED) {
@@ -2722,12 +2793,30 @@ static int hns3_nic_uninit_vector_data(struct hns3_nic_priv *priv)
}
priv->ring_data[i].ring->irq_init_flag = HNS3_VECTOR_NOT_INITED;
-
+ hns3_clear_ring_group(&tqp_vector->rx_group);
+ hns3_clear_ring_group(&tqp_vector->tx_group);
netif_napi_del(&priv->tqp_vector[i].napi);
}
- devm_kfree(&pdev->dev, priv->tqp_vector);
+ return 0;
+}
+
+static int hns3_nic_dealloc_vector_data(struct hns3_nic_priv *priv)
+{
+ struct hnae3_handle *h = priv->ae_handle;
+ struct pci_dev *pdev = h->pdev;
+ int i, ret;
+
+ for (i = 0; i < priv->vector_num; i++) {
+ struct hns3_enet_tqp_vector *tqp_vector;
+
+ tqp_vector = &priv->tqp_vector[i];
+ ret = h->ae_algo->ops->put_vector(h, tqp_vector->vector_irq);
+ if (ret)
+ return ret;
+ }
+ devm_kfree(&pdev->dev, priv->tqp_vector);
return 0;
}
@@ -2957,13 +3046,8 @@ int hns3_uninit_all_ring(struct hns3_nic_priv *priv)
h->ae_algo->ops->reset_queue(h, i);
hns3_fini_ring(priv->ring_data[i].ring);
- devm_kfree(priv->dev, priv->ring_data[i].ring);
hns3_fini_ring(priv->ring_data[i + h->kinfo.num_tqps].ring);
- devm_kfree(priv->dev,
- priv->ring_data[i + h->kinfo.num_tqps].ring);
}
- devm_kfree(priv->dev, priv->ring_data);
-
return 0;
}
@@ -2987,7 +3071,7 @@ static void hns3_init_mac_addr(struct net_device *netdev)
}
if (h->ae_algo->ops->set_mac_addr)
- h->ae_algo->ops->set_mac_addr(h, netdev->dev_addr);
+ h->ae_algo->ops->set_mac_addr(h, netdev->dev_addr, true);
}
@@ -3013,7 +3097,7 @@ static int hns3_client_init(struct hnae3_handle *handle)
int ret;
netdev = alloc_etherdev_mq(sizeof(struct hns3_nic_priv),
- handle->kinfo.num_tqps);
+ hns3_get_max_available_channels(handle));
if (!netdev)
return -ENOMEM;
@@ -3021,8 +3105,8 @@ static int hns3_client_init(struct hnae3_handle *handle)
priv->dev = &pdev->dev;
priv->netdev = netdev;
priv->ae_handle = handle;
- priv->last_reset_time = jiffies;
- priv->reset_level = HNAE3_FUNC_RESET;
+ priv->ae_handle->reset_level = HNAE3_NONE_RESET;
+ priv->ae_handle->last_reset_time = jiffies;
priv->tx_timeout_count = 0;
handle->kinfo.netdev = netdev;
@@ -3048,6 +3132,12 @@ static int hns3_client_init(struct hnae3_handle *handle)
goto out_get_ring_cfg;
}
+ ret = hns3_nic_alloc_vector_data(priv);
+ if (ret) {
+ ret = -ENOMEM;
+ goto out_alloc_vector_data;
+ }
+
ret = hns3_nic_init_vector_data(priv);
if (ret) {
ret = -ENOMEM;
@@ -3076,8 +3166,10 @@ static int hns3_client_init(struct hnae3_handle *handle)
out_reg_netdev_fail:
out_init_ring_data:
(void)hns3_nic_uninit_vector_data(priv);
- priv->ring_data = NULL;
out_init_vector_data:
+ hns3_nic_dealloc_vector_data(priv);
+out_alloc_vector_data:
+ priv->ring_data = NULL;
out_get_ring_cfg:
priv->ae_handle = NULL;
free_netdev(netdev);
@@ -3097,10 +3189,16 @@ static void hns3_client_uninit(struct hnae3_handle *handle, bool reset)
if (ret)
netdev_err(netdev, "uninit vector error\n");
+ ret = hns3_nic_dealloc_vector_data(priv);
+ if (ret)
+ netdev_err(netdev, "dealloc vector error\n");
+
ret = hns3_uninit_all_ring(priv);
if (ret)
netdev_err(netdev, "uninit ring error\n");
+ hns3_put_ring_config(priv);
+
priv->ring_data = NULL;
free_netdev(netdev);
@@ -3240,7 +3338,6 @@ static int hns3_reset_notify_down_enet(struct hnae3_handle *handle)
static int hns3_reset_notify_up_enet(struct hnae3_handle *handle)
{
struct hnae3_knic_private_info *kinfo = &handle->kinfo;
- struct hns3_nic_priv *priv = netdev_priv(kinfo->netdev);
int ret = 0;
if (netif_running(kinfo->netdev)) {
@@ -3250,8 +3347,7 @@ static int hns3_reset_notify_up_enet(struct hnae3_handle *handle)
"hns net up fail, ret=%d!\n", ret);
return ret;
}
-
- priv->last_reset_time = jiffies;
+ handle->last_reset_time = jiffies;
}
return ret;
@@ -3263,11 +3359,14 @@ static int hns3_reset_notify_init_enet(struct hnae3_handle *handle)
struct hns3_nic_priv *priv = netdev_priv(netdev);
int ret;
- priv->reset_level = 1;
hns3_init_mac_addr(netdev);
hns3_nic_set_rx_mode(netdev);
hns3_recover_hw_addr(netdev);
+ /* Hardware table is only clear when pf resets */
+ if (!(handle->flags & HNAE3_SUPPORT_VF))
+ hns3_restore_vlan(netdev);
+
/* Carrier off reporting is important to ethtool even BEFORE open */
netif_carrier_off(netdev);
@@ -3306,6 +3405,8 @@ static int hns3_reset_notify_uninit_enet(struct hnae3_handle *handle)
if (ret)
netdev_err(netdev, "uninit ring error\n");
+ hns3_put_ring_config(priv);
+
priv->ring_data = NULL;
return ret;
@@ -3336,18 +3437,24 @@ static int hns3_reset_notify(struct hnae3_handle *handle,
return ret;
}
-static u16 hns3_get_max_available_channels(struct net_device *netdev)
+static void hns3_restore_coal(struct hns3_nic_priv *priv,
+ struct hns3_enet_coalesce *tx,
+ struct hns3_enet_coalesce *rx)
{
- struct hnae3_handle *h = hns3_get_handle(netdev);
- u16 free_tqps, max_rss_size, max_tqps;
-
- h->ae_algo->ops->get_tqps_and_rss_info(h, &free_tqps, &max_rss_size);
- max_tqps = h->kinfo.num_tc * max_rss_size;
+ u16 vector_num = priv->vector_num;
+ int i;
- return min_t(u16, max_tqps, (free_tqps + h->kinfo.num_tqps));
+ for (i = 0; i < vector_num; i++) {
+ memcpy(&priv->tqp_vector[i].tx_group.coal, tx,
+ sizeof(struct hns3_enet_coalesce));
+ memcpy(&priv->tqp_vector[i].rx_group.coal, rx,
+ sizeof(struct hns3_enet_coalesce));
+ }
}
-static int hns3_modify_tqp_num(struct net_device *netdev, u16 new_tqp_num)
+static int hns3_modify_tqp_num(struct net_device *netdev, u16 new_tqp_num,
+ struct hns3_enet_coalesce *tx,
+ struct hns3_enet_coalesce *rx)
{
struct hns3_nic_priv *priv = netdev_priv(netdev);
struct hnae3_handle *h = hns3_get_handle(netdev);
@@ -3361,6 +3468,12 @@ static int hns3_modify_tqp_num(struct net_device *netdev, u16 new_tqp_num)
if (ret)
return ret;
+ ret = hns3_nic_alloc_vector_data(priv);
+ if (ret)
+ goto err_alloc_vector;
+
+ hns3_restore_coal(priv, tx, rx);
+
ret = hns3_nic_init_vector_data(priv);
if (ret)
goto err_uninit_vector;
@@ -3375,6 +3488,8 @@ err_put_ring:
hns3_put_ring_config(priv);
err_uninit_vector:
hns3_nic_uninit_vector_data(priv);
+err_alloc_vector:
+ hns3_nic_dealloc_vector_data(priv);
return ret;
}
@@ -3389,6 +3504,7 @@ int hns3_set_channels(struct net_device *netdev,
struct hns3_nic_priv *priv = netdev_priv(netdev);
struct hnae3_handle *h = hns3_get_handle(netdev);
struct hnae3_knic_private_info *kinfo = &h->kinfo;
+ struct hns3_enet_coalesce tx_coal, rx_coal;
bool if_running = netif_running(netdev);
u32 new_tqp_num = ch->combined_count;
u16 org_tqp_num;
@@ -3397,12 +3513,12 @@ int hns3_set_channels(struct net_device *netdev,
if (ch->rx_count || ch->tx_count)
return -EINVAL;
- if (new_tqp_num > hns3_get_max_available_channels(netdev) ||
+ if (new_tqp_num > hns3_get_max_available_channels(h) ||
new_tqp_num < kinfo->num_tc) {
dev_err(&netdev->dev,
"Change tqps fail, the tqp range is from %d to %d",
kinfo->num_tc,
- hns3_get_max_available_channels(netdev));
+ hns3_get_max_available_channels(h));
return -EINVAL;
}
@@ -3411,7 +3527,7 @@ int hns3_set_channels(struct net_device *netdev,
return 0;
if (if_running)
- dev_close(netdev);
+ hns3_nic_net_stop(netdev);
hns3_clear_all_ring(h);
@@ -3422,12 +3538,26 @@ int hns3_set_channels(struct net_device *netdev,
goto open_netdev;
}
+ /* Changing the tqp num may also change the vector num,
+ * ethtool only support setting and querying one coal
+ * configuation for now, so save the vector 0' coal
+ * configuation here in order to restore it.
+ */
+ memcpy(&tx_coal, &priv->tqp_vector[0].tx_group.coal,
+ sizeof(struct hns3_enet_coalesce));
+ memcpy(&rx_coal, &priv->tqp_vector[0].rx_group.coal,
+ sizeof(struct hns3_enet_coalesce));
+
+ hns3_nic_dealloc_vector_data(priv);
+
hns3_uninit_all_ring(priv);
+ hns3_put_ring_config(priv);
org_tqp_num = h->kinfo.num_tqps;
- ret = hns3_modify_tqp_num(netdev, new_tqp_num);
+ ret = hns3_modify_tqp_num(netdev, new_tqp_num, &tx_coal, &rx_coal);
if (ret) {
- ret = hns3_modify_tqp_num(netdev, org_tqp_num);
+ ret = hns3_modify_tqp_num(netdev, org_tqp_num,
+ &tx_coal, &rx_coal);
if (ret) {
/* If revert to old tqp failed, fatal error occurred */
dev_err(&netdev->dev,
@@ -3440,7 +3570,7 @@ int hns3_set_channels(struct net_device *netdev,
open_netdev:
if (if_running)
- dev_open(netdev);
+ hns3_nic_net_open(netdev);
return ret;
}
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h
index 213f501b30bb..98cdbd3a1163 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h
@@ -10,6 +10,8 @@
#ifndef __HNS3_ENET_H
#define __HNS3_ENET_H
+#include <linux/if_vlan.h>
+
#include "hnae3.h"
extern const char hns3_driver_version[];
@@ -286,7 +288,7 @@ struct hns3_desc_cb {
u16 page_offset;
u16 reuse_flag;
- u16 length; /* length of the buffer */
+ u32 length; /* length of the buffer */
/* desc type, used by the ring user to mark the type of the priv data */
u16 type;
@@ -460,15 +462,21 @@ enum hns3_link_mode_bits {
#define HNS3_INT_RL_MAX 0x00EC
#define HNS3_INT_RL_ENABLE_MASK 0x40
+#define HNS3_INT_ADAPT_DOWN_START 100
+
+struct hns3_enet_coalesce {
+ u16 int_gl;
+ u8 gl_adapt_enable;
+ enum hns3_flow_level_range flow_level;
+};
+
struct hns3_enet_ring_group {
/* array of pointers to rings */
struct hns3_enet_ring *ring;
u64 total_bytes; /* total bytes processed this group */
u64 total_packets; /* total packets processed this group */
u16 count;
- enum hns3_flow_level_range flow_level;
- u16 int_gl;
- u8 gl_adapt_enable;
+ struct hns3_enet_coalesce coal;
};
struct hns3_enet_tqp_vector {
@@ -491,6 +499,7 @@ struct hns3_enet_tqp_vector {
/* when 0 should adjust interrupt coalesce parameter */
u8 int_adapt_down;
+ unsigned long last_jiffies;
} ____cacheline_internodealigned_in_smp;
enum hns3_udp_tnl_type {
@@ -523,8 +532,6 @@ struct hns3_nic_priv {
/* The most recently read link state */
int link;
u64 tx_timeout_count;
- enum hnae3_reset_type reset_level;
- unsigned long last_reset_time;
unsigned long state;
@@ -535,6 +542,7 @@ struct hns3_nic_priv {
struct notifier_block notifier_block;
/* Vxlan/Geneve information */
struct hns3_udp_tunnel udp_tnl[HNS3_UDP_TNL_MAX];
+ unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
};
union l3_hdr_info {
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
index b034c7f24eda..eb3c34f3cf87 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
@@ -74,19 +74,6 @@ struct hns3_link_mode_mapping {
u32 ethtool_link_mode;
};
-static const struct hns3_link_mode_mapping hns3_lm_map[] = {
- {HNS3_LM_FIBRE_BIT, ETHTOOL_LINK_MODE_FIBRE_BIT},
- {HNS3_LM_AUTONEG_BIT, ETHTOOL_LINK_MODE_Autoneg_BIT},
- {HNS3_LM_TP_BIT, ETHTOOL_LINK_MODE_TP_BIT},
- {HNS3_LM_PAUSE_BIT, ETHTOOL_LINK_MODE_Pause_BIT},
- {HNS3_LM_BACKPLANE_BIT, ETHTOOL_LINK_MODE_Backplane_BIT},
- {HNS3_LM_10BASET_HALF_BIT, ETHTOOL_LINK_MODE_10baseT_Half_BIT},
- {HNS3_LM_10BASET_FULL_BIT, ETHTOOL_LINK_MODE_10baseT_Full_BIT},
- {HNS3_LM_100BASET_HALF_BIT, ETHTOOL_LINK_MODE_100baseT_Half_BIT},
- {HNS3_LM_100BASET_FULL_BIT, ETHTOOL_LINK_MODE_100baseT_Full_BIT},
- {HNS3_LM_1000BASET_FULL_BIT, ETHTOOL_LINK_MODE_1000baseT_Full_BIT},
-};
-
static int hns3_lp_setup(struct net_device *ndev, enum hnae3_loop loop)
{
struct hnae3_handle *h = hns3_get_handle(ndev);
@@ -309,6 +296,9 @@ static void hns3_self_test(struct net_device *ndev,
struct hnae3_handle *h = priv->ae_handle;
int st_param[HNS3_SELF_TEST_TPYE_NUM][2];
bool if_running = netif_running(ndev);
+#if IS_ENABLED(CONFIG_VLAN_8021Q)
+ bool dis_vlan_filter;
+#endif
int test_index = 0;
u32 i;
@@ -323,6 +313,14 @@ static void hns3_self_test(struct net_device *ndev,
if (if_running)
dev_close(ndev);
+#if IS_ENABLED(CONFIG_VLAN_8021Q)
+ /* Disable the vlan filter for selftest does not support it */
+ dis_vlan_filter = (ndev->features & NETIF_F_HW_VLAN_CTAG_FILTER) &&
+ h->ae_algo->ops->enable_vlan_filter;
+ if (dis_vlan_filter)
+ h->ae_algo->ops->enable_vlan_filter(h, false);
+#endif
+
set_bit(HNS3_NIC_STATE_TESTING, &priv->state);
for (i = 0; i < HNS3_SELF_TEST_TPYE_NUM; i++) {
@@ -345,28 +343,15 @@ static void hns3_self_test(struct net_device *ndev,
clear_bit(HNS3_NIC_STATE_TESTING, &priv->state);
+#if IS_ENABLED(CONFIG_VLAN_8021Q)
+ if (dis_vlan_filter)
+ h->ae_algo->ops->enable_vlan_filter(h, true);
+#endif
+
if (if_running)
dev_open(ndev);
}
-static void hns3_driv_to_eth_caps(u32 caps, struct ethtool_link_ksettings *cmd,
- bool is_advertised)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(hns3_lm_map); i++) {
- if (!(caps & hns3_lm_map[i].hns3_link_mode))
- continue;
-
- if (is_advertised)
- __set_bit(hns3_lm_map[i].ethtool_link_mode,
- cmd->link_modes.advertising);
- else
- __set_bit(hns3_lm_map[i].ethtool_link_mode,
- cmd->link_modes.supported);
- }
-}
-
static int hns3_get_sset_count(struct net_device *netdev, int stringset)
{
struct hnae3_handle *h = hns3_get_handle(netdev);
@@ -578,18 +563,19 @@ static int hns3_get_link_ksettings(struct net_device *netdev,
{
struct hnae3_handle *h = hns3_get_handle(netdev);
u32 flowctrl_adv = 0;
- u32 supported_caps;
- u32 advertised_caps;
- u8 media_type = HNAE3_MEDIA_TYPE_UNKNOWN;
u8 link_stat;
if (!h->ae_algo || !h->ae_algo->ops)
return -EOPNOTSUPP;
/* 1.auto_neg & speed & duplex from cmd */
- if (netdev->phydev)
+ if (netdev->phydev) {
phy_ethtool_ksettings_get(netdev->phydev, cmd);
- else if (h->ae_algo->ops->get_ksettings_an_result)
+
+ return 0;
+ }
+
+ if (h->ae_algo->ops->get_ksettings_an_result)
h->ae_algo->ops->get_ksettings_an_result(h,
&cmd->base.autoneg,
&cmd->base.speed,
@@ -603,62 +589,16 @@ static int hns3_get_link_ksettings(struct net_device *netdev,
cmd->base.duplex = DUPLEX_UNKNOWN;
}
- /* 2.media_type get from bios parameter block */
- if (h->ae_algo->ops->get_media_type) {
- h->ae_algo->ops->get_media_type(h, &media_type);
-
- switch (media_type) {
- case HNAE3_MEDIA_TYPE_FIBER:
- cmd->base.port = PORT_FIBRE;
- supported_caps = HNS3_LM_FIBRE_BIT |
- HNS3_LM_AUTONEG_BIT |
- HNS3_LM_PAUSE_BIT |
- HNS3_LM_1000BASET_FULL_BIT;
-
- advertised_caps = supported_caps;
- break;
- case HNAE3_MEDIA_TYPE_COPPER:
- cmd->base.port = PORT_TP;
- supported_caps = HNS3_LM_TP_BIT |
- HNS3_LM_AUTONEG_BIT |
- HNS3_LM_PAUSE_BIT |
- HNS3_LM_1000BASET_FULL_BIT |
- HNS3_LM_100BASET_FULL_BIT |
- HNS3_LM_100BASET_HALF_BIT |
- HNS3_LM_10BASET_FULL_BIT |
- HNS3_LM_10BASET_HALF_BIT;
- advertised_caps = supported_caps;
- break;
- case HNAE3_MEDIA_TYPE_BACKPLANE:
- cmd->base.port = PORT_NONE;
- supported_caps = HNS3_LM_BACKPLANE_BIT |
- HNS3_LM_PAUSE_BIT |
- HNS3_LM_AUTONEG_BIT |
- HNS3_LM_1000BASET_FULL_BIT |
- HNS3_LM_100BASET_FULL_BIT |
- HNS3_LM_100BASET_HALF_BIT |
- HNS3_LM_10BASET_FULL_BIT |
- HNS3_LM_10BASET_HALF_BIT;
-
- advertised_caps = supported_caps;
- break;
- case HNAE3_MEDIA_TYPE_UNKNOWN:
- default:
- cmd->base.port = PORT_OTHER;
- supported_caps = 0;
- advertised_caps = 0;
- break;
- }
-
- if (!cmd->base.autoneg)
- advertised_caps &= ~HNS3_LM_AUTONEG_BIT;
-
- advertised_caps &= ~HNS3_LM_PAUSE_BIT;
+ /* 2.get link mode and port type*/
+ if (h->ae_algo->ops->get_link_mode)
+ h->ae_algo->ops->get_link_mode(h,
+ cmd->link_modes.supported,
+ cmd->link_modes.advertising);
- /* now, map driver link modes to ethtool link modes */
- hns3_driv_to_eth_caps(supported_caps, cmd, false);
- hns3_driv_to_eth_caps(advertised_caps, cmd, true);
- }
+ cmd->base.port = PORT_NONE;
+ if (h->ae_algo->ops->get_port_type)
+ h->ae_algo->ops->get_port_type(h,
+ &cmd->base.port);
/* 3.mdix_ctrl&mdix get from phy reg */
if (h->ae_algo->ops->get_mdix_mode)
@@ -698,7 +638,7 @@ static u32 hns3_get_rss_key_size(struct net_device *netdev)
if (!h->ae_algo || !h->ae_algo->ops ||
!h->ae_algo->ops->get_rss_key_size)
- return -EOPNOTSUPP;
+ return 0;
return h->ae_algo->ops->get_rss_key_size(h);
}
@@ -709,7 +649,7 @@ static u32 hns3_get_rss_indir_size(struct net_device *netdev)
if (!h->ae_algo || !h->ae_algo->ops ||
!h->ae_algo->ops->get_rss_indir_size)
- return -EOPNOTSUPP;
+ return 0;
return h->ae_algo->ops->get_rss_indir_size(h);
}
@@ -905,11 +845,13 @@ static int hns3_get_coalesce_per_queue(struct net_device *netdev, u32 queue,
tx_vector = priv->ring_data[queue].ring->tqp_vector;
rx_vector = priv->ring_data[queue_num + queue].ring->tqp_vector;
- cmd->use_adaptive_tx_coalesce = tx_vector->tx_group.gl_adapt_enable;
- cmd->use_adaptive_rx_coalesce = rx_vector->rx_group.gl_adapt_enable;
+ cmd->use_adaptive_tx_coalesce =
+ tx_vector->tx_group.coal.gl_adapt_enable;
+ cmd->use_adaptive_rx_coalesce =
+ rx_vector->rx_group.coal.gl_adapt_enable;
- cmd->tx_coalesce_usecs = tx_vector->tx_group.int_gl;
- cmd->rx_coalesce_usecs = rx_vector->rx_group.int_gl;
+ cmd->tx_coalesce_usecs = tx_vector->tx_group.coal.int_gl;
+ cmd->rx_coalesce_usecs = rx_vector->rx_group.coal.int_gl;
cmd->tx_coalesce_usecs_high = h->kinfo.int_rl_setting;
cmd->rx_coalesce_usecs_high = h->kinfo.int_rl_setting;
@@ -1029,14 +971,18 @@ static void hns3_set_coalesce_per_queue(struct net_device *netdev,
tx_vector = priv->ring_data[queue].ring->tqp_vector;
rx_vector = priv->ring_data[queue_num + queue].ring->tqp_vector;
- tx_vector->tx_group.gl_adapt_enable = cmd->use_adaptive_tx_coalesce;
- rx_vector->rx_group.gl_adapt_enable = cmd->use_adaptive_rx_coalesce;
+ tx_vector->tx_group.coal.gl_adapt_enable =
+ cmd->use_adaptive_tx_coalesce;
+ rx_vector->rx_group.coal.gl_adapt_enable =
+ cmd->use_adaptive_rx_coalesce;
- tx_vector->tx_group.int_gl = cmd->tx_coalesce_usecs;
- rx_vector->rx_group.int_gl = cmd->rx_coalesce_usecs;
+ tx_vector->tx_group.coal.int_gl = cmd->tx_coalesce_usecs;
+ rx_vector->rx_group.coal.int_gl = cmd->rx_coalesce_usecs;
- hns3_set_vector_coalesce_tx_gl(tx_vector, tx_vector->tx_group.int_gl);
- hns3_set_vector_coalesce_rx_gl(rx_vector, rx_vector->rx_group.int_gl);
+ hns3_set_vector_coalesce_tx_gl(tx_vector,
+ tx_vector->tx_group.coal.int_gl);
+ hns3_set_vector_coalesce_rx_gl(rx_vector,
+ rx_vector->rx_group.coal.int_gl);
hns3_set_vector_coalesce_rl(tx_vector, h->kinfo.int_rl_setting);
hns3_set_vector_coalesce_rl(rx_vector, h->kinfo.int_rl_setting);
@@ -1111,6 +1057,7 @@ static const struct ethtool_ops hns3vf_ethtool_ops = {
.get_channels = hns3_get_channels,
.get_coalesce = hns3_get_coalesce,
.set_coalesce = hns3_set_coalesce,
+ .get_link = hns3_get_link,
};
static const struct ethtool_ops hns3_ethtool_ops = {
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
index 3fd10a6bec53..ee3cbac6dfaa 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
@@ -12,7 +12,7 @@
#include <linux/types.h>
#include <linux/io.h>
-#define HCLGE_CMDQ_TX_TIMEOUT 1000
+#define HCLGE_CMDQ_TX_TIMEOUT 30000
struct hclge_dev;
struct hclge_desc {
@@ -414,6 +414,8 @@ struct hclge_pf_res_cmd {
#define HCLGE_CFG_DEFAULT_SPEED_M GENMASK(23, 16)
#define HCLGE_CFG_RSS_SIZE_S 24
#define HCLGE_CFG_RSS_SIZE_M GENMASK(31, 24)
+#define HCLGE_CFG_SPEED_ABILITY_S 0
+#define HCLGE_CFG_SPEED_ABILITY_M GENMASK(7, 0)
struct hclge_cfg_param_cmd {
__le32 offset;
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c
index 5018d6633133..955f0e3d5c95 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c
@@ -144,6 +144,8 @@ static int hclge_map_update(struct hnae3_handle *h)
if (ret)
return ret;
+ hclge_rss_indir_init_cfg(hdev);
+
return hclge_rss_init_hw(hdev);
}
@@ -203,9 +205,11 @@ static int hclge_ieee_setets(struct hnae3_handle *h, struct ieee_ets *ets)
static int hclge_ieee_getpfc(struct hnae3_handle *h, struct ieee_pfc *pfc)
{
+ u64 requests[HNAE3_MAX_TC], indications[HNAE3_MAX_TC];
struct hclge_vport *vport = hclge_get_vport(h);
struct hclge_dev *hdev = vport->back;
u8 i, j, pfc_map, *prio_tc;
+ int ret;
memset(pfc, 0, sizeof(*pfc));
pfc->pfc_cap = hdev->pfc_max;
@@ -220,6 +224,18 @@ static int hclge_ieee_getpfc(struct hnae3_handle *h, struct ieee_pfc *pfc)
}
}
+ ret = hclge_pfc_tx_stats_get(hdev, requests);
+ if (ret)
+ return ret;
+
+ ret = hclge_pfc_rx_stats_get(hdev, indications);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < HCLGE_MAX_TC_NUM; i++) {
+ pfc->requests[i] = requests[i];
+ pfc->indications[i] = indications[i];
+ }
return 0;
}
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
index 32bc6f68e297..2066dd734444 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
@@ -55,6 +55,8 @@ static const struct pci_device_id ae_algo_pci_tbl[] = {
{0, }
};
+MODULE_DEVICE_TABLE(pci, ae_algo_pci_tbl);
+
static const char hns3_nic_test_strs[][ETH_GSTRING_LEN] = {
"Mac Loopback test",
"Serdes Loopback test",
@@ -1024,6 +1026,45 @@ static int hclge_parse_speed(int speed_cmd, int *speed)
return 0;
}
+static void hclge_parse_fiber_link_mode(struct hclge_dev *hdev,
+ u8 speed_ability)
+{
+ unsigned long *supported = hdev->hw.mac.supported;
+
+ if (speed_ability & HCLGE_SUPPORT_1G_BIT)
+ set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
+ supported);
+
+ if (speed_ability & HCLGE_SUPPORT_10G_BIT)
+ set_bit(ETHTOOL_LINK_MODE_10000baseSR_Full_BIT,
+ supported);
+
+ if (speed_ability & HCLGE_SUPPORT_25G_BIT)
+ set_bit(ETHTOOL_LINK_MODE_25000baseSR_Full_BIT,
+ supported);
+
+ if (speed_ability & HCLGE_SUPPORT_50G_BIT)
+ set_bit(ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT,
+ supported);
+
+ if (speed_ability & HCLGE_SUPPORT_100G_BIT)
+ set_bit(ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT,
+ supported);
+
+ set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, supported);
+ set_bit(ETHTOOL_LINK_MODE_Pause_BIT, supported);
+}
+
+static void hclge_parse_link_mode(struct hclge_dev *hdev, u8 speed_ability)
+{
+ u8 media_type = hdev->hw.mac.media_type;
+
+ if (media_type != HNAE3_MEDIA_TYPE_FIBER)
+ return;
+
+ hclge_parse_fiber_link_mode(hdev, speed_ability);
+}
+
static void hclge_parse_cfg(struct hclge_cfg *cfg, struct hclge_desc *desc)
{
struct hclge_cfg_param_cmd *req;
@@ -1072,6 +1113,10 @@ static void hclge_parse_cfg(struct hclge_cfg *cfg, struct hclge_desc *desc)
req = (struct hclge_cfg_param_cmd *)desc[1].data;
cfg->numa_node_map = __le32_to_cpu(req->param[0]);
+
+ cfg->speed_ability = hnae_get_field(__le32_to_cpu(req->param[1]),
+ HCLGE_CFG_SPEED_ABILITY_M,
+ HCLGE_CFG_SPEED_ABILITY_S);
}
/* hclge_get_cfg: query the static parameter from flash
@@ -1160,6 +1205,8 @@ static int hclge_configure(struct hclge_dev *hdev)
return ret;
}
+ hclge_parse_link_mode(hdev, cfg.speed_ability);
+
if ((hdev->tc_max > HNAE3_MAX_TC) ||
(hdev->tc_max < 1)) {
dev_warn(&hdev->pdev->dev, "TC num = %d.\n",
@@ -2702,7 +2749,7 @@ static int hclge_reset_wait(struct hclge_dev *hdev)
return 0;
}
-static int hclge_func_reset_cmd(struct hclge_dev *hdev, int func_id)
+int hclge_func_reset_cmd(struct hclge_dev *hdev, int func_id)
{
struct hclge_desc desc;
struct hclge_reset_cmd *req = (struct hclge_reset_cmd *)desc.data;
@@ -2798,27 +2845,31 @@ static void hclge_reset(struct hclge_dev *hdev)
hclge_notify_client(hdev, HNAE3_UP_CLIENT);
}
-static void hclge_reset_event(struct hnae3_handle *handle,
- enum hnae3_reset_type reset)
+static void hclge_reset_event(struct hnae3_handle *handle)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
- dev_info(&hdev->pdev->dev,
- "Receive reset event , reset_type is %d", reset);
+ /* check if this is a new reset request and we are not here just because
+ * last reset attempt did not succeed and watchdog hit us again. We will
+ * know this if last reset request did not occur very recently (watchdog
+ * timer = 5*HZ, let us check after sufficiently large time, say 4*5*Hz)
+ * In case of new request we reset the "reset level" to PF reset.
+ */
+ if (time_after(jiffies, (handle->last_reset_time + 4 * 5 * HZ)))
+ handle->reset_level = HNAE3_FUNC_RESET;
- switch (reset) {
- case HNAE3_FUNC_RESET:
- case HNAE3_CORE_RESET:
- case HNAE3_GLOBAL_RESET:
- /* request reset & schedule reset task */
- set_bit(reset, &hdev->reset_request);
- hclge_reset_task_schedule(hdev);
- break;
- default:
- dev_warn(&hdev->pdev->dev, "Unsupported reset event:%d", reset);
- break;
- }
+ dev_info(&hdev->pdev->dev, "received reset event , reset type is %d",
+ handle->reset_level);
+
+ /* request reset & schedule reset task */
+ set_bit(handle->reset_level, &hdev->reset_request);
+ hclge_reset_task_schedule(hdev);
+
+ if (handle->reset_level < HNAE3_GLOBAL_RESET)
+ handle->reset_level++;
+
+ handle->last_reset_time = jiffies;
}
static void hclge_reset_subtask(struct hclge_dev *hdev)
@@ -2969,6 +3020,24 @@ static int hclge_get_vector_index(struct hclge_dev *hdev, int vector)
return -EINVAL;
}
+static int hclge_put_vector(struct hnae3_handle *handle, int vector)
+{
+ struct hclge_vport *vport = hclge_get_vport(handle);
+ struct hclge_dev *hdev = vport->back;
+ int vector_id;
+
+ vector_id = hclge_get_vector_index(hdev, vector);
+ if (vector_id < 0) {
+ dev_err(&hdev->pdev->dev,
+ "Get vector index fail. vector_id =%d\n", vector_id);
+ return vector_id;
+ }
+
+ hclge_free_vector(hdev, vector_id);
+
+ return 0;
+}
+
static u32 hclge_get_rss_key_size(struct hnae3_handle *handle)
{
return HCLGE_RSS_KEY_SIZE;
@@ -2979,31 +3048,6 @@ static u32 hclge_get_rss_indir_size(struct hnae3_handle *handle)
return HCLGE_RSS_IND_TBL_SIZE;
}
-static int hclge_get_rss_algo(struct hclge_dev *hdev)
-{
- struct hclge_rss_config_cmd *req;
- struct hclge_desc desc;
- int rss_hash_algo;
- int ret;
-
- hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_RSS_GENERIC_CONFIG, true);
-
- ret = hclge_cmd_send(&hdev->hw, &desc, 1);
- if (ret) {
- dev_err(&hdev->pdev->dev,
- "Get link status error, status =%d\n", ret);
- return ret;
- }
-
- req = (struct hclge_rss_config_cmd *)desc.data;
- rss_hash_algo = (req->hash_config & HCLGE_RSS_HASH_ALGO_MASK);
-
- if (rss_hash_algo == HCLGE_RSS_HASH_ALGO_TOEPLITZ)
- return ETH_RSS_HASH_TOP;
-
- return -EINVAL;
-}
-
static int hclge_set_rss_algo_key(struct hclge_dev *hdev,
const u8 hfunc, const u8 *key)
{
@@ -3042,7 +3086,7 @@ static int hclge_set_rss_algo_key(struct hclge_dev *hdev,
return 0;
}
-static int hclge_set_rss_indir_table(struct hclge_dev *hdev, const u32 *indir)
+static int hclge_set_rss_indir_table(struct hclge_dev *hdev, const u8 *indir)
{
struct hclge_rss_indirection_table_cmd *req;
struct hclge_desc desc;
@@ -3116,14 +3160,16 @@ static int hclge_set_rss_input_tuple(struct hclge_dev *hdev)
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_RSS_INPUT_TUPLE, false);
req = (struct hclge_rss_input_tuple_cmd *)desc.data;
- req->ipv4_tcp_en = HCLGE_RSS_INPUT_TUPLE_OTHER;
- req->ipv4_udp_en = HCLGE_RSS_INPUT_TUPLE_OTHER;
- req->ipv4_sctp_en = HCLGE_RSS_INPUT_TUPLE_SCTP;
- req->ipv4_fragment_en = HCLGE_RSS_INPUT_TUPLE_OTHER;
- req->ipv6_tcp_en = HCLGE_RSS_INPUT_TUPLE_OTHER;
- req->ipv6_udp_en = HCLGE_RSS_INPUT_TUPLE_OTHER;
- req->ipv6_sctp_en = HCLGE_RSS_INPUT_TUPLE_SCTP;
- req->ipv6_fragment_en = HCLGE_RSS_INPUT_TUPLE_OTHER;
+
+ /* Get the tuple cfg from pf */
+ req->ipv4_tcp_en = hdev->vport[0].rss_tuple_sets.ipv4_tcp_en;
+ req->ipv4_udp_en = hdev->vport[0].rss_tuple_sets.ipv4_udp_en;
+ req->ipv4_sctp_en = hdev->vport[0].rss_tuple_sets.ipv4_sctp_en;
+ req->ipv4_fragment_en = hdev->vport[0].rss_tuple_sets.ipv4_fragment_en;
+ req->ipv6_tcp_en = hdev->vport[0].rss_tuple_sets.ipv6_tcp_en;
+ req->ipv6_udp_en = hdev->vport[0].rss_tuple_sets.ipv6_udp_en;
+ req->ipv6_sctp_en = hdev->vport[0].rss_tuple_sets.ipv6_sctp_en;
+ req->ipv6_fragment_en = hdev->vport[0].rss_tuple_sets.ipv6_fragment_en;
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret) {
dev_err(&hdev->pdev->dev,
@@ -3138,12 +3184,11 @@ static int hclge_get_rss(struct hnae3_handle *handle, u32 *indir,
u8 *key, u8 *hfunc)
{
struct hclge_vport *vport = hclge_get_vport(handle);
- struct hclge_dev *hdev = vport->back;
int i;
/* Get hash algorithm */
if (hfunc)
- *hfunc = hclge_get_rss_algo(hdev);
+ *hfunc = vport->rss_algo;
/* Get the RSS Key required by the user */
if (key)
@@ -3167,8 +3212,6 @@ static int hclge_set_rss(struct hnae3_handle *handle, const u32 *indir,
/* Set the RSS Hash Key if specififed by the user */
if (key) {
- /* Update the shadow RSS key with user specified qids */
- memcpy(vport->rss_hash_key, key, HCLGE_RSS_KEY_SIZE);
if (hfunc == ETH_RSS_HASH_TOP ||
hfunc == ETH_RSS_HASH_NO_CHANGE)
@@ -3178,6 +3221,10 @@ static int hclge_set_rss(struct hnae3_handle *handle, const u32 *indir,
ret = hclge_set_rss_algo_key(hdev, hash_algo, key);
if (ret)
return ret;
+
+ /* Update the shadow RSS key with user specified qids */
+ memcpy(vport->rss_hash_key, key, HCLGE_RSS_KEY_SIZE);
+ vport->rss_algo = hash_algo;
}
/* Update the shadow RSS table with user specified qids */
@@ -3185,8 +3232,7 @@ static int hclge_set_rss(struct hnae3_handle *handle, const u32 *indir,
vport->rss_indirection_tbl[i] = indir[i];
/* Update the hardware */
- ret = hclge_set_rss_indir_table(hdev, indir);
- return ret;
+ return hclge_set_rss_indir_table(hdev, vport->rss_indirection_tbl);
}
static u8 hclge_get_rss_hash_bits(struct ethtool_rxnfc *nfc)
@@ -3229,15 +3275,16 @@ static int hclge_set_rss_tuple(struct hnae3_handle *handle,
return -EINVAL;
req = (struct hclge_rss_input_tuple_cmd *)desc.data;
- hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_RSS_INPUT_TUPLE, true);
- ret = hclge_cmd_send(&hdev->hw, &desc, 1);
- if (ret) {
- dev_err(&hdev->pdev->dev,
- "Read rss tuple fail, status = %d\n", ret);
- return ret;
- }
+ hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_RSS_INPUT_TUPLE, false);
- hclge_cmd_reuse_desc(&desc, false);
+ req->ipv4_tcp_en = vport->rss_tuple_sets.ipv4_tcp_en;
+ req->ipv4_udp_en = vport->rss_tuple_sets.ipv4_udp_en;
+ req->ipv4_sctp_en = vport->rss_tuple_sets.ipv4_sctp_en;
+ req->ipv4_fragment_en = vport->rss_tuple_sets.ipv4_fragment_en;
+ req->ipv6_tcp_en = vport->rss_tuple_sets.ipv6_tcp_en;
+ req->ipv6_udp_en = vport->rss_tuple_sets.ipv6_udp_en;
+ req->ipv6_sctp_en = vport->rss_tuple_sets.ipv6_sctp_en;
+ req->ipv6_fragment_en = vport->rss_tuple_sets.ipv6_fragment_en;
tuple_sets = hclge_get_rss_hash_bits(nfc);
switch (nfc->flow_type) {
@@ -3274,52 +3321,49 @@ static int hclge_set_rss_tuple(struct hnae3_handle *handle,
}
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
- if (ret)
+ if (ret) {
dev_err(&hdev->pdev->dev,
"Set rss tuple fail, status = %d\n", ret);
+ return ret;
+ }
- return ret;
+ vport->rss_tuple_sets.ipv4_tcp_en = req->ipv4_tcp_en;
+ vport->rss_tuple_sets.ipv4_udp_en = req->ipv4_udp_en;
+ vport->rss_tuple_sets.ipv4_sctp_en = req->ipv4_sctp_en;
+ vport->rss_tuple_sets.ipv4_fragment_en = req->ipv4_fragment_en;
+ vport->rss_tuple_sets.ipv6_tcp_en = req->ipv6_tcp_en;
+ vport->rss_tuple_sets.ipv6_udp_en = req->ipv6_udp_en;
+ vport->rss_tuple_sets.ipv6_sctp_en = req->ipv6_sctp_en;
+ vport->rss_tuple_sets.ipv6_fragment_en = req->ipv6_fragment_en;
+ return 0;
}
static int hclge_get_rss_tuple(struct hnae3_handle *handle,
struct ethtool_rxnfc *nfc)
{
struct hclge_vport *vport = hclge_get_vport(handle);
- struct hclge_dev *hdev = vport->back;
- struct hclge_rss_input_tuple_cmd *req;
- struct hclge_desc desc;
u8 tuple_sets;
- int ret;
nfc->data = 0;
- req = (struct hclge_rss_input_tuple_cmd *)desc.data;
- hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_RSS_INPUT_TUPLE, true);
- ret = hclge_cmd_send(&hdev->hw, &desc, 1);
- if (ret) {
- dev_err(&hdev->pdev->dev,
- "Read rss tuple fail, status = %d\n", ret);
- return ret;
- }
-
switch (nfc->flow_type) {
case TCP_V4_FLOW:
- tuple_sets = req->ipv4_tcp_en;
+ tuple_sets = vport->rss_tuple_sets.ipv4_tcp_en;
break;
case UDP_V4_FLOW:
- tuple_sets = req->ipv4_udp_en;
+ tuple_sets = vport->rss_tuple_sets.ipv4_udp_en;
break;
case TCP_V6_FLOW:
- tuple_sets = req->ipv6_tcp_en;
+ tuple_sets = vport->rss_tuple_sets.ipv6_tcp_en;
break;
case UDP_V6_FLOW:
- tuple_sets = req->ipv6_udp_en;
+ tuple_sets = vport->rss_tuple_sets.ipv6_udp_en;
break;
case SCTP_V4_FLOW:
- tuple_sets = req->ipv4_sctp_en;
+ tuple_sets = vport->rss_tuple_sets.ipv4_sctp_en;
break;
case SCTP_V6_FLOW:
- tuple_sets = req->ipv6_sctp_en;
+ tuple_sets = vport->rss_tuple_sets.ipv6_sctp_en;
break;
case IPV4_FLOW:
case IPV6_FLOW:
@@ -3354,50 +3398,28 @@ static int hclge_get_tc_size(struct hnae3_handle *handle)
int hclge_rss_init_hw(struct hclge_dev *hdev)
{
- const u8 hfunc = HCLGE_RSS_HASH_ALGO_TOEPLITZ;
struct hclge_vport *vport = hdev->vport;
+ u8 *rss_indir = vport[0].rss_indirection_tbl;
+ u16 rss_size = vport[0].alloc_rss_size;
+ u8 *key = vport[0].rss_hash_key;
+ u8 hfunc = vport[0].rss_algo;
u16 tc_offset[HCLGE_MAX_TC_NUM];
- u8 rss_key[HCLGE_RSS_KEY_SIZE];
u16 tc_valid[HCLGE_MAX_TC_NUM];
u16 tc_size[HCLGE_MAX_TC_NUM];
- u32 *rss_indir = NULL;
- u16 rss_size = 0, roundup_size;
- const u8 *key;
- int i, ret, j;
-
- rss_indir = kcalloc(HCLGE_RSS_IND_TBL_SIZE, sizeof(u32), GFP_KERNEL);
- if (!rss_indir)
- return -ENOMEM;
-
- /* Get default RSS key */
- netdev_rss_key_fill(rss_key, HCLGE_RSS_KEY_SIZE);
-
- /* Initialize RSS indirect table for each vport */
- for (j = 0; j < hdev->num_vmdq_vport + 1; j++) {
- for (i = 0; i < HCLGE_RSS_IND_TBL_SIZE; i++) {
- vport[j].rss_indirection_tbl[i] =
- i % vport[j].alloc_rss_size;
-
- /* vport 0 is for PF */
- if (j != 0)
- continue;
+ u16 roundup_size;
+ int i, ret;
- rss_size = vport[j].alloc_rss_size;
- rss_indir[i] = vport[j].rss_indirection_tbl[i];
- }
- }
ret = hclge_set_rss_indir_table(hdev, rss_indir);
if (ret)
- goto err;
+ return ret;
- key = rss_key;
ret = hclge_set_rss_algo_key(hdev, hfunc, key);
if (ret)
- goto err;
+ return ret;
ret = hclge_set_rss_input_tuple(hdev);
if (ret)
- goto err;
+ return ret;
/* Each TC have the same queue size, and tc_size set to hardware is
* the log2 of roundup power of two of rss_size, the acutal queue
@@ -3407,8 +3429,7 @@ int hclge_rss_init_hw(struct hclge_dev *hdev)
dev_err(&hdev->pdev->dev,
"Configure rss tc size failed, invalid TC_SIZE = %d\n",
rss_size);
- ret = -EINVAL;
- goto err;
+ return -EINVAL;
}
roundup_size = roundup_pow_of_two(rss_size);
@@ -3425,12 +3446,50 @@ int hclge_rss_init_hw(struct hclge_dev *hdev)
tc_offset[i] = rss_size * i;
}
- ret = hclge_set_rss_tc_mode(hdev, tc_valid, tc_size, tc_offset);
+ return hclge_set_rss_tc_mode(hdev, tc_valid, tc_size, tc_offset);
+}
-err:
- kfree(rss_indir);
+void hclge_rss_indir_init_cfg(struct hclge_dev *hdev)
+{
+ struct hclge_vport *vport = hdev->vport;
+ int i, j;
- return ret;
+ for (j = 0; j < hdev->num_vmdq_vport + 1; j++) {
+ for (i = 0; i < HCLGE_RSS_IND_TBL_SIZE; i++)
+ vport[j].rss_indirection_tbl[i] =
+ i % vport[j].alloc_rss_size;
+ }
+}
+
+static void hclge_rss_init_cfg(struct hclge_dev *hdev)
+{
+ struct hclge_vport *vport = hdev->vport;
+ int i;
+
+ for (i = 0; i < hdev->num_vmdq_vport + 1; i++) {
+ vport[i].rss_tuple_sets.ipv4_tcp_en =
+ HCLGE_RSS_INPUT_TUPLE_OTHER;
+ vport[i].rss_tuple_sets.ipv4_udp_en =
+ HCLGE_RSS_INPUT_TUPLE_OTHER;
+ vport[i].rss_tuple_sets.ipv4_sctp_en =
+ HCLGE_RSS_INPUT_TUPLE_SCTP;
+ vport[i].rss_tuple_sets.ipv4_fragment_en =
+ HCLGE_RSS_INPUT_TUPLE_OTHER;
+ vport[i].rss_tuple_sets.ipv6_tcp_en =
+ HCLGE_RSS_INPUT_TUPLE_OTHER;
+ vport[i].rss_tuple_sets.ipv6_udp_en =
+ HCLGE_RSS_INPUT_TUPLE_OTHER;
+ vport[i].rss_tuple_sets.ipv6_sctp_en =
+ HCLGE_RSS_INPUT_TUPLE_SCTP;
+ vport[i].rss_tuple_sets.ipv6_fragment_en =
+ HCLGE_RSS_INPUT_TUPLE_OTHER;
+
+ vport[i].rss_algo = HCLGE_RSS_HASH_ALGO_TOEPLITZ;
+
+ netdev_rss_key_fill(vport[i].rss_hash_key, HCLGE_RSS_KEY_SIZE);
+ }
+
+ hclge_rss_indir_init_cfg(hdev);
}
int hclge_bind_ring_with_vector(struct hclge_vport *vport,
@@ -3525,6 +3584,9 @@ static int hclge_unmap_ring_frm_vector(struct hnae3_handle *handle,
struct hclge_dev *hdev = vport->back;
int vector_id, ret;
+ if (test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state))
+ return 0;
+
vector_id = hclge_get_vector_index(hdev, vector);
if (vector_id < 0) {
dev_err(&handle->pdev->dev,
@@ -3533,18 +3595,13 @@ static int hclge_unmap_ring_frm_vector(struct hnae3_handle *handle,
}
ret = hclge_bind_ring_with_vector(vport, vector_id, false, ring_chain);
- if (ret) {
+ if (ret)
dev_err(&handle->pdev->dev,
"Unmap ring from vector fail. vectorid=%d, ret =%d\n",
vector_id,
ret);
- return ret;
- }
- /* Free this MSIX or MSI vector */
- hclge_free_vector(hdev, vector_id);
-
- return 0;
+ return ret;
}
int hclge_cmd_set_promisc_mode(struct hclge_dev *hdev,
@@ -3717,32 +3774,26 @@ static int hclge_ae_start(struct hnae3_handle *handle)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
- int i, queue_id, ret;
+ int i, ret;
- for (i = 0; i < vport->alloc_tqps; i++) {
- /* todo clear interrupt */
- /* ring enable */
- queue_id = hclge_get_queue_id(handle->kinfo.tqp[i]);
- if (queue_id < 0) {
- dev_warn(&hdev->pdev->dev,
- "Get invalid queue id, ignore it\n");
- continue;
- }
+ for (i = 0; i < vport->alloc_tqps; i++)
+ hclge_tqp_enable(hdev, i, 0, true);
- hclge_tqp_enable(hdev, queue_id, 0, true);
- }
/* mac enable */
hclge_cfg_mac_mode(hdev, true);
clear_bit(HCLGE_STATE_DOWN, &hdev->state);
mod_timer(&hdev->service_timer, jiffies + HZ);
+ /* reset tqp stats */
+ hclge_reset_tqp_stats(handle);
+
+ if (test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state))
+ return 0;
+
ret = hclge_mac_start_phy(hdev);
if (ret)
return ret;
- /* reset tqp stats */
- hclge_reset_tqp_stats(handle);
-
return 0;
}
@@ -3750,19 +3801,17 @@ static void hclge_ae_stop(struct hnae3_handle *handle)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
- int i, queue_id;
+ int i;
- for (i = 0; i < vport->alloc_tqps; i++) {
- /* Ring disable */
- queue_id = hclge_get_queue_id(handle->kinfo.tqp[i]);
- if (queue_id < 0) {
- dev_warn(&hdev->pdev->dev,
- "Get invalid queue id, ignore it\n");
- continue;
- }
+ del_timer_sync(&hdev->service_timer);
+ cancel_work_sync(&hdev->service_task);
+
+ if (test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state))
+ return;
+
+ for (i = 0; i < vport->alloc_tqps; i++)
+ hclge_tqp_enable(hdev, i, 0, false);
- hclge_tqp_enable(hdev, queue_id, 0, false);
- }
/* Mac disable */
hclge_cfg_mac_mode(hdev, false);
@@ -3770,6 +3819,7 @@ static void hclge_ae_stop(struct hnae3_handle *handle)
/* reset tqp stats */
hclge_reset_tqp_stats(handle);
+ hclge_update_link_status(hdev);
}
static int hclge_get_mac_vlan_cmd_status(struct hclge_vport *vport,
@@ -3790,11 +3840,11 @@ static int hclge_get_mac_vlan_cmd_status(struct hclge_vport *vport,
if ((!resp_code) || (resp_code == 1)) {
return_status = 0;
} else if (resp_code == 2) {
- return_status = -EIO;
+ return_status = -ENOSPC;
dev_err(&hdev->pdev->dev,
"add mac addr failed for uc_overflow.\n");
} else if (resp_code == 3) {
- return_status = -EIO;
+ return_status = -ENOSPC;
dev_err(&hdev->pdev->dev,
"add mac addr failed for mc_overflow.\n");
} else {
@@ -3806,7 +3856,7 @@ static int hclge_get_mac_vlan_cmd_status(struct hclge_vport *vport,
if (!resp_code) {
return_status = 0;
} else if (resp_code == 1) {
- return_status = -EIO;
+ return_status = -ENOENT;
dev_dbg(&hdev->pdev->dev,
"remove mac addr failed for miss.\n");
} else {
@@ -3818,7 +3868,7 @@ static int hclge_get_mac_vlan_cmd_status(struct hclge_vport *vport,
if (!resp_code) {
return_status = 0;
} else if (resp_code == 1) {
- return_status = -EIO;
+ return_status = -ENOENT;
dev_dbg(&hdev->pdev->dev,
"lookup mac addr failed for miss.\n");
} else {
@@ -3827,7 +3877,7 @@ static int hclge_get_mac_vlan_cmd_status(struct hclge_vport *vport,
resp_code);
}
} else {
- return_status = -EIO;
+ return_status = -EINVAL;
dev_err(&hdev->pdev->dev,
"unknown opcode for get_mac_vlan_cmd_status,opcode=%d.\n",
op);
@@ -4118,8 +4168,9 @@ int hclge_add_uc_addr_common(struct hclge_vport *vport,
{
struct hclge_dev *hdev = vport->back;
struct hclge_mac_vlan_tbl_entry_cmd req;
- enum hclge_cmd_status status;
+ struct hclge_desc desc;
u16 egress_port = 0;
+ int ret;
/* mac addr check */
if (is_zero_ether_addr(addr) ||
@@ -4151,9 +4202,23 @@ int hclge_add_uc_addr_common(struct hclge_vport *vport,
hclge_prepare_mac_addr(&req, addr);
- status = hclge_add_mac_vlan_tbl(vport, &req, NULL);
+ /* Lookup the mac address in the mac_vlan table, and add
+ * it if the entry is inexistent. Repeated unicast entry
+ * is not allowed in the mac vlan table.
+ */
+ ret = hclge_lookup_mac_vlan_tbl(vport, &req, &desc, false);
+ if (ret == -ENOENT)
+ return hclge_add_mac_vlan_tbl(vport, &req, NULL);
+
+ /* check if we just hit the duplicate */
+ if (!ret)
+ ret = -EINVAL;
- return status;
+ dev_err(&hdev->pdev->dev,
+ "PF failed to add unicast entry(%pM) in the MAC table\n",
+ addr);
+
+ return ret;
}
static int hclge_rm_uc_addr(struct hnae3_handle *handle,
@@ -4169,7 +4234,7 @@ int hclge_rm_uc_addr_common(struct hclge_vport *vport,
{
struct hclge_dev *hdev = vport->back;
struct hclge_mac_vlan_tbl_entry_cmd req;
- enum hclge_cmd_status status;
+ int ret;
/* mac addr check */
if (is_zero_ether_addr(addr) ||
@@ -4185,9 +4250,9 @@ int hclge_rm_uc_addr_common(struct hclge_vport *vport,
hnae_set_bit(req.flags, HCLGE_MAC_VLAN_BIT0_EN_B, 1);
hnae_set_bit(req.entry_type, HCLGE_MAC_VLAN_BIT0_EN_B, 0);
hclge_prepare_mac_addr(&req, addr);
- status = hclge_remove_mac_vlan_tbl(vport, &req);
+ ret = hclge_remove_mac_vlan_tbl(vport, &req);
- return status;
+ return ret;
}
static int hclge_add_mc_addr(struct hnae3_handle *handle,
@@ -4392,7 +4457,8 @@ static void hclge_get_mac_addr(struct hnae3_handle *handle, u8 *p)
ether_addr_copy(p, hdev->hw.mac.mac_addr);
}
-static int hclge_set_mac_addr(struct hnae3_handle *handle, void *p)
+static int hclge_set_mac_addr(struct hnae3_handle *handle, void *p,
+ bool is_first)
{
const unsigned char *new_addr = (const unsigned char *)p;
struct hclge_vport *vport = hclge_get_vport(handle);
@@ -4409,11 +4475,9 @@ static int hclge_set_mac_addr(struct hnae3_handle *handle, void *p)
return -EINVAL;
}
- ret = hclge_rm_uc_addr(handle, hdev->hw.mac.mac_addr);
- if (ret)
+ if (!is_first && hclge_rm_uc_addr(handle, hdev->hw.mac.mac_addr))
dev_warn(&hdev->pdev->dev,
- "remove old uc mac address fail, ret =%d.\n",
- ret);
+ "remove old uc mac address fail.\n");
ret = hclge_add_uc_addr(handle, new_addr);
if (ret) {
@@ -4421,17 +4485,15 @@ static int hclge_set_mac_addr(struct hnae3_handle *handle, void *p)
"add uc mac address fail, ret =%d.\n",
ret);
- ret = hclge_add_uc_addr(handle, hdev->hw.mac.mac_addr);
- if (ret) {
+ if (!is_first &&
+ hclge_add_uc_addr(handle, hdev->hw.mac.mac_addr))
dev_err(&hdev->pdev->dev,
- "restore uc mac address fail, ret =%d.\n",
- ret);
- }
+ "restore uc mac address fail.\n");
return -EIO;
}
- ret = hclge_mac_pause_addr_cfg(hdev, new_addr);
+ ret = hclge_pause_addr_cfg(hdev, new_addr);
if (ret) {
dev_err(&hdev->pdev->dev,
"configure mac pause address fail, ret =%d.\n",
@@ -4771,11 +4833,9 @@ static int hclge_en_hw_strip_rxvtag(struct hnae3_handle *handle, bool enable)
return hclge_set_vlan_rx_offload_cfg(vport);
}
-static int hclge_set_mtu(struct hnae3_handle *handle, int new_mtu)
+static int hclge_set_mac_mtu(struct hclge_dev *hdev, int new_mtu)
{
- struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_config_max_frm_size_cmd *req;
- struct hclge_dev *hdev = vport->back;
struct hclge_desc desc;
int max_frm_size;
int ret;
@@ -4804,6 +4864,27 @@ static int hclge_set_mtu(struct hnae3_handle *handle, int new_mtu)
return 0;
}
+static int hclge_set_mtu(struct hnae3_handle *handle, int new_mtu)
+{
+ struct hclge_vport *vport = hclge_get_vport(handle);
+ struct hclge_dev *hdev = vport->back;
+ int ret;
+
+ ret = hclge_set_mac_mtu(hdev, new_mtu);
+ if (ret) {
+ dev_err(&hdev->pdev->dev,
+ "Change mtu fail, ret =%d\n", ret);
+ return ret;
+ }
+
+ ret = hclge_buffer_alloc(hdev);
+ if (ret)
+ dev_err(&hdev->pdev->dev,
+ "Allocate buffer fail, ret =%d\n", ret);
+
+ return ret;
+}
+
static int hclge_send_reset_tqp_cmd(struct hclge_dev *hdev, u16 queue_id,
bool enable)
{
@@ -4848,21 +4929,39 @@ static int hclge_get_reset_status(struct hclge_dev *hdev, u16 queue_id)
return hnae_get_bit(req->ready_to_reset, HCLGE_TQP_RESET_B);
}
+static u16 hclge_covert_handle_qid_global(struct hnae3_handle *handle,
+ u16 queue_id)
+{
+ struct hnae3_queue *queue;
+ struct hclge_tqp *tqp;
+
+ queue = handle->kinfo.tqp[queue_id];
+ tqp = container_of(queue, struct hclge_tqp, q);
+
+ return tqp->index;
+}
+
void hclge_reset_tqp(struct hnae3_handle *handle, u16 queue_id)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
int reset_try_times = 0;
int reset_status;
+ u16 queue_gid;
int ret;
+ if (test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state))
+ return;
+
+ queue_gid = hclge_covert_handle_qid_global(handle, queue_id);
+
ret = hclge_tqp_enable(hdev, queue_id, 0, false);
if (ret) {
dev_warn(&hdev->pdev->dev, "Disable tqp fail, ret = %d\n", ret);
return;
}
- ret = hclge_send_reset_tqp_cmd(hdev, queue_id, true);
+ ret = hclge_send_reset_tqp_cmd(hdev, queue_gid, true);
if (ret) {
dev_warn(&hdev->pdev->dev,
"Send reset tqp cmd fail, ret = %d\n", ret);
@@ -4873,7 +4972,7 @@ void hclge_reset_tqp(struct hnae3_handle *handle, u16 queue_id)
while (reset_try_times++ < HCLGE_TQP_RESET_TRY_TIMES) {
/* Wait for tqp hw reset */
msleep(20);
- reset_status = hclge_get_reset_status(hdev, queue_id);
+ reset_status = hclge_get_reset_status(hdev, queue_gid);
if (reset_status)
break;
}
@@ -4883,7 +4982,7 @@ void hclge_reset_tqp(struct hnae3_handle *handle, u16 queue_id)
return;
}
- ret = hclge_send_reset_tqp_cmd(hdev, queue_id, false);
+ ret = hclge_send_reset_tqp_cmd(hdev, queue_gid, false);
if (ret) {
dev_warn(&hdev->pdev->dev,
"Deassert the soft reset fail, ret = %d\n", ret);
@@ -4891,6 +4990,43 @@ void hclge_reset_tqp(struct hnae3_handle *handle, u16 queue_id)
}
}
+void hclge_reset_vf_queue(struct hclge_vport *vport, u16 queue_id)
+{
+ struct hclge_dev *hdev = vport->back;
+ int reset_try_times = 0;
+ int reset_status;
+ u16 queue_gid;
+ int ret;
+
+ queue_gid = hclge_covert_handle_qid_global(&vport->nic, queue_id);
+
+ ret = hclge_send_reset_tqp_cmd(hdev, queue_gid, true);
+ if (ret) {
+ dev_warn(&hdev->pdev->dev,
+ "Send reset tqp cmd fail, ret = %d\n", ret);
+ return;
+ }
+
+ reset_try_times = 0;
+ while (reset_try_times++ < HCLGE_TQP_RESET_TRY_TIMES) {
+ /* Wait for tqp hw reset */
+ msleep(20);
+ reset_status = hclge_get_reset_status(hdev, queue_gid);
+ if (reset_status)
+ break;
+ }
+
+ if (reset_try_times >= HCLGE_TQP_RESET_TRY_TIMES) {
+ dev_warn(&hdev->pdev->dev, "Reset TQP fail\n");
+ return;
+ }
+
+ ret = hclge_send_reset_tqp_cmd(hdev, queue_gid, false);
+ if (ret)
+ dev_warn(&hdev->pdev->dev,
+ "Deassert the soft reset fail, ret = %d\n", ret);
+}
+
static u32 hclge_get_fw_version(struct hnae3_handle *handle)
{
struct hclge_vport *vport = hclge_get_vport(handle);
@@ -5376,11 +5512,6 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev)
dev_err(&pdev->dev, "Mac init error, ret = %d\n", ret);
return ret;
}
- ret = hclge_buffer_alloc(hdev);
- if (ret) {
- dev_err(&pdev->dev, "Buffer allocate fail, ret =%d\n", ret);
- return ret;
- }
ret = hclge_config_tso(hdev, HCLGE_TSO_MSS_MIN, HCLGE_TSO_MSS_MAX);
if (ret) {
@@ -5400,6 +5531,7 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev)
return ret;
}
+ hclge_rss_init_cfg(hdev);
ret = hclge_rss_init_hw(hdev);
if (ret) {
dev_err(&pdev->dev, "Rss init fail, ret =%d\n", ret);
@@ -5486,12 +5618,6 @@ static int hclge_reset_ae_dev(struct hnae3_ae_dev *ae_dev)
return ret;
}
- ret = hclge_buffer_alloc(hdev);
- if (ret) {
- dev_err(&pdev->dev, "Buffer allocate fail, ret =%d\n", ret);
- return ret;
- }
-
ret = hclge_config_tso(hdev, HCLGE_TSO_MSS_MIN, HCLGE_TSO_MSS_MAX);
if (ret) {
dev_err(&pdev->dev, "Enable tso fail, ret =%d\n", ret);
@@ -5504,9 +5630,9 @@ static int hclge_reset_ae_dev(struct hnae3_ae_dev *ae_dev)
return ret;
}
- ret = hclge_tm_schd_init(hdev);
+ ret = hclge_tm_init_hw(hdev);
if (ret) {
- dev_err(&pdev->dev, "tm schd init fail, ret =%d\n", ret);
+ dev_err(&pdev->dev, "tm init hw fail, ret =%d\n", ret);
return ret;
}
@@ -5997,6 +6123,42 @@ static int hclge_update_led_status(struct hclge_dev *hdev)
HCLGE_LED_NO_CHANGE);
}
+static void hclge_get_link_mode(struct hnae3_handle *handle,
+ unsigned long *supported,
+ unsigned long *advertising)
+{
+ unsigned int size = BITS_TO_LONGS(__ETHTOOL_LINK_MODE_MASK_NBITS);
+ struct hclge_vport *vport = hclge_get_vport(handle);
+ struct hclge_dev *hdev = vport->back;
+ unsigned int idx = 0;
+
+ for (; idx < size; idx++) {
+ supported[idx] = hdev->hw.mac.supported[idx];
+ advertising[idx] = hdev->hw.mac.advertising[idx];
+ }
+}
+
+static void hclge_get_port_type(struct hnae3_handle *handle,
+ u8 *port_type)
+{
+ struct hclge_vport *vport = hclge_get_vport(handle);
+ struct hclge_dev *hdev = vport->back;
+ u8 media_type = hdev->hw.mac.media_type;
+
+ switch (media_type) {
+ case HNAE3_MEDIA_TYPE_FIBER:
+ *port_type = PORT_FIBRE;
+ break;
+ case HNAE3_MEDIA_TYPE_COPPER:
+ *port_type = PORT_TP;
+ break;
+ case HNAE3_MEDIA_TYPE_UNKNOWN:
+ default:
+ *port_type = PORT_OTHER;
+ break;
+ }
+}
+
static const struct hnae3_ae_ops hclge_ops = {
.init_ae_dev = hclge_init_ae_dev,
.uninit_ae_dev = hclge_uninit_ae_dev,
@@ -6005,6 +6167,7 @@ static const struct hnae3_ae_ops hclge_ops = {
.map_ring_to_vector = hclge_map_ring_to_vector,
.unmap_ring_from_vector = hclge_unmap_ring_frm_vector,
.get_vector = hclge_get_vector,
+ .put_vector = hclge_put_vector,
.set_promisc_mode = hclge_set_promisc_mode,
.set_loopback = hclge_set_loopback,
.start = hclge_ae_start,
@@ -6051,6 +6214,8 @@ static const struct hnae3_ae_ops hclge_ops = {
.get_regs_len = hclge_get_regs_len,
.get_regs = hclge_get_regs,
.set_led_id = hclge_set_led_id,
+ .get_link_mode = hclge_get_link_mode,
+ .get_port_type = hclge_get_port_type,
};
static struct hnae3_ae_algo ae_algo = {
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
index d99a76a9557c..0f4157e71282 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
@@ -106,6 +106,12 @@
#define HCLGE_MAC_MIN_FRAME 64
#define HCLGE_MAC_MAX_FRAME 9728
+#define HCLGE_SUPPORT_1G_BIT BIT(0)
+#define HCLGE_SUPPORT_10G_BIT BIT(1)
+#define HCLGE_SUPPORT_25G_BIT BIT(2)
+#define HCLGE_SUPPORT_50G_BIT BIT(3)
+#define HCLGE_SUPPORT_100G_BIT BIT(4)
+
enum HCLGE_DEV_STATE {
HCLGE_STATE_REINITING,
HCLGE_STATE_DOWN,
@@ -170,6 +176,8 @@ struct hclge_mac {
struct phy_device *phydev;
struct mii_bus *mdio_bus;
phy_interface_t phy_if;
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);
};
struct hclge_hw {
@@ -236,6 +244,7 @@ struct hclge_cfg {
u8 mac_addr[ETH_ALEN];
u8 default_speed;
u32 numa_node_map;
+ u8 speed_ability;
};
struct hclge_tm_info {
@@ -573,12 +582,27 @@ struct hclge_rx_vtag_cfg {
bool vlan2_vlan_prionly;/* Outer VLAN Tag up to descriptor Enable */
};
+struct hclge_rss_tuple_cfg {
+ u8 ipv4_tcp_en;
+ u8 ipv4_udp_en;
+ u8 ipv4_sctp_en;
+ u8 ipv4_fragment_en;
+ u8 ipv6_tcp_en;
+ u8 ipv6_udp_en;
+ u8 ipv6_sctp_en;
+ u8 ipv6_fragment_en;
+};
+
struct hclge_vport {
u16 alloc_tqps; /* Allocated Tx/Rx queues */
u8 rss_hash_key[HCLGE_RSS_KEY_SIZE]; /* User configured hash keys */
/* User configured lookup table entries */
u8 rss_indirection_tbl[HCLGE_RSS_IND_TBL_SIZE];
+ int rss_algo; /* User configured hash algorithm */
+ /* User configured rss tuple sets */
+ struct hclge_rss_tuple_cfg rss_tuple_sets;
+
u16 alloc_rss_size;
u16 qs_offset;
@@ -627,8 +651,11 @@ int hclge_set_vf_vlan_common(struct hclge_dev *vport, int vfid,
int hclge_buffer_alloc(struct hclge_dev *hdev);
int hclge_rss_init_hw(struct hclge_dev *hdev);
+void hclge_rss_indir_init_cfg(struct hclge_dev *hdev);
void hclge_mbx_handler(struct hclge_dev *hdev);
void hclge_reset_tqp(struct hnae3_handle *handle, u16 queue_id);
+void hclge_reset_vf_queue(struct hclge_vport *vport, u16 queue_id);
int hclge_cfg_flowctrl(struct hclge_dev *hdev);
+int hclge_func_reset_cmd(struct hclge_dev *hdev, int func_id);
#endif
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c
index f38fc5ce9f51..a6f7ffa9c259 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c
@@ -79,6 +79,18 @@ static int hclge_send_mbx_msg(struct hclge_vport *vport, u8 *msg, u16 msg_len,
return status;
}
+static int hclge_inform_reset_assert_to_vf(struct hclge_vport *vport)
+{
+ u8 msg_data[2];
+ u8 dest_vfid;
+
+ dest_vfid = (u8)vport->vport_id;
+
+ /* send this requested info to VF */
+ return hclge_send_mbx_msg(vport, msg_data, sizeof(u8),
+ HCLGE_MBX_ASSERTING_RESET, dest_vfid);
+}
+
static void hclge_free_vector_ring_chain(struct hnae3_ring_chain_node *head)
{
struct hnae3_ring_chain_node *chain_tmp, *chain;
@@ -105,14 +117,17 @@ static int hclge_get_ring_chain_from_mbx(
struct hnae3_ring_chain_node *ring_chain,
struct hclge_vport *vport)
{
-#define HCLGE_RING_NODE_VARIABLE_NUM 3
-#define HCLGE_RING_MAP_MBX_BASIC_MSG_NUM 3
struct hnae3_ring_chain_node *cur_chain, *new_chain;
int ring_num;
int i;
ring_num = req->msg[2];
+ if (ring_num > ((HCLGE_MBX_VF_MSG_DATA_NUM -
+ HCLGE_MBX_RING_MAP_BASIC_MSG_NUM) /
+ HCLGE_MBX_RING_NODE_VARIABLE_NUM))
+ return -ENOMEM;
+
hnae_set_bit(ring_chain->flag, HNAE3_RING_TYPE_B, req->msg[3]);
ring_chain->tqp_index =
hclge_get_queue_id(vport->nic.kinfo.tqp[req->msg[4]]);
@@ -128,18 +143,18 @@ static int hclge_get_ring_chain_from_mbx(
goto err;
hnae_set_bit(new_chain->flag, HNAE3_RING_TYPE_B,
- req->msg[HCLGE_RING_NODE_VARIABLE_NUM * i +
- HCLGE_RING_MAP_MBX_BASIC_MSG_NUM]);
+ req->msg[HCLGE_MBX_RING_NODE_VARIABLE_NUM * i +
+ HCLGE_MBX_RING_MAP_BASIC_MSG_NUM]);
new_chain->tqp_index =
hclge_get_queue_id(vport->nic.kinfo.tqp
- [req->msg[HCLGE_RING_NODE_VARIABLE_NUM * i +
- HCLGE_RING_MAP_MBX_BASIC_MSG_NUM + 1]]);
+ [req->msg[HCLGE_MBX_RING_NODE_VARIABLE_NUM * i +
+ HCLGE_MBX_RING_MAP_BASIC_MSG_NUM + 1]]);
hnae_set_field(new_chain->int_gl_idx, HCLGE_INT_GL_IDX_M,
HCLGE_INT_GL_IDX_S,
- req->msg[HCLGE_RING_NODE_VARIABLE_NUM * i +
- HCLGE_RING_MAP_MBX_BASIC_MSG_NUM + 2]);
+ req->msg[HCLGE_MBX_RING_NODE_VARIABLE_NUM * i +
+ HCLGE_MBX_RING_MAP_BASIC_MSG_NUM + 2]);
cur_chain->next = new_chain;
cur_chain = new_chain;
@@ -196,6 +211,8 @@ static int hclge_set_vf_uc_mac_addr(struct hclge_vport *vport,
hclge_rm_uc_addr_common(vport, old_addr);
status = hclge_add_uc_addr_common(vport, mac_addr);
+ if (status)
+ hclge_add_uc_addr_common(vport, old_addr);
} else if (mbx_req->msg[1] == HCLGE_MBX_MAC_VLAN_UC_ADD) {
status = hclge_add_uc_addr_common(vport, mac_addr);
} else if (mbx_req->msg[1] == HCLGE_MBX_MAC_VLAN_UC_REMOVE) {
@@ -291,7 +308,7 @@ static int hclge_get_vf_queue_info(struct hclge_vport *vport,
/* get the queue related info */
memcpy(&resp_data[0], &vport->alloc_tqps, sizeof(u16));
- memcpy(&resp_data[2], &hdev->rss_size_max, sizeof(u16));
+ memcpy(&resp_data[2], &vport->nic.kinfo.rss_size, sizeof(u16));
memcpy(&resp_data[4], &hdev->num_desc, sizeof(u16));
memcpy(&resp_data[6], &hdev->rx_buf_len, sizeof(u16));
@@ -304,27 +321,61 @@ static int hclge_get_link_info(struct hclge_vport *vport,
{
struct hclge_dev *hdev = vport->back;
u16 link_status;
- u8 msg_data[2];
+ u8 msg_data[8];
u8 dest_vfid;
+ u16 duplex;
/* mac.link can only be 0 or 1 */
link_status = (u16)hdev->hw.mac.link;
+ duplex = hdev->hw.mac.duplex;
memcpy(&msg_data[0], &link_status, sizeof(u16));
+ memcpy(&msg_data[2], &hdev->hw.mac.speed, sizeof(u32));
+ memcpy(&msg_data[6], &duplex, sizeof(u16));
dest_vfid = mbx_req->mbx_src_vfid;
/* send this requested info to VF */
- return hclge_send_mbx_msg(vport, msg_data, sizeof(u8),
+ return hclge_send_mbx_msg(vport, msg_data, sizeof(msg_data),
HCLGE_MBX_LINK_STAT_CHANGE, dest_vfid);
}
-static void hclge_reset_vf_queue(struct hclge_vport *vport,
- struct hclge_mbx_vf_to_pf_cmd *mbx_req)
+static void hclge_mbx_reset_vf_queue(struct hclge_vport *vport,
+ struct hclge_mbx_vf_to_pf_cmd *mbx_req)
{
u16 queue_id;
memcpy(&queue_id, &mbx_req->msg[2], sizeof(queue_id));
- hclge_reset_tqp(&vport->nic, queue_id);
+ hclge_reset_vf_queue(vport, queue_id);
+
+ /* send response msg to VF after queue reset complete*/
+ hclge_gen_resp_to_vf(vport, mbx_req, 0, NULL, 0);
+}
+
+static void hclge_reset_vf(struct hclge_vport *vport,
+ struct hclge_mbx_vf_to_pf_cmd *mbx_req)
+{
+ struct hclge_dev *hdev = vport->back;
+ int ret;
+
+ dev_warn(&hdev->pdev->dev, "PF received VF reset request from VF %d!",
+ mbx_req->mbx_src_vfid);
+
+ /* Acknowledge VF that PF is now about to assert the reset for the VF.
+ * On receiving this message VF will get into pending state and will
+ * start polling for the hardware reset completion status.
+ */
+ ret = hclge_inform_reset_assert_to_vf(vport);
+ if (ret) {
+ dev_err(&hdev->pdev->dev,
+ "PF fail(%d) to inform VF(%d)of reset, reset failed!\n",
+ ret, vport->vport_id);
+ return;
+ }
+
+ dev_warn(&hdev->pdev->dev, "PF is now resetting VF %d.\n",
+ mbx_req->mbx_src_vfid);
+ /* reset this virtual function */
+ hclge_func_reset_cmd(hdev, mbx_req->mbx_src_vfid);
}
void hclge_mbx_handler(struct hclge_dev *hdev)
@@ -333,11 +384,11 @@ void hclge_mbx_handler(struct hclge_dev *hdev)
struct hclge_mbx_vf_to_pf_cmd *req;
struct hclge_vport *vport;
struct hclge_desc *desc;
- int ret;
+ int ret, flag;
+ flag = le16_to_cpu(crq->desc[crq->next_to_use].flag);
/* handle all the mailbox requests in the queue */
- while (hnae_get_bit(crq->desc[crq->next_to_use].flag,
- HCLGE_CMDQ_RX_OUTVLD_B)) {
+ while (hnae_get_bit(flag, HCLGE_CMDQ_RX_OUTVLD_B)) {
desc = &crq->desc[crq->next_to_use];
req = (struct hclge_mbx_vf_to_pf_cmd *)desc->data;
@@ -360,7 +411,7 @@ void hclge_mbx_handler(struct hclge_dev *hdev)
ret);
break;
case HCLGE_MBX_SET_UNICAST:
- ret = hclge_set_vf_uc_mac_addr(vport, req, false);
+ ret = hclge_set_vf_uc_mac_addr(vport, req, true);
if (ret)
dev_err(&hdev->pdev->dev,
"PF fail(%d) to set VF UC MAC Addr\n",
@@ -402,7 +453,10 @@ void hclge_mbx_handler(struct hclge_dev *hdev)
ret);
break;
case HCLGE_MBX_QUEUE_RESET:
- hclge_reset_vf_queue(vport, req);
+ hclge_mbx_reset_vf_queue(vport, req);
+ break;
+ case HCLGE_MBX_RESET:
+ hclge_reset_vf(vport, req);
break;
default:
dev_err(&hdev->pdev->dev,
@@ -410,7 +464,9 @@ void hclge_mbx_handler(struct hclge_dev *hdev)
req->msg[0]);
break;
}
+ crq->desc[crq->next_to_use].flag = 0;
hclge_mbx_ring_ptr_move_crq(crq);
+ flag = le16_to_cpu(crq->desc[crq->next_to_use].flag);
}
/* Write back CMDQ_RQ header pointer, M7 need this pointer */
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c
index c1dea3a47bdd..682c2d6618e7 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c
@@ -60,6 +60,9 @@ static int hclge_mdio_write(struct mii_bus *bus, int phyid, int regnum,
struct hclge_desc desc;
int ret;
+ if (test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state))
+ return 0;
+
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_MDIO_CONFIG, false);
mdio_cmd = (struct hclge_mdio_cfg_cmd *)desc.data;
@@ -95,6 +98,9 @@ static int hclge_mdio_read(struct mii_bus *bus, int phyid, int regnum)
struct hclge_desc desc;
int ret;
+ if (test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state))
+ return 0;
+
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_MDIO_CONFIG, true);
mdio_cmd = (struct hclge_mdio_cfg_cmd *)desc.data;
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c
index 36bd79a77940..885f25cd7be4 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c
@@ -23,6 +23,9 @@ enum hclge_shaper_level {
HCLGE_SHAPER_LVL_PF = 1,
};
+#define HCLGE_TM_PFC_PKT_GET_CMD_NUM 3
+#define HCLGE_TM_PFC_NUM_GET_PER_CMD 3
+
#define HCLGE_SHAPER_BS_U_DEF 5
#define HCLGE_SHAPER_BS_S_DEF 20
@@ -112,6 +115,56 @@ static int hclge_shaper_para_calc(u32 ir, u8 shaper_level,
return 0;
}
+static int hclge_pfc_stats_get(struct hclge_dev *hdev,
+ enum hclge_opcode_type opcode, u64 *stats)
+{
+ struct hclge_desc desc[HCLGE_TM_PFC_PKT_GET_CMD_NUM];
+ int ret, i, j;
+
+ if (!(opcode == HCLGE_OPC_QUERY_PFC_RX_PKT_CNT ||
+ opcode == HCLGE_OPC_QUERY_PFC_TX_PKT_CNT))
+ return -EINVAL;
+
+ for (i = 0; i < HCLGE_TM_PFC_PKT_GET_CMD_NUM; i++) {
+ hclge_cmd_setup_basic_desc(&desc[i], opcode, true);
+ if (i != (HCLGE_TM_PFC_PKT_GET_CMD_NUM - 1))
+ desc[i].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+ else
+ desc[i].flag &= ~cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+ }
+
+ ret = hclge_cmd_send(&hdev->hw, desc, HCLGE_TM_PFC_PKT_GET_CMD_NUM);
+ if (ret) {
+ dev_err(&hdev->pdev->dev,
+ "Get pfc pause stats fail, ret = %d.\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < HCLGE_TM_PFC_PKT_GET_CMD_NUM; i++) {
+ struct hclge_pfc_stats_cmd *pfc_stats =
+ (struct hclge_pfc_stats_cmd *)desc[i].data;
+
+ for (j = 0; j < HCLGE_TM_PFC_NUM_GET_PER_CMD; j++) {
+ u32 index = i * HCLGE_TM_PFC_PKT_GET_CMD_NUM + j;
+
+ if (index < HCLGE_MAX_TC_NUM)
+ stats[index] =
+ le64_to_cpu(pfc_stats->pkt_num[j]);
+ }
+ }
+ return 0;
+}
+
+int hclge_pfc_rx_stats_get(struct hclge_dev *hdev, u64 *stats)
+{
+ return hclge_pfc_stats_get(hdev, HCLGE_OPC_QUERY_PFC_RX_PKT_CNT, stats);
+}
+
+int hclge_pfc_tx_stats_get(struct hclge_dev *hdev, u64 *stats)
+{
+ return hclge_pfc_stats_get(hdev, HCLGE_OPC_QUERY_PFC_TX_PKT_CNT, stats);
+}
+
int hclge_mac_pause_en_cfg(struct hclge_dev *hdev, bool tx, bool rx)
{
struct hclge_desc desc;
@@ -138,8 +191,8 @@ static int hclge_pfc_pause_en_cfg(struct hclge_dev *hdev, u8 tx_rx_bitmap,
return hclge_cmd_send(&hdev->hw, &desc, 1);
}
-static int hclge_mac_pause_param_cfg(struct hclge_dev *hdev, const u8 *addr,
- u8 pause_trans_gap, u16 pause_trans_time)
+static int hclge_pause_param_cfg(struct hclge_dev *hdev, const u8 *addr,
+ u8 pause_trans_gap, u16 pause_trans_time)
{
struct hclge_cfg_pause_param_cmd *pause_param;
struct hclge_desc desc;
@@ -155,7 +208,7 @@ static int hclge_mac_pause_param_cfg(struct hclge_dev *hdev, const u8 *addr,
return hclge_cmd_send(&hdev->hw, &desc, 1);
}
-int hclge_mac_pause_addr_cfg(struct hclge_dev *hdev, const u8 *mac_addr)
+int hclge_pause_addr_cfg(struct hclge_dev *hdev, const u8 *mac_addr)
{
struct hclge_cfg_pause_param_cmd *pause_param;
struct hclge_desc desc;
@@ -174,7 +227,7 @@ int hclge_mac_pause_addr_cfg(struct hclge_dev *hdev, const u8 *mac_addr)
trans_gap = pause_param->pause_trans_gap;
trans_time = le16_to_cpu(pause_param->pause_trans_time);
- return hclge_mac_pause_param_cfg(hdev, mac_addr, trans_gap,
+ return hclge_pause_param_cfg(hdev, mac_addr, trans_gap,
trans_time);
}
@@ -1096,11 +1149,11 @@ static int hclge_tm_schd_setup_hw(struct hclge_dev *hdev)
return hclge_tm_schd_mode_hw(hdev);
}
-static int hclge_mac_pause_param_setup_hw(struct hclge_dev *hdev)
+static int hclge_pause_param_setup_hw(struct hclge_dev *hdev)
{
struct hclge_mac *mac = &hdev->hw.mac;
- return hclge_mac_pause_param_cfg(hdev, mac->mac_addr,
+ return hclge_pause_param_cfg(hdev, mac->mac_addr,
HCLGE_DEFAULT_PAUSE_TRANS_GAP,
HCLGE_DEFAULT_PAUSE_TRANS_TIME);
}
@@ -1151,13 +1204,12 @@ int hclge_pause_setup_hw(struct hclge_dev *hdev)
int ret;
u8 i;
- if (hdev->tm_info.fc_mode != HCLGE_FC_PFC) {
- ret = hclge_mac_pause_setup_hw(hdev);
- if (ret)
- return ret;
+ ret = hclge_pause_param_setup_hw(hdev);
+ if (ret)
+ return ret;
- return hclge_mac_pause_param_setup_hw(hdev);
- }
+ if (hdev->tm_info.fc_mode != HCLGE_FC_PFC)
+ return hclge_mac_pause_setup_hw(hdev);
/* Only DCB-supported dev supports qset back pressure and pfc cmd */
if (!hnae3_dev_dcb_supported(hdev))
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h
index 5401e7559437..2dbe177581e9 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h
@@ -109,6 +109,10 @@ struct hclge_cfg_pause_param_cmd {
__le16 pause_trans_time;
};
+struct hclge_pfc_stats_cmd {
+ __le64 pkt_num[3];
+};
+
struct hclge_port_shapping_cmd {
__le32 port_shapping_para;
};
@@ -129,5 +133,7 @@ int hclge_tm_dwrr_cfg(struct hclge_dev *hdev);
int hclge_tm_map_cfg(struct hclge_dev *hdev);
int hclge_tm_init_hw(struct hclge_dev *hdev);
int hclge_mac_pause_en_cfg(struct hclge_dev *hdev, bool tx, bool rx);
-int hclge_mac_pause_addr_cfg(struct hclge_dev *hdev, const u8 *mac_addr);
+int hclge_pause_addr_cfg(struct hclge_dev *hdev, const u8 *mac_addr);
+int hclge_pfc_rx_stats_get(struct hclge_dev *hdev, u64 *stats);
+int hclge_pfc_tx_stats_get(struct hclge_dev *hdev, u64 *stats);
#endif
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.c
index 85985e731311..1bbfe131b596 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.c
@@ -315,6 +315,12 @@ int hclgevf_cmd_init(struct hclgevf_dev *hdev)
goto err_csq;
}
+ /* initialize the pointers of async rx queue of mailbox */
+ hdev->arq.hdev = hdev;
+ hdev->arq.head = 0;
+ hdev->arq.tail = 0;
+ hdev->arq.count = 0;
+
/* get firmware version */
ret = hclgevf_cmd_query_firmware_version(&hdev->hw, &version);
if (ret) {
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.h b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.h
index 2caca9317f8c..621c6cbacf76 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.h
@@ -7,7 +7,7 @@
#include <linux/types.h>
#include "hnae3.h"
-#define HCLGEVF_CMDQ_TX_TIMEOUT 200
+#define HCLGEVF_CMDQ_TX_TIMEOUT 30000
#define HCLGEVF_CMDQ_RX_INVLD_B 0
#define HCLGEVF_CMDQ_RX_OUTVLD_B 1
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c
index 0d89965f7928..2b8426412cc9 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c
@@ -2,6 +2,7 @@
// Copyright (c) 2016-2017 Hisilicon Limited.
#include <linux/etherdevice.h>
+#include <net/rtnetlink.h>
#include "hclgevf_cmd.h"
#include "hclgevf_main.h"
#include "hclge_mbx.h"
@@ -9,6 +10,8 @@
#define HCLGEVF_NAME "hclgevf"
+static int hclgevf_init_hdev(struct hclgevf_dev *hdev);
+static void hclgevf_uninit_hdev(struct hclgevf_dev *hdev);
static struct hnae3_ae_algo ae_algovf;
static const struct pci_device_id ae_algovf_pci_tbl[] = {
@@ -18,6 +21,8 @@ static const struct pci_device_id ae_algovf_pci_tbl[] = {
{0, }
};
+MODULE_DEVICE_TABLE(pci, ae_algovf_pci_tbl);
+
static inline struct hclgevf_dev *hclgevf_ae_get_hdev(
struct hnae3_handle *handle)
{
@@ -206,6 +211,12 @@ static int hclgevf_alloc_tqps(struct hclgevf_dev *hdev)
struct hclgevf_tqp *tqp;
int i;
+ /* if this is on going reset then we need to re-allocate the TPQs
+ * since we cannot assume we would get same number of TPQs back from PF
+ */
+ if (hclgevf_dev_ongoing_reset(hdev))
+ devm_kfree(&hdev->pdev->dev, hdev->htqp);
+
hdev->htqp = devm_kcalloc(&hdev->pdev->dev, hdev->num_tqps,
sizeof(struct hclgevf_tqp), GFP_KERNEL);
if (!hdev->htqp)
@@ -249,6 +260,12 @@ static int hclgevf_knic_setup(struct hclgevf_dev *hdev)
new_tqps = kinfo->rss_size * kinfo->num_tc;
kinfo->num_tqps = min(new_tqps, hdev->num_tqps);
+ /* if this is on going reset then we need to re-allocate the hnae queues
+ * as well since number of TPQs from PF might have changed.
+ */
+ if (hclgevf_dev_ongoing_reset(hdev))
+ devm_kfree(&hdev->pdev->dev, kinfo->tqp);
+
kinfo->tqp = devm_kcalloc(&hdev->pdev->dev, kinfo->num_tqps,
sizeof(struct hnae3_queue *), GFP_KERNEL);
if (!kinfo->tqp)
@@ -533,13 +550,11 @@ static int hclgevf_bind_ring_to_vector(struct hnae3_handle *handle, bool en,
int vector,
struct hnae3_ring_chain_node *ring_chain)
{
-#define HCLGEVF_RING_NODE_VARIABLE_NUM 3
-#define HCLGEVF_RING_MAP_MBX_BASIC_MSG_NUM 3
struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
struct hnae3_ring_chain_node *node;
struct hclge_mbx_vf_to_pf_cmd *req;
struct hclgevf_desc desc;
- int i, vector_id;
+ int i = 0, vector_id;
int status;
u8 type;
@@ -551,28 +566,33 @@ static int hclgevf_bind_ring_to_vector(struct hnae3_handle *handle, bool en,
return vector_id;
}
- hclgevf_cmd_setup_basic_desc(&desc, HCLGEVF_OPC_MBX_VF_TO_PF, false);
- type = en ?
- HCLGE_MBX_MAP_RING_TO_VECTOR : HCLGE_MBX_UNMAP_RING_TO_VECTOR;
- req->msg[0] = type;
- req->msg[1] = vector_id; /* vector_id should be id in VF */
-
- i = 0;
for (node = ring_chain; node; node = node->next) {
- i++;
- /* msg[2] is cause num */
- req->msg[HCLGEVF_RING_NODE_VARIABLE_NUM * i] =
+ int idx_offset = HCLGE_MBX_RING_MAP_BASIC_MSG_NUM +
+ HCLGE_MBX_RING_NODE_VARIABLE_NUM * i;
+
+ if (i == 0) {
+ hclgevf_cmd_setup_basic_desc(&desc,
+ HCLGEVF_OPC_MBX_VF_TO_PF,
+ false);
+ type = en ?
+ HCLGE_MBX_MAP_RING_TO_VECTOR :
+ HCLGE_MBX_UNMAP_RING_TO_VECTOR;
+ req->msg[0] = type;
+ req->msg[1] = vector_id;
+ }
+
+ req->msg[idx_offset] =
hnae_get_bit(node->flag, HNAE3_RING_TYPE_B);
- req->msg[HCLGEVF_RING_NODE_VARIABLE_NUM * i + 1] =
- node->tqp_index;
- req->msg[HCLGEVF_RING_NODE_VARIABLE_NUM * i + 2] =
- hnae_get_field(node->int_gl_idx,
- HNAE3_RING_GL_IDX_M,
- HNAE3_RING_GL_IDX_S);
-
- if (i == (HCLGE_MBX_VF_MSG_DATA_NUM -
- HCLGEVF_RING_MAP_MBX_BASIC_MSG_NUM) /
- HCLGEVF_RING_NODE_VARIABLE_NUM) {
+ req->msg[idx_offset + 1] = node->tqp_index;
+ req->msg[idx_offset + 2] = hnae_get_field(node->int_gl_idx,
+ HNAE3_RING_GL_IDX_M,
+ HNAE3_RING_GL_IDX_S);
+
+ i++;
+ if ((i == (HCLGE_MBX_VF_MSG_DATA_NUM -
+ HCLGE_MBX_RING_MAP_BASIC_MSG_NUM) /
+ HCLGE_MBX_RING_NODE_VARIABLE_NUM) ||
+ !node->next) {
req->msg[2] = i;
status = hclgevf_cmd_send(&hdev->hw, &desc, 1);
@@ -591,17 +611,6 @@ static int hclgevf_bind_ring_to_vector(struct hnae3_handle *handle, bool en,
}
}
- if (i > 0) {
- req->msg[2] = i;
-
- status = hclgevf_cmd_send(&hdev->hw, &desc, 1);
- if (status) {
- dev_err(&hdev->pdev->dev,
- "Map TQP fail, status is %d.\n", status);
- return status;
- }
- }
-
return 0;
}
@@ -627,13 +636,18 @@ static int hclgevf_unmap_ring_from_vector(
}
ret = hclgevf_bind_ring_to_vector(handle, false, vector, ring_chain);
- if (ret) {
+ if (ret)
dev_err(&handle->pdev->dev,
"Unmap ring from vector fail. vector=%d, ret =%d\n",
vector_id,
ret);
- return ret;
- }
+
+ return ret;
+}
+
+static int hclgevf_put_vector(struct hnae3_handle *handle, int vector)
+{
+ struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
hclgevf_free_vector(hdev, vector);
@@ -729,21 +743,25 @@ static void hclgevf_get_mac_addr(struct hnae3_handle *handle, u8 *p)
ether_addr_copy(p, hdev->hw.mac.mac_addr);
}
-static int hclgevf_set_mac_addr(struct hnae3_handle *handle, void *p)
+static int hclgevf_set_mac_addr(struct hnae3_handle *handle, void *p,
+ bool is_first)
{
struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
u8 *old_mac_addr = (u8 *)hdev->hw.mac.mac_addr;
u8 *new_mac_addr = (u8 *)p;
u8 msg_data[ETH_ALEN * 2];
+ u16 subcode;
int status;
ether_addr_copy(msg_data, new_mac_addr);
ether_addr_copy(&msg_data[ETH_ALEN], old_mac_addr);
+ subcode = is_first ? HCLGE_MBX_MAC_VLAN_UC_ADD :
+ HCLGE_MBX_MAC_VLAN_UC_MODIFY;
+
status = hclgevf_send_mbx_msg(hdev, HCLGE_MBX_SET_UNICAST,
- HCLGE_MBX_MAC_VLAN_UC_MODIFY,
- msg_data, ETH_ALEN * 2,
- false, NULL, 0);
+ subcode, msg_data, ETH_ALEN * 2,
+ true, NULL, 0);
if (!status)
ether_addr_copy(hdev->hw.mac.mac_addr, new_mac_addr);
@@ -816,11 +834,149 @@ static void hclgevf_reset_tqp(struct hnae3_handle *handle, u16 queue_id)
{
struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
u8 msg_data[2];
+ int ret;
memcpy(&msg_data[0], &queue_id, sizeof(queue_id));
- hclgevf_send_mbx_msg(hdev, HCLGE_MBX_QUEUE_RESET, 0, msg_data, 2, false,
- NULL, 0);
+ /* disable vf queue before send queue reset msg to PF */
+ ret = hclgevf_tqp_enable(hdev, queue_id, 0, false);
+ if (ret)
+ return;
+
+ hclgevf_send_mbx_msg(hdev, HCLGE_MBX_QUEUE_RESET, 0, msg_data,
+ 2, true, NULL, 0);
+}
+
+static int hclgevf_notify_client(struct hclgevf_dev *hdev,
+ enum hnae3_reset_notify_type type)
+{
+ struct hnae3_client *client = hdev->nic_client;
+ struct hnae3_handle *handle = &hdev->nic;
+
+ if (!client->ops->reset_notify)
+ return -EOPNOTSUPP;
+
+ return client->ops->reset_notify(handle, type);
+}
+
+static int hclgevf_reset_wait(struct hclgevf_dev *hdev)
+{
+#define HCLGEVF_RESET_WAIT_MS 500
+#define HCLGEVF_RESET_WAIT_CNT 20
+ u32 val, cnt = 0;
+
+ /* wait to check the hardware reset completion status */
+ val = hclgevf_read_dev(&hdev->hw, HCLGEVF_FUN_RST_ING);
+ while (hnae_get_bit(val, HCLGEVF_FUN_RST_ING_B) &&
+ (cnt < HCLGEVF_RESET_WAIT_CNT)) {
+ msleep(HCLGEVF_RESET_WAIT_MS);
+ val = hclgevf_read_dev(&hdev->hw, HCLGEVF_FUN_RST_ING);
+ cnt++;
+ }
+
+ /* hardware completion status should be available by this time */
+ if (cnt >= HCLGEVF_RESET_WAIT_CNT) {
+ dev_warn(&hdev->pdev->dev,
+ "could'nt get reset done status from h/w, timeout!\n");
+ return -EBUSY;
+ }
+
+ /* we will wait a bit more to let reset of the stack to complete. This
+ * might happen in case reset assertion was made by PF. Yes, this also
+ * means we might end up waiting bit more even for VF reset.
+ */
+ msleep(5000);
+
+ return 0;
+}
+
+static int hclgevf_reset_stack(struct hclgevf_dev *hdev)
+{
+ int ret;
+
+ /* uninitialize the nic client */
+ hclgevf_notify_client(hdev, HNAE3_UNINIT_CLIENT);
+
+ /* re-initialize the hclge device */
+ ret = hclgevf_init_hdev(hdev);
+ if (ret) {
+ dev_err(&hdev->pdev->dev,
+ "hclge device re-init failed, VF is disabled!\n");
+ return ret;
+ }
+
+ /* bring up the nic client again */
+ hclgevf_notify_client(hdev, HNAE3_INIT_CLIENT);
+
+ return 0;
+}
+
+static int hclgevf_reset(struct hclgevf_dev *hdev)
+{
+ int ret;
+
+ rtnl_lock();
+
+ /* bring down the nic to stop any ongoing TX/RX */
+ hclgevf_notify_client(hdev, HNAE3_DOWN_CLIENT);
+
+ /* check if VF could successfully fetch the hardware reset completion
+ * status from the hardware
+ */
+ ret = hclgevf_reset_wait(hdev);
+ if (ret) {
+ /* can't do much in this situation, will disable VF */
+ dev_err(&hdev->pdev->dev,
+ "VF failed(=%d) to fetch H/W reset completion status\n",
+ ret);
+
+ dev_warn(&hdev->pdev->dev, "VF reset failed, disabling VF!\n");
+ hclgevf_notify_client(hdev, HNAE3_UNINIT_CLIENT);
+
+ rtnl_unlock();
+ return ret;
+ }
+
+ /* now, re-initialize the nic client and ae device*/
+ ret = hclgevf_reset_stack(hdev);
+ if (ret)
+ dev_err(&hdev->pdev->dev, "failed to reset VF stack\n");
+
+ /* bring up the nic to enable TX/RX again */
+ hclgevf_notify_client(hdev, HNAE3_UP_CLIENT);
+
+ rtnl_unlock();
+
+ return ret;
+}
+
+static int hclgevf_do_reset(struct hclgevf_dev *hdev)
+{
+ int status;
+ u8 respmsg;
+
+ status = hclgevf_send_mbx_msg(hdev, HCLGE_MBX_RESET, 0, NULL,
+ 0, false, &respmsg, sizeof(u8));
+ if (status)
+ dev_err(&hdev->pdev->dev,
+ "VF reset request to PF failed(=%d)\n", status);
+
+ return status;
+}
+
+static void hclgevf_reset_event(struct hnae3_handle *handle)
+{
+ struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
+
+ dev_info(&hdev->pdev->dev, "received reset request from VF enet\n");
+
+ handle->reset_level = HNAE3_VF_RESET;
+
+ /* reset of this VF requested */
+ set_bit(HCLGEVF_RESET_REQUESTED, &hdev->reset_state);
+ hclgevf_reset_task_schedule(hdev);
+
+ handle->last_reset_time = jiffies;
}
static u32 hclgevf_get_fw_version(struct hnae3_handle *handle)
@@ -845,10 +1001,22 @@ static void hclgevf_get_misc_vector(struct hclgevf_dev *hdev)
hdev->num_msi_used += 1;
}
-static void hclgevf_mbx_task_schedule(struct hclgevf_dev *hdev)
+void hclgevf_reset_task_schedule(struct hclgevf_dev *hdev)
+{
+ if (!test_bit(HCLGEVF_STATE_RST_SERVICE_SCHED, &hdev->state) &&
+ !test_bit(HCLGEVF_STATE_RST_HANDLING, &hdev->state)) {
+ set_bit(HCLGEVF_STATE_RST_SERVICE_SCHED, &hdev->state);
+ schedule_work(&hdev->rst_service_task);
+ }
+}
+
+void hclgevf_mbx_task_schedule(struct hclgevf_dev *hdev)
{
- if (!test_and_set_bit(HCLGEVF_STATE_MBX_SERVICE_SCHED, &hdev->state))
+ if (!test_bit(HCLGEVF_STATE_MBX_SERVICE_SCHED, &hdev->state) &&
+ !test_bit(HCLGEVF_STATE_MBX_HANDLING, &hdev->state)) {
+ set_bit(HCLGEVF_STATE_MBX_SERVICE_SCHED, &hdev->state);
schedule_work(&hdev->mbx_service_task);
+ }
}
static void hclgevf_task_schedule(struct hclgevf_dev *hdev)
@@ -858,6 +1026,16 @@ static void hclgevf_task_schedule(struct hclgevf_dev *hdev)
schedule_work(&hdev->service_task);
}
+static void hclgevf_deferred_task_schedule(struct hclgevf_dev *hdev)
+{
+ /* if we have any pending mailbox event then schedule the mbx task */
+ if (hdev->mbx_event_pending)
+ hclgevf_mbx_task_schedule(hdev);
+
+ if (test_bit(HCLGEVF_RESET_PENDING, &hdev->reset_state))
+ hclgevf_reset_task_schedule(hdev);
+}
+
static void hclgevf_service_timer(struct timer_list *t)
{
struct hclgevf_dev *hdev = from_timer(hdev, t, service_timer);
@@ -867,6 +1045,75 @@ static void hclgevf_service_timer(struct timer_list *t)
hclgevf_task_schedule(hdev);
}
+static void hclgevf_reset_service_task(struct work_struct *work)
+{
+ struct hclgevf_dev *hdev =
+ container_of(work, struct hclgevf_dev, rst_service_task);
+ int ret;
+
+ if (test_and_set_bit(HCLGEVF_STATE_RST_HANDLING, &hdev->state))
+ return;
+
+ clear_bit(HCLGEVF_STATE_RST_SERVICE_SCHED, &hdev->state);
+
+ if (test_and_clear_bit(HCLGEVF_RESET_PENDING,
+ &hdev->reset_state)) {
+ /* PF has initmated that it is about to reset the hardware.
+ * We now have to poll & check if harware has actually completed
+ * the reset sequence. On hardware reset completion, VF needs to
+ * reset the client and ae device.
+ */
+ hdev->reset_attempts = 0;
+
+ ret = hclgevf_reset(hdev);
+ if (ret)
+ dev_err(&hdev->pdev->dev, "VF stack reset failed.\n");
+ } else if (test_and_clear_bit(HCLGEVF_RESET_REQUESTED,
+ &hdev->reset_state)) {
+ /* we could be here when either of below happens:
+ * 1. reset was initiated due to watchdog timeout due to
+ * a. IMP was earlier reset and our TX got choked down and
+ * which resulted in watchdog reacting and inducing VF
+ * reset. This also means our cmdq would be unreliable.
+ * b. problem in TX due to other lower layer(example link
+ * layer not functioning properly etc.)
+ * 2. VF reset might have been initiated due to some config
+ * change.
+ *
+ * NOTE: Theres no clear way to detect above cases than to react
+ * to the response of PF for this reset request. PF will ack the
+ * 1b and 2. cases but we will not get any intimation about 1a
+ * from PF as cmdq would be in unreliable state i.e. mailbox
+ * communication between PF and VF would be broken.
+ */
+
+ /* if we are never geting into pending state it means either:
+ * 1. PF is not receiving our request which could be due to IMP
+ * reset
+ * 2. PF is screwed
+ * We cannot do much for 2. but to check first we can try reset
+ * our PCIe + stack and see if it alleviates the problem.
+ */
+ if (hdev->reset_attempts > 3) {
+ /* prepare for full reset of stack + pcie interface */
+ hdev->nic.reset_level = HNAE3_VF_FULL_RESET;
+
+ /* "defer" schedule the reset task again */
+ set_bit(HCLGEVF_RESET_PENDING, &hdev->reset_state);
+ } else {
+ hdev->reset_attempts++;
+
+ /* request PF for resetting this VF via mailbox */
+ ret = hclgevf_do_reset(hdev);
+ if (ret)
+ dev_warn(&hdev->pdev->dev,
+ "VF rst fail, stack will call\n");
+ }
+ }
+
+ clear_bit(HCLGEVF_STATE_RST_HANDLING, &hdev->state);
+}
+
static void hclgevf_mailbox_service_task(struct work_struct *work)
{
struct hclgevf_dev *hdev;
@@ -878,7 +1125,7 @@ static void hclgevf_mailbox_service_task(struct work_struct *work)
clear_bit(HCLGEVF_STATE_MBX_SERVICE_SCHED, &hdev->state);
- hclgevf_mbx_handler(hdev);
+ hclgevf_mbx_async_handler(hdev);
clear_bit(HCLGEVF_STATE_MBX_HANDLING, &hdev->state);
}
@@ -894,6 +1141,8 @@ static void hclgevf_service_task(struct work_struct *work)
*/
hclgevf_request_link_info(hdev);
+ hclgevf_deferred_task_schedule(hdev);
+
clear_bit(HCLGEVF_STATE_SERVICE_SCHED, &hdev->state);
}
@@ -936,8 +1185,7 @@ static irqreturn_t hclgevf_misc_irq_handle(int irq, void *data)
if (!hclgevf_check_event_cause(hdev, &clearval))
goto skip_sched;
- /* schedule the VF mailbox service task, if not already scheduled */
- hclgevf_mbx_task_schedule(hdev);
+ hclgevf_mbx_handler(hdev);
hclgevf_clear_event_cause(hdev, clearval);
@@ -959,6 +1207,22 @@ static int hclgevf_configure(struct hclgevf_dev *hdev)
return hclgevf_get_tc_info(hdev);
}
+static int hclgevf_alloc_hdev(struct hnae3_ae_dev *ae_dev)
+{
+ struct pci_dev *pdev = ae_dev->pdev;
+ struct hclgevf_dev *hdev = ae_dev->priv;
+
+ hdev = devm_kzalloc(&pdev->dev, sizeof(*hdev), GFP_KERNEL);
+ if (!hdev)
+ return -ENOMEM;
+
+ hdev->pdev = pdev;
+ hdev->ae_dev = ae_dev;
+ ae_dev->priv = hdev;
+
+ return 0;
+}
+
static int hclgevf_init_roce_base_info(struct hclgevf_dev *hdev)
{
struct hnae3_handle *roce = &hdev->roce;
@@ -1057,10 +1321,17 @@ static void hclgevf_ae_stop(struct hnae3_handle *handle)
/* reset tqp stats */
hclgevf_reset_tqp_stats(handle);
+ del_timer_sync(&hdev->service_timer);
+ cancel_work_sync(&hdev->service_task);
+ hclgevf_update_link_status(hdev, 0);
}
static void hclgevf_state_init(struct hclgevf_dev *hdev)
{
+ /* if this is on going reset then skip this initialization */
+ if (hclgevf_dev_ongoing_reset(hdev))
+ return;
+
/* setup tasks for the MBX */
INIT_WORK(&hdev->mbx_service_task, hclgevf_mailbox_service_task);
clear_bit(HCLGEVF_STATE_MBX_SERVICE_SCHED, &hdev->state);
@@ -1072,6 +1343,8 @@ static void hclgevf_state_init(struct hclgevf_dev *hdev)
INIT_WORK(&hdev->service_task, hclgevf_service_task);
clear_bit(HCLGEVF_STATE_SERVICE_SCHED, &hdev->state);
+ INIT_WORK(&hdev->rst_service_task, hclgevf_reset_service_task);
+
mutex_init(&hdev->mbx_resp.mbx_mutex);
/* bring the device down */
@@ -1088,6 +1361,8 @@ static void hclgevf_state_uninit(struct hclgevf_dev *hdev)
cancel_work_sync(&hdev->service_task);
if (hdev->mbx_service_task.func)
cancel_work_sync(&hdev->mbx_service_task);
+ if (hdev->rst_service_task.func)
+ cancel_work_sync(&hdev->rst_service_task);
mutex_destroy(&hdev->mbx_resp.mbx_mutex);
}
@@ -1098,6 +1373,10 @@ static int hclgevf_init_msi(struct hclgevf_dev *hdev)
int vectors;
int i;
+ /* if this is on going reset then skip this initialization */
+ if (hclgevf_dev_ongoing_reset(hdev))
+ return 0;
+
hdev->num_msi = HCLGEVF_MAX_VF_VECTOR_NUM;
vectors = pci_alloc_irq_vectors(pdev, 1, hdev->num_msi,
@@ -1148,6 +1427,10 @@ static int hclgevf_misc_irq_init(struct hclgevf_dev *hdev)
{
int ret = 0;
+ /* if this is on going reset then skip this initialization */
+ if (hclgevf_dev_ongoing_reset(hdev))
+ return 0;
+
hclgevf_get_misc_vector(hdev);
ret = request_irq(hdev->misc_vector.vector_irq, hclgevf_misc_irq_handle,
@@ -1258,6 +1541,14 @@ static int hclgevf_pci_init(struct hclgevf_dev *hdev)
struct hclgevf_hw *hw;
int ret;
+ /* check if we need to skip initialization of pci. This will happen if
+ * device is undergoing VF reset. Otherwise, we would need to
+ * re-initialize pci interface again i.e. when device is not going
+ * through *any* reset or actually undergoing full reset.
+ */
+ if (hclgevf_dev_ongoing_reset(hdev))
+ return 0;
+
ret = pci_enable_device(pdev);
if (ret) {
dev_err(&pdev->dev, "failed to enable PCI device\n");
@@ -1309,19 +1600,16 @@ static void hclgevf_pci_uninit(struct hclgevf_dev *hdev)
pci_set_drvdata(pdev, NULL);
}
-static int hclgevf_init_ae_dev(struct hnae3_ae_dev *ae_dev)
+static int hclgevf_init_hdev(struct hclgevf_dev *hdev)
{
- struct pci_dev *pdev = ae_dev->pdev;
- struct hclgevf_dev *hdev;
+ struct pci_dev *pdev = hdev->pdev;
int ret;
- hdev = devm_kzalloc(&pdev->dev, sizeof(*hdev), GFP_KERNEL);
- if (!hdev)
- return -ENOMEM;
-
- hdev->pdev = pdev;
- hdev->ae_dev = ae_dev;
- ae_dev->priv = hdev;
+ /* check if device is on-going full reset(i.e. pcie as well) */
+ if (hclgevf_dev_ongoing_full_reset(hdev)) {
+ dev_warn(&pdev->dev, "device is going full reset\n");
+ hclgevf_uninit_hdev(hdev);
+ }
ret = hclgevf_pci_init(hdev);
if (ret) {
@@ -1406,15 +1694,38 @@ err_irq_init:
return ret;
}
-static void hclgevf_uninit_ae_dev(struct hnae3_ae_dev *ae_dev)
+static void hclgevf_uninit_hdev(struct hclgevf_dev *hdev)
{
- struct hclgevf_dev *hdev = ae_dev->priv;
-
hclgevf_cmd_uninit(hdev);
hclgevf_misc_irq_uninit(hdev);
hclgevf_state_uninit(hdev);
hclgevf_uninit_msi(hdev);
hclgevf_pci_uninit(hdev);
+}
+
+static int hclgevf_init_ae_dev(struct hnae3_ae_dev *ae_dev)
+{
+ struct pci_dev *pdev = ae_dev->pdev;
+ int ret;
+
+ ret = hclgevf_alloc_hdev(ae_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "hclge device allocation failed\n");
+ return ret;
+ }
+
+ ret = hclgevf_init_hdev(ae_dev->priv);
+ if (ret)
+ dev_err(&pdev->dev, "hclge device initialization failed\n");
+
+ return ret;
+}
+
+static void hclgevf_uninit_ae_dev(struct hnae3_ae_dev *ae_dev)
+{
+ struct hclgevf_dev *hdev = ae_dev->priv;
+
+ hclgevf_uninit_hdev(hdev);
ae_dev->priv = NULL;
}
@@ -1447,6 +1758,43 @@ static void hclgevf_get_channels(struct hnae3_handle *handle,
ch->combined_count = hdev->num_tqps;
}
+static void hclgevf_get_tqps_and_rss_info(struct hnae3_handle *handle,
+ u16 *free_tqps, u16 *max_rss_size)
+{
+ struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
+
+ *free_tqps = 0;
+ *max_rss_size = hdev->rss_size_max;
+}
+
+static int hclgevf_get_status(struct hnae3_handle *handle)
+{
+ struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
+
+ return hdev->hw.mac.link;
+}
+
+static void hclgevf_get_ksettings_an_result(struct hnae3_handle *handle,
+ u8 *auto_neg, u32 *speed,
+ u8 *duplex)
+{
+ struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
+
+ if (speed)
+ *speed = hdev->hw.mac.speed;
+ if (duplex)
+ *duplex = hdev->hw.mac.duplex;
+ if (auto_neg)
+ *auto_neg = AUTONEG_DISABLE;
+}
+
+void hclgevf_update_speed_duplex(struct hclgevf_dev *hdev, u32 speed,
+ u8 duplex)
+{
+ hdev->hw.mac.speed = speed;
+ hdev->hw.mac.duplex = duplex;
+}
+
static const struct hnae3_ae_ops hclgevf_ops = {
.init_ae_dev = hclgevf_init_ae_dev,
.uninit_ae_dev = hclgevf_uninit_ae_dev,
@@ -1457,6 +1805,7 @@ static const struct hnae3_ae_ops hclgevf_ops = {
.map_ring_to_vector = hclgevf_map_ring_to_vector,
.unmap_ring_from_vector = hclgevf_unmap_ring_from_vector,
.get_vector = hclgevf_get_vector,
+ .put_vector = hclgevf_put_vector,
.reset_queue = hclgevf_reset_tqp,
.set_promisc_mode = hclgevf_set_promisc_mode,
.get_mac_addr = hclgevf_get_mac_addr,
@@ -1476,7 +1825,11 @@ static const struct hnae3_ae_ops hclgevf_ops = {
.get_tc_size = hclgevf_get_tc_size,
.get_fw_version = hclgevf_get_fw_version,
.set_vlan_filter = hclgevf_set_vlan_filter,
+ .reset_event = hclgevf_reset_event,
.get_channels = hclgevf_get_channels,
+ .get_tqps_and_rss_info = hclgevf_get_tqps_and_rss_info,
+ .get_status = hclgevf_get_status,
+ .get_ksettings_an_result = hclgevf_get_ksettings_an_result,
};
static struct hnae3_ae_algo ae_algovf = {
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h
index a63bee4a3674..a477a7c36bbd 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h
@@ -34,6 +34,9 @@
#define HCLGEVF_VECTOR0_RX_CMDQ_INT_B 1
#define HCLGEVF_TQP_RESET_TRY_TIMES 10
+/* Reset related Registers */
+#define HCLGEVF_FUN_RST_ING 0x20C00
+#define HCLGEVF_FUN_RST_ING_B 0
#define HCLGEVF_RSS_IND_TBL_SIZE 512
#define HCLGEVF_RSS_SET_BITMAP_MSK 0xffff
@@ -52,6 +55,8 @@ enum hclgevf_states {
HCLGEVF_STATE_DISABLED,
/* task states */
HCLGEVF_STATE_SERVICE_SCHED,
+ HCLGEVF_STATE_RST_SERVICE_SCHED,
+ HCLGEVF_STATE_RST_HANDLING,
HCLGEVF_STATE_MBX_SERVICE_SCHED,
HCLGEVF_STATE_MBX_HANDLING,
};
@@ -61,6 +66,8 @@ enum hclgevf_states {
struct hclgevf_mac {
u8 mac_addr[ETH_ALEN];
int link;
+ u8 duplex;
+ u32 speed;
};
struct hclgevf_hw {
@@ -120,6 +127,11 @@ struct hclgevf_dev {
struct hclgevf_rss_cfg rss_cfg;
unsigned long state;
+#define HCLGEVF_RESET_REQUESTED 0
+#define HCLGEVF_RESET_PENDING 1
+ unsigned long reset_state; /* requested, pending */
+ u32 reset_attempts;
+
u32 fw_version;
u16 num_tqps; /* num task queue pairs of this PF */
@@ -140,10 +152,13 @@ struct hclgevf_dev {
int *vector_irq;
bool accept_mta_mc; /* whether to accept mta filter multicast */
+ bool mbx_event_pending;
struct hclgevf_mbx_resp_status mbx_resp; /* mailbox response */
+ struct hclgevf_mbx_arq_ring arq; /* mailbox async rx queue */
struct timer_list service_timer;
struct work_struct service_task;
+ struct work_struct rst_service_task;
struct work_struct mbx_service_task;
struct hclgevf_tqp *htqp;
@@ -156,9 +171,29 @@ struct hclgevf_dev {
u32 flag;
};
+static inline bool hclgevf_dev_ongoing_reset(struct hclgevf_dev *hdev)
+{
+ return (hdev &&
+ (test_bit(HCLGEVF_STATE_RST_HANDLING, &hdev->state)) &&
+ (hdev->nic.reset_level == HNAE3_VF_RESET));
+}
+
+static inline bool hclgevf_dev_ongoing_full_reset(struct hclgevf_dev *hdev)
+{
+ return (hdev &&
+ (test_bit(HCLGEVF_STATE_RST_HANDLING, &hdev->state)) &&
+ (hdev->nic.reset_level == HNAE3_VF_FULL_RESET));
+}
+
int hclgevf_send_mbx_msg(struct hclgevf_dev *hdev, u16 code, u16 subcode,
const u8 *msg_data, u8 msg_len, bool need_resp,
u8 *resp_data, u16 resp_len);
void hclgevf_mbx_handler(struct hclgevf_dev *hdev);
+void hclgevf_mbx_async_handler(struct hclgevf_dev *hdev);
+
void hclgevf_update_link_status(struct hclgevf_dev *hdev, int link_state);
+void hclgevf_update_speed_duplex(struct hclgevf_dev *hdev, u32 speed,
+ u8 duplex);
+void hclgevf_reset_task_schedule(struct hclgevf_dev *hdev);
+void hclgevf_mbx_task_schedule(struct hclgevf_dev *hdev);
#endif
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_mbx.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_mbx.c
index e39cad285fa9..a28618428338 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_mbx.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_mbx.c
@@ -54,6 +54,10 @@ static int hclgevf_get_mbx_resp(struct hclgevf_dev *hdev, u16 code0, u16 code1,
mbx_resp = &hdev->mbx_resp;
r_code0 = (u16)(mbx_resp->origin_mbx_msg >> 16);
r_code1 = (u16)(mbx_resp->origin_mbx_msg & 0xff);
+
+ if (mbx_resp->resp_status)
+ return mbx_resp->resp_status;
+
if (resp_data)
memcpy(resp_data, &mbx_resp->additional_info[0], resp_len);
@@ -128,7 +132,8 @@ void hclgevf_mbx_handler(struct hclgevf_dev *hdev)
struct hclge_mbx_pf_to_vf_cmd *req;
struct hclgevf_cmq_ring *crq;
struct hclgevf_desc *desc;
- u16 link_status, flag;
+ u16 *msg_q;
+ u16 flag;
u8 *temp;
int i;
@@ -140,6 +145,12 @@ void hclgevf_mbx_handler(struct hclgevf_dev *hdev)
desc = &crq->desc[crq->next_to_use];
req = (struct hclge_mbx_pf_to_vf_cmd *)desc->data;
+ /* synchronous messages are time critical and need preferential
+ * treatment. Therefore, we need to acknowledge all the sync
+ * responses as quickly as possible so that waiting tasks do not
+ * timeout and simultaneously queue the async messages for later
+ * prcessing in context of mailbox task i.e. the slow path.
+ */
switch (req->msg[0]) {
case HCLGE_MBX_PF_VF_RESP:
if (resp->received_resp)
@@ -159,10 +170,31 @@ void hclgevf_mbx_handler(struct hclgevf_dev *hdev)
}
break;
case HCLGE_MBX_LINK_STAT_CHANGE:
- link_status = le16_to_cpu(req->msg[1]);
+ case HCLGE_MBX_ASSERTING_RESET:
+ /* set this mbx event as pending. This is required as we
+ * might loose interrupt event when mbx task is busy
+ * handling. This shall be cleared when mbx task just
+ * enters handling state.
+ */
+ hdev->mbx_event_pending = true;
- /* update upper layer with new link link status */
- hclgevf_update_link_status(hdev, link_status);
+ /* we will drop the async msg if we find ARQ as full
+ * and continue with next message
+ */
+ if (hdev->arq.count >= HCLGE_MBX_MAX_ARQ_MSG_NUM) {
+ dev_warn(&hdev->pdev->dev,
+ "Async Q full, dropping msg(%d)\n",
+ req->msg[1]);
+ break;
+ }
+
+ /* tail the async message in arq */
+ msg_q = hdev->arq.msg_q[hdev->arq.tail];
+ memcpy(&msg_q[0], req->msg, HCLGE_MBX_MAX_ARQ_MSG_SIZE);
+ hclge_mbx_tail_ptr_move_arq(hdev->arq);
+ hdev->arq.count++;
+
+ hclgevf_mbx_task_schedule(hdev);
break;
default:
@@ -171,6 +203,7 @@ void hclgevf_mbx_handler(struct hclgevf_dev *hdev)
req->msg[0]);
break;
}
+ crq->desc[crq->next_to_use].flag = 0;
hclge_mbx_ring_ptr_move_crq(crq);
flag = le16_to_cpu(crq->desc[crq->next_to_use].flag);
}
@@ -179,3 +212,57 @@ void hclgevf_mbx_handler(struct hclgevf_dev *hdev)
hclgevf_write_dev(&hdev->hw, HCLGEVF_NIC_CRQ_HEAD_REG,
crq->next_to_use);
}
+
+void hclgevf_mbx_async_handler(struct hclgevf_dev *hdev)
+{
+ u16 link_status;
+ u16 *msg_q;
+ u8 duplex;
+ u32 speed;
+ u32 tail;
+
+ /* we can safely clear it now as we are at start of the async message
+ * processing
+ */
+ hdev->mbx_event_pending = false;
+
+ tail = hdev->arq.tail;
+
+ /* process all the async queue messages */
+ while (tail != hdev->arq.head) {
+ msg_q = hdev->arq.msg_q[hdev->arq.head];
+
+ switch (msg_q[0]) {
+ case HCLGE_MBX_LINK_STAT_CHANGE:
+ link_status = le16_to_cpu(msg_q[1]);
+ memcpy(&speed, &msg_q[2], sizeof(speed));
+ duplex = (u8)le16_to_cpu(msg_q[4]);
+
+ /* update upper layer with new link link status */
+ hclgevf_update_link_status(hdev, link_status);
+ hclgevf_update_speed_duplex(hdev, speed, duplex);
+
+ break;
+ case HCLGE_MBX_ASSERTING_RESET:
+ /* PF has asserted reset hence VF should go in pending
+ * state and poll for the hardware reset status till it
+ * has been completely reset. After this stack should
+ * eventually be re-initialized.
+ */
+ hdev->nic.reset_level = HNAE3_VF_RESET;
+ set_bit(HCLGEVF_RESET_PENDING, &hdev->reset_state);
+ hclgevf_reset_task_schedule(hdev);
+
+ break;
+ default:
+ dev_err(&hdev->pdev->dev,
+ "fetched unsupported(%d) message from arq\n",
+ msg_q[0]);
+ break;
+ }
+
+ hclge_mbx_head_ptr_move_arq(hdev->arq);
+ hdev->arq.count--;
+ msg_q = hdev->arq.msg_q[hdev->arq.head];
+ }
+}
diff --git a/drivers/net/ethernet/ibm/ehea/ehea_main.c b/drivers/net/ethernet/ibm/ehea/ehea_main.c
index 4878b7169e0f..ba580bfae512 100644
--- a/drivers/net/ethernet/ibm/ehea/ehea_main.c
+++ b/drivers/net/ethernet/ibm/ehea/ehea_main.c
@@ -2903,8 +2903,7 @@ static ssize_t ehea_show_port_id(struct device *dev,
return sprintf(buf, "%d", port->logical_port_id);
}
-static DEVICE_ATTR(log_port_id, S_IRUSR | S_IRGRP | S_IROTH, ehea_show_port_id,
- NULL);
+static DEVICE_ATTR(log_port_id, 0444, ehea_show_port_id, NULL);
static void logical_port_release(struct device *dev)
{
@@ -3235,8 +3234,8 @@ static ssize_t ehea_remove_port(struct device *dev,
return (ssize_t) count;
}
-static DEVICE_ATTR(probe_port, S_IWUSR, NULL, ehea_probe_port);
-static DEVICE_ATTR(remove_port, S_IWUSR, NULL, ehea_remove_port);
+static DEVICE_ATTR(probe_port, 0200, NULL, ehea_probe_port);
+static DEVICE_ATTR(remove_port, 0200, NULL, ehea_remove_port);
static int ehea_create_device_sysfs(struct platform_device *dev)
{
diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
index f210398200ec..c1b51edaaf62 100644
--- a/drivers/net/ethernet/ibm/ibmveth.c
+++ b/drivers/net/ethernet/ibm/ibmveth.c
@@ -82,7 +82,7 @@ module_param(rx_flush, uint, 0644);
MODULE_PARM_DESC(rx_flush, "Flush receive buffers before use");
static bool old_large_send __read_mostly;
-module_param(old_large_send, bool, S_IRUGO);
+module_param(old_large_send, bool, 0444);
MODULE_PARM_DESC(old_large_send,
"Use old large send method on firmware that supports the new method");
diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c
index 1b3cc8bb0705..6e8d6a6f6aaf 100644
--- a/drivers/net/ethernet/ibm/ibmvnic.c
+++ b/drivers/net/ethernet/ibm/ibmvnic.c
@@ -90,7 +90,7 @@ MODULE_VERSION(IBMVNIC_DRIVER_VERSION);
static int ibmvnic_version = IBMVNIC_INITIAL_VERSION;
static int ibmvnic_remove(struct vio_dev *);
-static void release_sub_crqs(struct ibmvnic_adapter *);
+static void release_sub_crqs(struct ibmvnic_adapter *, bool);
static int ibmvnic_reset_crq(struct ibmvnic_adapter *);
static int ibmvnic_send_crq_init(struct ibmvnic_adapter *);
static int ibmvnic_reenable_crq_queue(struct ibmvnic_adapter *);
@@ -111,13 +111,14 @@ static int ibmvnic_poll(struct napi_struct *napi, int data);
static void send_map_query(struct ibmvnic_adapter *adapter);
static void send_request_map(struct ibmvnic_adapter *, dma_addr_t, __be32, u8);
static void send_request_unmap(struct ibmvnic_adapter *, u8);
-static void send_login(struct ibmvnic_adapter *adapter);
+static int send_login(struct ibmvnic_adapter *adapter);
static void send_cap_queries(struct ibmvnic_adapter *adapter);
static int init_sub_crqs(struct ibmvnic_adapter *);
static int init_sub_crq_irqs(struct ibmvnic_adapter *adapter);
static int ibmvnic_init(struct ibmvnic_adapter *);
static void release_crq_queue(struct ibmvnic_adapter *);
static int __ibmvnic_set_mac(struct net_device *netdev, struct sockaddr *p);
+static int init_crq_queue(struct ibmvnic_adapter *adapter);
struct ibmvnic_stat {
char name[ETH_GSTRING_LEN];
@@ -320,18 +321,16 @@ failure:
dev_info(dev, "replenish pools failure\n");
pool->free_map[pool->next_free] = index;
pool->rx_buff[index].skb = NULL;
- if (!dma_mapping_error(dev, dma_addr))
- dma_unmap_single(dev, dma_addr, pool->buff_size,
- DMA_FROM_DEVICE);
dev_kfree_skb_any(skb);
adapter->replenish_add_buff_failure++;
atomic_add(buffers_added, &pool->available);
- if (lpar_rc == H_CLOSED) {
+ if (lpar_rc == H_CLOSED || adapter->failover_pending) {
/* Disable buffer pool replenishment and report carrier off if
- * queue is closed. Firmware guarantees that a signal will
- * be sent to the driver, triggering a reset.
+ * queue is closed or pending failover.
+ * Firmware guarantees that a signal will be sent to the
+ * driver, triggering a reset.
*/
deactivate_rx_pools(adapter);
netif_carrier_off(adapter->netdev);
@@ -361,14 +360,14 @@ static void release_stats_buffers(struct ibmvnic_adapter *adapter)
static int init_stats_buffers(struct ibmvnic_adapter *adapter)
{
adapter->tx_stats_buffers =
- kcalloc(adapter->req_tx_queues,
+ kcalloc(IBMVNIC_MAX_QUEUES,
sizeof(struct ibmvnic_tx_queue_stats),
GFP_KERNEL);
if (!adapter->tx_stats_buffers)
return -ENOMEM;
adapter->rx_stats_buffers =
- kcalloc(adapter->req_rx_queues,
+ kcalloc(IBMVNIC_MAX_QUEUES,
sizeof(struct ibmvnic_rx_queue_stats),
GFP_KERNEL);
if (!adapter->rx_stats_buffers)
@@ -509,7 +508,7 @@ static int init_rx_pools(struct net_device *netdev)
return -1;
}
- adapter->num_active_rx_pools = 0;
+ adapter->num_active_rx_pools = rxadd_subcrqs;
for (i = 0; i < rxadd_subcrqs; i++) {
rx_pool = &adapter->rx_pool[i];
@@ -554,41 +553,44 @@ static int init_rx_pools(struct net_device *netdev)
rx_pool->next_free = 0;
}
- adapter->num_active_rx_pools = rxadd_subcrqs;
+ return 0;
+}
+
+static int reset_one_tx_pool(struct ibmvnic_adapter *adapter,
+ struct ibmvnic_tx_pool *tx_pool)
+{
+ int rc, i;
+
+ rc = reset_long_term_buff(adapter, &tx_pool->long_term_buff);
+ if (rc)
+ return rc;
+
+ memset(tx_pool->tx_buff, 0,
+ tx_pool->num_buffers *
+ sizeof(struct ibmvnic_tx_buff));
+
+ for (i = 0; i < tx_pool->num_buffers; i++)
+ tx_pool->free_map[i] = i;
+
+ tx_pool->consumer_index = 0;
+ tx_pool->producer_index = 0;
return 0;
}
static int reset_tx_pools(struct ibmvnic_adapter *adapter)
{
- struct ibmvnic_tx_pool *tx_pool;
int tx_scrqs;
- int i, j, rc;
+ int i, rc;
tx_scrqs = be32_to_cpu(adapter->login_rsp_buf->num_txsubm_subcrqs);
for (i = 0; i < tx_scrqs; i++) {
- netdev_dbg(adapter->netdev, "Re-setting tx_pool[%d]\n", i);
-
- tx_pool = &adapter->tx_pool[i];
-
- rc = reset_long_term_buff(adapter, &tx_pool->long_term_buff);
+ rc = reset_one_tx_pool(adapter, &adapter->tso_pool[i]);
if (rc)
return rc;
-
- rc = reset_long_term_buff(adapter, &tx_pool->tso_ltb);
+ rc = reset_one_tx_pool(adapter, &adapter->tx_pool[i]);
if (rc)
return rc;
-
- memset(tx_pool->tx_buff, 0,
- adapter->req_tx_entries_per_subcrq *
- sizeof(struct ibmvnic_tx_buff));
-
- for (j = 0; j < adapter->req_tx_entries_per_subcrq; j++)
- tx_pool->free_map[j] = j;
-
- tx_pool->consumer_index = 0;
- tx_pool->producer_index = 0;
- tx_pool->tso_index = 0;
}
return 0;
@@ -605,35 +607,70 @@ static void release_vpd_data(struct ibmvnic_adapter *adapter)
adapter->vpd = NULL;
}
+static void release_one_tx_pool(struct ibmvnic_adapter *adapter,
+ struct ibmvnic_tx_pool *tx_pool)
+{
+ kfree(tx_pool->tx_buff);
+ kfree(tx_pool->free_map);
+ free_long_term_buff(adapter, &tx_pool->long_term_buff);
+}
+
static void release_tx_pools(struct ibmvnic_adapter *adapter)
{
- struct ibmvnic_tx_pool *tx_pool;
int i;
if (!adapter->tx_pool)
return;
for (i = 0; i < adapter->num_active_tx_pools; i++) {
- netdev_dbg(adapter->netdev, "Releasing tx_pool[%d]\n", i);
- tx_pool = &adapter->tx_pool[i];
- kfree(tx_pool->tx_buff);
- free_long_term_buff(adapter, &tx_pool->long_term_buff);
- free_long_term_buff(adapter, &tx_pool->tso_ltb);
- kfree(tx_pool->free_map);
+ release_one_tx_pool(adapter, &adapter->tx_pool[i]);
+ release_one_tx_pool(adapter, &adapter->tso_pool[i]);
}
kfree(adapter->tx_pool);
adapter->tx_pool = NULL;
+ kfree(adapter->tso_pool);
+ adapter->tso_pool = NULL;
adapter->num_active_tx_pools = 0;
}
+static int init_one_tx_pool(struct net_device *netdev,
+ struct ibmvnic_tx_pool *tx_pool,
+ int num_entries, int buf_size)
+{
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
+ int i;
+
+ tx_pool->tx_buff = kcalloc(num_entries,
+ sizeof(struct ibmvnic_tx_buff),
+ GFP_KERNEL);
+ if (!tx_pool->tx_buff)
+ return -1;
+
+ if (alloc_long_term_buff(adapter, &tx_pool->long_term_buff,
+ num_entries * buf_size))
+ return -1;
+
+ tx_pool->free_map = kcalloc(num_entries, sizeof(int), GFP_KERNEL);
+ if (!tx_pool->free_map)
+ return -1;
+
+ for (i = 0; i < num_entries; i++)
+ tx_pool->free_map[i] = i;
+
+ tx_pool->consumer_index = 0;
+ tx_pool->producer_index = 0;
+ tx_pool->num_buffers = num_entries;
+ tx_pool->buf_size = buf_size;
+
+ return 0;
+}
+
static int init_tx_pools(struct net_device *netdev)
{
struct ibmvnic_adapter *adapter = netdev_priv(netdev);
- struct device *dev = &adapter->vdev->dev;
- struct ibmvnic_tx_pool *tx_pool;
int tx_subcrqs;
- int i, j;
+ int i, rc;
tx_subcrqs = be32_to_cpu(adapter->login_rsp_buf->num_txsubm_subcrqs);
adapter->tx_pool = kcalloc(tx_subcrqs,
@@ -641,57 +678,31 @@ static int init_tx_pools(struct net_device *netdev)
if (!adapter->tx_pool)
return -1;
- adapter->num_active_tx_pools = 0;
-
- for (i = 0; i < tx_subcrqs; i++) {
- tx_pool = &adapter->tx_pool[i];
-
- netdev_dbg(adapter->netdev,
- "Initializing tx_pool[%d], %lld buffs\n",
- i, adapter->req_tx_entries_per_subcrq);
-
- tx_pool->tx_buff = kcalloc(adapter->req_tx_entries_per_subcrq,
- sizeof(struct ibmvnic_tx_buff),
- GFP_KERNEL);
- if (!tx_pool->tx_buff) {
- dev_err(dev, "tx pool buffer allocation failed\n");
- release_tx_pools(adapter);
- return -1;
- }
+ adapter->tso_pool = kcalloc(tx_subcrqs,
+ sizeof(struct ibmvnic_tx_pool), GFP_KERNEL);
+ if (!adapter->tso_pool)
+ return -1;
- if (alloc_long_term_buff(adapter, &tx_pool->long_term_buff,
- adapter->req_tx_entries_per_subcrq *
- adapter->req_mtu)) {
- release_tx_pools(adapter);
- return -1;
- }
+ adapter->num_active_tx_pools = tx_subcrqs;
- /* alloc TSO ltb */
- if (alloc_long_term_buff(adapter, &tx_pool->tso_ltb,
- IBMVNIC_TSO_BUFS *
- IBMVNIC_TSO_BUF_SZ)) {
+ for (i = 0; i < tx_subcrqs; i++) {
+ rc = init_one_tx_pool(netdev, &adapter->tx_pool[i],
+ adapter->req_tx_entries_per_subcrq,
+ adapter->req_mtu + VLAN_HLEN);
+ if (rc) {
release_tx_pools(adapter);
- return -1;
+ return rc;
}
- tx_pool->tso_index = 0;
-
- tx_pool->free_map = kcalloc(adapter->req_tx_entries_per_subcrq,
- sizeof(int), GFP_KERNEL);
- if (!tx_pool->free_map) {
+ init_one_tx_pool(netdev, &adapter->tso_pool[i],
+ IBMVNIC_TSO_BUFS,
+ IBMVNIC_TSO_BUF_SZ);
+ if (rc) {
release_tx_pools(adapter);
- return -1;
+ return rc;
}
-
- for (j = 0; j < adapter->req_tx_entries_per_subcrq; j++)
- tx_pool->free_map[j] = j;
-
- tx_pool->consumer_index = 0;
- tx_pool->producer_index = 0;
}
- adapter->num_active_tx_pools = tx_subcrqs;
-
return 0;
}
@@ -740,47 +751,104 @@ static void ibmvnic_napi_disable(struct ibmvnic_adapter *adapter)
adapter->napi_enabled = false;
}
+static int init_napi(struct ibmvnic_adapter *adapter)
+{
+ int i;
+
+ adapter->napi = kcalloc(adapter->req_rx_queues,
+ sizeof(struct napi_struct), GFP_KERNEL);
+ if (!adapter->napi)
+ return -ENOMEM;
+
+ for (i = 0; i < adapter->req_rx_queues; i++) {
+ netdev_dbg(adapter->netdev, "Adding napi[%d]\n", i);
+ netif_napi_add(adapter->netdev, &adapter->napi[i],
+ ibmvnic_poll, NAPI_POLL_WEIGHT);
+ }
+
+ adapter->num_active_rx_napi = adapter->req_rx_queues;
+ return 0;
+}
+
+static void release_napi(struct ibmvnic_adapter *adapter)
+{
+ int i;
+
+ if (!adapter->napi)
+ return;
+
+ for (i = 0; i < adapter->num_active_rx_napi; i++) {
+ if (&adapter->napi[i]) {
+ netdev_dbg(adapter->netdev,
+ "Releasing napi[%d]\n", i);
+ netif_napi_del(&adapter->napi[i]);
+ }
+ }
+
+ kfree(adapter->napi);
+ adapter->napi = NULL;
+ adapter->num_active_rx_napi = 0;
+}
+
static int ibmvnic_login(struct net_device *netdev)
{
struct ibmvnic_adapter *adapter = netdev_priv(netdev);
unsigned long timeout = msecs_to_jiffies(30000);
- struct device *dev = &adapter->vdev->dev;
+ int retry_count = 0;
int rc;
do {
- if (adapter->renegotiate) {
- adapter->renegotiate = false;
- release_sub_crqs(adapter);
+ if (retry_count > IBMVNIC_MAX_QUEUES) {
+ netdev_warn(netdev, "Login attempts exceeded\n");
+ return -1;
+ }
+
+ adapter->init_done_rc = 0;
+ reinit_completion(&adapter->init_done);
+ rc = send_login(adapter);
+ if (rc) {
+ netdev_warn(netdev, "Unable to login\n");
+ return rc;
+ }
+
+ if (!wait_for_completion_timeout(&adapter->init_done,
+ timeout)) {
+ netdev_warn(netdev, "Login timed out\n");
+ return -1;
+ }
+
+ if (adapter->init_done_rc == PARTIALSUCCESS) {
+ retry_count++;
+ release_sub_crqs(adapter, 1);
+ adapter->init_done_rc = 0;
reinit_completion(&adapter->init_done);
send_cap_queries(adapter);
if (!wait_for_completion_timeout(&adapter->init_done,
timeout)) {
- dev_err(dev, "Capabilities query timeout\n");
+ netdev_warn(netdev,
+ "Capabilities query timed out\n");
return -1;
}
+
rc = init_sub_crqs(adapter);
if (rc) {
- dev_err(dev,
- "Initialization of SCRQ's failed\n");
+ netdev_warn(netdev,
+ "SCRQ initialization failed\n");
return -1;
}
+
rc = init_sub_crq_irqs(adapter);
if (rc) {
- dev_err(dev,
- "Initialization of SCRQ's irqs failed\n");
+ netdev_warn(netdev,
+ "SCRQ irq initialization failed\n");
return -1;
}
- }
-
- reinit_completion(&adapter->init_done);
- send_login(adapter);
- if (!wait_for_completion_timeout(&adapter->init_done,
- timeout)) {
- dev_err(dev, "Login timeout\n");
+ } else if (adapter->init_done_rc) {
+ netdev_warn(netdev, "Adapter login failed\n");
return -1;
}
- } while (adapter->renegotiate);
+ } while (adapter->init_done_rc == PARTIALSUCCESS);
/* handle pending MAC address changes after successful login */
if (adapter->mac_change_pending) {
@@ -805,29 +873,13 @@ static void release_login_rsp_buffer(struct ibmvnic_adapter *adapter)
static void release_resources(struct ibmvnic_adapter *adapter)
{
- int i;
-
release_vpd_data(adapter);
release_tx_pools(adapter);
release_rx_pools(adapter);
- release_stats_token(adapter);
- release_stats_buffers(adapter);
release_error_buffers(adapter);
-
- if (adapter->napi) {
- for (i = 0; i < adapter->req_rx_queues; i++) {
- if (&adapter->napi[i]) {
- netdev_dbg(adapter->netdev,
- "Releasing napi[%d]\n", i);
- netif_napi_del(&adapter->napi[i]);
- }
- }
- }
- kfree(adapter->napi);
- adapter->napi = NULL;
-
+ release_napi(adapter);
release_login_rsp_buffer(adapter);
}
@@ -947,20 +999,12 @@ static int ibmvnic_get_vpd(struct ibmvnic_adapter *adapter)
static int init_resources(struct ibmvnic_adapter *adapter)
{
struct net_device *netdev = adapter->netdev;
- int i, rc;
+ int rc;
rc = set_real_num_queues(netdev);
if (rc)
return rc;
- rc = init_stats_buffers(adapter);
- if (rc)
- return rc;
-
- rc = init_stats_token(adapter);
- if (rc)
- return rc;
-
adapter->vpd = kzalloc(sizeof(*adapter->vpd), GFP_KERNEL);
if (!adapter->vpd)
return -ENOMEM;
@@ -973,16 +1017,10 @@ static int init_resources(struct ibmvnic_adapter *adapter)
}
adapter->map_id = 1;
- adapter->napi = kcalloc(adapter->req_rx_queues,
- sizeof(struct napi_struct), GFP_KERNEL);
- if (!adapter->napi)
- return -ENOMEM;
- for (i = 0; i < adapter->req_rx_queues; i++) {
- netdev_dbg(netdev, "Adding napi[%d]\n", i);
- netif_napi_add(netdev, &adapter->napi[i], ibmvnic_poll,
- NAPI_POLL_WEIGHT);
- }
+ rc = init_napi(adapter);
+ if (rc)
+ return rc;
send_map_query(adapter);
@@ -1011,16 +1049,14 @@ static int __ibmvnic_open(struct net_device *netdev)
netdev_dbg(netdev, "Enabling rx_scrq[%d] irq\n", i);
if (prev_state == VNIC_CLOSED)
enable_irq(adapter->rx_scrq[i]->irq);
- else
- enable_scrq_irq(adapter, adapter->rx_scrq[i]);
+ enable_scrq_irq(adapter, adapter->rx_scrq[i]);
}
for (i = 0; i < adapter->req_tx_queues; i++) {
netdev_dbg(netdev, "Enabling tx_scrq[%d] irq\n", i);
if (prev_state == VNIC_CLOSED)
enable_irq(adapter->tx_scrq[i]->irq);
- else
- enable_scrq_irq(adapter, adapter->tx_scrq[i]);
+ enable_scrq_irq(adapter, adapter->tx_scrq[i]);
}
rc = set_link_state(adapter, IBMVNIC_LOGICAL_LNK_UP);
@@ -1047,6 +1083,14 @@ static int ibmvnic_open(struct net_device *netdev)
struct ibmvnic_adapter *adapter = netdev_priv(netdev);
int rc;
+ /* If device failover is pending, just set device state and return.
+ * Device operation will be handled by reset routine.
+ */
+ if (adapter->failover_pending) {
+ adapter->state = VNIC_OPEN;
+ return 0;
+ }
+
mutex_lock(&adapter->reset_lock);
if (adapter->state != VNIC_CLOSED) {
@@ -1076,6 +1120,7 @@ static int ibmvnic_open(struct net_device *netdev)
static void clean_rx_pools(struct ibmvnic_adapter *adapter)
{
struct ibmvnic_rx_pool *rx_pool;
+ struct ibmvnic_rx_buff *rx_buff;
u64 rx_entries;
int rx_scrqs;
int i, j;
@@ -1083,106 +1128,120 @@ static void clean_rx_pools(struct ibmvnic_adapter *adapter)
if (!adapter->rx_pool)
return;
- rx_scrqs = be32_to_cpu(adapter->login_rsp_buf->num_rxadd_subcrqs);
+ rx_scrqs = adapter->num_active_rx_pools;
rx_entries = adapter->req_rx_add_entries_per_subcrq;
/* Free any remaining skbs in the rx buffer pools */
for (i = 0; i < rx_scrqs; i++) {
rx_pool = &adapter->rx_pool[i];
- if (!rx_pool)
+ if (!rx_pool || !rx_pool->rx_buff)
continue;
netdev_dbg(adapter->netdev, "Cleaning rx_pool[%d]\n", i);
for (j = 0; j < rx_entries; j++) {
- if (rx_pool->rx_buff[j].skb) {
- dev_kfree_skb_any(rx_pool->rx_buff[j].skb);
- rx_pool->rx_buff[j].skb = NULL;
+ rx_buff = &rx_pool->rx_buff[j];
+ if (rx_buff && rx_buff->skb) {
+ dev_kfree_skb_any(rx_buff->skb);
+ rx_buff->skb = NULL;
}
}
}
}
-static void clean_tx_pools(struct ibmvnic_adapter *adapter)
+static void clean_one_tx_pool(struct ibmvnic_adapter *adapter,
+ struct ibmvnic_tx_pool *tx_pool)
{
- struct ibmvnic_tx_pool *tx_pool;
+ struct ibmvnic_tx_buff *tx_buff;
u64 tx_entries;
- int tx_scrqs;
- int i, j;
+ int i;
- if (!adapter->tx_pool)
+ if (!tx_pool || !tx_pool->tx_buff)
return;
- tx_scrqs = be32_to_cpu(adapter->login_rsp_buf->num_txsubm_subcrqs);
- tx_entries = adapter->req_tx_entries_per_subcrq;
-
- /* Free any remaining skbs in the tx buffer pools */
- for (i = 0; i < tx_scrqs; i++) {
- tx_pool = &adapter->tx_pool[i];
- if (!tx_pool)
- continue;
+ tx_entries = tx_pool->num_buffers;
- netdev_dbg(adapter->netdev, "Cleaning tx_pool[%d]\n", i);
- for (j = 0; j < tx_entries; j++) {
- if (tx_pool->tx_buff[j].skb) {
- dev_kfree_skb_any(tx_pool->tx_buff[j].skb);
- tx_pool->tx_buff[j].skb = NULL;
- }
+ for (i = 0; i < tx_entries; i++) {
+ tx_buff = &tx_pool->tx_buff[i];
+ if (tx_buff && tx_buff->skb) {
+ dev_kfree_skb_any(tx_buff->skb);
+ tx_buff->skb = NULL;
}
}
}
-static int __ibmvnic_close(struct net_device *netdev)
+static void clean_tx_pools(struct ibmvnic_adapter *adapter)
{
- struct ibmvnic_adapter *adapter = netdev_priv(netdev);
- int rc = 0;
+ int tx_scrqs;
int i;
- adapter->state = VNIC_CLOSING;
+ if (!adapter->tx_pool || !adapter->tso_pool)
+ return;
- /* ensure that transmissions are stopped if called by do_reset */
- if (adapter->resetting)
- netif_tx_disable(netdev);
- else
- netif_tx_stop_all_queues(netdev);
+ tx_scrqs = adapter->num_active_tx_pools;
- ibmvnic_napi_disable(adapter);
+ /* Free any remaining skbs in the tx buffer pools */
+ for (i = 0; i < tx_scrqs; i++) {
+ netdev_dbg(adapter->netdev, "Cleaning tx_pool[%d]\n", i);
+ clean_one_tx_pool(adapter, &adapter->tx_pool[i]);
+ clean_one_tx_pool(adapter, &adapter->tso_pool[i]);
+ }
+}
+
+static void ibmvnic_disable_irqs(struct ibmvnic_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+ int i;
if (adapter->tx_scrq) {
for (i = 0; i < adapter->req_tx_queues; i++)
if (adapter->tx_scrq[i]->irq) {
- netdev_dbg(adapter->netdev,
+ netdev_dbg(netdev,
"Disabling tx_scrq[%d] irq\n", i);
+ disable_scrq_irq(adapter, adapter->tx_scrq[i]);
disable_irq(adapter->tx_scrq[i]->irq);
}
}
- rc = set_link_state(adapter, IBMVNIC_LOGICAL_LNK_DN);
- if (rc)
- return rc;
-
if (adapter->rx_scrq) {
for (i = 0; i < adapter->req_rx_queues; i++) {
- int retries = 10;
-
- while (pending_scrq(adapter, adapter->rx_scrq[i])) {
- retries--;
- mdelay(100);
-
- if (retries == 0)
- break;
- }
-
if (adapter->rx_scrq[i]->irq) {
- netdev_dbg(adapter->netdev,
+ netdev_dbg(netdev,
"Disabling rx_scrq[%d] irq\n", i);
+ disable_scrq_irq(adapter, adapter->rx_scrq[i]);
disable_irq(adapter->rx_scrq[i]->irq);
}
}
}
+}
+
+static void ibmvnic_cleanup(struct net_device *netdev)
+{
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
+
+ /* ensure that transmissions are stopped if called by do_reset */
+ if (adapter->resetting)
+ netif_tx_disable(netdev);
+ else
+ netif_tx_stop_all_queues(netdev);
+
+ ibmvnic_napi_disable(adapter);
+ ibmvnic_disable_irqs(adapter);
+
clean_rx_pools(adapter);
clean_tx_pools(adapter);
+}
+
+static int __ibmvnic_close(struct net_device *netdev)
+{
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
+ int rc = 0;
+
+ adapter->state = VNIC_CLOSING;
+ rc = set_link_state(adapter, IBMVNIC_LOGICAL_LNK_DN);
+ if (rc)
+ return rc;
adapter->state = VNIC_CLOSED;
- return rc;
+ return 0;
}
static int ibmvnic_close(struct net_device *netdev)
@@ -1190,8 +1249,17 @@ static int ibmvnic_close(struct net_device *netdev)
struct ibmvnic_adapter *adapter = netdev_priv(netdev);
int rc;
+ /* If device failover is pending, just set device state and return.
+ * Device operation will be handled by reset routine.
+ */
+ if (adapter->failover_pending) {
+ adapter->state = VNIC_CLOSED;
+ return 0;
+ }
+
mutex_lock(&adapter->reset_lock);
rc = __ibmvnic_close(netdev);
+ ibmvnic_cleanup(netdev);
mutex_unlock(&adapter->reset_lock);
return rc;
@@ -1214,7 +1282,10 @@ static int build_hdr_data(u8 hdr_field, struct sk_buff *skb,
int len = 0;
u8 *hdr;
- hdr_len[0] = sizeof(struct ethhdr);
+ if (skb_vlan_tagged(skb) && !skb_vlan_tag_present(skb))
+ hdr_len[0] = sizeof(struct vlan_ethhdr);
+ else
+ hdr_len[0] = sizeof(struct ethhdr);
if (skb->protocol == htons(ETH_P_IP)) {
hdr_len[1] = ip_hdr(skb)->ihl * 4;
@@ -1330,6 +1401,21 @@ static void build_hdr_descs_arr(struct ibmvnic_tx_buff *txbuff,
txbuff->indir_arr + 1);
}
+static int ibmvnic_xmit_workarounds(struct sk_buff *skb,
+ struct net_device *netdev)
+{
+ /* For some backing devices, mishandling of small packets
+ * can result in a loss of connection or TX stall. Device
+ * architects recommend that no packet should be smaller
+ * than the minimum MTU value provided to the driver, so
+ * pad any packets to that length
+ */
+ if (skb->len < netdev->min_mtu)
+ return skb_put_padto(skb, netdev->min_mtu);
+
+ return 0;
+}
+
static int ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev)
{
struct ibmvnic_adapter *adapter = netdev_priv(netdev);
@@ -1367,7 +1453,17 @@ static int ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev)
goto out;
}
- tx_pool = &adapter->tx_pool[queue_num];
+ if (ibmvnic_xmit_workarounds(skb, netdev)) {
+ tx_dropped++;
+ tx_send_failed++;
+ ret = NETDEV_TX_OK;
+ goto out;
+ }
+ if (skb_is_gso(skb))
+ tx_pool = &adapter->tso_pool[queue_num];
+ else
+ tx_pool = &adapter->tx_pool[queue_num];
+
tx_scrq = adapter->tx_scrq[queue_num];
txq = netdev_get_tx_queue(netdev, skb_get_queue_mapping(skb));
handle_array = (u64 *)((u8 *)(adapter->login_rsp_buf) +
@@ -1375,21 +1471,21 @@ static int ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev)
index = tx_pool->free_map[tx_pool->consumer_index];
- if (skb_is_gso(skb)) {
- offset = tx_pool->tso_index * IBMVNIC_TSO_BUF_SZ;
- dst = tx_pool->tso_ltb.buff + offset;
- memset(dst, 0, IBMVNIC_TSO_BUF_SZ);
- data_dma_addr = tx_pool->tso_ltb.addr + offset;
- tx_pool->tso_index++;
- if (tx_pool->tso_index == IBMVNIC_TSO_BUFS)
- tx_pool->tso_index = 0;
- } else {
- offset = index * adapter->req_mtu;
- dst = tx_pool->long_term_buff.buff + offset;
- memset(dst, 0, adapter->req_mtu);
- data_dma_addr = tx_pool->long_term_buff.addr + offset;
+ if (index == IBMVNIC_INVALID_MAP) {
+ dev_kfree_skb_any(skb);
+ tx_send_failed++;
+ tx_dropped++;
+ ret = NETDEV_TX_OK;
+ goto out;
}
+ tx_pool->free_map[tx_pool->consumer_index] = IBMVNIC_INVALID_MAP;
+
+ offset = index * tx_pool->buf_size;
+ dst = tx_pool->long_term_buff.buff + offset;
+ memset(dst, 0, tx_pool->buf_size);
+ data_dma_addr = tx_pool->long_term_buff.addr + offset;
+
if (skb_shinfo(skb)->nr_frags) {
int cur, i;
@@ -1411,8 +1507,7 @@ static int ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev)
}
tx_pool->consumer_index =
- (tx_pool->consumer_index + 1) %
- adapter->req_tx_entries_per_subcrq;
+ (tx_pool->consumer_index + 1) % tx_pool->num_buffers;
tx_buff = &tx_pool->tx_buff[index];
tx_buff->skb = skb;
@@ -1428,11 +1523,13 @@ static int ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev)
tx_crq.v1.n_crq_elem = 1;
tx_crq.v1.n_sge = 1;
tx_crq.v1.flags1 = IBMVNIC_TX_COMP_NEEDED;
- tx_crq.v1.correlator = cpu_to_be32(index);
+
if (skb_is_gso(skb))
- tx_crq.v1.dma_reg = cpu_to_be16(tx_pool->tso_ltb.map_id);
+ tx_crq.v1.correlator =
+ cpu_to_be32(index | IBMVNIC_TSO_POOL_MASK);
else
- tx_crq.v1.dma_reg = cpu_to_be16(tx_pool->long_term_buff.map_id);
+ tx_crq.v1.correlator = cpu_to_be32(index);
+ tx_crq.v1.dma_reg = cpu_to_be16(tx_pool->long_term_buff.map_id);
tx_crq.v1.sge_len = cpu_to_be32(skb->len);
tx_crq.v1.ioba = cpu_to_be64(data_dma_addr);
@@ -1467,6 +1564,7 @@ static int ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev)
if ((*hdrs >> 7) & 1) {
build_hdr_descs_arr(tx_buff, &num_entries, *hdrs);
tx_crq.v1.n_crq_elem = num_entries;
+ tx_buff->num_entries = num_entries;
tx_buff->indir_arr[0] = tx_crq;
tx_buff->indir_dma = dma_map_single(dev, tx_buff->indir_arr,
sizeof(tx_buff->indir_arr),
@@ -1479,29 +1577,24 @@ static int ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev)
tx_map_failed++;
tx_dropped++;
ret = NETDEV_TX_OK;
- goto out;
+ goto tx_err_out;
}
lpar_rc = send_subcrq_indirect(adapter, handle_array[queue_num],
(u64)tx_buff->indir_dma,
(u64)num_entries);
} else {
+ tx_buff->num_entries = num_entries;
lpar_rc = send_subcrq(adapter, handle_array[queue_num],
&tx_crq);
}
if (lpar_rc != H_SUCCESS) {
dev_err(dev, "tx failed with code %ld\n", lpar_rc);
-
- if (tx_pool->consumer_index == 0)
- tx_pool->consumer_index =
- adapter->req_tx_entries_per_subcrq - 1;
- else
- tx_pool->consumer_index--;
-
dev_kfree_skb_any(skb);
tx_buff->skb = NULL;
- if (lpar_rc == H_CLOSED) {
- /* Disable TX and report carrier off if queue is closed.
+ if (lpar_rc == H_CLOSED || adapter->failover_pending) {
+ /* Disable TX and report carrier off if queue is closed
+ * or pending failover.
* Firmware guarantees that a signal will be sent to the
* driver, triggering a reset or some other action.
*/
@@ -1512,12 +1605,12 @@ static int ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev)
tx_send_failed++;
tx_dropped++;
ret = NETDEV_TX_OK;
- goto out;
+ goto tx_err_out;
}
- if (atomic_inc_return(&tx_scrq->used)
+ if (atomic_add_return(num_entries, &tx_scrq->used)
>= adapter->req_tx_entries_per_subcrq) {
- netdev_info(netdev, "Stopping queue %d\n", queue_num);
+ netdev_dbg(netdev, "Stopping queue %d\n", queue_num);
netif_stop_subqueue(netdev, queue_num);
}
@@ -1525,7 +1618,16 @@ static int ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev)
tx_bytes += skb->len;
txq->trans_start = jiffies;
ret = NETDEV_TX_OK;
+ goto out;
+tx_err_out:
+ /* roll back consumer index and map array*/
+ if (tx_pool->consumer_index == 0)
+ tx_pool->consumer_index =
+ tx_pool->num_buffers - 1;
+ else
+ tx_pool->consumer_index--;
+ tx_pool->free_map[tx_pool->consumer_index] = index;
out:
netdev->stats.tx_dropped += tx_dropped;
netdev->stats.tx_bytes += tx_bytes;
@@ -1640,20 +1742,19 @@ static int do_reset(struct ibmvnic_adapter *adapter,
old_num_rx_queues = adapter->req_rx_queues;
old_num_tx_queues = adapter->req_tx_queues;
- if (rwi->reset_reason == VNIC_RESET_MOBILITY) {
- rc = ibmvnic_reenable_crq_queue(adapter);
+ ibmvnic_cleanup(netdev);
+
+ if (adapter->reset_reason != VNIC_RESET_MOBILITY &&
+ adapter->reset_reason != VNIC_RESET_FAILOVER) {
+ rc = __ibmvnic_close(netdev);
if (rc)
- return 0;
+ return rc;
}
- rc = __ibmvnic_close(netdev);
- if (rc)
- return rc;
-
if (adapter->reset_reason == VNIC_RESET_CHANGE_PARAM ||
adapter->wait_for_reset) {
release_resources(adapter);
- release_sub_crqs(adapter);
+ release_sub_crqs(adapter, 1);
release_crq_queue(adapter);
}
@@ -1663,6 +1764,23 @@ static int do_reset(struct ibmvnic_adapter *adapter,
*/
adapter->state = VNIC_PROBED;
+ if (adapter->wait_for_reset) {
+ rc = init_crq_queue(adapter);
+ } else if (adapter->reset_reason == VNIC_RESET_MOBILITY) {
+ rc = ibmvnic_reenable_crq_queue(adapter);
+ release_sub_crqs(adapter, 1);
+ } else {
+ rc = ibmvnic_reset_crq(adapter);
+ if (!rc)
+ rc = vio_enable_interrupts(adapter->vdev);
+ }
+
+ if (rc) {
+ netdev_err(adapter->netdev,
+ "Couldn't initialize crq. rc=%d\n", rc);
+ return rc;
+ }
+
rc = ibmvnic_init(adapter);
if (rc)
return IBMVNIC_INIT_FAILED;
@@ -1691,6 +1809,9 @@ static int do_reset(struct ibmvnic_adapter *adapter,
release_tx_pools(adapter);
init_rx_pools(netdev);
init_tx_pools(netdev);
+
+ release_napi(adapter);
+ init_napi(adapter);
} else {
rc = reset_tx_pools(adapter);
if (rc)
@@ -1699,12 +1820,15 @@ static int do_reset(struct ibmvnic_adapter *adapter,
rc = reset_rx_pools(adapter);
if (rc)
return rc;
-
- if (reset_state == VNIC_CLOSED)
- return 0;
}
}
+ ibmvnic_disable_irqs(adapter);
+ adapter->state = VNIC_CLOSED;
+
+ if (reset_state == VNIC_CLOSED)
+ return 0;
+
rc = __ibmvnic_open(netdev);
if (rc) {
if (list_empty(&adapter->rwi_list))
@@ -1719,7 +1843,8 @@ static int do_reset(struct ibmvnic_adapter *adapter,
for (i = 0; i < adapter->req_rx_queues; i++)
napi_schedule(&adapter->napi[i]);
- if (adapter->reset_reason != VNIC_RESET_FAILOVER)
+ if (adapter->reset_reason != VNIC_RESET_FAILOVER &&
+ adapter->reset_reason != VNIC_RESET_CHANGE_PARAM)
netdev_notify_peers(netdev);
netif_carrier_on(netdev);
@@ -1798,23 +1923,26 @@ static void __ibmvnic_reset(struct work_struct *work)
mutex_unlock(&adapter->reset_lock);
}
-static void ibmvnic_reset(struct ibmvnic_adapter *adapter,
- enum ibmvnic_reset_reason reason)
+static int ibmvnic_reset(struct ibmvnic_adapter *adapter,
+ enum ibmvnic_reset_reason reason)
{
struct ibmvnic_rwi *rwi, *tmp;
struct net_device *netdev = adapter->netdev;
struct list_head *entry;
+ int ret;
if (adapter->state == VNIC_REMOVING ||
- adapter->state == VNIC_REMOVED) {
- netdev_dbg(netdev, "Adapter removing, skipping reset\n");
- return;
+ adapter->state == VNIC_REMOVED ||
+ adapter->failover_pending) {
+ ret = EBUSY;
+ netdev_dbg(netdev, "Adapter removing or pending failover, skipping reset\n");
+ goto err;
}
if (adapter->state == VNIC_PROBING) {
netdev_warn(netdev, "Adapter reset during probe\n");
- adapter->init_done_rc = EAGAIN;
- return;
+ ret = adapter->init_done_rc = EAGAIN;
+ goto err;
}
mutex_lock(&adapter->rwi_lock);
@@ -1824,7 +1952,8 @@ static void ibmvnic_reset(struct ibmvnic_adapter *adapter,
if (tmp->reset_reason == reason) {
netdev_dbg(netdev, "Skipping matching reset\n");
mutex_unlock(&adapter->rwi_lock);
- return;
+ ret = EBUSY;
+ goto err;
}
}
@@ -1832,7 +1961,8 @@ static void ibmvnic_reset(struct ibmvnic_adapter *adapter,
if (!rwi) {
mutex_unlock(&adapter->rwi_lock);
ibmvnic_close(netdev);
- return;
+ ret = ENOMEM;
+ goto err;
}
rwi->reset_reason = reason;
@@ -1841,6 +1971,12 @@ static void ibmvnic_reset(struct ibmvnic_adapter *adapter,
netdev_dbg(adapter->netdev, "Scheduling reset (reason %d)\n", reason);
schedule_work(&adapter->ibmvnic_reset);
+
+ return 0;
+err:
+ if (adapter->wait_for_reset)
+ adapter->wait_for_reset = false;
+ return -ret;
}
static void ibmvnic_tx_timeout(struct net_device *dev)
@@ -1975,6 +2111,8 @@ static void ibmvnic_netpoll_controller(struct net_device *dev)
static int wait_for_reset(struct ibmvnic_adapter *adapter)
{
+ int rc, ret;
+
adapter->fallback.mtu = adapter->req_mtu;
adapter->fallback.rx_queues = adapter->req_rx_queues;
adapter->fallback.tx_queues = adapter->req_tx_queues;
@@ -1982,11 +2120,15 @@ static int wait_for_reset(struct ibmvnic_adapter *adapter)
adapter->fallback.tx_entries = adapter->req_tx_entries_per_subcrq;
init_completion(&adapter->reset_done);
- ibmvnic_reset(adapter, VNIC_RESET_CHANGE_PARAM);
adapter->wait_for_reset = true;
+ rc = ibmvnic_reset(adapter, VNIC_RESET_CHANGE_PARAM);
+ if (rc)
+ return rc;
wait_for_completion(&adapter->reset_done);
+ ret = 0;
if (adapter->reset_done_rc) {
+ ret = -EIO;
adapter->desired.mtu = adapter->fallback.mtu;
adapter->desired.rx_queues = adapter->fallback.rx_queues;
adapter->desired.tx_queues = adapter->fallback.tx_queues;
@@ -1994,12 +2136,15 @@ static int wait_for_reset(struct ibmvnic_adapter *adapter)
adapter->desired.tx_entries = adapter->fallback.tx_entries;
init_completion(&adapter->reset_done);
- ibmvnic_reset(adapter, VNIC_RESET_CHANGE_PARAM);
+ adapter->wait_for_reset = true;
+ rc = ibmvnic_reset(adapter, VNIC_RESET_CHANGE_PARAM);
+ if (rc)
+ return ret;
wait_for_completion(&adapter->reset_done);
}
adapter->wait_for_reset = false;
- return adapter->reset_done_rc;
+ return ret;
}
static int ibmvnic_change_mtu(struct net_device *netdev, int new_mtu)
@@ -2011,6 +2156,23 @@ static int ibmvnic_change_mtu(struct net_device *netdev, int new_mtu)
return wait_for_reset(adapter);
}
+static netdev_features_t ibmvnic_features_check(struct sk_buff *skb,
+ struct net_device *dev,
+ netdev_features_t features)
+{
+ /* Some backing hardware adapters can not
+ * handle packets with a MSS less than 224
+ * or with only one segment.
+ */
+ if (skb_is_gso(skb)) {
+ if (skb_shinfo(skb)->gso_size < 224 ||
+ skb_shinfo(skb)->gso_segs == 1)
+ features &= ~NETIF_F_GSO_MASK;
+ }
+
+ return features;
+}
+
static const struct net_device_ops ibmvnic_netdev_ops = {
.ndo_open = ibmvnic_open,
.ndo_stop = ibmvnic_close,
@@ -2023,6 +2185,7 @@ static const struct net_device_ops ibmvnic_netdev_ops = {
.ndo_poll_controller = ibmvnic_netpoll_controller,
#endif
.ndo_change_mtu = ibmvnic_change_mtu,
+ .ndo_features_check = ibmvnic_features_check,
};
/* ethtool functions */
@@ -2266,6 +2429,7 @@ static int reset_one_sub_crq_queue(struct ibmvnic_adapter *adapter,
}
memset(scrq->msgs, 0, 4 * PAGE_SIZE);
+ atomic_set(&scrq->used, 0);
scrq->cur = 0;
rc = h_reg_sub_crq(adapter->vdev->unit_address, scrq->msg_token,
@@ -2295,24 +2459,27 @@ static int reset_sub_crq_queues(struct ibmvnic_adapter *adapter)
}
static void release_sub_crq_queue(struct ibmvnic_adapter *adapter,
- struct ibmvnic_sub_crq_queue *scrq)
+ struct ibmvnic_sub_crq_queue *scrq,
+ bool do_h_free)
{
struct device *dev = &adapter->vdev->dev;
long rc;
netdev_dbg(adapter->netdev, "Releasing sub-CRQ\n");
- /* Close the sub-crqs */
- do {
- rc = plpar_hcall_norets(H_FREE_SUB_CRQ,
- adapter->vdev->unit_address,
- scrq->crq_num);
- } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
+ if (do_h_free) {
+ /* Close the sub-crqs */
+ do {
+ rc = plpar_hcall_norets(H_FREE_SUB_CRQ,
+ adapter->vdev->unit_address,
+ scrq->crq_num);
+ } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
- if (rc) {
- netdev_err(adapter->netdev,
- "Failed to release sub-CRQ %16lx, rc = %ld\n",
- scrq->crq_num, rc);
+ if (rc) {
+ netdev_err(adapter->netdev,
+ "Failed to release sub-CRQ %16lx, rc = %ld\n",
+ scrq->crq_num, rc);
+ }
}
dma_unmap_single(dev, scrq->msg_token, 4 * PAGE_SIZE,
@@ -2380,12 +2547,12 @@ zero_page_failed:
return NULL;
}
-static void release_sub_crqs(struct ibmvnic_adapter *adapter)
+static void release_sub_crqs(struct ibmvnic_adapter *adapter, bool do_h_free)
{
int i;
if (adapter->tx_scrq) {
- for (i = 0; i < adapter->req_tx_queues; i++) {
+ for (i = 0; i < adapter->num_active_tx_scrqs; i++) {
if (!adapter->tx_scrq[i])
continue;
@@ -2398,15 +2565,17 @@ static void release_sub_crqs(struct ibmvnic_adapter *adapter)
adapter->tx_scrq[i]->irq = 0;
}
- release_sub_crq_queue(adapter, adapter->tx_scrq[i]);
+ release_sub_crq_queue(adapter, adapter->tx_scrq[i],
+ do_h_free);
}
kfree(adapter->tx_scrq);
adapter->tx_scrq = NULL;
+ adapter->num_active_tx_scrqs = 0;
}
if (adapter->rx_scrq) {
- for (i = 0; i < adapter->req_rx_queues; i++) {
+ for (i = 0; i < adapter->num_active_rx_scrqs; i++) {
if (!adapter->rx_scrq[i])
continue;
@@ -2419,11 +2588,13 @@ static void release_sub_crqs(struct ibmvnic_adapter *adapter)
adapter->rx_scrq[i]->irq = 0;
}
- release_sub_crq_queue(adapter, adapter->rx_scrq[i]);
+ release_sub_crq_queue(adapter, adapter->rx_scrq[i],
+ do_h_free);
}
kfree(adapter->rx_scrq);
adapter->rx_scrq = NULL;
+ adapter->num_active_rx_scrqs = 0;
}
}
@@ -2446,12 +2617,19 @@ static int enable_scrq_irq(struct ibmvnic_adapter *adapter,
{
struct device *dev = &adapter->vdev->dev;
unsigned long rc;
+ u64 val;
if (scrq->hw_irq > 0x100000000ULL) {
dev_err(dev, "bad hw_irq = %lx\n", scrq->hw_irq);
return 1;
}
+ val = (0xff000000) | scrq->hw_irq;
+ rc = plpar_hcall_norets(H_EOI, val);
+ if (rc)
+ dev_err(dev, "H_EOI FAILED irq 0x%llx. rc=%ld\n",
+ val, rc);
+
rc = plpar_hcall_norets(H_VIOCTL, adapter->vdev->unit_address,
H_ENABLE_VIO_INTERRUPT, scrq->hw_irq, 0, 0);
if (rc)
@@ -2464,15 +2642,17 @@ static int ibmvnic_complete_tx(struct ibmvnic_adapter *adapter,
struct ibmvnic_sub_crq_queue *scrq)
{
struct device *dev = &adapter->vdev->dev;
+ struct ibmvnic_tx_pool *tx_pool;
struct ibmvnic_tx_buff *txbuff;
union sub_crq *next;
int index;
int i, j;
- u8 first;
+ u8 *first;
restart_loop:
while (pending_scrq(adapter, scrq)) {
unsigned int pool = scrq->pool_index;
+ int num_entries = 0;
next = ibmvnic_next_scrq(adapter, scrq);
for (i = 0; i < next->tx_comp.num_comps; i++) {
@@ -2482,7 +2662,14 @@ restart_loop:
continue;
}
index = be32_to_cpu(next->tx_comp.correlators[i]);
- txbuff = &adapter->tx_pool[pool].tx_buff[index];
+ if (index & IBMVNIC_TSO_POOL_MASK) {
+ tx_pool = &adapter->tso_pool[pool];
+ index &= ~IBMVNIC_TSO_POOL_MASK;
+ } else {
+ tx_pool = &adapter->tx_pool[pool];
+ }
+
+ txbuff = &tx_pool->tx_buff[index];
for (j = 0; j < IBMVNIC_MAX_FRAGS_PER_CRQ; j++) {
if (!txbuff->data_dma[j])
@@ -2491,11 +2678,12 @@ restart_loop:
txbuff->data_dma[j] = 0;
}
/* if sub_crq was sent indirectly */
- first = txbuff->indir_arr[0].generic.first;
- if (first == IBMVNIC_CRQ_CMD) {
+ first = &txbuff->indir_arr[0].generic.first;
+ if (*first == IBMVNIC_CRQ_CMD) {
dma_unmap_single(dev, txbuff->indir_dma,
sizeof(txbuff->indir_arr),
DMA_TO_DEVICE);
+ *first = 0;
}
if (txbuff->last_frag) {
@@ -2503,22 +2691,23 @@ restart_loop:
txbuff->skb = NULL;
}
- adapter->tx_pool[pool].free_map[adapter->tx_pool[pool].
- producer_index] = index;
- adapter->tx_pool[pool].producer_index =
- (adapter->tx_pool[pool].producer_index + 1) %
- adapter->req_tx_entries_per_subcrq;
+ num_entries += txbuff->num_entries;
+
+ tx_pool->free_map[tx_pool->producer_index] = index;
+ tx_pool->producer_index =
+ (tx_pool->producer_index + 1) %
+ tx_pool->num_buffers;
}
/* remove tx_comp scrq*/
next->tx_comp.first = 0;
- if (atomic_sub_return(next->tx_comp.num_comps, &scrq->used) <=
+ if (atomic_sub_return(num_entries, &scrq->used) <=
(adapter->req_tx_entries_per_subcrq / 2) &&
__netif_subqueue_stopped(adapter->netdev,
scrq->pool_index)) {
netif_wake_subqueue(adapter->netdev, scrq->pool_index);
- netdev_info(adapter->netdev, "Started queue %d\n",
- scrq->pool_index);
+ netdev_dbg(adapter->netdev, "Started queue %d\n",
+ scrq->pool_index);
}
}
@@ -2590,7 +2779,7 @@ static int init_sub_crq_irqs(struct ibmvnic_adapter *adapter)
dev_err(dev, "Couldn't register tx irq 0x%x. rc=%d\n",
scrq->irq, rc);
irq_dispose_mapping(scrq->irq);
- goto req_rx_irq_failed;
+ goto req_tx_irq_failed;
}
}
@@ -2626,7 +2815,7 @@ req_tx_irq_failed:
free_irq(adapter->tx_scrq[j]->irq, adapter->tx_scrq[j]);
irq_dispose_mapping(adapter->rx_scrq[j]->irq);
}
- release_sub_crqs(adapter);
+ release_sub_crqs(adapter, 1);
return rc;
}
@@ -2688,6 +2877,7 @@ static int init_sub_crqs(struct ibmvnic_adapter *adapter)
for (i = 0; i < adapter->req_tx_queues; i++) {
adapter->tx_scrq[i] = allqueues[i];
adapter->tx_scrq[i]->pool_index = i;
+ adapter->num_active_tx_scrqs++;
}
adapter->rx_scrq = kcalloc(adapter->req_rx_queues,
@@ -2698,6 +2888,7 @@ static int init_sub_crqs(struct ibmvnic_adapter *adapter)
for (i = 0; i < adapter->req_rx_queues; i++) {
adapter->rx_scrq[i] = allqueues[i + adapter->req_tx_queues];
adapter->rx_scrq[i]->scrq_num = i;
+ adapter->num_active_rx_scrqs++;
}
kfree(allqueues);
@@ -2708,7 +2899,7 @@ rx_failed:
adapter->tx_scrq = NULL;
tx_failed:
for (i = 0; i < registered_queues; i++)
- release_sub_crq_queue(adapter, allqueues[i]);
+ release_sub_crq_queue(adapter, allqueues[i], 1);
kfree(allqueues);
return -1;
}
@@ -3002,7 +3193,7 @@ static int send_version_xchg(struct ibmvnic_adapter *adapter)
struct vnic_login_client_data {
u8 type;
__be16 len;
- char name;
+ char name[];
} __packed;
static int vnic_client_data_len(struct ibmvnic_adapter *adapter)
@@ -3031,24 +3222,24 @@ static void vnic_add_client_data(struct ibmvnic_adapter *adapter,
vlcd->type = 1;
len = strlen(os_name) + 1;
vlcd->len = cpu_to_be16(len);
- strncpy(&vlcd->name, os_name, len);
- vlcd = (struct vnic_login_client_data *)((char *)&vlcd->name + len);
+ strncpy(vlcd->name, os_name, len);
+ vlcd = (struct vnic_login_client_data *)(vlcd->name + len);
/* Type 2 - LPAR name */
vlcd->type = 2;
len = strlen(utsname()->nodename) + 1;
vlcd->len = cpu_to_be16(len);
- strncpy(&vlcd->name, utsname()->nodename, len);
- vlcd = (struct vnic_login_client_data *)((char *)&vlcd->name + len);
+ strncpy(vlcd->name, utsname()->nodename, len);
+ vlcd = (struct vnic_login_client_data *)(vlcd->name + len);
/* Type 3 - device name */
vlcd->type = 3;
len = strlen(adapter->netdev->name) + 1;
vlcd->len = cpu_to_be16(len);
- strncpy(&vlcd->name, adapter->netdev->name, len);
+ strncpy(vlcd->name, adapter->netdev->name, len);
}
-static void send_login(struct ibmvnic_adapter *adapter)
+static int send_login(struct ibmvnic_adapter *adapter)
{
struct ibmvnic_login_rsp_buffer *login_rsp_buffer;
struct ibmvnic_login_buffer *login_buffer;
@@ -3064,6 +3255,12 @@ static void send_login(struct ibmvnic_adapter *adapter)
struct vnic_login_client_data *vlcd;
int i;
+ if (!adapter->tx_scrq || !adapter->rx_scrq) {
+ netdev_err(adapter->netdev,
+ "RX or TX queues are not allocated, device login failed\n");
+ return -1;
+ }
+
release_login_rsp_buffer(adapter);
client_data_len = vnic_client_data_len(adapter);
@@ -3161,7 +3358,7 @@ static void send_login(struct ibmvnic_adapter *adapter)
crq.login.len = cpu_to_be32(buffer_size);
ibmvnic_send_crq(adapter, &crq);
- return;
+ return 0;
buf_rsp_map_failed:
kfree(login_rsp_buffer);
@@ -3170,7 +3367,7 @@ buf_rsp_alloc_failed:
buf_map_failed:
kfree(login_buffer);
buf_alloc_failed:
- return;
+ return -1;
}
static void send_request_map(struct ibmvnic_adapter *adapter, dma_addr_t addr,
@@ -3759,16 +3956,16 @@ static int handle_login_rsp(union ibmvnic_crq *login_rsp_crq,
int i;
dma_unmap_single(dev, adapter->login_buf_token, adapter->login_buf_sz,
- DMA_BIDIRECTIONAL);
+ DMA_TO_DEVICE);
dma_unmap_single(dev, adapter->login_rsp_buf_token,
- adapter->login_rsp_buf_sz, DMA_BIDIRECTIONAL);
+ adapter->login_rsp_buf_sz, DMA_FROM_DEVICE);
/* If the number of queues requested can't be allocated by the
* server, the login response will return with code 1. We will need
* to resend the login buffer with fewer queues requested.
*/
if (login_rsp_crq->generic.rc.code) {
- adapter->renegotiate = true;
+ adapter->init_done_rc = login_rsp_crq->generic.rc.code;
complete(&adapter->init_done);
return 0;
}
@@ -4021,7 +4218,9 @@ static void ibmvnic_handle_crq(union ibmvnic_crq *crq,
case IBMVNIC_CRQ_INIT:
dev_info(dev, "Partner initialized\n");
adapter->from_passive_init = true;
+ adapter->failover_pending = false;
complete(&adapter->init_done);
+ ibmvnic_reset(adapter, VNIC_RESET_FAILOVER);
break;
case IBMVNIC_CRQ_INIT_COMPLETE:
dev_info(dev, "Partner initialization complete\n");
@@ -4038,7 +4237,7 @@ static void ibmvnic_handle_crq(union ibmvnic_crq *crq,
ibmvnic_reset(adapter, VNIC_RESET_MOBILITY);
} else if (gen_crq->cmd == IBMVNIC_DEVICE_FAILOVER) {
dev_info(dev, "Backing device failover detected\n");
- ibmvnic_reset(adapter, VNIC_RESET_FAILOVER);
+ adapter->failover_pending = true;
} else {
/* The adapter lost the connection */
dev_err(dev, "Virtual Adapter failed (rc=%d)\n",
@@ -4335,23 +4534,14 @@ static int ibmvnic_init(struct ibmvnic_adapter *adapter)
{
struct device *dev = &adapter->vdev->dev;
unsigned long timeout = msecs_to_jiffies(30000);
+ u64 old_num_rx_queues, old_num_tx_queues;
int rc;
- if (adapter->resetting && !adapter->wait_for_reset) {
- rc = ibmvnic_reset_crq(adapter);
- if (!rc)
- rc = vio_enable_interrupts(adapter->vdev);
- } else {
- rc = init_crq_queue(adapter);
- }
-
- if (rc) {
- dev_err(dev, "Couldn't initialize crq. rc=%d\n", rc);
- return rc;
- }
-
adapter->from_passive_init = false;
+ old_num_rx_queues = adapter->req_rx_queues;
+ old_num_tx_queues = adapter->req_tx_queues;
+
init_completion(&adapter->init_done);
adapter->init_done_rc = 0;
ibmvnic_send_crq_init(adapter);
@@ -4371,10 +4561,19 @@ static int ibmvnic_init(struct ibmvnic_adapter *adapter)
return -1;
}
- if (adapter->resetting && !adapter->wait_for_reset)
- rc = reset_sub_crq_queues(adapter);
- else
+ if (adapter->resetting && !adapter->wait_for_reset &&
+ adapter->reset_reason != VNIC_RESET_MOBILITY) {
+ if (adapter->req_rx_queues != old_num_rx_queues ||
+ adapter->req_tx_queues != old_num_tx_queues) {
+ release_sub_crqs(adapter, 0);
+ rc = init_sub_crqs(adapter);
+ } else {
+ rc = reset_sub_crq_queues(adapter);
+ }
+ } else {
rc = init_sub_crqs(adapter);
+ }
+
if (rc) {
dev_err(dev, "Initialization of sub crqs failed\n");
release_crq_queue(adapter);
@@ -4387,6 +4586,14 @@ static int ibmvnic_init(struct ibmvnic_adapter *adapter)
release_crq_queue(adapter);
}
+ rc = init_stats_buffers(adapter);
+ if (rc)
+ return rc;
+
+ rc = init_stats_token(adapter);
+ if (rc)
+ return rc;
+
return rc;
}
@@ -4443,6 +4650,13 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id)
adapter->mac_change_pending = false;
do {
+ rc = init_crq_queue(adapter);
+ if (rc) {
+ dev_err(&dev->dev, "Couldn't initialize crq. rc=%d\n",
+ rc);
+ goto ibmvnic_init_fail;
+ }
+
rc = ibmvnic_init(adapter);
if (rc && rc != EAGAIN)
goto ibmvnic_init_fail;
@@ -4474,7 +4688,7 @@ ibmvnic_register_fail:
device_remove_file(&dev->dev, &dev_attr_failover);
ibmvnic_init_fail:
- release_sub_crqs(adapter);
+ release_sub_crqs(adapter, 1);
release_crq_queue(adapter);
free_netdev(netdev);
@@ -4491,9 +4705,12 @@ static int ibmvnic_remove(struct vio_dev *dev)
mutex_lock(&adapter->reset_lock);
release_resources(adapter);
- release_sub_crqs(adapter);
+ release_sub_crqs(adapter, 1);
release_crq_queue(adapter);
+ release_stats_token(adapter);
+ release_stats_buffers(adapter);
+
adapter->state = VNIC_REMOVED;
mutex_unlock(&adapter->reset_lock);
diff --git a/drivers/net/ethernet/ibm/ibmvnic.h b/drivers/net/ethernet/ibm/ibmvnic.h
index fe21a6e2ddae..22391e8805f6 100644
--- a/drivers/net/ethernet/ibm/ibmvnic.h
+++ b/drivers/net/ethernet/ibm/ibmvnic.h
@@ -43,6 +43,7 @@
#define IBMVNIC_TSO_BUF_SZ 65536
#define IBMVNIC_TSO_BUFS 64
+#define IBMVNIC_TSO_POOL_MASK 0x80000000
#define IBMVNIC_MAX_LTB_SIZE ((1 << (MAX_ORDER - 1)) * PAGE_SIZE)
#define IBMVNIC_BUFFER_HLEN 500
@@ -909,6 +910,7 @@ struct ibmvnic_tx_buff {
union sub_crq indir_arr[6];
u8 hdr_data[140];
dma_addr_t indir_dma;
+ int num_entries;
};
struct ibmvnic_tx_pool {
@@ -916,11 +918,9 @@ struct ibmvnic_tx_pool {
int *free_map;
int consumer_index;
int producer_index;
- wait_queue_head_t ibmvnic_tx_comp_q;
- struct task_struct *work_thread;
struct ibmvnic_long_term_buff long_term_buff;
- struct ibmvnic_long_term_buff tso_ltb;
- int tso_index;
+ int num_buffers;
+ int buf_size;
};
struct ibmvnic_rx_buff {
@@ -1035,7 +1035,6 @@ struct ibmvnic_adapter {
struct ibmvnic_sub_crq_queue **tx_scrq;
struct ibmvnic_sub_crq_queue **rx_scrq;
- bool renegotiate;
/* rx structs */
struct napi_struct *napi;
@@ -1043,6 +1042,7 @@ struct ibmvnic_adapter {
u64 promisc;
struct ibmvnic_tx_pool *tx_pool;
+ struct ibmvnic_tx_pool *tso_pool;
struct completion init_done;
int init_done_rc;
@@ -1091,8 +1091,11 @@ struct ibmvnic_adapter {
u64 opt_rxba_entries_per_subcrq;
__be64 tx_rx_desc_req;
u8 map_id;
- u64 num_active_rx_pools;
- u64 num_active_tx_pools;
+ u32 num_active_rx_scrqs;
+ u32 num_active_rx_pools;
+ u32 num_active_rx_napi;
+ u32 num_active_tx_scrqs;
+ u32 num_active_tx_pools;
struct tasklet_struct tasklet;
enum vnic_state state;
@@ -1104,6 +1107,7 @@ struct ibmvnic_adapter {
bool napi_enabled, from_passive_init;
bool mac_change_pending;
+ bool failover_pending;
struct ibmvnic_tunables desired;
struct ibmvnic_tunables fallback;
diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig
index 1feb54b6d92e..14d287bed33c 100644
--- a/drivers/net/ethernet/intel/Kconfig
+++ b/drivers/net/ethernet/intel/Kconfig
@@ -251,6 +251,20 @@ config I40EVF
will be called i40evf. MSI-X interrupt support is required
for this driver to work correctly.
+config ICE
+ tristate "Intel(R) Ethernet Connection E800 Series Support"
+ default n
+ depends on PCI_MSI
+ ---help---
+ This driver supports Intel(R) Ethernet Connection E800 Series of
+ devices. For more information on how to identify your adapter, go
+ to the Adapter & Driver ID Guide that can be located at:
+
+ <http://support.intel.com>
+
+ To compile this driver as a module, choose M here. The module
+ will be called ice.
+
config FM10K
tristate "Intel(R) FM10000 Ethernet Switch Host Interface Support"
default n
diff --git a/drivers/net/ethernet/intel/Makefile b/drivers/net/ethernet/intel/Makefile
index 90af7757a885..807a4f8c7e4e 100644
--- a/drivers/net/ethernet/intel/Makefile
+++ b/drivers/net/ethernet/intel/Makefile
@@ -14,3 +14,4 @@ obj-$(CONFIG_I40E) += i40e/
obj-$(CONFIG_IXGB) += ixgb/
obj-$(CONFIG_I40EVF) += i40evf/
obj-$(CONFIG_FM10K) += fm10k/
+obj-$(CONFIG_ICE) += ice/
diff --git a/drivers/net/ethernet/intel/e100.c b/drivers/net/ethernet/intel/e100.c
index 29486478836e..41ad56edfb96 100644
--- a/drivers/net/ethernet/intel/e100.c
+++ b/drivers/net/ethernet/intel/e100.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
Intel PRO/100 Linux driver
diff --git a/drivers/net/ethernet/intel/e1000/Makefile b/drivers/net/ethernet/intel/e1000/Makefile
index 4a6ab1522451..c7caadd3c8af 100644
--- a/drivers/net/ethernet/intel/e1000/Makefile
+++ b/drivers/net/ethernet/intel/e1000/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
################################################################################
#
# Intel PRO/1000 Linux driver
diff --git a/drivers/net/ethernet/intel/e1000/e1000.h b/drivers/net/ethernet/intel/e1000/e1000.h
index 8fd2458060a0..3a0feea2df54 100644
--- a/drivers/net/ethernet/intel/e1000/e1000.h
+++ b/drivers/net/ethernet/intel/e1000/e1000.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
Intel PRO/1000 Linux driver
diff --git a/drivers/net/ethernet/intel/e1000/e1000_ethtool.c b/drivers/net/ethernet/intel/e1000/e1000_ethtool.c
index dc71e87c3260..3e80ca170dd7 100644
--- a/drivers/net/ethernet/intel/e1000/e1000_ethtool.c
+++ b/drivers/net/ethernet/intel/e1000/e1000_ethtool.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
* Intel PRO/1000 Linux driver
* Copyright(c) 1999 - 2006 Intel Corporation.
diff --git a/drivers/net/ethernet/intel/e1000/e1000_hw.c b/drivers/net/ethernet/intel/e1000/e1000_hw.c
index 3bac9df1c099..6e7e923d57bf 100644
--- a/drivers/net/ethernet/intel/e1000/e1000_hw.c
+++ b/drivers/net/ethernet/intel/e1000/e1000_hw.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
*
Intel PRO/1000 Linux driver
diff --git a/drivers/net/ethernet/intel/e1000/e1000_hw.h b/drivers/net/ethernet/intel/e1000/e1000_hw.h
index 5cf7268cc4e1..f09c569ec19b 100644
--- a/drivers/net/ethernet/intel/e1000/e1000_hw.h
+++ b/drivers/net/ethernet/intel/e1000/e1000_hw.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
Intel PRO/1000 Linux driver
diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c
index 3dd4aeb2706d..d5eb19b86a0a 100644
--- a/drivers/net/ethernet/intel/e1000/e1000_main.c
+++ b/drivers/net/ethernet/intel/e1000/e1000_main.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
Intel PRO/1000 Linux driver
diff --git a/drivers/net/ethernet/intel/e1000/e1000_osdep.h b/drivers/net/ethernet/intel/e1000/e1000_osdep.h
index 33e7c45a4fe4..ae0559b8b011 100644
--- a/drivers/net/ethernet/intel/e1000/e1000_osdep.h
+++ b/drivers/net/ethernet/intel/e1000/e1000_osdep.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
Intel PRO/1000 Linux driver
diff --git a/drivers/net/ethernet/intel/e1000/e1000_param.c b/drivers/net/ethernet/intel/e1000/e1000_param.c
index c9cde352b1c8..345f23927bcc 100644
--- a/drivers/net/ethernet/intel/e1000/e1000_param.c
+++ b/drivers/net/ethernet/intel/e1000/e1000_param.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
Intel PRO/1000 Linux driver
diff --git a/drivers/net/ethernet/intel/e1000e/80003es2lan.c b/drivers/net/ethernet/intel/e1000e/80003es2lan.c
index cd391376036c..953e99df420c 100644
--- a/drivers/net/ethernet/intel/e1000e/80003es2lan.c
+++ b/drivers/net/ethernet/intel/e1000e/80003es2lan.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel PRO/1000 Linux driver
* Copyright(c) 1999 - 2015 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/e1000e/80003es2lan.h b/drivers/net/ethernet/intel/e1000e/80003es2lan.h
index a2162e11673e..ee6d1256fda4 100644
--- a/drivers/net/ethernet/intel/e1000e/80003es2lan.h
+++ b/drivers/net/ethernet/intel/e1000e/80003es2lan.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Intel PRO/1000 Linux driver
* Copyright(c) 1999 - 2015 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/e1000e/82571.c b/drivers/net/ethernet/intel/e1000e/82571.c
index 6b03c8553e59..924f2c8dfa6c 100644
--- a/drivers/net/ethernet/intel/e1000e/82571.c
+++ b/drivers/net/ethernet/intel/e1000e/82571.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel PRO/1000 Linux driver
* Copyright(c) 1999 - 2015 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/e1000e/82571.h b/drivers/net/ethernet/intel/e1000e/82571.h
index abc6a9abff98..9a24c645f726 100644
--- a/drivers/net/ethernet/intel/e1000e/82571.h
+++ b/drivers/net/ethernet/intel/e1000e/82571.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Intel PRO/1000 Linux driver
* Copyright(c) 1999 - 2015 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/e1000e/Makefile b/drivers/net/ethernet/intel/e1000e/Makefile
index 106de493373c..24e391a4ac68 100644
--- a/drivers/net/ethernet/intel/e1000e/Makefile
+++ b/drivers/net/ethernet/intel/e1000e/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
################################################################################
#
# Intel PRO/1000 Linux driver
diff --git a/drivers/net/ethernet/intel/e1000e/defines.h b/drivers/net/ethernet/intel/e1000e/defines.h
index 824fd44e25f0..22883015a695 100644
--- a/drivers/net/ethernet/intel/e1000e/defines.h
+++ b/drivers/net/ethernet/intel/e1000e/defines.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Intel PRO/1000 Linux driver
* Copyright(c) 1999 - 2015 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/e1000e/e1000.h b/drivers/net/ethernet/intel/e1000e/e1000.h
index 2311b31bdcac..da88555ba1fd 100644
--- a/drivers/net/ethernet/intel/e1000e/e1000.h
+++ b/drivers/net/ethernet/intel/e1000e/e1000.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Intel PRO/1000 Linux driver
* Copyright(c) 1999 - 2015 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/e1000e/ethtool.c b/drivers/net/ethernet/intel/e1000e/ethtool.c
index 003cbd605799..64dc0c11147f 100644
--- a/drivers/net/ethernet/intel/e1000e/ethtool.c
+++ b/drivers/net/ethernet/intel/e1000e/ethtool.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel PRO/1000 Linux driver
* Copyright(c) 1999 - 2015 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/e1000e/hw.h b/drivers/net/ethernet/intel/e1000e/hw.h
index d803b1a12349..21802396bed6 100644
--- a/drivers/net/ethernet/intel/e1000e/hw.h
+++ b/drivers/net/ethernet/intel/e1000e/hw.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Intel PRO/1000 Linux driver
* Copyright(c) 1999 - 2015 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c
index 1dddfb7b2de6..1551d6ce5341 100644
--- a/drivers/net/ethernet/intel/e1000e/ich8lan.c
+++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel PRO/1000 Linux driver
* Copyright(c) 1999 - 2015 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.h b/drivers/net/ethernet/intel/e1000e/ich8lan.h
index 00a36df02a3f..3c4f82c21084 100644
--- a/drivers/net/ethernet/intel/e1000e/ich8lan.h
+++ b/drivers/net/ethernet/intel/e1000e/ich8lan.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Intel PRO/1000 Linux driver
* Copyright(c) 1999 - 2015 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/e1000e/mac.c b/drivers/net/ethernet/intel/e1000e/mac.c
index 5bdc3a2d4fd7..b293464a9f27 100644
--- a/drivers/net/ethernet/intel/e1000e/mac.c
+++ b/drivers/net/ethernet/intel/e1000e/mac.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel PRO/1000 Linux driver
* Copyright(c) 1999 - 2015 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/e1000e/mac.h b/drivers/net/ethernet/intel/e1000e/mac.h
index 8284618af9ff..cb0abf6c76a5 100644
--- a/drivers/net/ethernet/intel/e1000e/mac.h
+++ b/drivers/net/ethernet/intel/e1000e/mac.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Intel PRO/1000 Linux driver
* Copyright(c) 1999 - 2015 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/e1000e/manage.c b/drivers/net/ethernet/intel/e1000e/manage.c
index cc9b3befc2bc..e027660aeb92 100644
--- a/drivers/net/ethernet/intel/e1000e/manage.c
+++ b/drivers/net/ethernet/intel/e1000e/manage.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel PRO/1000 Linux driver
* Copyright(c) 1999 - 2015 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/e1000e/manage.h b/drivers/net/ethernet/intel/e1000e/manage.h
index 0b9ea5952b07..3268f2e58593 100644
--- a/drivers/net/ethernet/intel/e1000e/manage.h
+++ b/drivers/net/ethernet/intel/e1000e/manage.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Intel PRO/1000 Linux driver
* Copyright(c) 1999 - 2015 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c
index dc853b0863af..ec4a9759a6f2 100644
--- a/drivers/net/ethernet/intel/e1000e/netdev.c
+++ b/drivers/net/ethernet/intel/e1000e/netdev.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel PRO/1000 Linux driver
* Copyright(c) 1999 - 2015 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/e1000e/nvm.c b/drivers/net/ethernet/intel/e1000e/nvm.c
index 2efd80dfd88e..68949bb41b7b 100644
--- a/drivers/net/ethernet/intel/e1000e/nvm.c
+++ b/drivers/net/ethernet/intel/e1000e/nvm.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel PRO/1000 Linux driver
* Copyright(c) 1999 - 2015 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/e1000e/nvm.h b/drivers/net/ethernet/intel/e1000e/nvm.h
index 5d46967e0d1f..8e082028be7d 100644
--- a/drivers/net/ethernet/intel/e1000e/nvm.h
+++ b/drivers/net/ethernet/intel/e1000e/nvm.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Intel PRO/1000 Linux driver
* Copyright(c) 1999 - 2015 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/e1000e/param.c b/drivers/net/ethernet/intel/e1000e/param.c
index 47da51864543..2def33eba9e6 100644
--- a/drivers/net/ethernet/intel/e1000e/param.c
+++ b/drivers/net/ethernet/intel/e1000e/param.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel PRO/1000 Linux driver
* Copyright(c) 1999 - 2015 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/e1000e/phy.c b/drivers/net/ethernet/intel/e1000e/phy.c
index 86ff0969efb6..b8226ed0e338 100644
--- a/drivers/net/ethernet/intel/e1000e/phy.c
+++ b/drivers/net/ethernet/intel/e1000e/phy.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel PRO/1000 Linux driver
* Copyright(c) 1999 - 2015 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/e1000e/phy.h b/drivers/net/ethernet/intel/e1000e/phy.h
index 3027f63ee793..d4180b5e9196 100644
--- a/drivers/net/ethernet/intel/e1000e/phy.h
+++ b/drivers/net/ethernet/intel/e1000e/phy.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Intel PRO/1000 Linux driver
* Copyright(c) 1999 - 2015 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/e1000e/ptp.c b/drivers/net/ethernet/intel/e1000e/ptp.c
index b366885487a8..f941e5085f44 100644
--- a/drivers/net/ethernet/intel/e1000e/ptp.c
+++ b/drivers/net/ethernet/intel/e1000e/ptp.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel PRO/1000 Linux driver
* Copyright(c) 1999 - 2015 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/e1000e/regs.h b/drivers/net/ethernet/intel/e1000e/regs.h
index 0cb4d365e5ad..16afc3c2a986 100644
--- a/drivers/net/ethernet/intel/e1000e/regs.h
+++ b/drivers/net/ethernet/intel/e1000e/regs.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Intel PRO/1000 Linux driver
* Copyright(c) 1999 - 2015 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/fm10k/Makefile b/drivers/net/ethernet/intel/fm10k/Makefile
index cac645329cea..93277cb99cb7 100644
--- a/drivers/net/ethernet/intel/fm10k/Makefile
+++ b/drivers/net/ethernet/intel/fm10k/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
################################################################################
#
# Intel(R) Ethernet Switch Host Interface Driver
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k.h b/drivers/net/ethernet/intel/fm10k/fm10k.h
index 46973fb234c5..a9cdf763c59d 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k.h
+++ b/drivers/net/ethernet/intel/fm10k/fm10k.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Intel(R) Ethernet Switch Host Interface Driver
* Copyright(c) 2013 - 2017 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_common.c b/drivers/net/ethernet/intel/fm10k/fm10k_common.c
index 736a9f087bc9..e303d88720ef 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_common.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_common.c
@@ -1,5 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel(R) Ethernet Switch Host Interface Driver
- * Copyright(c) 2013 - 2017 Intel Corporation.
+ * Copyright(c) 2013 - 2018 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -262,6 +263,7 @@ s32 fm10k_stop_hw_generic(struct fm10k_hw *hw)
* fm10k_read_hw_stats_32b - Reads value of 32-bit registers
* @hw: pointer to the hardware structure
* @addr: address of register containing a 32-bit value
+ * @stat: pointer to structure holding hw stat information
*
* Function reads the content of the register and returns the delta
* between the base and the current value.
@@ -281,6 +283,7 @@ u32 fm10k_read_hw_stats_32b(struct fm10k_hw *hw, u32 addr,
* fm10k_read_hw_stats_48b - Reads value of 48-bit registers
* @hw: pointer to the hardware structure
* @addr: address of register containing the lower 32-bit value
+ * @stat: pointer to structure holding hw stat information
*
* Function reads the content of 2 registers, combined to represent a 48-bit
* statistical value. Extra processing is required to handle overflowing.
@@ -461,7 +464,6 @@ void fm10k_update_hw_stats_q(struct fm10k_hw *hw, struct fm10k_hw_stats_q *q,
/**
* fm10k_unbind_hw_stats_q - Unbind the queue counters from their queues
- * @hw: pointer to the hardware structure
* @q: pointer to the ring of hardware statistics queue
* @idx: index pointing to the start of the ring iteration
* @count: number of queues to iterate over
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_common.h b/drivers/net/ethernet/intel/fm10k/fm10k_common.h
index d51f9c7a47ff..2bdb24d2ca9d 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_common.h
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_common.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Intel(R) Ethernet Switch Host Interface Driver
* Copyright(c) 2013 - 2016 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_dcbnl.c b/drivers/net/ethernet/intel/fm10k/fm10k_dcbnl.c
index db4bd8bf9722..c4f733452ef2 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_dcbnl.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_dcbnl.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel(R) Ethernet Switch Host Interface Driver
* Copyright(c) 2013 - 2016 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c b/drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c
index 14df09e2d964..43e8d839831f 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel(R) Ethernet Switch Host Interface Driver
* Copyright(c) 2013 - 2016 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c
index c7234f35f8ff..28b6b4e56487 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel(R) Ethernet Switch Host Interface Driver
* Copyright(c) 2013 - 2017 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_iov.c b/drivers/net/ethernet/intel/fm10k/fm10k_iov.c
index 760cfa52d02c..30395f5e5e87 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_iov.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_iov.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel(R) Ethernet Switch Host Interface Driver
* Copyright(c) 2013 - 2017 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_main.c b/drivers/net/ethernet/intel/fm10k/fm10k_main.c
index 8e12aae065d8..df8607097e4a 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_main.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_main.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel(R) Ethernet Switch Host Interface Driver
* Copyright(c) 2013 - 2017 Intel Corporation.
*
@@ -28,13 +29,13 @@
#include "fm10k.h"
-#define DRV_VERSION "0.22.1-k"
+#define DRV_VERSION "0.23.4-k"
#define DRV_SUMMARY "Intel(R) Ethernet Switch Host Interface Driver"
const char fm10k_driver_version[] = DRV_VERSION;
char fm10k_driver_name[] = "fm10k";
static const char fm10k_driver_string[] = DRV_SUMMARY;
static const char fm10k_copyright[] =
- "Copyright(c) 2013 - 2017 Intel Corporation.";
+ "Copyright(c) 2013 - 2018 Intel Corporation.";
MODULE_AUTHOR("Intel Corporation, <linux.nics@intel.com>");
MODULE_DESCRIPTION(DRV_SUMMARY);
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c b/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c
index 244d3ad58ca7..c01bf30a0c9e 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel(R) Ethernet Switch Host Interface Driver
* Copyright(c) 2013 - 2017 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_mbx.h b/drivers/net/ethernet/intel/fm10k/fm10k_mbx.h
index 35c1dbad1330..007e1dfa9b7a 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_mbx.h
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_mbx.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Intel(R) Ethernet Switch Host Interface Driver
* Copyright(c) 2013 - 2016 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c
index a38ae5c54da3..45793491d4ba 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c
@@ -1,5 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel(R) Ethernet Switch Host Interface Driver
- * Copyright(c) 2013 - 2017 Intel Corporation.
+ * Copyright(c) 2013 - 2018 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -486,7 +487,7 @@ static void fm10k_insert_tunnel_port(struct list_head *ports,
/**
* fm10k_udp_tunnel_add
- * @netdev: network interface device structure
+ * @dev: network interface device structure
* @ti: Tunnel endpoint information
*
* This function is called when a new UDP tunnel port has been added.
@@ -518,8 +519,8 @@ static void fm10k_udp_tunnel_add(struct net_device *dev,
/**
* fm10k_udp_tunnel_del
- * @netdev: network interface device structure
- * @ti: Tunnel endpoint information
+ * @dev: network interface device structure
+ * @ti: Tunnel end point information
*
* This function is called when a new UDP tunnel port is deleted. The freed
* port will be removed from the list, then we reprogram the offloaded port
@@ -803,7 +804,7 @@ int fm10k_queue_vlan_request(struct fm10k_intfc *interface,
* @glort: the target glort for this update
* @addr: the address to update
* @vid: the vid to update
- * @sync: whether to add or remove
+ * @set: whether to add or remove
*
* This function queues up a MAC request for sending to the switch manager.
* A separate thread monitors the queue and sends updates to the switch
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c
index a434fecfdfeb..c4a2b688b38b 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c
@@ -1,5 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel(R) Ethernet Switch Host Interface Driver
- * Copyright(c) 2013 - 2017 Intel Corporation.
+ * Copyright(c) 2013 - 2018 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -29,7 +30,7 @@ static const struct fm10k_info *fm10k_info_tbl[] = {
[fm10k_device_vf] = &fm10k_vf_info,
};
-/**
+/*
* fm10k_pci_tbl - PCI Device ID Table
*
* Wildcard entries (PCI_ANY_ID) should come last
@@ -211,7 +212,7 @@ static void fm10k_start_service_event(struct fm10k_intfc *interface)
/**
* fm10k_service_timer - Timer Call-back
- * @data: pointer to interface cast into an unsigned long
+ * @t: pointer to timer data
**/
static void fm10k_service_timer(struct timer_list *t)
{
@@ -649,7 +650,7 @@ void fm10k_update_stats(struct fm10k_intfc *interface)
/**
* fm10k_watchdog_flush_tx - flush queues on host not ready
- * @interface - pointer to the device interface structure
+ * @interface: pointer to the device interface structure
**/
static void fm10k_watchdog_flush_tx(struct fm10k_intfc *interface)
{
@@ -679,7 +680,7 @@ static void fm10k_watchdog_flush_tx(struct fm10k_intfc *interface)
/**
* fm10k_watchdog_subtask - check and bring link up
- * @interface - pointer to the device interface structure
+ * @interface: pointer to the device interface structure
**/
static void fm10k_watchdog_subtask(struct fm10k_intfc *interface)
{
@@ -703,7 +704,7 @@ static void fm10k_watchdog_subtask(struct fm10k_intfc *interface)
/**
* fm10k_check_hang_subtask - check for hung queues and dropped interrupts
- * @interface - pointer to the device interface structure
+ * @interface: pointer to the device interface structure
*
* This function serves two purposes. First it strobes the interrupt lines
* in order to make certain interrupts are occurring. Secondly it sets the
@@ -1995,6 +1996,7 @@ skip_tx_dma_drain:
/**
* fm10k_sw_init - Initialize general software structures
* @interface: host interface private structure to initialize
+ * @ent: PCI device ID entry
*
* fm10k_sw_init initializes the interface private data structure.
* Fields are initialized based on PCI device information and
@@ -2120,91 +2122,6 @@ static int fm10k_sw_init(struct fm10k_intfc *interface,
return 0;
}
-static void fm10k_slot_warn(struct fm10k_intfc *interface)
-{
- enum pcie_link_width width = PCIE_LNK_WIDTH_UNKNOWN;
- enum pci_bus_speed speed = PCI_SPEED_UNKNOWN;
- struct fm10k_hw *hw = &interface->hw;
- int max_gts = 0, expected_gts = 0;
-
- if (pcie_get_minimum_link(interface->pdev, &speed, &width) ||
- speed == PCI_SPEED_UNKNOWN || width == PCIE_LNK_WIDTH_UNKNOWN) {
- dev_warn(&interface->pdev->dev,
- "Unable to determine PCI Express bandwidth.\n");
- return;
- }
-
- switch (speed) {
- case PCIE_SPEED_2_5GT:
- /* 8b/10b encoding reduces max throughput by 20% */
- max_gts = 2 * width;
- break;
- case PCIE_SPEED_5_0GT:
- /* 8b/10b encoding reduces max throughput by 20% */
- max_gts = 4 * width;
- break;
- case PCIE_SPEED_8_0GT:
- /* 128b/130b encoding has less than 2% impact on throughput */
- max_gts = 8 * width;
- break;
- default:
- dev_warn(&interface->pdev->dev,
- "Unable to determine PCI Express bandwidth.\n");
- return;
- }
-
- dev_info(&interface->pdev->dev,
- "PCI Express bandwidth of %dGT/s available\n",
- max_gts);
- dev_info(&interface->pdev->dev,
- "(Speed:%s, Width: x%d, Encoding Loss:%s, Payload:%s)\n",
- (speed == PCIE_SPEED_8_0GT ? "8.0GT/s" :
- speed == PCIE_SPEED_5_0GT ? "5.0GT/s" :
- speed == PCIE_SPEED_2_5GT ? "2.5GT/s" :
- "Unknown"),
- hw->bus.width,
- (speed == PCIE_SPEED_2_5GT ? "20%" :
- speed == PCIE_SPEED_5_0GT ? "20%" :
- speed == PCIE_SPEED_8_0GT ? "<2%" :
- "Unknown"),
- (hw->bus.payload == fm10k_bus_payload_128 ? "128B" :
- hw->bus.payload == fm10k_bus_payload_256 ? "256B" :
- hw->bus.payload == fm10k_bus_payload_512 ? "512B" :
- "Unknown"));
-
- switch (hw->bus_caps.speed) {
- case fm10k_bus_speed_2500:
- /* 8b/10b encoding reduces max throughput by 20% */
- expected_gts = 2 * hw->bus_caps.width;
- break;
- case fm10k_bus_speed_5000:
- /* 8b/10b encoding reduces max throughput by 20% */
- expected_gts = 4 * hw->bus_caps.width;
- break;
- case fm10k_bus_speed_8000:
- /* 128b/130b encoding has less than 2% impact on throughput */
- expected_gts = 8 * hw->bus_caps.width;
- break;
- default:
- dev_warn(&interface->pdev->dev,
- "Unable to determine expected PCI Express bandwidth.\n");
- return;
- }
-
- if (max_gts >= expected_gts)
- return;
-
- dev_warn(&interface->pdev->dev,
- "This device requires %dGT/s of bandwidth for optimal performance.\n",
- expected_gts);
- dev_warn(&interface->pdev->dev,
- "A %sslot with x%d lanes is suggested.\n",
- (hw->bus_caps.speed == fm10k_bus_speed_2500 ? "2.5GT/s " :
- hw->bus_caps.speed == fm10k_bus_speed_5000 ? "5.0GT/s " :
- hw->bus_caps.speed == fm10k_bus_speed_8000 ? "8.0GT/s " : ""),
- hw->bus_caps.width);
-}
-
/**
* fm10k_probe - Device Initialization Routine
* @pdev: PCI device information struct
@@ -2326,7 +2243,7 @@ static int fm10k_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
mod_timer(&interface->service_timer, (HZ * 2) + jiffies);
/* print warning for non-optimal configurations */
- fm10k_slot_warn(interface);
+ pcie_print_link_status(interface->pdev);
/* report MAC address for logging */
dev_info(&pdev->dev, "%pM\n", netdev->dev_addr);
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c
index d6406fc31ffb..7ba54c534f8c 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c
@@ -1,5 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel(R) Ethernet Switch Host Interface Driver
- * Copyright(c) 2013 - 2017 Intel Corporation.
+ * Copyright(c) 2013 - 2018 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -1180,7 +1181,7 @@ s32 fm10k_iov_msg_msix_pf(struct fm10k_hw *hw, u32 **results,
/**
* fm10k_iov_select_vid - Select correct default VLAN ID
- * @hw: Pointer to hardware structure
+ * @vf_info: pointer to VF information structure
* @vid: VLAN ID to correct
*
* Will report an error if the VLAN ID is out of range. For VID = 0, it will
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pf.h b/drivers/net/ethernet/intel/fm10k/fm10k_pf.h
index e04d41f1a532..ae81f9a16602 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_pf.h
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_pf.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Intel(R) Ethernet Switch Host Interface Driver
* Copyright(c) 2013 - 2017 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_tlv.c b/drivers/net/ethernet/intel/fm10k/fm10k_tlv.c
index f8e87bf086b9..725ecb7abccd 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_tlv.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_tlv.c
@@ -1,5 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel(R) Ethernet Switch Host Interface Driver
- * Copyright(c) 2013 - 2016 Intel Corporation.
+ * Copyright(c) 2013 - 2018 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -120,6 +121,7 @@ static s32 fm10k_tlv_attr_get_null_string(u32 *attr, unsigned char *string)
* @msg: Pointer to message block
* @attr_id: Attribute ID
* @mac_addr: MAC address to be stored
+ * @vlan: VLAN to be stored
*
* This function will reorder a MAC address to be CPU endian and store it
* in the attribute buffer. It will return success if provided with a
@@ -155,8 +157,8 @@ s32 fm10k_tlv_attr_put_mac_vlan(u32 *msg, u16 attr_id,
/**
* fm10k_tlv_attr_get_mac_vlan - Get MAC/VLAN stored in attribute
* @attr: Pointer to attribute
- * @attr_id: Attribute ID
* @mac_addr: location of buffer to store MAC address
+ * @vlan: location of buffer to store VLAN
*
* This function pulls the MAC address back out of the attribute and will
* place it in the array pointed by by mac_addr. It will return success
@@ -549,7 +551,7 @@ static s32 fm10k_tlv_attr_parse(u32 *attr, u32 **results,
* @hw: Pointer to hardware structure
* @msg: Pointer to message
* @mbx: Pointer to mailbox information structure
- * @func: Function array containing list of message handling functions
+ * @data: Pointer to message handler data structure
*
* This function should be the first function called upon receiving a
* message. The handler will identify the message type and call the correct
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_tlv.h b/drivers/net/ethernet/intel/fm10k/fm10k_tlv.h
index a1f1027fe184..5d2ee759507e 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_tlv.h
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_tlv.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Intel(R) Ethernet Switch Host Interface Driver
* Copyright(c) 2013 - 2016 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_type.h b/drivers/net/ethernet/intel/fm10k/fm10k_type.h
index 6bb16c13d9d6..dd23af11e2c1 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_type.h
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_type.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Intel(R) Ethernet Switch Host Interface Driver
* Copyright(c) 2013 - 2016 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_vf.c b/drivers/net/ethernet/intel/fm10k/fm10k_vf.c
index 337ba65a9411..f06913630b39 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_vf.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_vf.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel(R) Ethernet Switch Host Interface Driver
* Copyright(c) 2013 - 2016 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_vf.h b/drivers/net/ethernet/intel/fm10k/fm10k_vf.h
index 2662f33c0c71..66a66b73a2f1 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_vf.h
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_vf.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Intel(R) Ethernet Switch Host Interface Driver
* Copyright(c) 2013 - 2016 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/i40e/Makefile b/drivers/net/ethernet/intel/i40e/Makefile
index 3da482c3d68d..75437768a07c 100644
--- a/drivers/net/ethernet/intel/i40e/Makefile
+++ b/drivers/net/ethernet/intel/i40e/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
################################################################################
#
# Intel Ethernet Controller XL710 Family Linux Driver
diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
index 46e9f4e0a02c..a44139c1de80 100644
--- a/drivers/net/ethernet/intel/i40e/i40e.h
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
@@ -158,9 +159,17 @@ enum i40e_state_t {
__I40E_BAD_EEPROM,
__I40E_DOWN_REQUESTED,
__I40E_FD_FLUSH_REQUESTED,
+ __I40E_FD_ATR_AUTO_DISABLED,
+ __I40E_FD_SB_AUTO_DISABLED,
__I40E_RESET_FAILED,
__I40E_PORT_SUSPENDED,
__I40E_VF_DISABLE,
+ __I40E_MACVLAN_SYNC_PENDING,
+ __I40E_UDP_FILTER_SYNC_PENDING,
+ __I40E_TEMP_LINK_POLLING,
+ __I40E_CLIENT_SERVICE_REQUESTED,
+ __I40E_CLIENT_L2_CHANGE,
+ __I40E_CLIENT_RESET,
/* This must be last as it determines the size of the BITMAP */
__I40E_STATE_SIZE__,
};
@@ -507,41 +516,34 @@ struct i40e_pf {
#define I40E_HW_STOP_FW_LLDP BIT(16)
#define I40E_HW_PORT_ID_VALID BIT(17)
#define I40E_HW_RESTART_AUTONEG BIT(18)
-
- u64 flags;
-#define I40E_FLAG_RX_CSUM_ENABLED BIT_ULL(0)
-#define I40E_FLAG_MSI_ENABLED BIT_ULL(1)
-#define I40E_FLAG_MSIX_ENABLED BIT_ULL(2)
-#define I40E_FLAG_RSS_ENABLED BIT_ULL(3)
-#define I40E_FLAG_VMDQ_ENABLED BIT_ULL(4)
-#define I40E_FLAG_FILTER_SYNC BIT_ULL(5)
-#define I40E_FLAG_SRIOV_ENABLED BIT_ULL(6)
-#define I40E_FLAG_DCB_CAPABLE BIT_ULL(7)
-#define I40E_FLAG_DCB_ENABLED BIT_ULL(8)
-#define I40E_FLAG_FD_SB_ENABLED BIT_ULL(9)
-#define I40E_FLAG_FD_ATR_ENABLED BIT_ULL(10)
-#define I40E_FLAG_FD_SB_AUTO_DISABLED BIT_ULL(11)
-#define I40E_FLAG_FD_ATR_AUTO_DISABLED BIT_ULL(12)
-#define I40E_FLAG_MFP_ENABLED BIT_ULL(13)
-#define I40E_FLAG_UDP_FILTER_SYNC BIT_ULL(14)
-#define I40E_FLAG_HW_ATR_EVICT_ENABLED BIT_ULL(15)
-#define I40E_FLAG_VEB_MODE_ENABLED BIT_ULL(16)
-#define I40E_FLAG_VEB_STATS_ENABLED BIT_ULL(17)
-#define I40E_FLAG_LINK_POLLING_ENABLED BIT_ULL(18)
-#define I40E_FLAG_TRUE_PROMISC_SUPPORT BIT_ULL(19)
-#define I40E_FLAG_TEMP_LINK_POLLING BIT_ULL(20)
-#define I40E_FLAG_LEGACY_RX BIT_ULL(21)
-#define I40E_FLAG_PTP BIT_ULL(22)
-#define I40E_FLAG_IWARP_ENABLED BIT_ULL(23)
-#define I40E_FLAG_SERVICE_CLIENT_REQUESTED BIT_ULL(24)
-#define I40E_FLAG_CLIENT_L2_CHANGE BIT_ULL(25)
-#define I40E_FLAG_CLIENT_RESET BIT_ULL(26)
-#define I40E_FLAG_LINK_DOWN_ON_CLOSE_ENABLED BIT_ULL(27)
-#define I40E_FLAG_SOURCE_PRUNING_DISABLED BIT_ULL(28)
-#define I40E_FLAG_TC_MQPRIO BIT_ULL(29)
-#define I40E_FLAG_FD_SB_INACTIVE BIT_ULL(30)
-#define I40E_FLAG_FD_SB_TO_CLOUD_FILTER BIT_ULL(31)
-#define I40E_FLAG_DISABLE_FW_LLDP BIT_ULL(32)
+#define I40E_HW_STOPPABLE_FW_LLDP BIT(19)
+
+ u32 flags;
+#define I40E_FLAG_RX_CSUM_ENABLED BIT(0)
+#define I40E_FLAG_MSI_ENABLED BIT(1)
+#define I40E_FLAG_MSIX_ENABLED BIT(2)
+#define I40E_FLAG_RSS_ENABLED BIT(3)
+#define I40E_FLAG_VMDQ_ENABLED BIT(4)
+#define I40E_FLAG_SRIOV_ENABLED BIT(5)
+#define I40E_FLAG_DCB_CAPABLE BIT(6)
+#define I40E_FLAG_DCB_ENABLED BIT(7)
+#define I40E_FLAG_FD_SB_ENABLED BIT(8)
+#define I40E_FLAG_FD_ATR_ENABLED BIT(9)
+#define I40E_FLAG_MFP_ENABLED BIT(10)
+#define I40E_FLAG_HW_ATR_EVICT_ENABLED BIT(11)
+#define I40E_FLAG_VEB_MODE_ENABLED BIT(12)
+#define I40E_FLAG_VEB_STATS_ENABLED BIT(13)
+#define I40E_FLAG_LINK_POLLING_ENABLED BIT(14)
+#define I40E_FLAG_TRUE_PROMISC_SUPPORT BIT(15)
+#define I40E_FLAG_LEGACY_RX BIT(16)
+#define I40E_FLAG_PTP BIT(17)
+#define I40E_FLAG_IWARP_ENABLED BIT(18)
+#define I40E_FLAG_LINK_DOWN_ON_CLOSE_ENABLED BIT(19)
+#define I40E_FLAG_SOURCE_PRUNING_DISABLED BIT(20)
+#define I40E_FLAG_TC_MQPRIO BIT(21)
+#define I40E_FLAG_FD_SB_INACTIVE BIT(22)
+#define I40E_FLAG_FD_SB_TO_CLOUD_FILTER BIT(23)
+#define I40E_FLAG_DISABLE_FW_LLDP BIT(24)
struct i40e_client_instance *cinst;
bool stat_offsets_loaded;
@@ -824,6 +826,7 @@ struct i40e_q_vector {
struct i40e_ring_container rx;
struct i40e_ring_container tx;
+ u8 itr_countdown; /* when 0 should adjust adaptive ITR */
u8 num_ringpairs; /* total number of ring pairs in vector */
cpumask_t affinity_mask;
@@ -832,8 +835,6 @@ struct i40e_q_vector {
struct rcu_head rcu; /* to avoid race with update stats on free */
char name[I40E_INT_NAME_STR_LEN];
bool arm_wb_state;
-#define ITR_COUNTDOWN_START 100
- u8 itr_countdown; /* when 0 should adjust ITR */
} ____cacheline_internodealigned_in_smp;
/* lan device */
@@ -1041,6 +1042,7 @@ void i40e_notify_client_of_l2_param_changes(struct i40e_vsi *vsi);
void i40e_notify_client_of_netdev_close(struct i40e_vsi *vsi, bool reset);
void i40e_notify_client_of_vf_enable(struct i40e_pf *pf, u32 num_vfs);
void i40e_notify_client_of_vf_reset(struct i40e_pf *pf, u32 vf_id);
+void i40e_client_update_msix_info(struct i40e_pf *pf);
int i40e_vf_client_capable(struct i40e_pf *pf, u32 vf_id);
/**
* i40e_irq_dynamic_enable - Enable default interrupt generation settings
@@ -1109,4 +1111,10 @@ static inline bool i40e_enabled_xdp_vsi(struct i40e_vsi *vsi)
int i40e_create_queue_channel(struct i40e_vsi *vsi, struct i40e_channel *ch);
int i40e_set_bw_limit(struct i40e_vsi *vsi, u16 seid, u64 max_tx_rate);
+int i40e_add_del_cloud_filter(struct i40e_vsi *vsi,
+ struct i40e_cloud_filter *filter,
+ bool add);
+int i40e_add_del_cloud_filter_big_buf(struct i40e_vsi *vsi,
+ struct i40e_cloud_filter *filter,
+ bool add);
#endif /* _I40E_H_ */
diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq.c b/drivers/net/ethernet/intel/i40e/i40e_adminq.c
index e78971605e0b..843fc7781ef8 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_adminq.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_adminq.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq.h b/drivers/net/ethernet/intel/i40e/i40e_adminq.h
index 2349fbe04bd2..0a8749ee9fd3 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_adminq.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_adminq.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
index a852775d3059..0244923edeb8 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
@@ -1914,6 +1915,43 @@ enum i40e_aq_phy_type {
I40E_PHY_TYPE_DEFAULT = 0xFF,
};
+#define I40E_PHY_TYPES_BITMASK (BIT_ULL(I40E_PHY_TYPE_SGMII) | \
+ BIT_ULL(I40E_PHY_TYPE_1000BASE_KX) | \
+ BIT_ULL(I40E_PHY_TYPE_10GBASE_KX4) | \
+ BIT_ULL(I40E_PHY_TYPE_10GBASE_KR) | \
+ BIT_ULL(I40E_PHY_TYPE_40GBASE_KR4) | \
+ BIT_ULL(I40E_PHY_TYPE_XAUI) | \
+ BIT_ULL(I40E_PHY_TYPE_XFI) | \
+ BIT_ULL(I40E_PHY_TYPE_SFI) | \
+ BIT_ULL(I40E_PHY_TYPE_XLAUI) | \
+ BIT_ULL(I40E_PHY_TYPE_XLPPI) | \
+ BIT_ULL(I40E_PHY_TYPE_40GBASE_CR4_CU) | \
+ BIT_ULL(I40E_PHY_TYPE_10GBASE_CR1_CU) | \
+ BIT_ULL(I40E_PHY_TYPE_10GBASE_AOC) | \
+ BIT_ULL(I40E_PHY_TYPE_40GBASE_AOC) | \
+ BIT_ULL(I40E_PHY_TYPE_UNRECOGNIZED) | \
+ BIT_ULL(I40E_PHY_TYPE_UNSUPPORTED) | \
+ BIT_ULL(I40E_PHY_TYPE_100BASE_TX) | \
+ BIT_ULL(I40E_PHY_TYPE_1000BASE_T) | \
+ BIT_ULL(I40E_PHY_TYPE_10GBASE_T) | \
+ BIT_ULL(I40E_PHY_TYPE_10GBASE_SR) | \
+ BIT_ULL(I40E_PHY_TYPE_10GBASE_LR) | \
+ BIT_ULL(I40E_PHY_TYPE_10GBASE_SFPP_CU) | \
+ BIT_ULL(I40E_PHY_TYPE_10GBASE_CR1) | \
+ BIT_ULL(I40E_PHY_TYPE_40GBASE_CR4) | \
+ BIT_ULL(I40E_PHY_TYPE_40GBASE_SR4) | \
+ BIT_ULL(I40E_PHY_TYPE_40GBASE_LR4) | \
+ BIT_ULL(I40E_PHY_TYPE_1000BASE_SX) | \
+ BIT_ULL(I40E_PHY_TYPE_1000BASE_LX) | \
+ BIT_ULL(I40E_PHY_TYPE_1000BASE_T_OPTICAL) | \
+ BIT_ULL(I40E_PHY_TYPE_20GBASE_KR2) | \
+ BIT_ULL(I40E_PHY_TYPE_25GBASE_KR) | \
+ BIT_ULL(I40E_PHY_TYPE_25GBASE_CR) | \
+ BIT_ULL(I40E_PHY_TYPE_25GBASE_SR) | \
+ BIT_ULL(I40E_PHY_TYPE_25GBASE_LR) | \
+ BIT_ULL(I40E_PHY_TYPE_25GBASE_AOC) | \
+ BIT_ULL(I40E_PHY_TYPE_25GBASE_ACC))
+
#define I40E_LINK_SPEED_100MB_SHIFT 0x1
#define I40E_LINK_SPEED_1000MB_SHIFT 0x2
#define I40E_LINK_SPEED_10GB_SHIFT 0x3
diff --git a/drivers/net/ethernet/intel/i40e/i40e_alloc.h b/drivers/net/ethernet/intel/i40e/i40e_alloc.h
index 926811ad44ac..abed0c52e782 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_alloc.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_alloc.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
diff --git a/drivers/net/ethernet/intel/i40e/i40e_client.c b/drivers/net/ethernet/intel/i40e/i40e_client.c
index 0de9610c1d8d..d8ce4999864f 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_client.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_client.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
@@ -287,6 +288,17 @@ out:
return capable;
}
+void i40e_client_update_msix_info(struct i40e_pf *pf)
+{
+ struct i40e_client_instance *cdev = pf->cinst;
+
+ if (!cdev || !cdev->client)
+ return;
+
+ cdev->lan_info.msix_count = pf->num_iwarp_msix;
+ cdev->lan_info.msix_entries = &pf->msix_entries[pf->iwarp_base_vector];
+}
+
/**
* i40e_client_add_instance - add a client instance struct to the instance list
* @pf: pointer to the board struct
@@ -328,9 +340,6 @@ static void i40e_client_add_instance(struct i40e_pf *pf)
return;
}
- cdev->lan_info.msix_count = pf->num_iwarp_msix;
- cdev->lan_info.msix_entries = &pf->msix_entries[pf->iwarp_base_vector];
-
mac = list_first_entry(&cdev->lan_info.netdev->dev_addrs.list,
struct netdev_hw_addr, list);
if (mac)
@@ -340,6 +349,8 @@ static void i40e_client_add_instance(struct i40e_pf *pf)
cdev->client = registered_client;
pf->cinst = cdev;
+
+ i40e_client_update_msix_info(pf);
}
/**
@@ -365,9 +376,8 @@ void i40e_client_subtask(struct i40e_pf *pf)
struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi];
int ret = 0;
- if (!(pf->flags & I40E_FLAG_SERVICE_CLIENT_REQUESTED))
+ if (!test_and_clear_bit(__I40E_CLIENT_SERVICE_REQUESTED, pf->state))
return;
- pf->flags &= ~I40E_FLAG_SERVICE_CLIENT_REQUESTED;
cdev = pf->cinst;
/* If we're down or resetting, just bail */
@@ -448,7 +458,7 @@ int i40e_lan_add_device(struct i40e_pf *pf)
* added, we can schedule a subtask to go initiate the clients if
* they can be launched at probe time.
*/
- pf->flags |= I40E_FLAG_SERVICE_CLIENT_REQUESTED;
+ set_bit(__I40E_CLIENT_SERVICE_REQUESTED, pf->state);
i40e_service_event_schedule(pf);
out:
@@ -543,7 +553,7 @@ static void i40e_client_prepare(struct i40e_client *client)
pf = ldev->pf;
i40e_client_add_instance(pf);
/* Start the client subtask */
- pf->flags |= I40E_FLAG_SERVICE_CLIENT_REQUESTED;
+ set_bit(__I40E_CLIENT_SERVICE_REQUESTED, pf->state);
i40e_service_event_schedule(pf);
}
mutex_unlock(&i40e_device_mutex);
diff --git a/drivers/net/ethernet/intel/i40e/i40e_client.h b/drivers/net/ethernet/intel/i40e/i40e_client.h
index ba55c889e4c5..9d464d40bc17 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_client.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_client.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c
index ef5a868aae46..c0a3dae8a2db 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_common.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_common.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
@@ -1208,6 +1209,29 @@ static enum i40e_media_type i40e_get_media_type(struct i40e_hw *hw)
return media;
}
+/**
+ * i40e_poll_globr - Poll for Global Reset completion
+ * @hw: pointer to the hardware structure
+ * @retry_limit: how many times to retry before failure
+ **/
+static i40e_status i40e_poll_globr(struct i40e_hw *hw,
+ u32 retry_limit)
+{
+ u32 cnt, reg = 0;
+
+ for (cnt = 0; cnt < retry_limit; cnt++) {
+ reg = rd32(hw, I40E_GLGEN_RSTAT);
+ if (!(reg & I40E_GLGEN_RSTAT_DEVSTATE_MASK))
+ return 0;
+ msleep(100);
+ }
+
+ hw_dbg(hw, "Global reset failed.\n");
+ hw_dbg(hw, "I40E_GLGEN_RSTAT = 0x%x\n", reg);
+
+ return I40E_ERR_RESET_FAILED;
+}
+
#define I40E_PF_RESET_WAIT_COUNT_A0 200
#define I40E_PF_RESET_WAIT_COUNT 200
/**
@@ -1284,14 +1308,14 @@ i40e_status i40e_pf_reset(struct i40e_hw *hw)
if (!(reg & I40E_PFGEN_CTRL_PFSWR_MASK))
break;
reg2 = rd32(hw, I40E_GLGEN_RSTAT);
- if (reg2 & I40E_GLGEN_RSTAT_DEVSTATE_MASK) {
- hw_dbg(hw, "Core reset upcoming. Skipping PF reset request.\n");
- hw_dbg(hw, "I40E_GLGEN_RSTAT = 0x%x\n", reg2);
- return I40E_ERR_NOT_READY;
- }
+ if (reg2 & I40E_GLGEN_RSTAT_DEVSTATE_MASK)
+ break;
usleep_range(1000, 2000);
}
- if (reg & I40E_PFGEN_CTRL_PFSWR_MASK) {
+ if (reg2 & I40E_GLGEN_RSTAT_DEVSTATE_MASK) {
+ if (i40e_poll_globr(hw, grst_del))
+ return I40E_ERR_RESET_FAILED;
+ } else if (reg & I40E_PFGEN_CTRL_PFSWR_MASK) {
hw_dbg(hw, "PF reset polling failed to complete.\n");
return I40E_ERR_RESET_FAILED;
}
@@ -2415,6 +2439,7 @@ i40e_status i40e_aq_get_switch_config(struct i40e_hw *hw,
* i40e_aq_set_switch_config
* @hw: pointer to the hardware structure
* @flags: bit flag values to set
+ * @mode: cloud filter mode
* @valid_flags: which bit flags to set
* @mode: cloud filter mode
* @cmd_details: pointer to command details structure or NULL
@@ -3200,9 +3225,10 @@ static void i40e_parse_discover_capabilities(struct i40e_hw *hw, void *buff,
u32 valid_functions, num_functions;
u32 number, logical_id, phys_id;
struct i40e_hw_capabilities *p;
+ u16 id, ocp_cfg_word0;
+ i40e_status status;
u8 major_rev;
u32 i = 0;
- u16 id;
cap = (struct i40e_aqc_list_capabilities_element_resp *) buff;
@@ -3389,6 +3415,26 @@ static void i40e_parse_discover_capabilities(struct i40e_hw *hw, void *buff,
hw->num_ports++;
}
+ /* OCP cards case: if a mezz is removed the Ethernet port is at
+ * disabled state in PRTGEN_CNF register. Additional NVM read is
+ * needed in order to check if we are dealing with OCP card.
+ * Those cards have 4 PFs at minimum, so using PRTGEN_CNF for counting
+ * physical ports results in wrong partition id calculation and thus
+ * not supporting WoL.
+ */
+ if (hw->mac.type == I40E_MAC_X722) {
+ if (!i40e_acquire_nvm(hw, I40E_RESOURCE_READ)) {
+ status = i40e_aq_read_nvm(hw, I40E_SR_EMP_MODULE_PTR,
+ 2 * I40E_SR_OCP_CFG_WORD0,
+ sizeof(ocp_cfg_word0),
+ &ocp_cfg_word0, true, NULL);
+ if (!status &&
+ (ocp_cfg_word0 & I40E_SR_OCP_ENABLED))
+ hw->num_ports = 4;
+ i40e_release_nvm(hw);
+ }
+ }
+
valid_functions = p->valid_functions;
num_functions = 0;
while (valid_functions) {
@@ -5531,7 +5577,7 @@ i40e_aq_add_cloud_filters(struct i40e_hw *hw, u16 seid,
* function.
*
**/
-i40e_status
+enum i40e_status_code
i40e_aq_add_cloud_filters_bb(struct i40e_hw *hw, u16 seid,
struct i40e_aqc_cloud_filters_element_bb *filters,
u8 filter_count)
@@ -5625,7 +5671,7 @@ i40e_aq_rem_cloud_filters(struct i40e_hw *hw, u16 seid,
* function.
*
**/
-i40e_status
+enum i40e_status_code
i40e_aq_rem_cloud_filters_bb(struct i40e_hw *hw, u16 seid,
struct i40e_aqc_cloud_filters_element_bb *filters,
u8 filter_count)
diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb.c b/drivers/net/ethernet/intel/i40e/i40e_dcb.c
index 55079fe3ed63..9fec728dc4b9 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_dcb.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_dcb.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb.h b/drivers/net/ethernet/intel/i40e/i40e_dcb.h
index 92d01042c1f6..4f806386cb22 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_dcb.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_dcb.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c b/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c
index 886e667f2f1c..502818e3da78 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c
index 4c3b4243cf65..d494dcaf18d0 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
@@ -155,8 +156,8 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid)
dev_info(&pf->pdev->dev, " vlan_features = 0x%08lx\n",
(unsigned long int)nd->vlan_features);
}
- dev_info(&pf->pdev->dev,
- " vlgrp: & = %p\n", vsi->active_vlans);
+ dev_info(&pf->pdev->dev, " active_vlans is %s\n",
+ vsi->active_vlans ? "<valid>" : "<null>");
dev_info(&pf->pdev->dev,
" flags = 0x%08lx, netdev_registered = %i, current_netdev_flags = 0x%04x\n",
vsi->flags, vsi->netdev_registered, vsi->current_netdev_flags);
@@ -270,14 +271,6 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid)
continue;
dev_info(&pf->pdev->dev,
- " rx_rings[%i]: desc = %p\n",
- i, rx_ring->desc);
- dev_info(&pf->pdev->dev,
- " rx_rings[%i]: dev = %p, netdev = %p, rx_bi = %p\n",
- i, rx_ring->dev,
- rx_ring->netdev,
- rx_ring->rx_bi);
- dev_info(&pf->pdev->dev,
" rx_rings[%i]: state = %lu, queue_index = %d, reg_idx = %d\n",
i, *rx_ring->state,
rx_ring->queue_index,
@@ -307,17 +300,12 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid)
rx_ring->rx_stats.realloc_count,
rx_ring->rx_stats.page_reuse_count);
dev_info(&pf->pdev->dev,
- " rx_rings[%i]: size = %i, dma = 0x%08lx\n",
- i, rx_ring->size,
- (unsigned long int)rx_ring->dma);
- dev_info(&pf->pdev->dev,
- " rx_rings[%i]: vsi = %p, q_vector = %p\n",
- i, rx_ring->vsi,
- rx_ring->q_vector);
+ " rx_rings[%i]: size = %i\n",
+ i, rx_ring->size);
dev_info(&pf->pdev->dev,
- " rx_rings[%i]: rx_itr_setting = %d (%s)\n",
- i, rx_ring->rx_itr_setting,
- ITR_IS_DYNAMIC(rx_ring->rx_itr_setting) ? "dynamic" : "fixed");
+ " rx_rings[%i]: itr_setting = %d (%s)\n",
+ i, rx_ring->itr_setting,
+ ITR_IS_DYNAMIC(rx_ring->itr_setting) ? "dynamic" : "fixed");
}
for (i = 0; i < vsi->num_queue_pairs; i++) {
struct i40e_ring *tx_ring = READ_ONCE(vsi->tx_rings[i]);
@@ -326,14 +314,6 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid)
continue;
dev_info(&pf->pdev->dev,
- " tx_rings[%i]: desc = %p\n",
- i, tx_ring->desc);
- dev_info(&pf->pdev->dev,
- " tx_rings[%i]: dev = %p, netdev = %p, tx_bi = %p\n",
- i, tx_ring->dev,
- tx_ring->netdev,
- tx_ring->tx_bi);
- dev_info(&pf->pdev->dev,
" tx_rings[%i]: state = %lu, queue_index = %d, reg_idx = %d\n",
i, *tx_ring->state,
tx_ring->queue_index,
@@ -355,20 +335,15 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid)
tx_ring->tx_stats.tx_busy,
tx_ring->tx_stats.tx_done_old);
dev_info(&pf->pdev->dev,
- " tx_rings[%i]: size = %i, dma = 0x%08lx\n",
- i, tx_ring->size,
- (unsigned long int)tx_ring->dma);
- dev_info(&pf->pdev->dev,
- " tx_rings[%i]: vsi = %p, q_vector = %p\n",
- i, tx_ring->vsi,
- tx_ring->q_vector);
+ " tx_rings[%i]: size = %i\n",
+ i, tx_ring->size);
dev_info(&pf->pdev->dev,
" tx_rings[%i]: DCB tc = %d\n",
i, tx_ring->dcb_tc);
dev_info(&pf->pdev->dev,
- " tx_rings[%i]: tx_itr_setting = %d (%s)\n",
- i, tx_ring->tx_itr_setting,
- ITR_IS_DYNAMIC(tx_ring->tx_itr_setting) ? "dynamic" : "fixed");
+ " tx_rings[%i]: itr_setting = %d (%s)\n",
+ i, tx_ring->itr_setting,
+ ITR_IS_DYNAMIC(tx_ring->itr_setting) ? "dynamic" : "fixed");
}
rcu_read_unlock();
dev_info(&pf->pdev->dev,
@@ -466,8 +441,6 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid)
vsi->info.resp_reserved[6], vsi->info.resp_reserved[7],
vsi->info.resp_reserved[8], vsi->info.resp_reserved[9],
vsi->info.resp_reserved[10], vsi->info.resp_reserved[11]);
- if (vsi->back)
- dev_info(&pf->pdev->dev, " PF = %p\n", vsi->back);
dev_info(&pf->pdev->dev, " idx = %d\n", vsi->idx);
dev_info(&pf->pdev->dev,
" tc_config: numtc = %d, enabled_tc = 0x%x\n",
diff --git a/drivers/net/ethernet/intel/i40e/i40e_devids.h b/drivers/net/ethernet/intel/i40e/i40e_devids.h
index 8e46098bad57..ad6a66ccb576 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_devids.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_devids.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
diff --git a/drivers/net/ethernet/intel/i40e/i40e_diag.c b/drivers/net/ethernet/intel/i40e/i40e_diag.c
index 76ed56641864..df3e60470f8b 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_diag.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_diag.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
diff --git a/drivers/net/ethernet/intel/i40e/i40e_diag.h b/drivers/net/ethernet/intel/i40e/i40e_diag.h
index 0b5911652084..be8341763475 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_diag.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_diag.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
index 2f5bee713fef..b974482ff630 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
@@ -230,6 +231,8 @@ static const struct i40e_priv_flags i40e_gstrings_priv_flags[] = {
I40E_PRIV_FLAG("flow-director-atr", I40E_FLAG_FD_ATR_ENABLED, 0),
I40E_PRIV_FLAG("veb-stats", I40E_FLAG_VEB_STATS_ENABLED, 0),
I40E_PRIV_FLAG("hw-atr-eviction", I40E_FLAG_HW_ATR_EVICT_ENABLED, 0),
+ I40E_PRIV_FLAG("link-down-on-close",
+ I40E_FLAG_LINK_DOWN_ON_CLOSE_ENABLED, 0),
I40E_PRIV_FLAG("legacy-rx", I40E_FLAG_LEGACY_RX, 0),
I40E_PRIV_FLAG("disable-source-pruning",
I40E_FLAG_SOURCE_PRUNING_DISABLED, 0),
@@ -857,7 +860,9 @@ static int i40e_set_link_ksettings(struct net_device *netdev,
if (hw->device_id == I40E_DEV_ID_KX_B ||
hw->device_id == I40E_DEV_ID_KX_C ||
hw->device_id == I40E_DEV_ID_20G_KR2 ||
- hw->device_id == I40E_DEV_ID_20G_KR2_A) {
+ hw->device_id == I40E_DEV_ID_20G_KR2_A ||
+ hw->device_id == I40E_DEV_ID_25G_B ||
+ hw->device_id == I40E_DEV_ID_KX_X722) {
netdev_info(netdev, "Changing settings is not supported on backplane.\n");
return -EOPNOTSUPP;
}
@@ -868,23 +873,21 @@ static int i40e_set_link_ksettings(struct net_device *netdev,
/* save autoneg out of ksettings */
autoneg = copy_ks.base.autoneg;
- memset(&safe_ks, 0, sizeof(safe_ks));
+ /* get our own copy of the bits to check against */
+ memset(&safe_ks, 0, sizeof(struct ethtool_link_ksettings));
+ safe_ks.base.cmd = copy_ks.base.cmd;
+ safe_ks.base.link_mode_masks_nwords =
+ copy_ks.base.link_mode_masks_nwords;
+ i40e_get_link_ksettings(netdev, &safe_ks);
+
/* Get link modes supported by hardware and check against modes
* requested by the user. Return an error if unsupported mode was set.
*/
- i40e_phy_type_to_ethtool(pf, &safe_ks);
if (!bitmap_subset(copy_ks.link_modes.advertising,
safe_ks.link_modes.supported,
__ETHTOOL_LINK_MODE_MASK_NBITS))
return -EINVAL;
- /* get our own copy of the bits to check against */
- memset(&safe_ks, 0, sizeof(struct ethtool_link_ksettings));
- safe_ks.base.cmd = copy_ks.base.cmd;
- safe_ks.base.link_mode_masks_nwords =
- copy_ks.base.link_mode_masks_nwords;
- i40e_get_link_ksettings(netdev, &safe_ks);
-
/* set autoneg back to what it currently is */
copy_ks.base.autoneg = safe_ks.base.autoneg;
@@ -2244,14 +2247,14 @@ static int __i40e_get_coalesce(struct net_device *netdev,
rx_ring = vsi->rx_rings[queue];
tx_ring = vsi->tx_rings[queue];
- if (ITR_IS_DYNAMIC(rx_ring->rx_itr_setting))
+ if (ITR_IS_DYNAMIC(rx_ring->itr_setting))
ec->use_adaptive_rx_coalesce = 1;
- if (ITR_IS_DYNAMIC(tx_ring->tx_itr_setting))
+ if (ITR_IS_DYNAMIC(tx_ring->itr_setting))
ec->use_adaptive_tx_coalesce = 1;
- ec->rx_coalesce_usecs = rx_ring->rx_itr_setting & ~I40E_ITR_DYNAMIC;
- ec->tx_coalesce_usecs = tx_ring->tx_itr_setting & ~I40E_ITR_DYNAMIC;
+ ec->rx_coalesce_usecs = rx_ring->itr_setting & ~I40E_ITR_DYNAMIC;
+ ec->tx_coalesce_usecs = tx_ring->itr_setting & ~I40E_ITR_DYNAMIC;
/* we use the _usecs_high to store/set the interrupt rate limit
* that the hardware supports, that almost but not quite
@@ -2311,34 +2314,35 @@ static void i40e_set_itr_per_queue(struct i40e_vsi *vsi,
struct i40e_pf *pf = vsi->back;
struct i40e_hw *hw = &pf->hw;
struct i40e_q_vector *q_vector;
- u16 vector, intrl;
+ u16 intrl;
intrl = i40e_intrl_usec_to_reg(vsi->int_rate_limit);
- rx_ring->rx_itr_setting = ec->rx_coalesce_usecs;
- tx_ring->tx_itr_setting = ec->tx_coalesce_usecs;
+ rx_ring->itr_setting = ITR_REG_ALIGN(ec->rx_coalesce_usecs);
+ tx_ring->itr_setting = ITR_REG_ALIGN(ec->tx_coalesce_usecs);
if (ec->use_adaptive_rx_coalesce)
- rx_ring->rx_itr_setting |= I40E_ITR_DYNAMIC;
+ rx_ring->itr_setting |= I40E_ITR_DYNAMIC;
else
- rx_ring->rx_itr_setting &= ~I40E_ITR_DYNAMIC;
+ rx_ring->itr_setting &= ~I40E_ITR_DYNAMIC;
if (ec->use_adaptive_tx_coalesce)
- tx_ring->tx_itr_setting |= I40E_ITR_DYNAMIC;
+ tx_ring->itr_setting |= I40E_ITR_DYNAMIC;
else
- tx_ring->tx_itr_setting &= ~I40E_ITR_DYNAMIC;
+ tx_ring->itr_setting &= ~I40E_ITR_DYNAMIC;
q_vector = rx_ring->q_vector;
- q_vector->rx.itr = ITR_TO_REG(rx_ring->rx_itr_setting);
- vector = vsi->base_vector + q_vector->v_idx;
- wr32(hw, I40E_PFINT_ITRN(I40E_RX_ITR, vector - 1), q_vector->rx.itr);
+ q_vector->rx.target_itr = ITR_TO_REG(rx_ring->itr_setting);
q_vector = tx_ring->q_vector;
- q_vector->tx.itr = ITR_TO_REG(tx_ring->tx_itr_setting);
- vector = vsi->base_vector + q_vector->v_idx;
- wr32(hw, I40E_PFINT_ITRN(I40E_TX_ITR, vector - 1), q_vector->tx.itr);
+ q_vector->tx.target_itr = ITR_TO_REG(tx_ring->itr_setting);
- wr32(hw, I40E_PFINT_RATEN(vector - 1), intrl);
+ /* The interrupt handler itself will take care of programming
+ * the Tx and Rx ITR values based on the values we have entered
+ * into the q_vector, no need to write the values now.
+ */
+
+ wr32(hw, I40E_PFINT_RATEN(q_vector->reg_idx), intrl);
i40e_flush(hw);
}
@@ -2364,11 +2368,11 @@ static int __i40e_set_coalesce(struct net_device *netdev,
vsi->work_limit = ec->tx_max_coalesced_frames_irq;
if (queue < 0) {
- cur_rx_itr = vsi->rx_rings[0]->rx_itr_setting;
- cur_tx_itr = vsi->tx_rings[0]->tx_itr_setting;
+ cur_rx_itr = vsi->rx_rings[0]->itr_setting;
+ cur_tx_itr = vsi->tx_rings[0]->itr_setting;
} else if (queue < vsi->num_queue_pairs) {
- cur_rx_itr = vsi->rx_rings[queue]->rx_itr_setting;
- cur_tx_itr = vsi->tx_rings[queue]->tx_itr_setting;
+ cur_rx_itr = vsi->rx_rings[queue]->itr_setting;
+ cur_tx_itr = vsi->tx_rings[queue]->itr_setting;
} else {
netif_info(pf, drv, netdev, "Invalid queue value, queue range is 0 - %d\n",
vsi->num_queue_pairs - 1);
@@ -2396,7 +2400,7 @@ static int __i40e_set_coalesce(struct net_device *netdev,
return -EINVAL;
}
- if (ec->rx_coalesce_usecs > (I40E_MAX_ITR << 1)) {
+ if (ec->rx_coalesce_usecs > I40E_MAX_ITR) {
netif_info(pf, drv, netdev, "Invalid value, rx-usecs range is 0-8160\n");
return -EINVAL;
}
@@ -2407,16 +2411,16 @@ static int __i40e_set_coalesce(struct net_device *netdev,
return -EINVAL;
}
- if (ec->tx_coalesce_usecs > (I40E_MAX_ITR << 1)) {
+ if (ec->tx_coalesce_usecs > I40E_MAX_ITR) {
netif_info(pf, drv, netdev, "Invalid value, tx-usecs range is 0-8160\n");
return -EINVAL;
}
if (ec->use_adaptive_rx_coalesce && !cur_rx_itr)
- ec->rx_coalesce_usecs = I40E_MIN_ITR << 1;
+ ec->rx_coalesce_usecs = I40E_MIN_ITR;
if (ec->use_adaptive_tx_coalesce && !cur_tx_itr)
- ec->tx_coalesce_usecs = I40E_MIN_ITR << 1;
+ ec->tx_coalesce_usecs = I40E_MIN_ITR;
intrl_reg = i40e_intrl_usec_to_reg(ec->rx_coalesce_usecs_high);
vsi->int_rate_limit = INTRL_REG_TO_USEC(intrl_reg);
@@ -3947,7 +3951,7 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi,
if (!(pf->flags & I40E_FLAG_FD_SB_ENABLED))
return -EOPNOTSUPP;
- if (pf->flags & I40E_FLAG_FD_SB_AUTO_DISABLED)
+ if (test_bit(__I40E_FD_SB_AUTO_DISABLED, pf->state))
return -ENOSPC;
if (test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state) ||
@@ -4406,6 +4410,8 @@ static int i40e_set_priv_flags(struct net_device *dev, u32 flags)
}
flags_complete:
+ changed_flags = orig_flags ^ new_flags;
+
/* Before we finalize any flag changes, we need to perform some
* checks to ensure that the changes are supported and safe.
*/
@@ -4415,38 +4421,27 @@ flags_complete:
!(pf->hw_features & I40E_HW_ATR_EVICT_CAPABLE))
return -EOPNOTSUPP;
- /* Disable FW LLDP not supported if NPAR active or if FW
- * API version < 1.7
+ /* If the driver detected FW LLDP was disabled on init, this flag could
+ * be set, however we do not support _changing_ the flag if NPAR is
+ * enabled or FW API version < 1.7. There are situations where older
+ * FW versions/NPAR enabled PFs could disable LLDP, however we _must_
+ * not allow the user to enable/disable LLDP with this flag on
+ * unsupported FW versions.
*/
- if (new_flags & I40E_FLAG_DISABLE_FW_LLDP) {
- if (pf->hw.func_caps.npar_enable) {
- dev_warn(&pf->pdev->dev,
- "Unable to stop FW LLDP if NPAR active\n");
- return -EOPNOTSUPP;
- }
-
- if (pf->hw.aq.api_maj_ver < 1 ||
- (pf->hw.aq.api_maj_ver == 1 &&
- pf->hw.aq.api_min_ver < 7)) {
+ if (changed_flags & I40E_FLAG_DISABLE_FW_LLDP) {
+ if (!(pf->hw_features & I40E_HW_STOPPABLE_FW_LLDP)) {
dev_warn(&pf->pdev->dev,
- "FW ver does not support stopping FW LLDP\n");
+ "Device does not support changing FW LLDP\n");
return -EOPNOTSUPP;
}
}
- /* Compare and exchange the new flags into place. If we failed, that
- * is if cmpxchg returns anything but the old value, this means that
- * something else has modified the flags variable since we copied it
- * originally. We'll just punt with an error and log something in the
- * message buffer.
+ /* Now that we've checked to ensure that the new flags are valid, load
+ * them into place. Since we only modify flags either (a) during
+ * initialization or (b) while holding the RTNL lock, we don't need
+ * anything fancy here.
*/
- if (cmpxchg64(&pf->flags, orig_flags, new_flags) != orig_flags) {
- dev_warn(&pf->pdev->dev,
- "Unable to update pf->flags as it was modified by another thread...\n");
- return -EAGAIN;
- }
-
- changed_flags = orig_flags ^ new_flags;
+ pf->flags = new_flags;
/* Process any additional changes needed as a result of flag changes.
* The changed_flags value reflects the list of bits that were
@@ -4456,7 +4451,7 @@ flags_complete:
/* Flush current ATR settings if ATR was disabled */
if ((changed_flags & I40E_FLAG_FD_ATR_ENABLED) &&
!(pf->flags & I40E_FLAG_FD_ATR_ENABLED)) {
- pf->flags |= I40E_FLAG_FD_ATR_AUTO_DISABLED;
+ set_bit(__I40E_FD_ATR_AUTO_DISABLED, pf->state);
set_bit(__I40E_FD_FLUSH_REQUESTED, pf->state);
}
@@ -4479,6 +4474,12 @@ flags_complete:
}
}
+ if ((changed_flags & pf->flags &
+ I40E_FLAG_LINK_DOWN_ON_CLOSE_ENABLED) &&
+ (pf->flags & I40E_FLAG_MFP_ENABLED))
+ dev_warn(&pf->pdev->dev,
+ "Turning on link-down-on-close flag may affect other partitions\n");
+
if (changed_flags & I40E_FLAG_DISABLE_FW_LLDP) {
if (pf->flags & I40E_FLAG_DISABLE_FW_LLDP) {
struct i40e_dcbx_config *dcbcfg;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_fcoe.c b/drivers/net/ethernet/intel/i40e/i40e_fcoe.c
deleted file mode 100644
index 2d1253c5b7a1..000000000000
--- a/drivers/net/ethernet/intel/i40e/i40e_fcoe.c
+++ /dev/null
@@ -1,1571 +0,0 @@
-/*******************************************************************************
- *
- * Intel Ethernet Controller XL710 Family Linux Driver
- * Copyright(c) 2013 - 2016 Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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, see <http://www.gnu.org/licenses/>.
- *
- * The full GNU General Public License is included in this distribution in
- * the file called "COPYING".
- *
- * Contact Information:
- * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- ******************************************************************************/
-
-#include <linux/if_ether.h>
-#include <scsi/scsi_cmnd.h>
-#include <scsi/scsi_device.h>
-#include <scsi/fc/fc_fs.h>
-#include <scsi/fc/fc_fip.h>
-#include <scsi/fc/fc_fcoe.h>
-#include <scsi/libfc.h>
-#include <scsi/libfcoe.h>
-#include <uapi/linux/dcbnl.h>
-
-#include "i40e.h"
-#include "i40e_fcoe.h"
-
-/**
- * i40e_fcoe_sof_is_class2 - returns true if this is a FC Class 2 SOF
- * @sof: the FCoE start of frame delimiter
- **/
-static inline bool i40e_fcoe_sof_is_class2(u8 sof)
-{
- return (sof == FC_SOF_I2) || (sof == FC_SOF_N2);
-}
-
-/**
- * i40e_fcoe_sof_is_class3 - returns true if this is a FC Class 3 SOF
- * @sof: the FCoE start of frame delimiter
- **/
-static inline bool i40e_fcoe_sof_is_class3(u8 sof)
-{
- return (sof == FC_SOF_I3) || (sof == FC_SOF_N3);
-}
-
-/**
- * i40e_fcoe_sof_is_supported - returns true if the FC SOF is supported by HW
- * @sof: the input SOF value from the frame
- **/
-static inline bool i40e_fcoe_sof_is_supported(u8 sof)
-{
- return i40e_fcoe_sof_is_class2(sof) ||
- i40e_fcoe_sof_is_class3(sof);
-}
-
-/**
- * i40e_fcoe_fc_sof - pull the SOF from FCoE header in the frame
- * @skb: the frame whose EOF is to be pulled from
- **/
-static inline int i40e_fcoe_fc_sof(struct sk_buff *skb, u8 *sof)
-{
- *sof = ((struct fcoe_hdr *)skb_network_header(skb))->fcoe_sof;
-
- if (!i40e_fcoe_sof_is_supported(*sof))
- return -EINVAL;
- return 0;
-}
-
-/**
- * i40e_fcoe_eof_is_supported - returns true if the EOF is supported by HW
- * @eof: the input EOF value from the frame
- **/
-static inline bool i40e_fcoe_eof_is_supported(u8 eof)
-{
- return (eof == FC_EOF_N) || (eof == FC_EOF_T) ||
- (eof == FC_EOF_NI) || (eof == FC_EOF_A);
-}
-
-/**
- * i40e_fcoe_fc_eof - pull EOF from FCoE trailer in the frame
- * @skb: the frame whose EOF is to be pulled from
- **/
-static inline int i40e_fcoe_fc_eof(struct sk_buff *skb, u8 *eof)
-{
- /* the first byte of the last dword is EOF */
- skb_copy_bits(skb, skb->len - 4, eof, 1);
-
- if (!i40e_fcoe_eof_is_supported(*eof))
- return -EINVAL;
- return 0;
-}
-
-/**
- * i40e_fcoe_ctxt_eof - convert input FC EOF for descriptor programming
- * @eof: the input eof value from the frame
- *
- * The FC EOF is converted to the value understood by HW for descriptor
- * programming. Never call this w/o calling i40e_fcoe_eof_is_supported()
- * first and that already checks for all supported valid eof values.
- **/
-static inline u32 i40e_fcoe_ctxt_eof(u8 eof)
-{
- switch (eof) {
- case FC_EOF_N:
- return I40E_TX_DESC_CMD_L4T_EOFT_EOF_N;
- case FC_EOF_T:
- return I40E_TX_DESC_CMD_L4T_EOFT_EOF_T;
- case FC_EOF_NI:
- return I40E_TX_DESC_CMD_L4T_EOFT_EOF_NI;
- case FC_EOF_A:
- return I40E_TX_DESC_CMD_L4T_EOFT_EOF_A;
- default:
- /* Supported valid eof shall be already checked by
- * calling i40e_fcoe_eof_is_supported() first,
- * therefore this default case shall never hit.
- */
- WARN_ON(1);
- return -EINVAL;
- }
-}
-
-/**
- * i40e_fcoe_xid_is_valid - returns true if the exchange id is valid
- * @xid: the exchange id
- **/
-static inline bool i40e_fcoe_xid_is_valid(u16 xid)
-{
- return (xid != FC_XID_UNKNOWN) && (xid < I40E_FCOE_DDP_MAX);
-}
-
-/**
- * i40e_fcoe_ddp_unmap - unmap the mapped sglist associated
- * @pf: pointer to PF
- * @ddp: sw DDP context
- *
- * Unmap the scatter-gather list associated with the given SW DDP context
- *
- * Returns: data length already ddp-ed in bytes
- *
- **/
-static inline void i40e_fcoe_ddp_unmap(struct i40e_pf *pf,
- struct i40e_fcoe_ddp *ddp)
-{
- if (test_and_set_bit(__I40E_FCOE_DDP_UNMAPPED, &ddp->flags))
- return;
-
- if (ddp->sgl) {
- dma_unmap_sg(&pf->pdev->dev, ddp->sgl, ddp->sgc,
- DMA_FROM_DEVICE);
- ddp->sgl = NULL;
- ddp->sgc = 0;
- }
-
- if (ddp->pool) {
- dma_pool_free(ddp->pool, ddp->udl, ddp->udp);
- ddp->pool = NULL;
- }
-}
-
-/**
- * i40e_fcoe_ddp_clear - clear the given SW DDP context
- * @ddp - SW DDP context
- **/
-static inline void i40e_fcoe_ddp_clear(struct i40e_fcoe_ddp *ddp)
-{
- memset(ddp, 0, sizeof(struct i40e_fcoe_ddp));
- ddp->xid = FC_XID_UNKNOWN;
- ddp->flags = __I40E_FCOE_DDP_NONE;
-}
-
-/**
- * i40e_fcoe_progid_is_fcoe - check if the prog_id is for FCoE
- * @id: the prog id for the programming status Rx descriptor write-back
- **/
-static inline bool i40e_fcoe_progid_is_fcoe(u8 id)
-{
- return (id == I40E_RX_PROG_STATUS_DESC_FCOE_CTXT_PROG_STATUS) ||
- (id == I40E_RX_PROG_STATUS_DESC_FCOE_CTXT_INVL_STATUS);
-}
-
-/**
- * i40e_fcoe_fc_get_xid - get xid from the frame header
- * @fh: the fc frame header
- *
- * In case the incoming frame's exchange is originated from
- * the initiator, then received frame's exchange id is ANDed
- * with fc_cpu_mask bits to get the same cpu on which exchange
- * was originated, otherwise just use the current cpu.
- *
- * Returns ox_id if exchange originator, rx_id if responder
- **/
-static inline u16 i40e_fcoe_fc_get_xid(struct fc_frame_header *fh)
-{
- u32 f_ctl = ntoh24(fh->fh_f_ctl);
-
- return (f_ctl & FC_FC_EX_CTX) ?
- be16_to_cpu(fh->fh_ox_id) :
- be16_to_cpu(fh->fh_rx_id);
-}
-
-/**
- * i40e_fcoe_fc_frame_header - get fc frame header from skb
- * @skb: packet
- *
- * This checks if there is a VLAN header and returns the data
- * pointer to the start of the fc_frame_header.
- *
- * Returns pointer to the fc_frame_header
- **/
-static inline struct fc_frame_header *i40e_fcoe_fc_frame_header(
- struct sk_buff *skb)
-{
- void *fh = skb->data + sizeof(struct fcoe_hdr);
-
- if (eth_hdr(skb)->h_proto == htons(ETH_P_8021Q))
- fh += sizeof(struct vlan_hdr);
-
- return (struct fc_frame_header *)fh;
-}
-
-/**
- * i40e_fcoe_ddp_put - release the DDP context for a given exchange id
- * @netdev: the corresponding net_device
- * @xid: the exchange id that corresponding DDP context will be released
- *
- * This is the implementation of net_device_ops.ndo_fcoe_ddp_done
- * and it is expected to be called by ULD, i.e., FCP layer of libfc
- * to release the corresponding ddp context when the I/O is done.
- *
- * Returns : data length already ddp-ed in bytes
- **/
-static int i40e_fcoe_ddp_put(struct net_device *netdev, u16 xid)
-{
- struct i40e_netdev_priv *np = netdev_priv(netdev);
- struct i40e_pf *pf = np->vsi->back;
- struct i40e_fcoe *fcoe = &pf->fcoe;
- int len = 0;
- struct i40e_fcoe_ddp *ddp = &fcoe->ddp[xid];
-
- if (!fcoe || !ddp)
- goto out;
-
- if (test_bit(__I40E_FCOE_DDP_DONE, &ddp->flags))
- len = ddp->len;
- i40e_fcoe_ddp_unmap(pf, ddp);
-out:
- return len;
-}
-
-/**
- * i40e_fcoe_sw_init - sets up the HW for FCoE
- * @pf: pointer to PF
- **/
-void i40e_init_pf_fcoe(struct i40e_pf *pf)
-{
- struct i40e_hw *hw = &pf->hw;
- u32 val;
-
- pf->flags &= ~I40E_FLAG_FCOE_ENABLED;
- pf->num_fcoe_qps = 0;
- pf->fcoe_hmc_cntx_num = 0;
- pf->fcoe_hmc_filt_num = 0;
-
- if (!pf->hw.func_caps.fcoe) {
- dev_dbg(&pf->pdev->dev, "FCoE capability is disabled\n");
- return;
- }
-
- if (!pf->hw.func_caps.dcb) {
- dev_warn(&pf->pdev->dev,
- "Hardware is not DCB capable not enabling FCoE.\n");
- return;
- }
-
- /* enable FCoE hash filter */
- val = i40e_read_rx_ctl(hw, I40E_PFQF_HENA(1));
- val |= BIT(I40E_FILTER_PCTYPE_FCOE_OX - 32);
- val |= BIT(I40E_FILTER_PCTYPE_FCOE_RX - 32);
- val &= I40E_PFQF_HENA_PTYPE_ENA_MASK;
- i40e_write_rx_ctl(hw, I40E_PFQF_HENA(1), val);
-
- /* enable flag */
- pf->flags |= I40E_FLAG_FCOE_ENABLED;
- pf->num_fcoe_qps = I40E_DEFAULT_FCOE;
-
- /* Reserve 4K DDP contexts and 20K filter size for FCoE */
- pf->fcoe_hmc_cntx_num = BIT(I40E_DMA_CNTX_SIZE_4K) *
- I40E_DMA_CNTX_BASE_SIZE;
- pf->fcoe_hmc_filt_num = pf->fcoe_hmc_cntx_num +
- BIT(I40E_HASH_FILTER_SIZE_16K) *
- I40E_HASH_FILTER_BASE_SIZE;
-
- /* FCoE object: max 16K filter buckets and 4K DMA contexts */
- pf->filter_settings.fcoe_filt_num = I40E_HASH_FILTER_SIZE_16K;
- pf->filter_settings.fcoe_cntx_num = I40E_DMA_CNTX_SIZE_4K;
-
- /* Setup max frame with FCoE_MTU plus L2 overheads */
- val = i40e_read_rx_ctl(hw, I40E_GLFCOE_RCTL);
- val &= ~I40E_GLFCOE_RCTL_MAX_SIZE_MASK;
- val |= ((FCOE_MTU + ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN)
- << I40E_GLFCOE_RCTL_MAX_SIZE_SHIFT);
- i40e_write_rx_ctl(hw, I40E_GLFCOE_RCTL, val);
-
- dev_info(&pf->pdev->dev, "FCoE is supported.\n");
-}
-
-/**
- * i40e_get_fcoe_tc_map - Return TC map for FCoE APP
- * @pf: pointer to PF
- *
- **/
-u8 i40e_get_fcoe_tc_map(struct i40e_pf *pf)
-{
- struct i40e_dcb_app_priority_table app;
- struct i40e_hw *hw = &pf->hw;
- u8 enabled_tc = 0;
- u8 tc, i;
- /* Get the FCoE APP TLV */
- struct i40e_dcbx_config *dcbcfg = &hw->local_dcbx_config;
-
- for (i = 0; i < dcbcfg->numapps; i++) {
- app = dcbcfg->app[i];
- if (app.selector == IEEE_8021QAZ_APP_SEL_ETHERTYPE &&
- app.protocolid == ETH_P_FCOE) {
- tc = dcbcfg->etscfg.prioritytable[app.priority];
- enabled_tc |= BIT(tc);
- break;
- }
- }
-
- /* TC0 if there is no TC defined for FCoE APP TLV */
- enabled_tc = enabled_tc ? enabled_tc : 0x1;
-
- return enabled_tc;
-}
-
-/**
- * i40e_fcoe_vsi_init - prepares the VSI context for creating a FCoE VSI
- * @vsi: pointer to the associated VSI struct
- * @ctxt: pointer to the associated VSI context to be passed to HW
- *
- * Returns 0 on success or < 0 on error
- **/
-int i40e_fcoe_vsi_init(struct i40e_vsi *vsi, struct i40e_vsi_context *ctxt)
-{
- struct i40e_aqc_vsi_properties_data *info = &ctxt->info;
- struct i40e_pf *pf = vsi->back;
- struct i40e_hw *hw = &pf->hw;
- u8 enabled_tc = 0;
-
- if (!(pf->flags & I40E_FLAG_FCOE_ENABLED)) {
- dev_err(&pf->pdev->dev,
- "FCoE is not enabled for this device\n");
- return -EPERM;
- }
-
- /* initialize the hardware for FCoE */
- ctxt->pf_num = hw->pf_id;
- ctxt->vf_num = 0;
- ctxt->uplink_seid = vsi->uplink_seid;
- ctxt->connection_type = I40E_AQ_VSI_CONN_TYPE_NORMAL;
- ctxt->flags = I40E_AQ_VSI_TYPE_PF;
-
- /* FCoE VSI would need the following sections */
- info->valid_sections |= cpu_to_le16(I40E_AQ_VSI_PROP_QUEUE_OPT_VALID);
-
- /* FCoE VSI does not need these sections */
- info->valid_sections &= cpu_to_le16(~(I40E_AQ_VSI_PROP_SECURITY_VALID |
- I40E_AQ_VSI_PROP_VLAN_VALID |
- I40E_AQ_VSI_PROP_CAS_PV_VALID |
- I40E_AQ_VSI_PROP_INGRESS_UP_VALID |
- I40E_AQ_VSI_PROP_EGRESS_UP_VALID));
-
- if (i40e_is_vsi_uplink_mode_veb(vsi)) {
- info->valid_sections |=
- cpu_to_le16(I40E_AQ_VSI_PROP_SWITCH_VALID);
- info->switch_id =
- cpu_to_le16(I40E_AQ_VSI_SW_ID_FLAG_ALLOW_LB);
- }
- enabled_tc = i40e_get_fcoe_tc_map(pf);
- i40e_vsi_setup_queue_map(vsi, ctxt, enabled_tc, true);
-
- /* set up queue option section: only enable FCoE */
- info->queueing_opt_flags = I40E_AQ_VSI_QUE_OPT_FCOE_ENA;
-
- return 0;
-}
-
-/**
- * i40e_fcoe_enable - this is the implementation of ndo_fcoe_enable,
- * indicating the upper FCoE protocol stack is ready to use FCoE
- * offload features.
- *
- * @netdev: pointer to the netdev that FCoE is created on
- *
- * Returns 0 on success
- *
- * in RTNL
- *
- **/
-int i40e_fcoe_enable(struct net_device *netdev)
-{
- struct i40e_netdev_priv *np = netdev_priv(netdev);
- struct i40e_vsi *vsi = np->vsi;
- struct i40e_pf *pf = vsi->back;
- struct i40e_fcoe *fcoe = &pf->fcoe;
-
- if (!(pf->flags & I40E_FLAG_FCOE_ENABLED)) {
- netdev_err(netdev, "HW does not support FCoE.\n");
- return -ENODEV;
- }
-
- if (vsi->type != I40E_VSI_FCOE) {
- netdev_err(netdev, "interface does not support FCoE.\n");
- return -EBUSY;
- }
-
- atomic_inc(&fcoe->refcnt);
-
- return 0;
-}
-
-/**
- * i40e_fcoe_disable- disables FCoE for upper FCoE protocol stack.
- * @dev: pointer to the netdev that FCoE is created on
- *
- * Returns 0 on success
- *
- **/
-int i40e_fcoe_disable(struct net_device *netdev)
-{
- struct i40e_netdev_priv *np = netdev_priv(netdev);
- struct i40e_vsi *vsi = np->vsi;
- struct i40e_pf *pf = vsi->back;
- struct i40e_fcoe *fcoe = &pf->fcoe;
-
- if (!(pf->flags & I40E_FLAG_FCOE_ENABLED)) {
- netdev_err(netdev, "device does not support FCoE\n");
- return -ENODEV;
- }
- if (vsi->type != I40E_VSI_FCOE)
- return -EBUSY;
-
- if (!atomic_dec_and_test(&fcoe->refcnt))
- return -EINVAL;
-
- netdev_info(netdev, "FCoE disabled\n");
-
- return 0;
-}
-
-/**
- * i40e_fcoe_dma_pool_free - free the per cpu pool for FCoE DDP
- * @fcoe: the FCoE sw object
- * @dev: the device that the pool is associated with
- * @cpu: the cpu for this pool
- *
- **/
-static void i40e_fcoe_dma_pool_free(struct i40e_fcoe *fcoe,
- struct device *dev,
- unsigned int cpu)
-{
- struct i40e_fcoe_ddp_pool *ddp_pool;
-
- ddp_pool = per_cpu_ptr(fcoe->ddp_pool, cpu);
- if (!ddp_pool->pool) {
- dev_warn(dev, "DDP pool already freed for cpu %d\n", cpu);
- return;
- }
- dma_pool_destroy(ddp_pool->pool);
- ddp_pool->pool = NULL;
-}
-
-/**
- * i40e_fcoe_dma_pool_create - per cpu pool for FCoE DDP
- * @fcoe: the FCoE sw object
- * @dev: the device that the pool is associated with
- * @cpu: the cpu for this pool
- *
- * Returns 0 on successful or non zero on failure
- *
- **/
-static int i40e_fcoe_dma_pool_create(struct i40e_fcoe *fcoe,
- struct device *dev,
- unsigned int cpu)
-{
- struct i40e_fcoe_ddp_pool *ddp_pool;
- struct dma_pool *pool;
- char pool_name[32];
-
- ddp_pool = per_cpu_ptr(fcoe->ddp_pool, cpu);
- if (ddp_pool && ddp_pool->pool) {
- dev_warn(dev, "DDP pool already allocated for cpu %d\n", cpu);
- return 0;
- }
- snprintf(pool_name, sizeof(pool_name), "i40e_fcoe_ddp_%d", cpu);
- pool = dma_pool_create(pool_name, dev, I40E_FCOE_DDP_PTR_MAX,
- I40E_FCOE_DDP_PTR_ALIGN, PAGE_SIZE);
- if (!pool) {
- dev_err(dev, "dma_pool_create %s failed\n", pool_name);
- return -ENOMEM;
- }
- ddp_pool->pool = pool;
- return 0;
-}
-
-/**
- * i40e_fcoe_free_ddp_resources - release FCoE DDP resources
- * @vsi: the vsi FCoE is associated with
- *
- **/
-void i40e_fcoe_free_ddp_resources(struct i40e_vsi *vsi)
-{
- struct i40e_pf *pf = vsi->back;
- struct i40e_fcoe *fcoe = &pf->fcoe;
- int cpu, i;
-
- /* do nothing if not FCoE VSI */
- if (vsi->type != I40E_VSI_FCOE)
- return;
-
- /* do nothing if no DDP pools were allocated */
- if (!fcoe->ddp_pool)
- return;
-
- for (i = 0; i < I40E_FCOE_DDP_MAX; i++)
- i40e_fcoe_ddp_put(vsi->netdev, i);
-
- for_each_possible_cpu(cpu)
- i40e_fcoe_dma_pool_free(fcoe, &pf->pdev->dev, cpu);
-
- free_percpu(fcoe->ddp_pool);
- fcoe->ddp_pool = NULL;
-
- netdev_info(vsi->netdev, "VSI %d,%d FCoE DDP resources released\n",
- vsi->id, vsi->seid);
-}
-
-/**
- * i40e_fcoe_setup_ddp_resources - allocate per cpu DDP resources
- * @vsi: the VSI FCoE is associated with
- *
- * Returns 0 on successful or non zero on failure
- *
- **/
-int i40e_fcoe_setup_ddp_resources(struct i40e_vsi *vsi)
-{
- struct i40e_pf *pf = vsi->back;
- struct device *dev = &pf->pdev->dev;
- struct i40e_fcoe *fcoe = &pf->fcoe;
- unsigned int cpu;
- int i;
-
- if (vsi->type != I40E_VSI_FCOE)
- return -ENODEV;
-
- /* do nothing if no DDP pools were allocated */
- if (fcoe->ddp_pool)
- return -EEXIST;
-
- /* allocate per CPU memory to track DDP pools */
- fcoe->ddp_pool = alloc_percpu(struct i40e_fcoe_ddp_pool);
- if (!fcoe->ddp_pool) {
- dev_err(&pf->pdev->dev, "failed to allocate percpu DDP\n");
- return -ENOMEM;
- }
-
- /* allocate pci pool for each cpu */
- for_each_possible_cpu(cpu) {
- if (!i40e_fcoe_dma_pool_create(fcoe, dev, cpu))
- continue;
-
- dev_err(dev, "failed to alloc DDP pool on cpu:%d\n", cpu);
- i40e_fcoe_free_ddp_resources(vsi);
- return -ENOMEM;
- }
-
- /* initialize the sw context */
- for (i = 0; i < I40E_FCOE_DDP_MAX; i++)
- i40e_fcoe_ddp_clear(&fcoe->ddp[i]);
-
- netdev_info(vsi->netdev, "VSI %d,%d FCoE DDP resources allocated\n",
- vsi->id, vsi->seid);
-
- return 0;
-}
-
-/**
- * i40e_fcoe_handle_status - check the Programming Status for FCoE
- * @rx_ring: the Rx ring for this descriptor
- * @rx_desc: the Rx descriptor for Programming Status, not a packet descriptor.
- *
- * Check if this is the Rx Programming Status descriptor write-back for FCoE.
- * This is used to verify if the context/filter programming or invalidation
- * requested by SW to the HW is successful or not and take actions accordingly.
- **/
-void i40e_fcoe_handle_status(struct i40e_ring *rx_ring,
- union i40e_rx_desc *rx_desc, u8 prog_id)
-{
- struct i40e_pf *pf = rx_ring->vsi->back;
- struct i40e_fcoe *fcoe = &pf->fcoe;
- struct i40e_fcoe_ddp *ddp;
- u32 error;
- u16 xid;
- u64 qw;
-
- /* we only care for FCoE here */
- if (!i40e_fcoe_progid_is_fcoe(prog_id))
- return;
-
- xid = le32_to_cpu(rx_desc->wb.qword0.hi_dword.fcoe_param) &
- (I40E_FCOE_DDP_MAX - 1);
-
- if (!i40e_fcoe_xid_is_valid(xid))
- return;
-
- ddp = &fcoe->ddp[xid];
- WARN_ON(xid != ddp->xid);
-
- qw = le64_to_cpu(rx_desc->wb.qword1.status_error_len);
- error = (qw & I40E_RX_PROG_STATUS_DESC_QW1_ERROR_MASK) >>
- I40E_RX_PROG_STATUS_DESC_QW1_ERROR_SHIFT;
-
- /* DDP context programming status: failure or success */
- if (prog_id == I40E_RX_PROG_STATUS_DESC_FCOE_CTXT_PROG_STATUS) {
- if (I40E_RX_PROG_FCOE_ERROR_TBL_FULL(error)) {
- dev_err(&pf->pdev->dev, "xid %x ddp->xid %x TABLE FULL\n",
- xid, ddp->xid);
- ddp->prerr |= I40E_RX_PROG_FCOE_ERROR_TBL_FULL_BIT;
- }
- if (I40E_RX_PROG_FCOE_ERROR_CONFLICT(error)) {
- dev_err(&pf->pdev->dev, "xid %x ddp->xid %x CONFLICT\n",
- xid, ddp->xid);
- ddp->prerr |= I40E_RX_PROG_FCOE_ERROR_CONFLICT_BIT;
- }
- }
-
- /* DDP context invalidation status: failure or success */
- if (prog_id == I40E_RX_PROG_STATUS_DESC_FCOE_CTXT_INVL_STATUS) {
- if (I40E_RX_PROG_FCOE_ERROR_INVLFAIL(error)) {
- dev_err(&pf->pdev->dev, "xid %x ddp->xid %x INVALIDATION FAILURE\n",
- xid, ddp->xid);
- ddp->prerr |= I40E_RX_PROG_FCOE_ERROR_INVLFAIL_BIT;
- }
- /* clear the flag so we can retry invalidation */
- clear_bit(__I40E_FCOE_DDP_ABORTED, &ddp->flags);
- }
-
- /* unmap DMA */
- i40e_fcoe_ddp_unmap(pf, ddp);
- i40e_fcoe_ddp_clear(ddp);
-}
-
-/**
- * i40e_fcoe_handle_offload - check ddp status and mark it done
- * @adapter: i40e adapter
- * @rx_desc: advanced rx descriptor
- * @skb: the skb holding the received data
- *
- * This checks ddp status.
- *
- * Returns : < 0 indicates an error or not a FCOE ddp, 0 indicates
- * not passing the skb to ULD, > 0 indicates is the length of data
- * being ddped.
- *
- **/
-int i40e_fcoe_handle_offload(struct i40e_ring *rx_ring,
- union i40e_rx_desc *rx_desc,
- struct sk_buff *skb)
-{
- struct i40e_pf *pf = rx_ring->vsi->back;
- struct i40e_fcoe *fcoe = &pf->fcoe;
- struct fc_frame_header *fh = NULL;
- struct i40e_fcoe_ddp *ddp = NULL;
- u32 status, fltstat;
- u32 error, fcerr;
- int rc = -EINVAL;
- u16 ptype;
- u16 xid;
- u64 qw;
-
- /* check this rxd is for programming status */
- qw = le64_to_cpu(rx_desc->wb.qword1.status_error_len);
- /* packet descriptor, check packet type */
- ptype = (qw & I40E_RXD_QW1_PTYPE_MASK) >> I40E_RXD_QW1_PTYPE_SHIFT;
- if (!i40e_rx_is_fcoe(ptype))
- goto out_no_ddp;
-
- error = (qw & I40E_RXD_QW1_ERROR_MASK) >> I40E_RXD_QW1_ERROR_SHIFT;
- fcerr = (error >> I40E_RX_DESC_ERROR_L3L4E_SHIFT) &
- I40E_RX_DESC_FCOE_ERROR_MASK;
-
- /* check stateless offload error */
- if (unlikely(fcerr == I40E_RX_DESC_ERROR_L3L4E_PROT)) {
- dev_err(&pf->pdev->dev, "Protocol Error\n");
- skb->ip_summed = CHECKSUM_NONE;
- } else {
- skb->ip_summed = CHECKSUM_UNNECESSARY;
- }
-
- /* check hw status on ddp */
- status = (qw & I40E_RXD_QW1_STATUS_MASK) >> I40E_RXD_QW1_STATUS_SHIFT;
- fltstat = (status >> I40E_RX_DESC_STATUS_FLTSTAT_SHIFT) &
- I40E_RX_DESC_FLTSTAT_FCMASK;
-
- /* now we are ready to check DDP */
- fh = i40e_fcoe_fc_frame_header(skb);
- xid = i40e_fcoe_fc_get_xid(fh);
- if (!i40e_fcoe_xid_is_valid(xid))
- goto out_no_ddp;
-
- /* non DDP normal receive, return to the protocol stack */
- if (fltstat == I40E_RX_DESC_FLTSTAT_NOMTCH)
- goto out_no_ddp;
-
- /* do we have a sw ddp context setup ? */
- ddp = &fcoe->ddp[xid];
- if (!ddp->sgl)
- goto out_no_ddp;
-
- /* fetch xid from hw rxd wb, which should match up the sw ctxt */
- xid = le16_to_cpu(rx_desc->wb.qword0.lo_dword.mirr_fcoe.fcoe_ctx_id);
- if (ddp->xid != xid) {
- dev_err(&pf->pdev->dev, "xid 0x%x does not match ctx_xid 0x%x\n",
- ddp->xid, xid);
- goto out_put_ddp;
- }
-
- /* the same exchange has already errored out */
- if (ddp->fcerr) {
- dev_err(&pf->pdev->dev, "xid 0x%x fcerr 0x%x reported fcer 0x%x\n",
- xid, ddp->fcerr, fcerr);
- goto out_put_ddp;
- }
-
- /* fcoe param is valid by now with correct DDPed length */
- ddp->len = le32_to_cpu(rx_desc->wb.qword0.hi_dword.fcoe_param);
- ddp->fcerr = fcerr;
- /* header posting only, useful only for target mode and debugging */
- if (fltstat == I40E_RX_DESC_FLTSTAT_DDP) {
- /* For target mode, we get header of the last packet but it
- * does not have the FCoE trailer field, i.e., CRC and EOF
- * Ordered Set since they are offloaded by the HW, so fill
- * it up correspondingly to allow the packet to pass through
- * to the upper protocol stack.
- */
- u32 f_ctl = ntoh24(fh->fh_f_ctl);
-
- if ((f_ctl & FC_FC_END_SEQ) &&
- (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA)) {
- struct fcoe_crc_eof *crc = NULL;
-
- crc = skb_put(skb, sizeof(*crc));
- crc->fcoe_eof = FC_EOF_T;
- } else {
- /* otherwise, drop the header only frame */
- rc = 0;
- goto out_no_ddp;
- }
- }
-
-out_put_ddp:
- /* either we got RSP or we have an error, unmap DMA in both cases */
- i40e_fcoe_ddp_unmap(pf, ddp);
- if (ddp->len && !ddp->fcerr) {
- int pkts;
-
- rc = ddp->len;
- i40e_fcoe_ddp_clear(ddp);
- ddp->len = rc;
- pkts = DIV_ROUND_UP(rc, 2048);
- rx_ring->stats.bytes += rc;
- rx_ring->stats.packets += pkts;
- rx_ring->q_vector->rx.total_bytes += rc;
- rx_ring->q_vector->rx.total_packets += pkts;
- set_bit(__I40E_FCOE_DDP_DONE, &ddp->flags);
- }
-
-out_no_ddp:
- return rc;
-}
-
-/**
- * i40e_fcoe_ddp_setup - called to set up ddp context
- * @netdev: the corresponding net_device
- * @xid: the exchange id requesting ddp
- * @sgl: the scatter-gather list for this request
- * @sgc: the number of scatter-gather items
- * @target_mode: indicates this is a DDP request for target
- *
- * Returns : 1 for success and 0 for no DDP on this I/O
- **/
-static int i40e_fcoe_ddp_setup(struct net_device *netdev, u16 xid,
- struct scatterlist *sgl, unsigned int sgc,
- int target_mode)
-{
- static const unsigned int bufflen = I40E_FCOE_DDP_BUF_MIN;
- struct i40e_netdev_priv *np = netdev_priv(netdev);
- struct i40e_fcoe_ddp_pool *ddp_pool;
- struct i40e_pf *pf = np->vsi->back;
- struct i40e_fcoe *fcoe = &pf->fcoe;
- unsigned int i, j, dmacount;
- struct i40e_fcoe_ddp *ddp;
- unsigned int firstoff = 0;
- unsigned int thisoff = 0;
- unsigned int thislen = 0;
- struct scatterlist *sg;
- dma_addr_t addr = 0;
- unsigned int len;
-
- if (xid >= I40E_FCOE_DDP_MAX) {
- dev_warn(&pf->pdev->dev, "xid=0x%x out-of-range\n", xid);
- return 0;
- }
-
- /* no DDP if we are already down or resetting */
- if (test_bit(__I40E_DOWN, &pf->state) ||
- test_bit(__I40E_NEEDS_RESTART, &pf->state)) {
- dev_info(&pf->pdev->dev, "xid=0x%x device in reset/down\n",
- xid);
- return 0;
- }
-
- ddp = &fcoe->ddp[xid];
- if (ddp->sgl) {
- dev_info(&pf->pdev->dev, "xid 0x%x w/ non-null sgl=%p nents=%d\n",
- xid, ddp->sgl, ddp->sgc);
- return 0;
- }
- i40e_fcoe_ddp_clear(ddp);
-
- if (!fcoe->ddp_pool) {
- dev_info(&pf->pdev->dev, "No DDP pool, xid 0x%x\n", xid);
- return 0;
- }
-
- ddp_pool = per_cpu_ptr(fcoe->ddp_pool, get_cpu());
- if (!ddp_pool->pool) {
- dev_info(&pf->pdev->dev, "No percpu ddp pool, xid 0x%x\n", xid);
- goto out_noddp;
- }
-
- /* setup dma from scsi command sgl */
- dmacount = dma_map_sg(&pf->pdev->dev, sgl, sgc, DMA_FROM_DEVICE);
- if (dmacount == 0) {
- dev_info(&pf->pdev->dev, "dma_map_sg for sgl %p, sgc %d failed\n",
- sgl, sgc);
- goto out_noddp_unmap;
- }
-
- /* alloc the udl from our ddp pool */
- ddp->udl = dma_pool_alloc(ddp_pool->pool, GFP_ATOMIC, &ddp->udp);
- if (!ddp->udl) {
- dev_info(&pf->pdev->dev,
- "Failed allocated ddp context, xid 0x%x\n", xid);
- goto out_noddp_unmap;
- }
-
- j = 0;
- ddp->len = 0;
- for_each_sg(sgl, sg, dmacount, i) {
- addr = sg_dma_address(sg);
- len = sg_dma_len(sg);
- ddp->len += len;
- while (len) {
- /* max number of buffers allowed in one DDP context */
- if (j >= I40E_FCOE_DDP_BUFFCNT_MAX) {
- dev_info(&pf->pdev->dev,
- "xid=%x:%d,%d,%d:addr=%llx not enough descriptors\n",
- xid, i, j, dmacount, (u64)addr);
- goto out_noddp_free;
- }
-
- /* get the offset of length of current buffer */
- thisoff = addr & ((dma_addr_t)bufflen - 1);
- thislen = min_t(unsigned int, (bufflen - thisoff), len);
- /* all but the 1st buffer (j == 0)
- * must be aligned on bufflen
- */
- if ((j != 0) && (thisoff))
- goto out_noddp_free;
-
- /* all but the last buffer
- * ((i == (dmacount - 1)) && (thislen == len))
- * must end at bufflen
- */
- if (((i != (dmacount - 1)) || (thislen != len)) &&
- ((thislen + thisoff) != bufflen))
- goto out_noddp_free;
-
- ddp->udl[j] = (u64)(addr - thisoff);
- /* only the first buffer may have none-zero offset */
- if (j == 0)
- firstoff = thisoff;
- len -= thislen;
- addr += thislen;
- j++;
- }
- }
- /* only the last buffer may have non-full bufflen */
- ddp->lastsize = thisoff + thislen;
- ddp->firstoff = firstoff;
- ddp->list_len = j;
- ddp->pool = ddp_pool->pool;
- ddp->sgl = sgl;
- ddp->sgc = sgc;
- ddp->xid = xid;
- if (target_mode)
- set_bit(__I40E_FCOE_DDP_TARGET, &ddp->flags);
- set_bit(__I40E_FCOE_DDP_INITALIZED, &ddp->flags);
-
- put_cpu();
- return 1; /* Success */
-
-out_noddp_free:
- dma_pool_free(ddp->pool, ddp->udl, ddp->udp);
- i40e_fcoe_ddp_clear(ddp);
-
-out_noddp_unmap:
- dma_unmap_sg(&pf->pdev->dev, sgl, sgc, DMA_FROM_DEVICE);
-out_noddp:
- put_cpu();
- return 0;
-}
-
-/**
- * i40e_fcoe_ddp_get - called to set up ddp context in initiator mode
- * @netdev: the corresponding net_device
- * @xid: the exchange id requesting ddp
- * @sgl: the scatter-gather list for this request
- * @sgc: the number of scatter-gather items
- *
- * This is the implementation of net_device_ops.ndo_fcoe_ddp_setup
- * and is expected to be called from ULD, e.g., FCP layer of libfc
- * to set up ddp for the corresponding xid of the given sglist for
- * the corresponding I/O.
- *
- * Returns : 1 for success and 0 for no ddp
- **/
-static int i40e_fcoe_ddp_get(struct net_device *netdev, u16 xid,
- struct scatterlist *sgl, unsigned int sgc)
-{
- return i40e_fcoe_ddp_setup(netdev, xid, sgl, sgc, 0);
-}
-
-/**
- * i40e_fcoe_ddp_target - called to set up ddp context in target mode
- * @netdev: the corresponding net_device
- * @xid: the exchange id requesting ddp
- * @sgl: the scatter-gather list for this request
- * @sgc: the number of scatter-gather items
- *
- * This is the implementation of net_device_ops.ndo_fcoe_ddp_target
- * and is expected to be called from ULD, e.g., FCP layer of libfc
- * to set up ddp for the corresponding xid of the given sglist for
- * the corresponding I/O. The DDP in target mode is a write I/O request
- * from the initiator.
- *
- * Returns : 1 for success and 0 for no ddp
- **/
-static int i40e_fcoe_ddp_target(struct net_device *netdev, u16 xid,
- struct scatterlist *sgl, unsigned int sgc)
-{
- return i40e_fcoe_ddp_setup(netdev, xid, sgl, sgc, 1);
-}
-
-/**
- * i40e_fcoe_program_ddp - programs the HW DDP related descriptors
- * @tx_ring: transmit ring for this packet
- * @skb: the packet to be sent out
- * @sof: the SOF to indicate class of service
- *
- * Determine if it is READ/WRITE command, and finds out if there is
- * a matching SW DDP context for this command. DDP is applicable
- * only in case of READ if initiator or WRITE in case of
- * responder (via checking XFER_RDY).
- *
- * Note: caller checks sof and ddp sw context
- *
- * Returns : none
- *
- **/
-static void i40e_fcoe_program_ddp(struct i40e_ring *tx_ring,
- struct sk_buff *skb,
- struct i40e_fcoe_ddp *ddp, u8 sof)
-{
- struct i40e_fcoe_filter_context_desc *filter_desc = NULL;
- struct i40e_fcoe_queue_context_desc *queue_desc = NULL;
- struct i40e_fcoe_ddp_context_desc *ddp_desc = NULL;
- struct i40e_pf *pf = tx_ring->vsi->back;
- u16 i = tx_ring->next_to_use;
- struct fc_frame_header *fh;
- u64 flags_rsvd_lanq = 0;
- bool target_mode;
-
- /* check if abort is still pending */
- if (test_bit(__I40E_FCOE_DDP_ABORTED, &ddp->flags)) {
- dev_warn(&pf->pdev->dev,
- "DDP abort is still pending xid:%hx and ddp->flags:%lx:\n",
- ddp->xid, ddp->flags);
- return;
- }
-
- /* set the flag to indicate this is programmed */
- if (test_and_set_bit(__I40E_FCOE_DDP_PROGRAMMED, &ddp->flags)) {
- dev_warn(&pf->pdev->dev,
- "DDP is already programmed for xid:%hx and ddp->flags:%lx:\n",
- ddp->xid, ddp->flags);
- return;
- }
-
- /* Prepare the DDP context descriptor */
- ddp_desc = I40E_DDP_CONTEXT_DESC(tx_ring, i);
- i++;
- if (i == tx_ring->count)
- i = 0;
-
- ddp_desc->type_cmd_foff_lsize =
- cpu_to_le64(I40E_TX_DESC_DTYPE_DDP_CTX |
- ((u64)I40E_FCOE_DDP_CTX_DESC_BSIZE_4K <<
- I40E_FCOE_DDP_CTX_QW1_CMD_SHIFT) |
- ((u64)ddp->firstoff <<
- I40E_FCOE_DDP_CTX_QW1_FOFF_SHIFT) |
- ((u64)ddp->lastsize <<
- I40E_FCOE_DDP_CTX_QW1_LSIZE_SHIFT));
- ddp_desc->rsvd = cpu_to_le64(0);
-
- /* target mode needs last packet in the sequence */
- target_mode = test_bit(__I40E_FCOE_DDP_TARGET, &ddp->flags);
- if (target_mode)
- ddp_desc->type_cmd_foff_lsize |=
- cpu_to_le64(I40E_FCOE_DDP_CTX_DESC_LASTSEQH);
-
- /* Prepare queue_context descriptor */
- queue_desc = I40E_QUEUE_CONTEXT_DESC(tx_ring, i++);
- if (i == tx_ring->count)
- i = 0;
- queue_desc->dmaindx_fbase = cpu_to_le64(ddp->xid | ((u64)ddp->udp));
- queue_desc->flen_tph = cpu_to_le64(ddp->list_len |
- ((u64)(I40E_FCOE_QUEUE_CTX_DESC_TPHRDESC |
- I40E_FCOE_QUEUE_CTX_DESC_TPHDATA) <<
- I40E_FCOE_QUEUE_CTX_QW1_TPH_SHIFT));
-
- /* Prepare filter_context_desc */
- filter_desc = I40E_FILTER_CONTEXT_DESC(tx_ring, i);
- i++;
- if (i == tx_ring->count)
- i = 0;
-
- fh = (struct fc_frame_header *)skb_transport_header(skb);
- filter_desc->param = cpu_to_le32(ntohl(fh->fh_parm_offset));
- filter_desc->seqn = cpu_to_le16(ntohs(fh->fh_seq_cnt));
- filter_desc->rsvd_dmaindx = cpu_to_le16(ddp->xid <<
- I40E_FCOE_FILTER_CTX_QW0_DMAINDX_SHIFT);
-
- flags_rsvd_lanq = I40E_FCOE_FILTER_CTX_DESC_CTYP_DDP;
- flags_rsvd_lanq |= (u64)(target_mode ?
- I40E_FCOE_FILTER_CTX_DESC_ENODE_RSP :
- I40E_FCOE_FILTER_CTX_DESC_ENODE_INIT);
-
- flags_rsvd_lanq |= (u64)((sof == FC_SOF_I2 || sof == FC_SOF_N2) ?
- I40E_FCOE_FILTER_CTX_DESC_FC_CLASS2 :
- I40E_FCOE_FILTER_CTX_DESC_FC_CLASS3);
-
- flags_rsvd_lanq |= ((u64)skb->queue_mapping <<
- I40E_FCOE_FILTER_CTX_QW1_LANQINDX_SHIFT);
- filter_desc->flags_rsvd_lanq = cpu_to_le64(flags_rsvd_lanq);
-
- /* By this time, all offload related descriptors has been programmed */
- tx_ring->next_to_use = i;
-}
-
-/**
- * i40e_fcoe_invalidate_ddp - invalidates DDP in case of abort
- * @tx_ring: transmit ring for this packet
- * @skb: the packet associated w/ this DDP invalidation, i.e., ABTS
- * @ddp: the SW DDP context for this DDP
- *
- * Programs the Tx context descriptor to do DDP invalidation.
- **/
-static void i40e_fcoe_invalidate_ddp(struct i40e_ring *tx_ring,
- struct sk_buff *skb,
- struct i40e_fcoe_ddp *ddp)
-{
- struct i40e_tx_context_desc *context_desc;
- int i;
-
- if (test_and_set_bit(__I40E_FCOE_DDP_ABORTED, &ddp->flags))
- return;
-
- i = tx_ring->next_to_use;
- context_desc = I40E_TX_CTXTDESC(tx_ring, i);
- i++;
- if (i == tx_ring->count)
- i = 0;
-
- context_desc->tunneling_params = cpu_to_le32(0);
- context_desc->l2tag2 = cpu_to_le16(0);
- context_desc->rsvd = cpu_to_le16(0);
- context_desc->type_cmd_tso_mss = cpu_to_le64(
- I40E_TX_DESC_DTYPE_FCOE_CTX |
- (I40E_FCOE_TX_CTX_DESC_OPCODE_DDP_CTX_INVL <<
- I40E_TXD_CTX_QW1_CMD_SHIFT) |
- (I40E_FCOE_TX_CTX_DESC_OPCODE_SINGLE_SEND <<
- I40E_TXD_CTX_QW1_CMD_SHIFT));
- tx_ring->next_to_use = i;
-}
-
-/**
- * i40e_fcoe_handle_ddp - check we should setup or invalidate DDP
- * @tx_ring: transmit ring for this packet
- * @skb: the packet to be sent out
- * @sof: the SOF to indicate class of service
- *
- * Determine if it is ABTS/READ/XFER_RDY, and finds out if there is
- * a matching SW DDP context for this command. DDP is applicable
- * only in case of READ if initiator or WRITE in case of
- * responder (via checking XFER_RDY). In case this is an ABTS, send
- * just invalidate the context.
- **/
-static void i40e_fcoe_handle_ddp(struct i40e_ring *tx_ring,
- struct sk_buff *skb, u8 sof)
-{
- struct i40e_pf *pf = tx_ring->vsi->back;
- struct i40e_fcoe *fcoe = &pf->fcoe;
- struct fc_frame_header *fh;
- struct i40e_fcoe_ddp *ddp;
- u32 f_ctl;
- u8 r_ctl;
- u16 xid;
-
- fh = (struct fc_frame_header *)skb_transport_header(skb);
- f_ctl = ntoh24(fh->fh_f_ctl);
- r_ctl = fh->fh_r_ctl;
- ddp = NULL;
-
- if ((r_ctl == FC_RCTL_DD_DATA_DESC) && (f_ctl & FC_FC_EX_CTX)) {
- /* exchange responder? if so, XFER_RDY for write */
- xid = ntohs(fh->fh_rx_id);
- if (i40e_fcoe_xid_is_valid(xid)) {
- ddp = &fcoe->ddp[xid];
- if ((ddp->xid == xid) &&
- (test_bit(__I40E_FCOE_DDP_TARGET, &ddp->flags)))
- i40e_fcoe_program_ddp(tx_ring, skb, ddp, sof);
- }
- } else if (r_ctl == FC_RCTL_DD_UNSOL_CMD) {
- /* exchange originator, check READ cmd */
- xid = ntohs(fh->fh_ox_id);
- if (i40e_fcoe_xid_is_valid(xid)) {
- ddp = &fcoe->ddp[xid];
- if ((ddp->xid == xid) &&
- (!test_bit(__I40E_FCOE_DDP_TARGET, &ddp->flags)))
- i40e_fcoe_program_ddp(tx_ring, skb, ddp, sof);
- }
- } else if (r_ctl == FC_RCTL_BA_ABTS) {
- /* exchange originator, check ABTS */
- xid = ntohs(fh->fh_ox_id);
- if (i40e_fcoe_xid_is_valid(xid)) {
- ddp = &fcoe->ddp[xid];
- if ((ddp->xid == xid) &&
- (!test_bit(__I40E_FCOE_DDP_TARGET, &ddp->flags)))
- i40e_fcoe_invalidate_ddp(tx_ring, skb, ddp);
- }
- }
-}
-
-/**
- * i40e_fcoe_tso - set up FCoE TSO
- * @tx_ring: ring to send buffer on
- * @skb: send buffer
- * @tx_flags: collected send information
- * @hdr_len: the tso header length
- * @sof: the SOF to indicate class of service
- *
- * Note must already have sof checked to be either class 2 or class 3 before
- * calling this function.
- *
- * Returns 1 to indicate sequence segmentation offload is properly setup
- * or returns 0 to indicate no tso is needed, otherwise returns error
- * code to drop the frame.
- **/
-static int i40e_fcoe_tso(struct i40e_ring *tx_ring,
- struct sk_buff *skb,
- u32 tx_flags, u8 *hdr_len, u8 sof)
-{
- struct i40e_tx_context_desc *context_desc;
- u32 cd_type, cd_cmd, cd_tso_len, cd_mss;
- struct fc_frame_header *fh;
- u64 cd_type_cmd_tso_mss;
-
- /* must match gso type as FCoE */
- if (!skb_is_gso(skb))
- return 0;
-
- /* is it the expected gso type for FCoE ?*/
- if (skb_shinfo(skb)->gso_type != SKB_GSO_FCOE) {
- netdev_err(skb->dev,
- "wrong gso type %d:expecting SKB_GSO_FCOE\n",
- skb_shinfo(skb)->gso_type);
- return -EINVAL;
- }
-
- /* header and trailer are inserted by hw */
- *hdr_len = skb_transport_offset(skb) + sizeof(struct fc_frame_header) +
- sizeof(struct fcoe_crc_eof);
-
- /* check sof to decide a class 2 or 3 TSO */
- if (likely(i40e_fcoe_sof_is_class3(sof)))
- cd_cmd = I40E_FCOE_TX_CTX_DESC_OPCODE_TSO_FC_CLASS3;
- else
- cd_cmd = I40E_FCOE_TX_CTX_DESC_OPCODE_TSO_FC_CLASS2;
-
- /* param field valid? */
- fh = (struct fc_frame_header *)skb_transport_header(skb);
- if (fh->fh_f_ctl[2] & FC_FC_REL_OFF)
- cd_cmd |= I40E_FCOE_TX_CTX_DESC_RELOFF;
-
- /* fill the field values */
- cd_type = I40E_TX_DESC_DTYPE_FCOE_CTX;
- cd_tso_len = skb->len - *hdr_len;
- cd_mss = skb_shinfo(skb)->gso_size;
- cd_type_cmd_tso_mss =
- ((u64)cd_type << I40E_TXD_CTX_QW1_DTYPE_SHIFT) |
- ((u64)cd_cmd << I40E_TXD_CTX_QW1_CMD_SHIFT) |
- ((u64)cd_tso_len << I40E_TXD_CTX_QW1_TSO_LEN_SHIFT) |
- ((u64)cd_mss << I40E_TXD_CTX_QW1_MSS_SHIFT);
-
- /* grab the next descriptor */
- context_desc = I40E_TX_CTXTDESC(tx_ring, tx_ring->next_to_use);
- tx_ring->next_to_use++;
- if (tx_ring->next_to_use == tx_ring->count)
- tx_ring->next_to_use = 0;
-
- context_desc->tunneling_params = 0;
- context_desc->l2tag2 = cpu_to_le16((tx_flags & I40E_TX_FLAGS_VLAN_MASK)
- >> I40E_TX_FLAGS_VLAN_SHIFT);
- context_desc->type_cmd_tso_mss = cpu_to_le64(cd_type_cmd_tso_mss);
-
- return 1;
-}
-
-/**
- * i40e_fcoe_tx_map - build the tx descriptor
- * @tx_ring: ring to send buffer on
- * @skb: send buffer
- * @first: first buffer info buffer to use
- * @tx_flags: collected send information
- * @hdr_len: ptr to the size of the packet header
- * @eof: the frame eof value
- *
- * Note, for FCoE, sof and eof are already checked
- **/
-static void i40e_fcoe_tx_map(struct i40e_ring *tx_ring,
- struct sk_buff *skb,
- struct i40e_tx_buffer *first,
- u32 tx_flags, u8 hdr_len, u8 eof)
-{
- u32 td_offset = 0;
- u32 td_cmd = 0;
- u32 maclen;
-
- /* insert CRC */
- td_cmd = I40E_TX_DESC_CMD_ICRC;
-
- /* setup MACLEN */
- maclen = skb_network_offset(skb);
- if (tx_flags & I40E_TX_FLAGS_SW_VLAN)
- maclen += sizeof(struct vlan_hdr);
-
- if (skb->protocol == htons(ETH_P_FCOE)) {
- /* for FCoE, maclen should exclude ether type */
- maclen -= 2;
- /* setup type as FCoE and EOF insertion */
- td_cmd |= (I40E_TX_DESC_CMD_FCOET | i40e_fcoe_ctxt_eof(eof));
- /* setup FCoELEN and FCLEN */
- td_offset |= ((((sizeof(struct fcoe_hdr) + 2) >> 2) <<
- I40E_TX_DESC_LENGTH_IPLEN_SHIFT) |
- ((sizeof(struct fc_frame_header) >> 2) <<
- I40E_TX_DESC_LENGTH_L4_FC_LEN_SHIFT));
- /* trim to exclude trailer */
- pskb_trim(skb, skb->len - sizeof(struct fcoe_crc_eof));
- }
-
- /* MACLEN is ether header length in words not bytes */
- td_offset |= (maclen >> 1) << I40E_TX_DESC_LENGTH_MACLEN_SHIFT;
-
- i40e_tx_map(tx_ring, skb, first, tx_flags, hdr_len, td_cmd, td_offset);
-}
-
-/**
- * i40e_fcoe_set_skb_header - adjust skb header point for FIP/FCoE/FC
- * @skb: the skb to be adjusted
- *
- * Returns true if this skb is a FCoE/FIP or VLAN carried FCoE/FIP and then
- * adjusts the skb header pointers correspondingly. Otherwise, returns false.
- **/
-static inline int i40e_fcoe_set_skb_header(struct sk_buff *skb)
-{
- __be16 protocol = skb->protocol;
-
- skb_reset_mac_header(skb);
- skb->mac_len = sizeof(struct ethhdr);
- if (protocol == htons(ETH_P_8021Q)) {
- struct vlan_ethhdr *veth = (struct vlan_ethhdr *)eth_hdr(skb);
-
- protocol = veth->h_vlan_encapsulated_proto;
- skb->mac_len += sizeof(struct vlan_hdr);
- }
-
- /* FCoE or FIP only */
- if ((protocol != htons(ETH_P_FIP)) &&
- (protocol != htons(ETH_P_FCOE)))
- return -EINVAL;
-
- /* set header to L2 of FCoE/FIP */
- skb_set_network_header(skb, skb->mac_len);
- if (protocol == htons(ETH_P_FIP))
- return 0;
-
- /* set header to L3 of FC */
- skb_set_transport_header(skb, skb->mac_len + sizeof(struct fcoe_hdr));
- return 0;
-}
-
-/**
- * i40e_fcoe_xmit_frame - transmit buffer
- * @skb: send buffer
- * @netdev: the fcoe netdev
- *
- * Returns 0 if sent, else an error code
- **/
-static netdev_tx_t i40e_fcoe_xmit_frame(struct sk_buff *skb,
- struct net_device *netdev)
-{
- struct i40e_netdev_priv *np = netdev_priv(skb->dev);
- struct i40e_vsi *vsi = np->vsi;
- struct i40e_ring *tx_ring = vsi->tx_rings[skb->queue_mapping];
- struct i40e_tx_buffer *first;
- u32 tx_flags = 0;
- int fso, count;
- u8 hdr_len = 0;
- u8 sof = 0;
- u8 eof = 0;
-
- if (i40e_fcoe_set_skb_header(skb))
- goto out_drop;
-
- count = i40e_xmit_descriptor_count(skb);
- if (i40e_chk_linearize(skb, count)) {
- if (__skb_linearize(skb))
- goto out_drop;
- count = i40e_txd_use_count(skb->len);
- tx_ring->tx_stats.tx_linearize++;
- }
-
- /* need: 1 descriptor per page * PAGE_SIZE/I40E_MAX_DATA_PER_TXD,
- * + 1 desc for skb_head_len/I40E_MAX_DATA_PER_TXD,
- * + 4 desc gap to avoid the cache line where head is,
- * + 1 desc for context descriptor,
- * otherwise try next time
- */
- if (i40e_maybe_stop_tx(tx_ring, count + 4 + 1)) {
- tx_ring->tx_stats.tx_busy++;
- return NETDEV_TX_BUSY;
- }
-
- /* prepare the xmit flags */
- if (i40e_tx_prepare_vlan_flags(skb, tx_ring, &tx_flags))
- goto out_drop;
-
- /* record the location of the first descriptor for this packet */
- first = &tx_ring->tx_bi[tx_ring->next_to_use];
-
- /* FIP is a regular L2 traffic w/o offload */
- if (skb->protocol == htons(ETH_P_FIP))
- goto out_send;
-
- /* check sof and eof, only supports FC Class 2 or 3 */
- if (i40e_fcoe_fc_sof(skb, &sof) || i40e_fcoe_fc_eof(skb, &eof)) {
- netdev_err(netdev, "SOF/EOF error:%02x - %02x\n", sof, eof);
- goto out_drop;
- }
-
- /* always do FCCRC for FCoE */
- tx_flags |= I40E_TX_FLAGS_FCCRC;
-
- /* check we should do sequence offload */
- fso = i40e_fcoe_tso(tx_ring, skb, tx_flags, &hdr_len, sof);
- if (fso < 0)
- goto out_drop;
- else if (fso)
- tx_flags |= I40E_TX_FLAGS_FSO;
- else
- i40e_fcoe_handle_ddp(tx_ring, skb, sof);
-
-out_send:
- /* send out the packet */
- i40e_fcoe_tx_map(tx_ring, skb, first, tx_flags, hdr_len, eof);
-
- i40e_maybe_stop_tx(tx_ring, DESC_NEEDED);
- return NETDEV_TX_OK;
-
-out_drop:
- dev_kfree_skb_any(skb);
- return NETDEV_TX_OK;
-}
-
-/**
- * i40e_fcoe_change_mtu - NDO callback to change the Maximum Transfer Unit
- * @netdev: network interface device structure
- * @new_mtu: new value for maximum frame size
- *
- * Returns error as operation not permitted
- *
- **/
-static int i40e_fcoe_change_mtu(struct net_device *netdev, int new_mtu)
-{
- netdev_warn(netdev, "MTU change is not supported on FCoE interfaces\n");
- return -EPERM;
-}
-
-/**
- * i40e_fcoe_set_features - set the netdev feature flags
- * @netdev: ptr to the netdev being adjusted
- * @features: the feature set that the stack is suggesting
- *
- **/
-static int i40e_fcoe_set_features(struct net_device *netdev,
- netdev_features_t features)
-{
- struct i40e_netdev_priv *np = netdev_priv(netdev);
- struct i40e_vsi *vsi = np->vsi;
-
- if (features & NETIF_F_HW_VLAN_CTAG_RX)
- i40e_vlan_stripping_enable(vsi);
- else
- i40e_vlan_stripping_disable(vsi);
-
- return 0;
-}
-
-static const struct net_device_ops i40e_fcoe_netdev_ops = {
- .ndo_open = i40e_open,
- .ndo_stop = i40e_close,
- .ndo_get_stats64 = i40e_get_netdev_stats_struct,
- .ndo_set_rx_mode = i40e_set_rx_mode,
- .ndo_validate_addr = eth_validate_addr,
- .ndo_set_mac_address = i40e_set_mac,
- .ndo_change_mtu = i40e_fcoe_change_mtu,
- .ndo_do_ioctl = i40e_ioctl,
- .ndo_tx_timeout = i40e_tx_timeout,
- .ndo_vlan_rx_add_vid = i40e_vlan_rx_add_vid,
- .ndo_vlan_rx_kill_vid = i40e_vlan_rx_kill_vid,
- .ndo_setup_tc = __i40e_setup_tc,
-
-#ifdef CONFIG_NET_POLL_CONTROLLER
- .ndo_poll_controller = i40e_netpoll,
-#endif
- .ndo_start_xmit = i40e_fcoe_xmit_frame,
- .ndo_fcoe_enable = i40e_fcoe_enable,
- .ndo_fcoe_disable = i40e_fcoe_disable,
- .ndo_fcoe_ddp_setup = i40e_fcoe_ddp_get,
- .ndo_fcoe_ddp_done = i40e_fcoe_ddp_put,
- .ndo_fcoe_ddp_target = i40e_fcoe_ddp_target,
- .ndo_set_features = i40e_fcoe_set_features,
-};
-
-/* fcoe network device type */
-static struct device_type fcoe_netdev_type = {
- .name = "fcoe",
-};
-
-/**
- * i40e_fcoe_config_netdev - prepares the VSI context for creating a FCoE VSI
- * @vsi: pointer to the associated VSI struct
- * @ctxt: pointer to the associated VSI context to be passed to HW
- *
- * Returns 0 on success or < 0 on error
- **/
-void i40e_fcoe_config_netdev(struct net_device *netdev, struct i40e_vsi *vsi)
-{
- struct i40e_hw *hw = &vsi->back->hw;
- struct i40e_pf *pf = vsi->back;
-
- if (vsi->type != I40E_VSI_FCOE)
- return;
-
- netdev->features = (NETIF_F_HW_VLAN_CTAG_TX |
- NETIF_F_HW_VLAN_CTAG_RX |
- NETIF_F_HW_VLAN_CTAG_FILTER);
-
- netdev->vlan_features = netdev->features;
- netdev->vlan_features &= ~(NETIF_F_HW_VLAN_CTAG_TX |
- NETIF_F_HW_VLAN_CTAG_RX |
- NETIF_F_HW_VLAN_CTAG_FILTER);
- netdev->fcoe_ddp_xid = I40E_FCOE_DDP_MAX - 1;
- netdev->features |= NETIF_F_ALL_FCOE;
- netdev->vlan_features |= NETIF_F_ALL_FCOE;
- netdev->hw_features |= netdev->features;
- netdev->priv_flags |= IFF_UNICAST_FLT;
- netdev->priv_flags |= IFF_SUPP_NOFCS;
-
- strlcpy(netdev->name, "fcoe%d", IFNAMSIZ-1);
- netdev->mtu = FCOE_MTU;
- SET_NETDEV_DEV(netdev, &pf->pdev->dev);
- SET_NETDEV_DEVTYPE(netdev, &fcoe_netdev_type);
- /* set different dev_port value 1 for FCoE netdev than the default
- * zero dev_port value for PF netdev, this helps biosdevname user
- * tool to differentiate them correctly while both attached to the
- * same PCI function.
- */
- netdev->dev_port = 1;
- spin_lock_bh(&vsi->mac_filter_hash_lock);
- i40e_add_filter(vsi, hw->mac.san_addr, 0);
- i40e_add_filter(vsi, (u8[6]) FC_FCOE_FLOGI_MAC, 0);
- i40e_add_filter(vsi, FIP_ALL_FCOE_MACS, 0);
- i40e_add_filter(vsi, FIP_ALL_ENODE_MACS, 0);
- spin_unlock_bh(&vsi->mac_filter_hash_lock);
-
- /* use san mac */
- ether_addr_copy(netdev->dev_addr, hw->mac.san_addr);
- ether_addr_copy(netdev->perm_addr, hw->mac.san_addr);
- /* fcoe netdev ops */
- netdev->netdev_ops = &i40e_fcoe_netdev_ops;
-}
-
-/**
- * i40e_fcoe_vsi_setup - allocate and set up FCoE VSI
- * @pf: the PF that VSI is associated with
- *
- **/
-void i40e_fcoe_vsi_setup(struct i40e_pf *pf)
-{
- struct i40e_vsi *vsi;
- u16 seid;
- int i;
-
- if (!(pf->flags & I40E_FLAG_FCOE_ENABLED))
- return;
-
- for (i = 0; i < pf->num_alloc_vsi; i++) {
- vsi = pf->vsi[i];
- if (vsi && vsi->type == I40E_VSI_FCOE) {
- dev_warn(&pf->pdev->dev,
- "FCoE VSI already created\n");
- return;
- }
- }
-
- seid = pf->vsi[pf->lan_vsi]->seid;
- vsi = i40e_vsi_setup(pf, I40E_VSI_FCOE, seid, 0);
- if (vsi) {
- dev_dbg(&pf->pdev->dev,
- "Successfully created FCoE VSI seid %d id %d uplink_seid %d PF seid %d\n",
- vsi->seid, vsi->id, vsi->uplink_seid, seid);
- } else {
- dev_info(&pf->pdev->dev, "Failed to create FCoE VSI\n");
- }
-}
diff --git a/drivers/net/ethernet/intel/i40e/i40e_fcoe.h b/drivers/net/ethernet/intel/i40e/i40e_fcoe.h
deleted file mode 100644
index a93174ddeaba..000000000000
--- a/drivers/net/ethernet/intel/i40e/i40e_fcoe.h
+++ /dev/null
@@ -1,127 +0,0 @@
-/*******************************************************************************
- *
- * Intel Ethernet Controller XL710 Family Linux Driver
- * Copyright(c) 2013 - 2014 Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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, see <http://www.gnu.org/licenses/>.
- *
- * The full GNU General Public License is included in this distribution in
- * the file called "COPYING".
- *
- * Contact Information:
- * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- ******************************************************************************/
-
-#ifndef _I40E_FCOE_H_
-#define _I40E_FCOE_H_
-
-/* FCoE HW context helper macros */
-#define I40E_DDP_CONTEXT_DESC(R, i) \
- (&(((struct i40e_fcoe_ddp_context_desc *)((R)->desc))[i]))
-
-#define I40E_QUEUE_CONTEXT_DESC(R, i) \
- (&(((struct i40e_fcoe_queue_context_desc *)((R)->desc))[i]))
-
-#define I40E_FILTER_CONTEXT_DESC(R, i) \
- (&(((struct i40e_fcoe_filter_context_desc *)((R)->desc))[i]))
-
-/* receive queue descriptor filter status for FCoE */
-#define I40E_RX_DESC_FLTSTAT_FCMASK 0x3
-#define I40E_RX_DESC_FLTSTAT_NOMTCH 0x0 /* no ddp context match */
-#define I40E_RX_DESC_FLTSTAT_NODDP 0x1 /* no ddp due to error */
-#define I40E_RX_DESC_FLTSTAT_DDP 0x2 /* DDPed payload, post header */
-#define I40E_RX_DESC_FLTSTAT_FCPRSP 0x3 /* FCP_RSP */
-
-/* receive queue descriptor error codes for FCoE */
-#define I40E_RX_DESC_FCOE_ERROR_MASK \
- (I40E_RX_DESC_ERROR_L3L4E_PROT | \
- I40E_RX_DESC_ERROR_L3L4E_FC | \
- I40E_RX_DESC_ERROR_L3L4E_DMAC_ERR | \
- I40E_RX_DESC_ERROR_L3L4E_DMAC_WARN)
-
-/* receive queue descriptor programming error */
-#define I40E_RX_PROG_FCOE_ERROR_TBL_FULL(e) \
- (((e) >> I40E_RX_PROG_STATUS_DESC_FCOE_TBL_FULL_SHIFT) & 0x1)
-
-#define I40E_RX_PROG_FCOE_ERROR_CONFLICT(e) \
- (((e) >> I40E_RX_PROG_STATUS_DESC_FCOE_CONFLICT_SHIFT) & 0x1)
-
-#define I40E_RX_PROG_FCOE_ERROR_TBL_FULL_BIT \
- BIT(I40E_RX_PROG_STATUS_DESC_FCOE_TBL_FULL_SHIFT)
-#define I40E_RX_PROG_FCOE_ERROR_CONFLICT_BIT \
- BIT(I40E_RX_PROG_STATUS_DESC_FCOE_CONFLICT_SHIFT)
-
-#define I40E_RX_PROG_FCOE_ERROR_INVLFAIL(e) \
- I40E_RX_PROG_FCOE_ERROR_CONFLICT(e)
-#define I40E_RX_PROG_FCOE_ERROR_INVLFAIL_BIT \
- I40E_RX_PROG_FCOE_ERROR_CONFLICT_BIT
-
-/* FCoE DDP related definitions */
-#define I40E_FCOE_MIN_XID 0x0000 /* the min xid supported by fcoe_sw */
-#define I40E_FCOE_MAX_XID 0x0FFF /* the max xid supported by fcoe_sw */
-#define I40E_FCOE_DDP_BUFFCNT_MAX 512 /* 9 bits bufcnt */
-#define I40E_FCOE_DDP_PTR_ALIGN 16
-#define I40E_FCOE_DDP_PTR_MAX (I40E_FCOE_DDP_BUFFCNT_MAX * sizeof(dma_addr_t))
-#define I40E_FCOE_DDP_BUF_MIN 4096
-#define I40E_FCOE_DDP_MAX 2048
-#define I40E_FCOE_FILTER_CTX_QW1_PCTYPE_SHIFT 8
-
-/* supported netdev features for FCoE */
-#define I40E_FCOE_NETIF_FEATURES (NETIF_F_ALL_FCOE | \
- NETIF_F_HW_VLAN_CTAG_TX | \
- NETIF_F_HW_VLAN_CTAG_RX | \
- NETIF_F_HW_VLAN_CTAG_FILTER)
-
-/* DDP context flags */
-enum i40e_fcoe_ddp_flags {
- __I40E_FCOE_DDP_NONE = 1,
- __I40E_FCOE_DDP_TARGET,
- __I40E_FCOE_DDP_INITALIZED,
- __I40E_FCOE_DDP_PROGRAMMED,
- __I40E_FCOE_DDP_DONE,
- __I40E_FCOE_DDP_ABORTED,
- __I40E_FCOE_DDP_UNMAPPED,
-};
-
-/* DDP SW context struct */
-struct i40e_fcoe_ddp {
- int len;
- u16 xid;
- u16 firstoff;
- u16 lastsize;
- u16 list_len;
- u8 fcerr;
- u8 prerr;
- unsigned long flags;
- unsigned int sgc;
- struct scatterlist *sgl;
- dma_addr_t udp;
- u64 *udl;
- struct dma_pool *pool;
-
-};
-
-struct i40e_fcoe_ddp_pool {
- struct dma_pool *pool;
-};
-
-struct i40e_fcoe {
- unsigned long mode;
- atomic_t refcnt;
- struct i40e_fcoe_ddp_pool __percpu *ddp_pool;
- struct i40e_fcoe_ddp ddp[I40E_FCOE_DDP_MAX];
-};
-
-#endif /* _I40E_FCOE_H_ */
diff --git a/drivers/net/ethernet/intel/i40e/i40e_hmc.c b/drivers/net/ethernet/intel/i40e/i40e_hmc.c
index a7c7b1d9b7c8..6d4b590f851b 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_hmc.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_hmc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
diff --git a/drivers/net/ethernet/intel/i40e/i40e_hmc.h b/drivers/net/ethernet/intel/i40e/i40e_hmc.h
index d90669211392..7b5fd33d70ae 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_hmc.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_hmc.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
diff --git a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c
index daa9204426d4..cd40dc487b38 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
diff --git a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.h b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.h
index e74128db5be5..79e1396735d9 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index e31adbc75f9c..16229998fb1e 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
@@ -69,12 +70,6 @@ static int i40e_reset(struct i40e_pf *pf);
static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired);
static void i40e_fdir_sb_setup(struct i40e_pf *pf);
static int i40e_veb_get_bw_info(struct i40e_veb *veb);
-static int i40e_add_del_cloud_filter(struct i40e_vsi *vsi,
- struct i40e_cloud_filter *filter,
- bool add);
-static int i40e_add_del_cloud_filter_big_buf(struct i40e_vsi *vsi,
- struct i40e_cloud_filter *filter,
- bool add);
static int i40e_get_capabilities(struct i40e_pf *pf,
enum i40e_admin_queue_opc list_type);
@@ -215,8 +210,8 @@ static int i40e_get_lump(struct i40e_pf *pf, struct i40e_lump_tracking *pile,
if (!pile || needed == 0 || id >= I40E_PILE_VALID_BIT) {
dev_info(&pf->pdev->dev,
- "param err: pile=%p needed=%d id=0x%04x\n",
- pile, needed, id);
+ "param err: pile=%s needed=%d id=0x%04x\n",
+ pile ? "<valid>" : "<null>", needed, id);
return -EINVAL;
}
@@ -1088,13 +1083,13 @@ static void i40e_update_pf_stats(struct i40e_pf *pf)
&osd->rx_lpi_count, &nsd->rx_lpi_count);
if (pf->flags & I40E_FLAG_FD_SB_ENABLED &&
- !(pf->flags & I40E_FLAG_FD_SB_AUTO_DISABLED))
+ !test_bit(__I40E_FD_SB_AUTO_DISABLED, pf->state))
nsd->fd_sb_status = true;
else
nsd->fd_sb_status = false;
if (pf->flags & I40E_FLAG_FD_ATR_ENABLED &&
- !(pf->flags & I40E_FLAG_FD_ATR_AUTO_DISABLED))
+ !test_bit(__I40E_FD_ATR_AUTO_DISABLED, pf->state))
nsd->fd_atr_status = true;
else
nsd->fd_atr_status = false;
@@ -1380,21 +1375,14 @@ struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi,
ether_addr_copy(f->macaddr, macaddr);
f->vlan = vlan;
- /* If we're in overflow promisc mode, set the state directly
- * to failed, so we don't bother to try sending the filter
- * to the hardware.
- */
- if (test_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state))
- f->state = I40E_FILTER_FAILED;
- else
- f->state = I40E_FILTER_NEW;
+ f->state = I40E_FILTER_NEW;
INIT_HLIST_NODE(&f->hlist);
key = i40e_addr_to_hkey(macaddr);
hash_add(vsi->mac_filter_hash, &f->hlist, key);
vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED;
- vsi->back->flags |= I40E_FLAG_FILTER_SYNC;
+ set_bit(__I40E_MACVLAN_SYNC_PENDING, vsi->back->state);
}
/* If we're asked to add a filter that has been marked for removal, it
@@ -1444,7 +1432,7 @@ void __i40e_del_filter(struct i40e_vsi *vsi, struct i40e_mac_filter *f)
}
vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED;
- vsi->back->flags |= I40E_FLAG_FILTER_SYNC;
+ set_bit(__I40E_MACVLAN_SYNC_PENDING, vsi->state);
}
/**
@@ -1967,7 +1955,7 @@ static void i40e_set_rx_mode(struct net_device *netdev)
/* check for other flag changes */
if (vsi->current_netdev_flags != vsi->netdev->flags) {
vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED;
- vsi->back->flags |= I40E_FLAG_FILTER_SYNC;
+ set_bit(__I40E_MACVLAN_SYNC_PENDING, vsi->back->state);
}
}
@@ -2116,17 +2104,16 @@ void i40e_aqc_del_filters(struct i40e_vsi *vsi, const char *vsi_name,
* @list: the list of filters to send to firmware
* @add_head: Position in the add hlist
* @num_add: the number of filters to add
- * @promisc_change: set to true on exit if promiscuous mode was forced on
*
* Send a request to firmware via AdminQ to add a chunk of filters. Will set
- * promisc_changed to true if the firmware has run out of space for more
- * filters.
+ * __I40E_VSI_OVERFLOW_PROMISC bit in vsi->state if the firmware has run out of
+ * space for more filters.
*/
static
void i40e_aqc_add_filters(struct i40e_vsi *vsi, const char *vsi_name,
struct i40e_aqc_add_macvlan_element_data *list,
struct i40e_new_mac_filter *add_head,
- int num_add, bool *promisc_changed)
+ int num_add)
{
struct i40e_hw *hw = &vsi->back->hw;
int aq_err, fcnt;
@@ -2136,7 +2123,6 @@ void i40e_aqc_add_filters(struct i40e_vsi *vsi, const char *vsi_name,
fcnt = i40e_update_filter_state(num_add, list, add_head);
if (fcnt != num_add) {
- *promisc_changed = true;
set_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state);
dev_warn(&vsi->back->pdev->dev,
"Error %s adding RX filters on %s, promiscuous mode forced on\n",
@@ -2177,11 +2163,13 @@ i40e_aqc_broadcast_filter(struct i40e_vsi *vsi, const char *vsi_name,
NULL);
}
- if (aq_ret)
+ if (aq_ret) {
+ set_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state);
dev_warn(&vsi->back->pdev->dev,
- "Error %s setting broadcast promiscuous mode on %s\n",
+ "Error %s, forcing overflow promiscuous on %s\n",
i40e_aq_str(hw, hw->aq.asq_last_status),
vsi_name);
+ }
return aq_ret;
}
@@ -2267,9 +2255,9 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
struct i40e_mac_filter *f;
struct i40e_new_mac_filter *new, *add_head = NULL;
struct i40e_hw *hw = &vsi->back->hw;
+ bool old_overflow, new_overflow;
unsigned int failed_filters = 0;
unsigned int vlan_filters = 0;
- bool promisc_changed = false;
char vsi_name[16] = "PF";
int filter_list_len = 0;
i40e_status aq_ret = 0;
@@ -2291,6 +2279,8 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
usleep_range(1000, 2000);
pf = vsi->back;
+ old_overflow = test_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state);
+
if (vsi->netdev) {
changed_flags = vsi->current_netdev_flags ^ vsi->netdev->flags;
vsi->current_netdev_flags = vsi->netdev->flags;
@@ -2423,12 +2413,6 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
num_add = 0;
hlist_for_each_entry_safe(new, h, &tmp_add_list, hlist) {
- if (test_bit(__I40E_VSI_OVERFLOW_PROMISC,
- vsi->state)) {
- new->state = I40E_FILTER_FAILED;
- continue;
- }
-
/* handle broadcast filters by updating the broadcast
* promiscuous flag instead of adding a MAC filter.
*/
@@ -2464,15 +2448,14 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
/* flush a full buffer */
if (num_add == filter_list_len) {
i40e_aqc_add_filters(vsi, vsi_name, add_list,
- add_head, num_add,
- &promisc_changed);
+ add_head, num_add);
memset(add_list, 0, list_size);
num_add = 0;
}
}
if (num_add) {
i40e_aqc_add_filters(vsi, vsi_name, add_list, add_head,
- num_add, &promisc_changed);
+ num_add);
}
/* Now move all of the filters from the temp add list back to
* the VSI's list.
@@ -2501,24 +2484,16 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
}
spin_unlock_bh(&vsi->mac_filter_hash_lock);
- /* If promiscuous mode has changed, we need to calculate a new
- * threshold for when we are safe to exit
- */
- if (promisc_changed)
- vsi->promisc_threshold = (vsi->active_filters * 3) / 4;
-
/* Check if we are able to exit overflow promiscuous mode. We can
* safely exit if we didn't just enter, we no longer have any failed
* filters, and we have reduced filters below the threshold value.
*/
- if (test_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state) &&
- !promisc_changed && !failed_filters &&
- (vsi->active_filters < vsi->promisc_threshold)) {
+ if (old_overflow && !failed_filters &&
+ vsi->active_filters < vsi->promisc_threshold) {
dev_info(&pf->pdev->dev,
"filter logjam cleared on %s, leaving overflow promiscuous mode\n",
vsi_name);
clear_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state);
- promisc_changed = true;
vsi->promisc_threshold = 0;
}
@@ -2528,6 +2503,14 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
goto out;
}
+ new_overflow = test_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state);
+
+ /* If we are entering overflow promiscuous, we need to calculate a new
+ * threshold for when we are safe to exit
+ */
+ if (!old_overflow && new_overflow)
+ vsi->promisc_threshold = (vsi->active_filters * 3) / 4;
+
/* check for changes in promiscuous modes */
if (changed_flags & IFF_ALLMULTI) {
bool cur_multipromisc;
@@ -2548,12 +2531,11 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
}
}
- if ((changed_flags & IFF_PROMISC) || promisc_changed) {
+ if ((changed_flags & IFF_PROMISC) || old_overflow != new_overflow) {
bool cur_promisc;
cur_promisc = (!!(vsi->current_netdev_flags & IFF_PROMISC) ||
- test_bit(__I40E_VSI_OVERFLOW_PROMISC,
- vsi->state));
+ new_overflow);
aq_ret = i40e_set_promiscuous(pf, cur_promisc);
if (aq_ret) {
retval = i40e_aq_rc_to_posix(aq_ret,
@@ -2595,9 +2577,10 @@ static void i40e_sync_filters_subtask(struct i40e_pf *pf)
{
int v;
- if (!pf || !(pf->flags & I40E_FLAG_FILTER_SYNC))
+ if (!pf)
+ return;
+ if (!test_and_clear_bit(__I40E_MACVLAN_SYNC_PENDING, pf->state))
return;
- pf->flags &= ~I40E_FLAG_FILTER_SYNC;
for (v = 0; v < pf->num_alloc_vsi; v++) {
if (pf->vsi[v] &&
@@ -2606,7 +2589,8 @@ static void i40e_sync_filters_subtask(struct i40e_pf *pf)
if (ret) {
/* come back and try again later */
- pf->flags |= I40E_FLAG_FILTER_SYNC;
+ set_bit(__I40E_MACVLAN_SYNC_PENDING,
+ pf->state);
break;
}
}
@@ -2650,8 +2634,8 @@ static int i40e_change_mtu(struct net_device *netdev, int new_mtu)
netdev->mtu = new_mtu;
if (netif_running(netdev))
i40e_vsi_reinit_locked(vsi);
- pf->flags |= (I40E_FLAG_SERVICE_CLIENT_REQUESTED |
- I40E_FLAG_CLIENT_L2_CHANGE);
+ set_bit(__I40E_CLIENT_SERVICE_REQUESTED, pf->state);
+ set_bit(__I40E_CLIENT_L2_CHANGE, pf->state);
return 0;
}
@@ -2738,22 +2722,6 @@ void i40e_vlan_stripping_disable(struct i40e_vsi *vsi)
}
/**
- * i40e_vlan_rx_register - Setup or shutdown vlan offload
- * @netdev: network interface to be adjusted
- * @features: netdev features to test if VLAN offload is enabled or not
- **/
-static void i40e_vlan_rx_register(struct net_device *netdev, u32 features)
-{
- struct i40e_netdev_priv *np = netdev_priv(netdev);
- struct i40e_vsi *vsi = np->vsi;
-
- if (features & NETIF_F_HW_VLAN_CTAG_RX)
- i40e_vlan_stripping_enable(vsi);
- else
- i40e_vlan_stripping_disable(vsi);
-}
-
-/**
* i40e_add_vlan_all_mac - Add a MAC/VLAN filter for each existing MAC address
* @vsi: the vsi being configured
* @vid: vlan id to be added (0 = untagged only , -1 = any)
@@ -2928,7 +2896,10 @@ static void i40e_restore_vlan(struct i40e_vsi *vsi)
if (!vsi->netdev)
return;
- i40e_vlan_rx_register(vsi->netdev, vsi->netdev->features);
+ if (vsi->netdev->features & NETIF_F_HW_VLAN_CTAG_RX)
+ i40e_vlan_stripping_enable(vsi);
+ else
+ i40e_vlan_stripping_disable(vsi);
for_each_set_bit(vid, vsi->active_vlans, VLAN_N_VID)
i40e_vlan_rx_add_vid(vsi->netdev, htons(ETH_P_8021Q),
@@ -3449,15 +3420,20 @@ static void i40e_vsi_configure_msix(struct i40e_vsi *vsi)
for (i = 0; i < vsi->num_q_vectors; i++, vector++) {
struct i40e_q_vector *q_vector = vsi->q_vectors[i];
- q_vector->itr_countdown = ITR_COUNTDOWN_START;
- q_vector->rx.itr = ITR_TO_REG(vsi->rx_rings[i]->rx_itr_setting);
- q_vector->rx.latency_range = I40E_LOW_LATENCY;
+ q_vector->rx.next_update = jiffies + 1;
+ q_vector->rx.target_itr =
+ ITR_TO_REG(vsi->rx_rings[i]->itr_setting);
wr32(hw, I40E_PFINT_ITRN(I40E_RX_ITR, vector - 1),
- q_vector->rx.itr);
- q_vector->tx.itr = ITR_TO_REG(vsi->tx_rings[i]->tx_itr_setting);
- q_vector->tx.latency_range = I40E_LOW_LATENCY;
+ q_vector->rx.target_itr);
+ q_vector->rx.current_itr = q_vector->rx.target_itr;
+
+ q_vector->tx.next_update = jiffies + 1;
+ q_vector->tx.target_itr =
+ ITR_TO_REG(vsi->tx_rings[i]->itr_setting);
wr32(hw, I40E_PFINT_ITRN(I40E_TX_ITR, vector - 1),
- q_vector->tx.itr);
+ q_vector->tx.target_itr);
+ q_vector->tx.current_itr = q_vector->tx.target_itr;
+
wr32(hw, I40E_PFINT_RATEN(vector - 1),
i40e_intrl_usec_to_reg(vsi->int_rate_limit));
@@ -3558,13 +3534,14 @@ static void i40e_configure_msi_and_legacy(struct i40e_vsi *vsi)
u32 val;
/* set the ITR configuration */
- q_vector->itr_countdown = ITR_COUNTDOWN_START;
- q_vector->rx.itr = ITR_TO_REG(vsi->rx_rings[0]->rx_itr_setting);
- q_vector->rx.latency_range = I40E_LOW_LATENCY;
- wr32(hw, I40E_PFINT_ITR0(I40E_RX_ITR), q_vector->rx.itr);
- q_vector->tx.itr = ITR_TO_REG(vsi->tx_rings[0]->tx_itr_setting);
- q_vector->tx.latency_range = I40E_LOW_LATENCY;
- wr32(hw, I40E_PFINT_ITR0(I40E_TX_ITR), q_vector->tx.itr);
+ q_vector->rx.next_update = jiffies + 1;
+ q_vector->rx.target_itr = ITR_TO_REG(vsi->rx_rings[0]->itr_setting);
+ wr32(hw, I40E_PFINT_ITR0(I40E_RX_ITR), q_vector->rx.target_itr);
+ q_vector->rx.current_itr = q_vector->rx.target_itr;
+ q_vector->tx.next_update = jiffies + 1;
+ q_vector->tx.target_itr = ITR_TO_REG(vsi->tx_rings[0]->itr_setting);
+ wr32(hw, I40E_PFINT_ITR0(I40E_TX_ITR), q_vector->tx.target_itr);
+ q_vector->tx.current_itr = q_vector->tx.target_itr;
i40e_enable_misc_int_causes(pf);
@@ -4745,9 +4722,9 @@ static void i40e_vsi_close(struct i40e_vsi *vsi)
i40e_vsi_free_tx_resources(vsi);
i40e_vsi_free_rx_resources(vsi);
vsi->current_netdev_flags = 0;
- pf->flags |= I40E_FLAG_SERVICE_CLIENT_REQUESTED;
+ set_bit(__I40E_CLIENT_SERVICE_REQUESTED, pf->state);
if (test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state))
- pf->flags |= I40E_FLAG_CLIENT_RESET;
+ set_bit(__I40E_CLIENT_RESET, pf->state);
}
/**
@@ -5375,7 +5352,7 @@ out:
* @vsi: VSI to be configured
*
**/
-int i40e_get_link_speed(struct i40e_vsi *vsi)
+static int i40e_get_link_speed(struct i40e_vsi *vsi)
{
struct i40e_pf *pf = vsi->back;
@@ -6518,7 +6495,7 @@ static int i40e_up_complete(struct i40e_vsi *vsi)
/* On the next run of the service_task, notify any clients of the new
* opened netdev
*/
- pf->flags |= I40E_FLAG_SERVICE_CLIENT_REQUESTED;
+ set_bit(__I40E_CLIENT_SERVICE_REQUESTED, pf->state);
i40e_service_event_schedule(pf);
return 0;
@@ -6560,6 +6537,75 @@ int i40e_up(struct i40e_vsi *vsi)
}
/**
+ * i40e_force_link_state - Force the link status
+ * @pf: board private structure
+ * @is_up: whether the link state should be forced up or down
+ **/
+static i40e_status i40e_force_link_state(struct i40e_pf *pf, bool is_up)
+{
+ struct i40e_aq_get_phy_abilities_resp abilities;
+ struct i40e_aq_set_phy_config config = {0};
+ struct i40e_hw *hw = &pf->hw;
+ i40e_status err;
+ u64 mask;
+
+ /* Get the current phy config */
+ err = i40e_aq_get_phy_capabilities(hw, false, false, &abilities,
+ NULL);
+ if (err) {
+ dev_err(&pf->pdev->dev,
+ "failed to get phy cap., ret = %s last_status = %s\n",
+ i40e_stat_str(hw, err),
+ i40e_aq_str(hw, hw->aq.asq_last_status));
+ return err;
+ }
+
+ /* If link needs to go up, but was not forced to go down,
+ * no need for a flap
+ */
+ if (is_up && abilities.phy_type != 0)
+ return I40E_SUCCESS;
+
+ /* To force link we need to set bits for all supported PHY types,
+ * but there are now more than 32, so we need to split the bitmap
+ * across two fields.
+ */
+ mask = I40E_PHY_TYPES_BITMASK;
+ config.phy_type = is_up ? cpu_to_le32((u32)(mask & 0xffffffff)) : 0;
+ config.phy_type_ext = is_up ? (u8)((mask >> 32) & 0xff) : 0;
+ /* Copy the old settings, except of phy_type */
+ config.abilities = abilities.abilities;
+ config.link_speed = abilities.link_speed;
+ config.eee_capability = abilities.eee_capability;
+ config.eeer = abilities.eeer_val;
+ config.low_power_ctrl = abilities.d3_lpan;
+ err = i40e_aq_set_phy_config(hw, &config, NULL);
+
+ if (err) {
+ dev_err(&pf->pdev->dev,
+ "set phy config ret = %s last_status = %s\n",
+ i40e_stat_str(&pf->hw, err),
+ i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status));
+ return err;
+ }
+
+ /* Update the link info */
+ err = i40e_update_link_info(hw);
+ if (err) {
+ /* Wait a little bit (on 40G cards it sometimes takes a really
+ * long time for link to come back from the atomic reset)
+ * and try once more
+ */
+ msleep(1000);
+ i40e_update_link_info(hw);
+ }
+
+ i40e_aq_set_link_restart_an(hw, true, NULL);
+
+ return I40E_SUCCESS;
+}
+
+/**
* i40e_down - Shutdown the connection processing
* @vsi: the VSI being stopped
**/
@@ -6576,6 +6622,9 @@ void i40e_down(struct i40e_vsi *vsi)
}
i40e_vsi_disable_irq(vsi);
i40e_vsi_stop_rings(vsi);
+ if (vsi->type == I40E_VSI_MAIN &&
+ vsi->back->flags & I40E_FLAG_LINK_DOWN_ON_CLOSE_ENABLED)
+ i40e_force_link_state(vsi->back, false);
i40e_napi_disable_all(vsi);
for (i = 0; i < vsi->num_queue_pairs; i++) {
@@ -6848,8 +6897,8 @@ i40e_set_cld_element(struct i40e_cloud_filter *filter,
* Add or delete a cloud filter for a specific flow spec.
* Returns 0 if the filter were successfully added.
**/
-static int i40e_add_del_cloud_filter(struct i40e_vsi *vsi,
- struct i40e_cloud_filter *filter, bool add)
+int i40e_add_del_cloud_filter(struct i40e_vsi *vsi,
+ struct i40e_cloud_filter *filter, bool add)
{
struct i40e_aqc_cloud_filters_element_data cld_filter;
struct i40e_pf *pf = vsi->back;
@@ -6915,9 +6964,9 @@ static int i40e_add_del_cloud_filter(struct i40e_vsi *vsi,
* Add or delete a cloud filter for a specific flow spec using big buffer.
* Returns 0 if the filter were successfully added.
**/
-static int i40e_add_del_cloud_filter_big_buf(struct i40e_vsi *vsi,
- struct i40e_cloud_filter *filter,
- bool add)
+int i40e_add_del_cloud_filter_big_buf(struct i40e_vsi *vsi,
+ struct i40e_cloud_filter *filter,
+ bool add)
{
struct i40e_aqc_cloud_filters_element_bb cld_filter;
struct i40e_pf *pf = vsi->back;
@@ -7537,6 +7586,9 @@ int i40e_open(struct net_device *netdev)
netif_carrier_off(netdev);
+ if (i40e_force_link_state(pf, true))
+ return -EAGAIN;
+
err = i40e_vsi_open(vsi);
if (err)
return err;
@@ -7985,8 +8037,8 @@ static int i40e_handle_lldp_event(struct i40e_pf *pf,
i40e_service_event_schedule(pf);
} else {
i40e_pf_unquiesce_all_vsi(pf);
- pf->flags |= (I40E_FLAG_SERVICE_CLIENT_REQUESTED |
- I40E_FLAG_CLIENT_L2_CHANGE);
+ set_bit(__I40E_CLIENT_SERVICE_REQUESTED, pf->state);
+ set_bit(__I40E_CLIENT_L2_CHANGE, pf->state);
}
exit:
@@ -8087,6 +8139,85 @@ u32 i40e_get_global_fd_count(struct i40e_pf *pf)
}
/**
+ * i40e_reenable_fdir_sb - Restore FDir SB capability
+ * @pf: board private structure
+ **/
+static void i40e_reenable_fdir_sb(struct i40e_pf *pf)
+{
+ if (test_and_clear_bit(__I40E_FD_SB_AUTO_DISABLED, pf->state))
+ if ((pf->flags & I40E_FLAG_FD_SB_ENABLED) &&
+ (I40E_DEBUG_FD & pf->hw.debug_mask))
+ dev_info(&pf->pdev->dev, "FD Sideband/ntuple is being enabled since we have space in the table now\n");
+}
+
+/**
+ * i40e_reenable_fdir_atr - Restore FDir ATR capability
+ * @pf: board private structure
+ **/
+static void i40e_reenable_fdir_atr(struct i40e_pf *pf)
+{
+ if (test_and_clear_bit(__I40E_FD_ATR_AUTO_DISABLED, pf->state)) {
+ /* ATR uses the same filtering logic as SB rules. It only
+ * functions properly if the input set mask is at the default
+ * settings. It is safe to restore the default input set
+ * because there are no active TCPv4 filter rules.
+ */
+ i40e_write_fd_input_set(pf, I40E_FILTER_PCTYPE_NONF_IPV4_TCP,
+ I40E_L3_SRC_MASK | I40E_L3_DST_MASK |
+ I40E_L4_SRC_MASK | I40E_L4_DST_MASK);
+
+ if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) &&
+ (I40E_DEBUG_FD & pf->hw.debug_mask))
+ dev_info(&pf->pdev->dev, "ATR is being enabled since we have space in the table and there are no conflicting ntuple rules\n");
+ }
+}
+
+/**
+ * i40e_delete_invalid_filter - Delete an invalid FDIR filter
+ * @pf: board private structure
+ * @filter: FDir filter to remove
+ */
+static void i40e_delete_invalid_filter(struct i40e_pf *pf,
+ struct i40e_fdir_filter *filter)
+{
+ /* Update counters */
+ pf->fdir_pf_active_filters--;
+ pf->fd_inv = 0;
+
+ switch (filter->flow_type) {
+ case TCP_V4_FLOW:
+ pf->fd_tcp4_filter_cnt--;
+ break;
+ case UDP_V4_FLOW:
+ pf->fd_udp4_filter_cnt--;
+ break;
+ case SCTP_V4_FLOW:
+ pf->fd_sctp4_filter_cnt--;
+ break;
+ case IP_USER_FLOW:
+ switch (filter->ip4_proto) {
+ case IPPROTO_TCP:
+ pf->fd_tcp4_filter_cnt--;
+ break;
+ case IPPROTO_UDP:
+ pf->fd_udp4_filter_cnt--;
+ break;
+ case IPPROTO_SCTP:
+ pf->fd_sctp4_filter_cnt--;
+ break;
+ case IPPROTO_IP:
+ pf->fd_ip4_filter_cnt--;
+ break;
+ }
+ break;
+ }
+
+ /* Remove the filter from the list and free memory */
+ hlist_del(&filter->fdir_node);
+ kfree(filter);
+}
+
+/**
* i40e_fdir_check_and_reenable - Function to reenabe FD ATR or SB if disabled
* @pf: board private structure
**/
@@ -8104,40 +8235,23 @@ void i40e_fdir_check_and_reenable(struct i40e_pf *pf)
fcnt_avail = pf->fdir_pf_filter_count;
if ((fcnt_prog < (fcnt_avail - I40E_FDIR_BUFFER_HEAD_ROOM)) ||
(pf->fd_add_err == 0) ||
- (i40e_get_current_atr_cnt(pf) < pf->fd_atr_cnt)) {
- if (pf->flags & I40E_FLAG_FD_SB_AUTO_DISABLED) {
- pf->flags &= ~I40E_FLAG_FD_SB_AUTO_DISABLED;
- if ((pf->flags & I40E_FLAG_FD_SB_ENABLED) &&
- (I40E_DEBUG_FD & pf->hw.debug_mask))
- dev_info(&pf->pdev->dev, "FD Sideband/ntuple is being enabled since we have space in the table now\n");
- }
- }
+ (i40e_get_current_atr_cnt(pf) < pf->fd_atr_cnt))
+ i40e_reenable_fdir_sb(pf);
/* We should wait for even more space before re-enabling ATR.
* Additionally, we cannot enable ATR as long as we still have TCP SB
* rules active.
*/
if ((fcnt_prog < (fcnt_avail - I40E_FDIR_BUFFER_HEAD_ROOM_FOR_ATR)) &&
- (pf->fd_tcp4_filter_cnt == 0)) {
- if (pf->flags & I40E_FLAG_FD_ATR_AUTO_DISABLED) {
- pf->flags &= ~I40E_FLAG_FD_ATR_AUTO_DISABLED;
- if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) &&
- (I40E_DEBUG_FD & pf->hw.debug_mask))
- dev_info(&pf->pdev->dev, "ATR is being enabled since we have space in the table and there are no conflicting ntuple rules\n");
- }
- }
+ (pf->fd_tcp4_filter_cnt == 0))
+ i40e_reenable_fdir_atr(pf);
/* if hw had a problem adding a filter, delete it */
if (pf->fd_inv > 0) {
hlist_for_each_entry_safe(filter, node,
- &pf->fdir_filter_list, fdir_node) {
- if (filter->fd_id == pf->fd_inv) {
- hlist_del(&filter->fdir_node);
- kfree(filter);
- pf->fdir_pf_active_filters--;
- pf->fd_inv = 0;
- }
- }
+ &pf->fdir_filter_list, fdir_node)
+ if (filter->fd_id == pf->fd_inv)
+ i40e_delete_invalid_filter(pf, filter);
}
}
@@ -8174,7 +8288,7 @@ static void i40e_fdir_flush_and_replay(struct i40e_pf *pf)
}
pf->fd_flush_timestamp = jiffies;
- pf->flags |= I40E_FLAG_FD_ATR_AUTO_DISABLED;
+ set_bit(__I40E_FD_ATR_AUTO_DISABLED, pf->state);
/* flush all filters */
wr32(&pf->hw, I40E_PFQF_CTL_1,
I40E_PFQF_CTL_1_CLEARFDTABLE_MASK);
@@ -8194,7 +8308,7 @@ static void i40e_fdir_flush_and_replay(struct i40e_pf *pf)
/* replay sideband filters */
i40e_fdir_filter_restore(pf->vsi[pf->lan_vsi]);
if (!disable_atr && !pf->fd_tcp4_filter_cnt)
- pf->flags &= ~I40E_FLAG_FD_ATR_AUTO_DISABLED;
+ clear_bit(__I40E_FD_ATR_AUTO_DISABLED, pf->state);
clear_bit(__I40E_FD_FLUSH_REQUESTED, pf->state);
if (I40E_DEBUG_FD & pf->hw.debug_mask)
dev_info(&pf->pdev->dev, "FD Filter table flushed and FD-SB replayed.\n");
@@ -8318,13 +8432,12 @@ static void i40e_link_event(struct i40e_pf *pf)
/* On success, disable temp link polling */
if (status == I40E_SUCCESS) {
- if (pf->flags & I40E_FLAG_TEMP_LINK_POLLING)
- pf->flags &= ~I40E_FLAG_TEMP_LINK_POLLING;
+ clear_bit(__I40E_TEMP_LINK_POLLING, pf->state);
} else {
/* Enable link polling temporarily until i40e_get_link_status
* returns I40E_SUCCESS
*/
- pf->flags |= I40E_FLAG_TEMP_LINK_POLLING;
+ set_bit(__I40E_TEMP_LINK_POLLING, pf->state);
dev_dbg(&pf->pdev->dev, "couldn't get link state, status: %d\n",
status);
return;
@@ -8376,7 +8489,7 @@ static void i40e_watchdog_subtask(struct i40e_pf *pf)
pf->service_timer_previous = jiffies;
if ((pf->flags & I40E_FLAG_LINK_POLLING_ENABLED) ||
- (pf->flags & I40E_FLAG_TEMP_LINK_POLLING))
+ test_bit(__I40E_TEMP_LINK_POLLING, pf->state))
i40e_link_event(pf);
/* Update the stats for active netdevs so the network stack
@@ -9215,6 +9328,17 @@ static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired)
}
i40e_get_oem_version(&pf->hw);
+ if (test_bit(__I40E_EMP_RESET_INTR_RECEIVED, pf->state) &&
+ ((hw->aq.fw_maj_ver == 4 && hw->aq.fw_min_ver <= 33) ||
+ hw->aq.fw_maj_ver < 4) && hw->mac.type == I40E_MAC_XL710) {
+ /* The following delay is necessary for 4.33 firmware and older
+ * to recover after EMP reset. 200 ms should suffice but we
+ * put here 300 ms to be sure that FW is ready to operate
+ * after reset.
+ */
+ mdelay(300);
+ }
+
/* re-verify the eeprom if we just had an EMP reset */
if (test_and_clear_bit(__I40E_EMP_RESET_INTR_RECEIVED, pf->state))
i40e_verify_eeprom(pf);
@@ -9593,7 +9717,7 @@ static void i40e_sync_udp_filters(struct i40e_pf *pf)
pf->pending_udp_bitmap |= BIT_ULL(i);
}
- pf->flags |= I40E_FLAG_UDP_FILTER_SYNC;
+ set_bit(__I40E_UDP_FILTER_SYNC_PENDING, pf->state);
}
/**
@@ -9607,11 +9731,9 @@ static void i40e_sync_udp_filters_subtask(struct i40e_pf *pf)
u16 port;
int i;
- if (!(pf->flags & I40E_FLAG_UDP_FILTER_SYNC))
+ if (!test_and_clear_bit(__I40E_UDP_FILTER_SYNC_PENDING, pf->state))
return;
- pf->flags &= ~I40E_FLAG_UDP_FILTER_SYNC;
-
for (i = 0; i < I40E_MAX_PF_UDP_OFFLOAD_PORTS; i++) {
if (pf->pending_udp_bitmap & BIT_ULL(i)) {
pf->pending_udp_bitmap &= ~BIT_ULL(i);
@@ -9663,17 +9785,15 @@ static void i40e_service_task(struct work_struct *work)
i40e_vc_process_vflr_event(pf);
i40e_watchdog_subtask(pf);
i40e_fdir_reinit_subtask(pf);
- if (pf->flags & I40E_FLAG_CLIENT_RESET) {
+ if (test_and_clear_bit(__I40E_CLIENT_RESET, pf->state)) {
/* Client subtask will reopen next time through. */
i40e_notify_client_of_netdev_close(pf->vsi[pf->lan_vsi], true);
- pf->flags &= ~I40E_FLAG_CLIENT_RESET;
} else {
i40e_client_subtask(pf);
- if (pf->flags & I40E_FLAG_CLIENT_L2_CHANGE) {
+ if (test_and_clear_bit(__I40E_CLIENT_L2_CHANGE,
+ pf->state))
i40e_notify_client_of_l2_param_changes(
pf->vsi[pf->lan_vsi]);
- pf->flags &= ~I40E_FLAG_CLIENT_L2_CHANGE;
- }
}
i40e_sync_filters_subtask(pf);
i40e_sync_udp_filters_subtask(pf);
@@ -9937,18 +10057,17 @@ static int i40e_vsi_clear(struct i40e_vsi *vsi)
mutex_lock(&pf->switch_mutex);
if (!pf->vsi[vsi->idx]) {
- dev_err(&pf->pdev->dev, "pf->vsi[%d] is NULL, just free vsi[%d](%p,type %d)\n",
- vsi->idx, vsi->idx, vsi, vsi->type);
+ dev_err(&pf->pdev->dev, "pf->vsi[%d] is NULL, just free vsi[%d](type %d)\n",
+ vsi->idx, vsi->idx, vsi->type);
goto unlock_vsi;
}
if (pf->vsi[vsi->idx] != vsi) {
dev_err(&pf->pdev->dev,
- "pf->vsi[%d](%p, type %d) != vsi[%d](%p,type %d): no free!\n",
+ "pf->vsi[%d](type %d) != vsi[%d](type %d): no free!\n",
pf->vsi[vsi->idx]->idx,
- pf->vsi[vsi->idx],
pf->vsi[vsi->idx]->type,
- vsi->idx, vsi, vsi->type);
+ vsi->idx, vsi->type);
goto unlock_vsi;
}
@@ -10018,7 +10137,7 @@ static int i40e_alloc_rings(struct i40e_vsi *vsi)
ring->dcb_tc = 0;
if (vsi->back->hw_features & I40E_HW_WB_ON_ITR_CAPABLE)
ring->flags = I40E_TXR_FLAGS_WB_ON_ITR;
- ring->tx_itr_setting = pf->tx_itr_default;
+ ring->itr_setting = pf->tx_itr_default;
vsi->tx_rings[i] = ring++;
if (!i40e_enabled_xdp_vsi(vsi))
@@ -10036,7 +10155,7 @@ static int i40e_alloc_rings(struct i40e_vsi *vsi)
if (vsi->back->hw_features & I40E_HW_WB_ON_ITR_CAPABLE)
ring->flags = I40E_TXR_FLAGS_WB_ON_ITR;
set_ring_xdp(ring);
- ring->tx_itr_setting = pf->tx_itr_default;
+ ring->itr_setting = pf->tx_itr_default;
vsi->xdp_rings[i] = ring++;
setup_rx:
@@ -10049,7 +10168,7 @@ setup_rx:
ring->count = vsi->num_desc;
ring->size = 0;
ring->dcb_tc = 0;
- ring->rx_itr_setting = pf->rx_itr_default;
+ ring->itr_setting = pf->rx_itr_default;
vsi->rx_rings[i] = ring;
}
@@ -10328,9 +10447,6 @@ static int i40e_vsi_alloc_q_vector(struct i40e_vsi *vsi, int v_idx, int cpu)
netif_napi_add(vsi->netdev, &q_vector->napi,
i40e_napi_poll, NAPI_POLL_WEIGHT);
- q_vector->rx.latency_range = I40E_LOW_LATENCY;
- q_vector->tx.latency_range = I40E_LOW_LATENCY;
-
/* tie q_vector and vsi together */
vsi->q_vectors[v_idx] = q_vector;
@@ -10473,6 +10589,9 @@ static int i40e_restore_interrupt_scheme(struct i40e_pf *pf)
if (err)
goto err_unwind;
+ if (pf->flags & I40E_FLAG_IWARP_ENABLED)
+ i40e_client_update_msix_info(pf);
+
return 0;
err_unwind:
@@ -11089,6 +11208,16 @@ static int i40e_sw_init(struct i40e_pf *pf)
/* IWARP needs one extra vector for CQP just like MISC.*/
pf->num_iwarp_msix = (int)num_online_cpus() + 1;
}
+ /* Stopping the FW LLDP engine is only supported on the
+ * XL710 with a FW ver >= 1.7. Also, stopping FW LLDP
+ * engine is not supported if NPAR is functioning on this
+ * part
+ */
+ if (pf->hw.mac.type == I40E_MAC_XL710 &&
+ !pf->hw.func_caps.npar_enable &&
+ (pf->hw.aq.api_maj_ver > 1 ||
+ (pf->hw.aq.api_maj_ver == 1 && pf->hw.aq.api_min_ver > 6)))
+ pf->hw_features |= I40E_HW_STOPPABLE_FW_LLDP;
#ifdef CONFIG_PCI_IOV
if (pf->hw.func_caps.num_vfs && pf->hw.partition_id == 1) {
@@ -11156,20 +11285,18 @@ bool i40e_set_ntuple(struct i40e_pf *pf, netdev_features_t features)
need_reset = true;
i40e_fdir_filter_exit(pf);
}
- pf->flags &= ~(I40E_FLAG_FD_SB_ENABLED |
- I40E_FLAG_FD_SB_AUTO_DISABLED);
+ pf->flags &= ~I40E_FLAG_FD_SB_ENABLED;
+ clear_bit(__I40E_FD_SB_AUTO_DISABLED, pf->state);
pf->flags |= I40E_FLAG_FD_SB_INACTIVE;
/* reset fd counters */
pf->fd_add_err = 0;
pf->fd_atr_cnt = 0;
/* if ATR was auto disabled it can be re-enabled. */
- if (pf->flags & I40E_FLAG_FD_ATR_AUTO_DISABLED) {
- pf->flags &= ~I40E_FLAG_FD_ATR_AUTO_DISABLED;
+ if (test_and_clear_bit(__I40E_FD_ATR_AUTO_DISABLED, pf->state))
if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) &&
(I40E_DEBUG_FD & pf->hw.debug_mask))
dev_info(&pf->pdev->dev, "ATR re-enabled.\n");
- }
}
return need_reset;
}
@@ -11302,7 +11429,7 @@ static void i40e_udp_tunnel_add(struct net_device *netdev,
/* New port: add it and mark its index in the bitmap */
pf->udp_ports[next_idx].port = port;
pf->pending_udp_bitmap |= BIT_ULL(next_idx);
- pf->flags |= I40E_FLAG_UDP_FILTER_SYNC;
+ set_bit(__I40E_UDP_FILTER_SYNC_PENDING, pf->state);
}
/**
@@ -11343,7 +11470,7 @@ static void i40e_udp_tunnel_del(struct net_device *netdev,
*/
pf->udp_ports[idx].port = 0;
pf->pending_udp_bitmap |= BIT_ULL(idx);
- pf->flags |= I40E_FLAG_UDP_FILTER_SYNC;
+ set_bit(__I40E_UDP_FILTER_SYNC_PENDING, pf->state);
return;
not_found:
@@ -11688,6 +11815,8 @@ static const struct net_device_ops i40e_netdev_ops = {
.ndo_bridge_getlink = i40e_ndo_bridge_getlink,
.ndo_bridge_setlink = i40e_ndo_bridge_setlink,
.ndo_bpf = i40e_xdp,
+ .ndo_xdp_xmit = i40e_xdp_xmit,
+ .ndo_xdp_flush = i40e_xdp_flush,
};
/**
@@ -12105,7 +12234,7 @@ static int i40e_add_vsi(struct i40e_vsi *vsi)
if (f_count) {
vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED;
- pf->flags |= I40E_FLAG_FILTER_SYNC;
+ set_bit(__I40E_MACVLAN_SYNC_PENDING, pf->state);
}
/* Update VSI BW information */
@@ -14213,10 +14342,21 @@ static int __maybe_unused i40e_suspend(struct device *dev)
del_timer_sync(&pf->service_timer);
cancel_work_sync(&pf->service_task);
+ /* Client close must be called explicitly here because the timer
+ * has been stopped.
+ */
+ i40e_notify_client_of_netdev_close(pf->vsi[pf->lan_vsi], false);
+
if (pf->wol_en && (pf->hw_features & I40E_HW_WOL_MC_MAGIC_PKT_WAKE))
i40e_enable_mc_magic_wake(pf);
- i40e_prep_for_reset(pf, false);
+ /* Since we're going to destroy queues during the
+ * i40e_clear_interrupt_scheme() we should hold the RTNL lock for this
+ * whole section
+ */
+ rtnl_lock();
+
+ i40e_prep_for_reset(pf, true);
wr32(hw, I40E_PFPM_APM, (pf->wol_en ? I40E_PFPM_APM_APME_MASK : 0));
wr32(hw, I40E_PFPM_WUFC, (pf->wol_en ? I40E_PFPM_WUFC_MAG_MASK : 0));
@@ -14228,6 +14368,8 @@ static int __maybe_unused i40e_suspend(struct device *dev)
*/
i40e_clear_interrupt_scheme(pf);
+ rtnl_unlock();
+
return 0;
}
@@ -14245,6 +14387,11 @@ static int __maybe_unused i40e_resume(struct device *dev)
if (!test_bit(__I40E_SUSPENDED, pf->state))
return 0;
+ /* We need to hold the RTNL lock prior to restoring interrupt schemes,
+ * since we're going to be restoring queues
+ */
+ rtnl_lock();
+
/* We cleared the interrupt scheme when we suspended, so we need to
* restore it now to resume device functionality.
*/
@@ -14255,7 +14402,9 @@ static int __maybe_unused i40e_resume(struct device *dev)
}
clear_bit(__I40E_DOWN, pf->state);
- i40e_reset_and_rebuild(pf, false, false);
+ i40e_reset_and_rebuild(pf, false, true);
+
+ rtnl_unlock();
/* Clear suspended state last after everything is recovered */
clear_bit(__I40E_SUSPENDED, pf->state);
diff --git a/drivers/net/ethernet/intel/i40e/i40e_nvm.c b/drivers/net/ethernet/intel/i40e/i40e_nvm.c
index 76a5cb04e4fe..ba9687c03795 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_nvm.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_nvm.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
diff --git a/drivers/net/ethernet/intel/i40e/i40e_osdep.h b/drivers/net/ethernet/intel/i40e/i40e_osdep.h
index 80e66da6b145..9c3c3b0d3ac4 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_osdep.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_osdep.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
index 83798b7841b9..2ec24188d6e2 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
@@ -287,7 +288,7 @@ i40e_status i40e_aq_query_switch_comp_bw_config(struct i40e_hw *hw,
struct i40e_asq_cmd_details *cmd_details);
i40e_status i40e_aq_resume_port_tx(struct i40e_hw *hw,
struct i40e_asq_cmd_details *cmd_details);
-i40e_status
+enum i40e_status_code
i40e_aq_add_cloud_filters_bb(struct i40e_hw *hw, u16 seid,
struct i40e_aqc_cloud_filters_element_bb *filters,
u8 filter_count);
@@ -299,7 +300,7 @@ enum i40e_status_code
i40e_aq_rem_cloud_filters(struct i40e_hw *hw, u16 vsi,
struct i40e_aqc_cloud_filters_element_data *filters,
u8 filter_count);
-i40e_status
+enum i40e_status_code
i40e_aq_rem_cloud_filters_bb(struct i40e_hw *hw, u16 seid,
struct i40e_aqc_cloud_filters_element_bb *filters,
u8 filter_count);
diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c
index 97381238eb7c..5b47dd1f75a5 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
diff --git a/drivers/net/ethernet/intel/i40e/i40e_register.h b/drivers/net/ethernet/intel/i40e/i40e_register.h
index c234758dad15..b3e206e49cc2 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_register.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_register.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
diff --git a/drivers/net/ethernet/intel/i40e/i40e_status.h b/drivers/net/ethernet/intel/i40e/i40e_status.h
index afb72e711d43..10c86f63dc52 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_status.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_status.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
diff --git a/drivers/net/ethernet/intel/i40e/i40e_trace.h b/drivers/net/ethernet/intel/i40e/i40e_trace.h
index d3e55f54a05e..410ba13bcf21 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_trace.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_trace.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel(R) 40-10 Gigabit Ethernet Connection Network Driver
diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
index e554aa6cf070..f174c72480ab 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
@@ -335,7 +336,7 @@ static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi,
if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) &&
I40E_DEBUG_FD & pf->hw.debug_mask)
dev_info(&pf->pdev->dev, "Forcing ATR off, sideband rules for TCP/IPv4 flow being applied\n");
- pf->flags |= I40E_FLAG_FD_ATR_AUTO_DISABLED;
+ set_bit(__I40E_FD_ATR_AUTO_DISABLED, pf->state);
} else {
pf->fd_tcp4_filter_cnt--;
}
@@ -593,8 +594,14 @@ static void i40e_fd_handle_status(struct i40e_ring *rx_ring,
pf->fd_atr_cnt = i40e_get_current_atr_cnt(pf);
if ((rx_desc->wb.qword0.hi_dword.fd_id == 0) &&
- pf->flags & I40E_FLAG_FD_SB_AUTO_DISABLED) {
- pf->flags |= I40E_FLAG_FD_ATR_AUTO_DISABLED;
+ test_bit(__I40E_FD_SB_AUTO_DISABLED, pf->state)) {
+ /* These set_bit() calls aren't atomic with the
+ * test_bit() here, but worse case we potentially
+ * disable ATR and queue a flush right after SB
+ * support is re-enabled. That shouldn't cause an
+ * issue in practice
+ */
+ set_bit(__I40E_FD_ATR_AUTO_DISABLED, pf->state);
set_bit(__I40E_FD_FLUSH_REQUESTED, pf->state);
}
@@ -607,11 +614,10 @@ static void i40e_fd_handle_status(struct i40e_ring *rx_ring,
*/
if (fcnt_prog >= (fcnt_avail - I40E_FDIR_BUFFER_FULL_MARGIN)) {
if ((pf->flags & I40E_FLAG_FD_SB_ENABLED) &&
- !(pf->flags & I40E_FLAG_FD_SB_AUTO_DISABLED)) {
- pf->flags |= I40E_FLAG_FD_SB_AUTO_DISABLED;
+ !test_and_set_bit(__I40E_FD_SB_AUTO_DISABLED,
+ pf->state))
if (I40E_DEBUG_FD & pf->hw.debug_mask)
dev_warn(&pdev->dev, "FD filter space full, new ntuple rules will not be added\n");
- }
}
} else if (error == BIT(I40E_RX_PROG_STATUS_DESC_NO_FD_ENTRY_SHIFT)) {
if (I40E_DEBUG_FD & pf->hw.debug_mask)
@@ -708,16 +714,22 @@ void i40e_free_tx_resources(struct i40e_ring *tx_ring)
/**
* i40e_get_tx_pending - how many tx descriptors not processed
* @tx_ring: the ring of descriptors
+ * @in_sw: use SW variables
*
* Since there is no access to the ring head register
* in XL710, we need to use our local copies
**/
-u32 i40e_get_tx_pending(struct i40e_ring *ring)
+u32 i40e_get_tx_pending(struct i40e_ring *ring, bool in_sw)
{
u32 head, tail;
- head = i40e_get_head(ring);
- tail = readl(ring->tail);
+ if (!in_sw) {
+ head = i40e_get_head(ring);
+ tail = readl(ring->tail);
+ } else {
+ head = ring->next_to_clean;
+ tail = ring->next_to_use;
+ }
if (head != tail)
return (head < tail) ?
@@ -774,7 +786,7 @@ void i40e_detect_recover_hung(struct i40e_vsi *vsi)
*/
smp_rmb();
tx_ring->tx_stats.prev_pkt_ctr =
- i40e_get_tx_pending(tx_ring) ? packets : -1;
+ i40e_get_tx_pending(tx_ring, true) ? packets : -1;
}
}
}
@@ -898,7 +910,7 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi,
* them to be written back in case we stay in NAPI.
* In this mode on X722 we do not enable Interrupt.
*/
- unsigned int j = i40e_get_tx_pending(tx_ring);
+ unsigned int j = i40e_get_tx_pending(tx_ring, false);
if (budget &&
((j / WB_STRIDE) == 0) && (j > 0) &&
@@ -995,99 +1007,241 @@ void i40e_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector)
}
}
+static inline bool i40e_container_is_rx(struct i40e_q_vector *q_vector,
+ struct i40e_ring_container *rc)
+{
+ return &q_vector->rx == rc;
+}
+
+static inline unsigned int i40e_itr_divisor(struct i40e_q_vector *q_vector)
+{
+ unsigned int divisor;
+
+ switch (q_vector->vsi->back->hw.phy.link_info.link_speed) {
+ case I40E_LINK_SPEED_40GB:
+ divisor = I40E_ITR_ADAPTIVE_MIN_INC * 1024;
+ break;
+ case I40E_LINK_SPEED_25GB:
+ case I40E_LINK_SPEED_20GB:
+ divisor = I40E_ITR_ADAPTIVE_MIN_INC * 512;
+ break;
+ default:
+ case I40E_LINK_SPEED_10GB:
+ divisor = I40E_ITR_ADAPTIVE_MIN_INC * 256;
+ break;
+ case I40E_LINK_SPEED_1GB:
+ case I40E_LINK_SPEED_100MB:
+ divisor = I40E_ITR_ADAPTIVE_MIN_INC * 32;
+ break;
+ }
+
+ return divisor;
+}
+
/**
- * i40e_set_new_dynamic_itr - Find new ITR level
+ * i40e_update_itr - update the dynamic ITR value based on statistics
+ * @q_vector: structure containing interrupt and ring information
* @rc: structure containing ring performance data
*
- * Returns true if ITR changed, false if not
- *
- * Stores a new ITR value based on packets and byte counts during
- * the last interrupt. The advantage of per interrupt computation
- * is faster updates and more accurate ITR for the current traffic
- * pattern. Constants in this function were computed based on
- * theoretical maximum wire speed and thresholds were set based on
- * testing data as well as attempting to minimize response time
+ * Stores a new ITR value based on packets and byte
+ * counts during the last interrupt. The advantage of per interrupt
+ * computation is faster updates and more accurate ITR for the current
+ * traffic pattern. Constants in this function were computed
+ * based on theoretical maximum wire speed and thresholds were set based
+ * on testing data as well as attempting to minimize response time
* while increasing bulk throughput.
**/
-static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc)
+static void i40e_update_itr(struct i40e_q_vector *q_vector,
+ struct i40e_ring_container *rc)
{
- enum i40e_latency_range new_latency_range = rc->latency_range;
- u32 new_itr = rc->itr;
- int bytes_per_usec;
- unsigned int usecs, estimated_usecs;
+ unsigned int avg_wire_size, packets, bytes, itr;
+ unsigned long next_update = jiffies;
- if (rc->total_packets == 0 || !rc->itr)
- return false;
+ /* If we don't have any rings just leave ourselves set for maximum
+ * possible latency so we take ourselves out of the equation.
+ */
+ if (!rc->ring || !ITR_IS_DYNAMIC(rc->ring->itr_setting))
+ return;
- usecs = (rc->itr << 1) * ITR_COUNTDOWN_START;
- bytes_per_usec = rc->total_bytes / usecs;
+ /* For Rx we want to push the delay up and default to low latency.
+ * for Tx we want to pull the delay down and default to high latency.
+ */
+ itr = i40e_container_is_rx(q_vector, rc) ?
+ I40E_ITR_ADAPTIVE_MIN_USECS | I40E_ITR_ADAPTIVE_LATENCY :
+ I40E_ITR_ADAPTIVE_MAX_USECS | I40E_ITR_ADAPTIVE_LATENCY;
+
+ /* If we didn't update within up to 1 - 2 jiffies we can assume
+ * that either packets are coming in so slow there hasn't been
+ * any work, or that there is so much work that NAPI is dealing
+ * with interrupt moderation and we don't need to do anything.
+ */
+ if (time_after(next_update, rc->next_update))
+ goto clear_counts;
+
+ /* If itr_countdown is set it means we programmed an ITR within
+ * the last 4 interrupt cycles. This has a side effect of us
+ * potentially firing an early interrupt. In order to work around
+ * this we need to throw out any data received for a few
+ * interrupts following the update.
+ */
+ if (q_vector->itr_countdown) {
+ itr = rc->target_itr;
+ goto clear_counts;
+ }
- /* The calculations in this algorithm depend on interrupts actually
- * firing at the ITR rate. This may not happen if the packet rate is
- * really low, or if we've been napi polling. Check to make sure
- * that's not the case before we continue.
+ packets = rc->total_packets;
+ bytes = rc->total_bytes;
+
+ if (i40e_container_is_rx(q_vector, rc)) {
+ /* If Rx there are 1 to 4 packets and bytes are less than
+ * 9000 assume insufficient data to use bulk rate limiting
+ * approach unless Tx is already in bulk rate limiting. We
+ * are likely latency driven.
+ */
+ if (packets && packets < 4 && bytes < 9000 &&
+ (q_vector->tx.target_itr & I40E_ITR_ADAPTIVE_LATENCY)) {
+ itr = I40E_ITR_ADAPTIVE_LATENCY;
+ goto adjust_by_size;
+ }
+ } else if (packets < 4) {
+ /* If we have Tx and Rx ITR maxed and Tx ITR is running in
+ * bulk mode and we are receiving 4 or fewer packets just
+ * reset the ITR_ADAPTIVE_LATENCY bit for latency mode so
+ * that the Rx can relax.
+ */
+ if (rc->target_itr == I40E_ITR_ADAPTIVE_MAX_USECS &&
+ (q_vector->rx.target_itr & I40E_ITR_MASK) ==
+ I40E_ITR_ADAPTIVE_MAX_USECS)
+ goto clear_counts;
+ } else if (packets > 32) {
+ /* If we have processed over 32 packets in a single interrupt
+ * for Tx assume we need to switch over to "bulk" mode.
+ */
+ rc->target_itr &= ~I40E_ITR_ADAPTIVE_LATENCY;
+ }
+
+ /* We have no packets to actually measure against. This means
+ * either one of the other queues on this vector is active or
+ * we are a Tx queue doing TSO with too high of an interrupt rate.
+ *
+ * Between 4 and 56 we can assume that our current interrupt delay
+ * is only slightly too low. As such we should increase it by a small
+ * fixed amount.
*/
- estimated_usecs = jiffies_to_usecs(jiffies - rc->last_itr_update);
- if (estimated_usecs > usecs) {
- new_latency_range = I40E_LOW_LATENCY;
- goto reset_latency;
+ if (packets < 56) {
+ itr = rc->target_itr + I40E_ITR_ADAPTIVE_MIN_INC;
+ if ((itr & I40E_ITR_MASK) > I40E_ITR_ADAPTIVE_MAX_USECS) {
+ itr &= I40E_ITR_ADAPTIVE_LATENCY;
+ itr += I40E_ITR_ADAPTIVE_MAX_USECS;
+ }
+ goto clear_counts;
}
- /* simple throttlerate management
- * 0-10MB/s lowest (50000 ints/s)
- * 10-20MB/s low (20000 ints/s)
- * 20-1249MB/s bulk (18000 ints/s)
+ if (packets <= 256) {
+ itr = min(q_vector->tx.current_itr, q_vector->rx.current_itr);
+ itr &= I40E_ITR_MASK;
+
+ /* Between 56 and 112 is our "goldilocks" zone where we are
+ * working out "just right". Just report that our current
+ * ITR is good for us.
+ */
+ if (packets <= 112)
+ goto clear_counts;
+
+ /* If packet count is 128 or greater we are likely looking
+ * at a slight overrun of the delay we want. Try halving
+ * our delay to see if that will cut the number of packets
+ * in half per interrupt.
+ */
+ itr /= 2;
+ itr &= I40E_ITR_MASK;
+ if (itr < I40E_ITR_ADAPTIVE_MIN_USECS)
+ itr = I40E_ITR_ADAPTIVE_MIN_USECS;
+
+ goto clear_counts;
+ }
+
+ /* The paths below assume we are dealing with a bulk ITR since
+ * number of packets is greater than 256. We are just going to have
+ * to compute a value and try to bring the count under control,
+ * though for smaller packet sizes there isn't much we can do as
+ * NAPI polling will likely be kicking in sooner rather than later.
+ */
+ itr = I40E_ITR_ADAPTIVE_BULK;
+
+adjust_by_size:
+ /* If packet counts are 256 or greater we can assume we have a gross
+ * overestimation of what the rate should be. Instead of trying to fine
+ * tune it just use the formula below to try and dial in an exact value
+ * give the current packet size of the frame.
+ */
+ avg_wire_size = bytes / packets;
+
+ /* The following is a crude approximation of:
+ * wmem_default / (size + overhead) = desired_pkts_per_int
+ * rate / bits_per_byte / (size + ethernet overhead) = pkt_rate
+ * (desired_pkt_rate / pkt_rate) * usecs_per_sec = ITR value
+ *
+ * Assuming wmem_default is 212992 and overhead is 640 bytes per
+ * packet, (256 skb, 64 headroom, 320 shared info), we can reduce the
+ * formula down to
*
- * The math works out because the divisor is in 10^(-6) which
- * turns the bytes/us input value into MB/s values, but
- * make sure to use usecs, as the register values written
- * are in 2 usec increments in the ITR registers, and make sure
- * to use the smoothed values that the countdown timer gives us.
+ * (170 * (size + 24)) / (size + 640) = ITR
+ *
+ * We first do some math on the packet size and then finally bitshift
+ * by 8 after rounding up. We also have to account for PCIe link speed
+ * difference as ITR scales based on this.
*/
- switch (new_latency_range) {
- case I40E_LOWEST_LATENCY:
- if (bytes_per_usec > 10)
- new_latency_range = I40E_LOW_LATENCY;
- break;
- case I40E_LOW_LATENCY:
- if (bytes_per_usec > 20)
- new_latency_range = I40E_BULK_LATENCY;
- else if (bytes_per_usec <= 10)
- new_latency_range = I40E_LOWEST_LATENCY;
- break;
- case I40E_BULK_LATENCY:
- default:
- if (bytes_per_usec <= 20)
- new_latency_range = I40E_LOW_LATENCY;
- break;
+ if (avg_wire_size <= 60) {
+ /* Start at 250k ints/sec */
+ avg_wire_size = 4096;
+ } else if (avg_wire_size <= 380) {
+ /* 250K ints/sec to 60K ints/sec */
+ avg_wire_size *= 40;
+ avg_wire_size += 1696;
+ } else if (avg_wire_size <= 1084) {
+ /* 60K ints/sec to 36K ints/sec */
+ avg_wire_size *= 15;
+ avg_wire_size += 11452;
+ } else if (avg_wire_size <= 1980) {
+ /* 36K ints/sec to 30K ints/sec */
+ avg_wire_size *= 5;
+ avg_wire_size += 22420;
+ } else {
+ /* plateau at a limit of 30K ints/sec */
+ avg_wire_size = 32256;
}
-reset_latency:
- rc->latency_range = new_latency_range;
+ /* If we are in low latency mode halve our delay which doubles the
+ * rate to somewhere between 100K to 16K ints/sec
+ */
+ if (itr & I40E_ITR_ADAPTIVE_LATENCY)
+ avg_wire_size /= 2;
- switch (new_latency_range) {
- case I40E_LOWEST_LATENCY:
- new_itr = I40E_ITR_50K;
- break;
- case I40E_LOW_LATENCY:
- new_itr = I40E_ITR_20K;
- break;
- case I40E_BULK_LATENCY:
- new_itr = I40E_ITR_18K;
- break;
- default:
- break;
+ /* Resultant value is 256 times larger than it needs to be. This
+ * gives us room to adjust the value as needed to either increase
+ * or decrease the value based on link speeds of 10G, 2.5G, 1G, etc.
+ *
+ * Use addition as we have already recorded the new latency flag
+ * for the ITR value.
+ */
+ itr += DIV_ROUND_UP(avg_wire_size, i40e_itr_divisor(q_vector)) *
+ I40E_ITR_ADAPTIVE_MIN_INC;
+
+ if ((itr & I40E_ITR_MASK) > I40E_ITR_ADAPTIVE_MAX_USECS) {
+ itr &= I40E_ITR_ADAPTIVE_LATENCY;
+ itr += I40E_ITR_ADAPTIVE_MAX_USECS;
}
+clear_counts:
+ /* write back value */
+ rc->target_itr = itr;
+
+ /* next update should occur within next jiffy */
+ rc->next_update = next_update + 1;
+
rc->total_bytes = 0;
rc->total_packets = 0;
- rc->last_itr_update = jiffies;
-
- if (new_itr != rc->itr) {
- rc->itr = new_itr;
- return true;
- }
- return false;
}
/**
@@ -1434,9 +1588,8 @@ static bool i40e_alloc_mapped_page(struct i40e_ring *rx_ring,
bi->dma = dma;
bi->page = page;
bi->page_offset = i40e_rx_offset(rx_ring);
-
- /* initialize pagecnt_bias to 1 representing we fully own page */
- bi->pagecnt_bias = 1;
+ page_ref_add(page, USHRT_MAX - 1);
+ bi->pagecnt_bias = USHRT_MAX;
return true;
}
@@ -1802,8 +1955,8 @@ static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer)
* the pagecnt_bias and page count so that we fully restock the
* number of references the driver holds.
*/
- if (unlikely(!pagecnt_bias)) {
- page_ref_add(page, USHRT_MAX);
+ if (unlikely(pagecnt_bias == 1)) {
+ page_ref_add(page, USHRT_MAX - 1);
rx_buffer->pagecnt_bias = USHRT_MAX;
}
@@ -1991,7 +2144,7 @@ static struct sk_buff *i40e_build_skb(struct i40e_ring *rx_ring,
* @rx_buffer: rx buffer to pull data from
*
* This function will clean up the contents of the rx_buffer. It will
- * either recycle the bufer or unmap it and free the associated resources.
+ * either recycle the buffer or unmap it and free the associated resources.
*/
static void i40e_put_rx_buffer(struct i40e_ring *rx_ring,
struct i40e_rx_buffer *rx_buffer)
@@ -2061,7 +2214,7 @@ static int i40e_xmit_xdp_ring(struct xdp_buff *xdp,
static struct sk_buff *i40e_run_xdp(struct i40e_ring *rx_ring,
struct xdp_buff *xdp)
{
- int result = I40E_XDP_PASS;
+ int err, result = I40E_XDP_PASS;
struct i40e_ring *xdp_ring;
struct bpf_prog *xdp_prog;
u32 act;
@@ -2080,6 +2233,10 @@ static struct sk_buff *i40e_run_xdp(struct i40e_ring *rx_ring,
xdp_ring = rx_ring->vsi->xdp_rings[rx_ring->queue_index];
result = i40e_xmit_xdp_ring(xdp, xdp_ring);
break;
+ case XDP_REDIRECT:
+ err = xdp_do_redirect(rx_ring->netdev, xdp, xdp_prog);
+ result = !err ? I40E_XDP_TX : I40E_XDP_CONSUMED;
+ break;
default:
bpf_warn_invalid_xdp_action(act);
case XDP_ABORTED:
@@ -2115,6 +2272,15 @@ static void i40e_rx_buffer_flip(struct i40e_ring *rx_ring,
#endif
}
+static inline void i40e_xdp_ring_update_tail(struct i40e_ring *xdp_ring)
+{
+ /* Force memory writes to complete before letting h/w
+ * know there are new descriptors to fetch.
+ */
+ wmb();
+ writel_relaxed(xdp_ring->next_to_use, xdp_ring->tail);
+}
+
/**
* i40e_clean_rx_irq - Clean completed descriptors from Rx ring - bounce buf
* @rx_ring: rx descriptor ring to transact packets on
@@ -2249,16 +2415,11 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget)
}
if (xdp_xmit) {
- struct i40e_ring *xdp_ring;
+ struct i40e_ring *xdp_ring =
+ rx_ring->vsi->xdp_rings[rx_ring->queue_index];
- xdp_ring = rx_ring->vsi->xdp_rings[rx_ring->queue_index];
-
- /* Force memory writes to complete before letting h/w
- * know there are new descriptors to fetch.
- */
- wmb();
-
- writel(xdp_ring->next_to_use, xdp_ring->tail);
+ i40e_xdp_ring_update_tail(xdp_ring);
+ xdp_do_flush_map();
}
rx_ring->skb = skb;
@@ -2274,29 +2435,45 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget)
return failure ? budget : (int)total_rx_packets;
}
-static u32 i40e_buildreg_itr(const int type, const u16 itr)
+static inline u32 i40e_buildreg_itr(const int type, u16 itr)
{
u32 val;
+ /* We don't bother with setting the CLEARPBA bit as the data sheet
+ * points out doing so is "meaningless since it was already
+ * auto-cleared". The auto-clearing happens when the interrupt is
+ * asserted.
+ *
+ * Hardware errata 28 for also indicates that writing to a
+ * xxINT_DYN_CTLx CSR with INTENA_MSK (bit 31) set to 0 will clear
+ * an event in the PBA anyway so we need to rely on the automask
+ * to hold pending events for us until the interrupt is re-enabled
+ *
+ * The itr value is reported in microseconds, and the register
+ * value is recorded in 2 microsecond units. For this reason we
+ * only need to shift by the interval shift - 1 instead of the
+ * full value.
+ */
+ itr &= I40E_ITR_MASK;
+
val = I40E_PFINT_DYN_CTLN_INTENA_MASK |
- I40E_PFINT_DYN_CTLN_CLEARPBA_MASK |
(type << I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT) |
- (itr << I40E_PFINT_DYN_CTLN_INTERVAL_SHIFT);
+ (itr << (I40E_PFINT_DYN_CTLN_INTERVAL_SHIFT - 1));
return val;
}
/* a small macro to shorten up some long lines */
#define INTREG I40E_PFINT_DYN_CTLN
-static inline int get_rx_itr(struct i40e_vsi *vsi, int idx)
-{
- return vsi->rx_rings[idx]->rx_itr_setting;
-}
-static inline int get_tx_itr(struct i40e_vsi *vsi, int idx)
-{
- return vsi->tx_rings[idx]->tx_itr_setting;
-}
+/* The act of updating the ITR will cause it to immediately trigger. In order
+ * to prevent this from throwing off adaptive update statistics we defer the
+ * update so that it can only happen so often. So after either Tx or Rx are
+ * updated we make the adaptive scheme wait until either the ITR completely
+ * expires via the next_update expiration or we have been through at least
+ * 3 interrupts.
+ */
+#define ITR_COUNTDOWN_START 3
/**
* i40e_update_enable_itr - Update itr and re-enable MSIX interrupt
@@ -2308,10 +2485,7 @@ static inline void i40e_update_enable_itr(struct i40e_vsi *vsi,
struct i40e_q_vector *q_vector)
{
struct i40e_hw *hw = &vsi->back->hw;
- bool rx = false, tx = false;
- u32 rxval, txval;
- int idx = q_vector->v_idx;
- int rx_itr_setting, tx_itr_setting;
+ u32 intval;
/* If we don't have MSIX, then we only need to re-enable icr0 */
if (!(vsi->back->flags & I40E_FLAG_MSIX_ENABLED)) {
@@ -2319,65 +2493,49 @@ static inline void i40e_update_enable_itr(struct i40e_vsi *vsi,
return;
}
- /* avoid dynamic calculation if in countdown mode OR if
- * all dynamic is disabled
- */
- rxval = txval = i40e_buildreg_itr(I40E_ITR_NONE, 0);
-
- rx_itr_setting = get_rx_itr(vsi, idx);
- tx_itr_setting = get_tx_itr(vsi, idx);
-
- if (q_vector->itr_countdown > 0 ||
- (!ITR_IS_DYNAMIC(rx_itr_setting) &&
- !ITR_IS_DYNAMIC(tx_itr_setting))) {
- goto enable_int;
- }
-
- if (ITR_IS_DYNAMIC(rx_itr_setting)) {
- rx = i40e_set_new_dynamic_itr(&q_vector->rx);
- rxval = i40e_buildreg_itr(I40E_RX_ITR, q_vector->rx.itr);
- }
-
- if (ITR_IS_DYNAMIC(tx_itr_setting)) {
- tx = i40e_set_new_dynamic_itr(&q_vector->tx);
- txval = i40e_buildreg_itr(I40E_TX_ITR, q_vector->tx.itr);
- }
-
- if (rx || tx) {
- /* get the higher of the two ITR adjustments and
- * use the same value for both ITR registers
- * when in adaptive mode (Rx and/or Tx)
- */
- u16 itr = max(q_vector->tx.itr, q_vector->rx.itr);
-
- q_vector->tx.itr = q_vector->rx.itr = itr;
- txval = i40e_buildreg_itr(I40E_TX_ITR, itr);
- tx = true;
- rxval = i40e_buildreg_itr(I40E_RX_ITR, itr);
- rx = true;
- }
+ /* These will do nothing if dynamic updates are not enabled */
+ i40e_update_itr(q_vector, &q_vector->tx);
+ i40e_update_itr(q_vector, &q_vector->rx);
- /* only need to enable the interrupt once, but need
- * to possibly update both ITR values
+ /* This block of logic allows us to get away with only updating
+ * one ITR value with each interrupt. The idea is to perform a
+ * pseudo-lazy update with the following criteria.
+ *
+ * 1. Rx is given higher priority than Tx if both are in same state
+ * 2. If we must reduce an ITR that is given highest priority.
+ * 3. We then give priority to increasing ITR based on amount.
*/
- if (rx) {
- /* set the INTENA_MSK_MASK so that this first write
- * won't actually enable the interrupt, instead just
- * updating the ITR (it's bit 31 PF and VF)
+ if (q_vector->rx.target_itr < q_vector->rx.current_itr) {
+ /* Rx ITR needs to be reduced, this is highest priority */
+ intval = i40e_buildreg_itr(I40E_RX_ITR,
+ q_vector->rx.target_itr);
+ q_vector->rx.current_itr = q_vector->rx.target_itr;
+ q_vector->itr_countdown = ITR_COUNTDOWN_START;
+ } else if ((q_vector->tx.target_itr < q_vector->tx.current_itr) ||
+ ((q_vector->rx.target_itr - q_vector->rx.current_itr) <
+ (q_vector->tx.target_itr - q_vector->tx.current_itr))) {
+ /* Tx ITR needs to be reduced, this is second priority
+ * Tx ITR needs to be increased more than Rx, fourth priority
*/
- rxval |= BIT(31);
- /* don't check _DOWN because interrupt isn't being enabled */
- wr32(hw, INTREG(q_vector->reg_idx), rxval);
+ intval = i40e_buildreg_itr(I40E_TX_ITR,
+ q_vector->tx.target_itr);
+ q_vector->tx.current_itr = q_vector->tx.target_itr;
+ q_vector->itr_countdown = ITR_COUNTDOWN_START;
+ } else if (q_vector->rx.current_itr != q_vector->rx.target_itr) {
+ /* Rx ITR needs to be increased, third priority */
+ intval = i40e_buildreg_itr(I40E_RX_ITR,
+ q_vector->rx.target_itr);
+ q_vector->rx.current_itr = q_vector->rx.target_itr;
+ q_vector->itr_countdown = ITR_COUNTDOWN_START;
+ } else {
+ /* No ITR update, lowest priority */
+ intval = i40e_buildreg_itr(I40E_ITR_NONE, 0);
+ if (q_vector->itr_countdown)
+ q_vector->itr_countdown--;
}
-enable_int:
if (!test_bit(__I40E_VSI_DOWN, vsi->state))
- wr32(hw, INTREG(q_vector->reg_idx), txval);
-
- if (q_vector->itr_countdown)
- q_vector->itr_countdown--;
- else
- q_vector->itr_countdown = ITR_COUNTDOWN_START;
+ wr32(hw, INTREG(q_vector->reg_idx), intval);
}
/**
@@ -2501,7 +2659,7 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb,
if (!(pf->flags & I40E_FLAG_FD_ATR_ENABLED))
return;
- if (pf->flags & I40E_FLAG_FD_ATR_AUTO_DISABLED)
+ if (test_bit(__I40E_FD_ATR_AUTO_DISABLED, pf->state))
return;
/* if sampling is disabled do nothing */
@@ -2541,7 +2699,7 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb,
th = (struct tcphdr *)(hdr.network + hlen);
/* Due to lack of space, no more new filters can be programmed */
- if (th->syn && (pf->flags & I40E_FLAG_FD_ATR_AUTO_DISABLED))
+ if (th->syn && test_bit(__I40E_FD_ATR_AUTO_DISABLED, pf->state))
return;
if (pf->flags & I40E_FLAG_HW_ATR_EVICT_ENABLED) {
/* HW ATR eviction will take care of removing filters on FIN
@@ -3509,3 +3667,49 @@ netdev_tx_t i40e_lan_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
return i40e_xmit_frame_ring(skb, tx_ring);
}
+
+/**
+ * i40e_xdp_xmit - Implements ndo_xdp_xmit
+ * @dev: netdev
+ * @xdp: XDP buffer
+ *
+ * Returns Zero if sent, else an error code
+ **/
+int i40e_xdp_xmit(struct net_device *dev, struct xdp_buff *xdp)
+{
+ struct i40e_netdev_priv *np = netdev_priv(dev);
+ unsigned int queue_index = smp_processor_id();
+ struct i40e_vsi *vsi = np->vsi;
+ int err;
+
+ if (test_bit(__I40E_VSI_DOWN, vsi->state))
+ return -ENETDOWN;
+
+ if (!i40e_enabled_xdp_vsi(vsi) || queue_index >= vsi->num_queue_pairs)
+ return -ENXIO;
+
+ err = i40e_xmit_xdp_ring(xdp, vsi->xdp_rings[queue_index]);
+ if (err != I40E_XDP_TX)
+ return -ENOSPC;
+
+ return 0;
+}
+
+/**
+ * i40e_xdp_flush - Implements ndo_xdp_flush
+ * @dev: netdev
+ **/
+void i40e_xdp_flush(struct net_device *dev)
+{
+ struct i40e_netdev_priv *np = netdev_priv(dev);
+ unsigned int queue_index = smp_processor_id();
+ struct i40e_vsi *vsi = np->vsi;
+
+ if (test_bit(__I40E_VSI_DOWN, vsi->state))
+ return;
+
+ if (!i40e_enabled_xdp_vsi(vsi) || queue_index >= vsi->num_queue_pairs)
+ return;
+
+ i40e_xdp_ring_update_tail(vsi->xdp_rings[queue_index]);
+}
diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h
index 701b708628b0..3043483ec426 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
@@ -30,32 +31,37 @@
#include <net/xdp.h>
/* Interrupt Throttling and Rate Limiting Goodies */
-
-#define I40E_MAX_ITR 0x0FF0 /* reg uses 2 usec resolution */
-#define I40E_MIN_ITR 0x0001 /* reg uses 2 usec resolution */
-#define I40E_ITR_100K 0x0005
-#define I40E_ITR_50K 0x000A
-#define I40E_ITR_20K 0x0019
-#define I40E_ITR_18K 0x001B
-#define I40E_ITR_8K 0x003E
-#define I40E_ITR_4K 0x007A
-#define I40E_MAX_INTRL 0x3B /* reg uses 4 usec resolution */
-#define I40E_ITR_RX_DEF (ITR_REG_TO_USEC(I40E_ITR_20K) | \
- I40E_ITR_DYNAMIC)
-#define I40E_ITR_TX_DEF (ITR_REG_TO_USEC(I40E_ITR_20K) | \
- I40E_ITR_DYNAMIC)
-#define I40E_ITR_DYNAMIC 0x8000 /* use top bit as a flag */
-#define I40E_MIN_INT_RATE 250 /* ~= 1000000 / (I40E_MAX_ITR * 2) */
-#define I40E_MAX_INT_RATE 500000 /* == 1000000 / (I40E_MIN_ITR * 2) */
#define I40E_DEFAULT_IRQ_WORK 256
-#define ITR_TO_REG(setting) ((setting & ~I40E_ITR_DYNAMIC) >> 1)
-#define ITR_IS_DYNAMIC(setting) (!!(setting & I40E_ITR_DYNAMIC))
-#define ITR_REG_TO_USEC(itr_reg) (itr_reg << 1)
+
+/* The datasheet for the X710 and XL710 indicate that the maximum value for
+ * the ITR is 8160usec which is then called out as 0xFF0 with a 2usec
+ * resolution. 8160 is 0x1FE0 when written out in hex. So instead of storing
+ * the register value which is divided by 2 lets use the actual values and
+ * avoid an excessive amount of translation.
+ */
+#define I40E_ITR_DYNAMIC 0x8000 /* use top bit as a flag */
+#define I40E_ITR_MASK 0x1FFE /* mask for ITR register value */
+#define I40E_MIN_ITR 2 /* reg uses 2 usec resolution */
+#define I40E_ITR_100K 10 /* all values below must be even */
+#define I40E_ITR_50K 20
+#define I40E_ITR_20K 50
+#define I40E_ITR_18K 60
+#define I40E_ITR_8K 122
+#define I40E_MAX_ITR 8160 /* maximum value as per datasheet */
+#define ITR_TO_REG(setting) ((setting) & ~I40E_ITR_DYNAMIC)
+#define ITR_REG_ALIGN(setting) __ALIGN_MASK(setting, ~I40E_ITR_MASK)
+#define ITR_IS_DYNAMIC(setting) (!!((setting) & I40E_ITR_DYNAMIC))
+
+#define I40E_ITR_RX_DEF (I40E_ITR_20K | I40E_ITR_DYNAMIC)
+#define I40E_ITR_TX_DEF (I40E_ITR_20K | I40E_ITR_DYNAMIC)
+
/* 0x40 is the enable bit for interrupt rate limiting, and must be set if
* the value of the rate limit is non-zero
*/
#define INTRL_ENA BIT(6)
+#define I40E_MAX_INTRL 0x3B /* reg uses 4 usec resolution */
#define INTRL_REG_TO_USEC(intrl) ((intrl & ~INTRL_ENA) << 2)
+
/**
* i40e_intrl_usec_to_reg - convert interrupt rate limit to register
* @intrl: interrupt rate limit to convert
@@ -382,8 +388,7 @@ struct i40e_ring {
* these values always store the USER setting, and must be converted
* before programming to a register.
*/
- u16 rx_itr_setting;
- u16 tx_itr_setting;
+ u16 itr_setting;
u16 count; /* Number of descriptors */
u16 reg_idx; /* HW register index of the ring */
@@ -459,21 +464,21 @@ static inline void set_ring_xdp(struct i40e_ring *ring)
ring->flags |= I40E_TXR_FLAGS_XDP;
}
-enum i40e_latency_range {
- I40E_LOWEST_LATENCY = 0,
- I40E_LOW_LATENCY = 1,
- I40E_BULK_LATENCY = 2,
-};
+#define I40E_ITR_ADAPTIVE_MIN_INC 0x0002
+#define I40E_ITR_ADAPTIVE_MIN_USECS 0x0002
+#define I40E_ITR_ADAPTIVE_MAX_USECS 0x007e
+#define I40E_ITR_ADAPTIVE_LATENCY 0x8000
+#define I40E_ITR_ADAPTIVE_BULK 0x0000
+#define ITR_IS_BULK(x) (!((x) & I40E_ITR_ADAPTIVE_LATENCY))
struct i40e_ring_container {
- /* array of pointers to rings */
- struct i40e_ring *ring;
+ struct i40e_ring *ring; /* pointer to linked list of ring(s) */
+ unsigned long next_update; /* jiffies value of next update */
unsigned int total_bytes; /* total bytes processed this int */
unsigned int total_packets; /* total packets processed this int */
- unsigned long last_itr_update; /* jiffies of last ITR update */
u16 count;
- enum i40e_latency_range latency_range;
- u16 itr;
+ u16 target_itr; /* target ITR setting for ring(s) */
+ u16 current_itr; /* current ITR setting for ring(s) */
};
/* iterator for handling rings in ring container */
@@ -501,10 +506,12 @@ void i40e_free_tx_resources(struct i40e_ring *tx_ring);
void i40e_free_rx_resources(struct i40e_ring *rx_ring);
int i40e_napi_poll(struct napi_struct *napi, int budget);
void i40e_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector);
-u32 i40e_get_tx_pending(struct i40e_ring *ring);
+u32 i40e_get_tx_pending(struct i40e_ring *ring, bool in_sw);
void i40e_detect_recover_hung(struct i40e_vsi *vsi);
int __i40e_maybe_stop_tx(struct i40e_ring *tx_ring, int size);
bool __i40e_chk_linearize(struct sk_buff *skb);
+int i40e_xdp_xmit(struct net_device *dev, struct xdp_buff *xdp);
+void i40e_xdp_flush(struct net_device *dev);
/**
* i40e_get_head - Retrieve head from head writeback
diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h
index cd294e6a8587..bfb80092b352 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_type.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_type.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
@@ -39,7 +40,7 @@
#define I40E_MASK(mask, shift) ((u32)(mask) << (shift))
#define I40E_MAX_VSI_QP 16
-#define I40E_MAX_VF_VSI 3
+#define I40E_MAX_VF_VSI 4
#define I40E_MAX_CHAINED_RX_BUFFERS 5
#define I40E_MAX_PF_UDP_OFFLOAD_PORTS 16
@@ -1336,6 +1337,9 @@ struct i40e_hw_port_stats {
#define I40E_SR_PCIE_ALT_MODULE_MAX_SIZE 1024
#define I40E_SR_CONTROL_WORD_1_SHIFT 0x06
#define I40E_SR_CONTROL_WORD_1_MASK (0x03 << I40E_SR_CONTROL_WORD_1_SHIFT)
+#define I40E_PTR_TYPE BIT(15)
+#define I40E_SR_OCP_CFG_WORD0 0x2B
+#define I40E_SR_OCP_ENABLED BIT(15)
/* Shadow RAM related */
#define I40E_SR_SECTOR_SIZE_IN_WORDS 0x800
diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
index e9309fb9084b..35173cbe80f7 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
@@ -258,6 +259,38 @@ static u16 i40e_vc_get_pf_queue_id(struct i40e_vf *vf, u16 vsi_id,
}
/**
+ * i40e_get_real_pf_qid
+ * @vf: pointer to the VF info
+ * @vsi_id: vsi id
+ * @queue_id: queue number
+ *
+ * wrapper function to get pf_queue_id handling ADq code as well
+ **/
+static u16 i40e_get_real_pf_qid(struct i40e_vf *vf, u16 vsi_id, u16 queue_id)
+{
+ int i;
+
+ if (vf->adq_enabled) {
+ /* Although VF considers all the queues(can be 1 to 16) as its
+ * own but they may actually belong to different VSIs(up to 4).
+ * We need to find which queues belongs to which VSI.
+ */
+ for (i = 0; i < vf->num_tc; i++) {
+ if (queue_id < vf->ch[i].num_qps) {
+ vsi_id = vf->ch[i].vsi_id;
+ break;
+ }
+ /* find right queue id which is relative to a
+ * given VSI.
+ */
+ queue_id -= vf->ch[i].num_qps;
+ }
+ }
+
+ return i40e_vc_get_pf_queue_id(vf, vsi_id, queue_id);
+}
+
+/**
* i40e_config_irq_link_list
* @vf: pointer to the VF info
* @vsi_id: id of VSI as given by the FW
@@ -310,7 +343,7 @@ static void i40e_config_irq_link_list(struct i40e_vf *vf, u16 vsi_id,
vsi_queue_id = next_q / I40E_VIRTCHNL_SUPPORTED_QTYPES;
qtype = next_q % I40E_VIRTCHNL_SUPPORTED_QTYPES;
- pf_queue_id = i40e_vc_get_pf_queue_id(vf, vsi_id, vsi_queue_id);
+ pf_queue_id = i40e_get_real_pf_qid(vf, vsi_id, vsi_queue_id);
reg = ((qtype << I40E_VPINT_LNKLSTN_FIRSTQ_TYPE_SHIFT) | pf_queue_id);
wr32(hw, reg_idx, reg);
@@ -333,8 +366,9 @@ static void i40e_config_irq_link_list(struct i40e_vf *vf, u16 vsi_id,
if (next_q < size) {
vsi_queue_id = next_q / I40E_VIRTCHNL_SUPPORTED_QTYPES;
qtype = next_q % I40E_VIRTCHNL_SUPPORTED_QTYPES;
- pf_queue_id = i40e_vc_get_pf_queue_id(vf, vsi_id,
- vsi_queue_id);
+ pf_queue_id = i40e_get_real_pf_qid(vf,
+ vsi_id,
+ vsi_queue_id);
} else {
pf_queue_id = I40E_QUEUE_END_OF_LIST;
qtype = 0;
@@ -669,18 +703,20 @@ error_param:
/**
* i40e_alloc_vsi_res
* @vf: pointer to the VF info
- * @type: type of VSI to allocate
+ * @idx: VSI index, applies only for ADq mode, zero otherwise
*
* alloc VF vsi context & resources
**/
-static int i40e_alloc_vsi_res(struct i40e_vf *vf, enum i40e_vsi_type type)
+static int i40e_alloc_vsi_res(struct i40e_vf *vf, u8 idx)
{
struct i40e_mac_filter *f = NULL;
struct i40e_pf *pf = vf->pf;
struct i40e_vsi *vsi;
+ u64 max_tx_rate = 0;
int ret = 0;
- vsi = i40e_vsi_setup(pf, type, pf->vsi[pf->lan_vsi]->seid, vf->vf_id);
+ vsi = i40e_vsi_setup(pf, I40E_VSI_SRIOV, pf->vsi[pf->lan_vsi]->seid,
+ vf->vf_id);
if (!vsi) {
dev_err(&pf->pdev->dev,
@@ -689,7 +725,8 @@ static int i40e_alloc_vsi_res(struct i40e_vf *vf, enum i40e_vsi_type type)
ret = -ENOENT;
goto error_alloc_vsi_res;
}
- if (type == I40E_VSI_SRIOV) {
+
+ if (!idx) {
u64 hena = i40e_pf_get_default_rss_hena(pf);
u8 broadcast[ETH_ALEN];
@@ -721,17 +758,29 @@ static int i40e_alloc_vsi_res(struct i40e_vf *vf, enum i40e_vsi_type type)
spin_unlock_bh(&vsi->mac_filter_hash_lock);
wr32(&pf->hw, I40E_VFQF_HENA1(0, vf->vf_id), (u32)hena);
wr32(&pf->hw, I40E_VFQF_HENA1(1, vf->vf_id), (u32)(hena >> 32));
+ /* program mac filter only for VF VSI */
+ ret = i40e_sync_vsi_filters(vsi);
+ if (ret)
+ dev_err(&pf->pdev->dev, "Unable to program ucast filters\n");
}
- /* program mac filter */
- ret = i40e_sync_vsi_filters(vsi);
- if (ret)
- dev_err(&pf->pdev->dev, "Unable to program ucast filters\n");
+ /* storing VSI index and id for ADq and don't apply the mac filter */
+ if (vf->adq_enabled) {
+ vf->ch[idx].vsi_idx = vsi->idx;
+ vf->ch[idx].vsi_id = vsi->id;
+ }
/* Set VF bandwidth if specified */
if (vf->tx_rate) {
+ max_tx_rate = vf->tx_rate;
+ } else if (vf->ch[idx].max_tx_rate) {
+ max_tx_rate = vf->ch[idx].max_tx_rate;
+ }
+
+ if (max_tx_rate) {
+ max_tx_rate = div_u64(max_tx_rate, I40E_BW_CREDIT_DIVISOR);
ret = i40e_aq_config_vsi_bw_limit(&pf->hw, vsi->seid,
- vf->tx_rate / 50, 0, NULL);
+ max_tx_rate, 0, NULL);
if (ret)
dev_err(&pf->pdev->dev, "Unable to set tx rate, VF %d, error code %d.\n",
vf->vf_id, ret);
@@ -742,6 +791,92 @@ error_alloc_vsi_res:
}
/**
+ * i40e_map_pf_queues_to_vsi
+ * @vf: pointer to the VF info
+ *
+ * PF maps LQPs to a VF by programming VSILAN_QTABLE & VPLAN_QTABLE. This
+ * function takes care of first part VSILAN_QTABLE, mapping pf queues to VSI.
+ **/
+static void i40e_map_pf_queues_to_vsi(struct i40e_vf *vf)
+{
+ struct i40e_pf *pf = vf->pf;
+ struct i40e_hw *hw = &pf->hw;
+ u32 reg, num_tc = 1; /* VF has at least one traffic class */
+ u16 vsi_id, qps;
+ int i, j;
+
+ if (vf->adq_enabled)
+ num_tc = vf->num_tc;
+
+ for (i = 0; i < num_tc; i++) {
+ if (vf->adq_enabled) {
+ qps = vf->ch[i].num_qps;
+ vsi_id = vf->ch[i].vsi_id;
+ } else {
+ qps = pf->vsi[vf->lan_vsi_idx]->alloc_queue_pairs;
+ vsi_id = vf->lan_vsi_id;
+ }
+
+ for (j = 0; j < 7; j++) {
+ if (j * 2 >= qps) {
+ /* end of list */
+ reg = 0x07FF07FF;
+ } else {
+ u16 qid = i40e_vc_get_pf_queue_id(vf,
+ vsi_id,
+ j * 2);
+ reg = qid;
+ qid = i40e_vc_get_pf_queue_id(vf, vsi_id,
+ (j * 2) + 1);
+ reg |= qid << 16;
+ }
+ i40e_write_rx_ctl(hw,
+ I40E_VSILAN_QTABLE(j, vsi_id),
+ reg);
+ }
+ }
+}
+
+/**
+ * i40e_map_pf_to_vf_queues
+ * @vf: pointer to the VF info
+ *
+ * PF maps LQPs to a VF by programming VSILAN_QTABLE & VPLAN_QTABLE. This
+ * function takes care of the second part VPLAN_QTABLE & completes VF mappings.
+ **/
+static void i40e_map_pf_to_vf_queues(struct i40e_vf *vf)
+{
+ struct i40e_pf *pf = vf->pf;
+ struct i40e_hw *hw = &pf->hw;
+ u32 reg, total_qps = 0;
+ u32 qps, num_tc = 1; /* VF has at least one traffic class */
+ u16 vsi_id, qid;
+ int i, j;
+
+ if (vf->adq_enabled)
+ num_tc = vf->num_tc;
+
+ for (i = 0; i < num_tc; i++) {
+ if (vf->adq_enabled) {
+ qps = vf->ch[i].num_qps;
+ vsi_id = vf->ch[i].vsi_id;
+ } else {
+ qps = pf->vsi[vf->lan_vsi_idx]->alloc_queue_pairs;
+ vsi_id = vf->lan_vsi_id;
+ }
+
+ for (j = 0; j < qps; j++) {
+ qid = i40e_vc_get_pf_queue_id(vf, vsi_id, j);
+
+ reg = (qid & I40E_VPLAN_QTABLE_QINDEX_MASK);
+ wr32(hw, I40E_VPLAN_QTABLE(total_qps, vf->vf_id),
+ reg);
+ total_qps++;
+ }
+ }
+}
+
+/**
* i40e_enable_vf_mappings
* @vf: pointer to the VF info
*
@@ -751,8 +886,7 @@ static void i40e_enable_vf_mappings(struct i40e_vf *vf)
{
struct i40e_pf *pf = vf->pf;
struct i40e_hw *hw = &pf->hw;
- u32 reg, total_queue_pairs = 0;
- int j;
+ u32 reg;
/* Tell the hardware we're using noncontiguous mapping. HW requires
* that VF queues be mapped using this method, even when they are
@@ -765,30 +899,8 @@ static void i40e_enable_vf_mappings(struct i40e_vf *vf)
reg = I40E_VPLAN_MAPENA_TXRX_ENA_MASK;
wr32(hw, I40E_VPLAN_MAPENA(vf->vf_id), reg);
- /* map PF queues to VF queues */
- for (j = 0; j < pf->vsi[vf->lan_vsi_idx]->alloc_queue_pairs; j++) {
- u16 qid = i40e_vc_get_pf_queue_id(vf, vf->lan_vsi_id, j);
-
- reg = (qid & I40E_VPLAN_QTABLE_QINDEX_MASK);
- wr32(hw, I40E_VPLAN_QTABLE(total_queue_pairs, vf->vf_id), reg);
- total_queue_pairs++;
- }
-
- /* map PF queues to VSI */
- for (j = 0; j < 7; j++) {
- if (j * 2 >= pf->vsi[vf->lan_vsi_idx]->alloc_queue_pairs) {
- reg = 0x07FF07FF; /* unused */
- } else {
- u16 qid = i40e_vc_get_pf_queue_id(vf, vf->lan_vsi_id,
- j * 2);
- reg = qid;
- qid = i40e_vc_get_pf_queue_id(vf, vf->lan_vsi_id,
- (j * 2) + 1);
- reg |= qid << 16;
- }
- i40e_write_rx_ctl(hw, I40E_VSILAN_QTABLE(j, vf->lan_vsi_id),
- reg);
- }
+ i40e_map_pf_to_vf_queues(vf);
+ i40e_map_pf_queues_to_vsi(vf);
i40e_flush(hw);
}
@@ -824,7 +936,7 @@ static void i40e_free_vf_res(struct i40e_vf *vf)
struct i40e_pf *pf = vf->pf;
struct i40e_hw *hw = &pf->hw;
u32 reg_idx, reg;
- int i, msix_vf;
+ int i, j, msix_vf;
/* Start by disabling VF's configuration API to prevent the OS from
* accessing the VF's VSI after it's freed / invalidated.
@@ -846,6 +958,20 @@ static void i40e_free_vf_res(struct i40e_vf *vf)
vf->lan_vsi_id = 0;
vf->num_mac = 0;
}
+
+ /* do the accounting and remove additional ADq VSI's */
+ if (vf->adq_enabled && vf->ch[0].vsi_idx) {
+ for (j = 0; j < vf->num_tc; j++) {
+ /* At this point VSI0 is already released so don't
+ * release it again and only clear their values in
+ * structure variables
+ */
+ if (j)
+ i40e_vsi_release(pf->vsi[vf->ch[j].vsi_idx]);
+ vf->ch[j].vsi_idx = 0;
+ vf->ch[j].vsi_id = 0;
+ }
+ }
msix_vf = pf->hw.func_caps.num_msix_vectors_vf;
/* disable interrupts so the VF starts in a known state */
@@ -891,7 +1017,7 @@ static int i40e_alloc_vf_res(struct i40e_vf *vf)
{
struct i40e_pf *pf = vf->pf;
int total_queue_pairs = 0;
- int ret;
+ int ret, idx;
if (vf->num_req_queues &&
vf->num_req_queues <= pf->queues_left + I40E_DEFAULT_QUEUES_PER_VF)
@@ -900,11 +1026,30 @@ static int i40e_alloc_vf_res(struct i40e_vf *vf)
pf->num_vf_qps = I40E_DEFAULT_QUEUES_PER_VF;
/* allocate hw vsi context & associated resources */
- ret = i40e_alloc_vsi_res(vf, I40E_VSI_SRIOV);
+ ret = i40e_alloc_vsi_res(vf, 0);
if (ret)
goto error_alloc;
total_queue_pairs += pf->vsi[vf->lan_vsi_idx]->alloc_queue_pairs;
+ /* allocate additional VSIs based on tc information for ADq */
+ if (vf->adq_enabled) {
+ if (pf->queues_left >=
+ (I40E_MAX_VF_QUEUES - I40E_DEFAULT_QUEUES_PER_VF)) {
+ /* TC 0 always belongs to VF VSI */
+ for (idx = 1; idx < vf->num_tc; idx++) {
+ ret = i40e_alloc_vsi_res(vf, idx);
+ if (ret)
+ goto error_alloc;
+ }
+ /* send correct number of queues */
+ total_queue_pairs = I40E_MAX_VF_QUEUES;
+ } else {
+ dev_info(&pf->pdev->dev, "VF %d: Not enough queues to allocate, disabling ADq\n",
+ vf->vf_id);
+ vf->adq_enabled = false;
+ }
+ }
+
/* We account for each VF to get a default number of queue pairs. If
* the VF has now requested more, we need to account for that to make
* certain we never request more queues than we actually have left in
@@ -1537,6 +1682,27 @@ static int i40e_vc_get_version_msg(struct i40e_vf *vf, u8 *msg)
}
/**
+ * i40e_del_qch - delete all the additional VSIs created as a part of ADq
+ * @vf: pointer to VF structure
+ **/
+static void i40e_del_qch(struct i40e_vf *vf)
+{
+ struct i40e_pf *pf = vf->pf;
+ int i;
+
+ /* first element in the array belongs to primary VF VSI and we shouldn't
+ * delete it. We should however delete the rest of the VSIs created
+ */
+ for (i = 1; i < vf->num_tc; i++) {
+ if (vf->ch[i].vsi_idx) {
+ i40e_vsi_release(pf->vsi[vf->ch[i].vsi_idx]);
+ vf->ch[i].vsi_idx = 0;
+ vf->ch[i].vsi_id = 0;
+ }
+ }
+}
+
+/**
* i40e_vc_get_vf_resources_msg
* @vf: pointer to the VF info
* @msg: pointer to the msg buffer
@@ -1631,6 +1797,9 @@ static int i40e_vc_get_vf_resources_msg(struct i40e_vf *vf, u8 *msg)
if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_REQ_QUEUES)
vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_REQ_QUEUES;
+ if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_ADQ)
+ vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_ADQ;
+
vfres->num_vsis = num_vsis;
vfres->num_queue_pairs = vf->num_queue_pairs;
vfres->max_vectors = pf->hw.func_caps.num_msix_vectors_vf;
@@ -1855,27 +2024,37 @@ static int i40e_vc_config_queues_msg(struct i40e_vf *vf, u8 *msg, u16 msglen)
(struct virtchnl_vsi_queue_config_info *)msg;
struct virtchnl_queue_pair_info *qpi;
struct i40e_pf *pf = vf->pf;
- u16 vsi_id, vsi_queue_id;
+ u16 vsi_id, vsi_queue_id = 0;
i40e_status aq_ret = 0;
- int i;
+ int i, j = 0, idx = 0;
+
+ vsi_id = qci->vsi_id;
if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) {
aq_ret = I40E_ERR_PARAM;
goto error_param;
}
- vsi_id = qci->vsi_id;
if (!i40e_vc_isvalid_vsi_id(vf, vsi_id)) {
aq_ret = I40E_ERR_PARAM;
goto error_param;
}
+
for (i = 0; i < qci->num_queue_pairs; i++) {
qpi = &qci->qpair[i];
- vsi_queue_id = qpi->txq.queue_id;
- if ((qpi->txq.vsi_id != vsi_id) ||
- (qpi->rxq.vsi_id != vsi_id) ||
- (qpi->rxq.queue_id != vsi_queue_id) ||
- !i40e_vc_isvalid_queue_id(vf, vsi_id, vsi_queue_id)) {
+
+ if (!vf->adq_enabled) {
+ vsi_queue_id = qpi->txq.queue_id;
+
+ if (qpi->txq.vsi_id != qci->vsi_id ||
+ qpi->rxq.vsi_id != qci->vsi_id ||
+ qpi->rxq.queue_id != vsi_queue_id) {
+ aq_ret = I40E_ERR_PARAM;
+ goto error_param;
+ }
+ }
+
+ if (!i40e_vc_isvalid_queue_id(vf, vsi_id, vsi_queue_id)) {
aq_ret = I40E_ERR_PARAM;
goto error_param;
}
@@ -1887,9 +2066,33 @@ static int i40e_vc_config_queues_msg(struct i40e_vf *vf, u8 *msg, u16 msglen)
aq_ret = I40E_ERR_PARAM;
goto error_param;
}
+
+ /* For ADq there can be up to 4 VSIs with max 4 queues each.
+ * VF does not know about these additional VSIs and all
+ * it cares is about its own queues. PF configures these queues
+ * to its appropriate VSIs based on TC mapping
+ **/
+ if (vf->adq_enabled) {
+ if (j == (vf->ch[idx].num_qps - 1)) {
+ idx++;
+ j = 0; /* resetting the queue count */
+ vsi_queue_id = 0;
+ } else {
+ j++;
+ vsi_queue_id++;
+ }
+ vsi_id = vf->ch[idx].vsi_id;
+ }
}
/* set vsi num_queue_pairs in use to num configured by VF */
- pf->vsi[vf->lan_vsi_idx]->num_queue_pairs = qci->num_queue_pairs;
+ if (!vf->adq_enabled) {
+ pf->vsi[vf->lan_vsi_idx]->num_queue_pairs =
+ qci->num_queue_pairs;
+ } else {
+ for (i = 0; i < vf->num_tc; i++)
+ pf->vsi[vf->ch[i].vsi_idx]->num_queue_pairs =
+ vf->ch[i].num_qps;
+ }
error_param:
/* send the response to the VF */
@@ -1898,6 +2101,33 @@ error_param:
}
/**
+ * i40e_validate_queue_map
+ * @vsi_id: vsi id
+ * @queuemap: Tx or Rx queue map
+ *
+ * check if Tx or Rx queue map is valid
+ **/
+static int i40e_validate_queue_map(struct i40e_vf *vf, u16 vsi_id,
+ unsigned long queuemap)
+{
+ u16 vsi_queue_id, queue_id;
+
+ for_each_set_bit(vsi_queue_id, &queuemap, I40E_MAX_VSI_QP) {
+ if (vf->adq_enabled) {
+ vsi_id = vf->ch[vsi_queue_id / I40E_MAX_VF_VSI].vsi_id;
+ queue_id = (vsi_queue_id % I40E_DEFAULT_QUEUES_PER_VF);
+ } else {
+ queue_id = vsi_queue_id;
+ }
+
+ if (!i40e_vc_isvalid_queue_id(vf, vsi_id, queue_id))
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
* i40e_vc_config_irq_map_msg
* @vf: pointer to the VF info
* @msg: pointer to the msg buffer
@@ -1911,9 +2141,8 @@ static int i40e_vc_config_irq_map_msg(struct i40e_vf *vf, u8 *msg, u16 msglen)
struct virtchnl_irq_map_info *irqmap_info =
(struct virtchnl_irq_map_info *)msg;
struct virtchnl_vector_map *map;
- u16 vsi_id, vsi_queue_id, vector_id;
+ u16 vsi_id, vector_id;
i40e_status aq_ret = 0;
- unsigned long tempmap;
int i;
if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) {
@@ -1923,7 +2152,6 @@ static int i40e_vc_config_irq_map_msg(struct i40e_vf *vf, u8 *msg, u16 msglen)
for (i = 0; i < irqmap_info->num_vectors; i++) {
map = &irqmap_info->vecmap[i];
-
vector_id = map->vector_id;
vsi_id = map->vsi_id;
/* validate msg params */
@@ -1933,23 +2161,14 @@ static int i40e_vc_config_irq_map_msg(struct i40e_vf *vf, u8 *msg, u16 msglen)
goto error_param;
}
- /* lookout for the invalid queue index */
- tempmap = map->rxq_map;
- for_each_set_bit(vsi_queue_id, &tempmap, I40E_MAX_VSI_QP) {
- if (!i40e_vc_isvalid_queue_id(vf, vsi_id,
- vsi_queue_id)) {
- aq_ret = I40E_ERR_PARAM;
- goto error_param;
- }
+ if (i40e_validate_queue_map(vf, vsi_id, map->rxq_map)) {
+ aq_ret = I40E_ERR_PARAM;
+ goto error_param;
}
- tempmap = map->txq_map;
- for_each_set_bit(vsi_queue_id, &tempmap, I40E_MAX_VSI_QP) {
- if (!i40e_vc_isvalid_queue_id(vf, vsi_id,
- vsi_queue_id)) {
- aq_ret = I40E_ERR_PARAM;
- goto error_param;
- }
+ if (i40e_validate_queue_map(vf, vsi_id, map->txq_map)) {
+ aq_ret = I40E_ERR_PARAM;
+ goto error_param;
}
i40e_config_irq_link_list(vf, vsi_id, map);
@@ -1975,6 +2194,7 @@ static int i40e_vc_enable_queues_msg(struct i40e_vf *vf, u8 *msg, u16 msglen)
struct i40e_pf *pf = vf->pf;
u16 vsi_id = vqs->vsi_id;
i40e_status aq_ret = 0;
+ int i;
if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) {
aq_ret = I40E_ERR_PARAM;
@@ -1993,6 +2213,16 @@ static int i40e_vc_enable_queues_msg(struct i40e_vf *vf, u8 *msg, u16 msglen)
if (i40e_vsi_start_rings(pf->vsi[vf->lan_vsi_idx]))
aq_ret = I40E_ERR_TIMEOUT;
+
+ /* need to start the rings for additional ADq VSI's as well */
+ if (vf->adq_enabled) {
+ /* zero belongs to LAN VSI */
+ for (i = 1; i < vf->num_tc; i++) {
+ if (i40e_vsi_start_rings(pf->vsi[vf->ch[i].vsi_idx]))
+ aq_ret = I40E_ERR_TIMEOUT;
+ }
+ }
+
error_param:
/* send the response to the VF */
return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_ENABLE_QUEUES,
@@ -2139,25 +2369,47 @@ error_param:
/**
* i40e_check_vf_permission
* @vf: pointer to the VF info
- * @macaddr: pointer to the MAC Address being checked
+ * @al: MAC address list from virtchnl
*
- * Check if the VF has permission to add or delete unicast MAC address
- * filters and return error code -EPERM if not. Then check if the
- * address filter requested is broadcast or zero and if so return
- * an invalid MAC address error code.
+ * Check that the given list of MAC addresses is allowed. Will return -EPERM
+ * if any address in the list is not valid. Checks the following conditions:
+ *
+ * 1) broadcast and zero addresses are never valid
+ * 2) unicast addresses are not allowed if the VMM has administratively set
+ * the VF MAC address, unless the VF is marked as privileged.
+ * 3) There is enough space to add all the addresses.
+ *
+ * Note that to guarantee consistency, it is expected this function be called
+ * while holding the mac_filter_hash_lock, as otherwise the current number of
+ * addresses might not be accurate.
**/
-static inline int i40e_check_vf_permission(struct i40e_vf *vf, u8 *macaddr)
+static inline int i40e_check_vf_permission(struct i40e_vf *vf,
+ struct virtchnl_ether_addr_list *al)
{
struct i40e_pf *pf = vf->pf;
- int ret = 0;
+ int i;
+
+ /* If this VF is not privileged, then we can't add more than a limited
+ * number of addresses. Check to make sure that the additions do not
+ * push us over the limit.
+ */
+ if (!test_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps) &&
+ (vf->num_mac + al->num_elements) > I40E_VC_MAX_MAC_ADDR_PER_VF) {
+ dev_err(&pf->pdev->dev,
+ "Cannot add more MAC addresses, VF is not trusted, switch the VF to trusted to add more functionality\n");
+ return -EPERM;
+ }
+
+ for (i = 0; i < al->num_elements; i++) {
+ u8 *addr = al->list[i].addr;
+
+ if (is_broadcast_ether_addr(addr) ||
+ is_zero_ether_addr(addr)) {
+ dev_err(&pf->pdev->dev, "invalid VF MAC addr %pM\n",
+ addr);
+ return I40E_ERR_INVALID_MAC_ADDR;
+ }
- if (is_broadcast_ether_addr(macaddr) ||
- is_zero_ether_addr(macaddr)) {
- dev_err(&pf->pdev->dev, "invalid VF MAC addr %pM\n", macaddr);
- ret = I40E_ERR_INVALID_MAC_ADDR;
- } else if (vf->pf_set_mac && !is_multicast_ether_addr(macaddr) &&
- !test_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps) &&
- !ether_addr_equal(macaddr, vf->default_lan_addr.addr)) {
/* If the host VMM administrator has set the VF MAC address
* administratively via the ndo_set_vf_mac command then deny
* permission to the VF to add or delete unicast MAC addresses.
@@ -2165,16 +2417,16 @@ static inline int i40e_check_vf_permission(struct i40e_vf *vf, u8 *macaddr)
* The VF may request to set the MAC address filter already
* assigned to it so do not return an error in that case.
*/
- dev_err(&pf->pdev->dev,
- "VF attempting to override administratively set MAC address, reload the VF driver to resume normal operation\n");
- ret = -EPERM;
- } else if ((vf->num_mac >= I40E_VC_MAX_MAC_ADDR_PER_VF) &&
- !test_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps)) {
- dev_err(&pf->pdev->dev,
- "VF is not trusted, switch the VF to trusted to add more functionality\n");
- ret = -EPERM;
+ if (!test_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps) &&
+ !is_multicast_ether_addr(addr) && vf->pf_set_mac &&
+ !ether_addr_equal(addr, vf->default_lan_addr.addr)) {
+ dev_err(&pf->pdev->dev,
+ "VF attempting to override administratively set MAC address, reload the VF driver to resume normal operation\n");
+ return -EPERM;
+ }
}
- return ret;
+
+ return 0;
}
/**
@@ -2201,11 +2453,6 @@ static int i40e_vc_add_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen)
goto error_param;
}
- for (i = 0; i < al->num_elements; i++) {
- ret = i40e_check_vf_permission(vf, al->list[i].addr);
- if (ret)
- goto error_param;
- }
vsi = pf->vsi[vf->lan_vsi_idx];
/* Lock once, because all function inside for loop accesses VSI's
@@ -2213,6 +2460,12 @@ static int i40e_vc_add_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen)
*/
spin_lock_bh(&vsi->mac_filter_hash_lock);
+ ret = i40e_check_vf_permission(vf, al);
+ if (ret) {
+ spin_unlock_bh(&vsi->mac_filter_hash_lock);
+ goto error_param;
+ }
+
/* add new addresses to the list */
for (i = 0; i < al->num_elements; i++) {
struct i40e_mac_filter *f;
@@ -2688,6 +2941,618 @@ err:
}
/**
+ * i40e_validate_cloud_filter
+ * @mask: mask for TC filter
+ * @data: data for TC filter
+ *
+ * This function validates cloud filter programmed as TC filter for ADq
+ **/
+static int i40e_validate_cloud_filter(struct i40e_vf *vf,
+ struct virtchnl_filter *tc_filter)
+{
+ struct virtchnl_l4_spec mask = tc_filter->mask.tcp_spec;
+ struct virtchnl_l4_spec data = tc_filter->data.tcp_spec;
+ struct i40e_pf *pf = vf->pf;
+ struct i40e_vsi *vsi = NULL;
+ struct i40e_mac_filter *f;
+ struct hlist_node *h;
+ bool found = false;
+ int bkt;
+
+ if (!tc_filter->action) {
+ dev_info(&pf->pdev->dev,
+ "VF %d: Currently ADq doesn't support Drop Action\n",
+ vf->vf_id);
+ goto err;
+ }
+
+ /* action_meta is TC number here to which the filter is applied */
+ if (!tc_filter->action_meta ||
+ tc_filter->action_meta > I40E_MAX_VF_VSI) {
+ dev_info(&pf->pdev->dev, "VF %d: Invalid TC number %u\n",
+ vf->vf_id, tc_filter->action_meta);
+ goto err;
+ }
+
+ /* Check filter if it's programmed for advanced mode or basic mode.
+ * There are two ADq modes (for VF only),
+ * 1. Basic mode: intended to allow as many filter options as possible
+ * to be added to a VF in Non-trusted mode. Main goal is
+ * to add filters to its own MAC and VLAN id.
+ * 2. Advanced mode: is for allowing filters to be applied other than
+ * its own MAC or VLAN. This mode requires the VF to be
+ * Trusted.
+ */
+ if (mask.dst_mac[0] && !mask.dst_ip[0]) {
+ vsi = pf->vsi[vf->lan_vsi_idx];
+ f = i40e_find_mac(vsi, data.dst_mac);
+
+ if (!f) {
+ dev_info(&pf->pdev->dev,
+ "Destination MAC %pM doesn't belong to VF %d\n",
+ data.dst_mac, vf->vf_id);
+ goto err;
+ }
+
+ if (mask.vlan_id) {
+ hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f,
+ hlist) {
+ if (f->vlan == ntohs(data.vlan_id)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ dev_info(&pf->pdev->dev,
+ "VF %d doesn't have any VLAN id %u\n",
+ vf->vf_id, ntohs(data.vlan_id));
+ goto err;
+ }
+ }
+ } else {
+ /* Check if VF is trusted */
+ if (!test_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps)) {
+ dev_err(&pf->pdev->dev,
+ "VF %d not trusted, make VF trusted to add advanced mode ADq cloud filters\n",
+ vf->vf_id);
+ return I40E_ERR_CONFIG;
+ }
+ }
+
+ if (mask.dst_mac[0] & data.dst_mac[0]) {
+ if (is_broadcast_ether_addr(data.dst_mac) ||
+ is_zero_ether_addr(data.dst_mac)) {
+ dev_info(&pf->pdev->dev, "VF %d: Invalid Dest MAC addr %pM\n",
+ vf->vf_id, data.dst_mac);
+ goto err;
+ }
+ }
+
+ if (mask.src_mac[0] & data.src_mac[0]) {
+ if (is_broadcast_ether_addr(data.src_mac) ||
+ is_zero_ether_addr(data.src_mac)) {
+ dev_info(&pf->pdev->dev, "VF %d: Invalid Source MAC addr %pM\n",
+ vf->vf_id, data.src_mac);
+ goto err;
+ }
+ }
+
+ if (mask.dst_port & data.dst_port) {
+ if (!data.dst_port || be16_to_cpu(data.dst_port) > 0xFFFF) {
+ dev_info(&pf->pdev->dev, "VF %d: Invalid Dest port\n",
+ vf->vf_id);
+ goto err;
+ }
+ }
+
+ if (mask.src_port & data.src_port) {
+ if (!data.src_port || be16_to_cpu(data.src_port) > 0xFFFF) {
+ dev_info(&pf->pdev->dev, "VF %d: Invalid Source port\n",
+ vf->vf_id);
+ goto err;
+ }
+ }
+
+ if (tc_filter->flow_type != VIRTCHNL_TCP_V6_FLOW &&
+ tc_filter->flow_type != VIRTCHNL_TCP_V4_FLOW) {
+ dev_info(&pf->pdev->dev, "VF %d: Invalid Flow type\n",
+ vf->vf_id);
+ goto err;
+ }
+
+ if (mask.vlan_id & data.vlan_id) {
+ if (ntohs(data.vlan_id) > I40E_MAX_VLANID) {
+ dev_info(&pf->pdev->dev, "VF %d: invalid VLAN ID\n",
+ vf->vf_id);
+ goto err;
+ }
+ }
+
+ return I40E_SUCCESS;
+err:
+ return I40E_ERR_CONFIG;
+}
+
+/**
+ * i40e_find_vsi_from_seid - searches for the vsi with the given seid
+ * @vf: pointer to the VF info
+ * @seid - seid of the vsi it is searching for
+ **/
+static struct i40e_vsi *i40e_find_vsi_from_seid(struct i40e_vf *vf, u16 seid)
+{
+ struct i40e_pf *pf = vf->pf;
+ struct i40e_vsi *vsi = NULL;
+ int i;
+
+ for (i = 0; i < vf->num_tc ; i++) {
+ vsi = i40e_find_vsi_from_id(pf, vf->ch[i].vsi_id);
+ if (vsi && vsi->seid == seid)
+ return vsi;
+ }
+ return NULL;
+}
+
+/**
+ * i40e_del_all_cloud_filters
+ * @vf: pointer to the VF info
+ *
+ * This function deletes all cloud filters
+ **/
+static void i40e_del_all_cloud_filters(struct i40e_vf *vf)
+{
+ struct i40e_cloud_filter *cfilter = NULL;
+ struct i40e_pf *pf = vf->pf;
+ struct i40e_vsi *vsi = NULL;
+ struct hlist_node *node;
+ int ret;
+
+ hlist_for_each_entry_safe(cfilter, node,
+ &vf->cloud_filter_list, cloud_node) {
+ vsi = i40e_find_vsi_from_seid(vf, cfilter->seid);
+
+ if (!vsi) {
+ dev_err(&pf->pdev->dev, "VF %d: no VSI found for matching %u seid, can't delete cloud filter\n",
+ vf->vf_id, cfilter->seid);
+ continue;
+ }
+
+ if (cfilter->dst_port)
+ ret = i40e_add_del_cloud_filter_big_buf(vsi, cfilter,
+ false);
+ else
+ ret = i40e_add_del_cloud_filter(vsi, cfilter, false);
+ if (ret)
+ dev_err(&pf->pdev->dev,
+ "VF %d: Failed to delete cloud filter, err %s aq_err %s\n",
+ vf->vf_id, i40e_stat_str(&pf->hw, ret),
+ i40e_aq_str(&pf->hw,
+ pf->hw.aq.asq_last_status));
+
+ hlist_del(&cfilter->cloud_node);
+ kfree(cfilter);
+ vf->num_cloud_filters--;
+ }
+}
+
+/**
+ * i40e_vc_del_cloud_filter
+ * @vf: pointer to the VF info
+ * @msg: pointer to the msg buffer
+ *
+ * This function deletes a cloud filter programmed as TC filter for ADq
+ **/
+static int i40e_vc_del_cloud_filter(struct i40e_vf *vf, u8 *msg)
+{
+ struct virtchnl_filter *vcf = (struct virtchnl_filter *)msg;
+ struct virtchnl_l4_spec mask = vcf->mask.tcp_spec;
+ struct virtchnl_l4_spec tcf = vcf->data.tcp_spec;
+ struct i40e_cloud_filter cfilter, *cf = NULL;
+ struct i40e_pf *pf = vf->pf;
+ struct i40e_vsi *vsi = NULL;
+ struct hlist_node *node;
+ i40e_status aq_ret = 0;
+ int i, ret;
+
+ if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) {
+ aq_ret = I40E_ERR_PARAM;
+ goto err;
+ }
+
+ if (!vf->adq_enabled) {
+ dev_info(&pf->pdev->dev,
+ "VF %d: ADq not enabled, can't apply cloud filter\n",
+ vf->vf_id);
+ aq_ret = I40E_ERR_PARAM;
+ goto err;
+ }
+
+ if (i40e_validate_cloud_filter(vf, vcf)) {
+ dev_info(&pf->pdev->dev,
+ "VF %d: Invalid input, can't apply cloud filter\n",
+ vf->vf_id);
+ aq_ret = I40E_ERR_PARAM;
+ goto err;
+ }
+
+ memset(&cfilter, 0, sizeof(cfilter));
+ /* parse destination mac address */
+ for (i = 0; i < ETH_ALEN; i++)
+ cfilter.dst_mac[i] = mask.dst_mac[i] & tcf.dst_mac[i];
+
+ /* parse source mac address */
+ for (i = 0; i < ETH_ALEN; i++)
+ cfilter.src_mac[i] = mask.src_mac[i] & tcf.src_mac[i];
+
+ cfilter.vlan_id = mask.vlan_id & tcf.vlan_id;
+ cfilter.dst_port = mask.dst_port & tcf.dst_port;
+ cfilter.src_port = mask.src_port & tcf.src_port;
+
+ switch (vcf->flow_type) {
+ case VIRTCHNL_TCP_V4_FLOW:
+ cfilter.n_proto = ETH_P_IP;
+ if (mask.dst_ip[0] & tcf.dst_ip[0])
+ memcpy(&cfilter.ip.v4.dst_ip, tcf.dst_ip,
+ ARRAY_SIZE(tcf.dst_ip));
+ else if (mask.src_ip[0] & tcf.dst_ip[0])
+ memcpy(&cfilter.ip.v4.src_ip, tcf.src_ip,
+ ARRAY_SIZE(tcf.dst_ip));
+ break;
+ case VIRTCHNL_TCP_V6_FLOW:
+ cfilter.n_proto = ETH_P_IPV6;
+ if (mask.dst_ip[3] & tcf.dst_ip[3])
+ memcpy(&cfilter.ip.v6.dst_ip6, tcf.dst_ip,
+ sizeof(cfilter.ip.v6.dst_ip6));
+ if (mask.src_ip[3] & tcf.src_ip[3])
+ memcpy(&cfilter.ip.v6.src_ip6, tcf.src_ip,
+ sizeof(cfilter.ip.v6.src_ip6));
+ break;
+ default:
+ /* TC filter can be configured based on different combinations
+ * and in this case IP is not a part of filter config
+ */
+ dev_info(&pf->pdev->dev, "VF %d: Flow type not configured\n",
+ vf->vf_id);
+ }
+
+ /* get the vsi to which the tc belongs to */
+ vsi = pf->vsi[vf->ch[vcf->action_meta].vsi_idx];
+ cfilter.seid = vsi->seid;
+ cfilter.flags = vcf->field_flags;
+
+ /* Deleting TC filter */
+ if (tcf.dst_port)
+ ret = i40e_add_del_cloud_filter_big_buf(vsi, &cfilter, false);
+ else
+ ret = i40e_add_del_cloud_filter(vsi, &cfilter, false);
+ if (ret) {
+ dev_err(&pf->pdev->dev,
+ "VF %d: Failed to delete cloud filter, err %s aq_err %s\n",
+ vf->vf_id, i40e_stat_str(&pf->hw, ret),
+ i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status));
+ goto err;
+ }
+
+ hlist_for_each_entry_safe(cf, node,
+ &vf->cloud_filter_list, cloud_node) {
+ if (cf->seid != cfilter.seid)
+ continue;
+ if (mask.dst_port)
+ if (cfilter.dst_port != cf->dst_port)
+ continue;
+ if (mask.dst_mac[0])
+ if (!ether_addr_equal(cf->src_mac, cfilter.src_mac))
+ continue;
+ /* for ipv4 data to be valid, only first byte of mask is set */
+ if (cfilter.n_proto == ETH_P_IP && mask.dst_ip[0])
+ if (memcmp(&cfilter.ip.v4.dst_ip, &cf->ip.v4.dst_ip,
+ ARRAY_SIZE(tcf.dst_ip)))
+ continue;
+ /* for ipv6, mask is set for all sixteen bytes (4 words) */
+ if (cfilter.n_proto == ETH_P_IPV6 && mask.dst_ip[3])
+ if (memcmp(&cfilter.ip.v6.dst_ip6, &cf->ip.v6.dst_ip6,
+ sizeof(cfilter.ip.v6.src_ip6)))
+ continue;
+ if (mask.vlan_id)
+ if (cfilter.vlan_id != cf->vlan_id)
+ continue;
+
+ hlist_del(&cf->cloud_node);
+ kfree(cf);
+ vf->num_cloud_filters--;
+ }
+
+err:
+ return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_DEL_CLOUD_FILTER,
+ aq_ret);
+}
+
+/**
+ * i40e_vc_add_cloud_filter
+ * @vf: pointer to the VF info
+ * @msg: pointer to the msg buffer
+ *
+ * This function adds a cloud filter programmed as TC filter for ADq
+ **/
+static int i40e_vc_add_cloud_filter(struct i40e_vf *vf, u8 *msg)
+{
+ struct virtchnl_filter *vcf = (struct virtchnl_filter *)msg;
+ struct virtchnl_l4_spec mask = vcf->mask.tcp_spec;
+ struct virtchnl_l4_spec tcf = vcf->data.tcp_spec;
+ struct i40e_cloud_filter *cfilter = NULL;
+ struct i40e_pf *pf = vf->pf;
+ struct i40e_vsi *vsi = NULL;
+ i40e_status aq_ret = 0;
+ int i, ret;
+
+ if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) {
+ aq_ret = I40E_ERR_PARAM;
+ goto err;
+ }
+
+ if (!vf->adq_enabled) {
+ dev_info(&pf->pdev->dev,
+ "VF %d: ADq is not enabled, can't apply cloud filter\n",
+ vf->vf_id);
+ aq_ret = I40E_ERR_PARAM;
+ goto err;
+ }
+
+ if (i40e_validate_cloud_filter(vf, vcf)) {
+ dev_info(&pf->pdev->dev,
+ "VF %d: Invalid input/s, can't apply cloud filter\n",
+ vf->vf_id);
+ aq_ret = I40E_ERR_PARAM;
+ goto err;
+ }
+
+ cfilter = kzalloc(sizeof(*cfilter), GFP_KERNEL);
+ if (!cfilter)
+ return -ENOMEM;
+
+ /* parse destination mac address */
+ for (i = 0; i < ETH_ALEN; i++)
+ cfilter->dst_mac[i] = mask.dst_mac[i] & tcf.dst_mac[i];
+
+ /* parse source mac address */
+ for (i = 0; i < ETH_ALEN; i++)
+ cfilter->src_mac[i] = mask.src_mac[i] & tcf.src_mac[i];
+
+ cfilter->vlan_id = mask.vlan_id & tcf.vlan_id;
+ cfilter->dst_port = mask.dst_port & tcf.dst_port;
+ cfilter->src_port = mask.src_port & tcf.src_port;
+
+ switch (vcf->flow_type) {
+ case VIRTCHNL_TCP_V4_FLOW:
+ cfilter->n_proto = ETH_P_IP;
+ if (mask.dst_ip[0] & tcf.dst_ip[0])
+ memcpy(&cfilter->ip.v4.dst_ip, tcf.dst_ip,
+ ARRAY_SIZE(tcf.dst_ip));
+ else if (mask.src_ip[0] & tcf.dst_ip[0])
+ memcpy(&cfilter->ip.v4.src_ip, tcf.src_ip,
+ ARRAY_SIZE(tcf.dst_ip));
+ break;
+ case VIRTCHNL_TCP_V6_FLOW:
+ cfilter->n_proto = ETH_P_IPV6;
+ if (mask.dst_ip[3] & tcf.dst_ip[3])
+ memcpy(&cfilter->ip.v6.dst_ip6, tcf.dst_ip,
+ sizeof(cfilter->ip.v6.dst_ip6));
+ if (mask.src_ip[3] & tcf.src_ip[3])
+ memcpy(&cfilter->ip.v6.src_ip6, tcf.src_ip,
+ sizeof(cfilter->ip.v6.src_ip6));
+ break;
+ default:
+ /* TC filter can be configured based on different combinations
+ * and in this case IP is not a part of filter config
+ */
+ dev_info(&pf->pdev->dev, "VF %d: Flow type not configured\n",
+ vf->vf_id);
+ }
+
+ /* get the VSI to which the TC belongs to */
+ vsi = pf->vsi[vf->ch[vcf->action_meta].vsi_idx];
+ cfilter->seid = vsi->seid;
+ cfilter->flags = vcf->field_flags;
+
+ /* Adding cloud filter programmed as TC filter */
+ if (tcf.dst_port)
+ ret = i40e_add_del_cloud_filter_big_buf(vsi, cfilter, true);
+ else
+ ret = i40e_add_del_cloud_filter(vsi, cfilter, true);
+ if (ret) {
+ dev_err(&pf->pdev->dev,
+ "VF %d: Failed to add cloud filter, err %s aq_err %s\n",
+ vf->vf_id, i40e_stat_str(&pf->hw, ret),
+ i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status));
+ goto err;
+ }
+
+ INIT_HLIST_NODE(&cfilter->cloud_node);
+ hlist_add_head(&cfilter->cloud_node, &vf->cloud_filter_list);
+ vf->num_cloud_filters++;
+err:
+ return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_ADD_CLOUD_FILTER,
+ aq_ret);
+}
+
+/**
+ * i40e_vc_add_qch_msg: Add queue channel and enable ADq
+ * @vf: pointer to the VF info
+ * @msg: pointer to the msg buffer
+ **/
+static int i40e_vc_add_qch_msg(struct i40e_vf *vf, u8 *msg)
+{
+ struct virtchnl_tc_info *tci =
+ (struct virtchnl_tc_info *)msg;
+ struct i40e_pf *pf = vf->pf;
+ struct i40e_link_status *ls = &pf->hw.phy.link_info;
+ int i, adq_request_qps = 0, speed = 0;
+ i40e_status aq_ret = 0;
+
+ if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) {
+ aq_ret = I40E_ERR_PARAM;
+ goto err;
+ }
+
+ /* ADq cannot be applied if spoof check is ON */
+ if (vf->spoofchk) {
+ dev_err(&pf->pdev->dev,
+ "Spoof check is ON, turn it OFF to enable ADq\n");
+ aq_ret = I40E_ERR_PARAM;
+ goto err;
+ }
+
+ if (!(vf->driver_caps & VIRTCHNL_VF_OFFLOAD_ADQ)) {
+ dev_err(&pf->pdev->dev,
+ "VF %d attempting to enable ADq, but hasn't properly negotiated that capability\n",
+ vf->vf_id);
+ aq_ret = I40E_ERR_PARAM;
+ goto err;
+ }
+
+ /* max number of traffic classes for VF currently capped at 4 */
+ if (!tci->num_tc || tci->num_tc > I40E_MAX_VF_VSI) {
+ dev_err(&pf->pdev->dev,
+ "VF %d trying to set %u TCs, valid range 1-4 TCs per VF\n",
+ vf->vf_id, tci->num_tc);
+ aq_ret = I40E_ERR_PARAM;
+ goto err;
+ }
+
+ /* validate queues for each TC */
+ for (i = 0; i < tci->num_tc; i++)
+ if (!tci->list[i].count ||
+ tci->list[i].count > I40E_DEFAULT_QUEUES_PER_VF) {
+ dev_err(&pf->pdev->dev,
+ "VF %d: TC %d trying to set %u queues, valid range 1-4 queues per TC\n",
+ vf->vf_id, i, tci->list[i].count);
+ aq_ret = I40E_ERR_PARAM;
+ goto err;
+ }
+
+ /* need Max VF queues but already have default number of queues */
+ adq_request_qps = I40E_MAX_VF_QUEUES - I40E_DEFAULT_QUEUES_PER_VF;
+
+ if (pf->queues_left < adq_request_qps) {
+ dev_err(&pf->pdev->dev,
+ "No queues left to allocate to VF %d\n",
+ vf->vf_id);
+ aq_ret = I40E_ERR_PARAM;
+ goto err;
+ } else {
+ /* we need to allocate max VF queues to enable ADq so as to
+ * make sure ADq enabled VF always gets back queues when it
+ * goes through a reset.
+ */
+ vf->num_queue_pairs = I40E_MAX_VF_QUEUES;
+ }
+
+ /* get link speed in MB to validate rate limit */
+ switch (ls->link_speed) {
+ case VIRTCHNL_LINK_SPEED_100MB:
+ speed = SPEED_100;
+ break;
+ case VIRTCHNL_LINK_SPEED_1GB:
+ speed = SPEED_1000;
+ break;
+ case VIRTCHNL_LINK_SPEED_10GB:
+ speed = SPEED_10000;
+ break;
+ case VIRTCHNL_LINK_SPEED_20GB:
+ speed = SPEED_20000;
+ break;
+ case VIRTCHNL_LINK_SPEED_25GB:
+ speed = SPEED_25000;
+ break;
+ case VIRTCHNL_LINK_SPEED_40GB:
+ speed = SPEED_40000;
+ break;
+ default:
+ dev_err(&pf->pdev->dev,
+ "Cannot detect link speed\n");
+ aq_ret = I40E_ERR_PARAM;
+ goto err;
+ }
+
+ /* parse data from the queue channel info */
+ vf->num_tc = tci->num_tc;
+ for (i = 0; i < vf->num_tc; i++) {
+ if (tci->list[i].max_tx_rate) {
+ if (tci->list[i].max_tx_rate > speed) {
+ dev_err(&pf->pdev->dev,
+ "Invalid max tx rate %llu specified for VF %d.",
+ tci->list[i].max_tx_rate,
+ vf->vf_id);
+ aq_ret = I40E_ERR_PARAM;
+ goto err;
+ } else {
+ vf->ch[i].max_tx_rate =
+ tci->list[i].max_tx_rate;
+ }
+ }
+ vf->ch[i].num_qps = tci->list[i].count;
+ }
+
+ /* set this flag only after making sure all inputs are sane */
+ vf->adq_enabled = true;
+ /* num_req_queues is set when user changes number of queues via ethtool
+ * and this causes issue for default VSI(which depends on this variable)
+ * when ADq is enabled, hence reset it.
+ */
+ vf->num_req_queues = 0;
+
+ /* reset the VF in order to allocate resources */
+ i40e_vc_notify_vf_reset(vf);
+ i40e_reset_vf(vf, false);
+
+ return I40E_SUCCESS;
+
+ /* send the response to the VF */
+err:
+ return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_ENABLE_CHANNELS,
+ aq_ret);
+}
+
+/**
+ * i40e_vc_del_qch_msg
+ * @vf: pointer to the VF info
+ * @msg: pointer to the msg buffer
+ **/
+static int i40e_vc_del_qch_msg(struct i40e_vf *vf, u8 *msg)
+{
+ struct i40e_pf *pf = vf->pf;
+ i40e_status aq_ret = 0;
+
+ if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) {
+ aq_ret = I40E_ERR_PARAM;
+ goto err;
+ }
+
+ if (vf->adq_enabled) {
+ i40e_del_all_cloud_filters(vf);
+ i40e_del_qch(vf);
+ vf->adq_enabled = false;
+ vf->num_tc = 0;
+ dev_info(&pf->pdev->dev,
+ "Deleting Queue Channels and cloud filters for ADq on VF %d\n",
+ vf->vf_id);
+ } else {
+ dev_info(&pf->pdev->dev, "VF %d trying to delete queue channels but ADq isn't enabled\n",
+ vf->vf_id);
+ aq_ret = I40E_ERR_PARAM;
+ }
+
+ /* reset the VF in order to allocate resources */
+ i40e_vc_notify_vf_reset(vf);
+ i40e_reset_vf(vf, false);
+
+ return I40E_SUCCESS;
+
+err:
+ return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_DISABLE_CHANNELS,
+ aq_ret);
+}
+
+/**
* i40e_vc_process_vf_msg
* @pf: pointer to the PF structure
* @vf_id: source VF id
@@ -2816,7 +3681,18 @@ int i40e_vc_process_vf_msg(struct i40e_pf *pf, s16 vf_id, u32 v_opcode,
case VIRTCHNL_OP_REQUEST_QUEUES:
ret = i40e_vc_request_queues_msg(vf, msg, msglen);
break;
-
+ case VIRTCHNL_OP_ENABLE_CHANNELS:
+ ret = i40e_vc_add_qch_msg(vf, msg);
+ break;
+ case VIRTCHNL_OP_DISABLE_CHANNELS:
+ ret = i40e_vc_del_qch_msg(vf, msg);
+ break;
+ case VIRTCHNL_OP_ADD_CLOUD_FILTER:
+ ret = i40e_vc_add_cloud_filter(vf, msg);
+ break;
+ case VIRTCHNL_OP_DEL_CLOUD_FILTER:
+ ret = i40e_vc_del_cloud_filter(vf, msg);
+ break;
case VIRTCHNL_OP_UNKNOWN:
default:
dev_err(&pf->pdev->dev, "Unsupported opcode %d from VF %d\n",
@@ -2889,6 +3765,7 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac)
int ret = 0;
struct hlist_node *h;
int bkt;
+ u8 i;
/* validate the request */
if (vf_id >= pf->num_alloc_vfs) {
@@ -2900,6 +3777,16 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac)
vf = &(pf->vf[vf_id]);
vsi = pf->vsi[vf->lan_vsi_idx];
+
+ /* When the VF is resetting wait until it is done.
+ * It can take up to 200 milliseconds,
+ * but wait for up to 300 milliseconds to be safe.
+ */
+ for (i = 0; i < 15; i++) {
+ if (test_bit(I40E_VF_STATE_INIT, &vf->vf_states))
+ break;
+ msleep(20);
+ }
if (!test_bit(I40E_VF_STATE_INIT, &vf->vf_states)) {
dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n",
vf_id);
@@ -3382,6 +4269,16 @@ int i40e_ndo_set_vf_trust(struct net_device *netdev, int vf_id, bool setting)
i40e_vc_disable_vf(vf);
dev_info(&pf->pdev->dev, "VF %u is now %strusted\n",
vf_id, setting ? "" : "un");
+
+ if (vf->adq_enabled) {
+ if (!vf->trusted) {
+ dev_info(&pf->pdev->dev,
+ "VF %u no longer Trusted, deleting all cloud filters\n",
+ vf_id);
+ i40e_del_all_cloud_filters(vf);
+ }
+ }
+
out:
return ret;
}
diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h
index 5efc4f92bb37..57f727bb9e36 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
@@ -69,6 +70,19 @@ enum i40e_vf_capabilities {
I40E_VIRTCHNL_VF_CAP_IWARP,
};
+/* In ADq, max 4 VSI's can be allocated per VF including primary VF VSI.
+ * These variables are used to store indices, id's and number of queues
+ * for each VSI including that of primary VF VSI. Each Traffic class is
+ * termed as channel and each channel can in-turn have 4 queues which
+ * means max 16 queues overall per VF.
+ */
+struct i40evf_channel {
+ u16 vsi_idx; /* index in PF struct for all channel VSIs */
+ u16 vsi_id; /* VSI ID used by firmware */
+ u16 num_qps; /* number of queue pairs requested by user */
+ u64 max_tx_rate; /* bandwidth rate allocation for VSIs */
+};
+
/* VF information structure */
struct i40e_vf {
struct i40e_pf *pf;
@@ -111,6 +125,13 @@ struct i40e_vf {
u16 num_mac;
u16 num_vlan;
+ /* ADq related variables */
+ bool adq_enabled; /* flag to enable adq */
+ u8 num_tc;
+ struct i40evf_channel ch[I40E_MAX_VF_VSI];
+ struct hlist_head cloud_filter_list;
+ u16 num_cloud_filters;
+
/* RDMA Client */
struct virtchnl_iwarp_qvlist_info *qvlist_info;
};
diff --git a/drivers/net/ethernet/intel/i40evf/Makefile b/drivers/net/ethernet/intel/i40evf/Makefile
index a393f4a07f06..1e89c5487676 100644
--- a/drivers/net/ethernet/intel/i40evf/Makefile
+++ b/drivers/net/ethernet/intel/i40evf/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
################################################################################
#
# Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq.c b/drivers/net/ethernet/intel/i40evf/i40e_adminq.c
index d1aab6b8bfb1..6fd677efa9da 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_adminq.c
+++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq.h b/drivers/net/ethernet/intel/i40evf/i40e_adminq.h
index e0bfaa3d4a21..a7137c165256 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_adminq.h
+++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h
index 815de8d9c3fb..439e71882049 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h
+++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_alloc.h b/drivers/net/ethernet/intel/i40evf/i40e_alloc.h
index 8e6a6dd9212b..7e0fddd8af36 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_alloc.h
+++ b/drivers/net/ethernet/intel/i40evf/i40e_alloc.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_common.c b/drivers/net/ethernet/intel/i40evf/i40e_common.c
index 67bf5cebb76f..67140cdbcd7a 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_common.c
+++ b/drivers/net/ethernet/intel/i40evf/i40e_common.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_devids.h b/drivers/net/ethernet/intel/i40evf/i40e_devids.h
index 0469e4bfd3ec..352dd3f3eb6a 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_devids.h
+++ b/drivers/net/ethernet/intel/i40evf/i40e_devids.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_hmc.h b/drivers/net/ethernet/intel/i40evf/i40e_hmc.h
index 00ed24bfce13..7432596164f4 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_hmc.h
+++ b/drivers/net/ethernet/intel/i40evf/i40e_hmc.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_lan_hmc.h b/drivers/net/ethernet/intel/i40evf/i40e_lan_hmc.h
index a5d79877354c..ddac0e4908d3 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_lan_hmc.h
+++ b/drivers/net/ethernet/intel/i40evf/i40e_lan_hmc.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_osdep.h b/drivers/net/ethernet/intel/i40evf/i40e_osdep.h
index a90737786c34..8668ad6c1a65 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_osdep.h
+++ b/drivers/net/ethernet/intel/i40evf/i40e_osdep.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_prototype.h b/drivers/net/ethernet/intel/i40evf/i40e_prototype.h
index 47c429931a57..72501bd0f1a9 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_prototype.h
+++ b/drivers/net/ethernet/intel/i40evf/i40e_prototype.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_register.h b/drivers/net/ethernet/intel/i40evf/i40e_register.h
index 10febcfd7cd8..c9c935659758 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_register.h
+++ b/drivers/net/ethernet/intel/i40evf/i40e_register.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_status.h b/drivers/net/ethernet/intel/i40evf/i40e_status.h
index 5b222246e08b..0d7993ecb99a 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_status.h
+++ b/drivers/net/ethernet/intel/i40evf/i40e_status.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_trace.h b/drivers/net/ethernet/intel/i40evf/i40e_trace.h
index 9a5100b2b7c7..ece01dd12a3c 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_trace.h
+++ b/drivers/net/ethernet/intel/i40evf/i40e_trace.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel(R) 40-10 Gigabit Ethernet Virtual Function Driver
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c
index 357d6051281f..12bd937861e7 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c
+++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
@@ -196,7 +197,7 @@ void i40evf_detect_recover_hung(struct i40e_vsi *vsi)
*/
smp_rmb();
tx_ring->tx_stats.prev_pkt_ctr =
- i40evf_get_tx_pending(tx_ring, false) ? packets : -1;
+ i40evf_get_tx_pending(tx_ring, true) ? packets : -1;
}
}
}
@@ -392,99 +393,241 @@ void i40evf_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector)
val);
}
+static inline bool i40e_container_is_rx(struct i40e_q_vector *q_vector,
+ struct i40e_ring_container *rc)
+{
+ return &q_vector->rx == rc;
+}
+
+static inline unsigned int i40e_itr_divisor(struct i40e_q_vector *q_vector)
+{
+ unsigned int divisor;
+
+ switch (q_vector->adapter->link_speed) {
+ case I40E_LINK_SPEED_40GB:
+ divisor = I40E_ITR_ADAPTIVE_MIN_INC * 1024;
+ break;
+ case I40E_LINK_SPEED_25GB:
+ case I40E_LINK_SPEED_20GB:
+ divisor = I40E_ITR_ADAPTIVE_MIN_INC * 512;
+ break;
+ default:
+ case I40E_LINK_SPEED_10GB:
+ divisor = I40E_ITR_ADAPTIVE_MIN_INC * 256;
+ break;
+ case I40E_LINK_SPEED_1GB:
+ case I40E_LINK_SPEED_100MB:
+ divisor = I40E_ITR_ADAPTIVE_MIN_INC * 32;
+ break;
+ }
+
+ return divisor;
+}
+
/**
- * i40e_set_new_dynamic_itr - Find new ITR level
+ * i40e_update_itr - update the dynamic ITR value based on statistics
+ * @q_vector: structure containing interrupt and ring information
* @rc: structure containing ring performance data
*
- * Returns true if ITR changed, false if not
- *
- * Stores a new ITR value based on packets and byte counts during
- * the last interrupt. The advantage of per interrupt computation
- * is faster updates and more accurate ITR for the current traffic
- * pattern. Constants in this function were computed based on
- * theoretical maximum wire speed and thresholds were set based on
- * testing data as well as attempting to minimize response time
+ * Stores a new ITR value based on packets and byte
+ * counts during the last interrupt. The advantage of per interrupt
+ * computation is faster updates and more accurate ITR for the current
+ * traffic pattern. Constants in this function were computed
+ * based on theoretical maximum wire speed and thresholds were set based
+ * on testing data as well as attempting to minimize response time
* while increasing bulk throughput.
**/
-static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc)
+static void i40e_update_itr(struct i40e_q_vector *q_vector,
+ struct i40e_ring_container *rc)
{
- enum i40e_latency_range new_latency_range = rc->latency_range;
- u32 new_itr = rc->itr;
- int bytes_per_usec;
- unsigned int usecs, estimated_usecs;
+ unsigned int avg_wire_size, packets, bytes, itr;
+ unsigned long next_update = jiffies;
- if (rc->total_packets == 0 || !rc->itr)
- return false;
+ /* If we don't have any rings just leave ourselves set for maximum
+ * possible latency so we take ourselves out of the equation.
+ */
+ if (!rc->ring || !ITR_IS_DYNAMIC(rc->ring->itr_setting))
+ return;
+
+ /* For Rx we want to push the delay up and default to low latency.
+ * for Tx we want to pull the delay down and default to high latency.
+ */
+ itr = i40e_container_is_rx(q_vector, rc) ?
+ I40E_ITR_ADAPTIVE_MIN_USECS | I40E_ITR_ADAPTIVE_LATENCY :
+ I40E_ITR_ADAPTIVE_MAX_USECS | I40E_ITR_ADAPTIVE_LATENCY;
+
+ /* If we didn't update within up to 1 - 2 jiffies we can assume
+ * that either packets are coming in so slow there hasn't been
+ * any work, or that there is so much work that NAPI is dealing
+ * with interrupt moderation and we don't need to do anything.
+ */
+ if (time_after(next_update, rc->next_update))
+ goto clear_counts;
+
+ /* If itr_countdown is set it means we programmed an ITR within
+ * the last 4 interrupt cycles. This has a side effect of us
+ * potentially firing an early interrupt. In order to work around
+ * this we need to throw out any data received for a few
+ * interrupts following the update.
+ */
+ if (q_vector->itr_countdown) {
+ itr = rc->target_itr;
+ goto clear_counts;
+ }
+
+ packets = rc->total_packets;
+ bytes = rc->total_bytes;
- usecs = (rc->itr << 1) * ITR_COUNTDOWN_START;
- bytes_per_usec = rc->total_bytes / usecs;
+ if (i40e_container_is_rx(q_vector, rc)) {
+ /* If Rx there are 1 to 4 packets and bytes are less than
+ * 9000 assume insufficient data to use bulk rate limiting
+ * approach unless Tx is already in bulk rate limiting. We
+ * are likely latency driven.
+ */
+ if (packets && packets < 4 && bytes < 9000 &&
+ (q_vector->tx.target_itr & I40E_ITR_ADAPTIVE_LATENCY)) {
+ itr = I40E_ITR_ADAPTIVE_LATENCY;
+ goto adjust_by_size;
+ }
+ } else if (packets < 4) {
+ /* If we have Tx and Rx ITR maxed and Tx ITR is running in
+ * bulk mode and we are receiving 4 or fewer packets just
+ * reset the ITR_ADAPTIVE_LATENCY bit for latency mode so
+ * that the Rx can relax.
+ */
+ if (rc->target_itr == I40E_ITR_ADAPTIVE_MAX_USECS &&
+ (q_vector->rx.target_itr & I40E_ITR_MASK) ==
+ I40E_ITR_ADAPTIVE_MAX_USECS)
+ goto clear_counts;
+ } else if (packets > 32) {
+ /* If we have processed over 32 packets in a single interrupt
+ * for Tx assume we need to switch over to "bulk" mode.
+ */
+ rc->target_itr &= ~I40E_ITR_ADAPTIVE_LATENCY;
+ }
- /* The calculations in this algorithm depend on interrupts actually
- * firing at the ITR rate. This may not happen if the packet rate is
- * really low, or if we've been napi polling. Check to make sure
- * that's not the case before we continue.
+ /* We have no packets to actually measure against. This means
+ * either one of the other queues on this vector is active or
+ * we are a Tx queue doing TSO with too high of an interrupt rate.
+ *
+ * Between 4 and 56 we can assume that our current interrupt delay
+ * is only slightly too low. As such we should increase it by a small
+ * fixed amount.
*/
- estimated_usecs = jiffies_to_usecs(jiffies - rc->last_itr_update);
- if (estimated_usecs > usecs) {
- new_latency_range = I40E_LOW_LATENCY;
- goto reset_latency;
+ if (packets < 56) {
+ itr = rc->target_itr + I40E_ITR_ADAPTIVE_MIN_INC;
+ if ((itr & I40E_ITR_MASK) > I40E_ITR_ADAPTIVE_MAX_USECS) {
+ itr &= I40E_ITR_ADAPTIVE_LATENCY;
+ itr += I40E_ITR_ADAPTIVE_MAX_USECS;
+ }
+ goto clear_counts;
+ }
+
+ if (packets <= 256) {
+ itr = min(q_vector->tx.current_itr, q_vector->rx.current_itr);
+ itr &= I40E_ITR_MASK;
+
+ /* Between 56 and 112 is our "goldilocks" zone where we are
+ * working out "just right". Just report that our current
+ * ITR is good for us.
+ */
+ if (packets <= 112)
+ goto clear_counts;
+
+ /* If packet count is 128 or greater we are likely looking
+ * at a slight overrun of the delay we want. Try halving
+ * our delay to see if that will cut the number of packets
+ * in half per interrupt.
+ */
+ itr /= 2;
+ itr &= I40E_ITR_MASK;
+ if (itr < I40E_ITR_ADAPTIVE_MIN_USECS)
+ itr = I40E_ITR_ADAPTIVE_MIN_USECS;
+
+ goto clear_counts;
}
- /* simple throttlerate management
- * 0-10MB/s lowest (50000 ints/s)
- * 10-20MB/s low (20000 ints/s)
- * 20-1249MB/s bulk (18000 ints/s)
+ /* The paths below assume we are dealing with a bulk ITR since
+ * number of packets is greater than 256. We are just going to have
+ * to compute a value and try to bring the count under control,
+ * though for smaller packet sizes there isn't much we can do as
+ * NAPI polling will likely be kicking in sooner rather than later.
+ */
+ itr = I40E_ITR_ADAPTIVE_BULK;
+
+adjust_by_size:
+ /* If packet counts are 256 or greater we can assume we have a gross
+ * overestimation of what the rate should be. Instead of trying to fine
+ * tune it just use the formula below to try and dial in an exact value
+ * give the current packet size of the frame.
+ */
+ avg_wire_size = bytes / packets;
+
+ /* The following is a crude approximation of:
+ * wmem_default / (size + overhead) = desired_pkts_per_int
+ * rate / bits_per_byte / (size + ethernet overhead) = pkt_rate
+ * (desired_pkt_rate / pkt_rate) * usecs_per_sec = ITR value
*
- * The math works out because the divisor is in 10^(-6) which
- * turns the bytes/us input value into MB/s values, but
- * make sure to use usecs, as the register values written
- * are in 2 usec increments in the ITR registers, and make sure
- * to use the smoothed values that the countdown timer gives us.
+ * Assuming wmem_default is 212992 and overhead is 640 bytes per
+ * packet, (256 skb, 64 headroom, 320 shared info), we can reduce the
+ * formula down to
+ *
+ * (170 * (size + 24)) / (size + 640) = ITR
+ *
+ * We first do some math on the packet size and then finally bitshift
+ * by 8 after rounding up. We also have to account for PCIe link speed
+ * difference as ITR scales based on this.
*/
- switch (new_latency_range) {
- case I40E_LOWEST_LATENCY:
- if (bytes_per_usec > 10)
- new_latency_range = I40E_LOW_LATENCY;
- break;
- case I40E_LOW_LATENCY:
- if (bytes_per_usec > 20)
- new_latency_range = I40E_BULK_LATENCY;
- else if (bytes_per_usec <= 10)
- new_latency_range = I40E_LOWEST_LATENCY;
- break;
- case I40E_BULK_LATENCY:
- default:
- if (bytes_per_usec <= 20)
- new_latency_range = I40E_LOW_LATENCY;
- break;
+ if (avg_wire_size <= 60) {
+ /* Start at 250k ints/sec */
+ avg_wire_size = 4096;
+ } else if (avg_wire_size <= 380) {
+ /* 250K ints/sec to 60K ints/sec */
+ avg_wire_size *= 40;
+ avg_wire_size += 1696;
+ } else if (avg_wire_size <= 1084) {
+ /* 60K ints/sec to 36K ints/sec */
+ avg_wire_size *= 15;
+ avg_wire_size += 11452;
+ } else if (avg_wire_size <= 1980) {
+ /* 36K ints/sec to 30K ints/sec */
+ avg_wire_size *= 5;
+ avg_wire_size += 22420;
+ } else {
+ /* plateau at a limit of 30K ints/sec */
+ avg_wire_size = 32256;
}
-reset_latency:
- rc->latency_range = new_latency_range;
+ /* If we are in low latency mode halve our delay which doubles the
+ * rate to somewhere between 100K to 16K ints/sec
+ */
+ if (itr & I40E_ITR_ADAPTIVE_LATENCY)
+ avg_wire_size /= 2;
- switch (new_latency_range) {
- case I40E_LOWEST_LATENCY:
- new_itr = I40E_ITR_50K;
- break;
- case I40E_LOW_LATENCY:
- new_itr = I40E_ITR_20K;
- break;
- case I40E_BULK_LATENCY:
- new_itr = I40E_ITR_18K;
- break;
- default:
- break;
+ /* Resultant value is 256 times larger than it needs to be. This
+ * gives us room to adjust the value as needed to either increase
+ * or decrease the value based on link speeds of 10G, 2.5G, 1G, etc.
+ *
+ * Use addition as we have already recorded the new latency flag
+ * for the ITR value.
+ */
+ itr += DIV_ROUND_UP(avg_wire_size, i40e_itr_divisor(q_vector)) *
+ I40E_ITR_ADAPTIVE_MIN_INC;
+
+ if ((itr & I40E_ITR_MASK) > I40E_ITR_ADAPTIVE_MAX_USECS) {
+ itr &= I40E_ITR_ADAPTIVE_LATENCY;
+ itr += I40E_ITR_ADAPTIVE_MAX_USECS;
}
+clear_counts:
+ /* write back value */
+ rc->target_itr = itr;
+
+ /* next update should occur within next jiffy */
+ rc->next_update = next_update + 1;
+
rc->total_bytes = 0;
rc->total_packets = 0;
- rc->last_itr_update = jiffies;
-
- if (new_itr != rc->itr) {
- rc->itr = new_itr;
- return true;
- }
- return false;
}
/**
@@ -1273,7 +1416,7 @@ static struct sk_buff *i40e_build_skb(struct i40e_ring *rx_ring,
* @rx_buffer: rx buffer to pull data from
*
* This function will clean up the contents of the rx_buffer. It will
- * either recycle the bufer or unmap it and free the associated resources.
+ * either recycle the buffer or unmap it and free the associated resources.
*/
static void i40e_put_rx_buffer(struct i40e_ring *rx_ring,
struct i40e_rx_buffer *rx_buffer)
@@ -1457,33 +1600,45 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget)
return failure ? budget : (int)total_rx_packets;
}
-static u32 i40e_buildreg_itr(const int type, const u16 itr)
+static inline u32 i40e_buildreg_itr(const int type, u16 itr)
{
u32 val;
+ /* We don't bother with setting the CLEARPBA bit as the data sheet
+ * points out doing so is "meaningless since it was already
+ * auto-cleared". The auto-clearing happens when the interrupt is
+ * asserted.
+ *
+ * Hardware errata 28 for also indicates that writing to a
+ * xxINT_DYN_CTLx CSR with INTENA_MSK (bit 31) set to 0 will clear
+ * an event in the PBA anyway so we need to rely on the automask
+ * to hold pending events for us until the interrupt is re-enabled
+ *
+ * The itr value is reported in microseconds, and the register
+ * value is recorded in 2 microsecond units. For this reason we
+ * only need to shift by the interval shift - 1 instead of the
+ * full value.
+ */
+ itr &= I40E_ITR_MASK;
+
val = I40E_VFINT_DYN_CTLN1_INTENA_MASK |
- I40E_VFINT_DYN_CTLN1_CLEARPBA_MASK |
(type << I40E_VFINT_DYN_CTLN1_ITR_INDX_SHIFT) |
- (itr << I40E_VFINT_DYN_CTLN1_INTERVAL_SHIFT);
+ (itr << (I40E_VFINT_DYN_CTLN1_INTERVAL_SHIFT - 1));
return val;
}
/* a small macro to shorten up some long lines */
#define INTREG I40E_VFINT_DYN_CTLN1
-static inline int get_rx_itr(struct i40e_vsi *vsi, int idx)
-{
- struct i40evf_adapter *adapter = vsi->back;
- return adapter->rx_rings[idx].rx_itr_setting;
-}
-
-static inline int get_tx_itr(struct i40e_vsi *vsi, int idx)
-{
- struct i40evf_adapter *adapter = vsi->back;
-
- return adapter->tx_rings[idx].tx_itr_setting;
-}
+/* The act of updating the ITR will cause it to immediately trigger. In order
+ * to prevent this from throwing off adaptive update statistics we defer the
+ * update so that it can only happen so often. So after either Tx or Rx are
+ * updated we make the adaptive scheme wait until either the ITR completely
+ * expires via the next_update expiration or we have been through at least
+ * 3 interrupts.
+ */
+#define ITR_COUNTDOWN_START 3
/**
* i40e_update_enable_itr - Update itr and re-enable MSIX interrupt
@@ -1495,70 +1650,51 @@ static inline void i40e_update_enable_itr(struct i40e_vsi *vsi,
struct i40e_q_vector *q_vector)
{
struct i40e_hw *hw = &vsi->back->hw;
- bool rx = false, tx = false;
- u32 rxval, txval;
- int idx = q_vector->v_idx;
- int rx_itr_setting, tx_itr_setting;
-
- /* avoid dynamic calculation if in countdown mode OR if
- * all dynamic is disabled
- */
- rxval = txval = i40e_buildreg_itr(I40E_ITR_NONE, 0);
-
- rx_itr_setting = get_rx_itr(vsi, idx);
- tx_itr_setting = get_tx_itr(vsi, idx);
+ u32 intval;
- if (q_vector->itr_countdown > 0 ||
- (!ITR_IS_DYNAMIC(rx_itr_setting) &&
- !ITR_IS_DYNAMIC(tx_itr_setting))) {
- goto enable_int;
- }
-
- if (ITR_IS_DYNAMIC(rx_itr_setting)) {
- rx = i40e_set_new_dynamic_itr(&q_vector->rx);
- rxval = i40e_buildreg_itr(I40E_RX_ITR, q_vector->rx.itr);
- }
+ /* These will do nothing if dynamic updates are not enabled */
+ i40e_update_itr(q_vector, &q_vector->tx);
+ i40e_update_itr(q_vector, &q_vector->rx);
- if (ITR_IS_DYNAMIC(tx_itr_setting)) {
- tx = i40e_set_new_dynamic_itr(&q_vector->tx);
- txval = i40e_buildreg_itr(I40E_TX_ITR, q_vector->tx.itr);
- }
-
- if (rx || tx) {
- /* get the higher of the two ITR adjustments and
- * use the same value for both ITR registers
- * when in adaptive mode (Rx and/or Tx)
- */
- u16 itr = max(q_vector->tx.itr, q_vector->rx.itr);
-
- q_vector->tx.itr = q_vector->rx.itr = itr;
- txval = i40e_buildreg_itr(I40E_TX_ITR, itr);
- tx = true;
- rxval = i40e_buildreg_itr(I40E_RX_ITR, itr);
- rx = true;
- }
-
- /* only need to enable the interrupt once, but need
- * to possibly update both ITR values
+ /* This block of logic allows us to get away with only updating
+ * one ITR value with each interrupt. The idea is to perform a
+ * pseudo-lazy update with the following criteria.
+ *
+ * 1. Rx is given higher priority than Tx if both are in same state
+ * 2. If we must reduce an ITR that is given highest priority.
+ * 3. We then give priority to increasing ITR based on amount.
*/
- if (rx) {
- /* set the INTENA_MSK_MASK so that this first write
- * won't actually enable the interrupt, instead just
- * updating the ITR (it's bit 31 PF and VF)
+ if (q_vector->rx.target_itr < q_vector->rx.current_itr) {
+ /* Rx ITR needs to be reduced, this is highest priority */
+ intval = i40e_buildreg_itr(I40E_RX_ITR,
+ q_vector->rx.target_itr);
+ q_vector->rx.current_itr = q_vector->rx.target_itr;
+ q_vector->itr_countdown = ITR_COUNTDOWN_START;
+ } else if ((q_vector->tx.target_itr < q_vector->tx.current_itr) ||
+ ((q_vector->rx.target_itr - q_vector->rx.current_itr) <
+ (q_vector->tx.target_itr - q_vector->tx.current_itr))) {
+ /* Tx ITR needs to be reduced, this is second priority
+ * Tx ITR needs to be increased more than Rx, fourth priority
*/
- rxval |= BIT(31);
- /* don't check _DOWN because interrupt isn't being enabled */
- wr32(hw, INTREG(q_vector->reg_idx), rxval);
+ intval = i40e_buildreg_itr(I40E_TX_ITR,
+ q_vector->tx.target_itr);
+ q_vector->tx.current_itr = q_vector->tx.target_itr;
+ q_vector->itr_countdown = ITR_COUNTDOWN_START;
+ } else if (q_vector->rx.current_itr != q_vector->rx.target_itr) {
+ /* Rx ITR needs to be increased, third priority */
+ intval = i40e_buildreg_itr(I40E_RX_ITR,
+ q_vector->rx.target_itr);
+ q_vector->rx.current_itr = q_vector->rx.target_itr;
+ q_vector->itr_countdown = ITR_COUNTDOWN_START;
+ } else {
+ /* No ITR update, lowest priority */
+ intval = i40e_buildreg_itr(I40E_ITR_NONE, 0);
+ if (q_vector->itr_countdown)
+ q_vector->itr_countdown--;
}
-enable_int:
if (!test_bit(__I40E_VSI_DOWN, vsi->state))
- wr32(hw, INTREG(q_vector->reg_idx), txval);
-
- if (q_vector->itr_countdown)
- q_vector->itr_countdown--;
- else
- q_vector->itr_countdown = ITR_COUNTDOWN_START;
+ wr32(hw, INTREG(q_vector->reg_idx), intval);
}
/**
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h
index 7798a6645c3f..5790897eae2e 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h
+++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
@@ -28,31 +29,35 @@
#define _I40E_TXRX_H_
/* Interrupt Throttling and Rate Limiting Goodies */
-
-#define I40E_MAX_ITR 0x0FF0 /* reg uses 2 usec resolution */
-#define I40E_MIN_ITR 0x0001 /* reg uses 2 usec resolution */
-#define I40E_ITR_100K 0x0005
-#define I40E_ITR_50K 0x000A
-#define I40E_ITR_20K 0x0019
-#define I40E_ITR_18K 0x001B
-#define I40E_ITR_8K 0x003E
-#define I40E_ITR_4K 0x007A
-#define I40E_MAX_INTRL 0x3B /* reg uses 4 usec resolution */
-#define I40E_ITR_RX_DEF (ITR_REG_TO_USEC(I40E_ITR_20K) | \
- I40E_ITR_DYNAMIC)
-#define I40E_ITR_TX_DEF (ITR_REG_TO_USEC(I40E_ITR_20K) | \
- I40E_ITR_DYNAMIC)
-#define I40E_ITR_DYNAMIC 0x8000 /* use top bit as a flag */
-#define I40E_MIN_INT_RATE 250 /* ~= 1000000 / (I40E_MAX_ITR * 2) */
-#define I40E_MAX_INT_RATE 500000 /* == 1000000 / (I40E_MIN_ITR * 2) */
#define I40E_DEFAULT_IRQ_WORK 256
-#define ITR_TO_REG(setting) ((setting & ~I40E_ITR_DYNAMIC) >> 1)
-#define ITR_IS_DYNAMIC(setting) (!!(setting & I40E_ITR_DYNAMIC))
-#define ITR_REG_TO_USEC(itr_reg) (itr_reg << 1)
+
+/* The datasheet for the X710 and XL710 indicate that the maximum value for
+ * the ITR is 8160usec which is then called out as 0xFF0 with a 2usec
+ * resolution. 8160 is 0x1FE0 when written out in hex. So instead of storing
+ * the register value which is divided by 2 lets use the actual values and
+ * avoid an excessive amount of translation.
+ */
+#define I40E_ITR_DYNAMIC 0x8000 /* use top bit as a flag */
+#define I40E_ITR_MASK 0x1FFE /* mask for ITR register value */
+#define I40E_MIN_ITR 2 /* reg uses 2 usec resolution */
+#define I40E_ITR_100K 10 /* all values below must be even */
+#define I40E_ITR_50K 20
+#define I40E_ITR_20K 50
+#define I40E_ITR_18K 60
+#define I40E_ITR_8K 122
+#define I40E_MAX_ITR 8160 /* maximum value as per datasheet */
+#define ITR_TO_REG(setting) ((setting) & ~I40E_ITR_DYNAMIC)
+#define ITR_REG_ALIGN(setting) __ALIGN_MASK(setting, ~I40E_ITR_MASK)
+#define ITR_IS_DYNAMIC(setting) (!!((setting) & I40E_ITR_DYNAMIC))
+
+#define I40E_ITR_RX_DEF (I40E_ITR_20K | I40E_ITR_DYNAMIC)
+#define I40E_ITR_TX_DEF (I40E_ITR_20K | I40E_ITR_DYNAMIC)
+
/* 0x40 is the enable bit for interrupt rate limiting, and must be set if
* the value of the rate limit is non-zero
*/
#define INTRL_ENA BIT(6)
+#define I40E_MAX_INTRL 0x3B /* reg uses 4 usec resolution */
#define INTRL_REG_TO_USEC(intrl) ((intrl & ~INTRL_ENA) << 2)
#define INTRL_USEC_TO_REG(set) ((set) ? ((set) >> 2) | INTRL_ENA : 0)
#define I40E_INTRL_8K 125 /* 8000 ints/sec */
@@ -362,8 +367,7 @@ struct i40e_ring {
* these values always store the USER setting, and must be converted
* before programming to a register.
*/
- u16 rx_itr_setting;
- u16 tx_itr_setting;
+ u16 itr_setting;
u16 count; /* Number of descriptors */
u16 reg_idx; /* HW register index of the ring */
@@ -425,21 +429,21 @@ static inline void clear_ring_build_skb_enabled(struct i40e_ring *ring)
ring->flags &= ~I40E_RXR_FLAGS_BUILD_SKB_ENABLED;
}
-enum i40e_latency_range {
- I40E_LOWEST_LATENCY = 0,
- I40E_LOW_LATENCY = 1,
- I40E_BULK_LATENCY = 2,
-};
+#define I40E_ITR_ADAPTIVE_MIN_INC 0x0002
+#define I40E_ITR_ADAPTIVE_MIN_USECS 0x0002
+#define I40E_ITR_ADAPTIVE_MAX_USECS 0x007e
+#define I40E_ITR_ADAPTIVE_LATENCY 0x8000
+#define I40E_ITR_ADAPTIVE_BULK 0x0000
+#define ITR_IS_BULK(x) (!((x) & I40E_ITR_ADAPTIVE_LATENCY))
struct i40e_ring_container {
- /* array of pointers to rings */
- struct i40e_ring *ring;
+ struct i40e_ring *ring; /* pointer to linked list of ring(s) */
+ unsigned long next_update; /* jiffies value of next update */
unsigned int total_bytes; /* total bytes processed this int */
unsigned int total_packets; /* total packets processed this int */
- unsigned long last_itr_update; /* jiffies of last ITR update */
u16 count;
- enum i40e_latency_range latency_range;
- u16 itr;
+ u16 target_itr; /* target ITR setting for ring(s) */
+ u16 current_itr; /* current ITR setting for ring(s) */
};
/* iterator for handling rings in ring container */
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_type.h b/drivers/net/ethernet/intel/i40evf/i40e_type.h
index 54951c84a481..449de4b0058e 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_type.h
+++ b/drivers/net/ethernet/intel/i40evf/i40e_type.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h
index 9690c1ea019e..3a7a1e77bf39 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf.h
+++ b/drivers/net/ethernet/intel/i40evf/i40evf.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
@@ -52,7 +53,10 @@
#include <linux/socket.h>
#include <linux/jiffies.h>
#include <net/ip6_checksum.h>
+#include <net/pkt_cls.h>
#include <net/udp.h>
+#include <net/tc_act/tc_gact.h>
+#include <net/tc_act/tc_mirred.h>
#include "i40e_type.h"
#include <linux/avf/virtchnl.h>
@@ -106,6 +110,7 @@ struct i40e_vsi {
#define I40EVF_HKEY_ARRAY_SIZE ((I40E_VFQF_HKEY_MAX_INDEX + 1) * 4)
#define I40EVF_HLUT_ARRAY_SIZE ((I40E_VFQF_HLUT_MAX_INDEX + 1) * 4)
+#define I40EVF_MBPS_DIVISOR 125000 /* divisor to convert to Mbps */
/* MAX_MSIX_Q_VECTORS of these are allocated,
* but we only use one per queue-specific vector.
@@ -117,9 +122,8 @@ struct i40e_q_vector {
struct i40e_ring_container rx;
struct i40e_ring_container tx;
u32 ring_mask;
+ u8 itr_countdown; /* when 0 should adjust adaptive ITR */
u8 num_ringpairs; /* total number of ring pairs in vector */
-#define ITR_COUNTDOWN_START 100
- u8 itr_countdown; /* when 0 or 1 update ITR */
u16 v_idx; /* index in the vsi->q_vector array. */
u16 reg_idx; /* register index of the interrupt */
char name[IFNAMSIZ + 15];
@@ -169,6 +173,28 @@ struct i40evf_vlan_filter {
bool add; /* filter needs to be added */
};
+#define I40EVF_MAX_TRAFFIC_CLASS 4
+/* State of traffic class creation */
+enum i40evf_tc_state_t {
+ __I40EVF_TC_INVALID, /* no traffic class, default state */
+ __I40EVF_TC_RUNNING, /* traffic classes have been created */
+};
+
+/* channel info */
+struct i40evf_channel_config {
+ struct virtchnl_channel_info ch_info[I40EVF_MAX_TRAFFIC_CLASS];
+ enum i40evf_tc_state_t state;
+ u8 total_qps;
+};
+
+/* State of cloud filter */
+enum i40evf_cloud_filter_state_t {
+ __I40EVF_CF_INVALID, /* cloud filter not added */
+ __I40EVF_CF_ADD_PENDING, /* cloud filter pending add by the PF */
+ __I40EVF_CF_DEL_PENDING, /* cloud filter pending del by the PF */
+ __I40EVF_CF_ACTIVE, /* cloud filter is active */
+};
+
/* Driver state. The order of these is important! */
enum i40evf_state_t {
__I40EVF_STARTUP, /* driver loaded, probe complete */
@@ -190,6 +216,36 @@ enum i40evf_critical_section_t {
__I40EVF_IN_REMOVE_TASK, /* device being removed */
};
+#define I40EVF_CLOUD_FIELD_OMAC 0x01
+#define I40EVF_CLOUD_FIELD_IMAC 0x02
+#define I40EVF_CLOUD_FIELD_IVLAN 0x04
+#define I40EVF_CLOUD_FIELD_TEN_ID 0x08
+#define I40EVF_CLOUD_FIELD_IIP 0x10
+
+#define I40EVF_CF_FLAGS_OMAC I40EVF_CLOUD_FIELD_OMAC
+#define I40EVF_CF_FLAGS_IMAC I40EVF_CLOUD_FIELD_IMAC
+#define I40EVF_CF_FLAGS_IMAC_IVLAN (I40EVF_CLOUD_FIELD_IMAC |\
+ I40EVF_CLOUD_FIELD_IVLAN)
+#define I40EVF_CF_FLAGS_IMAC_TEN_ID (I40EVF_CLOUD_FIELD_IMAC |\
+ I40EVF_CLOUD_FIELD_TEN_ID)
+#define I40EVF_CF_FLAGS_OMAC_TEN_ID_IMAC (I40EVF_CLOUD_FIELD_OMAC |\
+ I40EVF_CLOUD_FIELD_IMAC |\
+ I40EVF_CLOUD_FIELD_TEN_ID)
+#define I40EVF_CF_FLAGS_IMAC_IVLAN_TEN_ID (I40EVF_CLOUD_FIELD_IMAC |\
+ I40EVF_CLOUD_FIELD_IVLAN |\
+ I40EVF_CLOUD_FIELD_TEN_ID)
+#define I40EVF_CF_FLAGS_IIP I40E_CLOUD_FIELD_IIP
+
+/* bookkeeping of cloud filters */
+struct i40evf_cloud_filter {
+ enum i40evf_cloud_filter_state_t state;
+ struct list_head list;
+ struct virtchnl_filter f;
+ unsigned long cookie;
+ bool del; /* filter needs to be deleted */
+ bool add; /* filter needs to be added */
+};
+
/* board specific private data structure */
struct i40evf_adapter {
struct timer_list watchdog_timer;
@@ -225,13 +281,10 @@ struct i40evf_adapter {
u32 flags;
#define I40EVF_FLAG_RX_CSUM_ENABLED BIT(0)
-#define I40EVF_FLAG_IMIR_ENABLED BIT(1)
-#define I40EVF_FLAG_MQ_CAPABLE BIT(2)
#define I40EVF_FLAG_PF_COMMS_FAILED BIT(3)
#define I40EVF_FLAG_RESET_PENDING BIT(4)
#define I40EVF_FLAG_RESET_NEEDED BIT(5)
#define I40EVF_FLAG_WB_ON_ITR_CAPABLE BIT(6)
-#define I40EVF_FLAG_OUTER_UDP_CSUM_CAPABLE BIT(7)
#define I40EVF_FLAG_ADDR_SET_BY_PF BIT(8)
#define I40EVF_FLAG_SERVICE_CLIENT_REQUESTED BIT(9)
#define I40EVF_FLAG_CLIENT_NEEDS_OPEN BIT(10)
@@ -241,6 +294,7 @@ struct i40evf_adapter {
#define I40EVF_FLAG_ALLMULTI_ON BIT(14)
#define I40EVF_FLAG_LEGACY_RX BIT(15)
#define I40EVF_FLAG_REINIT_ITR_NEEDED BIT(16)
+#define I40EVF_FLAG_QUEUES_DISABLED BIT(17)
/* duplicates for common code */
#define I40E_FLAG_DCB_ENABLED 0
#define I40E_FLAG_RX_CSUM_ENABLED I40EVF_FLAG_RX_CSUM_ENABLED
@@ -269,6 +323,10 @@ struct i40evf_adapter {
#define I40EVF_FLAG_AQ_RELEASE_ALLMULTI BIT(18)
#define I40EVF_FLAG_AQ_ENABLE_VLAN_STRIPPING BIT(19)
#define I40EVF_FLAG_AQ_DISABLE_VLAN_STRIPPING BIT(20)
+#define I40EVF_FLAG_AQ_ENABLE_CHANNELS BIT(21)
+#define I40EVF_FLAG_AQ_DISABLE_CHANNELS BIT(22)
+#define I40EVF_FLAG_AQ_ADD_CLOUD_FILTER BIT(23)
+#define I40EVF_FLAG_AQ_DEL_CLOUD_FILTER BIT(24)
/* OS defined structs */
struct net_device *netdev;
@@ -314,6 +372,13 @@ struct i40evf_adapter {
u16 rss_lut_size;
u8 *rss_key;
u8 *rss_lut;
+ /* ADQ related members */
+ struct i40evf_channel_config ch_config;
+ u8 num_tc;
+ struct list_head cloud_filter_list;
+ /* lock to protest access to the cloud filter list */
+ spinlock_t cloud_filter_list_lock;
+ u16 num_cloud_filters;
};
@@ -380,4 +445,8 @@ void i40evf_notify_client_message(struct i40e_vsi *vsi, u8 *msg, u16 len);
void i40evf_notify_client_l2_params(struct i40e_vsi *vsi);
void i40evf_notify_client_open(struct i40e_vsi *vsi);
void i40evf_notify_client_close(struct i40e_vsi *vsi, bool reset);
+void i40evf_enable_channels(struct i40evf_adapter *adapter);
+void i40evf_disable_channels(struct i40evf_adapter *adapter);
+void i40evf_add_cloud_filter(struct i40evf_adapter *adapter);
+void i40evf_del_cloud_filter(struct i40evf_adapter *adapter);
#endif /* _I40EVF_H_ */
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c
index e2d8aa19d205..dc4cde274fb8 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c
+++ b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
@@ -457,14 +458,14 @@ static int __i40evf_get_coalesce(struct net_device *netdev,
rx_ring = &adapter->rx_rings[queue];
tx_ring = &adapter->tx_rings[queue];
- if (ITR_IS_DYNAMIC(rx_ring->rx_itr_setting))
+ if (ITR_IS_DYNAMIC(rx_ring->itr_setting))
ec->use_adaptive_rx_coalesce = 1;
- if (ITR_IS_DYNAMIC(tx_ring->tx_itr_setting))
+ if (ITR_IS_DYNAMIC(tx_ring->itr_setting))
ec->use_adaptive_tx_coalesce = 1;
- ec->rx_coalesce_usecs = rx_ring->rx_itr_setting & ~I40E_ITR_DYNAMIC;
- ec->tx_coalesce_usecs = tx_ring->tx_itr_setting & ~I40E_ITR_DYNAMIC;
+ ec->rx_coalesce_usecs = rx_ring->itr_setting & ~I40E_ITR_DYNAMIC;
+ ec->tx_coalesce_usecs = tx_ring->itr_setting & ~I40E_ITR_DYNAMIC;
return 0;
}
@@ -502,7 +503,7 @@ static int i40evf_get_per_queue_coalesce(struct net_device *netdev,
/**
* i40evf_set_itr_per_queue - set ITR values for specific queue
- * @vsi: the VSI to set values for
+ * @adapter: the VF adapter struct to set values for
* @ec: coalesce settings from ethtool
* @queue: the queue to modify
*
@@ -514,33 +515,29 @@ static void i40evf_set_itr_per_queue(struct i40evf_adapter *adapter,
{
struct i40e_ring *rx_ring = &adapter->rx_rings[queue];
struct i40e_ring *tx_ring = &adapter->tx_rings[queue];
- struct i40e_vsi *vsi = &adapter->vsi;
- struct i40e_hw *hw = &adapter->hw;
struct i40e_q_vector *q_vector;
- u16 vector;
- rx_ring->rx_itr_setting = ec->rx_coalesce_usecs;
- tx_ring->tx_itr_setting = ec->tx_coalesce_usecs;
+ rx_ring->itr_setting = ITR_REG_ALIGN(ec->rx_coalesce_usecs);
+ tx_ring->itr_setting = ITR_REG_ALIGN(ec->tx_coalesce_usecs);
- rx_ring->rx_itr_setting |= I40E_ITR_DYNAMIC;
+ rx_ring->itr_setting |= I40E_ITR_DYNAMIC;
if (!ec->use_adaptive_rx_coalesce)
- rx_ring->rx_itr_setting ^= I40E_ITR_DYNAMIC;
+ rx_ring->itr_setting ^= I40E_ITR_DYNAMIC;
- tx_ring->tx_itr_setting |= I40E_ITR_DYNAMIC;
+ tx_ring->itr_setting |= I40E_ITR_DYNAMIC;
if (!ec->use_adaptive_tx_coalesce)
- tx_ring->tx_itr_setting ^= I40E_ITR_DYNAMIC;
+ tx_ring->itr_setting ^= I40E_ITR_DYNAMIC;
q_vector = rx_ring->q_vector;
- q_vector->rx.itr = ITR_TO_REG(rx_ring->rx_itr_setting);
- vector = vsi->base_vector + q_vector->v_idx;
- wr32(hw, I40E_VFINT_ITRN1(I40E_RX_ITR, vector - 1), q_vector->rx.itr);
+ q_vector->rx.target_itr = ITR_TO_REG(rx_ring->itr_setting);
q_vector = tx_ring->q_vector;
- q_vector->tx.itr = ITR_TO_REG(tx_ring->tx_itr_setting);
- vector = vsi->base_vector + q_vector->v_idx;
- wr32(hw, I40E_VFINT_ITRN1(I40E_TX_ITR, vector - 1), q_vector->tx.itr);
+ q_vector->tx.target_itr = ITR_TO_REG(tx_ring->itr_setting);
- i40e_flush(hw);
+ /* The interrupt handler itself will take care of programming
+ * the Tx and Rx ITR values based on the values we have entered
+ * into the q_vector, no need to write the values now.
+ */
}
/**
@@ -565,8 +562,8 @@ static int __i40evf_set_coalesce(struct net_device *netdev,
if (ec->rx_coalesce_usecs == 0) {
if (ec->use_adaptive_rx_coalesce)
netif_info(adapter, drv, netdev, "rx-usecs=0, need to disable adaptive-rx for a complete disable\n");
- } else if ((ec->rx_coalesce_usecs < (I40E_MIN_ITR << 1)) ||
- (ec->rx_coalesce_usecs > (I40E_MAX_ITR << 1))) {
+ } else if ((ec->rx_coalesce_usecs < I40E_MIN_ITR) ||
+ (ec->rx_coalesce_usecs > I40E_MAX_ITR)) {
netif_info(adapter, drv, netdev, "Invalid value, rx-usecs range is 0-8160\n");
return -EINVAL;
}
@@ -575,8 +572,8 @@ static int __i40evf_set_coalesce(struct net_device *netdev,
if (ec->tx_coalesce_usecs == 0) {
if (ec->use_adaptive_tx_coalesce)
netif_info(adapter, drv, netdev, "tx-usecs=0, need to disable adaptive-tx for a complete disable\n");
- } else if ((ec->tx_coalesce_usecs < (I40E_MIN_ITR << 1)) ||
- (ec->tx_coalesce_usecs > (I40E_MAX_ITR << 1))) {
+ } else if ((ec->tx_coalesce_usecs < I40E_MIN_ITR) ||
+ (ec->tx_coalesce_usecs > I40E_MAX_ITR)) {
netif_info(adapter, drv, netdev, "Invalid value, tx-usecs range is 0-8160\n");
return -EINVAL;
}
@@ -699,6 +696,12 @@ static int i40evf_set_channels(struct net_device *netdev,
return -EINVAL;
}
+ if ((adapter->vf_res->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_ADQ) &&
+ adapter->num_tc) {
+ dev_info(&adapter->pdev->dev, "Cannot set channels since ADq is enabled.\n");
+ return -EINVAL;
+ }
+
/* All of these should have already been checked by ethtool before this
* even gets to us, but just to be sure.
*/
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c
index 16989ad2ca90..5f71532be7f1 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c
+++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
@@ -353,11 +354,12 @@ i40evf_map_vector_to_rxq(struct i40evf_adapter *adapter, int v_idx, int r_idx)
rx_ring->vsi = &adapter->vsi;
q_vector->rx.ring = rx_ring;
q_vector->rx.count++;
- q_vector->rx.latency_range = I40E_LOW_LATENCY;
- q_vector->rx.itr = ITR_TO_REG(rx_ring->rx_itr_setting);
+ q_vector->rx.next_update = jiffies + 1;
+ q_vector->rx.target_itr = ITR_TO_REG(rx_ring->itr_setting);
q_vector->ring_mask |= BIT(r_idx);
- q_vector->itr_countdown = ITR_COUNTDOWN_START;
- wr32(hw, I40E_VFINT_ITRN1(I40E_RX_ITR, v_idx - 1), q_vector->rx.itr);
+ wr32(hw, I40E_VFINT_ITRN1(I40E_RX_ITR, q_vector->reg_idx),
+ q_vector->rx.current_itr);
+ q_vector->rx.current_itr = q_vector->rx.target_itr;
}
/**
@@ -378,11 +380,12 @@ i40evf_map_vector_to_txq(struct i40evf_adapter *adapter, int v_idx, int t_idx)
tx_ring->vsi = &adapter->vsi;
q_vector->tx.ring = tx_ring;
q_vector->tx.count++;
- q_vector->tx.latency_range = I40E_LOW_LATENCY;
- q_vector->tx.itr = ITR_TO_REG(tx_ring->tx_itr_setting);
- q_vector->itr_countdown = ITR_COUNTDOWN_START;
+ q_vector->tx.next_update = jiffies + 1;
+ q_vector->tx.target_itr = ITR_TO_REG(tx_ring->itr_setting);
q_vector->num_ringpairs++;
- wr32(hw, I40E_VFINT_ITRN1(I40E_TX_ITR, v_idx - 1), q_vector->tx.itr);
+ wr32(hw, I40E_VFINT_ITRN1(I40E_TX_ITR, q_vector->reg_idx),
+ q_vector->tx.target_itr);
+ q_vector->tx.current_itr = q_vector->tx.target_itr;
}
/**
@@ -783,7 +786,7 @@ static int i40evf_vlan_rx_kill_vid(struct net_device *netdev,
**/
static struct
i40evf_mac_filter *i40evf_find_filter(struct i40evf_adapter *adapter,
- u8 *macaddr)
+ const u8 *macaddr)
{
struct i40evf_mac_filter *f;
@@ -806,20 +809,18 @@ i40evf_mac_filter *i40evf_find_filter(struct i40evf_adapter *adapter,
**/
static struct
i40evf_mac_filter *i40evf_add_filter(struct i40evf_adapter *adapter,
- u8 *macaddr)
+ const u8 *macaddr)
{
struct i40evf_mac_filter *f;
if (!macaddr)
return NULL;
- spin_lock_bh(&adapter->mac_vlan_list_lock);
-
f = i40evf_find_filter(adapter, macaddr);
if (!f) {
f = kzalloc(sizeof(*f), GFP_ATOMIC);
if (!f)
- goto clearout;
+ return f;
ether_addr_copy(f->macaddr, macaddr);
@@ -830,8 +831,6 @@ i40evf_mac_filter *i40evf_add_filter(struct i40evf_adapter *adapter,
f->remove = false;
}
-clearout:
- spin_unlock_bh(&adapter->mac_vlan_list_lock);
return f;
}
@@ -866,9 +865,10 @@ static int i40evf_set_mac(struct net_device *netdev, void *p)
adapter->aq_required |= I40EVF_FLAG_AQ_DEL_MAC_FILTER;
}
+ f = i40evf_add_filter(adapter, addr->sa_data);
+
spin_unlock_bh(&adapter->mac_vlan_list_lock);
- f = i40evf_add_filter(adapter, addr->sa_data);
if (f) {
ether_addr_copy(hw->mac.addr, addr->sa_data);
ether_addr_copy(netdev->dev_addr, adapter->hw.mac.addr);
@@ -878,50 +878,64 @@ static int i40evf_set_mac(struct net_device *netdev, void *p)
}
/**
- * i40evf_set_rx_mode - NDO callback to set the netdev filters
- * @netdev: network interface device structure
- **/
-static void i40evf_set_rx_mode(struct net_device *netdev)
+ * i40evf_addr_sync - Callback for dev_(mc|uc)_sync to add address
+ * @netdev: the netdevice
+ * @addr: address to add
+ *
+ * Called by __dev_(mc|uc)_sync when an address needs to be added. We call
+ * __dev_(uc|mc)_sync from .set_rx_mode and guarantee to hold the hash lock.
+ */
+static int i40evf_addr_sync(struct net_device *netdev, const u8 *addr)
{
struct i40evf_adapter *adapter = netdev_priv(netdev);
- struct i40evf_mac_filter *f, *ftmp;
- struct netdev_hw_addr *uca;
- struct netdev_hw_addr *mca;
- struct netdev_hw_addr *ha;
-
- /* add addr if not already in the filter list */
- netdev_for_each_uc_addr(uca, netdev) {
- i40evf_add_filter(adapter, uca->addr);
- }
- netdev_for_each_mc_addr(mca, netdev) {
- i40evf_add_filter(adapter, mca->addr);
- }
- spin_lock_bh(&adapter->mac_vlan_list_lock);
-
- list_for_each_entry_safe(f, ftmp, &adapter->mac_filter_list, list) {
- netdev_for_each_mc_addr(mca, netdev)
- if (ether_addr_equal(mca->addr, f->macaddr))
- goto bottom_of_search_loop;
-
- netdev_for_each_uc_addr(uca, netdev)
- if (ether_addr_equal(uca->addr, f->macaddr))
- goto bottom_of_search_loop;
+ if (i40evf_add_filter(adapter, addr))
+ return 0;
+ else
+ return -ENOMEM;
+}
- for_each_dev_addr(netdev, ha)
- if (ether_addr_equal(ha->addr, f->macaddr))
- goto bottom_of_search_loop;
+/**
+ * i40evf_addr_unsync - Callback for dev_(mc|uc)_sync to remove address
+ * @netdev: the netdevice
+ * @addr: address to add
+ *
+ * Called by __dev_(mc|uc)_sync when an address needs to be removed. We call
+ * __dev_(uc|mc)_sync from .set_rx_mode and guarantee to hold the hash lock.
+ */
+static int i40evf_addr_unsync(struct net_device *netdev, const u8 *addr)
+{
+ struct i40evf_adapter *adapter = netdev_priv(netdev);
+ struct i40evf_mac_filter *f;
- if (ether_addr_equal(f->macaddr, adapter->hw.mac.addr))
- goto bottom_of_search_loop;
+ /* Under some circumstances, we might receive a request to delete
+ * our own device address from our uc list. Because we store the
+ * device address in the VSI's MAC/VLAN filter list, we need to ignore
+ * such requests and not delete our device address from this list.
+ */
+ if (ether_addr_equal(addr, netdev->dev_addr))
+ return 0;
- /* f->macaddr wasn't found in uc, mc, or ha list so delete it */
+ f = i40evf_find_filter(adapter, addr);
+ if (f) {
f->remove = true;
adapter->aq_required |= I40EVF_FLAG_AQ_DEL_MAC_FILTER;
-
-bottom_of_search_loop:
- continue;
}
+ return 0;
+}
+
+/**
+ * i40evf_set_rx_mode - NDO callback to set the netdev filters
+ * @netdev: network interface device structure
+ **/
+static void i40evf_set_rx_mode(struct net_device *netdev)
+{
+ struct i40evf_adapter *adapter = netdev_priv(netdev);
+
+ spin_lock_bh(&adapter->mac_vlan_list_lock);
+ __dev_uc_sync(netdev, i40evf_addr_sync, i40evf_addr_unsync);
+ __dev_mc_sync(netdev, i40evf_addr_sync, i40evf_addr_unsync);
+ spin_unlock_bh(&adapter->mac_vlan_list_lock);
if (netdev->flags & IFF_PROMISC &&
!(adapter->flags & I40EVF_FLAG_PROMISC_ON))
@@ -936,8 +950,6 @@ bottom_of_search_loop:
else if (!(netdev->flags & IFF_ALLMULTI) &&
adapter->flags & I40EVF_FLAG_ALLMULTI_ON)
adapter->aq_required |= I40EVF_FLAG_AQ_RELEASE_ALLMULTI;
-
- spin_unlock_bh(&adapter->mac_vlan_list_lock);
}
/**
@@ -1025,7 +1037,9 @@ static void i40evf_up_complete(struct i40evf_adapter *adapter)
void i40evf_down(struct i40evf_adapter *adapter)
{
struct net_device *netdev = adapter->netdev;
+ struct i40evf_vlan_filter *vlf;
struct i40evf_mac_filter *f;
+ struct i40evf_cloud_filter *cf;
if (adapter->state <= __I40EVF_DOWN_PENDING)
return;
@@ -1038,17 +1052,29 @@ void i40evf_down(struct i40evf_adapter *adapter)
spin_lock_bh(&adapter->mac_vlan_list_lock);
+ /* clear the sync flag on all filters */
+ __dev_uc_unsync(adapter->netdev, NULL);
+ __dev_mc_unsync(adapter->netdev, NULL);
+
/* remove all MAC filters */
list_for_each_entry(f, &adapter->mac_filter_list, list) {
f->remove = true;
}
+
/* remove all VLAN filters */
- list_for_each_entry(f, &adapter->vlan_filter_list, list) {
- f->remove = true;
+ list_for_each_entry(vlf, &adapter->vlan_filter_list, list) {
+ vlf->remove = true;
}
spin_unlock_bh(&adapter->mac_vlan_list_lock);
+ /* remove all cloud filters */
+ spin_lock_bh(&adapter->cloud_filter_list_lock);
+ list_for_each_entry(cf, &adapter->cloud_filter_list, list) {
+ cf->del = true;
+ }
+ spin_unlock_bh(&adapter->cloud_filter_list_lock);
+
if (!(adapter->flags & I40EVF_FLAG_PF_COMMS_FAILED) &&
adapter->state != __I40EVF_RESETTING) {
/* cancel any current operation */
@@ -1059,6 +1085,7 @@ void i40evf_down(struct i40evf_adapter *adapter)
*/
adapter->aq_required = I40EVF_FLAG_AQ_DEL_MAC_FILTER;
adapter->aq_required |= I40EVF_FLAG_AQ_DEL_VLAN_FILTER;
+ adapter->aq_required |= I40EVF_FLAG_AQ_DEL_CLOUD_FILTER;
adapter->aq_required |= I40EVF_FLAG_AQ_DISABLE_QUEUES;
}
@@ -1144,6 +1171,9 @@ static int i40evf_alloc_queues(struct i40evf_adapter *adapter)
*/
if (adapter->num_req_queues)
num_active_queues = adapter->num_req_queues;
+ else if ((adapter->vf_res->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_ADQ) &&
+ adapter->num_tc)
+ num_active_queues = adapter->ch_config.total_qps;
else
num_active_queues = min_t(int,
adapter->vsi_res->num_queue_pairs,
@@ -1169,7 +1199,7 @@ static int i40evf_alloc_queues(struct i40evf_adapter *adapter)
tx_ring->netdev = adapter->netdev;
tx_ring->dev = &adapter->pdev->dev;
tx_ring->count = adapter->tx_desc_count;
- tx_ring->tx_itr_setting = I40E_ITR_TX_DEF;
+ tx_ring->itr_setting = I40E_ITR_TX_DEF;
if (adapter->flags & I40EVF_FLAG_WB_ON_ITR_CAPABLE)
tx_ring->flags |= I40E_TXR_FLAGS_WB_ON_ITR;
@@ -1178,7 +1208,7 @@ static int i40evf_alloc_queues(struct i40evf_adapter *adapter)
rx_ring->netdev = adapter->netdev;
rx_ring->dev = &adapter->pdev->dev;
rx_ring->count = adapter->rx_desc_count;
- rx_ring->rx_itr_setting = I40E_ITR_RX_DEF;
+ rx_ring->itr_setting = I40E_ITR_RX_DEF;
}
adapter->num_active_queues = num_active_queues;
@@ -1471,6 +1501,16 @@ int i40evf_init_interrupt_scheme(struct i40evf_adapter *adapter)
goto err_alloc_q_vectors;
}
+ /* If we've made it so far while ADq flag being ON, then we haven't
+ * bailed out anywhere in middle. And ADq isn't just enabled but actual
+ * resources have been allocated in the reset path.
+ * Now we can truly claim that ADq is enabled.
+ */
+ if ((adapter->vf_res->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_ADQ) &&
+ adapter->num_tc)
+ dev_info(&adapter->pdev->dev, "ADq Enabled, %u TCs created",
+ adapter->num_tc);
+
dev_info(&adapter->pdev->dev, "Multiqueue %s: Queue pair count = %u",
(adapter->num_active_queues > 1) ? "Enabled" : "Disabled",
adapter->num_active_queues);
@@ -1712,6 +1752,27 @@ static void i40evf_watchdog_task(struct work_struct *work)
i40evf_set_promiscuous(adapter, 0);
goto watchdog_done;
}
+
+ if (adapter->aq_required & I40EVF_FLAG_AQ_ENABLE_CHANNELS) {
+ i40evf_enable_channels(adapter);
+ goto watchdog_done;
+ }
+
+ if (adapter->aq_required & I40EVF_FLAG_AQ_DISABLE_CHANNELS) {
+ i40evf_disable_channels(adapter);
+ goto watchdog_done;
+ }
+
+ if (adapter->aq_required & I40EVF_FLAG_AQ_ADD_CLOUD_FILTER) {
+ i40evf_add_cloud_filter(adapter);
+ goto watchdog_done;
+ }
+
+ if (adapter->aq_required & I40EVF_FLAG_AQ_DEL_CLOUD_FILTER) {
+ i40evf_del_cloud_filter(adapter);
+ goto watchdog_done;
+ }
+
schedule_delayed_work(&adapter->client_task, msecs_to_jiffies(5));
if (adapter->state == __I40EVF_RUNNING)
@@ -1735,6 +1796,7 @@ static void i40evf_disable_vf(struct i40evf_adapter *adapter)
{
struct i40evf_mac_filter *f, *ftmp;
struct i40evf_vlan_filter *fv, *fvtmp;
+ struct i40evf_cloud_filter *cf, *cftmp;
adapter->flags |= I40EVF_FLAG_PF_COMMS_FAILED;
@@ -1756,7 +1818,7 @@ static void i40evf_disable_vf(struct i40evf_adapter *adapter)
spin_lock_bh(&adapter->mac_vlan_list_lock);
- /* Delete all of the filters, both MAC and VLAN. */
+ /* Delete all of the filters */
list_for_each_entry_safe(f, ftmp, &adapter->mac_filter_list, list) {
list_del(&f->list);
kfree(f);
@@ -1769,6 +1831,14 @@ static void i40evf_disable_vf(struct i40evf_adapter *adapter)
spin_unlock_bh(&adapter->mac_vlan_list_lock);
+ spin_lock_bh(&adapter->cloud_filter_list_lock);
+ list_for_each_entry_safe(cf, cftmp, &adapter->cloud_filter_list, list) {
+ list_del(&cf->list);
+ kfree(cf);
+ adapter->num_cloud_filters--;
+ }
+ spin_unlock_bh(&adapter->cloud_filter_list_lock);
+
i40evf_free_misc_irq(adapter);
i40evf_reset_interrupt_capability(adapter);
i40evf_free_queues(adapter);
@@ -1798,9 +1868,11 @@ static void i40evf_reset_task(struct work_struct *work)
struct i40evf_adapter *adapter = container_of(work,
struct i40evf_adapter,
reset_task);
+ struct virtchnl_vf_resource *vfres = adapter->vf_res;
struct net_device *netdev = adapter->netdev;
struct i40e_hw *hw = &adapter->hw;
struct i40evf_vlan_filter *vlf;
+ struct i40evf_cloud_filter *cf;
struct i40evf_mac_filter *f;
u32 reg_val;
int i = 0, err;
@@ -1893,6 +1965,7 @@ continue_reset:
i40evf_free_all_rx_resources(adapter);
i40evf_free_all_tx_resources(adapter);
+ adapter->flags |= I40EVF_FLAG_QUEUES_DISABLED;
/* kill and reinit the admin queue */
i40evf_shutdown_adminq(hw);
adapter->current_op = VIRTCHNL_OP_UNKNOWN;
@@ -1924,8 +1997,19 @@ continue_reset:
spin_unlock_bh(&adapter->mac_vlan_list_lock);
+ /* check if TCs are running and re-add all cloud filters */
+ spin_lock_bh(&adapter->cloud_filter_list_lock);
+ if ((vfres->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_ADQ) &&
+ adapter->num_tc) {
+ list_for_each_entry(cf, &adapter->cloud_filter_list, list) {
+ cf->add = true;
+ }
+ }
+ spin_unlock_bh(&adapter->cloud_filter_list_lock);
+
adapter->aq_required |= I40EVF_FLAG_AQ_ADD_MAC_FILTER;
adapter->aq_required |= I40EVF_FLAG_AQ_ADD_VLAN_FILTER;
+ adapter->aq_required |= I40EVF_FLAG_AQ_ADD_CLOUD_FILTER;
i40evf_misc_irq_enable(adapter);
mod_timer(&adapter->watchdog_timer, jiffies + 2);
@@ -2191,6 +2275,712 @@ void i40evf_free_all_rx_resources(struct i40evf_adapter *adapter)
}
/**
+ * i40evf_validate_tx_bandwidth - validate the max Tx bandwidth
+ * @adapter: board private structure
+ * @max_tx_rate: max Tx bw for a tc
+ **/
+static int i40evf_validate_tx_bandwidth(struct i40evf_adapter *adapter,
+ u64 max_tx_rate)
+{
+ int speed = 0, ret = 0;
+
+ switch (adapter->link_speed) {
+ case I40E_LINK_SPEED_40GB:
+ speed = 40000;
+ break;
+ case I40E_LINK_SPEED_25GB:
+ speed = 25000;
+ break;
+ case I40E_LINK_SPEED_20GB:
+ speed = 20000;
+ break;
+ case I40E_LINK_SPEED_10GB:
+ speed = 10000;
+ break;
+ case I40E_LINK_SPEED_1GB:
+ speed = 1000;
+ break;
+ case I40E_LINK_SPEED_100MB:
+ speed = 100;
+ break;
+ default:
+ break;
+ }
+
+ if (max_tx_rate > speed) {
+ dev_err(&adapter->pdev->dev,
+ "Invalid tx rate specified\n");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+/**
+ * i40evf_validate_channel_config - validate queue mapping info
+ * @adapter: board private structure
+ * @mqprio_qopt: queue parameters
+ *
+ * This function validates if the config provided by the user to
+ * configure queue channels is valid or not. Returns 0 on a valid
+ * config.
+ **/
+static int i40evf_validate_ch_config(struct i40evf_adapter *adapter,
+ struct tc_mqprio_qopt_offload *mqprio_qopt)
+{
+ u64 total_max_rate = 0;
+ int i, num_qps = 0;
+ u64 tx_rate = 0;
+ int ret = 0;
+
+ if (mqprio_qopt->qopt.num_tc > I40EVF_MAX_TRAFFIC_CLASS ||
+ mqprio_qopt->qopt.num_tc < 1)
+ return -EINVAL;
+
+ for (i = 0; i <= mqprio_qopt->qopt.num_tc - 1; i++) {
+ if (!mqprio_qopt->qopt.count[i] ||
+ mqprio_qopt->qopt.offset[i] != num_qps)
+ return -EINVAL;
+ if (mqprio_qopt->min_rate[i]) {
+ dev_err(&adapter->pdev->dev,
+ "Invalid min tx rate (greater than 0) specified\n");
+ return -EINVAL;
+ }
+ /*convert to Mbps */
+ tx_rate = div_u64(mqprio_qopt->max_rate[i],
+ I40EVF_MBPS_DIVISOR);
+ total_max_rate += tx_rate;
+ num_qps += mqprio_qopt->qopt.count[i];
+ }
+ if (num_qps > MAX_QUEUES)
+ return -EINVAL;
+
+ ret = i40evf_validate_tx_bandwidth(adapter, total_max_rate);
+ return ret;
+}
+
+/**
+ * i40evf_del_all_cloud_filters - delete all cloud filters
+ * on the traffic classes
+ **/
+static void i40evf_del_all_cloud_filters(struct i40evf_adapter *adapter)
+{
+ struct i40evf_cloud_filter *cf, *cftmp;
+
+ spin_lock_bh(&adapter->cloud_filter_list_lock);
+ list_for_each_entry_safe(cf, cftmp, &adapter->cloud_filter_list,
+ list) {
+ list_del(&cf->list);
+ kfree(cf);
+ adapter->num_cloud_filters--;
+ }
+ spin_unlock_bh(&adapter->cloud_filter_list_lock);
+}
+
+/**
+ * __i40evf_setup_tc - configure multiple traffic classes
+ * @netdev: network interface device structure
+ * @type_date: tc offload data
+ *
+ * This function processes the config information provided by the
+ * user to configure traffic classes/queue channels and packages the
+ * information to request the PF to setup traffic classes.
+ *
+ * Returns 0 on success.
+ **/
+static int __i40evf_setup_tc(struct net_device *netdev, void *type_data)
+{
+ struct tc_mqprio_qopt_offload *mqprio_qopt = type_data;
+ struct i40evf_adapter *adapter = netdev_priv(netdev);
+ struct virtchnl_vf_resource *vfres = adapter->vf_res;
+ u8 num_tc = 0, total_qps = 0;
+ int ret = 0, netdev_tc = 0;
+ u64 max_tx_rate;
+ u16 mode;
+ int i;
+
+ num_tc = mqprio_qopt->qopt.num_tc;
+ mode = mqprio_qopt->mode;
+
+ /* delete queue_channel */
+ if (!mqprio_qopt->qopt.hw) {
+ if (adapter->ch_config.state == __I40EVF_TC_RUNNING) {
+ /* reset the tc configuration */
+ netdev_reset_tc(netdev);
+ adapter->num_tc = 0;
+ netif_tx_stop_all_queues(netdev);
+ netif_tx_disable(netdev);
+ i40evf_del_all_cloud_filters(adapter);
+ adapter->aq_required = I40EVF_FLAG_AQ_DISABLE_CHANNELS;
+ goto exit;
+ } else {
+ return -EINVAL;
+ }
+ }
+
+ /* add queue channel */
+ if (mode == TC_MQPRIO_MODE_CHANNEL) {
+ if (!(vfres->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_ADQ)) {
+ dev_err(&adapter->pdev->dev, "ADq not supported\n");
+ return -EOPNOTSUPP;
+ }
+ if (adapter->ch_config.state != __I40EVF_TC_INVALID) {
+ dev_err(&adapter->pdev->dev, "TC configuration already exists\n");
+ return -EINVAL;
+ }
+
+ ret = i40evf_validate_ch_config(adapter, mqprio_qopt);
+ if (ret)
+ return ret;
+ /* Return if same TC config is requested */
+ if (adapter->num_tc == num_tc)
+ return 0;
+ adapter->num_tc = num_tc;
+
+ for (i = 0; i < I40EVF_MAX_TRAFFIC_CLASS; i++) {
+ if (i < num_tc) {
+ adapter->ch_config.ch_info[i].count =
+ mqprio_qopt->qopt.count[i];
+ adapter->ch_config.ch_info[i].offset =
+ mqprio_qopt->qopt.offset[i];
+ total_qps += mqprio_qopt->qopt.count[i];
+ max_tx_rate = mqprio_qopt->max_rate[i];
+ /* convert to Mbps */
+ max_tx_rate = div_u64(max_tx_rate,
+ I40EVF_MBPS_DIVISOR);
+ adapter->ch_config.ch_info[i].max_tx_rate =
+ max_tx_rate;
+ } else {
+ adapter->ch_config.ch_info[i].count = 1;
+ adapter->ch_config.ch_info[i].offset = 0;
+ }
+ }
+ adapter->ch_config.total_qps = total_qps;
+ netif_tx_stop_all_queues(netdev);
+ netif_tx_disable(netdev);
+ adapter->aq_required |= I40EVF_FLAG_AQ_ENABLE_CHANNELS;
+ netdev_reset_tc(netdev);
+ /* Report the tc mapping up the stack */
+ netdev_set_num_tc(adapter->netdev, num_tc);
+ for (i = 0; i < I40EVF_MAX_TRAFFIC_CLASS; i++) {
+ u16 qcount = mqprio_qopt->qopt.count[i];
+ u16 qoffset = mqprio_qopt->qopt.offset[i];
+
+ if (i < num_tc)
+ netdev_set_tc_queue(netdev, netdev_tc++, qcount,
+ qoffset);
+ }
+ }
+exit:
+ return ret;
+}
+
+/**
+ * i40evf_parse_cls_flower - Parse tc flower filters provided by kernel
+ * @adapter: board private structure
+ * @cls_flower: pointer to struct tc_cls_flower_offload
+ * @filter: pointer to cloud filter structure
+ */
+static int i40evf_parse_cls_flower(struct i40evf_adapter *adapter,
+ struct tc_cls_flower_offload *f,
+ struct i40evf_cloud_filter *filter)
+{
+ u16 n_proto_mask = 0;
+ u16 n_proto_key = 0;
+ u8 field_flags = 0;
+ u16 addr_type = 0;
+ u16 n_proto = 0;
+ int i = 0;
+ struct virtchnl_filter *vf = &filter->f;
+
+ if (f->dissector->used_keys &
+ ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
+ BIT(FLOW_DISSECTOR_KEY_BASIC) |
+ BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
+ BIT(FLOW_DISSECTOR_KEY_VLAN) |
+ BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
+ BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
+ BIT(FLOW_DISSECTOR_KEY_PORTS) |
+ BIT(FLOW_DISSECTOR_KEY_ENC_KEYID))) {
+ dev_err(&adapter->pdev->dev, "Unsupported key used: 0x%x\n",
+ f->dissector->used_keys);
+ return -EOPNOTSUPP;
+ }
+
+ if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
+ struct flow_dissector_key_keyid *mask =
+ skb_flow_dissector_target(f->dissector,
+ FLOW_DISSECTOR_KEY_ENC_KEYID,
+ f->mask);
+
+ if (mask->keyid != 0)
+ field_flags |= I40EVF_CLOUD_FIELD_TEN_ID;
+ }
+
+ if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
+ struct flow_dissector_key_basic *key =
+ skb_flow_dissector_target(f->dissector,
+ FLOW_DISSECTOR_KEY_BASIC,
+ f->key);
+
+ struct flow_dissector_key_basic *mask =
+ skb_flow_dissector_target(f->dissector,
+ FLOW_DISSECTOR_KEY_BASIC,
+ f->mask);
+ n_proto_key = ntohs(key->n_proto);
+ n_proto_mask = ntohs(mask->n_proto);
+
+ if (n_proto_key == ETH_P_ALL) {
+ n_proto_key = 0;
+ n_proto_mask = 0;
+ }
+ n_proto = n_proto_key & n_proto_mask;
+ if (n_proto != ETH_P_IP && n_proto != ETH_P_IPV6)
+ return -EINVAL;
+ if (n_proto == ETH_P_IPV6) {
+ /* specify flow type as TCP IPv6 */
+ vf->flow_type = VIRTCHNL_TCP_V6_FLOW;
+ }
+
+ if (key->ip_proto != IPPROTO_TCP) {
+ dev_info(&adapter->pdev->dev, "Only TCP transport is supported\n");
+ return -EINVAL;
+ }
+ }
+
+ if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
+ struct flow_dissector_key_eth_addrs *key =
+ skb_flow_dissector_target(f->dissector,
+ FLOW_DISSECTOR_KEY_ETH_ADDRS,
+ f->key);
+
+ struct flow_dissector_key_eth_addrs *mask =
+ skb_flow_dissector_target(f->dissector,
+ FLOW_DISSECTOR_KEY_ETH_ADDRS,
+ f->mask);
+ /* use is_broadcast and is_zero to check for all 0xf or 0 */
+ if (!is_zero_ether_addr(mask->dst)) {
+ if (is_broadcast_ether_addr(mask->dst)) {
+ field_flags |= I40EVF_CLOUD_FIELD_OMAC;
+ } else {
+ dev_err(&adapter->pdev->dev, "Bad ether dest mask %pM\n",
+ mask->dst);
+ return I40E_ERR_CONFIG;
+ }
+ }
+
+ if (!is_zero_ether_addr(mask->src)) {
+ if (is_broadcast_ether_addr(mask->src)) {
+ field_flags |= I40EVF_CLOUD_FIELD_IMAC;
+ } else {
+ dev_err(&adapter->pdev->dev, "Bad ether src mask %pM\n",
+ mask->src);
+ return I40E_ERR_CONFIG;
+ }
+ }
+
+ if (!is_zero_ether_addr(key->dst))
+ if (is_valid_ether_addr(key->dst) ||
+ is_multicast_ether_addr(key->dst)) {
+ /* set the mask if a valid dst_mac address */
+ for (i = 0; i < ETH_ALEN; i++)
+ vf->mask.tcp_spec.dst_mac[i] |= 0xff;
+ ether_addr_copy(vf->data.tcp_spec.dst_mac,
+ key->dst);
+ }
+
+ if (!is_zero_ether_addr(key->src))
+ if (is_valid_ether_addr(key->src) ||
+ is_multicast_ether_addr(key->src)) {
+ /* set the mask if a valid dst_mac address */
+ for (i = 0; i < ETH_ALEN; i++)
+ vf->mask.tcp_spec.src_mac[i] |= 0xff;
+ ether_addr_copy(vf->data.tcp_spec.src_mac,
+ key->src);
+ }
+ }
+
+ if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_VLAN)) {
+ struct flow_dissector_key_vlan *key =
+ skb_flow_dissector_target(f->dissector,
+ FLOW_DISSECTOR_KEY_VLAN,
+ f->key);
+ struct flow_dissector_key_vlan *mask =
+ skb_flow_dissector_target(f->dissector,
+ FLOW_DISSECTOR_KEY_VLAN,
+ f->mask);
+
+ if (mask->vlan_id) {
+ if (mask->vlan_id == VLAN_VID_MASK) {
+ field_flags |= I40EVF_CLOUD_FIELD_IVLAN;
+ } else {
+ dev_err(&adapter->pdev->dev, "Bad vlan mask %u\n",
+ mask->vlan_id);
+ return I40E_ERR_CONFIG;
+ }
+ }
+ vf->mask.tcp_spec.vlan_id |= cpu_to_be16(0xffff);
+ vf->data.tcp_spec.vlan_id = cpu_to_be16(key->vlan_id);
+ }
+
+ if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CONTROL)) {
+ struct flow_dissector_key_control *key =
+ skb_flow_dissector_target(f->dissector,
+ FLOW_DISSECTOR_KEY_CONTROL,
+ f->key);
+
+ addr_type = key->addr_type;
+ }
+
+ if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
+ struct flow_dissector_key_ipv4_addrs *key =
+ skb_flow_dissector_target(f->dissector,
+ FLOW_DISSECTOR_KEY_IPV4_ADDRS,
+ f->key);
+ struct flow_dissector_key_ipv4_addrs *mask =
+ skb_flow_dissector_target(f->dissector,
+ FLOW_DISSECTOR_KEY_IPV4_ADDRS,
+ f->mask);
+
+ if (mask->dst) {
+ if (mask->dst == cpu_to_be32(0xffffffff)) {
+ field_flags |= I40EVF_CLOUD_FIELD_IIP;
+ } else {
+ dev_err(&adapter->pdev->dev, "Bad ip dst mask 0x%08x\n",
+ be32_to_cpu(mask->dst));
+ return I40E_ERR_CONFIG;
+ }
+ }
+
+ if (mask->src) {
+ if (mask->src == cpu_to_be32(0xffffffff)) {
+ field_flags |= I40EVF_CLOUD_FIELD_IIP;
+ } else {
+ dev_err(&adapter->pdev->dev, "Bad ip src mask 0x%08x\n",
+ be32_to_cpu(mask->dst));
+ return I40E_ERR_CONFIG;
+ }
+ }
+
+ if (field_flags & I40EVF_CLOUD_FIELD_TEN_ID) {
+ dev_info(&adapter->pdev->dev, "Tenant id not allowed for ip filter\n");
+ return I40E_ERR_CONFIG;
+ }
+ if (key->dst) {
+ vf->mask.tcp_spec.dst_ip[0] |= cpu_to_be32(0xffffffff);
+ vf->data.tcp_spec.dst_ip[0] = key->dst;
+ }
+ if (key->src) {
+ vf->mask.tcp_spec.src_ip[0] |= cpu_to_be32(0xffffffff);
+ vf->data.tcp_spec.src_ip[0] = key->src;
+ }
+ }
+
+ if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
+ struct flow_dissector_key_ipv6_addrs *key =
+ skb_flow_dissector_target(f->dissector,
+ FLOW_DISSECTOR_KEY_IPV6_ADDRS,
+ f->key);
+ struct flow_dissector_key_ipv6_addrs *mask =
+ skb_flow_dissector_target(f->dissector,
+ FLOW_DISSECTOR_KEY_IPV6_ADDRS,
+ f->mask);
+
+ /* validate mask, make sure it is not IPV6_ADDR_ANY */
+ if (ipv6_addr_any(&mask->dst)) {
+ dev_err(&adapter->pdev->dev, "Bad ipv6 dst mask 0x%02x\n",
+ IPV6_ADDR_ANY);
+ return I40E_ERR_CONFIG;
+ }
+
+ /* src and dest IPv6 address should not be LOOPBACK
+ * (0:0:0:0:0:0:0:1) which can be represented as ::1
+ */
+ if (ipv6_addr_loopback(&key->dst) ||
+ ipv6_addr_loopback(&key->src)) {
+ dev_err(&adapter->pdev->dev,
+ "ipv6 addr should not be loopback\n");
+ return I40E_ERR_CONFIG;
+ }
+ if (!ipv6_addr_any(&mask->dst) || !ipv6_addr_any(&mask->src))
+ field_flags |= I40EVF_CLOUD_FIELD_IIP;
+
+ for (i = 0; i < 4; i++)
+ vf->mask.tcp_spec.dst_ip[i] |= cpu_to_be32(0xffffffff);
+ memcpy(&vf->data.tcp_spec.dst_ip, &key->dst.s6_addr32,
+ sizeof(vf->data.tcp_spec.dst_ip));
+ for (i = 0; i < 4; i++)
+ vf->mask.tcp_spec.src_ip[i] |= cpu_to_be32(0xffffffff);
+ memcpy(&vf->data.tcp_spec.src_ip, &key->src.s6_addr32,
+ sizeof(vf->data.tcp_spec.src_ip));
+ }
+ if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_PORTS)) {
+ struct flow_dissector_key_ports *key =
+ skb_flow_dissector_target(f->dissector,
+ FLOW_DISSECTOR_KEY_PORTS,
+ f->key);
+ struct flow_dissector_key_ports *mask =
+ skb_flow_dissector_target(f->dissector,
+ FLOW_DISSECTOR_KEY_PORTS,
+ f->mask);
+
+ if (mask->src) {
+ if (mask->src == cpu_to_be16(0xffff)) {
+ field_flags |= I40EVF_CLOUD_FIELD_IIP;
+ } else {
+ dev_err(&adapter->pdev->dev, "Bad src port mask %u\n",
+ be16_to_cpu(mask->src));
+ return I40E_ERR_CONFIG;
+ }
+ }
+
+ if (mask->dst) {
+ if (mask->dst == cpu_to_be16(0xffff)) {
+ field_flags |= I40EVF_CLOUD_FIELD_IIP;
+ } else {
+ dev_err(&adapter->pdev->dev, "Bad dst port mask %u\n",
+ be16_to_cpu(mask->dst));
+ return I40E_ERR_CONFIG;
+ }
+ }
+ if (key->dst) {
+ vf->mask.tcp_spec.dst_port |= cpu_to_be16(0xffff);
+ vf->data.tcp_spec.dst_port = key->dst;
+ }
+
+ if (key->src) {
+ vf->mask.tcp_spec.src_port |= cpu_to_be16(0xffff);
+ vf->data.tcp_spec.src_port = key->src;
+ }
+ }
+ vf->field_flags = field_flags;
+
+ return 0;
+}
+
+/**
+ * i40evf_handle_tclass - Forward to a traffic class on the device
+ * @adapter: board private structure
+ * @tc: traffic class index on the device
+ * @filter: pointer to cloud filter structure
+ */
+static int i40evf_handle_tclass(struct i40evf_adapter *adapter, u32 tc,
+ struct i40evf_cloud_filter *filter)
+{
+ if (tc == 0)
+ return 0;
+ if (tc < adapter->num_tc) {
+ if (!filter->f.data.tcp_spec.dst_port) {
+ dev_err(&adapter->pdev->dev,
+ "Specify destination port to redirect to traffic class other than TC0\n");
+ return -EINVAL;
+ }
+ }
+ /* redirect to a traffic class on the same device */
+ filter->f.action = VIRTCHNL_ACTION_TC_REDIRECT;
+ filter->f.action_meta = tc;
+ return 0;
+}
+
+/**
+ * i40evf_configure_clsflower - Add tc flower filters
+ * @adapter: board private structure
+ * @cls_flower: Pointer to struct tc_cls_flower_offload
+ */
+static int i40evf_configure_clsflower(struct i40evf_adapter *adapter,
+ struct tc_cls_flower_offload *cls_flower)
+{
+ int tc = tc_classid_to_hwtc(adapter->netdev, cls_flower->classid);
+ struct i40evf_cloud_filter *filter = NULL;
+ int err = -EINVAL, count = 50;
+
+ if (tc < 0) {
+ dev_err(&adapter->pdev->dev, "Invalid traffic class\n");
+ return -EINVAL;
+ }
+
+ filter = kzalloc(sizeof(*filter), GFP_KERNEL);
+ if (!filter)
+ return -ENOMEM;
+
+ while (test_and_set_bit(__I40EVF_IN_CRITICAL_TASK,
+ &adapter->crit_section)) {
+ if (--count == 0)
+ goto err;
+ udelay(1);
+ }
+
+ filter->cookie = cls_flower->cookie;
+
+ /* set the mask to all zeroes to begin with */
+ memset(&filter->f.mask.tcp_spec, 0, sizeof(struct virtchnl_l4_spec));
+ /* start out with flow type and eth type IPv4 to begin with */
+ filter->f.flow_type = VIRTCHNL_TCP_V4_FLOW;
+ err = i40evf_parse_cls_flower(adapter, cls_flower, filter);
+ if (err < 0)
+ goto err;
+
+ err = i40evf_handle_tclass(adapter, tc, filter);
+ if (err < 0)
+ goto err;
+
+ /* add filter to the list */
+ spin_lock_bh(&adapter->cloud_filter_list_lock);
+ list_add_tail(&filter->list, &adapter->cloud_filter_list);
+ adapter->num_cloud_filters++;
+ filter->add = true;
+ adapter->aq_required |= I40EVF_FLAG_AQ_ADD_CLOUD_FILTER;
+ spin_unlock_bh(&adapter->cloud_filter_list_lock);
+err:
+ if (err)
+ kfree(filter);
+
+ clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section);
+ return err;
+}
+
+/* i40evf_find_cf - Find the cloud filter in the list
+ * @adapter: Board private structure
+ * @cookie: filter specific cookie
+ *
+ * Returns ptr to the filter object or NULL. Must be called while holding the
+ * cloud_filter_list_lock.
+ */
+static struct i40evf_cloud_filter *i40evf_find_cf(struct i40evf_adapter *adapter,
+ unsigned long *cookie)
+{
+ struct i40evf_cloud_filter *filter = NULL;
+
+ if (!cookie)
+ return NULL;
+
+ list_for_each_entry(filter, &adapter->cloud_filter_list, list) {
+ if (!memcmp(cookie, &filter->cookie, sizeof(filter->cookie)))
+ return filter;
+ }
+ return NULL;
+}
+
+/**
+ * i40evf_delete_clsflower - Remove tc flower filters
+ * @adapter: board private structure
+ * @cls_flower: Pointer to struct tc_cls_flower_offload
+ */
+static int i40evf_delete_clsflower(struct i40evf_adapter *adapter,
+ struct tc_cls_flower_offload *cls_flower)
+{
+ struct i40evf_cloud_filter *filter = NULL;
+ int err = 0;
+
+ spin_lock_bh(&adapter->cloud_filter_list_lock);
+ filter = i40evf_find_cf(adapter, &cls_flower->cookie);
+ if (filter) {
+ filter->del = true;
+ adapter->aq_required |= I40EVF_FLAG_AQ_DEL_CLOUD_FILTER;
+ } else {
+ err = -EINVAL;
+ }
+ spin_unlock_bh(&adapter->cloud_filter_list_lock);
+
+ return err;
+}
+
+/**
+ * i40evf_setup_tc_cls_flower - flower classifier offloads
+ * @netdev: net device to configure
+ * @type_data: offload data
+ */
+static int i40evf_setup_tc_cls_flower(struct i40evf_adapter *adapter,
+ struct tc_cls_flower_offload *cls_flower)
+{
+ if (cls_flower->common.chain_index)
+ return -EOPNOTSUPP;
+
+ switch (cls_flower->command) {
+ case TC_CLSFLOWER_REPLACE:
+ return i40evf_configure_clsflower(adapter, cls_flower);
+ case TC_CLSFLOWER_DESTROY:
+ return i40evf_delete_clsflower(adapter, cls_flower);
+ case TC_CLSFLOWER_STATS:
+ return -EOPNOTSUPP;
+ default:
+ return -EINVAL;
+ }
+}
+
+/**
+ * i40evf_setup_tc_block_cb - block callback for tc
+ * @type: type of offload
+ * @type_data: offload data
+ * @cb_priv:
+ *
+ * This function is the block callback for traffic classes
+ **/
+static int i40evf_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
+ void *cb_priv)
+{
+ switch (type) {
+ case TC_SETUP_CLSFLOWER:
+ return i40evf_setup_tc_cls_flower(cb_priv, type_data);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+/**
+ * i40evf_setup_tc_block - register callbacks for tc
+ * @netdev: network interface device structure
+ * @f: tc offload data
+ *
+ * This function registers block callbacks for tc
+ * offloads
+ **/
+static int i40evf_setup_tc_block(struct net_device *dev,
+ struct tc_block_offload *f)
+{
+ struct i40evf_adapter *adapter = netdev_priv(dev);
+
+ if (f->binder_type != TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
+ return -EOPNOTSUPP;
+
+ switch (f->command) {
+ case TC_BLOCK_BIND:
+ return tcf_block_cb_register(f->block, i40evf_setup_tc_block_cb,
+ adapter, adapter);
+ case TC_BLOCK_UNBIND:
+ tcf_block_cb_unregister(f->block, i40evf_setup_tc_block_cb,
+ adapter);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+/**
+ * i40evf_setup_tc - configure multiple traffic classes
+ * @netdev: network interface device structure
+ * @type: type of offload
+ * @type_date: tc offload data
+ *
+ * This function is the callback to ndo_setup_tc in the
+ * netdev_ops.
+ *
+ * Returns 0 on success
+ **/
+static int i40evf_setup_tc(struct net_device *netdev, enum tc_setup_type type,
+ void *type_data)
+{
+ switch (type) {
+ case TC_SETUP_QDISC_MQPRIO:
+ return __i40evf_setup_tc(netdev, type_data);
+ case TC_SETUP_BLOCK:
+ return i40evf_setup_tc_block(netdev, type_data);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+/**
* i40evf_open - Called when a network interface is made active
* @netdev: network interface device structure
*
@@ -2236,7 +3026,12 @@ static int i40evf_open(struct net_device *netdev)
if (err)
goto err_req_irq;
+ spin_lock_bh(&adapter->mac_vlan_list_lock);
+
i40evf_add_filter(adapter, adapter->hw.mac.addr);
+
+ spin_unlock_bh(&adapter->mac_vlan_list_lock);
+
i40evf_configure(adapter);
i40evf_up_complete(adapter);
@@ -2457,6 +3252,7 @@ static const struct net_device_ops i40evf_netdev_ops = {
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = i40evf_netpoll,
#endif
+ .ndo_setup_tc = i40evf_setup_tc,
};
/**
@@ -2571,6 +3367,9 @@ int i40evf_process_config(struct i40evf_adapter *adapter)
if (vfres->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_VLAN)
hw_features |= (NETIF_F_HW_VLAN_CTAG_TX |
NETIF_F_HW_VLAN_CTAG_RX);
+ /* Enable cloud filter if ADQ is supported */
+ if (vfres->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_ADQ)
+ hw_features |= NETIF_F_HW_TC;
netdev->hw_features |= hw_features;
@@ -2938,9 +3737,11 @@ static int i40evf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
mutex_init(&hw->aq.arq_mutex);
spin_lock_init(&adapter->mac_vlan_list_lock);
+ spin_lock_init(&adapter->cloud_filter_list_lock);
INIT_LIST_HEAD(&adapter->mac_filter_list);
INIT_LIST_HEAD(&adapter->vlan_filter_list);
+ INIT_LIST_HEAD(&adapter->cloud_filter_list);
INIT_WORK(&adapter->reset_task, i40evf_reset_task);
INIT_WORK(&adapter->adminq_task, i40evf_adminq_task);
@@ -3065,7 +3866,9 @@ static void i40evf_remove(struct pci_dev *pdev)
{
struct net_device *netdev = pci_get_drvdata(pdev);
struct i40evf_adapter *adapter = netdev_priv(netdev);
+ struct i40evf_vlan_filter *vlf, *vlftmp;
struct i40evf_mac_filter *f, *ftmp;
+ struct i40evf_cloud_filter *cf, *cftmp;
struct i40e_hw *hw = &adapter->hw;
int err;
/* Indicate we are in remove and not to run reset_task */
@@ -3087,6 +3890,7 @@ static void i40evf_remove(struct pci_dev *pdev)
/* Shut down all the garbage mashers on the detention level */
adapter->state = __I40EVF_REMOVE;
adapter->aq_required = 0;
+ adapter->flags &= ~I40EVF_FLAG_REINIT_ITR_NEEDED;
i40evf_request_reset(adapter);
msleep(50);
/* If the FW isn't responding, kick it once, but only once. */
@@ -3127,13 +3931,21 @@ static void i40evf_remove(struct pci_dev *pdev)
list_del(&f->list);
kfree(f);
}
- list_for_each_entry_safe(f, ftmp, &adapter->vlan_filter_list, list) {
- list_del(&f->list);
- kfree(f);
+ list_for_each_entry_safe(vlf, vlftmp, &adapter->vlan_filter_list,
+ list) {
+ list_del(&vlf->list);
+ kfree(vlf);
}
spin_unlock_bh(&adapter->mac_vlan_list_lock);
+ spin_lock_bh(&adapter->cloud_filter_list_lock);
+ list_for_each_entry_safe(cf, cftmp, &adapter->cloud_filter_list, list) {
+ list_del(&cf->list);
+ kfree(cf);
+ }
+ spin_unlock_bh(&adapter->cloud_filter_list_lock);
+
free_netdev(netdev);
pci_disable_pcie_error_reporting(pdev);
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c
index 50ce0d6c09ef..26a59890532f 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c
+++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
@@ -161,7 +162,8 @@ int i40evf_send_vf_config_msg(struct i40evf_adapter *adapter)
VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2 |
VIRTCHNL_VF_OFFLOAD_ENCAP |
VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM |
- VIRTCHNL_VF_OFFLOAD_REQ_QUEUES;
+ VIRTCHNL_VF_OFFLOAD_REQ_QUEUES |
+ VIRTCHNL_VF_OFFLOAD_ADQ;
adapter->current_op = VIRTCHNL_OP_GET_VF_RESOURCES;
adapter->aq_required &= ~I40EVF_FLAG_AQ_GET_CONFIG;
@@ -344,6 +346,7 @@ void i40evf_disable_queues(struct i40evf_adapter *adapter)
void i40evf_map_queues(struct i40evf_adapter *adapter)
{
struct virtchnl_irq_map_info *vimi;
+ struct virtchnl_vector_map *vecmap;
int v_idx, q_vectors, len;
struct i40e_q_vector *q_vector;
@@ -367,17 +370,22 @@ void i40evf_map_queues(struct i40evf_adapter *adapter)
vimi->num_vectors = adapter->num_msix_vectors;
/* Queue vectors first */
for (v_idx = 0; v_idx < q_vectors; v_idx++) {
- q_vector = adapter->q_vectors + v_idx;
- vimi->vecmap[v_idx].vsi_id = adapter->vsi_res->vsi_id;
- vimi->vecmap[v_idx].vector_id = v_idx + NONQ_VECS;
- vimi->vecmap[v_idx].txq_map = q_vector->ring_mask;
- vimi->vecmap[v_idx].rxq_map = q_vector->ring_mask;
+ q_vector = &adapter->q_vectors[v_idx];
+ vecmap = &vimi->vecmap[v_idx];
+
+ vecmap->vsi_id = adapter->vsi_res->vsi_id;
+ vecmap->vector_id = v_idx + NONQ_VECS;
+ vecmap->txq_map = q_vector->ring_mask;
+ vecmap->rxq_map = q_vector->ring_mask;
+ vecmap->rxitr_idx = I40E_RX_ITR;
+ vecmap->txitr_idx = I40E_TX_ITR;
}
/* Misc vector last - this is only for AdminQ messages */
- vimi->vecmap[v_idx].vsi_id = adapter->vsi_res->vsi_id;
- vimi->vecmap[v_idx].vector_id = 0;
- vimi->vecmap[v_idx].txq_map = 0;
- vimi->vecmap[v_idx].rxq_map = 0;
+ vecmap = &vimi->vecmap[v_idx];
+ vecmap->vsi_id = adapter->vsi_res->vsi_id;
+ vecmap->vector_id = 0;
+ vecmap->txq_map = 0;
+ vecmap->rxq_map = 0;
adapter->aq_required &= ~I40EVF_FLAG_AQ_MAP_VECTORS;
i40evf_send_pf_msg(adapter, VIRTCHNL_OP_CONFIG_IRQ_MAP,
@@ -459,7 +467,7 @@ void i40evf_add_ether_addrs(struct i40evf_adapter *adapter)
more = true;
}
- veal = kzalloc(len, GFP_KERNEL);
+ veal = kzalloc(len, GFP_ATOMIC);
if (!veal) {
spin_unlock_bh(&adapter->mac_vlan_list_lock);
return;
@@ -532,7 +540,7 @@ void i40evf_del_ether_addrs(struct i40evf_adapter *adapter)
(count * sizeof(struct virtchnl_ether_addr));
more = true;
}
- veal = kzalloc(len, GFP_KERNEL);
+ veal = kzalloc(len, GFP_ATOMIC);
if (!veal) {
spin_unlock_bh(&adapter->mac_vlan_list_lock);
return;
@@ -606,7 +614,7 @@ void i40evf_add_vlans(struct i40evf_adapter *adapter)
(count * sizeof(u16));
more = true;
}
- vvfl = kzalloc(len, GFP_KERNEL);
+ vvfl = kzalloc(len, GFP_ATOMIC);
if (!vvfl) {
spin_unlock_bh(&adapter->mac_vlan_list_lock);
return;
@@ -678,7 +686,7 @@ void i40evf_del_vlans(struct i40evf_adapter *adapter)
(count * sizeof(u16));
more = true;
}
- vvfl = kzalloc(len, GFP_KERNEL);
+ vvfl = kzalloc(len, GFP_ATOMIC);
if (!vvfl) {
spin_unlock_bh(&adapter->mac_vlan_list_lock);
return;
@@ -967,6 +975,205 @@ static void i40evf_print_link_message(struct i40evf_adapter *adapter)
}
/**
+ * i40evf_enable_channel
+ * @adapter: adapter structure
+ *
+ * Request that the PF enable channels as specified by
+ * the user via tc tool.
+ **/
+void i40evf_enable_channels(struct i40evf_adapter *adapter)
+{
+ struct virtchnl_tc_info *vti = NULL;
+ u16 len;
+ int i;
+
+ if (adapter->current_op != VIRTCHNL_OP_UNKNOWN) {
+ /* bail because we already have a command pending */
+ dev_err(&adapter->pdev->dev, "Cannot configure mqprio, command %d pending\n",
+ adapter->current_op);
+ return;
+ }
+
+ len = (adapter->num_tc * sizeof(struct virtchnl_channel_info)) +
+ sizeof(struct virtchnl_tc_info);
+
+ vti = kzalloc(len, GFP_KERNEL);
+ if (!vti)
+ return;
+ vti->num_tc = adapter->num_tc;
+ for (i = 0; i < vti->num_tc; i++) {
+ vti->list[i].count = adapter->ch_config.ch_info[i].count;
+ vti->list[i].offset = adapter->ch_config.ch_info[i].offset;
+ vti->list[i].pad = 0;
+ vti->list[i].max_tx_rate =
+ adapter->ch_config.ch_info[i].max_tx_rate;
+ }
+
+ adapter->ch_config.state = __I40EVF_TC_RUNNING;
+ adapter->flags |= I40EVF_FLAG_REINIT_ITR_NEEDED;
+ adapter->current_op = VIRTCHNL_OP_ENABLE_CHANNELS;
+ adapter->aq_required &= ~I40EVF_FLAG_AQ_ENABLE_CHANNELS;
+ i40evf_send_pf_msg(adapter, VIRTCHNL_OP_ENABLE_CHANNELS,
+ (u8 *)vti, len);
+ kfree(vti);
+}
+
+/**
+ * i40evf_disable_channel
+ * @adapter: adapter structure
+ *
+ * Request that the PF disable channels that are configured
+ **/
+void i40evf_disable_channels(struct i40evf_adapter *adapter)
+{
+ if (adapter->current_op != VIRTCHNL_OP_UNKNOWN) {
+ /* bail because we already have a command pending */
+ dev_err(&adapter->pdev->dev, "Cannot configure mqprio, command %d pending\n",
+ adapter->current_op);
+ return;
+ }
+
+ adapter->ch_config.state = __I40EVF_TC_INVALID;
+ adapter->flags |= I40EVF_FLAG_REINIT_ITR_NEEDED;
+ adapter->current_op = VIRTCHNL_OP_DISABLE_CHANNELS;
+ adapter->aq_required &= ~I40EVF_FLAG_AQ_DISABLE_CHANNELS;
+ i40evf_send_pf_msg(adapter, VIRTCHNL_OP_DISABLE_CHANNELS,
+ NULL, 0);
+}
+
+/**
+ * i40evf_print_cloud_filter
+ * @adapter: adapter structure
+ * @f: cloud filter to print
+ *
+ * Print the cloud filter
+ **/
+static void i40evf_print_cloud_filter(struct i40evf_adapter *adapter,
+ struct virtchnl_filter *f)
+{
+ switch (f->flow_type) {
+ case VIRTCHNL_TCP_V4_FLOW:
+ dev_info(&adapter->pdev->dev, "dst_mac: %pM src_mac: %pM vlan_id: %hu dst_ip: %pI4 src_ip %pI4 dst_port %hu src_port %hu\n",
+ &f->data.tcp_spec.dst_mac,
+ &f->data.tcp_spec.src_mac,
+ ntohs(f->data.tcp_spec.vlan_id),
+ &f->data.tcp_spec.dst_ip[0],
+ &f->data.tcp_spec.src_ip[0],
+ ntohs(f->data.tcp_spec.dst_port),
+ ntohs(f->data.tcp_spec.src_port));
+ break;
+ case VIRTCHNL_TCP_V6_FLOW:
+ dev_info(&adapter->pdev->dev, "dst_mac: %pM src_mac: %pM vlan_id: %hu dst_ip: %pI6 src_ip %pI6 dst_port %hu src_port %hu\n",
+ &f->data.tcp_spec.dst_mac,
+ &f->data.tcp_spec.src_mac,
+ ntohs(f->data.tcp_spec.vlan_id),
+ &f->data.tcp_spec.dst_ip,
+ &f->data.tcp_spec.src_ip,
+ ntohs(f->data.tcp_spec.dst_port),
+ ntohs(f->data.tcp_spec.src_port));
+ break;
+ }
+}
+
+/**
+ * i40evf_add_cloud_filter
+ * @adapter: adapter structure
+ *
+ * Request that the PF add cloud filters as specified
+ * by the user via tc tool.
+ **/
+void i40evf_add_cloud_filter(struct i40evf_adapter *adapter)
+{
+ struct i40evf_cloud_filter *cf;
+ struct virtchnl_filter *f;
+ int len = 0, count = 0;
+
+ if (adapter->current_op != VIRTCHNL_OP_UNKNOWN) {
+ /* bail because we already have a command pending */
+ dev_err(&adapter->pdev->dev, "Cannot add cloud filter, command %d pending\n",
+ adapter->current_op);
+ return;
+ }
+ list_for_each_entry(cf, &adapter->cloud_filter_list, list) {
+ if (cf->add) {
+ count++;
+ break;
+ }
+ }
+ if (!count) {
+ adapter->aq_required &= ~I40EVF_FLAG_AQ_ADD_CLOUD_FILTER;
+ return;
+ }
+ adapter->current_op = VIRTCHNL_OP_ADD_CLOUD_FILTER;
+
+ len = sizeof(struct virtchnl_filter);
+ f = kzalloc(len, GFP_KERNEL);
+ if (!f)
+ return;
+
+ list_for_each_entry(cf, &adapter->cloud_filter_list, list) {
+ if (cf->add) {
+ memcpy(f, &cf->f, sizeof(struct virtchnl_filter));
+ cf->add = false;
+ cf->state = __I40EVF_CF_ADD_PENDING;
+ i40evf_send_pf_msg(adapter,
+ VIRTCHNL_OP_ADD_CLOUD_FILTER,
+ (u8 *)f, len);
+ }
+ }
+ kfree(f);
+}
+
+/**
+ * i40evf_del_cloud_filter
+ * @adapter: adapter structure
+ *
+ * Request that the PF delete cloud filters as specified
+ * by the user via tc tool.
+ **/
+void i40evf_del_cloud_filter(struct i40evf_adapter *adapter)
+{
+ struct i40evf_cloud_filter *cf, *cftmp;
+ struct virtchnl_filter *f;
+ int len = 0, count = 0;
+
+ if (adapter->current_op != VIRTCHNL_OP_UNKNOWN) {
+ /* bail because we already have a command pending */
+ dev_err(&adapter->pdev->dev, "Cannot remove cloud filter, command %d pending\n",
+ adapter->current_op);
+ return;
+ }
+ list_for_each_entry(cf, &adapter->cloud_filter_list, list) {
+ if (cf->del) {
+ count++;
+ break;
+ }
+ }
+ if (!count) {
+ adapter->aq_required &= ~I40EVF_FLAG_AQ_DEL_CLOUD_FILTER;
+ return;
+ }
+ adapter->current_op = VIRTCHNL_OP_DEL_CLOUD_FILTER;
+
+ len = sizeof(struct virtchnl_filter);
+ f = kzalloc(len, GFP_KERNEL);
+ if (!f)
+ return;
+
+ list_for_each_entry_safe(cf, cftmp, &adapter->cloud_filter_list, list) {
+ if (cf->del) {
+ memcpy(f, &cf->f, sizeof(struct virtchnl_filter));
+ cf->del = false;
+ cf->state = __I40EVF_CF_DEL_PENDING;
+ i40evf_send_pf_msg(adapter,
+ VIRTCHNL_OP_DEL_CLOUD_FILTER,
+ (u8 *)f, len);
+ }
+ }
+ kfree(f);
+}
+
+/**
* i40evf_request_reset
* @adapter: adapter structure
*
@@ -1011,14 +1218,25 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter,
if (adapter->link_up == link_up)
break;
- /* If we get link up message and start queues before
- * our queues are configured it will trigger a TX hang.
- * In that case, just ignore the link status message,
- * we'll get another one after we enable queues and
- * actually prepared to send traffic.
- */
- if (link_up && adapter->state != __I40EVF_RUNNING)
- break;
+ if (link_up) {
+ /* If we get link up message and start queues
+ * before our queues are configured it will
+ * trigger a TX hang. In that case, just ignore
+ * the link status message,we'll get another one
+ * after we enable queues and actually prepared
+ * to send traffic.
+ */
+ if (adapter->state != __I40EVF_RUNNING)
+ break;
+
+ /* For ADq enabled VF, we reconfigure VSIs and
+ * re-allocate queues. Hence wait till all
+ * queues are enabled.
+ */
+ if (adapter->flags &
+ I40EVF_FLAG_QUEUES_DISABLED)
+ break;
+ }
adapter->link_up = link_up;
if (link_up) {
@@ -1031,7 +1249,7 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter,
i40evf_print_link_message(adapter);
break;
case VIRTCHNL_EVENT_RESET_IMPENDING:
- dev_info(&adapter->pdev->dev, "PF reset warning received\n");
+ dev_info(&adapter->pdev->dev, "Reset warning received from the PF\n");
if (!(adapter->flags & I40EVF_FLAG_RESET_PENDING)) {
adapter->flags |= I40EVF_FLAG_RESET_PENDING;
dev_info(&adapter->pdev->dev, "Scheduling reset task\n");
@@ -1063,6 +1281,57 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter,
dev_err(&adapter->pdev->dev, "Failed to delete MAC filter, error %s\n",
i40evf_stat_str(&adapter->hw, v_retval));
break;
+ case VIRTCHNL_OP_ENABLE_CHANNELS:
+ dev_err(&adapter->pdev->dev, "Failed to configure queue channels, error %s\n",
+ i40evf_stat_str(&adapter->hw, v_retval));
+ adapter->flags &= ~I40EVF_FLAG_REINIT_ITR_NEEDED;
+ adapter->ch_config.state = __I40EVF_TC_INVALID;
+ netdev_reset_tc(netdev);
+ netif_tx_start_all_queues(netdev);
+ break;
+ case VIRTCHNL_OP_DISABLE_CHANNELS:
+ dev_err(&adapter->pdev->dev, "Failed to disable queue channels, error %s\n",
+ i40evf_stat_str(&adapter->hw, v_retval));
+ adapter->flags &= ~I40EVF_FLAG_REINIT_ITR_NEEDED;
+ adapter->ch_config.state = __I40EVF_TC_RUNNING;
+ netif_tx_start_all_queues(netdev);
+ break;
+ case VIRTCHNL_OP_ADD_CLOUD_FILTER: {
+ struct i40evf_cloud_filter *cf, *cftmp;
+
+ list_for_each_entry_safe(cf, cftmp,
+ &adapter->cloud_filter_list,
+ list) {
+ if (cf->state == __I40EVF_CF_ADD_PENDING) {
+ cf->state = __I40EVF_CF_INVALID;
+ dev_info(&adapter->pdev->dev, "Failed to add cloud filter, error %s\n",
+ i40evf_stat_str(&adapter->hw,
+ v_retval));
+ i40evf_print_cloud_filter(adapter,
+ &cf->f);
+ list_del(&cf->list);
+ kfree(cf);
+ adapter->num_cloud_filters--;
+ }
+ }
+ }
+ break;
+ case VIRTCHNL_OP_DEL_CLOUD_FILTER: {
+ struct i40evf_cloud_filter *cf;
+
+ list_for_each_entry(cf, &adapter->cloud_filter_list,
+ list) {
+ if (cf->state == __I40EVF_CF_DEL_PENDING) {
+ cf->state = __I40EVF_CF_ACTIVE;
+ dev_info(&adapter->pdev->dev, "Failed to del cloud filter, error %s\n",
+ i40evf_stat_str(&adapter->hw,
+ v_retval));
+ i40evf_print_cloud_filter(adapter,
+ &cf->f);
+ }
+ }
+ }
+ break;
default:
dev_err(&adapter->pdev->dev, "PF returned error %d (%s) to our request %d\n",
v_retval,
@@ -1102,6 +1371,7 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter,
case VIRTCHNL_OP_ENABLE_QUEUES:
/* enable transmits */
i40evf_irq_enable(adapter, true);
+ adapter->flags &= ~I40EVF_FLAG_QUEUES_DISABLED;
break;
case VIRTCHNL_OP_DISABLE_QUEUES:
i40evf_free_all_tx_resources(adapter);
@@ -1156,6 +1426,29 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter,
}
}
break;
+ case VIRTCHNL_OP_ADD_CLOUD_FILTER: {
+ struct i40evf_cloud_filter *cf;
+
+ list_for_each_entry(cf, &adapter->cloud_filter_list, list) {
+ if (cf->state == __I40EVF_CF_ADD_PENDING)
+ cf->state = __I40EVF_CF_ACTIVE;
+ }
+ }
+ break;
+ case VIRTCHNL_OP_DEL_CLOUD_FILTER: {
+ struct i40evf_cloud_filter *cf, *cftmp;
+
+ list_for_each_entry_safe(cf, cftmp, &adapter->cloud_filter_list,
+ list) {
+ if (cf->state == __I40EVF_CF_DEL_PENDING) {
+ cf->state = __I40EVF_CF_INVALID;
+ list_del(&cf->list);
+ kfree(cf);
+ adapter->num_cloud_filters--;
+ }
+ }
+ }
+ break;
default:
if (adapter->current_op && (v_opcode != adapter->current_op))
dev_warn(&adapter->pdev->dev, "Expected response %d from PF, received %d\n",
diff --git a/drivers/net/ethernet/intel/ice/Makefile b/drivers/net/ethernet/intel/ice/Makefile
new file mode 100644
index 000000000000..4058673fd853
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/Makefile
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2018, Intel Corporation.
+
+#
+# Makefile for the Intel(R) Ethernet Connection E800 Series Linux Driver
+#
+
+obj-$(CONFIG_ICE) += ice.o
+
+ice-y := ice_main.o \
+ ice_controlq.o \
+ ice_common.o \
+ ice_nvm.o \
+ ice_switch.o \
+ ice_sched.o \
+ ice_txrx.o \
+ ice_ethtool.o
diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
new file mode 100644
index 000000000000..d8b5fff581e7
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -0,0 +1,312 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018, Intel Corporation. */
+
+#ifndef _ICE_H_
+#define _ICE_H_
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/compiler.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/cpumask.h>
+#include <linux/rtnetlink.h>
+#include <linux/if_vlan.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/workqueue.h>
+#include <linux/aer.h>
+#include <linux/interrupt.h>
+#include <linux/ethtool.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/bitmap.h>
+#include <linux/log2.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/if_bridge.h>
+#include <net/ipv6.h>
+#include "ice_devids.h"
+#include "ice_type.h"
+#include "ice_txrx.h"
+#include "ice_switch.h"
+#include "ice_common.h"
+#include "ice_sched.h"
+
+extern const char ice_drv_ver[];
+#define ICE_BAR0 0
+#define ICE_DFLT_NUM_DESC 128
+#define ICE_MIN_NUM_DESC 8
+#define ICE_MAX_NUM_DESC 8160
+#define ICE_REQ_DESC_MULTIPLE 32
+#define ICE_DFLT_TRAFFIC_CLASS BIT(0)
+#define ICE_INT_NAME_STR_LEN (IFNAMSIZ + 16)
+#define ICE_ETHTOOL_FWVER_LEN 32
+#define ICE_AQ_LEN 64
+#define ICE_MIN_MSIX 2
+#define ICE_NO_VSI 0xffff
+#define ICE_MAX_VSI_ALLOC 130
+#define ICE_MAX_TXQS 2048
+#define ICE_MAX_RXQS 2048
+#define ICE_VSI_MAP_CONTIG 0
+#define ICE_VSI_MAP_SCATTER 1
+#define ICE_MAX_SCATTER_TXQS 16
+#define ICE_MAX_SCATTER_RXQS 16
+#define ICE_Q_WAIT_RETRY_LIMIT 10
+#define ICE_Q_WAIT_MAX_RETRY (5 * ICE_Q_WAIT_RETRY_LIMIT)
+#define ICE_MAX_LG_RSS_QS 256
+#define ICE_MAX_SMALL_RSS_QS 8
+#define ICE_RES_VALID_BIT 0x8000
+#define ICE_RES_MISC_VEC_ID (ICE_RES_VALID_BIT - 1)
+#define ICE_INVAL_Q_INDEX 0xffff
+
+#define ICE_VSIQF_HKEY_ARRAY_SIZE ((VSIQF_HKEY_MAX_INDEX + 1) * 4)
+
+#define ICE_DFLT_NETIF_M (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK)
+
+#define ICE_MAX_MTU (ICE_AQ_SET_MAC_FRAME_SIZE_MAX - \
+ ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN)
+
+#define ICE_UP_TABLE_TRANSLATE(val, i) \
+ (((val) << ICE_AQ_VSI_UP_TABLE_UP##i##_S) & \
+ ICE_AQ_VSI_UP_TABLE_UP##i##_M)
+
+#define ICE_TX_DESC(R, i) (&(((struct ice_tx_desc *)((R)->desc))[i]))
+#define ICE_RX_DESC(R, i) (&(((union ice_32b_rx_flex_desc *)((R)->desc))[i]))
+#define ICE_TX_CTX_DESC(R, i) (&(((struct ice_tx_ctx_desc *)((R)->desc))[i]))
+
+/* Macro for each VSI in a PF */
+#define ice_for_each_vsi(pf, i) \
+ for ((i) = 0; (i) < (pf)->num_alloc_vsi; (i)++)
+
+/* Macros for each tx/rx ring in a VSI */
+#define ice_for_each_txq(vsi, i) \
+ for ((i) = 0; (i) < (vsi)->num_txq; (i)++)
+
+#define ice_for_each_rxq(vsi, i) \
+ for ((i) = 0; (i) < (vsi)->num_rxq; (i)++)
+
+struct ice_tc_info {
+ u16 qoffset;
+ u16 qcount;
+};
+
+struct ice_tc_cfg {
+ u8 numtc; /* Total number of enabled TCs */
+ u8 ena_tc; /* TX map */
+ struct ice_tc_info tc_info[ICE_MAX_TRAFFIC_CLASS];
+};
+
+struct ice_res_tracker {
+ u16 num_entries;
+ u16 search_hint;
+ u16 list[1];
+};
+
+struct ice_sw {
+ struct ice_pf *pf;
+ u16 sw_id; /* switch ID for this switch */
+ u16 bridge_mode; /* VEB/VEPA/Port Virtualizer */
+};
+
+enum ice_state {
+ __ICE_DOWN,
+ __ICE_NEEDS_RESTART,
+ __ICE_RESET_RECOVERY_PENDING, /* set by driver when reset starts */
+ __ICE_PFR_REQ, /* set by driver and peers */
+ __ICE_CORER_REQ, /* set by driver and peers */
+ __ICE_GLOBR_REQ, /* set by driver and peers */
+ __ICE_CORER_RECV, /* set by OICR handler */
+ __ICE_GLOBR_RECV, /* set by OICR handler */
+ __ICE_EMPR_RECV, /* set by OICR handler */
+ __ICE_SUSPENDED, /* set on module remove path */
+ __ICE_RESET_FAILED, /* set by reset/rebuild */
+ __ICE_ADMINQ_EVENT_PENDING,
+ __ICE_FLTR_OVERFLOW_PROMISC,
+ __ICE_CFG_BUSY,
+ __ICE_SERVICE_SCHED,
+ __ICE_STATE_NBITS /* must be last */
+};
+
+enum ice_vsi_flags {
+ ICE_VSI_FLAG_UMAC_FLTR_CHANGED,
+ ICE_VSI_FLAG_MMAC_FLTR_CHANGED,
+ ICE_VSI_FLAG_VLAN_FLTR_CHANGED,
+ ICE_VSI_FLAG_PROMISC_CHANGED,
+ ICE_VSI_FLAG_NBITS /* must be last */
+};
+
+/* struct that defines a VSI, associated with a dev */
+struct ice_vsi {
+ struct net_device *netdev;
+ struct ice_sw *vsw; /* switch this VSI is on */
+ struct ice_pf *back; /* back pointer to PF */
+ struct ice_port_info *port_info; /* back pointer to port_info */
+ struct ice_ring **rx_rings; /* rx ring array */
+ struct ice_ring **tx_rings; /* tx ring array */
+ struct ice_q_vector **q_vectors; /* q_vector array */
+
+ irqreturn_t (*irq_handler)(int irq, void *data);
+
+ u64 tx_linearize;
+ DECLARE_BITMAP(state, __ICE_STATE_NBITS);
+ DECLARE_BITMAP(flags, ICE_VSI_FLAG_NBITS);
+ unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
+ unsigned int current_netdev_flags;
+ u32 tx_restart;
+ u32 tx_busy;
+ u32 rx_buf_failed;
+ u32 rx_page_failed;
+ int num_q_vectors;
+ int base_vector;
+ enum ice_vsi_type type;
+ u16 vsi_num; /* HW (absolute) index of this VSI */
+ u16 idx; /* software index in pf->vsi[] */
+
+ /* Interrupt thresholds */
+ u16 work_lmt;
+
+ /* RSS config */
+ u16 rss_table_size; /* HW RSS table size */
+ u16 rss_size; /* Allocated RSS queues */
+ u8 *rss_hkey_user; /* User configured hash keys */
+ u8 *rss_lut_user; /* User configured lookup table entries */
+ u8 rss_lut_type; /* used to configure Get/Set RSS LUT AQ call */
+
+ u16 max_frame;
+ u16 rx_buf_len;
+
+ struct ice_aqc_vsi_props info; /* VSI properties */
+
+ /* VSI stats */
+ struct rtnl_link_stats64 net_stats;
+ struct ice_eth_stats eth_stats;
+ struct ice_eth_stats eth_stats_prev;
+
+ struct list_head tmp_sync_list; /* MAC filters to be synced */
+ struct list_head tmp_unsync_list; /* MAC filters to be unsynced */
+
+ bool irqs_ready;
+ bool current_isup; /* Sync 'link up' logging */
+ bool stat_offsets_loaded;
+
+ /* queue information */
+ u8 tx_mapping_mode; /* ICE_MAP_MODE_[CONTIG|SCATTER] */
+ u8 rx_mapping_mode; /* ICE_MAP_MODE_[CONTIG|SCATTER] */
+ u16 txq_map[ICE_MAX_TXQS]; /* index in pf->avail_txqs */
+ u16 rxq_map[ICE_MAX_RXQS]; /* index in pf->avail_rxqs */
+ u16 alloc_txq; /* Allocated Tx queues */
+ u16 num_txq; /* Used Tx queues */
+ u16 alloc_rxq; /* Allocated Rx queues */
+ u16 num_rxq; /* Used Rx queues */
+ u16 num_desc;
+ struct ice_tc_cfg tc_cfg;
+} ____cacheline_internodealigned_in_smp;
+
+/* struct that defines an interrupt vector */
+struct ice_q_vector {
+ struct ice_vsi *vsi;
+ cpumask_t affinity_mask;
+ struct napi_struct napi;
+ struct ice_ring_container rx;
+ struct ice_ring_container tx;
+ struct irq_affinity_notify affinity_notify;
+ u16 v_idx; /* index in the vsi->q_vector array. */
+ u8 num_ring_tx; /* total number of tx rings in vector */
+ u8 num_ring_rx; /* total number of rx rings in vector */
+ char name[ICE_INT_NAME_STR_LEN];
+} ____cacheline_internodealigned_in_smp;
+
+enum ice_pf_flags {
+ ICE_FLAG_MSIX_ENA,
+ ICE_FLAG_FLTR_SYNC,
+ ICE_FLAG_RSS_ENA,
+ ICE_PF_FLAGS_NBITS /* must be last */
+};
+
+struct ice_pf {
+ struct pci_dev *pdev;
+ struct msix_entry *msix_entries;
+ struct ice_res_tracker *irq_tracker;
+ struct ice_vsi **vsi; /* VSIs created by the driver */
+ struct ice_sw *first_sw; /* first switch created by firmware */
+ DECLARE_BITMAP(state, __ICE_STATE_NBITS);
+ DECLARE_BITMAP(avail_txqs, ICE_MAX_TXQS);
+ DECLARE_BITMAP(avail_rxqs, ICE_MAX_RXQS);
+ DECLARE_BITMAP(flags, ICE_PF_FLAGS_NBITS);
+ unsigned long serv_tmr_period;
+ unsigned long serv_tmr_prev;
+ struct timer_list serv_tmr;
+ struct work_struct serv_task;
+ struct mutex avail_q_mutex; /* protects access to avail_[rx|tx]qs */
+ struct mutex sw_mutex; /* lock for protecting VSI alloc flow */
+ u32 msg_enable;
+ u32 hw_csum_rx_error;
+ u32 oicr_idx; /* Other interrupt cause vector index */
+ u32 num_lan_msix; /* Total MSIX vectors for base driver */
+ u32 num_avail_msix; /* remaining MSIX vectors left unclaimed */
+ u16 num_lan_tx; /* num lan tx queues setup */
+ u16 num_lan_rx; /* num lan rx queues setup */
+ u16 q_left_tx; /* remaining num tx queues left unclaimed */
+ u16 q_left_rx; /* remaining num rx queues left unclaimed */
+ u16 next_vsi; /* Next free slot in pf->vsi[] - 0-based! */
+ u16 num_alloc_vsi;
+ u16 corer_count; /* Core reset count */
+ u16 globr_count; /* Global reset count */
+ u16 empr_count; /* EMP reset count */
+ u16 pfr_count; /* PF reset count */
+
+ struct ice_hw_port_stats stats;
+ struct ice_hw_port_stats stats_prev;
+ struct ice_hw hw;
+ bool stat_prev_loaded; /* has previous stats been loaded */
+ char int_name[ICE_INT_NAME_STR_LEN];
+};
+
+struct ice_netdev_priv {
+ struct ice_vsi *vsi;
+};
+
+/**
+ * ice_irq_dynamic_ena - Enable default interrupt generation settings
+ * @hw: pointer to hw struct
+ * @vsi: pointer to vsi struct, can be NULL
+ * @q_vector: pointer to q_vector, can be NULL
+ */
+static inline void ice_irq_dynamic_ena(struct ice_hw *hw, struct ice_vsi *vsi,
+ struct ice_q_vector *q_vector)
+{
+ u32 vector = (vsi && q_vector) ? vsi->base_vector + q_vector->v_idx :
+ ((struct ice_pf *)hw->back)->oicr_idx;
+ int itr = ICE_ITR_NONE;
+ u32 val;
+
+ /* clear the PBA here, as this function is meant to clean out all
+ * previous interrupts and enable the interrupt
+ */
+ val = GLINT_DYN_CTL_INTENA_M | GLINT_DYN_CTL_CLEARPBA_M |
+ (itr << GLINT_DYN_CTL_ITR_INDX_S);
+ if (vsi)
+ if (test_bit(__ICE_DOWN, vsi->state))
+ return;
+ wr32(hw, GLINT_DYN_CTL(vector), val);
+}
+
+static inline void ice_vsi_set_tc_cfg(struct ice_vsi *vsi)
+{
+ vsi->tc_cfg.ena_tc = ICE_DFLT_TRAFFIC_CLASS;
+ vsi->tc_cfg.numtc = 1;
+}
+
+void ice_set_ethtool_ops(struct net_device *netdev);
+int ice_up(struct ice_vsi *vsi);
+int ice_down(struct ice_vsi *vsi);
+int ice_set_rss(struct ice_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size);
+int ice_get_rss(struct ice_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size);
+void ice_fill_rss_lut(u8 *lut, u16 rss_table_size, u16 rss_size);
+void ice_print_link_msg(struct ice_vsi *vsi, bool isup);
+
+#endif /* _ICE_H_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
new file mode 100644
index 000000000000..7dc5f045e969
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
@@ -0,0 +1,1352 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018, Intel Corporation. */
+
+#ifndef _ICE_ADMINQ_CMD_H_
+#define _ICE_ADMINQ_CMD_H_
+
+/* This header file defines the Admin Queue commands, error codes and
+ * descriptor format. It is shared between Firmware and Software.
+ */
+
+#define ICE_MAX_VSI 768
+#define ICE_AQC_TOPO_MAX_LEVEL_NUM 0x9
+#define ICE_AQ_SET_MAC_FRAME_SIZE_MAX 9728
+
+struct ice_aqc_generic {
+ __le32 param0;
+ __le32 param1;
+ __le32 addr_high;
+ __le32 addr_low;
+};
+
+/* Get version (direct 0x0001) */
+struct ice_aqc_get_ver {
+ __le32 rom_ver;
+ __le32 fw_build;
+ u8 fw_branch;
+ u8 fw_major;
+ u8 fw_minor;
+ u8 fw_patch;
+ u8 api_branch;
+ u8 api_major;
+ u8 api_minor;
+ u8 api_patch;
+};
+
+/* Queue Shutdown (direct 0x0003) */
+struct ice_aqc_q_shutdown {
+#define ICE_AQC_DRIVER_UNLOADING BIT(0)
+ __le32 driver_unloading;
+ u8 reserved[12];
+};
+
+/* Request resource ownership (direct 0x0008)
+ * Release resource ownership (direct 0x0009)
+ */
+struct ice_aqc_req_res {
+ __le16 res_id;
+#define ICE_AQC_RES_ID_NVM 1
+#define ICE_AQC_RES_ID_SDP 2
+#define ICE_AQC_RES_ID_CHNG_LOCK 3
+#define ICE_AQC_RES_ID_GLBL_LOCK 4
+ __le16 access_type;
+#define ICE_AQC_RES_ACCESS_READ 1
+#define ICE_AQC_RES_ACCESS_WRITE 2
+
+ /* Upon successful completion, FW writes this value and driver is
+ * expected to release resource before timeout. This value is provided
+ * in milliseconds.
+ */
+ __le32 timeout;
+#define ICE_AQ_RES_NVM_READ_DFLT_TIMEOUT_MS 3000
+#define ICE_AQ_RES_NVM_WRITE_DFLT_TIMEOUT_MS 180000
+#define ICE_AQ_RES_CHNG_LOCK_DFLT_TIMEOUT_MS 1000
+#define ICE_AQ_RES_GLBL_LOCK_DFLT_TIMEOUT_MS 3000
+ /* For SDP: pin id of the SDP */
+ __le32 res_number;
+ /* Status is only used for ICE_AQC_RES_ID_GLBL_LOCK */
+ __le16 status;
+#define ICE_AQ_RES_GLBL_SUCCESS 0
+#define ICE_AQ_RES_GLBL_IN_PROG 1
+#define ICE_AQ_RES_GLBL_DONE 2
+ u8 reserved[2];
+};
+
+/* Get function capabilities (indirect 0x000A)
+ * Get device capabilities (indirect 0x000B)
+ */
+struct ice_aqc_list_caps {
+ u8 cmd_flags;
+ u8 pf_index;
+ u8 reserved[2];
+ __le32 count;
+ __le32 addr_high;
+ __le32 addr_low;
+};
+
+/* Device/Function buffer entry, repeated per reported capability */
+struct ice_aqc_list_caps_elem {
+ __le16 cap;
+#define ICE_AQC_CAPS_VSI 0x0017
+#define ICE_AQC_CAPS_RSS 0x0040
+#define ICE_AQC_CAPS_RXQS 0x0041
+#define ICE_AQC_CAPS_TXQS 0x0042
+#define ICE_AQC_CAPS_MSIX 0x0043
+#define ICE_AQC_CAPS_MAX_MTU 0x0047
+
+ u8 major_ver;
+ u8 minor_ver;
+ /* Number of resources described by this capability */
+ __le32 number;
+ /* Only meaningful for some types of resources */
+ __le32 logical_id;
+ /* Only meaningful for some types of resources */
+ __le32 phys_id;
+ __le64 rsvd1;
+ __le64 rsvd2;
+};
+
+/* Manage MAC address, read command - indirect (0x0107)
+ * This struct is also used for the response
+ */
+struct ice_aqc_manage_mac_read {
+ __le16 flags; /* Zeroed by device driver */
+#define ICE_AQC_MAN_MAC_LAN_ADDR_VALID BIT(4)
+#define ICE_AQC_MAN_MAC_SAN_ADDR_VALID BIT(5)
+#define ICE_AQC_MAN_MAC_PORT_ADDR_VALID BIT(6)
+#define ICE_AQC_MAN_MAC_WOL_ADDR_VALID BIT(7)
+#define ICE_AQC_MAN_MAC_READ_S 4
+#define ICE_AQC_MAN_MAC_READ_M (0xF << ICE_AQC_MAN_MAC_READ_S)
+ u8 lport_num;
+ u8 lport_num_valid;
+#define ICE_AQC_MAN_MAC_PORT_NUM_IS_VALID BIT(0)
+ u8 num_addr; /* Used in response */
+ u8 reserved[3];
+ __le32 addr_high;
+ __le32 addr_low;
+};
+
+/* Response buffer format for manage MAC read command */
+struct ice_aqc_manage_mac_read_resp {
+ u8 lport_num;
+ u8 addr_type;
+#define ICE_AQC_MAN_MAC_ADDR_TYPE_LAN 0
+#define ICE_AQC_MAN_MAC_ADDR_TYPE_WOL 1
+ u8 mac_addr[ETH_ALEN];
+};
+
+/* Manage MAC address, write command - direct (0x0108) */
+struct ice_aqc_manage_mac_write {
+ u8 port_num;
+ u8 flags;
+#define ICE_AQC_MAN_MAC_WR_MC_MAG_EN BIT(0)
+#define ICE_AQC_MAN_MAC_WR_WOL_LAA_PFR_KEEP BIT(1)
+#define ICE_AQC_MAN_MAC_WR_S 6
+#define ICE_AQC_MAN_MAC_WR_M (3 << ICE_AQC_MAN_MAC_WR_S)
+#define ICE_AQC_MAN_MAC_UPDATE_LAA 0
+#define ICE_AQC_MAN_MAC_UPDATE_LAA_WOL (BIT(0) << ICE_AQC_MAN_MAC_WR_S)
+ /* High 16 bits of MAC address in big endian order */
+ __be16 sah;
+ /* Low 32 bits of MAC address in big endian order */
+ __be32 sal;
+ __le32 addr_high;
+ __le32 addr_low;
+};
+
+/* Clear PXE Command and response (direct 0x0110) */
+struct ice_aqc_clear_pxe {
+ u8 rx_cnt;
+#define ICE_AQC_CLEAR_PXE_RX_CNT 0x2
+ u8 reserved[15];
+};
+
+/* Get switch configuration (0x0200) */
+struct ice_aqc_get_sw_cfg {
+ /* Reserved for command and copy of request flags for response */
+ __le16 flags;
+ /* First desc in case of command and next_elem in case of response
+ * In case of response, if it is not zero, means all the configuration
+ * was not returned and new command shall be sent with this value in
+ * the 'first desc' field
+ */
+ __le16 element;
+ /* Reserved for command, only used for response */
+ __le16 num_elems;
+ __le16 rsvd;
+ __le32 addr_high;
+ __le32 addr_low;
+};
+
+/* Each entry in the response buffer is of the following type: */
+struct ice_aqc_get_sw_cfg_resp_elem {
+ /* VSI/Port Number */
+ __le16 vsi_port_num;
+#define ICE_AQC_GET_SW_CONF_RESP_VSI_PORT_NUM_S 0
+#define ICE_AQC_GET_SW_CONF_RESP_VSI_PORT_NUM_M \
+ (0x3FF << ICE_AQC_GET_SW_CONF_RESP_VSI_PORT_NUM_S)
+#define ICE_AQC_GET_SW_CONF_RESP_TYPE_S 14
+#define ICE_AQC_GET_SW_CONF_RESP_TYPE_M (0x3 << ICE_AQC_GET_SW_CONF_RESP_TYPE_S)
+#define ICE_AQC_GET_SW_CONF_RESP_PHYS_PORT 0
+#define ICE_AQC_GET_SW_CONF_RESP_VIRT_PORT 1
+#define ICE_AQC_GET_SW_CONF_RESP_VSI 2
+
+ /* SWID VSI/Port belongs to */
+ __le16 swid;
+
+ /* Bit 14..0 : PF/VF number VSI belongs to
+ * Bit 15 : VF indication bit
+ */
+ __le16 pf_vf_num;
+#define ICE_AQC_GET_SW_CONF_RESP_FUNC_NUM_S 0
+#define ICE_AQC_GET_SW_CONF_RESP_FUNC_NUM_M \
+ (0x7FFF << ICE_AQC_GET_SW_CONF_RESP_FUNC_NUM_S)
+#define ICE_AQC_GET_SW_CONF_RESP_IS_VF BIT(15)
+};
+
+/* The response buffer is as follows. Note that the length of the
+ * elements array varies with the length of the command response.
+ */
+struct ice_aqc_get_sw_cfg_resp {
+ struct ice_aqc_get_sw_cfg_resp_elem elements[1];
+};
+
+/* These resource type defines are used for all switch resource
+ * commands where a resource type is required, such as:
+ * Get Resource Allocation command (indirect 0x0204)
+ * Allocate Resources command (indirect 0x0208)
+ * Free Resources command (indirect 0x0209)
+ * Get Allocated Resource Descriptors Command (indirect 0x020A)
+ */
+#define ICE_AQC_RES_TYPE_VSI_LIST_REP 0x03
+#define ICE_AQC_RES_TYPE_VSI_LIST_PRUNE 0x04
+
+/* Allocate Resources command (indirect 0x0208)
+ * Free Resources command (indirect 0x0209)
+ */
+struct ice_aqc_alloc_free_res_cmd {
+ __le16 num_entries; /* Number of Resource entries */
+ u8 reserved[6];
+ __le32 addr_high;
+ __le32 addr_low;
+};
+
+/* Resource descriptor */
+struct ice_aqc_res_elem {
+ union {
+ __le16 sw_resp;
+ __le16 flu_resp;
+ } e;
+};
+
+/* Buffer for Allocate/Free Resources commands */
+struct ice_aqc_alloc_free_res_elem {
+ __le16 res_type; /* Types defined above cmd 0x0204 */
+#define ICE_AQC_RES_TYPE_SHARED_S 7
+#define ICE_AQC_RES_TYPE_SHARED_M (0x1 << ICE_AQC_RES_TYPE_SHARED_S)
+#define ICE_AQC_RES_TYPE_VSI_PRUNE_LIST_S 8
+#define ICE_AQC_RES_TYPE_VSI_PRUNE_LIST_M \
+ (0xF << ICE_AQC_RES_TYPE_VSI_PRUNE_LIST_S)
+ __le16 num_elems;
+ struct ice_aqc_res_elem elem[1];
+};
+
+/* Add VSI (indirect 0x0210)
+ * Update VSI (indirect 0x0211)
+ * Get VSI (indirect 0x0212)
+ * Free VSI (indirect 0x0213)
+ */
+struct ice_aqc_add_get_update_free_vsi {
+ __le16 vsi_num;
+#define ICE_AQ_VSI_NUM_S 0
+#define ICE_AQ_VSI_NUM_M (0x03FF << ICE_AQ_VSI_NUM_S)
+#define ICE_AQ_VSI_IS_VALID BIT(15)
+ __le16 cmd_flags;
+#define ICE_AQ_VSI_KEEP_ALLOC 0x1
+ u8 vf_id;
+ u8 reserved;
+ __le16 vsi_flags;
+#define ICE_AQ_VSI_TYPE_S 0
+#define ICE_AQ_VSI_TYPE_M (0x3 << ICE_AQ_VSI_TYPE_S)
+#define ICE_AQ_VSI_TYPE_VF 0x0
+#define ICE_AQ_VSI_TYPE_VMDQ2 0x1
+#define ICE_AQ_VSI_TYPE_PF 0x2
+#define ICE_AQ_VSI_TYPE_EMP_MNG 0x3
+ __le32 addr_high;
+ __le32 addr_low;
+};
+
+/* Response descriptor for:
+ * Add VSI (indirect 0x0210)
+ * Update VSI (indirect 0x0211)
+ * Free VSI (indirect 0x0213)
+ */
+struct ice_aqc_add_update_free_vsi_resp {
+ __le16 vsi_num;
+ __le16 ext_status;
+ __le16 vsi_used;
+ __le16 vsi_free;
+ __le32 addr_high;
+ __le32 addr_low;
+};
+
+struct ice_aqc_vsi_props {
+ __le16 valid_sections;
+#define ICE_AQ_VSI_PROP_SW_VALID BIT(0)
+#define ICE_AQ_VSI_PROP_SECURITY_VALID BIT(1)
+#define ICE_AQ_VSI_PROP_VLAN_VALID BIT(2)
+#define ICE_AQ_VSI_PROP_OUTER_TAG_VALID BIT(3)
+#define ICE_AQ_VSI_PROP_INGRESS_UP_VALID BIT(4)
+#define ICE_AQ_VSI_PROP_EGRESS_UP_VALID BIT(5)
+#define ICE_AQ_VSI_PROP_RXQ_MAP_VALID BIT(6)
+#define ICE_AQ_VSI_PROP_Q_OPT_VALID BIT(7)
+#define ICE_AQ_VSI_PROP_OUTER_UP_VALID BIT(8)
+#define ICE_AQ_VSI_PROP_FLOW_DIR_VALID BIT(11)
+#define ICE_AQ_VSI_PROP_PASID_VALID BIT(12)
+ /* switch section */
+ u8 sw_id;
+ u8 sw_flags;
+#define ICE_AQ_VSI_SW_FLAG_ALLOW_LB BIT(5)
+#define ICE_AQ_VSI_SW_FLAG_LOCAL_LB BIT(6)
+#define ICE_AQ_VSI_SW_FLAG_SRC_PRUNE BIT(7)
+ u8 sw_flags2;
+#define ICE_AQ_VSI_SW_FLAG_RX_PRUNE_EN_S 0
+#define ICE_AQ_VSI_SW_FLAG_RX_PRUNE_EN_M \
+ (0xF << ICE_AQ_VSI_SW_FLAG_RX_PRUNE_EN_S)
+#define ICE_AQ_VSI_SW_FLAG_RX_VLAN_PRUNE_ENA BIT(0)
+#define ICE_AQ_VSI_SW_FLAG_LAN_ENA BIT(4)
+ u8 veb_stat_id;
+#define ICE_AQ_VSI_SW_VEB_STAT_ID_S 0
+#define ICE_AQ_VSI_SW_VEB_STAT_ID_M (0x1F << ICE_AQ_VSI_SW_VEB_STAT_ID_S)
+#define ICE_AQ_VSI_SW_VEB_STAT_ID_VALID BIT(5)
+ /* security section */
+ u8 sec_flags;
+#define ICE_AQ_VSI_SEC_FLAG_ALLOW_DEST_OVRD BIT(0)
+#define ICE_AQ_VSI_SEC_FLAG_ENA_MAC_ANTI_SPOOF BIT(2)
+#define ICE_AQ_VSI_SEC_TX_PRUNE_ENA_S 4
+#define ICE_AQ_VSI_SEC_TX_PRUNE_ENA_M (0xF << ICE_AQ_VSI_SEC_TX_PRUNE_ENA_S)
+#define ICE_AQ_VSI_SEC_TX_VLAN_PRUNE_ENA BIT(0)
+ u8 sec_reserved;
+ /* VLAN section */
+ __le16 pvid; /* VLANS include priority bits */
+ u8 pvlan_reserved[2];
+ u8 port_vlan_flags;
+#define ICE_AQ_VSI_PVLAN_MODE_S 0
+#define ICE_AQ_VSI_PVLAN_MODE_M (0x3 << ICE_AQ_VSI_PVLAN_MODE_S)
+#define ICE_AQ_VSI_PVLAN_MODE_UNTAGGED 0x1
+#define ICE_AQ_VSI_PVLAN_MODE_TAGGED 0x2
+#define ICE_AQ_VSI_PVLAN_MODE_ALL 0x3
+#define ICE_AQ_VSI_PVLAN_INSERT_PVID BIT(2)
+#define ICE_AQ_VSI_PVLAN_EMOD_S 3
+#define ICE_AQ_VSI_PVLAN_EMOD_M (0x3 << ICE_AQ_VSI_PVLAN_EMOD_S)
+#define ICE_AQ_VSI_PVLAN_EMOD_STR_BOTH (0x0 << ICE_AQ_VSI_PVLAN_EMOD_S)
+#define ICE_AQ_VSI_PVLAN_EMOD_STR_UP (0x1 << ICE_AQ_VSI_PVLAN_EMOD_S)
+#define ICE_AQ_VSI_PVLAN_EMOD_STR (0x2 << ICE_AQ_VSI_PVLAN_EMOD_S)
+#define ICE_AQ_VSI_PVLAN_EMOD_NOTHING (0x3 << ICE_AQ_VSI_PVLAN_EMOD_S)
+ u8 pvlan_reserved2[3];
+ /* ingress egress up sections */
+ __le32 ingress_table; /* bitmap, 3 bits per up */
+#define ICE_AQ_VSI_UP_TABLE_UP0_S 0
+#define ICE_AQ_VSI_UP_TABLE_UP0_M (0x7 << ICE_AQ_VSI_UP_TABLE_UP0_S)
+#define ICE_AQ_VSI_UP_TABLE_UP1_S 3
+#define ICE_AQ_VSI_UP_TABLE_UP1_M (0x7 << ICE_AQ_VSI_UP_TABLE_UP1_S)
+#define ICE_AQ_VSI_UP_TABLE_UP2_S 6
+#define ICE_AQ_VSI_UP_TABLE_UP2_M (0x7 << ICE_AQ_VSI_UP_TABLE_UP2_S)
+#define ICE_AQ_VSI_UP_TABLE_UP3_S 9
+#define ICE_AQ_VSI_UP_TABLE_UP3_M (0x7 << ICE_AQ_VSI_UP_TABLE_UP3_S)
+#define ICE_AQ_VSI_UP_TABLE_UP4_S 12
+#define ICE_AQ_VSI_UP_TABLE_UP4_M (0x7 << ICE_AQ_VSI_UP_TABLE_UP4_S)
+#define ICE_AQ_VSI_UP_TABLE_UP5_S 15
+#define ICE_AQ_VSI_UP_TABLE_UP5_M (0x7 << ICE_AQ_VSI_UP_TABLE_UP5_S)
+#define ICE_AQ_VSI_UP_TABLE_UP6_S 18
+#define ICE_AQ_VSI_UP_TABLE_UP6_M (0x7 << ICE_AQ_VSI_UP_TABLE_UP6_S)
+#define ICE_AQ_VSI_UP_TABLE_UP7_S 21
+#define ICE_AQ_VSI_UP_TABLE_UP7_M (0x7 << ICE_AQ_VSI_UP_TABLE_UP7_S)
+ __le32 egress_table; /* same defines as for ingress table */
+ /* outer tags section */
+ __le16 outer_tag;
+ u8 outer_tag_flags;
+#define ICE_AQ_VSI_OUTER_TAG_MODE_S 0
+#define ICE_AQ_VSI_OUTER_TAG_MODE_M (0x3 << ICE_AQ_VSI_OUTER_TAG_MODE_S)
+#define ICE_AQ_VSI_OUTER_TAG_NOTHING 0x0
+#define ICE_AQ_VSI_OUTER_TAG_REMOVE 0x1
+#define ICE_AQ_VSI_OUTER_TAG_COPY 0x2
+#define ICE_AQ_VSI_OUTER_TAG_TYPE_S 2
+#define ICE_AQ_VSI_OUTER_TAG_TYPE_M (0x3 << ICE_AQ_VSI_OUTER_TAG_TYPE_S)
+#define ICE_AQ_VSI_OUTER_TAG_NONE 0x0
+#define ICE_AQ_VSI_OUTER_TAG_STAG 0x1
+#define ICE_AQ_VSI_OUTER_TAG_VLAN_8100 0x2
+#define ICE_AQ_VSI_OUTER_TAG_VLAN_9100 0x3
+#define ICE_AQ_VSI_OUTER_TAG_INSERT BIT(4)
+#define ICE_AQ_VSI_OUTER_TAG_ACCEPT_HOST BIT(6)
+ u8 outer_tag_reserved;
+ /* queue mapping section */
+ __le16 mapping_flags;
+#define ICE_AQ_VSI_Q_MAP_CONTIG 0x0
+#define ICE_AQ_VSI_Q_MAP_NONCONTIG BIT(0)
+ __le16 q_mapping[16];
+#define ICE_AQ_VSI_Q_S 0
+#define ICE_AQ_VSI_Q_M (0x7FF << ICE_AQ_VSI_Q_S)
+ __le16 tc_mapping[8];
+#define ICE_AQ_VSI_TC_Q_OFFSET_S 0
+#define ICE_AQ_VSI_TC_Q_OFFSET_M (0x7FF << ICE_AQ_VSI_TC_Q_OFFSET_S)
+#define ICE_AQ_VSI_TC_Q_NUM_S 11
+#define ICE_AQ_VSI_TC_Q_NUM_M (0xF << ICE_AQ_VSI_TC_Q_NUM_S)
+ /* queueing option section */
+ u8 q_opt_rss;
+#define ICE_AQ_VSI_Q_OPT_RSS_LUT_S 0
+#define ICE_AQ_VSI_Q_OPT_RSS_LUT_M (0x3 << ICE_AQ_VSI_Q_OPT_RSS_LUT_S)
+#define ICE_AQ_VSI_Q_OPT_RSS_LUT_VSI 0x0
+#define ICE_AQ_VSI_Q_OPT_RSS_LUT_PF 0x2
+#define ICE_AQ_VSI_Q_OPT_RSS_LUT_GBL 0x3
+#define ICE_AQ_VSI_Q_OPT_RSS_GBL_LUT_S 2
+#define ICE_AQ_VSI_Q_OPT_RSS_GBL_LUT_M (0xF << ICE_AQ_VSI_Q_OPT_RSS_GBL_LUT_S)
+#define ICE_AQ_VSI_Q_OPT_RSS_HASH_S 6
+#define ICE_AQ_VSI_Q_OPT_RSS_HASH_M (0x3 << ICE_AQ_VSI_Q_OPT_RSS_HASH_S)
+#define ICE_AQ_VSI_Q_OPT_RSS_TPLZ (0x0 << ICE_AQ_VSI_Q_OPT_RSS_HASH_S)
+#define ICE_AQ_VSI_Q_OPT_RSS_SYM_TPLZ (0x1 << ICE_AQ_VSI_Q_OPT_RSS_HASH_S)
+#define ICE_AQ_VSI_Q_OPT_RSS_XOR (0x2 << ICE_AQ_VSI_Q_OPT_RSS_HASH_S)
+#define ICE_AQ_VSI_Q_OPT_RSS_JHASH (0x3 << ICE_AQ_VSI_Q_OPT_RSS_HASH_S)
+ u8 q_opt_tc;
+#define ICE_AQ_VSI_Q_OPT_TC_OVR_S 0
+#define ICE_AQ_VSI_Q_OPT_TC_OVR_M (0x1F << ICE_AQ_VSI_Q_OPT_TC_OVR_S)
+#define ICE_AQ_VSI_Q_OPT_PROF_TC_OVR BIT(7)
+ u8 q_opt_flags;
+#define ICE_AQ_VSI_Q_OPT_PE_FLTR_EN BIT(0)
+ u8 q_opt_reserved[3];
+ /* outer up section */
+ __le32 outer_up_table; /* same structure and defines as ingress tbl */
+ /* section 10 */
+ __le16 sect_10_reserved;
+ /* flow director section */
+ __le16 fd_options;
+#define ICE_AQ_VSI_FD_ENABLE BIT(0)
+#define ICE_AQ_VSI_FD_TX_AUTO_ENABLE BIT(1)
+#define ICE_AQ_VSI_FD_PROG_ENABLE BIT(3)
+ __le16 max_fd_fltr_dedicated;
+ __le16 max_fd_fltr_shared;
+ __le16 fd_def_q;
+#define ICE_AQ_VSI_FD_DEF_Q_S 0
+#define ICE_AQ_VSI_FD_DEF_Q_M (0x7FF << ICE_AQ_VSI_FD_DEF_Q_S)
+#define ICE_AQ_VSI_FD_DEF_GRP_S 12
+#define ICE_AQ_VSI_FD_DEF_GRP_M (0x7 << ICE_AQ_VSI_FD_DEF_GRP_S)
+ __le16 fd_report_opt;
+#define ICE_AQ_VSI_FD_REPORT_Q_S 0
+#define ICE_AQ_VSI_FD_REPORT_Q_M (0x7FF << ICE_AQ_VSI_FD_REPORT_Q_S)
+#define ICE_AQ_VSI_FD_DEF_PRIORITY_S 12
+#define ICE_AQ_VSI_FD_DEF_PRIORITY_M (0x7 << ICE_AQ_VSI_FD_DEF_PRIORITY_S)
+#define ICE_AQ_VSI_FD_DEF_DROP BIT(15)
+ /* PASID section */
+ __le32 pasid_id;
+#define ICE_AQ_VSI_PASID_ID_S 0
+#define ICE_AQ_VSI_PASID_ID_M (0xFFFFF << ICE_AQ_VSI_PASID_ID_S)
+#define ICE_AQ_VSI_PASID_ID_VALID BIT(31)
+ u8 reserved[24];
+};
+
+/* Add/Update/Remove/Get switch rules (indirect 0x02A0, 0x02A1, 0x02A2, 0x02A3)
+ */
+struct ice_aqc_sw_rules {
+ /* ops: add switch rules, referring the number of rules.
+ * ops: update switch rules, referring the number of filters
+ * ops: remove switch rules, referring the entry index.
+ * ops: get switch rules, referring to the number of filters.
+ */
+ __le16 num_rules_fltr_entry_index;
+ u8 reserved[6];
+ __le32 addr_high;
+ __le32 addr_low;
+};
+
+/* Add/Update/Get/Remove lookup Rx/Tx command/response entry
+ * This structures describes the lookup rules and associated actions. "index"
+ * is returned as part of a response to a successful Add command, and can be
+ * used to identify the rule for Update/Get/Remove commands.
+ */
+struct ice_sw_rule_lkup_rx_tx {
+ __le16 recipe_id;
+#define ICE_SW_RECIPE_LOGICAL_PORT_FWD 10
+ /* Source port for LOOKUP_RX and source VSI in case of LOOKUP_TX */
+ __le16 src;
+ __le32 act;
+
+ /* Bit 0:1 - Action type */
+#define ICE_SINGLE_ACT_TYPE_S 0x00
+#define ICE_SINGLE_ACT_TYPE_M (0x3 << ICE_SINGLE_ACT_TYPE_S)
+
+ /* Bit 2 - Loop back enable
+ * Bit 3 - LAN enable
+ */
+#define ICE_SINGLE_ACT_LB_ENABLE BIT(2)
+#define ICE_SINGLE_ACT_LAN_ENABLE BIT(3)
+
+ /* Action type = 0 - Forward to VSI or VSI list */
+#define ICE_SINGLE_ACT_VSI_FORWARDING 0x0
+
+#define ICE_SINGLE_ACT_VSI_ID_S 4
+#define ICE_SINGLE_ACT_VSI_ID_M (0x3FF << ICE_SINGLE_ACT_VSI_ID_S)
+#define ICE_SINGLE_ACT_VSI_LIST_ID_S 4
+#define ICE_SINGLE_ACT_VSI_LIST_ID_M (0x3FF << ICE_SINGLE_ACT_VSI_LIST_ID_S)
+ /* This bit needs to be set if action is forward to VSI list */
+#define ICE_SINGLE_ACT_VSI_LIST BIT(14)
+#define ICE_SINGLE_ACT_VALID_BIT BIT(17)
+#define ICE_SINGLE_ACT_DROP BIT(18)
+
+ /* Action type = 1 - Forward to Queue of Queue group */
+#define ICE_SINGLE_ACT_TO_Q 0x1
+#define ICE_SINGLE_ACT_Q_INDEX_S 4
+#define ICE_SINGLE_ACT_Q_INDEX_M (0x7FF << ICE_SINGLE_ACT_Q_INDEX_S)
+#define ICE_SINGLE_ACT_Q_REGION_S 15
+#define ICE_SINGLE_ACT_Q_REGION_M (0x7 << ICE_SINGLE_ACT_Q_REGION_S)
+#define ICE_SINGLE_ACT_Q_PRIORITY BIT(18)
+
+ /* Action type = 2 - Prune */
+#define ICE_SINGLE_ACT_PRUNE 0x2
+#define ICE_SINGLE_ACT_EGRESS BIT(15)
+#define ICE_SINGLE_ACT_INGRESS BIT(16)
+#define ICE_SINGLE_ACT_PRUNET BIT(17)
+ /* Bit 18 should be set to 0 for this action */
+
+ /* Action type = 2 - Pointer */
+#define ICE_SINGLE_ACT_PTR 0x2
+#define ICE_SINGLE_ACT_PTR_VAL_S 4
+#define ICE_SINGLE_ACT_PTR_VAL_M (0x1FFF << ICE_SINGLE_ACT_PTR_VAL_S)
+ /* Bit 18 should be set to 1 */
+#define ICE_SINGLE_ACT_PTR_BIT BIT(18)
+
+ /* Action type = 3 - Other actions. Last two bits
+ * are other action identifier
+ */
+#define ICE_SINGLE_ACT_OTHER_ACTS 0x3
+#define ICE_SINGLE_OTHER_ACT_IDENTIFIER_S 17
+#define ICE_SINGLE_OTHER_ACT_IDENTIFIER_M \
+ (0x3 << \ ICE_SINGLE_OTHER_ACT_IDENTIFIER_S)
+
+ /* Bit 17:18 - Defines other actions */
+ /* Other action = 0 - Mirror VSI */
+#define ICE_SINGLE_OTHER_ACT_MIRROR 0
+#define ICE_SINGLE_ACT_MIRROR_VSI_ID_S 4
+#define ICE_SINGLE_ACT_MIRROR_VSI_ID_M \
+ (0x3FF << ICE_SINGLE_ACT_MIRROR_VSI_ID_S)
+
+ /* Other action = 3 - Set Stat count */
+#define ICE_SINGLE_OTHER_ACT_STAT_COUNT 3
+#define ICE_SINGLE_ACT_STAT_COUNT_INDEX_S 4
+#define ICE_SINGLE_ACT_STAT_COUNT_INDEX_M \
+ (0x7F << ICE_SINGLE_ACT_STAT_COUNT_INDEX_S)
+
+ __le16 index; /* The index of the rule in the lookup table */
+ /* Length and values of the header to be matched per recipe or
+ * lookup-type
+ */
+ __le16 hdr_len;
+ u8 hdr[1];
+} __packed;
+
+/* Add/Update/Remove large action command/response entry
+ * "index" is returned as part of a response to a successful Add command, and
+ * can be used to identify the action for Update/Get/Remove commands.
+ */
+struct ice_sw_rule_lg_act {
+ __le16 index; /* Index in large action table */
+ __le16 size;
+ __le32 act[1]; /* array of size for actions */
+ /* Max number of large actions */
+#define ICE_MAX_LG_ACT 4
+ /* Bit 0:1 - Action type */
+#define ICE_LG_ACT_TYPE_S 0
+#define ICE_LG_ACT_TYPE_M (0x7 << ICE_LG_ACT_TYPE_S)
+
+ /* Action type = 0 - Forward to VSI or VSI list */
+#define ICE_LG_ACT_VSI_FORWARDING 0
+#define ICE_LG_ACT_VSI_ID_S 3
+#define ICE_LG_ACT_VSI_ID_M (0x3FF << ICE_LG_ACT_VSI_ID_S)
+#define ICE_LG_ACT_VSI_LIST_ID_S 3
+#define ICE_LG_ACT_VSI_LIST_ID_M (0x3FF << ICE_LG_ACT_VSI_LIST_ID_S)
+ /* This bit needs to be set if action is forward to VSI list */
+#define ICE_LG_ACT_VSI_LIST BIT(13)
+
+#define ICE_LG_ACT_VALID_BIT BIT(16)
+
+ /* Action type = 1 - Forward to Queue of Queue group */
+#define ICE_LG_ACT_TO_Q 0x1
+#define ICE_LG_ACT_Q_INDEX_S 3
+#define ICE_LG_ACT_Q_INDEX_M (0x7FF << ICE_LG_ACT_Q_INDEX_S)
+#define ICE_LG_ACT_Q_REGION_S 14
+#define ICE_LG_ACT_Q_REGION_M (0x7 << ICE_LG_ACT_Q_REGION_S)
+#define ICE_LG_ACT_Q_PRIORITY_SET BIT(17)
+
+ /* Action type = 2 - Prune */
+#define ICE_LG_ACT_PRUNE 0x2
+#define ICE_LG_ACT_EGRESS BIT(14)
+#define ICE_LG_ACT_INGRESS BIT(15)
+#define ICE_LG_ACT_PRUNET BIT(16)
+
+ /* Action type = 3 - Mirror VSI */
+#define ICE_LG_OTHER_ACT_MIRROR 0x3
+#define ICE_LG_ACT_MIRROR_VSI_ID_S 3
+#define ICE_LG_ACT_MIRROR_VSI_ID_M (0x3FF << ICE_LG_ACT_MIRROR_VSI_ID_S)
+
+ /* Action type = 5 - Generic Value */
+#define ICE_LG_ACT_GENERIC 0x5
+#define ICE_LG_ACT_GENERIC_VALUE_S 3
+#define ICE_LG_ACT_GENERIC_VALUE_M (0xFFFF << ICE_LG_ACT_GENERIC_VALUE_S)
+#define ICE_LG_ACT_GENERIC_OFFSET_S 19
+#define ICE_LG_ACT_GENERIC_OFFSET_M (0x7 << ICE_LG_ACT_GENERIC_OFFSET_S)
+#define ICE_LG_ACT_GENERIC_PRIORITY_S 22
+#define ICE_LG_ACT_GENERIC_PRIORITY_M (0x7 << ICE_LG_ACT_GENERIC_PRIORITY_S)
+
+ /* Action = 7 - Set Stat count */
+#define ICE_LG_ACT_STAT_COUNT 0x7
+#define ICE_LG_ACT_STAT_COUNT_S 3
+#define ICE_LG_ACT_STAT_COUNT_M (0x7F << ICE_LG_ACT_STAT_COUNT_S)
+};
+
+/* Add/Update/Remove VSI list command/response entry
+ * "index" is returned as part of a response to a successful Add command, and
+ * can be used to identify the VSI list for Update/Get/Remove commands.
+ */
+struct ice_sw_rule_vsi_list {
+ __le16 index; /* Index of VSI/Prune list */
+ __le16 number_vsi;
+ __le16 vsi[1]; /* Array of number_vsi VSI numbers */
+};
+
+/* Query VSI list command/response entry */
+struct ice_sw_rule_vsi_list_query {
+ __le16 index;
+ DECLARE_BITMAP(vsi_list, ICE_MAX_VSI);
+} __packed;
+
+/* Add switch rule response:
+ * Content of return buffer is same as the input buffer. The status field and
+ * LUT index are updated as part of the response
+ */
+struct ice_aqc_sw_rules_elem {
+ __le16 type; /* Switch rule type, one of T_... */
+#define ICE_AQC_SW_RULES_T_LKUP_RX 0x0
+#define ICE_AQC_SW_RULES_T_LKUP_TX 0x1
+#define ICE_AQC_SW_RULES_T_LG_ACT 0x2
+#define ICE_AQC_SW_RULES_T_VSI_LIST_SET 0x3
+#define ICE_AQC_SW_RULES_T_VSI_LIST_CLEAR 0x4
+#define ICE_AQC_SW_RULES_T_PRUNE_LIST_SET 0x5
+#define ICE_AQC_SW_RULES_T_PRUNE_LIST_CLEAR 0x6
+ __le16 status;
+ union {
+ struct ice_sw_rule_lkup_rx_tx lkup_tx_rx;
+ struct ice_sw_rule_lg_act lg_act;
+ struct ice_sw_rule_vsi_list vsi_list;
+ struct ice_sw_rule_vsi_list_query vsi_list_query;
+ } __packed pdata;
+};
+
+/* Get Default Topology (indirect 0x0400) */
+struct ice_aqc_get_topo {
+ u8 port_num;
+ u8 num_branches;
+ __le16 reserved1;
+ __le32 reserved2;
+ __le32 addr_high;
+ __le32 addr_low;
+};
+
+/* Update TSE (indirect 0x0403)
+ * Get TSE (indirect 0x0404)
+ */
+struct ice_aqc_get_cfg_elem {
+ __le16 num_elem_req; /* Used by commands */
+ __le16 num_elem_resp; /* Used by responses */
+ __le32 reserved;
+ __le32 addr_high;
+ __le32 addr_low;
+};
+
+/* This is the buffer for:
+ * Suspend Nodes (indirect 0x0409)
+ * Resume Nodes (indirect 0x040A)
+ */
+struct ice_aqc_suspend_resume_elem {
+ __le32 teid[1];
+};
+
+/* Add TSE (indirect 0x0401)
+ * Delete TSE (indirect 0x040F)
+ * Move TSE (indirect 0x0408)
+ */
+struct ice_aqc_add_move_delete_elem {
+ __le16 num_grps_req;
+ __le16 num_grps_updated;
+ __le32 reserved;
+ __le32 addr_high;
+ __le32 addr_low;
+};
+
+struct ice_aqc_elem_info_bw {
+ __le16 bw_profile_idx;
+ __le16 bw_alloc;
+};
+
+struct ice_aqc_txsched_elem {
+ u8 elem_type; /* Special field, reserved for some aq calls */
+#define ICE_AQC_ELEM_TYPE_UNDEFINED 0x0
+#define ICE_AQC_ELEM_TYPE_ROOT_PORT 0x1
+#define ICE_AQC_ELEM_TYPE_TC 0x2
+#define ICE_AQC_ELEM_TYPE_SE_GENERIC 0x3
+#define ICE_AQC_ELEM_TYPE_ENTRY_POINT 0x4
+#define ICE_AQC_ELEM_TYPE_LEAF 0x5
+#define ICE_AQC_ELEM_TYPE_SE_PADDED 0x6
+ u8 valid_sections;
+#define ICE_AQC_ELEM_VALID_GENERIC BIT(0)
+#define ICE_AQC_ELEM_VALID_CIR BIT(1)
+#define ICE_AQC_ELEM_VALID_EIR BIT(2)
+#define ICE_AQC_ELEM_VALID_SHARED BIT(3)
+ u8 generic;
+#define ICE_AQC_ELEM_GENERIC_MODE_M 0x1
+#define ICE_AQC_ELEM_GENERIC_PRIO_S 0x1
+#define ICE_AQC_ELEM_GENERIC_PRIO_M (0x7 << ICE_AQC_ELEM_GENERIC_PRIO_S)
+#define ICE_AQC_ELEM_GENERIC_SP_S 0x4
+#define ICE_AQC_ELEM_GENERIC_SP_M (0x1 << ICE_AQC_ELEM_GENERIC_SP_S)
+#define ICE_AQC_ELEM_GENERIC_ADJUST_VAL_S 0x5
+#define ICE_AQC_ELEM_GENERIC_ADJUST_VAL_M \
+ (0x3 << ICE_AQC_ELEM_GENERIC_ADJUST_VAL_S)
+ u8 flags; /* Special field, reserved for some aq calls */
+#define ICE_AQC_ELEM_FLAG_SUSPEND_M 0x1
+ struct ice_aqc_elem_info_bw cir_bw;
+ struct ice_aqc_elem_info_bw eir_bw;
+ __le16 srl_id;
+ __le16 reserved2;
+};
+
+struct ice_aqc_txsched_elem_data {
+ __le32 parent_teid;
+ __le32 node_teid;
+ struct ice_aqc_txsched_elem data;
+};
+
+struct ice_aqc_txsched_topo_grp_info_hdr {
+ __le32 parent_teid;
+ __le16 num_elems;
+ __le16 reserved2;
+};
+
+struct ice_aqc_add_elem {
+ struct ice_aqc_txsched_topo_grp_info_hdr hdr;
+ struct ice_aqc_txsched_elem_data generic[1];
+};
+
+struct ice_aqc_get_topo_elem {
+ struct ice_aqc_txsched_topo_grp_info_hdr hdr;
+ struct ice_aqc_txsched_elem_data
+ generic[ICE_AQC_TOPO_MAX_LEVEL_NUM];
+};
+
+struct ice_aqc_delete_elem {
+ struct ice_aqc_txsched_topo_grp_info_hdr hdr;
+ __le32 teid[1];
+};
+
+/* Query Scheduler Resource Allocation (indirect 0x0412)
+ * This indirect command retrieves the scheduler resources allocated by
+ * EMP Firmware to the given PF.
+ */
+struct ice_aqc_query_txsched_res {
+ u8 reserved[8];
+ __le32 addr_high;
+ __le32 addr_low;
+};
+
+struct ice_aqc_generic_sched_props {
+ __le16 phys_levels;
+ __le16 logical_levels;
+ u8 flattening_bitmap;
+ u8 max_device_cgds;
+ u8 max_pf_cgds;
+ u8 rsvd0;
+ __le16 rdma_qsets;
+ u8 rsvd1[22];
+};
+
+struct ice_aqc_layer_props {
+ u8 logical_layer;
+ u8 chunk_size;
+ __le16 max_device_nodes;
+ __le16 max_pf_nodes;
+ u8 rsvd0[2];
+ __le16 max_shared_rate_lmtr;
+ __le16 max_children;
+ __le16 max_cir_rl_profiles;
+ __le16 max_eir_rl_profiles;
+ __le16 max_srl_profiles;
+ u8 rsvd1[14];
+};
+
+struct ice_aqc_query_txsched_res_resp {
+ struct ice_aqc_generic_sched_props sched_props;
+ struct ice_aqc_layer_props layer_props[ICE_AQC_TOPO_MAX_LEVEL_NUM];
+};
+
+/* Get PHY capabilities (indirect 0x0600) */
+struct ice_aqc_get_phy_caps {
+ u8 lport_num;
+ u8 reserved;
+ __le16 param0;
+ /* 18.0 - Report qualified modules */
+#define ICE_AQC_GET_PHY_RQM BIT(0)
+ /* 18.1 - 18.2 : Report mode
+ * 00b - Report NVM capabilities
+ * 01b - Report topology capabilities
+ * 10b - Report SW configured
+ */
+#define ICE_AQC_REPORT_MODE_S 1
+#define ICE_AQC_REPORT_MODE_M (3 << ICE_AQC_REPORT_MODE_S)
+#define ICE_AQC_REPORT_NVM_CAP 0
+#define ICE_AQC_REPORT_TOPO_CAP BIT(1)
+#define ICE_AQC_REPORT_SW_CFG BIT(2)
+ __le32 reserved1;
+ __le32 addr_high;
+ __le32 addr_low;
+};
+
+/* This is #define of PHY type (Extended):
+ * The first set of defines is for phy_type_low.
+ */
+#define ICE_PHY_TYPE_LOW_100BASE_TX BIT_ULL(0)
+#define ICE_PHY_TYPE_LOW_100M_SGMII BIT_ULL(1)
+#define ICE_PHY_TYPE_LOW_1000BASE_T BIT_ULL(2)
+#define ICE_PHY_TYPE_LOW_1000BASE_SX BIT_ULL(3)
+#define ICE_PHY_TYPE_LOW_1000BASE_LX BIT_ULL(4)
+#define ICE_PHY_TYPE_LOW_1000BASE_KX BIT_ULL(5)
+#define ICE_PHY_TYPE_LOW_1G_SGMII BIT_ULL(6)
+#define ICE_PHY_TYPE_LOW_2500BASE_T BIT_ULL(7)
+#define ICE_PHY_TYPE_LOW_2500BASE_X BIT_ULL(8)
+#define ICE_PHY_TYPE_LOW_2500BASE_KX BIT_ULL(9)
+#define ICE_PHY_TYPE_LOW_5GBASE_T BIT_ULL(10)
+#define ICE_PHY_TYPE_LOW_5GBASE_KR BIT_ULL(11)
+#define ICE_PHY_TYPE_LOW_10GBASE_T BIT_ULL(12)
+#define ICE_PHY_TYPE_LOW_10G_SFI_DA BIT_ULL(13)
+#define ICE_PHY_TYPE_LOW_10GBASE_SR BIT_ULL(14)
+#define ICE_PHY_TYPE_LOW_10GBASE_LR BIT_ULL(15)
+#define ICE_PHY_TYPE_LOW_10GBASE_KR_CR1 BIT_ULL(16)
+#define ICE_PHY_TYPE_LOW_10G_SFI_AOC_ACC BIT_ULL(17)
+#define ICE_PHY_TYPE_LOW_10G_SFI_C2C BIT_ULL(18)
+#define ICE_PHY_TYPE_LOW_25GBASE_T BIT_ULL(19)
+#define ICE_PHY_TYPE_LOW_25GBASE_CR BIT_ULL(20)
+#define ICE_PHY_TYPE_LOW_25GBASE_CR_S BIT_ULL(21)
+#define ICE_PHY_TYPE_LOW_25GBASE_CR1 BIT_ULL(22)
+#define ICE_PHY_TYPE_LOW_25GBASE_SR BIT_ULL(23)
+#define ICE_PHY_TYPE_LOW_25GBASE_LR BIT_ULL(24)
+#define ICE_PHY_TYPE_LOW_25GBASE_KR BIT_ULL(25)
+#define ICE_PHY_TYPE_LOW_25GBASE_KR_S BIT_ULL(26)
+#define ICE_PHY_TYPE_LOW_25GBASE_KR1 BIT_ULL(27)
+#define ICE_PHY_TYPE_LOW_25G_AUI_AOC_ACC BIT_ULL(28)
+#define ICE_PHY_TYPE_LOW_25G_AUI_C2C BIT_ULL(29)
+#define ICE_PHY_TYPE_LOW_40GBASE_CR4 BIT_ULL(30)
+#define ICE_PHY_TYPE_LOW_40GBASE_SR4 BIT_ULL(31)
+#define ICE_PHY_TYPE_LOW_40GBASE_LR4 BIT_ULL(32)
+#define ICE_PHY_TYPE_LOW_40GBASE_KR4 BIT_ULL(33)
+#define ICE_PHY_TYPE_LOW_40G_XLAUI_AOC_ACC BIT_ULL(34)
+#define ICE_PHY_TYPE_LOW_40G_XLAUI BIT_ULL(35)
+#define ICE_PHY_TYPE_LOW_MAX_INDEX 63
+
+struct ice_aqc_get_phy_caps_data {
+ __le64 phy_type_low; /* Use values from ICE_PHY_TYPE_LOW_* */
+ __le64 reserved;
+ u8 caps;
+#define ICE_AQC_PHY_EN_TX_LINK_PAUSE BIT(0)
+#define ICE_AQC_PHY_EN_RX_LINK_PAUSE BIT(1)
+#define ICE_AQC_PHY_LOW_POWER_MODE BIT(2)
+#define ICE_AQC_PHY_EN_LINK BIT(3)
+#define ICE_AQC_PHY_AN_MODE BIT(4)
+#define ICE_AQC_GET_PHY_EN_MOD_QUAL BIT(5)
+ u8 low_power_ctrl;
+#define ICE_AQC_PHY_EN_D3COLD_LOW_POWER_AUTONEG BIT(0)
+ __le16 eee_cap;
+#define ICE_AQC_PHY_EEE_EN_100BASE_TX BIT(0)
+#define ICE_AQC_PHY_EEE_EN_1000BASE_T BIT(1)
+#define ICE_AQC_PHY_EEE_EN_10GBASE_T BIT(2)
+#define ICE_AQC_PHY_EEE_EN_1000BASE_KX BIT(3)
+#define ICE_AQC_PHY_EEE_EN_10GBASE_KR BIT(4)
+#define ICE_AQC_PHY_EEE_EN_25GBASE_KR BIT(5)
+#define ICE_AQC_PHY_EEE_EN_40GBASE_KR4 BIT(6)
+ __le16 eeer_value;
+ u8 phy_id_oui[4]; /* PHY/Module ID connected on the port */
+ u8 link_fec_options;
+#define ICE_AQC_PHY_FEC_10G_KR_40G_KR4_EN BIT(0)
+#define ICE_AQC_PHY_FEC_10G_KR_40G_KR4_REQ BIT(1)
+#define ICE_AQC_PHY_FEC_25G_RS_528_REQ BIT(2)
+#define ICE_AQC_PHY_FEC_25G_KR_REQ BIT(3)
+#define ICE_AQC_PHY_FEC_25G_RS_544_REQ BIT(4)
+#define ICE_AQC_PHY_FEC_25G_RS_CLAUSE91_EN BIT(6)
+#define ICE_AQC_PHY_FEC_25G_KR_CLAUSE74_EN BIT(7)
+ u8 extended_compliance_code;
+#define ICE_MODULE_TYPE_TOTAL_BYTE 3
+ u8 module_type[ICE_MODULE_TYPE_TOTAL_BYTE];
+#define ICE_AQC_MOD_TYPE_BYTE0_SFP_PLUS 0xA0
+#define ICE_AQC_MOD_TYPE_BYTE0_QSFP_PLUS 0x80
+#define ICE_AQC_MOD_TYPE_BYTE1_SFP_PLUS_CU_PASSIVE BIT(0)
+#define ICE_AQC_MOD_TYPE_BYTE1_SFP_PLUS_CU_ACTIVE BIT(1)
+#define ICE_AQC_MOD_TYPE_BYTE1_10G_BASE_SR BIT(4)
+#define ICE_AQC_MOD_TYPE_BYTE1_10G_BASE_LR BIT(5)
+#define ICE_AQC_MOD_TYPE_BYTE1_10G_BASE_LRM BIT(6)
+#define ICE_AQC_MOD_TYPE_BYTE1_10G_BASE_ER BIT(7)
+#define ICE_AQC_MOD_TYPE_BYTE2_SFP_PLUS 0xA0
+#define ICE_AQC_MOD_TYPE_BYTE2_QSFP_PLUS 0x86
+ u8 qualified_module_count;
+#define ICE_AQC_QUAL_MOD_COUNT_MAX 16
+ struct {
+ u8 v_oui[3];
+ u8 rsvd1;
+ u8 v_part[16];
+ __le32 v_rev;
+ __le64 rsvd8;
+ } qual_modules[ICE_AQC_QUAL_MOD_COUNT_MAX];
+};
+
+/* Set PHY capabilities (direct 0x0601)
+ * NOTE: This command must be followed by setup link and restart auto-neg
+ */
+struct ice_aqc_set_phy_cfg {
+ u8 lport_num;
+ u8 reserved[7];
+ __le32 addr_high;
+ __le32 addr_low;
+};
+
+/* Set PHY config command data structure */
+struct ice_aqc_set_phy_cfg_data {
+ __le64 phy_type_low; /* Use values from ICE_PHY_TYPE_LOW_* */
+ __le64 rsvd0;
+ u8 caps;
+#define ICE_AQ_PHY_ENA_TX_PAUSE_ABILITY BIT(0)
+#define ICE_AQ_PHY_ENA_RX_PAUSE_ABILITY BIT(1)
+#define ICE_AQ_PHY_ENA_LOW_POWER BIT(2)
+#define ICE_AQ_PHY_ENA_LINK BIT(3)
+#define ICE_AQ_PHY_ENA_ATOMIC_LINK BIT(5)
+ u8 low_power_ctrl;
+ __le16 eee_cap; /* Value from ice_aqc_get_phy_caps */
+ __le16 eeer_value;
+ u8 link_fec_opt; /* Use defines from ice_aqc_get_phy_caps */
+ u8 rsvd1;
+};
+
+/* Restart AN command data structure (direct 0x0605)
+ * Also used for response, with only the lport_num field present.
+ */
+struct ice_aqc_restart_an {
+ u8 lport_num;
+ u8 reserved;
+ u8 cmd_flags;
+#define ICE_AQC_RESTART_AN_LINK_RESTART BIT(1)
+#define ICE_AQC_RESTART_AN_LINK_ENABLE BIT(2)
+ u8 reserved2[13];
+};
+
+/* Get link status (indirect 0x0607), also used for Link Status Event */
+struct ice_aqc_get_link_status {
+ u8 lport_num;
+ u8 reserved;
+ __le16 cmd_flags;
+#define ICE_AQ_LSE_M 0x3
+#define ICE_AQ_LSE_NOP 0x0
+#define ICE_AQ_LSE_DIS 0x2
+#define ICE_AQ_LSE_ENA 0x3
+ /* only response uses this flag */
+#define ICE_AQ_LSE_IS_ENABLED 0x1
+ __le32 reserved2;
+ __le32 addr_high;
+ __le32 addr_low;
+};
+
+/* Get link status response data structure, also used for Link Status Event */
+struct ice_aqc_get_link_status_data {
+ u8 topo_media_conflict;
+#define ICE_AQ_LINK_TOPO_CONFLICT BIT(0)
+#define ICE_AQ_LINK_MEDIA_CONFLICT BIT(1)
+#define ICE_AQ_LINK_TOPO_CORRUPT BIT(2)
+ u8 reserved1;
+ u8 link_info;
+#define ICE_AQ_LINK_UP BIT(0) /* Link Status */
+#define ICE_AQ_LINK_FAULT BIT(1)
+#define ICE_AQ_LINK_FAULT_TX BIT(2)
+#define ICE_AQ_LINK_FAULT_RX BIT(3)
+#define ICE_AQ_LINK_FAULT_REMOTE BIT(4)
+#define ICE_AQ_LINK_UP_PORT BIT(5) /* External Port Link Status */
+#define ICE_AQ_MEDIA_AVAILABLE BIT(6)
+#define ICE_AQ_SIGNAL_DETECT BIT(7)
+ u8 an_info;
+#define ICE_AQ_AN_COMPLETED BIT(0)
+#define ICE_AQ_LP_AN_ABILITY BIT(1)
+#define ICE_AQ_PD_FAULT BIT(2) /* Parallel Detection Fault */
+#define ICE_AQ_FEC_EN BIT(3)
+#define ICE_AQ_PHY_LOW_POWER BIT(4) /* Low Power State */
+#define ICE_AQ_LINK_PAUSE_TX BIT(5)
+#define ICE_AQ_LINK_PAUSE_RX BIT(6)
+#define ICE_AQ_QUALIFIED_MODULE BIT(7)
+ u8 ext_info;
+#define ICE_AQ_LINK_PHY_TEMP_ALARM BIT(0)
+#define ICE_AQ_LINK_EXCESSIVE_ERRORS BIT(1) /* Excessive Link Errors */
+ /* Port TX Suspended */
+#define ICE_AQ_LINK_TX_S 2
+#define ICE_AQ_LINK_TX_M (0x03 << ICE_AQ_LINK_TX_S)
+#define ICE_AQ_LINK_TX_ACTIVE 0
+#define ICE_AQ_LINK_TX_DRAINED 1
+#define ICE_AQ_LINK_TX_FLUSHED 3
+ u8 reserved2;
+ __le16 max_frame_size;
+ u8 cfg;
+#define ICE_AQ_LINK_25G_KR_FEC_EN BIT(0)
+#define ICE_AQ_LINK_25G_RS_528_FEC_EN BIT(1)
+#define ICE_AQ_LINK_25G_RS_544_FEC_EN BIT(2)
+ /* Pacing Config */
+#define ICE_AQ_CFG_PACING_S 3
+#define ICE_AQ_CFG_PACING_M (0xF << ICE_AQ_CFG_PACING_S)
+#define ICE_AQ_CFG_PACING_TYPE_M BIT(7)
+#define ICE_AQ_CFG_PACING_TYPE_AVG 0
+#define ICE_AQ_CFG_PACING_TYPE_FIXED ICE_AQ_CFG_PACING_TYPE_M
+ /* External Device Power Ability */
+ u8 power_desc;
+#define ICE_AQ_PWR_CLASS_M 0x3
+#define ICE_AQ_LINK_PWR_BASET_LOW_HIGH 0
+#define ICE_AQ_LINK_PWR_BASET_HIGH 1
+#define ICE_AQ_LINK_PWR_QSFP_CLASS_1 0
+#define ICE_AQ_LINK_PWR_QSFP_CLASS_2 1
+#define ICE_AQ_LINK_PWR_QSFP_CLASS_3 2
+#define ICE_AQ_LINK_PWR_QSFP_CLASS_4 3
+ __le16 link_speed;
+#define ICE_AQ_LINK_SPEED_10MB BIT(0)
+#define ICE_AQ_LINK_SPEED_100MB BIT(1)
+#define ICE_AQ_LINK_SPEED_1000MB BIT(2)
+#define ICE_AQ_LINK_SPEED_2500MB BIT(3)
+#define ICE_AQ_LINK_SPEED_5GB BIT(4)
+#define ICE_AQ_LINK_SPEED_10GB BIT(5)
+#define ICE_AQ_LINK_SPEED_20GB BIT(6)
+#define ICE_AQ_LINK_SPEED_25GB BIT(7)
+#define ICE_AQ_LINK_SPEED_40GB BIT(8)
+#define ICE_AQ_LINK_SPEED_UNKNOWN BIT(15)
+ __le32 reserved3; /* Aligns next field to 8-byte boundary */
+ __le64 phy_type_low; /* Use values from ICE_PHY_TYPE_LOW_* */
+ __le64 reserved4;
+};
+
+/* Set event mask command (direct 0x0613) */
+struct ice_aqc_set_event_mask {
+ u8 lport_num;
+ u8 reserved[7];
+ __le16 event_mask;
+#define ICE_AQ_LINK_EVENT_UPDOWN BIT(1)
+#define ICE_AQ_LINK_EVENT_MEDIA_NA BIT(2)
+#define ICE_AQ_LINK_EVENT_LINK_FAULT BIT(3)
+#define ICE_AQ_LINK_EVENT_PHY_TEMP_ALARM BIT(4)
+#define ICE_AQ_LINK_EVENT_EXCESSIVE_ERRORS BIT(5)
+#define ICE_AQ_LINK_EVENT_SIGNAL_DETECT BIT(6)
+#define ICE_AQ_LINK_EVENT_AN_COMPLETED BIT(7)
+#define ICE_AQ_LINK_EVENT_MODULE_QUAL_FAIL BIT(8)
+#define ICE_AQ_LINK_EVENT_PORT_TX_SUSPENDED BIT(9)
+ u8 reserved1[6];
+};
+
+/* NVM Read command (indirect 0x0701)
+ * NVM Erase commands (direct 0x0702)
+ * NVM Update commands (indirect 0x0703)
+ */
+struct ice_aqc_nvm {
+ u8 cmd_flags;
+#define ICE_AQC_NVM_LAST_CMD BIT(0)
+#define ICE_AQC_NVM_PCIR_REQ BIT(0) /* Used by NVM Update reply */
+#define ICE_AQC_NVM_PRESERVATION_S 1
+#define ICE_AQC_NVM_PRESERVATION_M (3 << CSR_AQ_NVM_PRESERVATION_S)
+#define ICE_AQC_NVM_NO_PRESERVATION (0 << CSR_AQ_NVM_PRESERVATION_S)
+#define ICE_AQC_NVM_PRESERVE_ALL BIT(1)
+#define ICE_AQC_NVM_PRESERVE_SELECTED (3 << CSR_AQ_NVM_PRESERVATION_S)
+#define ICE_AQC_NVM_FLASH_ONLY BIT(7)
+ u8 module_typeid;
+ __le16 length;
+#define ICE_AQC_NVM_ERASE_LEN 0xFFFF
+ __le32 offset;
+ __le32 addr_high;
+ __le32 addr_low;
+};
+
+/* Get/Set RSS key (indirect 0x0B04/0x0B02) */
+struct ice_aqc_get_set_rss_key {
+#define ICE_AQC_GSET_RSS_KEY_VSI_VALID BIT(15)
+#define ICE_AQC_GSET_RSS_KEY_VSI_ID_S 0
+#define ICE_AQC_GSET_RSS_KEY_VSI_ID_M (0x3FF << ICE_AQC_GSET_RSS_KEY_VSI_ID_S)
+ __le16 vsi_id;
+ u8 reserved[6];
+ __le32 addr_high;
+ __le32 addr_low;
+};
+
+#define ICE_AQC_GET_SET_RSS_KEY_DATA_RSS_KEY_SIZE 0x28
+#define ICE_AQC_GET_SET_RSS_KEY_DATA_HASH_KEY_SIZE 0xC
+
+struct ice_aqc_get_set_rss_keys {
+ u8 standard_rss_key[ICE_AQC_GET_SET_RSS_KEY_DATA_RSS_KEY_SIZE];
+ u8 extended_hash_key[ICE_AQC_GET_SET_RSS_KEY_DATA_HASH_KEY_SIZE];
+};
+
+/* Get/Set RSS LUT (indirect 0x0B05/0x0B03) */
+struct ice_aqc_get_set_rss_lut {
+#define ICE_AQC_GSET_RSS_LUT_VSI_VALID BIT(15)
+#define ICE_AQC_GSET_RSS_LUT_VSI_ID_S 0
+#define ICE_AQC_GSET_RSS_LUT_VSI_ID_M (0x1FF << ICE_AQC_GSET_RSS_LUT_VSI_ID_S)
+ __le16 vsi_id;
+#define ICE_AQC_GSET_RSS_LUT_TABLE_TYPE_S 0
+#define ICE_AQC_GSET_RSS_LUT_TABLE_TYPE_M \
+ (0x3 << ICE_AQC_GSET_RSS_LUT_TABLE_TYPE_S)
+
+#define ICE_AQC_GSET_RSS_LUT_TABLE_TYPE_VSI 0
+#define ICE_AQC_GSET_RSS_LUT_TABLE_TYPE_PF 1
+#define ICE_AQC_GSET_RSS_LUT_TABLE_TYPE_GLOBAL 2
+
+#define ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_S 2
+#define ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_M \
+ (0x3 << ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_S)
+
+#define ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_128 128
+#define ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_128_FLAG 0
+#define ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_512 512
+#define ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_512_FLAG 1
+#define ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_2K 2048
+#define ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_2K_FLAG 2
+
+#define ICE_AQC_GSET_RSS_LUT_GLOBAL_IDX_S 4
+#define ICE_AQC_GSET_RSS_LUT_GLOBAL_IDX_M \
+ (0xF << ICE_AQC_GSET_RSS_LUT_GLOBAL_IDX_S)
+
+ __le16 flags;
+ __le32 reserved;
+ __le32 addr_high;
+ __le32 addr_low;
+};
+
+/* Add TX LAN Queues (indirect 0x0C30) */
+struct ice_aqc_add_txqs {
+ u8 num_qgrps;
+ u8 reserved[3];
+ __le32 reserved1;
+ __le32 addr_high;
+ __le32 addr_low;
+};
+
+/* This is the descriptor of each queue entry for the Add TX LAN Queues
+ * command (0x0C30). Only used within struct ice_aqc_add_tx_qgrp.
+ */
+struct ice_aqc_add_txqs_perq {
+ __le16 txq_id;
+ u8 rsvd[2];
+ __le32 q_teid;
+ u8 txq_ctx[22];
+ u8 rsvd2[2];
+ struct ice_aqc_txsched_elem info;
+};
+
+/* The format of the command buffer for Add TX LAN Queues (0x0C30)
+ * is an array of the following structs. Please note that the length of
+ * each struct ice_aqc_add_tx_qgrp is variable due
+ * to the variable number of queues in each group!
+ */
+struct ice_aqc_add_tx_qgrp {
+ __le32 parent_teid;
+ u8 num_txqs;
+ u8 rsvd[3];
+ struct ice_aqc_add_txqs_perq txqs[1];
+};
+
+/* Disable TX LAN Queues (indirect 0x0C31) */
+struct ice_aqc_dis_txqs {
+ u8 cmd_type;
+#define ICE_AQC_Q_DIS_CMD_S 0
+#define ICE_AQC_Q_DIS_CMD_M (0x3 << ICE_AQC_Q_DIS_CMD_S)
+#define ICE_AQC_Q_DIS_CMD_NO_FUNC_RESET (0 << ICE_AQC_Q_DIS_CMD_S)
+#define ICE_AQC_Q_DIS_CMD_VM_RESET BIT(ICE_AQC_Q_DIS_CMD_S)
+#define ICE_AQC_Q_DIS_CMD_VF_RESET (2 << ICE_AQC_Q_DIS_CMD_S)
+#define ICE_AQC_Q_DIS_CMD_PF_RESET (3 << ICE_AQC_Q_DIS_CMD_S)
+#define ICE_AQC_Q_DIS_CMD_SUBSEQ_CALL BIT(2)
+#define ICE_AQC_Q_DIS_CMD_FLUSH_PIPE BIT(3)
+ u8 num_entries;
+ __le16 vmvf_and_timeout;
+#define ICE_AQC_Q_DIS_VMVF_NUM_S 0
+#define ICE_AQC_Q_DIS_VMVF_NUM_M (0x3FF << ICE_AQC_Q_DIS_VMVF_NUM_S)
+#define ICE_AQC_Q_DIS_TIMEOUT_S 10
+#define ICE_AQC_Q_DIS_TIMEOUT_M (0x3F << ICE_AQC_Q_DIS_TIMEOUT_S)
+ __le32 blocked_cgds;
+ __le32 addr_high;
+ __le32 addr_low;
+};
+
+/* The buffer for Disable TX LAN Queues (indirect 0x0C31)
+ * contains the following structures, arrayed one after the
+ * other.
+ * Note: Since the q_id is 16 bits wide, if the
+ * number of queues is even, then 2 bytes of alignment MUST be
+ * added before the start of the next group, to allow correct
+ * alignment of the parent_teid field.
+ */
+struct ice_aqc_dis_txq_item {
+ __le32 parent_teid;
+ u8 num_qs;
+ u8 rsvd;
+ /* The length of the q_id array varies according to num_qs */
+ __le16 q_id[1];
+ /* This only applies from F8 onward */
+#define ICE_AQC_Q_DIS_BUF_ELEM_TYPE_S 15
+#define ICE_AQC_Q_DIS_BUF_ELEM_TYPE_LAN_Q \
+ (0 << ICE_AQC_Q_DIS_BUF_ELEM_TYPE_S)
+#define ICE_AQC_Q_DIS_BUF_ELEM_TYPE_RDMA_QSET \
+ (1 << ICE_AQC_Q_DIS_BUF_ELEM_TYPE_S)
+};
+
+struct ice_aqc_dis_txq {
+ struct ice_aqc_dis_txq_item qgrps[1];
+};
+
+/**
+ * struct ice_aq_desc - Admin Queue (AQ) descriptor
+ * @flags: ICE_AQ_FLAG_* flags
+ * @opcode: AQ command opcode
+ * @datalen: length in bytes of indirect/external data buffer
+ * @retval: return value from firmware
+ * @cookie_h: opaque data high-half
+ * @cookie_l: opaque data low-half
+ * @params: command-specific parameters
+ *
+ * Descriptor format for commands the driver posts on the Admin Transmit Queue
+ * (ATQ). The firmware writes back onto the command descriptor and returns
+ * the result of the command. Asynchronous events that are not an immediate
+ * result of the command are written to the Admin Receive Queue (ARQ) using
+ * the same descriptor format. Descriptors are in little-endian notation with
+ * 32-bit words.
+ */
+struct ice_aq_desc {
+ __le16 flags;
+ __le16 opcode;
+ __le16 datalen;
+ __le16 retval;
+ __le32 cookie_high;
+ __le32 cookie_low;
+ union {
+ u8 raw[16];
+ struct ice_aqc_generic generic;
+ struct ice_aqc_get_ver get_ver;
+ struct ice_aqc_q_shutdown q_shutdown;
+ struct ice_aqc_req_res res_owner;
+ struct ice_aqc_manage_mac_read mac_read;
+ struct ice_aqc_manage_mac_write mac_write;
+ struct ice_aqc_clear_pxe clear_pxe;
+ struct ice_aqc_list_caps get_cap;
+ struct ice_aqc_get_phy_caps get_phy;
+ struct ice_aqc_set_phy_cfg set_phy;
+ struct ice_aqc_restart_an restart_an;
+ struct ice_aqc_get_sw_cfg get_sw_conf;
+ struct ice_aqc_sw_rules sw_rules;
+ struct ice_aqc_get_topo get_topo;
+ struct ice_aqc_get_cfg_elem get_update_elem;
+ struct ice_aqc_query_txsched_res query_sched_res;
+ struct ice_aqc_add_move_delete_elem add_move_delete_elem;
+ struct ice_aqc_nvm nvm;
+ struct ice_aqc_get_set_rss_lut get_set_rss_lut;
+ struct ice_aqc_get_set_rss_key get_set_rss_key;
+ struct ice_aqc_add_txqs add_txqs;
+ struct ice_aqc_dis_txqs dis_txqs;
+ struct ice_aqc_add_get_update_free_vsi vsi_cmd;
+ struct ice_aqc_alloc_free_res_cmd sw_res_ctrl;
+ struct ice_aqc_set_event_mask set_event_mask;
+ struct ice_aqc_get_link_status get_link_status;
+ } params;
+};
+
+/* FW defined boundary for a large buffer, 4k >= Large buffer > 512 bytes */
+#define ICE_AQ_LG_BUF 512
+
+#define ICE_AQ_FLAG_ERR_S 2
+#define ICE_AQ_FLAG_LB_S 9
+#define ICE_AQ_FLAG_RD_S 10
+#define ICE_AQ_FLAG_BUF_S 12
+#define ICE_AQ_FLAG_SI_S 13
+
+#define ICE_AQ_FLAG_ERR BIT(ICE_AQ_FLAG_ERR_S) /* 0x4 */
+#define ICE_AQ_FLAG_LB BIT(ICE_AQ_FLAG_LB_S) /* 0x200 */
+#define ICE_AQ_FLAG_RD BIT(ICE_AQ_FLAG_RD_S) /* 0x400 */
+#define ICE_AQ_FLAG_BUF BIT(ICE_AQ_FLAG_BUF_S) /* 0x1000 */
+#define ICE_AQ_FLAG_SI BIT(ICE_AQ_FLAG_SI_S) /* 0x2000 */
+
+/* error codes */
+enum ice_aq_err {
+ ICE_AQ_RC_OK = 0, /* success */
+ ICE_AQ_RC_ENOMEM = 9, /* Out of memory */
+ ICE_AQ_RC_EBUSY = 12, /* Device or resource busy */
+ ICE_AQ_RC_EEXIST = 13, /* object already exists */
+ ICE_AQ_RC_ENOSPC = 16, /* No space left or allocation failure */
+};
+
+/* Admin Queue command opcodes */
+enum ice_adminq_opc {
+ /* AQ commands */
+ ice_aqc_opc_get_ver = 0x0001,
+ ice_aqc_opc_q_shutdown = 0x0003,
+
+ /* resource ownership */
+ ice_aqc_opc_req_res = 0x0008,
+ ice_aqc_opc_release_res = 0x0009,
+
+ /* device/function capabilities */
+ ice_aqc_opc_list_func_caps = 0x000A,
+ ice_aqc_opc_list_dev_caps = 0x000B,
+
+ /* manage MAC address */
+ ice_aqc_opc_manage_mac_read = 0x0107,
+ ice_aqc_opc_manage_mac_write = 0x0108,
+
+ /* PXE */
+ ice_aqc_opc_clear_pxe_mode = 0x0110,
+
+ /* internal switch commands */
+ ice_aqc_opc_get_sw_cfg = 0x0200,
+
+ /* Alloc/Free/Get Resources */
+ ice_aqc_opc_alloc_res = 0x0208,
+ ice_aqc_opc_free_res = 0x0209,
+
+ /* VSI commands */
+ ice_aqc_opc_add_vsi = 0x0210,
+ ice_aqc_opc_update_vsi = 0x0211,
+ ice_aqc_opc_free_vsi = 0x0213,
+
+ /* switch rules population commands */
+ ice_aqc_opc_add_sw_rules = 0x02A0,
+ ice_aqc_opc_update_sw_rules = 0x02A1,
+ ice_aqc_opc_remove_sw_rules = 0x02A2,
+
+ ice_aqc_opc_clear_pf_cfg = 0x02A4,
+
+ /* transmit scheduler commands */
+ ice_aqc_opc_get_dflt_topo = 0x0400,
+ ice_aqc_opc_add_sched_elems = 0x0401,
+ ice_aqc_opc_suspend_sched_elems = 0x0409,
+ ice_aqc_opc_resume_sched_elems = 0x040A,
+ ice_aqc_opc_delete_sched_elems = 0x040F,
+ ice_aqc_opc_query_sched_res = 0x0412,
+
+ /* PHY commands */
+ ice_aqc_opc_get_phy_caps = 0x0600,
+ ice_aqc_opc_set_phy_cfg = 0x0601,
+ ice_aqc_opc_restart_an = 0x0605,
+ ice_aqc_opc_get_link_status = 0x0607,
+ ice_aqc_opc_set_event_mask = 0x0613,
+
+ /* NVM commands */
+ ice_aqc_opc_nvm_read = 0x0701,
+
+ /* RSS commands */
+ ice_aqc_opc_set_rss_key = 0x0B02,
+ ice_aqc_opc_set_rss_lut = 0x0B03,
+ ice_aqc_opc_get_rss_key = 0x0B04,
+ ice_aqc_opc_get_rss_lut = 0x0B05,
+
+ /* TX queue handling commands/events */
+ ice_aqc_opc_add_txqs = 0x0C30,
+ ice_aqc_opc_dis_txqs = 0x0C31,
+};
+
+#endif /* _ICE_ADMINQ_CMD_H_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c
new file mode 100644
index 000000000000..71d032cc5fa7
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_common.c
@@ -0,0 +1,2247 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2018, Intel Corporation. */
+
+#include "ice_common.h"
+#include "ice_sched.h"
+#include "ice_adminq_cmd.h"
+
+#define ICE_PF_RESET_WAIT_COUNT 200
+
+#define ICE_NIC_FLX_ENTRY(hw, mdid, idx) \
+ wr32((hw), GLFLXP_RXDID_FLX_WRD_##idx(ICE_RXDID_FLEX_NIC), \
+ ((ICE_RX_OPC_MDID << \
+ GLFLXP_RXDID_FLX_WRD_##idx##_RXDID_OPCODE_S) & \
+ GLFLXP_RXDID_FLX_WRD_##idx##_RXDID_OPCODE_M) | \
+ (((mdid) << GLFLXP_RXDID_FLX_WRD_##idx##_PROT_MDID_S) & \
+ GLFLXP_RXDID_FLX_WRD_##idx##_PROT_MDID_M))
+
+#define ICE_NIC_FLX_FLG_ENTRY(hw, flg_0, flg_1, flg_2, flg_3, idx) \
+ wr32((hw), GLFLXP_RXDID_FLAGS(ICE_RXDID_FLEX_NIC, idx), \
+ (((flg_0) << GLFLXP_RXDID_FLAGS_FLEXIFLAG_4N_S) & \
+ GLFLXP_RXDID_FLAGS_FLEXIFLAG_4N_M) | \
+ (((flg_1) << GLFLXP_RXDID_FLAGS_FLEXIFLAG_4N_1_S) & \
+ GLFLXP_RXDID_FLAGS_FLEXIFLAG_4N_1_M) | \
+ (((flg_2) << GLFLXP_RXDID_FLAGS_FLEXIFLAG_4N_2_S) & \
+ GLFLXP_RXDID_FLAGS_FLEXIFLAG_4N_2_M) | \
+ (((flg_3) << GLFLXP_RXDID_FLAGS_FLEXIFLAG_4N_3_S) & \
+ GLFLXP_RXDID_FLAGS_FLEXIFLAG_4N_3_M))
+
+/**
+ * ice_set_mac_type - Sets MAC type
+ * @hw: pointer to the HW structure
+ *
+ * This function sets the MAC type of the adapter based on the
+ * vendor ID and device ID stored in the hw structure.
+ */
+static enum ice_status ice_set_mac_type(struct ice_hw *hw)
+{
+ if (hw->vendor_id != PCI_VENDOR_ID_INTEL)
+ return ICE_ERR_DEVICE_NOT_SUPPORTED;
+
+ hw->mac_type = ICE_MAC_GENERIC;
+ return 0;
+}
+
+/**
+ * ice_clear_pf_cfg - Clear PF configuration
+ * @hw: pointer to the hardware structure
+ */
+enum ice_status ice_clear_pf_cfg(struct ice_hw *hw)
+{
+ struct ice_aq_desc desc;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_clear_pf_cfg);
+
+ return ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+}
+
+/**
+ * ice_aq_manage_mac_read - manage MAC address read command
+ * @hw: pointer to the hw struct
+ * @buf: a virtual buffer to hold the manage MAC read response
+ * @buf_size: Size of the virtual buffer
+ * @cd: pointer to command details structure or NULL
+ *
+ * This function is used to return per PF station MAC address (0x0107).
+ * NOTE: Upon successful completion of this command, MAC address information
+ * is returned in user specified buffer. Please interpret user specified
+ * buffer as "manage_mac_read" response.
+ * Response such as various MAC addresses are stored in HW struct (port.mac)
+ * ice_aq_discover_caps is expected to be called before this function is called.
+ */
+static enum ice_status
+ice_aq_manage_mac_read(struct ice_hw *hw, void *buf, u16 buf_size,
+ struct ice_sq_cd *cd)
+{
+ struct ice_aqc_manage_mac_read_resp *resp;
+ struct ice_aqc_manage_mac_read *cmd;
+ struct ice_aq_desc desc;
+ enum ice_status status;
+ u16 flags;
+ u8 i;
+
+ cmd = &desc.params.mac_read;
+
+ if (buf_size < sizeof(*resp))
+ return ICE_ERR_BUF_TOO_SHORT;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_manage_mac_read);
+
+ status = ice_aq_send_cmd(hw, &desc, buf, buf_size, cd);
+ if (status)
+ return status;
+
+ resp = (struct ice_aqc_manage_mac_read_resp *)buf;
+ flags = le16_to_cpu(cmd->flags) & ICE_AQC_MAN_MAC_READ_M;
+
+ if (!(flags & ICE_AQC_MAN_MAC_LAN_ADDR_VALID)) {
+ ice_debug(hw, ICE_DBG_LAN, "got invalid MAC address\n");
+ return ICE_ERR_CFG;
+ }
+
+ /* A single port can report up to two (LAN and WoL) addresses */
+ for (i = 0; i < cmd->num_addr; i++)
+ if (resp[i].addr_type == ICE_AQC_MAN_MAC_ADDR_TYPE_LAN) {
+ ether_addr_copy(hw->port_info->mac.lan_addr,
+ resp[i].mac_addr);
+ ether_addr_copy(hw->port_info->mac.perm_addr,
+ resp[i].mac_addr);
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * ice_aq_get_phy_caps - returns PHY capabilities
+ * @pi: port information structure
+ * @qual_mods: report qualified modules
+ * @report_mode: report mode capabilities
+ * @pcaps: structure for PHY capabilities to be filled
+ * @cd: pointer to command details structure or NULL
+ *
+ * Returns the various PHY capabilities supported on the Port (0x0600)
+ */
+static enum ice_status
+ice_aq_get_phy_caps(struct ice_port_info *pi, bool qual_mods, u8 report_mode,
+ struct ice_aqc_get_phy_caps_data *pcaps,
+ struct ice_sq_cd *cd)
+{
+ struct ice_aqc_get_phy_caps *cmd;
+ u16 pcaps_size = sizeof(*pcaps);
+ struct ice_aq_desc desc;
+ enum ice_status status;
+
+ cmd = &desc.params.get_phy;
+
+ if (!pcaps || (report_mode & ~ICE_AQC_REPORT_MODE_M) || !pi)
+ return ICE_ERR_PARAM;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_phy_caps);
+
+ if (qual_mods)
+ cmd->param0 |= cpu_to_le16(ICE_AQC_GET_PHY_RQM);
+
+ cmd->param0 |= cpu_to_le16(report_mode);
+ status = ice_aq_send_cmd(pi->hw, &desc, pcaps, pcaps_size, cd);
+
+ if (!status && report_mode == ICE_AQC_REPORT_TOPO_CAP)
+ pi->phy.phy_type_low = le64_to_cpu(pcaps->phy_type_low);
+
+ return status;
+}
+
+/**
+ * ice_get_media_type - Gets media type
+ * @pi: port information structure
+ */
+static enum ice_media_type ice_get_media_type(struct ice_port_info *pi)
+{
+ struct ice_link_status *hw_link_info;
+
+ if (!pi)
+ return ICE_MEDIA_UNKNOWN;
+
+ hw_link_info = &pi->phy.link_info;
+
+ if (hw_link_info->phy_type_low) {
+ switch (hw_link_info->phy_type_low) {
+ case ICE_PHY_TYPE_LOW_1000BASE_SX:
+ case ICE_PHY_TYPE_LOW_1000BASE_LX:
+ case ICE_PHY_TYPE_LOW_10GBASE_SR:
+ case ICE_PHY_TYPE_LOW_10GBASE_LR:
+ case ICE_PHY_TYPE_LOW_10G_SFI_C2C:
+ case ICE_PHY_TYPE_LOW_25GBASE_SR:
+ case ICE_PHY_TYPE_LOW_25GBASE_LR:
+ case ICE_PHY_TYPE_LOW_25G_AUI_C2C:
+ case ICE_PHY_TYPE_LOW_40GBASE_SR4:
+ case ICE_PHY_TYPE_LOW_40GBASE_LR4:
+ return ICE_MEDIA_FIBER;
+ case ICE_PHY_TYPE_LOW_100BASE_TX:
+ case ICE_PHY_TYPE_LOW_1000BASE_T:
+ case ICE_PHY_TYPE_LOW_2500BASE_T:
+ case ICE_PHY_TYPE_LOW_5GBASE_T:
+ case ICE_PHY_TYPE_LOW_10GBASE_T:
+ case ICE_PHY_TYPE_LOW_25GBASE_T:
+ return ICE_MEDIA_BASET;
+ case ICE_PHY_TYPE_LOW_10G_SFI_DA:
+ case ICE_PHY_TYPE_LOW_25GBASE_CR:
+ case ICE_PHY_TYPE_LOW_25GBASE_CR_S:
+ case ICE_PHY_TYPE_LOW_25GBASE_CR1:
+ case ICE_PHY_TYPE_LOW_40GBASE_CR4:
+ return ICE_MEDIA_DA;
+ case ICE_PHY_TYPE_LOW_1000BASE_KX:
+ case ICE_PHY_TYPE_LOW_2500BASE_KX:
+ case ICE_PHY_TYPE_LOW_2500BASE_X:
+ case ICE_PHY_TYPE_LOW_5GBASE_KR:
+ case ICE_PHY_TYPE_LOW_10GBASE_KR_CR1:
+ case ICE_PHY_TYPE_LOW_25GBASE_KR:
+ case ICE_PHY_TYPE_LOW_25GBASE_KR1:
+ case ICE_PHY_TYPE_LOW_25GBASE_KR_S:
+ case ICE_PHY_TYPE_LOW_40GBASE_KR4:
+ return ICE_MEDIA_BACKPLANE;
+ }
+ }
+
+ return ICE_MEDIA_UNKNOWN;
+}
+
+/**
+ * ice_aq_get_link_info
+ * @pi: port information structure
+ * @ena_lse: enable/disable LinkStatusEvent reporting
+ * @link: pointer to link status structure - optional
+ * @cd: pointer to command details structure or NULL
+ *
+ * Get Link Status (0x607). Returns the link status of the adapter.
+ */
+enum ice_status
+ice_aq_get_link_info(struct ice_port_info *pi, bool ena_lse,
+ struct ice_link_status *link, struct ice_sq_cd *cd)
+{
+ struct ice_link_status *hw_link_info_old, *hw_link_info;
+ struct ice_aqc_get_link_status_data link_data = { 0 };
+ struct ice_aqc_get_link_status *resp;
+ enum ice_media_type *hw_media_type;
+ struct ice_fc_info *hw_fc_info;
+ bool tx_pause, rx_pause;
+ struct ice_aq_desc desc;
+ enum ice_status status;
+ u16 cmd_flags;
+
+ if (!pi)
+ return ICE_ERR_PARAM;
+ hw_link_info_old = &pi->phy.link_info_old;
+ hw_media_type = &pi->phy.media_type;
+ hw_link_info = &pi->phy.link_info;
+ hw_fc_info = &pi->fc;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_link_status);
+ cmd_flags = (ena_lse) ? ICE_AQ_LSE_ENA : ICE_AQ_LSE_DIS;
+ resp = &desc.params.get_link_status;
+ resp->cmd_flags = cpu_to_le16(cmd_flags);
+ resp->lport_num = pi->lport;
+
+ status = ice_aq_send_cmd(pi->hw, &desc, &link_data, sizeof(link_data),
+ cd);
+
+ if (status)
+ return status;
+
+ /* save off old link status information */
+ *hw_link_info_old = *hw_link_info;
+
+ /* update current link status information */
+ hw_link_info->link_speed = le16_to_cpu(link_data.link_speed);
+ hw_link_info->phy_type_low = le64_to_cpu(link_data.phy_type_low);
+ *hw_media_type = ice_get_media_type(pi);
+ hw_link_info->link_info = link_data.link_info;
+ hw_link_info->an_info = link_data.an_info;
+ hw_link_info->ext_info = link_data.ext_info;
+ hw_link_info->max_frame_size = le16_to_cpu(link_data.max_frame_size);
+ hw_link_info->pacing = link_data.cfg & ICE_AQ_CFG_PACING_M;
+
+ /* update fc info */
+ tx_pause = !!(link_data.an_info & ICE_AQ_LINK_PAUSE_TX);
+ rx_pause = !!(link_data.an_info & ICE_AQ_LINK_PAUSE_RX);
+ if (tx_pause && rx_pause)
+ hw_fc_info->current_mode = ICE_FC_FULL;
+ else if (tx_pause)
+ hw_fc_info->current_mode = ICE_FC_TX_PAUSE;
+ else if (rx_pause)
+ hw_fc_info->current_mode = ICE_FC_RX_PAUSE;
+ else
+ hw_fc_info->current_mode = ICE_FC_NONE;
+
+ hw_link_info->lse_ena =
+ !!(resp->cmd_flags & cpu_to_le16(ICE_AQ_LSE_IS_ENABLED));
+
+ /* save link status information */
+ if (link)
+ *link = *hw_link_info;
+
+ /* flag cleared so calling functions don't call AQ again */
+ pi->phy.get_link_info = false;
+
+ return status;
+}
+
+/**
+ * ice_init_flex_parser - initialize rx flex parser
+ * @hw: pointer to the hardware structure
+ *
+ * Function to initialize flex descriptors
+ */
+static void ice_init_flex_parser(struct ice_hw *hw)
+{
+ u8 idx = 0;
+
+ ICE_NIC_FLX_ENTRY(hw, ICE_RX_MDID_HASH_LOW, 0);
+ ICE_NIC_FLX_ENTRY(hw, ICE_RX_MDID_HASH_HIGH, 1);
+ ICE_NIC_FLX_ENTRY(hw, ICE_RX_MDID_FLOW_ID_LOWER, 2);
+ ICE_NIC_FLX_ENTRY(hw, ICE_RX_MDID_FLOW_ID_HIGH, 3);
+ ICE_NIC_FLX_FLG_ENTRY(hw, ICE_RXFLG_PKT_FRG, ICE_RXFLG_UDP_GRE,
+ ICE_RXFLG_PKT_DSI, ICE_RXFLG_FIN, idx++);
+ ICE_NIC_FLX_FLG_ENTRY(hw, ICE_RXFLG_SYN, ICE_RXFLG_RST,
+ ICE_RXFLG_PKT_DSI, ICE_RXFLG_PKT_DSI, idx++);
+ ICE_NIC_FLX_FLG_ENTRY(hw, ICE_RXFLG_PKT_DSI, ICE_RXFLG_PKT_DSI,
+ ICE_RXFLG_EVLAN_x8100, ICE_RXFLG_EVLAN_x9100,
+ idx++);
+ ICE_NIC_FLX_FLG_ENTRY(hw, ICE_RXFLG_VLAN_x8100, ICE_RXFLG_TNL_VLAN,
+ ICE_RXFLG_TNL_MAC, ICE_RXFLG_TNL0, idx++);
+ ICE_NIC_FLX_FLG_ENTRY(hw, ICE_RXFLG_TNL1, ICE_RXFLG_TNL2,
+ ICE_RXFLG_PKT_DSI, ICE_RXFLG_PKT_DSI, idx);
+}
+
+/**
+ * ice_init_fltr_mgmt_struct - initializes filter management list and locks
+ * @hw: pointer to the hw struct
+ */
+static enum ice_status ice_init_fltr_mgmt_struct(struct ice_hw *hw)
+{
+ struct ice_switch_info *sw;
+
+ hw->switch_info = devm_kzalloc(ice_hw_to_dev(hw),
+ sizeof(*hw->switch_info), GFP_KERNEL);
+ sw = hw->switch_info;
+
+ if (!sw)
+ return ICE_ERR_NO_MEMORY;
+
+ INIT_LIST_HEAD(&sw->vsi_list_map_head);
+
+ mutex_init(&sw->mac_list_lock);
+ INIT_LIST_HEAD(&sw->mac_list_head);
+
+ mutex_init(&sw->vlan_list_lock);
+ INIT_LIST_HEAD(&sw->vlan_list_head);
+
+ mutex_init(&sw->eth_m_list_lock);
+ INIT_LIST_HEAD(&sw->eth_m_list_head);
+
+ mutex_init(&sw->promisc_list_lock);
+ INIT_LIST_HEAD(&sw->promisc_list_head);
+
+ mutex_init(&sw->mac_vlan_list_lock);
+ INIT_LIST_HEAD(&sw->mac_vlan_list_head);
+
+ return 0;
+}
+
+/**
+ * ice_cleanup_fltr_mgmt_struct - cleanup filter management list and locks
+ * @hw: pointer to the hw struct
+ */
+static void ice_cleanup_fltr_mgmt_struct(struct ice_hw *hw)
+{
+ struct ice_switch_info *sw = hw->switch_info;
+ struct ice_vsi_list_map_info *v_pos_map;
+ struct ice_vsi_list_map_info *v_tmp_map;
+
+ list_for_each_entry_safe(v_pos_map, v_tmp_map, &sw->vsi_list_map_head,
+ list_entry) {
+ list_del(&v_pos_map->list_entry);
+ devm_kfree(ice_hw_to_dev(hw), v_pos_map);
+ }
+
+ mutex_destroy(&sw->mac_list_lock);
+ mutex_destroy(&sw->vlan_list_lock);
+ mutex_destroy(&sw->eth_m_list_lock);
+ mutex_destroy(&sw->promisc_list_lock);
+ mutex_destroy(&sw->mac_vlan_list_lock);
+
+ devm_kfree(ice_hw_to_dev(hw), sw);
+}
+
+/**
+ * ice_init_hw - main hardware initialization routine
+ * @hw: pointer to the hardware structure
+ */
+enum ice_status ice_init_hw(struct ice_hw *hw)
+{
+ struct ice_aqc_get_phy_caps_data *pcaps;
+ enum ice_status status;
+ u16 mac_buf_len;
+ void *mac_buf;
+
+ /* Set MAC type based on DeviceID */
+ status = ice_set_mac_type(hw);
+ if (status)
+ return status;
+
+ hw->pf_id = (u8)(rd32(hw, PF_FUNC_RID) &
+ PF_FUNC_RID_FUNC_NUM_M) >>
+ PF_FUNC_RID_FUNC_NUM_S;
+
+ status = ice_reset(hw, ICE_RESET_PFR);
+ if (status)
+ return status;
+
+ /* set these values to minimum allowed */
+ hw->itr_gran_200 = ICE_ITR_GRAN_MIN_200;
+ hw->itr_gran_100 = ICE_ITR_GRAN_MIN_100;
+ hw->itr_gran_50 = ICE_ITR_GRAN_MIN_50;
+ hw->itr_gran_25 = ICE_ITR_GRAN_MIN_25;
+
+ status = ice_init_all_ctrlq(hw);
+ if (status)
+ goto err_unroll_cqinit;
+
+ status = ice_clear_pf_cfg(hw);
+ if (status)
+ goto err_unroll_cqinit;
+
+ ice_clear_pxe_mode(hw);
+
+ status = ice_init_nvm(hw);
+ if (status)
+ goto err_unroll_cqinit;
+
+ status = ice_get_caps(hw);
+ if (status)
+ goto err_unroll_cqinit;
+
+ hw->port_info = devm_kzalloc(ice_hw_to_dev(hw),
+ sizeof(*hw->port_info), GFP_KERNEL);
+ if (!hw->port_info) {
+ status = ICE_ERR_NO_MEMORY;
+ goto err_unroll_cqinit;
+ }
+
+ /* set the back pointer to hw */
+ hw->port_info->hw = hw;
+
+ /* Initialize port_info struct with switch configuration data */
+ status = ice_get_initial_sw_cfg(hw);
+ if (status)
+ goto err_unroll_alloc;
+
+ hw->evb_veb = true;
+
+ /* Query the allocated resources for tx scheduler */
+ status = ice_sched_query_res_alloc(hw);
+ if (status) {
+ ice_debug(hw, ICE_DBG_SCHED,
+ "Failed to get scheduler allocated resources\n");
+ goto err_unroll_alloc;
+ }
+
+ /* Initialize port_info struct with scheduler data */
+ status = ice_sched_init_port(hw->port_info);
+ if (status)
+ goto err_unroll_sched;
+
+ pcaps = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*pcaps), GFP_KERNEL);
+ if (!pcaps) {
+ status = ICE_ERR_NO_MEMORY;
+ goto err_unroll_sched;
+ }
+
+ /* Initialize port_info struct with PHY capabilities */
+ status = ice_aq_get_phy_caps(hw->port_info, false,
+ ICE_AQC_REPORT_TOPO_CAP, pcaps, NULL);
+ devm_kfree(ice_hw_to_dev(hw), pcaps);
+ if (status)
+ goto err_unroll_sched;
+
+ /* Initialize port_info struct with link information */
+ status = ice_aq_get_link_info(hw->port_info, false, NULL, NULL);
+ if (status)
+ goto err_unroll_sched;
+
+ status = ice_init_fltr_mgmt_struct(hw);
+ if (status)
+ goto err_unroll_sched;
+
+ /* Get MAC information */
+ /* A single port can report up to two (LAN and WoL) addresses */
+ mac_buf = devm_kcalloc(ice_hw_to_dev(hw), 2,
+ sizeof(struct ice_aqc_manage_mac_read_resp),
+ GFP_KERNEL);
+ mac_buf_len = 2 * sizeof(struct ice_aqc_manage_mac_read_resp);
+
+ if (!mac_buf) {
+ status = ICE_ERR_NO_MEMORY;
+ goto err_unroll_fltr_mgmt_struct;
+ }
+
+ status = ice_aq_manage_mac_read(hw, mac_buf, mac_buf_len, NULL);
+ devm_kfree(ice_hw_to_dev(hw), mac_buf);
+
+ if (status)
+ goto err_unroll_fltr_mgmt_struct;
+
+ ice_init_flex_parser(hw);
+
+ return 0;
+
+err_unroll_fltr_mgmt_struct:
+ ice_cleanup_fltr_mgmt_struct(hw);
+err_unroll_sched:
+ ice_sched_cleanup_all(hw);
+err_unroll_alloc:
+ devm_kfree(ice_hw_to_dev(hw), hw->port_info);
+err_unroll_cqinit:
+ ice_shutdown_all_ctrlq(hw);
+ return status;
+}
+
+/**
+ * ice_deinit_hw - unroll initialization operations done by ice_init_hw
+ * @hw: pointer to the hardware structure
+ */
+void ice_deinit_hw(struct ice_hw *hw)
+{
+ ice_sched_cleanup_all(hw);
+ ice_shutdown_all_ctrlq(hw);
+
+ if (hw->port_info) {
+ devm_kfree(ice_hw_to_dev(hw), hw->port_info);
+ hw->port_info = NULL;
+ }
+
+ ice_cleanup_fltr_mgmt_struct(hw);
+}
+
+/**
+ * ice_check_reset - Check to see if a global reset is complete
+ * @hw: pointer to the hardware structure
+ */
+enum ice_status ice_check_reset(struct ice_hw *hw)
+{
+ u32 cnt, reg = 0, grst_delay;
+
+ /* Poll for Device Active state in case a recent CORER, GLOBR,
+ * or EMPR has occurred. The grst delay value is in 100ms units.
+ * Add 1sec for outstanding AQ commands that can take a long time.
+ */
+ grst_delay = ((rd32(hw, GLGEN_RSTCTL) & GLGEN_RSTCTL_GRSTDEL_M) >>
+ GLGEN_RSTCTL_GRSTDEL_S) + 10;
+
+ for (cnt = 0; cnt < grst_delay; cnt++) {
+ mdelay(100);
+ reg = rd32(hw, GLGEN_RSTAT);
+ if (!(reg & GLGEN_RSTAT_DEVSTATE_M))
+ break;
+ }
+
+ if (cnt == grst_delay) {
+ ice_debug(hw, ICE_DBG_INIT,
+ "Global reset polling failed to complete.\n");
+ return ICE_ERR_RESET_FAILED;
+ }
+
+#define ICE_RESET_DONE_MASK (GLNVM_ULD_CORER_DONE_M | \
+ GLNVM_ULD_GLOBR_DONE_M)
+
+ /* Device is Active; check Global Reset processes are done */
+ for (cnt = 0; cnt < ICE_PF_RESET_WAIT_COUNT; cnt++) {
+ reg = rd32(hw, GLNVM_ULD) & ICE_RESET_DONE_MASK;
+ if (reg == ICE_RESET_DONE_MASK) {
+ ice_debug(hw, ICE_DBG_INIT,
+ "Global reset processes done. %d\n", cnt);
+ break;
+ }
+ mdelay(10);
+ }
+
+ if (cnt == ICE_PF_RESET_WAIT_COUNT) {
+ ice_debug(hw, ICE_DBG_INIT,
+ "Wait for Reset Done timed out. GLNVM_ULD = 0x%x\n",
+ reg);
+ return ICE_ERR_RESET_FAILED;
+ }
+
+ return 0;
+}
+
+/**
+ * ice_pf_reset - Reset the PF
+ * @hw: pointer to the hardware structure
+ *
+ * If a global reset has been triggered, this function checks
+ * for its completion and then issues the PF reset
+ */
+static enum ice_status ice_pf_reset(struct ice_hw *hw)
+{
+ u32 cnt, reg;
+
+ /* If at function entry a global reset was already in progress, i.e.
+ * state is not 'device active' or any of the reset done bits are not
+ * set in GLNVM_ULD, there is no need for a PF Reset; poll until the
+ * global reset is done.
+ */
+ if ((rd32(hw, GLGEN_RSTAT) & GLGEN_RSTAT_DEVSTATE_M) ||
+ (rd32(hw, GLNVM_ULD) & ICE_RESET_DONE_MASK) ^ ICE_RESET_DONE_MASK) {
+ /* poll on global reset currently in progress until done */
+ if (ice_check_reset(hw))
+ return ICE_ERR_RESET_FAILED;
+
+ return 0;
+ }
+
+ /* Reset the PF */
+ reg = rd32(hw, PFGEN_CTRL);
+
+ wr32(hw, PFGEN_CTRL, (reg | PFGEN_CTRL_PFSWR_M));
+
+ for (cnt = 0; cnt < ICE_PF_RESET_WAIT_COUNT; cnt++) {
+ reg = rd32(hw, PFGEN_CTRL);
+ if (!(reg & PFGEN_CTRL_PFSWR_M))
+ break;
+
+ mdelay(1);
+ }
+
+ if (cnt == ICE_PF_RESET_WAIT_COUNT) {
+ ice_debug(hw, ICE_DBG_INIT,
+ "PF reset polling failed to complete.\n");
+ return ICE_ERR_RESET_FAILED;
+ }
+
+ return 0;
+}
+
+/**
+ * ice_reset - Perform different types of reset
+ * @hw: pointer to the hardware structure
+ * @req: reset request
+ *
+ * This function triggers a reset as specified by the req parameter.
+ *
+ * Note:
+ * If anything other than a PF reset is triggered, PXE mode is restored.
+ * This has to be cleared using ice_clear_pxe_mode again, once the AQ
+ * interface has been restored in the rebuild flow.
+ */
+enum ice_status ice_reset(struct ice_hw *hw, enum ice_reset_req req)
+{
+ u32 val = 0;
+
+ switch (req) {
+ case ICE_RESET_PFR:
+ return ice_pf_reset(hw);
+ case ICE_RESET_CORER:
+ ice_debug(hw, ICE_DBG_INIT, "CoreR requested\n");
+ val = GLGEN_RTRIG_CORER_M;
+ break;
+ case ICE_RESET_GLOBR:
+ ice_debug(hw, ICE_DBG_INIT, "GlobalR requested\n");
+ val = GLGEN_RTRIG_GLOBR_M;
+ break;
+ }
+
+ val |= rd32(hw, GLGEN_RTRIG);
+ wr32(hw, GLGEN_RTRIG, val);
+ ice_flush(hw);
+
+ /* wait for the FW to be ready */
+ return ice_check_reset(hw);
+}
+
+/**
+ * ice_copy_rxq_ctx_to_hw
+ * @hw: pointer to the hardware structure
+ * @ice_rxq_ctx: pointer to the rxq context
+ * @rxq_index: the index of the rx queue
+ *
+ * Copies rxq context from dense structure to hw register space
+ */
+static enum ice_status
+ice_copy_rxq_ctx_to_hw(struct ice_hw *hw, u8 *ice_rxq_ctx, u32 rxq_index)
+{
+ u8 i;
+
+ if (!ice_rxq_ctx)
+ return ICE_ERR_BAD_PTR;
+
+ if (rxq_index > QRX_CTRL_MAX_INDEX)
+ return ICE_ERR_PARAM;
+
+ /* Copy each dword separately to hw */
+ for (i = 0; i < ICE_RXQ_CTX_SIZE_DWORDS; i++) {
+ wr32(hw, QRX_CONTEXT(i, rxq_index),
+ *((u32 *)(ice_rxq_ctx + (i * sizeof(u32)))));
+
+ ice_debug(hw, ICE_DBG_QCTX, "qrxdata[%d]: %08X\n", i,
+ *((u32 *)(ice_rxq_ctx + (i * sizeof(u32)))));
+ }
+
+ return 0;
+}
+
+/* LAN Rx Queue Context */
+static const struct ice_ctx_ele ice_rlan_ctx_info[] = {
+ /* Field Width LSB */
+ ICE_CTX_STORE(ice_rlan_ctx, head, 13, 0),
+ ICE_CTX_STORE(ice_rlan_ctx, cpuid, 8, 13),
+ ICE_CTX_STORE(ice_rlan_ctx, base, 57, 32),
+ ICE_CTX_STORE(ice_rlan_ctx, qlen, 13, 89),
+ ICE_CTX_STORE(ice_rlan_ctx, dbuf, 7, 102),
+ ICE_CTX_STORE(ice_rlan_ctx, hbuf, 5, 109),
+ ICE_CTX_STORE(ice_rlan_ctx, dtype, 2, 114),
+ ICE_CTX_STORE(ice_rlan_ctx, dsize, 1, 116),
+ ICE_CTX_STORE(ice_rlan_ctx, crcstrip, 1, 117),
+ ICE_CTX_STORE(ice_rlan_ctx, l2tsel, 1, 119),
+ ICE_CTX_STORE(ice_rlan_ctx, hsplit_0, 4, 120),
+ ICE_CTX_STORE(ice_rlan_ctx, hsplit_1, 2, 124),
+ ICE_CTX_STORE(ice_rlan_ctx, showiv, 1, 127),
+ ICE_CTX_STORE(ice_rlan_ctx, rxmax, 14, 174),
+ ICE_CTX_STORE(ice_rlan_ctx, tphrdesc_ena, 1, 193),
+ ICE_CTX_STORE(ice_rlan_ctx, tphwdesc_ena, 1, 194),
+ ICE_CTX_STORE(ice_rlan_ctx, tphdata_ena, 1, 195),
+ ICE_CTX_STORE(ice_rlan_ctx, tphhead_ena, 1, 196),
+ ICE_CTX_STORE(ice_rlan_ctx, lrxqthresh, 3, 198),
+ { 0 }
+};
+
+/**
+ * ice_write_rxq_ctx
+ * @hw: pointer to the hardware structure
+ * @rlan_ctx: pointer to the rxq context
+ * @rxq_index: the index of the rx queue
+ *
+ * Converts rxq context from sparse to dense structure and then writes
+ * it to hw register space
+ */
+enum ice_status
+ice_write_rxq_ctx(struct ice_hw *hw, struct ice_rlan_ctx *rlan_ctx,
+ u32 rxq_index)
+{
+ u8 ctx_buf[ICE_RXQ_CTX_SZ] = { 0 };
+
+ ice_set_ctx((u8 *)rlan_ctx, ctx_buf, ice_rlan_ctx_info);
+ return ice_copy_rxq_ctx_to_hw(hw, ctx_buf, rxq_index);
+}
+
+/* LAN Tx Queue Context */
+const struct ice_ctx_ele ice_tlan_ctx_info[] = {
+ /* Field Width LSB */
+ ICE_CTX_STORE(ice_tlan_ctx, base, 57, 0),
+ ICE_CTX_STORE(ice_tlan_ctx, port_num, 3, 57),
+ ICE_CTX_STORE(ice_tlan_ctx, cgd_num, 5, 60),
+ ICE_CTX_STORE(ice_tlan_ctx, pf_num, 3, 65),
+ ICE_CTX_STORE(ice_tlan_ctx, vmvf_num, 10, 68),
+ ICE_CTX_STORE(ice_tlan_ctx, vmvf_type, 2, 78),
+ ICE_CTX_STORE(ice_tlan_ctx, src_vsi, 10, 80),
+ ICE_CTX_STORE(ice_tlan_ctx, tsyn_ena, 1, 90),
+ ICE_CTX_STORE(ice_tlan_ctx, alt_vlan, 1, 92),
+ ICE_CTX_STORE(ice_tlan_ctx, cpuid, 8, 93),
+ ICE_CTX_STORE(ice_tlan_ctx, wb_mode, 1, 101),
+ ICE_CTX_STORE(ice_tlan_ctx, tphrd_desc, 1, 102),
+ ICE_CTX_STORE(ice_tlan_ctx, tphrd, 1, 103),
+ ICE_CTX_STORE(ice_tlan_ctx, tphwr_desc, 1, 104),
+ ICE_CTX_STORE(ice_tlan_ctx, cmpq_id, 9, 105),
+ ICE_CTX_STORE(ice_tlan_ctx, qnum_in_func, 14, 114),
+ ICE_CTX_STORE(ice_tlan_ctx, itr_notification_mode, 1, 128),
+ ICE_CTX_STORE(ice_tlan_ctx, adjust_prof_id, 6, 129),
+ ICE_CTX_STORE(ice_tlan_ctx, qlen, 13, 135),
+ ICE_CTX_STORE(ice_tlan_ctx, quanta_prof_idx, 4, 148),
+ ICE_CTX_STORE(ice_tlan_ctx, tso_ena, 1, 152),
+ ICE_CTX_STORE(ice_tlan_ctx, tso_qnum, 11, 153),
+ ICE_CTX_STORE(ice_tlan_ctx, legacy_int, 1, 164),
+ ICE_CTX_STORE(ice_tlan_ctx, drop_ena, 1, 165),
+ ICE_CTX_STORE(ice_tlan_ctx, cache_prof_idx, 2, 166),
+ ICE_CTX_STORE(ice_tlan_ctx, pkt_shaper_prof_idx, 3, 168),
+ ICE_CTX_STORE(ice_tlan_ctx, int_q_state, 110, 171),
+ { 0 }
+};
+
+/**
+ * ice_debug_cq
+ * @hw: pointer to the hardware structure
+ * @mask: debug mask
+ * @desc: pointer to control queue descriptor
+ * @buf: pointer to command buffer
+ * @buf_len: max length of buf
+ *
+ * Dumps debug log about control command with descriptor contents.
+ */
+void ice_debug_cq(struct ice_hw *hw, u32 __maybe_unused mask, void *desc,
+ void *buf, u16 buf_len)
+{
+ struct ice_aq_desc *cq_desc = (struct ice_aq_desc *)desc;
+ u16 len;
+
+#ifndef CONFIG_DYNAMIC_DEBUG
+ if (!(mask & hw->debug_mask))
+ return;
+#endif
+
+ if (!desc)
+ return;
+
+ len = le16_to_cpu(cq_desc->datalen);
+
+ ice_debug(hw, mask,
+ "CQ CMD: opcode 0x%04X, flags 0x%04X, datalen 0x%04X, retval 0x%04X\n",
+ le16_to_cpu(cq_desc->opcode),
+ le16_to_cpu(cq_desc->flags),
+ le16_to_cpu(cq_desc->datalen), le16_to_cpu(cq_desc->retval));
+ ice_debug(hw, mask, "\tcookie (h,l) 0x%08X 0x%08X\n",
+ le32_to_cpu(cq_desc->cookie_high),
+ le32_to_cpu(cq_desc->cookie_low));
+ ice_debug(hw, mask, "\tparam (0,1) 0x%08X 0x%08X\n",
+ le32_to_cpu(cq_desc->params.generic.param0),
+ le32_to_cpu(cq_desc->params.generic.param1));
+ ice_debug(hw, mask, "\taddr (h,l) 0x%08X 0x%08X\n",
+ le32_to_cpu(cq_desc->params.generic.addr_high),
+ le32_to_cpu(cq_desc->params.generic.addr_low));
+ if (buf && cq_desc->datalen != 0) {
+ ice_debug(hw, mask, "Buffer:\n");
+ if (buf_len < len)
+ len = buf_len;
+
+ ice_debug_array(hw, mask, 16, 1, (u8 *)buf, len);
+ }
+}
+
+/* FW Admin Queue command wrappers */
+
+/**
+ * ice_aq_send_cmd - send FW Admin Queue command to FW Admin Queue
+ * @hw: pointer to the hw struct
+ * @desc: descriptor describing the command
+ * @buf: buffer to use for indirect commands (NULL for direct commands)
+ * @buf_size: size of buffer for indirect commands (0 for direct commands)
+ * @cd: pointer to command details structure
+ *
+ * Helper function to send FW Admin Queue commands to the FW Admin Queue.
+ */
+enum ice_status
+ice_aq_send_cmd(struct ice_hw *hw, struct ice_aq_desc *desc, void *buf,
+ u16 buf_size, struct ice_sq_cd *cd)
+{
+ return ice_sq_send_cmd(hw, &hw->adminq, desc, buf, buf_size, cd);
+}
+
+/**
+ * ice_aq_get_fw_ver
+ * @hw: pointer to the hw struct
+ * @cd: pointer to command details structure or NULL
+ *
+ * Get the firmware version (0x0001) from the admin queue commands
+ */
+enum ice_status ice_aq_get_fw_ver(struct ice_hw *hw, struct ice_sq_cd *cd)
+{
+ struct ice_aqc_get_ver *resp;
+ struct ice_aq_desc desc;
+ enum ice_status status;
+
+ resp = &desc.params.get_ver;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_ver);
+
+ status = ice_aq_send_cmd(hw, &desc, NULL, 0, cd);
+
+ if (!status) {
+ hw->fw_branch = resp->fw_branch;
+ hw->fw_maj_ver = resp->fw_major;
+ hw->fw_min_ver = resp->fw_minor;
+ hw->fw_patch = resp->fw_patch;
+ hw->fw_build = le32_to_cpu(resp->fw_build);
+ hw->api_branch = resp->api_branch;
+ hw->api_maj_ver = resp->api_major;
+ hw->api_min_ver = resp->api_minor;
+ hw->api_patch = resp->api_patch;
+ }
+
+ return status;
+}
+
+/**
+ * ice_aq_q_shutdown
+ * @hw: pointer to the hw struct
+ * @unloading: is the driver unloading itself
+ *
+ * Tell the Firmware that we're shutting down the AdminQ and whether
+ * or not the driver is unloading as well (0x0003).
+ */
+enum ice_status ice_aq_q_shutdown(struct ice_hw *hw, bool unloading)
+{
+ struct ice_aqc_q_shutdown *cmd;
+ struct ice_aq_desc desc;
+
+ cmd = &desc.params.q_shutdown;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_q_shutdown);
+
+ if (unloading)
+ cmd->driver_unloading = cpu_to_le32(ICE_AQC_DRIVER_UNLOADING);
+
+ return ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+}
+
+/**
+ * ice_aq_req_res
+ * @hw: pointer to the hw struct
+ * @res: resource id
+ * @access: access type
+ * @sdp_number: resource number
+ * @timeout: the maximum time in ms that the driver may hold the resource
+ * @cd: pointer to command details structure or NULL
+ *
+ * requests common resource using the admin queue commands (0x0008)
+ */
+static enum ice_status
+ice_aq_req_res(struct ice_hw *hw, enum ice_aq_res_ids res,
+ enum ice_aq_res_access_type access, u8 sdp_number, u32 *timeout,
+ struct ice_sq_cd *cd)
+{
+ struct ice_aqc_req_res *cmd_resp;
+ struct ice_aq_desc desc;
+ enum ice_status status;
+
+ cmd_resp = &desc.params.res_owner;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_req_res);
+
+ cmd_resp->res_id = cpu_to_le16(res);
+ cmd_resp->access_type = cpu_to_le16(access);
+ cmd_resp->res_number = cpu_to_le32(sdp_number);
+
+ status = ice_aq_send_cmd(hw, &desc, NULL, 0, cd);
+ /* The completion specifies the maximum time in ms that the driver
+ * may hold the resource in the Timeout field.
+ * If the resource is held by someone else, the command completes with
+ * busy return value and the timeout field indicates the maximum time
+ * the current owner of the resource has to free it.
+ */
+ if (!status || hw->adminq.sq_last_status == ICE_AQ_RC_EBUSY)
+ *timeout = le32_to_cpu(cmd_resp->timeout);
+
+ return status;
+}
+
+/**
+ * ice_aq_release_res
+ * @hw: pointer to the hw struct
+ * @res: resource id
+ * @sdp_number: resource number
+ * @cd: pointer to command details structure or NULL
+ *
+ * release common resource using the admin queue commands (0x0009)
+ */
+static enum ice_status
+ice_aq_release_res(struct ice_hw *hw, enum ice_aq_res_ids res, u8 sdp_number,
+ struct ice_sq_cd *cd)
+{
+ struct ice_aqc_req_res *cmd;
+ struct ice_aq_desc desc;
+
+ cmd = &desc.params.res_owner;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_release_res);
+
+ cmd->res_id = cpu_to_le16(res);
+ cmd->res_number = cpu_to_le32(sdp_number);
+
+ return ice_aq_send_cmd(hw, &desc, NULL, 0, cd);
+}
+
+/**
+ * ice_acquire_res
+ * @hw: pointer to the HW structure
+ * @res: resource id
+ * @access: access type (read or write)
+ *
+ * This function will attempt to acquire the ownership of a resource.
+ */
+enum ice_status
+ice_acquire_res(struct ice_hw *hw, enum ice_aq_res_ids res,
+ enum ice_aq_res_access_type access)
+{
+#define ICE_RES_POLLING_DELAY_MS 10
+ u32 delay = ICE_RES_POLLING_DELAY_MS;
+ enum ice_status status;
+ u32 time_left = 0;
+ u32 timeout;
+
+ status = ice_aq_req_res(hw, res, access, 0, &time_left, NULL);
+
+ /* An admin queue return code of ICE_AQ_RC_EEXIST means that another
+ * driver has previously acquired the resource and performed any
+ * necessary updates; in this case the caller does not obtain the
+ * resource and has no further work to do.
+ */
+ if (hw->adminq.sq_last_status == ICE_AQ_RC_EEXIST) {
+ status = ICE_ERR_AQ_NO_WORK;
+ goto ice_acquire_res_exit;
+ }
+
+ if (status)
+ ice_debug(hw, ICE_DBG_RES,
+ "resource %d acquire type %d failed.\n", res, access);
+
+ /* If necessary, poll until the current lock owner timeouts */
+ timeout = time_left;
+ while (status && timeout && time_left) {
+ mdelay(delay);
+ timeout = (timeout > delay) ? timeout - delay : 0;
+ status = ice_aq_req_res(hw, res, access, 0, &time_left, NULL);
+
+ if (hw->adminq.sq_last_status == ICE_AQ_RC_EEXIST) {
+ /* lock free, but no work to do */
+ status = ICE_ERR_AQ_NO_WORK;
+ break;
+ }
+
+ if (!status)
+ /* lock acquired */
+ break;
+ }
+ if (status && status != ICE_ERR_AQ_NO_WORK)
+ ice_debug(hw, ICE_DBG_RES, "resource acquire timed out.\n");
+
+ice_acquire_res_exit:
+ if (status == ICE_ERR_AQ_NO_WORK) {
+ if (access == ICE_RES_WRITE)
+ ice_debug(hw, ICE_DBG_RES,
+ "resource indicates no work to do.\n");
+ else
+ ice_debug(hw, ICE_DBG_RES,
+ "Warning: ICE_ERR_AQ_NO_WORK not expected\n");
+ }
+ return status;
+}
+
+/**
+ * ice_release_res
+ * @hw: pointer to the HW structure
+ * @res: resource id
+ *
+ * This function will release a resource using the proper Admin Command.
+ */
+void ice_release_res(struct ice_hw *hw, enum ice_aq_res_ids res)
+{
+ enum ice_status status;
+ u32 total_delay = 0;
+
+ status = ice_aq_release_res(hw, res, 0, NULL);
+
+ /* there are some rare cases when trying to release the resource
+ * results in an admin Q timeout, so handle them correctly
+ */
+ while ((status == ICE_ERR_AQ_TIMEOUT) &&
+ (total_delay < hw->adminq.sq_cmd_timeout)) {
+ mdelay(1);
+ status = ice_aq_release_res(hw, res, 0, NULL);
+ total_delay++;
+ }
+}
+
+/**
+ * ice_parse_caps - parse function/device capabilities
+ * @hw: pointer to the hw struct
+ * @buf: pointer to a buffer containing function/device capability records
+ * @cap_count: number of capability records in the list
+ * @opc: type of capabilities list to parse
+ *
+ * Helper function to parse function(0x000a)/device(0x000b) capabilities list.
+ */
+static void
+ice_parse_caps(struct ice_hw *hw, void *buf, u32 cap_count,
+ enum ice_adminq_opc opc)
+{
+ struct ice_aqc_list_caps_elem *cap_resp;
+ struct ice_hw_func_caps *func_p = NULL;
+ struct ice_hw_dev_caps *dev_p = NULL;
+ struct ice_hw_common_caps *caps;
+ u32 i;
+
+ if (!buf)
+ return;
+
+ cap_resp = (struct ice_aqc_list_caps_elem *)buf;
+
+ if (opc == ice_aqc_opc_list_dev_caps) {
+ dev_p = &hw->dev_caps;
+ caps = &dev_p->common_cap;
+ } else if (opc == ice_aqc_opc_list_func_caps) {
+ func_p = &hw->func_caps;
+ caps = &func_p->common_cap;
+ } else {
+ ice_debug(hw, ICE_DBG_INIT, "wrong opcode\n");
+ return;
+ }
+
+ for (i = 0; caps && i < cap_count; i++, cap_resp++) {
+ u32 logical_id = le32_to_cpu(cap_resp->logical_id);
+ u32 phys_id = le32_to_cpu(cap_resp->phys_id);
+ u32 number = le32_to_cpu(cap_resp->number);
+ u16 cap = le16_to_cpu(cap_resp->cap);
+
+ switch (cap) {
+ case ICE_AQC_CAPS_VSI:
+ if (dev_p) {
+ dev_p->num_vsi_allocd_to_host = number;
+ ice_debug(hw, ICE_DBG_INIT,
+ "HW caps: Dev.VSI cnt = %d\n",
+ dev_p->num_vsi_allocd_to_host);
+ } else if (func_p) {
+ func_p->guaranteed_num_vsi = number;
+ ice_debug(hw, ICE_DBG_INIT,
+ "HW caps: Func.VSI cnt = %d\n",
+ func_p->guaranteed_num_vsi);
+ }
+ break;
+ case ICE_AQC_CAPS_RSS:
+ caps->rss_table_size = number;
+ caps->rss_table_entry_width = logical_id;
+ ice_debug(hw, ICE_DBG_INIT,
+ "HW caps: RSS table size = %d\n",
+ caps->rss_table_size);
+ ice_debug(hw, ICE_DBG_INIT,
+ "HW caps: RSS table width = %d\n",
+ caps->rss_table_entry_width);
+ break;
+ case ICE_AQC_CAPS_RXQS:
+ caps->num_rxq = number;
+ caps->rxq_first_id = phys_id;
+ ice_debug(hw, ICE_DBG_INIT,
+ "HW caps: Num Rx Qs = %d\n", caps->num_rxq);
+ ice_debug(hw, ICE_DBG_INIT,
+ "HW caps: Rx first queue ID = %d\n",
+ caps->rxq_first_id);
+ break;
+ case ICE_AQC_CAPS_TXQS:
+ caps->num_txq = number;
+ caps->txq_first_id = phys_id;
+ ice_debug(hw, ICE_DBG_INIT,
+ "HW caps: Num Tx Qs = %d\n", caps->num_txq);
+ ice_debug(hw, ICE_DBG_INIT,
+ "HW caps: Tx first queue ID = %d\n",
+ caps->txq_first_id);
+ break;
+ case ICE_AQC_CAPS_MSIX:
+ caps->num_msix_vectors = number;
+ caps->msix_vector_first_id = phys_id;
+ ice_debug(hw, ICE_DBG_INIT,
+ "HW caps: MSIX vector count = %d\n",
+ caps->num_msix_vectors);
+ ice_debug(hw, ICE_DBG_INIT,
+ "HW caps: MSIX first vector index = %d\n",
+ caps->msix_vector_first_id);
+ break;
+ case ICE_AQC_CAPS_MAX_MTU:
+ caps->max_mtu = number;
+ if (dev_p)
+ ice_debug(hw, ICE_DBG_INIT,
+ "HW caps: Dev.MaxMTU = %d\n",
+ caps->max_mtu);
+ else if (func_p)
+ ice_debug(hw, ICE_DBG_INIT,
+ "HW caps: func.MaxMTU = %d\n",
+ caps->max_mtu);
+ break;
+ default:
+ ice_debug(hw, ICE_DBG_INIT,
+ "HW caps: Unknown capability[%d]: 0x%x\n", i,
+ cap);
+ break;
+ }
+ }
+}
+
+/**
+ * ice_aq_discover_caps - query function/device capabilities
+ * @hw: pointer to the hw struct
+ * @buf: a virtual buffer to hold the capabilities
+ * @buf_size: Size of the virtual buffer
+ * @data_size: Size of the returned data, or buf size needed if AQ err==ENOMEM
+ * @opc: capabilities type to discover - pass in the command opcode
+ * @cd: pointer to command details structure or NULL
+ *
+ * Get the function(0x000a)/device(0x000b) capabilities description from
+ * the firmware.
+ */
+static enum ice_status
+ice_aq_discover_caps(struct ice_hw *hw, void *buf, u16 buf_size, u16 *data_size,
+ enum ice_adminq_opc opc, struct ice_sq_cd *cd)
+{
+ struct ice_aqc_list_caps *cmd;
+ struct ice_aq_desc desc;
+ enum ice_status status;
+
+ cmd = &desc.params.get_cap;
+
+ if (opc != ice_aqc_opc_list_func_caps &&
+ opc != ice_aqc_opc_list_dev_caps)
+ return ICE_ERR_PARAM;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, opc);
+
+ status = ice_aq_send_cmd(hw, &desc, buf, buf_size, cd);
+ if (!status)
+ ice_parse_caps(hw, buf, le32_to_cpu(cmd->count), opc);
+ *data_size = le16_to_cpu(desc.datalen);
+
+ return status;
+}
+
+/**
+ * ice_get_caps - get info about the HW
+ * @hw: pointer to the hardware structure
+ */
+enum ice_status ice_get_caps(struct ice_hw *hw)
+{
+ enum ice_status status;
+ u16 data_size = 0;
+ u16 cbuf_len;
+ u8 retries;
+
+ /* The driver doesn't know how many capabilities the device will return
+ * so the buffer size required isn't known ahead of time. The driver
+ * starts with cbuf_len and if this turns out to be insufficient, the
+ * device returns ICE_AQ_RC_ENOMEM and also the buffer size it needs.
+ * The driver then allocates the buffer of this size and retries the
+ * operation. So it follows that the retry count is 2.
+ */
+#define ICE_GET_CAP_BUF_COUNT 40
+#define ICE_GET_CAP_RETRY_COUNT 2
+
+ cbuf_len = ICE_GET_CAP_BUF_COUNT *
+ sizeof(struct ice_aqc_list_caps_elem);
+
+ retries = ICE_GET_CAP_RETRY_COUNT;
+
+ do {
+ void *cbuf;
+
+ cbuf = devm_kzalloc(ice_hw_to_dev(hw), cbuf_len, GFP_KERNEL);
+ if (!cbuf)
+ return ICE_ERR_NO_MEMORY;
+
+ status = ice_aq_discover_caps(hw, cbuf, cbuf_len, &data_size,
+ ice_aqc_opc_list_func_caps, NULL);
+ devm_kfree(ice_hw_to_dev(hw), cbuf);
+
+ if (!status || hw->adminq.sq_last_status != ICE_AQ_RC_ENOMEM)
+ break;
+
+ /* If ENOMEM is returned, try again with bigger buffer */
+ cbuf_len = data_size;
+ } while (--retries);
+
+ return status;
+}
+
+/**
+ * ice_aq_manage_mac_write - manage MAC address write command
+ * @hw: pointer to the hw struct
+ * @mac_addr: MAC address to be written as LAA/LAA+WoL/Port address
+ * @flags: flags to control write behavior
+ * @cd: pointer to command details structure or NULL
+ *
+ * This function is used to write MAC address to the NVM (0x0108).
+ */
+enum ice_status
+ice_aq_manage_mac_write(struct ice_hw *hw, u8 *mac_addr, u8 flags,
+ struct ice_sq_cd *cd)
+{
+ struct ice_aqc_manage_mac_write *cmd;
+ struct ice_aq_desc desc;
+
+ cmd = &desc.params.mac_write;
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_manage_mac_write);
+
+ cmd->flags = flags;
+
+ /* Prep values for flags, sah, sal */
+ cmd->sah = htons(*((u16 *)mac_addr));
+ cmd->sal = htonl(*((u32 *)(mac_addr + 2)));
+
+ return ice_aq_send_cmd(hw, &desc, NULL, 0, cd);
+}
+
+/**
+ * ice_aq_clear_pxe_mode
+ * @hw: pointer to the hw struct
+ *
+ * Tell the firmware that the driver is taking over from PXE (0x0110).
+ */
+static enum ice_status ice_aq_clear_pxe_mode(struct ice_hw *hw)
+{
+ struct ice_aq_desc desc;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_clear_pxe_mode);
+ desc.params.clear_pxe.rx_cnt = ICE_AQC_CLEAR_PXE_RX_CNT;
+
+ return ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+}
+
+/**
+ * ice_clear_pxe_mode - clear pxe operations mode
+ * @hw: pointer to the hw struct
+ *
+ * Make sure all PXE mode settings are cleared, including things
+ * like descriptor fetch/write-back mode.
+ */
+void ice_clear_pxe_mode(struct ice_hw *hw)
+{
+ if (ice_check_sq_alive(hw, &hw->adminq))
+ ice_aq_clear_pxe_mode(hw);
+}
+
+/**
+ * ice_aq_set_phy_cfg
+ * @hw: pointer to the hw struct
+ * @lport: logical port number
+ * @cfg: structure with PHY configuration data to be set
+ * @cd: pointer to command details structure or NULL
+ *
+ * Set the various PHY configuration parameters supported on the Port.
+ * One or more of the Set PHY config parameters may be ignored in an MFP
+ * mode as the PF may not have the privilege to set some of the PHY Config
+ * parameters. This status will be indicated by the command response (0x0601).
+ */
+static enum ice_status
+ice_aq_set_phy_cfg(struct ice_hw *hw, u8 lport,
+ struct ice_aqc_set_phy_cfg_data *cfg, struct ice_sq_cd *cd)
+{
+ struct ice_aqc_set_phy_cfg *cmd;
+ struct ice_aq_desc desc;
+
+ if (!cfg)
+ return ICE_ERR_PARAM;
+
+ cmd = &desc.params.set_phy;
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_set_phy_cfg);
+ cmd->lport_num = lport;
+
+ return ice_aq_send_cmd(hw, &desc, cfg, sizeof(*cfg), cd);
+}
+
+/**
+ * ice_update_link_info - update status of the HW network link
+ * @pi: port info structure of the interested logical port
+ */
+static enum ice_status
+ice_update_link_info(struct ice_port_info *pi)
+{
+ struct ice_aqc_get_phy_caps_data *pcaps;
+ struct ice_phy_info *phy_info;
+ enum ice_status status;
+ struct ice_hw *hw;
+
+ if (!pi)
+ return ICE_ERR_PARAM;
+
+ hw = pi->hw;
+
+ pcaps = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*pcaps), GFP_KERNEL);
+ if (!pcaps)
+ return ICE_ERR_NO_MEMORY;
+
+ phy_info = &pi->phy;
+ status = ice_aq_get_link_info(pi, true, NULL, NULL);
+ if (status)
+ goto out;
+
+ if (phy_info->link_info.link_info & ICE_AQ_MEDIA_AVAILABLE) {
+ status = ice_aq_get_phy_caps(pi, false, ICE_AQC_REPORT_SW_CFG,
+ pcaps, NULL);
+ if (status)
+ goto out;
+
+ memcpy(phy_info->link_info.module_type, &pcaps->module_type,
+ sizeof(phy_info->link_info.module_type));
+ }
+out:
+ devm_kfree(ice_hw_to_dev(hw), pcaps);
+ return status;
+}
+
+/**
+ * ice_set_fc
+ * @pi: port information structure
+ * @aq_failures: pointer to status code, specific to ice_set_fc routine
+ * @atomic_restart: enable automatic link update
+ *
+ * Set the requested flow control mode.
+ */
+enum ice_status
+ice_set_fc(struct ice_port_info *pi, u8 *aq_failures, bool atomic_restart)
+{
+ struct ice_aqc_set_phy_cfg_data cfg = { 0 };
+ struct ice_aqc_get_phy_caps_data *pcaps;
+ enum ice_status status;
+ u8 pause_mask = 0x0;
+ struct ice_hw *hw;
+
+ if (!pi)
+ return ICE_ERR_PARAM;
+ hw = pi->hw;
+ *aq_failures = ICE_SET_FC_AQ_FAIL_NONE;
+
+ switch (pi->fc.req_mode) {
+ case ICE_FC_FULL:
+ pause_mask |= ICE_AQC_PHY_EN_TX_LINK_PAUSE;
+ pause_mask |= ICE_AQC_PHY_EN_RX_LINK_PAUSE;
+ break;
+ case ICE_FC_RX_PAUSE:
+ pause_mask |= ICE_AQC_PHY_EN_RX_LINK_PAUSE;
+ break;
+ case ICE_FC_TX_PAUSE:
+ pause_mask |= ICE_AQC_PHY_EN_TX_LINK_PAUSE;
+ break;
+ default:
+ break;
+ }
+
+ pcaps = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*pcaps), GFP_KERNEL);
+ if (!pcaps)
+ return ICE_ERR_NO_MEMORY;
+
+ /* Get the current phy config */
+ status = ice_aq_get_phy_caps(pi, false, ICE_AQC_REPORT_SW_CFG, pcaps,
+ NULL);
+ if (status) {
+ *aq_failures = ICE_SET_FC_AQ_FAIL_GET;
+ goto out;
+ }
+
+ /* clear the old pause settings */
+ cfg.caps = pcaps->caps & ~(ICE_AQC_PHY_EN_TX_LINK_PAUSE |
+ ICE_AQC_PHY_EN_RX_LINK_PAUSE);
+ /* set the new capabilities */
+ cfg.caps |= pause_mask;
+ /* If the capabilities have changed, then set the new config */
+ if (cfg.caps != pcaps->caps) {
+ int retry_count, retry_max = 10;
+
+ /* Auto restart link so settings take effect */
+ if (atomic_restart)
+ cfg.caps |= ICE_AQ_PHY_ENA_ATOMIC_LINK;
+ /* Copy over all the old settings */
+ cfg.phy_type_low = pcaps->phy_type_low;
+ cfg.low_power_ctrl = pcaps->low_power_ctrl;
+ cfg.eee_cap = pcaps->eee_cap;
+ cfg.eeer_value = pcaps->eeer_value;
+ cfg.link_fec_opt = pcaps->link_fec_options;
+
+ status = ice_aq_set_phy_cfg(hw, pi->lport, &cfg, NULL);
+ if (status) {
+ *aq_failures = ICE_SET_FC_AQ_FAIL_SET;
+ goto out;
+ }
+
+ /* Update the link info
+ * It sometimes takes a really long time for link to
+ * come back from the atomic reset. Thus, we wait a
+ * little bit.
+ */
+ for (retry_count = 0; retry_count < retry_max; retry_count++) {
+ status = ice_update_link_info(pi);
+
+ if (!status)
+ break;
+
+ mdelay(100);
+ }
+
+ if (status)
+ *aq_failures = ICE_SET_FC_AQ_FAIL_UPDATE;
+ }
+
+out:
+ devm_kfree(ice_hw_to_dev(hw), pcaps);
+ return status;
+}
+
+/**
+ * ice_get_link_status - get status of the HW network link
+ * @pi: port information structure
+ * @link_up: pointer to bool (true/false = linkup/linkdown)
+ *
+ * Variable link_up is true if link is up, false if link is down.
+ * The variable link_up is invalid if status is non zero. As a
+ * result of this call, link status reporting becomes enabled
+ */
+enum ice_status ice_get_link_status(struct ice_port_info *pi, bool *link_up)
+{
+ struct ice_phy_info *phy_info;
+ enum ice_status status = 0;
+
+ if (!pi)
+ return ICE_ERR_PARAM;
+
+ phy_info = &pi->phy;
+
+ if (phy_info->get_link_info) {
+ status = ice_update_link_info(pi);
+
+ if (status)
+ ice_debug(pi->hw, ICE_DBG_LINK,
+ "get link status error, status = %d\n",
+ status);
+ }
+
+ *link_up = phy_info->link_info.link_info & ICE_AQ_LINK_UP;
+
+ return status;
+}
+
+/**
+ * ice_aq_set_link_restart_an
+ * @pi: pointer to the port information structure
+ * @ena_link: if true: enable link, if false: disable link
+ * @cd: pointer to command details structure or NULL
+ *
+ * Sets up the link and restarts the Auto-Negotiation over the link.
+ */
+enum ice_status
+ice_aq_set_link_restart_an(struct ice_port_info *pi, bool ena_link,
+ struct ice_sq_cd *cd)
+{
+ struct ice_aqc_restart_an *cmd;
+ struct ice_aq_desc desc;
+
+ cmd = &desc.params.restart_an;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_restart_an);
+
+ cmd->cmd_flags = ICE_AQC_RESTART_AN_LINK_RESTART;
+ cmd->lport_num = pi->lport;
+ if (ena_link)
+ cmd->cmd_flags |= ICE_AQC_RESTART_AN_LINK_ENABLE;
+ else
+ cmd->cmd_flags &= ~ICE_AQC_RESTART_AN_LINK_ENABLE;
+
+ return ice_aq_send_cmd(pi->hw, &desc, NULL, 0, cd);
+}
+
+/**
+ * ice_aq_set_event_mask
+ * @hw: pointer to the hw struct
+ * @port_num: port number of the physical function
+ * @mask: event mask to be set
+ * @cd: pointer to command details structure or NULL
+ *
+ * Set event mask (0x0613)
+ */
+enum ice_status
+ice_aq_set_event_mask(struct ice_hw *hw, u8 port_num, u16 mask,
+ struct ice_sq_cd *cd)
+{
+ struct ice_aqc_set_event_mask *cmd;
+ struct ice_aq_desc desc;
+
+ cmd = &desc.params.set_event_mask;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_set_event_mask);
+
+ cmd->lport_num = port_num;
+
+ cmd->event_mask = cpu_to_le16(mask);
+
+ return ice_aq_send_cmd(hw, &desc, NULL, 0, cd);
+}
+
+/**
+ * __ice_aq_get_set_rss_lut
+ * @hw: pointer to the hardware structure
+ * @vsi_id: VSI FW index
+ * @lut_type: LUT table type
+ * @lut: pointer to the LUT buffer provided by the caller
+ * @lut_size: size of the LUT buffer
+ * @glob_lut_idx: global LUT index
+ * @set: set true to set the table, false to get the table
+ *
+ * Internal function to get (0x0B05) or set (0x0B03) RSS look up table
+ */
+static enum ice_status
+__ice_aq_get_set_rss_lut(struct ice_hw *hw, u16 vsi_id, u8 lut_type, u8 *lut,
+ u16 lut_size, u8 glob_lut_idx, bool set)
+{
+ struct ice_aqc_get_set_rss_lut *cmd_resp;
+ struct ice_aq_desc desc;
+ enum ice_status status;
+ u16 flags = 0;
+
+ cmd_resp = &desc.params.get_set_rss_lut;
+
+ if (set) {
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_set_rss_lut);
+ desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD);
+ } else {
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_rss_lut);
+ }
+
+ cmd_resp->vsi_id = cpu_to_le16(((vsi_id <<
+ ICE_AQC_GSET_RSS_LUT_VSI_ID_S) &
+ ICE_AQC_GSET_RSS_LUT_VSI_ID_M) |
+ ICE_AQC_GSET_RSS_LUT_VSI_VALID);
+
+ switch (lut_type) {
+ case ICE_AQC_GSET_RSS_LUT_TABLE_TYPE_VSI:
+ case ICE_AQC_GSET_RSS_LUT_TABLE_TYPE_PF:
+ case ICE_AQC_GSET_RSS_LUT_TABLE_TYPE_GLOBAL:
+ flags |= ((lut_type << ICE_AQC_GSET_RSS_LUT_TABLE_TYPE_S) &
+ ICE_AQC_GSET_RSS_LUT_TABLE_TYPE_M);
+ break;
+ default:
+ status = ICE_ERR_PARAM;
+ goto ice_aq_get_set_rss_lut_exit;
+ }
+
+ if (lut_type == ICE_AQC_GSET_RSS_LUT_TABLE_TYPE_GLOBAL) {
+ flags |= ((glob_lut_idx << ICE_AQC_GSET_RSS_LUT_GLOBAL_IDX_S) &
+ ICE_AQC_GSET_RSS_LUT_GLOBAL_IDX_M);
+
+ if (!set)
+ goto ice_aq_get_set_rss_lut_send;
+ } else if (lut_type == ICE_AQC_GSET_RSS_LUT_TABLE_TYPE_PF) {
+ if (!set)
+ goto ice_aq_get_set_rss_lut_send;
+ } else {
+ goto ice_aq_get_set_rss_lut_send;
+ }
+
+ /* LUT size is only valid for Global and PF table types */
+ if (lut_size == ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_128) {
+ flags |= (ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_128_FLAG <<
+ ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_S) &
+ ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_M;
+ } else if (lut_size == ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_512) {
+ flags |= (ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_512_FLAG <<
+ ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_S) &
+ ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_M;
+ } else if ((lut_size == ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_2K) &&
+ (lut_type == ICE_AQC_GSET_RSS_LUT_TABLE_TYPE_PF)) {
+ flags |= (ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_2K_FLAG <<
+ ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_S) &
+ ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_M;
+ } else {
+ status = ICE_ERR_PARAM;
+ goto ice_aq_get_set_rss_lut_exit;
+ }
+
+ice_aq_get_set_rss_lut_send:
+ cmd_resp->flags = cpu_to_le16(flags);
+ status = ice_aq_send_cmd(hw, &desc, lut, lut_size, NULL);
+
+ice_aq_get_set_rss_lut_exit:
+ return status;
+}
+
+/**
+ * ice_aq_get_rss_lut
+ * @hw: pointer to the hardware structure
+ * @vsi_id: VSI FW index
+ * @lut_type: LUT table type
+ * @lut: pointer to the LUT buffer provided by the caller
+ * @lut_size: size of the LUT buffer
+ *
+ * get the RSS lookup table, PF or VSI type
+ */
+enum ice_status
+ice_aq_get_rss_lut(struct ice_hw *hw, u16 vsi_id, u8 lut_type, u8 *lut,
+ u16 lut_size)
+{
+ return __ice_aq_get_set_rss_lut(hw, vsi_id, lut_type, lut, lut_size, 0,
+ false);
+}
+
+/**
+ * ice_aq_set_rss_lut
+ * @hw: pointer to the hardware structure
+ * @vsi_id: VSI FW index
+ * @lut_type: LUT table type
+ * @lut: pointer to the LUT buffer provided by the caller
+ * @lut_size: size of the LUT buffer
+ *
+ * set the RSS lookup table, PF or VSI type
+ */
+enum ice_status
+ice_aq_set_rss_lut(struct ice_hw *hw, u16 vsi_id, u8 lut_type, u8 *lut,
+ u16 lut_size)
+{
+ return __ice_aq_get_set_rss_lut(hw, vsi_id, lut_type, lut, lut_size, 0,
+ true);
+}
+
+/**
+ * __ice_aq_get_set_rss_key
+ * @hw: pointer to the hw struct
+ * @vsi_id: VSI FW index
+ * @key: pointer to key info struct
+ * @set: set true to set the key, false to get the key
+ *
+ * get (0x0B04) or set (0x0B02) the RSS key per VSI
+ */
+static enum
+ice_status __ice_aq_get_set_rss_key(struct ice_hw *hw, u16 vsi_id,
+ struct ice_aqc_get_set_rss_keys *key,
+ bool set)
+{
+ struct ice_aqc_get_set_rss_key *cmd_resp;
+ u16 key_size = sizeof(*key);
+ struct ice_aq_desc desc;
+
+ cmd_resp = &desc.params.get_set_rss_key;
+
+ if (set) {
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_set_rss_key);
+ desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD);
+ } else {
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_rss_key);
+ }
+
+ cmd_resp->vsi_id = cpu_to_le16(((vsi_id <<
+ ICE_AQC_GSET_RSS_KEY_VSI_ID_S) &
+ ICE_AQC_GSET_RSS_KEY_VSI_ID_M) |
+ ICE_AQC_GSET_RSS_KEY_VSI_VALID);
+
+ return ice_aq_send_cmd(hw, &desc, key, key_size, NULL);
+}
+
+/**
+ * ice_aq_get_rss_key
+ * @hw: pointer to the hw struct
+ * @vsi_id: VSI FW index
+ * @key: pointer to key info struct
+ *
+ * get the RSS key per VSI
+ */
+enum ice_status
+ice_aq_get_rss_key(struct ice_hw *hw, u16 vsi_id,
+ struct ice_aqc_get_set_rss_keys *key)
+{
+ return __ice_aq_get_set_rss_key(hw, vsi_id, key, false);
+}
+
+/**
+ * ice_aq_set_rss_key
+ * @hw: pointer to the hw struct
+ * @vsi_id: VSI FW index
+ * @keys: pointer to key info struct
+ *
+ * set the RSS key per VSI
+ */
+enum ice_status
+ice_aq_set_rss_key(struct ice_hw *hw, u16 vsi_id,
+ struct ice_aqc_get_set_rss_keys *keys)
+{
+ return __ice_aq_get_set_rss_key(hw, vsi_id, keys, true);
+}
+
+/**
+ * ice_aq_add_lan_txq
+ * @hw: pointer to the hardware structure
+ * @num_qgrps: Number of added queue groups
+ * @qg_list: list of queue groups to be added
+ * @buf_size: size of buffer for indirect command
+ * @cd: pointer to command details structure or NULL
+ *
+ * Add Tx LAN queue (0x0C30)
+ *
+ * NOTE:
+ * Prior to calling add Tx LAN queue:
+ * Initialize the following as part of the Tx queue context:
+ * Completion queue ID if the queue uses Completion queue, Quanta profile,
+ * Cache profile and Packet shaper profile.
+ *
+ * After add Tx LAN queue AQ command is completed:
+ * Interrupts should be associated with specific queues,
+ * Association of Tx queue to Doorbell queue is not part of Add LAN Tx queue
+ * flow.
+ */
+static enum ice_status
+ice_aq_add_lan_txq(struct ice_hw *hw, u8 num_qgrps,
+ struct ice_aqc_add_tx_qgrp *qg_list, u16 buf_size,
+ struct ice_sq_cd *cd)
+{
+ u16 i, sum_header_size, sum_q_size = 0;
+ struct ice_aqc_add_tx_qgrp *list;
+ struct ice_aqc_add_txqs *cmd;
+ struct ice_aq_desc desc;
+
+ cmd = &desc.params.add_txqs;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_add_txqs);
+
+ if (!qg_list)
+ return ICE_ERR_PARAM;
+
+ if (num_qgrps > ICE_LAN_TXQ_MAX_QGRPS)
+ return ICE_ERR_PARAM;
+
+ sum_header_size = num_qgrps *
+ (sizeof(*qg_list) - sizeof(*qg_list->txqs));
+
+ list = qg_list;
+ for (i = 0; i < num_qgrps; i++) {
+ struct ice_aqc_add_txqs_perq *q = list->txqs;
+
+ sum_q_size += list->num_txqs * sizeof(*q);
+ list = (struct ice_aqc_add_tx_qgrp *)(q + list->num_txqs);
+ }
+
+ if (buf_size != (sum_header_size + sum_q_size))
+ return ICE_ERR_PARAM;
+
+ desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD);
+
+ cmd->num_qgrps = num_qgrps;
+
+ return ice_aq_send_cmd(hw, &desc, qg_list, buf_size, cd);
+}
+
+/**
+ * ice_aq_dis_lan_txq
+ * @hw: pointer to the hardware structure
+ * @num_qgrps: number of groups in the list
+ * @qg_list: the list of groups to disable
+ * @buf_size: the total size of the qg_list buffer in bytes
+ * @cd: pointer to command details structure or NULL
+ *
+ * Disable LAN Tx queue (0x0C31)
+ */
+static enum ice_status
+ice_aq_dis_lan_txq(struct ice_hw *hw, u8 num_qgrps,
+ struct ice_aqc_dis_txq_item *qg_list, u16 buf_size,
+ struct ice_sq_cd *cd)
+{
+ struct ice_aqc_dis_txqs *cmd;
+ struct ice_aq_desc desc;
+ u16 i, sz = 0;
+
+ cmd = &desc.params.dis_txqs;
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_dis_txqs);
+
+ if (!qg_list)
+ return ICE_ERR_PARAM;
+
+ if (num_qgrps > ICE_LAN_TXQ_MAX_QGRPS)
+ return ICE_ERR_PARAM;
+ desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD);
+ cmd->num_entries = num_qgrps;
+
+ for (i = 0; i < num_qgrps; ++i) {
+ /* Calculate the size taken up by the queue IDs in this group */
+ sz += qg_list[i].num_qs * sizeof(qg_list[i].q_id);
+
+ /* Add the size of the group header */
+ sz += sizeof(qg_list[i]) - sizeof(qg_list[i].q_id);
+
+ /* If the num of queues is even, add 2 bytes of padding */
+ if ((qg_list[i].num_qs % 2) == 0)
+ sz += 2;
+ }
+
+ if (buf_size != sz)
+ return ICE_ERR_PARAM;
+
+ return ice_aq_send_cmd(hw, &desc, qg_list, buf_size, cd);
+}
+
+/* End of FW Admin Queue command wrappers */
+
+/**
+ * ice_write_byte - write a byte to a packed context structure
+ * @src_ctx: the context structure to read from
+ * @dest_ctx: the context to be written to
+ * @ce_info: a description of the struct to be filled
+ */
+static void ice_write_byte(u8 *src_ctx, u8 *dest_ctx,
+ const struct ice_ctx_ele *ce_info)
+{
+ u8 src_byte, dest_byte, mask;
+ u8 *from, *dest;
+ u16 shift_width;
+
+ /* copy from the next struct field */
+ from = src_ctx + ce_info->offset;
+
+ /* prepare the bits and mask */
+ shift_width = ce_info->lsb % 8;
+ mask = (u8)(BIT(ce_info->width) - 1);
+
+ src_byte = *from;
+ src_byte &= mask;
+
+ /* shift to correct alignment */
+ mask <<= shift_width;
+ src_byte <<= shift_width;
+
+ /* get the current bits from the target bit string */
+ dest = dest_ctx + (ce_info->lsb / 8);
+
+ memcpy(&dest_byte, dest, sizeof(dest_byte));
+
+ dest_byte &= ~mask; /* get the bits not changing */
+ dest_byte |= src_byte; /* add in the new bits */
+
+ /* put it all back */
+ memcpy(dest, &dest_byte, sizeof(dest_byte));
+}
+
+/**
+ * ice_write_word - write a word to a packed context structure
+ * @src_ctx: the context structure to read from
+ * @dest_ctx: the context to be written to
+ * @ce_info: a description of the struct to be filled
+ */
+static void ice_write_word(u8 *src_ctx, u8 *dest_ctx,
+ const struct ice_ctx_ele *ce_info)
+{
+ u16 src_word, mask;
+ __le16 dest_word;
+ u8 *from, *dest;
+ u16 shift_width;
+
+ /* copy from the next struct field */
+ from = src_ctx + ce_info->offset;
+
+ /* prepare the bits and mask */
+ shift_width = ce_info->lsb % 8;
+ mask = BIT(ce_info->width) - 1;
+
+ /* don't swizzle the bits until after the mask because the mask bits
+ * will be in a different bit position on big endian machines
+ */
+ src_word = *(u16 *)from;
+ src_word &= mask;
+
+ /* shift to correct alignment */
+ mask <<= shift_width;
+ src_word <<= shift_width;
+
+ /* get the current bits from the target bit string */
+ dest = dest_ctx + (ce_info->lsb / 8);
+
+ memcpy(&dest_word, dest, sizeof(dest_word));
+
+ dest_word &= ~(cpu_to_le16(mask)); /* get the bits not changing */
+ dest_word |= cpu_to_le16(src_word); /* add in the new bits */
+
+ /* put it all back */
+ memcpy(dest, &dest_word, sizeof(dest_word));
+}
+
+/**
+ * ice_write_dword - write a dword to a packed context structure
+ * @src_ctx: the context structure to read from
+ * @dest_ctx: the context to be written to
+ * @ce_info: a description of the struct to be filled
+ */
+static void ice_write_dword(u8 *src_ctx, u8 *dest_ctx,
+ const struct ice_ctx_ele *ce_info)
+{
+ u32 src_dword, mask;
+ __le32 dest_dword;
+ u8 *from, *dest;
+ u16 shift_width;
+
+ /* copy from the next struct field */
+ from = src_ctx + ce_info->offset;
+
+ /* prepare the bits and mask */
+ shift_width = ce_info->lsb % 8;
+
+ /* if the field width is exactly 32 on an x86 machine, then the shift
+ * operation will not work because the SHL instructions count is masked
+ * to 5 bits so the shift will do nothing
+ */
+ if (ce_info->width < 32)
+ mask = BIT(ce_info->width) - 1;
+ else
+ mask = (u32)~0;
+
+ /* don't swizzle the bits until after the mask because the mask bits
+ * will be in a different bit position on big endian machines
+ */
+ src_dword = *(u32 *)from;
+ src_dword &= mask;
+
+ /* shift to correct alignment */
+ mask <<= shift_width;
+ src_dword <<= shift_width;
+
+ /* get the current bits from the target bit string */
+ dest = dest_ctx + (ce_info->lsb / 8);
+
+ memcpy(&dest_dword, dest, sizeof(dest_dword));
+
+ dest_dword &= ~(cpu_to_le32(mask)); /* get the bits not changing */
+ dest_dword |= cpu_to_le32(src_dword); /* add in the new bits */
+
+ /* put it all back */
+ memcpy(dest, &dest_dword, sizeof(dest_dword));
+}
+
+/**
+ * ice_write_qword - write a qword to a packed context structure
+ * @src_ctx: the context structure to read from
+ * @dest_ctx: the context to be written to
+ * @ce_info: a description of the struct to be filled
+ */
+static void ice_write_qword(u8 *src_ctx, u8 *dest_ctx,
+ const struct ice_ctx_ele *ce_info)
+{
+ u64 src_qword, mask;
+ __le64 dest_qword;
+ u8 *from, *dest;
+ u16 shift_width;
+
+ /* copy from the next struct field */
+ from = src_ctx + ce_info->offset;
+
+ /* prepare the bits and mask */
+ shift_width = ce_info->lsb % 8;
+
+ /* if the field width is exactly 64 on an x86 machine, then the shift
+ * operation will not work because the SHL instructions count is masked
+ * to 6 bits so the shift will do nothing
+ */
+ if (ce_info->width < 64)
+ mask = BIT_ULL(ce_info->width) - 1;
+ else
+ mask = (u64)~0;
+
+ /* don't swizzle the bits until after the mask because the mask bits
+ * will be in a different bit position on big endian machines
+ */
+ src_qword = *(u64 *)from;
+ src_qword &= mask;
+
+ /* shift to correct alignment */
+ mask <<= shift_width;
+ src_qword <<= shift_width;
+
+ /* get the current bits from the target bit string */
+ dest = dest_ctx + (ce_info->lsb / 8);
+
+ memcpy(&dest_qword, dest, sizeof(dest_qword));
+
+ dest_qword &= ~(cpu_to_le64(mask)); /* get the bits not changing */
+ dest_qword |= cpu_to_le64(src_qword); /* add in the new bits */
+
+ /* put it all back */
+ memcpy(dest, &dest_qword, sizeof(dest_qword));
+}
+
+/**
+ * ice_set_ctx - set context bits in packed structure
+ * @src_ctx: pointer to a generic non-packed context structure
+ * @dest_ctx: pointer to memory for the packed structure
+ * @ce_info: a description of the structure to be transformed
+ */
+enum ice_status
+ice_set_ctx(u8 *src_ctx, u8 *dest_ctx, const struct ice_ctx_ele *ce_info)
+{
+ int f;
+
+ for (f = 0; ce_info[f].width; f++) {
+ /* We have to deal with each element of the FW response
+ * using the correct size so that we are correct regardless
+ * of the endianness of the machine.
+ */
+ switch (ce_info[f].size_of) {
+ case sizeof(u8):
+ ice_write_byte(src_ctx, dest_ctx, &ce_info[f]);
+ break;
+ case sizeof(u16):
+ ice_write_word(src_ctx, dest_ctx, &ce_info[f]);
+ break;
+ case sizeof(u32):
+ ice_write_dword(src_ctx, dest_ctx, &ce_info[f]);
+ break;
+ case sizeof(u64):
+ ice_write_qword(src_ctx, dest_ctx, &ce_info[f]);
+ break;
+ default:
+ return ICE_ERR_INVAL_SIZE;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * ice_ena_vsi_txq
+ * @pi: port information structure
+ * @vsi_id: VSI id
+ * @tc: tc number
+ * @num_qgrps: Number of added queue groups
+ * @buf: list of queue groups to be added
+ * @buf_size: size of buffer for indirect command
+ * @cd: pointer to command details structure or NULL
+ *
+ * This function adds one lan q
+ */
+enum ice_status
+ice_ena_vsi_txq(struct ice_port_info *pi, u16 vsi_id, u8 tc, u8 num_qgrps,
+ struct ice_aqc_add_tx_qgrp *buf, u16 buf_size,
+ struct ice_sq_cd *cd)
+{
+ struct ice_aqc_txsched_elem_data node = { 0 };
+ struct ice_sched_node *parent;
+ enum ice_status status;
+ struct ice_hw *hw;
+
+ if (!pi || pi->port_state != ICE_SCHED_PORT_STATE_READY)
+ return ICE_ERR_CFG;
+
+ if (num_qgrps > 1 || buf->num_txqs > 1)
+ return ICE_ERR_MAX_LIMIT;
+
+ hw = pi->hw;
+
+ mutex_lock(&pi->sched_lock);
+
+ /* find a parent node */
+ parent = ice_sched_get_free_qparent(pi, vsi_id, tc,
+ ICE_SCHED_NODE_OWNER_LAN);
+ if (!parent) {
+ status = ICE_ERR_PARAM;
+ goto ena_txq_exit;
+ }
+ buf->parent_teid = parent->info.node_teid;
+ node.parent_teid = parent->info.node_teid;
+ /* Mark that the values in the "generic" section as valid. The default
+ * value in the "generic" section is zero. This means that :
+ * - Scheduling mode is Bytes Per Second (BPS), indicated by Bit 0.
+ * - 0 priority among siblings, indicated by Bit 1-3.
+ * - WFQ, indicated by Bit 4.
+ * - 0 Adjustment value is used in PSM credit update flow, indicated by
+ * Bit 5-6.
+ * - Bit 7 is reserved.
+ * Without setting the generic section as valid in valid_sections, the
+ * Admin Q command will fail with error code ICE_AQ_RC_EINVAL.
+ */
+ buf->txqs[0].info.valid_sections = ICE_AQC_ELEM_VALID_GENERIC;
+
+ /* add the lan q */
+ status = ice_aq_add_lan_txq(hw, num_qgrps, buf, buf_size, cd);
+ if (status)
+ goto ena_txq_exit;
+
+ node.node_teid = buf->txqs[0].q_teid;
+ node.data.elem_type = ICE_AQC_ELEM_TYPE_LEAF;
+
+ /* add a leaf node into schduler tree q layer */
+ status = ice_sched_add_node(pi, hw->num_tx_sched_layers - 1, &node);
+
+ena_txq_exit:
+ mutex_unlock(&pi->sched_lock);
+ return status;
+}
+
+/**
+ * ice_dis_vsi_txq
+ * @pi: port information structure
+ * @num_queues: number of queues
+ * @q_ids: pointer to the q_id array
+ * @q_teids: pointer to queue node teids
+ * @cd: pointer to command details structure or NULL
+ *
+ * This function removes queues and their corresponding nodes in SW DB
+ */
+enum ice_status
+ice_dis_vsi_txq(struct ice_port_info *pi, u8 num_queues, u16 *q_ids,
+ u32 *q_teids, struct ice_sq_cd *cd)
+{
+ enum ice_status status = ICE_ERR_DOES_NOT_EXIST;
+ struct ice_aqc_dis_txq_item qg_list;
+ u16 i;
+
+ if (!pi || pi->port_state != ICE_SCHED_PORT_STATE_READY)
+ return ICE_ERR_CFG;
+
+ mutex_lock(&pi->sched_lock);
+
+ for (i = 0; i < num_queues; i++) {
+ struct ice_sched_node *node;
+
+ node = ice_sched_find_node_by_teid(pi->root, q_teids[i]);
+ if (!node)
+ continue;
+ qg_list.parent_teid = node->info.parent_teid;
+ qg_list.num_qs = 1;
+ qg_list.q_id[0] = cpu_to_le16(q_ids[i]);
+ status = ice_aq_dis_lan_txq(pi->hw, 1, &qg_list,
+ sizeof(qg_list), cd);
+
+ if (status)
+ break;
+ ice_free_sched_node(pi, node);
+ }
+ mutex_unlock(&pi->sched_lock);
+ return status;
+}
+
+/**
+ * ice_cfg_vsi_qs - configure the new/exisiting VSI queues
+ * @pi: port information structure
+ * @vsi_id: VSI Id
+ * @tc_bitmap: TC bitmap
+ * @maxqs: max queues array per TC
+ * @owner: lan or rdma
+ *
+ * This function adds/updates the VSI queues per TC.
+ */
+static enum ice_status
+ice_cfg_vsi_qs(struct ice_port_info *pi, u16 vsi_id, u8 tc_bitmap,
+ u16 *maxqs, u8 owner)
+{
+ enum ice_status status = 0;
+ u8 i;
+
+ if (!pi || pi->port_state != ICE_SCHED_PORT_STATE_READY)
+ return ICE_ERR_CFG;
+
+ mutex_lock(&pi->sched_lock);
+
+ for (i = 0; i < ICE_MAX_TRAFFIC_CLASS; i++) {
+ /* configuration is possible only if TC node is present */
+ if (!ice_sched_get_tc_node(pi, i))
+ continue;
+
+ status = ice_sched_cfg_vsi(pi, vsi_id, i, maxqs[i], owner,
+ ice_is_tc_ena(tc_bitmap, i));
+ if (status)
+ break;
+ }
+
+ mutex_unlock(&pi->sched_lock);
+ return status;
+}
+
+/**
+ * ice_cfg_vsi_lan - configure VSI lan queues
+ * @pi: port information structure
+ * @vsi_id: VSI Id
+ * @tc_bitmap: TC bitmap
+ * @max_lanqs: max lan queues array per TC
+ *
+ * This function adds/updates the VSI lan queues per TC.
+ */
+enum ice_status
+ice_cfg_vsi_lan(struct ice_port_info *pi, u16 vsi_id, u8 tc_bitmap,
+ u16 *max_lanqs)
+{
+ return ice_cfg_vsi_qs(pi, vsi_id, tc_bitmap, max_lanqs,
+ ICE_SCHED_NODE_OWNER_LAN);
+}
diff --git a/drivers/net/ethernet/intel/ice/ice_common.h b/drivers/net/ethernet/intel/ice/ice_common.h
new file mode 100644
index 000000000000..9a5519130af1
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_common.h
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018, Intel Corporation. */
+
+#ifndef _ICE_COMMON_H_
+#define _ICE_COMMON_H_
+
+#include "ice.h"
+#include "ice_type.h"
+#include "ice_switch.h"
+
+void ice_debug_cq(struct ice_hw *hw, u32 mask, void *desc, void *buf,
+ u16 buf_len);
+enum ice_status ice_init_hw(struct ice_hw *hw);
+void ice_deinit_hw(struct ice_hw *hw);
+enum ice_status ice_check_reset(struct ice_hw *hw);
+enum ice_status ice_reset(struct ice_hw *hw, enum ice_reset_req req);
+enum ice_status ice_init_all_ctrlq(struct ice_hw *hw);
+void ice_shutdown_all_ctrlq(struct ice_hw *hw);
+enum ice_status
+ice_clean_rq_elem(struct ice_hw *hw, struct ice_ctl_q_info *cq,
+ struct ice_rq_event_info *e, u16 *pending);
+enum ice_status
+ice_get_link_status(struct ice_port_info *pi, bool *link_up);
+enum ice_status
+ice_acquire_res(struct ice_hw *hw, enum ice_aq_res_ids res,
+ enum ice_aq_res_access_type access);
+void ice_release_res(struct ice_hw *hw, enum ice_aq_res_ids res);
+enum ice_status ice_init_nvm(struct ice_hw *hw);
+enum ice_status
+ice_sq_send_cmd(struct ice_hw *hw, struct ice_ctl_q_info *cq,
+ struct ice_aq_desc *desc, void *buf, u16 buf_size,
+ struct ice_sq_cd *cd);
+void ice_clear_pxe_mode(struct ice_hw *hw);
+enum ice_status ice_get_caps(struct ice_hw *hw);
+enum ice_status
+ice_write_rxq_ctx(struct ice_hw *hw, struct ice_rlan_ctx *rlan_ctx,
+ u32 rxq_index);
+
+enum ice_status
+ice_aq_get_rss_lut(struct ice_hw *hw, u16 vsi_id, u8 lut_type, u8 *lut,
+ u16 lut_size);
+enum ice_status
+ice_aq_set_rss_lut(struct ice_hw *hw, u16 vsi_id, u8 lut_type, u8 *lut,
+ u16 lut_size);
+enum ice_status
+ice_aq_get_rss_key(struct ice_hw *hw, u16 vsi_id,
+ struct ice_aqc_get_set_rss_keys *keys);
+enum ice_status
+ice_aq_set_rss_key(struct ice_hw *hw, u16 vsi_id,
+ struct ice_aqc_get_set_rss_keys *keys);
+bool ice_check_sq_alive(struct ice_hw *hw, struct ice_ctl_q_info *cq);
+enum ice_status ice_aq_q_shutdown(struct ice_hw *hw, bool unloading);
+void ice_fill_dflt_direct_cmd_desc(struct ice_aq_desc *desc, u16 opcode);
+extern const struct ice_ctx_ele ice_tlan_ctx_info[];
+enum ice_status
+ice_set_ctx(u8 *src_ctx, u8 *dest_ctx, const struct ice_ctx_ele *ce_info);
+enum ice_status
+ice_aq_send_cmd(struct ice_hw *hw, struct ice_aq_desc *desc,
+ void *buf, u16 buf_size, struct ice_sq_cd *cd);
+enum ice_status ice_aq_get_fw_ver(struct ice_hw *hw, struct ice_sq_cd *cd);
+enum ice_status
+ice_aq_manage_mac_write(struct ice_hw *hw, u8 *mac_addr, u8 flags,
+ struct ice_sq_cd *cd);
+enum ice_status ice_clear_pf_cfg(struct ice_hw *hw);
+enum ice_status
+ice_set_fc(struct ice_port_info *pi, u8 *aq_failures, bool atomic_restart);
+enum ice_status
+ice_aq_set_link_restart_an(struct ice_port_info *pi, bool ena_link,
+ struct ice_sq_cd *cd);
+enum ice_status
+ice_aq_get_link_info(struct ice_port_info *pi, bool ena_lse,
+ struct ice_link_status *link, struct ice_sq_cd *cd);
+enum ice_status
+ice_aq_set_event_mask(struct ice_hw *hw, u8 port_num, u16 mask,
+ struct ice_sq_cd *cd);
+enum ice_status
+ice_dis_vsi_txq(struct ice_port_info *pi, u8 num_queues, u16 *q_ids,
+ u32 *q_teids, struct ice_sq_cd *cmd_details);
+enum ice_status
+ice_cfg_vsi_lan(struct ice_port_info *pi, u16 vsi_id, u8 tc_bitmap,
+ u16 *max_lanqs);
+enum ice_status
+ice_ena_vsi_txq(struct ice_port_info *pi, u16 vsi_id, u8 tc, u8 num_qgrps,
+ struct ice_aqc_add_tx_qgrp *buf, u16 buf_size,
+ struct ice_sq_cd *cd);
+#endif /* _ICE_COMMON_H_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_controlq.c b/drivers/net/ethernet/intel/ice/ice_controlq.c
new file mode 100644
index 000000000000..5909a4407e38
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_controlq.c
@@ -0,0 +1,1066 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2018, Intel Corporation. */
+
+#include "ice_common.h"
+
+/**
+ * ice_adminq_init_regs - Initialize AdminQ registers
+ * @hw: pointer to the hardware structure
+ *
+ * This assumes the alloc_sq and alloc_rq functions have already been called
+ */
+static void ice_adminq_init_regs(struct ice_hw *hw)
+{
+ struct ice_ctl_q_info *cq = &hw->adminq;
+
+ cq->sq.head = PF_FW_ATQH;
+ cq->sq.tail = PF_FW_ATQT;
+ cq->sq.len = PF_FW_ATQLEN;
+ cq->sq.bah = PF_FW_ATQBAH;
+ cq->sq.bal = PF_FW_ATQBAL;
+ cq->sq.len_mask = PF_FW_ATQLEN_ATQLEN_M;
+ cq->sq.len_ena_mask = PF_FW_ATQLEN_ATQENABLE_M;
+ cq->sq.head_mask = PF_FW_ATQH_ATQH_M;
+
+ cq->rq.head = PF_FW_ARQH;
+ cq->rq.tail = PF_FW_ARQT;
+ cq->rq.len = PF_FW_ARQLEN;
+ cq->rq.bah = PF_FW_ARQBAH;
+ cq->rq.bal = PF_FW_ARQBAL;
+ cq->rq.len_mask = PF_FW_ARQLEN_ARQLEN_M;
+ cq->rq.len_ena_mask = PF_FW_ARQLEN_ARQENABLE_M;
+ cq->rq.head_mask = PF_FW_ARQH_ARQH_M;
+}
+
+/**
+ * ice_check_sq_alive
+ * @hw: pointer to the hw struct
+ * @cq: pointer to the specific Control queue
+ *
+ * Returns true if Queue is enabled else false.
+ */
+bool ice_check_sq_alive(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+ /* check both queue-length and queue-enable fields */
+ if (cq->sq.len && cq->sq.len_mask && cq->sq.len_ena_mask)
+ return (rd32(hw, cq->sq.len) & (cq->sq.len_mask |
+ cq->sq.len_ena_mask)) ==
+ (cq->num_sq_entries | cq->sq.len_ena_mask);
+
+ return false;
+}
+
+/**
+ * ice_alloc_ctrlq_sq_ring - Allocate Control Transmit Queue (ATQ) rings
+ * @hw: pointer to the hardware structure
+ * @cq: pointer to the specific Control queue
+ */
+static enum ice_status
+ice_alloc_ctrlq_sq_ring(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+ size_t size = cq->num_sq_entries * sizeof(struct ice_aq_desc);
+
+ cq->sq.desc_buf.va = dmam_alloc_coherent(ice_hw_to_dev(hw), size,
+ &cq->sq.desc_buf.pa,
+ GFP_KERNEL | __GFP_ZERO);
+ if (!cq->sq.desc_buf.va)
+ return ICE_ERR_NO_MEMORY;
+ cq->sq.desc_buf.size = size;
+
+ cq->sq.cmd_buf = devm_kcalloc(ice_hw_to_dev(hw), cq->num_sq_entries,
+ sizeof(struct ice_sq_cd), GFP_KERNEL);
+ if (!cq->sq.cmd_buf) {
+ dmam_free_coherent(ice_hw_to_dev(hw), cq->sq.desc_buf.size,
+ cq->sq.desc_buf.va, cq->sq.desc_buf.pa);
+ cq->sq.desc_buf.va = NULL;
+ cq->sq.desc_buf.pa = 0;
+ cq->sq.desc_buf.size = 0;
+ return ICE_ERR_NO_MEMORY;
+ }
+
+ return 0;
+}
+
+/**
+ * ice_alloc_ctrlq_rq_ring - Allocate Control Receive Queue (ARQ) rings
+ * @hw: pointer to the hardware structure
+ * @cq: pointer to the specific Control queue
+ */
+static enum ice_status
+ice_alloc_ctrlq_rq_ring(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+ size_t size = cq->num_rq_entries * sizeof(struct ice_aq_desc);
+
+ cq->rq.desc_buf.va = dmam_alloc_coherent(ice_hw_to_dev(hw), size,
+ &cq->rq.desc_buf.pa,
+ GFP_KERNEL | __GFP_ZERO);
+ if (!cq->rq.desc_buf.va)
+ return ICE_ERR_NO_MEMORY;
+ cq->rq.desc_buf.size = size;
+ return 0;
+}
+
+/**
+ * ice_free_ctrlq_sq_ring - Free Control Transmit Queue (ATQ) rings
+ * @hw: pointer to the hardware structure
+ * @cq: pointer to the specific Control queue
+ *
+ * This assumes the posted send buffers have already been cleaned
+ * and de-allocated
+ */
+static void ice_free_ctrlq_sq_ring(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+ dmam_free_coherent(ice_hw_to_dev(hw), cq->sq.desc_buf.size,
+ cq->sq.desc_buf.va, cq->sq.desc_buf.pa);
+ cq->sq.desc_buf.va = NULL;
+ cq->sq.desc_buf.pa = 0;
+ cq->sq.desc_buf.size = 0;
+}
+
+/**
+ * ice_free_ctrlq_rq_ring - Free Control Receive Queue (ARQ) rings
+ * @hw: pointer to the hardware structure
+ * @cq: pointer to the specific Control queue
+ *
+ * This assumes the posted receive buffers have already been cleaned
+ * and de-allocated
+ */
+static void ice_free_ctrlq_rq_ring(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+ dmam_free_coherent(ice_hw_to_dev(hw), cq->rq.desc_buf.size,
+ cq->rq.desc_buf.va, cq->rq.desc_buf.pa);
+ cq->rq.desc_buf.va = NULL;
+ cq->rq.desc_buf.pa = 0;
+ cq->rq.desc_buf.size = 0;
+}
+
+/**
+ * ice_alloc_rq_bufs - Allocate pre-posted buffers for the ARQ
+ * @hw: pointer to the hardware structure
+ * @cq: pointer to the specific Control queue
+ */
+static enum ice_status
+ice_alloc_rq_bufs(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+ int i;
+
+ /* We'll be allocating the buffer info memory first, then we can
+ * allocate the mapped buffers for the event processing
+ */
+ cq->rq.dma_head = devm_kcalloc(ice_hw_to_dev(hw), cq->num_rq_entries,
+ sizeof(cq->rq.desc_buf), GFP_KERNEL);
+ if (!cq->rq.dma_head)
+ return ICE_ERR_NO_MEMORY;
+ cq->rq.r.rq_bi = (struct ice_dma_mem *)cq->rq.dma_head;
+
+ /* allocate the mapped buffers */
+ for (i = 0; i < cq->num_rq_entries; i++) {
+ struct ice_aq_desc *desc;
+ struct ice_dma_mem *bi;
+
+ bi = &cq->rq.r.rq_bi[i];
+ bi->va = dmam_alloc_coherent(ice_hw_to_dev(hw),
+ cq->rq_buf_size, &bi->pa,
+ GFP_KERNEL | __GFP_ZERO);
+ if (!bi->va)
+ goto unwind_alloc_rq_bufs;
+ bi->size = cq->rq_buf_size;
+
+ /* now configure the descriptors for use */
+ desc = ICE_CTL_Q_DESC(cq->rq, i);
+
+ desc->flags = cpu_to_le16(ICE_AQ_FLAG_BUF);
+ if (cq->rq_buf_size > ICE_AQ_LG_BUF)
+ desc->flags |= cpu_to_le16(ICE_AQ_FLAG_LB);
+ desc->opcode = 0;
+ /* This is in accordance with Admin queue design, there is no
+ * register for buffer size configuration
+ */
+ desc->datalen = cpu_to_le16(bi->size);
+ desc->retval = 0;
+ desc->cookie_high = 0;
+ desc->cookie_low = 0;
+ desc->params.generic.addr_high =
+ cpu_to_le32(upper_32_bits(bi->pa));
+ desc->params.generic.addr_low =
+ cpu_to_le32(lower_32_bits(bi->pa));
+ desc->params.generic.param0 = 0;
+ desc->params.generic.param1 = 0;
+ }
+ return 0;
+
+unwind_alloc_rq_bufs:
+ /* don't try to free the one that failed... */
+ i--;
+ for (; i >= 0; i--) {
+ dmam_free_coherent(ice_hw_to_dev(hw), cq->rq.r.rq_bi[i].size,
+ cq->rq.r.rq_bi[i].va, cq->rq.r.rq_bi[i].pa);
+ cq->rq.r.rq_bi[i].va = NULL;
+ cq->rq.r.rq_bi[i].pa = 0;
+ cq->rq.r.rq_bi[i].size = 0;
+ }
+ devm_kfree(ice_hw_to_dev(hw), cq->rq.dma_head);
+
+ return ICE_ERR_NO_MEMORY;
+}
+
+/**
+ * ice_alloc_sq_bufs - Allocate empty buffer structs for the ATQ
+ * @hw: pointer to the hardware structure
+ * @cq: pointer to the specific Control queue
+ */
+static enum ice_status
+ice_alloc_sq_bufs(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+ int i;
+
+ /* No mapped memory needed yet, just the buffer info structures */
+ cq->sq.dma_head = devm_kcalloc(ice_hw_to_dev(hw), cq->num_sq_entries,
+ sizeof(cq->sq.desc_buf), GFP_KERNEL);
+ if (!cq->sq.dma_head)
+ return ICE_ERR_NO_MEMORY;
+ cq->sq.r.sq_bi = (struct ice_dma_mem *)cq->sq.dma_head;
+
+ /* allocate the mapped buffers */
+ for (i = 0; i < cq->num_sq_entries; i++) {
+ struct ice_dma_mem *bi;
+
+ bi = &cq->sq.r.sq_bi[i];
+ bi->va = dmam_alloc_coherent(ice_hw_to_dev(hw),
+ cq->sq_buf_size, &bi->pa,
+ GFP_KERNEL | __GFP_ZERO);
+ if (!bi->va)
+ goto unwind_alloc_sq_bufs;
+ bi->size = cq->sq_buf_size;
+ }
+ return 0;
+
+unwind_alloc_sq_bufs:
+ /* don't try to free the one that failed... */
+ i--;
+ for (; i >= 0; i--) {
+ dmam_free_coherent(ice_hw_to_dev(hw), cq->sq.r.sq_bi[i].size,
+ cq->sq.r.sq_bi[i].va, cq->sq.r.sq_bi[i].pa);
+ cq->sq.r.sq_bi[i].va = NULL;
+ cq->sq.r.sq_bi[i].pa = 0;
+ cq->sq.r.sq_bi[i].size = 0;
+ }
+ devm_kfree(ice_hw_to_dev(hw), cq->sq.dma_head);
+
+ return ICE_ERR_NO_MEMORY;
+}
+
+/**
+ * ice_free_rq_bufs - Free ARQ buffer info elements
+ * @hw: pointer to the hardware structure
+ * @cq: pointer to the specific Control queue
+ */
+static void ice_free_rq_bufs(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+ int i;
+
+ /* free descriptors */
+ for (i = 0; i < cq->num_rq_entries; i++) {
+ dmam_free_coherent(ice_hw_to_dev(hw), cq->rq.r.rq_bi[i].size,
+ cq->rq.r.rq_bi[i].va, cq->rq.r.rq_bi[i].pa);
+ cq->rq.r.rq_bi[i].va = NULL;
+ cq->rq.r.rq_bi[i].pa = 0;
+ cq->rq.r.rq_bi[i].size = 0;
+ }
+
+ /* free the dma header */
+ devm_kfree(ice_hw_to_dev(hw), cq->rq.dma_head);
+}
+
+/**
+ * ice_free_sq_bufs - Free ATQ buffer info elements
+ * @hw: pointer to the hardware structure
+ * @cq: pointer to the specific Control queue
+ */
+static void ice_free_sq_bufs(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+ int i;
+
+ /* only unmap if the address is non-NULL */
+ for (i = 0; i < cq->num_sq_entries; i++)
+ if (cq->sq.r.sq_bi[i].pa) {
+ dmam_free_coherent(ice_hw_to_dev(hw),
+ cq->sq.r.sq_bi[i].size,
+ cq->sq.r.sq_bi[i].va,
+ cq->sq.r.sq_bi[i].pa);
+ cq->sq.r.sq_bi[i].va = NULL;
+ cq->sq.r.sq_bi[i].pa = 0;
+ cq->sq.r.sq_bi[i].size = 0;
+ }
+
+ /* free the buffer info list */
+ devm_kfree(ice_hw_to_dev(hw), cq->sq.cmd_buf);
+
+ /* free the dma header */
+ devm_kfree(ice_hw_to_dev(hw), cq->sq.dma_head);
+}
+
+/**
+ * ice_cfg_sq_regs - configure Control ATQ registers
+ * @hw: pointer to the hardware structure
+ * @cq: pointer to the specific Control queue
+ *
+ * Configure base address and length registers for the transmit queue
+ */
+static enum ice_status
+ice_cfg_sq_regs(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+ u32 reg = 0;
+
+ /* Clear Head and Tail */
+ wr32(hw, cq->sq.head, 0);
+ wr32(hw, cq->sq.tail, 0);
+
+ /* set starting point */
+ wr32(hw, cq->sq.len, (cq->num_sq_entries | cq->sq.len_ena_mask));
+ wr32(hw, cq->sq.bal, lower_32_bits(cq->sq.desc_buf.pa));
+ wr32(hw, cq->sq.bah, upper_32_bits(cq->sq.desc_buf.pa));
+
+ /* Check one register to verify that config was applied */
+ reg = rd32(hw, cq->sq.bal);
+ if (reg != lower_32_bits(cq->sq.desc_buf.pa))
+ return ICE_ERR_AQ_ERROR;
+
+ return 0;
+}
+
+/**
+ * ice_cfg_rq_regs - configure Control ARQ register
+ * @hw: pointer to the hardware structure
+ * @cq: pointer to the specific Control queue
+ *
+ * Configure base address and length registers for the receive (event q)
+ */
+static enum ice_status
+ice_cfg_rq_regs(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+ u32 reg = 0;
+
+ /* Clear Head and Tail */
+ wr32(hw, cq->rq.head, 0);
+ wr32(hw, cq->rq.tail, 0);
+
+ /* set starting point */
+ wr32(hw, cq->rq.len, (cq->num_rq_entries | cq->rq.len_ena_mask));
+ wr32(hw, cq->rq.bal, lower_32_bits(cq->rq.desc_buf.pa));
+ wr32(hw, cq->rq.bah, upper_32_bits(cq->rq.desc_buf.pa));
+
+ /* Update tail in the HW to post pre-allocated buffers */
+ wr32(hw, cq->rq.tail, (u32)(cq->num_rq_entries - 1));
+
+ /* Check one register to verify that config was applied */
+ reg = rd32(hw, cq->rq.bal);
+ if (reg != lower_32_bits(cq->rq.desc_buf.pa))
+ return ICE_ERR_AQ_ERROR;
+
+ return 0;
+}
+
+/**
+ * ice_init_sq - main initialization routine for Control ATQ
+ * @hw: pointer to the hardware structure
+ * @cq: pointer to the specific Control queue
+ *
+ * This is the main initialization routine for the Control Send Queue
+ * Prior to calling this function, drivers *MUST* set the following fields
+ * in the cq->structure:
+ * - cq->num_sq_entries
+ * - cq->sq_buf_size
+ *
+ * Do *NOT* hold the lock when calling this as the memory allocation routines
+ * called are not going to be atomic context safe
+ */
+static enum ice_status ice_init_sq(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+ enum ice_status ret_code;
+
+ if (cq->sq.count > 0) {
+ /* queue already initialized */
+ ret_code = ICE_ERR_NOT_READY;
+ goto init_ctrlq_exit;
+ }
+
+ /* verify input for valid configuration */
+ if (!cq->num_sq_entries || !cq->sq_buf_size) {
+ ret_code = ICE_ERR_CFG;
+ goto init_ctrlq_exit;
+ }
+
+ cq->sq.next_to_use = 0;
+ cq->sq.next_to_clean = 0;
+
+ /* allocate the ring memory */
+ ret_code = ice_alloc_ctrlq_sq_ring(hw, cq);
+ if (ret_code)
+ goto init_ctrlq_exit;
+
+ /* allocate buffers in the rings */
+ ret_code = ice_alloc_sq_bufs(hw, cq);
+ if (ret_code)
+ goto init_ctrlq_free_rings;
+
+ /* initialize base registers */
+ ret_code = ice_cfg_sq_regs(hw, cq);
+ if (ret_code)
+ goto init_ctrlq_free_rings;
+
+ /* success! */
+ cq->sq.count = cq->num_sq_entries;
+ goto init_ctrlq_exit;
+
+init_ctrlq_free_rings:
+ ice_free_ctrlq_sq_ring(hw, cq);
+
+init_ctrlq_exit:
+ return ret_code;
+}
+
+/**
+ * ice_init_rq - initialize ARQ
+ * @hw: pointer to the hardware structure
+ * @cq: pointer to the specific Control queue
+ *
+ * The main initialization routine for the Admin Receive (Event) Queue.
+ * Prior to calling this function, drivers *MUST* set the following fields
+ * in the cq->structure:
+ * - cq->num_rq_entries
+ * - cq->rq_buf_size
+ *
+ * Do *NOT* hold the lock when calling this as the memory allocation routines
+ * called are not going to be atomic context safe
+ */
+static enum ice_status ice_init_rq(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+ enum ice_status ret_code;
+
+ if (cq->rq.count > 0) {
+ /* queue already initialized */
+ ret_code = ICE_ERR_NOT_READY;
+ goto init_ctrlq_exit;
+ }
+
+ /* verify input for valid configuration */
+ if (!cq->num_rq_entries || !cq->rq_buf_size) {
+ ret_code = ICE_ERR_CFG;
+ goto init_ctrlq_exit;
+ }
+
+ cq->rq.next_to_use = 0;
+ cq->rq.next_to_clean = 0;
+
+ /* allocate the ring memory */
+ ret_code = ice_alloc_ctrlq_rq_ring(hw, cq);
+ if (ret_code)
+ goto init_ctrlq_exit;
+
+ /* allocate buffers in the rings */
+ ret_code = ice_alloc_rq_bufs(hw, cq);
+ if (ret_code)
+ goto init_ctrlq_free_rings;
+
+ /* initialize base registers */
+ ret_code = ice_cfg_rq_regs(hw, cq);
+ if (ret_code)
+ goto init_ctrlq_free_rings;
+
+ /* success! */
+ cq->rq.count = cq->num_rq_entries;
+ goto init_ctrlq_exit;
+
+init_ctrlq_free_rings:
+ ice_free_ctrlq_rq_ring(hw, cq);
+
+init_ctrlq_exit:
+ return ret_code;
+}
+
+/**
+ * ice_shutdown_sq - shutdown the Control ATQ
+ * @hw: pointer to the hardware structure
+ * @cq: pointer to the specific Control queue
+ *
+ * The main shutdown routine for the Control Transmit Queue
+ */
+static enum ice_status
+ice_shutdown_sq(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+ enum ice_status ret_code = 0;
+
+ mutex_lock(&cq->sq_lock);
+
+ if (!cq->sq.count) {
+ ret_code = ICE_ERR_NOT_READY;
+ goto shutdown_sq_out;
+ }
+
+ /* Stop firmware AdminQ processing */
+ wr32(hw, cq->sq.head, 0);
+ wr32(hw, cq->sq.tail, 0);
+ wr32(hw, cq->sq.len, 0);
+ wr32(hw, cq->sq.bal, 0);
+ wr32(hw, cq->sq.bah, 0);
+
+ cq->sq.count = 0; /* to indicate uninitialized queue */
+
+ /* free ring buffers and the ring itself */
+ ice_free_sq_bufs(hw, cq);
+ ice_free_ctrlq_sq_ring(hw, cq);
+
+shutdown_sq_out:
+ mutex_unlock(&cq->sq_lock);
+ return ret_code;
+}
+
+/**
+ * ice_aq_ver_check - Check the reported AQ API version.
+ * @fw_branch: The "branch" of FW, typically describes the device type
+ * @fw_major: The major version of the FW API
+ * @fw_minor: The minor version increment of the FW API
+ *
+ * Checks if the driver should load on a given AQ API version.
+ *
+ * Return: 'true' iff the driver should attempt to load. 'false' otherwise.
+ */
+static bool ice_aq_ver_check(u8 fw_branch, u8 fw_major, u8 fw_minor)
+{
+ if (fw_branch != EXP_FW_API_VER_BRANCH)
+ return false;
+ if (fw_major != EXP_FW_API_VER_MAJOR)
+ return false;
+ if (fw_minor != EXP_FW_API_VER_MINOR)
+ return false;
+ return true;
+}
+
+/**
+ * ice_shutdown_rq - shutdown Control ARQ
+ * @hw: pointer to the hardware structure
+ * @cq: pointer to the specific Control queue
+ *
+ * The main shutdown routine for the Control Receive Queue
+ */
+static enum ice_status
+ice_shutdown_rq(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+ enum ice_status ret_code = 0;
+
+ mutex_lock(&cq->rq_lock);
+
+ if (!cq->rq.count) {
+ ret_code = ICE_ERR_NOT_READY;
+ goto shutdown_rq_out;
+ }
+
+ /* Stop Control Queue processing */
+ wr32(hw, cq->rq.head, 0);
+ wr32(hw, cq->rq.tail, 0);
+ wr32(hw, cq->rq.len, 0);
+ wr32(hw, cq->rq.bal, 0);
+ wr32(hw, cq->rq.bah, 0);
+
+ /* set rq.count to 0 to indicate uninitialized queue */
+ cq->rq.count = 0;
+
+ /* free ring buffers and the ring itself */
+ ice_free_rq_bufs(hw, cq);
+ ice_free_ctrlq_rq_ring(hw, cq);
+
+shutdown_rq_out:
+ mutex_unlock(&cq->rq_lock);
+ return ret_code;
+}
+
+/**
+ * ice_init_check_adminq - Check version for Admin Queue to know if its alive
+ * @hw: pointer to the hardware structure
+ */
+static enum ice_status ice_init_check_adminq(struct ice_hw *hw)
+{
+ struct ice_ctl_q_info *cq = &hw->adminq;
+ enum ice_status status;
+
+ status = ice_aq_get_fw_ver(hw, NULL);
+ if (status)
+ goto init_ctrlq_free_rq;
+
+ if (!ice_aq_ver_check(hw->api_branch, hw->api_maj_ver,
+ hw->api_min_ver)) {
+ status = ICE_ERR_FW_API_VER;
+ goto init_ctrlq_free_rq;
+ }
+
+ return 0;
+
+init_ctrlq_free_rq:
+ ice_shutdown_rq(hw, cq);
+ ice_shutdown_sq(hw, cq);
+ mutex_destroy(&cq->sq_lock);
+ mutex_destroy(&cq->rq_lock);
+ return status;
+}
+
+/**
+ * ice_init_ctrlq - main initialization routine for any control Queue
+ * @hw: pointer to the hardware structure
+ * @q_type: specific Control queue type
+ *
+ * Prior to calling this function, drivers *MUST* set the following fields
+ * in the cq->structure:
+ * - cq->num_sq_entries
+ * - cq->num_rq_entries
+ * - cq->rq_buf_size
+ * - cq->sq_buf_size
+ *
+ */
+static enum ice_status ice_init_ctrlq(struct ice_hw *hw, enum ice_ctl_q q_type)
+{
+ struct ice_ctl_q_info *cq;
+ enum ice_status ret_code;
+
+ switch (q_type) {
+ case ICE_CTL_Q_ADMIN:
+ ice_adminq_init_regs(hw);
+ cq = &hw->adminq;
+ break;
+ default:
+ return ICE_ERR_PARAM;
+ }
+ cq->qtype = q_type;
+
+ /* verify input for valid configuration */
+ if (!cq->num_rq_entries || !cq->num_sq_entries ||
+ !cq->rq_buf_size || !cq->sq_buf_size) {
+ return ICE_ERR_CFG;
+ }
+ mutex_init(&cq->sq_lock);
+ mutex_init(&cq->rq_lock);
+
+ /* setup SQ command write back timeout */
+ cq->sq_cmd_timeout = ICE_CTL_Q_SQ_CMD_TIMEOUT;
+
+ /* allocate the ATQ */
+ ret_code = ice_init_sq(hw, cq);
+ if (ret_code)
+ goto init_ctrlq_destroy_locks;
+
+ /* allocate the ARQ */
+ ret_code = ice_init_rq(hw, cq);
+ if (ret_code)
+ goto init_ctrlq_free_sq;
+
+ /* success! */
+ return 0;
+
+init_ctrlq_free_sq:
+ ice_shutdown_sq(hw, cq);
+init_ctrlq_destroy_locks:
+ mutex_destroy(&cq->sq_lock);
+ mutex_destroy(&cq->rq_lock);
+ return ret_code;
+}
+
+/**
+ * ice_init_all_ctrlq - main initialization routine for all control queues
+ * @hw: pointer to the hardware structure
+ *
+ * Prior to calling this function, drivers *MUST* set the following fields
+ * in the cq->structure for all control queues:
+ * - cq->num_sq_entries
+ * - cq->num_rq_entries
+ * - cq->rq_buf_size
+ * - cq->sq_buf_size
+ */
+enum ice_status ice_init_all_ctrlq(struct ice_hw *hw)
+{
+ enum ice_status ret_code;
+
+ /* Init FW admin queue */
+ ret_code = ice_init_ctrlq(hw, ICE_CTL_Q_ADMIN);
+ if (ret_code)
+ return ret_code;
+
+ return ice_init_check_adminq(hw);
+}
+
+/**
+ * ice_shutdown_ctrlq - shutdown routine for any control queue
+ * @hw: pointer to the hardware structure
+ * @q_type: specific Control queue type
+ */
+static void ice_shutdown_ctrlq(struct ice_hw *hw, enum ice_ctl_q q_type)
+{
+ struct ice_ctl_q_info *cq;
+
+ switch (q_type) {
+ case ICE_CTL_Q_ADMIN:
+ cq = &hw->adminq;
+ if (ice_check_sq_alive(hw, cq))
+ ice_aq_q_shutdown(hw, true);
+ break;
+ default:
+ return;
+ }
+
+ ice_shutdown_sq(hw, cq);
+ ice_shutdown_rq(hw, cq);
+ mutex_destroy(&cq->sq_lock);
+ mutex_destroy(&cq->rq_lock);
+}
+
+/**
+ * ice_shutdown_all_ctrlq - shutdown routine for all control queues
+ * @hw: pointer to the hardware structure
+ */
+void ice_shutdown_all_ctrlq(struct ice_hw *hw)
+{
+ /* Shutdown FW admin queue */
+ ice_shutdown_ctrlq(hw, ICE_CTL_Q_ADMIN);
+}
+
+/**
+ * ice_clean_sq - cleans Admin send queue (ATQ)
+ * @hw: pointer to the hardware structure
+ * @cq: pointer to the specific Control queue
+ *
+ * returns the number of free desc
+ */
+static u16 ice_clean_sq(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+ struct ice_ctl_q_ring *sq = &cq->sq;
+ u16 ntc = sq->next_to_clean;
+ struct ice_sq_cd *details;
+ struct ice_aq_desc *desc;
+
+ desc = ICE_CTL_Q_DESC(*sq, ntc);
+ details = ICE_CTL_Q_DETAILS(*sq, ntc);
+
+ while (rd32(hw, cq->sq.head) != ntc) {
+ ice_debug(hw, ICE_DBG_AQ_MSG,
+ "ntc %d head %d.\n", ntc, rd32(hw, cq->sq.head));
+ memset(desc, 0, sizeof(*desc));
+ memset(details, 0, sizeof(*details));
+ ntc++;
+ if (ntc == sq->count)
+ ntc = 0;
+ desc = ICE_CTL_Q_DESC(*sq, ntc);
+ details = ICE_CTL_Q_DETAILS(*sq, ntc);
+ }
+
+ sq->next_to_clean = ntc;
+
+ return ICE_CTL_Q_DESC_UNUSED(sq);
+}
+
+/**
+ * ice_sq_done - check if FW has processed the Admin Send Queue (ATQ)
+ * @hw: pointer to the hw struct
+ * @cq: pointer to the specific Control queue
+ *
+ * Returns true if the firmware has processed all descriptors on the
+ * admin send queue. Returns false if there are still requests pending.
+ */
+static bool ice_sq_done(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+ /* AQ designers suggest use of head for better
+ * timing reliability than DD bit
+ */
+ return rd32(hw, cq->sq.head) == cq->sq.next_to_use;
+}
+
+/**
+ * ice_sq_send_cmd - send command to Control Queue (ATQ)
+ * @hw: pointer to the hw struct
+ * @cq: pointer to the specific Control queue
+ * @desc: prefilled descriptor describing the command (non DMA mem)
+ * @buf: buffer to use for indirect commands (or NULL for direct commands)
+ * @buf_size: size of buffer for indirect commands (or 0 for direct commands)
+ * @cd: pointer to command details structure
+ *
+ * This is the main send command routine for the ATQ. It runs the q,
+ * cleans the queue, etc.
+ */
+enum ice_status
+ice_sq_send_cmd(struct ice_hw *hw, struct ice_ctl_q_info *cq,
+ struct ice_aq_desc *desc, void *buf, u16 buf_size,
+ struct ice_sq_cd *cd)
+{
+ struct ice_dma_mem *dma_buf = NULL;
+ struct ice_aq_desc *desc_on_ring;
+ bool cmd_completed = false;
+ enum ice_status status = 0;
+ struct ice_sq_cd *details;
+ u32 total_delay = 0;
+ u16 retval = 0;
+ u32 val = 0;
+
+ mutex_lock(&cq->sq_lock);
+
+ cq->sq_last_status = ICE_AQ_RC_OK;
+
+ if (!cq->sq.count) {
+ ice_debug(hw, ICE_DBG_AQ_MSG,
+ "Control Send queue not initialized.\n");
+ status = ICE_ERR_AQ_EMPTY;
+ goto sq_send_command_error;
+ }
+
+ if ((buf && !buf_size) || (!buf && buf_size)) {
+ status = ICE_ERR_PARAM;
+ goto sq_send_command_error;
+ }
+
+ if (buf) {
+ if (buf_size > cq->sq_buf_size) {
+ ice_debug(hw, ICE_DBG_AQ_MSG,
+ "Invalid buffer size for Control Send queue: %d.\n",
+ buf_size);
+ status = ICE_ERR_INVAL_SIZE;
+ goto sq_send_command_error;
+ }
+
+ desc->flags |= cpu_to_le16(ICE_AQ_FLAG_BUF);
+ if (buf_size > ICE_AQ_LG_BUF)
+ desc->flags |= cpu_to_le16(ICE_AQ_FLAG_LB);
+ }
+
+ val = rd32(hw, cq->sq.head);
+ if (val >= cq->num_sq_entries) {
+ ice_debug(hw, ICE_DBG_AQ_MSG,
+ "head overrun at %d in the Control Send Queue ring\n",
+ val);
+ status = ICE_ERR_AQ_EMPTY;
+ goto sq_send_command_error;
+ }
+
+ details = ICE_CTL_Q_DETAILS(cq->sq, cq->sq.next_to_use);
+ if (cd)
+ memcpy(details, cd, sizeof(*details));
+ else
+ memset(details, 0, sizeof(*details));
+
+ /* Call clean and check queue available function to reclaim the
+ * descriptors that were processed by FW/MBX; the function returns the
+ * number of desc available. The clean function called here could be
+ * called in a separate thread in case of asynchronous completions.
+ */
+ if (ice_clean_sq(hw, cq) == 0) {
+ ice_debug(hw, ICE_DBG_AQ_MSG,
+ "Error: Control Send Queue is full.\n");
+ status = ICE_ERR_AQ_FULL;
+ goto sq_send_command_error;
+ }
+
+ /* initialize the temp desc pointer with the right desc */
+ desc_on_ring = ICE_CTL_Q_DESC(cq->sq, cq->sq.next_to_use);
+
+ /* if the desc is available copy the temp desc to the right place */
+ memcpy(desc_on_ring, desc, sizeof(*desc_on_ring));
+
+ /* if buf is not NULL assume indirect command */
+ if (buf) {
+ dma_buf = &cq->sq.r.sq_bi[cq->sq.next_to_use];
+ /* copy the user buf into the respective DMA buf */
+ memcpy(dma_buf->va, buf, buf_size);
+ desc_on_ring->datalen = cpu_to_le16(buf_size);
+
+ /* Update the address values in the desc with the pa value
+ * for respective buffer
+ */
+ desc_on_ring->params.generic.addr_high =
+ cpu_to_le32(upper_32_bits(dma_buf->pa));
+ desc_on_ring->params.generic.addr_low =
+ cpu_to_le32(lower_32_bits(dma_buf->pa));
+ }
+
+ /* Debug desc and buffer */
+ ice_debug(hw, ICE_DBG_AQ_MSG,
+ "ATQ: Control Send queue desc and buffer:\n");
+
+ ice_debug_cq(hw, ICE_DBG_AQ_CMD, (void *)desc_on_ring, buf, buf_size);
+
+ (cq->sq.next_to_use)++;
+ if (cq->sq.next_to_use == cq->sq.count)
+ cq->sq.next_to_use = 0;
+ wr32(hw, cq->sq.tail, cq->sq.next_to_use);
+
+ do {
+ if (ice_sq_done(hw, cq))
+ break;
+
+ mdelay(1);
+ total_delay++;
+ } while (total_delay < cq->sq_cmd_timeout);
+
+ /* if ready, copy the desc back to temp */
+ if (ice_sq_done(hw, cq)) {
+ memcpy(desc, desc_on_ring, sizeof(*desc));
+ if (buf) {
+ /* get returned length to copy */
+ u16 copy_size = le16_to_cpu(desc->datalen);
+
+ if (copy_size > buf_size) {
+ ice_debug(hw, ICE_DBG_AQ_MSG,
+ "Return len %d > than buf len %d\n",
+ copy_size, buf_size);
+ status = ICE_ERR_AQ_ERROR;
+ } else {
+ memcpy(buf, dma_buf->va, copy_size);
+ }
+ }
+ retval = le16_to_cpu(desc->retval);
+ if (retval) {
+ ice_debug(hw, ICE_DBG_AQ_MSG,
+ "Control Send Queue command completed with error 0x%x\n",
+ retval);
+
+ /* strip off FW internal code */
+ retval &= 0xff;
+ }
+ cmd_completed = true;
+ if (!status && retval != ICE_AQ_RC_OK)
+ status = ICE_ERR_AQ_ERROR;
+ cq->sq_last_status = (enum ice_aq_err)retval;
+ }
+
+ ice_debug(hw, ICE_DBG_AQ_MSG,
+ "ATQ: desc and buffer writeback:\n");
+
+ ice_debug_cq(hw, ICE_DBG_AQ_CMD, (void *)desc, buf, buf_size);
+
+ /* save writeback AQ if requested */
+ if (details->wb_desc)
+ memcpy(details->wb_desc, desc_on_ring,
+ sizeof(*details->wb_desc));
+
+ /* update the error if time out occurred */
+ if (!cmd_completed) {
+ ice_debug(hw, ICE_DBG_AQ_MSG,
+ "Control Send Queue Writeback timeout.\n");
+ status = ICE_ERR_AQ_TIMEOUT;
+ }
+
+sq_send_command_error:
+ mutex_unlock(&cq->sq_lock);
+ return status;
+}
+
+/**
+ * ice_fill_dflt_direct_cmd_desc - AQ descriptor helper function
+ * @desc: pointer to the temp descriptor (non DMA mem)
+ * @opcode: the opcode can be used to decide which flags to turn off or on
+ *
+ * Fill the desc with default values
+ */
+void ice_fill_dflt_direct_cmd_desc(struct ice_aq_desc *desc, u16 opcode)
+{
+ /* zero out the desc */
+ memset(desc, 0, sizeof(*desc));
+ desc->opcode = cpu_to_le16(opcode);
+ desc->flags = cpu_to_le16(ICE_AQ_FLAG_SI);
+}
+
+/**
+ * ice_clean_rq_elem
+ * @hw: pointer to the hw struct
+ * @cq: pointer to the specific Control queue
+ * @e: event info from the receive descriptor, includes any buffers
+ * @pending: number of events that could be left to process
+ *
+ * This function cleans one Admin Receive Queue element and returns
+ * the contents through e. It can also return how many events are
+ * left to process through 'pending'.
+ */
+enum ice_status
+ice_clean_rq_elem(struct ice_hw *hw, struct ice_ctl_q_info *cq,
+ struct ice_rq_event_info *e, u16 *pending)
+{
+ u16 ntc = cq->rq.next_to_clean;
+ enum ice_status ret_code = 0;
+ struct ice_aq_desc *desc;
+ struct ice_dma_mem *bi;
+ u16 desc_idx;
+ u16 datalen;
+ u16 flags;
+ u16 ntu;
+
+ /* pre-clean the event info */
+ memset(&e->desc, 0, sizeof(e->desc));
+
+ /* take the lock before we start messing with the ring */
+ mutex_lock(&cq->rq_lock);
+
+ if (!cq->rq.count) {
+ ice_debug(hw, ICE_DBG_AQ_MSG,
+ "Control Receive queue not initialized.\n");
+ ret_code = ICE_ERR_AQ_EMPTY;
+ goto clean_rq_elem_err;
+ }
+
+ /* set next_to_use to head */
+ ntu = (u16)(rd32(hw, cq->rq.head) & cq->rq.head_mask);
+
+ if (ntu == ntc) {
+ /* nothing to do - shouldn't need to update ring's values */
+ ret_code = ICE_ERR_AQ_NO_WORK;
+ goto clean_rq_elem_out;
+ }
+
+ /* now clean the next descriptor */
+ desc = ICE_CTL_Q_DESC(cq->rq, ntc);
+ desc_idx = ntc;
+
+ flags = le16_to_cpu(desc->flags);
+ if (flags & ICE_AQ_FLAG_ERR) {
+ ret_code = ICE_ERR_AQ_ERROR;
+ cq->rq_last_status = (enum ice_aq_err)le16_to_cpu(desc->retval);
+ ice_debug(hw, ICE_DBG_AQ_MSG,
+ "Control Receive Queue Event received with error 0x%x\n",
+ cq->rq_last_status);
+ }
+ memcpy(&e->desc, desc, sizeof(e->desc));
+ datalen = le16_to_cpu(desc->datalen);
+ e->msg_len = min(datalen, e->buf_len);
+ if (e->msg_buf && e->msg_len)
+ memcpy(e->msg_buf, cq->rq.r.rq_bi[desc_idx].va, e->msg_len);
+
+ ice_debug(hw, ICE_DBG_AQ_MSG, "ARQ: desc and buffer:\n");
+
+ ice_debug_cq(hw, ICE_DBG_AQ_CMD, (void *)desc, e->msg_buf,
+ cq->rq_buf_size);
+
+ /* Restore the original datalen and buffer address in the desc,
+ * FW updates datalen to indicate the event message size
+ */
+ bi = &cq->rq.r.rq_bi[ntc];
+ memset(desc, 0, sizeof(*desc));
+
+ desc->flags = cpu_to_le16(ICE_AQ_FLAG_BUF);
+ if (cq->rq_buf_size > ICE_AQ_LG_BUF)
+ desc->flags |= cpu_to_le16(ICE_AQ_FLAG_LB);
+ desc->datalen = cpu_to_le16(bi->size);
+ desc->params.generic.addr_high = cpu_to_le32(upper_32_bits(bi->pa));
+ desc->params.generic.addr_low = cpu_to_le32(lower_32_bits(bi->pa));
+
+ /* set tail = the last cleaned desc index. */
+ wr32(hw, cq->rq.tail, ntc);
+ /* ntc is updated to tail + 1 */
+ ntc++;
+ if (ntc == cq->num_rq_entries)
+ ntc = 0;
+ cq->rq.next_to_clean = ntc;
+ cq->rq.next_to_use = ntu;
+
+clean_rq_elem_out:
+ /* Set pending if needed, unlock and return */
+ if (pending)
+ *pending = (u16)((ntc > ntu ? cq->rq.count : 0) + (ntu - ntc));
+clean_rq_elem_err:
+ mutex_unlock(&cq->rq_lock);
+
+ return ret_code;
+}
diff --git a/drivers/net/ethernet/intel/ice/ice_controlq.h b/drivers/net/ethernet/intel/ice/ice_controlq.h
new file mode 100644
index 000000000000..ea02b89243e2
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_controlq.h
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018, Intel Corporation. */
+
+#ifndef _ICE_CONTROLQ_H_
+#define _ICE_CONTROLQ_H_
+
+#include "ice_adminq_cmd.h"
+
+/* Maximum buffer lengths for all control queue types */
+#define ICE_AQ_MAX_BUF_LEN 4096
+
+#define ICE_CTL_Q_DESC(R, i) \
+ (&(((struct ice_aq_desc *)((R).desc_buf.va))[i]))
+
+#define ICE_CTL_Q_DESC_UNUSED(R) \
+ (u16)((((R)->next_to_clean > (R)->next_to_use) ? 0 : (R)->count) + \
+ (R)->next_to_clean - (R)->next_to_use - 1)
+
+/* Defines that help manage the driver vs FW API checks.
+ * Take a look at ice_aq_ver_check in ice_controlq.c for actual usage.
+ *
+ */
+#define EXP_FW_API_VER_BRANCH 0x00
+#define EXP_FW_API_VER_MAJOR 0x00
+#define EXP_FW_API_VER_MINOR 0x01
+
+/* Different control queue types: These are mainly for SW consumption. */
+enum ice_ctl_q {
+ ICE_CTL_Q_UNKNOWN = 0,
+ ICE_CTL_Q_ADMIN,
+};
+
+/* Control Queue default settings */
+#define ICE_CTL_Q_SQ_CMD_TIMEOUT 250 /* msecs */
+
+struct ice_ctl_q_ring {
+ void *dma_head; /* Virtual address to dma head */
+ struct ice_dma_mem desc_buf; /* descriptor ring memory */
+ void *cmd_buf; /* command buffer memory */
+
+ union {
+ struct ice_dma_mem *sq_bi;
+ struct ice_dma_mem *rq_bi;
+ } r;
+
+ u16 count; /* Number of descriptors */
+
+ /* used for interrupt processing */
+ u16 next_to_use;
+ u16 next_to_clean;
+
+ /* used for queue tracking */
+ u32 head;
+ u32 tail;
+ u32 len;
+ u32 bah;
+ u32 bal;
+ u32 len_mask;
+ u32 len_ena_mask;
+ u32 head_mask;
+};
+
+/* sq transaction details */
+struct ice_sq_cd {
+ struct ice_aq_desc *wb_desc;
+};
+
+#define ICE_CTL_Q_DETAILS(R, i) (&(((struct ice_sq_cd *)((R).cmd_buf))[i]))
+
+/* rq event information */
+struct ice_rq_event_info {
+ struct ice_aq_desc desc;
+ u16 msg_len;
+ u16 buf_len;
+ u8 *msg_buf;
+};
+
+/* Control Queue information */
+struct ice_ctl_q_info {
+ enum ice_ctl_q qtype;
+ struct ice_ctl_q_ring rq; /* receive queue */
+ struct ice_ctl_q_ring sq; /* send queue */
+ u32 sq_cmd_timeout; /* send queue cmd write back timeout */
+ u16 num_rq_entries; /* receive queue depth */
+ u16 num_sq_entries; /* send queue depth */
+ u16 rq_buf_size; /* receive queue buffer size */
+ u16 sq_buf_size; /* send queue buffer size */
+ struct mutex sq_lock; /* Send queue lock */
+ struct mutex rq_lock; /* Receive queue lock */
+ enum ice_aq_err sq_last_status; /* last status on send queue */
+ enum ice_aq_err rq_last_status; /* last status on receive queue */
+};
+
+#endif /* _ICE_CONTROLQ_H_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_devids.h b/drivers/net/ethernet/intel/ice/ice_devids.h
new file mode 100644
index 000000000000..0e14d7215a6e
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_devids.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018, Intel Corporation. */
+
+#ifndef _ICE_DEVIDS_H_
+#define _ICE_DEVIDS_H_
+
+/* Device IDs */
+/* Intel(R) Ethernet Controller C810 for backplane */
+#define ICE_DEV_ID_C810_BACKPLANE 0x1591
+/* Intel(R) Ethernet Controller C810 for QSFP */
+#define ICE_DEV_ID_C810_QSFP 0x1592
+/* Intel(R) Ethernet Controller C810 for SFP */
+#define ICE_DEV_ID_C810_SFP 0x1593
+/* Intel(R) Ethernet Controller C810/X557-AT 10GBASE-T */
+#define ICE_DEV_ID_C810_10G_BASE_T 0x1594
+/* Intel(R) Ethernet Controller C810 1GbE */
+#define ICE_DEV_ID_C810_SGMII 0x1595
+
+#endif /* _ICE_DEVIDS_H_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c b/drivers/net/ethernet/intel/ice/ice_ethtool.c
new file mode 100644
index 000000000000..1db304c01d10
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c
@@ -0,0 +1,940 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2018, Intel Corporation. */
+
+/* ethtool support for ice */
+
+#include "ice.h"
+
+struct ice_stats {
+ char stat_string[ETH_GSTRING_LEN];
+ int sizeof_stat;
+ int stat_offset;
+};
+
+#define ICE_STAT(_type, _name, _stat) { \
+ .stat_string = _name, \
+ .sizeof_stat = FIELD_SIZEOF(_type, _stat), \
+ .stat_offset = offsetof(_type, _stat) \
+}
+
+#define ICE_VSI_STAT(_name, _stat) \
+ ICE_STAT(struct ice_vsi, _name, _stat)
+#define ICE_PF_STAT(_name, _stat) \
+ ICE_STAT(struct ice_pf, _name, _stat)
+
+static int ice_q_stats_len(struct net_device *netdev)
+{
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+
+ return ((np->vsi->num_txq + np->vsi->num_rxq) *
+ (sizeof(struct ice_q_stats) / sizeof(u64)));
+}
+
+#define ICE_PF_STATS_LEN ARRAY_SIZE(ice_gstrings_pf_stats)
+#define ICE_VSI_STATS_LEN ARRAY_SIZE(ice_gstrings_vsi_stats)
+
+#define ICE_ALL_STATS_LEN(n) (ICE_PF_STATS_LEN + ICE_VSI_STATS_LEN + \
+ ice_q_stats_len(n))
+
+static const struct ice_stats ice_gstrings_vsi_stats[] = {
+ ICE_VSI_STAT("tx_unicast", eth_stats.tx_unicast),
+ ICE_VSI_STAT("rx_unicast", eth_stats.rx_unicast),
+ ICE_VSI_STAT("tx_multicast", eth_stats.tx_multicast),
+ ICE_VSI_STAT("rx_multicast", eth_stats.rx_multicast),
+ ICE_VSI_STAT("tx_broadcast", eth_stats.tx_broadcast),
+ ICE_VSI_STAT("rx_broadcast", eth_stats.rx_broadcast),
+ ICE_VSI_STAT("tx_bytes", eth_stats.tx_bytes),
+ ICE_VSI_STAT("rx_bytes", eth_stats.rx_bytes),
+ ICE_VSI_STAT("rx_discards", eth_stats.rx_discards),
+ ICE_VSI_STAT("tx_errors", eth_stats.tx_errors),
+ ICE_VSI_STAT("tx_linearize", tx_linearize),
+ ICE_VSI_STAT("rx_unknown_protocol", eth_stats.rx_unknown_protocol),
+ ICE_VSI_STAT("rx_alloc_fail", rx_buf_failed),
+ ICE_VSI_STAT("rx_pg_alloc_fail", rx_page_failed),
+};
+
+/* These PF_STATs might look like duplicates of some NETDEV_STATs,
+ * but they aren't. This device is capable of supporting multiple
+ * VSIs/netdevs on a single PF. The NETDEV_STATs are for individual
+ * netdevs whereas the PF_STATs are for the physical function that's
+ * hosting these netdevs.
+ *
+ * The PF_STATs are appended to the netdev stats only when ethtool -S
+ * is queried on the base PF netdev.
+ */
+static struct ice_stats ice_gstrings_pf_stats[] = {
+ ICE_PF_STAT("tx_bytes", stats.eth.tx_bytes),
+ ICE_PF_STAT("rx_bytes", stats.eth.rx_bytes),
+ ICE_PF_STAT("tx_unicast", stats.eth.tx_unicast),
+ ICE_PF_STAT("rx_unicast", stats.eth.rx_unicast),
+ ICE_PF_STAT("tx_multicast", stats.eth.tx_multicast),
+ ICE_PF_STAT("rx_multicast", stats.eth.rx_multicast),
+ ICE_PF_STAT("tx_broadcast", stats.eth.tx_broadcast),
+ ICE_PF_STAT("rx_broadcast", stats.eth.rx_broadcast),
+ ICE_PF_STAT("tx_errors", stats.eth.tx_errors),
+ ICE_PF_STAT("tx_size_64", stats.tx_size_64),
+ ICE_PF_STAT("rx_size_64", stats.rx_size_64),
+ ICE_PF_STAT("tx_size_127", stats.tx_size_127),
+ ICE_PF_STAT("rx_size_127", stats.rx_size_127),
+ ICE_PF_STAT("tx_size_255", stats.tx_size_255),
+ ICE_PF_STAT("rx_size_255", stats.rx_size_255),
+ ICE_PF_STAT("tx_size_511", stats.tx_size_511),
+ ICE_PF_STAT("rx_size_511", stats.rx_size_511),
+ ICE_PF_STAT("tx_size_1023", stats.tx_size_1023),
+ ICE_PF_STAT("rx_size_1023", stats.rx_size_1023),
+ ICE_PF_STAT("tx_size_1522", stats.tx_size_1522),
+ ICE_PF_STAT("rx_size_1522", stats.rx_size_1522),
+ ICE_PF_STAT("tx_size_big", stats.tx_size_big),
+ ICE_PF_STAT("rx_size_big", stats.rx_size_big),
+ ICE_PF_STAT("link_xon_tx", stats.link_xon_tx),
+ ICE_PF_STAT("link_xon_rx", stats.link_xon_rx),
+ ICE_PF_STAT("link_xoff_tx", stats.link_xoff_tx),
+ ICE_PF_STAT("link_xoff_rx", stats.link_xoff_rx),
+ ICE_PF_STAT("tx_dropped_link_down", stats.tx_dropped_link_down),
+ ICE_PF_STAT("rx_undersize", stats.rx_undersize),
+ ICE_PF_STAT("rx_fragments", stats.rx_fragments),
+ ICE_PF_STAT("rx_oversize", stats.rx_oversize),
+ ICE_PF_STAT("rx_jabber", stats.rx_jabber),
+ ICE_PF_STAT("rx_csum_bad", hw_csum_rx_error),
+ ICE_PF_STAT("rx_length_errors", stats.rx_len_errors),
+ ICE_PF_STAT("rx_dropped", stats.eth.rx_discards),
+ ICE_PF_STAT("rx_crc_errors", stats.crc_errors),
+ ICE_PF_STAT("illegal_bytes", stats.illegal_bytes),
+ ICE_PF_STAT("mac_local_faults", stats.mac_local_faults),
+ ICE_PF_STAT("mac_remote_faults", stats.mac_remote_faults),
+};
+
+static u32 ice_regs_dump_list[] = {
+ PFGEN_STATE,
+ PRTGEN_STATUS,
+ QRX_CTRL(0),
+ QINT_TQCTL(0),
+ QINT_RQCTL(0),
+ PFINT_OICR_ENA,
+ QRX_ITR(0),
+};
+
+/**
+ * ice_nvm_version_str - format the NVM version strings
+ * @hw: ptr to the hardware info
+ */
+static char *ice_nvm_version_str(struct ice_hw *hw)
+{
+ static char buf[ICE_ETHTOOL_FWVER_LEN];
+ u8 ver, patch;
+ u32 full_ver;
+ u16 build;
+
+ full_ver = hw->nvm.oem_ver;
+ ver = (u8)((full_ver & ICE_OEM_VER_MASK) >> ICE_OEM_VER_SHIFT);
+ build = (u16)((full_ver & ICE_OEM_VER_BUILD_MASK) >>
+ ICE_OEM_VER_BUILD_SHIFT);
+ patch = (u8)(full_ver & ICE_OEM_VER_PATCH_MASK);
+
+ snprintf(buf, sizeof(buf), "%x.%02x 0x%x %d.%d.%d",
+ (hw->nvm.ver & ICE_NVM_VER_HI_MASK) >> ICE_NVM_VER_HI_SHIFT,
+ (hw->nvm.ver & ICE_NVM_VER_LO_MASK) >> ICE_NVM_VER_LO_SHIFT,
+ hw->nvm.eetrack, ver, build, patch);
+
+ return buf;
+}
+
+static void
+ice_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
+{
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct ice_vsi *vsi = np->vsi;
+ struct ice_pf *pf = vsi->back;
+
+ strlcpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver));
+ strlcpy(drvinfo->version, ice_drv_ver, sizeof(drvinfo->version));
+ strlcpy(drvinfo->fw_version, ice_nvm_version_str(&pf->hw),
+ sizeof(drvinfo->fw_version));
+ strlcpy(drvinfo->bus_info, pci_name(pf->pdev),
+ sizeof(drvinfo->bus_info));
+}
+
+static int ice_get_regs_len(struct net_device __always_unused *netdev)
+{
+ return sizeof(ice_regs_dump_list);
+}
+
+static void
+ice_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p)
+{
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct ice_pf *pf = np->vsi->back;
+ struct ice_hw *hw = &pf->hw;
+ u32 *regs_buf = (u32 *)p;
+ int i;
+
+ regs->version = 1;
+
+ for (i = 0; i < ARRAY_SIZE(ice_regs_dump_list); ++i)
+ regs_buf[i] = rd32(hw, ice_regs_dump_list[i]);
+}
+
+static u32 ice_get_msglevel(struct net_device *netdev)
+{
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct ice_pf *pf = np->vsi->back;
+
+#ifndef CONFIG_DYNAMIC_DEBUG
+ if (pf->hw.debug_mask)
+ netdev_info(netdev, "hw debug_mask: 0x%llX\n",
+ pf->hw.debug_mask);
+#endif /* !CONFIG_DYNAMIC_DEBUG */
+
+ return pf->msg_enable;
+}
+
+static void ice_set_msglevel(struct net_device *netdev, u32 data)
+{
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct ice_pf *pf = np->vsi->back;
+
+#ifndef CONFIG_DYNAMIC_DEBUG
+ if (ICE_DBG_USER & data)
+ pf->hw.debug_mask = data;
+ else
+ pf->msg_enable = data;
+#else
+ pf->msg_enable = data;
+#endif /* !CONFIG_DYNAMIC_DEBUG */
+}
+
+static void ice_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
+{
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct ice_vsi *vsi = np->vsi;
+ char *p = (char *)data;
+ unsigned int i;
+
+ switch (stringset) {
+ case ETH_SS_STATS:
+ for (i = 0; i < ICE_VSI_STATS_LEN; i++) {
+ snprintf(p, ETH_GSTRING_LEN, "%s",
+ ice_gstrings_vsi_stats[i].stat_string);
+ p += ETH_GSTRING_LEN;
+ }
+
+ ice_for_each_txq(vsi, i) {
+ snprintf(p, ETH_GSTRING_LEN,
+ "tx-queue-%u.tx_packets", i);
+ p += ETH_GSTRING_LEN;
+ snprintf(p, ETH_GSTRING_LEN, "tx-queue-%u.tx_bytes", i);
+ p += ETH_GSTRING_LEN;
+ }
+
+ ice_for_each_rxq(vsi, i) {
+ snprintf(p, ETH_GSTRING_LEN,
+ "rx-queue-%u.rx_packets", i);
+ p += ETH_GSTRING_LEN;
+ snprintf(p, ETH_GSTRING_LEN, "rx-queue-%u.rx_bytes", i);
+ p += ETH_GSTRING_LEN;
+ }
+
+ if (vsi->type != ICE_VSI_PF)
+ return;
+
+ for (i = 0; i < ICE_PF_STATS_LEN; i++) {
+ snprintf(p, ETH_GSTRING_LEN, "port.%s",
+ ice_gstrings_pf_stats[i].stat_string);
+ p += ETH_GSTRING_LEN;
+ }
+
+ break;
+ default:
+ break;
+ }
+}
+
+static int ice_get_sset_count(struct net_device *netdev, int sset)
+{
+ switch (sset) {
+ case ETH_SS_STATS:
+ return ICE_ALL_STATS_LEN(netdev);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static void
+ice_get_ethtool_stats(struct net_device *netdev,
+ struct ethtool_stats __always_unused *stats, u64 *data)
+{
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct ice_vsi *vsi = np->vsi;
+ struct ice_pf *pf = vsi->back;
+ struct ice_ring *ring;
+ unsigned int j = 0;
+ int i = 0;
+ char *p;
+
+ for (j = 0; j < ICE_VSI_STATS_LEN; j++) {
+ p = (char *)vsi + ice_gstrings_vsi_stats[j].stat_offset;
+ data[i++] = (ice_gstrings_vsi_stats[j].sizeof_stat ==
+ sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
+ }
+
+ /* populate per queue stats */
+ rcu_read_lock();
+
+ ice_for_each_txq(vsi, j) {
+ ring = READ_ONCE(vsi->tx_rings[j]);
+ if (!ring)
+ continue;
+ data[i++] = ring->stats.pkts;
+ data[i++] = ring->stats.bytes;
+ }
+
+ ice_for_each_rxq(vsi, j) {
+ ring = READ_ONCE(vsi->rx_rings[j]);
+ data[i++] = ring->stats.pkts;
+ data[i++] = ring->stats.bytes;
+ }
+
+ rcu_read_unlock();
+
+ if (vsi->type != ICE_VSI_PF)
+ return;
+
+ for (j = 0; j < ICE_PF_STATS_LEN; j++) {
+ p = (char *)pf + ice_gstrings_pf_stats[j].stat_offset;
+ data[i++] = (ice_gstrings_pf_stats[j].sizeof_stat ==
+ sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
+ }
+}
+
+static int
+ice_get_link_ksettings(struct net_device *netdev,
+ struct ethtool_link_ksettings *ks)
+{
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct ice_link_status *hw_link_info;
+ struct ice_vsi *vsi = np->vsi;
+ bool link_up;
+
+ hw_link_info = &vsi->port_info->phy.link_info;
+ link_up = hw_link_info->link_info & ICE_AQ_LINK_UP;
+
+ ethtool_link_ksettings_add_link_mode(ks, supported,
+ 10000baseT_Full);
+ ethtool_link_ksettings_add_link_mode(ks, advertising,
+ 10000baseT_Full);
+
+ /* set speed and duplex */
+ if (link_up) {
+ switch (hw_link_info->link_speed) {
+ case ICE_AQ_LINK_SPEED_100MB:
+ ks->base.speed = SPEED_100;
+ break;
+ case ICE_AQ_LINK_SPEED_2500MB:
+ ks->base.speed = SPEED_2500;
+ break;
+ case ICE_AQ_LINK_SPEED_5GB:
+ ks->base.speed = SPEED_5000;
+ break;
+ case ICE_AQ_LINK_SPEED_10GB:
+ ks->base.speed = SPEED_10000;
+ break;
+ case ICE_AQ_LINK_SPEED_25GB:
+ ks->base.speed = SPEED_25000;
+ break;
+ case ICE_AQ_LINK_SPEED_40GB:
+ ks->base.speed = SPEED_40000;
+ break;
+ default:
+ ks->base.speed = SPEED_UNKNOWN;
+ break;
+ }
+
+ ks->base.duplex = DUPLEX_FULL;
+ } else {
+ ks->base.speed = SPEED_UNKNOWN;
+ ks->base.duplex = DUPLEX_UNKNOWN;
+ }
+
+ /* set autoneg settings */
+ ks->base.autoneg = ((hw_link_info->an_info & ICE_AQ_AN_COMPLETED) ?
+ AUTONEG_ENABLE : AUTONEG_DISABLE);
+
+ /* set media type settings */
+ switch (vsi->port_info->phy.media_type) {
+ case ICE_MEDIA_FIBER:
+ ethtool_link_ksettings_add_link_mode(ks, supported, FIBRE);
+ ks->base.port = PORT_FIBRE;
+ break;
+ case ICE_MEDIA_BASET:
+ ethtool_link_ksettings_add_link_mode(ks, supported, TP);
+ ethtool_link_ksettings_add_link_mode(ks, advertising, TP);
+ ks->base.port = PORT_TP;
+ break;
+ case ICE_MEDIA_BACKPLANE:
+ ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg);
+ ethtool_link_ksettings_add_link_mode(ks, supported, Backplane);
+ ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg);
+ ethtool_link_ksettings_add_link_mode(ks, advertising,
+ Backplane);
+ ks->base.port = PORT_NONE;
+ break;
+ case ICE_MEDIA_DA:
+ ethtool_link_ksettings_add_link_mode(ks, supported, FIBRE);
+ ethtool_link_ksettings_add_link_mode(ks, advertising, FIBRE);
+ ks->base.port = PORT_DA;
+ break;
+ default:
+ ks->base.port = PORT_OTHER;
+ break;
+ }
+
+ /* flow control is symmetric and always supported */
+ ethtool_link_ksettings_add_link_mode(ks, supported, Pause);
+
+ switch (vsi->port_info->fc.req_mode) {
+ case ICE_FC_FULL:
+ ethtool_link_ksettings_add_link_mode(ks, advertising, Pause);
+ break;
+ case ICE_FC_TX_PAUSE:
+ ethtool_link_ksettings_add_link_mode(ks, advertising,
+ Asym_Pause);
+ break;
+ case ICE_FC_RX_PAUSE:
+ ethtool_link_ksettings_add_link_mode(ks, advertising, Pause);
+ ethtool_link_ksettings_add_link_mode(ks, advertising,
+ Asym_Pause);
+ break;
+ case ICE_FC_PFC:
+ default:
+ ethtool_link_ksettings_del_link_mode(ks, advertising, Pause);
+ ethtool_link_ksettings_del_link_mode(ks, advertising,
+ Asym_Pause);
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * ice_get_rxnfc - command to get RX flow classification rules
+ * @netdev: network interface device structure
+ * @cmd: ethtool rxnfc command
+ * @rule_locs: buffer to rturn Rx flow classification rules
+ *
+ * Returns Success if the command is supported.
+ */
+static int ice_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
+ u32 __always_unused *rule_locs)
+{
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct ice_vsi *vsi = np->vsi;
+ int ret = -EOPNOTSUPP;
+
+ switch (cmd->cmd) {
+ case ETHTOOL_GRXRINGS:
+ cmd->data = vsi->rss_size;
+ ret = 0;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static void
+ice_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring)
+{
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct ice_vsi *vsi = np->vsi;
+
+ ring->rx_max_pending = ICE_MAX_NUM_DESC;
+ ring->tx_max_pending = ICE_MAX_NUM_DESC;
+ ring->rx_pending = vsi->rx_rings[0]->count;
+ ring->tx_pending = vsi->tx_rings[0]->count;
+ ring->rx_mini_pending = ICE_MIN_NUM_DESC;
+ ring->rx_mini_max_pending = 0;
+ ring->rx_jumbo_max_pending = 0;
+ ring->rx_jumbo_pending = 0;
+}
+
+static int
+ice_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring)
+{
+ struct ice_ring *tx_rings = NULL, *rx_rings = NULL;
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct ice_vsi *vsi = np->vsi;
+ struct ice_pf *pf = vsi->back;
+ int i, timeout = 50, err = 0;
+ u32 new_rx_cnt, new_tx_cnt;
+
+ if (ring->tx_pending > ICE_MAX_NUM_DESC ||
+ ring->tx_pending < ICE_MIN_NUM_DESC ||
+ ring->rx_pending > ICE_MAX_NUM_DESC ||
+ ring->rx_pending < ICE_MIN_NUM_DESC) {
+ netdev_err(netdev, "Descriptors requested (Tx: %d / Rx: %d) out of range [%d-%d]\n",
+ ring->tx_pending, ring->rx_pending,
+ ICE_MIN_NUM_DESC, ICE_MAX_NUM_DESC);
+ return -EINVAL;
+ }
+
+ new_tx_cnt = ALIGN(ring->tx_pending, ICE_REQ_DESC_MULTIPLE);
+ new_rx_cnt = ALIGN(ring->rx_pending, ICE_REQ_DESC_MULTIPLE);
+
+ /* if nothing to do return success */
+ if (new_tx_cnt == vsi->tx_rings[0]->count &&
+ new_rx_cnt == vsi->rx_rings[0]->count) {
+ netdev_dbg(netdev, "Nothing to change, descriptor count is same as requested\n");
+ return 0;
+ }
+
+ while (test_and_set_bit(__ICE_CFG_BUSY, pf->state)) {
+ timeout--;
+ if (!timeout)
+ return -EBUSY;
+ usleep_range(1000, 2000);
+ }
+
+ /* set for the next time the netdev is started */
+ if (!netif_running(vsi->netdev)) {
+ for (i = 0; i < vsi->alloc_txq; i++)
+ vsi->tx_rings[i]->count = new_tx_cnt;
+ for (i = 0; i < vsi->alloc_rxq; i++)
+ vsi->rx_rings[i]->count = new_rx_cnt;
+ netdev_dbg(netdev, "Link is down, descriptor count change happens when link is brought up\n");
+ goto done;
+ }
+
+ if (new_tx_cnt == vsi->tx_rings[0]->count)
+ goto process_rx;
+
+ /* alloc updated Tx resources */
+ netdev_info(netdev, "Changing Tx descriptor count from %d to %d\n",
+ vsi->tx_rings[0]->count, new_tx_cnt);
+
+ tx_rings = devm_kcalloc(&pf->pdev->dev, vsi->alloc_txq,
+ sizeof(struct ice_ring), GFP_KERNEL);
+ if (!tx_rings) {
+ err = -ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; i < vsi->num_txq; i++) {
+ /* clone ring and setup updated count */
+ tx_rings[i] = *vsi->tx_rings[i];
+ tx_rings[i].count = new_tx_cnt;
+ tx_rings[i].desc = NULL;
+ tx_rings[i].tx_buf = NULL;
+ err = ice_setup_tx_ring(&tx_rings[i]);
+ if (err) {
+ while (i) {
+ i--;
+ ice_clean_tx_ring(&tx_rings[i]);
+ }
+ devm_kfree(&pf->pdev->dev, tx_rings);
+ goto done;
+ }
+ }
+
+process_rx:
+ if (new_rx_cnt == vsi->rx_rings[0]->count)
+ goto process_link;
+
+ /* alloc updated Rx resources */
+ netdev_info(netdev, "Changing Rx descriptor count from %d to %d\n",
+ vsi->rx_rings[0]->count, new_rx_cnt);
+
+ rx_rings = devm_kcalloc(&pf->pdev->dev, vsi->alloc_rxq,
+ sizeof(struct ice_ring), GFP_KERNEL);
+ if (!rx_rings) {
+ err = -ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; i < vsi->num_rxq; i++) {
+ /* clone ring and setup updated count */
+ rx_rings[i] = *vsi->rx_rings[i];
+ rx_rings[i].count = new_rx_cnt;
+ rx_rings[i].desc = NULL;
+ rx_rings[i].rx_buf = NULL;
+ /* this is to allow wr32 to have something to write to
+ * during early allocation of Rx buffers
+ */
+ rx_rings[i].tail = vsi->back->hw.hw_addr + PRTGEN_STATUS;
+
+ err = ice_setup_rx_ring(&rx_rings[i]);
+ if (err)
+ goto rx_unwind;
+
+ /* allocate Rx buffers */
+ err = ice_alloc_rx_bufs(&rx_rings[i],
+ ICE_DESC_UNUSED(&rx_rings[i]));
+rx_unwind:
+ if (err) {
+ while (i) {
+ i--;
+ ice_free_rx_ring(&rx_rings[i]);
+ }
+ devm_kfree(&pf->pdev->dev, rx_rings);
+ err = -ENOMEM;
+ goto free_tx;
+ }
+ }
+
+process_link:
+ /* Bring interface down, copy in the new ring info, then restore the
+ * interface. if VSI is up, bring it down and then back up
+ */
+ if (!test_and_set_bit(__ICE_DOWN, vsi->state)) {
+ ice_down(vsi);
+
+ if (tx_rings) {
+ for (i = 0; i < vsi->alloc_txq; i++) {
+ ice_free_tx_ring(vsi->tx_rings[i]);
+ *vsi->tx_rings[i] = tx_rings[i];
+ }
+ devm_kfree(&pf->pdev->dev, tx_rings);
+ }
+
+ if (rx_rings) {
+ for (i = 0; i < vsi->alloc_rxq; i++) {
+ ice_free_rx_ring(vsi->rx_rings[i]);
+ /* copy the real tail offset */
+ rx_rings[i].tail = vsi->rx_rings[i]->tail;
+ /* this is to fake out the allocation routine
+ * into thinking it has to realloc everything
+ * but the recycling logic will let us re-use
+ * the buffers allocated above
+ */
+ rx_rings[i].next_to_use = 0;
+ rx_rings[i].next_to_clean = 0;
+ rx_rings[i].next_to_alloc = 0;
+ *vsi->rx_rings[i] = rx_rings[i];
+ }
+ devm_kfree(&pf->pdev->dev, rx_rings);
+ }
+
+ ice_up(vsi);
+ }
+ goto done;
+
+free_tx:
+ /* error cleanup if the Rx allocations failed after getting Tx */
+ if (tx_rings) {
+ for (i = 0; i < vsi->alloc_txq; i++)
+ ice_free_tx_ring(&tx_rings[i]);
+ devm_kfree(&pf->pdev->dev, tx_rings);
+ }
+
+done:
+ clear_bit(__ICE_CFG_BUSY, pf->state);
+ return err;
+}
+
+static int ice_nway_reset(struct net_device *netdev)
+{
+ /* restart autonegotiation */
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct ice_link_status *hw_link_info;
+ struct ice_vsi *vsi = np->vsi;
+ struct ice_port_info *pi;
+ enum ice_status status;
+ bool link_up;
+
+ pi = vsi->port_info;
+ hw_link_info = &pi->phy.link_info;
+ link_up = hw_link_info->link_info & ICE_AQ_LINK_UP;
+
+ status = ice_aq_set_link_restart_an(pi, link_up, NULL);
+ if (status) {
+ netdev_info(netdev, "link restart failed, err %d aq_err %d\n",
+ status, pi->hw->adminq.sq_last_status);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * ice_get_pauseparam - Get Flow Control status
+ * @netdev: network interface device structure
+ * @pause: ethernet pause (flow control) parameters
+ */
+static void
+ice_get_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause)
+{
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct ice_port_info *pi;
+
+ pi = np->vsi->port_info;
+ pause->autoneg =
+ ((pi->phy.link_info.an_info & ICE_AQ_AN_COMPLETED) ?
+ AUTONEG_ENABLE : AUTONEG_DISABLE);
+
+ if (pi->fc.current_mode == ICE_FC_RX_PAUSE) {
+ pause->rx_pause = 1;
+ } else if (pi->fc.current_mode == ICE_FC_TX_PAUSE) {
+ pause->tx_pause = 1;
+ } else if (pi->fc.current_mode == ICE_FC_FULL) {
+ pause->rx_pause = 1;
+ pause->tx_pause = 1;
+ }
+}
+
+/**
+ * ice_set_pauseparam - Set Flow Control parameter
+ * @netdev: network interface device structure
+ * @pause: return tx/rx flow control status
+ */
+static int
+ice_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause)
+{
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct ice_link_status *hw_link_info;
+ struct ice_pf *pf = np->vsi->back;
+ struct ice_vsi *vsi = np->vsi;
+ struct ice_hw *hw = &pf->hw;
+ struct ice_port_info *pi;
+ enum ice_status status;
+ u8 aq_failures;
+ bool link_up;
+ int err = 0;
+
+ pi = vsi->port_info;
+ hw_link_info = &pi->phy.link_info;
+ link_up = hw_link_info->link_info & ICE_AQ_LINK_UP;
+
+ /* Changing the port's flow control is not supported if this isn't the
+ * PF VSI
+ */
+ if (vsi->type != ICE_VSI_PF) {
+ netdev_info(netdev, "Changing flow control parameters only supported for PF VSI\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (pause->autoneg != (hw_link_info->an_info & ICE_AQ_AN_COMPLETED)) {
+ netdev_info(netdev, "To change autoneg please use: ethtool -s <dev> autoneg <on|off>\n");
+ return -EOPNOTSUPP;
+ }
+
+ /* If we have link and don't have autoneg */
+ if (!test_bit(__ICE_DOWN, pf->state) &&
+ !(hw_link_info->an_info & ICE_AQ_AN_COMPLETED)) {
+ /* Send message that it might not necessarily work*/
+ netdev_info(netdev, "Autoneg did not complete so changing settings may not result in an actual change.\n");
+ }
+
+ if (pause->rx_pause && pause->tx_pause)
+ pi->fc.req_mode = ICE_FC_FULL;
+ else if (pause->rx_pause && !pause->tx_pause)
+ pi->fc.req_mode = ICE_FC_RX_PAUSE;
+ else if (!pause->rx_pause && pause->tx_pause)
+ pi->fc.req_mode = ICE_FC_TX_PAUSE;
+ else if (!pause->rx_pause && !pause->tx_pause)
+ pi->fc.req_mode = ICE_FC_NONE;
+ else
+ return -EINVAL;
+
+ /* Tell the OS link is going down, the link will go back up when fw
+ * says it is ready asynchronously
+ */
+ ice_print_link_msg(vsi, false);
+ netif_carrier_off(netdev);
+ netif_tx_stop_all_queues(netdev);
+
+ /* Set the FC mode and only restart AN if link is up */
+ status = ice_set_fc(pi, &aq_failures, link_up);
+
+ if (aq_failures & ICE_SET_FC_AQ_FAIL_GET) {
+ netdev_info(netdev, "Set fc failed on the get_phy_capabilities call with err %d aq_err %d\n",
+ status, hw->adminq.sq_last_status);
+ err = -EAGAIN;
+ } else if (aq_failures & ICE_SET_FC_AQ_FAIL_SET) {
+ netdev_info(netdev, "Set fc failed on the set_phy_config call with err %d aq_err %d\n",
+ status, hw->adminq.sq_last_status);
+ err = -EAGAIN;
+ } else if (aq_failures & ICE_SET_FC_AQ_FAIL_UPDATE) {
+ netdev_info(netdev, "Set fc failed on the get_link_info call with err %d aq_err %d\n",
+ status, hw->adminq.sq_last_status);
+ err = -EAGAIN;
+ }
+
+ if (!test_bit(__ICE_DOWN, pf->state)) {
+ /* Give it a little more time to try to come back */
+ msleep(75);
+ if (!test_bit(__ICE_DOWN, pf->state))
+ return ice_nway_reset(netdev);
+ }
+
+ return err;
+}
+
+/**
+ * ice_get_rxfh_key_size - get the RSS hash key size
+ * @netdev: network interface device structure
+ *
+ * Returns the table size.
+ */
+static u32 ice_get_rxfh_key_size(struct net_device __always_unused *netdev)
+{
+ return ICE_VSIQF_HKEY_ARRAY_SIZE;
+}
+
+/**
+ * ice_get_rxfh_indir_size - get the rx flow hash indirection table size
+ * @netdev: network interface device structure
+ *
+ * Returns the table size.
+ */
+static u32 ice_get_rxfh_indir_size(struct net_device *netdev)
+{
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+
+ return np->vsi->rss_table_size;
+}
+
+/**
+ * ice_get_rxfh - get the rx flow hash indirection table
+ * @netdev: network interface device structure
+ * @indir: indirection table
+ * @key: hash key
+ * @hfunc: hash function
+ *
+ * Reads the indirection table directly from the hardware.
+ */
+static int
+ice_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key, u8 *hfunc)
+{
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct ice_vsi *vsi = np->vsi;
+ struct ice_pf *pf = vsi->back;
+ int ret = 0, i;
+ u8 *lut;
+
+ if (hfunc)
+ *hfunc = ETH_RSS_HASH_TOP;
+
+ if (!indir)
+ return 0;
+
+ if (!test_bit(ICE_FLAG_RSS_ENA, pf->flags)) {
+ /* RSS not supported return error here */
+ netdev_warn(netdev, "RSS is not configured on this VSI!\n");
+ return -EIO;
+ }
+
+ lut = devm_kzalloc(&pf->pdev->dev, vsi->rss_table_size, GFP_KERNEL);
+ if (!lut)
+ return -ENOMEM;
+
+ if (ice_get_rss(vsi, key, lut, vsi->rss_table_size)) {
+ ret = -EIO;
+ goto out;
+ }
+
+ for (i = 0; i < vsi->rss_table_size; i++)
+ indir[i] = (u32)(lut[i]);
+
+out:
+ devm_kfree(&pf->pdev->dev, lut);
+ return ret;
+}
+
+/**
+ * ice_set_rxfh - set the rx flow hash indirection table
+ * @netdev: network interface device structure
+ * @indir: indirection table
+ * @key: hash key
+ * @hfunc: hash function
+ *
+ * Returns -EINVAL if the table specifies an invalid queue id, otherwise
+ * returns 0 after programming the table.
+ */
+static int ice_set_rxfh(struct net_device *netdev, const u32 *indir,
+ const u8 *key, const u8 hfunc)
+{
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct ice_vsi *vsi = np->vsi;
+ struct ice_pf *pf = vsi->back;
+ u8 *seed = NULL;
+
+ if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)
+ return -EOPNOTSUPP;
+
+ if (!test_bit(ICE_FLAG_RSS_ENA, pf->flags)) {
+ /* RSS not supported return error here */
+ netdev_warn(netdev, "RSS is not configured on this VSI!\n");
+ return -EIO;
+ }
+
+ if (key) {
+ if (!vsi->rss_hkey_user) {
+ vsi->rss_hkey_user =
+ devm_kzalloc(&pf->pdev->dev,
+ ICE_VSIQF_HKEY_ARRAY_SIZE,
+ GFP_KERNEL);
+ if (!vsi->rss_hkey_user)
+ return -ENOMEM;
+ }
+ memcpy(vsi->rss_hkey_user, key, ICE_VSIQF_HKEY_ARRAY_SIZE);
+ seed = vsi->rss_hkey_user;
+ }
+
+ if (!vsi->rss_lut_user) {
+ vsi->rss_lut_user = devm_kzalloc(&pf->pdev->dev,
+ vsi->rss_table_size,
+ GFP_KERNEL);
+ if (!vsi->rss_lut_user)
+ return -ENOMEM;
+ }
+
+ /* Each 32 bits pointed by 'indir' is stored with a lut entry */
+ if (indir) {
+ int i;
+
+ for (i = 0; i < vsi->rss_table_size; i++)
+ vsi->rss_lut_user[i] = (u8)(indir[i]);
+ } else {
+ ice_fill_rss_lut(vsi->rss_lut_user, vsi->rss_table_size,
+ vsi->rss_size);
+ }
+
+ if (ice_set_rss(vsi, seed, vsi->rss_lut_user, vsi->rss_table_size))
+ return -EIO;
+
+ return 0;
+}
+
+static const struct ethtool_ops ice_ethtool_ops = {
+ .get_link_ksettings = ice_get_link_ksettings,
+ .get_drvinfo = ice_get_drvinfo,
+ .get_regs_len = ice_get_regs_len,
+ .get_regs = ice_get_regs,
+ .get_msglevel = ice_get_msglevel,
+ .set_msglevel = ice_set_msglevel,
+ .get_link = ethtool_op_get_link,
+ .get_strings = ice_get_strings,
+ .get_ethtool_stats = ice_get_ethtool_stats,
+ .get_sset_count = ice_get_sset_count,
+ .get_rxnfc = ice_get_rxnfc,
+ .get_ringparam = ice_get_ringparam,
+ .set_ringparam = ice_set_ringparam,
+ .nway_reset = ice_nway_reset,
+ .get_pauseparam = ice_get_pauseparam,
+ .set_pauseparam = ice_set_pauseparam,
+ .get_rxfh_key_size = ice_get_rxfh_key_size,
+ .get_rxfh_indir_size = ice_get_rxfh_indir_size,
+ .get_rxfh = ice_get_rxfh,
+ .set_rxfh = ice_set_rxfh,
+};
+
+/**
+ * ice_set_ethtool_ops - setup netdev ethtool ops
+ * @netdev: network interface device structure
+ *
+ * setup netdev ethtool ops with ice specific ops
+ */
+void ice_set_ethtool_ops(struct net_device *netdev)
+{
+ netdev->ethtool_ops = &ice_ethtool_ops;
+}
diff --git a/drivers/net/ethernet/intel/ice/ice_hw_autogen.h b/drivers/net/ethernet/intel/ice/ice_hw_autogen.h
new file mode 100644
index 000000000000..499904874b3f
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_hw_autogen.h
@@ -0,0 +1,264 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018, Intel Corporation. */
+
+/* Machine-generated file */
+
+#ifndef _ICE_HW_AUTOGEN_H_
+#define _ICE_HW_AUTOGEN_H_
+
+#define QTX_COMM_DBELL(_DBQM) (0x002C0000 + ((_DBQM) * 4))
+#define PF_FW_ARQBAH 0x00080180
+#define PF_FW_ARQBAL 0x00080080
+#define PF_FW_ARQH 0x00080380
+#define PF_FW_ARQH_ARQH_S 0
+#define PF_FW_ARQH_ARQH_M ICE_M(0x3FF, PF_FW_ARQH_ARQH_S)
+#define PF_FW_ARQLEN 0x00080280
+#define PF_FW_ARQLEN_ARQLEN_S 0
+#define PF_FW_ARQLEN_ARQLEN_M ICE_M(0x3FF, PF_FW_ARQLEN_ARQLEN_S)
+#define PF_FW_ARQLEN_ARQVFE_S 28
+#define PF_FW_ARQLEN_ARQVFE_M BIT(PF_FW_ARQLEN_ARQVFE_S)
+#define PF_FW_ARQLEN_ARQOVFL_S 29
+#define PF_FW_ARQLEN_ARQOVFL_M BIT(PF_FW_ARQLEN_ARQOVFL_S)
+#define PF_FW_ARQLEN_ARQCRIT_S 30
+#define PF_FW_ARQLEN_ARQCRIT_M BIT(PF_FW_ARQLEN_ARQCRIT_S)
+#define PF_FW_ARQLEN_ARQENABLE_S 31
+#define PF_FW_ARQLEN_ARQENABLE_M BIT(PF_FW_ARQLEN_ARQENABLE_S)
+#define PF_FW_ARQT 0x00080480
+#define PF_FW_ATQBAH 0x00080100
+#define PF_FW_ATQBAL 0x00080000
+#define PF_FW_ATQH 0x00080300
+#define PF_FW_ATQH_ATQH_S 0
+#define PF_FW_ATQH_ATQH_M ICE_M(0x3FF, PF_FW_ATQH_ATQH_S)
+#define PF_FW_ATQLEN 0x00080200
+#define PF_FW_ATQLEN_ATQLEN_S 0
+#define PF_FW_ATQLEN_ATQLEN_M ICE_M(0x3FF, PF_FW_ATQLEN_ATQLEN_S)
+#define PF_FW_ATQLEN_ATQVFE_S 28
+#define PF_FW_ATQLEN_ATQVFE_M BIT(PF_FW_ATQLEN_ATQVFE_S)
+#define PF_FW_ATQLEN_ATQOVFL_S 29
+#define PF_FW_ATQLEN_ATQOVFL_M BIT(PF_FW_ATQLEN_ATQOVFL_S)
+#define PF_FW_ATQLEN_ATQCRIT_S 30
+#define PF_FW_ATQLEN_ATQCRIT_M BIT(PF_FW_ATQLEN_ATQCRIT_S)
+#define PF_FW_ATQLEN_ATQENABLE_S 31
+#define PF_FW_ATQLEN_ATQENABLE_M BIT(PF_FW_ATQLEN_ATQENABLE_S)
+#define PF_FW_ATQT 0x00080400
+
+#define GLFLXP_RXDID_FLAGS(_i, _j) (0x0045D000 + ((_i) * 4 + (_j) * 256))
+#define GLFLXP_RXDID_FLAGS_FLEXIFLAG_4N_S 0
+#define GLFLXP_RXDID_FLAGS_FLEXIFLAG_4N_M ICE_M(0x3F, GLFLXP_RXDID_FLAGS_FLEXIFLAG_4N_S)
+#define GLFLXP_RXDID_FLAGS_FLEXIFLAG_4N_1_S 8
+#define GLFLXP_RXDID_FLAGS_FLEXIFLAG_4N_1_M ICE_M(0x3F, GLFLXP_RXDID_FLAGS_FLEXIFLAG_4N_1_S)
+#define GLFLXP_RXDID_FLAGS_FLEXIFLAG_4N_2_S 16
+#define GLFLXP_RXDID_FLAGS_FLEXIFLAG_4N_2_M ICE_M(0x3F, GLFLXP_RXDID_FLAGS_FLEXIFLAG_4N_2_S)
+#define GLFLXP_RXDID_FLAGS_FLEXIFLAG_4N_3_S 24
+#define GLFLXP_RXDID_FLAGS_FLEXIFLAG_4N_3_M ICE_M(0x3F, GLFLXP_RXDID_FLAGS_FLEXIFLAG_4N_3_S)
+#define GLFLXP_RXDID_FLX_WRD_0(_i) (0x0045c800 + ((_i) * 4))
+#define GLFLXP_RXDID_FLX_WRD_0_PROT_MDID_S 0
+#define GLFLXP_RXDID_FLX_WRD_0_PROT_MDID_M ICE_M(0xFF, GLFLXP_RXDID_FLX_WRD_0_PROT_MDID_S)
+#define GLFLXP_RXDID_FLX_WRD_0_RXDID_OPCODE_S 30
+#define GLFLXP_RXDID_FLX_WRD_0_RXDID_OPCODE_M ICE_M(0x3, GLFLXP_RXDID_FLX_WRD_0_RXDID_OPCODE_S)
+#define GLFLXP_RXDID_FLX_WRD_1(_i) (0x0045c900 + ((_i) * 4))
+#define GLFLXP_RXDID_FLX_WRD_1_PROT_MDID_S 0
+#define GLFLXP_RXDID_FLX_WRD_1_PROT_MDID_M ICE_M(0xFF, GLFLXP_RXDID_FLX_WRD_1_PROT_MDID_S)
+#define GLFLXP_RXDID_FLX_WRD_1_RXDID_OPCODE_S 30
+#define GLFLXP_RXDID_FLX_WRD_1_RXDID_OPCODE_M ICE_M(0x3, GLFLXP_RXDID_FLX_WRD_1_RXDID_OPCODE_S)
+#define GLFLXP_RXDID_FLX_WRD_2(_i) (0x0045ca00 + ((_i) * 4))
+#define GLFLXP_RXDID_FLX_WRD_2_PROT_MDID_S 0
+#define GLFLXP_RXDID_FLX_WRD_2_PROT_MDID_M ICE_M(0xFF, GLFLXP_RXDID_FLX_WRD_2_PROT_MDID_S)
+#define GLFLXP_RXDID_FLX_WRD_2_RXDID_OPCODE_S 30
+#define GLFLXP_RXDID_FLX_WRD_2_RXDID_OPCODE_M ICE_M(0x3, GLFLXP_RXDID_FLX_WRD_2_RXDID_OPCODE_S)
+#define GLFLXP_RXDID_FLX_WRD_3(_i) (0x0045cb00 + ((_i) * 4))
+#define GLFLXP_RXDID_FLX_WRD_3_PROT_MDID_S 0
+#define GLFLXP_RXDID_FLX_WRD_3_PROT_MDID_M ICE_M(0xFF, GLFLXP_RXDID_FLX_WRD_3_PROT_MDID_S)
+#define GLFLXP_RXDID_FLX_WRD_3_RXDID_OPCODE_S 30
+#define GLFLXP_RXDID_FLX_WRD_3_RXDID_OPCODE_M ICE_M(0x3, GLFLXP_RXDID_FLX_WRD_3_RXDID_OPCODE_S)
+
+#define QRXFLXP_CNTXT(_QRX) (0x00480000 + ((_QRX) * 4))
+#define QRXFLXP_CNTXT_RXDID_IDX_S 0
+#define QRXFLXP_CNTXT_RXDID_IDX_M ICE_M(0x3F, QRXFLXP_CNTXT_RXDID_IDX_S)
+#define QRXFLXP_CNTXT_RXDID_PRIO_S 8
+#define QRXFLXP_CNTXT_RXDID_PRIO_M ICE_M(0x7, QRXFLXP_CNTXT_RXDID_PRIO_S)
+#define QRXFLXP_CNTXT_TS_S 11
+#define QRXFLXP_CNTXT_TS_M BIT(QRXFLXP_CNTXT_TS_S)
+#define GLGEN_RSTAT 0x000B8188
+#define GLGEN_RSTAT_DEVSTATE_S 0
+#define GLGEN_RSTAT_DEVSTATE_M ICE_M(0x3, GLGEN_RSTAT_DEVSTATE_S)
+#define GLGEN_RSTCTL 0x000B8180
+#define GLGEN_RSTCTL_GRSTDEL_S 0
+#define GLGEN_RSTCTL_GRSTDEL_M ICE_M(0x3F, GLGEN_RSTCTL_GRSTDEL_S)
+#define GLGEN_RSTAT_RESET_TYPE_S 2
+#define GLGEN_RSTAT_RESET_TYPE_M ICE_M(0x3, GLGEN_RSTAT_RESET_TYPE_S)
+#define GLGEN_RTRIG 0x000B8190
+#define GLGEN_RTRIG_CORER_S 0
+#define GLGEN_RTRIG_CORER_M BIT(GLGEN_RTRIG_CORER_S)
+#define GLGEN_RTRIG_GLOBR_S 1
+#define GLGEN_RTRIG_GLOBR_M BIT(GLGEN_RTRIG_GLOBR_S)
+#define GLGEN_STAT 0x000B612C
+#define PFGEN_CTRL 0x00091000
+#define PFGEN_CTRL_PFSWR_S 0
+#define PFGEN_CTRL_PFSWR_M BIT(PFGEN_CTRL_PFSWR_S)
+#define PFGEN_STATE 0x00088000
+#define PRTGEN_STATUS 0x000B8100
+#define PFHMC_ERRORDATA 0x00520500
+#define PFHMC_ERRORINFO 0x00520400
+#define GLINT_DYN_CTL(_INT) (0x00160000 + ((_INT) * 4))
+#define GLINT_DYN_CTL_INTENA_S 0
+#define GLINT_DYN_CTL_INTENA_M BIT(GLINT_DYN_CTL_INTENA_S)
+#define GLINT_DYN_CTL_CLEARPBA_S 1
+#define GLINT_DYN_CTL_CLEARPBA_M BIT(GLINT_DYN_CTL_CLEARPBA_S)
+#define GLINT_DYN_CTL_SWINT_TRIG_S 2
+#define GLINT_DYN_CTL_SWINT_TRIG_M BIT(GLINT_DYN_CTL_SWINT_TRIG_S)
+#define GLINT_DYN_CTL_ITR_INDX_S 3
+#define GLINT_DYN_CTL_SW_ITR_INDX_S 25
+#define GLINT_DYN_CTL_SW_ITR_INDX_M ICE_M(0x3, GLINT_DYN_CTL_SW_ITR_INDX_S)
+#define GLINT_DYN_CTL_INTENA_MSK_S 31
+#define GLINT_DYN_CTL_INTENA_MSK_M BIT(GLINT_DYN_CTL_INTENA_MSK_S)
+#define GLINT_ITR(_i, _INT) (0x00154000 + ((_i) * 8192 + (_INT) * 4))
+#define PFINT_FW_CTL 0x0016C800
+#define PFINT_FW_CTL_MSIX_INDX_S 0
+#define PFINT_FW_CTL_MSIX_INDX_M ICE_M(0x7FF, PFINT_FW_CTL_MSIX_INDX_S)
+#define PFINT_FW_CTL_ITR_INDX_S 11
+#define PFINT_FW_CTL_ITR_INDX_M ICE_M(0x3, PFINT_FW_CTL_ITR_INDX_S)
+#define PFINT_FW_CTL_CAUSE_ENA_S 30
+#define PFINT_FW_CTL_CAUSE_ENA_M BIT(PFINT_FW_CTL_CAUSE_ENA_S)
+#define PFINT_OICR 0x0016CA00
+#define PFINT_OICR_HLP_RDY_S 14
+#define PFINT_OICR_HLP_RDY_M BIT(PFINT_OICR_HLP_RDY_S)
+#define PFINT_OICR_CPM_RDY_S 15
+#define PFINT_OICR_CPM_RDY_M BIT(PFINT_OICR_CPM_RDY_S)
+#define PFINT_OICR_ECC_ERR_S 16
+#define PFINT_OICR_ECC_ERR_M BIT(PFINT_OICR_ECC_ERR_S)
+#define PFINT_OICR_MAL_DETECT_S 19
+#define PFINT_OICR_MAL_DETECT_M BIT(PFINT_OICR_MAL_DETECT_S)
+#define PFINT_OICR_GRST_S 20
+#define PFINT_OICR_GRST_M BIT(PFINT_OICR_GRST_S)
+#define PFINT_OICR_PCI_EXCEPTION_S 21
+#define PFINT_OICR_PCI_EXCEPTION_M BIT(PFINT_OICR_PCI_EXCEPTION_S)
+#define PFINT_OICR_GPIO_S 22
+#define PFINT_OICR_GPIO_M BIT(PFINT_OICR_GPIO_S)
+#define PFINT_OICR_STORM_DETECT_S 24
+#define PFINT_OICR_STORM_DETECT_M BIT(PFINT_OICR_STORM_DETECT_S)
+#define PFINT_OICR_HMC_ERR_S 26
+#define PFINT_OICR_HMC_ERR_M BIT(PFINT_OICR_HMC_ERR_S)
+#define PFINT_OICR_PE_CRITERR_S 28
+#define PFINT_OICR_PE_CRITERR_M BIT(PFINT_OICR_PE_CRITERR_S)
+#define PFINT_OICR_CTL 0x0016CA80
+#define PFINT_OICR_CTL_MSIX_INDX_S 0
+#define PFINT_OICR_CTL_MSIX_INDX_M ICE_M(0x7FF, PFINT_OICR_CTL_MSIX_INDX_S)
+#define PFINT_OICR_CTL_ITR_INDX_S 11
+#define PFINT_OICR_CTL_ITR_INDX_M ICE_M(0x3, PFINT_OICR_CTL_ITR_INDX_S)
+#define PFINT_OICR_CTL_CAUSE_ENA_S 30
+#define PFINT_OICR_CTL_CAUSE_ENA_M BIT(PFINT_OICR_CTL_CAUSE_ENA_S)
+#define PFINT_OICR_ENA 0x0016C900
+#define QINT_RQCTL(_QRX) (0x00150000 + ((_QRX) * 4))
+#define QINT_RQCTL_MSIX_INDX_S 0
+#define QINT_RQCTL_ITR_INDX_S 11
+#define QINT_RQCTL_CAUSE_ENA_S 30
+#define QINT_RQCTL_CAUSE_ENA_M BIT(QINT_RQCTL_CAUSE_ENA_S)
+#define QINT_TQCTL(_DBQM) (0x00140000 + ((_DBQM) * 4))
+#define QINT_TQCTL_MSIX_INDX_S 0
+#define QINT_TQCTL_ITR_INDX_S 11
+#define QINT_TQCTL_CAUSE_ENA_S 30
+#define QINT_TQCTL_CAUSE_ENA_M BIT(QINT_TQCTL_CAUSE_ENA_S)
+#define GLLAN_RCTL_0 0x002941F8
+#define QRX_CONTEXT(_i, _QRX) (0x00280000 + ((_i) * 8192 + (_QRX) * 4))
+#define QRX_CTRL(_QRX) (0x00120000 + ((_QRX) * 4))
+#define QRX_CTRL_MAX_INDEX 2047
+#define QRX_CTRL_QENA_REQ_S 0
+#define QRX_CTRL_QENA_REQ_M BIT(QRX_CTRL_QENA_REQ_S)
+#define QRX_CTRL_QENA_STAT_S 2
+#define QRX_CTRL_QENA_STAT_M BIT(QRX_CTRL_QENA_STAT_S)
+#define QRX_ITR(_QRX) (0x00292000 + ((_QRX) * 4))
+#define QRX_TAIL(_QRX) (0x00290000 + ((_QRX) * 4))
+#define GLNVM_FLA 0x000B6108
+#define GLNVM_FLA_LOCKED_S 6
+#define GLNVM_FLA_LOCKED_M BIT(GLNVM_FLA_LOCKED_S)
+#define GLNVM_GENS 0x000B6100
+#define GLNVM_GENS_SR_SIZE_S 5
+#define GLNVM_GENS_SR_SIZE_M ICE_M(0x7, GLNVM_GENS_SR_SIZE_S)
+#define GLNVM_ULD 0x000B6008
+#define GLNVM_ULD_CORER_DONE_S 3
+#define GLNVM_ULD_CORER_DONE_M BIT(GLNVM_ULD_CORER_DONE_S)
+#define GLNVM_ULD_GLOBR_DONE_S 4
+#define GLNVM_ULD_GLOBR_DONE_M BIT(GLNVM_ULD_GLOBR_DONE_S)
+#define PF_FUNC_RID 0x0009E880
+#define PF_FUNC_RID_FUNC_NUM_S 0
+#define PF_FUNC_RID_FUNC_NUM_M ICE_M(0x7, PF_FUNC_RID_FUNC_NUM_S)
+#define GLPRT_BPRCH(_i) (0x00381384 + ((_i) * 8))
+#define GLPRT_BPRCL(_i) (0x00381380 + ((_i) * 8))
+#define GLPRT_BPTCH(_i) (0x00381244 + ((_i) * 8))
+#define GLPRT_BPTCL(_i) (0x00381240 + ((_i) * 8))
+#define GLPRT_CRCERRS(_i) (0x00380100 + ((_i) * 8))
+#define GLPRT_GORCH(_i) (0x00380004 + ((_i) * 8))
+#define GLPRT_GORCL(_i) (0x00380000 + ((_i) * 8))
+#define GLPRT_GOTCH(_i) (0x00380B44 + ((_i) * 8))
+#define GLPRT_GOTCL(_i) (0x00380B40 + ((_i) * 8))
+#define GLPRT_ILLERRC(_i) (0x003801C0 + ((_i) * 8))
+#define GLPRT_LXOFFRXC(_i) (0x003802C0 + ((_i) * 8))
+#define GLPRT_LXOFFTXC(_i) (0x00381180 + ((_i) * 8))
+#define GLPRT_LXONRXC(_i) (0x00380280 + ((_i) * 8))
+#define GLPRT_LXONTXC(_i) (0x00381140 + ((_i) * 8))
+#define GLPRT_MLFC(_i) (0x00380040 + ((_i) * 8))
+#define GLPRT_MPRCH(_i) (0x00381344 + ((_i) * 8))
+#define GLPRT_MPRCL(_i) (0x00381340 + ((_i) * 8))
+#define GLPRT_MPTCH(_i) (0x00381204 + ((_i) * 8))
+#define GLPRT_MPTCL(_i) (0x00381200 + ((_i) * 8))
+#define GLPRT_MRFC(_i) (0x00380080 + ((_i) * 8))
+#define GLPRT_PRC1023H(_i) (0x00380A04 + ((_i) * 8))
+#define GLPRT_PRC1023L(_i) (0x00380A00 + ((_i) * 8))
+#define GLPRT_PRC127H(_i) (0x00380944 + ((_i) * 8))
+#define GLPRT_PRC127L(_i) (0x00380940 + ((_i) * 8))
+#define GLPRT_PRC1522H(_i) (0x00380A44 + ((_i) * 8))
+#define GLPRT_PRC1522L(_i) (0x00380A40 + ((_i) * 8))
+#define GLPRT_PRC255H(_i) (0x00380984 + ((_i) * 8))
+#define GLPRT_PRC255L(_i) (0x00380980 + ((_i) * 8))
+#define GLPRT_PRC511H(_i) (0x003809C4 + ((_i) * 8))
+#define GLPRT_PRC511L(_i) (0x003809C0 + ((_i) * 8))
+#define GLPRT_PRC64H(_i) (0x00380904 + ((_i) * 8))
+#define GLPRT_PRC64L(_i) (0x00380900 + ((_i) * 8))
+#define GLPRT_PRC9522H(_i) (0x00380A84 + ((_i) * 8))
+#define GLPRT_PRC9522L(_i) (0x00380A80 + ((_i) * 8))
+#define GLPRT_PTC1023H(_i) (0x00380C84 + ((_i) * 8))
+#define GLPRT_PTC1023L(_i) (0x00380C80 + ((_i) * 8))
+#define GLPRT_PTC127H(_i) (0x00380BC4 + ((_i) * 8))
+#define GLPRT_PTC127L(_i) (0x00380BC0 + ((_i) * 8))
+#define GLPRT_PTC1522H(_i) (0x00380CC4 + ((_i) * 8))
+#define GLPRT_PTC1522L(_i) (0x00380CC0 + ((_i) * 8))
+#define GLPRT_PTC255H(_i) (0x00380C04 + ((_i) * 8))
+#define GLPRT_PTC255L(_i) (0x00380C00 + ((_i) * 8))
+#define GLPRT_PTC511H(_i) (0x00380C44 + ((_i) * 8))
+#define GLPRT_PTC511L(_i) (0x00380C40 + ((_i) * 8))
+#define GLPRT_PTC64H(_i) (0x00380B84 + ((_i) * 8))
+#define GLPRT_PTC64L(_i) (0x00380B80 + ((_i) * 8))
+#define GLPRT_PTC9522H(_i) (0x00380D04 + ((_i) * 8))
+#define GLPRT_PTC9522L(_i) (0x00380D00 + ((_i) * 8))
+#define GLPRT_RFC(_i) (0x00380AC0 + ((_i) * 8))
+#define GLPRT_RJC(_i) (0x00380B00 + ((_i) * 8))
+#define GLPRT_RLEC(_i) (0x00380140 + ((_i) * 8))
+#define GLPRT_ROC(_i) (0x00380240 + ((_i) * 8))
+#define GLPRT_RUC(_i) (0x00380200 + ((_i) * 8))
+#define GLPRT_TDOLD(_i) (0x00381280 + ((_i) * 8))
+#define GLPRT_UPRCH(_i) (0x00381304 + ((_i) * 8))
+#define GLPRT_UPRCL(_i) (0x00381300 + ((_i) * 8))
+#define GLPRT_UPTCH(_i) (0x003811C4 + ((_i) * 8))
+#define GLPRT_UPTCL(_i) (0x003811C0 + ((_i) * 8))
+#define GLV_BPRCH(_i) (0x003B6004 + ((_i) * 8))
+#define GLV_BPRCL(_i) (0x003B6000 + ((_i) * 8))
+#define GLV_BPTCH(_i) (0x0030E004 + ((_i) * 8))
+#define GLV_BPTCL(_i) (0x0030E000 + ((_i) * 8))
+#define GLV_GORCH(_i) (0x003B0004 + ((_i) * 8))
+#define GLV_GORCL(_i) (0x003B0000 + ((_i) * 8))
+#define GLV_GOTCH(_i) (0x00300004 + ((_i) * 8))
+#define GLV_GOTCL(_i) (0x00300000 + ((_i) * 8))
+#define GLV_MPRCH(_i) (0x003B4004 + ((_i) * 8))
+#define GLV_MPRCL(_i) (0x003B4000 + ((_i) * 8))
+#define GLV_MPTCH(_i) (0x0030C004 + ((_i) * 8))
+#define GLV_MPTCL(_i) (0x0030C000 + ((_i) * 8))
+#define GLV_RDPC(_i) (0x00294C04 + ((_i) * 4))
+#define GLV_TEPC(_VSI) (0x00312000 + ((_VSI) * 4))
+#define GLV_UPRCH(_i) (0x003B2004 + ((_i) * 8))
+#define GLV_UPRCL(_i) (0x003B2000 + ((_i) * 8))
+#define GLV_UPTCH(_i) (0x0030A004 + ((_i) * 8))
+#define GLV_UPTCL(_i) (0x0030A000 + ((_i) * 8))
+#define VSIQF_HKEY_MAX_INDEX 12
+
+#endif /* _ICE_HW_AUTOGEN_H_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_lan_tx_rx.h b/drivers/net/ethernet/intel/ice/ice_lan_tx_rx.h
new file mode 100644
index 000000000000..d23a91665b46
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_lan_tx_rx.h
@@ -0,0 +1,473 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018, Intel Corporation. */
+
+#ifndef _ICE_LAN_TX_RX_H_
+#define _ICE_LAN_TX_RX_H_
+
+union ice_32byte_rx_desc {
+ struct {
+ __le64 pkt_addr; /* Packet buffer address */
+ __le64 hdr_addr; /* Header buffer address */
+ /* bit 0 of hdr_addr is DD bit */
+ __le64 rsvd1;
+ __le64 rsvd2;
+ } read;
+ struct {
+ struct {
+ struct {
+ __le16 mirroring_status;
+ __le16 l2tag1;
+ } lo_dword;
+ union {
+ __le32 rss; /* RSS Hash */
+ __le32 fd_id; /* Flow Director filter id */
+ } hi_dword;
+ } qword0;
+ struct {
+ /* status/error/PTYPE/length */
+ __le64 status_error_len;
+ } qword1;
+ struct {
+ __le16 ext_status; /* extended status */
+ __le16 rsvd;
+ __le16 l2tag2_1;
+ __le16 l2tag2_2;
+ } qword2;
+ struct {
+ __le32 reserved;
+ __le32 fd_id;
+ } qword3;
+ } wb; /* writeback */
+};
+
+struct ice_rx_ptype_decoded {
+ u32 ptype:10;
+ u32 known:1;
+ u32 outer_ip:1;
+ u32 outer_ip_ver:2;
+ u32 outer_frag:1;
+ u32 tunnel_type:3;
+ u32 tunnel_end_prot:2;
+ u32 tunnel_end_frag:1;
+ u32 inner_prot:4;
+ u32 payload_layer:3;
+};
+
+enum ice_rx_ptype_outer_ip {
+ ICE_RX_PTYPE_OUTER_L2 = 0,
+ ICE_RX_PTYPE_OUTER_IP = 1,
+};
+
+enum ice_rx_ptype_outer_ip_ver {
+ ICE_RX_PTYPE_OUTER_NONE = 0,
+ ICE_RX_PTYPE_OUTER_IPV4 = 1,
+ ICE_RX_PTYPE_OUTER_IPV6 = 2,
+};
+
+enum ice_rx_ptype_outer_fragmented {
+ ICE_RX_PTYPE_NOT_FRAG = 0,
+ ICE_RX_PTYPE_FRAG = 1,
+};
+
+enum ice_rx_ptype_tunnel_type {
+ ICE_RX_PTYPE_TUNNEL_NONE = 0,
+ ICE_RX_PTYPE_TUNNEL_IP_IP = 1,
+ ICE_RX_PTYPE_TUNNEL_IP_GRENAT = 2,
+ ICE_RX_PTYPE_TUNNEL_IP_GRENAT_MAC = 3,
+ ICE_RX_PTYPE_TUNNEL_IP_GRENAT_MAC_VLAN = 4,
+};
+
+enum ice_rx_ptype_tunnel_end_prot {
+ ICE_RX_PTYPE_TUNNEL_END_NONE = 0,
+ ICE_RX_PTYPE_TUNNEL_END_IPV4 = 1,
+ ICE_RX_PTYPE_TUNNEL_END_IPV6 = 2,
+};
+
+enum ice_rx_ptype_inner_prot {
+ ICE_RX_PTYPE_INNER_PROT_NONE = 0,
+ ICE_RX_PTYPE_INNER_PROT_UDP = 1,
+ ICE_RX_PTYPE_INNER_PROT_TCP = 2,
+ ICE_RX_PTYPE_INNER_PROT_SCTP = 3,
+ ICE_RX_PTYPE_INNER_PROT_ICMP = 4,
+ ICE_RX_PTYPE_INNER_PROT_TIMESYNC = 5,
+};
+
+enum ice_rx_ptype_payload_layer {
+ ICE_RX_PTYPE_PAYLOAD_LAYER_NONE = 0,
+ ICE_RX_PTYPE_PAYLOAD_LAYER_PAY2 = 1,
+ ICE_RX_PTYPE_PAYLOAD_LAYER_PAY3 = 2,
+ ICE_RX_PTYPE_PAYLOAD_LAYER_PAY4 = 3,
+};
+
+/* RX Flex Descriptor
+ * This descriptor is used instead of the legacy version descriptor when
+ * ice_rlan_ctx.adv_desc is set
+ */
+union ice_32b_rx_flex_desc {
+ struct {
+ __le64 pkt_addr; /* Packet buffer address */
+ __le64 hdr_addr; /* Header buffer address */
+ /* bit 0 of hdr_addr is DD bit */
+ __le64 rsvd1;
+ __le64 rsvd2;
+ } read;
+ struct {
+ /* Qword 0 */
+ u8 rxdid; /* descriptor builder profile id */
+ u8 mir_id_umb_cast; /* mirror=[5:0], umb=[7:6] */
+ __le16 ptype_flex_flags0; /* ptype=[9:0], ff0=[15:10] */
+ __le16 pkt_len; /* [15:14] are reserved */
+ __le16 hdr_len_sph_flex_flags1; /* header=[10:0] */
+ /* sph=[11:11] */
+ /* ff1/ext=[15:12] */
+
+ /* Qword 1 */
+ __le16 status_error0;
+ __le16 l2tag1;
+ __le16 flex_meta0;
+ __le16 flex_meta1;
+
+ /* Qword 2 */
+ __le16 status_error1;
+ u8 flex_flags2;
+ u8 time_stamp_low;
+ __le16 l2tag2_1st;
+ __le16 l2tag2_2nd;
+
+ /* Qword 3 */
+ __le16 flex_meta2;
+ __le16 flex_meta3;
+ union {
+ struct {
+ __le16 flex_meta4;
+ __le16 flex_meta5;
+ } flex;
+ __le32 ts_high;
+ } flex_ts;
+ } wb; /* writeback */
+};
+
+/* Rx Flex Descriptor NIC Profile
+ * This descriptor corresponds to RxDID 2 which contains
+ * metadata fields for RSS, flow id and timestamp info
+ */
+struct ice_32b_rx_flex_desc_nic {
+ /* Qword 0 */
+ u8 rxdid;
+ u8 mir_id_umb_cast;
+ __le16 ptype_flexi_flags0;
+ __le16 pkt_len;
+ __le16 hdr_len_sph_flex_flags1;
+
+ /* Qword 1 */
+ __le16 status_error0;
+ __le16 l2tag1;
+ __le32 rss_hash;
+
+ /* Qword 2 */
+ __le16 status_error1;
+ u8 flexi_flags2;
+ u8 ts_low;
+ __le16 l2tag2_1st;
+ __le16 l2tag2_2nd;
+
+ /* Qword 3 */
+ __le32 flow_id;
+ union {
+ struct {
+ __le16 vlan_id;
+ __le16 flow_id_ipv6;
+ } flex;
+ __le32 ts_high;
+ } flex_ts;
+};
+
+/* Receive Flex Descriptor profile IDs: There are a total
+ * of 64 profiles where profile IDs 0/1 are for legacy; and
+ * profiles 2-63 are flex profiles that can be programmed
+ * with a specific metadata (profile 7 reserved for HW)
+ */
+enum ice_rxdid {
+ ICE_RXDID_START = 0,
+ ICE_RXDID_LEGACY_0 = ICE_RXDID_START,
+ ICE_RXDID_LEGACY_1,
+ ICE_RXDID_FLX_START,
+ ICE_RXDID_FLEX_NIC = ICE_RXDID_FLX_START,
+ ICE_RXDID_FLX_LAST = 63,
+ ICE_RXDID_LAST = ICE_RXDID_FLX_LAST
+};
+
+/* Receive Flex Descriptor Rx opcode values */
+#define ICE_RX_OPC_MDID 0x01
+
+/* Receive Descriptor MDID values */
+#define ICE_RX_MDID_FLOW_ID_LOWER 5
+#define ICE_RX_MDID_FLOW_ID_HIGH 6
+#define ICE_RX_MDID_HASH_LOW 56
+#define ICE_RX_MDID_HASH_HIGH 57
+
+/* Rx Flag64 packet flag bits */
+enum ice_rx_flg64_bits {
+ ICE_RXFLG_PKT_DSI = 0,
+ ICE_RXFLG_EVLAN_x8100 = 15,
+ ICE_RXFLG_EVLAN_x9100,
+ ICE_RXFLG_VLAN_x8100,
+ ICE_RXFLG_TNL_MAC = 22,
+ ICE_RXFLG_TNL_VLAN,
+ ICE_RXFLG_PKT_FRG,
+ ICE_RXFLG_FIN = 32,
+ ICE_RXFLG_SYN,
+ ICE_RXFLG_RST,
+ ICE_RXFLG_TNL0 = 38,
+ ICE_RXFLG_TNL1,
+ ICE_RXFLG_TNL2,
+ ICE_RXFLG_UDP_GRE,
+ ICE_RXFLG_RSVD = 63
+};
+
+/* for ice_32byte_rx_flex_desc.ptype_flexi_flags0 member */
+#define ICE_RX_FLEX_DESC_PTYPE_M (0x3FF) /* 10-bits */
+
+/* for ice_32byte_rx_flex_desc.pkt_length member */
+#define ICE_RX_FLX_DESC_PKT_LEN_M (0x3FFF) /* 14-bits */
+
+enum ice_rx_flex_desc_status_error_0_bits {
+ /* Note: These are predefined bit offsets */
+ ICE_RX_FLEX_DESC_STATUS0_DD_S = 0,
+ ICE_RX_FLEX_DESC_STATUS0_EOF_S,
+ ICE_RX_FLEX_DESC_STATUS0_HBO_S,
+ ICE_RX_FLEX_DESC_STATUS0_L3L4P_S,
+ ICE_RX_FLEX_DESC_STATUS0_XSUM_IPE_S,
+ ICE_RX_FLEX_DESC_STATUS0_XSUM_L4E_S,
+ ICE_RX_FLEX_DESC_STATUS0_XSUM_EIPE_S,
+ ICE_RX_FLEX_DESC_STATUS0_XSUM_EUDPE_S,
+ ICE_RX_FLEX_DESC_STATUS0_LPBK_S,
+ ICE_RX_FLEX_DESC_STATUS0_IPV6EXADD_S,
+ ICE_RX_FLEX_DESC_STATUS0_RXE_S,
+ ICE_RX_FLEX_DESC_STATUS0_CRCP_S,
+ ICE_RX_FLEX_DESC_STATUS0_RSS_VALID_S,
+ ICE_RX_FLEX_DESC_STATUS0_L2TAG1P_S,
+ ICE_RX_FLEX_DESC_STATUS0_XTRMD0_VALID_S,
+ ICE_RX_FLEX_DESC_STATUS0_XTRMD1_VALID_S,
+ ICE_RX_FLEX_DESC_STATUS0_LAST /* this entry must be last!!! */
+};
+
+#define ICE_RXQ_CTX_SIZE_DWORDS 8
+#define ICE_RXQ_CTX_SZ (ICE_RXQ_CTX_SIZE_DWORDS * sizeof(u32))
+
+/* RLAN Rx queue context data
+ *
+ * The sizes of the variables may be larger than needed due to crossing byte
+ * boundaries. If we do not have the width of the variable set to the correct
+ * size then we could end up shifting bits off the top of the variable when the
+ * variable is at the top of a byte and crosses over into the next byte.
+ */
+struct ice_rlan_ctx {
+ u16 head;
+ u16 cpuid; /* bigger than needed, see above for reason */
+ u64 base;
+ u16 qlen;
+#define ICE_RLAN_CTX_DBUF_S 7
+ u16 dbuf; /* bigger than needed, see above for reason */
+#define ICE_RLAN_CTX_HBUF_S 6
+ u16 hbuf; /* bigger than needed, see above for reason */
+ u8 dtype;
+ u8 dsize;
+ u8 crcstrip;
+ u8 l2tsel;
+ u8 hsplit_0;
+ u8 hsplit_1;
+ u8 showiv;
+ u32 rxmax; /* bigger than needed, see above for reason */
+ u8 tphrdesc_ena;
+ u8 tphwdesc_ena;
+ u8 tphdata_ena;
+ u8 tphhead_ena;
+ u16 lrxqthresh; /* bigger than needed, see above for reason */
+};
+
+struct ice_ctx_ele {
+ u16 offset;
+ u16 size_of;
+ u16 width;
+ u16 lsb;
+};
+
+#define ICE_CTX_STORE(_struct, _ele, _width, _lsb) { \
+ .offset = offsetof(struct _struct, _ele), \
+ .size_of = FIELD_SIZEOF(struct _struct, _ele), \
+ .width = _width, \
+ .lsb = _lsb, \
+}
+
+/* for hsplit_0 field of Rx RLAN context */
+enum ice_rlan_ctx_rx_hsplit_0 {
+ ICE_RLAN_RX_HSPLIT_0_NO_SPLIT = 0,
+ ICE_RLAN_RX_HSPLIT_0_SPLIT_L2 = 1,
+ ICE_RLAN_RX_HSPLIT_0_SPLIT_IP = 2,
+ ICE_RLAN_RX_HSPLIT_0_SPLIT_TCP_UDP = 4,
+ ICE_RLAN_RX_HSPLIT_0_SPLIT_SCTP = 8,
+};
+
+/* for hsplit_1 field of Rx RLAN context */
+enum ice_rlan_ctx_rx_hsplit_1 {
+ ICE_RLAN_RX_HSPLIT_1_NO_SPLIT = 0,
+ ICE_RLAN_RX_HSPLIT_1_SPLIT_L2 = 1,
+ ICE_RLAN_RX_HSPLIT_1_SPLIT_ALWAYS = 2,
+};
+
+/* TX Descriptor */
+struct ice_tx_desc {
+ __le64 buf_addr; /* Address of descriptor's data buf */
+ __le64 cmd_type_offset_bsz;
+};
+
+enum ice_tx_desc_dtype_value {
+ ICE_TX_DESC_DTYPE_DATA = 0x0,
+ ICE_TX_DESC_DTYPE_CTX = 0x1,
+ /* DESC_DONE - HW has completed write-back of descriptor */
+ ICE_TX_DESC_DTYPE_DESC_DONE = 0xF,
+};
+
+#define ICE_TXD_QW1_CMD_S 4
+#define ICE_TXD_QW1_CMD_M (0xFFFUL << ICE_TXD_QW1_CMD_S)
+
+enum ice_tx_desc_cmd_bits {
+ ICE_TX_DESC_CMD_EOP = 0x0001,
+ ICE_TX_DESC_CMD_RS = 0x0002,
+ ICE_TX_DESC_CMD_IL2TAG1 = 0x0008,
+ ICE_TX_DESC_CMD_IIPT_IPV6 = 0x0020, /* 2 BITS */
+ ICE_TX_DESC_CMD_IIPT_IPV4 = 0x0040, /* 2 BITS */
+ ICE_TX_DESC_CMD_IIPT_IPV4_CSUM = 0x0060, /* 2 BITS */
+ ICE_TX_DESC_CMD_L4T_EOFT_TCP = 0x0100, /* 2 BITS */
+ ICE_TX_DESC_CMD_L4T_EOFT_UDP = 0x0300, /* 2 BITS */
+};
+
+#define ICE_TXD_QW1_OFFSET_S 16
+#define ICE_TXD_QW1_OFFSET_M (0x3FFFFULL << ICE_TXD_QW1_OFFSET_S)
+
+enum ice_tx_desc_len_fields {
+ /* Note: These are predefined bit offsets */
+ ICE_TX_DESC_LEN_MACLEN_S = 0, /* 7 BITS */
+ ICE_TX_DESC_LEN_IPLEN_S = 7, /* 7 BITS */
+ ICE_TX_DESC_LEN_L4_LEN_S = 14 /* 4 BITS */
+};
+
+#define ICE_TXD_QW1_MACLEN_M (0x7FUL << ICE_TX_DESC_LEN_MACLEN_S)
+#define ICE_TXD_QW1_IPLEN_M (0x7FUL << ICE_TX_DESC_LEN_IPLEN_S)
+#define ICE_TXD_QW1_L4LEN_M (0xFUL << ICE_TX_DESC_LEN_L4_LEN_S)
+
+/* Tx descriptor field limits in bytes */
+#define ICE_TXD_MACLEN_MAX ((ICE_TXD_QW1_MACLEN_M >> \
+ ICE_TX_DESC_LEN_MACLEN_S) * ICE_BYTES_PER_WORD)
+#define ICE_TXD_IPLEN_MAX ((ICE_TXD_QW1_IPLEN_M >> \
+ ICE_TX_DESC_LEN_IPLEN_S) * ICE_BYTES_PER_DWORD)
+#define ICE_TXD_L4LEN_MAX ((ICE_TXD_QW1_L4LEN_M >> \
+ ICE_TX_DESC_LEN_L4_LEN_S) * ICE_BYTES_PER_DWORD)
+
+#define ICE_TXD_QW1_TX_BUF_SZ_S 34
+#define ICE_TXD_QW1_L2TAG1_S 48
+
+/* Context descriptors */
+struct ice_tx_ctx_desc {
+ __le32 tunneling_params;
+ __le16 l2tag2;
+ __le16 rsvd;
+ __le64 qw1;
+};
+
+#define ICE_TXD_CTX_QW1_CMD_S 4
+#define ICE_TXD_CTX_QW1_CMD_M (0x7FUL << ICE_TXD_CTX_QW1_CMD_S)
+
+#define ICE_TXD_CTX_QW1_TSO_LEN_S 30
+#define ICE_TXD_CTX_QW1_TSO_LEN_M \
+ (0x3FFFFULL << ICE_TXD_CTX_QW1_TSO_LEN_S)
+
+#define ICE_TXD_CTX_QW1_MSS_S 50
+
+enum ice_tx_ctx_desc_cmd_bits {
+ ICE_TX_CTX_DESC_TSO = 0x01,
+ ICE_TX_CTX_DESC_TSYN = 0x02,
+ ICE_TX_CTX_DESC_IL2TAG2 = 0x04,
+ ICE_TX_CTX_DESC_IL2TAG2_IL2H = 0x08,
+ ICE_TX_CTX_DESC_SWTCH_NOTAG = 0x00,
+ ICE_TX_CTX_DESC_SWTCH_UPLINK = 0x10,
+ ICE_TX_CTX_DESC_SWTCH_LOCAL = 0x20,
+ ICE_TX_CTX_DESC_SWTCH_VSI = 0x30,
+ ICE_TX_CTX_DESC_RESERVED = 0x40
+};
+
+#define ICE_LAN_TXQ_MAX_QGRPS 127
+#define ICE_LAN_TXQ_MAX_QDIS 1023
+
+/* Tx queue context data
+ *
+ * The sizes of the variables may be larger than needed due to crossing byte
+ * boundaries. If we do not have the width of the variable set to the correct
+ * size then we could end up shifting bits off the top of the variable when the
+ * variable is at the top of a byte and crosses over into the next byte.
+ */
+struct ice_tlan_ctx {
+#define ICE_TLAN_CTX_BASE_S 7
+ u64 base; /* base is defined in 128-byte units */
+ u8 port_num;
+ u16 cgd_num; /* bigger than needed, see above for reason */
+ u8 pf_num;
+ u16 vmvf_num;
+ u8 vmvf_type;
+#define ICE_TLAN_CTX_VMVF_TYPE_VMQ 1
+#define ICE_TLAN_CTX_VMVF_TYPE_PF 2
+ u16 src_vsi;
+ u8 tsyn_ena;
+ u8 alt_vlan;
+ u16 cpuid; /* bigger than needed, see above for reason */
+ u8 wb_mode;
+ u8 tphrd_desc;
+ u8 tphrd;
+ u8 tphwr_desc;
+ u16 cmpq_id;
+ u16 qnum_in_func;
+ u8 itr_notification_mode;
+ u8 adjust_prof_id;
+ u32 qlen; /* bigger than needed, see above for reason */
+ u8 quanta_prof_idx;
+ u8 tso_ena;
+ u16 tso_qnum;
+ u8 legacy_int;
+ u8 drop_ena;
+ u8 cache_prof_idx;
+ u8 pkt_shaper_prof_idx;
+ u8 int_q_state; /* width not needed - internal do not write */
+};
+
+/* macro to make the table lines short */
+#define ICE_PTT(PTYPE, OUTER_IP, OUTER_IP_VER, OUTER_FRAG, T, TE, TEF, I, PL)\
+ { PTYPE, \
+ 1, \
+ ICE_RX_PTYPE_OUTER_##OUTER_IP, \
+ ICE_RX_PTYPE_OUTER_##OUTER_IP_VER, \
+ ICE_RX_PTYPE_##OUTER_FRAG, \
+ ICE_RX_PTYPE_TUNNEL_##T, \
+ ICE_RX_PTYPE_TUNNEL_END_##TE, \
+ ICE_RX_PTYPE_##TEF, \
+ ICE_RX_PTYPE_INNER_PROT_##I, \
+ ICE_RX_PTYPE_PAYLOAD_LAYER_##PL }
+
+#define ICE_PTT_UNUSED_ENTRY(PTYPE) { PTYPE, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+
+/* shorter macros makes the table fit but are terse */
+#define ICE_RX_PTYPE_NOF ICE_RX_PTYPE_NOT_FRAG
+
+/* Lookup table mapping the HW PTYPE to the bit field for decoding */
+static const struct ice_rx_ptype_decoded ice_ptype_lkup[] = {
+ /* L2 Packet types */
+ ICE_PTT_UNUSED_ENTRY(0),
+ ICE_PTT(1, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY2),
+ ICE_PTT(2, L2, NONE, NOF, NONE, NONE, NOF, NONE, NONE),
+};
+
+static inline struct ice_rx_ptype_decoded ice_decode_rx_desc_ptype(u16 ptype)
+{
+ return ice_ptype_lkup[ptype];
+}
+#endif /* _ICE_LAN_TX_RX_H_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
new file mode 100644
index 000000000000..5299caf55a7f
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -0,0 +1,5491 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2018, Intel Corporation. */
+
+/* Intel(R) Ethernet Connection E800 Series Linux Driver */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "ice.h"
+
+#define DRV_VERSION "ice-0.7.0-k"
+#define DRV_SUMMARY "Intel(R) Ethernet Connection E800 Series Linux Driver"
+const char ice_drv_ver[] = DRV_VERSION;
+static const char ice_driver_string[] = DRV_SUMMARY;
+static const char ice_copyright[] = "Copyright (c) 2018, Intel Corporation.";
+
+MODULE_AUTHOR("Intel Corporation, <linux.nics@intel.com>");
+MODULE_DESCRIPTION(DRV_SUMMARY);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
+static int debug = -1;
+module_param(debug, int, 0644);
+#ifndef CONFIG_DYNAMIC_DEBUG
+MODULE_PARM_DESC(debug, "netif level (0=none,...,16=all), hw debug_mask (0x8XXXXXXX)");
+#else
+MODULE_PARM_DESC(debug, "netif level (0=none,...,16=all)");
+#endif /* !CONFIG_DYNAMIC_DEBUG */
+
+static struct workqueue_struct *ice_wq;
+static const struct net_device_ops ice_netdev_ops;
+
+static void ice_pf_dis_all_vsi(struct ice_pf *pf);
+static void ice_rebuild(struct ice_pf *pf);
+static int ice_vsi_release(struct ice_vsi *vsi);
+static void ice_update_vsi_stats(struct ice_vsi *vsi);
+static void ice_update_pf_stats(struct ice_pf *pf);
+
+/**
+ * ice_get_free_slot - get the next non-NULL location index in array
+ * @array: array to search
+ * @size: size of the array
+ * @curr: last known occupied index to be used as a search hint
+ *
+ * void * is being used to keep the functionality generic. This lets us use this
+ * function on any array of pointers.
+ */
+static int ice_get_free_slot(void *array, int size, int curr)
+{
+ int **tmp_array = (int **)array;
+ int next;
+
+ if (curr < (size - 1) && !tmp_array[curr + 1]) {
+ next = curr + 1;
+ } else {
+ int i = 0;
+
+ while ((i < size) && (tmp_array[i]))
+ i++;
+ if (i == size)
+ next = ICE_NO_VSI;
+ else
+ next = i;
+ }
+ return next;
+}
+
+/**
+ * ice_search_res - Search the tracker for a block of resources
+ * @res: pointer to the resource
+ * @needed: size of the block needed
+ * @id: identifier to track owner
+ * Returns the base item index of the block, or -ENOMEM for error
+ */
+static int ice_search_res(struct ice_res_tracker *res, u16 needed, u16 id)
+{
+ int start = res->search_hint;
+ int end = start;
+
+ id |= ICE_RES_VALID_BIT;
+
+ do {
+ /* skip already allocated entries */
+ if (res->list[end++] & ICE_RES_VALID_BIT) {
+ start = end;
+ if ((start + needed) > res->num_entries)
+ break;
+ }
+
+ if (end == (start + needed)) {
+ int i = start;
+
+ /* there was enough, so assign it to the requestor */
+ while (i != end)
+ res->list[i++] = id;
+
+ if (end == res->num_entries)
+ end = 0;
+
+ res->search_hint = end;
+ return start;
+ }
+ } while (1);
+
+ return -ENOMEM;
+}
+
+/**
+ * ice_get_res - get a block of resources
+ * @pf: board private structure
+ * @res: pointer to the resource
+ * @needed: size of the block needed
+ * @id: identifier to track owner
+ *
+ * Returns the base item index of the block, or -ENOMEM for error
+ * The search_hint trick and lack of advanced fit-finding only works
+ * because we're highly likely to have all the same sized requests.
+ * Linear search time and any fragmentation should be minimal.
+ */
+static int
+ice_get_res(struct ice_pf *pf, struct ice_res_tracker *res, u16 needed, u16 id)
+{
+ int ret;
+
+ if (!res || !pf)
+ return -EINVAL;
+
+ if (!needed || needed > res->num_entries || id >= ICE_RES_VALID_BIT) {
+ dev_err(&pf->pdev->dev,
+ "param err: needed=%d, num_entries = %d id=0x%04x\n",
+ needed, res->num_entries, id);
+ return -EINVAL;
+ }
+
+ /* search based on search_hint */
+ ret = ice_search_res(res, needed, id);
+
+ if (ret < 0) {
+ /* previous search failed. Reset search hint and try again */
+ res->search_hint = 0;
+ ret = ice_search_res(res, needed, id);
+ }
+
+ return ret;
+}
+
+/**
+ * ice_free_res - free a block of resources
+ * @res: pointer to the resource
+ * @index: starting index previously returned by ice_get_res
+ * @id: identifier to track owner
+ * Returns number of resources freed
+ */
+static int ice_free_res(struct ice_res_tracker *res, u16 index, u16 id)
+{
+ int count = 0;
+ int i;
+
+ if (!res || index >= res->num_entries)
+ return -EINVAL;
+
+ id |= ICE_RES_VALID_BIT;
+ for (i = index; i < res->num_entries && res->list[i] == id; i++) {
+ res->list[i] = 0;
+ count++;
+ }
+
+ return count;
+}
+
+/**
+ * ice_add_mac_to_list - Add a mac address filter entry to the list
+ * @vsi: the VSI to be forwarded to
+ * @add_list: pointer to the list which contains MAC filter entries
+ * @macaddr: the MAC address to be added.
+ *
+ * Adds mac address filter entry to the temp list
+ *
+ * Returns 0 on success or ENOMEM on failure.
+ */
+static int ice_add_mac_to_list(struct ice_vsi *vsi, struct list_head *add_list,
+ const u8 *macaddr)
+{
+ struct ice_fltr_list_entry *tmp;
+ struct ice_pf *pf = vsi->back;
+
+ tmp = devm_kzalloc(&pf->pdev->dev, sizeof(*tmp), GFP_ATOMIC);
+ if (!tmp)
+ return -ENOMEM;
+
+ tmp->fltr_info.flag = ICE_FLTR_TX;
+ tmp->fltr_info.src = vsi->vsi_num;
+ tmp->fltr_info.lkup_type = ICE_SW_LKUP_MAC;
+ tmp->fltr_info.fltr_act = ICE_FWD_TO_VSI;
+ tmp->fltr_info.fwd_id.vsi_id = vsi->vsi_num;
+ ether_addr_copy(tmp->fltr_info.l_data.mac.mac_addr, macaddr);
+
+ INIT_LIST_HEAD(&tmp->list_entry);
+ list_add(&tmp->list_entry, add_list);
+
+ return 0;
+}
+
+/**
+ * ice_add_mac_to_sync_list - creates list of mac addresses to be synced
+ * @netdev: the net device on which the sync is happening
+ * @addr: mac address to sync
+ *
+ * This is a callback function which is called by the in kernel device sync
+ * functions (like __dev_uc_sync, __dev_mc_sync, etc). This function only
+ * populates the tmp_sync_list, which is later used by ice_add_mac to add the
+ * mac filters from the hardware.
+ */
+static int ice_add_mac_to_sync_list(struct net_device *netdev, const u8 *addr)
+{
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct ice_vsi *vsi = np->vsi;
+
+ if (ice_add_mac_to_list(vsi, &vsi->tmp_sync_list, addr))
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * ice_add_mac_to_unsync_list - creates list of mac addresses to be unsynced
+ * @netdev: the net device on which the unsync is happening
+ * @addr: mac address to unsync
+ *
+ * This is a callback function which is called by the in kernel device unsync
+ * functions (like __dev_uc_unsync, __dev_mc_unsync, etc). This function only
+ * populates the tmp_unsync_list, which is later used by ice_remove_mac to
+ * delete the mac filters from the hardware.
+ */
+static int ice_add_mac_to_unsync_list(struct net_device *netdev, const u8 *addr)
+{
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct ice_vsi *vsi = np->vsi;
+
+ if (ice_add_mac_to_list(vsi, &vsi->tmp_unsync_list, addr))
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * ice_free_fltr_list - free filter lists helper
+ * @dev: pointer to the device struct
+ * @h: pointer to the list head to be freed
+ *
+ * Helper function to free filter lists previously created using
+ * ice_add_mac_to_list
+ */
+static void ice_free_fltr_list(struct device *dev, struct list_head *h)
+{
+ struct ice_fltr_list_entry *e, *tmp;
+
+ list_for_each_entry_safe(e, tmp, h, list_entry) {
+ list_del(&e->list_entry);
+ devm_kfree(dev, e);
+ }
+}
+
+/**
+ * ice_vsi_fltr_changed - check if filter state changed
+ * @vsi: VSI to be checked
+ *
+ * returns true if filter state has changed, false otherwise.
+ */
+static bool ice_vsi_fltr_changed(struct ice_vsi *vsi)
+{
+ return test_bit(ICE_VSI_FLAG_UMAC_FLTR_CHANGED, vsi->flags) ||
+ test_bit(ICE_VSI_FLAG_MMAC_FLTR_CHANGED, vsi->flags) ||
+ test_bit(ICE_VSI_FLAG_VLAN_FLTR_CHANGED, vsi->flags);
+}
+
+/**
+ * ice_vsi_sync_fltr - Update the VSI filter list to the HW
+ * @vsi: ptr to the VSI
+ *
+ * Push any outstanding VSI filter changes through the AdminQ.
+ */
+static int ice_vsi_sync_fltr(struct ice_vsi *vsi)
+{
+ struct device *dev = &vsi->back->pdev->dev;
+ struct net_device *netdev = vsi->netdev;
+ bool promisc_forced_on = false;
+ struct ice_pf *pf = vsi->back;
+ struct ice_hw *hw = &pf->hw;
+ enum ice_status status = 0;
+ u32 changed_flags = 0;
+ int err = 0;
+
+ if (!vsi->netdev)
+ return -EINVAL;
+
+ while (test_and_set_bit(__ICE_CFG_BUSY, vsi->state))
+ usleep_range(1000, 2000);
+
+ changed_flags = vsi->current_netdev_flags ^ vsi->netdev->flags;
+ vsi->current_netdev_flags = vsi->netdev->flags;
+
+ INIT_LIST_HEAD(&vsi->tmp_sync_list);
+ INIT_LIST_HEAD(&vsi->tmp_unsync_list);
+
+ if (ice_vsi_fltr_changed(vsi)) {
+ clear_bit(ICE_VSI_FLAG_UMAC_FLTR_CHANGED, vsi->flags);
+ clear_bit(ICE_VSI_FLAG_MMAC_FLTR_CHANGED, vsi->flags);
+ clear_bit(ICE_VSI_FLAG_VLAN_FLTR_CHANGED, vsi->flags);
+
+ /* grab the netdev's addr_list_lock */
+ netif_addr_lock_bh(netdev);
+ __dev_uc_sync(netdev, ice_add_mac_to_sync_list,
+ ice_add_mac_to_unsync_list);
+ __dev_mc_sync(netdev, ice_add_mac_to_sync_list,
+ ice_add_mac_to_unsync_list);
+ /* our temp lists are populated. release lock */
+ netif_addr_unlock_bh(netdev);
+ }
+
+ /* Remove mac addresses in the unsync list */
+ status = ice_remove_mac(hw, &vsi->tmp_unsync_list);
+ ice_free_fltr_list(dev, &vsi->tmp_unsync_list);
+ if (status) {
+ netdev_err(netdev, "Failed to delete MAC filters\n");
+ /* if we failed because of alloc failures, just bail */
+ if (status == ICE_ERR_NO_MEMORY) {
+ err = -ENOMEM;
+ goto out;
+ }
+ }
+
+ /* Add mac addresses in the sync list */
+ status = ice_add_mac(hw, &vsi->tmp_sync_list);
+ ice_free_fltr_list(dev, &vsi->tmp_sync_list);
+ if (status) {
+ netdev_err(netdev, "Failed to add MAC filters\n");
+ /* If there is no more space for new umac filters, vsi
+ * should go into promiscuous mode. There should be some
+ * space reserved for promiscuous filters.
+ */
+ if (hw->adminq.sq_last_status == ICE_AQ_RC_ENOSPC &&
+ !test_and_set_bit(__ICE_FLTR_OVERFLOW_PROMISC,
+ vsi->state)) {
+ promisc_forced_on = true;
+ netdev_warn(netdev,
+ "Reached MAC filter limit, forcing promisc mode on VSI %d\n",
+ vsi->vsi_num);
+ } else {
+ err = -EIO;
+ goto out;
+ }
+ }
+ /* check for changes in promiscuous modes */
+ if (changed_flags & IFF_ALLMULTI)
+ netdev_warn(netdev, "Unsupported configuration\n");
+
+ if (((changed_flags & IFF_PROMISC) || promisc_forced_on) ||
+ test_bit(ICE_VSI_FLAG_PROMISC_CHANGED, vsi->flags)) {
+ clear_bit(ICE_VSI_FLAG_PROMISC_CHANGED, vsi->flags);
+ if (vsi->current_netdev_flags & IFF_PROMISC) {
+ /* Apply TX filter rule to get traffic from VMs */
+ status = ice_cfg_dflt_vsi(hw, vsi->vsi_num, true,
+ ICE_FLTR_TX);
+ if (status) {
+ netdev_err(netdev, "Error setting default VSI %i tx rule\n",
+ vsi->vsi_num);
+ vsi->current_netdev_flags &= ~IFF_PROMISC;
+ err = -EIO;
+ goto out_promisc;
+ }
+ /* Apply RX filter rule to get traffic from wire */
+ status = ice_cfg_dflt_vsi(hw, vsi->vsi_num, true,
+ ICE_FLTR_RX);
+ if (status) {
+ netdev_err(netdev, "Error setting default VSI %i rx rule\n",
+ vsi->vsi_num);
+ vsi->current_netdev_flags &= ~IFF_PROMISC;
+ err = -EIO;
+ goto out_promisc;
+ }
+ } else {
+ /* Clear TX filter rule to stop traffic from VMs */
+ status = ice_cfg_dflt_vsi(hw, vsi->vsi_num, false,
+ ICE_FLTR_TX);
+ if (status) {
+ netdev_err(netdev, "Error clearing default VSI %i tx rule\n",
+ vsi->vsi_num);
+ vsi->current_netdev_flags |= IFF_PROMISC;
+ err = -EIO;
+ goto out_promisc;
+ }
+ /* Clear filter RX to remove traffic from wire */
+ status = ice_cfg_dflt_vsi(hw, vsi->vsi_num, false,
+ ICE_FLTR_RX);
+ if (status) {
+ netdev_err(netdev, "Error clearing default VSI %i rx rule\n",
+ vsi->vsi_num);
+ vsi->current_netdev_flags |= IFF_PROMISC;
+ err = -EIO;
+ goto out_promisc;
+ }
+ }
+ }
+ goto exit;
+
+out_promisc:
+ set_bit(ICE_VSI_FLAG_PROMISC_CHANGED, vsi->flags);
+ goto exit;
+out:
+ /* if something went wrong then set the changed flag so we try again */
+ set_bit(ICE_VSI_FLAG_UMAC_FLTR_CHANGED, vsi->flags);
+ set_bit(ICE_VSI_FLAG_MMAC_FLTR_CHANGED, vsi->flags);
+exit:
+ clear_bit(__ICE_CFG_BUSY, vsi->state);
+ return err;
+}
+
+/**
+ * ice_sync_fltr_subtask - Sync the VSI filter list with HW
+ * @pf: board private structure
+ */
+static void ice_sync_fltr_subtask(struct ice_pf *pf)
+{
+ int v;
+
+ if (!pf || !(test_bit(ICE_FLAG_FLTR_SYNC, pf->flags)))
+ return;
+
+ clear_bit(ICE_FLAG_FLTR_SYNC, pf->flags);
+
+ for (v = 0; v < pf->num_alloc_vsi; v++)
+ if (pf->vsi[v] && ice_vsi_fltr_changed(pf->vsi[v]) &&
+ ice_vsi_sync_fltr(pf->vsi[v])) {
+ /* come back and try again later */
+ set_bit(ICE_FLAG_FLTR_SYNC, pf->flags);
+ break;
+ }
+}
+
+/**
+ * ice_is_reset_recovery_pending - schedule a reset
+ * @state: pf state field
+ */
+static bool ice_is_reset_recovery_pending(unsigned long int *state)
+{
+ return test_bit(__ICE_RESET_RECOVERY_PENDING, state);
+}
+
+/**
+ * ice_prepare_for_reset - prep for the core to reset
+ * @pf: board private structure
+ *
+ * Inform or close all dependent features in prep for reset.
+ */
+static void
+ice_prepare_for_reset(struct ice_pf *pf)
+{
+ struct ice_hw *hw = &pf->hw;
+ u32 v;
+
+ ice_for_each_vsi(pf, v)
+ if (pf->vsi[v])
+ ice_remove_vsi_fltr(hw, pf->vsi[v]->vsi_num);
+
+ dev_dbg(&pf->pdev->dev, "Tearing down internal switch for reset\n");
+
+ /* disable the VSIs and their queues that are not already DOWN */
+ /* pf_dis_all_vsi modifies netdev structures -rtnl_lock needed */
+ ice_pf_dis_all_vsi(pf);
+
+ ice_for_each_vsi(pf, v)
+ if (pf->vsi[v])
+ pf->vsi[v]->vsi_num = 0;
+
+ ice_shutdown_all_ctrlq(hw);
+}
+
+/**
+ * ice_do_reset - Initiate one of many types of resets
+ * @pf: board private structure
+ * @reset_type: reset type requested
+ * before this function was called.
+ */
+static void ice_do_reset(struct ice_pf *pf, enum ice_reset_req reset_type)
+{
+ struct device *dev = &pf->pdev->dev;
+ struct ice_hw *hw = &pf->hw;
+
+ dev_dbg(dev, "reset_type 0x%x requested\n", reset_type);
+ WARN_ON(in_interrupt());
+
+ /* PFR is a bit of a special case because it doesn't result in an OICR
+ * interrupt. So for PFR, we prepare for reset, issue the reset and
+ * rebuild sequentially.
+ */
+ if (reset_type == ICE_RESET_PFR) {
+ set_bit(__ICE_RESET_RECOVERY_PENDING, pf->state);
+ ice_prepare_for_reset(pf);
+ }
+
+ /* trigger the reset */
+ if (ice_reset(hw, reset_type)) {
+ dev_err(dev, "reset %d failed\n", reset_type);
+ set_bit(__ICE_RESET_FAILED, pf->state);
+ clear_bit(__ICE_RESET_RECOVERY_PENDING, pf->state);
+ return;
+ }
+
+ if (reset_type == ICE_RESET_PFR) {
+ pf->pfr_count++;
+ ice_rebuild(pf);
+ clear_bit(__ICE_RESET_RECOVERY_PENDING, pf->state);
+ }
+}
+
+/**
+ * ice_reset_subtask - Set up for resetting the device and driver
+ * @pf: board private structure
+ */
+static void ice_reset_subtask(struct ice_pf *pf)
+{
+ enum ice_reset_req reset_type;
+
+ rtnl_lock();
+
+ /* When a CORER/GLOBR/EMPR is about to happen, the hardware triggers an
+ * OICR interrupt. The OICR handler (ice_misc_intr) determines what
+ * type of reset happened and sets __ICE_RESET_RECOVERY_PENDING bit in
+ * pf->state. So if reset/recovery is pending (as indicated by this bit)
+ * we do a rebuild and return.
+ */
+ if (ice_is_reset_recovery_pending(pf->state)) {
+ clear_bit(__ICE_GLOBR_RECV, pf->state);
+ clear_bit(__ICE_CORER_RECV, pf->state);
+ ice_prepare_for_reset(pf);
+
+ /* make sure we are ready to rebuild */
+ if (ice_check_reset(&pf->hw))
+ set_bit(__ICE_RESET_FAILED, pf->state);
+ else
+ ice_rebuild(pf);
+ clear_bit(__ICE_RESET_RECOVERY_PENDING, pf->state);
+ goto unlock;
+ }
+
+ /* No pending resets to finish processing. Check for new resets */
+ if (test_and_clear_bit(__ICE_GLOBR_REQ, pf->state))
+ reset_type = ICE_RESET_GLOBR;
+ else if (test_and_clear_bit(__ICE_CORER_REQ, pf->state))
+ reset_type = ICE_RESET_CORER;
+ else if (test_and_clear_bit(__ICE_PFR_REQ, pf->state))
+ reset_type = ICE_RESET_PFR;
+ else
+ goto unlock;
+
+ /* reset if not already down or resetting */
+ if (!test_bit(__ICE_DOWN, pf->state) &&
+ !test_bit(__ICE_CFG_BUSY, pf->state)) {
+ ice_do_reset(pf, reset_type);
+ }
+
+unlock:
+ rtnl_unlock();
+}
+
+/**
+ * ice_watchdog_subtask - periodic tasks not using event driven scheduling
+ * @pf: board private structure
+ */
+static void ice_watchdog_subtask(struct ice_pf *pf)
+{
+ int i;
+
+ /* if interface is down do nothing */
+ if (test_bit(__ICE_DOWN, pf->state) ||
+ test_bit(__ICE_CFG_BUSY, pf->state))
+ return;
+
+ /* make sure we don't do these things too often */
+ if (time_before(jiffies,
+ pf->serv_tmr_prev + pf->serv_tmr_period))
+ return;
+
+ pf->serv_tmr_prev = jiffies;
+
+ /* Update the stats for active netdevs so the network stack
+ * can look at updated numbers whenever it cares to
+ */
+ ice_update_pf_stats(pf);
+ for (i = 0; i < pf->num_alloc_vsi; i++)
+ if (pf->vsi[i] && pf->vsi[i]->netdev)
+ ice_update_vsi_stats(pf->vsi[i]);
+}
+
+/**
+ * ice_print_link_msg - print link up or down message
+ * @vsi: the VSI whose link status is being queried
+ * @isup: boolean for if the link is now up or down
+ */
+void ice_print_link_msg(struct ice_vsi *vsi, bool isup)
+{
+ const char *speed;
+ const char *fc;
+
+ if (vsi->current_isup == isup)
+ return;
+
+ vsi->current_isup = isup;
+
+ if (!isup) {
+ netdev_info(vsi->netdev, "NIC Link is Down\n");
+ return;
+ }
+
+ switch (vsi->port_info->phy.link_info.link_speed) {
+ case ICE_AQ_LINK_SPEED_40GB:
+ speed = "40 G";
+ break;
+ case ICE_AQ_LINK_SPEED_25GB:
+ speed = "25 G";
+ break;
+ case ICE_AQ_LINK_SPEED_20GB:
+ speed = "20 G";
+ break;
+ case ICE_AQ_LINK_SPEED_10GB:
+ speed = "10 G";
+ break;
+ case ICE_AQ_LINK_SPEED_5GB:
+ speed = "5 G";
+ break;
+ case ICE_AQ_LINK_SPEED_2500MB:
+ speed = "2.5 G";
+ break;
+ case ICE_AQ_LINK_SPEED_1000MB:
+ speed = "1 G";
+ break;
+ case ICE_AQ_LINK_SPEED_100MB:
+ speed = "100 M";
+ break;
+ default:
+ speed = "Unknown";
+ break;
+ }
+
+ switch (vsi->port_info->fc.current_mode) {
+ case ICE_FC_FULL:
+ fc = "RX/TX";
+ break;
+ case ICE_FC_TX_PAUSE:
+ fc = "TX";
+ break;
+ case ICE_FC_RX_PAUSE:
+ fc = "RX";
+ break;
+ default:
+ fc = "Unknown";
+ break;
+ }
+
+ netdev_info(vsi->netdev, "NIC Link is up %sbps, Flow Control: %s\n",
+ speed, fc);
+}
+
+/**
+ * ice_init_link_events - enable/initialize link events
+ * @pi: pointer to the port_info instance
+ *
+ * Returns -EIO on failure, 0 on success
+ */
+static int ice_init_link_events(struct ice_port_info *pi)
+{
+ u16 mask;
+
+ mask = ~((u16)(ICE_AQ_LINK_EVENT_UPDOWN | ICE_AQ_LINK_EVENT_MEDIA_NA |
+ ICE_AQ_LINK_EVENT_MODULE_QUAL_FAIL));
+
+ if (ice_aq_set_event_mask(pi->hw, pi->lport, mask, NULL)) {
+ dev_dbg(ice_hw_to_dev(pi->hw),
+ "Failed to set link event mask for port %d\n",
+ pi->lport);
+ return -EIO;
+ }
+
+ if (ice_aq_get_link_info(pi, true, NULL, NULL)) {
+ dev_dbg(ice_hw_to_dev(pi->hw),
+ "Failed to enable link events for port %d\n",
+ pi->lport);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * ice_vsi_link_event - update the vsi's netdev
+ * @vsi: the vsi on which the link event occurred
+ * @link_up: whether or not the vsi needs to be set up or down
+ */
+static void ice_vsi_link_event(struct ice_vsi *vsi, bool link_up)
+{
+ if (!vsi || test_bit(__ICE_DOWN, vsi->state))
+ return;
+
+ if (vsi->type == ICE_VSI_PF) {
+ if (!vsi->netdev) {
+ dev_dbg(&vsi->back->pdev->dev,
+ "vsi->netdev is not initialized!\n");
+ return;
+ }
+ if (link_up) {
+ netif_carrier_on(vsi->netdev);
+ netif_tx_wake_all_queues(vsi->netdev);
+ } else {
+ netif_carrier_off(vsi->netdev);
+ netif_tx_stop_all_queues(vsi->netdev);
+ }
+ }
+}
+
+/**
+ * ice_link_event - process the link event
+ * @pf: pf that the link event is associated with
+ * @pi: port_info for the port that the link event is associated with
+ *
+ * Returns -EIO if ice_get_link_status() fails
+ * Returns 0 on success
+ */
+static int
+ice_link_event(struct ice_pf *pf, struct ice_port_info *pi)
+{
+ u8 new_link_speed, old_link_speed;
+ struct ice_phy_info *phy_info;
+ bool new_link_same_as_old;
+ bool new_link, old_link;
+ u8 lport;
+ u16 v;
+
+ phy_info = &pi->phy;
+ phy_info->link_info_old = phy_info->link_info;
+ /* Force ice_get_link_status() to update link info */
+ phy_info->get_link_info = true;
+
+ old_link = (phy_info->link_info_old.link_info & ICE_AQ_LINK_UP);
+ old_link_speed = phy_info->link_info_old.link_speed;
+
+ lport = pi->lport;
+ if (ice_get_link_status(pi, &new_link)) {
+ dev_dbg(&pf->pdev->dev,
+ "Could not get link status for port %d\n", lport);
+ return -EIO;
+ }
+
+ new_link_speed = phy_info->link_info.link_speed;
+
+ new_link_same_as_old = (new_link == old_link &&
+ new_link_speed == old_link_speed);
+
+ ice_for_each_vsi(pf, v) {
+ struct ice_vsi *vsi = pf->vsi[v];
+
+ if (!vsi || !vsi->port_info)
+ continue;
+
+ if (new_link_same_as_old &&
+ (test_bit(__ICE_DOWN, vsi->state) ||
+ new_link == netif_carrier_ok(vsi->netdev)))
+ continue;
+
+ if (vsi->port_info->lport == lport) {
+ ice_print_link_msg(vsi, new_link);
+ ice_vsi_link_event(vsi, new_link);
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * ice_handle_link_event - handle link event via ARQ
+ * @pf: pf that the link event is associated with
+ *
+ * Return -EINVAL if port_info is null
+ * Return status on succes
+ */
+static int ice_handle_link_event(struct ice_pf *pf)
+{
+ struct ice_port_info *port_info;
+ int status;
+
+ port_info = pf->hw.port_info;
+ if (!port_info)
+ return -EINVAL;
+
+ status = ice_link_event(pf, port_info);
+ if (status)
+ dev_dbg(&pf->pdev->dev,
+ "Could not process link event, error %d\n", status);
+
+ return status;
+}
+
+/**
+ * __ice_clean_ctrlq - helper function to clean controlq rings
+ * @pf: ptr to struct ice_pf
+ * @q_type: specific Control queue type
+ */
+static int __ice_clean_ctrlq(struct ice_pf *pf, enum ice_ctl_q q_type)
+{
+ struct ice_rq_event_info event;
+ struct ice_hw *hw = &pf->hw;
+ struct ice_ctl_q_info *cq;
+ u16 pending, i = 0;
+ const char *qtype;
+ u32 oldval, val;
+
+ /* Do not clean control queue if/when PF reset fails */
+ if (test_bit(__ICE_RESET_FAILED, pf->state))
+ return 0;
+
+ switch (q_type) {
+ case ICE_CTL_Q_ADMIN:
+ cq = &hw->adminq;
+ qtype = "Admin";
+ break;
+ default:
+ dev_warn(&pf->pdev->dev, "Unknown control queue type 0x%x\n",
+ q_type);
+ return 0;
+ }
+
+ /* check for error indications - PF_xx_AxQLEN register layout for
+ * FW/MBX/SB are identical so just use defines for PF_FW_AxQLEN.
+ */
+ val = rd32(hw, cq->rq.len);
+ if (val & (PF_FW_ARQLEN_ARQVFE_M | PF_FW_ARQLEN_ARQOVFL_M |
+ PF_FW_ARQLEN_ARQCRIT_M)) {
+ oldval = val;
+ if (val & PF_FW_ARQLEN_ARQVFE_M)
+ dev_dbg(&pf->pdev->dev,
+ "%s Receive Queue VF Error detected\n", qtype);
+ if (val & PF_FW_ARQLEN_ARQOVFL_M) {
+ dev_dbg(&pf->pdev->dev,
+ "%s Receive Queue Overflow Error detected\n",
+ qtype);
+ }
+ if (val & PF_FW_ARQLEN_ARQCRIT_M)
+ dev_dbg(&pf->pdev->dev,
+ "%s Receive Queue Critical Error detected\n",
+ qtype);
+ val &= ~(PF_FW_ARQLEN_ARQVFE_M | PF_FW_ARQLEN_ARQOVFL_M |
+ PF_FW_ARQLEN_ARQCRIT_M);
+ if (oldval != val)
+ wr32(hw, cq->rq.len, val);
+ }
+
+ val = rd32(hw, cq->sq.len);
+ if (val & (PF_FW_ATQLEN_ATQVFE_M | PF_FW_ATQLEN_ATQOVFL_M |
+ PF_FW_ATQLEN_ATQCRIT_M)) {
+ oldval = val;
+ if (val & PF_FW_ATQLEN_ATQVFE_M)
+ dev_dbg(&pf->pdev->dev,
+ "%s Send Queue VF Error detected\n", qtype);
+ if (val & PF_FW_ATQLEN_ATQOVFL_M) {
+ dev_dbg(&pf->pdev->dev,
+ "%s Send Queue Overflow Error detected\n",
+ qtype);
+ }
+ if (val & PF_FW_ATQLEN_ATQCRIT_M)
+ dev_dbg(&pf->pdev->dev,
+ "%s Send Queue Critical Error detected\n",
+ qtype);
+ val &= ~(PF_FW_ATQLEN_ATQVFE_M | PF_FW_ATQLEN_ATQOVFL_M |
+ PF_FW_ATQLEN_ATQCRIT_M);
+ if (oldval != val)
+ wr32(hw, cq->sq.len, val);
+ }
+
+ event.buf_len = cq->rq_buf_size;
+ event.msg_buf = devm_kzalloc(&pf->pdev->dev, event.buf_len,
+ GFP_KERNEL);
+ if (!event.msg_buf)
+ return 0;
+
+ do {
+ enum ice_status ret;
+ u16 opcode;
+
+ ret = ice_clean_rq_elem(hw, cq, &event, &pending);
+ if (ret == ICE_ERR_AQ_NO_WORK)
+ break;
+ if (ret) {
+ dev_err(&pf->pdev->dev,
+ "%s Receive Queue event error %d\n", qtype,
+ ret);
+ break;
+ }
+
+ opcode = le16_to_cpu(event.desc.opcode);
+
+ switch (opcode) {
+ case ice_aqc_opc_get_link_status:
+ if (ice_handle_link_event(pf))
+ dev_err(&pf->pdev->dev,
+ "Could not handle link event");
+ break;
+ default:
+ dev_dbg(&pf->pdev->dev,
+ "%s Receive Queue unknown event 0x%04x ignored\n",
+ qtype, opcode);
+ break;
+ }
+ } while (pending && (i++ < ICE_DFLT_IRQ_WORK));
+
+ devm_kfree(&pf->pdev->dev, event.msg_buf);
+
+ return pending && (i == ICE_DFLT_IRQ_WORK);
+}
+
+/**
+ * ice_clean_adminq_subtask - clean the AdminQ rings
+ * @pf: board private structure
+ */
+static void ice_clean_adminq_subtask(struct ice_pf *pf)
+{
+ struct ice_hw *hw = &pf->hw;
+ u32 val;
+
+ if (!test_bit(__ICE_ADMINQ_EVENT_PENDING, pf->state))
+ return;
+
+ if (__ice_clean_ctrlq(pf, ICE_CTL_Q_ADMIN))
+ return;
+
+ clear_bit(__ICE_ADMINQ_EVENT_PENDING, pf->state);
+
+ /* re-enable Admin queue interrupt causes */
+ val = rd32(hw, PFINT_FW_CTL);
+ wr32(hw, PFINT_FW_CTL, (val | PFINT_FW_CTL_CAUSE_ENA_M));
+
+ ice_flush(hw);
+}
+
+/**
+ * ice_service_task_schedule - schedule the service task to wake up
+ * @pf: board private structure
+ *
+ * If not already scheduled, this puts the task into the work queue.
+ */
+static void ice_service_task_schedule(struct ice_pf *pf)
+{
+ if (!test_bit(__ICE_DOWN, pf->state) &&
+ !test_and_set_bit(__ICE_SERVICE_SCHED, pf->state))
+ queue_work(ice_wq, &pf->serv_task);
+}
+
+/**
+ * ice_service_task_complete - finish up the service task
+ * @pf: board private structure
+ */
+static void ice_service_task_complete(struct ice_pf *pf)
+{
+ WARN_ON(!test_bit(__ICE_SERVICE_SCHED, pf->state));
+
+ /* force memory (pf->state) to sync before next service task */
+ smp_mb__before_atomic();
+ clear_bit(__ICE_SERVICE_SCHED, pf->state);
+}
+
+/**
+ * ice_service_timer - timer callback to schedule service task
+ * @t: pointer to timer_list
+ */
+static void ice_service_timer(struct timer_list *t)
+{
+ struct ice_pf *pf = from_timer(pf, t, serv_tmr);
+
+ mod_timer(&pf->serv_tmr, round_jiffies(pf->serv_tmr_period + jiffies));
+ ice_service_task_schedule(pf);
+}
+
+/**
+ * ice_service_task - manage and run subtasks
+ * @work: pointer to work_struct contained by the PF struct
+ */
+static void ice_service_task(struct work_struct *work)
+{
+ struct ice_pf *pf = container_of(work, struct ice_pf, serv_task);
+ unsigned long start_time = jiffies;
+
+ /* subtasks */
+
+ /* process reset requests first */
+ ice_reset_subtask(pf);
+
+ /* bail if a reset/recovery cycle is pending */
+ if (ice_is_reset_recovery_pending(pf->state) ||
+ test_bit(__ICE_SUSPENDED, pf->state)) {
+ ice_service_task_complete(pf);
+ return;
+ }
+
+ ice_sync_fltr_subtask(pf);
+ ice_watchdog_subtask(pf);
+ ice_clean_adminq_subtask(pf);
+
+ /* Clear __ICE_SERVICE_SCHED flag to allow scheduling next event */
+ ice_service_task_complete(pf);
+
+ /* If the tasks have taken longer than one service timer period
+ * or there is more work to be done, reset the service timer to
+ * schedule the service task now.
+ */
+ if (time_after(jiffies, (start_time + pf->serv_tmr_period)) ||
+ test_bit(__ICE_ADMINQ_EVENT_PENDING, pf->state))
+ mod_timer(&pf->serv_tmr, jiffies);
+}
+
+/**
+ * ice_set_ctrlq_len - helper function to set controlq length
+ * @hw: pointer to the hw instance
+ */
+static void ice_set_ctrlq_len(struct ice_hw *hw)
+{
+ hw->adminq.num_rq_entries = ICE_AQ_LEN;
+ hw->adminq.num_sq_entries = ICE_AQ_LEN;
+ hw->adminq.rq_buf_size = ICE_AQ_MAX_BUF_LEN;
+ hw->adminq.sq_buf_size = ICE_AQ_MAX_BUF_LEN;
+}
+
+/**
+ * ice_irq_affinity_notify - Callback for affinity changes
+ * @notify: context as to what irq was changed
+ * @mask: the new affinity mask
+ *
+ * This is a callback function used by the irq_set_affinity_notifier function
+ * so that we may register to receive changes to the irq affinity masks.
+ */
+static void ice_irq_affinity_notify(struct irq_affinity_notify *notify,
+ const cpumask_t *mask)
+{
+ struct ice_q_vector *q_vector =
+ container_of(notify, struct ice_q_vector, affinity_notify);
+
+ cpumask_copy(&q_vector->affinity_mask, mask);
+}
+
+/**
+ * ice_irq_affinity_release - Callback for affinity notifier release
+ * @ref: internal core kernel usage
+ *
+ * This is a callback function used by the irq_set_affinity_notifier function
+ * to inform the current notification subscriber that they will no longer
+ * receive notifications.
+ */
+static void ice_irq_affinity_release(struct kref __always_unused *ref) {}
+
+/**
+ * ice_vsi_dis_irq - Mask off queue interrupt generation on the VSI
+ * @vsi: the VSI being un-configured
+ */
+static void ice_vsi_dis_irq(struct ice_vsi *vsi)
+{
+ struct ice_pf *pf = vsi->back;
+ struct ice_hw *hw = &pf->hw;
+ int base = vsi->base_vector;
+ u32 val;
+ int i;
+
+ /* disable interrupt causation from each queue */
+ if (vsi->tx_rings) {
+ ice_for_each_txq(vsi, i) {
+ if (vsi->tx_rings[i]) {
+ u16 reg;
+
+ reg = vsi->tx_rings[i]->reg_idx;
+ val = rd32(hw, QINT_TQCTL(reg));
+ val &= ~QINT_TQCTL_CAUSE_ENA_M;
+ wr32(hw, QINT_TQCTL(reg), val);
+ }
+ }
+ }
+
+ if (vsi->rx_rings) {
+ ice_for_each_rxq(vsi, i) {
+ if (vsi->rx_rings[i]) {
+ u16 reg;
+
+ reg = vsi->rx_rings[i]->reg_idx;
+ val = rd32(hw, QINT_RQCTL(reg));
+ val &= ~QINT_RQCTL_CAUSE_ENA_M;
+ wr32(hw, QINT_RQCTL(reg), val);
+ }
+ }
+ }
+
+ /* disable each interrupt */
+ if (test_bit(ICE_FLAG_MSIX_ENA, pf->flags)) {
+ for (i = vsi->base_vector;
+ i < (vsi->num_q_vectors + vsi->base_vector); i++)
+ wr32(hw, GLINT_DYN_CTL(i), 0);
+
+ ice_flush(hw);
+ for (i = 0; i < vsi->num_q_vectors; i++)
+ synchronize_irq(pf->msix_entries[i + base].vector);
+ }
+}
+
+/**
+ * ice_vsi_ena_irq - Enable IRQ for the given VSI
+ * @vsi: the VSI being configured
+ */
+static int ice_vsi_ena_irq(struct ice_vsi *vsi)
+{
+ struct ice_pf *pf = vsi->back;
+ struct ice_hw *hw = &pf->hw;
+
+ if (test_bit(ICE_FLAG_MSIX_ENA, pf->flags)) {
+ int i;
+
+ for (i = 0; i < vsi->num_q_vectors; i++)
+ ice_irq_dynamic_ena(hw, vsi, vsi->q_vectors[i]);
+ }
+
+ ice_flush(hw);
+ return 0;
+}
+
+/**
+ * ice_vsi_delete - delete a VSI from the switch
+ * @vsi: pointer to VSI being removed
+ */
+static void ice_vsi_delete(struct ice_vsi *vsi)
+{
+ struct ice_pf *pf = vsi->back;
+ struct ice_vsi_ctx ctxt;
+ enum ice_status status;
+
+ ctxt.vsi_num = vsi->vsi_num;
+
+ memcpy(&ctxt.info, &vsi->info, sizeof(struct ice_aqc_vsi_props));
+
+ status = ice_aq_free_vsi(&pf->hw, &ctxt, false, NULL);
+ if (status)
+ dev_err(&pf->pdev->dev, "Failed to delete VSI %i in FW\n",
+ vsi->vsi_num);
+}
+
+/**
+ * ice_vsi_req_irq_msix - get MSI-X vectors from the OS for the VSI
+ * @vsi: the VSI being configured
+ * @basename: name for the vector
+ */
+static int ice_vsi_req_irq_msix(struct ice_vsi *vsi, char *basename)
+{
+ int q_vectors = vsi->num_q_vectors;
+ struct ice_pf *pf = vsi->back;
+ int base = vsi->base_vector;
+ int rx_int_idx = 0;
+ int tx_int_idx = 0;
+ int vector, err;
+ int irq_num;
+
+ for (vector = 0; vector < q_vectors; vector++) {
+ struct ice_q_vector *q_vector = vsi->q_vectors[vector];
+
+ irq_num = pf->msix_entries[base + vector].vector;
+
+ if (q_vector->tx.ring && q_vector->rx.ring) {
+ snprintf(q_vector->name, sizeof(q_vector->name) - 1,
+ "%s-%s-%d", basename, "TxRx", rx_int_idx++);
+ tx_int_idx++;
+ } else if (q_vector->rx.ring) {
+ snprintf(q_vector->name, sizeof(q_vector->name) - 1,
+ "%s-%s-%d", basename, "rx", rx_int_idx++);
+ } else if (q_vector->tx.ring) {
+ snprintf(q_vector->name, sizeof(q_vector->name) - 1,
+ "%s-%s-%d", basename, "tx", tx_int_idx++);
+ } else {
+ /* skip this unused q_vector */
+ continue;
+ }
+ err = devm_request_irq(&pf->pdev->dev,
+ pf->msix_entries[base + vector].vector,
+ vsi->irq_handler, 0, q_vector->name,
+ q_vector);
+ if (err) {
+ netdev_err(vsi->netdev,
+ "MSIX request_irq failed, error: %d\n", err);
+ goto free_q_irqs;
+ }
+
+ /* register for affinity change notifications */
+ q_vector->affinity_notify.notify = ice_irq_affinity_notify;
+ q_vector->affinity_notify.release = ice_irq_affinity_release;
+ irq_set_affinity_notifier(irq_num, &q_vector->affinity_notify);
+
+ /* assign the mask for this irq */
+ irq_set_affinity_hint(irq_num, &q_vector->affinity_mask);
+ }
+
+ vsi->irqs_ready = true;
+ return 0;
+
+free_q_irqs:
+ while (vector) {
+ vector--;
+ irq_num = pf->msix_entries[base + vector].vector,
+ irq_set_affinity_notifier(irq_num, NULL);
+ irq_set_affinity_hint(irq_num, NULL);
+ devm_free_irq(&pf->pdev->dev, irq_num, &vsi->q_vectors[vector]);
+ }
+ return err;
+}
+
+/**
+ * ice_vsi_set_rss_params - Setup RSS capabilities per VSI type
+ * @vsi: the VSI being configured
+ */
+static void ice_vsi_set_rss_params(struct ice_vsi *vsi)
+{
+ struct ice_hw_common_caps *cap;
+ struct ice_pf *pf = vsi->back;
+
+ if (!test_bit(ICE_FLAG_RSS_ENA, pf->flags)) {
+ vsi->rss_size = 1;
+ return;
+ }
+
+ cap = &pf->hw.func_caps.common_cap;
+ switch (vsi->type) {
+ case ICE_VSI_PF:
+ /* PF VSI will inherit RSS instance of PF */
+ vsi->rss_table_size = cap->rss_table_size;
+ vsi->rss_size = min_t(int, num_online_cpus(),
+ BIT(cap->rss_table_entry_width));
+ vsi->rss_lut_type = ICE_AQC_GSET_RSS_LUT_TABLE_TYPE_PF;
+ break;
+ default:
+ dev_warn(&pf->pdev->dev, "Unknown VSI type %d\n", vsi->type);
+ break;
+ }
+}
+
+/**
+ * ice_vsi_setup_q_map - Setup a VSI queue map
+ * @vsi: the VSI being configured
+ * @ctxt: VSI context structure
+ */
+static void ice_vsi_setup_q_map(struct ice_vsi *vsi, struct ice_vsi_ctx *ctxt)
+{
+ u16 offset = 0, qmap = 0, numq_tc;
+ u16 pow = 0, max_rss = 0, qcount;
+ u16 qcount_tx = vsi->alloc_txq;
+ u16 qcount_rx = vsi->alloc_rxq;
+ bool ena_tc0 = false;
+ int i;
+
+ /* at least TC0 should be enabled by default */
+ if (vsi->tc_cfg.numtc) {
+ if (!(vsi->tc_cfg.ena_tc & BIT(0)))
+ ena_tc0 = true;
+ } else {
+ ena_tc0 = true;
+ }
+
+ if (ena_tc0) {
+ vsi->tc_cfg.numtc++;
+ vsi->tc_cfg.ena_tc |= 1;
+ }
+
+ numq_tc = qcount_rx / vsi->tc_cfg.numtc;
+
+ /* TC mapping is a function of the number of Rx queues assigned to the
+ * VSI for each traffic class and the offset of these queues.
+ * The first 10 bits are for queue offset for TC0, next 4 bits for no:of
+ * queues allocated to TC0. No:of queues is a power-of-2.
+ *
+ * If TC is not enabled, the queue offset is set to 0, and allocate one
+ * queue, this way, traffic for the given TC will be sent to the default
+ * queue.
+ *
+ * Setup number and offset of Rx queues for all TCs for the VSI
+ */
+
+ /* qcount will change if RSS is enabled */
+ if (test_bit(ICE_FLAG_RSS_ENA, vsi->back->flags)) {
+ if (vsi->type == ICE_VSI_PF)
+ max_rss = ICE_MAX_LG_RSS_QS;
+ else
+ max_rss = ICE_MAX_SMALL_RSS_QS;
+
+ qcount = min_t(int, numq_tc, max_rss);
+ qcount = min_t(int, qcount, vsi->rss_size);
+ } else {
+ qcount = numq_tc;
+ }
+
+ /* find higher power-of-2 of qcount */
+ pow = ilog2(qcount);
+
+ if (!is_power_of_2(qcount))
+ pow++;
+
+ for (i = 0; i < ICE_MAX_TRAFFIC_CLASS; i++) {
+ if (!(vsi->tc_cfg.ena_tc & BIT(i))) {
+ /* TC is not enabled */
+ vsi->tc_cfg.tc_info[i].qoffset = 0;
+ vsi->tc_cfg.tc_info[i].qcount = 1;
+ ctxt->info.tc_mapping[i] = 0;
+ continue;
+ }
+
+ /* TC is enabled */
+ vsi->tc_cfg.tc_info[i].qoffset = offset;
+ vsi->tc_cfg.tc_info[i].qcount = qcount;
+
+ qmap = ((offset << ICE_AQ_VSI_TC_Q_OFFSET_S) &
+ ICE_AQ_VSI_TC_Q_OFFSET_M) |
+ ((pow << ICE_AQ_VSI_TC_Q_NUM_S) &
+ ICE_AQ_VSI_TC_Q_NUM_M);
+ offset += qcount;
+ ctxt->info.tc_mapping[i] = cpu_to_le16(qmap);
+ }
+
+ vsi->num_txq = qcount_tx;
+ vsi->num_rxq = offset;
+
+ /* Rx queue mapping */
+ ctxt->info.mapping_flags |= cpu_to_le16(ICE_AQ_VSI_Q_MAP_CONTIG);
+ /* q_mapping buffer holds the info for the first queue allocated for
+ * this VSI in the PF space and also the number of queues associated
+ * with this VSI.
+ */
+ ctxt->info.q_mapping[0] = cpu_to_le16(vsi->rxq_map[0]);
+ ctxt->info.q_mapping[1] = cpu_to_le16(vsi->num_rxq);
+}
+
+/**
+ * ice_set_dflt_vsi_ctx - Set default VSI context before adding a VSI
+ * @ctxt: the VSI context being set
+ *
+ * This initializes a default VSI context for all sections except the Queues.
+ */
+static void ice_set_dflt_vsi_ctx(struct ice_vsi_ctx *ctxt)
+{
+ u32 table = 0;
+
+ memset(&ctxt->info, 0, sizeof(ctxt->info));
+ /* VSI's should be allocated from shared pool */
+ ctxt->alloc_from_pool = true;
+ /* Src pruning enabled by default */
+ ctxt->info.sw_flags = ICE_AQ_VSI_SW_FLAG_SRC_PRUNE;
+ /* Traffic from VSI can be sent to LAN */
+ ctxt->info.sw_flags2 = ICE_AQ_VSI_SW_FLAG_LAN_ENA;
+ /* Allow all packets untagged/tagged */
+ ctxt->info.port_vlan_flags = ((ICE_AQ_VSI_PVLAN_MODE_ALL &
+ ICE_AQ_VSI_PVLAN_MODE_M) >>
+ ICE_AQ_VSI_PVLAN_MODE_S);
+ /* Show VLAN/UP from packets in Rx descriptors */
+ ctxt->info.port_vlan_flags |= ((ICE_AQ_VSI_PVLAN_EMOD_STR_BOTH &
+ ICE_AQ_VSI_PVLAN_EMOD_M) >>
+ ICE_AQ_VSI_PVLAN_EMOD_S);
+ /* Have 1:1 UP mapping for both ingress/egress tables */
+ table |= ICE_UP_TABLE_TRANSLATE(0, 0);
+ table |= ICE_UP_TABLE_TRANSLATE(1, 1);
+ table |= ICE_UP_TABLE_TRANSLATE(2, 2);
+ table |= ICE_UP_TABLE_TRANSLATE(3, 3);
+ table |= ICE_UP_TABLE_TRANSLATE(4, 4);
+ table |= ICE_UP_TABLE_TRANSLATE(5, 5);
+ table |= ICE_UP_TABLE_TRANSLATE(6, 6);
+ table |= ICE_UP_TABLE_TRANSLATE(7, 7);
+ ctxt->info.ingress_table = cpu_to_le32(table);
+ ctxt->info.egress_table = cpu_to_le32(table);
+ /* Have 1:1 UP mapping for outer to inner UP table */
+ ctxt->info.outer_up_table = cpu_to_le32(table);
+ /* No Outer tag support outer_tag_flags remains to zero */
+}
+
+/**
+ * ice_set_rss_vsi_ctx - Set RSS VSI context before adding a VSI
+ * @ctxt: the VSI context being set
+ * @vsi: the VSI being configured
+ */
+static void ice_set_rss_vsi_ctx(struct ice_vsi_ctx *ctxt, struct ice_vsi *vsi)
+{
+ u8 lut_type, hash_type;
+
+ switch (vsi->type) {
+ case ICE_VSI_PF:
+ /* PF VSI will inherit RSS instance of PF */
+ lut_type = ICE_AQ_VSI_Q_OPT_RSS_LUT_PF;
+ hash_type = ICE_AQ_VSI_Q_OPT_RSS_TPLZ;
+ break;
+ default:
+ dev_warn(&vsi->back->pdev->dev, "Unknown VSI type %d\n",
+ vsi->type);
+ return;
+ }
+
+ ctxt->info.q_opt_rss = ((lut_type << ICE_AQ_VSI_Q_OPT_RSS_LUT_S) &
+ ICE_AQ_VSI_Q_OPT_RSS_LUT_M) |
+ ((hash_type << ICE_AQ_VSI_Q_OPT_RSS_HASH_S) &
+ ICE_AQ_VSI_Q_OPT_RSS_HASH_M);
+}
+
+/**
+ * ice_vsi_add - Create a new VSI or fetch preallocated VSI
+ * @vsi: the VSI being configured
+ *
+ * This initializes a VSI context depending on the VSI type to be added and
+ * passes it down to the add_vsi aq command to create a new VSI.
+ */
+static int ice_vsi_add(struct ice_vsi *vsi)
+{
+ struct ice_vsi_ctx ctxt = { 0 };
+ struct ice_pf *pf = vsi->back;
+ struct ice_hw *hw = &pf->hw;
+ int ret = 0;
+
+ switch (vsi->type) {
+ case ICE_VSI_PF:
+ ctxt.flags = ICE_AQ_VSI_TYPE_PF;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ ice_set_dflt_vsi_ctx(&ctxt);
+ /* if the switch is in VEB mode, allow VSI loopback */
+ if (vsi->vsw->bridge_mode == BRIDGE_MODE_VEB)
+ ctxt.info.sw_flags |= ICE_AQ_VSI_SW_FLAG_ALLOW_LB;
+
+ /* Set LUT type and HASH type if RSS is enabled */
+ if (test_bit(ICE_FLAG_RSS_ENA, pf->flags))
+ ice_set_rss_vsi_ctx(&ctxt, vsi);
+
+ ctxt.info.sw_id = vsi->port_info->sw_id;
+ ice_vsi_setup_q_map(vsi, &ctxt);
+
+ ret = ice_aq_add_vsi(hw, &ctxt, NULL);
+ if (ret) {
+ dev_err(&vsi->back->pdev->dev,
+ "Add VSI AQ call failed, err %d\n", ret);
+ return -EIO;
+ }
+ vsi->info = ctxt.info;
+ vsi->vsi_num = ctxt.vsi_num;
+
+ return ret;
+}
+
+/**
+ * ice_vsi_release_msix - Clear the queue to Interrupt mapping in HW
+ * @vsi: the VSI being cleaned up
+ */
+static void ice_vsi_release_msix(struct ice_vsi *vsi)
+{
+ struct ice_pf *pf = vsi->back;
+ u16 vector = vsi->base_vector;
+ struct ice_hw *hw = &pf->hw;
+ u32 txq = 0;
+ u32 rxq = 0;
+ int i, q;
+
+ for (i = 0; i < vsi->num_q_vectors; i++, vector++) {
+ struct ice_q_vector *q_vector = vsi->q_vectors[i];
+
+ wr32(hw, GLINT_ITR(ICE_RX_ITR, vector), 0);
+ wr32(hw, GLINT_ITR(ICE_TX_ITR, vector), 0);
+ for (q = 0; q < q_vector->num_ring_tx; q++) {
+ wr32(hw, QINT_TQCTL(vsi->txq_map[txq]), 0);
+ txq++;
+ }
+
+ for (q = 0; q < q_vector->num_ring_rx; q++) {
+ wr32(hw, QINT_RQCTL(vsi->rxq_map[rxq]), 0);
+ rxq++;
+ }
+ }
+
+ ice_flush(hw);
+}
+
+/**
+ * ice_vsi_clear_rings - Deallocates the Tx and Rx rings for VSI
+ * @vsi: the VSI having rings deallocated
+ */
+static void ice_vsi_clear_rings(struct ice_vsi *vsi)
+{
+ int i;
+
+ if (vsi->tx_rings) {
+ for (i = 0; i < vsi->alloc_txq; i++) {
+ if (vsi->tx_rings[i]) {
+ kfree_rcu(vsi->tx_rings[i], rcu);
+ vsi->tx_rings[i] = NULL;
+ }
+ }
+ }
+ if (vsi->rx_rings) {
+ for (i = 0; i < vsi->alloc_rxq; i++) {
+ if (vsi->rx_rings[i]) {
+ kfree_rcu(vsi->rx_rings[i], rcu);
+ vsi->rx_rings[i] = NULL;
+ }
+ }
+ }
+}
+
+/**
+ * ice_vsi_alloc_rings - Allocates Tx and Rx rings for the VSI
+ * @vsi: VSI which is having rings allocated
+ */
+static int ice_vsi_alloc_rings(struct ice_vsi *vsi)
+{
+ struct ice_pf *pf = vsi->back;
+ int i;
+
+ /* Allocate tx_rings */
+ for (i = 0; i < vsi->alloc_txq; i++) {
+ struct ice_ring *ring;
+
+ /* allocate with kzalloc(), free with kfree_rcu() */
+ ring = kzalloc(sizeof(*ring), GFP_KERNEL);
+
+ if (!ring)
+ goto err_out;
+
+ ring->q_index = i;
+ ring->reg_idx = vsi->txq_map[i];
+ ring->ring_active = false;
+ ring->vsi = vsi;
+ ring->netdev = vsi->netdev;
+ ring->dev = &pf->pdev->dev;
+ ring->count = vsi->num_desc;
+
+ vsi->tx_rings[i] = ring;
+ }
+
+ /* Allocate rx_rings */
+ for (i = 0; i < vsi->alloc_rxq; i++) {
+ struct ice_ring *ring;
+
+ /* allocate with kzalloc(), free with kfree_rcu() */
+ ring = kzalloc(sizeof(*ring), GFP_KERNEL);
+ if (!ring)
+ goto err_out;
+
+ ring->q_index = i;
+ ring->reg_idx = vsi->rxq_map[i];
+ ring->ring_active = false;
+ ring->vsi = vsi;
+ ring->netdev = vsi->netdev;
+ ring->dev = &pf->pdev->dev;
+ ring->count = vsi->num_desc;
+ vsi->rx_rings[i] = ring;
+ }
+
+ return 0;
+
+err_out:
+ ice_vsi_clear_rings(vsi);
+ return -ENOMEM;
+}
+
+/**
+ * ice_vsi_free_irq - Free the irq association with the OS
+ * @vsi: the VSI being configured
+ */
+static void ice_vsi_free_irq(struct ice_vsi *vsi)
+{
+ struct ice_pf *pf = vsi->back;
+ int base = vsi->base_vector;
+
+ if (test_bit(ICE_FLAG_MSIX_ENA, pf->flags)) {
+ int i;
+
+ if (!vsi->q_vectors || !vsi->irqs_ready)
+ return;
+
+ vsi->irqs_ready = false;
+ for (i = 0; i < vsi->num_q_vectors; i++) {
+ u16 vector = i + base;
+ int irq_num;
+
+ irq_num = pf->msix_entries[vector].vector;
+
+ /* free only the irqs that were actually requested */
+ if (!vsi->q_vectors[i] ||
+ !(vsi->q_vectors[i]->num_ring_tx ||
+ vsi->q_vectors[i]->num_ring_rx))
+ continue;
+
+ /* clear the affinity notifier in the IRQ descriptor */
+ irq_set_affinity_notifier(irq_num, NULL);
+
+ /* clear the affinity_mask in the IRQ descriptor */
+ irq_set_affinity_hint(irq_num, NULL);
+ synchronize_irq(irq_num);
+ devm_free_irq(&pf->pdev->dev, irq_num,
+ vsi->q_vectors[i]);
+ }
+ ice_vsi_release_msix(vsi);
+ }
+}
+
+/**
+ * ice_vsi_cfg_msix - MSIX mode Interrupt Config in the HW
+ * @vsi: the VSI being configured
+ */
+static void ice_vsi_cfg_msix(struct ice_vsi *vsi)
+{
+ struct ice_pf *pf = vsi->back;
+ u16 vector = vsi->base_vector;
+ struct ice_hw *hw = &pf->hw;
+ u32 txq = 0, rxq = 0;
+ int i, q, itr;
+ u8 itr_gran;
+
+ for (i = 0; i < vsi->num_q_vectors; i++, vector++) {
+ struct ice_q_vector *q_vector = vsi->q_vectors[i];
+
+ itr_gran = hw->itr_gran_200;
+
+ if (q_vector->num_ring_rx) {
+ q_vector->rx.itr =
+ ITR_TO_REG(vsi->rx_rings[rxq]->rx_itr_setting,
+ itr_gran);
+ q_vector->rx.latency_range = ICE_LOW_LATENCY;
+ }
+
+ if (q_vector->num_ring_tx) {
+ q_vector->tx.itr =
+ ITR_TO_REG(vsi->tx_rings[txq]->tx_itr_setting,
+ itr_gran);
+ q_vector->tx.latency_range = ICE_LOW_LATENCY;
+ }
+ wr32(hw, GLINT_ITR(ICE_RX_ITR, vector), q_vector->rx.itr);
+ wr32(hw, GLINT_ITR(ICE_TX_ITR, vector), q_vector->tx.itr);
+
+ /* Both Transmit Queue Interrupt Cause Control register
+ * and Receive Queue Interrupt Cause control register
+ * expects MSIX_INDX field to be the vector index
+ * within the function space and not the absolute
+ * vector index across PF or across device.
+ * For SR-IOV VF VSIs queue vector index always starts
+ * with 1 since first vector index(0) is used for OICR
+ * in VF space. Since VMDq and other PF VSIs are withtin
+ * the PF function space, use the vector index thats
+ * tracked for this PF.
+ */
+ for (q = 0; q < q_vector->num_ring_tx; q++) {
+ u32 val;
+
+ itr = ICE_TX_ITR;
+ val = QINT_TQCTL_CAUSE_ENA_M |
+ (itr << QINT_TQCTL_ITR_INDX_S) |
+ (vector << QINT_TQCTL_MSIX_INDX_S);
+ wr32(hw, QINT_TQCTL(vsi->txq_map[txq]), val);
+ txq++;
+ }
+
+ for (q = 0; q < q_vector->num_ring_rx; q++) {
+ u32 val;
+
+ itr = ICE_RX_ITR;
+ val = QINT_RQCTL_CAUSE_ENA_M |
+ (itr << QINT_RQCTL_ITR_INDX_S) |
+ (vector << QINT_RQCTL_MSIX_INDX_S);
+ wr32(hw, QINT_RQCTL(vsi->rxq_map[rxq]), val);
+ rxq++;
+ }
+ }
+
+ ice_flush(hw);
+}
+
+/**
+ * ice_ena_misc_vector - enable the non-queue interrupts
+ * @pf: board private structure
+ */
+static void ice_ena_misc_vector(struct ice_pf *pf)
+{
+ struct ice_hw *hw = &pf->hw;
+ u32 val;
+
+ /* clear things first */
+ wr32(hw, PFINT_OICR_ENA, 0); /* disable all */
+ rd32(hw, PFINT_OICR); /* read to clear */
+
+ val = (PFINT_OICR_HLP_RDY_M |
+ PFINT_OICR_CPM_RDY_M |
+ PFINT_OICR_ECC_ERR_M |
+ PFINT_OICR_MAL_DETECT_M |
+ PFINT_OICR_GRST_M |
+ PFINT_OICR_PCI_EXCEPTION_M |
+ PFINT_OICR_GPIO_M |
+ PFINT_OICR_STORM_DETECT_M |
+ PFINT_OICR_HMC_ERR_M);
+
+ wr32(hw, PFINT_OICR_ENA, val);
+
+ /* SW_ITR_IDX = 0, but don't change INTENA */
+ wr32(hw, GLINT_DYN_CTL(pf->oicr_idx),
+ GLINT_DYN_CTL_SW_ITR_INDX_M | GLINT_DYN_CTL_INTENA_MSK_M);
+}
+
+/**
+ * ice_misc_intr - misc interrupt handler
+ * @irq: interrupt number
+ * @data: pointer to a q_vector
+ */
+static irqreturn_t ice_misc_intr(int __always_unused irq, void *data)
+{
+ struct ice_pf *pf = (struct ice_pf *)data;
+ struct ice_hw *hw = &pf->hw;
+ irqreturn_t ret = IRQ_NONE;
+ u32 oicr, ena_mask;
+
+ set_bit(__ICE_ADMINQ_EVENT_PENDING, pf->state);
+
+ oicr = rd32(hw, PFINT_OICR);
+ ena_mask = rd32(hw, PFINT_OICR_ENA);
+
+ if (oicr & PFINT_OICR_GRST_M) {
+ u32 reset;
+ /* we have a reset warning */
+ ena_mask &= ~PFINT_OICR_GRST_M;
+ reset = (rd32(hw, GLGEN_RSTAT) & GLGEN_RSTAT_RESET_TYPE_M) >>
+ GLGEN_RSTAT_RESET_TYPE_S;
+
+ if (reset == ICE_RESET_CORER)
+ pf->corer_count++;
+ else if (reset == ICE_RESET_GLOBR)
+ pf->globr_count++;
+ else
+ pf->empr_count++;
+
+ /* If a reset cycle isn't already in progress, we set a bit in
+ * pf->state so that the service task can start a reset/rebuild.
+ * We also make note of which reset happened so that peer
+ * devices/drivers can be informed.
+ */
+ if (!test_bit(__ICE_RESET_RECOVERY_PENDING, pf->state)) {
+ if (reset == ICE_RESET_CORER)
+ set_bit(__ICE_CORER_RECV, pf->state);
+ else if (reset == ICE_RESET_GLOBR)
+ set_bit(__ICE_GLOBR_RECV, pf->state);
+ else
+ set_bit(__ICE_EMPR_RECV, pf->state);
+
+ set_bit(__ICE_RESET_RECOVERY_PENDING, pf->state);
+ }
+ }
+
+ if (oicr & PFINT_OICR_HMC_ERR_M) {
+ ena_mask &= ~PFINT_OICR_HMC_ERR_M;
+ dev_dbg(&pf->pdev->dev,
+ "HMC Error interrupt - info 0x%x, data 0x%x\n",
+ rd32(hw, PFHMC_ERRORINFO),
+ rd32(hw, PFHMC_ERRORDATA));
+ }
+
+ /* Report and mask off any remaining unexpected interrupts */
+ oicr &= ena_mask;
+ if (oicr) {
+ dev_dbg(&pf->pdev->dev, "unhandled interrupt oicr=0x%08x\n",
+ oicr);
+ /* If a critical error is pending there is no choice but to
+ * reset the device.
+ */
+ if (oicr & (PFINT_OICR_PE_CRITERR_M |
+ PFINT_OICR_PCI_EXCEPTION_M |
+ PFINT_OICR_ECC_ERR_M)) {
+ set_bit(__ICE_PFR_REQ, pf->state);
+ ice_service_task_schedule(pf);
+ }
+ ena_mask &= ~oicr;
+ }
+ ret = IRQ_HANDLED;
+
+ /* re-enable interrupt causes that are not handled during this pass */
+ wr32(hw, PFINT_OICR_ENA, ena_mask);
+ if (!test_bit(__ICE_DOWN, pf->state)) {
+ ice_service_task_schedule(pf);
+ ice_irq_dynamic_ena(hw, NULL, NULL);
+ }
+
+ return ret;
+}
+
+/**
+ * ice_vsi_map_rings_to_vectors - Map VSI rings to interrupt vectors
+ * @vsi: the VSI being configured
+ *
+ * This function maps descriptor rings to the queue-specific vectors allotted
+ * through the MSI-X enabling code. On a constrained vector budget, we map Tx
+ * and Rx rings to the vector as "efficiently" as possible.
+ */
+static void ice_vsi_map_rings_to_vectors(struct ice_vsi *vsi)
+{
+ int q_vectors = vsi->num_q_vectors;
+ int tx_rings_rem, rx_rings_rem;
+ int v_id;
+
+ /* initially assigning remaining rings count to VSIs num queue value */
+ tx_rings_rem = vsi->num_txq;
+ rx_rings_rem = vsi->num_rxq;
+
+ for (v_id = 0; v_id < q_vectors; v_id++) {
+ struct ice_q_vector *q_vector = vsi->q_vectors[v_id];
+ int tx_rings_per_v, rx_rings_per_v, q_id, q_base;
+
+ /* Tx rings mapping to vector */
+ tx_rings_per_v = DIV_ROUND_UP(tx_rings_rem, q_vectors - v_id);
+ q_vector->num_ring_tx = tx_rings_per_v;
+ q_vector->tx.ring = NULL;
+ q_base = vsi->num_txq - tx_rings_rem;
+
+ for (q_id = q_base; q_id < (q_base + tx_rings_per_v); q_id++) {
+ struct ice_ring *tx_ring = vsi->tx_rings[q_id];
+
+ tx_ring->q_vector = q_vector;
+ tx_ring->next = q_vector->tx.ring;
+ q_vector->tx.ring = tx_ring;
+ }
+ tx_rings_rem -= tx_rings_per_v;
+
+ /* Rx rings mapping to vector */
+ rx_rings_per_v = DIV_ROUND_UP(rx_rings_rem, q_vectors - v_id);
+ q_vector->num_ring_rx = rx_rings_per_v;
+ q_vector->rx.ring = NULL;
+ q_base = vsi->num_rxq - rx_rings_rem;
+
+ for (q_id = q_base; q_id < (q_base + rx_rings_per_v); q_id++) {
+ struct ice_ring *rx_ring = vsi->rx_rings[q_id];
+
+ rx_ring->q_vector = q_vector;
+ rx_ring->next = q_vector->rx.ring;
+ q_vector->rx.ring = rx_ring;
+ }
+ rx_rings_rem -= rx_rings_per_v;
+ }
+}
+
+/**
+ * ice_vsi_set_num_qs - Set num queues, descriptors and vectors for a VSI
+ * @vsi: the VSI being configured
+ *
+ * Return 0 on success and a negative value on error
+ */
+static void ice_vsi_set_num_qs(struct ice_vsi *vsi)
+{
+ struct ice_pf *pf = vsi->back;
+
+ switch (vsi->type) {
+ case ICE_VSI_PF:
+ vsi->alloc_txq = pf->num_lan_tx;
+ vsi->alloc_rxq = pf->num_lan_rx;
+ vsi->num_desc = ALIGN(ICE_DFLT_NUM_DESC, ICE_REQ_DESC_MULTIPLE);
+ vsi->num_q_vectors = max_t(int, pf->num_lan_rx, pf->num_lan_tx);
+ break;
+ default:
+ dev_warn(&vsi->back->pdev->dev, "Unknown VSI type %d\n",
+ vsi->type);
+ break;
+ }
+}
+
+/**
+ * ice_vsi_alloc_arrays - Allocate queue and vector pointer arrays for the vsi
+ * @vsi: VSI pointer
+ * @alloc_qvectors: a bool to specify if q_vectors need to be allocated.
+ *
+ * On error: returns error code (negative)
+ * On success: returns 0
+ */
+static int ice_vsi_alloc_arrays(struct ice_vsi *vsi, bool alloc_qvectors)
+{
+ struct ice_pf *pf = vsi->back;
+
+ /* allocate memory for both Tx and Rx ring pointers */
+ vsi->tx_rings = devm_kcalloc(&pf->pdev->dev, vsi->alloc_txq,
+ sizeof(struct ice_ring *), GFP_KERNEL);
+ if (!vsi->tx_rings)
+ goto err_txrings;
+
+ vsi->rx_rings = devm_kcalloc(&pf->pdev->dev, vsi->alloc_rxq,
+ sizeof(struct ice_ring *), GFP_KERNEL);
+ if (!vsi->rx_rings)
+ goto err_rxrings;
+
+ if (alloc_qvectors) {
+ /* allocate memory for q_vector pointers */
+ vsi->q_vectors = devm_kcalloc(&pf->pdev->dev,
+ vsi->num_q_vectors,
+ sizeof(struct ice_q_vector *),
+ GFP_KERNEL);
+ if (!vsi->q_vectors)
+ goto err_vectors;
+ }
+
+ return 0;
+
+err_vectors:
+ devm_kfree(&pf->pdev->dev, vsi->rx_rings);
+err_rxrings:
+ devm_kfree(&pf->pdev->dev, vsi->tx_rings);
+err_txrings:
+ return -ENOMEM;
+}
+
+/**
+ * ice_msix_clean_rings - MSIX mode Interrupt Handler
+ * @irq: interrupt number
+ * @data: pointer to a q_vector
+ */
+static irqreturn_t ice_msix_clean_rings(int __always_unused irq, void *data)
+{
+ struct ice_q_vector *q_vector = (struct ice_q_vector *)data;
+
+ if (!q_vector->tx.ring && !q_vector->rx.ring)
+ return IRQ_HANDLED;
+
+ napi_schedule(&q_vector->napi);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ice_vsi_alloc - Allocates the next available struct vsi in the PF
+ * @pf: board private structure
+ * @type: type of VSI
+ *
+ * returns a pointer to a VSI on success, NULL on failure.
+ */
+static struct ice_vsi *ice_vsi_alloc(struct ice_pf *pf, enum ice_vsi_type type)
+{
+ struct ice_vsi *vsi = NULL;
+
+ /* Need to protect the allocation of the VSIs at the PF level */
+ mutex_lock(&pf->sw_mutex);
+
+ /* If we have already allocated our maximum number of VSIs,
+ * pf->next_vsi will be ICE_NO_VSI. If not, pf->next_vsi index
+ * is available to be populated
+ */
+ if (pf->next_vsi == ICE_NO_VSI) {
+ dev_dbg(&pf->pdev->dev, "out of VSI slots!\n");
+ goto unlock_pf;
+ }
+
+ vsi = devm_kzalloc(&pf->pdev->dev, sizeof(*vsi), GFP_KERNEL);
+ if (!vsi)
+ goto unlock_pf;
+
+ vsi->type = type;
+ vsi->back = pf;
+ set_bit(__ICE_DOWN, vsi->state);
+ vsi->idx = pf->next_vsi;
+ vsi->work_lmt = ICE_DFLT_IRQ_WORK;
+
+ ice_vsi_set_num_qs(vsi);
+
+ switch (vsi->type) {
+ case ICE_VSI_PF:
+ if (ice_vsi_alloc_arrays(vsi, true))
+ goto err_rings;
+
+ /* Setup default MSIX irq handler for VSI */
+ vsi->irq_handler = ice_msix_clean_rings;
+ break;
+ default:
+ dev_warn(&pf->pdev->dev, "Unknown VSI type %d\n", vsi->type);
+ goto unlock_pf;
+ }
+
+ /* fill VSI slot in the PF struct */
+ pf->vsi[pf->next_vsi] = vsi;
+
+ /* prepare pf->next_vsi for next use */
+ pf->next_vsi = ice_get_free_slot(pf->vsi, pf->num_alloc_vsi,
+ pf->next_vsi);
+ goto unlock_pf;
+
+err_rings:
+ devm_kfree(&pf->pdev->dev, vsi);
+ vsi = NULL;
+unlock_pf:
+ mutex_unlock(&pf->sw_mutex);
+ return vsi;
+}
+
+/**
+ * ice_free_irq_msix_misc - Unroll misc vector setup
+ * @pf: board private structure
+ */
+static void ice_free_irq_msix_misc(struct ice_pf *pf)
+{
+ /* disable OICR interrupt */
+ wr32(&pf->hw, PFINT_OICR_ENA, 0);
+ ice_flush(&pf->hw);
+
+ if (test_bit(ICE_FLAG_MSIX_ENA, pf->flags) && pf->msix_entries) {
+ synchronize_irq(pf->msix_entries[pf->oicr_idx].vector);
+ devm_free_irq(&pf->pdev->dev,
+ pf->msix_entries[pf->oicr_idx].vector, pf);
+ }
+
+ ice_free_res(pf->irq_tracker, pf->oicr_idx, ICE_RES_MISC_VEC_ID);
+}
+
+/**
+ * ice_req_irq_msix_misc - Setup the misc vector to handle non queue events
+ * @pf: board private structure
+ *
+ * This sets up the handler for MSIX 0, which is used to manage the
+ * non-queue interrupts, e.g. AdminQ and errors. This is not used
+ * when in MSI or Legacy interrupt mode.
+ */
+static int ice_req_irq_msix_misc(struct ice_pf *pf)
+{
+ struct ice_hw *hw = &pf->hw;
+ int oicr_idx, err = 0;
+ u8 itr_gran;
+ u32 val;
+
+ if (!pf->int_name[0])
+ snprintf(pf->int_name, sizeof(pf->int_name) - 1, "%s-%s:misc",
+ dev_driver_string(&pf->pdev->dev),
+ dev_name(&pf->pdev->dev));
+
+ /* Do not request IRQ but do enable OICR interrupt since settings are
+ * lost during reset. Note that this function is called only during
+ * rebuild path and not while reset is in progress.
+ */
+ if (ice_is_reset_recovery_pending(pf->state))
+ goto skip_req_irq;
+
+ /* reserve one vector in irq_tracker for misc interrupts */
+ oicr_idx = ice_get_res(pf, pf->irq_tracker, 1, ICE_RES_MISC_VEC_ID);
+ if (oicr_idx < 0)
+ return oicr_idx;
+
+ pf->oicr_idx = oicr_idx;
+
+ err = devm_request_irq(&pf->pdev->dev,
+ pf->msix_entries[pf->oicr_idx].vector,
+ ice_misc_intr, 0, pf->int_name, pf);
+ if (err) {
+ dev_err(&pf->pdev->dev,
+ "devm_request_irq for %s failed: %d\n",
+ pf->int_name, err);
+ ice_free_res(pf->irq_tracker, 1, ICE_RES_MISC_VEC_ID);
+ return err;
+ }
+
+skip_req_irq:
+ ice_ena_misc_vector(pf);
+
+ val = (pf->oicr_idx & PFINT_OICR_CTL_MSIX_INDX_M) |
+ (ICE_RX_ITR & PFINT_OICR_CTL_ITR_INDX_M) |
+ PFINT_OICR_CTL_CAUSE_ENA_M;
+ wr32(hw, PFINT_OICR_CTL, val);
+
+ /* This enables Admin queue Interrupt causes */
+ val = (pf->oicr_idx & PFINT_FW_CTL_MSIX_INDX_M) |
+ (ICE_RX_ITR & PFINT_FW_CTL_ITR_INDX_M) |
+ PFINT_FW_CTL_CAUSE_ENA_M;
+ wr32(hw, PFINT_FW_CTL, val);
+
+ itr_gran = hw->itr_gran_200;
+
+ wr32(hw, GLINT_ITR(ICE_RX_ITR, pf->oicr_idx),
+ ITR_TO_REG(ICE_ITR_8K, itr_gran));
+
+ ice_flush(hw);
+ ice_irq_dynamic_ena(hw, NULL, NULL);
+
+ return 0;
+}
+
+/**
+ * ice_vsi_get_qs_contig - Assign a contiguous chunk of queues to VSI
+ * @vsi: the VSI getting queues
+ *
+ * Return 0 on success and a negative value on error
+ */
+static int ice_vsi_get_qs_contig(struct ice_vsi *vsi)
+{
+ struct ice_pf *pf = vsi->back;
+ int offset, ret = 0;
+
+ mutex_lock(&pf->avail_q_mutex);
+ /* look for contiguous block of queues for tx */
+ offset = bitmap_find_next_zero_area(pf->avail_txqs, ICE_MAX_TXQS,
+ 0, vsi->alloc_txq, 0);
+ if (offset < ICE_MAX_TXQS) {
+ int i;
+
+ bitmap_set(pf->avail_txqs, offset, vsi->alloc_txq);
+ for (i = 0; i < vsi->alloc_txq; i++)
+ vsi->txq_map[i] = i + offset;
+ } else {
+ ret = -ENOMEM;
+ vsi->tx_mapping_mode = ICE_VSI_MAP_SCATTER;
+ }
+
+ /* look for contiguous block of queues for rx */
+ offset = bitmap_find_next_zero_area(pf->avail_rxqs, ICE_MAX_RXQS,
+ 0, vsi->alloc_rxq, 0);
+ if (offset < ICE_MAX_RXQS) {
+ int i;
+
+ bitmap_set(pf->avail_rxqs, offset, vsi->alloc_rxq);
+ for (i = 0; i < vsi->alloc_rxq; i++)
+ vsi->rxq_map[i] = i + offset;
+ } else {
+ ret = -ENOMEM;
+ vsi->rx_mapping_mode = ICE_VSI_MAP_SCATTER;
+ }
+ mutex_unlock(&pf->avail_q_mutex);
+
+ return ret;
+}
+
+/**
+ * ice_vsi_get_qs_scatter - Assign a scattered queues to VSI
+ * @vsi: the VSI getting queues
+ *
+ * Return 0 on success and a negative value on error
+ */
+static int ice_vsi_get_qs_scatter(struct ice_vsi *vsi)
+{
+ struct ice_pf *pf = vsi->back;
+ int i, index = 0;
+
+ mutex_lock(&pf->avail_q_mutex);
+
+ if (vsi->tx_mapping_mode == ICE_VSI_MAP_SCATTER) {
+ for (i = 0; i < vsi->alloc_txq; i++) {
+ index = find_next_zero_bit(pf->avail_txqs,
+ ICE_MAX_TXQS, index);
+ if (index < ICE_MAX_TXQS) {
+ set_bit(index, pf->avail_txqs);
+ vsi->txq_map[i] = index;
+ } else {
+ goto err_scatter_tx;
+ }
+ }
+ }
+
+ if (vsi->rx_mapping_mode == ICE_VSI_MAP_SCATTER) {
+ for (i = 0; i < vsi->alloc_rxq; i++) {
+ index = find_next_zero_bit(pf->avail_rxqs,
+ ICE_MAX_RXQS, index);
+ if (index < ICE_MAX_RXQS) {
+ set_bit(index, pf->avail_rxqs);
+ vsi->rxq_map[i] = index;
+ } else {
+ goto err_scatter_rx;
+ }
+ }
+ }
+
+ mutex_unlock(&pf->avail_q_mutex);
+ return 0;
+
+err_scatter_rx:
+ /* unflag any queues we have grabbed (i is failed position) */
+ for (index = 0; index < i; index++) {
+ clear_bit(vsi->rxq_map[index], pf->avail_rxqs);
+ vsi->rxq_map[index] = 0;
+ }
+ i = vsi->alloc_txq;
+err_scatter_tx:
+ /* i is either position of failed attempt or vsi->alloc_txq */
+ for (index = 0; index < i; index++) {
+ clear_bit(vsi->txq_map[index], pf->avail_txqs);
+ vsi->txq_map[index] = 0;
+ }
+
+ mutex_unlock(&pf->avail_q_mutex);
+ return -ENOMEM;
+}
+
+/**
+ * ice_vsi_get_qs - Assign queues from PF to VSI
+ * @vsi: the VSI to assign queues to
+ *
+ * Returns 0 on success and a negative value on error
+ */
+static int ice_vsi_get_qs(struct ice_vsi *vsi)
+{
+ int ret = 0;
+
+ vsi->tx_mapping_mode = ICE_VSI_MAP_CONTIG;
+ vsi->rx_mapping_mode = ICE_VSI_MAP_CONTIG;
+
+ /* NOTE: ice_vsi_get_qs_contig() will set the rx/tx mapping
+ * modes individually to scatter if assigning contiguous queues
+ * to rx or tx fails
+ */
+ ret = ice_vsi_get_qs_contig(vsi);
+ if (ret < 0) {
+ if (vsi->tx_mapping_mode == ICE_VSI_MAP_SCATTER)
+ vsi->alloc_txq = max_t(u16, vsi->alloc_txq,
+ ICE_MAX_SCATTER_TXQS);
+ if (vsi->rx_mapping_mode == ICE_VSI_MAP_SCATTER)
+ vsi->alloc_rxq = max_t(u16, vsi->alloc_rxq,
+ ICE_MAX_SCATTER_RXQS);
+ ret = ice_vsi_get_qs_scatter(vsi);
+ }
+
+ return ret;
+}
+
+/**
+ * ice_vsi_put_qs - Release queues from VSI to PF
+ * @vsi: the VSI thats going to release queues
+ */
+static void ice_vsi_put_qs(struct ice_vsi *vsi)
+{
+ struct ice_pf *pf = vsi->back;
+ int i;
+
+ mutex_lock(&pf->avail_q_mutex);
+
+ for (i = 0; i < vsi->alloc_txq; i++) {
+ clear_bit(vsi->txq_map[i], pf->avail_txqs);
+ vsi->txq_map[i] = ICE_INVAL_Q_INDEX;
+ }
+
+ for (i = 0; i < vsi->alloc_rxq; i++) {
+ clear_bit(vsi->rxq_map[i], pf->avail_rxqs);
+ vsi->rxq_map[i] = ICE_INVAL_Q_INDEX;
+ }
+
+ mutex_unlock(&pf->avail_q_mutex);
+}
+
+/**
+ * ice_free_q_vector - Free memory allocated for a specific interrupt vector
+ * @vsi: VSI having the memory freed
+ * @v_idx: index of the vector to be freed
+ */
+static void ice_free_q_vector(struct ice_vsi *vsi, int v_idx)
+{
+ struct ice_q_vector *q_vector;
+ struct ice_ring *ring;
+
+ if (!vsi->q_vectors[v_idx]) {
+ dev_dbg(&vsi->back->pdev->dev, "Queue vector at index %d not found\n",
+ v_idx);
+ return;
+ }
+ q_vector = vsi->q_vectors[v_idx];
+
+ ice_for_each_ring(ring, q_vector->tx)
+ ring->q_vector = NULL;
+ ice_for_each_ring(ring, q_vector->rx)
+ ring->q_vector = NULL;
+
+ /* only VSI with an associated netdev is set up with NAPI */
+ if (vsi->netdev)
+ netif_napi_del(&q_vector->napi);
+
+ devm_kfree(&vsi->back->pdev->dev, q_vector);
+ vsi->q_vectors[v_idx] = NULL;
+}
+
+/**
+ * ice_vsi_free_q_vectors - Free memory allocated for interrupt vectors
+ * @vsi: the VSI having memory freed
+ */
+static void ice_vsi_free_q_vectors(struct ice_vsi *vsi)
+{
+ int v_idx;
+
+ for (v_idx = 0; v_idx < vsi->num_q_vectors; v_idx++)
+ ice_free_q_vector(vsi, v_idx);
+}
+
+/**
+ * ice_cfg_netdev - Setup the netdev flags
+ * @vsi: the VSI being configured
+ *
+ * Returns 0 on success, negative value on failure
+ */
+static int ice_cfg_netdev(struct ice_vsi *vsi)
+{
+ netdev_features_t csumo_features;
+ netdev_features_t vlano_features;
+ netdev_features_t dflt_features;
+ netdev_features_t tso_features;
+ struct ice_netdev_priv *np;
+ struct net_device *netdev;
+ u8 mac_addr[ETH_ALEN];
+
+ netdev = alloc_etherdev_mqs(sizeof(struct ice_netdev_priv),
+ vsi->alloc_txq, vsi->alloc_rxq);
+ if (!netdev)
+ return -ENOMEM;
+
+ vsi->netdev = netdev;
+ np = netdev_priv(netdev);
+ np->vsi = vsi;
+
+ dflt_features = NETIF_F_SG |
+ NETIF_F_HIGHDMA |
+ NETIF_F_RXHASH;
+
+ csumo_features = NETIF_F_RXCSUM |
+ NETIF_F_IP_CSUM |
+ NETIF_F_IPV6_CSUM;
+
+ vlano_features = NETIF_F_HW_VLAN_CTAG_FILTER |
+ NETIF_F_HW_VLAN_CTAG_TX |
+ NETIF_F_HW_VLAN_CTAG_RX;
+
+ tso_features = NETIF_F_TSO;
+
+ /* set features that user can change */
+ netdev->hw_features = dflt_features | csumo_features |
+ vlano_features | tso_features;
+
+ /* enable features */
+ netdev->features |= netdev->hw_features;
+ /* encap and VLAN devices inherit default, csumo and tso features */
+ netdev->hw_enc_features |= dflt_features | csumo_features |
+ tso_features;
+ netdev->vlan_features |= dflt_features | csumo_features |
+ tso_features;
+
+ if (vsi->type == ICE_VSI_PF) {
+ SET_NETDEV_DEV(netdev, &vsi->back->pdev->dev);
+ ether_addr_copy(mac_addr, vsi->port_info->mac.perm_addr);
+
+ ether_addr_copy(netdev->dev_addr, mac_addr);
+ ether_addr_copy(netdev->perm_addr, mac_addr);
+ }
+
+ netdev->priv_flags |= IFF_UNICAST_FLT;
+
+ /* assign netdev_ops */
+ netdev->netdev_ops = &ice_netdev_ops;
+
+ /* setup watchdog timeout value to be 5 second */
+ netdev->watchdog_timeo = 5 * HZ;
+
+ ice_set_ethtool_ops(netdev);
+
+ netdev->min_mtu = ETH_MIN_MTU;
+ netdev->max_mtu = ICE_MAX_MTU;
+
+ return 0;
+}
+
+/**
+ * ice_vsi_free_arrays - clean up vsi resources
+ * @vsi: pointer to VSI being cleared
+ * @free_qvectors: bool to specify if q_vectors should be deallocated
+ */
+static void ice_vsi_free_arrays(struct ice_vsi *vsi, bool free_qvectors)
+{
+ struct ice_pf *pf = vsi->back;
+
+ /* free the ring and vector containers */
+ if (free_qvectors && vsi->q_vectors) {
+ devm_kfree(&pf->pdev->dev, vsi->q_vectors);
+ vsi->q_vectors = NULL;
+ }
+ if (vsi->tx_rings) {
+ devm_kfree(&pf->pdev->dev, vsi->tx_rings);
+ vsi->tx_rings = NULL;
+ }
+ if (vsi->rx_rings) {
+ devm_kfree(&pf->pdev->dev, vsi->rx_rings);
+ vsi->rx_rings = NULL;
+ }
+}
+
+/**
+ * ice_vsi_clear - clean up and deallocate the provided vsi
+ * @vsi: pointer to VSI being cleared
+ *
+ * This deallocates the vsi's queue resources, removes it from the PF's
+ * VSI array if necessary, and deallocates the VSI
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int ice_vsi_clear(struct ice_vsi *vsi)
+{
+ struct ice_pf *pf = NULL;
+
+ if (!vsi)
+ return 0;
+
+ if (!vsi->back)
+ return -EINVAL;
+
+ pf = vsi->back;
+
+ if (!pf->vsi[vsi->idx] || pf->vsi[vsi->idx] != vsi) {
+ dev_dbg(&pf->pdev->dev, "vsi does not exist at pf->vsi[%d]\n",
+ vsi->idx);
+ return -EINVAL;
+ }
+
+ mutex_lock(&pf->sw_mutex);
+ /* updates the PF for this cleared vsi */
+
+ pf->vsi[vsi->idx] = NULL;
+ if (vsi->idx < pf->next_vsi)
+ pf->next_vsi = vsi->idx;
+
+ ice_vsi_free_arrays(vsi, true);
+ mutex_unlock(&pf->sw_mutex);
+ devm_kfree(&pf->pdev->dev, vsi);
+
+ return 0;
+}
+
+/**
+ * ice_vsi_alloc_q_vector - Allocate memory for a single interrupt vector
+ * @vsi: the VSI being configured
+ * @v_idx: index of the vector in the vsi struct
+ *
+ * We allocate one q_vector. If allocation fails we return -ENOMEM.
+ */
+static int ice_vsi_alloc_q_vector(struct ice_vsi *vsi, int v_idx)
+{
+ struct ice_pf *pf = vsi->back;
+ struct ice_q_vector *q_vector;
+
+ /* allocate q_vector */
+ q_vector = devm_kzalloc(&pf->pdev->dev, sizeof(*q_vector), GFP_KERNEL);
+ if (!q_vector)
+ return -ENOMEM;
+
+ q_vector->vsi = vsi;
+ q_vector->v_idx = v_idx;
+ /* only set affinity_mask if the CPU is online */
+ if (cpu_online(v_idx))
+ cpumask_set_cpu(v_idx, &q_vector->affinity_mask);
+
+ if (vsi->netdev)
+ netif_napi_add(vsi->netdev, &q_vector->napi, ice_napi_poll,
+ NAPI_POLL_WEIGHT);
+ /* tie q_vector and vsi together */
+ vsi->q_vectors[v_idx] = q_vector;
+
+ return 0;
+}
+
+/**
+ * ice_vsi_alloc_q_vectors - Allocate memory for interrupt vectors
+ * @vsi: the VSI being configured
+ *
+ * We allocate one q_vector per queue interrupt. If allocation fails we
+ * return -ENOMEM.
+ */
+static int ice_vsi_alloc_q_vectors(struct ice_vsi *vsi)
+{
+ struct ice_pf *pf = vsi->back;
+ int v_idx = 0, num_q_vectors;
+ int err;
+
+ if (vsi->q_vectors[0]) {
+ dev_dbg(&pf->pdev->dev, "VSI %d has existing q_vectors\n",
+ vsi->vsi_num);
+ return -EEXIST;
+ }
+
+ if (test_bit(ICE_FLAG_MSIX_ENA, pf->flags)) {
+ num_q_vectors = vsi->num_q_vectors;
+ } else {
+ err = -EINVAL;
+ goto err_out;
+ }
+
+ for (v_idx = 0; v_idx < num_q_vectors; v_idx++) {
+ err = ice_vsi_alloc_q_vector(vsi, v_idx);
+ if (err)
+ goto err_out;
+ }
+
+ return 0;
+
+err_out:
+ while (v_idx--)
+ ice_free_q_vector(vsi, v_idx);
+
+ dev_err(&pf->pdev->dev,
+ "Failed to allocate %d q_vector for VSI %d, ret=%d\n",
+ vsi->num_q_vectors, vsi->vsi_num, err);
+ vsi->num_q_vectors = 0;
+ return err;
+}
+
+/**
+ * ice_vsi_setup_vector_base - Set up the base vector for the given VSI
+ * @vsi: ptr to the VSI
+ *
+ * This should only be called after ice_vsi_alloc() which allocates the
+ * corresponding SW VSI structure and initializes num_queue_pairs for the
+ * newly allocated VSI.
+ *
+ * Returns 0 on success or negative on failure
+ */
+static int ice_vsi_setup_vector_base(struct ice_vsi *vsi)
+{
+ struct ice_pf *pf = vsi->back;
+ int num_q_vectors = 0;
+
+ if (vsi->base_vector) {
+ dev_dbg(&pf->pdev->dev, "VSI %d has non-zero base vector %d\n",
+ vsi->vsi_num, vsi->base_vector);
+ return -EEXIST;
+ }
+
+ if (!test_bit(ICE_FLAG_MSIX_ENA, pf->flags))
+ return -ENOENT;
+
+ switch (vsi->type) {
+ case ICE_VSI_PF:
+ num_q_vectors = vsi->num_q_vectors;
+ break;
+ default:
+ dev_warn(&vsi->back->pdev->dev, "Unknown VSI type %d\n",
+ vsi->type);
+ break;
+ }
+
+ if (num_q_vectors)
+ vsi->base_vector = ice_get_res(pf, pf->irq_tracker,
+ num_q_vectors, vsi->idx);
+
+ if (vsi->base_vector < 0) {
+ dev_err(&pf->pdev->dev,
+ "Failed to get tracking for %d vectors for VSI %d, err=%d\n",
+ num_q_vectors, vsi->vsi_num, vsi->base_vector);
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+/**
+ * ice_fill_rss_lut - Fill the RSS lookup table with default values
+ * @lut: Lookup table
+ * @rss_table_size: Lookup table size
+ * @rss_size: Range of queue number for hashing
+ */
+void ice_fill_rss_lut(u8 *lut, u16 rss_table_size, u16 rss_size)
+{
+ u16 i;
+
+ for (i = 0; i < rss_table_size; i++)
+ lut[i] = i % rss_size;
+}
+
+/**
+ * ice_vsi_cfg_rss - Configure RSS params for a VSI
+ * @vsi: VSI to be configured
+ */
+static int ice_vsi_cfg_rss(struct ice_vsi *vsi)
+{
+ u8 seed[ICE_AQC_GET_SET_RSS_KEY_DATA_RSS_KEY_SIZE];
+ struct ice_aqc_get_set_rss_keys *key;
+ struct ice_pf *pf = vsi->back;
+ enum ice_status status;
+ int err = 0;
+ u8 *lut;
+
+ vsi->rss_size = min_t(int, vsi->rss_size, vsi->num_rxq);
+
+ lut = devm_kzalloc(&pf->pdev->dev, vsi->rss_table_size, GFP_KERNEL);
+ if (!lut)
+ return -ENOMEM;
+
+ if (vsi->rss_lut_user)
+ memcpy(lut, vsi->rss_lut_user, vsi->rss_table_size);
+ else
+ ice_fill_rss_lut(lut, vsi->rss_table_size, vsi->rss_size);
+
+ status = ice_aq_set_rss_lut(&pf->hw, vsi->vsi_num, vsi->rss_lut_type,
+ lut, vsi->rss_table_size);
+
+ if (status) {
+ dev_err(&vsi->back->pdev->dev,
+ "set_rss_lut failed, error %d\n", status);
+ err = -EIO;
+ goto ice_vsi_cfg_rss_exit;
+ }
+
+ key = devm_kzalloc(&vsi->back->pdev->dev, sizeof(*key), GFP_KERNEL);
+ if (!key) {
+ err = -ENOMEM;
+ goto ice_vsi_cfg_rss_exit;
+ }
+
+ if (vsi->rss_hkey_user)
+ memcpy(seed, vsi->rss_hkey_user,
+ ICE_AQC_GET_SET_RSS_KEY_DATA_RSS_KEY_SIZE);
+ else
+ netdev_rss_key_fill((void *)seed,
+ ICE_AQC_GET_SET_RSS_KEY_DATA_RSS_KEY_SIZE);
+ memcpy(&key->standard_rss_key, seed,
+ ICE_AQC_GET_SET_RSS_KEY_DATA_RSS_KEY_SIZE);
+
+ status = ice_aq_set_rss_key(&pf->hw, vsi->vsi_num, key);
+
+ if (status) {
+ dev_err(&vsi->back->pdev->dev, "set_rss_key failed, error %d\n",
+ status);
+ err = -EIO;
+ }
+
+ devm_kfree(&pf->pdev->dev, key);
+ice_vsi_cfg_rss_exit:
+ devm_kfree(&pf->pdev->dev, lut);
+ return err;
+}
+
+/**
+ * ice_vsi_reinit_setup - return resource and reallocate resource for a VSI
+ * @vsi: pointer to the ice_vsi
+ *
+ * This reallocates the VSIs queue resources
+ *
+ * Returns 0 on success and negative value on failure
+ */
+static int ice_vsi_reinit_setup(struct ice_vsi *vsi)
+{
+ u16 max_txqs[ICE_MAX_TRAFFIC_CLASS] = { 0 };
+ int ret, i;
+
+ if (!vsi)
+ return -EINVAL;
+
+ ice_vsi_free_q_vectors(vsi);
+ ice_free_res(vsi->back->irq_tracker, vsi->base_vector, vsi->idx);
+ vsi->base_vector = 0;
+ ice_vsi_clear_rings(vsi);
+ ice_vsi_free_arrays(vsi, false);
+ ice_vsi_set_num_qs(vsi);
+
+ /* Initialize VSI struct elements and create VSI in FW */
+ ret = ice_vsi_add(vsi);
+ if (ret < 0)
+ goto err_vsi;
+
+ ret = ice_vsi_alloc_arrays(vsi, false);
+ if (ret < 0)
+ goto err_vsi;
+
+ switch (vsi->type) {
+ case ICE_VSI_PF:
+ if (!vsi->netdev) {
+ ret = ice_cfg_netdev(vsi);
+ if (ret)
+ goto err_rings;
+
+ ret = register_netdev(vsi->netdev);
+ if (ret)
+ goto err_rings;
+
+ netif_carrier_off(vsi->netdev);
+ netif_tx_stop_all_queues(vsi->netdev);
+ }
+
+ ret = ice_vsi_alloc_q_vectors(vsi);
+ if (ret)
+ goto err_rings;
+
+ ret = ice_vsi_setup_vector_base(vsi);
+ if (ret)
+ goto err_vectors;
+
+ ret = ice_vsi_alloc_rings(vsi);
+ if (ret)
+ goto err_vectors;
+
+ ice_vsi_map_rings_to_vectors(vsi);
+ break;
+ default:
+ break;
+ }
+
+ ice_vsi_set_tc_cfg(vsi);
+
+ /* configure VSI nodes based on number of queues and TC's */
+ for (i = 0; i < vsi->tc_cfg.numtc; i++)
+ max_txqs[i] = vsi->num_txq;
+
+ ret = ice_cfg_vsi_lan(vsi->port_info, vsi->vsi_num,
+ vsi->tc_cfg.ena_tc, max_txqs);
+ if (ret) {
+ dev_info(&vsi->back->pdev->dev,
+ "Failed VSI lan queue config\n");
+ goto err_vectors;
+ }
+ return 0;
+
+err_vectors:
+ ice_vsi_free_q_vectors(vsi);
+err_rings:
+ if (vsi->netdev) {
+ vsi->current_netdev_flags = 0;
+ unregister_netdev(vsi->netdev);
+ free_netdev(vsi->netdev);
+ vsi->netdev = NULL;
+ }
+err_vsi:
+ ice_vsi_clear(vsi);
+ set_bit(__ICE_RESET_FAILED, vsi->back->state);
+ return ret;
+}
+
+/**
+ * ice_vsi_setup - Set up a VSI by a given type
+ * @pf: board private structure
+ * @type: VSI type
+ * @pi: pointer to the port_info instance
+ *
+ * This allocates the sw VSI structure and its queue resources.
+ *
+ * Returns pointer to the successfully allocated and configure VSI sw struct on
+ * success, otherwise returns NULL on failure.
+ */
+static struct ice_vsi *
+ice_vsi_setup(struct ice_pf *pf, enum ice_vsi_type type,
+ struct ice_port_info *pi)
+{
+ u16 max_txqs[ICE_MAX_TRAFFIC_CLASS] = { 0 };
+ struct device *dev = &pf->pdev->dev;
+ struct ice_vsi_ctx ctxt = { 0 };
+ struct ice_vsi *vsi;
+ int ret, i;
+
+ vsi = ice_vsi_alloc(pf, type);
+ if (!vsi) {
+ dev_err(dev, "could not allocate VSI\n");
+ return NULL;
+ }
+
+ vsi->port_info = pi;
+ vsi->vsw = pf->first_sw;
+
+ if (ice_vsi_get_qs(vsi)) {
+ dev_err(dev, "Failed to allocate queues. vsi->idx = %d\n",
+ vsi->idx);
+ goto err_get_qs;
+ }
+
+ /* set RSS capabilities */
+ ice_vsi_set_rss_params(vsi);
+
+ /* create the VSI */
+ ret = ice_vsi_add(vsi);
+ if (ret)
+ goto err_vsi;
+
+ ctxt.vsi_num = vsi->vsi_num;
+
+ switch (vsi->type) {
+ case ICE_VSI_PF:
+ ret = ice_cfg_netdev(vsi);
+ if (ret)
+ goto err_cfg_netdev;
+
+ ret = register_netdev(vsi->netdev);
+ if (ret)
+ goto err_register_netdev;
+
+ netif_carrier_off(vsi->netdev);
+
+ /* make sure transmit queues start off as stopped */
+ netif_tx_stop_all_queues(vsi->netdev);
+ ret = ice_vsi_alloc_q_vectors(vsi);
+ if (ret)
+ goto err_msix;
+
+ ret = ice_vsi_setup_vector_base(vsi);
+ if (ret)
+ goto err_rings;
+
+ ret = ice_vsi_alloc_rings(vsi);
+ if (ret)
+ goto err_rings;
+
+ ice_vsi_map_rings_to_vectors(vsi);
+
+ /* Do not exit if configuring RSS had an issue, at least
+ * receive traffic on first queue. Hence no need to capture
+ * return value
+ */
+ if (test_bit(ICE_FLAG_RSS_ENA, pf->flags))
+ ice_vsi_cfg_rss(vsi);
+ break;
+ default:
+ /* if vsi type is not recognized, clean up the resources and
+ * exit
+ */
+ goto err_rings;
+ }
+
+ ice_vsi_set_tc_cfg(vsi);
+
+ /* configure VSI nodes based on number of queues and TC's */
+ for (i = 0; i < vsi->tc_cfg.numtc; i++)
+ max_txqs[i] = vsi->num_txq;
+
+ ret = ice_cfg_vsi_lan(vsi->port_info, vsi->vsi_num,
+ vsi->tc_cfg.ena_tc, max_txqs);
+ if (ret) {
+ dev_info(&pf->pdev->dev, "Failed VSI lan queue config\n");
+ goto err_rings;
+ }
+
+ return vsi;
+
+err_rings:
+ ice_vsi_free_q_vectors(vsi);
+err_msix:
+ if (vsi->netdev && vsi->netdev->reg_state == NETREG_REGISTERED)
+ unregister_netdev(vsi->netdev);
+err_register_netdev:
+ if (vsi->netdev) {
+ free_netdev(vsi->netdev);
+ vsi->netdev = NULL;
+ }
+err_cfg_netdev:
+ ret = ice_aq_free_vsi(&pf->hw, &ctxt, false, NULL);
+ if (ret)
+ dev_err(&vsi->back->pdev->dev,
+ "Free VSI AQ call failed, err %d\n", ret);
+err_vsi:
+ ice_vsi_put_qs(vsi);
+err_get_qs:
+ pf->q_left_tx += vsi->alloc_txq;
+ pf->q_left_rx += vsi->alloc_rxq;
+ ice_vsi_clear(vsi);
+
+ return NULL;
+}
+
+/**
+ * ice_vsi_add_vlan - Add vsi membership for given vlan
+ * @vsi: the vsi being configured
+ * @vid: vlan id to be added
+ */
+static int ice_vsi_add_vlan(struct ice_vsi *vsi, u16 vid)
+{
+ struct ice_fltr_list_entry *tmp;
+ struct ice_pf *pf = vsi->back;
+ LIST_HEAD(tmp_add_list);
+ enum ice_status status;
+ int err = 0;
+
+ tmp = devm_kzalloc(&pf->pdev->dev, sizeof(*tmp), GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+
+ tmp->fltr_info.lkup_type = ICE_SW_LKUP_VLAN;
+ tmp->fltr_info.fltr_act = ICE_FWD_TO_VSI;
+ tmp->fltr_info.flag = ICE_FLTR_TX;
+ tmp->fltr_info.src = vsi->vsi_num;
+ tmp->fltr_info.fwd_id.vsi_id = vsi->vsi_num;
+ tmp->fltr_info.l_data.vlan.vlan_id = vid;
+
+ INIT_LIST_HEAD(&tmp->list_entry);
+ list_add(&tmp->list_entry, &tmp_add_list);
+
+ status = ice_add_vlan(&pf->hw, &tmp_add_list);
+ if (status) {
+ err = -ENODEV;
+ dev_err(&pf->pdev->dev, "Failure Adding VLAN %d on VSI %i\n",
+ vid, vsi->vsi_num);
+ }
+
+ ice_free_fltr_list(&pf->pdev->dev, &tmp_add_list);
+ return err;
+}
+
+/**
+ * ice_vlan_rx_add_vid - Add a vlan id filter to HW offload
+ * @netdev: network interface to be adjusted
+ * @proto: unused protocol
+ * @vid: vlan id to be added
+ *
+ * net_device_ops implementation for adding vlan ids
+ */
+static int ice_vlan_rx_add_vid(struct net_device *netdev,
+ __always_unused __be16 proto, u16 vid)
+{
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct ice_vsi *vsi = np->vsi;
+ int ret = 0;
+
+ if (vid >= VLAN_N_VID) {
+ netdev_err(netdev, "VLAN id requested %d is out of range %d\n",
+ vid, VLAN_N_VID);
+ return -EINVAL;
+ }
+
+ if (vsi->info.pvid)
+ return -EINVAL;
+
+ /* Add all VLAN ids including 0 to the switch filter. VLAN id 0 is
+ * needed to continue allowing all untagged packets since VLAN prune
+ * list is applied to all packets by the switch
+ */
+ ret = ice_vsi_add_vlan(vsi, vid);
+
+ if (!ret)
+ set_bit(vid, vsi->active_vlans);
+
+ return ret;
+}
+
+/**
+ * ice_vsi_kill_vlan - Remove VSI membership for a given VLAN
+ * @vsi: the VSI being configured
+ * @vid: VLAN id to be removed
+ */
+static void ice_vsi_kill_vlan(struct ice_vsi *vsi, u16 vid)
+{
+ struct ice_fltr_list_entry *list;
+ struct ice_pf *pf = vsi->back;
+ LIST_HEAD(tmp_add_list);
+
+ list = devm_kzalloc(&pf->pdev->dev, sizeof(*list), GFP_KERNEL);
+ if (!list)
+ return;
+
+ list->fltr_info.lkup_type = ICE_SW_LKUP_VLAN;
+ list->fltr_info.fwd_id.vsi_id = vsi->vsi_num;
+ list->fltr_info.fltr_act = ICE_FWD_TO_VSI;
+ list->fltr_info.l_data.vlan.vlan_id = vid;
+ list->fltr_info.flag = ICE_FLTR_TX;
+ list->fltr_info.src = vsi->vsi_num;
+
+ INIT_LIST_HEAD(&list->list_entry);
+ list_add(&list->list_entry, &tmp_add_list);
+
+ if (ice_remove_vlan(&pf->hw, &tmp_add_list))
+ dev_err(&pf->pdev->dev, "Error removing VLAN %d on vsi %i\n",
+ vid, vsi->vsi_num);
+
+ ice_free_fltr_list(&pf->pdev->dev, &tmp_add_list);
+}
+
+/**
+ * ice_vlan_rx_kill_vid - Remove a vlan id filter from HW offload
+ * @netdev: network interface to be adjusted
+ * @proto: unused protocol
+ * @vid: vlan id to be removed
+ *
+ * net_device_ops implementation for removing vlan ids
+ */
+static int ice_vlan_rx_kill_vid(struct net_device *netdev,
+ __always_unused __be16 proto, u16 vid)
+{
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct ice_vsi *vsi = np->vsi;
+
+ if (vsi->info.pvid)
+ return -EINVAL;
+
+ /* return code is ignored as there is nothing a user
+ * can do about failure to remove and a log message was
+ * already printed from the other function
+ */
+ ice_vsi_kill_vlan(vsi, vid);
+
+ clear_bit(vid, vsi->active_vlans);
+
+ return 0;
+}
+
+/**
+ * ice_setup_pf_sw - Setup the HW switch on startup or after reset
+ * @pf: board private structure
+ *
+ * Returns 0 on success, negative value on failure
+ */
+static int ice_setup_pf_sw(struct ice_pf *pf)
+{
+ LIST_HEAD(tmp_add_list);
+ u8 broadcast[ETH_ALEN];
+ struct ice_vsi *vsi;
+ int status = 0;
+
+ if (!ice_is_reset_recovery_pending(pf->state)) {
+ vsi = ice_vsi_setup(pf, ICE_VSI_PF, pf->hw.port_info);
+ if (!vsi) {
+ status = -ENOMEM;
+ goto error_exit;
+ }
+ } else {
+ vsi = pf->vsi[0];
+ status = ice_vsi_reinit_setup(vsi);
+ if (status < 0)
+ return -EIO;
+ }
+
+ /* tmp_add_list contains a list of MAC addresses for which MAC
+ * filters need to be programmed. Add the VSI's unicast MAC to
+ * this list
+ */
+ status = ice_add_mac_to_list(vsi, &tmp_add_list,
+ vsi->port_info->mac.perm_addr);
+ if (status)
+ goto error_exit;
+
+ /* VSI needs to receive broadcast traffic, so add the broadcast
+ * MAC address to the list.
+ */
+ eth_broadcast_addr(broadcast);
+ status = ice_add_mac_to_list(vsi, &tmp_add_list, broadcast);
+ if (status)
+ goto error_exit;
+
+ /* program MAC filters for entries in tmp_add_list */
+ status = ice_add_mac(&pf->hw, &tmp_add_list);
+ if (status) {
+ dev_err(&pf->pdev->dev, "Could not add MAC filters\n");
+ status = -ENOMEM;
+ goto error_exit;
+ }
+
+ ice_free_fltr_list(&pf->pdev->dev, &tmp_add_list);
+ return status;
+
+error_exit:
+ ice_free_fltr_list(&pf->pdev->dev, &tmp_add_list);
+
+ if (vsi) {
+ ice_vsi_free_q_vectors(vsi);
+ if (vsi->netdev && vsi->netdev->reg_state == NETREG_REGISTERED)
+ unregister_netdev(vsi->netdev);
+ if (vsi->netdev) {
+ free_netdev(vsi->netdev);
+ vsi->netdev = NULL;
+ }
+
+ ice_vsi_delete(vsi);
+ ice_vsi_put_qs(vsi);
+ pf->q_left_tx += vsi->alloc_txq;
+ pf->q_left_rx += vsi->alloc_rxq;
+ ice_vsi_clear(vsi);
+ }
+ return status;
+}
+
+/**
+ * ice_determine_q_usage - Calculate queue distribution
+ * @pf: board private structure
+ *
+ * Return -ENOMEM if we don't get enough queues for all ports
+ */
+static void ice_determine_q_usage(struct ice_pf *pf)
+{
+ u16 q_left_tx, q_left_rx;
+
+ q_left_tx = pf->hw.func_caps.common_cap.num_txq;
+ q_left_rx = pf->hw.func_caps.common_cap.num_rxq;
+
+ pf->num_lan_tx = min_t(int, q_left_tx, num_online_cpus());
+
+ /* only 1 rx queue unless RSS is enabled */
+ if (!test_bit(ICE_FLAG_RSS_ENA, pf->flags))
+ pf->num_lan_rx = 1;
+ else
+ pf->num_lan_rx = min_t(int, q_left_rx, num_online_cpus());
+
+ pf->q_left_tx = q_left_tx - pf->num_lan_tx;
+ pf->q_left_rx = q_left_rx - pf->num_lan_rx;
+}
+
+/**
+ * ice_deinit_pf - Unrolls initialziations done by ice_init_pf
+ * @pf: board private structure to initialize
+ */
+static void ice_deinit_pf(struct ice_pf *pf)
+{
+ if (pf->serv_tmr.function)
+ del_timer_sync(&pf->serv_tmr);
+ if (pf->serv_task.func)
+ cancel_work_sync(&pf->serv_task);
+ mutex_destroy(&pf->sw_mutex);
+ mutex_destroy(&pf->avail_q_mutex);
+}
+
+/**
+ * ice_init_pf - Initialize general software structures (struct ice_pf)
+ * @pf: board private structure to initialize
+ */
+static void ice_init_pf(struct ice_pf *pf)
+{
+ bitmap_zero(pf->flags, ICE_PF_FLAGS_NBITS);
+ set_bit(ICE_FLAG_MSIX_ENA, pf->flags);
+
+ mutex_init(&pf->sw_mutex);
+ mutex_init(&pf->avail_q_mutex);
+
+ /* Clear avail_[t|r]x_qs bitmaps (set all to avail) */
+ mutex_lock(&pf->avail_q_mutex);
+ bitmap_zero(pf->avail_txqs, ICE_MAX_TXQS);
+ bitmap_zero(pf->avail_rxqs, ICE_MAX_RXQS);
+ mutex_unlock(&pf->avail_q_mutex);
+
+ if (pf->hw.func_caps.common_cap.rss_table_size)
+ set_bit(ICE_FLAG_RSS_ENA, pf->flags);
+
+ /* setup service timer and periodic service task */
+ timer_setup(&pf->serv_tmr, ice_service_timer, 0);
+ pf->serv_tmr_period = HZ;
+ INIT_WORK(&pf->serv_task, ice_service_task);
+ clear_bit(__ICE_SERVICE_SCHED, pf->state);
+}
+
+/**
+ * ice_ena_msix_range - Request a range of MSIX vectors from the OS
+ * @pf: board private structure
+ *
+ * compute the number of MSIX vectors required (v_budget) and request from
+ * the OS. Return the number of vectors reserved or negative on failure
+ */
+static int ice_ena_msix_range(struct ice_pf *pf)
+{
+ int v_left, v_actual, v_budget = 0;
+ int needed, err, i;
+
+ v_left = pf->hw.func_caps.common_cap.num_msix_vectors;
+
+ /* reserve one vector for miscellaneous handler */
+ needed = 1;
+ v_budget += needed;
+ v_left -= needed;
+
+ /* reserve vectors for LAN traffic */
+ pf->num_lan_msix = min_t(int, num_online_cpus(), v_left);
+ v_budget += pf->num_lan_msix;
+
+ pf->msix_entries = devm_kcalloc(&pf->pdev->dev, v_budget,
+ sizeof(struct msix_entry), GFP_KERNEL);
+
+ if (!pf->msix_entries) {
+ err = -ENOMEM;
+ goto exit_err;
+ }
+
+ for (i = 0; i < v_budget; i++)
+ pf->msix_entries[i].entry = i;
+
+ /* actually reserve the vectors */
+ v_actual = pci_enable_msix_range(pf->pdev, pf->msix_entries,
+ ICE_MIN_MSIX, v_budget);
+
+ if (v_actual < 0) {
+ dev_err(&pf->pdev->dev, "unable to reserve MSI-X vectors\n");
+ err = v_actual;
+ goto msix_err;
+ }
+
+ if (v_actual < v_budget) {
+ dev_warn(&pf->pdev->dev,
+ "not enough vectors. requested = %d, obtained = %d\n",
+ v_budget, v_actual);
+ if (v_actual >= (pf->num_lan_msix + 1)) {
+ pf->num_avail_msix = v_actual - (pf->num_lan_msix + 1);
+ } else if (v_actual >= 2) {
+ pf->num_lan_msix = 1;
+ pf->num_avail_msix = v_actual - 2;
+ } else {
+ pci_disable_msix(pf->pdev);
+ err = -ERANGE;
+ goto msix_err;
+ }
+ }
+
+ return v_actual;
+
+msix_err:
+ devm_kfree(&pf->pdev->dev, pf->msix_entries);
+ goto exit_err;
+
+exit_err:
+ pf->num_lan_msix = 0;
+ clear_bit(ICE_FLAG_MSIX_ENA, pf->flags);
+ return err;
+}
+
+/**
+ * ice_dis_msix - Disable MSI-X interrupt setup in OS
+ * @pf: board private structure
+ */
+static void ice_dis_msix(struct ice_pf *pf)
+{
+ pci_disable_msix(pf->pdev);
+ devm_kfree(&pf->pdev->dev, pf->msix_entries);
+ pf->msix_entries = NULL;
+ clear_bit(ICE_FLAG_MSIX_ENA, pf->flags);
+}
+
+/**
+ * ice_init_interrupt_scheme - Determine proper interrupt scheme
+ * @pf: board private structure to initialize
+ */
+static int ice_init_interrupt_scheme(struct ice_pf *pf)
+{
+ int vectors = 0;
+ ssize_t size;
+
+ if (test_bit(ICE_FLAG_MSIX_ENA, pf->flags))
+ vectors = ice_ena_msix_range(pf);
+ else
+ return -ENODEV;
+
+ if (vectors < 0)
+ return vectors;
+
+ /* set up vector assignment tracking */
+ size = sizeof(struct ice_res_tracker) + (sizeof(u16) * vectors);
+
+ pf->irq_tracker = devm_kzalloc(&pf->pdev->dev, size, GFP_KERNEL);
+ if (!pf->irq_tracker) {
+ ice_dis_msix(pf);
+ return -ENOMEM;
+ }
+
+ pf->irq_tracker->num_entries = vectors;
+
+ return 0;
+}
+
+/**
+ * ice_clear_interrupt_scheme - Undo things done by ice_init_interrupt_scheme
+ * @pf: board private structure
+ */
+static void ice_clear_interrupt_scheme(struct ice_pf *pf)
+{
+ if (test_bit(ICE_FLAG_MSIX_ENA, pf->flags))
+ ice_dis_msix(pf);
+
+ devm_kfree(&pf->pdev->dev, pf->irq_tracker);
+ pf->irq_tracker = NULL;
+}
+
+/**
+ * ice_probe - Device initialization routine
+ * @pdev: PCI device information struct
+ * @ent: entry in ice_pci_tbl
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int ice_probe(struct pci_dev *pdev,
+ const struct pci_device_id __always_unused *ent)
+{
+ struct ice_pf *pf;
+ struct ice_hw *hw;
+ int err;
+
+ /* this driver uses devres, see Documentation/driver-model/devres.txt */
+ err = pcim_enable_device(pdev);
+ if (err)
+ return err;
+
+ err = pcim_iomap_regions(pdev, BIT(ICE_BAR0), pci_name(pdev));
+ if (err) {
+ dev_err(&pdev->dev, "I/O map error %d\n", err);
+ return err;
+ }
+
+ pf = devm_kzalloc(&pdev->dev, sizeof(*pf), GFP_KERNEL);
+ if (!pf)
+ return -ENOMEM;
+
+ /* set up for high or low dma */
+ err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+ if (err)
+ err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (err) {
+ dev_err(&pdev->dev, "DMA configuration failed: 0x%x\n", err);
+ return err;
+ }
+
+ pci_enable_pcie_error_reporting(pdev);
+ pci_set_master(pdev);
+
+ pf->pdev = pdev;
+ pci_set_drvdata(pdev, pf);
+ set_bit(__ICE_DOWN, pf->state);
+
+ hw = &pf->hw;
+ hw->hw_addr = pcim_iomap_table(pdev)[ICE_BAR0];
+ hw->back = pf;
+ hw->vendor_id = pdev->vendor;
+ hw->device_id = pdev->device;
+ pci_read_config_byte(pdev, PCI_REVISION_ID, &hw->revision_id);
+ hw->subsystem_vendor_id = pdev->subsystem_vendor;
+ hw->subsystem_device_id = pdev->subsystem_device;
+ hw->bus.device = PCI_SLOT(pdev->devfn);
+ hw->bus.func = PCI_FUNC(pdev->devfn);
+ ice_set_ctrlq_len(hw);
+
+ pf->msg_enable = netif_msg_init(debug, ICE_DFLT_NETIF_M);
+
+#ifndef CONFIG_DYNAMIC_DEBUG
+ if (debug < -1)
+ hw->debug_mask = debug;
+#endif
+
+ err = ice_init_hw(hw);
+ if (err) {
+ dev_err(&pdev->dev, "ice_init_hw failed: %d\n", err);
+ err = -EIO;
+ goto err_exit_unroll;
+ }
+
+ dev_info(&pdev->dev, "firmware %d.%d.%05d api %d.%d\n",
+ hw->fw_maj_ver, hw->fw_min_ver, hw->fw_build,
+ hw->api_maj_ver, hw->api_min_ver);
+
+ ice_init_pf(pf);
+
+ ice_determine_q_usage(pf);
+
+ pf->num_alloc_vsi = min_t(u16, ICE_MAX_VSI_ALLOC,
+ hw->func_caps.guaranteed_num_vsi);
+ if (!pf->num_alloc_vsi) {
+ err = -EIO;
+ goto err_init_pf_unroll;
+ }
+
+ pf->vsi = devm_kcalloc(&pdev->dev, pf->num_alloc_vsi,
+ sizeof(struct ice_vsi *), GFP_KERNEL);
+ if (!pf->vsi) {
+ err = -ENOMEM;
+ goto err_init_pf_unroll;
+ }
+
+ err = ice_init_interrupt_scheme(pf);
+ if (err) {
+ dev_err(&pdev->dev,
+ "ice_init_interrupt_scheme failed: %d\n", err);
+ err = -EIO;
+ goto err_init_interrupt_unroll;
+ }
+
+ /* In case of MSIX we are going to setup the misc vector right here
+ * to handle admin queue events etc. In case of legacy and MSI
+ * the misc functionality and queue processing is combined in
+ * the same vector and that gets setup at open.
+ */
+ if (test_bit(ICE_FLAG_MSIX_ENA, pf->flags)) {
+ err = ice_req_irq_msix_misc(pf);
+ if (err) {
+ dev_err(&pdev->dev,
+ "setup of misc vector failed: %d\n", err);
+ goto err_init_interrupt_unroll;
+ }
+ }
+
+ /* create switch struct for the switch element created by FW on boot */
+ pf->first_sw = devm_kzalloc(&pdev->dev, sizeof(struct ice_sw),
+ GFP_KERNEL);
+ if (!pf->first_sw) {
+ err = -ENOMEM;
+ goto err_msix_misc_unroll;
+ }
+
+ pf->first_sw->bridge_mode = BRIDGE_MODE_VEB;
+ pf->first_sw->pf = pf;
+
+ /* record the sw_id available for later use */
+ pf->first_sw->sw_id = hw->port_info->sw_id;
+
+ err = ice_setup_pf_sw(pf);
+ if (err) {
+ dev_err(&pdev->dev,
+ "probe failed due to setup pf switch:%d\n", err);
+ goto err_alloc_sw_unroll;
+ }
+
+ /* Driver is mostly up */
+ clear_bit(__ICE_DOWN, pf->state);
+
+ /* since everything is good, start the service timer */
+ mod_timer(&pf->serv_tmr, round_jiffies(jiffies + pf->serv_tmr_period));
+
+ err = ice_init_link_events(pf->hw.port_info);
+ if (err) {
+ dev_err(&pdev->dev, "ice_init_link_events failed: %d\n", err);
+ goto err_alloc_sw_unroll;
+ }
+
+ return 0;
+
+err_alloc_sw_unroll:
+ set_bit(__ICE_DOWN, pf->state);
+ devm_kfree(&pf->pdev->dev, pf->first_sw);
+err_msix_misc_unroll:
+ ice_free_irq_msix_misc(pf);
+err_init_interrupt_unroll:
+ ice_clear_interrupt_scheme(pf);
+ devm_kfree(&pdev->dev, pf->vsi);
+err_init_pf_unroll:
+ ice_deinit_pf(pf);
+ ice_deinit_hw(hw);
+err_exit_unroll:
+ pci_disable_pcie_error_reporting(pdev);
+ return err;
+}
+
+/**
+ * ice_remove - Device removal routine
+ * @pdev: PCI device information struct
+ */
+static void ice_remove(struct pci_dev *pdev)
+{
+ struct ice_pf *pf = pci_get_drvdata(pdev);
+ int i = 0;
+ int err;
+
+ if (!pf)
+ return;
+
+ set_bit(__ICE_DOWN, pf->state);
+
+ for (i = 0; i < pf->num_alloc_vsi; i++) {
+ if (!pf->vsi[i])
+ continue;
+
+ err = ice_vsi_release(pf->vsi[i]);
+ if (err)
+ dev_dbg(&pf->pdev->dev, "Failed to release VSI index %d (err %d)\n",
+ i, err);
+ }
+
+ ice_free_irq_msix_misc(pf);
+ ice_clear_interrupt_scheme(pf);
+ ice_deinit_pf(pf);
+ ice_deinit_hw(&pf->hw);
+ pci_disable_pcie_error_reporting(pdev);
+}
+
+/* ice_pci_tbl - PCI Device ID Table
+ *
+ * Wildcard entries (PCI_ANY_ID) should come last
+ * Last entry must be all 0s
+ *
+ * { Vendor ID, Device ID, SubVendor ID, SubDevice ID,
+ * Class, Class Mask, private data (not used) }
+ */
+static const struct pci_device_id ice_pci_tbl[] = {
+ { PCI_VDEVICE(INTEL, ICE_DEV_ID_C810_BACKPLANE), 0 },
+ { PCI_VDEVICE(INTEL, ICE_DEV_ID_C810_QSFP), 0 },
+ { PCI_VDEVICE(INTEL, ICE_DEV_ID_C810_SFP), 0 },
+ { PCI_VDEVICE(INTEL, ICE_DEV_ID_C810_10G_BASE_T), 0 },
+ { PCI_VDEVICE(INTEL, ICE_DEV_ID_C810_SGMII), 0 },
+ /* required last entry */
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, ice_pci_tbl);
+
+static struct pci_driver ice_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = ice_pci_tbl,
+ .probe = ice_probe,
+ .remove = ice_remove,
+};
+
+/**
+ * ice_module_init - Driver registration routine
+ *
+ * ice_module_init is the first routine called when the driver is
+ * loaded. All it does is register with the PCI subsystem.
+ */
+static int __init ice_module_init(void)
+{
+ int status;
+
+ pr_info("%s - version %s\n", ice_driver_string, ice_drv_ver);
+ pr_info("%s\n", ice_copyright);
+
+ ice_wq = alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM, KBUILD_MODNAME);
+ if (!ice_wq) {
+ pr_err("Failed to create workqueue\n");
+ return -ENOMEM;
+ }
+
+ status = pci_register_driver(&ice_driver);
+ if (status) {
+ pr_err("failed to register pci driver, err %d\n", status);
+ destroy_workqueue(ice_wq);
+ }
+
+ return status;
+}
+module_init(ice_module_init);
+
+/**
+ * ice_module_exit - Driver exit cleanup routine
+ *
+ * ice_module_exit is called just before the driver is removed
+ * from memory.
+ */
+static void __exit ice_module_exit(void)
+{
+ pci_unregister_driver(&ice_driver);
+ destroy_workqueue(ice_wq);
+ pr_info("module unloaded\n");
+}
+module_exit(ice_module_exit);
+
+/**
+ * ice_set_mac_address - NDO callback to set mac address
+ * @netdev: network interface device structure
+ * @pi: pointer to an address structure
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int ice_set_mac_address(struct net_device *netdev, void *pi)
+{
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct ice_vsi *vsi = np->vsi;
+ struct ice_pf *pf = vsi->back;
+ struct ice_hw *hw = &pf->hw;
+ struct sockaddr *addr = pi;
+ enum ice_status status;
+ LIST_HEAD(a_mac_list);
+ LIST_HEAD(r_mac_list);
+ u8 flags = 0;
+ int err;
+ u8 *mac;
+
+ mac = (u8 *)addr->sa_data;
+
+ if (!is_valid_ether_addr(mac))
+ return -EADDRNOTAVAIL;
+
+ if (ether_addr_equal(netdev->dev_addr, mac)) {
+ netdev_warn(netdev, "already using mac %pM\n", mac);
+ return 0;
+ }
+
+ if (test_bit(__ICE_DOWN, pf->state) ||
+ ice_is_reset_recovery_pending(pf->state)) {
+ netdev_err(netdev, "can't set mac %pM. device not ready\n",
+ mac);
+ return -EBUSY;
+ }
+
+ /* When we change the mac address we also have to change the mac address
+ * based filter rules that were created previously for the old mac
+ * address. So first, we remove the old filter rule using ice_remove_mac
+ * and then create a new filter rule using ice_add_mac. Note that for
+ * both these operations, we first need to form a "list" of mac
+ * addresses (even though in this case, we have only 1 mac address to be
+ * added/removed) and this done using ice_add_mac_to_list. Depending on
+ * the ensuing operation this "list" of mac addresses is either to be
+ * added or removed from the filter.
+ */
+ err = ice_add_mac_to_list(vsi, &r_mac_list, netdev->dev_addr);
+ if (err) {
+ err = -EADDRNOTAVAIL;
+ goto free_lists;
+ }
+
+ status = ice_remove_mac(hw, &r_mac_list);
+ if (status) {
+ err = -EADDRNOTAVAIL;
+ goto free_lists;
+ }
+
+ err = ice_add_mac_to_list(vsi, &a_mac_list, mac);
+ if (err) {
+ err = -EADDRNOTAVAIL;
+ goto free_lists;
+ }
+
+ status = ice_add_mac(hw, &a_mac_list);
+ if (status) {
+ err = -EADDRNOTAVAIL;
+ goto free_lists;
+ }
+
+free_lists:
+ /* free list entries */
+ ice_free_fltr_list(&pf->pdev->dev, &r_mac_list);
+ ice_free_fltr_list(&pf->pdev->dev, &a_mac_list);
+
+ if (err) {
+ netdev_err(netdev, "can't set mac %pM. filter update failed\n",
+ mac);
+ return err;
+ }
+
+ /* change the netdev's mac address */
+ memcpy(netdev->dev_addr, mac, netdev->addr_len);
+ netdev_dbg(vsi->netdev, "updated mac address to %pM\n",
+ netdev->dev_addr);
+
+ /* write new mac address to the firmware */
+ flags = ICE_AQC_MAN_MAC_UPDATE_LAA_WOL;
+ status = ice_aq_manage_mac_write(hw, mac, flags, NULL);
+ if (status) {
+ netdev_err(netdev, "can't set mac %pM. write to firmware failed.\n",
+ mac);
+ }
+ return 0;
+}
+
+/**
+ * ice_set_rx_mode - NDO callback to set the netdev filters
+ * @netdev: network interface device structure
+ */
+static void ice_set_rx_mode(struct net_device *netdev)
+{
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct ice_vsi *vsi = np->vsi;
+
+ if (!vsi)
+ return;
+
+ /* Set the flags to synchronize filters
+ * ndo_set_rx_mode may be triggered even without a change in netdev
+ * flags
+ */
+ set_bit(ICE_VSI_FLAG_UMAC_FLTR_CHANGED, vsi->flags);
+ set_bit(ICE_VSI_FLAG_MMAC_FLTR_CHANGED, vsi->flags);
+ set_bit(ICE_FLAG_FLTR_SYNC, vsi->back->flags);
+
+ /* schedule our worker thread which will take care of
+ * applying the new filter changes
+ */
+ ice_service_task_schedule(vsi->back);
+}
+
+/**
+ * ice_fdb_add - add an entry to the hardware database
+ * @ndm: the input from the stack
+ * @tb: pointer to array of nladdr (unused)
+ * @dev: the net device pointer
+ * @addr: the MAC address entry being added
+ * @vid: VLAN id
+ * @flags: instructions from stack about fdb operation
+ */
+static int ice_fdb_add(struct ndmsg *ndm, struct nlattr __always_unused *tb[],
+ struct net_device *dev, const unsigned char *addr,
+ u16 vid, u16 flags)
+{
+ int err;
+
+ if (vid) {
+ netdev_err(dev, "VLANs aren't supported yet for dev_uc|mc_add()\n");
+ return -EINVAL;
+ }
+ if (ndm->ndm_state && !(ndm->ndm_state & NUD_PERMANENT)) {
+ netdev_err(dev, "FDB only supports static addresses\n");
+ return -EINVAL;
+ }
+
+ if (is_unicast_ether_addr(addr) || is_link_local_ether_addr(addr))
+ err = dev_uc_add_excl(dev, addr);
+ else if (is_multicast_ether_addr(addr))
+ err = dev_mc_add_excl(dev, addr);
+ else
+ err = -EINVAL;
+
+ /* Only return duplicate errors if NLM_F_EXCL is set */
+ if (err == -EEXIST && !(flags & NLM_F_EXCL))
+ err = 0;
+
+ return err;
+}
+
+/**
+ * ice_fdb_del - delete an entry from the hardware database
+ * @ndm: the input from the stack
+ * @tb: pointer to array of nladdr (unused)
+ * @dev: the net device pointer
+ * @addr: the MAC address entry being added
+ * @vid: VLAN id
+ */
+static int ice_fdb_del(struct ndmsg *ndm, __always_unused struct nlattr *tb[],
+ struct net_device *dev, const unsigned char *addr,
+ __always_unused u16 vid)
+{
+ int err;
+
+ if (ndm->ndm_state & NUD_PERMANENT) {
+ netdev_err(dev, "FDB only supports static addresses\n");
+ return -EINVAL;
+ }
+
+ if (is_unicast_ether_addr(addr))
+ err = dev_uc_del(dev, addr);
+ else if (is_multicast_ether_addr(addr))
+ err = dev_mc_del(dev, addr);
+ else
+ err = -EINVAL;
+
+ return err;
+}
+
+/**
+ * ice_vsi_manage_vlan_insertion - Manage VLAN insertion for the VSI for Tx
+ * @vsi: the vsi being changed
+ */
+static int ice_vsi_manage_vlan_insertion(struct ice_vsi *vsi)
+{
+ struct device *dev = &vsi->back->pdev->dev;
+ struct ice_hw *hw = &vsi->back->hw;
+ struct ice_vsi_ctx ctxt = { 0 };
+ enum ice_status status;
+
+ /* Here we are configuring the VSI to let the driver add VLAN tags by
+ * setting port_vlan_flags to ICE_AQ_VSI_PVLAN_MODE_ALL. The actual VLAN
+ * tag insertion happens in the Tx hot path, in ice_tx_map.
+ */
+ ctxt.info.port_vlan_flags = ICE_AQ_VSI_PVLAN_MODE_ALL;
+
+ ctxt.info.valid_sections = cpu_to_le16(ICE_AQ_VSI_PROP_VLAN_VALID);
+ ctxt.vsi_num = vsi->vsi_num;
+
+ status = ice_aq_update_vsi(hw, &ctxt, NULL);
+ if (status) {
+ dev_err(dev, "update VSI for VLAN insert failed, err %d aq_err %d\n",
+ status, hw->adminq.sq_last_status);
+ return -EIO;
+ }
+
+ vsi->info.port_vlan_flags = ctxt.info.port_vlan_flags;
+ return 0;
+}
+
+/**
+ * ice_vsi_manage_vlan_stripping - Manage VLAN stripping for the VSI for Rx
+ * @vsi: the vsi being changed
+ * @ena: boolean value indicating if this is a enable or disable request
+ */
+static int ice_vsi_manage_vlan_stripping(struct ice_vsi *vsi, bool ena)
+{
+ struct device *dev = &vsi->back->pdev->dev;
+ struct ice_hw *hw = &vsi->back->hw;
+ struct ice_vsi_ctx ctxt = { 0 };
+ enum ice_status status;
+
+ /* Here we are configuring what the VSI should do with the VLAN tag in
+ * the Rx packet. We can either leave the tag in the packet or put it in
+ * the Rx descriptor.
+ */
+ if (ena) {
+ /* Strip VLAN tag from Rx packet and put it in the desc */
+ ctxt.info.port_vlan_flags = ICE_AQ_VSI_PVLAN_EMOD_STR_BOTH;
+ } else {
+ /* Disable stripping. Leave tag in packet */
+ ctxt.info.port_vlan_flags = ICE_AQ_VSI_PVLAN_EMOD_NOTHING;
+ }
+
+ ctxt.info.valid_sections = cpu_to_le16(ICE_AQ_VSI_PROP_VLAN_VALID);
+ ctxt.vsi_num = vsi->vsi_num;
+
+ status = ice_aq_update_vsi(hw, &ctxt, NULL);
+ if (status) {
+ dev_err(dev, "update VSI for VALN strip failed, ena = %d err %d aq_err %d\n",
+ ena, status, hw->adminq.sq_last_status);
+ return -EIO;
+ }
+
+ vsi->info.port_vlan_flags = ctxt.info.port_vlan_flags;
+ return 0;
+}
+
+/**
+ * ice_set_features - set the netdev feature flags
+ * @netdev: ptr to the netdev being adjusted
+ * @features: the feature set that the stack is suggesting
+ */
+static int ice_set_features(struct net_device *netdev,
+ netdev_features_t features)
+{
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct ice_vsi *vsi = np->vsi;
+ int ret = 0;
+
+ if ((features & NETIF_F_HW_VLAN_CTAG_RX) &&
+ !(netdev->features & NETIF_F_HW_VLAN_CTAG_RX))
+ ret = ice_vsi_manage_vlan_stripping(vsi, true);
+ else if (!(features & NETIF_F_HW_VLAN_CTAG_RX) &&
+ (netdev->features & NETIF_F_HW_VLAN_CTAG_RX))
+ ret = ice_vsi_manage_vlan_stripping(vsi, false);
+ else if ((features & NETIF_F_HW_VLAN_CTAG_TX) &&
+ !(netdev->features & NETIF_F_HW_VLAN_CTAG_TX))
+ ret = ice_vsi_manage_vlan_insertion(vsi);
+ else if (!(features & NETIF_F_HW_VLAN_CTAG_TX) &&
+ (netdev->features & NETIF_F_HW_VLAN_CTAG_TX))
+ ret = ice_vsi_manage_vlan_insertion(vsi);
+
+ return ret;
+}
+
+/**
+ * ice_vsi_vlan_setup - Setup vlan offload properties on a VSI
+ * @vsi: VSI to setup vlan properties for
+ */
+static int ice_vsi_vlan_setup(struct ice_vsi *vsi)
+{
+ int ret = 0;
+
+ if (vsi->netdev->features & NETIF_F_HW_VLAN_CTAG_RX)
+ ret = ice_vsi_manage_vlan_stripping(vsi, true);
+ if (vsi->netdev->features & NETIF_F_HW_VLAN_CTAG_TX)
+ ret = ice_vsi_manage_vlan_insertion(vsi);
+
+ return ret;
+}
+
+/**
+ * ice_restore_vlan - Reinstate VLANs when vsi/netdev comes back up
+ * @vsi: the VSI being brought back up
+ */
+static int ice_restore_vlan(struct ice_vsi *vsi)
+{
+ int err;
+ u16 vid;
+
+ if (!vsi->netdev)
+ return -EINVAL;
+
+ err = ice_vsi_vlan_setup(vsi);
+ if (err)
+ return err;
+
+ for_each_set_bit(vid, vsi->active_vlans, VLAN_N_VID) {
+ err = ice_vlan_rx_add_vid(vsi->netdev, htons(ETH_P_8021Q), vid);
+ if (err)
+ break;
+ }
+
+ return err;
+}
+
+/**
+ * ice_setup_tx_ctx - setup a struct ice_tlan_ctx instance
+ * @ring: The Tx ring to configure
+ * @tlan_ctx: Pointer to the Tx LAN queue context structure to be initialized
+ * @pf_q: queue index in the PF space
+ *
+ * Configure the Tx descriptor ring in TLAN context.
+ */
+static void
+ice_setup_tx_ctx(struct ice_ring *ring, struct ice_tlan_ctx *tlan_ctx, u16 pf_q)
+{
+ struct ice_vsi *vsi = ring->vsi;
+ struct ice_hw *hw = &vsi->back->hw;
+
+ tlan_ctx->base = ring->dma >> ICE_TLAN_CTX_BASE_S;
+
+ tlan_ctx->port_num = vsi->port_info->lport;
+
+ /* Transmit Queue Length */
+ tlan_ctx->qlen = ring->count;
+
+ /* PF number */
+ tlan_ctx->pf_num = hw->pf_id;
+
+ /* queue belongs to a specific VSI type
+ * VF / VM index should be programmed per vmvf_type setting:
+ * for vmvf_type = VF, it is VF number between 0-256
+ * for vmvf_type = VM, it is VM number between 0-767
+ * for PF or EMP this field should be set to zero
+ */
+ switch (vsi->type) {
+ case ICE_VSI_PF:
+ tlan_ctx->vmvf_type = ICE_TLAN_CTX_VMVF_TYPE_PF;
+ break;
+ default:
+ return;
+ }
+
+ /* make sure the context is associated with the right VSI */
+ tlan_ctx->src_vsi = vsi->vsi_num;
+
+ tlan_ctx->tso_ena = ICE_TX_LEGACY;
+ tlan_ctx->tso_qnum = pf_q;
+
+ /* Legacy or Advanced Host Interface:
+ * 0: Advanced Host Interface
+ * 1: Legacy Host Interface
+ */
+ tlan_ctx->legacy_int = ICE_TX_LEGACY;
+}
+
+/**
+ * ice_vsi_cfg_txqs - Configure the VSI for Tx
+ * @vsi: the VSI being configured
+ *
+ * Return 0 on success and a negative value on error
+ * Configure the Tx VSI for operation.
+ */
+static int ice_vsi_cfg_txqs(struct ice_vsi *vsi)
+{
+ struct ice_aqc_add_tx_qgrp *qg_buf;
+ struct ice_aqc_add_txqs_perq *txq;
+ struct ice_pf *pf = vsi->back;
+ enum ice_status status;
+ u16 buf_len, i, pf_q;
+ int err = 0, tc = 0;
+ u8 num_q_grps;
+
+ buf_len = sizeof(struct ice_aqc_add_tx_qgrp);
+ qg_buf = devm_kzalloc(&pf->pdev->dev, buf_len, GFP_KERNEL);
+ if (!qg_buf)
+ return -ENOMEM;
+
+ if (vsi->num_txq > ICE_MAX_TXQ_PER_TXQG) {
+ err = -EINVAL;
+ goto err_cfg_txqs;
+ }
+ qg_buf->num_txqs = 1;
+ num_q_grps = 1;
+
+ /* set up and configure the tx queues */
+ ice_for_each_txq(vsi, i) {
+ struct ice_tlan_ctx tlan_ctx = { 0 };
+
+ pf_q = vsi->txq_map[i];
+ ice_setup_tx_ctx(vsi->tx_rings[i], &tlan_ctx, pf_q);
+ /* copy context contents into the qg_buf */
+ qg_buf->txqs[0].txq_id = cpu_to_le16(pf_q);
+ ice_set_ctx((u8 *)&tlan_ctx, qg_buf->txqs[0].txq_ctx,
+ ice_tlan_ctx_info);
+
+ /* init queue specific tail reg. It is referred as transmit
+ * comm scheduler queue doorbell.
+ */
+ vsi->tx_rings[i]->tail = pf->hw.hw_addr + QTX_COMM_DBELL(pf_q);
+ status = ice_ena_vsi_txq(vsi->port_info, vsi->vsi_num, tc,
+ num_q_grps, qg_buf, buf_len, NULL);
+ if (status) {
+ dev_err(&vsi->back->pdev->dev,
+ "Failed to set LAN Tx queue context, error: %d\n",
+ status);
+ err = -ENODEV;
+ goto err_cfg_txqs;
+ }
+
+ /* Add Tx Queue TEID into the VSI tx ring from the response
+ * This will complete configuring and enabling the queue.
+ */
+ txq = &qg_buf->txqs[0];
+ if (pf_q == le16_to_cpu(txq->txq_id))
+ vsi->tx_rings[i]->txq_teid =
+ le32_to_cpu(txq->q_teid);
+ }
+err_cfg_txqs:
+ devm_kfree(&pf->pdev->dev, qg_buf);
+ return err;
+}
+
+/**
+ * ice_setup_rx_ctx - Configure a receive ring context
+ * @ring: The Rx ring to configure
+ *
+ * Configure the Rx descriptor ring in RLAN context.
+ */
+static int ice_setup_rx_ctx(struct ice_ring *ring)
+{
+ struct ice_vsi *vsi = ring->vsi;
+ struct ice_hw *hw = &vsi->back->hw;
+ u32 rxdid = ICE_RXDID_FLEX_NIC;
+ struct ice_rlan_ctx rlan_ctx;
+ u32 regval;
+ u16 pf_q;
+ int err;
+
+ /* what is RX queue number in global space of 2K rx queues */
+ pf_q = vsi->rxq_map[ring->q_index];
+
+ /* clear the context structure first */
+ memset(&rlan_ctx, 0, sizeof(rlan_ctx));
+
+ rlan_ctx.base = ring->dma >> 7;
+
+ rlan_ctx.qlen = ring->count;
+
+ /* Receive Packet Data Buffer Size.
+ * The Packet Data Buffer Size is defined in 128 byte units.
+ */
+ rlan_ctx.dbuf = vsi->rx_buf_len >> ICE_RLAN_CTX_DBUF_S;
+
+ /* use 32 byte descriptors */
+ rlan_ctx.dsize = 1;
+
+ /* Strip the Ethernet CRC bytes before the packet is posted to host
+ * memory.
+ */
+ rlan_ctx.crcstrip = 1;
+
+ /* L2TSEL flag defines the reported L2 Tags in the receive descriptor */
+ rlan_ctx.l2tsel = 1;
+
+ rlan_ctx.dtype = ICE_RX_DTYPE_NO_SPLIT;
+ rlan_ctx.hsplit_0 = ICE_RLAN_RX_HSPLIT_0_NO_SPLIT;
+ rlan_ctx.hsplit_1 = ICE_RLAN_RX_HSPLIT_1_NO_SPLIT;
+
+ /* This controls whether VLAN is stripped from inner headers
+ * The VLAN in the inner L2 header is stripped to the receive
+ * descriptor if enabled by this flag.
+ */
+ rlan_ctx.showiv = 0;
+
+ /* Max packet size for this queue - must not be set to a larger value
+ * than 5 x DBUF
+ */
+ rlan_ctx.rxmax = min_t(u16, vsi->max_frame,
+ ICE_MAX_CHAINED_RX_BUFS * vsi->rx_buf_len);
+
+ /* Rx queue threshold in units of 64 */
+ rlan_ctx.lrxqthresh = 1;
+
+ /* Enable Flexible Descriptors in the queue context which
+ * allows this driver to select a specific receive descriptor format
+ */
+ regval = rd32(hw, QRXFLXP_CNTXT(pf_q));
+ regval |= (rxdid << QRXFLXP_CNTXT_RXDID_IDX_S) &
+ QRXFLXP_CNTXT_RXDID_IDX_M;
+
+ /* increasing context priority to pick up profile id;
+ * default is 0x01; setting to 0x03 to ensure profile
+ * is programming if prev context is of same priority
+ */
+ regval |= (0x03 << QRXFLXP_CNTXT_RXDID_PRIO_S) &
+ QRXFLXP_CNTXT_RXDID_PRIO_M;
+
+ wr32(hw, QRXFLXP_CNTXT(pf_q), regval);
+
+ /* Absolute queue number out of 2K needs to be passed */
+ err = ice_write_rxq_ctx(hw, &rlan_ctx, pf_q);
+ if (err) {
+ dev_err(&vsi->back->pdev->dev,
+ "Failed to set LAN Rx queue context for absolute Rx queue %d error: %d\n",
+ pf_q, err);
+ return -EIO;
+ }
+
+ /* init queue specific tail register */
+ ring->tail = hw->hw_addr + QRX_TAIL(pf_q);
+ writel(0, ring->tail);
+ ice_alloc_rx_bufs(ring, ICE_DESC_UNUSED(ring));
+
+ return 0;
+}
+
+/**
+ * ice_vsi_cfg_rxqs - Configure the VSI for Rx
+ * @vsi: the VSI being configured
+ *
+ * Return 0 on success and a negative value on error
+ * Configure the Rx VSI for operation.
+ */
+static int ice_vsi_cfg_rxqs(struct ice_vsi *vsi)
+{
+ int err = 0;
+ u16 i;
+
+ if (vsi->netdev && vsi->netdev->mtu > ETH_DATA_LEN)
+ vsi->max_frame = vsi->netdev->mtu +
+ ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
+ else
+ vsi->max_frame = ICE_RXBUF_2048;
+
+ vsi->rx_buf_len = ICE_RXBUF_2048;
+ /* set up individual rings */
+ for (i = 0; i < vsi->num_rxq && !err; i++)
+ err = ice_setup_rx_ctx(vsi->rx_rings[i]);
+
+ if (err) {
+ dev_err(&vsi->back->pdev->dev, "ice_setup_rx_ctx failed\n");
+ return -EIO;
+ }
+ return err;
+}
+
+/**
+ * ice_vsi_cfg - Setup the VSI
+ * @vsi: the VSI being configured
+ *
+ * Return 0 on success and negative value on error
+ */
+static int ice_vsi_cfg(struct ice_vsi *vsi)
+{
+ int err;
+
+ ice_set_rx_mode(vsi->netdev);
+
+ err = ice_restore_vlan(vsi);
+ if (err)
+ return err;
+
+ err = ice_vsi_cfg_txqs(vsi);
+ if (!err)
+ err = ice_vsi_cfg_rxqs(vsi);
+
+ return err;
+}
+
+/**
+ * ice_vsi_stop_tx_rings - Disable Tx rings
+ * @vsi: the VSI being configured
+ */
+static int ice_vsi_stop_tx_rings(struct ice_vsi *vsi)
+{
+ struct ice_pf *pf = vsi->back;
+ struct ice_hw *hw = &pf->hw;
+ enum ice_status status;
+ u32 *q_teids, val;
+ u16 *q_ids, i;
+ int err = 0;
+
+ if (vsi->num_txq > ICE_LAN_TXQ_MAX_QDIS)
+ return -EINVAL;
+
+ q_teids = devm_kcalloc(&pf->pdev->dev, vsi->num_txq, sizeof(*q_teids),
+ GFP_KERNEL);
+ if (!q_teids)
+ return -ENOMEM;
+
+ q_ids = devm_kcalloc(&pf->pdev->dev, vsi->num_txq, sizeof(*q_ids),
+ GFP_KERNEL);
+ if (!q_ids) {
+ err = -ENOMEM;
+ goto err_alloc_q_ids;
+ }
+
+ /* set up the tx queue list to be disabled */
+ ice_for_each_txq(vsi, i) {
+ u16 v_idx;
+
+ if (!vsi->tx_rings || !vsi->tx_rings[i]) {
+ err = -EINVAL;
+ goto err_out;
+ }
+
+ q_ids[i] = vsi->txq_map[i];
+ q_teids[i] = vsi->tx_rings[i]->txq_teid;
+
+ /* clear cause_ena bit for disabled queues */
+ val = rd32(hw, QINT_TQCTL(vsi->tx_rings[i]->reg_idx));
+ val &= ~QINT_TQCTL_CAUSE_ENA_M;
+ wr32(hw, QINT_TQCTL(vsi->tx_rings[i]->reg_idx), val);
+
+ /* software is expected to wait for 100 ns */
+ ndelay(100);
+
+ /* trigger a software interrupt for the vector associated to
+ * the queue to schedule napi handler
+ */
+ v_idx = vsi->tx_rings[i]->q_vector->v_idx;
+ wr32(hw, GLINT_DYN_CTL(vsi->base_vector + v_idx),
+ GLINT_DYN_CTL_SWINT_TRIG_M | GLINT_DYN_CTL_INTENA_MSK_M);
+ }
+ status = ice_dis_vsi_txq(vsi->port_info, vsi->num_txq, q_ids, q_teids,
+ NULL);
+ if (status) {
+ dev_err(&pf->pdev->dev,
+ "Failed to disable LAN Tx queues, error: %d\n",
+ status);
+ err = -ENODEV;
+ }
+
+err_out:
+ devm_kfree(&pf->pdev->dev, q_ids);
+
+err_alloc_q_ids:
+ devm_kfree(&pf->pdev->dev, q_teids);
+
+ return err;
+}
+
+/**
+ * ice_pf_rxq_wait - Wait for a PF's Rx queue to be enabled or disabled
+ * @pf: the PF being configured
+ * @pf_q: the PF queue
+ * @ena: enable or disable state of the queue
+ *
+ * This routine will wait for the given Rx queue of the PF to reach the
+ * enabled or disabled state.
+ * Returns -ETIMEDOUT in case of failing to reach the requested state after
+ * multiple retries; else will return 0 in case of success.
+ */
+static int ice_pf_rxq_wait(struct ice_pf *pf, int pf_q, bool ena)
+{
+ int i;
+
+ for (i = 0; i < ICE_Q_WAIT_RETRY_LIMIT; i++) {
+ u32 rx_reg = rd32(&pf->hw, QRX_CTRL(pf_q));
+
+ if (ena == !!(rx_reg & QRX_CTRL_QENA_STAT_M))
+ break;
+
+ usleep_range(10, 20);
+ }
+ if (i >= ICE_Q_WAIT_RETRY_LIMIT)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+/**
+ * ice_vsi_ctrl_rx_rings - Start or stop a VSI's rx rings
+ * @vsi: the VSI being configured
+ * @ena: start or stop the rx rings
+ */
+static int ice_vsi_ctrl_rx_rings(struct ice_vsi *vsi, bool ena)
+{
+ struct ice_pf *pf = vsi->back;
+ struct ice_hw *hw = &pf->hw;
+ int i, j, ret = 0;
+
+ for (i = 0; i < vsi->num_rxq; i++) {
+ int pf_q = vsi->rxq_map[i];
+ u32 rx_reg;
+
+ for (j = 0; j < ICE_Q_WAIT_MAX_RETRY; j++) {
+ rx_reg = rd32(hw, QRX_CTRL(pf_q));
+ if (((rx_reg >> QRX_CTRL_QENA_REQ_S) & 1) ==
+ ((rx_reg >> QRX_CTRL_QENA_STAT_S) & 1))
+ break;
+ usleep_range(1000, 2000);
+ }
+
+ /* Skip if the queue is already in the requested state */
+ if (ena == !!(rx_reg & QRX_CTRL_QENA_STAT_M))
+ continue;
+
+ /* turn on/off the queue */
+ if (ena)
+ rx_reg |= QRX_CTRL_QENA_REQ_M;
+ else
+ rx_reg &= ~QRX_CTRL_QENA_REQ_M;
+ wr32(hw, QRX_CTRL(pf_q), rx_reg);
+
+ /* wait for the change to finish */
+ ret = ice_pf_rxq_wait(pf, pf_q, ena);
+ if (ret) {
+ dev_err(&pf->pdev->dev,
+ "VSI idx %d Rx ring %d %sable timeout\n",
+ vsi->idx, pf_q, (ena ? "en" : "dis"));
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * ice_vsi_start_rx_rings - start VSI's rx rings
+ * @vsi: the VSI whose rings are to be started
+ *
+ * Returns 0 on success and a negative value on error
+ */
+static int ice_vsi_start_rx_rings(struct ice_vsi *vsi)
+{
+ return ice_vsi_ctrl_rx_rings(vsi, true);
+}
+
+/**
+ * ice_vsi_stop_rx_rings - stop VSI's rx rings
+ * @vsi: the VSI
+ *
+ * Returns 0 on success and a negative value on error
+ */
+static int ice_vsi_stop_rx_rings(struct ice_vsi *vsi)
+{
+ return ice_vsi_ctrl_rx_rings(vsi, false);
+}
+
+/**
+ * ice_vsi_stop_tx_rx_rings - stop VSI's tx and rx rings
+ * @vsi: the VSI
+ * Returns 0 on success and a negative value on error
+ */
+static int ice_vsi_stop_tx_rx_rings(struct ice_vsi *vsi)
+{
+ int err_tx, err_rx;
+
+ err_tx = ice_vsi_stop_tx_rings(vsi);
+ if (err_tx)
+ dev_dbg(&vsi->back->pdev->dev, "Failed to disable Tx rings\n");
+
+ err_rx = ice_vsi_stop_rx_rings(vsi);
+ if (err_rx)
+ dev_dbg(&vsi->back->pdev->dev, "Failed to disable Rx rings\n");
+
+ if (err_tx || err_rx)
+ return -EIO;
+
+ return 0;
+}
+
+/**
+ * ice_napi_enable_all - Enable NAPI for all q_vectors in the VSI
+ * @vsi: the VSI being configured
+ */
+static void ice_napi_enable_all(struct ice_vsi *vsi)
+{
+ int q_idx;
+
+ if (!vsi->netdev)
+ return;
+
+ for (q_idx = 0; q_idx < vsi->num_q_vectors; q_idx++)
+ napi_enable(&vsi->q_vectors[q_idx]->napi);
+}
+
+/**
+ * ice_up_complete - Finish the last steps of bringing up a connection
+ * @vsi: The VSI being configured
+ *
+ * Return 0 on success and negative value on error
+ */
+static int ice_up_complete(struct ice_vsi *vsi)
+{
+ struct ice_pf *pf = vsi->back;
+ int err;
+
+ if (test_bit(ICE_FLAG_MSIX_ENA, pf->flags))
+ ice_vsi_cfg_msix(vsi);
+ else
+ return -ENOTSUPP;
+
+ /* Enable only Rx rings, Tx rings were enabled by the FW when the
+ * Tx queue group list was configured and the context bits were
+ * programmed using ice_vsi_cfg_txqs
+ */
+ err = ice_vsi_start_rx_rings(vsi);
+ if (err)
+ return err;
+
+ clear_bit(__ICE_DOWN, vsi->state);
+ ice_napi_enable_all(vsi);
+ ice_vsi_ena_irq(vsi);
+
+ if (vsi->port_info &&
+ (vsi->port_info->phy.link_info.link_info & ICE_AQ_LINK_UP) &&
+ vsi->netdev) {
+ ice_print_link_msg(vsi, true);
+ netif_tx_start_all_queues(vsi->netdev);
+ netif_carrier_on(vsi->netdev);
+ }
+
+ ice_service_task_schedule(pf);
+
+ return err;
+}
+
+/**
+ * ice_up - Bring the connection back up after being down
+ * @vsi: VSI being configured
+ */
+int ice_up(struct ice_vsi *vsi)
+{
+ int err;
+
+ err = ice_vsi_cfg(vsi);
+ if (!err)
+ err = ice_up_complete(vsi);
+
+ return err;
+}
+
+/**
+ * ice_fetch_u64_stats_per_ring - get packets and bytes stats per ring
+ * @ring: Tx or Rx ring to read stats from
+ * @pkts: packets stats counter
+ * @bytes: bytes stats counter
+ *
+ * This function fetches stats from the ring considering the atomic operations
+ * that needs to be performed to read u64 values in 32 bit machine.
+ */
+static void ice_fetch_u64_stats_per_ring(struct ice_ring *ring, u64 *pkts,
+ u64 *bytes)
+{
+ unsigned int start;
+ *pkts = 0;
+ *bytes = 0;
+
+ if (!ring)
+ return;
+ do {
+ start = u64_stats_fetch_begin_irq(&ring->syncp);
+ *pkts = ring->stats.pkts;
+ *bytes = ring->stats.bytes;
+ } while (u64_stats_fetch_retry_irq(&ring->syncp, start));
+}
+
+/**
+ * ice_stat_update40 - read 40 bit stat from the chip and update stat values
+ * @hw: ptr to the hardware info
+ * @hireg: high 32 bit HW register to read from
+ * @loreg: low 32 bit HW register to read from
+ * @prev_stat_loaded: bool to specify if previous stats are loaded
+ * @prev_stat: ptr to previous loaded stat value
+ * @cur_stat: ptr to current stat value
+ */
+static void ice_stat_update40(struct ice_hw *hw, u32 hireg, u32 loreg,
+ bool prev_stat_loaded, u64 *prev_stat,
+ u64 *cur_stat)
+{
+ u64 new_data;
+
+ new_data = rd32(hw, loreg);
+ new_data |= ((u64)(rd32(hw, hireg) & 0xFFFF)) << 32;
+
+ /* device stats are not reset at PFR, they likely will not be zeroed
+ * when the driver starts. So save the first values read and use them as
+ * offsets to be subtracted from the raw values in order to report stats
+ * that count from zero.
+ */
+ if (!prev_stat_loaded)
+ *prev_stat = new_data;
+ if (likely(new_data >= *prev_stat))
+ *cur_stat = new_data - *prev_stat;
+ else
+ /* to manage the potential roll-over */
+ *cur_stat = (new_data + BIT_ULL(40)) - *prev_stat;
+ *cur_stat &= 0xFFFFFFFFFFULL;
+}
+
+/**
+ * ice_stat_update32 - read 32 bit stat from the chip and update stat values
+ * @hw: ptr to the hardware info
+ * @reg: HW register to read from
+ * @prev_stat_loaded: bool to specify if previous stats are loaded
+ * @prev_stat: ptr to previous loaded stat value
+ * @cur_stat: ptr to current stat value
+ */
+static void ice_stat_update32(struct ice_hw *hw, u32 reg, bool prev_stat_loaded,
+ u64 *prev_stat, u64 *cur_stat)
+{
+ u32 new_data;
+
+ new_data = rd32(hw, reg);
+
+ /* device stats are not reset at PFR, they likely will not be zeroed
+ * when the driver starts. So save the first values read and use them as
+ * offsets to be subtracted from the raw values in order to report stats
+ * that count from zero.
+ */
+ if (!prev_stat_loaded)
+ *prev_stat = new_data;
+ if (likely(new_data >= *prev_stat))
+ *cur_stat = new_data - *prev_stat;
+ else
+ /* to manage the potential roll-over */
+ *cur_stat = (new_data + BIT_ULL(32)) - *prev_stat;
+}
+
+/**
+ * ice_update_eth_stats - Update VSI-specific ethernet statistics counters
+ * @vsi: the VSI to be updated
+ */
+static void ice_update_eth_stats(struct ice_vsi *vsi)
+{
+ struct ice_eth_stats *prev_es, *cur_es;
+ struct ice_hw *hw = &vsi->back->hw;
+ u16 vsi_num = vsi->vsi_num; /* HW absolute index of a VSI */
+
+ prev_es = &vsi->eth_stats_prev;
+ cur_es = &vsi->eth_stats;
+
+ ice_stat_update40(hw, GLV_GORCH(vsi_num), GLV_GORCL(vsi_num),
+ vsi->stat_offsets_loaded, &prev_es->rx_bytes,
+ &cur_es->rx_bytes);
+
+ ice_stat_update40(hw, GLV_UPRCH(vsi_num), GLV_UPRCL(vsi_num),
+ vsi->stat_offsets_loaded, &prev_es->rx_unicast,
+ &cur_es->rx_unicast);
+
+ ice_stat_update40(hw, GLV_MPRCH(vsi_num), GLV_MPRCL(vsi_num),
+ vsi->stat_offsets_loaded, &prev_es->rx_multicast,
+ &cur_es->rx_multicast);
+
+ ice_stat_update40(hw, GLV_BPRCH(vsi_num), GLV_BPRCL(vsi_num),
+ vsi->stat_offsets_loaded, &prev_es->rx_broadcast,
+ &cur_es->rx_broadcast);
+
+ ice_stat_update32(hw, GLV_RDPC(vsi_num), vsi->stat_offsets_loaded,
+ &prev_es->rx_discards, &cur_es->rx_discards);
+
+ ice_stat_update40(hw, GLV_GOTCH(vsi_num), GLV_GOTCL(vsi_num),
+ vsi->stat_offsets_loaded, &prev_es->tx_bytes,
+ &cur_es->tx_bytes);
+
+ ice_stat_update40(hw, GLV_UPTCH(vsi_num), GLV_UPTCL(vsi_num),
+ vsi->stat_offsets_loaded, &prev_es->tx_unicast,
+ &cur_es->tx_unicast);
+
+ ice_stat_update40(hw, GLV_MPTCH(vsi_num), GLV_MPTCL(vsi_num),
+ vsi->stat_offsets_loaded, &prev_es->tx_multicast,
+ &cur_es->tx_multicast);
+
+ ice_stat_update40(hw, GLV_BPTCH(vsi_num), GLV_BPTCL(vsi_num),
+ vsi->stat_offsets_loaded, &prev_es->tx_broadcast,
+ &cur_es->tx_broadcast);
+
+ ice_stat_update32(hw, GLV_TEPC(vsi_num), vsi->stat_offsets_loaded,
+ &prev_es->tx_errors, &cur_es->tx_errors);
+
+ vsi->stat_offsets_loaded = true;
+}
+
+/**
+ * ice_update_vsi_ring_stats - Update VSI stats counters
+ * @vsi: the VSI to be updated
+ */
+static void ice_update_vsi_ring_stats(struct ice_vsi *vsi)
+{
+ struct rtnl_link_stats64 *vsi_stats = &vsi->net_stats;
+ struct ice_ring *ring;
+ u64 pkts, bytes;
+ int i;
+
+ /* reset netdev stats */
+ vsi_stats->tx_packets = 0;
+ vsi_stats->tx_bytes = 0;
+ vsi_stats->rx_packets = 0;
+ vsi_stats->rx_bytes = 0;
+
+ /* reset non-netdev (extended) stats */
+ vsi->tx_restart = 0;
+ vsi->tx_busy = 0;
+ vsi->tx_linearize = 0;
+ vsi->rx_buf_failed = 0;
+ vsi->rx_page_failed = 0;
+
+ rcu_read_lock();
+
+ /* update Tx rings counters */
+ ice_for_each_txq(vsi, i) {
+ ring = READ_ONCE(vsi->tx_rings[i]);
+ ice_fetch_u64_stats_per_ring(ring, &pkts, &bytes);
+ vsi_stats->tx_packets += pkts;
+ vsi_stats->tx_bytes += bytes;
+ vsi->tx_restart += ring->tx_stats.restart_q;
+ vsi->tx_busy += ring->tx_stats.tx_busy;
+ vsi->tx_linearize += ring->tx_stats.tx_linearize;
+ }
+
+ /* update Rx rings counters */
+ ice_for_each_rxq(vsi, i) {
+ ring = READ_ONCE(vsi->rx_rings[i]);
+ ice_fetch_u64_stats_per_ring(ring, &pkts, &bytes);
+ vsi_stats->rx_packets += pkts;
+ vsi_stats->rx_bytes += bytes;
+ vsi->rx_buf_failed += ring->rx_stats.alloc_buf_failed;
+ vsi->rx_page_failed += ring->rx_stats.alloc_page_failed;
+ }
+
+ rcu_read_unlock();
+}
+
+/**
+ * ice_update_vsi_stats - Update VSI stats counters
+ * @vsi: the VSI to be updated
+ */
+static void ice_update_vsi_stats(struct ice_vsi *vsi)
+{
+ struct rtnl_link_stats64 *cur_ns = &vsi->net_stats;
+ struct ice_eth_stats *cur_es = &vsi->eth_stats;
+ struct ice_pf *pf = vsi->back;
+
+ if (test_bit(__ICE_DOWN, vsi->state) ||
+ test_bit(__ICE_CFG_BUSY, pf->state))
+ return;
+
+ /* get stats as recorded by Tx/Rx rings */
+ ice_update_vsi_ring_stats(vsi);
+
+ /* get VSI stats as recorded by the hardware */
+ ice_update_eth_stats(vsi);
+
+ cur_ns->tx_errors = cur_es->tx_errors;
+ cur_ns->rx_dropped = cur_es->rx_discards;
+ cur_ns->tx_dropped = cur_es->tx_discards;
+ cur_ns->multicast = cur_es->rx_multicast;
+
+ /* update some more netdev stats if this is main VSI */
+ if (vsi->type == ICE_VSI_PF) {
+ cur_ns->rx_crc_errors = pf->stats.crc_errors;
+ cur_ns->rx_errors = pf->stats.crc_errors +
+ pf->stats.illegal_bytes;
+ cur_ns->rx_length_errors = pf->stats.rx_len_errors;
+ }
+}
+
+/**
+ * ice_update_pf_stats - Update PF port stats counters
+ * @pf: PF whose stats needs to be updated
+ */
+static void ice_update_pf_stats(struct ice_pf *pf)
+{
+ struct ice_hw_port_stats *prev_ps, *cur_ps;
+ struct ice_hw *hw = &pf->hw;
+ u8 pf_id;
+
+ prev_ps = &pf->stats_prev;
+ cur_ps = &pf->stats;
+ pf_id = hw->pf_id;
+
+ ice_stat_update40(hw, GLPRT_GORCH(pf_id), GLPRT_GORCL(pf_id),
+ pf->stat_prev_loaded, &prev_ps->eth.rx_bytes,
+ &cur_ps->eth.rx_bytes);
+
+ ice_stat_update40(hw, GLPRT_UPRCH(pf_id), GLPRT_UPRCL(pf_id),
+ pf->stat_prev_loaded, &prev_ps->eth.rx_unicast,
+ &cur_ps->eth.rx_unicast);
+
+ ice_stat_update40(hw, GLPRT_MPRCH(pf_id), GLPRT_MPRCL(pf_id),
+ pf->stat_prev_loaded, &prev_ps->eth.rx_multicast,
+ &cur_ps->eth.rx_multicast);
+
+ ice_stat_update40(hw, GLPRT_BPRCH(pf_id), GLPRT_BPRCL(pf_id),
+ pf->stat_prev_loaded, &prev_ps->eth.rx_broadcast,
+ &cur_ps->eth.rx_broadcast);
+
+ ice_stat_update40(hw, GLPRT_GOTCH(pf_id), GLPRT_GOTCL(pf_id),
+ pf->stat_prev_loaded, &prev_ps->eth.tx_bytes,
+ &cur_ps->eth.tx_bytes);
+
+ ice_stat_update40(hw, GLPRT_UPTCH(pf_id), GLPRT_UPTCL(pf_id),
+ pf->stat_prev_loaded, &prev_ps->eth.tx_unicast,
+ &cur_ps->eth.tx_unicast);
+
+ ice_stat_update40(hw, GLPRT_MPTCH(pf_id), GLPRT_MPTCL(pf_id),
+ pf->stat_prev_loaded, &prev_ps->eth.tx_multicast,
+ &cur_ps->eth.tx_multicast);
+
+ ice_stat_update40(hw, GLPRT_BPTCH(pf_id), GLPRT_BPTCL(pf_id),
+ pf->stat_prev_loaded, &prev_ps->eth.tx_broadcast,
+ &cur_ps->eth.tx_broadcast);
+
+ ice_stat_update32(hw, GLPRT_TDOLD(pf_id), pf->stat_prev_loaded,
+ &prev_ps->tx_dropped_link_down,
+ &cur_ps->tx_dropped_link_down);
+
+ ice_stat_update40(hw, GLPRT_PRC64H(pf_id), GLPRT_PRC64L(pf_id),
+ pf->stat_prev_loaded, &prev_ps->rx_size_64,
+ &cur_ps->rx_size_64);
+
+ ice_stat_update40(hw, GLPRT_PRC127H(pf_id), GLPRT_PRC127L(pf_id),
+ pf->stat_prev_loaded, &prev_ps->rx_size_127,
+ &cur_ps->rx_size_127);
+
+ ice_stat_update40(hw, GLPRT_PRC255H(pf_id), GLPRT_PRC255L(pf_id),
+ pf->stat_prev_loaded, &prev_ps->rx_size_255,
+ &cur_ps->rx_size_255);
+
+ ice_stat_update40(hw, GLPRT_PRC511H(pf_id), GLPRT_PRC511L(pf_id),
+ pf->stat_prev_loaded, &prev_ps->rx_size_511,
+ &cur_ps->rx_size_511);
+
+ ice_stat_update40(hw, GLPRT_PRC1023H(pf_id),
+ GLPRT_PRC1023L(pf_id), pf->stat_prev_loaded,
+ &prev_ps->rx_size_1023, &cur_ps->rx_size_1023);
+
+ ice_stat_update40(hw, GLPRT_PRC1522H(pf_id),
+ GLPRT_PRC1522L(pf_id), pf->stat_prev_loaded,
+ &prev_ps->rx_size_1522, &cur_ps->rx_size_1522);
+
+ ice_stat_update40(hw, GLPRT_PRC9522H(pf_id),
+ GLPRT_PRC9522L(pf_id), pf->stat_prev_loaded,
+ &prev_ps->rx_size_big, &cur_ps->rx_size_big);
+
+ ice_stat_update40(hw, GLPRT_PTC64H(pf_id), GLPRT_PTC64L(pf_id),
+ pf->stat_prev_loaded, &prev_ps->tx_size_64,
+ &cur_ps->tx_size_64);
+
+ ice_stat_update40(hw, GLPRT_PTC127H(pf_id), GLPRT_PTC127L(pf_id),
+ pf->stat_prev_loaded, &prev_ps->tx_size_127,
+ &cur_ps->tx_size_127);
+
+ ice_stat_update40(hw, GLPRT_PTC255H(pf_id), GLPRT_PTC255L(pf_id),
+ pf->stat_prev_loaded, &prev_ps->tx_size_255,
+ &cur_ps->tx_size_255);
+
+ ice_stat_update40(hw, GLPRT_PTC511H(pf_id), GLPRT_PTC511L(pf_id),
+ pf->stat_prev_loaded, &prev_ps->tx_size_511,
+ &cur_ps->tx_size_511);
+
+ ice_stat_update40(hw, GLPRT_PTC1023H(pf_id),
+ GLPRT_PTC1023L(pf_id), pf->stat_prev_loaded,
+ &prev_ps->tx_size_1023, &cur_ps->tx_size_1023);
+
+ ice_stat_update40(hw, GLPRT_PTC1522H(pf_id),
+ GLPRT_PTC1522L(pf_id), pf->stat_prev_loaded,
+ &prev_ps->tx_size_1522, &cur_ps->tx_size_1522);
+
+ ice_stat_update40(hw, GLPRT_PTC9522H(pf_id),
+ GLPRT_PTC9522L(pf_id), pf->stat_prev_loaded,
+ &prev_ps->tx_size_big, &cur_ps->tx_size_big);
+
+ ice_stat_update32(hw, GLPRT_LXONRXC(pf_id), pf->stat_prev_loaded,
+ &prev_ps->link_xon_rx, &cur_ps->link_xon_rx);
+
+ ice_stat_update32(hw, GLPRT_LXOFFRXC(pf_id), pf->stat_prev_loaded,
+ &prev_ps->link_xoff_rx, &cur_ps->link_xoff_rx);
+
+ ice_stat_update32(hw, GLPRT_LXONTXC(pf_id), pf->stat_prev_loaded,
+ &prev_ps->link_xon_tx, &cur_ps->link_xon_tx);
+
+ ice_stat_update32(hw, GLPRT_LXOFFTXC(pf_id), pf->stat_prev_loaded,
+ &prev_ps->link_xoff_tx, &cur_ps->link_xoff_tx);
+
+ ice_stat_update32(hw, GLPRT_CRCERRS(pf_id), pf->stat_prev_loaded,
+ &prev_ps->crc_errors, &cur_ps->crc_errors);
+
+ ice_stat_update32(hw, GLPRT_ILLERRC(pf_id), pf->stat_prev_loaded,
+ &prev_ps->illegal_bytes, &cur_ps->illegal_bytes);
+
+ ice_stat_update32(hw, GLPRT_MLFC(pf_id), pf->stat_prev_loaded,
+ &prev_ps->mac_local_faults,
+ &cur_ps->mac_local_faults);
+
+ ice_stat_update32(hw, GLPRT_MRFC(pf_id), pf->stat_prev_loaded,
+ &prev_ps->mac_remote_faults,
+ &cur_ps->mac_remote_faults);
+
+ ice_stat_update32(hw, GLPRT_RLEC(pf_id), pf->stat_prev_loaded,
+ &prev_ps->rx_len_errors, &cur_ps->rx_len_errors);
+
+ ice_stat_update32(hw, GLPRT_RUC(pf_id), pf->stat_prev_loaded,
+ &prev_ps->rx_undersize, &cur_ps->rx_undersize);
+
+ ice_stat_update32(hw, GLPRT_RFC(pf_id), pf->stat_prev_loaded,
+ &prev_ps->rx_fragments, &cur_ps->rx_fragments);
+
+ ice_stat_update32(hw, GLPRT_ROC(pf_id), pf->stat_prev_loaded,
+ &prev_ps->rx_oversize, &cur_ps->rx_oversize);
+
+ ice_stat_update32(hw, GLPRT_RJC(pf_id), pf->stat_prev_loaded,
+ &prev_ps->rx_jabber, &cur_ps->rx_jabber);
+
+ pf->stat_prev_loaded = true;
+}
+
+/**
+ * ice_get_stats64 - get statistics for network device structure
+ * @netdev: network interface device structure
+ * @stats: main device statistics structure
+ */
+static
+void ice_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats)
+{
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct rtnl_link_stats64 *vsi_stats;
+ struct ice_vsi *vsi = np->vsi;
+
+ vsi_stats = &vsi->net_stats;
+
+ if (test_bit(__ICE_DOWN, vsi->state) || !vsi->num_txq || !vsi->num_rxq)
+ return;
+ /* netdev packet/byte stats come from ring counter. These are obtained
+ * by summing up ring counters (done by ice_update_vsi_ring_stats).
+ */
+ ice_update_vsi_ring_stats(vsi);
+ stats->tx_packets = vsi_stats->tx_packets;
+ stats->tx_bytes = vsi_stats->tx_bytes;
+ stats->rx_packets = vsi_stats->rx_packets;
+ stats->rx_bytes = vsi_stats->rx_bytes;
+
+ /* The rest of the stats can be read from the hardware but instead we
+ * just return values that the watchdog task has already obtained from
+ * the hardware.
+ */
+ stats->multicast = vsi_stats->multicast;
+ stats->tx_errors = vsi_stats->tx_errors;
+ stats->tx_dropped = vsi_stats->tx_dropped;
+ stats->rx_errors = vsi_stats->rx_errors;
+ stats->rx_dropped = vsi_stats->rx_dropped;
+ stats->rx_crc_errors = vsi_stats->rx_crc_errors;
+ stats->rx_length_errors = vsi_stats->rx_length_errors;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/**
+ * ice_netpoll - polling "interrupt" handler
+ * @netdev: network interface device structure
+ *
+ * Used by netconsole to send skbs without having to re-enable interrupts.
+ * This is not called in the normal interrupt path.
+ */
+static void ice_netpoll(struct net_device *netdev)
+{
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct ice_vsi *vsi = np->vsi;
+ struct ice_pf *pf = vsi->back;
+ int i;
+
+ if (test_bit(__ICE_DOWN, vsi->state) ||
+ !test_bit(ICE_FLAG_MSIX_ENA, pf->flags))
+ return;
+
+ for (i = 0; i < vsi->num_q_vectors; i++)
+ ice_msix_clean_rings(0, vsi->q_vectors[i]);
+}
+#endif /* CONFIG_NET_POLL_CONTROLLER */
+
+/**
+ * ice_napi_disable_all - Disable NAPI for all q_vectors in the VSI
+ * @vsi: VSI having NAPI disabled
+ */
+static void ice_napi_disable_all(struct ice_vsi *vsi)
+{
+ int q_idx;
+
+ if (!vsi->netdev)
+ return;
+
+ for (q_idx = 0; q_idx < vsi->num_q_vectors; q_idx++)
+ napi_disable(&vsi->q_vectors[q_idx]->napi);
+}
+
+/**
+ * ice_down - Shutdown the connection
+ * @vsi: The VSI being stopped
+ */
+int ice_down(struct ice_vsi *vsi)
+{
+ int i, err;
+
+ /* Caller of this function is expected to set the
+ * vsi->state __ICE_DOWN bit
+ */
+ if (vsi->netdev) {
+ netif_carrier_off(vsi->netdev);
+ netif_tx_disable(vsi->netdev);
+ }
+
+ ice_vsi_dis_irq(vsi);
+ err = ice_vsi_stop_tx_rx_rings(vsi);
+ ice_napi_disable_all(vsi);
+
+ ice_for_each_txq(vsi, i)
+ ice_clean_tx_ring(vsi->tx_rings[i]);
+
+ ice_for_each_rxq(vsi, i)
+ ice_clean_rx_ring(vsi->rx_rings[i]);
+
+ if (err)
+ netdev_err(vsi->netdev, "Failed to close VSI 0x%04X on switch 0x%04X\n",
+ vsi->vsi_num, vsi->vsw->sw_id);
+ return err;
+}
+
+/**
+ * ice_vsi_setup_tx_rings - Allocate VSI Tx queue resources
+ * @vsi: VSI having resources allocated
+ *
+ * Return 0 on success, negative on failure
+ */
+static int ice_vsi_setup_tx_rings(struct ice_vsi *vsi)
+{
+ int i, err;
+
+ if (!vsi->num_txq) {
+ dev_err(&vsi->back->pdev->dev, "VSI %d has 0 Tx queues\n",
+ vsi->vsi_num);
+ return -EINVAL;
+ }
+
+ ice_for_each_txq(vsi, i) {
+ err = ice_setup_tx_ring(vsi->tx_rings[i]);
+ if (err)
+ break;
+ }
+
+ return err;
+}
+
+/**
+ * ice_vsi_setup_rx_rings - Allocate VSI Rx queue resources
+ * @vsi: VSI having resources allocated
+ *
+ * Return 0 on success, negative on failure
+ */
+static int ice_vsi_setup_rx_rings(struct ice_vsi *vsi)
+{
+ int i, err;
+
+ if (!vsi->num_rxq) {
+ dev_err(&vsi->back->pdev->dev, "VSI %d has 0 Rx queues\n",
+ vsi->vsi_num);
+ return -EINVAL;
+ }
+
+ ice_for_each_rxq(vsi, i) {
+ err = ice_setup_rx_ring(vsi->rx_rings[i]);
+ if (err)
+ break;
+ }
+
+ return err;
+}
+
+/**
+ * ice_vsi_req_irq - Request IRQ from the OS
+ * @vsi: The VSI IRQ is being requested for
+ * @basename: name for the vector
+ *
+ * Return 0 on success and a negative value on error
+ */
+static int ice_vsi_req_irq(struct ice_vsi *vsi, char *basename)
+{
+ struct ice_pf *pf = vsi->back;
+ int err = -EINVAL;
+
+ if (test_bit(ICE_FLAG_MSIX_ENA, pf->flags))
+ err = ice_vsi_req_irq_msix(vsi, basename);
+
+ return err;
+}
+
+/**
+ * ice_vsi_free_tx_rings - Free Tx resources for VSI queues
+ * @vsi: the VSI having resources freed
+ */
+static void ice_vsi_free_tx_rings(struct ice_vsi *vsi)
+{
+ int i;
+
+ if (!vsi->tx_rings)
+ return;
+
+ ice_for_each_txq(vsi, i)
+ if (vsi->tx_rings[i] && vsi->tx_rings[i]->desc)
+ ice_free_tx_ring(vsi->tx_rings[i]);
+}
+
+/**
+ * ice_vsi_free_rx_rings - Free Rx resources for VSI queues
+ * @vsi: the VSI having resources freed
+ */
+static void ice_vsi_free_rx_rings(struct ice_vsi *vsi)
+{
+ int i;
+
+ if (!vsi->rx_rings)
+ return;
+
+ ice_for_each_rxq(vsi, i)
+ if (vsi->rx_rings[i] && vsi->rx_rings[i]->desc)
+ ice_free_rx_ring(vsi->rx_rings[i]);
+}
+
+/**
+ * ice_vsi_open - Called when a network interface is made active
+ * @vsi: the VSI to open
+ *
+ * Initialization of the VSI
+ *
+ * Returns 0 on success, negative value on error
+ */
+static int ice_vsi_open(struct ice_vsi *vsi)
+{
+ char int_name[ICE_INT_NAME_STR_LEN];
+ struct ice_pf *pf = vsi->back;
+ int err;
+
+ /* allocate descriptors */
+ err = ice_vsi_setup_tx_rings(vsi);
+ if (err)
+ goto err_setup_tx;
+
+ err = ice_vsi_setup_rx_rings(vsi);
+ if (err)
+ goto err_setup_rx;
+
+ err = ice_vsi_cfg(vsi);
+ if (err)
+ goto err_setup_rx;
+
+ snprintf(int_name, sizeof(int_name) - 1, "%s-%s",
+ dev_driver_string(&pf->pdev->dev), vsi->netdev->name);
+ err = ice_vsi_req_irq(vsi, int_name);
+ if (err)
+ goto err_setup_rx;
+
+ /* Notify the stack of the actual queue counts. */
+ err = netif_set_real_num_tx_queues(vsi->netdev, vsi->num_txq);
+ if (err)
+ goto err_set_qs;
+
+ err = netif_set_real_num_rx_queues(vsi->netdev, vsi->num_rxq);
+ if (err)
+ goto err_set_qs;
+
+ err = ice_up_complete(vsi);
+ if (err)
+ goto err_up_complete;
+
+ return 0;
+
+err_up_complete:
+ ice_down(vsi);
+err_set_qs:
+ ice_vsi_free_irq(vsi);
+err_setup_rx:
+ ice_vsi_free_rx_rings(vsi);
+err_setup_tx:
+ ice_vsi_free_tx_rings(vsi);
+
+ return err;
+}
+
+/**
+ * ice_vsi_close - Shut down a VSI
+ * @vsi: the VSI being shut down
+ */
+static void ice_vsi_close(struct ice_vsi *vsi)
+{
+ if (!test_and_set_bit(__ICE_DOWN, vsi->state))
+ ice_down(vsi);
+
+ ice_vsi_free_irq(vsi);
+ ice_vsi_free_tx_rings(vsi);
+ ice_vsi_free_rx_rings(vsi);
+}
+
+/**
+ * ice_rss_clean - Delete RSS related VSI structures that hold user inputs
+ * @vsi: the VSI being removed
+ */
+static void ice_rss_clean(struct ice_vsi *vsi)
+{
+ struct ice_pf *pf;
+
+ pf = vsi->back;
+
+ if (vsi->rss_hkey_user)
+ devm_kfree(&pf->pdev->dev, vsi->rss_hkey_user);
+ if (vsi->rss_lut_user)
+ devm_kfree(&pf->pdev->dev, vsi->rss_lut_user);
+}
+
+/**
+ * ice_vsi_release - Delete a VSI and free its resources
+ * @vsi: the VSI being removed
+ *
+ * Returns 0 on success or < 0 on error
+ */
+static int ice_vsi_release(struct ice_vsi *vsi)
+{
+ struct ice_pf *pf;
+
+ if (!vsi->back)
+ return -ENODEV;
+ pf = vsi->back;
+
+ if (vsi->netdev) {
+ unregister_netdev(vsi->netdev);
+ free_netdev(vsi->netdev);
+ vsi->netdev = NULL;
+ }
+
+ if (test_bit(ICE_FLAG_RSS_ENA, pf->flags))
+ ice_rss_clean(vsi);
+
+ /* Disable VSI and free resources */
+ ice_vsi_dis_irq(vsi);
+ ice_vsi_close(vsi);
+
+ /* reclaim interrupt vectors back to PF */
+ ice_free_res(vsi->back->irq_tracker, vsi->base_vector, vsi->idx);
+ pf->num_avail_msix += vsi->num_q_vectors;
+
+ ice_remove_vsi_fltr(&pf->hw, vsi->vsi_num);
+ ice_vsi_delete(vsi);
+ ice_vsi_free_q_vectors(vsi);
+ ice_vsi_clear_rings(vsi);
+
+ ice_vsi_put_qs(vsi);
+ pf->q_left_tx += vsi->alloc_txq;
+ pf->q_left_rx += vsi->alloc_rxq;
+
+ ice_vsi_clear(vsi);
+
+ return 0;
+}
+
+/**
+ * ice_dis_vsi - pause a VSI
+ * @vsi: the VSI being paused
+ */
+static void ice_dis_vsi(struct ice_vsi *vsi)
+{
+ if (test_bit(__ICE_DOWN, vsi->state))
+ return;
+
+ set_bit(__ICE_NEEDS_RESTART, vsi->state);
+
+ if (vsi->netdev && netif_running(vsi->netdev) &&
+ vsi->type == ICE_VSI_PF)
+ vsi->netdev->netdev_ops->ndo_stop(vsi->netdev);
+
+ ice_vsi_close(vsi);
+}
+
+/**
+ * ice_ena_vsi - resume a VSI
+ * @vsi: the VSI being resume
+ */
+static void ice_ena_vsi(struct ice_vsi *vsi)
+{
+ if (!test_and_clear_bit(__ICE_NEEDS_RESTART, vsi->state))
+ return;
+
+ if (vsi->netdev && netif_running(vsi->netdev))
+ vsi->netdev->netdev_ops->ndo_open(vsi->netdev);
+ else if (ice_vsi_open(vsi))
+ /* this clears the DOWN bit */
+ dev_dbg(&vsi->back->pdev->dev, "Failed open VSI 0x%04X on switch 0x%04X\n",
+ vsi->vsi_num, vsi->vsw->sw_id);
+}
+
+/**
+ * ice_pf_dis_all_vsi - Pause all VSIs on a PF
+ * @pf: the PF
+ */
+static void ice_pf_dis_all_vsi(struct ice_pf *pf)
+{
+ int v;
+
+ ice_for_each_vsi(pf, v)
+ if (pf->vsi[v])
+ ice_dis_vsi(pf->vsi[v]);
+}
+
+/**
+ * ice_pf_ena_all_vsi - Resume all VSIs on a PF
+ * @pf: the PF
+ */
+static void ice_pf_ena_all_vsi(struct ice_pf *pf)
+{
+ int v;
+
+ ice_for_each_vsi(pf, v)
+ if (pf->vsi[v])
+ ice_ena_vsi(pf->vsi[v]);
+}
+
+/**
+ * ice_rebuild - rebuild after reset
+ * @pf: pf to rebuild
+ */
+static void ice_rebuild(struct ice_pf *pf)
+{
+ struct device *dev = &pf->pdev->dev;
+ struct ice_hw *hw = &pf->hw;
+ enum ice_status ret;
+ int err;
+
+ if (test_bit(__ICE_DOWN, pf->state))
+ goto clear_recovery;
+
+ dev_dbg(dev, "rebuilding pf\n");
+
+ ret = ice_init_all_ctrlq(hw);
+ if (ret) {
+ dev_err(dev, "control queues init failed %d\n", ret);
+ goto fail_reset;
+ }
+
+ ret = ice_clear_pf_cfg(hw);
+ if (ret) {
+ dev_err(dev, "clear PF configuration failed %d\n", ret);
+ goto fail_reset;
+ }
+
+ ice_clear_pxe_mode(hw);
+
+ ret = ice_get_caps(hw);
+ if (ret) {
+ dev_err(dev, "ice_get_caps failed %d\n", ret);
+ goto fail_reset;
+ }
+
+ /* basic nic switch setup */
+ err = ice_setup_pf_sw(pf);
+ if (err) {
+ dev_err(dev, "ice_setup_pf_sw failed\n");
+ goto fail_reset;
+ }
+
+ /* start misc vector */
+ if (test_bit(ICE_FLAG_MSIX_ENA, pf->flags)) {
+ err = ice_req_irq_msix_misc(pf);
+ if (err) {
+ dev_err(dev, "misc vector setup failed: %d\n", err);
+ goto fail_reset;
+ }
+ }
+
+ /* restart the VSIs that were rebuilt and running before the reset */
+ ice_pf_ena_all_vsi(pf);
+
+ return;
+
+fail_reset:
+ ice_shutdown_all_ctrlq(hw);
+ set_bit(__ICE_RESET_FAILED, pf->state);
+clear_recovery:
+ set_bit(__ICE_RESET_RECOVERY_PENDING, pf->state);
+}
+
+/**
+ * ice_change_mtu - NDO callback to change the MTU
+ * @netdev: network interface device structure
+ * @new_mtu: new value for maximum frame size
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int ice_change_mtu(struct net_device *netdev, int new_mtu)
+{
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct ice_vsi *vsi = np->vsi;
+ struct ice_pf *pf = vsi->back;
+ u8 count = 0;
+
+ if (new_mtu == netdev->mtu) {
+ netdev_warn(netdev, "mtu is already %d\n", netdev->mtu);
+ return 0;
+ }
+
+ if (new_mtu < netdev->min_mtu) {
+ netdev_err(netdev, "new mtu invalid. min_mtu is %d\n",
+ netdev->min_mtu);
+ return -EINVAL;
+ } else if (new_mtu > netdev->max_mtu) {
+ netdev_err(netdev, "new mtu invalid. max_mtu is %d\n",
+ netdev->min_mtu);
+ return -EINVAL;
+ }
+ /* if a reset is in progress, wait for some time for it to complete */
+ do {
+ if (ice_is_reset_recovery_pending(pf->state)) {
+ count++;
+ usleep_range(1000, 2000);
+ } else {
+ break;
+ }
+
+ } while (count < 100);
+
+ if (count == 100) {
+ netdev_err(netdev, "can't change mtu. Device is busy\n");
+ return -EBUSY;
+ }
+
+ netdev->mtu = new_mtu;
+
+ /* if VSI is up, bring it down and then back up */
+ if (!test_and_set_bit(__ICE_DOWN, vsi->state)) {
+ int err;
+
+ err = ice_down(vsi);
+ if (err) {
+ netdev_err(netdev, "change mtu if_up err %d\n", err);
+ return err;
+ }
+
+ err = ice_up(vsi);
+ if (err) {
+ netdev_err(netdev, "change mtu if_up err %d\n", err);
+ return err;
+ }
+ }
+
+ netdev_dbg(netdev, "changed mtu to %d\n", new_mtu);
+ return 0;
+}
+
+/**
+ * ice_set_rss - Set RSS keys and lut
+ * @vsi: Pointer to VSI structure
+ * @seed: RSS hash seed
+ * @lut: Lookup table
+ * @lut_size: Lookup table size
+ *
+ * Returns 0 on success, negative on failure
+ */
+int ice_set_rss(struct ice_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size)
+{
+ struct ice_pf *pf = vsi->back;
+ struct ice_hw *hw = &pf->hw;
+ enum ice_status status;
+
+ if (seed) {
+ struct ice_aqc_get_set_rss_keys *buf =
+ (struct ice_aqc_get_set_rss_keys *)seed;
+
+ status = ice_aq_set_rss_key(hw, vsi->vsi_num, buf);
+
+ if (status) {
+ dev_err(&pf->pdev->dev,
+ "Cannot set RSS key, err %d aq_err %d\n",
+ status, hw->adminq.rq_last_status);
+ return -EIO;
+ }
+ }
+
+ if (lut) {
+ status = ice_aq_set_rss_lut(hw, vsi->vsi_num,
+ vsi->rss_lut_type, lut, lut_size);
+ if (status) {
+ dev_err(&pf->pdev->dev,
+ "Cannot set RSS lut, err %d aq_err %d\n",
+ status, hw->adminq.rq_last_status);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * ice_get_rss - Get RSS keys and lut
+ * @vsi: Pointer to VSI structure
+ * @seed: Buffer to store the keys
+ * @lut: Buffer to store the lookup table entries
+ * @lut_size: Size of buffer to store the lookup table entries
+ *
+ * Returns 0 on success, negative on failure
+ */
+int ice_get_rss(struct ice_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size)
+{
+ struct ice_pf *pf = vsi->back;
+ struct ice_hw *hw = &pf->hw;
+ enum ice_status status;
+
+ if (seed) {
+ struct ice_aqc_get_set_rss_keys *buf =
+ (struct ice_aqc_get_set_rss_keys *)seed;
+
+ status = ice_aq_get_rss_key(hw, vsi->vsi_num, buf);
+ if (status) {
+ dev_err(&pf->pdev->dev,
+ "Cannot get RSS key, err %d aq_err %d\n",
+ status, hw->adminq.rq_last_status);
+ return -EIO;
+ }
+ }
+
+ if (lut) {
+ status = ice_aq_get_rss_lut(hw, vsi->vsi_num,
+ vsi->rss_lut_type, lut, lut_size);
+ if (status) {
+ dev_err(&pf->pdev->dev,
+ "Cannot get RSS lut, err %d aq_err %d\n",
+ status, hw->adminq.rq_last_status);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * ice_open - Called when a network interface becomes active
+ * @netdev: network interface device structure
+ *
+ * The open entry point is called when a network interface is made
+ * active by the system (IFF_UP). At this point all resources needed
+ * for transmit and receive operations are allocated, the interrupt
+ * handler is registered with the OS, the netdev watchdog is enabled,
+ * and the stack is notified that the interface is ready.
+ *
+ * Returns 0 on success, negative value on failure
+ */
+static int ice_open(struct net_device *netdev)
+{
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct ice_vsi *vsi = np->vsi;
+ int err;
+
+ netif_carrier_off(netdev);
+
+ err = ice_vsi_open(vsi);
+
+ if (err)
+ netdev_err(netdev, "Failed to open VSI 0x%04X on switch 0x%04X\n",
+ vsi->vsi_num, vsi->vsw->sw_id);
+ return err;
+}
+
+/**
+ * ice_stop - Disables a network interface
+ * @netdev: network interface device structure
+ *
+ * The stop entry point is called when an interface is de-activated by the OS,
+ * and the netdevice enters the DOWN state. The hardware is still under the
+ * driver's control, but the netdev interface is disabled.
+ *
+ * Returns success only - not allowed to fail
+ */
+static int ice_stop(struct net_device *netdev)
+{
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct ice_vsi *vsi = np->vsi;
+
+ ice_vsi_close(vsi);
+
+ return 0;
+}
+
+/**
+ * ice_features_check - Validate encapsulated packet conforms to limits
+ * @skb: skb buffer
+ * @netdev: This port's netdev
+ * @features: Offload features that the stack believes apply
+ */
+static netdev_features_t
+ice_features_check(struct sk_buff *skb,
+ struct net_device __always_unused *netdev,
+ netdev_features_t features)
+{
+ size_t len;
+
+ /* No point in doing any of this if neither checksum nor GSO are
+ * being requested for this frame. We can rule out both by just
+ * checking for CHECKSUM_PARTIAL
+ */
+ if (skb->ip_summed != CHECKSUM_PARTIAL)
+ return features;
+
+ /* We cannot support GSO if the MSS is going to be less than
+ * 64 bytes. If it is then we need to drop support for GSO.
+ */
+ if (skb_is_gso(skb) && (skb_shinfo(skb)->gso_size < 64))
+ features &= ~NETIF_F_GSO_MASK;
+
+ len = skb_network_header(skb) - skb->data;
+ if (len & ~(ICE_TXD_MACLEN_MAX))
+ goto out_rm_features;
+
+ len = skb_transport_header(skb) - skb_network_header(skb);
+ if (len & ~(ICE_TXD_IPLEN_MAX))
+ goto out_rm_features;
+
+ if (skb->encapsulation) {
+ len = skb_inner_network_header(skb) - skb_transport_header(skb);
+ if (len & ~(ICE_TXD_L4LEN_MAX))
+ goto out_rm_features;
+
+ len = skb_inner_transport_header(skb) -
+ skb_inner_network_header(skb);
+ if (len & ~(ICE_TXD_IPLEN_MAX))
+ goto out_rm_features;
+ }
+
+ return features;
+out_rm_features:
+ return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);
+}
+
+static const struct net_device_ops ice_netdev_ops = {
+ .ndo_open = ice_open,
+ .ndo_stop = ice_stop,
+ .ndo_start_xmit = ice_start_xmit,
+ .ndo_features_check = ice_features_check,
+ .ndo_set_rx_mode = ice_set_rx_mode,
+ .ndo_set_mac_address = ice_set_mac_address,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = ice_change_mtu,
+ .ndo_get_stats64 = ice_get_stats64,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ice_netpoll,
+#endif /* CONFIG_NET_POLL_CONTROLLER */
+ .ndo_vlan_rx_add_vid = ice_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = ice_vlan_rx_kill_vid,
+ .ndo_set_features = ice_set_features,
+ .ndo_fdb_add = ice_fdb_add,
+ .ndo_fdb_del = ice_fdb_del,
+};
diff --git a/drivers/net/ethernet/intel/ice/ice_nvm.c b/drivers/net/ethernet/intel/ice/ice_nvm.c
new file mode 100644
index 000000000000..fa7a69ac92b0
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_nvm.c
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2018, Intel Corporation. */
+
+#include "ice_common.h"
+
+/**
+ * ice_aq_read_nvm
+ * @hw: pointer to the hw struct
+ * @module_typeid: module pointer location in words from the NVM beginning
+ * @offset: byte offset from the module beginning
+ * @length: length of the section to be read (in bytes from the offset)
+ * @data: command buffer (size [bytes] = length)
+ * @last_command: tells if this is the last command in a series
+ * @cd: pointer to command details structure or NULL
+ *
+ * Read the NVM using the admin queue commands (0x0701)
+ */
+static enum ice_status
+ice_aq_read_nvm(struct ice_hw *hw, u8 module_typeid, u32 offset, u16 length,
+ void *data, bool last_command, struct ice_sq_cd *cd)
+{
+ struct ice_aq_desc desc;
+ struct ice_aqc_nvm *cmd;
+
+ cmd = &desc.params.nvm;
+
+ /* In offset the highest byte must be zeroed. */
+ if (offset & 0xFF000000)
+ return ICE_ERR_PARAM;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_nvm_read);
+
+ /* If this is the last command in a series, set the proper flag. */
+ if (last_command)
+ cmd->cmd_flags |= ICE_AQC_NVM_LAST_CMD;
+ cmd->module_typeid = module_typeid;
+ cmd->offset = cpu_to_le32(offset);
+ cmd->length = cpu_to_le16(length);
+
+ return ice_aq_send_cmd(hw, &desc, data, length, cd);
+}
+
+/**
+ * ice_check_sr_access_params - verify params for Shadow RAM R/W operations.
+ * @hw: pointer to the HW structure
+ * @offset: offset in words from module start
+ * @words: number of words to access
+ */
+static enum ice_status
+ice_check_sr_access_params(struct ice_hw *hw, u32 offset, u16 words)
+{
+ if ((offset + words) > hw->nvm.sr_words) {
+ ice_debug(hw, ICE_DBG_NVM,
+ "NVM error: offset beyond SR lmt.\n");
+ return ICE_ERR_PARAM;
+ }
+
+ if (words > ICE_SR_SECTOR_SIZE_IN_WORDS) {
+ /* We can access only up to 4KB (one sector), in one AQ write */
+ ice_debug(hw, ICE_DBG_NVM,
+ "NVM error: tried to access %d words, limit is %d.\n",
+ words, ICE_SR_SECTOR_SIZE_IN_WORDS);
+ return ICE_ERR_PARAM;
+ }
+
+ if (((offset + (words - 1)) / ICE_SR_SECTOR_SIZE_IN_WORDS) !=
+ (offset / ICE_SR_SECTOR_SIZE_IN_WORDS)) {
+ /* A single access cannot spread over two sectors */
+ ice_debug(hw, ICE_DBG_NVM,
+ "NVM error: cannot spread over two sectors.\n");
+ return ICE_ERR_PARAM;
+ }
+
+ return 0;
+}
+
+/**
+ * ice_read_sr_aq - Read Shadow RAM.
+ * @hw: pointer to the HW structure
+ * @offset: offset in words from module start
+ * @words: number of words to read
+ * @data: buffer for words reads from Shadow RAM
+ * @last_command: tells the AdminQ that this is the last command
+ *
+ * Reads 16-bit word buffers from the Shadow RAM using the admin command.
+ */
+static enum ice_status
+ice_read_sr_aq(struct ice_hw *hw, u32 offset, u16 words, u16 *data,
+ bool last_command)
+{
+ enum ice_status status;
+
+ status = ice_check_sr_access_params(hw, offset, words);
+
+ /* values in "offset" and "words" parameters are sized as words
+ * (16 bits) but ice_aq_read_nvm expects these values in bytes.
+ * So do this conversion while calling ice_aq_read_nvm.
+ */
+ if (!status)
+ status = ice_aq_read_nvm(hw, 0, 2 * offset, 2 * words, data,
+ last_command, NULL);
+
+ return status;
+}
+
+/**
+ * ice_read_sr_word_aq - Reads Shadow RAM via AQ
+ * @hw: pointer to the HW structure
+ * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF)
+ * @data: word read from the Shadow RAM
+ *
+ * Reads one 16 bit word from the Shadow RAM using the ice_read_sr_aq method.
+ */
+static enum ice_status
+ice_read_sr_word_aq(struct ice_hw *hw, u16 offset, u16 *data)
+{
+ enum ice_status status;
+
+ status = ice_read_sr_aq(hw, offset, 1, data, true);
+ if (!status)
+ *data = le16_to_cpu(*(__le16 *)data);
+
+ return status;
+}
+
+/**
+ * ice_acquire_nvm - Generic request for acquiring the NVM ownership
+ * @hw: pointer to the HW structure
+ * @access: NVM access type (read or write)
+ *
+ * This function will request NVM ownership.
+ */
+static enum
+ice_status ice_acquire_nvm(struct ice_hw *hw,
+ enum ice_aq_res_access_type access)
+{
+ if (hw->nvm.blank_nvm_mode)
+ return 0;
+
+ return ice_acquire_res(hw, ICE_NVM_RES_ID, access);
+}
+
+/**
+ * ice_release_nvm - Generic request for releasing the NVM ownership
+ * @hw: pointer to the HW structure
+ *
+ * This function will release NVM ownership.
+ */
+static void ice_release_nvm(struct ice_hw *hw)
+{
+ if (hw->nvm.blank_nvm_mode)
+ return;
+
+ ice_release_res(hw, ICE_NVM_RES_ID);
+}
+
+/**
+ * ice_read_sr_word - Reads Shadow RAM word and acquire NVM if necessary
+ * @hw: pointer to the HW structure
+ * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF)
+ * @data: word read from the Shadow RAM
+ *
+ * Reads one 16 bit word from the Shadow RAM using the ice_read_sr_word_aq.
+ */
+static enum ice_status
+ice_read_sr_word(struct ice_hw *hw, u16 offset, u16 *data)
+{
+ enum ice_status status;
+
+ status = ice_acquire_nvm(hw, ICE_RES_READ);
+ if (!status) {
+ status = ice_read_sr_word_aq(hw, offset, data);
+ ice_release_nvm(hw);
+ }
+
+ return status;
+}
+
+/**
+ * ice_init_nvm - initializes NVM setting
+ * @hw: pointer to the hw struct
+ *
+ * This function reads and populates NVM settings such as Shadow RAM size,
+ * max_timeout, and blank_nvm_mode
+ */
+enum ice_status ice_init_nvm(struct ice_hw *hw)
+{
+ struct ice_nvm_info *nvm = &hw->nvm;
+ u16 eetrack_lo, eetrack_hi;
+ enum ice_status status = 0;
+ u32 fla, gens_stat;
+ u8 sr_size;
+
+ /* The SR size is stored regardless of the nvm programming mode
+ * as the blank mode may be used in the factory line.
+ */
+ gens_stat = rd32(hw, GLNVM_GENS);
+ sr_size = (gens_stat & GLNVM_GENS_SR_SIZE_M) >> GLNVM_GENS_SR_SIZE_S;
+
+ /* Switching to words (sr_size contains power of 2) */
+ nvm->sr_words = BIT(sr_size) * ICE_SR_WORDS_IN_1KB;
+
+ /* Check if we are in the normal or blank NVM programming mode */
+ fla = rd32(hw, GLNVM_FLA);
+ if (fla & GLNVM_FLA_LOCKED_M) { /* Normal programming mode */
+ nvm->blank_nvm_mode = false;
+ } else { /* Blank programming mode */
+ nvm->blank_nvm_mode = true;
+ status = ICE_ERR_NVM_BLANK_MODE;
+ ice_debug(hw, ICE_DBG_NVM,
+ "NVM init error: unsupported blank mode.\n");
+ return status;
+ }
+
+ status = ice_read_sr_word(hw, ICE_SR_NVM_DEV_STARTER_VER, &hw->nvm.ver);
+ if (status) {
+ ice_debug(hw, ICE_DBG_INIT,
+ "Failed to read DEV starter version.\n");
+ return status;
+ }
+
+ status = ice_read_sr_word(hw, ICE_SR_NVM_EETRACK_LO, &eetrack_lo);
+ if (status) {
+ ice_debug(hw, ICE_DBG_INIT, "Failed to read EETRACK lo.\n");
+ return status;
+ }
+ status = ice_read_sr_word(hw, ICE_SR_NVM_EETRACK_HI, &eetrack_hi);
+ if (status) {
+ ice_debug(hw, ICE_DBG_INIT, "Failed to read EETRACK hi.\n");
+ return status;
+ }
+
+ hw->nvm.eetrack = (eetrack_hi << 16) | eetrack_lo;
+
+ return status;
+}
diff --git a/drivers/net/ethernet/intel/ice/ice_osdep.h b/drivers/net/ethernet/intel/ice/ice_osdep.h
new file mode 100644
index 000000000000..f57c414bc0a9
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_osdep.h
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018, Intel Corporation. */
+
+#ifndef _ICE_OSDEP_H_
+#define _ICE_OSDEP_H_
+
+#include <linux/types.h>
+#include <linux/io.h>
+#ifndef CONFIG_64BIT
+#include <linux/io-64-nonatomic-lo-hi.h>
+#endif
+
+#define wr32(a, reg, value) writel((value), ((a)->hw_addr + (reg)))
+#define rd32(a, reg) readl((a)->hw_addr + (reg))
+#define wr64(a, reg, value) writeq((value), ((a)->hw_addr + (reg)))
+#define rd64(a, reg) readq((a)->hw_addr + (reg))
+
+#define ice_flush(a) rd32((a), GLGEN_STAT)
+#define ICE_M(m, s) ((m) << (s))
+
+struct ice_dma_mem {
+ void *va;
+ dma_addr_t pa;
+ size_t size;
+};
+
+#define ice_hw_to_dev(ptr) \
+ (&(container_of((ptr), struct ice_pf, hw))->pdev->dev)
+
+#ifdef CONFIG_DYNAMIC_DEBUG
+#define ice_debug(hw, type, fmt, args...) \
+ dev_dbg(ice_hw_to_dev(hw), fmt, ##args)
+
+#define ice_debug_array(hw, type, rowsize, groupsize, buf, len) \
+ print_hex_dump_debug(KBUILD_MODNAME " ", \
+ DUMP_PREFIX_OFFSET, rowsize, \
+ groupsize, buf, len, false)
+#else
+#define ice_debug(hw, type, fmt, args...) \
+do { \
+ if ((type) & (hw)->debug_mask) \
+ dev_info(ice_hw_to_dev(hw), fmt, ##args); \
+} while (0)
+
+#ifdef DEBUG
+#define ice_debug_array(hw, type, rowsize, groupsize, buf, len) \
+do { \
+ if ((type) & (hw)->debug_mask) \
+ print_hex_dump_debug(KBUILD_MODNAME, \
+ DUMP_PREFIX_OFFSET, \
+ rowsize, groupsize, buf, \
+ len, false); \
+} while (0)
+#else
+#define ice_debug_array(hw, type, rowsize, groupsize, buf, len) \
+do { \
+ struct ice_hw *hw_l = hw; \
+ if ((type) & (hw_l)->debug_mask) { \
+ u16 len_l = len; \
+ u8 *buf_l = buf; \
+ int i; \
+ for (i = 0; i < (len_l - 16); i += 16) \
+ ice_debug(hw_l, type, "0x%04X %16ph\n",\
+ i, ((buf_l) + i)); \
+ if (i < len_l) \
+ ice_debug(hw_l, type, "0x%04X %*ph\n", \
+ i, ((len_l) - i), ((buf_l) + i));\
+ } \
+} while (0)
+#endif /* DEBUG */
+#endif /* CONFIG_DYNAMIC_DEBUG */
+
+#endif /* _ICE_OSDEP_H_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_sched.c b/drivers/net/ethernet/intel/ice/ice_sched.c
new file mode 100644
index 000000000000..2e6c1d92cc88
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_sched.c
@@ -0,0 +1,1659 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2018, Intel Corporation. */
+
+#include "ice_sched.h"
+
+/**
+ * ice_sched_add_root_node - Insert the Tx scheduler root node in SW DB
+ * @pi: port information structure
+ * @info: Scheduler element information from firmware
+ *
+ * This function inserts the root node of the scheduling tree topology
+ * to the SW DB.
+ */
+static enum ice_status
+ice_sched_add_root_node(struct ice_port_info *pi,
+ struct ice_aqc_txsched_elem_data *info)
+{
+ struct ice_sched_node *root;
+ struct ice_hw *hw;
+ u16 max_children;
+
+ if (!pi)
+ return ICE_ERR_PARAM;
+
+ hw = pi->hw;
+
+ root = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*root), GFP_KERNEL);
+ if (!root)
+ return ICE_ERR_NO_MEMORY;
+
+ max_children = le16_to_cpu(hw->layer_info[0].max_children);
+ root->children = devm_kcalloc(ice_hw_to_dev(hw), max_children,
+ sizeof(*root), GFP_KERNEL);
+ if (!root->children) {
+ devm_kfree(ice_hw_to_dev(hw), root);
+ return ICE_ERR_NO_MEMORY;
+ }
+
+ memcpy(&root->info, info, sizeof(*info));
+ pi->root = root;
+ return 0;
+}
+
+/**
+ * ice_sched_find_node_by_teid - Find the Tx scheduler node in SW DB
+ * @start_node: pointer to the starting ice_sched_node struct in a sub-tree
+ * @teid: node teid to search
+ *
+ * This function searches for a node matching the teid in the scheduling tree
+ * from the SW DB. The search is recursive and is restricted by the number of
+ * layers it has searched through; stopping at the max supported layer.
+ *
+ * This function needs to be called when holding the port_info->sched_lock
+ */
+struct ice_sched_node *
+ice_sched_find_node_by_teid(struct ice_sched_node *start_node, u32 teid)
+{
+ u16 i;
+
+ /* The TEID is same as that of the start_node */
+ if (ICE_TXSCHED_GET_NODE_TEID(start_node) == teid)
+ return start_node;
+
+ /* The node has no children or is at the max layer */
+ if (!start_node->num_children ||
+ start_node->tx_sched_layer >= ICE_AQC_TOPO_MAX_LEVEL_NUM ||
+ start_node->info.data.elem_type == ICE_AQC_ELEM_TYPE_LEAF)
+ return NULL;
+
+ /* Check if teid matches to any of the children nodes */
+ for (i = 0; i < start_node->num_children; i++)
+ if (ICE_TXSCHED_GET_NODE_TEID(start_node->children[i]) == teid)
+ return start_node->children[i];
+
+ /* Search within each child's sub-tree */
+ for (i = 0; i < start_node->num_children; i++) {
+ struct ice_sched_node *tmp;
+
+ tmp = ice_sched_find_node_by_teid(start_node->children[i],
+ teid);
+ if (tmp)
+ return tmp;
+ }
+
+ return NULL;
+}
+
+/**
+ * ice_sched_add_node - Insert the Tx scheduler node in SW DB
+ * @pi: port information structure
+ * @layer: Scheduler layer of the node
+ * @info: Scheduler element information from firmware
+ *
+ * This function inserts a scheduler node to the SW DB.
+ */
+enum ice_status
+ice_sched_add_node(struct ice_port_info *pi, u8 layer,
+ struct ice_aqc_txsched_elem_data *info)
+{
+ struct ice_sched_node *parent;
+ struct ice_sched_node *node;
+ struct ice_hw *hw;
+ u16 max_children;
+
+ if (!pi)
+ return ICE_ERR_PARAM;
+
+ hw = pi->hw;
+
+ /* A valid parent node should be there */
+ parent = ice_sched_find_node_by_teid(pi->root,
+ le32_to_cpu(info->parent_teid));
+ if (!parent) {
+ ice_debug(hw, ICE_DBG_SCHED,
+ "Parent Node not found for parent_teid=0x%x\n",
+ le32_to_cpu(info->parent_teid));
+ return ICE_ERR_PARAM;
+ }
+
+ node = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*node), GFP_KERNEL);
+ if (!node)
+ return ICE_ERR_NO_MEMORY;
+ max_children = le16_to_cpu(hw->layer_info[layer].max_children);
+ if (max_children) {
+ node->children = devm_kcalloc(ice_hw_to_dev(hw), max_children,
+ sizeof(*node), GFP_KERNEL);
+ if (!node->children) {
+ devm_kfree(ice_hw_to_dev(hw), node);
+ return ICE_ERR_NO_MEMORY;
+ }
+ }
+
+ node->in_use = true;
+ node->parent = parent;
+ node->tx_sched_layer = layer;
+ parent->children[parent->num_children++] = node;
+ memcpy(&node->info, info, sizeof(*info));
+ return 0;
+}
+
+/**
+ * ice_aq_delete_sched_elems - delete scheduler elements
+ * @hw: pointer to the hw struct
+ * @grps_req: number of groups to delete
+ * @buf: pointer to buffer
+ * @buf_size: buffer size in bytes
+ * @grps_del: returns total number of elements deleted
+ * @cd: pointer to command details structure or NULL
+ *
+ * Delete scheduling elements (0x040F)
+ */
+static enum ice_status
+ice_aq_delete_sched_elems(struct ice_hw *hw, u16 grps_req,
+ struct ice_aqc_delete_elem *buf, u16 buf_size,
+ u16 *grps_del, struct ice_sq_cd *cd)
+{
+ struct ice_aqc_add_move_delete_elem *cmd;
+ struct ice_aq_desc desc;
+ enum ice_status status;
+
+ cmd = &desc.params.add_move_delete_elem;
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_delete_sched_elems);
+ desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD);
+ cmd->num_grps_req = cpu_to_le16(grps_req);
+
+ status = ice_aq_send_cmd(hw, &desc, buf, buf_size, cd);
+ if (!status && grps_del)
+ *grps_del = le16_to_cpu(cmd->num_grps_updated);
+
+ return status;
+}
+
+/**
+ * ice_sched_remove_elems - remove nodes from hw
+ * @hw: pointer to the hw struct
+ * @parent: pointer to the parent node
+ * @num_nodes: number of nodes
+ * @node_teids: array of node teids to be deleted
+ *
+ * This function remove nodes from hw
+ */
+static enum ice_status
+ice_sched_remove_elems(struct ice_hw *hw, struct ice_sched_node *parent,
+ u16 num_nodes, u32 *node_teids)
+{
+ struct ice_aqc_delete_elem *buf;
+ u16 i, num_groups_removed = 0;
+ enum ice_status status;
+ u16 buf_size;
+
+ buf_size = sizeof(*buf) + sizeof(u32) * (num_nodes - 1);
+ buf = devm_kzalloc(ice_hw_to_dev(hw), buf_size, GFP_KERNEL);
+ if (!buf)
+ return ICE_ERR_NO_MEMORY;
+ buf->hdr.parent_teid = parent->info.node_teid;
+ buf->hdr.num_elems = cpu_to_le16(num_nodes);
+ for (i = 0; i < num_nodes; i++)
+ buf->teid[i] = cpu_to_le32(node_teids[i]);
+ status = ice_aq_delete_sched_elems(hw, 1, buf, buf_size,
+ &num_groups_removed, NULL);
+ if (status || num_groups_removed != 1)
+ ice_debug(hw, ICE_DBG_SCHED, "remove elements failed\n");
+ devm_kfree(ice_hw_to_dev(hw), buf);
+ return status;
+}
+
+/**
+ * ice_sched_get_first_node - get the first node of the given layer
+ * @hw: pointer to the hw struct
+ * @parent: pointer the base node of the subtree
+ * @layer: layer number
+ *
+ * This function retrieves the first node of the given layer from the subtree
+ */
+static struct ice_sched_node *
+ice_sched_get_first_node(struct ice_hw *hw, struct ice_sched_node *parent,
+ u8 layer)
+{
+ u8 i;
+
+ if (layer < hw->sw_entry_point_layer)
+ return NULL;
+ for (i = 0; i < parent->num_children; i++) {
+ struct ice_sched_node *node = parent->children[i];
+
+ if (node) {
+ if (node->tx_sched_layer == layer)
+ return node;
+ /* this recursion is intentional, and wouldn't
+ * go more than 9 calls
+ */
+ return ice_sched_get_first_node(hw, node, layer);
+ }
+ }
+ return NULL;
+}
+
+/**
+ * ice_sched_get_tc_node - get pointer to TC node
+ * @pi: port information structure
+ * @tc: TC number
+ *
+ * This function returns the TC node pointer
+ */
+struct ice_sched_node *ice_sched_get_tc_node(struct ice_port_info *pi, u8 tc)
+{
+ u8 i;
+
+ if (!pi)
+ return NULL;
+ for (i = 0; i < pi->root->num_children; i++)
+ if (pi->root->children[i]->tc_num == tc)
+ return pi->root->children[i];
+ return NULL;
+}
+
+/**
+ * ice_free_sched_node - Free a Tx scheduler node from SW DB
+ * @pi: port information structure
+ * @node: pointer to the ice_sched_node struct
+ *
+ * This function frees up a node from SW DB as well as from HW
+ *
+ * This function needs to be called with the port_info->sched_lock held
+ */
+void ice_free_sched_node(struct ice_port_info *pi, struct ice_sched_node *node)
+{
+ struct ice_sched_node *parent;
+ struct ice_hw *hw = pi->hw;
+ u8 i, j;
+
+ /* Free the children before freeing up the parent node
+ * The parent array is updated below and that shifts the nodes
+ * in the array. So always pick the first child if num children > 0
+ */
+ while (node->num_children)
+ ice_free_sched_node(pi, node->children[0]);
+
+ /* Leaf, TC and root nodes can't be deleted by SW */
+ if (node->tx_sched_layer >= hw->sw_entry_point_layer &&
+ node->info.data.elem_type != ICE_AQC_ELEM_TYPE_TC &&
+ node->info.data.elem_type != ICE_AQC_ELEM_TYPE_ROOT_PORT &&
+ node->info.data.elem_type != ICE_AQC_ELEM_TYPE_LEAF) {
+ u32 teid = le32_to_cpu(node->info.node_teid);
+ enum ice_status status;
+
+ status = ice_sched_remove_elems(hw, node->parent, 1, &teid);
+ if (status)
+ ice_debug(hw, ICE_DBG_SCHED,
+ "remove element failed %d\n", status);
+ }
+ parent = node->parent;
+ /* root has no parent */
+ if (parent) {
+ struct ice_sched_node *p, *tc_node;
+
+ /* update the parent */
+ for (i = 0; i < parent->num_children; i++)
+ if (parent->children[i] == node) {
+ for (j = i + 1; j < parent->num_children; j++)
+ parent->children[j - 1] =
+ parent->children[j];
+ parent->num_children--;
+ break;
+ }
+
+ /* search for previous sibling that points to this node and
+ * remove the reference
+ */
+ tc_node = ice_sched_get_tc_node(pi, node->tc_num);
+ if (!tc_node) {
+ ice_debug(hw, ICE_DBG_SCHED,
+ "Invalid TC number %d\n", node->tc_num);
+ goto err_exit;
+ }
+ p = ice_sched_get_first_node(hw, tc_node, node->tx_sched_layer);
+ while (p) {
+ if (p->sibling == node) {
+ p->sibling = node->sibling;
+ break;
+ }
+ p = p->sibling;
+ }
+ }
+err_exit:
+ /* leaf nodes have no children */
+ if (node->children)
+ devm_kfree(ice_hw_to_dev(hw), node->children);
+ devm_kfree(ice_hw_to_dev(hw), node);
+}
+
+/**
+ * ice_aq_get_dflt_topo - gets default scheduler topology
+ * @hw: pointer to the hw struct
+ * @lport: logical port number
+ * @buf: pointer to buffer
+ * @buf_size: buffer size in bytes
+ * @num_branches: returns total number of queue to port branches
+ * @cd: pointer to command details structure or NULL
+ *
+ * Get default scheduler topology (0x400)
+ */
+static enum ice_status
+ice_aq_get_dflt_topo(struct ice_hw *hw, u8 lport,
+ struct ice_aqc_get_topo_elem *buf, u16 buf_size,
+ u8 *num_branches, struct ice_sq_cd *cd)
+{
+ struct ice_aqc_get_topo *cmd;
+ struct ice_aq_desc desc;
+ enum ice_status status;
+
+ cmd = &desc.params.get_topo;
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_dflt_topo);
+ cmd->port_num = lport;
+ status = ice_aq_send_cmd(hw, &desc, buf, buf_size, cd);
+ if (!status && num_branches)
+ *num_branches = cmd->num_branches;
+
+ return status;
+}
+
+/**
+ * ice_aq_add_sched_elems - adds scheduling element
+ * @hw: pointer to the hw struct
+ * @grps_req: the number of groups that are requested to be added
+ * @buf: pointer to buffer
+ * @buf_size: buffer size in bytes
+ * @grps_added: returns total number of groups added
+ * @cd: pointer to command details structure or NULL
+ *
+ * Add scheduling elements (0x0401)
+ */
+static enum ice_status
+ice_aq_add_sched_elems(struct ice_hw *hw, u16 grps_req,
+ struct ice_aqc_add_elem *buf, u16 buf_size,
+ u16 *grps_added, struct ice_sq_cd *cd)
+{
+ struct ice_aqc_add_move_delete_elem *cmd;
+ struct ice_aq_desc desc;
+ enum ice_status status;
+
+ cmd = &desc.params.add_move_delete_elem;
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_add_sched_elems);
+ desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD);
+
+ cmd->num_grps_req = cpu_to_le16(grps_req);
+ status = ice_aq_send_cmd(hw, &desc, buf, buf_size, cd);
+ if (!status && grps_added)
+ *grps_added = le16_to_cpu(cmd->num_grps_updated);
+
+ return status;
+}
+
+/**
+ * ice_suspend_resume_elems - suspend/resume scheduler elements
+ * @hw: pointer to the hw struct
+ * @elems_req: number of elements to suspend
+ * @buf: pointer to buffer
+ * @buf_size: buffer size in bytes
+ * @elems_ret: returns total number of elements suspended
+ * @cd: pointer to command details structure or NULL
+ * @cmd_code: command code for suspend or resume
+ *
+ * suspend/resume scheduler elements
+ */
+static enum ice_status
+ice_suspend_resume_elems(struct ice_hw *hw, u16 elems_req,
+ struct ice_aqc_suspend_resume_elem *buf, u16 buf_size,
+ u16 *elems_ret, struct ice_sq_cd *cd,
+ enum ice_adminq_opc cmd_code)
+{
+ struct ice_aqc_get_cfg_elem *cmd;
+ struct ice_aq_desc desc;
+ enum ice_status status;
+
+ cmd = &desc.params.get_update_elem;
+ ice_fill_dflt_direct_cmd_desc(&desc, cmd_code);
+ cmd->num_elem_req = cpu_to_le16(elems_req);
+ desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD);
+ status = ice_aq_send_cmd(hw, &desc, buf, buf_size, cd);
+ if (!status && elems_ret)
+ *elems_ret = le16_to_cpu(cmd->num_elem_resp);
+ return status;
+}
+
+/**
+ * ice_aq_suspend_sched_elems - suspend scheduler elements
+ * @hw: pointer to the hw struct
+ * @elems_req: number of elements to suspend
+ * @buf: pointer to buffer
+ * @buf_size: buffer size in bytes
+ * @elems_ret: returns total number of elements suspended
+ * @cd: pointer to command details structure or NULL
+ *
+ * Suspend scheduling elements (0x0409)
+ */
+static enum ice_status
+ice_aq_suspend_sched_elems(struct ice_hw *hw, u16 elems_req,
+ struct ice_aqc_suspend_resume_elem *buf,
+ u16 buf_size, u16 *elems_ret, struct ice_sq_cd *cd)
+{
+ return ice_suspend_resume_elems(hw, elems_req, buf, buf_size, elems_ret,
+ cd, ice_aqc_opc_suspend_sched_elems);
+}
+
+/**
+ * ice_aq_resume_sched_elems - resume scheduler elements
+ * @hw: pointer to the hw struct
+ * @elems_req: number of elements to resume
+ * @buf: pointer to buffer
+ * @buf_size: buffer size in bytes
+ * @elems_ret: returns total number of elements resumed
+ * @cd: pointer to command details structure or NULL
+ *
+ * resume scheduling elements (0x040A)
+ */
+static enum ice_status
+ice_aq_resume_sched_elems(struct ice_hw *hw, u16 elems_req,
+ struct ice_aqc_suspend_resume_elem *buf,
+ u16 buf_size, u16 *elems_ret, struct ice_sq_cd *cd)
+{
+ return ice_suspend_resume_elems(hw, elems_req, buf, buf_size, elems_ret,
+ cd, ice_aqc_opc_resume_sched_elems);
+}
+
+/**
+ * ice_aq_query_sched_res - query scheduler resource
+ * @hw: pointer to the hw struct
+ * @buf_size: buffer size in bytes
+ * @buf: pointer to buffer
+ * @cd: pointer to command details structure or NULL
+ *
+ * Query scheduler resource allocation (0x0412)
+ */
+static enum ice_status
+ice_aq_query_sched_res(struct ice_hw *hw, u16 buf_size,
+ struct ice_aqc_query_txsched_res_resp *buf,
+ struct ice_sq_cd *cd)
+{
+ struct ice_aq_desc desc;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_query_sched_res);
+ return ice_aq_send_cmd(hw, &desc, buf, buf_size, cd);
+}
+
+/**
+ * ice_sched_suspend_resume_elems - suspend or resume hw nodes
+ * @hw: pointer to the hw struct
+ * @num_nodes: number of nodes
+ * @node_teids: array of node teids to be suspended or resumed
+ * @suspend: true means suspend / false means resume
+ *
+ * This function suspends or resumes hw nodes
+ */
+static enum ice_status
+ice_sched_suspend_resume_elems(struct ice_hw *hw, u8 num_nodes, u32 *node_teids,
+ bool suspend)
+{
+ struct ice_aqc_suspend_resume_elem *buf;
+ u16 i, buf_size, num_elem_ret = 0;
+ enum ice_status status;
+
+ buf_size = sizeof(*buf) * num_nodes;
+ buf = devm_kzalloc(ice_hw_to_dev(hw), buf_size, GFP_KERNEL);
+ if (!buf)
+ return ICE_ERR_NO_MEMORY;
+
+ for (i = 0; i < num_nodes; i++)
+ buf->teid[i] = cpu_to_le32(node_teids[i]);
+
+ if (suspend)
+ status = ice_aq_suspend_sched_elems(hw, num_nodes, buf,
+ buf_size, &num_elem_ret,
+ NULL);
+ else
+ status = ice_aq_resume_sched_elems(hw, num_nodes, buf,
+ buf_size, &num_elem_ret,
+ NULL);
+ if (status || num_elem_ret != num_nodes)
+ ice_debug(hw, ICE_DBG_SCHED, "suspend/resume failed\n");
+
+ devm_kfree(ice_hw_to_dev(hw), buf);
+ return status;
+}
+
+/**
+ * ice_sched_clear_tx_topo - clears the schduler tree nodes
+ * @pi: port information structure
+ *
+ * This function removes all the nodes from HW as well as from SW DB.
+ */
+static void ice_sched_clear_tx_topo(struct ice_port_info *pi)
+{
+ struct ice_sched_agg_info *agg_info;
+ struct ice_sched_vsi_info *vsi_elem;
+ struct ice_sched_agg_info *atmp;
+ struct ice_sched_vsi_info *tmp;
+ struct ice_hw *hw;
+
+ if (!pi)
+ return;
+
+ hw = pi->hw;
+
+ list_for_each_entry_safe(agg_info, atmp, &pi->agg_list, list_entry) {
+ struct ice_sched_agg_vsi_info *agg_vsi_info;
+ struct ice_sched_agg_vsi_info *vtmp;
+
+ list_for_each_entry_safe(agg_vsi_info, vtmp,
+ &agg_info->agg_vsi_list, list_entry) {
+ list_del(&agg_vsi_info->list_entry);
+ devm_kfree(ice_hw_to_dev(hw), agg_vsi_info);
+ }
+ }
+
+ /* remove the vsi list */
+ list_for_each_entry_safe(vsi_elem, tmp, &pi->vsi_info_list,
+ list_entry) {
+ list_del(&vsi_elem->list_entry);
+ devm_kfree(ice_hw_to_dev(hw), vsi_elem);
+ }
+
+ if (pi->root) {
+ ice_free_sched_node(pi, pi->root);
+ pi->root = NULL;
+ }
+}
+
+/**
+ * ice_sched_clear_port - clear the scheduler elements from SW DB for a port
+ * @pi: port information structure
+ *
+ * Cleanup scheduling elements from SW DB
+ */
+static void ice_sched_clear_port(struct ice_port_info *pi)
+{
+ if (!pi || pi->port_state != ICE_SCHED_PORT_STATE_READY)
+ return;
+
+ pi->port_state = ICE_SCHED_PORT_STATE_INIT;
+ mutex_lock(&pi->sched_lock);
+ ice_sched_clear_tx_topo(pi);
+ mutex_unlock(&pi->sched_lock);
+ mutex_destroy(&pi->sched_lock);
+}
+
+/**
+ * ice_sched_cleanup_all - cleanup scheduler elements from SW DB for all ports
+ * @hw: pointer to the hw struct
+ *
+ * Cleanup scheduling elements from SW DB for all the ports
+ */
+void ice_sched_cleanup_all(struct ice_hw *hw)
+{
+ if (!hw || !hw->port_info)
+ return;
+
+ if (hw->layer_info)
+ devm_kfree(ice_hw_to_dev(hw), hw->layer_info);
+
+ ice_sched_clear_port(hw->port_info);
+
+ hw->num_tx_sched_layers = 0;
+ hw->num_tx_sched_phys_layers = 0;
+ hw->flattened_layers = 0;
+ hw->max_cgds = 0;
+}
+
+/**
+ * ice_sched_create_vsi_info_entry - create an empty new VSI entry
+ * @pi: port information structure
+ * @vsi_id: VSI Id
+ *
+ * This function creates a new VSI entry and adds it to list
+ */
+static struct ice_sched_vsi_info *
+ice_sched_create_vsi_info_entry(struct ice_port_info *pi, u16 vsi_id)
+{
+ struct ice_sched_vsi_info *vsi_elem;
+
+ if (!pi)
+ return NULL;
+
+ vsi_elem = devm_kzalloc(ice_hw_to_dev(pi->hw), sizeof(*vsi_elem),
+ GFP_KERNEL);
+ if (!vsi_elem)
+ return NULL;
+
+ list_add(&vsi_elem->list_entry, &pi->vsi_info_list);
+ vsi_elem->vsi_id = vsi_id;
+ return vsi_elem;
+}
+
+/**
+ * ice_sched_add_elems - add nodes to hw and SW DB
+ * @pi: port information structure
+ * @tc_node: pointer to the branch node
+ * @parent: pointer to the parent node
+ * @layer: layer number to add nodes
+ * @num_nodes: number of nodes
+ * @num_nodes_added: pointer to num nodes added
+ * @first_node_teid: if new nodes are added then return the teid of first node
+ *
+ * This function add nodes to hw as well as to SW DB for a given layer
+ */
+static enum ice_status
+ice_sched_add_elems(struct ice_port_info *pi, struct ice_sched_node *tc_node,
+ struct ice_sched_node *parent, u8 layer, u16 num_nodes,
+ u16 *num_nodes_added, u32 *first_node_teid)
+{
+ struct ice_sched_node *prev, *new_node;
+ struct ice_aqc_add_elem *buf;
+ u16 i, num_groups_added = 0;
+ enum ice_status status = 0;
+ struct ice_hw *hw = pi->hw;
+ u16 buf_size;
+ u32 teid;
+
+ buf_size = sizeof(*buf) + sizeof(*buf->generic) * (num_nodes - 1);
+ buf = devm_kzalloc(ice_hw_to_dev(hw), buf_size, GFP_KERNEL);
+ if (!buf)
+ return ICE_ERR_NO_MEMORY;
+
+ buf->hdr.parent_teid = parent->info.node_teid;
+ buf->hdr.num_elems = cpu_to_le16(num_nodes);
+ for (i = 0; i < num_nodes; i++) {
+ buf->generic[i].parent_teid = parent->info.node_teid;
+ buf->generic[i].data.elem_type = ICE_AQC_ELEM_TYPE_SE_GENERIC;
+ buf->generic[i].data.valid_sections =
+ ICE_AQC_ELEM_VALID_GENERIC | ICE_AQC_ELEM_VALID_CIR |
+ ICE_AQC_ELEM_VALID_EIR;
+ buf->generic[i].data.generic = 0;
+ buf->generic[i].data.cir_bw.bw_profile_idx =
+ ICE_SCHED_DFLT_RL_PROF_ID;
+ buf->generic[i].data.eir_bw.bw_profile_idx =
+ ICE_SCHED_DFLT_RL_PROF_ID;
+ }
+
+ status = ice_aq_add_sched_elems(hw, 1, buf, buf_size,
+ &num_groups_added, NULL);
+ if (status || num_groups_added != 1) {
+ ice_debug(hw, ICE_DBG_SCHED, "add elements failed\n");
+ devm_kfree(ice_hw_to_dev(hw), buf);
+ return ICE_ERR_CFG;
+ }
+
+ *num_nodes_added = num_nodes;
+ /* add nodes to the SW DB */
+ for (i = 0; i < num_nodes; i++) {
+ status = ice_sched_add_node(pi, layer, &buf->generic[i]);
+ if (status) {
+ ice_debug(hw, ICE_DBG_SCHED,
+ "add nodes in SW DB failed status =%d\n",
+ status);
+ break;
+ }
+
+ teid = le32_to_cpu(buf->generic[i].node_teid);
+ new_node = ice_sched_find_node_by_teid(parent, teid);
+
+ if (!new_node) {
+ ice_debug(hw, ICE_DBG_SCHED,
+ "Node is missing for teid =%d\n", teid);
+ break;
+ }
+
+ new_node->sibling = NULL;
+ new_node->tc_num = tc_node->tc_num;
+
+ /* add it to previous node sibling pointer */
+ /* Note: siblings are not linked across branches */
+ prev = ice_sched_get_first_node(hw, tc_node, layer);
+
+ if (prev && prev != new_node) {
+ while (prev->sibling)
+ prev = prev->sibling;
+ prev->sibling = new_node;
+ }
+
+ if (i == 0)
+ *first_node_teid = teid;
+ }
+
+ devm_kfree(ice_hw_to_dev(hw), buf);
+ return status;
+}
+
+/**
+ * ice_sched_add_nodes_to_layer - Add nodes to a given layer
+ * @pi: port information structure
+ * @tc_node: pointer to TC node
+ * @parent: pointer to parent node
+ * @layer: layer number to add nodes
+ * @num_nodes: number of nodes to be added
+ * @first_node_teid: pointer to the first node teid
+ * @num_nodes_added: pointer to number of nodes added
+ *
+ * This function add nodes to a given layer.
+ */
+static enum ice_status
+ice_sched_add_nodes_to_layer(struct ice_port_info *pi,
+ struct ice_sched_node *tc_node,
+ struct ice_sched_node *parent, u8 layer,
+ u16 num_nodes, u32 *first_node_teid,
+ u16 *num_nodes_added)
+{
+ u32 *first_teid_ptr = first_node_teid;
+ u16 new_num_nodes, max_child_nodes;
+ enum ice_status status = 0;
+ struct ice_hw *hw = pi->hw;
+ u16 num_added = 0;
+ u32 temp;
+
+ *num_nodes_added = 0;
+
+ if (!num_nodes)
+ return status;
+
+ if (!parent || layer < hw->sw_entry_point_layer)
+ return ICE_ERR_PARAM;
+
+ /* max children per node per layer */
+ max_child_nodes =
+ le16_to_cpu(hw->layer_info[parent->tx_sched_layer].max_children);
+
+ /* current number of children + required nodes exceed max children ? */
+ if ((parent->num_children + num_nodes) > max_child_nodes) {
+ /* Fail if the parent is a TC node */
+ if (parent == tc_node)
+ return ICE_ERR_CFG;
+
+ /* utilize all the spaces if the parent is not full */
+ if (parent->num_children < max_child_nodes) {
+ new_num_nodes = max_child_nodes - parent->num_children;
+ /* this recursion is intentional, and wouldn't
+ * go more than 2 calls
+ */
+ status = ice_sched_add_nodes_to_layer(pi, tc_node,
+ parent, layer,
+ new_num_nodes,
+ first_node_teid,
+ &num_added);
+ if (status)
+ return status;
+
+ *num_nodes_added += num_added;
+ }
+ /* Don't modify the first node teid memory if the first node was
+ * added already in the above call. Instead send some temp
+ * memory for all other recursive calls.
+ */
+ if (num_added)
+ first_teid_ptr = &temp;
+
+ new_num_nodes = num_nodes - num_added;
+
+ /* This parent is full, try the next sibling */
+ parent = parent->sibling;
+
+ /* this recursion is intentional, for 1024 queues
+ * per VSI, it goes max of 16 iterations.
+ * 1024 / 8 = 128 layer 8 nodes
+ * 128 /8 = 16 (add 8 nodes per iteration)
+ */
+ status = ice_sched_add_nodes_to_layer(pi, tc_node, parent,
+ layer, new_num_nodes,
+ first_teid_ptr,
+ &num_added);
+ *num_nodes_added += num_added;
+ return status;
+ }
+
+ status = ice_sched_add_elems(pi, tc_node, parent, layer, num_nodes,
+ num_nodes_added, first_node_teid);
+ return status;
+}
+
+/**
+ * ice_sched_get_qgrp_layer - get the current queue group layer number
+ * @hw: pointer to the hw struct
+ *
+ * This function returns the current queue group layer number
+ */
+static u8 ice_sched_get_qgrp_layer(struct ice_hw *hw)
+{
+ /* It's always total layers - 1, the array is 0 relative so -2 */
+ return hw->num_tx_sched_layers - ICE_QGRP_LAYER_OFFSET;
+}
+
+/**
+ * ice_sched_get_vsi_layer - get the current VSI layer number
+ * @hw: pointer to the hw struct
+ *
+ * This function returns the current VSI layer number
+ */
+static u8 ice_sched_get_vsi_layer(struct ice_hw *hw)
+{
+ /* Num Layers VSI layer
+ * 9 6
+ * 7 4
+ * 5 or less sw_entry_point_layer
+ */
+ /* calculate the vsi layer based on number of layers. */
+ if (hw->num_tx_sched_layers > ICE_VSI_LAYER_OFFSET + 1) {
+ u8 layer = hw->num_tx_sched_layers - ICE_VSI_LAYER_OFFSET;
+
+ if (layer > hw->sw_entry_point_layer)
+ return layer;
+ }
+ return hw->sw_entry_point_layer;
+}
+
+/**
+ * ice_sched_get_num_nodes_per_layer - Get the total number of nodes per layer
+ * @pi: pointer to the port info struct
+ * @layer: layer number
+ *
+ * This function calculates the number of nodes present in the scheduler tree
+ * including all the branches for a given layer
+ */
+static u16
+ice_sched_get_num_nodes_per_layer(struct ice_port_info *pi, u8 layer)
+{
+ struct ice_hw *hw;
+ u16 num_nodes = 0;
+ u8 i;
+
+ if (!pi)
+ return num_nodes;
+
+ hw = pi->hw;
+
+ /* Calculate the number of nodes for all TCs */
+ for (i = 0; i < pi->root->num_children; i++) {
+ struct ice_sched_node *tc_node, *node;
+
+ tc_node = pi->root->children[i];
+
+ /* Get the first node */
+ node = ice_sched_get_first_node(hw, tc_node, layer);
+ if (!node)
+ continue;
+
+ /* count the siblings */
+ while (node) {
+ num_nodes++;
+ node = node->sibling;
+ }
+ }
+
+ return num_nodes;
+}
+
+/**
+ * ice_sched_val_max_nodes - check max number of nodes reached or not
+ * @pi: port information structure
+ * @new_num_nodes_per_layer: pointer to the new number of nodes array
+ *
+ * This function checks whether the scheduler tree layers have enough space to
+ * add new nodes
+ */
+static enum ice_status
+ice_sched_validate_for_max_nodes(struct ice_port_info *pi,
+ u16 *new_num_nodes_per_layer)
+{
+ struct ice_hw *hw = pi->hw;
+ u8 i, qg_layer;
+ u16 num_nodes;
+
+ qg_layer = ice_sched_get_qgrp_layer(hw);
+
+ /* walk through all the layers from SW entry point to qgroup layer */
+ for (i = hw->sw_entry_point_layer; i <= qg_layer; i++) {
+ num_nodes = ice_sched_get_num_nodes_per_layer(pi, i);
+ if (num_nodes + new_num_nodes_per_layer[i] >
+ le16_to_cpu(hw->layer_info[i].max_pf_nodes)) {
+ ice_debug(hw, ICE_DBG_SCHED,
+ "max nodes reached for layer = %d\n", i);
+ return ICE_ERR_CFG;
+ }
+ }
+ return 0;
+}
+
+/**
+ * ice_rm_dflt_leaf_node - remove the default leaf node in the tree
+ * @pi: port information structure
+ *
+ * This function removes the leaf node that was created by the FW
+ * during initialization
+ */
+static void
+ice_rm_dflt_leaf_node(struct ice_port_info *pi)
+{
+ struct ice_sched_node *node;
+
+ node = pi->root;
+ while (node) {
+ if (!node->num_children)
+ break;
+ node = node->children[0];
+ }
+ if (node && node->info.data.elem_type == ICE_AQC_ELEM_TYPE_LEAF) {
+ u32 teid = le32_to_cpu(node->info.node_teid);
+ enum ice_status status;
+
+ /* remove the default leaf node */
+ status = ice_sched_remove_elems(pi->hw, node->parent, 1, &teid);
+ if (!status)
+ ice_free_sched_node(pi, node);
+ }
+}
+
+/**
+ * ice_sched_rm_dflt_nodes - free the default nodes in the tree
+ * @pi: port information structure
+ *
+ * This function frees all the nodes except root and TC that were created by
+ * the FW during initialization
+ */
+static void
+ice_sched_rm_dflt_nodes(struct ice_port_info *pi)
+{
+ struct ice_sched_node *node;
+
+ ice_rm_dflt_leaf_node(pi);
+
+ /* remove the default nodes except TC and root nodes */
+ node = pi->root;
+ while (node) {
+ if (node->tx_sched_layer >= pi->hw->sw_entry_point_layer &&
+ node->info.data.elem_type != ICE_AQC_ELEM_TYPE_TC &&
+ node->info.data.elem_type != ICE_AQC_ELEM_TYPE_ROOT_PORT) {
+ ice_free_sched_node(pi, node);
+ break;
+ }
+
+ if (!node->num_children)
+ break;
+ node = node->children[0];
+ }
+}
+
+/**
+ * ice_sched_init_port - Initialize scheduler by querying information from FW
+ * @pi: port info structure for the tree to cleanup
+ *
+ * This function is the initial call to find the total number of Tx scheduler
+ * resources, default topology created by firmware and storing the information
+ * in SW DB.
+ */
+enum ice_status ice_sched_init_port(struct ice_port_info *pi)
+{
+ struct ice_aqc_get_topo_elem *buf;
+ enum ice_status status;
+ struct ice_hw *hw;
+ u8 num_branches;
+ u16 num_elems;
+ u8 i, j;
+
+ if (!pi)
+ return ICE_ERR_PARAM;
+ hw = pi->hw;
+
+ /* Query the Default Topology from FW */
+ buf = devm_kcalloc(ice_hw_to_dev(hw), ICE_TXSCHED_MAX_BRANCHES,
+ sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return ICE_ERR_NO_MEMORY;
+
+ /* Query default scheduling tree topology */
+ status = ice_aq_get_dflt_topo(hw, pi->lport, buf,
+ sizeof(*buf) * ICE_TXSCHED_MAX_BRANCHES,
+ &num_branches, NULL);
+ if (status)
+ goto err_init_port;
+
+ /* num_branches should be between 1-8 */
+ if (num_branches < 1 || num_branches > ICE_TXSCHED_MAX_BRANCHES) {
+ ice_debug(hw, ICE_DBG_SCHED, "num_branches unexpected %d\n",
+ num_branches);
+ status = ICE_ERR_PARAM;
+ goto err_init_port;
+ }
+
+ /* get the number of elements on the default/first branch */
+ num_elems = le16_to_cpu(buf[0].hdr.num_elems);
+
+ /* num_elems should always be between 1-9 */
+ if (num_elems < 1 || num_elems > ICE_AQC_TOPO_MAX_LEVEL_NUM) {
+ ice_debug(hw, ICE_DBG_SCHED, "num_elems unexpected %d\n",
+ num_elems);
+ status = ICE_ERR_PARAM;
+ goto err_init_port;
+ }
+
+ /* If the last node is a leaf node then the index of the Q group
+ * layer is two less than the number of elements.
+ */
+ if (num_elems > 2 && buf[0].generic[num_elems - 1].data.elem_type ==
+ ICE_AQC_ELEM_TYPE_LEAF)
+ pi->last_node_teid =
+ le32_to_cpu(buf[0].generic[num_elems - 2].node_teid);
+ else
+ pi->last_node_teid =
+ le32_to_cpu(buf[0].generic[num_elems - 1].node_teid);
+
+ /* Insert the Tx Sched root node */
+ status = ice_sched_add_root_node(pi, &buf[0].generic[0]);
+ if (status)
+ goto err_init_port;
+
+ /* Parse the default tree and cache the information */
+ for (i = 0; i < num_branches; i++) {
+ num_elems = le16_to_cpu(buf[i].hdr.num_elems);
+
+ /* Skip root element as already inserted */
+ for (j = 1; j < num_elems; j++) {
+ /* update the sw entry point */
+ if (buf[0].generic[j].data.elem_type ==
+ ICE_AQC_ELEM_TYPE_ENTRY_POINT)
+ hw->sw_entry_point_layer = j;
+
+ status = ice_sched_add_node(pi, j, &buf[i].generic[j]);
+ if (status)
+ goto err_init_port;
+ }
+ }
+
+ /* Remove the default nodes. */
+ if (pi->root)
+ ice_sched_rm_dflt_nodes(pi);
+
+ /* initialize the port for handling the scheduler tree */
+ pi->port_state = ICE_SCHED_PORT_STATE_READY;
+ mutex_init(&pi->sched_lock);
+ INIT_LIST_HEAD(&pi->agg_list);
+ INIT_LIST_HEAD(&pi->vsi_info_list);
+
+err_init_port:
+ if (status && pi->root) {
+ ice_free_sched_node(pi, pi->root);
+ pi->root = NULL;
+ }
+
+ devm_kfree(ice_hw_to_dev(hw), buf);
+ return status;
+}
+
+/**
+ * ice_sched_query_res_alloc - query the FW for num of logical sched layers
+ * @hw: pointer to the HW struct
+ *
+ * query FW for allocated scheduler resources and store in HW struct
+ */
+enum ice_status ice_sched_query_res_alloc(struct ice_hw *hw)
+{
+ struct ice_aqc_query_txsched_res_resp *buf;
+ enum ice_status status = 0;
+
+ if (hw->layer_info)
+ return status;
+
+ buf = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return ICE_ERR_NO_MEMORY;
+
+ status = ice_aq_query_sched_res(hw, sizeof(*buf), buf, NULL);
+ if (status)
+ goto sched_query_out;
+
+ hw->num_tx_sched_layers = le16_to_cpu(buf->sched_props.logical_levels);
+ hw->num_tx_sched_phys_layers =
+ le16_to_cpu(buf->sched_props.phys_levels);
+ hw->flattened_layers = buf->sched_props.flattening_bitmap;
+ hw->max_cgds = buf->sched_props.max_pf_cgds;
+
+ hw->layer_info = devm_kmemdup(ice_hw_to_dev(hw), buf->layer_props,
+ (hw->num_tx_sched_layers *
+ sizeof(*hw->layer_info)),
+ GFP_KERNEL);
+ if (!hw->layer_info) {
+ status = ICE_ERR_NO_MEMORY;
+ goto sched_query_out;
+ }
+
+sched_query_out:
+ devm_kfree(ice_hw_to_dev(hw), buf);
+ return status;
+}
+
+/**
+ * ice_sched_get_vsi_info_entry - Get the vsi entry list for given vsi_id
+ * @pi: port information structure
+ * @vsi_id: vsi id
+ *
+ * This function retrieves the vsi list for the given vsi id
+ */
+static struct ice_sched_vsi_info *
+ice_sched_get_vsi_info_entry(struct ice_port_info *pi, u16 vsi_id)
+{
+ struct ice_sched_vsi_info *list_elem;
+
+ if (!pi)
+ return NULL;
+
+ list_for_each_entry(list_elem, &pi->vsi_info_list, list_entry)
+ if (list_elem->vsi_id == vsi_id)
+ return list_elem;
+ return NULL;
+}
+
+/**
+ * ice_sched_find_node_in_subtree - Find node in part of base node subtree
+ * @hw: pointer to the hw struct
+ * @base: pointer to the base node
+ * @node: pointer to the node to search
+ *
+ * This function checks whether a given node is part of the base node
+ * subtree or not
+ */
+static bool
+ice_sched_find_node_in_subtree(struct ice_hw *hw, struct ice_sched_node *base,
+ struct ice_sched_node *node)
+{
+ u8 i;
+
+ for (i = 0; i < base->num_children; i++) {
+ struct ice_sched_node *child = base->children[i];
+
+ if (node == child)
+ return true;
+
+ if (child->tx_sched_layer > node->tx_sched_layer)
+ return false;
+
+ /* this recursion is intentional, and wouldn't
+ * go more than 8 calls
+ */
+ if (ice_sched_find_node_in_subtree(hw, child, node))
+ return true;
+ }
+ return false;
+}
+
+/**
+ * ice_sched_get_free_qparent - Get a free lan or rdma q group node
+ * @pi: port information structure
+ * @vsi_id: vsi id
+ * @tc: branch number
+ * @owner: lan or rdma
+ *
+ * This function retrieves a free lan or rdma q group node
+ */
+struct ice_sched_node *
+ice_sched_get_free_qparent(struct ice_port_info *pi, u16 vsi_id, u8 tc,
+ u8 owner)
+{
+ struct ice_sched_node *vsi_node, *qgrp_node = NULL;
+ struct ice_sched_vsi_info *list_elem;
+ u16 max_children;
+ u8 qgrp_layer;
+
+ qgrp_layer = ice_sched_get_qgrp_layer(pi->hw);
+ max_children = le16_to_cpu(pi->hw->layer_info[qgrp_layer].max_children);
+
+ list_elem = ice_sched_get_vsi_info_entry(pi, vsi_id);
+ if (!list_elem)
+ goto lan_q_exit;
+
+ vsi_node = list_elem->vsi_node[tc];
+
+ /* validate invalid VSI id */
+ if (!vsi_node)
+ goto lan_q_exit;
+
+ /* get the first q group node from VSI sub-tree */
+ qgrp_node = ice_sched_get_first_node(pi->hw, vsi_node, qgrp_layer);
+ while (qgrp_node) {
+ /* make sure the qgroup node is part of the VSI subtree */
+ if (ice_sched_find_node_in_subtree(pi->hw, vsi_node, qgrp_node))
+ if (qgrp_node->num_children < max_children &&
+ qgrp_node->owner == owner)
+ break;
+ qgrp_node = qgrp_node->sibling;
+ }
+
+lan_q_exit:
+ return qgrp_node;
+}
+
+/**
+ * ice_sched_get_vsi_node - Get a VSI node based on VSI id
+ * @hw: pointer to the hw struct
+ * @tc_node: pointer to the TC node
+ * @vsi_id: VSI id
+ *
+ * This function retrieves a VSI node for a given VSI id from a given
+ * TC branch
+ */
+static struct ice_sched_node *
+ice_sched_get_vsi_node(struct ice_hw *hw, struct ice_sched_node *tc_node,
+ u16 vsi_id)
+{
+ struct ice_sched_node *node;
+ u8 vsi_layer;
+
+ vsi_layer = ice_sched_get_vsi_layer(hw);
+ node = ice_sched_get_first_node(hw, tc_node, vsi_layer);
+
+ /* Check whether it already exists */
+ while (node) {
+ if (node->vsi_id == vsi_id)
+ return node;
+ node = node->sibling;
+ }
+
+ return node;
+}
+
+/**
+ * ice_sched_calc_vsi_child_nodes - calculate number of VSI child nodes
+ * @hw: pointer to the hw struct
+ * @num_qs: number of queues
+ * @num_nodes: num nodes array
+ *
+ * This function calculates the number of VSI child nodes based on the
+ * number of queues.
+ */
+static void
+ice_sched_calc_vsi_child_nodes(struct ice_hw *hw, u16 num_qs, u16 *num_nodes)
+{
+ u16 num = num_qs;
+ u8 i, qgl, vsil;
+
+ qgl = ice_sched_get_qgrp_layer(hw);
+ vsil = ice_sched_get_vsi_layer(hw);
+
+ /* calculate num nodes from q group to VSI layer */
+ for (i = qgl; i > vsil; i--) {
+ u16 max_children = le16_to_cpu(hw->layer_info[i].max_children);
+
+ /* round to the next integer if there is a remainder */
+ num = DIV_ROUND_UP(num, max_children);
+
+ /* need at least one node */
+ num_nodes[i] = num ? num : 1;
+ }
+}
+
+/**
+ * ice_sched_add_vsi_child_nodes - add VSI child nodes to tree
+ * @pi: port information structure
+ * @vsi_id: VSI id
+ * @tc_node: pointer to the TC node
+ * @num_nodes: pointer to the num nodes that needs to be added per layer
+ * @owner: node owner (lan or rdma)
+ *
+ * This function adds the VSI child nodes to tree. It gets called for
+ * lan and rdma separately.
+ */
+static enum ice_status
+ice_sched_add_vsi_child_nodes(struct ice_port_info *pi, u16 vsi_id,
+ struct ice_sched_node *tc_node, u16 *num_nodes,
+ u8 owner)
+{
+ struct ice_sched_node *parent, *node;
+ struct ice_hw *hw = pi->hw;
+ enum ice_status status;
+ u32 first_node_teid;
+ u16 num_added = 0;
+ u8 i, qgl, vsil;
+
+ status = ice_sched_validate_for_max_nodes(pi, num_nodes);
+ if (status)
+ return status;
+
+ qgl = ice_sched_get_qgrp_layer(hw);
+ vsil = ice_sched_get_vsi_layer(hw);
+ parent = ice_sched_get_vsi_node(hw, tc_node, vsi_id);
+ for (i = vsil + 1; i <= qgl; i++) {
+ if (!parent)
+ return ICE_ERR_CFG;
+ status = ice_sched_add_nodes_to_layer(pi, tc_node, parent, i,
+ num_nodes[i],
+ &first_node_teid,
+ &num_added);
+ if (status || num_nodes[i] != num_added)
+ return ICE_ERR_CFG;
+
+ /* The newly added node can be a new parent for the next
+ * layer nodes
+ */
+ if (num_added) {
+ parent = ice_sched_find_node_by_teid(tc_node,
+ first_node_teid);
+ node = parent;
+ while (node) {
+ node->owner = owner;
+ node = node->sibling;
+ }
+ } else {
+ parent = parent->children[0];
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * ice_sched_rm_vsi_child_nodes - remove VSI child nodes from the tree
+ * @pi: port information structure
+ * @vsi_node: pointer to the VSI node
+ * @num_nodes: pointer to the num nodes that needs to be removed per layer
+ * @owner: node owner (lan or rdma)
+ *
+ * This function removes the VSI child nodes from the tree. It gets called for
+ * lan and rdma separately.
+ */
+static void
+ice_sched_rm_vsi_child_nodes(struct ice_port_info *pi,
+ struct ice_sched_node *vsi_node, u16 *num_nodes,
+ u8 owner)
+{
+ struct ice_sched_node *node, *next;
+ u8 i, qgl, vsil;
+ u16 num;
+
+ qgl = ice_sched_get_qgrp_layer(pi->hw);
+ vsil = ice_sched_get_vsi_layer(pi->hw);
+
+ for (i = qgl; i > vsil; i--) {
+ num = num_nodes[i];
+ node = ice_sched_get_first_node(pi->hw, vsi_node, i);
+ while (node && num) {
+ next = node->sibling;
+ if (node->owner == owner && !node->num_children) {
+ ice_free_sched_node(pi, node);
+ num--;
+ }
+ node = next;
+ }
+ }
+}
+
+/**
+ * ice_sched_calc_vsi_support_nodes - calculate number of VSI support nodes
+ * @hw: pointer to the hw struct
+ * @tc_node: pointer to TC node
+ * @num_nodes: pointer to num nodes array
+ *
+ * This function calculates the number of supported nodes needed to add this
+ * VSI into tx tree including the VSI, parent and intermediate nodes in below
+ * layers
+ */
+static void
+ice_sched_calc_vsi_support_nodes(struct ice_hw *hw,
+ struct ice_sched_node *tc_node, u16 *num_nodes)
+{
+ struct ice_sched_node *node;
+ u16 max_child;
+ u8 i, vsil;
+
+ vsil = ice_sched_get_vsi_layer(hw);
+ for (i = vsil; i >= hw->sw_entry_point_layer; i--)
+ /* Add intermediate nodes if TC has no children and
+ * need at least one node for VSI
+ */
+ if (!tc_node->num_children || i == vsil) {
+ num_nodes[i]++;
+ } else {
+ /* If intermediate nodes are reached max children
+ * then add a new one.
+ */
+ node = ice_sched_get_first_node(hw, tc_node, i);
+ max_child = le16_to_cpu(hw->layer_info[i].max_children);
+
+ /* scan all the siblings */
+ while (node) {
+ if (node->num_children < max_child)
+ break;
+ node = node->sibling;
+ }
+
+ /* all the nodes are full, allocate a new one */
+ if (!node)
+ num_nodes[i]++;
+ }
+}
+
+/**
+ * ice_sched_add_vsi_support_nodes - add VSI supported nodes into tx tree
+ * @pi: port information structure
+ * @vsi_id: VSI Id
+ * @tc_node: pointer to TC node
+ * @num_nodes: pointer to num nodes array
+ *
+ * This function adds the VSI supported nodes into tx tree including the
+ * VSI, its parent and intermediate nodes in below layers
+ */
+static enum ice_status
+ice_sched_add_vsi_support_nodes(struct ice_port_info *pi, u16 vsi_id,
+ struct ice_sched_node *tc_node, u16 *num_nodes)
+{
+ struct ice_sched_node *parent = tc_node;
+ enum ice_status status;
+ u32 first_node_teid;
+ u16 num_added = 0;
+ u8 i, vsil;
+
+ if (!pi)
+ return ICE_ERR_PARAM;
+
+ status = ice_sched_validate_for_max_nodes(pi, num_nodes);
+ if (status)
+ return status;
+
+ vsil = ice_sched_get_vsi_layer(pi->hw);
+ for (i = pi->hw->sw_entry_point_layer; i <= vsil; i++) {
+ status = ice_sched_add_nodes_to_layer(pi, tc_node, parent,
+ i, num_nodes[i],
+ &first_node_teid,
+ &num_added);
+ if (status || num_nodes[i] != num_added)
+ return ICE_ERR_CFG;
+
+ /* The newly added node can be a new parent for the next
+ * layer nodes
+ */
+ if (num_added)
+ parent = ice_sched_find_node_by_teid(tc_node,
+ first_node_teid);
+ else
+ parent = parent->children[0];
+
+ if (!parent)
+ return ICE_ERR_CFG;
+
+ if (i == vsil)
+ parent->vsi_id = vsi_id;
+ }
+ return 0;
+}
+
+/**
+ * ice_sched_add_vsi_to_topo - add a new VSI into tree
+ * @pi: port information structure
+ * @vsi_id: VSI Id
+ * @tc: TC number
+ *
+ * This function adds a new VSI into scheduler tree
+ */
+static enum ice_status
+ice_sched_add_vsi_to_topo(struct ice_port_info *pi, u16 vsi_id, u8 tc)
+{
+ u16 num_nodes[ICE_AQC_TOPO_MAX_LEVEL_NUM] = { 0 };
+ struct ice_sched_node *tc_node;
+ struct ice_hw *hw = pi->hw;
+
+ tc_node = ice_sched_get_tc_node(pi, tc);
+ if (!tc_node)
+ return ICE_ERR_PARAM;
+
+ /* calculate number of supported nodes needed for this VSI */
+ ice_sched_calc_vsi_support_nodes(hw, tc_node, num_nodes);
+
+ /* add vsi supported nodes to tc subtree */
+ return ice_sched_add_vsi_support_nodes(pi, vsi_id, tc_node, num_nodes);
+}
+
+/**
+ * ice_sched_update_vsi_child_nodes - update VSI child nodes
+ * @pi: port information structure
+ * @vsi_id: VSI Id
+ * @tc: TC number
+ * @new_numqs: new number of max queues
+ * @owner: owner of this subtree
+ *
+ * This function updates the VSI child nodes based on the number of queues
+ */
+static enum ice_status
+ice_sched_update_vsi_child_nodes(struct ice_port_info *pi, u16 vsi_id, u8 tc,
+ u16 new_numqs, u8 owner)
+{
+ u16 prev_num_nodes[ICE_AQC_TOPO_MAX_LEVEL_NUM] = { 0 };
+ u16 new_num_nodes[ICE_AQC_TOPO_MAX_LEVEL_NUM] = { 0 };
+ struct ice_sched_node *vsi_node;
+ struct ice_sched_node *tc_node;
+ struct ice_sched_vsi_info *vsi;
+ enum ice_status status = 0;
+ struct ice_hw *hw = pi->hw;
+ u16 prev_numqs;
+ u8 i;
+
+ tc_node = ice_sched_get_tc_node(pi, tc);
+ if (!tc_node)
+ return ICE_ERR_CFG;
+
+ vsi_node = ice_sched_get_vsi_node(hw, tc_node, vsi_id);
+ if (!vsi_node)
+ return ICE_ERR_CFG;
+
+ vsi = ice_sched_get_vsi_info_entry(pi, vsi_id);
+ if (!vsi)
+ return ICE_ERR_CFG;
+
+ if (owner == ICE_SCHED_NODE_OWNER_LAN)
+ prev_numqs = vsi->max_lanq[tc];
+ else
+ return ICE_ERR_PARAM;
+
+ /* num queues are not changed */
+ if (prev_numqs == new_numqs)
+ return status;
+
+ /* calculate number of nodes based on prev/new number of qs */
+ if (prev_numqs)
+ ice_sched_calc_vsi_child_nodes(hw, prev_numqs, prev_num_nodes);
+
+ if (new_numqs)
+ ice_sched_calc_vsi_child_nodes(hw, new_numqs, new_num_nodes);
+
+ if (prev_numqs > new_numqs) {
+ for (i = 0; i < ICE_AQC_TOPO_MAX_LEVEL_NUM; i++)
+ new_num_nodes[i] = prev_num_nodes[i] - new_num_nodes[i];
+
+ ice_sched_rm_vsi_child_nodes(pi, vsi_node, new_num_nodes,
+ owner);
+ } else {
+ for (i = 0; i < ICE_AQC_TOPO_MAX_LEVEL_NUM; i++)
+ new_num_nodes[i] -= prev_num_nodes[i];
+
+ status = ice_sched_add_vsi_child_nodes(pi, vsi_id, tc_node,
+ new_num_nodes, owner);
+ if (status)
+ return status;
+ }
+
+ if (owner == ICE_SCHED_NODE_OWNER_LAN)
+ vsi->max_lanq[tc] = new_numqs;
+
+ return status;
+}
+
+/**
+ * ice_sched_cfg_vsi - configure the new/exisiting VSI
+ * @pi: port information structure
+ * @vsi_id: VSI Id
+ * @tc: TC number
+ * @maxqs: max number of queues
+ * @owner: lan or rdma
+ * @enable: TC enabled or disabled
+ *
+ * This function adds/updates VSI nodes based on the number of queues. If TC is
+ * enabled and VSI is in suspended state then resume the VSI back. If TC is
+ * disabled then suspend the VSI if it is not already.
+ */
+enum ice_status
+ice_sched_cfg_vsi(struct ice_port_info *pi, u16 vsi_id, u8 tc, u16 maxqs,
+ u8 owner, bool enable)
+{
+ struct ice_sched_node *vsi_node, *tc_node;
+ struct ice_sched_vsi_info *vsi;
+ enum ice_status status = 0;
+ struct ice_hw *hw = pi->hw;
+
+ tc_node = ice_sched_get_tc_node(pi, tc);
+ if (!tc_node)
+ return ICE_ERR_PARAM;
+
+ vsi = ice_sched_get_vsi_info_entry(pi, vsi_id);
+ if (!vsi)
+ vsi = ice_sched_create_vsi_info_entry(pi, vsi_id);
+ if (!vsi)
+ return ICE_ERR_NO_MEMORY;
+
+ vsi_node = ice_sched_get_vsi_node(hw, tc_node, vsi_id);
+
+ /* suspend the VSI if tc is not enabled */
+ if (!enable) {
+ if (vsi_node && vsi_node->in_use) {
+ u32 teid = le32_to_cpu(vsi_node->info.node_teid);
+
+ status = ice_sched_suspend_resume_elems(hw, 1, &teid,
+ true);
+ if (!status)
+ vsi_node->in_use = false;
+ }
+ return status;
+ }
+
+ /* TC is enabled, if it is a new VSI then add it to the tree */
+ if (!vsi_node) {
+ status = ice_sched_add_vsi_to_topo(pi, vsi_id, tc);
+ if (status)
+ return status;
+ vsi_node = ice_sched_get_vsi_node(hw, tc_node, vsi_id);
+ if (!vsi_node)
+ return ICE_ERR_CFG;
+ vsi->vsi_node[tc] = vsi_node;
+ vsi_node->in_use = true;
+ }
+
+ /* update the VSI child nodes */
+ status = ice_sched_update_vsi_child_nodes(pi, vsi_id, tc, maxqs, owner);
+ if (status)
+ return status;
+
+ /* TC is enabled, resume the VSI if it is in the suspend state */
+ if (!vsi_node->in_use) {
+ u32 teid = le32_to_cpu(vsi_node->info.node_teid);
+
+ status = ice_sched_suspend_resume_elems(hw, 1, &teid, false);
+ if (!status)
+ vsi_node->in_use = true;
+ }
+
+ return status;
+}
diff --git a/drivers/net/ethernet/intel/ice/ice_sched.h b/drivers/net/ethernet/intel/ice/ice_sched.h
new file mode 100644
index 000000000000..badadcc120d3
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_sched.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018, Intel Corporation. */
+
+#ifndef _ICE_SCHED_H_
+#define _ICE_SCHED_H_
+
+#include "ice_common.h"
+
+#define ICE_QGRP_LAYER_OFFSET 2
+#define ICE_VSI_LAYER_OFFSET 4
+
+struct ice_sched_agg_vsi_info {
+ struct list_head list_entry;
+ DECLARE_BITMAP(tc_bitmap, ICE_MAX_TRAFFIC_CLASS);
+ u16 vsi_id;
+};
+
+struct ice_sched_agg_info {
+ struct list_head agg_vsi_list;
+ struct list_head list_entry;
+ DECLARE_BITMAP(tc_bitmap, ICE_MAX_TRAFFIC_CLASS);
+ u32 agg_id;
+ enum ice_agg_type agg_type;
+};
+
+/* FW AQ command calls */
+enum ice_status ice_sched_init_port(struct ice_port_info *pi);
+enum ice_status ice_sched_query_res_alloc(struct ice_hw *hw);
+void ice_sched_cleanup_all(struct ice_hw *hw);
+struct ice_sched_node *
+ice_sched_find_node_by_teid(struct ice_sched_node *start_node, u32 teid);
+enum ice_status
+ice_sched_add_node(struct ice_port_info *pi, u8 layer,
+ struct ice_aqc_txsched_elem_data *info);
+void ice_free_sched_node(struct ice_port_info *pi, struct ice_sched_node *node);
+struct ice_sched_node *ice_sched_get_tc_node(struct ice_port_info *pi, u8 tc);
+struct ice_sched_node *
+ice_sched_get_free_qparent(struct ice_port_info *pi, u16 vsi_id, u8 tc,
+ u8 owner);
+enum ice_status
+ice_sched_cfg_vsi(struct ice_port_info *pi, u16 vsi_id, u8 tc, u16 maxqs,
+ u8 owner, bool enable);
+#endif /* _ICE_SCHED_H_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_status.h b/drivers/net/ethernet/intel/ice/ice_status.h
new file mode 100644
index 000000000000..9a95c4ffd7d7
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_status.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018, Intel Corporation. */
+
+#ifndef _ICE_STATUS_H_
+#define _ICE_STATUS_H_
+
+/* Error Codes */
+enum ice_status {
+ ICE_ERR_PARAM = -1,
+ ICE_ERR_NOT_IMPL = -2,
+ ICE_ERR_NOT_READY = -3,
+ ICE_ERR_BAD_PTR = -5,
+ ICE_ERR_INVAL_SIZE = -6,
+ ICE_ERR_DEVICE_NOT_SUPPORTED = -8,
+ ICE_ERR_RESET_FAILED = -9,
+ ICE_ERR_FW_API_VER = -10,
+ ICE_ERR_NO_MEMORY = -11,
+ ICE_ERR_CFG = -12,
+ ICE_ERR_OUT_OF_RANGE = -13,
+ ICE_ERR_ALREADY_EXISTS = -14,
+ ICE_ERR_DOES_NOT_EXIST = -15,
+ ICE_ERR_MAX_LIMIT = -17,
+ ICE_ERR_BUF_TOO_SHORT = -52,
+ ICE_ERR_NVM_BLANK_MODE = -53,
+ ICE_ERR_AQ_ERROR = -100,
+ ICE_ERR_AQ_TIMEOUT = -101,
+ ICE_ERR_AQ_FULL = -102,
+ ICE_ERR_AQ_NO_WORK = -103,
+ ICE_ERR_AQ_EMPTY = -104,
+};
+
+#endif /* _ICE_STATUS_H_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_switch.c b/drivers/net/ethernet/intel/ice/ice_switch.c
new file mode 100644
index 000000000000..723d15f1e90b
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_switch.c
@@ -0,0 +1,1883 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2018, Intel Corporation. */
+
+#include "ice_switch.h"
+
+#define ICE_ETH_DA_OFFSET 0
+#define ICE_ETH_ETHTYPE_OFFSET 12
+#define ICE_ETH_VLAN_TCI_OFFSET 14
+#define ICE_MAX_VLAN_ID 0xFFF
+
+/* Dummy ethernet header needed in the ice_aqc_sw_rules_elem
+ * struct to configure any switch filter rules.
+ * {DA (6 bytes), SA(6 bytes),
+ * Ether type (2 bytes for header without VLAN tag) OR
+ * VLAN tag (4 bytes for header with VLAN tag) }
+ *
+ * Word on Hardcoded values
+ * byte 0 = 0x2: to identify it as locally administered DA MAC
+ * byte 6 = 0x2: to identify it as locally administered SA MAC
+ * byte 12 = 0x81 & byte 13 = 0x00:
+ * In case of VLAN filter first two bytes defines ether type (0x8100)
+ * and remaining two bytes are placeholder for programming a given VLAN id
+ * In case of Ether type filter it is treated as header without VLAN tag
+ * and byte 12 and 13 is used to program a given Ether type instead
+ */
+#define DUMMY_ETH_HDR_LEN 16
+static const u8 dummy_eth_header[DUMMY_ETH_HDR_LEN] = { 0x2, 0, 0, 0, 0, 0,
+ 0x2, 0, 0, 0, 0, 0,
+ 0x81, 0, 0, 0};
+
+#define ICE_SW_RULE_RX_TX_ETH_HDR_SIZE \
+ (sizeof(struct ice_aqc_sw_rules_elem) - \
+ sizeof(((struct ice_aqc_sw_rules_elem *)0)->pdata) + \
+ sizeof(struct ice_sw_rule_lkup_rx_tx) + DUMMY_ETH_HDR_LEN - 1)
+#define ICE_SW_RULE_RX_TX_NO_HDR_SIZE \
+ (sizeof(struct ice_aqc_sw_rules_elem) - \
+ sizeof(((struct ice_aqc_sw_rules_elem *)0)->pdata) + \
+ sizeof(struct ice_sw_rule_lkup_rx_tx) - 1)
+#define ICE_SW_RULE_LG_ACT_SIZE(n) \
+ (sizeof(struct ice_aqc_sw_rules_elem) - \
+ sizeof(((struct ice_aqc_sw_rules_elem *)0)->pdata) + \
+ sizeof(struct ice_sw_rule_lg_act) - \
+ sizeof(((struct ice_sw_rule_lg_act *)0)->act) + \
+ ((n) * sizeof(((struct ice_sw_rule_lg_act *)0)->act)))
+#define ICE_SW_RULE_VSI_LIST_SIZE(n) \
+ (sizeof(struct ice_aqc_sw_rules_elem) - \
+ sizeof(((struct ice_aqc_sw_rules_elem *)0)->pdata) + \
+ sizeof(struct ice_sw_rule_vsi_list) - \
+ sizeof(((struct ice_sw_rule_vsi_list *)0)->vsi) + \
+ ((n) * sizeof(((struct ice_sw_rule_vsi_list *)0)->vsi)))
+
+/**
+ * ice_aq_alloc_free_res - command to allocate/free resources
+ * @hw: pointer to the hw struct
+ * @num_entries: number of resource entries in buffer
+ * @buf: Indirect buffer to hold data parameters and response
+ * @buf_size: size of buffer for indirect commands
+ * @opc: pass in the command opcode
+ * @cd: pointer to command details structure or NULL
+ *
+ * Helper function to allocate/free resources using the admin queue commands
+ */
+static enum ice_status
+ice_aq_alloc_free_res(struct ice_hw *hw, u16 num_entries,
+ struct ice_aqc_alloc_free_res_elem *buf, u16 buf_size,
+ enum ice_adminq_opc opc, struct ice_sq_cd *cd)
+{
+ struct ice_aqc_alloc_free_res_cmd *cmd;
+ struct ice_aq_desc desc;
+
+ cmd = &desc.params.sw_res_ctrl;
+
+ if (!buf)
+ return ICE_ERR_PARAM;
+
+ if (buf_size < (num_entries * sizeof(buf->elem[0])))
+ return ICE_ERR_PARAM;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, opc);
+
+ desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD);
+
+ cmd->num_entries = cpu_to_le16(num_entries);
+
+ return ice_aq_send_cmd(hw, &desc, buf, buf_size, cd);
+}
+
+/**
+ * ice_aq_get_sw_cfg - get switch configuration
+ * @hw: pointer to the hardware structure
+ * @buf: pointer to the result buffer
+ * @buf_size: length of the buffer available for response
+ * @req_desc: pointer to requested descriptor
+ * @num_elems: pointer to number of elements
+ * @cd: pointer to command details structure or NULL
+ *
+ * Get switch configuration (0x0200) to be placed in 'buff'.
+ * This admin command returns information such as initial VSI/port number
+ * and switch ID it belongs to.
+ *
+ * NOTE: *req_desc is both an input/output parameter.
+ * The caller of this function first calls this function with *request_desc set
+ * to 0. If the response from f/w has *req_desc set to 0, all the switch
+ * configuration information has been returned; if non-zero (meaning not all
+ * the information was returned), the caller should call this function again
+ * with *req_desc set to the previous value returned by f/w to get the
+ * next block of switch configuration information.
+ *
+ * *num_elems is output only parameter. This reflects the number of elements
+ * in response buffer. The caller of this function to use *num_elems while
+ * parsing the response buffer.
+ */
+static enum ice_status
+ice_aq_get_sw_cfg(struct ice_hw *hw, struct ice_aqc_get_sw_cfg_resp *buf,
+ u16 buf_size, u16 *req_desc, u16 *num_elems,
+ struct ice_sq_cd *cd)
+{
+ struct ice_aqc_get_sw_cfg *cmd;
+ enum ice_status status;
+ struct ice_aq_desc desc;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_sw_cfg);
+ cmd = &desc.params.get_sw_conf;
+ cmd->element = cpu_to_le16(*req_desc);
+
+ status = ice_aq_send_cmd(hw, &desc, buf, buf_size, cd);
+ if (!status) {
+ *req_desc = le16_to_cpu(cmd->element);
+ *num_elems = le16_to_cpu(cmd->num_elems);
+ }
+
+ return status;
+}
+
+/**
+ * ice_aq_add_vsi
+ * @hw: pointer to the hw struct
+ * @vsi_ctx: pointer to a VSI context struct
+ * @cd: pointer to command details structure or NULL
+ *
+ * Add a VSI context to the hardware (0x0210)
+ */
+enum ice_status
+ice_aq_add_vsi(struct ice_hw *hw, struct ice_vsi_ctx *vsi_ctx,
+ struct ice_sq_cd *cd)
+{
+ struct ice_aqc_add_update_free_vsi_resp *res;
+ struct ice_aqc_add_get_update_free_vsi *cmd;
+ enum ice_status status;
+ struct ice_aq_desc desc;
+
+ cmd = &desc.params.vsi_cmd;
+ res = (struct ice_aqc_add_update_free_vsi_resp *)&desc.params.raw;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_add_vsi);
+
+ if (!vsi_ctx->alloc_from_pool)
+ cmd->vsi_num = cpu_to_le16(vsi_ctx->vsi_num |
+ ICE_AQ_VSI_IS_VALID);
+
+ cmd->vsi_flags = cpu_to_le16(vsi_ctx->flags);
+
+ desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD);
+
+ status = ice_aq_send_cmd(hw, &desc, &vsi_ctx->info,
+ sizeof(vsi_ctx->info), cd);
+
+ if (!status) {
+ vsi_ctx->vsi_num = le16_to_cpu(res->vsi_num) & ICE_AQ_VSI_NUM_M;
+ vsi_ctx->vsis_allocd = le16_to_cpu(res->vsi_used);
+ vsi_ctx->vsis_unallocated = le16_to_cpu(res->vsi_free);
+ }
+
+ return status;
+}
+
+/**
+ * ice_aq_update_vsi
+ * @hw: pointer to the hw struct
+ * @vsi_ctx: pointer to a VSI context struct
+ * @cd: pointer to command details structure or NULL
+ *
+ * Update VSI context in the hardware (0x0211)
+ */
+enum ice_status
+ice_aq_update_vsi(struct ice_hw *hw, struct ice_vsi_ctx *vsi_ctx,
+ struct ice_sq_cd *cd)
+{
+ struct ice_aqc_add_update_free_vsi_resp *resp;
+ struct ice_aqc_add_get_update_free_vsi *cmd;
+ struct ice_aq_desc desc;
+ enum ice_status status;
+
+ cmd = &desc.params.vsi_cmd;
+ resp = (struct ice_aqc_add_update_free_vsi_resp *)&desc.params.raw;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_update_vsi);
+
+ cmd->vsi_num = cpu_to_le16(vsi_ctx->vsi_num | ICE_AQ_VSI_IS_VALID);
+
+ desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD);
+
+ status = ice_aq_send_cmd(hw, &desc, &vsi_ctx->info,
+ sizeof(vsi_ctx->info), cd);
+
+ if (!status) {
+ vsi_ctx->vsis_allocd = le16_to_cpu(resp->vsi_used);
+ vsi_ctx->vsis_unallocated = le16_to_cpu(resp->vsi_free);
+ }
+
+ return status;
+}
+
+/**
+ * ice_aq_free_vsi
+ * @hw: pointer to the hw struct
+ * @vsi_ctx: pointer to a VSI context struct
+ * @keep_vsi_alloc: keep VSI allocation as part of this PF's resources
+ * @cd: pointer to command details structure or NULL
+ *
+ * Get VSI context info from hardware (0x0213)
+ */
+enum ice_status
+ice_aq_free_vsi(struct ice_hw *hw, struct ice_vsi_ctx *vsi_ctx,
+ bool keep_vsi_alloc, struct ice_sq_cd *cd)
+{
+ struct ice_aqc_add_update_free_vsi_resp *resp;
+ struct ice_aqc_add_get_update_free_vsi *cmd;
+ struct ice_aq_desc desc;
+ enum ice_status status;
+
+ cmd = &desc.params.vsi_cmd;
+ resp = (struct ice_aqc_add_update_free_vsi_resp *)&desc.params.raw;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_free_vsi);
+
+ cmd->vsi_num = cpu_to_le16(vsi_ctx->vsi_num | ICE_AQ_VSI_IS_VALID);
+ if (keep_vsi_alloc)
+ cmd->cmd_flags = cpu_to_le16(ICE_AQ_VSI_KEEP_ALLOC);
+
+ status = ice_aq_send_cmd(hw, &desc, NULL, 0, cd);
+ if (!status) {
+ vsi_ctx->vsis_allocd = le16_to_cpu(resp->vsi_used);
+ vsi_ctx->vsis_unallocated = le16_to_cpu(resp->vsi_free);
+ }
+
+ return status;
+}
+
+/**
+ * ice_aq_alloc_free_vsi_list
+ * @hw: pointer to the hw struct
+ * @vsi_list_id: VSI list id returned or used for lookup
+ * @lkup_type: switch rule filter lookup type
+ * @opc: switch rules population command type - pass in the command opcode
+ *
+ * allocates or free a VSI list resource
+ */
+static enum ice_status
+ice_aq_alloc_free_vsi_list(struct ice_hw *hw, u16 *vsi_list_id,
+ enum ice_sw_lkup_type lkup_type,
+ enum ice_adminq_opc opc)
+{
+ struct ice_aqc_alloc_free_res_elem *sw_buf;
+ struct ice_aqc_res_elem *vsi_ele;
+ enum ice_status status;
+ u16 buf_len;
+
+ buf_len = sizeof(*sw_buf);
+ sw_buf = devm_kzalloc(ice_hw_to_dev(hw), buf_len, GFP_KERNEL);
+ if (!sw_buf)
+ return ICE_ERR_NO_MEMORY;
+ sw_buf->num_elems = cpu_to_le16(1);
+
+ if (lkup_type == ICE_SW_LKUP_MAC ||
+ lkup_type == ICE_SW_LKUP_MAC_VLAN ||
+ lkup_type == ICE_SW_LKUP_ETHERTYPE ||
+ lkup_type == ICE_SW_LKUP_ETHERTYPE_MAC ||
+ lkup_type == ICE_SW_LKUP_PROMISC ||
+ lkup_type == ICE_SW_LKUP_PROMISC_VLAN) {
+ sw_buf->res_type = cpu_to_le16(ICE_AQC_RES_TYPE_VSI_LIST_REP);
+ } else if (lkup_type == ICE_SW_LKUP_VLAN) {
+ sw_buf->res_type =
+ cpu_to_le16(ICE_AQC_RES_TYPE_VSI_LIST_PRUNE);
+ } else {
+ status = ICE_ERR_PARAM;
+ goto ice_aq_alloc_free_vsi_list_exit;
+ }
+
+ if (opc == ice_aqc_opc_free_res)
+ sw_buf->elem[0].e.sw_resp = cpu_to_le16(*vsi_list_id);
+
+ status = ice_aq_alloc_free_res(hw, 1, sw_buf, buf_len, opc, NULL);
+ if (status)
+ goto ice_aq_alloc_free_vsi_list_exit;
+
+ if (opc == ice_aqc_opc_alloc_res) {
+ vsi_ele = &sw_buf->elem[0];
+ *vsi_list_id = le16_to_cpu(vsi_ele->e.sw_resp);
+ }
+
+ice_aq_alloc_free_vsi_list_exit:
+ devm_kfree(ice_hw_to_dev(hw), sw_buf);
+ return status;
+}
+
+/**
+ * ice_aq_sw_rules - add/update/remove switch rules
+ * @hw: pointer to the hw struct
+ * @rule_list: pointer to switch rule population list
+ * @rule_list_sz: total size of the rule list in bytes
+ * @num_rules: number of switch rules in the rule_list
+ * @opc: switch rules population command type - pass in the command opcode
+ * @cd: pointer to command details structure or NULL
+ *
+ * Add(0x02a0)/Update(0x02a1)/Remove(0x02a2) switch rules commands to firmware
+ */
+static enum ice_status
+ice_aq_sw_rules(struct ice_hw *hw, void *rule_list, u16 rule_list_sz,
+ u8 num_rules, enum ice_adminq_opc opc, struct ice_sq_cd *cd)
+{
+ struct ice_aq_desc desc;
+
+ if (opc != ice_aqc_opc_add_sw_rules &&
+ opc != ice_aqc_opc_update_sw_rules &&
+ opc != ice_aqc_opc_remove_sw_rules)
+ return ICE_ERR_PARAM;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, opc);
+
+ desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD);
+ desc.params.sw_rules.num_rules_fltr_entry_index =
+ cpu_to_le16(num_rules);
+ return ice_aq_send_cmd(hw, &desc, rule_list, rule_list_sz, cd);
+}
+
+/* ice_init_port_info - Initialize port_info with switch configuration data
+ * @pi: pointer to port_info
+ * @vsi_port_num: VSI number or port number
+ * @type: Type of switch element (port or VSI)
+ * @swid: switch ID of the switch the element is attached to
+ * @pf_vf_num: PF or VF number
+ * @is_vf: true if the element is a VF, false otherwise
+ */
+static void
+ice_init_port_info(struct ice_port_info *pi, u16 vsi_port_num, u8 type,
+ u16 swid, u16 pf_vf_num, bool is_vf)
+{
+ switch (type) {
+ case ICE_AQC_GET_SW_CONF_RESP_PHYS_PORT:
+ pi->lport = (u8)(vsi_port_num & ICE_LPORT_MASK);
+ pi->sw_id = swid;
+ pi->pf_vf_num = pf_vf_num;
+ pi->is_vf = is_vf;
+ pi->dflt_tx_vsi_num = ICE_DFLT_VSI_INVAL;
+ pi->dflt_rx_vsi_num = ICE_DFLT_VSI_INVAL;
+ break;
+ default:
+ ice_debug(pi->hw, ICE_DBG_SW,
+ "incorrect VSI/port type received\n");
+ break;
+ }
+}
+
+/* ice_get_initial_sw_cfg - Get initial port and default VSI data
+ * @hw: pointer to the hardware structure
+ */
+enum ice_status ice_get_initial_sw_cfg(struct ice_hw *hw)
+{
+ struct ice_aqc_get_sw_cfg_resp *rbuf;
+ enum ice_status status;
+ u16 req_desc = 0;
+ u16 num_elems;
+ u16 i;
+
+ rbuf = devm_kzalloc(ice_hw_to_dev(hw), ICE_SW_CFG_MAX_BUF_LEN,
+ GFP_KERNEL);
+
+ if (!rbuf)
+ return ICE_ERR_NO_MEMORY;
+
+ /* Multiple calls to ice_aq_get_sw_cfg may be required
+ * to get all the switch configuration information. The need
+ * for additional calls is indicated by ice_aq_get_sw_cfg
+ * writing a non-zero value in req_desc
+ */
+ do {
+ status = ice_aq_get_sw_cfg(hw, rbuf, ICE_SW_CFG_MAX_BUF_LEN,
+ &req_desc, &num_elems, NULL);
+
+ if (status)
+ break;
+
+ for (i = 0; i < num_elems; i++) {
+ struct ice_aqc_get_sw_cfg_resp_elem *ele;
+ u16 pf_vf_num, swid, vsi_port_num;
+ bool is_vf = false;
+ u8 type;
+
+ ele = rbuf[i].elements;
+ vsi_port_num = le16_to_cpu(ele->vsi_port_num) &
+ ICE_AQC_GET_SW_CONF_RESP_VSI_PORT_NUM_M;
+
+ pf_vf_num = le16_to_cpu(ele->pf_vf_num) &
+ ICE_AQC_GET_SW_CONF_RESP_FUNC_NUM_M;
+
+ swid = le16_to_cpu(ele->swid);
+
+ if (le16_to_cpu(ele->pf_vf_num) &
+ ICE_AQC_GET_SW_CONF_RESP_IS_VF)
+ is_vf = true;
+
+ type = le16_to_cpu(ele->vsi_port_num) >>
+ ICE_AQC_GET_SW_CONF_RESP_TYPE_S;
+
+ if (type == ICE_AQC_GET_SW_CONF_RESP_VSI) {
+ /* FW VSI is not needed. Just continue. */
+ continue;
+ }
+
+ ice_init_port_info(hw->port_info, vsi_port_num,
+ type, swid, pf_vf_num, is_vf);
+ }
+ } while (req_desc && !status);
+
+ devm_kfree(ice_hw_to_dev(hw), (void *)rbuf);
+ return status;
+}
+
+/**
+ * ice_fill_sw_info - Helper function to populate lb_en and lan_en
+ * @hw: pointer to the hardware structure
+ * @f_info: filter info structure to fill/update
+ *
+ * This helper function populates the lb_en and lan_en elements of the provided
+ * ice_fltr_info struct using the switch's type and characteristics of the
+ * switch rule being configured.
+ */
+static void ice_fill_sw_info(struct ice_hw *hw, struct ice_fltr_info *f_info)
+{
+ f_info->lb_en = false;
+ f_info->lan_en = false;
+ if ((f_info->flag & ICE_FLTR_TX) &&
+ (f_info->fltr_act == ICE_FWD_TO_VSI ||
+ f_info->fltr_act == ICE_FWD_TO_VSI_LIST ||
+ f_info->fltr_act == ICE_FWD_TO_Q ||
+ f_info->fltr_act == ICE_FWD_TO_QGRP)) {
+ f_info->lb_en = true;
+ if (!(hw->evb_veb && f_info->lkup_type == ICE_SW_LKUP_MAC &&
+ is_unicast_ether_addr(f_info->l_data.mac.mac_addr)))
+ f_info->lan_en = true;
+ }
+}
+
+/**
+ * ice_fill_sw_rule - Helper function to fill switch rule structure
+ * @hw: pointer to the hardware structure
+ * @f_info: entry containing packet forwarding information
+ * @s_rule: switch rule structure to be filled in based on mac_entry
+ * @opc: switch rules population command type - pass in the command opcode
+ */
+static void
+ice_fill_sw_rule(struct ice_hw *hw, struct ice_fltr_info *f_info,
+ struct ice_aqc_sw_rules_elem *s_rule, enum ice_adminq_opc opc)
+{
+ u16 vlan_id = ICE_MAX_VLAN_ID + 1;
+ u8 eth_hdr[DUMMY_ETH_HDR_LEN];
+ void *daddr = NULL;
+ u32 act = 0;
+ __be16 *off;
+
+ if (opc == ice_aqc_opc_remove_sw_rules) {
+ s_rule->pdata.lkup_tx_rx.act = 0;
+ s_rule->pdata.lkup_tx_rx.index =
+ cpu_to_le16(f_info->fltr_rule_id);
+ s_rule->pdata.lkup_tx_rx.hdr_len = 0;
+ return;
+ }
+
+ /* initialize the ether header with a dummy header */
+ memcpy(eth_hdr, dummy_eth_header, sizeof(dummy_eth_header));
+ ice_fill_sw_info(hw, f_info);
+
+ switch (f_info->fltr_act) {
+ case ICE_FWD_TO_VSI:
+ act |= (f_info->fwd_id.vsi_id << ICE_SINGLE_ACT_VSI_ID_S) &
+ ICE_SINGLE_ACT_VSI_ID_M;
+ if (f_info->lkup_type != ICE_SW_LKUP_VLAN)
+ act |= ICE_SINGLE_ACT_VSI_FORWARDING |
+ ICE_SINGLE_ACT_VALID_BIT;
+ break;
+ case ICE_FWD_TO_VSI_LIST:
+ act |= ICE_SINGLE_ACT_VSI_LIST;
+ act |= (f_info->fwd_id.vsi_list_id <<
+ ICE_SINGLE_ACT_VSI_LIST_ID_S) &
+ ICE_SINGLE_ACT_VSI_LIST_ID_M;
+ if (f_info->lkup_type != ICE_SW_LKUP_VLAN)
+ act |= ICE_SINGLE_ACT_VSI_FORWARDING |
+ ICE_SINGLE_ACT_VALID_BIT;
+ break;
+ case ICE_FWD_TO_Q:
+ act |= ICE_SINGLE_ACT_TO_Q;
+ act |= (f_info->fwd_id.q_id << ICE_SINGLE_ACT_Q_INDEX_S) &
+ ICE_SINGLE_ACT_Q_INDEX_M;
+ break;
+ case ICE_FWD_TO_QGRP:
+ act |= ICE_SINGLE_ACT_TO_Q;
+ act |= (f_info->qgrp_size << ICE_SINGLE_ACT_Q_REGION_S) &
+ ICE_SINGLE_ACT_Q_REGION_M;
+ break;
+ case ICE_DROP_PACKET:
+ act |= ICE_SINGLE_ACT_VSI_FORWARDING | ICE_SINGLE_ACT_DROP;
+ break;
+ default:
+ return;
+ }
+
+ if (f_info->lb_en)
+ act |= ICE_SINGLE_ACT_LB_ENABLE;
+ if (f_info->lan_en)
+ act |= ICE_SINGLE_ACT_LAN_ENABLE;
+
+ switch (f_info->lkup_type) {
+ case ICE_SW_LKUP_MAC:
+ daddr = f_info->l_data.mac.mac_addr;
+ break;
+ case ICE_SW_LKUP_VLAN:
+ vlan_id = f_info->l_data.vlan.vlan_id;
+ if (f_info->fltr_act == ICE_FWD_TO_VSI ||
+ f_info->fltr_act == ICE_FWD_TO_VSI_LIST) {
+ act |= ICE_SINGLE_ACT_PRUNE;
+ act |= ICE_SINGLE_ACT_EGRESS | ICE_SINGLE_ACT_INGRESS;
+ }
+ break;
+ case ICE_SW_LKUP_ETHERTYPE_MAC:
+ daddr = f_info->l_data.ethertype_mac.mac_addr;
+ /* fall-through */
+ case ICE_SW_LKUP_ETHERTYPE:
+ off = (__be16 *)&eth_hdr[ICE_ETH_ETHTYPE_OFFSET];
+ *off = cpu_to_be16(f_info->l_data.ethertype_mac.ethertype);
+ break;
+ case ICE_SW_LKUP_MAC_VLAN:
+ daddr = f_info->l_data.mac_vlan.mac_addr;
+ vlan_id = f_info->l_data.mac_vlan.vlan_id;
+ break;
+ case ICE_SW_LKUP_PROMISC_VLAN:
+ vlan_id = f_info->l_data.mac_vlan.vlan_id;
+ /* fall-through */
+ case ICE_SW_LKUP_PROMISC:
+ daddr = f_info->l_data.mac_vlan.mac_addr;
+ break;
+ default:
+ break;
+ }
+
+ s_rule->type = (f_info->flag & ICE_FLTR_RX) ?
+ cpu_to_le16(ICE_AQC_SW_RULES_T_LKUP_RX) :
+ cpu_to_le16(ICE_AQC_SW_RULES_T_LKUP_TX);
+
+ /* Recipe set depending on lookup type */
+ s_rule->pdata.lkup_tx_rx.recipe_id = cpu_to_le16(f_info->lkup_type);
+ s_rule->pdata.lkup_tx_rx.src = cpu_to_le16(f_info->src);
+ s_rule->pdata.lkup_tx_rx.act = cpu_to_le32(act);
+
+ if (daddr)
+ ether_addr_copy(&eth_hdr[ICE_ETH_DA_OFFSET], daddr);
+
+ if (!(vlan_id > ICE_MAX_VLAN_ID)) {
+ off = (__be16 *)&eth_hdr[ICE_ETH_VLAN_TCI_OFFSET];
+ *off = cpu_to_be16(vlan_id);
+ }
+
+ /* Create the switch rule with the final dummy Ethernet header */
+ if (opc != ice_aqc_opc_update_sw_rules)
+ s_rule->pdata.lkup_tx_rx.hdr_len = cpu_to_le16(sizeof(eth_hdr));
+
+ memcpy(s_rule->pdata.lkup_tx_rx.hdr, eth_hdr, sizeof(eth_hdr));
+}
+
+/**
+ * ice_add_marker_act
+ * @hw: pointer to the hardware structure
+ * @m_ent: the management entry for which sw marker needs to be added
+ * @sw_marker: sw marker to tag the Rx descriptor with
+ * @l_id: large action resource id
+ *
+ * Create a large action to hold software marker and update the switch rule
+ * entry pointed by m_ent with newly created large action
+ */
+static enum ice_status
+ice_add_marker_act(struct ice_hw *hw, struct ice_fltr_mgmt_list_entry *m_ent,
+ u16 sw_marker, u16 l_id)
+{
+ struct ice_aqc_sw_rules_elem *lg_act, *rx_tx;
+ /* For software marker we need 3 large actions
+ * 1. FWD action: FWD TO VSI or VSI LIST
+ * 2. GENERIC VALUE action to hold the profile id
+ * 3. GENERIC VALUE action to hold the software marker id
+ */
+ const u16 num_lg_acts = 3;
+ enum ice_status status;
+ u16 lg_act_size;
+ u16 rules_size;
+ u16 vsi_info;
+ u32 act;
+
+ if (m_ent->fltr_info.lkup_type != ICE_SW_LKUP_MAC)
+ return ICE_ERR_PARAM;
+
+ /* Create two back-to-back switch rules and submit them to the HW using
+ * one memory buffer:
+ * 1. Large Action
+ * 2. Look up tx rx
+ */
+ lg_act_size = (u16)ICE_SW_RULE_LG_ACT_SIZE(num_lg_acts);
+ rules_size = lg_act_size + ICE_SW_RULE_RX_TX_ETH_HDR_SIZE;
+ lg_act = devm_kzalloc(ice_hw_to_dev(hw), rules_size, GFP_KERNEL);
+ if (!lg_act)
+ return ICE_ERR_NO_MEMORY;
+
+ rx_tx = (struct ice_aqc_sw_rules_elem *)((u8 *)lg_act + lg_act_size);
+
+ /* Fill in the first switch rule i.e. large action */
+ lg_act->type = cpu_to_le16(ICE_AQC_SW_RULES_T_LG_ACT);
+ lg_act->pdata.lg_act.index = cpu_to_le16(l_id);
+ lg_act->pdata.lg_act.size = cpu_to_le16(num_lg_acts);
+
+ /* First action VSI forwarding or VSI list forwarding depending on how
+ * many VSIs
+ */
+ vsi_info = (m_ent->vsi_count > 1) ?
+ m_ent->fltr_info.fwd_id.vsi_list_id :
+ m_ent->fltr_info.fwd_id.vsi_id;
+
+ act = ICE_LG_ACT_VSI_FORWARDING | ICE_LG_ACT_VALID_BIT;
+ act |= (vsi_info << ICE_LG_ACT_VSI_LIST_ID_S) &
+ ICE_LG_ACT_VSI_LIST_ID_M;
+ if (m_ent->vsi_count > 1)
+ act |= ICE_LG_ACT_VSI_LIST;
+ lg_act->pdata.lg_act.act[0] = cpu_to_le32(act);
+
+ /* Second action descriptor type */
+ act = ICE_LG_ACT_GENERIC;
+
+ act |= (1 << ICE_LG_ACT_GENERIC_VALUE_S) & ICE_LG_ACT_GENERIC_VALUE_M;
+ lg_act->pdata.lg_act.act[1] = cpu_to_le32(act);
+
+ act = (7 << ICE_LG_ACT_GENERIC_OFFSET_S) & ICE_LG_ACT_GENERIC_VALUE_M;
+
+ /* Third action Marker value */
+ act |= ICE_LG_ACT_GENERIC;
+ act |= (sw_marker << ICE_LG_ACT_GENERIC_VALUE_S) &
+ ICE_LG_ACT_GENERIC_VALUE_M;
+
+ act |= (0 << ICE_LG_ACT_GENERIC_OFFSET_S) & ICE_LG_ACT_GENERIC_VALUE_M;
+ lg_act->pdata.lg_act.act[2] = cpu_to_le32(act);
+
+ /* call the fill switch rule to fill the lookup tx rx structure */
+ ice_fill_sw_rule(hw, &m_ent->fltr_info, rx_tx,
+ ice_aqc_opc_update_sw_rules);
+
+ /* Update the action to point to the large action id */
+ rx_tx->pdata.lkup_tx_rx.act =
+ cpu_to_le32(ICE_SINGLE_ACT_PTR |
+ ((l_id << ICE_SINGLE_ACT_PTR_VAL_S) &
+ ICE_SINGLE_ACT_PTR_VAL_M));
+
+ /* Use the filter rule id of the previously created rule with single
+ * act. Once the update happens, hardware will treat this as large
+ * action
+ */
+ rx_tx->pdata.lkup_tx_rx.index =
+ cpu_to_le16(m_ent->fltr_info.fltr_rule_id);
+
+ status = ice_aq_sw_rules(hw, lg_act, rules_size, 2,
+ ice_aqc_opc_update_sw_rules, NULL);
+ if (!status) {
+ m_ent->lg_act_idx = l_id;
+ m_ent->sw_marker_id = sw_marker;
+ }
+
+ devm_kfree(ice_hw_to_dev(hw), lg_act);
+ return status;
+}
+
+/**
+ * ice_create_vsi_list_map
+ * @hw: pointer to the hardware structure
+ * @vsi_array: array of VSIs to form a VSI list
+ * @num_vsi: num VSI in the array
+ * @vsi_list_id: VSI list id generated as part of allocate resource
+ *
+ * Helper function to create a new entry of VSI list id to VSI mapping
+ * using the given VSI list id
+ */
+static struct ice_vsi_list_map_info *
+ice_create_vsi_list_map(struct ice_hw *hw, u16 *vsi_array, u16 num_vsi,
+ u16 vsi_list_id)
+{
+ struct ice_switch_info *sw = hw->switch_info;
+ struct ice_vsi_list_map_info *v_map;
+ int i;
+
+ v_map = devm_kcalloc(ice_hw_to_dev(hw), 1, sizeof(*v_map), GFP_KERNEL);
+ if (!v_map)
+ return NULL;
+
+ v_map->vsi_list_id = vsi_list_id;
+
+ for (i = 0; i < num_vsi; i++)
+ set_bit(vsi_array[i], v_map->vsi_map);
+
+ list_add(&v_map->list_entry, &sw->vsi_list_map_head);
+ return v_map;
+}
+
+/**
+ * ice_update_vsi_list_rule
+ * @hw: pointer to the hardware structure
+ * @vsi_array: array of VSIs to form a VSI list
+ * @num_vsi: num VSI in the array
+ * @vsi_list_id: VSI list id generated as part of allocate resource
+ * @remove: Boolean value to indicate if this is a remove action
+ * @opc: switch rules population command type - pass in the command opcode
+ * @lkup_type: lookup type of the filter
+ *
+ * Call AQ command to add a new switch rule or update existing switch rule
+ * using the given VSI list id
+ */
+static enum ice_status
+ice_update_vsi_list_rule(struct ice_hw *hw, u16 *vsi_array, u16 num_vsi,
+ u16 vsi_list_id, bool remove, enum ice_adminq_opc opc,
+ enum ice_sw_lkup_type lkup_type)
+{
+ struct ice_aqc_sw_rules_elem *s_rule;
+ enum ice_status status;
+ u16 s_rule_size;
+ u16 type;
+ int i;
+
+ if (!num_vsi)
+ return ICE_ERR_PARAM;
+
+ if (lkup_type == ICE_SW_LKUP_MAC ||
+ lkup_type == ICE_SW_LKUP_MAC_VLAN ||
+ lkup_type == ICE_SW_LKUP_ETHERTYPE ||
+ lkup_type == ICE_SW_LKUP_ETHERTYPE_MAC ||
+ lkup_type == ICE_SW_LKUP_PROMISC ||
+ lkup_type == ICE_SW_LKUP_PROMISC_VLAN)
+ type = remove ? ICE_AQC_SW_RULES_T_VSI_LIST_CLEAR :
+ ICE_AQC_SW_RULES_T_VSI_LIST_SET;
+ else if (lkup_type == ICE_SW_LKUP_VLAN)
+ type = remove ? ICE_AQC_SW_RULES_T_PRUNE_LIST_CLEAR :
+ ICE_AQC_SW_RULES_T_PRUNE_LIST_SET;
+ else
+ return ICE_ERR_PARAM;
+
+ s_rule_size = (u16)ICE_SW_RULE_VSI_LIST_SIZE(num_vsi);
+ s_rule = devm_kzalloc(ice_hw_to_dev(hw), s_rule_size, GFP_KERNEL);
+ if (!s_rule)
+ return ICE_ERR_NO_MEMORY;
+
+ for (i = 0; i < num_vsi; i++)
+ s_rule->pdata.vsi_list.vsi[i] = cpu_to_le16(vsi_array[i]);
+
+ s_rule->type = cpu_to_le16(type);
+ s_rule->pdata.vsi_list.number_vsi = cpu_to_le16(num_vsi);
+ s_rule->pdata.vsi_list.index = cpu_to_le16(vsi_list_id);
+
+ status = ice_aq_sw_rules(hw, s_rule, s_rule_size, 1, opc, NULL);
+
+ devm_kfree(ice_hw_to_dev(hw), s_rule);
+ return status;
+}
+
+/**
+ * ice_create_vsi_list_rule - Creates and populates a VSI list rule
+ * @hw: pointer to the hw struct
+ * @vsi_array: array of VSIs to form a VSI list
+ * @num_vsi: number of VSIs in the array
+ * @vsi_list_id: stores the ID of the VSI list to be created
+ * @lkup_type: switch rule filter's lookup type
+ */
+static enum ice_status
+ice_create_vsi_list_rule(struct ice_hw *hw, u16 *vsi_array, u16 num_vsi,
+ u16 *vsi_list_id, enum ice_sw_lkup_type lkup_type)
+{
+ enum ice_status status;
+ int i;
+
+ for (i = 0; i < num_vsi; i++)
+ if (vsi_array[i] >= ICE_MAX_VSI)
+ return ICE_ERR_OUT_OF_RANGE;
+
+ status = ice_aq_alloc_free_vsi_list(hw, vsi_list_id, lkup_type,
+ ice_aqc_opc_alloc_res);
+ if (status)
+ return status;
+
+ /* Update the newly created VSI list to include the specified VSIs */
+ return ice_update_vsi_list_rule(hw, vsi_array, num_vsi, *vsi_list_id,
+ false, ice_aqc_opc_add_sw_rules,
+ lkup_type);
+}
+
+/**
+ * ice_create_pkt_fwd_rule
+ * @hw: pointer to the hardware structure
+ * @f_entry: entry containing packet forwarding information
+ *
+ * Create switch rule with given filter information and add an entry
+ * to the corresponding filter management list to track this switch rule
+ * and VSI mapping
+ */
+static enum ice_status
+ice_create_pkt_fwd_rule(struct ice_hw *hw,
+ struct ice_fltr_list_entry *f_entry)
+{
+ struct ice_switch_info *sw = hw->switch_info;
+ struct ice_fltr_mgmt_list_entry *fm_entry;
+ struct ice_aqc_sw_rules_elem *s_rule;
+ enum ice_sw_lkup_type l_type;
+ enum ice_status status;
+
+ s_rule = devm_kzalloc(ice_hw_to_dev(hw),
+ ICE_SW_RULE_RX_TX_ETH_HDR_SIZE, GFP_KERNEL);
+ if (!s_rule)
+ return ICE_ERR_NO_MEMORY;
+ fm_entry = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*fm_entry),
+ GFP_KERNEL);
+ if (!fm_entry) {
+ status = ICE_ERR_NO_MEMORY;
+ goto ice_create_pkt_fwd_rule_exit;
+ }
+
+ fm_entry->fltr_info = f_entry->fltr_info;
+
+ /* Initialize all the fields for the management entry */
+ fm_entry->vsi_count = 1;
+ fm_entry->lg_act_idx = ICE_INVAL_LG_ACT_INDEX;
+ fm_entry->sw_marker_id = ICE_INVAL_SW_MARKER_ID;
+ fm_entry->counter_index = ICE_INVAL_COUNTER_ID;
+
+ ice_fill_sw_rule(hw, &fm_entry->fltr_info, s_rule,
+ ice_aqc_opc_add_sw_rules);
+
+ status = ice_aq_sw_rules(hw, s_rule, ICE_SW_RULE_RX_TX_ETH_HDR_SIZE, 1,
+ ice_aqc_opc_add_sw_rules, NULL);
+ if (status) {
+ devm_kfree(ice_hw_to_dev(hw), fm_entry);
+ goto ice_create_pkt_fwd_rule_exit;
+ }
+
+ f_entry->fltr_info.fltr_rule_id =
+ le16_to_cpu(s_rule->pdata.lkup_tx_rx.index);
+ fm_entry->fltr_info.fltr_rule_id =
+ le16_to_cpu(s_rule->pdata.lkup_tx_rx.index);
+
+ /* The book keeping entries will get removed when base driver
+ * calls remove filter AQ command
+ */
+ l_type = fm_entry->fltr_info.lkup_type;
+ if (l_type == ICE_SW_LKUP_MAC) {
+ mutex_lock(&sw->mac_list_lock);
+ list_add(&fm_entry->list_entry, &sw->mac_list_head);
+ mutex_unlock(&sw->mac_list_lock);
+ } else if (l_type == ICE_SW_LKUP_VLAN) {
+ mutex_lock(&sw->vlan_list_lock);
+ list_add(&fm_entry->list_entry, &sw->vlan_list_head);
+ mutex_unlock(&sw->vlan_list_lock);
+ } else if (l_type == ICE_SW_LKUP_ETHERTYPE ||
+ l_type == ICE_SW_LKUP_ETHERTYPE_MAC) {
+ mutex_lock(&sw->eth_m_list_lock);
+ list_add(&fm_entry->list_entry, &sw->eth_m_list_head);
+ mutex_unlock(&sw->eth_m_list_lock);
+ } else if (l_type == ICE_SW_LKUP_PROMISC ||
+ l_type == ICE_SW_LKUP_PROMISC_VLAN) {
+ mutex_lock(&sw->promisc_list_lock);
+ list_add(&fm_entry->list_entry, &sw->promisc_list_head);
+ mutex_unlock(&sw->promisc_list_lock);
+ } else if (fm_entry->fltr_info.lkup_type == ICE_SW_LKUP_MAC_VLAN) {
+ mutex_lock(&sw->mac_vlan_list_lock);
+ list_add(&fm_entry->list_entry, &sw->mac_vlan_list_head);
+ mutex_unlock(&sw->mac_vlan_list_lock);
+ } else {
+ status = ICE_ERR_NOT_IMPL;
+ }
+ice_create_pkt_fwd_rule_exit:
+ devm_kfree(ice_hw_to_dev(hw), s_rule);
+ return status;
+}
+
+/**
+ * ice_update_pkt_fwd_rule
+ * @hw: pointer to the hardware structure
+ * @rule_id: rule of previously created switch rule to update
+ * @vsi_list_id: VSI list id to be updated with
+ * @f_info: ice_fltr_info to pull other information for switch rule
+ *
+ * Call AQ command to update a previously created switch rule with a
+ * VSI list id
+ */
+static enum ice_status
+ice_update_pkt_fwd_rule(struct ice_hw *hw, u16 rule_id, u16 vsi_list_id,
+ struct ice_fltr_info f_info)
+{
+ struct ice_aqc_sw_rules_elem *s_rule;
+ struct ice_fltr_info tmp_fltr;
+ enum ice_status status;
+
+ s_rule = devm_kzalloc(ice_hw_to_dev(hw),
+ ICE_SW_RULE_RX_TX_ETH_HDR_SIZE, GFP_KERNEL);
+ if (!s_rule)
+ return ICE_ERR_NO_MEMORY;
+
+ tmp_fltr = f_info;
+ tmp_fltr.fltr_act = ICE_FWD_TO_VSI_LIST;
+ tmp_fltr.fwd_id.vsi_list_id = vsi_list_id;
+
+ ice_fill_sw_rule(hw, &tmp_fltr, s_rule,
+ ice_aqc_opc_update_sw_rules);
+
+ s_rule->pdata.lkup_tx_rx.index = cpu_to_le16(rule_id);
+
+ /* Update switch rule with new rule set to forward VSI list */
+ status = ice_aq_sw_rules(hw, s_rule, ICE_SW_RULE_RX_TX_ETH_HDR_SIZE, 1,
+ ice_aqc_opc_update_sw_rules, NULL);
+
+ devm_kfree(ice_hw_to_dev(hw), s_rule);
+ return status;
+}
+
+/**
+ * ice_handle_vsi_list_mgmt
+ * @hw: pointer to the hardware structure
+ * @m_entry: pointer to current filter management list entry
+ * @cur_fltr: filter information from the book keeping entry
+ * @new_fltr: filter information with the new VSI to be added
+ *
+ * Call AQ command to add or update previously created VSI list with new VSI.
+ *
+ * Helper function to do book keeping associated with adding filter information
+ * The algorithm to do the booking keeping is described below :
+ * When a VSI needs to subscribe to a given filter( MAC/VLAN/Ethtype etc.)
+ * if only one VSI has been added till now
+ * Allocate a new VSI list and add two VSIs
+ * to this list using switch rule command
+ * Update the previously created switch rule with the
+ * newly created VSI list id
+ * if a VSI list was previously created
+ * Add the new VSI to the previously created VSI list set
+ * using the update switch rule command
+ */
+static enum ice_status
+ice_handle_vsi_list_mgmt(struct ice_hw *hw,
+ struct ice_fltr_mgmt_list_entry *m_entry,
+ struct ice_fltr_info *cur_fltr,
+ struct ice_fltr_info *new_fltr)
+{
+ enum ice_status status = 0;
+ u16 vsi_list_id = 0;
+
+ if ((cur_fltr->fltr_act == ICE_FWD_TO_Q ||
+ cur_fltr->fltr_act == ICE_FWD_TO_QGRP))
+ return ICE_ERR_NOT_IMPL;
+
+ if ((new_fltr->fltr_act == ICE_FWD_TO_Q ||
+ new_fltr->fltr_act == ICE_FWD_TO_QGRP) &&
+ (cur_fltr->fltr_act == ICE_FWD_TO_VSI ||
+ cur_fltr->fltr_act == ICE_FWD_TO_VSI_LIST))
+ return ICE_ERR_NOT_IMPL;
+
+ if (m_entry->vsi_count < 2 && !m_entry->vsi_list_info) {
+ /* Only one entry existed in the mapping and it was not already
+ * a part of a VSI list. So, create a VSI list with the old and
+ * new VSIs.
+ */
+ u16 vsi_id_arr[2];
+ u16 fltr_rule;
+
+ /* A rule already exists with the new VSI being added */
+ if (cur_fltr->fwd_id.vsi_id == new_fltr->fwd_id.vsi_id)
+ return ICE_ERR_ALREADY_EXISTS;
+
+ vsi_id_arr[0] = cur_fltr->fwd_id.vsi_id;
+ vsi_id_arr[1] = new_fltr->fwd_id.vsi_id;
+ status = ice_create_vsi_list_rule(hw, &vsi_id_arr[0], 2,
+ &vsi_list_id,
+ new_fltr->lkup_type);
+ if (status)
+ return status;
+
+ fltr_rule = cur_fltr->fltr_rule_id;
+ /* Update the previous switch rule of "MAC forward to VSI" to
+ * "MAC fwd to VSI list"
+ */
+ status = ice_update_pkt_fwd_rule(hw, fltr_rule, vsi_list_id,
+ *new_fltr);
+ if (status)
+ return status;
+
+ cur_fltr->fwd_id.vsi_list_id = vsi_list_id;
+ cur_fltr->fltr_act = ICE_FWD_TO_VSI_LIST;
+ m_entry->vsi_list_info =
+ ice_create_vsi_list_map(hw, &vsi_id_arr[0], 2,
+ vsi_list_id);
+
+ /* If this entry was large action then the large action needs
+ * to be updated to point to FWD to VSI list
+ */
+ if (m_entry->sw_marker_id != ICE_INVAL_SW_MARKER_ID)
+ status =
+ ice_add_marker_act(hw, m_entry,
+ m_entry->sw_marker_id,
+ m_entry->lg_act_idx);
+ } else {
+ u16 vsi_id = new_fltr->fwd_id.vsi_id;
+ enum ice_adminq_opc opcode;
+
+ /* A rule already exists with the new VSI being added */
+ if (test_bit(vsi_id, m_entry->vsi_list_info->vsi_map))
+ return 0;
+
+ /* Update the previously created VSI list set with
+ * the new VSI id passed in
+ */
+ vsi_list_id = cur_fltr->fwd_id.vsi_list_id;
+ opcode = ice_aqc_opc_update_sw_rules;
+
+ status = ice_update_vsi_list_rule(hw, &vsi_id, 1, vsi_list_id,
+ false, opcode,
+ new_fltr->lkup_type);
+ /* update VSI list mapping info with new VSI id */
+ if (!status)
+ set_bit(vsi_id, m_entry->vsi_list_info->vsi_map);
+ }
+ if (!status)
+ m_entry->vsi_count++;
+ return status;
+}
+
+/**
+ * ice_find_mac_entry
+ * @hw: pointer to the hardware structure
+ * @mac_addr: MAC address to search for
+ *
+ * Helper function to search for a MAC entry using a given MAC address
+ * Returns pointer to the entry if found.
+ */
+static struct ice_fltr_mgmt_list_entry *
+ice_find_mac_entry(struct ice_hw *hw, u8 *mac_addr)
+{
+ struct ice_fltr_mgmt_list_entry *m_list_itr, *mac_ret = NULL;
+ struct ice_switch_info *sw = hw->switch_info;
+
+ mutex_lock(&sw->mac_list_lock);
+ list_for_each_entry(m_list_itr, &sw->mac_list_head, list_entry) {
+ u8 *buf = &m_list_itr->fltr_info.l_data.mac.mac_addr[0];
+
+ if (ether_addr_equal(buf, mac_addr)) {
+ mac_ret = m_list_itr;
+ break;
+ }
+ }
+ mutex_unlock(&sw->mac_list_lock);
+ return mac_ret;
+}
+
+/**
+ * ice_add_shared_mac - Add one MAC shared filter rule
+ * @hw: pointer to the hardware structure
+ * @f_entry: structure containing MAC forwarding information
+ *
+ * Adds or updates the book keeping list for the MAC addresses
+ */
+static enum ice_status
+ice_add_shared_mac(struct ice_hw *hw, struct ice_fltr_list_entry *f_entry)
+{
+ struct ice_fltr_info *new_fltr, *cur_fltr;
+ struct ice_fltr_mgmt_list_entry *m_entry;
+
+ new_fltr = &f_entry->fltr_info;
+
+ m_entry = ice_find_mac_entry(hw, &new_fltr->l_data.mac.mac_addr[0]);
+ if (!m_entry)
+ return ice_create_pkt_fwd_rule(hw, f_entry);
+
+ cur_fltr = &m_entry->fltr_info;
+
+ return ice_handle_vsi_list_mgmt(hw, m_entry, cur_fltr, new_fltr);
+}
+
+/**
+ * ice_add_mac - Add a MAC address based filter rule
+ * @hw: pointer to the hardware structure
+ * @m_list: list of MAC addresses and forwarding information
+ *
+ * IMPORTANT: When the ucast_shared flag is set to false and m_list has
+ * multiple unicast addresses, the function assumes that all the
+ * addresses are unique in a given add_mac call. It doesn't
+ * check for duplicates in this case, removing duplicates from a given
+ * list should be taken care of in the caller of this function.
+ */
+enum ice_status
+ice_add_mac(struct ice_hw *hw, struct list_head *m_list)
+{
+ struct ice_aqc_sw_rules_elem *s_rule, *r_iter;
+ struct ice_fltr_list_entry *m_list_itr;
+ u16 elem_sent, total_elem_left;
+ enum ice_status status = 0;
+ u16 num_unicast = 0;
+ u16 s_rule_size;
+
+ if (!m_list || !hw)
+ return ICE_ERR_PARAM;
+
+ list_for_each_entry(m_list_itr, m_list, list_entry) {
+ u8 *add = &m_list_itr->fltr_info.l_data.mac.mac_addr[0];
+
+ if (m_list_itr->fltr_info.lkup_type != ICE_SW_LKUP_MAC)
+ return ICE_ERR_PARAM;
+ if (is_zero_ether_addr(add))
+ return ICE_ERR_PARAM;
+ if (is_unicast_ether_addr(add) && !hw->ucast_shared) {
+ /* Don't overwrite the unicast address */
+ if (ice_find_mac_entry(hw, add))
+ return ICE_ERR_ALREADY_EXISTS;
+ num_unicast++;
+ } else if (is_multicast_ether_addr(add) ||
+ (is_unicast_ether_addr(add) && hw->ucast_shared)) {
+ status = ice_add_shared_mac(hw, m_list_itr);
+ if (status) {
+ m_list_itr->status = ICE_FLTR_STATUS_FW_FAIL;
+ return status;
+ }
+ m_list_itr->status = ICE_FLTR_STATUS_FW_SUCCESS;
+ }
+ }
+
+ /* Exit if no suitable entries were found for adding bulk switch rule */
+ if (!num_unicast)
+ return 0;
+
+ /* Allocate switch rule buffer for the bulk update for unicast */
+ s_rule_size = ICE_SW_RULE_RX_TX_ETH_HDR_SIZE;
+ s_rule = devm_kcalloc(ice_hw_to_dev(hw), num_unicast, s_rule_size,
+ GFP_KERNEL);
+ if (!s_rule)
+ return ICE_ERR_NO_MEMORY;
+
+ r_iter = s_rule;
+ list_for_each_entry(m_list_itr, m_list, list_entry) {
+ struct ice_fltr_info *f_info = &m_list_itr->fltr_info;
+ u8 *addr = &f_info->l_data.mac.mac_addr[0];
+
+ if (is_unicast_ether_addr(addr)) {
+ ice_fill_sw_rule(hw, &m_list_itr->fltr_info,
+ r_iter, ice_aqc_opc_add_sw_rules);
+ r_iter = (struct ice_aqc_sw_rules_elem *)
+ ((u8 *)r_iter + s_rule_size);
+ }
+ }
+
+ /* Call AQ bulk switch rule update for all unicast addresses */
+ r_iter = s_rule;
+ /* Call AQ switch rule in AQ_MAX chunk */
+ for (total_elem_left = num_unicast; total_elem_left > 0;
+ total_elem_left -= elem_sent) {
+ struct ice_aqc_sw_rules_elem *entry = r_iter;
+
+ elem_sent = min(total_elem_left,
+ (u16)(ICE_AQ_MAX_BUF_LEN / s_rule_size));
+ status = ice_aq_sw_rules(hw, entry, elem_sent * s_rule_size,
+ elem_sent, ice_aqc_opc_add_sw_rules,
+ NULL);
+ if (status)
+ goto ice_add_mac_exit;
+ r_iter = (struct ice_aqc_sw_rules_elem *)
+ ((u8 *)r_iter + (elem_sent * s_rule_size));
+ }
+
+ /* Fill up rule id based on the value returned from FW */
+ r_iter = s_rule;
+ list_for_each_entry(m_list_itr, m_list, list_entry) {
+ struct ice_fltr_info *f_info = &m_list_itr->fltr_info;
+ u8 *addr = &f_info->l_data.mac.mac_addr[0];
+ struct ice_switch_info *sw = hw->switch_info;
+ struct ice_fltr_mgmt_list_entry *fm_entry;
+
+ if (is_unicast_ether_addr(addr)) {
+ f_info->fltr_rule_id =
+ le16_to_cpu(r_iter->pdata.lkup_tx_rx.index);
+ f_info->fltr_act = ICE_FWD_TO_VSI;
+ /* Create an entry to track this MAC address */
+ fm_entry = devm_kzalloc(ice_hw_to_dev(hw),
+ sizeof(*fm_entry), GFP_KERNEL);
+ if (!fm_entry) {
+ status = ICE_ERR_NO_MEMORY;
+ goto ice_add_mac_exit;
+ }
+ fm_entry->fltr_info = *f_info;
+ fm_entry->vsi_count = 1;
+ /* The book keeping entries will get removed when
+ * base driver calls remove filter AQ command
+ */
+ mutex_lock(&sw->mac_list_lock);
+ list_add(&fm_entry->list_entry, &sw->mac_list_head);
+ mutex_unlock(&sw->mac_list_lock);
+
+ r_iter = (struct ice_aqc_sw_rules_elem *)
+ ((u8 *)r_iter + s_rule_size);
+ }
+ }
+
+ice_add_mac_exit:
+ devm_kfree(ice_hw_to_dev(hw), s_rule);
+ return status;
+}
+
+/**
+ * ice_find_vlan_entry
+ * @hw: pointer to the hardware structure
+ * @vlan_id: VLAN id to search for
+ *
+ * Helper function to search for a VLAN entry using a given VLAN id
+ * Returns pointer to the entry if found.
+ */
+static struct ice_fltr_mgmt_list_entry *
+ice_find_vlan_entry(struct ice_hw *hw, u16 vlan_id)
+{
+ struct ice_fltr_mgmt_list_entry *vlan_list_itr, *vlan_ret = NULL;
+ struct ice_switch_info *sw = hw->switch_info;
+
+ mutex_lock(&sw->vlan_list_lock);
+ list_for_each_entry(vlan_list_itr, &sw->vlan_list_head, list_entry)
+ if (vlan_list_itr->fltr_info.l_data.vlan.vlan_id == vlan_id) {
+ vlan_ret = vlan_list_itr;
+ break;
+ }
+
+ mutex_unlock(&sw->vlan_list_lock);
+ return vlan_ret;
+}
+
+/**
+ * ice_add_vlan_internal - Add one VLAN based filter rule
+ * @hw: pointer to the hardware structure
+ * @f_entry: filter entry containing one VLAN information
+ */
+static enum ice_status
+ice_add_vlan_internal(struct ice_hw *hw, struct ice_fltr_list_entry *f_entry)
+{
+ struct ice_fltr_info *new_fltr, *cur_fltr;
+ struct ice_fltr_mgmt_list_entry *v_list_itr;
+ u16 vlan_id;
+
+ new_fltr = &f_entry->fltr_info;
+ /* VLAN id should only be 12 bits */
+ if (new_fltr->l_data.vlan.vlan_id > ICE_MAX_VLAN_ID)
+ return ICE_ERR_PARAM;
+
+ vlan_id = new_fltr->l_data.vlan.vlan_id;
+ v_list_itr = ice_find_vlan_entry(hw, vlan_id);
+ if (!v_list_itr) {
+ u16 vsi_id = ICE_VSI_INVAL_ID;
+ enum ice_status status;
+ u16 vsi_list_id = 0;
+
+ if (new_fltr->fltr_act == ICE_FWD_TO_VSI) {
+ enum ice_sw_lkup_type lkup_type = new_fltr->lkup_type;
+
+ /* All VLAN pruning rules use a VSI list.
+ * Convert the action to forwarding to a VSI list.
+ */
+ vsi_id = new_fltr->fwd_id.vsi_id;
+ status = ice_create_vsi_list_rule(hw, &vsi_id, 1,
+ &vsi_list_id,
+ lkup_type);
+ if (status)
+ return status;
+ new_fltr->fltr_act = ICE_FWD_TO_VSI_LIST;
+ new_fltr->fwd_id.vsi_list_id = vsi_list_id;
+ }
+
+ status = ice_create_pkt_fwd_rule(hw, f_entry);
+ if (!status && vsi_id != ICE_VSI_INVAL_ID) {
+ v_list_itr = ice_find_vlan_entry(hw, vlan_id);
+ if (!v_list_itr)
+ return ICE_ERR_DOES_NOT_EXIST;
+ v_list_itr->vsi_list_info =
+ ice_create_vsi_list_map(hw, &vsi_id, 1,
+ vsi_list_id);
+ }
+
+ return status;
+ }
+
+ cur_fltr = &v_list_itr->fltr_info;
+ return ice_handle_vsi_list_mgmt(hw, v_list_itr, cur_fltr, new_fltr);
+}
+
+/**
+ * ice_add_vlan - Add VLAN based filter rule
+ * @hw: pointer to the hardware structure
+ * @v_list: list of VLAN entries and forwarding information
+ */
+enum ice_status
+ice_add_vlan(struct ice_hw *hw, struct list_head *v_list)
+{
+ struct ice_fltr_list_entry *v_list_itr;
+
+ if (!v_list || !hw)
+ return ICE_ERR_PARAM;
+
+ list_for_each_entry(v_list_itr, v_list, list_entry) {
+ enum ice_status status;
+
+ if (v_list_itr->fltr_info.lkup_type != ICE_SW_LKUP_VLAN)
+ return ICE_ERR_PARAM;
+
+ status = ice_add_vlan_internal(hw, v_list_itr);
+ if (status) {
+ v_list_itr->status = ICE_FLTR_STATUS_FW_FAIL;
+ return status;
+ }
+ v_list_itr->status = ICE_FLTR_STATUS_FW_SUCCESS;
+ }
+ return 0;
+}
+
+/**
+ * ice_remove_vsi_list_rule
+ * @hw: pointer to the hardware structure
+ * @vsi_list_id: VSI list id generated as part of allocate resource
+ * @lkup_type: switch rule filter lookup type
+ */
+static enum ice_status
+ice_remove_vsi_list_rule(struct ice_hw *hw, u16 vsi_list_id,
+ enum ice_sw_lkup_type lkup_type)
+{
+ struct ice_aqc_sw_rules_elem *s_rule;
+ enum ice_status status;
+ u16 s_rule_size;
+
+ s_rule_size = (u16)ICE_SW_RULE_VSI_LIST_SIZE(0);
+ s_rule = devm_kzalloc(ice_hw_to_dev(hw), s_rule_size, GFP_KERNEL);
+ if (!s_rule)
+ return ICE_ERR_NO_MEMORY;
+
+ s_rule->type = cpu_to_le16(ICE_AQC_SW_RULES_T_VSI_LIST_CLEAR);
+ s_rule->pdata.vsi_list.index = cpu_to_le16(vsi_list_id);
+ /* FW expects number of VSIs in vsi_list resource to be 0 for clear
+ * command. Since memory is zero'ed out during initialization, it's not
+ * necessary to explicitly initialize the variable to 0.
+ */
+
+ status = ice_aq_sw_rules(hw, s_rule, s_rule_size, 1,
+ ice_aqc_opc_remove_sw_rules, NULL);
+ if (!status)
+ /* Free the vsi_list resource that we allocated */
+ status = ice_aq_alloc_free_vsi_list(hw, &vsi_list_id, lkup_type,
+ ice_aqc_opc_free_res);
+
+ devm_kfree(ice_hw_to_dev(hw), s_rule);
+ return status;
+}
+
+/**
+ * ice_handle_rem_vsi_list_mgmt
+ * @hw: pointer to the hardware structure
+ * @vsi_id: ID of the VSI to remove
+ * @fm_list_itr: filter management entry for which the VSI list management
+ * needs to be done
+ */
+static enum ice_status
+ice_handle_rem_vsi_list_mgmt(struct ice_hw *hw, u16 vsi_id,
+ struct ice_fltr_mgmt_list_entry *fm_list_itr)
+{
+ struct ice_switch_info *sw = hw->switch_info;
+ enum ice_status status = 0;
+ enum ice_sw_lkup_type lkup_type;
+ bool is_last_elem = true;
+ bool conv_list = false;
+ bool del_list = false;
+ u16 vsi_list_id;
+
+ lkup_type = fm_list_itr->fltr_info.lkup_type;
+ vsi_list_id = fm_list_itr->fltr_info.fwd_id.vsi_list_id;
+
+ if (fm_list_itr->vsi_count > 1) {
+ status = ice_update_vsi_list_rule(hw, &vsi_id, 1, vsi_list_id,
+ true,
+ ice_aqc_opc_update_sw_rules,
+ lkup_type);
+ if (status)
+ return status;
+ fm_list_itr->vsi_count--;
+ is_last_elem = false;
+ clear_bit(vsi_id, fm_list_itr->vsi_list_info->vsi_map);
+ }
+
+ /* For non-VLAN rules that forward packets to a VSI list, convert them
+ * to forwarding packets to a VSI if there is only one VSI left in the
+ * list. Unused lists are then removed.
+ * VLAN rules need to use VSI lists even with only one VSI.
+ */
+ if (fm_list_itr->fltr_info.fltr_act == ICE_FWD_TO_VSI_LIST) {
+ if (lkup_type == ICE_SW_LKUP_VLAN) {
+ del_list = is_last_elem;
+ } else if (fm_list_itr->vsi_count == 1) {
+ conv_list = true;
+ del_list = true;
+ }
+ }
+
+ if (del_list) {
+ /* Remove the VSI list since it is no longer used */
+ struct ice_vsi_list_map_info *vsi_list_info =
+ fm_list_itr->vsi_list_info;
+
+ status = ice_remove_vsi_list_rule(hw, vsi_list_id, lkup_type);
+ if (status)
+ return status;
+
+ if (conv_list) {
+ u16 rem_vsi_id;
+
+ rem_vsi_id = find_first_bit(vsi_list_info->vsi_map,
+ ICE_MAX_VSI);
+
+ /* Error out when the expected last element is not in
+ * the VSI list map
+ */
+ if (rem_vsi_id == ICE_MAX_VSI)
+ return ICE_ERR_OUT_OF_RANGE;
+
+ /* Change the list entry action from VSI_LIST to VSI */
+ fm_list_itr->fltr_info.fltr_act = ICE_FWD_TO_VSI;
+ fm_list_itr->fltr_info.fwd_id.vsi_id = rem_vsi_id;
+ }
+
+ list_del(&vsi_list_info->list_entry);
+ devm_kfree(ice_hw_to_dev(hw), vsi_list_info);
+ fm_list_itr->vsi_list_info = NULL;
+ }
+
+ if (conv_list) {
+ /* Convert the rule's forward action to forwarding packets to
+ * a VSI
+ */
+ struct ice_aqc_sw_rules_elem *s_rule;
+
+ s_rule = devm_kzalloc(ice_hw_to_dev(hw),
+ ICE_SW_RULE_RX_TX_ETH_HDR_SIZE,
+ GFP_KERNEL);
+ if (!s_rule)
+ return ICE_ERR_NO_MEMORY;
+
+ ice_fill_sw_rule(hw, &fm_list_itr->fltr_info, s_rule,
+ ice_aqc_opc_update_sw_rules);
+
+ s_rule->pdata.lkup_tx_rx.index =
+ cpu_to_le16(fm_list_itr->fltr_info.fltr_rule_id);
+
+ status = ice_aq_sw_rules(hw, s_rule,
+ ICE_SW_RULE_RX_TX_ETH_HDR_SIZE, 1,
+ ice_aqc_opc_update_sw_rules, NULL);
+ devm_kfree(ice_hw_to_dev(hw), s_rule);
+ if (status)
+ return status;
+ }
+
+ if (is_last_elem) {
+ /* Remove the lookup rule */
+ struct ice_aqc_sw_rules_elem *s_rule;
+
+ s_rule = devm_kzalloc(ice_hw_to_dev(hw),
+ ICE_SW_RULE_RX_TX_NO_HDR_SIZE,
+ GFP_KERNEL);
+ if (!s_rule)
+ return ICE_ERR_NO_MEMORY;
+
+ ice_fill_sw_rule(hw, &fm_list_itr->fltr_info, s_rule,
+ ice_aqc_opc_remove_sw_rules);
+
+ status = ice_aq_sw_rules(hw, s_rule,
+ ICE_SW_RULE_RX_TX_NO_HDR_SIZE, 1,
+ ice_aqc_opc_remove_sw_rules, NULL);
+ if (status)
+ return status;
+
+ /* Remove a book keeping entry from the MAC address list */
+ mutex_lock(&sw->mac_list_lock);
+ list_del(&fm_list_itr->list_entry);
+ mutex_unlock(&sw->mac_list_lock);
+ devm_kfree(ice_hw_to_dev(hw), fm_list_itr);
+ devm_kfree(ice_hw_to_dev(hw), s_rule);
+ }
+ return status;
+}
+
+/**
+ * ice_remove_mac_entry
+ * @hw: pointer to the hardware structure
+ * @f_entry: structure containing MAC forwarding information
+ */
+static enum ice_status
+ice_remove_mac_entry(struct ice_hw *hw, struct ice_fltr_list_entry *f_entry)
+{
+ struct ice_fltr_mgmt_list_entry *m_entry;
+ u16 vsi_id;
+ u8 *add;
+
+ add = &f_entry->fltr_info.l_data.mac.mac_addr[0];
+
+ m_entry = ice_find_mac_entry(hw, add);
+ if (!m_entry)
+ return ICE_ERR_PARAM;
+
+ vsi_id = f_entry->fltr_info.fwd_id.vsi_id;
+ return ice_handle_rem_vsi_list_mgmt(hw, vsi_id, m_entry);
+}
+
+/**
+ * ice_remove_mac - remove a MAC address based filter rule
+ * @hw: pointer to the hardware structure
+ * @m_list: list of MAC addresses and forwarding information
+ *
+ * This function removes either a MAC filter rule or a specific VSI from a
+ * VSI list for a multicast MAC address.
+ *
+ * Returns ICE_ERR_DOES_NOT_EXIST if a given entry was not added by
+ * ice_add_mac. Caller should be aware that this call will only work if all
+ * the entries passed into m_list were added previously. It will not attempt to
+ * do a partial remove of entries that were found.
+ */
+enum ice_status
+ice_remove_mac(struct ice_hw *hw, struct list_head *m_list)
+{
+ struct ice_aqc_sw_rules_elem *s_rule, *r_iter;
+ u8 s_rule_size = ICE_SW_RULE_RX_TX_NO_HDR_SIZE;
+ struct ice_switch_info *sw = hw->switch_info;
+ struct ice_fltr_mgmt_list_entry *m_entry;
+ struct ice_fltr_list_entry *m_list_itr;
+ u16 elem_sent, total_elem_left;
+ enum ice_status status = 0;
+ u16 num_unicast = 0;
+
+ if (!m_list)
+ return ICE_ERR_PARAM;
+
+ list_for_each_entry(m_list_itr, m_list, list_entry) {
+ u8 *addr = m_list_itr->fltr_info.l_data.mac.mac_addr;
+
+ if (is_unicast_ether_addr(addr) && !hw->ucast_shared)
+ num_unicast++;
+ else if (is_multicast_ether_addr(addr) ||
+ (is_unicast_ether_addr(addr) && hw->ucast_shared))
+ ice_remove_mac_entry(hw, m_list_itr);
+ }
+
+ /* Exit if no unicast addresses found. Multicast switch rules
+ * were added individually
+ */
+ if (!num_unicast)
+ return 0;
+
+ /* Allocate switch rule buffer for the bulk update for unicast */
+ s_rule = devm_kcalloc(ice_hw_to_dev(hw), num_unicast, s_rule_size,
+ GFP_KERNEL);
+ if (!s_rule)
+ return ICE_ERR_NO_MEMORY;
+
+ r_iter = s_rule;
+ list_for_each_entry(m_list_itr, m_list, list_entry) {
+ u8 *addr = m_list_itr->fltr_info.l_data.mac.mac_addr;
+
+ if (is_unicast_ether_addr(addr)) {
+ m_entry = ice_find_mac_entry(hw, addr);
+ if (!m_entry) {
+ status = ICE_ERR_DOES_NOT_EXIST;
+ goto ice_remove_mac_exit;
+ }
+
+ ice_fill_sw_rule(hw, &m_entry->fltr_info,
+ r_iter, ice_aqc_opc_remove_sw_rules);
+ r_iter = (struct ice_aqc_sw_rules_elem *)
+ ((u8 *)r_iter + s_rule_size);
+ }
+ }
+
+ /* Call AQ bulk switch rule update for all unicast addresses */
+ r_iter = s_rule;
+ /* Call AQ switch rule in AQ_MAX chunk */
+ for (total_elem_left = num_unicast; total_elem_left > 0;
+ total_elem_left -= elem_sent) {
+ struct ice_aqc_sw_rules_elem *entry = r_iter;
+
+ elem_sent = min(total_elem_left,
+ (u16)(ICE_AQ_MAX_BUF_LEN / s_rule_size));
+ status = ice_aq_sw_rules(hw, entry, elem_sent * s_rule_size,
+ elem_sent, ice_aqc_opc_remove_sw_rules,
+ NULL);
+ if (status)
+ break;
+ r_iter = (struct ice_aqc_sw_rules_elem *)
+ ((u8 *)r_iter + s_rule_size);
+ }
+
+ list_for_each_entry(m_list_itr, m_list, list_entry) {
+ u8 *addr = m_list_itr->fltr_info.l_data.mac.mac_addr;
+
+ if (is_unicast_ether_addr(addr)) {
+ m_entry = ice_find_mac_entry(hw, addr);
+ if (!m_entry)
+ return ICE_ERR_OUT_OF_RANGE;
+ mutex_lock(&sw->mac_list_lock);
+ list_del(&m_entry->list_entry);
+ mutex_unlock(&sw->mac_list_lock);
+ devm_kfree(ice_hw_to_dev(hw), m_entry);
+ }
+ }
+
+ice_remove_mac_exit:
+ devm_kfree(ice_hw_to_dev(hw), s_rule);
+ return status;
+}
+
+/**
+ * ice_cfg_dflt_vsi - add filter rule to set/unset given VSI as default
+ * VSI for the switch (represented by swid)
+ * @hw: pointer to the hardware structure
+ * @vsi_id: number of VSI to set as default
+ * @set: true to add the above mentioned switch rule, false to remove it
+ * @direction: ICE_FLTR_RX or ICE_FLTR_TX
+ */
+enum ice_status
+ice_cfg_dflt_vsi(struct ice_hw *hw, u16 vsi_id, bool set, u8 direction)
+{
+ struct ice_aqc_sw_rules_elem *s_rule;
+ struct ice_fltr_info f_info;
+ enum ice_adminq_opc opcode;
+ enum ice_status status;
+ u16 s_rule_size;
+
+ s_rule_size = set ? ICE_SW_RULE_RX_TX_ETH_HDR_SIZE :
+ ICE_SW_RULE_RX_TX_NO_HDR_SIZE;
+ s_rule = devm_kzalloc(ice_hw_to_dev(hw), s_rule_size, GFP_KERNEL);
+ if (!s_rule)
+ return ICE_ERR_NO_MEMORY;
+
+ memset(&f_info, 0, sizeof(f_info));
+
+ f_info.lkup_type = ICE_SW_LKUP_DFLT;
+ f_info.flag = direction;
+ f_info.fltr_act = ICE_FWD_TO_VSI;
+ f_info.fwd_id.vsi_id = vsi_id;
+
+ if (f_info.flag & ICE_FLTR_RX) {
+ f_info.src = hw->port_info->lport;
+ if (!set)
+ f_info.fltr_rule_id =
+ hw->port_info->dflt_rx_vsi_rule_id;
+ } else if (f_info.flag & ICE_FLTR_TX) {
+ f_info.src = vsi_id;
+ if (!set)
+ f_info.fltr_rule_id =
+ hw->port_info->dflt_tx_vsi_rule_id;
+ }
+
+ if (set)
+ opcode = ice_aqc_opc_add_sw_rules;
+ else
+ opcode = ice_aqc_opc_remove_sw_rules;
+
+ ice_fill_sw_rule(hw, &f_info, s_rule, opcode);
+
+ status = ice_aq_sw_rules(hw, s_rule, s_rule_size, 1, opcode, NULL);
+ if (status || !(f_info.flag & ICE_FLTR_TX_RX))
+ goto out;
+ if (set) {
+ u16 index = le16_to_cpu(s_rule->pdata.lkup_tx_rx.index);
+
+ if (f_info.flag & ICE_FLTR_TX) {
+ hw->port_info->dflt_tx_vsi_num = vsi_id;
+ hw->port_info->dflt_tx_vsi_rule_id = index;
+ } else if (f_info.flag & ICE_FLTR_RX) {
+ hw->port_info->dflt_rx_vsi_num = vsi_id;
+ hw->port_info->dflt_rx_vsi_rule_id = index;
+ }
+ } else {
+ if (f_info.flag & ICE_FLTR_TX) {
+ hw->port_info->dflt_tx_vsi_num = ICE_DFLT_VSI_INVAL;
+ hw->port_info->dflt_tx_vsi_rule_id = ICE_INVAL_ACT;
+ } else if (f_info.flag & ICE_FLTR_RX) {
+ hw->port_info->dflt_rx_vsi_num = ICE_DFLT_VSI_INVAL;
+ hw->port_info->dflt_rx_vsi_rule_id = ICE_INVAL_ACT;
+ }
+ }
+
+out:
+ devm_kfree(ice_hw_to_dev(hw), s_rule);
+ return status;
+}
+
+/**
+ * ice_remove_vlan_internal - Remove one VLAN based filter rule
+ * @hw: pointer to the hardware structure
+ * @f_entry: filter entry containing one VLAN information
+ */
+static enum ice_status
+ice_remove_vlan_internal(struct ice_hw *hw,
+ struct ice_fltr_list_entry *f_entry)
+{
+ struct ice_fltr_info *new_fltr;
+ struct ice_fltr_mgmt_list_entry *v_list_elem;
+ u16 vsi_id;
+
+ new_fltr = &f_entry->fltr_info;
+
+ v_list_elem = ice_find_vlan_entry(hw, new_fltr->l_data.vlan.vlan_id);
+ if (!v_list_elem)
+ return ICE_ERR_PARAM;
+
+ vsi_id = f_entry->fltr_info.fwd_id.vsi_id;
+ return ice_handle_rem_vsi_list_mgmt(hw, vsi_id, v_list_elem);
+}
+
+/**
+ * ice_remove_vlan - Remove VLAN based filter rule
+ * @hw: pointer to the hardware structure
+ * @v_list: list of VLAN entries and forwarding information
+ */
+enum ice_status
+ice_remove_vlan(struct ice_hw *hw, struct list_head *v_list)
+{
+ struct ice_fltr_list_entry *v_list_itr;
+ enum ice_status status = 0;
+
+ if (!v_list || !hw)
+ return ICE_ERR_PARAM;
+
+ list_for_each_entry(v_list_itr, v_list, list_entry) {
+ status = ice_remove_vlan_internal(hw, v_list_itr);
+ if (status) {
+ v_list_itr->status = ICE_FLTR_STATUS_FW_FAIL;
+ return status;
+ }
+ v_list_itr->status = ICE_FLTR_STATUS_FW_SUCCESS;
+ }
+ return status;
+}
+
+/**
+ * ice_add_to_vsi_fltr_list - Add VSI filters to the list
+ * @hw: pointer to the hardware structure
+ * @vsi_id: ID of VSI to remove filters from
+ * @lkup_list_head: pointer to the list that has certain lookup type filters
+ * @vsi_list_head: pointer to the list pertaining to VSI with vsi_id
+ */
+static enum ice_status
+ice_add_to_vsi_fltr_list(struct ice_hw *hw, u16 vsi_id,
+ struct list_head *lkup_list_head,
+ struct list_head *vsi_list_head)
+{
+ struct ice_fltr_mgmt_list_entry *fm_entry;
+
+ /* check to make sure VSI id is valid and within boundary */
+ if (vsi_id >=
+ (sizeof(fm_entry->vsi_list_info->vsi_map) * BITS_PER_BYTE - 1))
+ return ICE_ERR_PARAM;
+
+ list_for_each_entry(fm_entry, lkup_list_head, list_entry) {
+ struct ice_fltr_info *fi;
+
+ fi = &fm_entry->fltr_info;
+ if ((fi->fltr_act == ICE_FWD_TO_VSI &&
+ fi->fwd_id.vsi_id == vsi_id) ||
+ (fi->fltr_act == ICE_FWD_TO_VSI_LIST &&
+ (test_bit(vsi_id, fm_entry->vsi_list_info->vsi_map)))) {
+ struct ice_fltr_list_entry *tmp;
+
+ /* this memory is freed up in the caller function
+ * ice_remove_vsi_lkup_fltr() once filters for
+ * this VSI are removed
+ */
+ tmp = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*tmp),
+ GFP_KERNEL);
+ if (!tmp)
+ return ICE_ERR_NO_MEMORY;
+
+ memcpy(&tmp->fltr_info, fi, sizeof(*fi));
+
+ /* Expected below fields to be set to ICE_FWD_TO_VSI and
+ * the particular VSI id since we are only removing this
+ * one VSI
+ */
+ if (fi->fltr_act == ICE_FWD_TO_VSI_LIST) {
+ tmp->fltr_info.fltr_act = ICE_FWD_TO_VSI;
+ tmp->fltr_info.fwd_id.vsi_id = vsi_id;
+ }
+
+ list_add(&tmp->list_entry, vsi_list_head);
+ }
+ }
+ return 0;
+}
+
+/**
+ * ice_remove_vsi_lkup_fltr - Remove lookup type filters for a VSI
+ * @hw: pointer to the hardware structure
+ * @vsi_id: ID of VSI to remove filters from
+ * @lkup: switch rule filter lookup type
+ */
+static void
+ice_remove_vsi_lkup_fltr(struct ice_hw *hw, u16 vsi_id,
+ enum ice_sw_lkup_type lkup)
+{
+ struct ice_switch_info *sw = hw->switch_info;
+ struct ice_fltr_list_entry *fm_entry;
+ struct list_head remove_list_head;
+ struct ice_fltr_list_entry *tmp;
+ enum ice_status status;
+
+ INIT_LIST_HEAD(&remove_list_head);
+ switch (lkup) {
+ case ICE_SW_LKUP_MAC:
+ mutex_lock(&sw->mac_list_lock);
+ status = ice_add_to_vsi_fltr_list(hw, vsi_id,
+ &sw->mac_list_head,
+ &remove_list_head);
+ mutex_unlock(&sw->mac_list_lock);
+ if (!status) {
+ ice_remove_mac(hw, &remove_list_head);
+ goto free_fltr_list;
+ }
+ break;
+ case ICE_SW_LKUP_VLAN:
+ mutex_lock(&sw->vlan_list_lock);
+ status = ice_add_to_vsi_fltr_list(hw, vsi_id,
+ &sw->vlan_list_head,
+ &remove_list_head);
+ mutex_unlock(&sw->vlan_list_lock);
+ if (!status) {
+ ice_remove_vlan(hw, &remove_list_head);
+ goto free_fltr_list;
+ }
+ break;
+ case ICE_SW_LKUP_MAC_VLAN:
+ case ICE_SW_LKUP_ETHERTYPE:
+ case ICE_SW_LKUP_ETHERTYPE_MAC:
+ case ICE_SW_LKUP_PROMISC:
+ case ICE_SW_LKUP_PROMISC_VLAN:
+ case ICE_SW_LKUP_DFLT:
+ ice_debug(hw, ICE_DBG_SW,
+ "Remove filters for this lookup type hasn't been implemented yet\n");
+ break;
+ }
+
+ return;
+free_fltr_list:
+ list_for_each_entry_safe(fm_entry, tmp, &remove_list_head, list_entry) {
+ list_del(&fm_entry->list_entry);
+ devm_kfree(ice_hw_to_dev(hw), fm_entry);
+ }
+}
+
+/**
+ * ice_remove_vsi_fltr - Remove all filters for a VSI
+ * @hw: pointer to the hardware structure
+ * @vsi_id: ID of VSI to remove filters from
+ */
+void ice_remove_vsi_fltr(struct ice_hw *hw, u16 vsi_id)
+{
+ ice_remove_vsi_lkup_fltr(hw, vsi_id, ICE_SW_LKUP_MAC);
+ ice_remove_vsi_lkup_fltr(hw, vsi_id, ICE_SW_LKUP_MAC_VLAN);
+ ice_remove_vsi_lkup_fltr(hw, vsi_id, ICE_SW_LKUP_PROMISC);
+ ice_remove_vsi_lkup_fltr(hw, vsi_id, ICE_SW_LKUP_VLAN);
+ ice_remove_vsi_lkup_fltr(hw, vsi_id, ICE_SW_LKUP_DFLT);
+ ice_remove_vsi_lkup_fltr(hw, vsi_id, ICE_SW_LKUP_ETHERTYPE);
+ ice_remove_vsi_lkup_fltr(hw, vsi_id, ICE_SW_LKUP_ETHERTYPE_MAC);
+ ice_remove_vsi_lkup_fltr(hw, vsi_id, ICE_SW_LKUP_PROMISC_VLAN);
+}
diff --git a/drivers/net/ethernet/intel/ice/ice_switch.h b/drivers/net/ethernet/intel/ice/ice_switch.h
new file mode 100644
index 000000000000..6f4a0d159dbf
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_switch.h
@@ -0,0 +1,161 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018, Intel Corporation. */
+
+#ifndef _ICE_SWITCH_H_
+#define _ICE_SWITCH_H_
+
+#include "ice_common.h"
+
+#define ICE_SW_CFG_MAX_BUF_LEN 2048
+#define ICE_DFLT_VSI_INVAL 0xff
+#define ICE_VSI_INVAL_ID 0xffff
+
+/* VSI context structure for add/get/update/free operations */
+struct ice_vsi_ctx {
+ u16 vsi_num;
+ u16 vsis_allocd;
+ u16 vsis_unallocated;
+ u16 flags;
+ struct ice_aqc_vsi_props info;
+ bool alloc_from_pool;
+};
+
+enum ice_sw_fwd_act_type {
+ ICE_FWD_TO_VSI = 0,
+ ICE_FWD_TO_VSI_LIST, /* Do not use this when adding filter */
+ ICE_FWD_TO_Q,
+ ICE_FWD_TO_QGRP,
+ ICE_DROP_PACKET,
+ ICE_INVAL_ACT
+};
+
+/* Switch recipe ID enum values are specific to hardware */
+enum ice_sw_lkup_type {
+ ICE_SW_LKUP_ETHERTYPE = 0,
+ ICE_SW_LKUP_MAC = 1,
+ ICE_SW_LKUP_MAC_VLAN = 2,
+ ICE_SW_LKUP_PROMISC = 3,
+ ICE_SW_LKUP_VLAN = 4,
+ ICE_SW_LKUP_DFLT = 5,
+ ICE_SW_LKUP_ETHERTYPE_MAC = 8,
+ ICE_SW_LKUP_PROMISC_VLAN = 9,
+};
+
+struct ice_fltr_info {
+ /* Look up information: how to look up packet */
+ enum ice_sw_lkup_type lkup_type;
+ /* Forward action: filter action to do after lookup */
+ enum ice_sw_fwd_act_type fltr_act;
+ /* rule ID returned by firmware once filter rule is created */
+ u16 fltr_rule_id;
+ u16 flag;
+#define ICE_FLTR_RX BIT(0)
+#define ICE_FLTR_TX BIT(1)
+#define ICE_FLTR_TX_RX (ICE_FLTR_RX | ICE_FLTR_TX)
+
+ /* Source VSI for LOOKUP_TX or source port for LOOKUP_RX */
+ u16 src;
+
+ union {
+ struct {
+ u8 mac_addr[ETH_ALEN];
+ } mac;
+ struct {
+ u8 mac_addr[ETH_ALEN];
+ u16 vlan_id;
+ } mac_vlan;
+ struct {
+ u16 vlan_id;
+ } vlan;
+ /* Set lkup_type as ICE_SW_LKUP_ETHERTYPE
+ * if just using ethertype as filter. Set lkup_type as
+ * ICE_SW_LKUP_ETHERTYPE_MAC if MAC also needs to be
+ * passed in as filter.
+ */
+ struct {
+ u16 ethertype;
+ u8 mac_addr[ETH_ALEN]; /* optional */
+ } ethertype_mac;
+ } l_data;
+
+ /* Depending on filter action */
+ union {
+ /* queue id in case of ICE_FWD_TO_Q and starting
+ * queue id in case of ICE_FWD_TO_QGRP.
+ */
+ u16 q_id:11;
+ u16 vsi_id:10;
+ u16 vsi_list_id:10;
+ } fwd_id;
+
+ /* Set to num_queues if action is ICE_FWD_TO_QGRP. This field
+ * determines the range of queues the packet needs to be forwarded to
+ */
+ u8 qgrp_size;
+
+ /* Rule creations populate these indicators basing on the switch type */
+ bool lb_en; /* Indicate if packet can be looped back */
+ bool lan_en; /* Indicate if packet can be forwarded to the uplink */
+};
+
+/* Bookkeeping structure to hold bitmap of VSIs corresponding to VSI list id */
+struct ice_vsi_list_map_info {
+ struct list_head list_entry;
+ DECLARE_BITMAP(vsi_map, ICE_MAX_VSI);
+ u16 vsi_list_id;
+};
+
+enum ice_sw_fltr_status {
+ ICE_FLTR_STATUS_NEW = 0,
+ ICE_FLTR_STATUS_FW_SUCCESS,
+ ICE_FLTR_STATUS_FW_FAIL,
+};
+
+struct ice_fltr_list_entry {
+ struct list_head list_entry;
+ enum ice_sw_fltr_status status;
+ struct ice_fltr_info fltr_info;
+};
+
+/* This defines an entry in the list that maintains MAC or VLAN membership
+ * to HW list mapping, since multiple VSIs can subscribe to the same MAC or
+ * VLAN. As an optimization the VSI list should be created only when a
+ * second VSI becomes a subscriber to the VLAN address.
+ */
+struct ice_fltr_mgmt_list_entry {
+ /* back pointer to VSI list id to VSI list mapping */
+ struct ice_vsi_list_map_info *vsi_list_info;
+ u16 vsi_count;
+#define ICE_INVAL_LG_ACT_INDEX 0xffff
+ u16 lg_act_idx;
+#define ICE_INVAL_SW_MARKER_ID 0xffff
+ u16 sw_marker_id;
+ struct list_head list_entry;
+ struct ice_fltr_info fltr_info;
+#define ICE_INVAL_COUNTER_ID 0xff
+ u8 counter_index;
+};
+
+/* VSI related commands */
+enum ice_status
+ice_aq_add_vsi(struct ice_hw *hw, struct ice_vsi_ctx *vsi_ctx,
+ struct ice_sq_cd *cd);
+enum ice_status
+ice_aq_update_vsi(struct ice_hw *hw, struct ice_vsi_ctx *vsi_ctx,
+ struct ice_sq_cd *cd);
+enum ice_status
+ice_aq_free_vsi(struct ice_hw *hw, struct ice_vsi_ctx *vsi_ctx,
+ bool keep_vsi_alloc, struct ice_sq_cd *cd);
+
+enum ice_status ice_get_initial_sw_cfg(struct ice_hw *hw);
+
+/* Switch/bridge related commands */
+enum ice_status ice_add_mac(struct ice_hw *hw, struct list_head *m_lst);
+enum ice_status ice_remove_mac(struct ice_hw *hw, struct list_head *m_lst);
+void ice_remove_vsi_fltr(struct ice_hw *hw, u16 vsi_id);
+enum ice_status ice_add_vlan(struct ice_hw *hw, struct list_head *m_list);
+enum ice_status ice_remove_vlan(struct ice_hw *hw, struct list_head *v_list);
+enum ice_status
+ice_cfg_dflt_vsi(struct ice_hw *hw, u16 vsi_id, bool set, u8 direction);
+
+#endif /* _ICE_SWITCH_H_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.c b/drivers/net/ethernet/intel/ice/ice_txrx.c
new file mode 100644
index 000000000000..6481e3d86374
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_txrx.c
@@ -0,0 +1,1782 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2018, Intel Corporation. */
+
+/* The driver transmit and receive code */
+
+#include <linux/prefetch.h>
+#include <linux/mm.h>
+#include "ice.h"
+
+#define ICE_RX_HDR_SIZE 256
+
+/**
+ * ice_unmap_and_free_tx_buf - Release a Tx buffer
+ * @ring: the ring that owns the buffer
+ * @tx_buf: the buffer to free
+ */
+static void
+ice_unmap_and_free_tx_buf(struct ice_ring *ring, struct ice_tx_buf *tx_buf)
+{
+ if (tx_buf->skb) {
+ dev_kfree_skb_any(tx_buf->skb);
+ if (dma_unmap_len(tx_buf, len))
+ dma_unmap_single(ring->dev,
+ dma_unmap_addr(tx_buf, dma),
+ dma_unmap_len(tx_buf, len),
+ DMA_TO_DEVICE);
+ } else if (dma_unmap_len(tx_buf, len)) {
+ dma_unmap_page(ring->dev,
+ dma_unmap_addr(tx_buf, dma),
+ dma_unmap_len(tx_buf, len),
+ DMA_TO_DEVICE);
+ }
+
+ tx_buf->next_to_watch = NULL;
+ tx_buf->skb = NULL;
+ dma_unmap_len_set(tx_buf, len, 0);
+ /* tx_buf must be completely set up in the transmit path */
+}
+
+static struct netdev_queue *txring_txq(const struct ice_ring *ring)
+{
+ return netdev_get_tx_queue(ring->netdev, ring->q_index);
+}
+
+/**
+ * ice_clean_tx_ring - Free any empty Tx buffers
+ * @tx_ring: ring to be cleaned
+ */
+void ice_clean_tx_ring(struct ice_ring *tx_ring)
+{
+ unsigned long size;
+ u16 i;
+
+ /* ring already cleared, nothing to do */
+ if (!tx_ring->tx_buf)
+ return;
+
+ /* Free all the Tx ring sk_bufss */
+ for (i = 0; i < tx_ring->count; i++)
+ ice_unmap_and_free_tx_buf(tx_ring, &tx_ring->tx_buf[i]);
+
+ size = sizeof(struct ice_tx_buf) * tx_ring->count;
+ memset(tx_ring->tx_buf, 0, size);
+
+ /* Zero out the descriptor ring */
+ memset(tx_ring->desc, 0, tx_ring->size);
+
+ tx_ring->next_to_use = 0;
+ tx_ring->next_to_clean = 0;
+
+ if (!tx_ring->netdev)
+ return;
+
+ /* cleanup Tx queue statistics */
+ netdev_tx_reset_queue(txring_txq(tx_ring));
+}
+
+/**
+ * ice_free_tx_ring - Free Tx resources per queue
+ * @tx_ring: Tx descriptor ring for a specific queue
+ *
+ * Free all transmit software resources
+ */
+void ice_free_tx_ring(struct ice_ring *tx_ring)
+{
+ ice_clean_tx_ring(tx_ring);
+ devm_kfree(tx_ring->dev, tx_ring->tx_buf);
+ tx_ring->tx_buf = NULL;
+
+ if (tx_ring->desc) {
+ dmam_free_coherent(tx_ring->dev, tx_ring->size,
+ tx_ring->desc, tx_ring->dma);
+ tx_ring->desc = NULL;
+ }
+}
+
+/**
+ * ice_clean_tx_irq - Reclaim resources after transmit completes
+ * @vsi: the VSI we care about
+ * @tx_ring: Tx ring to clean
+ * @napi_budget: Used to determine if we are in netpoll
+ *
+ * Returns true if there's any budget left (e.g. the clean is finished)
+ */
+static bool ice_clean_tx_irq(struct ice_vsi *vsi, struct ice_ring *tx_ring,
+ int napi_budget)
+{
+ unsigned int total_bytes = 0, total_pkts = 0;
+ unsigned int budget = vsi->work_lmt;
+ s16 i = tx_ring->next_to_clean;
+ struct ice_tx_desc *tx_desc;
+ struct ice_tx_buf *tx_buf;
+
+ tx_buf = &tx_ring->tx_buf[i];
+ tx_desc = ICE_TX_DESC(tx_ring, i);
+ i -= tx_ring->count;
+
+ do {
+ struct ice_tx_desc *eop_desc = tx_buf->next_to_watch;
+
+ /* if next_to_watch is not set then there is no work pending */
+ if (!eop_desc)
+ break;
+
+ smp_rmb(); /* prevent any other reads prior to eop_desc */
+
+ /* if the descriptor isn't done, no work yet to do */
+ if (!(eop_desc->cmd_type_offset_bsz &
+ cpu_to_le64(ICE_TX_DESC_DTYPE_DESC_DONE)))
+ break;
+
+ /* clear next_to_watch to prevent false hangs */
+ tx_buf->next_to_watch = NULL;
+
+ /* update the statistics for this packet */
+ total_bytes += tx_buf->bytecount;
+ total_pkts += tx_buf->gso_segs;
+
+ /* free the skb */
+ napi_consume_skb(tx_buf->skb, napi_budget);
+
+ /* unmap skb header data */
+ dma_unmap_single(tx_ring->dev,
+ dma_unmap_addr(tx_buf, dma),
+ dma_unmap_len(tx_buf, len),
+ DMA_TO_DEVICE);
+
+ /* clear tx_buf data */
+ tx_buf->skb = NULL;
+ dma_unmap_len_set(tx_buf, len, 0);
+
+ /* unmap remaining buffers */
+ while (tx_desc != eop_desc) {
+ tx_buf++;
+ tx_desc++;
+ i++;
+ if (unlikely(!i)) {
+ i -= tx_ring->count;
+ tx_buf = tx_ring->tx_buf;
+ tx_desc = ICE_TX_DESC(tx_ring, 0);
+ }
+
+ /* unmap any remaining paged data */
+ if (dma_unmap_len(tx_buf, len)) {
+ dma_unmap_page(tx_ring->dev,
+ dma_unmap_addr(tx_buf, dma),
+ dma_unmap_len(tx_buf, len),
+ DMA_TO_DEVICE);
+ dma_unmap_len_set(tx_buf, len, 0);
+ }
+ }
+
+ /* move us one more past the eop_desc for start of next pkt */
+ tx_buf++;
+ tx_desc++;
+ i++;
+ if (unlikely(!i)) {
+ i -= tx_ring->count;
+ tx_buf = tx_ring->tx_buf;
+ tx_desc = ICE_TX_DESC(tx_ring, 0);
+ }
+
+ prefetch(tx_desc);
+
+ /* update budget accounting */
+ budget--;
+ } while (likely(budget));
+
+ i += tx_ring->count;
+ tx_ring->next_to_clean = i;
+ u64_stats_update_begin(&tx_ring->syncp);
+ tx_ring->stats.bytes += total_bytes;
+ tx_ring->stats.pkts += total_pkts;
+ u64_stats_update_end(&tx_ring->syncp);
+ tx_ring->q_vector->tx.total_bytes += total_bytes;
+ tx_ring->q_vector->tx.total_pkts += total_pkts;
+
+ netdev_tx_completed_queue(txring_txq(tx_ring), total_pkts,
+ total_bytes);
+
+#define TX_WAKE_THRESHOLD ((s16)(DESC_NEEDED * 2))
+ if (unlikely(total_pkts && netif_carrier_ok(tx_ring->netdev) &&
+ (ICE_DESC_UNUSED(tx_ring) >= TX_WAKE_THRESHOLD))) {
+ /* Make sure that anybody stopping the queue after this
+ * sees the new next_to_clean.
+ */
+ smp_mb();
+ if (__netif_subqueue_stopped(tx_ring->netdev,
+ tx_ring->q_index) &&
+ !test_bit(__ICE_DOWN, vsi->state)) {
+ netif_wake_subqueue(tx_ring->netdev,
+ tx_ring->q_index);
+ ++tx_ring->tx_stats.restart_q;
+ }
+ }
+
+ return !!budget;
+}
+
+/**
+ * ice_setup_tx_ring - Allocate the Tx descriptors
+ * @tx_ring: the tx ring to set up
+ *
+ * Return 0 on success, negative on error
+ */
+int ice_setup_tx_ring(struct ice_ring *tx_ring)
+{
+ struct device *dev = tx_ring->dev;
+ int bi_size;
+
+ if (!dev)
+ return -ENOMEM;
+
+ /* warn if we are about to overwrite the pointer */
+ WARN_ON(tx_ring->tx_buf);
+ bi_size = sizeof(struct ice_tx_buf) * tx_ring->count;
+ tx_ring->tx_buf = devm_kzalloc(dev, bi_size, GFP_KERNEL);
+ if (!tx_ring->tx_buf)
+ return -ENOMEM;
+
+ /* round up to nearest 4K */
+ tx_ring->size = tx_ring->count * sizeof(struct ice_tx_desc);
+ tx_ring->size = ALIGN(tx_ring->size, 4096);
+ tx_ring->desc = dmam_alloc_coherent(dev, tx_ring->size, &tx_ring->dma,
+ GFP_KERNEL);
+ if (!tx_ring->desc) {
+ dev_err(dev, "Unable to allocate memory for the Tx descriptor ring, size=%d\n",
+ tx_ring->size);
+ goto err;
+ }
+
+ tx_ring->next_to_use = 0;
+ tx_ring->next_to_clean = 0;
+ return 0;
+
+err:
+ devm_kfree(dev, tx_ring->tx_buf);
+ tx_ring->tx_buf = NULL;
+ return -ENOMEM;
+}
+
+/**
+ * ice_clean_rx_ring - Free Rx buffers
+ * @rx_ring: ring to be cleaned
+ */
+void ice_clean_rx_ring(struct ice_ring *rx_ring)
+{
+ struct device *dev = rx_ring->dev;
+ unsigned long size;
+ u16 i;
+
+ /* ring already cleared, nothing to do */
+ if (!rx_ring->rx_buf)
+ return;
+
+ /* Free all the Rx ring sk_buffs */
+ for (i = 0; i < rx_ring->count; i++) {
+ struct ice_rx_buf *rx_buf = &rx_ring->rx_buf[i];
+
+ if (rx_buf->skb) {
+ dev_kfree_skb(rx_buf->skb);
+ rx_buf->skb = NULL;
+ }
+ if (!rx_buf->page)
+ continue;
+
+ dma_unmap_page(dev, rx_buf->dma, PAGE_SIZE, DMA_FROM_DEVICE);
+ __free_pages(rx_buf->page, 0);
+
+ rx_buf->page = NULL;
+ rx_buf->page_offset = 0;
+ }
+
+ size = sizeof(struct ice_rx_buf) * rx_ring->count;
+ memset(rx_ring->rx_buf, 0, size);
+
+ /* Zero out the descriptor ring */
+ memset(rx_ring->desc, 0, rx_ring->size);
+
+ rx_ring->next_to_alloc = 0;
+ rx_ring->next_to_clean = 0;
+ rx_ring->next_to_use = 0;
+}
+
+/**
+ * ice_free_rx_ring - Free Rx resources
+ * @rx_ring: ring to clean the resources from
+ *
+ * Free all receive software resources
+ */
+void ice_free_rx_ring(struct ice_ring *rx_ring)
+{
+ ice_clean_rx_ring(rx_ring);
+ devm_kfree(rx_ring->dev, rx_ring->rx_buf);
+ rx_ring->rx_buf = NULL;
+
+ if (rx_ring->desc) {
+ dmam_free_coherent(rx_ring->dev, rx_ring->size,
+ rx_ring->desc, rx_ring->dma);
+ rx_ring->desc = NULL;
+ }
+}
+
+/**
+ * ice_setup_rx_ring - Allocate the Rx descriptors
+ * @rx_ring: the rx ring to set up
+ *
+ * Return 0 on success, negative on error
+ */
+int ice_setup_rx_ring(struct ice_ring *rx_ring)
+{
+ struct device *dev = rx_ring->dev;
+ int bi_size;
+
+ if (!dev)
+ return -ENOMEM;
+
+ /* warn if we are about to overwrite the pointer */
+ WARN_ON(rx_ring->rx_buf);
+ bi_size = sizeof(struct ice_rx_buf) * rx_ring->count;
+ rx_ring->rx_buf = devm_kzalloc(dev, bi_size, GFP_KERNEL);
+ if (!rx_ring->rx_buf)
+ return -ENOMEM;
+
+ /* round up to nearest 4K */
+ rx_ring->size = rx_ring->count * sizeof(union ice_32byte_rx_desc);
+ rx_ring->size = ALIGN(rx_ring->size, 4096);
+ rx_ring->desc = dmam_alloc_coherent(dev, rx_ring->size, &rx_ring->dma,
+ GFP_KERNEL);
+ if (!rx_ring->desc) {
+ dev_err(dev, "Unable to allocate memory for the Rx descriptor ring, size=%d\n",
+ rx_ring->size);
+ goto err;
+ }
+
+ rx_ring->next_to_use = 0;
+ rx_ring->next_to_clean = 0;
+ return 0;
+
+err:
+ devm_kfree(dev, rx_ring->rx_buf);
+ rx_ring->rx_buf = NULL;
+ return -ENOMEM;
+}
+
+/**
+ * ice_release_rx_desc - Store the new tail and head values
+ * @rx_ring: ring to bump
+ * @val: new head index
+ */
+static void ice_release_rx_desc(struct ice_ring *rx_ring, u32 val)
+{
+ rx_ring->next_to_use = val;
+
+ /* update next to alloc since we have filled the ring */
+ rx_ring->next_to_alloc = val;
+
+ /* Force memory writes to complete before letting h/w
+ * know there are new descriptors to fetch. (Only
+ * applicable for weak-ordered memory model archs,
+ * such as IA-64).
+ */
+ wmb();
+ writel(val, rx_ring->tail);
+}
+
+/**
+ * ice_alloc_mapped_page - recycle or make a new page
+ * @rx_ring: ring to use
+ * @bi: rx_buf struct to modify
+ *
+ * Returns true if the page was successfully allocated or
+ * reused.
+ */
+static bool ice_alloc_mapped_page(struct ice_ring *rx_ring,
+ struct ice_rx_buf *bi)
+{
+ struct page *page = bi->page;
+ dma_addr_t dma;
+
+ /* since we are recycling buffers we should seldom need to alloc */
+ if (likely(page)) {
+ rx_ring->rx_stats.page_reuse_count++;
+ return true;
+ }
+
+ /* alloc new page for storage */
+ page = alloc_page(GFP_ATOMIC | __GFP_NOWARN);
+ if (unlikely(!page)) {
+ rx_ring->rx_stats.alloc_page_failed++;
+ return false;
+ }
+
+ /* map page for use */
+ dma = dma_map_page(rx_ring->dev, page, 0, PAGE_SIZE, DMA_FROM_DEVICE);
+
+ /* if mapping failed free memory back to system since
+ * there isn't much point in holding memory we can't use
+ */
+ if (dma_mapping_error(rx_ring->dev, dma)) {
+ __free_pages(page, 0);
+ rx_ring->rx_stats.alloc_page_failed++;
+ return false;
+ }
+
+ bi->dma = dma;
+ bi->page = page;
+ bi->page_offset = 0;
+
+ return true;
+}
+
+/**
+ * ice_alloc_rx_bufs - Replace used receive buffers
+ * @rx_ring: ring to place buffers on
+ * @cleaned_count: number of buffers to replace
+ *
+ * Returns false if all allocations were successful, true if any fail
+ */
+bool ice_alloc_rx_bufs(struct ice_ring *rx_ring, u16 cleaned_count)
+{
+ union ice_32b_rx_flex_desc *rx_desc;
+ u16 ntu = rx_ring->next_to_use;
+ struct ice_rx_buf *bi;
+
+ /* do nothing if no valid netdev defined */
+ if (!rx_ring->netdev || !cleaned_count)
+ return false;
+
+ /* get the RX descriptor and buffer based on next_to_use */
+ rx_desc = ICE_RX_DESC(rx_ring, ntu);
+ bi = &rx_ring->rx_buf[ntu];
+
+ do {
+ if (!ice_alloc_mapped_page(rx_ring, bi))
+ goto no_bufs;
+
+ /* Refresh the desc even if buffer_addrs didn't change
+ * because each write-back erases this info.
+ */
+ rx_desc->read.pkt_addr = cpu_to_le64(bi->dma + bi->page_offset);
+
+ rx_desc++;
+ bi++;
+ ntu++;
+ if (unlikely(ntu == rx_ring->count)) {
+ rx_desc = ICE_RX_DESC(rx_ring, 0);
+ bi = rx_ring->rx_buf;
+ ntu = 0;
+ }
+
+ /* clear the status bits for the next_to_use descriptor */
+ rx_desc->wb.status_error0 = 0;
+
+ cleaned_count--;
+ } while (cleaned_count);
+
+ if (rx_ring->next_to_use != ntu)
+ ice_release_rx_desc(rx_ring, ntu);
+
+ return false;
+
+no_bufs:
+ if (rx_ring->next_to_use != ntu)
+ ice_release_rx_desc(rx_ring, ntu);
+
+ /* make sure to come back via polling to try again after
+ * allocation failure
+ */
+ return true;
+}
+
+/**
+ * ice_page_is_reserved - check if reuse is possible
+ * @page: page struct to check
+ */
+static bool ice_page_is_reserved(struct page *page)
+{
+ return (page_to_nid(page) != numa_mem_id()) || page_is_pfmemalloc(page);
+}
+
+/**
+ * ice_add_rx_frag - Add contents of Rx buffer to sk_buff
+ * @rx_buf: buffer containing page to add
+ * @rx_desc: descriptor containing length of buffer written by hardware
+ * @skb: sk_buf to place the data into
+ *
+ * This function will add the data contained in rx_buf->page to the skb.
+ * This is done either through a direct copy if the data in the buffer is
+ * less than the skb header size, otherwise it will just attach the page as
+ * a frag to the skb.
+ *
+ * The function will then update the page offset if necessary and return
+ * true if the buffer can be reused by the adapter.
+ */
+static bool ice_add_rx_frag(struct ice_rx_buf *rx_buf,
+ union ice_32b_rx_flex_desc *rx_desc,
+ struct sk_buff *skb)
+{
+#if (PAGE_SIZE < 8192)
+ unsigned int truesize = ICE_RXBUF_2048;
+#else
+ unsigned int last_offset = PAGE_SIZE - ICE_RXBUF_2048;
+ unsigned int truesize;
+#endif /* PAGE_SIZE < 8192) */
+
+ struct page *page;
+ unsigned int size;
+
+ size = le16_to_cpu(rx_desc->wb.pkt_len) &
+ ICE_RX_FLX_DESC_PKT_LEN_M;
+
+ page = rx_buf->page;
+
+#if (PAGE_SIZE >= 8192)
+ truesize = ALIGN(size, L1_CACHE_BYTES);
+#endif /* PAGE_SIZE >= 8192) */
+
+ /* will the data fit in the skb we allocated? if so, just
+ * copy it as it is pretty small anyway
+ */
+ if (size <= ICE_RX_HDR_SIZE && !skb_is_nonlinear(skb)) {
+ unsigned char *va = page_address(page) + rx_buf->page_offset;
+
+ memcpy(__skb_put(skb, size), va, ALIGN(size, sizeof(long)));
+
+ /* page is not reserved, we can reuse buffer as-is */
+ if (likely(!ice_page_is_reserved(page)))
+ return true;
+
+ /* this page cannot be reused so discard it */
+ __free_pages(page, 0);
+ return false;
+ }
+
+ skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page,
+ rx_buf->page_offset, size, truesize);
+
+ /* avoid re-using remote pages */
+ if (unlikely(ice_page_is_reserved(page)))
+ return false;
+
+#if (PAGE_SIZE < 8192)
+ /* if we are only owner of page we can reuse it */
+ if (unlikely(page_count(page) != 1))
+ return false;
+
+ /* flip page offset to other buffer */
+ rx_buf->page_offset ^= truesize;
+#else
+ /* move offset up to the next cache line */
+ rx_buf->page_offset += truesize;
+
+ if (rx_buf->page_offset > last_offset)
+ return false;
+#endif /* PAGE_SIZE < 8192) */
+
+ /* Even if we own the page, we are not allowed to use atomic_set()
+ * This would break get_page_unless_zero() users.
+ */
+ get_page(rx_buf->page);
+
+ return true;
+}
+
+/**
+ * ice_reuse_rx_page - page flip buffer and store it back on the ring
+ * @rx_ring: rx descriptor ring to store buffers on
+ * @old_buf: donor buffer to have page reused
+ *
+ * Synchronizes page for reuse by the adapter
+ */
+static void ice_reuse_rx_page(struct ice_ring *rx_ring,
+ struct ice_rx_buf *old_buf)
+{
+ u16 nta = rx_ring->next_to_alloc;
+ struct ice_rx_buf *new_buf;
+
+ new_buf = &rx_ring->rx_buf[nta];
+
+ /* update, and store next to alloc */
+ nta++;
+ rx_ring->next_to_alloc = (nta < rx_ring->count) ? nta : 0;
+
+ /* transfer page from old buffer to new buffer */
+ *new_buf = *old_buf;
+}
+
+/**
+ * ice_fetch_rx_buf - Allocate skb and populate it
+ * @rx_ring: rx descriptor ring to transact packets on
+ * @rx_desc: descriptor containing info written by hardware
+ *
+ * This function allocates an skb on the fly, and populates it with the page
+ * data from the current receive descriptor, taking care to set up the skb
+ * correctly, as well as handling calling the page recycle function if
+ * necessary.
+ */
+static struct sk_buff *ice_fetch_rx_buf(struct ice_ring *rx_ring,
+ union ice_32b_rx_flex_desc *rx_desc)
+{
+ struct ice_rx_buf *rx_buf;
+ struct sk_buff *skb;
+ struct page *page;
+
+ rx_buf = &rx_ring->rx_buf[rx_ring->next_to_clean];
+ page = rx_buf->page;
+ prefetchw(page);
+
+ skb = rx_buf->skb;
+
+ if (likely(!skb)) {
+ u8 *page_addr = page_address(page) + rx_buf->page_offset;
+
+ /* prefetch first cache line of first page */
+ prefetch(page_addr);
+#if L1_CACHE_BYTES < 128
+ prefetch((void *)(page_addr + L1_CACHE_BYTES));
+#endif /* L1_CACHE_BYTES */
+
+ /* allocate a skb to store the frags */
+ skb = __napi_alloc_skb(&rx_ring->q_vector->napi,
+ ICE_RX_HDR_SIZE,
+ GFP_ATOMIC | __GFP_NOWARN);
+ if (unlikely(!skb)) {
+ rx_ring->rx_stats.alloc_buf_failed++;
+ return NULL;
+ }
+
+ /* we will be copying header into skb->data in
+ * pskb_may_pull so it is in our interest to prefetch
+ * it now to avoid a possible cache miss
+ */
+ prefetchw(skb->data);
+
+ skb_record_rx_queue(skb, rx_ring->q_index);
+ } else {
+ /* we are reusing so sync this buffer for CPU use */
+ dma_sync_single_range_for_cpu(rx_ring->dev, rx_buf->dma,
+ rx_buf->page_offset,
+ ICE_RXBUF_2048,
+ DMA_FROM_DEVICE);
+
+ rx_buf->skb = NULL;
+ }
+
+ /* pull page into skb */
+ if (ice_add_rx_frag(rx_buf, rx_desc, skb)) {
+ /* hand second half of page back to the ring */
+ ice_reuse_rx_page(rx_ring, rx_buf);
+ rx_ring->rx_stats.page_reuse_count++;
+ } else {
+ /* we are not reusing the buffer so unmap it */
+ dma_unmap_page(rx_ring->dev, rx_buf->dma, PAGE_SIZE,
+ DMA_FROM_DEVICE);
+ }
+
+ /* clear contents of buffer_info */
+ rx_buf->page = NULL;
+
+ return skb;
+}
+
+/**
+ * ice_pull_tail - ice specific version of skb_pull_tail
+ * @skb: pointer to current skb being adjusted
+ *
+ * This function is an ice specific version of __pskb_pull_tail. The
+ * main difference between this version and the original function is that
+ * this function can make several assumptions about the state of things
+ * that allow for significant optimizations versus the standard function.
+ * As a result we can do things like drop a frag and maintain an accurate
+ * truesize for the skb.
+ */
+static void ice_pull_tail(struct sk_buff *skb)
+{
+ struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[0];
+ unsigned int pull_len;
+ unsigned char *va;
+
+ /* it is valid to use page_address instead of kmap since we are
+ * working with pages allocated out of the lomem pool per
+ * alloc_page(GFP_ATOMIC)
+ */
+ va = skb_frag_address(frag);
+
+ /* we need the header to contain the greater of either ETH_HLEN or
+ * 60 bytes if the skb->len is less than 60 for skb_pad.
+ */
+ pull_len = eth_get_headlen(va, ICE_RX_HDR_SIZE);
+
+ /* align pull length to size of long to optimize memcpy performance */
+ skb_copy_to_linear_data(skb, va, ALIGN(pull_len, sizeof(long)));
+
+ /* update all of the pointers */
+ skb_frag_size_sub(frag, pull_len);
+ frag->page_offset += pull_len;
+ skb->data_len -= pull_len;
+ skb->tail += pull_len;
+}
+
+/**
+ * ice_cleanup_headers - Correct empty headers
+ * @skb: pointer to current skb being fixed
+ *
+ * Also address the case where we are pulling data in on pages only
+ * and as such no data is present in the skb header.
+ *
+ * In addition if skb is not at least 60 bytes we need to pad it so that
+ * it is large enough to qualify as a valid Ethernet frame.
+ *
+ * Returns true if an error was encountered and skb was freed.
+ */
+static bool ice_cleanup_headers(struct sk_buff *skb)
+{
+ /* place header in linear portion of buffer */
+ if (skb_is_nonlinear(skb))
+ ice_pull_tail(skb);
+
+ /* if eth_skb_pad returns an error the skb was freed */
+ if (eth_skb_pad(skb))
+ return true;
+
+ return false;
+}
+
+/**
+ * ice_test_staterr - tests bits in Rx descriptor status and error fields
+ * @rx_desc: pointer to receive descriptor (in le64 format)
+ * @stat_err_bits: value to mask
+ *
+ * This function does some fast chicanery in order to return the
+ * value of the mask which is really only used for boolean tests.
+ * The status_error_len doesn't need to be shifted because it begins
+ * at offset zero.
+ */
+static bool ice_test_staterr(union ice_32b_rx_flex_desc *rx_desc,
+ const u16 stat_err_bits)
+{
+ return !!(rx_desc->wb.status_error0 &
+ cpu_to_le16(stat_err_bits));
+}
+
+/**
+ * ice_is_non_eop - process handling of non-EOP buffers
+ * @rx_ring: Rx ring being processed
+ * @rx_desc: Rx descriptor for current buffer
+ * @skb: Current socket buffer containing buffer in progress
+ *
+ * This function updates next to clean. If the buffer is an EOP buffer
+ * this function exits returning false, otherwise it will place the
+ * sk_buff in the next buffer to be chained and return true indicating
+ * that this is in fact a non-EOP buffer.
+ */
+static bool ice_is_non_eop(struct ice_ring *rx_ring,
+ union ice_32b_rx_flex_desc *rx_desc,
+ struct sk_buff *skb)
+{
+ u32 ntc = rx_ring->next_to_clean + 1;
+
+ /* fetch, update, and store next to clean */
+ ntc = (ntc < rx_ring->count) ? ntc : 0;
+ rx_ring->next_to_clean = ntc;
+
+ prefetch(ICE_RX_DESC(rx_ring, ntc));
+
+ /* if we are the last buffer then there is nothing else to do */
+#define ICE_RXD_EOF BIT(ICE_RX_FLEX_DESC_STATUS0_EOF_S)
+ if (likely(ice_test_staterr(rx_desc, ICE_RXD_EOF)))
+ return false;
+
+ /* place skb in next buffer to be received */
+ rx_ring->rx_buf[ntc].skb = skb;
+ rx_ring->rx_stats.non_eop_descs++;
+
+ return true;
+}
+
+/**
+ * ice_ptype_to_htype - get a hash type
+ * @ptype: the ptype value from the descriptor
+ *
+ * Returns a hash type to be used by skb_set_hash
+ */
+static enum pkt_hash_types ice_ptype_to_htype(u8 __always_unused ptype)
+{
+ return PKT_HASH_TYPE_NONE;
+}
+
+/**
+ * ice_rx_hash - set the hash value in the skb
+ * @rx_ring: descriptor ring
+ * @rx_desc: specific descriptor
+ * @skb: pointer to current skb
+ * @rx_ptype: the ptype value from the descriptor
+ */
+static void
+ice_rx_hash(struct ice_ring *rx_ring, union ice_32b_rx_flex_desc *rx_desc,
+ struct sk_buff *skb, u8 rx_ptype)
+{
+ struct ice_32b_rx_flex_desc_nic *nic_mdid;
+ u32 hash;
+
+ if (!(rx_ring->netdev->features & NETIF_F_RXHASH))
+ return;
+
+ if (rx_desc->wb.rxdid != ICE_RXDID_FLEX_NIC)
+ return;
+
+ nic_mdid = (struct ice_32b_rx_flex_desc_nic *)rx_desc;
+ hash = le32_to_cpu(nic_mdid->rss_hash);
+ skb_set_hash(skb, hash, ice_ptype_to_htype(rx_ptype));
+}
+
+/**
+ * ice_rx_csum - Indicate in skb if checksum is good
+ * @vsi: the VSI we care about
+ * @skb: skb currently being received and modified
+ * @rx_desc: the receive descriptor
+ * @ptype: the packet type decoded by hardware
+ *
+ * skb->protocol must be set before this function is called
+ */
+static void ice_rx_csum(struct ice_vsi *vsi, struct sk_buff *skb,
+ union ice_32b_rx_flex_desc *rx_desc, u8 ptype)
+{
+ struct ice_rx_ptype_decoded decoded;
+ u32 rx_error, rx_status;
+ bool ipv4, ipv6;
+
+ rx_status = le16_to_cpu(rx_desc->wb.status_error0);
+ rx_error = rx_status;
+
+ decoded = ice_decode_rx_desc_ptype(ptype);
+
+ /* Start with CHECKSUM_NONE and by default csum_level = 0 */
+ skb->ip_summed = CHECKSUM_NONE;
+ skb_checksum_none_assert(skb);
+
+ /* check if Rx checksum is enabled */
+ if (!(vsi->netdev->features & NETIF_F_RXCSUM))
+ return;
+
+ /* check if HW has decoded the packet and checksum */
+ if (!(rx_status & BIT(ICE_RX_FLEX_DESC_STATUS0_L3L4P_S)))
+ return;
+
+ if (!(decoded.known && decoded.outer_ip))
+ return;
+
+ ipv4 = (decoded.outer_ip == ICE_RX_PTYPE_OUTER_IP) &&
+ (decoded.outer_ip_ver == ICE_RX_PTYPE_OUTER_IPV4);
+ ipv6 = (decoded.outer_ip == ICE_RX_PTYPE_OUTER_IP) &&
+ (decoded.outer_ip_ver == ICE_RX_PTYPE_OUTER_IPV6);
+
+ if (ipv4 && (rx_error & (BIT(ICE_RX_FLEX_DESC_STATUS0_XSUM_IPE_S) |
+ BIT(ICE_RX_FLEX_DESC_STATUS0_XSUM_EIPE_S))))
+ goto checksum_fail;
+ else if (ipv6 && (rx_status &
+ (BIT(ICE_RX_FLEX_DESC_STATUS0_IPV6EXADD_S))))
+ goto checksum_fail;
+
+ /* check for L4 errors and handle packets that were not able to be
+ * checksummed due to arrival speed
+ */
+ if (rx_error & BIT(ICE_RX_FLEX_DESC_STATUS0_XSUM_L4E_S))
+ goto checksum_fail;
+
+ /* Only report checksum unnecessary for TCP, UDP, or SCTP */
+ switch (decoded.inner_prot) {
+ case ICE_RX_PTYPE_INNER_PROT_TCP:
+ case ICE_RX_PTYPE_INNER_PROT_UDP:
+ case ICE_RX_PTYPE_INNER_PROT_SCTP:
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ default:
+ break;
+ }
+ return;
+
+checksum_fail:
+ vsi->back->hw_csum_rx_error++;
+}
+
+/**
+ * ice_process_skb_fields - Populate skb header fields from Rx descriptor
+ * @rx_ring: rx descriptor ring packet is being transacted on
+ * @rx_desc: pointer to the EOP Rx descriptor
+ * @skb: pointer to current skb being populated
+ * @ptype: the packet type decoded by hardware
+ *
+ * This function checks the ring, descriptor, and packet information in
+ * order to populate the hash, checksum, VLAN, protocol, and
+ * other fields within the skb.
+ */
+static void ice_process_skb_fields(struct ice_ring *rx_ring,
+ union ice_32b_rx_flex_desc *rx_desc,
+ struct sk_buff *skb, u8 ptype)
+{
+ ice_rx_hash(rx_ring, rx_desc, skb, ptype);
+
+ /* modifies the skb - consumes the enet header */
+ skb->protocol = eth_type_trans(skb, rx_ring->netdev);
+
+ ice_rx_csum(rx_ring->vsi, skb, rx_desc, ptype);
+}
+
+/**
+ * ice_receive_skb - Send a completed packet up the stack
+ * @rx_ring: rx ring in play
+ * @skb: packet to send up
+ * @vlan_tag: vlan tag for packet
+ *
+ * This function sends the completed packet (via. skb) up the stack using
+ * gro receive functions (with/without vlan tag)
+ */
+static void ice_receive_skb(struct ice_ring *rx_ring, struct sk_buff *skb,
+ u16 vlan_tag)
+{
+ if ((rx_ring->netdev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
+ (vlan_tag & VLAN_VID_MASK)) {
+ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag);
+ }
+ napi_gro_receive(&rx_ring->q_vector->napi, skb);
+}
+
+/**
+ * ice_clean_rx_irq - Clean completed descriptors from Rx ring - bounce buf
+ * @rx_ring: rx descriptor ring to transact packets on
+ * @budget: Total limit on number of packets to process
+ *
+ * This function provides a "bounce buffer" approach to Rx interrupt
+ * processing. The advantage to this is that on systems that have
+ * expensive overhead for IOMMU access this provides a means of avoiding
+ * it by maintaining the mapping of the page to the system.
+ *
+ * Returns amount of work completed
+ */
+static int ice_clean_rx_irq(struct ice_ring *rx_ring, int budget)
+{
+ unsigned int total_rx_bytes = 0, total_rx_pkts = 0;
+ u16 cleaned_count = ICE_DESC_UNUSED(rx_ring);
+ bool failure = false;
+
+ /* start the loop to process RX packets bounded by 'budget' */
+ while (likely(total_rx_pkts < (unsigned int)budget)) {
+ union ice_32b_rx_flex_desc *rx_desc;
+ struct sk_buff *skb;
+ u16 stat_err_bits;
+ u16 vlan_tag = 0;
+ u8 rx_ptype;
+
+ /* return some buffers to hardware, one at a time is too slow */
+ if (cleaned_count >= ICE_RX_BUF_WRITE) {
+ failure = failure ||
+ ice_alloc_rx_bufs(rx_ring, cleaned_count);
+ cleaned_count = 0;
+ }
+
+ /* get the RX desc from RX ring based on 'next_to_clean' */
+ rx_desc = ICE_RX_DESC(rx_ring, rx_ring->next_to_clean);
+
+ /* status_error_len will always be zero for unused descriptors
+ * because it's cleared in cleanup, and overlaps with hdr_addr
+ * which is always zero because packet split isn't used, if the
+ * hardware wrote DD then it will be non-zero
+ */
+ stat_err_bits = BIT(ICE_RX_FLEX_DESC_STATUS0_DD_S);
+ if (!ice_test_staterr(rx_desc, stat_err_bits))
+ break;
+
+ /* This memory barrier is needed to keep us from reading
+ * any other fields out of the rx_desc until we know the
+ * DD bit is set.
+ */
+ dma_rmb();
+
+ /* allocate (if needed) and populate skb */
+ skb = ice_fetch_rx_buf(rx_ring, rx_desc);
+ if (!skb)
+ break;
+
+ cleaned_count++;
+
+ /* skip if it is NOP desc */
+ if (ice_is_non_eop(rx_ring, rx_desc, skb))
+ continue;
+
+ stat_err_bits = BIT(ICE_RX_FLEX_DESC_STATUS0_RXE_S);
+ if (unlikely(ice_test_staterr(rx_desc, stat_err_bits))) {
+ dev_kfree_skb_any(skb);
+ continue;
+ }
+
+ rx_ptype = le16_to_cpu(rx_desc->wb.ptype_flex_flags0) &
+ ICE_RX_FLEX_DESC_PTYPE_M;
+
+ stat_err_bits = BIT(ICE_RX_FLEX_DESC_STATUS0_L2TAG1P_S);
+ if (ice_test_staterr(rx_desc, stat_err_bits))
+ vlan_tag = le16_to_cpu(rx_desc->wb.l2tag1);
+
+ /* correct empty headers and pad skb if needed (to make valid
+ * ethernet frame
+ */
+ if (ice_cleanup_headers(skb)) {
+ skb = NULL;
+ continue;
+ }
+
+ /* probably a little skewed due to removing CRC */
+ total_rx_bytes += skb->len;
+
+ /* populate checksum, VLAN, and protocol */
+ ice_process_skb_fields(rx_ring, rx_desc, skb, rx_ptype);
+
+ /* send completed skb up the stack */
+ ice_receive_skb(rx_ring, skb, vlan_tag);
+
+ /* update budget accounting */
+ total_rx_pkts++;
+ }
+
+ /* update queue and vector specific stats */
+ u64_stats_update_begin(&rx_ring->syncp);
+ rx_ring->stats.pkts += total_rx_pkts;
+ rx_ring->stats.bytes += total_rx_bytes;
+ u64_stats_update_end(&rx_ring->syncp);
+ rx_ring->q_vector->rx.total_pkts += total_rx_pkts;
+ rx_ring->q_vector->rx.total_bytes += total_rx_bytes;
+
+ /* guarantee a trip back through this routine if there was a failure */
+ return failure ? budget : (int)total_rx_pkts;
+}
+
+/**
+ * ice_napi_poll - NAPI polling Rx/Tx cleanup routine
+ * @napi: napi struct with our devices info in it
+ * @budget: amount of work driver is allowed to do this pass, in packets
+ *
+ * This function will clean all queues associated with a q_vector.
+ *
+ * Returns the amount of work done
+ */
+int ice_napi_poll(struct napi_struct *napi, int budget)
+{
+ struct ice_q_vector *q_vector =
+ container_of(napi, struct ice_q_vector, napi);
+ struct ice_vsi *vsi = q_vector->vsi;
+ struct ice_pf *pf = vsi->back;
+ bool clean_complete = true;
+ int budget_per_ring = 0;
+ struct ice_ring *ring;
+ int work_done = 0;
+
+ /* Since the actual Tx work is minimal, we can give the Tx a larger
+ * budget and be more aggressive about cleaning up the Tx descriptors.
+ */
+ ice_for_each_ring(ring, q_vector->tx)
+ if (!ice_clean_tx_irq(vsi, ring, budget))
+ clean_complete = false;
+
+ /* Handle case where we are called by netpoll with a budget of 0 */
+ if (budget <= 0)
+ return budget;
+
+ /* We attempt to distribute budget to each Rx queue fairly, but don't
+ * allow the budget to go below 1 because that would exit polling early.
+ */
+ if (q_vector->num_ring_rx)
+ budget_per_ring = max(budget / q_vector->num_ring_rx, 1);
+
+ ice_for_each_ring(ring, q_vector->rx) {
+ int cleaned;
+
+ cleaned = ice_clean_rx_irq(ring, budget_per_ring);
+ work_done += cleaned;
+ /* if we clean as many as budgeted, we must not be done */
+ if (cleaned >= budget_per_ring)
+ clean_complete = false;
+ }
+
+ /* If work not completed, return budget and polling will return */
+ if (!clean_complete)
+ return budget;
+
+ /* Work is done so exit the polling mode and re-enable the interrupt */
+ napi_complete_done(napi, work_done);
+ if (test_bit(ICE_FLAG_MSIX_ENA, pf->flags))
+ ice_irq_dynamic_ena(&vsi->back->hw, vsi, q_vector);
+ return 0;
+}
+
+/* helper function for building cmd/type/offset */
+static __le64
+build_ctob(u64 td_cmd, u64 td_offset, unsigned int size, u64 td_tag)
+{
+ return cpu_to_le64(ICE_TX_DESC_DTYPE_DATA |
+ (td_cmd << ICE_TXD_QW1_CMD_S) |
+ (td_offset << ICE_TXD_QW1_OFFSET_S) |
+ ((u64)size << ICE_TXD_QW1_TX_BUF_SZ_S) |
+ (td_tag << ICE_TXD_QW1_L2TAG1_S));
+}
+
+/**
+ * __ice_maybe_stop_tx - 2nd level check for tx stop conditions
+ * @tx_ring: the ring to be checked
+ * @size: the size buffer we want to assure is available
+ *
+ * Returns -EBUSY if a stop is needed, else 0
+ */
+static int __ice_maybe_stop_tx(struct ice_ring *tx_ring, unsigned int size)
+{
+ netif_stop_subqueue(tx_ring->netdev, tx_ring->q_index);
+ /* Memory barrier before checking head and tail */
+ smp_mb();
+
+ /* Check again in a case another CPU has just made room available. */
+ if (likely(ICE_DESC_UNUSED(tx_ring) < size))
+ return -EBUSY;
+
+ /* A reprieve! - use start_subqueue because it doesn't call schedule */
+ netif_start_subqueue(tx_ring->netdev, tx_ring->q_index);
+ ++tx_ring->tx_stats.restart_q;
+ return 0;
+}
+
+/**
+ * ice_maybe_stop_tx - 1st level check for tx stop conditions
+ * @tx_ring: the ring to be checked
+ * @size: the size buffer we want to assure is available
+ *
+ * Returns 0 if stop is not needed
+ */
+static int ice_maybe_stop_tx(struct ice_ring *tx_ring, unsigned int size)
+{
+ if (likely(ICE_DESC_UNUSED(tx_ring) >= size))
+ return 0;
+ return __ice_maybe_stop_tx(tx_ring, size);
+}
+
+/**
+ * ice_tx_map - Build the Tx descriptor
+ * @tx_ring: ring to send buffer on
+ * @first: first buffer info buffer to use
+ * @off: pointer to struct that holds offload parameters
+ *
+ * This function loops over the skb data pointed to by *first
+ * and gets a physical address for each memory location and programs
+ * it and the length into the transmit descriptor.
+ */
+static void
+ice_tx_map(struct ice_ring *tx_ring, struct ice_tx_buf *first,
+ struct ice_tx_offload_params *off)
+{
+ u64 td_offset, td_tag, td_cmd;
+ u16 i = tx_ring->next_to_use;
+ struct skb_frag_struct *frag;
+ unsigned int data_len, size;
+ struct ice_tx_desc *tx_desc;
+ struct ice_tx_buf *tx_buf;
+ struct sk_buff *skb;
+ dma_addr_t dma;
+
+ td_tag = off->td_l2tag1;
+ td_cmd = off->td_cmd;
+ td_offset = off->td_offset;
+ skb = first->skb;
+
+ data_len = skb->data_len;
+ size = skb_headlen(skb);
+
+ tx_desc = ICE_TX_DESC(tx_ring, i);
+
+ if (first->tx_flags & ICE_TX_FLAGS_HW_VLAN) {
+ td_cmd |= (u64)ICE_TX_DESC_CMD_IL2TAG1;
+ td_tag = (first->tx_flags & ICE_TX_FLAGS_VLAN_M) >>
+ ICE_TX_FLAGS_VLAN_S;
+ }
+
+ dma = dma_map_single(tx_ring->dev, skb->data, size, DMA_TO_DEVICE);
+
+ tx_buf = first;
+
+ for (frag = &skb_shinfo(skb)->frags[0];; frag++) {
+ unsigned int max_data = ICE_MAX_DATA_PER_TXD_ALIGNED;
+
+ if (dma_mapping_error(tx_ring->dev, dma))
+ goto dma_error;
+
+ /* record length, and DMA address */
+ dma_unmap_len_set(tx_buf, len, size);
+ dma_unmap_addr_set(tx_buf, dma, dma);
+
+ /* align size to end of page */
+ max_data += -dma & (ICE_MAX_READ_REQ_SIZE - 1);
+ tx_desc->buf_addr = cpu_to_le64(dma);
+
+ /* account for data chunks larger than the hardware
+ * can handle
+ */
+ while (unlikely(size > ICE_MAX_DATA_PER_TXD)) {
+ tx_desc->cmd_type_offset_bsz =
+ build_ctob(td_cmd, td_offset, max_data, td_tag);
+
+ tx_desc++;
+ i++;
+
+ if (i == tx_ring->count) {
+ tx_desc = ICE_TX_DESC(tx_ring, 0);
+ i = 0;
+ }
+
+ dma += max_data;
+ size -= max_data;
+
+ max_data = ICE_MAX_DATA_PER_TXD_ALIGNED;
+ tx_desc->buf_addr = cpu_to_le64(dma);
+ }
+
+ if (likely(!data_len))
+ break;
+
+ tx_desc->cmd_type_offset_bsz = build_ctob(td_cmd, td_offset,
+ size, td_tag);
+
+ tx_desc++;
+ i++;
+
+ if (i == tx_ring->count) {
+ tx_desc = ICE_TX_DESC(tx_ring, 0);
+ i = 0;
+ }
+
+ size = skb_frag_size(frag);
+ data_len -= size;
+
+ dma = skb_frag_dma_map(tx_ring->dev, frag, 0, size,
+ DMA_TO_DEVICE);
+
+ tx_buf = &tx_ring->tx_buf[i];
+ }
+
+ /* record bytecount for BQL */
+ netdev_tx_sent_queue(txring_txq(tx_ring), first->bytecount);
+
+ /* record SW timestamp if HW timestamp is not available */
+ skb_tx_timestamp(first->skb);
+
+ i++;
+ if (i == tx_ring->count)
+ i = 0;
+
+ /* write last descriptor with RS and EOP bits */
+ td_cmd |= (u64)(ICE_TX_DESC_CMD_EOP | ICE_TX_DESC_CMD_RS);
+ tx_desc->cmd_type_offset_bsz =
+ build_ctob(td_cmd, td_offset, size, td_tag);
+
+ /* Force memory writes to complete before letting h/w know there
+ * are new descriptors to fetch.
+ *
+ * We also use this memory barrier to make certain all of the
+ * status bits have been updated before next_to_watch is written.
+ */
+ wmb();
+
+ /* set next_to_watch value indicating a packet is present */
+ first->next_to_watch = tx_desc;
+
+ tx_ring->next_to_use = i;
+
+ ice_maybe_stop_tx(tx_ring, DESC_NEEDED);
+
+ /* notify HW of packet */
+ if (netif_xmit_stopped(txring_txq(tx_ring)) || !skb->xmit_more) {
+ writel(i, tx_ring->tail);
+
+ /* we need this if more than one processor can write to our tail
+ * at a time, it synchronizes IO on IA64/Altix systems
+ */
+ mmiowb();
+ }
+
+ return;
+
+dma_error:
+ /* clear dma mappings for failed tx_buf map */
+ for (;;) {
+ tx_buf = &tx_ring->tx_buf[i];
+ ice_unmap_and_free_tx_buf(tx_ring, tx_buf);
+ if (tx_buf == first)
+ break;
+ if (i == 0)
+ i = tx_ring->count;
+ i--;
+ }
+
+ tx_ring->next_to_use = i;
+}
+
+/**
+ * ice_tx_csum - Enable Tx checksum offloads
+ * @first: pointer to the first descriptor
+ * @off: pointer to struct that holds offload parameters
+ *
+ * Returns 0 or error (negative) if checksum offload can't happen, 1 otherwise.
+ */
+static
+int ice_tx_csum(struct ice_tx_buf *first, struct ice_tx_offload_params *off)
+{
+ u32 l4_len = 0, l3_len = 0, l2_len = 0;
+ struct sk_buff *skb = first->skb;
+ union {
+ struct iphdr *v4;
+ struct ipv6hdr *v6;
+ unsigned char *hdr;
+ } ip;
+ union {
+ struct tcphdr *tcp;
+ unsigned char *hdr;
+ } l4;
+ __be16 frag_off, protocol;
+ unsigned char *exthdr;
+ u32 offset, cmd = 0;
+ u8 l4_proto = 0;
+
+ if (skb->ip_summed != CHECKSUM_PARTIAL)
+ return 0;
+
+ ip.hdr = skb_network_header(skb);
+ l4.hdr = skb_transport_header(skb);
+
+ /* compute outer L2 header size */
+ l2_len = ip.hdr - skb->data;
+ offset = (l2_len / 2) << ICE_TX_DESC_LEN_MACLEN_S;
+
+ if (skb->encapsulation)
+ return -1;
+
+ /* Enable IP checksum offloads */
+ protocol = vlan_get_protocol(skb);
+ if (protocol == htons(ETH_P_IP)) {
+ l4_proto = ip.v4->protocol;
+ /* the stack computes the IP header already, the only time we
+ * need the hardware to recompute it is in the case of TSO.
+ */
+ if (first->tx_flags & ICE_TX_FLAGS_TSO)
+ cmd |= ICE_TX_DESC_CMD_IIPT_IPV4_CSUM;
+ else
+ cmd |= ICE_TX_DESC_CMD_IIPT_IPV4;
+
+ } else if (protocol == htons(ETH_P_IPV6)) {
+ cmd |= ICE_TX_DESC_CMD_IIPT_IPV6;
+ exthdr = ip.hdr + sizeof(*ip.v6);
+ l4_proto = ip.v6->nexthdr;
+ if (l4.hdr != exthdr)
+ ipv6_skip_exthdr(skb, exthdr - skb->data, &l4_proto,
+ &frag_off);
+ } else {
+ return -1;
+ }
+
+ /* compute inner L3 header size */
+ l3_len = l4.hdr - ip.hdr;
+ offset |= (l3_len / 4) << ICE_TX_DESC_LEN_IPLEN_S;
+
+ /* Enable L4 checksum offloads */
+ switch (l4_proto) {
+ case IPPROTO_TCP:
+ /* enable checksum offloads */
+ cmd |= ICE_TX_DESC_CMD_L4T_EOFT_TCP;
+ l4_len = l4.tcp->doff;
+ offset |= l4_len << ICE_TX_DESC_LEN_L4_LEN_S;
+ break;
+ case IPPROTO_UDP:
+ /* enable UDP checksum offload */
+ cmd |= ICE_TX_DESC_CMD_L4T_EOFT_UDP;
+ l4_len = (sizeof(struct udphdr) >> 2);
+ offset |= l4_len << ICE_TX_DESC_LEN_L4_LEN_S;
+ break;
+ case IPPROTO_SCTP:
+ default:
+ if (first->tx_flags & ICE_TX_FLAGS_TSO)
+ return -1;
+ skb_checksum_help(skb);
+ return 0;
+ }
+
+ off->td_cmd |= cmd;
+ off->td_offset |= offset;
+ return 1;
+}
+
+/**
+ * ice_tx_prepare_vlan_flags - prepare generic TX VLAN tagging flags for HW
+ * @tx_ring: ring to send buffer on
+ * @first: pointer to struct ice_tx_buf
+ *
+ * Checks the skb and set up correspondingly several generic transmit flags
+ * related to VLAN tagging for the HW, such as VLAN, DCB, etc.
+ *
+ * Returns error code indicate the frame should be dropped upon error and the
+ * otherwise returns 0 to indicate the flags has been set properly.
+ */
+static int
+ice_tx_prepare_vlan_flags(struct ice_ring *tx_ring, struct ice_tx_buf *first)
+{
+ struct sk_buff *skb = first->skb;
+ __be16 protocol = skb->protocol;
+
+ if (protocol == htons(ETH_P_8021Q) &&
+ !(tx_ring->netdev->features & NETIF_F_HW_VLAN_CTAG_TX)) {
+ /* when HW VLAN acceleration is turned off by the user the
+ * stack sets the protocol to 8021q so that the driver
+ * can take any steps required to support the SW only
+ * VLAN handling. In our case the driver doesn't need
+ * to take any further steps so just set the protocol
+ * to the encapsulated ethertype.
+ */
+ skb->protocol = vlan_get_protocol(skb);
+ goto out;
+ }
+
+ /* if we have a HW VLAN tag being added, default to the HW one */
+ if (skb_vlan_tag_present(skb)) {
+ first->tx_flags |= skb_vlan_tag_get(skb) << ICE_TX_FLAGS_VLAN_S;
+ first->tx_flags |= ICE_TX_FLAGS_HW_VLAN;
+ } else if (protocol == htons(ETH_P_8021Q)) {
+ struct vlan_hdr *vhdr, _vhdr;
+
+ /* for SW VLAN, check the next protocol and store the tag */
+ vhdr = (struct vlan_hdr *)skb_header_pointer(skb, ETH_HLEN,
+ sizeof(_vhdr),
+ &_vhdr);
+ if (!vhdr)
+ return -EINVAL;
+
+ first->tx_flags |= ntohs(vhdr->h_vlan_TCI) <<
+ ICE_TX_FLAGS_VLAN_S;
+ first->tx_flags |= ICE_TX_FLAGS_SW_VLAN;
+ }
+
+out:
+ return 0;
+}
+
+/**
+ * ice_tso - computes mss and TSO length to prepare for TSO
+ * @first: pointer to struct ice_tx_buf
+ * @off: pointer to struct that holds offload parameters
+ *
+ * Returns 0 or error (negative) if TSO can't happen, 1 otherwise.
+ */
+static
+int ice_tso(struct ice_tx_buf *first, struct ice_tx_offload_params *off)
+{
+ struct sk_buff *skb = first->skb;
+ union {
+ struct iphdr *v4;
+ struct ipv6hdr *v6;
+ unsigned char *hdr;
+ } ip;
+ union {
+ struct tcphdr *tcp;
+ unsigned char *hdr;
+ } l4;
+ u64 cd_mss, cd_tso_len;
+ u32 paylen, l4_start;
+ int err;
+
+ if (skb->ip_summed != CHECKSUM_PARTIAL)
+ return 0;
+
+ if (!skb_is_gso(skb))
+ return 0;
+
+ err = skb_cow_head(skb, 0);
+ if (err < 0)
+ return err;
+
+ ip.hdr = skb_network_header(skb);
+ l4.hdr = skb_transport_header(skb);
+
+ /* initialize outer IP header fields */
+ if (ip.v4->version == 4) {
+ ip.v4->tot_len = 0;
+ ip.v4->check = 0;
+ } else {
+ ip.v6->payload_len = 0;
+ }
+
+ /* determine offset of transport header */
+ l4_start = l4.hdr - skb->data;
+
+ /* remove payload length from checksum */
+ paylen = skb->len - l4_start;
+ csum_replace_by_diff(&l4.tcp->check, (__force __wsum)htonl(paylen));
+
+ /* compute length of segmentation header */
+ off->header_len = (l4.tcp->doff * 4) + l4_start;
+
+ /* update gso_segs and bytecount */
+ first->gso_segs = skb_shinfo(skb)->gso_segs;
+ first->bytecount = (first->gso_segs - 1) * off->header_len;
+
+ cd_tso_len = skb->len - off->header_len;
+ cd_mss = skb_shinfo(skb)->gso_size;
+
+ /* record cdesc_qw1 with TSO parameters */
+ off->cd_qw1 |= ICE_TX_DESC_DTYPE_CTX |
+ (ICE_TX_CTX_DESC_TSO << ICE_TXD_CTX_QW1_CMD_S) |
+ (cd_tso_len << ICE_TXD_CTX_QW1_TSO_LEN_S) |
+ (cd_mss << ICE_TXD_CTX_QW1_MSS_S);
+ first->tx_flags |= ICE_TX_FLAGS_TSO;
+ return 1;
+}
+
+/**
+ * ice_txd_use_count - estimate the number of descriptors needed for Tx
+ * @size: transmit request size in bytes
+ *
+ * Due to hardware alignment restrictions (4K alignment), we need to
+ * assume that we can have no more than 12K of data per descriptor, even
+ * though each descriptor can take up to 16K - 1 bytes of aligned memory.
+ * Thus, we need to divide by 12K. But division is slow! Instead,
+ * we decompose the operation into shifts and one relatively cheap
+ * multiply operation.
+ *
+ * To divide by 12K, we first divide by 4K, then divide by 3:
+ * To divide by 4K, shift right by 12 bits
+ * To divide by 3, multiply by 85, then divide by 256
+ * (Divide by 256 is done by shifting right by 8 bits)
+ * Finally, we add one to round up. Because 256 isn't an exact multiple of
+ * 3, we'll underestimate near each multiple of 12K. This is actually more
+ * accurate as we have 4K - 1 of wiggle room that we can fit into the last
+ * segment. For our purposes this is accurate out to 1M which is orders of
+ * magnitude greater than our largest possible GSO size.
+ *
+ * This would then be implemented as:
+ * return (((size >> 12) * 85) >> 8) + 1;
+ *
+ * Since multiplication and division are commutative, we can reorder
+ * operations into:
+ * return ((size * 85) >> 20) + 1;
+ */
+static unsigned int ice_txd_use_count(unsigned int size)
+{
+ return ((size * 85) >> 20) + 1;
+}
+
+/**
+ * ice_xmit_desc_count - calculate number of tx descriptors needed
+ * @skb: send buffer
+ *
+ * Returns number of data descriptors needed for this skb.
+ */
+static unsigned int ice_xmit_desc_count(struct sk_buff *skb)
+{
+ const struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[0];
+ unsigned int nr_frags = skb_shinfo(skb)->nr_frags;
+ unsigned int count = 0, size = skb_headlen(skb);
+
+ for (;;) {
+ count += ice_txd_use_count(size);
+
+ if (!nr_frags--)
+ break;
+
+ size = skb_frag_size(frag++);
+ }
+
+ return count;
+}
+
+/**
+ * __ice_chk_linearize - Check if there are more than 8 buffers per packet
+ * @skb: send buffer
+ *
+ * Note: This HW can't DMA more than 8 buffers to build a packet on the wire
+ * and so we need to figure out the cases where we need to linearize the skb.
+ *
+ * For TSO we need to count the TSO header and segment payload separately.
+ * As such we need to check cases where we have 7 fragments or more as we
+ * can potentially require 9 DMA transactions, 1 for the TSO header, 1 for
+ * the segment payload in the first descriptor, and another 7 for the
+ * fragments.
+ */
+static bool __ice_chk_linearize(struct sk_buff *skb)
+{
+ const struct skb_frag_struct *frag, *stale;
+ int nr_frags, sum;
+
+ /* no need to check if number of frags is less than 7 */
+ nr_frags = skb_shinfo(skb)->nr_frags;
+ if (nr_frags < (ICE_MAX_BUF_TXD - 1))
+ return false;
+
+ /* We need to walk through the list and validate that each group
+ * of 6 fragments totals at least gso_size.
+ */
+ nr_frags -= ICE_MAX_BUF_TXD - 2;
+ frag = &skb_shinfo(skb)->frags[0];
+
+ /* Initialize size to the negative value of gso_size minus 1. We
+ * use this as the worst case scenerio in which the frag ahead
+ * of us only provides one byte which is why we are limited to 6
+ * descriptors for a single transmit as the header and previous
+ * fragment are already consuming 2 descriptors.
+ */
+ sum = 1 - skb_shinfo(skb)->gso_size;
+
+ /* Add size of frags 0 through 4 to create our initial sum */
+ sum += skb_frag_size(frag++);
+ sum += skb_frag_size(frag++);
+ sum += skb_frag_size(frag++);
+ sum += skb_frag_size(frag++);
+ sum += skb_frag_size(frag++);
+
+ /* Walk through fragments adding latest fragment, testing it, and
+ * then removing stale fragments from the sum.
+ */
+ stale = &skb_shinfo(skb)->frags[0];
+ for (;;) {
+ sum += skb_frag_size(frag++);
+
+ /* if sum is negative we failed to make sufficient progress */
+ if (sum < 0)
+ return true;
+
+ if (!nr_frags--)
+ break;
+
+ sum -= skb_frag_size(stale++);
+ }
+
+ return false;
+}
+
+/**
+ * ice_chk_linearize - Check if there are more than 8 fragments per packet
+ * @skb: send buffer
+ * @count: number of buffers used
+ *
+ * Note: Our HW can't scatter-gather more than 8 fragments to build
+ * a packet on the wire and so we need to figure out the cases where we
+ * need to linearize the skb.
+ */
+static bool ice_chk_linearize(struct sk_buff *skb, unsigned int count)
+{
+ /* Both TSO and single send will work if count is less than 8 */
+ if (likely(count < ICE_MAX_BUF_TXD))
+ return false;
+
+ if (skb_is_gso(skb))
+ return __ice_chk_linearize(skb);
+
+ /* we can support up to 8 data buffers for a single send */
+ return count != ICE_MAX_BUF_TXD;
+}
+
+/**
+ * ice_xmit_frame_ring - Sends buffer on Tx ring
+ * @skb: send buffer
+ * @tx_ring: ring to send buffer on
+ *
+ * Returns NETDEV_TX_OK if sent, else an error code
+ */
+static netdev_tx_t
+ice_xmit_frame_ring(struct sk_buff *skb, struct ice_ring *tx_ring)
+{
+ struct ice_tx_offload_params offload = { 0 };
+ struct ice_tx_buf *first;
+ unsigned int count;
+ int tso, csum;
+
+ count = ice_xmit_desc_count(skb);
+ if (ice_chk_linearize(skb, count)) {
+ if (__skb_linearize(skb))
+ goto out_drop;
+ count = ice_txd_use_count(skb->len);
+ tx_ring->tx_stats.tx_linearize++;
+ }
+
+ /* need: 1 descriptor per page * PAGE_SIZE/ICE_MAX_DATA_PER_TXD,
+ * + 1 desc for skb_head_len/ICE_MAX_DATA_PER_TXD,
+ * + 4 desc gap to avoid the cache line where head is,
+ * + 1 desc for context descriptor,
+ * otherwise try next time
+ */
+ if (ice_maybe_stop_tx(tx_ring, count + 4 + 1)) {
+ tx_ring->tx_stats.tx_busy++;
+ return NETDEV_TX_BUSY;
+ }
+
+ offload.tx_ring = tx_ring;
+
+ /* record the location of the first descriptor for this packet */
+ first = &tx_ring->tx_buf[tx_ring->next_to_use];
+ first->skb = skb;
+ first->bytecount = max_t(unsigned int, skb->len, ETH_ZLEN);
+ first->gso_segs = 1;
+ first->tx_flags = 0;
+
+ /* prepare the VLAN tagging flags for Tx */
+ if (ice_tx_prepare_vlan_flags(tx_ring, first))
+ goto out_drop;
+
+ /* set up TSO offload */
+ tso = ice_tso(first, &offload);
+ if (tso < 0)
+ goto out_drop;
+
+ /* always set up Tx checksum offload */
+ csum = ice_tx_csum(first, &offload);
+ if (csum < 0)
+ goto out_drop;
+
+ if (tso || offload.cd_tunnel_params) {
+ struct ice_tx_ctx_desc *cdesc;
+ int i = tx_ring->next_to_use;
+
+ /* grab the next descriptor */
+ cdesc = ICE_TX_CTX_DESC(tx_ring, i);
+ i++;
+ tx_ring->next_to_use = (i < tx_ring->count) ? i : 0;
+
+ /* setup context descriptor */
+ cdesc->tunneling_params = cpu_to_le32(offload.cd_tunnel_params);
+ cdesc->l2tag2 = cpu_to_le16(offload.cd_l2tag2);
+ cdesc->rsvd = cpu_to_le16(0);
+ cdesc->qw1 = cpu_to_le64(offload.cd_qw1);
+ }
+
+ ice_tx_map(tx_ring, first, &offload);
+ return NETDEV_TX_OK;
+
+out_drop:
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+}
+
+/**
+ * ice_start_xmit - Selects the correct VSI and Tx queue to send buffer
+ * @skb: send buffer
+ * @netdev: network interface device structure
+ *
+ * Returns NETDEV_TX_OK if sent, else an error code
+ */
+netdev_tx_t ice_start_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct ice_vsi *vsi = np->vsi;
+ struct ice_ring *tx_ring;
+
+ tx_ring = vsi->tx_rings[skb->queue_mapping];
+
+ /* hardware can't handle really short frames, hardware padding works
+ * beyond this point
+ */
+ if (skb_put_padto(skb, ICE_MIN_TX_LEN))
+ return NETDEV_TX_OK;
+
+ return ice_xmit_frame_ring(skb, tx_ring);
+}
diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.h b/drivers/net/ethernet/intel/ice/ice_txrx.h
new file mode 100644
index 000000000000..567067b650c4
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_txrx.h
@@ -0,0 +1,192 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018, Intel Corporation. */
+
+#ifndef _ICE_TXRX_H_
+#define _ICE_TXRX_H_
+
+#define ICE_DFLT_IRQ_WORK 256
+#define ICE_RXBUF_2048 2048
+#define ICE_MAX_CHAINED_RX_BUFS 5
+#define ICE_MAX_BUF_TXD 8
+#define ICE_MIN_TX_LEN 17
+
+/* The size limit for a transmit buffer in a descriptor is (16K - 1).
+ * In order to align with the read requests we will align the value to
+ * the nearest 4K which represents our maximum read request size.
+ */
+#define ICE_MAX_READ_REQ_SIZE 4096
+#define ICE_MAX_DATA_PER_TXD (16 * 1024 - 1)
+#define ICE_MAX_DATA_PER_TXD_ALIGNED \
+ (~(ICE_MAX_READ_REQ_SIZE - 1) & ICE_MAX_DATA_PER_TXD)
+
+#define ICE_RX_BUF_WRITE 16 /* Must be power of 2 */
+#define ICE_MAX_TXQ_PER_TXQG 128
+
+/* Tx Descriptors needed, worst case */
+#define DESC_NEEDED (MAX_SKB_FRAGS + 4)
+#define ICE_DESC_UNUSED(R) \
+ ((((R)->next_to_clean > (R)->next_to_use) ? 0 : (R)->count) + \
+ (R)->next_to_clean - (R)->next_to_use - 1)
+
+#define ICE_TX_FLAGS_TSO BIT(0)
+#define ICE_TX_FLAGS_HW_VLAN BIT(1)
+#define ICE_TX_FLAGS_SW_VLAN BIT(2)
+#define ICE_TX_FLAGS_VLAN_M 0xffff0000
+#define ICE_TX_FLAGS_VLAN_S 16
+
+struct ice_tx_buf {
+ struct ice_tx_desc *next_to_watch;
+ struct sk_buff *skb;
+ unsigned int bytecount;
+ unsigned short gso_segs;
+ u32 tx_flags;
+ DEFINE_DMA_UNMAP_ADDR(dma);
+ DEFINE_DMA_UNMAP_LEN(len);
+};
+
+struct ice_tx_offload_params {
+ u8 header_len;
+ u32 td_cmd;
+ u32 td_offset;
+ u32 td_l2tag1;
+ u16 cd_l2tag2;
+ u32 cd_tunnel_params;
+ u64 cd_qw1;
+ struct ice_ring *tx_ring;
+};
+
+struct ice_rx_buf {
+ struct sk_buff *skb;
+ dma_addr_t dma;
+ struct page *page;
+ unsigned int page_offset;
+};
+
+struct ice_q_stats {
+ u64 pkts;
+ u64 bytes;
+};
+
+struct ice_txq_stats {
+ u64 restart_q;
+ u64 tx_busy;
+ u64 tx_linearize;
+};
+
+struct ice_rxq_stats {
+ u64 non_eop_descs;
+ u64 alloc_page_failed;
+ u64 alloc_buf_failed;
+ u64 page_reuse_count;
+};
+
+/* this enum matches hardware bits and is meant to be used by DYN_CTLN
+ * registers and QINT registers or more generally anywhere in the manual
+ * mentioning ITR_INDX, ITR_NONE cannot be used as an index 'n' into any
+ * register but instead is a special value meaning "don't update" ITR0/1/2.
+ */
+enum ice_dyn_idx_t {
+ ICE_IDX_ITR0 = 0,
+ ICE_IDX_ITR1 = 1,
+ ICE_IDX_ITR2 = 2,
+ ICE_ITR_NONE = 3 /* ITR_NONE must not be used as an index */
+};
+
+/* Header split modes defined by DTYPE field of Rx RLAN context */
+enum ice_rx_dtype {
+ ICE_RX_DTYPE_NO_SPLIT = 0,
+ ICE_RX_DTYPE_HEADER_SPLIT = 1,
+ ICE_RX_DTYPE_SPLIT_ALWAYS = 2,
+};
+
+/* indices into GLINT_ITR registers */
+#define ICE_RX_ITR ICE_IDX_ITR0
+#define ICE_TX_ITR ICE_IDX_ITR1
+#define ICE_ITR_DYNAMIC 0x8000 /* use top bit as a flag */
+#define ICE_ITR_8K 0x003E
+
+/* apply ITR HW granularity translation to program the HW registers */
+#define ITR_TO_REG(val, itr_gran) (((val) & ~ICE_ITR_DYNAMIC) >> (itr_gran))
+
+/* Legacy or Advanced Mode Queue */
+#define ICE_TX_ADVANCED 0
+#define ICE_TX_LEGACY 1
+
+/* descriptor ring, associated with a VSI */
+struct ice_ring {
+ struct ice_ring *next; /* pointer to next ring in q_vector */
+ void *desc; /* Descriptor ring memory */
+ struct device *dev; /* Used for DMA mapping */
+ struct net_device *netdev; /* netdev ring maps to */
+ struct ice_vsi *vsi; /* Backreference to associated VSI */
+ struct ice_q_vector *q_vector; /* Backreference to associated vector */
+ u8 __iomem *tail;
+ union {
+ struct ice_tx_buf *tx_buf;
+ struct ice_rx_buf *rx_buf;
+ };
+ u16 q_index; /* Queue number of ring */
+ u32 txq_teid; /* Added Tx queue TEID */
+
+ /* high bit set means dynamic, use accessor routines to read/write.
+ * hardware supports 2us/1us resolution for the ITR registers.
+ * these values always store the USER setting, and must be converted
+ * before programming to a register.
+ */
+ u16 rx_itr_setting;
+ u16 tx_itr_setting;
+
+ u16 count; /* Number of descriptors */
+ u16 reg_idx; /* HW register index of the ring */
+
+ /* used in interrupt processing */
+ u16 next_to_use;
+ u16 next_to_clean;
+
+ bool ring_active; /* is ring online or not */
+
+ /* stats structs */
+ struct ice_q_stats stats;
+ struct u64_stats_sync syncp;
+ union {
+ struct ice_txq_stats tx_stats;
+ struct ice_rxq_stats rx_stats;
+ };
+
+ unsigned int size; /* length of descriptor ring in bytes */
+ dma_addr_t dma; /* physical address of ring */
+ struct rcu_head rcu; /* to avoid race on free */
+ u16 next_to_alloc;
+} ____cacheline_internodealigned_in_smp;
+
+enum ice_latency_range {
+ ICE_LOWEST_LATENCY = 0,
+ ICE_LOW_LATENCY = 1,
+ ICE_BULK_LATENCY = 2,
+ ICE_ULTRA_LATENCY = 3,
+};
+
+struct ice_ring_container {
+ /* array of pointers to rings */
+ struct ice_ring *ring;
+ unsigned int total_bytes; /* total bytes processed this int */
+ unsigned int total_pkts; /* total packets processed this int */
+ enum ice_latency_range latency_range;
+ u16 itr;
+};
+
+/* iterator for handling rings in ring container */
+#define ice_for_each_ring(pos, head) \
+ for (pos = (head).ring; pos; pos = pos->next)
+
+bool ice_alloc_rx_bufs(struct ice_ring *rxr, u16 cleaned_count);
+netdev_tx_t ice_start_xmit(struct sk_buff *skb, struct net_device *netdev);
+void ice_clean_tx_ring(struct ice_ring *tx_ring);
+void ice_clean_rx_ring(struct ice_ring *rx_ring);
+int ice_setup_tx_ring(struct ice_ring *tx_ring);
+int ice_setup_rx_ring(struct ice_ring *rx_ring);
+void ice_free_tx_ring(struct ice_ring *tx_ring);
+void ice_free_rx_ring(struct ice_ring *rx_ring);
+int ice_napi_poll(struct napi_struct *napi, int budget);
+
+#endif /* _ICE_TXRX_H_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h
new file mode 100644
index 000000000000..99c8a9a71b5e
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_type.h
@@ -0,0 +1,394 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018, Intel Corporation. */
+
+#ifndef _ICE_TYPE_H_
+#define _ICE_TYPE_H_
+
+#include "ice_status.h"
+#include "ice_hw_autogen.h"
+#include "ice_osdep.h"
+#include "ice_controlq.h"
+#include "ice_lan_tx_rx.h"
+
+#define ICE_BYTES_PER_WORD 2
+#define ICE_BYTES_PER_DWORD 4
+
+static inline bool ice_is_tc_ena(u8 bitmap, u8 tc)
+{
+ return test_bit(tc, (unsigned long *)&bitmap);
+}
+
+/* debug masks - set these bits in hw->debug_mask to control output */
+#define ICE_DBG_INIT BIT_ULL(1)
+#define ICE_DBG_LINK BIT_ULL(4)
+#define ICE_DBG_QCTX BIT_ULL(6)
+#define ICE_DBG_NVM BIT_ULL(7)
+#define ICE_DBG_LAN BIT_ULL(8)
+#define ICE_DBG_SW BIT_ULL(13)
+#define ICE_DBG_SCHED BIT_ULL(14)
+#define ICE_DBG_RES BIT_ULL(17)
+#define ICE_DBG_AQ_MSG BIT_ULL(24)
+#define ICE_DBG_AQ_CMD BIT_ULL(27)
+#define ICE_DBG_USER BIT_ULL(31)
+
+enum ice_aq_res_ids {
+ ICE_NVM_RES_ID = 1,
+ ICE_SPD_RES_ID,
+ ICE_GLOBAL_CFG_LOCK_RES_ID,
+ ICE_CHANGE_LOCK_RES_ID
+};
+
+enum ice_aq_res_access_type {
+ ICE_RES_READ = 1,
+ ICE_RES_WRITE
+};
+
+enum ice_fc_mode {
+ ICE_FC_NONE = 0,
+ ICE_FC_RX_PAUSE,
+ ICE_FC_TX_PAUSE,
+ ICE_FC_FULL,
+ ICE_FC_PFC,
+ ICE_FC_DFLT
+};
+
+enum ice_set_fc_aq_failures {
+ ICE_SET_FC_AQ_FAIL_NONE = 0,
+ ICE_SET_FC_AQ_FAIL_GET,
+ ICE_SET_FC_AQ_FAIL_SET,
+ ICE_SET_FC_AQ_FAIL_UPDATE
+};
+
+/* Various MAC types */
+enum ice_mac_type {
+ ICE_MAC_UNKNOWN = 0,
+ ICE_MAC_GENERIC,
+};
+
+/* Media Types */
+enum ice_media_type {
+ ICE_MEDIA_UNKNOWN = 0,
+ ICE_MEDIA_FIBER,
+ ICE_MEDIA_BASET,
+ ICE_MEDIA_BACKPLANE,
+ ICE_MEDIA_DA,
+};
+
+enum ice_vsi_type {
+ ICE_VSI_PF = 0,
+};
+
+struct ice_link_status {
+ /* Refer to ice_aq_phy_type for bits definition */
+ u64 phy_type_low;
+ u16 max_frame_size;
+ u16 link_speed;
+ bool lse_ena; /* Link Status Event notification */
+ u8 link_info;
+ u8 an_info;
+ u8 ext_info;
+ u8 pacing;
+ u8 req_speeds;
+ /* Refer to #define from module_type[ICE_MODULE_TYPE_TOTAL_BYTE] of
+ * ice_aqc_get_phy_caps structure
+ */
+ u8 module_type[ICE_MODULE_TYPE_TOTAL_BYTE];
+};
+
+/* PHY info such as phy_type, etc... */
+struct ice_phy_info {
+ struct ice_link_status link_info;
+ struct ice_link_status link_info_old;
+ u64 phy_type_low;
+ enum ice_media_type media_type;
+ bool get_link_info;
+};
+
+/* Common HW capabilities for SW use */
+struct ice_hw_common_caps {
+ /* TX/RX queues */
+ u16 num_rxq; /* Number/Total RX queues */
+ u16 rxq_first_id; /* First queue ID for RX queues */
+ u16 num_txq; /* Number/Total TX queues */
+ u16 txq_first_id; /* First queue ID for TX queues */
+
+ /* MSI-X vectors */
+ u16 num_msix_vectors;
+ u16 msix_vector_first_id;
+
+ /* Max MTU for function or device */
+ u16 max_mtu;
+
+ /* RSS related capabilities */
+ u16 rss_table_size; /* 512 for PFs and 64 for VFs */
+ u8 rss_table_entry_width; /* RSS Entry width in bits */
+};
+
+/* Function specific capabilities */
+struct ice_hw_func_caps {
+ struct ice_hw_common_caps common_cap;
+ u32 guaranteed_num_vsi;
+};
+
+/* Device wide capabilities */
+struct ice_hw_dev_caps {
+ struct ice_hw_common_caps common_cap;
+ u32 num_vsi_allocd_to_host; /* Excluding EMP VSI */
+};
+
+/* MAC info */
+struct ice_mac_info {
+ u8 lan_addr[ETH_ALEN];
+ u8 perm_addr[ETH_ALEN];
+};
+
+/* Various RESET request, These are not tied with HW reset types */
+enum ice_reset_req {
+ ICE_RESET_PFR = 0,
+ ICE_RESET_CORER = 1,
+ ICE_RESET_GLOBR = 2,
+};
+
+/* Bus parameters */
+struct ice_bus_info {
+ u16 device;
+ u8 func;
+};
+
+/* Flow control (FC) parameters */
+struct ice_fc_info {
+ enum ice_fc_mode current_mode; /* FC mode in effect */
+ enum ice_fc_mode req_mode; /* FC mode requested by caller */
+};
+
+/* NVM Information */
+struct ice_nvm_info {
+ u32 eetrack; /* NVM data version */
+ u32 oem_ver; /* OEM version info */
+ u16 sr_words; /* Shadow RAM size in words */
+ u16 ver; /* NVM package version */
+ bool blank_nvm_mode; /* is NVM empty (no FW present) */
+};
+
+/* Max number of port to queue branches w.r.t topology */
+#define ICE_MAX_TRAFFIC_CLASS 8
+#define ICE_TXSCHED_MAX_BRANCHES ICE_MAX_TRAFFIC_CLASS
+
+struct ice_sched_node {
+ struct ice_sched_node *parent;
+ struct ice_sched_node *sibling; /* next sibling in the same layer */
+ struct ice_sched_node **children;
+ struct ice_aqc_txsched_elem_data info;
+ u32 agg_id; /* aggregator group id */
+ u16 vsi_id;
+ bool in_use; /* suspended or in use */
+ u8 tx_sched_layer; /* Logical Layer (1-9) */
+ u8 num_children;
+ u8 tc_num;
+ u8 owner;
+#define ICE_SCHED_NODE_OWNER_LAN 0
+};
+
+/* Access Macros for Tx Sched Elements data */
+#define ICE_TXSCHED_GET_NODE_TEID(x) le32_to_cpu((x)->info.node_teid)
+
+/* The aggregator type determines if identifier is for a VSI group,
+ * aggregator group, aggregator of queues, or queue group.
+ */
+enum ice_agg_type {
+ ICE_AGG_TYPE_UNKNOWN = 0,
+ ICE_AGG_TYPE_VSI,
+ ICE_AGG_TYPE_AGG, /* aggregator */
+ ICE_AGG_TYPE_Q,
+ ICE_AGG_TYPE_QG
+};
+
+#define ICE_SCHED_DFLT_RL_PROF_ID 0
+
+/* vsi type list entry to locate corresponding vsi/ag nodes */
+struct ice_sched_vsi_info {
+ struct ice_sched_node *vsi_node[ICE_MAX_TRAFFIC_CLASS];
+ struct ice_sched_node *ag_node[ICE_MAX_TRAFFIC_CLASS];
+ struct list_head list_entry;
+ u16 max_lanq[ICE_MAX_TRAFFIC_CLASS];
+ u16 vsi_id;
+};
+
+/* driver defines the policy */
+struct ice_sched_tx_policy {
+ u16 max_num_vsis;
+ u8 max_num_lan_qs_per_tc[ICE_MAX_TRAFFIC_CLASS];
+ bool rdma_ena;
+};
+
+struct ice_port_info {
+ struct ice_sched_node *root; /* Root Node per Port */
+ struct ice_hw *hw; /* back pointer to hw instance */
+ u32 last_node_teid; /* scheduler last node info */
+ u16 sw_id; /* Initial switch ID belongs to port */
+ u16 pf_vf_num;
+ u8 port_state;
+#define ICE_SCHED_PORT_STATE_INIT 0x0
+#define ICE_SCHED_PORT_STATE_READY 0x1
+ u16 dflt_tx_vsi_rule_id;
+ u16 dflt_tx_vsi_num;
+ u16 dflt_rx_vsi_rule_id;
+ u16 dflt_rx_vsi_num;
+ struct ice_fc_info fc;
+ struct ice_mac_info mac;
+ struct ice_phy_info phy;
+ struct mutex sched_lock; /* protect access to TXSched tree */
+ struct ice_sched_tx_policy sched_policy;
+ struct list_head vsi_info_list;
+ struct list_head agg_list; /* lists all aggregator */
+ u8 lport;
+#define ICE_LPORT_MASK 0xff
+ bool is_vf;
+};
+
+struct ice_switch_info {
+ /* Switch VSI lists to MAC/VLAN translation */
+ struct mutex mac_list_lock; /* protect MAC list */
+ struct list_head mac_list_head;
+ struct mutex vlan_list_lock; /* protect VLAN list */
+ struct list_head vlan_list_head;
+ struct mutex eth_m_list_lock; /* protect ethtype list */
+ struct list_head eth_m_list_head;
+ struct mutex promisc_list_lock; /* protect promisc mode list */
+ struct list_head promisc_list_head;
+ struct mutex mac_vlan_list_lock; /* protect MAC-VLAN list */
+ struct list_head mac_vlan_list_head;
+
+ struct list_head vsi_list_map_head;
+};
+
+/* Port hardware description */
+struct ice_hw {
+ u8 __iomem *hw_addr;
+ void *back;
+ struct ice_aqc_layer_props *layer_info;
+ struct ice_port_info *port_info;
+ u64 debug_mask; /* bitmap for debug mask */
+ enum ice_mac_type mac_type;
+
+ /* pci info */
+ u16 device_id;
+ u16 vendor_id;
+ u16 subsystem_device_id;
+ u16 subsystem_vendor_id;
+ u8 revision_id;
+
+ u8 pf_id; /* device profile info */
+
+ /* TX Scheduler values */
+ u16 num_tx_sched_layers;
+ u16 num_tx_sched_phys_layers;
+ u8 flattened_layers;
+ u8 max_cgds;
+ u8 sw_entry_point_layer;
+
+ bool evb_veb; /* true for VEB, false for VEPA */
+ struct ice_bus_info bus;
+ struct ice_nvm_info nvm;
+ struct ice_hw_dev_caps dev_caps; /* device capabilities */
+ struct ice_hw_func_caps func_caps; /* function capabilities */
+
+ struct ice_switch_info *switch_info; /* switch filter lists */
+
+ /* Control Queue info */
+ struct ice_ctl_q_info adminq;
+
+ u8 api_branch; /* API branch version */
+ u8 api_maj_ver; /* API major version */
+ u8 api_min_ver; /* API minor version */
+ u8 api_patch; /* API patch version */
+ u8 fw_branch; /* firmware branch version */
+ u8 fw_maj_ver; /* firmware major version */
+ u8 fw_min_ver; /* firmware minor version */
+ u8 fw_patch; /* firmware patch version */
+ u32 fw_build; /* firmware build number */
+
+ /* minimum allowed value for different speeds */
+#define ICE_ITR_GRAN_MIN_200 1
+#define ICE_ITR_GRAN_MIN_100 1
+#define ICE_ITR_GRAN_MIN_50 2
+#define ICE_ITR_GRAN_MIN_25 4
+ /* ITR granularity in 1 us */
+ u8 itr_gran_200;
+ u8 itr_gran_100;
+ u8 itr_gran_50;
+ u8 itr_gran_25;
+ bool ucast_shared; /* true if VSIs can share unicast addr */
+
+};
+
+/* Statistics collected by each port, VSI, VEB, and S-channel */
+struct ice_eth_stats {
+ u64 rx_bytes; /* gorc */
+ u64 rx_unicast; /* uprc */
+ u64 rx_multicast; /* mprc */
+ u64 rx_broadcast; /* bprc */
+ u64 rx_discards; /* rdpc */
+ u64 rx_unknown_protocol; /* rupp */
+ u64 tx_bytes; /* gotc */
+ u64 tx_unicast; /* uptc */
+ u64 tx_multicast; /* mptc */
+ u64 tx_broadcast; /* bptc */
+ u64 tx_discards; /* tdpc */
+ u64 tx_errors; /* tepc */
+};
+
+/* Statistics collected by the MAC */
+struct ice_hw_port_stats {
+ /* eth stats collected by the port */
+ struct ice_eth_stats eth;
+ /* additional port specific stats */
+ u64 tx_dropped_link_down; /* tdold */
+ u64 crc_errors; /* crcerrs */
+ u64 illegal_bytes; /* illerrc */
+ u64 error_bytes; /* errbc */
+ u64 mac_local_faults; /* mlfc */
+ u64 mac_remote_faults; /* mrfc */
+ u64 rx_len_errors; /* rlec */
+ u64 link_xon_rx; /* lxonrxc */
+ u64 link_xoff_rx; /* lxoffrxc */
+ u64 link_xon_tx; /* lxontxc */
+ u64 link_xoff_tx; /* lxofftxc */
+ u64 rx_size_64; /* prc64 */
+ u64 rx_size_127; /* prc127 */
+ u64 rx_size_255; /* prc255 */
+ u64 rx_size_511; /* prc511 */
+ u64 rx_size_1023; /* prc1023 */
+ u64 rx_size_1522; /* prc1522 */
+ u64 rx_size_big; /* prc9522 */
+ u64 rx_undersize; /* ruc */
+ u64 rx_fragments; /* rfc */
+ u64 rx_oversize; /* roc */
+ u64 rx_jabber; /* rjc */
+ u64 tx_size_64; /* ptc64 */
+ u64 tx_size_127; /* ptc127 */
+ u64 tx_size_255; /* ptc255 */
+ u64 tx_size_511; /* ptc511 */
+ u64 tx_size_1023; /* ptc1023 */
+ u64 tx_size_1522; /* ptc1522 */
+ u64 tx_size_big; /* ptc9522 */
+};
+
+/* Checksum and Shadow RAM pointers */
+#define ICE_SR_NVM_DEV_STARTER_VER 0x18
+#define ICE_SR_NVM_EETRACK_LO 0x2D
+#define ICE_SR_NVM_EETRACK_HI 0x2E
+#define ICE_NVM_VER_LO_SHIFT 0
+#define ICE_NVM_VER_LO_MASK (0xff << ICE_NVM_VER_LO_SHIFT)
+#define ICE_NVM_VER_HI_SHIFT 12
+#define ICE_NVM_VER_HI_MASK (0xf << ICE_NVM_VER_HI_SHIFT)
+#define ICE_OEM_VER_PATCH_SHIFT 0
+#define ICE_OEM_VER_PATCH_MASK (0xff << ICE_OEM_VER_PATCH_SHIFT)
+#define ICE_OEM_VER_BUILD_SHIFT 8
+#define ICE_OEM_VER_BUILD_MASK (0xffff << ICE_OEM_VER_BUILD_SHIFT)
+#define ICE_OEM_VER_SHIFT 24
+#define ICE_OEM_VER_MASK (0xff << ICE_OEM_VER_SHIFT)
+#define ICE_SR_SECTOR_SIZE_IN_WORDS 0x800
+#define ICE_SR_WORDS_IN_1KB 512
+
+#endif /* _ICE_TYPE_H_ */
diff --git a/drivers/net/ethernet/intel/igb/Makefile b/drivers/net/ethernet/intel/igb/Makefile
index 5bcb2de75933..c48583e98ac1 100644
--- a/drivers/net/ethernet/intel/igb/Makefile
+++ b/drivers/net/ethernet/intel/igb/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
################################################################################
#
# Intel 82575 PCI-Express Ethernet Linux driver
diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c
index c37cc8bccf47..dd9b6cac220d 100644
--- a/drivers/net/ethernet/intel/igb/e1000_82575.c
+++ b/drivers/net/ethernet/intel/igb/e1000_82575.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel(R) Gigabit Ethernet Linux driver
* Copyright(c) 2007-2015 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.h b/drivers/net/ethernet/intel/igb/e1000_82575.h
index acf06051e111..e53ebe97d709 100644
--- a/drivers/net/ethernet/intel/igb/e1000_82575.h
+++ b/drivers/net/ethernet/intel/igb/e1000_82575.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Intel(R) Gigabit Ethernet Linux driver
* Copyright(c) 2007-2014 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/igb/e1000_defines.h b/drivers/net/ethernet/intel/igb/e1000_defines.h
index 83cabff1e0ab..98534f765e0e 100644
--- a/drivers/net/ethernet/intel/igb/e1000_defines.h
+++ b/drivers/net/ethernet/intel/igb/e1000_defines.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Intel(R) Gigabit Ethernet Linux driver
* Copyright(c) 2007-2014 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/igb/e1000_hw.h b/drivers/net/ethernet/intel/igb/e1000_hw.h
index 6c9485ab4b57..ff835e1e853d 100644
--- a/drivers/net/ethernet/intel/igb/e1000_hw.h
+++ b/drivers/net/ethernet/intel/igb/e1000_hw.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Intel(R) Gigabit Ethernet Linux driver
* Copyright(c) 2007-2014 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/igb/e1000_i210.c b/drivers/net/ethernet/intel/igb/e1000_i210.c
index 07d48f2e3369..6f548247e6d8 100644
--- a/drivers/net/ethernet/intel/igb/e1000_i210.c
+++ b/drivers/net/ethernet/intel/igb/e1000_i210.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel(R) Gigabit Ethernet Linux driver
* Copyright(c) 2007-2014 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/igb/e1000_i210.h b/drivers/net/ethernet/intel/igb/e1000_i210.h
index b2964a2a60b1..56f015ccb206 100644
--- a/drivers/net/ethernet/intel/igb/e1000_i210.h
+++ b/drivers/net/ethernet/intel/igb/e1000_i210.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Intel(R) Gigabit Ethernet Linux driver
* Copyright(c) 2007-2014 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/igb/e1000_mac.c b/drivers/net/ethernet/intel/igb/e1000_mac.c
index 5eff82678f0b..298afa0d9159 100644
--- a/drivers/net/ethernet/intel/igb/e1000_mac.c
+++ b/drivers/net/ethernet/intel/igb/e1000_mac.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel(R) Gigabit Ethernet Linux driver
* Copyright(c) 2007-2014 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/igb/e1000_mac.h b/drivers/net/ethernet/intel/igb/e1000_mac.h
index 90c8893c3eed..04d80c765aee 100644
--- a/drivers/net/ethernet/intel/igb/e1000_mac.h
+++ b/drivers/net/ethernet/intel/igb/e1000_mac.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Intel(R) Gigabit Ethernet Linux driver
* Copyright(c) 2007-2014 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/igb/e1000_mbx.c b/drivers/net/ethernet/intel/igb/e1000_mbx.c
index bffd58f7b2a1..ef42f1689b3b 100644
--- a/drivers/net/ethernet/intel/igb/e1000_mbx.c
+++ b/drivers/net/ethernet/intel/igb/e1000_mbx.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel(R) Gigabit Ethernet Linux driver
* Copyright(c) 2007-2014 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/igb/e1000_mbx.h b/drivers/net/ethernet/intel/igb/e1000_mbx.h
index a62b08e1572e..4f0ecd28354d 100644
--- a/drivers/net/ethernet/intel/igb/e1000_mbx.h
+++ b/drivers/net/ethernet/intel/igb/e1000_mbx.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Intel(R) Gigabit Ethernet Linux driver
* Copyright(c) 2007-2014 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/igb/e1000_nvm.c b/drivers/net/ethernet/intel/igb/e1000_nvm.c
index 3582c5cf8843..e4596f151cd4 100644
--- a/drivers/net/ethernet/intel/igb/e1000_nvm.c
+++ b/drivers/net/ethernet/intel/igb/e1000_nvm.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel(R) Gigabit Ethernet Linux driver
* Copyright(c) 2007-2014 Intel Corporation.
* This program is free software; you can redistribute it and/or modify it
diff --git a/drivers/net/ethernet/intel/igb/e1000_nvm.h b/drivers/net/ethernet/intel/igb/e1000_nvm.h
index febc9cdb7391..dde68cd54a53 100644
--- a/drivers/net/ethernet/intel/igb/e1000_nvm.h
+++ b/drivers/net/ethernet/intel/igb/e1000_nvm.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Intel(R) Gigabit Ethernet Linux driver
* Copyright(c) 2007-2014 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.c b/drivers/net/ethernet/intel/igb/e1000_phy.c
index 413025bdcb50..4ec61243da82 100644
--- a/drivers/net/ethernet/intel/igb/e1000_phy.c
+++ b/drivers/net/ethernet/intel/igb/e1000_phy.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel(R) Gigabit Ethernet Linux driver
* Copyright(c) 2007-2015 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.h b/drivers/net/ethernet/intel/igb/e1000_phy.h
index 9b622b33bb5a..856d2cda0643 100644
--- a/drivers/net/ethernet/intel/igb/e1000_phy.h
+++ b/drivers/net/ethernet/intel/igb/e1000_phy.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Intel(R) Gigabit Ethernet Linux driver
* Copyright(c) 2007-2014 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/igb/e1000_regs.h b/drivers/net/ethernet/intel/igb/e1000_regs.h
index 568c96842f28..e8fa8c6530e0 100644
--- a/drivers/net/ethernet/intel/igb/e1000_regs.h
+++ b/drivers/net/ethernet/intel/igb/e1000_regs.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Intel(R) Gigabit Ethernet Linux driver
* Copyright(c) 2007-2014 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h
index 1c6b8d9176a8..8dbc399b345e 100644
--- a/drivers/net/ethernet/intel/igb/igb.h
+++ b/drivers/net/ethernet/intel/igb/igb.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Intel(R) Gigabit Ethernet Linux driver
* Copyright(c) 2007-2014 Intel Corporation.
*
@@ -109,6 +110,7 @@ struct vf_data_storage {
u16 pf_qos;
u16 tx_rate;
bool spoofchk_enabled;
+ bool trusted;
};
/* Number of unicast MAC filters reserved for the PF in the RAR registers */
diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c
index 606e6761758f..e77ba0d5866d 100644
--- a/drivers/net/ethernet/intel/igb/igb_ethtool.c
+++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel(R) Gigabit Ethernet Linux driver
* Copyright(c) 2007-2014 Intel Corporation.
*
diff --git a/drivers/net/ethernet/intel/igb/igb_hwmon.c b/drivers/net/ethernet/intel/igb/igb_hwmon.c
index 44b6a68f1af7..bebe43b3a836 100644
--- a/drivers/net/ethernet/intel/igb/igb_hwmon.c
+++ b/drivers/net/ethernet/intel/igb/igb_hwmon.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel(R) Gigabit Ethernet Linux driver
* Copyright(c) 2007-2014 Intel Corporation.
*
@@ -147,7 +148,7 @@ static int igb_add_hwmon_attr(struct igb_adapter *adapter,
&adapter->hw.mac.thermal_sensor_data.sensor[offset];
igb_attr->hw = &adapter->hw;
igb_attr->dev_attr.store = NULL;
- igb_attr->dev_attr.attr.mode = S_IRUGO;
+ igb_attr->dev_attr.attr.mode = 0444;
igb_attr->dev_attr.attr.name = igb_attr->name;
sysfs_attr_init(&igb_attr->dev_attr.attr);
diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
index b88fae785369..cce7ada89255 100644
--- a/drivers/net/ethernet/intel/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* Intel(R) Gigabit Ethernet Linux driver
* Copyright(c) 2007-2014 Intel Corporation.
*
@@ -190,6 +191,8 @@ static int igb_ndo_set_vf_vlan(struct net_device *netdev,
static int igb_ndo_set_vf_bw(struct net_device *, int, int, int);
static int igb_ndo_set_vf_spoofchk(struct net_device *netdev, int vf,
bool setting);
+static int igb_ndo_set_vf_trust(struct net_device *netdev, int vf,
+ bool setting);
static int igb_ndo_get_vf_config(struct net_device *netdev, int vf,
struct ifla_vf_info *ivi);
static void igb_check_vf_rate_limit(struct igb_adapter *);
@@ -774,8 +777,7 @@ u32 igb_rd32(struct e1000_hw *hw, u32 reg)
if (!(~value) && (!reg || !(~readl(hw_addr)))) {
struct net_device *netdev = igb->netdev;
hw->hw_addr = NULL;
- netif_device_detach(netdev);
- netdev_err(netdev, "PCIe link lost, device now detached\n");
+ netdev_err(netdev, "PCIe link lost\n");
}
return value;
@@ -1698,7 +1700,22 @@ static void igb_configure_cbs(struct igb_adapter *adapter, int queue,
WARN_ON(hw->mac.type != e1000_i210);
WARN_ON(queue < 0 || queue > 1);
- if (enable) {
+ if (enable || queue == 0) {
+ /* i210 does not allow the queue 0 to be in the Strict
+ * Priority mode while the Qav mode is enabled, so,
+ * instead of disabling strict priority mode, we give
+ * queue 0 the maximum of credits possible.
+ *
+ * See section 8.12.19 of the i210 datasheet, "Note:
+ * Queue0 QueueMode must be set to 1b when
+ * TransmitMode is set to Qav."
+ */
+ if (queue == 0 && !enable) {
+ /* max "linkspeed" idleslope in kbps */
+ idleslope = 1000000;
+ hicredit = ETH_FRAME_LEN;
+ }
+
set_tx_desc_fetch_prio(hw, queue, TX_QUEUE_PRIO_HIGH);
set_queue_mode(hw, queue, QUEUE_MODE_STREAM_RESERVATION);
@@ -2527,6 +2544,7 @@ static const struct net_device_ops igb_netdev_ops = {
.ndo_set_vf_vlan = igb_ndo_set_vf_vlan,
.ndo_set_vf_rate = igb_ndo_set_vf_bw,
.ndo_set_vf_spoofchk = igb_ndo_set_vf_spoofchk,
+ .ndo_set_vf_trust = igb_ndo_set_vf_trust,
.ndo_get_vf_config = igb_ndo_get_vf_config,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = igb_netpoll,
@@ -5747,7 +5765,7 @@ netdev_tx_t igb_xmit_frame_ring(struct sk_buff *skb,
if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
struct igb_adapter *adapter = netdev_priv(tx_ring->netdev);
- if (adapter->tstamp_config.tx_type & HWTSTAMP_TX_ON &&
+ if (adapter->tstamp_config.tx_type == HWTSTAMP_TX_ON &&
!test_and_set_bit_lock(__IGB_PTP_TX_IN_PROGRESS,
&adapter->state)) {
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
@@ -6383,6 +6401,9 @@ static int igb_vf_configure(struct igb_adapter *adapter, int vf)
/* By default spoof check is enabled for all VFs */
adapter->vf_data[vf].spoofchk_enabled = true;
+ /* By default VFs are not trusted */
+ adapter->vf_data[vf].trusted = false;
+
return 0;
}
@@ -6940,13 +6961,13 @@ static int igb_set_vf_mac_filter(struct igb_adapter *adapter, const int vf,
}
break;
case E1000_VF_MAC_FILTER_ADD:
- if (vf_data->flags & IGB_VF_FLAG_PF_SET_MAC) {
+ if ((vf_data->flags & IGB_VF_FLAG_PF_SET_MAC) &&
+ !vf_data->trusted) {
dev_warn(&pdev->dev,
"VF %d requested MAC filter but is administratively denied\n",
vf);
return -EINVAL;
}
-
if (!is_valid_ether_addr(addr)) {
dev_warn(&pdev->dev,
"VF %d attempted to set invalid MAC filter\n",
@@ -6998,7 +7019,8 @@ static int igb_set_vf_mac_addr(struct igb_adapter *adapter, u32 *msg, int vf)
int ret = 0;
if (!info) {
- if (vf_data->flags & IGB_VF_FLAG_PF_SET_MAC) {
+ if ((vf_data->flags & IGB_VF_FLAG_PF_SET_MAC) &&
+ !vf_data->trusted) {
dev_warn(&pdev->dev,
"VF %d attempted to override administratively set MAC address\nReload the VF driver to resume operations\n",
vf);
@@ -8934,6 +8956,22 @@ static int igb_ndo_set_vf_spoofchk(struct net_device *netdev, int vf,
return 0;
}
+static int igb_ndo_set_vf_trust(struct net_device *netdev, int vf, bool setting)
+{
+ struct igb_adapter *adapter = netdev_priv(netdev);
+
+ if (vf >= adapter->vfs_allocated_count)
+ return -EINVAL;
+ if (adapter->vf_data[vf].trusted == setting)
+ return 0;
+
+ adapter->vf_data[vf].trusted = setting;
+
+ dev_info(&adapter->pdev->dev, "VF %u is %strusted\n",
+ vf, setting ? "" : "not ");
+ return 0;
+}
+
static int igb_ndo_get_vf_config(struct net_device *netdev,
int vf, struct ifla_vf_info *ivi)
{
@@ -8947,6 +8985,7 @@ static int igb_ndo_get_vf_config(struct net_device *netdev,
ivi->vlan = adapter->vf_data[vf].pf_vlan;
ivi->qos = adapter->vf_data[vf].pf_qos;
ivi->spoofchk = adapter->vf_data[vf].spoofchk_enabled;
+ ivi->trusted = adapter->vf_data[vf].trusted;
return 0;
}
diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c
index 0746b19ec6d3..7454b9895a65 100644
--- a/drivers/net/ethernet/intel/igb/igb_ptp.c
+++ b/drivers/net/ethernet/intel/igb/igb_ptp.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/* PTP Hardware Clock (PHC) driver for the Intel 82576 and 82580
*
* Copyright (C) 2011 Richard Cochran <richardcochran@gmail.com>
diff --git a/drivers/net/ethernet/intel/igbvf/Makefile b/drivers/net/ethernet/intel/igbvf/Makefile
index 044b0ad5fcb9..efe29dae384a 100644
--- a/drivers/net/ethernet/intel/igbvf/Makefile
+++ b/drivers/net/ethernet/intel/igbvf/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
################################################################################
#
# Intel(R) 82576 Virtual Function Linux driver
diff --git a/drivers/net/ethernet/intel/igbvf/defines.h b/drivers/net/ethernet/intel/igbvf/defines.h
index f1789d192e24..04bcfec0641b 100644
--- a/drivers/net/ethernet/intel/igbvf/defines.h
+++ b/drivers/net/ethernet/intel/igbvf/defines.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
Intel(R) 82576 Virtual Function Linux driver
diff --git a/drivers/net/ethernet/intel/igbvf/ethtool.c b/drivers/net/ethernet/intel/igbvf/ethtool.c
index a127688e83e6..ca39e3cccaeb 100644
--- a/drivers/net/ethernet/intel/igbvf/ethtool.c
+++ b/drivers/net/ethernet/intel/igbvf/ethtool.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
Intel(R) 82576 Virtual Function Linux driver
diff --git a/drivers/net/ethernet/intel/igbvf/igbvf.h b/drivers/net/ethernet/intel/igbvf/igbvf.h
index bf69f01f8467..f5bf248e22eb 100644
--- a/drivers/net/ethernet/intel/igbvf/igbvf.h
+++ b/drivers/net/ethernet/intel/igbvf/igbvf.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
Intel(R) 82576 Virtual Function Linux driver
diff --git a/drivers/net/ethernet/intel/igbvf/mbx.c b/drivers/net/ethernet/intel/igbvf/mbx.c
index c9a441632e9f..9195884096f8 100644
--- a/drivers/net/ethernet/intel/igbvf/mbx.c
+++ b/drivers/net/ethernet/intel/igbvf/mbx.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
Intel(R) 82576 Virtual Function Linux driver
diff --git a/drivers/net/ethernet/intel/igbvf/mbx.h b/drivers/net/ethernet/intel/igbvf/mbx.h
index 30d58c4a444e..479b062fe9ee 100644
--- a/drivers/net/ethernet/intel/igbvf/mbx.h
+++ b/drivers/net/ethernet/intel/igbvf/mbx.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
Intel(R) 82576 Virtual Function Linux driver
diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c
index 4214c1519a87..e2b7502f1953 100644
--- a/drivers/net/ethernet/intel/igbvf/netdev.c
+++ b/drivers/net/ethernet/intel/igbvf/netdev.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
Intel(R) 82576 Virtual Function Linux driver
diff --git a/drivers/net/ethernet/intel/igbvf/regs.h b/drivers/net/ethernet/intel/igbvf/regs.h
index 86a7c120b574..614e52409f11 100644
--- a/drivers/net/ethernet/intel/igbvf/regs.h
+++ b/drivers/net/ethernet/intel/igbvf/regs.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
Intel(R) 82576 Virtual Function Linux driver
diff --git a/drivers/net/ethernet/intel/igbvf/vf.c b/drivers/net/ethernet/intel/igbvf/vf.c
index 9577ccf4b26a..bfe8d8297b2e 100644
--- a/drivers/net/ethernet/intel/igbvf/vf.c
+++ b/drivers/net/ethernet/intel/igbvf/vf.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
Intel(R) 82576 Virtual Function Linux driver
diff --git a/drivers/net/ethernet/intel/igbvf/vf.h b/drivers/net/ethernet/intel/igbvf/vf.h
index d213eefb6169..193b50026246 100644
--- a/drivers/net/ethernet/intel/igbvf/vf.h
+++ b/drivers/net/ethernet/intel/igbvf/vf.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
Intel(R) 82576 Virtual Function Linux driver
diff --git a/drivers/net/ethernet/intel/ixgb/Makefile b/drivers/net/ethernet/intel/ixgb/Makefile
index 0b20c5e62ffe..1b42dd554dd2 100644
--- a/drivers/net/ethernet/intel/ixgb/Makefile
+++ b/drivers/net/ethernet/intel/ixgb/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
################################################################################
#
# Intel PRO/10GbE Linux driver
diff --git a/drivers/net/ethernet/intel/ixgb/ixgb.h b/drivers/net/ethernet/intel/ixgb/ixgb.h
index 1180cd59b570..92022841755f 100644
--- a/drivers/net/ethernet/intel/ixgb/ixgb.h
+++ b/drivers/net/ethernet/intel/ixgb/ixgb.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
Intel PRO/10GbE Linux driver
diff --git a/drivers/net/ethernet/intel/ixgb/ixgb_ee.h b/drivers/net/ethernet/intel/ixgb/ixgb_ee.h
index 5680f64314b8..475297a810fe 100644
--- a/drivers/net/ethernet/intel/ixgb/ixgb_ee.h
+++ b/drivers/net/ethernet/intel/ixgb/ixgb_ee.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
Intel PRO/10GbE Linux driver
diff --git a/drivers/net/ethernet/intel/ixgb/ixgb_hw.h b/drivers/net/ethernet/intel/ixgb/ixgb_hw.h
index 0bd5d72e1af5..19f36d87ef61 100644
--- a/drivers/net/ethernet/intel/ixgb/ixgb_hw.h
+++ b/drivers/net/ethernet/intel/ixgb/ixgb_hw.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
Intel PRO/10GbE Linux driver
diff --git a/drivers/net/ethernet/intel/ixgb/ixgb_ids.h b/drivers/net/ethernet/intel/ixgb/ixgb_ids.h
index 32c1b302d791..24e849902d60 100644
--- a/drivers/net/ethernet/intel/ixgb/ixgb_ids.h
+++ b/drivers/net/ethernet/intel/ixgb/ixgb_ids.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
Intel PRO/10GbE Linux driver
diff --git a/drivers/net/ethernet/intel/ixgb/ixgb_osdep.h b/drivers/net/ethernet/intel/ixgb/ixgb_osdep.h
index 8fc905192231..b1710379192e 100644
--- a/drivers/net/ethernet/intel/ixgb/ixgb_osdep.h
+++ b/drivers/net/ethernet/intel/ixgb/ixgb_osdep.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
Intel PRO/10GbE Linux driver
diff --git a/drivers/net/ethernet/intel/ixgbe/Makefile b/drivers/net/ethernet/intel/ixgbe/Makefile
index 8319465eb38d..4cd96c88cb5d 100644
--- a/drivers/net/ethernet/intel/ixgbe/Makefile
+++ b/drivers/net/ethernet/intel/ixgbe/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
################################################################################
#
# Intel 10 Gigabit PCI Express Linux driver
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h
index c1e3a0039ea5..4f08c712e58e 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c
index a0ebd9ecf243..cb0fe5fedb33 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
index 4dfc81dbee4b..66a74f4651e8 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c
index 61188f343955..633be93f3dbb 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h
index 4d4c02366cb3..2b311382167a 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
@@ -153,6 +154,7 @@ s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw,
void ixgbe_set_soft_rate_select_speed(struct ixgbe_hw *hw,
ixgbe_link_speed speed);
+#define IXGBE_FAILED_READ_RETRIES 5
#define IXGBE_FAILED_READ_REG 0xffffffffU
#define IXGBE_FAILED_READ_CFG_DWORD 0xffffffffU
#define IXGBE_FAILED_READ_CFG_WORD 0xffffU
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h
index fc0a2dd52499..73b6362d4327 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.c
index f94c7e82a30b..085130626330 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.h
index 3164f5453b8f..7edce607f901 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h
index 90c370230e20..fa030f0abc18 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
index 221f15803480..c0e6ab42e0e1 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
@@ -97,6 +97,7 @@ static const struct ixgbe_stats ixgbe_gstrings_stats[] = {
{"tx_heartbeat_errors", IXGBE_NETDEV_STAT(tx_heartbeat_errors)},
{"tx_timeout_count", IXGBE_STAT(tx_timeout_count)},
{"tx_restart_queue", IXGBE_STAT(restart_queue)},
+ {"rx_length_errors", IXGBE_STAT(stats.rlec)},
{"rx_long_length_errors", IXGBE_STAT(stats.roc)},
{"rx_short_length_errors", IXGBE_STAT(stats.ruc)},
{"tx_flow_control_xon", IXGBE_STAT(stats.lxontxc)},
@@ -3059,6 +3060,8 @@ static int ixgbe_set_rxfh(struct net_device *netdev, const u32 *indir,
for (i = 0; i < reta_entries; i++)
adapter->rss_indir_tbl[i] = indir[i];
+
+ ixgbe_store_reta(adapter);
}
/* Fill out the rss hash key */
@@ -3067,8 +3070,6 @@ static int ixgbe_set_rxfh(struct net_device *netdev, const u32 *indir,
ixgbe_store_key(adapter);
}
- ixgbe_store_reta(adapter);
-
return 0;
}
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h
index 38385876effb..cf1919901514 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c
index 93eacddb6704..68af127987bc 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c
@@ -423,15 +423,21 @@ static int ixgbe_ipsec_parse_proto_keys(struct xfrm_state *xs,
const char aes_gcm_name[] = "rfc4106(gcm(aes))";
int key_len;
- if (xs->aead) {
- key_data = &xs->aead->alg_key[0];
- key_len = xs->aead->alg_key_len;
- alg_name = xs->aead->alg_name;
- } else {
+ if (!xs->aead) {
netdev_err(dev, "Unsupported IPsec algorithm\n");
return -EINVAL;
}
+ if (xs->aead->alg_icv_len != IXGBE_IPSEC_AUTH_BITS) {
+ netdev_err(dev, "IPsec offload requires %d bit authentication\n",
+ IXGBE_IPSEC_AUTH_BITS);
+ return -EINVAL;
+ }
+
+ key_data = &xs->aead->alg_key[0];
+ key_len = xs->aead->alg_key_len;
+ alg_name = xs->aead->alg_name;
+
if (strcmp(alg_name, aes_gcm_name)) {
netdev_err(dev, "Unsupported IPsec algorithm - please use %s\n",
aes_gcm_name);
@@ -718,23 +724,10 @@ static bool ixgbe_ipsec_offload_ok(struct sk_buff *skb, struct xfrm_state *xs)
return true;
}
-/**
- * ixgbe_ipsec_free - called by xfrm garbage collections
- * @xs: pointer to transformer state struct
- *
- * We don't have any garbage to collect, so we shouldn't bother
- * implementing this function, but the XFRM code doesn't check for
- * existence before calling the API callback.
- **/
-static void ixgbe_ipsec_free(struct xfrm_state *xs)
-{
-}
-
static const struct xfrmdev_ops ixgbe_xfrmdev_ops = {
.xdo_dev_state_add = ixgbe_ipsec_add_sa,
.xdo_dev_state_delete = ixgbe_ipsec_del_sa,
.xdo_dev_offload_ok = ixgbe_ipsec_offload_ok,
- .xdo_dev_state_free = ixgbe_ipsec_free,
};
/**
@@ -781,13 +774,40 @@ int ixgbe_ipsec_tx(struct ixgbe_ring *tx_ring,
first->tx_flags |= IXGBE_TX_FLAGS_IPSEC | IXGBE_TX_FLAGS_CC;
- itd->flags = 0;
if (xs->id.proto == IPPROTO_ESP) {
+
itd->flags |= IXGBE_ADVTXD_TUCMD_IPSEC_TYPE_ESP |
IXGBE_ADVTXD_TUCMD_L4T_TCP;
if (first->protocol == htons(ETH_P_IP))
itd->flags |= IXGBE_ADVTXD_TUCMD_IPV4;
- itd->trailer_len = xs->props.trailer_len;
+
+ /* The actual trailer length is authlen (16 bytes) plus
+ * 2 bytes for the proto and the padlen values, plus
+ * padlen bytes of padding. This ends up not the same
+ * as the static value found in xs->props.trailer_len (21).
+ *
+ * ... but if we're doing GSO, don't bother as the stack
+ * doesn't add a trailer for those.
+ */
+ if (!skb_is_gso(first->skb)) {
+ /* The "correct" way to get the auth length would be
+ * to use
+ * authlen = crypto_aead_authsize(xs->data);
+ * but since we know we only have one size to worry
+ * about * we can let the compiler use the constant
+ * and save us a few CPU cycles.
+ */
+ const int authlen = IXGBE_IPSEC_AUTH_BITS / 8;
+ struct sk_buff *skb = first->skb;
+ u8 padlen;
+ int ret;
+
+ ret = skb_copy_bits(skb, skb->len - (authlen + 2),
+ &padlen, 1);
+ if (unlikely(ret))
+ return 0;
+ itd->trailer_len = authlen + 2 + padlen;
+ }
}
if (tsa->encrypt)
itd->flags |= IXGBE_ADVTXD_TUCMD_IPSEC_ENCRYPT_EN;
@@ -909,8 +929,13 @@ void ixgbe_init_ipsec_offload(struct ixgbe_adapter *adapter)
ixgbe_ipsec_clear_hw_tables(adapter);
adapter->netdev->xfrmdev_ops = &ixgbe_xfrmdev_ops;
- adapter->netdev->features |= NETIF_F_HW_ESP;
- adapter->netdev->hw_enc_features |= NETIF_F_HW_ESP;
+
+#define IXGBE_ESP_FEATURES (NETIF_F_HW_ESP | \
+ NETIF_F_HW_ESP_TX_CSUM | \
+ NETIF_F_GSO_ESP)
+
+ adapter->netdev->features |= IXGBE_ESP_FEATURES;
+ adapter->netdev->hw_enc_features |= IXGBE_ESP_FEATURES;
return;
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.h
index da3ce7849e85..4f099f516645 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
@@ -32,6 +33,7 @@
#define IXGBE_IPSEC_MAX_RX_IP_COUNT 128
#define IXGBE_IPSEC_BASE_RX_INDEX 0
#define IXGBE_IPSEC_BASE_TX_INDEX IXGBE_IPSEC_MAX_SA_COUNT
+#define IXGBE_IPSEC_AUTH_BITS 128
#define IXGBE_RXTXIDX_IPS_EN 0x00000001
#define IXGBE_RXIDX_TBL_SHIFT 1
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c
index 4242f0213e46..ed4cbe94c355 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c
@@ -58,7 +58,6 @@ static bool ixgbe_cache_ring_dcb_sriov(struct ixgbe_adapter *adapter)
return false;
/* start at VMDq register offset for SR-IOV enabled setups */
- pool = 0;
reg_idx = vmdq->offset * __ALIGN_MASK(1, ~vmdq->mask);
for (i = 0, pool = 0; i < adapter->num_rx_queues; i++, reg_idx++) {
/* If we are greater than indices move to next pool */
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index 9fc063af233c..afadba99f7b8 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -353,23 +353,32 @@ static void ixgbe_remove_adapter(struct ixgbe_hw *hw)
ixgbe_service_event_schedule(adapter);
}
-static void ixgbe_check_remove(struct ixgbe_hw *hw, u32 reg)
+static u32 ixgbe_check_remove(struct ixgbe_hw *hw, u32 reg)
{
+ u8 __iomem *reg_addr;
u32 value;
+ int i;
+
+ reg_addr = READ_ONCE(hw->hw_addr);
+ if (ixgbe_removed(reg_addr))
+ return IXGBE_FAILED_READ_REG;
- /* The following check not only optimizes a bit by not
- * performing a read on the status register when the
- * register just read was a status register read that
- * returned IXGBE_FAILED_READ_REG. It also blocks any
- * potential recursion.
+ /* Register read of 0xFFFFFFF can indicate the adapter has been removed,
+ * so perform several status register reads to determine if the adapter
+ * has been removed.
*/
- if (reg == IXGBE_STATUS) {
- ixgbe_remove_adapter(hw);
- return;
+ for (i = 0; i < IXGBE_FAILED_READ_RETRIES; i++) {
+ value = readl(reg_addr + IXGBE_STATUS);
+ if (value != IXGBE_FAILED_READ_REG)
+ break;
+ mdelay(3);
}
- value = ixgbe_read_reg(hw, IXGBE_STATUS);
+
if (value == IXGBE_FAILED_READ_REG)
ixgbe_remove_adapter(hw);
+ else
+ value = readl(reg_addr + reg);
+ return value;
}
/**
@@ -415,7 +424,7 @@ u32 ixgbe_read_reg(struct ixgbe_hw *hw, u32 reg)
writes_completed:
value = readl(reg_addr + reg);
if (unlikely(value == IXGBE_FAILED_READ_REG))
- ixgbe_check_remove(hw, reg);
+ value = ixgbe_check_remove(hw, reg);
return value;
}
@@ -1620,7 +1629,8 @@ static bool ixgbe_alloc_mapped_page(struct ixgbe_ring *rx_ring,
bi->dma = dma;
bi->page = page;
bi->page_offset = ixgbe_rx_offset(rx_ring);
- bi->pagecnt_bias = 1;
+ page_ref_add(page, USHRT_MAX - 1);
+ bi->pagecnt_bias = USHRT_MAX;
rx_ring->rx_stats.alloc_rx_page++;
return true;
@@ -2030,8 +2040,8 @@ static bool ixgbe_can_reuse_rx_page(struct ixgbe_rx_buffer *rx_buffer)
* the pagecnt_bias and page count so that we fully restock the
* number of references the driver holds.
*/
- if (unlikely(!pagecnt_bias)) {
- page_ref_add(page, USHRT_MAX);
+ if (unlikely(pagecnt_bias == 1)) {
+ page_ref_add(page, USHRT_MAX - 1);
rx_buffer->pagecnt_bias = USHRT_MAX;
}
@@ -7711,7 +7721,8 @@ static void ixgbe_service_task(struct work_struct *work)
if (test_bit(__IXGBE_PTP_RUNNING, &adapter->state)) {
ixgbe_ptp_overflow_check(adapter);
- ixgbe_ptp_rx_hang(adapter);
+ if (adapter->flags & IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER)
+ ixgbe_ptp_rx_hang(adapter);
ixgbe_ptp_tx_hang(adapter);
}
@@ -7720,7 +7731,8 @@ static void ixgbe_service_task(struct work_struct *work)
static int ixgbe_tso(struct ixgbe_ring *tx_ring,
struct ixgbe_tx_buffer *first,
- u8 *hdr_len)
+ u8 *hdr_len,
+ struct ixgbe_ipsec_tx_data *itd)
{
u32 vlan_macip_lens, type_tucmd, mss_l4len_idx;
struct sk_buff *skb = first->skb;
@@ -7734,6 +7746,7 @@ static int ixgbe_tso(struct ixgbe_ring *tx_ring,
unsigned char *hdr;
} l4;
u32 paylen, l4_offset;
+ u32 fceof_saidx = 0;
int err;
if (skb->ip_summed != CHECKSUM_PARTIAL)
@@ -7759,13 +7772,15 @@ static int ixgbe_tso(struct ixgbe_ring *tx_ring,
if (ip.v4->version == 4) {
unsigned char *csum_start = skb_checksum_start(skb);
unsigned char *trans_start = ip.hdr + (ip.v4->ihl * 4);
+ int len = csum_start - trans_start;
/* IP header will have to cancel out any data that
- * is not a part of the outer IP header
+ * is not a part of the outer IP header, so set to
+ * a reverse csum if needed, else init check to 0.
*/
- ip.v4->check = csum_fold(csum_partial(trans_start,
- csum_start - trans_start,
- 0));
+ ip.v4->check = (skb_shinfo(skb)->gso_type & SKB_GSO_PARTIAL) ?
+ csum_fold(csum_partial(trans_start,
+ len, 0)) : 0;
type_tucmd |= IXGBE_ADVTXD_TUCMD_IPV4;
ip.v4->tot_len = 0;
@@ -7796,12 +7811,15 @@ static int ixgbe_tso(struct ixgbe_ring *tx_ring,
mss_l4len_idx = (*hdr_len - l4_offset) << IXGBE_ADVTXD_L4LEN_SHIFT;
mss_l4len_idx |= skb_shinfo(skb)->gso_size << IXGBE_ADVTXD_MSS_SHIFT;
+ fceof_saidx |= itd->sa_idx;
+ type_tucmd |= itd->flags | itd->trailer_len;
+
/* vlan_macip_lens: HEADLEN, MACLEN, VLAN tag */
vlan_macip_lens = l4.hdr - ip.hdr;
vlan_macip_lens |= (ip.hdr - skb->data) << IXGBE_ADVTXD_MACLEN_SHIFT;
vlan_macip_lens |= first->tx_flags & IXGBE_TX_FLAGS_VLAN_MASK;
- ixgbe_tx_ctxtdesc(tx_ring, vlan_macip_lens, 0, type_tucmd,
+ ixgbe_tx_ctxtdesc(tx_ring, vlan_macip_lens, fceof_saidx, type_tucmd,
mss_l4len_idx);
return 1;
@@ -7863,10 +7881,8 @@ no_csum:
vlan_macip_lens |= skb_network_offset(skb) << IXGBE_ADVTXD_MACLEN_SHIFT;
vlan_macip_lens |= first->tx_flags & IXGBE_TX_FLAGS_VLAN_MASK;
- if (first->tx_flags & IXGBE_TX_FLAGS_IPSEC) {
- fceof_saidx |= itd->sa_idx;
- type_tucmd |= itd->flags | itd->trailer_len;
- }
+ fceof_saidx |= itd->sa_idx;
+ type_tucmd |= itd->flags | itd->trailer_len;
ixgbe_tx_ctxtdesc(tx_ring, vlan_macip_lens, fceof_saidx, type_tucmd, 0);
}
@@ -8494,7 +8510,7 @@ netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *skb,
if (skb->sp && !ixgbe_ipsec_tx(tx_ring, first, &ipsec_tx))
goto out_drop;
#endif
- tso = ixgbe_tso(tx_ring, first, &hdr_len);
+ tso = ixgbe_tso(tx_ring, first, &hdr_len, &ipsec_tx);
if (tso < 0)
goto out_drop;
else if (!tso)
@@ -9903,15 +9919,15 @@ ixgbe_features_check(struct sk_buff *skb, struct net_device *dev,
/* We can only support IPV4 TSO in tunnels if we can mangle the
* inner IP ID field, so strip TSO if MANGLEID is not supported.
+ * IPsec offoad sets skb->encapsulation but still can handle
+ * the TSO, so it's the exception.
*/
- if (skb->encapsulation && !(features & NETIF_F_TSO_MANGLEID))
- features &= ~NETIF_F_TSO;
-
-#ifdef CONFIG_XFRM_OFFLOAD
- /* IPsec offload doesn't get along well with others *yet* */
- if (skb->sp)
- features &= ~(NETIF_F_TSO | NETIF_F_HW_CSUM);
+ if (skb->encapsulation && !(features & NETIF_F_TSO_MANGLEID)) {
+#ifdef CONFIG_XFRM
+ if (!skb->sp)
#endif
+ features &= ~NETIF_F_TSO;
+ }
return features;
}
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h
index 811cb4f64a5b..c4628b663590 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_model.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_model.h
index 538a1c5475b6..72446644f9fa 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_model.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_model.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel 10 Gigabit PCI Express Linux drive
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h
index b0cac961df3b..d6a7e77348c5 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c
index 27a70a52f3c9..008aa073a679 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c
@@ -831,7 +831,11 @@ static int ixgbe_vf_reset_msg(struct ixgbe_adapter *adapter, u32 vf)
IXGBE_WRITE_REG(hw, IXGBE_VFTE(reg_offset), reg);
/* force drop enable for all VF Rx queues */
- ixgbe_write_qde(adapter, vf, IXGBE_QDE_ENABLE);
+ reg = IXGBE_QDE_ENABLE;
+ if (adapter->vfinfo[vf].pf_vlan)
+ reg |= IXGBE_QDE_HIDE_VLAN;
+
+ ixgbe_write_qde(adapter, vf, reg);
/* enable receive for vf */
reg = IXGBE_READ_REG(hw, IXGBE_VFRE(reg_offset));
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h
index cf67b9b18ed7..e30d1f07e891 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sysfs.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sysfs.c
index ef6df3d6437e..24766e125592 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sysfs.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sysfs.c
@@ -146,7 +146,7 @@ static int ixgbe_add_hwmon_attr(struct ixgbe_adapter *adapter,
&adapter->hw.mac.thermal_sensor_data.sensor[offset];
ixgbe_attr->hw = &adapter->hw;
ixgbe_attr->dev_attr.store = NULL;
- ixgbe_attr->dev_attr.attr.mode = S_IRUGO;
+ ixgbe_attr->dev_attr.attr.mode = 0444;
ixgbe_attr->dev_attr.attr.name = ixgbe_attr->name;
sysfs_attr_init(&ixgbe_attr->dev_attr.attr);
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h
index ca45359686d3..2daa81e6e9b2 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.h
index e21cd48491d3..182d640e9f7a 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
*
* Intel 10 Gigabit PCI Express Linux driver
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c
index f470d0204771..3123267dfba9 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c
@@ -1847,9 +1847,9 @@ ixgbe_setup_mac_link_sfp_x550a(struct ixgbe_hw *hw, ixgbe_link_speed speed,
(IXGBE_CS4227_EDC_MODE_SR << 1));
if (setup_linear)
- reg_phy_ext = (IXGBE_CS4227_EDC_MODE_CX1 << 1) | 1;
+ reg_phy_ext |= (IXGBE_CS4227_EDC_MODE_CX1 << 1) | 1;
else
- reg_phy_ext = (IXGBE_CS4227_EDC_MODE_SR << 1) | 1;
+ reg_phy_ext |= (IXGBE_CS4227_EDC_MODE_SR << 1) | 1;
ret_val = hw->phy.ops.write_reg(hw, reg_slice,
IXGBE_MDIO_ZERO_DEV_TYPE, reg_phy_ext);
diff --git a/drivers/net/ethernet/intel/ixgbevf/Makefile b/drivers/net/ethernet/intel/ixgbevf/Makefile
index 4ce4c97ef5ad..bb47814cfa90 100644
--- a/drivers/net/ethernet/intel/ixgbevf/Makefile
+++ b/drivers/net/ethernet/intel/ixgbevf/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
################################################################################
#
# Intel 82599 Virtual Function driver
diff --git a/drivers/net/ethernet/intel/ixgbevf/defines.h b/drivers/net/ethernet/intel/ixgbevf/defines.h
index 8617cae2f801..71c828842b11 100644
--- a/drivers/net/ethernet/intel/ixgbevf/defines.h
+++ b/drivers/net/ethernet/intel/ixgbevf/defines.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
Intel 82599 Virtual Function driver
diff --git a/drivers/net/ethernet/intel/ixgbevf/ethtool.c b/drivers/net/ethernet/intel/ixgbevf/ethtool.c
index 4400e49090b4..8e7d6c6f5c92 100644
--- a/drivers/net/ethernet/intel/ixgbevf/ethtool.c
+++ b/drivers/net/ethernet/intel/ixgbevf/ethtool.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel 82599 Virtual Function driver
- Copyright(c) 1999 - 2015 Intel Corporation.
+ Copyright(c) 1999 - 2018 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -82,6 +82,7 @@ static struct ixgbe_stats ixgbevf_gstrings_stats[] = {
#define IXGBEVF_QUEUE_STATS_LEN ( \
(((struct ixgbevf_adapter *)netdev_priv(netdev))->num_tx_queues + \
+ ((struct ixgbevf_adapter *)netdev_priv(netdev))->num_xdp_queues + \
((struct ixgbevf_adapter *)netdev_priv(netdev))->num_rx_queues) * \
(sizeof(struct ixgbevf_stats) / sizeof(u64)))
#define IXGBEVF_GLOBAL_STATS_LEN ARRAY_SIZE(ixgbevf_gstrings_stats)
@@ -94,6 +95,13 @@ static const char ixgbe_gstrings_test[][ETH_GSTRING_LEN] = {
#define IXGBEVF_TEST_LEN (sizeof(ixgbe_gstrings_test) / ETH_GSTRING_LEN)
+static const char ixgbevf_priv_flags_strings[][ETH_GSTRING_LEN] = {
+#define IXGBEVF_PRIV_FLAGS_LEGACY_RX BIT(0)
+ "legacy-rx",
+};
+
+#define IXGBEVF_PRIV_FLAGS_STR_LEN ARRAY_SIZE(ixgbevf_priv_flags_strings)
+
static int ixgbevf_get_link_ksettings(struct net_device *netdev,
struct ethtool_link_ksettings *cmd)
{
@@ -241,6 +249,8 @@ static void ixgbevf_get_drvinfo(struct net_device *netdev,
sizeof(drvinfo->version));
strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
sizeof(drvinfo->bus_info));
+
+ drvinfo->n_priv_flags = IXGBEVF_PRIV_FLAGS_STR_LEN;
}
static void ixgbevf_get_ringparam(struct net_device *netdev,
@@ -260,7 +270,7 @@ static int ixgbevf_set_ringparam(struct net_device *netdev,
struct ixgbevf_adapter *adapter = netdev_priv(netdev);
struct ixgbevf_ring *tx_ring = NULL, *rx_ring = NULL;
u32 new_rx_count, new_tx_count;
- int i, err = 0;
+ int i, j, err = 0;
if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending))
return -EINVAL;
@@ -284,15 +294,19 @@ static int ixgbevf_set_ringparam(struct net_device *netdev,
if (!netif_running(adapter->netdev)) {
for (i = 0; i < adapter->num_tx_queues; i++)
adapter->tx_ring[i]->count = new_tx_count;
+ for (i = 0; i < adapter->num_xdp_queues; i++)
+ adapter->xdp_ring[i]->count = new_tx_count;
for (i = 0; i < adapter->num_rx_queues; i++)
adapter->rx_ring[i]->count = new_rx_count;
adapter->tx_ring_count = new_tx_count;
+ adapter->xdp_ring_count = new_tx_count;
adapter->rx_ring_count = new_rx_count;
goto clear_reset;
}
if (new_tx_count != adapter->tx_ring_count) {
- tx_ring = vmalloc(adapter->num_tx_queues * sizeof(*tx_ring));
+ tx_ring = vmalloc((adapter->num_tx_queues +
+ adapter->num_xdp_queues) * sizeof(*tx_ring));
if (!tx_ring) {
err = -ENOMEM;
goto clear_reset;
@@ -315,6 +329,24 @@ static int ixgbevf_set_ringparam(struct net_device *netdev,
goto clear_reset;
}
}
+
+ for (j = 0; j < adapter->num_xdp_queues; i++, j++) {
+ /* clone ring and setup updated count */
+ tx_ring[i] = *adapter->xdp_ring[j];
+ tx_ring[i].count = new_tx_count;
+ err = ixgbevf_setup_tx_resources(&tx_ring[i]);
+ if (err) {
+ while (i) {
+ i--;
+ ixgbevf_free_tx_resources(&tx_ring[i]);
+ }
+
+ vfree(tx_ring);
+ tx_ring = NULL;
+
+ goto clear_reset;
+ }
+ }
}
if (new_rx_count != adapter->rx_ring_count) {
@@ -327,8 +359,13 @@ static int ixgbevf_set_ringparam(struct net_device *netdev,
for (i = 0; i < adapter->num_rx_queues; i++) {
/* clone ring and setup updated count */
rx_ring[i] = *adapter->rx_ring[i];
+
+ /* Clear copied XDP RX-queue info */
+ memset(&rx_ring[i].xdp_rxq, 0,
+ sizeof(rx_ring[i].xdp_rxq));
+
rx_ring[i].count = new_rx_count;
- err = ixgbevf_setup_rx_resources(&rx_ring[i]);
+ err = ixgbevf_setup_rx_resources(adapter, &rx_ring[i]);
if (err) {
while (i) {
i--;
@@ -354,6 +391,12 @@ static int ixgbevf_set_ringparam(struct net_device *netdev,
}
adapter->tx_ring_count = new_tx_count;
+ for (j = 0; j < adapter->num_xdp_queues; i++, j++) {
+ ixgbevf_free_tx_resources(adapter->xdp_ring[j]);
+ *adapter->xdp_ring[j] = tx_ring[i];
+ }
+ adapter->xdp_ring_count = new_tx_count;
+
vfree(tx_ring);
tx_ring = NULL;
}
@@ -376,7 +419,8 @@ static int ixgbevf_set_ringparam(struct net_device *netdev,
clear_reset:
/* free Tx resources if Rx error is encountered */
if (tx_ring) {
- for (i = 0; i < adapter->num_tx_queues; i++)
+ for (i = 0;
+ i < adapter->num_tx_queues + adapter->num_xdp_queues; i++)
ixgbevf_free_tx_resources(&tx_ring[i]);
vfree(tx_ring);
}
@@ -392,6 +436,8 @@ static int ixgbevf_get_sset_count(struct net_device *netdev, int stringset)
return IXGBEVF_TEST_LEN;
case ETH_SS_STATS:
return IXGBEVF_STATS_LEN;
+ case ETH_SS_PRIV_FLAGS:
+ return IXGBEVF_PRIV_FLAGS_STR_LEN;
default:
return -EINVAL;
}
@@ -446,6 +492,23 @@ static void ixgbevf_get_ethtool_stats(struct net_device *netdev,
i += 2;
}
+ /* populate XDP queue data */
+ for (j = 0; j < adapter->num_xdp_queues; j++) {
+ ring = adapter->xdp_ring[j];
+ if (!ring) {
+ data[i++] = 0;
+ data[i++] = 0;
+ continue;
+ }
+
+ do {
+ start = u64_stats_fetch_begin_irq(&ring->syncp);
+ data[i] = ring->stats.packets;
+ data[i + 1] = ring->stats.bytes;
+ } while (u64_stats_fetch_retry_irq(&ring->syncp, start));
+ i += 2;
+ }
+
/* populate Rx queue data */
for (j = 0; j < adapter->num_rx_queues; j++) {
ring = adapter->rx_ring[j];
@@ -489,6 +552,12 @@ static void ixgbevf_get_strings(struct net_device *netdev, u32 stringset,
sprintf(p, "tx_queue_%u_bytes", i);
p += ETH_GSTRING_LEN;
}
+ for (i = 0; i < adapter->num_xdp_queues; i++) {
+ sprintf(p, "xdp_queue_%u_packets", i);
+ p += ETH_GSTRING_LEN;
+ sprintf(p, "xdp_queue_%u_bytes", i);
+ p += ETH_GSTRING_LEN;
+ }
for (i = 0; i < adapter->num_rx_queues; i++) {
sprintf(p, "rx_queue_%u_packets", i);
p += ETH_GSTRING_LEN;
@@ -496,6 +565,10 @@ static void ixgbevf_get_strings(struct net_device *netdev, u32 stringset,
p += ETH_GSTRING_LEN;
}
break;
+ case ETH_SS_PRIV_FLAGS:
+ memcpy(data, ixgbevf_priv_flags_strings,
+ IXGBEVF_PRIV_FLAGS_STR_LEN * ETH_GSTRING_LEN);
+ break;
}
}
@@ -888,6 +961,37 @@ static int ixgbevf_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
return err;
}
+static u32 ixgbevf_get_priv_flags(struct net_device *netdev)
+{
+ struct ixgbevf_adapter *adapter = netdev_priv(netdev);
+ u32 priv_flags = 0;
+
+ if (adapter->flags & IXGBEVF_FLAGS_LEGACY_RX)
+ priv_flags |= IXGBEVF_PRIV_FLAGS_LEGACY_RX;
+
+ return priv_flags;
+}
+
+static int ixgbevf_set_priv_flags(struct net_device *netdev, u32 priv_flags)
+{
+ struct ixgbevf_adapter *adapter = netdev_priv(netdev);
+ unsigned int flags = adapter->flags;
+
+ flags &= ~IXGBEVF_FLAGS_LEGACY_RX;
+ if (priv_flags & IXGBEVF_PRIV_FLAGS_LEGACY_RX)
+ flags |= IXGBEVF_FLAGS_LEGACY_RX;
+
+ if (flags != adapter->flags) {
+ adapter->flags = flags;
+
+ /* reset interface to repopulate queues */
+ if (netif_running(netdev))
+ ixgbevf_reinit_locked(adapter);
+ }
+
+ return 0;
+}
+
static const struct ethtool_ops ixgbevf_ethtool_ops = {
.get_drvinfo = ixgbevf_get_drvinfo,
.get_regs_len = ixgbevf_get_regs_len,
@@ -909,6 +1013,8 @@ static const struct ethtool_ops ixgbevf_ethtool_ops = {
.get_rxfh_key_size = ixgbevf_get_rxfh_key_size,
.get_rxfh = ixgbevf_get_rxfh,
.get_link_ksettings = ixgbevf_get_link_ksettings,
+ .get_priv_flags = ixgbevf_get_priv_flags,
+ .set_priv_flags = ixgbevf_set_priv_flags,
};
void ixgbevf_set_ethtool_ops(struct net_device *netdev)
diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h
index f6952425c87d..447ce1d5e0e3 100644
--- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h
+++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h
@@ -1,7 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
Intel 82599 Virtual Function driver
- Copyright(c) 1999 - 2015 Intel Corporation.
+ Copyright(c) 1999 - 2018 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -34,6 +35,7 @@
#include <linux/netdevice.h>
#include <linux/if_vlan.h>
#include <linux/u64_stats_sync.h>
+#include <net/xdp.h>
#include "vf.h"
@@ -50,7 +52,11 @@
struct ixgbevf_tx_buffer {
union ixgbe_adv_tx_desc *next_to_watch;
unsigned long time_stamp;
- struct sk_buff *skb;
+ union {
+ struct sk_buff *skb;
+ /* XDP uses address ptr on irq_clean */
+ void *data;
+ };
unsigned int bytecount;
unsigned short gso_segs;
__be16 protocol;
@@ -89,20 +95,25 @@ struct ixgbevf_rx_queue_stats {
};
enum ixgbevf_ring_state_t {
+ __IXGBEVF_RX_3K_BUFFER,
+ __IXGBEVF_RX_BUILD_SKB_ENABLED,
__IXGBEVF_TX_DETECT_HANG,
__IXGBEVF_HANG_CHECK_ARMED,
+ __IXGBEVF_TX_XDP_RING,
};
-#define check_for_tx_hang(ring) \
- test_bit(__IXGBEVF_TX_DETECT_HANG, &(ring)->state)
-#define set_check_for_tx_hang(ring) \
- set_bit(__IXGBEVF_TX_DETECT_HANG, &(ring)->state)
-#define clear_check_for_tx_hang(ring) \
- clear_bit(__IXGBEVF_TX_DETECT_HANG, &(ring)->state)
+#define ring_is_xdp(ring) \
+ test_bit(__IXGBEVF_TX_XDP_RING, &(ring)->state)
+#define set_ring_xdp(ring) \
+ set_bit(__IXGBEVF_TX_XDP_RING, &(ring)->state)
+#define clear_ring_xdp(ring) \
+ clear_bit(__IXGBEVF_TX_XDP_RING, &(ring)->state)
struct ixgbevf_ring {
struct ixgbevf_ring *next;
+ struct ixgbevf_q_vector *q_vector; /* backpointer to q_vector */
struct net_device *netdev;
+ struct bpf_prog *xdp_prog;
struct device *dev;
void *desc; /* descriptor ring memory */
dma_addr_t dma; /* phys. address of descriptor ring */
@@ -123,7 +134,7 @@ struct ixgbevf_ring {
struct ixgbevf_tx_queue_stats tx_stats;
struct ixgbevf_rx_queue_stats rx_stats;
};
-
+ struct xdp_rxq_info xdp_rxq;
u64 hw_csum_rx_error;
u8 __iomem *tail;
struct sk_buff *skb;
@@ -133,13 +144,14 @@ struct ixgbevf_ring {
*/
u16 reg_idx;
int queue_index; /* needed for multiqueue queue management */
-};
+} ____cacheline_internodealigned_in_smp;
/* How many Rx Buffers do we bundle into one write to the hardware ? */
#define IXGBEVF_RX_BUFFER_WRITE 16 /* Must be power of 2 */
#define MAX_RX_QUEUES IXGBE_VF_MAX_RX_QUEUES
#define MAX_TX_QUEUES IXGBE_VF_MAX_TX_QUEUES
+#define MAX_XDP_QUEUES IXGBE_VF_MAX_TX_QUEUES
#define IXGBEVF_MAX_RSS_QUEUES 2
#define IXGBEVF_82599_RETA_SIZE 128 /* 128 entries */
#define IXGBEVF_X550_VFRETA_SIZE 64 /* 64 entries */
@@ -156,12 +168,20 @@ struct ixgbevf_ring {
/* Supported Rx Buffer Sizes */
#define IXGBEVF_RXBUFFER_256 256 /* Used for packet split */
#define IXGBEVF_RXBUFFER_2048 2048
+#define IXGBEVF_RXBUFFER_3072 3072
#define IXGBEVF_RX_HDR_SIZE IXGBEVF_RXBUFFER_256
-#define IXGBEVF_RX_BUFSZ IXGBEVF_RXBUFFER_2048
#define MAXIMUM_ETHERNET_VLAN_SIZE (VLAN_ETH_FRAME_LEN + ETH_FCS_LEN)
+#define IXGBEVF_SKB_PAD (NET_SKB_PAD + NET_IP_ALIGN)
+#if (PAGE_SIZE < 8192)
+#define IXGBEVF_MAX_FRAME_BUILD_SKB \
+ (SKB_WITH_OVERHEAD(IXGBEVF_RXBUFFER_2048) - IXGBEVF_SKB_PAD)
+#else
+#define IXGBEVF_MAX_FRAME_BUILD_SKB IXGBEVF_RXBUFFER_2048
+#endif
+
#define IXGBE_TX_FLAGS_CSUM BIT(0)
#define IXGBE_TX_FLAGS_VLAN BIT(1)
#define IXGBE_TX_FLAGS_TSO BIT(2)
@@ -170,6 +190,50 @@ struct ixgbevf_ring {
#define IXGBE_TX_FLAGS_VLAN_PRIO_MASK 0x0000e000
#define IXGBE_TX_FLAGS_VLAN_SHIFT 16
+#define ring_uses_large_buffer(ring) \
+ test_bit(__IXGBEVF_RX_3K_BUFFER, &(ring)->state)
+#define set_ring_uses_large_buffer(ring) \
+ set_bit(__IXGBEVF_RX_3K_BUFFER, &(ring)->state)
+#define clear_ring_uses_large_buffer(ring) \
+ clear_bit(__IXGBEVF_RX_3K_BUFFER, &(ring)->state)
+
+#define ring_uses_build_skb(ring) \
+ test_bit(__IXGBEVF_RX_BUILD_SKB_ENABLED, &(ring)->state)
+#define set_ring_build_skb_enabled(ring) \
+ set_bit(__IXGBEVF_RX_BUILD_SKB_ENABLED, &(ring)->state)
+#define clear_ring_build_skb_enabled(ring) \
+ clear_bit(__IXGBEVF_RX_BUILD_SKB_ENABLED, &(ring)->state)
+
+static inline unsigned int ixgbevf_rx_bufsz(struct ixgbevf_ring *ring)
+{
+#if (PAGE_SIZE < 8192)
+ if (ring_uses_large_buffer(ring))
+ return IXGBEVF_RXBUFFER_3072;
+
+ if (ring_uses_build_skb(ring))
+ return IXGBEVF_MAX_FRAME_BUILD_SKB;
+#endif
+ return IXGBEVF_RXBUFFER_2048;
+}
+
+static inline unsigned int ixgbevf_rx_pg_order(struct ixgbevf_ring *ring)
+{
+#if (PAGE_SIZE < 8192)
+ if (ring_uses_large_buffer(ring))
+ return 1;
+#endif
+ return 0;
+}
+
+#define ixgbevf_rx_pg_size(_ring) (PAGE_SIZE << ixgbevf_rx_pg_order(_ring))
+
+#define check_for_tx_hang(ring) \
+ test_bit(__IXGBEVF_TX_DETECT_HANG, &(ring)->state)
+#define set_check_for_tx_hang(ring) \
+ set_bit(__IXGBEVF_TX_DETECT_HANG, &(ring)->state)
+#define clear_check_for_tx_hang(ring) \
+ clear_bit(__IXGBEVF_TX_DETECT_HANG, &(ring)->state)
+
struct ixgbevf_ring_container {
struct ixgbevf_ring *ring; /* pointer to linked list of rings */
unsigned int total_bytes; /* total bytes processed this int */
@@ -194,7 +258,11 @@ struct ixgbevf_q_vector {
u16 itr; /* Interrupt throttle rate written to EITR */
struct napi_struct napi;
struct ixgbevf_ring_container rx, tx;
+ struct rcu_head rcu; /* to avoid race with update stats on free */
char name[IFNAMSIZ + 9];
+
+ /* for dynamic allocation of rings associated with this q_vector */
+ struct ixgbevf_ring ring[0] ____cacheline_internodealigned_in_smp;
#ifdef CONFIG_NET_RX_BUSY_POLL
unsigned int state;
#define IXGBEVF_QV_STATE_IDLE 0
@@ -284,6 +352,10 @@ struct ixgbevf_adapter {
u32 eims_enable_mask;
u32 eims_other;
+ /* XDP */
+ int num_xdp_queues;
+ struct ixgbevf_ring *xdp_ring[MAX_XDP_QUEUES];
+
/* TX */
int num_tx_queues;
struct ixgbevf_ring *tx_ring[MAX_TX_QUEUES]; /* One per active queue */
@@ -304,6 +376,7 @@ struct ixgbevf_adapter {
/* OS defined structs */
struct net_device *netdev;
+ struct bpf_prog *xdp_prog;
struct pci_dev *pdev;
/* structs defined in ixgbe_vf.h */
@@ -317,6 +390,7 @@ struct ixgbevf_adapter {
unsigned long state;
u64 tx_busy;
unsigned int tx_ring_count;
+ unsigned int xdp_ring_count;
unsigned int rx_ring_count;
u8 __iomem *io_addr; /* Mainly for iounmap use */
@@ -331,6 +405,8 @@ struct ixgbevf_adapter {
u32 *rss_key;
u8 rss_indir_tbl[IXGBEVF_X550_VFRETA_SIZE];
+ u32 flags;
+#define IXGBEVF_FLAGS_LEGACY_RX BIT(1)
};
enum ixbgevf_state_t {
@@ -388,7 +464,8 @@ void ixgbevf_down(struct ixgbevf_adapter *adapter);
void ixgbevf_reinit_locked(struct ixgbevf_adapter *adapter);
void ixgbevf_reset(struct ixgbevf_adapter *adapter);
void ixgbevf_set_ethtool_ops(struct net_device *netdev);
-int ixgbevf_setup_rx_resources(struct ixgbevf_ring *);
+int ixgbevf_setup_rx_resources(struct ixgbevf_adapter *adapter,
+ struct ixgbevf_ring *rx_ring);
int ixgbevf_setup_tx_resources(struct ixgbevf_ring *);
void ixgbevf_free_rx_resources(struct ixgbevf_ring *);
void ixgbevf_free_tx_resources(struct ixgbevf_ring *);
diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
index 9b3d43d28106..e3d04f226d57 100644
--- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
+++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel 82599 Virtual Function driver
- Copyright(c) 1999 - 2015 Intel Corporation.
+ Copyright(c) 1999 - 2018 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -50,6 +50,9 @@
#include <linux/if_vlan.h>
#include <linux/prefetch.h>
#include <net/mpls.h>
+#include <linux/bpf.h>
+#include <linux/bpf_trace.h>
+#include <linux/atomic.h>
#include "ixgbevf.h"
@@ -130,6 +133,9 @@ static void ixgbevf_service_event_complete(struct ixgbevf_adapter *adapter)
static void ixgbevf_queue_reset_subtask(struct ixgbevf_adapter *adapter);
static void ixgbevf_set_itr(struct ixgbevf_q_vector *q_vector);
static void ixgbevf_free_all_rx_resources(struct ixgbevf_adapter *adapter);
+static bool ixgbevf_can_reuse_rx_page(struct ixgbevf_rx_buffer *rx_buffer);
+static void ixgbevf_reuse_rx_page(struct ixgbevf_ring *rx_ring,
+ struct ixgbevf_rx_buffer *old_buff);
static void ixgbevf_remove_adapter(struct ixgbe_hw *hw)
{
@@ -318,7 +324,10 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector,
total_packets += tx_buffer->gso_segs;
/* free the skb */
- napi_consume_skb(tx_buffer->skb, napi_budget);
+ if (ring_is_xdp(tx_ring))
+ page_frag_free(tx_buffer->data);
+ else
+ napi_consume_skb(tx_buffer->skb, napi_budget);
/* unmap skb header data */
dma_unmap_single(tx_ring->dev,
@@ -382,7 +391,7 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector,
eop_desc = tx_ring->tx_buffer_info[i].next_to_watch;
- pr_err("Detected Tx Unit Hang\n"
+ pr_err("Detected Tx Unit Hang%s\n"
" Tx Queue <%d>\n"
" TDH, TDT <%x>, <%x>\n"
" next_to_use <%x>\n"
@@ -392,6 +401,7 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector,
" eop_desc->wb.status <%x>\n"
" time_stamp <%lx>\n"
" jiffies <%lx>\n",
+ ring_is_xdp(tx_ring) ? " XDP" : "",
tx_ring->queue_index,
IXGBE_READ_REG(hw, IXGBE_VFTDH(tx_ring->reg_idx)),
IXGBE_READ_REG(hw, IXGBE_VFTDT(tx_ring->reg_idx)),
@@ -399,7 +409,9 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector,
eop_desc, (eop_desc ? eop_desc->wb.status : 0),
tx_ring->tx_buffer_info[i].time_stamp, jiffies);
- netif_stop_subqueue(tx_ring->netdev, tx_ring->queue_index);
+ if (!ring_is_xdp(tx_ring))
+ netif_stop_subqueue(tx_ring->netdev,
+ tx_ring->queue_index);
/* schedule immediate reset if we believe we hung */
ixgbevf_tx_timeout_reset(adapter);
@@ -407,6 +419,9 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector,
return true;
}
+ if (ring_is_xdp(tx_ring))
+ return !!budget;
+
#define TX_WAKE_THRESHOLD (DESC_NEEDED * 2)
if (unlikely(total_packets && netif_carrier_ok(tx_ring->netdev) &&
(ixgbevf_desc_unused(tx_ring) >= TX_WAKE_THRESHOLD))) {
@@ -527,6 +542,51 @@ static void ixgbevf_process_skb_fields(struct ixgbevf_ring *rx_ring,
skb->protocol = eth_type_trans(skb, rx_ring->netdev);
}
+static
+struct ixgbevf_rx_buffer *ixgbevf_get_rx_buffer(struct ixgbevf_ring *rx_ring,
+ const unsigned int size)
+{
+ struct ixgbevf_rx_buffer *rx_buffer;
+
+ rx_buffer = &rx_ring->rx_buffer_info[rx_ring->next_to_clean];
+ prefetchw(rx_buffer->page);
+
+ /* we are reusing so sync this buffer for CPU use */
+ dma_sync_single_range_for_cpu(rx_ring->dev,
+ rx_buffer->dma,
+ rx_buffer->page_offset,
+ size,
+ DMA_FROM_DEVICE);
+
+ rx_buffer->pagecnt_bias--;
+
+ return rx_buffer;
+}
+
+static void ixgbevf_put_rx_buffer(struct ixgbevf_ring *rx_ring,
+ struct ixgbevf_rx_buffer *rx_buffer,
+ struct sk_buff *skb)
+{
+ if (ixgbevf_can_reuse_rx_page(rx_buffer)) {
+ /* hand second half of page back to the ring */
+ ixgbevf_reuse_rx_page(rx_ring, rx_buffer);
+ } else {
+ if (IS_ERR(skb))
+ /* We are not reusing the buffer so unmap it and free
+ * any references we are holding to it
+ */
+ dma_unmap_page_attrs(rx_ring->dev, rx_buffer->dma,
+ ixgbevf_rx_pg_size(rx_ring),
+ DMA_FROM_DEVICE,
+ IXGBEVF_RX_DMA_ATTR);
+ __page_frag_cache_drain(rx_buffer->page,
+ rx_buffer->pagecnt_bias);
+ }
+
+ /* clear contents of rx_buffer */
+ rx_buffer->page = NULL;
+}
+
/**
* ixgbevf_is_non_eop - process handling of non-EOP buffers
* @rx_ring: Rx ring being processed
@@ -554,32 +614,38 @@ static bool ixgbevf_is_non_eop(struct ixgbevf_ring *rx_ring,
return true;
}
+static inline unsigned int ixgbevf_rx_offset(struct ixgbevf_ring *rx_ring)
+{
+ return ring_uses_build_skb(rx_ring) ? IXGBEVF_SKB_PAD : 0;
+}
+
static bool ixgbevf_alloc_mapped_page(struct ixgbevf_ring *rx_ring,
struct ixgbevf_rx_buffer *bi)
{
struct page *page = bi->page;
- dma_addr_t dma = bi->dma;
+ dma_addr_t dma;
/* since we are recycling buffers we should seldom need to alloc */
if (likely(page))
return true;
/* alloc new page for storage */
- page = dev_alloc_page();
+ page = dev_alloc_pages(ixgbevf_rx_pg_order(rx_ring));
if (unlikely(!page)) {
rx_ring->rx_stats.alloc_rx_page_failed++;
return false;
}
/* map page for use */
- dma = dma_map_page_attrs(rx_ring->dev, page, 0, PAGE_SIZE,
+ dma = dma_map_page_attrs(rx_ring->dev, page, 0,
+ ixgbevf_rx_pg_size(rx_ring),
DMA_FROM_DEVICE, IXGBEVF_RX_DMA_ATTR);
/* if mapping failed free memory back to system since
* there isn't much point in holding memory we can't use
*/
if (dma_mapping_error(rx_ring->dev, dma)) {
- __free_page(page);
+ __free_pages(page, ixgbevf_rx_pg_order(rx_ring));
rx_ring->rx_stats.alloc_rx_page_failed++;
return false;
@@ -587,7 +653,7 @@ static bool ixgbevf_alloc_mapped_page(struct ixgbevf_ring *rx_ring,
bi->dma = dma;
bi->page = page;
- bi->page_offset = 0;
+ bi->page_offset = ixgbevf_rx_offset(rx_ring);
bi->pagecnt_bias = 1;
rx_ring->rx_stats.alloc_rx_page++;
@@ -621,7 +687,7 @@ static void ixgbevf_alloc_rx_buffers(struct ixgbevf_ring *rx_ring,
/* sync the buffer for use by the device */
dma_sync_single_range_for_device(rx_ring->dev, bi->dma,
bi->page_offset,
- IXGBEVF_RX_BUFSZ,
+ ixgbevf_rx_bufsz(rx_ring),
DMA_FROM_DEVICE);
/* Refresh the desc even if pkt_addr didn't change
@@ -685,6 +751,10 @@ static bool ixgbevf_cleanup_headers(struct ixgbevf_ring *rx_ring,
union ixgbe_adv_rx_desc *rx_desc,
struct sk_buff *skb)
{
+ /* XDP packets use error pointer so abort at this point */
+ if (IS_ERR(skb))
+ return true;
+
/* verify that the packet does not have any known errors */
if (unlikely(ixgbevf_test_staterr(rx_desc,
IXGBE_RXDADV_ERR_FRAME_ERR_MASK))) {
@@ -734,11 +804,10 @@ static inline bool ixgbevf_page_is_reserved(struct page *page)
return (page_to_nid(page) != numa_mem_id()) || page_is_pfmemalloc(page);
}
-static bool ixgbevf_can_reuse_rx_page(struct ixgbevf_rx_buffer *rx_buffer,
- struct page *page,
- const unsigned int truesize)
+static bool ixgbevf_can_reuse_rx_page(struct ixgbevf_rx_buffer *rx_buffer)
{
- unsigned int pagecnt_bias = rx_buffer->pagecnt_bias--;
+ unsigned int pagecnt_bias = rx_buffer->pagecnt_bias;
+ struct page *page = rx_buffer->page;
/* avoid re-using remote pages */
if (unlikely(ixgbevf_page_is_reserved(page)))
@@ -746,17 +815,13 @@ static bool ixgbevf_can_reuse_rx_page(struct ixgbevf_rx_buffer *rx_buffer,
#if (PAGE_SIZE < 8192)
/* if we are only owner of page we can reuse it */
- if (unlikely(page_ref_count(page) != pagecnt_bias))
+ if (unlikely((page_ref_count(page) - pagecnt_bias) > 1))
return false;
-
- /* flip page offset to other buffer */
- rx_buffer->page_offset ^= IXGBEVF_RX_BUFSZ;
-
#else
- /* move offset up to the next cache line */
- rx_buffer->page_offset += truesize;
+#define IXGBEVF_LAST_OFFSET \
+ (SKB_WITH_OVERHEAD(PAGE_SIZE) - IXGBEVF_RXBUFFER_2048)
- if (rx_buffer->page_offset > (PAGE_SIZE - IXGBEVF_RX_BUFSZ))
+ if (rx_buffer->page_offset > IXGBEVF_LAST_OFFSET)
return false;
#endif
@@ -765,7 +830,7 @@ static bool ixgbevf_can_reuse_rx_page(struct ixgbevf_rx_buffer *rx_buffer,
* the pagecnt_bias and page count so that we fully restock the
* number of references the driver holds.
*/
- if (unlikely(pagecnt_bias == 1)) {
+ if (unlikely(!pagecnt_bias)) {
page_ref_add(page, USHRT_MAX);
rx_buffer->pagecnt_bias = USHRT_MAX;
}
@@ -777,136 +842,268 @@ static bool ixgbevf_can_reuse_rx_page(struct ixgbevf_rx_buffer *rx_buffer,
* ixgbevf_add_rx_frag - Add contents of Rx buffer to sk_buff
* @rx_ring: rx descriptor ring to transact packets on
* @rx_buffer: buffer containing page to add
- * @rx_desc: descriptor containing length of buffer written by hardware
* @skb: sk_buff to place the data into
+ * @size: size of buffer to be added
*
* This function will add the data contained in rx_buffer->page to the skb.
- * This is done either through a direct copy if the data in the buffer is
- * less than the skb header size, otherwise it will just attach the page as
- * a frag to the skb.
- *
- * The function will then update the page offset if necessary and return
- * true if the buffer can be reused by the adapter.
**/
-static bool ixgbevf_add_rx_frag(struct ixgbevf_ring *rx_ring,
+static void ixgbevf_add_rx_frag(struct ixgbevf_ring *rx_ring,
struct ixgbevf_rx_buffer *rx_buffer,
- u16 size,
- union ixgbe_adv_rx_desc *rx_desc,
- struct sk_buff *skb)
+ struct sk_buff *skb,
+ unsigned int size)
{
- struct page *page = rx_buffer->page;
- unsigned char *va = page_address(page) + rx_buffer->page_offset;
#if (PAGE_SIZE < 8192)
- unsigned int truesize = IXGBEVF_RX_BUFSZ;
+ unsigned int truesize = ixgbevf_rx_pg_size(rx_ring) / 2;
#else
- unsigned int truesize = ALIGN(size, L1_CACHE_BYTES);
+ unsigned int truesize = ring_uses_build_skb(rx_ring) ?
+ SKB_DATA_ALIGN(IXGBEVF_SKB_PAD + size) :
+ SKB_DATA_ALIGN(size);
#endif
- unsigned int pull_len;
-
- if (unlikely(skb_is_nonlinear(skb)))
- goto add_tail_frag;
+ skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, rx_buffer->page,
+ rx_buffer->page_offset, size, truesize);
+#if (PAGE_SIZE < 8192)
+ rx_buffer->page_offset ^= truesize;
+#else
+ rx_buffer->page_offset += truesize;
+#endif
+}
- if (likely(size <= IXGBEVF_RX_HDR_SIZE)) {
- memcpy(__skb_put(skb, size), va, ALIGN(size, sizeof(long)));
+static
+struct sk_buff *ixgbevf_construct_skb(struct ixgbevf_ring *rx_ring,
+ struct ixgbevf_rx_buffer *rx_buffer,
+ struct xdp_buff *xdp,
+ union ixgbe_adv_rx_desc *rx_desc)
+{
+ unsigned int size = xdp->data_end - xdp->data;
+#if (PAGE_SIZE < 8192)
+ unsigned int truesize = ixgbevf_rx_pg_size(rx_ring) / 2;
+#else
+ unsigned int truesize = SKB_DATA_ALIGN(xdp->data_end -
+ xdp->data_hard_start);
+#endif
+ unsigned int headlen;
+ struct sk_buff *skb;
- /* page is not reserved, we can reuse buffer as is */
- if (likely(!ixgbevf_page_is_reserved(page)))
- return true;
+ /* prefetch first cache line of first page */
+ prefetch(xdp->data);
+#if L1_CACHE_BYTES < 128
+ prefetch(xdp->data + L1_CACHE_BYTES);
+#endif
+ /* Note, we get here by enabling legacy-rx via:
+ *
+ * ethtool --set-priv-flags <dev> legacy-rx on
+ *
+ * In this mode, we currently get 0 extra XDP headroom as
+ * opposed to having legacy-rx off, where we process XDP
+ * packets going to stack via ixgbevf_build_skb().
+ *
+ * For ixgbevf_construct_skb() mode it means that the
+ * xdp->data_meta will always point to xdp->data, since
+ * the helper cannot expand the head. Should this ever
+ * changed in future for legacy-rx mode on, then lets also
+ * add xdp->data_meta handling here.
+ */
- /* this page cannot be reused so discard it */
- return false;
- }
+ /* allocate a skb to store the frags */
+ skb = napi_alloc_skb(&rx_ring->q_vector->napi, IXGBEVF_RX_HDR_SIZE);
+ if (unlikely(!skb))
+ return NULL;
- /* we need the header to contain the greater of either ETH_HLEN or
- * 60 bytes if the skb->len is less than 60 for skb_pad.
- */
- pull_len = eth_get_headlen(va, IXGBEVF_RX_HDR_SIZE);
+ /* Determine available headroom for copy */
+ headlen = size;
+ if (headlen > IXGBEVF_RX_HDR_SIZE)
+ headlen = eth_get_headlen(xdp->data, IXGBEVF_RX_HDR_SIZE);
/* align pull length to size of long to optimize memcpy performance */
- memcpy(__skb_put(skb, pull_len), va, ALIGN(pull_len, sizeof(long)));
+ memcpy(__skb_put(skb, headlen), xdp->data,
+ ALIGN(headlen, sizeof(long)));
/* update all of the pointers */
- va += pull_len;
- size -= pull_len;
+ size -= headlen;
+ if (size) {
+ skb_add_rx_frag(skb, 0, rx_buffer->page,
+ (xdp->data + headlen) -
+ page_address(rx_buffer->page),
+ size, truesize);
+#if (PAGE_SIZE < 8192)
+ rx_buffer->page_offset ^= truesize;
+#else
+ rx_buffer->page_offset += truesize;
+#endif
+ } else {
+ rx_buffer->pagecnt_bias++;
+ }
-add_tail_frag:
- skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page,
- (unsigned long)va & ~PAGE_MASK, size, truesize);
+ return skb;
+}
+
+static inline void ixgbevf_irq_enable_queues(struct ixgbevf_adapter *adapter,
+ u32 qmask)
+{
+ struct ixgbe_hw *hw = &adapter->hw;
- return ixgbevf_can_reuse_rx_page(rx_buffer, page, truesize);
+ IXGBE_WRITE_REG(hw, IXGBE_VTEIMS, qmask);
}
-static struct sk_buff *ixgbevf_fetch_rx_buffer(struct ixgbevf_ring *rx_ring,
- union ixgbe_adv_rx_desc *rx_desc,
- struct sk_buff *skb)
+static struct sk_buff *ixgbevf_build_skb(struct ixgbevf_ring *rx_ring,
+ struct ixgbevf_rx_buffer *rx_buffer,
+ struct xdp_buff *xdp,
+ union ixgbe_adv_rx_desc *rx_desc)
{
- struct ixgbevf_rx_buffer *rx_buffer;
- struct page *page;
- u16 size = le16_to_cpu(rx_desc->wb.upper.length);
+ unsigned int metasize = xdp->data - xdp->data_meta;
+#if (PAGE_SIZE < 8192)
+ unsigned int truesize = ixgbevf_rx_pg_size(rx_ring) / 2;
+#else
+ unsigned int truesize = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) +
+ SKB_DATA_ALIGN(xdp->data_end -
+ xdp->data_hard_start);
+#endif
+ struct sk_buff *skb;
- rx_buffer = &rx_ring->rx_buffer_info[rx_ring->next_to_clean];
- page = rx_buffer->page;
- prefetchw(page);
+ /* Prefetch first cache line of first page. If xdp->data_meta
+ * is unused, this points to xdp->data, otherwise, we likely
+ * have a consumer accessing first few bytes of meta data,
+ * and then actual data.
+ */
+ prefetch(xdp->data_meta);
+#if L1_CACHE_BYTES < 128
+ prefetch(xdp->data_meta + L1_CACHE_BYTES);
+#endif
- /* we are reusing so sync this buffer for CPU use */
- dma_sync_single_range_for_cpu(rx_ring->dev,
- rx_buffer->dma,
- rx_buffer->page_offset,
- size,
- DMA_FROM_DEVICE);
+ /* build an skb around the page buffer */
+ skb = build_skb(xdp->data_hard_start, truesize);
+ if (unlikely(!skb))
+ return NULL;
- if (likely(!skb)) {
- void *page_addr = page_address(page) +
- rx_buffer->page_offset;
+ /* update pointers within the skb to store the data */
+ skb_reserve(skb, xdp->data - xdp->data_hard_start);
+ __skb_put(skb, xdp->data_end - xdp->data);
+ if (metasize)
+ skb_metadata_set(skb, metasize);
- /* prefetch first cache line of first page */
- prefetch(page_addr);
-#if L1_CACHE_BYTES < 128
- prefetch(page_addr + L1_CACHE_BYTES);
+ /* update buffer offset */
+#if (PAGE_SIZE < 8192)
+ rx_buffer->page_offset ^= truesize;
+#else
+ rx_buffer->page_offset += truesize;
#endif
- /* allocate a skb to store the frags */
- skb = netdev_alloc_skb_ip_align(rx_ring->netdev,
- IXGBEVF_RX_HDR_SIZE);
- if (unlikely(!skb)) {
- rx_ring->rx_stats.alloc_rx_buff_failed++;
- return NULL;
- }
+ return skb;
+}
- /* we will be copying header into skb->data in
- * pskb_may_pull so it is in our interest to prefetch
- * it now to avoid a possible cache miss
- */
- prefetchw(skb->data);
- }
+#define IXGBEVF_XDP_PASS 0
+#define IXGBEVF_XDP_CONSUMED 1
+#define IXGBEVF_XDP_TX 2
- /* pull page into skb */
- if (ixgbevf_add_rx_frag(rx_ring, rx_buffer, size, rx_desc, skb)) {
- /* hand second half of page back to the ring */
- ixgbevf_reuse_rx_page(rx_ring, rx_buffer);
- } else {
- /* We are not reusing the buffer so unmap it and free
- * any references we are holding to it
- */
- dma_unmap_page_attrs(rx_ring->dev, rx_buffer->dma,
- PAGE_SIZE, DMA_FROM_DEVICE,
- IXGBEVF_RX_DMA_ATTR);
- __page_frag_cache_drain(page, rx_buffer->pagecnt_bias);
- }
+static int ixgbevf_xmit_xdp_ring(struct ixgbevf_ring *ring,
+ struct xdp_buff *xdp)
+{
+ struct ixgbevf_tx_buffer *tx_buffer;
+ union ixgbe_adv_tx_desc *tx_desc;
+ u32 len, cmd_type;
+ dma_addr_t dma;
+ u16 i;
- /* clear contents of buffer_info */
- rx_buffer->dma = 0;
- rx_buffer->page = NULL;
+ len = xdp->data_end - xdp->data;
- return skb;
+ if (unlikely(!ixgbevf_desc_unused(ring)))
+ return IXGBEVF_XDP_CONSUMED;
+
+ dma = dma_map_single(ring->dev, xdp->data, len, DMA_TO_DEVICE);
+ if (dma_mapping_error(ring->dev, dma))
+ return IXGBEVF_XDP_CONSUMED;
+
+ /* record the location of the first descriptor for this packet */
+ tx_buffer = &ring->tx_buffer_info[ring->next_to_use];
+ tx_buffer->bytecount = len;
+ tx_buffer->gso_segs = 1;
+ tx_buffer->protocol = 0;
+
+ i = ring->next_to_use;
+ tx_desc = IXGBEVF_TX_DESC(ring, i);
+
+ dma_unmap_len_set(tx_buffer, len, len);
+ dma_unmap_addr_set(tx_buffer, dma, dma);
+ tx_buffer->data = xdp->data;
+ tx_desc->read.buffer_addr = cpu_to_le64(dma);
+
+ /* put descriptor type bits */
+ cmd_type = IXGBE_ADVTXD_DTYP_DATA |
+ IXGBE_ADVTXD_DCMD_DEXT |
+ IXGBE_ADVTXD_DCMD_IFCS;
+ cmd_type |= len | IXGBE_TXD_CMD;
+ tx_desc->read.cmd_type_len = cpu_to_le32(cmd_type);
+ tx_desc->read.olinfo_status =
+ cpu_to_le32((len << IXGBE_ADVTXD_PAYLEN_SHIFT) |
+ IXGBE_ADVTXD_CC);
+
+ /* Avoid any potential race with cleanup */
+ smp_wmb();
+
+ /* set next_to_watch value indicating a packet is present */
+ i++;
+ if (i == ring->count)
+ i = 0;
+
+ tx_buffer->next_to_watch = tx_desc;
+ ring->next_to_use = i;
+
+ return IXGBEVF_XDP_TX;
}
-static inline void ixgbevf_irq_enable_queues(struct ixgbevf_adapter *adapter,
- u32 qmask)
+static struct sk_buff *ixgbevf_run_xdp(struct ixgbevf_adapter *adapter,
+ struct ixgbevf_ring *rx_ring,
+ struct xdp_buff *xdp)
{
- struct ixgbe_hw *hw = &adapter->hw;
+ int result = IXGBEVF_XDP_PASS;
+ struct ixgbevf_ring *xdp_ring;
+ struct bpf_prog *xdp_prog;
+ u32 act;
- IXGBE_WRITE_REG(hw, IXGBE_VTEIMS, qmask);
+ rcu_read_lock();
+ xdp_prog = READ_ONCE(rx_ring->xdp_prog);
+
+ if (!xdp_prog)
+ goto xdp_out;
+
+ act = bpf_prog_run_xdp(xdp_prog, xdp);
+ switch (act) {
+ case XDP_PASS:
+ break;
+ case XDP_TX:
+ xdp_ring = adapter->xdp_ring[rx_ring->queue_index];
+ result = ixgbevf_xmit_xdp_ring(xdp_ring, xdp);
+ break;
+ default:
+ bpf_warn_invalid_xdp_action(act);
+ /* fallthrough */
+ case XDP_ABORTED:
+ trace_xdp_exception(rx_ring->netdev, xdp_prog, act);
+ /* fallthrough -- handle aborts by dropping packet */
+ case XDP_DROP:
+ result = IXGBEVF_XDP_CONSUMED;
+ break;
+ }
+xdp_out:
+ rcu_read_unlock();
+ return ERR_PTR(-result);
+}
+
+static void ixgbevf_rx_buffer_flip(struct ixgbevf_ring *rx_ring,
+ struct ixgbevf_rx_buffer *rx_buffer,
+ unsigned int size)
+{
+#if (PAGE_SIZE < 8192)
+ unsigned int truesize = ixgbevf_rx_pg_size(rx_ring) / 2;
+
+ rx_buffer->page_offset ^= truesize;
+#else
+ unsigned int truesize = ring_uses_build_skb(rx_ring) ?
+ SKB_DATA_ALIGN(IXGBEVF_SKB_PAD + size) :
+ SKB_DATA_ALIGN(size);
+
+ rx_buffer->page_offset += truesize;
+#endif
}
static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector,
@@ -914,11 +1111,18 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector,
int budget)
{
unsigned int total_rx_bytes = 0, total_rx_packets = 0;
+ struct ixgbevf_adapter *adapter = q_vector->adapter;
u16 cleaned_count = ixgbevf_desc_unused(rx_ring);
struct sk_buff *skb = rx_ring->skb;
+ bool xdp_xmit = false;
+ struct xdp_buff xdp;
+
+ xdp.rxq = &rx_ring->xdp_rxq;
while (likely(total_rx_packets < budget)) {
+ struct ixgbevf_rx_buffer *rx_buffer;
union ixgbe_adv_rx_desc *rx_desc;
+ unsigned int size;
/* return some buffers to hardware, one at a time is too slow */
if (cleaned_count >= IXGBEVF_RX_BUFFER_WRITE) {
@@ -927,8 +1131,8 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector,
}
rx_desc = IXGBEVF_RX_DESC(rx_ring, rx_ring->next_to_clean);
-
- if (!rx_desc->wb.upper.length)
+ size = le16_to_cpu(rx_desc->wb.upper.length);
+ if (!size)
break;
/* This memory barrier is needed to keep us from reading
@@ -937,15 +1141,48 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector,
*/
rmb();
+ rx_buffer = ixgbevf_get_rx_buffer(rx_ring, size);
+
/* retrieve a buffer from the ring */
- skb = ixgbevf_fetch_rx_buffer(rx_ring, rx_desc, skb);
+ if (!skb) {
+ xdp.data = page_address(rx_buffer->page) +
+ rx_buffer->page_offset;
+ xdp.data_meta = xdp.data;
+ xdp.data_hard_start = xdp.data -
+ ixgbevf_rx_offset(rx_ring);
+ xdp.data_end = xdp.data + size;
+
+ skb = ixgbevf_run_xdp(adapter, rx_ring, &xdp);
+ }
+
+ if (IS_ERR(skb)) {
+ if (PTR_ERR(skb) == -IXGBEVF_XDP_TX) {
+ xdp_xmit = true;
+ ixgbevf_rx_buffer_flip(rx_ring, rx_buffer,
+ size);
+ } else {
+ rx_buffer->pagecnt_bias++;
+ }
+ total_rx_packets++;
+ total_rx_bytes += size;
+ } else if (skb) {
+ ixgbevf_add_rx_frag(rx_ring, rx_buffer, skb, size);
+ } else if (ring_uses_build_skb(rx_ring)) {
+ skb = ixgbevf_build_skb(rx_ring, rx_buffer,
+ &xdp, rx_desc);
+ } else {
+ skb = ixgbevf_construct_skb(rx_ring, rx_buffer,
+ &xdp, rx_desc);
+ }
/* exit if we failed to retrieve a buffer */
if (!skb) {
rx_ring->rx_stats.alloc_rx_buff_failed++;
+ rx_buffer->pagecnt_bias++;
break;
}
+ ixgbevf_put_rx_buffer(rx_ring, rx_buffer, skb);
cleaned_count++;
/* fetch next buffer in frame if non-eop */
@@ -987,6 +1224,17 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector,
/* place incomplete frames back on ring for completion */
rx_ring->skb = skb;
+ if (xdp_xmit) {
+ struct ixgbevf_ring *xdp_ring =
+ adapter->xdp_ring[rx_ring->queue_index];
+
+ /* Force memory writes to complete before letting h/w
+ * know there are new descriptors to fetch.
+ */
+ wmb();
+ ixgbevf_write_tail(xdp_ring, xdp_ring->next_to_use);
+ }
+
u64_stats_update_begin(&rx_ring->syncp);
rx_ring->stats.packets += total_rx_packets;
rx_ring->stats.bytes += total_rx_bytes;
@@ -1260,85 +1508,6 @@ static irqreturn_t ixgbevf_msix_clean_rings(int irq, void *data)
return IRQ_HANDLED;
}
-static inline void map_vector_to_rxq(struct ixgbevf_adapter *a, int v_idx,
- int r_idx)
-{
- struct ixgbevf_q_vector *q_vector = a->q_vector[v_idx];
-
- a->rx_ring[r_idx]->next = q_vector->rx.ring;
- q_vector->rx.ring = a->rx_ring[r_idx];
- q_vector->rx.count++;
-}
-
-static inline void map_vector_to_txq(struct ixgbevf_adapter *a, int v_idx,
- int t_idx)
-{
- struct ixgbevf_q_vector *q_vector = a->q_vector[v_idx];
-
- a->tx_ring[t_idx]->next = q_vector->tx.ring;
- q_vector->tx.ring = a->tx_ring[t_idx];
- q_vector->tx.count++;
-}
-
-/**
- * ixgbevf_map_rings_to_vectors - Maps descriptor rings to vectors
- * @adapter: board private structure to initialize
- *
- * This function maps descriptor rings to the queue-specific vectors
- * we were allotted through the MSI-X enabling code. Ideally, we'd have
- * one vector per ring/queue, but on a constrained vector budget, we
- * group the rings as "efficiently" as possible. You would add new
- * mapping configurations in here.
- **/
-static int ixgbevf_map_rings_to_vectors(struct ixgbevf_adapter *adapter)
-{
- int q_vectors;
- int v_start = 0;
- int rxr_idx = 0, txr_idx = 0;
- int rxr_remaining = adapter->num_rx_queues;
- int txr_remaining = adapter->num_tx_queues;
- int i, j;
- int rqpv, tqpv;
-
- q_vectors = adapter->num_msix_vectors - NON_Q_VECTORS;
-
- /* The ideal configuration...
- * We have enough vectors to map one per queue.
- */
- if (q_vectors == adapter->num_rx_queues + adapter->num_tx_queues) {
- for (; rxr_idx < rxr_remaining; v_start++, rxr_idx++)
- map_vector_to_rxq(adapter, v_start, rxr_idx);
-
- for (; txr_idx < txr_remaining; v_start++, txr_idx++)
- map_vector_to_txq(adapter, v_start, txr_idx);
- return 0;
- }
-
- /* If we don't have enough vectors for a 1-to-1
- * mapping, we'll have to group them so there are
- * multiple queues per vector.
- */
- /* Re-adjusting *qpv takes care of the remainder. */
- for (i = v_start; i < q_vectors; i++) {
- rqpv = DIV_ROUND_UP(rxr_remaining, q_vectors - i);
- for (j = 0; j < rqpv; j++) {
- map_vector_to_rxq(adapter, i, rxr_idx);
- rxr_idx++;
- rxr_remaining--;
- }
- }
- for (i = v_start; i < q_vectors; i++) {
- tqpv = DIV_ROUND_UP(txr_remaining, q_vectors - i);
- for (j = 0; j < tqpv; j++) {
- map_vector_to_txq(adapter, i, txr_idx);
- txr_idx++;
- txr_remaining--;
- }
- }
-
- return 0;
-}
-
/**
* ixgbevf_request_msix_irqs - Initialize MSI-X interrupts
* @adapter: board private structure
@@ -1411,20 +1580,6 @@ free_queue_irqs:
return err;
}
-static inline void ixgbevf_reset_q_vectors(struct ixgbevf_adapter *adapter)
-{
- int i, q_vectors = adapter->num_msix_vectors - NON_Q_VECTORS;
-
- for (i = 0; i < q_vectors; i++) {
- struct ixgbevf_q_vector *q_vector = adapter->q_vector[i];
-
- q_vector->rx.ring = NULL;
- q_vector->tx.ring = NULL;
- q_vector->rx.count = 0;
- q_vector->tx.count = 0;
- }
-}
-
/**
* ixgbevf_request_irq - initialize interrupts
* @adapter: board private structure
@@ -1464,8 +1619,6 @@ static void ixgbevf_free_irq(struct ixgbevf_adapter *adapter)
free_irq(adapter->msix_entries[i].vector,
adapter->q_vector[i]);
}
-
- ixgbevf_reset_q_vectors(adapter);
}
/**
@@ -1583,11 +1736,14 @@ static void ixgbevf_configure_tx(struct ixgbevf_adapter *adapter)
/* Setup the HW Tx Head and Tail descriptor pointers */
for (i = 0; i < adapter->num_tx_queues; i++)
ixgbevf_configure_tx_ring(adapter, adapter->tx_ring[i]);
+ for (i = 0; i < adapter->num_xdp_queues; i++)
+ ixgbevf_configure_tx_ring(adapter, adapter->xdp_ring[i]);
}
#define IXGBE_SRRCTL_BSIZEHDRSIZE_SHIFT 2
-static void ixgbevf_configure_srrctl(struct ixgbevf_adapter *adapter, int index)
+static void ixgbevf_configure_srrctl(struct ixgbevf_adapter *adapter,
+ struct ixgbevf_ring *ring, int index)
{
struct ixgbe_hw *hw = &adapter->hw;
u32 srrctl;
@@ -1595,7 +1751,10 @@ static void ixgbevf_configure_srrctl(struct ixgbevf_adapter *adapter, int index)
srrctl = IXGBE_SRRCTL_DROP_EN;
srrctl |= IXGBEVF_RX_HDR_SIZE << IXGBE_SRRCTL_BSIZEHDRSIZE_SHIFT;
- srrctl |= IXGBEVF_RX_BUFSZ >> IXGBE_SRRCTL_BSIZEPKT_SHIFT;
+ if (ring_uses_large_buffer(ring))
+ srrctl |= IXGBEVF_RXBUFFER_3072 >> IXGBE_SRRCTL_BSIZEPKT_SHIFT;
+ else
+ srrctl |= IXGBEVF_RXBUFFER_2048 >> IXGBE_SRRCTL_BSIZEPKT_SHIFT;
srrctl |= IXGBE_SRRCTL_DESCTYPE_ADV_ONEBUF;
IXGBE_WRITE_REG(hw, IXGBE_VFSRRCTL(index), srrctl);
@@ -1767,10 +1926,21 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter,
ring->next_to_use = 0;
ring->next_to_alloc = 0;
- ixgbevf_configure_srrctl(adapter, reg_idx);
+ ixgbevf_configure_srrctl(adapter, ring, reg_idx);
+
+ /* RXDCTL.RLPML does not work on 82599 */
+ if (adapter->hw.mac.type != ixgbe_mac_82599_vf) {
+ rxdctl &= ~(IXGBE_RXDCTL_RLPMLMASK |
+ IXGBE_RXDCTL_RLPML_EN);
- /* allow any size packet since we can handle overflow */
- rxdctl &= ~IXGBE_RXDCTL_RLPML_EN;
+#if (PAGE_SIZE < 8192)
+ /* Limit the maximum frame size so we don't overrun the skb */
+ if (ring_uses_build_skb(ring) &&
+ !ring_uses_large_buffer(ring))
+ rxdctl |= IXGBEVF_MAX_FRAME_BUILD_SKB |
+ IXGBE_RXDCTL_RLPML_EN;
+#endif
+ }
rxdctl |= IXGBE_RXDCTL_ENABLE | IXGBE_RXDCTL_VME;
IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl);
@@ -1779,6 +1949,29 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter,
ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring));
}
+static void ixgbevf_set_rx_buffer_len(struct ixgbevf_adapter *adapter,
+ struct ixgbevf_ring *rx_ring)
+{
+ struct net_device *netdev = adapter->netdev;
+ unsigned int max_frame = netdev->mtu + ETH_HLEN + ETH_FCS_LEN;
+
+ /* set build_skb and buffer size flags */
+ clear_ring_build_skb_enabled(rx_ring);
+ clear_ring_uses_large_buffer(rx_ring);
+
+ if (adapter->flags & IXGBEVF_FLAGS_LEGACY_RX)
+ return;
+
+ set_ring_build_skb_enabled(rx_ring);
+
+ if (PAGE_SIZE < 8192) {
+ if (max_frame <= IXGBEVF_MAX_FRAME_BUILD_SKB)
+ return;
+
+ set_ring_uses_large_buffer(rx_ring);
+ }
+}
+
/**
* ixgbevf_configure_rx - Configure 82599 VF Receive Unit after Reset
* @adapter: board private structure
@@ -1806,8 +1999,12 @@ static void ixgbevf_configure_rx(struct ixgbevf_adapter *adapter)
/* Setup the HW Rx Head and Tail Descriptor Pointers and
* the Base and Length of the Rx Descriptor Ring
*/
- for (i = 0; i < adapter->num_rx_queues; i++)
- ixgbevf_configure_rx_ring(adapter, adapter->rx_ring[i]);
+ for (i = 0; i < adapter->num_rx_queues; i++) {
+ struct ixgbevf_ring *rx_ring = adapter->rx_ring[i];
+
+ ixgbevf_set_rx_buffer_len(adapter, rx_ring);
+ ixgbevf_configure_rx_ring(adapter, rx_ring);
+ }
}
static int ixgbevf_vlan_rx_add_vid(struct net_device *netdev,
@@ -2136,13 +2333,13 @@ static void ixgbevf_clean_rx_ring(struct ixgbevf_ring *rx_ring)
dma_sync_single_range_for_cpu(rx_ring->dev,
rx_buffer->dma,
rx_buffer->page_offset,
- IXGBEVF_RX_BUFSZ,
+ ixgbevf_rx_bufsz(rx_ring),
DMA_FROM_DEVICE);
/* free resources associated with mapping */
dma_unmap_page_attrs(rx_ring->dev,
rx_buffer->dma,
- PAGE_SIZE,
+ ixgbevf_rx_pg_size(rx_ring),
DMA_FROM_DEVICE,
IXGBEVF_RX_DMA_ATTR);
@@ -2172,7 +2369,10 @@ static void ixgbevf_clean_tx_ring(struct ixgbevf_ring *tx_ring)
union ixgbe_adv_tx_desc *eop_desc, *tx_desc;
/* Free all the Tx ring sk_buffs */
- dev_kfree_skb_any(tx_buffer->skb);
+ if (ring_is_xdp(tx_ring))
+ page_frag_free(tx_buffer->data);
+ else
+ dev_kfree_skb_any(tx_buffer->skb);
/* unmap skb header data */
dma_unmap_single(tx_ring->dev,
@@ -2240,6 +2440,8 @@ static void ixgbevf_clean_all_tx_rings(struct ixgbevf_adapter *adapter)
for (i = 0; i < adapter->num_tx_queues; i++)
ixgbevf_clean_tx_ring(adapter->tx_ring[i]);
+ for (i = 0; i < adapter->num_xdp_queues; i++)
+ ixgbevf_clean_tx_ring(adapter->xdp_ring[i]);
}
void ixgbevf_down(struct ixgbevf_adapter *adapter)
@@ -2278,6 +2480,13 @@ void ixgbevf_down(struct ixgbevf_adapter *adapter)
IXGBE_TXDCTL_SWFLSH);
}
+ for (i = 0; i < adapter->num_xdp_queues; i++) {
+ u8 reg_idx = adapter->xdp_ring[i]->reg_idx;
+
+ IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx),
+ IXGBE_TXDCTL_SWFLSH);
+ }
+
if (!pci_channel_offline(adapter->pdev))
ixgbevf_reset(adapter);
@@ -2375,6 +2584,7 @@ static void ixgbevf_set_num_queues(struct ixgbevf_adapter *adapter)
/* Start with base case */
adapter->num_rx_queues = 1;
adapter->num_tx_queues = 1;
+ adapter->num_xdp_queues = 0;
spin_lock_bh(&adapter->mbx_lock);
@@ -2396,8 +2606,13 @@ static void ixgbevf_set_num_queues(struct ixgbevf_adapter *adapter)
case ixgbe_mbox_api_11:
case ixgbe_mbox_api_12:
case ixgbe_mbox_api_13:
+ if (adapter->xdp_prog &&
+ hw->mac.max_tx_queues == rss)
+ rss = rss > 3 ? 2 : 1;
+
adapter->num_rx_queues = rss;
adapter->num_tx_queues = rss;
+ adapter->num_xdp_queues = adapter->xdp_prog ? rss : 0;
default:
break;
}
@@ -2405,105 +2620,209 @@ static void ixgbevf_set_num_queues(struct ixgbevf_adapter *adapter)
}
/**
- * ixgbevf_alloc_queues - Allocate memory for all rings
+ * ixgbevf_set_interrupt_capability - set MSI-X or FAIL if not supported
* @adapter: board private structure to initialize
*
- * We allocate one ring per queue at run-time since we don't know the
- * number of queues at compile-time. The polling_netdev array is
- * intended for Multiqueue, but should work fine with a single queue.
+ * Attempt to configure the interrupts using the best available
+ * capabilities of the hardware and the kernel.
**/
-static int ixgbevf_alloc_queues(struct ixgbevf_adapter *adapter)
+static int ixgbevf_set_interrupt_capability(struct ixgbevf_adapter *adapter)
{
+ int vector, v_budget;
+
+ /* It's easy to be greedy for MSI-X vectors, but it really
+ * doesn't do us much good if we have a lot more vectors
+ * than CPU's. So let's be conservative and only ask for
+ * (roughly) the same number of vectors as there are CPU's.
+ * The default is to use pairs of vectors.
+ */
+ v_budget = max(adapter->num_rx_queues, adapter->num_tx_queues);
+ v_budget = min_t(int, v_budget, num_online_cpus());
+ v_budget += NON_Q_VECTORS;
+
+ adapter->msix_entries = kcalloc(v_budget,
+ sizeof(struct msix_entry), GFP_KERNEL);
+ if (!adapter->msix_entries)
+ return -ENOMEM;
+
+ for (vector = 0; vector < v_budget; vector++)
+ adapter->msix_entries[vector].entry = vector;
+
+ /* A failure in MSI-X entry allocation isn't fatal, but the VF driver
+ * does not support any other modes, so we will simply fail here. Note
+ * that we clean up the msix_entries pointer else-where.
+ */
+ return ixgbevf_acquire_msix_vectors(adapter, v_budget);
+}
+
+static void ixgbevf_add_ring(struct ixgbevf_ring *ring,
+ struct ixgbevf_ring_container *head)
+{
+ ring->next = head->ring;
+ head->ring = ring;
+ head->count++;
+}
+
+/**
+ * ixgbevf_alloc_q_vector - Allocate memory for a single interrupt vector
+ * @adapter: board private structure to initialize
+ * @v_idx: index of vector in adapter struct
+ * @txr_count: number of Tx rings for q vector
+ * @txr_idx: index of first Tx ring to assign
+ * @xdp_count: total number of XDP rings to allocate
+ * @xdp_idx: index of first XDP ring to allocate
+ * @rxr_count: number of Rx rings for q vector
+ * @rxr_idx: index of first Rx ring to assign
+ *
+ * We allocate one q_vector. If allocation fails we return -ENOMEM.
+ **/
+static int ixgbevf_alloc_q_vector(struct ixgbevf_adapter *adapter, int v_idx,
+ int txr_count, int txr_idx,
+ int xdp_count, int xdp_idx,
+ int rxr_count, int rxr_idx)
+{
+ struct ixgbevf_q_vector *q_vector;
+ int reg_idx = txr_idx + xdp_idx;
struct ixgbevf_ring *ring;
- int rx = 0, tx = 0;
+ int ring_count, size;
+
+ ring_count = txr_count + xdp_count + rxr_count;
+ size = sizeof(*q_vector) + (sizeof(*ring) * ring_count);
+
+ /* allocate q_vector and rings */
+ q_vector = kzalloc(size, GFP_KERNEL);
+ if (!q_vector)
+ return -ENOMEM;
+
+ /* initialize NAPI */
+ netif_napi_add(adapter->netdev, &q_vector->napi, ixgbevf_poll, 64);
+
+ /* tie q_vector and adapter together */
+ adapter->q_vector[v_idx] = q_vector;
+ q_vector->adapter = adapter;
+ q_vector->v_idx = v_idx;
- for (; tx < adapter->num_tx_queues; tx++) {
- ring = kzalloc(sizeof(*ring), GFP_KERNEL);
- if (!ring)
- goto err_allocation;
+ /* initialize pointer to rings */
+ ring = q_vector->ring;
+ while (txr_count) {
+ /* assign generic ring traits */
ring->dev = &adapter->pdev->dev;
ring->netdev = adapter->netdev;
+
+ /* configure backlink on ring */
+ ring->q_vector = q_vector;
+
+ /* update q_vector Tx values */
+ ixgbevf_add_ring(ring, &q_vector->tx);
+
+ /* apply Tx specific ring traits */
ring->count = adapter->tx_ring_count;
- ring->queue_index = tx;
- ring->reg_idx = tx;
+ ring->queue_index = txr_idx;
+ ring->reg_idx = reg_idx;
+
+ /* assign ring to adapter */
+ adapter->tx_ring[txr_idx] = ring;
- adapter->tx_ring[tx] = ring;
+ /* update count and index */
+ txr_count--;
+ txr_idx++;
+ reg_idx++;
+
+ /* push pointer to next ring */
+ ring++;
}
- for (; rx < adapter->num_rx_queues; rx++) {
- ring = kzalloc(sizeof(*ring), GFP_KERNEL);
- if (!ring)
- goto err_allocation;
+ while (xdp_count) {
+ /* assign generic ring traits */
+ ring->dev = &adapter->pdev->dev;
+ ring->netdev = adapter->netdev;
+
+ /* configure backlink on ring */
+ ring->q_vector = q_vector;
+
+ /* update q_vector Tx values */
+ ixgbevf_add_ring(ring, &q_vector->tx);
+
+ /* apply Tx specific ring traits */
+ ring->count = adapter->tx_ring_count;
+ ring->queue_index = xdp_idx;
+ ring->reg_idx = reg_idx;
+ set_ring_xdp(ring);
+
+ /* assign ring to adapter */
+ adapter->xdp_ring[xdp_idx] = ring;
+
+ /* update count and index */
+ xdp_count--;
+ xdp_idx++;
+ reg_idx++;
+
+ /* push pointer to next ring */
+ ring++;
+ }
+ while (rxr_count) {
+ /* assign generic ring traits */
ring->dev = &adapter->pdev->dev;
ring->netdev = adapter->netdev;
+ /* configure backlink on ring */
+ ring->q_vector = q_vector;
+
+ /* update q_vector Rx values */
+ ixgbevf_add_ring(ring, &q_vector->rx);
+
+ /* apply Rx specific ring traits */
ring->count = adapter->rx_ring_count;
- ring->queue_index = rx;
- ring->reg_idx = rx;
+ ring->queue_index = rxr_idx;
+ ring->reg_idx = rxr_idx;
- adapter->rx_ring[rx] = ring;
- }
+ /* assign ring to adapter */
+ adapter->rx_ring[rxr_idx] = ring;
- return 0;
+ /* update count and index */
+ rxr_count--;
+ rxr_idx++;
-err_allocation:
- while (tx) {
- kfree(adapter->tx_ring[--tx]);
- adapter->tx_ring[tx] = NULL;
+ /* push pointer to next ring */
+ ring++;
}
- while (rx) {
- kfree(adapter->rx_ring[--rx]);
- adapter->rx_ring[rx] = NULL;
- }
- return -ENOMEM;
+ return 0;
}
/**
- * ixgbevf_set_interrupt_capability - set MSI-X or FAIL if not supported
+ * ixgbevf_free_q_vector - Free memory allocated for specific interrupt vector
* @adapter: board private structure to initialize
+ * @v_idx: index of vector in adapter struct
*
- * Attempt to configure the interrupts using the best available
- * capabilities of the hardware and the kernel.
+ * This function frees the memory allocated to the q_vector. In addition if
+ * NAPI is enabled it will delete any references to the NAPI struct prior
+ * to freeing the q_vector.
**/
-static int ixgbevf_set_interrupt_capability(struct ixgbevf_adapter *adapter)
+static void ixgbevf_free_q_vector(struct ixgbevf_adapter *adapter, int v_idx)
{
- struct net_device *netdev = adapter->netdev;
- int err;
- int vector, v_budget;
-
- /* It's easy to be greedy for MSI-X vectors, but it really
- * doesn't do us much good if we have a lot more vectors
- * than CPU's. So let's be conservative and only ask for
- * (roughly) the same number of vectors as there are CPU's.
- * The default is to use pairs of vectors.
- */
- v_budget = max(adapter->num_rx_queues, adapter->num_tx_queues);
- v_budget = min_t(int, v_budget, num_online_cpus());
- v_budget += NON_Q_VECTORS;
-
- /* A failure in MSI-X entry allocation isn't fatal, but it does
- * mean we disable MSI-X capabilities of the adapter.
- */
- adapter->msix_entries = kcalloc(v_budget,
- sizeof(struct msix_entry), GFP_KERNEL);
- if (!adapter->msix_entries)
- return -ENOMEM;
+ struct ixgbevf_q_vector *q_vector = adapter->q_vector[v_idx];
+ struct ixgbevf_ring *ring;
- for (vector = 0; vector < v_budget; vector++)
- adapter->msix_entries[vector].entry = vector;
+ ixgbevf_for_each_ring(ring, q_vector->tx) {
+ if (ring_is_xdp(ring))
+ adapter->xdp_ring[ring->queue_index] = NULL;
+ else
+ adapter->tx_ring[ring->queue_index] = NULL;
+ }
- err = ixgbevf_acquire_msix_vectors(adapter, v_budget);
- if (err)
- return err;
+ ixgbevf_for_each_ring(ring, q_vector->rx)
+ adapter->rx_ring[ring->queue_index] = NULL;
- err = netif_set_real_num_tx_queues(netdev, adapter->num_tx_queues);
- if (err)
- return err;
+ adapter->q_vector[v_idx] = NULL;
+ netif_napi_del(&q_vector->napi);
- return netif_set_real_num_rx_queues(netdev, adapter->num_rx_queues);
+ /* ixgbevf_get_stats() might access the rings on this vector,
+ * we must wait a grace period before freeing it.
+ */
+ kfree_rcu(q_vector, rcu);
}
/**
@@ -2515,35 +2834,58 @@ static int ixgbevf_set_interrupt_capability(struct ixgbevf_adapter *adapter)
**/
static int ixgbevf_alloc_q_vectors(struct ixgbevf_adapter *adapter)
{
- int q_idx, num_q_vectors;
- struct ixgbevf_q_vector *q_vector;
+ int q_vectors = adapter->num_msix_vectors - NON_Q_VECTORS;
+ int rxr_remaining = adapter->num_rx_queues;
+ int txr_remaining = adapter->num_tx_queues;
+ int xdp_remaining = adapter->num_xdp_queues;
+ int rxr_idx = 0, txr_idx = 0, xdp_idx = 0, v_idx = 0;
+ int err;
- num_q_vectors = adapter->num_msix_vectors - NON_Q_VECTORS;
+ if (q_vectors >= (rxr_remaining + txr_remaining + xdp_remaining)) {
+ for (; rxr_remaining; v_idx++, q_vectors--) {
+ int rqpv = DIV_ROUND_UP(rxr_remaining, q_vectors);
- for (q_idx = 0; q_idx < num_q_vectors; q_idx++) {
- q_vector = kzalloc(sizeof(struct ixgbevf_q_vector), GFP_KERNEL);
- if (!q_vector)
+ err = ixgbevf_alloc_q_vector(adapter, v_idx,
+ 0, 0, 0, 0, rqpv, rxr_idx);
+ if (err)
+ goto err_out;
+
+ /* update counts and index */
+ rxr_remaining -= rqpv;
+ rxr_idx += rqpv;
+ }
+ }
+
+ for (; q_vectors; v_idx++, q_vectors--) {
+ int rqpv = DIV_ROUND_UP(rxr_remaining, q_vectors);
+ int tqpv = DIV_ROUND_UP(txr_remaining, q_vectors);
+ int xqpv = DIV_ROUND_UP(xdp_remaining, q_vectors);
+
+ err = ixgbevf_alloc_q_vector(adapter, v_idx,
+ tqpv, txr_idx,
+ xqpv, xdp_idx,
+ rqpv, rxr_idx);
+
+ if (err)
goto err_out;
- q_vector->adapter = adapter;
- q_vector->v_idx = q_idx;
- netif_napi_add(adapter->netdev, &q_vector->napi,
- ixgbevf_poll, 64);
- adapter->q_vector[q_idx] = q_vector;
+
+ /* update counts and index */
+ rxr_remaining -= rqpv;
+ rxr_idx += rqpv;
+ txr_remaining -= tqpv;
+ txr_idx += tqpv;
+ xdp_remaining -= xqpv;
+ xdp_idx += xqpv;
}
return 0;
err_out:
- while (q_idx) {
- q_idx--;
- q_vector = adapter->q_vector[q_idx];
-#ifdef CONFIG_NET_RX_BUSY_POLL
- napi_hash_del(&q_vector->napi);
-#endif
- netif_napi_del(&q_vector->napi);
- kfree(q_vector);
- adapter->q_vector[q_idx] = NULL;
+ while (v_idx) {
+ v_idx--;
+ ixgbevf_free_q_vector(adapter, v_idx);
}
+
return -ENOMEM;
}
@@ -2557,17 +2899,11 @@ err_out:
**/
static void ixgbevf_free_q_vectors(struct ixgbevf_adapter *adapter)
{
- int q_idx, num_q_vectors = adapter->num_msix_vectors - NON_Q_VECTORS;
-
- for (q_idx = 0; q_idx < num_q_vectors; q_idx++) {
- struct ixgbevf_q_vector *q_vector = adapter->q_vector[q_idx];
+ int q_vectors = adapter->num_msix_vectors - NON_Q_VECTORS;
- adapter->q_vector[q_idx] = NULL;
-#ifdef CONFIG_NET_RX_BUSY_POLL
- napi_hash_del(&q_vector->napi);
-#endif
- netif_napi_del(&q_vector->napi);
- kfree(q_vector);
+ while (q_vectors) {
+ q_vectors--;
+ ixgbevf_free_q_vector(adapter, q_vectors);
}
}
@@ -2611,21 +2947,14 @@ static int ixgbevf_init_interrupt_scheme(struct ixgbevf_adapter *adapter)
goto err_alloc_q_vectors;
}
- err = ixgbevf_alloc_queues(adapter);
- if (err) {
- pr_err("Unable to allocate memory for queues\n");
- goto err_alloc_queues;
- }
-
- hw_dbg(&adapter->hw, "Multiqueue %s: Rx Queue count = %u, Tx Queue count = %u\n",
- (adapter->num_rx_queues > 1) ? "Enabled" :
- "Disabled", adapter->num_rx_queues, adapter->num_tx_queues);
+ hw_dbg(&adapter->hw, "Multiqueue %s: Rx Queue count = %u, Tx Queue count = %u XDP Queue count %u\n",
+ (adapter->num_rx_queues > 1) ? "Enabled" : "Disabled",
+ adapter->num_rx_queues, adapter->num_tx_queues,
+ adapter->num_xdp_queues);
set_bit(__IXGBEVF_DOWN, &adapter->state);
return 0;
-err_alloc_queues:
- ixgbevf_free_q_vectors(adapter);
err_alloc_q_vectors:
ixgbevf_reset_interrupt_capability(adapter);
err_set_interrupt:
@@ -2641,18 +2970,8 @@ err_set_interrupt:
**/
static void ixgbevf_clear_interrupt_scheme(struct ixgbevf_adapter *adapter)
{
- int i;
-
- for (i = 0; i < adapter->num_tx_queues; i++) {
- kfree(adapter->tx_ring[i]);
- adapter->tx_ring[i] = NULL;
- }
- for (i = 0; i < adapter->num_rx_queues; i++) {
- kfree(adapter->rx_ring[i]);
- adapter->rx_ring[i] = NULL;
- }
-
adapter->num_tx_queues = 0;
+ adapter->num_xdp_queues = 0;
adapter->num_rx_queues = 0;
ixgbevf_free_q_vectors(adapter);
@@ -2860,6 +3179,8 @@ static void ixgbevf_check_hang_subtask(struct ixgbevf_adapter *adapter)
if (netif_carrier_ok(adapter->netdev)) {
for (i = 0; i < adapter->num_tx_queues; i++)
set_check_for_tx_hang(adapter->tx_ring[i]);
+ for (i = 0; i < adapter->num_xdp_queues; i++)
+ set_check_for_tx_hang(adapter->xdp_ring[i]);
}
/* get one bit for every active Tx/Rx interrupt vector */
@@ -3031,6 +3352,9 @@ static void ixgbevf_free_all_tx_resources(struct ixgbevf_adapter *adapter)
for (i = 0; i < adapter->num_tx_queues; i++)
if (adapter->tx_ring[i]->desc)
ixgbevf_free_tx_resources(adapter->tx_ring[i]);
+ for (i = 0; i < adapter->num_xdp_queues; i++)
+ if (adapter->xdp_ring[i]->desc)
+ ixgbevf_free_tx_resources(adapter->xdp_ring[i]);
}
/**
@@ -3081,26 +3405,44 @@ err:
**/
static int ixgbevf_setup_all_tx_resources(struct ixgbevf_adapter *adapter)
{
- int i, err = 0;
+ int i, j = 0, err = 0;
for (i = 0; i < adapter->num_tx_queues; i++) {
err = ixgbevf_setup_tx_resources(adapter->tx_ring[i]);
if (!err)
continue;
hw_dbg(&adapter->hw, "Allocation for Tx Queue %u failed\n", i);
- break;
+ goto err_setup_tx;
}
+ for (j = 0; j < adapter->num_xdp_queues; j++) {
+ err = ixgbevf_setup_tx_resources(adapter->xdp_ring[j]);
+ if (!err)
+ continue;
+ hw_dbg(&adapter->hw, "Allocation for XDP Queue %u failed\n", j);
+ goto err_setup_tx;
+ }
+
+ return 0;
+err_setup_tx:
+ /* rewind the index freeing the rings as we go */
+ while (j--)
+ ixgbevf_free_tx_resources(adapter->xdp_ring[j]);
+ while (i--)
+ ixgbevf_free_tx_resources(adapter->tx_ring[i]);
+
return err;
}
/**
* ixgbevf_setup_rx_resources - allocate Rx resources (Descriptors)
+ * @adapter: board private structure
* @rx_ring: Rx descriptor ring (for a specific queue) to setup
*
* Returns 0 on success, negative on failure
**/
-int ixgbevf_setup_rx_resources(struct ixgbevf_ring *rx_ring)
+int ixgbevf_setup_rx_resources(struct ixgbevf_adapter *adapter,
+ struct ixgbevf_ring *rx_ring)
{
int size;
@@ -3121,6 +3463,13 @@ int ixgbevf_setup_rx_resources(struct ixgbevf_ring *rx_ring)
if (!rx_ring->desc)
goto err;
+ /* XDP RX-queue info */
+ if (xdp_rxq_info_reg(&rx_ring->xdp_rxq, adapter->netdev,
+ rx_ring->queue_index) < 0)
+ goto err;
+
+ rx_ring->xdp_prog = adapter->xdp_prog;
+
return 0;
err:
vfree(rx_ring->rx_buffer_info);
@@ -3144,12 +3493,18 @@ static int ixgbevf_setup_all_rx_resources(struct ixgbevf_adapter *adapter)
int i, err = 0;
for (i = 0; i < adapter->num_rx_queues; i++) {
- err = ixgbevf_setup_rx_resources(adapter->rx_ring[i]);
+ err = ixgbevf_setup_rx_resources(adapter, adapter->rx_ring[i]);
if (!err)
continue;
hw_dbg(&adapter->hw, "Allocation for Rx Queue %u failed\n", i);
- break;
+ goto err_setup_rx;
}
+
+ return 0;
+err_setup_rx:
+ /* rewind the index freeing the rings as we go */
+ while (i--)
+ ixgbevf_free_rx_resources(adapter->rx_ring[i]);
return err;
}
@@ -3163,6 +3518,8 @@ void ixgbevf_free_rx_resources(struct ixgbevf_ring *rx_ring)
{
ixgbevf_clean_rx_ring(rx_ring);
+ rx_ring->xdp_prog = NULL;
+ xdp_rxq_info_unreg(&rx_ring->xdp_rxq);
vfree(rx_ring->rx_buffer_info);
rx_ring->rx_buffer_info = NULL;
@@ -3244,28 +3601,31 @@ int ixgbevf_open(struct net_device *netdev)
ixgbevf_configure(adapter);
- /* Map the Tx/Rx rings to the vectors we were allotted.
- * if request_irq will be called in this function map_rings
- * must be called *before* up_complete
- */
- ixgbevf_map_rings_to_vectors(adapter);
-
err = ixgbevf_request_irq(adapter);
if (err)
goto err_req_irq;
+ /* Notify the stack of the actual queue counts. */
+ err = netif_set_real_num_tx_queues(netdev, adapter->num_tx_queues);
+ if (err)
+ goto err_set_queues;
+
+ err = netif_set_real_num_rx_queues(netdev, adapter->num_rx_queues);
+ if (err)
+ goto err_set_queues;
+
ixgbevf_up_complete(adapter);
return 0;
+err_set_queues:
+ ixgbevf_free_irq(adapter);
err_req_irq:
- ixgbevf_down(adapter);
-err_setup_rx:
ixgbevf_free_all_rx_resources(adapter);
-err_setup_tx:
+err_setup_rx:
ixgbevf_free_all_tx_resources(adapter);
+err_setup_tx:
ixgbevf_reset(adapter);
-
err_setup_reset:
return err;
@@ -3707,11 +4067,10 @@ static int ixgbevf_maybe_stop_tx(struct ixgbevf_ring *tx_ring, int size)
return __ixgbevf_maybe_stop_tx(tx_ring, size);
}
-static int ixgbevf_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
+static int ixgbevf_xmit_frame_ring(struct sk_buff *skb,
+ struct ixgbevf_ring *tx_ring)
{
- struct ixgbevf_adapter *adapter = netdev_priv(netdev);
struct ixgbevf_tx_buffer *first;
- struct ixgbevf_ring *tx_ring;
int tso;
u32 tx_flags = 0;
u16 count = TXD_USE_COUNT(skb_headlen(skb));
@@ -3726,8 +4085,6 @@ static int ixgbevf_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
return NETDEV_TX_OK;
}
- tx_ring = adapter->tx_ring[skb->queue_mapping];
-
/* need: 1 descriptor per page * PAGE_SIZE/IXGBE_MAX_DATA_PER_TXD,
* + 1 desc for skb_headlen/IXGBE_MAX_DATA_PER_TXD,
* + 2 desc gap to keep tail from touching head,
@@ -3780,6 +4137,29 @@ out_drop:
return NETDEV_TX_OK;
}
+static int ixgbevf_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
+{
+ struct ixgbevf_adapter *adapter = netdev_priv(netdev);
+ struct ixgbevf_ring *tx_ring;
+
+ if (skb->len <= 0) {
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+ }
+
+ /* The minimum packet size for olinfo paylen is 17 so pad the skb
+ * in order to meet this minimum size requirement.
+ */
+ if (skb->len < 17) {
+ if (skb_padto(skb, 17))
+ return NETDEV_TX_OK;
+ skb->len = 17;
+ }
+
+ tx_ring = adapter->tx_ring[skb->queue_mapping];
+ return ixgbevf_xmit_frame_ring(skb, tx_ring);
+}
+
/**
* ixgbevf_set_mac - Change the Ethernet Address of the NIC
* @netdev: network interface device structure
@@ -3826,6 +4206,12 @@ static int ixgbevf_change_mtu(struct net_device *netdev, int new_mtu)
int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN;
int ret;
+ /* prevent MTU being changed to a size unsupported by XDP */
+ if (adapter->xdp_prog) {
+ dev_warn(&adapter->pdev->dev, "MTU cannot be changed while XDP program is loaded\n");
+ return -EPERM;
+ }
+
spin_lock_bh(&adapter->mbx_lock);
/* notify the PF of our intent to use this size of frame */
ret = hw->mac.ops.set_rlpml(hw, max_frame);
@@ -3839,6 +4225,9 @@ static int ixgbevf_change_mtu(struct net_device *netdev, int new_mtu)
/* must set new MTU before calling down or up */
netdev->mtu = new_mtu;
+ if (netif_running(netdev))
+ ixgbevf_reinit_locked(adapter);
+
return 0;
}
@@ -3917,17 +4306,11 @@ static int ixgbevf_resume(struct pci_dev *pdev)
rtnl_lock();
err = ixgbevf_init_interrupt_scheme(adapter);
+ if (!err && netif_running(netdev))
+ err = ixgbevf_open(netdev);
rtnl_unlock();
- if (err) {
- dev_err(&pdev->dev, "Cannot initialize interrupts\n");
+ if (err)
return err;
- }
-
- if (netif_running(netdev)) {
- err = ixgbevf_open(netdev);
- if (err)
- return err;
- }
netif_device_attach(netdev);
@@ -3940,6 +4323,23 @@ static void ixgbevf_shutdown(struct pci_dev *pdev)
ixgbevf_suspend(pdev, PMSG_SUSPEND);
}
+static void ixgbevf_get_tx_ring_stats(struct rtnl_link_stats64 *stats,
+ const struct ixgbevf_ring *ring)
+{
+ u64 bytes, packets;
+ unsigned int start;
+
+ if (ring) {
+ do {
+ start = u64_stats_fetch_begin_irq(&ring->syncp);
+ bytes = ring->stats.bytes;
+ packets = ring->stats.packets;
+ } while (u64_stats_fetch_retry_irq(&ring->syncp, start));
+ stats->tx_bytes += bytes;
+ stats->tx_packets += packets;
+ }
+}
+
static void ixgbevf_get_stats(struct net_device *netdev,
struct rtnl_link_stats64 *stats)
{
@@ -3953,6 +4353,7 @@ static void ixgbevf_get_stats(struct net_device *netdev,
stats->multicast = adapter->stats.vfmprc - adapter->stats.base_vfmprc;
+ rcu_read_lock();
for (i = 0; i < adapter->num_rx_queues; i++) {
ring = adapter->rx_ring[i];
do {
@@ -3966,14 +4367,14 @@ static void ixgbevf_get_stats(struct net_device *netdev,
for (i = 0; i < adapter->num_tx_queues; i++) {
ring = adapter->tx_ring[i];
- do {
- start = u64_stats_fetch_begin_irq(&ring->syncp);
- bytes = ring->stats.bytes;
- packets = ring->stats.packets;
- } while (u64_stats_fetch_retry_irq(&ring->syncp, start));
- stats->tx_bytes += bytes;
- stats->tx_packets += packets;
+ ixgbevf_get_tx_ring_stats(stats, ring);
+ }
+
+ for (i = 0; i < adapter->num_xdp_queues; i++) {
+ ring = adapter->xdp_ring[i];
+ ixgbevf_get_tx_ring_stats(stats, ring);
}
+ rcu_read_unlock();
}
#define IXGBEVF_MAX_MAC_HDR_LEN 127
@@ -4010,6 +4411,64 @@ ixgbevf_features_check(struct sk_buff *skb, struct net_device *dev,
return features;
}
+static int ixgbevf_xdp_setup(struct net_device *dev, struct bpf_prog *prog)
+{
+ int i, frame_size = dev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
+ struct ixgbevf_adapter *adapter = netdev_priv(dev);
+ struct bpf_prog *old_prog;
+
+ /* verify ixgbevf ring attributes are sufficient for XDP */
+ for (i = 0; i < adapter->num_rx_queues; i++) {
+ struct ixgbevf_ring *ring = adapter->rx_ring[i];
+
+ if (frame_size > ixgbevf_rx_bufsz(ring))
+ return -EINVAL;
+ }
+
+ old_prog = xchg(&adapter->xdp_prog, prog);
+
+ /* If transitioning XDP modes reconfigure rings */
+ if (!!prog != !!old_prog) {
+ /* Hardware has to reinitialize queues and interrupts to
+ * match packet buffer alignment. Unfortunately, the
+ * hardware is not flexible enough to do this dynamically.
+ */
+ if (netif_running(dev))
+ ixgbevf_close(dev);
+
+ ixgbevf_clear_interrupt_scheme(adapter);
+ ixgbevf_init_interrupt_scheme(adapter);
+
+ if (netif_running(dev))
+ ixgbevf_open(dev);
+ } else {
+ for (i = 0; i < adapter->num_rx_queues; i++)
+ xchg(&adapter->rx_ring[i]->xdp_prog, adapter->xdp_prog);
+ }
+
+ if (old_prog)
+ bpf_prog_put(old_prog);
+
+ return 0;
+}
+
+static int ixgbevf_xdp(struct net_device *dev, struct netdev_bpf *xdp)
+{
+ struct ixgbevf_adapter *adapter = netdev_priv(dev);
+
+ switch (xdp->command) {
+ case XDP_SETUP_PROG:
+ return ixgbevf_xdp_setup(dev, xdp->prog);
+ case XDP_QUERY_PROG:
+ xdp->prog_attached = !!(adapter->xdp_prog);
+ xdp->prog_id = adapter->xdp_prog ?
+ adapter->xdp_prog->aux->id : 0;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
static const struct net_device_ops ixgbevf_netdev_ops = {
.ndo_open = ixgbevf_open,
.ndo_stop = ixgbevf_close,
@@ -4026,6 +4485,7 @@ static const struct net_device_ops ixgbevf_netdev_ops = {
.ndo_poll_controller = ixgbevf_netpoll,
#endif
.ndo_features_check = ixgbevf_features_check,
+ .ndo_bpf = ixgbevf_xdp,
};
static void ixgbevf_assign_netdev_ops(struct net_device *dev)
diff --git a/drivers/net/ethernet/intel/ixgbevf/mbx.h b/drivers/net/ethernet/intel/ixgbevf/mbx.h
index bc0442acae78..5ec947fe3d09 100644
--- a/drivers/net/ethernet/intel/ixgbevf/mbx.h
+++ b/drivers/net/ethernet/intel/ixgbevf/mbx.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
Intel 82599 Virtual Function driver
diff --git a/drivers/net/ethernet/intel/ixgbevf/regs.h b/drivers/net/ethernet/intel/ixgbevf/regs.h
index 2764fd16261f..278f73980501 100644
--- a/drivers/net/ethernet/intel/ixgbevf/regs.h
+++ b/drivers/net/ethernet/intel/ixgbevf/regs.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
Intel 82599 Virtual Function driver
diff --git a/drivers/net/ethernet/intel/ixgbevf/vf.h b/drivers/net/ethernet/intel/ixgbevf/vf.h
index c651fefcc3d2..194fbdaa4519 100644
--- a/drivers/net/ethernet/intel/ixgbevf/vf.h
+++ b/drivers/net/ethernet/intel/ixgbevf/vf.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*******************************************************************************
Intel 82599 Virtual Function driver
diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index 25e9a551cc8c..17a904cc6a5e 100644
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -1132,6 +1132,7 @@ static void mvneta_port_up(struct mvneta_port *pp)
}
mvreg_write(pp, MVNETA_TXQ_CMD, q_map);
+ q_map = 0;
/* Enable all initialized RXQs. */
for (queue = 0; queue < rxq_number; queue++) {
struct mvneta_rx_queue *rxq = &pp->rxqs[queue];
@@ -1555,7 +1556,6 @@ static void mvneta_rx_pkts_coal_set(struct mvneta_port *pp,
{
mvreg_write(pp, MVNETA_RXQ_THRESHOLD_REG(rxq->id),
value | MVNETA_RXQ_NON_OCCUPIED(0));
- rxq->pkts_coal = value;
}
/* Set the time delay in usec before RX interrupt will be generated by
@@ -1571,7 +1571,6 @@ static void mvneta_rx_time_coal_set(struct mvneta_port *pp,
val = (clk_rate / 1000000) * value;
mvreg_write(pp, MVNETA_RXQ_TIME_COAL_REG(rxq->id), val);
- rxq->time_coal = value;
}
/* Set threshold for TX_DONE pkts coalescing */
@@ -1586,8 +1585,6 @@ static void mvneta_tx_done_pkts_coal_set(struct mvneta_port *pp,
val |= MVNETA_TXQ_SENT_THRESH_MASK(value);
mvreg_write(pp, MVNETA_TXQ_SIZE_REG(txq->id), val);
-
- txq->done_pkts_coal = value;
}
/* Handle rx descriptor fill by setting buf_cookie and buf_phys_addr */
@@ -2800,10 +2797,8 @@ static void mvneta_rx_reset(struct mvneta_port *pp)
/* Rx/Tx queue initialization/cleanup methods */
-/* Create a specified RX queue */
-static int mvneta_rxq_init(struct mvneta_port *pp,
- struct mvneta_rx_queue *rxq)
-
+static int mvneta_rxq_sw_init(struct mvneta_port *pp,
+ struct mvneta_rx_queue *rxq)
{
rxq->size = pp->rx_ring_size;
@@ -2816,6 +2811,12 @@ static int mvneta_rxq_init(struct mvneta_port *pp,
rxq->last_desc = rxq->size - 1;
+ return 0;
+}
+
+static void mvneta_rxq_hw_init(struct mvneta_port *pp,
+ struct mvneta_rx_queue *rxq)
+{
/* Set Rx descriptors queue starting address */
mvreg_write(pp, MVNETA_RXQ_BASE_ADDR_REG(rxq->id), rxq->descs_phys);
mvreg_write(pp, MVNETA_RXQ_SIZE_REG(rxq->id), rxq->size);
@@ -2839,6 +2840,20 @@ static int mvneta_rxq_init(struct mvneta_port *pp,
mvneta_rxq_short_pool_set(pp, rxq);
mvneta_rxq_non_occup_desc_add(pp, rxq, rxq->size);
}
+}
+
+/* Create a specified RX queue */
+static int mvneta_rxq_init(struct mvneta_port *pp,
+ struct mvneta_rx_queue *rxq)
+
+{
+ int ret;
+
+ ret = mvneta_rxq_sw_init(pp, rxq);
+ if (ret < 0)
+ return ret;
+
+ mvneta_rxq_hw_init(pp, rxq);
return 0;
}
@@ -2861,9 +2876,8 @@ static void mvneta_rxq_deinit(struct mvneta_port *pp,
rxq->descs_phys = 0;
}
-/* Create and initialize a tx queue */
-static int mvneta_txq_init(struct mvneta_port *pp,
- struct mvneta_tx_queue *txq)
+static int mvneta_txq_sw_init(struct mvneta_port *pp,
+ struct mvneta_tx_queue *txq)
{
int cpu;
@@ -2876,7 +2890,6 @@ static int mvneta_txq_init(struct mvneta_port *pp,
txq->tx_stop_threshold = txq->size - MVNETA_MAX_SKB_DESCS;
txq->tx_wake_threshold = txq->tx_stop_threshold / 2;
-
/* Allocate memory for TX descriptors */
txq->descs = dma_alloc_coherent(pp->dev->dev.parent,
txq->size * MVNETA_DESC_ALIGNED_SIZE,
@@ -2886,14 +2899,6 @@ static int mvneta_txq_init(struct mvneta_port *pp,
txq->last_desc = txq->size - 1;
- /* Set maximum bandwidth for enabled TXQs */
- mvreg_write(pp, MVETH_TXQ_TOKEN_CFG_REG(txq->id), 0x03ffffff);
- mvreg_write(pp, MVETH_TXQ_TOKEN_COUNT_REG(txq->id), 0x3fffffff);
-
- /* Set Tx descriptors queue starting address */
- mvreg_write(pp, MVNETA_TXQ_BASE_ADDR_REG(txq->id), txq->descs_phys);
- mvreg_write(pp, MVNETA_TXQ_SIZE_REG(txq->id), txq->size);
-
txq->tx_skb = kmalloc_array(txq->size, sizeof(*txq->tx_skb),
GFP_KERNEL);
if (!txq->tx_skb) {
@@ -2914,7 +2919,6 @@ static int mvneta_txq_init(struct mvneta_port *pp,
txq->descs, txq->descs_phys);
return -ENOMEM;
}
- mvneta_tx_done_pkts_coal_set(pp, txq, txq->done_pkts_coal);
/* Setup XPS mapping */
if (txq_number > 1)
@@ -2927,9 +2931,38 @@ static int mvneta_txq_init(struct mvneta_port *pp,
return 0;
}
+static void mvneta_txq_hw_init(struct mvneta_port *pp,
+ struct mvneta_tx_queue *txq)
+{
+ /* Set maximum bandwidth for enabled TXQs */
+ mvreg_write(pp, MVETH_TXQ_TOKEN_CFG_REG(txq->id), 0x03ffffff);
+ mvreg_write(pp, MVETH_TXQ_TOKEN_COUNT_REG(txq->id), 0x3fffffff);
+
+ /* Set Tx descriptors queue starting address */
+ mvreg_write(pp, MVNETA_TXQ_BASE_ADDR_REG(txq->id), txq->descs_phys);
+ mvreg_write(pp, MVNETA_TXQ_SIZE_REG(txq->id), txq->size);
+
+ mvneta_tx_done_pkts_coal_set(pp, txq, txq->done_pkts_coal);
+}
+
+/* Create and initialize a tx queue */
+static int mvneta_txq_init(struct mvneta_port *pp,
+ struct mvneta_tx_queue *txq)
+{
+ int ret;
+
+ ret = mvneta_txq_sw_init(pp, txq);
+ if (ret < 0)
+ return ret;
+
+ mvneta_txq_hw_init(pp, txq);
+
+ return 0;
+}
+
/* Free allocated resources when mvneta_txq_init() fails to allocate memory*/
-static void mvneta_txq_deinit(struct mvneta_port *pp,
- struct mvneta_tx_queue *txq)
+static void mvneta_txq_sw_deinit(struct mvneta_port *pp,
+ struct mvneta_tx_queue *txq)
{
struct netdev_queue *nq = netdev_get_tx_queue(pp->dev, txq->id);
@@ -2950,7 +2983,11 @@ static void mvneta_txq_deinit(struct mvneta_port *pp,
txq->last_desc = 0;
txq->next_desc_to_proc = 0;
txq->descs_phys = 0;
+}
+static void mvneta_txq_hw_deinit(struct mvneta_port *pp,
+ struct mvneta_tx_queue *txq)
+{
/* Set minimum bandwidth for disabled TXQs */
mvreg_write(pp, MVETH_TXQ_TOKEN_CFG_REG(txq->id), 0);
mvreg_write(pp, MVETH_TXQ_TOKEN_COUNT_REG(txq->id), 0);
@@ -2960,6 +2997,13 @@ static void mvneta_txq_deinit(struct mvneta_port *pp,
mvreg_write(pp, MVNETA_TXQ_SIZE_REG(txq->id), 0);
}
+static void mvneta_txq_deinit(struct mvneta_port *pp,
+ struct mvneta_tx_queue *txq)
+{
+ mvneta_txq_sw_deinit(pp, txq);
+ mvneta_txq_hw_deinit(pp, txq);
+}
+
/* Cleanup all Tx queues */
static void mvneta_cleanup_txqs(struct mvneta_port *pp)
{
@@ -3396,7 +3440,8 @@ static void mvneta_set_eee(struct mvneta_port *pp, bool enable)
mvreg_write(pp, MVNETA_LPI_CTRL_1, lpi_ctl1);
}
-static void mvneta_mac_link_down(struct net_device *ndev, unsigned int mode)
+static void mvneta_mac_link_down(struct net_device *ndev, unsigned int mode,
+ phy_interface_t interface)
{
struct mvneta_port *pp = netdev_priv(ndev);
u32 val;
@@ -3415,6 +3460,7 @@ static void mvneta_mac_link_down(struct net_device *ndev, unsigned int mode)
}
static void mvneta_mac_link_up(struct net_device *ndev, unsigned int mode,
+ phy_interface_t interface,
struct phy_device *phy)
{
struct mvneta_port *pp = netdev_priv(ndev);
@@ -4073,22 +4119,6 @@ static int mvneta_ethtool_set_wol(struct net_device *dev,
return ret;
}
-static int mvneta_ethtool_get_module_info(struct net_device *dev,
- struct ethtool_modinfo *modinfo)
-{
- struct mvneta_port *pp = netdev_priv(dev);
-
- return phylink_ethtool_get_module_info(pp->phylink, modinfo);
-}
-
-static int mvneta_ethtool_get_module_eeprom(struct net_device *dev,
- struct ethtool_eeprom *ee, u8 *buf)
-{
- struct mvneta_port *pp = netdev_priv(dev);
-
- return phylink_ethtool_get_module_eeprom(pp->phylink, ee, buf);
-}
-
static int mvneta_ethtool_get_eee(struct net_device *dev,
struct ethtool_eee *eee)
{
@@ -4163,8 +4193,6 @@ static const struct ethtool_ops mvneta_eth_tool_ops = {
.set_link_ksettings = mvneta_ethtool_set_link_ksettings,
.get_wol = mvneta_ethtool_get_wol,
.set_wol = mvneta_ethtool_set_wol,
- .get_module_info = mvneta_ethtool_get_module_info,
- .get_module_eeprom = mvneta_ethtool_get_module_eeprom,
.get_eee = mvneta_ethtool_get_eee,
.set_eee = mvneta_ethtool_set_eee,
};
@@ -4543,16 +4571,45 @@ static int mvneta_remove(struct platform_device *pdev)
#ifdef CONFIG_PM_SLEEP
static int mvneta_suspend(struct device *device)
{
+ int queue;
struct net_device *dev = dev_get_drvdata(device);
struct mvneta_port *pp = netdev_priv(dev);
+ if (!netif_running(dev))
+ goto clean_exit;
+
+ if (!pp->neta_armada3700) {
+ spin_lock(&pp->lock);
+ pp->is_stopped = true;
+ spin_unlock(&pp->lock);
+
+ cpuhp_state_remove_instance_nocalls(online_hpstate,
+ &pp->node_online);
+ cpuhp_state_remove_instance_nocalls(CPUHP_NET_MVNETA_DEAD,
+ &pp->node_dead);
+ }
+
rtnl_lock();
- if (netif_running(dev))
- mvneta_stop(dev);
+ mvneta_stop_dev(pp);
rtnl_unlock();
+
+ for (queue = 0; queue < rxq_number; queue++) {
+ struct mvneta_rx_queue *rxq = &pp->rxqs[queue];
+
+ mvneta_rxq_drop_pkts(pp, rxq);
+ }
+
+ for (queue = 0; queue < txq_number; queue++) {
+ struct mvneta_tx_queue *txq = &pp->txqs[queue];
+
+ mvneta_txq_hw_deinit(pp, txq);
+ }
+
+clean_exit:
netif_device_detach(dev);
clk_disable_unprepare(pp->clk_bus);
clk_disable_unprepare(pp->clk);
+
return 0;
}
@@ -4561,7 +4618,7 @@ static int mvneta_resume(struct device *device)
struct platform_device *pdev = to_platform_device(device);
struct net_device *dev = dev_get_drvdata(device);
struct mvneta_port *pp = netdev_priv(dev);
- int err;
+ int err, queue;
clk_prepare_enable(pp->clk);
if (!IS_ERR(pp->clk_bus))
@@ -4583,12 +4640,38 @@ static int mvneta_resume(struct device *device)
}
netif_device_attach(dev);
- rtnl_lock();
- if (netif_running(dev)) {
- mvneta_open(dev);
- mvneta_set_rx_mode(dev);
+
+ if (!netif_running(dev))
+ return 0;
+
+ for (queue = 0; queue < rxq_number; queue++) {
+ struct mvneta_rx_queue *rxq = &pp->rxqs[queue];
+
+ rxq->next_desc_to_proc = 0;
+ mvneta_rxq_hw_init(pp, rxq);
+ }
+
+ for (queue = 0; queue < txq_number; queue++) {
+ struct mvneta_tx_queue *txq = &pp->txqs[queue];
+
+ txq->next_desc_to_proc = 0;
+ mvneta_txq_hw_init(pp, txq);
+ }
+
+ if (!pp->neta_armada3700) {
+ spin_lock(&pp->lock);
+ pp->is_stopped = false;
+ spin_unlock(&pp->lock);
+ cpuhp_state_add_instance_nocalls(online_hpstate,
+ &pp->node_online);
+ cpuhp_state_add_instance_nocalls(CPUHP_NET_MVNETA_DEAD,
+ &pp->node_dead);
}
+
+ rtnl_lock();
+ mvneta_start_dev(pp);
rtnl_unlock();
+ mvneta_set_rx_mode(dev);
return 0;
}
@@ -4655,8 +4738,8 @@ MODULE_DESCRIPTION("Marvell NETA Ethernet Driver - www.marvell.com");
MODULE_AUTHOR("Rami Rosen <rosenr@marvell.com>, Thomas Petazzoni <thomas.petazzoni@free-electrons.com>");
MODULE_LICENSE("GPL");
-module_param(rxq_number, int, S_IRUGO);
-module_param(txq_number, int, S_IRUGO);
+module_param(rxq_number, int, 0444);
+module_param(txq_number, int, 0444);
-module_param(rxq_def, int, S_IRUGO);
-module_param(rx_copybreak, int, S_IRUGO | S_IWUSR);
+module_param(rxq_def, int, 0444);
+module_param(rx_copybreak, int, 0644);
diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c
index 5a1668cdb461..4202f9b5b966 100644
--- a/drivers/net/ethernet/marvell/mvpp2.c
+++ b/drivers/net/ethernet/marvell/mvpp2.c
@@ -44,6 +44,7 @@
#define MVPP2_RX_ATTR_FIFO_SIZE_REG(port) (0x20 + 4 * (port))
#define MVPP2_RX_MIN_PKT_SIZE_REG 0x60
#define MVPP2_RX_FIFO_INIT_REG 0x64
+#define MVPP22_TX_FIFO_THRESH_REG(port) (0x8840 + 4 * (port))
#define MVPP22_TX_FIFO_SIZE_REG(port) (0x8860 + 4 * (port))
/* RX DMA Top Registers */
@@ -65,6 +66,10 @@
#define MVPP2_RXQ_PACKET_OFFSET_MASK 0x70000000
#define MVPP2_RXQ_DISABLE_MASK BIT(31)
+/* Top Registers */
+#define MVPP2_MH_REG(port) (0x5040 + 4 * (port))
+#define MVPP2_DSA_EXTENDED BIT(5)
+
/* Parser Registers */
#define MVPP2_PRS_INIT_LOOKUP_REG 0x1000
#define MVPP2_PRS_PORT_LU_MAX 0xf
@@ -254,6 +259,7 @@
#define MVPP2_BM_BPPI_READ_PTR_REG(pool) (0x6100 + ((pool) * 4))
#define MVPP2_BM_BPPI_PTRS_NUM_REG(pool) (0x6140 + ((pool) * 4))
#define MVPP2_BM_BPPI_PTR_NUM_MASK 0x7ff
+#define MVPP22_BM_POOL_PTRS_NUM_MASK 0xfff8
#define MVPP2_BM_BPPI_PREFETCH_FULL_MASK BIT(16)
#define MVPP2_BM_POOL_CTRL_REG(pool) (0x6200 + ((pool) * 4))
#define MVPP2_BM_START_MASK BIT(0)
@@ -473,6 +479,7 @@
#define MVPP2_ETH_TYPE_LEN 2
#define MVPP2_PPPOE_HDR_SIZE 8
#define MVPP2_VLAN_TAG_LEN 4
+#define MVPP2_VLAN_TAG_EDSA_LEN 8
/* Lbtd 802.3 type */
#define MVPP2_IP_LBDT_TYPE 0xfffa
@@ -536,6 +543,11 @@
/* TX FIFO constants */
#define MVPP22_TX_FIFO_DATA_SIZE_10KB 0xa
#define MVPP22_TX_FIFO_DATA_SIZE_3KB 0x3
+#define MVPP2_TX_FIFO_THRESHOLD_MIN 256
+#define MVPP2_TX_FIFO_THRESHOLD_10KB \
+ (MVPP22_TX_FIFO_DATA_SIZE_10KB * 1024 - MVPP2_TX_FIFO_THRESHOLD_MIN)
+#define MVPP2_TX_FIFO_THRESHOLD_3KB \
+ (MVPP22_TX_FIFO_DATA_SIZE_3KB * 1024 - MVPP2_TX_FIFO_THRESHOLD_MIN)
/* RX buffer constants */
#define MVPP2_SKB_SHINFO_SIZE \
@@ -589,6 +601,9 @@ enum mvpp2_tag_type {
#define MVPP2_PRS_TCAM_PROTO_MASK 0xff
#define MVPP2_PRS_TCAM_PROTO_MASK_L 0x3f
#define MVPP2_PRS_DBL_VLANS_MAX 100
+#define MVPP2_PRS_CAST_MASK BIT(0)
+#define MVPP2_PRS_MCAST_VAL BIT(0)
+#define MVPP2_PRS_UCAST_VAL 0x0
/* Tcam structure:
* - lookup ID - 4 bits
@@ -609,35 +624,81 @@ enum mvpp2_tag_type {
#define MVPP2_PRS_TCAM_LU_BYTE 20
#define MVPP2_PRS_TCAM_EN_OFFS(offs) ((offs) + 2)
#define MVPP2_PRS_TCAM_INV_WORD 5
+
+#define MVPP2_PRS_VID_TCAM_BYTE 2
+
+/* TCAM range for unicast and multicast filtering. We have 25 entries per port,
+ * with 4 dedicated to UC filtering and the rest to multicast filtering.
+ * Additionnally we reserve one entry for the broadcast address, and one for
+ * each port's own address.
+ */
+#define MVPP2_PRS_MAC_UC_MC_FILT_MAX 25
+#define MVPP2_PRS_MAC_RANGE_SIZE 80
+
+/* Number of entries per port dedicated to UC and MC filtering */
+#define MVPP2_PRS_MAC_UC_FILT_MAX 4
+#define MVPP2_PRS_MAC_MC_FILT_MAX (MVPP2_PRS_MAC_UC_MC_FILT_MAX - \
+ MVPP2_PRS_MAC_UC_FILT_MAX)
+
+/* There is a TCAM range reserved for VLAN filtering entries, range size is 33
+ * 10 VLAN ID filter entries per port
+ * 1 default VLAN filter entry per port
+ * It is assumed that there are 3 ports for filter, not including loopback port
+ */
+#define MVPP2_PRS_VLAN_FILT_MAX 11
+#define MVPP2_PRS_VLAN_FILT_RANGE_SIZE 33
+
+#define MVPP2_PRS_VLAN_FILT_MAX_ENTRY (MVPP2_PRS_VLAN_FILT_MAX - 2)
+#define MVPP2_PRS_VLAN_FILT_DFLT_ENTRY (MVPP2_PRS_VLAN_FILT_MAX - 1)
+
/* Tcam entries ID */
#define MVPP2_PE_DROP_ALL 0
#define MVPP2_PE_FIRST_FREE_TID 1
-#define MVPP2_PE_LAST_FREE_TID (MVPP2_PRS_TCAM_SRAM_SIZE - 31)
+
+/* MAC filtering range */
+#define MVPP2_PE_MAC_RANGE_END (MVPP2_PE_VID_FILT_RANGE_START - 1)
+#define MVPP2_PE_MAC_RANGE_START (MVPP2_PE_MAC_RANGE_END - \
+ MVPP2_PRS_MAC_RANGE_SIZE + 1)
+/* VLAN filtering range */
+#define MVPP2_PE_VID_FILT_RANGE_END (MVPP2_PRS_TCAM_SRAM_SIZE - 31)
+#define MVPP2_PE_VID_FILT_RANGE_START (MVPP2_PE_VID_FILT_RANGE_END - \
+ MVPP2_PRS_VLAN_FILT_RANGE_SIZE + 1)
+#define MVPP2_PE_LAST_FREE_TID (MVPP2_PE_MAC_RANGE_START - 1)
#define MVPP2_PE_IP6_EXT_PROTO_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 30)
-#define MVPP2_PE_MAC_MC_IP6 (MVPP2_PRS_TCAM_SRAM_SIZE - 29)
-#define MVPP2_PE_IP6_ADDR_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 28)
-#define MVPP2_PE_IP4_ADDR_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 27)
-#define MVPP2_PE_LAST_DEFAULT_FLOW (MVPP2_PRS_TCAM_SRAM_SIZE - 26)
-#define MVPP2_PE_FIRST_DEFAULT_FLOW (MVPP2_PRS_TCAM_SRAM_SIZE - 19)
-#define MVPP2_PE_EDSA_TAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 18)
-#define MVPP2_PE_EDSA_UNTAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 17)
-#define MVPP2_PE_DSA_TAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 16)
-#define MVPP2_PE_DSA_UNTAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 15)
-#define MVPP2_PE_ETYPE_EDSA_TAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 14)
-#define MVPP2_PE_ETYPE_EDSA_UNTAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 13)
-#define MVPP2_PE_ETYPE_DSA_TAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 12)
-#define MVPP2_PE_ETYPE_DSA_UNTAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 11)
-#define MVPP2_PE_MH_DEFAULT (MVPP2_PRS_TCAM_SRAM_SIZE - 10)
-#define MVPP2_PE_DSA_DEFAULT (MVPP2_PRS_TCAM_SRAM_SIZE - 9)
-#define MVPP2_PE_IP6_PROTO_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 8)
-#define MVPP2_PE_IP4_PROTO_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 7)
-#define MVPP2_PE_ETH_TYPE_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 6)
-#define MVPP2_PE_VLAN_DBL (MVPP2_PRS_TCAM_SRAM_SIZE - 5)
-#define MVPP2_PE_VLAN_NONE (MVPP2_PRS_TCAM_SRAM_SIZE - 4)
-#define MVPP2_PE_MAC_MC_ALL (MVPP2_PRS_TCAM_SRAM_SIZE - 3)
-#define MVPP2_PE_MAC_PROMISCUOUS (MVPP2_PRS_TCAM_SRAM_SIZE - 2)
+#define MVPP2_PE_IP6_ADDR_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 29)
+#define MVPP2_PE_IP4_ADDR_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 28)
+#define MVPP2_PE_LAST_DEFAULT_FLOW (MVPP2_PRS_TCAM_SRAM_SIZE - 27)
+#define MVPP2_PE_FIRST_DEFAULT_FLOW (MVPP2_PRS_TCAM_SRAM_SIZE - 22)
+#define MVPP2_PE_EDSA_TAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 21)
+#define MVPP2_PE_EDSA_UNTAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 20)
+#define MVPP2_PE_DSA_TAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 19)
+#define MVPP2_PE_DSA_UNTAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 18)
+#define MVPP2_PE_ETYPE_EDSA_TAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 17)
+#define MVPP2_PE_ETYPE_EDSA_UNTAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 16)
+#define MVPP2_PE_ETYPE_DSA_TAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 15)
+#define MVPP2_PE_ETYPE_DSA_UNTAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 14)
+#define MVPP2_PE_MH_DEFAULT (MVPP2_PRS_TCAM_SRAM_SIZE - 13)
+#define MVPP2_PE_DSA_DEFAULT (MVPP2_PRS_TCAM_SRAM_SIZE - 12)
+#define MVPP2_PE_IP6_PROTO_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 11)
+#define MVPP2_PE_IP4_PROTO_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 10)
+#define MVPP2_PE_ETH_TYPE_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 9)
+#define MVPP2_PE_VID_FLTR_DEFAULT (MVPP2_PRS_TCAM_SRAM_SIZE - 8)
+#define MVPP2_PE_VID_EDSA_FLTR_DEFAULT (MVPP2_PRS_TCAM_SRAM_SIZE - 7)
+#define MVPP2_PE_VLAN_DBL (MVPP2_PRS_TCAM_SRAM_SIZE - 6)
+#define MVPP2_PE_VLAN_NONE (MVPP2_PRS_TCAM_SRAM_SIZE - 5)
+/* reserved */
+#define MVPP2_PE_MAC_MC_PROMISCUOUS (MVPP2_PRS_TCAM_SRAM_SIZE - 3)
+#define MVPP2_PE_MAC_UC_PROMISCUOUS (MVPP2_PRS_TCAM_SRAM_SIZE - 2)
#define MVPP2_PE_MAC_NON_PROMISCUOUS (MVPP2_PRS_TCAM_SRAM_SIZE - 1)
+#define MVPP2_PRS_VID_PORT_FIRST(port) (MVPP2_PE_VID_FILT_RANGE_START + \
+ ((port) * MVPP2_PRS_VLAN_FILT_MAX))
+#define MVPP2_PRS_VID_PORT_LAST(port) (MVPP2_PRS_VID_PORT_FIRST(port) \
+ + MVPP2_PRS_VLAN_FILT_MAX_ENTRY)
+/* Index of default vid filter for given port */
+#define MVPP2_PRS_VID_PORT_DFLT(port) (MVPP2_PRS_VID_PORT_FIRST(port) \
+ + MVPP2_PRS_VLAN_FILT_DFLT_ENTRY)
+
/* Sram structure
* The fields are represented by MVPP2_PRS_TCAM_DATA_REG(3)->(0).
*/
@@ -725,6 +786,7 @@ enum mvpp2_tag_type {
#define MVPP2_PRS_IPV6_EXT_AH_L4_AI_BIT BIT(4)
#define MVPP2_PRS_SINGLE_VLAN_AI 0
#define MVPP2_PRS_DBL_VLAN_AI_BIT BIT(7)
+#define MVPP2_PRS_EDSA_VID_AI_BIT BIT(0)
/* DSA/EDSA type */
#define MVPP2_PRS_TAGGED true
@@ -747,6 +809,7 @@ enum mvpp2_prs_lookup {
MVPP2_PRS_LU_MAC,
MVPP2_PRS_LU_DSA,
MVPP2_PRS_LU_VLAN,
+ MVPP2_PRS_LU_VID,
MVPP2_PRS_LU_L2,
MVPP2_PRS_LU_PPPOE,
MVPP2_PRS_LU_IP4,
@@ -755,6 +818,12 @@ enum mvpp2_prs_lookup {
MVPP2_PRS_LU_LAST,
};
+/* L2 cast enum */
+enum mvpp2_prs_l2_cast {
+ MVPP2_PRS_L2_UNI_CAST,
+ MVPP2_PRS_L2_MULTI_CAST,
+};
+
/* L3 cast enum */
enum mvpp2_prs_l3_cast {
MVPP2_PRS_L3_UNI_CAST,
@@ -772,23 +841,26 @@ enum mvpp2_prs_l3_cast {
#define MVPP22_RSS_TABLE_ENTRIES 32
/* BM constants */
-#define MVPP2_BM_POOLS_NUM 8
+#define MVPP2_BM_JUMBO_BUF_NUM 512
#define MVPP2_BM_LONG_BUF_NUM 1024
#define MVPP2_BM_SHORT_BUF_NUM 2048
#define MVPP2_BM_POOL_SIZE_MAX (16*1024 - MVPP2_BM_POOL_PTR_ALIGN/4)
#define MVPP2_BM_POOL_PTR_ALIGN 128
-#define MVPP2_BM_SWF_LONG_POOL(port) ((port > 2) ? 2 : port)
-#define MVPP2_BM_SWF_SHORT_POOL 3
/* BM cookie (32 bits) definition */
#define MVPP2_BM_COOKIE_POOL_OFFS 8
#define MVPP2_BM_COOKIE_CPU_OFFS 24
+#define MVPP2_BM_SHORT_FRAME_SIZE 512
+#define MVPP2_BM_LONG_FRAME_SIZE 2048
+#define MVPP2_BM_JUMBO_FRAME_SIZE 10240
/* BM short pool packet size
* These value assure that for SWF the total number
* of bytes allocated for each buffer will be 512
*/
-#define MVPP2_BM_SHORT_PKT_SIZE MVPP2_RX_MAX_PKT_SIZE(512)
+#define MVPP2_BM_SHORT_PKT_SIZE MVPP2_RX_MAX_PKT_SIZE(MVPP2_BM_SHORT_FRAME_SIZE)
+#define MVPP2_BM_LONG_PKT_SIZE MVPP2_RX_MAX_PKT_SIZE(MVPP2_BM_LONG_FRAME_SIZE)
+#define MVPP2_BM_JUMBO_PKT_SIZE MVPP2_RX_MAX_PKT_SIZE(MVPP2_BM_JUMBO_FRAME_SIZE)
#define MVPP21_ADDR_SPACE_SZ 0
#define MVPP22_ADDR_SPACE_SZ SZ_64K
@@ -796,12 +868,18 @@ enum mvpp2_prs_l3_cast {
#define MVPP2_MAX_THREADS 8
#define MVPP2_MAX_QVECS MVPP2_MAX_THREADS
-enum mvpp2_bm_type {
- MVPP2_BM_FREE,
- MVPP2_BM_SWF_LONG,
- MVPP2_BM_SWF_SHORT
+enum mvpp2_bm_pool_log_num {
+ MVPP2_BM_SHORT,
+ MVPP2_BM_LONG,
+ MVPP2_BM_JUMBO,
+ MVPP2_BM_POOLS_NUM
};
+static struct {
+ int pkt_size;
+ int buf_num;
+} mvpp2_pools[MVPP2_BM_POOLS_NUM];
+
/* GMAC MIB Counters register definitions */
#define MVPP21_MIB_COUNTERS_OFFSET 0x1000
#define MVPP21_MIB_COUNTERS_PORT_SZ 0x400
@@ -838,6 +916,8 @@ enum mvpp2_bm_type {
#define MVPP2_MIB_COUNTERS_STATS_DELAY (1 * HZ)
+#define MVPP2_DESC_DMA_MASK DMA_BIT_MASK(40)
+
/* Definitions */
/* Shared Packet Processor resources */
@@ -1230,7 +1310,6 @@ struct mvpp2_cls_lookup_entry {
struct mvpp2_bm_pool {
/* Pool number in the range 0-7 */
int id;
- enum mvpp2_bm_type type;
/* Buffer Pointers Pool External (BPPE) size */
int size;
@@ -1282,6 +1361,10 @@ static u32 mvpp2_read(struct mvpp2 *priv, u32 offset)
return readl(priv->swth_base[0] + offset);
}
+static u32 mvpp2_read_relaxed(struct mvpp2 *priv, u32 offset)
+{
+ return readl_relaxed(priv->swth_base[0] + offset);
+}
/* These accessors should be used to access:
*
* - per-CPU registers, where each CPU has its own copy of the
@@ -1330,13 +1413,25 @@ static u32 mvpp2_percpu_read(struct mvpp2 *priv, int cpu,
return readl(priv->swth_base[cpu] + offset);
}
+static void mvpp2_percpu_write_relaxed(struct mvpp2 *priv, int cpu,
+ u32 offset, u32 data)
+{
+ writel_relaxed(data, priv->swth_base[cpu] + offset);
+}
+
+static u32 mvpp2_percpu_read_relaxed(struct mvpp2 *priv, int cpu,
+ u32 offset)
+{
+ return readl_relaxed(priv->swth_base[cpu] + offset);
+}
+
static dma_addr_t mvpp2_txdesc_dma_addr_get(struct mvpp2_port *port,
struct mvpp2_tx_desc *tx_desc)
{
if (port->priv->hw_version == MVPP21)
return tx_desc->pp21.buf_dma_addr;
else
- return tx_desc->pp22.buf_dma_addr_ptp & GENMASK_ULL(40, 0);
+ return tx_desc->pp22.buf_dma_addr_ptp & MVPP2_DESC_DMA_MASK;
}
static void mvpp2_txdesc_dma_addr_set(struct mvpp2_port *port,
@@ -1354,7 +1449,7 @@ static void mvpp2_txdesc_dma_addr_set(struct mvpp2_port *port,
} else {
u64 val = (u64)addr;
- tx_desc->pp22.buf_dma_addr_ptp &= ~GENMASK_ULL(40, 0);
+ tx_desc->pp22.buf_dma_addr_ptp &= ~MVPP2_DESC_DMA_MASK;
tx_desc->pp22.buf_dma_addr_ptp |= val;
tx_desc->pp22.packet_offset = offset;
}
@@ -1414,7 +1509,7 @@ static dma_addr_t mvpp2_rxdesc_dma_addr_get(struct mvpp2_port *port,
if (port->priv->hw_version == MVPP21)
return rx_desc->pp21.buf_dma_addr;
else
- return rx_desc->pp22.buf_dma_addr_key_hash & GENMASK_ULL(40, 0);
+ return rx_desc->pp22.buf_dma_addr_key_hash & MVPP2_DESC_DMA_MASK;
}
static unsigned long mvpp2_rxdesc_cookie_get(struct mvpp2_port *port,
@@ -1423,7 +1518,7 @@ static unsigned long mvpp2_rxdesc_cookie_get(struct mvpp2_port *port,
if (port->priv->hw_version == MVPP21)
return rx_desc->pp21.buf_cookie;
else
- return rx_desc->pp22.buf_cookie_misc & GENMASK_ULL(40, 0);
+ return rx_desc->pp22.buf_cookie_misc & MVPP2_DESC_DMA_MASK;
}
static size_t mvpp2_rxdesc_size_get(struct mvpp2_port *port,
@@ -1505,14 +1600,18 @@ static int mvpp2_prs_hw_write(struct mvpp2 *priv, struct mvpp2_prs_entry *pe)
return 0;
}
-/* Read tcam entry from hw */
-static int mvpp2_prs_hw_read(struct mvpp2 *priv, struct mvpp2_prs_entry *pe)
+/* Initialize tcam entry from hw */
+static int mvpp2_prs_init_from_hw(struct mvpp2 *priv,
+ struct mvpp2_prs_entry *pe, int tid)
{
int i;
- if (pe->index > MVPP2_PRS_TCAM_SRAM_SIZE - 1)
+ if (tid > MVPP2_PRS_TCAM_SRAM_SIZE - 1)
return -EINVAL;
+ memset(pe, 0, sizeof(*pe));
+ pe->index = tid;
+
/* Write tcam index - indirect access */
mvpp2_write(priv, MVPP2_PRS_TCAM_IDX_REG, pe->index);
@@ -1662,6 +1761,14 @@ static void mvpp2_prs_match_etype(struct mvpp2_prs_entry *pe, int offset,
mvpp2_prs_tcam_data_byte_set(pe, offset + 1, ethertype & 0xff, 0xff);
}
+/* Set vid in tcam sw entry */
+static void mvpp2_prs_match_vid(struct mvpp2_prs_entry *pe, int offset,
+ unsigned short vid)
+{
+ mvpp2_prs_tcam_data_byte_set(pe, offset + 0, (vid & 0xf00) >> 8, 0xf);
+ mvpp2_prs_tcam_data_byte_set(pe, offset + 1, vid & 0xff, 0xff);
+}
+
/* Set bits in sram sw entry */
static void mvpp2_prs_sram_bits_set(struct mvpp2_prs_entry *pe, int bit_num,
int val)
@@ -1828,16 +1935,11 @@ static void mvpp2_prs_sram_offset_set(struct mvpp2_prs_entry *pe,
}
/* Find parser flow entry */
-static struct mvpp2_prs_entry *mvpp2_prs_flow_find(struct mvpp2 *priv, int flow)
+static int mvpp2_prs_flow_find(struct mvpp2 *priv, int flow)
{
- struct mvpp2_prs_entry *pe;
+ struct mvpp2_prs_entry pe;
int tid;
- pe = kzalloc(sizeof(*pe), GFP_KERNEL);
- if (!pe)
- return NULL;
- mvpp2_prs_tcam_lu_set(pe, MVPP2_PRS_LU_FLOWS);
-
/* Go through the all entires with MVPP2_PRS_LU_FLOWS */
for (tid = MVPP2_PRS_TCAM_SRAM_SIZE - 1; tid >= 0; tid--) {
u8 bits;
@@ -1846,17 +1948,15 @@ static struct mvpp2_prs_entry *mvpp2_prs_flow_find(struct mvpp2 *priv, int flow)
priv->prs_shadow[tid].lu != MVPP2_PRS_LU_FLOWS)
continue;
- pe->index = tid;
- mvpp2_prs_hw_read(priv, pe);
- bits = mvpp2_prs_sram_ai_get(pe);
+ mvpp2_prs_init_from_hw(priv, &pe, tid);
+ bits = mvpp2_prs_sram_ai_get(&pe);
/* Sram store classification lookup ID in AI bits [5:0] */
if ((bits & MVPP2_PRS_FLOW_ID_MASK) == flow)
- return pe;
+ return tid;
}
- kfree(pe);
- return NULL;
+ return -ENOENT;
}
/* Return first free tcam index, seeking from start to end */
@@ -1886,8 +1986,7 @@ static void mvpp2_prs_mac_drop_all_set(struct mvpp2 *priv, int port, bool add)
if (priv->prs_shadow[MVPP2_PE_DROP_ALL].valid) {
/* Entry exist - update port only */
- pe.index = MVPP2_PE_DROP_ALL;
- mvpp2_prs_hw_read(priv, &pe);
+ mvpp2_prs_init_from_hw(priv, &pe, MVPP2_PE_DROP_ALL);
} else {
/* Entry doesn't exist - create new */
memset(&pe, 0, sizeof(pe));
@@ -1914,78 +2013,42 @@ static void mvpp2_prs_mac_drop_all_set(struct mvpp2 *priv, int port, bool add)
mvpp2_prs_hw_write(priv, &pe);
}
-/* Set port to promiscuous mode */
-static void mvpp2_prs_mac_promisc_set(struct mvpp2 *priv, int port, bool add)
+/* Set port to unicast or multicast promiscuous mode */
+static void mvpp2_prs_mac_promisc_set(struct mvpp2 *priv, int port,
+ enum mvpp2_prs_l2_cast l2_cast, bool add)
{
struct mvpp2_prs_entry pe;
+ unsigned char cast_match;
+ unsigned int ri;
+ int tid;
- /* Promiscuous mode - Accept unknown packets */
-
- if (priv->prs_shadow[MVPP2_PE_MAC_PROMISCUOUS].valid) {
- /* Entry exist - update port only */
- pe.index = MVPP2_PE_MAC_PROMISCUOUS;
- mvpp2_prs_hw_read(priv, &pe);
+ if (l2_cast == MVPP2_PRS_L2_UNI_CAST) {
+ cast_match = MVPP2_PRS_UCAST_VAL;
+ tid = MVPP2_PE_MAC_UC_PROMISCUOUS;
+ ri = MVPP2_PRS_RI_L2_UCAST;
} else {
- /* Entry doesn't exist - create new */
- memset(&pe, 0, sizeof(pe));
- mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_MAC);
- pe.index = MVPP2_PE_MAC_PROMISCUOUS;
-
- /* Continue - set next lookup */
- mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_DSA);
-
- /* Set result info bits */
- mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L2_UCAST,
- MVPP2_PRS_RI_L2_CAST_MASK);
-
- /* Shift to ethertype */
- mvpp2_prs_sram_shift_set(&pe, 2 * ETH_ALEN,
- MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD);
-
- /* Mask all ports */
- mvpp2_prs_tcam_port_map_set(&pe, 0);
-
- /* Update shadow table */
- mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_MAC);
+ cast_match = MVPP2_PRS_MCAST_VAL;
+ tid = MVPP2_PE_MAC_MC_PROMISCUOUS;
+ ri = MVPP2_PRS_RI_L2_MCAST;
}
- /* Update port mask */
- mvpp2_prs_tcam_port_set(&pe, port, add);
-
- mvpp2_prs_hw_write(priv, &pe);
-}
-
-/* Accept multicast */
-static void mvpp2_prs_mac_multi_set(struct mvpp2 *priv, int port, int index,
- bool add)
-{
- struct mvpp2_prs_entry pe;
- unsigned char da_mc;
-
- /* Ethernet multicast address first byte is
- * 0x01 for IPv4 and 0x33 for IPv6
- */
- da_mc = (index == MVPP2_PE_MAC_MC_ALL) ? 0x01 : 0x33;
-
- if (priv->prs_shadow[index].valid) {
- /* Entry exist - update port only */
- pe.index = index;
- mvpp2_prs_hw_read(priv, &pe);
+ /* promiscuous mode - Accept unknown unicast or multicast packets */
+ if (priv->prs_shadow[tid].valid) {
+ mvpp2_prs_init_from_hw(priv, &pe, tid);
} else {
- /* Entry doesn't exist - create new */
memset(&pe, 0, sizeof(pe));
mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_MAC);
- pe.index = index;
+ pe.index = tid;
/* Continue - set next lookup */
mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_DSA);
/* Set result info bits */
- mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L2_MCAST,
- MVPP2_PRS_RI_L2_CAST_MASK);
+ mvpp2_prs_sram_ri_update(&pe, ri, MVPP2_PRS_RI_L2_CAST_MASK);
- /* Update tcam entry data first byte */
- mvpp2_prs_tcam_data_byte_set(&pe, 0, da_mc, 0xff);
+ /* Match UC or MC addresses */
+ mvpp2_prs_tcam_data_byte_set(&pe, 0, cast_match,
+ MVPP2_PRS_CAST_MASK);
/* Shift to ethertype */
mvpp2_prs_sram_shift_set(&pe, 2 * ETH_ALEN,
@@ -2021,32 +2084,37 @@ static void mvpp2_prs_dsa_tag_set(struct mvpp2 *priv, int port, bool add,
if (priv->prs_shadow[tid].valid) {
/* Entry exist - update port only */
- pe.index = tid;
- mvpp2_prs_hw_read(priv, &pe);
+ mvpp2_prs_init_from_hw(priv, &pe, tid);
} else {
/* Entry doesn't exist - create new */
memset(&pe, 0, sizeof(pe));
mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_DSA);
pe.index = tid;
- /* Shift 4 bytes if DSA tag or 8 bytes in case of EDSA tag*/
- mvpp2_prs_sram_shift_set(&pe, shift,
- MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD);
-
/* Update shadow table */
mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_DSA);
if (tagged) {
/* Set tagged bit in DSA tag */
mvpp2_prs_tcam_data_byte_set(&pe, 0,
- MVPP2_PRS_TCAM_DSA_TAGGED_BIT,
- MVPP2_PRS_TCAM_DSA_TAGGED_BIT);
- /* Clear all ai bits for next iteration */
- mvpp2_prs_sram_ai_update(&pe, 0,
- MVPP2_PRS_SRAM_AI_MASK);
- /* If packet is tagged continue check vlans */
- mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_VLAN);
+ MVPP2_PRS_TCAM_DSA_TAGGED_BIT,
+ MVPP2_PRS_TCAM_DSA_TAGGED_BIT);
+
+ /* Set ai bits for next iteration */
+ if (extend)
+ mvpp2_prs_sram_ai_update(&pe, 1,
+ MVPP2_PRS_SRAM_AI_MASK);
+ else
+ mvpp2_prs_sram_ai_update(&pe, 0,
+ MVPP2_PRS_SRAM_AI_MASK);
+
+ /* If packet is tagged continue check vid filtering */
+ mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_VID);
} else {
+ /* Shift 4 bytes for DSA tag or 8 bytes for EDSA tag*/
+ mvpp2_prs_sram_shift_set(&pe, shift,
+ MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD);
+
/* Set result info bits to 'no vlans' */
mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_VLAN_NONE,
MVPP2_PRS_RI_VLAN_MASK);
@@ -2084,8 +2152,7 @@ static void mvpp2_prs_dsa_tag_ethertype_set(struct mvpp2 *priv, int port,
if (priv->prs_shadow[tid].valid) {
/* Entry exist - update port only */
- pe.index = tid;
- mvpp2_prs_hw_read(priv, &pe);
+ mvpp2_prs_init_from_hw(priv, &pe, tid);
} else {
/* Entry doesn't exist - create new */
memset(&pe, 0, sizeof(pe));
@@ -2133,17 +2200,11 @@ static void mvpp2_prs_dsa_tag_ethertype_set(struct mvpp2 *priv, int port,
}
/* Search for existing single/triple vlan entry */
-static struct mvpp2_prs_entry *mvpp2_prs_vlan_find(struct mvpp2 *priv,
- unsigned short tpid, int ai)
+static int mvpp2_prs_vlan_find(struct mvpp2 *priv, unsigned short tpid, int ai)
{
- struct mvpp2_prs_entry *pe;
+ struct mvpp2_prs_entry pe;
int tid;
- pe = kzalloc(sizeof(*pe), GFP_KERNEL);
- if (!pe)
- return NULL;
- mvpp2_prs_tcam_lu_set(pe, MVPP2_PRS_LU_VLAN);
-
/* Go through the all entries with MVPP2_PRS_LU_VLAN */
for (tid = MVPP2_PE_FIRST_FREE_TID;
tid <= MVPP2_PE_LAST_FREE_TID; tid++) {
@@ -2154,19 +2215,17 @@ static struct mvpp2_prs_entry *mvpp2_prs_vlan_find(struct mvpp2 *priv,
priv->prs_shadow[tid].lu != MVPP2_PRS_LU_VLAN)
continue;
- pe->index = tid;
-
- mvpp2_prs_hw_read(priv, pe);
- match = mvpp2_prs_tcam_data_cmp(pe, 0, swab16(tpid));
+ mvpp2_prs_init_from_hw(priv, &pe, tid);
+ match = mvpp2_prs_tcam_data_cmp(&pe, 0, swab16(tpid));
if (!match)
continue;
/* Get vlan type */
- ri_bits = mvpp2_prs_sram_ri_get(pe);
+ ri_bits = mvpp2_prs_sram_ri_get(&pe);
ri_bits &= MVPP2_PRS_RI_VLAN_MASK;
/* Get current ai value from tcam */
- ai_bits = mvpp2_prs_tcam_ai_get(pe);
+ ai_bits = mvpp2_prs_tcam_ai_get(&pe);
/* Clear double vlan bit */
ai_bits &= ~MVPP2_PRS_DBL_VLAN_AI_BIT;
@@ -2175,34 +2234,31 @@ static struct mvpp2_prs_entry *mvpp2_prs_vlan_find(struct mvpp2 *priv,
if (ri_bits == MVPP2_PRS_RI_VLAN_SINGLE ||
ri_bits == MVPP2_PRS_RI_VLAN_TRIPLE)
- return pe;
+ return tid;
}
- kfree(pe);
- return NULL;
+ return -ENOENT;
}
/* Add/update single/triple vlan entry */
static int mvpp2_prs_vlan_add(struct mvpp2 *priv, unsigned short tpid, int ai,
unsigned int port_map)
{
- struct mvpp2_prs_entry *pe;
+ struct mvpp2_prs_entry pe;
int tid_aux, tid;
int ret = 0;
- pe = mvpp2_prs_vlan_find(priv, tpid, ai);
+ memset(&pe, 0, sizeof(pe));
+
+ tid = mvpp2_prs_vlan_find(priv, tpid, ai);
- if (!pe) {
+ if (tid < 0) {
/* Create new tcam entry */
tid = mvpp2_prs_tcam_first_free(priv, MVPP2_PE_LAST_FREE_TID,
MVPP2_PE_FIRST_FREE_TID);
if (tid < 0)
return tid;
- pe = kzalloc(sizeof(*pe), GFP_KERNEL);
- if (!pe)
- return -ENOMEM;
-
/* Get last double vlan tid */
for (tid_aux = MVPP2_PE_LAST_FREE_TID;
tid_aux >= MVPP2_PE_FIRST_FREE_TID; tid_aux--) {
@@ -2212,50 +2268,46 @@ static int mvpp2_prs_vlan_add(struct mvpp2 *priv, unsigned short tpid, int ai,
priv->prs_shadow[tid_aux].lu != MVPP2_PRS_LU_VLAN)
continue;
- pe->index = tid_aux;
- mvpp2_prs_hw_read(priv, pe);
- ri_bits = mvpp2_prs_sram_ri_get(pe);
+ mvpp2_prs_init_from_hw(priv, &pe, tid_aux);
+ ri_bits = mvpp2_prs_sram_ri_get(&pe);
if ((ri_bits & MVPP2_PRS_RI_VLAN_MASK) ==
MVPP2_PRS_RI_VLAN_DOUBLE)
break;
}
- if (tid <= tid_aux) {
- ret = -EINVAL;
- goto free_pe;
- }
+ if (tid <= tid_aux)
+ return -EINVAL;
+
+ memset(&pe, 0, sizeof(pe));
+ pe.index = tid;
+ mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_VLAN);
- memset(pe, 0, sizeof(*pe));
- mvpp2_prs_tcam_lu_set(pe, MVPP2_PRS_LU_VLAN);
- pe->index = tid;
+ mvpp2_prs_match_etype(&pe, 0, tpid);
- mvpp2_prs_match_etype(pe, 0, tpid);
+ /* VLAN tag detected, proceed with VID filtering */
+ mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_VID);
- mvpp2_prs_sram_next_lu_set(pe, MVPP2_PRS_LU_L2);
- /* Shift 4 bytes - skip 1 vlan tag */
- mvpp2_prs_sram_shift_set(pe, MVPP2_VLAN_TAG_LEN,
- MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD);
/* Clear all ai bits for next iteration */
- mvpp2_prs_sram_ai_update(pe, 0, MVPP2_PRS_SRAM_AI_MASK);
+ mvpp2_prs_sram_ai_update(&pe, 0, MVPP2_PRS_SRAM_AI_MASK);
if (ai == MVPP2_PRS_SINGLE_VLAN_AI) {
- mvpp2_prs_sram_ri_update(pe, MVPP2_PRS_RI_VLAN_SINGLE,
+ mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_VLAN_SINGLE,
MVPP2_PRS_RI_VLAN_MASK);
} else {
ai |= MVPP2_PRS_DBL_VLAN_AI_BIT;
- mvpp2_prs_sram_ri_update(pe, MVPP2_PRS_RI_VLAN_TRIPLE,
+ mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_VLAN_TRIPLE,
MVPP2_PRS_RI_VLAN_MASK);
}
- mvpp2_prs_tcam_ai_update(pe, ai, MVPP2_PRS_SRAM_AI_MASK);
+ mvpp2_prs_tcam_ai_update(&pe, ai, MVPP2_PRS_SRAM_AI_MASK);
- mvpp2_prs_shadow_set(priv, pe->index, MVPP2_PRS_LU_VLAN);
+ mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_VLAN);
+ } else {
+ mvpp2_prs_init_from_hw(priv, &pe, tid);
}
/* Update ports' mask */
- mvpp2_prs_tcam_port_map_set(pe, port_map);
+ mvpp2_prs_tcam_port_map_set(&pe, port_map);
- mvpp2_prs_hw_write(priv, pe);
-free_pe:
- kfree(pe);
+ mvpp2_prs_hw_write(priv, &pe);
return ret;
}
@@ -2274,18 +2326,12 @@ static int mvpp2_prs_double_vlan_ai_free_get(struct mvpp2 *priv)
}
/* Search for existing double vlan entry */
-static struct mvpp2_prs_entry *mvpp2_prs_double_vlan_find(struct mvpp2 *priv,
- unsigned short tpid1,
- unsigned short tpid2)
+static int mvpp2_prs_double_vlan_find(struct mvpp2 *priv, unsigned short tpid1,
+ unsigned short tpid2)
{
- struct mvpp2_prs_entry *pe;
+ struct mvpp2_prs_entry pe;
int tid;
- pe = kzalloc(sizeof(*pe), GFP_KERNEL);
- if (!pe)
- return NULL;
- mvpp2_prs_tcam_lu_set(pe, MVPP2_PRS_LU_VLAN);
-
/* Go through the all entries with MVPP2_PRS_LU_VLAN */
for (tid = MVPP2_PE_FIRST_FREE_TID;
tid <= MVPP2_PE_LAST_FREE_TID; tid++) {
@@ -2296,22 +2342,20 @@ static struct mvpp2_prs_entry *mvpp2_prs_double_vlan_find(struct mvpp2 *priv,
priv->prs_shadow[tid].lu != MVPP2_PRS_LU_VLAN)
continue;
- pe->index = tid;
- mvpp2_prs_hw_read(priv, pe);
+ mvpp2_prs_init_from_hw(priv, &pe, tid);
- match = mvpp2_prs_tcam_data_cmp(pe, 0, swab16(tpid1))
- && mvpp2_prs_tcam_data_cmp(pe, 4, swab16(tpid2));
+ match = mvpp2_prs_tcam_data_cmp(&pe, 0, swab16(tpid1)) &&
+ mvpp2_prs_tcam_data_cmp(&pe, 4, swab16(tpid2));
if (!match)
continue;
- ri_mask = mvpp2_prs_sram_ri_get(pe) & MVPP2_PRS_RI_VLAN_MASK;
+ ri_mask = mvpp2_prs_sram_ri_get(&pe) & MVPP2_PRS_RI_VLAN_MASK;
if (ri_mask == MVPP2_PRS_RI_VLAN_DOUBLE)
- return pe;
+ return tid;
}
- kfree(pe);
- return NULL;
+ return -ENOENT;
}
/* Add or update double vlan entry */
@@ -2319,28 +2363,24 @@ static int mvpp2_prs_double_vlan_add(struct mvpp2 *priv, unsigned short tpid1,
unsigned short tpid2,
unsigned int port_map)
{
- struct mvpp2_prs_entry *pe;
int tid_aux, tid, ai, ret = 0;
+ struct mvpp2_prs_entry pe;
- pe = mvpp2_prs_double_vlan_find(priv, tpid1, tpid2);
+ memset(&pe, 0, sizeof(pe));
+
+ tid = mvpp2_prs_double_vlan_find(priv, tpid1, tpid2);
- if (!pe) {
+ if (tid < 0) {
/* Create new tcam entry */
tid = mvpp2_prs_tcam_first_free(priv, MVPP2_PE_FIRST_FREE_TID,
MVPP2_PE_LAST_FREE_TID);
if (tid < 0)
return tid;
- pe = kzalloc(sizeof(*pe), GFP_KERNEL);
- if (!pe)
- return -ENOMEM;
-
/* Set ai value for new double vlan entry */
ai = mvpp2_prs_double_vlan_ai_free_get(priv);
- if (ai < 0) {
- ret = ai;
- goto free_pe;
- }
+ if (ai < 0)
+ return ai;
/* Get first single/triple vlan tid */
for (tid_aux = MVPP2_PE_FIRST_FREE_TID;
@@ -2351,46 +2391,44 @@ static int mvpp2_prs_double_vlan_add(struct mvpp2 *priv, unsigned short tpid1,
priv->prs_shadow[tid_aux].lu != MVPP2_PRS_LU_VLAN)
continue;
- pe->index = tid_aux;
- mvpp2_prs_hw_read(priv, pe);
- ri_bits = mvpp2_prs_sram_ri_get(pe);
+ mvpp2_prs_init_from_hw(priv, &pe, tid_aux);
+ ri_bits = mvpp2_prs_sram_ri_get(&pe);
ri_bits &= MVPP2_PRS_RI_VLAN_MASK;
if (ri_bits == MVPP2_PRS_RI_VLAN_SINGLE ||
ri_bits == MVPP2_PRS_RI_VLAN_TRIPLE)
break;
}
- if (tid >= tid_aux) {
- ret = -ERANGE;
- goto free_pe;
- }
+ if (tid >= tid_aux)
+ return -ERANGE;
- memset(pe, 0, sizeof(*pe));
- mvpp2_prs_tcam_lu_set(pe, MVPP2_PRS_LU_VLAN);
- pe->index = tid;
+ memset(&pe, 0, sizeof(pe));
+ mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_VLAN);
+ pe.index = tid;
priv->prs_double_vlans[ai] = true;
- mvpp2_prs_match_etype(pe, 0, tpid1);
- mvpp2_prs_match_etype(pe, 4, tpid2);
+ mvpp2_prs_match_etype(&pe, 0, tpid1);
+ mvpp2_prs_match_etype(&pe, 4, tpid2);
- mvpp2_prs_sram_next_lu_set(pe, MVPP2_PRS_LU_VLAN);
- /* Shift 8 bytes - skip 2 vlan tags */
- mvpp2_prs_sram_shift_set(pe, 2 * MVPP2_VLAN_TAG_LEN,
+ mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_VLAN);
+ /* Shift 4 bytes - skip outer vlan tag */
+ mvpp2_prs_sram_shift_set(&pe, MVPP2_VLAN_TAG_LEN,
MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD);
- mvpp2_prs_sram_ri_update(pe, MVPP2_PRS_RI_VLAN_DOUBLE,
+ mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_VLAN_DOUBLE,
MVPP2_PRS_RI_VLAN_MASK);
- mvpp2_prs_sram_ai_update(pe, ai | MVPP2_PRS_DBL_VLAN_AI_BIT,
+ mvpp2_prs_sram_ai_update(&pe, ai | MVPP2_PRS_DBL_VLAN_AI_BIT,
MVPP2_PRS_SRAM_AI_MASK);
- mvpp2_prs_shadow_set(priv, pe->index, MVPP2_PRS_LU_VLAN);
+ mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_VLAN);
+ } else {
+ mvpp2_prs_init_from_hw(priv, &pe, tid);
}
/* Update ports' mask */
- mvpp2_prs_tcam_port_map_set(pe, port_map);
- mvpp2_prs_hw_write(priv, pe);
-free_pe:
- kfree(pe);
+ mvpp2_prs_tcam_port_map_set(&pe, port_map);
+ mvpp2_prs_hw_write(priv, &pe);
+
return ret;
}
@@ -2694,11 +2732,10 @@ static void mvpp2_prs_mac_init(struct mvpp2 *priv)
mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_MAC);
mvpp2_prs_hw_write(priv, &pe);
- /* place holders only - no ports */
+ /* Create dummy entries for drop all and promiscuous modes */
mvpp2_prs_mac_drop_all_set(priv, 0, false);
- mvpp2_prs_mac_promisc_set(priv, 0, false);
- mvpp2_prs_mac_multi_set(priv, 0, MVPP2_PE_MAC_MC_ALL, false);
- mvpp2_prs_mac_multi_set(priv, 0, MVPP2_PE_MAC_MC_IP6, false);
+ mvpp2_prs_mac_promisc_set(priv, 0, MVPP2_PRS_L2_UNI_CAST, false);
+ mvpp2_prs_mac_promisc_set(priv, 0, MVPP2_PRS_L2_MULTI_CAST, false);
}
/* Set default entries for various types of dsa packets */
@@ -2755,6 +2792,62 @@ static void mvpp2_prs_dsa_init(struct mvpp2 *priv)
mvpp2_prs_hw_write(priv, &pe);
}
+/* Initialize parser entries for VID filtering */
+static void mvpp2_prs_vid_init(struct mvpp2 *priv)
+{
+ struct mvpp2_prs_entry pe;
+
+ memset(&pe, 0, sizeof(pe));
+
+ /* Set default vid entry */
+ pe.index = MVPP2_PE_VID_FLTR_DEFAULT;
+ mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_VID);
+
+ mvpp2_prs_tcam_ai_update(&pe, 0, MVPP2_PRS_EDSA_VID_AI_BIT);
+
+ /* Skip VLAN header - Set offset to 4 bytes */
+ mvpp2_prs_sram_shift_set(&pe, MVPP2_VLAN_TAG_LEN,
+ MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD);
+
+ /* Clear all ai bits for next iteration */
+ mvpp2_prs_sram_ai_update(&pe, 0, MVPP2_PRS_SRAM_AI_MASK);
+
+ mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_L2);
+
+ /* Unmask all ports */
+ mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK);
+
+ /* Update shadow table and hw entry */
+ mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_VID);
+ mvpp2_prs_hw_write(priv, &pe);
+
+ /* Set default vid entry for extended DSA*/
+ memset(&pe, 0, sizeof(pe));
+
+ /* Set default vid entry */
+ pe.index = MVPP2_PE_VID_EDSA_FLTR_DEFAULT;
+ mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_VID);
+
+ mvpp2_prs_tcam_ai_update(&pe, MVPP2_PRS_EDSA_VID_AI_BIT,
+ MVPP2_PRS_EDSA_VID_AI_BIT);
+
+ /* Skip VLAN header - Set offset to 8 bytes */
+ mvpp2_prs_sram_shift_set(&pe, MVPP2_VLAN_TAG_EDSA_LEN,
+ MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD);
+
+ /* Clear all ai bits for next iteration */
+ mvpp2_prs_sram_ai_update(&pe, 0, MVPP2_PRS_SRAM_AI_MASK);
+
+ mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_L2);
+
+ /* Unmask all ports */
+ mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK);
+
+ /* Update shadow table and hw entry */
+ mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_VID);
+ mvpp2_prs_hw_write(priv, &pe);
+}
+
/* Match basic ethertypes */
static int mvpp2_prs_etype_init(struct mvpp2 *priv)
{
@@ -3023,7 +3116,8 @@ static int mvpp2_prs_vlan_init(struct platform_device *pdev, struct mvpp2 *priv)
mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_VLAN);
pe.index = MVPP2_PE_VLAN_DBL;
- mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_L2);
+ mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_VID);
+
/* Clear ai for next iterations */
mvpp2_prs_sram_ai_update(&pe, 0, MVPP2_PRS_SRAM_AI_MASK);
mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_VLAN_DOUBLE,
@@ -3386,6 +3480,192 @@ static int mvpp2_prs_ip6_init(struct mvpp2 *priv)
return 0;
}
+/* Find tcam entry with matched pair <vid,port> */
+static int mvpp2_prs_vid_range_find(struct mvpp2 *priv, int pmap, u16 vid,
+ u16 mask)
+{
+ unsigned char byte[2], enable[2];
+ struct mvpp2_prs_entry pe;
+ u16 rvid, rmask;
+ int tid;
+
+ /* Go through the all entries with MVPP2_PRS_LU_VID */
+ for (tid = MVPP2_PE_VID_FILT_RANGE_START;
+ tid <= MVPP2_PE_VID_FILT_RANGE_END; tid++) {
+ if (!priv->prs_shadow[tid].valid ||
+ priv->prs_shadow[tid].lu != MVPP2_PRS_LU_VID)
+ continue;
+
+ mvpp2_prs_init_from_hw(priv, &pe, tid);
+
+ mvpp2_prs_tcam_data_byte_get(&pe, 2, &byte[0], &enable[0]);
+ mvpp2_prs_tcam_data_byte_get(&pe, 3, &byte[1], &enable[1]);
+
+ rvid = ((byte[0] & 0xf) << 8) + byte[1];
+ rmask = ((enable[0] & 0xf) << 8) + enable[1];
+
+ if (rvid != vid || rmask != mask)
+ continue;
+
+ return tid;
+ }
+
+ return -ENOENT;
+}
+
+/* Write parser entry for VID filtering */
+static int mvpp2_prs_vid_entry_add(struct mvpp2_port *port, u16 vid)
+{
+ unsigned int vid_start = MVPP2_PE_VID_FILT_RANGE_START +
+ port->id * MVPP2_PRS_VLAN_FILT_MAX;
+ unsigned int mask = 0xfff, reg_val, shift;
+ struct mvpp2 *priv = port->priv;
+ struct mvpp2_prs_entry pe;
+ int tid;
+
+ memset(&pe, 0, sizeof(pe));
+
+ /* Scan TCAM and see if entry with this <vid,port> already exist */
+ tid = mvpp2_prs_vid_range_find(priv, (1 << port->id), vid, mask);
+
+ reg_val = mvpp2_read(priv, MVPP2_MH_REG(port->id));
+ if (reg_val & MVPP2_DSA_EXTENDED)
+ shift = MVPP2_VLAN_TAG_EDSA_LEN;
+ else
+ shift = MVPP2_VLAN_TAG_LEN;
+
+ /* No such entry */
+ if (tid < 0) {
+
+ /* Go through all entries from first to last in vlan range */
+ tid = mvpp2_prs_tcam_first_free(priv, vid_start,
+ vid_start +
+ MVPP2_PRS_VLAN_FILT_MAX_ENTRY);
+
+ /* There isn't room for a new VID filter */
+ if (tid < 0)
+ return tid;
+
+ mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_VID);
+ pe.index = tid;
+
+ /* Mask all ports */
+ mvpp2_prs_tcam_port_map_set(&pe, 0);
+ } else {
+ mvpp2_prs_init_from_hw(priv, &pe, tid);
+ }
+
+ /* Enable the current port */
+ mvpp2_prs_tcam_port_set(&pe, port->id, true);
+
+ /* Continue - set next lookup */
+ mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_L2);
+
+ /* Skip VLAN header - Set offset to 4 or 8 bytes */
+ mvpp2_prs_sram_shift_set(&pe, shift, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD);
+
+ /* Set match on VID */
+ mvpp2_prs_match_vid(&pe, MVPP2_PRS_VID_TCAM_BYTE, vid);
+
+ /* Clear all ai bits for next iteration */
+ mvpp2_prs_sram_ai_update(&pe, 0, MVPP2_PRS_SRAM_AI_MASK);
+
+ /* Update shadow table */
+ mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_VID);
+ mvpp2_prs_hw_write(priv, &pe);
+
+ return 0;
+}
+
+/* Write parser entry for VID filtering */
+static void mvpp2_prs_vid_entry_remove(struct mvpp2_port *port, u16 vid)
+{
+ struct mvpp2 *priv = port->priv;
+ int tid;
+
+ /* Scan TCAM and see if entry with this <vid,port> already exist */
+ tid = mvpp2_prs_vid_range_find(priv, (1 << port->id), vid, 0xfff);
+
+ /* No such entry */
+ if (tid < 0)
+ return;
+
+ mvpp2_prs_hw_inv(priv, tid);
+ priv->prs_shadow[tid].valid = false;
+}
+
+/* Remove all existing VID filters on this port */
+static void mvpp2_prs_vid_remove_all(struct mvpp2_port *port)
+{
+ struct mvpp2 *priv = port->priv;
+ int tid;
+
+ for (tid = MVPP2_PRS_VID_PORT_FIRST(port->id);
+ tid <= MVPP2_PRS_VID_PORT_LAST(port->id); tid++) {
+ if (priv->prs_shadow[tid].valid)
+ mvpp2_prs_vid_entry_remove(port, tid);
+ }
+}
+
+/* Remove VID filering entry for this port */
+static void mvpp2_prs_vid_disable_filtering(struct mvpp2_port *port)
+{
+ unsigned int tid = MVPP2_PRS_VID_PORT_DFLT(port->id);
+ struct mvpp2 *priv = port->priv;
+
+ /* Invalidate the guard entry */
+ mvpp2_prs_hw_inv(priv, tid);
+
+ priv->prs_shadow[tid].valid = false;
+}
+
+/* Add guard entry that drops packets when no VID is matched on this port */
+static void mvpp2_prs_vid_enable_filtering(struct mvpp2_port *port)
+{
+ unsigned int tid = MVPP2_PRS_VID_PORT_DFLT(port->id);
+ struct mvpp2 *priv = port->priv;
+ unsigned int reg_val, shift;
+ struct mvpp2_prs_entry pe;
+
+ if (priv->prs_shadow[tid].valid)
+ return;
+
+ memset(&pe, 0, sizeof(pe));
+
+ pe.index = tid;
+
+ reg_val = mvpp2_read(priv, MVPP2_MH_REG(port->id));
+ if (reg_val & MVPP2_DSA_EXTENDED)
+ shift = MVPP2_VLAN_TAG_EDSA_LEN;
+ else
+ shift = MVPP2_VLAN_TAG_LEN;
+
+ mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_VID);
+
+ /* Mask all ports */
+ mvpp2_prs_tcam_port_map_set(&pe, 0);
+
+ /* Update port mask */
+ mvpp2_prs_tcam_port_set(&pe, port->id, true);
+
+ /* Continue - set next lookup */
+ mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_L2);
+
+ /* Skip VLAN header - Set offset to 4 or 8 bytes */
+ mvpp2_prs_sram_shift_set(&pe, shift, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD);
+
+ /* Drop VLAN packets that don't belong to any VIDs on this port */
+ mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_DROP_MASK,
+ MVPP2_PRS_RI_DROP_MASK);
+
+ /* Clear all ai bits for next iteration */
+ mvpp2_prs_sram_ai_update(&pe, 0, MVPP2_PRS_SRAM_AI_MASK);
+
+ /* Update shadow table */
+ mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_VID);
+ mvpp2_prs_hw_write(priv, &pe);
+}
+
/* Parser default initialization */
static int mvpp2_prs_default_init(struct platform_device *pdev,
struct mvpp2 *priv)
@@ -3429,6 +3709,8 @@ static int mvpp2_prs_default_init(struct platform_device *pdev,
mvpp2_prs_dsa_init(priv);
+ mvpp2_prs_vid_init(priv);
+
err = mvpp2_prs_etype_init(priv);
if (err)
return err;
@@ -3472,21 +3754,16 @@ static bool mvpp2_prs_mac_range_equals(struct mvpp2_prs_entry *pe,
}
/* Find tcam entry with matched pair <MAC DA, port> */
-static struct mvpp2_prs_entry *
+static int
mvpp2_prs_mac_da_range_find(struct mvpp2 *priv, int pmap, const u8 *da,
unsigned char *mask, int udf_type)
{
- struct mvpp2_prs_entry *pe;
+ struct mvpp2_prs_entry pe;
int tid;
- pe = kzalloc(sizeof(*pe), GFP_ATOMIC);
- if (!pe)
- return NULL;
- mvpp2_prs_tcam_lu_set(pe, MVPP2_PRS_LU_MAC);
-
/* Go through the all entires with MVPP2_PRS_LU_MAC */
- for (tid = MVPP2_PE_FIRST_FREE_TID;
- tid <= MVPP2_PE_LAST_FREE_TID; tid++) {
+ for (tid = MVPP2_PE_MAC_RANGE_START;
+ tid <= MVPP2_PE_MAC_RANGE_END; tid++) {
unsigned int entry_pmap;
if (!priv->prs_shadow[tid].valid ||
@@ -3494,110 +3771,103 @@ mvpp2_prs_mac_da_range_find(struct mvpp2 *priv, int pmap, const u8 *da,
(priv->prs_shadow[tid].udf != udf_type))
continue;
- pe->index = tid;
- mvpp2_prs_hw_read(priv, pe);
- entry_pmap = mvpp2_prs_tcam_port_map_get(pe);
+ mvpp2_prs_init_from_hw(priv, &pe, tid);
+ entry_pmap = mvpp2_prs_tcam_port_map_get(&pe);
- if (mvpp2_prs_mac_range_equals(pe, da, mask) &&
+ if (mvpp2_prs_mac_range_equals(&pe, da, mask) &&
entry_pmap == pmap)
- return pe;
+ return tid;
}
- kfree(pe);
- return NULL;
+ return -ENOENT;
}
/* Update parser's mac da entry */
-static int mvpp2_prs_mac_da_accept(struct mvpp2 *priv, int port,
- const u8 *da, bool add)
+static int mvpp2_prs_mac_da_accept(struct mvpp2_port *port, const u8 *da,
+ bool add)
{
- struct mvpp2_prs_entry *pe;
- unsigned int pmap, len, ri;
unsigned char mask[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+ struct mvpp2 *priv = port->priv;
+ unsigned int pmap, len, ri;
+ struct mvpp2_prs_entry pe;
int tid;
+ memset(&pe, 0, sizeof(pe));
+
/* Scan TCAM and see if entry with this <MAC DA, port> already exist */
- pe = mvpp2_prs_mac_da_range_find(priv, (1 << port), da, mask,
- MVPP2_PRS_UDF_MAC_DEF);
+ tid = mvpp2_prs_mac_da_range_find(priv, BIT(port->id), da, mask,
+ MVPP2_PRS_UDF_MAC_DEF);
/* No such entry */
- if (!pe) {
+ if (tid < 0) {
if (!add)
return 0;
/* Create new TCAM entry */
- /* Find first range mac entry*/
- for (tid = MVPP2_PE_FIRST_FREE_TID;
- tid <= MVPP2_PE_LAST_FREE_TID; tid++)
- if (priv->prs_shadow[tid].valid &&
- (priv->prs_shadow[tid].lu == MVPP2_PRS_LU_MAC) &&
- (priv->prs_shadow[tid].udf ==
- MVPP2_PRS_UDF_MAC_RANGE))
- break;
-
/* Go through the all entries from first to last */
- tid = mvpp2_prs_tcam_first_free(priv, MVPP2_PE_FIRST_FREE_TID,
- tid - 1);
+ tid = mvpp2_prs_tcam_first_free(priv,
+ MVPP2_PE_MAC_RANGE_START,
+ MVPP2_PE_MAC_RANGE_END);
if (tid < 0)
return tid;
- pe = kzalloc(sizeof(*pe), GFP_ATOMIC);
- if (!pe)
- return -ENOMEM;
- mvpp2_prs_tcam_lu_set(pe, MVPP2_PRS_LU_MAC);
- pe->index = tid;
+ pe.index = tid;
/* Mask all ports */
- mvpp2_prs_tcam_port_map_set(pe, 0);
+ mvpp2_prs_tcam_port_map_set(&pe, 0);
+ } else {
+ mvpp2_prs_init_from_hw(priv, &pe, tid);
}
+ mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_MAC);
+
/* Update port mask */
- mvpp2_prs_tcam_port_set(pe, port, add);
+ mvpp2_prs_tcam_port_set(&pe, port->id, add);
/* Invalidate the entry if no ports are left enabled */
- pmap = mvpp2_prs_tcam_port_map_get(pe);
+ pmap = mvpp2_prs_tcam_port_map_get(&pe);
if (pmap == 0) {
- if (add) {
- kfree(pe);
+ if (add)
return -EINVAL;
- }
- mvpp2_prs_hw_inv(priv, pe->index);
- priv->prs_shadow[pe->index].valid = false;
- kfree(pe);
+
+ mvpp2_prs_hw_inv(priv, pe.index);
+ priv->prs_shadow[pe.index].valid = false;
return 0;
}
/* Continue - set next lookup */
- mvpp2_prs_sram_next_lu_set(pe, MVPP2_PRS_LU_DSA);
+ mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_DSA);
/* Set match on DA */
len = ETH_ALEN;
while (len--)
- mvpp2_prs_tcam_data_byte_set(pe, len, da[len], 0xff);
+ mvpp2_prs_tcam_data_byte_set(&pe, len, da[len], 0xff);
/* Set result info bits */
- if (is_broadcast_ether_addr(da))
+ if (is_broadcast_ether_addr(da)) {
ri = MVPP2_PRS_RI_L2_BCAST;
- else if (is_multicast_ether_addr(da))
+ } else if (is_multicast_ether_addr(da)) {
ri = MVPP2_PRS_RI_L2_MCAST;
- else
- ri = MVPP2_PRS_RI_L2_UCAST | MVPP2_PRS_RI_MAC_ME_MASK;
+ } else {
+ ri = MVPP2_PRS_RI_L2_UCAST;
+
+ if (ether_addr_equal(da, port->dev->dev_addr))
+ ri |= MVPP2_PRS_RI_MAC_ME_MASK;
+ }
- mvpp2_prs_sram_ri_update(pe, ri, MVPP2_PRS_RI_L2_CAST_MASK |
+ mvpp2_prs_sram_ri_update(&pe, ri, MVPP2_PRS_RI_L2_CAST_MASK |
MVPP2_PRS_RI_MAC_ME_MASK);
- mvpp2_prs_shadow_ri_set(priv, pe->index, ri, MVPP2_PRS_RI_L2_CAST_MASK |
+ mvpp2_prs_shadow_ri_set(priv, pe.index, ri, MVPP2_PRS_RI_L2_CAST_MASK |
MVPP2_PRS_RI_MAC_ME_MASK);
/* Shift to ethertype */
- mvpp2_prs_sram_shift_set(pe, 2 * ETH_ALEN,
+ mvpp2_prs_sram_shift_set(&pe, 2 * ETH_ALEN,
MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD);
/* Update shadow table and hw entry */
- priv->prs_shadow[pe->index].udf = MVPP2_PRS_UDF_MAC_DEF;
- mvpp2_prs_shadow_set(priv, pe->index, MVPP2_PRS_LU_MAC);
- mvpp2_prs_hw_write(priv, pe);
-
- kfree(pe);
+ priv->prs_shadow[pe.index].udf = MVPP2_PRS_UDF_MAC_DEF;
+ mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_MAC);
+ mvpp2_prs_hw_write(priv, &pe);
return 0;
}
@@ -3608,13 +3878,12 @@ static int mvpp2_prs_update_mac_da(struct net_device *dev, const u8 *da)
int err;
/* Remove old parser entry */
- err = mvpp2_prs_mac_da_accept(port->priv, port->id, dev->dev_addr,
- false);
+ err = mvpp2_prs_mac_da_accept(port, dev->dev_addr, false);
if (err)
return err;
/* Add new parser entry */
- err = mvpp2_prs_mac_da_accept(port->priv, port->id, da, true);
+ err = mvpp2_prs_mac_da_accept(port, da, true);
if (err)
return err;
@@ -3624,14 +3893,15 @@ static int mvpp2_prs_update_mac_da(struct net_device *dev, const u8 *da)
return 0;
}
-/* Delete all port's multicast simple (not range) entries */
-static void mvpp2_prs_mcast_del_all(struct mvpp2 *priv, int port)
+static void mvpp2_prs_mac_del_all(struct mvpp2_port *port)
{
+ struct mvpp2 *priv = port->priv;
struct mvpp2_prs_entry pe;
+ unsigned long pmap;
int index, tid;
- for (tid = MVPP2_PE_FIRST_FREE_TID;
- tid <= MVPP2_PE_LAST_FREE_TID; tid++) {
+ for (tid = MVPP2_PE_MAC_RANGE_START;
+ tid <= MVPP2_PE_MAC_RANGE_END; tid++) {
unsigned char da[ETH_ALEN], da_mask[ETH_ALEN];
if (!priv->prs_shadow[tid].valid ||
@@ -3639,18 +3909,28 @@ static void mvpp2_prs_mcast_del_all(struct mvpp2 *priv, int port)
(priv->prs_shadow[tid].udf != MVPP2_PRS_UDF_MAC_DEF))
continue;
- /* Only simple mac entries */
- pe.index = tid;
- mvpp2_prs_hw_read(priv, &pe);
+ mvpp2_prs_init_from_hw(priv, &pe, tid);
+
+ pmap = mvpp2_prs_tcam_port_map_get(&pe);
+
+ /* We only want entries active on this port */
+ if (!test_bit(port->id, &pmap))
+ continue;
/* Read mac addr from entry */
for (index = 0; index < ETH_ALEN; index++)
mvpp2_prs_tcam_data_byte_get(&pe, index, &da[index],
&da_mask[index]);
- if (is_multicast_ether_addr(da) && !is_broadcast_ether_addr(da))
- /* Delete this entry */
- mvpp2_prs_mac_da_accept(priv, port, da, false);
+ /* Special cases : Don't remove broadcast and port's own
+ * address
+ */
+ if (is_broadcast_ether_addr(da) ||
+ ether_addr_equal(da, port->dev->dev_addr))
+ continue;
+
+ /* Remove entry from TCAM */
+ mvpp2_prs_mac_da_accept(port, da, false);
}
}
@@ -3707,13 +3987,15 @@ static int mvpp2_prs_tag_mode_set(struct mvpp2 *priv, int port, int type)
/* Set prs flow for the port */
static int mvpp2_prs_def_flow(struct mvpp2_port *port)
{
- struct mvpp2_prs_entry *pe;
+ struct mvpp2_prs_entry pe;
int tid;
- pe = mvpp2_prs_flow_find(port->priv, port->id);
+ memset(&pe, 0, sizeof(pe));
+
+ tid = mvpp2_prs_flow_find(port->priv, port->id);
/* Such entry not exist */
- if (!pe) {
+ if (tid < 0) {
/* Go through the all entires from last to first */
tid = mvpp2_prs_tcam_first_free(port->priv,
MVPP2_PE_LAST_FREE_TID,
@@ -3721,24 +4003,21 @@ static int mvpp2_prs_def_flow(struct mvpp2_port *port)
if (tid < 0)
return tid;
- pe = kzalloc(sizeof(*pe), GFP_KERNEL);
- if (!pe)
- return -ENOMEM;
-
- mvpp2_prs_tcam_lu_set(pe, MVPP2_PRS_LU_FLOWS);
- pe->index = tid;
+ pe.index = tid;
/* Set flow ID*/
- mvpp2_prs_sram_ai_update(pe, port->id, MVPP2_PRS_FLOW_ID_MASK);
- mvpp2_prs_sram_bits_set(pe, MVPP2_PRS_SRAM_LU_DONE_BIT, 1);
+ mvpp2_prs_sram_ai_update(&pe, port->id, MVPP2_PRS_FLOW_ID_MASK);
+ mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_DONE_BIT, 1);
/* Update shadow table */
- mvpp2_prs_shadow_set(port->priv, pe->index, MVPP2_PRS_LU_FLOWS);
+ mvpp2_prs_shadow_set(port->priv, pe.index, MVPP2_PRS_LU_FLOWS);
+ } else {
+ mvpp2_prs_init_from_hw(port->priv, &pe, tid);
}
- mvpp2_prs_tcam_port_map_set(pe, (1 << port->id));
- mvpp2_prs_hw_write(port->priv, pe);
- kfree(pe);
+ mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_FLOWS);
+ mvpp2_prs_tcam_port_map_set(&pe, (1 << port->id));
+ mvpp2_prs_hw_write(port->priv, &pe);
return 0;
}
@@ -3901,7 +4180,6 @@ static int mvpp2_bm_pool_create(struct platform_device *pdev,
val |= MVPP2_BM_START_MASK;
mvpp2_write(priv, MVPP2_BM_POOL_CTRL_REG(bm_pool->id), val);
- bm_pool->type = MVPP2_BM_FREE;
bm_pool->size = size;
bm_pool->pkt_size = 0;
bm_pool->buf_num = 0;
@@ -3954,11 +4232,17 @@ static void mvpp2_bm_bufs_get_addrs(struct device *dev, struct mvpp2 *priv,
/* Free all buffers from the pool */
static void mvpp2_bm_bufs_free(struct device *dev, struct mvpp2 *priv,
- struct mvpp2_bm_pool *bm_pool)
+ struct mvpp2_bm_pool *bm_pool, int buf_num)
{
int i;
- for (i = 0; i < bm_pool->buf_num; i++) {
+ if (buf_num > bm_pool->buf_num) {
+ WARN(1, "Pool does not have so many bufs pool(%d) bufs(%d)\n",
+ bm_pool->id, buf_num);
+ buf_num = bm_pool->buf_num;
+ }
+
+ for (i = 0; i < buf_num; i++) {
dma_addr_t buf_dma_addr;
phys_addr_t buf_phys_addr;
void *data;
@@ -3980,16 +4264,39 @@ static void mvpp2_bm_bufs_free(struct device *dev, struct mvpp2 *priv,
bm_pool->buf_num -= i;
}
+/* Check number of buffers in BM pool */
+static int mvpp2_check_hw_buf_num(struct mvpp2 *priv, struct mvpp2_bm_pool *bm_pool)
+{
+ int buf_num = 0;
+
+ buf_num += mvpp2_read(priv, MVPP2_BM_POOL_PTRS_NUM_REG(bm_pool->id)) &
+ MVPP22_BM_POOL_PTRS_NUM_MASK;
+ buf_num += mvpp2_read(priv, MVPP2_BM_BPPI_PTRS_NUM_REG(bm_pool->id)) &
+ MVPP2_BM_BPPI_PTR_NUM_MASK;
+
+ /* HW has one buffer ready which is not reflected in the counters */
+ if (buf_num)
+ buf_num += 1;
+
+ return buf_num;
+}
+
/* Cleanup pool */
static int mvpp2_bm_pool_destroy(struct platform_device *pdev,
struct mvpp2 *priv,
struct mvpp2_bm_pool *bm_pool)
{
+ int buf_num;
u32 val;
- mvpp2_bm_bufs_free(&pdev->dev, priv, bm_pool);
- if (bm_pool->buf_num) {
- WARN(1, "cannot free all buffers in pool %d\n", bm_pool->id);
+ buf_num = mvpp2_check_hw_buf_num(priv, bm_pool);
+ mvpp2_bm_bufs_free(&pdev->dev, priv, bm_pool, buf_num);
+
+ /* Check buffer counters after free */
+ buf_num = mvpp2_check_hw_buf_num(priv, bm_pool);
+ if (buf_num) {
+ WARN(1, "cannot free all buffers in pool %d, buf_num left %d\n",
+ bm_pool->id, bm_pool->buf_num);
return 0;
}
@@ -4051,6 +4358,21 @@ static int mvpp2_bm_init(struct platform_device *pdev, struct mvpp2 *priv)
return 0;
}
+static void mvpp2_setup_bm_pool(void)
+{
+ /* Short pool */
+ mvpp2_pools[MVPP2_BM_SHORT].buf_num = MVPP2_BM_SHORT_BUF_NUM;
+ mvpp2_pools[MVPP2_BM_SHORT].pkt_size = MVPP2_BM_SHORT_PKT_SIZE;
+
+ /* Long pool */
+ mvpp2_pools[MVPP2_BM_LONG].buf_num = MVPP2_BM_LONG_BUF_NUM;
+ mvpp2_pools[MVPP2_BM_LONG].pkt_size = MVPP2_BM_LONG_PKT_SIZE;
+
+ /* Jumbo pool */
+ mvpp2_pools[MVPP2_BM_JUMBO].buf_num = MVPP2_BM_JUMBO_BUF_NUM;
+ mvpp2_pools[MVPP2_BM_JUMBO].pkt_size = MVPP2_BM_JUMBO_PKT_SIZE;
+}
+
/* Attach long pool to rxq */
static void mvpp2_rxq_long_pool_set(struct mvpp2_port *port,
int lrxq, int long_pool)
@@ -4138,8 +4460,8 @@ static inline void mvpp2_bm_pool_put(struct mvpp2_port *port, int pool,
<< MVPP22_BM_ADDR_HIGH_VIRT_RLS_SHIFT) &
MVPP22_BM_ADDR_HIGH_VIRT_RLS_MASK;
- mvpp2_percpu_write(port->priv, cpu,
- MVPP22_BM_ADDR_HIGH_RLS_REG, val);
+ mvpp2_percpu_write_relaxed(port->priv, cpu,
+ MVPP22_BM_ADDR_HIGH_RLS_REG, val);
}
/* MVPP2_BM_VIRT_RLS_REG is not interpreted by HW, and simply
@@ -4147,10 +4469,10 @@ static inline void mvpp2_bm_pool_put(struct mvpp2_port *port, int pool,
* descriptor. Instead of storing the virtual address, we
* store the physical address
*/
- mvpp2_percpu_write(port->priv, cpu,
- MVPP2_BM_VIRT_RLS_REG, buf_phys_addr);
- mvpp2_percpu_write(port->priv, cpu,
- MVPP2_BM_PHY_RLS_REG(pool), buf_dma_addr);
+ mvpp2_percpu_write_relaxed(port->priv, cpu,
+ MVPP2_BM_VIRT_RLS_REG, buf_phys_addr);
+ mvpp2_percpu_write_relaxed(port->priv, cpu,
+ MVPP2_BM_PHY_RLS_REG(pool), buf_dma_addr);
put_cpu();
}
@@ -4189,13 +4511,11 @@ static int mvpp2_bm_bufs_add(struct mvpp2_port *port,
bm_pool->buf_num += i;
netdev_dbg(port->dev,
- "%s pool %d: pkt_size=%4d, buf_size=%4d, total_size=%4d\n",
- bm_pool->type == MVPP2_BM_SWF_SHORT ? "short" : " long",
+ "pool %d: pkt_size=%4d, buf_size=%4d, total_size=%4d\n",
bm_pool->id, bm_pool->pkt_size, buf_size, total_size);
netdev_dbg(port->dev,
- "%s pool %d: %d of %d buffers added\n",
- bm_pool->type == MVPP2_BM_SWF_SHORT ? "short" : " long",
+ "pool %d: %d of %d buffers added\n",
bm_pool->id, i, buf_num);
return i;
}
@@ -4204,25 +4524,20 @@ static int mvpp2_bm_bufs_add(struct mvpp2_port *port,
* pool pointer on success
*/
static struct mvpp2_bm_pool *
-mvpp2_bm_pool_use(struct mvpp2_port *port, int pool, enum mvpp2_bm_type type,
- int pkt_size)
+mvpp2_bm_pool_use(struct mvpp2_port *port, unsigned pool, int pkt_size)
{
struct mvpp2_bm_pool *new_pool = &port->priv->bm_pools[pool];
int num;
- if (new_pool->type != MVPP2_BM_FREE && new_pool->type != type) {
- netdev_err(port->dev, "mixing pool types is forbidden\n");
+ if (pool >= MVPP2_BM_POOLS_NUM) {
+ netdev_err(port->dev, "Invalid pool %d\n", pool);
return NULL;
}
- if (new_pool->type == MVPP2_BM_FREE)
- new_pool->type = type;
-
/* Allocate buffers in case BM pool is used as long pool, but packet
* size doesn't match MTU or BM pool hasn't being used yet
*/
- if (((type == MVPP2_BM_SWF_LONG) && (pkt_size > new_pool->pkt_size)) ||
- (new_pool->pkt_size == 0)) {
+ if (new_pool->pkt_size == 0) {
int pkts_num;
/* Set default buffer number or free all the buffers in case
@@ -4230,12 +4545,10 @@ mvpp2_bm_pool_use(struct mvpp2_port *port, int pool, enum mvpp2_bm_type type,
*/
pkts_num = new_pool->buf_num;
if (pkts_num == 0)
- pkts_num = type == MVPP2_BM_SWF_LONG ?
- MVPP2_BM_LONG_BUF_NUM :
- MVPP2_BM_SHORT_BUF_NUM;
+ pkts_num = mvpp2_pools[pool].buf_num;
else
mvpp2_bm_bufs_free(port->dev->dev.parent,
- port->priv, new_pool);
+ port->priv, new_pool, pkts_num);
new_pool->pkt_size = pkt_size;
new_pool->frag_size =
@@ -4261,16 +4574,28 @@ mvpp2_bm_pool_use(struct mvpp2_port *port, int pool, enum mvpp2_bm_type type,
static int mvpp2_swf_bm_pool_init(struct mvpp2_port *port)
{
int rxq;
+ enum mvpp2_bm_pool_log_num long_log_pool, short_log_pool;
+
+ /* If port pkt_size is higher than 1518B:
+ * HW Long pool - SW Jumbo pool, HW Short pool - SW Long pool
+ * else: HW Long pool - SW Long pool, HW Short pool - SW Short pool
+ */
+ if (port->pkt_size > MVPP2_BM_LONG_PKT_SIZE) {
+ long_log_pool = MVPP2_BM_JUMBO;
+ short_log_pool = MVPP2_BM_LONG;
+ } else {
+ long_log_pool = MVPP2_BM_LONG;
+ short_log_pool = MVPP2_BM_SHORT;
+ }
if (!port->pool_long) {
port->pool_long =
- mvpp2_bm_pool_use(port, MVPP2_BM_SWF_LONG_POOL(port->id),
- MVPP2_BM_SWF_LONG,
- port->pkt_size);
+ mvpp2_bm_pool_use(port, long_log_pool,
+ mvpp2_pools[long_log_pool].pkt_size);
if (!port->pool_long)
return -ENOMEM;
- port->pool_long->port_map |= (1 << port->id);
+ port->pool_long->port_map |= BIT(port->id);
for (rxq = 0; rxq < port->nrxqs; rxq++)
mvpp2_rxq_long_pool_set(port, rxq, port->pool_long->id);
@@ -4278,13 +4603,12 @@ static int mvpp2_swf_bm_pool_init(struct mvpp2_port *port)
if (!port->pool_short) {
port->pool_short =
- mvpp2_bm_pool_use(port, MVPP2_BM_SWF_SHORT_POOL,
- MVPP2_BM_SWF_SHORT,
- MVPP2_BM_SHORT_PKT_SIZE);
+ mvpp2_bm_pool_use(port, short_log_pool,
+ mvpp2_pools[short_log_pool].pkt_size);
if (!port->pool_short)
return -ENOMEM;
- port->pool_short->port_map |= (1 << port->id);
+ port->pool_short->port_map |= BIT(port->id);
for (rxq = 0; rxq < port->nrxqs; rxq++)
mvpp2_rxq_short_pool_set(port, rxq,
@@ -4297,30 +4621,49 @@ static int mvpp2_swf_bm_pool_init(struct mvpp2_port *port)
static int mvpp2_bm_update_mtu(struct net_device *dev, int mtu)
{
struct mvpp2_port *port = netdev_priv(dev);
- struct mvpp2_bm_pool *port_pool = port->pool_long;
- int num, pkts_num = port_pool->buf_num;
+ enum mvpp2_bm_pool_log_num new_long_pool;
int pkt_size = MVPP2_RX_PKT_SIZE(mtu);
- /* Update BM pool with new buffer size */
- mvpp2_bm_bufs_free(dev->dev.parent, port->priv, port_pool);
- if (port_pool->buf_num) {
- WARN(1, "cannot free all buffers in pool %d\n", port_pool->id);
- return -EIO;
- }
-
- port_pool->pkt_size = pkt_size;
- port_pool->frag_size = SKB_DATA_ALIGN(MVPP2_RX_BUF_SIZE(pkt_size)) +
- MVPP2_SKB_SHINFO_SIZE;
- num = mvpp2_bm_bufs_add(port, port_pool, pkts_num);
- if (num != pkts_num) {
- WARN(1, "pool %d: %d of %d allocated\n",
- port_pool->id, num, pkts_num);
- return -EIO;
+ /* If port MTU is higher than 1518B:
+ * HW Long pool - SW Jumbo pool, HW Short pool - SW Long pool
+ * else: HW Long pool - SW Long pool, HW Short pool - SW Short pool
+ */
+ if (pkt_size > MVPP2_BM_LONG_PKT_SIZE)
+ new_long_pool = MVPP2_BM_JUMBO;
+ else
+ new_long_pool = MVPP2_BM_LONG;
+
+ if (new_long_pool != port->pool_long->id) {
+ /* Remove port from old short & long pool */
+ port->pool_long = mvpp2_bm_pool_use(port, port->pool_long->id,
+ port->pool_long->pkt_size);
+ port->pool_long->port_map &= ~BIT(port->id);
+ port->pool_long = NULL;
+
+ port->pool_short = mvpp2_bm_pool_use(port, port->pool_short->id,
+ port->pool_short->pkt_size);
+ port->pool_short->port_map &= ~BIT(port->id);
+ port->pool_short = NULL;
+
+ port->pkt_size = pkt_size;
+
+ /* Add port to new short & long pool */
+ mvpp2_swf_bm_pool_init(port);
+
+ /* Update L4 checksum when jumbo enable/disable on port */
+ if (new_long_pool == MVPP2_BM_JUMBO && port->id != 0) {
+ dev->features &= ~(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM);
+ dev->hw_features &= ~(NETIF_F_IP_CSUM |
+ NETIF_F_IPV6_CSUM);
+ } else {
+ dev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
+ dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
+ }
}
- mvpp2_bm_pool_bufsize_set(port->priv, port_pool,
- MVPP2_RX_BUF_SIZE(port_pool->pkt_size));
dev->mtu = mtu;
+ dev->wanted_features = dev->features;
+
netdev_update_features(dev);
return 0;
}
@@ -5221,7 +5564,8 @@ static int mvpp2_aggr_desc_num_check(struct mvpp2 *priv,
if ((aggr_txq->count + num) > MVPP2_AGGR_TXQ_SIZE) {
/* Update number of occupied aggregated Tx descriptors */
int cpu = smp_processor_id();
- u32 val = mvpp2_read(priv, MVPP2_AGGR_TXQ_STATUS_REG(cpu));
+ u32 val = mvpp2_read_relaxed(priv,
+ MVPP2_AGGR_TXQ_STATUS_REG(cpu));
aggr_txq->count = val & MVPP2_AGGR_TXQ_PENDING_MASK;
}
@@ -5245,9 +5589,9 @@ static int mvpp2_txq_alloc_reserved_desc(struct mvpp2 *priv,
int cpu = smp_processor_id();
val = (txq->id << MVPP2_TXQ_RSVD_REQ_Q_OFFSET) | num;
- mvpp2_percpu_write(priv, cpu, MVPP2_TXQ_RSVD_REQ_REG, val);
+ mvpp2_percpu_write_relaxed(priv, cpu, MVPP2_TXQ_RSVD_REQ_REG, val);
- val = mvpp2_percpu_read(priv, cpu, MVPP2_TXQ_RSVD_RSLT_REG);
+ val = mvpp2_percpu_read_relaxed(priv, cpu, MVPP2_TXQ_RSVD_RSLT_REG);
return val & MVPP2_TXQ_RSVD_RSLT_MASK;
}
@@ -5352,8 +5696,8 @@ static inline int mvpp2_txq_sent_desc_proc(struct mvpp2_port *port,
u32 val;
/* Reading status reg resets transmitted descriptor counter */
- val = mvpp2_percpu_read(port->priv, smp_processor_id(),
- MVPP2_TXQ_SENT_REG(txq->id));
+ val = mvpp2_percpu_read_relaxed(port->priv, smp_processor_id(),
+ MVPP2_TXQ_SENT_REG(txq->id));
return (val & MVPP2_TRANSMITTED_COUNT_MASK) >>
MVPP2_TRANSMITTED_COUNT_OFFSET;
@@ -6719,8 +7063,8 @@ static int mvpp2_poll(struct napi_struct *napi, int budget)
*
* Each CPU has its own Rx/Tx cause register
*/
- cause_rx_tx = mvpp2_percpu_read(port->priv, qv->sw_thread_id,
- MVPP2_ISR_RX_TX_CAUSE_REG(port->id));
+ cause_rx_tx = mvpp2_percpu_read_relaxed(port->priv, qv->sw_thread_id,
+ MVPP2_ISR_RX_TX_CAUSE_REG(port->id));
cause_misc = cause_rx_tx & MVPP2_CAUSE_MISC_SUM_MASK;
if (cause_misc) {
@@ -7007,15 +7351,14 @@ static int mvpp2_open(struct net_device *dev)
0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
int err;
- err = mvpp2_prs_mac_da_accept(port->priv, port->id, mac_bcast, true);
+ err = mvpp2_prs_mac_da_accept(port, mac_bcast, true);
if (err) {
netdev_err(dev, "mvpp2_prs_mac_da_accept BC failed\n");
return err;
}
- err = mvpp2_prs_mac_da_accept(port->priv, port->id,
- dev->dev_addr, true);
+ err = mvpp2_prs_mac_da_accept(port, dev->dev_addr, true);
if (err) {
- netdev_err(dev, "mvpp2_prs_mac_da_accept MC failed\n");
+ netdev_err(dev, "mvpp2_prs_mac_da_accept own addr failed\n");
return err;
}
err = mvpp2_prs_tag_mode_set(port->priv, port->id, MVPP2_TAG_TYPE_MH);
@@ -7129,30 +7472,64 @@ static int mvpp2_stop(struct net_device *dev)
return 0;
}
+static int mvpp2_prs_mac_da_accept_list(struct mvpp2_port *port,
+ struct netdev_hw_addr_list *list)
+{
+ struct netdev_hw_addr *ha;
+ int ret;
+
+ netdev_hw_addr_list_for_each(ha, list) {
+ ret = mvpp2_prs_mac_da_accept(port, ha->addr, true);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void mvpp2_set_rx_promisc(struct mvpp2_port *port, bool enable)
+{
+ if (!enable && (port->dev->features & NETIF_F_HW_VLAN_CTAG_FILTER))
+ mvpp2_prs_vid_enable_filtering(port);
+ else
+ mvpp2_prs_vid_disable_filtering(port);
+
+ mvpp2_prs_mac_promisc_set(port->priv, port->id,
+ MVPP2_PRS_L2_UNI_CAST, enable);
+
+ mvpp2_prs_mac_promisc_set(port->priv, port->id,
+ MVPP2_PRS_L2_MULTI_CAST, enable);
+}
+
static void mvpp2_set_rx_mode(struct net_device *dev)
{
struct mvpp2_port *port = netdev_priv(dev);
- struct mvpp2 *priv = port->priv;
- struct netdev_hw_addr *ha;
- int id = port->id;
- bool allmulti = dev->flags & IFF_ALLMULTI;
-
-retry:
- mvpp2_prs_mac_promisc_set(priv, id, dev->flags & IFF_PROMISC);
- mvpp2_prs_mac_multi_set(priv, id, MVPP2_PE_MAC_MC_ALL, allmulti);
- mvpp2_prs_mac_multi_set(priv, id, MVPP2_PE_MAC_MC_IP6, allmulti);
-
- /* Remove all port->id's mcast enries */
- mvpp2_prs_mcast_del_all(priv, id);
-
- if (!allmulti) {
- netdev_for_each_mc_addr(ha, dev) {
- if (mvpp2_prs_mac_da_accept(priv, id, ha->addr, true)) {
- allmulti = true;
- goto retry;
- }
- }
+
+ /* Clear the whole UC and MC list */
+ mvpp2_prs_mac_del_all(port);
+
+ if (dev->flags & IFF_PROMISC) {
+ mvpp2_set_rx_promisc(port, true);
+ return;
+ }
+
+ mvpp2_set_rx_promisc(port, false);
+
+ if (netdev_uc_count(dev) > MVPP2_PRS_MAC_UC_FILT_MAX ||
+ mvpp2_prs_mac_da_accept_list(port, &dev->uc))
+ mvpp2_prs_mac_promisc_set(port->priv, port->id,
+ MVPP2_PRS_L2_UNI_CAST, true);
+
+ if (dev->flags & IFF_ALLMULTI) {
+ mvpp2_prs_mac_promisc_set(port->priv, port->id,
+ MVPP2_PRS_L2_MULTI_CAST, true);
+ return;
}
+
+ if (netdev_mc_count(dev) > MVPP2_PRS_MAC_MC_FILT_MAX ||
+ mvpp2_prs_mac_da_accept_list(port, &dev->mc))
+ mvpp2_prs_mac_promisc_set(port->priv, port->id,
+ MVPP2_PRS_L2_MULTI_CAST, true);
}
static int mvpp2_set_mac_address(struct net_device *dev, void *p)
@@ -7292,6 +7669,48 @@ static int mvpp2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
return ret;
}
+static int mvpp2_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
+{
+ struct mvpp2_port *port = netdev_priv(dev);
+ int ret;
+
+ ret = mvpp2_prs_vid_entry_add(port, vid);
+ if (ret)
+ netdev_err(dev, "rx-vlan-filter offloading cannot accept more than %d VIDs per port\n",
+ MVPP2_PRS_VLAN_FILT_MAX - 1);
+ return ret;
+}
+
+static int mvpp2_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
+{
+ struct mvpp2_port *port = netdev_priv(dev);
+
+ mvpp2_prs_vid_entry_remove(port, vid);
+ return 0;
+}
+
+static int mvpp2_set_features(struct net_device *dev,
+ netdev_features_t features)
+{
+ netdev_features_t changed = dev->features ^ features;
+ struct mvpp2_port *port = netdev_priv(dev);
+
+ if (changed & NETIF_F_HW_VLAN_CTAG_FILTER) {
+ if (features & NETIF_F_HW_VLAN_CTAG_FILTER) {
+ mvpp2_prs_vid_enable_filtering(port);
+ } else {
+ /* Invalidate all registered VID filters for this
+ * port
+ */
+ mvpp2_prs_vid_remove_all(port);
+
+ mvpp2_prs_vid_disable_filtering(port);
+ }
+ }
+
+ return 0;
+}
+
/* Ethtool methods */
/* Set interrupt coalescing for ethtools */
@@ -7433,6 +7852,9 @@ static const struct net_device_ops mvpp2_netdev_ops = {
.ndo_change_mtu = mvpp2_change_mtu,
.ndo_get_stats64 = mvpp2_get_stats64,
.ndo_do_ioctl = mvpp2_ioctl,
+ .ndo_vlan_rx_add_vid = mvpp2_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = mvpp2_vlan_rx_kill_vid,
+ .ndo_set_features = mvpp2_set_features,
};
static const struct ethtool_ops mvpp2_eth_tool_ops = {
@@ -7943,16 +8365,25 @@ static int mvpp2_port_probe(struct platform_device *pdev,
}
}
- features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO;
+ features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+ NETIF_F_TSO;
dev->features = features | NETIF_F_RXCSUM;
- dev->hw_features |= features | NETIF_F_RXCSUM | NETIF_F_GRO;
+ dev->hw_features |= features | NETIF_F_RXCSUM | NETIF_F_GRO |
+ NETIF_F_HW_VLAN_CTAG_FILTER;
+
+ if (port->pool_long->id == MVPP2_BM_JUMBO && port->id != 0) {
+ dev->features &= ~(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM);
+ dev->hw_features &= ~(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM);
+ }
+
dev->vlan_features |= features;
dev->gso_max_segs = MVPP2_MAX_TSO_SEGS;
+ dev->priv_flags |= IFF_UNICAST_FLT;
- /* MTU range: 68 - 9676 */
+ /* MTU range: 68 - 9704 */
dev->min_mtu = ETH_MIN_MTU;
- /* 9676 == 9700 - 20 and rounding to 8 */
- dev->max_mtu = 9676;
+ /* 9704 == 9728 - 20 and rounding to 8 */
+ dev->max_mtu = MVPP2_BM_JUMBO_PKT_SIZE;
err = register_netdev(dev);
if (err < 0) {
@@ -8083,14 +8514,25 @@ static void mvpp22_rx_fifo_init(struct mvpp2 *priv)
mvpp2_write(priv, MVPP2_RX_FIFO_INIT_REG, 0x1);
}
-/* Initialize Tx FIFO's */
+/* Initialize Tx FIFO's: the total FIFO size is 19kB on PPv2.2 and 10G
+ * interfaces must have a Tx FIFO size of 10kB. As only port 0 can do 10G,
+ * configure its Tx FIFO size to 10kB and the others ports Tx FIFO size to 3kB.
+ */
static void mvpp22_tx_fifo_init(struct mvpp2 *priv)
{
- int port;
+ int port, size, thrs;
- for (port = 0; port < MVPP2_MAX_PORTS; port++)
- mvpp2_write(priv, MVPP22_TX_FIFO_SIZE_REG(port),
- MVPP22_TX_FIFO_DATA_SIZE_3KB);
+ for (port = 0; port < MVPP2_MAX_PORTS; port++) {
+ if (port == 0) {
+ size = MVPP22_TX_FIFO_DATA_SIZE_10KB;
+ thrs = MVPP2_TX_FIFO_THRESHOLD_10KB;
+ } else {
+ size = MVPP22_TX_FIFO_DATA_SIZE_3KB;
+ thrs = MVPP2_TX_FIFO_THRESHOLD_3KB;
+ }
+ mvpp2_write(priv, MVPP22_TX_FIFO_SIZE_REG(port), size);
+ mvpp2_write(priv, MVPP22_TX_FIFO_THRESH_REG(port), thrs);
+ }
}
static void mvpp2_axi_init(struct mvpp2 *priv)
@@ -8284,6 +8726,8 @@ static int mvpp2_probe(struct platform_device *pdev)
priv->sysctrl_base = NULL;
}
+ mvpp2_setup_bm_pool();
+
for (i = 0; i < MVPP2_MAX_THREADS; i++) {
u32 addr_space_sz;
@@ -8347,7 +8791,7 @@ static int mvpp2_probe(struct platform_device *pdev)
}
if (priv->hw_version == MVPP22) {
- err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(40));
+ err = dma_set_mask(&pdev->dev, MVPP2_DESC_DMA_MASK);
if (err)
goto err_mg_clk;
/* Sadly, the BM pools all share the same register to
diff --git a/drivers/net/ethernet/marvell/skge.c b/drivers/net/ethernet/marvell/skge.c
index 31efc47c847e..9c08c3650c02 100644
--- a/drivers/net/ethernet/marvell/skge.c
+++ b/drivers/net/ethernet/marvell/skge.c
@@ -3783,7 +3783,7 @@ static int skge_device_event(struct notifier_block *unused,
break;
case NETDEV_UP:
- d = debugfs_create_file(dev->name, S_IRUGO,
+ d = debugfs_create_file(dev->name, 0444,
skge_debug, dev,
&skge_debug_fops);
if (!d || IS_ERR(d))
diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c
index 9fe85300e7b6..697d9b374f5e 100644
--- a/drivers/net/ethernet/marvell/sky2.c
+++ b/drivers/net/ethernet/marvell/sky2.c
@@ -4667,7 +4667,7 @@ static int sky2_device_event(struct notifier_block *unused,
break;
case NETDEV_UP:
- sky2->debugfs = debugfs_create_file(dev->name, S_IRUGO,
+ sky2->debugfs = debugfs_create_file(dev->name, 0444,
sky2_debug, dev,
&sky2_debug_fops);
if (IS_ERR(sky2->debugfs))
@@ -5087,7 +5087,7 @@ static int sky2_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
INIT_WORK(&hw->restart_work, sky2_restart);
pci_set_drvdata(pdev, hw);
- pdev->d3_delay = 150;
+ pdev->d3_delay = 200;
return 0;
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c b/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c
index 1a0c3bf86ead..752a72499b4f 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c
@@ -156,57 +156,63 @@ static int mlx4_en_dcbnl_getnumtcs(struct net_device *netdev, int tcid, u8 *num)
static u8 mlx4_en_dcbnl_set_all(struct net_device *netdev)
{
struct mlx4_en_priv *priv = netdev_priv(netdev);
+ struct mlx4_en_port_profile *prof = priv->prof;
struct mlx4_en_dev *mdev = priv->mdev;
+ u8 tx_pause, tx_ppp, rx_pause, rx_ppp;
if (!(priv->dcbx_cap & DCB_CAP_DCBX_VER_CEE))
return 1;
if (priv->cee_config.pfc_state) {
int tc;
+ rx_ppp = prof->rx_ppp;
+ tx_ppp = prof->tx_ppp;
- priv->prof->rx_pause = 0;
- priv->prof->tx_pause = 0;
for (tc = 0; tc < CEE_DCBX_MAX_PRIO; tc++) {
u8 tc_mask = 1 << tc;
switch (priv->cee_config.dcb_pfc[tc]) {
case pfc_disabled:
- priv->prof->tx_ppp &= ~tc_mask;
- priv->prof->rx_ppp &= ~tc_mask;
+ tx_ppp &= ~tc_mask;
+ rx_ppp &= ~tc_mask;
break;
case pfc_enabled_full:
- priv->prof->tx_ppp |= tc_mask;
- priv->prof->rx_ppp |= tc_mask;
+ tx_ppp |= tc_mask;
+ rx_ppp |= tc_mask;
break;
case pfc_enabled_tx:
- priv->prof->tx_ppp |= tc_mask;
- priv->prof->rx_ppp &= ~tc_mask;
+ tx_ppp |= tc_mask;
+ rx_ppp &= ~tc_mask;
break;
case pfc_enabled_rx:
- priv->prof->tx_ppp &= ~tc_mask;
- priv->prof->rx_ppp |= tc_mask;
+ tx_ppp &= ~tc_mask;
+ rx_ppp |= tc_mask;
break;
default:
break;
}
}
- en_dbg(DRV, priv, "Set pfc on\n");
+ rx_pause = !!(rx_ppp || tx_ppp) ? 0 : prof->rx_pause;
+ tx_pause = !!(rx_ppp || tx_ppp) ? 0 : prof->tx_pause;
} else {
- priv->prof->rx_pause = 1;
- priv->prof->tx_pause = 1;
- en_dbg(DRV, priv, "Set pfc off\n");
+ rx_ppp = 0;
+ tx_ppp = 0;
+ rx_pause = prof->rx_pause;
+ tx_pause = prof->tx_pause;
}
if (mlx4_SET_PORT_general(mdev->dev, priv->port,
priv->rx_skb_size + ETH_FCS_LEN,
- priv->prof->tx_pause,
- priv->prof->tx_ppp,
- priv->prof->rx_pause,
- priv->prof->rx_ppp)) {
+ tx_pause, tx_ppp, rx_pause, rx_ppp)) {
en_err(priv, "Failed setting pause params\n");
return 1;
}
+ prof->tx_ppp = tx_ppp;
+ prof->rx_ppp = rx_ppp;
+ prof->tx_pause = tx_pause;
+ prof->rx_pause = rx_pause;
+
return 0;
}
@@ -408,6 +414,7 @@ static int mlx4_en_dcbnl_ieee_setpfc(struct net_device *dev,
struct mlx4_en_priv *priv = netdev_priv(dev);
struct mlx4_en_port_profile *prof = priv->prof;
struct mlx4_en_dev *mdev = priv->mdev;
+ u32 tx_pause, tx_ppp, rx_pause, rx_ppp;
int err;
en_dbg(DRV, priv, "cap: 0x%x en: 0x%x mbc: 0x%x delay: %d\n",
@@ -416,23 +423,26 @@ static int mlx4_en_dcbnl_ieee_setpfc(struct net_device *dev,
pfc->mbc,
pfc->delay);
- prof->rx_pause = !pfc->pfc_en;
- prof->tx_pause = !pfc->pfc_en;
- prof->rx_ppp = pfc->pfc_en;
- prof->tx_ppp = pfc->pfc_en;
+ rx_pause = prof->rx_pause && !pfc->pfc_en;
+ tx_pause = prof->tx_pause && !pfc->pfc_en;
+ rx_ppp = pfc->pfc_en;
+ tx_ppp = pfc->pfc_en;
err = mlx4_SET_PORT_general(mdev->dev, priv->port,
priv->rx_skb_size + ETH_FCS_LEN,
- prof->tx_pause,
- prof->tx_ppp,
- prof->rx_pause,
- prof->rx_ppp);
- if (err)
+ tx_pause, tx_ppp, rx_pause, rx_ppp);
+ if (err) {
en_err(priv, "Failed setting pause params\n");
- else
- mlx4_en_update_pfc_stats_bitmap(mdev->dev, &priv->stats_bitmap,
- prof->rx_ppp, prof->rx_pause,
- prof->tx_ppp, prof->tx_pause);
+ return err;
+ }
+
+ mlx4_en_update_pfc_stats_bitmap(mdev->dev, &priv->stats_bitmap,
+ rx_ppp, rx_pause, tx_ppp, tx_pause);
+
+ prof->tx_ppp = tx_ppp;
+ prof->rx_ppp = rx_ppp;
+ prof->rx_pause = rx_pause;
+ prof->tx_pause = tx_pause;
return err;
}
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
index ebc1f566a4d9..a30a2e95d13f 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
@@ -199,6 +199,10 @@ static const char main_strings[][ETH_GSTRING_LEN] = {
"rx_xdp_drop",
"rx_xdp_tx",
"rx_xdp_tx_full",
+
+ /* phy statistics */
+ "rx_packets_phy", "rx_bytes_phy",
+ "tx_packets_phy", "tx_bytes_phy",
};
static const char mlx4_en_test_names[][ETH_GSTRING_LEN]= {
@@ -411,6 +415,10 @@ static void mlx4_en_get_ethtool_stats(struct net_device *dev,
if (bitmap_iterator_test(&it))
data[index++] = ((unsigned long *)&priv->xdp_stats)[i];
+ for (i = 0; i < NUM_PHY_STATS; i++, bitmap_iterator_inc(&it))
+ if (bitmap_iterator_test(&it))
+ data[index++] = ((unsigned long *)&priv->phy_stats)[i];
+
for (i = 0; i < priv->tx_ring_num[TX]; i++) {
data[index++] = priv->tx_ring[TX][i]->packets;
data[index++] = priv->tx_ring[TX][i]->bytes;
@@ -490,6 +498,12 @@ static void mlx4_en_get_strings(struct net_device *dev,
strcpy(data + (index++) * ETH_GSTRING_LEN,
main_strings[strings]);
+ for (i = 0; i < NUM_PHY_STATS; i++, strings++,
+ bitmap_iterator_inc(&it))
+ if (bitmap_iterator_test(&it))
+ strcpy(data + (index++) * ETH_GSTRING_LEN,
+ main_strings[strings]);
+
for (i = 0; i < priv->tx_ring_num[TX]; i++) {
sprintf(data + (index++) * ETH_GSTRING_LEN,
"tx%d_packets", i);
@@ -1046,27 +1060,32 @@ static int mlx4_en_set_pauseparam(struct net_device *dev,
{
struct mlx4_en_priv *priv = netdev_priv(dev);
struct mlx4_en_dev *mdev = priv->mdev;
+ u8 tx_pause, tx_ppp, rx_pause, rx_ppp;
int err;
if (pause->autoneg)
return -EINVAL;
- priv->prof->tx_pause = pause->tx_pause != 0;
- priv->prof->rx_pause = pause->rx_pause != 0;
+ tx_pause = !!(pause->tx_pause);
+ rx_pause = !!(pause->rx_pause);
+ rx_ppp = priv->prof->rx_ppp && !(tx_pause || rx_pause);
+ tx_ppp = priv->prof->tx_ppp && !(tx_pause || rx_pause);
+
err = mlx4_SET_PORT_general(mdev->dev, priv->port,
priv->rx_skb_size + ETH_FCS_LEN,
- priv->prof->tx_pause,
- priv->prof->tx_ppp,
- priv->prof->rx_pause,
- priv->prof->rx_ppp);
- if (err)
- en_err(priv, "Failed setting pause params\n");
- else
- mlx4_en_update_pfc_stats_bitmap(mdev->dev, &priv->stats_bitmap,
- priv->prof->rx_ppp,
- priv->prof->rx_pause,
- priv->prof->tx_ppp,
- priv->prof->tx_pause);
+ tx_pause, tx_ppp, rx_pause, rx_ppp);
+ if (err) {
+ en_err(priv, "Failed setting pause params, err = %d\n", err);
+ return err;
+ }
+
+ mlx4_en_update_pfc_stats_bitmap(mdev->dev, &priv->stats_bitmap,
+ rx_ppp, rx_pause, tx_ppp, tx_pause);
+
+ priv->prof->tx_pause = tx_pause;
+ priv->prof->rx_pause = rx_pause;
+ priv->prof->tx_ppp = tx_ppp;
+ priv->prof->rx_ppp = rx_ppp;
return err;
}
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_main.c b/drivers/net/ethernet/mellanox/mlx4/en_main.c
index 2c2965497ed3..d25e16d2c319 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_main.c
@@ -163,9 +163,9 @@ static void mlx4_en_get_profile(struct mlx4_en_dev *mdev)
params->udp_rss = 0;
}
for (i = 1; i <= MLX4_MAX_PORTS; i++) {
- params->prof[i].rx_pause = 1;
+ params->prof[i].rx_pause = !(pfcrx || pfctx);
params->prof[i].rx_ppp = pfcrx;
- params->prof[i].tx_pause = 1;
+ params->prof[i].tx_pause = !(pfcrx || pfctx);
params->prof[i].tx_ppp = pfctx;
params->prof[i].tx_ring_size = MLX4_EN_DEF_TX_RING_SIZE;
params->prof[i].rx_ring_size = MLX4_EN_DEF_RX_RING_SIZE;
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
index 8fc51bc29003..e0adac4a9a19 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
@@ -3256,6 +3256,10 @@ void mlx4_en_set_stats_bitmap(struct mlx4_dev *dev,
bitmap_set(stats_bitmap->bitmap, last_i, NUM_XDP_STATS);
last_i += NUM_XDP_STATS;
+
+ if (!mlx4_is_slave(dev))
+ bitmap_set(stats_bitmap->bitmap, last_i, NUM_PHY_STATS);
+ last_i += NUM_PHY_STATS;
}
int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
@@ -3630,10 +3634,6 @@ int mlx4_en_reset_config(struct net_device *dev,
mlx4_en_stop_port(dev, 1);
}
- en_warn(priv, "Changing device configuration rx filter(%x) rx vlan(%x)\n",
- ts_config.rx_filter,
- !!(features & NETIF_F_HW_VLAN_CTAG_RX));
-
mlx4_en_safe_replace_resources(priv, tmp);
if (DEV_FEATURE_CHANGED(dev, features, NETIF_F_HW_VLAN_CTAG_RX)) {
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_port.c b/drivers/net/ethernet/mellanox/mlx4/en_port.c
index 1fa4849a6f56..0158b88bea5b 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_port.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_port.c
@@ -275,19 +275,31 @@ int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset)
priv->port_stats.xmit_more += READ_ONCE(ring->xmit_more);
}
- if (mlx4_is_master(mdev->dev)) {
- stats->rx_packets = en_stats_adder(&mlx4_en_stats->RTOT_prio_0,
- &mlx4_en_stats->RTOT_prio_1,
- NUM_PRIORITIES);
- stats->tx_packets = en_stats_adder(&mlx4_en_stats->TTOT_prio_0,
- &mlx4_en_stats->TTOT_prio_1,
- NUM_PRIORITIES);
- stats->rx_bytes = en_stats_adder(&mlx4_en_stats->ROCT_prio_0,
- &mlx4_en_stats->ROCT_prio_1,
- NUM_PRIORITIES);
- stats->tx_bytes = en_stats_adder(&mlx4_en_stats->TOCT_prio_0,
- &mlx4_en_stats->TOCT_prio_1,
- NUM_PRIORITIES);
+ if (!mlx4_is_slave(mdev->dev)) {
+ struct mlx4_en_phy_stats *p_stats = &priv->phy_stats;
+
+ p_stats->rx_packets_phy =
+ en_stats_adder(&mlx4_en_stats->RTOT_prio_0,
+ &mlx4_en_stats->RTOT_prio_1,
+ NUM_PRIORITIES);
+ p_stats->tx_packets_phy =
+ en_stats_adder(&mlx4_en_stats->TTOT_prio_0,
+ &mlx4_en_stats->TTOT_prio_1,
+ NUM_PRIORITIES);
+ p_stats->rx_bytes_phy =
+ en_stats_adder(&mlx4_en_stats->ROCT_prio_0,
+ &mlx4_en_stats->ROCT_prio_1,
+ NUM_PRIORITIES);
+ p_stats->tx_bytes_phy =
+ en_stats_adder(&mlx4_en_stats->TOCT_prio_0,
+ &mlx4_en_stats->TOCT_prio_1,
+ NUM_PRIORITIES);
+ if (mlx4_is_master(mdev->dev)) {
+ stats->rx_packets = p_stats->rx_packets_phy;
+ stats->tx_packets = p_stats->tx_packets_phy;
+ stats->rx_bytes = p_stats->rx_bytes_phy;
+ stats->tx_bytes = p_stats->tx_bytes_phy;
+ }
}
/* net device stats */
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
index b4d144e67514..5c613c6663da 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
@@ -291,13 +291,10 @@ int mlx4_en_create_rx_ring(struct mlx4_en_priv *priv,
tmp = size * roundup_pow_of_two(MLX4_EN_MAX_RX_FRAGS *
sizeof(struct mlx4_en_rx_alloc));
- ring->rx_info = vzalloc_node(tmp, node);
+ ring->rx_info = kvzalloc_node(tmp, GFP_KERNEL, node);
if (!ring->rx_info) {
- ring->rx_info = vzalloc(tmp);
- if (!ring->rx_info) {
- err = -ENOMEM;
- goto err_xdp_info;
- }
+ err = -ENOMEM;
+ goto err_xdp_info;
}
en_dbg(DRV, priv, "Allocated rx_info ring at addr:%p size:%d\n",
@@ -318,7 +315,7 @@ int mlx4_en_create_rx_ring(struct mlx4_en_priv *priv,
return 0;
err_info:
- vfree(ring->rx_info);
+ kvfree(ring->rx_info);
ring->rx_info = NULL;
err_xdp_info:
xdp_rxq_info_unreg(&ring->xdp_rxq);
@@ -447,7 +444,7 @@ void mlx4_en_destroy_rx_ring(struct mlx4_en_priv *priv,
bpf_prog_put(old_prog);
xdp_rxq_info_unreg(&ring->xdp_rxq);
mlx4_free_hwq_res(mdev->dev, &ring->wqres, size * stride + TXBB_SIZE);
- vfree(ring->rx_info);
+ kvfree(ring->rx_info);
ring->rx_info = NULL;
kfree(ring);
*pring = NULL;
@@ -649,6 +646,12 @@ static int check_csum(struct mlx4_cqe *cqe, struct sk_buff *skb, void *va,
return get_fixed_ipv4_csum(hw_checksum, skb, hdr);
}
+#if IS_ENABLED(CONFIG_IPV6)
+#define MLX4_CQE_STATUS_IP_ANY (MLX4_CQE_STATUS_IPV4 | MLX4_CQE_STATUS_IPV6)
+#else
+#define MLX4_CQE_STATUS_IP_ANY (MLX4_CQE_STATUS_IPV4)
+#endif
+
int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int budget)
{
struct mlx4_en_priv *priv = netdev_priv(dev);
@@ -662,12 +665,9 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
int polled = 0;
int index;
- if (unlikely(!priv->port_up))
+ if (unlikely(!priv->port_up || budget <= 0))
return 0;
- if (unlikely(budget <= 0))
- return polled;
-
ring = priv->rx_ring[cq_ring];
/* Protect accesses to: ring->xdp_prog, priv->mac_hash list */
@@ -821,14 +821,12 @@ xdp_drop_no_cnt:
skb_record_rx_queue(skb, cq_ring);
if (likely(dev->features & NETIF_F_RXCSUM)) {
- if (cqe->status & cpu_to_be16(MLX4_CQE_STATUS_TCP |
- MLX4_CQE_STATUS_UDP)) {
+ if ((cqe->status & cpu_to_be16(MLX4_CQE_STATUS_TCP |
+ MLX4_CQE_STATUS_UDP)) &&
+ (cqe->status & cpu_to_be16(MLX4_CQE_STATUS_IPOK)) &&
+ cqe->checksum == cpu_to_be16(0xffff)) {
bool l2_tunnel;
- if (!((cqe->status & cpu_to_be16(MLX4_CQE_STATUS_IPOK)) &&
- cqe->checksum == cpu_to_be16(0xffff)))
- goto csum_none;
-
l2_tunnel = (dev->hw_enc_features & NETIF_F_RXCSUM) &&
(cqe->vlan_my_qpn & cpu_to_be32(MLX4_CQE_L2_TUNNEL));
ip_summed = CHECKSUM_UNNECESSARY;
@@ -838,12 +836,7 @@ xdp_drop_no_cnt:
ring->csum_ok++;
} else {
if (!(priv->flags & MLX4_EN_FLAG_RX_CSUM_NON_TCP_UDP &&
- (cqe->status & cpu_to_be16(MLX4_CQE_STATUS_IPV4 |
-#if IS_ENABLED(CONFIG_IPV6)
- MLX4_CQE_STATUS_IPV6))))
-#else
- 0))))
-#endif
+ (cqe->status & cpu_to_be16(MLX4_CQE_STATUS_IP_ANY))))
goto csum_none;
if (check_csum(cqe, skb, va, dev->features))
goto csum_none;
diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c
index 634f603f941c..de6b3d416148 100644
--- a/drivers/net/ethernet/mellanox/mlx4/fw.c
+++ b/drivers/net/ethernet/mellanox/mlx4/fw.c
@@ -37,6 +37,7 @@
#include <linux/module.h>
#include <linux/cache.h>
#include <linux/kernel.h>
+#include <uapi/rdma/mlx4-abi.h>
#include "fw.h"
#include "icm.h"
diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c
index 4d84cab77105..bfef69235d71 100644
--- a/drivers/net/ethernet/mellanox/mlx4/main.c
+++ b/drivers/net/ethernet/mellanox/mlx4/main.c
@@ -46,6 +46,7 @@
#include <linux/etherdevice.h>
#include <net/devlink.h>
+#include <uapi/rdma/mlx4-abi.h>
#include <linux/mlx4/device.h>
#include <linux/mlx4/doorbell.h>
@@ -623,85 +624,6 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
return 0;
}
-static int mlx4_get_pcie_dev_link_caps(struct mlx4_dev *dev,
- enum pci_bus_speed *speed,
- enum pcie_link_width *width)
-{
- u32 lnkcap1, lnkcap2;
- int err1, err2;
-
-#define PCIE_MLW_CAP_SHIFT 4 /* start of MLW mask in link capabilities */
-
- *speed = PCI_SPEED_UNKNOWN;
- *width = PCIE_LNK_WIDTH_UNKNOWN;
-
- err1 = pcie_capability_read_dword(dev->persist->pdev, PCI_EXP_LNKCAP,
- &lnkcap1);
- err2 = pcie_capability_read_dword(dev->persist->pdev, PCI_EXP_LNKCAP2,
- &lnkcap2);
- if (!err2 && lnkcap2) { /* PCIe r3.0-compliant */
- if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_8_0GB)
- *speed = PCIE_SPEED_8_0GT;
- else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_5_0GB)
- *speed = PCIE_SPEED_5_0GT;
- else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_2_5GB)
- *speed = PCIE_SPEED_2_5GT;
- }
- if (!err1) {
- *width = (lnkcap1 & PCI_EXP_LNKCAP_MLW) >> PCIE_MLW_CAP_SHIFT;
- if (!lnkcap2) { /* pre-r3.0 */
- if (lnkcap1 & PCI_EXP_LNKCAP_SLS_5_0GB)
- *speed = PCIE_SPEED_5_0GT;
- else if (lnkcap1 & PCI_EXP_LNKCAP_SLS_2_5GB)
- *speed = PCIE_SPEED_2_5GT;
- }
- }
-
- if (*speed == PCI_SPEED_UNKNOWN || *width == PCIE_LNK_WIDTH_UNKNOWN) {
- return err1 ? err1 :
- err2 ? err2 : -EINVAL;
- }
- return 0;
-}
-
-static void mlx4_check_pcie_caps(struct mlx4_dev *dev)
-{
- enum pcie_link_width width, width_cap;
- enum pci_bus_speed speed, speed_cap;
- int err;
-
-#define PCIE_SPEED_STR(speed) \
- (speed == PCIE_SPEED_8_0GT ? "8.0GT/s" : \
- speed == PCIE_SPEED_5_0GT ? "5.0GT/s" : \
- speed == PCIE_SPEED_2_5GT ? "2.5GT/s" : \
- "Unknown")
-
- err = mlx4_get_pcie_dev_link_caps(dev, &speed_cap, &width_cap);
- if (err) {
- mlx4_warn(dev,
- "Unable to determine PCIe device BW capabilities\n");
- return;
- }
-
- err = pcie_get_minimum_link(dev->persist->pdev, &speed, &width);
- if (err || speed == PCI_SPEED_UNKNOWN ||
- width == PCIE_LNK_WIDTH_UNKNOWN) {
- mlx4_warn(dev,
- "Unable to determine PCI device chain minimum BW\n");
- return;
- }
-
- if (width != width_cap || speed != speed_cap)
- mlx4_warn(dev,
- "PCIe BW is different than device's capability\n");
-
- mlx4_info(dev, "PCIe link speed is %s, device supports %s\n",
- PCIE_SPEED_STR(speed), PCIE_SPEED_STR(speed_cap));
- mlx4_info(dev, "PCIe link width is x%d, device supports x%d\n",
- width, width_cap);
- return;
-}
-
/*The function checks if there are live vf, return the num of them*/
static int mlx4_how_many_lives_vf(struct mlx4_dev *dev)
{
@@ -2993,10 +2915,10 @@ static int mlx4_init_port_info(struct mlx4_dev *dev, int port)
sprintf(info->dev_name, "mlx4_port%d", port);
info->port_attr.attr.name = info->dev_name;
- if (mlx4_is_mfunc(dev))
- info->port_attr.attr.mode = S_IRUGO;
- else {
- info->port_attr.attr.mode = S_IRUGO | S_IWUSR;
+ if (mlx4_is_mfunc(dev)) {
+ info->port_attr.attr.mode = 0444;
+ } else {
+ info->port_attr.attr.mode = 0644;
info->port_attr.store = set_port_type;
}
info->port_attr.show = show_port_type;
@@ -3011,10 +2933,10 @@ static int mlx4_init_port_info(struct mlx4_dev *dev, int port)
sprintf(info->dev_mtu_name, "mlx4_port%d_mtu", port);
info->port_mtu_attr.attr.name = info->dev_mtu_name;
- if (mlx4_is_mfunc(dev))
- info->port_mtu_attr.attr.mode = S_IRUGO;
- else {
- info->port_mtu_attr.attr.mode = S_IRUGO | S_IWUSR;
+ if (mlx4_is_mfunc(dev)) {
+ info->port_mtu_attr.attr.mode = 0444;
+ } else {
+ info->port_mtu_attr.attr.mode = 0644;
info->port_mtu_attr.store = set_port_ib_mtu;
}
info->port_mtu_attr.show = show_port_ib_mtu;
@@ -3475,7 +3397,7 @@ slave_start:
* express device capabilities are under-satisfied by the bus.
*/
if (!mlx4_is_slave(dev))
- mlx4_check_pcie_caps(dev);
+ pcie_print_link_status(dev->persist->pdev);
/* In master functions, the communication channel must be initialized
* after obtaining its address from fw */
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
index f470ae37d937..f7c81133594f 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
@@ -608,6 +608,7 @@ struct mlx4_en_priv {
struct mlx4_en_flow_stats_tx tx_flowstats;
struct mlx4_en_port_stats port_stats;
struct mlx4_en_xdp_stats xdp_stats;
+ struct mlx4_en_phy_stats phy_stats;
struct mlx4_en_stats_bitmap stats_bitmap;
struct list_head mc_list;
struct list_head curr_list;
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_stats.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_stats.h
index aab28eb27a30..86b6051da8ec 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4_stats.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_stats.h
@@ -63,6 +63,14 @@ struct mlx4_en_xdp_stats {
#define NUM_XDP_STATS 3
};
+struct mlx4_en_phy_stats {
+ unsigned long rx_packets_phy;
+ unsigned long rx_bytes_phy;
+ unsigned long tx_packets_phy;
+ unsigned long tx_bytes_phy;
+#define NUM_PHY_STATS 4
+};
+
#define NUM_MAIN_STATS 21
#define MLX4_NUM_PRIORITIES 8
@@ -116,7 +124,7 @@ enum {
#define NUM_ALL_STATS (NUM_MAIN_STATS + NUM_PORT_STATS + NUM_PKT_STATS + \
NUM_FLOW_STATS + NUM_PERF_STATS + NUM_PF_STATS + \
- NUM_XDP_STATS)
+ NUM_XDP_STATS + NUM_PHY_STATS)
#define MLX4_FIND_NETDEV_STAT(n) (offsetof(struct net_device_stats, n) / \
sizeof(((struct net_device_stats *)0)->n))
diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
index 606a0e0beeae..29e50f787349 100644
--- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
+++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
@@ -5088,6 +5088,7 @@ static void rem_slave_fs_rule(struct mlx4_dev *dev, int slave)
&tracker->res_tree[RES_FS_RULE]);
list_del(&fs_rule->com.list);
spin_unlock_irq(mlx4_tlock(dev));
+ kfree(fs_rule->mirr_mbox);
kfree(fs_rule);
state = 0;
break;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig
index 25deaa5a534c..c032319f1cb9 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig
+++ b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig
@@ -46,7 +46,7 @@ config MLX5_MPFS
config MLX5_ESWITCH
bool "Mellanox Technologies MLX5 SRIOV E-Switch support"
- depends on MLX5_CORE_EN
+ depends on MLX5_CORE_EN && NET_SWITCHDEV
default y
---help---
Mellanox Technologies Ethernet SRIOV E-Switch support in ConnectX NIC.
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/accel/ipsec.c
index 53e69edaedde..9f1b1939716a 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/accel/ipsec.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/accel/ipsec.c
@@ -37,24 +37,11 @@
#include "mlx5_core.h"
#include "fpga/ipsec.h"
-void *mlx5_accel_ipsec_sa_cmd_exec(struct mlx5_core_dev *mdev,
- struct mlx5_accel_ipsec_sa *cmd)
-{
- if (!MLX5_IPSEC_DEV(mdev))
- return ERR_PTR(-EOPNOTSUPP);
-
- return mlx5_fpga_ipsec_sa_cmd_exec(mdev, cmd);
-}
-
-int mlx5_accel_ipsec_sa_cmd_wait(void *ctx)
-{
- return mlx5_fpga_ipsec_sa_cmd_wait(ctx);
-}
-
u32 mlx5_accel_ipsec_device_caps(struct mlx5_core_dev *mdev)
{
return mlx5_fpga_ipsec_device_caps(mdev);
}
+EXPORT_SYMBOL_GPL(mlx5_accel_ipsec_device_caps);
unsigned int mlx5_accel_ipsec_counters_count(struct mlx5_core_dev *mdev)
{
@@ -67,6 +54,21 @@ int mlx5_accel_ipsec_counters_read(struct mlx5_core_dev *mdev, u64 *counters,
return mlx5_fpga_ipsec_counters_read(mdev, counters, count);
}
+void *mlx5_accel_esp_create_hw_context(struct mlx5_core_dev *mdev,
+ struct mlx5_accel_esp_xfrm *xfrm,
+ const __be32 saddr[4],
+ const __be32 daddr[4],
+ const __be32 spi, bool is_ipv6)
+{
+ return mlx5_fpga_ipsec_create_sa_ctx(mdev, xfrm, saddr, daddr,
+ spi, is_ipv6);
+}
+
+void mlx5_accel_esp_free_hw_context(void *context)
+{
+ mlx5_fpga_ipsec_delete_sa_ctx(context);
+}
+
int mlx5_accel_ipsec_init(struct mlx5_core_dev *mdev)
{
return mlx5_fpga_ipsec_init(mdev);
@@ -76,3 +78,32 @@ void mlx5_accel_ipsec_cleanup(struct mlx5_core_dev *mdev)
{
mlx5_fpga_ipsec_cleanup(mdev);
}
+
+struct mlx5_accel_esp_xfrm *
+mlx5_accel_esp_create_xfrm(struct mlx5_core_dev *mdev,
+ const struct mlx5_accel_esp_xfrm_attrs *attrs,
+ u32 flags)
+{
+ struct mlx5_accel_esp_xfrm *xfrm;
+
+ xfrm = mlx5_fpga_esp_create_xfrm(mdev, attrs, flags);
+ if (IS_ERR(xfrm))
+ return xfrm;
+
+ xfrm->mdev = mdev;
+ return xfrm;
+}
+EXPORT_SYMBOL_GPL(mlx5_accel_esp_create_xfrm);
+
+void mlx5_accel_esp_destroy_xfrm(struct mlx5_accel_esp_xfrm *xfrm)
+{
+ mlx5_fpga_esp_destroy_xfrm(xfrm);
+}
+EXPORT_SYMBOL_GPL(mlx5_accel_esp_destroy_xfrm);
+
+int mlx5_accel_esp_modify_xfrm(struct mlx5_accel_esp_xfrm *xfrm,
+ const struct mlx5_accel_esp_xfrm_attrs *attrs)
+{
+ return mlx5_fpga_esp_modify_xfrm(xfrm, attrs);
+}
+EXPORT_SYMBOL_GPL(mlx5_accel_esp_modify_xfrm);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/accel/ipsec.h b/drivers/net/ethernet/mellanox/mlx5/core/accel/ipsec.h
index d6e20fea9554..024dbd22a89b 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/accel/ipsec.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/accel/ipsec.h
@@ -34,89 +34,25 @@
#ifndef __MLX5_ACCEL_IPSEC_H__
#define __MLX5_ACCEL_IPSEC_H__
-#ifdef CONFIG_MLX5_ACCEL
-
#include <linux/mlx5/driver.h>
+#include <linux/mlx5/accel.h>
-enum {
- MLX5_ACCEL_IPSEC_DEVICE = BIT(1),
- MLX5_ACCEL_IPSEC_IPV6 = BIT(2),
- MLX5_ACCEL_IPSEC_ESP = BIT(3),
- MLX5_ACCEL_IPSEC_LSO = BIT(4),
-};
-
-#define MLX5_IPSEC_SADB_IP_AH BIT(7)
-#define MLX5_IPSEC_SADB_IP_ESP BIT(6)
-#define MLX5_IPSEC_SADB_SA_VALID BIT(5)
-#define MLX5_IPSEC_SADB_SPI_EN BIT(4)
-#define MLX5_IPSEC_SADB_DIR_SX BIT(3)
-#define MLX5_IPSEC_SADB_IPV6 BIT(2)
-
-enum {
- MLX5_IPSEC_CMD_ADD_SA = 0,
- MLX5_IPSEC_CMD_DEL_SA = 1,
-};
-
-enum mlx5_accel_ipsec_enc_mode {
- MLX5_IPSEC_SADB_MODE_NONE = 0,
- MLX5_IPSEC_SADB_MODE_AES_GCM_128_AUTH_128 = 1,
- MLX5_IPSEC_SADB_MODE_AES_GCM_256_AUTH_128 = 3,
-};
+#ifdef CONFIG_MLX5_ACCEL
#define MLX5_IPSEC_DEV(mdev) (mlx5_accel_ipsec_device_caps(mdev) & \
- MLX5_ACCEL_IPSEC_DEVICE)
-
-struct mlx5_accel_ipsec_sa {
- __be32 cmd;
- u8 key_enc[32];
- u8 key_auth[32];
- __be32 sip[4];
- __be32 dip[4];
- union {
- struct {
- __be32 reserved;
- u8 salt_iv[8];
- __be32 salt;
- } __packed gcm;
- struct {
- u8 salt[16];
- } __packed cbc;
- };
- __be32 spi;
- __be32 sw_sa_handle;
- __be16 tfclen;
- u8 enc_mode;
- u8 sip_masklen;
- u8 dip_masklen;
- u8 flags;
- u8 reserved[2];
-} __packed;
-
-/**
- * mlx5_accel_ipsec_sa_cmd_exec - Execute an IPSec SADB command
- * @mdev: mlx5 device
- * @cmd: command to execute
- * May be called from atomic context. Returns context pointer, or error
- * Caller must eventually call mlx5_accel_ipsec_sa_cmd_wait from non-atomic
- * context, to cleanup the context pointer
- */
-void *mlx5_accel_ipsec_sa_cmd_exec(struct mlx5_core_dev *mdev,
- struct mlx5_accel_ipsec_sa *cmd);
-
-/**
- * mlx5_accel_ipsec_sa_cmd_wait - Wait for command execution completion
- * @context: Context pointer returned from call to mlx5_accel_ipsec_sa_cmd_exec
- * Sleeps (killable) until command execution is complete.
- * Returns the command result, or -EINTR if killed
- */
-int mlx5_accel_ipsec_sa_cmd_wait(void *context);
-
-u32 mlx5_accel_ipsec_device_caps(struct mlx5_core_dev *mdev);
+ MLX5_ACCEL_IPSEC_CAP_DEVICE)
unsigned int mlx5_accel_ipsec_counters_count(struct mlx5_core_dev *mdev);
int mlx5_accel_ipsec_counters_read(struct mlx5_core_dev *mdev, u64 *counters,
unsigned int count);
+void *mlx5_accel_esp_create_hw_context(struct mlx5_core_dev *mdev,
+ struct mlx5_accel_esp_xfrm *xfrm,
+ const __be32 saddr[4],
+ const __be32 daddr[4],
+ const __be32 spi, bool is_ipv6);
+void mlx5_accel_esp_free_hw_context(void *context);
+
int mlx5_accel_ipsec_init(struct mlx5_core_dev *mdev);
void mlx5_accel_ipsec_cleanup(struct mlx5_core_dev *mdev);
@@ -124,6 +60,20 @@ void mlx5_accel_ipsec_cleanup(struct mlx5_core_dev *mdev);
#define MLX5_IPSEC_DEV(mdev) false
+static inline void *
+mlx5_accel_esp_create_hw_context(struct mlx5_core_dev *mdev,
+ struct mlx5_accel_esp_xfrm *xfrm,
+ const __be32 saddr[4],
+ const __be32 daddr[4],
+ const __be32 spi, bool is_ipv6)
+{
+ return NULL;
+}
+
+static inline void mlx5_accel_esp_free_hw_context(void *context)
+{
+}
+
static inline int mlx5_accel_ipsec_init(struct mlx5_core_dev *mdev)
{
return 0;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/alloc.c b/drivers/net/ethernet/mellanox/mlx5/core/alloc.c
index 47239bf7bf43..323ffe8bf7e4 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/alloc.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/alloc.c
@@ -71,19 +71,24 @@ static void *mlx5_dma_zalloc_coherent_node(struct mlx5_core_dev *dev,
}
int mlx5_buf_alloc_node(struct mlx5_core_dev *dev, int size,
- struct mlx5_buf *buf, int node)
+ struct mlx5_frag_buf *buf, int node)
{
dma_addr_t t;
buf->size = size;
buf->npages = 1;
buf->page_shift = (u8)get_order(size) + PAGE_SHIFT;
- buf->direct.buf = mlx5_dma_zalloc_coherent_node(dev, size,
- &t, node);
- if (!buf->direct.buf)
+
+ buf->frags = kzalloc(sizeof(*buf->frags), GFP_KERNEL);
+ if (!buf->frags)
return -ENOMEM;
- buf->direct.map = t;
+ buf->frags->buf = mlx5_dma_zalloc_coherent_node(dev, size,
+ &t, node);
+ if (!buf->frags->buf)
+ goto err_out;
+
+ buf->frags->map = t;
while (t & ((1 << buf->page_shift) - 1)) {
--buf->page_shift;
@@ -91,18 +96,24 @@ int mlx5_buf_alloc_node(struct mlx5_core_dev *dev, int size,
}
return 0;
+err_out:
+ kfree(buf->frags);
+ return -ENOMEM;
}
-int mlx5_buf_alloc(struct mlx5_core_dev *dev, int size, struct mlx5_buf *buf)
+int mlx5_buf_alloc(struct mlx5_core_dev *dev,
+ int size, struct mlx5_frag_buf *buf)
{
return mlx5_buf_alloc_node(dev, size, buf, dev->priv.numa_node);
}
-EXPORT_SYMBOL_GPL(mlx5_buf_alloc);
+EXPORT_SYMBOL(mlx5_buf_alloc);
-void mlx5_buf_free(struct mlx5_core_dev *dev, struct mlx5_buf *buf)
+void mlx5_buf_free(struct mlx5_core_dev *dev, struct mlx5_frag_buf *buf)
{
- dma_free_coherent(&dev->pdev->dev, buf->size, buf->direct.buf,
- buf->direct.map);
+ dma_free_coherent(&dev->pdev->dev, buf->size, buf->frags->buf,
+ buf->frags->map);
+
+ kfree(buf->frags);
}
EXPORT_SYMBOL_GPL(mlx5_buf_free);
@@ -147,6 +158,7 @@ err_free_buf:
err_out:
return -ENOMEM;
}
+EXPORT_SYMBOL_GPL(mlx5_frag_buf_alloc_node);
void mlx5_frag_buf_free(struct mlx5_core_dev *dev, struct mlx5_frag_buf *buf)
{
@@ -162,6 +174,7 @@ void mlx5_frag_buf_free(struct mlx5_core_dev *dev, struct mlx5_frag_buf *buf)
}
kfree(buf->frags);
}
+EXPORT_SYMBOL_GPL(mlx5_frag_buf_free);
static struct mlx5_db_pgdir *mlx5_alloc_db_pgdir(struct mlx5_core_dev *dev,
int node)
@@ -275,13 +288,13 @@ void mlx5_db_free(struct mlx5_core_dev *dev, struct mlx5_db *db)
}
EXPORT_SYMBOL_GPL(mlx5_db_free);
-void mlx5_fill_page_array(struct mlx5_buf *buf, __be64 *pas)
+void mlx5_fill_page_array(struct mlx5_frag_buf *buf, __be64 *pas)
{
u64 addr;
int i;
for (i = 0; i < buf->npages; i++) {
- addr = buf->direct.map + (i << buf->page_shift);
+ addr = buf->frags->map + (i << buf->page_shift);
pas[i] = cpu_to_be64(addr);
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
index e9a1fbcc4adf..21cd1703a862 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
@@ -359,6 +359,7 @@ static int mlx5_internal_err_ret_value(struct mlx5_core_dev *dev, u16 op,
case MLX5_CMD_OP_MODIFY_HCA_VPORT_CONTEXT:
case MLX5_CMD_OP_QUERY_HCA_VPORT_GID:
case MLX5_CMD_OP_QUERY_HCA_VPORT_PKEY:
+ case MLX5_CMD_OP_QUERY_VNIC_ENV:
case MLX5_CMD_OP_QUERY_VPORT_COUNTER:
case MLX5_CMD_OP_ALLOC_Q_COUNTER:
case MLX5_CMD_OP_QUERY_Q_COUNTER:
@@ -501,6 +502,7 @@ const char *mlx5_command_str(int command)
MLX5_COMMAND_STR_CASE(MODIFY_HCA_VPORT_CONTEXT);
MLX5_COMMAND_STR_CASE(QUERY_HCA_VPORT_GID);
MLX5_COMMAND_STR_CASE(QUERY_HCA_VPORT_PKEY);
+ MLX5_COMMAND_STR_CASE(QUERY_VNIC_ENV);
MLX5_COMMAND_STR_CASE(QUERY_VPORT_COUNTER);
MLX5_COMMAND_STR_CASE(ALLOC_Q_COUNTER);
MLX5_COMMAND_STR_CASE(DEALLOC_Q_COUNTER);
@@ -1802,7 +1804,7 @@ int mlx5_cmd_init(struct mlx5_core_dev *dev)
cmd->checksum_disabled = 1;
cmd->max_reg_cmds = (1 << cmd->log_sz) - 1;
- cmd->bitmask = (1 << cmd->max_reg_cmds) - 1;
+ cmd->bitmask = (1UL << cmd->max_reg_cmds) - 1;
cmd->cmdif_rev = ioread32be(&dev->iseg->cmdif_rev_fw_sub) >> 16;
if (cmd->cmdif_rev > CMD_IF_REV) {
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cq.c b/drivers/net/ethernet/mellanox/mlx5/core/cq.c
index 1016e05c7ec7..a4179122a279 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/cq.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/cq.c
@@ -58,8 +58,7 @@ void mlx5_cq_tasklet_cb(unsigned long data)
tasklet_ctx.list) {
list_del_init(&mcq->tasklet_ctx.list);
mcq->tasklet_ctx.comp(mcq);
- if (refcount_dec_and_test(&mcq->refcount))
- complete(&mcq->free);
+ mlx5_cq_put(mcq);
if (time_after(jiffies, end))
break;
}
@@ -80,69 +79,19 @@ static void mlx5_add_cq_to_tasklet(struct mlx5_core_cq *cq)
* still arrive.
*/
if (list_empty_careful(&cq->tasklet_ctx.list)) {
- refcount_inc(&cq->refcount);
+ mlx5_cq_hold(cq);
list_add_tail(&cq->tasklet_ctx.list, &tasklet_ctx->list);
}
spin_unlock_irqrestore(&tasklet_ctx->lock, flags);
}
-void mlx5_cq_completion(struct mlx5_core_dev *dev, u32 cqn)
-{
- struct mlx5_core_cq *cq;
- struct mlx5_cq_table *table = &dev->priv.cq_table;
-
- spin_lock(&table->lock);
- cq = radix_tree_lookup(&table->tree, cqn);
- if (likely(cq))
- refcount_inc(&cq->refcount);
- spin_unlock(&table->lock);
-
- if (!cq) {
- mlx5_core_warn(dev, "Completion event for bogus CQ 0x%x\n", cqn);
- return;
- }
-
- ++cq->arm_sn;
-
- cq->comp(cq);
-
- if (refcount_dec_and_test(&cq->refcount))
- complete(&cq->free);
-}
-
-void mlx5_cq_event(struct mlx5_core_dev *dev, u32 cqn, int event_type)
-{
- struct mlx5_cq_table *table = &dev->priv.cq_table;
- struct mlx5_core_cq *cq;
-
- spin_lock(&table->lock);
-
- cq = radix_tree_lookup(&table->tree, cqn);
- if (cq)
- refcount_inc(&cq->refcount);
-
- spin_unlock(&table->lock);
-
- if (!cq) {
- mlx5_core_warn(dev, "Async event for bogus CQ 0x%x\n", cqn);
- return;
- }
-
- cq->event(cq, event_type);
-
- if (refcount_dec_and_test(&cq->refcount))
- complete(&cq->free);
-}
-
int mlx5_core_create_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq,
u32 *in, int inlen)
{
- struct mlx5_cq_table *table = &dev->priv.cq_table;
+ int eqn = MLX5_GET(cqc, MLX5_ADDR_OF(create_cq_in, in, cq_context), c_eqn);
+ u32 dout[MLX5_ST_SZ_DW(destroy_cq_out)];
u32 out[MLX5_ST_SZ_DW(create_cq_out)];
u32 din[MLX5_ST_SZ_DW(destroy_cq_in)];
- u32 dout[MLX5_ST_SZ_DW(destroy_cq_out)];
- int eqn = MLX5_GET(cqc, MLX5_ADDR_OF(create_cq_in, in, cq_context),
- c_eqn);
struct mlx5_eq *eq;
int err;
@@ -159,6 +108,7 @@ int mlx5_core_create_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq,
cq->cqn = MLX5_GET(create_cq_out, out, cqn);
cq->cons_index = 0;
cq->arm_sn = 0;
+ cq->eq = eq;
refcount_set(&cq->refcount, 1);
init_completion(&cq->free);
if (!cq->comp)
@@ -167,12 +117,16 @@ int mlx5_core_create_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq,
cq->tasklet_ctx.priv = &eq->tasklet_ctx;
INIT_LIST_HEAD(&cq->tasklet_ctx.list);
- spin_lock_irq(&table->lock);
- err = radix_tree_insert(&table->tree, cq->cqn, cq);
- spin_unlock_irq(&table->lock);
+ /* Add to comp EQ CQ tree to recv comp events */
+ err = mlx5_eq_add_cq(eq, cq);
if (err)
goto err_cmd;
+ /* Add to async EQ CQ tree to recv async events */
+ err = mlx5_eq_add_cq(&dev->priv.eq_table.async_eq, cq);
+ if (err)
+ goto err_cq_add;
+
cq->pid = current->pid;
err = mlx5_debug_cq_add(dev, cq);
if (err)
@@ -183,6 +137,8 @@ int mlx5_core_create_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq,
return 0;
+err_cq_add:
+ mlx5_eq_del_cq(eq, cq);
err_cmd:
memset(din, 0, sizeof(din));
memset(dout, 0, sizeof(dout));
@@ -195,23 +151,17 @@ EXPORT_SYMBOL(mlx5_core_create_cq);
int mlx5_core_destroy_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq)
{
- struct mlx5_cq_table *table = &dev->priv.cq_table;
u32 out[MLX5_ST_SZ_DW(destroy_cq_out)] = {0};
u32 in[MLX5_ST_SZ_DW(destroy_cq_in)] = {0};
- struct mlx5_core_cq *tmp;
int err;
- spin_lock_irq(&table->lock);
- tmp = radix_tree_delete(&table->tree, cq->cqn);
- spin_unlock_irq(&table->lock);
- if (!tmp) {
- mlx5_core_warn(dev, "cq 0x%x not found in tree\n", cq->cqn);
- return -EINVAL;
- }
- if (tmp != cq) {
- mlx5_core_warn(dev, "corruption on srqn 0x%x\n", cq->cqn);
- return -EINVAL;
- }
+ err = mlx5_eq_del_cq(&dev->priv.eq_table.async_eq, cq);
+ if (err)
+ return err;
+
+ err = mlx5_eq_del_cq(cq->eq, cq);
+ if (err)
+ return err;
MLX5_SET(destroy_cq_in, in, opcode, MLX5_CMD_OP_DESTROY_CQ);
MLX5_SET(destroy_cq_in, in, cqn, cq->cqn);
@@ -222,8 +172,7 @@ int mlx5_core_destroy_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq)
synchronize_irq(cq->irqn);
mlx5_debug_cq_remove(dev, cq);
- if (refcount_dec_and_test(&cq->refcount))
- complete(&cq->free);
+ mlx5_cq_put(cq);
wait_for_completion(&cq->free);
return 0;
@@ -270,21 +219,3 @@ int mlx5_core_modify_cq_moderation(struct mlx5_core_dev *dev,
return mlx5_core_modify_cq(dev, cq, in, sizeof(in));
}
EXPORT_SYMBOL(mlx5_core_modify_cq_moderation);
-
-int mlx5_init_cq_table(struct mlx5_core_dev *dev)
-{
- struct mlx5_cq_table *table = &dev->priv.cq_table;
- int err;
-
- memset(table, 0, sizeof(*table));
- spin_lock_init(&table->lock);
- INIT_RADIX_TREE(&table->tree, GFP_ATOMIC);
- err = mlx5_cq_debugfs_init(dev);
-
- return err;
-}
-
-void mlx5_cleanup_cq_table(struct mlx5_core_dev *dev)
-{
- mlx5_cq_debugfs_cleanup(dev);
-}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/dev.c b/drivers/net/ethernet/mellanox/mlx5/core/dev.c
index 17b723218b0c..b994b80d5714 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/dev.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/dev.c
@@ -337,6 +337,14 @@ void mlx5_unregister_interface(struct mlx5_interface *intf)
}
EXPORT_SYMBOL(mlx5_unregister_interface);
+void mlx5_reload_interface(struct mlx5_core_dev *mdev, int protocol)
+{
+ mutex_lock(&mlx5_intf_mutex);
+ mlx5_remove_dev_by_protocol(mdev, protocol);
+ mlx5_add_dev_by_protocol(mdev, protocol);
+ mutex_unlock(&mlx5_intf_mutex);
+}
+
void *mlx5_get_protocol_dev(struct mlx5_core_dev *mdev, int protocol)
{
struct mlx5_priv *priv = &mdev->priv;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.c b/drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.c
index fd509160c8f6..d93ff567b40d 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.c
@@ -246,6 +246,9 @@ const char *parse_fs_dst(struct trace_seq *p,
case MLX5_FLOW_DESTINATION_TYPE_COUNTER:
trace_seq_printf(p, "counter_id=%u\n", counter_id);
break;
+ case MLX5_FLOW_DESTINATION_TYPE_PORT:
+ trace_seq_printf(p, "port\n");
+ break;
}
trace_seq_putc(p, 0);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.h b/drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.h
index 80eef4163f52..09f178a3fcab 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.h
@@ -136,6 +136,8 @@ TRACE_EVENT(mlx5_fs_del_fg,
{MLX5_FLOW_CONTEXT_ACTION_ENCAP, "ENCAP"},\
{MLX5_FLOW_CONTEXT_ACTION_DECAP, "DECAP"},\
{MLX5_FLOW_CONTEXT_ACTION_MOD_HDR, "MOD_HDR"},\
+ {MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH, "VLAN_PUSH"},\
+ {MLX5_FLOW_CONTEXT_ACTION_VLAN_POP, "VLAN_POP"},\
{MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO, "NEXT_PRIO"}
TRACE_EVENT(mlx5_fs_set_fte,
@@ -163,9 +165,9 @@ TRACE_EVENT(mlx5_fs_set_fte,
fs_get_obj(__entry->fg, fte->node.parent);
__entry->group_index = __entry->fg->id;
__entry->index = fte->index;
- __entry->action = fte->action;
+ __entry->action = fte->action.action;
__entry->mask_enable = __entry->fg->mask.match_criteria_enable;
- __entry->flow_tag = fte->flow_tag;
+ __entry->flow_tag = fte->action.flow_tag;
memcpy(__entry->mask_outer,
MLX5_ADDR_OF(fte_match_param,
&__entry->fg->mask.match_criteria,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h
index 4c9360b25532..30cad07be2b5 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h
@@ -57,24 +57,12 @@
#define MLX5E_ETH_HARD_MTU (ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN)
-#define MLX5E_HW2SW_MTU(priv, hwmtu) ((hwmtu) - ((priv)->hard_mtu))
-#define MLX5E_SW2HW_MTU(priv, swmtu) ((swmtu) + ((priv)->hard_mtu))
+#define MLX5E_HW2SW_MTU(params, hwmtu) ((hwmtu) - ((params)->hard_mtu))
+#define MLX5E_SW2HW_MTU(params, swmtu) ((swmtu) + ((params)->hard_mtu))
#define MLX5E_MAX_DSCP 64
#define MLX5E_MAX_NUM_TC 8
-#define MLX5E_PARAMS_MINIMUM_LOG_SQ_SIZE 0x6
-#define MLX5E_PARAMS_DEFAULT_LOG_SQ_SIZE 0xa
-#define MLX5E_PARAMS_MAXIMUM_LOG_SQ_SIZE 0xd
-
-#define MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE 0x1
-#define MLX5E_PARAMS_DEFAULT_LOG_RQ_SIZE 0xa
-#define MLX5E_PARAMS_MAXIMUM_LOG_RQ_SIZE 0xd
-
-#define MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE_MPW 0x2
-#define MLX5E_PARAMS_DEFAULT_LOG_RQ_SIZE_MPW 0x3
-#define MLX5E_PARAMS_MAXIMUM_LOG_RQ_SIZE_MPW 0x6
-
#define MLX5_RX_HEADROOM NET_SKB_PAD
#define MLX5_SKB_FRAG_SZ(len) (SKB_DATA_ALIGN(len) + \
SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
@@ -93,15 +81,31 @@
#define MLX5_MPWRQ_WQE_PAGE_ORDER (MLX5_MPWRQ_LOG_WQE_SZ - PAGE_SHIFT > 0 ? \
MLX5_MPWRQ_LOG_WQE_SZ - PAGE_SHIFT : 0)
#define MLX5_MPWRQ_PAGES_PER_WQE BIT(MLX5_MPWRQ_WQE_PAGE_ORDER)
-#define MLX5_MPWRQ_STRIDES_PER_PAGE (MLX5_MPWRQ_NUM_STRIDES >> \
- MLX5_MPWRQ_WQE_PAGE_ORDER)
#define MLX5_MTT_OCTW(npages) (ALIGN(npages, 8) / 2)
-#define MLX5E_REQUIRED_MTTS(wqes) \
- (wqes * ALIGN(MLX5_MPWRQ_PAGES_PER_WQE, 8))
-#define MLX5E_VALID_NUM_MTTS(num_mtts) (MLX5_MTT_OCTW(num_mtts) - 1 <= U16_MAX)
+#define MLX5E_REQUIRED_WQE_MTTS (ALIGN(MLX5_MPWRQ_PAGES_PER_WQE, 8))
+#define MLX5E_LOG_ALIGNED_MPWQE_PPW (ilog2(MLX5E_REQUIRED_WQE_MTTS))
+#define MLX5E_REQUIRED_MTTS(wqes) (wqes * MLX5E_REQUIRED_WQE_MTTS)
+#define MLX5E_MAX_RQ_NUM_MTTS \
+ ((1 << 16) * 2) /* So that MLX5_MTT_OCTW(num_mtts) fits into u16 */
+#define MLX5E_ORDER2_MAX_PACKET_MTU (order_base_2(10 * 1024))
+#define MLX5E_PARAMS_MAXIMUM_LOG_RQ_SIZE_MPW \
+ (ilog2(MLX5E_MAX_RQ_NUM_MTTS / MLX5E_REQUIRED_WQE_MTTS))
+#define MLX5E_LOG_MAX_RQ_NUM_PACKETS_MPW \
+ (MLX5E_PARAMS_MAXIMUM_LOG_RQ_SIZE_MPW + \
+ (MLX5_MPWRQ_LOG_WQE_SZ - MLX5E_ORDER2_MAX_PACKET_MTU))
+
+#define MLX5E_PARAMS_MINIMUM_LOG_SQ_SIZE 0x6
+#define MLX5E_PARAMS_DEFAULT_LOG_SQ_SIZE 0xa
+#define MLX5E_PARAMS_MAXIMUM_LOG_SQ_SIZE 0xd
+
+#define MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE 0x1
+#define MLX5E_PARAMS_DEFAULT_LOG_RQ_SIZE 0xa
+#define MLX5E_PARAMS_MAXIMUM_LOG_RQ_SIZE min_t(u8, 0xd, \
+ MLX5E_LOG_MAX_RQ_NUM_PACKETS_MPW)
+
+#define MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE_MPW 0x2
-#define MLX5_UMR_ALIGN (2048)
#define MLX5_MPWRQ_SMALL_PACKET_THRESHOLD (256)
#define MLX5E_PARAMS_DEFAULT_LRO_WQE_SZ (64 * 1024)
@@ -124,9 +128,15 @@
#define MLX5E_MAX_NUM_SQS (MLX5E_MAX_NUM_CHANNELS * MLX5E_MAX_NUM_TC)
#define MLX5E_TX_CQ_POLL_BUDGET 128
#define MLX5E_UPDATE_STATS_INTERVAL 200 /* msecs */
+#define MLX5E_SQ_RECOVER_MIN_INTERVAL 500 /* msecs */
-#define MLX5E_ICOSQ_MAX_WQEBBS \
- (DIV_ROUND_UP(sizeof(struct mlx5e_umr_wqe), MLX5_SEND_WQE_BB))
+#define MLX5E_UMR_WQE_INLINE_SZ \
+ (sizeof(struct mlx5e_umr_wqe) + \
+ ALIGN(MLX5_MPWRQ_PAGES_PER_WQE * sizeof(struct mlx5_mtt), \
+ MLX5_UMR_MTT_ALIGNMENT))
+#define MLX5E_UMR_WQEBBS \
+ (DIV_ROUND_UP(MLX5E_UMR_WQE_INLINE_SZ, MLX5_SEND_WQE_BB))
+#define MLX5E_ICOSQ_MAX_WQEBBS MLX5E_UMR_WQEBBS
#define MLX5E_XDP_MIN_INLINE (ETH_HLEN + VLAN_HLEN)
#define MLX5E_XDP_TX_DS_COUNT \
@@ -156,26 +166,6 @@ static inline u16 mlx5_min_rx_wqes(int wq_type, u32 wq_size)
}
}
-static inline int mlx5_min_log_rq_size(int wq_type)
-{
- switch (wq_type) {
- case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ:
- return MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE_MPW;
- default:
- return MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE;
- }
-}
-
-static inline int mlx5_max_log_rq_size(int wq_type)
-{
- switch (wq_type) {
- case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ:
- return MLX5E_PARAMS_MAXIMUM_LOG_RQ_SIZE_MPW;
- default:
- return MLX5E_PARAMS_MAXIMUM_LOG_RQ_SIZE;
- }
-}
-
static inline int mlx5e_get_max_num_channels(struct mlx5_core_dev *mdev)
{
return is_kdump_kernel() ?
@@ -198,7 +188,7 @@ struct mlx5e_umr_wqe {
struct mlx5_wqe_ctrl_seg ctrl;
struct mlx5_wqe_umr_ctrl_seg uctrl;
struct mlx5_mkey_seg mkc;
- struct mlx5_wqe_data_seg data;
+ struct mlx5_mtt inline_mtts[0];
};
extern const char mlx5e_self_tests[][ETH_GSTRING_LEN];
@@ -207,12 +197,14 @@ static const char mlx5e_priv_flags[][ETH_GSTRING_LEN] = {
"rx_cqe_moder",
"tx_cqe_moder",
"rx_cqe_compress",
+ "rx_striding_rq",
};
enum mlx5e_priv_flag {
MLX5E_PFLAG_RX_CQE_BASED_MODER = (1 << 0),
MLX5E_PFLAG_TX_CQE_BASED_MODER = (1 << 1),
MLX5E_PFLAG_RX_CQE_COMPRESS = (1 << 2),
+ MLX5E_PFLAG_RX_STRIDING_RQ = (1 << 3),
};
#define MLX5E_SET_PFLAG(params, pflag, enable) \
@@ -232,10 +224,7 @@ enum mlx5e_priv_flag {
struct mlx5e_params {
u8 log_sq_size;
u8 rq_wq_type;
- u16 rq_headroom;
- u8 mpwqe_log_stride_sz;
- u8 mpwqe_log_num_strides;
- u8 log_rq_size;
+ u8 log_rq_mtu_frames;
u16 num_channels;
u8 num_tc;
bool rx_cqe_compress_def;
@@ -243,7 +232,6 @@ struct mlx5e_params {
struct net_dim_cq_moder tx_cq_moderation;
bool lro_en;
u32 lro_wqe_sz;
- u16 tx_max_inline;
u8 tx_min_inline_mode;
u8 rss_hfunc;
u8 toeplitz_hash_key[40];
@@ -254,6 +242,8 @@ struct mlx5e_params {
u32 lro_timeout;
u32 pflags;
struct bpf_prog *xdp_prog;
+ unsigned int sw_mtu;
+ int hard_mtu;
};
#ifdef CONFIG_MLX5_CORE_EN_DCB
@@ -336,6 +326,7 @@ struct mlx5e_sq_dma {
enum {
MLX5E_SQ_STATE_ENABLED,
+ MLX5E_SQ_STATE_RECOVERING,
MLX5E_SQ_STATE_IPSEC,
};
@@ -369,7 +360,6 @@ struct mlx5e_txqsq {
void __iomem *uar_map;
struct netdev_queue *txq;
u32 sqn;
- u16 max_inline;
u8 min_inline_mode;
u16 edge;
struct device *pdev;
@@ -383,6 +373,10 @@ struct mlx5e_txqsq {
struct mlx5e_channel *channel;
int txq_ix;
u32 rate_limit;
+ struct mlx5e_txqsq_recover {
+ struct work_struct recover_work;
+ u64 last_recover;
+ } recover;
} ____cacheline_aligned_in_smp;
struct mlx5e_xdpsq {
@@ -432,7 +426,6 @@ struct mlx5e_icosq {
void __iomem *uar_map;
u32 sqn;
u16 edge;
- __be32 mkey_be;
unsigned long state;
/* control path */
@@ -457,16 +450,13 @@ struct mlx5e_wqe_frag_info {
};
struct mlx5e_umr_dma_info {
- __be64 *mtt;
- dma_addr_t mtt_addr;
struct mlx5e_dma_info dma_info[MLX5_MPWRQ_PAGES_PER_WQE];
- struct mlx5e_umr_wqe wqe;
};
struct mlx5e_mpw_info {
struct mlx5e_umr_dma_info umr;
u16 consumed_strides;
- u16 skbs_frags[MLX5_MPWRQ_PAGES_PER_WQE];
+ DECLARE_BITMAP(xdp_xmit_bitmap, MLX5_MPWRQ_PAGES_PER_WQE);
};
/* a single cache unit is capable to serve one napi call (for non-striding rq)
@@ -483,9 +473,16 @@ struct mlx5e_page_cache {
struct mlx5e_rq;
typedef void (*mlx5e_fp_handle_rx_cqe)(struct mlx5e_rq*, struct mlx5_cqe64*);
+typedef struct sk_buff *
+(*mlx5e_fp_skb_from_cqe_mpwrq)(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi,
+ u16 cqe_bcnt, u32 head_offset, u32 page_idx);
typedef bool (*mlx5e_fp_post_rx_wqes)(struct mlx5e_rq *rq);
typedef void (*mlx5e_fp_dealloc_wqe)(struct mlx5e_rq*, u16);
+enum mlx5e_rq_flag {
+ MLX5E_RQ_FLAG_XDP_XMIT = BIT(0),
+};
+
struct mlx5e_rq {
/* data path */
struct mlx5_wq_ll wq;
@@ -496,12 +493,12 @@ struct mlx5e_rq {
u32 frag_sz; /* max possible skb frag_sz */
union {
bool page_reuse;
- bool xdp_xmit;
};
} wqe;
struct {
+ struct mlx5e_umr_wqe umr_wqe;
struct mlx5e_mpw_info *info;
- void *mtt_no_align;
+ mlx5e_fp_skb_from_cqe_mpwrq skb_from_cqe_mpwrq;
u16 num_strides;
u8 log_stride_sz;
bool umr_in_progress;
@@ -533,7 +530,9 @@ struct mlx5e_rq {
/* XDP */
struct bpf_prog *xdp_prog;
+ unsigned int hw_mtu;
struct mlx5e_xdpsq xdpsq;
+ DECLARE_BITMAP(flags, 8);
/* control */
struct mlx5_wq_ctrl wq_ctrl;
@@ -766,7 +765,6 @@ struct mlx5e_priv {
struct mlx5e_tir inner_indir_tir[MLX5E_NUM_INDIR_TIRS];
struct mlx5e_tir direct_tir[MLX5E_MAX_NUM_CHANNELS];
u32 tx_rates[MLX5E_MAX_NUM_SQS];
- int hard_mtu;
struct mlx5e_flow_steering fs;
struct mlx5e_vxlan_db vxlan;
@@ -781,7 +779,8 @@ struct mlx5e_priv {
struct net_device *netdev;
struct mlx5e_stats stats;
struct hwtstamp_config tstamp;
- u16 q_counter;
+ u16 q_counter;
+ u16 drop_rq_q_counter;
#ifdef CONFIG_MLX5_CORE_EN_DCB
struct mlx5e_dcbx dcbx;
#endif
@@ -831,6 +830,10 @@ bool mlx5e_poll_xdpsq_cq(struct mlx5e_cq *cq);
void mlx5e_free_txqsq_descs(struct mlx5e_txqsq *sq);
void mlx5e_free_xdpsq_descs(struct mlx5e_xdpsq *sq);
+bool mlx5e_check_fragmented_striding_rq_cap(struct mlx5_core_dev *mdev);
+bool mlx5e_striding_rq_possible(struct mlx5_core_dev *mdev,
+ struct mlx5e_params *params);
+
void mlx5e_page_release(struct mlx5e_rq *rq, struct mlx5e_dma_info *dma_info,
bool recycle);
void mlx5e_handle_rx_cqe(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe);
@@ -840,6 +843,12 @@ bool mlx5e_post_rx_mpwqes(struct mlx5e_rq *rq);
void mlx5e_dealloc_rx_wqe(struct mlx5e_rq *rq, u16 ix);
void mlx5e_dealloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix);
void mlx5e_free_rx_mpwqe(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi);
+struct sk_buff *
+mlx5e_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi,
+ u16 cqe_bcnt, u32 head_offset, u32 page_idx);
+struct sk_buff *
+mlx5e_skb_from_cqe_mpwrq_nonlinear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi,
+ u16 cqe_bcnt, u32 head_offset, u32 page_idx);
void mlx5e_update_stats(struct mlx5e_priv *priv);
@@ -916,9 +925,9 @@ void mlx5e_set_tx_cq_mode_params(struct mlx5e_params *params,
u8 cq_period_mode);
void mlx5e_set_rx_cq_mode_params(struct mlx5e_params *params,
u8 cq_period_mode);
+void mlx5e_set_rq_type(struct mlx5_core_dev *mdev, struct mlx5e_params *params);
void mlx5e_init_rq_type_params(struct mlx5_core_dev *mdev,
- struct mlx5e_params *params,
- u8 rq_type);
+ struct mlx5e_params *params);
static inline bool mlx5e_tunnel_inner_ft_supported(struct mlx5_core_dev *mdev)
{
@@ -970,11 +979,6 @@ static inline void mlx5e_cq_arm(struct mlx5e_cq *cq)
mlx5_cq_arm(mcq, MLX5_CQ_DB_REQ_NOT, mcq->uar->map, cq->wq.cc);
}
-static inline u32 mlx5e_get_wqe_mtt_offset(struct mlx5e_rq *rq, u16 wqe_ix)
-{
- return wqe_ix * ALIGN(MLX5_MPWRQ_PAGES_PER_WQE, 8);
-}
-
extern const struct ethtool_ops mlx5e_ethtool_ops;
#ifdef CONFIG_MLX5_CORE_EN_DCB
extern const struct dcbnl_rtnl_ops mlx5e_dcbnl_ops;
@@ -1010,7 +1014,6 @@ int mlx5e_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb,
u16 rxq_index, u32 flow_id);
#endif
-u16 mlx5e_get_max_inline_cap(struct mlx5_core_dev *mdev);
int mlx5e_create_tir(struct mlx5_core_dev *mdev,
struct mlx5e_tir *tir, u32 *in, int inlen);
void mlx5e_destroy_tir(struct mlx5_core_dev *mdev,
@@ -1061,7 +1064,6 @@ void mlx5e_cleanup_nic_tx(struct mlx5e_priv *priv);
int mlx5e_close(struct net_device *netdev);
int mlx5e_open(struct net_device *netdev);
void mlx5e_update_stats_work(struct work_struct *work);
-u32 mlx5e_choose_lro_timeout(struct mlx5_core_dev *mdev, u32 wanted_timeout);
int mlx5e_bits_invert(unsigned long a, int size);
@@ -1102,7 +1104,7 @@ void mlx5e_detach_netdev(struct mlx5e_priv *priv);
void mlx5e_destroy_netdev(struct mlx5e_priv *priv);
void mlx5e_build_nic_params(struct mlx5_core_dev *mdev,
struct mlx5e_params *params,
- u16 max_channels);
+ u16 max_channels, u16 mtu);
u8 mlx5e_params_calculate_tx_min_inline(struct mlx5_core_dev *mdev);
void mlx5e_rx_dim_work(struct work_struct *work);
#endif /* __MLX5_EN_H__ */
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c
index bac5103efad3..cf58c9637904 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c
@@ -38,17 +38,24 @@
#include <linux/module.h>
#include "en.h"
-#include "accel/ipsec.h"
#include "en_accel/ipsec.h"
#include "en_accel/ipsec_rxtx.h"
-struct mlx5e_ipsec_sa_entry {
- struct hlist_node hlist; /* Item in SADB_RX hashtable */
- unsigned int handle; /* Handle in SADB_RX */
- struct xfrm_state *x;
- struct mlx5e_ipsec *ipsec;
- void *context;
-};
+
+static struct mlx5e_ipsec_sa_entry *to_ipsec_sa_entry(struct xfrm_state *x)
+{
+ struct mlx5e_ipsec_sa_entry *sa;
+
+ if (!x)
+ return NULL;
+
+ sa = (struct mlx5e_ipsec_sa_entry *)x->xso.offload_handle;
+ if (!sa)
+ return NULL;
+
+ WARN_ON(sa->x != x);
+ return sa;
+}
struct xfrm_state *mlx5e_ipsec_sadb_rx_lookup(struct mlx5e_ipsec *ipsec,
unsigned int handle)
@@ -74,18 +81,16 @@ static int mlx5e_ipsec_sadb_rx_add(struct mlx5e_ipsec_sa_entry *sa_entry)
unsigned long flags;
int ret;
- spin_lock_irqsave(&ipsec->sadb_rx_lock, flags);
ret = ida_simple_get(&ipsec->halloc, 1, 0, GFP_KERNEL);
if (ret < 0)
- goto out;
+ return ret;
+ spin_lock_irqsave(&ipsec->sadb_rx_lock, flags);
sa_entry->handle = ret;
hash_add_rcu(ipsec->sadb_rx, &sa_entry->hlist, sa_entry->handle);
- ret = 0;
-
-out:
spin_unlock_irqrestore(&ipsec->sadb_rx_lock, flags);
- return ret;
+
+ return 0;
}
static void mlx5e_ipsec_sadb_rx_del(struct mlx5e_ipsec_sa_entry *sa_entry)
@@ -101,87 +106,99 @@ static void mlx5e_ipsec_sadb_rx_del(struct mlx5e_ipsec_sa_entry *sa_entry)
static void mlx5e_ipsec_sadb_rx_free(struct mlx5e_ipsec_sa_entry *sa_entry)
{
struct mlx5e_ipsec *ipsec = sa_entry->ipsec;
- unsigned long flags;
- /* Wait for the hash_del_rcu call in sadb_rx_del to affect data path */
- synchronize_rcu();
- spin_lock_irqsave(&ipsec->sadb_rx_lock, flags);
+ /* xfrm already doing sync rcu between del and free callbacks */
+
ida_simple_remove(&ipsec->halloc, sa_entry->handle);
- spin_unlock_irqrestore(&ipsec->sadb_rx_lock, flags);
}
-static enum mlx5_accel_ipsec_enc_mode mlx5e_ipsec_enc_mode(struct xfrm_state *x)
+static bool mlx5e_ipsec_update_esn_state(struct mlx5e_ipsec_sa_entry *sa_entry)
{
- unsigned int key_len = (x->aead->alg_key_len + 7) / 8 - 4;
-
- switch (key_len) {
- case 16:
- return MLX5_IPSEC_SADB_MODE_AES_GCM_128_AUTH_128;
- case 32:
- return MLX5_IPSEC_SADB_MODE_AES_GCM_256_AUTH_128;
- default:
- netdev_warn(x->xso.dev, "Bad key len: %d for alg %s\n",
- key_len, x->aead->alg_name);
- return -1;
+ struct xfrm_replay_state_esn *replay_esn;
+ u32 seq_bottom;
+ u8 overlap;
+ u32 *esn;
+
+ if (!(sa_entry->x->props.flags & XFRM_STATE_ESN)) {
+ sa_entry->esn_state.trigger = 0;
+ return false;
+ }
+
+ replay_esn = sa_entry->x->replay_esn;
+ seq_bottom = replay_esn->seq - replay_esn->replay_window + 1;
+ overlap = sa_entry->esn_state.overlap;
+
+ sa_entry->esn_state.esn = xfrm_replay_seqhi(sa_entry->x,
+ htonl(seq_bottom));
+ esn = &sa_entry->esn_state.esn;
+
+ sa_entry->esn_state.trigger = 1;
+ if (unlikely(overlap && seq_bottom < MLX5E_IPSEC_ESN_SCOPE_MID)) {
+ ++(*esn);
+ sa_entry->esn_state.overlap = 0;
+ return true;
+ } else if (unlikely(!overlap &&
+ (seq_bottom >= MLX5E_IPSEC_ESN_SCOPE_MID))) {
+ sa_entry->esn_state.overlap = 1;
+ return true;
}
+
+ return false;
}
-static void mlx5e_ipsec_build_hw_sa(u32 op, struct mlx5e_ipsec_sa_entry *sa_entry,
- struct mlx5_accel_ipsec_sa *hw_sa)
+static void
+mlx5e_ipsec_build_accel_xfrm_attrs(struct mlx5e_ipsec_sa_entry *sa_entry,
+ struct mlx5_accel_esp_xfrm_attrs *attrs)
{
struct xfrm_state *x = sa_entry->x;
+ struct aes_gcm_keymat *aes_gcm = &attrs->keymat.aes_gcm;
struct aead_geniv_ctx *geniv_ctx;
- unsigned int crypto_data_len;
struct crypto_aead *aead;
- unsigned int key_len;
+ unsigned int crypto_data_len, key_len;
int ivsize;
- memset(hw_sa, 0, sizeof(*hw_sa));
-
- if (op == MLX5_IPSEC_CMD_ADD_SA) {
- crypto_data_len = (x->aead->alg_key_len + 7) / 8;
- key_len = crypto_data_len - 4; /* 4 bytes salt at end */
- aead = x->data;
- geniv_ctx = crypto_aead_ctx(aead);
- ivsize = crypto_aead_ivsize(aead);
-
- memcpy(&hw_sa->key_enc, x->aead->alg_key, key_len);
- /* Duplicate 128 bit key twice according to HW layout */
- if (key_len == 16)
- memcpy(&hw_sa->key_enc[16], x->aead->alg_key, key_len);
- memcpy(&hw_sa->gcm.salt_iv, geniv_ctx->salt, ivsize);
- hw_sa->gcm.salt = *((__be32 *)(x->aead->alg_key + key_len));
- }
+ memset(attrs, 0, sizeof(*attrs));
- hw_sa->cmd = htonl(op);
- hw_sa->flags |= MLX5_IPSEC_SADB_SA_VALID | MLX5_IPSEC_SADB_SPI_EN;
- if (x->props.family == AF_INET) {
- hw_sa->sip[3] = x->props.saddr.a4;
- hw_sa->dip[3] = x->id.daddr.a4;
- hw_sa->sip_masklen = 32;
- hw_sa->dip_masklen = 32;
- } else {
- memcpy(hw_sa->sip, x->props.saddr.a6, sizeof(hw_sa->sip));
- memcpy(hw_sa->dip, x->id.daddr.a6, sizeof(hw_sa->dip));
- hw_sa->sip_masklen = 128;
- hw_sa->dip_masklen = 128;
- hw_sa->flags |= MLX5_IPSEC_SADB_IPV6;
- }
- hw_sa->spi = x->id.spi;
- hw_sa->sw_sa_handle = htonl(sa_entry->handle);
- switch (x->id.proto) {
- case IPPROTO_ESP:
- hw_sa->flags |= MLX5_IPSEC_SADB_IP_ESP;
- break;
- case IPPROTO_AH:
- hw_sa->flags |= MLX5_IPSEC_SADB_IP_AH;
- break;
- default:
- break;
+ /* key */
+ crypto_data_len = (x->aead->alg_key_len + 7) / 8;
+ key_len = crypto_data_len - 4; /* 4 bytes salt at end */
+
+ memcpy(aes_gcm->aes_key, x->aead->alg_key, key_len);
+ aes_gcm->key_len = key_len * 8;
+
+ /* salt and seq_iv */
+ aead = x->data;
+ geniv_ctx = crypto_aead_ctx(aead);
+ ivsize = crypto_aead_ivsize(aead);
+ memcpy(&aes_gcm->seq_iv, &geniv_ctx->salt, ivsize);
+ memcpy(&aes_gcm->salt, x->aead->alg_key + key_len,
+ sizeof(aes_gcm->salt));
+
+ /* iv len */
+ aes_gcm->icv_len = x->aead->alg_icv_len;
+
+ /* esn */
+ if (sa_entry->esn_state.trigger) {
+ attrs->flags |= MLX5_ACCEL_ESP_FLAGS_ESN_TRIGGERED;
+ attrs->esn = sa_entry->esn_state.esn;
+ if (sa_entry->esn_state.overlap)
+ attrs->flags |= MLX5_ACCEL_ESP_FLAGS_ESN_STATE_OVERLAP;
}
- hw_sa->enc_mode = mlx5e_ipsec_enc_mode(x);
- if (!(x->xso.flags & XFRM_OFFLOAD_INBOUND))
- hw_sa->flags |= MLX5_IPSEC_SADB_DIR_SX;
+
+ /* rx handle */
+ attrs->sa_handle = sa_entry->handle;
+
+ /* algo type */
+ attrs->keymat_type = MLX5_ACCEL_ESP_KEYMAT_AES_GCM;
+
+ /* action */
+ attrs->action = (!(x->xso.flags & XFRM_OFFLOAD_INBOUND)) ?
+ MLX5_ACCEL_ESP_ACTION_ENCRYPT :
+ MLX5_ACCEL_ESP_ACTION_DECRYPT;
+ /* flags */
+ attrs->flags |= (x->props.mode == XFRM_MODE_TRANSPORT) ?
+ MLX5_ACCEL_ESP_FLAGS_TRANSPORT :
+ MLX5_ACCEL_ESP_FLAGS_TUNNEL;
}
static inline int mlx5e_xfrm_validate_state(struct xfrm_state *x)
@@ -203,7 +220,9 @@ static inline int mlx5e_xfrm_validate_state(struct xfrm_state *x)
netdev_info(netdev, "Cannot offload compressed xfrm states\n");
return -EINVAL;
}
- if (x->props.flags & XFRM_STATE_ESN) {
+ if (x->props.flags & XFRM_STATE_ESN &&
+ !(mlx5_accel_ipsec_device_caps(priv->mdev) &
+ MLX5_ACCEL_IPSEC_CAP_ESN)) {
netdev_info(netdev, "Cannot offload ESN xfrm states\n");
return -EINVAL;
}
@@ -251,7 +270,8 @@ static inline int mlx5e_xfrm_validate_state(struct xfrm_state *x)
return -EINVAL;
}
if (x->props.family == AF_INET6 &&
- !(mlx5_accel_ipsec_device_caps(priv->mdev) & MLX5_ACCEL_IPSEC_IPV6)) {
+ !(mlx5_accel_ipsec_device_caps(priv->mdev) &
+ MLX5_ACCEL_IPSEC_CAP_IPV6)) {
netdev_info(netdev, "IPv6 xfrm state offload is not supported by this device\n");
return -EINVAL;
}
@@ -262,9 +282,10 @@ static int mlx5e_xfrm_add_state(struct xfrm_state *x)
{
struct mlx5e_ipsec_sa_entry *sa_entry = NULL;
struct net_device *netdev = x->xso.dev;
- struct mlx5_accel_ipsec_sa hw_sa;
+ struct mlx5_accel_esp_xfrm_attrs attrs;
struct mlx5e_priv *priv;
- void *context;
+ __be32 saddr[4] = {0}, daddr[4] = {0}, spi;
+ bool is_ipv6 = false;
int err;
priv = netdev_priv(netdev);
@@ -291,22 +312,49 @@ static int mlx5e_xfrm_add_state(struct xfrm_state *x)
netdev_info(netdev, "Failed adding to SADB_RX: %d\n", err);
goto err_entry;
}
+ } else {
+ sa_entry->set_iv_op = (x->props.flags & XFRM_STATE_ESN) ?
+ mlx5e_ipsec_set_iv_esn : mlx5e_ipsec_set_iv;
}
- mlx5e_ipsec_build_hw_sa(MLX5_IPSEC_CMD_ADD_SA, sa_entry, &hw_sa);
- context = mlx5_accel_ipsec_sa_cmd_exec(sa_entry->ipsec->en_priv->mdev, &hw_sa);
- if (IS_ERR(context)) {
- err = PTR_ERR(context);
+ /* check esn */
+ mlx5e_ipsec_update_esn_state(sa_entry);
+
+ /* create xfrm */
+ mlx5e_ipsec_build_accel_xfrm_attrs(sa_entry, &attrs);
+ sa_entry->xfrm =
+ mlx5_accel_esp_create_xfrm(priv->mdev, &attrs,
+ MLX5_ACCEL_XFRM_FLAG_REQUIRE_METADATA);
+ if (IS_ERR(sa_entry->xfrm)) {
+ err = PTR_ERR(sa_entry->xfrm);
goto err_sadb_rx;
}
- err = mlx5_accel_ipsec_sa_cmd_wait(context);
- if (err)
- goto err_sadb_rx;
+ /* create hw context */
+ if (x->props.family == AF_INET) {
+ saddr[3] = x->props.saddr.a4;
+ daddr[3] = x->id.daddr.a4;
+ } else {
+ memcpy(saddr, x->props.saddr.a6, sizeof(saddr));
+ memcpy(daddr, x->id.daddr.a6, sizeof(daddr));
+ is_ipv6 = true;
+ }
+ spi = x->id.spi;
+ sa_entry->hw_context =
+ mlx5_accel_esp_create_hw_context(priv->mdev,
+ sa_entry->xfrm,
+ saddr, daddr, spi,
+ is_ipv6);
+ if (IS_ERR(sa_entry->hw_context)) {
+ err = PTR_ERR(sa_entry->hw_context);
+ goto err_xfrm;
+ }
x->xso.offload_handle = (unsigned long)sa_entry;
goto out;
+err_xfrm:
+ mlx5_accel_esp_destroy_xfrm(sa_entry->xfrm);
err_sadb_rx:
if (x->xso.flags & XFRM_OFFLOAD_INBOUND) {
mlx5e_ipsec_sadb_rx_del(sa_entry);
@@ -320,43 +368,26 @@ out:
static void mlx5e_xfrm_del_state(struct xfrm_state *x)
{
- struct mlx5e_ipsec_sa_entry *sa_entry;
- struct mlx5_accel_ipsec_sa hw_sa;
- void *context;
+ struct mlx5e_ipsec_sa_entry *sa_entry = to_ipsec_sa_entry(x);
- if (!x->xso.offload_handle)
+ if (!sa_entry)
return;
- sa_entry = (struct mlx5e_ipsec_sa_entry *)x->xso.offload_handle;
- WARN_ON(sa_entry->x != x);
-
if (x->xso.flags & XFRM_OFFLOAD_INBOUND)
mlx5e_ipsec_sadb_rx_del(sa_entry);
-
- mlx5e_ipsec_build_hw_sa(MLX5_IPSEC_CMD_DEL_SA, sa_entry, &hw_sa);
- context = mlx5_accel_ipsec_sa_cmd_exec(sa_entry->ipsec->en_priv->mdev, &hw_sa);
- if (IS_ERR(context))
- return;
-
- sa_entry->context = context;
}
static void mlx5e_xfrm_free_state(struct xfrm_state *x)
{
- struct mlx5e_ipsec_sa_entry *sa_entry;
- int res;
+ struct mlx5e_ipsec_sa_entry *sa_entry = to_ipsec_sa_entry(x);
- if (!x->xso.offload_handle)
+ if (!sa_entry)
return;
- sa_entry = (struct mlx5e_ipsec_sa_entry *)x->xso.offload_handle;
- WARN_ON(sa_entry->x != x);
-
- res = mlx5_accel_ipsec_sa_cmd_wait(sa_entry->context);
- sa_entry->context = NULL;
- if (res) {
- /* Leftover object will leak */
- return;
+ if (sa_entry->hw_context) {
+ flush_workqueue(sa_entry->ipsec->wq);
+ mlx5_accel_esp_free_hw_context(sa_entry->hw_context);
+ mlx5_accel_esp_destroy_xfrm(sa_entry->xfrm);
}
if (x->xso.flags & XFRM_OFFLOAD_INBOUND)
@@ -383,6 +414,14 @@ int mlx5e_ipsec_init(struct mlx5e_priv *priv)
ida_init(&ipsec->halloc);
ipsec->en_priv = priv;
ipsec->en_priv->ipsec = ipsec;
+ ipsec->no_trailer = !!(mlx5_accel_ipsec_device_caps(priv->mdev) &
+ MLX5_ACCEL_IPSEC_CAP_RX_NO_TRAILER);
+ ipsec->wq = alloc_ordered_workqueue("mlx5e_ipsec: %s", 0,
+ priv->netdev->name);
+ if (!ipsec->wq) {
+ kfree(ipsec);
+ return -ENOMEM;
+ }
netdev_dbg(priv->netdev, "IPSec attached to netdevice\n");
return 0;
}
@@ -394,6 +433,9 @@ void mlx5e_ipsec_cleanup(struct mlx5e_priv *priv)
if (!ipsec)
return;
+ drain_workqueue(ipsec->wq);
+ destroy_workqueue(ipsec->wq);
+
ida_destroy(&ipsec->halloc);
kfree(ipsec);
priv->ipsec = NULL;
@@ -414,11 +456,58 @@ static bool mlx5e_ipsec_offload_ok(struct sk_buff *skb, struct xfrm_state *x)
return true;
}
+struct mlx5e_ipsec_modify_state_work {
+ struct work_struct work;
+ struct mlx5_accel_esp_xfrm_attrs attrs;
+ struct mlx5e_ipsec_sa_entry *sa_entry;
+};
+
+static void _update_xfrm_state(struct work_struct *work)
+{
+ int ret;
+ struct mlx5e_ipsec_modify_state_work *modify_work =
+ container_of(work, struct mlx5e_ipsec_modify_state_work, work);
+ struct mlx5e_ipsec_sa_entry *sa_entry = modify_work->sa_entry;
+
+ ret = mlx5_accel_esp_modify_xfrm(sa_entry->xfrm,
+ &modify_work->attrs);
+ if (ret)
+ netdev_warn(sa_entry->ipsec->en_priv->netdev,
+ "Not an IPSec offload device\n");
+
+ kfree(modify_work);
+}
+
+static void mlx5e_xfrm_advance_esn_state(struct xfrm_state *x)
+{
+ struct mlx5e_ipsec_sa_entry *sa_entry = to_ipsec_sa_entry(x);
+ struct mlx5e_ipsec_modify_state_work *modify_work;
+ bool need_update;
+
+ if (!sa_entry)
+ return;
+
+ need_update = mlx5e_ipsec_update_esn_state(sa_entry);
+ if (!need_update)
+ return;
+
+ modify_work = kzalloc(sizeof(*modify_work), GFP_ATOMIC);
+ if (!modify_work)
+ return;
+
+ mlx5e_ipsec_build_accel_xfrm_attrs(sa_entry, &modify_work->attrs);
+ modify_work->sa_entry = sa_entry;
+
+ INIT_WORK(&modify_work->work, _update_xfrm_state);
+ WARN_ON(!queue_work(sa_entry->ipsec->wq, &modify_work->work));
+}
+
static const struct xfrmdev_ops mlx5e_ipsec_xfrmdev_ops = {
.xdo_dev_state_add = mlx5e_xfrm_add_state,
.xdo_dev_state_delete = mlx5e_xfrm_del_state,
.xdo_dev_state_free = mlx5e_xfrm_free_state,
.xdo_dev_offload_ok = mlx5e_ipsec_offload_ok,
+ .xdo_dev_state_advance_esn = mlx5e_xfrm_advance_esn_state,
};
void mlx5e_ipsec_build_netdev(struct mlx5e_priv *priv)
@@ -429,7 +518,7 @@ void mlx5e_ipsec_build_netdev(struct mlx5e_priv *priv)
if (!priv->ipsec)
return;
- if (!(mlx5_accel_ipsec_device_caps(mdev) & MLX5_ACCEL_IPSEC_ESP) ||
+ if (!(mlx5_accel_ipsec_device_caps(mdev) & MLX5_ACCEL_IPSEC_CAP_ESP) ||
!MLX5_CAP_ETH(mdev, swp)) {
mlx5_core_dbg(mdev, "mlx5e: ESP and SWP offload not supported\n");
return;
@@ -448,7 +537,7 @@ void mlx5e_ipsec_build_netdev(struct mlx5e_priv *priv)
netdev->features |= NETIF_F_HW_ESP_TX_CSUM;
netdev->hw_enc_features |= NETIF_F_HW_ESP_TX_CSUM;
- if (!(mlx5_accel_ipsec_device_caps(mdev) & MLX5_ACCEL_IPSEC_LSO) ||
+ if (!(mlx5_accel_ipsec_device_caps(mdev) & MLX5_ACCEL_IPSEC_CAP_LSO) ||
!MLX5_CAP_ETH(mdev, swp_lso)) {
mlx5_core_dbg(mdev, "mlx5e: ESP LSO not supported\n");
return;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h
index 56e00baf16cc..1198fc1eba4c 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h
@@ -40,7 +40,11 @@
#include <net/xfrm.h>
#include <linux/idr.h>
+#include "accel/ipsec.h"
+
#define MLX5E_IPSEC_SADB_RX_BITS 10
+#define MLX5E_IPSEC_ESN_SCOPE_MID 0x80000000L
+
#define MLX5E_METADATA_ETHER_TYPE (0x8CE4)
#define MLX5E_METADATA_ETHER_LEN 8
@@ -77,10 +81,30 @@ struct mlx5e_ipsec_stats {
struct mlx5e_ipsec {
struct mlx5e_priv *en_priv;
DECLARE_HASHTABLE(sadb_rx, MLX5E_IPSEC_SADB_RX_BITS);
+ bool no_trailer;
spinlock_t sadb_rx_lock; /* Protects sadb_rx and halloc */
struct ida halloc;
struct mlx5e_ipsec_sw_stats sw_stats;
struct mlx5e_ipsec_stats stats;
+ struct workqueue_struct *wq;
+};
+
+struct mlx5e_ipsec_esn_state {
+ u32 esn;
+ u8 trigger: 1;
+ u8 overlap: 1;
+};
+
+struct mlx5e_ipsec_sa_entry {
+ struct hlist_node hlist; /* Item in SADB_RX hashtable */
+ struct mlx5e_ipsec_esn_state esn_state;
+ unsigned int handle; /* Handle in SADB_RX */
+ struct xfrm_state *x;
+ struct mlx5e_ipsec *ipsec;
+ struct mlx5_accel_esp_xfrm *xfrm;
+ void *hw_context;
+ void (*set_iv_op)(struct sk_buff *skb, struct xfrm_state *x,
+ struct xfrm_offload *xo);
};
void mlx5e_ipsec_build_inverse_table(void);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c
index 6a7c8b04447e..c245d8e78509 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c
@@ -42,10 +42,11 @@
enum {
MLX5E_IPSEC_RX_SYNDROME_DECRYPTED = 0x11,
MLX5E_IPSEC_RX_SYNDROME_AUTH_FAILED = 0x12,
+ MLX5E_IPSEC_RX_SYNDROME_BAD_PROTO = 0x17,
};
struct mlx5e_ipsec_rx_metadata {
- unsigned char reserved;
+ unsigned char nexthdr;
__be32 sa_handle;
} __packed;
@@ -175,7 +176,30 @@ static void mlx5e_ipsec_set_swp(struct sk_buff *skb,
}
}
-static void mlx5e_ipsec_set_iv(struct sk_buff *skb, struct xfrm_offload *xo)
+void mlx5e_ipsec_set_iv_esn(struct sk_buff *skb, struct xfrm_state *x,
+ struct xfrm_offload *xo)
+{
+ struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
+ __u32 oseq = replay_esn->oseq;
+ int iv_offset;
+ __be64 seqno;
+ u32 seq_hi;
+
+ if (unlikely(skb_is_gso(skb) && oseq < MLX5E_IPSEC_ESN_SCOPE_MID &&
+ MLX5E_IPSEC_ESN_SCOPE_MID < (oseq - skb_shinfo(skb)->gso_segs))) {
+ seq_hi = xo->seq.hi - 1;
+ } else {
+ seq_hi = xo->seq.hi;
+ }
+
+ /* Place the SN in the IV field */
+ seqno = cpu_to_be64(xo->seq.low + ((u64)seq_hi << 32));
+ iv_offset = skb_transport_offset(skb) + sizeof(struct ip_esp_hdr);
+ skb_store_bits(skb, iv_offset, &seqno, 8);
+}
+
+void mlx5e_ipsec_set_iv(struct sk_buff *skb, struct xfrm_state *x,
+ struct xfrm_offload *xo)
{
int iv_offset;
__be64 seqno;
@@ -227,6 +251,7 @@ struct sk_buff *mlx5e_ipsec_handle_tx_skb(struct net_device *netdev,
struct mlx5e_priv *priv = netdev_priv(netdev);
struct xfrm_offload *xo = xfrm_offload(skb);
struct mlx5e_ipsec_metadata *mdata;
+ struct mlx5e_ipsec_sa_entry *sa_entry;
struct xfrm_state *x;
if (!xo)
@@ -261,7 +286,8 @@ struct sk_buff *mlx5e_ipsec_handle_tx_skb(struct net_device *netdev,
goto drop;
}
mlx5e_ipsec_set_swp(skb, &wqe->eth, x->props.mode, xo);
- mlx5e_ipsec_set_iv(skb, xo);
+ sa_entry = (struct mlx5e_ipsec_sa_entry *)x->xso.offload_handle;
+ sa_entry->set_iv_op(skb, x, xo);
mlx5e_ipsec_set_metadata(skb, mdata, xo);
return skb;
@@ -301,10 +327,17 @@ mlx5e_ipsec_build_sp(struct net_device *netdev, struct sk_buff *skb,
switch (mdata->syndrome) {
case MLX5E_IPSEC_RX_SYNDROME_DECRYPTED:
xo->status = CRYPTO_SUCCESS;
+ if (likely(priv->ipsec->no_trailer)) {
+ xo->flags |= XFRM_ESP_NO_TRAILER;
+ xo->proto = mdata->content.rx.nexthdr;
+ }
break;
case MLX5E_IPSEC_RX_SYNDROME_AUTH_FAILED:
xo->status = CRYPTO_TUNNEL_ESP_AUTH_FAILED;
break;
+ case MLX5E_IPSEC_RX_SYNDROME_BAD_PROTO:
+ xo->status = CRYPTO_INVALID_PROTOCOL;
+ break;
default:
atomic64_inc(&priv->ipsec->sw_stats.ipsec_rx_drop_syndrome);
return NULL;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.h
index e37ae2598dbb..2bfbbef1b054 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.h
@@ -37,6 +37,7 @@
#ifdef CONFIG_MLX5_EN_IPSEC
#include <linux/skbuff.h>
+#include <net/xfrm.h>
#include "en.h"
struct sk_buff *mlx5e_ipsec_handle_rx_skb(struct net_device *netdev,
@@ -46,6 +47,10 @@ void mlx5e_ipsec_handle_rx_cqe(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe);
void mlx5e_ipsec_inverse_table_init(void);
bool mlx5e_ipsec_feature_check(struct sk_buff *skb, struct net_device *netdev,
netdev_features_t features);
+void mlx5e_ipsec_set_iv_esn(struct sk_buff *skb, struct xfrm_state *x,
+ struct xfrm_offload *xo);
+void mlx5e_ipsec_set_iv(struct sk_buff *skb, struct xfrm_state *x,
+ struct xfrm_offload *xo);
struct sk_buff *mlx5e_ipsec_handle_tx_skb(struct net_device *netdev,
struct mlx5e_tx_wqe *wqe,
struct sk_buff *skb);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_common.c b/drivers/net/ethernet/mellanox/mlx5/core/en_common.c
index 784e282803db..db3278cc052b 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_common.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_common.c
@@ -70,7 +70,7 @@ static int mlx5e_create_mkey(struct mlx5_core_dev *mdev, u32 pdn,
return -ENOMEM;
mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry);
- MLX5_SET(mkc, mkc, access_mode, MLX5_MKC_ACCESS_MODE_PA);
+ MLX5_SET(mkc, mkc, access_mode_1_0, MLX5_MKC_ACCESS_MODE_PA);
MLX5_SET(mkc, mkc, lw, 1);
MLX5_SET(mkc, mkc, lr, 1);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
index cc8048f68f11..37fd0245b6c1 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
@@ -203,9 +203,6 @@ void mlx5e_ethtool_get_ethtool_stats(struct mlx5e_priv *priv,
{
int i, idx = 0;
- if (!data)
- return;
-
mutex_lock(&priv->state_lock);
mlx5e_update_stats(priv);
mutex_unlock(&priv->state_lock);
@@ -223,60 +220,12 @@ static void mlx5e_get_ethtool_stats(struct net_device *dev,
mlx5e_ethtool_get_ethtool_stats(priv, stats, data);
}
-static u32 mlx5e_rx_wqes_to_packets(struct mlx5e_priv *priv, int rq_wq_type,
- int num_wqe)
-{
- int packets_per_wqe;
- int stride_size;
- int num_strides;
- int wqe_size;
-
- if (rq_wq_type != MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ)
- return num_wqe;
-
- stride_size = 1 << priv->channels.params.mpwqe_log_stride_sz;
- num_strides = 1 << priv->channels.params.mpwqe_log_num_strides;
- wqe_size = stride_size * num_strides;
-
- packets_per_wqe = wqe_size /
- ALIGN(ETH_DATA_LEN, stride_size);
- return (1 << (order_base_2(num_wqe * packets_per_wqe) - 1));
-}
-
-static u32 mlx5e_packets_to_rx_wqes(struct mlx5e_priv *priv, int rq_wq_type,
- int num_packets)
-{
- int packets_per_wqe;
- int stride_size;
- int num_strides;
- int wqe_size;
- int num_wqes;
-
- if (rq_wq_type != MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ)
- return num_packets;
-
- stride_size = 1 << priv->channels.params.mpwqe_log_stride_sz;
- num_strides = 1 << priv->channels.params.mpwqe_log_num_strides;
- wqe_size = stride_size * num_strides;
-
- num_packets = (1 << order_base_2(num_packets));
-
- packets_per_wqe = wqe_size /
- ALIGN(ETH_DATA_LEN, stride_size);
- num_wqes = DIV_ROUND_UP(num_packets, packets_per_wqe);
- return 1 << (order_base_2(num_wqes));
-}
-
void mlx5e_ethtool_get_ringparam(struct mlx5e_priv *priv,
struct ethtool_ringparam *param)
{
- int rq_wq_type = priv->channels.params.rq_wq_type;
-
- param->rx_max_pending = mlx5e_rx_wqes_to_packets(priv, rq_wq_type,
- 1 << mlx5_max_log_rq_size(rq_wq_type));
+ param->rx_max_pending = 1 << MLX5E_PARAMS_MAXIMUM_LOG_RQ_SIZE;
param->tx_max_pending = 1 << MLX5E_PARAMS_MAXIMUM_LOG_SQ_SIZE;
- param->rx_pending = mlx5e_rx_wqes_to_packets(priv, rq_wq_type,
- 1 << priv->channels.params.log_rq_size);
+ param->rx_pending = 1 << priv->channels.params.log_rq_mtu_frames;
param->tx_pending = 1 << priv->channels.params.log_sq_size;
}
@@ -291,13 +240,9 @@ static void mlx5e_get_ringparam(struct net_device *dev,
int mlx5e_ethtool_set_ringparam(struct mlx5e_priv *priv,
struct ethtool_ringparam *param)
{
- int rq_wq_type = priv->channels.params.rq_wq_type;
struct mlx5e_channels new_channels = {};
- u32 rx_pending_wqes;
- u32 min_rq_size;
u8 log_rq_size;
u8 log_sq_size;
- u32 num_mtts;
int err = 0;
if (param->rx_jumbo_pending) {
@@ -311,23 +256,10 @@ int mlx5e_ethtool_set_ringparam(struct mlx5e_priv *priv,
return -EINVAL;
}
- min_rq_size = mlx5e_rx_wqes_to_packets(priv, rq_wq_type,
- 1 << mlx5_min_log_rq_size(rq_wq_type));
- rx_pending_wqes = mlx5e_packets_to_rx_wqes(priv, rq_wq_type,
- param->rx_pending);
-
- if (param->rx_pending < min_rq_size) {
+ if (param->rx_pending < (1 << MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE)) {
netdev_info(priv->netdev, "%s: rx_pending (%d) < min (%d)\n",
__func__, param->rx_pending,
- min_rq_size);
- return -EINVAL;
- }
-
- num_mtts = MLX5E_REQUIRED_MTTS(rx_pending_wqes);
- if (priv->channels.params.rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ &&
- !MLX5E_VALID_NUM_MTTS(num_mtts)) {
- netdev_info(priv->netdev, "%s: rx_pending (%d) request can't be satisfied, try to reduce.\n",
- __func__, param->rx_pending);
+ 1 << MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE);
return -EINVAL;
}
@@ -338,17 +270,17 @@ int mlx5e_ethtool_set_ringparam(struct mlx5e_priv *priv,
return -EINVAL;
}
- log_rq_size = order_base_2(rx_pending_wqes);
+ log_rq_size = order_base_2(param->rx_pending);
log_sq_size = order_base_2(param->tx_pending);
- if (log_rq_size == priv->channels.params.log_rq_size &&
+ if (log_rq_size == priv->channels.params.log_rq_mtu_frames &&
log_sq_size == priv->channels.params.log_sq_size)
return 0;
mutex_lock(&priv->state_lock);
new_channels.params = priv->channels.params;
- new_channels.params.log_rq_size = log_rq_size;
+ new_channels.params.log_rq_mtu_frames = log_rq_size;
new_channels.params.log_sq_size = log_sq_size;
if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) {
@@ -477,6 +409,9 @@ static int mlx5e_get_coalesce(struct net_device *netdev,
return mlx5e_ethtool_get_coalesce(priv, coal);
}
+#define MLX5E_MAX_COAL_TIME MLX5_MAX_CQ_PERIOD
+#define MLX5E_MAX_COAL_FRAMES MLX5_MAX_CQ_COUNT
+
static void
mlx5e_set_priv_channels_coalesce(struct mlx5e_priv *priv, struct ethtool_coalesce *coal)
{
@@ -511,6 +446,20 @@ int mlx5e_ethtool_set_coalesce(struct mlx5e_priv *priv,
if (!MLX5_CAP_GEN(mdev, cq_moderation))
return -EOPNOTSUPP;
+ if (coal->tx_coalesce_usecs > MLX5E_MAX_COAL_TIME ||
+ coal->rx_coalesce_usecs > MLX5E_MAX_COAL_TIME) {
+ netdev_info(priv->netdev, "%s: maximum coalesce time supported is %lu usecs\n",
+ __func__, MLX5E_MAX_COAL_TIME);
+ return -ERANGE;
+ }
+
+ if (coal->tx_max_coalesced_frames > MLX5E_MAX_COAL_FRAMES ||
+ coal->rx_max_coalesced_frames > MLX5E_MAX_COAL_FRAMES) {
+ netdev_info(priv->netdev, "%s: maximum coalesced frames supported is %lu\n",
+ __func__, MLX5E_MAX_COAL_FRAMES);
+ return -ERANGE;
+ }
+
mutex_lock(&priv->state_lock);
new_channels.params = priv->channels.params;
@@ -1066,16 +1015,66 @@ static int mlx5e_get_rxnfc(struct net_device *netdev,
return err;
}
+#define MLX5E_PFC_PREVEN_AUTO_TOUT_MSEC 100
+#define MLX5E_PFC_PREVEN_TOUT_MAX_MSEC 8000
+#define MLX5E_PFC_PREVEN_MINOR_PRECENT 85
+#define MLX5E_PFC_PREVEN_TOUT_MIN_MSEC 80
+#define MLX5E_DEVICE_STALL_MINOR_WATERMARK(critical_tout) \
+ max_t(u16, MLX5E_PFC_PREVEN_TOUT_MIN_MSEC, \
+ (critical_tout * MLX5E_PFC_PREVEN_MINOR_PRECENT) / 100)
+
+static int mlx5e_get_pfc_prevention_tout(struct net_device *netdev,
+ u16 *pfc_prevention_tout)
+{
+ struct mlx5e_priv *priv = netdev_priv(netdev);
+ struct mlx5_core_dev *mdev = priv->mdev;
+
+ if (!MLX5_CAP_PCAM_FEATURE((priv)->mdev, pfcc_mask) ||
+ !MLX5_CAP_DEBUG((priv)->mdev, stall_detect))
+ return -EOPNOTSUPP;
+
+ return mlx5_query_port_stall_watermark(mdev, pfc_prevention_tout, NULL);
+}
+
+static int mlx5e_set_pfc_prevention_tout(struct net_device *netdev,
+ u16 pfc_preven)
+{
+ struct mlx5e_priv *priv = netdev_priv(netdev);
+ struct mlx5_core_dev *mdev = priv->mdev;
+ u16 critical_tout;
+ u16 minor;
+
+ if (!MLX5_CAP_PCAM_FEATURE((priv)->mdev, pfcc_mask) ||
+ !MLX5_CAP_DEBUG((priv)->mdev, stall_detect))
+ return -EOPNOTSUPP;
+
+ critical_tout = (pfc_preven == PFC_STORM_PREVENTION_AUTO) ?
+ MLX5E_PFC_PREVEN_AUTO_TOUT_MSEC :
+ pfc_preven;
+
+ if (critical_tout != PFC_STORM_PREVENTION_DISABLE &&
+ (critical_tout > MLX5E_PFC_PREVEN_TOUT_MAX_MSEC ||
+ critical_tout < MLX5E_PFC_PREVEN_TOUT_MIN_MSEC)) {
+ netdev_info(netdev, "%s: pfc prevention tout not in range (%d-%d)\n",
+ __func__, MLX5E_PFC_PREVEN_TOUT_MIN_MSEC,
+ MLX5E_PFC_PREVEN_TOUT_MAX_MSEC);
+ return -EINVAL;
+ }
+
+ minor = MLX5E_DEVICE_STALL_MINOR_WATERMARK(critical_tout);
+ return mlx5_set_port_stall_watermark(mdev, critical_tout,
+ minor);
+}
+
static int mlx5e_get_tunable(struct net_device *dev,
const struct ethtool_tunable *tuna,
void *data)
{
- const struct mlx5e_priv *priv = netdev_priv(dev);
- int err = 0;
+ int err;
switch (tuna->id) {
- case ETHTOOL_TX_COPYBREAK:
- *(u32 *)data = priv->channels.params.tx_max_inline;
+ case ETHTOOL_PFC_PREVENTION_TOUT:
+ err = mlx5e_get_pfc_prevention_tout(dev, data);
break;
default:
err = -EINVAL;
@@ -1090,34 +1089,13 @@ static int mlx5e_set_tunable(struct net_device *dev,
const void *data)
{
struct mlx5e_priv *priv = netdev_priv(dev);
- struct mlx5_core_dev *mdev = priv->mdev;
- struct mlx5e_channels new_channels = {};
- int err = 0;
- u32 val;
+ int err;
mutex_lock(&priv->state_lock);
switch (tuna->id) {
- case ETHTOOL_TX_COPYBREAK:
- val = *(u32 *)data;
- if (val > mlx5e_get_max_inline_cap(mdev)) {
- err = -EINVAL;
- break;
- }
-
- new_channels.params = priv->channels.params;
- new_channels.params.tx_max_inline = val;
-
- if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) {
- priv->channels.params = new_channels.params;
- break;
- }
-
- err = mlx5e_open_channels(priv, &new_channels);
- if (err)
- break;
- mlx5e_switch_priv_channels(priv, &new_channels, NULL);
-
+ case ETHTOOL_PFC_PREVENTION_TOUT:
+ err = mlx5e_set_pfc_prevention_tout(dev, *(u16 *)data);
break;
default:
err = -EINVAL;
@@ -1507,11 +1485,6 @@ int mlx5e_modify_rx_cqe_compression_locked(struct mlx5e_priv *priv, bool new_val
new_channels.params = priv->channels.params;
MLX5E_SET_PFLAG(&new_channels.params, MLX5E_PFLAG_RX_CQE_COMPRESS, new_val);
- new_channels.params.mpwqe_log_stride_sz =
- MLX5E_MPWQE_STRIDE_SZ(priv->mdev, new_val);
- new_channels.params.mpwqe_log_num_strides =
- MLX5_MPWRQ_LOG_WQE_SZ - new_channels.params.mpwqe_log_stride_sz;
-
if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) {
priv->channels.params = new_channels.params;
return 0;
@@ -1549,6 +1522,38 @@ static int set_pflag_rx_cqe_compress(struct net_device *netdev,
return 0;
}
+static int set_pflag_rx_striding_rq(struct net_device *netdev, bool enable)
+{
+ struct mlx5e_priv *priv = netdev_priv(netdev);
+ struct mlx5_core_dev *mdev = priv->mdev;
+ struct mlx5e_channels new_channels = {};
+ int err;
+
+ if (enable) {
+ if (!mlx5e_check_fragmented_striding_rq_cap(mdev))
+ return -EOPNOTSUPP;
+ if (!mlx5e_striding_rq_possible(mdev, &priv->channels.params))
+ return -EINVAL;
+ }
+
+ new_channels.params = priv->channels.params;
+
+ MLX5E_SET_PFLAG(&new_channels.params, MLX5E_PFLAG_RX_STRIDING_RQ, enable);
+ mlx5e_set_rq_type(mdev, &new_channels.params);
+
+ if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) {
+ priv->channels.params = new_channels.params;
+ return 0;
+ }
+
+ err = mlx5e_open_channels(priv, &new_channels);
+ if (err)
+ return err;
+
+ mlx5e_switch_priv_channels(priv, &new_channels, NULL);
+ return 0;
+}
+
static int mlx5e_handle_pflag(struct net_device *netdev,
u32 wanted_flags,
enum mlx5e_priv_flag flag,
@@ -1594,6 +1599,12 @@ static int mlx5e_set_priv_flags(struct net_device *netdev, u32 pflags)
err = mlx5e_handle_pflag(netdev, pflags,
MLX5E_PFLAG_RX_CQE_COMPRESS,
set_pflag_rx_cqe_compress);
+ if (err)
+ goto out;
+
+ err = mlx5e_handle_pflag(netdev, pflags,
+ MLX5E_PFLAG_RX_STRIDING_RQ,
+ set_pflag_rx_striding_rq);
out:
mutex_unlock(&priv->state_lock);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index da94c8cba5ee..b29c1d93f058 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -71,56 +71,145 @@ struct mlx5e_channel_param {
struct mlx5e_cq_param icosq_cq;
};
-static bool mlx5e_check_fragmented_striding_rq_cap(struct mlx5_core_dev *mdev)
+bool mlx5e_check_fragmented_striding_rq_cap(struct mlx5_core_dev *mdev)
{
- return MLX5_CAP_GEN(mdev, striding_rq) &&
+ bool striding_rq_umr = MLX5_CAP_GEN(mdev, striding_rq) &&
MLX5_CAP_GEN(mdev, umr_ptr_rlky) &&
MLX5_CAP_ETH(mdev, reg_umr_sq);
+ u16 max_wqe_sz_cap = MLX5_CAP_GEN(mdev, max_wqe_sz_sq);
+ bool inline_umr = MLX5E_UMR_WQE_INLINE_SZ <= max_wqe_sz_cap;
+
+ if (!striding_rq_umr)
+ return false;
+ if (!inline_umr) {
+ mlx5_core_warn(mdev, "Cannot support Striding RQ: UMR WQE size (%d) exceeds maximum supported (%d).\n",
+ (int)MLX5E_UMR_WQE_INLINE_SZ, max_wqe_sz_cap);
+ return false;
+ }
+ return true;
+}
+
+static u32 mlx5e_mpwqe_get_linear_frag_sz(struct mlx5e_params *params)
+{
+ if (!params->xdp_prog) {
+ u16 hw_mtu = MLX5E_SW2HW_MTU(params, params->sw_mtu);
+ u16 rq_headroom = MLX5_RX_HEADROOM + NET_IP_ALIGN;
+
+ return MLX5_SKB_FRAG_SZ(rq_headroom + hw_mtu);
+ }
+
+ return PAGE_SIZE;
+}
+
+static u8 mlx5e_mpwqe_log_pkts_per_wqe(struct mlx5e_params *params)
+{
+ u32 linear_frag_sz = mlx5e_mpwqe_get_linear_frag_sz(params);
+
+ return MLX5_MPWRQ_LOG_WQE_SZ - order_base_2(linear_frag_sz);
+}
+
+static bool mlx5e_rx_mpwqe_is_linear_skb(struct mlx5_core_dev *mdev,
+ struct mlx5e_params *params)
+{
+ u32 frag_sz = mlx5e_mpwqe_get_linear_frag_sz(params);
+ s8 signed_log_num_strides_param;
+ u8 log_num_strides;
+
+ if (params->lro_en || frag_sz > PAGE_SIZE)
+ return false;
+
+ if (MLX5_CAP_GEN(mdev, ext_stride_num_range))
+ return true;
+
+ log_num_strides = MLX5_MPWRQ_LOG_WQE_SZ - order_base_2(frag_sz);
+ signed_log_num_strides_param =
+ (s8)log_num_strides - MLX5_MPWQE_LOG_NUM_STRIDES_BASE;
+
+ return signed_log_num_strides_param >= 0;
+}
+
+static u8 mlx5e_mpwqe_get_log_rq_size(struct mlx5e_params *params)
+{
+ if (params->log_rq_mtu_frames <
+ mlx5e_mpwqe_log_pkts_per_wqe(params) + MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE_MPW)
+ return MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE_MPW;
+
+ return params->log_rq_mtu_frames - mlx5e_mpwqe_log_pkts_per_wqe(params);
+}
+
+static u8 mlx5e_mpwqe_get_log_stride_size(struct mlx5_core_dev *mdev,
+ struct mlx5e_params *params)
+{
+ if (mlx5e_rx_mpwqe_is_linear_skb(mdev, params))
+ return order_base_2(mlx5e_mpwqe_get_linear_frag_sz(params));
+
+ return MLX5E_MPWQE_STRIDE_SZ(mdev,
+ MLX5E_GET_PFLAG(params, MLX5E_PFLAG_RX_CQE_COMPRESS));
+}
+
+static u8 mlx5e_mpwqe_get_log_num_strides(struct mlx5_core_dev *mdev,
+ struct mlx5e_params *params)
+{
+ return MLX5_MPWRQ_LOG_WQE_SZ -
+ mlx5e_mpwqe_get_log_stride_size(mdev, params);
+}
+
+static u16 mlx5e_get_rq_headroom(struct mlx5_core_dev *mdev,
+ struct mlx5e_params *params)
+{
+ u16 linear_rq_headroom = params->xdp_prog ?
+ XDP_PACKET_HEADROOM : MLX5_RX_HEADROOM;
+
+ linear_rq_headroom += NET_IP_ALIGN;
+
+ if (params->rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST)
+ return linear_rq_headroom;
+
+ if (mlx5e_rx_mpwqe_is_linear_skb(mdev, params))
+ return linear_rq_headroom;
+
+ return 0;
}
void mlx5e_init_rq_type_params(struct mlx5_core_dev *mdev,
- struct mlx5e_params *params, u8 rq_type)
+ struct mlx5e_params *params)
{
- params->rq_wq_type = rq_type;
params->lro_wqe_sz = MLX5E_PARAMS_DEFAULT_LRO_WQE_SZ;
+ params->log_rq_mtu_frames = is_kdump_kernel() ?
+ MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE :
+ MLX5E_PARAMS_DEFAULT_LOG_RQ_SIZE;
switch (params->rq_wq_type) {
case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ:
- params->log_rq_size = is_kdump_kernel() ?
- MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE_MPW :
- MLX5E_PARAMS_DEFAULT_LOG_RQ_SIZE_MPW;
- params->mpwqe_log_stride_sz = MLX5E_MPWQE_STRIDE_SZ(mdev,
- MLX5E_GET_PFLAG(params, MLX5E_PFLAG_RX_CQE_COMPRESS));
- params->mpwqe_log_num_strides = MLX5_MPWRQ_LOG_WQE_SZ -
- params->mpwqe_log_stride_sz;
break;
default: /* MLX5_WQ_TYPE_LINKED_LIST */
- params->log_rq_size = is_kdump_kernel() ?
- MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE :
- MLX5E_PARAMS_DEFAULT_LOG_RQ_SIZE;
- params->rq_headroom = params->xdp_prog ?
- XDP_PACKET_HEADROOM : MLX5_RX_HEADROOM;
- params->rq_headroom += NET_IP_ALIGN;
-
/* Extra room needed for build_skb */
- params->lro_wqe_sz -= params->rq_headroom +
+ params->lro_wqe_sz -= mlx5e_get_rq_headroom(mdev, params) +
SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
}
mlx5_core_info(mdev, "MLX5E: StrdRq(%d) RqSz(%ld) StrdSz(%ld) RxCqeCmprss(%d)\n",
params->rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ,
- BIT(params->log_rq_size),
- BIT(params->mpwqe_log_stride_sz),
+ params->rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ ?
+ BIT(mlx5e_mpwqe_get_log_rq_size(params)) :
+ BIT(params->log_rq_mtu_frames),
+ BIT(mlx5e_mpwqe_get_log_stride_size(mdev, params)),
MLX5E_GET_PFLAG(params, MLX5E_PFLAG_RX_CQE_COMPRESS));
}
-static void mlx5e_set_rq_params(struct mlx5_core_dev *mdev,
+bool mlx5e_striding_rq_possible(struct mlx5_core_dev *mdev,
struct mlx5e_params *params)
{
- u8 rq_type = mlx5e_check_fragmented_striding_rq_cap(mdev) &&
- !params->xdp_prog && !MLX5_IPSEC_DEV(mdev) ?
- MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ :
- MLX5_WQ_TYPE_LINKED_LIST;
- mlx5e_init_rq_type_params(mdev, params, rq_type);
+ return mlx5e_check_fragmented_striding_rq_cap(mdev) &&
+ !MLX5_IPSEC_DEV(mdev) &&
+ !(params->xdp_prog && !mlx5e_rx_mpwqe_is_linear_skb(mdev, params));
+}
+
+void mlx5e_set_rq_type(struct mlx5_core_dev *mdev, struct mlx5e_params *params)
+{
+ params->rq_wq_type = mlx5e_striding_rq_possible(mdev, params) &&
+ MLX5E_GET_PFLAG(params, MLX5E_PFLAG_RX_STRIDING_RQ) ?
+ MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ :
+ MLX5_WQ_TYPE_LINKED_LIST;
}
static void mlx5e_update_carrier(struct mlx5e_priv *priv)
@@ -153,26 +242,6 @@ static void mlx5e_update_carrier_work(struct work_struct *work)
mutex_unlock(&priv->state_lock);
}
-static void mlx5e_tx_timeout_work(struct work_struct *work)
-{
- struct mlx5e_priv *priv = container_of(work, struct mlx5e_priv,
- tx_timeout_work);
- int err;
-
- rtnl_lock();
- mutex_lock(&priv->state_lock);
- if (!test_bit(MLX5E_STATE_OPENED, &priv->state))
- goto unlock;
- mlx5e_close_locked(priv->netdev);
- err = mlx5e_open_locked(priv->netdev);
- if (err)
- netdev_err(priv->netdev, "mlx5e_open_locked failed recovering from a tx_timeout, err(%d).\n",
- err);
-unlock:
- mutex_unlock(&priv->state_lock);
- rtnl_unlock();
-}
-
void mlx5e_update_stats(struct mlx5e_priv *priv)
{
int i;
@@ -235,107 +304,38 @@ static void mlx5e_disable_async_events(struct mlx5e_priv *priv)
synchronize_irq(pci_irq_vector(priv->mdev->pdev, MLX5_EQ_VEC_ASYNC));
}
-static inline int mlx5e_get_wqe_mtt_sz(void)
-{
- /* UMR copies MTTs in units of MLX5_UMR_MTT_ALIGNMENT bytes.
- * To avoid copying garbage after the mtt array, we allocate
- * a little more.
- */
- return ALIGN(MLX5_MPWRQ_PAGES_PER_WQE * sizeof(__be64),
- MLX5_UMR_MTT_ALIGNMENT);
-}
-
static inline void mlx5e_build_umr_wqe(struct mlx5e_rq *rq,
struct mlx5e_icosq *sq,
- struct mlx5e_umr_wqe *wqe,
- u16 ix)
+ struct mlx5e_umr_wqe *wqe)
{
struct mlx5_wqe_ctrl_seg *cseg = &wqe->ctrl;
struct mlx5_wqe_umr_ctrl_seg *ucseg = &wqe->uctrl;
- struct mlx5_wqe_data_seg *dseg = &wqe->data;
- struct mlx5e_mpw_info *wi = &rq->mpwqe.info[ix];
- u8 ds_cnt = DIV_ROUND_UP(sizeof(*wqe), MLX5_SEND_WQE_DS);
- u32 umr_wqe_mtt_offset = mlx5e_get_wqe_mtt_offset(rq, ix);
+ u8 ds_cnt = DIV_ROUND_UP(MLX5E_UMR_WQE_INLINE_SZ, MLX5_SEND_WQE_DS);
cseg->qpn_ds = cpu_to_be32((sq->sqn << MLX5_WQE_CTRL_QPN_SHIFT) |
ds_cnt);
cseg->fm_ce_se = MLX5_WQE_CTRL_CQ_UPDATE;
cseg->imm = rq->mkey_be;
- ucseg->flags = MLX5_UMR_TRANSLATION_OFFSET_EN;
+ ucseg->flags = MLX5_UMR_TRANSLATION_OFFSET_EN | MLX5_UMR_INLINE;
ucseg->xlt_octowords =
cpu_to_be16(MLX5_MTT_OCTW(MLX5_MPWRQ_PAGES_PER_WQE));
- ucseg->bsf_octowords =
- cpu_to_be16(MLX5_MTT_OCTW(umr_wqe_mtt_offset));
ucseg->mkey_mask = cpu_to_be64(MLX5_MKEY_MASK_FREE);
-
- dseg->lkey = sq->mkey_be;
- dseg->addr = cpu_to_be64(wi->umr.mtt_addr);
}
static int mlx5e_rq_alloc_mpwqe_info(struct mlx5e_rq *rq,
struct mlx5e_channel *c)
{
int wq_sz = mlx5_wq_ll_get_size(&rq->wq);
- int mtt_sz = mlx5e_get_wqe_mtt_sz();
- int mtt_alloc = mtt_sz + MLX5_UMR_ALIGN - 1;
- int i;
rq->mpwqe.info = kzalloc_node(wq_sz * sizeof(*rq->mpwqe.info),
GFP_KERNEL, cpu_to_node(c->cpu));
if (!rq->mpwqe.info)
- goto err_out;
-
- /* We allocate more than mtt_sz as we will align the pointer */
- rq->mpwqe.mtt_no_align = kzalloc_node(mtt_alloc * wq_sz, GFP_KERNEL,
- cpu_to_node(c->cpu));
- if (unlikely(!rq->mpwqe.mtt_no_align))
- goto err_free_wqe_info;
-
- for (i = 0; i < wq_sz; i++) {
- struct mlx5e_mpw_info *wi = &rq->mpwqe.info[i];
-
- wi->umr.mtt = PTR_ALIGN(rq->mpwqe.mtt_no_align + i * mtt_alloc,
- MLX5_UMR_ALIGN);
- wi->umr.mtt_addr = dma_map_single(c->pdev, wi->umr.mtt, mtt_sz,
- PCI_DMA_TODEVICE);
- if (unlikely(dma_mapping_error(c->pdev, wi->umr.mtt_addr)))
- goto err_unmap_mtts;
+ return -ENOMEM;
- mlx5e_build_umr_wqe(rq, &c->icosq, &wi->umr.wqe, i);
- }
+ mlx5e_build_umr_wqe(rq, &c->icosq, &rq->mpwqe.umr_wqe);
return 0;
-
-err_unmap_mtts:
- while (--i >= 0) {
- struct mlx5e_mpw_info *wi = &rq->mpwqe.info[i];
-
- dma_unmap_single(c->pdev, wi->umr.mtt_addr, mtt_sz,
- PCI_DMA_TODEVICE);
- }
- kfree(rq->mpwqe.mtt_no_align);
-err_free_wqe_info:
- kfree(rq->mpwqe.info);
-
-err_out:
- return -ENOMEM;
-}
-
-static void mlx5e_rq_free_mpwqe_info(struct mlx5e_rq *rq)
-{
- int wq_sz = mlx5_wq_ll_get_size(&rq->wq);
- int mtt_sz = mlx5e_get_wqe_mtt_sz();
- int i;
-
- for (i = 0; i < wq_sz; i++) {
- struct mlx5e_mpw_info *wi = &rq->mpwqe.info[i];
-
- dma_unmap_single(rq->pdev, wi->umr.mtt_addr, mtt_sz,
- PCI_DMA_TODEVICE);
- }
- kfree(rq->mpwqe.mtt_no_align);
- kfree(rq->mpwqe.info);
}
static int mlx5e_create_umr_mkey(struct mlx5_core_dev *mdev,
@@ -347,9 +347,6 @@ static int mlx5e_create_umr_mkey(struct mlx5_core_dev *mdev,
u32 *in;
int err;
- if (!MLX5E_VALID_NUM_MTTS(npages))
- return -EINVAL;
-
in = kvzalloc(inlen, GFP_KERNEL);
if (!in)
return -ENOMEM;
@@ -360,7 +357,7 @@ static int mlx5e_create_umr_mkey(struct mlx5_core_dev *mdev,
MLX5_SET(mkc, mkc, umr_en, 1);
MLX5_SET(mkc, mkc, lw, 1);
MLX5_SET(mkc, mkc, lr, 1);
- MLX5_SET(mkc, mkc, access_mode, MLX5_MKC_ACCESS_MODE_MTT);
+ MLX5_SET(mkc, mkc, access_mode_1_0, MLX5_MKC_ACCESS_MODE_MTT);
MLX5_SET(mkc, mkc, qpn, 0xffffff);
MLX5_SET(mkc, mkc, pd, mdev->mlx5e_res.pdn);
@@ -382,6 +379,11 @@ static int mlx5e_create_rq_umr_mkey(struct mlx5_core_dev *mdev, struct mlx5e_rq
return mlx5e_create_umr_mkey(mdev, num_mtts, PAGE_SHIFT, &rq->umr_mkey);
}
+static inline u64 mlx5e_get_mpwqe_offset(struct mlx5e_rq *rq, u16 wqe_ix)
+{
+ return (wqe_ix << MLX5E_LOG_ALIGNED_MPWQE_PPW) << PAGE_SHIFT;
+}
+
static int mlx5e_alloc_rq(struct mlx5e_channel *c,
struct mlx5e_params *params,
struct mlx5e_rq_param *rqp,
@@ -415,6 +417,7 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c,
rq->channel = c;
rq->ix = c->ix;
rq->mdev = mdev;
+ rq->hw_mtu = MLX5E_SW2HW_MTU(params, params->sw_mtu);
rq->xdp_prog = params->xdp_prog ? bpf_prog_inc(params->xdp_prog) : NULL;
if (IS_ERR(rq->xdp_prog)) {
@@ -428,11 +431,10 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c,
goto err_rq_wq_destroy;
rq->buff.map_dir = rq->xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE;
- rq->buff.headroom = params->rq_headroom;
+ rq->buff.headroom = mlx5e_get_rq_headroom(mdev, params);
switch (rq->wq_type) {
case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ:
-
rq->post_wqes = mlx5e_post_rx_mpwqes;
rq->dealloc_wqe = mlx5e_dealloc_rx_mpwqe;
@@ -450,8 +452,12 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c,
goto err_rq_wq_destroy;
}
- rq->mpwqe.log_stride_sz = params->mpwqe_log_stride_sz;
- rq->mpwqe.num_strides = BIT(params->mpwqe_log_num_strides);
+ rq->mpwqe.skb_from_cqe_mpwrq =
+ mlx5e_rx_mpwqe_is_linear_skb(mdev, params) ?
+ mlx5e_skb_from_cqe_mpwrq_linear :
+ mlx5e_skb_from_cqe_mpwrq_nonlinear;
+ rq->mpwqe.log_stride_sz = mlx5e_mpwqe_get_log_stride_size(mdev, params);
+ rq->mpwqe.num_strides = BIT(mlx5e_mpwqe_get_log_num_strides(mdev, params));
byte_count = rq->mpwqe.num_strides << rq->mpwqe.log_stride_sz;
@@ -490,7 +496,7 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c,
byte_count = params->lro_en ?
params->lro_wqe_sz :
- MLX5E_SW2HW_MTU(c->priv, c->netdev->mtu);
+ MLX5E_SW2HW_MTU(params, params->sw_mtu);
#ifdef CONFIG_MLX5_EN_IPSEC
if (MLX5_IPSEC_DEV(mdev))
byte_count += MLX5E_METADATA_ETHER_LEN;
@@ -510,9 +516,9 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c,
struct mlx5e_rx_wqe *wqe = mlx5_wq_ll_get_wqe(&rq->wq, i);
if (rq->wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ) {
- u64 dma_offset = (u64)mlx5e_get_wqe_mtt_offset(rq, i) << PAGE_SHIFT;
+ u64 dma_offset = mlx5e_get_mpwqe_offset(rq, i);
- wqe->data.addr = cpu_to_be64(dma_offset);
+ wqe->data.addr = cpu_to_be64(dma_offset + rq->buff.headroom);
}
wqe->data.byte_count = cpu_to_be32(byte_count);
@@ -558,7 +564,7 @@ static void mlx5e_free_rq(struct mlx5e_rq *rq)
switch (rq->wq_type) {
case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ:
- mlx5e_rq_free_mpwqe_info(rq);
+ kfree(rq->mpwqe.info);
mlx5_core_destroy_mkey(rq->mdev, &rq->umr_mkey);
break;
default: /* MLX5_WQ_TYPE_LINKED_LIST */
@@ -615,8 +621,7 @@ static int mlx5e_create_rq(struct mlx5e_rq *rq,
static int mlx5e_modify_rq_state(struct mlx5e_rq *rq, int curr_state,
int next_state)
{
- struct mlx5e_channel *c = rq->channel;
- struct mlx5_core_dev *mdev = c->mdev;
+ struct mlx5_core_dev *mdev = rq->mdev;
void *in;
void *rqc;
@@ -898,7 +903,6 @@ static int mlx5e_alloc_icosq(struct mlx5e_channel *c,
struct mlx5_core_dev *mdev = c->mdev;
int err;
- sq->mkey_be = c->mkey_be;
sq->channel = c;
sq->uar_map = mdev->mlx5e_res.bfreg.map;
@@ -953,6 +957,7 @@ static int mlx5e_alloc_txqsq_db(struct mlx5e_txqsq *sq, int numa)
return 0;
}
+static void mlx5e_sq_recover(struct work_struct *work);
static int mlx5e_alloc_txqsq(struct mlx5e_channel *c,
int txq_ix,
struct mlx5e_params *params,
@@ -970,8 +975,8 @@ static int mlx5e_alloc_txqsq(struct mlx5e_channel *c,
sq->channel = c;
sq->txq_ix = txq_ix;
sq->uar_map = mdev->mlx5e_res.bfreg.map;
- sq->max_inline = params->tx_max_inline;
sq->min_inline_mode = params->tx_min_inline_mode;
+ INIT_WORK(&sq->recover.recover_work, mlx5e_sq_recover);
if (MLX5_IPSEC_DEV(c->priv->mdev))
set_bit(MLX5E_SQ_STATE_IPSEC, &sq->state);
@@ -1038,6 +1043,7 @@ static int mlx5e_create_sq(struct mlx5_core_dev *mdev,
MLX5_SET(sqc, sqc, min_wqe_inline_mode, csp->min_inline_mode);
MLX5_SET(sqc, sqc, state, MLX5_SQC_STATE_RST);
+ MLX5_SET(sqc, sqc, flush_in_error_en, 1);
MLX5_SET(wq, wq, wq_type, MLX5_WQ_TYPE_CYCLIC);
MLX5_SET(wq, wq, uar_page, mdev->mlx5e_res.bfreg.index);
@@ -1156,9 +1162,20 @@ err_free_txqsq:
return err;
}
+static void mlx5e_reset_txqsq_cc_pc(struct mlx5e_txqsq *sq)
+{
+ WARN_ONCE(sq->cc != sq->pc,
+ "SQ 0x%x: cc (0x%x) != pc (0x%x)\n",
+ sq->sqn, sq->cc, sq->pc);
+ sq->cc = 0;
+ sq->dma_fifo_cc = 0;
+ sq->pc = 0;
+}
+
static void mlx5e_activate_txqsq(struct mlx5e_txqsq *sq)
{
sq->txq = netdev_get_tx_queue(sq->channel->netdev, sq->txq_ix);
+ clear_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state);
set_bit(MLX5E_SQ_STATE_ENABLED, &sq->state);
netdev_tx_reset_queue(sq->txq);
netif_tx_start_queue(sq->txq);
@@ -1195,14 +1212,118 @@ static void mlx5e_close_txqsq(struct mlx5e_txqsq *sq)
{
struct mlx5e_channel *c = sq->channel;
struct mlx5_core_dev *mdev = c->mdev;
+ struct mlx5_rate_limit rl = {0};
mlx5e_destroy_sq(mdev, sq->sqn);
- if (sq->rate_limit)
- mlx5_rl_remove_rate(mdev, sq->rate_limit);
+ if (sq->rate_limit) {
+ rl.rate = sq->rate_limit;
+ mlx5_rl_remove_rate(mdev, &rl);
+ }
mlx5e_free_txqsq_descs(sq);
mlx5e_free_txqsq(sq);
}
+static int mlx5e_wait_for_sq_flush(struct mlx5e_txqsq *sq)
+{
+ unsigned long exp_time = jiffies + msecs_to_jiffies(2000);
+
+ while (time_before(jiffies, exp_time)) {
+ if (sq->cc == sq->pc)
+ return 0;
+
+ msleep(20);
+ }
+
+ netdev_err(sq->channel->netdev,
+ "Wait for SQ 0x%x flush timeout (sq cc = 0x%x, sq pc = 0x%x)\n",
+ sq->sqn, sq->cc, sq->pc);
+
+ return -ETIMEDOUT;
+}
+
+static int mlx5e_sq_to_ready(struct mlx5e_txqsq *sq, int curr_state)
+{
+ struct mlx5_core_dev *mdev = sq->channel->mdev;
+ struct net_device *dev = sq->channel->netdev;
+ struct mlx5e_modify_sq_param msp = {0};
+ int err;
+
+ msp.curr_state = curr_state;
+ msp.next_state = MLX5_SQC_STATE_RST;
+
+ err = mlx5e_modify_sq(mdev, sq->sqn, &msp);
+ if (err) {
+ netdev_err(dev, "Failed to move sq 0x%x to reset\n", sq->sqn);
+ return err;
+ }
+
+ memset(&msp, 0, sizeof(msp));
+ msp.curr_state = MLX5_SQC_STATE_RST;
+ msp.next_state = MLX5_SQC_STATE_RDY;
+
+ err = mlx5e_modify_sq(mdev, sq->sqn, &msp);
+ if (err) {
+ netdev_err(dev, "Failed to move sq 0x%x to ready\n", sq->sqn);
+ return err;
+ }
+
+ return 0;
+}
+
+static void mlx5e_sq_recover(struct work_struct *work)
+{
+ struct mlx5e_txqsq_recover *recover =
+ container_of(work, struct mlx5e_txqsq_recover,
+ recover_work);
+ struct mlx5e_txqsq *sq = container_of(recover, struct mlx5e_txqsq,
+ recover);
+ struct mlx5_core_dev *mdev = sq->channel->mdev;
+ struct net_device *dev = sq->channel->netdev;
+ u8 state;
+ int err;
+
+ err = mlx5_core_query_sq_state(mdev, sq->sqn, &state);
+ if (err) {
+ netdev_err(dev, "Failed to query SQ 0x%x state. err = %d\n",
+ sq->sqn, err);
+ return;
+ }
+
+ if (state != MLX5_RQC_STATE_ERR) {
+ netdev_err(dev, "SQ 0x%x not in ERROR state\n", sq->sqn);
+ return;
+ }
+
+ netif_tx_disable_queue(sq->txq);
+
+ if (mlx5e_wait_for_sq_flush(sq))
+ return;
+
+ /* If the interval between two consecutive recovers per SQ is too
+ * short, don't recover to avoid infinite loop of ERR_CQE -> recover.
+ * If we reached this state, there is probably a bug that needs to be
+ * fixed. let's keep the queue close and let tx timeout cleanup.
+ */
+ if (jiffies_to_msecs(jiffies - recover->last_recover) <
+ MLX5E_SQ_RECOVER_MIN_INTERVAL) {
+ netdev_err(dev, "Recover SQ 0x%x canceled, too many error CQEs\n",
+ sq->sqn);
+ return;
+ }
+
+ /* At this point, no new packets will arrive from the stack as TXQ is
+ * marked with QUEUE_STATE_DRV_XOFF. In addition, NAPI cleared all
+ * pending WQEs. SQ can safely reset the SQ.
+ */
+ if (mlx5e_sq_to_ready(sq, state))
+ return;
+
+ mlx5e_reset_txqsq_cc_pc(sq);
+ sq->stats.recover++;
+ recover->last_recover = jiffies;
+ mlx5e_activate_txqsq(sq);
+}
+
static int mlx5e_open_icosq(struct mlx5e_channel *c,
struct mlx5e_params *params,
struct mlx5e_sq_param *param,
@@ -1528,6 +1649,7 @@ static int mlx5e_set_sq_maxrate(struct net_device *dev,
struct mlx5e_priv *priv = netdev_priv(dev);
struct mlx5_core_dev *mdev = priv->mdev;
struct mlx5e_modify_sq_param msp = {0};
+ struct mlx5_rate_limit rl = {0};
u16 rl_index = 0;
int err;
@@ -1535,14 +1657,17 @@ static int mlx5e_set_sq_maxrate(struct net_device *dev,
/* nothing to do */
return 0;
- if (sq->rate_limit)
+ if (sq->rate_limit) {
+ rl.rate = sq->rate_limit;
/* remove current rl index to free space to next ones */
- mlx5_rl_remove_rate(mdev, sq->rate_limit);
+ mlx5_rl_remove_rate(mdev, &rl);
+ }
sq->rate_limit = 0;
if (rate) {
- err = mlx5_rl_add_rate(mdev, rate, &rl_index);
+ rl.rate = rate;
+ err = mlx5_rl_add_rate(mdev, &rl_index, &rl);
if (err) {
netdev_err(dev, "Failed configuring rate %u: %d\n",
rate, err);
@@ -1560,7 +1685,7 @@ static int mlx5e_set_sq_maxrate(struct net_device *dev,
rate, err);
/* remove the rate from the table */
if (rate)
- mlx5_rl_remove_rate(mdev, rate);
+ mlx5_rl_remove_rate(mdev, &rl);
return err;
}
@@ -1743,39 +1868,47 @@ static void mlx5e_build_rq_param(struct mlx5e_priv *priv,
struct mlx5e_params *params,
struct mlx5e_rq_param *param)
{
+ struct mlx5_core_dev *mdev = priv->mdev;
void *rqc = param->rqc;
void *wq = MLX5_ADDR_OF(rqc, rqc, wq);
switch (params->rq_wq_type) {
case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ:
- MLX5_SET(wq, wq, log_wqe_num_of_strides, params->mpwqe_log_num_strides - 9);
- MLX5_SET(wq, wq, log_wqe_stride_size, params->mpwqe_log_stride_sz - 6);
+ MLX5_SET(wq, wq, log_wqe_num_of_strides,
+ mlx5e_mpwqe_get_log_num_strides(mdev, params) -
+ MLX5_MPWQE_LOG_NUM_STRIDES_BASE);
+ MLX5_SET(wq, wq, log_wqe_stride_size,
+ mlx5e_mpwqe_get_log_stride_size(mdev, params) -
+ MLX5_MPWQE_LOG_STRIDE_SZ_BASE);
MLX5_SET(wq, wq, wq_type, MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ);
+ MLX5_SET(wq, wq, log_wq_sz, mlx5e_mpwqe_get_log_rq_size(params));
break;
default: /* MLX5_WQ_TYPE_LINKED_LIST */
MLX5_SET(wq, wq, wq_type, MLX5_WQ_TYPE_LINKED_LIST);
+ MLX5_SET(wq, wq, log_wq_sz, params->log_rq_mtu_frames);
}
MLX5_SET(wq, wq, end_padding_mode, MLX5_WQ_END_PAD_MODE_ALIGN);
MLX5_SET(wq, wq, log_wq_stride, ilog2(sizeof(struct mlx5e_rx_wqe)));
- MLX5_SET(wq, wq, log_wq_sz, params->log_rq_size);
- MLX5_SET(wq, wq, pd, priv->mdev->mlx5e_res.pdn);
+ MLX5_SET(wq, wq, pd, mdev->mlx5e_res.pdn);
MLX5_SET(rqc, rqc, counter_set_id, priv->q_counter);
MLX5_SET(rqc, rqc, vsd, params->vlan_strip_disable);
MLX5_SET(rqc, rqc, scatter_fcs, params->scatter_fcs_en);
- param->wq.buf_numa_node = dev_to_node(&priv->mdev->pdev->dev);
+ param->wq.buf_numa_node = dev_to_node(&mdev->pdev->dev);
param->wq.linear = 1;
}
-static void mlx5e_build_drop_rq_param(struct mlx5_core_dev *mdev,
+static void mlx5e_build_drop_rq_param(struct mlx5e_priv *priv,
struct mlx5e_rq_param *param)
{
+ struct mlx5_core_dev *mdev = priv->mdev;
void *rqc = param->rqc;
void *wq = MLX5_ADDR_OF(rqc, rqc, wq);
MLX5_SET(wq, wq, wq_type, MLX5_WQ_TYPE_LINKED_LIST);
MLX5_SET(wq, wq, log_wq_stride, ilog2(sizeof(struct mlx5e_rx_wqe)));
+ MLX5_SET(rqc, rqc, counter_set_id, priv->drop_rq_q_counter);
param->wq.buf_numa_node = dev_to_node(&mdev->pdev->dev);
}
@@ -1816,15 +1949,17 @@ static void mlx5e_build_rx_cq_param(struct mlx5e_priv *priv,
struct mlx5e_params *params,
struct mlx5e_cq_param *param)
{
+ struct mlx5_core_dev *mdev = priv->mdev;
void *cqc = param->cqc;
u8 log_cq_size;
switch (params->rq_wq_type) {
case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ:
- log_cq_size = params->log_rq_size + params->mpwqe_log_num_strides;
+ log_cq_size = mlx5e_mpwqe_get_log_rq_size(params) +
+ mlx5e_mpwqe_get_log_num_strides(mdev, params);
break;
default: /* MLX5_WQ_TYPE_LINKED_LIST */
- log_cq_size = params->log_rq_size;
+ log_cq_size = params->log_rq_mtu_frames;
}
MLX5_SET(cqc, cqc, log_cq_size, log_cq_size);
@@ -2375,10 +2510,10 @@ static void mlx5e_build_inner_indir_tir_ctx(struct mlx5e_priv *priv,
mlx5e_build_indir_tir_ctx_hash(&priv->channels.params, tt, tirc, true);
}
-static int mlx5e_set_mtu(struct mlx5e_priv *priv, u16 mtu)
+static int mlx5e_set_mtu(struct mlx5_core_dev *mdev,
+ struct mlx5e_params *params, u16 mtu)
{
- struct mlx5_core_dev *mdev = priv->mdev;
- u16 hw_mtu = MLX5E_SW2HW_MTU(priv, mtu);
+ u16 hw_mtu = MLX5E_SW2HW_MTU(params, mtu);
int err;
err = mlx5_set_port_mtu(mdev, hw_mtu, 1);
@@ -2390,9 +2525,9 @@ static int mlx5e_set_mtu(struct mlx5e_priv *priv, u16 mtu)
return 0;
}
-static void mlx5e_query_mtu(struct mlx5e_priv *priv, u16 *mtu)
+static void mlx5e_query_mtu(struct mlx5_core_dev *mdev,
+ struct mlx5e_params *params, u16 *mtu)
{
- struct mlx5_core_dev *mdev = priv->mdev;
u16 hw_mtu = 0;
int err;
@@ -2400,25 +2535,27 @@ static void mlx5e_query_mtu(struct mlx5e_priv *priv, u16 *mtu)
if (err || !hw_mtu) /* fallback to port oper mtu */
mlx5_query_port_oper_mtu(mdev, &hw_mtu, 1);
- *mtu = MLX5E_HW2SW_MTU(priv, hw_mtu);
+ *mtu = MLX5E_HW2SW_MTU(params, hw_mtu);
}
static int mlx5e_set_dev_port_mtu(struct mlx5e_priv *priv)
{
+ struct mlx5e_params *params = &priv->channels.params;
struct net_device *netdev = priv->netdev;
+ struct mlx5_core_dev *mdev = priv->mdev;
u16 mtu;
int err;
- err = mlx5e_set_mtu(priv, netdev->mtu);
+ err = mlx5e_set_mtu(mdev, params, params->sw_mtu);
if (err)
return err;
- mlx5e_query_mtu(priv, &mtu);
- if (mtu != netdev->mtu)
+ mlx5e_query_mtu(mdev, params, &mtu);
+ if (mtu != params->sw_mtu)
netdev_warn(netdev, "%s: VPort MTU %d is different than netdev mtu %d\n",
- __func__, mtu, netdev->mtu);
+ __func__, mtu, params->sw_mtu);
- netdev->mtu = mtu;
+ params->sw_mtu = mtu;
return 0;
}
@@ -2572,6 +2709,9 @@ int mlx5e_open(struct net_device *netdev)
mlx5_set_port_admin_status(priv->mdev, MLX5_PORT_UP);
mutex_unlock(&priv->state_lock);
+ if (mlx5e_vxlan_allowed(priv->mdev))
+ udp_tunnel_get_rx_info(netdev);
+
return err;
}
@@ -2643,15 +2783,16 @@ static int mlx5e_alloc_drop_cq(struct mlx5_core_dev *mdev,
return mlx5e_alloc_cq_common(mdev, param, cq);
}
-static int mlx5e_open_drop_rq(struct mlx5_core_dev *mdev,
+static int mlx5e_open_drop_rq(struct mlx5e_priv *priv,
struct mlx5e_rq *drop_rq)
{
+ struct mlx5_core_dev *mdev = priv->mdev;
struct mlx5e_cq_param cq_param = {};
struct mlx5e_rq_param rq_param = {};
struct mlx5e_cq *cq = &drop_rq->cq;
int err;
- mlx5e_build_drop_rq_param(mdev, &rq_param);
+ mlx5e_build_drop_rq_param(priv, &rq_param);
err = mlx5e_alloc_drop_cq(mdev, cq, &cq_param);
if (err)
@@ -2669,6 +2810,10 @@ static int mlx5e_open_drop_rq(struct mlx5_core_dev *mdev,
if (err)
goto err_free_rq;
+ err = mlx5e_modify_rq_state(drop_rq, MLX5_RQC_STATE_RST, MLX5_RQC_STATE_RDY);
+ if (err)
+ mlx5_core_warn(priv->mdev, "modify_rq_state failed, rx_if_down_packets won't be counted %d\n", err);
+
return 0;
err_free_rq:
@@ -3094,20 +3239,28 @@ typedef int (*mlx5e_feature_handler)(struct net_device *netdev, bool enable);
static int set_feature_lro(struct net_device *netdev, bool enable)
{
struct mlx5e_priv *priv = netdev_priv(netdev);
+ struct mlx5_core_dev *mdev = priv->mdev;
struct mlx5e_channels new_channels = {};
+ struct mlx5e_params *old_params;
int err = 0;
bool reset;
mutex_lock(&priv->state_lock);
- reset = (priv->channels.params.rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST);
- reset = reset && test_bit(MLX5E_STATE_OPENED, &priv->state);
+ old_params = &priv->channels.params;
+ reset = test_bit(MLX5E_STATE_OPENED, &priv->state);
- new_channels.params = priv->channels.params;
+ new_channels.params = *old_params;
new_channels.params.lro_en = enable;
+ if (old_params->rq_wq_type != MLX5_WQ_TYPE_LINKED_LIST) {
+ if (mlx5e_rx_mpwqe_is_linear_skb(mdev, old_params) ==
+ mlx5e_rx_mpwqe_is_linear_skb(mdev, &new_channels.params))
+ reset = false;
+ }
+
if (!reset) {
- priv->channels.params = new_channels.params;
+ *old_params = new_channels.params;
err = mlx5e_modify_tirs_lro(priv);
goto out;
}
@@ -3236,24 +3389,20 @@ static int mlx5e_set_features(struct net_device *netdev,
netdev_features_t features)
{
netdev_features_t oper_features = netdev->features;
- int err;
+ int err = 0;
- err = mlx5e_handle_feature(netdev, &oper_features, features,
- NETIF_F_LRO, set_feature_lro);
- err |= mlx5e_handle_feature(netdev, &oper_features, features,
- NETIF_F_HW_VLAN_CTAG_FILTER,
+#define MLX5E_HANDLE_FEATURE(feature, handler) \
+ mlx5e_handle_feature(netdev, &oper_features, features, feature, handler)
+
+ err |= MLX5E_HANDLE_FEATURE(NETIF_F_LRO, set_feature_lro);
+ err |= MLX5E_HANDLE_FEATURE(NETIF_F_HW_VLAN_CTAG_FILTER,
set_feature_cvlan_filter);
- err |= mlx5e_handle_feature(netdev, &oper_features, features,
- NETIF_F_HW_TC, set_feature_tc_num_filters);
- err |= mlx5e_handle_feature(netdev, &oper_features, features,
- NETIF_F_RXALL, set_feature_rx_all);
- err |= mlx5e_handle_feature(netdev, &oper_features, features,
- NETIF_F_RXFCS, set_feature_rx_fcs);
- err |= mlx5e_handle_feature(netdev, &oper_features, features,
- NETIF_F_HW_VLAN_CTAG_RX, set_feature_rx_vlan);
+ err |= MLX5E_HANDLE_FEATURE(NETIF_F_HW_TC, set_feature_tc_num_filters);
+ err |= MLX5E_HANDLE_FEATURE(NETIF_F_RXALL, set_feature_rx_all);
+ err |= MLX5E_HANDLE_FEATURE(NETIF_F_RXFCS, set_feature_rx_fcs);
+ err |= MLX5E_HANDLE_FEATURE(NETIF_F_HW_VLAN_CTAG_RX, set_feature_rx_vlan);
#ifdef CONFIG_RFS_ACCEL
- err |= mlx5e_handle_feature(netdev, &oper_features, features,
- NETIF_F_NTUPLE, set_feature_arfs);
+ err |= MLX5E_HANDLE_FEATURE(NETIF_F_NTUPLE, set_feature_arfs);
#endif
if (err) {
@@ -3287,34 +3436,40 @@ static int mlx5e_change_mtu(struct net_device *netdev, int new_mtu)
{
struct mlx5e_priv *priv = netdev_priv(netdev);
struct mlx5e_channels new_channels = {};
- int curr_mtu;
+ struct mlx5e_params *params;
int err = 0;
bool reset;
mutex_lock(&priv->state_lock);
- reset = !priv->channels.params.lro_en &&
- (priv->channels.params.rq_wq_type !=
- MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ);
+ params = &priv->channels.params;
+ reset = !params->lro_en;
reset = reset && test_bit(MLX5E_STATE_OPENED, &priv->state);
- curr_mtu = netdev->mtu;
- netdev->mtu = new_mtu;
+ new_channels.params = *params;
+ new_channels.params.sw_mtu = new_mtu;
+
+ if (params->rq_wq_type != MLX5_WQ_TYPE_LINKED_LIST) {
+ u8 ppw_old = mlx5e_mpwqe_log_pkts_per_wqe(params);
+ u8 ppw_new = mlx5e_mpwqe_log_pkts_per_wqe(&new_channels.params);
+
+ reset = reset && (ppw_old != ppw_new);
+ }
if (!reset) {
+ params->sw_mtu = new_mtu;
mlx5e_set_dev_port_mtu(priv);
+ netdev->mtu = params->sw_mtu;
goto out;
}
- new_channels.params = priv->channels.params;
err = mlx5e_open_channels(priv, &new_channels);
- if (err) {
- netdev->mtu = curr_mtu;
+ if (err)
goto out;
- }
mlx5e_switch_priv_channels(priv, &new_channels, mlx5e_set_dev_port_mtu);
+ netdev->mtu = new_channels.params.sw_mtu;
out:
mutex_unlock(&priv->state_lock);
@@ -3604,21 +3759,11 @@ static netdev_features_t mlx5e_features_check(struct sk_buff *skb,
static bool mlx5e_tx_timeout_eq_recover(struct net_device *dev,
struct mlx5e_txqsq *sq)
{
- struct mlx5e_priv *priv = netdev_priv(dev);
- struct mlx5_core_dev *mdev = priv->mdev;
- int irqn_not_used, eqn;
- struct mlx5_eq *eq;
+ struct mlx5_eq *eq = sq->cq.mcq.eq;
u32 eqe_count;
- if (mlx5_vector2eqn(mdev, sq->cq.mcq.vector, &eqn, &irqn_not_used))
- return false;
-
- eq = mlx5_eqn2eq(mdev, eqn);
- if (IS_ERR(eq))
- return false;
-
netdev_err(dev, "EQ 0x%x: Cons = 0x%x, irqn = 0x%x\n",
- eqn, eq->cons_index, eq->irqn);
+ eq->eqn, eq->cons_index, eq->irqn);
eqe_count = mlx5_eq_poll_irq_disabled(eq);
if (!eqe_count)
@@ -3629,13 +3774,19 @@ static bool mlx5e_tx_timeout_eq_recover(struct net_device *dev,
return true;
}
-static void mlx5e_tx_timeout(struct net_device *dev)
+static void mlx5e_tx_timeout_work(struct work_struct *work)
{
- struct mlx5e_priv *priv = netdev_priv(dev);
+ struct mlx5e_priv *priv = container_of(work, struct mlx5e_priv,
+ tx_timeout_work);
+ struct net_device *dev = priv->netdev;
bool reopen_channels = false;
- int i;
+ int i, err;
- netdev_err(dev, "TX timeout detected\n");
+ rtnl_lock();
+ mutex_lock(&priv->state_lock);
+
+ if (!test_bit(MLX5E_STATE_OPENED, &priv->state))
+ goto unlock;
for (i = 0; i < priv->channels.num * priv->channels.params.num_tc; i++) {
struct netdev_queue *dev_queue = netdev_get_tx_queue(dev, i);
@@ -3643,7 +3794,9 @@ static void mlx5e_tx_timeout(struct net_device *dev)
if (!netif_xmit_stopped(dev_queue))
continue;
- netdev_err(dev, "TX timeout on queue: %d, SQ: 0x%x, CQ: 0x%x, SQ Cons: 0x%x SQ Prod: 0x%x, usecs since last trans: %u\n",
+
+ netdev_err(dev,
+ "TX timeout on queue: %d, SQ: 0x%x, CQ: 0x%x, SQ Cons: 0x%x SQ Prod: 0x%x, usecs since last trans: %u\n",
i, sq->sqn, sq->cq.mcq.cqn, sq->cc, sq->pc,
jiffies_to_usecs(jiffies - dev_queue->trans_start));
@@ -3656,8 +3809,27 @@ static void mlx5e_tx_timeout(struct net_device *dev)
}
}
- if (reopen_channels && test_bit(MLX5E_STATE_OPENED, &priv->state))
- schedule_work(&priv->tx_timeout_work);
+ if (!reopen_channels)
+ goto unlock;
+
+ mlx5e_close_locked(dev);
+ err = mlx5e_open_locked(dev);
+ if (err)
+ netdev_err(priv->netdev,
+ "mlx5e_open_locked failed recovering from a tx_timeout, err(%d).\n",
+ err);
+
+unlock:
+ mutex_unlock(&priv->state_lock);
+ rtnl_unlock();
+}
+
+static void mlx5e_tx_timeout(struct net_device *dev)
+{
+ struct mlx5e_priv *priv = netdev_priv(dev);
+
+ netdev_err(dev, "TX timeout detected\n");
+ queue_work(priv->wq, &priv->tx_timeout_work);
}
static int mlx5e_xdp_set(struct net_device *netdev, struct bpf_prog *prog)
@@ -3707,7 +3879,7 @@ static int mlx5e_xdp_set(struct net_device *netdev, struct bpf_prog *prog)
bpf_prog_put(old_prog);
if (reset) /* change RQ type according to priv->xdp_prog */
- mlx5e_set_rq_params(priv->mdev, &priv->channels.params);
+ mlx5e_set_rq_type(priv->mdev, &priv->channels.params);
if (was_opened && reset)
mlx5e_open_locked(netdev);
@@ -3852,15 +4024,6 @@ static int mlx5e_check_required_hca_cap(struct mlx5_core_dev *mdev)
return 0;
}
-u16 mlx5e_get_max_inline_cap(struct mlx5_core_dev *mdev)
-{
- int bf_buf_size = (1 << MLX5_CAP_GEN(mdev, log_bf_reg_size)) / 2;
-
- return bf_buf_size -
- sizeof(struct mlx5e_tx_wqe) +
- 2 /*sizeof(mlx5e_tx_wqe.inline_hdr_start)*/;
-}
-
void mlx5e_build_default_indir_rqt(u32 *indirection_rqt, int len,
int num_channels)
{
@@ -3870,46 +4033,20 @@ void mlx5e_build_default_indir_rqt(u32 *indirection_rqt, int len,
indirection_rqt[i] = i % num_channels;
}
-static int mlx5e_get_pci_bw(struct mlx5_core_dev *mdev, u32 *pci_bw)
+static bool slow_pci_heuristic(struct mlx5_core_dev *mdev)
{
- enum pcie_link_width width;
- enum pci_bus_speed speed;
- int err = 0;
-
- err = pcie_get_minimum_link(mdev->pdev, &speed, &width);
- if (err)
- return err;
-
- if (speed == PCI_SPEED_UNKNOWN || width == PCIE_LNK_WIDTH_UNKNOWN)
- return -EINVAL;
-
- switch (speed) {
- case PCIE_SPEED_2_5GT:
- *pci_bw = 2500 * width;
- break;
- case PCIE_SPEED_5_0GT:
- *pci_bw = 5000 * width;
- break;
- case PCIE_SPEED_8_0GT:
- *pci_bw = 8000 * width;
- break;
- default:
- return -EINVAL;
- }
+ u32 link_speed = 0;
+ u32 pci_bw = 0;
- return 0;
-}
+ mlx5e_get_max_linkspeed(mdev, &link_speed);
+ pci_bw = pcie_bandwidth_available(mdev->pdev, NULL, NULL, NULL);
+ mlx5_core_dbg_once(mdev, "Max link speed = %d, PCI BW = %d\n",
+ link_speed, pci_bw);
-static bool cqe_compress_heuristic(u32 link_speed, u32 pci_bw)
-{
- return (link_speed && pci_bw &&
- (pci_bw < 40000) && (pci_bw < link_speed));
-}
+#define MLX5E_SLOW_PCI_RATIO (2)
-static bool hw_lro_heuristic(u32 link_speed, u32 pci_bw)
-{
- return !(link_speed && pci_bw &&
- (pci_bw <= 16000) && (pci_bw < link_speed));
+ return link_speed && pci_bw &&
+ link_speed > MLX5E_SLOW_PCI_RATIO * pci_bw;
}
void mlx5e_set_tx_cq_mode_params(struct mlx5e_params *params, u8 cq_period_mode)
@@ -3961,7 +4098,7 @@ void mlx5e_set_rx_cq_mode_params(struct mlx5e_params *params, u8 cq_period_mode)
MLX5_CQ_PERIOD_MODE_START_FROM_CQE);
}
-u32 mlx5e_choose_lro_timeout(struct mlx5_core_dev *mdev, u32 wanted_timeout)
+static u32 mlx5e_choose_lro_timeout(struct mlx5_core_dev *mdev, u32 wanted_timeout)
{
int i;
@@ -3975,20 +4112,15 @@ u32 mlx5e_choose_lro_timeout(struct mlx5_core_dev *mdev, u32 wanted_timeout)
void mlx5e_build_nic_params(struct mlx5_core_dev *mdev,
struct mlx5e_params *params,
- u16 max_channels)
+ u16 max_channels, u16 mtu)
{
- u8 cq_period_mode = 0;
- u32 link_speed = 0;
- u32 pci_bw = 0;
+ u8 rx_cq_period_mode;
+ params->sw_mtu = mtu;
+ params->hard_mtu = MLX5E_ETH_HARD_MTU;
params->num_channels = max_channels;
params->num_tc = 1;
- mlx5e_get_max_linkspeed(mdev, &link_speed);
- mlx5e_get_pci_bw(mdev, &pci_bw);
- mlx5_core_dbg(mdev, "Max link speed = %d, PCI BW = %d\n",
- link_speed, pci_bw);
-
/* SQ */
params->log_sq_size = is_kdump_kernel() ?
MLX5E_PARAMS_MINIMUM_LOG_SQ_SIZE :
@@ -3998,30 +4130,34 @@ void mlx5e_build_nic_params(struct mlx5_core_dev *mdev,
params->rx_cqe_compress_def = false;
if (MLX5_CAP_GEN(mdev, cqe_compression) &&
MLX5_CAP_GEN(mdev, vport_group_manager))
- params->rx_cqe_compress_def = cqe_compress_heuristic(link_speed, pci_bw);
+ params->rx_cqe_compress_def = slow_pci_heuristic(mdev);
MLX5E_SET_PFLAG(params, MLX5E_PFLAG_RX_CQE_COMPRESS, params->rx_cqe_compress_def);
/* RQ */
- mlx5e_set_rq_params(mdev, params);
+ if (mlx5e_striding_rq_possible(mdev, params))
+ MLX5E_SET_PFLAG(params, MLX5E_PFLAG_RX_STRIDING_RQ,
+ !slow_pci_heuristic(mdev));
+ mlx5e_set_rq_type(mdev, params);
+ mlx5e_init_rq_type_params(mdev, params);
/* HW LRO */
/* TODO: && MLX5_CAP_ETH(mdev, lro_cap) */
if (params->rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ)
- params->lro_en = hw_lro_heuristic(link_speed, pci_bw);
+ if (!mlx5e_rx_mpwqe_is_linear_skb(mdev, params))
+ params->lro_en = !slow_pci_heuristic(mdev);
params->lro_timeout = mlx5e_choose_lro_timeout(mdev, MLX5E_DEFAULT_LRO_TIMEOUT);
/* CQ moderation params */
- cq_period_mode = MLX5_CAP_GEN(mdev, cq_period_start_from_cqe) ?
+ rx_cq_period_mode = MLX5_CAP_GEN(mdev, cq_period_start_from_cqe) ?
MLX5_CQ_PERIOD_MODE_START_FROM_CQE :
MLX5_CQ_PERIOD_MODE_START_FROM_EQE;
params->rx_dim_enabled = MLX5_CAP_GEN(mdev, cq_moderation);
- mlx5e_set_rx_cq_mode_params(params, cq_period_mode);
- mlx5e_set_tx_cq_mode_params(params, cq_period_mode);
+ mlx5e_set_rx_cq_mode_params(params, rx_cq_period_mode);
+ mlx5e_set_tx_cq_mode_params(params, MLX5_CQ_PERIOD_MODE_START_FROM_EQE);
/* TX inline */
- params->tx_max_inline = mlx5e_get_max_inline_cap(mdev);
params->tx_min_inline_mode = mlx5e_params_calculate_tx_min_inline(mdev);
/* RSS */
@@ -4043,9 +4179,9 @@ static void mlx5e_build_nic_netdev_priv(struct mlx5_core_dev *mdev,
priv->profile = profile;
priv->ppriv = ppriv;
priv->msglevel = MLX5E_MSG_LEVEL;
- priv->hard_mtu = MLX5E_ETH_HARD_MTU;
- mlx5e_build_nic_params(mdev, &priv->channels.params, profile->max_nch(mdev));
+ mlx5e_build_nic_params(mdev, &priv->channels.params,
+ profile->max_nch(mdev), netdev->mtu);
mutex_init(&priv->state_lock);
@@ -4069,7 +4205,7 @@ static void mlx5e_set_netdev_dev_addr(struct net_device *netdev)
}
}
-#if IS_ENABLED(CONFIG_NET_SWITCHDEV) && IS_ENABLED(CONFIG_MLX5_ESWITCH)
+#if IS_ENABLED(CONFIG_MLX5_ESWITCH)
static const struct switchdev_ops mlx5e_switchdev_ops = {
.switchdev_port_attr_get = mlx5e_attr_get,
};
@@ -4104,6 +4240,9 @@ static void mlx5e_build_nic_netdev(struct net_device *netdev)
netdev->vlan_features |= NETIF_F_RXCSUM;
netdev->vlan_features |= NETIF_F_RXHASH;
+ netdev->hw_enc_features |= NETIF_F_HW_VLAN_CTAG_TX;
+ netdev->hw_enc_features |= NETIF_F_HW_VLAN_CTAG_RX;
+
if (!!MLX5_CAP_ETH(mdev, lro_cap))
netdev->vlan_features |= NETIF_F_LRO;
@@ -4175,7 +4314,7 @@ static void mlx5e_build_nic_netdev(struct net_device *netdev)
mlx5e_set_netdev_dev_addr(netdev);
-#if IS_ENABLED(CONFIG_NET_SWITCHDEV) && IS_ENABLED(CONFIG_MLX5_ESWITCH)
+#if IS_ENABLED(CONFIG_MLX5_ESWITCH)
if (MLX5_VPORT_MANAGER(mdev))
netdev->switchdev_ops = &mlx5e_switchdev_ops;
#endif
@@ -4183,7 +4322,7 @@ static void mlx5e_build_nic_netdev(struct net_device *netdev)
mlx5e_ipsec_build_netdev(priv);
}
-static void mlx5e_create_q_counter(struct mlx5e_priv *priv)
+static void mlx5e_create_q_counters(struct mlx5e_priv *priv)
{
struct mlx5_core_dev *mdev = priv->mdev;
int err;
@@ -4193,14 +4332,21 @@ static void mlx5e_create_q_counter(struct mlx5e_priv *priv)
mlx5_core_warn(mdev, "alloc queue counter failed, %d\n", err);
priv->q_counter = 0;
}
+
+ err = mlx5_core_alloc_q_counter(mdev, &priv->drop_rq_q_counter);
+ if (err) {
+ mlx5_core_warn(mdev, "alloc drop RQ counter failed, %d\n", err);
+ priv->drop_rq_q_counter = 0;
+ }
}
-static void mlx5e_destroy_q_counter(struct mlx5e_priv *priv)
+static void mlx5e_destroy_q_counters(struct mlx5e_priv *priv)
{
- if (!priv->q_counter)
- return;
+ if (priv->q_counter)
+ mlx5_core_dealloc_q_counter(priv->mdev, priv->q_counter);
- mlx5_core_dealloc_q_counter(priv->mdev, priv->q_counter);
+ if (priv->drop_rq_q_counter)
+ mlx5_core_dealloc_q_counter(priv->mdev, priv->drop_rq_q_counter);
}
static void mlx5e_nic_init(struct mlx5_core_dev *mdev,
@@ -4312,7 +4458,7 @@ static void mlx5e_nic_enable(struct mlx5e_priv *priv)
/* MTU range: 68 - hw-specific max */
netdev->min_mtu = ETH_MIN_MTU;
mlx5_query_port_max_mtu(priv->mdev, &max_mtu, 1);
- netdev->max_mtu = MLX5E_HW2SW_MTU(priv, max_mtu);
+ netdev->max_mtu = MLX5E_HW2SW_MTU(&priv->channels.params, max_mtu);
mlx5e_set_dev_port_mtu(priv);
mlx5_lag_add(mdev, netdev);
@@ -4327,12 +4473,6 @@ static void mlx5e_nic_enable(struct mlx5e_priv *priv)
#ifdef CONFIG_MLX5_CORE_EN_DCB
mlx5e_dcbnl_init_app(priv);
#endif
- /* Device already registered: sync netdev system state */
- if (mlx5e_vxlan_allowed(mdev)) {
- rtnl_lock();
- udp_tunnel_get_rx_info(netdev);
- rtnl_unlock();
- }
queue_work(priv->wq, &priv->set_rx_mode_work);
@@ -4439,18 +4579,18 @@ int mlx5e_attach_netdev(struct mlx5e_priv *priv)
if (err)
goto out;
- err = mlx5e_open_drop_rq(mdev, &priv->drop_rq);
+ mlx5e_create_q_counters(priv);
+
+ err = mlx5e_open_drop_rq(priv, &priv->drop_rq);
if (err) {
mlx5_core_err(mdev, "open drop rq failed, %d\n", err);
- goto err_cleanup_tx;
+ goto err_destroy_q_counters;
}
err = profile->init_rx(priv);
if (err)
goto err_close_drop_rq;
- mlx5e_create_q_counter(priv);
-
if (profile->enable)
profile->enable(priv);
@@ -4459,7 +4599,8 @@ int mlx5e_attach_netdev(struct mlx5e_priv *priv)
err_close_drop_rq:
mlx5e_close_drop_rq(&priv->drop_rq);
-err_cleanup_tx:
+err_destroy_q_counters:
+ mlx5e_destroy_q_counters(priv);
profile->cleanup_tx(priv);
out:
@@ -4476,9 +4617,9 @@ void mlx5e_detach_netdev(struct mlx5e_priv *priv)
profile->disable(priv);
flush_workqueue(priv->wq);
- mlx5e_destroy_q_counter(priv);
profile->cleanup_rx(priv);
mlx5e_close_drop_rq(&priv->drop_rq);
+ mlx5e_destroy_q_counters(priv);
profile->cleanup_tx(priv);
cancel_delayed_work_sync(&priv->update_stats_work);
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
index 363d8dcb7f17..d8f68e4d1018 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
@@ -44,6 +44,11 @@
#include "en_tc.h"
#include "fs_core.h"
+#define MLX5E_REP_PARAMS_LOG_SQ_SIZE \
+ max(0x6, MLX5E_PARAMS_MINIMUM_LOG_SQ_SIZE)
+#define MLX5E_REP_PARAMS_LOG_RQ_SIZE \
+ max(0x6, MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE)
+
static const char mlx5e_rep_driver_name[] = "mlx5e_rep";
static void mlx5e_rep_get_drvinfo(struct net_device *dev,
@@ -209,7 +214,7 @@ static void mlx5e_sqs2vport_stop(struct mlx5_eswitch *esw,
static int mlx5e_sqs2vport_start(struct mlx5_eswitch *esw,
struct mlx5_eswitch_rep *rep,
- u16 *sqns_array, int sqns_num)
+ u32 *sqns_array, int sqns_num)
{
struct mlx5_flow_handle *flow_rule;
struct mlx5e_rep_priv *rpriv;
@@ -255,9 +260,9 @@ int mlx5e_add_sqs_fwd_rules(struct mlx5e_priv *priv)
struct mlx5e_channel *c;
int n, tc, num_sqs = 0;
int err = -ENOMEM;
- u16 *sqs;
+ u32 *sqs;
- sqs = kcalloc(priv->channels.num * priv->channels.params.num_tc, sizeof(u16), GFP_KERNEL);
+ sqs = kcalloc(priv->channels.num * priv->channels.params.num_tc, sizeof(*sqs), GFP_KERNEL);
if (!sqs)
goto out;
@@ -288,7 +293,7 @@ void mlx5e_remove_sqs_fwd_rules(struct mlx5e_priv *priv)
static void mlx5e_rep_neigh_update_init_interval(struct mlx5e_rep_priv *rpriv)
{
#if IS_ENABLED(CONFIG_IPV6)
- unsigned long ipv6_interval = NEIGH_VAR(&ipv6_stub->nd_tbl->parms,
+ unsigned long ipv6_interval = NEIGH_VAR(&nd_tbl.parms,
DELAY_PROBE_TIME);
#else
unsigned long ipv6_interval = ~0UL;
@@ -424,7 +429,7 @@ static int mlx5e_rep_netevent_event(struct notifier_block *nb,
case NETEVENT_NEIGH_UPDATE:
n = ptr;
#if IS_ENABLED(CONFIG_IPV6)
- if (n->tbl != ipv6_stub->nd_tbl && n->tbl != &arp_tbl)
+ if (n->tbl != &nd_tbl && n->tbl != &arp_tbl)
#else
if (n->tbl != &arp_tbl)
#endif
@@ -472,7 +477,7 @@ static int mlx5e_rep_netevent_event(struct notifier_block *nb,
* done per device delay prob time parameter.
*/
#if IS_ENABLED(CONFIG_IPV6)
- if (!p->dev || (p->tbl != ipv6_stub->nd_tbl && p->tbl != &arp_tbl))
+ if (!p->dev || (p->tbl != &nd_tbl && p->tbl != &arp_tbl))
#else
if (!p->dev || p->tbl != &arp_tbl)
#endif
@@ -668,7 +673,6 @@ static int mlx5e_rep_open(struct net_device *dev)
struct mlx5e_priv *priv = netdev_priv(dev);
struct mlx5e_rep_priv *rpriv = priv->ppriv;
struct mlx5_eswitch_rep *rep = rpriv->rep;
- struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
int err;
mutex_lock(&priv->state_lock);
@@ -676,8 +680,9 @@ static int mlx5e_rep_open(struct net_device *dev)
if (err)
goto unlock;
- if (!mlx5_eswitch_set_vport_state(esw, rep->vport,
- MLX5_ESW_VPORT_ADMIN_STATE_UP))
+ if (!mlx5_modify_vport_admin_state(priv->mdev,
+ MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT,
+ rep->vport, MLX5_ESW_VPORT_ADMIN_STATE_UP))
netif_carrier_on(dev);
unlock:
@@ -690,11 +695,12 @@ static int mlx5e_rep_close(struct net_device *dev)
struct mlx5e_priv *priv = netdev_priv(dev);
struct mlx5e_rep_priv *rpriv = priv->ppriv;
struct mlx5_eswitch_rep *rep = rpriv->rep;
- struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
int ret;
mutex_lock(&priv->state_lock);
- (void)mlx5_eswitch_set_vport_state(esw, rep->vport, MLX5_ESW_VPORT_ADMIN_STATE_DOWN);
+ mlx5_modify_vport_admin_state(priv->mdev,
+ MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT,
+ rep->vport, MLX5_ESW_VPORT_ADMIN_STATE_DOWN);
ret = mlx5e_close_locked(dev);
mutex_unlock(&priv->state_lock);
return ret;
@@ -877,14 +883,14 @@ static void mlx5e_build_rep_params(struct mlx5_core_dev *mdev,
MLX5_CQ_PERIOD_MODE_START_FROM_CQE :
MLX5_CQ_PERIOD_MODE_START_FROM_EQE;
- params->log_sq_size = MLX5E_PARAMS_MINIMUM_LOG_SQ_SIZE;
+ params->hard_mtu = MLX5E_ETH_HARD_MTU;
+ params->log_sq_size = MLX5E_REP_PARAMS_LOG_SQ_SIZE;
params->rq_wq_type = MLX5_WQ_TYPE_LINKED_LIST;
- params->log_rq_size = MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE;
+ params->log_rq_mtu_frames = MLX5E_REP_PARAMS_LOG_RQ_SIZE;
params->rx_dim_enabled = MLX5_CAP_GEN(mdev, cq_moderation);
mlx5e_set_rx_cq_mode_params(params, cq_period_mode);
- params->tx_max_inline = mlx5e_get_max_inline_cap(mdev);
params->num_tc = 1;
params->lro_wqe_sz = MLX5E_PARAMS_DEFAULT_LRO_WQE_SZ;
@@ -899,9 +905,7 @@ static void mlx5e_build_rep_netdev(struct net_device *netdev)
netdev->ethtool_ops = &mlx5e_rep_ethtool_ops;
-#ifdef CONFIG_NET_SWITCHDEV
netdev->switchdev_ops = &mlx5e_rep_switchdev_ops;
-#endif
netdev->features |= NETIF_F_VLAN_CHALLENGED | NETIF_F_HW_TC | NETIF_F_NETNS_LOCAL;
netdev->hw_features |= NETIF_F_HW_TC;
@@ -927,8 +931,6 @@ static void mlx5e_init_rep(struct mlx5_core_dev *mdev,
priv->channels.params.num_channels = profile->max_nch(mdev);
- priv->hard_mtu = MLX5E_ETH_HARD_MTU;
-
mlx5e_build_rep_params(mdev, &priv->channels.params);
mlx5e_build_rep_netdev(netdev);
@@ -1156,6 +1158,15 @@ mlx5e_vport_rep_unload(struct mlx5_eswitch_rep *rep)
kfree(ppriv); /* mlx5e_rep_priv */
}
+static void *mlx5e_vport_rep_get_proto_dev(struct mlx5_eswitch_rep *rep)
+{
+ struct mlx5e_rep_priv *rpriv;
+
+ rpriv = mlx5e_rep_to_rep_priv(rep);
+
+ return rpriv->netdev;
+}
+
static void mlx5e_rep_register_vf_vports(struct mlx5e_priv *priv)
{
struct mlx5_core_dev *mdev = priv->mdev;
@@ -1168,6 +1179,7 @@ static void mlx5e_rep_register_vf_vports(struct mlx5e_priv *priv)
rep_if.load = mlx5e_vport_rep_load;
rep_if.unload = mlx5e_vport_rep_unload;
+ rep_if.get_proto_dev = mlx5e_vport_rep_get_proto_dev;
mlx5_eswitch_register_vport_rep(esw, vport, &rep_if, REP_ETH);
}
}
@@ -1195,6 +1207,7 @@ void mlx5e_register_vport_reps(struct mlx5e_priv *priv)
rep_if.load = mlx5e_nic_rep_load;
rep_if.unload = mlx5e_nic_rep_unload;
+ rep_if.get_proto_dev = mlx5e_vport_rep_get_proto_dev;
rep_if.priv = rpriv;
INIT_LIST_HEAD(&rpriv->vport_sqs_list);
mlx5_eswitch_register_vport_rep(esw, 0, &rep_if, REP_ETH); /* UPLINK PF vport*/
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
index e5c3ab46a24a..176645762e49 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
@@ -53,7 +53,7 @@ static inline bool mlx5e_rx_hw_stamp(struct hwtstamp_config *config)
static inline void mlx5e_read_cqe_slot(struct mlx5e_cq *cq, u32 cqcc,
void *data)
{
- u32 ci = cqcc & cq->wq.sz_m1;
+ u32 ci = cqcc & cq->wq.fbc.sz_m1;
memcpy(data, mlx5_cqwq_get_wqe(&cq->wq, ci), sizeof(struct mlx5_cqe64));
}
@@ -75,9 +75,10 @@ static inline void mlx5e_read_mini_arr_slot(struct mlx5e_cq *cq, u32 cqcc)
static inline void mlx5e_cqes_update_owner(struct mlx5e_cq *cq, u32 cqcc, int n)
{
- u8 op_own = (cqcc >> cq->wq.log_sz) & 1;
- u32 wq_sz = 1 << cq->wq.log_sz;
- u32 ci = cqcc & cq->wq.sz_m1;
+ struct mlx5_frag_buf_ctrl *fbc = &cq->wq.fbc;
+ u8 op_own = (cqcc >> fbc->log_sz) & 1;
+ u32 wq_sz = 1 << fbc->log_sz;
+ u32 ci = cqcc & fbc->sz_m1;
u32 ci_top = min_t(u32, wq_sz, ci + n);
for (; ci < ci_top; ci++, n--) {
@@ -102,7 +103,7 @@ static inline void mlx5e_decompress_cqe(struct mlx5e_rq *rq,
cq->title.byte_cnt = cq->mini_arr[cq->mini_arr_idx].byte_cnt;
cq->title.check_sum = cq->mini_arr[cq->mini_arr_idx].checksum;
cq->title.op_own &= 0xf0;
- cq->title.op_own |= 0x01 & (cqcc >> cq->wq.log_sz);
+ cq->title.op_own |= 0x01 & (cqcc >> cq->wq.fbc.log_sz);
cq->title.wqe_counter = cpu_to_be16(cq->decmprs_wqe_counter);
if (rq->wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ)
@@ -295,46 +296,36 @@ void mlx5e_dealloc_rx_wqe(struct mlx5e_rq *rq, u16 ix)
mlx5e_free_rx_wqe(rq, wi);
}
-static inline int mlx5e_mpwqe_strides_per_page(struct mlx5e_rq *rq)
-{
- return rq->mpwqe.num_strides >> MLX5_MPWRQ_WQE_PAGE_ORDER;
-}
-
static inline void mlx5e_add_skb_frag_mpwqe(struct mlx5e_rq *rq,
struct sk_buff *skb,
- struct mlx5e_mpw_info *wi,
- u32 page_idx, u32 frag_offset,
- u32 len)
+ struct mlx5e_dma_info *di,
+ u32 frag_offset, u32 len)
{
unsigned int truesize = ALIGN(len, BIT(rq->mpwqe.log_stride_sz));
dma_sync_single_for_cpu(rq->pdev,
- wi->umr.dma_info[page_idx].addr + frag_offset,
+ di->addr + frag_offset,
len, DMA_FROM_DEVICE);
- wi->skbs_frags[page_idx]++;
+ page_ref_inc(di->page);
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
- wi->umr.dma_info[page_idx].page, frag_offset,
- len, truesize);
+ di->page, frag_offset, len, truesize);
}
static inline void
mlx5e_copy_skb_header_mpwqe(struct device *pdev,
struct sk_buff *skb,
- struct mlx5e_mpw_info *wi,
- u32 page_idx, u32 offset,
- u32 headlen)
+ struct mlx5e_dma_info *dma_info,
+ u32 offset, u32 headlen)
{
u16 headlen_pg = min_t(u32, headlen, PAGE_SIZE - offset);
- struct mlx5e_dma_info *dma_info = &wi->umr.dma_info[page_idx];
unsigned int len;
/* Aligning len to sizeof(long) optimizes memcpy performance */
len = ALIGN(headlen_pg, sizeof(long));
dma_sync_single_for_cpu(pdev, dma_info->addr + offset, len,
DMA_FROM_DEVICE);
- skb_copy_to_linear_data_offset(skb, 0,
- page_address(dma_info->page) + offset,
- len);
+ skb_copy_to_linear_data(skb, page_address(dma_info->page) + offset, len);
+
if (unlikely(offset + headlen > PAGE_SIZE)) {
dma_info++;
headlen_pg = len;
@@ -347,14 +338,49 @@ mlx5e_copy_skb_header_mpwqe(struct device *pdev,
}
}
-static inline void mlx5e_post_umr_wqe(struct mlx5e_rq *rq, u16 ix)
+void mlx5e_free_rx_mpwqe(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi)
+{
+ const bool no_xdp_xmit =
+ bitmap_empty(wi->xdp_xmit_bitmap, MLX5_MPWRQ_PAGES_PER_WQE);
+ struct mlx5e_dma_info *dma_info = wi->umr.dma_info;
+ int i;
+
+ for (i = 0; i < MLX5_MPWRQ_PAGES_PER_WQE; i++)
+ if (no_xdp_xmit || !test_bit(i, wi->xdp_xmit_bitmap))
+ mlx5e_page_release(rq, &dma_info[i], true);
+}
+
+static void mlx5e_post_rx_mpwqe(struct mlx5e_rq *rq)
+{
+ struct mlx5_wq_ll *wq = &rq->wq;
+ struct mlx5e_rx_wqe *wqe = mlx5_wq_ll_get_wqe(wq, wq->head);
+
+ rq->mpwqe.umr_in_progress = false;
+
+ mlx5_wq_ll_push(wq, be16_to_cpu(wqe->next.next_wqe_index));
+
+ /* ensure wqes are visible to device before updating doorbell record */
+ dma_wmb();
+
+ mlx5_wq_ll_update_db_record(wq);
+}
+
+static inline u16 mlx5e_icosq_wrap_cnt(struct mlx5e_icosq *sq)
+{
+ return sq->pc >> MLX5E_PARAMS_MINIMUM_LOG_SQ_SIZE;
+}
+
+static int mlx5e_alloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix)
{
struct mlx5e_mpw_info *wi = &rq->mpwqe.info[ix];
+ struct mlx5e_dma_info *dma_info = &wi->umr.dma_info[0];
struct mlx5e_icosq *sq = &rq->channel->icosq;
struct mlx5_wq_cyc *wq = &sq->wq;
- struct mlx5e_umr_wqe *wqe;
- u8 num_wqebbs = DIV_ROUND_UP(sizeof(*wqe), MLX5_SEND_WQE_BB);
+ struct mlx5e_umr_wqe *umr_wqe;
+ u16 xlt_offset = ix << (MLX5E_LOG_ALIGNED_MPWQE_PPW - 1);
+ int err;
u16 pi;
+ int i;
/* fill sq edge with nops to avoid wqe wrap around */
while ((pi = (sq->pc & wq->sz_m1)) > sq->edge) {
@@ -362,90 +388,44 @@ static inline void mlx5e_post_umr_wqe(struct mlx5e_rq *rq, u16 ix)
mlx5e_post_nop(wq, sq->sqn, &sq->pc);
}
- wqe = mlx5_wq_cyc_get_wqe(wq, pi);
- memcpy(wqe, &wi->umr.wqe, sizeof(*wqe));
- wqe->ctrl.opmod_idx_opcode =
- cpu_to_be32((sq->pc << MLX5_WQE_CTRL_WQE_INDEX_SHIFT) |
- MLX5_OPCODE_UMR);
-
- sq->db.ico_wqe[pi].opcode = MLX5_OPCODE_UMR;
- sq->pc += num_wqebbs;
- mlx5e_notify_hw(&sq->wq, sq->pc, sq->uar_map, &wqe->ctrl);
-}
-
-static int mlx5e_alloc_rx_umr_mpwqe(struct mlx5e_rq *rq,
- u16 ix)
-{
- struct mlx5e_mpw_info *wi = &rq->mpwqe.info[ix];
- int pg_strides = mlx5e_mpwqe_strides_per_page(rq);
- struct mlx5e_dma_info *dma_info = &wi->umr.dma_info[0];
- int err;
- int i;
+ umr_wqe = mlx5_wq_cyc_get_wqe(wq, pi);
+ if (unlikely(mlx5e_icosq_wrap_cnt(sq) < 2))
+ memcpy(umr_wqe, &rq->mpwqe.umr_wqe,
+ offsetof(struct mlx5e_umr_wqe, inline_mtts));
for (i = 0; i < MLX5_MPWRQ_PAGES_PER_WQE; i++, dma_info++) {
err = mlx5e_page_alloc_mapped(rq, dma_info);
if (unlikely(err))
goto err_unmap;
- wi->umr.mtt[i] = cpu_to_be64(dma_info->addr | MLX5_EN_WR);
- page_ref_add(dma_info->page, pg_strides);
+ umr_wqe->inline_mtts[i].ptag = cpu_to_be64(dma_info->addr | MLX5_EN_WR);
}
- memset(wi->skbs_frags, 0, sizeof(*wi->skbs_frags) * MLX5_MPWRQ_PAGES_PER_WQE);
+ bitmap_zero(wi->xdp_xmit_bitmap, MLX5_MPWRQ_PAGES_PER_WQE);
wi->consumed_strides = 0;
+ rq->mpwqe.umr_in_progress = true;
+
+ umr_wqe->ctrl.opmod_idx_opcode =
+ cpu_to_be32((sq->pc << MLX5_WQE_CTRL_WQE_INDEX_SHIFT) |
+ MLX5_OPCODE_UMR);
+ umr_wqe->uctrl.xlt_offset = cpu_to_be16(xlt_offset);
+
+ sq->db.ico_wqe[pi].opcode = MLX5_OPCODE_UMR;
+ sq->pc += MLX5E_UMR_WQEBBS;
+ mlx5e_notify_hw(&sq->wq, sq->pc, sq->uar_map, &umr_wqe->ctrl);
+
return 0;
err_unmap:
while (--i >= 0) {
dma_info--;
- page_ref_sub(dma_info->page, pg_strides);
mlx5e_page_release(rq, dma_info, true);
}
+ rq->stats.buff_alloc_err++;
return err;
}
-void mlx5e_free_rx_mpwqe(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi)
-{
- int pg_strides = mlx5e_mpwqe_strides_per_page(rq);
- struct mlx5e_dma_info *dma_info = &wi->umr.dma_info[0];
- int i;
-
- for (i = 0; i < MLX5_MPWRQ_PAGES_PER_WQE; i++, dma_info++) {
- page_ref_sub(dma_info->page, pg_strides - wi->skbs_frags[i]);
- mlx5e_page_release(rq, dma_info, true);
- }
-}
-
-static void mlx5e_post_rx_mpwqe(struct mlx5e_rq *rq)
-{
- struct mlx5_wq_ll *wq = &rq->wq;
- struct mlx5e_rx_wqe *wqe = mlx5_wq_ll_get_wqe(wq, wq->head);
-
- rq->mpwqe.umr_in_progress = false;
-
- mlx5_wq_ll_push(wq, be16_to_cpu(wqe->next.next_wqe_index));
-
- /* ensure wqes are visible to device before updating doorbell record */
- dma_wmb();
-
- mlx5_wq_ll_update_db_record(wq);
-}
-
-static int mlx5e_alloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix)
-{
- int err;
-
- err = mlx5e_alloc_rx_umr_mpwqe(rq, ix);
- if (unlikely(err)) {
- rq->stats.buff_alloc_err++;
- return err;
- }
- rq->mpwqe.umr_in_progress = true;
- mlx5e_post_umr_wqe(rq, ix);
- return 0;
-}
-
void mlx5e_dealloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix)
{
struct mlx5e_mpw_info *wi = &rq->mpwqe.info[ix];
@@ -544,7 +524,7 @@ bool mlx5e_post_rx_mpwqes(struct mlx5e_rq *rq)
if (!rq->mpwqe.umr_in_progress)
mlx5e_alloc_rx_mpwqe(rq, wq->head);
- return true;
+ return false;
}
static void mlx5e_lro_update_tcp_hdr(struct mlx5_cqe64 *cqe, struct tcphdr *tcp)
@@ -766,8 +746,7 @@ static inline bool mlx5e_xmit_xdp_frame(struct mlx5e_rq *rq,
prefetchw(wqe);
- if (unlikely(dma_len < MLX5E_XDP_MIN_INLINE ||
- MLX5E_SW2HW_MTU(rq->channel->priv, rq->netdev->mtu) < dma_len)) {
+ if (unlikely(dma_len < MLX5E_XDP_MIN_INLINE || rq->hw_mtu < dma_len)) {
rq->stats.xdp_drop++;
return false;
}
@@ -806,7 +785,7 @@ static inline bool mlx5e_xmit_xdp_frame(struct mlx5e_rq *rq,
/* move page to reference to sq responsibility,
* and mark so it's not put back in page-cache.
*/
- rq->wqe.xdp_xmit = true;
+ __set_bit(MLX5E_RQ_FLAG_XDP_XMIT, rq->flags); /* non-atomic */
sq->db.di[pi] = *di;
sq->pc++;
@@ -855,6 +834,24 @@ static inline int mlx5e_xdp_handle(struct mlx5e_rq *rq,
}
static inline
+struct sk_buff *mlx5e_build_linear_skb(struct mlx5e_rq *rq, void *va,
+ u32 frag_size, u16 headroom,
+ u32 cqe_bcnt)
+{
+ struct sk_buff *skb = build_skb(va, frag_size);
+
+ if (unlikely(!skb)) {
+ rq->stats.buff_alloc_err++;
+ return NULL;
+ }
+
+ skb_reserve(skb, headroom);
+ skb_put(skb, cqe_bcnt);
+
+ return skb;
+}
+
+static inline
struct sk_buff *skb_from_cqe(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe,
struct mlx5e_wqe_frag_info *wi, u32 cqe_bcnt)
{
@@ -869,10 +866,8 @@ struct sk_buff *skb_from_cqe(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe,
data = va + rx_headroom;
frag_size = MLX5_SKB_FRAG_SZ(rx_headroom + cqe_bcnt);
- dma_sync_single_range_for_cpu(rq->pdev,
- di->addr + wi->offset,
- 0, frag_size,
- DMA_FROM_DEVICE);
+ dma_sync_single_range_for_cpu(rq->pdev, di->addr, wi->offset,
+ frag_size, DMA_FROM_DEVICE);
prefetch(data);
wi->offset += frag_size;
@@ -887,18 +882,13 @@ struct sk_buff *skb_from_cqe(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe,
if (consumed)
return NULL; /* page/packet was consumed by XDP */
- skb = build_skb(va, frag_size);
- if (unlikely(!skb)) {
- rq->stats.buff_alloc_err++;
+ skb = mlx5e_build_linear_skb(rq, va, frag_size, rx_headroom, cqe_bcnt);
+ if (unlikely(!skb))
return NULL;
- }
/* queue up for recycling/reuse */
page_ref_inc(di->page);
- skb_reserve(skb, rx_headroom);
- skb_put(skb, cqe_bcnt);
-
return skb;
}
@@ -920,9 +910,8 @@ void mlx5e_handle_rx_cqe(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe)
skb = skb_from_cqe(rq, cqe, wi, cqe_bcnt);
if (!skb) {
/* probably for XDP */
- if (rq->wqe.xdp_xmit) {
+ if (__test_and_clear_bit(MLX5E_RQ_FLAG_XDP_XMIT, rq->flags)) {
wi->di.page = NULL;
- rq->wqe.xdp_xmit = false;
/* do not return page to cache, it will be returned on XDP_TX completion */
goto wq_ll_pop;
}
@@ -962,9 +951,8 @@ void mlx5e_handle_rx_cqe_rep(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe)
skb = skb_from_cqe(rq, cqe, wi, cqe_bcnt);
if (!skb) {
- if (rq->wqe.xdp_xmit) {
+ if (__test_and_clear_bit(MLX5E_RQ_FLAG_XDP_XMIT, rq->flags)) {
wi->di.page = NULL;
- rq->wqe.xdp_xmit = false;
/* do not return page to cache, it will be returned on XDP_TX completion */
goto wq_ll_pop;
}
@@ -987,23 +975,28 @@ wq_ll_pop:
}
#endif
-static inline void mlx5e_mpwqe_fill_rx_skb(struct mlx5e_rq *rq,
- struct mlx5_cqe64 *cqe,
- struct mlx5e_mpw_info *wi,
- u32 cqe_bcnt,
- struct sk_buff *skb)
+struct sk_buff *
+mlx5e_skb_from_cqe_mpwrq_nonlinear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi,
+ u16 cqe_bcnt, u32 head_offset, u32 page_idx)
{
- u16 stride_ix = mpwrq_get_cqe_stride_index(cqe);
- u32 wqe_offset = stride_ix << rq->mpwqe.log_stride_sz;
- u32 head_offset = wqe_offset & (PAGE_SIZE - 1);
- u32 page_idx = wqe_offset >> PAGE_SHIFT;
- u32 head_page_idx = page_idx;
u16 headlen = min_t(u16, MLX5_MPWRQ_SMALL_PACKET_THRESHOLD, cqe_bcnt);
+ struct mlx5e_dma_info *di = &wi->umr.dma_info[page_idx];
u32 frag_offset = head_offset + headlen;
- u16 byte_cnt = cqe_bcnt - headlen;
+ u32 byte_cnt = cqe_bcnt - headlen;
+ struct mlx5e_dma_info *head_di = di;
+ struct sk_buff *skb;
+
+ skb = napi_alloc_skb(rq->cq.napi,
+ ALIGN(MLX5_MPWRQ_SMALL_PACKET_THRESHOLD, sizeof(long)));
+ if (unlikely(!skb)) {
+ rq->stats.buff_alloc_err++;
+ return NULL;
+ }
+
+ prefetchw(skb->data);
if (unlikely(frag_offset >= PAGE_SIZE)) {
- page_idx++;
+ di++;
frag_offset -= PAGE_SIZE;
}
@@ -1011,18 +1004,59 @@ static inline void mlx5e_mpwqe_fill_rx_skb(struct mlx5e_rq *rq,
u32 pg_consumed_bytes =
min_t(u32, PAGE_SIZE - frag_offset, byte_cnt);
- mlx5e_add_skb_frag_mpwqe(rq, skb, wi, page_idx, frag_offset,
+ mlx5e_add_skb_frag_mpwqe(rq, skb, di, frag_offset,
pg_consumed_bytes);
byte_cnt -= pg_consumed_bytes;
frag_offset = 0;
- page_idx++;
+ di++;
}
/* copy header */
- mlx5e_copy_skb_header_mpwqe(rq->pdev, skb, wi, head_page_idx,
+ mlx5e_copy_skb_header_mpwqe(rq->pdev, skb, head_di,
head_offset, headlen);
/* skb linear part was allocated with headlen and aligned to long */
skb->tail += headlen;
skb->len += headlen;
+
+ return skb;
+}
+
+struct sk_buff *
+mlx5e_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi,
+ u16 cqe_bcnt, u32 head_offset, u32 page_idx)
+{
+ struct mlx5e_dma_info *di = &wi->umr.dma_info[page_idx];
+ u16 rx_headroom = rq->buff.headroom;
+ u32 cqe_bcnt32 = cqe_bcnt;
+ struct sk_buff *skb;
+ void *va, *data;
+ u32 frag_size;
+ bool consumed;
+
+ va = page_address(di->page) + head_offset;
+ data = va + rx_headroom;
+ frag_size = MLX5_SKB_FRAG_SZ(rx_headroom + cqe_bcnt32);
+
+ dma_sync_single_range_for_cpu(rq->pdev, di->addr, head_offset,
+ frag_size, DMA_FROM_DEVICE);
+ prefetch(data);
+
+ rcu_read_lock();
+ consumed = mlx5e_xdp_handle(rq, di, va, &rx_headroom, &cqe_bcnt32);
+ rcu_read_unlock();
+ if (consumed) {
+ if (__test_and_clear_bit(MLX5E_RQ_FLAG_XDP_XMIT, rq->flags))
+ __set_bit(page_idx, wi->xdp_xmit_bitmap); /* non-atomic */
+ return NULL; /* page/packet was consumed by XDP */
+ }
+
+ skb = mlx5e_build_linear_skb(rq, va, frag_size, rx_headroom, cqe_bcnt32);
+ if (unlikely(!skb))
+ return NULL;
+
+ /* queue up for recycling/reuse */
+ page_ref_inc(di->page);
+
+ return skb;
}
void mlx5e_handle_rx_cqe_mpwrq(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe)
@@ -1030,7 +1064,11 @@ void mlx5e_handle_rx_cqe_mpwrq(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe)
u16 cstrides = mpwrq_get_cqe_consumed_strides(cqe);
u16 wqe_id = be16_to_cpu(cqe->wqe_id);
struct mlx5e_mpw_info *wi = &rq->mpwqe.info[wqe_id];
- struct mlx5e_rx_wqe *wqe = mlx5_wq_ll_get_wqe(&rq->wq, wqe_id);
+ u16 stride_ix = mpwrq_get_cqe_stride_index(cqe);
+ u32 wqe_offset = stride_ix << rq->mpwqe.log_stride_sz;
+ u32 head_offset = wqe_offset & (PAGE_SIZE - 1);
+ u32 page_idx = wqe_offset >> PAGE_SHIFT;
+ struct mlx5e_rx_wqe *wqe;
struct sk_buff *skb;
u16 cqe_bcnt;
@@ -1046,18 +1084,13 @@ void mlx5e_handle_rx_cqe_mpwrq(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe)
goto mpwrq_cqe_out;
}
- skb = napi_alloc_skb(rq->cq.napi,
- ALIGN(MLX5_MPWRQ_SMALL_PACKET_THRESHOLD,
- sizeof(long)));
- if (unlikely(!skb)) {
- rq->stats.buff_alloc_err++;
- goto mpwrq_cqe_out;
- }
-
- prefetchw(skb->data);
cqe_bcnt = mpwrq_get_cqe_byte_cnt(cqe);
- mlx5e_mpwqe_fill_rx_skb(rq, cqe, wi, cqe_bcnt, skb);
+ skb = rq->mpwqe.skb_from_cqe_mpwrq(rq, wi, cqe_bcnt, head_offset,
+ page_idx);
+ if (!skb)
+ goto mpwrq_cqe_out;
+
mlx5e_complete_rx_cqe(rq, cqe, cqe_bcnt, skb);
napi_gro_receive(rq->cq.napi, skb);
@@ -1065,6 +1098,7 @@ mpwrq_cqe_out:
if (likely(wi->consumed_strides < rq->mpwqe.num_strides))
return;
+ wqe = mlx5_wq_ll_get_wqe(&rq->wq, wqe_id);
mlx5e_free_rx_mpwqe(rq, wi);
mlx5_wq_ll_pop(&rq->wq, cqe->wqe_id, &wqe->next.next_wqe_index);
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c
index 5f0f3493d747..b08c94422907 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c
@@ -60,6 +60,8 @@ static const struct counter_desc sw_stats_desc[] = {
{ MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_queue_wake) },
{ MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_queue_dropped) },
{ MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_xmit_more) },
+ { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_cqe_err) },
+ { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_recover) },
{ MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_wqe_err) },
{ MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_mpwqe_filler) },
{ MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_buff_alloc_err) },
@@ -153,6 +155,8 @@ static void mlx5e_grp_sw_update_stats(struct mlx5e_priv *priv)
s->tx_queue_stopped += sq_stats->stopped;
s->tx_queue_wake += sq_stats->wake;
s->tx_queue_dropped += sq_stats->dropped;
+ s->tx_cqe_err += sq_stats->cqe_err;
+ s->tx_recover += sq_stats->recover;
s->tx_xmit_more += sq_stats->xmit_more;
s->tx_csum_partial_inner += sq_stats->csum_partial_inner;
s->tx_csum_none += sq_stats->csum_none;
@@ -170,11 +174,24 @@ static const struct counter_desc q_stats_desc[] = {
{ MLX5E_DECLARE_STAT(struct mlx5e_qcounter_stats, rx_out_of_buffer) },
};
+static const struct counter_desc drop_rq_stats_desc[] = {
+ { MLX5E_DECLARE_STAT(struct mlx5e_qcounter_stats, rx_if_down_packets) },
+};
+
#define NUM_Q_COUNTERS ARRAY_SIZE(q_stats_desc)
+#define NUM_DROP_RQ_COUNTERS ARRAY_SIZE(drop_rq_stats_desc)
static int mlx5e_grp_q_get_num_stats(struct mlx5e_priv *priv)
{
- return priv->q_counter ? NUM_Q_COUNTERS : 0;
+ int num_stats = 0;
+
+ if (priv->q_counter)
+ num_stats += NUM_Q_COUNTERS;
+
+ if (priv->drop_rq_q_counter)
+ num_stats += NUM_DROP_RQ_COUNTERS;
+
+ return num_stats;
}
static int mlx5e_grp_q_fill_strings(struct mlx5e_priv *priv, u8 *data, int idx)
@@ -182,7 +199,13 @@ static int mlx5e_grp_q_fill_strings(struct mlx5e_priv *priv, u8 *data, int idx)
int i;
for (i = 0; i < NUM_Q_COUNTERS && priv->q_counter; i++)
- strcpy(data + (idx++) * ETH_GSTRING_LEN, q_stats_desc[i].format);
+ strcpy(data + (idx++) * ETH_GSTRING_LEN,
+ q_stats_desc[i].format);
+
+ for (i = 0; i < NUM_DROP_RQ_COUNTERS && priv->drop_rq_q_counter; i++)
+ strcpy(data + (idx++) * ETH_GSTRING_LEN,
+ drop_rq_stats_desc[i].format);
+
return idx;
}
@@ -191,7 +214,11 @@ static int mlx5e_grp_q_fill_stats(struct mlx5e_priv *priv, u64 *data, int idx)
int i;
for (i = 0; i < NUM_Q_COUNTERS && priv->q_counter; i++)
- data[idx++] = MLX5E_READ_CTR32_CPU(&priv->stats.qcnt, q_stats_desc, i);
+ data[idx++] = MLX5E_READ_CTR32_CPU(&priv->stats.qcnt,
+ q_stats_desc, i);
+ for (i = 0; i < NUM_DROP_RQ_COUNTERS && priv->drop_rq_q_counter; i++)
+ data[idx++] = MLX5E_READ_CTR32_CPU(&priv->stats.qcnt,
+ drop_rq_stats_desc, i);
return idx;
}
@@ -199,16 +226,76 @@ static void mlx5e_grp_q_update_stats(struct mlx5e_priv *priv)
{
struct mlx5e_qcounter_stats *qcnt = &priv->stats.qcnt;
u32 out[MLX5_ST_SZ_DW(query_q_counter_out)];
- int err;
- if (!priv->q_counter)
- return;
+ if (priv->q_counter &&
+ !mlx5_core_query_q_counter(priv->mdev, priv->q_counter, 0, out,
+ sizeof(out)))
+ qcnt->rx_out_of_buffer = MLX5_GET(query_q_counter_out,
+ out, out_of_buffer);
+ if (priv->drop_rq_q_counter &&
+ !mlx5_core_query_q_counter(priv->mdev, priv->drop_rq_q_counter, 0,
+ out, sizeof(out)))
+ qcnt->rx_if_down_packets = MLX5_GET(query_q_counter_out, out,
+ out_of_buffer);
+}
+
+#define VNIC_ENV_OFF(c) MLX5_BYTE_OFF(query_vnic_env_out, c)
+static const struct counter_desc vnic_env_stats_desc[] = {
+ { "rx_steer_missed_packets",
+ VNIC_ENV_OFF(vport_env.nic_receive_steering_discard) },
+};
+
+#define NUM_VNIC_ENV_COUNTERS ARRAY_SIZE(vnic_env_stats_desc)
+
+static int mlx5e_grp_vnic_env_get_num_stats(struct mlx5e_priv *priv)
+{
+ return MLX5_CAP_GEN(priv->mdev, nic_receive_steering_discard) ?
+ NUM_VNIC_ENV_COUNTERS : 0;
+}
+
+static int mlx5e_grp_vnic_env_fill_strings(struct mlx5e_priv *priv, u8 *data,
+ int idx)
+{
+ int i;
+
+ if (!MLX5_CAP_GEN(priv->mdev, nic_receive_steering_discard))
+ return idx;
+
+ for (i = 0; i < NUM_VNIC_ENV_COUNTERS; i++)
+ strcpy(data + (idx++) * ETH_GSTRING_LEN,
+ vnic_env_stats_desc[i].format);
+ return idx;
+}
+
+static int mlx5e_grp_vnic_env_fill_stats(struct mlx5e_priv *priv, u64 *data,
+ int idx)
+{
+ int i;
+
+ if (!MLX5_CAP_GEN(priv->mdev, nic_receive_steering_discard))
+ return idx;
- err = mlx5_core_query_q_counter(priv->mdev, priv->q_counter, 0, out, sizeof(out));
- if (err)
+ for (i = 0; i < NUM_VNIC_ENV_COUNTERS; i++)
+ data[idx++] = MLX5E_READ_CTR64_BE(priv->stats.vnic.query_vnic_env_out,
+ vnic_env_stats_desc, i);
+ return idx;
+}
+
+static void mlx5e_grp_vnic_env_update_stats(struct mlx5e_priv *priv)
+{
+ u32 *out = (u32 *)priv->stats.vnic.query_vnic_env_out;
+ int outlen = MLX5_ST_SZ_BYTES(query_vnic_env_out);
+ u32 in[MLX5_ST_SZ_DW(query_vnic_env_in)] = {0};
+ struct mlx5_core_dev *mdev = priv->mdev;
+
+ if (!MLX5_CAP_GEN(priv->mdev, nic_receive_steering_discard))
return;
- qcnt->rx_out_of_buffer = MLX5_GET(query_q_counter_out, out, out_of_buffer);
+ MLX5_SET(query_vnic_env_in, in, opcode,
+ MLX5_CMD_OP_QUERY_VNIC_ENV);
+ MLX5_SET(query_vnic_env_in, in, op_mod, 0);
+ MLX5_SET(query_vnic_env_in, in, other_vport, 0);
+ mlx5_cmd_exec(mdev, in, sizeof(in), out, outlen);
}
#define VPORT_COUNTER_OFF(c) MLX5_BYTE_OFF(query_vport_counter_out, c)
@@ -754,7 +841,15 @@ static const struct counter_desc pport_per_prio_pfc_stats_desc[] = {
{ "rx_%s_pause_transition", PPORT_PER_PRIO_OFF(rx_pause_transition) },
};
+static const struct counter_desc pport_pfc_stall_stats_desc[] = {
+ { "tx_pause_storm_warning_events ", PPORT_PER_PRIO_OFF(device_stall_minor_watermark_cnt) },
+ { "tx_pause_storm_error_events", PPORT_PER_PRIO_OFF(device_stall_critical_watermark_cnt) },
+};
+
#define NUM_PPORT_PER_PRIO_PFC_COUNTERS ARRAY_SIZE(pport_per_prio_pfc_stats_desc)
+#define NUM_PPORT_PFC_STALL_COUNTERS(priv) (ARRAY_SIZE(pport_pfc_stall_stats_desc) * \
+ MLX5_CAP_PCAM_FEATURE((priv)->mdev, pfcc_mask) * \
+ MLX5_CAP_DEBUG((priv)->mdev, stall_detect))
static unsigned long mlx5e_query_pfc_combined(struct mlx5e_priv *priv)
{
@@ -790,7 +885,8 @@ static int mlx5e_grp_per_prio_pfc_get_num_stats(struct mlx5e_priv *priv)
{
return (mlx5e_query_global_pause_combined(priv) +
hweight8(mlx5e_query_pfc_combined(priv))) *
- NUM_PPORT_PER_PRIO_PFC_COUNTERS;
+ NUM_PPORT_PER_PRIO_PFC_COUNTERS +
+ NUM_PPORT_PFC_STALL_COUNTERS(priv);
}
static int mlx5e_grp_per_prio_pfc_fill_strings(struct mlx5e_priv *priv,
@@ -818,6 +914,10 @@ static int mlx5e_grp_per_prio_pfc_fill_strings(struct mlx5e_priv *priv,
}
}
+ for (i = 0; i < NUM_PPORT_PFC_STALL_COUNTERS(priv); i++)
+ strcpy(data + (idx++) * ETH_GSTRING_LEN,
+ pport_pfc_stall_stats_desc[i].format);
+
return idx;
}
@@ -845,6 +945,10 @@ static int mlx5e_grp_per_prio_pfc_fill_stats(struct mlx5e_priv *priv,
}
}
+ for (i = 0; i < NUM_PPORT_PFC_STALL_COUNTERS(priv); i++)
+ data[idx++] = MLX5E_READ_CTR64_BE(&priv->stats.pport.per_prio_counters[0],
+ pport_pfc_stall_stats_desc, i);
+
return idx;
}
@@ -1003,6 +1107,8 @@ static const struct counter_desc sq_stats_desc[] = {
{ MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, wake) },
{ MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, dropped) },
{ MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, xmit_more) },
+ { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, cqe_err) },
+ { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, recover) },
};
static const struct counter_desc ch_stats_desc[] = {
@@ -1095,6 +1201,12 @@ const struct mlx5e_stats_grp mlx5e_stats_grps[] = {
.update_stats = mlx5e_grp_q_update_stats,
},
{
+ .get_num_stats = mlx5e_grp_vnic_env_get_num_stats,
+ .fill_strings = mlx5e_grp_vnic_env_fill_strings,
+ .fill_stats = mlx5e_grp_vnic_env_fill_stats,
+ .update_stats = mlx5e_grp_vnic_env_update_stats,
+ },
+ {
.get_num_stats = mlx5e_grp_vport_get_num_stats,
.fill_strings = mlx5e_grp_vport_fill_strings,
.fill_stats = mlx5e_grp_vport_fill_stats,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h
index 0b3320a2b072..53111a2df587 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h
@@ -78,6 +78,8 @@ struct mlx5e_sw_stats {
u64 tx_queue_wake;
u64 tx_queue_dropped;
u64 tx_xmit_more;
+ u64 tx_cqe_err;
+ u64 tx_recover;
u64 rx_wqe_err;
u64 rx_mpwqe_filler;
u64 rx_buff_alloc_err;
@@ -97,6 +99,11 @@ struct mlx5e_sw_stats {
struct mlx5e_qcounter_stats {
u32 rx_out_of_buffer;
+ u32 rx_if_down_packets;
+};
+
+struct mlx5e_vnic_env_stats {
+ __be64 query_vnic_env_out[MLX5_ST_SZ_QW(query_vnic_env_out)];
};
#define VPORT_COUNTER_GET(vstats, c) MLX5_GET64(query_vport_counter_out, \
@@ -192,6 +199,8 @@ struct mlx5e_sq_stats {
u64 stopped;
u64 wake;
u64 dropped;
+ u64 cqe_err;
+ u64 recover;
};
struct mlx5e_ch_stats {
@@ -201,6 +210,7 @@ struct mlx5e_ch_stats {
struct mlx5e_stats {
struct mlx5e_sw_stats sw;
struct mlx5e_qcounter_stats qcnt;
+ struct mlx5e_vnic_env_stats vnic;
struct mlx5e_vport_stats vport;
struct mlx5e_pport_stats pport;
struct rtnl_link_stats64 vf_vport;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
index fa86a1466718..4197001f9801 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
@@ -675,6 +675,7 @@ mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv,
struct mlx5_flow_destination dest[2] = {};
struct mlx5_flow_act flow_act = {
.action = attr->action,
+ .has_flow_tag = true,
.flow_tag = attr->flow_tag,
.encap_id = 0,
};
@@ -963,7 +964,7 @@ void mlx5e_tc_update_neigh_used_value(struct mlx5e_neigh_hash_entry *nhe)
tbl = &arp_tbl;
#if IS_ENABLED(CONFIG_IPV6)
else if (m_neigh->family == AF_INET6)
- tbl = ipv6_stub->nd_tbl;
+ tbl = &nd_tbl;
#endif
else
return;
@@ -2529,12 +2530,17 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
if (tcf_vlan_action(a) == TCA_VLAN_ACT_POP) {
attr->action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_POP;
} else if (tcf_vlan_action(a) == TCA_VLAN_ACT_PUSH) {
- if (tcf_vlan_push_proto(a) != htons(ETH_P_8021Q) ||
- tcf_vlan_push_prio(a))
- return -EOPNOTSUPP;
-
attr->action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH;
- attr->vlan = tcf_vlan_push_vid(a);
+ attr->vlan_vid = tcf_vlan_push_vid(a);
+ if (mlx5_eswitch_vlan_actions_supported(priv->mdev)) {
+ attr->vlan_prio = tcf_vlan_push_prio(a);
+ attr->vlan_proto = tcf_vlan_push_proto(a);
+ if (!attr->vlan_proto)
+ attr->vlan_proto = htons(ETH_P_8021Q);
+ } else if (tcf_vlan_push_proto(a) != htons(ETH_P_8021Q) ||
+ tcf_vlan_push_prio(a)) {
+ return -EOPNOTSUPP;
+ }
} else { /* action is TCA_VLAN_ACT_MODIFY */
return -EOPNOTSUPP;
}
@@ -2608,19 +2614,19 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv,
if (err != -EAGAIN)
flow->flags |= MLX5E_TC_FLOW_OFFLOADED;
+ if (!(flow->flags & MLX5E_TC_FLOW_ESWITCH) ||
+ !(flow->esw_attr->action & MLX5_FLOW_CONTEXT_ACTION_ENCAP))
+ kvfree(parse_attr);
+
err = rhashtable_insert_fast(&tc->ht, &flow->node,
tc->ht_params);
- if (err)
- goto err_del_rule;
+ if (err) {
+ mlx5e_tc_del_flow(priv, flow);
+ kfree(flow);
+ }
- if (flow->flags & MLX5E_TC_FLOW_ESWITCH &&
- !(flow->esw_attr->action & MLX5_FLOW_CONTEXT_ACTION_ENCAP))
- kvfree(parse_attr);
return err;
-err_del_rule:
- mlx5e_tc_del_flow(priv, flow);
-
err_free:
kvfree(parse_attr);
kfree(flow);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c
index 11b4f1089d1c..20297108528a 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c
@@ -417,6 +417,18 @@ netdev_tx_t mlx5e_xmit(struct sk_buff *skb, struct net_device *dev)
return mlx5e_sq_xmit(sq, skb, wqe, pi);
}
+static void mlx5e_dump_error_cqe(struct mlx5e_txqsq *sq,
+ struct mlx5_err_cqe *err_cqe)
+{
+ u32 ci = mlx5_cqwq_get_ci(&sq->cq.wq);
+
+ netdev_err(sq->channel->netdev,
+ "Error cqe on cqn 0x%x, ci 0x%x, sqn 0x%x, syndrome 0x%x, vendor syndrome 0x%x\n",
+ sq->cq.mcq.cqn, ci, sq->sqn, err_cqe->syndrome,
+ err_cqe->vendor_err_synd);
+ mlx5_dump_err_cqe(sq->cq.mdev, err_cqe);
+}
+
bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget)
{
struct mlx5e_txqsq *sq;
@@ -456,6 +468,17 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget)
wqe_counter = be16_to_cpu(cqe->wqe_counter);
+ if (unlikely(cqe->op_own >> 4 == MLX5_CQE_REQ_ERR)) {
+ if (!test_and_set_bit(MLX5E_SQ_STATE_RECOVERING,
+ &sq->state)) {
+ mlx5e_dump_error_cqe(sq,
+ (struct mlx5_err_cqe *)cqe);
+ queue_work(cq->channel->priv->wq,
+ &sq->recover.recover_work);
+ }
+ sq->stats.cqe_err++;
+ }
+
do {
struct mlx5e_tx_wqe_info *wi;
struct sk_buff *skb;
@@ -509,7 +532,9 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget)
netdev_tx_completed_queue(sq->txq, npkts, nbytes);
if (netif_tx_queue_stopped(sq->txq) &&
- mlx5e_wqc_has_room_for(&sq->wq, sq->cc, sq->pc, MLX5E_SQ_STOP_ROOM)) {
+ mlx5e_wqc_has_room_for(&sq->wq, sq->cc, sq->pc,
+ MLX5E_SQ_STOP_ROOM) &&
+ !test_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state)) {
netif_tx_wake_queue(sq->txq);
sq->stats.wake++;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c
index 25106e996a96..c1c94974e16b 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c
@@ -393,6 +393,51 @@ static void general_event_handler(struct mlx5_core_dev *dev,
}
}
+/* caller must eventually call mlx5_cq_put on the returned cq */
+static struct mlx5_core_cq *mlx5_eq_cq_get(struct mlx5_eq *eq, u32 cqn)
+{
+ struct mlx5_cq_table *table = &eq->cq_table;
+ struct mlx5_core_cq *cq = NULL;
+
+ spin_lock(&table->lock);
+ cq = radix_tree_lookup(&table->tree, cqn);
+ if (likely(cq))
+ mlx5_cq_hold(cq);
+ spin_unlock(&table->lock);
+
+ return cq;
+}
+
+static void mlx5_eq_cq_completion(struct mlx5_eq *eq, u32 cqn)
+{
+ struct mlx5_core_cq *cq = mlx5_eq_cq_get(eq, cqn);
+
+ if (unlikely(!cq)) {
+ mlx5_core_warn(eq->dev, "Completion event for bogus CQ 0x%x\n", cqn);
+ return;
+ }
+
+ ++cq->arm_sn;
+
+ cq->comp(cq);
+
+ mlx5_cq_put(cq);
+}
+
+static void mlx5_eq_cq_event(struct mlx5_eq *eq, u32 cqn, int event_type)
+{
+ struct mlx5_core_cq *cq = mlx5_eq_cq_get(eq, cqn);
+
+ if (unlikely(!cq)) {
+ mlx5_core_warn(eq->dev, "Async event for bogus CQ 0x%x\n", cqn);
+ return;
+ }
+
+ cq->event(cq, event_type);
+
+ mlx5_cq_put(cq);
+}
+
static irqreturn_t mlx5_eq_int(int irq, void *eq_ptr)
{
struct mlx5_eq *eq = eq_ptr;
@@ -415,7 +460,7 @@ static irqreturn_t mlx5_eq_int(int irq, void *eq_ptr)
switch (eqe->type) {
case MLX5_EVENT_TYPE_COMP:
cqn = be32_to_cpu(eqe->data.comp.cqn) & 0xffffff;
- mlx5_cq_completion(dev, cqn);
+ mlx5_eq_cq_completion(eq, cqn);
break;
case MLX5_EVENT_TYPE_DCT_DRAINED:
rsn = be32_to_cpu(eqe->data.dct.dctn) & 0xffffff;
@@ -472,7 +517,7 @@ static irqreturn_t mlx5_eq_int(int irq, void *eq_ptr)
cqn = be32_to_cpu(eqe->data.cq_err.cqn) & 0xffffff;
mlx5_core_warn(dev, "CQ error on CQN 0x%x, syndrome 0x%x\n",
cqn, eqe->data.cq_err.syndrome);
- mlx5_cq_event(dev, cqn, eqe->type);
+ mlx5_eq_cq_event(eq, cqn, eqe->type);
break;
case MLX5_EVENT_TYPE_PAGE_REQUEST:
@@ -567,6 +612,7 @@ int mlx5_create_map_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq, u8 vecidx,
int nent, u64 mask, const char *name,
enum mlx5_eq_type type)
{
+ struct mlx5_cq_table *cq_table = &eq->cq_table;
u32 out[MLX5_ST_SZ_DW(create_eq_out)] = {0};
struct mlx5_priv *priv = &dev->priv;
irq_handler_t handler;
@@ -576,6 +622,11 @@ int mlx5_create_map_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq, u8 vecidx,
u32 *in;
int err;
+ /* Init CQ table */
+ memset(cq_table, 0, sizeof(*cq_table));
+ spin_lock_init(&cq_table->lock);
+ INIT_RADIX_TREE(&cq_table->tree, GFP_ATOMIC);
+
eq->type = type;
eq->nent = roundup_pow_of_two(nent + MLX5_NUM_SPARE_EQE);
eq->cons_index = 0;
@@ -669,7 +720,6 @@ err_buf:
mlx5_buf_free(dev, &eq->buf);
return err;
}
-EXPORT_SYMBOL_GPL(mlx5_create_map_eq);
int mlx5_destroy_unmap_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq)
{
@@ -696,7 +746,40 @@ int mlx5_destroy_unmap_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq)
return err;
}
-EXPORT_SYMBOL_GPL(mlx5_destroy_unmap_eq);
+
+int mlx5_eq_add_cq(struct mlx5_eq *eq, struct mlx5_core_cq *cq)
+{
+ struct mlx5_cq_table *table = &eq->cq_table;
+ int err;
+
+ spin_lock_irq(&table->lock);
+ err = radix_tree_insert(&table->tree, cq->cqn, cq);
+ spin_unlock_irq(&table->lock);
+
+ return err;
+}
+
+int mlx5_eq_del_cq(struct mlx5_eq *eq, struct mlx5_core_cq *cq)
+{
+ struct mlx5_cq_table *table = &eq->cq_table;
+ struct mlx5_core_cq *tmp;
+
+ spin_lock_irq(&table->lock);
+ tmp = radix_tree_delete(&table->tree, cq->cqn);
+ spin_unlock_irq(&table->lock);
+
+ if (!tmp) {
+ mlx5_core_warn(eq->dev, "cq 0x%x not found in eq 0x%x tree\n", eq->eqn, cq->cqn);
+ return -ENOENT;
+ }
+
+ if (tmp != cq) {
+ mlx5_core_warn(eq->dev, "corruption on cqn 0x%x in eq 0x%x\n", eq->eqn, cq->cqn);
+ return -EINVAL;
+ }
+
+ return 0;
+}
int mlx5_eq_init(struct mlx5_core_dev *dev)
{
@@ -840,4 +923,3 @@ int mlx5_core_eq_query(struct mlx5_core_dev *dev, struct mlx5_eq *eq,
MLX5_SET(query_eq_in, in, eq_number, eq->eqn);
return mlx5_cmd_exec(dev, in, sizeof(in), out, outlen);
}
-EXPORT_SYMBOL_GPL(mlx5_core_eq_query);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
index c2b1d7d351fc..332bc56306bf 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
@@ -1619,10 +1619,14 @@ int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs, int mode)
esw_info(esw->dev, "E-Switch enable SRIOV: nvfs(%d) mode (%d)\n", nvfs, mode);
esw->mode = mode;
- if (mode == SRIOV_LEGACY)
+ if (mode == SRIOV_LEGACY) {
err = esw_create_legacy_fdb_table(esw, nvfs + 1);
- else
+ } else {
+ mlx5_reload_interface(esw->dev, MLX5_INTERFACE_PROTOCOL_IB);
+
err = esw_offloads_init(esw, nvfs + 1);
+ }
+
if (err)
goto abort;
@@ -1644,12 +1648,17 @@ int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs, int mode)
abort:
esw->mode = SRIOV_NONE;
+
+ if (mode == SRIOV_OFFLOADS)
+ mlx5_reload_interface(esw->dev, MLX5_INTERFACE_PROTOCOL_IB);
+
return err;
}
void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw)
{
struct esw_mc_addr *mc_promisc;
+ int old_mode;
int nvports;
int i;
@@ -1675,7 +1684,11 @@ void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw)
else if (esw->mode == SRIOV_OFFLOADS)
esw_offloads_cleanup(esw, nvports);
+ old_mode = esw->mode;
esw->mode = SRIOV_NONE;
+
+ if (old_mode == SRIOV_OFFLOADS)
+ mlx5_reload_interface(esw->dev, MLX5_INTERFACE_PROTOCOL_IB);
}
int mlx5_eswitch_init(struct mlx5_core_dev *dev)
@@ -2083,17 +2096,19 @@ unlock:
return err;
}
-static void mlx5_eswitch_query_vport_drop_stats(struct mlx5_core_dev *dev,
- int vport_idx,
- struct mlx5_vport_drop_stats *stats)
+static int mlx5_eswitch_query_vport_drop_stats(struct mlx5_core_dev *dev,
+ int vport_idx,
+ struct mlx5_vport_drop_stats *stats)
{
struct mlx5_eswitch *esw = dev->priv.eswitch;
struct mlx5_vport *vport = &esw->vports[vport_idx];
+ u64 rx_discard_vport_down, tx_discard_vport_down;
u64 bytes = 0;
u16 idx = 0;
+ int err = 0;
if (!vport->enabled || esw->mode != SRIOV_LEGACY)
- return;
+ return 0;
if (vport->egress.drop_counter) {
idx = vport->egress.drop_counter->id;
@@ -2104,6 +2119,23 @@ static void mlx5_eswitch_query_vport_drop_stats(struct mlx5_core_dev *dev,
idx = vport->ingress.drop_counter->id;
mlx5_fc_query(dev, idx, &stats->tx_dropped, &bytes);
}
+
+ if (!MLX5_CAP_GEN(dev, receive_discard_vport_down) &&
+ !MLX5_CAP_GEN(dev, transmit_discard_vport_down))
+ return 0;
+
+ err = mlx5_query_vport_down_stats(dev, vport_idx,
+ &rx_discard_vport_down,
+ &tx_discard_vport_down);
+ if (err)
+ return err;
+
+ if (MLX5_CAP_GEN(dev, receive_discard_vport_down))
+ stats->rx_dropped += rx_discard_vport_down;
+ if (MLX5_CAP_GEN(dev, transmit_discard_vport_down))
+ stats->tx_dropped += tx_discard_vport_down;
+
+ return 0;
}
int mlx5_eswitch_get_vport_stats(struct mlx5_eswitch *esw,
@@ -2167,7 +2199,9 @@ int mlx5_eswitch_get_vport_stats(struct mlx5_eswitch *esw,
vf_stats->broadcast =
MLX5_GET_CTR(out, received_eth_broadcast.packets);
- mlx5_eswitch_query_vport_drop_stats(esw->dev, vport, &stats);
+ err = mlx5_eswitch_query_vport_drop_stats(esw->dev, vport, &stats);
+ if (err)
+ goto free_out;
vf_stats->rx_dropped = stats.rx_dropped;
vf_stats->tx_dropped = stats.tx_dropped;
@@ -2175,3 +2209,9 @@ free_out:
kvfree(out);
return err;
}
+
+u8 mlx5_eswitch_mode(struct mlx5_eswitch *esw)
+{
+ return esw->mode;
+}
+EXPORT_SYMBOL_GPL(mlx5_eswitch_mode);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
index 2fa037066b2f..4cd773fa55e3 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
@@ -37,19 +37,9 @@
#include <linux/if_link.h>
#include <net/devlink.h>
#include <linux/mlx5/device.h>
+#include <linux/mlx5/eswitch.h>
#include "lib/mpfs.h"
-enum {
- SRIOV_NONE,
- SRIOV_LEGACY,
- SRIOV_OFFLOADS
-};
-
-enum {
- REP_ETH,
- NUM_REP_TYPES,
-};
-
#ifdef CONFIG_MLX5_ESWITCH
#define MLX5_MAX_UC_PER_VPORT(dev) \
@@ -139,29 +129,13 @@ struct mlx5_eswitch_fdb {
struct mlx5_flow_table *fdb;
struct mlx5_flow_group *send_to_vport_grp;
struct mlx5_flow_group *miss_grp;
- struct mlx5_flow_handle *miss_rule;
+ struct mlx5_flow_handle *miss_rule_uni;
+ struct mlx5_flow_handle *miss_rule_multi;
int vlan_push_pop_refcount;
} offloads;
};
};
-struct mlx5_eswitch_rep;
-struct mlx5_eswitch_rep_if {
- int (*load)(struct mlx5_core_dev *dev,
- struct mlx5_eswitch_rep *rep);
- void (*unload)(struct mlx5_eswitch_rep *rep);
- void *priv;
- bool valid;
-};
-
-struct mlx5_eswitch_rep {
- struct mlx5_eswitch_rep_if rep_if[NUM_REP_TYPES];
- u16 vport;
- u8 hw_id[ETH_ALEN];
- u16 vlan;
- u32 vlan_refcount;
-};
-
struct mlx5_esw_offload {
struct mlx5_flow_table *ft_offloads;
struct mlx5_flow_group *vport_rx_group;
@@ -231,9 +205,6 @@ int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw,
int mlx5_eswitch_get_vport_stats(struct mlx5_eswitch *esw,
int vport,
struct ifla_vf_stats *vf_stats);
-struct mlx5_flow_handle *
-mlx5_eswitch_add_send_to_vport_rule(struct mlx5_eswitch *esw, int vport,
- u32 sqn);
void mlx5_eswitch_del_send_to_vport_rule(struct mlx5_flow_handle *rule);
struct mlx5_flow_spec;
@@ -256,15 +227,14 @@ enum {
SET_VLAN_INSERT = BIT(1)
};
-#define MLX5_FLOW_CONTEXT_ACTION_VLAN_POP 0x4000
-#define MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH 0x8000
-
struct mlx5_esw_flow_attr {
struct mlx5_eswitch_rep *in_rep;
struct mlx5_eswitch_rep *out_rep;
int action;
- u16 vlan;
+ __be16 vlan_proto;
+ u16 vlan_vid;
+ u8 vlan_prio;
bool vlan_handled;
u32 encap_id;
u32 mod_hdr_id;
@@ -278,13 +248,6 @@ int mlx5_devlink_eswitch_inline_mode_get(struct devlink *devlink, u8 *mode);
int mlx5_eswitch_inline_mode_get(struct mlx5_eswitch *esw, int nvfs, u8 *mode);
int mlx5_devlink_eswitch_encap_mode_set(struct devlink *devlink, u8 encap);
int mlx5_devlink_eswitch_encap_mode_get(struct devlink *devlink, u8 *encap);
-void mlx5_eswitch_register_vport_rep(struct mlx5_eswitch *esw,
- int vport_index,
- struct mlx5_eswitch_rep_if *rep_if,
- u8 rep_type);
-void mlx5_eswitch_unregister_vport_rep(struct mlx5_eswitch *esw,
- int vport_index,
- u8 rep_type);
void *mlx5_eswitch_get_uplink_priv(struct mlx5_eswitch *esw, u8 rep_type);
int mlx5_eswitch_add_vlan_action(struct mlx5_eswitch *esw,
@@ -294,6 +257,12 @@ int mlx5_eswitch_del_vlan_action(struct mlx5_eswitch *esw,
int __mlx5_eswitch_set_vport_vlan(struct mlx5_eswitch *esw,
int vport, u16 vlan, u8 qos, u8 set_flags);
+static inline bool mlx5_eswitch_vlan_actions_supported(struct mlx5_core_dev *dev)
+{
+ return MLX5_CAP_ESW_FLOWTABLE_FDB(dev, pop_vlan) &&
+ MLX5_CAP_ESW_FLOWTABLE_FDB(dev, push_vlan);
+}
+
#define MLX5_DEBUG_ESWITCH_MASK BIT(3)
#define esw_info(dev, format, ...) \
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
index 99f583a15cc3..35e256eb2f6e 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
@@ -58,8 +58,16 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw,
if (esw->mode != SRIOV_OFFLOADS)
return ERR_PTR(-EOPNOTSUPP);
- /* per flow vlan pop/push is emulated, don't set that into the firmware */
- flow_act.action = attr->action & ~(MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH | MLX5_FLOW_CONTEXT_ACTION_VLAN_POP);
+ flow_act.action = attr->action;
+ /* if per flow vlan pop/push is emulated, don't set that into the firmware */
+ if (!mlx5_eswitch_vlan_actions_supported(esw->dev))
+ flow_act.action &= ~(MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH |
+ MLX5_FLOW_CONTEXT_ACTION_VLAN_POP);
+ else if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH) {
+ flow_act.vlan.ethtype = ntohs(attr->vlan_proto);
+ flow_act.vlan.vid = attr->vlan_vid;
+ flow_act.vlan.prio = attr->vlan_prio;
+ }
if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) {
dest[i].type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
@@ -88,10 +96,10 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw,
if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_DECAP)
spec->match_criteria_enable |= MLX5_MATCH_INNER_HEADERS;
- if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)
+ if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)
flow_act.modify_id = attr->mod_hdr_id;
- if (attr->action & MLX5_FLOW_CONTEXT_ACTION_ENCAP)
+ if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_ENCAP)
flow_act.encap_id = attr->encap_id;
rule = mlx5_add_flow_rules((struct mlx5_flow_table *)esw->fdb_table.fdb,
@@ -185,7 +193,7 @@ static int esw_add_vlan_action_check(struct mlx5_esw_flow_attr *attr,
/* protects against (1) setting rules with different vlans to push and
* (2) setting rules w.o vlans (attr->vlan = 0) && w. vlans to push (!= 0)
*/
- if (push && in_rep->vlan_refcount && (in_rep->vlan != attr->vlan))
+ if (push && in_rep->vlan_refcount && (in_rep->vlan != attr->vlan_vid))
goto out_notsupp;
return 0;
@@ -202,6 +210,10 @@ int mlx5_eswitch_add_vlan_action(struct mlx5_eswitch *esw,
bool push, pop, fwd;
int err = 0;
+ /* nop if we're on the vlan push/pop non emulation mode */
+ if (mlx5_eswitch_vlan_actions_supported(esw->dev))
+ return 0;
+
push = !!(attr->action & MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH);
pop = !!(attr->action & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP);
fwd = !!(attr->action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST);
@@ -239,11 +251,11 @@ int mlx5_eswitch_add_vlan_action(struct mlx5_eswitch *esw,
if (vport->vlan_refcount)
goto skip_set_push;
- err = __mlx5_eswitch_set_vport_vlan(esw, vport->vport, attr->vlan, 0,
+ err = __mlx5_eswitch_set_vport_vlan(esw, vport->vport, attr->vlan_vid, 0,
SET_VLAN_INSERT | SET_VLAN_STRIP);
if (err)
goto out;
- vport->vlan = attr->vlan;
+ vport->vlan = attr->vlan_vid;
skip_set_push:
vport->vlan_refcount++;
}
@@ -261,6 +273,10 @@ int mlx5_eswitch_del_vlan_action(struct mlx5_eswitch *esw,
bool push, pop, fwd;
int err = 0;
+ /* nop if we're on the vlan push/pop non emulation mode */
+ if (mlx5_eswitch_vlan_actions_supported(esw->dev))
+ return 0;
+
if (!attr->vlan_handled)
return 0;
@@ -338,6 +354,7 @@ out:
kvfree(spec);
return flow_rule;
}
+EXPORT_SYMBOL(mlx5_eswitch_add_send_to_vport_rule);
void mlx5_eswitch_del_send_to_vport_rule(struct mlx5_flow_handle *rule)
{
@@ -350,7 +367,11 @@ static int esw_add_fdb_miss_rule(struct mlx5_eswitch *esw)
struct mlx5_flow_destination dest = {};
struct mlx5_flow_handle *flow_rule = NULL;
struct mlx5_flow_spec *spec;
+ void *headers_c;
+ void *headers_v;
int err = 0;
+ u8 *dmac_c;
+ u8 *dmac_v;
spec = kvzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec) {
@@ -358,6 +379,13 @@ static int esw_add_fdb_miss_rule(struct mlx5_eswitch *esw)
goto out;
}
+ spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
+ headers_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
+ outer_headers);
+ dmac_c = MLX5_ADDR_OF(fte_match_param, headers_c,
+ outer_headers.dmac_47_16);
+ dmac_c[0] = 0x01;
+
dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
dest.vport_num = 0;
flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
@@ -366,11 +394,28 @@ static int esw_add_fdb_miss_rule(struct mlx5_eswitch *esw)
&flow_act, &dest, 1);
if (IS_ERR(flow_rule)) {
err = PTR_ERR(flow_rule);
- esw_warn(esw->dev, "FDB: Failed to add miss flow rule err %d\n", err);
+ esw_warn(esw->dev, "FDB: Failed to add unicast miss flow rule err %d\n", err);
goto out;
}
- esw->fdb_table.offloads.miss_rule = flow_rule;
+ esw->fdb_table.offloads.miss_rule_uni = flow_rule;
+
+ headers_v = MLX5_ADDR_OF(fte_match_param, spec->match_value,
+ outer_headers);
+ dmac_v = MLX5_ADDR_OF(fte_match_param, headers_v,
+ outer_headers.dmac_47_16);
+ dmac_v[0] = 0x01;
+ flow_rule = mlx5_add_flow_rules(esw->fdb_table.offloads.fdb, spec,
+ &flow_act, &dest, 1);
+ if (IS_ERR(flow_rule)) {
+ err = PTR_ERR(flow_rule);
+ esw_warn(esw->dev, "FDB: Failed to add multicast miss flow rule err %d\n", err);
+ mlx5_del_flow_rules(esw->fdb_table.offloads.miss_rule_uni);
+ goto out;
+ }
+
+ esw->fdb_table.offloads.miss_rule_multi = flow_rule;
+
out:
kvfree(spec);
return err;
@@ -426,6 +471,7 @@ static void esw_destroy_offloads_fast_fdb_table(struct mlx5_eswitch *esw)
}
#define MAX_PF_SQ 256
+#define MAX_SQ_NVPORTS 32
static int esw_create_offloads_fdb_tables(struct mlx5_eswitch *esw, int nvports)
{
@@ -438,6 +484,7 @@ static int esw_create_offloads_fdb_tables(struct mlx5_eswitch *esw, int nvports)
struct mlx5_flow_group *g;
void *match_criteria;
u32 *flow_group_in;
+ u8 *dmac;
esw_debug(esw->dev, "Create offloads FDB Tables\n");
flow_group_in = kvzalloc(inlen, GFP_KERNEL);
@@ -455,7 +502,7 @@ static int esw_create_offloads_fdb_tables(struct mlx5_eswitch *esw, int nvports)
if (err)
goto fast_fdb_err;
- table_size = nvports + MAX_PF_SQ + 1;
+ table_size = nvports * MAX_SQ_NVPORTS + MAX_PF_SQ + 2;
ft_attr.max_fte = table_size;
ft_attr.prio = FDB_SLOW_PATH;
@@ -478,7 +525,7 @@ static int esw_create_offloads_fdb_tables(struct mlx5_eswitch *esw, int nvports)
MLX5_SET_TO_ONES(fte_match_param, match_criteria, misc_parameters.source_sqn);
MLX5_SET_TO_ONES(fte_match_param, match_criteria, misc_parameters.source_port);
- ix = nvports + MAX_PF_SQ;
+ ix = nvports * MAX_SQ_NVPORTS + MAX_PF_SQ;
MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0);
MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, ix - 1);
@@ -492,10 +539,16 @@ static int esw_create_offloads_fdb_tables(struct mlx5_eswitch *esw, int nvports)
/* create miss group */
memset(flow_group_in, 0, inlen);
- MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, 0);
+ MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable,
+ MLX5_MATCH_OUTER_HEADERS);
+ match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in,
+ match_criteria);
+ dmac = MLX5_ADDR_OF(fte_match_param, match_criteria,
+ outer_headers.dmac_47_16);
+ dmac[0] = 0x01;
MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, ix);
- MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, ix + 1);
+ MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, ix + 2);
g = mlx5_create_flow_group(fdb, flow_group_in);
if (IS_ERR(g)) {
@@ -531,7 +584,8 @@ static void esw_destroy_offloads_fdb_tables(struct mlx5_eswitch *esw)
return;
esw_debug(esw->dev, "Destroy offloads FDB Tables\n");
- mlx5_del_flow_rules(esw->fdb_table.offloads.miss_rule);
+ mlx5_del_flow_rules(esw->fdb_table.offloads.miss_rule_multi);
+ mlx5_del_flow_rules(esw->fdb_table.offloads.miss_rule_uni);
mlx5_destroy_flow_group(esw->fdb_table.offloads.send_to_vport_grp);
mlx5_destroy_flow_group(esw->fdb_table.offloads.miss_grp);
@@ -789,14 +843,9 @@ int esw_offloads_init(struct mlx5_eswitch *esw, int nvports)
{
int err;
- /* disable PF RoCE so missed packets don't go through RoCE steering */
- mlx5_dev_list_lock();
- mlx5_remove_dev_by_protocol(esw->dev, MLX5_INTERFACE_PROTOCOL_IB);
- mlx5_dev_list_unlock();
-
err = esw_create_offloads_fdb_tables(esw, nvports);
if (err)
- goto create_fdb_err;
+ return err;
err = esw_create_offloads_table(esw);
if (err)
@@ -821,12 +870,6 @@ create_fg_err:
create_ft_err:
esw_destroy_offloads_fdb_tables(esw);
-create_fdb_err:
- /* enable back PF RoCE */
- mlx5_dev_list_lock();
- mlx5_add_dev_by_protocol(esw->dev, MLX5_INTERFACE_PROTOCOL_IB);
- mlx5_dev_list_unlock();
-
return err;
}
@@ -844,9 +887,7 @@ static int esw_offloads_stop(struct mlx5_eswitch *esw)
}
/* enable back PF RoCE */
- mlx5_dev_list_lock();
- mlx5_add_dev_by_protocol(esw->dev, MLX5_INTERFACE_PROTOCOL_IB);
- mlx5_dev_list_unlock();
+ mlx5_reload_interface(esw->dev, MLX5_INTERFACE_PROTOCOL_IB);
return err;
}
@@ -1160,10 +1201,12 @@ void mlx5_eswitch_register_vport_rep(struct mlx5_eswitch *esw,
rep_if->load = __rep_if->load;
rep_if->unload = __rep_if->unload;
+ rep_if->get_proto_dev = __rep_if->get_proto_dev;
rep_if->priv = __rep_if->priv;
rep_if->valid = true;
}
+EXPORT_SYMBOL(mlx5_eswitch_register_vport_rep);
void mlx5_eswitch_unregister_vport_rep(struct mlx5_eswitch *esw,
int vport_index, u8 rep_type)
@@ -1178,6 +1221,7 @@ void mlx5_eswitch_unregister_vport_rep(struct mlx5_eswitch *esw,
rep->rep_if[rep_type].valid = false;
}
+EXPORT_SYMBOL(mlx5_eswitch_unregister_vport_rep);
void *mlx5_eswitch_get_uplink_priv(struct mlx5_eswitch *esw, u8 rep_type)
{
@@ -1188,3 +1232,35 @@ void *mlx5_eswitch_get_uplink_priv(struct mlx5_eswitch *esw, u8 rep_type)
rep = &offloads->vport_reps[UPLINK_REP_INDEX];
return rep->rep_if[rep_type].priv;
}
+
+void *mlx5_eswitch_get_proto_dev(struct mlx5_eswitch *esw,
+ int vport,
+ u8 rep_type)
+{
+ struct mlx5_esw_offload *offloads = &esw->offloads;
+ struct mlx5_eswitch_rep *rep;
+
+ if (vport == FDB_UPLINK_VPORT)
+ vport = UPLINK_REP_INDEX;
+
+ rep = &offloads->vport_reps[vport];
+
+ if (rep->rep_if[rep_type].valid &&
+ rep->rep_if[rep_type].get_proto_dev)
+ return rep->rep_if[rep_type].get_proto_dev(rep);
+ return NULL;
+}
+EXPORT_SYMBOL(mlx5_eswitch_get_proto_dev);
+
+void *mlx5_eswitch_uplink_get_proto_dev(struct mlx5_eswitch *esw, u8 rep_type)
+{
+ return mlx5_eswitch_get_proto_dev(esw, UPLINK_REP_INDEX, rep_type);
+}
+EXPORT_SYMBOL(mlx5_eswitch_uplink_get_proto_dev);
+
+struct mlx5_eswitch_rep *mlx5_eswitch_vport_rep(struct mlx5_eswitch *esw,
+ int vport)
+{
+ return &esw->offloads.vport_reps[vport];
+}
+EXPORT_SYMBOL(mlx5_eswitch_vport_rep);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fpga/conn.c b/drivers/net/ethernet/mellanox/mlx5/core/fpga/conn.c
index e6175f8ac0e4..de7fe087d6fe 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fpga/conn.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fpga/conn.c
@@ -232,7 +232,7 @@ static int mlx5_fpga_conn_create_mkey(struct mlx5_core_dev *mdev, u32 pdn,
return -ENOMEM;
mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry);
- MLX5_SET(mkc, mkc, access_mode, MLX5_MKC_ACCESS_MODE_PA);
+ MLX5_SET(mkc, mkc, access_mode_1_0, MLX5_MKC_ACCESS_MODE_PA);
MLX5_SET(mkc, mkc, lw, 1);
MLX5_SET(mkc, mkc, lr, 1);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.c
index 35d0e33381ca..0f5da499a223 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.c
@@ -31,49 +31,91 @@
*
*/
+#include <linux/rhashtable.h>
#include <linux/mlx5/driver.h>
+#include <linux/mlx5/fs_helpers.h>
+#include <linux/mlx5/fs.h>
+#include <linux/rbtree.h>
#include "mlx5_core.h"
+#include "fs_cmd.h"
#include "fpga/ipsec.h"
#include "fpga/sdk.h"
#include "fpga/core.h"
#define SBU_QP_QUEUE_SIZE 8
+#define MLX5_FPGA_IPSEC_CMD_TIMEOUT_MSEC (60 * 1000)
-enum mlx5_ipsec_response_syndrome {
- MLX5_IPSEC_RESPONSE_SUCCESS = 0,
- MLX5_IPSEC_RESPONSE_ILLEGAL_REQUEST = 1,
- MLX5_IPSEC_RESPONSE_SADB_ISSUE = 2,
- MLX5_IPSEC_RESPONSE_WRITE_RESPONSE_ISSUE = 3,
+enum mlx5_fpga_ipsec_cmd_status {
+ MLX5_FPGA_IPSEC_CMD_PENDING,
+ MLX5_FPGA_IPSEC_CMD_SEND_FAIL,
+ MLX5_FPGA_IPSEC_CMD_COMPLETE,
};
-enum mlx5_fpga_ipsec_sacmd_status {
- MLX5_FPGA_IPSEC_SACMD_PENDING,
- MLX5_FPGA_IPSEC_SACMD_SEND_FAIL,
- MLX5_FPGA_IPSEC_SACMD_COMPLETE,
-};
-
-struct mlx5_ipsec_command_context {
+struct mlx5_fpga_ipsec_cmd_context {
struct mlx5_fpga_dma_buf buf;
- struct mlx5_accel_ipsec_sa sa;
- enum mlx5_fpga_ipsec_sacmd_status status;
+ enum mlx5_fpga_ipsec_cmd_status status;
+ struct mlx5_ifc_fpga_ipsec_cmd_resp resp;
int status_code;
struct completion complete;
struct mlx5_fpga_device *dev;
struct list_head list; /* Item in pending_cmds */
+ u8 command[0];
+};
+
+struct mlx5_fpga_esp_xfrm;
+
+struct mlx5_fpga_ipsec_sa_ctx {
+ struct rhash_head hash;
+ struct mlx5_ifc_fpga_ipsec_sa hw_sa;
+ struct mlx5_core_dev *dev;
+ struct mlx5_fpga_esp_xfrm *fpga_xfrm;
+};
+
+struct mlx5_fpga_esp_xfrm {
+ unsigned int num_rules;
+ struct mlx5_fpga_ipsec_sa_ctx *sa_ctx;
+ struct mutex lock; /* xfrm lock */
+ struct mlx5_accel_esp_xfrm accel_xfrm;
+};
+
+struct mlx5_fpga_ipsec_rule {
+ struct rb_node node;
+ struct fs_fte *fte;
+ struct mlx5_fpga_ipsec_sa_ctx *ctx;
};
-struct mlx5_ipsec_sadb_resp {
- __be32 syndrome;
- __be32 sw_sa_handle;
- u8 reserved[24];
-} __packed;
+static const struct rhashtable_params rhash_sa = {
+ .key_len = FIELD_SIZEOF(struct mlx5_fpga_ipsec_sa_ctx, hw_sa),
+ .key_offset = offsetof(struct mlx5_fpga_ipsec_sa_ctx, hw_sa),
+ .head_offset = offsetof(struct mlx5_fpga_ipsec_sa_ctx, hash),
+ .automatic_shrinking = true,
+ .min_size = 1,
+};
struct mlx5_fpga_ipsec {
+ struct mlx5_fpga_device *fdev;
struct list_head pending_cmds;
spinlock_t pending_cmds_lock; /* Protects pending_cmds */
u32 caps[MLX5_ST_SZ_DW(ipsec_extended_cap)];
struct mlx5_fpga_conn *conn;
+
+ struct notifier_block fs_notifier_ingress_bypass;
+ struct notifier_block fs_notifier_egress;
+
+ /* Map hardware SA --> SA context
+ * (mlx5_fpga_ipsec_sa) (mlx5_fpga_ipsec_sa_ctx)
+ * We will use this hash to avoid SAs duplication in fpga which
+ * aren't allowed
+ */
+ struct rhashtable sa_hash; /* hw_sa -> mlx5_fpga_ipsec_sa_ctx */
+ struct mutex sa_hash_lock;
+
+ /* Tree holding all rules for this fpga device
+ * Key for searching a rule (mlx5_fpga_ipsec_rule) is (ft, id)
+ */
+ struct rb_root rules_rb;
+ struct mutex rules_rb_lock; /* rules lock */
};
static bool mlx5_fpga_is_ipsec_device(struct mlx5_core_dev *mdev)
@@ -97,28 +139,29 @@ static void mlx5_fpga_ipsec_send_complete(struct mlx5_fpga_conn *conn,
struct mlx5_fpga_dma_buf *buf,
u8 status)
{
- struct mlx5_ipsec_command_context *context;
+ struct mlx5_fpga_ipsec_cmd_context *context;
if (status) {
- context = container_of(buf, struct mlx5_ipsec_command_context,
+ context = container_of(buf, struct mlx5_fpga_ipsec_cmd_context,
buf);
mlx5_fpga_warn(fdev, "IPSec command send failed with status %u\n",
status);
- context->status = MLX5_FPGA_IPSEC_SACMD_SEND_FAIL;
+ context->status = MLX5_FPGA_IPSEC_CMD_SEND_FAIL;
complete(&context->complete);
}
}
-static inline int syndrome_to_errno(enum mlx5_ipsec_response_syndrome syndrome)
+static inline
+int syndrome_to_errno(enum mlx5_ifc_fpga_ipsec_response_syndrome syndrome)
{
switch (syndrome) {
- case MLX5_IPSEC_RESPONSE_SUCCESS:
+ case MLX5_FPGA_IPSEC_RESPONSE_SUCCESS:
return 0;
- case MLX5_IPSEC_RESPONSE_SADB_ISSUE:
+ case MLX5_FPGA_IPSEC_RESPONSE_SADB_ISSUE:
return -EEXIST;
- case MLX5_IPSEC_RESPONSE_ILLEGAL_REQUEST:
+ case MLX5_FPGA_IPSEC_RESPONSE_ILLEGAL_REQUEST:
return -EINVAL;
- case MLX5_IPSEC_RESPONSE_WRITE_RESPONSE_ISSUE:
+ case MLX5_FPGA_IPSEC_RESPONSE_WRITE_RESPONSE_ISSUE:
return -EIO;
}
return -EIO;
@@ -126,9 +169,9 @@ static inline int syndrome_to_errno(enum mlx5_ipsec_response_syndrome syndrome)
static void mlx5_fpga_ipsec_recv(void *cb_arg, struct mlx5_fpga_dma_buf *buf)
{
- struct mlx5_ipsec_sadb_resp *resp = buf->sg[0].data;
- struct mlx5_ipsec_command_context *context;
- enum mlx5_ipsec_response_syndrome syndrome;
+ struct mlx5_ifc_fpga_ipsec_cmd_resp *resp = buf->sg[0].data;
+ struct mlx5_fpga_ipsec_cmd_context *context;
+ enum mlx5_ifc_fpga_ipsec_response_syndrome syndrome;
struct mlx5_fpga_device *fdev = cb_arg;
unsigned long flags;
@@ -138,12 +181,12 @@ static void mlx5_fpga_ipsec_recv(void *cb_arg, struct mlx5_fpga_dma_buf *buf)
return;
}
- mlx5_fpga_dbg(fdev, "mlx5_ipsec recv_cb syndrome %08x sa_id %x\n",
- ntohl(resp->syndrome), ntohl(resp->sw_sa_handle));
+ mlx5_fpga_dbg(fdev, "mlx5_ipsec recv_cb syndrome %08x\n",
+ ntohl(resp->syndrome));
spin_lock_irqsave(&fdev->ipsec->pending_cmds_lock, flags);
context = list_first_entry_or_null(&fdev->ipsec->pending_cmds,
- struct mlx5_ipsec_command_context,
+ struct mlx5_fpga_ipsec_cmd_context,
list);
if (context)
list_del(&context->list);
@@ -155,51 +198,48 @@ static void mlx5_fpga_ipsec_recv(void *cb_arg, struct mlx5_fpga_dma_buf *buf)
}
mlx5_fpga_dbg(fdev, "Handling response for %p\n", context);
- if (context->sa.sw_sa_handle != resp->sw_sa_handle) {
- mlx5_fpga_err(fdev, "mismatch SA handle. cmd 0x%08x vs resp 0x%08x\n",
- ntohl(context->sa.sw_sa_handle),
- ntohl(resp->sw_sa_handle));
- return;
- }
-
syndrome = ntohl(resp->syndrome);
context->status_code = syndrome_to_errno(syndrome);
- context->status = MLX5_FPGA_IPSEC_SACMD_COMPLETE;
+ context->status = MLX5_FPGA_IPSEC_CMD_COMPLETE;
+ memcpy(&context->resp, resp, sizeof(*resp));
if (context->status_code)
- mlx5_fpga_warn(fdev, "IPSec SADB command failed with syndrome %08x\n",
+ mlx5_fpga_warn(fdev, "IPSec command failed with syndrome %08x\n",
syndrome);
+
complete(&context->complete);
}
-void *mlx5_fpga_ipsec_sa_cmd_exec(struct mlx5_core_dev *mdev,
- struct mlx5_accel_ipsec_sa *cmd)
+static void *mlx5_fpga_ipsec_cmd_exec(struct mlx5_core_dev *mdev,
+ const void *cmd, int cmd_size)
{
- struct mlx5_ipsec_command_context *context;
+ struct mlx5_fpga_ipsec_cmd_context *context;
struct mlx5_fpga_device *fdev = mdev->fpga;
unsigned long flags;
- int res = 0;
+ int res;
- BUILD_BUG_ON((sizeof(struct mlx5_accel_ipsec_sa) & 3) != 0);
if (!fdev || !fdev->ipsec)
return ERR_PTR(-EOPNOTSUPP);
- context = kzalloc(sizeof(*context), GFP_ATOMIC);
+ if (cmd_size & 3)
+ return ERR_PTR(-EINVAL);
+
+ context = kzalloc(sizeof(*context) + cmd_size, GFP_ATOMIC);
if (!context)
return ERR_PTR(-ENOMEM);
- memcpy(&context->sa, cmd, sizeof(*cmd));
+ context->status = MLX5_FPGA_IPSEC_CMD_PENDING;
+ context->dev = fdev;
context->buf.complete = mlx5_fpga_ipsec_send_complete;
- context->buf.sg[0].size = sizeof(context->sa);
- context->buf.sg[0].data = &context->sa;
init_completion(&context->complete);
- context->dev = fdev;
+ memcpy(&context->command, cmd, cmd_size);
+ context->buf.sg[0].size = cmd_size;
+ context->buf.sg[0].data = &context->command;
+
spin_lock_irqsave(&fdev->ipsec->pending_cmds_lock, flags);
list_add_tail(&context->list, &fdev->ipsec->pending_cmds);
spin_unlock_irqrestore(&fdev->ipsec->pending_cmds_lock, flags);
- context->status = MLX5_FPGA_IPSEC_SACMD_PENDING;
-
res = mlx5_fpga_sbu_conn_sendmsg(fdev->ipsec->conn, &context->buf);
if (res) {
mlx5_fpga_warn(fdev, "Failure sending IPSec command: %d\n",
@@ -214,47 +254,103 @@ void *mlx5_fpga_ipsec_sa_cmd_exec(struct mlx5_core_dev *mdev,
return context;
}
-int mlx5_fpga_ipsec_sa_cmd_wait(void *ctx)
+static int mlx5_fpga_ipsec_cmd_wait(void *ctx)
{
- struct mlx5_ipsec_command_context *context = ctx;
+ struct mlx5_fpga_ipsec_cmd_context *context = ctx;
+ unsigned long timeout =
+ msecs_to_jiffies(MLX5_FPGA_IPSEC_CMD_TIMEOUT_MSEC);
int res;
- res = wait_for_completion_killable(&context->complete);
- if (res) {
+ res = wait_for_completion_timeout(&context->complete, timeout);
+ if (!res) {
mlx5_fpga_warn(context->dev, "Failure waiting for IPSec command response\n");
- return -EINTR;
+ return -ETIMEDOUT;
}
- if (context->status == MLX5_FPGA_IPSEC_SACMD_COMPLETE)
+ if (context->status == MLX5_FPGA_IPSEC_CMD_COMPLETE)
res = context->status_code;
else
res = -EIO;
- kfree(context);
return res;
}
+static inline bool is_v2_sadb_supported(struct mlx5_fpga_ipsec *fipsec)
+{
+ if (MLX5_GET(ipsec_extended_cap, fipsec->caps, v2_command))
+ return true;
+ return false;
+}
+
+static int mlx5_fpga_ipsec_update_hw_sa(struct mlx5_fpga_device *fdev,
+ struct mlx5_ifc_fpga_ipsec_sa *hw_sa,
+ int opcode)
+{
+ struct mlx5_core_dev *dev = fdev->mdev;
+ struct mlx5_ifc_fpga_ipsec_sa *sa;
+ struct mlx5_fpga_ipsec_cmd_context *cmd_context;
+ size_t sa_cmd_size;
+ int err;
+
+ hw_sa->ipsec_sa_v1.cmd = htonl(opcode);
+ if (is_v2_sadb_supported(fdev->ipsec))
+ sa_cmd_size = sizeof(*hw_sa);
+ else
+ sa_cmd_size = sizeof(hw_sa->ipsec_sa_v1);
+
+ cmd_context = (struct mlx5_fpga_ipsec_cmd_context *)
+ mlx5_fpga_ipsec_cmd_exec(dev, hw_sa, sa_cmd_size);
+ if (IS_ERR(cmd_context))
+ return PTR_ERR(cmd_context);
+
+ err = mlx5_fpga_ipsec_cmd_wait(cmd_context);
+ if (err)
+ goto out;
+
+ sa = (struct mlx5_ifc_fpga_ipsec_sa *)&cmd_context->command;
+ if (sa->ipsec_sa_v1.sw_sa_handle != cmd_context->resp.sw_sa_handle) {
+ mlx5_fpga_err(fdev, "mismatch SA handle. cmd 0x%08x vs resp 0x%08x\n",
+ ntohl(sa->ipsec_sa_v1.sw_sa_handle),
+ ntohl(cmd_context->resp.sw_sa_handle));
+ err = -EIO;
+ }
+
+out:
+ kfree(cmd_context);
+ return err;
+}
+
u32 mlx5_fpga_ipsec_device_caps(struct mlx5_core_dev *mdev)
{
struct mlx5_fpga_device *fdev = mdev->fpga;
u32 ret = 0;
- if (mlx5_fpga_is_ipsec_device(mdev))
- ret |= MLX5_ACCEL_IPSEC_DEVICE;
- else
+ if (mlx5_fpga_is_ipsec_device(mdev)) {
+ ret |= MLX5_ACCEL_IPSEC_CAP_DEVICE;
+ ret |= MLX5_ACCEL_IPSEC_CAP_REQUIRED_METADATA;
+ } else {
return ret;
+ }
if (!fdev->ipsec)
return ret;
if (MLX5_GET(ipsec_extended_cap, fdev->ipsec->caps, esp))
- ret |= MLX5_ACCEL_IPSEC_ESP;
+ ret |= MLX5_ACCEL_IPSEC_CAP_ESP;
if (MLX5_GET(ipsec_extended_cap, fdev->ipsec->caps, ipv6))
- ret |= MLX5_ACCEL_IPSEC_IPV6;
+ ret |= MLX5_ACCEL_IPSEC_CAP_IPV6;
if (MLX5_GET(ipsec_extended_cap, fdev->ipsec->caps, lso))
- ret |= MLX5_ACCEL_IPSEC_LSO;
+ ret |= MLX5_ACCEL_IPSEC_CAP_LSO;
+
+ if (MLX5_GET(ipsec_extended_cap, fdev->ipsec->caps, rx_no_trailer))
+ ret |= MLX5_ACCEL_IPSEC_CAP_RX_NO_TRAILER;
+
+ if (MLX5_GET(ipsec_extended_cap, fdev->ipsec->caps, esn)) {
+ ret |= MLX5_ACCEL_IPSEC_CAP_ESN;
+ ret |= MLX5_ACCEL_IPSEC_CAP_TX_IV_IS_ESN;
+ }
return ret;
}
@@ -318,6 +414,829 @@ out:
return ret;
}
+static int mlx5_fpga_ipsec_set_caps(struct mlx5_core_dev *mdev, u32 flags)
+{
+ struct mlx5_fpga_ipsec_cmd_context *context;
+ struct mlx5_ifc_fpga_ipsec_cmd_cap cmd = {0};
+ int err;
+
+ cmd.cmd = htonl(MLX5_FPGA_IPSEC_CMD_OP_SET_CAP);
+ cmd.flags = htonl(flags);
+ context = mlx5_fpga_ipsec_cmd_exec(mdev, &cmd, sizeof(cmd));
+ if (IS_ERR(context)) {
+ err = PTR_ERR(context);
+ goto out;
+ }
+
+ err = mlx5_fpga_ipsec_cmd_wait(context);
+ if (err)
+ goto out;
+
+ if ((context->resp.flags & cmd.flags) != cmd.flags) {
+ mlx5_fpga_err(context->dev, "Failed to set capabilities. cmd 0x%08x vs resp 0x%08x\n",
+ cmd.flags,
+ context->resp.flags);
+ err = -EIO;
+ }
+
+out:
+ return err;
+}
+
+static int mlx5_fpga_ipsec_enable_supported_caps(struct mlx5_core_dev *mdev)
+{
+ u32 dev_caps = mlx5_fpga_ipsec_device_caps(mdev);
+ u32 flags = 0;
+
+ if (dev_caps & MLX5_ACCEL_IPSEC_CAP_RX_NO_TRAILER)
+ flags |= MLX5_FPGA_IPSEC_CAP_NO_TRAILER;
+
+ return mlx5_fpga_ipsec_set_caps(mdev, flags);
+}
+
+static void
+mlx5_fpga_ipsec_build_hw_xfrm(struct mlx5_core_dev *mdev,
+ const struct mlx5_accel_esp_xfrm_attrs *xfrm_attrs,
+ struct mlx5_ifc_fpga_ipsec_sa *hw_sa)
+{
+ const struct aes_gcm_keymat *aes_gcm = &xfrm_attrs->keymat.aes_gcm;
+
+ /* key */
+ memcpy(&hw_sa->ipsec_sa_v1.key_enc, aes_gcm->aes_key,
+ aes_gcm->key_len / 8);
+ /* Duplicate 128 bit key twice according to HW layout */
+ if (aes_gcm->key_len == 128)
+ memcpy(&hw_sa->ipsec_sa_v1.key_enc[16],
+ aes_gcm->aes_key, aes_gcm->key_len / 8);
+
+ /* salt and seq_iv */
+ memcpy(&hw_sa->ipsec_sa_v1.gcm.salt_iv, &aes_gcm->seq_iv,
+ sizeof(aes_gcm->seq_iv));
+ memcpy(&hw_sa->ipsec_sa_v1.gcm.salt, &aes_gcm->salt,
+ sizeof(aes_gcm->salt));
+
+ /* esn */
+ if (xfrm_attrs->flags & MLX5_ACCEL_ESP_FLAGS_ESN_TRIGGERED) {
+ hw_sa->ipsec_sa_v1.flags |= MLX5_FPGA_IPSEC_SA_ESN_EN;
+ hw_sa->ipsec_sa_v1.flags |=
+ (xfrm_attrs->flags &
+ MLX5_ACCEL_ESP_FLAGS_ESN_STATE_OVERLAP) ?
+ MLX5_FPGA_IPSEC_SA_ESN_OVERLAP : 0;
+ hw_sa->esn = htonl(xfrm_attrs->esn);
+ } else {
+ hw_sa->ipsec_sa_v1.flags &= ~MLX5_FPGA_IPSEC_SA_ESN_EN;
+ hw_sa->ipsec_sa_v1.flags &=
+ ~(xfrm_attrs->flags &
+ MLX5_ACCEL_ESP_FLAGS_ESN_STATE_OVERLAP) ?
+ MLX5_FPGA_IPSEC_SA_ESN_OVERLAP : 0;
+ hw_sa->esn = 0;
+ }
+
+ /* rx handle */
+ hw_sa->ipsec_sa_v1.sw_sa_handle = htonl(xfrm_attrs->sa_handle);
+
+ /* enc mode */
+ switch (aes_gcm->key_len) {
+ case 128:
+ hw_sa->ipsec_sa_v1.enc_mode =
+ MLX5_FPGA_IPSEC_SA_ENC_MODE_AES_GCM_128_AUTH_128;
+ break;
+ case 256:
+ hw_sa->ipsec_sa_v1.enc_mode =
+ MLX5_FPGA_IPSEC_SA_ENC_MODE_AES_GCM_256_AUTH_128;
+ break;
+ }
+
+ /* flags */
+ hw_sa->ipsec_sa_v1.flags |= MLX5_FPGA_IPSEC_SA_SA_VALID |
+ MLX5_FPGA_IPSEC_SA_SPI_EN |
+ MLX5_FPGA_IPSEC_SA_IP_ESP;
+
+ if (xfrm_attrs->action & MLX5_ACCEL_ESP_ACTION_ENCRYPT)
+ hw_sa->ipsec_sa_v1.flags |= MLX5_FPGA_IPSEC_SA_DIR_SX;
+ else
+ hw_sa->ipsec_sa_v1.flags &= ~MLX5_FPGA_IPSEC_SA_DIR_SX;
+}
+
+static void
+mlx5_fpga_ipsec_build_hw_sa(struct mlx5_core_dev *mdev,
+ struct mlx5_accel_esp_xfrm_attrs *xfrm_attrs,
+ const __be32 saddr[4],
+ const __be32 daddr[4],
+ const __be32 spi, bool is_ipv6,
+ struct mlx5_ifc_fpga_ipsec_sa *hw_sa)
+{
+ mlx5_fpga_ipsec_build_hw_xfrm(mdev, xfrm_attrs, hw_sa);
+
+ /* IPs */
+ memcpy(hw_sa->ipsec_sa_v1.sip, saddr, sizeof(hw_sa->ipsec_sa_v1.sip));
+ memcpy(hw_sa->ipsec_sa_v1.dip, daddr, sizeof(hw_sa->ipsec_sa_v1.dip));
+
+ /* SPI */
+ hw_sa->ipsec_sa_v1.spi = spi;
+
+ /* flags */
+ if (is_ipv6)
+ hw_sa->ipsec_sa_v1.flags |= MLX5_FPGA_IPSEC_SA_IPV6;
+}
+
+static bool is_full_mask(const void *p, size_t len)
+{
+ WARN_ON(len % 4);
+
+ return !memchr_inv(p, 0xff, len);
+}
+
+static bool validate_fpga_full_mask(struct mlx5_core_dev *dev,
+ const u32 *match_c,
+ const u32 *match_v)
+{
+ const void *misc_params_c = MLX5_ADDR_OF(fte_match_param,
+ match_c,
+ misc_parameters);
+ const void *headers_c = MLX5_ADDR_OF(fte_match_param,
+ match_c,
+ outer_headers);
+ const void *headers_v = MLX5_ADDR_OF(fte_match_param,
+ match_v,
+ outer_headers);
+
+ if (mlx5_fs_is_outer_ipv4_flow(dev, headers_c, headers_v)) {
+ const void *s_ipv4_c = MLX5_ADDR_OF(fte_match_set_lyr_2_4,
+ headers_c,
+ src_ipv4_src_ipv6.ipv4_layout.ipv4);
+ const void *d_ipv4_c = MLX5_ADDR_OF(fte_match_set_lyr_2_4,
+ headers_c,
+ dst_ipv4_dst_ipv6.ipv4_layout.ipv4);
+
+ if (!is_full_mask(s_ipv4_c, MLX5_FLD_SZ_BYTES(ipv4_layout,
+ ipv4)) ||
+ !is_full_mask(d_ipv4_c, MLX5_FLD_SZ_BYTES(ipv4_layout,
+ ipv4)))
+ return false;
+ } else {
+ const void *s_ipv6_c = MLX5_ADDR_OF(fte_match_set_lyr_2_4,
+ headers_c,
+ src_ipv4_src_ipv6.ipv6_layout.ipv6);
+ const void *d_ipv6_c = MLX5_ADDR_OF(fte_match_set_lyr_2_4,
+ headers_c,
+ dst_ipv4_dst_ipv6.ipv6_layout.ipv6);
+
+ if (!is_full_mask(s_ipv6_c, MLX5_FLD_SZ_BYTES(ipv6_layout,
+ ipv6)) ||
+ !is_full_mask(d_ipv6_c, MLX5_FLD_SZ_BYTES(ipv6_layout,
+ ipv6)))
+ return false;
+ }
+
+ if (!is_full_mask(MLX5_ADDR_OF(fte_match_set_misc, misc_params_c,
+ outer_esp_spi),
+ MLX5_FLD_SZ_BYTES(fte_match_set_misc, outer_esp_spi)))
+ return false;
+
+ return true;
+}
+
+static bool mlx5_is_fpga_ipsec_rule(struct mlx5_core_dev *dev,
+ u8 match_criteria_enable,
+ const u32 *match_c,
+ const u32 *match_v)
+{
+ u32 ipsec_dev_caps = mlx5_accel_ipsec_device_caps(dev);
+ bool ipv6_flow;
+
+ ipv6_flow = mlx5_fs_is_outer_ipv6_flow(dev, match_c, match_v);
+
+ if (!(match_criteria_enable & MLX5_MATCH_OUTER_HEADERS) ||
+ mlx5_fs_is_outer_udp_flow(match_c, match_v) ||
+ mlx5_fs_is_outer_tcp_flow(match_c, match_v) ||
+ mlx5_fs_is_vxlan_flow(match_c) ||
+ !(mlx5_fs_is_outer_ipv4_flow(dev, match_c, match_v) ||
+ ipv6_flow))
+ return false;
+
+ if (!(ipsec_dev_caps & MLX5_ACCEL_IPSEC_CAP_DEVICE))
+ return false;
+
+ if (!(ipsec_dev_caps & MLX5_ACCEL_IPSEC_CAP_ESP) &&
+ mlx5_fs_is_outer_ipsec_flow(match_c))
+ return false;
+
+ if (!(ipsec_dev_caps & MLX5_ACCEL_IPSEC_CAP_IPV6) &&
+ ipv6_flow)
+ return false;
+
+ if (!validate_fpga_full_mask(dev, match_c, match_v))
+ return false;
+
+ return true;
+}
+
+static bool mlx5_is_fpga_egress_ipsec_rule(struct mlx5_core_dev *dev,
+ u8 match_criteria_enable,
+ const u32 *match_c,
+ const u32 *match_v,
+ struct mlx5_flow_act *flow_act)
+{
+ const void *outer_c = MLX5_ADDR_OF(fte_match_param, match_c,
+ outer_headers);
+ bool is_dmac = MLX5_GET(fte_match_set_lyr_2_4, outer_c, dmac_47_16) ||
+ MLX5_GET(fte_match_set_lyr_2_4, outer_c, dmac_15_0);
+ bool is_smac = MLX5_GET(fte_match_set_lyr_2_4, outer_c, smac_47_16) ||
+ MLX5_GET(fte_match_set_lyr_2_4, outer_c, smac_15_0);
+ int ret;
+
+ ret = mlx5_is_fpga_ipsec_rule(dev, match_criteria_enable, match_c,
+ match_v);
+ if (!ret)
+ return ret;
+
+ if (is_dmac || is_smac ||
+ (match_criteria_enable &
+ ~(MLX5_MATCH_OUTER_HEADERS | MLX5_MATCH_MISC_PARAMETERS)) ||
+ (flow_act->action & ~(MLX5_FLOW_CONTEXT_ACTION_ENCRYPT | MLX5_FLOW_CONTEXT_ACTION_ALLOW)) ||
+ flow_act->has_flow_tag)
+ return false;
+
+ return true;
+}
+
+void *mlx5_fpga_ipsec_create_sa_ctx(struct mlx5_core_dev *mdev,
+ struct mlx5_accel_esp_xfrm *accel_xfrm,
+ const __be32 saddr[4],
+ const __be32 daddr[4],
+ const __be32 spi, bool is_ipv6)
+{
+ struct mlx5_fpga_ipsec_sa_ctx *sa_ctx;
+ struct mlx5_fpga_esp_xfrm *fpga_xfrm =
+ container_of(accel_xfrm, typeof(*fpga_xfrm),
+ accel_xfrm);
+ struct mlx5_fpga_device *fdev = mdev->fpga;
+ struct mlx5_fpga_ipsec *fipsec = fdev->ipsec;
+ int opcode, err;
+ void *context;
+
+ /* alloc SA */
+ sa_ctx = kzalloc(sizeof(*sa_ctx), GFP_KERNEL);
+ if (!sa_ctx)
+ return ERR_PTR(-ENOMEM);
+
+ sa_ctx->dev = mdev;
+
+ /* build candidate SA */
+ mlx5_fpga_ipsec_build_hw_sa(mdev, &accel_xfrm->attrs,
+ saddr, daddr, spi, is_ipv6,
+ &sa_ctx->hw_sa);
+
+ mutex_lock(&fpga_xfrm->lock);
+
+ if (fpga_xfrm->sa_ctx) { /* multiple rules for same accel_xfrm */
+ /* all rules must be with same IPs and SPI */
+ if (memcmp(&sa_ctx->hw_sa, &fpga_xfrm->sa_ctx->hw_sa,
+ sizeof(sa_ctx->hw_sa))) {
+ context = ERR_PTR(-EINVAL);
+ goto exists;
+ }
+
+ ++fpga_xfrm->num_rules;
+ context = fpga_xfrm->sa_ctx;
+ goto exists;
+ }
+
+ /* This is unbounded fpga_xfrm, try to add to hash */
+ mutex_lock(&fipsec->sa_hash_lock);
+
+ err = rhashtable_lookup_insert_fast(&fipsec->sa_hash, &sa_ctx->hash,
+ rhash_sa);
+ if (err) {
+ /* Can't bound different accel_xfrm to already existing sa_ctx.
+ * This is because we can't support multiple ketmats for
+ * same IPs and SPI
+ */
+ context = ERR_PTR(-EEXIST);
+ goto unlock_hash;
+ }
+
+ /* Bound accel_xfrm to sa_ctx */
+ opcode = is_v2_sadb_supported(fdev->ipsec) ?
+ MLX5_FPGA_IPSEC_CMD_OP_ADD_SA_V2 :
+ MLX5_FPGA_IPSEC_CMD_OP_ADD_SA;
+ err = mlx5_fpga_ipsec_update_hw_sa(fdev, &sa_ctx->hw_sa, opcode);
+ sa_ctx->hw_sa.ipsec_sa_v1.cmd = 0;
+ if (err) {
+ context = ERR_PTR(err);
+ goto delete_hash;
+ }
+
+ mutex_unlock(&fipsec->sa_hash_lock);
+
+ ++fpga_xfrm->num_rules;
+ fpga_xfrm->sa_ctx = sa_ctx;
+ sa_ctx->fpga_xfrm = fpga_xfrm;
+
+ mutex_unlock(&fpga_xfrm->lock);
+
+ return sa_ctx;
+
+delete_hash:
+ WARN_ON(rhashtable_remove_fast(&fipsec->sa_hash, &sa_ctx->hash,
+ rhash_sa));
+unlock_hash:
+ mutex_unlock(&fipsec->sa_hash_lock);
+
+exists:
+ mutex_unlock(&fpga_xfrm->lock);
+ kfree(sa_ctx);
+ return context;
+}
+
+static void *
+mlx5_fpga_ipsec_fs_create_sa_ctx(struct mlx5_core_dev *mdev,
+ struct fs_fte *fte,
+ bool is_egress)
+{
+ struct mlx5_accel_esp_xfrm *accel_xfrm;
+ __be32 saddr[4], daddr[4], spi;
+ struct mlx5_flow_group *fg;
+ bool is_ipv6 = false;
+
+ fs_get_obj(fg, fte->node.parent);
+ /* validate */
+ if (is_egress &&
+ !mlx5_is_fpga_egress_ipsec_rule(mdev,
+ fg->mask.match_criteria_enable,
+ fg->mask.match_criteria,
+ fte->val,
+ &fte->action))
+ return ERR_PTR(-EINVAL);
+ else if (!mlx5_is_fpga_ipsec_rule(mdev,
+ fg->mask.match_criteria_enable,
+ fg->mask.match_criteria,
+ fte->val))
+ return ERR_PTR(-EINVAL);
+
+ /* get xfrm context */
+ accel_xfrm =
+ (struct mlx5_accel_esp_xfrm *)fte->action.esp_id;
+
+ /* IPs */
+ if (mlx5_fs_is_outer_ipv4_flow(mdev, fg->mask.match_criteria,
+ fte->val)) {
+ memcpy(&saddr[3],
+ MLX5_ADDR_OF(fte_match_set_lyr_2_4,
+ fte->val,
+ src_ipv4_src_ipv6.ipv4_layout.ipv4),
+ sizeof(saddr[3]));
+ memcpy(&daddr[3],
+ MLX5_ADDR_OF(fte_match_set_lyr_2_4,
+ fte->val,
+ dst_ipv4_dst_ipv6.ipv4_layout.ipv4),
+ sizeof(daddr[3]));
+ } else {
+ memcpy(saddr,
+ MLX5_ADDR_OF(fte_match_param,
+ fte->val,
+ outer_headers.src_ipv4_src_ipv6.ipv6_layout.ipv6),
+ sizeof(saddr));
+ memcpy(daddr,
+ MLX5_ADDR_OF(fte_match_param,
+ fte->val,
+ outer_headers.dst_ipv4_dst_ipv6.ipv6_layout.ipv6),
+ sizeof(daddr));
+ is_ipv6 = true;
+ }
+
+ /* SPI */
+ spi = MLX5_GET_BE(typeof(spi),
+ fte_match_param, fte->val,
+ misc_parameters.outer_esp_spi);
+
+ /* create */
+ return mlx5_fpga_ipsec_create_sa_ctx(mdev, accel_xfrm,
+ saddr, daddr,
+ spi, is_ipv6);
+}
+
+static void
+mlx5_fpga_ipsec_release_sa_ctx(struct mlx5_fpga_ipsec_sa_ctx *sa_ctx)
+{
+ struct mlx5_fpga_device *fdev = sa_ctx->dev->fpga;
+ struct mlx5_fpga_ipsec *fipsec = fdev->ipsec;
+ int opcode = is_v2_sadb_supported(fdev->ipsec) ?
+ MLX5_FPGA_IPSEC_CMD_OP_DEL_SA_V2 :
+ MLX5_FPGA_IPSEC_CMD_OP_DEL_SA;
+ int err;
+
+ err = mlx5_fpga_ipsec_update_hw_sa(fdev, &sa_ctx->hw_sa, opcode);
+ sa_ctx->hw_sa.ipsec_sa_v1.cmd = 0;
+ if (err) {
+ WARN_ON(err);
+ return;
+ }
+
+ mutex_lock(&fipsec->sa_hash_lock);
+ WARN_ON(rhashtable_remove_fast(&fipsec->sa_hash, &sa_ctx->hash,
+ rhash_sa));
+ mutex_unlock(&fipsec->sa_hash_lock);
+}
+
+void mlx5_fpga_ipsec_delete_sa_ctx(void *context)
+{
+ struct mlx5_fpga_esp_xfrm *fpga_xfrm =
+ ((struct mlx5_fpga_ipsec_sa_ctx *)context)->fpga_xfrm;
+
+ mutex_lock(&fpga_xfrm->lock);
+ if (!--fpga_xfrm->num_rules) {
+ mlx5_fpga_ipsec_release_sa_ctx(fpga_xfrm->sa_ctx);
+ fpga_xfrm->sa_ctx = NULL;
+ }
+ mutex_unlock(&fpga_xfrm->lock);
+}
+
+static inline struct mlx5_fpga_ipsec_rule *
+_rule_search(struct rb_root *root, struct fs_fte *fte)
+{
+ struct rb_node *node = root->rb_node;
+
+ while (node) {
+ struct mlx5_fpga_ipsec_rule *rule =
+ container_of(node, struct mlx5_fpga_ipsec_rule,
+ node);
+
+ if (rule->fte < fte)
+ node = node->rb_left;
+ else if (rule->fte > fte)
+ node = node->rb_right;
+ else
+ return rule;
+ }
+ return NULL;
+}
+
+static struct mlx5_fpga_ipsec_rule *
+rule_search(struct mlx5_fpga_ipsec *ipsec_dev, struct fs_fte *fte)
+{
+ struct mlx5_fpga_ipsec_rule *rule;
+
+ mutex_lock(&ipsec_dev->rules_rb_lock);
+ rule = _rule_search(&ipsec_dev->rules_rb, fte);
+ mutex_unlock(&ipsec_dev->rules_rb_lock);
+
+ return rule;
+}
+
+static inline int _rule_insert(struct rb_root *root,
+ struct mlx5_fpga_ipsec_rule *rule)
+{
+ struct rb_node **new = &root->rb_node, *parent = NULL;
+
+ /* Figure out where to put new node */
+ while (*new) {
+ struct mlx5_fpga_ipsec_rule *this =
+ container_of(*new, struct mlx5_fpga_ipsec_rule,
+ node);
+
+ parent = *new;
+ if (rule->fte < this->fte)
+ new = &((*new)->rb_left);
+ else if (rule->fte > this->fte)
+ new = &((*new)->rb_right);
+ else
+ return -EEXIST;
+ }
+
+ /* Add new node and rebalance tree. */
+ rb_link_node(&rule->node, parent, new);
+ rb_insert_color(&rule->node, root);
+
+ return 0;
+}
+
+static int rule_insert(struct mlx5_fpga_ipsec *ipsec_dev,
+ struct mlx5_fpga_ipsec_rule *rule)
+{
+ int ret;
+
+ mutex_lock(&ipsec_dev->rules_rb_lock);
+ ret = _rule_insert(&ipsec_dev->rules_rb, rule);
+ mutex_unlock(&ipsec_dev->rules_rb_lock);
+
+ return ret;
+}
+
+static inline void _rule_delete(struct mlx5_fpga_ipsec *ipsec_dev,
+ struct mlx5_fpga_ipsec_rule *rule)
+{
+ struct rb_root *root = &ipsec_dev->rules_rb;
+
+ mutex_lock(&ipsec_dev->rules_rb_lock);
+ rb_erase(&rule->node, root);
+ mutex_unlock(&ipsec_dev->rules_rb_lock);
+}
+
+static void rule_delete(struct mlx5_fpga_ipsec *ipsec_dev,
+ struct mlx5_fpga_ipsec_rule *rule)
+{
+ _rule_delete(ipsec_dev, rule);
+ kfree(rule);
+}
+
+struct mailbox_mod {
+ uintptr_t saved_esp_id;
+ u32 saved_action;
+ u32 saved_outer_esp_spi_value;
+};
+
+static void restore_spec_mailbox(struct fs_fte *fte,
+ struct mailbox_mod *mbox_mod)
+{
+ char *misc_params_v = MLX5_ADDR_OF(fte_match_param,
+ fte->val,
+ misc_parameters);
+
+ MLX5_SET(fte_match_set_misc, misc_params_v, outer_esp_spi,
+ mbox_mod->saved_outer_esp_spi_value);
+ fte->action.action |= mbox_mod->saved_action;
+ fte->action.esp_id = (uintptr_t)mbox_mod->saved_esp_id;
+}
+
+static void modify_spec_mailbox(struct mlx5_core_dev *mdev,
+ struct fs_fte *fte,
+ struct mailbox_mod *mbox_mod)
+{
+ char *misc_params_v = MLX5_ADDR_OF(fte_match_param,
+ fte->val,
+ misc_parameters);
+
+ mbox_mod->saved_esp_id = fte->action.esp_id;
+ mbox_mod->saved_action = fte->action.action &
+ (MLX5_FLOW_CONTEXT_ACTION_ENCRYPT |
+ MLX5_FLOW_CONTEXT_ACTION_DECRYPT);
+ mbox_mod->saved_outer_esp_spi_value =
+ MLX5_GET(fte_match_set_misc, misc_params_v,
+ outer_esp_spi);
+
+ fte->action.esp_id = 0;
+ fte->action.action &= ~(MLX5_FLOW_CONTEXT_ACTION_ENCRYPT |
+ MLX5_FLOW_CONTEXT_ACTION_DECRYPT);
+ if (!MLX5_CAP_FLOWTABLE(mdev,
+ flow_table_properties_nic_receive.ft_field_support.outer_esp_spi))
+ MLX5_SET(fte_match_set_misc, misc_params_v, outer_esp_spi, 0);
+}
+
+static enum fs_flow_table_type egress_to_fs_ft(bool egress)
+{
+ return egress ? FS_FT_NIC_TX : FS_FT_NIC_RX;
+}
+
+static int fpga_ipsec_fs_create_flow_group(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ u32 *in,
+ unsigned int *group_id,
+ bool is_egress)
+{
+ int (*create_flow_group)(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft, u32 *in,
+ unsigned int *group_id) =
+ mlx5_fs_cmd_get_default(egress_to_fs_ft(is_egress))->create_flow_group;
+ char *misc_params_c = MLX5_ADDR_OF(create_flow_group_in, in,
+ match_criteria.misc_parameters);
+ u32 saved_outer_esp_spi_mask;
+ u8 match_criteria_enable;
+ int ret;
+
+ if (MLX5_CAP_FLOWTABLE(dev,
+ flow_table_properties_nic_receive.ft_field_support.outer_esp_spi))
+ return create_flow_group(dev, ft, in, group_id);
+
+ match_criteria_enable =
+ MLX5_GET(create_flow_group_in, in, match_criteria_enable);
+ saved_outer_esp_spi_mask =
+ MLX5_GET(fte_match_set_misc, misc_params_c, outer_esp_spi);
+ if (!match_criteria_enable || !saved_outer_esp_spi_mask)
+ return create_flow_group(dev, ft, in, group_id);
+
+ MLX5_SET(fte_match_set_misc, misc_params_c, outer_esp_spi, 0);
+
+ if (!(*misc_params_c) &&
+ !memcmp(misc_params_c, misc_params_c + 1, MLX5_ST_SZ_BYTES(fte_match_set_misc) - 1))
+ MLX5_SET(create_flow_group_in, in, match_criteria_enable,
+ match_criteria_enable & ~MLX5_MATCH_MISC_PARAMETERS);
+
+ ret = create_flow_group(dev, ft, in, group_id);
+
+ MLX5_SET(fte_match_set_misc, misc_params_c, outer_esp_spi, saved_outer_esp_spi_mask);
+ MLX5_SET(create_flow_group_in, in, match_criteria_enable, match_criteria_enable);
+
+ return ret;
+}
+
+static int fpga_ipsec_fs_create_fte(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ struct mlx5_flow_group *fg,
+ struct fs_fte *fte,
+ bool is_egress)
+{
+ int (*create_fte)(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ struct mlx5_flow_group *fg,
+ struct fs_fte *fte) =
+ mlx5_fs_cmd_get_default(egress_to_fs_ft(is_egress))->create_fte;
+ struct mlx5_fpga_device *fdev = dev->fpga;
+ struct mlx5_fpga_ipsec *fipsec = fdev->ipsec;
+ struct mlx5_fpga_ipsec_rule *rule;
+ bool is_esp = fte->action.esp_id;
+ struct mailbox_mod mbox_mod;
+ int ret;
+
+ if (!is_esp ||
+ !(fte->action.action &
+ (MLX5_FLOW_CONTEXT_ACTION_ENCRYPT |
+ MLX5_FLOW_CONTEXT_ACTION_DECRYPT)))
+ return create_fte(dev, ft, fg, fte);
+
+ rule = kzalloc(sizeof(*rule), GFP_KERNEL);
+ if (!rule)
+ return -ENOMEM;
+
+ rule->ctx = mlx5_fpga_ipsec_fs_create_sa_ctx(dev, fte, is_egress);
+ if (IS_ERR(rule->ctx)) {
+ int err = PTR_ERR(rule->ctx);
+ kfree(rule);
+ return err;
+ }
+
+ rule->fte = fte;
+ WARN_ON(rule_insert(fipsec, rule));
+
+ modify_spec_mailbox(dev, fte, &mbox_mod);
+ ret = create_fte(dev, ft, fg, fte);
+ restore_spec_mailbox(fte, &mbox_mod);
+ if (ret) {
+ _rule_delete(fipsec, rule);
+ mlx5_fpga_ipsec_delete_sa_ctx(rule->ctx);
+ kfree(rule);
+ }
+
+ return ret;
+}
+
+static int fpga_ipsec_fs_update_fte(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ unsigned int group_id,
+ int modify_mask,
+ struct fs_fte *fte,
+ bool is_egress)
+{
+ int (*update_fte)(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ unsigned int group_id,
+ int modify_mask,
+ struct fs_fte *fte) =
+ mlx5_fs_cmd_get_default(egress_to_fs_ft(is_egress))->update_fte;
+ bool is_esp = fte->action.esp_id;
+ struct mailbox_mod mbox_mod;
+ int ret;
+
+ if (!is_esp ||
+ !(fte->action.action &
+ (MLX5_FLOW_CONTEXT_ACTION_ENCRYPT |
+ MLX5_FLOW_CONTEXT_ACTION_DECRYPT)))
+ return update_fte(dev, ft, group_id, modify_mask, fte);
+
+ modify_spec_mailbox(dev, fte, &mbox_mod);
+ ret = update_fte(dev, ft, group_id, modify_mask, fte);
+ restore_spec_mailbox(fte, &mbox_mod);
+
+ return ret;
+}
+
+static int fpga_ipsec_fs_delete_fte(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ struct fs_fte *fte,
+ bool is_egress)
+{
+ int (*delete_fte)(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ struct fs_fte *fte) =
+ mlx5_fs_cmd_get_default(egress_to_fs_ft(is_egress))->delete_fte;
+ struct mlx5_fpga_device *fdev = dev->fpga;
+ struct mlx5_fpga_ipsec *fipsec = fdev->ipsec;
+ struct mlx5_fpga_ipsec_rule *rule;
+ bool is_esp = fte->action.esp_id;
+ struct mailbox_mod mbox_mod;
+ int ret;
+
+ if (!is_esp ||
+ !(fte->action.action &
+ (MLX5_FLOW_CONTEXT_ACTION_ENCRYPT |
+ MLX5_FLOW_CONTEXT_ACTION_DECRYPT)))
+ return delete_fte(dev, ft, fte);
+
+ rule = rule_search(fipsec, fte);
+ if (!rule)
+ return -ENOENT;
+
+ mlx5_fpga_ipsec_delete_sa_ctx(rule->ctx);
+ rule_delete(fipsec, rule);
+
+ modify_spec_mailbox(dev, fte, &mbox_mod);
+ ret = delete_fte(dev, ft, fte);
+ restore_spec_mailbox(fte, &mbox_mod);
+
+ return ret;
+}
+
+static int
+mlx5_fpga_ipsec_fs_create_flow_group_egress(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ u32 *in,
+ unsigned int *group_id)
+{
+ return fpga_ipsec_fs_create_flow_group(dev, ft, in, group_id, true);
+}
+
+static int
+mlx5_fpga_ipsec_fs_create_fte_egress(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ struct mlx5_flow_group *fg,
+ struct fs_fte *fte)
+{
+ return fpga_ipsec_fs_create_fte(dev, ft, fg, fte, true);
+}
+
+static int
+mlx5_fpga_ipsec_fs_update_fte_egress(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ unsigned int group_id,
+ int modify_mask,
+ struct fs_fte *fte)
+{
+ return fpga_ipsec_fs_update_fte(dev, ft, group_id, modify_mask, fte,
+ true);
+}
+
+static int
+mlx5_fpga_ipsec_fs_delete_fte_egress(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ struct fs_fte *fte)
+{
+ return fpga_ipsec_fs_delete_fte(dev, ft, fte, true);
+}
+
+static int
+mlx5_fpga_ipsec_fs_create_flow_group_ingress(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ u32 *in,
+ unsigned int *group_id)
+{
+ return fpga_ipsec_fs_create_flow_group(dev, ft, in, group_id, false);
+}
+
+static int
+mlx5_fpga_ipsec_fs_create_fte_ingress(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ struct mlx5_flow_group *fg,
+ struct fs_fte *fte)
+{
+ return fpga_ipsec_fs_create_fte(dev, ft, fg, fte, false);
+}
+
+static int
+mlx5_fpga_ipsec_fs_update_fte_ingress(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ unsigned int group_id,
+ int modify_mask,
+ struct fs_fte *fte)
+{
+ return fpga_ipsec_fs_update_fte(dev, ft, group_id, modify_mask, fte,
+ false);
+}
+
+static int
+mlx5_fpga_ipsec_fs_delete_fte_ingress(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ struct fs_fte *fte)
+{
+ return fpga_ipsec_fs_delete_fte(dev, ft, fte, false);
+}
+
+static struct mlx5_flow_cmds fpga_ipsec_ingress;
+static struct mlx5_flow_cmds fpga_ipsec_egress;
+
+const struct mlx5_flow_cmds *mlx5_fs_cmd_get_default_ipsec_fpga_cmds(enum fs_flow_table_type type)
+{
+ switch (type) {
+ case FS_FT_NIC_RX:
+ return &fpga_ipsec_ingress;
+ case FS_FT_NIC_TX:
+ return &fpga_ipsec_egress;
+ default:
+ WARN_ON(true);
+ return NULL;
+ }
+}
+
int mlx5_fpga_ipsec_init(struct mlx5_core_dev *mdev)
{
struct mlx5_fpga_conn_attr init_attr = {0};
@@ -332,6 +1251,8 @@ int mlx5_fpga_ipsec_init(struct mlx5_core_dev *mdev)
if (!fdev->ipsec)
return -ENOMEM;
+ fdev->ipsec->fdev = fdev;
+
err = mlx5_fpga_get_sbu_caps(fdev, sizeof(fdev->ipsec->caps),
fdev->ipsec->caps);
if (err) {
@@ -355,14 +1276,47 @@ int mlx5_fpga_ipsec_init(struct mlx5_core_dev *mdev)
goto error;
}
fdev->ipsec->conn = conn;
+
+ err = rhashtable_init(&fdev->ipsec->sa_hash, &rhash_sa);
+ if (err)
+ goto err_destroy_conn;
+ mutex_init(&fdev->ipsec->sa_hash_lock);
+
+ fdev->ipsec->rules_rb = RB_ROOT;
+ mutex_init(&fdev->ipsec->rules_rb_lock);
+
+ err = mlx5_fpga_ipsec_enable_supported_caps(mdev);
+ if (err) {
+ mlx5_fpga_err(fdev, "Failed to enable IPSec extended capabilities: %d\n",
+ err);
+ goto err_destroy_hash;
+ }
+
return 0;
+err_destroy_hash:
+ rhashtable_destroy(&fdev->ipsec->sa_hash);
+
+err_destroy_conn:
+ mlx5_fpga_sbu_conn_destroy(conn);
+
error:
kfree(fdev->ipsec);
fdev->ipsec = NULL;
return err;
}
+static void destroy_rules_rb(struct rb_root *root)
+{
+ struct mlx5_fpga_ipsec_rule *r, *tmp;
+
+ rbtree_postorder_for_each_entry_safe(r, tmp, root, node) {
+ rb_erase(&r->node, root);
+ mlx5_fpga_ipsec_delete_sa_ctx(r->ctx);
+ kfree(r);
+ }
+}
+
void mlx5_fpga_ipsec_cleanup(struct mlx5_core_dev *mdev)
{
struct mlx5_fpga_device *fdev = mdev->fpga;
@@ -370,7 +1324,209 @@ void mlx5_fpga_ipsec_cleanup(struct mlx5_core_dev *mdev)
if (!mlx5_fpga_is_ipsec_device(mdev))
return;
+ destroy_rules_rb(&fdev->ipsec->rules_rb);
+ rhashtable_destroy(&fdev->ipsec->sa_hash);
+
mlx5_fpga_sbu_conn_destroy(fdev->ipsec->conn);
kfree(fdev->ipsec);
fdev->ipsec = NULL;
}
+
+void mlx5_fpga_ipsec_build_fs_cmds(void)
+{
+ /* ingress */
+ fpga_ipsec_ingress.create_flow_table =
+ mlx5_fs_cmd_get_default(egress_to_fs_ft(false))->create_flow_table;
+ fpga_ipsec_ingress.destroy_flow_table =
+ mlx5_fs_cmd_get_default(egress_to_fs_ft(false))->destroy_flow_table;
+ fpga_ipsec_ingress.modify_flow_table =
+ mlx5_fs_cmd_get_default(egress_to_fs_ft(false))->modify_flow_table;
+ fpga_ipsec_ingress.create_flow_group =
+ mlx5_fpga_ipsec_fs_create_flow_group_ingress;
+ fpga_ipsec_ingress.destroy_flow_group =
+ mlx5_fs_cmd_get_default(egress_to_fs_ft(false))->destroy_flow_group;
+ fpga_ipsec_ingress.create_fte =
+ mlx5_fpga_ipsec_fs_create_fte_ingress;
+ fpga_ipsec_ingress.update_fte =
+ mlx5_fpga_ipsec_fs_update_fte_ingress;
+ fpga_ipsec_ingress.delete_fte =
+ mlx5_fpga_ipsec_fs_delete_fte_ingress;
+ fpga_ipsec_ingress.update_root_ft =
+ mlx5_fs_cmd_get_default(egress_to_fs_ft(false))->update_root_ft;
+
+ /* egress */
+ fpga_ipsec_egress.create_flow_table =
+ mlx5_fs_cmd_get_default(egress_to_fs_ft(true))->create_flow_table;
+ fpga_ipsec_egress.destroy_flow_table =
+ mlx5_fs_cmd_get_default(egress_to_fs_ft(true))->destroy_flow_table;
+ fpga_ipsec_egress.modify_flow_table =
+ mlx5_fs_cmd_get_default(egress_to_fs_ft(true))->modify_flow_table;
+ fpga_ipsec_egress.create_flow_group =
+ mlx5_fpga_ipsec_fs_create_flow_group_egress;
+ fpga_ipsec_egress.destroy_flow_group =
+ mlx5_fs_cmd_get_default(egress_to_fs_ft(true))->destroy_flow_group;
+ fpga_ipsec_egress.create_fte =
+ mlx5_fpga_ipsec_fs_create_fte_egress;
+ fpga_ipsec_egress.update_fte =
+ mlx5_fpga_ipsec_fs_update_fte_egress;
+ fpga_ipsec_egress.delete_fte =
+ mlx5_fpga_ipsec_fs_delete_fte_egress;
+ fpga_ipsec_egress.update_root_ft =
+ mlx5_fs_cmd_get_default(egress_to_fs_ft(true))->update_root_ft;
+}
+
+static int
+mlx5_fpga_esp_validate_xfrm_attrs(struct mlx5_core_dev *mdev,
+ const struct mlx5_accel_esp_xfrm_attrs *attrs)
+{
+ if (attrs->tfc_pad) {
+ mlx5_core_err(mdev, "Cannot offload xfrm states with tfc padding\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (attrs->replay_type != MLX5_ACCEL_ESP_REPLAY_NONE) {
+ mlx5_core_err(mdev, "Cannot offload xfrm states with anti replay\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (attrs->keymat_type != MLX5_ACCEL_ESP_KEYMAT_AES_GCM) {
+ mlx5_core_err(mdev, "Only aes gcm keymat is supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (attrs->keymat.aes_gcm.iv_algo !=
+ MLX5_ACCEL_ESP_AES_GCM_IV_ALGO_SEQ) {
+ mlx5_core_err(mdev, "Only iv sequence algo is supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (attrs->keymat.aes_gcm.icv_len != 128) {
+ mlx5_core_err(mdev, "Cannot offload xfrm states with AEAD ICV length other than 128bit\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (attrs->keymat.aes_gcm.key_len != 128 &&
+ attrs->keymat.aes_gcm.key_len != 256) {
+ mlx5_core_err(mdev, "Cannot offload xfrm states with AEAD key length other than 128/256 bit\n");
+ return -EOPNOTSUPP;
+ }
+
+ if ((attrs->flags & MLX5_ACCEL_ESP_FLAGS_ESN_TRIGGERED) &&
+ (!MLX5_GET(ipsec_extended_cap, mdev->fpga->ipsec->caps,
+ v2_command))) {
+ mlx5_core_err(mdev, "Cannot offload xfrm states with AEAD key length other than 128/256 bit\n");
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+struct mlx5_accel_esp_xfrm *
+mlx5_fpga_esp_create_xfrm(struct mlx5_core_dev *mdev,
+ const struct mlx5_accel_esp_xfrm_attrs *attrs,
+ u32 flags)
+{
+ struct mlx5_fpga_esp_xfrm *fpga_xfrm;
+
+ if (!(flags & MLX5_ACCEL_XFRM_FLAG_REQUIRE_METADATA)) {
+ mlx5_core_warn(mdev, "Tried to create an esp action without metadata\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (mlx5_fpga_esp_validate_xfrm_attrs(mdev, attrs)) {
+ mlx5_core_warn(mdev, "Tried to create an esp with unsupported attrs\n");
+ return ERR_PTR(-EOPNOTSUPP);
+ }
+
+ fpga_xfrm = kzalloc(sizeof(*fpga_xfrm), GFP_KERNEL);
+ if (!fpga_xfrm)
+ return ERR_PTR(-ENOMEM);
+
+ mutex_init(&fpga_xfrm->lock);
+ memcpy(&fpga_xfrm->accel_xfrm.attrs, attrs,
+ sizeof(fpga_xfrm->accel_xfrm.attrs));
+
+ return &fpga_xfrm->accel_xfrm;
+}
+
+void mlx5_fpga_esp_destroy_xfrm(struct mlx5_accel_esp_xfrm *xfrm)
+{
+ struct mlx5_fpga_esp_xfrm *fpga_xfrm =
+ container_of(xfrm, struct mlx5_fpga_esp_xfrm,
+ accel_xfrm);
+ /* assuming no sa_ctx are connected to this xfrm_ctx */
+ kfree(fpga_xfrm);
+}
+
+int mlx5_fpga_esp_modify_xfrm(struct mlx5_accel_esp_xfrm *xfrm,
+ const struct mlx5_accel_esp_xfrm_attrs *attrs)
+{
+ struct mlx5_core_dev *mdev = xfrm->mdev;
+ struct mlx5_fpga_device *fdev = mdev->fpga;
+ struct mlx5_fpga_ipsec *fipsec = fdev->ipsec;
+ struct mlx5_fpga_esp_xfrm *fpga_xfrm;
+ struct mlx5_ifc_fpga_ipsec_sa org_hw_sa;
+
+ int err = 0;
+
+ if (!memcmp(&xfrm->attrs, attrs, sizeof(xfrm->attrs)))
+ return 0;
+
+ if (!mlx5_fpga_esp_validate_xfrm_attrs(mdev, attrs)) {
+ mlx5_core_warn(mdev, "Tried to create an esp with unsupported attrs\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (is_v2_sadb_supported(fipsec)) {
+ mlx5_core_warn(mdev, "Modify esp is not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ fpga_xfrm = container_of(xfrm, struct mlx5_fpga_esp_xfrm, accel_xfrm);
+
+ mutex_lock(&fpga_xfrm->lock);
+
+ if (!fpga_xfrm->sa_ctx)
+ /* Unbounded xfrm, chane only sw attrs */
+ goto change_sw_xfrm_attrs;
+
+ /* copy original hw sa */
+ memcpy(&org_hw_sa, &fpga_xfrm->sa_ctx->hw_sa, sizeof(org_hw_sa));
+ mutex_lock(&fipsec->sa_hash_lock);
+ /* remove original hw sa from hash */
+ WARN_ON(rhashtable_remove_fast(&fipsec->sa_hash,
+ &fpga_xfrm->sa_ctx->hash, rhash_sa));
+ /* update hw_sa with new xfrm attrs*/
+ mlx5_fpga_ipsec_build_hw_xfrm(xfrm->mdev, attrs,
+ &fpga_xfrm->sa_ctx->hw_sa);
+ /* try to insert new hw_sa to hash */
+ err = rhashtable_insert_fast(&fipsec->sa_hash,
+ &fpga_xfrm->sa_ctx->hash, rhash_sa);
+ if (err)
+ goto rollback_sa;
+
+ /* modify device with new hw_sa */
+ err = mlx5_fpga_ipsec_update_hw_sa(fdev, &fpga_xfrm->sa_ctx->hw_sa,
+ MLX5_FPGA_IPSEC_CMD_OP_MOD_SA_V2);
+ fpga_xfrm->sa_ctx->hw_sa.ipsec_sa_v1.cmd = 0;
+ if (err)
+ WARN_ON(rhashtable_remove_fast(&fipsec->sa_hash,
+ &fpga_xfrm->sa_ctx->hash,
+ rhash_sa));
+rollback_sa:
+ if (err) {
+ /* return original hw_sa to hash */
+ memcpy(&fpga_xfrm->sa_ctx->hw_sa, &org_hw_sa,
+ sizeof(org_hw_sa));
+ WARN_ON(rhashtable_insert_fast(&fipsec->sa_hash,
+ &fpga_xfrm->sa_ctx->hash,
+ rhash_sa));
+ }
+ mutex_unlock(&fipsec->sa_hash_lock);
+
+change_sw_xfrm_attrs:
+ if (!err)
+ memcpy(&xfrm->attrs, attrs, sizeof(xfrm->attrs));
+ mutex_unlock(&fpga_xfrm->lock);
+ return err;
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.h b/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.h
index 26a3e4b56972..2b5e63b0d4d6 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.h
@@ -35,33 +35,38 @@
#define __MLX5_FPGA_IPSEC_H__
#include "accel/ipsec.h"
+#include "fs_cmd.h"
#ifdef CONFIG_MLX5_FPGA
-void *mlx5_fpga_ipsec_sa_cmd_exec(struct mlx5_core_dev *mdev,
- struct mlx5_accel_ipsec_sa *cmd);
-int mlx5_fpga_ipsec_sa_cmd_wait(void *context);
-
u32 mlx5_fpga_ipsec_device_caps(struct mlx5_core_dev *mdev);
unsigned int mlx5_fpga_ipsec_counters_count(struct mlx5_core_dev *mdev);
int mlx5_fpga_ipsec_counters_read(struct mlx5_core_dev *mdev, u64 *counters,
unsigned int counters_count);
+void *mlx5_fpga_ipsec_create_sa_ctx(struct mlx5_core_dev *mdev,
+ struct mlx5_accel_esp_xfrm *accel_xfrm,
+ const __be32 saddr[4],
+ const __be32 daddr[4],
+ const __be32 spi, bool is_ipv6);
+void mlx5_fpga_ipsec_delete_sa_ctx(void *context);
+
int mlx5_fpga_ipsec_init(struct mlx5_core_dev *mdev);
void mlx5_fpga_ipsec_cleanup(struct mlx5_core_dev *mdev);
+void mlx5_fpga_ipsec_build_fs_cmds(void);
-#else
+struct mlx5_accel_esp_xfrm *
+mlx5_fpga_esp_create_xfrm(struct mlx5_core_dev *mdev,
+ const struct mlx5_accel_esp_xfrm_attrs *attrs,
+ u32 flags);
+void mlx5_fpga_esp_destroy_xfrm(struct mlx5_accel_esp_xfrm *xfrm);
+int mlx5_fpga_esp_modify_xfrm(struct mlx5_accel_esp_xfrm *xfrm,
+ const struct mlx5_accel_esp_xfrm_attrs *attrs);
-static inline void *mlx5_fpga_ipsec_sa_cmd_exec(struct mlx5_core_dev *mdev,
- struct mlx5_accel_ipsec_sa *cmd)
-{
- return ERR_PTR(-EOPNOTSUPP);
-}
+const struct mlx5_flow_cmds *
+mlx5_fs_cmd_get_default_ipsec_fpga_cmds(enum fs_flow_table_type type);
-static inline int mlx5_fpga_ipsec_sa_cmd_wait(void *context)
-{
- return -EOPNOTSUPP;
-}
+#else
static inline u32 mlx5_fpga_ipsec_device_caps(struct mlx5_core_dev *mdev)
{
@@ -80,6 +85,20 @@ static inline int mlx5_fpga_ipsec_counters_read(struct mlx5_core_dev *mdev,
return 0;
}
+static inline void *
+mlx5_fpga_ipsec_create_sa_ctx(struct mlx5_core_dev *mdev,
+ struct mlx5_accel_esp_xfrm *accel_xfrm,
+ const __be32 saddr[4],
+ const __be32 daddr[4],
+ const __be32 spi, bool is_ipv6)
+{
+ return NULL;
+}
+
+static inline void mlx5_fpga_ipsec_delete_sa_ctx(void *context)
+{
+}
+
static inline int mlx5_fpga_ipsec_init(struct mlx5_core_dev *mdev)
{
return 0;
@@ -89,6 +108,35 @@ static inline void mlx5_fpga_ipsec_cleanup(struct mlx5_core_dev *mdev)
{
}
+static inline void mlx5_fpga_ipsec_build_fs_cmds(void)
+{
+}
+
+static inline struct mlx5_accel_esp_xfrm *
+mlx5_fpga_esp_create_xfrm(struct mlx5_core_dev *mdev,
+ const struct mlx5_accel_esp_xfrm_attrs *attrs,
+ u32 flags)
+{
+ return ERR_PTR(-EOPNOTSUPP);
+}
+
+static inline void mlx5_fpga_esp_destroy_xfrm(struct mlx5_accel_esp_xfrm *xfrm)
+{
+}
+
+static inline int
+mlx5_fpga_esp_modify_xfrm(struct mlx5_accel_esp_xfrm *xfrm,
+ const struct mlx5_accel_esp_xfrm_attrs *attrs)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline const struct mlx5_flow_cmds *
+mlx5_fs_cmd_get_default_ipsec_fpga_cmds(enum fs_flow_table_type type)
+{
+ return mlx5_fs_cmd_get_default(type);
+}
+
#endif /* CONFIG_MLX5_FPGA */
#endif /* __MLX5_FPGA_SADB_H__ */
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c
index 881e2e55840c..ef5afd7c9325 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c
@@ -39,9 +39,81 @@
#include "mlx5_core.h"
#include "eswitch.h"
-int mlx5_cmd_update_root_ft(struct mlx5_core_dev *dev,
- struct mlx5_flow_table *ft, u32 underlay_qpn,
- bool disconnect)
+static int mlx5_cmd_stub_update_root_ft(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ u32 underlay_qpn,
+ bool disconnect)
+{
+ return 0;
+}
+
+static int mlx5_cmd_stub_create_flow_table(struct mlx5_core_dev *dev,
+ u16 vport,
+ enum fs_flow_table_op_mod op_mod,
+ enum fs_flow_table_type type,
+ unsigned int level,
+ unsigned int log_size,
+ struct mlx5_flow_table *next_ft,
+ unsigned int *table_id, u32 flags)
+{
+ return 0;
+}
+
+static int mlx5_cmd_stub_destroy_flow_table(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft)
+{
+ return 0;
+}
+
+static int mlx5_cmd_stub_modify_flow_table(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ struct mlx5_flow_table *next_ft)
+{
+ return 0;
+}
+
+static int mlx5_cmd_stub_create_flow_group(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ u32 *in,
+ unsigned int *group_id)
+{
+ return 0;
+}
+
+static int mlx5_cmd_stub_destroy_flow_group(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ unsigned int group_id)
+{
+ return 0;
+}
+
+static int mlx5_cmd_stub_create_fte(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ struct mlx5_flow_group *group,
+ struct fs_fte *fte)
+{
+ return 0;
+}
+
+static int mlx5_cmd_stub_update_fte(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ unsigned int group_id,
+ int modify_mask,
+ struct fs_fte *fte)
+{
+ return -EOPNOTSUPP;
+}
+
+static int mlx5_cmd_stub_delete_fte(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ struct fs_fte *fte)
+{
+ return 0;
+}
+
+static int mlx5_cmd_update_root_ft(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft, u32 underlay_qpn,
+ bool disconnect)
{
u32 in[MLX5_ST_SZ_DW(set_flow_table_root_in)] = {0};
u32 out[MLX5_ST_SZ_DW(set_flow_table_root_out)] = {0};
@@ -71,12 +143,14 @@ int mlx5_cmd_update_root_ft(struct mlx5_core_dev *dev,
return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
}
-int mlx5_cmd_create_flow_table(struct mlx5_core_dev *dev,
- u16 vport,
- enum fs_flow_table_op_mod op_mod,
- enum fs_flow_table_type type, unsigned int level,
- unsigned int log_size, struct mlx5_flow_table
- *next_ft, unsigned int *table_id, u32 flags)
+static int mlx5_cmd_create_flow_table(struct mlx5_core_dev *dev,
+ u16 vport,
+ enum fs_flow_table_op_mod op_mod,
+ enum fs_flow_table_type type,
+ unsigned int level,
+ unsigned int log_size,
+ struct mlx5_flow_table *next_ft,
+ unsigned int *table_id, u32 flags)
{
int en_encap_decap = !!(flags & MLX5_FLOW_TABLE_TUNNEL_EN);
u32 out[MLX5_ST_SZ_DW(create_flow_table_out)] = {0};
@@ -125,8 +199,8 @@ int mlx5_cmd_create_flow_table(struct mlx5_core_dev *dev,
return err;
}
-int mlx5_cmd_destroy_flow_table(struct mlx5_core_dev *dev,
- struct mlx5_flow_table *ft)
+static int mlx5_cmd_destroy_flow_table(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft)
{
u32 in[MLX5_ST_SZ_DW(destroy_flow_table_in)] = {0};
u32 out[MLX5_ST_SZ_DW(destroy_flow_table_out)] = {0};
@@ -143,9 +217,9 @@ int mlx5_cmd_destroy_flow_table(struct mlx5_core_dev *dev,
return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
}
-int mlx5_cmd_modify_flow_table(struct mlx5_core_dev *dev,
- struct mlx5_flow_table *ft,
- struct mlx5_flow_table *next_ft)
+static int mlx5_cmd_modify_flow_table(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ struct mlx5_flow_table *next_ft)
{
u32 in[MLX5_ST_SZ_DW(modify_flow_table_in)] = {0};
u32 out[MLX5_ST_SZ_DW(modify_flow_table_out)] = {0};
@@ -188,10 +262,10 @@ int mlx5_cmd_modify_flow_table(struct mlx5_core_dev *dev,
return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
}
-int mlx5_cmd_create_flow_group(struct mlx5_core_dev *dev,
- struct mlx5_flow_table *ft,
- u32 *in,
- unsigned int *group_id)
+static int mlx5_cmd_create_flow_group(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ u32 *in,
+ unsigned int *group_id)
{
u32 out[MLX5_ST_SZ_DW(create_flow_group_out)] = {0};
int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
@@ -213,9 +287,9 @@ int mlx5_cmd_create_flow_group(struct mlx5_core_dev *dev,
return err;
}
-int mlx5_cmd_destroy_flow_group(struct mlx5_core_dev *dev,
- struct mlx5_flow_table *ft,
- unsigned int group_id)
+static int mlx5_cmd_destroy_flow_group(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ unsigned int group_id)
{
u32 out[MLX5_ST_SZ_DW(destroy_flow_group_out)] = {0};
u32 in[MLX5_ST_SZ_DW(destroy_flow_group_in)] = {0};
@@ -243,7 +317,7 @@ static int mlx5_cmd_set_fte(struct mlx5_core_dev *dev,
fte->dests_size * MLX5_ST_SZ_BYTES(dest_format_struct);
u32 out[MLX5_ST_SZ_DW(set_fte_out)] = {0};
struct mlx5_flow_rule *dst;
- void *in_flow_context;
+ void *in_flow_context, *vlan;
void *in_match_value;
void *in_dests;
u32 *in;
@@ -266,16 +340,25 @@ static int mlx5_cmd_set_fte(struct mlx5_core_dev *dev,
in_flow_context = MLX5_ADDR_OF(set_fte_in, in, flow_context);
MLX5_SET(flow_context, in_flow_context, group_id, group_id);
- MLX5_SET(flow_context, in_flow_context, flow_tag, fte->flow_tag);
- MLX5_SET(flow_context, in_flow_context, action, fte->action);
- MLX5_SET(flow_context, in_flow_context, encap_id, fte->encap_id);
- MLX5_SET(flow_context, in_flow_context, modify_header_id, fte->modify_id);
+
+ MLX5_SET(flow_context, in_flow_context, flow_tag, fte->action.flow_tag);
+ MLX5_SET(flow_context, in_flow_context, action, fte->action.action);
+ MLX5_SET(flow_context, in_flow_context, encap_id, fte->action.encap_id);
+ MLX5_SET(flow_context, in_flow_context, modify_header_id,
+ fte->action.modify_id);
+
+ vlan = MLX5_ADDR_OF(flow_context, in_flow_context, push_vlan);
+
+ MLX5_SET(vlan, vlan, ethtype, fte->action.vlan.ethtype);
+ MLX5_SET(vlan, vlan, vid, fte->action.vlan.vid);
+ MLX5_SET(vlan, vlan, prio, fte->action.vlan.prio);
+
in_match_value = MLX5_ADDR_OF(flow_context, in_flow_context,
match_value);
memcpy(in_match_value, &fte->val, sizeof(fte->val));
in_dests = MLX5_ADDR_OF(flow_context, in_flow_context, destination);
- if (fte->action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) {
+ if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) {
int list_size = 0;
list_for_each_entry(dst, &fte->node.children, node.list) {
@@ -301,7 +384,7 @@ static int mlx5_cmd_set_fte(struct mlx5_core_dev *dev,
list_size);
}
- if (fte->action & MLX5_FLOW_CONTEXT_ACTION_COUNT) {
+ if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_COUNT) {
int max_list_size = BIT(MLX5_CAP_FLOWTABLE_TYPE(dev,
log_max_flow_counter,
ft->type));
@@ -332,19 +415,21 @@ err_out:
return err;
}
-int mlx5_cmd_create_fte(struct mlx5_core_dev *dev,
- struct mlx5_flow_table *ft,
- unsigned group_id,
- struct fs_fte *fte)
+static int mlx5_cmd_create_fte(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ struct mlx5_flow_group *group,
+ struct fs_fte *fte)
{
+ unsigned int group_id = group->id;
+
return mlx5_cmd_set_fte(dev, 0, 0, ft, group_id, fte);
}
-int mlx5_cmd_update_fte(struct mlx5_core_dev *dev,
- struct mlx5_flow_table *ft,
- unsigned group_id,
- int modify_mask,
- struct fs_fte *fte)
+static int mlx5_cmd_update_fte(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ unsigned int group_id,
+ int modify_mask,
+ struct fs_fte *fte)
{
int opmod;
int atomic_mod_cap = MLX5_CAP_FLOWTABLE(dev,
@@ -357,9 +442,9 @@ int mlx5_cmd_update_fte(struct mlx5_core_dev *dev,
return mlx5_cmd_set_fte(dev, opmod, modify_mask, ft, group_id, fte);
}
-int mlx5_cmd_delete_fte(struct mlx5_core_dev *dev,
- struct mlx5_flow_table *ft,
- unsigned int index)
+static int mlx5_cmd_delete_fte(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ struct fs_fte *fte)
{
u32 out[MLX5_ST_SZ_DW(delete_fte_out)] = {0};
u32 in[MLX5_ST_SZ_DW(delete_fte_in)] = {0};
@@ -367,7 +452,7 @@ int mlx5_cmd_delete_fte(struct mlx5_core_dev *dev,
MLX5_SET(delete_fte_in, in, opcode, MLX5_CMD_OP_DELETE_FLOW_TABLE_ENTRY);
MLX5_SET(delete_fte_in, in, table_type, ft->type);
MLX5_SET(delete_fte_in, in, table_id, ft->id);
- MLX5_SET(delete_fte_in, in, flow_index, index);
+ MLX5_SET(delete_fte_in, in, flow_index, fte->index);
if (ft->vport) {
MLX5_SET(delete_fte_in, in, vport_number, ft->vport);
MLX5_SET(delete_fte_in, in, other_vport, 1);
@@ -610,3 +695,53 @@ void mlx5_modify_header_dealloc(struct mlx5_core_dev *dev, u32 modify_header_id)
mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
}
+
+static const struct mlx5_flow_cmds mlx5_flow_cmds = {
+ .create_flow_table = mlx5_cmd_create_flow_table,
+ .destroy_flow_table = mlx5_cmd_destroy_flow_table,
+ .modify_flow_table = mlx5_cmd_modify_flow_table,
+ .create_flow_group = mlx5_cmd_create_flow_group,
+ .destroy_flow_group = mlx5_cmd_destroy_flow_group,
+ .create_fte = mlx5_cmd_create_fte,
+ .update_fte = mlx5_cmd_update_fte,
+ .delete_fte = mlx5_cmd_delete_fte,
+ .update_root_ft = mlx5_cmd_update_root_ft,
+};
+
+static const struct mlx5_flow_cmds mlx5_flow_cmd_stubs = {
+ .create_flow_table = mlx5_cmd_stub_create_flow_table,
+ .destroy_flow_table = mlx5_cmd_stub_destroy_flow_table,
+ .modify_flow_table = mlx5_cmd_stub_modify_flow_table,
+ .create_flow_group = mlx5_cmd_stub_create_flow_group,
+ .destroy_flow_group = mlx5_cmd_stub_destroy_flow_group,
+ .create_fte = mlx5_cmd_stub_create_fte,
+ .update_fte = mlx5_cmd_stub_update_fte,
+ .delete_fte = mlx5_cmd_stub_delete_fte,
+ .update_root_ft = mlx5_cmd_stub_update_root_ft,
+};
+
+static const struct mlx5_flow_cmds *mlx5_fs_cmd_get_fw_cmds(void)
+{
+ return &mlx5_flow_cmds;
+}
+
+static const struct mlx5_flow_cmds *mlx5_fs_cmd_get_stub_cmds(void)
+{
+ return &mlx5_flow_cmd_stubs;
+}
+
+const struct mlx5_flow_cmds *mlx5_fs_cmd_get_default(enum fs_flow_table_type type)
+{
+ switch (type) {
+ case FS_FT_NIC_RX:
+ case FS_FT_ESW_EGRESS_ACL:
+ case FS_FT_ESW_INGRESS_ACL:
+ case FS_FT_FDB:
+ case FS_FT_SNIFFER_RX:
+ case FS_FT_SNIFFER_TX:
+ return mlx5_fs_cmd_get_fw_cmds();
+ case FS_FT_NIC_TX:
+ default:
+ return mlx5_fs_cmd_get_stub_cmds();
+ }
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h
index 71e2d0f37ad9..6228ba7bfa1a 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h
@@ -33,46 +33,52 @@
#ifndef _MLX5_FS_CMD_
#define _MLX5_FS_CMD_
-int mlx5_cmd_create_flow_table(struct mlx5_core_dev *dev,
- u16 vport,
- enum fs_flow_table_op_mod op_mod,
- enum fs_flow_table_type type, unsigned int level,
- unsigned int log_size, struct mlx5_flow_table
- *next_ft, unsigned int *table_id, u32 flags);
+#include "fs_core.h"
-int mlx5_cmd_destroy_flow_table(struct mlx5_core_dev *dev,
- struct mlx5_flow_table *ft);
+struct mlx5_flow_cmds {
+ int (*create_flow_table)(struct mlx5_core_dev *dev,
+ u16 vport,
+ enum fs_flow_table_op_mod op_mod,
+ enum fs_flow_table_type type,
+ unsigned int level, unsigned int log_size,
+ struct mlx5_flow_table *next_ft,
+ unsigned int *table_id, u32 flags);
+ int (*destroy_flow_table)(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft);
-int mlx5_cmd_modify_flow_table(struct mlx5_core_dev *dev,
- struct mlx5_flow_table *ft,
- struct mlx5_flow_table *next_ft);
+ int (*modify_flow_table)(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ struct mlx5_flow_table *next_ft);
-int mlx5_cmd_create_flow_group(struct mlx5_core_dev *dev,
- struct mlx5_flow_table *ft,
- u32 *in, unsigned int *group_id);
+ int (*create_flow_group)(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ u32 *in,
+ unsigned int *group_id);
-int mlx5_cmd_destroy_flow_group(struct mlx5_core_dev *dev,
- struct mlx5_flow_table *ft,
- unsigned int group_id);
+ int (*destroy_flow_group)(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ unsigned int group_id);
-int mlx5_cmd_create_fte(struct mlx5_core_dev *dev,
- struct mlx5_flow_table *ft,
- unsigned group_id,
- struct fs_fte *fte);
+ int (*create_fte)(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ struct mlx5_flow_group *fg,
+ struct fs_fte *fte);
-int mlx5_cmd_update_fte(struct mlx5_core_dev *dev,
- struct mlx5_flow_table *ft,
- unsigned group_id,
- int modify_mask,
- struct fs_fte *fte);
+ int (*update_fte)(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ unsigned int group_id,
+ int modify_mask,
+ struct fs_fte *fte);
-int mlx5_cmd_delete_fte(struct mlx5_core_dev *dev,
- struct mlx5_flow_table *ft,
- unsigned int index);
+ int (*delete_fte)(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ struct fs_fte *fte);
-int mlx5_cmd_update_root_ft(struct mlx5_core_dev *dev,
- struct mlx5_flow_table *ft, u32 underlay_qpn,
- bool disconnect);
+ int (*update_root_ft)(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ u32 underlay_qpn,
+ bool disconnect);
+};
int mlx5_cmd_fc_alloc(struct mlx5_core_dev *dev, u32 *id);
int mlx5_cmd_fc_free(struct mlx5_core_dev *dev, u32 id);
@@ -90,4 +96,6 @@ void mlx5_cmd_fc_bulk_get(struct mlx5_core_dev *dev,
struct mlx5_cmd_fc_bulk *b, u32 id,
u64 *packets, u64 *bytes);
+const struct mlx5_flow_cmds *mlx5_fs_cmd_get_default(enum fs_flow_table_type type);
+
#endif
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
index 31fc2cfac3b3..de51e7c39bc8 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
@@ -37,6 +37,8 @@
#include "fs_core.h"
#include "fs_cmd.h"
#include "diag/fs_tracepoint.h"
+#include "accel/ipsec.h"
+#include "fpga/ipsec.h"
#define INIT_TREE_NODE_ARRAY_SIZE(...) (sizeof((struct init_tree_node[]){__VA_ARGS__}) /\
sizeof(struct init_tree_node))
@@ -425,15 +427,17 @@ static void del_sw_prio(struct fs_node *node)
static void del_hw_flow_table(struct fs_node *node)
{
+ struct mlx5_flow_root_namespace *root;
struct mlx5_flow_table *ft;
struct mlx5_core_dev *dev;
int err;
fs_get_obj(ft, node);
dev = get_dev(&ft->node);
+ root = find_root(&ft->node);
if (node->active) {
- err = mlx5_cmd_destroy_flow_table(dev, ft);
+ err = root->cmds->destroy_flow_table(dev, ft);
if (err)
mlx5_core_warn(dev, "flow steering can't destroy ft\n");
}
@@ -454,6 +458,7 @@ static void del_sw_flow_table(struct fs_node *node)
static void del_sw_hw_rule(struct fs_node *node)
{
+ struct mlx5_flow_root_namespace *root;
struct mlx5_flow_rule *rule;
struct mlx5_flow_table *ft;
struct mlx5_flow_group *fg;
@@ -477,19 +482,20 @@ static void del_sw_hw_rule(struct fs_node *node)
if (rule->dest_attr.type == MLX5_FLOW_DESTINATION_TYPE_COUNTER &&
--fte->dests_size) {
modify_mask = BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_ACTION);
- fte->action &= ~MLX5_FLOW_CONTEXT_ACTION_COUNT;
+ fte->action.action &= ~MLX5_FLOW_CONTEXT_ACTION_COUNT;
update_fte = true;
goto out;
}
- if ((fte->action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) &&
+ if ((fte->action.action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) &&
--fte->dests_size) {
modify_mask = BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_DESTINATION_LIST),
update_fte = true;
}
out:
+ root = find_root(&ft->node);
if (update_fte && fte->dests_size) {
- err = mlx5_cmd_update_fte(dev, ft, fg->id, modify_mask, fte);
+ err = root->cmds->update_fte(dev, ft, fg->id, modify_mask, fte);
if (err)
mlx5_core_warn(dev,
"%s can't del rule fg id=%d fte_index=%d\n",
@@ -500,6 +506,7 @@ out:
static void del_hw_fte(struct fs_node *node)
{
+ struct mlx5_flow_root_namespace *root;
struct mlx5_flow_table *ft;
struct mlx5_flow_group *fg;
struct mlx5_core_dev *dev;
@@ -512,9 +519,9 @@ static void del_hw_fte(struct fs_node *node)
trace_mlx5_fs_del_fte(fte);
dev = get_dev(&ft->node);
+ root = find_root(&ft->node);
if (node->active) {
- err = mlx5_cmd_delete_fte(dev, ft,
- fte->index);
+ err = root->cmds->delete_fte(dev, ft, fte);
if (err)
mlx5_core_warn(dev,
"flow steering can't delete fte in index %d of flow group id %d\n",
@@ -542,6 +549,7 @@ static void del_sw_fte(struct fs_node *node)
static void del_hw_flow_group(struct fs_node *node)
{
+ struct mlx5_flow_root_namespace *root;
struct mlx5_flow_group *fg;
struct mlx5_flow_table *ft;
struct mlx5_core_dev *dev;
@@ -551,7 +559,8 @@ static void del_hw_flow_group(struct fs_node *node)
dev = get_dev(&ft->node);
trace_mlx5_fs_del_fg(fg);
- if (fg->node.active && mlx5_cmd_destroy_flow_group(dev, ft, fg->id))
+ root = find_root(&ft->node);
+ if (fg->node.active && root->cmds->destroy_flow_group(dev, ft, fg->id))
mlx5_core_warn(dev, "flow steering can't destroy fg %d of ft %d\n",
fg->id, ft->id);
}
@@ -615,10 +624,7 @@ static struct fs_fte *alloc_fte(struct mlx5_flow_table *ft,
memcpy(fte->val, match_value, sizeof(fte->val));
fte->node.type = FS_TYPE_FLOW_ENTRY;
- fte->flow_tag = flow_act->flow_tag;
- fte->action = flow_act->action;
- fte->encap_id = flow_act->encap_id;
- fte->modify_id = flow_act->modify_id;
+ fte->action = *flow_act;
tree_init_node(&fte->node, del_hw_fte, del_sw_fte);
@@ -797,15 +803,14 @@ static int connect_fts_in_prio(struct mlx5_core_dev *dev,
struct fs_prio *prio,
struct mlx5_flow_table *ft)
{
+ struct mlx5_flow_root_namespace *root = find_root(&prio->node);
struct mlx5_flow_table *iter;
int i = 0;
int err;
fs_for_each_ft(iter, prio) {
i++;
- err = mlx5_cmd_modify_flow_table(dev,
- iter,
- ft);
+ err = root->cmds->modify_flow_table(dev, iter, ft);
if (err) {
mlx5_core_warn(dev, "Failed to modify flow table %d\n",
iter->id);
@@ -853,12 +858,12 @@ static int update_root_ft_create(struct mlx5_flow_table *ft, struct fs_prio
if (list_empty(&root->underlay_qpns)) {
/* Don't set any QPN (zero) in case QPN list is empty */
qpn = 0;
- err = mlx5_cmd_update_root_ft(root->dev, ft, qpn, false);
+ err = root->cmds->update_root_ft(root->dev, ft, qpn, false);
} else {
list_for_each_entry(uqp, &root->underlay_qpns, list) {
qpn = uqp->qpn;
- err = mlx5_cmd_update_root_ft(root->dev, ft, qpn,
- false);
+ err = root->cmds->update_root_ft(root->dev, ft,
+ qpn, false);
if (err)
break;
}
@@ -877,6 +882,7 @@ static int update_root_ft_create(struct mlx5_flow_table *ft, struct fs_prio
static int _mlx5_modify_rule_destination(struct mlx5_flow_rule *rule,
struct mlx5_flow_destination *dest)
{
+ struct mlx5_flow_root_namespace *root;
struct mlx5_flow_table *ft;
struct mlx5_flow_group *fg;
struct fs_fte *fte;
@@ -884,17 +890,16 @@ static int _mlx5_modify_rule_destination(struct mlx5_flow_rule *rule,
int err = 0;
fs_get_obj(fte, rule->node.parent);
- if (!(fte->action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST))
+ if (!(fte->action.action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST))
return -EINVAL;
down_write_ref_node(&fte->node);
fs_get_obj(fg, fte->node.parent);
fs_get_obj(ft, fg->node.parent);
memcpy(&rule->dest_attr, dest, sizeof(*dest));
- err = mlx5_cmd_update_fte(get_dev(&ft->node),
- ft, fg->id,
- modify_mask,
- fte);
+ root = find_root(&ft->node);
+ err = root->cmds->update_fte(get_dev(&ft->node), ft, fg->id,
+ modify_mask, fte);
up_write_ref_node(&fte->node);
return err;
@@ -1035,9 +1040,9 @@ static struct mlx5_flow_table *__mlx5_create_flow_table(struct mlx5_flow_namespa
tree_init_node(&ft->node, del_hw_flow_table, del_sw_flow_table);
log_table_sz = ft->max_fte ? ilog2(ft->max_fte) : 0;
next_ft = find_next_chained_ft(fs_prio);
- err = mlx5_cmd_create_flow_table(root->dev, ft->vport, ft->op_mod, ft->type,
- ft->level, log_table_sz, next_ft, &ft->id,
- ft->flags);
+ err = root->cmds->create_flow_table(root->dev, ft->vport, ft->op_mod,
+ ft->type, ft->level, log_table_sz,
+ next_ft, &ft->id, ft->flags);
if (err)
goto free_ft;
@@ -1053,7 +1058,7 @@ static struct mlx5_flow_table *__mlx5_create_flow_table(struct mlx5_flow_namespa
mutex_unlock(&root->chain_lock);
return ft;
destroy_ft:
- mlx5_cmd_destroy_flow_table(root->dev, ft);
+ root->cmds->destroy_flow_table(root->dev, ft);
free_ft:
kfree(ft);
unlock_root:
@@ -1125,6 +1130,7 @@ EXPORT_SYMBOL(mlx5_create_auto_grouped_flow_table);
struct mlx5_flow_group *mlx5_create_flow_group(struct mlx5_flow_table *ft,
u32 *fg_in)
{
+ struct mlx5_flow_root_namespace *root = find_root(&ft->node);
void *match_criteria = MLX5_ADDR_OF(create_flow_group_in,
fg_in, match_criteria);
u8 match_criteria_enable = MLX5_GET(create_flow_group_in,
@@ -1152,7 +1158,7 @@ struct mlx5_flow_group *mlx5_create_flow_group(struct mlx5_flow_table *ft,
if (IS_ERR(fg))
return fg;
- err = mlx5_cmd_create_flow_group(dev, ft, fg_in, &fg->id);
+ err = root->cmds->create_flow_group(dev, ft, fg_in, &fg->id);
if (err) {
tree_put_node(&fg->node);
return ERR_PTR(err);
@@ -1275,6 +1281,7 @@ add_rule_fte(struct fs_fte *fte,
int dest_num,
bool update_action)
{
+ struct mlx5_flow_root_namespace *root;
struct mlx5_flow_handle *handle;
struct mlx5_flow_table *ft;
int modify_mask = 0;
@@ -1290,12 +1297,13 @@ add_rule_fte(struct fs_fte *fte,
modify_mask |= BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_ACTION);
fs_get_obj(ft, fg->node.parent);
+ root = find_root(&fg->node);
if (!(fte->status & FS_FTE_STATUS_EXISTING))
- err = mlx5_cmd_create_fte(get_dev(&ft->node),
- ft, fg->id, fte);
+ err = root->cmds->create_fte(get_dev(&ft->node),
+ ft, fg, fte);
else
- err = mlx5_cmd_update_fte(get_dev(&ft->node),
- ft, fg->id, modify_mask, fte);
+ err = root->cmds->update_fte(get_dev(&ft->node), ft, fg->id,
+ modify_mask, fte);
if (err)
goto free_handle;
@@ -1360,6 +1368,7 @@ out:
static int create_auto_flow_group(struct mlx5_flow_table *ft,
struct mlx5_flow_group *fg)
{
+ struct mlx5_flow_root_namespace *root = find_root(&ft->node);
struct mlx5_core_dev *dev = get_dev(&ft->node);
int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
void *match_criteria_addr;
@@ -1380,7 +1389,7 @@ static int create_auto_flow_group(struct mlx5_flow_table *ft,
memcpy(match_criteria_addr, fg->mask.match_criteria,
sizeof(fg->mask.match_criteria));
- err = mlx5_cmd_create_flow_group(dev, ft, in, &fg->id);
+ err = root->cmds->create_flow_group(dev, ft, in, &fg->id);
if (!err) {
fg->node.active = true;
trace_mlx5_fs_add_fg(fg);
@@ -1430,7 +1439,9 @@ static bool check_conflicting_actions(u32 action1, u32 action2)
if (xored_actions & (MLX5_FLOW_CONTEXT_ACTION_DROP |
MLX5_FLOW_CONTEXT_ACTION_ENCAP |
MLX5_FLOW_CONTEXT_ACTION_DECAP |
- MLX5_FLOW_CONTEXT_ACTION_MOD_HDR))
+ MLX5_FLOW_CONTEXT_ACTION_MOD_HDR |
+ MLX5_FLOW_CONTEXT_ACTION_VLAN_POP |
+ MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH))
return true;
return false;
@@ -1438,16 +1449,17 @@ static bool check_conflicting_actions(u32 action1, u32 action2)
static int check_conflicting_ftes(struct fs_fte *fte, const struct mlx5_flow_act *flow_act)
{
- if (check_conflicting_actions(flow_act->action, fte->action)) {
+ if (check_conflicting_actions(flow_act->action, fte->action.action)) {
mlx5_core_warn(get_dev(&fte->node),
"Found two FTEs with conflicting actions\n");
return -EEXIST;
}
- if (fte->flow_tag != flow_act->flow_tag) {
+ if (flow_act->has_flow_tag &&
+ fte->action.flow_tag != flow_act->flow_tag) {
mlx5_core_warn(get_dev(&fte->node),
"FTE flow tag %u already exists with different flow tag %u\n",
- fte->flow_tag,
+ fte->action.flow_tag,
flow_act->flow_tag);
return -EEXIST;
}
@@ -1471,12 +1483,12 @@ static struct mlx5_flow_handle *add_rule_fg(struct mlx5_flow_group *fg,
if (ret)
return ERR_PTR(ret);
- old_action = fte->action;
- fte->action |= flow_act->action;
+ old_action = fte->action.action;
+ fte->action.action |= flow_act->action;
handle = add_rule_fte(fte, fg, dest, dest_num,
old_action != flow_act->action);
if (IS_ERR(handle)) {
- fte->action = old_action;
+ fte->action.action = old_action;
return handle;
}
trace_mlx5_fs_set_fte(fte, false);
@@ -1637,7 +1649,6 @@ try_add_to_existing_fg(struct mlx5_flow_table *ft,
list_for_each_entry(iter, match_head, list) {
nested_down_read_ref_node(&iter->g->node, FS_LOCK_PARENT);
- ida_pre_get(&iter->g->fte_allocator, GFP_KERNEL);
}
search_again_locked:
@@ -1919,7 +1930,6 @@ static int update_root_ft_destroy(struct mlx5_flow_table *ft)
return 0;
new_root_ft = find_next_ft(ft);
-
if (!new_root_ft) {
root->root_ft = NULL;
return 0;
@@ -1928,13 +1938,14 @@ static int update_root_ft_destroy(struct mlx5_flow_table *ft)
if (list_empty(&root->underlay_qpns)) {
/* Don't set any QPN (zero) in case QPN list is empty */
qpn = 0;
- err = mlx5_cmd_update_root_ft(root->dev, new_root_ft, qpn,
- false);
+ err = root->cmds->update_root_ft(root->dev, new_root_ft,
+ qpn, false);
} else {
list_for_each_entry(uqp, &root->underlay_qpns, list) {
qpn = uqp->qpn;
- err = mlx5_cmd_update_root_ft(root->dev, new_root_ft,
- qpn, false);
+ err = root->cmds->update_root_ft(root->dev,
+ new_root_ft, qpn,
+ false);
if (err)
break;
}
@@ -2046,6 +2057,11 @@ struct mlx5_flow_namespace *mlx5_get_flow_namespace(struct mlx5_core_dev *dev,
return &steering->sniffer_tx_root_ns->ns;
else
return NULL;
+ case MLX5_FLOW_NAMESPACE_EGRESS:
+ if (steering->egress_root_ns)
+ return &steering->egress_root_ns->ns;
+ else
+ return NULL;
default:
return NULL;
}
@@ -2236,13 +2252,18 @@ static int init_root_tree(struct mlx5_flow_steering *steering,
return 0;
}
-static struct mlx5_flow_root_namespace *create_root_ns(struct mlx5_flow_steering *steering,
- enum fs_flow_table_type
- table_type)
+static struct mlx5_flow_root_namespace
+*create_root_ns(struct mlx5_flow_steering *steering,
+ enum fs_flow_table_type table_type)
{
+ const struct mlx5_flow_cmds *cmds = mlx5_fs_cmd_get_default(table_type);
struct mlx5_flow_root_namespace *root_ns;
struct mlx5_flow_namespace *ns;
+ if (mlx5_accel_ipsec_device_caps(steering->dev) & MLX5_ACCEL_IPSEC_CAP_DEVICE &&
+ (table_type == FS_FT_NIC_RX || table_type == FS_FT_NIC_TX))
+ cmds = mlx5_fs_cmd_get_default_ipsec_fpga_cmds(table_type);
+
/* Create the root namespace */
root_ns = kvzalloc(sizeof(*root_ns), GFP_KERNEL);
if (!root_ns)
@@ -2250,6 +2271,7 @@ static struct mlx5_flow_root_namespace *create_root_ns(struct mlx5_flow_steering
root_ns->dev = steering->dev;
root_ns->table_type = table_type;
+ root_ns->cmds = cmds;
INIT_LIST_HEAD(&root_ns->underlay_qpns);
@@ -2408,6 +2430,7 @@ void mlx5_cleanup_fs(struct mlx5_core_dev *dev)
cleanup_root_ns(steering->fdb_root_ns);
cleanup_root_ns(steering->sniffer_rx_root_ns);
cleanup_root_ns(steering->sniffer_tx_root_ns);
+ cleanup_root_ns(steering->egress_root_ns);
mlx5_cleanup_fc_stats(dev);
kmem_cache_destroy(steering->ftes_cache);
kmem_cache_destroy(steering->fgs_cache);
@@ -2553,6 +2576,20 @@ cleanup_root_ns:
return err;
}
+static int init_egress_root_ns(struct mlx5_flow_steering *steering)
+{
+ struct fs_prio *prio;
+
+ steering->egress_root_ns = create_root_ns(steering,
+ FS_FT_NIC_TX);
+ if (!steering->egress_root_ns)
+ return -ENOMEM;
+
+ /* create 1 prio*/
+ prio = fs_create_prio(&steering->egress_root_ns->ns, 0, 1);
+ return PTR_ERR_OR_ZERO(prio);
+}
+
int mlx5_init_fs(struct mlx5_core_dev *dev)
{
struct mlx5_flow_steering *steering;
@@ -2618,6 +2655,12 @@ int mlx5_init_fs(struct mlx5_core_dev *dev)
goto err;
}
+ if (MLX5_IPSEC_DEV(dev)) {
+ err = init_egress_root_ns(steering);
+ if (err)
+ goto err;
+ }
+
return 0;
err:
mlx5_cleanup_fs(dev);
@@ -2641,7 +2684,8 @@ int mlx5_fs_add_rx_underlay_qpn(struct mlx5_core_dev *dev, u32 underlay_qpn)
goto update_ft_fail;
}
- err = mlx5_cmd_update_root_ft(dev, root->root_ft, underlay_qpn, false);
+ err = root->cmds->update_root_ft(dev, root->root_ft, underlay_qpn,
+ false);
if (err) {
mlx5_core_warn(dev, "Failed adding underlay QPN (%u) to root FT err(%d)\n",
underlay_qpn, err);
@@ -2684,7 +2728,8 @@ int mlx5_fs_remove_rx_underlay_qpn(struct mlx5_core_dev *dev, u32 underlay_qpn)
goto out;
}
- err = mlx5_cmd_update_root_ft(dev, root->root_ft, underlay_qpn, true);
+ err = root->cmds->update_root_ft(dev, root->root_ft, underlay_qpn,
+ true);
if (err)
mlx5_core_warn(dev, "Failed removing underlay QPN (%u) from root FT err(%d)\n",
underlay_qpn, err);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h
index 05262708f14b..e26d3e9d5f9f 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h
@@ -48,6 +48,7 @@ enum fs_node_type {
enum fs_flow_table_type {
FS_FT_NIC_RX = 0x0,
+ FS_FT_NIC_TX = 0x1,
FS_FT_ESW_EGRESS_ACL = 0x2,
FS_FT_ESW_INGRESS_ACL = 0x3,
FS_FT_FDB = 0X4,
@@ -75,6 +76,7 @@ struct mlx5_flow_steering {
struct mlx5_flow_root_namespace **esw_ingress_root_ns;
struct mlx5_flow_root_namespace *sniffer_tx_root_ns;
struct mlx5_flow_root_namespace *sniffer_rx_root_ns;
+ struct mlx5_flow_root_namespace *egress_root_ns;
};
struct fs_node {
@@ -174,11 +176,8 @@ struct fs_fte {
struct fs_node node;
u32 val[MLX5_ST_SZ_DW_MATCH_PARAM];
u32 dests_size;
- u32 flow_tag;
u32 index;
- u32 action;
- u32 encap_id;
- u32 modify_id;
+ struct mlx5_flow_act action;
enum fs_fte_status status;
struct mlx5_fc *counter;
struct rhash_head hash;
@@ -224,6 +223,7 @@ struct mlx5_flow_root_namespace {
/* Should be held when chaining flow tables */
struct mutex chain_lock;
struct list_head underlay_qpns;
+ const struct mlx5_flow_cmds *cmds;
};
int mlx5_init_fc_stats(struct mlx5_core_dev *dev);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw.c b/drivers/net/ethernet/mellanox/mlx5/core/fw.c
index 9d11e92fb541..afd9f4fa22f4 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fw.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fw.c
@@ -183,6 +183,9 @@ int mlx5_query_hca_caps(struct mlx5_core_dev *dev)
return err;
}
+ if (MLX5_CAP_GEN(dev, debug))
+ mlx5_core_get_caps(dev, MLX5_CAP_DEBUG);
+
if (MLX5_CAP_GEN(dev, pcam_reg))
mlx5_get_pcam_reg(dev);
@@ -192,6 +195,12 @@ int mlx5_query_hca_caps(struct mlx5_core_dev *dev)
if (MLX5_CAP_GEN(dev, qcam_reg))
mlx5_get_qcam_reg(dev);
+ if (MLX5_CAP_GEN(dev, device_memory)) {
+ err = mlx5_core_get_caps(dev, MLX5_CAP_DEV_MEM);
+ if (err)
+ return err;
+ }
+
return 0;
}
@@ -242,7 +251,7 @@ int mlx5_cmd_force_teardown_hca(struct mlx5_core_dev *dev)
force_state = MLX5_GET(teardown_hca_out, out, force_state);
if (force_state == MLX5_TEARDOWN_HCA_OUT_FORCE_STATE_FAIL) {
- mlx5_core_err(dev, "teardown with force mode failed\n");
+ mlx5_core_warn(dev, "teardown with force mode failed, doing normal teardown\n");
return -EIO;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c
index f953378bd13d..af3bb2f7a504 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c
@@ -56,14 +56,17 @@ static void mlx5i_build_nic_params(struct mlx5_core_dev *mdev,
struct mlx5e_params *params)
{
/* Override RQ params as IPoIB supports only LINKED LIST RQ for now */
- mlx5e_init_rq_type_params(mdev, params, MLX5_WQ_TYPE_LINKED_LIST);
+ MLX5E_SET_PFLAG(params, MLX5E_PFLAG_RX_STRIDING_RQ, false);
+ mlx5e_set_rq_type(mdev, params);
+ mlx5e_init_rq_type_params(mdev, params);
/* RQ size in ipoib by default is 512 */
- params->log_rq_size = is_kdump_kernel() ?
+ params->log_rq_mtu_frames = is_kdump_kernel() ?
MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE :
MLX5I_PARAMS_DEFAULT_LOG_RQ_SIZE;
params->lro_en = false;
+ params->hard_mtu = MLX5_IB_GRH_BYTES + MLX5_IPOIB_HARD_LEN;
}
/* Called directly after IPoIB netdevice was created to initialize SW structs */
@@ -79,10 +82,10 @@ void mlx5i_init(struct mlx5_core_dev *mdev,
priv->netdev = netdev;
priv->profile = profile;
priv->ppriv = ppriv;
- priv->hard_mtu = MLX5_IB_GRH_BYTES + MLX5_IPOIB_HARD_LEN;
mutex_init(&priv->state_lock);
- mlx5e_build_nic_params(mdev, &priv->channels.params, profile->max_nch(mdev));
+ mlx5e_build_nic_params(mdev, &priv->channels.params,
+ profile->max_nch(mdev), netdev->mtu);
mlx5i_build_nic_params(mdev, &priv->channels.params);
mlx5e_timestamp_init(priv);
@@ -366,25 +369,27 @@ static int mlx5i_change_mtu(struct net_device *netdev, int new_mtu)
{
struct mlx5e_priv *priv = mlx5i_epriv(netdev);
struct mlx5e_channels new_channels = {};
- int curr_mtu;
+ struct mlx5e_params *params;
int err = 0;
mutex_lock(&priv->state_lock);
- curr_mtu = netdev->mtu;
- netdev->mtu = new_mtu;
+ params = &priv->channels.params;
- if (!test_bit(MLX5E_STATE_OPENED, &priv->state))
+ if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) {
+ params->sw_mtu = new_mtu;
+ netdev->mtu = params->sw_mtu;
goto out;
+ }
- new_channels.params = priv->channels.params;
+ new_channels.params = *params;
+ new_channels.params.sw_mtu = new_mtu;
err = mlx5e_open_channels(priv, &new_channels);
- if (err) {
- netdev->mtu = curr_mtu;
+ if (err)
goto out;
- }
mlx5e_switch_priv_channels(priv, &new_channels, NULL);
+ netdev->mtu = new_channels.params.sw_mtu;
out:
mutex_unlock(&priv->state_lock);
@@ -538,7 +543,7 @@ static int mlx5i_detach_mcast(struct net_device *netdev, struct ib_device *hca,
err = mlx5_core_detach_mcg(mdev, gid, ipriv->qp.qpn);
if (err)
- mlx5_core_dbg(mdev, "failed dettaching QPN 0x%x, MGID %pI6\n",
+ mlx5_core_dbg(mdev, "failed detaching QPN 0x%x, MGID %pI6\n",
ipriv->qp.qpn, gid->raw);
return err;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c
index b69e9d847a6b..54a188f41f90 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c
@@ -290,7 +290,7 @@ static void mlx5i_pkey_init(struct mlx5_core_dev *mdev,
netdev->ethtool_ops = &mlx5i_pkey_ethtool_ops;
/* Use dummy rqs */
- priv->channels.params.log_rq_size = MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE;
+ priv->channels.params.log_rq_mtu_frames = MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE;
}
/* Called directly before IPoIB netdevice is destroyed to cleanup SW structs */
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c
index ae391e4b7070..63a8ea31601c 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c
@@ -58,6 +58,7 @@
#include "eswitch.h"
#include "lib/mlx5.h"
#include "fpga/core.h"
+#include "fpga/ipsec.h"
#include "accel/ipsec.h"
#include "lib/clock.h"
@@ -942,9 +943,9 @@ static int mlx5_init_once(struct mlx5_core_dev *dev, struct mlx5_priv *priv)
goto out;
}
- err = mlx5_init_cq_table(dev);
+ err = mlx5_cq_debugfs_init(dev);
if (err) {
- dev_err(&pdev->dev, "failed to initialize cq table\n");
+ dev_err(&pdev->dev, "failed to initialize cq debugfs\n");
goto err_eq_cleanup;
}
@@ -1002,7 +1003,7 @@ err_tables_cleanup:
mlx5_cleanup_mkey_table(dev);
mlx5_cleanup_srq_table(dev);
mlx5_cleanup_qp_table(dev);
- mlx5_cleanup_cq_table(dev);
+ mlx5_cq_debugfs_cleanup(dev);
err_eq_cleanup:
mlx5_eq_cleanup(dev);
@@ -1023,7 +1024,7 @@ static void mlx5_cleanup_once(struct mlx5_core_dev *dev)
mlx5_cleanup_mkey_table(dev);
mlx5_cleanup_srq_table(dev);
mlx5_cleanup_qp_table(dev);
- mlx5_cleanup_cq_table(dev);
+ mlx5_cq_debugfs_cleanup(dev);
mlx5_eq_cleanup(dev);
}
@@ -1043,6 +1044,10 @@ static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv,
dev_info(&pdev->dev, "firmware version: %d.%d.%d\n", fw_rev_maj(dev),
fw_rev_min(dev), fw_rev_sub(dev));
+ /* Only PFs hold the relevant PCIe information for this query */
+ if (mlx5_core_is_pf(dev))
+ pcie_print_link_status(dev->pdev);
+
/* on load removing any previous indication of internal error, device is
* up
*/
@@ -1173,6 +1178,18 @@ static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv,
goto err_affinity_hints;
}
+ err = mlx5_fpga_device_start(dev);
+ if (err) {
+ dev_err(&pdev->dev, "fpga device start failed %d\n", err);
+ goto err_fpga_start;
+ }
+
+ err = mlx5_accel_ipsec_init(dev);
+ if (err) {
+ dev_err(&pdev->dev, "IPSec device start failed %d\n", err);
+ goto err_ipsec_start;
+ }
+
err = mlx5_init_fs(dev);
if (err) {
dev_err(&pdev->dev, "Failed to init flow steering\n");
@@ -1191,17 +1208,6 @@ static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv,
goto err_sriov;
}
- err = mlx5_fpga_device_start(dev);
- if (err) {
- dev_err(&pdev->dev, "fpga device start failed %d\n", err);
- goto err_fpga_start;
- }
- err = mlx5_accel_ipsec_init(dev);
- if (err) {
- dev_err(&pdev->dev, "IPSec device start failed %d\n", err);
- goto err_ipsec_start;
- }
-
if (mlx5_device_registered(dev)) {
mlx5_attach_device(dev);
} else {
@@ -1219,17 +1225,18 @@ out:
return 0;
err_reg_dev:
- mlx5_accel_ipsec_cleanup(dev);
-err_ipsec_start:
- mlx5_fpga_device_stop(dev);
-
-err_fpga_start:
mlx5_sriov_detach(dev);
err_sriov:
mlx5_cleanup_fs(dev);
err_fs:
+ mlx5_accel_ipsec_cleanup(dev);
+
+err_ipsec_start:
+ mlx5_fpga_device_stop(dev);
+
+err_fpga_start:
mlx5_irq_clear_affinity_hints(dev);
err_affinity_hints:
@@ -1296,11 +1303,10 @@ static int mlx5_unload_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv,
if (mlx5_device_registered(dev))
mlx5_detach_device(dev);
- mlx5_accel_ipsec_cleanup(dev);
- mlx5_fpga_device_stop(dev);
-
mlx5_sriov_detach(dev);
mlx5_cleanup_fs(dev);
+ mlx5_accel_ipsec_cleanup(dev);
+ mlx5_fpga_device_stop(dev);
mlx5_irq_clear_affinity_hints(dev);
free_comp_eqs(dev);
mlx5_stop_eqs(dev);
@@ -1657,6 +1663,7 @@ static int __init init(void)
get_random_bytes(&sw_owner_id, sizeof(sw_owner_id));
mlx5_core_verify_params();
+ mlx5_fpga_ipsec_build_fs_cmds();
mlx5_register_debugfs();
err = pci_register_driver(&mlx5_core_driver);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
index 394552f36fcf..7d001fe6e631 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
@@ -38,16 +38,11 @@
#include <linux/sched.h>
#include <linux/if_link.h>
#include <linux/firmware.h>
+#include <linux/mlx5/cq.h>
#define DRIVER_NAME "mlx5_core"
#define DRIVER_VERSION "5.0-0"
-#define MLX5_TOTAL_VPORTS(mdev) (1 + pci_sriov_get_totalvfs(mdev->pdev))
-#define MLX5_VPORT_MANAGER(mdev) \
- (MLX5_CAP_GEN(mdev, vport_group_manager) && \
- (MLX5_CAP_GEN(mdev, port_type) == MLX5_CAP_PORT_TYPE_ETH) && \
- mlx5_core_is_pf(mdev))
-
extern uint mlx5_core_debug_mask;
#define mlx5_core_dbg(__dev, format, ...) \
@@ -55,6 +50,11 @@ extern uint mlx5_core_debug_mask;
__func__, __LINE__, current->pid, \
##__VA_ARGS__)
+#define mlx5_core_dbg_once(__dev, format, ...) \
+ dev_dbg_once(&(__dev)->pdev->dev, "%s:%d:(pid %d): " format, \
+ __func__, __LINE__, current->pid, \
+ ##__VA_ARGS__)
+
#define mlx5_core_dbg_mask(__dev, mask, format, ...) \
do { \
if ((mask) & mlx5_core_debug_mask) \
@@ -115,9 +115,29 @@ int mlx5_destroy_scheduling_element_cmd(struct mlx5_core_dev *dev, u8 hierarchy,
u32 element_id);
int mlx5_wait_for_vf_pages(struct mlx5_core_dev *dev);
u64 mlx5_read_internal_timer(struct mlx5_core_dev *dev);
+
+int mlx5_eq_init(struct mlx5_core_dev *dev);
+void mlx5_eq_cleanup(struct mlx5_core_dev *dev);
+int mlx5_create_map_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq, u8 vecidx,
+ int nent, u64 mask, const char *name,
+ enum mlx5_eq_type type);
+int mlx5_destroy_unmap_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq);
+int mlx5_eq_add_cq(struct mlx5_eq *eq, struct mlx5_core_cq *cq);
+int mlx5_eq_del_cq(struct mlx5_eq *eq, struct mlx5_core_cq *cq);
+int mlx5_core_eq_query(struct mlx5_core_dev *dev, struct mlx5_eq *eq,
+ u32 *out, int outlen);
+int mlx5_start_eqs(struct mlx5_core_dev *dev);
+void mlx5_stop_eqs(struct mlx5_core_dev *dev);
struct mlx5_eq *mlx5_eqn2eq(struct mlx5_core_dev *dev, int eqn);
u32 mlx5_eq_poll_irq_disabled(struct mlx5_eq *eq);
void mlx5_cq_tasklet_cb(unsigned long data);
+void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec, bool forced);
+int mlx5_debug_eq_add(struct mlx5_core_dev *dev, struct mlx5_eq *eq);
+void mlx5_debug_eq_remove(struct mlx5_core_dev *dev, struct mlx5_eq *eq);
+int mlx5_eq_debugfs_init(struct mlx5_core_dev *dev);
+void mlx5_eq_debugfs_cleanup(struct mlx5_core_dev *dev);
+int mlx5_cq_debugfs_init(struct mlx5_core_dev *dev);
+void mlx5_cq_debugfs_cleanup(struct mlx5_core_dev *dev);
int mlx5_query_pcam_reg(struct mlx5_core_dev *dev, u32 *pcam, u8 feature_group,
u8 access_reg_group);
@@ -186,4 +206,5 @@ static inline int mlx5_lag_is_lacp_owner(struct mlx5_core_dev *dev)
int mlx5_lag_allow(struct mlx5_core_dev *dev);
int mlx5_lag_forbid(struct mlx5_core_dev *dev);
+void mlx5_reload_interface(struct mlx5_core_dev *mdev, int protocol);
#endif /* __MLX5_CORE_H__ */
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/port.c b/drivers/net/ethernet/mellanox/mlx5/core/port.c
index c37d00cd472a..fa9d0760dd36 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/port.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/port.c
@@ -483,6 +483,17 @@ int mlx5_core_query_ib_ppcnt(struct mlx5_core_dev *dev,
}
EXPORT_SYMBOL_GPL(mlx5_core_query_ib_ppcnt);
+static int mlx5_query_pfcc_reg(struct mlx5_core_dev *dev, u32 *out,
+ u32 out_size)
+{
+ u32 in[MLX5_ST_SZ_DW(pfcc_reg)] = {0};
+
+ MLX5_SET(pfcc_reg, in, local_port, 1);
+
+ return mlx5_core_access_reg(dev, in, sizeof(in), out,
+ out_size, MLX5_REG_PFCC, 0, 0);
+}
+
int mlx5_set_port_pause(struct mlx5_core_dev *dev, u32 rx_pause, u32 tx_pause)
{
u32 in[MLX5_ST_SZ_DW(pfcc_reg)] = {0};
@@ -500,13 +511,10 @@ EXPORT_SYMBOL_GPL(mlx5_set_port_pause);
int mlx5_query_port_pause(struct mlx5_core_dev *dev,
u32 *rx_pause, u32 *tx_pause)
{
- u32 in[MLX5_ST_SZ_DW(pfcc_reg)] = {0};
u32 out[MLX5_ST_SZ_DW(pfcc_reg)];
int err;
- MLX5_SET(pfcc_reg, in, local_port, 1);
- err = mlx5_core_access_reg(dev, in, sizeof(in), out,
- sizeof(out), MLX5_REG_PFCC, 0, 0);
+ err = mlx5_query_pfcc_reg(dev, out, sizeof(out));
if (err)
return err;
@@ -520,6 +528,49 @@ int mlx5_query_port_pause(struct mlx5_core_dev *dev,
}
EXPORT_SYMBOL_GPL(mlx5_query_port_pause);
+int mlx5_set_port_stall_watermark(struct mlx5_core_dev *dev,
+ u16 stall_critical_watermark,
+ u16 stall_minor_watermark)
+{
+ u32 in[MLX5_ST_SZ_DW(pfcc_reg)] = {0};
+ u32 out[MLX5_ST_SZ_DW(pfcc_reg)];
+
+ MLX5_SET(pfcc_reg, in, local_port, 1);
+ MLX5_SET(pfcc_reg, in, pptx_mask_n, 1);
+ MLX5_SET(pfcc_reg, in, pprx_mask_n, 1);
+ MLX5_SET(pfcc_reg, in, ppan_mask_n, 1);
+ MLX5_SET(pfcc_reg, in, critical_stall_mask, 1);
+ MLX5_SET(pfcc_reg, in, minor_stall_mask, 1);
+ MLX5_SET(pfcc_reg, in, device_stall_critical_watermark,
+ stall_critical_watermark);
+ MLX5_SET(pfcc_reg, in, device_stall_minor_watermark, stall_minor_watermark);
+
+ return mlx5_core_access_reg(dev, in, sizeof(in), out,
+ sizeof(out), MLX5_REG_PFCC, 0, 1);
+}
+
+int mlx5_query_port_stall_watermark(struct mlx5_core_dev *dev,
+ u16 *stall_critical_watermark,
+ u16 *stall_minor_watermark)
+{
+ u32 out[MLX5_ST_SZ_DW(pfcc_reg)];
+ int err;
+
+ err = mlx5_query_pfcc_reg(dev, out, sizeof(out));
+ if (err)
+ return err;
+
+ if (stall_critical_watermark)
+ *stall_critical_watermark = MLX5_GET(pfcc_reg, out,
+ device_stall_critical_watermark);
+
+ if (stall_minor_watermark)
+ *stall_minor_watermark = MLX5_GET(pfcc_reg, out,
+ device_stall_minor_watermark);
+
+ return 0;
+}
+
int mlx5_set_port_pfc(struct mlx5_core_dev *dev, u8 pfc_en_tx, u8 pfc_en_rx)
{
u32 in[MLX5_ST_SZ_DW(pfcc_reg)] = {0};
@@ -538,13 +589,10 @@ EXPORT_SYMBOL_GPL(mlx5_set_port_pfc);
int mlx5_query_port_pfc(struct mlx5_core_dev *dev, u8 *pfc_en_tx, u8 *pfc_en_rx)
{
- u32 in[MLX5_ST_SZ_DW(pfcc_reg)] = {0};
u32 out[MLX5_ST_SZ_DW(pfcc_reg)];
int err;
- MLX5_SET(pfcc_reg, in, local_port, 1);
- err = mlx5_core_access_reg(dev, in, sizeof(in), out,
- sizeof(out), MLX5_REG_PFCC, 0, 0);
+ err = mlx5_query_pfcc_reg(dev, out, sizeof(out));
if (err)
return err;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/rl.c b/drivers/net/ethernet/mellanox/mlx5/core/rl.c
index d3c33e9eea72..bc86dffdc43c 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/rl.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/rl.c
@@ -107,16 +107,16 @@ int mlx5_destroy_scheduling_element_cmd(struct mlx5_core_dev *dev, u8 hierarchy,
* If the table is full, return NULL
*/
static struct mlx5_rl_entry *find_rl_entry(struct mlx5_rl_table *table,
- u32 rate)
+ struct mlx5_rate_limit *rl)
{
struct mlx5_rl_entry *ret_entry = NULL;
bool empty_found = false;
int i;
for (i = 0; i < table->max_size; i++) {
- if (table->rl_entry[i].rate == rate)
+ if (mlx5_rl_are_equal(&table->rl_entry[i].rl, rl))
return &table->rl_entry[i];
- if (!empty_found && !table->rl_entry[i].rate) {
+ if (!empty_found && !table->rl_entry[i].rl.rate) {
empty_found = true;
ret_entry = &table->rl_entry[i];
}
@@ -126,7 +126,8 @@ static struct mlx5_rl_entry *find_rl_entry(struct mlx5_rl_table *table,
}
static int mlx5_set_pp_rate_limit_cmd(struct mlx5_core_dev *dev,
- u32 rate, u16 index)
+ u16 index,
+ struct mlx5_rate_limit *rl)
{
u32 in[MLX5_ST_SZ_DW(set_pp_rate_limit_in)] = {0};
u32 out[MLX5_ST_SZ_DW(set_pp_rate_limit_out)] = {0};
@@ -134,7 +135,9 @@ static int mlx5_set_pp_rate_limit_cmd(struct mlx5_core_dev *dev,
MLX5_SET(set_pp_rate_limit_in, in, opcode,
MLX5_CMD_OP_SET_PP_RATE_LIMIT);
MLX5_SET(set_pp_rate_limit_in, in, rate_limit_index, index);
- MLX5_SET(set_pp_rate_limit_in, in, rate_limit, rate);
+ MLX5_SET(set_pp_rate_limit_in, in, rate_limit, rl->rate);
+ MLX5_SET(set_pp_rate_limit_in, in, burst_upper_bound, rl->max_burst_sz);
+ MLX5_SET(set_pp_rate_limit_in, in, typical_packet_size, rl->typical_pkt_sz);
return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
}
@@ -146,7 +149,17 @@ bool mlx5_rl_is_in_range(struct mlx5_core_dev *dev, u32 rate)
}
EXPORT_SYMBOL(mlx5_rl_is_in_range);
-int mlx5_rl_add_rate(struct mlx5_core_dev *dev, u32 rate, u16 *index)
+bool mlx5_rl_are_equal(struct mlx5_rate_limit *rl_0,
+ struct mlx5_rate_limit *rl_1)
+{
+ return ((rl_0->rate == rl_1->rate) &&
+ (rl_0->max_burst_sz == rl_1->max_burst_sz) &&
+ (rl_0->typical_pkt_sz == rl_1->typical_pkt_sz));
+}
+EXPORT_SYMBOL(mlx5_rl_are_equal);
+
+int mlx5_rl_add_rate(struct mlx5_core_dev *dev, u16 *index,
+ struct mlx5_rate_limit *rl)
{
struct mlx5_rl_table *table = &dev->priv.rl_table;
struct mlx5_rl_entry *entry;
@@ -154,14 +167,14 @@ int mlx5_rl_add_rate(struct mlx5_core_dev *dev, u32 rate, u16 *index)
mutex_lock(&table->rl_lock);
- if (!rate || !mlx5_rl_is_in_range(dev, rate)) {
+ if (!rl->rate || !mlx5_rl_is_in_range(dev, rl->rate)) {
mlx5_core_err(dev, "Invalid rate: %u, should be %u to %u\n",
- rate, table->min_rate, table->max_rate);
+ rl->rate, table->min_rate, table->max_rate);
err = -EINVAL;
goto out;
}
- entry = find_rl_entry(table, rate);
+ entry = find_rl_entry(table, rl);
if (!entry) {
mlx5_core_err(dev, "Max number of %u rates reached\n",
table->max_size);
@@ -173,13 +186,15 @@ int mlx5_rl_add_rate(struct mlx5_core_dev *dev, u32 rate, u16 *index)
entry->refcount++;
} else {
/* new rate limit */
- err = mlx5_set_pp_rate_limit_cmd(dev, rate, entry->index);
+ err = mlx5_set_pp_rate_limit_cmd(dev, entry->index, rl);
if (err) {
- mlx5_core_err(dev, "Failed configuring rate: %u (%d)\n",
- rate, err);
+ mlx5_core_err(dev, "Failed configuring rate limit(err %d): \
+ rate %u, max_burst_sz %u, typical_pkt_sz %u\n",
+ err, rl->rate, rl->max_burst_sz,
+ rl->typical_pkt_sz);
goto out;
}
- entry->rate = rate;
+ entry->rl = *rl;
entry->refcount = 1;
}
*index = entry->index;
@@ -190,27 +205,30 @@ out:
}
EXPORT_SYMBOL(mlx5_rl_add_rate);
-void mlx5_rl_remove_rate(struct mlx5_core_dev *dev, u32 rate)
+void mlx5_rl_remove_rate(struct mlx5_core_dev *dev, struct mlx5_rate_limit *rl)
{
struct mlx5_rl_table *table = &dev->priv.rl_table;
struct mlx5_rl_entry *entry = NULL;
+ struct mlx5_rate_limit reset_rl = {0};
/* 0 is a reserved value for unlimited rate */
- if (rate == 0)
+ if (rl->rate == 0)
return;
mutex_lock(&table->rl_lock);
- entry = find_rl_entry(table, rate);
+ entry = find_rl_entry(table, rl);
if (!entry || !entry->refcount) {
- mlx5_core_warn(dev, "Rate %u is not configured\n", rate);
+ mlx5_core_warn(dev, "Rate %u, max_burst_sz %u typical_pkt_sz %u \
+ are not configured\n",
+ rl->rate, rl->max_burst_sz, rl->typical_pkt_sz);
goto out;
}
entry->refcount--;
if (!entry->refcount) {
/* need to remove rate */
- mlx5_set_pp_rate_limit_cmd(dev, 0, entry->index);
- entry->rate = 0;
+ mlx5_set_pp_rate_limit_cmd(dev, entry->index, &reset_rl);
+ entry->rl = reset_rl;
}
out:
@@ -257,13 +275,14 @@ int mlx5_init_rl_table(struct mlx5_core_dev *dev)
void mlx5_cleanup_rl_table(struct mlx5_core_dev *dev)
{
struct mlx5_rl_table *table = &dev->priv.rl_table;
+ struct mlx5_rate_limit rl = {0};
int i;
/* Clear all configured rates */
for (i = 0; i < table->max_size; i++)
- if (table->rl_entry[i].rate)
- mlx5_set_pp_rate_limit_cmd(dev, 0,
- table->rl_entry[i].index);
+ if (table->rl_entry[i].rl.rate)
+ mlx5_set_pp_rate_limit_cmd(dev, table->rl_entry[i].index,
+ &rl);
kfree(dev->priv.rl_table.rl_entry);
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/transobj.c b/drivers/net/ethernet/mellanox/mlx5/core/transobj.c
index 9e38343a951f..dae1c5c5d27c 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/transobj.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/transobj.c
@@ -157,6 +157,31 @@ int mlx5_core_query_sq(struct mlx5_core_dev *dev, u32 sqn, u32 *out)
}
EXPORT_SYMBOL(mlx5_core_query_sq);
+int mlx5_core_query_sq_state(struct mlx5_core_dev *dev, u32 sqn, u8 *state)
+{
+ void *out;
+ void *sqc;
+ int inlen;
+ int err;
+
+ inlen = MLX5_ST_SZ_BYTES(query_sq_out);
+ out = kvzalloc(inlen, GFP_KERNEL);
+ if (!out)
+ return -ENOMEM;
+
+ err = mlx5_core_query_sq(dev, sqn, out);
+ if (err)
+ goto out;
+
+ sqc = MLX5_ADDR_OF(query_sq_out, out, sq_context);
+ *state = MLX5_GET(sqc, sqc, state);
+
+out:
+ kvfree(out);
+ return err;
+}
+EXPORT_SYMBOL_GPL(mlx5_core_query_sq_state);
+
int mlx5_core_create_tir(struct mlx5_core_dev *dev, u32 *in, int inlen,
u32 *tirn)
{
@@ -329,27 +354,6 @@ int mlx5_core_destroy_xsrq(struct mlx5_core_dev *dev, u32 xsrqn)
return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
}
-int mlx5_core_query_xsrq(struct mlx5_core_dev *dev, u32 xsrqn, u32 *out)
-{
- u32 in[MLX5_ST_SZ_DW(query_xrc_srq_in)] = {0};
- void *srqc;
- void *xrc_srqc;
- int err;
-
- MLX5_SET(query_xrc_srq_in, in, opcode, MLX5_CMD_OP_QUERY_XRC_SRQ);
- MLX5_SET(query_xrc_srq_in, in, xrc_srqn, xsrqn);
- err = mlx5_cmd_exec(dev, in, sizeof(in), out,
- MLX5_ST_SZ_BYTES(query_xrc_srq_out));
- if (!err) {
- xrc_srqc = MLX5_ADDR_OF(query_xrc_srq_out, out,
- xrc_srq_context_entry);
- srqc = MLX5_ADDR_OF(query_srq_out, out, srq_context_entry);
- memcpy(srqc, xrc_srqc, MLX5_ST_SZ_BYTES(srqc));
- }
-
- return err;
-}
-
int mlx5_core_arm_xsrq(struct mlx5_core_dev *dev, u32 xsrqn, u16 lwm)
{
u32 in[MLX5_ST_SZ_DW(arm_xrc_srq_in)] = {0};
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vport.c b/drivers/net/ethernet/mellanox/mlx5/core/vport.c
index dfe36cf6fbea..177e076b8d17 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/vport.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/vport.c
@@ -1070,6 +1070,32 @@ free:
}
EXPORT_SYMBOL_GPL(mlx5_core_query_vport_counter);
+int mlx5_query_vport_down_stats(struct mlx5_core_dev *mdev, u16 vport,
+ u64 *rx_discard_vport_down,
+ u64 *tx_discard_vport_down)
+{
+ u32 out[MLX5_ST_SZ_DW(query_vnic_env_out)] = {0};
+ u32 in[MLX5_ST_SZ_DW(query_vnic_env_in)] = {0};
+ int err;
+
+ MLX5_SET(query_vnic_env_in, in, opcode,
+ MLX5_CMD_OP_QUERY_VNIC_ENV);
+ MLX5_SET(query_vnic_env_in, in, op_mod, 0);
+ MLX5_SET(query_vnic_env_in, in, vport_number, vport);
+ if (vport)
+ MLX5_SET(query_vnic_env_in, in, other_vport, 1);
+
+ err = mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
+ if (err)
+ return err;
+
+ *rx_discard_vport_down = MLX5_GET64(query_vnic_env_out, out,
+ vport_env.receive_discard_vport_down);
+ *tx_discard_vport_down = MLX5_GET64(query_vnic_env_out, out,
+ vport_env.transmit_discard_vport_down);
+ return 0;
+}
+
int mlx5_core_modify_hca_vport_context(struct mlx5_core_dev *dev,
u8 other_vport, u8 port_num,
int vf,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/wq.c b/drivers/net/ethernet/mellanox/mlx5/core/wq.c
index 6bcfc25350f5..ea66448ba365 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/wq.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/wq.c
@@ -41,7 +41,7 @@ u32 mlx5_wq_cyc_get_size(struct mlx5_wq_cyc *wq)
u32 mlx5_cqwq_get_size(struct mlx5_cqwq *wq)
{
- return wq->sz_m1 + 1;
+ return wq->fbc.sz_m1 + 1;
}
u32 mlx5_wq_ll_get_size(struct mlx5_wq_ll *wq)
@@ -62,7 +62,7 @@ static u32 mlx5_wq_qp_get_byte_size(struct mlx5_wq_qp *wq)
static u32 mlx5_cqwq_get_byte_size(struct mlx5_cqwq *wq)
{
- return mlx5_cqwq_get_size(wq) << wq->log_stride;
+ return mlx5_cqwq_get_size(wq) << wq->fbc.log_stride;
}
static u32 mlx5_wq_ll_get_byte_size(struct mlx5_wq_ll *wq)
@@ -92,7 +92,7 @@ int mlx5_wq_cyc_create(struct mlx5_core_dev *mdev, struct mlx5_wq_param *param,
goto err_db_free;
}
- wq->buf = wq_ctrl->buf.direct.buf;
+ wq->buf = wq_ctrl->buf.frags->buf;
wq->db = wq_ctrl->db.db;
wq_ctrl->mdev = mdev;
@@ -130,7 +130,7 @@ int mlx5_wq_qp_create(struct mlx5_core_dev *mdev, struct mlx5_wq_param *param,
goto err_db_free;
}
- wq->rq.buf = wq_ctrl->buf.direct.buf;
+ wq->rq.buf = wq_ctrl->buf.frags->buf;
wq->sq.buf = wq->rq.buf + mlx5_wq_cyc_get_byte_size(&wq->rq);
wq->rq.db = &wq_ctrl->db.db[MLX5_RCV_DBR];
wq->sq.db = &wq_ctrl->db.db[MLX5_SND_DBR];
@@ -151,11 +151,7 @@ int mlx5_cqwq_create(struct mlx5_core_dev *mdev, struct mlx5_wq_param *param,
{
int err;
- wq->log_stride = 6 + MLX5_GET(cqc, cqc, cqe_sz);
- wq->log_sz = MLX5_GET(cqc, cqc, log_cq_size);
- wq->sz_m1 = (1 << wq->log_sz) - 1;
- wq->log_frag_strides = PAGE_SHIFT - wq->log_stride;
- wq->frag_sz_m1 = (1 << wq->log_frag_strides) - 1;
+ mlx5_core_init_cq_frag_buf(&wq->fbc, cqc);
err = mlx5_db_alloc_node(mdev, &wq_ctrl->db, param->db_numa_node);
if (err) {
@@ -172,7 +168,7 @@ int mlx5_cqwq_create(struct mlx5_core_dev *mdev, struct mlx5_wq_param *param,
goto err_db_free;
}
- wq->frag_buf = wq_ctrl->frag_buf;
+ wq->fbc.frag_buf = wq_ctrl->frag_buf;
wq->db = wq_ctrl->db.db;
wq_ctrl->mdev = mdev;
@@ -209,7 +205,7 @@ int mlx5_wq_ll_create(struct mlx5_core_dev *mdev, struct mlx5_wq_param *param,
goto err_db_free;
}
- wq->buf = wq_ctrl->buf.direct.buf;
+ wq->buf = wq_ctrl->buf.frags->buf;
wq->db = wq_ctrl->db.db;
for (i = 0; i < wq->sz_m1; i++) {
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/wq.h b/drivers/net/ethernet/mellanox/mlx5/core/wq.h
index 718589d0cec2..fca90b94596d 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/wq.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/wq.h
@@ -45,7 +45,7 @@ struct mlx5_wq_param {
struct mlx5_wq_ctrl {
struct mlx5_core_dev *mdev;
- struct mlx5_buf buf;
+ struct mlx5_frag_buf buf;
struct mlx5_db db;
};
@@ -68,14 +68,9 @@ struct mlx5_wq_qp {
};
struct mlx5_cqwq {
- struct mlx5_frag_buf frag_buf;
- __be32 *db;
- u32 sz_m1;
- u32 frag_sz_m1;
- u32 cc; /* consumer counter */
- u8 log_sz;
- u8 log_stride;
- u8 log_frag_strides;
+ struct mlx5_frag_buf_ctrl fbc;
+ __be32 *db;
+ u32 cc; /* consumer counter */
};
struct mlx5_wq_ll {
@@ -131,20 +126,17 @@ static inline int mlx5_wq_cyc_cc_bigger(u16 cc1, u16 cc2)
static inline u32 mlx5_cqwq_get_ci(struct mlx5_cqwq *wq)
{
- return wq->cc & wq->sz_m1;
+ return wq->cc & wq->fbc.sz_m1;
}
static inline void *mlx5_cqwq_get_wqe(struct mlx5_cqwq *wq, u32 ix)
{
- unsigned int frag = (ix >> wq->log_frag_strides);
-
- return wq->frag_buf.frags[frag].buf +
- ((wq->frag_sz_m1 & ix) << wq->log_stride);
+ return mlx5_frag_buf_get_wqe(&wq->fbc, ix);
}
static inline u32 mlx5_cqwq_get_wrap_cnt(struct mlx5_cqwq *wq)
{
- return wq->cc >> wq->log_sz;
+ return wq->cc >> wq->fbc.log_sz;
}
static inline void mlx5_cqwq_pop(struct mlx5_cqwq *wq)
diff --git a/drivers/net/ethernet/mellanox/mlxsw/Kconfig b/drivers/net/ethernet/mellanox/mlxsw/Kconfig
index d56eea310509..f4d9c9975ac3 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/Kconfig
+++ b/drivers/net/ethernet/mellanox/mlxsw/Kconfig
@@ -76,6 +76,8 @@ config MLXSW_SPECTRUM
depends on PSAMPLE || PSAMPLE=n
depends on BRIDGE || BRIDGE=n
depends on IPV6 || IPV6=n
+ depends on NET_IPGRE || NET_IPGRE=n
+ depends on IPV6_GRE || IPV6_GRE=n
select PARMAN
select MLXFW
default m
diff --git a/drivers/net/ethernet/mellanox/mlxsw/Makefile b/drivers/net/ethernet/mellanox/mlxsw/Makefile
index 9463c3fa254f..0cadcabfe86f 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/Makefile
+++ b/drivers/net/ethernet/mellanox/mlxsw/Makefile
@@ -20,7 +20,7 @@ mlxsw_spectrum-objs := spectrum.o spectrum_buffers.o \
spectrum_cnt.o spectrum_fid.o \
spectrum_ipip.o spectrum_acl_flex_actions.o \
spectrum_mr.o spectrum_mr_tcam.o \
- spectrum_qdisc.o
+ spectrum_qdisc.o spectrum_span.o
mlxsw_spectrum-$(CONFIG_MLXSW_SPECTRUM_DCB) += spectrum_dcb.o
mlxsw_spectrum-$(CONFIG_NET_DEVLINK) += spectrum_dpipe.o
obj-$(CONFIG_MLXSW_MINIMAL) += mlxsw_minimal.o
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c
index 3529b545675d..93ea56620a24 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/core.c
@@ -1008,6 +1008,7 @@ int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
const char *device_kind = mlxsw_bus_info->device_kind;
struct mlxsw_core *mlxsw_core;
struct mlxsw_driver *mlxsw_driver;
+ struct mlxsw_res *res;
size_t alloc_size;
int err;
@@ -1032,8 +1033,8 @@ int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
mlxsw_core->bus_priv = bus_priv;
mlxsw_core->bus_info = mlxsw_bus_info;
- err = mlxsw_bus->init(bus_priv, mlxsw_core, mlxsw_driver->profile,
- &mlxsw_core->res);
+ res = mlxsw_driver->res_query_enabled ? &mlxsw_core->res : NULL;
+ err = mlxsw_bus->init(bus_priv, mlxsw_core, mlxsw_driver->profile, res);
if (err)
goto err_bus_init;
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h
index 5ddafd74dc00..092d39399f3c 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/core.h
@@ -235,8 +235,7 @@ struct mlxsw_config_profile {
used_max_pkey:1,
used_ar_sec:1,
used_adaptive_routing_group_cap:1,
- used_kvd_split_data:1; /* indicate for the kvd's values */
-
+ used_kvd_sizes:1;
u8 max_vepa_channels;
u16 max_mid;
u16 max_pgt;
@@ -256,10 +255,8 @@ struct mlxsw_config_profile {
u16 adaptive_routing_group_cap;
u8 arn;
u32 kvd_linear_size;
- u16 kvd_hash_granularity;
u8 kvd_hash_single_parts;
u8 kvd_hash_double_parts;
- u8 resource_query_enable;
struct mlxsw_swid_config swid_config[MLXSW_CONFIG_PROFILE_SWID_COUNT];
};
@@ -316,6 +313,7 @@ struct mlxsw_driver {
u64 *p_linear_size);
u8 txhdr_len;
const struct mlxsw_config_profile *profile;
+ bool res_query_enabled;
};
int mlxsw_core_kvd_sizes_get(struct mlxsw_core *mlxsw_core,
@@ -326,14 +324,14 @@ int mlxsw_core_kvd_sizes_get(struct mlxsw_core *mlxsw_core,
bool mlxsw_core_res_valid(struct mlxsw_core *mlxsw_core,
enum mlxsw_res_id res_id);
-#define MLXSW_CORE_RES_VALID(res, short_res_id) \
- mlxsw_core_res_valid(res, MLXSW_RES_ID_##short_res_id)
+#define MLXSW_CORE_RES_VALID(mlxsw_core, short_res_id) \
+ mlxsw_core_res_valid(mlxsw_core, MLXSW_RES_ID_##short_res_id)
u64 mlxsw_core_res_get(struct mlxsw_core *mlxsw_core,
enum mlxsw_res_id res_id);
-#define MLXSW_CORE_RES_GET(res, short_res_id) \
- mlxsw_core_res_get(res, MLXSW_RES_ID_##short_res_id)
+#define MLXSW_CORE_RES_GET(mlxsw_core, short_res_id) \
+ mlxsw_core_res_get(mlxsw_core, MLXSW_RES_ID_##short_res_id)
#define MLXSW_BUS_F_TXRX BIT(0)
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c
index 996dc099cd58..3c0d882ba183 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c
@@ -1,6 +1,6 @@
/*
* drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c
- * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2017, 2018 Mellanox Technologies. All rights reserved.
* Copyright (c) 2017 Jiri Pirko <jiri@mellanox.com>
*
* Redistribution and use in source and binary forms, with or without
@@ -849,7 +849,6 @@ struct mlxsw_afa_mirror {
struct mlxsw_afa_resource resource;
int span_id;
u8 local_in_port;
- u8 local_out_port;
bool ingress;
};
@@ -859,7 +858,7 @@ mlxsw_afa_mirror_destroy(struct mlxsw_afa_block *block,
{
block->afa->ops->mirror_del(block->afa->ops_priv,
mirror->local_in_port,
- mirror->local_out_port,
+ mirror->span_id,
mirror->ingress);
kfree(mirror);
}
@@ -875,9 +874,8 @@ mlxsw_afa_mirror_destructor(struct mlxsw_afa_block *block,
}
static struct mlxsw_afa_mirror *
-mlxsw_afa_mirror_create(struct mlxsw_afa_block *block,
- u8 local_in_port, u8 local_out_port,
- bool ingress)
+mlxsw_afa_mirror_create(struct mlxsw_afa_block *block, u8 local_in_port,
+ const struct net_device *out_dev, bool ingress)
{
struct mlxsw_afa_mirror *mirror;
int err;
@@ -887,13 +885,12 @@ mlxsw_afa_mirror_create(struct mlxsw_afa_block *block,
return ERR_PTR(-ENOMEM);
err = block->afa->ops->mirror_add(block->afa->ops_priv,
- local_in_port, local_out_port,
+ local_in_port, out_dev,
ingress, &mirror->span_id);
if (err)
goto err_mirror_add;
mirror->ingress = ingress;
- mirror->local_out_port = local_out_port;
mirror->local_in_port = local_in_port;
mirror->resource.destructor = mlxsw_afa_mirror_destructor;
mlxsw_afa_resource_add(block, &mirror->resource);
@@ -920,13 +917,13 @@ mlxsw_afa_block_append_allocated_mirror(struct mlxsw_afa_block *block,
}
int
-mlxsw_afa_block_append_mirror(struct mlxsw_afa_block *block,
- u8 local_in_port, u8 local_out_port, bool ingress)
+mlxsw_afa_block_append_mirror(struct mlxsw_afa_block *block, u8 local_in_port,
+ const struct net_device *out_dev, bool ingress)
{
struct mlxsw_afa_mirror *mirror;
int err;
- mirror = mlxsw_afa_mirror_create(block, local_in_port, local_out_port,
+ mirror = mlxsw_afa_mirror_create(block, local_in_port, out_dev,
ingress);
if (IS_ERR(mirror))
return PTR_ERR(mirror);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h
index b91f2b0829b0..3a155d104384 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h
@@ -36,6 +36,7 @@
#define _MLXSW_CORE_ACL_FLEX_ACTIONS_H
#include <linux/types.h>
+#include <linux/netdevice.h>
struct mlxsw_afa;
struct mlxsw_afa_block;
@@ -48,9 +49,10 @@ struct mlxsw_afa_ops {
void (*kvdl_fwd_entry_del)(void *priv, u32 kvdl_index);
int (*counter_index_get)(void *priv, unsigned int *p_counter_index);
void (*counter_index_put)(void *priv, unsigned int counter_index);
- int (*mirror_add)(void *priv, u8 locol_in_port, u8 local_out_port,
+ int (*mirror_add)(void *priv, u8 local_in_port,
+ const struct net_device *out_dev,
bool ingress, int *p_span_id);
- void (*mirror_del)(void *priv, u8 locol_in_port, u8 local_out_port,
+ void (*mirror_del)(void *priv, u8 local_in_port, int span_id,
bool ingress);
};
@@ -71,7 +73,8 @@ int mlxsw_afa_block_append_trap(struct mlxsw_afa_block *block, u16 trap_id);
int mlxsw_afa_block_append_trap_and_forward(struct mlxsw_afa_block *block,
u16 trap_id);
int mlxsw_afa_block_append_mirror(struct mlxsw_afa_block *block,
- u8 local_in_port, u8 local_out_port,
+ u8 local_in_port,
+ const struct net_device *out_dev,
bool ingress);
int mlxsw_afa_block_append_fwd(struct mlxsw_afa_block *block,
u8 local_port, bool in_port);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c
index ab710e37af99..84185f8dfbae 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c
@@ -218,32 +218,32 @@ static void mlxsw_hwmon_attr_add(struct mlxsw_hwmon *mlxsw_hwmon,
switch (attr_type) {
case MLXSW_HWMON_ATTR_TYPE_TEMP:
mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_temp_show;
- mlxsw_hwmon_attr->dev_attr.attr.mode = S_IRUGO;
+ mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
"temp%u_input", num + 1);
break;
case MLXSW_HWMON_ATTR_TYPE_TEMP_MAX:
mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_temp_max_show;
- mlxsw_hwmon_attr->dev_attr.attr.mode = S_IRUGO;
+ mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
"temp%u_highest", num + 1);
break;
case MLXSW_HWMON_ATTR_TYPE_TEMP_RST:
mlxsw_hwmon_attr->dev_attr.store = mlxsw_hwmon_temp_rst_store;
- mlxsw_hwmon_attr->dev_attr.attr.mode = S_IWUSR;
+ mlxsw_hwmon_attr->dev_attr.attr.mode = 0200;
snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
"temp%u_reset_history", num + 1);
break;
case MLXSW_HWMON_ATTR_TYPE_FAN_RPM:
mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_fan_rpm_show;
- mlxsw_hwmon_attr->dev_attr.attr.mode = S_IRUGO;
+ mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
"fan%u_input", num + 1);
break;
case MLXSW_HWMON_ATTR_TYPE_PWM:
mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_pwm_show;
mlxsw_hwmon_attr->dev_attr.store = mlxsw_hwmon_pwm_store;
- mlxsw_hwmon_attr->dev_attr.attr.mode = S_IWUSR | S_IRUGO;
+ mlxsw_hwmon_attr->dev_attr.attr.mode = 0644;
snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
"pwm%u", num + 1);
break;
diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c
index 85faa87bf42d..3a9381977d6d 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/pci.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c
@@ -1015,16 +1015,14 @@ mlxsw_pci_config_profile_swid_config(struct mlxsw_pci *mlxsw_pci,
}
static int mlxsw_pci_resources_query(struct mlxsw_pci *mlxsw_pci, char *mbox,
- struct mlxsw_res *res,
- u8 query_enabled)
+ struct mlxsw_res *res)
{
int index, i;
u64 data;
u16 id;
int err;
- /* Not all the versions support resources query */
- if (!query_enabled)
+ if (!res)
return 0;
mlxsw_cmd_mbox_zero(mbox);
@@ -1164,7 +1162,7 @@ static int mlxsw_pci_config_profile(struct mlxsw_pci *mlxsw_pci, char *mbox,
mlxsw_cmd_mbox_config_profile_adaptive_routing_group_cap_set(
mbox, profile->adaptive_routing_group_cap);
}
- if (MLXSW_RES_VALID(res, KVD_SIZE)) {
+ if (profile->used_kvd_sizes && MLXSW_RES_VALID(res, KVD_SIZE)) {
err = mlxsw_pci_profile_get_kvd_sizes(mlxsw_pci, profile, res);
if (err)
return err;
@@ -1376,8 +1374,7 @@ static int mlxsw_pci_init(void *bus_priv, struct mlxsw_core *mlxsw_core,
if (err)
goto err_boardinfo;
- err = mlxsw_pci_resources_query(mlxsw_pci, mbox, res,
- profile->resource_query_enable);
+ err = mlxsw_pci_resources_query(mlxsw_pci, mbox, res);
if (err)
goto err_query_resources;
@@ -1519,8 +1516,7 @@ static int mlxsw_pci_cmd_exec(void *bus_priv, u16 opcode, u8 opcode_mod,
u8 *p_status)
{
struct mlxsw_pci *mlxsw_pci = bus_priv;
- dma_addr_t in_mapaddr = mlxsw_pci->cmd.in_mbox.mapaddr;
- dma_addr_t out_mapaddr = mlxsw_pci->cmd.out_mbox.mapaddr;
+ dma_addr_t in_mapaddr = 0, out_mapaddr = 0;
bool evreq = mlxsw_pci->cmd.nopoll;
unsigned long timeout = msecs_to_jiffies(MLXSW_PCI_CIR_TIMEOUT_MSECS);
bool *p_wait_done = &mlxsw_pci->cmd.wait_done;
@@ -1532,11 +1528,15 @@ static int mlxsw_pci_cmd_exec(void *bus_priv, u16 opcode, u8 opcode_mod,
if (err)
return err;
- if (in_mbox)
+ if (in_mbox) {
memcpy(mlxsw_pci->cmd.in_mbox.buf, in_mbox, in_mbox_size);
+ in_mapaddr = mlxsw_pci->cmd.in_mbox.mapaddr;
+ }
mlxsw_pci_write32(mlxsw_pci, CIR_IN_PARAM_HI, upper_32_bits(in_mapaddr));
mlxsw_pci_write32(mlxsw_pci, CIR_IN_PARAM_LO, lower_32_bits(in_mapaddr));
+ if (out_mbox)
+ out_mapaddr = mlxsw_pci->cmd.out_mbox.mapaddr;
mlxsw_pci_write32(mlxsw_pci, CIR_OUT_PARAM_HI, upper_32_bits(out_mapaddr));
mlxsw_pci_write32(mlxsw_pci, CIR_OUT_PARAM_LO, lower_32_bits(out_mapaddr));
diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h
index 0e08be41c8e0..6218231e379e 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/reg.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h
@@ -1,11 +1,11 @@
/*
* drivers/net/ethernet/mellanox/mlxsw/reg.h
- * Copyright (c) 2015-2017 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2015-2018 Mellanox Technologies. All rights reserved.
* Copyright (c) 2015-2016 Ido Schimmel <idosch@mellanox.com>
* Copyright (c) 2015 Elad Raz <eladr@mellanox.com>
* Copyright (c) 2015-2017 Jiri Pirko <jiri@mellanox.com>
* Copyright (c) 2016 Yotam Gigi <yotamg@mellanox.com>
- * Copyright (c) 2017 Petr Machata <petrm@mellanox.com>
+ * Copyright (c) 2017-2018 Petr Machata <petrm@mellanox.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -2872,6 +2872,14 @@ static inline void mlxsw_reg_pmtu_pack(char *payload, u8 local_port,
MLXSW_REG_DEFINE(ptys, MLXSW_REG_PTYS_ID, MLXSW_REG_PTYS_LEN);
+/* an_disable_admin
+ * Auto negotiation disable administrative configuration
+ * 0 - Device doesn't support AN disable.
+ * 1 - Device supports AN disable.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, ptys, an_disable_admin, 0x00, 30, 1);
+
/* reg_ptys_local_port
* Local port number.
* Access: Index
@@ -3000,12 +3008,13 @@ MLXSW_ITEM32(reg, ptys, ib_proto_oper, 0x28, 0, 16);
MLXSW_ITEM32(reg, ptys, eth_proto_lp_advertise, 0x30, 0, 32);
static inline void mlxsw_reg_ptys_eth_pack(char *payload, u8 local_port,
- u32 proto_admin)
+ u32 proto_admin, bool autoneg)
{
MLXSW_REG_ZERO(ptys, payload);
mlxsw_reg_ptys_local_port_set(payload, local_port);
mlxsw_reg_ptys_proto_mask_set(payload, MLXSW_REG_PTYS_PROTO_MASK_ETH);
mlxsw_reg_ptys_eth_proto_admin_set(payload, proto_admin);
+ mlxsw_reg_ptys_an_disable_admin_set(payload, !autoneg);
}
static inline void mlxsw_reg_ptys_eth_unpack(char *payload,
@@ -4216,6 +4225,12 @@ MLXSW_ITEM32(reg, ritr, ipv6, 0x00, 28, 1);
*/
MLXSW_ITEM32(reg, ritr, ipv4_mc, 0x00, 27, 1);
+/* reg_ritr_ipv6_mc
+ * IPv6 multicast routing enable.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, ritr, ipv6_mc, 0x00, 26, 1);
+
enum mlxsw_reg_ritr_if_type {
/* VLAN interface. */
MLXSW_REG_RITR_VLAN_IF,
@@ -4281,6 +4296,14 @@ MLXSW_ITEM32(reg, ritr, ipv6_fe, 0x04, 28, 1);
*/
MLXSW_ITEM32(reg, ritr, ipv4_mc_fe, 0x04, 27, 1);
+/* reg_ritr_ipv6_mc_fe
+ * IPv6 Multicast Forwarding Enable.
+ * When disabled, forwarding is blocked but local traffic (traps and IP to me)
+ * will be enabled.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, ritr, ipv6_mc_fe, 0x04, 26, 1);
+
/* reg_ritr_lb_en
* Loop-back filter enable for unicast packets.
* If the flag is set then loop-back filter for unicast packets is
@@ -4504,12 +4527,14 @@ static inline void mlxsw_reg_ritr_pack(char *payload, bool enable,
mlxsw_reg_ritr_ipv4_set(payload, 1);
mlxsw_reg_ritr_ipv6_set(payload, 1);
mlxsw_reg_ritr_ipv4_mc_set(payload, 1);
+ mlxsw_reg_ritr_ipv6_mc_set(payload, 1);
mlxsw_reg_ritr_type_set(payload, type);
mlxsw_reg_ritr_op_set(payload, op);
mlxsw_reg_ritr_rif_set(payload, rif);
mlxsw_reg_ritr_ipv4_fe_set(payload, 1);
mlxsw_reg_ritr_ipv6_fe_set(payload, 1);
mlxsw_reg_ritr_ipv4_mc_fe_set(payload, 1);
+ mlxsw_reg_ritr_ipv6_mc_fe_set(payload, 1);
mlxsw_reg_ritr_lb_en_set(payload, 1);
mlxsw_reg_ritr_virtual_router_set(payload, vr_id);
mlxsw_reg_ritr_mtu_set(payload, mtu);
@@ -6293,30 +6318,34 @@ MLXSW_ITEM32(reg, rmft2, irif_mask, 0x08, 24, 1);
*/
MLXSW_ITEM32(reg, rmft2, irif, 0x08, 0, 16);
-/* reg_rmft2_dip4
- * Destination IPv4 address
+/* reg_rmft2_dip{4,6}
+ * Destination IPv4/6 address
* Access: RW
*/
+MLXSW_ITEM_BUF(reg, rmft2, dip6, 0x10, 16);
MLXSW_ITEM32(reg, rmft2, dip4, 0x1C, 0, 32);
-/* reg_rmft2_dip4_mask
+/* reg_rmft2_dip{4,6}_mask
* A bit that is set directs the TCAM to compare the corresponding bit in key. A
* bit that is clear directs the TCAM to ignore the corresponding bit in key.
* Access: RW
*/
+MLXSW_ITEM_BUF(reg, rmft2, dip6_mask, 0x20, 16);
MLXSW_ITEM32(reg, rmft2, dip4_mask, 0x2C, 0, 32);
-/* reg_rmft2_sip4
- * Source IPv4 address
+/* reg_rmft2_sip{4,6}
+ * Source IPv4/6 address
* Access: RW
*/
+MLXSW_ITEM_BUF(reg, rmft2, sip6, 0x30, 16);
MLXSW_ITEM32(reg, rmft2, sip4, 0x3C, 0, 32);
-/* reg_rmft2_sip4_mask
+/* reg_rmft2_sip{4,6}_mask
* A bit that is set directs the TCAM to compare the corresponding bit in key. A
* bit that is clear directs the TCAM to ignore the corresponding bit in key.
* Access: RW
*/
+MLXSW_ITEM_BUF(reg, rmft2, sip6_mask, 0x40, 16);
MLXSW_ITEM32(reg, rmft2, sip4_mask, 0x4C, 0, 32);
/* reg_rmft2_flexible_action_set
@@ -6334,26 +6363,52 @@ MLXSW_ITEM_BUF(reg, rmft2, flexible_action_set, 0x80,
MLXSW_REG_FLEX_ACTION_SET_LEN);
static inline void
-mlxsw_reg_rmft2_ipv4_pack(char *payload, bool v, u16 offset, u16 virtual_router,
- enum mlxsw_reg_rmft2_irif_mask irif_mask, u16 irif,
- u32 dip4, u32 dip4_mask, u32 sip4, u32 sip4_mask,
- const char *flexible_action_set)
+mlxsw_reg_rmft2_common_pack(char *payload, bool v, u16 offset,
+ u16 virtual_router,
+ enum mlxsw_reg_rmft2_irif_mask irif_mask, u16 irif,
+ const char *flex_action_set)
{
MLXSW_REG_ZERO(rmft2, payload);
mlxsw_reg_rmft2_v_set(payload, v);
- mlxsw_reg_rmft2_type_set(payload, MLXSW_REG_RMFT2_TYPE_IPV4);
mlxsw_reg_rmft2_op_set(payload, MLXSW_REG_RMFT2_OP_READ_WRITE);
mlxsw_reg_rmft2_offset_set(payload, offset);
mlxsw_reg_rmft2_virtual_router_set(payload, virtual_router);
mlxsw_reg_rmft2_irif_mask_set(payload, irif_mask);
mlxsw_reg_rmft2_irif_set(payload, irif);
+ if (flex_action_set)
+ mlxsw_reg_rmft2_flexible_action_set_memcpy_to(payload,
+ flex_action_set);
+}
+
+static inline void
+mlxsw_reg_rmft2_ipv4_pack(char *payload, bool v, u16 offset, u16 virtual_router,
+ enum mlxsw_reg_rmft2_irif_mask irif_mask, u16 irif,
+ u32 dip4, u32 dip4_mask, u32 sip4, u32 sip4_mask,
+ const char *flexible_action_set)
+{
+ mlxsw_reg_rmft2_common_pack(payload, v, offset, virtual_router,
+ irif_mask, irif, flexible_action_set);
+ mlxsw_reg_rmft2_type_set(payload, MLXSW_REG_RMFT2_TYPE_IPV4);
mlxsw_reg_rmft2_dip4_set(payload, dip4);
mlxsw_reg_rmft2_dip4_mask_set(payload, dip4_mask);
mlxsw_reg_rmft2_sip4_set(payload, sip4);
mlxsw_reg_rmft2_sip4_mask_set(payload, sip4_mask);
- if (flexible_action_set)
- mlxsw_reg_rmft2_flexible_action_set_memcpy_to(payload,
- flexible_action_set);
+}
+
+static inline void
+mlxsw_reg_rmft2_ipv6_pack(char *payload, bool v, u16 offset, u16 virtual_router,
+ enum mlxsw_reg_rmft2_irif_mask irif_mask, u16 irif,
+ struct in6_addr dip6, struct in6_addr dip6_mask,
+ struct in6_addr sip6, struct in6_addr sip6_mask,
+ const char *flexible_action_set)
+{
+ mlxsw_reg_rmft2_common_pack(payload, v, offset, virtual_router,
+ irif_mask, irif, flexible_action_set);
+ mlxsw_reg_rmft2_type_set(payload, MLXSW_REG_RMFT2_TYPE_IPV6);
+ mlxsw_reg_rmft2_dip6_memcpy_to(payload, (void *)&dip6);
+ mlxsw_reg_rmft2_dip6_mask_memcpy_to(payload, (void *)&dip6_mask);
+ mlxsw_reg_rmft2_sip6_memcpy_to(payload, (void *)&sip6);
+ mlxsw_reg_rmft2_sip6_mask_memcpy_to(payload, (void *)&sip6_mask);
}
/* MFCR - Management Fan Control Register
@@ -6772,8 +6827,104 @@ MLXSW_ITEM32(reg, mpat, qos, 0x04, 26, 1);
*/
MLXSW_ITEM32(reg, mpat, be, 0x04, 25, 1);
+enum mlxsw_reg_mpat_span_type {
+ /* Local SPAN Ethernet.
+ * The original packet is not encapsulated.
+ */
+ MLXSW_REG_MPAT_SPAN_TYPE_LOCAL_ETH = 0x0,
+
+ /* Encapsulated Remote SPAN Ethernet L3 GRE.
+ * The packet is encapsulated with GRE header.
+ */
+ MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3 = 0x3,
+};
+
+/* reg_mpat_span_type
+ * SPAN type.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, mpat, span_type, 0x04, 0, 4);
+
+/* Remote SPAN - Ethernet VLAN
+ * - - - - - - - - - - - - - -
+ */
+
+/* reg_mpat_eth_rspan_vid
+ * Encapsulation header VLAN ID.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, mpat, eth_rspan_vid, 0x18, 0, 12);
+
+/* Encapsulated Remote SPAN - Ethernet L2
+ * - - - - - - - - - - - - - - - - - - -
+ */
+
+enum mlxsw_reg_mpat_eth_rspan_version {
+ MLXSW_REG_MPAT_ETH_RSPAN_VERSION_NO_HEADER = 15,
+};
+
+/* reg_mpat_eth_rspan_version
+ * RSPAN mirror header version.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, mpat, eth_rspan_version, 0x10, 18, 4);
+
+/* reg_mpat_eth_rspan_mac
+ * Destination MAC address.
+ * Access: RW
+ */
+MLXSW_ITEM_BUF(reg, mpat, eth_rspan_mac, 0x12, 6);
+
+/* reg_mpat_eth_rspan_tp
+ * Tag Packet. Indicates whether the mirroring header should be VLAN tagged.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, mpat, eth_rspan_tp, 0x18, 16, 1);
+
+/* Encapsulated Remote SPAN - Ethernet L3
+ * - - - - - - - - - - - - - - - - - - -
+ */
+
+enum mlxsw_reg_mpat_eth_rspan_protocol {
+ MLXSW_REG_MPAT_ETH_RSPAN_PROTOCOL_IPV4,
+ MLXSW_REG_MPAT_ETH_RSPAN_PROTOCOL_IPV6,
+};
+
+/* reg_mpat_eth_rspan_protocol
+ * SPAN encapsulation protocol.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, mpat, eth_rspan_protocol, 0x18, 24, 4);
+
+/* reg_mpat_eth_rspan_ttl
+ * Encapsulation header Time-to-Live/HopLimit.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, mpat, eth_rspan_ttl, 0x1C, 4, 8);
+
+/* reg_mpat_eth_rspan_smac
+ * Source MAC address
+ * Access: RW
+ */
+MLXSW_ITEM_BUF(reg, mpat, eth_rspan_smac, 0x22, 6);
+
+/* reg_mpat_eth_rspan_dip*
+ * Destination IP address. The IP version is configured by protocol.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, mpat, eth_rspan_dip4, 0x4C, 0, 32);
+MLXSW_ITEM_BUF(reg, mpat, eth_rspan_dip6, 0x40, 16);
+
+/* reg_mpat_eth_rspan_sip*
+ * Source IP address. The IP version is configured by protocol.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, mpat, eth_rspan_sip4, 0x5C, 0, 32);
+MLXSW_ITEM_BUF(reg, mpat, eth_rspan_sip6, 0x50, 16);
+
static inline void mlxsw_reg_mpat_pack(char *payload, u8 pa_id,
- u16 system_port, bool e)
+ u16 system_port, bool e,
+ enum mlxsw_reg_mpat_span_type span_type)
{
MLXSW_REG_ZERO(mpat, payload);
mlxsw_reg_mpat_pa_id_set(payload, pa_id);
@@ -6781,6 +6932,49 @@ static inline void mlxsw_reg_mpat_pack(char *payload, u8 pa_id,
mlxsw_reg_mpat_e_set(payload, e);
mlxsw_reg_mpat_qos_set(payload, 1);
mlxsw_reg_mpat_be_set(payload, 1);
+ mlxsw_reg_mpat_span_type_set(payload, span_type);
+}
+
+static inline void mlxsw_reg_mpat_eth_rspan_pack(char *payload, u16 vid)
+{
+ mlxsw_reg_mpat_eth_rspan_vid_set(payload, vid);
+}
+
+static inline void
+mlxsw_reg_mpat_eth_rspan_l2_pack(char *payload,
+ enum mlxsw_reg_mpat_eth_rspan_version version,
+ const char *mac,
+ bool tp)
+{
+ mlxsw_reg_mpat_eth_rspan_version_set(payload, version);
+ mlxsw_reg_mpat_eth_rspan_mac_memcpy_to(payload, mac);
+ mlxsw_reg_mpat_eth_rspan_tp_set(payload, tp);
+}
+
+static inline void
+mlxsw_reg_mpat_eth_rspan_l3_ipv4_pack(char *payload, u8 ttl,
+ const char *smac,
+ u32 sip, u32 dip)
+{
+ mlxsw_reg_mpat_eth_rspan_ttl_set(payload, ttl);
+ mlxsw_reg_mpat_eth_rspan_smac_memcpy_to(payload, smac);
+ mlxsw_reg_mpat_eth_rspan_protocol_set(payload,
+ MLXSW_REG_MPAT_ETH_RSPAN_PROTOCOL_IPV4);
+ mlxsw_reg_mpat_eth_rspan_sip4_set(payload, sip);
+ mlxsw_reg_mpat_eth_rspan_dip4_set(payload, dip);
+}
+
+static inline void
+mlxsw_reg_mpat_eth_rspan_l3_ipv6_pack(char *payload, u8 ttl,
+ const char *smac,
+ struct in6_addr sip, struct in6_addr dip)
+{
+ mlxsw_reg_mpat_eth_rspan_ttl_set(payload, ttl);
+ mlxsw_reg_mpat_eth_rspan_smac_memcpy_to(payload, smac);
+ mlxsw_reg_mpat_eth_rspan_protocol_set(payload,
+ MLXSW_REG_MPAT_ETH_RSPAN_PROTOCOL_IPV6);
+ mlxsw_reg_mpat_eth_rspan_sip6_memcpy_to(payload, (void *)&sip);
+ mlxsw_reg_mpat_eth_rspan_dip6_memcpy_to(payload, (void *)&dip);
}
/* MPAR - Monitoring Port Analyzer Register
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
index bf400c75fcc8..ca38a30fbe91 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
@@ -1,6 +1,6 @@
/*
* drivers/net/ethernet/mellanox/mlxsw/spectrum.c
- * Copyright (c) 2015-2017 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2015-2018 Mellanox Technologies. All rights reserved.
* Copyright (c) 2015-2017 Jiri Pirko <jiri@mellanox.com>
* Copyright (c) 2015 Ido Schimmel <idosch@mellanox.com>
* Copyright (c) 2015 Elad Raz <eladr@mellanox.com>
@@ -71,11 +71,12 @@
#include "spectrum_cnt.h"
#include "spectrum_dpipe.h"
#include "spectrum_acl_flex_actions.h"
+#include "spectrum_span.h"
#include "../mlxfw/mlxfw.h"
#define MLXSW_FWREV_MAJOR 13
-#define MLXSW_FWREV_MINOR 1530
-#define MLXSW_FWREV_SUBMINOR 152
+#define MLXSW_FWREV_MINOR 1620
+#define MLXSW_FWREV_SUBMINOR 192
#define MLXSW_FWREV_MINOR_TO_BRANCH(minor) ((minor) / 100)
#define MLXSW_SP_FW_FILENAME \
@@ -487,347 +488,6 @@ static int mlxsw_sp_base_mac_get(struct mlxsw_sp *mlxsw_sp)
return 0;
}
-static int mlxsw_sp_span_init(struct mlxsw_sp *mlxsw_sp)
-{
- int i;
-
- if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_SPAN))
- return -EIO;
-
- mlxsw_sp->span.entries_count = MLXSW_CORE_RES_GET(mlxsw_sp->core,
- MAX_SPAN);
- mlxsw_sp->span.entries = kcalloc(mlxsw_sp->span.entries_count,
- sizeof(struct mlxsw_sp_span_entry),
- GFP_KERNEL);
- if (!mlxsw_sp->span.entries)
- return -ENOMEM;
-
- for (i = 0; i < mlxsw_sp->span.entries_count; i++)
- INIT_LIST_HEAD(&mlxsw_sp->span.entries[i].bound_ports_list);
-
- return 0;
-}
-
-static void mlxsw_sp_span_fini(struct mlxsw_sp *mlxsw_sp)
-{
- int i;
-
- for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
- struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span.entries[i];
-
- WARN_ON_ONCE(!list_empty(&curr->bound_ports_list));
- }
- kfree(mlxsw_sp->span.entries);
-}
-
-static struct mlxsw_sp_span_entry *
-mlxsw_sp_span_entry_create(struct mlxsw_sp_port *port)
-{
- struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
- struct mlxsw_sp_span_entry *span_entry;
- char mpat_pl[MLXSW_REG_MPAT_LEN];
- u8 local_port = port->local_port;
- int index;
- int i;
- int err;
-
- /* find a free entry to use */
- index = -1;
- for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
- if (!mlxsw_sp->span.entries[i].used) {
- index = i;
- span_entry = &mlxsw_sp->span.entries[i];
- break;
- }
- }
- if (index < 0)
- return NULL;
-
- /* create a new port analayzer entry for local_port */
- mlxsw_reg_mpat_pack(mpat_pl, index, local_port, true);
- err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
- if (err)
- return NULL;
-
- span_entry->used = true;
- span_entry->id = index;
- span_entry->ref_count = 1;
- span_entry->local_port = local_port;
- return span_entry;
-}
-
-static void mlxsw_sp_span_entry_destroy(struct mlxsw_sp *mlxsw_sp,
- struct mlxsw_sp_span_entry *span_entry)
-{
- u8 local_port = span_entry->local_port;
- char mpat_pl[MLXSW_REG_MPAT_LEN];
- int pa_id = span_entry->id;
-
- mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, false);
- mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
- span_entry->used = false;
-}
-
-struct mlxsw_sp_span_entry *
-mlxsw_sp_span_entry_find(struct mlxsw_sp *mlxsw_sp, u8 local_port)
-{
- int i;
-
- for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
- struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span.entries[i];
-
- if (curr->used && curr->local_port == local_port)
- return curr;
- }
- return NULL;
-}
-
-static struct mlxsw_sp_span_entry
-*mlxsw_sp_span_entry_get(struct mlxsw_sp_port *port)
-{
- struct mlxsw_sp_span_entry *span_entry;
-
- span_entry = mlxsw_sp_span_entry_find(port->mlxsw_sp,
- port->local_port);
- if (span_entry) {
- /* Already exists, just take a reference */
- span_entry->ref_count++;
- return span_entry;
- }
-
- return mlxsw_sp_span_entry_create(port);
-}
-
-static int mlxsw_sp_span_entry_put(struct mlxsw_sp *mlxsw_sp,
- struct mlxsw_sp_span_entry *span_entry)
-{
- WARN_ON(!span_entry->ref_count);
- if (--span_entry->ref_count == 0)
- mlxsw_sp_span_entry_destroy(mlxsw_sp, span_entry);
- return 0;
-}
-
-static bool mlxsw_sp_span_is_egress_mirror(struct mlxsw_sp_port *port)
-{
- struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
- struct mlxsw_sp_span_inspected_port *p;
- int i;
-
- for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
- struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span.entries[i];
-
- list_for_each_entry(p, &curr->bound_ports_list, list)
- if (p->local_port == port->local_port &&
- p->type == MLXSW_SP_SPAN_EGRESS)
- return true;
- }
-
- return false;
-}
-
-static int mlxsw_sp_span_mtu_to_buffsize(const struct mlxsw_sp *mlxsw_sp,
- int mtu)
-{
- return mlxsw_sp_bytes_cells(mlxsw_sp, mtu * 5 / 2) + 1;
-}
-
-static int mlxsw_sp_span_port_mtu_update(struct mlxsw_sp_port *port, u16 mtu)
-{
- struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
- char sbib_pl[MLXSW_REG_SBIB_LEN];
- int err;
-
- /* If port is egress mirrored, the shared buffer size should be
- * updated according to the mtu value
- */
- if (mlxsw_sp_span_is_egress_mirror(port)) {
- u32 buffsize = mlxsw_sp_span_mtu_to_buffsize(mlxsw_sp, mtu);
-
- mlxsw_reg_sbib_pack(sbib_pl, port->local_port, buffsize);
- err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbib), sbib_pl);
- if (err) {
- netdev_err(port->dev, "Could not update shared buffer for mirroring\n");
- return err;
- }
- }
-
- return 0;
-}
-
-static struct mlxsw_sp_span_inspected_port *
-mlxsw_sp_span_entry_bound_port_find(struct mlxsw_sp_span_entry *span_entry,
- enum mlxsw_sp_span_type type,
- struct mlxsw_sp_port *port,
- bool bind)
-{
- struct mlxsw_sp_span_inspected_port *p;
-
- list_for_each_entry(p, &span_entry->bound_ports_list, list)
- if (type == p->type &&
- port->local_port == p->local_port &&
- bind == p->bound)
- return p;
- return NULL;
-}
-
-static int
-mlxsw_sp_span_inspected_port_bind(struct mlxsw_sp_port *port,
- struct mlxsw_sp_span_entry *span_entry,
- enum mlxsw_sp_span_type type,
- bool bind)
-{
- struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
- char mpar_pl[MLXSW_REG_MPAR_LEN];
- int pa_id = span_entry->id;
-
- /* bind the port to the SPAN entry */
- mlxsw_reg_mpar_pack(mpar_pl, port->local_port,
- (enum mlxsw_reg_mpar_i_e) type, bind, pa_id);
- return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpar), mpar_pl);
-}
-
-static int
-mlxsw_sp_span_inspected_port_add(struct mlxsw_sp_port *port,
- struct mlxsw_sp_span_entry *span_entry,
- enum mlxsw_sp_span_type type,
- bool bind)
-{
- struct mlxsw_sp_span_inspected_port *inspected_port;
- struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
- char sbib_pl[MLXSW_REG_SBIB_LEN];
- int i;
- int err;
-
- /* A given (source port, direction) can only be bound to one analyzer,
- * so if a binding is requested, check for conflicts.
- */
- if (bind)
- for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
- struct mlxsw_sp_span_entry *curr =
- &mlxsw_sp->span.entries[i];
-
- if (mlxsw_sp_span_entry_bound_port_find(curr, type,
- port, bind))
- return -EEXIST;
- }
-
- /* if it is an egress SPAN, bind a shared buffer to it */
- if (type == MLXSW_SP_SPAN_EGRESS) {
- u32 buffsize = mlxsw_sp_span_mtu_to_buffsize(mlxsw_sp,
- port->dev->mtu);
-
- mlxsw_reg_sbib_pack(sbib_pl, port->local_port, buffsize);
- err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbib), sbib_pl);
- if (err) {
- netdev_err(port->dev, "Could not create shared buffer for mirroring\n");
- return err;
- }
- }
-
- if (bind) {
- err = mlxsw_sp_span_inspected_port_bind(port, span_entry, type,
- true);
- if (err)
- goto err_port_bind;
- }
-
- inspected_port = kzalloc(sizeof(*inspected_port), GFP_KERNEL);
- if (!inspected_port) {
- err = -ENOMEM;
- goto err_inspected_port_alloc;
- }
- inspected_port->local_port = port->local_port;
- inspected_port->type = type;
- inspected_port->bound = bind;
- list_add_tail(&inspected_port->list, &span_entry->bound_ports_list);
-
- return 0;
-
-err_inspected_port_alloc:
- if (bind)
- mlxsw_sp_span_inspected_port_bind(port, span_entry, type,
- false);
-err_port_bind:
- if (type == MLXSW_SP_SPAN_EGRESS) {
- mlxsw_reg_sbib_pack(sbib_pl, port->local_port, 0);
- mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbib), sbib_pl);
- }
- return err;
-}
-
-static void
-mlxsw_sp_span_inspected_port_del(struct mlxsw_sp_port *port,
- struct mlxsw_sp_span_entry *span_entry,
- enum mlxsw_sp_span_type type,
- bool bind)
-{
- struct mlxsw_sp_span_inspected_port *inspected_port;
- struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
- char sbib_pl[MLXSW_REG_SBIB_LEN];
-
- inspected_port = mlxsw_sp_span_entry_bound_port_find(span_entry, type,
- port, bind);
- if (!inspected_port)
- return;
-
- if (bind)
- mlxsw_sp_span_inspected_port_bind(port, span_entry, type,
- false);
- /* remove the SBIB buffer if it was egress SPAN */
- if (type == MLXSW_SP_SPAN_EGRESS) {
- mlxsw_reg_sbib_pack(sbib_pl, port->local_port, 0);
- mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbib), sbib_pl);
- }
-
- mlxsw_sp_span_entry_put(mlxsw_sp, span_entry);
-
- list_del(&inspected_port->list);
- kfree(inspected_port);
-}
-
-int mlxsw_sp_span_mirror_add(struct mlxsw_sp_port *from,
- struct mlxsw_sp_port *to,
- enum mlxsw_sp_span_type type, bool bind)
-{
- struct mlxsw_sp *mlxsw_sp = from->mlxsw_sp;
- struct mlxsw_sp_span_entry *span_entry;
- int err;
-
- span_entry = mlxsw_sp_span_entry_get(to);
- if (!span_entry)
- return -ENOENT;
-
- netdev_dbg(from->dev, "Adding inspected port to SPAN entry %d\n",
- span_entry->id);
-
- err = mlxsw_sp_span_inspected_port_add(from, span_entry, type, bind);
- if (err)
- goto err_port_bind;
-
- return 0;
-
-err_port_bind:
- mlxsw_sp_span_entry_put(mlxsw_sp, span_entry);
- return err;
-}
-
-void mlxsw_sp_span_mirror_del(struct mlxsw_sp_port *from, u8 destination_port,
- enum mlxsw_sp_span_type type, bool bind)
-{
- struct mlxsw_sp_span_entry *span_entry;
-
- span_entry = mlxsw_sp_span_entry_find(from->mlxsw_sp,
- destination_port);
- if (!span_entry) {
- netdev_err(from->dev, "no span entry found\n");
- return;
- }
-
- netdev_dbg(from->dev, "removing inspected port from SPAN entry %d\n",
- span_entry->id);
- mlxsw_sp_span_inspected_port_del(from, span_entry, type, bind);
-}
-
static int mlxsw_sp_port_sample_set(struct mlxsw_sp_port *mlxsw_sp_port,
bool enable, u32 rate)
{
@@ -1380,6 +1040,16 @@ mlxsw_sp_port_get_hw_xstats(struct net_device *dev,
xstats->tail_drop[i] =
mlxsw_reg_ppcnt_tc_no_buffer_discard_uc_get(ppcnt_pl);
}
+
+ for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
+ err = mlxsw_sp_port_get_stats_raw(dev, MLXSW_REG_PPCNT_PRIO_CNT,
+ i, ppcnt_pl);
+ if (err)
+ continue;
+
+ xstats->tx_packets[i] = mlxsw_reg_ppcnt_tx_frames_get(ppcnt_pl);
+ xstats->tx_bytes[i] = mlxsw_reg_ppcnt_tx_octets_get(ppcnt_pl);
+ }
}
static void update_stats_cache(struct work_struct *work)
@@ -1604,7 +1274,6 @@ mlxsw_sp_port_add_cls_matchall_mirror(struct mlxsw_sp_port *mlxsw_sp_port,
bool ingress)
{
enum mlxsw_sp_span_type span_type;
- struct mlxsw_sp_port *to_port;
struct net_device *to_dev;
to_dev = tcf_mirred_dev(a);
@@ -1613,17 +1282,10 @@ mlxsw_sp_port_add_cls_matchall_mirror(struct mlxsw_sp_port *mlxsw_sp_port,
return -EINVAL;
}
- if (!mlxsw_sp_port_dev_check(to_dev)) {
- netdev_err(mlxsw_sp_port->dev, "Cannot mirror to a non-spectrum port");
- return -EOPNOTSUPP;
- }
- to_port = netdev_priv(to_dev);
-
- mirror->to_local_port = to_port->local_port;
mirror->ingress = ingress;
span_type = ingress ? MLXSW_SP_SPAN_INGRESS : MLXSW_SP_SPAN_EGRESS;
- return mlxsw_sp_span_mirror_add(mlxsw_sp_port, to_port, span_type,
- true);
+ return mlxsw_sp_span_mirror_add(mlxsw_sp_port, to_dev, span_type,
+ true, &mirror->span_id);
}
static void
@@ -1634,7 +1296,7 @@ mlxsw_sp_port_del_cls_matchall_mirror(struct mlxsw_sp_port *mlxsw_sp_port,
span_type = mirror->ingress ?
MLXSW_SP_SPAN_INGRESS : MLXSW_SP_SPAN_EGRESS;
- mlxsw_sp_span_mirror_del(mlxsw_sp_port, mirror->to_local_port,
+ mlxsw_sp_span_mirror_del(mlxsw_sp_port, mirror->span_id,
span_type, true);
}
@@ -2728,7 +2390,7 @@ static int mlxsw_sp_port_get_link_ksettings(struct net_device *dev,
int err;
autoneg = mlxsw_sp_port->link.autoneg;
- mlxsw_reg_ptys_eth_pack(ptys_pl, mlxsw_sp_port->local_port, 0);
+ mlxsw_reg_ptys_eth_pack(ptys_pl, mlxsw_sp_port->local_port, 0, false);
err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl);
if (err)
return err;
@@ -2762,7 +2424,7 @@ mlxsw_sp_port_set_link_ksettings(struct net_device *dev,
bool autoneg;
int err;
- mlxsw_reg_ptys_eth_pack(ptys_pl, mlxsw_sp_port->local_port, 0);
+ mlxsw_reg_ptys_eth_pack(ptys_pl, mlxsw_sp_port->local_port, 0, false);
err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl);
if (err)
return err;
@@ -2780,7 +2442,7 @@ mlxsw_sp_port_set_link_ksettings(struct net_device *dev,
}
mlxsw_reg_ptys_eth_pack(ptys_pl, mlxsw_sp_port->local_port,
- eth_proto_new);
+ eth_proto_new, autoneg);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl);
if (err)
return err;
@@ -2991,7 +2653,7 @@ mlxsw_sp_port_speed_by_width_set(struct mlxsw_sp_port *mlxsw_sp_port, u8 width)
eth_proto_admin = mlxsw_sp_to_ptys_upper_speed(upper_speed);
mlxsw_reg_ptys_eth_pack(ptys_pl, mlxsw_sp_port->local_port,
- eth_proto_admin);
+ eth_proto_admin, mlxsw_sp_port->link.autoneg);
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl);
}
@@ -3718,6 +3380,7 @@ static const struct mlxsw_listener mlxsw_sp_listener[] = {
MLXSW_SP_RXL_NO_MARK(ACL0, TRAP_TO_CPU, IP2ME, false),
/* Multicast Router Traps */
MLXSW_SP_RXL_MARK(IPV4_PIM, TRAP_TO_CPU, PIM, false),
+ MLXSW_SP_RXL_MARK(IPV6_PIM, TRAP_TO_CPU, PIM, false),
MLXSW_SP_RXL_MARK(RPF, TRAP_TO_CPU, RPF, false),
MLXSW_SP_RXL_MARK(ACL1, TRAP_TO_CPU, MULTICAST, false),
MLXSW_SP_RXL_MR_MARK(ACL2, TRAP_TO_CPU, MULTICAST, false),
@@ -4021,14 +3684,24 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core,
goto err_afa_init;
}
+ err = mlxsw_sp_span_init(mlxsw_sp);
+ if (err) {
+ dev_err(mlxsw_sp->bus_info->dev, "Failed to init span system\n");
+ goto err_span_init;
+ }
+
+ /* Initialize router after SPAN is initialized, so that the FIB and
+ * neighbor event handlers can issue SPAN respin.
+ */
err = mlxsw_sp_router_init(mlxsw_sp);
if (err) {
dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize router\n");
goto err_router_init;
}
- /* Initialize netdevice notifier after router is initialized, so that
- * the event handler can use router structures.
+ /* Initialize netdevice notifier after router and SPAN is initialized,
+ * so that the event handler can use router structures and call SPAN
+ * respin.
*/
mlxsw_sp->netdevice_nb.notifier_call = mlxsw_sp_netdevice_event;
err = register_netdevice_notifier(&mlxsw_sp->netdevice_nb);
@@ -4037,12 +3710,6 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core,
goto err_netdev_notifier;
}
- err = mlxsw_sp_span_init(mlxsw_sp);
- if (err) {
- dev_err(mlxsw_sp->bus_info->dev, "Failed to init span system\n");
- goto err_span_init;
- }
-
err = mlxsw_sp_acl_init(mlxsw_sp);
if (err) {
dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize ACL\n");
@@ -4068,12 +3735,12 @@ err_ports_create:
err_dpipe_init:
mlxsw_sp_acl_fini(mlxsw_sp);
err_acl_init:
- mlxsw_sp_span_fini(mlxsw_sp);
-err_span_init:
unregister_netdevice_notifier(&mlxsw_sp->netdevice_nb);
err_netdev_notifier:
mlxsw_sp_router_fini(mlxsw_sp);
err_router_init:
+ mlxsw_sp_span_fini(mlxsw_sp);
+err_span_init:
mlxsw_sp_afa_fini(mlxsw_sp);
err_afa_init:
mlxsw_sp_counter_pool_fini(mlxsw_sp);
@@ -4099,9 +3766,9 @@ static void mlxsw_sp_fini(struct mlxsw_core *mlxsw_core)
mlxsw_sp_ports_remove(mlxsw_sp);
mlxsw_sp_dpipe_fini(mlxsw_sp);
mlxsw_sp_acl_fini(mlxsw_sp);
- mlxsw_sp_span_fini(mlxsw_sp);
unregister_netdevice_notifier(&mlxsw_sp->netdevice_nb);
mlxsw_sp_router_fini(mlxsw_sp);
+ mlxsw_sp_span_fini(mlxsw_sp);
mlxsw_sp_afa_fini(mlxsw_sp);
mlxsw_sp_counter_pool_fini(mlxsw_sp);
mlxsw_sp_switchdev_fini(mlxsw_sp);
@@ -4113,12 +3780,8 @@ static void mlxsw_sp_fini(struct mlxsw_core *mlxsw_core)
}
static const struct mlxsw_config_profile mlxsw_sp_config_profile = {
- .used_max_vepa_channels = 1,
- .max_vepa_channels = 0,
.used_max_mid = 1,
.max_mid = MLXSW_SP_MID_MAX,
- .used_max_pgt = 1,
- .max_pgt = 0,
.used_flood_tables = 1,
.used_flood_mode = 1,
.flood_mode = 3,
@@ -4130,8 +3793,7 @@ static const struct mlxsw_config_profile mlxsw_sp_config_profile = {
.max_ib_mc = 0,
.used_max_pkey = 1,
.max_pkey = 0,
- .used_kvd_split_data = 1,
- .kvd_hash_granularity = MLXSW_SP_KVD_GRANULARITY,
+ .used_kvd_sizes = 1,
.kvd_hash_single_parts = 59,
.kvd_hash_double_parts = 41,
.kvd_linear_size = MLXSW_SP_KVD_LINEAR_SIZE,
@@ -4141,96 +3803,6 @@ static const struct mlxsw_config_profile mlxsw_sp_config_profile = {
.type = MLXSW_PORT_SWID_TYPE_ETH,
}
},
- .resource_query_enable = 1,
-};
-
-static bool
-mlxsw_sp_resource_kvd_granularity_validate(struct netlink_ext_ack *extack,
- u64 size)
-{
- const struct mlxsw_config_profile *profile;
-
- profile = &mlxsw_sp_config_profile;
- if (size % profile->kvd_hash_granularity) {
- NL_SET_ERR_MSG_MOD(extack, "resource set with wrong granularity");
- return false;
- }
- return true;
-}
-
-static int
-mlxsw_sp_resource_kvd_size_validate(struct devlink *devlink, u64 size,
- struct netlink_ext_ack *extack)
-{
- NL_SET_ERR_MSG_MOD(extack, "kvd size cannot be changed");
- return -EINVAL;
-}
-
-static int
-mlxsw_sp_resource_kvd_linear_size_validate(struct devlink *devlink, u64 size,
- struct netlink_ext_ack *extack)
-{
- if (!mlxsw_sp_resource_kvd_granularity_validate(extack, size))
- return -EINVAL;
-
- return 0;
-}
-
-static int
-mlxsw_sp_resource_kvd_hash_single_size_validate(struct devlink *devlink, u64 size,
- struct netlink_ext_ack *extack)
-{
- struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
-
- if (!mlxsw_sp_resource_kvd_granularity_validate(extack, size))
- return -EINVAL;
-
- if (size < MLXSW_CORE_RES_GET(mlxsw_core, KVD_SINGLE_MIN_SIZE)) {
- NL_SET_ERR_MSG_MOD(extack, "hash single size is smaller than minimum");
- return -EINVAL;
- }
- return 0;
-}
-
-static int
-mlxsw_sp_resource_kvd_hash_double_size_validate(struct devlink *devlink, u64 size,
- struct netlink_ext_ack *extack)
-{
- struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
-
- if (!mlxsw_sp_resource_kvd_granularity_validate(extack, size))
- return -EINVAL;
-
- if (size < MLXSW_CORE_RES_GET(mlxsw_core, KVD_DOUBLE_MIN_SIZE)) {
- NL_SET_ERR_MSG_MOD(extack, "hash double size is smaller than minimum");
- return -EINVAL;
- }
- return 0;
-}
-
-static u64 mlxsw_sp_resource_kvd_linear_occ_get(struct devlink *devlink)
-{
- struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
- struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
-
- return mlxsw_sp_kvdl_occ_get(mlxsw_sp);
-}
-
-static struct devlink_resource_ops mlxsw_sp_resource_kvd_ops = {
- .size_validate = mlxsw_sp_resource_kvd_size_validate,
-};
-
-static struct devlink_resource_ops mlxsw_sp_resource_kvd_linear_ops = {
- .size_validate = mlxsw_sp_resource_kvd_linear_size_validate,
- .occ_get = mlxsw_sp_resource_kvd_linear_occ_get,
-};
-
-static struct devlink_resource_ops mlxsw_sp_resource_kvd_hash_single_ops = {
- .size_validate = mlxsw_sp_resource_kvd_hash_single_size_validate,
-};
-
-static struct devlink_resource_ops mlxsw_sp_resource_kvd_hash_double_ops = {
- .size_validate = mlxsw_sp_resource_kvd_hash_double_size_validate,
};
static void
@@ -4291,45 +3863,44 @@ static int mlxsw_sp_resources_register(struct mlxsw_core *mlxsw_core)
kvd_size = MLXSW_CORE_RES_GET(mlxsw_core, KVD_SIZE);
err = devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD,
- true, kvd_size,
- MLXSW_SP_RESOURCE_KVD,
+ kvd_size, MLXSW_SP_RESOURCE_KVD,
DEVLINK_RESOURCE_ID_PARENT_TOP,
- &kvd_size_params,
- &mlxsw_sp_resource_kvd_ops);
+ &kvd_size_params);
if (err)
return err;
linear_size = profile->kvd_linear_size;
err = devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_LINEAR,
- false, linear_size,
+ linear_size,
MLXSW_SP_RESOURCE_KVD_LINEAR,
MLXSW_SP_RESOURCE_KVD,
- &linear_size_params,
- &mlxsw_sp_resource_kvd_linear_ops);
+ &linear_size_params);
if (err)
return err;
+ err = mlxsw_sp_kvdl_resources_register(mlxsw_core);
+ if (err)
+ return err;
+
double_size = kvd_size - linear_size;
double_size *= profile->kvd_hash_double_parts;
double_size /= profile->kvd_hash_double_parts +
profile->kvd_hash_single_parts;
- double_size = rounddown(double_size, profile->kvd_hash_granularity);
+ double_size = rounddown(double_size, MLXSW_SP_KVD_GRANULARITY);
err = devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_HASH_DOUBLE,
- false, double_size,
+ double_size,
MLXSW_SP_RESOURCE_KVD_HASH_DOUBLE,
MLXSW_SP_RESOURCE_KVD,
- &hash_double_size_params,
- &mlxsw_sp_resource_kvd_hash_double_ops);
+ &hash_double_size_params);
if (err)
return err;
single_size = kvd_size - double_size - linear_size;
err = devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_HASH_SINGLE,
- false, single_size,
+ single_size,
MLXSW_SP_RESOURCE_KVD_HASH_SINGLE,
MLXSW_SP_RESOURCE_KVD,
- &hash_single_size_params,
- &mlxsw_sp_resource_kvd_hash_single_ops);
+ &hash_single_size_params);
if (err)
return err;
@@ -4346,8 +3917,7 @@ static int mlxsw_sp_kvd_sizes_get(struct mlxsw_core *mlxsw_core,
int err;
if (!MLXSW_CORE_RES_VALID(mlxsw_core, KVD_SINGLE_MIN_SIZE) ||
- !MLXSW_CORE_RES_VALID(mlxsw_core, KVD_DOUBLE_MIN_SIZE) ||
- !profile->used_kvd_split_data)
+ !MLXSW_CORE_RES_VALID(mlxsw_core, KVD_DOUBLE_MIN_SIZE))
return -EIO;
/* The hash part is what left of the kvd without the
@@ -4373,7 +3943,7 @@ static int mlxsw_sp_kvd_sizes_get(struct mlxsw_core *mlxsw_core,
double_size /= profile->kvd_hash_double_parts +
profile->kvd_hash_single_parts;
*p_double_size = rounddown(double_size,
- profile->kvd_hash_granularity);
+ MLXSW_SP_KVD_GRANULARITY);
}
err = devlink_resource_size_get(devlink,
@@ -4415,6 +3985,7 @@ static struct mlxsw_driver mlxsw_sp_driver = {
.kvd_sizes_get = mlxsw_sp_kvd_sizes_get,
.txhdr_len = MLXSW_TXHDR_LEN,
.profile = &mlxsw_sp_config_profile,
+ .res_query_enabled = true,
};
bool mlxsw_sp_port_dev_check(const struct net_device *dev)
@@ -4583,13 +4154,11 @@ mlxsw_sp_master_lag_check(struct mlxsw_sp *mlxsw_sp,
u16 lag_id;
if (mlxsw_sp_lag_index_get(mlxsw_sp, lag_dev, &lag_id) != 0) {
- NL_SET_ERR_MSG(extack,
- "spectrum: Exceeded number of supported LAG devices");
+ NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported LAG devices");
return false;
}
if (lag_upper_info->tx_type != NETDEV_LAG_TX_TYPE_HASH) {
- NL_SET_ERR_MSG(extack,
- "spectrum: LAG device using unsupported Tx type");
+ NL_SET_ERR_MSG_MOD(extack, "LAG device using unsupported Tx type");
return false;
}
return true;
@@ -4831,8 +4400,7 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *lower_dev,
!netif_is_lag_master(upper_dev) &&
!netif_is_bridge_master(upper_dev) &&
!netif_is_ovs_master(upper_dev)) {
- NL_SET_ERR_MSG(extack,
- "spectrum: Unknown upper device type");
+ NL_SET_ERR_MSG_MOD(extack, "Unknown upper device type");
return -EINVAL;
}
if (!info->linking)
@@ -4841,8 +4409,7 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *lower_dev,
(!netif_is_bridge_master(upper_dev) ||
!mlxsw_sp_bridge_device_is_offloaded(mlxsw_sp,
upper_dev))) {
- NL_SET_ERR_MSG(extack,
- "spectrum: Enslaving a port to a device that already has an upper device is not supported");
+ NL_SET_ERR_MSG_MOD(extack, "Enslaving a port to a device that already has an upper device is not supported");
return -EINVAL;
}
if (netif_is_lag_master(upper_dev) &&
@@ -4850,24 +4417,20 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *lower_dev,
info->upper_info, extack))
return -EINVAL;
if (netif_is_lag_master(upper_dev) && vlan_uses_dev(dev)) {
- NL_SET_ERR_MSG(extack,
- "spectrum: Master device is a LAG master and this device has a VLAN");
+ NL_SET_ERR_MSG_MOD(extack, "Master device is a LAG master and this device has a VLAN");
return -EINVAL;
}
if (netif_is_lag_port(dev) && is_vlan_dev(upper_dev) &&
!netif_is_lag_master(vlan_dev_real_dev(upper_dev))) {
- NL_SET_ERR_MSG(extack,
- "spectrum: Can not put a VLAN on a LAG port");
+ NL_SET_ERR_MSG_MOD(extack, "Can not put a VLAN on a LAG port");
return -EINVAL;
}
if (netif_is_ovs_master(upper_dev) && vlan_uses_dev(dev)) {
- NL_SET_ERR_MSG(extack,
- "spectrum: Master device is an OVS master and this device has a VLAN");
+ NL_SET_ERR_MSG_MOD(extack, "Master device is an OVS master and this device has a VLAN");
return -EINVAL;
}
if (netif_is_ovs_port(dev) && is_vlan_dev(upper_dev)) {
- NL_SET_ERR_MSG(extack,
- "spectrum: Can not put a VLAN on an OVS port");
+ NL_SET_ERR_MSG_MOD(extack, "Can not put a VLAN on an OVS port");
return -EINVAL;
}
break;
@@ -4980,7 +4543,7 @@ static int mlxsw_sp_netdevice_port_vlan_event(struct net_device *vlan_dev,
case NETDEV_PRECHANGEUPPER:
upper_dev = info->upper_dev;
if (!netif_is_bridge_master(upper_dev)) {
- NL_SET_ERR_MSG(extack, "spectrum: VLAN devices only support bridge and VRF uppers");
+ NL_SET_ERR_MSG_MOD(extack, "VLAN devices only support bridge and VRF uppers");
return -EINVAL;
}
if (!info->linking)
@@ -4989,7 +4552,7 @@ static int mlxsw_sp_netdevice_port_vlan_event(struct net_device *vlan_dev,
(!netif_is_bridge_master(upper_dev) ||
!mlxsw_sp_bridge_device_is_offloaded(mlxsw_sp,
upper_dev))) {
- NL_SET_ERR_MSG(extack, "spectrum: Enslaving a port to a device that already has an upper device is not supported");
+ NL_SET_ERR_MSG_MOD(extack, "Enslaving a port to a device that already has an upper device is not supported");
return -EINVAL;
}
break;
@@ -5067,10 +4630,18 @@ static int mlxsw_sp_netdevice_event(struct notifier_block *nb,
unsigned long event, void *ptr)
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+ struct mlxsw_sp_span_entry *span_entry;
struct mlxsw_sp *mlxsw_sp;
int err = 0;
mlxsw_sp = container_of(nb, struct mlxsw_sp, netdevice_nb);
+ if (event == NETDEV_UNREGISTER) {
+ span_entry = mlxsw_sp_span_entry_find_by_port(mlxsw_sp, dev);
+ if (span_entry)
+ mlxsw_sp_span_entry_invalidate(mlxsw_sp, span_entry);
+ }
+ mlxsw_sp_span_respin(mlxsw_sp);
+
if (mlxsw_sp_netdev_is_ipip_ol(mlxsw_sp, dev))
err = mlxsw_sp_netdevice_ipip_ol_event(mlxsw_sp, dev,
event, ptr);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
index 92064db2ae44..804d4d2c8031 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
@@ -70,16 +70,23 @@
#define MLXSW_SP_RESOURCE_NAME_KVD_LINEAR "linear"
#define MLXSW_SP_RESOURCE_NAME_KVD_HASH_SINGLE "hash_single"
#define MLXSW_SP_RESOURCE_NAME_KVD_HASH_DOUBLE "hash_double"
+#define MLXSW_SP_RESOURCE_NAME_KVD_LINEAR_SINGLES "singles"
+#define MLXSW_SP_RESOURCE_NAME_KVD_LINEAR_CHUNKS "chunks"
+#define MLXSW_SP_RESOURCE_NAME_KVD_LINEAR_LARGE_CHUNKS "large_chunks"
enum mlxsw_sp_resource_id {
- MLXSW_SP_RESOURCE_KVD,
+ MLXSW_SP_RESOURCE_KVD = 1,
MLXSW_SP_RESOURCE_KVD_LINEAR,
MLXSW_SP_RESOURCE_KVD_HASH_SINGLE,
MLXSW_SP_RESOURCE_KVD_HASH_DOUBLE,
+ MLXSW_SP_RESOURCE_KVD_LINEAR_SINGLE,
+ MLXSW_SP_RESOURCE_KVD_LINEAR_CHUNKS,
+ MLXSW_SP_RESOURCE_KVD_LINEAR_LARGE_CHUNKS,
};
struct mlxsw_sp_port;
struct mlxsw_sp_rif;
+struct mlxsw_sp_span_entry;
struct mlxsw_sp_upper {
struct net_device *dev;
@@ -111,35 +118,13 @@ struct mlxsw_sp_mid {
unsigned long *ports_in_mid; /* bits array */
};
-enum mlxsw_sp_span_type {
- MLXSW_SP_SPAN_EGRESS,
- MLXSW_SP_SPAN_INGRESS
-};
-
-struct mlxsw_sp_span_inspected_port {
- struct list_head list;
- enum mlxsw_sp_span_type type;
- u8 local_port;
-
- /* Whether this is a directly bound mirror (port-to-port) or an ACL. */
- bool bound;
-};
-
-struct mlxsw_sp_span_entry {
- u8 local_port;
- bool used;
- struct list_head bound_ports_list;
- int ref_count;
- int id;
-};
-
enum mlxsw_sp_port_mall_action_type {
MLXSW_SP_PORT_MALL_MIRROR,
MLXSW_SP_PORT_MALL_SAMPLE,
};
struct mlxsw_sp_port_mall_mirror_tc_entry {
- u8 to_local_port;
+ int span_id;
bool ingress;
};
@@ -226,6 +211,8 @@ struct mlxsw_sp_port_xstats {
u64 wred_drop[TC_MAX_QUEUE];
u64 tail_drop[TC_MAX_QUEUE];
u64 backlog[TC_MAX_QUEUE];
+ u64 tx_bytes[IEEE_8021QAZ_MAX_TCS];
+ u64 tx_packets[IEEE_8021QAZ_MAX_TCS];
};
struct mlxsw_sp_port {
@@ -263,6 +250,7 @@ struct mlxsw_sp_port {
struct mlxsw_sp_port_sample *sample;
struct list_head vlans_list;
struct mlxsw_sp_qdisc *root_qdisc;
+ struct mlxsw_sp_qdisc *tclass_qdiscs;
unsigned acl_rule_count;
struct mlxsw_sp_acl_block *ing_acl_block;
struct mlxsw_sp_acl_block *eg_acl_block;
@@ -400,16 +388,6 @@ struct mlxsw_sp_port *mlxsw_sp_port_dev_lower_find(struct net_device *dev);
struct mlxsw_sp_port *mlxsw_sp_port_lower_dev_hold(struct net_device *dev);
void mlxsw_sp_port_dev_put(struct mlxsw_sp_port *mlxsw_sp_port);
struct mlxsw_sp_port *mlxsw_sp_port_dev_lower_find_rcu(struct net_device *dev);
-int mlxsw_sp_span_mirror_add(struct mlxsw_sp_port *from,
- struct mlxsw_sp_port *to,
- enum mlxsw_sp_span_type type,
- bool bind);
-void mlxsw_sp_span_mirror_del(struct mlxsw_sp_port *from,
- u8 destination_port,
- enum mlxsw_sp_span_type type,
- bool bind);
-struct mlxsw_sp_span_entry *
-mlxsw_sp_span_entry_find(struct mlxsw_sp *mlxsw_sp, u8 local_port);
/* spectrum_dcb.c */
#ifdef CONFIG_MLXSW_SPECTRUM_DCB
@@ -464,7 +442,7 @@ void mlxsw_sp_kvdl_free(struct mlxsw_sp *mlxsw_sp, int entry_index);
int mlxsw_sp_kvdl_alloc_size_query(struct mlxsw_sp *mlxsw_sp,
unsigned int entry_count,
unsigned int *p_alloc_size);
-u64 mlxsw_sp_kvdl_occ_get(const struct mlxsw_sp *mlxsw_sp);
+int mlxsw_sp_kvdl_resources_register(struct mlxsw_core *mlxsw_core);
struct mlxsw_sp_acl_rule_info {
unsigned int priority;
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c
index 92d90ed7207e..79b1fa27a9a4 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c
@@ -160,6 +160,13 @@ bool mlxsw_sp_acl_block_disabled(struct mlxsw_sp_acl_block *block)
return block->disable_count;
}
+static bool
+mlxsw_sp_acl_ruleset_is_singular(const struct mlxsw_sp_acl_ruleset *ruleset)
+{
+ /* We hold a reference on ruleset ourselves */
+ return ruleset->ref_count == 2;
+}
+
static int
mlxsw_sp_acl_ruleset_bind(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_block *block,
@@ -341,21 +348,8 @@ mlxsw_sp_acl_ruleset_create(struct mlxsw_sp *mlxsw_sp,
if (err)
goto err_ht_insert;
- if (!chain_index) {
- /* We only need ruleset with chain index 0, the implicit one,
- * to be directly bound to device. The rest of the rulesets
- * are bound by "Goto action set".
- */
- err = mlxsw_sp_acl_ruleset_block_bind(mlxsw_sp, ruleset, block);
- if (err)
- goto err_ruleset_bind;
- }
-
return ruleset;
-err_ruleset_bind:
- rhashtable_remove_fast(&acl->ruleset_ht, &ruleset->ht_node,
- mlxsw_sp_acl_ruleset_ht_params);
err_ht_insert:
ops->ruleset_del(mlxsw_sp, ruleset->priv);
err_ops_ruleset_add:
@@ -369,12 +363,8 @@ static void mlxsw_sp_acl_ruleset_destroy(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_ruleset *ruleset)
{
const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
- struct mlxsw_sp_acl_block *block = ruleset->ht_key.block;
- u32 chain_index = ruleset->ht_key.chain_index;
struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
- if (!chain_index)
- mlxsw_sp_acl_ruleset_block_unbind(mlxsw_sp, ruleset, block);
rhashtable_remove_fast(&acl->ruleset_ht, &ruleset->ht_node,
mlxsw_sp_acl_ruleset_ht_params);
ops->ruleset_del(mlxsw_sp, ruleset->priv);
@@ -577,7 +567,6 @@ int mlxsw_sp_acl_rulei_act_mirror(struct mlxsw_sp *mlxsw_sp,
struct net_device *out_dev)
{
struct mlxsw_sp_acl_block_binding *binding;
- struct mlxsw_sp_port *out_port;
struct mlxsw_sp_port *in_port;
if (!list_is_singular(&block->binding_list))
@@ -586,16 +575,10 @@ int mlxsw_sp_acl_rulei_act_mirror(struct mlxsw_sp *mlxsw_sp,
binding = list_first_entry(&block->binding_list,
struct mlxsw_sp_acl_block_binding, list);
in_port = binding->mlxsw_sp_port;
- if (!mlxsw_sp_port_dev_check(out_dev))
- return -EINVAL;
-
- out_port = netdev_priv(out_dev);
- if (out_port->mlxsw_sp != mlxsw_sp)
- return -EINVAL;
return mlxsw_afa_block_append_mirror(rulei->act_block,
in_port->local_port,
- out_port->local_port,
+ out_dev,
binding->ingress);
}
@@ -700,10 +683,25 @@ int mlxsw_sp_acl_rule_add(struct mlxsw_sp *mlxsw_sp,
if (err)
goto err_rhashtable_insert;
+ if (!ruleset->ht_key.chain_index &&
+ mlxsw_sp_acl_ruleset_is_singular(ruleset)) {
+ /* We only need ruleset with chain index 0, the implicit
+ * one, to be directly bound to device. The rest of the
+ * rulesets are bound by "Goto action set".
+ */
+ err = mlxsw_sp_acl_ruleset_block_bind(mlxsw_sp, ruleset,
+ ruleset->ht_key.block);
+ if (err)
+ goto err_ruleset_block_bind;
+ }
+
list_add_tail(&rule->list, &mlxsw_sp->acl->rules);
ruleset->ht_key.block->rule_count++;
return 0;
+err_ruleset_block_bind:
+ rhashtable_remove_fast(&ruleset->rule_ht, &rule->ht_node,
+ mlxsw_sp_acl_rule_ht_params);
err_rhashtable_insert:
ops->rule_del(mlxsw_sp, rule->priv);
return err;
@@ -717,6 +715,10 @@ void mlxsw_sp_acl_rule_del(struct mlxsw_sp *mlxsw_sp,
ruleset->ht_key.block->rule_count--;
list_del(&rule->list);
+ if (!ruleset->ht_key.chain_index &&
+ mlxsw_sp_acl_ruleset_is_singular(ruleset))
+ mlxsw_sp_acl_ruleset_block_unbind(mlxsw_sp, ruleset,
+ ruleset->ht_key.block);
rhashtable_remove_fast(&ruleset->rule_ht, &rule->ht_node,
mlxsw_sp_acl_rule_ht_params);
ops->rule_del(mlxsw_sp, rule->priv);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_actions.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_actions.c
index 6ca6894125f0..510ce48d87f7 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_actions.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_actions.c
@@ -1,6 +1,6 @@
/*
* drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_actions.c
- * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2017, 2018 Mellanox Technologies. All rights reserved.
* Copyright (c) 2017 Jiri Pirko <jiri@mellanox.com>
* Copyright (c) 2017 Yotam Gigi <yotamg@mellanox.com>
*
@@ -35,6 +35,7 @@
#include "spectrum_acl_flex_actions.h"
#include "core_acl_flex_actions.h"
+#include "spectrum_span.h"
#define MLXSW_SP_KVDL_ACT_EXT_SIZE 1
@@ -125,40 +126,23 @@ mlxsw_sp_act_counter_index_put(void *priv, unsigned int counter_index)
}
static int
-mlxsw_sp_act_mirror_add(void *priv, u8 local_in_port, u8 local_out_port,
+mlxsw_sp_act_mirror_add(void *priv, u8 local_in_port,
+ const struct net_device *out_dev,
bool ingress, int *p_span_id)
{
- struct mlxsw_sp_port *in_port, *out_port;
- struct mlxsw_sp_span_entry *span_entry;
+ struct mlxsw_sp_port *in_port;
struct mlxsw_sp *mlxsw_sp = priv;
enum mlxsw_sp_span_type type;
- int err;
type = ingress ? MLXSW_SP_SPAN_INGRESS : MLXSW_SP_SPAN_EGRESS;
- out_port = mlxsw_sp->ports[local_out_port];
in_port = mlxsw_sp->ports[local_in_port];
- err = mlxsw_sp_span_mirror_add(in_port, out_port, type, false);
- if (err)
- return err;
-
- span_entry = mlxsw_sp_span_entry_find(mlxsw_sp, local_out_port);
- if (!span_entry) {
- err = -ENOENT;
- goto err_span_entry_find;
- }
-
- *p_span_id = span_entry->id;
- return 0;
-
-err_span_entry_find:
- mlxsw_sp_span_mirror_del(in_port, local_out_port, type, false);
- return err;
+ return mlxsw_sp_span_mirror_add(in_port, out_dev, type,
+ false, p_span_id);
}
static void
-mlxsw_sp_act_mirror_del(void *priv, u8 local_in_port, u8 local_out_port,
- bool ingress)
+mlxsw_sp_act_mirror_del(void *priv, u8 local_in_port, int span_id, bool ingress)
{
struct mlxsw_sp *mlxsw_sp = priv;
struct mlxsw_sp_port *in_port;
@@ -167,7 +151,7 @@ mlxsw_sp_act_mirror_del(void *priv, u8 local_in_port, u8 local_out_port,
type = ingress ? MLXSW_SP_SPAN_INGRESS : MLXSW_SP_SPAN_EGRESS;
in_port = mlxsw_sp->ports[local_in_port];
- mlxsw_sp_span_mirror_del(in_port, local_out_port, type, false);
+ mlxsw_sp_span_mirror_del(in_port, span_id, type, false);
}
static const struct mlxsw_afa_ops mlxsw_sp_act_afa_ops = {
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_actions.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_actions.h
index 2726192836ad..bd6d552d95b9 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_actions.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_actions.h
@@ -33,8 +33,8 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef _MLXSW_SPECTRUM_ACL_FLEX_KEYS_H
-#define _MLXSW_SPECTRUM_ACL_FLEX_KEYS_H
+#ifndef _MLXSW_SPECTRUM_ACL_FLEX_ACTIONS_H
+#define _MLXSW_SPECTRUM_ACL_FLEX_ACTIONS_H
#include "spectrum.h"
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c
index c6e180c2be1e..ad1b548e3cac 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c
@@ -228,10 +228,6 @@ mlxsw_sp_acl_tcam_group_add(struct mlxsw_sp *mlxsw_sp,
if (err)
return err;
- err = mlxsw_sp_acl_tcam_group_update(mlxsw_sp, group);
- if (err)
- goto err_group_update;
-
err = rhashtable_init(&group->chunk_ht,
&mlxsw_sp_acl_tcam_chunk_ht_params);
if (err)
@@ -240,7 +236,6 @@ mlxsw_sp_acl_tcam_group_add(struct mlxsw_sp *mlxsw_sp,
return 0;
err_rhashtable_init:
-err_group_update:
mlxsw_sp_acl_tcam_group_id_put(tcam, group->id);
return err;
}
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c
index 7502e53447bd..98d896c14b87 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c
@@ -1,7 +1,7 @@
/*
* drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c
- * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
- * Copyright (c) 2017 Petr Machata <petrm@mellanox.com>
+ * Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2017-2018 Petr Machata <petrm@mellanox.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -33,126 +33,125 @@
*/
#include <net/ip_tunnels.h>
+#include <net/ip6_tunnel.h>
#include "spectrum_ipip.h"
struct ip_tunnel_parm
-mlxsw_sp_ipip_netdev_parms(const struct net_device *ol_dev)
+mlxsw_sp_ipip_netdev_parms4(const struct net_device *ol_dev)
{
struct ip_tunnel *tun = netdev_priv(ol_dev);
return tun->parms;
}
-static bool mlxsw_sp_ipip_parms_has_ikey(struct ip_tunnel_parm parms)
+struct __ip6_tnl_parm
+mlxsw_sp_ipip_netdev_parms6(const struct net_device *ol_dev)
+{
+ struct ip6_tnl *tun = netdev_priv(ol_dev);
+
+ return tun->parms;
+}
+
+static bool mlxsw_sp_ipip_parms4_has_ikey(struct ip_tunnel_parm parms)
{
return !!(parms.i_flags & TUNNEL_KEY);
}
-static bool mlxsw_sp_ipip_parms_has_okey(struct ip_tunnel_parm parms)
+static bool mlxsw_sp_ipip_parms4_has_okey(struct ip_tunnel_parm parms)
{
return !!(parms.o_flags & TUNNEL_KEY);
}
-static u32 mlxsw_sp_ipip_parms_ikey(struct ip_tunnel_parm parms)
+static u32 mlxsw_sp_ipip_parms4_ikey(struct ip_tunnel_parm parms)
{
- return mlxsw_sp_ipip_parms_has_ikey(parms) ?
+ return mlxsw_sp_ipip_parms4_has_ikey(parms) ?
be32_to_cpu(parms.i_key) : 0;
}
-static u32 mlxsw_sp_ipip_parms_okey(struct ip_tunnel_parm parms)
+static u32 mlxsw_sp_ipip_parms4_okey(struct ip_tunnel_parm parms)
{
- return mlxsw_sp_ipip_parms_has_okey(parms) ?
+ return mlxsw_sp_ipip_parms4_has_okey(parms) ?
be32_to_cpu(parms.o_key) : 0;
}
-static __be32 mlxsw_sp_ipip_parms_saddr4(struct ip_tunnel_parm parms)
+static union mlxsw_sp_l3addr
+mlxsw_sp_ipip_parms4_saddr(struct ip_tunnel_parm parms)
{
- return parms.iph.saddr;
+ return (union mlxsw_sp_l3addr) { .addr4 = parms.iph.saddr };
}
static union mlxsw_sp_l3addr
-mlxsw_sp_ipip_parms_saddr(enum mlxsw_sp_l3proto proto,
- struct ip_tunnel_parm parms)
+mlxsw_sp_ipip_parms6_saddr(struct __ip6_tnl_parm parms)
{
- switch (proto) {
- case MLXSW_SP_L3_PROTO_IPV4:
- return (union mlxsw_sp_l3addr) {
- .addr4 = mlxsw_sp_ipip_parms_saddr4(parms),
- };
- case MLXSW_SP_L3_PROTO_IPV6:
- break;
- }
-
- WARN_ON(1);
- return (union mlxsw_sp_l3addr) {
- .addr4 = 0,
- };
+ return (union mlxsw_sp_l3addr) { .addr6 = parms.laddr };
}
-static __be32 mlxsw_sp_ipip_parms_daddr4(struct ip_tunnel_parm parms)
+static union mlxsw_sp_l3addr
+mlxsw_sp_ipip_parms4_daddr(struct ip_tunnel_parm parms)
{
- return parms.iph.daddr;
+ return (union mlxsw_sp_l3addr) { .addr4 = parms.iph.daddr };
}
static union mlxsw_sp_l3addr
-mlxsw_sp_ipip_parms_daddr(enum mlxsw_sp_l3proto proto,
- struct ip_tunnel_parm parms)
+mlxsw_sp_ipip_parms6_daddr(struct __ip6_tnl_parm parms)
+{
+ return (union mlxsw_sp_l3addr) { .addr6 = parms.raddr };
+}
+
+union mlxsw_sp_l3addr
+mlxsw_sp_ipip_netdev_saddr(enum mlxsw_sp_l3proto proto,
+ const struct net_device *ol_dev)
{
+ struct ip_tunnel_parm parms4;
+ struct __ip6_tnl_parm parms6;
+
switch (proto) {
case MLXSW_SP_L3_PROTO_IPV4:
- return (union mlxsw_sp_l3addr) {
- .addr4 = mlxsw_sp_ipip_parms_daddr4(parms),
- };
+ parms4 = mlxsw_sp_ipip_netdev_parms4(ol_dev);
+ return mlxsw_sp_ipip_parms4_saddr(parms4);
case MLXSW_SP_L3_PROTO_IPV6:
- break;
+ parms6 = mlxsw_sp_ipip_netdev_parms6(ol_dev);
+ return mlxsw_sp_ipip_parms6_saddr(parms6);
}
WARN_ON(1);
- return (union mlxsw_sp_l3addr) {
- .addr4 = 0,
- };
-}
-
-static bool mlxsw_sp_ipip_netdev_has_ikey(const struct net_device *ol_dev)
-{
- return mlxsw_sp_ipip_parms_has_ikey(mlxsw_sp_ipip_netdev_parms(ol_dev));
+ return (union mlxsw_sp_l3addr) {0};
}
-static bool mlxsw_sp_ipip_netdev_has_okey(const struct net_device *ol_dev)
+static __be32 mlxsw_sp_ipip_netdev_daddr4(const struct net_device *ol_dev)
{
- return mlxsw_sp_ipip_parms_has_okey(mlxsw_sp_ipip_netdev_parms(ol_dev));
-}
-static u32 mlxsw_sp_ipip_netdev_ikey(const struct net_device *ol_dev)
-{
- return mlxsw_sp_ipip_parms_ikey(mlxsw_sp_ipip_netdev_parms(ol_dev));
-}
+ struct ip_tunnel_parm parms4 = mlxsw_sp_ipip_netdev_parms4(ol_dev);
-static u32 mlxsw_sp_ipip_netdev_okey(const struct net_device *ol_dev)
-{
- return mlxsw_sp_ipip_parms_okey(mlxsw_sp_ipip_netdev_parms(ol_dev));
+ return mlxsw_sp_ipip_parms4_daddr(parms4).addr4;
}
-union mlxsw_sp_l3addr
-mlxsw_sp_ipip_netdev_saddr(enum mlxsw_sp_l3proto proto,
+static union mlxsw_sp_l3addr
+mlxsw_sp_ipip_netdev_daddr(enum mlxsw_sp_l3proto proto,
const struct net_device *ol_dev)
{
- return mlxsw_sp_ipip_parms_saddr(proto,
- mlxsw_sp_ipip_netdev_parms(ol_dev));
-}
+ struct ip_tunnel_parm parms4;
+ struct __ip6_tnl_parm parms6;
-static __be32 mlxsw_sp_ipip_netdev_daddr4(const struct net_device *ol_dev)
-{
- return mlxsw_sp_ipip_parms_daddr4(mlxsw_sp_ipip_netdev_parms(ol_dev));
+ switch (proto) {
+ case MLXSW_SP_L3_PROTO_IPV4:
+ parms4 = mlxsw_sp_ipip_netdev_parms4(ol_dev);
+ return mlxsw_sp_ipip_parms4_daddr(parms4);
+ case MLXSW_SP_L3_PROTO_IPV6:
+ parms6 = mlxsw_sp_ipip_netdev_parms6(ol_dev);
+ return mlxsw_sp_ipip_parms6_daddr(parms6);
+ }
+
+ WARN_ON(1);
+ return (union mlxsw_sp_l3addr) {0};
}
-static union mlxsw_sp_l3addr
-mlxsw_sp_ipip_netdev_daddr(enum mlxsw_sp_l3proto proto,
- const struct net_device *ol_dev)
+bool mlxsw_sp_l3addr_is_zero(union mlxsw_sp_l3addr addr)
{
- return mlxsw_sp_ipip_parms_daddr(proto,
- mlxsw_sp_ipip_netdev_parms(ol_dev));
+ union mlxsw_sp_l3addr naddr = {0};
+
+ return !memcmp(&addr, &naddr, sizeof(naddr));
}
static int
@@ -176,12 +175,17 @@ mlxsw_sp_ipip_fib_entry_op_gre4_rtdp(struct mlxsw_sp *mlxsw_sp,
u32 tunnel_index,
struct mlxsw_sp_ipip_entry *ipip_entry)
{
- bool has_ikey = mlxsw_sp_ipip_netdev_has_ikey(ipip_entry->ol_dev);
u16 rif_index = mlxsw_sp_ipip_lb_rif_index(ipip_entry->ol_lb);
- u32 ikey = mlxsw_sp_ipip_netdev_ikey(ipip_entry->ol_dev);
char rtdp_pl[MLXSW_REG_RTDP_LEN];
+ struct ip_tunnel_parm parms;
unsigned int type_check;
+ bool has_ikey;
u32 daddr4;
+ u32 ikey;
+
+ parms = mlxsw_sp_ipip_netdev_parms4(ipip_entry->ol_dev);
+ has_ikey = mlxsw_sp_ipip_parms4_has_ikey(parms);
+ ikey = mlxsw_sp_ipip_parms4_ikey(parms);
mlxsw_reg_rtdp_pack(rtdp_pl, MLXSW_REG_RTDP_TYPE_IPIP, tunnel_index);
@@ -243,15 +247,14 @@ static bool mlxsw_sp_ipip_tunnel_complete(enum mlxsw_sp_l3proto proto,
{
union mlxsw_sp_l3addr saddr = mlxsw_sp_ipip_netdev_saddr(proto, ol_dev);
union mlxsw_sp_l3addr daddr = mlxsw_sp_ipip_netdev_daddr(proto, ol_dev);
- union mlxsw_sp_l3addr naddr = {0};
/* Tunnels with unset local or remote address are valid in Linux and
* used for lightweight tunnels (LWT) and Non-Broadcast Multi-Access
* (NBMA) tunnels. In principle these can be offloaded, but the driver
* currently doesn't support this. So punt.
*/
- return memcmp(&saddr, &naddr, sizeof(naddr)) &&
- memcmp(&daddr, &naddr, sizeof(naddr));
+ return !mlxsw_sp_l3addr_is_zero(saddr) &&
+ !mlxsw_sp_l3addr_is_zero(daddr);
}
static bool mlxsw_sp_ipip_can_offload_gre4(const struct mlxsw_sp *mlxsw_sp,
@@ -273,14 +276,15 @@ static struct mlxsw_sp_rif_ipip_lb_config
mlxsw_sp_ipip_ol_loopback_config_gre4(struct mlxsw_sp *mlxsw_sp,
const struct net_device *ol_dev)
{
+ struct ip_tunnel_parm parms = mlxsw_sp_ipip_netdev_parms4(ol_dev);
enum mlxsw_reg_ritr_loopback_ipip_type lb_ipipt;
- lb_ipipt = mlxsw_sp_ipip_netdev_has_okey(ol_dev) ?
+ lb_ipipt = mlxsw_sp_ipip_parms4_has_okey(parms) ?
MLXSW_REG_RITR_LOOPBACK_IPIP_TYPE_IP_IN_GRE_KEY_IN_IP :
MLXSW_REG_RITR_LOOPBACK_IPIP_TYPE_IP_IN_GRE_IN_IP;
return (struct mlxsw_sp_rif_ipip_lb_config){
.lb_ipipt = lb_ipipt,
- .okey = mlxsw_sp_ipip_netdev_okey(ol_dev),
+ .okey = mlxsw_sp_ipip_parms4_okey(parms),
.ul_protocol = MLXSW_SP_L3_PROTO_IPV4,
.saddr = mlxsw_sp_ipip_netdev_saddr(MLXSW_SP_L3_PROTO_IPV4,
ol_dev),
@@ -300,16 +304,12 @@ mlxsw_sp_ipip_ol_netdev_change_gre4(struct mlxsw_sp *mlxsw_sp,
bool update_nhs = false;
int err = 0;
- new_parms = mlxsw_sp_ipip_netdev_parms(ipip_entry->ol_dev);
+ new_parms = mlxsw_sp_ipip_netdev_parms4(ipip_entry->ol_dev);
- new_saddr = mlxsw_sp_ipip_parms_saddr(MLXSW_SP_L3_PROTO_IPV4,
- new_parms);
- old_saddr = mlxsw_sp_ipip_parms_saddr(MLXSW_SP_L3_PROTO_IPV4,
- ipip_entry->parms);
- new_daddr = mlxsw_sp_ipip_parms_daddr(MLXSW_SP_L3_PROTO_IPV4,
- new_parms);
- old_daddr = mlxsw_sp_ipip_parms_daddr(MLXSW_SP_L3_PROTO_IPV4,
- ipip_entry->parms);
+ new_saddr = mlxsw_sp_ipip_parms4_saddr(new_parms);
+ old_saddr = mlxsw_sp_ipip_parms4_saddr(ipip_entry->parms4);
+ new_daddr = mlxsw_sp_ipip_parms4_daddr(new_parms);
+ old_daddr = mlxsw_sp_ipip_parms4_daddr(ipip_entry->parms4);
if (!mlxsw_sp_l3addr_eq(&new_saddr, &old_saddr)) {
u16 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ipip_entry->ol_dev);
@@ -326,14 +326,14 @@ mlxsw_sp_ipip_ol_netdev_change_gre4(struct mlxsw_sp *mlxsw_sp,
}
update_tunnel = true;
- } else if ((mlxsw_sp_ipip_parms_okey(ipip_entry->parms) !=
- mlxsw_sp_ipip_parms_okey(new_parms)) ||
- ipip_entry->parms.link != new_parms.link) {
+ } else if ((mlxsw_sp_ipip_parms4_okey(ipip_entry->parms4) !=
+ mlxsw_sp_ipip_parms4_okey(new_parms)) ||
+ ipip_entry->parms4.link != new_parms.link) {
update_tunnel = true;
} else if (!mlxsw_sp_l3addr_eq(&new_daddr, &old_daddr)) {
update_nhs = true;
- } else if (mlxsw_sp_ipip_parms_ikey(ipip_entry->parms) !=
- mlxsw_sp_ipip_parms_ikey(new_parms)) {
+ } else if (mlxsw_sp_ipip_parms4_ikey(ipip_entry->parms4) !=
+ mlxsw_sp_ipip_parms4_ikey(new_parms)) {
update_decap = true;
}
@@ -350,7 +350,7 @@ mlxsw_sp_ipip_ol_netdev_change_gre4(struct mlxsw_sp *mlxsw_sp,
false, false, false,
extack);
- ipip_entry->parms = new_parms;
+ ipip_entry->parms4 = new_parms;
return err;
}
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h
index 04b08d9d76e9..6909d867bb59 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h
@@ -1,7 +1,7 @@
/*
* drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h
- * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
- * Copyright (c) 2017 Petr Machata <petrm@mellanox.com>
+ * Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2017-2018 Petr Machata <petrm@mellanox.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -37,14 +37,19 @@
#include "spectrum_router.h"
#include <net/ip_fib.h>
+#include <linux/if_tunnel.h>
struct ip_tunnel_parm
-mlxsw_sp_ipip_netdev_parms(const struct net_device *ol_dev);
+mlxsw_sp_ipip_netdev_parms4(const struct net_device *ol_dev);
+struct __ip6_tnl_parm
+mlxsw_sp_ipip_netdev_parms6(const struct net_device *ol_dev);
union mlxsw_sp_l3addr
mlxsw_sp_ipip_netdev_saddr(enum mlxsw_sp_l3proto proto,
const struct net_device *ol_dev);
+bool mlxsw_sp_l3addr_is_zero(union mlxsw_sp_l3addr addr);
+
enum mlxsw_sp_ipip_type {
MLXSW_SP_IPIP_TYPE_GRE4,
MLXSW_SP_IPIP_TYPE_MAX,
@@ -56,7 +61,9 @@ struct mlxsw_sp_ipip_entry {
struct mlxsw_sp_rif_ipip_lb *ol_lb;
struct mlxsw_sp_fib_entry *decap_fib_entry;
struct list_head ipip_list_node;
- struct ip_tunnel_parm parms;
+ union {
+ struct ip_tunnel_parm parms4;
+ };
};
struct mlxsw_sp_ipip_ops {
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_kvdl.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_kvdl.c
index 55f9d2d70f9e..fe4327f547d2 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_kvdl.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_kvdl.c
@@ -55,24 +55,47 @@
#define MLXSW_SP_KVDL_LARGE_CHUNKS_END \
(MLXSW_SP_KVDL_LARGE_CHUNKS_SIZE + MLXSW_SP_KVDL_LARGE_CHUNKS_BASE - 1)
-#define MLXSW_SP_CHUNK_MAX 32
-#define MLXSW_SP_LARGE_CHUNK_MAX 512
+#define MLXSW_SP_KVDL_SINGLE_ALLOC_SIZE 1
+#define MLXSW_SP_KVDL_CHUNKS_ALLOC_SIZE 32
+#define MLXSW_SP_KVDL_LARGE_CHUNKS_ALLOC_SIZE 512
struct mlxsw_sp_kvdl_part_info {
unsigned int part_index;
unsigned int start_index;
unsigned int end_index;
unsigned int alloc_size;
+ enum mlxsw_sp_resource_id resource_id;
};
+enum mlxsw_sp_kvdl_part_id {
+ MLXSW_SP_KVDL_PART_ID_SINGLE,
+ MLXSW_SP_KVDL_PART_ID_CHUNKS,
+ MLXSW_SP_KVDL_PART_ID_LARGE_CHUNKS,
+};
+
+#define MLXSW_SP_KVDL_PART_INFO(id) \
+[MLXSW_SP_KVDL_PART_ID_##id] = { \
+ .start_index = MLXSW_SP_KVDL_##id##_BASE, \
+ .end_index = MLXSW_SP_KVDL_##id##_END, \
+ .alloc_size = MLXSW_SP_KVDL_##id##_ALLOC_SIZE, \
+ .resource_id = MLXSW_SP_RESOURCE_KVD_LINEAR_##id, \
+}
+
+static const struct mlxsw_sp_kvdl_part_info mlxsw_sp_kvdl_parts_info[] = {
+ MLXSW_SP_KVDL_PART_INFO(SINGLE),
+ MLXSW_SP_KVDL_PART_INFO(CHUNKS),
+ MLXSW_SP_KVDL_PART_INFO(LARGE_CHUNKS),
+};
+
+#define MLXSW_SP_KVDL_PARTS_INFO_LEN ARRAY_SIZE(mlxsw_sp_kvdl_parts_info)
+
struct mlxsw_sp_kvdl_part {
- struct list_head list;
- const struct mlxsw_sp_kvdl_part_info *info;
+ struct mlxsw_sp_kvdl_part_info info;
unsigned long usage[0]; /* Entries */
};
struct mlxsw_sp_kvdl {
- struct list_head parts_list;
+ struct mlxsw_sp_kvdl_part *parts[MLXSW_SP_KVDL_PARTS_INFO_LEN];
};
static struct mlxsw_sp_kvdl_part *
@@ -80,11 +103,13 @@ mlxsw_sp_kvdl_alloc_size_part(struct mlxsw_sp_kvdl *kvdl,
unsigned int alloc_size)
{
struct mlxsw_sp_kvdl_part *part, *min_part = NULL;
+ int i;
- list_for_each_entry(part, &kvdl->parts_list, list) {
- if (alloc_size <= part->info->alloc_size &&
+ for (i = 0; i < MLXSW_SP_KVDL_PARTS_INFO_LEN; i++) {
+ part = kvdl->parts[i];
+ if (alloc_size <= part->info.alloc_size &&
(!min_part ||
- part->info->alloc_size <= min_part->info->alloc_size))
+ part->info.alloc_size <= min_part->info.alloc_size))
min_part = part;
}
@@ -95,10 +120,12 @@ static struct mlxsw_sp_kvdl_part *
mlxsw_sp_kvdl_index_part(struct mlxsw_sp_kvdl *kvdl, u32 kvdl_index)
{
struct mlxsw_sp_kvdl_part *part;
+ int i;
- list_for_each_entry(part, &kvdl->parts_list, list) {
- if (kvdl_index >= part->info->start_index &&
- kvdl_index <= part->info->end_index)
+ for (i = 0; i < MLXSW_SP_KVDL_PARTS_INFO_LEN; i++) {
+ part = kvdl->parts[i];
+ if (kvdl_index >= part->info.start_index &&
+ kvdl_index <= part->info.end_index)
return part;
}
@@ -122,7 +149,7 @@ mlxsw_sp_kvdl_index_entry_index(const struct mlxsw_sp_kvdl_part_info *info,
static int mlxsw_sp_kvdl_part_alloc(struct mlxsw_sp_kvdl_part *part,
u32 *p_kvdl_index)
{
- const struct mlxsw_sp_kvdl_part_info *info = part->info;
+ const struct mlxsw_sp_kvdl_part_info *info = &part->info;
unsigned int entry_index, nr_entries;
nr_entries = (info->end_index - info->start_index + 1) /
@@ -132,8 +159,7 @@ static int mlxsw_sp_kvdl_part_alloc(struct mlxsw_sp_kvdl_part *part,
return -ENOBUFS;
__set_bit(entry_index, part->usage);
- *p_kvdl_index = mlxsw_sp_entry_index_kvdl_index(part->info,
- entry_index);
+ *p_kvdl_index = mlxsw_sp_entry_index_kvdl_index(info, entry_index);
return 0;
}
@@ -141,10 +167,10 @@ static int mlxsw_sp_kvdl_part_alloc(struct mlxsw_sp_kvdl_part *part,
static void mlxsw_sp_kvdl_part_free(struct mlxsw_sp_kvdl_part *part,
u32 kvdl_index)
{
+ const struct mlxsw_sp_kvdl_part_info *info = &part->info;
unsigned int entry_index;
- entry_index = mlxsw_sp_kvdl_index_entry_index(part->info,
- kvdl_index);
+ entry_index = mlxsw_sp_kvdl_index_entry_index(info, kvdl_index);
__clear_bit(entry_index, part->usage);
}
@@ -183,137 +209,198 @@ int mlxsw_sp_kvdl_alloc_size_query(struct mlxsw_sp *mlxsw_sp,
if (IS_ERR(part))
return PTR_ERR(part);
- *p_alloc_size = part->info->alloc_size;
+ *p_alloc_size = part->info.alloc_size;
return 0;
}
-static const struct mlxsw_sp_kvdl_part_info kvdl_parts_info[] = {
- {
- .part_index = 0,
- .start_index = MLXSW_SP_KVDL_SINGLE_BASE,
- .end_index = MLXSW_SP_KVDL_SINGLE_END,
- .alloc_size = 1,
- },
- {
- .part_index = 1,
- .start_index = MLXSW_SP_KVDL_CHUNKS_BASE,
- .end_index = MLXSW_SP_KVDL_CHUNKS_END,
- .alloc_size = MLXSW_SP_CHUNK_MAX,
- },
- {
- .part_index = 2,
- .start_index = MLXSW_SP_KVDL_LARGE_CHUNKS_BASE,
- .end_index = MLXSW_SP_KVDL_LARGE_CHUNKS_END,
- .alloc_size = MLXSW_SP_LARGE_CHUNK_MAX,
- },
-};
-
-static struct mlxsw_sp_kvdl_part *
-mlxsw_sp_kvdl_part_find(struct mlxsw_sp *mlxsw_sp, unsigned int part_index)
+static void mlxsw_sp_kvdl_part_update(struct mlxsw_sp_kvdl_part *part,
+ struct mlxsw_sp_kvdl_part *part_prev,
+ unsigned int size)
{
- struct mlxsw_sp_kvdl_part *part;
- list_for_each_entry(part, &mlxsw_sp->kvdl->parts_list, list) {
- if (part->info->part_index == part_index)
- return part;
+ if (!part_prev) {
+ part->info.end_index = size - 1;
+ } else {
+ part->info.start_index = part_prev->info.end_index + 1;
+ part->info.end_index = part->info.start_index + size - 1;
}
-
- return NULL;
}
-static int mlxsw_sp_kvdl_part_init(struct mlxsw_sp *mlxsw_sp,
- unsigned int part_index)
+static struct mlxsw_sp_kvdl_part *
+mlxsw_sp_kvdl_part_init(struct mlxsw_sp *mlxsw_sp,
+ const struct mlxsw_sp_kvdl_part_info *info,
+ struct mlxsw_sp_kvdl_part *part_prev)
{
- const struct mlxsw_sp_kvdl_part_info *info;
+ struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
struct mlxsw_sp_kvdl_part *part;
+ bool need_update = true;
unsigned int nr_entries;
size_t usage_size;
+ u64 resource_size;
+ int err;
- info = &kvdl_parts_info[part_index];
+ err = devlink_resource_size_get(devlink, info->resource_id,
+ &resource_size);
+ if (err) {
+ need_update = false;
+ resource_size = info->end_index - info->start_index + 1;
+ }
- nr_entries = (info->end_index - info->start_index + 1) /
- info->alloc_size;
+ nr_entries = div_u64(resource_size, info->alloc_size);
usage_size = BITS_TO_LONGS(nr_entries) * sizeof(unsigned long);
part = kzalloc(sizeof(*part) + usage_size, GFP_KERNEL);
if (!part)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
- part->info = info;
- list_add(&part->list, &mlxsw_sp->kvdl->parts_list);
+ memcpy(&part->info, info, sizeof(part->info));
- return 0;
+ if (need_update)
+ mlxsw_sp_kvdl_part_update(part, part_prev, resource_size);
+ return part;
}
-static void mlxsw_sp_kvdl_part_fini(struct mlxsw_sp *mlxsw_sp,
- unsigned int part_index)
+static void mlxsw_sp_kvdl_part_fini(struct mlxsw_sp_kvdl_part *part)
{
- struct mlxsw_sp_kvdl_part *part;
-
- part = mlxsw_sp_kvdl_part_find(mlxsw_sp, part_index);
- if (!part)
- return;
-
- list_del(&part->list);
kfree(part);
}
static int mlxsw_sp_kvdl_parts_init(struct mlxsw_sp *mlxsw_sp)
{
+ struct mlxsw_sp_kvdl *kvdl = mlxsw_sp->kvdl;
+ const struct mlxsw_sp_kvdl_part_info *info;
+ struct mlxsw_sp_kvdl_part *part_prev = NULL;
int err, i;
- INIT_LIST_HEAD(&mlxsw_sp->kvdl->parts_list);
-
- for (i = 0; i < ARRAY_SIZE(kvdl_parts_info); i++) {
- err = mlxsw_sp_kvdl_part_init(mlxsw_sp, i);
- if (err)
+ for (i = 0; i < MLXSW_SP_KVDL_PARTS_INFO_LEN; i++) {
+ info = &mlxsw_sp_kvdl_parts_info[i];
+ kvdl->parts[i] = mlxsw_sp_kvdl_part_init(mlxsw_sp, info,
+ part_prev);
+ if (IS_ERR(kvdl->parts[i])) {
+ err = PTR_ERR(kvdl->parts[i]);
goto err_kvdl_part_init;
+ }
+ part_prev = kvdl->parts[i];
}
-
return 0;
err_kvdl_part_init:
for (i--; i >= 0; i--)
- mlxsw_sp_kvdl_part_fini(mlxsw_sp, i);
+ mlxsw_sp_kvdl_part_fini(kvdl->parts[i]);
return err;
}
static void mlxsw_sp_kvdl_parts_fini(struct mlxsw_sp *mlxsw_sp)
{
+ struct mlxsw_sp_kvdl *kvdl = mlxsw_sp->kvdl;
int i;
- for (i = ARRAY_SIZE(kvdl_parts_info) - 1; i >= 0; i--)
- mlxsw_sp_kvdl_part_fini(mlxsw_sp, i);
+ for (i = 0; i < MLXSW_SP_KVDL_PARTS_INFO_LEN; i++)
+ mlxsw_sp_kvdl_part_fini(kvdl->parts[i]);
}
static u64 mlxsw_sp_kvdl_part_occ(struct mlxsw_sp_kvdl_part *part)
{
+ const struct mlxsw_sp_kvdl_part_info *info = &part->info;
unsigned int nr_entries;
int bit = -1;
u64 occ = 0;
- nr_entries = (part->info->end_index -
- part->info->start_index + 1) /
- part->info->alloc_size;
+ nr_entries = (info->end_index -
+ info->start_index + 1) /
+ info->alloc_size;
while ((bit = find_next_bit(part->usage, nr_entries, bit + 1))
< nr_entries)
- occ += part->info->alloc_size;
+ occ += info->alloc_size;
return occ;
}
-u64 mlxsw_sp_kvdl_occ_get(const struct mlxsw_sp *mlxsw_sp)
+static u64 mlxsw_sp_kvdl_occ_get(void *priv)
{
- struct mlxsw_sp_kvdl_part *part;
+ const struct mlxsw_sp *mlxsw_sp = priv;
u64 occ = 0;
+ int i;
- list_for_each_entry(part, &mlxsw_sp->kvdl->parts_list, list)
- occ += mlxsw_sp_kvdl_part_occ(part);
+ for (i = 0; i < MLXSW_SP_KVDL_PARTS_INFO_LEN; i++)
+ occ += mlxsw_sp_kvdl_part_occ(mlxsw_sp->kvdl->parts[i]);
return occ;
}
+static u64 mlxsw_sp_kvdl_single_occ_get(void *priv)
+{
+ const struct mlxsw_sp *mlxsw_sp = priv;
+ struct mlxsw_sp_kvdl_part *part;
+
+ part = mlxsw_sp->kvdl->parts[MLXSW_SP_KVDL_PART_ID_SINGLE];
+ return mlxsw_sp_kvdl_part_occ(part);
+}
+
+static u64 mlxsw_sp_kvdl_chunks_occ_get(void *priv)
+{
+ const struct mlxsw_sp *mlxsw_sp = priv;
+ struct mlxsw_sp_kvdl_part *part;
+
+ part = mlxsw_sp->kvdl->parts[MLXSW_SP_KVDL_PART_ID_CHUNKS];
+ return mlxsw_sp_kvdl_part_occ(part);
+}
+
+static u64 mlxsw_sp_kvdl_large_chunks_occ_get(void *priv)
+{
+ const struct mlxsw_sp *mlxsw_sp = priv;
+ struct mlxsw_sp_kvdl_part *part;
+
+ part = mlxsw_sp->kvdl->parts[MLXSW_SP_KVDL_PART_ID_LARGE_CHUNKS];
+ return mlxsw_sp_kvdl_part_occ(part);
+}
+
+int mlxsw_sp_kvdl_resources_register(struct mlxsw_core *mlxsw_core)
+{
+ struct devlink *devlink = priv_to_devlink(mlxsw_core);
+ static struct devlink_resource_size_params size_params;
+ u32 kvdl_max_size;
+ int err;
+
+ kvdl_max_size = MLXSW_CORE_RES_GET(mlxsw_core, KVD_SIZE) -
+ MLXSW_CORE_RES_GET(mlxsw_core, KVD_SINGLE_MIN_SIZE) -
+ MLXSW_CORE_RES_GET(mlxsw_core, KVD_DOUBLE_MIN_SIZE);
+
+ devlink_resource_size_params_init(&size_params, 0, kvdl_max_size,
+ MLXSW_SP_KVDL_SINGLE_ALLOC_SIZE,
+ DEVLINK_RESOURCE_UNIT_ENTRY);
+ err = devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_LINEAR_SINGLES,
+ MLXSW_SP_KVDL_SINGLE_SIZE,
+ MLXSW_SP_RESOURCE_KVD_LINEAR_SINGLE,
+ MLXSW_SP_RESOURCE_KVD_LINEAR,
+ &size_params);
+ if (err)
+ return err;
+
+ devlink_resource_size_params_init(&size_params, 0, kvdl_max_size,
+ MLXSW_SP_KVDL_CHUNKS_ALLOC_SIZE,
+ DEVLINK_RESOURCE_UNIT_ENTRY);
+ err = devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_LINEAR_CHUNKS,
+ MLXSW_SP_KVDL_CHUNKS_SIZE,
+ MLXSW_SP_RESOURCE_KVD_LINEAR_CHUNKS,
+ MLXSW_SP_RESOURCE_KVD_LINEAR,
+ &size_params);
+ if (err)
+ return err;
+
+ devlink_resource_size_params_init(&size_params, 0, kvdl_max_size,
+ MLXSW_SP_KVDL_LARGE_CHUNKS_ALLOC_SIZE,
+ DEVLINK_RESOURCE_UNIT_ENTRY);
+ err = devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_LINEAR_LARGE_CHUNKS,
+ MLXSW_SP_KVDL_LARGE_CHUNKS_SIZE,
+ MLXSW_SP_RESOURCE_KVD_LINEAR_LARGE_CHUNKS,
+ MLXSW_SP_RESOURCE_KVD_LINEAR,
+ &size_params);
+ return err;
+}
+
int mlxsw_sp_kvdl_init(struct mlxsw_sp *mlxsw_sp)
{
+ struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
struct mlxsw_sp_kvdl *kvdl;
int err;
@@ -326,6 +413,23 @@ int mlxsw_sp_kvdl_init(struct mlxsw_sp *mlxsw_sp)
if (err)
goto err_kvdl_parts_init;
+ devlink_resource_occ_get_register(devlink,
+ MLXSW_SP_RESOURCE_KVD_LINEAR,
+ mlxsw_sp_kvdl_occ_get,
+ mlxsw_sp);
+ devlink_resource_occ_get_register(devlink,
+ MLXSW_SP_RESOURCE_KVD_LINEAR_SINGLE,
+ mlxsw_sp_kvdl_single_occ_get,
+ mlxsw_sp);
+ devlink_resource_occ_get_register(devlink,
+ MLXSW_SP_RESOURCE_KVD_LINEAR_CHUNKS,
+ mlxsw_sp_kvdl_chunks_occ_get,
+ mlxsw_sp);
+ devlink_resource_occ_get_register(devlink,
+ MLXSW_SP_RESOURCE_KVD_LINEAR_LARGE_CHUNKS,
+ mlxsw_sp_kvdl_large_chunks_occ_get,
+ mlxsw_sp);
+
return 0;
err_kvdl_parts_init:
@@ -335,6 +439,16 @@ err_kvdl_parts_init:
void mlxsw_sp_kvdl_fini(struct mlxsw_sp *mlxsw_sp)
{
+ struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
+
+ devlink_resource_occ_get_unregister(devlink,
+ MLXSW_SP_RESOURCE_KVD_LINEAR_LARGE_CHUNKS);
+ devlink_resource_occ_get_unregister(devlink,
+ MLXSW_SP_RESOURCE_KVD_LINEAR_CHUNKS);
+ devlink_resource_occ_get_unregister(devlink,
+ MLXSW_SP_RESOURCE_KVD_LINEAR_SINGLE);
+ devlink_resource_occ_get_unregister(devlink,
+ MLXSW_SP_RESOURCE_KVD_LINEAR);
mlxsw_sp_kvdl_parts_fini(mlxsw_sp);
kfree(mlxsw_sp->kvdl);
}
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c
index d20b143de3b4..a82539609d49 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c
@@ -33,6 +33,7 @@
*/
#include <linux/rhashtable.h>
+#include <net/ipv6.h>
#include "spectrum_mr.h"
#include "spectrum_router.h"
@@ -47,6 +48,11 @@ struct mlxsw_sp_mr {
/* priv has to be always the last item */
};
+struct mlxsw_sp_mr_vif;
+struct mlxsw_sp_mr_vif_ops {
+ bool (*is_regular)(const struct mlxsw_sp_mr_vif *vif);
+};
+
struct mlxsw_sp_mr_vif {
struct net_device *dev;
const struct mlxsw_sp_rif *rif;
@@ -61,6 +67,9 @@ struct mlxsw_sp_mr_vif {
* instance is used as an ingress VIF
*/
struct list_head route_ivif_list;
+
+ /* Protocol specific operations for a VIF */
+ const struct mlxsw_sp_mr_vif_ops *ops;
};
struct mlxsw_sp_mr_route_vif_entry {
@@ -70,6 +79,17 @@ struct mlxsw_sp_mr_route_vif_entry {
struct mlxsw_sp_mr_route *mr_route;
};
+struct mlxsw_sp_mr_table;
+struct mlxsw_sp_mr_table_ops {
+ bool (*is_route_valid)(const struct mlxsw_sp_mr_table *mr_table,
+ const struct mr_mfc *mfc);
+ void (*key_create)(struct mlxsw_sp_mr_table *mr_table,
+ struct mlxsw_sp_mr_route_key *key,
+ struct mr_mfc *mfc);
+ bool (*is_route_starg)(const struct mlxsw_sp_mr_table *mr_table,
+ const struct mlxsw_sp_mr_route *mr_route);
+};
+
struct mlxsw_sp_mr_table {
struct list_head node;
enum mlxsw_sp_l3proto proto;
@@ -78,6 +98,7 @@ struct mlxsw_sp_mr_table {
struct mlxsw_sp_mr_vif vifs[MAXVIFS];
struct list_head route_list;
struct rhashtable route_ht;
+ const struct mlxsw_sp_mr_table_ops *ops;
char catchall_route_priv[0];
/* catchall_route_priv has to be always the last item */
};
@@ -88,7 +109,7 @@ struct mlxsw_sp_mr_route {
struct mlxsw_sp_mr_route_key key;
enum mlxsw_sp_mr_route_action route_action;
u16 min_mtu;
- struct mfc_cache *mfc4;
+ struct mr_mfc *mfc;
void *route_priv;
const struct mlxsw_sp_mr_table *mr_table;
/* A list of route_vif_entry structs that point to the egress VIFs */
@@ -104,14 +125,9 @@ static const struct rhashtable_params mlxsw_sp_mr_route_ht_params = {
.automatic_shrinking = true,
};
-static bool mlxsw_sp_mr_vif_regular(const struct mlxsw_sp_mr_vif *vif)
-{
- return !(vif->vif_flags & (VIFF_TUNNEL | VIFF_REGISTER));
-}
-
static bool mlxsw_sp_mr_vif_valid(const struct mlxsw_sp_mr_vif *vif)
{
- return mlxsw_sp_mr_vif_regular(vif) && vif->dev && vif->rif;
+ return vif->ops->is_regular(vif) && vif->dev && vif->rif;
}
static bool mlxsw_sp_mr_vif_exists(const struct mlxsw_sp_mr_vif *vif)
@@ -122,18 +138,9 @@ static bool mlxsw_sp_mr_vif_exists(const struct mlxsw_sp_mr_vif *vif)
static bool
mlxsw_sp_mr_route_ivif_in_evifs(const struct mlxsw_sp_mr_route *mr_route)
{
- vifi_t ivif;
+ vifi_t ivif = mr_route->mfc->mfc_parent;
- switch (mr_route->mr_table->proto) {
- case MLXSW_SP_L3_PROTO_IPV4:
- ivif = mr_route->mfc4->mfc_parent;
- return mr_route->mfc4->mfc_un.res.ttls[ivif] != 255;
- case MLXSW_SP_L3_PROTO_IPV6:
- /* fall through */
- default:
- WARN_ON_ONCE(1);
- }
- return false;
+ return mr_route->mfc->mfc_un.res.ttls[ivif] != 255;
}
static int
@@ -149,19 +156,6 @@ mlxsw_sp_mr_route_valid_evifs_num(const struct mlxsw_sp_mr_route *mr_route)
return valid_evifs;
}
-static bool mlxsw_sp_mr_route_starg(const struct mlxsw_sp_mr_route *mr_route)
-{
- switch (mr_route->mr_table->proto) {
- case MLXSW_SP_L3_PROTO_IPV4:
- return mr_route->key.source_mask.addr4 == htonl(INADDR_ANY);
- case MLXSW_SP_L3_PROTO_IPV6:
- /* fall through */
- default:
- WARN_ON_ONCE(1);
- }
- return false;
-}
-
static enum mlxsw_sp_mr_route_action
mlxsw_sp_mr_route_action(const struct mlxsw_sp_mr_route *mr_route)
{
@@ -174,7 +168,8 @@ mlxsw_sp_mr_route_action(const struct mlxsw_sp_mr_route *mr_route)
/* The kernel does not match a (*,G) route that the ingress interface is
* not one of the egress interfaces, so trap these kind of routes.
*/
- if (mlxsw_sp_mr_route_starg(mr_route) &&
+ if (mr_route->mr_table->ops->is_route_starg(mr_route->mr_table,
+ mr_route) &&
!mlxsw_sp_mr_route_ivif_in_evifs(mr_route))
return MLXSW_SP_MR_ROUTE_ACTION_TRAP;
@@ -195,25 +190,11 @@ mlxsw_sp_mr_route_action(const struct mlxsw_sp_mr_route *mr_route)
static enum mlxsw_sp_mr_route_prio
mlxsw_sp_mr_route_prio(const struct mlxsw_sp_mr_route *mr_route)
{
- return mlxsw_sp_mr_route_starg(mr_route) ?
+ return mr_route->mr_table->ops->is_route_starg(mr_route->mr_table,
+ mr_route) ?
MLXSW_SP_MR_ROUTE_PRIO_STARG : MLXSW_SP_MR_ROUTE_PRIO_SG;
}
-static void mlxsw_sp_mr_route4_key(struct mlxsw_sp_mr_table *mr_table,
- struct mlxsw_sp_mr_route_key *key,
- const struct mfc_cache *mfc)
-{
- bool starg = (mfc->mfc_origin == htonl(INADDR_ANY));
-
- memset(key, 0, sizeof(*key));
- key->vrid = mr_table->vr_id;
- key->proto = mr_table->proto;
- key->group.addr4 = mfc->mfc_mcastgrp;
- key->group_mask.addr4 = htonl(0xffffffff);
- key->source.addr4 = mfc->mfc_origin;
- key->source_mask.addr4 = htonl(starg ? 0 : 0xffffffff);
-}
-
static int mlxsw_sp_mr_route_evif_link(struct mlxsw_sp_mr_route *mr_route,
struct mlxsw_sp_mr_vif *mr_vif)
{
@@ -343,8 +324,8 @@ static void mlxsw_sp_mr_route_erase(struct mlxsw_sp_mr_table *mr_table,
}
static struct mlxsw_sp_mr_route *
-mlxsw_sp_mr_route4_create(struct mlxsw_sp_mr_table *mr_table,
- struct mfc_cache *mfc)
+mlxsw_sp_mr_route_create(struct mlxsw_sp_mr_table *mr_table,
+ struct mr_mfc *mfc)
{
struct mlxsw_sp_mr_route_vif_entry *rve, *tmp;
struct mlxsw_sp_mr_route *mr_route;
@@ -356,12 +337,13 @@ mlxsw_sp_mr_route4_create(struct mlxsw_sp_mr_table *mr_table,
if (!mr_route)
return ERR_PTR(-ENOMEM);
INIT_LIST_HEAD(&mr_route->evif_list);
- mlxsw_sp_mr_route4_key(mr_table, &mr_route->key, mfc);
/* Find min_mtu and link iVIF and eVIFs */
mr_route->min_mtu = ETH_MAX_MTU;
- ipmr_cache_hold(mfc);
- mr_route->mfc4 = mfc;
+ mr_cache_hold(mfc);
+ mr_route->mfc = mfc;
+ mr_table->ops->key_create(mr_table, &mr_route->key, mr_route->mfc);
+
mr_route->mr_table = mr_table;
for (i = 0; i < MAXVIFS; i++) {
if (mfc->mfc_un.res.ttls[i] != 255) {
@@ -374,59 +356,38 @@ mlxsw_sp_mr_route4_create(struct mlxsw_sp_mr_table *mr_table,
mr_route->min_mtu = mr_table->vifs[i].dev->mtu;
}
}
- mlxsw_sp_mr_route_ivif_link(mr_route, &mr_table->vifs[mfc->mfc_parent]);
+ mlxsw_sp_mr_route_ivif_link(mr_route,
+ &mr_table->vifs[mfc->mfc_parent]);
mr_route->route_action = mlxsw_sp_mr_route_action(mr_route);
return mr_route;
err:
- ipmr_cache_put(mfc);
+ mr_cache_put(mfc);
list_for_each_entry_safe(rve, tmp, &mr_route->evif_list, route_node)
mlxsw_sp_mr_route_evif_unlink(rve);
kfree(mr_route);
return ERR_PTR(err);
}
-static void mlxsw_sp_mr_route4_destroy(struct mlxsw_sp_mr_table *mr_table,
- struct mlxsw_sp_mr_route *mr_route)
+static void mlxsw_sp_mr_route_destroy(struct mlxsw_sp_mr_table *mr_table,
+ struct mlxsw_sp_mr_route *mr_route)
{
struct mlxsw_sp_mr_route_vif_entry *rve, *tmp;
mlxsw_sp_mr_route_ivif_unlink(mr_route);
- ipmr_cache_put(mr_route->mfc4);
+ mr_cache_put(mr_route->mfc);
list_for_each_entry_safe(rve, tmp, &mr_route->evif_list, route_node)
mlxsw_sp_mr_route_evif_unlink(rve);
kfree(mr_route);
}
-static void mlxsw_sp_mr_route_destroy(struct mlxsw_sp_mr_table *mr_table,
- struct mlxsw_sp_mr_route *mr_route)
-{
- switch (mr_table->proto) {
- case MLXSW_SP_L3_PROTO_IPV4:
- mlxsw_sp_mr_route4_destroy(mr_table, mr_route);
- break;
- case MLXSW_SP_L3_PROTO_IPV6:
- /* fall through */
- default:
- WARN_ON_ONCE(1);
- }
-}
-
static void mlxsw_sp_mr_mfc_offload_set(struct mlxsw_sp_mr_route *mr_route,
bool offload)
{
- switch (mr_route->mr_table->proto) {
- case MLXSW_SP_L3_PROTO_IPV4:
- if (offload)
- mr_route->mfc4->mfc_flags |= MFC_OFFLOAD;
- else
- mr_route->mfc4->mfc_flags &= ~MFC_OFFLOAD;
- break;
- case MLXSW_SP_L3_PROTO_IPV6:
- /* fall through */
- default:
- WARN_ON_ONCE(1);
- }
+ if (offload)
+ mr_route->mfc->mfc_flags |= MFC_OFFLOAD;
+ else
+ mr_route->mfc->mfc_flags &= ~MFC_OFFLOAD;
}
static void mlxsw_sp_mr_mfc_offload_update(struct mlxsw_sp_mr_route *mr_route)
@@ -448,25 +409,18 @@ static void __mlxsw_sp_mr_route_del(struct mlxsw_sp_mr_table *mr_table,
mlxsw_sp_mr_route_destroy(mr_table, mr_route);
}
-int mlxsw_sp_mr_route4_add(struct mlxsw_sp_mr_table *mr_table,
- struct mfc_cache *mfc, bool replace)
+int mlxsw_sp_mr_route_add(struct mlxsw_sp_mr_table *mr_table,
+ struct mr_mfc *mfc, bool replace)
{
struct mlxsw_sp_mr_route *mr_orig_route = NULL;
struct mlxsw_sp_mr_route *mr_route;
int err;
- /* If the route is a (*,*) route, abort, as these kind of routes are
- * used for proxy routes.
- */
- if (mfc->mfc_origin == htonl(INADDR_ANY) &&
- mfc->mfc_mcastgrp == htonl(INADDR_ANY)) {
- dev_warn(mr_table->mlxsw_sp->bus_info->dev,
- "Offloading proxy routes is not supported.\n");
+ if (!mr_table->ops->is_route_valid(mr_table, mfc))
return -EINVAL;
- }
/* Create a new route */
- mr_route = mlxsw_sp_mr_route4_create(mr_table, mfc);
+ mr_route = mlxsw_sp_mr_route_create(mr_table, mfc);
if (IS_ERR(mr_route))
return PTR_ERR(mr_route);
@@ -511,7 +465,7 @@ int mlxsw_sp_mr_route4_add(struct mlxsw_sp_mr_table *mr_table,
&mr_orig_route->ht_node,
mlxsw_sp_mr_route_ht_params);
list_del(&mr_orig_route->node);
- mlxsw_sp_mr_route4_destroy(mr_table, mr_orig_route);
+ mlxsw_sp_mr_route_destroy(mr_table, mr_orig_route);
}
mlxsw_sp_mr_mfc_offload_update(mr_route);
@@ -524,17 +478,17 @@ err_rhashtable_insert:
list_del(&mr_route->node);
err_no_orig_route:
err_duplicate_route:
- mlxsw_sp_mr_route4_destroy(mr_table, mr_route);
+ mlxsw_sp_mr_route_destroy(mr_table, mr_route);
return err;
}
-void mlxsw_sp_mr_route4_del(struct mlxsw_sp_mr_table *mr_table,
- struct mfc_cache *mfc)
+void mlxsw_sp_mr_route_del(struct mlxsw_sp_mr_table *mr_table,
+ struct mr_mfc *mfc)
{
struct mlxsw_sp_mr_route *mr_route;
struct mlxsw_sp_mr_route_key key;
- mlxsw_sp_mr_route4_key(mr_table, &key, mfc);
+ mr_table->ops->key_create(mr_table, &key, mfc);
mr_route = rhashtable_lookup_fast(&mr_table->route_ht, &key,
mlxsw_sp_mr_route_ht_params);
if (mr_route)
@@ -839,6 +793,125 @@ void mlxsw_sp_mr_rif_mtu_update(struct mlxsw_sp_mr_table *mr_table,
}
}
+/* Protocol specific functions */
+static bool
+mlxsw_sp_mr_route4_validate(const struct mlxsw_sp_mr_table *mr_table,
+ const struct mr_mfc *c)
+{
+ struct mfc_cache *mfc = (struct mfc_cache *) c;
+
+ /* If the route is a (*,*) route, abort, as these kind of routes are
+ * used for proxy routes.
+ */
+ if (mfc->mfc_origin == htonl(INADDR_ANY) &&
+ mfc->mfc_mcastgrp == htonl(INADDR_ANY)) {
+ dev_warn(mr_table->mlxsw_sp->bus_info->dev,
+ "Offloading proxy routes is not supported.\n");
+ return false;
+ }
+ return true;
+}
+
+static void mlxsw_sp_mr_route4_key(struct mlxsw_sp_mr_table *mr_table,
+ struct mlxsw_sp_mr_route_key *key,
+ struct mr_mfc *c)
+{
+ const struct mfc_cache *mfc = (struct mfc_cache *) c;
+ bool starg;
+
+ starg = (mfc->mfc_origin == htonl(INADDR_ANY));
+
+ memset(key, 0, sizeof(*key));
+ key->vrid = mr_table->vr_id;
+ key->proto = MLXSW_SP_L3_PROTO_IPV4;
+ key->group.addr4 = mfc->mfc_mcastgrp;
+ key->group_mask.addr4 = htonl(0xffffffff);
+ key->source.addr4 = mfc->mfc_origin;
+ key->source_mask.addr4 = htonl(starg ? 0 : 0xffffffff);
+}
+
+static bool mlxsw_sp_mr_route4_starg(const struct mlxsw_sp_mr_table *mr_table,
+ const struct mlxsw_sp_mr_route *mr_route)
+{
+ return mr_route->key.source_mask.addr4 == htonl(INADDR_ANY);
+}
+
+static bool mlxsw_sp_mr_vif4_is_regular(const struct mlxsw_sp_mr_vif *vif)
+{
+ return !(vif->vif_flags & (VIFF_TUNNEL | VIFF_REGISTER));
+}
+
+static bool
+mlxsw_sp_mr_route6_validate(const struct mlxsw_sp_mr_table *mr_table,
+ const struct mr_mfc *c)
+{
+ struct mfc6_cache *mfc = (struct mfc6_cache *) c;
+
+ /* If the route is a (*,*) route, abort, as these kind of routes are
+ * used for proxy routes.
+ */
+ if (ipv6_addr_any(&mfc->mf6c_origin) &&
+ ipv6_addr_any(&mfc->mf6c_mcastgrp)) {
+ dev_warn(mr_table->mlxsw_sp->bus_info->dev,
+ "Offloading proxy routes is not supported.\n");
+ return false;
+ }
+ return true;
+}
+
+static void mlxsw_sp_mr_route6_key(struct mlxsw_sp_mr_table *mr_table,
+ struct mlxsw_sp_mr_route_key *key,
+ struct mr_mfc *c)
+{
+ const struct mfc6_cache *mfc = (struct mfc6_cache *) c;
+
+ memset(key, 0, sizeof(*key));
+ key->vrid = mr_table->vr_id;
+ key->proto = MLXSW_SP_L3_PROTO_IPV6;
+ key->group.addr6 = mfc->mf6c_mcastgrp;
+ memset(&key->group_mask.addr6, 0xff, sizeof(key->group_mask.addr6));
+ key->source.addr6 = mfc->mf6c_origin;
+ if (!ipv6_addr_any(&mfc->mf6c_origin))
+ memset(&key->source_mask.addr6, 0xff,
+ sizeof(key->source_mask.addr6));
+}
+
+static bool mlxsw_sp_mr_route6_starg(const struct mlxsw_sp_mr_table *mr_table,
+ const struct mlxsw_sp_mr_route *mr_route)
+{
+ return ipv6_addr_any(&mr_route->key.source_mask.addr6);
+}
+
+static bool mlxsw_sp_mr_vif6_is_regular(const struct mlxsw_sp_mr_vif *vif)
+{
+ return !(vif->vif_flags & MIFF_REGISTER);
+}
+
+static struct
+mlxsw_sp_mr_vif_ops mlxsw_sp_mr_vif_ops_arr[] = {
+ {
+ .is_regular = mlxsw_sp_mr_vif4_is_regular,
+ },
+ {
+ .is_regular = mlxsw_sp_mr_vif6_is_regular,
+ },
+};
+
+static struct
+mlxsw_sp_mr_table_ops mlxsw_sp_mr_table_ops_arr[] = {
+ {
+ .is_route_valid = mlxsw_sp_mr_route4_validate,
+ .key_create = mlxsw_sp_mr_route4_key,
+ .is_route_starg = mlxsw_sp_mr_route4_starg,
+ },
+ {
+ .is_route_valid = mlxsw_sp_mr_route6_validate,
+ .key_create = mlxsw_sp_mr_route6_key,
+ .is_route_starg = mlxsw_sp_mr_route6_starg,
+ },
+
+};
+
struct mlxsw_sp_mr_table *mlxsw_sp_mr_table_create(struct mlxsw_sp *mlxsw_sp,
u32 vr_id,
enum mlxsw_sp_l3proto proto)
@@ -847,6 +920,7 @@ struct mlxsw_sp_mr_table *mlxsw_sp_mr_table_create(struct mlxsw_sp *mlxsw_sp,
.prio = MLXSW_SP_MR_ROUTE_PRIO_CATCHALL,
.key = {
.vrid = vr_id,
+ .proto = proto,
},
.value = {
.route_action = MLXSW_SP_MR_ROUTE_ACTION_TRAP,
@@ -865,6 +939,7 @@ struct mlxsw_sp_mr_table *mlxsw_sp_mr_table_create(struct mlxsw_sp *mlxsw_sp,
mr_table->vr_id = vr_id;
mr_table->mlxsw_sp = mlxsw_sp;
mr_table->proto = proto;
+ mr_table->ops = &mlxsw_sp_mr_table_ops_arr[proto];
INIT_LIST_HEAD(&mr_table->route_list);
err = rhashtable_init(&mr_table->route_ht,
@@ -875,6 +950,7 @@ struct mlxsw_sp_mr_table *mlxsw_sp_mr_table_create(struct mlxsw_sp *mlxsw_sp,
for (i = 0; i < MAXVIFS; i++) {
INIT_LIST_HEAD(&mr_table->vifs[i].route_evif_list);
INIT_LIST_HEAD(&mr_table->vifs[i].route_ivif_list);
+ mr_table->vifs[i].ops = &mlxsw_sp_mr_vif_ops_arr[proto];
}
err = mr->mr_ops->route_create(mlxsw_sp, mr->priv,
@@ -941,18 +1017,10 @@ static void mlxsw_sp_mr_route_stats_update(struct mlxsw_sp *mlxsw_sp,
mr->mr_ops->route_stats(mlxsw_sp, mr_route->route_priv, &packets,
&bytes);
- switch (mr_route->mr_table->proto) {
- case MLXSW_SP_L3_PROTO_IPV4:
- if (mr_route->mfc4->mfc_un.res.pkt != packets)
- mr_route->mfc4->mfc_un.res.lastuse = jiffies;
- mr_route->mfc4->mfc_un.res.pkt = packets;
- mr_route->mfc4->mfc_un.res.bytes = bytes;
- break;
- case MLXSW_SP_L3_PROTO_IPV6:
- /* fall through */
- default:
- WARN_ON_ONCE(1);
- }
+ if (mr_route->mfc->mfc_un.res.pkt != packets)
+ mr_route->mfc->mfc_un.res.lastuse = jiffies;
+ mr_route->mfc->mfc_un.res.pkt = packets;
+ mr_route->mfc->mfc_un.res.bytes = bytes;
}
static void mlxsw_sp_mr_stats_update(struct work_struct *work)
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.h
index 5d26a122af49..7c864a86811d 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.h
@@ -36,6 +36,7 @@
#define _MLXSW_SPECTRUM_MCROUTER_H
#include <linux/mroute.h>
+#include <linux/mroute6.h>
#include "spectrum_router.h"
#include "spectrum.h"
@@ -109,10 +110,10 @@ struct mlxsw_sp_mr_table;
int mlxsw_sp_mr_init(struct mlxsw_sp *mlxsw_sp,
const struct mlxsw_sp_mr_ops *mr_ops);
void mlxsw_sp_mr_fini(struct mlxsw_sp *mlxsw_sp);
-int mlxsw_sp_mr_route4_add(struct mlxsw_sp_mr_table *mr_table,
- struct mfc_cache *mfc, bool replace);
-void mlxsw_sp_mr_route4_del(struct mlxsw_sp_mr_table *mr_table,
- struct mfc_cache *mfc);
+int mlxsw_sp_mr_route_add(struct mlxsw_sp_mr_table *mr_table,
+ struct mr_mfc *mfc, bool replace);
+void mlxsw_sp_mr_route_del(struct mlxsw_sp_mr_table *mr_table,
+ struct mr_mfc *mfc);
int mlxsw_sp_mr_vif_add(struct mlxsw_sp_mr_table *mr_table,
struct net_device *dev, vifi_t vif_index,
unsigned long vif_flags,
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr_tcam.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr_tcam.c
index 4c7f32d4288d..4f4c0d311883 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr_tcam.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr_tcam.c
@@ -51,7 +51,7 @@ struct mlxsw_sp_mr_tcam_region {
};
struct mlxsw_sp_mr_tcam {
- struct mlxsw_sp_mr_tcam_region ipv4_tcam_region;
+ struct mlxsw_sp_mr_tcam_region tcam_regions[MLXSW_SP_L3_PROTO_MAX];
};
/* This struct maps to one RIGR2 register entry */
@@ -316,20 +316,37 @@ static int mlxsw_sp_mr_tcam_route_replace(struct mlxsw_sp *mlxsw_sp,
mlxsw_afa_block_first_set(afa_block));
break;
case MLXSW_SP_L3_PROTO_IPV6:
- default:
- WARN_ON_ONCE(1);
+ mlxsw_reg_rmft2_ipv6_pack(rmft2_pl, true, parman_item->index,
+ key->vrid,
+ MLXSW_REG_RMFT2_IRIF_MASK_IGNORE, 0,
+ key->group.addr6,
+ key->group_mask.addr6,
+ key->source.addr6,
+ key->source_mask.addr6,
+ mlxsw_afa_block_first_set(afa_block));
}
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rmft2), rmft2_pl);
}
static int mlxsw_sp_mr_tcam_route_remove(struct mlxsw_sp *mlxsw_sp, int vrid,
+ struct mlxsw_sp_mr_route_key *key,
struct parman_item *parman_item)
{
+ struct in6_addr zero_addr = IN6ADDR_ANY_INIT;
char rmft2_pl[MLXSW_REG_RMFT2_LEN];
- mlxsw_reg_rmft2_ipv4_pack(rmft2_pl, false, parman_item->index, vrid,
- 0, 0, 0, 0, 0, 0, NULL);
+ switch (key->proto) {
+ case MLXSW_SP_L3_PROTO_IPV4:
+ mlxsw_reg_rmft2_ipv4_pack(rmft2_pl, false, parman_item->index,
+ vrid, 0, 0, 0, 0, 0, 0, NULL);
+ break;
+ case MLXSW_SP_L3_PROTO_IPV6:
+ mlxsw_reg_rmft2_ipv6_pack(rmft2_pl, false, parman_item->index,
+ vrid, 0, 0, zero_addr, zero_addr,
+ zero_addr, zero_addr, NULL);
+ break;
+ }
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rmft2), rmft2_pl);
}
@@ -353,27 +370,30 @@ mlxsw_sp_mr_tcam_erif_populate(struct mlxsw_sp *mlxsw_sp,
return 0;
}
+static struct mlxsw_sp_mr_tcam_region *
+mlxsw_sp_mr_tcam_protocol_region(struct mlxsw_sp_mr_tcam *mr_tcam,
+ enum mlxsw_sp_l3proto proto)
+{
+ return &mr_tcam->tcam_regions[proto];
+}
+
static int
mlxsw_sp_mr_tcam_route_parman_item_add(struct mlxsw_sp_mr_tcam *mr_tcam,
struct mlxsw_sp_mr_tcam_route *route,
enum mlxsw_sp_mr_route_prio prio)
{
- struct parman_prio *parman_prio = NULL;
+ struct mlxsw_sp_mr_tcam_region *tcam_region;
int err;
- switch (route->key.proto) {
- case MLXSW_SP_L3_PROTO_IPV4:
- parman_prio = &mr_tcam->ipv4_tcam_region.parman_prios[prio];
- err = parman_item_add(mr_tcam->ipv4_tcam_region.parman,
- parman_prio, &route->parman_item);
- if (err)
- return err;
- break;
- case MLXSW_SP_L3_PROTO_IPV6:
- default:
- WARN_ON_ONCE(1);
- }
- route->parman_prio = parman_prio;
+ tcam_region = mlxsw_sp_mr_tcam_protocol_region(mr_tcam,
+ route->key.proto);
+ err = parman_item_add(tcam_region->parman,
+ &tcam_region->parman_prios[prio],
+ &route->parman_item);
+ if (err)
+ return err;
+
+ route->parman_prio = &tcam_region->parman_prios[prio];
return 0;
}
@@ -381,15 +401,13 @@ static void
mlxsw_sp_mr_tcam_route_parman_item_remove(struct mlxsw_sp_mr_tcam *mr_tcam,
struct mlxsw_sp_mr_tcam_route *route)
{
- switch (route->key.proto) {
- case MLXSW_SP_L3_PROTO_IPV4:
- parman_item_remove(mr_tcam->ipv4_tcam_region.parman,
- route->parman_prio, &route->parman_item);
- break;
- case MLXSW_SP_L3_PROTO_IPV6:
- default:
- WARN_ON_ONCE(1);
- }
+ struct mlxsw_sp_mr_tcam_region *tcam_region;
+
+ tcam_region = mlxsw_sp_mr_tcam_protocol_region(mr_tcam,
+ route->key.proto);
+
+ parman_item_remove(tcam_region->parman,
+ route->parman_prio, &route->parman_item);
}
static int
@@ -462,7 +480,7 @@ static void mlxsw_sp_mr_tcam_route_destroy(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_mr_tcam *mr_tcam = priv;
mlxsw_sp_mr_tcam_route_remove(mlxsw_sp, route->key.vrid,
- &route->parman_item);
+ &route->key, &route->parman_item);
mlxsw_sp_mr_tcam_route_parman_item_remove(mr_tcam, route);
mlxsw_sp_mr_tcam_afa_block_destroy(route->afa_block);
mlxsw_sp_flow_counter_free(mlxsw_sp, route->counter_index);
@@ -806,21 +824,42 @@ mlxsw_sp_mr_tcam_region_fini(struct mlxsw_sp_mr_tcam_region *mr_tcam_region)
static int mlxsw_sp_mr_tcam_init(struct mlxsw_sp *mlxsw_sp, void *priv)
{
struct mlxsw_sp_mr_tcam *mr_tcam = priv;
+ struct mlxsw_sp_mr_tcam_region *region = &mr_tcam->tcam_regions[0];
+ u32 rtar_key;
+ int err;
if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MC_ERIF_LIST_ENTRIES) ||
!MLXSW_CORE_RES_VALID(mlxsw_sp->core, ACL_MAX_TCAM_RULES))
return -EIO;
- return mlxsw_sp_mr_tcam_region_init(mlxsw_sp,
- &mr_tcam->ipv4_tcam_region,
- MLXSW_REG_RTAR_KEY_TYPE_IPV4_MULTICAST);
+ rtar_key = MLXSW_REG_RTAR_KEY_TYPE_IPV4_MULTICAST;
+ err = mlxsw_sp_mr_tcam_region_init(mlxsw_sp,
+ &region[MLXSW_SP_L3_PROTO_IPV4],
+ rtar_key);
+ if (err)
+ return err;
+
+ rtar_key = MLXSW_REG_RTAR_KEY_TYPE_IPV6_MULTICAST;
+ err = mlxsw_sp_mr_tcam_region_init(mlxsw_sp,
+ &region[MLXSW_SP_L3_PROTO_IPV6],
+ rtar_key);
+ if (err)
+ goto err_ipv6_region_init;
+
+ return 0;
+
+err_ipv6_region_init:
+ mlxsw_sp_mr_tcam_region_fini(&region[MLXSW_SP_L3_PROTO_IPV4]);
+ return err;
}
static void mlxsw_sp_mr_tcam_fini(void *priv)
{
struct mlxsw_sp_mr_tcam *mr_tcam = priv;
+ struct mlxsw_sp_mr_tcam_region *region = &mr_tcam->tcam_regions[0];
- mlxsw_sp_mr_tcam_region_fini(&mr_tcam->ipv4_tcam_region);
+ mlxsw_sp_mr_tcam_region_fini(&region[MLXSW_SP_L3_PROTO_IPV6]);
+ mlxsw_sp_mr_tcam_region_fini(&region[MLXSW_SP_L3_PROTO_IPV4]);
}
const struct mlxsw_sp_mr_ops mlxsw_sp_mr_tcam_ops = {
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c
index 0b7670459051..91262b0573e3 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c
@@ -42,6 +42,8 @@
#include "reg.h"
#define MLXSW_SP_PRIO_BAND_TO_TCLASS(band) (IEEE_8021QAZ_MAX_TCS - band - 1)
+#define MLXSW_SP_PRIO_CHILD_TO_TCLASS(child) \
+ MLXSW_SP_PRIO_BAND_TO_TCLASS((child - 1))
enum mlxsw_sp_qdisc_type {
MLXSW_SP_QDISC_NO_QDISC,
@@ -76,6 +78,7 @@ struct mlxsw_sp_qdisc_ops {
struct mlxsw_sp_qdisc {
u32 handle;
u8 tclass_num;
+ u8 prio_bitmap;
union {
struct red_stats red;
} xstats_base;
@@ -99,6 +102,44 @@ mlxsw_sp_qdisc_compare(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, u32 handle,
mlxsw_sp_qdisc->handle == handle;
}
+static struct mlxsw_sp_qdisc *
+mlxsw_sp_qdisc_find(struct mlxsw_sp_port *mlxsw_sp_port, u32 parent,
+ bool root_only)
+{
+ int tclass, child_index;
+
+ if (parent == TC_H_ROOT)
+ return mlxsw_sp_port->root_qdisc;
+
+ if (root_only || !mlxsw_sp_port->root_qdisc ||
+ !mlxsw_sp_port->root_qdisc->ops ||
+ TC_H_MAJ(parent) != mlxsw_sp_port->root_qdisc->handle ||
+ TC_H_MIN(parent) > IEEE_8021QAZ_MAX_TCS)
+ return NULL;
+
+ child_index = TC_H_MIN(parent);
+ tclass = MLXSW_SP_PRIO_CHILD_TO_TCLASS(child_index);
+ return &mlxsw_sp_port->tclass_qdiscs[tclass];
+}
+
+static struct mlxsw_sp_qdisc *
+mlxsw_sp_qdisc_find_by_handle(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle)
+{
+ int i;
+
+ if (mlxsw_sp_port->root_qdisc->handle == handle)
+ return mlxsw_sp_port->root_qdisc;
+
+ if (mlxsw_sp_port->root_qdisc->handle == TC_H_UNSPEC)
+ return NULL;
+
+ for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
+ if (mlxsw_sp_port->tclass_qdiscs[i].handle == handle)
+ return &mlxsw_sp_port->tclass_qdiscs[i];
+
+ return NULL;
+}
+
static int
mlxsw_sp_qdisc_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
@@ -185,6 +226,23 @@ mlxsw_sp_qdisc_get_xstats(struct mlxsw_sp_port *mlxsw_sp_port,
return -EOPNOTSUPP;
}
+static void
+mlxsw_sp_qdisc_bstats_per_priority_get(struct mlxsw_sp_port_xstats *xstats,
+ u8 prio_bitmap, u64 *tx_packets,
+ u64 *tx_bytes)
+{
+ int i;
+
+ *tx_packets = 0;
+ *tx_bytes = 0;
+ for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
+ if (prio_bitmap & BIT(i)) {
+ *tx_packets += xstats->tx_packets[i];
+ *tx_bytes += xstats->tx_bytes[i];
+ }
+ }
+}
+
static int
mlxsw_sp_tclass_congestion_enable(struct mlxsw_sp_port *mlxsw_sp_port,
int tclass_num, u32 min, u32 max,
@@ -230,17 +288,16 @@ mlxsw_sp_setup_tc_qdisc_red_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
struct mlxsw_sp_qdisc_stats *stats_base;
struct mlxsw_sp_port_xstats *xstats;
- struct rtnl_link_stats64 *stats;
struct red_stats *red_base;
xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
- stats = &mlxsw_sp_port->periodic_hw_stats.stats;
stats_base = &mlxsw_sp_qdisc->stats_base;
red_base = &mlxsw_sp_qdisc->xstats_base.red;
- stats_base->tx_packets = stats->tx_packets;
- stats_base->tx_bytes = stats->tx_bytes;
-
+ mlxsw_sp_qdisc_bstats_per_priority_get(xstats,
+ mlxsw_sp_qdisc->prio_bitmap,
+ &stats_base->tx_packets,
+ &stats_base->tx_bytes);
red_base->prob_mark = xstats->ecn;
red_base->prob_drop = xstats->wred_drop[tclass_num];
red_base->pdrop = xstats->tail_drop[tclass_num];
@@ -255,6 +312,12 @@ static int
mlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
{
+ struct mlxsw_sp_qdisc *root_qdisc = mlxsw_sp_port->root_qdisc;
+
+ if (root_qdisc != mlxsw_sp_qdisc)
+ root_qdisc->stats_base.backlog -=
+ mlxsw_sp_qdisc->stats_base.backlog;
+
return mlxsw_sp_tclass_congestion_disable(mlxsw_sp_port,
mlxsw_sp_qdisc->tclass_num);
}
@@ -319,6 +382,7 @@ mlxsw_sp_qdisc_red_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
mlxsw_sp_qdisc->stats_base.backlog);
p->qstats->backlog -= backlog;
+ mlxsw_sp_qdisc->stats_base.backlog = 0;
}
static int
@@ -357,14 +421,16 @@ mlxsw_sp_qdisc_get_red_stats(struct mlxsw_sp_port *mlxsw_sp_port,
u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
struct mlxsw_sp_qdisc_stats *stats_base;
struct mlxsw_sp_port_xstats *xstats;
- struct rtnl_link_stats64 *stats;
xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
- stats = &mlxsw_sp_port->periodic_hw_stats.stats;
stats_base = &mlxsw_sp_qdisc->stats_base;
- tx_bytes = stats->tx_bytes - stats_base->tx_bytes;
- tx_packets = stats->tx_packets - stats_base->tx_packets;
+ mlxsw_sp_qdisc_bstats_per_priority_get(xstats,
+ mlxsw_sp_qdisc->prio_bitmap,
+ &tx_packets, &tx_bytes);
+ tx_bytes = tx_bytes - stats_base->tx_bytes;
+ tx_packets = tx_packets - stats_base->tx_packets;
+
overlimits = xstats->wred_drop[tclass_num] + xstats->ecn -
stats_base->overlimits;
drops = xstats->wred_drop[tclass_num] + xstats->tail_drop[tclass_num] -
@@ -406,11 +472,10 @@ int mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port,
{
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
- if (p->parent != TC_H_ROOT)
+ mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false);
+ if (!mlxsw_sp_qdisc)
return -EOPNOTSUPP;
- mlxsw_sp_qdisc = mlxsw_sp_port->root_qdisc;
-
if (p->command == TC_RED_REPLACE)
return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
mlxsw_sp_qdisc,
@@ -441,9 +506,13 @@ mlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
{
int i;
- for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
+ for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i,
MLXSW_SP_PORT_DEFAULT_TCLASS);
+ mlxsw_sp_qdisc_destroy(mlxsw_sp_port,
+ &mlxsw_sp_port->tclass_qdiscs[i]);
+ mlxsw_sp_port->tclass_qdiscs[i].prio_bitmap = 0;
+ }
return 0;
}
@@ -467,16 +536,41 @@ mlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port *mlxsw_sp_port,
void *params)
{
struct tc_prio_qopt_offload_params *p = params;
- int tclass, i;
+ struct mlxsw_sp_qdisc *child_qdisc;
+ int tclass, i, band, backlog;
+ u8 old_priomap;
int err;
- for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
- tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(p->priomap[i]);
- err = mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i, tclass);
- if (err)
- return err;
+ for (band = 0; band < p->bands; band++) {
+ tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band);
+ child_qdisc = &mlxsw_sp_port->tclass_qdiscs[tclass];
+ old_priomap = child_qdisc->prio_bitmap;
+ child_qdisc->prio_bitmap = 0;
+ for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
+ if (p->priomap[i] == band) {
+ child_qdisc->prio_bitmap |= BIT(i);
+ if (BIT(i) & old_priomap)
+ continue;
+ err = mlxsw_sp_port_prio_tc_set(mlxsw_sp_port,
+ i, tclass);
+ if (err)
+ return err;
+ }
+ }
+ if (old_priomap != child_qdisc->prio_bitmap &&
+ child_qdisc->ops && child_qdisc->ops->clean_stats) {
+ backlog = child_qdisc->stats_base.backlog;
+ child_qdisc->ops->clean_stats(mlxsw_sp_port,
+ child_qdisc);
+ child_qdisc->stats_base.backlog = backlog;
+ }
+ }
+ for (; band < IEEE_8021QAZ_MAX_TCS; band++) {
+ tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band);
+ child_qdisc = &mlxsw_sp_port->tclass_qdiscs[tclass];
+ child_qdisc->prio_bitmap = 0;
+ mlxsw_sp_qdisc_destroy(mlxsw_sp_port, child_qdisc);
}
-
return 0;
}
@@ -513,6 +607,7 @@ mlxsw_sp_qdisc_get_prio_stats(struct mlxsw_sp_port *mlxsw_sp_port,
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
drops += xstats->tail_drop[i];
+ drops += xstats->wred_drop[i];
backlog += xstats->backlog[i];
}
drops = drops - stats_base->drops;
@@ -548,8 +643,10 @@ mlxsw_sp_setup_tc_qdisc_prio_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
stats_base->tx_bytes = stats->tx_bytes;
stats_base->drops = 0;
- for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
+ for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
stats_base->drops += xstats->tail_drop[i];
+ stats_base->drops += xstats->wred_drop[i];
+ }
mlxsw_sp_qdisc->stats_base.backlog = 0;
}
@@ -564,15 +661,48 @@ static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = {
.clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats,
};
+/* Grafting is not supported in mlxsw. It will result in un-offloading of the
+ * grafted qdisc as well as the qdisc in the qdisc new location.
+ * (However, if the graft is to the location where the qdisc is already at, it
+ * will be ignored completely and won't cause un-offloading).
+ */
+static int
+mlxsw_sp_qdisc_prio_graft(struct mlxsw_sp_port *mlxsw_sp_port,
+ struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
+ struct tc_prio_qopt_offload_graft_params *p)
+{
+ int tclass_num = MLXSW_SP_PRIO_BAND_TO_TCLASS(p->band);
+ struct mlxsw_sp_qdisc *old_qdisc;
+
+ /* Check if the grafted qdisc is already in its "new" location. If so -
+ * nothing needs to be done.
+ */
+ if (p->band < IEEE_8021QAZ_MAX_TCS &&
+ mlxsw_sp_port->tclass_qdiscs[tclass_num].handle == p->child_handle)
+ return 0;
+
+ /* See if the grafted qdisc is already offloaded on any tclass. If so,
+ * unoffload it.
+ */
+ old_qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port,
+ p->child_handle);
+ if (old_qdisc)
+ mlxsw_sp_qdisc_destroy(mlxsw_sp_port, old_qdisc);
+
+ mlxsw_sp_qdisc_destroy(mlxsw_sp_port,
+ &mlxsw_sp_port->tclass_qdiscs[tclass_num]);
+ return -EOPNOTSUPP;
+}
+
int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port,
struct tc_prio_qopt_offload *p)
{
struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
- if (p->parent != TC_H_ROOT)
+ mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, true);
+ if (!mlxsw_sp_qdisc)
return -EOPNOTSUPP;
- mlxsw_sp_qdisc = mlxsw_sp_port->root_qdisc;
if (p->command == TC_PRIO_REPLACE)
return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
mlxsw_sp_qdisc,
@@ -589,6 +719,9 @@ int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port,
case TC_PRIO_STATS:
return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
&p->stats);
+ case TC_PRIO_GRAFT:
+ return mlxsw_sp_qdisc_prio_graft(mlxsw_sp_port, mlxsw_sp_qdisc,
+ &p->graft_params);
default:
return -EOPNOTSUPP;
}
@@ -596,17 +729,36 @@ int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port,
int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port)
{
- mlxsw_sp_port->root_qdisc = kzalloc(sizeof(*mlxsw_sp_port->root_qdisc),
- GFP_KERNEL);
- if (!mlxsw_sp_port->root_qdisc)
- return -ENOMEM;
+ struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
+ int i;
+ mlxsw_sp_qdisc = kzalloc(sizeof(*mlxsw_sp_qdisc), GFP_KERNEL);
+ if (!mlxsw_sp_qdisc)
+ goto err_root_qdisc_init;
+
+ mlxsw_sp_port->root_qdisc = mlxsw_sp_qdisc;
+ mlxsw_sp_port->root_qdisc->prio_bitmap = 0xff;
mlxsw_sp_port->root_qdisc->tclass_num = MLXSW_SP_PORT_DEFAULT_TCLASS;
+ mlxsw_sp_qdisc = kzalloc(sizeof(*mlxsw_sp_qdisc) * IEEE_8021QAZ_MAX_TCS,
+ GFP_KERNEL);
+ if (!mlxsw_sp_qdisc)
+ goto err_tclass_qdiscs_init;
+
+ mlxsw_sp_port->tclass_qdiscs = mlxsw_sp_qdisc;
+ for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
+ mlxsw_sp_port->tclass_qdiscs[i].tclass_num = i;
+
return 0;
+
+err_tclass_qdiscs_init:
+ kfree(mlxsw_sp_port->root_qdisc);
+err_root_qdisc_init:
+ return -ENOMEM;
}
void mlxsw_sp_tc_qdisc_fini(struct mlxsw_sp_port *mlxsw_sp_port)
{
+ kfree(mlxsw_sp_port->tclass_qdiscs);
kfree(mlxsw_sp_port->root_qdisc);
}
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
index f7948e983637..1904c0323d39 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
@@ -1,10 +1,10 @@
/*
* drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
- * Copyright (c) 2016-2017 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2016-2018 Mellanox Technologies. All rights reserved.
* Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
* Copyright (c) 2016 Ido Schimmel <idosch@mellanox.com>
* Copyright (c) 2016 Yotam Gigi <yotamg@mellanox.com>
- * Copyright (c) 2017 Petr Machata <petrm@mellanox.com>
+ * Copyright (c) 2017-2018 Petr Machata <petrm@mellanox.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -70,6 +70,7 @@
#include "spectrum_mr.h"
#include "spectrum_mr_tcam.h"
#include "spectrum_router.h"
+#include "spectrum_span.h"
struct mlxsw_sp_fib;
struct mlxsw_sp_vr;
@@ -466,7 +467,7 @@ struct mlxsw_sp_vr {
unsigned int rif_count;
struct mlxsw_sp_fib *fib4;
struct mlxsw_sp_fib *fib6;
- struct mlxsw_sp_mr_table *mr4_table;
+ struct mlxsw_sp_mr_table *mr_table[MLXSW_SP_L3_PROTO_MAX];
};
static const struct rhashtable_params mlxsw_sp_fib_ht_params;
@@ -710,7 +711,9 @@ static void mlxsw_sp_lpm_fini(struct mlxsw_sp *mlxsw_sp)
static bool mlxsw_sp_vr_is_used(const struct mlxsw_sp_vr *vr)
{
- return !!vr->fib4 || !!vr->fib6 || !!vr->mr4_table;
+ return !!vr->fib4 || !!vr->fib6 ||
+ !!vr->mr_table[MLXSW_SP_L3_PROTO_IPV4] ||
+ !!vr->mr_table[MLXSW_SP_L3_PROTO_IPV6];
}
static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp)
@@ -788,7 +791,7 @@ static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,
u32 tb_id,
struct netlink_ext_ack *extack)
{
- struct mlxsw_sp_mr_table *mr4_table;
+ struct mlxsw_sp_mr_table *mr4_table, *mr6_table;
struct mlxsw_sp_fib *fib4;
struct mlxsw_sp_fib *fib6;
struct mlxsw_sp_vr *vr;
@@ -796,7 +799,7 @@ static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,
vr = mlxsw_sp_vr_find_unused(mlxsw_sp);
if (!vr) {
- NL_SET_ERR_MSG(extack, "spectrum: Exceeded number of supported virtual routers");
+ NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported virtual routers");
return ERR_PTR(-EBUSY);
}
fib4 = mlxsw_sp_fib_create(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4);
@@ -811,15 +814,25 @@ static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,
MLXSW_SP_L3_PROTO_IPV4);
if (IS_ERR(mr4_table)) {
err = PTR_ERR(mr4_table);
- goto err_mr_table_create;
+ goto err_mr4_table_create;
}
+ mr6_table = mlxsw_sp_mr_table_create(mlxsw_sp, vr->id,
+ MLXSW_SP_L3_PROTO_IPV6);
+ if (IS_ERR(mr6_table)) {
+ err = PTR_ERR(mr6_table);
+ goto err_mr6_table_create;
+ }
+
vr->fib4 = fib4;
vr->fib6 = fib6;
- vr->mr4_table = mr4_table;
+ vr->mr_table[MLXSW_SP_L3_PROTO_IPV4] = mr4_table;
+ vr->mr_table[MLXSW_SP_L3_PROTO_IPV6] = mr6_table;
vr->tb_id = tb_id;
return vr;
-err_mr_table_create:
+err_mr6_table_create:
+ mlxsw_sp_mr_table_destroy(mr4_table);
+err_mr4_table_create:
mlxsw_sp_fib_destroy(mlxsw_sp, fib6);
err_fib6_create:
mlxsw_sp_fib_destroy(mlxsw_sp, fib4);
@@ -829,8 +842,10 @@ err_fib6_create:
static void mlxsw_sp_vr_destroy(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_vr *vr)
{
- mlxsw_sp_mr_table_destroy(vr->mr4_table);
- vr->mr4_table = NULL;
+ mlxsw_sp_mr_table_destroy(vr->mr_table[MLXSW_SP_L3_PROTO_IPV6]);
+ vr->mr_table[MLXSW_SP_L3_PROTO_IPV6] = NULL;
+ mlxsw_sp_mr_table_destroy(vr->mr_table[MLXSW_SP_L3_PROTO_IPV4]);
+ vr->mr_table[MLXSW_SP_L3_PROTO_IPV4] = NULL;
mlxsw_sp_fib_destroy(mlxsw_sp, vr->fib6);
vr->fib6 = NULL;
mlxsw_sp_fib_destroy(mlxsw_sp, vr->fib4);
@@ -853,7 +868,8 @@ static void mlxsw_sp_vr_put(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr)
{
if (!vr->rif_count && list_empty(&vr->fib4->node_list) &&
list_empty(&vr->fib6->node_list) &&
- mlxsw_sp_mr_table_empty(vr->mr4_table))
+ mlxsw_sp_mr_table_empty(vr->mr_table[MLXSW_SP_L3_PROTO_IPV4]) &&
+ mlxsw_sp_mr_table_empty(vr->mr_table[MLXSW_SP_L3_PROTO_IPV6]))
mlxsw_sp_vr_destroy(mlxsw_sp, vr);
}
@@ -1024,9 +1040,11 @@ mlxsw_sp_ipip_entry_alloc(struct mlxsw_sp *mlxsw_sp,
enum mlxsw_sp_ipip_type ipipt,
struct net_device *ol_dev)
{
+ const struct mlxsw_sp_ipip_ops *ipip_ops;
struct mlxsw_sp_ipip_entry *ipip_entry;
struct mlxsw_sp_ipip_entry *ret = NULL;
+ ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipipt];
ipip_entry = kzalloc(sizeof(*ipip_entry), GFP_KERNEL);
if (!ipip_entry)
return ERR_PTR(-ENOMEM);
@@ -1040,7 +1058,15 @@ mlxsw_sp_ipip_entry_alloc(struct mlxsw_sp *mlxsw_sp,
ipip_entry->ipipt = ipipt;
ipip_entry->ol_dev = ol_dev;
- ipip_entry->parms = mlxsw_sp_ipip_netdev_parms(ol_dev);
+
+ switch (ipip_ops->ul_proto) {
+ case MLXSW_SP_L3_PROTO_IPV4:
+ ipip_entry->parms4 = mlxsw_sp_ipip_netdev_parms4(ol_dev);
+ break;
+ case MLXSW_SP_L3_PROTO_IPV6:
+ WARN_ON(1);
+ break;
+ }
return ipip_entry;
@@ -1380,6 +1406,55 @@ mlxsw_sp_ipip_entry_ol_up_event(struct mlxsw_sp *mlxsw_sp,
decap_fib_entry);
}
+static int
+mlxsw_sp_rif_ipip_lb_op(struct mlxsw_sp_rif_ipip_lb *lb_rif,
+ struct mlxsw_sp_vr *ul_vr, bool enable)
+{
+ struct mlxsw_sp_rif_ipip_lb_config lb_cf = lb_rif->lb_config;
+ struct mlxsw_sp_rif *rif = &lb_rif->common;
+ struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
+ char ritr_pl[MLXSW_REG_RITR_LEN];
+ u32 saddr4;
+
+ switch (lb_cf.ul_protocol) {
+ case MLXSW_SP_L3_PROTO_IPV4:
+ saddr4 = be32_to_cpu(lb_cf.saddr.addr4);
+ mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_LOOPBACK_IF,
+ rif->rif_index, rif->vr_id, rif->dev->mtu);
+ mlxsw_reg_ritr_loopback_ipip4_pack(ritr_pl, lb_cf.lb_ipipt,
+ MLXSW_REG_RITR_LOOPBACK_IPIP_OPTIONS_GRE_KEY_PRESET,
+ ul_vr->id, saddr4, lb_cf.okey);
+ break;
+
+ case MLXSW_SP_L3_PROTO_IPV6:
+ return -EAFNOSUPPORT;
+ }
+
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
+}
+
+static int mlxsw_sp_netdevice_ipip_ol_update_mtu(struct mlxsw_sp *mlxsw_sp,
+ struct net_device *ol_dev)
+{
+ struct mlxsw_sp_ipip_entry *ipip_entry;
+ struct mlxsw_sp_rif_ipip_lb *lb_rif;
+ struct mlxsw_sp_vr *ul_vr;
+ int err = 0;
+
+ ipip_entry = mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, ol_dev);
+ if (ipip_entry) {
+ lb_rif = ipip_entry->ol_lb;
+ ul_vr = &mlxsw_sp->router->vrs[lb_rif->ul_vr_id];
+ err = mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr, true);
+ if (err)
+ goto out;
+ lb_rif->common.mtu = ol_dev->mtu;
+ }
+
+out:
+ return err;
+}
+
static void mlxsw_sp_netdevice_ipip_ol_up_event(struct mlxsw_sp *mlxsw_sp,
struct net_device *ol_dev)
{
@@ -1660,6 +1735,8 @@ int mlxsw_sp_netdevice_ipip_ol_event(struct mlxsw_sp *mlxsw_sp,
extack = info->extack;
return mlxsw_sp_netdevice_ipip_ol_change_event(mlxsw_sp,
ol_dev, extack);
+ case NETDEV_CHANGEMTU:
+ return mlxsw_sp_netdevice_ipip_ol_update_mtu(mlxsw_sp, ol_dev);
}
return 0;
}
@@ -2320,6 +2397,8 @@ static void mlxsw_sp_router_neigh_event_work(struct work_struct *work)
read_unlock_bh(&n->lock);
rtnl_lock();
+ mlxsw_sp_span_respin(mlxsw_sp);
+
entry_connected = nud_state & NUD_VALID && !dead;
neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
if (!entry_connected && !neigh_entry)
@@ -2417,7 +2496,8 @@ static int mlxsw_sp_router_netevent_event(struct notifier_block *nb,
mlxsw_core_schedule_work(&net_work->work);
mlxsw_sp_port_dev_put(mlxsw_sp_port);
break;
- case NETEVENT_MULTIPATH_HASH_UPDATE:
+ case NETEVENT_IPV4_MPATH_HASH_UPDATE:
+ case NETEVENT_IPV6_MPATH_HASH_UPDATE:
net = ptr;
if (!net_eq(net, &init_net))
@@ -5364,10 +5444,20 @@ static int __mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp,
return 0;
}
+static struct mlxsw_sp_mr_table *
+mlxsw_sp_router_fibmr_family_to_table(struct mlxsw_sp_vr *vr, int family)
+{
+ if (family == RTNL_FAMILY_IPMR)
+ return vr->mr_table[MLXSW_SP_L3_PROTO_IPV4];
+ else
+ return vr->mr_table[MLXSW_SP_L3_PROTO_IPV6];
+}
+
static int mlxsw_sp_router_fibmr_add(struct mlxsw_sp *mlxsw_sp,
struct mfc_entry_notifier_info *men_info,
bool replace)
{
+ struct mlxsw_sp_mr_table *mrt;
struct mlxsw_sp_vr *vr;
if (mlxsw_sp->router->aborted)
@@ -5377,12 +5467,14 @@ static int mlxsw_sp_router_fibmr_add(struct mlxsw_sp *mlxsw_sp,
if (IS_ERR(vr))
return PTR_ERR(vr);
- return mlxsw_sp_mr_route4_add(vr->mr4_table, men_info->mfc, replace);
+ mrt = mlxsw_sp_router_fibmr_family_to_table(vr, men_info->info.family);
+ return mlxsw_sp_mr_route_add(mrt, men_info->mfc, replace);
}
static void mlxsw_sp_router_fibmr_del(struct mlxsw_sp *mlxsw_sp,
struct mfc_entry_notifier_info *men_info)
{
+ struct mlxsw_sp_mr_table *mrt;
struct mlxsw_sp_vr *vr;
if (mlxsw_sp->router->aborted)
@@ -5392,7 +5484,8 @@ static void mlxsw_sp_router_fibmr_del(struct mlxsw_sp *mlxsw_sp,
if (WARN_ON(!vr))
return;
- mlxsw_sp_mr_route4_del(vr->mr4_table, men_info->mfc);
+ mrt = mlxsw_sp_router_fibmr_family_to_table(vr, men_info->info.family);
+ mlxsw_sp_mr_route_del(mrt, men_info->mfc);
mlxsw_sp_vr_put(mlxsw_sp, vr);
}
@@ -5400,6 +5493,7 @@ static int
mlxsw_sp_router_fibmr_vif_add(struct mlxsw_sp *mlxsw_sp,
struct vif_entry_notifier_info *ven_info)
{
+ struct mlxsw_sp_mr_table *mrt;
struct mlxsw_sp_rif *rif;
struct mlxsw_sp_vr *vr;
@@ -5410,8 +5504,9 @@ mlxsw_sp_router_fibmr_vif_add(struct mlxsw_sp *mlxsw_sp,
if (IS_ERR(vr))
return PTR_ERR(vr);
+ mrt = mlxsw_sp_router_fibmr_family_to_table(vr, ven_info->info.family);
rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, ven_info->dev);
- return mlxsw_sp_mr_vif_add(vr->mr4_table, ven_info->dev,
+ return mlxsw_sp_mr_vif_add(mrt, ven_info->dev,
ven_info->vif_index,
ven_info->vif_flags, rif);
}
@@ -5420,6 +5515,7 @@ static void
mlxsw_sp_router_fibmr_vif_del(struct mlxsw_sp *mlxsw_sp,
struct vif_entry_notifier_info *ven_info)
{
+ struct mlxsw_sp_mr_table *mrt;
struct mlxsw_sp_vr *vr;
if (mlxsw_sp->router->aborted)
@@ -5429,7 +5525,8 @@ mlxsw_sp_router_fibmr_vif_del(struct mlxsw_sp *mlxsw_sp,
if (WARN_ON(!vr))
return;
- mlxsw_sp_mr_vif_del(vr->mr4_table, ven_info->vif_index);
+ mrt = mlxsw_sp_router_fibmr_family_to_table(vr, ven_info->info.family);
+ mlxsw_sp_mr_vif_del(mrt, ven_info->vif_index);
mlxsw_sp_vr_put(mlxsw_sp, vr);
}
@@ -5521,7 +5618,7 @@ static void mlxsw_sp_vr_fib_flush(struct mlxsw_sp *mlxsw_sp,
static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp)
{
- int i;
+ int i, j;
for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) {
struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i];
@@ -5529,7 +5626,8 @@ static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp)
if (!mlxsw_sp_vr_is_used(vr))
continue;
- mlxsw_sp_mr_table_flush(vr->mr4_table);
+ for (j = 0; j < MLXSW_SP_L3_PROTO_MAX; j++)
+ mlxsw_sp_mr_table_flush(vr->mr_table[j]);
mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4);
/* If virtual router was only used for IPv4, then it's no
@@ -5579,6 +5677,8 @@ static void mlxsw_sp_router_fib4_event_work(struct work_struct *work)
/* Protect internal structures from changes */
rtnl_lock();
+ mlxsw_sp_span_respin(mlxsw_sp);
+
switch (fib_work->event) {
case FIB_EVENT_ENTRY_REPLACE: /* fall through */
case FIB_EVENT_ENTRY_APPEND: /* fall through */
@@ -5621,6 +5721,8 @@ static void mlxsw_sp_router_fib6_event_work(struct work_struct *work)
int err;
rtnl_lock();
+ mlxsw_sp_span_respin(mlxsw_sp);
+
switch (fib_work->event) {
case FIB_EVENT_ENTRY_REPLACE: /* fall through */
case FIB_EVENT_ENTRY_ADD:
@@ -5664,11 +5766,11 @@ static void mlxsw_sp_router_fibmr_event_work(struct work_struct *work)
replace);
if (err)
mlxsw_sp_router_fib_abort(mlxsw_sp);
- ipmr_cache_put(fib_work->men_info.mfc);
+ mr_cache_put(fib_work->men_info.mfc);
break;
case FIB_EVENT_ENTRY_DEL:
mlxsw_sp_router_fibmr_del(mlxsw_sp, &fib_work->men_info);
- ipmr_cache_put(fib_work->men_info.mfc);
+ mr_cache_put(fib_work->men_info.mfc);
break;
case FIB_EVENT_VIF_ADD:
err = mlxsw_sp_router_fibmr_vif_add(mlxsw_sp,
@@ -5748,7 +5850,7 @@ mlxsw_sp_router_fibmr_event(struct mlxsw_sp_fib_event_work *fib_work,
case FIB_EVENT_ENTRY_ADD: /* fall through */
case FIB_EVENT_ENTRY_DEL:
memcpy(&fib_work->men_info, info, sizeof(fib_work->men_info));
- ipmr_cache_hold(fib_work->men_info.mfc);
+ mr_cache_hold(fib_work->men_info.mfc);
break;
case FIB_EVENT_VIF_ADD: /* fall through */
case FIB_EVENT_VIF_DEL:
@@ -5790,10 +5892,14 @@ static int mlxsw_sp_router_fib_rule_event(unsigned long event,
if (!ipmr_rule_default(rule) && !rule->l3mdev)
err = -1;
break;
+ case RTNL_FAMILY_IP6MR:
+ if (!ip6mr_rule_default(rule) && !rule->l3mdev)
+ err = -1;
+ break;
}
if (err < 0)
- NL_SET_ERR_MSG(extack, "spectrum: FIB rules not supported. Aborting offload");
+ NL_SET_ERR_MSG_MOD(extack, "FIB rules not supported. Aborting offload");
return err;
}
@@ -5809,7 +5915,8 @@ static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
if (!net_eq(info->net, &init_net) ||
(info->family != AF_INET && info->family != AF_INET6 &&
- info->family != RTNL_FAMILY_IPMR))
+ info->family != RTNL_FAMILY_IPMR &&
+ info->family != RTNL_FAMILY_IP6MR))
return NOTIFY_DONE;
router = container_of(nb, struct mlxsw_sp_router, fib_nb);
@@ -5839,6 +5946,7 @@ static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
INIT_WORK(&fib_work->work, mlxsw_sp_router_fib6_event_work);
mlxsw_sp_router_fib6_event(fib_work, info);
break;
+ case RTNL_FAMILY_IP6MR:
case RTNL_FAMILY_IPMR:
INIT_WORK(&fib_work->work, mlxsw_sp_router_fibmr_event_work);
mlxsw_sp_router_fibmr_event(fib_work, info);
@@ -6020,7 +6128,7 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_rif *rif;
struct mlxsw_sp_vr *vr;
u16 rif_index;
- int err;
+ int i, err;
type = mlxsw_sp_dev_rif_type(mlxsw_sp, params->dev);
ops = mlxsw_sp->router->rif_ops_arr[type];
@@ -6032,7 +6140,7 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
err = mlxsw_sp_rif_index_alloc(mlxsw_sp, &rif_index);
if (err) {
- NL_SET_ERR_MSG(extack, "spectrum: Exceeded number of supported router interfaces");
+ NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported router interfaces");
goto err_rif_index_alloc;
}
@@ -6060,9 +6168,11 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
if (err)
goto err_configure;
- err = mlxsw_sp_mr_rif_add(vr->mr4_table, rif);
- if (err)
- goto err_mr_rif_add;
+ for (i = 0; i < MLXSW_SP_L3_PROTO_MAX; i++) {
+ err = mlxsw_sp_mr_rif_add(vr->mr_table[i], rif);
+ if (err)
+ goto err_mr_rif_add;
+ }
mlxsw_sp_rif_counters_alloc(rif);
mlxsw_sp->router->rifs[rif_index] = rif;
@@ -6070,6 +6180,8 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
return rif;
err_mr_rif_add:
+ for (i--; i >= 0; i--)
+ mlxsw_sp_mr_rif_del(vr->mr_table[i], rif);
ops->deconfigure(rif);
err_configure:
if (fid)
@@ -6089,13 +6201,15 @@ void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
struct mlxsw_sp_fid *fid = rif->fid;
struct mlxsw_sp_vr *vr;
+ int i;
mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif);
vr = &mlxsw_sp->router->vrs[rif->vr_id];
mlxsw_sp->router->rifs[rif->rif_index] = NULL;
mlxsw_sp_rif_counters_free(rif);
- mlxsw_sp_mr_rif_del(vr->mr4_table, rif);
+ for (i = 0; i < MLXSW_SP_L3_PROTO_MAX; i++)
+ mlxsw_sp_mr_rif_del(vr->mr_table[i], rif);
ops->deconfigure(rif);
if (fid)
/* Loopback RIFs are not associated with a FID. */
@@ -6502,13 +6616,16 @@ int mlxsw_sp_netdevice_router_port_event(struct net_device *dev)
if (rif->mtu != dev->mtu) {
struct mlxsw_sp_vr *vr;
+ int i;
/* The RIF is relevant only to its mr_table instance, as unlike
* unicast routing, in multicast routing a RIF cannot be shared
* between several multicast routing tables.
*/
vr = &mlxsw_sp->router->vrs[rif->vr_id];
- mlxsw_sp_mr_rif_mtu_update(vr->mr4_table, rif, dev->mtu);
+ for (i = 0; i < MLXSW_SP_L3_PROTO_MAX; i++)
+ mlxsw_sp_mr_rif_mtu_update(vr->mr_table[i],
+ rif, dev->mtu);
}
ether_addr_copy(rif->addr, dev->dev_addr);
@@ -6844,33 +6961,6 @@ mlxsw_sp_rif_ipip_lb_setup(struct mlxsw_sp_rif *rif,
}
static int
-mlxsw_sp_rif_ipip_lb_op(struct mlxsw_sp_rif_ipip_lb *lb_rif,
- struct mlxsw_sp_vr *ul_vr, bool enable)
-{
- struct mlxsw_sp_rif_ipip_lb_config lb_cf = lb_rif->lb_config;
- struct mlxsw_sp_rif *rif = &lb_rif->common;
- struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
- char ritr_pl[MLXSW_REG_RITR_LEN];
- u32 saddr4;
-
- switch (lb_cf.ul_protocol) {
- case MLXSW_SP_L3_PROTO_IPV4:
- saddr4 = be32_to_cpu(lb_cf.saddr.addr4);
- mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_LOOPBACK_IF,
- rif->rif_index, rif->vr_id, rif->dev->mtu);
- mlxsw_reg_ritr_loopback_ipip4_pack(ritr_pl, lb_cf.lb_ipipt,
- MLXSW_REG_RITR_LOOPBACK_IPIP_OPTIONS_GRE_KEY_PRESET,
- ul_vr->id, saddr4, lb_cf.okey);
- break;
-
- case MLXSW_SP_L3_PROTO_IPV6:
- return -EAFNOSUPPORT;
- }
-
- return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
-}
-
-static int
mlxsw_sp_rif_ipip_lb_configure(struct mlxsw_sp_rif *rif)
{
struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif);
@@ -7013,13 +7103,25 @@ static void mlxsw_sp_mp4_hash_init(char *recr2_pl)
static void mlxsw_sp_mp6_hash_init(char *recr2_pl)
{
+ bool only_l3 = !ip6_multipath_hash_policy(&init_net);
+
mlxsw_sp_mp_hash_header_set(recr2_pl,
MLXSW_REG_RECR2_IPV6_EN_NOT_TCP_NOT_UDP);
mlxsw_sp_mp_hash_header_set(recr2_pl, MLXSW_REG_RECR2_IPV6_EN_TCP_UDP);
mlxsw_reg_recr2_ipv6_sip_enable(recr2_pl);
mlxsw_reg_recr2_ipv6_dip_enable(recr2_pl);
- mlxsw_sp_mp_hash_field_set(recr2_pl, MLXSW_REG_RECR2_IPV6_FLOW_LABEL);
mlxsw_sp_mp_hash_field_set(recr2_pl, MLXSW_REG_RECR2_IPV6_NEXT_HEADER);
+ if (only_l3) {
+ mlxsw_sp_mp_hash_field_set(recr2_pl,
+ MLXSW_REG_RECR2_IPV6_FLOW_LABEL);
+ } else {
+ mlxsw_sp_mp_hash_header_set(recr2_pl,
+ MLXSW_REG_RECR2_TCP_UDP_EN_IPV6);
+ mlxsw_sp_mp_hash_field_set(recr2_pl,
+ MLXSW_REG_RECR2_TCP_UDP_SPORT);
+ mlxsw_sp_mp_hash_field_set(recr2_pl,
+ MLXSW_REG_RECR2_TCP_UDP_DPORT);
+ }
}
static int mlxsw_sp_mp_hash_init(struct mlxsw_sp *mlxsw_sp)
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h
index 1fb82246ce96..a01edcf56797 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h
@@ -41,6 +41,7 @@
enum mlxsw_sp_l3proto {
MLXSW_SP_L3_PROTO_IPV4,
MLXSW_SP_L3_PROTO_IPV6,
+#define MLXSW_SP_L3_PROTO_MAX (MLXSW_SP_L3_PROTO_IPV6 + 1)
};
union mlxsw_sp_l3addr {
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
new file mode 100644
index 000000000000..65a77708ff61
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
@@ -0,0 +1,824 @@
+/*
+ * drivers/net/ethernet/mellanox/mlxsw/mlxsw_span.c
+ * Copyright (c) 2018 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2018 Petr Machata <petrm@mellanox.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+ */
+
+#include <linux/list.h>
+#include <net/arp.h>
+#include <net/gre.h>
+#include <net/ndisc.h>
+#include <net/ip6_tunnel.h>
+
+#include "spectrum.h"
+#include "spectrum_span.h"
+#include "spectrum_ipip.h"
+
+int mlxsw_sp_span_init(struct mlxsw_sp *mlxsw_sp)
+{
+ int i;
+
+ if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_SPAN))
+ return -EIO;
+
+ mlxsw_sp->span.entries_count = MLXSW_CORE_RES_GET(mlxsw_sp->core,
+ MAX_SPAN);
+ mlxsw_sp->span.entries = kcalloc(mlxsw_sp->span.entries_count,
+ sizeof(struct mlxsw_sp_span_entry),
+ GFP_KERNEL);
+ if (!mlxsw_sp->span.entries)
+ return -ENOMEM;
+
+ for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
+ struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span.entries[i];
+
+ INIT_LIST_HEAD(&curr->bound_ports_list);
+ curr->id = i;
+ }
+
+ return 0;
+}
+
+void mlxsw_sp_span_fini(struct mlxsw_sp *mlxsw_sp)
+{
+ int i;
+
+ for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
+ struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span.entries[i];
+
+ WARN_ON_ONCE(!list_empty(&curr->bound_ports_list));
+ }
+ kfree(mlxsw_sp->span.entries);
+}
+
+static int
+mlxsw_sp_span_entry_phys_parms(const struct net_device *to_dev,
+ struct mlxsw_sp_span_parms *sparmsp)
+{
+ sparmsp->dest_port = netdev_priv(to_dev);
+ return 0;
+}
+
+static int
+mlxsw_sp_span_entry_phys_configure(struct mlxsw_sp_span_entry *span_entry,
+ struct mlxsw_sp_span_parms sparms)
+{
+ struct mlxsw_sp_port *dest_port = sparms.dest_port;
+ struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp;
+ u8 local_port = dest_port->local_port;
+ char mpat_pl[MLXSW_REG_MPAT_LEN];
+ int pa_id = span_entry->id;
+
+ /* Create a new port analayzer entry for local_port. */
+ mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true,
+ MLXSW_REG_MPAT_SPAN_TYPE_LOCAL_ETH);
+
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
+}
+
+static void
+mlxsw_sp_span_entry_deconfigure_common(struct mlxsw_sp_span_entry *span_entry,
+ enum mlxsw_reg_mpat_span_type span_type)
+{
+ struct mlxsw_sp_port *dest_port = span_entry->parms.dest_port;
+ struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp;
+ u8 local_port = dest_port->local_port;
+ char mpat_pl[MLXSW_REG_MPAT_LEN];
+ int pa_id = span_entry->id;
+
+ mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, false, span_type);
+ mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
+}
+
+static void
+mlxsw_sp_span_entry_phys_deconfigure(struct mlxsw_sp_span_entry *span_entry)
+{
+ mlxsw_sp_span_entry_deconfigure_common(span_entry,
+ MLXSW_REG_MPAT_SPAN_TYPE_LOCAL_ETH);
+}
+
+static const
+struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_phys = {
+ .can_handle = mlxsw_sp_port_dev_check,
+ .parms = mlxsw_sp_span_entry_phys_parms,
+ .configure = mlxsw_sp_span_entry_phys_configure,
+ .deconfigure = mlxsw_sp_span_entry_phys_deconfigure,
+};
+
+static int mlxsw_sp_span_dmac(struct neigh_table *tbl,
+ const void *pkey,
+ struct net_device *l3edev,
+ unsigned char dmac[ETH_ALEN])
+{
+ struct neighbour *neigh = neigh_lookup(tbl, pkey, l3edev);
+ int err = 0;
+
+ if (!neigh) {
+ neigh = neigh_create(tbl, pkey, l3edev);
+ if (IS_ERR(neigh))
+ return PTR_ERR(neigh);
+ }
+
+ neigh_event_send(neigh, NULL);
+
+ read_lock_bh(&neigh->lock);
+ if ((neigh->nud_state & NUD_VALID) && !neigh->dead)
+ memcpy(dmac, neigh->ha, ETH_ALEN);
+ else
+ err = -ENOENT;
+ read_unlock_bh(&neigh->lock);
+
+ neigh_release(neigh);
+ return err;
+}
+
+static int
+mlxsw_sp_span_entry_unoffloadable(struct mlxsw_sp_span_parms *sparmsp)
+{
+ sparmsp->dest_port = NULL;
+ return 0;
+}
+
+static __maybe_unused int
+mlxsw_sp_span_entry_tunnel_parms_common(struct net_device *l3edev,
+ union mlxsw_sp_l3addr saddr,
+ union mlxsw_sp_l3addr daddr,
+ union mlxsw_sp_l3addr gw,
+ __u8 ttl,
+ struct neigh_table *tbl,
+ struct mlxsw_sp_span_parms *sparmsp)
+{
+ unsigned char dmac[ETH_ALEN];
+
+ if (mlxsw_sp_l3addr_is_zero(gw))
+ gw = daddr;
+
+ if (!l3edev || !mlxsw_sp_port_dev_check(l3edev) ||
+ mlxsw_sp_span_dmac(tbl, &gw, l3edev, dmac))
+ return mlxsw_sp_span_entry_unoffloadable(sparmsp);
+
+ sparmsp->dest_port = netdev_priv(l3edev);
+ sparmsp->ttl = ttl;
+ memcpy(sparmsp->dmac, dmac, ETH_ALEN);
+ memcpy(sparmsp->smac, l3edev->dev_addr, ETH_ALEN);
+ sparmsp->saddr = saddr;
+ sparmsp->daddr = daddr;
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_NET_IPGRE)
+static struct net_device *
+mlxsw_sp_span_gretap4_route(const struct net_device *to_dev,
+ __be32 *saddrp, __be32 *daddrp)
+{
+ struct ip_tunnel *tun = netdev_priv(to_dev);
+ struct net_device *dev = NULL;
+ struct ip_tunnel_parm parms;
+ struct rtable *rt = NULL;
+ struct flowi4 fl4;
+
+ /* We assume "dev" stays valid after rt is put. */
+ ASSERT_RTNL();
+
+ parms = mlxsw_sp_ipip_netdev_parms4(to_dev);
+ ip_tunnel_init_flow(&fl4, parms.iph.protocol, *daddrp, *saddrp,
+ 0, 0, parms.link, tun->fwmark);
+
+ rt = ip_route_output_key(tun->net, &fl4);
+ if (IS_ERR(rt))
+ return NULL;
+
+ if (rt->rt_type != RTN_UNICAST)
+ goto out;
+
+ dev = rt->dst.dev;
+ *saddrp = fl4.saddr;
+ *daddrp = rt->rt_gateway;
+
+out:
+ ip_rt_put(rt);
+ return dev;
+}
+
+static int
+mlxsw_sp_span_entry_gretap4_parms(const struct net_device *to_dev,
+ struct mlxsw_sp_span_parms *sparmsp)
+{
+ struct ip_tunnel_parm tparm = mlxsw_sp_ipip_netdev_parms4(to_dev);
+ union mlxsw_sp_l3addr saddr = { .addr4 = tparm.iph.saddr };
+ union mlxsw_sp_l3addr daddr = { .addr4 = tparm.iph.daddr };
+ bool inherit_tos = tparm.iph.tos & 0x1;
+ bool inherit_ttl = !tparm.iph.ttl;
+ union mlxsw_sp_l3addr gw = daddr;
+ struct net_device *l3edev;
+
+ if (!(to_dev->flags & IFF_UP) ||
+ /* Reject tunnels with GRE keys, checksums, etc. */
+ tparm.i_flags || tparm.o_flags ||
+ /* Require a fixed TTL and a TOS copied from the mirrored packet. */
+ inherit_ttl || !inherit_tos ||
+ /* A destination address may not be "any". */
+ mlxsw_sp_l3addr_is_zero(daddr))
+ return mlxsw_sp_span_entry_unoffloadable(sparmsp);
+
+ l3edev = mlxsw_sp_span_gretap4_route(to_dev, &saddr.addr4, &gw.addr4);
+ return mlxsw_sp_span_entry_tunnel_parms_common(l3edev, saddr, daddr, gw,
+ tparm.iph.ttl,
+ &arp_tbl, sparmsp);
+}
+
+static int
+mlxsw_sp_span_entry_gretap4_configure(struct mlxsw_sp_span_entry *span_entry,
+ struct mlxsw_sp_span_parms sparms)
+{
+ struct mlxsw_sp_port *dest_port = sparms.dest_port;
+ struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp;
+ u8 local_port = dest_port->local_port;
+ char mpat_pl[MLXSW_REG_MPAT_LEN];
+ int pa_id = span_entry->id;
+
+ /* Create a new port analayzer entry for local_port. */
+ mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true,
+ MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3);
+ mlxsw_reg_mpat_eth_rspan_l2_pack(mpat_pl,
+ MLXSW_REG_MPAT_ETH_RSPAN_VERSION_NO_HEADER,
+ sparms.dmac, false);
+ mlxsw_reg_mpat_eth_rspan_l3_ipv4_pack(mpat_pl,
+ sparms.ttl, sparms.smac,
+ be32_to_cpu(sparms.saddr.addr4),
+ be32_to_cpu(sparms.daddr.addr4));
+
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
+}
+
+static void
+mlxsw_sp_span_entry_gretap4_deconfigure(struct mlxsw_sp_span_entry *span_entry)
+{
+ mlxsw_sp_span_entry_deconfigure_common(span_entry,
+ MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3);
+}
+
+static const struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_gretap4 = {
+ .can_handle = is_gretap_dev,
+ .parms = mlxsw_sp_span_entry_gretap4_parms,
+ .configure = mlxsw_sp_span_entry_gretap4_configure,
+ .deconfigure = mlxsw_sp_span_entry_gretap4_deconfigure,
+};
+#endif
+
+#if IS_ENABLED(CONFIG_IPV6_GRE)
+static struct net_device *
+mlxsw_sp_span_gretap6_route(const struct net_device *to_dev,
+ struct in6_addr *saddrp,
+ struct in6_addr *daddrp)
+{
+ struct ip6_tnl *t = netdev_priv(to_dev);
+ struct flowi6 fl6 = t->fl.u.ip6;
+ struct net_device *dev = NULL;
+ struct dst_entry *dst;
+ struct rt6_info *rt6;
+
+ /* We assume "dev" stays valid after dst is released. */
+ ASSERT_RTNL();
+
+ fl6.flowi6_mark = t->parms.fwmark;
+ if (!ip6_tnl_xmit_ctl(t, &fl6.saddr, &fl6.daddr))
+ return NULL;
+
+ dst = ip6_route_output(t->net, NULL, &fl6);
+ if (!dst || dst->error)
+ goto out;
+
+ rt6 = container_of(dst, struct rt6_info, dst);
+
+ dev = dst->dev;
+ *saddrp = fl6.saddr;
+ *daddrp = rt6->rt6i_gateway;
+
+out:
+ dst_release(dst);
+ return dev;
+}
+
+static int
+mlxsw_sp_span_entry_gretap6_parms(const struct net_device *to_dev,
+ struct mlxsw_sp_span_parms *sparmsp)
+{
+ struct __ip6_tnl_parm tparm = mlxsw_sp_ipip_netdev_parms6(to_dev);
+ bool inherit_tos = tparm.flags & IP6_TNL_F_USE_ORIG_TCLASS;
+ union mlxsw_sp_l3addr saddr = { .addr6 = tparm.laddr };
+ union mlxsw_sp_l3addr daddr = { .addr6 = tparm.raddr };
+ bool inherit_ttl = !tparm.hop_limit;
+ union mlxsw_sp_l3addr gw = daddr;
+ struct net_device *l3edev;
+
+ if (!(to_dev->flags & IFF_UP) ||
+ /* Reject tunnels with GRE keys, checksums, etc. */
+ tparm.i_flags || tparm.o_flags ||
+ /* Require a fixed TTL and a TOS copied from the mirrored packet. */
+ inherit_ttl || !inherit_tos ||
+ /* A destination address may not be "any". */
+ mlxsw_sp_l3addr_is_zero(daddr))
+ return mlxsw_sp_span_entry_unoffloadable(sparmsp);
+
+ l3edev = mlxsw_sp_span_gretap6_route(to_dev, &saddr.addr6, &gw.addr6);
+ return mlxsw_sp_span_entry_tunnel_parms_common(l3edev, saddr, daddr, gw,
+ tparm.hop_limit,
+ &nd_tbl, sparmsp);
+}
+
+static int
+mlxsw_sp_span_entry_gretap6_configure(struct mlxsw_sp_span_entry *span_entry,
+ struct mlxsw_sp_span_parms sparms)
+{
+ struct mlxsw_sp_port *dest_port = sparms.dest_port;
+ struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp;
+ u8 local_port = dest_port->local_port;
+ char mpat_pl[MLXSW_REG_MPAT_LEN];
+ int pa_id = span_entry->id;
+
+ /* Create a new port analayzer entry for local_port. */
+ mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true,
+ MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3);
+ mlxsw_reg_mpat_eth_rspan_l2_pack(mpat_pl,
+ MLXSW_REG_MPAT_ETH_RSPAN_VERSION_NO_HEADER,
+ sparms.dmac, false);
+ mlxsw_reg_mpat_eth_rspan_l3_ipv6_pack(mpat_pl, sparms.ttl, sparms.smac,
+ sparms.saddr.addr6,
+ sparms.daddr.addr6);
+
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
+}
+
+static void
+mlxsw_sp_span_entry_gretap6_deconfigure(struct mlxsw_sp_span_entry *span_entry)
+{
+ mlxsw_sp_span_entry_deconfigure_common(span_entry,
+ MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3);
+}
+
+static const
+struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_gretap6 = {
+ .can_handle = is_ip6gretap_dev,
+ .parms = mlxsw_sp_span_entry_gretap6_parms,
+ .configure = mlxsw_sp_span_entry_gretap6_configure,
+ .deconfigure = mlxsw_sp_span_entry_gretap6_deconfigure,
+};
+#endif
+
+static const
+struct mlxsw_sp_span_entry_ops *const mlxsw_sp_span_entry_types[] = {
+ &mlxsw_sp_span_entry_ops_phys,
+#if IS_ENABLED(CONFIG_NET_IPGRE)
+ &mlxsw_sp_span_entry_ops_gretap4,
+#endif
+#if IS_ENABLED(CONFIG_IPV6_GRE)
+ &mlxsw_sp_span_entry_ops_gretap6,
+#endif
+};
+
+static int
+mlxsw_sp_span_entry_nop_parms(const struct net_device *to_dev,
+ struct mlxsw_sp_span_parms *sparmsp)
+{
+ return mlxsw_sp_span_entry_unoffloadable(sparmsp);
+}
+
+static int
+mlxsw_sp_span_entry_nop_configure(struct mlxsw_sp_span_entry *span_entry,
+ struct mlxsw_sp_span_parms sparms)
+{
+ return 0;
+}
+
+static void
+mlxsw_sp_span_entry_nop_deconfigure(struct mlxsw_sp_span_entry *span_entry)
+{
+}
+
+static const struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_nop = {
+ .parms = mlxsw_sp_span_entry_nop_parms,
+ .configure = mlxsw_sp_span_entry_nop_configure,
+ .deconfigure = mlxsw_sp_span_entry_nop_deconfigure,
+};
+
+static void
+mlxsw_sp_span_entry_configure(struct mlxsw_sp *mlxsw_sp,
+ struct mlxsw_sp_span_entry *span_entry,
+ struct mlxsw_sp_span_parms sparms)
+{
+ if (sparms.dest_port) {
+ if (sparms.dest_port->mlxsw_sp != mlxsw_sp) {
+ netdev_err(span_entry->to_dev, "Cannot mirror to %s, which belongs to a different mlxsw instance",
+ sparms.dest_port->dev->name);
+ sparms.dest_port = NULL;
+ } else if (span_entry->ops->configure(span_entry, sparms)) {
+ netdev_err(span_entry->to_dev, "Failed to offload mirror to %s",
+ sparms.dest_port->dev->name);
+ sparms.dest_port = NULL;
+ }
+ }
+
+ span_entry->parms = sparms;
+}
+
+static void
+mlxsw_sp_span_entry_deconfigure(struct mlxsw_sp_span_entry *span_entry)
+{
+ if (span_entry->parms.dest_port)
+ span_entry->ops->deconfigure(span_entry);
+}
+
+static struct mlxsw_sp_span_entry *
+mlxsw_sp_span_entry_create(struct mlxsw_sp *mlxsw_sp,
+ const struct net_device *to_dev,
+ const struct mlxsw_sp_span_entry_ops *ops,
+ struct mlxsw_sp_span_parms sparms)
+{
+ struct mlxsw_sp_span_entry *span_entry = NULL;
+ int i;
+
+ /* find a free entry to use */
+ for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
+ if (!mlxsw_sp->span.entries[i].ref_count) {
+ span_entry = &mlxsw_sp->span.entries[i];
+ break;
+ }
+ }
+ if (!span_entry)
+ return NULL;
+
+ span_entry->ops = ops;
+ span_entry->ref_count = 1;
+ span_entry->to_dev = to_dev;
+ mlxsw_sp_span_entry_configure(mlxsw_sp, span_entry, sparms);
+
+ return span_entry;
+}
+
+static void mlxsw_sp_span_entry_destroy(struct mlxsw_sp_span_entry *span_entry)
+{
+ mlxsw_sp_span_entry_deconfigure(span_entry);
+}
+
+struct mlxsw_sp_span_entry *
+mlxsw_sp_span_entry_find_by_port(struct mlxsw_sp *mlxsw_sp,
+ const struct net_device *to_dev)
+{
+ int i;
+
+ for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
+ struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span.entries[i];
+
+ if (curr->ref_count && curr->to_dev == to_dev)
+ return curr;
+ }
+ return NULL;
+}
+
+void mlxsw_sp_span_entry_invalidate(struct mlxsw_sp *mlxsw_sp,
+ struct mlxsw_sp_span_entry *span_entry)
+{
+ mlxsw_sp_span_entry_deconfigure(span_entry);
+ span_entry->ops = &mlxsw_sp_span_entry_ops_nop;
+}
+
+static struct mlxsw_sp_span_entry *
+mlxsw_sp_span_entry_find_by_id(struct mlxsw_sp *mlxsw_sp, int span_id)
+{
+ int i;
+
+ for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
+ struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span.entries[i];
+
+ if (curr->ref_count && curr->id == span_id)
+ return curr;
+ }
+ return NULL;
+}
+
+static struct mlxsw_sp_span_entry *
+mlxsw_sp_span_entry_get(struct mlxsw_sp *mlxsw_sp,
+ const struct net_device *to_dev,
+ const struct mlxsw_sp_span_entry_ops *ops,
+ struct mlxsw_sp_span_parms sparms)
+{
+ struct mlxsw_sp_span_entry *span_entry;
+
+ span_entry = mlxsw_sp_span_entry_find_by_port(mlxsw_sp, to_dev);
+ if (span_entry) {
+ /* Already exists, just take a reference */
+ span_entry->ref_count++;
+ return span_entry;
+ }
+
+ return mlxsw_sp_span_entry_create(mlxsw_sp, to_dev, ops, sparms);
+}
+
+static int mlxsw_sp_span_entry_put(struct mlxsw_sp *mlxsw_sp,
+ struct mlxsw_sp_span_entry *span_entry)
+{
+ WARN_ON(!span_entry->ref_count);
+ if (--span_entry->ref_count == 0)
+ mlxsw_sp_span_entry_destroy(span_entry);
+ return 0;
+}
+
+static bool mlxsw_sp_span_is_egress_mirror(struct mlxsw_sp_port *port)
+{
+ struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
+ struct mlxsw_sp_span_inspected_port *p;
+ int i;
+
+ for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
+ struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span.entries[i];
+
+ list_for_each_entry(p, &curr->bound_ports_list, list)
+ if (p->local_port == port->local_port &&
+ p->type == MLXSW_SP_SPAN_EGRESS)
+ return true;
+ }
+
+ return false;
+}
+
+static int mlxsw_sp_span_mtu_to_buffsize(const struct mlxsw_sp *mlxsw_sp,
+ int mtu)
+{
+ return mlxsw_sp_bytes_cells(mlxsw_sp, mtu * 5 / 2) + 1;
+}
+
+int mlxsw_sp_span_port_mtu_update(struct mlxsw_sp_port *port, u16 mtu)
+{
+ struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
+ char sbib_pl[MLXSW_REG_SBIB_LEN];
+ int err;
+
+ /* If port is egress mirrored, the shared buffer size should be
+ * updated according to the mtu value
+ */
+ if (mlxsw_sp_span_is_egress_mirror(port)) {
+ u32 buffsize = mlxsw_sp_span_mtu_to_buffsize(mlxsw_sp, mtu);
+
+ mlxsw_reg_sbib_pack(sbib_pl, port->local_port, buffsize);
+ err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbib), sbib_pl);
+ if (err) {
+ netdev_err(port->dev, "Could not update shared buffer for mirroring\n");
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static struct mlxsw_sp_span_inspected_port *
+mlxsw_sp_span_entry_bound_port_find(struct mlxsw_sp_span_entry *span_entry,
+ enum mlxsw_sp_span_type type,
+ struct mlxsw_sp_port *port,
+ bool bind)
+{
+ struct mlxsw_sp_span_inspected_port *p;
+
+ list_for_each_entry(p, &span_entry->bound_ports_list, list)
+ if (type == p->type &&
+ port->local_port == p->local_port &&
+ bind == p->bound)
+ return p;
+ return NULL;
+}
+
+static int
+mlxsw_sp_span_inspected_port_bind(struct mlxsw_sp_port *port,
+ struct mlxsw_sp_span_entry *span_entry,
+ enum mlxsw_sp_span_type type,
+ bool bind)
+{
+ struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
+ char mpar_pl[MLXSW_REG_MPAR_LEN];
+ int pa_id = span_entry->id;
+
+ /* bind the port to the SPAN entry */
+ mlxsw_reg_mpar_pack(mpar_pl, port->local_port,
+ (enum mlxsw_reg_mpar_i_e)type, bind, pa_id);
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpar), mpar_pl);
+}
+
+static int
+mlxsw_sp_span_inspected_port_add(struct mlxsw_sp_port *port,
+ struct mlxsw_sp_span_entry *span_entry,
+ enum mlxsw_sp_span_type type,
+ bool bind)
+{
+ struct mlxsw_sp_span_inspected_port *inspected_port;
+ struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
+ char sbib_pl[MLXSW_REG_SBIB_LEN];
+ int i;
+ int err;
+
+ /* A given (source port, direction) can only be bound to one analyzer,
+ * so if a binding is requested, check for conflicts.
+ */
+ if (bind)
+ for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
+ struct mlxsw_sp_span_entry *curr =
+ &mlxsw_sp->span.entries[i];
+
+ if (mlxsw_sp_span_entry_bound_port_find(curr, type,
+ port, bind))
+ return -EEXIST;
+ }
+
+ /* if it is an egress SPAN, bind a shared buffer to it */
+ if (type == MLXSW_SP_SPAN_EGRESS) {
+ u32 buffsize = mlxsw_sp_span_mtu_to_buffsize(mlxsw_sp,
+ port->dev->mtu);
+
+ mlxsw_reg_sbib_pack(sbib_pl, port->local_port, buffsize);
+ err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbib), sbib_pl);
+ if (err) {
+ netdev_err(port->dev, "Could not create shared buffer for mirroring\n");
+ return err;
+ }
+ }
+
+ if (bind) {
+ err = mlxsw_sp_span_inspected_port_bind(port, span_entry, type,
+ true);
+ if (err)
+ goto err_port_bind;
+ }
+
+ inspected_port = kzalloc(sizeof(*inspected_port), GFP_KERNEL);
+ if (!inspected_port) {
+ err = -ENOMEM;
+ goto err_inspected_port_alloc;
+ }
+ inspected_port->local_port = port->local_port;
+ inspected_port->type = type;
+ inspected_port->bound = bind;
+ list_add_tail(&inspected_port->list, &span_entry->bound_ports_list);
+
+ return 0;
+
+err_inspected_port_alloc:
+ if (bind)
+ mlxsw_sp_span_inspected_port_bind(port, span_entry, type,
+ false);
+err_port_bind:
+ if (type == MLXSW_SP_SPAN_EGRESS) {
+ mlxsw_reg_sbib_pack(sbib_pl, port->local_port, 0);
+ mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbib), sbib_pl);
+ }
+ return err;
+}
+
+static void
+mlxsw_sp_span_inspected_port_del(struct mlxsw_sp_port *port,
+ struct mlxsw_sp_span_entry *span_entry,
+ enum mlxsw_sp_span_type type,
+ bool bind)
+{
+ struct mlxsw_sp_span_inspected_port *inspected_port;
+ struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
+ char sbib_pl[MLXSW_REG_SBIB_LEN];
+
+ inspected_port = mlxsw_sp_span_entry_bound_port_find(span_entry, type,
+ port, bind);
+ if (!inspected_port)
+ return;
+
+ if (bind)
+ mlxsw_sp_span_inspected_port_bind(port, span_entry, type,
+ false);
+ /* remove the SBIB buffer if it was egress SPAN */
+ if (type == MLXSW_SP_SPAN_EGRESS) {
+ mlxsw_reg_sbib_pack(sbib_pl, port->local_port, 0);
+ mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbib), sbib_pl);
+ }
+
+ mlxsw_sp_span_entry_put(mlxsw_sp, span_entry);
+
+ list_del(&inspected_port->list);
+ kfree(inspected_port);
+}
+
+static const struct mlxsw_sp_span_entry_ops *
+mlxsw_sp_span_entry_ops(struct mlxsw_sp *mlxsw_sp,
+ const struct net_device *to_dev)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(mlxsw_sp_span_entry_types); ++i)
+ if (mlxsw_sp_span_entry_types[i]->can_handle(to_dev))
+ return mlxsw_sp_span_entry_types[i];
+
+ return NULL;
+}
+
+int mlxsw_sp_span_mirror_add(struct mlxsw_sp_port *from,
+ const struct net_device *to_dev,
+ enum mlxsw_sp_span_type type, bool bind,
+ int *p_span_id)
+{
+ struct mlxsw_sp *mlxsw_sp = from->mlxsw_sp;
+ const struct mlxsw_sp_span_entry_ops *ops;
+ struct mlxsw_sp_span_parms sparms = {NULL};
+ struct mlxsw_sp_span_entry *span_entry;
+ int err;
+
+ ops = mlxsw_sp_span_entry_ops(mlxsw_sp, to_dev);
+ if (!ops) {
+ netdev_err(to_dev, "Cannot mirror to %s", to_dev->name);
+ return -EOPNOTSUPP;
+ }
+
+ err = ops->parms(to_dev, &sparms);
+ if (err)
+ return err;
+
+ span_entry = mlxsw_sp_span_entry_get(mlxsw_sp, to_dev, ops, sparms);
+ if (!span_entry)
+ return -ENOENT;
+
+ netdev_dbg(from->dev, "Adding inspected port to SPAN entry %d\n",
+ span_entry->id);
+
+ err = mlxsw_sp_span_inspected_port_add(from, span_entry, type, bind);
+ if (err)
+ goto err_port_bind;
+
+ *p_span_id = span_entry->id;
+ return 0;
+
+err_port_bind:
+ mlxsw_sp_span_entry_put(mlxsw_sp, span_entry);
+ return err;
+}
+
+void mlxsw_sp_span_mirror_del(struct mlxsw_sp_port *from, int span_id,
+ enum mlxsw_sp_span_type type, bool bind)
+{
+ struct mlxsw_sp_span_entry *span_entry;
+
+ span_entry = mlxsw_sp_span_entry_find_by_id(from->mlxsw_sp, span_id);
+ if (!span_entry) {
+ netdev_err(from->dev, "no span entry found\n");
+ return;
+ }
+
+ netdev_dbg(from->dev, "removing inspected port from SPAN entry %d\n",
+ span_entry->id);
+ mlxsw_sp_span_inspected_port_del(from, span_entry, type, bind);
+}
+
+void mlxsw_sp_span_respin(struct mlxsw_sp *mlxsw_sp)
+{
+ int i;
+ int err;
+
+ ASSERT_RTNL();
+ for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
+ struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span.entries[i];
+ struct mlxsw_sp_span_parms sparms = {NULL};
+
+ if (!curr->ref_count)
+ continue;
+
+ err = curr->ops->parms(curr->to_dev, &sparms);
+ if (err)
+ continue;
+
+ if (memcmp(&sparms, &curr->parms, sizeof(sparms))) {
+ mlxsw_sp_span_entry_deconfigure(curr);
+ mlxsw_sp_span_entry_configure(mlxsw_sp, curr, sparms);
+ }
+ }
+}
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.h
new file mode 100644
index 000000000000..4b87ec20e658
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.h
@@ -0,0 +1,107 @@
+/*
+ * drivers/net/ethernet/mellanox/mlxsw/mlxsw_span.h
+ * Copyright (c) 2018 Mellanox Technologies. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+ */
+
+#ifndef _MLXSW_SPECTRUM_SPAN_H
+#define _MLXSW_SPECTRUM_SPAN_H
+
+#include <linux/types.h>
+#include <linux/if_ether.h>
+
+#include "spectrum_router.h"
+
+struct mlxsw_sp;
+struct mlxsw_sp_port;
+
+enum mlxsw_sp_span_type {
+ MLXSW_SP_SPAN_EGRESS,
+ MLXSW_SP_SPAN_INGRESS
+};
+
+struct mlxsw_sp_span_inspected_port {
+ struct list_head list;
+ enum mlxsw_sp_span_type type;
+ u8 local_port;
+
+ /* Whether this is a directly bound mirror (port-to-port) or an ACL. */
+ bool bound;
+};
+
+struct mlxsw_sp_span_parms {
+ struct mlxsw_sp_port *dest_port; /* NULL for unoffloaded SPAN. */
+ unsigned int ttl;
+ unsigned char dmac[ETH_ALEN];
+ unsigned char smac[ETH_ALEN];
+ union mlxsw_sp_l3addr daddr;
+ union mlxsw_sp_l3addr saddr;
+};
+
+struct mlxsw_sp_span_entry_ops;
+
+struct mlxsw_sp_span_entry {
+ const struct net_device *to_dev;
+ const struct mlxsw_sp_span_entry_ops *ops;
+ struct mlxsw_sp_span_parms parms;
+ struct list_head bound_ports_list;
+ int ref_count;
+ int id;
+};
+
+struct mlxsw_sp_span_entry_ops {
+ bool (*can_handle)(const struct net_device *to_dev);
+ int (*parms)(const struct net_device *to_dev,
+ struct mlxsw_sp_span_parms *sparmsp);
+ int (*configure)(struct mlxsw_sp_span_entry *span_entry,
+ struct mlxsw_sp_span_parms sparms);
+ void (*deconfigure)(struct mlxsw_sp_span_entry *span_entry);
+};
+
+int mlxsw_sp_span_init(struct mlxsw_sp *mlxsw_sp);
+void mlxsw_sp_span_fini(struct mlxsw_sp *mlxsw_sp);
+void mlxsw_sp_span_respin(struct mlxsw_sp *mlxsw_sp);
+
+int mlxsw_sp_span_mirror_add(struct mlxsw_sp_port *from,
+ const struct net_device *to_dev,
+ enum mlxsw_sp_span_type type,
+ bool bind, int *p_span_id);
+void mlxsw_sp_span_mirror_del(struct mlxsw_sp_port *from, int span_id,
+ enum mlxsw_sp_span_type type, bool bind);
+struct mlxsw_sp_span_entry *
+mlxsw_sp_span_entry_find_by_port(struct mlxsw_sp *mlxsw_sp,
+ const struct net_device *to_dev);
+
+void mlxsw_sp_span_entry_invalidate(struct mlxsw_sp *mlxsw_sp,
+ struct mlxsw_sp_span_entry *span_entry);
+
+int mlxsw_sp_span_port_mtu_update(struct mlxsw_sp_port *port, u16 mtu);
+
+#endif
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
index 161bcdc012f0..c11c9a635866 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
@@ -1844,7 +1844,7 @@ mlxsw_sp_bridge_8021q_port_join(struct mlxsw_sp_bridge_device *bridge_device,
struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
if (is_vlan_dev(bridge_port->dev)) {
- NL_SET_ERR_MSG(extack, "spectrum: Can not enslave a VLAN device to a VLAN-aware bridge");
+ NL_SET_ERR_MSG_MOD(extack, "Can not enslave a VLAN device to a VLAN-aware bridge");
return -EINVAL;
}
@@ -1907,20 +1907,16 @@ mlxsw_sp_bridge_8021d_port_join(struct mlxsw_sp_bridge_device *bridge_device,
struct netlink_ext_ack *extack)
{
struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
+ struct net_device *dev = bridge_port->dev;
u16 vid;
- if (!is_vlan_dev(bridge_port->dev)) {
- NL_SET_ERR_MSG(extack, "spectrum: Only VLAN devices can be enslaved to a VLAN-unaware bridge");
- return -EINVAL;
- }
- vid = vlan_dev_vlan_id(bridge_port->dev);
-
+ vid = is_vlan_dev(dev) ? vlan_dev_vlan_id(dev) : 1;
mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
if (WARN_ON(!mlxsw_sp_port_vlan))
return -EINVAL;
if (mlxsw_sp_port_is_br_member(mlxsw_sp_port, bridge_device->dev)) {
- NL_SET_ERR_MSG(extack, "spectrum: Can not bridge VLAN uppers of the same port");
+ NL_SET_ERR_MSG_MOD(extack, "Can not bridge VLAN uppers of the same port");
return -EINVAL;
}
@@ -1937,8 +1933,10 @@ mlxsw_sp_bridge_8021d_port_leave(struct mlxsw_sp_bridge_device *bridge_device,
struct mlxsw_sp_port *mlxsw_sp_port)
{
struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
- u16 vid = vlan_dev_vlan_id(bridge_port->dev);
+ struct net_device *dev = bridge_port->dev;
+ u16 vid;
+ vid = is_vlan_dev(dev) ? vlan_dev_vlan_id(dev) : 1;
mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
if (WARN_ON(!mlxsw_sp_port_vlan))
return;
diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchib.c b/drivers/net/ethernet/mellanox/mlxsw/switchib.c
index ab7a29846bfa..c698ec4fd9d4 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/switchib.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/switchib.c
@@ -510,7 +510,6 @@ static const struct mlxsw_config_profile mlxsw_sib_config_profile = {
.type = MLXSW_PORT_SWID_TYPE_IB,
}
},
- .resource_query_enable = 0,
};
static struct mlxsw_driver mlxsw_sib_driver = {
diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c
index f3c29bbf07e2..a655c5850aa6 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c
@@ -789,7 +789,7 @@ mlxsw_sx_port_get_link_ksettings(struct net_device *dev,
u32 supported, advertising, lp_advertising;
int err;
- mlxsw_reg_ptys_eth_pack(ptys_pl, mlxsw_sx_port->local_port, 0);
+ mlxsw_reg_ptys_eth_pack(ptys_pl, mlxsw_sx_port->local_port, 0, false);
err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(ptys), ptys_pl);
if (err) {
netdev_err(dev, "Failed to get proto");
@@ -879,7 +879,7 @@ mlxsw_sx_port_set_link_ksettings(struct net_device *dev,
mlxsw_sx_to_ptys_advert_link(advertising) :
mlxsw_sx_to_ptys_speed(speed);
- mlxsw_reg_ptys_eth_pack(ptys_pl, mlxsw_sx_port->local_port, 0);
+ mlxsw_reg_ptys_eth_pack(ptys_pl, mlxsw_sx_port->local_port, 0, false);
err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(ptys), ptys_pl);
if (err) {
netdev_err(dev, "Failed to get proto");
@@ -897,7 +897,7 @@ mlxsw_sx_port_set_link_ksettings(struct net_device *dev,
return 0;
mlxsw_reg_ptys_eth_pack(ptys_pl, mlxsw_sx_port->local_port,
- eth_proto_new);
+ eth_proto_new, true);
err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(ptys), ptys_pl);
if (err) {
netdev_err(dev, "Failed to set proto admin");
@@ -1029,7 +1029,7 @@ mlxsw_sx_port_speed_by_width_set(struct mlxsw_sx_port *mlxsw_sx_port, u8 width)
eth_proto_admin = mlxsw_sx_to_ptys_upper_speed(upper_speed);
mlxsw_reg_ptys_eth_pack(ptys_pl, mlxsw_sx_port->local_port,
- eth_proto_admin);
+ eth_proto_admin, true);
return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(ptys), ptys_pl);
}
@@ -1706,7 +1706,6 @@ static const struct mlxsw_config_profile mlxsw_sx_config_profile = {
.type = MLXSW_PORT_SWID_TYPE_IB,
}
},
- .resource_query_enable = 0,
};
static struct mlxsw_driver mlxsw_sx_driver = {
diff --git a/drivers/net/ethernet/mellanox/mlxsw/trap.h b/drivers/net/ethernet/mellanox/mlxsw/trap.h
index ec6cef8267ae..399e9d6993f7 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/trap.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/trap.h
@@ -77,6 +77,7 @@ enum {
MLXSW_TRAP_ID_IPV6_DHCP = 0x69,
MLXSW_TRAP_ID_IPV6_ALL_ROUTERS_LINK = 0x6F,
MLXSW_TRAP_ID_RTR_INGRESS0 = 0x70,
+ MLXSW_TRAP_ID_IPV6_PIM = 0x79,
MLXSW_TRAP_ID_IPV4_BGP = 0x88,
MLXSW_TRAP_ID_IPV6_BGP = 0x89,
MLXSW_TRAP_ID_L3_IPV6_ROUTER_SOLICITATION = 0x8A,
diff --git a/drivers/net/ethernet/microchip/Kconfig b/drivers/net/ethernet/microchip/Kconfig
index 36a09d94b368..71dca8bd51ac 100644
--- a/drivers/net/ethernet/microchip/Kconfig
+++ b/drivers/net/ethernet/microchip/Kconfig
@@ -42,4 +42,14 @@ config ENCX24J600
To compile this driver as a module, choose M here. The module will be
called encx24j600.
+config LAN743X
+ tristate "LAN743x support"
+ depends on PCI
+ select PHYLIB
+ ---help---
+ Support for the Microchip LAN743x PCI Express Gigabit Ethernet chip
+
+ To compile this driver as a module, choose M here. The module will be
+ called lan743x.
+
endif # NET_VENDOR_MICROCHIP
diff --git a/drivers/net/ethernet/microchip/Makefile b/drivers/net/ethernet/microchip/Makefile
index ff78f621b59a..2e982cc249fb 100644
--- a/drivers/net/ethernet/microchip/Makefile
+++ b/drivers/net/ethernet/microchip/Makefile
@@ -4,3 +4,6 @@
obj-$(CONFIG_ENC28J60) += enc28j60.o
obj-$(CONFIG_ENCX24J600) += encx24j600.o encx24j600-regmap.o
+obj-$(CONFIG_LAN743X) += lan743x.o
+
+lan743x-objs := lan743x_main.o
diff --git a/drivers/net/ethernet/microchip/lan743x_main.c b/drivers/net/ethernet/microchip/lan743x_main.c
new file mode 100644
index 000000000000..dd947e4dd3ce
--- /dev/null
+++ b/drivers/net/ethernet/microchip/lan743x_main.c
@@ -0,0 +1,2771 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (C) 2018 Microchip Technology Inc. */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/crc32.h>
+#include <linux/microchipphy.h>
+#include <linux/net_tstamp.h>
+#include <linux/phy.h>
+#include <linux/rtnetlink.h>
+#include <linux/iopoll.h>
+#include "lan743x_main.h"
+
+static void lan743x_pci_cleanup(struct lan743x_adapter *adapter)
+{
+ pci_release_selected_regions(adapter->pdev,
+ pci_select_bars(adapter->pdev,
+ IORESOURCE_MEM));
+ pci_disable_device(adapter->pdev);
+}
+
+static int lan743x_pci_init(struct lan743x_adapter *adapter,
+ struct pci_dev *pdev)
+{
+ unsigned long bars = 0;
+ int ret;
+
+ adapter->pdev = pdev;
+ ret = pci_enable_device_mem(pdev);
+ if (ret)
+ goto return_error;
+
+ netif_info(adapter, probe, adapter->netdev,
+ "PCI: Vendor ID = 0x%04X, Device ID = 0x%04X\n",
+ pdev->vendor, pdev->device);
+ bars = pci_select_bars(pdev, IORESOURCE_MEM);
+ if (!test_bit(0, &bars))
+ goto disable_device;
+
+ ret = pci_request_selected_regions(pdev, bars, DRIVER_NAME);
+ if (ret)
+ goto disable_device;
+
+ pci_set_master(pdev);
+ return 0;
+
+disable_device:
+ pci_disable_device(adapter->pdev);
+
+return_error:
+ return ret;
+}
+
+static u32 lan743x_csr_read(struct lan743x_adapter *adapter, int offset)
+{
+ return ioread32(&adapter->csr.csr_address[offset]);
+}
+
+static void lan743x_csr_write(struct lan743x_adapter *adapter, int offset,
+ u32 data)
+{
+ iowrite32(data, &adapter->csr.csr_address[offset]);
+}
+
+#define LAN743X_CSR_READ_OP(offset) lan743x_csr_read(adapter, offset)
+
+static int lan743x_csr_light_reset(struct lan743x_adapter *adapter)
+{
+ u32 data;
+
+ data = lan743x_csr_read(adapter, HW_CFG);
+ data |= HW_CFG_LRST_;
+ lan743x_csr_write(adapter, HW_CFG, data);
+
+ return readx_poll_timeout(LAN743X_CSR_READ_OP, HW_CFG, data,
+ !(data & HW_CFG_LRST_), 100000, 10000000);
+}
+
+static int lan743x_csr_wait_for_bit(struct lan743x_adapter *adapter,
+ int offset, u32 bit_mask,
+ int target_value, int usleep_min,
+ int usleep_max, int count)
+{
+ u32 data;
+
+ return readx_poll_timeout(LAN743X_CSR_READ_OP, offset, data,
+ target_value == ((data & bit_mask) ? 1 : 0),
+ usleep_max, usleep_min * count);
+}
+
+static int lan743x_csr_init(struct lan743x_adapter *adapter)
+{
+ struct lan743x_csr *csr = &adapter->csr;
+ resource_size_t bar_start, bar_length;
+ int result;
+
+ bar_start = pci_resource_start(adapter->pdev, 0);
+ bar_length = pci_resource_len(adapter->pdev, 0);
+ csr->csr_address = devm_ioremap(&adapter->pdev->dev,
+ bar_start, bar_length);
+ if (!csr->csr_address) {
+ result = -ENOMEM;
+ goto clean_up;
+ }
+
+ csr->id_rev = lan743x_csr_read(adapter, ID_REV);
+ csr->fpga_rev = lan743x_csr_read(adapter, FPGA_REV);
+ netif_info(adapter, probe, adapter->netdev,
+ "ID_REV = 0x%08X, FPGA_REV = %d.%d\n",
+ csr->id_rev, FPGA_REV_GET_MAJOR_(csr->fpga_rev),
+ FPGA_REV_GET_MINOR_(csr->fpga_rev));
+ if (!ID_REV_IS_VALID_CHIP_ID_(csr->id_rev)) {
+ result = -ENODEV;
+ goto clean_up;
+ }
+
+ csr->flags = LAN743X_CSR_FLAG_SUPPORTS_INTR_AUTO_SET_CLR;
+ switch (csr->id_rev & ID_REV_CHIP_REV_MASK_) {
+ case ID_REV_CHIP_REV_A0_:
+ csr->flags |= LAN743X_CSR_FLAG_IS_A0;
+ csr->flags &= ~LAN743X_CSR_FLAG_SUPPORTS_INTR_AUTO_SET_CLR;
+ break;
+ case ID_REV_CHIP_REV_B0_:
+ csr->flags |= LAN743X_CSR_FLAG_IS_B0;
+ break;
+ }
+
+ result = lan743x_csr_light_reset(adapter);
+ if (result)
+ goto clean_up;
+ return 0;
+clean_up:
+ return result;
+}
+
+static void lan743x_intr_software_isr(void *context)
+{
+ struct lan743x_adapter *adapter = context;
+ struct lan743x_intr *intr = &adapter->intr;
+ u32 int_sts;
+
+ int_sts = lan743x_csr_read(adapter, INT_STS);
+ if (int_sts & INT_BIT_SW_GP_) {
+ lan743x_csr_write(adapter, INT_STS, INT_BIT_SW_GP_);
+ intr->software_isr_flag = 1;
+ }
+}
+
+static void lan743x_tx_isr(void *context, u32 int_sts, u32 flags)
+{
+ struct lan743x_tx *tx = context;
+ struct lan743x_adapter *adapter = tx->adapter;
+ bool enable_flag = true;
+ u32 int_en = 0;
+
+ int_en = lan743x_csr_read(adapter, INT_EN_SET);
+ if (flags & LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CLEAR) {
+ lan743x_csr_write(adapter, INT_EN_CLR,
+ INT_BIT_DMA_TX_(tx->channel_number));
+ }
+
+ if (int_sts & INT_BIT_DMA_TX_(tx->channel_number)) {
+ u32 ioc_bit = DMAC_INT_BIT_TX_IOC_(tx->channel_number);
+ u32 dmac_int_sts;
+ u32 dmac_int_en;
+
+ if (flags & LAN743X_VECTOR_FLAG_SOURCE_STATUS_READ)
+ dmac_int_sts = lan743x_csr_read(adapter, DMAC_INT_STS);
+ else
+ dmac_int_sts = ioc_bit;
+ if (flags & LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CHECK)
+ dmac_int_en = lan743x_csr_read(adapter,
+ DMAC_INT_EN_SET);
+ else
+ dmac_int_en = ioc_bit;
+
+ dmac_int_en &= ioc_bit;
+ dmac_int_sts &= dmac_int_en;
+ if (dmac_int_sts & ioc_bit) {
+ napi_schedule(&tx->napi);
+ enable_flag = false;/* poll func will enable later */
+ }
+ }
+
+ if (enable_flag)
+ /* enable isr */
+ lan743x_csr_write(adapter, INT_EN_SET,
+ INT_BIT_DMA_TX_(tx->channel_number));
+}
+
+static void lan743x_rx_isr(void *context, u32 int_sts, u32 flags)
+{
+ struct lan743x_rx *rx = context;
+ struct lan743x_adapter *adapter = rx->adapter;
+ bool enable_flag = true;
+
+ if (flags & LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CLEAR) {
+ lan743x_csr_write(adapter, INT_EN_CLR,
+ INT_BIT_DMA_RX_(rx->channel_number));
+ }
+
+ if (int_sts & INT_BIT_DMA_RX_(rx->channel_number)) {
+ u32 rx_frame_bit = DMAC_INT_BIT_RXFRM_(rx->channel_number);
+ u32 dmac_int_sts;
+ u32 dmac_int_en;
+
+ if (flags & LAN743X_VECTOR_FLAG_SOURCE_STATUS_READ)
+ dmac_int_sts = lan743x_csr_read(adapter, DMAC_INT_STS);
+ else
+ dmac_int_sts = rx_frame_bit;
+ if (flags & LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CHECK)
+ dmac_int_en = lan743x_csr_read(adapter,
+ DMAC_INT_EN_SET);
+ else
+ dmac_int_en = rx_frame_bit;
+
+ dmac_int_en &= rx_frame_bit;
+ dmac_int_sts &= dmac_int_en;
+ if (dmac_int_sts & rx_frame_bit) {
+ napi_schedule(&rx->napi);
+ enable_flag = false;/* poll funct will enable later */
+ }
+ }
+
+ if (enable_flag) {
+ /* enable isr */
+ lan743x_csr_write(adapter, INT_EN_SET,
+ INT_BIT_DMA_RX_(rx->channel_number));
+ }
+}
+
+static void lan743x_intr_shared_isr(void *context, u32 int_sts, u32 flags)
+{
+ struct lan743x_adapter *adapter = context;
+ unsigned int channel;
+
+ if (int_sts & INT_BIT_ALL_RX_) {
+ for (channel = 0; channel < LAN743X_USED_RX_CHANNELS;
+ channel++) {
+ u32 int_bit = INT_BIT_DMA_RX_(channel);
+
+ if (int_sts & int_bit) {
+ lan743x_rx_isr(&adapter->rx[channel],
+ int_bit, flags);
+ int_sts &= ~int_bit;
+ }
+ }
+ }
+ if (int_sts & INT_BIT_ALL_TX_) {
+ for (channel = 0; channel < LAN743X_USED_TX_CHANNELS;
+ channel++) {
+ u32 int_bit = INT_BIT_DMA_TX_(channel);
+
+ if (int_sts & int_bit) {
+ lan743x_tx_isr(&adapter->tx[channel],
+ int_bit, flags);
+ int_sts &= ~int_bit;
+ }
+ }
+ }
+ if (int_sts & INT_BIT_ALL_OTHER_) {
+ if (int_sts & INT_BIT_SW_GP_) {
+ lan743x_intr_software_isr(adapter);
+ int_sts &= ~INT_BIT_SW_GP_;
+ }
+ }
+ if (int_sts)
+ lan743x_csr_write(adapter, INT_EN_CLR, int_sts);
+}
+
+static irqreturn_t lan743x_intr_entry_isr(int irq, void *ptr)
+{
+ struct lan743x_vector *vector = ptr;
+ struct lan743x_adapter *adapter = vector->adapter;
+ irqreturn_t result = IRQ_NONE;
+ u32 int_enables;
+ u32 int_sts;
+
+ if (vector->flags & LAN743X_VECTOR_FLAG_SOURCE_STATUS_READ) {
+ int_sts = lan743x_csr_read(adapter, INT_STS);
+ } else if (vector->flags &
+ (LAN743X_VECTOR_FLAG_SOURCE_STATUS_R2C |
+ LAN743X_VECTOR_FLAG_SOURCE_ENABLE_R2C)) {
+ int_sts = lan743x_csr_read(adapter, INT_STS_R2C);
+ } else {
+ /* use mask as implied status */
+ int_sts = vector->int_mask | INT_BIT_MAS_;
+ }
+
+ if (!(int_sts & INT_BIT_MAS_))
+ goto irq_done;
+
+ if (vector->flags & LAN743X_VECTOR_FLAG_VECTOR_ENABLE_ISR_CLEAR)
+ /* disable vector interrupt */
+ lan743x_csr_write(adapter,
+ INT_VEC_EN_CLR,
+ INT_VEC_EN_(vector->vector_index));
+
+ if (vector->flags & LAN743X_VECTOR_FLAG_MASTER_ENABLE_CLEAR)
+ /* disable master interrupt */
+ lan743x_csr_write(adapter, INT_EN_CLR, INT_BIT_MAS_);
+
+ if (vector->flags & LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CHECK) {
+ int_enables = lan743x_csr_read(adapter, INT_EN_SET);
+ } else {
+ /* use vector mask as implied enable mask */
+ int_enables = vector->int_mask;
+ }
+
+ int_sts &= int_enables;
+ int_sts &= vector->int_mask;
+ if (int_sts) {
+ if (vector->handler) {
+ vector->handler(vector->context,
+ int_sts, vector->flags);
+ } else {
+ /* disable interrupts on this vector */
+ lan743x_csr_write(adapter, INT_EN_CLR,
+ vector->int_mask);
+ }
+ result = IRQ_HANDLED;
+ }
+
+ if (vector->flags & LAN743X_VECTOR_FLAG_MASTER_ENABLE_SET)
+ /* enable master interrupt */
+ lan743x_csr_write(adapter, INT_EN_SET, INT_BIT_MAS_);
+
+ if (vector->flags & LAN743X_VECTOR_FLAG_VECTOR_ENABLE_ISR_SET)
+ /* enable vector interrupt */
+ lan743x_csr_write(adapter,
+ INT_VEC_EN_SET,
+ INT_VEC_EN_(vector->vector_index));
+irq_done:
+ return result;
+}
+
+static int lan743x_intr_test_isr(struct lan743x_adapter *adapter)
+{
+ struct lan743x_intr *intr = &adapter->intr;
+ int result = -ENODEV;
+ int timeout = 10;
+
+ intr->software_isr_flag = 0;
+
+ /* enable interrupt */
+ lan743x_csr_write(adapter, INT_EN_SET, INT_BIT_SW_GP_);
+
+ /* activate interrupt here */
+ lan743x_csr_write(adapter, INT_SET, INT_BIT_SW_GP_);
+ while ((timeout > 0) && (!(intr->software_isr_flag))) {
+ usleep_range(1000, 20000);
+ timeout--;
+ }
+
+ if (intr->software_isr_flag)
+ result = 0;
+
+ /* disable interrupts */
+ lan743x_csr_write(adapter, INT_EN_CLR, INT_BIT_SW_GP_);
+ return result;
+}
+
+static int lan743x_intr_register_isr(struct lan743x_adapter *adapter,
+ int vector_index, u32 flags,
+ u32 int_mask,
+ lan743x_vector_handler handler,
+ void *context)
+{
+ struct lan743x_vector *vector = &adapter->intr.vector_list
+ [vector_index];
+ int ret;
+
+ vector->adapter = adapter;
+ vector->flags = flags;
+ vector->vector_index = vector_index;
+ vector->int_mask = int_mask;
+ vector->handler = handler;
+ vector->context = context;
+
+ ret = request_irq(vector->irq,
+ lan743x_intr_entry_isr,
+ (flags & LAN743X_VECTOR_FLAG_IRQ_SHARED) ?
+ IRQF_SHARED : 0, DRIVER_NAME, vector);
+ if (ret) {
+ vector->handler = NULL;
+ vector->context = NULL;
+ vector->int_mask = 0;
+ vector->flags = 0;
+ }
+ return ret;
+}
+
+static void lan743x_intr_unregister_isr(struct lan743x_adapter *adapter,
+ int vector_index)
+{
+ struct lan743x_vector *vector = &adapter->intr.vector_list
+ [vector_index];
+
+ free_irq(vector->irq, vector);
+ vector->handler = NULL;
+ vector->context = NULL;
+ vector->int_mask = 0;
+ vector->flags = 0;
+}
+
+static u32 lan743x_intr_get_vector_flags(struct lan743x_adapter *adapter,
+ u32 int_mask)
+{
+ int index;
+
+ for (index = 0; index < LAN743X_MAX_VECTOR_COUNT; index++) {
+ if (adapter->intr.vector_list[index].int_mask & int_mask)
+ return adapter->intr.vector_list[index].flags;
+ }
+ return 0;
+}
+
+static void lan743x_intr_close(struct lan743x_adapter *adapter)
+{
+ struct lan743x_intr *intr = &adapter->intr;
+ int index = 0;
+
+ lan743x_csr_write(adapter, INT_EN_CLR, INT_BIT_MAS_);
+ lan743x_csr_write(adapter, INT_VEC_EN_CLR, 0x000000FF);
+
+ for (index = 0; index < LAN743X_MAX_VECTOR_COUNT; index++) {
+ if (intr->flags & INTR_FLAG_IRQ_REQUESTED(index)) {
+ lan743x_intr_unregister_isr(adapter, index);
+ intr->flags &= ~INTR_FLAG_IRQ_REQUESTED(index);
+ }
+ }
+
+ if (intr->flags & INTR_FLAG_MSI_ENABLED) {
+ pci_disable_msi(adapter->pdev);
+ intr->flags &= ~INTR_FLAG_MSI_ENABLED;
+ }
+
+ if (intr->flags & INTR_FLAG_MSIX_ENABLED) {
+ pci_disable_msix(adapter->pdev);
+ intr->flags &= ~INTR_FLAG_MSIX_ENABLED;
+ }
+}
+
+static int lan743x_intr_open(struct lan743x_adapter *adapter)
+{
+ struct msix_entry msix_entries[LAN743X_MAX_VECTOR_COUNT];
+ struct lan743x_intr *intr = &adapter->intr;
+ u32 int_vec_en_auto_clr = 0;
+ u32 int_vec_map0 = 0;
+ u32 int_vec_map1 = 0;
+ int ret = -ENODEV;
+ int index = 0;
+ u32 flags = 0;
+
+ intr->number_of_vectors = 0;
+
+ /* Try to set up MSIX interrupts */
+ memset(&msix_entries[0], 0,
+ sizeof(struct msix_entry) * LAN743X_MAX_VECTOR_COUNT);
+ for (index = 0; index < LAN743X_MAX_VECTOR_COUNT; index++)
+ msix_entries[index].entry = index;
+ ret = pci_enable_msix_range(adapter->pdev,
+ msix_entries, 1,
+ 1 + LAN743X_USED_TX_CHANNELS +
+ LAN743X_USED_RX_CHANNELS);
+
+ if (ret > 0) {
+ intr->flags |= INTR_FLAG_MSIX_ENABLED;
+ intr->number_of_vectors = ret;
+ intr->using_vectors = true;
+ for (index = 0; index < intr->number_of_vectors; index++)
+ intr->vector_list[index].irq = msix_entries
+ [index].vector;
+ netif_info(adapter, ifup, adapter->netdev,
+ "using MSIX interrupts, number of vectors = %d\n",
+ intr->number_of_vectors);
+ }
+
+ /* If MSIX failed try to setup using MSI interrupts */
+ if (!intr->number_of_vectors) {
+ if (!(adapter->csr.flags & LAN743X_CSR_FLAG_IS_A0)) {
+ if (!pci_enable_msi(adapter->pdev)) {
+ intr->flags |= INTR_FLAG_MSI_ENABLED;
+ intr->number_of_vectors = 1;
+ intr->using_vectors = true;
+ intr->vector_list[0].irq =
+ adapter->pdev->irq;
+ netif_info(adapter, ifup, adapter->netdev,
+ "using MSI interrupts, number of vectors = %d\n",
+ intr->number_of_vectors);
+ }
+ }
+ }
+
+ /* If MSIX, and MSI failed, setup using legacy interrupt */
+ if (!intr->number_of_vectors) {
+ intr->number_of_vectors = 1;
+ intr->using_vectors = false;
+ intr->vector_list[0].irq = intr->irq;
+ netif_info(adapter, ifup, adapter->netdev,
+ "using legacy interrupts\n");
+ }
+
+ /* At this point we must have at least one irq */
+ lan743x_csr_write(adapter, INT_VEC_EN_CLR, 0xFFFFFFFF);
+
+ /* map all interrupts to vector 0 */
+ lan743x_csr_write(adapter, INT_VEC_MAP0, 0x00000000);
+ lan743x_csr_write(adapter, INT_VEC_MAP1, 0x00000000);
+ lan743x_csr_write(adapter, INT_VEC_MAP2, 0x00000000);
+ flags = LAN743X_VECTOR_FLAG_SOURCE_STATUS_READ |
+ LAN743X_VECTOR_FLAG_SOURCE_STATUS_W2C |
+ LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CHECK |
+ LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CLEAR;
+
+ if (intr->using_vectors) {
+ flags |= LAN743X_VECTOR_FLAG_VECTOR_ENABLE_ISR_CLEAR |
+ LAN743X_VECTOR_FLAG_VECTOR_ENABLE_ISR_SET;
+ } else {
+ flags |= LAN743X_VECTOR_FLAG_MASTER_ENABLE_CLEAR |
+ LAN743X_VECTOR_FLAG_MASTER_ENABLE_SET |
+ LAN743X_VECTOR_FLAG_IRQ_SHARED;
+ }
+
+ if (adapter->csr.flags & LAN743X_CSR_FLAG_SUPPORTS_INTR_AUTO_SET_CLR) {
+ flags &= ~LAN743X_VECTOR_FLAG_SOURCE_STATUS_READ;
+ flags &= ~LAN743X_VECTOR_FLAG_SOURCE_STATUS_W2C;
+ flags &= ~LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CLEAR;
+ flags &= ~LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CHECK;
+ flags |= LAN743X_VECTOR_FLAG_SOURCE_STATUS_R2C;
+ flags |= LAN743X_VECTOR_FLAG_SOURCE_ENABLE_R2C;
+ }
+
+ ret = lan743x_intr_register_isr(adapter, 0, flags,
+ INT_BIT_ALL_RX_ | INT_BIT_ALL_TX_ |
+ INT_BIT_ALL_OTHER_,
+ lan743x_intr_shared_isr, adapter);
+ if (ret)
+ goto clean_up;
+ intr->flags |= INTR_FLAG_IRQ_REQUESTED(0);
+
+ if (intr->using_vectors)
+ lan743x_csr_write(adapter, INT_VEC_EN_SET,
+ INT_VEC_EN_(0));
+
+ if (!(adapter->csr.flags & LAN743X_CSR_FLAG_IS_A0)) {
+ lan743x_csr_write(adapter, INT_MOD_CFG0, LAN743X_INT_MOD);
+ lan743x_csr_write(adapter, INT_MOD_CFG1, LAN743X_INT_MOD);
+ lan743x_csr_write(adapter, INT_MOD_CFG2, LAN743X_INT_MOD);
+ lan743x_csr_write(adapter, INT_MOD_CFG3, LAN743X_INT_MOD);
+ lan743x_csr_write(adapter, INT_MOD_CFG4, LAN743X_INT_MOD);
+ lan743x_csr_write(adapter, INT_MOD_CFG5, LAN743X_INT_MOD);
+ lan743x_csr_write(adapter, INT_MOD_CFG6, LAN743X_INT_MOD);
+ lan743x_csr_write(adapter, INT_MOD_CFG7, LAN743X_INT_MOD);
+ lan743x_csr_write(adapter, INT_MOD_MAP0, 0x00005432);
+ lan743x_csr_write(adapter, INT_MOD_MAP1, 0x00000001);
+ lan743x_csr_write(adapter, INT_MOD_MAP2, 0x00FFFFFF);
+ }
+
+ /* enable interrupts */
+ lan743x_csr_write(adapter, INT_EN_SET, INT_BIT_MAS_);
+ ret = lan743x_intr_test_isr(adapter);
+ if (ret)
+ goto clean_up;
+
+ if (intr->number_of_vectors > 1) {
+ int number_of_tx_vectors = intr->number_of_vectors - 1;
+
+ if (number_of_tx_vectors > LAN743X_USED_TX_CHANNELS)
+ number_of_tx_vectors = LAN743X_USED_TX_CHANNELS;
+ flags = LAN743X_VECTOR_FLAG_SOURCE_STATUS_READ |
+ LAN743X_VECTOR_FLAG_SOURCE_STATUS_W2C |
+ LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CHECK |
+ LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CLEAR |
+ LAN743X_VECTOR_FLAG_VECTOR_ENABLE_ISR_CLEAR |
+ LAN743X_VECTOR_FLAG_VECTOR_ENABLE_ISR_SET;
+
+ if (adapter->csr.flags &
+ LAN743X_CSR_FLAG_SUPPORTS_INTR_AUTO_SET_CLR) {
+ flags = LAN743X_VECTOR_FLAG_VECTOR_ENABLE_AUTO_CLEAR |
+ LAN743X_VECTOR_FLAG_VECTOR_ENABLE_AUTO_SET |
+ LAN743X_VECTOR_FLAG_SOURCE_ENABLE_AUTO_SET |
+ LAN743X_VECTOR_FLAG_SOURCE_ENABLE_AUTO_CLEAR |
+ LAN743X_VECTOR_FLAG_SOURCE_STATUS_AUTO_CLEAR;
+ }
+
+ for (index = 0; index < number_of_tx_vectors; index++) {
+ u32 int_bit = INT_BIT_DMA_TX_(index);
+ int vector = index + 1;
+
+ /* map TX interrupt to vector */
+ int_vec_map1 |= INT_VEC_MAP1_TX_VEC_(index, vector);
+ lan743x_csr_write(adapter, INT_VEC_MAP1, int_vec_map1);
+ if (flags &
+ LAN743X_VECTOR_FLAG_VECTOR_ENABLE_AUTO_CLEAR) {
+ int_vec_en_auto_clr |= INT_VEC_EN_(vector);
+ lan743x_csr_write(adapter, INT_VEC_EN_AUTO_CLR,
+ int_vec_en_auto_clr);
+ }
+
+ /* Remove TX interrupt from shared mask */
+ intr->vector_list[0].int_mask &= ~int_bit;
+ ret = lan743x_intr_register_isr(adapter, vector, flags,
+ int_bit, lan743x_tx_isr,
+ &adapter->tx[index]);
+ if (ret)
+ goto clean_up;
+ intr->flags |= INTR_FLAG_IRQ_REQUESTED(vector);
+ if (!(flags &
+ LAN743X_VECTOR_FLAG_VECTOR_ENABLE_AUTO_SET))
+ lan743x_csr_write(adapter, INT_VEC_EN_SET,
+ INT_VEC_EN_(vector));
+ }
+ }
+ if ((intr->number_of_vectors - LAN743X_USED_TX_CHANNELS) > 1) {
+ int number_of_rx_vectors = intr->number_of_vectors -
+ LAN743X_USED_TX_CHANNELS - 1;
+
+ if (number_of_rx_vectors > LAN743X_USED_RX_CHANNELS)
+ number_of_rx_vectors = LAN743X_USED_RX_CHANNELS;
+
+ flags = LAN743X_VECTOR_FLAG_SOURCE_STATUS_READ |
+ LAN743X_VECTOR_FLAG_SOURCE_STATUS_W2C |
+ LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CHECK |
+ LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CLEAR |
+ LAN743X_VECTOR_FLAG_VECTOR_ENABLE_ISR_CLEAR |
+ LAN743X_VECTOR_FLAG_VECTOR_ENABLE_ISR_SET;
+
+ if (adapter->csr.flags &
+ LAN743X_CSR_FLAG_SUPPORTS_INTR_AUTO_SET_CLR) {
+ flags = LAN743X_VECTOR_FLAG_VECTOR_ENABLE_AUTO_CLEAR |
+ LAN743X_VECTOR_FLAG_VECTOR_ENABLE_AUTO_SET |
+ LAN743X_VECTOR_FLAG_SOURCE_ENABLE_AUTO_SET |
+ LAN743X_VECTOR_FLAG_SOURCE_ENABLE_AUTO_CLEAR |
+ LAN743X_VECTOR_FLAG_SOURCE_STATUS_AUTO_CLEAR;
+ }
+ for (index = 0; index < number_of_rx_vectors; index++) {
+ int vector = index + 1 + LAN743X_USED_TX_CHANNELS;
+ u32 int_bit = INT_BIT_DMA_RX_(index);
+
+ /* map RX interrupt to vector */
+ int_vec_map0 |= INT_VEC_MAP0_RX_VEC_(index, vector);
+ lan743x_csr_write(adapter, INT_VEC_MAP0, int_vec_map0);
+ if (flags &
+ LAN743X_VECTOR_FLAG_VECTOR_ENABLE_AUTO_CLEAR) {
+ int_vec_en_auto_clr |= INT_VEC_EN_(vector);
+ lan743x_csr_write(adapter, INT_VEC_EN_AUTO_CLR,
+ int_vec_en_auto_clr);
+ }
+
+ /* Remove RX interrupt from shared mask */
+ intr->vector_list[0].int_mask &= ~int_bit;
+ ret = lan743x_intr_register_isr(adapter, vector, flags,
+ int_bit, lan743x_rx_isr,
+ &adapter->rx[index]);
+ if (ret)
+ goto clean_up;
+ intr->flags |= INTR_FLAG_IRQ_REQUESTED(vector);
+
+ lan743x_csr_write(adapter, INT_VEC_EN_SET,
+ INT_VEC_EN_(vector));
+ }
+ }
+ return 0;
+
+clean_up:
+ lan743x_intr_close(adapter);
+ return ret;
+}
+
+static int lan743x_dp_write(struct lan743x_adapter *adapter,
+ u32 select, u32 addr, u32 length, u32 *buf)
+{
+ int ret = -EIO;
+ u32 dp_sel;
+ int i;
+
+ mutex_lock(&adapter->dp_lock);
+ if (lan743x_csr_wait_for_bit(adapter, DP_SEL, DP_SEL_DPRDY_,
+ 1, 40, 100, 100))
+ goto unlock;
+ dp_sel = lan743x_csr_read(adapter, DP_SEL);
+ dp_sel &= ~DP_SEL_MASK_;
+ dp_sel |= select;
+ lan743x_csr_write(adapter, DP_SEL, dp_sel);
+
+ for (i = 0; i < length; i++) {
+ lan743x_csr_write(adapter, DP_ADDR, addr + i);
+ lan743x_csr_write(adapter, DP_DATA_0, buf[i]);
+ lan743x_csr_write(adapter, DP_CMD, DP_CMD_WRITE_);
+ if (lan743x_csr_wait_for_bit(adapter, DP_SEL, DP_SEL_DPRDY_,
+ 1, 40, 100, 100))
+ goto unlock;
+ }
+ ret = 0;
+
+unlock:
+ mutex_unlock(&adapter->dp_lock);
+ return ret;
+}
+
+static u32 lan743x_mac_mii_access(u16 id, u16 index, int read)
+{
+ u32 ret;
+
+ ret = (id << MAC_MII_ACC_PHY_ADDR_SHIFT_) &
+ MAC_MII_ACC_PHY_ADDR_MASK_;
+ ret |= (index << MAC_MII_ACC_MIIRINDA_SHIFT_) &
+ MAC_MII_ACC_MIIRINDA_MASK_;
+
+ if (read)
+ ret |= MAC_MII_ACC_MII_READ_;
+ else
+ ret |= MAC_MII_ACC_MII_WRITE_;
+ ret |= MAC_MII_ACC_MII_BUSY_;
+
+ return ret;
+}
+
+static int lan743x_mac_mii_wait_till_not_busy(struct lan743x_adapter *adapter)
+{
+ u32 data;
+
+ return readx_poll_timeout(LAN743X_CSR_READ_OP, MAC_MII_ACC, data,
+ !(data & MAC_MII_ACC_MII_BUSY_), 0, 1000000);
+}
+
+static int lan743x_mdiobus_read(struct mii_bus *bus, int phy_id, int index)
+{
+ struct lan743x_adapter *adapter = bus->priv;
+ u32 val, mii_access;
+ int ret;
+
+ /* comfirm MII not busy */
+ ret = lan743x_mac_mii_wait_till_not_busy(adapter);
+ if (ret < 0)
+ return ret;
+
+ /* set the address, index & direction (read from PHY) */
+ mii_access = lan743x_mac_mii_access(phy_id, index, MAC_MII_READ);
+ lan743x_csr_write(adapter, MAC_MII_ACC, mii_access);
+ ret = lan743x_mac_mii_wait_till_not_busy(adapter);
+ if (ret < 0)
+ return ret;
+
+ val = lan743x_csr_read(adapter, MAC_MII_DATA);
+ return (int)(val & 0xFFFF);
+}
+
+static int lan743x_mdiobus_write(struct mii_bus *bus,
+ int phy_id, int index, u16 regval)
+{
+ struct lan743x_adapter *adapter = bus->priv;
+ u32 val, mii_access;
+ int ret;
+
+ /* confirm MII not busy */
+ ret = lan743x_mac_mii_wait_till_not_busy(adapter);
+ if (ret < 0)
+ return ret;
+ val = (u32)regval;
+ lan743x_csr_write(adapter, MAC_MII_DATA, val);
+
+ /* set the address, index & direction (write to PHY) */
+ mii_access = lan743x_mac_mii_access(phy_id, index, MAC_MII_WRITE);
+ lan743x_csr_write(adapter, MAC_MII_ACC, mii_access);
+ ret = lan743x_mac_mii_wait_till_not_busy(adapter);
+ return ret;
+}
+
+static void lan743x_mac_set_address(struct lan743x_adapter *adapter,
+ u8 *addr)
+{
+ u32 addr_lo, addr_hi;
+
+ addr_lo = addr[0] |
+ addr[1] << 8 |
+ addr[2] << 16 |
+ addr[3] << 24;
+ addr_hi = addr[4] |
+ addr[5] << 8;
+ lan743x_csr_write(adapter, MAC_RX_ADDRL, addr_lo);
+ lan743x_csr_write(adapter, MAC_RX_ADDRH, addr_hi);
+
+ ether_addr_copy(adapter->mac_address, addr);
+ netif_info(adapter, drv, adapter->netdev,
+ "MAC address set to %pM\n", addr);
+}
+
+static int lan743x_mac_init(struct lan743x_adapter *adapter)
+{
+ bool mac_address_valid = true;
+ struct net_device *netdev;
+ u32 mac_addr_hi = 0;
+ u32 mac_addr_lo = 0;
+ u32 data;
+ int ret;
+
+ netdev = adapter->netdev;
+ lan743x_csr_write(adapter, MAC_CR, MAC_CR_RST_);
+ ret = lan743x_csr_wait_for_bit(adapter, MAC_CR, MAC_CR_RST_,
+ 0, 1000, 20000, 100);
+ if (ret)
+ return ret;
+
+ /* setup auto duplex, and speed detection */
+ data = lan743x_csr_read(adapter, MAC_CR);
+ data |= MAC_CR_ADD_ | MAC_CR_ASD_;
+ data |= MAC_CR_CNTR_RST_;
+ lan743x_csr_write(adapter, MAC_CR, data);
+
+ mac_addr_hi = lan743x_csr_read(adapter, MAC_RX_ADDRH);
+ mac_addr_lo = lan743x_csr_read(adapter, MAC_RX_ADDRL);
+ adapter->mac_address[0] = mac_addr_lo & 0xFF;
+ adapter->mac_address[1] = (mac_addr_lo >> 8) & 0xFF;
+ adapter->mac_address[2] = (mac_addr_lo >> 16) & 0xFF;
+ adapter->mac_address[3] = (mac_addr_lo >> 24) & 0xFF;
+ adapter->mac_address[4] = mac_addr_hi & 0xFF;
+ adapter->mac_address[5] = (mac_addr_hi >> 8) & 0xFF;
+
+ if (((mac_addr_hi & 0x0000FFFF) == 0x0000FFFF) &&
+ mac_addr_lo == 0xFFFFFFFF) {
+ mac_address_valid = false;
+ } else if (!is_valid_ether_addr(adapter->mac_address)) {
+ mac_address_valid = false;
+ }
+
+ if (!mac_address_valid)
+ random_ether_addr(adapter->mac_address);
+ lan743x_mac_set_address(adapter, adapter->mac_address);
+ ether_addr_copy(netdev->dev_addr, adapter->mac_address);
+ return 0;
+}
+
+static int lan743x_mac_open(struct lan743x_adapter *adapter)
+{
+ int ret = 0;
+ u32 temp;
+
+ temp = lan743x_csr_read(adapter, MAC_RX);
+ lan743x_csr_write(adapter, MAC_RX, temp | MAC_RX_RXEN_);
+ temp = lan743x_csr_read(adapter, MAC_TX);
+ lan743x_csr_write(adapter, MAC_TX, temp | MAC_TX_TXEN_);
+ return ret;
+}
+
+static void lan743x_mac_close(struct lan743x_adapter *adapter)
+{
+ u32 temp;
+
+ temp = lan743x_csr_read(adapter, MAC_TX);
+ temp &= ~MAC_TX_TXEN_;
+ lan743x_csr_write(adapter, MAC_TX, temp);
+ lan743x_csr_wait_for_bit(adapter, MAC_TX, MAC_TX_TXD_,
+ 1, 1000, 20000, 100);
+
+ temp = lan743x_csr_read(adapter, MAC_RX);
+ temp &= ~MAC_RX_RXEN_;
+ lan743x_csr_write(adapter, MAC_RX, temp);
+ lan743x_csr_wait_for_bit(adapter, MAC_RX, MAC_RX_RXD_,
+ 1, 1000, 20000, 100);
+}
+
+static void lan743x_mac_flow_ctrl_set_enables(struct lan743x_adapter *adapter,
+ bool tx_enable, bool rx_enable)
+{
+ u32 flow_setting = 0;
+
+ /* set maximum pause time because when fifo space frees
+ * up a zero value pause frame will be sent to release the pause
+ */
+ flow_setting = MAC_FLOW_CR_FCPT_MASK_;
+ if (tx_enable)
+ flow_setting |= MAC_FLOW_CR_TX_FCEN_;
+ if (rx_enable)
+ flow_setting |= MAC_FLOW_CR_RX_FCEN_;
+ lan743x_csr_write(adapter, MAC_FLOW, flow_setting);
+}
+
+static int lan743x_mac_set_mtu(struct lan743x_adapter *adapter, int new_mtu)
+{
+ int enabled = 0;
+ u32 mac_rx = 0;
+
+ mac_rx = lan743x_csr_read(adapter, MAC_RX);
+ if (mac_rx & MAC_RX_RXEN_) {
+ enabled = 1;
+ if (mac_rx & MAC_RX_RXD_) {
+ lan743x_csr_write(adapter, MAC_RX, mac_rx);
+ mac_rx &= ~MAC_RX_RXD_;
+ }
+ mac_rx &= ~MAC_RX_RXEN_;
+ lan743x_csr_write(adapter, MAC_RX, mac_rx);
+ lan743x_csr_wait_for_bit(adapter, MAC_RX, MAC_RX_RXD_,
+ 1, 1000, 20000, 100);
+ lan743x_csr_write(adapter, MAC_RX, mac_rx | MAC_RX_RXD_);
+ }
+
+ mac_rx &= ~(MAC_RX_MAX_SIZE_MASK_);
+ mac_rx |= (((new_mtu + ETH_HLEN + 4) << MAC_RX_MAX_SIZE_SHIFT_) &
+ MAC_RX_MAX_SIZE_MASK_);
+ lan743x_csr_write(adapter, MAC_RX, mac_rx);
+
+ if (enabled) {
+ mac_rx |= MAC_RX_RXEN_;
+ lan743x_csr_write(adapter, MAC_RX, mac_rx);
+ }
+ return 0;
+}
+
+/* PHY */
+static int lan743x_phy_reset(struct lan743x_adapter *adapter)
+{
+ u32 data;
+
+ /* Only called with in probe, and before mdiobus_register */
+
+ data = lan743x_csr_read(adapter, PMT_CTL);
+ data |= PMT_CTL_ETH_PHY_RST_;
+ lan743x_csr_write(adapter, PMT_CTL, data);
+
+ return readx_poll_timeout(LAN743X_CSR_READ_OP, PMT_CTL, data,
+ (!(data & PMT_CTL_ETH_PHY_RST_) &&
+ (data & PMT_CTL_READY_)),
+ 50000, 1000000);
+}
+
+static void lan743x_phy_update_flowcontrol(struct lan743x_adapter *adapter,
+ u8 duplex, u16 local_adv,
+ u16 remote_adv)
+{
+ struct lan743x_phy *phy = &adapter->phy;
+ u8 cap;
+
+ if (phy->fc_autoneg)
+ cap = mii_resolve_flowctrl_fdx(local_adv, remote_adv);
+ else
+ cap = phy->fc_request_control;
+
+ lan743x_mac_flow_ctrl_set_enables(adapter,
+ cap & FLOW_CTRL_TX,
+ cap & FLOW_CTRL_RX);
+}
+
+static int lan743x_phy_init(struct lan743x_adapter *adapter)
+{
+ return lan743x_phy_reset(adapter);
+}
+
+static void lan743x_phy_link_status_change(struct net_device *netdev)
+{
+ struct lan743x_adapter *adapter = netdev_priv(netdev);
+ struct phy_device *phydev = netdev->phydev;
+
+ phy_print_status(phydev);
+ if (phydev->state == PHY_RUNNING) {
+ struct ethtool_link_ksettings ksettings;
+ int remote_advertisement = 0;
+ int local_advertisement = 0;
+
+ memset(&ksettings, 0, sizeof(ksettings));
+ phy_ethtool_get_link_ksettings(netdev, &ksettings);
+ local_advertisement = phy_read(phydev, MII_ADVERTISE);
+ if (local_advertisement < 0)
+ return;
+
+ remote_advertisement = phy_read(phydev, MII_LPA);
+ if (remote_advertisement < 0)
+ return;
+
+ lan743x_phy_update_flowcontrol(adapter,
+ ksettings.base.duplex,
+ local_advertisement,
+ remote_advertisement);
+ }
+}
+
+static void lan743x_phy_close(struct lan743x_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+
+ phy_stop(netdev->phydev);
+ phy_disconnect(netdev->phydev);
+ netdev->phydev = NULL;
+}
+
+static int lan743x_phy_open(struct lan743x_adapter *adapter)
+{
+ struct lan743x_phy *phy = &adapter->phy;
+ struct phy_device *phydev;
+ struct net_device *netdev;
+ int ret = -EIO;
+ u32 mii_adv;
+
+ netdev = adapter->netdev;
+ phydev = phy_find_first(adapter->mdiobus);
+ if (!phydev)
+ goto return_error;
+
+ ret = phy_connect_direct(netdev, phydev,
+ lan743x_phy_link_status_change,
+ PHY_INTERFACE_MODE_GMII);
+ if (ret)
+ goto return_error;
+
+ /* MAC doesn't support 1000T Half */
+ phydev->supported &= ~SUPPORTED_1000baseT_Half;
+
+ /* support both flow controls */
+ phy->fc_request_control = (FLOW_CTRL_RX | FLOW_CTRL_TX);
+ phydev->advertising &= ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause);
+ mii_adv = (u32)mii_advertise_flowctrl(phy->fc_request_control);
+ phydev->advertising |= mii_adv_to_ethtool_adv_t(mii_adv);
+ phy->fc_autoneg = phydev->autoneg;
+
+ phy_start(phydev);
+ phy_start_aneg(phydev);
+ return 0;
+
+return_error:
+ return ret;
+}
+
+static void lan743x_rfe_update_mac_address(struct lan743x_adapter *adapter)
+{
+ u8 *mac_addr;
+ u32 mac_addr_hi = 0;
+ u32 mac_addr_lo = 0;
+
+ /* Add mac address to perfect Filter */
+ mac_addr = adapter->mac_address;
+ mac_addr_lo = ((((u32)(mac_addr[0])) << 0) |
+ (((u32)(mac_addr[1])) << 8) |
+ (((u32)(mac_addr[2])) << 16) |
+ (((u32)(mac_addr[3])) << 24));
+ mac_addr_hi = ((((u32)(mac_addr[4])) << 0) |
+ (((u32)(mac_addr[5])) << 8));
+
+ lan743x_csr_write(adapter, RFE_ADDR_FILT_LO(0), mac_addr_lo);
+ lan743x_csr_write(adapter, RFE_ADDR_FILT_HI(0),
+ mac_addr_hi | RFE_ADDR_FILT_HI_VALID_);
+}
+
+static void lan743x_rfe_set_multicast(struct lan743x_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+ u32 hash_table[DP_SEL_VHF_HASH_LEN];
+ u32 rfctl;
+ u32 data;
+
+ rfctl = lan743x_csr_read(adapter, RFE_CTL);
+ rfctl &= ~(RFE_CTL_AU_ | RFE_CTL_AM_ |
+ RFE_CTL_DA_PERFECT_ | RFE_CTL_MCAST_HASH_);
+ rfctl |= RFE_CTL_AB_;
+ if (netdev->flags & IFF_PROMISC) {
+ rfctl |= RFE_CTL_AM_ | RFE_CTL_AU_;
+ } else {
+ if (netdev->flags & IFF_ALLMULTI)
+ rfctl |= RFE_CTL_AM_;
+ }
+
+ memset(hash_table, 0, DP_SEL_VHF_HASH_LEN * sizeof(u32));
+ if (netdev_mc_count(netdev)) {
+ struct netdev_hw_addr *ha;
+ int i;
+
+ rfctl |= RFE_CTL_DA_PERFECT_;
+ i = 1;
+ netdev_for_each_mc_addr(ha, netdev) {
+ /* set first 32 into Perfect Filter */
+ if (i < 33) {
+ lan743x_csr_write(adapter,
+ RFE_ADDR_FILT_HI(i), 0);
+ data = ha->addr[3];
+ data = ha->addr[2] | (data << 8);
+ data = ha->addr[1] | (data << 8);
+ data = ha->addr[0] | (data << 8);
+ lan743x_csr_write(adapter,
+ RFE_ADDR_FILT_LO(i), data);
+ data = ha->addr[5];
+ data = ha->addr[4] | (data << 8);
+ data |= RFE_ADDR_FILT_HI_VALID_;
+ lan743x_csr_write(adapter,
+ RFE_ADDR_FILT_HI(i), data);
+ } else {
+ u32 bitnum = (ether_crc(ETH_ALEN, ha->addr) >>
+ 23) & 0x1FF;
+ hash_table[bitnum / 32] |= (1 << (bitnum % 32));
+ rfctl |= RFE_CTL_MCAST_HASH_;
+ }
+ i++;
+ }
+ }
+
+ lan743x_dp_write(adapter, DP_SEL_RFE_RAM,
+ DP_SEL_VHF_VLAN_LEN,
+ DP_SEL_VHF_HASH_LEN, hash_table);
+ lan743x_csr_write(adapter, RFE_CTL, rfctl);
+}
+
+static int lan743x_dmac_init(struct lan743x_adapter *adapter)
+{
+ u32 data = 0;
+
+ lan743x_csr_write(adapter, DMAC_CMD, DMAC_CMD_SWR_);
+ lan743x_csr_wait_for_bit(adapter, DMAC_CMD, DMAC_CMD_SWR_,
+ 0, 1000, 20000, 100);
+ switch (DEFAULT_DMA_DESCRIPTOR_SPACING) {
+ case DMA_DESCRIPTOR_SPACING_16:
+ data = DMAC_CFG_MAX_DSPACE_16_;
+ break;
+ case DMA_DESCRIPTOR_SPACING_32:
+ data = DMAC_CFG_MAX_DSPACE_32_;
+ break;
+ case DMA_DESCRIPTOR_SPACING_64:
+ data = DMAC_CFG_MAX_DSPACE_64_;
+ break;
+ case DMA_DESCRIPTOR_SPACING_128:
+ data = DMAC_CFG_MAX_DSPACE_128_;
+ break;
+ default:
+ return -EPERM;
+ }
+ if (!(adapter->csr.flags & LAN743X_CSR_FLAG_IS_A0))
+ data |= DMAC_CFG_COAL_EN_;
+ data |= DMAC_CFG_CH_ARB_SEL_RX_HIGH_;
+ data |= DMAC_CFG_MAX_READ_REQ_SET_(6);
+ lan743x_csr_write(adapter, DMAC_CFG, data);
+ data = DMAC_COAL_CFG_TIMER_LIMIT_SET_(1);
+ data |= DMAC_COAL_CFG_TIMER_TX_START_;
+ data |= DMAC_COAL_CFG_FLUSH_INTS_;
+ data |= DMAC_COAL_CFG_INT_EXIT_COAL_;
+ data |= DMAC_COAL_CFG_CSR_EXIT_COAL_;
+ data |= DMAC_COAL_CFG_TX_THRES_SET_(0x0A);
+ data |= DMAC_COAL_CFG_RX_THRES_SET_(0x0C);
+ lan743x_csr_write(adapter, DMAC_COAL_CFG, data);
+ data = DMAC_OBFF_TX_THRES_SET_(0x08);
+ data |= DMAC_OBFF_RX_THRES_SET_(0x0A);
+ lan743x_csr_write(adapter, DMAC_OBFF_CFG, data);
+ return 0;
+}
+
+static int lan743x_dmac_tx_get_state(struct lan743x_adapter *adapter,
+ int tx_channel)
+{
+ u32 dmac_cmd = 0;
+
+ dmac_cmd = lan743x_csr_read(adapter, DMAC_CMD);
+ return DMAC_CHANNEL_STATE_SET((dmac_cmd &
+ DMAC_CMD_START_T_(tx_channel)),
+ (dmac_cmd &
+ DMAC_CMD_STOP_T_(tx_channel)));
+}
+
+static int lan743x_dmac_tx_wait_till_stopped(struct lan743x_adapter *adapter,
+ int tx_channel)
+{
+ int timeout = 100;
+ int result = 0;
+
+ while (timeout &&
+ ((result = lan743x_dmac_tx_get_state(adapter, tx_channel)) ==
+ DMAC_CHANNEL_STATE_STOP_PENDING)) {
+ usleep_range(1000, 20000);
+ timeout--;
+ }
+ if (result == DMAC_CHANNEL_STATE_STOP_PENDING)
+ result = -ENODEV;
+ return result;
+}
+
+static int lan743x_dmac_rx_get_state(struct lan743x_adapter *adapter,
+ int rx_channel)
+{
+ u32 dmac_cmd = 0;
+
+ dmac_cmd = lan743x_csr_read(adapter, DMAC_CMD);
+ return DMAC_CHANNEL_STATE_SET((dmac_cmd &
+ DMAC_CMD_START_R_(rx_channel)),
+ (dmac_cmd &
+ DMAC_CMD_STOP_R_(rx_channel)));
+}
+
+static int lan743x_dmac_rx_wait_till_stopped(struct lan743x_adapter *adapter,
+ int rx_channel)
+{
+ int timeout = 100;
+ int result = 0;
+
+ while (timeout &&
+ ((result = lan743x_dmac_rx_get_state(adapter, rx_channel)) ==
+ DMAC_CHANNEL_STATE_STOP_PENDING)) {
+ usleep_range(1000, 20000);
+ timeout--;
+ }
+ if (result == DMAC_CHANNEL_STATE_STOP_PENDING)
+ result = -ENODEV;
+ return result;
+}
+
+static void lan743x_tx_release_desc(struct lan743x_tx *tx,
+ int descriptor_index, bool cleanup)
+{
+ struct lan743x_tx_buffer_info *buffer_info = NULL;
+ struct lan743x_tx_descriptor *descriptor = NULL;
+ u32 descriptor_type = 0;
+
+ descriptor = &tx->ring_cpu_ptr[descriptor_index];
+ buffer_info = &tx->buffer_info[descriptor_index];
+ if (!(buffer_info->flags & TX_BUFFER_INFO_FLAG_ACTIVE))
+ goto done;
+
+ descriptor_type = (descriptor->data0) &
+ TX_DESC_DATA0_DTYPE_MASK_;
+ if (descriptor_type == TX_DESC_DATA0_DTYPE_DATA_)
+ goto clean_up_data_descriptor;
+ else
+ goto clear_active;
+
+clean_up_data_descriptor:
+ if (buffer_info->dma_ptr) {
+ if (buffer_info->flags &
+ TX_BUFFER_INFO_FLAG_SKB_FRAGMENT) {
+ dma_unmap_page(&tx->adapter->pdev->dev,
+ buffer_info->dma_ptr,
+ buffer_info->buffer_length,
+ DMA_TO_DEVICE);
+ } else {
+ dma_unmap_single(&tx->adapter->pdev->dev,
+ buffer_info->dma_ptr,
+ buffer_info->buffer_length,
+ DMA_TO_DEVICE);
+ }
+ buffer_info->dma_ptr = 0;
+ buffer_info->buffer_length = 0;
+ }
+ if (buffer_info->skb) {
+ dev_kfree_skb(buffer_info->skb);
+ buffer_info->skb = NULL;
+ }
+
+clear_active:
+ buffer_info->flags &= ~TX_BUFFER_INFO_FLAG_ACTIVE;
+
+done:
+ memset(buffer_info, 0, sizeof(*buffer_info));
+ memset(descriptor, 0, sizeof(*descriptor));
+}
+
+static int lan743x_tx_next_index(struct lan743x_tx *tx, int index)
+{
+ return ((++index) % tx->ring_size);
+}
+
+static void lan743x_tx_release_completed_descriptors(struct lan743x_tx *tx)
+{
+ while ((*tx->head_cpu_ptr) != (tx->last_head)) {
+ lan743x_tx_release_desc(tx, tx->last_head, false);
+ tx->last_head = lan743x_tx_next_index(tx, tx->last_head);
+ }
+}
+
+static void lan743x_tx_release_all_descriptors(struct lan743x_tx *tx)
+{
+ u32 original_head = 0;
+
+ original_head = tx->last_head;
+ do {
+ lan743x_tx_release_desc(tx, tx->last_head, true);
+ tx->last_head = lan743x_tx_next_index(tx, tx->last_head);
+ } while (tx->last_head != original_head);
+ memset(tx->ring_cpu_ptr, 0,
+ sizeof(*tx->ring_cpu_ptr) * (tx->ring_size));
+ memset(tx->buffer_info, 0,
+ sizeof(*tx->buffer_info) * (tx->ring_size));
+}
+
+static int lan743x_tx_get_desc_cnt(struct lan743x_tx *tx,
+ struct sk_buff *skb)
+{
+ int result = 1; /* 1 for the main skb buffer */
+ int nr_frags = 0;
+
+ if (skb_is_gso(skb))
+ result++; /* requires an extension descriptor */
+ nr_frags = skb_shinfo(skb)->nr_frags;
+ result += nr_frags; /* 1 for each fragment buffer */
+ return result;
+}
+
+static int lan743x_tx_get_avail_desc(struct lan743x_tx *tx)
+{
+ int last_head = tx->last_head;
+ int last_tail = tx->last_tail;
+
+ if (last_tail >= last_head)
+ return tx->ring_size - last_tail + last_head - 1;
+ else
+ return last_head - last_tail - 1;
+}
+
+static int lan743x_tx_frame_start(struct lan743x_tx *tx,
+ unsigned char *first_buffer,
+ unsigned int first_buffer_length,
+ unsigned int frame_length,
+ bool check_sum)
+{
+ /* called only from within lan743x_tx_xmit_frame.
+ * assuming tx->ring_lock has already been acquired.
+ */
+ struct lan743x_tx_descriptor *tx_descriptor = NULL;
+ struct lan743x_tx_buffer_info *buffer_info = NULL;
+ struct lan743x_adapter *adapter = tx->adapter;
+ struct device *dev = &adapter->pdev->dev;
+ dma_addr_t dma_ptr;
+
+ tx->frame_flags |= TX_FRAME_FLAG_IN_PROGRESS;
+ tx->frame_first = tx->last_tail;
+ tx->frame_tail = tx->frame_first;
+
+ tx_descriptor = &tx->ring_cpu_ptr[tx->frame_tail];
+ buffer_info = &tx->buffer_info[tx->frame_tail];
+ dma_ptr = dma_map_single(dev, first_buffer, first_buffer_length,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, dma_ptr))
+ return -ENOMEM;
+
+ tx_descriptor->data1 = DMA_ADDR_LOW32(dma_ptr);
+ tx_descriptor->data2 = DMA_ADDR_HIGH32(dma_ptr);
+ tx_descriptor->data3 = (frame_length << 16) &
+ TX_DESC_DATA3_FRAME_LENGTH_MSS_MASK_;
+
+ buffer_info->skb = NULL;
+ buffer_info->dma_ptr = dma_ptr;
+ buffer_info->buffer_length = first_buffer_length;
+ buffer_info->flags |= TX_BUFFER_INFO_FLAG_ACTIVE;
+
+ tx->frame_data0 = (first_buffer_length &
+ TX_DESC_DATA0_BUF_LENGTH_MASK_) |
+ TX_DESC_DATA0_DTYPE_DATA_ |
+ TX_DESC_DATA0_FS_ |
+ TX_DESC_DATA0_FCS_;
+
+ if (check_sum)
+ tx->frame_data0 |= TX_DESC_DATA0_ICE_ |
+ TX_DESC_DATA0_IPE_ |
+ TX_DESC_DATA0_TPE_;
+
+ /* data0 will be programmed in one of other frame assembler functions */
+ return 0;
+}
+
+static void lan743x_tx_frame_add_lso(struct lan743x_tx *tx,
+ unsigned int frame_length)
+{
+ /* called only from within lan743x_tx_xmit_frame.
+ * assuming tx->ring_lock has already been acquired.
+ */
+ struct lan743x_tx_descriptor *tx_descriptor = NULL;
+ struct lan743x_tx_buffer_info *buffer_info = NULL;
+
+ /* wrap up previous descriptor */
+ tx->frame_data0 |= TX_DESC_DATA0_EXT_;
+ tx_descriptor = &tx->ring_cpu_ptr[tx->frame_tail];
+ tx_descriptor->data0 = tx->frame_data0;
+
+ /* move to next descriptor */
+ tx->frame_tail = lan743x_tx_next_index(tx, tx->frame_tail);
+ tx_descriptor = &tx->ring_cpu_ptr[tx->frame_tail];
+ buffer_info = &tx->buffer_info[tx->frame_tail];
+
+ /* add extension descriptor */
+ tx_descriptor->data1 = 0;
+ tx_descriptor->data2 = 0;
+ tx_descriptor->data3 = 0;
+
+ buffer_info->skb = NULL;
+ buffer_info->dma_ptr = 0;
+ buffer_info->buffer_length = 0;
+ buffer_info->flags |= TX_BUFFER_INFO_FLAG_ACTIVE;
+
+ tx->frame_data0 = (frame_length & TX_DESC_DATA0_EXT_PAY_LENGTH_MASK_) |
+ TX_DESC_DATA0_DTYPE_EXT_ |
+ TX_DESC_DATA0_EXT_LSO_;
+
+ /* data0 will be programmed in one of other frame assembler functions */
+}
+
+static int lan743x_tx_frame_add_fragment(struct lan743x_tx *tx,
+ const struct skb_frag_struct *fragment,
+ unsigned int frame_length)
+{
+ /* called only from within lan743x_tx_xmit_frame
+ * assuming tx->ring_lock has already been acquired
+ */
+ struct lan743x_tx_descriptor *tx_descriptor = NULL;
+ struct lan743x_tx_buffer_info *buffer_info = NULL;
+ struct lan743x_adapter *adapter = tx->adapter;
+ struct device *dev = &adapter->pdev->dev;
+ unsigned int fragment_length = 0;
+ dma_addr_t dma_ptr;
+
+ fragment_length = skb_frag_size(fragment);
+ if (!fragment_length)
+ return 0;
+
+ /* wrap up previous descriptor */
+ tx_descriptor = &tx->ring_cpu_ptr[tx->frame_tail];
+ tx_descriptor->data0 = tx->frame_data0;
+
+ /* move to next descriptor */
+ tx->frame_tail = lan743x_tx_next_index(tx, tx->frame_tail);
+ tx_descriptor = &tx->ring_cpu_ptr[tx->frame_tail];
+ buffer_info = &tx->buffer_info[tx->frame_tail];
+ dma_ptr = skb_frag_dma_map(dev, fragment,
+ 0, fragment_length,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, dma_ptr)) {
+ int desc_index;
+
+ /* cleanup all previously setup descriptors */
+ desc_index = tx->frame_first;
+ while (desc_index != tx->frame_tail) {
+ lan743x_tx_release_desc(tx, desc_index, true);
+ desc_index = lan743x_tx_next_index(tx, desc_index);
+ }
+ dma_wmb();
+ tx->frame_flags &= ~TX_FRAME_FLAG_IN_PROGRESS;
+ tx->frame_first = 0;
+ tx->frame_data0 = 0;
+ tx->frame_tail = 0;
+ return -ENOMEM;
+ }
+
+ tx_descriptor->data1 = DMA_ADDR_LOW32(dma_ptr);
+ tx_descriptor->data2 = DMA_ADDR_HIGH32(dma_ptr);
+ tx_descriptor->data3 = (frame_length << 16) &
+ TX_DESC_DATA3_FRAME_LENGTH_MSS_MASK_;
+
+ buffer_info->skb = NULL;
+ buffer_info->dma_ptr = dma_ptr;
+ buffer_info->buffer_length = fragment_length;
+ buffer_info->flags |= TX_BUFFER_INFO_FLAG_ACTIVE;
+ buffer_info->flags |= TX_BUFFER_INFO_FLAG_SKB_FRAGMENT;
+
+ tx->frame_data0 = (fragment_length & TX_DESC_DATA0_BUF_LENGTH_MASK_) |
+ TX_DESC_DATA0_DTYPE_DATA_ |
+ TX_DESC_DATA0_FCS_;
+
+ /* data0 will be programmed in one of other frame assembler functions */
+ return 0;
+}
+
+static void lan743x_tx_frame_end(struct lan743x_tx *tx,
+ struct sk_buff *skb,
+ bool ignore_sync)
+{
+ /* called only from within lan743x_tx_xmit_frame
+ * assuming tx->ring_lock has already been acquired
+ */
+ struct lan743x_tx_descriptor *tx_descriptor = NULL;
+ struct lan743x_tx_buffer_info *buffer_info = NULL;
+ struct lan743x_adapter *adapter = tx->adapter;
+ u32 tx_tail_flags = 0;
+
+ /* wrap up previous descriptor */
+ tx->frame_data0 |= TX_DESC_DATA0_LS_;
+ tx->frame_data0 |= TX_DESC_DATA0_IOC_;
+
+ tx_descriptor = &tx->ring_cpu_ptr[tx->frame_tail];
+ buffer_info = &tx->buffer_info[tx->frame_tail];
+ buffer_info->skb = skb;
+ if (ignore_sync)
+ buffer_info->flags |= TX_BUFFER_INFO_FLAG_IGNORE_SYNC;
+
+ tx_descriptor->data0 = tx->frame_data0;
+ tx->frame_tail = lan743x_tx_next_index(tx, tx->frame_tail);
+ tx->last_tail = tx->frame_tail;
+
+ dma_wmb();
+
+ if (tx->vector_flags & LAN743X_VECTOR_FLAG_VECTOR_ENABLE_AUTO_SET)
+ tx_tail_flags |= TX_TAIL_SET_TOP_INT_VEC_EN_;
+ if (tx->vector_flags & LAN743X_VECTOR_FLAG_SOURCE_ENABLE_AUTO_SET)
+ tx_tail_flags |= TX_TAIL_SET_DMAC_INT_EN_ |
+ TX_TAIL_SET_TOP_INT_EN_;
+
+ lan743x_csr_write(adapter, TX_TAIL(tx->channel_number),
+ tx_tail_flags | tx->frame_tail);
+ tx->frame_flags &= ~TX_FRAME_FLAG_IN_PROGRESS;
+}
+
+static netdev_tx_t lan743x_tx_xmit_frame(struct lan743x_tx *tx,
+ struct sk_buff *skb)
+{
+ int required_number_of_descriptors = 0;
+ unsigned int start_frame_length = 0;
+ unsigned int frame_length = 0;
+ unsigned int head_length = 0;
+ unsigned long irq_flags = 0;
+ bool ignore_sync = false;
+ int nr_frags = 0;
+ bool gso = false;
+ int j;
+
+ required_number_of_descriptors = lan743x_tx_get_desc_cnt(tx, skb);
+
+ spin_lock_irqsave(&tx->ring_lock, irq_flags);
+ if (required_number_of_descriptors >
+ lan743x_tx_get_avail_desc(tx)) {
+ if (required_number_of_descriptors > (tx->ring_size - 1)) {
+ dev_kfree_skb(skb);
+ } else {
+ /* save to overflow buffer */
+ tx->overflow_skb = skb;
+ netif_stop_queue(tx->adapter->netdev);
+ }
+ goto unlock;
+ }
+
+ /* space available, transmit skb */
+ head_length = skb_headlen(skb);
+ frame_length = skb_pagelen(skb);
+ nr_frags = skb_shinfo(skb)->nr_frags;
+ start_frame_length = frame_length;
+ gso = skb_is_gso(skb);
+ if (gso) {
+ start_frame_length = max(skb_shinfo(skb)->gso_size,
+ (unsigned short)8);
+ }
+
+ if (lan743x_tx_frame_start(tx,
+ skb->data, head_length,
+ start_frame_length,
+ skb->ip_summed == CHECKSUM_PARTIAL)) {
+ dev_kfree_skb(skb);
+ goto unlock;
+ }
+
+ if (gso)
+ lan743x_tx_frame_add_lso(tx, frame_length);
+
+ if (nr_frags <= 0)
+ goto finish;
+
+ for (j = 0; j < nr_frags; j++) {
+ const struct skb_frag_struct *frag;
+
+ frag = &(skb_shinfo(skb)->frags[j]);
+ if (lan743x_tx_frame_add_fragment(tx, frag, frame_length)) {
+ /* upon error no need to call
+ * lan743x_tx_frame_end
+ * frame assembler clean up was performed inside
+ * lan743x_tx_frame_add_fragment
+ */
+ dev_kfree_skb(skb);
+ goto unlock;
+ }
+ }
+
+finish:
+ lan743x_tx_frame_end(tx, skb, ignore_sync);
+
+unlock:
+ spin_unlock_irqrestore(&tx->ring_lock, irq_flags);
+ return NETDEV_TX_OK;
+}
+
+static int lan743x_tx_napi_poll(struct napi_struct *napi, int weight)
+{
+ struct lan743x_tx *tx = container_of(napi, struct lan743x_tx, napi);
+ struct lan743x_adapter *adapter = tx->adapter;
+ bool start_transmitter = false;
+ unsigned long irq_flags = 0;
+ u32 ioc_bit = 0;
+ u32 int_sts = 0;
+
+ ioc_bit = DMAC_INT_BIT_TX_IOC_(tx->channel_number);
+ int_sts = lan743x_csr_read(adapter, DMAC_INT_STS);
+ if (tx->vector_flags & LAN743X_VECTOR_FLAG_SOURCE_STATUS_W2C)
+ lan743x_csr_write(adapter, DMAC_INT_STS, ioc_bit);
+ spin_lock_irqsave(&tx->ring_lock, irq_flags);
+
+ /* clean up tx ring */
+ lan743x_tx_release_completed_descriptors(tx);
+ if (netif_queue_stopped(adapter->netdev)) {
+ if (tx->overflow_skb) {
+ if (lan743x_tx_get_desc_cnt(tx, tx->overflow_skb) <=
+ lan743x_tx_get_avail_desc(tx))
+ start_transmitter = true;
+ } else {
+ netif_wake_queue(adapter->netdev);
+ }
+ }
+ spin_unlock_irqrestore(&tx->ring_lock, irq_flags);
+
+ if (start_transmitter) {
+ /* space is now available, transmit overflow skb */
+ lan743x_tx_xmit_frame(tx, tx->overflow_skb);
+ tx->overflow_skb = NULL;
+ netif_wake_queue(adapter->netdev);
+ }
+
+ if (!napi_complete_done(napi, weight))
+ goto done;
+
+ /* enable isr */
+ lan743x_csr_write(adapter, INT_EN_SET,
+ INT_BIT_DMA_TX_(tx->channel_number));
+ lan743x_csr_read(adapter, INT_STS);
+
+done:
+ return weight;
+}
+
+static void lan743x_tx_ring_cleanup(struct lan743x_tx *tx)
+{
+ if (tx->head_cpu_ptr) {
+ pci_free_consistent(tx->adapter->pdev,
+ sizeof(*tx->head_cpu_ptr),
+ (void *)(tx->head_cpu_ptr),
+ tx->head_dma_ptr);
+ tx->head_cpu_ptr = NULL;
+ tx->head_dma_ptr = 0;
+ }
+ kfree(tx->buffer_info);
+ tx->buffer_info = NULL;
+
+ if (tx->ring_cpu_ptr) {
+ pci_free_consistent(tx->adapter->pdev,
+ tx->ring_allocation_size,
+ tx->ring_cpu_ptr,
+ tx->ring_dma_ptr);
+ tx->ring_allocation_size = 0;
+ tx->ring_cpu_ptr = NULL;
+ tx->ring_dma_ptr = 0;
+ }
+ tx->ring_size = 0;
+}
+
+static int lan743x_tx_ring_init(struct lan743x_tx *tx)
+{
+ size_t ring_allocation_size = 0;
+ void *cpu_ptr = NULL;
+ dma_addr_t dma_ptr;
+ int ret = -ENOMEM;
+
+ tx->ring_size = LAN743X_TX_RING_SIZE;
+ if (tx->ring_size & ~TX_CFG_B_TX_RING_LEN_MASK_) {
+ ret = -EINVAL;
+ goto cleanup;
+ }
+ ring_allocation_size = ALIGN(tx->ring_size *
+ sizeof(struct lan743x_tx_descriptor),
+ PAGE_SIZE);
+ dma_ptr = 0;
+ cpu_ptr = pci_zalloc_consistent(tx->adapter->pdev,
+ ring_allocation_size, &dma_ptr);
+ if (!cpu_ptr) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ tx->ring_allocation_size = ring_allocation_size;
+ tx->ring_cpu_ptr = (struct lan743x_tx_descriptor *)cpu_ptr;
+ tx->ring_dma_ptr = dma_ptr;
+
+ cpu_ptr = kcalloc(tx->ring_size, sizeof(*tx->buffer_info), GFP_KERNEL);
+ if (!cpu_ptr) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+ tx->buffer_info = (struct lan743x_tx_buffer_info *)cpu_ptr;
+ dma_ptr = 0;
+ cpu_ptr = pci_zalloc_consistent(tx->adapter->pdev,
+ sizeof(*tx->head_cpu_ptr), &dma_ptr);
+ if (!cpu_ptr) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ tx->head_cpu_ptr = cpu_ptr;
+ tx->head_dma_ptr = dma_ptr;
+ if (tx->head_dma_ptr & 0x3) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ return 0;
+
+cleanup:
+ lan743x_tx_ring_cleanup(tx);
+ return ret;
+}
+
+static void lan743x_tx_close(struct lan743x_tx *tx)
+{
+ struct lan743x_adapter *adapter = tx->adapter;
+
+ lan743x_csr_write(adapter,
+ DMAC_CMD,
+ DMAC_CMD_STOP_T_(tx->channel_number));
+ lan743x_dmac_tx_wait_till_stopped(adapter, tx->channel_number);
+
+ lan743x_csr_write(adapter,
+ DMAC_INT_EN_CLR,
+ DMAC_INT_BIT_TX_IOC_(tx->channel_number));
+ lan743x_csr_write(adapter, INT_EN_CLR,
+ INT_BIT_DMA_TX_(tx->channel_number));
+ napi_disable(&tx->napi);
+ netif_napi_del(&tx->napi);
+
+ lan743x_csr_write(adapter, FCT_TX_CTL,
+ FCT_TX_CTL_DIS_(tx->channel_number));
+ lan743x_csr_wait_for_bit(adapter, FCT_TX_CTL,
+ FCT_TX_CTL_EN_(tx->channel_number),
+ 0, 1000, 20000, 100);
+
+ lan743x_tx_release_all_descriptors(tx);
+
+ if (tx->overflow_skb) {
+ dev_kfree_skb(tx->overflow_skb);
+ tx->overflow_skb = NULL;
+ }
+
+ lan743x_tx_ring_cleanup(tx);
+}
+
+static int lan743x_tx_open(struct lan743x_tx *tx)
+{
+ struct lan743x_adapter *adapter = NULL;
+ u32 data = 0;
+ int ret;
+
+ adapter = tx->adapter;
+ ret = lan743x_tx_ring_init(tx);
+ if (ret)
+ return ret;
+
+ /* initialize fifo */
+ lan743x_csr_write(adapter, FCT_TX_CTL,
+ FCT_TX_CTL_RESET_(tx->channel_number));
+ lan743x_csr_wait_for_bit(adapter, FCT_TX_CTL,
+ FCT_TX_CTL_RESET_(tx->channel_number),
+ 0, 1000, 20000, 100);
+
+ /* enable fifo */
+ lan743x_csr_write(adapter, FCT_TX_CTL,
+ FCT_TX_CTL_EN_(tx->channel_number));
+
+ /* reset tx channel */
+ lan743x_csr_write(adapter, DMAC_CMD,
+ DMAC_CMD_TX_SWR_(tx->channel_number));
+ lan743x_csr_wait_for_bit(adapter, DMAC_CMD,
+ DMAC_CMD_TX_SWR_(tx->channel_number),
+ 0, 1000, 20000, 100);
+
+ /* Write TX_BASE_ADDR */
+ lan743x_csr_write(adapter,
+ TX_BASE_ADDRH(tx->channel_number),
+ DMA_ADDR_HIGH32(tx->ring_dma_ptr));
+ lan743x_csr_write(adapter,
+ TX_BASE_ADDRL(tx->channel_number),
+ DMA_ADDR_LOW32(tx->ring_dma_ptr));
+
+ /* Write TX_CFG_B */
+ data = lan743x_csr_read(adapter, TX_CFG_B(tx->channel_number));
+ data &= ~TX_CFG_B_TX_RING_LEN_MASK_;
+ data |= ((tx->ring_size) & TX_CFG_B_TX_RING_LEN_MASK_);
+ if (!(adapter->csr.flags & LAN743X_CSR_FLAG_IS_A0))
+ data |= TX_CFG_B_TDMABL_512_;
+ lan743x_csr_write(adapter, TX_CFG_B(tx->channel_number), data);
+
+ /* Write TX_CFG_A */
+ data = TX_CFG_A_TX_TMR_HPWB_SEL_IOC_ | TX_CFG_A_TX_HP_WB_EN_;
+ if (!(adapter->csr.flags & LAN743X_CSR_FLAG_IS_A0)) {
+ data |= TX_CFG_A_TX_HP_WB_ON_INT_TMR_;
+ data |= TX_CFG_A_TX_PF_THRES_SET_(0x10);
+ data |= TX_CFG_A_TX_PF_PRI_THRES_SET_(0x04);
+ data |= TX_CFG_A_TX_HP_WB_THRES_SET_(0x07);
+ }
+ lan743x_csr_write(adapter, TX_CFG_A(tx->channel_number), data);
+
+ /* Write TX_HEAD_WRITEBACK_ADDR */
+ lan743x_csr_write(adapter,
+ TX_HEAD_WRITEBACK_ADDRH(tx->channel_number),
+ DMA_ADDR_HIGH32(tx->head_dma_ptr));
+ lan743x_csr_write(adapter,
+ TX_HEAD_WRITEBACK_ADDRL(tx->channel_number),
+ DMA_ADDR_LOW32(tx->head_dma_ptr));
+
+ /* set last head */
+ tx->last_head = lan743x_csr_read(adapter, TX_HEAD(tx->channel_number));
+
+ /* write TX_TAIL */
+ tx->last_tail = 0;
+ lan743x_csr_write(adapter, TX_TAIL(tx->channel_number),
+ (u32)(tx->last_tail));
+ tx->vector_flags = lan743x_intr_get_vector_flags(adapter,
+ INT_BIT_DMA_TX_
+ (tx->channel_number));
+ netif_napi_add(adapter->netdev,
+ &tx->napi, lan743x_tx_napi_poll,
+ tx->ring_size - 1);
+ napi_enable(&tx->napi);
+
+ data = 0;
+ if (tx->vector_flags & LAN743X_VECTOR_FLAG_SOURCE_ENABLE_AUTO_CLEAR)
+ data |= TX_CFG_C_TX_TOP_INT_EN_AUTO_CLR_;
+ if (tx->vector_flags & LAN743X_VECTOR_FLAG_SOURCE_STATUS_AUTO_CLEAR)
+ data |= TX_CFG_C_TX_DMA_INT_STS_AUTO_CLR_;
+ if (tx->vector_flags & LAN743X_VECTOR_FLAG_SOURCE_STATUS_R2C)
+ data |= TX_CFG_C_TX_INT_STS_R2C_MODE_MASK_;
+ if (tx->vector_flags & LAN743X_VECTOR_FLAG_SOURCE_ENABLE_R2C)
+ data |= TX_CFG_C_TX_INT_EN_R2C_;
+ lan743x_csr_write(adapter, TX_CFG_C(tx->channel_number), data);
+
+ if (!(tx->vector_flags & LAN743X_VECTOR_FLAG_SOURCE_ENABLE_AUTO_SET))
+ lan743x_csr_write(adapter, INT_EN_SET,
+ INT_BIT_DMA_TX_(tx->channel_number));
+ lan743x_csr_write(adapter, DMAC_INT_EN_SET,
+ DMAC_INT_BIT_TX_IOC_(tx->channel_number));
+
+ /* start dmac channel */
+ lan743x_csr_write(adapter, DMAC_CMD,
+ DMAC_CMD_START_T_(tx->channel_number));
+ return 0;
+}
+
+static int lan743x_rx_next_index(struct lan743x_rx *rx, int index)
+{
+ return ((++index) % rx->ring_size);
+}
+
+static int lan743x_rx_allocate_ring_element(struct lan743x_rx *rx, int index)
+{
+ struct lan743x_rx_buffer_info *buffer_info;
+ struct lan743x_rx_descriptor *descriptor;
+ int length = 0;
+
+ length = (LAN743X_MAX_FRAME_SIZE + ETH_HLEN + 4 + RX_HEAD_PADDING);
+ descriptor = &rx->ring_cpu_ptr[index];
+ buffer_info = &rx->buffer_info[index];
+ buffer_info->skb = __netdev_alloc_skb(rx->adapter->netdev,
+ length,
+ GFP_ATOMIC | GFP_DMA);
+ if (!(buffer_info->skb))
+ return -ENOMEM;
+ buffer_info->dma_ptr = dma_map_single(&rx->adapter->pdev->dev,
+ buffer_info->skb->data,
+ length,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(&rx->adapter->pdev->dev,
+ buffer_info->dma_ptr)) {
+ buffer_info->dma_ptr = 0;
+ return -ENOMEM;
+ }
+
+ buffer_info->buffer_length = length;
+ descriptor->data1 = DMA_ADDR_LOW32(buffer_info->dma_ptr);
+ descriptor->data2 = DMA_ADDR_HIGH32(buffer_info->dma_ptr);
+ descriptor->data3 = 0;
+ descriptor->data0 = (RX_DESC_DATA0_OWN_ |
+ (length & RX_DESC_DATA0_BUF_LENGTH_MASK_));
+ skb_reserve(buffer_info->skb, RX_HEAD_PADDING);
+
+ return 0;
+}
+
+static void lan743x_rx_reuse_ring_element(struct lan743x_rx *rx, int index)
+{
+ struct lan743x_rx_buffer_info *buffer_info;
+ struct lan743x_rx_descriptor *descriptor;
+
+ descriptor = &rx->ring_cpu_ptr[index];
+ buffer_info = &rx->buffer_info[index];
+
+ descriptor->data1 = DMA_ADDR_LOW32(buffer_info->dma_ptr);
+ descriptor->data2 = DMA_ADDR_HIGH32(buffer_info->dma_ptr);
+ descriptor->data3 = 0;
+ descriptor->data0 = (RX_DESC_DATA0_OWN_ |
+ ((buffer_info->buffer_length) &
+ RX_DESC_DATA0_BUF_LENGTH_MASK_));
+}
+
+static void lan743x_rx_release_ring_element(struct lan743x_rx *rx, int index)
+{
+ struct lan743x_rx_buffer_info *buffer_info;
+ struct lan743x_rx_descriptor *descriptor;
+
+ descriptor = &rx->ring_cpu_ptr[index];
+ buffer_info = &rx->buffer_info[index];
+
+ memset(descriptor, 0, sizeof(*descriptor));
+
+ if (buffer_info->dma_ptr) {
+ dma_unmap_single(&rx->adapter->pdev->dev,
+ buffer_info->dma_ptr,
+ buffer_info->buffer_length,
+ DMA_FROM_DEVICE);
+ buffer_info->dma_ptr = 0;
+ }
+
+ if (buffer_info->skb) {
+ dev_kfree_skb(buffer_info->skb);
+ buffer_info->skb = NULL;
+ }
+
+ memset(buffer_info, 0, sizeof(*buffer_info));
+}
+
+static int lan743x_rx_process_packet(struct lan743x_rx *rx)
+{
+ struct skb_shared_hwtstamps *hwtstamps = NULL;
+ int result = RX_PROCESS_RESULT_NOTHING_TO_DO;
+ struct lan743x_rx_buffer_info *buffer_info;
+ struct lan743x_rx_descriptor *descriptor;
+ int current_head_index = -1;
+ int extension_index = -1;
+ int first_index = -1;
+ int last_index = -1;
+
+ current_head_index = *rx->head_cpu_ptr;
+ if (current_head_index < 0 || current_head_index >= rx->ring_size)
+ goto done;
+
+ if (rx->last_head < 0 || rx->last_head >= rx->ring_size)
+ goto done;
+
+ if (rx->last_head != current_head_index) {
+ descriptor = &rx->ring_cpu_ptr[rx->last_head];
+ if (descriptor->data0 & RX_DESC_DATA0_OWN_)
+ goto done;
+
+ if (!(descriptor->data0 & RX_DESC_DATA0_FS_))
+ goto done;
+
+ first_index = rx->last_head;
+ if (descriptor->data0 & RX_DESC_DATA0_LS_) {
+ last_index = rx->last_head;
+ } else {
+ int index;
+
+ index = lan743x_rx_next_index(rx, first_index);
+ while (index != current_head_index) {
+ descriptor = &rx->ring_cpu_ptr[index];
+ if (descriptor->data0 & RX_DESC_DATA0_OWN_)
+ goto done;
+
+ if (descriptor->data0 & RX_DESC_DATA0_LS_) {
+ last_index = index;
+ break;
+ }
+ index = lan743x_rx_next_index(rx, index);
+ }
+ }
+ if (last_index >= 0) {
+ descriptor = &rx->ring_cpu_ptr[last_index];
+ if (descriptor->data0 & RX_DESC_DATA0_EXT_) {
+ /* extension is expected to follow */
+ int index = lan743x_rx_next_index(rx,
+ last_index);
+ if (index != current_head_index) {
+ descriptor = &rx->ring_cpu_ptr[index];
+ if (descriptor->data0 &
+ RX_DESC_DATA0_OWN_) {
+ goto done;
+ }
+ if (descriptor->data0 &
+ RX_DESC_DATA0_EXT_) {
+ extension_index = index;
+ } else {
+ goto done;
+ }
+ } else {
+ /* extension is not yet available */
+ /* prevent processing of this packet */
+ first_index = -1;
+ last_index = -1;
+ }
+ }
+ }
+ }
+ if (first_index >= 0 && last_index >= 0) {
+ int real_last_index = last_index;
+ struct sk_buff *skb = NULL;
+ u32 ts_sec = 0;
+ u32 ts_nsec = 0;
+
+ /* packet is available */
+ if (first_index == last_index) {
+ /* single buffer packet */
+ int packet_length;
+
+ buffer_info = &rx->buffer_info[first_index];
+ skb = buffer_info->skb;
+ descriptor = &rx->ring_cpu_ptr[first_index];
+
+ /* unmap from dma */
+ if (buffer_info->dma_ptr) {
+ dma_unmap_single(&rx->adapter->pdev->dev,
+ buffer_info->dma_ptr,
+ buffer_info->buffer_length,
+ DMA_FROM_DEVICE);
+ buffer_info->dma_ptr = 0;
+ buffer_info->buffer_length = 0;
+ }
+ buffer_info->skb = NULL;
+ packet_length = RX_DESC_DATA0_FRAME_LENGTH_GET_
+ (descriptor->data0);
+ skb_put(skb, packet_length - 4);
+ skb->protocol = eth_type_trans(skb,
+ rx->adapter->netdev);
+ lan743x_rx_allocate_ring_element(rx, first_index);
+ } else {
+ int index = first_index;
+
+ /* multi buffer packet not supported */
+ /* this should not happen since
+ * buffers are allocated to be at least jumbo size
+ */
+
+ /* clean up buffers */
+ if (first_index <= last_index) {
+ while ((index >= first_index) &&
+ (index <= last_index)) {
+ lan743x_rx_release_ring_element(rx,
+ index);
+ lan743x_rx_allocate_ring_element(rx,
+ index);
+ index = lan743x_rx_next_index(rx,
+ index);
+ }
+ } else {
+ while ((index >= first_index) ||
+ (index <= last_index)) {
+ lan743x_rx_release_ring_element(rx,
+ index);
+ lan743x_rx_allocate_ring_element(rx,
+ index);
+ index = lan743x_rx_next_index(rx,
+ index);
+ }
+ }
+ }
+
+ if (extension_index >= 0) {
+ descriptor = &rx->ring_cpu_ptr[extension_index];
+ buffer_info = &rx->buffer_info[extension_index];
+
+ ts_sec = descriptor->data1;
+ ts_nsec = (descriptor->data2 &
+ RX_DESC_DATA2_TS_NS_MASK_);
+ lan743x_rx_reuse_ring_element(rx, extension_index);
+ real_last_index = extension_index;
+ }
+
+ if (!skb) {
+ result = RX_PROCESS_RESULT_PACKET_DROPPED;
+ goto move_forward;
+ }
+
+ if (extension_index < 0)
+ goto pass_packet_to_os;
+ hwtstamps = skb_hwtstamps(skb);
+ if (hwtstamps)
+ hwtstamps->hwtstamp = ktime_set(ts_sec, ts_nsec);
+
+pass_packet_to_os:
+ /* pass packet to OS */
+ napi_gro_receive(&rx->napi, skb);
+ result = RX_PROCESS_RESULT_PACKET_RECEIVED;
+
+move_forward:
+ /* push tail and head forward */
+ rx->last_tail = real_last_index;
+ rx->last_head = lan743x_rx_next_index(rx, real_last_index);
+ }
+done:
+ return result;
+}
+
+static int lan743x_rx_napi_poll(struct napi_struct *napi, int weight)
+{
+ struct lan743x_rx *rx = container_of(napi, struct lan743x_rx, napi);
+ struct lan743x_adapter *adapter = rx->adapter;
+ u32 rx_tail_flags = 0;
+ int count;
+
+ if (rx->vector_flags & LAN743X_VECTOR_FLAG_SOURCE_STATUS_W2C) {
+ /* clear int status bit before reading packet */
+ lan743x_csr_write(adapter, DMAC_INT_STS,
+ DMAC_INT_BIT_RXFRM_(rx->channel_number));
+ }
+ count = 0;
+ while (count < weight) {
+ int rx_process_result = -1;
+
+ rx_process_result = lan743x_rx_process_packet(rx);
+ if (rx_process_result == RX_PROCESS_RESULT_PACKET_RECEIVED) {
+ count++;
+ } else if (rx_process_result ==
+ RX_PROCESS_RESULT_NOTHING_TO_DO) {
+ break;
+ } else if (rx_process_result ==
+ RX_PROCESS_RESULT_PACKET_DROPPED) {
+ continue;
+ }
+ }
+ rx->frame_count += count;
+ if (count == weight)
+ goto done;
+
+ if (!napi_complete_done(napi, count))
+ goto done;
+
+ if (rx->vector_flags & LAN743X_VECTOR_FLAG_VECTOR_ENABLE_AUTO_SET)
+ rx_tail_flags |= RX_TAIL_SET_TOP_INT_VEC_EN_;
+ if (rx->vector_flags & LAN743X_VECTOR_FLAG_SOURCE_ENABLE_AUTO_SET) {
+ rx_tail_flags |= RX_TAIL_SET_TOP_INT_EN_;
+ } else {
+ lan743x_csr_write(adapter, INT_EN_SET,
+ INT_BIT_DMA_RX_(rx->channel_number));
+ }
+
+ /* update RX_TAIL */
+ lan743x_csr_write(adapter, RX_TAIL(rx->channel_number),
+ rx_tail_flags | rx->last_tail);
+done:
+ return count;
+}
+
+static void lan743x_rx_ring_cleanup(struct lan743x_rx *rx)
+{
+ if (rx->buffer_info && rx->ring_cpu_ptr) {
+ int index;
+
+ for (index = 0; index < rx->ring_size; index++)
+ lan743x_rx_release_ring_element(rx, index);
+ }
+
+ if (rx->head_cpu_ptr) {
+ pci_free_consistent(rx->adapter->pdev,
+ sizeof(*rx->head_cpu_ptr),
+ rx->head_cpu_ptr,
+ rx->head_dma_ptr);
+ rx->head_cpu_ptr = NULL;
+ rx->head_dma_ptr = 0;
+ }
+
+ kfree(rx->buffer_info);
+ rx->buffer_info = NULL;
+
+ if (rx->ring_cpu_ptr) {
+ pci_free_consistent(rx->adapter->pdev,
+ rx->ring_allocation_size,
+ rx->ring_cpu_ptr,
+ rx->ring_dma_ptr);
+ rx->ring_allocation_size = 0;
+ rx->ring_cpu_ptr = NULL;
+ rx->ring_dma_ptr = 0;
+ }
+
+ rx->ring_size = 0;
+ rx->last_head = 0;
+}
+
+static int lan743x_rx_ring_init(struct lan743x_rx *rx)
+{
+ size_t ring_allocation_size = 0;
+ dma_addr_t dma_ptr = 0;
+ void *cpu_ptr = NULL;
+ int ret = -ENOMEM;
+ int index = 0;
+
+ rx->ring_size = LAN743X_RX_RING_SIZE;
+ if (rx->ring_size <= 1) {
+ ret = -EINVAL;
+ goto cleanup;
+ }
+ if (rx->ring_size & ~RX_CFG_B_RX_RING_LEN_MASK_) {
+ ret = -EINVAL;
+ goto cleanup;
+ }
+ ring_allocation_size = ALIGN(rx->ring_size *
+ sizeof(struct lan743x_rx_descriptor),
+ PAGE_SIZE);
+ dma_ptr = 0;
+ cpu_ptr = pci_zalloc_consistent(rx->adapter->pdev,
+ ring_allocation_size, &dma_ptr);
+ if (!cpu_ptr) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+ rx->ring_allocation_size = ring_allocation_size;
+ rx->ring_cpu_ptr = (struct lan743x_rx_descriptor *)cpu_ptr;
+ rx->ring_dma_ptr = dma_ptr;
+
+ cpu_ptr = kcalloc(rx->ring_size, sizeof(*rx->buffer_info),
+ GFP_KERNEL);
+ if (!cpu_ptr) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+ rx->buffer_info = (struct lan743x_rx_buffer_info *)cpu_ptr;
+ dma_ptr = 0;
+ cpu_ptr = pci_zalloc_consistent(rx->adapter->pdev,
+ sizeof(*rx->head_cpu_ptr), &dma_ptr);
+ if (!cpu_ptr) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ rx->head_cpu_ptr = cpu_ptr;
+ rx->head_dma_ptr = dma_ptr;
+ if (rx->head_dma_ptr & 0x3) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ rx->last_head = 0;
+ for (index = 0; index < rx->ring_size; index++) {
+ ret = lan743x_rx_allocate_ring_element(rx, index);
+ if (ret)
+ goto cleanup;
+ }
+ return 0;
+
+cleanup:
+ lan743x_rx_ring_cleanup(rx);
+ return ret;
+}
+
+static void lan743x_rx_close(struct lan743x_rx *rx)
+{
+ struct lan743x_adapter *adapter = rx->adapter;
+
+ lan743x_csr_write(adapter, FCT_RX_CTL,
+ FCT_RX_CTL_DIS_(rx->channel_number));
+ lan743x_csr_wait_for_bit(adapter, FCT_RX_CTL,
+ FCT_RX_CTL_EN_(rx->channel_number),
+ 0, 1000, 20000, 100);
+
+ lan743x_csr_write(adapter, DMAC_CMD,
+ DMAC_CMD_STOP_R_(rx->channel_number));
+ lan743x_dmac_rx_wait_till_stopped(adapter, rx->channel_number);
+
+ lan743x_csr_write(adapter, DMAC_INT_EN_CLR,
+ DMAC_INT_BIT_RXFRM_(rx->channel_number));
+ lan743x_csr_write(adapter, INT_EN_CLR,
+ INT_BIT_DMA_RX_(rx->channel_number));
+ napi_disable(&rx->napi);
+
+ netif_napi_del(&rx->napi);
+
+ lan743x_rx_ring_cleanup(rx);
+}
+
+static int lan743x_rx_open(struct lan743x_rx *rx)
+{
+ struct lan743x_adapter *adapter = rx->adapter;
+ u32 data = 0;
+ int ret;
+
+ rx->frame_count = 0;
+ ret = lan743x_rx_ring_init(rx);
+ if (ret)
+ goto return_error;
+
+ netif_napi_add(adapter->netdev,
+ &rx->napi, lan743x_rx_napi_poll,
+ rx->ring_size - 1);
+
+ lan743x_csr_write(adapter, DMAC_CMD,
+ DMAC_CMD_RX_SWR_(rx->channel_number));
+ lan743x_csr_wait_for_bit(adapter, DMAC_CMD,
+ DMAC_CMD_RX_SWR_(rx->channel_number),
+ 0, 1000, 20000, 100);
+
+ /* set ring base address */
+ lan743x_csr_write(adapter,
+ RX_BASE_ADDRH(rx->channel_number),
+ DMA_ADDR_HIGH32(rx->ring_dma_ptr));
+ lan743x_csr_write(adapter,
+ RX_BASE_ADDRL(rx->channel_number),
+ DMA_ADDR_LOW32(rx->ring_dma_ptr));
+
+ /* set rx write back address */
+ lan743x_csr_write(adapter,
+ RX_HEAD_WRITEBACK_ADDRH(rx->channel_number),
+ DMA_ADDR_HIGH32(rx->head_dma_ptr));
+ lan743x_csr_write(adapter,
+ RX_HEAD_WRITEBACK_ADDRL(rx->channel_number),
+ DMA_ADDR_LOW32(rx->head_dma_ptr));
+ data = RX_CFG_A_RX_HP_WB_EN_;
+ if (!(adapter->csr.flags & LAN743X_CSR_FLAG_IS_A0)) {
+ data |= (RX_CFG_A_RX_WB_ON_INT_TMR_ |
+ RX_CFG_A_RX_WB_THRES_SET_(0x7) |
+ RX_CFG_A_RX_PF_THRES_SET_(16) |
+ RX_CFG_A_RX_PF_PRI_THRES_SET_(4));
+ }
+
+ /* set RX_CFG_A */
+ lan743x_csr_write(adapter,
+ RX_CFG_A(rx->channel_number), data);
+
+ /* set RX_CFG_B */
+ data = lan743x_csr_read(adapter, RX_CFG_B(rx->channel_number));
+ data &= ~RX_CFG_B_RX_PAD_MASK_;
+ if (!RX_HEAD_PADDING)
+ data |= RX_CFG_B_RX_PAD_0_;
+ else
+ data |= RX_CFG_B_RX_PAD_2_;
+ data &= ~RX_CFG_B_RX_RING_LEN_MASK_;
+ data |= ((rx->ring_size) & RX_CFG_B_RX_RING_LEN_MASK_);
+ data |= RX_CFG_B_TS_ALL_RX_;
+ if (!(adapter->csr.flags & LAN743X_CSR_FLAG_IS_A0))
+ data |= RX_CFG_B_RDMABL_512_;
+
+ lan743x_csr_write(adapter, RX_CFG_B(rx->channel_number), data);
+ rx->vector_flags = lan743x_intr_get_vector_flags(adapter,
+ INT_BIT_DMA_RX_
+ (rx->channel_number));
+
+ /* set RX_CFG_C */
+ data = 0;
+ if (rx->vector_flags & LAN743X_VECTOR_FLAG_SOURCE_ENABLE_AUTO_CLEAR)
+ data |= RX_CFG_C_RX_TOP_INT_EN_AUTO_CLR_;
+ if (rx->vector_flags & LAN743X_VECTOR_FLAG_SOURCE_STATUS_AUTO_CLEAR)
+ data |= RX_CFG_C_RX_DMA_INT_STS_AUTO_CLR_;
+ if (rx->vector_flags & LAN743X_VECTOR_FLAG_SOURCE_STATUS_R2C)
+ data |= RX_CFG_C_RX_INT_STS_R2C_MODE_MASK_;
+ if (rx->vector_flags & LAN743X_VECTOR_FLAG_SOURCE_ENABLE_R2C)
+ data |= RX_CFG_C_RX_INT_EN_R2C_;
+ lan743x_csr_write(adapter, RX_CFG_C(rx->channel_number), data);
+
+ rx->last_tail = ((u32)(rx->ring_size - 1));
+ lan743x_csr_write(adapter, RX_TAIL(rx->channel_number),
+ rx->last_tail);
+ rx->last_head = lan743x_csr_read(adapter, RX_HEAD(rx->channel_number));
+ if (rx->last_head) {
+ ret = -EIO;
+ goto napi_delete;
+ }
+
+ napi_enable(&rx->napi);
+
+ lan743x_csr_write(adapter, INT_EN_SET,
+ INT_BIT_DMA_RX_(rx->channel_number));
+ lan743x_csr_write(adapter, DMAC_INT_STS,
+ DMAC_INT_BIT_RXFRM_(rx->channel_number));
+ lan743x_csr_write(adapter, DMAC_INT_EN_SET,
+ DMAC_INT_BIT_RXFRM_(rx->channel_number));
+ lan743x_csr_write(adapter, DMAC_CMD,
+ DMAC_CMD_START_R_(rx->channel_number));
+
+ /* initialize fifo */
+ lan743x_csr_write(adapter, FCT_RX_CTL,
+ FCT_RX_CTL_RESET_(rx->channel_number));
+ lan743x_csr_wait_for_bit(adapter, FCT_RX_CTL,
+ FCT_RX_CTL_RESET_(rx->channel_number),
+ 0, 1000, 20000, 100);
+ lan743x_csr_write(adapter, FCT_FLOW(rx->channel_number),
+ FCT_FLOW_CTL_REQ_EN_ |
+ FCT_FLOW_CTL_ON_THRESHOLD_SET_(0x2A) |
+ FCT_FLOW_CTL_OFF_THRESHOLD_SET_(0xA));
+
+ /* enable fifo */
+ lan743x_csr_write(adapter, FCT_RX_CTL,
+ FCT_RX_CTL_EN_(rx->channel_number));
+ return 0;
+
+napi_delete:
+ netif_napi_del(&rx->napi);
+ lan743x_rx_ring_cleanup(rx);
+
+return_error:
+ return ret;
+}
+
+static int lan743x_netdev_close(struct net_device *netdev)
+{
+ struct lan743x_adapter *adapter = netdev_priv(netdev);
+ int index;
+
+ lan743x_tx_close(&adapter->tx[0]);
+
+ for (index = 0; index < LAN743X_USED_RX_CHANNELS; index++)
+ lan743x_rx_close(&adapter->rx[index]);
+
+ lan743x_phy_close(adapter);
+
+ lan743x_mac_close(adapter);
+
+ lan743x_intr_close(adapter);
+
+ return 0;
+}
+
+static int lan743x_netdev_open(struct net_device *netdev)
+{
+ struct lan743x_adapter *adapter = netdev_priv(netdev);
+ int index;
+ int ret;
+
+ ret = lan743x_intr_open(adapter);
+ if (ret)
+ goto return_error;
+
+ ret = lan743x_mac_open(adapter);
+ if (ret)
+ goto close_intr;
+
+ ret = lan743x_phy_open(adapter);
+ if (ret)
+ goto close_mac;
+
+ for (index = 0; index < LAN743X_USED_RX_CHANNELS; index++) {
+ ret = lan743x_rx_open(&adapter->rx[index]);
+ if (ret)
+ goto close_rx;
+ }
+
+ ret = lan743x_tx_open(&adapter->tx[0]);
+ if (ret)
+ goto close_rx;
+
+ return 0;
+
+close_rx:
+ for (index = 0; index < LAN743X_USED_RX_CHANNELS; index++) {
+ if (adapter->rx[index].ring_cpu_ptr)
+ lan743x_rx_close(&adapter->rx[index]);
+ }
+ lan743x_phy_close(adapter);
+
+close_mac:
+ lan743x_mac_close(adapter);
+
+close_intr:
+ lan743x_intr_close(adapter);
+
+return_error:
+ netif_warn(adapter, ifup, adapter->netdev,
+ "Error opening LAN743x\n");
+ return ret;
+}
+
+static netdev_tx_t lan743x_netdev_xmit_frame(struct sk_buff *skb,
+ struct net_device *netdev)
+{
+ struct lan743x_adapter *adapter = netdev_priv(netdev);
+
+ return lan743x_tx_xmit_frame(&adapter->tx[0], skb);
+}
+
+static int lan743x_netdev_ioctl(struct net_device *netdev,
+ struct ifreq *ifr, int cmd)
+{
+ if (!netif_running(netdev))
+ return -EINVAL;
+ return phy_mii_ioctl(netdev->phydev, ifr, cmd);
+}
+
+static void lan743x_netdev_set_multicast(struct net_device *netdev)
+{
+ struct lan743x_adapter *adapter = netdev_priv(netdev);
+
+ lan743x_rfe_set_multicast(adapter);
+}
+
+static int lan743x_netdev_change_mtu(struct net_device *netdev, int new_mtu)
+{
+ struct lan743x_adapter *adapter = netdev_priv(netdev);
+ int ret = 0;
+
+ ret = lan743x_mac_set_mtu(adapter, new_mtu);
+ if (!ret)
+ netdev->mtu = new_mtu;
+ return ret;
+}
+
+static void lan743x_netdev_get_stats64(struct net_device *netdev,
+ struct rtnl_link_stats64 *stats)
+{
+ struct lan743x_adapter *adapter = netdev_priv(netdev);
+
+ stats->rx_packets = lan743x_csr_read(adapter, STAT_RX_TOTAL_FRAMES);
+ stats->tx_packets = lan743x_csr_read(adapter, STAT_TX_TOTAL_FRAMES);
+ stats->rx_bytes = lan743x_csr_read(adapter,
+ STAT_RX_UNICAST_BYTE_COUNT) +
+ lan743x_csr_read(adapter,
+ STAT_RX_BROADCAST_BYTE_COUNT) +
+ lan743x_csr_read(adapter,
+ STAT_RX_MULTICAST_BYTE_COUNT);
+ stats->tx_bytes = lan743x_csr_read(adapter,
+ STAT_TX_UNICAST_BYTE_COUNT) +
+ lan743x_csr_read(adapter,
+ STAT_TX_BROADCAST_BYTE_COUNT) +
+ lan743x_csr_read(adapter,
+ STAT_TX_MULTICAST_BYTE_COUNT);
+ stats->rx_errors = lan743x_csr_read(adapter, STAT_RX_FCS_ERRORS) +
+ lan743x_csr_read(adapter,
+ STAT_RX_ALIGNMENT_ERRORS) +
+ lan743x_csr_read(adapter, STAT_RX_JABBER_ERRORS) +
+ lan743x_csr_read(adapter,
+ STAT_RX_UNDERSIZE_FRAME_ERRORS) +
+ lan743x_csr_read(adapter,
+ STAT_RX_OVERSIZE_FRAME_ERRORS);
+ stats->tx_errors = lan743x_csr_read(adapter, STAT_TX_FCS_ERRORS) +
+ lan743x_csr_read(adapter,
+ STAT_TX_EXCESS_DEFERRAL_ERRORS) +
+ lan743x_csr_read(adapter, STAT_TX_CARRIER_ERRORS);
+ stats->rx_dropped = lan743x_csr_read(adapter,
+ STAT_RX_DROPPED_FRAMES);
+ stats->tx_dropped = lan743x_csr_read(adapter,
+ STAT_TX_EXCESSIVE_COLLISION);
+ stats->multicast = lan743x_csr_read(adapter,
+ STAT_RX_MULTICAST_FRAMES) +
+ lan743x_csr_read(adapter,
+ STAT_TX_MULTICAST_FRAMES);
+ stats->collisions = lan743x_csr_read(adapter,
+ STAT_TX_SINGLE_COLLISIONS) +
+ lan743x_csr_read(adapter,
+ STAT_TX_MULTIPLE_COLLISIONS) +
+ lan743x_csr_read(adapter,
+ STAT_TX_LATE_COLLISIONS);
+}
+
+static int lan743x_netdev_set_mac_address(struct net_device *netdev,
+ void *addr)
+{
+ struct lan743x_adapter *adapter = netdev_priv(netdev);
+ struct sockaddr *sock_addr = addr;
+ int ret;
+
+ ret = eth_prepare_mac_addr_change(netdev, sock_addr);
+ if (ret)
+ return ret;
+ ether_addr_copy(netdev->dev_addr, sock_addr->sa_data);
+ lan743x_mac_set_address(adapter, sock_addr->sa_data);
+ lan743x_rfe_update_mac_address(adapter);
+ return 0;
+}
+
+static const struct net_device_ops lan743x_netdev_ops = {
+ .ndo_open = lan743x_netdev_open,
+ .ndo_stop = lan743x_netdev_close,
+ .ndo_start_xmit = lan743x_netdev_xmit_frame,
+ .ndo_do_ioctl = lan743x_netdev_ioctl,
+ .ndo_set_rx_mode = lan743x_netdev_set_multicast,
+ .ndo_change_mtu = lan743x_netdev_change_mtu,
+ .ndo_get_stats64 = lan743x_netdev_get_stats64,
+ .ndo_set_mac_address = lan743x_netdev_set_mac_address,
+};
+
+static void lan743x_hardware_cleanup(struct lan743x_adapter *adapter)
+{
+ lan743x_csr_write(adapter, INT_EN_CLR, 0xFFFFFFFF);
+}
+
+static void lan743x_mdiobus_cleanup(struct lan743x_adapter *adapter)
+{
+ mdiobus_unregister(adapter->mdiobus);
+}
+
+static void lan743x_full_cleanup(struct lan743x_adapter *adapter)
+{
+ unregister_netdev(adapter->netdev);
+
+ lan743x_mdiobus_cleanup(adapter);
+ lan743x_hardware_cleanup(adapter);
+ lan743x_pci_cleanup(adapter);
+}
+
+static int lan743x_hardware_init(struct lan743x_adapter *adapter,
+ struct pci_dev *pdev)
+{
+ struct lan743x_tx *tx;
+ int index;
+ int ret;
+
+ adapter->intr.irq = adapter->pdev->irq;
+ lan743x_csr_write(adapter, INT_EN_CLR, 0xFFFFFFFF);
+ mutex_init(&adapter->dp_lock);
+ ret = lan743x_mac_init(adapter);
+ if (ret)
+ return ret;
+
+ ret = lan743x_phy_init(adapter);
+ if (ret)
+ return ret;
+
+ lan743x_rfe_update_mac_address(adapter);
+
+ ret = lan743x_dmac_init(adapter);
+ if (ret)
+ return ret;
+
+ for (index = 0; index < LAN743X_USED_RX_CHANNELS; index++) {
+ adapter->rx[index].adapter = adapter;
+ adapter->rx[index].channel_number = index;
+ }
+
+ tx = &adapter->tx[0];
+ tx->adapter = adapter;
+ tx->channel_number = 0;
+ spin_lock_init(&tx->ring_lock);
+ return 0;
+}
+
+static int lan743x_mdiobus_init(struct lan743x_adapter *adapter)
+{
+ int ret;
+
+ adapter->mdiobus = devm_mdiobus_alloc(&adapter->pdev->dev);
+ if (!(adapter->mdiobus)) {
+ ret = -ENOMEM;
+ goto return_error;
+ }
+
+ adapter->mdiobus->priv = (void *)adapter;
+ adapter->mdiobus->read = lan743x_mdiobus_read;
+ adapter->mdiobus->write = lan743x_mdiobus_write;
+ adapter->mdiobus->name = "lan743x-mdiobus";
+ snprintf(adapter->mdiobus->id, MII_BUS_ID_SIZE,
+ "pci-%s", pci_name(adapter->pdev));
+
+ /* set to internal PHY id */
+ adapter->mdiobus->phy_mask = ~(u32)BIT(1);
+
+ /* register mdiobus */
+ ret = mdiobus_register(adapter->mdiobus);
+ if (ret < 0)
+ goto return_error;
+ return 0;
+
+return_error:
+ return ret;
+}
+
+/* lan743x_pcidev_probe - Device Initialization Routine
+ * @pdev: PCI device information struct
+ * @id: entry in lan743x_pci_tbl
+ *
+ * Returns 0 on success, negative on failure
+ *
+ * initializes an adapter identified by a pci_dev structure.
+ * The OS initialization, configuring of the adapter private structure,
+ * and a hardware reset occur.
+ **/
+static int lan743x_pcidev_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct lan743x_adapter *adapter = NULL;
+ struct net_device *netdev = NULL;
+ int ret = -ENODEV;
+
+ netdev = devm_alloc_etherdev(&pdev->dev,
+ sizeof(struct lan743x_adapter));
+ if (!netdev)
+ goto return_error;
+
+ SET_NETDEV_DEV(netdev, &pdev->dev);
+ pci_set_drvdata(pdev, netdev);
+ adapter = netdev_priv(netdev);
+ adapter->netdev = netdev;
+ adapter->msg_enable = NETIF_MSG_DRV | NETIF_MSG_PROBE |
+ NETIF_MSG_LINK | NETIF_MSG_IFUP |
+ NETIF_MSG_IFDOWN | NETIF_MSG_TX_QUEUED;
+ netdev->max_mtu = LAN743X_MAX_FRAME_SIZE;
+
+ ret = lan743x_pci_init(adapter, pdev);
+ if (ret)
+ goto return_error;
+
+ ret = lan743x_csr_init(adapter);
+ if (ret)
+ goto cleanup_pci;
+
+ ret = lan743x_hardware_init(adapter, pdev);
+ if (ret)
+ goto cleanup_pci;
+
+ ret = lan743x_mdiobus_init(adapter);
+ if (ret)
+ goto cleanup_hardware;
+
+ adapter->netdev->netdev_ops = &lan743x_netdev_ops;
+ adapter->netdev->features = NETIF_F_SG | NETIF_F_TSO | NETIF_F_HW_CSUM;
+ adapter->netdev->hw_features = adapter->netdev->features;
+
+ /* carrier off reporting is important to ethtool even BEFORE open */
+ netif_carrier_off(netdev);
+
+ ret = register_netdev(adapter->netdev);
+ if (ret < 0)
+ goto cleanup_mdiobus;
+ return 0;
+
+cleanup_mdiobus:
+ lan743x_mdiobus_cleanup(adapter);
+
+cleanup_hardware:
+ lan743x_hardware_cleanup(adapter);
+
+cleanup_pci:
+ lan743x_pci_cleanup(adapter);
+
+return_error:
+ pr_warn("Initialization failed\n");
+ return ret;
+}
+
+/**
+ * lan743x_pcidev_remove - Device Removal Routine
+ * @pdev: PCI device information struct
+ *
+ * this is called by the PCI subsystem to alert the driver
+ * that it should release a PCI device. This could be caused by a
+ * Hot-Plug event, or because the driver is going to be removed from
+ * memory.
+ **/
+static void lan743x_pcidev_remove(struct pci_dev *pdev)
+{
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct lan743x_adapter *adapter = netdev_priv(netdev);
+
+ lan743x_full_cleanup(adapter);
+}
+
+static void lan743x_pcidev_shutdown(struct pci_dev *pdev)
+{
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct lan743x_adapter *adapter = netdev_priv(netdev);
+
+ rtnl_lock();
+ netif_device_detach(netdev);
+
+ /* close netdev when netdev is at running state.
+ * For instance, it is true when system goes to sleep by pm-suspend
+ * However, it is false when system goes to sleep by suspend GUI menu
+ */
+ if (netif_running(netdev))
+ lan743x_netdev_close(netdev);
+ rtnl_unlock();
+
+ /* clean up lan743x portion */
+ lan743x_hardware_cleanup(adapter);
+}
+
+static const struct pci_device_id lan743x_pcidev_tbl[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_SMSC, PCI_DEVICE_ID_SMSC_LAN7430) },
+ { 0, }
+};
+
+static struct pci_driver lan743x_pcidev_driver = {
+ .name = DRIVER_NAME,
+ .id_table = lan743x_pcidev_tbl,
+ .probe = lan743x_pcidev_probe,
+ .remove = lan743x_pcidev_remove,
+ .shutdown = lan743x_pcidev_shutdown,
+};
+
+module_pci_driver(lan743x_pcidev_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/microchip/lan743x_main.h b/drivers/net/ethernet/microchip/lan743x_main.h
new file mode 100644
index 000000000000..73b463a9df61
--- /dev/null
+++ b/drivers/net/ethernet/microchip/lan743x_main.h
@@ -0,0 +1,597 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (C) 2018 Microchip Technology Inc. */
+
+#ifndef _LAN743X_H
+#define _LAN743X_H
+
+#define DRIVER_AUTHOR "Bryan Whitehead <Bryan.Whitehead@microchip.com>"
+#define DRIVER_DESC "LAN743x PCIe Gigabit Ethernet Driver"
+#define DRIVER_NAME "lan743x"
+
+/* Register Definitions */
+#define ID_REV (0x00)
+#define ID_REV_IS_VALID_CHIP_ID_(id_rev) \
+ (((id_rev) & 0xFFF00000) == 0x74300000)
+#define ID_REV_CHIP_REV_MASK_ (0x0000FFFF)
+#define ID_REV_CHIP_REV_A0_ (0x00000000)
+#define ID_REV_CHIP_REV_B0_ (0x00000010)
+
+#define FPGA_REV (0x04)
+#define FPGA_REV_GET_MINOR_(fpga_rev) (((fpga_rev) >> 8) & 0x000000FF)
+#define FPGA_REV_GET_MAJOR_(fpga_rev) ((fpga_rev) & 0x000000FF)
+
+#define HW_CFG (0x010)
+#define HW_CFG_LRST_ BIT(1)
+
+#define PMT_CTL (0x014)
+#define PMT_CTL_READY_ BIT(7)
+#define PMT_CTL_ETH_PHY_RST_ BIT(4)
+
+#define DP_SEL (0x024)
+#define DP_SEL_DPRDY_ BIT(31)
+#define DP_SEL_MASK_ (0x0000001F)
+#define DP_SEL_RFE_RAM (0x00000001)
+
+#define DP_SEL_VHF_HASH_LEN (16)
+#define DP_SEL_VHF_VLAN_LEN (128)
+
+#define DP_CMD (0x028)
+#define DP_CMD_WRITE_ (0x00000001)
+
+#define DP_ADDR (0x02C)
+
+#define DP_DATA_0 (0x030)
+
+#define FCT_RX_CTL (0xAC)
+#define FCT_RX_CTL_EN_(channel) BIT(28 + (channel))
+#define FCT_RX_CTL_DIS_(channel) BIT(24 + (channel))
+#define FCT_RX_CTL_RESET_(channel) BIT(20 + (channel))
+
+#define FCT_TX_CTL (0xC4)
+#define FCT_TX_CTL_EN_(channel) BIT(28 + (channel))
+#define FCT_TX_CTL_DIS_(channel) BIT(24 + (channel))
+#define FCT_TX_CTL_RESET_(channel) BIT(20 + (channel))
+
+#define FCT_FLOW(rx_channel) (0xE0 + ((rx_channel) << 2))
+#define FCT_FLOW_CTL_OFF_THRESHOLD_ (0x00007F00)
+#define FCT_FLOW_CTL_OFF_THRESHOLD_SET_(value) \
+ ((value << 8) & FCT_FLOW_CTL_OFF_THRESHOLD_)
+#define FCT_FLOW_CTL_REQ_EN_ BIT(7)
+#define FCT_FLOW_CTL_ON_THRESHOLD_ (0x0000007F)
+#define FCT_FLOW_CTL_ON_THRESHOLD_SET_(value) \
+ ((value << 0) & FCT_FLOW_CTL_ON_THRESHOLD_)
+
+#define MAC_CR (0x100)
+#define MAC_CR_ADD_ BIT(12)
+#define MAC_CR_ASD_ BIT(11)
+#define MAC_CR_CNTR_RST_ BIT(5)
+#define MAC_CR_RST_ BIT(0)
+
+#define MAC_RX (0x104)
+#define MAC_RX_MAX_SIZE_SHIFT_ (16)
+#define MAC_RX_MAX_SIZE_MASK_ (0x3FFF0000)
+#define MAC_RX_RXD_ BIT(1)
+#define MAC_RX_RXEN_ BIT(0)
+
+#define MAC_TX (0x108)
+#define MAC_TX_TXD_ BIT(1)
+#define MAC_TX_TXEN_ BIT(0)
+
+#define MAC_FLOW (0x10C)
+#define MAC_FLOW_CR_TX_FCEN_ BIT(30)
+#define MAC_FLOW_CR_RX_FCEN_ BIT(29)
+#define MAC_FLOW_CR_FCPT_MASK_ (0x0000FFFF)
+
+#define MAC_RX_ADDRH (0x118)
+
+#define MAC_RX_ADDRL (0x11C)
+
+#define MAC_MII_ACC (0x120)
+#define MAC_MII_ACC_PHY_ADDR_SHIFT_ (11)
+#define MAC_MII_ACC_PHY_ADDR_MASK_ (0x0000F800)
+#define MAC_MII_ACC_MIIRINDA_SHIFT_ (6)
+#define MAC_MII_ACC_MIIRINDA_MASK_ (0x000007C0)
+#define MAC_MII_ACC_MII_READ_ (0x00000000)
+#define MAC_MII_ACC_MII_WRITE_ (0x00000002)
+#define MAC_MII_ACC_MII_BUSY_ BIT(0)
+
+#define MAC_MII_DATA (0x124)
+
+/* offset 0x400 - 0x500, x may range from 0 to 32, for a total of 33 entries */
+#define RFE_ADDR_FILT_HI(x) (0x400 + (8 * (x)))
+#define RFE_ADDR_FILT_HI_VALID_ BIT(31)
+
+/* offset 0x404 - 0x504, x may range from 0 to 32, for a total of 33 entries */
+#define RFE_ADDR_FILT_LO(x) (0x404 + (8 * (x)))
+
+#define RFE_CTL (0x508)
+#define RFE_CTL_AB_ BIT(10)
+#define RFE_CTL_AM_ BIT(9)
+#define RFE_CTL_AU_ BIT(8)
+#define RFE_CTL_MCAST_HASH_ BIT(3)
+#define RFE_CTL_DA_PERFECT_ BIT(1)
+
+#define INT_STS (0x780)
+#define INT_BIT_DMA_RX_(channel) BIT(24 + (channel))
+#define INT_BIT_ALL_RX_ (0x0F000000)
+#define INT_BIT_DMA_TX_(channel) BIT(16 + (channel))
+#define INT_BIT_ALL_TX_ (0x000F0000)
+#define INT_BIT_SW_GP_ BIT(9)
+#define INT_BIT_ALL_OTHER_ (0x00000280)
+#define INT_BIT_MAS_ BIT(0)
+
+#define INT_SET (0x784)
+
+#define INT_EN_SET (0x788)
+
+#define INT_EN_CLR (0x78C)
+
+#define INT_STS_R2C (0x790)
+
+#define INT_VEC_EN_SET (0x794)
+#define INT_VEC_EN_CLR (0x798)
+#define INT_VEC_EN_AUTO_CLR (0x79C)
+#define INT_VEC_EN_(vector_index) BIT(0 + vector_index)
+
+#define INT_VEC_MAP0 (0x7A0)
+#define INT_VEC_MAP0_RX_VEC_(channel, vector) \
+ (((u32)(vector)) << ((channel) << 2))
+
+#define INT_VEC_MAP1 (0x7A4)
+#define INT_VEC_MAP1_TX_VEC_(channel, vector) \
+ (((u32)(vector)) << ((channel) << 2))
+
+#define INT_VEC_MAP2 (0x7A8)
+
+#define INT_MOD_MAP0 (0x7B0)
+
+#define INT_MOD_MAP1 (0x7B4)
+
+#define INT_MOD_MAP2 (0x7B8)
+
+#define INT_MOD_CFG0 (0x7C0)
+#define INT_MOD_CFG1 (0x7C4)
+#define INT_MOD_CFG2 (0x7C8)
+#define INT_MOD_CFG3 (0x7CC)
+#define INT_MOD_CFG4 (0x7D0)
+#define INT_MOD_CFG5 (0x7D4)
+#define INT_MOD_CFG6 (0x7D8)
+#define INT_MOD_CFG7 (0x7DC)
+
+#define DMAC_CFG (0xC00)
+#define DMAC_CFG_COAL_EN_ BIT(16)
+#define DMAC_CFG_CH_ARB_SEL_RX_HIGH_ (0x00000000)
+#define DMAC_CFG_MAX_READ_REQ_MASK_ (0x00000070)
+#define DMAC_CFG_MAX_READ_REQ_SET_(val) \
+ ((((u32)(val)) << 4) & DMAC_CFG_MAX_READ_REQ_MASK_)
+#define DMAC_CFG_MAX_DSPACE_16_ (0x00000000)
+#define DMAC_CFG_MAX_DSPACE_32_ (0x00000001)
+#define DMAC_CFG_MAX_DSPACE_64_ BIT(1)
+#define DMAC_CFG_MAX_DSPACE_128_ (0x00000003)
+
+#define DMAC_COAL_CFG (0xC04)
+#define DMAC_COAL_CFG_TIMER_LIMIT_MASK_ (0xFFF00000)
+#define DMAC_COAL_CFG_TIMER_LIMIT_SET_(val) \
+ ((((u32)(val)) << 20) & DMAC_COAL_CFG_TIMER_LIMIT_MASK_)
+#define DMAC_COAL_CFG_TIMER_TX_START_ BIT(19)
+#define DMAC_COAL_CFG_FLUSH_INTS_ BIT(18)
+#define DMAC_COAL_CFG_INT_EXIT_COAL_ BIT(17)
+#define DMAC_COAL_CFG_CSR_EXIT_COAL_ BIT(16)
+#define DMAC_COAL_CFG_TX_THRES_MASK_ (0x0000FF00)
+#define DMAC_COAL_CFG_TX_THRES_SET_(val) \
+ ((((u32)(val)) << 8) & DMAC_COAL_CFG_TX_THRES_MASK_)
+#define DMAC_COAL_CFG_RX_THRES_MASK_ (0x000000FF)
+#define DMAC_COAL_CFG_RX_THRES_SET_(val) \
+ (((u32)(val)) & DMAC_COAL_CFG_RX_THRES_MASK_)
+
+#define DMAC_OBFF_CFG (0xC08)
+#define DMAC_OBFF_TX_THRES_MASK_ (0x0000FF00)
+#define DMAC_OBFF_TX_THRES_SET_(val) \
+ ((((u32)(val)) << 8) & DMAC_OBFF_TX_THRES_MASK_)
+#define DMAC_OBFF_RX_THRES_MASK_ (0x000000FF)
+#define DMAC_OBFF_RX_THRES_SET_(val) \
+ (((u32)(val)) & DMAC_OBFF_RX_THRES_MASK_)
+
+#define DMAC_CMD (0xC0C)
+#define DMAC_CMD_SWR_ BIT(31)
+#define DMAC_CMD_TX_SWR_(channel) BIT(24 + (channel))
+#define DMAC_CMD_START_T_(channel) BIT(20 + (channel))
+#define DMAC_CMD_STOP_T_(channel) BIT(16 + (channel))
+#define DMAC_CMD_RX_SWR_(channel) BIT(8 + (channel))
+#define DMAC_CMD_START_R_(channel) BIT(4 + (channel))
+#define DMAC_CMD_STOP_R_(channel) BIT(0 + (channel))
+
+#define DMAC_INT_STS (0xC10)
+#define DMAC_INT_EN_SET (0xC14)
+#define DMAC_INT_EN_CLR (0xC18)
+#define DMAC_INT_BIT_RXFRM_(channel) BIT(16 + (channel))
+#define DMAC_INT_BIT_TX_IOC_(channel) BIT(0 + (channel))
+
+#define RX_CFG_A(channel) (0xC40 + ((channel) << 6))
+#define RX_CFG_A_RX_WB_ON_INT_TMR_ BIT(30)
+#define RX_CFG_A_RX_WB_THRES_MASK_ (0x1F000000)
+#define RX_CFG_A_RX_WB_THRES_SET_(val) \
+ ((((u32)(val)) << 24) & RX_CFG_A_RX_WB_THRES_MASK_)
+#define RX_CFG_A_RX_PF_THRES_MASK_ (0x001F0000)
+#define RX_CFG_A_RX_PF_THRES_SET_(val) \
+ ((((u32)(val)) << 16) & RX_CFG_A_RX_PF_THRES_MASK_)
+#define RX_CFG_A_RX_PF_PRI_THRES_MASK_ (0x00001F00)
+#define RX_CFG_A_RX_PF_PRI_THRES_SET_(val) \
+ ((((u32)(val)) << 8) & RX_CFG_A_RX_PF_PRI_THRES_MASK_)
+#define RX_CFG_A_RX_HP_WB_EN_ BIT(5)
+
+#define RX_CFG_B(channel) (0xC44 + ((channel) << 6))
+#define RX_CFG_B_TS_ALL_RX_ BIT(29)
+#define RX_CFG_B_RX_PAD_MASK_ (0x03000000)
+#define RX_CFG_B_RX_PAD_0_ (0x00000000)
+#define RX_CFG_B_RX_PAD_2_ (0x02000000)
+#define RX_CFG_B_RDMABL_512_ (0x00040000)
+#define RX_CFG_B_RX_RING_LEN_MASK_ (0x0000FFFF)
+
+#define RX_BASE_ADDRH(channel) (0xC48 + ((channel) << 6))
+
+#define RX_BASE_ADDRL(channel) (0xC4C + ((channel) << 6))
+
+#define RX_HEAD_WRITEBACK_ADDRH(channel) (0xC50 + ((channel) << 6))
+
+#define RX_HEAD_WRITEBACK_ADDRL(channel) (0xC54 + ((channel) << 6))
+
+#define RX_HEAD(channel) (0xC58 + ((channel) << 6))
+
+#define RX_TAIL(channel) (0xC5C + ((channel) << 6))
+#define RX_TAIL_SET_TOP_INT_EN_ BIT(30)
+#define RX_TAIL_SET_TOP_INT_VEC_EN_ BIT(29)
+
+#define RX_CFG_C(channel) (0xC64 + ((channel) << 6))
+#define RX_CFG_C_RX_TOP_INT_EN_AUTO_CLR_ BIT(6)
+#define RX_CFG_C_RX_INT_EN_R2C_ BIT(4)
+#define RX_CFG_C_RX_DMA_INT_STS_AUTO_CLR_ BIT(3)
+#define RX_CFG_C_RX_INT_STS_R2C_MODE_MASK_ (0x00000007)
+
+#define TX_CFG_A(channel) (0xD40 + ((channel) << 6))
+#define TX_CFG_A_TX_HP_WB_ON_INT_TMR_ BIT(30)
+#define TX_CFG_A_TX_TMR_HPWB_SEL_IOC_ (0x10000000)
+#define TX_CFG_A_TX_PF_THRES_MASK_ (0x001F0000)
+#define TX_CFG_A_TX_PF_THRES_SET_(value) \
+ ((((u32)(value)) << 16) & TX_CFG_A_TX_PF_THRES_MASK_)
+#define TX_CFG_A_TX_PF_PRI_THRES_MASK_ (0x00001F00)
+#define TX_CFG_A_TX_PF_PRI_THRES_SET_(value) \
+ ((((u32)(value)) << 8) & TX_CFG_A_TX_PF_PRI_THRES_MASK_)
+#define TX_CFG_A_TX_HP_WB_EN_ BIT(5)
+#define TX_CFG_A_TX_HP_WB_THRES_MASK_ (0x0000000F)
+#define TX_CFG_A_TX_HP_WB_THRES_SET_(value) \
+ (((u32)(value)) & TX_CFG_A_TX_HP_WB_THRES_MASK_)
+
+#define TX_CFG_B(channel) (0xD44 + ((channel) << 6))
+#define TX_CFG_B_TDMABL_512_ (0x00040000)
+#define TX_CFG_B_TX_RING_LEN_MASK_ (0x0000FFFF)
+
+#define TX_BASE_ADDRH(channel) (0xD48 + ((channel) << 6))
+
+#define TX_BASE_ADDRL(channel) (0xD4C + ((channel) << 6))
+
+#define TX_HEAD_WRITEBACK_ADDRH(channel) (0xD50 + ((channel) << 6))
+
+#define TX_HEAD_WRITEBACK_ADDRL(channel) (0xD54 + ((channel) << 6))
+
+#define TX_HEAD(channel) (0xD58 + ((channel) << 6))
+
+#define TX_TAIL(channel) (0xD5C + ((channel) << 6))
+#define TX_TAIL_SET_DMAC_INT_EN_ BIT(31)
+#define TX_TAIL_SET_TOP_INT_EN_ BIT(30)
+#define TX_TAIL_SET_TOP_INT_VEC_EN_ BIT(29)
+
+#define TX_CFG_C(channel) (0xD64 + ((channel) << 6))
+#define TX_CFG_C_TX_TOP_INT_EN_AUTO_CLR_ BIT(6)
+#define TX_CFG_C_TX_DMA_INT_EN_AUTO_CLR_ BIT(5)
+#define TX_CFG_C_TX_INT_EN_R2C_ BIT(4)
+#define TX_CFG_C_TX_DMA_INT_STS_AUTO_CLR_ BIT(3)
+#define TX_CFG_C_TX_INT_STS_R2C_MODE_MASK_ (0x00000007)
+
+/* MAC statistics registers */
+#define STAT_RX_FCS_ERRORS (0x1200)
+#define STAT_RX_ALIGNMENT_ERRORS (0x1204)
+#define STAT_RX_JABBER_ERRORS (0x120C)
+#define STAT_RX_UNDERSIZE_FRAME_ERRORS (0x1210)
+#define STAT_RX_OVERSIZE_FRAME_ERRORS (0x1214)
+#define STAT_RX_DROPPED_FRAMES (0x1218)
+#define STAT_RX_UNICAST_BYTE_COUNT (0x121C)
+#define STAT_RX_BROADCAST_BYTE_COUNT (0x1220)
+#define STAT_RX_MULTICAST_BYTE_COUNT (0x1224)
+#define STAT_RX_MULTICAST_FRAMES (0x1230)
+#define STAT_RX_TOTAL_FRAMES (0x1254)
+
+#define STAT_TX_FCS_ERRORS (0x1280)
+#define STAT_TX_EXCESS_DEFERRAL_ERRORS (0x1284)
+#define STAT_TX_CARRIER_ERRORS (0x1288)
+#define STAT_TX_SINGLE_COLLISIONS (0x1290)
+#define STAT_TX_MULTIPLE_COLLISIONS (0x1294)
+#define STAT_TX_EXCESSIVE_COLLISION (0x1298)
+#define STAT_TX_LATE_COLLISIONS (0x129C)
+#define STAT_TX_UNICAST_BYTE_COUNT (0x12A0)
+#define STAT_TX_BROADCAST_BYTE_COUNT (0x12A4)
+#define STAT_TX_MULTICAST_BYTE_COUNT (0x12A8)
+#define STAT_TX_MULTICAST_FRAMES (0x12B4)
+#define STAT_TX_TOTAL_FRAMES (0x12D8)
+
+/* End of Register definitions */
+
+#define LAN743X_MAX_RX_CHANNELS (4)
+#define LAN743X_MAX_TX_CHANNELS (1)
+struct lan743x_adapter;
+
+#define LAN743X_USED_RX_CHANNELS (4)
+#define LAN743X_USED_TX_CHANNELS (1)
+#define LAN743X_INT_MOD (400)
+
+#if (LAN743X_USED_RX_CHANNELS > LAN743X_MAX_RX_CHANNELS)
+#error Invalid LAN743X_USED_RX_CHANNELS
+#endif
+#if (LAN743X_USED_TX_CHANNELS > LAN743X_MAX_TX_CHANNELS)
+#error Invalid LAN743X_USED_TX_CHANNELS
+#endif
+
+/* PCI */
+/* SMSC acquired EFAR late 1990's, MCHP acquired SMSC 2012 */
+#define PCI_VENDOR_ID_SMSC PCI_VENDOR_ID_EFAR
+#define PCI_DEVICE_ID_SMSC_LAN7430 (0x7430)
+
+#define PCI_CONFIG_LENGTH (0x1000)
+
+/* CSR */
+#define CSR_LENGTH (0x2000)
+
+#define LAN743X_CSR_FLAG_IS_A0 BIT(0)
+#define LAN743X_CSR_FLAG_IS_B0 BIT(1)
+#define LAN743X_CSR_FLAG_SUPPORTS_INTR_AUTO_SET_CLR BIT(8)
+
+struct lan743x_csr {
+ u32 flags;
+ u8 __iomem *csr_address;
+ u32 id_rev;
+ u32 fpga_rev;
+};
+
+/* INTERRUPTS */
+typedef void(*lan743x_vector_handler)(void *context, u32 int_sts, u32 flags);
+
+#define LAN743X_VECTOR_FLAG_IRQ_SHARED BIT(0)
+#define LAN743X_VECTOR_FLAG_SOURCE_STATUS_READ BIT(1)
+#define LAN743X_VECTOR_FLAG_SOURCE_STATUS_R2C BIT(2)
+#define LAN743X_VECTOR_FLAG_SOURCE_STATUS_W2C BIT(3)
+#define LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CHECK BIT(4)
+#define LAN743X_VECTOR_FLAG_SOURCE_ENABLE_CLEAR BIT(5)
+#define LAN743X_VECTOR_FLAG_SOURCE_ENABLE_R2C BIT(6)
+#define LAN743X_VECTOR_FLAG_MASTER_ENABLE_CLEAR BIT(7)
+#define LAN743X_VECTOR_FLAG_MASTER_ENABLE_SET BIT(8)
+#define LAN743X_VECTOR_FLAG_VECTOR_ENABLE_ISR_CLEAR BIT(9)
+#define LAN743X_VECTOR_FLAG_VECTOR_ENABLE_ISR_SET BIT(10)
+#define LAN743X_VECTOR_FLAG_VECTOR_ENABLE_AUTO_CLEAR BIT(11)
+#define LAN743X_VECTOR_FLAG_VECTOR_ENABLE_AUTO_SET BIT(12)
+#define LAN743X_VECTOR_FLAG_SOURCE_ENABLE_AUTO_CLEAR BIT(13)
+#define LAN743X_VECTOR_FLAG_SOURCE_ENABLE_AUTO_SET BIT(14)
+#define LAN743X_VECTOR_FLAG_SOURCE_STATUS_AUTO_CLEAR BIT(15)
+
+struct lan743x_vector {
+ int irq;
+ u32 flags;
+ struct lan743x_adapter *adapter;
+ int vector_index;
+ u32 int_mask;
+ lan743x_vector_handler handler;
+ void *context;
+};
+
+#define LAN743X_MAX_VECTOR_COUNT (8)
+
+struct lan743x_intr {
+ int flags;
+
+ unsigned int irq;
+
+ struct lan743x_vector vector_list[LAN743X_MAX_VECTOR_COUNT];
+ int number_of_vectors;
+ bool using_vectors;
+
+ int software_isr_flag;
+};
+
+#define LAN743X_MAX_FRAME_SIZE (9 * 1024)
+
+/* PHY */
+struct lan743x_phy {
+ bool fc_autoneg;
+ u8 fc_request_control;
+};
+
+/* TX */
+struct lan743x_tx_descriptor;
+struct lan743x_tx_buffer_info;
+
+#define GPIO_QUEUE_STARTED (0)
+#define GPIO_TX_FUNCTION (1)
+#define GPIO_TX_COMPLETION (2)
+#define GPIO_TX_FRAGMENT (3)
+
+#define TX_FRAME_FLAG_IN_PROGRESS BIT(0)
+
+struct lan743x_tx {
+ struct lan743x_adapter *adapter;
+ u32 vector_flags;
+ int channel_number;
+
+ int ring_size;
+ size_t ring_allocation_size;
+ struct lan743x_tx_descriptor *ring_cpu_ptr;
+ dma_addr_t ring_dma_ptr;
+ /* ring_lock: used to prevent concurrent access to tx ring */
+ spinlock_t ring_lock;
+ u32 frame_flags;
+ u32 frame_first;
+ u32 frame_data0;
+ u32 frame_tail;
+
+ struct lan743x_tx_buffer_info *buffer_info;
+
+ u32 *head_cpu_ptr;
+ dma_addr_t head_dma_ptr;
+ int last_head;
+ int last_tail;
+
+ struct napi_struct napi;
+
+ struct sk_buff *overflow_skb;
+};
+
+/* RX */
+struct lan743x_rx_descriptor;
+struct lan743x_rx_buffer_info;
+
+struct lan743x_rx {
+ struct lan743x_adapter *adapter;
+ u32 vector_flags;
+ int channel_number;
+
+ int ring_size;
+ size_t ring_allocation_size;
+ struct lan743x_rx_descriptor *ring_cpu_ptr;
+ dma_addr_t ring_dma_ptr;
+
+ struct lan743x_rx_buffer_info *buffer_info;
+
+ u32 *head_cpu_ptr;
+ dma_addr_t head_dma_ptr;
+ u32 last_head;
+ u32 last_tail;
+
+ struct napi_struct napi;
+
+ u32 frame_count;
+};
+
+struct lan743x_adapter {
+ struct net_device *netdev;
+ struct mii_bus *mdiobus;
+ int msg_enable;
+ struct pci_dev *pdev;
+ struct lan743x_csr csr;
+ struct lan743x_intr intr;
+
+ /* lock, used to prevent concurrent access to data port */
+ struct mutex dp_lock;
+
+ u8 mac_address[ETH_ALEN];
+
+ struct lan743x_phy phy;
+ struct lan743x_tx tx[LAN743X_MAX_TX_CHANNELS];
+ struct lan743x_rx rx[LAN743X_MAX_RX_CHANNELS];
+};
+
+#define LAN743X_COMPONENT_FLAG_RX(channel) BIT(20 + (channel))
+
+#define INTR_FLAG_IRQ_REQUESTED(vector_index) BIT(0 + vector_index)
+#define INTR_FLAG_MSI_ENABLED BIT(8)
+#define INTR_FLAG_MSIX_ENABLED BIT(9)
+
+#define MAC_MII_READ 1
+#define MAC_MII_WRITE 0
+
+#define PHY_FLAG_OPENED BIT(0)
+#define PHY_FLAG_ATTACHED BIT(1)
+
+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+#define DMA_ADDR_HIGH32(dma_addr) ((u32)(((dma_addr) >> 32) & 0xFFFFFFFF))
+#else
+#define DMA_ADDR_HIGH32(dma_addr) ((u32)(0))
+#endif
+#define DMA_ADDR_LOW32(dma_addr) ((u32)((dma_addr) & 0xFFFFFFFF))
+#define DMA_DESCRIPTOR_SPACING_16 (16)
+#define DMA_DESCRIPTOR_SPACING_32 (32)
+#define DMA_DESCRIPTOR_SPACING_64 (64)
+#define DMA_DESCRIPTOR_SPACING_128 (128)
+#define DEFAULT_DMA_DESCRIPTOR_SPACING (L1_CACHE_BYTES)
+
+#define DMAC_CHANNEL_STATE_SET(start_bit, stop_bit) \
+ (((start_bit) ? 2 : 0) | ((stop_bit) ? 1 : 0))
+#define DMAC_CHANNEL_STATE_INITIAL DMAC_CHANNEL_STATE_SET(0, 0)
+#define DMAC_CHANNEL_STATE_STARTED DMAC_CHANNEL_STATE_SET(1, 0)
+#define DMAC_CHANNEL_STATE_STOP_PENDING DMAC_CHANNEL_STATE_SET(1, 1)
+#define DMAC_CHANNEL_STATE_STOPPED DMAC_CHANNEL_STATE_SET(0, 1)
+
+/* TX Descriptor bits */
+#define TX_DESC_DATA0_DTYPE_MASK_ (0xC0000000)
+#define TX_DESC_DATA0_DTYPE_DATA_ (0x00000000)
+#define TX_DESC_DATA0_DTYPE_EXT_ (0x40000000)
+#define TX_DESC_DATA0_FS_ (0x20000000)
+#define TX_DESC_DATA0_LS_ (0x10000000)
+#define TX_DESC_DATA0_EXT_ (0x08000000)
+#define TX_DESC_DATA0_IOC_ (0x04000000)
+#define TX_DESC_DATA0_ICE_ (0x00400000)
+#define TX_DESC_DATA0_IPE_ (0x00200000)
+#define TX_DESC_DATA0_TPE_ (0x00100000)
+#define TX_DESC_DATA0_FCS_ (0x00020000)
+#define TX_DESC_DATA0_BUF_LENGTH_MASK_ (0x0000FFFF)
+#define TX_DESC_DATA0_EXT_LSO_ (0x00200000)
+#define TX_DESC_DATA0_EXT_PAY_LENGTH_MASK_ (0x000FFFFF)
+#define TX_DESC_DATA3_FRAME_LENGTH_MSS_MASK_ (0x3FFF0000)
+
+struct lan743x_tx_descriptor {
+ u32 data0;
+ u32 data1;
+ u32 data2;
+ u32 data3;
+} __aligned(DEFAULT_DMA_DESCRIPTOR_SPACING);
+
+#define TX_BUFFER_INFO_FLAG_ACTIVE BIT(0)
+#define TX_BUFFER_INFO_FLAG_IGNORE_SYNC BIT(2)
+#define TX_BUFFER_INFO_FLAG_SKB_FRAGMENT BIT(3)
+struct lan743x_tx_buffer_info {
+ int flags;
+ struct sk_buff *skb;
+ dma_addr_t dma_ptr;
+ unsigned int buffer_length;
+};
+
+#define LAN743X_TX_RING_SIZE (50)
+
+/* OWN bit is set. ie, Descs are owned by RX DMAC */
+#define RX_DESC_DATA0_OWN_ (0x00008000)
+/* OWN bit is clear. ie, Descs are owned by host */
+#define RX_DESC_DATA0_FS_ (0x80000000)
+#define RX_DESC_DATA0_LS_ (0x40000000)
+#define RX_DESC_DATA0_FRAME_LENGTH_MASK_ (0x3FFF0000)
+#define RX_DESC_DATA0_FRAME_LENGTH_GET_(data0) \
+ (((data0) & RX_DESC_DATA0_FRAME_LENGTH_MASK_) >> 16)
+#define RX_DESC_DATA0_EXT_ (0x00004000)
+#define RX_DESC_DATA0_BUF_LENGTH_MASK_ (0x00003FFF)
+#define RX_DESC_DATA2_TS_NS_MASK_ (0x3FFFFFFF)
+
+#if ((NET_IP_ALIGN != 0) && (NET_IP_ALIGN != 2))
+#error NET_IP_ALIGN must be 0 or 2
+#endif
+
+#define RX_HEAD_PADDING NET_IP_ALIGN
+
+struct lan743x_rx_descriptor {
+ u32 data0;
+ u32 data1;
+ u32 data2;
+ u32 data3;
+} __aligned(DEFAULT_DMA_DESCRIPTOR_SPACING);
+
+#define RX_BUFFER_INFO_FLAG_ACTIVE BIT(0)
+struct lan743x_rx_buffer_info {
+ int flags;
+ struct sk_buff *skb;
+
+ dma_addr_t dma_ptr;
+ unsigned int buffer_length;
+};
+
+#define LAN743X_RX_RING_SIZE (65)
+
+#define RX_PROCESS_RESULT_NOTHING_TO_DO (0)
+#define RX_PROCESS_RESULT_PACKET_RECEIVED (1)
+#define RX_PROCESS_RESULT_PACKET_DROPPED (2)
+
+#endif /* _LAN743X_H */
diff --git a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c
index 2521c8c40015..b2d2ec8c11e2 100644
--- a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c
+++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c
@@ -266,7 +266,7 @@ MODULE_FIRMWARE("myri10ge_rss_eth_z8e.dat");
/* Careful: must be accessed under kernel_param_lock() */
static char *myri10ge_fw_name = NULL;
-module_param(myri10ge_fw_name, charp, S_IRUGO | S_IWUSR);
+module_param(myri10ge_fw_name, charp, 0644);
MODULE_PARM_DESC(myri10ge_fw_name, "Firmware image name");
#define MYRI10GE_MAX_BOARDS 8
@@ -277,49 +277,49 @@ module_param_array_named(myri10ge_fw_names, myri10ge_fw_names, charp, NULL,
MODULE_PARM_DESC(myri10ge_fw_names, "Firmware image names per board");
static int myri10ge_ecrc_enable = 1;
-module_param(myri10ge_ecrc_enable, int, S_IRUGO);
+module_param(myri10ge_ecrc_enable, int, 0444);
MODULE_PARM_DESC(myri10ge_ecrc_enable, "Enable Extended CRC on PCI-E");
static int myri10ge_small_bytes = -1; /* -1 == auto */
-module_param(myri10ge_small_bytes, int, S_IRUGO | S_IWUSR);
+module_param(myri10ge_small_bytes, int, 0644);
MODULE_PARM_DESC(myri10ge_small_bytes, "Threshold of small packets");
static int myri10ge_msi = 1; /* enable msi by default */
-module_param(myri10ge_msi, int, S_IRUGO | S_IWUSR);
+module_param(myri10ge_msi, int, 0644);
MODULE_PARM_DESC(myri10ge_msi, "Enable Message Signalled Interrupts");
static int myri10ge_intr_coal_delay = 75;
-module_param(myri10ge_intr_coal_delay, int, S_IRUGO);
+module_param(myri10ge_intr_coal_delay, int, 0444);
MODULE_PARM_DESC(myri10ge_intr_coal_delay, "Interrupt coalescing delay");
static int myri10ge_flow_control = 1;
-module_param(myri10ge_flow_control, int, S_IRUGO);
+module_param(myri10ge_flow_control, int, 0444);
MODULE_PARM_DESC(myri10ge_flow_control, "Pause parameter");
static int myri10ge_deassert_wait = 1;
-module_param(myri10ge_deassert_wait, int, S_IRUGO | S_IWUSR);
+module_param(myri10ge_deassert_wait, int, 0644);
MODULE_PARM_DESC(myri10ge_deassert_wait,
"Wait when deasserting legacy interrupts");
static int myri10ge_force_firmware = 0;
-module_param(myri10ge_force_firmware, int, S_IRUGO);
+module_param(myri10ge_force_firmware, int, 0444);
MODULE_PARM_DESC(myri10ge_force_firmware,
"Force firmware to assume aligned completions");
static int myri10ge_initial_mtu = MYRI10GE_MAX_ETHER_MTU - ETH_HLEN;
-module_param(myri10ge_initial_mtu, int, S_IRUGO);
+module_param(myri10ge_initial_mtu, int, 0444);
MODULE_PARM_DESC(myri10ge_initial_mtu, "Initial MTU");
static int myri10ge_napi_weight = 64;
-module_param(myri10ge_napi_weight, int, S_IRUGO);
+module_param(myri10ge_napi_weight, int, 0444);
MODULE_PARM_DESC(myri10ge_napi_weight, "Set NAPI weight");
static int myri10ge_watchdog_timeout = 1;
-module_param(myri10ge_watchdog_timeout, int, S_IRUGO);
+module_param(myri10ge_watchdog_timeout, int, 0444);
MODULE_PARM_DESC(myri10ge_watchdog_timeout, "Set watchdog timeout");
static int myri10ge_max_irq_loops = 1048576;
-module_param(myri10ge_max_irq_loops, int, S_IRUGO);
+module_param(myri10ge_max_irq_loops, int, 0444);
MODULE_PARM_DESC(myri10ge_max_irq_loops,
"Set stuck legacy IRQ detection threshold");
@@ -330,21 +330,21 @@ module_param(myri10ge_debug, int, 0);
MODULE_PARM_DESC(myri10ge_debug, "Debug level (0=none,...,16=all)");
static int myri10ge_fill_thresh = 256;
-module_param(myri10ge_fill_thresh, int, S_IRUGO | S_IWUSR);
+module_param(myri10ge_fill_thresh, int, 0644);
MODULE_PARM_DESC(myri10ge_fill_thresh, "Number of empty rx slots allowed");
static int myri10ge_reset_recover = 1;
static int myri10ge_max_slices = 1;
-module_param(myri10ge_max_slices, int, S_IRUGO);
+module_param(myri10ge_max_slices, int, 0444);
MODULE_PARM_DESC(myri10ge_max_slices, "Max tx/rx queues");
static int myri10ge_rss_hash = MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT;
-module_param(myri10ge_rss_hash, int, S_IRUGO);
+module_param(myri10ge_rss_hash, int, 0444);
MODULE_PARM_DESC(myri10ge_rss_hash, "Type of RSS hashing to do");
static int myri10ge_dca = 1;
-module_param(myri10ge_dca, int, S_IRUGO);
+module_param(myri10ge_dca, int, 0444);
MODULE_PARM_DESC(myri10ge_dca, "Enable DCA if possible");
#define MYRI10GE_FW_OFFSET 1024*1024
diff --git a/drivers/net/ethernet/natsemi/jazzsonic.c b/drivers/net/ethernet/natsemi/jazzsonic.c
index d5b28884e21e..51fa82b429a3 100644
--- a/drivers/net/ethernet/natsemi/jazzsonic.c
+++ b/drivers/net/ethernet/natsemi/jazzsonic.c
@@ -60,14 +60,6 @@ do { \
*((volatile unsigned int *)dev->base_addr+(reg)) = (val); \
} while (0)
-
-/* use 0 for production, 1 for verification, >1 for debug */
-#ifdef SONIC_DEBUG
-static unsigned int sonic_debug = SONIC_DEBUG;
-#else
-static unsigned int sonic_debug = 1;
-#endif
-
/*
* We cannot use station (ethernet) address prefixes to detect the
* sonic controller since these are board manufacturer depended.
@@ -117,7 +109,6 @@ static const struct net_device_ops sonic_netdev_ops = {
static int sonic_probe1(struct net_device *dev)
{
- static unsigned version_printed;
unsigned int silicon_revision;
unsigned int val;
struct sonic_local *lp = netdev_priv(dev);
@@ -133,26 +124,17 @@ static int sonic_probe1(struct net_device *dev)
* the expected location.
*/
silicon_revision = SONIC_READ(SONIC_SR);
- if (sonic_debug > 1)
- printk("SONIC Silicon Revision = 0x%04x\n",silicon_revision);
-
i = 0;
while (known_revisions[i] != 0xffff &&
known_revisions[i] != silicon_revision)
i++;
if (known_revisions[i] == 0xffff) {
- printk("SONIC ethernet controller not found (0x%4x)\n",
- silicon_revision);
+ pr_info("SONIC ethernet controller not found (0x%4x)\n",
+ silicon_revision);
goto out;
}
- if (sonic_debug && version_printed++ == 0)
- printk(version);
-
- printk(KERN_INFO "%s: Sonic ethernet found at 0x%08lx, ",
- dev_name(lp->device), dev->base_addr);
-
/*
* Put the sonic into software reset, then
* retrieve and print the ethernet address.
@@ -245,12 +227,16 @@ static int jazz_sonic_probe(struct platform_device *pdev)
err = sonic_probe1(dev);
if (err)
goto out;
+
+ pr_info("SONIC ethernet @%08lx, MAC %pM, IRQ %d\n",
+ dev->base_addr, dev->dev_addr, dev->irq);
+
+ sonic_msg_init(dev);
+
err = register_netdev(dev);
if (err)
goto out1;
- printk("%s: MAC %pM IRQ %d\n", dev->name, dev->dev_addr, dev->irq);
-
return 0;
out1:
@@ -262,8 +248,6 @@ out:
}
MODULE_DESCRIPTION("Jazz SONIC ethernet driver");
-module_param(sonic_debug, int, 0);
-MODULE_PARM_DESC(sonic_debug, "jazzsonic debug level (1-4)");
MODULE_ALIAS("platform:jazzsonic");
#include "sonic.c"
diff --git a/drivers/net/ethernet/natsemi/macsonic.c b/drivers/net/ethernet/natsemi/macsonic.c
index b922ab5cedea..0937fc2a928e 100644
--- a/drivers/net/ethernet/natsemi/macsonic.c
+++ b/drivers/net/ethernet/natsemi/macsonic.c
@@ -60,8 +60,6 @@
#include <asm/macints.h>
#include <asm/mac_via.h>
-static char mac_sonic_string[] = "macsonic";
-
#include "sonic.h"
/* These should basically be bus-size and endian independent (since
@@ -72,15 +70,6 @@ static char mac_sonic_string[] = "macsonic";
#define SONIC_WRITE(reg,val) (nubus_writew(val, dev->base_addr + (reg * 4) \
+ lp->reg_offset))
-/* use 0 for production, 1 for verification, >1 for debug */
-#ifdef SONIC_DEBUG
-static unsigned int sonic_debug = SONIC_DEBUG;
-#else
-static unsigned int sonic_debug = 1;
-#endif
-
-static int sonic_version_printed;
-
/* For onboard SONIC */
#define ONBOARD_SONIC_REGISTERS 0x50F0A000
#define ONBOARD_SONIC_PROM_BASE 0x50f08000
@@ -313,11 +302,6 @@ static int mac_onboard_sonic_probe(struct net_device *dev)
int sr;
bool commslot = macintosh_config->expansion_type == MAC_EXP_PDS_COMM;
- if (!MACH_IS_MAC)
- return -ENODEV;
-
- printk(KERN_INFO "Checking for internal Macintosh ethernet (SONIC).. ");
-
/* Bogus probing, on the models which may or may not have
Ethernet (BTW, the Ethernet *is* always at the same
address, and nothing else lives there, at least if Apple's
@@ -327,13 +311,11 @@ static int mac_onboard_sonic_probe(struct net_device *dev)
card_present = hwreg_present((void*)ONBOARD_SONIC_REGISTERS);
if (!card_present) {
- printk("none.\n");
+ pr_info("Onboard/comm-slot SONIC not found\n");
return -ENODEV;
}
}
- printk("yes\n");
-
/* Danger! My arms are flailing wildly! You *must* set lp->reg_offset
* and dev->base_addr before using SONIC_READ() or SONIC_WRITE() */
dev->base_addr = ONBOARD_SONIC_REGISTERS;
@@ -342,18 +324,10 @@ static int mac_onboard_sonic_probe(struct net_device *dev)
else
dev->irq = IRQ_NUBUS_9;
- if (!sonic_version_printed) {
- printk(KERN_INFO "%s", version);
- sonic_version_printed = 1;
- }
- printk(KERN_INFO "%s: onboard / comm-slot SONIC at 0x%08lx\n",
- dev_name(lp->device), dev->base_addr);
-
/* The PowerBook's SONIC is 16 bit always. */
if (macintosh_config->ident == MAC_MODEL_PB520) {
lp->reg_offset = 0;
lp->dma_bitmode = SONIC_BITMODE16;
- sr = SONIC_READ(SONIC_SR);
} else if (commslot) {
/* Some of the comm-slot cards are 16 bit. But some
of them are not. The 32-bit cards use offset 2 and
@@ -370,22 +344,21 @@ static int mac_onboard_sonic_probe(struct net_device *dev)
else {
lp->dma_bitmode = SONIC_BITMODE16;
lp->reg_offset = 0;
- sr = SONIC_READ(SONIC_SR);
}
} else {
/* All onboard cards are at offset 2 with 32 bit DMA. */
lp->reg_offset = 2;
lp->dma_bitmode = SONIC_BITMODE32;
- sr = SONIC_READ(SONIC_SR);
}
- printk(KERN_INFO
- "%s: revision 0x%04x, using %d bit DMA and register offset %d\n",
- dev_name(lp->device), sr, lp->dma_bitmode?32:16, lp->reg_offset);
-#if 0 /* This is sometimes useful to find out how MacOS configured the card. */
- printk(KERN_INFO "%s: DCR: 0x%04x, DCR2: 0x%04x\n", dev_name(lp->device),
- SONIC_READ(SONIC_DCR) & 0xffff, SONIC_READ(SONIC_DCR2) & 0xffff);
-#endif
+ pr_info("Onboard/comm-slot SONIC, revision 0x%04x, %d bit DMA, register offset %d\n",
+ SONIC_READ(SONIC_SR), lp->dma_bitmode ? 32 : 16,
+ lp->reg_offset);
+
+ /* This is sometimes useful to find out how MacOS configured the card */
+ pr_debug("%s: DCR=0x%04x, DCR2=0x%04x\n", __func__,
+ SONIC_READ(SONIC_DCR) & 0xffff,
+ SONIC_READ(SONIC_DCR2) & 0xffff);
/* Software reset, then initialize control registers. */
SONIC_WRITE(SONIC_CMD, SONIC_CR_RST);
@@ -406,11 +379,14 @@ static int mac_onboard_sonic_probe(struct net_device *dev)
/* Now look for the MAC address. */
mac_onboard_sonic_ethernet_addr(dev);
+ pr_info("SONIC ethernet @%08lx, MAC %pM, IRQ %d\n",
+ dev->base_addr, dev->dev_addr, dev->irq);
+
/* Shared init code */
return macsonic_init(dev);
}
-static int mac_nubus_sonic_ethernet_addr(struct net_device *dev,
+static int mac_sonic_nubus_ethernet_addr(struct net_device *dev,
unsigned long prom_addr, int id)
{
int i;
@@ -449,70 +425,49 @@ static int macsonic_ident(struct nubus_rsrc *fres)
return -1;
}
-static int mac_nubus_sonic_probe(struct net_device *dev)
+static int mac_sonic_nubus_probe_board(struct nubus_board *board, int id,
+ struct net_device *dev)
{
- static int slots;
- struct nubus_rsrc *ndev = NULL;
struct sonic_local* lp = netdev_priv(dev);
unsigned long base_addr, prom_addr;
u16 sonic_dcr;
- int id = -1;
int reg_offset, dma_bitmode;
- /* Find the first SONIC that hasn't been initialized already */
- for_each_func_rsrc(ndev) {
- if (ndev->category != NUBUS_CAT_NETWORK ||
- ndev->type != NUBUS_TYPE_ETHERNET)
- continue;
-
- /* Have we seen it already? */
- if (slots & (1<<ndev->board->slot))
- continue;
- slots |= 1<<ndev->board->slot;
-
- /* Is it one of ours? */
- if ((id = macsonic_ident(ndev)) != -1)
- break;
- }
-
- if (ndev == NULL)
- return -ENODEV;
-
switch (id) {
case MACSONIC_DUODOCK:
- base_addr = ndev->board->slot_addr + DUODOCK_SONIC_REGISTERS;
- prom_addr = ndev->board->slot_addr + DUODOCK_SONIC_PROM_BASE;
+ base_addr = board->slot_addr + DUODOCK_SONIC_REGISTERS;
+ prom_addr = board->slot_addr + DUODOCK_SONIC_PROM_BASE;
sonic_dcr = SONIC_DCR_EXBUS | SONIC_DCR_RFT0 | SONIC_DCR_RFT1 |
SONIC_DCR_TFT0;
reg_offset = 2;
dma_bitmode = SONIC_BITMODE32;
break;
case MACSONIC_APPLE:
- base_addr = ndev->board->slot_addr + APPLE_SONIC_REGISTERS;
- prom_addr = ndev->board->slot_addr + APPLE_SONIC_PROM_BASE;
+ base_addr = board->slot_addr + APPLE_SONIC_REGISTERS;
+ prom_addr = board->slot_addr + APPLE_SONIC_PROM_BASE;
sonic_dcr = SONIC_DCR_BMS | SONIC_DCR_RFT1 | SONIC_DCR_TFT0;
reg_offset = 0;
dma_bitmode = SONIC_BITMODE32;
break;
case MACSONIC_APPLE16:
- base_addr = ndev->board->slot_addr + APPLE_SONIC_REGISTERS;
- prom_addr = ndev->board->slot_addr + APPLE_SONIC_PROM_BASE;
+ base_addr = board->slot_addr + APPLE_SONIC_REGISTERS;
+ prom_addr = board->slot_addr + APPLE_SONIC_PROM_BASE;
sonic_dcr = SONIC_DCR_EXBUS | SONIC_DCR_RFT1 | SONIC_DCR_TFT0 |
SONIC_DCR_PO1 | SONIC_DCR_BMS;
reg_offset = 0;
dma_bitmode = SONIC_BITMODE16;
break;
case MACSONIC_DAYNALINK:
- base_addr = ndev->board->slot_addr + APPLE_SONIC_REGISTERS;
- prom_addr = ndev->board->slot_addr + DAYNALINK_PROM_BASE;
+ base_addr = board->slot_addr + APPLE_SONIC_REGISTERS;
+ prom_addr = board->slot_addr + DAYNALINK_PROM_BASE;
sonic_dcr = SONIC_DCR_RFT1 | SONIC_DCR_TFT0 |
SONIC_DCR_PO1 | SONIC_DCR_BMS;
reg_offset = 0;
dma_bitmode = SONIC_BITMODE16;
break;
case MACSONIC_DAYNA:
- base_addr = ndev->board->slot_addr + DAYNA_SONIC_REGISTERS;
- prom_addr = ndev->board->slot_addr + DAYNA_SONIC_MAC_ADDR;
+ base_addr = board->slot_addr + DAYNA_SONIC_REGISTERS;
+ prom_addr = board->slot_addr + DAYNA_SONIC_MAC_ADDR;
sonic_dcr = SONIC_DCR_BMS |
SONIC_DCR_RFT1 | SONIC_DCR_TFT0 | SONIC_DCR_PO1;
reg_offset = 0;
@@ -528,21 +483,16 @@ static int mac_nubus_sonic_probe(struct net_device *dev)
dev->base_addr = base_addr;
lp->reg_offset = reg_offset;
lp->dma_bitmode = dma_bitmode;
- dev->irq = SLOT2IRQ(ndev->board->slot);
+ dev->irq = SLOT2IRQ(board->slot);
- if (!sonic_version_printed) {
- printk(KERN_INFO "%s", version);
- sonic_version_printed = 1;
- }
- printk(KERN_INFO "%s: %s in slot %X\n",
- dev_name(lp->device), ndev->board->name, ndev->board->slot);
- printk(KERN_INFO "%s: revision 0x%04x, using %d bit DMA and register offset %d\n",
- dev_name(lp->device), SONIC_READ(SONIC_SR), dma_bitmode?32:16, reg_offset);
+ dev_info(&board->dev, "%s, revision 0x%04x, %d bit DMA, register offset %d\n",
+ board->name, SONIC_READ(SONIC_SR),
+ lp->dma_bitmode ? 32 : 16, lp->reg_offset);
-#if 0 /* This is sometimes useful to find out how MacOS configured the card. */
- printk(KERN_INFO "%s: DCR: 0x%04x, DCR2: 0x%04x\n", dev_name(lp->device),
- SONIC_READ(SONIC_DCR) & 0xffff, SONIC_READ(SONIC_DCR2) & 0xffff);
-#endif
+ /* This is sometimes useful to find out how MacOS configured the card */
+ dev_dbg(&board->dev, "%s: DCR=0x%04x, DCR2=0x%04x\n", __func__,
+ SONIC_READ(SONIC_DCR) & 0xffff,
+ SONIC_READ(SONIC_DCR2) & 0xffff);
/* Software reset, then initialize control registers. */
SONIC_WRITE(SONIC_CMD, SONIC_CR_RST);
@@ -557,14 +507,17 @@ static int mac_nubus_sonic_probe(struct net_device *dev)
SONIC_WRITE(SONIC_ISR, 0x7fff);
/* Now look for the MAC address. */
- if (mac_nubus_sonic_ethernet_addr(dev, prom_addr, id) != 0)
+ if (mac_sonic_nubus_ethernet_addr(dev, prom_addr, id) != 0)
return -ENODEV;
+ dev_info(&board->dev, "SONIC ethernet @%08lx, MAC %pM, IRQ %d\n",
+ dev->base_addr, dev->dev_addr, dev->irq);
+
/* Shared init code */
return macsonic_init(dev);
}
-static int mac_sonic_probe(struct platform_device *pdev)
+static int mac_sonic_platform_probe(struct platform_device *pdev)
{
struct net_device *dev;
struct sonic_local *lp;
@@ -579,22 +532,16 @@ static int mac_sonic_probe(struct platform_device *pdev)
SET_NETDEV_DEV(dev, &pdev->dev);
platform_set_drvdata(pdev, dev);
- /* This will catch fatal stuff like -ENOMEM as well as success */
err = mac_onboard_sonic_probe(dev);
- if (err == 0)
- goto found;
- if (err != -ENODEV)
- goto out;
- err = mac_nubus_sonic_probe(dev);
if (err)
goto out;
-found:
+
+ sonic_msg_init(dev);
+
err = register_netdev(dev);
if (err)
goto out;
- printk("%s: MAC %pM IRQ %d\n", dev->name, dev->dev_addr, dev->irq);
-
return 0;
out:
@@ -604,13 +551,11 @@ out:
}
MODULE_DESCRIPTION("Macintosh SONIC ethernet driver");
-module_param(sonic_debug, int, 0);
-MODULE_PARM_DESC(sonic_debug, "macsonic debug level (1-4)");
MODULE_ALIAS("platform:macsonic");
#include "sonic.c"
-static int mac_sonic_device_remove(struct platform_device *pdev)
+static int mac_sonic_platform_remove(struct platform_device *pdev)
{
struct net_device *dev = platform_get_drvdata(pdev);
struct sonic_local* lp = netdev_priv(dev);
@@ -623,12 +568,105 @@ static int mac_sonic_device_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_driver mac_sonic_driver = {
- .probe = mac_sonic_probe,
- .remove = mac_sonic_device_remove,
- .driver = {
- .name = mac_sonic_string,
+static struct platform_driver mac_sonic_platform_driver = {
+ .probe = mac_sonic_platform_probe,
+ .remove = mac_sonic_platform_remove,
+ .driver = {
+ .name = "macsonic",
+ },
+};
+
+static int mac_sonic_nubus_probe(struct nubus_board *board)
+{
+ struct net_device *ndev;
+ struct sonic_local *lp;
+ struct nubus_rsrc *fres;
+ int id = -1;
+ int err;
+
+ /* The platform driver will handle a PDS or Comm Slot card (even if
+ * it has a pseudoslot declaration ROM).
+ */
+ if (macintosh_config->expansion_type == MAC_EXP_PDS_COMM)
+ return -ENODEV;
+
+ for_each_board_func_rsrc(board, fres) {
+ if (fres->category != NUBUS_CAT_NETWORK ||
+ fres->type != NUBUS_TYPE_ETHERNET)
+ continue;
+
+ id = macsonic_ident(fres);
+ if (id != -1)
+ break;
+ }
+ if (!fres)
+ return -ENODEV;
+
+ ndev = alloc_etherdev(sizeof(struct sonic_local));
+ if (!ndev)
+ return -ENOMEM;
+
+ lp = netdev_priv(ndev);
+ lp->device = &board->dev;
+ SET_NETDEV_DEV(ndev, &board->dev);
+
+ err = mac_sonic_nubus_probe_board(board, id, ndev);
+ if (err)
+ goto out;
+
+ sonic_msg_init(ndev);
+
+ err = register_netdev(ndev);
+ if (err)
+ goto out;
+
+ nubus_set_drvdata(board, ndev);
+
+ return 0;
+
+out:
+ free_netdev(ndev);
+ return err;
+}
+
+static int mac_sonic_nubus_remove(struct nubus_board *board)
+{
+ struct net_device *ndev = nubus_get_drvdata(board);
+ struct sonic_local *lp = netdev_priv(ndev);
+
+ unregister_netdev(ndev);
+ dma_free_coherent(lp->device,
+ SIZEOF_SONIC_DESC * SONIC_BUS_SCALE(lp->dma_bitmode),
+ lp->descriptors, lp->descriptors_laddr);
+ free_netdev(ndev);
+
+ return 0;
+}
+
+static struct nubus_driver mac_sonic_nubus_driver = {
+ .probe = mac_sonic_nubus_probe,
+ .remove = mac_sonic_nubus_remove,
+ .driver = {
+ .name = "macsonic-nubus",
+ .owner = THIS_MODULE,
},
};
-module_platform_driver(mac_sonic_driver);
+static int perr, nerr;
+
+static int __init mac_sonic_init(void)
+{
+ perr = platform_driver_register(&mac_sonic_platform_driver);
+ nerr = nubus_driver_register(&mac_sonic_nubus_driver);
+ return 0;
+}
+module_init(mac_sonic_init);
+
+static void __exit mac_sonic_exit(void)
+{
+ if (!perr)
+ platform_driver_unregister(&mac_sonic_platform_driver);
+ if (!nerr)
+ nubus_driver_unregister(&mac_sonic_nubus_driver);
+}
+module_exit(mac_sonic_exit);
diff --git a/drivers/net/ethernet/natsemi/sonic.c b/drivers/net/ethernet/natsemi/sonic.c
index 612c7a44b26c..7ed08486ae23 100644
--- a/drivers/net/ethernet/natsemi/sonic.c
+++ b/drivers/net/ethernet/natsemi/sonic.c
@@ -33,7 +33,21 @@
* the NetBSD file "sys/arch/mac68k/dev/if_sn.c".
*/
+static unsigned int version_printed;
+static int sonic_debug = -1;
+module_param(sonic_debug, int, 0);
+MODULE_PARM_DESC(sonic_debug, "debug message level");
+
+static void sonic_msg_init(struct net_device *dev)
+{
+ struct sonic_local *lp = netdev_priv(dev);
+
+ lp->msg_enable = netif_msg_init(sonic_debug, 0);
+
+ if (version_printed++ == 0)
+ netif_dbg(lp, drv, dev, "%s", version);
+}
/*
* Open/initialize the SONIC controller.
@@ -47,8 +61,7 @@ static int sonic_open(struct net_device *dev)
struct sonic_local *lp = netdev_priv(dev);
int i;
- if (sonic_debug > 2)
- printk("sonic_open: initializing sonic driver.\n");
+ netif_dbg(lp, ifup, dev, "%s: initializing sonic driver\n", __func__);
for (i = 0; i < SONIC_NUM_RRS; i++) {
struct sk_buff *skb = netdev_alloc_skb(dev, SONIC_RBSIZE + 2);
@@ -95,8 +108,7 @@ static int sonic_open(struct net_device *dev)
netif_start_queue(dev);
- if (sonic_debug > 2)
- printk("sonic_open: Initialization done.\n");
+ netif_dbg(lp, ifup, dev, "%s: Initialization done\n", __func__);
return 0;
}
@@ -110,8 +122,7 @@ static int sonic_close(struct net_device *dev)
struct sonic_local *lp = netdev_priv(dev);
int i;
- if (sonic_debug > 2)
- printk("sonic_close\n");
+ netif_dbg(lp, ifdown, dev, "%s\n", __func__);
netif_stop_queue(dev);
@@ -205,8 +216,7 @@ static int sonic_send_packet(struct sk_buff *skb, struct net_device *dev)
int length;
int entry = lp->next_tx;
- if (sonic_debug > 2)
- printk("sonic_send_packet: skb=%p, dev=%p\n", skb, dev);
+ netif_dbg(lp, tx_queued, dev, "%s: skb=%p\n", __func__, skb);
length = skb->len;
if (length < ETH_ZLEN) {
@@ -252,14 +262,12 @@ static int sonic_send_packet(struct sk_buff *skb, struct net_device *dev)
lp->next_tx = (entry + 1) & SONIC_TDS_MASK;
if (lp->tx_skb[lp->next_tx] != NULL) {
/* The ring is full, the ISR has yet to process the next TD. */
- if (sonic_debug > 3)
- printk("%s: stopping queue\n", dev->name);
+ netif_dbg(lp, tx_queued, dev, "%s: stopping queue\n", __func__);
netif_stop_queue(dev);
/* after this packet, wait for ISR to free up some TDAs */
} else netif_start_queue(dev);
- if (sonic_debug > 2)
- printk("sonic_send_packet: issuing Tx command\n");
+ netif_dbg(lp, tx_queued, dev, "%s: issuing Tx command\n", __func__);
SONIC_WRITE(SONIC_CMD, SONIC_CR_TXP);
@@ -281,8 +289,7 @@ static irqreturn_t sonic_interrupt(int irq, void *dev_id)
do {
if (status & SONIC_INT_PKTRX) {
- if (sonic_debug > 2)
- printk("%s: packet rx\n", dev->name);
+ netif_dbg(lp, intr, dev, "%s: packet rx\n", __func__);
sonic_rx(dev); /* got packet(s) */
SONIC_WRITE(SONIC_ISR, SONIC_INT_PKTRX); /* clear the interrupt */
}
@@ -299,8 +306,7 @@ static irqreturn_t sonic_interrupt(int irq, void *dev_id)
* still being allocated by sonic_send_packet (status clear & tx_skb[entry] clear)
*/
- if (sonic_debug > 2)
- printk("%s: tx done\n", dev->name);
+ netif_dbg(lp, intr, dev, "%s: tx done\n", __func__);
while (lp->tx_skb[entry] != NULL) {
if ((td_status = sonic_tda_get(dev, entry, SONIC_TD_STATUS)) == 0)
@@ -346,20 +352,20 @@ static irqreturn_t sonic_interrupt(int irq, void *dev_id)
* check error conditions
*/
if (status & SONIC_INT_RFO) {
- if (sonic_debug > 1)
- printk("%s: rx fifo overrun\n", dev->name);
+ netif_dbg(lp, rx_err, dev, "%s: rx fifo overrun\n",
+ __func__);
lp->stats.rx_fifo_errors++;
SONIC_WRITE(SONIC_ISR, SONIC_INT_RFO); /* clear the interrupt */
}
if (status & SONIC_INT_RDE) {
- if (sonic_debug > 1)
- printk("%s: rx descriptors exhausted\n", dev->name);
+ netif_dbg(lp, rx_err, dev, "%s: rx descriptors exhausted\n",
+ __func__);
lp->stats.rx_dropped++;
SONIC_WRITE(SONIC_ISR, SONIC_INT_RDE); /* clear the interrupt */
}
if (status & SONIC_INT_RBAE) {
- if (sonic_debug > 1)
- printk("%s: rx buffer area exceeded\n", dev->name);
+ netif_dbg(lp, rx_err, dev, "%s: rx buffer area exceeded\n",
+ __func__);
lp->stats.rx_dropped++;
SONIC_WRITE(SONIC_ISR, SONIC_INT_RBAE); /* clear the interrupt */
}
@@ -380,8 +386,9 @@ static irqreturn_t sonic_interrupt(int irq, void *dev_id)
/* transmit error */
if (status & SONIC_INT_TXER) {
- if ((SONIC_READ(SONIC_TCR) & SONIC_TCR_FU) && (sonic_debug > 2))
- printk(KERN_ERR "%s: tx fifo underrun\n", dev->name);
+ if (SONIC_READ(SONIC_TCR) & SONIC_TCR_FU)
+ netif_dbg(lp, tx_err, dev, "%s: tx fifo underrun\n",
+ __func__);
SONIC_WRITE(SONIC_ISR, SONIC_INT_TXER); /* clear the interrupt */
}
@@ -475,8 +482,8 @@ static void sonic_rx(struct net_device *dev)
if (lp->cur_rwp >= lp->rra_end) lp->cur_rwp = lp->rra_laddr & 0xffff;
SONIC_WRITE(SONIC_RWP, lp->cur_rwp);
if (SONIC_READ(SONIC_ISR) & SONIC_INT_RBE) {
- if (sonic_debug > 2)
- printk("%s: rx buffer exhausted\n", dev->name);
+ netif_dbg(lp, rx_err, dev, "%s: rx buffer exhausted\n",
+ __func__);
SONIC_WRITE(SONIC_ISR, SONIC_INT_RBE); /* clear the flag */
}
} else
@@ -542,9 +549,8 @@ static void sonic_multicast_list(struct net_device *dev)
(netdev_mc_count(dev) > 15)) {
rcr |= SONIC_RCR_AMC;
} else {
- if (sonic_debug > 2)
- printk("sonic_multicast_list: mc_count %d\n",
- netdev_mc_count(dev));
+ netif_dbg(lp, ifup, dev, "%s: mc_count %d\n", __func__,
+ netdev_mc_count(dev));
sonic_set_cam_enable(dev, 1); /* always enable our own address */
i = 1;
netdev_for_each_mc_addr(ha, dev) {
@@ -562,8 +568,7 @@ static void sonic_multicast_list(struct net_device *dev)
}
}
- if (sonic_debug > 2)
- printk("sonic_multicast_list: setting RCR=%x\n", rcr);
+ netif_dbg(lp, ifup, dev, "%s: setting RCR=%x\n", __func__, rcr);
SONIC_WRITE(SONIC_RCR, rcr);
}
@@ -596,8 +601,8 @@ static int sonic_init(struct net_device *dev)
/*
* initialize the receive resource area
*/
- if (sonic_debug > 2)
- printk("sonic_init: initialize receive resource area\n");
+ netif_dbg(lp, ifup, dev, "%s: initialize receive resource area\n",
+ __func__);
for (i = 0; i < SONIC_NUM_RRS; i++) {
u16 bufadr_l = (unsigned long)lp->rx_laddr[i] & 0xffff;
@@ -622,8 +627,7 @@ static int sonic_init(struct net_device *dev)
SONIC_WRITE(SONIC_EOBC, (SONIC_RBSIZE >> 1) - (lp->dma_bitmode ? 2 : 1));
/* load the resource pointers */
- if (sonic_debug > 3)
- printk("sonic_init: issuing RRRA command\n");
+ netif_dbg(lp, ifup, dev, "%s: issuing RRRA command\n", __func__);
SONIC_WRITE(SONIC_CMD, SONIC_CR_RRRA);
i = 0;
@@ -632,16 +636,17 @@ static int sonic_init(struct net_device *dev)
break;
}
- if (sonic_debug > 2)
- printk("sonic_init: status=%x i=%d\n", SONIC_READ(SONIC_CMD), i);
+ netif_dbg(lp, ifup, dev, "%s: status=%x, i=%d\n", __func__,
+ SONIC_READ(SONIC_CMD), i);
/*
* Initialize the receive descriptors so that they
* become a circular linked list, ie. let the last
* descriptor point to the first again.
*/
- if (sonic_debug > 2)
- printk("sonic_init: initialize receive descriptors\n");
+ netif_dbg(lp, ifup, dev, "%s: initialize receive descriptors\n",
+ __func__);
+
for (i=0; i<SONIC_NUM_RDS; i++) {
sonic_rda_put(dev, i, SONIC_RD_STATUS, 0);
sonic_rda_put(dev, i, SONIC_RD_PKTLEN, 0);
@@ -664,8 +669,9 @@ static int sonic_init(struct net_device *dev)
/*
* initialize transmit descriptors
*/
- if (sonic_debug > 2)
- printk("sonic_init: initialize transmit descriptors\n");
+ netif_dbg(lp, ifup, dev, "%s: initialize transmit descriptors\n",
+ __func__);
+
for (i = 0; i < SONIC_NUM_TDS; i++) {
sonic_tda_put(dev, i, SONIC_TD_STATUS, 0);
sonic_tda_put(dev, i, SONIC_TD_CONFIG, 0);
@@ -712,10 +718,8 @@ static int sonic_init(struct net_device *dev)
if (SONIC_READ(SONIC_ISR) & SONIC_INT_LCD)
break;
}
- if (sonic_debug > 2) {
- printk("sonic_init: CMD=%x, ISR=%x\n, i=%d",
- SONIC_READ(SONIC_CMD), SONIC_READ(SONIC_ISR), i);
- }
+ netif_dbg(lp, ifup, dev, "%s: CMD=%x, ISR=%x, i=%d\n", __func__,
+ SONIC_READ(SONIC_CMD), SONIC_READ(SONIC_ISR), i);
/*
* enable receiver, disable loopback
@@ -731,9 +735,8 @@ static int sonic_init(struct net_device *dev)
if ((cmd & SONIC_CR_RXEN) == 0 || (cmd & SONIC_CR_STP) == 0)
printk(KERN_ERR "sonic_init: failed, status=%x\n", cmd);
- if (sonic_debug > 2)
- printk("sonic_init: new status=%x\n",
- SONIC_READ(SONIC_CMD));
+ netif_dbg(lp, ifup, dev, "%s: new status=%x\n", __func__,
+ SONIC_READ(SONIC_CMD));
return 0;
}
diff --git a/drivers/net/ethernet/natsemi/sonic.h b/drivers/net/ethernet/natsemi/sonic.h
index 421b1a283fed..2b27f7049acb 100644
--- a/drivers/net/ethernet/natsemi/sonic.h
+++ b/drivers/net/ethernet/natsemi/sonic.h
@@ -319,6 +319,7 @@ struct sonic_local {
unsigned int eol_rx;
unsigned int eol_tx; /* last unacked transmit packet */
unsigned int next_tx; /* next free TD */
+ int msg_enable;
struct device *device; /* generic device */
struct net_device_stats stats;
};
@@ -336,6 +337,7 @@ static struct net_device_stats *sonic_get_stats(struct net_device *dev);
static void sonic_multicast_list(struct net_device *dev);
static int sonic_init(struct net_device *dev);
static void sonic_tx_timeout(struct net_device *dev);
+static void sonic_msg_init(struct net_device *dev);
/* Internal inlines for reading/writing DMA buffers. Note that bus
size and endianness matter here, whereas they don't for registers,
diff --git a/drivers/net/ethernet/natsemi/xtsonic.c b/drivers/net/ethernet/natsemi/xtsonic.c
index 1817deea98a4..e1b886e87a76 100644
--- a/drivers/net/ethernet/natsemi/xtsonic.c
+++ b/drivers/net/ethernet/natsemi/xtsonic.c
@@ -73,14 +73,6 @@ extern void xtboard_get_ether_addr(unsigned char *buf);
#define SONIC_WRITE(reg,val) \
*((volatile unsigned int *)dev->base_addr+reg) = val
-
-/* Use 0 for production, 1 for verification, and >2 for debug */
-#ifdef SONIC_DEBUG
-static unsigned int sonic_debug = SONIC_DEBUG;
-#else
-static unsigned int sonic_debug = 1;
-#endif
-
/*
* We cannot use station (ethernet) address prefixes to detect the
* sonic controller since these are board manufacturer depended.
@@ -130,7 +122,6 @@ static const struct net_device_ops xtsonic_netdev_ops = {
static int __init sonic_probe1(struct net_device *dev)
{
- static unsigned version_printed = 0;
unsigned int silicon_revision;
struct sonic_local *lp = netdev_priv(dev);
unsigned int base_addr = dev->base_addr;
@@ -146,23 +137,17 @@ static int __init sonic_probe1(struct net_device *dev)
* the expected location.
*/
silicon_revision = SONIC_READ(SONIC_SR);
- if (sonic_debug > 1)
- printk("SONIC Silicon Revision = 0x%04x\n",silicon_revision);
-
i = 0;
while ((known_revisions[i] != 0xffff) &&
(known_revisions[i] != silicon_revision))
i++;
if (known_revisions[i] == 0xffff) {
- printk("SONIC ethernet controller not found (0x%4x)\n",
- silicon_revision);
+ pr_info("SONIC ethernet controller not found (0x%4x)\n",
+ silicon_revision);
return -ENODEV;
}
- if (sonic_debug && version_printed++ == 0)
- printk(version);
-
/*
* Put the sonic into software reset, then retrieve ethernet address.
* Note: we are assuming that the boot-loader has initialized the cam.
@@ -273,12 +258,15 @@ int xtsonic_probe(struct platform_device *pdev)
if ((err = sonic_probe1(dev)))
goto out;
+
+ pr_info("SONIC ethernet @%08lx, MAC %pM, IRQ %d\n",
+ dev->base_addr, dev->dev_addr, dev->irq);
+
+ sonic_msg_init(dev);
+
if ((err = register_netdev(dev)))
goto out1;
- printk("%s: SONIC ethernet @%08lx, MAC %pM, IRQ %d\n", dev->name,
- dev->base_addr, dev->dev_addr, dev->irq);
-
return 0;
out1:
@@ -290,8 +278,6 @@ out:
}
MODULE_DESCRIPTION("Xtensa XT2000 SONIC ethernet driver");
-module_param(sonic_debug, int, 0);
-MODULE_PARM_DESC(sonic_debug, "xtsonic debug level (1-4)");
#include "sonic.c"
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/Makefile b/drivers/net/ethernet/netronome/nfp/bpf/Makefile
new file mode 100644
index 000000000000..805fa28f391a
--- /dev/null
+++ b/drivers/net/ethernet/netronome/nfp/bpf/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+# kbuild requires Makefile in a directory to build individual objects
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/cmsg.c b/drivers/net/ethernet/netronome/nfp/bpf/cmsg.c
index 80d3aa0fc9d3..7e298148ca26 100644
--- a/drivers/net/ethernet/netronome/nfp/bpf/cmsg.c
+++ b/drivers/net/ethernet/netronome/nfp/bpf/cmsg.c
@@ -218,17 +218,17 @@ nfp_bpf_cmsg_communicate(struct nfp_app_bpf *bpf, struct sk_buff *skb,
return skb;
hdr = (struct cmsg_hdr *)skb->data;
- /* 0 reply_size means caller will do the validation */
- if (reply_size && skb->len != reply_size) {
- cmsg_warn(bpf, "cmsg drop - wrong size %d != %d!\n",
- skb->len, reply_size);
- goto err_free;
- }
if (hdr->type != __CMSG_REPLY(type)) {
cmsg_warn(bpf, "cmsg drop - wrong type 0x%02x != 0x%02lx!\n",
hdr->type, __CMSG_REPLY(type));
goto err_free;
}
+ /* 0 reply_size means caller will do the validation */
+ if (reply_size && skb->len != reply_size) {
+ cmsg_warn(bpf, "cmsg drop - type 0x%02x wrong size %d != %d!\n",
+ type, skb->len, reply_size);
+ goto err_free;
+ }
return skb;
err_free:
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/fw.h b/drivers/net/ethernet/netronome/nfp/bpf/fw.h
index cfcc7bcb2c67..39639ac28b01 100644
--- a/drivers/net/ethernet/netronome/nfp/bpf/fw.h
+++ b/drivers/net/ethernet/netronome/nfp/bpf/fw.h
@@ -41,6 +41,7 @@ enum bpf_cap_tlv_type {
NFP_BPF_CAP_TYPE_FUNC = 1,
NFP_BPF_CAP_TYPE_ADJUST_HEAD = 2,
NFP_BPF_CAP_TYPE_MAPS = 3,
+ NFP_BPF_CAP_TYPE_RANDOM = 4,
};
struct nfp_bpf_cap_tlv_func {
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/jit.c b/drivers/net/ethernet/netronome/nfp/bpf/jit.c
index 56451edf01c2..29b4e5f8c102 100644
--- a/drivers/net/ethernet/netronome/nfp/bpf/jit.c
+++ b/drivers/net/ethernet/netronome/nfp/bpf/jit.c
@@ -74,7 +74,9 @@ nfp_meta_has_prev(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
static void nfp_prog_push(struct nfp_prog *nfp_prog, u64 insn)
{
- if (nfp_prog->__prog_alloc_len == nfp_prog->prog_len) {
+ if (nfp_prog->__prog_alloc_len / sizeof(u64) == nfp_prog->prog_len) {
+ pr_warn("instruction limit reached (%u NFP instructions)\n",
+ nfp_prog->prog_len);
nfp_prog->error = -ENOSPC;
return;
}
@@ -103,23 +105,18 @@ nfp_prog_confirm_current_offset(struct nfp_prog *nfp_prog, unsigned int off)
/* --- Emitters --- */
static void
__emit_cmd(struct nfp_prog *nfp_prog, enum cmd_tgt_map op,
- u8 mode, u8 xfer, u8 areg, u8 breg, u8 size, bool sync, bool indir)
+ u8 mode, u8 xfer, u8 areg, u8 breg, u8 size, enum cmd_ctx_swap ctx,
+ bool indir)
{
- enum cmd_ctx_swap ctx;
u64 insn;
- if (sync)
- ctx = CMD_CTX_SWAP;
- else
- ctx = CMD_CTX_NO_SWAP;
-
insn = FIELD_PREP(OP_CMD_A_SRC, areg) |
FIELD_PREP(OP_CMD_CTX, ctx) |
FIELD_PREP(OP_CMD_B_SRC, breg) |
FIELD_PREP(OP_CMD_TOKEN, cmd_tgt_act[op].token) |
FIELD_PREP(OP_CMD_XFER, xfer) |
FIELD_PREP(OP_CMD_CNT, size) |
- FIELD_PREP(OP_CMD_SIG, sync) |
+ FIELD_PREP(OP_CMD_SIG, ctx != CMD_CTX_NO_SWAP) |
FIELD_PREP(OP_CMD_TGT_CMD, cmd_tgt_act[op].tgt_cmd) |
FIELD_PREP(OP_CMD_INDIR, indir) |
FIELD_PREP(OP_CMD_MODE, mode);
@@ -129,7 +126,7 @@ __emit_cmd(struct nfp_prog *nfp_prog, enum cmd_tgt_map op,
static void
emit_cmd_any(struct nfp_prog *nfp_prog, enum cmd_tgt_map op, u8 mode, u8 xfer,
- swreg lreg, swreg rreg, u8 size, bool sync, bool indir)
+ swreg lreg, swreg rreg, u8 size, enum cmd_ctx_swap ctx, bool indir)
{
struct nfp_insn_re_regs reg;
int err;
@@ -150,22 +147,22 @@ emit_cmd_any(struct nfp_prog *nfp_prog, enum cmd_tgt_map op, u8 mode, u8 xfer,
return;
}
- __emit_cmd(nfp_prog, op, mode, xfer, reg.areg, reg.breg, size, sync,
+ __emit_cmd(nfp_prog, op, mode, xfer, reg.areg, reg.breg, size, ctx,
indir);
}
static void
emit_cmd(struct nfp_prog *nfp_prog, enum cmd_tgt_map op, u8 mode, u8 xfer,
- swreg lreg, swreg rreg, u8 size, bool sync)
+ swreg lreg, swreg rreg, u8 size, enum cmd_ctx_swap ctx)
{
- emit_cmd_any(nfp_prog, op, mode, xfer, lreg, rreg, size, sync, false);
+ emit_cmd_any(nfp_prog, op, mode, xfer, lreg, rreg, size, ctx, false);
}
static void
emit_cmd_indir(struct nfp_prog *nfp_prog, enum cmd_tgt_map op, u8 mode, u8 xfer,
- swreg lreg, swreg rreg, u8 size, bool sync)
+ swreg lreg, swreg rreg, u8 size, enum cmd_ctx_swap ctx)
{
- emit_cmd_any(nfp_prog, op, mode, xfer, lreg, rreg, size, sync, true);
+ emit_cmd_any(nfp_prog, op, mode, xfer, lreg, rreg, size, ctx, true);
}
static void
@@ -410,7 +407,7 @@ __emit_lcsr(struct nfp_prog *nfp_prog, u16 areg, u16 breg, bool wr, u16 addr,
FIELD_PREP(OP_LCSR_A_SRC, areg) |
FIELD_PREP(OP_LCSR_B_SRC, breg) |
FIELD_PREP(OP_LCSR_WRITE, wr) |
- FIELD_PREP(OP_LCSR_ADDR, addr) |
+ FIELD_PREP(OP_LCSR_ADDR, addr / 4) |
FIELD_PREP(OP_LCSR_SRC_LMEXTN, src_lmextn) |
FIELD_PREP(OP_LCSR_DST_LMEXTN, dst_lmextn);
@@ -438,10 +435,16 @@ static void emit_csr_wr(struct nfp_prog *nfp_prog, swreg src, u16 addr)
return;
}
- __emit_lcsr(nfp_prog, reg.areg, reg.breg, true, addr / 4,
+ __emit_lcsr(nfp_prog, reg.areg, reg.breg, true, addr,
false, reg.src_lmextn);
}
+/* CSR value is read in following immed[gpr, 0] */
+static void __emit_csr_rd(struct nfp_prog *nfp_prog, u16 addr)
+{
+ __emit_lcsr(nfp_prog, 0, 0, false, addr, false, false);
+}
+
static void emit_nop(struct nfp_prog *nfp_prog)
{
__emit_immed(nfp_prog, UR_REG_IMM, UR_REG_IMM, 0, 0, 0, 0, 0, 0, 0);
@@ -553,6 +556,19 @@ wrp_reg_subpart(struct nfp_prog *nfp_prog, swreg dst, swreg src, u8 field_len,
emit_ld_field_any(nfp_prog, dst, mask, src, sc, offset * 8, true);
}
+/* wrp_reg_or_subpart() - load @field_len bytes from low end of @src, or the
+ * result to @dst from offset, there is no change on the other bits of @dst.
+ */
+static void
+wrp_reg_or_subpart(struct nfp_prog *nfp_prog, swreg dst, swreg src,
+ u8 field_len, u8 offset)
+{
+ enum shf_sc sc = offset ? SHF_SC_L_SHF : SHF_SC_NONE;
+ u8 mask = ((1 << field_len) - 1) << offset;
+
+ emit_ld_field(nfp_prog, dst, mask, src, sc, 32 - offset * 8);
+}
+
static void
addr40_offset(struct nfp_prog *nfp_prog, u8 src_gpr, swreg offset,
swreg *rega, swreg *regb)
@@ -597,7 +613,7 @@ static int nfp_cpp_memcpy(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
/* Memory read from source addr into transfer-in registers. */
emit_cmd_any(nfp_prog, CMD_TGT_READ32_SWAP,
src_40bit_addr ? CMD_MODE_40b_BA : CMD_MODE_32b, 0,
- src_base, off, xfer_num - 1, true, len > 32);
+ src_base, off, xfer_num - 1, CMD_CTX_SWAP, len > 32);
/* Move from transfer-in to transfer-out. */
for (i = 0; i < xfer_num; i++)
@@ -609,39 +625,39 @@ static int nfp_cpp_memcpy(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
/* Use single direct_ref write8. */
emit_cmd(nfp_prog, CMD_TGT_WRITE8_SWAP, CMD_MODE_32b, 0,
reg_a(meta->paired_st->dst_reg * 2), off, len - 1,
- true);
+ CMD_CTX_SWAP);
} else if (len <= 32 && IS_ALIGNED(len, 4)) {
/* Use single direct_ref write32. */
emit_cmd(nfp_prog, CMD_TGT_WRITE32_SWAP, CMD_MODE_32b, 0,
reg_a(meta->paired_st->dst_reg * 2), off, xfer_num - 1,
- true);
+ CMD_CTX_SWAP);
} else if (len <= 32) {
/* Use single indirect_ref write8. */
wrp_immed(nfp_prog, reg_none(),
CMD_OVE_LEN | FIELD_PREP(CMD_OV_LEN, len - 1));
emit_cmd_indir(nfp_prog, CMD_TGT_WRITE8_SWAP, CMD_MODE_32b, 0,
reg_a(meta->paired_st->dst_reg * 2), off,
- len - 1, true);
+ len - 1, CMD_CTX_SWAP);
} else if (IS_ALIGNED(len, 4)) {
/* Use single indirect_ref write32. */
wrp_immed(nfp_prog, reg_none(),
CMD_OVE_LEN | FIELD_PREP(CMD_OV_LEN, xfer_num - 1));
emit_cmd_indir(nfp_prog, CMD_TGT_WRITE32_SWAP, CMD_MODE_32b, 0,
reg_a(meta->paired_st->dst_reg * 2), off,
- xfer_num - 1, true);
+ xfer_num - 1, CMD_CTX_SWAP);
} else if (len <= 40) {
/* Use one direct_ref write32 to write the first 32-bytes, then
* another direct_ref write8 to write the remaining bytes.
*/
emit_cmd(nfp_prog, CMD_TGT_WRITE32_SWAP, CMD_MODE_32b, 0,
reg_a(meta->paired_st->dst_reg * 2), off, 7,
- true);
+ CMD_CTX_SWAP);
off = re_load_imm_any(nfp_prog, meta->paired_st->off + 32,
imm_b(nfp_prog));
emit_cmd(nfp_prog, CMD_TGT_WRITE8_SWAP, CMD_MODE_32b, 8,
reg_a(meta->paired_st->dst_reg * 2), off, len - 33,
- true);
+ CMD_CTX_SWAP);
} else {
/* Use one indirect_ref write32 to write 4-bytes aligned length,
* then another direct_ref write8 to write the remaining bytes.
@@ -652,12 +668,12 @@ static int nfp_cpp_memcpy(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
CMD_OVE_LEN | FIELD_PREP(CMD_OV_LEN, xfer_num - 2));
emit_cmd_indir(nfp_prog, CMD_TGT_WRITE32_SWAP, CMD_MODE_32b, 0,
reg_a(meta->paired_st->dst_reg * 2), off,
- xfer_num - 2, true);
+ xfer_num - 2, CMD_CTX_SWAP);
new_off = meta->paired_st->off + (xfer_num - 1) * 4;
off = re_load_imm_any(nfp_prog, new_off, imm_b(nfp_prog));
emit_cmd(nfp_prog, CMD_TGT_WRITE8_SWAP, CMD_MODE_32b,
xfer_num - 1, reg_a(meta->paired_st->dst_reg * 2), off,
- (len & 0x3) - 1, true);
+ (len & 0x3) - 1, CMD_CTX_SWAP);
}
/* TODO: The following extra load is to make sure data flow be identical
@@ -718,7 +734,7 @@ data_ld(struct nfp_prog *nfp_prog, swreg offset, u8 dst_gpr, int size)
shift = size < 4 ? 4 - size : 0;
emit_cmd(nfp_prog, CMD_TGT_READ8, CMD_MODE_32b, 0,
- pptr_reg(nfp_prog), offset, sz - 1, true);
+ pptr_reg(nfp_prog), offset, sz - 1, CMD_CTX_SWAP);
i = 0;
if (shift)
@@ -748,7 +764,7 @@ data_ld_host_order(struct nfp_prog *nfp_prog, u8 dst_gpr,
mask = size < 4 ? GENMASK(size - 1, 0) : 0;
emit_cmd(nfp_prog, CMD_TGT_READ32_SWAP, mode, 0,
- lreg, rreg, sz / 4 - 1, true);
+ lreg, rreg, sz / 4 - 1, CMD_CTX_SWAP);
i = 0;
if (mask)
@@ -828,7 +844,7 @@ data_stx_host_order(struct nfp_prog *nfp_prog, u8 dst_gpr, swreg offset,
wrp_mov(nfp_prog, reg_xfer(i), reg_a(src_gpr + i));
emit_cmd(nfp_prog, CMD_TGT_WRITE8_SWAP, CMD_MODE_32b, 0,
- reg_a(dst_gpr), offset, size - 1, true);
+ reg_a(dst_gpr), offset, size - 1, CMD_CTX_SWAP);
return 0;
}
@@ -842,7 +858,7 @@ data_st_host_order(struct nfp_prog *nfp_prog, u8 dst_gpr, swreg offset,
wrp_immed(nfp_prog, reg_xfer(1), imm >> 32);
emit_cmd(nfp_prog, CMD_TGT_WRITE8_SWAP, CMD_MODE_32b, 0,
- reg_a(dst_gpr), offset, size - 1, true);
+ reg_a(dst_gpr), offset, size - 1, CMD_CTX_SWAP);
return 0;
}
@@ -1339,7 +1355,7 @@ static int adjust_head(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
}
static int
-map_lookup_stack(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
+map_call_stack_common(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
{
struct bpf_offloaded_map *offmap;
struct nfp_bpf_map *nfp_map;
@@ -1353,19 +1369,21 @@ map_lookup_stack(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
/* We only have to reload LM0 if the key is not at start of stack */
lm_off = nfp_prog->stack_depth;
- lm_off += meta->arg2.var_off.value + meta->arg2.off;
- load_lm_ptr = meta->arg2_var_off || lm_off;
+ lm_off += meta->arg2.reg.var_off.value + meta->arg2.reg.off;
+ load_lm_ptr = meta->arg2.var_off || lm_off;
/* Set LM0 to start of key */
if (load_lm_ptr)
emit_csr_wr(nfp_prog, reg_b(2 * 2), NFP_CSR_ACT_LM_ADDR0);
+ if (meta->func_id == BPF_FUNC_map_update_elem)
+ emit_csr_wr(nfp_prog, reg_b(3 * 2), NFP_CSR_ACT_LM_ADDR2);
/* Load map ID into a register, it should actually fit as an immediate
* but in case it doesn't deal with it here, not in the delay slots.
*/
tid = ur_load_imm_any(nfp_prog, nfp_map->tid, imm_a(nfp_prog));
- emit_br_relo(nfp_prog, BR_UNC, BR_OFF_RELO + BPF_FUNC_map_lookup_elem,
+ emit_br_relo(nfp_prog, BR_UNC, BR_OFF_RELO + meta->func_id,
2, RELO_BR_HELPER);
ret_tgt = nfp_prog_current_offset(nfp_prog) + 2;
@@ -1388,6 +1406,18 @@ map_lookup_stack(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
return 0;
}
+static int
+nfp_get_prandom_u32(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
+{
+ __emit_csr_rd(nfp_prog, NFP_CSR_PSEUDO_RND_NUM);
+ /* CSR value is read in following immed[gpr, 0] */
+ emit_immed(nfp_prog, reg_both(0), 0,
+ IMMED_WIDTH_ALL, false, IMMED_SHIFT_0B);
+ emit_immed(nfp_prog, reg_both(1), 0,
+ IMMED_WIDTH_ALL, false, IMMED_SHIFT_0B);
+ return 0;
+}
+
/* --- Callbacks --- */
static int mov_reg64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
{
@@ -1838,6 +1868,128 @@ mem_ldx_emem(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,
tmp_reg, meta->insn.dst_reg * 2, size);
}
+static void
+mem_ldx_data_init_pktcache(struct nfp_prog *nfp_prog,
+ struct nfp_insn_meta *meta)
+{
+ s16 range_start = meta->pkt_cache.range_start;
+ s16 range_end = meta->pkt_cache.range_end;
+ swreg src_base, off;
+ u8 xfer_num, len;
+ bool indir;
+
+ off = re_load_imm_any(nfp_prog, range_start, imm_b(nfp_prog));
+ src_base = reg_a(meta->insn.src_reg * 2);
+ len = range_end - range_start;
+ xfer_num = round_up(len, REG_WIDTH) / REG_WIDTH;
+
+ indir = len > 8 * REG_WIDTH;
+ /* Setup PREV_ALU for indirect mode. */
+ if (indir)
+ wrp_immed(nfp_prog, reg_none(),
+ CMD_OVE_LEN | FIELD_PREP(CMD_OV_LEN, xfer_num - 1));
+
+ /* Cache memory into transfer-in registers. */
+ emit_cmd_any(nfp_prog, CMD_TGT_READ32_SWAP, CMD_MODE_32b, 0, src_base,
+ off, xfer_num - 1, CMD_CTX_SWAP, indir);
+}
+
+static int
+mem_ldx_data_from_pktcache_unaligned(struct nfp_prog *nfp_prog,
+ struct nfp_insn_meta *meta,
+ unsigned int size)
+{
+ s16 range_start = meta->pkt_cache.range_start;
+ s16 insn_off = meta->insn.off - range_start;
+ swreg dst_lo, dst_hi, src_lo, src_mid;
+ u8 dst_gpr = meta->insn.dst_reg * 2;
+ u8 len_lo = size, len_mid = 0;
+ u8 idx = insn_off / REG_WIDTH;
+ u8 off = insn_off % REG_WIDTH;
+
+ dst_hi = reg_both(dst_gpr + 1);
+ dst_lo = reg_both(dst_gpr);
+ src_lo = reg_xfer(idx);
+
+ /* The read length could involve as many as three registers. */
+ if (size > REG_WIDTH - off) {
+ /* Calculate the part in the second register. */
+ len_lo = REG_WIDTH - off;
+ len_mid = size - len_lo;
+
+ /* Calculate the part in the third register. */
+ if (size > 2 * REG_WIDTH - off)
+ len_mid = REG_WIDTH;
+ }
+
+ wrp_reg_subpart(nfp_prog, dst_lo, src_lo, len_lo, off);
+
+ if (!len_mid) {
+ wrp_immed(nfp_prog, dst_hi, 0);
+ return 0;
+ }
+
+ src_mid = reg_xfer(idx + 1);
+
+ if (size <= REG_WIDTH) {
+ wrp_reg_or_subpart(nfp_prog, dst_lo, src_mid, len_mid, len_lo);
+ wrp_immed(nfp_prog, dst_hi, 0);
+ } else {
+ swreg src_hi = reg_xfer(idx + 2);
+
+ wrp_reg_or_subpart(nfp_prog, dst_lo, src_mid,
+ REG_WIDTH - len_lo, len_lo);
+ wrp_reg_subpart(nfp_prog, dst_hi, src_mid, len_lo,
+ REG_WIDTH - len_lo);
+ wrp_reg_or_subpart(nfp_prog, dst_hi, src_hi, REG_WIDTH - len_lo,
+ len_lo);
+ }
+
+ return 0;
+}
+
+static int
+mem_ldx_data_from_pktcache_aligned(struct nfp_prog *nfp_prog,
+ struct nfp_insn_meta *meta,
+ unsigned int size)
+{
+ swreg dst_lo, dst_hi, src_lo;
+ u8 dst_gpr, idx;
+
+ idx = (meta->insn.off - meta->pkt_cache.range_start) / REG_WIDTH;
+ dst_gpr = meta->insn.dst_reg * 2;
+ dst_hi = reg_both(dst_gpr + 1);
+ dst_lo = reg_both(dst_gpr);
+ src_lo = reg_xfer(idx);
+
+ if (size < REG_WIDTH) {
+ wrp_reg_subpart(nfp_prog, dst_lo, src_lo, size, 0);
+ wrp_immed(nfp_prog, dst_hi, 0);
+ } else if (size == REG_WIDTH) {
+ wrp_mov(nfp_prog, dst_lo, src_lo);
+ wrp_immed(nfp_prog, dst_hi, 0);
+ } else {
+ swreg src_hi = reg_xfer(idx + 1);
+
+ wrp_mov(nfp_prog, dst_lo, src_lo);
+ wrp_mov(nfp_prog, dst_hi, src_hi);
+ }
+
+ return 0;
+}
+
+static int
+mem_ldx_data_from_pktcache(struct nfp_prog *nfp_prog,
+ struct nfp_insn_meta *meta, unsigned int size)
+{
+ u8 off = meta->insn.off - meta->pkt_cache.range_start;
+
+ if (IS_ALIGNED(off, REG_WIDTH))
+ return mem_ldx_data_from_pktcache_aligned(nfp_prog, meta, size);
+
+ return mem_ldx_data_from_pktcache_unaligned(nfp_prog, meta, size);
+}
+
static int
mem_ldx(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,
unsigned int size)
@@ -1852,8 +2004,16 @@ mem_ldx(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,
return mem_ldx_skb(nfp_prog, meta, size);
}
- if (meta->ptr.type == PTR_TO_PACKET)
- return mem_ldx_data(nfp_prog, meta, size);
+ if (meta->ptr.type == PTR_TO_PACKET) {
+ if (meta->pkt_cache.range_end) {
+ if (meta->pkt_cache.do_init)
+ mem_ldx_data_init_pktcache(nfp_prog, meta);
+
+ return mem_ldx_data_from_pktcache(nfp_prog, meta, size);
+ } else {
+ return mem_ldx_data(nfp_prog, meta, size);
+ }
+ }
if (meta->ptr.type == PTR_TO_STACK)
return mem_ldx_stack(nfp_prog, meta, size,
@@ -1982,6 +2142,111 @@ static int mem_stx8(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
return mem_stx(nfp_prog, meta, 8);
}
+static int
+mem_xadd(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, bool is64)
+{
+ u8 dst_gpr = meta->insn.dst_reg * 2;
+ u8 src_gpr = meta->insn.src_reg * 2;
+ unsigned int full_add, out;
+ swreg addra, addrb, off;
+
+ off = ur_load_imm_any(nfp_prog, meta->insn.off, imm_b(nfp_prog));
+
+ /* We can fit 16 bits into command immediate, if we know the immediate
+ * is guaranteed to either always or never fit into 16 bit we only
+ * generate code to handle that particular case, otherwise generate
+ * code for both.
+ */
+ out = nfp_prog_current_offset(nfp_prog);
+ full_add = nfp_prog_current_offset(nfp_prog);
+
+ if (meta->insn.off) {
+ out += 2;
+ full_add += 2;
+ }
+ if (meta->xadd_maybe_16bit) {
+ out += 3;
+ full_add += 3;
+ }
+ if (meta->xadd_over_16bit)
+ out += 2 + is64;
+ if (meta->xadd_maybe_16bit && meta->xadd_over_16bit) {
+ out += 5;
+ full_add += 5;
+ }
+
+ /* Generate the branch for choosing add_imm vs add */
+ if (meta->xadd_maybe_16bit && meta->xadd_over_16bit) {
+ swreg max_imm = imm_a(nfp_prog);
+
+ wrp_immed(nfp_prog, max_imm, 0xffff);
+ emit_alu(nfp_prog, reg_none(),
+ max_imm, ALU_OP_SUB, reg_b(src_gpr));
+ emit_alu(nfp_prog, reg_none(),
+ reg_imm(0), ALU_OP_SUB_C, reg_b(src_gpr + 1));
+ emit_br(nfp_prog, BR_BLO, full_add, meta->insn.off ? 2 : 0);
+ /* defer for add */
+ }
+
+ /* If insn has an offset add to the address */
+ if (!meta->insn.off) {
+ addra = reg_a(dst_gpr);
+ addrb = reg_b(dst_gpr + 1);
+ } else {
+ emit_alu(nfp_prog, imma_a(nfp_prog),
+ reg_a(dst_gpr), ALU_OP_ADD, off);
+ emit_alu(nfp_prog, imma_b(nfp_prog),
+ reg_a(dst_gpr + 1), ALU_OP_ADD_C, reg_imm(0));
+ addra = imma_a(nfp_prog);
+ addrb = imma_b(nfp_prog);
+ }
+
+ /* Generate the add_imm if 16 bits are possible */
+ if (meta->xadd_maybe_16bit) {
+ swreg prev_alu = imm_a(nfp_prog);
+
+ wrp_immed(nfp_prog, prev_alu,
+ FIELD_PREP(CMD_OVE_DATA, 2) |
+ CMD_OVE_LEN |
+ FIELD_PREP(CMD_OV_LEN, 0x8 | is64 << 2));
+ wrp_reg_or_subpart(nfp_prog, prev_alu, reg_b(src_gpr), 2, 2);
+ emit_cmd_indir(nfp_prog, CMD_TGT_ADD_IMM, CMD_MODE_40b_BA, 0,
+ addra, addrb, 0, CMD_CTX_NO_SWAP);
+
+ if (meta->xadd_over_16bit)
+ emit_br(nfp_prog, BR_UNC, out, 0);
+ }
+
+ if (!nfp_prog_confirm_current_offset(nfp_prog, full_add))
+ return -EINVAL;
+
+ /* Generate the add if 16 bits are not guaranteed */
+ if (meta->xadd_over_16bit) {
+ emit_cmd(nfp_prog, CMD_TGT_ADD, CMD_MODE_40b_BA, 0,
+ addra, addrb, is64 << 2,
+ is64 ? CMD_CTX_SWAP_DEFER2 : CMD_CTX_SWAP_DEFER1);
+
+ wrp_mov(nfp_prog, reg_xfer(0), reg_a(src_gpr));
+ if (is64)
+ wrp_mov(nfp_prog, reg_xfer(1), reg_a(src_gpr + 1));
+ }
+
+ if (!nfp_prog_confirm_current_offset(nfp_prog, out))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int mem_xadd4(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
+{
+ return mem_xadd(nfp_prog, meta, false);
+}
+
+static int mem_xadd8(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
+{
+ return mem_xadd(nfp_prog, meta, true);
+}
+
static int jump(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
{
emit_br(nfp_prog, BR_UNC, meta->insn.off, 0);
@@ -2183,7 +2448,11 @@ static int call(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
case BPF_FUNC_xdp_adjust_head:
return adjust_head(nfp_prog, meta);
case BPF_FUNC_map_lookup_elem:
- return map_lookup_stack(nfp_prog, meta);
+ case BPF_FUNC_map_update_elem:
+ case BPF_FUNC_map_delete_elem:
+ return map_call_stack_common(nfp_prog, meta);
+ case BPF_FUNC_get_prandom_u32:
+ return nfp_get_prandom_u32(nfp_prog, meta);
default:
WARN_ONCE(1, "verifier allowed unsupported function\n");
return -EOPNOTSUPP;
@@ -2243,6 +2512,8 @@ static const instr_cb_t instr_cb[256] = {
[BPF_STX | BPF_MEM | BPF_H] = mem_stx2,
[BPF_STX | BPF_MEM | BPF_W] = mem_stx4,
[BPF_STX | BPF_MEM | BPF_DW] = mem_stx8,
+ [BPF_STX | BPF_XADD | BPF_W] = mem_xadd4,
+ [BPF_STX | BPF_XADD | BPF_DW] = mem_xadd8,
[BPF_ST | BPF_MEM | BPF_B] = mem_st1,
[BPF_ST | BPF_MEM | BPF_H] = mem_st2,
[BPF_ST | BPF_MEM | BPF_W] = mem_st4,
@@ -2463,6 +2734,8 @@ static int nfp_translate(struct nfp_prog *nfp_prog)
err = cb(nfp_prog, meta);
if (err)
return err;
+ if (nfp_prog->error)
+ return nfp_prog->error;
nfp_prog->n_translated++;
}
@@ -2821,6 +3094,120 @@ static void nfp_bpf_opt_ldst_gather(struct nfp_prog *nfp_prog)
}
}
+static void nfp_bpf_opt_pkt_cache(struct nfp_prog *nfp_prog)
+{
+ struct nfp_insn_meta *meta, *range_node = NULL;
+ s16 range_start = 0, range_end = 0;
+ bool cache_avail = false;
+ struct bpf_insn *insn;
+ s32 range_ptr_off = 0;
+ u32 range_ptr_id = 0;
+
+ list_for_each_entry(meta, &nfp_prog->insns, l) {
+ if (meta->flags & FLAG_INSN_IS_JUMP_DST)
+ cache_avail = false;
+
+ if (meta->skip)
+ continue;
+
+ insn = &meta->insn;
+
+ if (is_mbpf_store_pkt(meta) ||
+ insn->code == (BPF_JMP | BPF_CALL) ||
+ is_mbpf_classic_store_pkt(meta) ||
+ is_mbpf_classic_load(meta)) {
+ cache_avail = false;
+ continue;
+ }
+
+ if (!is_mbpf_load(meta))
+ continue;
+
+ if (meta->ptr.type != PTR_TO_PACKET || meta->ldst_gather_len) {
+ cache_avail = false;
+ continue;
+ }
+
+ if (!cache_avail) {
+ cache_avail = true;
+ if (range_node)
+ goto end_current_then_start_new;
+ goto start_new;
+ }
+
+ /* Check ID to make sure two reads share the same
+ * variable offset against PTR_TO_PACKET, and check OFF
+ * to make sure they also share the same constant
+ * offset.
+ *
+ * OFFs don't really need to be the same, because they
+ * are the constant offsets against PTR_TO_PACKET, so
+ * for different OFFs, we could canonicalize them to
+ * offsets against original packet pointer. We don't
+ * support this.
+ */
+ if (meta->ptr.id == range_ptr_id &&
+ meta->ptr.off == range_ptr_off) {
+ s16 new_start = range_start;
+ s16 end, off = insn->off;
+ s16 new_end = range_end;
+ bool changed = false;
+
+ if (off < range_start) {
+ new_start = off;
+ changed = true;
+ }
+
+ end = off + BPF_LDST_BYTES(insn);
+ if (end > range_end) {
+ new_end = end;
+ changed = true;
+ }
+
+ if (!changed)
+ continue;
+
+ if (new_end - new_start <= 64) {
+ /* Install new range. */
+ range_start = new_start;
+ range_end = new_end;
+ continue;
+ }
+ }
+
+end_current_then_start_new:
+ range_node->pkt_cache.range_start = range_start;
+ range_node->pkt_cache.range_end = range_end;
+start_new:
+ range_node = meta;
+ range_node->pkt_cache.do_init = true;
+ range_ptr_id = range_node->ptr.id;
+ range_ptr_off = range_node->ptr.off;
+ range_start = insn->off;
+ range_end = insn->off + BPF_LDST_BYTES(insn);
+ }
+
+ if (range_node) {
+ range_node->pkt_cache.range_start = range_start;
+ range_node->pkt_cache.range_end = range_end;
+ }
+
+ list_for_each_entry(meta, &nfp_prog->insns, l) {
+ if (meta->skip)
+ continue;
+
+ if (is_mbpf_load_pkt(meta) && !meta->ldst_gather_len) {
+ if (meta->pkt_cache.do_init) {
+ range_start = meta->pkt_cache.range_start;
+ range_end = meta->pkt_cache.range_end;
+ } else {
+ meta->pkt_cache.range_start = range_start;
+ meta->pkt_cache.range_end = range_end;
+ }
+ }
+ }
+}
+
static int nfp_bpf_optimize(struct nfp_prog *nfp_prog)
{
nfp_bpf_opt_reg_init(nfp_prog);
@@ -2828,6 +3215,7 @@ static int nfp_bpf_optimize(struct nfp_prog *nfp_prog)
nfp_bpf_opt_ld_mask(nfp_prog);
nfp_bpf_opt_ld_shift(nfp_prog);
nfp_bpf_opt_ldst_gather(nfp_prog);
+ nfp_bpf_opt_pkt_cache(nfp_prog);
return 0;
}
@@ -2952,6 +3340,12 @@ void *nfp_bpf_relo_for_vnic(struct nfp_prog *nfp_prog, struct nfp_bpf_vnic *bv)
case BPF_FUNC_map_lookup_elem:
val = nfp_prog->bpf->helpers.map_lookup;
break;
+ case BPF_FUNC_map_update_elem:
+ val = nfp_prog->bpf->helpers.map_update;
+ break;
+ case BPF_FUNC_map_delete_elem:
+ val = nfp_prog->bpf->helpers.map_delete;
+ break;
default:
pr_err("relocation of unknown helper %d\n",
val);
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/main.c b/drivers/net/ethernet/netronome/nfp/bpf/main.c
index 34e98aa6b956..1dc424685f4e 100644
--- a/drivers/net/ethernet/netronome/nfp/bpf/main.c
+++ b/drivers/net/ethernet/netronome/nfp/bpf/main.c
@@ -221,7 +221,7 @@ static int nfp_bpf_setup_tc(struct nfp_app *app, struct net_device *netdev,
}
static int
-nfp_bpf_change_mtu(struct nfp_app *app, struct net_device *netdev, int new_mtu)
+nfp_bpf_check_mtu(struct nfp_app *app, struct net_device *netdev, int new_mtu)
{
struct nfp_net *nn = netdev_priv(netdev);
unsigned int max_mtu;
@@ -284,6 +284,12 @@ nfp_bpf_parse_cap_func(struct nfp_app_bpf *bpf, void __iomem *value, u32 length)
case BPF_FUNC_map_lookup_elem:
bpf->helpers.map_lookup = readl(&cap->func_addr);
break;
+ case BPF_FUNC_map_update_elem:
+ bpf->helpers.map_update = readl(&cap->func_addr);
+ break;
+ case BPF_FUNC_map_delete_elem:
+ bpf->helpers.map_delete = readl(&cap->func_addr);
+ break;
}
return 0;
@@ -309,6 +315,14 @@ nfp_bpf_parse_cap_maps(struct nfp_app_bpf *bpf, void __iomem *value, u32 length)
return 0;
}
+static int
+nfp_bpf_parse_cap_random(struct nfp_app_bpf *bpf, void __iomem *value,
+ u32 length)
+{
+ bpf->pseudo_random = true;
+ return 0;
+}
+
static int nfp_bpf_parse_capabilities(struct nfp_app *app)
{
struct nfp_cpp *cpp = app->pf->cpp;
@@ -347,6 +361,10 @@ static int nfp_bpf_parse_capabilities(struct nfp_app *app)
if (nfp_bpf_parse_cap_maps(app->priv, value, length))
goto err_release_free;
break;
+ case NFP_BPF_CAP_TYPE_RANDOM:
+ if (nfp_bpf_parse_cap_random(app->priv, value, length))
+ goto err_release_free;
+ break;
default:
nfp_dbg(cpp, "unknown BPF capability: %d\n", type);
break;
@@ -413,7 +431,7 @@ const struct nfp_app_type app_bpf = {
.init = nfp_bpf_init,
.clean = nfp_bpf_clean,
- .change_mtu = nfp_bpf_change_mtu,
+ .check_mtu = nfp_bpf_check_mtu,
.extra_cap = nfp_bpf_extra_cap,
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/main.h b/drivers/net/ethernet/netronome/nfp/bpf/main.h
index 054df3dc0698..4981c8944ca3 100644
--- a/drivers/net/ethernet/netronome/nfp/bpf/main.h
+++ b/drivers/net/ethernet/netronome/nfp/bpf/main.h
@@ -72,6 +72,7 @@ enum nfp_relo_type {
#define BR_OFF_RELO 15000
enum static_regs {
+ STATIC_REG_IMMA = 20, /* Bank AB */
STATIC_REG_IMM = 21, /* Bank AB */
STATIC_REG_STACK = 22, /* Bank A */
STATIC_REG_PKT_LEN = 22, /* Bank B */
@@ -91,6 +92,8 @@ enum pkt_vec {
#define pptr_reg(np) pv_ctm_ptr(np)
#define imm_a(np) reg_a(STATIC_REG_IMM)
#define imm_b(np) reg_b(STATIC_REG_IMM)
+#define imma_a(np) reg_a(STATIC_REG_IMMA)
+#define imma_b(np) reg_b(STATIC_REG_IMMA)
#define imm_both(np) reg_both(STATIC_REG_IMM)
#define NFP_BPF_ABI_FLAGS reg_imm(0)
@@ -128,6 +131,10 @@ enum pkt_vec {
*
* @helpers: helper addressess for various calls
* @helpers.map_lookup: map lookup helper address
+ * @helpers.map_update: map update helper address
+ * @helpers.map_delete: map delete helper address
+ *
+ * @pseudo_random: FW initialized the pseudo-random machinery (CSRs)
*/
struct nfp_app_bpf {
struct nfp_app *app;
@@ -162,7 +169,18 @@ struct nfp_app_bpf {
struct {
u32 map_lookup;
+ u32 map_update;
+ u32 map_delete;
} helpers;
+
+ bool pseudo_random;
+};
+
+enum nfp_bpf_map_use {
+ NFP_MAP_UNUSED = 0,
+ NFP_MAP_USE_READ,
+ NFP_MAP_USE_WRITE,
+ NFP_MAP_USE_ATOMIC_CNT,
};
/**
@@ -171,12 +189,14 @@ struct nfp_app_bpf {
* @bpf: back pointer to bpf app private structure
* @tid: table id identifying map on datapath
* @l: link on the nfp_app_bpf->map_list list
+ * @use_map: map of how the value is used (in 4B chunks)
*/
struct nfp_bpf_map {
struct bpf_offloaded_map *offmap;
struct nfp_app_bpf *bpf;
u32 tid;
struct list_head l;
+ enum nfp_bpf_map_use use_map[];
};
struct nfp_prog;
@@ -190,6 +210,16 @@ typedef int (*instr_cb_t)(struct nfp_prog *, struct nfp_insn_meta *);
#define nfp_meta_next(meta) list_next_entry(meta, l)
#define nfp_meta_prev(meta) list_prev_entry(meta, l)
+/**
+ * struct nfp_bpf_reg_state - register state for calls
+ * @reg: BPF register state from latest path
+ * @var_off: for stack arg - changes stack offset on different paths
+ */
+struct nfp_bpf_reg_state {
+ struct bpf_reg_state reg;
+ bool var_off;
+};
+
#define FLAG_INSN_IS_JUMP_DST BIT(0)
/**
@@ -199,11 +229,16 @@ typedef int (*instr_cb_t)(struct nfp_prog *, struct nfp_insn_meta *);
* @ldst_gather_len: memcpy length gathered from load/store sequence
* @paired_st: the paired store insn at the head of the sequence
* @ptr_not_const: pointer is not always constant
+ * @pkt_cache: packet data cache information
+ * @pkt_cache.range_start: start offset for associated packet data cache
+ * @pkt_cache.range_end: end offset for associated packet data cache
+ * @pkt_cache.do_init: this read needs to initialize packet data cache
+ * @xadd_over_16bit: 16bit immediate is not guaranteed
+ * @xadd_maybe_16bit: 16bit immediate is possible
* @jmp_dst: destination info for jump instructions
* @func_id: function id for call instructions
* @arg1: arg1 for call instructions
* @arg2: arg2 for call instructions
- * @arg2_var_off: arg2 changes stack offset on different paths
* @off: index of first generated machine instruction (in nfp_prog.prog)
* @n: eBPF instruction number
* @flags: eBPF instruction extra optimization flags
@@ -214,18 +249,27 @@ typedef int (*instr_cb_t)(struct nfp_prog *, struct nfp_insn_meta *);
struct nfp_insn_meta {
struct bpf_insn insn;
union {
+ /* pointer ops (ld/st/xadd) */
struct {
struct bpf_reg_state ptr;
struct bpf_insn *paired_st;
s16 ldst_gather_len;
bool ptr_not_const;
+ struct {
+ s16 range_start;
+ s16 range_end;
+ bool do_init;
+ } pkt_cache;
+ bool xadd_over_16bit;
+ bool xadd_maybe_16bit;
};
+ /* jump */
struct nfp_insn_meta *jmp_dst;
+ /* function calls */
struct {
u32 func_id;
struct bpf_reg_state arg1;
- struct bpf_reg_state arg2;
- bool arg2_var_off;
+ struct nfp_bpf_reg_state arg2;
};
};
unsigned int off;
@@ -269,6 +313,41 @@ static inline bool is_mbpf_store(const struct nfp_insn_meta *meta)
return (meta->insn.code & ~BPF_SIZE_MASK) == (BPF_STX | BPF_MEM);
}
+static inline bool is_mbpf_load_pkt(const struct nfp_insn_meta *meta)
+{
+ return is_mbpf_load(meta) && meta->ptr.type == PTR_TO_PACKET;
+}
+
+static inline bool is_mbpf_store_pkt(const struct nfp_insn_meta *meta)
+{
+ return is_mbpf_store(meta) && meta->ptr.type == PTR_TO_PACKET;
+}
+
+static inline bool is_mbpf_classic_load(const struct nfp_insn_meta *meta)
+{
+ u8 code = meta->insn.code;
+
+ return BPF_CLASS(code) == BPF_LD &&
+ (BPF_MODE(code) == BPF_ABS || BPF_MODE(code) == BPF_IND);
+}
+
+static inline bool is_mbpf_classic_store(const struct nfp_insn_meta *meta)
+{
+ u8 code = meta->insn.code;
+
+ return BPF_CLASS(code) == BPF_ST && BPF_MODE(code) == BPF_MEM;
+}
+
+static inline bool is_mbpf_classic_store_pkt(const struct nfp_insn_meta *meta)
+{
+ return is_mbpf_classic_store(meta) && meta->ptr.type == PTR_TO_PACKET;
+}
+
+static inline bool is_mbpf_xadd(const struct nfp_insn_meta *meta)
+{
+ return (meta->insn.code & ~BPF_SIZE_MASK) == (BPF_STX | BPF_XADD);
+}
+
/**
* struct nfp_prog - nfp BPF program
* @bpf: backpointer to the bpf app priv structure
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/offload.c b/drivers/net/ethernet/netronome/nfp/bpf/offload.c
index 0a7732385469..42d98792bd25 100644
--- a/drivers/net/ethernet/netronome/nfp/bpf/offload.c
+++ b/drivers/net/ethernet/netronome/nfp/bpf/offload.c
@@ -164,6 +164,41 @@ static int nfp_bpf_destroy(struct nfp_net *nn, struct bpf_prog *prog)
return 0;
}
+/* Atomic engine requires values to be in big endian, we need to byte swap
+ * the value words used with xadd.
+ */
+static void nfp_map_bpf_byte_swap(struct nfp_bpf_map *nfp_map, void *value)
+{
+ u32 *word = value;
+ unsigned int i;
+
+ for (i = 0; i < DIV_ROUND_UP(nfp_map->offmap->map.value_size, 4); i++)
+ if (nfp_map->use_map[i] == NFP_MAP_USE_ATOMIC_CNT)
+ word[i] = (__force u32)cpu_to_be32(word[i]);
+}
+
+static int
+nfp_bpf_map_lookup_entry(struct bpf_offloaded_map *offmap,
+ void *key, void *value)
+{
+ int err;
+
+ err = nfp_bpf_ctrl_lookup_entry(offmap, key, value);
+ if (err)
+ return err;
+
+ nfp_map_bpf_byte_swap(offmap->dev_priv, value);
+ return 0;
+}
+
+static int
+nfp_bpf_map_update_entry(struct bpf_offloaded_map *offmap,
+ void *key, void *value, u64 flags)
+{
+ nfp_map_bpf_byte_swap(offmap->dev_priv, value);
+ return nfp_bpf_ctrl_update_entry(offmap, key, value, flags);
+}
+
static int
nfp_bpf_map_get_next_key(struct bpf_offloaded_map *offmap,
void *key, void *next_key)
@@ -183,8 +218,8 @@ nfp_bpf_map_delete_elem(struct bpf_offloaded_map *offmap, void *key)
static const struct bpf_map_dev_ops nfp_bpf_map_ops = {
.map_get_next_key = nfp_bpf_map_get_next_key,
- .map_lookup_elem = nfp_bpf_ctrl_lookup_entry,
- .map_update_elem = nfp_bpf_ctrl_update_entry,
+ .map_lookup_elem = nfp_bpf_map_lookup_entry,
+ .map_update_elem = nfp_bpf_map_update_entry,
.map_delete_elem = nfp_bpf_map_delete_elem,
};
@@ -192,6 +227,7 @@ static int
nfp_bpf_map_alloc(struct nfp_app_bpf *bpf, struct bpf_offloaded_map *offmap)
{
struct nfp_bpf_map *nfp_map;
+ unsigned int use_map_size;
long long int res;
if (!bpf->maps.types)
@@ -226,7 +262,10 @@ nfp_bpf_map_alloc(struct nfp_app_bpf *bpf, struct bpf_offloaded_map *offmap)
return -ENOMEM;
}
- nfp_map = kzalloc(sizeof(*nfp_map), GFP_USER);
+ use_map_size = DIV_ROUND_UP(offmap->map.value_size, 4) *
+ FIELD_SIZEOF(struct nfp_bpf_map, use_map[0]);
+
+ nfp_map = kzalloc(sizeof(*nfp_map) + use_map_size, GFP_USER);
if (!nfp_map)
return -ENOMEM;
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/verifier.c b/drivers/net/ethernet/netronome/nfp/bpf/verifier.c
index 479f602887e9..06ad53ce4ad9 100644
--- a/drivers/net/ethernet/netronome/nfp/bpf/verifier.c
+++ b/drivers/net/ethernet/netronome/nfp/bpf/verifier.c
@@ -97,7 +97,7 @@ nfp_record_adjust_head(struct nfp_app_bpf *bpf, struct nfp_prog *nfp_prog,
if (nfp_prog->adjust_head_location != meta->n)
goto exit_set_location;
- if (meta->arg2.var_off.value != imm)
+ if (meta->arg2.reg.var_off.value != imm)
goto exit_set_location;
}
@@ -107,14 +107,69 @@ exit_set_location:
}
static int
+nfp_bpf_stack_arg_ok(const char *fname, struct bpf_verifier_env *env,
+ const struct bpf_reg_state *reg,
+ struct nfp_bpf_reg_state *old_arg)
+{
+ s64 off, old_off;
+
+ if (reg->type != PTR_TO_STACK) {
+ pr_vlog(env, "%s: unsupported ptr type %d\n",
+ fname, reg->type);
+ return false;
+ }
+ if (!tnum_is_const(reg->var_off)) {
+ pr_vlog(env, "%s: variable pointer\n", fname);
+ return false;
+ }
+
+ off = reg->var_off.value + reg->off;
+ if (-off % 4) {
+ pr_vlog(env, "%s: unaligned stack pointer %lld\n", fname, -off);
+ return false;
+ }
+
+ /* Rest of the checks is only if we re-parse the same insn */
+ if (!old_arg)
+ return true;
+
+ old_off = old_arg->reg.var_off.value + old_arg->reg.off;
+ old_arg->var_off |= off != old_off;
+
+ return true;
+}
+
+static bool
+nfp_bpf_map_call_ok(const char *fname, struct bpf_verifier_env *env,
+ struct nfp_insn_meta *meta,
+ u32 helper_tgt, const struct bpf_reg_state *reg1)
+{
+ if (!helper_tgt) {
+ pr_vlog(env, "%s: not supported by FW\n", fname);
+ return false;
+ }
+
+ /* Rest of the checks is only if we re-parse the same insn */
+ if (!meta->func_id)
+ return true;
+
+ if (meta->arg1.map_ptr != reg1->map_ptr) {
+ pr_vlog(env, "%s: called for different map\n", fname);
+ return false;
+ }
+
+ return true;
+}
+
+static int
nfp_bpf_check_call(struct nfp_prog *nfp_prog, struct bpf_verifier_env *env,
struct nfp_insn_meta *meta)
{
const struct bpf_reg_state *reg1 = cur_regs(env) + BPF_REG_1;
const struct bpf_reg_state *reg2 = cur_regs(env) + BPF_REG_2;
+ const struct bpf_reg_state *reg3 = cur_regs(env) + BPF_REG_3;
struct nfp_app_bpf *bpf = nfp_prog->bpf;
u32 func_id = meta->insn.imm;
- s64 off, old_off;
switch (func_id) {
case BPF_FUNC_xdp_adjust_head:
@@ -131,41 +186,36 @@ nfp_bpf_check_call(struct nfp_prog *nfp_prog, struct bpf_verifier_env *env,
break;
case BPF_FUNC_map_lookup_elem:
- if (!bpf->helpers.map_lookup) {
- pr_vlog(env, "map_lookup: not supported by FW\n");
+ if (!nfp_bpf_map_call_ok("map_lookup", env, meta,
+ bpf->helpers.map_lookup, reg1) ||
+ !nfp_bpf_stack_arg_ok("map_lookup", env, reg2,
+ meta->func_id ? &meta->arg2 : NULL))
return -EOPNOTSUPP;
- }
- if (reg2->type != PTR_TO_STACK) {
- pr_vlog(env,
- "map_lookup: unsupported key ptr type %d\n",
- reg2->type);
- return -EOPNOTSUPP;
- }
- if (!tnum_is_const(reg2->var_off)) {
- pr_vlog(env, "map_lookup: variable key pointer\n");
+ break;
+
+ case BPF_FUNC_map_update_elem:
+ if (!nfp_bpf_map_call_ok("map_update", env, meta,
+ bpf->helpers.map_update, reg1) ||
+ !nfp_bpf_stack_arg_ok("map_update", env, reg2,
+ meta->func_id ? &meta->arg2 : NULL) ||
+ !nfp_bpf_stack_arg_ok("map_update", env, reg3, NULL))
return -EOPNOTSUPP;
- }
+ break;
- off = reg2->var_off.value + reg2->off;
- if (-off % 4) {
- pr_vlog(env,
- "map_lookup: unaligned stack pointer %lld\n",
- -off);
+ case BPF_FUNC_map_delete_elem:
+ if (!nfp_bpf_map_call_ok("map_delete", env, meta,
+ bpf->helpers.map_delete, reg1) ||
+ !nfp_bpf_stack_arg_ok("map_delete", env, reg2,
+ meta->func_id ? &meta->arg2 : NULL))
return -EOPNOTSUPP;
- }
+ break;
- /* Rest of the checks is only if we re-parse the same insn */
- if (!meta->func_id)
+ case BPF_FUNC_get_prandom_u32:
+ if (bpf->pseudo_random)
break;
+ pr_vlog(env, "bpf_get_prandom_u32(): FW doesn't support random number generation\n");
+ return -EOPNOTSUPP;
- old_off = meta->arg2.var_off.value + meta->arg2.off;
- meta->arg2_var_off |= off != old_off;
-
- if (meta->arg1.map_ptr != reg1->map_ptr) {
- pr_vlog(env, "map_lookup: called for different map\n");
- return -EOPNOTSUPP;
- }
- break;
default:
pr_vlog(env, "unsupported function id: %d\n", func_id);
return -EOPNOTSUPP;
@@ -173,7 +223,7 @@ nfp_bpf_check_call(struct nfp_prog *nfp_prog, struct bpf_verifier_env *env,
meta->func_id = func_id;
meta->arg1 = *reg1;
- meta->arg2 = *reg2;
+ meta->arg2.reg = *reg2;
return 0;
}
@@ -242,6 +292,72 @@ nfp_bpf_check_stack_access(struct nfp_prog *nfp_prog,
return -EINVAL;
}
+static const char *nfp_bpf_map_use_name(enum nfp_bpf_map_use use)
+{
+ static const char * const names[] = {
+ [NFP_MAP_UNUSED] = "unused",
+ [NFP_MAP_USE_READ] = "read",
+ [NFP_MAP_USE_WRITE] = "write",
+ [NFP_MAP_USE_ATOMIC_CNT] = "atomic",
+ };
+
+ if (use >= ARRAY_SIZE(names) || !names[use])
+ return "unknown";
+ return names[use];
+}
+
+static int
+nfp_bpf_map_mark_used_one(struct bpf_verifier_env *env,
+ struct nfp_bpf_map *nfp_map,
+ unsigned int off, enum nfp_bpf_map_use use)
+{
+ if (nfp_map->use_map[off / 4] != NFP_MAP_UNUSED &&
+ nfp_map->use_map[off / 4] != use) {
+ pr_vlog(env, "map value use type conflict %s vs %s off: %u\n",
+ nfp_bpf_map_use_name(nfp_map->use_map[off / 4]),
+ nfp_bpf_map_use_name(use), off);
+ return -EOPNOTSUPP;
+ }
+
+ nfp_map->use_map[off / 4] = use;
+
+ return 0;
+}
+
+static int
+nfp_bpf_map_mark_used(struct bpf_verifier_env *env, struct nfp_insn_meta *meta,
+ const struct bpf_reg_state *reg,
+ enum nfp_bpf_map_use use)
+{
+ struct bpf_offloaded_map *offmap;
+ struct nfp_bpf_map *nfp_map;
+ unsigned int size, off;
+ int i, err;
+
+ if (!tnum_is_const(reg->var_off)) {
+ pr_vlog(env, "map value offset is variable\n");
+ return -EOPNOTSUPP;
+ }
+
+ off = reg->var_off.value + meta->insn.off + reg->off;
+ size = BPF_LDST_BYTES(&meta->insn);
+ offmap = map_to_offmap(reg->map_ptr);
+ nfp_map = offmap->dev_priv;
+
+ if (off + size > offmap->map.value_size) {
+ pr_vlog(env, "map value access out-of-bounds\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < size; i += 4 - (off + i) % 4) {
+ err = nfp_bpf_map_mark_used_one(env, nfp_map, off + i, use);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
static int
nfp_bpf_check_ptr(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,
struct bpf_verifier_env *env, u8 reg_no)
@@ -264,10 +380,22 @@ nfp_bpf_check_ptr(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,
}
if (reg->type == PTR_TO_MAP_VALUE) {
+ if (is_mbpf_load(meta)) {
+ err = nfp_bpf_map_mark_used(env, meta, reg,
+ NFP_MAP_USE_READ);
+ if (err)
+ return err;
+ }
if (is_mbpf_store(meta)) {
pr_vlog(env, "map writes not supported\n");
return -EOPNOTSUPP;
}
+ if (is_mbpf_xadd(meta)) {
+ err = nfp_bpf_map_mark_used(env, meta, reg,
+ NFP_MAP_USE_ATOMIC_CNT);
+ if (err)
+ return err;
+ }
}
if (meta->ptr.type != NOT_INIT && meta->ptr.type != reg->type) {
@@ -282,6 +410,31 @@ nfp_bpf_check_ptr(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,
}
static int
+nfp_bpf_check_xadd(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,
+ struct bpf_verifier_env *env)
+{
+ const struct bpf_reg_state *sreg = cur_regs(env) + meta->insn.src_reg;
+ const struct bpf_reg_state *dreg = cur_regs(env) + meta->insn.dst_reg;
+
+ if (dreg->type != PTR_TO_MAP_VALUE) {
+ pr_vlog(env, "atomic add not to a map value pointer: %d\n",
+ dreg->type);
+ return -EOPNOTSUPP;
+ }
+ if (sreg->type != SCALAR_VALUE) {
+ pr_vlog(env, "atomic add not of a scalar: %d\n", sreg->type);
+ return -EOPNOTSUPP;
+ }
+
+ meta->xadd_over_16bit |=
+ sreg->var_off.value > 0xffff || sreg->var_off.mask > 0xffff;
+ meta->xadd_maybe_16bit |=
+ (sreg->var_off.value & ~sreg->var_off.mask) <= 0xffff;
+
+ return nfp_bpf_check_ptr(nfp_prog, meta, env, meta->insn.dst_reg);
+}
+
+static int
nfp_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn_idx)
{
struct nfp_prog *nfp_prog = env->prog->aux->offload->dev_priv;
@@ -313,6 +466,8 @@ nfp_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn_idx)
if (is_mbpf_store(meta))
return nfp_bpf_check_ptr(nfp_prog, meta, env,
meta->insn.dst_reg);
+ if (is_mbpf_xadd(meta))
+ return nfp_bpf_check_xadd(nfp_prog, meta, env);
return 0;
}
diff --git a/drivers/net/ethernet/netronome/nfp/flower/Makefile b/drivers/net/ethernet/netronome/nfp/flower/Makefile
new file mode 100644
index 000000000000..805fa28f391a
--- /dev/null
+++ b/drivers/net/ethernet/netronome/nfp/flower/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+# kbuild requires Makefile in a directory to build individual objects
diff --git a/drivers/net/ethernet/netronome/nfp/flower/cmsg.c b/drivers/net/ethernet/netronome/nfp/flower/cmsg.c
index baaea6f1a9d8..577659f332e4 100644
--- a/drivers/net/ethernet/netronome/nfp/flower/cmsg.c
+++ b/drivers/net/ethernet/netronome/nfp/flower/cmsg.c
@@ -104,7 +104,8 @@ nfp_flower_cmsg_mac_repr_add(struct sk_buff *skb, unsigned int idx,
msg->ports[idx].phys_port = phys_port;
}
-int nfp_flower_cmsg_portmod(struct nfp_repr *repr, bool carrier_ok)
+int nfp_flower_cmsg_portmod(struct nfp_repr *repr, bool carrier_ok,
+ unsigned int mtu, bool mtu_only)
{
struct nfp_flower_cmsg_portmod *msg;
struct sk_buff *skb;
@@ -118,7 +119,11 @@ int nfp_flower_cmsg_portmod(struct nfp_repr *repr, bool carrier_ok)
msg->portnum = cpu_to_be32(repr->dst->u.port_info.port_id);
msg->reserved = 0;
msg->info = carrier_ok;
- msg->mtu = cpu_to_be16(repr->netdev->mtu);
+
+ if (mtu_only)
+ msg->info |= NFP_FLOWER_CMSG_PORTMOD_MTU_CHANGE_ONLY;
+
+ msg->mtu = cpu_to_be16(mtu);
nfp_ctrl_tx(repr->app->ctrl, skb);
@@ -146,6 +151,34 @@ int nfp_flower_cmsg_portreify(struct nfp_repr *repr, bool exists)
return 0;
}
+static bool
+nfp_flower_process_mtu_ack(struct nfp_app *app, struct sk_buff *skb)
+{
+ struct nfp_flower_priv *app_priv = app->priv;
+ struct nfp_flower_cmsg_portmod *msg;
+
+ msg = nfp_flower_cmsg_get_data(skb);
+
+ if (!(msg->info & NFP_FLOWER_CMSG_PORTMOD_MTU_CHANGE_ONLY))
+ return false;
+
+ spin_lock_bh(&app_priv->mtu_conf.lock);
+ if (!app_priv->mtu_conf.requested_val ||
+ app_priv->mtu_conf.portnum != be32_to_cpu(msg->portnum) ||
+ be16_to_cpu(msg->mtu) != app_priv->mtu_conf.requested_val) {
+ /* Not an ack for requested MTU change. */
+ spin_unlock_bh(&app_priv->mtu_conf.lock);
+ return false;
+ }
+
+ app_priv->mtu_conf.ack = true;
+ app_priv->mtu_conf.requested_val = 0;
+ wake_up(&app_priv->mtu_conf.wait_q);
+ spin_unlock_bh(&app_priv->mtu_conf.lock);
+
+ return true;
+}
+
static void
nfp_flower_cmsg_portmod_rx(struct nfp_app *app, struct sk_buff *skb)
{
@@ -225,9 +258,6 @@ nfp_flower_cmsg_process_one_rx(struct nfp_app *app, struct sk_buff *skb)
case NFP_FLOWER_CMSG_TYPE_ACTIVE_TUNS:
nfp_tunnel_keep_alive(app, skb);
break;
- case NFP_FLOWER_CMSG_TYPE_TUN_NEIGH:
- /* Acks from the NFP that the route is added - ignore. */
- break;
default:
nfp_flower_cmsg_warn(app, "Cannot handle invalid repr control type %u\n",
type);
@@ -242,18 +272,49 @@ out:
void nfp_flower_cmsg_process_rx(struct work_struct *work)
{
+ struct sk_buff_head cmsg_joined;
struct nfp_flower_priv *priv;
struct sk_buff *skb;
priv = container_of(work, struct nfp_flower_priv, cmsg_work);
+ skb_queue_head_init(&cmsg_joined);
+
+ spin_lock_bh(&priv->cmsg_skbs_high.lock);
+ skb_queue_splice_tail_init(&priv->cmsg_skbs_high, &cmsg_joined);
+ spin_unlock_bh(&priv->cmsg_skbs_high.lock);
- while ((skb = skb_dequeue(&priv->cmsg_skbs)))
+ spin_lock_bh(&priv->cmsg_skbs_low.lock);
+ skb_queue_splice_tail_init(&priv->cmsg_skbs_low, &cmsg_joined);
+ spin_unlock_bh(&priv->cmsg_skbs_low.lock);
+
+ while ((skb = __skb_dequeue(&cmsg_joined)))
nfp_flower_cmsg_process_one_rx(priv->app, skb);
}
-void nfp_flower_cmsg_rx(struct nfp_app *app, struct sk_buff *skb)
+static void
+nfp_flower_queue_ctl_msg(struct nfp_app *app, struct sk_buff *skb, int type)
{
struct nfp_flower_priv *priv = app->priv;
+ struct sk_buff_head *skb_head;
+
+ if (type == NFP_FLOWER_CMSG_TYPE_PORT_REIFY ||
+ type == NFP_FLOWER_CMSG_TYPE_PORT_MOD)
+ skb_head = &priv->cmsg_skbs_high;
+ else
+ skb_head = &priv->cmsg_skbs_low;
+
+ if (skb_queue_len(skb_head) >= NFP_FLOWER_WORKQ_MAX_SKBS) {
+ nfp_flower_cmsg_warn(app, "Dropping queued control messages\n");
+ dev_kfree_skb_any(skb);
+ return;
+ }
+
+ skb_queue_tail(skb_head, skb);
+ schedule_work(&priv->cmsg_work);
+}
+
+void nfp_flower_cmsg_rx(struct nfp_app *app, struct sk_buff *skb)
+{
struct nfp_flower_cmsg_hdr *cmsg_hdr;
cmsg_hdr = nfp_flower_cmsg_get_hdr(skb);
@@ -269,8 +330,14 @@ void nfp_flower_cmsg_rx(struct nfp_app *app, struct sk_buff *skb)
/* We need to deal with stats updates from HW asap */
nfp_flower_rx_flow_stats(app, skb);
dev_consume_skb_any(skb);
+ } else if (cmsg_hdr->type == NFP_FLOWER_CMSG_TYPE_PORT_MOD &&
+ nfp_flower_process_mtu_ack(app, skb)) {
+ /* Handle MTU acks outside wq to prevent RTNL conflict. */
+ dev_consume_skb_any(skb);
+ } else if (cmsg_hdr->type == NFP_FLOWER_CMSG_TYPE_TUN_NEIGH) {
+ /* Acks from the NFP that the route is added - ignore. */
+ dev_consume_skb_any(skb);
} else {
- skb_queue_tail(&priv->cmsg_skbs, skb);
- schedule_work(&priv->cmsg_work);
+ nfp_flower_queue_ctl_msg(app, skb, cmsg_hdr->type);
}
}
diff --git a/drivers/net/ethernet/netronome/nfp/flower/cmsg.h b/drivers/net/ethernet/netronome/nfp/flower/cmsg.h
index adfe474c2cf0..b6c0fd053a50 100644
--- a/drivers/net/ethernet/netronome/nfp/flower/cmsg.h
+++ b/drivers/net/ethernet/netronome/nfp/flower/cmsg.h
@@ -61,6 +61,16 @@
#define NFP_FLOWER_MASK_MPLS_BOS BIT(8)
#define NFP_FLOWER_MASK_MPLS_Q BIT(0)
+#define NFP_FL_IP_FRAG_FIRST BIT(7)
+#define NFP_FL_IP_FRAGMENTED BIT(6)
+
+/* Compressed HW representation of TCP Flags */
+#define NFP_FL_TCP_FLAG_URG BIT(4)
+#define NFP_FL_TCP_FLAG_PSH BIT(3)
+#define NFP_FL_TCP_FLAG_RST BIT(2)
+#define NFP_FL_TCP_FLAG_SYN BIT(1)
+#define NFP_FL_TCP_FLAG_FIN BIT(0)
+
#define NFP_FL_SC_ACT_DROP 0x80000000
#define NFP_FL_SC_ACT_USER 0x7D000000
#define NFP_FL_SC_ACT_POPV 0x6A000000
@@ -98,6 +108,8 @@
#define NFP_FL_IPV4_TUNNEL_TYPE GENMASK(7, 4)
#define NFP_FL_IPV4_PRE_TUN_INDEX GENMASK(2, 0)
+#define NFP_FLOWER_WORKQ_MAX_SKBS 30000
+
#define nfp_flower_cmsg_warn(app, fmt, args...) \
do { \
if (net_ratelimit()) \
@@ -253,11 +265,18 @@ struct nfp_flower_tp_ports {
__be16 port_dst;
};
+struct nfp_flower_ip_ext {
+ u8 tos;
+ u8 proto;
+ u8 ttl;
+ u8 flags;
+};
+
/* L3 IPv4 details (3W/12B)
* 3 2 1
* 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | DSCP |ECN| protocol | reserved |
+ * | DSCP |ECN| protocol | ttl | flags |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | ipv4_addr_src |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -265,10 +284,7 @@ struct nfp_flower_tp_ports {
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
struct nfp_flower_ipv4 {
- u8 tos;
- u8 proto;
- u8 ttl;
- u8 reserved;
+ struct nfp_flower_ip_ext ip_ext;
__be32 ipv4_src;
__be32 ipv4_dst;
};
@@ -277,7 +293,7 @@ struct nfp_flower_ipv4 {
* 3 2 1
* 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | DSCP |ECN| protocol | reserved |
+ * | DSCP |ECN| protocol | ttl | flags |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | ipv6_exthdr | res | ipv6_flow_label |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -299,10 +315,7 @@ struct nfp_flower_ipv4 {
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
struct nfp_flower_ipv6 {
- u8 tos;
- u8 proto;
- u8 ttl;
- u8 reserved;
+ struct nfp_flower_ip_ext ip_ext;
__be32 ipv6_flow_label_exthdr;
struct in6_addr ipv6_src;
struct in6_addr ipv6_dst;
@@ -386,6 +399,7 @@ struct nfp_flower_cmsg_portmod {
};
#define NFP_FLOWER_CMSG_PORTMOD_INFO_LINK BIT(0)
+#define NFP_FLOWER_CMSG_PORTMOD_MTU_CHANGE_ONLY BIT(1)
/* NFP_FLOWER_CMSG_TYPE_PORT_REIFY */
struct nfp_flower_cmsg_portreify {
@@ -453,7 +467,8 @@ void
nfp_flower_cmsg_mac_repr_add(struct sk_buff *skb, unsigned int idx,
unsigned int nbi, unsigned int nbi_port,
unsigned int phys_port);
-int nfp_flower_cmsg_portmod(struct nfp_repr *repr, bool carrier_ok);
+int nfp_flower_cmsg_portmod(struct nfp_repr *repr, bool carrier_ok,
+ unsigned int mtu, bool mtu_only);
int nfp_flower_cmsg_portreify(struct nfp_repr *repr, bool exists);
void nfp_flower_cmsg_process_rx(struct work_struct *work);
void nfp_flower_cmsg_rx(struct nfp_app *app, struct sk_buff *skb);
diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.c b/drivers/net/ethernet/netronome/nfp/flower/main.c
index 742d6f1575b5..ad02592a82b7 100644
--- a/drivers/net/ethernet/netronome/nfp/flower/main.c
+++ b/drivers/net/ethernet/netronome/nfp/flower/main.c
@@ -52,6 +52,8 @@
#define NFP_FLOWER_ALLOWED_VER 0x0001000000010000UL
+#define NFP_FLOWER_FRAME_HEADROOM 158
+
static const char *nfp_flower_extra_cap(struct nfp_app *app, struct nfp_net *nn)
{
return "FLOWER";
@@ -157,7 +159,7 @@ nfp_flower_repr_netdev_open(struct nfp_app *app, struct nfp_repr *repr)
{
int err;
- err = nfp_flower_cmsg_portmod(repr, true);
+ err = nfp_flower_cmsg_portmod(repr, true, repr->netdev->mtu, false);
if (err)
return err;
@@ -171,7 +173,7 @@ nfp_flower_repr_netdev_stop(struct nfp_app *app, struct nfp_repr *repr)
{
netif_tx_disable(repr->netdev);
- return nfp_flower_cmsg_portmod(repr, false);
+ return nfp_flower_cmsg_portmod(repr, false, repr->netdev->mtu, false);
}
static int
@@ -517,10 +519,14 @@ static int nfp_flower_init(struct nfp_app *app)
app->priv = app_priv;
app_priv->app = app;
- skb_queue_head_init(&app_priv->cmsg_skbs);
+ skb_queue_head_init(&app_priv->cmsg_skbs_high);
+ skb_queue_head_init(&app_priv->cmsg_skbs_low);
INIT_WORK(&app_priv->cmsg_work, nfp_flower_cmsg_process_rx);
init_waitqueue_head(&app_priv->reify_wait_queue);
+ init_waitqueue_head(&app_priv->mtu_conf.wait_q);
+ spin_lock_init(&app_priv->mtu_conf.lock);
+
err = nfp_flower_metadata_init(app);
if (err)
goto err_free_app_priv;
@@ -544,7 +550,8 @@ static void nfp_flower_clean(struct nfp_app *app)
{
struct nfp_flower_priv *app_priv = app->priv;
- skb_queue_purge(&app_priv->cmsg_skbs);
+ skb_queue_purge(&app_priv->cmsg_skbs_high);
+ skb_queue_purge(&app_priv->cmsg_skbs_low);
flush_work(&app_priv->cmsg_work);
nfp_flower_metadata_cleanup(app);
@@ -552,6 +559,81 @@ static void nfp_flower_clean(struct nfp_app *app)
app->priv = NULL;
}
+static int
+nfp_flower_check_mtu(struct nfp_app *app, struct net_device *netdev,
+ int new_mtu)
+{
+ /* The flower fw reserves NFP_FLOWER_FRAME_HEADROOM bytes of the
+ * supported max MTU to allow for appending tunnel headers. To prevent
+ * unexpected behaviour this needs to be accounted for.
+ */
+ if (new_mtu > netdev->max_mtu - NFP_FLOWER_FRAME_HEADROOM) {
+ nfp_err(app->cpp, "New MTU (%d) is not valid\n", new_mtu);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static bool nfp_flower_check_ack(struct nfp_flower_priv *app_priv)
+{
+ bool ret;
+
+ spin_lock_bh(&app_priv->mtu_conf.lock);
+ ret = app_priv->mtu_conf.ack;
+ spin_unlock_bh(&app_priv->mtu_conf.lock);
+
+ return ret;
+}
+
+static int
+nfp_flower_repr_change_mtu(struct nfp_app *app, struct net_device *netdev,
+ int new_mtu)
+{
+ struct nfp_flower_priv *app_priv = app->priv;
+ struct nfp_repr *repr = netdev_priv(netdev);
+ int err, ack;
+
+ /* Only need to config FW for physical port MTU change. */
+ if (repr->port->type != NFP_PORT_PHYS_PORT)
+ return 0;
+
+ if (!(app_priv->flower_ext_feats & NFP_FL_NBI_MTU_SETTING)) {
+ nfp_err(app->cpp, "Physical port MTU setting not supported\n");
+ return -EINVAL;
+ }
+
+ spin_lock_bh(&app_priv->mtu_conf.lock);
+ app_priv->mtu_conf.ack = false;
+ app_priv->mtu_conf.requested_val = new_mtu;
+ app_priv->mtu_conf.portnum = repr->dst->u.port_info.port_id;
+ spin_unlock_bh(&app_priv->mtu_conf.lock);
+
+ err = nfp_flower_cmsg_portmod(repr, netif_carrier_ok(netdev), new_mtu,
+ true);
+ if (err) {
+ spin_lock_bh(&app_priv->mtu_conf.lock);
+ app_priv->mtu_conf.requested_val = 0;
+ spin_unlock_bh(&app_priv->mtu_conf.lock);
+ return err;
+ }
+
+ /* Wait for fw to ack the change. */
+ ack = wait_event_timeout(app_priv->mtu_conf.wait_q,
+ nfp_flower_check_ack(app_priv),
+ msecs_to_jiffies(10));
+
+ if (!ack) {
+ spin_lock_bh(&app_priv->mtu_conf.lock);
+ app_priv->mtu_conf.requested_val = 0;
+ spin_unlock_bh(&app_priv->mtu_conf.lock);
+ nfp_warn(app->cpp, "MTU change not verified with fw\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
static int nfp_flower_start(struct nfp_app *app)
{
return nfp_tunnel_config_start(app);
@@ -574,6 +656,9 @@ const struct nfp_app_type app_flower = {
.init = nfp_flower_init,
.clean = nfp_flower_clean,
+ .check_mtu = nfp_flower_check_mtu,
+ .repr_change_mtu = nfp_flower_repr_change_mtu,
+
.vnic_alloc = nfp_flower_vnic_alloc,
.vnic_init = nfp_flower_vnic_init,
.vnic_clean = nfp_flower_vnic_clean,
diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.h b/drivers/net/ethernet/netronome/nfp/flower/main.h
index 332ff0fdc038..c67e1b54c614 100644
--- a/drivers/net/ethernet/netronome/nfp/flower/main.h
+++ b/drivers/net/ethernet/netronome/nfp/flower/main.h
@@ -41,6 +41,7 @@
#include <linux/time64.h>
#include <linux/types.h>
#include <net/pkt_cls.h>
+#include <net/tcp.h>
#include <linux/workqueue.h>
struct net_device;
@@ -64,6 +65,7 @@ struct nfp_app;
/* Extra features bitmap. */
#define NFP_FL_FEATS_GENEVE BIT(0)
+#define NFP_FL_NBI_MTU_SETTING BIT(1)
struct nfp_fl_mask_id {
struct circ_buf mask_id_free_list;
@@ -78,6 +80,22 @@ struct nfp_fl_stats_id {
};
/**
+ * struct nfp_mtu_conf - manage MTU setting
+ * @portnum: NFP port number of repr with requested MTU change
+ * @requested_val: MTU value requested for repr
+ * @ack: Received ack that MTU has been correctly set
+ * @wait_q: Wait queue for MTU acknowledgements
+ * @lock: Lock for setting/reading MTU variables
+ */
+struct nfp_mtu_conf {
+ u32 portnum;
+ unsigned int requested_val;
+ bool ack;
+ wait_queue_head_t wait_q;
+ spinlock_t lock;
+};
+
+/**
* struct nfp_flower_priv - Flower APP per-vNIC priv data
* @app: Back pointer to app
* @nn: Pointer to vNIC
@@ -89,7 +107,10 @@ struct nfp_fl_stats_id {
* @mask_table: Hash table used to store masks
* @flow_table: Hash table used to store flower rules
* @cmsg_work: Workqueue for control messages processing
- * @cmsg_skbs: List of skbs for control message processing
+ * @cmsg_skbs_high: List of higher priority skbs for control message
+ * processing
+ * @cmsg_skbs_low: List of lower priority skbs for control message
+ * processing
* @nfp_mac_off_list: List of MAC addresses to offload
* @nfp_mac_index_list: List of unique 8-bit indexes for non NFP netdevs
* @nfp_ipv4_off_list: List of IPv4 addresses to offload
@@ -105,6 +126,7 @@ struct nfp_fl_stats_id {
* @reify_replies: atomically stores the number of replies received
* from firmware for repr reify
* @reify_wait_queue: wait queue for repr reify response counting
+ * @mtu_conf: Configuration of repr MTU value
*/
struct nfp_flower_priv {
struct nfp_app *app;
@@ -117,7 +139,8 @@ struct nfp_flower_priv {
DECLARE_HASHTABLE(mask_table, NFP_FLOWER_MASK_HASH_BITS);
DECLARE_HASHTABLE(flow_table, NFP_FLOWER_HASH_BITS);
struct work_struct cmsg_work;
- struct sk_buff_head cmsg_skbs;
+ struct sk_buff_head cmsg_skbs_high;
+ struct sk_buff_head cmsg_skbs_low;
struct list_head nfp_mac_off_list;
struct list_head nfp_mac_index_list;
struct list_head nfp_ipv4_off_list;
@@ -132,6 +155,7 @@ struct nfp_flower_priv {
struct notifier_block nfp_tun_neigh_nb;
atomic_t reify_replies;
wait_queue_head_t reify_wait_queue;
+ struct nfp_mtu_conf mtu_conf;
};
struct nfp_fl_key_ls {
diff --git a/drivers/net/ethernet/netronome/nfp/flower/match.c b/drivers/net/ethernet/netronome/nfp/flower/match.c
index 37c2ecae2a7a..91935405f586 100644
--- a/drivers/net/ethernet/netronome/nfp/flower/match.c
+++ b/drivers/net/ethernet/netronome/nfp/flower/match.c
@@ -146,26 +146,15 @@ nfp_flower_compile_tport(struct nfp_flower_tp_ports *frame,
}
static void
-nfp_flower_compile_ipv4(struct nfp_flower_ipv4 *frame,
- struct tc_cls_flower_offload *flow,
- bool mask_version)
+nfp_flower_compile_ip_ext(struct nfp_flower_ip_ext *frame,
+ struct tc_cls_flower_offload *flow,
+ bool mask_version)
{
struct fl_flow_key *target = mask_version ? flow->mask : flow->key;
- struct flow_dissector_key_ipv4_addrs *addr;
- struct flow_dissector_key_basic *basic;
-
- memset(frame, 0, sizeof(struct nfp_flower_ipv4));
-
- if (dissector_uses_key(flow->dissector,
- FLOW_DISSECTOR_KEY_IPV4_ADDRS)) {
- addr = skb_flow_dissector_target(flow->dissector,
- FLOW_DISSECTOR_KEY_IPV4_ADDRS,
- target);
- frame->ipv4_src = addr->src;
- frame->ipv4_dst = addr->dst;
- }
if (dissector_uses_key(flow->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
+ struct flow_dissector_key_basic *basic;
+
basic = skb_flow_dissector_target(flow->dissector,
FLOW_DISSECTOR_KEY_BASIC,
target);
@@ -181,6 +170,60 @@ nfp_flower_compile_ipv4(struct nfp_flower_ipv4 *frame,
frame->tos = flow_ip->tos;
frame->ttl = flow_ip->ttl;
}
+
+ if (dissector_uses_key(flow->dissector, FLOW_DISSECTOR_KEY_TCP)) {
+ struct flow_dissector_key_tcp *tcp;
+ u32 tcp_flags;
+
+ tcp = skb_flow_dissector_target(flow->dissector,
+ FLOW_DISSECTOR_KEY_TCP, target);
+ tcp_flags = be16_to_cpu(tcp->flags);
+
+ if (tcp_flags & TCPHDR_FIN)
+ frame->flags |= NFP_FL_TCP_FLAG_FIN;
+ if (tcp_flags & TCPHDR_SYN)
+ frame->flags |= NFP_FL_TCP_FLAG_SYN;
+ if (tcp_flags & TCPHDR_RST)
+ frame->flags |= NFP_FL_TCP_FLAG_RST;
+ if (tcp_flags & TCPHDR_PSH)
+ frame->flags |= NFP_FL_TCP_FLAG_PSH;
+ if (tcp_flags & TCPHDR_URG)
+ frame->flags |= NFP_FL_TCP_FLAG_URG;
+ }
+
+ if (dissector_uses_key(flow->dissector, FLOW_DISSECTOR_KEY_CONTROL)) {
+ struct flow_dissector_key_control *key;
+
+ key = skb_flow_dissector_target(flow->dissector,
+ FLOW_DISSECTOR_KEY_CONTROL,
+ target);
+ if (key->flags & FLOW_DIS_IS_FRAGMENT)
+ frame->flags |= NFP_FL_IP_FRAGMENTED;
+ if (key->flags & FLOW_DIS_FIRST_FRAG)
+ frame->flags |= NFP_FL_IP_FRAG_FIRST;
+ }
+}
+
+static void
+nfp_flower_compile_ipv4(struct nfp_flower_ipv4 *frame,
+ struct tc_cls_flower_offload *flow,
+ bool mask_version)
+{
+ struct fl_flow_key *target = mask_version ? flow->mask : flow->key;
+ struct flow_dissector_key_ipv4_addrs *addr;
+
+ memset(frame, 0, sizeof(struct nfp_flower_ipv4));
+
+ if (dissector_uses_key(flow->dissector,
+ FLOW_DISSECTOR_KEY_IPV4_ADDRS)) {
+ addr = skb_flow_dissector_target(flow->dissector,
+ FLOW_DISSECTOR_KEY_IPV4_ADDRS,
+ target);
+ frame->ipv4_src = addr->src;
+ frame->ipv4_dst = addr->dst;
+ }
+
+ nfp_flower_compile_ip_ext(&frame->ip_ext, flow, mask_version);
}
static void
@@ -190,7 +233,6 @@ nfp_flower_compile_ipv6(struct nfp_flower_ipv6 *frame,
{
struct fl_flow_key *target = mask_version ? flow->mask : flow->key;
struct flow_dissector_key_ipv6_addrs *addr;
- struct flow_dissector_key_basic *basic;
memset(frame, 0, sizeof(struct nfp_flower_ipv6));
@@ -203,22 +245,7 @@ nfp_flower_compile_ipv6(struct nfp_flower_ipv6 *frame,
frame->ipv6_dst = addr->dst;
}
- if (dissector_uses_key(flow->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
- basic = skb_flow_dissector_target(flow->dissector,
- FLOW_DISSECTOR_KEY_BASIC,
- target);
- frame->proto = basic->ip_proto;
- }
-
- if (dissector_uses_key(flow->dissector, FLOW_DISSECTOR_KEY_IP)) {
- struct flow_dissector_key_ip *flow_ip;
-
- flow_ip = skb_flow_dissector_target(flow->dissector,
- FLOW_DISSECTOR_KEY_IP,
- target);
- frame->tos = flow_ip->tos;
- frame->ttl = flow_ip->ttl;
- }
+ nfp_flower_compile_ip_ext(&frame->ip_ext, flow, mask_version);
}
static void
diff --git a/drivers/net/ethernet/netronome/nfp/flower/offload.c b/drivers/net/ethernet/netronome/nfp/flower/offload.c
index eb5c13dea8f5..114d2ab02a38 100644
--- a/drivers/net/ethernet/netronome/nfp/flower/offload.c
+++ b/drivers/net/ethernet/netronome/nfp/flower/offload.c
@@ -44,11 +44,20 @@
#include "../nfp_net.h"
#include "../nfp_port.h"
+#define NFP_FLOWER_SUPPORTED_TCPFLAGS \
+ (TCPHDR_FIN | TCPHDR_SYN | TCPHDR_RST | \
+ TCPHDR_PSH | TCPHDR_URG)
+
+#define NFP_FLOWER_SUPPORTED_CTLFLAGS \
+ (FLOW_DIS_IS_FRAGMENT | \
+ FLOW_DIS_FIRST_FRAG)
+
#define NFP_FLOWER_WHITELIST_DISSECTOR \
(BIT(FLOW_DISSECTOR_KEY_CONTROL) | \
BIT(FLOW_DISSECTOR_KEY_BASIC) | \
BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) | \
BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) | \
+ BIT(FLOW_DISSECTOR_KEY_TCP) | \
BIT(FLOW_DISSECTOR_KEY_PORTS) | \
BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) | \
BIT(FLOW_DISSECTOR_KEY_VLAN) | \
@@ -288,6 +297,46 @@ nfp_flower_calculate_key_layers(struct nfp_app *app,
}
}
+ if (dissector_uses_key(flow->dissector, FLOW_DISSECTOR_KEY_TCP)) {
+ struct flow_dissector_key_tcp *tcp;
+ u32 tcp_flags;
+
+ tcp = skb_flow_dissector_target(flow->dissector,
+ FLOW_DISSECTOR_KEY_TCP,
+ flow->key);
+ tcp_flags = be16_to_cpu(tcp->flags);
+
+ if (tcp_flags & ~NFP_FLOWER_SUPPORTED_TCPFLAGS)
+ return -EOPNOTSUPP;
+
+ /* We only support PSH and URG flags when either
+ * FIN, SYN or RST is present as well.
+ */
+ if ((tcp_flags & (TCPHDR_PSH | TCPHDR_URG)) &&
+ !(tcp_flags & (TCPHDR_FIN | TCPHDR_SYN | TCPHDR_RST)))
+ return -EOPNOTSUPP;
+
+ /* We need to store TCP flags in the IPv4 key space, thus
+ * we need to ensure we include a IPv4 key layer if we have
+ * not done so already.
+ */
+ if (!(key_layer & NFP_FLOWER_LAYER_IPV4)) {
+ key_layer |= NFP_FLOWER_LAYER_IPV4;
+ key_size += sizeof(struct nfp_flower_ipv4);
+ }
+ }
+
+ if (dissector_uses_key(flow->dissector, FLOW_DISSECTOR_KEY_CONTROL)) {
+ struct flow_dissector_key_control *key_ctl;
+
+ key_ctl = skb_flow_dissector_target(flow->dissector,
+ FLOW_DISSECTOR_KEY_CONTROL,
+ flow->key);
+
+ if (key_ctl->flags & ~NFP_FLOWER_SUPPORTED_CTLFLAGS)
+ return -EOPNOTSUPP;
+ }
+
ret_key_ls->key_layer = key_layer;
ret_key_ls->key_layer_two = key_layer_two;
ret_key_ls->key_size = key_size;
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_app.h b/drivers/net/ethernet/netronome/nfp/nfp_app.h
index 20546ae67909..2d9cb2528fc7 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_app.h
+++ b/drivers/net/ethernet/netronome/nfp/nfp_app.h
@@ -86,8 +86,8 @@ extern const struct nfp_app_type app_flower;
* @repr_clean: representor about to be unregistered
* @repr_open: representor netdev open callback
* @repr_stop: representor netdev stop callback
- * @change_mtu: MTU change on a netdev has been requested (veto-only, change
- * is not guaranteed to be committed)
+ * @check_mtu: MTU change request on a netdev (verify it is valid)
+ * @repr_change_mtu: MTU change request on repr (make and verify change)
* @start: start application logic
* @stop: stop application logic
* @ctrl_msg_rx: control message handler
@@ -124,8 +124,10 @@ struct nfp_app_type {
int (*repr_open)(struct nfp_app *app, struct nfp_repr *repr);
int (*repr_stop)(struct nfp_app *app, struct nfp_repr *repr);
- int (*change_mtu)(struct nfp_app *app, struct net_device *netdev,
- int new_mtu);
+ int (*check_mtu)(struct nfp_app *app, struct net_device *netdev,
+ int new_mtu);
+ int (*repr_change_mtu)(struct nfp_app *app, struct net_device *netdev,
+ int new_mtu);
int (*start)(struct nfp_app *app);
void (*stop)(struct nfp_app *app);
@@ -247,11 +249,20 @@ nfp_app_repr_clean(struct nfp_app *app, struct net_device *netdev)
}
static inline int
-nfp_app_change_mtu(struct nfp_app *app, struct net_device *netdev, int new_mtu)
+nfp_app_check_mtu(struct nfp_app *app, struct net_device *netdev, int new_mtu)
{
- if (!app || !app->type->change_mtu)
+ if (!app || !app->type->check_mtu)
return 0;
- return app->type->change_mtu(app, netdev, new_mtu);
+ return app->type->check_mtu(app, netdev, new_mtu);
+}
+
+static inline int
+nfp_app_repr_change_mtu(struct nfp_app *app, struct net_device *netdev,
+ int new_mtu)
+{
+ if (!app || !app->type->repr_change_mtu)
+ return 0;
+ return app->type->repr_change_mtu(app, netdev, new_mtu);
}
static inline int nfp_app_start(struct nfp_app *app, struct nfp_net *ctrl)
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_asm.c b/drivers/net/ethernet/netronome/nfp/nfp_asm.c
index 1e597600c693..cc6ace2be8a9 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_asm.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_asm.c
@@ -48,6 +48,8 @@ const struct cmd_tgt_act cmd_tgt_act[__CMD_TGT_MAP_SIZE] = {
[CMD_TGT_READ32_SWAP] = { 0x02, 0x5c },
[CMD_TGT_READ_LE] = { 0x01, 0x40 },
[CMD_TGT_READ_SWAP_LE] = { 0x03, 0x40 },
+ [CMD_TGT_ADD] = { 0x00, 0x47 },
+ [CMD_TGT_ADD_IMM] = { 0x02, 0x47 },
};
static bool unreg_is_imm(u16 reg)
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_asm.h b/drivers/net/ethernet/netronome/nfp/nfp_asm.h
index 5f9291db98e0..5f2b2f24f4fa 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_asm.h
+++ b/drivers/net/ethernet/netronome/nfp/nfp_asm.h
@@ -39,6 +39,7 @@
#include <linux/types.h>
#define REG_NONE 0
+#define REG_WIDTH 4
#define RE_REG_NO_DST 0x020
#define RE_REG_IMM 0x020
@@ -237,6 +238,8 @@ enum cmd_tgt_map {
CMD_TGT_READ32_SWAP,
CMD_TGT_READ_LE,
CMD_TGT_READ_SWAP_LE,
+ CMD_TGT_ADD,
+ CMD_TGT_ADD_IMM,
__CMD_TGT_MAP_SIZE,
};
@@ -250,9 +253,12 @@ enum cmd_mode {
enum cmd_ctx_swap {
CMD_CTX_SWAP = 0,
+ CMD_CTX_SWAP_DEFER1 = 1,
+ CMD_CTX_SWAP_DEFER2 = 2,
CMD_CTX_NO_SWAP = 3,
};
+#define CMD_OVE_DATA GENMASK(5, 3)
#define CMD_OVE_LEN BIT(7)
#define CMD_OV_LEN GENMASK(12, 8)
@@ -278,6 +284,7 @@ enum lcsr_wr_src {
#define NFP_CSR_ACT_LM_ADDR1 0x6c
#define NFP_CSR_ACT_LM_ADDR2 0x94
#define NFP_CSR_ACT_LM_ADDR3 0x9c
+#define NFP_CSR_PSEUDO_RND_NUM 0x148
/* Software register representation, independent of operand type */
#define NN_REG_TYPE GENMASK(31, 24)
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.c b/drivers/net/ethernet/netronome/nfp/nfp_main.c
index ab301d56430b..c4b1f344b4da 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_main.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_main.c
@@ -645,6 +645,7 @@ MODULE_FIRMWARE("netronome/nic_AMDA0097-0001_4x10_1x40.nffw");
MODULE_FIRMWARE("netronome/nic_AMDA0097-0001_8x10.nffw");
MODULE_FIRMWARE("netronome/nic_AMDA0099-0001_2x10.nffw");
MODULE_FIRMWARE("netronome/nic_AMDA0099-0001_2x25.nffw");
+MODULE_FIRMWARE("netronome/nic_AMDA0099-0001_1x10_1x25.nffw");
MODULE_AUTHOR("Netronome Systems <oss-drivers@netronome.com>");
MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h
index 787df47ec430..bd7d8ae31e17 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net.h
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h
@@ -391,6 +391,7 @@ struct nfp_net_rx_ring {
* @rx_drops: Number of packets dropped on RX due to lack of resources
* @hw_csum_rx_ok: Counter of packets where the HW checksum was OK
* @hw_csum_rx_inner_ok: Counter of packets where the inner HW checksum was OK
+ * @hw_csum_rx_complete: Counter of packets with CHECKSUM_COMPLETE reported
* @hw_csum_rx_error: Counter of packets with bad checksums
* @tx_sync: Seqlock for atomic updates of TX stats
* @tx_pkts: Number of Transmitted packets
@@ -434,7 +435,7 @@ struct nfp_net_r_vector {
u64 rx_drops;
u64 hw_csum_rx_ok;
u64 hw_csum_rx_inner_ok;
- u64 hw_csum_rx_error;
+ u64 hw_csum_rx_complete;
struct nfp_net_tx_ring *xdp_ring;
@@ -446,6 +447,7 @@ struct nfp_net_r_vector {
u64 tx_gather;
u64 tx_lso;
+ u64 hw_csum_rx_error;
u64 rx_replace_buf_alloc_fail;
u64 tx_errors;
u64 tx_busy;
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
index a05be0ab2713..1eb6549f2a54 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
@@ -1406,7 +1406,7 @@ static void nfp_net_rx_csum(struct nfp_net_dp *dp,
skb->ip_summed = meta->csum_type;
skb->csum = meta->csum;
u64_stats_update_begin(&r_vec->rx_sync);
- r_vec->hw_csum_rx_ok++;
+ r_vec->hw_csum_rx_complete++;
u64_stats_update_end(&r_vec->rx_sync);
return;
}
@@ -3066,7 +3066,7 @@ static int nfp_net_change_mtu(struct net_device *netdev, int new_mtu)
struct nfp_net_dp *dp;
int err;
- err = nfp_app_change_mtu(nn->app, netdev, new_mtu);
+ err = nfp_app_check_mtu(nn->app, netdev, new_mtu);
if (err)
return err;
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h
index 4499a7333078..bb63c115537d 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015-2017 Netronome Systems, Inc.
+ * Copyright (C) 2015-2018 Netronome Systems, Inc.
*
* This software is dual licensed under the GNU General License Version 2,
* June 1991 as shown in the file COPYING in the top-level directory of this
@@ -51,12 +51,12 @@
* The configuration BAR is 8K in size, but due to
* THB-350, 32k needs to be reserved.
*/
-#define NFP_NET_CFG_BAR_SZ (32 * 1024)
+#define NFP_NET_CFG_BAR_SZ (32 * 1024)
/**
* Offset in Freelist buffer where packet starts on RX
*/
-#define NFP_NET_RX_OFFSET 32
+#define NFP_NET_RX_OFFSET 32
/**
* LSO parameters
@@ -75,65 +75,65 @@
#define NFP_NET_META_PORTID 5
#define NFP_NET_META_CSUM 6 /* checksum complete type */
-#define NFP_META_PORT_ID_CTRL ~0U
+#define NFP_META_PORT_ID_CTRL ~0U
/**
* Hash type pre-pended when a RSS hash was computed
*/
-#define NFP_NET_RSS_NONE 0
-#define NFP_NET_RSS_IPV4 1
-#define NFP_NET_RSS_IPV6 2
-#define NFP_NET_RSS_IPV6_EX 3
-#define NFP_NET_RSS_IPV4_TCP 4
-#define NFP_NET_RSS_IPV6_TCP 5
-#define NFP_NET_RSS_IPV6_EX_TCP 6
-#define NFP_NET_RSS_IPV4_UDP 7
-#define NFP_NET_RSS_IPV6_UDP 8
-#define NFP_NET_RSS_IPV6_EX_UDP 9
+#define NFP_NET_RSS_NONE 0
+#define NFP_NET_RSS_IPV4 1
+#define NFP_NET_RSS_IPV6 2
+#define NFP_NET_RSS_IPV6_EX 3
+#define NFP_NET_RSS_IPV4_TCP 4
+#define NFP_NET_RSS_IPV6_TCP 5
+#define NFP_NET_RSS_IPV6_EX_TCP 6
+#define NFP_NET_RSS_IPV4_UDP 7
+#define NFP_NET_RSS_IPV6_UDP 8
+#define NFP_NET_RSS_IPV6_EX_UDP 9
/**
* Ring counts
- * %NFP_NET_TXR_MAX: Maximum number of TX rings
- * %NFP_NET_RXR_MAX: Maximum number of RX rings
+ * %NFP_NET_TXR_MAX: Maximum number of TX rings
+ * %NFP_NET_RXR_MAX: Maximum number of RX rings
*/
-#define NFP_NET_TXR_MAX 64
-#define NFP_NET_RXR_MAX 64
+#define NFP_NET_TXR_MAX 64
+#define NFP_NET_RXR_MAX 64
/**
* Read/Write config words (0x0000 - 0x002c)
- * %NFP_NET_CFG_CTRL: Global control
+ * %NFP_NET_CFG_CTRL: Global control
* %NFP_NET_CFG_UPDATE: Indicate which fields are updated
* %NFP_NET_CFG_TXRS_ENABLE: Bitmask of enabled TX rings
* %NFP_NET_CFG_RXRS_ENABLE: Bitmask of enabled RX rings
- * %NFP_NET_CFG_MTU: Set MTU size
+ * %NFP_NET_CFG_MTU: Set MTU size
* %NFP_NET_CFG_FLBUFSZ: Set freelist buffer size (must be larger than MTU)
- * %NFP_NET_CFG_EXN: MSI-X table entry for exceptions
- * %NFP_NET_CFG_LSC: MSI-X table entry for link state changes
+ * %NFP_NET_CFG_EXN: MSI-X table entry for exceptions
+ * %NFP_NET_CFG_LSC: MSI-X table entry for link state changes
* %NFP_NET_CFG_MACADDR: MAC address
*
* TODO:
* - define Error details in UPDATE
*/
-#define NFP_NET_CFG_CTRL 0x0000
-#define NFP_NET_CFG_CTRL_ENABLE (0x1 << 0) /* Global enable */
-#define NFP_NET_CFG_CTRL_PROMISC (0x1 << 1) /* Enable Promisc mode */
-#define NFP_NET_CFG_CTRL_L2BC (0x1 << 2) /* Allow L2 Broadcast */
-#define NFP_NET_CFG_CTRL_L2MC (0x1 << 3) /* Allow L2 Multicast */
-#define NFP_NET_CFG_CTRL_RXCSUM (0x1 << 4) /* Enable RX Checksum */
-#define NFP_NET_CFG_CTRL_TXCSUM (0x1 << 5) /* Enable TX Checksum */
-#define NFP_NET_CFG_CTRL_RXVLAN (0x1 << 6) /* Enable VLAN strip */
-#define NFP_NET_CFG_CTRL_TXVLAN (0x1 << 7) /* Enable VLAN insert */
-#define NFP_NET_CFG_CTRL_SCATTER (0x1 << 8) /* Scatter DMA */
-#define NFP_NET_CFG_CTRL_GATHER (0x1 << 9) /* Gather DMA */
-#define NFP_NET_CFG_CTRL_LSO (0x1 << 10) /* LSO/TSO (version 1) */
+#define NFP_NET_CFG_CTRL 0x0000
+#define NFP_NET_CFG_CTRL_ENABLE (0x1 << 0) /* Global enable */
+#define NFP_NET_CFG_CTRL_PROMISC (0x1 << 1) /* Enable Promisc mode */
+#define NFP_NET_CFG_CTRL_L2BC (0x1 << 2) /* Allow L2 Broadcast */
+#define NFP_NET_CFG_CTRL_L2MC (0x1 << 3) /* Allow L2 Multicast */
+#define NFP_NET_CFG_CTRL_RXCSUM (0x1 << 4) /* Enable RX Checksum */
+#define NFP_NET_CFG_CTRL_TXCSUM (0x1 << 5) /* Enable TX Checksum */
+#define NFP_NET_CFG_CTRL_RXVLAN (0x1 << 6) /* Enable VLAN strip */
+#define NFP_NET_CFG_CTRL_TXVLAN (0x1 << 7) /* Enable VLAN insert */
+#define NFP_NET_CFG_CTRL_SCATTER (0x1 << 8) /* Scatter DMA */
+#define NFP_NET_CFG_CTRL_GATHER (0x1 << 9) /* Gather DMA */
+#define NFP_NET_CFG_CTRL_LSO (0x1 << 10) /* LSO/TSO (version 1) */
#define NFP_NET_CFG_CTRL_CTAG_FILTER (0x1 << 11) /* VLAN CTAG filtering */
-#define NFP_NET_CFG_CTRL_RINGCFG (0x1 << 16) /* Ring runtime changes */
+#define NFP_NET_CFG_CTRL_RINGCFG (0x1 << 16) /* Ring runtime changes */
#define NFP_NET_CFG_CTRL_RSS (0x1 << 17) /* RSS (version 1) */
-#define NFP_NET_CFG_CTRL_IRQMOD (0x1 << 18) /* Interrupt moderation */
-#define NFP_NET_CFG_CTRL_RINGPRIO (0x1 << 19) /* Ring priorities */
-#define NFP_NET_CFG_CTRL_MSIXAUTO (0x1 << 20) /* MSI-X auto-masking */
-#define NFP_NET_CFG_CTRL_TXRWB (0x1 << 21) /* Write-back of TX ring*/
-#define NFP_NET_CFG_CTRL_L2SWITCH (0x1 << 22) /* L2 Switch */
+#define NFP_NET_CFG_CTRL_IRQMOD (0x1 << 18) /* Interrupt moderation */
+#define NFP_NET_CFG_CTRL_RINGPRIO (0x1 << 19) /* Ring priorities */
+#define NFP_NET_CFG_CTRL_MSIXAUTO (0x1 << 20) /* MSI-X auto-masking */
+#define NFP_NET_CFG_CTRL_TXRWB (0x1 << 21) /* Write-back of TX ring*/
+#define NFP_NET_CFG_CTRL_L2SWITCH (0x1 << 22) /* L2 Switch */
#define NFP_NET_CFG_CTRL_L2SWITCH_LOCAL (0x1 << 23) /* Switch to local */
#define NFP_NET_CFG_CTRL_VXLAN (0x1 << 24) /* VXLAN tunnel support */
#define NFP_NET_CFG_CTRL_NVGRE (0x1 << 25) /* NVGRE tunnel support */
@@ -152,35 +152,35 @@
#define NFP_NET_CFG_CTRL_CHAIN_META (NFP_NET_CFG_CTRL_RSS2 | \
NFP_NET_CFG_CTRL_CSUM_COMPLETE)
-#define NFP_NET_CFG_UPDATE 0x0004
-#define NFP_NET_CFG_UPDATE_GEN (0x1 << 0) /* General update */
-#define NFP_NET_CFG_UPDATE_RING (0x1 << 1) /* Ring config change */
-#define NFP_NET_CFG_UPDATE_RSS (0x1 << 2) /* RSS config change */
-#define NFP_NET_CFG_UPDATE_TXRPRIO (0x1 << 3) /* TX Ring prio change */
-#define NFP_NET_CFG_UPDATE_RXRPRIO (0x1 << 4) /* RX Ring prio change */
-#define NFP_NET_CFG_UPDATE_MSIX (0x1 << 5) /* MSI-X change */
-#define NFP_NET_CFG_UPDATE_L2SWITCH (0x1 << 6) /* Switch changes */
-#define NFP_NET_CFG_UPDATE_RESET (0x1 << 7) /* Update due to FLR */
-#define NFP_NET_CFG_UPDATE_IRQMOD (0x1 << 8) /* IRQ mod change */
+#define NFP_NET_CFG_UPDATE 0x0004
+#define NFP_NET_CFG_UPDATE_GEN (0x1 << 0) /* General update */
+#define NFP_NET_CFG_UPDATE_RING (0x1 << 1) /* Ring config change */
+#define NFP_NET_CFG_UPDATE_RSS (0x1 << 2) /* RSS config change */
+#define NFP_NET_CFG_UPDATE_TXRPRIO (0x1 << 3) /* TX Ring prio change */
+#define NFP_NET_CFG_UPDATE_RXRPRIO (0x1 << 4) /* RX Ring prio change */
+#define NFP_NET_CFG_UPDATE_MSIX (0x1 << 5) /* MSI-X change */
+#define NFP_NET_CFG_UPDATE_L2SWITCH (0x1 << 6) /* Switch changes */
+#define NFP_NET_CFG_UPDATE_RESET (0x1 << 7) /* Update due to FLR */
+#define NFP_NET_CFG_UPDATE_IRQMOD (0x1 << 8) /* IRQ mod change */
#define NFP_NET_CFG_UPDATE_VXLAN (0x1 << 9) /* VXLAN port change */
#define NFP_NET_CFG_UPDATE_BPF (0x1 << 10) /* BPF program load */
#define NFP_NET_CFG_UPDATE_MACADDR (0x1 << 11) /* MAC address change */
#define NFP_NET_CFG_UPDATE_MBOX (0x1 << 12) /* Mailbox update */
#define NFP_NET_CFG_UPDATE_VF (0x1 << 13) /* VF settings change */
-#define NFP_NET_CFG_UPDATE_ERR (0x1 << 31) /* A error occurred */
-#define NFP_NET_CFG_TXRS_ENABLE 0x0008
-#define NFP_NET_CFG_RXRS_ENABLE 0x0010
-#define NFP_NET_CFG_MTU 0x0018
-#define NFP_NET_CFG_FLBUFSZ 0x001c
-#define NFP_NET_CFG_EXN 0x001f
-#define NFP_NET_CFG_LSC 0x0020
-#define NFP_NET_CFG_MACADDR 0x0024
+#define NFP_NET_CFG_UPDATE_ERR (0x1 << 31) /* A error occurred */
+#define NFP_NET_CFG_TXRS_ENABLE 0x0008
+#define NFP_NET_CFG_RXRS_ENABLE 0x0010
+#define NFP_NET_CFG_MTU 0x0018
+#define NFP_NET_CFG_FLBUFSZ 0x001c
+#define NFP_NET_CFG_EXN 0x001f
+#define NFP_NET_CFG_LSC 0x0020
+#define NFP_NET_CFG_MACADDR 0x0024
/**
* Read-only words (0x0030 - 0x0050):
* %NFP_NET_CFG_VERSION: Firmware version number
- * %NFP_NET_CFG_STS: Status
- * %NFP_NET_CFG_CAP: Capabilities (same bits as %NFP_NET_CFG_CTRL)
+ * %NFP_NET_CFG_STS: Status
+ * %NFP_NET_CFG_CAP: Capabilities (same bits as %NFP_NET_CFG_CTRL)
* %NFP_NET_CFG_MAX_TXRINGS: Maximum number of TX rings
* %NFP_NET_CFG_MAX_RXRINGS: Maximum number of RX rings
* %NFP_NET_CFG_MAX_MTU: Maximum support MTU
@@ -190,37 +190,37 @@
* TODO:
* - define more STS bits
*/
-#define NFP_NET_CFG_VERSION 0x0030
+#define NFP_NET_CFG_VERSION 0x0030
#define NFP_NET_CFG_VERSION_RESERVED_MASK (0xff << 24)
#define NFP_NET_CFG_VERSION_CLASS_MASK (0xff << 16)
-#define NFP_NET_CFG_VERSION_CLASS(x) (((x) & 0xff) << 16)
+#define NFP_NET_CFG_VERSION_CLASS(x) (((x) & 0xff) << 16)
#define NFP_NET_CFG_VERSION_CLASS_GENERIC 0
#define NFP_NET_CFG_VERSION_MAJOR_MASK (0xff << 8)
-#define NFP_NET_CFG_VERSION_MAJOR(x) (((x) & 0xff) << 8)
+#define NFP_NET_CFG_VERSION_MAJOR(x) (((x) & 0xff) << 8)
#define NFP_NET_CFG_VERSION_MINOR_MASK (0xff << 0)
-#define NFP_NET_CFG_VERSION_MINOR(x) (((x) & 0xff) << 0)
-#define NFP_NET_CFG_STS 0x0034
-#define NFP_NET_CFG_STS_LINK (0x1 << 0) /* Link up or down */
+#define NFP_NET_CFG_VERSION_MINOR(x) (((x) & 0xff) << 0)
+#define NFP_NET_CFG_STS 0x0034
+#define NFP_NET_CFG_STS_LINK (0x1 << 0) /* Link up or down */
/* Link rate */
#define NFP_NET_CFG_STS_LINK_RATE_SHIFT 1
#define NFP_NET_CFG_STS_LINK_RATE_MASK 0xF
-#define NFP_NET_CFG_STS_LINK_RATE \
+#define NFP_NET_CFG_STS_LINK_RATE \
(NFP_NET_CFG_STS_LINK_RATE_MASK << NFP_NET_CFG_STS_LINK_RATE_SHIFT)
#define NFP_NET_CFG_STS_LINK_RATE_UNSUPPORTED 0
-#define NFP_NET_CFG_STS_LINK_RATE_UNKNOWN 1
-#define NFP_NET_CFG_STS_LINK_RATE_1G 2
-#define NFP_NET_CFG_STS_LINK_RATE_10G 3
-#define NFP_NET_CFG_STS_LINK_RATE_25G 4
-#define NFP_NET_CFG_STS_LINK_RATE_40G 5
-#define NFP_NET_CFG_STS_LINK_RATE_50G 6
-#define NFP_NET_CFG_STS_LINK_RATE_100G 7
-#define NFP_NET_CFG_CAP 0x0038
-#define NFP_NET_CFG_MAX_TXRINGS 0x003c
-#define NFP_NET_CFG_MAX_RXRINGS 0x0040
-#define NFP_NET_CFG_MAX_MTU 0x0044
+#define NFP_NET_CFG_STS_LINK_RATE_UNKNOWN 1
+#define NFP_NET_CFG_STS_LINK_RATE_1G 2
+#define NFP_NET_CFG_STS_LINK_RATE_10G 3
+#define NFP_NET_CFG_STS_LINK_RATE_25G 4
+#define NFP_NET_CFG_STS_LINK_RATE_40G 5
+#define NFP_NET_CFG_STS_LINK_RATE_50G 6
+#define NFP_NET_CFG_STS_LINK_RATE_100G 7
+#define NFP_NET_CFG_CAP 0x0038
+#define NFP_NET_CFG_MAX_TXRINGS 0x003c
+#define NFP_NET_CFG_MAX_RXRINGS 0x0040
+#define NFP_NET_CFG_MAX_MTU 0x0044
/* Next two words are being used by VFs for solving THB350 issue */
-#define NFP_NET_CFG_START_TXQ 0x0048
-#define NFP_NET_CFG_START_RXQ 0x004c
+#define NFP_NET_CFG_START_TXQ 0x0048
+#define NFP_NET_CFG_START_RXQ 0x004c
/**
* Prepend configuration
@@ -280,8 +280,8 @@
/**
* 40B reserved for future use (0x0098 - 0x00c0)
*/
-#define NFP_NET_CFG_RESERVED 0x0098
-#define NFP_NET_CFG_RESERVED_SZ 0x0028
+#define NFP_NET_CFG_RESERVED 0x0098
+#define NFP_NET_CFG_RESERVED_SZ 0x0028
/**
* RSS configuration (0x0100 - 0x01ac):
@@ -290,26 +290,26 @@
* %NFP_NET_CFG_RSS_KEY: RSS "secret" key
* %NFP_NET_CFG_RSS_ITBL: RSS indirection table
*/
-#define NFP_NET_CFG_RSS_BASE 0x0100
-#define NFP_NET_CFG_RSS_CTRL NFP_NET_CFG_RSS_BASE
-#define NFP_NET_CFG_RSS_MASK (0x7f)
-#define NFP_NET_CFG_RSS_MASK_of(_x) ((_x) & 0x7f)
-#define NFP_NET_CFG_RSS_IPV4 (1 << 8) /* RSS for IPv4 */
-#define NFP_NET_CFG_RSS_IPV6 (1 << 9) /* RSS for IPv6 */
-#define NFP_NET_CFG_RSS_IPV4_TCP (1 << 10) /* RSS for IPv4/TCP */
-#define NFP_NET_CFG_RSS_IPV4_UDP (1 << 11) /* RSS for IPv4/UDP */
-#define NFP_NET_CFG_RSS_IPV6_TCP (1 << 12) /* RSS for IPv6/TCP */
-#define NFP_NET_CFG_RSS_IPV6_UDP (1 << 13) /* RSS for IPv6/UDP */
+#define NFP_NET_CFG_RSS_BASE 0x0100
+#define NFP_NET_CFG_RSS_CTRL NFP_NET_CFG_RSS_BASE
+#define NFP_NET_CFG_RSS_MASK (0x7f)
+#define NFP_NET_CFG_RSS_MASK_of(_x) ((_x) & 0x7f)
+#define NFP_NET_CFG_RSS_IPV4 (1 << 8) /* RSS for IPv4 */
+#define NFP_NET_CFG_RSS_IPV6 (1 << 9) /* RSS for IPv6 */
+#define NFP_NET_CFG_RSS_IPV4_TCP (1 << 10) /* RSS for IPv4/TCP */
+#define NFP_NET_CFG_RSS_IPV4_UDP (1 << 11) /* RSS for IPv4/UDP */
+#define NFP_NET_CFG_RSS_IPV6_TCP (1 << 12) /* RSS for IPv6/TCP */
+#define NFP_NET_CFG_RSS_IPV6_UDP (1 << 13) /* RSS for IPv6/UDP */
#define NFP_NET_CFG_RSS_HFUNC 0xff000000
-#define NFP_NET_CFG_RSS_TOEPLITZ (1 << 24) /* Use Toeplitz hash */
+#define NFP_NET_CFG_RSS_TOEPLITZ (1 << 24) /* Use Toeplitz hash */
#define NFP_NET_CFG_RSS_XOR (1 << 25) /* Use XOR as hash */
#define NFP_NET_CFG_RSS_CRC32 (1 << 26) /* Use CRC32 as hash */
#define NFP_NET_CFG_RSS_HFUNCS 3
-#define NFP_NET_CFG_RSS_KEY (NFP_NET_CFG_RSS_BASE + 0x4)
-#define NFP_NET_CFG_RSS_KEY_SZ 0x28
-#define NFP_NET_CFG_RSS_ITBL (NFP_NET_CFG_RSS_BASE + 0x4 + \
+#define NFP_NET_CFG_RSS_KEY (NFP_NET_CFG_RSS_BASE + 0x4)
+#define NFP_NET_CFG_RSS_KEY_SZ 0x28
+#define NFP_NET_CFG_RSS_ITBL (NFP_NET_CFG_RSS_BASE + 0x4 + \
NFP_NET_CFG_RSS_KEY_SZ)
-#define NFP_NET_CFG_RSS_ITBL_SZ 0x80
+#define NFP_NET_CFG_RSS_ITBL_SZ 0x80
/**
* TX ring configuration (0x200 - 0x800)
@@ -321,13 +321,13 @@
* %NFP_NET_CFG_TXR_PRIO: Per TX ring priority (1B entries)
* %NFP_NET_CFG_TXR_IRQ_MOD: Per TX ring interrupt moderation packet
*/
-#define NFP_NET_CFG_TXR_BASE 0x0200
-#define NFP_NET_CFG_TXR_ADDR(_x) (NFP_NET_CFG_TXR_BASE + ((_x) * 0x8))
-#define NFP_NET_CFG_TXR_WB_ADDR(_x) (NFP_NET_CFG_TXR_BASE + 0x200 + \
+#define NFP_NET_CFG_TXR_BASE 0x0200
+#define NFP_NET_CFG_TXR_ADDR(_x) (NFP_NET_CFG_TXR_BASE + ((_x) * 0x8))
+#define NFP_NET_CFG_TXR_WB_ADDR(_x) (NFP_NET_CFG_TXR_BASE + 0x200 + \
((_x) * 0x8))
-#define NFP_NET_CFG_TXR_SZ(_x) (NFP_NET_CFG_TXR_BASE + 0x400 + (_x))
-#define NFP_NET_CFG_TXR_VEC(_x) (NFP_NET_CFG_TXR_BASE + 0x440 + (_x))
-#define NFP_NET_CFG_TXR_PRIO(_x) (NFP_NET_CFG_TXR_BASE + 0x480 + (_x))
+#define NFP_NET_CFG_TXR_SZ(_x) (NFP_NET_CFG_TXR_BASE + 0x400 + (_x))
+#define NFP_NET_CFG_TXR_VEC(_x) (NFP_NET_CFG_TXR_BASE + 0x440 + (_x))
+#define NFP_NET_CFG_TXR_PRIO(_x) (NFP_NET_CFG_TXR_BASE + 0x480 + (_x))
#define NFP_NET_CFG_TXR_IRQ_MOD(_x) (NFP_NET_CFG_TXR_BASE + 0x500 + \
((_x) * 0x4))
@@ -340,11 +340,11 @@
* %NFP_NET_CFG_RXR_PRIO: Per RX ring priority (1B entries)
* %NFP_NET_CFG_RXR_IRQ_MOD: Per RX ring interrupt moderation (4B entries)
*/
-#define NFP_NET_CFG_RXR_BASE 0x0800
-#define NFP_NET_CFG_RXR_ADDR(_x) (NFP_NET_CFG_RXR_BASE + ((_x) * 0x8))
-#define NFP_NET_CFG_RXR_SZ(_x) (NFP_NET_CFG_RXR_BASE + 0x200 + (_x))
-#define NFP_NET_CFG_RXR_VEC(_x) (NFP_NET_CFG_RXR_BASE + 0x240 + (_x))
-#define NFP_NET_CFG_RXR_PRIO(_x) (NFP_NET_CFG_RXR_BASE + 0x280 + (_x))
+#define NFP_NET_CFG_RXR_BASE 0x0800
+#define NFP_NET_CFG_RXR_ADDR(_x) (NFP_NET_CFG_RXR_BASE + ((_x) * 0x8))
+#define NFP_NET_CFG_RXR_SZ(_x) (NFP_NET_CFG_RXR_BASE + 0x200 + (_x))
+#define NFP_NET_CFG_RXR_VEC(_x) (NFP_NET_CFG_RXR_BASE + 0x240 + (_x))
+#define NFP_NET_CFG_RXR_PRIO(_x) (NFP_NET_CFG_RXR_BASE + 0x280 + (_x))
#define NFP_NET_CFG_RXR_IRQ_MOD(_x) (NFP_NET_CFG_RXR_BASE + 0x300 + \
((_x) * 0x4))
@@ -358,36 +358,36 @@
* the MSI-X entry and the host driver must clear the register to
* re-enable the interrupt.
*/
-#define NFP_NET_CFG_ICR_BASE 0x0c00
-#define NFP_NET_CFG_ICR(_x) (NFP_NET_CFG_ICR_BASE + (_x))
-#define NFP_NET_CFG_ICR_UNMASKED 0x0
-#define NFP_NET_CFG_ICR_RXTX 0x1
-#define NFP_NET_CFG_ICR_LSC 0x2
+#define NFP_NET_CFG_ICR_BASE 0x0c00
+#define NFP_NET_CFG_ICR(_x) (NFP_NET_CFG_ICR_BASE + (_x))
+#define NFP_NET_CFG_ICR_UNMASKED 0x0
+#define NFP_NET_CFG_ICR_RXTX 0x1
+#define NFP_NET_CFG_ICR_LSC 0x2
/**
* General device stats (0x0d00 - 0x0d90)
* all counters are 64bit.
*/
-#define NFP_NET_CFG_STATS_BASE 0x0d00
-#define NFP_NET_CFG_STATS_RX_DISCARDS (NFP_NET_CFG_STATS_BASE + 0x00)
-#define NFP_NET_CFG_STATS_RX_ERRORS (NFP_NET_CFG_STATS_BASE + 0x08)
-#define NFP_NET_CFG_STATS_RX_OCTETS (NFP_NET_CFG_STATS_BASE + 0x10)
-#define NFP_NET_CFG_STATS_RX_UC_OCTETS (NFP_NET_CFG_STATS_BASE + 0x18)
-#define NFP_NET_CFG_STATS_RX_MC_OCTETS (NFP_NET_CFG_STATS_BASE + 0x20)
-#define NFP_NET_CFG_STATS_RX_BC_OCTETS (NFP_NET_CFG_STATS_BASE + 0x28)
-#define NFP_NET_CFG_STATS_RX_FRAMES (NFP_NET_CFG_STATS_BASE + 0x30)
-#define NFP_NET_CFG_STATS_RX_MC_FRAMES (NFP_NET_CFG_STATS_BASE + 0x38)
-#define NFP_NET_CFG_STATS_RX_BC_FRAMES (NFP_NET_CFG_STATS_BASE + 0x40)
-
-#define NFP_NET_CFG_STATS_TX_DISCARDS (NFP_NET_CFG_STATS_BASE + 0x48)
-#define NFP_NET_CFG_STATS_TX_ERRORS (NFP_NET_CFG_STATS_BASE + 0x50)
-#define NFP_NET_CFG_STATS_TX_OCTETS (NFP_NET_CFG_STATS_BASE + 0x58)
-#define NFP_NET_CFG_STATS_TX_UC_OCTETS (NFP_NET_CFG_STATS_BASE + 0x60)
-#define NFP_NET_CFG_STATS_TX_MC_OCTETS (NFP_NET_CFG_STATS_BASE + 0x68)
-#define NFP_NET_CFG_STATS_TX_BC_OCTETS (NFP_NET_CFG_STATS_BASE + 0x70)
-#define NFP_NET_CFG_STATS_TX_FRAMES (NFP_NET_CFG_STATS_BASE + 0x78)
-#define NFP_NET_CFG_STATS_TX_MC_FRAMES (NFP_NET_CFG_STATS_BASE + 0x80)
-#define NFP_NET_CFG_STATS_TX_BC_FRAMES (NFP_NET_CFG_STATS_BASE + 0x88)
+#define NFP_NET_CFG_STATS_BASE 0x0d00
+#define NFP_NET_CFG_STATS_RX_DISCARDS (NFP_NET_CFG_STATS_BASE + 0x00)
+#define NFP_NET_CFG_STATS_RX_ERRORS (NFP_NET_CFG_STATS_BASE + 0x08)
+#define NFP_NET_CFG_STATS_RX_OCTETS (NFP_NET_CFG_STATS_BASE + 0x10)
+#define NFP_NET_CFG_STATS_RX_UC_OCTETS (NFP_NET_CFG_STATS_BASE + 0x18)
+#define NFP_NET_CFG_STATS_RX_MC_OCTETS (NFP_NET_CFG_STATS_BASE + 0x20)
+#define NFP_NET_CFG_STATS_RX_BC_OCTETS (NFP_NET_CFG_STATS_BASE + 0x28)
+#define NFP_NET_CFG_STATS_RX_FRAMES (NFP_NET_CFG_STATS_BASE + 0x30)
+#define NFP_NET_CFG_STATS_RX_MC_FRAMES (NFP_NET_CFG_STATS_BASE + 0x38)
+#define NFP_NET_CFG_STATS_RX_BC_FRAMES (NFP_NET_CFG_STATS_BASE + 0x40)
+
+#define NFP_NET_CFG_STATS_TX_DISCARDS (NFP_NET_CFG_STATS_BASE + 0x48)
+#define NFP_NET_CFG_STATS_TX_ERRORS (NFP_NET_CFG_STATS_BASE + 0x50)
+#define NFP_NET_CFG_STATS_TX_OCTETS (NFP_NET_CFG_STATS_BASE + 0x58)
+#define NFP_NET_CFG_STATS_TX_UC_OCTETS (NFP_NET_CFG_STATS_BASE + 0x60)
+#define NFP_NET_CFG_STATS_TX_MC_OCTETS (NFP_NET_CFG_STATS_BASE + 0x68)
+#define NFP_NET_CFG_STATS_TX_BC_OCTETS (NFP_NET_CFG_STATS_BASE + 0x70)
+#define NFP_NET_CFG_STATS_TX_FRAMES (NFP_NET_CFG_STATS_BASE + 0x78)
+#define NFP_NET_CFG_STATS_TX_MC_FRAMES (NFP_NET_CFG_STATS_BASE + 0x80)
+#define NFP_NET_CFG_STATS_TX_BC_FRAMES (NFP_NET_CFG_STATS_BASE + 0x88)
#define NFP_NET_CFG_STATS_APP0_FRAMES (NFP_NET_CFG_STATS_BASE + 0x90)
#define NFP_NET_CFG_STATS_APP0_BYTES (NFP_NET_CFG_STATS_BASE + 0x98)
@@ -404,11 +404,11 @@
* %NFP_NET_CFG_TXR_STATS: TX ring statistics (Packet and Byte count)
* %NFP_NET_CFG_RXR_STATS: RX ring statistics (Packet and Byte count)
*/
-#define NFP_NET_CFG_TXR_STATS_BASE 0x1000
-#define NFP_NET_CFG_TXR_STATS(_x) (NFP_NET_CFG_TXR_STATS_BASE + \
+#define NFP_NET_CFG_TXR_STATS_BASE 0x1000
+#define NFP_NET_CFG_TXR_STATS(_x) (NFP_NET_CFG_TXR_STATS_BASE + \
((_x) * 0x10))
-#define NFP_NET_CFG_RXR_STATS_BASE 0x1400
-#define NFP_NET_CFG_RXR_STATS(_x) (NFP_NET_CFG_RXR_STATS_BASE + \
+#define NFP_NET_CFG_RXR_STATS_BASE 0x1400
+#define NFP_NET_CFG_RXR_STATS(_x) (NFP_NET_CFG_RXR_STATS_BASE + \
((_x) * 0x10))
/**
@@ -444,7 +444,7 @@
* %NFP_NET_CFG_TLV_TYPE: Offset of type within the TLV
* %NFP_NET_CFG_TLV_TYPE_REQUIRED: Driver must be able to parse the TLV
* %NFP_NET_CFG_TLV_LENGTH: Offset of length within the TLV
- * %NFP_NET_CFG_TLV_LENGTH_INC: TLV length increments
+ * %NFP_NET_CFG_TLV_LENGTH_INC: TLV length increments
* %NFP_NET_CFG_TLV_VALUE: Offset of value with the TLV
*
* List of simple TLV structures, first one starts at %NFP_NET_CFG_TLV_BASE.
@@ -457,12 +457,12 @@
* Note that the 4 byte TLV header is not counted in %NFP_NET_CFG_TLV_LENGTH.
*/
#define NFP_NET_CFG_TLV_TYPE 0x00
-#define NFP_NET_CFG_TLV_TYPE_REQUIRED 0x8000
+#define NFP_NET_CFG_TLV_TYPE_REQUIRED 0x8000
#define NFP_NET_CFG_TLV_LENGTH 0x02
#define NFP_NET_CFG_TLV_LENGTH_INC 4
#define NFP_NET_CFG_TLV_VALUE 0x04
-#define NFP_NET_CFG_TLV_HEADER_REQUIRED 0x80000000
+#define NFP_NET_CFG_TLV_HEADER_REQUIRED 0x80000000
#define NFP_NET_CFG_TLV_HEADER_TYPE 0x7fff0000
#define NFP_NET_CFG_TLV_HEADER_LENGTH 0x0000ffff
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c b/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c
index cf81cf95d1d8..67cdd8330c59 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c
@@ -231,15 +231,15 @@ void nfp_net_debugfs_vnic_add(struct nfp_net *nn, struct dentry *ddir, int id)
for (i = 0; i < min(nn->max_rx_rings, nn->max_r_vecs); i++) {
sprintf(name, "%d", i);
- debugfs_create_file(name, S_IRUSR, rx,
+ debugfs_create_file(name, 0400, rx,
&nn->r_vecs[i], &nfp_rx_q_fops);
- debugfs_create_file(name, S_IRUSR, xdp,
+ debugfs_create_file(name, 0400, xdp,
&nn->r_vecs[i], &nfp_xdp_q_fops);
}
for (i = 0; i < min(nn->max_tx_rings, nn->max_r_vecs); i++) {
sprintf(name, "%d", i);
- debugfs_create_file(name, S_IRUSR, tx,
+ debugfs_create_file(name, 0400, tx,
&nn->r_vecs[i], &nfp_tx_q_fops);
}
}
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c
index e1dae0616f52..c9016419bfa0 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c
@@ -179,7 +179,7 @@ static const struct nfp_et_stat nfp_mac_et_stats[] = {
#define NN_ET_GLOBAL_STATS_LEN ARRAY_SIZE(nfp_net_et_stats)
#define NN_ET_SWITCH_STATS_LEN 9
-#define NN_RVEC_GATHER_STATS 8
+#define NN_RVEC_GATHER_STATS 9
#define NN_RVEC_PER_Q_STATS 3
static void nfp_net_get_nspinfo(struct nfp_app *app, char *version)
@@ -468,6 +468,7 @@ static u8 *nfp_vnic_get_sw_stats_strings(struct net_device *netdev, u8 *data)
data = nfp_pr_et(data, "hw_rx_csum_ok");
data = nfp_pr_et(data, "hw_rx_csum_inner_ok");
+ data = nfp_pr_et(data, "hw_rx_csum_complete");
data = nfp_pr_et(data, "hw_rx_csum_err");
data = nfp_pr_et(data, "rx_replace_buf_alloc_fail");
data = nfp_pr_et(data, "hw_tx_csum");
@@ -493,18 +494,19 @@ static u64 *nfp_vnic_get_sw_stats(struct net_device *netdev, u64 *data)
data[0] = nn->r_vecs[i].rx_pkts;
tmp[0] = nn->r_vecs[i].hw_csum_rx_ok;
tmp[1] = nn->r_vecs[i].hw_csum_rx_inner_ok;
- tmp[2] = nn->r_vecs[i].hw_csum_rx_error;
- tmp[3] = nn->r_vecs[i].rx_replace_buf_alloc_fail;
+ tmp[2] = nn->r_vecs[i].hw_csum_rx_complete;
+ tmp[3] = nn->r_vecs[i].hw_csum_rx_error;
+ tmp[4] = nn->r_vecs[i].rx_replace_buf_alloc_fail;
} while (u64_stats_fetch_retry(&nn->r_vecs[i].rx_sync, start));
do {
start = u64_stats_fetch_begin(&nn->r_vecs[i].tx_sync);
data[1] = nn->r_vecs[i].tx_pkts;
data[2] = nn->r_vecs[i].tx_busy;
- tmp[4] = nn->r_vecs[i].hw_csum_tx;
- tmp[5] = nn->r_vecs[i].hw_csum_tx_inner;
- tmp[6] = nn->r_vecs[i].tx_gather;
- tmp[7] = nn->r_vecs[i].tx_lso;
+ tmp[5] = nn->r_vecs[i].hw_csum_tx;
+ tmp[6] = nn->r_vecs[i].hw_csum_tx_inner;
+ tmp[7] = nn->r_vecs[i].tx_gather;
+ tmp[8] = nn->r_vecs[i].tx_lso;
} while (u64_stats_fetch_retry(&nn->r_vecs[i].tx_sync, start));
data += NN_RVEC_PER_Q_STATS;
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c
index 619570524d2a..0cd077addb26 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c
@@ -196,8 +196,19 @@ nfp_repr_get_offload_stats(int attr_id, const struct net_device *dev,
static int nfp_repr_change_mtu(struct net_device *netdev, int new_mtu)
{
struct nfp_repr *repr = netdev_priv(netdev);
+ int err;
- return nfp_app_change_mtu(repr->app, netdev, new_mtu);
+ err = nfp_app_check_mtu(repr->app, netdev, new_mtu);
+ if (err)
+ return err;
+
+ err = nfp_app_repr_change_mtu(repr->app, netdev, new_mtu);
+ if (err)
+ return err;
+
+ netdev->mtu = new_mtu;
+
+ return 0;
}
static netdev_tx_t nfp_repr_xmit(struct sk_buff *skb, struct net_device *netdev)
diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/Makefile b/drivers/net/ethernet/netronome/nfp/nfpcore/Makefile
new file mode 100644
index 000000000000..805fa28f391a
--- /dev/null
+++ b/drivers/net/ethernet/netronome/nfp/nfpcore/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+# kbuild requires Makefile in a directory to build individual objects
diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp6000/Makefile b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp6000/Makefile
new file mode 100644
index 000000000000..805fa28f391a
--- /dev/null
+++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp6000/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+# kbuild requires Makefile in a directory to build individual objects
diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_mutex.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_mutex.c
index f7b958181126..cb28ac03e4ca 100644
--- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_mutex.c
+++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_mutex.c
@@ -211,8 +211,11 @@ int nfp_cpp_mutex_lock(struct nfp_cpp_mutex *mutex)
break;
err = msleep_interruptible(timeout_ms);
- if (err != 0)
+ if (err != 0) {
+ nfp_info(mutex->cpp,
+ "interrupted waiting for NFP mutex\n");
return -ERESTARTSYS;
+ }
if (time_is_before_eq_jiffies(warn_at)) {
warn_at = jiffies + NFP_MUTEX_WAIT_NEXT_WARN * HZ;
diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c
index 39abac678b71..2abee0fe3a7c 100644
--- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c
+++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c
@@ -71,10 +71,11 @@
/* CPP address to retrieve the data from */
#define NSP_BUFFER 0x10
#define NSP_BUFFER_CPP GENMASK_ULL(63, 40)
-#define NSP_BUFFER_PCIE GENMASK_ULL(39, 38)
-#define NSP_BUFFER_ADDRESS GENMASK_ULL(37, 0)
+#define NSP_BUFFER_ADDRESS GENMASK_ULL(39, 0)
#define NSP_DFLT_BUFFER 0x18
+#define NSP_DFLT_BUFFER_CPP GENMASK_ULL(63, 40)
+#define NSP_DFLT_BUFFER_ADDRESS GENMASK_ULL(39, 0)
#define NSP_DFLT_BUFFER_CONFIG 0x20
#define NSP_DFLT_BUFFER_SIZE_MB GENMASK_ULL(7, 0)
@@ -280,8 +281,7 @@ nfp_nsp_wait_reg(struct nfp_cpp *cpp, u64 *reg, u32 nsp_cpp, u64 addr,
if ((*reg & mask) == val)
return 0;
- if (msleep_interruptible(25))
- return -ERESTARTSYS;
+ msleep(25);
if (time_after(start_time, wait_until))
return -ETIMEDOUT;
@@ -427,8 +427,8 @@ __nfp_nsp_command_buf(struct nfp_nsp *nsp, u16 code, u32 option,
if (err < 0)
return err;
- cpp_id = FIELD_GET(NSP_BUFFER_CPP, reg) << 8;
- cpp_buf = FIELD_GET(NSP_BUFFER_ADDRESS, reg);
+ cpp_id = FIELD_GET(NSP_DFLT_BUFFER_CPP, reg) << 8;
+ cpp_buf = FIELD_GET(NSP_DFLT_BUFFER_ADDRESS, reg);
if (in_buf && in_size) {
err = nfp_cpp_write(cpp, cpp_id, cpp_buf, in_buf, in_size);
diff --git a/drivers/net/ethernet/netronome/nfp/nic/Makefile b/drivers/net/ethernet/netronome/nfp/nic/Makefile
new file mode 100644
index 000000000000..805fa28f391a
--- /dev/null
+++ b/drivers/net/ethernet/netronome/nfp/nic/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+# kbuild requires Makefile in a directory to build individual objects
diff --git a/drivers/net/ethernet/ni/Kconfig b/drivers/net/ethernet/ni/Kconfig
new file mode 100644
index 000000000000..aa41e5f6e437
--- /dev/null
+++ b/drivers/net/ethernet/ni/Kconfig
@@ -0,0 +1,27 @@
+#
+# National Instuments network device configuration
+#
+
+config NET_VENDOR_NI
+ bool "National Instruments Devices"
+ default y
+ help
+ If you have a network (Ethernet) device belonging to this class, say Y.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about National Instrument devices.
+ If you say Y, you will be asked for your specific device in the
+ following questions.
+
+if NET_VENDOR_NI
+
+config NI_XGE_MANAGEMENT_ENET
+ tristate "National Instruments XGE management enet support"
+ depends on ARCH_ZYNQ
+ select PHYLIB
+ help
+ Simple LAN device for debug or management purposes. Can
+ support either 10G or 1G PHYs via SFP+ ports.
+
+endif
diff --git a/drivers/net/ethernet/ni/Makefile b/drivers/net/ethernet/ni/Makefile
new file mode 100644
index 000000000000..99c664651c51
--- /dev/null
+++ b/drivers/net/ethernet/ni/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_NI_XGE_MANAGEMENT_ENET) += nixge.o
diff --git a/drivers/net/ethernet/ni/nixge.c b/drivers/net/ethernet/ni/nixge.c
new file mode 100644
index 000000000000..27364b7572fc
--- /dev/null
+++ b/drivers/net/ethernet/ni/nixge.c
@@ -0,0 +1,1310 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2016-2017, National Instruments Corp.
+ *
+ * Author: Moritz Fischer <mdf@kernel.org>
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of_address.h>
+#include <linux/of_mdio.h>
+#include <linux/of_net.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+#include <linux/skbuff.h>
+#include <linux/phy.h>
+#include <linux/mii.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/ethtool.h>
+#include <linux/iopoll.h>
+
+#define TX_BD_NUM 64
+#define RX_BD_NUM 128
+
+/* Axi DMA Register definitions */
+#define XAXIDMA_TX_CR_OFFSET 0x00 /* Channel control */
+#define XAXIDMA_TX_SR_OFFSET 0x04 /* Status */
+#define XAXIDMA_TX_CDESC_OFFSET 0x08 /* Current descriptor pointer */
+#define XAXIDMA_TX_TDESC_OFFSET 0x10 /* Tail descriptor pointer */
+
+#define XAXIDMA_RX_CR_OFFSET 0x30 /* Channel control */
+#define XAXIDMA_RX_SR_OFFSET 0x34 /* Status */
+#define XAXIDMA_RX_CDESC_OFFSET 0x38 /* Current descriptor pointer */
+#define XAXIDMA_RX_TDESC_OFFSET 0x40 /* Tail descriptor pointer */
+
+#define XAXIDMA_CR_RUNSTOP_MASK 0x1 /* Start/stop DMA channel */
+#define XAXIDMA_CR_RESET_MASK 0x4 /* Reset DMA engine */
+
+#define XAXIDMA_BD_CTRL_LENGTH_MASK 0x007FFFFF /* Requested len */
+#define XAXIDMA_BD_CTRL_TXSOF_MASK 0x08000000 /* First tx packet */
+#define XAXIDMA_BD_CTRL_TXEOF_MASK 0x04000000 /* Last tx packet */
+#define XAXIDMA_BD_CTRL_ALL_MASK 0x0C000000 /* All control bits */
+
+#define XAXIDMA_DELAY_MASK 0xFF000000 /* Delay timeout counter */
+#define XAXIDMA_COALESCE_MASK 0x00FF0000 /* Coalesce counter */
+
+#define XAXIDMA_DELAY_SHIFT 24
+#define XAXIDMA_COALESCE_SHIFT 16
+
+#define XAXIDMA_IRQ_IOC_MASK 0x00001000 /* Completion intr */
+#define XAXIDMA_IRQ_DELAY_MASK 0x00002000 /* Delay interrupt */
+#define XAXIDMA_IRQ_ERROR_MASK 0x00004000 /* Error interrupt */
+#define XAXIDMA_IRQ_ALL_MASK 0x00007000 /* All interrupts */
+
+/* Default TX/RX Threshold and waitbound values for SGDMA mode */
+#define XAXIDMA_DFT_TX_THRESHOLD 24
+#define XAXIDMA_DFT_TX_WAITBOUND 254
+#define XAXIDMA_DFT_RX_THRESHOLD 24
+#define XAXIDMA_DFT_RX_WAITBOUND 254
+
+#define XAXIDMA_BD_STS_ACTUAL_LEN_MASK 0x007FFFFF /* Actual len */
+#define XAXIDMA_BD_STS_COMPLETE_MASK 0x80000000 /* Completed */
+#define XAXIDMA_BD_STS_DEC_ERR_MASK 0x40000000 /* Decode error */
+#define XAXIDMA_BD_STS_SLV_ERR_MASK 0x20000000 /* Slave error */
+#define XAXIDMA_BD_STS_INT_ERR_MASK 0x10000000 /* Internal err */
+#define XAXIDMA_BD_STS_ALL_ERR_MASK 0x70000000 /* All errors */
+#define XAXIDMA_BD_STS_RXSOF_MASK 0x08000000 /* First rx pkt */
+#define XAXIDMA_BD_STS_RXEOF_MASK 0x04000000 /* Last rx pkt */
+#define XAXIDMA_BD_STS_ALL_MASK 0xFC000000 /* All status bits */
+
+#define NIXGE_REG_CTRL_OFFSET 0x4000
+#define NIXGE_REG_INFO 0x00
+#define NIXGE_REG_MAC_CTL 0x04
+#define NIXGE_REG_PHY_CTL 0x08
+#define NIXGE_REG_LED_CTL 0x0c
+#define NIXGE_REG_MDIO_DATA 0x10
+#define NIXGE_REG_MDIO_ADDR 0x14
+#define NIXGE_REG_MDIO_OP 0x18
+#define NIXGE_REG_MDIO_CTRL 0x1c
+
+#define NIXGE_ID_LED_CTL_EN BIT(0)
+#define NIXGE_ID_LED_CTL_VAL BIT(1)
+
+#define NIXGE_MDIO_CLAUSE45 BIT(12)
+#define NIXGE_MDIO_CLAUSE22 0
+#define NIXGE_MDIO_OP(n) (((n) & 0x3) << 10)
+#define NIXGE_MDIO_OP_ADDRESS 0
+#define NIXGE_MDIO_C45_WRITE BIT(0)
+#define NIXGE_MDIO_C45_READ (BIT(1) | BIT(0))
+#define NIXGE_MDIO_C22_WRITE BIT(0)
+#define NIXGE_MDIO_C22_READ BIT(1)
+#define NIXGE_MDIO_ADDR(n) (((n) & 0x1f) << 5)
+#define NIXGE_MDIO_MMD(n) (((n) & 0x1f) << 0)
+
+#define NIXGE_REG_MAC_LSB 0x1000
+#define NIXGE_REG_MAC_MSB 0x1004
+
+/* Packet size info */
+#define NIXGE_HDR_SIZE 14 /* Size of Ethernet header */
+#define NIXGE_TRL_SIZE 4 /* Size of Ethernet trailer (FCS) */
+#define NIXGE_MTU 1500 /* Max MTU of an Ethernet frame */
+#define NIXGE_JUMBO_MTU 9000 /* Max MTU of a jumbo Eth. frame */
+
+#define NIXGE_MAX_FRAME_SIZE (NIXGE_MTU + NIXGE_HDR_SIZE + NIXGE_TRL_SIZE)
+#define NIXGE_MAX_JUMBO_FRAME_SIZE \
+ (NIXGE_JUMBO_MTU + NIXGE_HDR_SIZE + NIXGE_TRL_SIZE)
+
+struct nixge_hw_dma_bd {
+ u32 next;
+ u32 reserved1;
+ u32 phys;
+ u32 reserved2;
+ u32 reserved3;
+ u32 reserved4;
+ u32 cntrl;
+ u32 status;
+ u32 app0;
+ u32 app1;
+ u32 app2;
+ u32 app3;
+ u32 app4;
+ u32 sw_id_offset;
+ u32 reserved5;
+ u32 reserved6;
+};
+
+struct nixge_tx_skb {
+ struct sk_buff *skb;
+ dma_addr_t mapping;
+ size_t size;
+ bool mapped_as_page;
+};
+
+struct nixge_priv {
+ struct net_device *ndev;
+ struct napi_struct napi;
+ struct device *dev;
+
+ /* Connection to PHY device */
+ struct device_node *phy_node;
+ phy_interface_t phy_mode;
+
+ int link;
+ unsigned int speed;
+ unsigned int duplex;
+
+ /* MDIO bus data */
+ struct mii_bus *mii_bus; /* MII bus reference */
+
+ /* IO registers, dma functions and IRQs */
+ void __iomem *ctrl_regs;
+ void __iomem *dma_regs;
+
+ struct tasklet_struct dma_err_tasklet;
+
+ int tx_irq;
+ int rx_irq;
+ u32 last_link;
+
+ /* Buffer descriptors */
+ struct nixge_hw_dma_bd *tx_bd_v;
+ struct nixge_tx_skb *tx_skb;
+ dma_addr_t tx_bd_p;
+
+ struct nixge_hw_dma_bd *rx_bd_v;
+ dma_addr_t rx_bd_p;
+ u32 tx_bd_ci;
+ u32 tx_bd_tail;
+ u32 rx_bd_ci;
+
+ u32 coalesce_count_rx;
+ u32 coalesce_count_tx;
+};
+
+static void nixge_dma_write_reg(struct nixge_priv *priv, off_t offset, u32 val)
+{
+ writel(val, priv->dma_regs + offset);
+}
+
+static u32 nixge_dma_read_reg(const struct nixge_priv *priv, off_t offset)
+{
+ return readl(priv->dma_regs + offset);
+}
+
+static void nixge_ctrl_write_reg(struct nixge_priv *priv, off_t offset, u32 val)
+{
+ writel(val, priv->ctrl_regs + offset);
+}
+
+static u32 nixge_ctrl_read_reg(struct nixge_priv *priv, off_t offset)
+{
+ return readl(priv->ctrl_regs + offset);
+}
+
+#define nixge_ctrl_poll_timeout(priv, addr, val, cond, sleep_us, timeout_us) \
+ readl_poll_timeout((priv)->ctrl_regs + (addr), (val), (cond), \
+ (sleep_us), (timeout_us))
+
+#define nixge_dma_poll_timeout(priv, addr, val, cond, sleep_us, timeout_us) \
+ readl_poll_timeout((priv)->dma_regs + (addr), (val), (cond), \
+ (sleep_us), (timeout_us))
+
+static void nixge_hw_dma_bd_release(struct net_device *ndev)
+{
+ struct nixge_priv *priv = netdev_priv(ndev);
+ int i;
+
+ for (i = 0; i < RX_BD_NUM; i++) {
+ dma_unmap_single(ndev->dev.parent, priv->rx_bd_v[i].phys,
+ NIXGE_MAX_JUMBO_FRAME_SIZE, DMA_FROM_DEVICE);
+ dev_kfree_skb((struct sk_buff *)
+ (priv->rx_bd_v[i].sw_id_offset));
+ }
+
+ if (priv->rx_bd_v)
+ dma_free_coherent(ndev->dev.parent,
+ sizeof(*priv->rx_bd_v) * RX_BD_NUM,
+ priv->rx_bd_v,
+ priv->rx_bd_p);
+
+ if (priv->tx_skb)
+ devm_kfree(ndev->dev.parent, priv->tx_skb);
+
+ if (priv->tx_bd_v)
+ dma_free_coherent(ndev->dev.parent,
+ sizeof(*priv->tx_bd_v) * TX_BD_NUM,
+ priv->tx_bd_v,
+ priv->tx_bd_p);
+}
+
+static int nixge_hw_dma_bd_init(struct net_device *ndev)
+{
+ struct nixge_priv *priv = netdev_priv(ndev);
+ struct sk_buff *skb;
+ u32 cr;
+ int i;
+
+ /* Reset the indexes which are used for accessing the BDs */
+ priv->tx_bd_ci = 0;
+ priv->tx_bd_tail = 0;
+ priv->rx_bd_ci = 0;
+
+ /* Allocate the Tx and Rx buffer descriptors. */
+ priv->tx_bd_v = dma_zalloc_coherent(ndev->dev.parent,
+ sizeof(*priv->tx_bd_v) * TX_BD_NUM,
+ &priv->tx_bd_p, GFP_KERNEL);
+ if (!priv->tx_bd_v)
+ goto out;
+
+ priv->tx_skb = devm_kzalloc(ndev->dev.parent,
+ sizeof(*priv->tx_skb) *
+ TX_BD_NUM,
+ GFP_KERNEL);
+ if (!priv->tx_skb)
+ goto out;
+
+ priv->rx_bd_v = dma_zalloc_coherent(ndev->dev.parent,
+ sizeof(*priv->rx_bd_v) * RX_BD_NUM,
+ &priv->rx_bd_p, GFP_KERNEL);
+ if (!priv->rx_bd_v)
+ goto out;
+
+ for (i = 0; i < TX_BD_NUM; i++) {
+ priv->tx_bd_v[i].next = priv->tx_bd_p +
+ sizeof(*priv->tx_bd_v) *
+ ((i + 1) % TX_BD_NUM);
+ }
+
+ for (i = 0; i < RX_BD_NUM; i++) {
+ priv->rx_bd_v[i].next = priv->rx_bd_p +
+ sizeof(*priv->rx_bd_v) *
+ ((i + 1) % RX_BD_NUM);
+
+ skb = netdev_alloc_skb_ip_align(ndev,
+ NIXGE_MAX_JUMBO_FRAME_SIZE);
+ if (!skb)
+ goto out;
+
+ priv->rx_bd_v[i].sw_id_offset = (u32)skb;
+ priv->rx_bd_v[i].phys =
+ dma_map_single(ndev->dev.parent,
+ skb->data,
+ NIXGE_MAX_JUMBO_FRAME_SIZE,
+ DMA_FROM_DEVICE);
+ priv->rx_bd_v[i].cntrl = NIXGE_MAX_JUMBO_FRAME_SIZE;
+ }
+
+ /* Start updating the Rx channel control register */
+ cr = nixge_dma_read_reg(priv, XAXIDMA_RX_CR_OFFSET);
+ /* Update the interrupt coalesce count */
+ cr = ((cr & ~XAXIDMA_COALESCE_MASK) |
+ ((priv->coalesce_count_rx) << XAXIDMA_COALESCE_SHIFT));
+ /* Update the delay timer count */
+ cr = ((cr & ~XAXIDMA_DELAY_MASK) |
+ (XAXIDMA_DFT_RX_WAITBOUND << XAXIDMA_DELAY_SHIFT));
+ /* Enable coalesce, delay timer and error interrupts */
+ cr |= XAXIDMA_IRQ_ALL_MASK;
+ /* Write to the Rx channel control register */
+ nixge_dma_write_reg(priv, XAXIDMA_RX_CR_OFFSET, cr);
+
+ /* Start updating the Tx channel control register */
+ cr = nixge_dma_read_reg(priv, XAXIDMA_TX_CR_OFFSET);
+ /* Update the interrupt coalesce count */
+ cr = (((cr & ~XAXIDMA_COALESCE_MASK)) |
+ ((priv->coalesce_count_tx) << XAXIDMA_COALESCE_SHIFT));
+ /* Update the delay timer count */
+ cr = (((cr & ~XAXIDMA_DELAY_MASK)) |
+ (XAXIDMA_DFT_TX_WAITBOUND << XAXIDMA_DELAY_SHIFT));
+ /* Enable coalesce, delay timer and error interrupts */
+ cr |= XAXIDMA_IRQ_ALL_MASK;
+ /* Write to the Tx channel control register */
+ nixge_dma_write_reg(priv, XAXIDMA_TX_CR_OFFSET, cr);
+
+ /* Populate the tail pointer and bring the Rx Axi DMA engine out of
+ * halted state. This will make the Rx side ready for reception.
+ */
+ nixge_dma_write_reg(priv, XAXIDMA_RX_CDESC_OFFSET, priv->rx_bd_p);
+ cr = nixge_dma_read_reg(priv, XAXIDMA_RX_CR_OFFSET);
+ nixge_dma_write_reg(priv, XAXIDMA_RX_CR_OFFSET,
+ cr | XAXIDMA_CR_RUNSTOP_MASK);
+ nixge_dma_write_reg(priv, XAXIDMA_RX_TDESC_OFFSET, priv->rx_bd_p +
+ (sizeof(*priv->rx_bd_v) * (RX_BD_NUM - 1)));
+
+ /* Write to the RS (Run-stop) bit in the Tx channel control register.
+ * Tx channel is now ready to run. But only after we write to the
+ * tail pointer register that the Tx channel will start transmitting.
+ */
+ nixge_dma_write_reg(priv, XAXIDMA_TX_CDESC_OFFSET, priv->tx_bd_p);
+ cr = nixge_dma_read_reg(priv, XAXIDMA_TX_CR_OFFSET);
+ nixge_dma_write_reg(priv, XAXIDMA_TX_CR_OFFSET,
+ cr | XAXIDMA_CR_RUNSTOP_MASK);
+
+ return 0;
+out:
+ nixge_hw_dma_bd_release(ndev);
+ return -ENOMEM;
+}
+
+static void __nixge_device_reset(struct nixge_priv *priv, off_t offset)
+{
+ u32 status;
+ int err;
+
+ /* Reset Axi DMA. This would reset NIXGE Ethernet core as well.
+ * The reset process of Axi DMA takes a while to complete as all
+ * pending commands/transfers will be flushed or completed during
+ * this reset process.
+ */
+ nixge_dma_write_reg(priv, offset, XAXIDMA_CR_RESET_MASK);
+ err = nixge_dma_poll_timeout(priv, offset, status,
+ !(status & XAXIDMA_CR_RESET_MASK), 10,
+ 1000);
+ if (err)
+ netdev_err(priv->ndev, "%s: DMA reset timeout!\n", __func__);
+}
+
+static void nixge_device_reset(struct net_device *ndev)
+{
+ struct nixge_priv *priv = netdev_priv(ndev);
+
+ __nixge_device_reset(priv, XAXIDMA_TX_CR_OFFSET);
+ __nixge_device_reset(priv, XAXIDMA_RX_CR_OFFSET);
+
+ if (nixge_hw_dma_bd_init(ndev))
+ netdev_err(ndev, "%s: descriptor allocation failed\n",
+ __func__);
+
+ netif_trans_update(ndev);
+}
+
+static void nixge_handle_link_change(struct net_device *ndev)
+{
+ struct nixge_priv *priv = netdev_priv(ndev);
+ struct phy_device *phydev = ndev->phydev;
+
+ if (phydev->link != priv->link || phydev->speed != priv->speed ||
+ phydev->duplex != priv->duplex) {
+ priv->link = phydev->link;
+ priv->speed = phydev->speed;
+ priv->duplex = phydev->duplex;
+ phy_print_status(phydev);
+ }
+}
+
+static void nixge_tx_skb_unmap(struct nixge_priv *priv,
+ struct nixge_tx_skb *tx_skb)
+{
+ if (tx_skb->mapping) {
+ if (tx_skb->mapped_as_page)
+ dma_unmap_page(priv->ndev->dev.parent, tx_skb->mapping,
+ tx_skb->size, DMA_TO_DEVICE);
+ else
+ dma_unmap_single(priv->ndev->dev.parent,
+ tx_skb->mapping,
+ tx_skb->size, DMA_TO_DEVICE);
+ tx_skb->mapping = 0;
+ }
+
+ if (tx_skb->skb) {
+ dev_kfree_skb_any(tx_skb->skb);
+ tx_skb->skb = NULL;
+ }
+}
+
+static void nixge_start_xmit_done(struct net_device *ndev)
+{
+ struct nixge_priv *priv = netdev_priv(ndev);
+ struct nixge_hw_dma_bd *cur_p;
+ struct nixge_tx_skb *tx_skb;
+ unsigned int status = 0;
+ u32 packets = 0;
+ u32 size = 0;
+
+ cur_p = &priv->tx_bd_v[priv->tx_bd_ci];
+ tx_skb = &priv->tx_skb[priv->tx_bd_ci];
+
+ status = cur_p->status;
+
+ while (status & XAXIDMA_BD_STS_COMPLETE_MASK) {
+ nixge_tx_skb_unmap(priv, tx_skb);
+ cur_p->status = 0;
+
+ size += status & XAXIDMA_BD_STS_ACTUAL_LEN_MASK;
+ packets++;
+
+ ++priv->tx_bd_ci;
+ priv->tx_bd_ci %= TX_BD_NUM;
+ cur_p = &priv->tx_bd_v[priv->tx_bd_ci];
+ tx_skb = &priv->tx_skb[priv->tx_bd_ci];
+ status = cur_p->status;
+ }
+
+ ndev->stats.tx_packets += packets;
+ ndev->stats.tx_bytes += size;
+
+ if (packets)
+ netif_wake_queue(ndev);
+}
+
+static int nixge_check_tx_bd_space(struct nixge_priv *priv,
+ int num_frag)
+{
+ struct nixge_hw_dma_bd *cur_p;
+
+ cur_p = &priv->tx_bd_v[(priv->tx_bd_tail + num_frag) % TX_BD_NUM];
+ if (cur_p->status & XAXIDMA_BD_STS_ALL_MASK)
+ return NETDEV_TX_BUSY;
+ return 0;
+}
+
+static int nixge_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct nixge_priv *priv = netdev_priv(ndev);
+ struct nixge_hw_dma_bd *cur_p;
+ struct nixge_tx_skb *tx_skb;
+ dma_addr_t tail_p;
+ skb_frag_t *frag;
+ u32 num_frag;
+ u32 ii;
+
+ num_frag = skb_shinfo(skb)->nr_frags;
+ cur_p = &priv->tx_bd_v[priv->tx_bd_tail];
+ tx_skb = &priv->tx_skb[priv->tx_bd_tail];
+
+ if (nixge_check_tx_bd_space(priv, num_frag)) {
+ if (!netif_queue_stopped(ndev))
+ netif_stop_queue(ndev);
+ return NETDEV_TX_OK;
+ }
+
+ cur_p->phys = dma_map_single(ndev->dev.parent, skb->data,
+ skb_headlen(skb), DMA_TO_DEVICE);
+ if (dma_mapping_error(ndev->dev.parent, cur_p->phys))
+ goto drop;
+
+ cur_p->cntrl = skb_headlen(skb) | XAXIDMA_BD_CTRL_TXSOF_MASK;
+
+ tx_skb->skb = NULL;
+ tx_skb->mapping = cur_p->phys;
+ tx_skb->size = skb_headlen(skb);
+ tx_skb->mapped_as_page = false;
+
+ for (ii = 0; ii < num_frag; ii++) {
+ ++priv->tx_bd_tail;
+ priv->tx_bd_tail %= TX_BD_NUM;
+ cur_p = &priv->tx_bd_v[priv->tx_bd_tail];
+ tx_skb = &priv->tx_skb[priv->tx_bd_tail];
+ frag = &skb_shinfo(skb)->frags[ii];
+
+ cur_p->phys = skb_frag_dma_map(ndev->dev.parent, frag, 0,
+ skb_frag_size(frag),
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(ndev->dev.parent, cur_p->phys))
+ goto frag_err;
+
+ cur_p->cntrl = skb_frag_size(frag);
+
+ tx_skb->skb = NULL;
+ tx_skb->mapping = cur_p->phys;
+ tx_skb->size = skb_frag_size(frag);
+ tx_skb->mapped_as_page = true;
+ }
+
+ /* last buffer of the frame */
+ tx_skb->skb = skb;
+
+ cur_p->cntrl |= XAXIDMA_BD_CTRL_TXEOF_MASK;
+ cur_p->app4 = (unsigned long)skb;
+
+ tail_p = priv->tx_bd_p + sizeof(*priv->tx_bd_v) * priv->tx_bd_tail;
+ /* Start the transfer */
+ nixge_dma_write_reg(priv, XAXIDMA_TX_TDESC_OFFSET, tail_p);
+ ++priv->tx_bd_tail;
+ priv->tx_bd_tail %= TX_BD_NUM;
+
+ return NETDEV_TX_OK;
+frag_err:
+ for (; ii > 0; ii--) {
+ if (priv->tx_bd_tail)
+ priv->tx_bd_tail--;
+ else
+ priv->tx_bd_tail = TX_BD_NUM - 1;
+
+ tx_skb = &priv->tx_skb[priv->tx_bd_tail];
+ nixge_tx_skb_unmap(priv, tx_skb);
+
+ cur_p = &priv->tx_bd_v[priv->tx_bd_tail];
+ cur_p->status = 0;
+ }
+ dma_unmap_single(priv->ndev->dev.parent,
+ tx_skb->mapping,
+ tx_skb->size, DMA_TO_DEVICE);
+drop:
+ ndev->stats.tx_dropped++;
+ return NETDEV_TX_OK;
+}
+
+static int nixge_recv(struct net_device *ndev, int budget)
+{
+ struct nixge_priv *priv = netdev_priv(ndev);
+ struct sk_buff *skb, *new_skb;
+ struct nixge_hw_dma_bd *cur_p;
+ dma_addr_t tail_p = 0;
+ u32 packets = 0;
+ u32 length = 0;
+ u32 size = 0;
+
+ cur_p = &priv->rx_bd_v[priv->rx_bd_ci];
+
+ while ((cur_p->status & XAXIDMA_BD_STS_COMPLETE_MASK &&
+ budget > packets)) {
+ tail_p = priv->rx_bd_p + sizeof(*priv->rx_bd_v) *
+ priv->rx_bd_ci;
+
+ skb = (struct sk_buff *)(cur_p->sw_id_offset);
+
+ length = cur_p->status & XAXIDMA_BD_STS_ACTUAL_LEN_MASK;
+ if (length > NIXGE_MAX_JUMBO_FRAME_SIZE)
+ length = NIXGE_MAX_JUMBO_FRAME_SIZE;
+
+ dma_unmap_single(ndev->dev.parent, cur_p->phys,
+ NIXGE_MAX_JUMBO_FRAME_SIZE,
+ DMA_FROM_DEVICE);
+
+ skb_put(skb, length);
+
+ skb->protocol = eth_type_trans(skb, ndev);
+ skb_checksum_none_assert(skb);
+
+ /* For now mark them as CHECKSUM_NONE since
+ * we don't have offload capabilities
+ */
+ skb->ip_summed = CHECKSUM_NONE;
+
+ napi_gro_receive(&priv->napi, skb);
+
+ size += length;
+ packets++;
+
+ new_skb = netdev_alloc_skb_ip_align(ndev,
+ NIXGE_MAX_JUMBO_FRAME_SIZE);
+ if (!new_skb)
+ return packets;
+
+ cur_p->phys = dma_map_single(ndev->dev.parent, new_skb->data,
+ NIXGE_MAX_JUMBO_FRAME_SIZE,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(ndev->dev.parent, cur_p->phys)) {
+ /* FIXME: bail out and clean up */
+ netdev_err(ndev, "Failed to map ...\n");
+ }
+ cur_p->cntrl = NIXGE_MAX_JUMBO_FRAME_SIZE;
+ cur_p->status = 0;
+ cur_p->sw_id_offset = (u32)new_skb;
+
+ ++priv->rx_bd_ci;
+ priv->rx_bd_ci %= RX_BD_NUM;
+ cur_p = &priv->rx_bd_v[priv->rx_bd_ci];
+ }
+
+ ndev->stats.rx_packets += packets;
+ ndev->stats.rx_bytes += size;
+
+ if (tail_p)
+ nixge_dma_write_reg(priv, XAXIDMA_RX_TDESC_OFFSET, tail_p);
+
+ return packets;
+}
+
+static int nixge_poll(struct napi_struct *napi, int budget)
+{
+ struct nixge_priv *priv = container_of(napi, struct nixge_priv, napi);
+ int work_done;
+ u32 status, cr;
+
+ work_done = 0;
+
+ work_done = nixge_recv(priv->ndev, budget);
+ if (work_done < budget) {
+ napi_complete_done(napi, work_done);
+ status = nixge_dma_read_reg(priv, XAXIDMA_RX_SR_OFFSET);
+
+ if (status & (XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_DELAY_MASK)) {
+ /* If there's more, reschedule, but clear */
+ nixge_dma_write_reg(priv, XAXIDMA_RX_SR_OFFSET, status);
+ napi_reschedule(napi);
+ } else {
+ /* if not, turn on RX IRQs again ... */
+ cr = nixge_dma_read_reg(priv, XAXIDMA_RX_CR_OFFSET);
+ cr |= (XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_DELAY_MASK);
+ nixge_dma_write_reg(priv, XAXIDMA_RX_CR_OFFSET, cr);
+ }
+ }
+
+ return work_done;
+}
+
+static irqreturn_t nixge_tx_irq(int irq, void *_ndev)
+{
+ struct nixge_priv *priv = netdev_priv(_ndev);
+ struct net_device *ndev = _ndev;
+ unsigned int status;
+ u32 cr;
+
+ status = nixge_dma_read_reg(priv, XAXIDMA_TX_SR_OFFSET);
+ if (status & (XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_DELAY_MASK)) {
+ nixge_dma_write_reg(priv, XAXIDMA_TX_SR_OFFSET, status);
+ nixge_start_xmit_done(priv->ndev);
+ goto out;
+ }
+ if (!(status & XAXIDMA_IRQ_ALL_MASK)) {
+ netdev_err(ndev, "No interrupts asserted in Tx path\n");
+ return IRQ_NONE;
+ }
+ if (status & XAXIDMA_IRQ_ERROR_MASK) {
+ netdev_err(ndev, "DMA Tx error 0x%x\n", status);
+ netdev_err(ndev, "Current BD is at: 0x%x\n",
+ (priv->tx_bd_v[priv->tx_bd_ci]).phys);
+
+ cr = nixge_dma_read_reg(priv, XAXIDMA_TX_CR_OFFSET);
+ /* Disable coalesce, delay timer and error interrupts */
+ cr &= (~XAXIDMA_IRQ_ALL_MASK);
+ /* Write to the Tx channel control register */
+ nixge_dma_write_reg(priv, XAXIDMA_TX_CR_OFFSET, cr);
+
+ cr = nixge_dma_read_reg(priv, XAXIDMA_RX_CR_OFFSET);
+ /* Disable coalesce, delay timer and error interrupts */
+ cr &= (~XAXIDMA_IRQ_ALL_MASK);
+ /* Write to the Rx channel control register */
+ nixge_dma_write_reg(priv, XAXIDMA_RX_CR_OFFSET, cr);
+
+ tasklet_schedule(&priv->dma_err_tasklet);
+ nixge_dma_write_reg(priv, XAXIDMA_TX_SR_OFFSET, status);
+ }
+out:
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t nixge_rx_irq(int irq, void *_ndev)
+{
+ struct nixge_priv *priv = netdev_priv(_ndev);
+ struct net_device *ndev = _ndev;
+ unsigned int status;
+ u32 cr;
+
+ status = nixge_dma_read_reg(priv, XAXIDMA_RX_SR_OFFSET);
+ if (status & (XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_DELAY_MASK)) {
+ /* Turn of IRQs because NAPI */
+ nixge_dma_write_reg(priv, XAXIDMA_RX_SR_OFFSET, status);
+ cr = nixge_dma_read_reg(priv, XAXIDMA_RX_CR_OFFSET);
+ cr &= ~(XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_DELAY_MASK);
+ nixge_dma_write_reg(priv, XAXIDMA_RX_CR_OFFSET, cr);
+
+ if (napi_schedule_prep(&priv->napi))
+ __napi_schedule(&priv->napi);
+ goto out;
+ }
+ if (!(status & XAXIDMA_IRQ_ALL_MASK)) {
+ netdev_err(ndev, "No interrupts asserted in Rx path\n");
+ return IRQ_NONE;
+ }
+ if (status & XAXIDMA_IRQ_ERROR_MASK) {
+ netdev_err(ndev, "DMA Rx error 0x%x\n", status);
+ netdev_err(ndev, "Current BD is at: 0x%x\n",
+ (priv->rx_bd_v[priv->rx_bd_ci]).phys);
+
+ cr = nixge_dma_read_reg(priv, XAXIDMA_TX_CR_OFFSET);
+ /* Disable coalesce, delay timer and error interrupts */
+ cr &= (~XAXIDMA_IRQ_ALL_MASK);
+ /* Finally write to the Tx channel control register */
+ nixge_dma_write_reg(priv, XAXIDMA_TX_CR_OFFSET, cr);
+
+ cr = nixge_dma_read_reg(priv, XAXIDMA_RX_CR_OFFSET);
+ /* Disable coalesce, delay timer and error interrupts */
+ cr &= (~XAXIDMA_IRQ_ALL_MASK);
+ /* write to the Rx channel control register */
+ nixge_dma_write_reg(priv, XAXIDMA_RX_CR_OFFSET, cr);
+
+ tasklet_schedule(&priv->dma_err_tasklet);
+ nixge_dma_write_reg(priv, XAXIDMA_RX_SR_OFFSET, status);
+ }
+out:
+ return IRQ_HANDLED;
+}
+
+static void nixge_dma_err_handler(unsigned long data)
+{
+ struct nixge_priv *lp = (struct nixge_priv *)data;
+ struct nixge_hw_dma_bd *cur_p;
+ struct nixge_tx_skb *tx_skb;
+ u32 cr, i;
+
+ __nixge_device_reset(lp, XAXIDMA_TX_CR_OFFSET);
+ __nixge_device_reset(lp, XAXIDMA_RX_CR_OFFSET);
+
+ for (i = 0; i < TX_BD_NUM; i++) {
+ cur_p = &lp->tx_bd_v[i];
+ tx_skb = &lp->tx_skb[i];
+ nixge_tx_skb_unmap(lp, tx_skb);
+
+ cur_p->phys = 0;
+ cur_p->cntrl = 0;
+ cur_p->status = 0;
+ cur_p->app0 = 0;
+ cur_p->app1 = 0;
+ cur_p->app2 = 0;
+ cur_p->app3 = 0;
+ cur_p->app4 = 0;
+ cur_p->sw_id_offset = 0;
+ }
+
+ for (i = 0; i < RX_BD_NUM; i++) {
+ cur_p = &lp->rx_bd_v[i];
+ cur_p->status = 0;
+ cur_p->app0 = 0;
+ cur_p->app1 = 0;
+ cur_p->app2 = 0;
+ cur_p->app3 = 0;
+ cur_p->app4 = 0;
+ }
+
+ lp->tx_bd_ci = 0;
+ lp->tx_bd_tail = 0;
+ lp->rx_bd_ci = 0;
+
+ /* Start updating the Rx channel control register */
+ cr = nixge_dma_read_reg(lp, XAXIDMA_RX_CR_OFFSET);
+ /* Update the interrupt coalesce count */
+ cr = ((cr & ~XAXIDMA_COALESCE_MASK) |
+ (XAXIDMA_DFT_RX_THRESHOLD << XAXIDMA_COALESCE_SHIFT));
+ /* Update the delay timer count */
+ cr = ((cr & ~XAXIDMA_DELAY_MASK) |
+ (XAXIDMA_DFT_RX_WAITBOUND << XAXIDMA_DELAY_SHIFT));
+ /* Enable coalesce, delay timer and error interrupts */
+ cr |= XAXIDMA_IRQ_ALL_MASK;
+ /* Finally write to the Rx channel control register */
+ nixge_dma_write_reg(lp, XAXIDMA_RX_CR_OFFSET, cr);
+
+ /* Start updating the Tx channel control register */
+ cr = nixge_dma_read_reg(lp, XAXIDMA_TX_CR_OFFSET);
+ /* Update the interrupt coalesce count */
+ cr = (((cr & ~XAXIDMA_COALESCE_MASK)) |
+ (XAXIDMA_DFT_TX_THRESHOLD << XAXIDMA_COALESCE_SHIFT));
+ /* Update the delay timer count */
+ cr = (((cr & ~XAXIDMA_DELAY_MASK)) |
+ (XAXIDMA_DFT_TX_WAITBOUND << XAXIDMA_DELAY_SHIFT));
+ /* Enable coalesce, delay timer and error interrupts */
+ cr |= XAXIDMA_IRQ_ALL_MASK;
+ /* Finally write to the Tx channel control register */
+ nixge_dma_write_reg(lp, XAXIDMA_TX_CR_OFFSET, cr);
+
+ /* Populate the tail pointer and bring the Rx Axi DMA engine out of
+ * halted state. This will make the Rx side ready for reception.
+ */
+ nixge_dma_write_reg(lp, XAXIDMA_RX_CDESC_OFFSET, lp->rx_bd_p);
+ cr = nixge_dma_read_reg(lp, XAXIDMA_RX_CR_OFFSET);
+ nixge_dma_write_reg(lp, XAXIDMA_RX_CR_OFFSET,
+ cr | XAXIDMA_CR_RUNSTOP_MASK);
+ nixge_dma_write_reg(lp, XAXIDMA_RX_TDESC_OFFSET, lp->rx_bd_p +
+ (sizeof(*lp->rx_bd_v) * (RX_BD_NUM - 1)));
+
+ /* Write to the RS (Run-stop) bit in the Tx channel control register.
+ * Tx channel is now ready to run. But only after we write to the
+ * tail pointer register that the Tx channel will start transmitting
+ */
+ nixge_dma_write_reg(lp, XAXIDMA_TX_CDESC_OFFSET, lp->tx_bd_p);
+ cr = nixge_dma_read_reg(lp, XAXIDMA_TX_CR_OFFSET);
+ nixge_dma_write_reg(lp, XAXIDMA_TX_CR_OFFSET,
+ cr | XAXIDMA_CR_RUNSTOP_MASK);
+}
+
+static int nixge_open(struct net_device *ndev)
+{
+ struct nixge_priv *priv = netdev_priv(ndev);
+ struct phy_device *phy;
+ int ret;
+
+ nixge_device_reset(ndev);
+
+ phy = of_phy_connect(ndev, priv->phy_node,
+ &nixge_handle_link_change, 0, priv->phy_mode);
+ if (!phy)
+ return -ENODEV;
+
+ phy_start(phy);
+
+ /* Enable tasklets for Axi DMA error handling */
+ tasklet_init(&priv->dma_err_tasklet, nixge_dma_err_handler,
+ (unsigned long)priv);
+
+ napi_enable(&priv->napi);
+
+ /* Enable interrupts for Axi DMA Tx */
+ ret = request_irq(priv->tx_irq, nixge_tx_irq, 0, ndev->name, ndev);
+ if (ret)
+ goto err_tx_irq;
+ /* Enable interrupts for Axi DMA Rx */
+ ret = request_irq(priv->rx_irq, nixge_rx_irq, 0, ndev->name, ndev);
+ if (ret)
+ goto err_rx_irq;
+
+ netif_start_queue(ndev);
+
+ return 0;
+
+err_rx_irq:
+ free_irq(priv->tx_irq, ndev);
+err_tx_irq:
+ phy_stop(phy);
+ phy_disconnect(phy);
+ tasklet_kill(&priv->dma_err_tasklet);
+ netdev_err(ndev, "request_irq() failed\n");
+ return ret;
+}
+
+static int nixge_stop(struct net_device *ndev)
+{
+ struct nixge_priv *priv = netdev_priv(ndev);
+ u32 cr;
+
+ netif_stop_queue(ndev);
+ napi_disable(&priv->napi);
+
+ if (ndev->phydev) {
+ phy_stop(ndev->phydev);
+ phy_disconnect(ndev->phydev);
+ }
+
+ cr = nixge_dma_read_reg(priv, XAXIDMA_RX_CR_OFFSET);
+ nixge_dma_write_reg(priv, XAXIDMA_RX_CR_OFFSET,
+ cr & (~XAXIDMA_CR_RUNSTOP_MASK));
+ cr = nixge_dma_read_reg(priv, XAXIDMA_TX_CR_OFFSET);
+ nixge_dma_write_reg(priv, XAXIDMA_TX_CR_OFFSET,
+ cr & (~XAXIDMA_CR_RUNSTOP_MASK));
+
+ tasklet_kill(&priv->dma_err_tasklet);
+
+ free_irq(priv->tx_irq, ndev);
+ free_irq(priv->rx_irq, ndev);
+
+ nixge_hw_dma_bd_release(ndev);
+
+ return 0;
+}
+
+static int nixge_change_mtu(struct net_device *ndev, int new_mtu)
+{
+ if (netif_running(ndev))
+ return -EBUSY;
+
+ if ((new_mtu + NIXGE_HDR_SIZE + NIXGE_TRL_SIZE) >
+ NIXGE_MAX_JUMBO_FRAME_SIZE)
+ return -EINVAL;
+
+ ndev->mtu = new_mtu;
+
+ return 0;
+}
+
+static s32 __nixge_hw_set_mac_address(struct net_device *ndev)
+{
+ struct nixge_priv *priv = netdev_priv(ndev);
+
+ nixge_ctrl_write_reg(priv, NIXGE_REG_MAC_LSB,
+ (ndev->dev_addr[2]) << 24 |
+ (ndev->dev_addr[3] << 16) |
+ (ndev->dev_addr[4] << 8) |
+ (ndev->dev_addr[5] << 0));
+
+ nixge_ctrl_write_reg(priv, NIXGE_REG_MAC_MSB,
+ (ndev->dev_addr[1] | (ndev->dev_addr[0] << 8)));
+
+ return 0;
+}
+
+static int nixge_net_set_mac_address(struct net_device *ndev, void *p)
+{
+ int err;
+
+ err = eth_mac_addr(ndev, p);
+ if (!err)
+ __nixge_hw_set_mac_address(ndev);
+
+ return err;
+}
+
+static const struct net_device_ops nixge_netdev_ops = {
+ .ndo_open = nixge_open,
+ .ndo_stop = nixge_stop,
+ .ndo_start_xmit = nixge_start_xmit,
+ .ndo_change_mtu = nixge_change_mtu,
+ .ndo_set_mac_address = nixge_net_set_mac_address,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static void nixge_ethtools_get_drvinfo(struct net_device *ndev,
+ struct ethtool_drvinfo *ed)
+{
+ strlcpy(ed->driver, "nixge", sizeof(ed->driver));
+ strlcpy(ed->bus_info, "platform", sizeof(ed->driver));
+}
+
+static int nixge_ethtools_get_coalesce(struct net_device *ndev,
+ struct ethtool_coalesce *ecoalesce)
+{
+ struct nixge_priv *priv = netdev_priv(ndev);
+ u32 regval = 0;
+
+ regval = nixge_dma_read_reg(priv, XAXIDMA_RX_CR_OFFSET);
+ ecoalesce->rx_max_coalesced_frames = (regval & XAXIDMA_COALESCE_MASK)
+ >> XAXIDMA_COALESCE_SHIFT;
+ regval = nixge_dma_read_reg(priv, XAXIDMA_TX_CR_OFFSET);
+ ecoalesce->tx_max_coalesced_frames = (regval & XAXIDMA_COALESCE_MASK)
+ >> XAXIDMA_COALESCE_SHIFT;
+ return 0;
+}
+
+static int nixge_ethtools_set_coalesce(struct net_device *ndev,
+ struct ethtool_coalesce *ecoalesce)
+{
+ struct nixge_priv *priv = netdev_priv(ndev);
+
+ if (netif_running(ndev)) {
+ netdev_err(ndev,
+ "Please stop netif before applying configuration\n");
+ return -EBUSY;
+ }
+
+ if (ecoalesce->rx_coalesce_usecs ||
+ ecoalesce->rx_coalesce_usecs_irq ||
+ ecoalesce->rx_max_coalesced_frames_irq ||
+ ecoalesce->tx_coalesce_usecs ||
+ ecoalesce->tx_coalesce_usecs_irq ||
+ ecoalesce->tx_max_coalesced_frames_irq ||
+ ecoalesce->stats_block_coalesce_usecs ||
+ ecoalesce->use_adaptive_rx_coalesce ||
+ ecoalesce->use_adaptive_tx_coalesce ||
+ ecoalesce->pkt_rate_low ||
+ ecoalesce->rx_coalesce_usecs_low ||
+ ecoalesce->rx_max_coalesced_frames_low ||
+ ecoalesce->tx_coalesce_usecs_low ||
+ ecoalesce->tx_max_coalesced_frames_low ||
+ ecoalesce->pkt_rate_high ||
+ ecoalesce->rx_coalesce_usecs_high ||
+ ecoalesce->rx_max_coalesced_frames_high ||
+ ecoalesce->tx_coalesce_usecs_high ||
+ ecoalesce->tx_max_coalesced_frames_high ||
+ ecoalesce->rate_sample_interval)
+ return -EOPNOTSUPP;
+ if (ecoalesce->rx_max_coalesced_frames)
+ priv->coalesce_count_rx = ecoalesce->rx_max_coalesced_frames;
+ if (ecoalesce->tx_max_coalesced_frames)
+ priv->coalesce_count_tx = ecoalesce->tx_max_coalesced_frames;
+
+ return 0;
+}
+
+static int nixge_ethtools_set_phys_id(struct net_device *ndev,
+ enum ethtool_phys_id_state state)
+{
+ struct nixge_priv *priv = netdev_priv(ndev);
+ u32 ctrl;
+
+ ctrl = nixge_ctrl_read_reg(priv, NIXGE_REG_LED_CTL);
+ switch (state) {
+ case ETHTOOL_ID_ACTIVE:
+ ctrl |= NIXGE_ID_LED_CTL_EN;
+ /* Enable identification LED override*/
+ nixge_ctrl_write_reg(priv, NIXGE_REG_LED_CTL, ctrl);
+ return 2;
+
+ case ETHTOOL_ID_ON:
+ ctrl |= NIXGE_ID_LED_CTL_VAL;
+ nixge_ctrl_write_reg(priv, NIXGE_REG_LED_CTL, ctrl);
+ break;
+
+ case ETHTOOL_ID_OFF:
+ ctrl &= ~NIXGE_ID_LED_CTL_VAL;
+ nixge_ctrl_write_reg(priv, NIXGE_REG_LED_CTL, ctrl);
+ break;
+
+ case ETHTOOL_ID_INACTIVE:
+ /* Restore LED settings */
+ ctrl &= ~NIXGE_ID_LED_CTL_EN;
+ nixge_ctrl_write_reg(priv, NIXGE_REG_LED_CTL, ctrl);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct ethtool_ops nixge_ethtool_ops = {
+ .get_drvinfo = nixge_ethtools_get_drvinfo,
+ .get_coalesce = nixge_ethtools_get_coalesce,
+ .set_coalesce = nixge_ethtools_set_coalesce,
+ .set_phys_id = nixge_ethtools_set_phys_id,
+ .get_link_ksettings = phy_ethtool_get_link_ksettings,
+ .set_link_ksettings = phy_ethtool_set_link_ksettings,
+ .get_link = ethtool_op_get_link,
+};
+
+static int nixge_mdio_read(struct mii_bus *bus, int phy_id, int reg)
+{
+ struct nixge_priv *priv = bus->priv;
+ u32 status, tmp;
+ int err;
+ u16 device;
+
+ if (reg & MII_ADDR_C45) {
+ device = (reg >> 16) & 0x1f;
+
+ nixge_ctrl_write_reg(priv, NIXGE_REG_MDIO_ADDR, reg & 0xffff);
+
+ tmp = NIXGE_MDIO_CLAUSE45 | NIXGE_MDIO_OP(NIXGE_MDIO_OP_ADDRESS)
+ | NIXGE_MDIO_ADDR(phy_id) | NIXGE_MDIO_MMD(device);
+
+ nixge_ctrl_write_reg(priv, NIXGE_REG_MDIO_OP, tmp);
+ nixge_ctrl_write_reg(priv, NIXGE_REG_MDIO_CTRL, 1);
+
+ err = nixge_ctrl_poll_timeout(priv, NIXGE_REG_MDIO_CTRL, status,
+ !status, 10, 1000);
+ if (err) {
+ dev_err(priv->dev, "timeout setting address");
+ return err;
+ }
+
+ tmp = NIXGE_MDIO_CLAUSE45 | NIXGE_MDIO_OP(NIXGE_MDIO_C45_READ) |
+ NIXGE_MDIO_ADDR(phy_id) | NIXGE_MDIO_MMD(device);
+ } else {
+ device = reg & 0x1f;
+
+ tmp = NIXGE_MDIO_CLAUSE22 | NIXGE_MDIO_OP(NIXGE_MDIO_C22_READ) |
+ NIXGE_MDIO_ADDR(phy_id) | NIXGE_MDIO_MMD(device);
+ }
+
+ nixge_ctrl_write_reg(priv, NIXGE_REG_MDIO_OP, tmp);
+ nixge_ctrl_write_reg(priv, NIXGE_REG_MDIO_CTRL, 1);
+
+ err = nixge_ctrl_poll_timeout(priv, NIXGE_REG_MDIO_CTRL, status,
+ !status, 10, 1000);
+ if (err) {
+ dev_err(priv->dev, "timeout setting read command");
+ return err;
+ }
+
+ status = nixge_ctrl_read_reg(priv, NIXGE_REG_MDIO_DATA);
+
+ return status;
+}
+
+static int nixge_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 val)
+{
+ struct nixge_priv *priv = bus->priv;
+ u32 status, tmp;
+ u16 device;
+ int err;
+
+ if (reg & MII_ADDR_C45) {
+ device = (reg >> 16) & 0x1f;
+
+ nixge_ctrl_write_reg(priv, NIXGE_REG_MDIO_ADDR, reg & 0xffff);
+
+ tmp = NIXGE_MDIO_CLAUSE45 | NIXGE_MDIO_OP(NIXGE_MDIO_OP_ADDRESS)
+ | NIXGE_MDIO_ADDR(phy_id) | NIXGE_MDIO_MMD(device);
+
+ nixge_ctrl_write_reg(priv, NIXGE_REG_MDIO_OP, tmp);
+ nixge_ctrl_write_reg(priv, NIXGE_REG_MDIO_CTRL, 1);
+
+ err = nixge_ctrl_poll_timeout(priv, NIXGE_REG_MDIO_CTRL, status,
+ !status, 10, 1000);
+ if (err) {
+ dev_err(priv->dev, "timeout setting address");
+ return err;
+ }
+
+ tmp = NIXGE_MDIO_CLAUSE45 | NIXGE_MDIO_OP(NIXGE_MDIO_C45_WRITE)
+ | NIXGE_MDIO_ADDR(phy_id) | NIXGE_MDIO_MMD(device);
+
+ nixge_ctrl_write_reg(priv, NIXGE_REG_MDIO_DATA, val);
+ nixge_ctrl_write_reg(priv, NIXGE_REG_MDIO_OP, tmp);
+ err = nixge_ctrl_poll_timeout(priv, NIXGE_REG_MDIO_CTRL, status,
+ !status, 10, 1000);
+ if (err)
+ dev_err(priv->dev, "timeout setting write command");
+ } else {
+ device = reg & 0x1f;
+
+ tmp = NIXGE_MDIO_CLAUSE22 |
+ NIXGE_MDIO_OP(NIXGE_MDIO_C22_WRITE) |
+ NIXGE_MDIO_ADDR(phy_id) | NIXGE_MDIO_MMD(device);
+
+ nixge_ctrl_write_reg(priv, NIXGE_REG_MDIO_DATA, val);
+ nixge_ctrl_write_reg(priv, NIXGE_REG_MDIO_OP, tmp);
+ nixge_ctrl_write_reg(priv, NIXGE_REG_MDIO_CTRL, 1);
+
+ err = nixge_ctrl_poll_timeout(priv, NIXGE_REG_MDIO_CTRL, status,
+ !status, 10, 1000);
+ if (err)
+ dev_err(priv->dev, "timeout setting write command");
+ }
+
+ return err;
+}
+
+static int nixge_mdio_setup(struct nixge_priv *priv, struct device_node *np)
+{
+ struct mii_bus *bus;
+
+ bus = devm_mdiobus_alloc(priv->dev);
+ if (!bus)
+ return -ENOMEM;
+
+ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(priv->dev));
+ bus->priv = priv;
+ bus->name = "nixge_mii_bus";
+ bus->read = nixge_mdio_read;
+ bus->write = nixge_mdio_write;
+ bus->parent = priv->dev;
+
+ priv->mii_bus = bus;
+
+ return of_mdiobus_register(bus, np);
+}
+
+static void *nixge_get_nvmem_address(struct device *dev)
+{
+ struct nvmem_cell *cell;
+ size_t cell_size;
+ char *mac;
+
+ cell = nvmem_cell_get(dev, "address");
+ if (IS_ERR(cell))
+ return cell;
+
+ mac = nvmem_cell_read(cell, &cell_size);
+ nvmem_cell_put(cell);
+
+ return mac;
+}
+
+static int nixge_probe(struct platform_device *pdev)
+{
+ struct nixge_priv *priv;
+ struct net_device *ndev;
+ struct resource *dmares;
+ const char *mac_addr;
+ int err;
+
+ ndev = alloc_etherdev(sizeof(*priv));
+ if (!ndev)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, ndev);
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+
+ ndev->features = NETIF_F_SG;
+ ndev->netdev_ops = &nixge_netdev_ops;
+ ndev->ethtool_ops = &nixge_ethtool_ops;
+
+ /* MTU range: 64 - 9000 */
+ ndev->min_mtu = 64;
+ ndev->max_mtu = NIXGE_JUMBO_MTU;
+
+ mac_addr = nixge_get_nvmem_address(&pdev->dev);
+ if (mac_addr && is_valid_ether_addr(mac_addr))
+ ether_addr_copy(ndev->dev_addr, mac_addr);
+ else
+ eth_hw_addr_random(ndev);
+
+ priv = netdev_priv(ndev);
+ priv->ndev = ndev;
+ priv->dev = &pdev->dev;
+
+ netif_napi_add(ndev, &priv->napi, nixge_poll, NAPI_POLL_WEIGHT);
+
+ dmares = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->dma_regs = devm_ioremap_resource(&pdev->dev, dmares);
+ if (IS_ERR(priv->dma_regs)) {
+ netdev_err(ndev, "failed to map dma regs\n");
+ return PTR_ERR(priv->dma_regs);
+ }
+ priv->ctrl_regs = priv->dma_regs + NIXGE_REG_CTRL_OFFSET;
+ __nixge_hw_set_mac_address(ndev);
+
+ priv->tx_irq = platform_get_irq_byname(pdev, "tx");
+ if (priv->tx_irq < 0) {
+ netdev_err(ndev, "could not find 'tx' irq");
+ return priv->tx_irq;
+ }
+
+ priv->rx_irq = platform_get_irq_byname(pdev, "rx");
+ if (priv->rx_irq < 0) {
+ netdev_err(ndev, "could not find 'rx' irq");
+ return priv->rx_irq;
+ }
+
+ priv->coalesce_count_rx = XAXIDMA_DFT_RX_THRESHOLD;
+ priv->coalesce_count_tx = XAXIDMA_DFT_TX_THRESHOLD;
+
+ err = nixge_mdio_setup(priv, pdev->dev.of_node);
+ if (err) {
+ netdev_err(ndev, "error registering mdio bus");
+ goto free_netdev;
+ }
+
+ priv->phy_mode = of_get_phy_mode(pdev->dev.of_node);
+ if (priv->phy_mode < 0) {
+ netdev_err(ndev, "not find \"phy-mode\" property\n");
+ err = -EINVAL;
+ goto unregister_mdio;
+ }
+
+ priv->phy_node = of_parse_phandle(pdev->dev.of_node, "phy-handle", 0);
+ if (!priv->phy_node) {
+ netdev_err(ndev, "not find \"phy-handle\" property\n");
+ err = -EINVAL;
+ goto unregister_mdio;
+ }
+
+ err = register_netdev(priv->ndev);
+ if (err) {
+ netdev_err(ndev, "register_netdev() error (%i)\n", err);
+ goto unregister_mdio;
+ }
+
+ return 0;
+
+unregister_mdio:
+ mdiobus_unregister(priv->mii_bus);
+
+free_netdev:
+ free_netdev(ndev);
+
+ return err;
+}
+
+static int nixge_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct nixge_priv *priv = netdev_priv(ndev);
+
+ unregister_netdev(ndev);
+
+ mdiobus_unregister(priv->mii_bus);
+
+ free_netdev(ndev);
+
+ return 0;
+}
+
+/* Match table for of_platform binding */
+static const struct of_device_id nixge_dt_ids[] = {
+ { .compatible = "ni,xge-enet-2.00", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, nixge_dt_ids);
+
+static struct platform_driver nixge_driver = {
+ .probe = nixge_probe,
+ .remove = nixge_remove,
+ .driver = {
+ .name = "nixge",
+ .of_match_table = of_match_ptr(nixge_dt_ids),
+ },
+};
+module_platform_driver(nixge_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("National Instruments XGE Management MAC");
+MODULE_AUTHOR("Moritz Fischer <mdf@kernel.org>");
diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c
index 3dd973475125..0ea141ece19e 100644
--- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c
+++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c
@@ -603,7 +603,7 @@ static struct uni_table_desc *nx_get_table_desc(const u8 *unirom, int section)
static int
netxen_nic_validate_header(struct netxen_adapter *adapter)
- {
+{
const u8 *unirom = adapter->fw->data;
struct uni_table_desc *directory = (struct uni_table_desc *) &unirom[0];
u32 fw_file_size = adapter->fw->size;
diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
index f2e8de607119..8259e8309320 100644
--- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
+++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
@@ -2829,9 +2829,9 @@ netxen_show_bridged_mode(struct device *dev,
}
static const struct device_attribute dev_attr_bridged_mode = {
- .attr = {.name = "bridged_mode", .mode = (S_IRUGO | S_IWUSR)},
- .show = netxen_show_bridged_mode,
- .store = netxen_store_bridged_mode,
+ .attr = { .name = "bridged_mode", .mode = 0644 },
+ .show = netxen_show_bridged_mode,
+ .store = netxen_store_bridged_mode,
};
static ssize_t
@@ -2861,7 +2861,7 @@ netxen_show_diag_mode(struct device *dev,
}
static const struct device_attribute dev_attr_diag_mode = {
- .attr = {.name = "diag_mode", .mode = (S_IRUGO | S_IWUSR)},
+ .attr = { .name = "diag_mode", .mode = 0644 },
.show = netxen_show_diag_mode,
.store = netxen_store_diag_mode,
};
@@ -3006,14 +3006,14 @@ static ssize_t netxen_sysfs_write_mem(struct file *filp, struct kobject *kobj,
static const struct bin_attribute bin_attr_crb = {
- .attr = {.name = "crb", .mode = (S_IRUGO | S_IWUSR)},
+ .attr = { .name = "crb", .mode = 0644 },
.size = 0,
.read = netxen_sysfs_read_crb,
.write = netxen_sysfs_write_crb,
};
static const struct bin_attribute bin_attr_mem = {
- .attr = {.name = "mem", .mode = (S_IRUGO | S_IWUSR)},
+ .attr = { .name = "mem", .mode = 0644 },
.size = 0,
.read = netxen_sysfs_read_mem,
.write = netxen_sysfs_write_mem,
@@ -3142,7 +3142,7 @@ out:
}
static const struct bin_attribute bin_attr_dimm = {
- .attr = { .name = "dimm", .mode = (S_IRUGO | S_IWUSR) },
+ .attr = { .name = "dimm", .mode = 0644 },
.size = sizeof(struct netxen_dimm_cfg),
.read = netxen_sysfs_read_dimm,
};
diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h
index 69488554f4b9..e07460a68d30 100644
--- a/drivers/net/ethernet/qlogic/qed/qed.h
+++ b/drivers/net/ethernet/qlogic/qed/qed.h
@@ -81,6 +81,13 @@ enum qed_coalescing_mode {
QED_COAL_MODE_ENABLE
};
+enum qed_nvm_cmd {
+ QED_PUT_FILE_BEGIN = DRV_MSG_CODE_NVM_PUT_FILE_BEGIN,
+ QED_PUT_FILE_DATA = DRV_MSG_CODE_NVM_PUT_FILE_DATA,
+ QED_NVM_WRITE_NVRAM = DRV_MSG_CODE_NVM_WRITE_NVRAM,
+ QED_GET_MCP_NVM_RESP = 0xFFFFFF00
+};
+
struct qed_eth_cb_ops;
struct qed_dev_info;
union qed_mcp_protocol_stats;
@@ -437,6 +444,11 @@ enum BAR_ID {
BAR_ID_1 /* Used for doorbells */
};
+struct qed_nvm_image_info {
+ u32 num_images;
+ struct bist_nvm_image_att *image_att;
+};
+
#define DRV_MODULE_VERSION \
__stringify(QED_MAJOR_VERSION) "." \
__stringify(QED_MINOR_VERSION) "." \
@@ -561,6 +573,9 @@ struct qed_hwfn {
/* L2-related */
struct qed_l2_info *p_l2_info;
+ /* Nvm images number and attributes */
+ struct qed_nvm_image_info nvm_info;
+
struct qed_ptt *p_arfs_ptt;
struct qed_simd_fp_handler simd_proto_handler[64];
diff --git a/drivers/net/ethernet/qlogic/qed/qed_debug.c b/drivers/net/ethernet/qlogic/qed/qed_debug.c
index fdf37abee3d3..4926c5532fba 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_debug.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_debug.c
@@ -265,6 +265,7 @@ struct grc_param_defs {
u32 min;
u32 max;
bool is_preset;
+ bool is_persistent;
u32 exclude_all_preset_val;
u32 crash_preset_val;
};
@@ -1520,129 +1521,129 @@ static struct platform_defs s_platform_defs[] = {
static struct grc_param_defs s_grc_param_defs[] = {
/* DBG_GRC_PARAM_DUMP_TSTORM */
- {{1, 1, 1}, 0, 1, false, 1, 1},
+ {{1, 1, 1}, 0, 1, false, false, 1, 1},
/* DBG_GRC_PARAM_DUMP_MSTORM */
- {{1, 1, 1}, 0, 1, false, 1, 1},
+ {{1, 1, 1}, 0, 1, false, false, 1, 1},
/* DBG_GRC_PARAM_DUMP_USTORM */
- {{1, 1, 1}, 0, 1, false, 1, 1},
+ {{1, 1, 1}, 0, 1, false, false, 1, 1},
/* DBG_GRC_PARAM_DUMP_XSTORM */
- {{1, 1, 1}, 0, 1, false, 1, 1},
+ {{1, 1, 1}, 0, 1, false, false, 1, 1},
/* DBG_GRC_PARAM_DUMP_YSTORM */
- {{1, 1, 1}, 0, 1, false, 1, 1},
+ {{1, 1, 1}, 0, 1, false, false, 1, 1},
/* DBG_GRC_PARAM_DUMP_PSTORM */
- {{1, 1, 1}, 0, 1, false, 1, 1},
+ {{1, 1, 1}, 0, 1, false, false, 1, 1},
/* DBG_GRC_PARAM_DUMP_REGS */
- {{1, 1, 1}, 0, 1, false, 0, 1},
+ {{1, 1, 1}, 0, 1, false, false, 0, 1},
/* DBG_GRC_PARAM_DUMP_RAM */
- {{1, 1, 1}, 0, 1, false, 0, 1},
+ {{1, 1, 1}, 0, 1, false, false, 0, 1},
/* DBG_GRC_PARAM_DUMP_PBUF */
- {{1, 1, 1}, 0, 1, false, 0, 1},
+ {{1, 1, 1}, 0, 1, false, false, 0, 1},
/* DBG_GRC_PARAM_DUMP_IOR */
- {{0, 0, 0}, 0, 1, false, 0, 1},
+ {{0, 0, 0}, 0, 1, false, false, 0, 1},
/* DBG_GRC_PARAM_DUMP_VFC */
- {{0, 0, 0}, 0, 1, false, 0, 1},
+ {{0, 0, 0}, 0, 1, false, false, 0, 1},
/* DBG_GRC_PARAM_DUMP_CM_CTX */
- {{1, 1, 1}, 0, 1, false, 0, 1},
+ {{1, 1, 1}, 0, 1, false, false, 0, 1},
/* DBG_GRC_PARAM_DUMP_ILT */
- {{1, 1, 1}, 0, 1, false, 0, 1},
+ {{1, 1, 1}, 0, 1, false, false, 0, 1},
/* DBG_GRC_PARAM_DUMP_RSS */
- {{1, 1, 1}, 0, 1, false, 0, 1},
+ {{1, 1, 1}, 0, 1, false, false, 0, 1},
/* DBG_GRC_PARAM_DUMP_CAU */
- {{1, 1, 1}, 0, 1, false, 0, 1},
+ {{1, 1, 1}, 0, 1, false, false, 0, 1},
/* DBG_GRC_PARAM_DUMP_QM */
- {{1, 1, 1}, 0, 1, false, 0, 1},
+ {{1, 1, 1}, 0, 1, false, false, 0, 1},
/* DBG_GRC_PARAM_DUMP_MCP */
- {{1, 1, 1}, 0, 1, false, 0, 1},
+ {{1, 1, 1}, 0, 1, false, false, 0, 1},
- /* DBG_GRC_PARAM_RESERVED */
- {{1, 1, 1}, 0, 1, false, 0, 1},
+ /* DBG_GRC_PARAM_MCP_TRACE_META_SIZE */
+ {{1, 1, 1}, 1, 0xffffffff, false, true, 0, 1},
/* DBG_GRC_PARAM_DUMP_CFC */
- {{1, 1, 1}, 0, 1, false, 0, 1},
+ {{1, 1, 1}, 0, 1, false, false, 0, 1},
/* DBG_GRC_PARAM_DUMP_IGU */
- {{1, 1, 1}, 0, 1, false, 0, 1},
+ {{1, 1, 1}, 0, 1, false, false, 0, 1},
/* DBG_GRC_PARAM_DUMP_BRB */
- {{0, 0, 0}, 0, 1, false, 0, 1},
+ {{0, 0, 0}, 0, 1, false, false, 0, 1},
/* DBG_GRC_PARAM_DUMP_BTB */
- {{0, 0, 0}, 0, 1, false, 0, 1},
+ {{0, 0, 0}, 0, 1, false, false, 0, 1},
/* DBG_GRC_PARAM_DUMP_BMB */
- {{0, 0, 0}, 0, 1, false, 0, 1},
+ {{0, 0, 0}, 0, 1, false, false, 0, 1},
/* DBG_GRC_PARAM_DUMP_NIG */
- {{1, 1, 1}, 0, 1, false, 0, 1},
+ {{1, 1, 1}, 0, 1, false, false, 0, 1},
/* DBG_GRC_PARAM_DUMP_MULD */
- {{1, 1, 1}, 0, 1, false, 0, 1},
+ {{1, 1, 1}, 0, 1, false, false, 0, 1},
/* DBG_GRC_PARAM_DUMP_PRS */
- {{1, 1, 1}, 0, 1, false, 0, 1},
+ {{1, 1, 1}, 0, 1, false, false, 0, 1},
/* DBG_GRC_PARAM_DUMP_DMAE */
- {{1, 1, 1}, 0, 1, false, 0, 1},
+ {{1, 1, 1}, 0, 1, false, false, 0, 1},
/* DBG_GRC_PARAM_DUMP_TM */
- {{1, 1, 1}, 0, 1, false, 0, 1},
+ {{1, 1, 1}, 0, 1, false, false, 0, 1},
/* DBG_GRC_PARAM_DUMP_SDM */
- {{1, 1, 1}, 0, 1, false, 0, 1},
+ {{1, 1, 1}, 0, 1, false, false, 0, 1},
/* DBG_GRC_PARAM_DUMP_DIF */
- {{1, 1, 1}, 0, 1, false, 0, 1},
+ {{1, 1, 1}, 0, 1, false, false, 0, 1},
/* DBG_GRC_PARAM_DUMP_STATIC */
- {{1, 1, 1}, 0, 1, false, 0, 1},
+ {{1, 1, 1}, 0, 1, false, false, 0, 1},
/* DBG_GRC_PARAM_UNSTALL */
- {{0, 0, 0}, 0, 1, false, 0, 0},
+ {{0, 0, 0}, 0, 1, false, false, 0, 0},
/* DBG_GRC_PARAM_NUM_LCIDS */
- {{MAX_LCIDS, MAX_LCIDS, MAX_LCIDS}, 1, MAX_LCIDS, false, MAX_LCIDS,
- MAX_LCIDS},
+ {{MAX_LCIDS, MAX_LCIDS, MAX_LCIDS}, 1, MAX_LCIDS, false, false,
+ MAX_LCIDS, MAX_LCIDS},
/* DBG_GRC_PARAM_NUM_LTIDS */
- {{MAX_LTIDS, MAX_LTIDS, MAX_LTIDS}, 1, MAX_LTIDS, false, MAX_LTIDS,
- MAX_LTIDS},
+ {{MAX_LTIDS, MAX_LTIDS, MAX_LTIDS}, 1, MAX_LTIDS, false, false,
+ MAX_LTIDS, MAX_LTIDS},
/* DBG_GRC_PARAM_EXCLUDE_ALL */
- {{0, 0, 0}, 0, 1, true, 0, 0},
+ {{0, 0, 0}, 0, 1, true, false, 0, 0},
/* DBG_GRC_PARAM_CRASH */
- {{0, 0, 0}, 0, 1, true, 0, 0},
+ {{0, 0, 0}, 0, 1, true, false, 0, 0},
/* DBG_GRC_PARAM_PARITY_SAFE */
- {{0, 0, 0}, 0, 1, false, 1, 0},
+ {{0, 0, 0}, 0, 1, false, false, 1, 0},
/* DBG_GRC_PARAM_DUMP_CM */
- {{1, 1, 1}, 0, 1, false, 0, 1},
+ {{1, 1, 1}, 0, 1, false, false, 0, 1},
/* DBG_GRC_PARAM_DUMP_PHY */
- {{1, 1, 1}, 0, 1, false, 0, 1},
+ {{1, 1, 1}, 0, 1, false, false, 0, 1},
/* DBG_GRC_PARAM_NO_MCP */
- {{0, 0, 0}, 0, 1, false, 0, 0},
+ {{0, 0, 0}, 0, 1, false, false, 0, 0},
/* DBG_GRC_PARAM_NO_FW_VER */
- {{0, 0, 0}, 0, 1, false, 0, 0}
+ {{0, 0, 0}, 0, 1, false, false, 0, 0}
};
static struct rss_mem_defs s_rss_mem_defs[] = {
@@ -4731,8 +4732,13 @@ static enum dbg_status qed_mcp_trace_dump(struct qed_hwfn *p_hwfn,
offset += qed_dump_section_hdr(dump_buf + offset,
dump, "mcp_trace_meta", 1);
- /* Read trace meta info (trace_meta_size_bytes is dword-aligned) */
- if (mcp_access) {
+ /* If MCP Trace meta size parameter was set, use it.
+ * Otherwise, read trace meta.
+ * trace_meta_size_bytes is dword-aligned.
+ */
+ trace_meta_size_bytes =
+ qed_grc_get_param(p_hwfn, DBG_GRC_PARAM_MCP_TRACE_META_SIZE);
+ if ((!trace_meta_size_bytes || dump) && mcp_access) {
status = qed_mcp_trace_get_meta_info(p_hwfn,
p_ptt,
trace_data_size_bytes,
@@ -5063,8 +5069,9 @@ void qed_dbg_grc_set_params_default(struct qed_hwfn *p_hwfn)
u32 i;
for (i = 0; i < MAX_DBG_GRC_PARAMS; i++)
- dev_data->grc.param_val[i] =
- s_grc_param_defs[i].default_val[dev_data->chip_id];
+ if (!s_grc_param_defs[i].is_persistent)
+ dev_data->grc.param_val[i] =
+ s_grc_param_defs[i].default_val[dev_data->chip_id];
}
enum dbg_status qed_dbg_grc_get_dump_buf_size(struct qed_hwfn *p_hwfn,
@@ -6071,10 +6078,14 @@ static const struct igu_fifo_addr_data s_igu_fifo_addr_data[] = {
/******************************** Variables **********************************/
-/* MCP Trace meta data - used in case the dump doesn't contain the meta data
- * (e.g. due to no NVRAM access).
+/* MCP Trace meta data array - used in case the dump doesn't contain the
+ * meta data (e.g. due to no NVRAM access).
*/
-static struct user_dbg_array s_mcp_trace_meta = { NULL, 0 };
+static struct user_dbg_array s_mcp_trace_meta_arr = { NULL, 0 };
+
+/* Parsed MCP Trace meta data info, based on MCP trace meta array */
+static struct mcp_trace_meta s_mcp_trace_meta;
+static bool s_mcp_trace_meta_valid;
/* Temporary buffer, used for print size calculations */
static char s_temp_buf[MAX_MSG_LEN];
@@ -6104,6 +6115,9 @@ static u32 qed_read_from_cyclic_buf(void *buf,
val_ptr = (u8 *)&val;
+ /* Assume running on a LITTLE ENDIAN and the buffer is network order
+ * (BIG ENDIAN), as high order bytes are placed in lower memory address.
+ */
for (i = 0; i < num_bytes_to_read; i++) {
val_ptr[i] = bytes_buf[*offset];
*offset = qed_cyclic_add(*offset, 1, buf_size);
@@ -6185,7 +6199,7 @@ static u32 qed_read_param(u32 *dump_buf,
offset += 4;
}
- return offset / 4;
+ return (u32)offset / 4;
}
/* Reads a section header from the specified buffer.
@@ -6503,6 +6517,8 @@ static void qed_mcp_trace_free_meta(struct qed_hwfn *p_hwfn,
{
u32 i;
+ s_mcp_trace_meta_valid = false;
+
/* Release modules */
if (meta->modules) {
for (i = 0; i < meta->modules_num; i++)
@@ -6529,6 +6545,10 @@ static enum dbg_status qed_mcp_trace_alloc_meta(struct qed_hwfn *p_hwfn,
u8 *meta_buf_bytes = (u8 *)meta_buf;
u32 offset = 0, signature, i;
+ /* Free the previous meta before loading a new one. */
+ if (s_mcp_trace_meta_valid)
+ qed_mcp_trace_free_meta(p_hwfn, meta);
+
memset(meta, 0, sizeof(*meta));
/* Read first signature */
@@ -6594,31 +6614,153 @@ static enum dbg_status qed_mcp_trace_alloc_meta(struct qed_hwfn *p_hwfn,
format_len, format_ptr->format_str);
}
+ s_mcp_trace_meta_valid = true;
return DBG_STATUS_OK;
}
+/* Parses an MCP trace buffer. If result_buf is not NULL, the MCP Trace results
+ * are printed to it. The parsing status is returned.
+ * Arguments:
+ * trace_buf - MCP trace cyclic buffer
+ * trace_buf_size - MCP trace cyclic buffer size in bytes
+ * data_offset - offset in bytes of the data to parse in the MCP trace cyclic
+ * buffer.
+ * data_size - size in bytes of data to parse.
+ * parsed_buf - destination buffer for parsed data.
+ * parsed_bytes - size of parsed data in bytes.
+ */
+static enum dbg_status qed_parse_mcp_trace_buf(u8 *trace_buf,
+ u32 trace_buf_size,
+ u32 data_offset,
+ u32 data_size,
+ char *parsed_buf,
+ u32 *parsed_bytes)
+{
+ u32 param_mask, param_shift;
+ enum dbg_status status;
+
+ *parsed_bytes = 0;
+
+ if (!s_mcp_trace_meta_valid)
+ return DBG_STATUS_MCP_TRACE_BAD_DATA;
+
+ status = DBG_STATUS_OK;
+
+ while (data_size) {
+ struct mcp_trace_format *format_ptr;
+ u8 format_level, format_module;
+ u32 params[3] = { 0, 0, 0 };
+ u32 header, format_idx, i;
+
+ if (data_size < MFW_TRACE_ENTRY_SIZE)
+ return DBG_STATUS_MCP_TRACE_BAD_DATA;
+
+ header = qed_read_from_cyclic_buf(trace_buf,
+ &data_offset,
+ trace_buf_size,
+ MFW_TRACE_ENTRY_SIZE);
+ data_size -= MFW_TRACE_ENTRY_SIZE;
+ format_idx = header & MFW_TRACE_EVENTID_MASK;
+
+ /* Skip message if its index doesn't exist in the meta data */
+ if (format_idx > s_mcp_trace_meta.formats_num) {
+ u8 format_size =
+ (u8)((header & MFW_TRACE_PRM_SIZE_MASK) >>
+ MFW_TRACE_PRM_SIZE_SHIFT);
+
+ if (data_size < format_size)
+ return DBG_STATUS_MCP_TRACE_BAD_DATA;
+
+ data_offset = qed_cyclic_add(data_offset,
+ format_size,
+ trace_buf_size);
+ data_size -= format_size;
+ continue;
+ }
+
+ format_ptr = &s_mcp_trace_meta.formats[format_idx];
+
+ for (i = 0,
+ param_mask = MCP_TRACE_FORMAT_P1_SIZE_MASK,
+ param_shift = MCP_TRACE_FORMAT_P1_SIZE_SHIFT;
+ i < MCP_TRACE_FORMAT_MAX_PARAMS;
+ i++,
+ param_mask <<= MCP_TRACE_FORMAT_PARAM_WIDTH,
+ param_shift += MCP_TRACE_FORMAT_PARAM_WIDTH) {
+ /* Extract param size (0..3) */
+ u8 param_size = (u8)((format_ptr->data & param_mask) >>
+ param_shift);
+
+ /* If the param size is zero, there are no other
+ * parameters.
+ */
+ if (!param_size)
+ break;
+
+ /* Size is encoded using 2 bits, where 3 is used to
+ * encode 4.
+ */
+ if (param_size == 3)
+ param_size = 4;
+
+ if (data_size < param_size)
+ return DBG_STATUS_MCP_TRACE_BAD_DATA;
+
+ params[i] = qed_read_from_cyclic_buf(trace_buf,
+ &data_offset,
+ trace_buf_size,
+ param_size);
+ data_size -= param_size;
+ }
+
+ format_level = (u8)((format_ptr->data &
+ MCP_TRACE_FORMAT_LEVEL_MASK) >>
+ MCP_TRACE_FORMAT_LEVEL_SHIFT);
+ format_module = (u8)((format_ptr->data &
+ MCP_TRACE_FORMAT_MODULE_MASK) >>
+ MCP_TRACE_FORMAT_MODULE_SHIFT);
+ if (format_level >= ARRAY_SIZE(s_mcp_trace_level_str))
+ return DBG_STATUS_MCP_TRACE_BAD_DATA;
+
+ /* Print current message to results buffer */
+ *parsed_bytes +=
+ sprintf(qed_get_buf_ptr(parsed_buf, *parsed_bytes),
+ "%s %-8s: ",
+ s_mcp_trace_level_str[format_level],
+ s_mcp_trace_meta.modules[format_module]);
+ *parsed_bytes +=
+ sprintf(qed_get_buf_ptr(parsed_buf, *parsed_bytes),
+ format_ptr->format_str,
+ params[0], params[1], params[2]);
+ }
+
+ /* Add string NULL terminator */
+ (*parsed_bytes)++;
+
+ return status;
+}
+
/* Parses an MCP Trace dump buffer.
* If result_buf is not NULL, the MCP Trace results are printed to it.
* In any case, the required results buffer size is assigned to
- * parsed_results_bytes.
+ * parsed_bytes.
* The parsing status is returned.
*/
static enum dbg_status qed_parse_mcp_trace_dump(struct qed_hwfn *p_hwfn,
u32 *dump_buf,
- char *results_buf,
- u32 *parsed_results_bytes)
+ char *parsed_buf,
+ u32 *parsed_bytes)
{
- u32 end_offset, bytes_left, trace_data_dwords, trace_meta_dwords;
- u32 param_mask, param_shift, param_num_val, num_section_params;
const char *section_name, *param_name, *param_str_val;
- u32 offset, results_offset = 0;
- struct mcp_trace_meta meta;
+ u32 data_size, trace_data_dwords, trace_meta_dwords;
+ u32 offset, results_offset, parsed_buf_bytes;
+ u32 param_num_val, num_section_params;
struct mcp_trace *trace;
enum dbg_status status;
const u32 *meta_buf;
u8 *trace_buf;
- *parsed_results_bytes = 0;
+ *parsed_bytes = 0;
/* Read global_params section */
dump_buf += qed_read_section_hdr(dump_buf,
@@ -6629,7 +6771,7 @@ static enum dbg_status qed_parse_mcp_trace_dump(struct qed_hwfn *p_hwfn,
/* Print global params */
dump_buf += qed_print_section_params(dump_buf,
num_section_params,
- results_buf, &results_offset);
+ parsed_buf, &results_offset);
/* Read trace_data section */
dump_buf += qed_read_section_hdr(dump_buf,
@@ -6646,8 +6788,7 @@ static enum dbg_status qed_parse_mcp_trace_dump(struct qed_hwfn *p_hwfn,
trace = (struct mcp_trace *)dump_buf;
trace_buf = (u8 *)dump_buf + sizeof(*trace);
offset = trace->trace_oldest;
- end_offset = trace->trace_prod;
- bytes_left = qed_cyclic_sub(end_offset, offset, trace->size);
+ data_size = qed_cyclic_sub(trace->trace_prod, offset, trace->size);
dump_buf += trace_data_dwords;
/* Read meta_data section */
@@ -6664,126 +6805,33 @@ static enum dbg_status qed_parse_mcp_trace_dump(struct qed_hwfn *p_hwfn,
/* Choose meta data buffer */
if (!trace_meta_dwords) {
/* Dump doesn't include meta data */
- if (!s_mcp_trace_meta.ptr)
+ if (!s_mcp_trace_meta_arr.ptr)
return DBG_STATUS_MCP_TRACE_NO_META;
- meta_buf = s_mcp_trace_meta.ptr;
+ meta_buf = s_mcp_trace_meta_arr.ptr;
} else {
/* Dump includes meta data */
meta_buf = dump_buf;
}
/* Allocate meta data memory */
- status = qed_mcp_trace_alloc_meta(p_hwfn, meta_buf, &meta);
+ status = qed_mcp_trace_alloc_meta(p_hwfn, meta_buf, &s_mcp_trace_meta);
if (status != DBG_STATUS_OK)
- goto free_mem;
-
- /* Ignore the level and modules masks - just print everything that is
- * already in the buffer.
- */
- while (bytes_left) {
- struct mcp_trace_format *format_ptr;
- u8 format_level, format_module;
- u32 params[3] = { 0, 0, 0 };
- u32 header, format_idx, i;
-
- if (bytes_left < MFW_TRACE_ENTRY_SIZE) {
- status = DBG_STATUS_MCP_TRACE_BAD_DATA;
- goto free_mem;
- }
-
- header = qed_read_from_cyclic_buf(trace_buf,
- &offset,
- trace->size,
- MFW_TRACE_ENTRY_SIZE);
- bytes_left -= MFW_TRACE_ENTRY_SIZE;
- format_idx = header & MFW_TRACE_EVENTID_MASK;
-
- /* Skip message if its index doesn't exist in the meta data */
- if (format_idx > meta.formats_num) {
- u8 format_size =
- (u8)((header &
- MFW_TRACE_PRM_SIZE_MASK) >>
- MFW_TRACE_PRM_SIZE_SHIFT);
-
- if (bytes_left < format_size) {
- status = DBG_STATUS_MCP_TRACE_BAD_DATA;
- goto free_mem;
- }
-
- offset = qed_cyclic_add(offset,
- format_size, trace->size);
- bytes_left -= format_size;
- continue;
- }
-
- format_ptr = &meta.formats[format_idx];
-
- for (i = 0,
- param_mask = MCP_TRACE_FORMAT_P1_SIZE_MASK, param_shift =
- MCP_TRACE_FORMAT_P1_SIZE_SHIFT;
- i < MCP_TRACE_FORMAT_MAX_PARAMS;
- i++, param_mask <<= MCP_TRACE_FORMAT_PARAM_WIDTH,
- param_shift += MCP_TRACE_FORMAT_PARAM_WIDTH) {
- /* Extract param size (0..3) */
- u8 param_size =
- (u8)((format_ptr->data &
- param_mask) >> param_shift);
-
- /* If the param size is zero, there are no other
- * parameters.
- */
- if (!param_size)
- break;
-
- /* Size is encoded using 2 bits, where 3 is used to
- * encode 4.
- */
- if (param_size == 3)
- param_size = 4;
-
- if (bytes_left < param_size) {
- status = DBG_STATUS_MCP_TRACE_BAD_DATA;
- goto free_mem;
- }
-
- params[i] = qed_read_from_cyclic_buf(trace_buf,
- &offset,
- trace->size,
- param_size);
-
- bytes_left -= param_size;
- }
+ return status;
- format_level =
- (u8)((format_ptr->data &
- MCP_TRACE_FORMAT_LEVEL_MASK) >>
- MCP_TRACE_FORMAT_LEVEL_SHIFT);
- format_module =
- (u8)((format_ptr->data &
- MCP_TRACE_FORMAT_MODULE_MASK) >>
- MCP_TRACE_FORMAT_MODULE_SHIFT);
- if (format_level >= ARRAY_SIZE(s_mcp_trace_level_str)) {
- status = DBG_STATUS_MCP_TRACE_BAD_DATA;
- goto free_mem;
- }
+ status = qed_parse_mcp_trace_buf(trace_buf,
+ trace->size,
+ offset,
+ data_size,
+ parsed_buf ?
+ parsed_buf + results_offset :
+ NULL,
+ &parsed_buf_bytes);
+ if (status != DBG_STATUS_OK)
+ return status;
- /* Print current message to results buffer */
- results_offset +=
- sprintf(qed_get_buf_ptr(results_buf,
- results_offset), "%s %-8s: ",
- s_mcp_trace_level_str[format_level],
- meta.modules[format_module]);
- results_offset +=
- sprintf(qed_get_buf_ptr(results_buf,
- results_offset),
- format_ptr->format_str, params[0], params[1],
- params[2]);
- }
+ *parsed_bytes = results_offset + parsed_buf_bytes;
-free_mem:
- *parsed_results_bytes = results_offset + 1;
- qed_mcp_trace_free_meta(p_hwfn, &meta);
- return status;
+ return DBG_STATUS_OK;
}
/* Parses a Reg FIFO dump buffer.
@@ -7291,8 +7339,8 @@ enum dbg_status qed_print_idle_chk_results(struct qed_hwfn *p_hwfn,
void qed_dbg_mcp_trace_set_meta_data(u32 *data, u32 size)
{
- s_mcp_trace_meta.ptr = data;
- s_mcp_trace_meta.size_in_dwords = size;
+ s_mcp_trace_meta_arr.ptr = data;
+ s_mcp_trace_meta_arr.size_in_dwords = size;
}
enum dbg_status qed_get_mcp_trace_results_buf_size(struct qed_hwfn *p_hwfn,
@@ -7316,6 +7364,19 @@ enum dbg_status qed_print_mcp_trace_results(struct qed_hwfn *p_hwfn,
results_buf, &parsed_buf_size);
}
+enum dbg_status qed_print_mcp_trace_line(u8 *dump_buf,
+ u32 num_dumped_bytes,
+ char *results_buf)
+{
+ u32 parsed_bytes;
+
+ return qed_parse_mcp_trace_buf(dump_buf,
+ num_dumped_bytes,
+ 0,
+ num_dumped_bytes,
+ results_buf, &parsed_bytes);
+}
+
enum dbg_status qed_get_reg_fifo_results_buf_size(struct qed_hwfn *p_hwfn,
u32 *dump_buf,
u32 num_dumped_dwords,
@@ -7891,6 +7952,7 @@ int qed_dbg_all_data(struct qed_dev *cdev, void *buffer)
}
}
+ qed_set_debug_engine(cdev, org_engine);
/* mcp_trace */
rc = qed_dbg_mcp_trace(cdev, (u8 *)buffer + offset +
REGDUMP_HEADER_SIZE, &feature_size);
@@ -7903,8 +7965,6 @@ int qed_dbg_all_data(struct qed_dev *cdev, void *buffer)
DP_ERR(cdev, "qed_dbg_mcp_trace failed. rc = %d\n", rc);
}
- qed_set_debug_engine(cdev, org_engine);
-
return 0;
}
@@ -7929,9 +7989,10 @@ int qed_dbg_all_data_size(struct qed_dev *cdev)
REGDUMP_HEADER_SIZE + qed_dbg_fw_asserts_size(cdev);
}
+ qed_set_debug_engine(cdev, org_engine);
+
/* Engine common */
regs_len += REGDUMP_HEADER_SIZE + qed_dbg_mcp_trace_size(cdev);
- qed_set_debug_engine(cdev, org_engine);
return regs_len;
}
diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c
index 553a6d17260e..d2ad5e92c74f 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_dev.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c
@@ -298,8 +298,8 @@ static void qed_init_qm_params(struct qed_hwfn *p_hwfn)
qm_info->start_vport = (u8) RESC_START(p_hwfn, QED_VPORT);
/* rate limiting and weighted fair queueing are always enabled */
- qm_info->vport_rl_en = 1;
- qm_info->vport_wfq_en = 1;
+ qm_info->vport_rl_en = true;
+ qm_info->vport_wfq_en = true;
/* TC config is different for AH 4 port */
four_port = p_hwfn->cdev->num_ports_in_engine == MAX_NUM_PORTS_K2;
@@ -407,6 +407,7 @@ static void qed_init_qm_pq(struct qed_hwfn *p_hwfn,
"pq overflow! pq %d, max pq %d\n", pq_idx, max_pq);
/* init pq params */
+ qm_info->qm_pq_params[pq_idx].port_id = p_hwfn->port_id;
qm_info->qm_pq_params[pq_idx].vport_id = qm_info->start_vport +
qm_info->num_vports;
qm_info->qm_pq_params[pq_idx].tc_id = tc;
@@ -727,8 +728,9 @@ static void qed_dp_init_qm_params(struct qed_hwfn *p_hwfn)
pq = &(qm_info->qm_pq_params[i]);
DP_VERBOSE(p_hwfn,
NETIF_MSG_HW,
- "pq idx %d, vport_id %d, tc %d, wrr_grp %d, rl_valid %d\n",
+ "pq idx %d, port %d, vport_id %d, tc %d, wrr_grp %d, rl_valid %d\n",
qm_info->start_pq + i,
+ pq->port_id,
pq->vport_id,
pq->tc_id, pq->wrr_group, pq->rl_valid);
}
@@ -1276,9 +1278,9 @@ static int qed_hw_init_common(struct qed_hwfn *p_hwfn,
if (p_hwfn->mcp_info) {
if (p_hwfn->mcp_info->func_info.bandwidth_max)
- qm_info->pf_rl_en = 1;
+ qm_info->pf_rl_en = true;
if (p_hwfn->mcp_info->func_info.bandwidth_min)
- qm_info->pf_wfq_en = 1;
+ qm_info->pf_wfq_en = true;
}
memset(&params, 0, sizeof(params));
@@ -1630,7 +1632,7 @@ static int qed_vf_start(struct qed_hwfn *p_hwfn,
qed_vf_pf_tunnel_param_update(p_hwfn, p_params->p_tunn);
}
- p_hwfn->b_int_enabled = 1;
+ p_hwfn->b_int_enabled = true;
return 0;
}
@@ -2930,6 +2932,12 @@ static int qed_get_dev_info(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
return 0;
}
+static void qed_nvm_info_free(struct qed_hwfn *p_hwfn)
+{
+ kfree(p_hwfn->nvm_info.image_att);
+ p_hwfn->nvm_info.image_att = NULL;
+}
+
static int qed_hw_prepare_single(struct qed_hwfn *p_hwfn,
void __iomem *p_regview,
void __iomem *p_doorbells,
@@ -2993,12 +3001,25 @@ static int qed_hw_prepare_single(struct qed_hwfn *p_hwfn,
DP_NOTICE(p_hwfn, "Failed to initiate PF FLR\n");
}
+ /* NVRAM info initialization and population */
+ if (IS_LEAD_HWFN(p_hwfn)) {
+ rc = qed_mcp_nvm_info_populate(p_hwfn);
+ if (rc) {
+ DP_NOTICE(p_hwfn,
+ "Failed to populate nvm info shadow\n");
+ goto err2;
+ }
+ }
+
/* Allocate the init RT array and initialize the init-ops engine */
rc = qed_init_alloc(p_hwfn);
if (rc)
- goto err2;
+ goto err3;
return rc;
+err3:
+ if (IS_LEAD_HWFN(p_hwfn))
+ qed_nvm_info_free(p_hwfn);
err2:
if (IS_LEAD_HWFN(p_hwfn))
qed_iov_free_hw_info(p_hwfn->cdev);
@@ -3054,6 +3075,7 @@ int qed_hw_prepare(struct qed_dev *cdev,
if (rc) {
if (IS_PF(cdev)) {
qed_init_free(p_hwfn);
+ qed_nvm_info_free(p_hwfn);
qed_mcp_free(p_hwfn);
qed_hw_hwfn_free(p_hwfn);
}
@@ -3086,6 +3108,8 @@ void qed_hw_remove(struct qed_dev *cdev)
}
qed_iov_free_hw_info(cdev);
+
+ qed_nvm_info_free(p_hwfn);
}
static void qed_chain_free_next_ptr(struct qed_dev *cdev,
diff --git a/drivers/net/ethernet/qlogic/qed/qed_hsi.h b/drivers/net/ethernet/qlogic/qed/qed_hsi.h
index de873d770575..7f5ec42dde48 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_hsi.h
+++ b/drivers/net/ethernet/qlogic/qed/qed_hsi.h
@@ -612,7 +612,7 @@ struct e4_xstorm_core_conn_ag_ctx {
__le16 reserved16;
__le16 tx_bd_cons;
__le16 tx_bd_or_spq_prod;
- __le16 word5;
+ __le16 updated_qm_pq_id;
__le16 conn_dpi;
u8 byte3;
u8 byte4;
@@ -1005,7 +1005,9 @@ enum fw_flow_ctrl_mode {
enum gft_profile_type {
GFT_PROFILE_TYPE_4_TUPLE,
GFT_PROFILE_TYPE_L4_DST_PORT,
- GFT_PROFILE_TYPE_IP_DST_PORT,
+ GFT_PROFILE_TYPE_IP_DST_ADDR,
+ GFT_PROFILE_TYPE_IP_SRC_ADDR,
+ GFT_PROFILE_TYPE_TUNNEL_TYPE,
MAX_GFT_PROFILE_TYPE
};
@@ -1133,7 +1135,7 @@ struct protocol_dcb_data {
u8 dcb_priority;
u8 dcb_tc;
u8 dscp_val;
- u8 reserved0;
+ u8 dcb_dont_add_vlan0;
};
/* Update tunnel configuration */
@@ -1932,7 +1934,7 @@ enum bin_dbg_buffer_type {
/* Attention bit mapping */
struct dbg_attn_bit_mapping {
- __le16 data;
+ u16 data;
#define DBG_ATTN_BIT_MAPPING_VAL_MASK 0x7FFF
#define DBG_ATTN_BIT_MAPPING_VAL_SHIFT 0
#define DBG_ATTN_BIT_MAPPING_IS_UNUSED_BIT_CNT_MASK 0x1
@@ -1941,11 +1943,12 @@ struct dbg_attn_bit_mapping {
/* Attention block per-type data */
struct dbg_attn_block_type_data {
- __le16 names_offset;
- __le16 reserved1;
+ u16 names_offset;
+ u16 reserved1;
u8 num_regs;
u8 reserved2;
- __le16 regs_offset;
+ u16 regs_offset;
+
};
/* Block attentions */
@@ -1955,15 +1958,15 @@ struct dbg_attn_block {
/* Attention register result */
struct dbg_attn_reg_result {
- __le32 data;
+ u32 data;
#define DBG_ATTN_REG_RESULT_STS_ADDRESS_MASK 0xFFFFFF
#define DBG_ATTN_REG_RESULT_STS_ADDRESS_SHIFT 0
#define DBG_ATTN_REG_RESULT_NUM_REG_ATTN_MASK 0xFF
#define DBG_ATTN_REG_RESULT_NUM_REG_ATTN_SHIFT 24
- __le16 block_attn_offset;
- __le16 reserved;
- __le32 sts_val;
- __le32 mask_val;
+ u16 block_attn_offset;
+ u16 reserved;
+ u32 sts_val;
+ u32 mask_val;
};
/* Attention block result */
@@ -1974,13 +1977,13 @@ struct dbg_attn_block_result {
#define DBG_ATTN_BLOCK_RESULT_ATTN_TYPE_SHIFT 0
#define DBG_ATTN_BLOCK_RESULT_NUM_REGS_MASK 0x3F
#define DBG_ATTN_BLOCK_RESULT_NUM_REGS_SHIFT 2
- __le16 names_offset;
+ u16 names_offset;
struct dbg_attn_reg_result reg_results[15];
};
/* Mode header */
struct dbg_mode_hdr {
- __le16 data;
+ u16 data;
#define DBG_MODE_HDR_EVAL_MODE_MASK 0x1
#define DBG_MODE_HDR_EVAL_MODE_SHIFT 0
#define DBG_MODE_HDR_MODES_BUF_OFFSET_MASK 0x7FFF
@@ -1990,14 +1993,14 @@ struct dbg_mode_hdr {
/* Attention register */
struct dbg_attn_reg {
struct dbg_mode_hdr mode;
- __le16 block_attn_offset;
- __le32 data;
+ u16 block_attn_offset;
+ u32 data;
#define DBG_ATTN_REG_STS_ADDRESS_MASK 0xFFFFFF
#define DBG_ATTN_REG_STS_ADDRESS_SHIFT 0
#define DBG_ATTN_REG_NUM_REG_ATTN_MASK 0xFF
#define DBG_ATTN_REG_NUM_REG_ATTN_SHIFT 24
- __le32 sts_clr_address;
- __le32 mask_address;
+ u32 sts_clr_address;
+ u32 mask_address;
};
/* Attention types */
@@ -2011,14 +2014,14 @@ enum dbg_attn_type {
struct dbg_bus_block {
u8 num_of_lines;
u8 has_latency_events;
- __le16 lines_offset;
+ u16 lines_offset;
};
/* Debug Bus block user data */
struct dbg_bus_block_user_data {
u8 num_of_lines;
u8 has_latency_events;
- __le16 names_offset;
+ u16 names_offset;
};
/* Block Debug line data */
@@ -2042,12 +2045,12 @@ struct dbg_dump_cond_hdr {
/* Memory data for registers dump */
struct dbg_dump_mem {
- __le32 dword0;
+ u32 dword0;
#define DBG_DUMP_MEM_ADDRESS_MASK 0xFFFFFF
#define DBG_DUMP_MEM_ADDRESS_SHIFT 0
#define DBG_DUMP_MEM_MEM_GROUP_ID_MASK 0xFF
#define DBG_DUMP_MEM_MEM_GROUP_ID_SHIFT 24
- __le32 dword1;
+ u32 dword1;
#define DBG_DUMP_MEM_LENGTH_MASK 0xFFFFFF
#define DBG_DUMP_MEM_LENGTH_SHIFT 0
#define DBG_DUMP_MEM_WIDE_BUS_MASK 0x1
@@ -2058,7 +2061,7 @@ struct dbg_dump_mem {
/* Register data for registers dump */
struct dbg_dump_reg {
- __le32 data;
+ u32 data;
#define DBG_DUMP_REG_ADDRESS_MASK 0x7FFFFF
#define DBG_DUMP_REG_ADDRESS_SHIFT 0
#define DBG_DUMP_REG_WIDE_BUS_MASK 0x1
@@ -2069,7 +2072,7 @@ struct dbg_dump_reg {
/* Split header for registers dump */
struct dbg_dump_split_hdr {
- __le32 hdr;
+ u32 hdr;
#define DBG_DUMP_SPLIT_HDR_DATA_SIZE_MASK 0xFFFFFF
#define DBG_DUMP_SPLIT_HDR_DATA_SIZE_SHIFT 0
#define DBG_DUMP_SPLIT_HDR_SPLIT_TYPE_ID_MASK 0xFF
@@ -2079,33 +2082,33 @@ struct dbg_dump_split_hdr {
/* Condition header for idle check */
struct dbg_idle_chk_cond_hdr {
struct dbg_mode_hdr mode; /* Mode header */
- __le16 data_size; /* size in dwords of the data following this header */
+ u16 data_size; /* size in dwords of the data following this header */
};
/* Idle Check condition register */
struct dbg_idle_chk_cond_reg {
- __le32 data;
+ u32 data;
#define DBG_IDLE_CHK_COND_REG_ADDRESS_MASK 0x7FFFFF
#define DBG_IDLE_CHK_COND_REG_ADDRESS_SHIFT 0
#define DBG_IDLE_CHK_COND_REG_WIDE_BUS_MASK 0x1
#define DBG_IDLE_CHK_COND_REG_WIDE_BUS_SHIFT 23
#define DBG_IDLE_CHK_COND_REG_BLOCK_ID_MASK 0xFF
#define DBG_IDLE_CHK_COND_REG_BLOCK_ID_SHIFT 24
- __le16 num_entries;
+ u16 num_entries;
u8 entry_size;
u8 start_entry;
};
/* Idle Check info register */
struct dbg_idle_chk_info_reg {
- __le32 data;
+ u32 data;
#define DBG_IDLE_CHK_INFO_REG_ADDRESS_MASK 0x7FFFFF
#define DBG_IDLE_CHK_INFO_REG_ADDRESS_SHIFT 0
#define DBG_IDLE_CHK_INFO_REG_WIDE_BUS_MASK 0x1
#define DBG_IDLE_CHK_INFO_REG_WIDE_BUS_SHIFT 23
#define DBG_IDLE_CHK_INFO_REG_BLOCK_ID_MASK 0xFF
#define DBG_IDLE_CHK_INFO_REG_BLOCK_ID_SHIFT 24
- __le16 size; /* register size in dwords */
+ u16 size; /* register size in dwords */
struct dbg_mode_hdr mode; /* Mode header */
};
@@ -2117,8 +2120,8 @@ union dbg_idle_chk_reg {
/* Idle Check result header */
struct dbg_idle_chk_result_hdr {
- __le16 rule_id; /* Failing rule index */
- __le16 mem_entry_id; /* Failing memory entry index */
+ u16 rule_id; /* Failing rule index */
+ u16 mem_entry_id; /* Failing memory entry index */
u8 num_dumped_cond_regs; /* number of dumped condition registers */
u8 num_dumped_info_regs; /* number of dumped condition registers */
u8 severity; /* from dbg_idle_chk_severity_types enum */
@@ -2133,29 +2136,29 @@ struct dbg_idle_chk_result_reg_hdr {
#define DBG_IDLE_CHK_RESULT_REG_HDR_REG_ID_MASK 0x7F
#define DBG_IDLE_CHK_RESULT_REG_HDR_REG_ID_SHIFT 1
u8 start_entry; /* index of the first checked entry */
- __le16 size; /* register size in dwords */
+ u16 size; /* register size in dwords */
};
/* Idle Check rule */
struct dbg_idle_chk_rule {
- __le16 rule_id; /* Idle Check rule ID */
+ u16 rule_id; /* Idle Check rule ID */
u8 severity; /* value from dbg_idle_chk_severity_types enum */
u8 cond_id; /* Condition ID */
u8 num_cond_regs; /* number of condition registers */
u8 num_info_regs; /* number of info registers */
u8 num_imms; /* number of immediates in the condition */
u8 reserved1;
- __le16 reg_offset; /* offset of this rules registers in the idle check
- * register array (in dbg_idle_chk_reg units).
- */
- __le16 imm_offset; /* offset of this rules immediate values in the
- * immediate values array (in dwords).
- */
+ u16 reg_offset; /* offset of this rules registers in the idle check
+ * register array (in dbg_idle_chk_reg units).
+ */
+ u16 imm_offset; /* offset of this rules immediate values in the
+ * immediate values array (in dwords).
+ */
};
/* Idle Check rule parsing data */
struct dbg_idle_chk_rule_parsing_data {
- __le32 data;
+ u32 data;
#define DBG_IDLE_CHK_RULE_PARSING_DATA_HAS_FW_MSG_MASK 0x1
#define DBG_IDLE_CHK_RULE_PARSING_DATA_HAS_FW_MSG_SHIFT 0
#define DBG_IDLE_CHK_RULE_PARSING_DATA_STR_OFFSET_MASK 0x7FFFFFFF
@@ -2175,7 +2178,7 @@ enum dbg_idle_chk_severity_types {
/* Debug Bus block data */
struct dbg_bus_block_data {
- __le16 data;
+ u16 data;
#define DBG_BUS_BLOCK_DATA_ENABLE_MASK_MASK 0xF
#define DBG_BUS_BLOCK_DATA_ENABLE_MASK_SHIFT 0
#define DBG_BUS_BLOCK_DATA_RIGHT_SHIFT_MASK 0xF
@@ -2238,15 +2241,15 @@ struct dbg_bus_trigger_state_data {
/* Debug Bus memory address */
struct dbg_bus_mem_addr {
- __le32 lo;
- __le32 hi;
+ u32 lo;
+ u32 hi;
};
/* Debug Bus PCI buffer data */
struct dbg_bus_pci_buf_data {
struct dbg_bus_mem_addr phys_addr; /* PCI buffer physical address */
struct dbg_bus_mem_addr virt_addr; /* PCI buffer virtual address */
- __le32 size; /* PCI buffer size in bytes */
+ u32 size; /* PCI buffer size in bytes */
};
/* Debug Bus Storm EID range filter params */
@@ -2276,15 +2279,15 @@ struct dbg_bus_storm_data {
u8 eid_range_not_mask;
u8 cid_filter_en;
union dbg_bus_storm_eid_params eid_filter_params;
- __le32 cid;
+ u32 cid;
};
/* Debug Bus data */
struct dbg_bus_data {
- __le32 app_version;
+ u32 app_version;
u8 state;
u8 hw_dwords;
- __le16 hw_id_mask;
+ u16 hw_id_mask;
u8 num_enabled_blocks;
u8 num_enabled_storms;
u8 target;
@@ -2295,7 +2298,7 @@ struct dbg_bus_data {
u8 adding_filter;
u8 filter_pre_trigger;
u8 filter_post_trigger;
- __le16 reserved;
+ u16 reserved;
u8 trigger_en;
struct dbg_bus_trigger_state_data trigger_states[3];
u8 next_trigger_state;
@@ -2391,8 +2394,8 @@ enum dbg_bus_targets {
struct dbg_grc_data {
u8 params_initialized;
u8 reserved1;
- __le16 reserved2;
- __le32 param_val[48];
+ u16 reserved2;
+ u32 param_val[48];
};
/* Debug GRC params */
@@ -2414,7 +2417,7 @@ enum dbg_grc_params {
DBG_GRC_PARAM_DUMP_CAU,
DBG_GRC_PARAM_DUMP_QM,
DBG_GRC_PARAM_DUMP_MCP,
- DBG_GRC_PARAM_RESERVED,
+ DBG_GRC_PARAM_MCP_TRACE_META_SIZE,
DBG_GRC_PARAM_DUMP_CFC,
DBG_GRC_PARAM_DUMP_IGU,
DBG_GRC_PARAM_DUMP_BRB,
@@ -2526,10 +2529,10 @@ enum dbg_storms {
/* Idle Check data */
struct idle_chk_data {
- __le32 buf_size;
+ u32 buf_size;
u8 buf_size_set;
u8 reserved1;
- __le16 reserved2;
+ u16 reserved2;
};
/* Debug Tools data (per HW function) */
@@ -2543,7 +2546,7 @@ struct dbg_tools_data {
u8 platform_id;
u8 initialized;
u8 use_dmae;
- __le32 num_regs_read;
+ u32 num_regs_read;
};
/********************************/
@@ -2555,10 +2558,10 @@ struct dbg_tools_data {
/* BRB RAM init requirements */
struct init_brb_ram_req {
- __le32 guranteed_per_tc;
- __le32 headroom_per_tc;
- __le32 min_pkt_size;
- __le32 max_ports_per_engine;
+ u32 guranteed_per_tc;
+ u32 headroom_per_tc;
+ u32 min_pkt_size;
+ u32 max_ports_per_engine;
u8 num_active_tcs[MAX_NUM_PORTS];
};
@@ -2566,21 +2569,21 @@ struct init_brb_ram_req {
struct init_ets_tc_req {
u8 use_sp;
u8 use_wfq;
- __le16 weight;
+ u16 weight;
};
/* ETS init requirements */
struct init_ets_req {
- __le32 mtu;
+ u32 mtu;
struct init_ets_tc_req tc_req[NUM_OF_TCS];
};
/* NIG LB RL init requirements */
struct init_nig_lb_rl_req {
- __le16 lb_mac_rate;
- __le16 lb_rate;
- __le32 mtu;
- __le16 tc_rate[NUM_OF_PHYS_TCS];
+ u16 lb_mac_rate;
+ u16 lb_rate;
+ u32 mtu;
+ u16 tc_rate[NUM_OF_PHYS_TCS];
};
/* NIG TC mapping for each priority */
@@ -2598,9 +2601,9 @@ struct init_nig_pri_tc_map_req {
struct init_qm_port_params {
u8 active;
u8 active_phys_tcs;
- __le16 num_pbf_cmd_lines;
- __le16 num_btb_blocks;
- __le16 reserved;
+ u16 num_pbf_cmd_lines;
+ u16 num_btb_blocks;
+ u16 reserved;
};
/* QM per-PQ init parameters */
@@ -2609,13 +2612,16 @@ struct init_qm_pq_params {
u8 tc_id;
u8 wrr_group;
u8 rl_valid;
+ u8 port_id;
+ u8 reserved0;
+ u16 reserved1;
};
/* QM per-vport init parameters */
struct init_qm_vport_params {
- __le32 vport_rl;
- __le16 vport_wfq;
- __le16 first_tx_pq_id[NUM_OF_TCS];
+ u32 vport_rl;
+ u16 vport_wfq;
+ u16 first_tx_pq_id[NUM_OF_TCS];
};
/**************************************/
@@ -2639,8 +2645,8 @@ enum chip_ids {
};
struct fw_asserts_ram_section {
- __le16 section_ram_line_offset;
- __le16 section_ram_line_size;
+ u16 section_ram_line_offset;
+ u16 section_ram_line_size;
u8 list_dword_offset;
u8 list_element_dword_size;
u8 list_num_elements;
@@ -2713,8 +2719,8 @@ enum init_split_types {
/* Binary buffer header */
struct bin_buffer_hdr {
- __le32 offset;
- __le32 length;
+ u32 offset;
+ u32 length;
};
/* Binary init buffer types */
@@ -2729,7 +2735,7 @@ enum bin_init_buffer_type {
/* init array header: raw */
struct init_array_raw_hdr {
- __le32 data;
+ u32 data;
#define INIT_ARRAY_RAW_HDR_TYPE_MASK 0xF
#define INIT_ARRAY_RAW_HDR_TYPE_SHIFT 0
#define INIT_ARRAY_RAW_HDR_PARAMS_MASK 0xFFFFFFF
@@ -2738,7 +2744,7 @@ struct init_array_raw_hdr {
/* init array header: standard */
struct init_array_standard_hdr {
- __le32 data;
+ u32 data;
#define INIT_ARRAY_STANDARD_HDR_TYPE_MASK 0xF
#define INIT_ARRAY_STANDARD_HDR_TYPE_SHIFT 0
#define INIT_ARRAY_STANDARD_HDR_SIZE_MASK 0xFFFFFFF
@@ -2747,7 +2753,7 @@ struct init_array_standard_hdr {
/* init array header: zipped */
struct init_array_zipped_hdr {
- __le32 data;
+ u32 data;
#define INIT_ARRAY_ZIPPED_HDR_TYPE_MASK 0xF
#define INIT_ARRAY_ZIPPED_HDR_TYPE_SHIFT 0
#define INIT_ARRAY_ZIPPED_HDR_ZIPPED_SIZE_MASK 0xFFFFFFF
@@ -2756,7 +2762,7 @@ struct init_array_zipped_hdr {
/* init array header: pattern */
struct init_array_pattern_hdr {
- __le32 data;
+ u32 data;
#define INIT_ARRAY_PATTERN_HDR_TYPE_MASK 0xF
#define INIT_ARRAY_PATTERN_HDR_TYPE_SHIFT 0
#define INIT_ARRAY_PATTERN_HDR_PATTERN_SIZE_MASK 0xF
@@ -2783,41 +2789,41 @@ enum init_array_types {
/* init operation: callback */
struct init_callback_op {
- __le32 op_data;
+ u32 op_data;
#define INIT_CALLBACK_OP_OP_MASK 0xF
#define INIT_CALLBACK_OP_OP_SHIFT 0
#define INIT_CALLBACK_OP_RESERVED_MASK 0xFFFFFFF
#define INIT_CALLBACK_OP_RESERVED_SHIFT 4
- __le16 callback_id;
- __le16 block_id;
+ u16 callback_id;
+ u16 block_id;
};
/* init operation: delay */
struct init_delay_op {
- __le32 op_data;
+ u32 op_data;
#define INIT_DELAY_OP_OP_MASK 0xF
#define INIT_DELAY_OP_OP_SHIFT 0
#define INIT_DELAY_OP_RESERVED_MASK 0xFFFFFFF
#define INIT_DELAY_OP_RESERVED_SHIFT 4
- __le32 delay;
+ u32 delay;
};
/* init operation: if_mode */
struct init_if_mode_op {
- __le32 op_data;
+ u32 op_data;
#define INIT_IF_MODE_OP_OP_MASK 0xF
#define INIT_IF_MODE_OP_OP_SHIFT 0
#define INIT_IF_MODE_OP_RESERVED1_MASK 0xFFF
#define INIT_IF_MODE_OP_RESERVED1_SHIFT 4
#define INIT_IF_MODE_OP_CMD_OFFSET_MASK 0xFFFF
#define INIT_IF_MODE_OP_CMD_OFFSET_SHIFT 16
- __le16 reserved2;
- __le16 modes_buf_offset;
+ u16 reserved2;
+ u16 modes_buf_offset;
};
/* init operation: if_phase */
struct init_if_phase_op {
- __le32 op_data;
+ u32 op_data;
#define INIT_IF_PHASE_OP_OP_MASK 0xF
#define INIT_IF_PHASE_OP_OP_SHIFT 0
#define INIT_IF_PHASE_OP_DMAE_ENABLE_MASK 0x1
@@ -2826,7 +2832,7 @@ struct init_if_phase_op {
#define INIT_IF_PHASE_OP_RESERVED1_SHIFT 5
#define INIT_IF_PHASE_OP_CMD_OFFSET_MASK 0xFFFF
#define INIT_IF_PHASE_OP_CMD_OFFSET_SHIFT 16
- __le32 phase_data;
+ u32 phase_data;
#define INIT_IF_PHASE_OP_PHASE_MASK 0xFF
#define INIT_IF_PHASE_OP_PHASE_SHIFT 0
#define INIT_IF_PHASE_OP_RESERVED2_MASK 0xFF
@@ -2845,31 +2851,31 @@ enum init_mode_ops {
/* init operation: raw */
struct init_raw_op {
- __le32 op_data;
+ u32 op_data;
#define INIT_RAW_OP_OP_MASK 0xF
#define INIT_RAW_OP_OP_SHIFT 0
#define INIT_RAW_OP_PARAM1_MASK 0xFFFFFFF
#define INIT_RAW_OP_PARAM1_SHIFT 4
- __le32 param2;
+ u32 param2;
};
/* init array params */
struct init_op_array_params {
- __le16 size;
- __le16 offset;
+ u16 size;
+ u16 offset;
};
/* Write init operation arguments */
union init_write_args {
- __le32 inline_val;
- __le32 zeros_count;
- __le32 array_offset;
+ u32 inline_val;
+ u32 zeros_count;
+ u32 array_offset;
struct init_op_array_params runtime;
};
/* init operation: write */
struct init_write_op {
- __le32 data;
+ u32 data;
#define INIT_WRITE_OP_OP_MASK 0xF
#define INIT_WRITE_OP_OP_SHIFT 0
#define INIT_WRITE_OP_SOURCE_MASK 0x7
@@ -2885,7 +2891,7 @@ struct init_write_op {
/* init operation: read */
struct init_read_op {
- __le32 op_data;
+ u32 op_data;
#define INIT_READ_OP_OP_MASK 0xF
#define INIT_READ_OP_OP_SHIFT 0
#define INIT_READ_OP_POLL_TYPE_MASK 0xF
@@ -2894,7 +2900,7 @@ struct init_read_op {
#define INIT_READ_OP_RESERVED_SHIFT 8
#define INIT_READ_OP_ADDRESS_MASK 0x7FFFFF
#define INIT_READ_OP_ADDRESS_SHIFT 9
- __le32 expected_val;
+ u32 expected_val;
};
/* Init operations union */
@@ -2939,11 +2945,11 @@ enum init_source_types {
/* Internal RAM Offsets macro data */
struct iro {
- __le32 base;
- __le16 m1;
- __le16 m2;
- __le16 m3;
- __le16 size;
+ u32 base;
+ u16 m1;
+ u16 m2;
+ u16 m3;
+ u16 size;
};
/***************************** Public Functions *******************************/
@@ -3384,6 +3390,19 @@ enum dbg_status qed_print_mcp_trace_results(struct qed_hwfn *p_hwfn,
char *results_buf);
/**
+ * @brief print_mcp_trace_line - Prints MCP Trace results for a single line
+ *
+ * @param dump_buf - mcp trace dump buffer, starting from the header.
+ * @param num_dumped_bytes - number of bytes that were dumped.
+ * @param results_buf - buffer for printing the mcp trace results.
+ *
+ * @return error if the parsing fails, ok otherwise.
+ */
+enum dbg_status qed_print_mcp_trace_line(u8 *dump_buf,
+ u32 num_dumped_bytes,
+ char *results_buf);
+
+/**
* @brief qed_get_reg_fifo_results_buf_size - Returns the required buffer size
* for reg_fifo results (in bytes).
*
@@ -4005,6 +4024,9 @@ void qed_set_geneve_enable(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
bool eth_geneve_enable, bool ip_geneve_enable);
+void qed_set_vxlan_no_l2_enable(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt, bool enable);
+
/**
* @brief qed_gft_disable - Disable GFT
*
@@ -4348,8 +4370,8 @@ static const struct iro iro_arr[51] = {
{0x80, 0x8, 0x0, 0x0, 0x4},
{0x84, 0x8, 0x0, 0x0, 0x2},
{0x4c48, 0x0, 0x0, 0x0, 0x78},
- {0x3e18, 0x0, 0x0, 0x0, 0x78},
- {0x2b58, 0x0, 0x0, 0x0, 0x78},
+ {0x3e38, 0x0, 0x0, 0x0, 0x78},
+ {0x2b78, 0x0, 0x0, 0x0, 0x78},
{0x4c40, 0x0, 0x0, 0x0, 0x78},
{0x4998, 0x0, 0x0, 0x0, 0x78},
{0x7f50, 0x0, 0x0, 0x0, 0x78},
@@ -4364,7 +4386,7 @@ static const struct iro iro_arr[51] = {
{0x4ba8, 0x80, 0x0, 0x0, 0x20},
{0x8158, 0x40, 0x0, 0x0, 0x30},
{0xe770, 0x60, 0x0, 0x0, 0x60},
- {0x2cf0, 0x80, 0x0, 0x0, 0x38},
+ {0x2d10, 0x80, 0x0, 0x0, 0x38},
{0xf2b8, 0x78, 0x0, 0x0, 0x78},
{0x1f8, 0x4, 0x0, 0x0, 0x4},
{0xaf20, 0x0, 0x0, 0x0, 0xf0},
@@ -4384,10 +4406,10 @@ static const struct iro iro_arr[51] = {
{0x10300, 0x18, 0x0, 0x0, 0x10},
{0xde48, 0x48, 0x0, 0x0, 0x38},
{0x10768, 0x20, 0x0, 0x0, 0x20},
- {0x2d28, 0x80, 0x0, 0x0, 0x10},
+ {0x2d48, 0x80, 0x0, 0x0, 0x10},
{0x5048, 0x10, 0x0, 0x0, 0x10},
{0xc9b8, 0x30, 0x0, 0x0, 0x10},
- {0xeee0, 0x10, 0x0, 0x0, 0x10},
+ {0xed90, 0x10, 0x0, 0x0, 0x10},
{0xa3a0, 0x10, 0x0, 0x0, 0x10},
{0x13108, 0x8, 0x0, 0x0, 0x8},
};
@@ -5151,7 +5173,7 @@ struct e4_xstorm_eth_conn_ag_ctx {
__le16 edpm_num_bds;
__le16 tx_bd_cons;
__le16 tx_bd_prod;
- __le16 tx_class;
+ __le16 updated_qm_pq_id;
__le16 conn_dpi;
u8 byte3;
u8 byte4;
@@ -5674,7 +5696,6 @@ struct eth_vport_rx_mode {
#define ETH_VPORT_RX_MODE_BCAST_ACCEPT_ALL_SHIFT 5
#define ETH_VPORT_RX_MODE_RESERVED1_MASK 0x3FF
#define ETH_VPORT_RX_MODE_RESERVED1_SHIFT 6
- __le16 reserved2[3];
};
/* Command for setting tpa parameters */
@@ -5712,7 +5733,6 @@ struct eth_vport_tx_mode {
#define ETH_VPORT_TX_MODE_BCAST_ACCEPT_ALL_SHIFT 4
#define ETH_VPORT_TX_MODE_RESERVED1_MASK 0x7FF
#define ETH_VPORT_TX_MODE_RESERVED1_SHIFT 5
- __le16 reserved2[3];
};
/* GFT filter update action type */
@@ -5805,7 +5825,8 @@ struct rx_queue_update_ramrod_data {
u8 complete_cqe_flg;
u8 complete_event_flg;
u8 vport_id;
- u8 reserved[4];
+ u8 set_default_rss_queue;
+ u8 reserved[3];
u8 reserved1;
u8 reserved2;
u8 reserved3;
@@ -5843,7 +5864,7 @@ struct rx_update_gft_filter_data {
u8 flow_id_valid;
u8 filter_action;
u8 assert_on_error;
- u8 reserved;
+ u8 inner_vlan_removal_en;
};
/* Ramrod data for rx queue start ramrod */
@@ -5927,7 +5948,7 @@ struct vport_start_ramrod_data {
u8 zero_placement_offset;
u8 ctl_frame_mac_check_en;
u8 ctl_frame_ethtype_check_en;
- u8 reserved[5];
+ u8 reserved[1];
};
/* Ramrod data for vport stop ramrod */
@@ -5992,6 +6013,7 @@ struct vport_update_ramrod_data {
struct eth_vport_rx_mode rx_mode;
struct eth_vport_tx_mode tx_mode;
+ __le32 reserved[3];
struct eth_vport_tpa_param tpa_param;
struct vport_update_ramrod_mcast approx_mcast;
struct eth_vport_rss_config rss_config;
@@ -6213,7 +6235,7 @@ struct e4_xstorm_eth_conn_ag_ctx_dq_ext_ldpart {
__le16 edpm_num_bds;
__le16 tx_bd_cons;
__le16 tx_bd_prod;
- __le16 tx_class;
+ __le16 updated_qm_pq_id;
__le16 conn_dpi;
u8 byte3;
u8 byte4;
@@ -6479,7 +6501,7 @@ struct e4_xstorm_eth_hw_conn_ag_ctx {
__le16 edpm_num_bds;
__le16 tx_bd_cons;
__le16 tx_bd_prod;
- __le16 tx_class;
+ __le16 updated_qm_pq_id;
__le16 conn_dpi;
};
@@ -6703,8 +6725,8 @@ struct e4_ystorm_rdma_task_ag_ctx {
#define E4_YSTORM_RDMA_TASK_AG_CTX_BIT1_SHIFT 5
#define E4_YSTORM_RDMA_TASK_AG_CTX_VALID_MASK 0x1
#define E4_YSTORM_RDMA_TASK_AG_CTX_VALID_SHIFT 6
-#define E4_YSTORM_RDMA_TASK_AG_CTX_BIT3_MASK 0x1
-#define E4_YSTORM_RDMA_TASK_AG_CTX_BIT3_SHIFT 7
+#define E4_YSTORM_RDMA_TASK_AG_CTX_DIF_FIRST_IO_MASK 0x1
+#define E4_YSTORM_RDMA_TASK_AG_CTX_DIF_FIRST_IO_SHIFT 7
u8 flags1;
#define E4_YSTORM_RDMA_TASK_AG_CTX_CF0_MASK 0x3
#define E4_YSTORM_RDMA_TASK_AG_CTX_CF0_SHIFT 0
@@ -6759,8 +6781,8 @@ struct e4_mstorm_rdma_task_ag_ctx {
#define E4_MSTORM_RDMA_TASK_AG_CTX_BIT1_SHIFT 5
#define E4_MSTORM_RDMA_TASK_AG_CTX_BIT2_MASK 0x1
#define E4_MSTORM_RDMA_TASK_AG_CTX_BIT2_SHIFT 6
-#define E4_MSTORM_RDMA_TASK_AG_CTX_BIT3_MASK 0x1
-#define E4_MSTORM_RDMA_TASK_AG_CTX_BIT3_SHIFT 7
+#define E4_MSTORM_RDMA_TASK_AG_CTX_DIF_FIRST_IO_MASK 0x1
+#define E4_MSTORM_RDMA_TASK_AG_CTX_DIF_FIRST_IO_SHIFT 7
u8 flags1;
#define E4_MSTORM_RDMA_TASK_AG_CTX_CF0_MASK 0x3
#define E4_MSTORM_RDMA_TASK_AG_CTX_CF0_SHIFT 0
@@ -6814,7 +6836,7 @@ struct ustorm_rdma_task_st_ctx {
struct e4_ustorm_rdma_task_ag_ctx {
u8 reserved;
- u8 byte1;
+ u8 state;
__le16 icid;
u8 flags0;
#define E4_USTORM_RDMA_TASK_AG_CTX_CONNECTION_TYPE_MASK 0xF
@@ -6830,8 +6852,8 @@ struct e4_ustorm_rdma_task_ag_ctx {
#define E4_USTORM_RDMA_TASK_AG_CTX_DIF_RESULT_TOGGLE_BIT_SHIFT 0
#define E4_USTORM_RDMA_TASK_AG_CTX_DIF_TX_IO_FLG_MASK 0x3
#define E4_USTORM_RDMA_TASK_AG_CTX_DIF_TX_IO_FLG_SHIFT 2
-#define E4_USTORM_RDMA_TASK_AG_CTX_CF3_MASK 0x3
-#define E4_USTORM_RDMA_TASK_AG_CTX_CF3_SHIFT 4
+#define E4_USTORM_RDMA_TASK_AG_CTX_DIF_BLOCK_SIZE_MASK 0x3
+#define E4_USTORM_RDMA_TASK_AG_CTX_DIF_BLOCK_SIZE_SHIFT 4
#define E4_USTORM_RDMA_TASK_AG_CTX_DIF_ERROR_CF_MASK 0x3
#define E4_USTORM_RDMA_TASK_AG_CTX_DIF_ERROR_CF_SHIFT 6
u8 flags2;
@@ -6841,8 +6863,8 @@ struct e4_ustorm_rdma_task_ag_ctx {
#define E4_USTORM_RDMA_TASK_AG_CTX_RESERVED2_SHIFT 1
#define E4_USTORM_RDMA_TASK_AG_CTX_RESERVED3_MASK 0x1
#define E4_USTORM_RDMA_TASK_AG_CTX_RESERVED3_SHIFT 2
-#define E4_USTORM_RDMA_TASK_AG_CTX_CF3EN_MASK 0x1
-#define E4_USTORM_RDMA_TASK_AG_CTX_CF3EN_SHIFT 3
+#define E4_USTORM_RDMA_TASK_AG_CTX_RESERVED4_MASK 0x1
+#define E4_USTORM_RDMA_TASK_AG_CTX_RESERVED4_SHIFT 3
#define E4_USTORM_RDMA_TASK_AG_CTX_DIF_ERROR_CF_EN_MASK 0x1
#define E4_USTORM_RDMA_TASK_AG_CTX_DIF_ERROR_CF_EN_SHIFT 4
#define E4_USTORM_RDMA_TASK_AG_CTX_RULE0EN_MASK 0x1
@@ -6864,10 +6886,17 @@ struct e4_ustorm_rdma_task_ag_ctx {
#define E4_USTORM_RDMA_TASK_AG_CTX_DIF_ERROR_TYPE_SHIFT 4
__le32 dif_err_intervals;
__le32 dif_error_1st_interval;
- __le32 reg2;
+ __le32 sq_cons;
__le32 dif_runt_value;
- __le32 reg4;
+ __le32 sge_index;
__le32 reg5;
+ u8 byte2;
+ u8 byte3;
+ __le16 word1;
+ __le16 word2;
+ __le16 word3;
+ __le32 reg6;
+ __le32 reg7;
};
/* RDMA task context */
@@ -6970,7 +6999,9 @@ struct rdma_init_func_hdr {
u8 vf_id;
u8 vf_valid;
u8 relaxed_ordering;
- u8 reserved[2];
+ __le16 first_reg_srq_id;
+ __le32 reg_srq_base_addr;
+ __le32 reserved;
};
/* rdma function init ramrod data */
@@ -7077,13 +7108,23 @@ struct rdma_srq_context {
/* rdma create qp requester ramrod data */
struct rdma_srq_create_ramrod_data {
+ u8 flags;
+#define RDMA_SRQ_CREATE_RAMROD_DATA_XRC_FLAG_MASK 0x1
+#define RDMA_SRQ_CREATE_RAMROD_DATA_XRC_FLAG_SHIFT 0
+#define RDMA_SRQ_CREATE_RAMROD_DATA_RESERVED_KEY_EN_MASK 0x1
+#define RDMA_SRQ_CREATE_RAMROD_DATA_RESERVED_KEY_EN_SHIFT 1
+#define RDMA_SRQ_CREATE_RAMROD_DATA_RESERVED1_MASK 0x3F
+#define RDMA_SRQ_CREATE_RAMROD_DATA_RESERVED1_SHIFT 2
+ u8 reserved2;
+ __le16 xrc_domain;
+ __le32 xrc_srq_cq_cid;
struct regpair pbl_base_addr;
__le16 pages_in_srq_pbl;
__le16 pd_id;
struct rdma_srq_id srq_id;
__le16 page_size;
- __le16 reserved1;
- __le32 reserved2;
+ __le16 reserved3;
+ __le32 reserved4;
struct regpair producers_addr;
};
@@ -7108,372 +7149,8 @@ enum rdma_tid_type {
MAX_RDMA_TID_TYPE
};
-struct e4_xstorm_roce_conn_ag_ctx_dq_ext_ld_part {
- u8 reserved0;
- u8 state;
- u8 flags0;
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_EXIST_IN_QM0_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_EXIST_IN_QM0_SHIFT 0
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT1_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT1_SHIFT 1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT2_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT2_SHIFT 2
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_EXIST_IN_QM3_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_EXIST_IN_QM3_SHIFT 3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT4_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT4_SHIFT 4
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT5_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT5_SHIFT 5
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT6_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT6_SHIFT 6
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT7_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT7_SHIFT 7
- u8 flags1;
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT8_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT8_SHIFT 0
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT9_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT9_SHIFT 1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT10_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT10_SHIFT 2
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT11_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT11_SHIFT 3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT12_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT12_SHIFT 4
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_MSTORM_FLUSH_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_MSTORM_FLUSH_SHIFT 5
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT14_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT14_SHIFT 6
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_YSTORM_FLUSH_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_YSTORM_FLUSH_SHIFT 7
- u8 flags2;
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF0_MASK 0x3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF0_SHIFT 0
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF1_MASK 0x3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF1_SHIFT 2
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF2_MASK 0x3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF2_SHIFT 4
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF3_MASK 0x3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF3_SHIFT 6
- u8 flags3;
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF4_MASK 0x3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF4_SHIFT 0
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF5_MASK 0x3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF5_SHIFT 2
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF6_MASK 0x3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF6_SHIFT 4
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_FLUSH_Q0_CF_MASK 0x3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_FLUSH_Q0_CF_SHIFT 6
- u8 flags4;
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF8_MASK 0x3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF8_SHIFT 0
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF9_MASK 0x3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF9_SHIFT 2
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF10_MASK 0x3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF10_SHIFT 4
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF11_MASK 0x3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF11_SHIFT 6
- u8 flags5;
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF12_MASK 0x3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF12_SHIFT 0
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF13_MASK 0x3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF13_SHIFT 2
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF14_MASK 0x3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF14_SHIFT 4
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF15_MASK 0x3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF15_SHIFT 6
- u8 flags6;
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF16_MASK 0x3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF16_SHIFT 0
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF17_MASK 0x3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF17_SHIFT 2
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF18_MASK 0x3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF18_SHIFT 4
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF19_MASK 0x3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF19_SHIFT 6
- u8 flags7;
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF20_MASK 0x3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF20_SHIFT 0
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF21_MASK 0x3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF21_SHIFT 2
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_SLOW_PATH_MASK 0x3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_SLOW_PATH_SHIFT 4
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF0EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF0EN_SHIFT 6
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF1EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF1EN_SHIFT 7
- u8 flags8;
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF2EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF2EN_SHIFT 0
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF3EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF3EN_SHIFT 1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF4EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF4EN_SHIFT 2
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF5EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF5EN_SHIFT 3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF6EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF6EN_SHIFT 4
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_FLUSH_Q0_CF_EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_FLUSH_Q0_CF_EN_SHIFT 5
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF8EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF8EN_SHIFT 6
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF9EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF9EN_SHIFT 7
- u8 flags9;
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF10EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF10EN_SHIFT 0
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF11EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF11EN_SHIFT 1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF12EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF12EN_SHIFT 2
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF13EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF13EN_SHIFT 3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF14EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF14EN_SHIFT 4
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF15EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF15EN_SHIFT 5
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF16EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF16EN_SHIFT 6
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF17EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF17EN_SHIFT 7
- u8 flags10;
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF18EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF18EN_SHIFT 0
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF19EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF19EN_SHIFT 1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF20EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF20EN_SHIFT 2
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF21EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF21EN_SHIFT 3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_SLOW_PATH_EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_SLOW_PATH_EN_SHIFT 4
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF23EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF23EN_SHIFT 5
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE0EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE0EN_SHIFT 6
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE1EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE1EN_SHIFT 7
- u8 flags11;
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE2EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE2EN_SHIFT 0
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE3EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE3EN_SHIFT 1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE4EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE4EN_SHIFT 2
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE5EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE5EN_SHIFT 3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE6EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE6EN_SHIFT 4
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE7EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE7EN_SHIFT 5
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED1_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED1_SHIFT 6
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE9EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE9EN_SHIFT 7
- u8 flags12;
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE10EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE10EN_SHIFT 0
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE11EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE11EN_SHIFT 1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED2_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED2_SHIFT 2
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED3_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED3_SHIFT 3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE14EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE14EN_SHIFT 4
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE15EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE15EN_SHIFT 5
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE16EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE16EN_SHIFT 6
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE17EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE17EN_SHIFT 7
- u8 flags13;
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE18EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE18EN_SHIFT 0
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE19EN_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE19EN_SHIFT 1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED4_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED4_SHIFT 2
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED5_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED5_SHIFT 3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED6_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED6_SHIFT 4
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED7_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED7_SHIFT 5
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED8_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED8_SHIFT 6
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED9_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED9_SHIFT 7
- u8 flags14;
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_MIGRATION_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_MIGRATION_SHIFT 0
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT17_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT17_SHIFT 1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_DPM_PORT_NUM_MASK 0x3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_DPM_PORT_NUM_SHIFT 2
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RESERVED_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RESERVED_SHIFT 4
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_ROCE_EDPM_ENABLE_MASK 0x1
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_ROCE_EDPM_ENABLE_SHIFT 5
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF23_MASK 0x3
-#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF23_SHIFT 6
- u8 byte2;
- __le16 physical_q0;
- __le16 word1;
- __le16 word2;
- __le16 word3;
- __le16 word4;
- __le16 word5;
- __le16 conn_dpi;
- u8 byte3;
- u8 byte4;
- u8 byte5;
- u8 byte6;
- __le32 reg0;
- __le32 reg1;
- __le32 reg2;
- __le32 snd_nxt_psn;
- __le32 reg4;
-};
-
-struct e4_mstorm_rdma_conn_ag_ctx {
- u8 byte0;
- u8 byte1;
- u8 flags0;
-#define E4_MSTORM_RDMA_CONN_AG_CTX_BIT0_MASK 0x1
-#define E4_MSTORM_RDMA_CONN_AG_CTX_BIT0_SHIFT 0
-#define E4_MSTORM_RDMA_CONN_AG_CTX_BIT1_MASK 0x1
-#define E4_MSTORM_RDMA_CONN_AG_CTX_BIT1_SHIFT 1
-#define E4_MSTORM_RDMA_CONN_AG_CTX_CF0_MASK 0x3
-#define E4_MSTORM_RDMA_CONN_AG_CTX_CF0_SHIFT 2
-#define E4_MSTORM_RDMA_CONN_AG_CTX_CF1_MASK 0x3
-#define E4_MSTORM_RDMA_CONN_AG_CTX_CF1_SHIFT 4
-#define E4_MSTORM_RDMA_CONN_AG_CTX_CF2_MASK 0x3
-#define E4_MSTORM_RDMA_CONN_AG_CTX_CF2_SHIFT 6
- u8 flags1;
-#define E4_MSTORM_RDMA_CONN_AG_CTX_CF0EN_MASK 0x1
-#define E4_MSTORM_RDMA_CONN_AG_CTX_CF0EN_SHIFT 0
-#define E4_MSTORM_RDMA_CONN_AG_CTX_CF1EN_MASK 0x1
-#define E4_MSTORM_RDMA_CONN_AG_CTX_CF1EN_SHIFT 1
-#define E4_MSTORM_RDMA_CONN_AG_CTX_CF2EN_MASK 0x1
-#define E4_MSTORM_RDMA_CONN_AG_CTX_CF2EN_SHIFT 2
-#define E4_MSTORM_RDMA_CONN_AG_CTX_RULE0EN_MASK 0x1
-#define E4_MSTORM_RDMA_CONN_AG_CTX_RULE0EN_SHIFT 3
-#define E4_MSTORM_RDMA_CONN_AG_CTX_RULE1EN_MASK 0x1
-#define E4_MSTORM_RDMA_CONN_AG_CTX_RULE1EN_SHIFT 4
-#define E4_MSTORM_RDMA_CONN_AG_CTX_RULE2EN_MASK 0x1
-#define E4_MSTORM_RDMA_CONN_AG_CTX_RULE2EN_SHIFT 5
-#define E4_MSTORM_RDMA_CONN_AG_CTX_RULE3EN_MASK 0x1
-#define E4_MSTORM_RDMA_CONN_AG_CTX_RULE3EN_SHIFT 6
-#define E4_MSTORM_RDMA_CONN_AG_CTX_RULE4EN_MASK 0x1
-#define E4_MSTORM_RDMA_CONN_AG_CTX_RULE4EN_SHIFT 7
- __le16 word0;
- __le16 word1;
- __le32 reg0;
- __le32 reg1;
-};
-
-struct e4_tstorm_rdma_conn_ag_ctx {
- u8 reserved0;
- u8 byte1;
- u8 flags0;
-#define E4_TSTORM_RDMA_CONN_AG_CTX_EXIST_IN_QM0_MASK 0x1
-#define E4_TSTORM_RDMA_CONN_AG_CTX_EXIST_IN_QM0_SHIFT 0
-#define E4_TSTORM_RDMA_CONN_AG_CTX_BIT1_MASK 0x1
-#define E4_TSTORM_RDMA_CONN_AG_CTX_BIT1_SHIFT 1
-#define E4_TSTORM_RDMA_CONN_AG_CTX_BIT2_MASK 0x1
-#define E4_TSTORM_RDMA_CONN_AG_CTX_BIT2_SHIFT 2
-#define E4_TSTORM_RDMA_CONN_AG_CTX_BIT3_MASK 0x1
-#define E4_TSTORM_RDMA_CONN_AG_CTX_BIT3_SHIFT 3
-#define E4_TSTORM_RDMA_CONN_AG_CTX_BIT4_MASK 0x1
-#define E4_TSTORM_RDMA_CONN_AG_CTX_BIT4_SHIFT 4
-#define E4_TSTORM_RDMA_CONN_AG_CTX_BIT5_MASK 0x1
-#define E4_TSTORM_RDMA_CONN_AG_CTX_BIT5_SHIFT 5
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF0_MASK 0x3
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF0_SHIFT 6
- u8 flags1;
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF1_MASK 0x3
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF1_SHIFT 0
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF2_MASK 0x3
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF2_SHIFT 2
-#define E4_TSTORM_RDMA_CONN_AG_CTX_TIMER_STOP_ALL_CF_MASK 0x3
-#define E4_TSTORM_RDMA_CONN_AG_CTX_TIMER_STOP_ALL_CF_SHIFT 4
-#define E4_TSTORM_RDMA_CONN_AG_CTX_FLUSH_Q0_CF_MASK 0x3
-#define E4_TSTORM_RDMA_CONN_AG_CTX_FLUSH_Q0_CF_SHIFT 6
- u8 flags2;
-#define E4_TSTORM_RDMA_CONN_AG_CTX_MSTORM_FLUSH_CF_MASK 0x3
-#define E4_TSTORM_RDMA_CONN_AG_CTX_MSTORM_FLUSH_CF_SHIFT 0
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF6_MASK 0x3
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF6_SHIFT 2
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF7_MASK 0x3
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF7_SHIFT 4
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF8_MASK 0x3
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF8_SHIFT 6
- u8 flags3;
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF9_MASK 0x3
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF9_SHIFT 0
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF10_MASK 0x3
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF10_SHIFT 2
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF0EN_MASK 0x1
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF0EN_SHIFT 4
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF1EN_MASK 0x1
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF1EN_SHIFT 5
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF2EN_MASK 0x1
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF2EN_SHIFT 6
-#define E4_TSTORM_RDMA_CONN_AG_CTX_TIMER_STOP_ALL_CF_EN_MASK 0x1
-#define E4_TSTORM_RDMA_CONN_AG_CTX_TIMER_STOP_ALL_CF_EN_SHIFT 7
- u8 flags4;
-#define E4_TSTORM_RDMA_CONN_AG_CTX_FLUSH_Q0_CF_EN_MASK 0x1
-#define E4_TSTORM_RDMA_CONN_AG_CTX_FLUSH_Q0_CF_EN_SHIFT 0
-#define E4_TSTORM_RDMA_CONN_AG_CTX_MSTORM_FLUSH_CF_EN_MASK 0x1
-#define E4_TSTORM_RDMA_CONN_AG_CTX_MSTORM_FLUSH_CF_EN_SHIFT 1
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF6EN_MASK 0x1
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF6EN_SHIFT 2
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF7EN_MASK 0x1
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF7EN_SHIFT 3
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF8EN_MASK 0x1
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF8EN_SHIFT 4
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF9EN_MASK 0x1
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF9EN_SHIFT 5
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF10EN_MASK 0x1
-#define E4_TSTORM_RDMA_CONN_AG_CTX_CF10EN_SHIFT 6
-#define E4_TSTORM_RDMA_CONN_AG_CTX_RULE0EN_MASK 0x1
-#define E4_TSTORM_RDMA_CONN_AG_CTX_RULE0EN_SHIFT 7
- u8 flags5;
-#define E4_TSTORM_RDMA_CONN_AG_CTX_RULE1EN_MASK 0x1
-#define E4_TSTORM_RDMA_CONN_AG_CTX_RULE1EN_SHIFT 0
-#define E4_TSTORM_RDMA_CONN_AG_CTX_RULE2EN_MASK 0x1
-#define E4_TSTORM_RDMA_CONN_AG_CTX_RULE2EN_SHIFT 1
-#define E4_TSTORM_RDMA_CONN_AG_CTX_RULE3EN_MASK 0x1
-#define E4_TSTORM_RDMA_CONN_AG_CTX_RULE3EN_SHIFT 2
-#define E4_TSTORM_RDMA_CONN_AG_CTX_RULE4EN_MASK 0x1
-#define E4_TSTORM_RDMA_CONN_AG_CTX_RULE4EN_SHIFT 3
-#define E4_TSTORM_RDMA_CONN_AG_CTX_RULE5EN_MASK 0x1
-#define E4_TSTORM_RDMA_CONN_AG_CTX_RULE5EN_SHIFT 4
-#define E4_TSTORM_RDMA_CONN_AG_CTX_RULE6EN_MASK 0x1
-#define E4_TSTORM_RDMA_CONN_AG_CTX_RULE6EN_SHIFT 5
-#define E4_TSTORM_RDMA_CONN_AG_CTX_RULE7EN_MASK 0x1
-#define E4_TSTORM_RDMA_CONN_AG_CTX_RULE7EN_SHIFT 6
-#define E4_TSTORM_RDMA_CONN_AG_CTX_RULE8EN_MASK 0x1
-#define E4_TSTORM_RDMA_CONN_AG_CTX_RULE8EN_SHIFT 7
- __le32 reg0;
- __le32 reg1;
- __le32 reg2;
- __le32 reg3;
- __le32 reg4;
- __le32 reg5;
- __le32 reg6;
- __le32 reg7;
- __le32 reg8;
- u8 byte2;
- u8 byte3;
- __le16 word0;
- u8 byte4;
- u8 byte5;
- __le16 word1;
- __le16 word2;
- __le16 word3;
- __le32 reg9;
- __le32 reg10;
+struct rdma_xrc_srq_context {
+ struct regpair temp[9];
};
struct e4_tstorm_rdma_task_ag_ctx {
@@ -7561,8 +7238,8 @@ struct e4_ustorm_rdma_conn_ag_ctx {
u8 flags0;
#define E4_USTORM_RDMA_CONN_AG_CTX_EXIST_IN_QM0_MASK 0x1
#define E4_USTORM_RDMA_CONN_AG_CTX_EXIST_IN_QM0_SHIFT 0
-#define E4_USTORM_RDMA_CONN_AG_CTX_BIT1_MASK 0x1
-#define E4_USTORM_RDMA_CONN_AG_CTX_BIT1_SHIFT 1
+#define E4_USTORM_RDMA_CONN_AG_CTX_DIF_ERROR_REPORTED_MASK 0x1
+#define E4_USTORM_RDMA_CONN_AG_CTX_DIF_ERROR_REPORTED_SHIFT 1
#define E4_USTORM_RDMA_CONN_AG_CTX_FLUSH_Q0_CF_MASK 0x3
#define E4_USTORM_RDMA_CONN_AG_CTX_FLUSH_Q0_CF_SHIFT 2
#define E4_USTORM_RDMA_CONN_AG_CTX_CF1_MASK 0x3
@@ -7624,214 +7301,214 @@ struct e4_ustorm_rdma_conn_ag_ctx {
__le16 word3;
};
-struct e4_xstorm_rdma_conn_ag_ctx {
+struct e4_xstorm_roce_conn_ag_ctx {
u8 reserved0;
u8 state;
u8 flags0;
-#define E4_XSTORM_RDMA_CONN_AG_CTX_EXIST_IN_QM0_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_EXIST_IN_QM0_SHIFT 0
-#define E4_XSTORM_RDMA_CONN_AG_CTX_BIT1_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_BIT1_SHIFT 1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_BIT2_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_BIT2_SHIFT 2
-#define E4_XSTORM_RDMA_CONN_AG_CTX_EXIST_IN_QM3_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_EXIST_IN_QM3_SHIFT 3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_BIT4_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_BIT4_SHIFT 4
-#define E4_XSTORM_RDMA_CONN_AG_CTX_BIT5_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_BIT5_SHIFT 5
-#define E4_XSTORM_RDMA_CONN_AG_CTX_BIT6_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_BIT6_SHIFT 6
-#define E4_XSTORM_RDMA_CONN_AG_CTX_BIT7_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_BIT7_SHIFT 7
+#define E4_XSTORM_ROCE_CONN_AG_CTX_EXIST_IN_QM0_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_EXIST_IN_QM0_SHIFT 0
+#define E4_XSTORM_ROCE_CONN_AG_CTX_BIT1_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_BIT1_SHIFT 1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_BIT2_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_BIT2_SHIFT 2
+#define E4_XSTORM_ROCE_CONN_AG_CTX_EXIST_IN_QM3_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_EXIST_IN_QM3_SHIFT 3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_BIT4_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_BIT4_SHIFT 4
+#define E4_XSTORM_ROCE_CONN_AG_CTX_BIT5_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_BIT5_SHIFT 5
+#define E4_XSTORM_ROCE_CONN_AG_CTX_BIT6_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_BIT6_SHIFT 6
+#define E4_XSTORM_ROCE_CONN_AG_CTX_BIT7_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_BIT7_SHIFT 7
u8 flags1;
-#define E4_XSTORM_RDMA_CONN_AG_CTX_BIT8_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_BIT8_SHIFT 0
-#define E4_XSTORM_RDMA_CONN_AG_CTX_BIT9_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_BIT9_SHIFT 1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_BIT10_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_BIT10_SHIFT 2
-#define E4_XSTORM_RDMA_CONN_AG_CTX_BIT11_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_BIT11_SHIFT 3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_BIT12_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_BIT12_SHIFT 4
-#define E4_XSTORM_RDMA_CONN_AG_CTX_MSTORM_FLUSH_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_MSTORM_FLUSH_SHIFT 5
-#define E4_XSTORM_RDMA_CONN_AG_CTX_BIT14_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_BIT14_SHIFT 6
-#define E4_XSTORM_RDMA_CONN_AG_CTX_YSTORM_FLUSH_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_YSTORM_FLUSH_SHIFT 7
+#define E4_XSTORM_ROCE_CONN_AG_CTX_BIT8_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_BIT8_SHIFT 0
+#define E4_XSTORM_ROCE_CONN_AG_CTX_BIT9_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_BIT9_SHIFT 1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_BIT10_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_BIT10_SHIFT 2
+#define E4_XSTORM_ROCE_CONN_AG_CTX_BIT11_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_BIT11_SHIFT 3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_BIT12_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_BIT12_SHIFT 4
+#define E4_XSTORM_ROCE_CONN_AG_CTX_MSEM_FLUSH_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_MSEM_FLUSH_SHIFT 5
+#define E4_XSTORM_ROCE_CONN_AG_CTX_MSDM_FLUSH_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_MSDM_FLUSH_SHIFT 6
+#define E4_XSTORM_ROCE_CONN_AG_CTX_YSTORM_FLUSH_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_YSTORM_FLUSH_SHIFT 7
u8 flags2;
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF0_MASK 0x3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF0_SHIFT 0
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF1_MASK 0x3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF1_SHIFT 2
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF2_MASK 0x3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF2_SHIFT 4
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF3_MASK 0x3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF3_SHIFT 6
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF0_MASK 0x3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF0_SHIFT 0
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF1_MASK 0x3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF1_SHIFT 2
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF2_MASK 0x3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF2_SHIFT 4
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF3_MASK 0x3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF3_SHIFT 6
u8 flags3;
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF4_MASK 0x3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF4_SHIFT 0
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF5_MASK 0x3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF5_SHIFT 2
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF6_MASK 0x3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF6_SHIFT 4
-#define E4_XSTORM_RDMA_CONN_AG_CTX_FLUSH_Q0_CF_MASK 0x3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_FLUSH_Q0_CF_SHIFT 6
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF4_MASK 0x3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF4_SHIFT 0
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF5_MASK 0x3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF5_SHIFT 2
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF6_MASK 0x3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF6_SHIFT 4
+#define E4_XSTORM_ROCE_CONN_AG_CTX_FLUSH_Q0_CF_MASK 0x3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_FLUSH_Q0_CF_SHIFT 6
u8 flags4;
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF8_MASK 0x3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF8_SHIFT 0
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF9_MASK 0x3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF9_SHIFT 2
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF10_MASK 0x3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF10_SHIFT 4
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF11_MASK 0x3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF11_SHIFT 6
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF8_MASK 0x3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF8_SHIFT 0
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF9_MASK 0x3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF9_SHIFT 2
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF10_MASK 0x3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF10_SHIFT 4
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF11_MASK 0x3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF11_SHIFT 6
u8 flags5;
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF12_MASK 0x3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF12_SHIFT 0
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF13_MASK 0x3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF13_SHIFT 2
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF14_MASK 0x3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF14_SHIFT 4
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF15_MASK 0x3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF15_SHIFT 6
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF12_MASK 0x3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF12_SHIFT 0
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF13_MASK 0x3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF13_SHIFT 2
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF14_MASK 0x3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF14_SHIFT 4
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF15_MASK 0x3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF15_SHIFT 6
u8 flags6;
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF16_MASK 0x3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF16_SHIFT 0
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF17_MASK 0x3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF17_SHIFT 2
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF18_MASK 0x3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF18_SHIFT 4
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF19_MASK 0x3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF19_SHIFT 6
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF16_MASK 0x3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF16_SHIFT 0
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF17_MASK 0x3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF17_SHIFT 2
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF18_MASK 0x3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF18_SHIFT 4
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF19_MASK 0x3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF19_SHIFT 6
u8 flags7;
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF20_MASK 0x3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF20_SHIFT 0
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF21_MASK 0x3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF21_SHIFT 2
-#define E4_XSTORM_RDMA_CONN_AG_CTX_SLOW_PATH_MASK 0x3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_SLOW_PATH_SHIFT 4
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF0EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF0EN_SHIFT 6
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF1EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF1EN_SHIFT 7
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF20_MASK 0x3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF20_SHIFT 0
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF21_MASK 0x3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF21_SHIFT 2
+#define E4_XSTORM_ROCE_CONN_AG_CTX_SLOW_PATH_MASK 0x3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_SLOW_PATH_SHIFT 4
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF0EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF0EN_SHIFT 6
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF1EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF1EN_SHIFT 7
u8 flags8;
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF2EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF2EN_SHIFT 0
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF3EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF3EN_SHIFT 1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF4EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF4EN_SHIFT 2
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF5EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF5EN_SHIFT 3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF6EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF6EN_SHIFT 4
-#define E4_XSTORM_RDMA_CONN_AG_CTX_FLUSH_Q0_CF_EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_FLUSH_Q0_CF_EN_SHIFT 5
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF8EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF8EN_SHIFT 6
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF9EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF9EN_SHIFT 7
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF2EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF2EN_SHIFT 0
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF3EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF3EN_SHIFT 1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF4EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF4EN_SHIFT 2
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF5EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF5EN_SHIFT 3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF6EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF6EN_SHIFT 4
+#define E4_XSTORM_ROCE_CONN_AG_CTX_FLUSH_Q0_CF_EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_FLUSH_Q0_CF_EN_SHIFT 5
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF8EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF8EN_SHIFT 6
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF9EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF9EN_SHIFT 7
u8 flags9;
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF10EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF10EN_SHIFT 0
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF11EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF11EN_SHIFT 1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF12EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF12EN_SHIFT 2
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF13EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF13EN_SHIFT 3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF14EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF14EN_SHIFT 4
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF15EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF15EN_SHIFT 5
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF16EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF16EN_SHIFT 6
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF17EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF17EN_SHIFT 7
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF10EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF10EN_SHIFT 0
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF11EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF11EN_SHIFT 1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF12EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF12EN_SHIFT 2
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF13EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF13EN_SHIFT 3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF14EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF14EN_SHIFT 4
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF15EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF15EN_SHIFT 5
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF16EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF16EN_SHIFT 6
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF17EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF17EN_SHIFT 7
u8 flags10;
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF18EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF18EN_SHIFT 0
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF19EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF19EN_SHIFT 1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF20EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF20EN_SHIFT 2
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF21EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF21EN_SHIFT 3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_SLOW_PATH_EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_SLOW_PATH_EN_SHIFT 4
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF23EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF23EN_SHIFT 5
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE0EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE0EN_SHIFT 6
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE1EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE1EN_SHIFT 7
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF18EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF18EN_SHIFT 0
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF19EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF19EN_SHIFT 1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF20EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF20EN_SHIFT 2
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF21EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF21EN_SHIFT 3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_SLOW_PATH_EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_SLOW_PATH_EN_SHIFT 4
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF23EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF23EN_SHIFT 5
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE0EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE0EN_SHIFT 6
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE1EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE1EN_SHIFT 7
u8 flags11;
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE2EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE2EN_SHIFT 0
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE3EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE3EN_SHIFT 1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE4EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE4EN_SHIFT 2
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE5EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE5EN_SHIFT 3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE6EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE6EN_SHIFT 4
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE7EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE7EN_SHIFT 5
-#define E4_XSTORM_RDMA_CONN_AG_CTX_A0_RESERVED1_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_A0_RESERVED1_SHIFT 6
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE9EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE9EN_SHIFT 7
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE2EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE2EN_SHIFT 0
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE3EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE3EN_SHIFT 1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE4EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE4EN_SHIFT 2
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE5EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE5EN_SHIFT 3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE6EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE6EN_SHIFT 4
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE7EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE7EN_SHIFT 5
+#define E4_XSTORM_ROCE_CONN_AG_CTX_A0_RESERVED1_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_A0_RESERVED1_SHIFT 6
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE9EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE9EN_SHIFT 7
u8 flags12;
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE10EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE10EN_SHIFT 0
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE11EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE11EN_SHIFT 1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_A0_RESERVED2_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_A0_RESERVED2_SHIFT 2
-#define E4_XSTORM_RDMA_CONN_AG_CTX_A0_RESERVED3_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_A0_RESERVED3_SHIFT 3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE14EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE14EN_SHIFT 4
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE15EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE15EN_SHIFT 5
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE16EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE16EN_SHIFT 6
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE17EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE17EN_SHIFT 7
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE10EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE10EN_SHIFT 0
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE11EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE11EN_SHIFT 1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_A0_RESERVED2_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_A0_RESERVED2_SHIFT 2
+#define E4_XSTORM_ROCE_CONN_AG_CTX_A0_RESERVED3_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_A0_RESERVED3_SHIFT 3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE14EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE14EN_SHIFT 4
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE15EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE15EN_SHIFT 5
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE16EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE16EN_SHIFT 6
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE17EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE17EN_SHIFT 7
u8 flags13;
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE18EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE18EN_SHIFT 0
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE19EN_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RULE19EN_SHIFT 1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_A0_RESERVED4_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_A0_RESERVED4_SHIFT 2
-#define E4_XSTORM_RDMA_CONN_AG_CTX_A0_RESERVED5_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_A0_RESERVED5_SHIFT 3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_A0_RESERVED6_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_A0_RESERVED6_SHIFT 4
-#define E4_XSTORM_RDMA_CONN_AG_CTX_A0_RESERVED7_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_A0_RESERVED7_SHIFT 5
-#define E4_XSTORM_RDMA_CONN_AG_CTX_A0_RESERVED8_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_A0_RESERVED8_SHIFT 6
-#define E4_XSTORM_RDMA_CONN_AG_CTX_A0_RESERVED9_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_A0_RESERVED9_SHIFT 7
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE18EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE18EN_SHIFT 0
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE19EN_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RULE19EN_SHIFT 1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_A0_RESERVED4_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_A0_RESERVED4_SHIFT 2
+#define E4_XSTORM_ROCE_CONN_AG_CTX_A0_RESERVED5_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_A0_RESERVED5_SHIFT 3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_A0_RESERVED6_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_A0_RESERVED6_SHIFT 4
+#define E4_XSTORM_ROCE_CONN_AG_CTX_A0_RESERVED7_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_A0_RESERVED7_SHIFT 5
+#define E4_XSTORM_ROCE_CONN_AG_CTX_A0_RESERVED8_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_A0_RESERVED8_SHIFT 6
+#define E4_XSTORM_ROCE_CONN_AG_CTX_A0_RESERVED9_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_A0_RESERVED9_SHIFT 7
u8 flags14;
-#define E4_XSTORM_RDMA_CONN_AG_CTX_MIGRATION_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_MIGRATION_SHIFT 0
-#define E4_XSTORM_RDMA_CONN_AG_CTX_BIT17_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_BIT17_SHIFT 1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_DPM_PORT_NUM_MASK 0x3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_DPM_PORT_NUM_SHIFT 2
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RESERVED_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_RESERVED_SHIFT 4
-#define E4_XSTORM_RDMA_CONN_AG_CTX_ROCE_EDPM_ENABLE_MASK 0x1
-#define E4_XSTORM_RDMA_CONN_AG_CTX_ROCE_EDPM_ENABLE_SHIFT 5
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF23_MASK 0x3
-#define E4_XSTORM_RDMA_CONN_AG_CTX_CF23_SHIFT 6
+#define E4_XSTORM_ROCE_CONN_AG_CTX_MIGRATION_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_MIGRATION_SHIFT 0
+#define E4_XSTORM_ROCE_CONN_AG_CTX_BIT17_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_BIT17_SHIFT 1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_DPM_PORT_NUM_MASK 0x3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_DPM_PORT_NUM_SHIFT 2
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RESERVED_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_RESERVED_SHIFT 4
+#define E4_XSTORM_ROCE_CONN_AG_CTX_ROCE_EDPM_ENABLE_MASK 0x1
+#define E4_XSTORM_ROCE_CONN_AG_CTX_ROCE_EDPM_ENABLE_SHIFT 5
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF23_MASK 0x3
+#define E4_XSTORM_ROCE_CONN_AG_CTX_CF23_SHIFT 6
u8 byte2;
__le16 physical_q0;
__le16 word1;
@@ -7853,48 +7530,108 @@ struct e4_xstorm_rdma_conn_ag_ctx {
__le32 reg6;
};
-struct e4_ystorm_rdma_conn_ag_ctx {
- u8 byte0;
+struct e4_tstorm_roce_conn_ag_ctx {
+ u8 reserved0;
u8 byte1;
u8 flags0;
-#define E4_YSTORM_RDMA_CONN_AG_CTX_BIT0_MASK 0x1
-#define E4_YSTORM_RDMA_CONN_AG_CTX_BIT0_SHIFT 0
-#define E4_YSTORM_RDMA_CONN_AG_CTX_BIT1_MASK 0x1
-#define E4_YSTORM_RDMA_CONN_AG_CTX_BIT1_SHIFT 1
-#define E4_YSTORM_RDMA_CONN_AG_CTX_CF0_MASK 0x3
-#define E4_YSTORM_RDMA_CONN_AG_CTX_CF0_SHIFT 2
-#define E4_YSTORM_RDMA_CONN_AG_CTX_CF1_MASK 0x3
-#define E4_YSTORM_RDMA_CONN_AG_CTX_CF1_SHIFT 4
-#define E4_YSTORM_RDMA_CONN_AG_CTX_CF2_MASK 0x3
-#define E4_YSTORM_RDMA_CONN_AG_CTX_CF2_SHIFT 6
+#define E4_TSTORM_ROCE_CONN_AG_CTX_EXIST_IN_QM0_MASK 0x1
+#define E4_TSTORM_ROCE_CONN_AG_CTX_EXIST_IN_QM0_SHIFT 0
+#define E4_TSTORM_ROCE_CONN_AG_CTX_BIT1_MASK 0x1
+#define E4_TSTORM_ROCE_CONN_AG_CTX_BIT1_SHIFT 1
+#define E4_TSTORM_ROCE_CONN_AG_CTX_BIT2_MASK 0x1
+#define E4_TSTORM_ROCE_CONN_AG_CTX_BIT2_SHIFT 2
+#define E4_TSTORM_ROCE_CONN_AG_CTX_BIT3_MASK 0x1
+#define E4_TSTORM_ROCE_CONN_AG_CTX_BIT3_SHIFT 3
+#define E4_TSTORM_ROCE_CONN_AG_CTX_BIT4_MASK 0x1
+#define E4_TSTORM_ROCE_CONN_AG_CTX_BIT4_SHIFT 4
+#define E4_TSTORM_ROCE_CONN_AG_CTX_BIT5_MASK 0x1
+#define E4_TSTORM_ROCE_CONN_AG_CTX_BIT5_SHIFT 5
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF0_MASK 0x3
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF0_SHIFT 6
u8 flags1;
-#define E4_YSTORM_RDMA_CONN_AG_CTX_CF0EN_MASK 0x1
-#define E4_YSTORM_RDMA_CONN_AG_CTX_CF0EN_SHIFT 0
-#define E4_YSTORM_RDMA_CONN_AG_CTX_CF1EN_MASK 0x1
-#define E4_YSTORM_RDMA_CONN_AG_CTX_CF1EN_SHIFT 1
-#define E4_YSTORM_RDMA_CONN_AG_CTX_CF2EN_MASK 0x1
-#define E4_YSTORM_RDMA_CONN_AG_CTX_CF2EN_SHIFT 2
-#define E4_YSTORM_RDMA_CONN_AG_CTX_RULE0EN_MASK 0x1
-#define E4_YSTORM_RDMA_CONN_AG_CTX_RULE0EN_SHIFT 3
-#define E4_YSTORM_RDMA_CONN_AG_CTX_RULE1EN_MASK 0x1
-#define E4_YSTORM_RDMA_CONN_AG_CTX_RULE1EN_SHIFT 4
-#define E4_YSTORM_RDMA_CONN_AG_CTX_RULE2EN_MASK 0x1
-#define E4_YSTORM_RDMA_CONN_AG_CTX_RULE2EN_SHIFT 5
-#define E4_YSTORM_RDMA_CONN_AG_CTX_RULE3EN_MASK 0x1
-#define E4_YSTORM_RDMA_CONN_AG_CTX_RULE3EN_SHIFT 6
-#define E4_YSTORM_RDMA_CONN_AG_CTX_RULE4EN_MASK 0x1
-#define E4_YSTORM_RDMA_CONN_AG_CTX_RULE4EN_SHIFT 7
+#define E4_TSTORM_ROCE_CONN_AG_CTX_MSTORM_FLUSH_CF_MASK 0x3
+#define E4_TSTORM_ROCE_CONN_AG_CTX_MSTORM_FLUSH_CF_SHIFT 0
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF2_MASK 0x3
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF2_SHIFT 2
+#define E4_TSTORM_ROCE_CONN_AG_CTX_TIMER_STOP_ALL_CF_MASK 0x3
+#define E4_TSTORM_ROCE_CONN_AG_CTX_TIMER_STOP_ALL_CF_SHIFT 4
+#define E4_TSTORM_ROCE_CONN_AG_CTX_FLUSH_Q0_CF_MASK 0x3
+#define E4_TSTORM_ROCE_CONN_AG_CTX_FLUSH_Q0_CF_SHIFT 6
+ u8 flags2;
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF5_MASK 0x3
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF5_SHIFT 0
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF6_MASK 0x3
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF6_SHIFT 2
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF7_MASK 0x3
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF7_SHIFT 4
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF8_MASK 0x3
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF8_SHIFT 6
+ u8 flags3;
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF9_MASK 0x3
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF9_SHIFT 0
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF10_MASK 0x3
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF10_SHIFT 2
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF0EN_MASK 0x1
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF0EN_SHIFT 4
+#define E4_TSTORM_ROCE_CONN_AG_CTX_MSTORM_FLUSH_CF_EN_MASK 0x1
+#define E4_TSTORM_ROCE_CONN_AG_CTX_MSTORM_FLUSH_CF_EN_SHIFT 5
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF2EN_MASK 0x1
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF2EN_SHIFT 6
+#define E4_TSTORM_ROCE_CONN_AG_CTX_TIMER_STOP_ALL_CF_EN_MASK 0x1
+#define E4_TSTORM_ROCE_CONN_AG_CTX_TIMER_STOP_ALL_CF_EN_SHIFT 7
+ u8 flags4;
+#define E4_TSTORM_ROCE_CONN_AG_CTX_FLUSH_Q0_CF_EN_MASK 0x1
+#define E4_TSTORM_ROCE_CONN_AG_CTX_FLUSH_Q0_CF_EN_SHIFT 0
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF5EN_MASK 0x1
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF5EN_SHIFT 1
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF6EN_MASK 0x1
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF6EN_SHIFT 2
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF7EN_MASK 0x1
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF7EN_SHIFT 3
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF8EN_MASK 0x1
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF8EN_SHIFT 4
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF9EN_MASK 0x1
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF9EN_SHIFT 5
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF10EN_MASK 0x1
+#define E4_TSTORM_ROCE_CONN_AG_CTX_CF10EN_SHIFT 6
+#define E4_TSTORM_ROCE_CONN_AG_CTX_RULE0EN_MASK 0x1
+#define E4_TSTORM_ROCE_CONN_AG_CTX_RULE0EN_SHIFT 7
+ u8 flags5;
+#define E4_TSTORM_ROCE_CONN_AG_CTX_RULE1EN_MASK 0x1
+#define E4_TSTORM_ROCE_CONN_AG_CTX_RULE1EN_SHIFT 0
+#define E4_TSTORM_ROCE_CONN_AG_CTX_RULE2EN_MASK 0x1
+#define E4_TSTORM_ROCE_CONN_AG_CTX_RULE2EN_SHIFT 1
+#define E4_TSTORM_ROCE_CONN_AG_CTX_RULE3EN_MASK 0x1
+#define E4_TSTORM_ROCE_CONN_AG_CTX_RULE3EN_SHIFT 2
+#define E4_TSTORM_ROCE_CONN_AG_CTX_RULE4EN_MASK 0x1
+#define E4_TSTORM_ROCE_CONN_AG_CTX_RULE4EN_SHIFT 3
+#define E4_TSTORM_ROCE_CONN_AG_CTX_RULE5EN_MASK 0x1
+#define E4_TSTORM_ROCE_CONN_AG_CTX_RULE5EN_SHIFT 4
+#define E4_TSTORM_ROCE_CONN_AG_CTX_RULE6EN_MASK 0x1
+#define E4_TSTORM_ROCE_CONN_AG_CTX_RULE6EN_SHIFT 5
+#define E4_TSTORM_ROCE_CONN_AG_CTX_RULE7EN_MASK 0x1
+#define E4_TSTORM_ROCE_CONN_AG_CTX_RULE7EN_SHIFT 6
+#define E4_TSTORM_ROCE_CONN_AG_CTX_RULE8EN_MASK 0x1
+#define E4_TSTORM_ROCE_CONN_AG_CTX_RULE8EN_SHIFT 7
+ __le32 reg0;
+ __le32 reg1;
+ __le32 reg2;
+ __le32 reg3;
+ __le32 reg4;
+ __le32 reg5;
+ __le32 reg6;
+ __le32 reg7;
+ __le32 reg8;
u8 byte2;
u8 byte3;
__le16 word0;
- __le32 reg0;
- __le32 reg1;
+ u8 byte4;
+ u8 byte5;
__le16 word1;
__le16 word2;
__le16 word3;
- __le16 word4;
- __le32 reg2;
- __le32 reg3;
+ __le32 reg9;
+ __le32 reg10;
};
/* The roce storm context of Ystorm */
@@ -7933,15 +7670,15 @@ struct e4_roce_conn_context {
struct regpair ystorm_st_padding[2];
struct pstorm_roce_conn_st_ctx pstorm_st_context;
struct xstorm_roce_conn_st_ctx xstorm_st_context;
- struct regpair xstorm_st_padding[2];
- struct e4_xstorm_rdma_conn_ag_ctx xstorm_ag_context;
- struct e4_tstorm_rdma_conn_ag_ctx tstorm_ag_context;
+ struct e4_xstorm_roce_conn_ag_ctx xstorm_ag_context;
+ struct e4_tstorm_roce_conn_ag_ctx tstorm_ag_context;
struct timers_context timer_context;
struct e4_ustorm_rdma_conn_ag_ctx ustorm_ag_context;
struct tstorm_roce_conn_st_ctx tstorm_st_context;
+ struct regpair tstorm_st_padding[2];
struct mstorm_roce_conn_st_ctx mstorm_st_context;
+ struct regpair mstorm_st_padding[2];
struct ustorm_roce_conn_st_ctx ustorm_st_context;
- struct regpair ustorm_st_padding[2];
};
/* roce create qp requester ramrod data */
@@ -7955,8 +7692,8 @@ struct roce_create_qp_req_ramrod_data {
#define ROCE_CREATE_QP_REQ_RAMROD_DATA_SIGNALED_COMP_SHIFT 3
#define ROCE_CREATE_QP_REQ_RAMROD_DATA_PRI_MASK 0x7
#define ROCE_CREATE_QP_REQ_RAMROD_DATA_PRI_SHIFT 4
-#define ROCE_CREATE_QP_REQ_RAMROD_DATA_RESERVED_MASK 0x1
-#define ROCE_CREATE_QP_REQ_RAMROD_DATA_RESERVED_SHIFT 7
+#define ROCE_CREATE_QP_REQ_RAMROD_DATA_XRC_FLAG_MASK 0x1
+#define ROCE_CREATE_QP_REQ_RAMROD_DATA_XRC_FLAG_SHIFT 7
#define ROCE_CREATE_QP_REQ_RAMROD_DATA_ERR_RETRY_CNT_MASK 0xF
#define ROCE_CREATE_QP_REQ_RAMROD_DATA_ERR_RETRY_CNT_SHIFT 8
#define ROCE_CREATE_QP_REQ_RAMROD_DATA_RNR_NAK_CNT_MASK 0xF
@@ -7982,18 +7719,18 @@ struct roce_create_qp_req_ramrod_data {
__le16 udp_src_port;
__le32 src_gid[4];
__le32 dst_gid[4];
+ __le32 cq_cid;
struct regpair qp_handle_for_cqe;
struct regpair qp_handle_for_async;
u8 stats_counter_id;
u8 reserved3[7];
- __le32 cq_cid;
__le16 regular_latency_phy_queue;
__le16 dpi;
};
/* roce create qp responder ramrod data */
struct roce_create_qp_resp_ramrod_data {
- __le16 flags;
+ __le32 flags;
#define ROCE_CREATE_QP_RESP_RAMROD_DATA_ROCE_FLAVOR_MASK 0x3
#define ROCE_CREATE_QP_RESP_RAMROD_DATA_ROCE_FLAVOR_SHIFT 0
#define ROCE_CREATE_QP_RESP_RAMROD_DATA_RDMA_RD_EN_MASK 0x1
@@ -8012,6 +7749,11 @@ struct roce_create_qp_resp_ramrod_data {
#define ROCE_CREATE_QP_RESP_RAMROD_DATA_PRI_SHIFT 8
#define ROCE_CREATE_QP_RESP_RAMROD_DATA_MIN_RNR_NAK_TIMER_MASK 0x1F
#define ROCE_CREATE_QP_RESP_RAMROD_DATA_MIN_RNR_NAK_TIMER_SHIFT 11
+#define ROCE_CREATE_QP_RESP_RAMROD_DATA_XRC_FLAG_MASK 0x1
+#define ROCE_CREATE_QP_RESP_RAMROD_DATA_XRC_FLAG_SHIFT 16
+#define ROCE_CREATE_QP_RESP_RAMROD_DATA_RESERVED_MASK 0x7FFF
+#define ROCE_CREATE_QP_RESP_RAMROD_DATA_RESERVED_SHIFT 17
+ __le16 xrc_domain;
u8 max_ird;
u8 traffic_class;
u8 hop_limit;
@@ -8037,7 +7779,7 @@ struct roce_create_qp_resp_ramrod_data {
struct regpair qp_handle_for_cqe;
struct regpair qp_handle_for_async;
__le16 low_latency_phy_queue;
- u8 reserved2[6];
+ u8 reserved2[2];
__le32 cq_cid;
__le16 regular_latency_phy_queue;
__le16 dpi;
@@ -8248,6 +7990,270 @@ enum roce_ramrod_cmd_id {
MAX_ROCE_RAMROD_CMD_ID
};
+struct e4_xstorm_roce_conn_ag_ctx_dq_ext_ld_part {
+ u8 reserved0;
+ u8 state;
+ u8 flags0;
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_EXIST_IN_QM0_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_EXIST_IN_QM0_SHIFT 0
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT1_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT1_SHIFT 1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT2_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT2_SHIFT 2
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_EXIST_IN_QM3_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_EXIST_IN_QM3_SHIFT 3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT4_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT4_SHIFT 4
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT5_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT5_SHIFT 5
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT6_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT6_SHIFT 6
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT7_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT7_SHIFT 7
+ u8 flags1;
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT8_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT8_SHIFT 0
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT9_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT9_SHIFT 1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT10_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT10_SHIFT 2
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT11_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT11_SHIFT 3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT12_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT12_SHIFT 4
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_MSEM_FLUSH_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_MSEM_FLUSH_SHIFT 5
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_MSDM_FLUSH_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_MSDM_FLUSH_SHIFT 6
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_YSTORM_FLUSH_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_YSTORM_FLUSH_SHIFT 7
+ u8 flags2;
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF0_MASK 0x3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF0_SHIFT 0
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF1_MASK 0x3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF1_SHIFT 2
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF2_MASK 0x3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF2_SHIFT 4
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF3_MASK 0x3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF3_SHIFT 6
+ u8 flags3;
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF4_MASK 0x3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF4_SHIFT 0
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF5_MASK 0x3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF5_SHIFT 2
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF6_MASK 0x3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF6_SHIFT 4
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_FLUSH_Q0_CF_MASK 0x3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_FLUSH_Q0_CF_SHIFT 6
+ u8 flags4;
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF8_MASK 0x3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF8_SHIFT 0
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF9_MASK 0x3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF9_SHIFT 2
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF10_MASK 0x3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF10_SHIFT 4
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF11_MASK 0x3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF11_SHIFT 6
+ u8 flags5;
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF12_MASK 0x3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF12_SHIFT 0
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF13_MASK 0x3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF13_SHIFT 2
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF14_MASK 0x3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF14_SHIFT 4
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF15_MASK 0x3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF15_SHIFT 6
+ u8 flags6;
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF16_MASK 0x3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF16_SHIFT 0
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF17_MASK 0x3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF17_SHIFT 2
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF18_MASK 0x3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF18_SHIFT 4
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF19_MASK 0x3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF19_SHIFT 6
+ u8 flags7;
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF20_MASK 0x3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF20_SHIFT 0
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF21_MASK 0x3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF21_SHIFT 2
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_SLOW_PATH_MASK 0x3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_SLOW_PATH_SHIFT 4
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF0EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF0EN_SHIFT 6
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF1EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF1EN_SHIFT 7
+ u8 flags8;
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF2EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF2EN_SHIFT 0
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF3EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF3EN_SHIFT 1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF4EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF4EN_SHIFT 2
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF5EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF5EN_SHIFT 3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF6EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF6EN_SHIFT 4
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_FLUSH_Q0_CF_EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_FLUSH_Q0_CF_EN_SHIFT 5
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF8EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF8EN_SHIFT 6
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF9EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF9EN_SHIFT 7
+ u8 flags9;
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF10EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF10EN_SHIFT 0
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF11EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF11EN_SHIFT 1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF12EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF12EN_SHIFT 2
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF13EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF13EN_SHIFT 3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF14EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF14EN_SHIFT 4
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF15EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF15EN_SHIFT 5
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF16EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF16EN_SHIFT 6
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF17EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF17EN_SHIFT 7
+ u8 flags10;
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF18EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF18EN_SHIFT 0
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF19EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF19EN_SHIFT 1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF20EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF20EN_SHIFT 2
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF21EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF21EN_SHIFT 3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_SLOW_PATH_EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_SLOW_PATH_EN_SHIFT 4
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF23EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF23EN_SHIFT 5
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE0EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE0EN_SHIFT 6
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE1EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE1EN_SHIFT 7
+ u8 flags11;
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE2EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE2EN_SHIFT 0
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE3EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE3EN_SHIFT 1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE4EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE4EN_SHIFT 2
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE5EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE5EN_SHIFT 3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE6EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE6EN_SHIFT 4
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE7EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE7EN_SHIFT 5
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED1_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED1_SHIFT 6
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE9EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE9EN_SHIFT 7
+ u8 flags12;
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE10EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE10EN_SHIFT 0
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE11EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE11EN_SHIFT 1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED2_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED2_SHIFT 2
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED3_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED3_SHIFT 3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE14EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE14EN_SHIFT 4
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE15EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE15EN_SHIFT 5
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE16EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE16EN_SHIFT 6
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE17EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE17EN_SHIFT 7
+ u8 flags13;
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE18EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE18EN_SHIFT 0
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE19EN_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RULE19EN_SHIFT 1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED4_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED4_SHIFT 2
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED5_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED5_SHIFT 3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED6_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED6_SHIFT 4
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED7_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED7_SHIFT 5
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED8_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED8_SHIFT 6
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED9_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_A0_RESERVED9_SHIFT 7
+ u8 flags14;
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_MIGRATION_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_MIGRATION_SHIFT 0
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT17_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_BIT17_SHIFT 1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_DPM_PORT_NUM_MASK 0x3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_DPM_PORT_NUM_SHIFT 2
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RESERVED_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_RESERVED_SHIFT 4
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_ROCE_EDPM_ENABLE_MASK 0x1
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_ROCE_EDPM_ENABLE_SHIFT 5
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF23_MASK 0x3
+#define E4XSTORMROCECONNAGCTXDQEXTLDPART_CF23_SHIFT 6
+ u8 byte2;
+ __le16 physical_q0;
+ __le16 word1;
+ __le16 word2;
+ __le16 word3;
+ __le16 word4;
+ __le16 word5;
+ __le16 conn_dpi;
+ u8 byte3;
+ u8 byte4;
+ u8 byte5;
+ u8 byte6;
+ __le32 reg0;
+ __le32 reg1;
+ __le32 reg2;
+ __le32 snd_nxt_psn;
+ __le32 reg4;
+};
+
+struct e4_mstorm_roce_conn_ag_ctx {
+ u8 byte0;
+ u8 byte1;
+ u8 flags0;
+#define E4_MSTORM_ROCE_CONN_AG_CTX_BIT0_MASK 0x1
+#define E4_MSTORM_ROCE_CONN_AG_CTX_BIT0_SHIFT 0
+#define E4_MSTORM_ROCE_CONN_AG_CTX_BIT1_MASK 0x1
+#define E4_MSTORM_ROCE_CONN_AG_CTX_BIT1_SHIFT 1
+#define E4_MSTORM_ROCE_CONN_AG_CTX_CF0_MASK 0x3
+#define E4_MSTORM_ROCE_CONN_AG_CTX_CF0_SHIFT 2
+#define E4_MSTORM_ROCE_CONN_AG_CTX_CF1_MASK 0x3
+#define E4_MSTORM_ROCE_CONN_AG_CTX_CF1_SHIFT 4
+#define E4_MSTORM_ROCE_CONN_AG_CTX_CF2_MASK 0x3
+#define E4_MSTORM_ROCE_CONN_AG_CTX_CF2_SHIFT 6
+ u8 flags1;
+#define E4_MSTORM_ROCE_CONN_AG_CTX_CF0EN_MASK 0x1
+#define E4_MSTORM_ROCE_CONN_AG_CTX_CF0EN_SHIFT 0
+#define E4_MSTORM_ROCE_CONN_AG_CTX_CF1EN_MASK 0x1
+#define E4_MSTORM_ROCE_CONN_AG_CTX_CF1EN_SHIFT 1
+#define E4_MSTORM_ROCE_CONN_AG_CTX_CF2EN_MASK 0x1
+#define E4_MSTORM_ROCE_CONN_AG_CTX_CF2EN_SHIFT 2
+#define E4_MSTORM_ROCE_CONN_AG_CTX_RULE0EN_MASK 0x1
+#define E4_MSTORM_ROCE_CONN_AG_CTX_RULE0EN_SHIFT 3
+#define E4_MSTORM_ROCE_CONN_AG_CTX_RULE1EN_MASK 0x1
+#define E4_MSTORM_ROCE_CONN_AG_CTX_RULE1EN_SHIFT 4
+#define E4_MSTORM_ROCE_CONN_AG_CTX_RULE2EN_MASK 0x1
+#define E4_MSTORM_ROCE_CONN_AG_CTX_RULE2EN_SHIFT 5
+#define E4_MSTORM_ROCE_CONN_AG_CTX_RULE3EN_MASK 0x1
+#define E4_MSTORM_ROCE_CONN_AG_CTX_RULE3EN_SHIFT 6
+#define E4_MSTORM_ROCE_CONN_AG_CTX_RULE4EN_MASK 0x1
+#define E4_MSTORM_ROCE_CONN_AG_CTX_RULE4EN_SHIFT 7
+ __le16 word0;
+ __le16 word1;
+ __le32 reg0;
+ __le32 reg1;
+};
+
struct e4_mstorm_roce_req_conn_ag_ctx {
u8 byte0;
u8 byte1;
@@ -8341,8 +8347,8 @@ struct e4_tstorm_roce_req_conn_ag_ctx {
#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_TIMER_CF_MASK 0x3
#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_TIMER_CF_SHIFT 6
u8 flags1;
-#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_CF1_MASK 0x3
-#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_CF1_SHIFT 0
+#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_MSTORM_FLUSH_CF_MASK 0x3
+#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_MSTORM_FLUSH_CF_SHIFT 0
#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_FLUSH_SQ_CF_MASK 0x3
#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_FLUSH_SQ_CF_SHIFT 2
#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_TIMER_STOP_ALL_CF_MASK 0x3
@@ -8350,8 +8356,8 @@ struct e4_tstorm_roce_req_conn_ag_ctx {
#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_FLUSH_Q0_CF_MASK 0x3
#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_FLUSH_Q0_CF_SHIFT 6
u8 flags2;
-#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_MSTORM_FLUSH_CF_MASK 0x3
-#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_MSTORM_FLUSH_CF_SHIFT 0
+#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_FORCE_COMP_CF_MASK 0x3
+#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_FORCE_COMP_CF_SHIFT 0
#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_SET_TIMER_CF_MASK 0x3
#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_SET_TIMER_CF_SHIFT 2
#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_TX_ASYNC_ERROR_CF_MASK 0x3
@@ -8365,8 +8371,8 @@ struct e4_tstorm_roce_req_conn_ag_ctx {
#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_SQ_DRAIN_COMPLETED_CF_SHIFT 2
#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_TIMER_CF_EN_MASK 0x1
#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_TIMER_CF_EN_SHIFT 4
-#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_CF1EN_MASK 0x1
-#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_CF1EN_SHIFT 5
+#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_MSTORM_FLUSH_CF_EN_MASK 0x1
+#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_MSTORM_FLUSH_CF_EN_SHIFT 5
#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_FLUSH_SQ_CF_EN_MASK 0x1
#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_FLUSH_SQ_CF_EN_SHIFT 6
#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_TIMER_STOP_ALL_CF_EN_MASK 0x1
@@ -8374,8 +8380,8 @@ struct e4_tstorm_roce_req_conn_ag_ctx {
u8 flags4;
#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_FLUSH_Q0_CF_EN_MASK 0x1
#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_FLUSH_Q0_CF_EN_SHIFT 0
-#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_MSTORM_FLUSH_CF_EN_MASK 0x1
-#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_MSTORM_FLUSH_CF_EN_SHIFT 1
+#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_FORCE_COMP_CF_EN_MASK 0x1
+#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_FORCE_COMP_CF_EN_SHIFT 1
#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_SET_TIMER_CF_EN_MASK 0x1
#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_SET_TIMER_CF_EN_SHIFT 2
#define E4_TSTORM_ROCE_REQ_CONN_AG_CTX_TX_ASYNC_ERROR_CF_EN_MASK 0x1
@@ -8421,7 +8427,7 @@ struct e4_tstorm_roce_req_conn_ag_ctx {
u8 byte5;
__le16 snd_sq_cons;
__le16 conn_dpi;
- __le16 word3;
+ __le16 force_comp_cons;
__le32 reg9;
__le32 reg10;
};
@@ -8445,8 +8451,8 @@ struct e4_tstorm_roce_resp_conn_ag_ctx {
#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_CF0_MASK 0x3
#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_CF0_SHIFT 6
u8 flags1;
-#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_RX_ERROR_CF_MASK 0x3
-#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_RX_ERROR_CF_SHIFT 0
+#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_MSTORM_FLUSH_CF_MASK 0x3
+#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_MSTORM_FLUSH_CF_SHIFT 0
#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_TX_ERROR_CF_MASK 0x3
#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_TX_ERROR_CF_SHIFT 2
#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_CF3_MASK 0x3
@@ -8454,8 +8460,8 @@ struct e4_tstorm_roce_resp_conn_ag_ctx {
#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_FLUSH_Q0_CF_MASK 0x3
#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_FLUSH_Q0_CF_SHIFT 6
u8 flags2;
-#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_MSTORM_FLUSH_CF_MASK 0x3
-#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_MSTORM_FLUSH_CF_SHIFT 0
+#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_RX_ERROR_CF_MASK 0x3
+#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_RX_ERROR_CF_SHIFT 0
#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_CF6_MASK 0x3
#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_CF6_SHIFT 2
#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_CF7_MASK 0x3
@@ -8469,8 +8475,8 @@ struct e4_tstorm_roce_resp_conn_ag_ctx {
#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_CF10_SHIFT 2
#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_CF0EN_MASK 0x1
#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_CF0EN_SHIFT 4
-#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_RX_ERROR_CF_EN_MASK 0x1
-#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_RX_ERROR_CF_EN_SHIFT 5
+#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_MSTORM_FLUSH_CF_EN_MASK 0x1
+#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_MSTORM_FLUSH_CF_EN_SHIFT 5
#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_TX_ERROR_CF_EN_MASK 0x1
#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_TX_ERROR_CF_EN_SHIFT 6
#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_CF3EN_MASK 0x1
@@ -8478,8 +8484,8 @@ struct e4_tstorm_roce_resp_conn_ag_ctx {
u8 flags4;
#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_FLUSH_Q0_CF_EN_MASK 0x1
#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_FLUSH_Q0_CF_EN_SHIFT 0
-#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_MSTORM_FLUSH_CF_EN_MASK 0x1
-#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_MSTORM_FLUSH_CF_EN_SHIFT 1
+#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_RX_ERROR_CF_EN_MASK 0x1
+#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_RX_ERROR_CF_EN_SHIFT 1
#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_CF6EN_MASK 0x1
#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_CF6EN_SHIFT 2
#define E4_TSTORM_ROCE_RESP_CONN_AG_CTX_CF7EN_MASK 0x1
@@ -8724,10 +8730,10 @@ struct e4_xstorm_roce_req_conn_ag_ctx {
#define E4_XSTORM_ROCE_REQ_CONN_AG_CTX_FLUSH_Q0_CF_MASK 0x3
#define E4_XSTORM_ROCE_REQ_CONN_AG_CTX_FLUSH_Q0_CF_SHIFT 6
u8 flags4;
-#define E4_XSTORM_ROCE_REQ_CONN_AG_CTX_CF8_MASK 0x3
-#define E4_XSTORM_ROCE_REQ_CONN_AG_CTX_CF8_SHIFT 0
-#define E4_XSTORM_ROCE_REQ_CONN_AG_CTX_CF9_MASK 0x3
-#define E4_XSTORM_ROCE_REQ_CONN_AG_CTX_CF9_SHIFT 2
+#define E4_XSTORM_ROCE_REQ_CONN_AG_CTX_DIF_ERROR_CF_MASK 0x3
+#define E4_XSTORM_ROCE_REQ_CONN_AG_CTX_DIF_ERROR_CF_SHIFT 0
+#define E4_XSTORM_ROCE_REQ_CONN_AG_CTX_SCAN_SQ_FOR_COMP_CF_MASK 0x3
+#define E4_XSTORM_ROCE_REQ_CONN_AG_CTX_SCAN_SQ_FOR_COMP_CF_SHIFT 2
#define E4_XSTORM_ROCE_REQ_CONN_AG_CTX_CF10_MASK 0x3
#define E4_XSTORM_ROCE_REQ_CONN_AG_CTX_CF10_SHIFT 4
#define E4_XSTORM_ROCE_REQ_CONN_AG_CTX_CF11_MASK 0x3
@@ -8774,10 +8780,10 @@ struct e4_xstorm_roce_req_conn_ag_ctx {
#define E4_XSTORM_ROCE_REQ_CONN_AG_CTX_SND_RXMIT_CF_EN_SHIFT 4
#define E4_XSTORM_ROCE_REQ_CONN_AG_CTX_FLUSH_Q0_CF_EN_MASK 0x1
#define E4_XSTORM_ROCE_REQ_CONN_AG_CTX_FLUSH_Q0_CF_EN_SHIFT 5
-#define E4_XSTORM_ROCE_REQ_CONN_AG_CTX_CF8EN_MASK 0x1
-#define E4_XSTORM_ROCE_REQ_CONN_AG_CTX_CF8EN_SHIFT 6
-#define E4_XSTORM_ROCE_REQ_CONN_AG_CTX_CF9EN_MASK 0x1
-#define E4_XSTORM_ROCE_REQ_CONN_AG_CTX_CF9EN_SHIFT 7
+#define E4_XSTORM_ROCE_REQ_CONN_AG_CTX_DIF_ERROR_CF_EN_MASK 0x1
+#define E4_XSTORM_ROCE_REQ_CONN_AG_CTX_DIF_ERROR_CF_EN_SHIFT 6
+#define E4_XSTORM_ROCE_REQ_CONN_AG_CTX_SCAN_SQ_FOR_COMP_CF_EN_MASK 0x1
+#define E4_XSTORM_ROCE_REQ_CONN_AG_CTX_SCAN_SQ_FOR_COMP_CF_EN_SHIFT 7
u8 flags9;
#define E4_XSTORM_ROCE_REQ_CONN_AG_CTX_CF10EN_MASK 0x1
#define E4_XSTORM_ROCE_REQ_CONN_AG_CTX_CF10EN_SHIFT 0
@@ -8882,9 +8888,9 @@ struct e4_xstorm_roce_req_conn_ag_ctx {
__le16 sq_cmp_cons;
__le16 sq_cons;
__le16 sq_prod;
- __le16 word5;
+ __le16 dif_error_first_sq_cons;
__le16 conn_dpi;
- u8 byte3;
+ u8 dif_error_sge_index;
u8 byte4;
u8 byte5;
u8 byte6;
@@ -8892,7 +8898,7 @@ struct e4_xstorm_roce_req_conn_ag_ctx {
__le32 ssn;
__le32 snd_una_psn;
__le32 snd_nxt_psn;
- __le32 reg4;
+ __le32 dif_error_offset;
__le32 orq_cons_th;
__le32 orq_cons;
};
@@ -9128,6 +9134,50 @@ struct e4_xstorm_roce_resp_conn_ag_ctx {
__le32 msn_and_syndrome;
};
+struct e4_ystorm_roce_conn_ag_ctx {
+ u8 byte0;
+ u8 byte1;
+ u8 flags0;
+#define E4_YSTORM_ROCE_CONN_AG_CTX_BIT0_MASK 0x1
+#define E4_YSTORM_ROCE_CONN_AG_CTX_BIT0_SHIFT 0
+#define E4_YSTORM_ROCE_CONN_AG_CTX_BIT1_MASK 0x1
+#define E4_YSTORM_ROCE_CONN_AG_CTX_BIT1_SHIFT 1
+#define E4_YSTORM_ROCE_CONN_AG_CTX_CF0_MASK 0x3
+#define E4_YSTORM_ROCE_CONN_AG_CTX_CF0_SHIFT 2
+#define E4_YSTORM_ROCE_CONN_AG_CTX_CF1_MASK 0x3
+#define E4_YSTORM_ROCE_CONN_AG_CTX_CF1_SHIFT 4
+#define E4_YSTORM_ROCE_CONN_AG_CTX_CF2_MASK 0x3
+#define E4_YSTORM_ROCE_CONN_AG_CTX_CF2_SHIFT 6
+ u8 flags1;
+#define E4_YSTORM_ROCE_CONN_AG_CTX_CF0EN_MASK 0x1
+#define E4_YSTORM_ROCE_CONN_AG_CTX_CF0EN_SHIFT 0
+#define E4_YSTORM_ROCE_CONN_AG_CTX_CF1EN_MASK 0x1
+#define E4_YSTORM_ROCE_CONN_AG_CTX_CF1EN_SHIFT 1
+#define E4_YSTORM_ROCE_CONN_AG_CTX_CF2EN_MASK 0x1
+#define E4_YSTORM_ROCE_CONN_AG_CTX_CF2EN_SHIFT 2
+#define E4_YSTORM_ROCE_CONN_AG_CTX_RULE0EN_MASK 0x1
+#define E4_YSTORM_ROCE_CONN_AG_CTX_RULE0EN_SHIFT 3
+#define E4_YSTORM_ROCE_CONN_AG_CTX_RULE1EN_MASK 0x1
+#define E4_YSTORM_ROCE_CONN_AG_CTX_RULE1EN_SHIFT 4
+#define E4_YSTORM_ROCE_CONN_AG_CTX_RULE2EN_MASK 0x1
+#define E4_YSTORM_ROCE_CONN_AG_CTX_RULE2EN_SHIFT 5
+#define E4_YSTORM_ROCE_CONN_AG_CTX_RULE3EN_MASK 0x1
+#define E4_YSTORM_ROCE_CONN_AG_CTX_RULE3EN_SHIFT 6
+#define E4_YSTORM_ROCE_CONN_AG_CTX_RULE4EN_MASK 0x1
+#define E4_YSTORM_ROCE_CONN_AG_CTX_RULE4EN_SHIFT 7
+ u8 byte2;
+ u8 byte3;
+ __le16 word0;
+ __le32 reg0;
+ __le32 reg1;
+ __le16 word1;
+ __le16 word2;
+ __le16 word3;
+ __le16 word4;
+ __le32 reg2;
+ __le32 reg3;
+};
+
struct e4_ystorm_roce_req_conn_ag_ctx {
u8 byte0;
u8 byte1;
@@ -9236,7 +9286,7 @@ struct pstorm_iwarp_conn_st_ctx {
/* The iwarp storm context of Xstorm */
struct xstorm_iwarp_conn_st_ctx {
- __le32 reserved[44];
+ __le32 reserved[48];
};
struct e4_xstorm_iwarp_conn_ag_ctx {
@@ -9377,8 +9427,8 @@ struct e4_xstorm_iwarp_conn_ag_ctx {
#define E4_XSTORM_IWARP_CONN_AG_CTX_FLUSH_Q1_EN_SHIFT 3
#define E4_XSTORM_IWARP_CONN_AG_CTX_SLOW_PATH_EN_MASK 0x1
#define E4_XSTORM_IWARP_CONN_AG_CTX_SLOW_PATH_EN_SHIFT 4
-#define E4_XSTORM_IWARP_CONN_AG_CTX_CF23EN_MASK 0x1
-#define E4_XSTORM_IWARP_CONN_AG_CTX_CF23EN_SHIFT 5
+#define E4_XSTORM_IWARP_CONN_AG_CTX_SEND_TERMINATE_CF_EN_MASK 0x1
+#define E4_XSTORM_IWARP_CONN_AG_CTX_SEND_TERMINATE_CF_EN_SHIFT 5
#define E4_XSTORM_IWARP_CONN_AG_CTX_RULE0EN_MASK 0x1
#define E4_XSTORM_IWARP_CONN_AG_CTX_RULE0EN_SHIFT 6
#define E4_XSTORM_IWARP_CONN_AG_CTX_MORE_TO_SEND_RULE_EN_MASK 0x1
@@ -9447,8 +9497,8 @@ struct e4_xstorm_iwarp_conn_ag_ctx {
#define E4_XSTORM_IWARP_CONN_AG_CTX_E5_RESERVED2_SHIFT 4
#define E4_XSTORM_IWARP_CONN_AG_CTX_E5_RESERVED3_MASK 0x1
#define E4_XSTORM_IWARP_CONN_AG_CTX_E5_RESERVED3_SHIFT 5
-#define E4_XSTORM_IWARP_CONN_AG_CTX_CF23_MASK 0x3
-#define E4_XSTORM_IWARP_CONN_AG_CTX_CF23_SHIFT 6
+#define E4_XSTORM_IWARP_CONN_AG_CTX_SEND_TERMINATE_CF_MASK 0x3
+#define E4_XSTORM_IWARP_CONN_AG_CTX_SEND_TERMINATE_CF_SHIFT 6
u8 byte2;
__le16 physical_q0;
__le16 physical_q1;
@@ -9466,7 +9516,7 @@ struct e4_xstorm_iwarp_conn_ag_ctx {
__le32 reg2;
__le32 more_to_send_seq;
__le32 reg4;
- __le32 rewinded_snd_max;
+ __le32 rewinded_snd_max_or_term_opcode;
__le32 rd_msn;
__le16 irq_prod_via_msdm;
__le16 irq_cons;
@@ -9476,8 +9526,8 @@ struct e4_xstorm_iwarp_conn_ag_ctx {
__le32 orq_cons;
__le32 orq_cons_th;
u8 byte7;
- u8 max_ord;
u8 wqe_data_pad_bytes;
+ u8 max_ord;
u8 former_hq_prod;
u8 irq_prod_via_msem;
u8 byte12;
@@ -9506,8 +9556,8 @@ struct e4_tstorm_iwarp_conn_ag_ctx {
#define E4_TSTORM_IWARP_CONN_AG_CTX_BIT1_SHIFT 1
#define E4_TSTORM_IWARP_CONN_AG_CTX_BIT2_MASK 0x1
#define E4_TSTORM_IWARP_CONN_AG_CTX_BIT2_SHIFT 2
-#define E4_TSTORM_IWARP_CONN_AG_CTX_MSTORM_FLUSH_MASK 0x1
-#define E4_TSTORM_IWARP_CONN_AG_CTX_MSTORM_FLUSH_SHIFT 3
+#define E4_TSTORM_IWARP_CONN_AG_CTX_MSTORM_FLUSH_OR_TERMINATE_SENT_MASK 0x1
+#define E4_TSTORM_IWARP_CONN_AG_CTX_MSTORM_FLUSH_OR_TERMINATE_SENT_SHIFT 3
#define E4_TSTORM_IWARP_CONN_AG_CTX_BIT4_MASK 0x1
#define E4_TSTORM_IWARP_CONN_AG_CTX_BIT4_SHIFT 4
#define E4_TSTORM_IWARP_CONN_AG_CTX_CACHED_ORQ_MASK 0x1
@@ -9622,7 +9672,6 @@ struct e4_iwarp_conn_context {
struct pstorm_iwarp_conn_st_ctx pstorm_st_context;
struct regpair pstorm_st_padding[2];
struct xstorm_iwarp_conn_st_ctx xstorm_st_context;
- struct regpair xstorm_st_padding[2];
struct e4_xstorm_iwarp_conn_ag_ctx xstorm_ag_context;
struct e4_tstorm_iwarp_conn_ag_ctx tstorm_ag_context;
struct timers_context timer_context;
@@ -9648,8 +9697,10 @@ struct iwarp_create_qp_ramrod_data {
#define IWARP_CREATE_QP_RAMROD_DATA_ATOMIC_EN_SHIFT 4
#define IWARP_CREATE_QP_RAMROD_DATA_SRQ_FLG_MASK 0x1
#define IWARP_CREATE_QP_RAMROD_DATA_SRQ_FLG_SHIFT 5
-#define IWARP_CREATE_QP_RAMROD_DATA_RESERVED0_MASK 0x3
-#define IWARP_CREATE_QP_RAMROD_DATA_RESERVED0_SHIFT 6
+#define IWARP_CREATE_QP_RAMROD_DATA_LOW_LATENCY_QUEUE_EN_MASK 0x1
+#define IWARP_CREATE_QP_RAMROD_DATA_LOW_LATENCY_QUEUE_EN_SHIFT 6
+#define IWARP_CREATE_QP_RAMROD_DATA_RESERVED0_MASK 0x1
+#define IWARP_CREATE_QP_RAMROD_DATA_RESERVED0_SHIFT 7
u8 reserved1;
__le16 pd;
__le16 sq_num_pages;
@@ -9698,6 +9749,7 @@ enum iwarp_eqe_sync_opcode {
IWARP_EVENT_TYPE_QUERY_QP,
IWARP_EVENT_TYPE_MODIFY_QP,
IWARP_EVENT_TYPE_DESTROY_QP,
+ IWARP_EVENT_TYPE_ABORT_TCP_OFFLOAD,
MAX_IWARP_EQE_SYNC_OPCODE
};
@@ -9722,6 +9774,8 @@ enum iwarp_fw_return_code {
IWARP_EXCEPTION_DETECTED_LLP_RESET,
IWARP_EXCEPTION_DETECTED_IRQ_FULL,
IWARP_EXCEPTION_DETECTED_RQ_EMPTY,
+ IWARP_EXCEPTION_DETECTED_SRQ_EMPTY,
+ IWARP_EXCEPTION_DETECTED_SRQ_LIMIT,
IWARP_EXCEPTION_DETECTED_LLP_TIMEOUT,
IWARP_EXCEPTION_DETECTED_REMOTE_PROTECTION_ERROR,
IWARP_EXCEPTION_DETECTED_CQ_OVERFLOW,
@@ -9766,10 +9820,13 @@ struct iwarp_modify_qp_ramrod_data {
#define IWARP_MODIFY_QP_RAMROD_DATA_STATE_TRANS_EN_SHIFT 3
#define IWARP_MODIFY_QP_RAMROD_DATA_RDMA_OPS_EN_FLG_MASK 0x1
#define IWARP_MODIFY_QP_RAMROD_DATA_RDMA_OPS_EN_FLG_SHIFT 4
-#define IWARP_MODIFY_QP_RAMROD_DATA_RESERVED_MASK 0x7FF
-#define IWARP_MODIFY_QP_RAMROD_DATA_RESERVED_SHIFT 5
- __le32 reserved3[3];
- __le32 reserved4[8];
+#define IWARP_MODIFY_QP_RAMROD_DATA_PHYSICAL_QUEUE_FLG_MASK 0x1
+#define IWARP_MODIFY_QP_RAMROD_DATA_PHYSICAL_QUEUE_FLG_SHIFT 5
+#define IWARP_MODIFY_QP_RAMROD_DATA_RESERVED_MASK 0x3FF
+#define IWARP_MODIFY_QP_RAMROD_DATA_RESERVED_SHIFT 6
+ __le16 physical_q0;
+ __le16 physical_q1;
+ __le32 reserved1[10];
};
/* MPA params for Enhanced mode */
@@ -9853,6 +9910,7 @@ enum iwarp_ramrod_cmd_id {
IWARP_RAMROD_CMD_ID_QUERY_QP,
IWARP_RAMROD_CMD_ID_MODIFY_QP,
IWARP_RAMROD_CMD_ID_DESTROY_QP,
+ IWARP_RAMROD_CMD_ID_ABORT_TCP_OFFLOAD,
MAX_IWARP_RAMROD_CMD_ID
};
@@ -11205,7 +11263,7 @@ struct e4_tstorm_iscsi_conn_ag_ctx {
#define E4_TSTORM_ISCSI_CONN_AG_CTX_RULE8EN_SHIFT 7
__le32 reg0;
__le32 reg1;
- __le32 reg2;
+ __le32 rx_tcp_checksum_err_cnt;
__le32 reg3;
__le32 reg4;
__le32 reg5;
@@ -12210,8 +12268,11 @@ struct public_drv_mb {
#define DRV_MSG_CODE_VF_DISABLED_DONE 0xc0000000
#define DRV_MSG_CODE_CFG_VF_MSIX 0xc0010000
#define DRV_MSG_CODE_CFG_PF_VFS_MSIX 0xc0020000
+#define DRV_MSG_CODE_NVM_PUT_FILE_BEGIN 0x00010000
+#define DRV_MSG_CODE_NVM_PUT_FILE_DATA 0x00020000
#define DRV_MSG_CODE_NVM_GET_FILE_ATT 0x00030000
#define DRV_MSG_CODE_NVM_READ_NVRAM 0x00050000
+#define DRV_MSG_CODE_NVM_WRITE_NVRAM 0x00060000
#define DRV_MSG_CODE_MCP_RESET 0x00090000
#define DRV_MSG_CODE_SET_VERSION 0x000f0000
#define DRV_MSG_CODE_MCP_HALT 0x00100000
@@ -12265,7 +12326,6 @@ struct public_drv_mb {
#define DRV_MSG_CODE_FEATURE_SUPPORT 0x00300000
#define DRV_MSG_CODE_GET_MFW_FEATURE_SUPPORT 0x00310000
-
#define DRV_MSG_SEQ_NUMBER_MASK 0x0000ffff
u32 drv_mb_param;
@@ -12377,7 +12437,10 @@ struct public_drv_mb {
#define FW_MSG_CODE_DRV_CFG_VF_MSIX_DONE 0xb0010000
#define FW_MSG_CODE_NVM_OK 0x00010000
+#define FW_MSG_CODE_NVM_PUT_FILE_FINISH_OK 0x00400000
+#define FW_MSG_CODE_PHY_OK 0x00110000
#define FW_MSG_CODE_OK 0x00160000
+#define FW_MSG_CODE_ERROR 0x00170000
#define FW_MSG_CODE_OS_WOL_SUPPORTED 0x00800000
#define FW_MSG_CODE_OS_WOL_NOT_SUPPORTED 0x00810000
diff --git a/drivers/net/ethernet/qlogic/qed/qed_init_fw_funcs.c b/drivers/net/ethernet/qlogic/qed/qed_init_fw_funcs.c
index 18fb5062a83d..1365da7c8900 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_init_fw_funcs.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_init_fw_funcs.c
@@ -467,12 +467,11 @@ static void qed_tx_pq_map_rt_init(struct qed_hwfn *p_hwfn,
u16 *p_first_tx_pq_id;
ext_voq = qed_get_ext_voq(p_hwfn,
- p_params->port_id,
+ pq_params[i].port_id,
tc_id,
p_params->max_phys_tcs_per_port);
is_vf_pq = (i >= p_params->num_pf_pqs);
- rl_valid = pq_params[i].rl_valid &&
- pq_params[i].vport_id < max_qm_global_rls;
+ rl_valid = pq_params[i].rl_valid > 0;
/* Update first Tx PQ of VPORT/TC */
vport_id_in_pf = pq_params[i].vport_id - p_params->start_vport;
@@ -494,10 +493,11 @@ static void qed_tx_pq_map_rt_init(struct qed_hwfn *p_hwfn,
}
/* Check RL ID */
- if (pq_params[i].rl_valid && pq_params[i].vport_id >=
- max_qm_global_rls)
+ if (rl_valid && pq_params[i].vport_id >= max_qm_global_rls) {
DP_NOTICE(p_hwfn,
"Invalid VPORT ID for rate limiter configuration\n");
+ rl_valid = false;
+ }
/* Prepare PQ map entry */
QM_INIT_TX_PQ_MAP(p_hwfn,
@@ -528,7 +528,7 @@ static void qed_tx_pq_map_rt_init(struct qed_hwfn *p_hwfn,
pq_info = PQ_INFO_ELEMENT(*p_first_tx_pq_id,
p_params->pf_id,
tc_id,
- p_params->port_id,
+ pq_params[i].port_id,
rl_valid ? 1 : 0,
rl_valid ?
pq_params[i].vport_id : 0);
@@ -603,6 +603,7 @@ static void qed_other_pq_map_rt_init(struct qed_hwfn *p_hwfn,
* Return -1 on error.
*/
static int qed_pf_wfq_rt_init(struct qed_hwfn *p_hwfn,
+
struct qed_qm_pf_rt_init_params *p_params)
{
u16 num_tx_pqs = p_params->num_pf_pqs + p_params->num_vf_pqs;
@@ -619,7 +620,7 @@ static int qed_pf_wfq_rt_init(struct qed_hwfn *p_hwfn,
for (i = 0; i < num_tx_pqs; i++) {
ext_voq = qed_get_ext_voq(p_hwfn,
- p_params->port_id,
+ pq_params[i].port_id,
pq_params[i].tc_id,
p_params->max_phys_tcs_per_port);
crd_reg_offset =
@@ -1020,7 +1021,8 @@ bool qed_send_qm_stop_cmd(struct qed_hwfn *p_hwfn,
*__p_var = (*__p_var & ~BIT(__offset)) | \
((enable) ? BIT(__offset) : 0); \
} while (0)
-#define PRS_ETH_TUNN_FIC_FORMAT -188897008
+#define PRS_ETH_TUNN_OUTPUT_FORMAT -188897008
+#define PRS_ETH_OUTPUT_FORMAT -46832
void qed_set_vxlan_dest_port(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt, u16 dest_port)
@@ -1046,11 +1048,15 @@ void qed_set_vxlan_enable(struct qed_hwfn *p_hwfn,
shift = PRS_REG_ENCAPSULATION_TYPE_EN_VXLAN_ENABLE_SHIFT;
SET_TUNNEL_TYPE_ENABLE_BIT(reg_val, shift, vxlan_enable);
qed_wr(p_hwfn, p_ptt, PRS_REG_ENCAPSULATION_TYPE_EN, reg_val);
- if (reg_val)
- qed_wr(p_hwfn,
- p_ptt,
- PRS_REG_OUTPUT_FORMAT_4_0_BB_K2,
- (u32)PRS_ETH_TUNN_FIC_FORMAT);
+ if (reg_val) {
+ reg_val =
+ qed_rd(p_hwfn, p_ptt, PRS_REG_OUTPUT_FORMAT_4_0_BB_K2);
+
+ /* Update output only if tunnel blocks not included. */
+ if (reg_val == (u32)PRS_ETH_OUTPUT_FORMAT)
+ qed_wr(p_hwfn, p_ptt, PRS_REG_OUTPUT_FORMAT_4_0_BB_K2,
+ (u32)PRS_ETH_TUNN_OUTPUT_FORMAT);
+ }
/* Update NIG register */
reg_val = qed_rd(p_hwfn, p_ptt, NIG_REG_ENC_TYPE_ENABLE);
@@ -1077,11 +1083,15 @@ void qed_set_gre_enable(struct qed_hwfn *p_hwfn,
shift = PRS_REG_ENCAPSULATION_TYPE_EN_IP_OVER_GRE_ENABLE_SHIFT;
SET_TUNNEL_TYPE_ENABLE_BIT(reg_val, shift, ip_gre_enable);
qed_wr(p_hwfn, p_ptt, PRS_REG_ENCAPSULATION_TYPE_EN, reg_val);
- if (reg_val)
- qed_wr(p_hwfn,
- p_ptt,
- PRS_REG_OUTPUT_FORMAT_4_0_BB_K2,
- (u32)PRS_ETH_TUNN_FIC_FORMAT);
+ if (reg_val) {
+ reg_val =
+ qed_rd(p_hwfn, p_ptt, PRS_REG_OUTPUT_FORMAT_4_0_BB_K2);
+
+ /* Update output only if tunnel blocks not included. */
+ if (reg_val == (u32)PRS_ETH_OUTPUT_FORMAT)
+ qed_wr(p_hwfn, p_ptt, PRS_REG_OUTPUT_FORMAT_4_0_BB_K2,
+ (u32)PRS_ETH_TUNN_OUTPUT_FORMAT);
+ }
/* Update NIG register */
reg_val = qed_rd(p_hwfn, p_ptt, NIG_REG_ENC_TYPE_ENABLE);
@@ -1126,11 +1136,15 @@ void qed_set_geneve_enable(struct qed_hwfn *p_hwfn,
shift = PRS_REG_ENCAPSULATION_TYPE_EN_IP_OVER_GENEVE_ENABLE_SHIFT;
SET_TUNNEL_TYPE_ENABLE_BIT(reg_val, shift, ip_geneve_enable);
qed_wr(p_hwfn, p_ptt, PRS_REG_ENCAPSULATION_TYPE_EN, reg_val);
- if (reg_val)
- qed_wr(p_hwfn,
- p_ptt,
- PRS_REG_OUTPUT_FORMAT_4_0_BB_K2,
- (u32)PRS_ETH_TUNN_FIC_FORMAT);
+ if (reg_val) {
+ reg_val =
+ qed_rd(p_hwfn, p_ptt, PRS_REG_OUTPUT_FORMAT_4_0_BB_K2);
+
+ /* Update output only if tunnel blocks not included. */
+ if (reg_val == (u32)PRS_ETH_OUTPUT_FORMAT)
+ qed_wr(p_hwfn, p_ptt, PRS_REG_OUTPUT_FORMAT_4_0_BB_K2,
+ (u32)PRS_ETH_TUNN_OUTPUT_FORMAT);
+ }
/* Update NIG register */
qed_wr(p_hwfn, p_ptt, NIG_REG_NGE_ETH_ENABLE,
@@ -1152,6 +1166,38 @@ void qed_set_geneve_enable(struct qed_hwfn *p_hwfn,
ip_geneve_enable ? 1 : 0);
}
+#define PRS_ETH_VXLAN_NO_L2_ENABLE_OFFSET 4
+#define PRS_ETH_VXLAN_NO_L2_OUTPUT_FORMAT -927094512
+
+void qed_set_vxlan_no_l2_enable(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt, bool enable)
+{
+ u32 reg_val, cfg_mask;
+
+ /* read PRS config register */
+ reg_val = qed_rd(p_hwfn, p_ptt, PRS_REG_MSG_INFO);
+
+ /* set VXLAN_NO_L2_ENABLE mask */
+ cfg_mask = BIT(PRS_ETH_VXLAN_NO_L2_ENABLE_OFFSET);
+
+ if (enable) {
+ /* set VXLAN_NO_L2_ENABLE flag */
+ reg_val |= cfg_mask;
+
+ /* update PRS FIC register */
+ qed_wr(p_hwfn,
+ p_ptt,
+ PRS_REG_OUTPUT_FORMAT_4_0_BB_K2,
+ (u32)PRS_ETH_VXLAN_NO_L2_OUTPUT_FORMAT);
+ } else {
+ /* clear VXLAN_NO_L2_ENABLE flag */
+ reg_val &= ~cfg_mask;
+ }
+
+ /* write PRS config register */
+ qed_wr(p_hwfn, p_ptt, PRS_REG_MSG_INFO, reg_val);
+}
+
#define T_ETH_PACKET_ACTION_GFT_EVENTID 23
#define PARSER_ETH_CONN_GFT_ACTION_CM_HDR 272
#define T_ETH_PACKET_MATCH_RFS_EVENTID 25
@@ -1268,6 +1314,10 @@ void qed_gft_config(struct qed_hwfn *p_hwfn,
ram_line_lo = 0;
ram_line_hi = 0;
+ /* Tunnel type */
+ SET_FIELD(ram_line_lo, GFT_RAM_LINE_TUNNEL_DST_PORT, 1);
+ SET_FIELD(ram_line_lo, GFT_RAM_LINE_TUNNEL_OVER_IP_PROTOCOL, 1);
+
if (profile_type == GFT_PROFILE_TYPE_4_TUPLE) {
SET_FIELD(ram_line_hi, GFT_RAM_LINE_DST_IP, 1);
SET_FIELD(ram_line_hi, GFT_RAM_LINE_SRC_IP, 1);
@@ -1279,9 +1329,14 @@ void qed_gft_config(struct qed_hwfn *p_hwfn,
SET_FIELD(ram_line_hi, GFT_RAM_LINE_OVER_IP_PROTOCOL, 1);
SET_FIELD(ram_line_lo, GFT_RAM_LINE_ETHERTYPE, 1);
SET_FIELD(ram_line_lo, GFT_RAM_LINE_DST_PORT, 1);
- } else if (profile_type == GFT_PROFILE_TYPE_IP_DST_PORT) {
+ } else if (profile_type == GFT_PROFILE_TYPE_IP_DST_ADDR) {
SET_FIELD(ram_line_hi, GFT_RAM_LINE_DST_IP, 1);
SET_FIELD(ram_line_lo, GFT_RAM_LINE_ETHERTYPE, 1);
+ } else if (profile_type == GFT_PROFILE_TYPE_IP_SRC_ADDR) {
+ SET_FIELD(ram_line_hi, GFT_RAM_LINE_SRC_IP, 1);
+ SET_FIELD(ram_line_lo, GFT_RAM_LINE_ETHERTYPE, 1);
+ } else if (profile_type == GFT_PROFILE_TYPE_TUNNEL_TYPE) {
+ SET_FIELD(ram_line_lo, GFT_RAM_LINE_TUNNEL_ETHERTYPE, 1);
}
qed_wr(p_hwfn,
diff --git a/drivers/net/ethernet/qlogic/qed/qed_int.c b/drivers/net/ethernet/qlogic/qed/qed_int.c
index d3eabcf9c86c..af3a28ec04eb 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_int.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_int.c
@@ -321,7 +321,7 @@ static int qed_pglub_rbc_attn_cb(struct qed_hwfn *p_hwfn)
tmp = qed_rd(p_hwfn, p_hwfn->p_dpc_ptt,
PGLUE_B_REG_TX_ERR_WR_DETAILS_ICPL);
if (tmp & PGLUE_ATTENTION_ICPL_VALID)
- DP_INFO(p_hwfn, "ICPL eror - %08x\n", tmp);
+ DP_INFO(p_hwfn, "ICPL error - %08x\n", tmp);
tmp = qed_rd(p_hwfn, p_hwfn->p_dpc_ptt,
PGLUE_B_REG_MASTER_ZLR_ERR_DETAILS);
diff --git a/drivers/net/ethernet/qlogic/qed/qed_iwarp.c b/drivers/net/ethernet/qlogic/qed/qed_iwarp.c
index d5d02be72947..2a2b1018ed1d 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_iwarp.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_iwarp.c
@@ -1799,7 +1799,7 @@ enum qed_iwarp_mpa_pkt_type {
/* fpdu can be fragmented over maximum 3 bds: header, partial mpa, unaligned */
#define QED_IWARP_MAX_BDS_PER_FPDU 3
-char *pkt_type_str[] = {
+static const char * const pkt_type_str[] = {
"QED_IWARP_MPA_PKT_PACKED",
"QED_IWARP_MPA_PKT_PARTIAL",
"QED_IWARP_MPA_PKT_UNALIGNED"
@@ -2375,13 +2375,6 @@ qed_iwarp_ll2_comp_syn_pkt(void *cxt, struct qed_ll2_comp_rx_data *data)
memset(&tx_pkt, 0, sizeof(tx_pkt));
tx_pkt.num_of_bds = 1;
- tx_pkt.vlan = data->vlan;
-
- if (GET_FIELD(data->parse_flags,
- PARSING_AND_ERR_FLAGS_TAG8021QEXIST))
- SET_FIELD(tx_pkt.bd_flags,
- CORE_TX_BD_DATA_VLAN_INSERTION, 1);
-
tx_pkt.l4_hdr_offset_w = (data->length.packet_length) >> 2;
tx_pkt.tx_dest = QED_LL2_TX_DEST_LB;
tx_pkt.first_frag = buf->data_phys_addr +
diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.c b/drivers/net/ethernet/qlogic/qed/qed_l2.c
index 893ef08a4b39..e874504e8b28 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_l2.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_l2.c
@@ -1974,7 +1974,7 @@ qed_arfs_mode_to_hsi(enum qed_filter_config_mode mode)
if (mode == QED_FILTER_CONFIG_MODE_5_TUPLE)
return GFT_PROFILE_TYPE_4_TUPLE;
if (mode == QED_FILTER_CONFIG_MODE_IP_DEST)
- return GFT_PROFILE_TYPE_IP_DST_PORT;
+ return GFT_PROFILE_TYPE_IP_DST_ADDR;
return GFT_PROFILE_TYPE_L4_DST_PORT;
}
diff --git a/drivers/net/ethernet/qlogic/qed/qed_ll2.c b/drivers/net/ethernet/qlogic/qed/qed_ll2.c
index c4f14fdc4e77..74fc626b1ec1 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_ll2.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_ll2.c
@@ -591,16 +591,6 @@ static void qed_ll2_rxq_flush(struct qed_hwfn *p_hwfn, u8 connection_handle)
}
}
-static u8 qed_ll2_convert_rx_parse_to_tx_flags(u16 parse_flags)
-{
- u8 bd_flags = 0;
-
- if (GET_FIELD(parse_flags, PARSING_AND_ERR_FLAGS_TAG8021QEXIST))
- SET_FIELD(bd_flags, CORE_TX_BD_DATA_VLAN_INSERTION, 1);
-
- return bd_flags;
-}
-
static int qed_ll2_lb_rxq_handler(struct qed_hwfn *p_hwfn,
struct qed_ll2_info *p_ll2_conn)
{
@@ -744,7 +734,6 @@ qed_ooo_submit_tx_buffers(struct qed_hwfn *p_hwfn,
struct qed_ooo_buffer *p_buffer;
u16 l4_hdr_offset_w;
dma_addr_t first_frag;
- u16 parse_flags;
u8 bd_flags;
int rc;
@@ -756,8 +745,6 @@ qed_ooo_submit_tx_buffers(struct qed_hwfn *p_hwfn,
first_frag = p_buffer->rx_buffer_phys_addr +
p_buffer->placement_offset;
- parse_flags = p_buffer->parse_flags;
- bd_flags = qed_ll2_convert_rx_parse_to_tx_flags(parse_flags);
SET_FIELD(bd_flags, CORE_TX_BD_DATA_FORCE_VLAN_MODE, 1);
SET_FIELD(bd_flags, CORE_TX_BD_DATA_L4_PROTOCOL, 1);
diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c
index 27832885a87f..9854aa9139af 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_main.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_main.c
@@ -45,6 +45,7 @@
#include <linux/etherdevice.h>
#include <linux/vmalloc.h>
#include <linux/crash_dump.h>
+#include <linux/crc32.h>
#include <linux/qed/qed_if.h>
#include <linux/qed/qed_ll2_if.h>
@@ -1553,6 +1554,342 @@ static int qed_drain(struct qed_dev *cdev)
return 0;
}
+static u32 qed_nvm_flash_image_access_crc(struct qed_dev *cdev,
+ struct qed_nvm_image_att *nvm_image,
+ u32 *crc)
+{
+ u8 *buf = NULL;
+ int rc, j;
+ u32 val;
+
+ /* Allocate a buffer for holding the nvram image */
+ buf = kzalloc(nvm_image->length, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ /* Read image into buffer */
+ rc = qed_mcp_nvm_read(cdev, nvm_image->start_addr,
+ buf, nvm_image->length);
+ if (rc) {
+ DP_ERR(cdev, "Failed reading image from nvm\n");
+ goto out;
+ }
+
+ /* Convert the buffer into big-endian format (excluding the
+ * closing 4 bytes of CRC).
+ */
+ for (j = 0; j < nvm_image->length - 4; j += 4) {
+ val = cpu_to_be32(*(u32 *)&buf[j]);
+ *(u32 *)&buf[j] = val;
+ }
+
+ /* Calc CRC for the "actual" image buffer, i.e. not including
+ * the last 4 CRC bytes.
+ */
+ *crc = (~cpu_to_be32(crc32(0xffffffff, buf, nvm_image->length - 4)));
+
+out:
+ kfree(buf);
+
+ return rc;
+}
+
+/* Binary file format -
+ * /----------------------------------------------------------------------\
+ * 0B | 0x4 [command index] |
+ * 4B | image_type | Options | Number of register settings |
+ * 8B | Value |
+ * 12B | Mask |
+ * 16B | Offset |
+ * \----------------------------------------------------------------------/
+ * There can be several Value-Mask-Offset sets as specified by 'Number of...'.
+ * Options - 0'b - Calculate & Update CRC for image
+ */
+static int qed_nvm_flash_image_access(struct qed_dev *cdev, const u8 **data,
+ bool *check_resp)
+{
+ struct qed_nvm_image_att nvm_image;
+ struct qed_hwfn *p_hwfn;
+ bool is_crc = false;
+ u32 image_type;
+ int rc = 0, i;
+ u16 len;
+
+ *data += 4;
+ image_type = **data;
+ p_hwfn = QED_LEADING_HWFN(cdev);
+ for (i = 0; i < p_hwfn->nvm_info.num_images; i++)
+ if (image_type == p_hwfn->nvm_info.image_att[i].image_type)
+ break;
+ if (i == p_hwfn->nvm_info.num_images) {
+ DP_ERR(cdev, "Failed to find nvram image of type %08x\n",
+ image_type);
+ return -ENOENT;
+ }
+
+ nvm_image.start_addr = p_hwfn->nvm_info.image_att[i].nvm_start_addr;
+ nvm_image.length = p_hwfn->nvm_info.image_att[i].len;
+
+ DP_VERBOSE(cdev, NETIF_MSG_DRV,
+ "Read image %02x; type = %08x; NVM [%08x,...,%08x]\n",
+ **data, image_type, nvm_image.start_addr,
+ nvm_image.start_addr + nvm_image.length - 1);
+ (*data)++;
+ is_crc = !!(**data & BIT(0));
+ (*data)++;
+ len = *((u16 *)*data);
+ *data += 2;
+ if (is_crc) {
+ u32 crc = 0;
+
+ rc = qed_nvm_flash_image_access_crc(cdev, &nvm_image, &crc);
+ if (rc) {
+ DP_ERR(cdev, "Failed calculating CRC, rc = %d\n", rc);
+ goto exit;
+ }
+
+ rc = qed_mcp_nvm_write(cdev, QED_NVM_WRITE_NVRAM,
+ (nvm_image.start_addr +
+ nvm_image.length - 4), (u8 *)&crc, 4);
+ if (rc)
+ DP_ERR(cdev, "Failed writing to %08x, rc = %d\n",
+ nvm_image.start_addr + nvm_image.length - 4, rc);
+ goto exit;
+ }
+
+ /* Iterate over the values for setting */
+ while (len) {
+ u32 offset, mask, value, cur_value;
+ u8 buf[4];
+
+ value = *((u32 *)*data);
+ *data += 4;
+ mask = *((u32 *)*data);
+ *data += 4;
+ offset = *((u32 *)*data);
+ *data += 4;
+
+ rc = qed_mcp_nvm_read(cdev, nvm_image.start_addr + offset, buf,
+ 4);
+ if (rc) {
+ DP_ERR(cdev, "Failed reading from %08x\n",
+ nvm_image.start_addr + offset);
+ goto exit;
+ }
+
+ cur_value = le32_to_cpu(*((__le32 *)buf));
+ DP_VERBOSE(cdev, NETIF_MSG_DRV,
+ "NVM %08x: %08x -> %08x [Value %08x Mask %08x]\n",
+ nvm_image.start_addr + offset, cur_value,
+ (cur_value & ~mask) | (value & mask), value, mask);
+ value = (value & mask) | (cur_value & ~mask);
+ rc = qed_mcp_nvm_write(cdev, QED_NVM_WRITE_NVRAM,
+ nvm_image.start_addr + offset,
+ (u8 *)&value, 4);
+ if (rc) {
+ DP_ERR(cdev, "Failed writing to %08x\n",
+ nvm_image.start_addr + offset);
+ goto exit;
+ }
+
+ len--;
+ }
+exit:
+ return rc;
+}
+
+/* Binary file format -
+ * /----------------------------------------------------------------------\
+ * 0B | 0x3 [command index] |
+ * 4B | b'0: check_response? | b'1-31 reserved |
+ * 8B | File-type | reserved |
+ * \----------------------------------------------------------------------/
+ * Start a new file of the provided type
+ */
+static int qed_nvm_flash_image_file_start(struct qed_dev *cdev,
+ const u8 **data, bool *check_resp)
+{
+ int rc;
+
+ *data += 4;
+ *check_resp = !!(**data & BIT(0));
+ *data += 4;
+
+ DP_VERBOSE(cdev, NETIF_MSG_DRV,
+ "About to start a new file of type %02x\n", **data);
+ rc = qed_mcp_nvm_put_file_begin(cdev, **data);
+ *data += 4;
+
+ return rc;
+}
+
+/* Binary file format -
+ * /----------------------------------------------------------------------\
+ * 0B | 0x2 [command index] |
+ * 4B | Length in bytes |
+ * 8B | b'0: check_response? | b'1-31 reserved |
+ * 12B | Offset in bytes |
+ * 16B | Data ... |
+ * \----------------------------------------------------------------------/
+ * Write data as part of a file that was previously started. Data should be
+ * of length equal to that provided in the message
+ */
+static int qed_nvm_flash_image_file_data(struct qed_dev *cdev,
+ const u8 **data, bool *check_resp)
+{
+ u32 offset, len;
+ int rc;
+
+ *data += 4;
+ len = *((u32 *)(*data));
+ *data += 4;
+ *check_resp = !!(**data & BIT(0));
+ *data += 4;
+ offset = *((u32 *)(*data));
+ *data += 4;
+
+ DP_VERBOSE(cdev, NETIF_MSG_DRV,
+ "About to write File-data: %08x bytes to offset %08x\n",
+ len, offset);
+
+ rc = qed_mcp_nvm_write(cdev, QED_PUT_FILE_DATA, offset,
+ (char *)(*data), len);
+ *data += len;
+
+ return rc;
+}
+
+/* Binary file format [General header] -
+ * /----------------------------------------------------------------------\
+ * 0B | QED_NVM_SIGNATURE |
+ * 4B | Length in bytes |
+ * 8B | Highest command in this batchfile | Reserved |
+ * \----------------------------------------------------------------------/
+ */
+static int qed_nvm_flash_image_validate(struct qed_dev *cdev,
+ const struct firmware *image,
+ const u8 **data)
+{
+ u32 signature, len;
+
+ /* Check minimum size */
+ if (image->size < 12) {
+ DP_ERR(cdev, "Image is too short [%08x]\n", (u32)image->size);
+ return -EINVAL;
+ }
+
+ /* Check signature */
+ signature = *((u32 *)(*data));
+ if (signature != QED_NVM_SIGNATURE) {
+ DP_ERR(cdev, "Wrong signature '%08x'\n", signature);
+ return -EINVAL;
+ }
+
+ *data += 4;
+ /* Validate internal size equals the image-size */
+ len = *((u32 *)(*data));
+ if (len != image->size) {
+ DP_ERR(cdev, "Size mismatch: internal = %08x image = %08x\n",
+ len, (u32)image->size);
+ return -EINVAL;
+ }
+
+ *data += 4;
+ /* Make sure driver familiar with all commands necessary for this */
+ if (*((u16 *)(*data)) >= QED_NVM_FLASH_CMD_NVM_MAX) {
+ DP_ERR(cdev, "File contains unsupported commands [Need %04x]\n",
+ *((u16 *)(*data)));
+ return -EINVAL;
+ }
+
+ *data += 4;
+
+ return 0;
+}
+
+static int qed_nvm_flash(struct qed_dev *cdev, const char *name)
+{
+ const struct firmware *image;
+ const u8 *data, *data_end;
+ u32 cmd_type;
+ int rc;
+
+ rc = request_firmware(&image, name, &cdev->pdev->dev);
+ if (rc) {
+ DP_ERR(cdev, "Failed to find '%s'\n", name);
+ return rc;
+ }
+
+ DP_VERBOSE(cdev, NETIF_MSG_DRV,
+ "Flashing '%s' - firmware's data at %p, size is %08x\n",
+ name, image->data, (u32)image->size);
+ data = image->data;
+ data_end = data + image->size;
+
+ rc = qed_nvm_flash_image_validate(cdev, image, &data);
+ if (rc)
+ goto exit;
+
+ while (data < data_end) {
+ bool check_resp = false;
+
+ /* Parse the actual command */
+ cmd_type = *((u32 *)data);
+ switch (cmd_type) {
+ case QED_NVM_FLASH_CMD_FILE_DATA:
+ rc = qed_nvm_flash_image_file_data(cdev, &data,
+ &check_resp);
+ break;
+ case QED_NVM_FLASH_CMD_FILE_START:
+ rc = qed_nvm_flash_image_file_start(cdev, &data,
+ &check_resp);
+ break;
+ case QED_NVM_FLASH_CMD_NVM_CHANGE:
+ rc = qed_nvm_flash_image_access(cdev, &data,
+ &check_resp);
+ break;
+ default:
+ DP_ERR(cdev, "Unknown command %08x\n", cmd_type);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ if (rc) {
+ DP_ERR(cdev, "Command %08x failed\n", cmd_type);
+ goto exit;
+ }
+
+ /* Check response if needed */
+ if (check_resp) {
+ u32 mcp_response = 0;
+
+ if (qed_mcp_nvm_resp(cdev, (u8 *)&mcp_response)) {
+ DP_ERR(cdev, "Failed getting MCP response\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ switch (mcp_response & FW_MSG_CODE_MASK) {
+ case FW_MSG_CODE_OK:
+ case FW_MSG_CODE_NVM_OK:
+ case FW_MSG_CODE_NVM_PUT_FILE_FINISH_OK:
+ case FW_MSG_CODE_PHY_OK:
+ break;
+ default:
+ DP_ERR(cdev, "MFW returns error: %08x\n",
+ mcp_response);
+ rc = -EINVAL;
+ goto exit;
+ }
+ }
+ }
+
+exit:
+ release_firmware(image);
+
+ return rc;
+}
+
static int qed_nvm_get_image(struct qed_dev *cdev, enum qed_nvm_images type,
u8 *buf, u16 len)
{
@@ -1719,6 +2056,7 @@ const struct qed_common_ops qed_common_ops_pass = {
.dbg_all_data_size = &qed_dbg_all_data_size,
.chain_alloc = &qed_chain_alloc,
.chain_free = &qed_chain_free,
+ .nvm_flash = &qed_nvm_flash,
.nvm_get_image = &qed_nvm_get_image,
.set_coalesce = &qed_set_coalesce,
.set_led = &qed_set_led,
diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c
index 6f46cb11f349..ec0d425766a7 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c
@@ -569,6 +569,31 @@ int qed_mcp_cmd(struct qed_hwfn *p_hwfn,
return 0;
}
+int qed_mcp_nvm_wr_cmd(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt,
+ u32 cmd,
+ u32 param,
+ u32 *o_mcp_resp,
+ u32 *o_mcp_param, u32 i_txn_size, u32 *i_buf)
+{
+ struct qed_mcp_mb_params mb_params;
+ int rc;
+
+ memset(&mb_params, 0, sizeof(mb_params));
+ mb_params.cmd = cmd;
+ mb_params.param = param;
+ mb_params.p_data_src = i_buf;
+ mb_params.data_src_size = (u8)i_txn_size;
+ rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params);
+ if (rc)
+ return rc;
+
+ *o_mcp_resp = mb_params.mcp_resp;
+ *o_mcp_param = mb_params.mcp_param;
+
+ return 0;
+}
+
int qed_mcp_nvm_rd_cmd(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
u32 cmd,
@@ -2261,6 +2286,102 @@ int qed_mcp_nvm_read(struct qed_dev *cdev, u32 addr, u8 *p_buf, u32 len)
return rc;
}
+int qed_mcp_nvm_resp(struct qed_dev *cdev, u8 *p_buf)
+{
+ struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
+ struct qed_ptt *p_ptt;
+
+ p_ptt = qed_ptt_acquire(p_hwfn);
+ if (!p_ptt)
+ return -EBUSY;
+
+ memcpy(p_buf, &cdev->mcp_nvm_resp, sizeof(cdev->mcp_nvm_resp));
+ qed_ptt_release(p_hwfn, p_ptt);
+
+ return 0;
+}
+
+int qed_mcp_nvm_put_file_begin(struct qed_dev *cdev, u32 addr)
+{
+ struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
+ struct qed_ptt *p_ptt;
+ u32 resp, param;
+ int rc;
+
+ p_ptt = qed_ptt_acquire(p_hwfn);
+ if (!p_ptt)
+ return -EBUSY;
+ rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_NVM_PUT_FILE_BEGIN, addr,
+ &resp, &param);
+ cdev->mcp_nvm_resp = resp;
+ qed_ptt_release(p_hwfn, p_ptt);
+
+ return rc;
+}
+
+int qed_mcp_nvm_write(struct qed_dev *cdev,
+ u32 cmd, u32 addr, u8 *p_buf, u32 len)
+{
+ u32 buf_idx = 0, buf_size, nvm_cmd, nvm_offset, resp = 0, param;
+ struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
+ struct qed_ptt *p_ptt;
+ int rc = -EINVAL;
+
+ p_ptt = qed_ptt_acquire(p_hwfn);
+ if (!p_ptt)
+ return -EBUSY;
+
+ switch (cmd) {
+ case QED_PUT_FILE_DATA:
+ nvm_cmd = DRV_MSG_CODE_NVM_PUT_FILE_DATA;
+ break;
+ case QED_NVM_WRITE_NVRAM:
+ nvm_cmd = DRV_MSG_CODE_NVM_WRITE_NVRAM;
+ break;
+ default:
+ DP_NOTICE(p_hwfn, "Invalid nvm write command 0x%x\n", cmd);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ while (buf_idx < len) {
+ buf_size = min_t(u32, (len - buf_idx), MCP_DRV_NVM_BUF_LEN);
+ nvm_offset = ((buf_size << DRV_MB_PARAM_NVM_LEN_OFFSET) |
+ addr) + buf_idx;
+ rc = qed_mcp_nvm_wr_cmd(p_hwfn, p_ptt, nvm_cmd, nvm_offset,
+ &resp, &param, buf_size,
+ (u32 *)&p_buf[buf_idx]);
+ if (rc) {
+ DP_NOTICE(cdev, "nvm write failed, rc = %d\n", rc);
+ resp = FW_MSG_CODE_ERROR;
+ break;
+ }
+
+ if (resp != FW_MSG_CODE_OK &&
+ resp != FW_MSG_CODE_NVM_OK &&
+ resp != FW_MSG_CODE_NVM_PUT_FILE_FINISH_OK) {
+ DP_NOTICE(cdev,
+ "nvm write failed, resp = 0x%08x\n", resp);
+ rc = -EINVAL;
+ break;
+ }
+
+ /* This can be a lengthy process, and it's possible scheduler
+ * isn't pre-emptable. Sleep a bit to prevent CPU hogging.
+ */
+ if (buf_idx % 0x1000 > (buf_idx + buf_size) % 0x1000)
+ usleep_range(1000, 2000);
+
+ buf_idx += buf_size;
+ }
+
+ cdev->mcp_nvm_resp = resp;
+out:
+ qed_ptt_release(p_hwfn, p_ptt);
+
+ return rc;
+}
+
int qed_mcp_bist_register_test(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
{
u32 drv_mb_param = 0, rsp, param;
@@ -2303,9 +2424,9 @@ int qed_mcp_bist_clock_test(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
return rc;
}
-int qed_mcp_bist_nvm_test_get_num_images(struct qed_hwfn *p_hwfn,
- struct qed_ptt *p_ptt,
- u32 *num_images)
+int qed_mcp_bist_nvm_get_num_images(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt,
+ u32 *num_images)
{
u32 drv_mb_param = 0, rsp;
int rc = 0;
@@ -2324,10 +2445,10 @@ int qed_mcp_bist_nvm_test_get_num_images(struct qed_hwfn *p_hwfn,
return rc;
}
-int qed_mcp_bist_nvm_test_get_image_att(struct qed_hwfn *p_hwfn,
- struct qed_ptt *p_ptt,
- struct bist_nvm_image_att *p_image_att,
- u32 image_index)
+int qed_mcp_bist_nvm_get_image_att(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt,
+ struct bist_nvm_image_att *p_image_att,
+ u32 image_index)
{
u32 buf_size = 0, param, resp = 0, resp_param = 0;
int rc;
@@ -2351,16 +2472,71 @@ int qed_mcp_bist_nvm_test_get_image_att(struct qed_hwfn *p_hwfn,
return rc;
}
+int qed_mcp_nvm_info_populate(struct qed_hwfn *p_hwfn)
+{
+ struct qed_nvm_image_info *nvm_info = &p_hwfn->nvm_info;
+ struct qed_ptt *p_ptt;
+ int rc;
+ u32 i;
+
+ p_ptt = qed_ptt_acquire(p_hwfn);
+ if (!p_ptt) {
+ DP_ERR(p_hwfn, "failed to acquire ptt\n");
+ return -EBUSY;
+ }
+
+ /* Acquire from MFW the amount of available images */
+ nvm_info->num_images = 0;
+ rc = qed_mcp_bist_nvm_get_num_images(p_hwfn,
+ p_ptt, &nvm_info->num_images);
+ if (rc == -EOPNOTSUPP) {
+ DP_INFO(p_hwfn, "DRV_MSG_CODE_BIST_TEST is not supported\n");
+ goto out;
+ } else if (rc || !nvm_info->num_images) {
+ DP_ERR(p_hwfn, "Failed getting number of images\n");
+ goto err0;
+ }
+
+ nvm_info->image_att = kmalloc(nvm_info->num_images *
+ sizeof(struct bist_nvm_image_att),
+ GFP_KERNEL);
+ if (!nvm_info->image_att) {
+ rc = -ENOMEM;
+ goto err0;
+ }
+
+ /* Iterate over images and get their attributes */
+ for (i = 0; i < nvm_info->num_images; i++) {
+ rc = qed_mcp_bist_nvm_get_image_att(p_hwfn, p_ptt,
+ &nvm_info->image_att[i], i);
+ if (rc) {
+ DP_ERR(p_hwfn,
+ "Failed getting image index %d attributes\n", i);
+ goto err1;
+ }
+
+ DP_VERBOSE(p_hwfn, QED_MSG_SP, "image index %d, size %x\n", i,
+ nvm_info->image_att[i].len);
+ }
+out:
+ qed_ptt_release(p_hwfn, p_ptt);
+ return 0;
+
+err1:
+ kfree(nvm_info->image_att);
+err0:
+ qed_ptt_release(p_hwfn, p_ptt);
+ return rc;
+}
+
static int
qed_mcp_get_nvm_image_att(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
enum qed_nvm_images image_id,
struct qed_nvm_image_att *p_image_att)
{
- struct bist_nvm_image_att mfw_image_att;
enum nvm_image_type type;
- u32 num_images, i;
- int rc;
+ u32 i;
/* Translate image_id into MFW definitions */
switch (image_id) {
@@ -2376,29 +2552,18 @@ qed_mcp_get_nvm_image_att(struct qed_hwfn *p_hwfn,
return -EINVAL;
}
- /* Learn number of images, then traverse and see if one fits */
- rc = qed_mcp_bist_nvm_test_get_num_images(p_hwfn, p_ptt, &num_images);
- if (rc || !num_images)
- return -EINVAL;
-
- for (i = 0; i < num_images; i++) {
- rc = qed_mcp_bist_nvm_test_get_image_att(p_hwfn, p_ptt,
- &mfw_image_att, i);
- if (rc)
- return rc;
-
- if (type == mfw_image_att.image_type)
+ for (i = 0; i < p_hwfn->nvm_info.num_images; i++)
+ if (type == p_hwfn->nvm_info.image_att[i].image_type)
break;
- }
- if (i == num_images) {
+ if (i == p_hwfn->nvm_info.num_images) {
DP_VERBOSE(p_hwfn, QED_MSG_STORAGE,
"Failed to find nvram image of type %08x\n",
image_id);
- return -EINVAL;
+ return -ENOENT;
}
- p_image_att->start_addr = mfw_image_att.nvm_start_addr;
- p_image_att->length = mfw_image_att.len;
+ p_image_att->start_addr = p_hwfn->nvm_info.image_att[i].nvm_start_addr;
+ p_image_att->length = p_hwfn->nvm_info.image_att[i].len;
return 0;
}
diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.h b/drivers/net/ethernet/qlogic/qed/qed_mcp.h
index c7ec2395d1ce..8a5c988d0c3c 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_mcp.h
+++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.h
@@ -443,6 +443,40 @@ int qed_mcp_set_led(struct qed_hwfn *p_hwfn,
*/
int qed_mcp_nvm_read(struct qed_dev *cdev, u32 addr, u8 *p_buf, u32 len);
+/**
+ * @brief Write to nvm
+ *
+ * @param cdev
+ * @param addr - nvm offset
+ * @param cmd - nvm command
+ * @param p_buf - nvm write buffer
+ * @param len - buffer len
+ *
+ * @return int - 0 - operation was successful.
+ */
+int qed_mcp_nvm_write(struct qed_dev *cdev,
+ u32 cmd, u32 addr, u8 *p_buf, u32 len);
+
+/**
+ * @brief Put file begin
+ *
+ * @param cdev
+ * @param addr - nvm offset
+ *
+ * @return int - 0 - operation was successful.
+ */
+int qed_mcp_nvm_put_file_begin(struct qed_dev *cdev, u32 addr);
+
+/**
+ * @brief Check latest response
+ *
+ * @param cdev
+ * @param p_buf - nvm write buffer
+ *
+ * @return int - 0 - operation was successful.
+ */
+int qed_mcp_nvm_resp(struct qed_dev *cdev, u8 *p_buf);
+
struct qed_nvm_image_att {
u32 start_addr;
u32 length;
@@ -496,9 +530,9 @@ int qed_mcp_bist_clock_test(struct qed_hwfn *p_hwfn,
*
* @return int - 0 - operation was successful.
*/
-int qed_mcp_bist_nvm_test_get_num_images(struct qed_hwfn *p_hwfn,
- struct qed_ptt *p_ptt,
- u32 *num_images);
+int qed_mcp_bist_nvm_get_num_images(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt,
+ u32 *num_images);
/**
* @brief Bist nvm test - get image attributes by index
@@ -510,10 +544,10 @@ int qed_mcp_bist_nvm_test_get_num_images(struct qed_hwfn *p_hwfn,
*
* @return int - 0 - operation was successful.
*/
-int qed_mcp_bist_nvm_test_get_image_att(struct qed_hwfn *p_hwfn,
- struct qed_ptt *p_ptt,
- struct bist_nvm_image_att *p_image_att,
- u32 image_index);
+int qed_mcp_bist_nvm_get_image_att(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt,
+ struct bist_nvm_image_att *p_image_att,
+ u32 image_index);
/* Using hwfn number (and not pf_num) is required since in CMT mode,
* same pf_num may be used by two different hwfn
@@ -957,4 +991,12 @@ int qed_mcp_get_capabilities(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt);
* @param p_ptt
*/
int qed_mcp_set_capabilities(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt);
+
+/**
+ * @brief Populate the nvm info shadow in the given hardware function
+ *
+ * @param p_hwfn
+ */
+int qed_mcp_nvm_info_populate(struct qed_hwfn *p_hwfn);
+
#endif
diff --git a/drivers/net/ethernet/qlogic/qed/qed_selftest.c b/drivers/net/ethernet/qlogic/qed/qed_selftest.c
index 1bafc05db2b8..cf1d4476f9d8 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_selftest.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_selftest.c
@@ -125,10 +125,11 @@ int qed_selftest_nvram(struct qed_dev *cdev)
}
/* Acquire from MFW the amount of available images */
- rc = qed_mcp_bist_nvm_test_get_num_images(p_hwfn, p_ptt, &num_images);
+ rc = qed_mcp_bist_nvm_get_num_images(p_hwfn, p_ptt, &num_images);
if (rc || !num_images) {
DP_ERR(p_hwfn, "Failed getting number of images\n");
- return -EINVAL;
+ rc = -EINVAL;
+ goto err0;
}
/* Iterate over images and validate CRC */
@@ -136,8 +137,8 @@ int qed_selftest_nvram(struct qed_dev *cdev)
/* This mailbox returns information about the image required for
* reading it.
*/
- rc = qed_mcp_bist_nvm_test_get_image_att(p_hwfn, p_ptt,
- &image_att, i);
+ rc = qed_mcp_bist_nvm_get_image_att(p_hwfn, p_ptt,
+ &image_att, i);
if (rc) {
DP_ERR(p_hwfn,
"Failed getting image index %d attributes\n",
diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
index 4ca3847fffd4..ecbf1ded7a39 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
@@ -699,6 +699,14 @@ static u32 qede_get_link(struct net_device *dev)
return current_link.link_up;
}
+static int qede_flash_device(struct net_device *dev,
+ struct ethtool_flash *flash)
+{
+ struct qede_dev *edev = netdev_priv(dev);
+
+ return edev->ops->common->nvm_flash(edev->cdev, flash->data);
+}
+
static int qede_get_coalesce(struct net_device *dev,
struct ethtool_coalesce *coal)
{
@@ -1806,6 +1814,7 @@ static const struct ethtool_ops qede_ethtool_ops = {
.get_tunable = qede_get_tunable,
.set_tunable = qede_set_tunable,
+ .flash_device = qede_flash_device,
};
static const struct ethtool_ops qede_vf_ethtool_ops = {
diff --git a/drivers/net/ethernet/qlogic/qede/qede_fp.c b/drivers/net/ethernet/qlogic/qede/qede_fp.c
index dafc079ab6b9..14941303189d 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_fp.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_fp.c
@@ -320,13 +320,11 @@ static inline void qede_update_tx_producer(struct qede_tx_queue *txq)
barrier();
writel(txq->tx_db.raw, txq->doorbell_addr);
- /* mmiowb is needed to synchronize doorbell writes from more than one
- * processor. It guarantees that the write arrives to the device before
- * the queue lock is released and another start_xmit is called (possibly
- * on another CPU). Without this barrier, the next doorbell can bypass
- * this doorbell. This is applicable to IA64/Altix systems.
+ /* Fence required to flush the write combined buffer, since another
+ * CPU may write to the same doorbell address and data may be lost
+ * due to relaxed order nature of write combined bar.
*/
- mmiowb();
+ wmb();
}
static int qede_xdp_xmit(struct qede_dev *edev, struct qede_fastpath *fp,
@@ -1249,16 +1247,10 @@ static int qede_rx_process_cqe(struct qede_dev *edev,
csum_flag = qede_check_csum(parse_flag);
if (unlikely(csum_flag == QEDE_CSUM_ERROR)) {
- if (qede_pkt_is_ip_fragmented(fp_cqe, parse_flag)) {
+ if (qede_pkt_is_ip_fragmented(fp_cqe, parse_flag))
rxq->rx_ip_frags++;
- } else {
- DP_NOTICE(edev,
- "CQE has error, flags = %x, dropping incoming packet\n",
- parse_flag);
+ else
rxq->rx_hw_errors++;
- qede_recycle_rx_bd_ring(rxq, fp_cqe->bd_num);
- return 0;
- }
}
/* Basic validation passed; Need to prepare an SKB. This would also
diff --git a/drivers/net/ethernet/qlogic/qla3xxx.c b/drivers/net/ethernet/qlogic/qla3xxx.c
index 9e5264d8773b..b48f76182049 100644
--- a/drivers/net/ethernet/qlogic/qla3xxx.c
+++ b/drivers/net/ethernet/qlogic/qla3xxx.c
@@ -1858,8 +1858,9 @@ static void ql_update_small_bufq_prod_index(struct ql3_adapter *qdev)
qdev->small_buf_release_cnt -= 8;
}
wmb();
- writel(qdev->small_buf_q_producer_index,
- &port_regs->CommonRegs.rxSmallQProducerIndex);
+ writel_relaxed(qdev->small_buf_q_producer_index,
+ &port_regs->CommonRegs.rxSmallQProducerIndex);
+ mmiowb();
}
}
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
index 46b0372dd032..97c146e7698a 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
@@ -478,7 +478,7 @@ irqreturn_t qlcnic_83xx_clear_legacy_intr(struct qlcnic_adapter *adapter)
wmb();
/* clear the interrupt trigger control register */
- writel(0, adapter->isr_int_vec);
+ writel_relaxed(0, adapter->isr_int_vec);
intr_val = readl(adapter->isr_int_vec);
do {
intr_val = readl(adapter->tgt_status_reg);
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c
index 287d89dd086f..891f03a7a33d 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c
@@ -1175,81 +1175,81 @@ static ssize_t qlcnic_83xx_sysfs_flash_write_handler(struct file *filp,
}
static const struct device_attribute dev_attr_bridged_mode = {
- .attr = {.name = "bridged_mode", .mode = (S_IRUGO | S_IWUSR)},
- .show = qlcnic_show_bridged_mode,
- .store = qlcnic_store_bridged_mode,
+ .attr = { .name = "bridged_mode", .mode = 0644 },
+ .show = qlcnic_show_bridged_mode,
+ .store = qlcnic_store_bridged_mode,
};
static const struct device_attribute dev_attr_diag_mode = {
- .attr = {.name = "diag_mode", .mode = (S_IRUGO | S_IWUSR)},
+ .attr = { .name = "diag_mode", .mode = 0644 },
.show = qlcnic_show_diag_mode,
.store = qlcnic_store_diag_mode,
};
static const struct device_attribute dev_attr_beacon = {
- .attr = {.name = "beacon", .mode = (S_IRUGO | S_IWUSR)},
+ .attr = { .name = "beacon", .mode = 0644 },
.show = qlcnic_show_beacon,
.store = qlcnic_store_beacon,
};
static const struct bin_attribute bin_attr_crb = {
- .attr = {.name = "crb", .mode = (S_IRUGO | S_IWUSR)},
+ .attr = { .name = "crb", .mode = 0644 },
.size = 0,
.read = qlcnic_sysfs_read_crb,
.write = qlcnic_sysfs_write_crb,
};
static const struct bin_attribute bin_attr_mem = {
- .attr = {.name = "mem", .mode = (S_IRUGO | S_IWUSR)},
+ .attr = { .name = "mem", .mode = 0644 },
.size = 0,
.read = qlcnic_sysfs_read_mem,
.write = qlcnic_sysfs_write_mem,
};
static const struct bin_attribute bin_attr_npar_config = {
- .attr = {.name = "npar_config", .mode = (S_IRUGO | S_IWUSR)},
+ .attr = { .name = "npar_config", .mode = 0644 },
.size = 0,
.read = qlcnic_sysfs_read_npar_config,
.write = qlcnic_sysfs_write_npar_config,
};
static const struct bin_attribute bin_attr_pci_config = {
- .attr = {.name = "pci_config", .mode = (S_IRUGO | S_IWUSR)},
+ .attr = { .name = "pci_config", .mode = 0644 },
.size = 0,
.read = qlcnic_sysfs_read_pci_config,
.write = NULL,
};
static const struct bin_attribute bin_attr_port_stats = {
- .attr = {.name = "port_stats", .mode = (S_IRUGO | S_IWUSR)},
+ .attr = { .name = "port_stats", .mode = 0644 },
.size = 0,
.read = qlcnic_sysfs_get_port_stats,
.write = qlcnic_sysfs_clear_port_stats,
};
static const struct bin_attribute bin_attr_esw_stats = {
- .attr = {.name = "esw_stats", .mode = (S_IRUGO | S_IWUSR)},
+ .attr = { .name = "esw_stats", .mode = 0644 },
.size = 0,
.read = qlcnic_sysfs_get_esw_stats,
.write = qlcnic_sysfs_clear_esw_stats,
};
static const struct bin_attribute bin_attr_esw_config = {
- .attr = {.name = "esw_config", .mode = (S_IRUGO | S_IWUSR)},
+ .attr = { .name = "esw_config", .mode = 0644 },
.size = 0,
.read = qlcnic_sysfs_read_esw_config,
.write = qlcnic_sysfs_write_esw_config,
};
static const struct bin_attribute bin_attr_pm_config = {
- .attr = {.name = "pm_config", .mode = (S_IRUGO | S_IWUSR)},
+ .attr = { .name = "pm_config", .mode = 0644 },
.size = 0,
.read = qlcnic_sysfs_read_pm_config,
.write = qlcnic_sysfs_write_pm_config,
};
static const struct bin_attribute bin_attr_flash = {
- .attr = {.name = "flash", .mode = (S_IRUGO | S_IWUSR)},
+ .attr = { .name = "flash", .mode = 0644 },
.size = 0,
.read = qlcnic_83xx_sysfs_flash_read_handler,
.write = qlcnic_83xx_sysfs_flash_write_handler,
@@ -1276,7 +1276,7 @@ static ssize_t qlcnic_hwmon_show_temp(struct device *dev,
}
/* hwmon-sysfs attributes */
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
+static SENSOR_DEVICE_ATTR(temp1_input, 0444,
qlcnic_hwmon_show_temp, NULL, 1);
static struct attribute *qlcnic_hwmon_attrs[] = {
diff --git a/drivers/net/ethernet/qlogic/qlge/qlge.h b/drivers/net/ethernet/qlogic/qlge/qlge.h
index 84ac50f92c9c..3e71b65a9546 100644
--- a/drivers/net/ethernet/qlogic/qlge/qlge.h
+++ b/drivers/net/ethernet/qlogic/qlge/qlge.h
@@ -2185,6 +2185,22 @@ static inline void ql_write_db_reg(u32 val, void __iomem *addr)
}
/*
+ * Doorbell Registers:
+ * Doorbell registers are virtual registers in the PCI memory space.
+ * The space is allocated by the chip during PCI initialization. The
+ * device driver finds the doorbell address in BAR 3 in PCI config space.
+ * The registers are used to control outbound and inbound queues. For
+ * example, the producer index for an outbound queue. Each queue uses
+ * 1 4k chunk of memory. The lower half of the space is for outbound
+ * queues. The upper half is for inbound queues.
+ * Caller has to guarantee ordering.
+ */
+static inline void ql_write_db_reg_relaxed(u32 val, void __iomem *addr)
+{
+ writel_relaxed(val, addr);
+}
+
+/*
* Shadow Registers:
* Outbound queues have a consumer index that is maintained by the chip.
* Inbound queues have a producer index that is maintained by the chip.
diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_main.c b/drivers/net/ethernet/qlogic/qlge/qlge_main.c
index 50038d946ced..8293c2028002 100644
--- a/drivers/net/ethernet/qlogic/qlge/qlge_main.c
+++ b/drivers/net/ethernet/qlogic/qlge/qlge_main.c
@@ -2700,7 +2700,8 @@ static netdev_tx_t qlge_send(struct sk_buff *skb, struct net_device *ndev)
tx_ring->prod_idx = 0;
wmb();
- ql_write_db_reg(tx_ring->prod_idx, tx_ring->prod_idx_db_reg);
+ ql_write_db_reg_relaxed(tx_ring->prod_idx, tx_ring->prod_idx_db_reg);
+ mmiowb();
netif_printk(qdev, tx_queued, KERN_DEBUG, qdev->ndev,
"tx queued, slot %d, len %d\n",
tx_ring->prod_idx, skb->len);
diff --git a/drivers/net/ethernet/qualcomm/qca_debug.c b/drivers/net/ethernet/qualcomm/qca_debug.c
index 92b6be9c4429..51d89c86e60f 100644
--- a/drivers/net/ethernet/qualcomm/qca_debug.c
+++ b/drivers/net/ethernet/qualcomm/qca_debug.c
@@ -151,7 +151,7 @@ qcaspi_init_device_debugfs(struct qcaspi *qca)
dev_name(&qca->net_dev->dev));
return;
}
- debugfs_create_file("info", S_IFREG | S_IRUGO, device_root, qca,
+ debugfs_create_file("info", S_IFREG | 0444, device_root, qca,
&qcaspi_info_ops);
}
diff --git a/drivers/net/ethernet/qualcomm/qca_spi.c b/drivers/net/ethernet/qualcomm/qca_spi.c
index 9c236298fe21..5803cd6db406 100644
--- a/drivers/net/ethernet/qualcomm/qca_spi.c
+++ b/drivers/net/ethernet/qualcomm/qca_spi.c
@@ -705,7 +705,6 @@ qcaspi_netdev_xmit(struct sk_buff *skb, struct net_device *dev)
tskb = skb_copy_expand(skb, QCAFRM_HEADER_LEN,
QCAFRM_FOOTER_LEN + pad_len, GFP_ATOMIC);
if (!tskb) {
- netdev_dbg(qca->net_dev, "could not allocate tx_buff\n");
qca->stats.out_of_mem++;
return NETDEV_TX_BUSY;
}
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
index c4949183eef3..5f4e447c5dce 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -43,6 +43,11 @@
/* Local Definitions and Declarations */
+static const struct nla_policy rmnet_policy[IFLA_RMNET_MAX + 1] = {
+ [IFLA_RMNET_MUX_ID] = { .type = NLA_U16 },
+ [IFLA_RMNET_FLAGS] = { .len = sizeof(struct ifla_rmnet_flags) },
+};
+
static int rmnet_is_real_dev_registered(const struct net_device *real_dev)
{
return rcu_access_pointer(real_dev->rx_handler) == rmnet_rx_handler;
@@ -131,7 +136,7 @@ static int rmnet_newlink(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[],
struct netlink_ext_ack *extack)
{
- u32 data_format = RMNET_INGRESS_FORMAT_DEAGGREGATION;
+ u32 data_format = RMNET_FLAGS_INGRESS_DEAGGREGATION;
struct net_device *real_dev;
int mode = RMNET_EPMODE_VND;
struct rmnet_endpoint *ep;
@@ -143,14 +148,14 @@ static int rmnet_newlink(struct net *src_net, struct net_device *dev,
if (!real_dev || !dev)
return -ENODEV;
- if (!data[IFLA_VLAN_ID])
+ if (!data[IFLA_RMNET_MUX_ID])
return -EINVAL;
ep = kzalloc(sizeof(*ep), GFP_ATOMIC);
if (!ep)
return -ENOMEM;
- mux_id = nla_get_u16(data[IFLA_VLAN_ID]);
+ mux_id = nla_get_u16(data[IFLA_RMNET_MUX_ID]);
err = rmnet_register_real_device(real_dev);
if (err)
@@ -165,10 +170,10 @@ static int rmnet_newlink(struct net *src_net, struct net_device *dev,
hlist_add_head_rcu(&ep->hlnode, &port->muxed_ep[mux_id]);
- if (data[IFLA_VLAN_FLAGS]) {
- struct ifla_vlan_flags *flags;
+ if (data[IFLA_RMNET_FLAGS]) {
+ struct ifla_rmnet_flags *flags;
- flags = nla_data(data[IFLA_VLAN_FLAGS]);
+ flags = nla_data(data[IFLA_RMNET_FLAGS]);
data_format = flags->flags & flags->mask;
}
@@ -276,10 +281,10 @@ static int rmnet_rtnl_validate(struct nlattr *tb[], struct nlattr *data[],
{
u16 mux_id;
- if (!data || !data[IFLA_VLAN_ID])
+ if (!data || !data[IFLA_RMNET_MUX_ID])
return -EINVAL;
- mux_id = nla_get_u16(data[IFLA_VLAN_ID]);
+ mux_id = nla_get_u16(data[IFLA_RMNET_MUX_ID]);
if (mux_id > (RMNET_MAX_LOGICAL_EP - 1))
return -ERANGE;
@@ -304,9 +309,11 @@ static int rmnet_changelink(struct net_device *dev, struct nlattr *tb[],
port = rmnet_get_port_rtnl(real_dev);
- if (data[IFLA_VLAN_ID]) {
- mux_id = nla_get_u16(data[IFLA_VLAN_ID]);
+ if (data[IFLA_RMNET_MUX_ID]) {
+ mux_id = nla_get_u16(data[IFLA_RMNET_MUX_ID]);
ep = rmnet_get_endpoint(port, priv->mux_id);
+ if (!ep)
+ return -ENODEV;
hlist_del_init_rcu(&ep->hlnode);
hlist_add_head_rcu(&ep->hlnode, &port->muxed_ep[mux_id]);
@@ -315,10 +322,10 @@ static int rmnet_changelink(struct net_device *dev, struct nlattr *tb[],
priv->mux_id = mux_id;
}
- if (data[IFLA_VLAN_FLAGS]) {
- struct ifla_vlan_flags *flags;
+ if (data[IFLA_RMNET_FLAGS]) {
+ struct ifla_rmnet_flags *flags;
- flags = nla_data(data[IFLA_VLAN_FLAGS]);
+ flags = nla_data(data[IFLA_RMNET_FLAGS]);
port->data_format = flags->flags & flags->mask;
}
@@ -327,13 +334,46 @@ static int rmnet_changelink(struct net_device *dev, struct nlattr *tb[],
static size_t rmnet_get_size(const struct net_device *dev)
{
- return nla_total_size(2) /* IFLA_VLAN_ID */ +
- nla_total_size(sizeof(struct ifla_vlan_flags)); /* IFLA_VLAN_FLAGS */
+ return
+ /* IFLA_RMNET_MUX_ID */
+ nla_total_size(2) +
+ /* IFLA_RMNET_FLAGS */
+ nla_total_size(sizeof(struct ifla_rmnet_flags));
+}
+
+static int rmnet_fill_info(struct sk_buff *skb, const struct net_device *dev)
+{
+ struct rmnet_priv *priv = netdev_priv(dev);
+ struct net_device *real_dev;
+ struct ifla_rmnet_flags f;
+ struct rmnet_port *port;
+
+ real_dev = priv->real_dev;
+
+ if (nla_put_u16(skb, IFLA_RMNET_MUX_ID, priv->mux_id))
+ goto nla_put_failure;
+
+ if (rmnet_is_real_dev_registered(real_dev)) {
+ port = rmnet_get_port_rtnl(real_dev);
+ f.flags = port->data_format;
+ } else {
+ f.flags = 0;
+ }
+
+ f.mask = ~0;
+
+ if (nla_put(skb, IFLA_RMNET_FLAGS, sizeof(f), &f))
+ goto nla_put_failure;
+
+ return 0;
+
+nla_put_failure:
+ return -EMSGSIZE;
}
struct rtnl_link_ops rmnet_link_ops __read_mostly = {
.kind = "rmnet",
- .maxtype = __IFLA_VLAN_MAX,
+ .maxtype = __IFLA_RMNET_MAX,
.priv_size = sizeof(struct rmnet_priv),
.setup = rmnet_vnd_setup,
.validate = rmnet_rtnl_validate,
@@ -341,6 +381,8 @@ struct rtnl_link_ops rmnet_link_ops __read_mostly = {
.dellink = rmnet_dellink,
.get_size = rmnet_get_size,
.changelink = rmnet_changelink,
+ .policy = rmnet_policy,
+ .fill_info = rmnet_fill_info,
};
/* Needs either rcu_read_lock() or rtnl lock */
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h
index 00e4634100d3..0b5b5da80198 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2014, 2016-2017 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, 2016-2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c
index 601edec28c5f..6fcd586e9804 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -70,7 +70,7 @@ __rmnet_map_ingress_handler(struct sk_buff *skb,
u8 mux_id;
if (RMNET_MAP_GET_CD_BIT(skb)) {
- if (port->data_format & RMNET_INGRESS_FORMAT_MAP_COMMANDS)
+ if (port->data_format & RMNET_FLAGS_INGRESS_MAP_COMMANDS)
return rmnet_map_command(skb, port);
goto free_skb;
@@ -93,7 +93,7 @@ __rmnet_map_ingress_handler(struct sk_buff *skb,
skb_pull(skb, sizeof(struct rmnet_map_header));
rmnet_set_skb_proto(skb);
- if (port->data_format & RMNET_INGRESS_FORMAT_MAP_CKSUMV4) {
+ if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) {
if (!rmnet_map_checksum_downlink_packet(skb, len + pad))
skb->ip_summed = CHECKSUM_UNNECESSARY;
}
@@ -121,7 +121,7 @@ rmnet_map_ingress_handler(struct sk_buff *skb,
skb_push(skb, ETH_HLEN);
}
- if (port->data_format & RMNET_INGRESS_FORMAT_DEAGGREGATION) {
+ if (port->data_format & RMNET_FLAGS_INGRESS_DEAGGREGATION) {
while ((skbn = rmnet_map_deaggregate(skb, port)) != NULL)
__rmnet_map_ingress_handler(skbn, port);
@@ -141,7 +141,7 @@ static int rmnet_map_egress_handler(struct sk_buff *skb,
additional_header_len = 0;
required_headroom = sizeof(struct rmnet_map_header);
- if (port->data_format & RMNET_EGRESS_FORMAT_MAP_CKSUMV4) {
+ if (port->data_format & RMNET_FLAGS_EGRESS_MAP_CKSUMV4) {
additional_header_len = sizeof(struct rmnet_map_ul_csum_header);
required_headroom += additional_header_len;
}
@@ -151,7 +151,7 @@ static int rmnet_map_egress_handler(struct sk_buff *skb,
goto fail;
}
- if (port->data_format & RMNET_EGRESS_FORMAT_MAP_CKSUMV4)
+ if (port->data_format & RMNET_FLAGS_EGRESS_MAP_CKSUMV4)
rmnet_map_checksum_uplink_packet(skb, orig_dev);
map_header = rmnet_map_add_map_header(skb, additional_header_len, 0);
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h
index 6ce31e29136d..884f1f52dcc2 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -23,8 +23,8 @@ struct rmnet_map_control_command {
struct {
u16 ip_family:2;
u16 reserved:14;
- u16 flow_control_seq_num;
- u32 qos_id;
+ __be16 flow_control_seq_num;
+ __be32 qos_id;
} flow_control;
u8 data[0];
};
@@ -44,7 +44,7 @@ struct rmnet_map_header {
u8 reserved_bit:1;
u8 cd_bit:1;
u8 mux_id;
- u16 pkt_len;
+ __be16 pkt_len;
} __aligned(1);
struct rmnet_map_dl_csum_trailer {
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c
index b0dbca070c00..78fdad0c6f76 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -69,7 +69,7 @@ static void rmnet_map_send_ack(struct sk_buff *skb,
struct rmnet_map_control_command *cmd;
int xmit_status;
- if (port->data_format & RMNET_INGRESS_FORMAT_MAP_CKSUMV4) {
+ if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) {
if (skb->len < sizeof(struct rmnet_map_header) +
RMNET_MAP_GET_LENGTH(skb) +
sizeof(struct rmnet_map_dl_csum_trailer)) {
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
index c74a6c56d315..a6ea09416f8d 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -309,7 +309,7 @@ struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,
maph = (struct rmnet_map_header *)skb->data;
packet_len = ntohs(maph->pkt_len) + sizeof(struct rmnet_map_header);
- if (port->data_format & RMNET_INGRESS_FORMAT_MAP_CKSUMV4)
+ if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4)
packet_len += sizeof(struct rmnet_map_dl_csum_trailer);
if (((int)skb->len - (int)packet_len) < 0)
@@ -323,7 +323,6 @@ struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,
if (!skbn)
return NULL;
- skbn->dev = skb->dev;
skb_reserve(skbn, RMNET_MAP_DEAGGR_HEADROOM);
skb_put(skbn, packet_len);
memcpy(skbn->data, skb->data, packet_len);
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h
index de0143eaa05a..b9cc4f85f229 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2014, 2016-2017 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, 2016-2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -18,12 +18,6 @@
#define RMNET_NEEDED_HEADROOM 16
#define RMNET_TX_QUEUE_LEN 1000
-/* Constants */
-#define RMNET_INGRESS_FORMAT_DEAGGREGATION BIT(0)
-#define RMNET_INGRESS_FORMAT_MAP_COMMANDS BIT(1)
-#define RMNET_INGRESS_FORMAT_MAP_CKSUMV4 BIT(2)
-#define RMNET_EGRESS_FORMAT_MAP_CKSUMV4 BIT(3)
-
/* Replace skb->dev to a virtual rmnet device and pass up the stack */
#define RMNET_EPMODE_VND (1)
/* Pass the frame directly to another device with dev_queue_xmit() */
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c
index 346d310914df..2ea16a088de8 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index 0bf7d1759250..604ae78381ae 100644
--- a/drivers/net/ethernet/realtek/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -99,12 +99,12 @@ static const int multicast_filter_limit = 32;
#define RTL8169_PHY_TIMEOUT (10*HZ)
/* write/read MMIO register */
-#define RTL_W8(reg, val8) writeb ((val8), ioaddr + (reg))
-#define RTL_W16(reg, val16) writew ((val16), ioaddr + (reg))
-#define RTL_W32(reg, val32) writel ((val32), ioaddr + (reg))
-#define RTL_R8(reg) readb (ioaddr + (reg))
-#define RTL_R16(reg) readw (ioaddr + (reg))
-#define RTL_R32(reg) readl (ioaddr + (reg))
+#define RTL_W8(tp, reg, val8) writeb((val8), tp->mmio_addr + (reg))
+#define RTL_W16(tp, reg, val16) writew((val16), tp->mmio_addr + (reg))
+#define RTL_W32(tp, reg, val32) writel((val32), tp->mmio_addr + (reg))
+#define RTL_R8(tp, reg) readb(tp->mmio_addr + (reg))
+#define RTL_R16(tp, reg) readw(tp->mmio_addr + (reg))
+#define RTL_R32(tp, reg) readl(tp->mmio_addr + (reg))
enum mac_version {
RTL_GIGA_MAC_VER_01 = 0,
@@ -735,12 +735,6 @@ struct ring_info {
u8 __pad[sizeof(void *) - sizeof(u32)];
};
-enum features {
- RTL_FEATURE_WOL = (1 << 0),
- RTL_FEATURE_MSI = (1 << 1),
- RTL_FEATURE_GMII = (1 << 2),
-};
-
struct rtl8169_counters {
__le64 tx_packets;
__le64 rx_packets;
@@ -829,7 +823,7 @@ struct rtl8169_private {
void (*phy_reset_enable)(struct rtl8169_private *tp);
void (*hw_start)(struct net_device *);
unsigned int (*phy_reset_pending)(struct rtl8169_private *tp);
- unsigned int (*link_ok)(void __iomem *);
+ unsigned int (*link_ok)(struct rtl8169_private *tp);
int (*do_ioctl)(struct rtl8169_private *tp, struct mii_ioctl_data *data, int cmd);
bool (*tso_csum)(struct rtl8169_private *, struct sk_buff *, u32 *);
@@ -893,6 +887,11 @@ MODULE_FIRMWARE(FIRMWARE_8168H_2);
MODULE_FIRMWARE(FIRMWARE_8107E_1);
MODULE_FIRMWARE(FIRMWARE_8107E_2);
+static inline struct device *tp_to_dev(struct rtl8169_private *tp)
+{
+ return &tp->pci_dev->dev;
+}
+
static void rtl_lock_work(struct rtl8169_private *tp)
{
mutex_lock(&tp->wk.mutex);
@@ -903,9 +902,9 @@ static void rtl_unlock_work(struct rtl8169_private *tp)
mutex_unlock(&tp->wk.mutex);
}
-static void rtl_tx_performance_tweak(struct pci_dev *pdev, u16 force)
+static void rtl_tx_performance_tweak(struct rtl8169_private *tp, u16 force)
{
- pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL,
+ pcie_capability_clear_and_set_word(tp->pci_dev, PCI_EXP_DEVCTL,
PCI_EXP_DEVCTL_READRQ, force);
}
@@ -984,56 +983,46 @@ static bool rtl_ocp_reg_failure(struct rtl8169_private *tp, u32 reg)
DECLARE_RTL_COND(rtl_ocp_gphy_cond)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- return RTL_R32(GPHY_OCP) & OCPAR_FLAG;
+ return RTL_R32(tp, GPHY_OCP) & OCPAR_FLAG;
}
static void r8168_phy_ocp_write(struct rtl8169_private *tp, u32 reg, u32 data)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
if (rtl_ocp_reg_failure(tp, reg))
return;
- RTL_W32(GPHY_OCP, OCPAR_FLAG | (reg << 15) | data);
+ RTL_W32(tp, GPHY_OCP, OCPAR_FLAG | (reg << 15) | data);
rtl_udelay_loop_wait_low(tp, &rtl_ocp_gphy_cond, 25, 10);
}
static u16 r8168_phy_ocp_read(struct rtl8169_private *tp, u32 reg)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
if (rtl_ocp_reg_failure(tp, reg))
return 0;
- RTL_W32(GPHY_OCP, reg << 15);
+ RTL_W32(tp, GPHY_OCP, reg << 15);
return rtl_udelay_loop_wait_high(tp, &rtl_ocp_gphy_cond, 25, 10) ?
- (RTL_R32(GPHY_OCP) & 0xffff) : ~0;
+ (RTL_R32(tp, GPHY_OCP) & 0xffff) : ~0;
}
static void r8168_mac_ocp_write(struct rtl8169_private *tp, u32 reg, u32 data)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
if (rtl_ocp_reg_failure(tp, reg))
return;
- RTL_W32(OCPDR, OCPAR_FLAG | (reg << 15) | data);
+ RTL_W32(tp, OCPDR, OCPAR_FLAG | (reg << 15) | data);
}
static u16 r8168_mac_ocp_read(struct rtl8169_private *tp, u32 reg)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
if (rtl_ocp_reg_failure(tp, reg))
return 0;
- RTL_W32(OCPDR, reg << 15);
+ RTL_W32(tp, OCPDR, reg << 15);
- return RTL_R32(OCPDR);
+ return RTL_R32(tp, OCPDR);
}
#define OCP_STD_PHY_BASE 0xa400
@@ -1076,16 +1065,12 @@ static int mac_mcu_read(struct rtl8169_private *tp, int reg)
DECLARE_RTL_COND(rtl_phyar_cond)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- return RTL_R32(PHYAR) & 0x80000000;
+ return RTL_R32(tp, PHYAR) & 0x80000000;
}
static void r8169_mdio_write(struct rtl8169_private *tp, int reg, int value)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W32(PHYAR, 0x80000000 | (reg & 0x1f) << 16 | (value & 0xffff));
+ RTL_W32(tp, PHYAR, 0x80000000 | (reg & 0x1f) << 16 | (value & 0xffff));
rtl_udelay_loop_wait_low(tp, &rtl_phyar_cond, 25, 20);
/*
@@ -1097,13 +1082,12 @@ static void r8169_mdio_write(struct rtl8169_private *tp, int reg, int value)
static int r8169_mdio_read(struct rtl8169_private *tp, int reg)
{
- void __iomem *ioaddr = tp->mmio_addr;
int value;
- RTL_W32(PHYAR, 0x0 | (reg & 0x1f) << 16);
+ RTL_W32(tp, PHYAR, 0x0 | (reg & 0x1f) << 16);
value = rtl_udelay_loop_wait_high(tp, &rtl_phyar_cond, 25, 20) ?
- RTL_R32(PHYAR) & 0xffff : ~0;
+ RTL_R32(tp, PHYAR) & 0xffff : ~0;
/*
* According to hardware specs a 20us delay is required after read
@@ -1116,18 +1100,14 @@ static int r8169_mdio_read(struct rtl8169_private *tp, int reg)
DECLARE_RTL_COND(rtl_ocpar_cond)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- return RTL_R32(OCPAR) & OCPAR_FLAG;
+ return RTL_R32(tp, OCPAR) & OCPAR_FLAG;
}
static void r8168dp_1_mdio_access(struct rtl8169_private *tp, int reg, u32 data)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W32(OCPDR, data | ((reg & OCPDR_REG_MASK) << OCPDR_GPHY_REG_SHIFT));
- RTL_W32(OCPAR, OCPAR_GPHY_WRITE_CMD);
- RTL_W32(EPHY_RXER_NUM, 0);
+ RTL_W32(tp, OCPDR, data | ((reg & OCPDR_REG_MASK) << OCPDR_GPHY_REG_SHIFT));
+ RTL_W32(tp, OCPAR, OCPAR_GPHY_WRITE_CMD);
+ RTL_W32(tp, EPHY_RXER_NUM, 0);
rtl_udelay_loop_wait_low(tp, &rtl_ocpar_cond, 1000, 100);
}
@@ -1140,51 +1120,46 @@ static void r8168dp_1_mdio_write(struct rtl8169_private *tp, int reg, int value)
static int r8168dp_1_mdio_read(struct rtl8169_private *tp, int reg)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
r8168dp_1_mdio_access(tp, reg, OCPDR_READ_CMD);
mdelay(1);
- RTL_W32(OCPAR, OCPAR_GPHY_READ_CMD);
- RTL_W32(EPHY_RXER_NUM, 0);
+ RTL_W32(tp, OCPAR, OCPAR_GPHY_READ_CMD);
+ RTL_W32(tp, EPHY_RXER_NUM, 0);
return rtl_udelay_loop_wait_high(tp, &rtl_ocpar_cond, 1000, 100) ?
- RTL_R32(OCPDR) & OCPDR_DATA_MASK : ~0;
+ RTL_R32(tp, OCPDR) & OCPDR_DATA_MASK : ~0;
}
#define R8168DP_1_MDIO_ACCESS_BIT 0x00020000
-static void r8168dp_2_mdio_start(void __iomem *ioaddr)
+static void r8168dp_2_mdio_start(struct rtl8169_private *tp)
{
- RTL_W32(0xd0, RTL_R32(0xd0) & ~R8168DP_1_MDIO_ACCESS_BIT);
+ RTL_W32(tp, 0xd0, RTL_R32(tp, 0xd0) & ~R8168DP_1_MDIO_ACCESS_BIT);
}
-static void r8168dp_2_mdio_stop(void __iomem *ioaddr)
+static void r8168dp_2_mdio_stop(struct rtl8169_private *tp)
{
- RTL_W32(0xd0, RTL_R32(0xd0) | R8168DP_1_MDIO_ACCESS_BIT);
+ RTL_W32(tp, 0xd0, RTL_R32(tp, 0xd0) | R8168DP_1_MDIO_ACCESS_BIT);
}
static void r8168dp_2_mdio_write(struct rtl8169_private *tp, int reg, int value)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- r8168dp_2_mdio_start(ioaddr);
+ r8168dp_2_mdio_start(tp);
r8169_mdio_write(tp, reg, value);
- r8168dp_2_mdio_stop(ioaddr);
+ r8168dp_2_mdio_stop(tp);
}
static int r8168dp_2_mdio_read(struct rtl8169_private *tp, int reg)
{
- void __iomem *ioaddr = tp->mmio_addr;
int value;
- r8168dp_2_mdio_start(ioaddr);
+ r8168dp_2_mdio_start(tp);
value = r8169_mdio_read(tp, reg);
- r8168dp_2_mdio_stop(ioaddr);
+ r8168dp_2_mdio_stop(tp);
return value;
}
@@ -1229,16 +1204,12 @@ static int rtl_mdio_read(struct net_device *dev, int phy_id, int location)
DECLARE_RTL_COND(rtl_ephyar_cond)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- return RTL_R32(EPHYAR) & EPHYAR_FLAG;
+ return RTL_R32(tp, EPHYAR) & EPHYAR_FLAG;
}
static void rtl_ephy_write(struct rtl8169_private *tp, int reg_addr, int value)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W32(EPHYAR, EPHYAR_WRITE_CMD | (value & EPHYAR_DATA_MASK) |
+ RTL_W32(tp, EPHYAR, EPHYAR_WRITE_CMD | (value & EPHYAR_DATA_MASK) |
(reg_addr & EPHYAR_REG_MASK) << EPHYAR_REG_SHIFT);
rtl_udelay_loop_wait_low(tp, &rtl_ephyar_cond, 10, 100);
@@ -1248,41 +1219,33 @@ static void rtl_ephy_write(struct rtl8169_private *tp, int reg_addr, int value)
static u16 rtl_ephy_read(struct rtl8169_private *tp, int reg_addr)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W32(EPHYAR, (reg_addr & EPHYAR_REG_MASK) << EPHYAR_REG_SHIFT);
+ RTL_W32(tp, EPHYAR, (reg_addr & EPHYAR_REG_MASK) << EPHYAR_REG_SHIFT);
return rtl_udelay_loop_wait_high(tp, &rtl_ephyar_cond, 10, 100) ?
- RTL_R32(EPHYAR) & EPHYAR_DATA_MASK : ~0;
+ RTL_R32(tp, EPHYAR) & EPHYAR_DATA_MASK : ~0;
}
DECLARE_RTL_COND(rtl_eriar_cond)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- return RTL_R32(ERIAR) & ERIAR_FLAG;
+ return RTL_R32(tp, ERIAR) & ERIAR_FLAG;
}
static void rtl_eri_write(struct rtl8169_private *tp, int addr, u32 mask,
u32 val, int type)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
BUG_ON((addr & 3) || (mask == 0));
- RTL_W32(ERIDR, val);
- RTL_W32(ERIAR, ERIAR_WRITE_CMD | type | mask | addr);
+ RTL_W32(tp, ERIDR, val);
+ RTL_W32(tp, ERIAR, ERIAR_WRITE_CMD | type | mask | addr);
rtl_udelay_loop_wait_low(tp, &rtl_eriar_cond, 100, 100);
}
static u32 rtl_eri_read(struct rtl8169_private *tp, int addr, int type)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W32(ERIAR, ERIAR_READ_CMD | type | ERIAR_MASK_1111 | addr);
+ RTL_W32(tp, ERIAR, ERIAR_READ_CMD | type | ERIAR_MASK_1111 | addr);
return rtl_udelay_loop_wait_high(tp, &rtl_eriar_cond, 100, 100) ?
- RTL_R32(ERIDR) : ~0;
+ RTL_R32(tp, ERIDR) : ~0;
}
static void rtl_w0w1_eri(struct rtl8169_private *tp, int addr, u32 mask, u32 p,
@@ -1296,11 +1259,9 @@ static void rtl_w0w1_eri(struct rtl8169_private *tp, int addr, u32 mask, u32 p,
static u32 r8168dp_ocp_read(struct rtl8169_private *tp, u8 mask, u16 reg)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W32(OCPAR, ((u32)mask & 0x0f) << 12 | (reg & 0x0fff));
+ RTL_W32(tp, OCPAR, ((u32)mask & 0x0f) << 12 | (reg & 0x0fff));
return rtl_udelay_loop_wait_high(tp, &rtl_ocpar_cond, 100, 20) ?
- RTL_R32(OCPDR) : ~0;
+ RTL_R32(tp, OCPDR) : ~0;
}
static u32 r8168ep_ocp_read(struct rtl8169_private *tp, u8 mask, u16 reg)
@@ -1328,10 +1289,8 @@ static u32 ocp_read(struct rtl8169_private *tp, u8 mask, u16 reg)
static void r8168dp_ocp_write(struct rtl8169_private *tp, u8 mask, u16 reg,
u32 data)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W32(OCPDR, data);
- RTL_W32(OCPAR, OCPAR_FLAG | ((u32)mask & 0x0f) << 12 | (reg & 0x0fff));
+ RTL_W32(tp, OCPDR, data);
+ RTL_W32(tp, OCPAR, OCPAR_FLAG | ((u32)mask & 0x0f) << 12 | (reg & 0x0fff));
rtl_udelay_loop_wait_low(tp, &rtl_ocpar_cond, 100, 20);
}
@@ -1393,19 +1352,15 @@ DECLARE_RTL_COND(rtl_ep_ocp_read_cond)
DECLARE_RTL_COND(rtl_ocp_tx_cond)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- return RTL_R8(IBISR0) & 0x20;
+ return RTL_R8(tp, IBISR0) & 0x20;
}
static void rtl8168ep_stop_cmac(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W8(IBCR2, RTL_R8(IBCR2) & ~0x01);
+ RTL_W8(tp, IBCR2, RTL_R8(tp, IBCR2) & ~0x01);
rtl_msleep_loop_wait_high(tp, &rtl_ocp_tx_cond, 50, 2000);
- RTL_W8(IBISR0, RTL_R8(IBISR0) | 0x20);
- RTL_W8(IBCR0, RTL_R8(IBCR0) & ~0x01);
+ RTL_W8(tp, IBISR0, RTL_R8(tp, IBISR0) | 0x20);
+ RTL_W8(tp, IBCR0, RTL_R8(tp, IBCR0) & ~0x01);
}
static void rtl8168dp_driver_start(struct rtl8169_private *tp)
@@ -1473,19 +1428,19 @@ static void rtl8168_driver_stop(struct rtl8169_private *tp)
}
}
-static int r8168dp_check_dash(struct rtl8169_private *tp)
+static bool r8168dp_check_dash(struct rtl8169_private *tp)
{
u16 reg = rtl8168_get_ocp_reg(tp);
- return (ocp_read(tp, 0x0f, reg) & 0x00008000) ? 1 : 0;
+ return !!(ocp_read(tp, 0x0f, reg) & 0x00008000);
}
-static int r8168ep_check_dash(struct rtl8169_private *tp)
+static bool r8168ep_check_dash(struct rtl8169_private *tp)
{
- return (ocp_read(tp, 0x0f, 0x128) & 0x00000001) ? 1 : 0;
+ return !!(ocp_read(tp, 0x0f, 0x128) & 0x00000001);
}
-static int r8168_check_dash(struct rtl8169_private *tp)
+static bool r8168_check_dash(struct rtl8169_private *tp)
{
switch (tp->mac_version) {
case RTL_GIGA_MAC_VER_27:
@@ -1497,7 +1452,7 @@ static int r8168_check_dash(struct rtl8169_private *tp)
case RTL_GIGA_MAC_VER_51:
return r8168ep_check_dash(tp);
default:
- return 0;
+ return false;
}
}
@@ -1518,49 +1473,37 @@ static void rtl_write_exgmac_batch(struct rtl8169_private *tp,
DECLARE_RTL_COND(rtl_efusear_cond)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- return RTL_R32(EFUSEAR) & EFUSEAR_FLAG;
+ return RTL_R32(tp, EFUSEAR) & EFUSEAR_FLAG;
}
static u8 rtl8168d_efuse_read(struct rtl8169_private *tp, int reg_addr)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W32(EFUSEAR, (reg_addr & EFUSEAR_REG_MASK) << EFUSEAR_REG_SHIFT);
+ RTL_W32(tp, EFUSEAR, (reg_addr & EFUSEAR_REG_MASK) << EFUSEAR_REG_SHIFT);
return rtl_udelay_loop_wait_high(tp, &rtl_efusear_cond, 100, 300) ?
- RTL_R32(EFUSEAR) & EFUSEAR_DATA_MASK : ~0;
+ RTL_R32(tp, EFUSEAR) & EFUSEAR_DATA_MASK : ~0;
}
static u16 rtl_get_events(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- return RTL_R16(IntrStatus);
+ return RTL_R16(tp, IntrStatus);
}
static void rtl_ack_events(struct rtl8169_private *tp, u16 bits)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W16(IntrStatus, bits);
+ RTL_W16(tp, IntrStatus, bits);
mmiowb();
}
static void rtl_irq_disable(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W16(IntrMask, 0);
+ RTL_W16(tp, IntrMask, 0);
mmiowb();
}
static void rtl_irq_enable(struct rtl8169_private *tp, u16 bits)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W16(IntrMask, bits);
+ RTL_W16(tp, IntrMask, bits);
}
#define RTL_EVENT_NAPI_RX (RxOK | RxErr)
@@ -1574,18 +1517,14 @@ static void rtl_irq_enable_all(struct rtl8169_private *tp)
static void rtl8169_irq_mask_and_ack(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
rtl_irq_disable(tp);
rtl_ack_events(tp, RTL_EVENT_NAPI | tp->event_slow);
- RTL_R8(ChipCmd);
+ RTL_R8(tp, ChipCmd);
}
static unsigned int rtl8169_tbi_reset_pending(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- return RTL_R32(TBICSR) & TBIReset;
+ return RTL_R32(tp, TBICSR) & TBIReset;
}
static unsigned int rtl8169_xmii_reset_pending(struct rtl8169_private *tp)
@@ -1593,21 +1532,19 @@ static unsigned int rtl8169_xmii_reset_pending(struct rtl8169_private *tp)
return rtl_readphy(tp, MII_BMCR) & BMCR_RESET;
}
-static unsigned int rtl8169_tbi_link_ok(void __iomem *ioaddr)
+static unsigned int rtl8169_tbi_link_ok(struct rtl8169_private *tp)
{
- return RTL_R32(TBICSR) & TBILinkOk;
+ return RTL_R32(tp, TBICSR) & TBILinkOk;
}
-static unsigned int rtl8169_xmii_link_ok(void __iomem *ioaddr)
+static unsigned int rtl8169_xmii_link_ok(struct rtl8169_private *tp)
{
- return RTL_R8(PHYstatus) & LinkStatus;
+ return RTL_R8(tp, PHYstatus) & LinkStatus;
}
static void rtl8169_tbi_reset_enable(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W32(TBICSR, RTL_R32(TBICSR) | TBIReset);
+ RTL_W32(tp, TBICSR, RTL_R32(tp, TBICSR) | TBIReset);
}
static void rtl8169_xmii_reset_enable(struct rtl8169_private *tp)
@@ -1620,7 +1557,6 @@ static void rtl8169_xmii_reset_enable(struct rtl8169_private *tp)
static void rtl_link_chg_patch(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
struct net_device *dev = tp->dev;
if (!netif_running(dev))
@@ -1628,12 +1564,12 @@ static void rtl_link_chg_patch(struct rtl8169_private *tp)
if (tp->mac_version == RTL_GIGA_MAC_VER_34 ||
tp->mac_version == RTL_GIGA_MAC_VER_38) {
- if (RTL_R8(PHYstatus) & _1000bpsF) {
+ if (RTL_R8(tp, PHYstatus) & _1000bpsF) {
rtl_eri_write(tp, 0x1bc, ERIAR_MASK_1111, 0x00000011,
ERIAR_EXGMAC);
rtl_eri_write(tp, 0x1dc, ERIAR_MASK_1111, 0x00000005,
ERIAR_EXGMAC);
- } else if (RTL_R8(PHYstatus) & _100bps) {
+ } else if (RTL_R8(tp, PHYstatus) & _100bps) {
rtl_eri_write(tp, 0x1bc, ERIAR_MASK_1111, 0x0000001f,
ERIAR_EXGMAC);
rtl_eri_write(tp, 0x1dc, ERIAR_MASK_1111, 0x00000005,
@@ -1651,7 +1587,7 @@ static void rtl_link_chg_patch(struct rtl8169_private *tp)
ERIAR_EXGMAC);
} else if (tp->mac_version == RTL_GIGA_MAC_VER_35 ||
tp->mac_version == RTL_GIGA_MAC_VER_36) {
- if (RTL_R8(PHYstatus) & _1000bpsF) {
+ if (RTL_R8(tp, PHYstatus) & _1000bpsF) {
rtl_eri_write(tp, 0x1bc, ERIAR_MASK_1111, 0x00000011,
ERIAR_EXGMAC);
rtl_eri_write(tp, 0x1dc, ERIAR_MASK_1111, 0x00000005,
@@ -1663,7 +1599,7 @@ static void rtl_link_chg_patch(struct rtl8169_private *tp)
ERIAR_EXGMAC);
}
} else if (tp->mac_version == RTL_GIGA_MAC_VER_37) {
- if (RTL_R8(PHYstatus) & _10bps) {
+ if (RTL_R8(tp, PHYstatus) & _10bps) {
rtl_eri_write(tp, 0x1d0, ERIAR_MASK_0011, 0x4d02,
ERIAR_EXGMAC);
rtl_eri_write(tp, 0x1dc, ERIAR_MASK_0011, 0x0060,
@@ -1676,20 +1612,21 @@ static void rtl_link_chg_patch(struct rtl8169_private *tp)
}
static void rtl8169_check_link_status(struct net_device *dev,
- struct rtl8169_private *tp,
- void __iomem *ioaddr)
+ struct rtl8169_private *tp)
{
- if (tp->link_ok(ioaddr)) {
+ struct device *d = tp_to_dev(tp);
+
+ if (tp->link_ok(tp)) {
rtl_link_chg_patch(tp);
/* This is to cancel a scheduled suspend if there's one. */
- pm_request_resume(&tp->pci_dev->dev);
+ pm_request_resume(d);
netif_carrier_on(dev);
if (net_ratelimit())
netif_info(tp, ifup, dev, "link up\n");
} else {
netif_carrier_off(dev);
netif_info(tp, ifdown, dev, "link down\n");
- pm_runtime_idle(&tp->pci_dev->dev);
+ pm_runtime_idle(d);
}
}
@@ -1697,15 +1634,14 @@ static void rtl8169_check_link_status(struct net_device *dev,
static u32 __rtl8169_get_wol(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
u8 options;
u32 wolopts = 0;
- options = RTL_R8(Config1);
+ options = RTL_R8(tp, Config1);
if (!(options & PMEnable))
return 0;
- options = RTL_R8(Config3);
+ options = RTL_R8(tp, Config3);
if (options & LinkUp)
wolopts |= WAKE_PHY;
switch (tp->mac_version) {
@@ -1735,7 +1671,7 @@ static u32 __rtl8169_get_wol(struct rtl8169_private *tp)
break;
}
- options = RTL_R8(Config5);
+ options = RTL_R8(tp, Config5);
if (options & UWF)
wolopts |= WAKE_UCAST;
if (options & BWF)
@@ -1749,7 +1685,7 @@ static u32 __rtl8169_get_wol(struct rtl8169_private *tp)
static void rtl8169_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
struct rtl8169_private *tp = netdev_priv(dev);
- struct device *d = &tp->pci_dev->dev;
+ struct device *d = tp_to_dev(tp);
pm_runtime_get_noresume(d);
@@ -1768,7 +1704,6 @@ static void rtl8169_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts)
{
- void __iomem *ioaddr = tp->mmio_addr;
unsigned int i, tmp;
static const struct {
u32 opt;
@@ -1784,7 +1719,7 @@ static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts)
};
u8 options;
- RTL_W8(Cfg9346, Cfg9346_Unlock);
+ RTL_W8(tp, Cfg9346, Cfg9346_Unlock);
switch (tp->mac_version) {
case RTL_GIGA_MAC_VER_34:
@@ -1826,43 +1761,39 @@ static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts)
}
for (i = 0; i < tmp; i++) {
- options = RTL_R8(cfg[i].reg) & ~cfg[i].mask;
+ options = RTL_R8(tp, cfg[i].reg) & ~cfg[i].mask;
if (wolopts & cfg[i].opt)
options |= cfg[i].mask;
- RTL_W8(cfg[i].reg, options);
+ RTL_W8(tp, cfg[i].reg, options);
}
switch (tp->mac_version) {
case RTL_GIGA_MAC_VER_01 ... RTL_GIGA_MAC_VER_17:
- options = RTL_R8(Config1) & ~PMEnable;
+ options = RTL_R8(tp, Config1) & ~PMEnable;
if (wolopts)
options |= PMEnable;
- RTL_W8(Config1, options);
+ RTL_W8(tp, Config1, options);
break;
default:
- options = RTL_R8(Config2) & ~PME_SIGNAL;
+ options = RTL_R8(tp, Config2) & ~PME_SIGNAL;
if (wolopts)
options |= PME_SIGNAL;
- RTL_W8(Config2, options);
+ RTL_W8(tp, Config2, options);
break;
}
- RTL_W8(Cfg9346, Cfg9346_Lock);
+ RTL_W8(tp, Cfg9346, Cfg9346_Lock);
}
static int rtl8169_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
struct rtl8169_private *tp = netdev_priv(dev);
- struct device *d = &tp->pci_dev->dev;
+ struct device *d = tp_to_dev(tp);
pm_runtime_get_noresume(d);
rtl_lock_work(tp);
- if (wol->wolopts)
- tp->features |= RTL_FEATURE_WOL;
- else
- tp->features &= ~RTL_FEATURE_WOL;
if (pm_runtime_active(d))
__rtl8169_set_wol(tp, wol->wolopts);
else
@@ -1870,7 +1801,7 @@ static int rtl8169_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
rtl_unlock_work(tp);
- device_set_wakeup_enable(&tp->pci_dev->dev, wol->wolopts);
+ device_set_wakeup_enable(d, wol->wolopts);
pm_runtime_put_noidle(d);
@@ -1906,16 +1837,15 @@ static int rtl8169_set_speed_tbi(struct net_device *dev,
u8 autoneg, u16 speed, u8 duplex, u32 ignored)
{
struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
int ret = 0;
u32 reg;
- reg = RTL_R32(TBICSR);
+ reg = RTL_R32(tp, TBICSR);
if ((autoneg == AUTONEG_DISABLE) && (speed == SPEED_1000) &&
(duplex == DUPLEX_FULL)) {
- RTL_W32(TBICSR, reg & ~(TBINwEnable | TBINwRestart));
+ RTL_W32(tp, TBICSR, reg & ~(TBINwEnable | TBINwRestart));
} else if (autoneg == AUTONEG_ENABLE)
- RTL_W32(TBICSR, reg | TBINwEnable | TBINwRestart);
+ RTL_W32(tp, TBICSR, reg | TBINwEnable | TBINwRestart);
else {
netif_warn(tp, link, dev,
"incorrect speed setting refused in TBI mode\n");
@@ -2040,16 +1970,15 @@ static void __rtl8169_set_features(struct net_device *dev,
netdev_features_t features)
{
struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
u32 rx_config;
- rx_config = RTL_R32(RxConfig);
+ rx_config = RTL_R32(tp, RxConfig);
if (features & NETIF_F_RXALL)
rx_config |= (AcceptErr | AcceptRunt);
else
rx_config &= ~(AcceptErr | AcceptRunt);
- RTL_W32(RxConfig, rx_config);
+ RTL_W32(tp, RxConfig, rx_config);
if (features & NETIF_F_RXCSUM)
tp->cp_cmd |= RxChkSum;
@@ -2061,10 +1990,10 @@ static void __rtl8169_set_features(struct net_device *dev,
else
tp->cp_cmd &= ~RxVlan;
- tp->cp_cmd |= RTL_R16(CPlusCmd) & ~(RxVlan | RxChkSum);
+ tp->cp_cmd |= RTL_R16(tp, CPlusCmd) & ~(RxVlan | RxChkSum);
- RTL_W16(CPlusCmd, tp->cp_cmd);
- RTL_R16(CPlusCmd);
+ RTL_W16(tp, CPlusCmd, tp->cp_cmd);
+ RTL_R16(tp, CPlusCmd);
}
static int rtl8169_set_features(struct net_device *dev,
@@ -2101,7 +2030,6 @@ static int rtl8169_get_link_ksettings_tbi(struct net_device *dev,
struct ethtool_link_ksettings *cmd)
{
struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
u32 status;
u32 supported, advertising;
@@ -2109,7 +2037,7 @@ static int rtl8169_get_link_ksettings_tbi(struct net_device *dev,
SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_FIBRE;
cmd->base.port = PORT_FIBRE;
- status = RTL_R32(TBICSR);
+ status = RTL_R32(tp, TBICSR);
advertising = (status & TBINwEnable) ? ADVERTISED_Autoneg : 0;
cmd->base.autoneg = !!(status & TBINwEnable);
@@ -2224,23 +2152,20 @@ static int rtl8169_get_sset_count(struct net_device *dev, int sset)
DECLARE_RTL_COND(rtl_counters_cond)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- return RTL_R32(CounterAddrLow) & (CounterReset | CounterDump);
+ return RTL_R32(tp, CounterAddrLow) & (CounterReset | CounterDump);
}
static bool rtl8169_do_counters(struct net_device *dev, u32 counter_cmd)
{
struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
dma_addr_t paddr = tp->counters_phys_addr;
u32 cmd;
- RTL_W32(CounterAddrHigh, (u64)paddr >> 32);
- RTL_R32(CounterAddrHigh);
+ RTL_W32(tp, CounterAddrHigh, (u64)paddr >> 32);
+ RTL_R32(tp, CounterAddrHigh);
cmd = (u64)paddr & DMA_BIT_MASK(32);
- RTL_W32(CounterAddrLow, cmd);
- RTL_W32(CounterAddrLow, cmd | counter_cmd);
+ RTL_W32(tp, CounterAddrLow, cmd);
+ RTL_W32(tp, CounterAddrLow, cmd | counter_cmd);
return rtl_udelay_loop_wait_low(tp, &rtl_counters_cond, 10, 1000);
}
@@ -2262,13 +2187,12 @@ static bool rtl8169_reset_counters(struct net_device *dev)
static bool rtl8169_update_counters(struct net_device *dev)
{
struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
/*
* Some chips are unable to dump tally counters when the receiver
* is disabled.
*/
- if ((RTL_R8(ChipCmd) & CmdRxEnb) == 0)
+ if ((RTL_R8(tp, ChipCmd) & CmdRxEnb) == 0)
return true;
return rtl8169_do_counters(dev, CounterDump);
@@ -2317,7 +2241,7 @@ static void rtl8169_get_ethtool_stats(struct net_device *dev,
struct ethtool_stats *stats, u64 *data)
{
struct rtl8169_private *tp = netdev_priv(dev);
- struct device *d = &tp->pci_dev->dev;
+ struct device *d = tp_to_dev(tp);
struct rtl8169_counters *counters = tp->counters;
ASSERT_RTNL();
@@ -2448,7 +2372,6 @@ static const struct rtl_coalesce_info *rtl_coalesce_info(struct net_device *dev)
static int rtl_get_coalesce(struct net_device *dev, struct ethtool_coalesce *ec)
{
struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
const struct rtl_coalesce_info *ci;
const struct rtl_coalesce_scale *scale;
struct {
@@ -2468,10 +2391,10 @@ static int rtl_get_coalesce(struct net_device *dev, struct ethtool_coalesce *ec)
if (IS_ERR(ci))
return PTR_ERR(ci);
- scale = &ci->scalev[RTL_R16(CPlusCmd) & 3];
+ scale = &ci->scalev[RTL_R16(tp, CPlusCmd) & 3];
/* read IntrMitigate and adjust according to scale */
- for (w = RTL_R16(IntrMitigate); w; w >>= RTL_COALESCE_SHIFT, p++) {
+ for (w = RTL_R16(tp, IntrMitigate); w; w >>= RTL_COALESCE_SHIFT, p++) {
*p->max_frames = (w & RTL_COALESCE_MASK) << 2;
w >>= RTL_COALESCE_SHIFT;
*p->usecs = w & RTL_COALESCE_MASK;
@@ -2518,7 +2441,6 @@ static const struct rtl_coalesce_scale *rtl_coalesce_choose_scale(
static int rtl_set_coalesce(struct net_device *dev, struct ethtool_coalesce *ec)
{
struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
const struct rtl_coalesce_scale *scale;
struct {
u32 frames;
@@ -2566,11 +2488,11 @@ static int rtl_set_coalesce(struct net_device *dev, struct ethtool_coalesce *ec)
rtl_lock_work(tp);
- RTL_W16(IntrMitigate, swab16(w));
+ RTL_W16(tp, IntrMitigate, swab16(w));
tp->cp_cmd = (tp->cp_cmd & ~3) | cp01;
- RTL_W16(CPlusCmd, tp->cp_cmd);
- RTL_R16(CPlusCmd);
+ RTL_W16(tp, CPlusCmd, tp->cp_cmd);
+ RTL_R16(tp, CPlusCmd);
rtl_unlock_work(tp);
@@ -2600,17 +2522,16 @@ static const struct ethtool_ops rtl8169_ethtool_ops = {
static void rtl8169_get_mac_version(struct rtl8169_private *tp,
struct net_device *dev, u8 default_version)
{
- void __iomem *ioaddr = tp->mmio_addr;
/*
* The driver currently handles the 8168Bf and the 8168Be identically
* but they can be identified more specifically through the test below
* if needed:
*
- * (RTL_R32(TxConfig) & 0x700000) == 0x500000 ? 8168Bf : 8168Be
+ * (RTL_R32(tp, TxConfig) & 0x700000) == 0x500000 ? 8168Bf : 8168Be
*
* Same thing for the 8101Eb and the 8101Ec:
*
- * (RTL_R32(TxConfig) & 0x700000) == 0x200000 ? 8101Eb : 8101Ec
+ * (RTL_R32(tp, TxConfig) & 0x700000) == 0x200000 ? 8101Eb : 8101Ec
*/
static const struct rtl_mac_info {
u32 mask;
@@ -2708,7 +2629,7 @@ static void rtl8169_get_mac_version(struct rtl8169_private *tp,
const struct rtl_mac_info *p = mac_info;
u32 reg;
- reg = RTL_R32(TxConfig);
+ reg = RTL_R32(tp, TxConfig);
while ((reg & p->mask) != p->val)
p++;
tp->mac_version = p->mac_version;
@@ -3805,8 +3726,6 @@ static void rtl8168e_2_hw_phy_config(struct rtl8169_private *tp)
rtl_writephy(tp, 0x1f, 0x0005);
rtl_w0w1_phy(tp, 0x01, 0x0100, 0x0000);
rtl_writephy(tp, 0x1f, 0x0000);
- /* soft-reset phy */
- rtl_writephy(tp, MII_BMCR, BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART);
/* Broken BIOS workaround: feed GigaMAC registers with MAC address. */
rtl_rar_exgmac_set(tp, tp->dev->dev_addr);
@@ -4591,7 +4510,6 @@ static void rtl_hw_phy_config(struct net_device *dev)
static void rtl_phy_work(struct rtl8169_private *tp)
{
struct timer_list *timer = &tp->timer;
- void __iomem *ioaddr = tp->mmio_addr;
unsigned long timeout = RTL8169_PHY_TIMEOUT;
assert(tp->mac_version > RTL_GIGA_MAC_VER_01);
@@ -4605,7 +4523,7 @@ static void rtl_phy_work(struct rtl8169_private *tp)
goto out_mod_timer;
}
- if (tp->link_ok(ioaddr))
+ if (tp->link_ok(tp))
return;
netif_dbg(tp, link, tp->dev, "PHY reset until link up\n");
@@ -4643,21 +4561,17 @@ static void rtl8169_phy_reset(struct net_device *dev,
static bool rtl_tbi_enabled(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
return (tp->mac_version == RTL_GIGA_MAC_VER_01) &&
- (RTL_R8(PHYstatus) & TBI_Enable);
+ (RTL_R8(tp, PHYstatus) & TBI_Enable);
}
static void rtl8169_init_phy(struct net_device *dev, struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
rtl_hw_phy_config(dev);
if (tp->mac_version <= RTL_GIGA_MAC_VER_06) {
dprintk("Set MAC Reg C+CR Offset 0x82h = 0x01h\n");
- RTL_W8(0x82, 0x01);
+ RTL_W8(tp, 0x82, 0x01);
}
pci_write_config_byte(tp->pci_dev, PCI_LATENCY_TIMER, 0x40);
@@ -4667,7 +4581,7 @@ static void rtl8169_init_phy(struct net_device *dev, struct rtl8169_private *tp)
if (tp->mac_version == RTL_GIGA_MAC_VER_02) {
dprintk("Set MAC Reg C+CR Offset 0x82h = 0x01h\n");
- RTL_W8(0x82, 0x01);
+ RTL_W8(tp, 0x82, 0x01);
dprintk("Set PHY Reg 0x0bh = 0x00h\n");
rtl_writephy(tp, 0x0b, 0x0000); //w 0x0b 15 0 0
}
@@ -4687,22 +4601,20 @@ static void rtl8169_init_phy(struct net_device *dev, struct rtl8169_private *tp)
static void rtl_rar_set(struct rtl8169_private *tp, u8 *addr)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
rtl_lock_work(tp);
- RTL_W8(Cfg9346, Cfg9346_Unlock);
+ RTL_W8(tp, Cfg9346, Cfg9346_Unlock);
- RTL_W32(MAC4, addr[4] | addr[5] << 8);
- RTL_R32(MAC4);
+ RTL_W32(tp, MAC4, addr[4] | addr[5] << 8);
+ RTL_R32(tp, MAC4);
- RTL_W32(MAC0, addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24);
- RTL_R32(MAC0);
+ RTL_W32(tp, MAC0, addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24);
+ RTL_R32(tp, MAC0);
if (tp->mac_version == RTL_GIGA_MAC_VER_34)
rtl_rar_exgmac_set(tp, addr);
- RTL_W8(Cfg9346, Cfg9346_Lock);
+ RTL_W8(tp, Cfg9346, Cfg9346_Lock);
rtl_unlock_work(tp);
}
@@ -4710,13 +4622,12 @@ static void rtl_rar_set(struct rtl8169_private *tp, u8 *addr)
static int rtl_set_mac_address(struct net_device *dev, void *p)
{
struct rtl8169_private *tp = netdev_priv(dev);
- struct device *d = &tp->pci_dev->dev;
- struct sockaddr *addr = p;
-
- if (!is_valid_ether_addr(addr->sa_data))
- return -EADDRNOTAVAIL;
+ struct device *d = tp_to_dev(tp);
+ int ret;
- memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+ ret = eth_mac_addr(dev, p);
+ if (ret)
+ return ret;
pm_runtime_get_noresume(d);
@@ -4822,8 +4733,6 @@ static void rtl_speed_down(struct rtl8169_private *tp)
static void rtl_wol_suspend_quirk(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
switch (tp->mac_version) {
case RTL_GIGA_MAC_VER_25:
case RTL_GIGA_MAC_VER_26:
@@ -4847,7 +4756,7 @@ static void rtl_wol_suspend_quirk(struct rtl8169_private *tp)
case RTL_GIGA_MAC_VER_49:
case RTL_GIGA_MAC_VER_50:
case RTL_GIGA_MAC_VER_51:
- RTL_W32(RxConfig, RTL_R32(RxConfig) |
+ RTL_W32(tp, RxConfig, RTL_R32(tp, RxConfig) |
AcceptBroadcast | AcceptMulticast | AcceptMyPhys);
break;
default:
@@ -4880,8 +4789,6 @@ static void r810x_phy_power_up(struct rtl8169_private *tp)
static void r810x_pll_power_down(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
if (rtl_wol_pll_power_down(tp))
return;
@@ -4896,15 +4803,13 @@ static void r810x_pll_power_down(struct rtl8169_private *tp)
case RTL_GIGA_MAC_VER_16:
break;
default:
- RTL_W8(PMCH, RTL_R8(PMCH) & ~0x80);
+ RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) & ~0x80);
break;
}
}
static void r810x_pll_power_up(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
r810x_phy_power_up(tp);
switch (tp->mac_version) {
@@ -4917,10 +4822,10 @@ static void r810x_pll_power_up(struct rtl8169_private *tp)
break;
case RTL_GIGA_MAC_VER_47:
case RTL_GIGA_MAC_VER_48:
- RTL_W8(PMCH, RTL_R8(PMCH) | 0xc0);
+ RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) | 0xc0);
break;
default:
- RTL_W8(PMCH, RTL_R8(PMCH) | 0x80);
+ RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) | 0x80);
break;
}
}
@@ -4987,21 +4892,12 @@ static void r8168_phy_power_down(struct rtl8169_private *tp)
static void r8168_pll_power_down(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- if ((tp->mac_version == RTL_GIGA_MAC_VER_27 ||
- tp->mac_version == RTL_GIGA_MAC_VER_28 ||
- tp->mac_version == RTL_GIGA_MAC_VER_31 ||
- tp->mac_version == RTL_GIGA_MAC_VER_49 ||
- tp->mac_version == RTL_GIGA_MAC_VER_50 ||
- tp->mac_version == RTL_GIGA_MAC_VER_51) &&
- r8168_check_dash(tp)) {
+ if (r8168_check_dash(tp))
return;
- }
if ((tp->mac_version == RTL_GIGA_MAC_VER_23 ||
tp->mac_version == RTL_GIGA_MAC_VER_24) &&
- (RTL_R16(CPlusCmd) & ASF)) {
+ (RTL_R16(tp, CPlusCmd) & ASF)) {
return;
}
@@ -5027,22 +4923,20 @@ static void r8168_pll_power_down(struct rtl8169_private *tp)
case RTL_GIGA_MAC_VER_46:
case RTL_GIGA_MAC_VER_50:
case RTL_GIGA_MAC_VER_51:
- RTL_W8(PMCH, RTL_R8(PMCH) & ~0x80);
+ RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) & ~0x80);
break;
case RTL_GIGA_MAC_VER_40:
case RTL_GIGA_MAC_VER_41:
case RTL_GIGA_MAC_VER_49:
rtl_w0w1_eri(tp, 0x1a8, ERIAR_MASK_1111, 0x00000000,
0xfc000000, ERIAR_EXGMAC);
- RTL_W8(PMCH, RTL_R8(PMCH) & ~0x80);
+ RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) & ~0x80);
break;
}
}
static void r8168_pll_power_up(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
switch (tp->mac_version) {
case RTL_GIGA_MAC_VER_25:
case RTL_GIGA_MAC_VER_26:
@@ -5051,19 +4945,19 @@ static void r8168_pll_power_up(struct rtl8169_private *tp)
case RTL_GIGA_MAC_VER_31:
case RTL_GIGA_MAC_VER_32:
case RTL_GIGA_MAC_VER_33:
- RTL_W8(PMCH, RTL_R8(PMCH) | 0x80);
+ RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) | 0x80);
break;
case RTL_GIGA_MAC_VER_44:
case RTL_GIGA_MAC_VER_45:
case RTL_GIGA_MAC_VER_46:
case RTL_GIGA_MAC_VER_50:
case RTL_GIGA_MAC_VER_51:
- RTL_W8(PMCH, RTL_R8(PMCH) | 0xc0);
+ RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) | 0xc0);
break;
case RTL_GIGA_MAC_VER_40:
case RTL_GIGA_MAC_VER_41:
case RTL_GIGA_MAC_VER_49:
- RTL_W8(PMCH, RTL_R8(PMCH) | 0xc0);
+ RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) | 0xc0);
rtl_w0w1_eri(tp, 0x1a8, ERIAR_MASK_1111, 0xfc000000,
0x00000000, ERIAR_EXGMAC);
break;
@@ -5153,8 +5047,6 @@ static void rtl_init_pll_power_ops(struct rtl8169_private *tp)
static void rtl_init_rxcfg(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
switch (tp->mac_version) {
case RTL_GIGA_MAC_VER_01:
case RTL_GIGA_MAC_VER_02:
@@ -5170,7 +5062,7 @@ static void rtl_init_rxcfg(struct rtl8169_private *tp)
case RTL_GIGA_MAC_VER_15:
case RTL_GIGA_MAC_VER_16:
case RTL_GIGA_MAC_VER_17:
- RTL_W32(RxConfig, RX_FIFO_THRESH | RX_DMA_BURST);
+ RTL_W32(tp, RxConfig, RX_FIFO_THRESH | RX_DMA_BURST);
break;
case RTL_GIGA_MAC_VER_18:
case RTL_GIGA_MAC_VER_19:
@@ -5181,7 +5073,7 @@ static void rtl_init_rxcfg(struct rtl8169_private *tp)
case RTL_GIGA_MAC_VER_24:
case RTL_GIGA_MAC_VER_34:
case RTL_GIGA_MAC_VER_35:
- RTL_W32(RxConfig, RX128_INT_EN | RX_MULTI_EN | RX_DMA_BURST);
+ RTL_W32(tp, RxConfig, RX128_INT_EN | RX_MULTI_EN | RX_DMA_BURST);
break;
case RTL_GIGA_MAC_VER_40:
case RTL_GIGA_MAC_VER_41:
@@ -5195,10 +5087,10 @@ static void rtl_init_rxcfg(struct rtl8169_private *tp)
case RTL_GIGA_MAC_VER_49:
case RTL_GIGA_MAC_VER_50:
case RTL_GIGA_MAC_VER_51:
- RTL_W32(RxConfig, RX128_INT_EN | RX_MULTI_EN | RX_DMA_BURST | RX_EARLY_OFF);
+ RTL_W32(tp, RxConfig, RX128_INT_EN | RX_MULTI_EN | RX_DMA_BURST | RX_EARLY_OFF);
break;
default:
- RTL_W32(RxConfig, RX128_INT_EN | RX_DMA_BURST);
+ RTL_W32(tp, RxConfig, RX128_INT_EN | RX_DMA_BURST);
break;
}
}
@@ -5210,102 +5102,82 @@ static void rtl8169_init_ring_indexes(struct rtl8169_private *tp)
static void rtl_hw_jumbo_enable(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W8(Cfg9346, Cfg9346_Unlock);
+ RTL_W8(tp, Cfg9346, Cfg9346_Unlock);
rtl_generic_op(tp, tp->jumbo_ops.enable);
- RTL_W8(Cfg9346, Cfg9346_Lock);
+ RTL_W8(tp, Cfg9346, Cfg9346_Lock);
}
static void rtl_hw_jumbo_disable(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W8(Cfg9346, Cfg9346_Unlock);
+ RTL_W8(tp, Cfg9346, Cfg9346_Unlock);
rtl_generic_op(tp, tp->jumbo_ops.disable);
- RTL_W8(Cfg9346, Cfg9346_Lock);
+ RTL_W8(tp, Cfg9346, Cfg9346_Lock);
}
static void r8168c_hw_jumbo_enable(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W8(Config3, RTL_R8(Config3) | Jumbo_En0);
- RTL_W8(Config4, RTL_R8(Config4) | Jumbo_En1);
- rtl_tx_performance_tweak(tp->pci_dev, PCI_EXP_DEVCTL_READRQ_512B);
+ RTL_W8(tp, Config3, RTL_R8(tp, Config3) | Jumbo_En0);
+ RTL_W8(tp, Config4, RTL_R8(tp, Config4) | Jumbo_En1);
+ rtl_tx_performance_tweak(tp, PCI_EXP_DEVCTL_READRQ_512B);
}
static void r8168c_hw_jumbo_disable(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W8(Config3, RTL_R8(Config3) & ~Jumbo_En0);
- RTL_W8(Config4, RTL_R8(Config4) & ~Jumbo_En1);
- rtl_tx_performance_tweak(tp->pci_dev, 0x5 << MAX_READ_REQUEST_SHIFT);
+ RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Jumbo_En0);
+ RTL_W8(tp, Config4, RTL_R8(tp, Config4) & ~Jumbo_En1);
+ rtl_tx_performance_tweak(tp, 0x5 << MAX_READ_REQUEST_SHIFT);
}
static void r8168dp_hw_jumbo_enable(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W8(Config3, RTL_R8(Config3) | Jumbo_En0);
+ RTL_W8(tp, Config3, RTL_R8(tp, Config3) | Jumbo_En0);
}
static void r8168dp_hw_jumbo_disable(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W8(Config3, RTL_R8(Config3) & ~Jumbo_En0);
+ RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Jumbo_En0);
}
static void r8168e_hw_jumbo_enable(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W8(MaxTxPacketSize, 0x3f);
- RTL_W8(Config3, RTL_R8(Config3) | Jumbo_En0);
- RTL_W8(Config4, RTL_R8(Config4) | 0x01);
- rtl_tx_performance_tweak(tp->pci_dev, PCI_EXP_DEVCTL_READRQ_512B);
+ RTL_W8(tp, MaxTxPacketSize, 0x3f);
+ RTL_W8(tp, Config3, RTL_R8(tp, Config3) | Jumbo_En0);
+ RTL_W8(tp, Config4, RTL_R8(tp, Config4) | 0x01);
+ rtl_tx_performance_tweak(tp, PCI_EXP_DEVCTL_READRQ_512B);
}
static void r8168e_hw_jumbo_disable(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W8(MaxTxPacketSize, 0x0c);
- RTL_W8(Config3, RTL_R8(Config3) & ~Jumbo_En0);
- RTL_W8(Config4, RTL_R8(Config4) & ~0x01);
- rtl_tx_performance_tweak(tp->pci_dev, 0x5 << MAX_READ_REQUEST_SHIFT);
+ RTL_W8(tp, MaxTxPacketSize, 0x0c);
+ RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Jumbo_En0);
+ RTL_W8(tp, Config4, RTL_R8(tp, Config4) & ~0x01);
+ rtl_tx_performance_tweak(tp, 0x5 << MAX_READ_REQUEST_SHIFT);
}
static void r8168b_0_hw_jumbo_enable(struct rtl8169_private *tp)
{
- rtl_tx_performance_tweak(tp->pci_dev,
+ rtl_tx_performance_tweak(tp,
PCI_EXP_DEVCTL_READRQ_512B | PCI_EXP_DEVCTL_NOSNOOP_EN);
}
static void r8168b_0_hw_jumbo_disable(struct rtl8169_private *tp)
{
- rtl_tx_performance_tweak(tp->pci_dev,
+ rtl_tx_performance_tweak(tp,
(0x5 << MAX_READ_REQUEST_SHIFT) | PCI_EXP_DEVCTL_NOSNOOP_EN);
}
static void r8168b_1_hw_jumbo_enable(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
r8168b_0_hw_jumbo_enable(tp);
- RTL_W8(Config4, RTL_R8(Config4) | (1 << 0));
+ RTL_W8(tp, Config4, RTL_R8(tp, Config4) | (1 << 0));
}
static void r8168b_1_hw_jumbo_disable(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
r8168b_0_hw_jumbo_disable(tp);
- RTL_W8(Config4, RTL_R8(Config4) & ~(1 << 0));
+ RTL_W8(tp, Config4, RTL_R8(tp, Config4) & ~(1 << 0));
}
static void rtl_init_jumbo_ops(struct rtl8169_private *tp)
@@ -5372,16 +5244,12 @@ static void rtl_init_jumbo_ops(struct rtl8169_private *tp)
DECLARE_RTL_COND(rtl_chipcmd_cond)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- return RTL_R8(ChipCmd) & CmdReset;
+ return RTL_R8(tp, ChipCmd) & CmdReset;
}
static void rtl_hw_reset(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W8(ChipCmd, CmdReset);
+ RTL_W8(tp, ChipCmd, CmdReset);
rtl_udelay_loop_wait_low(tp, &rtl_chipcmd_cond, 100, 100);
}
@@ -5400,7 +5268,7 @@ static void rtl_request_uncached_firmware(struct rtl8169_private *tp)
if (!rtl_fw)
goto err_warn;
- rc = request_firmware(&rtl_fw->fw, name, &tp->pci_dev->dev);
+ rc = request_firmware(&rtl_fw->fw, name, tp_to_dev(tp));
if (rc < 0)
goto err_free;
@@ -5432,29 +5300,21 @@ static void rtl_request_firmware(struct rtl8169_private *tp)
static void rtl_rx_close(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W32(RxConfig, RTL_R32(RxConfig) & ~RX_CONFIG_ACCEPT_MASK);
+ RTL_W32(tp, RxConfig, RTL_R32(tp, RxConfig) & ~RX_CONFIG_ACCEPT_MASK);
}
DECLARE_RTL_COND(rtl_npq_cond)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- return RTL_R8(TxPoll) & NPQ;
+ return RTL_R8(tp, TxPoll) & NPQ;
}
DECLARE_RTL_COND(rtl_txcfg_empty_cond)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- return RTL_R32(TxConfig) & TXCFG_EMPTY;
+ return RTL_R32(tp, TxConfig) & TXCFG_EMPTY;
}
static void rtl8169_hw_reset(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
/* Disable interrupts */
rtl8169_irq_mask_and_ack(tp);
@@ -5481,10 +5341,10 @@ static void rtl8169_hw_reset(struct rtl8169_private *tp)
tp->mac_version == RTL_GIGA_MAC_VER_49 ||
tp->mac_version == RTL_GIGA_MAC_VER_50 ||
tp->mac_version == RTL_GIGA_MAC_VER_51) {
- RTL_W8(ChipCmd, RTL_R8(ChipCmd) | StopReq);
+ RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) | StopReq);
rtl_udelay_loop_wait_high(tp, &rtl_txcfg_empty_cond, 100, 666);
} else {
- RTL_W8(ChipCmd, RTL_R8(ChipCmd) | StopReq);
+ RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) | StopReq);
udelay(100);
}
@@ -5493,10 +5353,8 @@ static void rtl8169_hw_reset(struct rtl8169_private *tp)
static void rtl_set_rx_tx_config_registers(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
/* Set DMA burst size and Interframe Gap Time */
- RTL_W32(TxConfig, (TX_DMA_BURST << TxDMAShift) |
+ RTL_W32(tp, TxConfig, (TX_DMA_BURST << TxDMAShift) |
(InterFrameGap << TxInterFrameGapShift));
}
@@ -5509,36 +5367,35 @@ static void rtl_hw_start(struct net_device *dev)
rtl_irq_enable_all(tp);
}
-static void rtl_set_rx_tx_desc_registers(struct rtl8169_private *tp,
- void __iomem *ioaddr)
+static void rtl_set_rx_tx_desc_registers(struct rtl8169_private *tp)
{
/*
* Magic spell: some iop3xx ARM board needs the TxDescAddrHigh
* register to be written before TxDescAddrLow to work.
* Switching from MMIO to I/O access fixes the issue as well.
*/
- RTL_W32(TxDescStartAddrHigh, ((u64) tp->TxPhyAddr) >> 32);
- RTL_W32(TxDescStartAddrLow, ((u64) tp->TxPhyAddr) & DMA_BIT_MASK(32));
- RTL_W32(RxDescAddrHigh, ((u64) tp->RxPhyAddr) >> 32);
- RTL_W32(RxDescAddrLow, ((u64) tp->RxPhyAddr) & DMA_BIT_MASK(32));
+ RTL_W32(tp, TxDescStartAddrHigh, ((u64) tp->TxPhyAddr) >> 32);
+ RTL_W32(tp, TxDescStartAddrLow, ((u64) tp->TxPhyAddr) & DMA_BIT_MASK(32));
+ RTL_W32(tp, RxDescAddrHigh, ((u64) tp->RxPhyAddr) >> 32);
+ RTL_W32(tp, RxDescAddrLow, ((u64) tp->RxPhyAddr) & DMA_BIT_MASK(32));
}
-static u16 rtl_rw_cpluscmd(void __iomem *ioaddr)
+static u16 rtl_rw_cpluscmd(struct rtl8169_private *tp)
{
u16 cmd;
- cmd = RTL_R16(CPlusCmd);
- RTL_W16(CPlusCmd, cmd);
+ cmd = RTL_R16(tp, CPlusCmd);
+ RTL_W16(tp, CPlusCmd, cmd);
return cmd;
}
-static void rtl_set_rx_max_size(void __iomem *ioaddr, unsigned int rx_buf_sz)
+static void rtl_set_rx_max_size(struct rtl8169_private *tp, unsigned int rx_buf_sz)
{
/* Low hurts. Let's disable the filtering. */
- RTL_W16(RxMaxSize, rx_buf_sz + 1);
+ RTL_W16(tp, RxMaxSize, rx_buf_sz + 1);
}
-static void rtl8169_set_magic_reg(void __iomem *ioaddr, unsigned mac_version)
+static void rtl8169_set_magic_reg(struct rtl8169_private *tp, unsigned mac_version)
{
static const struct rtl_cfg2_info {
u32 mac_version;
@@ -5554,10 +5411,10 @@ static void rtl8169_set_magic_reg(void __iomem *ioaddr, unsigned mac_version)
unsigned int i;
u32 clk;
- clk = RTL_R8(Config2) & PCI_Clock_66MHz;
+ clk = RTL_R8(tp, Config2) & PCI_Clock_66MHz;
for (i = 0; i < ARRAY_SIZE(cfg2_info); i++, p++) {
if ((p->mac_version == mac_version) && (p->clk == clk)) {
- RTL_W32(0x7c, p->val);
+ RTL_W32(tp, 0x7c, p->val);
break;
}
}
@@ -5566,7 +5423,6 @@ static void rtl8169_set_magic_reg(void __iomem *ioaddr, unsigned mac_version)
static void rtl_set_rx_mode(struct net_device *dev)
{
struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
u32 mc_filter[2]; /* Multicast hash filter */
int rx_mode;
u32 tmp = 0;
@@ -5598,7 +5454,7 @@ static void rtl_set_rx_mode(struct net_device *dev)
if (dev->features & NETIF_F_RXALL)
rx_mode |= (AcceptErr | AcceptRunt);
- tmp = (RTL_R32(RxConfig) & ~RX_CONFIG_ACCEPT_MASK) | rx_mode;
+ tmp = (RTL_R32(tp, RxConfig) & ~RX_CONFIG_ACCEPT_MASK) | rx_mode;
if (tp->mac_version > RTL_GIGA_MAC_VER_06) {
u32 data = mc_filter[0];
@@ -5610,35 +5466,34 @@ static void rtl_set_rx_mode(struct net_device *dev)
if (tp->mac_version == RTL_GIGA_MAC_VER_35)
mc_filter[1] = mc_filter[0] = 0xffffffff;
- RTL_W32(MAR0 + 4, mc_filter[1]);
- RTL_W32(MAR0 + 0, mc_filter[0]);
+ RTL_W32(tp, MAR0 + 4, mc_filter[1]);
+ RTL_W32(tp, MAR0 + 0, mc_filter[0]);
- RTL_W32(RxConfig, tmp);
+ RTL_W32(tp, RxConfig, tmp);
}
static void rtl_hw_start_8169(struct net_device *dev)
{
struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
struct pci_dev *pdev = tp->pci_dev;
if (tp->mac_version == RTL_GIGA_MAC_VER_05) {
- RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) | PCIMulRW);
+ RTL_W16(tp, CPlusCmd, RTL_R16(tp, CPlusCmd) | PCIMulRW);
pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 0x08);
}
- RTL_W8(Cfg9346, Cfg9346_Unlock);
+ RTL_W8(tp, Cfg9346, Cfg9346_Unlock);
if (tp->mac_version == RTL_GIGA_MAC_VER_01 ||
tp->mac_version == RTL_GIGA_MAC_VER_02 ||
tp->mac_version == RTL_GIGA_MAC_VER_03 ||
tp->mac_version == RTL_GIGA_MAC_VER_04)
- RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb);
+ RTL_W8(tp, ChipCmd, CmdTxEnb | CmdRxEnb);
rtl_init_rxcfg(tp);
- RTL_W8(EarlyTxThres, NoEarlyTx);
+ RTL_W8(tp, EarlyTxThres, NoEarlyTx);
- rtl_set_rx_max_size(ioaddr, rx_buf_sz);
+ rtl_set_rx_max_size(tp, rx_buf_sz);
if (tp->mac_version == RTL_GIGA_MAC_VER_01 ||
tp->mac_version == RTL_GIGA_MAC_VER_02 ||
@@ -5646,7 +5501,7 @@ static void rtl_hw_start_8169(struct net_device *dev)
tp->mac_version == RTL_GIGA_MAC_VER_04)
rtl_set_rx_tx_config_registers(tp);
- tp->cp_cmd |= rtl_rw_cpluscmd(ioaddr) | PCIMulRW;
+ tp->cp_cmd |= rtl_rw_cpluscmd(tp) | PCIMulRW;
if (tp->mac_version == RTL_GIGA_MAC_VER_02 ||
tp->mac_version == RTL_GIGA_MAC_VER_03) {
@@ -5655,37 +5510,37 @@ static void rtl_hw_start_8169(struct net_device *dev)
tp->cp_cmd |= (1 << 14);
}
- RTL_W16(CPlusCmd, tp->cp_cmd);
+ RTL_W16(tp, CPlusCmd, tp->cp_cmd);
- rtl8169_set_magic_reg(ioaddr, tp->mac_version);
+ rtl8169_set_magic_reg(tp, tp->mac_version);
/*
* Undocumented corner. Supposedly:
* (TxTimer << 12) | (TxPackets << 8) | (RxTimer << 4) | RxPackets
*/
- RTL_W16(IntrMitigate, 0x0000);
+ RTL_W16(tp, IntrMitigate, 0x0000);
- rtl_set_rx_tx_desc_registers(tp, ioaddr);
+ rtl_set_rx_tx_desc_registers(tp);
if (tp->mac_version != RTL_GIGA_MAC_VER_01 &&
tp->mac_version != RTL_GIGA_MAC_VER_02 &&
tp->mac_version != RTL_GIGA_MAC_VER_03 &&
tp->mac_version != RTL_GIGA_MAC_VER_04) {
- RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb);
+ RTL_W8(tp, ChipCmd, CmdTxEnb | CmdRxEnb);
rtl_set_rx_tx_config_registers(tp);
}
- RTL_W8(Cfg9346, Cfg9346_Lock);
+ RTL_W8(tp, Cfg9346, Cfg9346_Lock);
/* Initially a 10 us delay. Turned it into a PCI commit. - FR */
- RTL_R8(IntrMask);
+ RTL_R8(tp, IntrMask);
- RTL_W32(RxMissed, 0);
+ RTL_W32(tp, RxMissed, 0);
rtl_set_rx_mode(dev);
/* no early-rx interrupts */
- RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xf000);
+ RTL_W16(tp, MultiIntr, RTL_R16(tp, MultiIntr) & 0xf000);
}
static void rtl_csi_write(struct rtl8169_private *tp, int addr, int value)
@@ -5719,17 +5574,13 @@ static void rtl_csi_access_enable_2(struct rtl8169_private *tp)
DECLARE_RTL_COND(rtl_csiar_cond)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- return RTL_R32(CSIAR) & CSIAR_FLAG;
+ return RTL_R32(tp, CSIAR) & CSIAR_FLAG;
}
static void r8169_csi_write(struct rtl8169_private *tp, int addr, int value)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W32(CSIDR, value);
- RTL_W32(CSIAR, CSIAR_WRITE_CMD | (addr & CSIAR_ADDR_MASK) |
+ RTL_W32(tp, CSIDR, value);
+ RTL_W32(tp, CSIAR, CSIAR_WRITE_CMD | (addr & CSIAR_ADDR_MASK) |
CSIAR_BYTE_ENABLE << CSIAR_BYTE_ENABLE_SHIFT);
rtl_udelay_loop_wait_low(tp, &rtl_csiar_cond, 10, 100);
@@ -5737,21 +5588,17 @@ static void r8169_csi_write(struct rtl8169_private *tp, int addr, int value)
static u32 r8169_csi_read(struct rtl8169_private *tp, int addr)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W32(CSIAR, (addr & CSIAR_ADDR_MASK) |
+ RTL_W32(tp, CSIAR, (addr & CSIAR_ADDR_MASK) |
CSIAR_BYTE_ENABLE << CSIAR_BYTE_ENABLE_SHIFT);
return rtl_udelay_loop_wait_high(tp, &rtl_csiar_cond, 10, 100) ?
- RTL_R32(CSIDR) : ~0;
+ RTL_R32(tp, CSIDR) : ~0;
}
static void r8402_csi_write(struct rtl8169_private *tp, int addr, int value)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W32(CSIDR, value);
- RTL_W32(CSIAR, CSIAR_WRITE_CMD | (addr & CSIAR_ADDR_MASK) |
+ RTL_W32(tp, CSIDR, value);
+ RTL_W32(tp, CSIAR, CSIAR_WRITE_CMD | (addr & CSIAR_ADDR_MASK) |
CSIAR_BYTE_ENABLE << CSIAR_BYTE_ENABLE_SHIFT |
CSIAR_FUNC_NIC);
@@ -5760,21 +5607,17 @@ static void r8402_csi_write(struct rtl8169_private *tp, int addr, int value)
static u32 r8402_csi_read(struct rtl8169_private *tp, int addr)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W32(CSIAR, (addr & CSIAR_ADDR_MASK) | CSIAR_FUNC_NIC |
+ RTL_W32(tp, CSIAR, (addr & CSIAR_ADDR_MASK) | CSIAR_FUNC_NIC |
CSIAR_BYTE_ENABLE << CSIAR_BYTE_ENABLE_SHIFT);
return rtl_udelay_loop_wait_high(tp, &rtl_csiar_cond, 10, 100) ?
- RTL_R32(CSIDR) : ~0;
+ RTL_R32(tp, CSIDR) : ~0;
}
static void r8411_csi_write(struct rtl8169_private *tp, int addr, int value)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W32(CSIDR, value);
- RTL_W32(CSIAR, CSIAR_WRITE_CMD | (addr & CSIAR_ADDR_MASK) |
+ RTL_W32(tp, CSIDR, value);
+ RTL_W32(tp, CSIAR, CSIAR_WRITE_CMD | (addr & CSIAR_ADDR_MASK) |
CSIAR_BYTE_ENABLE << CSIAR_BYTE_ENABLE_SHIFT |
CSIAR_FUNC_NIC2);
@@ -5783,13 +5626,11 @@ static void r8411_csi_write(struct rtl8169_private *tp, int addr, int value)
static u32 r8411_csi_read(struct rtl8169_private *tp, int addr)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W32(CSIAR, (addr & CSIAR_ADDR_MASK) | CSIAR_FUNC_NIC2 |
+ RTL_W32(tp, CSIAR, (addr & CSIAR_ADDR_MASK) | CSIAR_FUNC_NIC2 |
CSIAR_BYTE_ENABLE << CSIAR_BYTE_ENABLE_SHIFT);
return rtl_udelay_loop_wait_high(tp, &rtl_csiar_cond, 10, 100) ?
- RTL_R32(CSIDR) : ~0;
+ RTL_R32(tp, CSIDR) : ~0;
}
static void rtl_init_csi_ops(struct rtl8169_private *tp)
@@ -5851,31 +5692,30 @@ static void rtl_ephy_init(struct rtl8169_private *tp, const struct ephy_info *e,
}
}
-static void rtl_disable_clock_request(struct pci_dev *pdev)
+static void rtl_disable_clock_request(struct rtl8169_private *tp)
{
- pcie_capability_clear_word(pdev, PCI_EXP_LNKCTL,
+ pcie_capability_clear_word(tp->pci_dev, PCI_EXP_LNKCTL,
PCI_EXP_LNKCTL_CLKREQ_EN);
}
-static void rtl_enable_clock_request(struct pci_dev *pdev)
+static void rtl_enable_clock_request(struct rtl8169_private *tp)
{
- pcie_capability_set_word(pdev, PCI_EXP_LNKCTL,
+ pcie_capability_set_word(tp->pci_dev, PCI_EXP_LNKCTL,
PCI_EXP_LNKCTL_CLKREQ_EN);
}
static void rtl_pcie_state_l2l3_enable(struct rtl8169_private *tp, bool enable)
{
- void __iomem *ioaddr = tp->mmio_addr;
u8 data;
- data = RTL_R8(Config3);
+ data = RTL_R8(tp, Config3);
if (enable)
data |= Rdy_to_L23;
else
data &= ~Rdy_to_L23;
- RTL_W8(Config3, data);
+ RTL_W8(tp, Config3, data);
}
#define R8168_CPCMD_QUIRK_MASK (\
@@ -5891,45 +5731,37 @@ static void rtl_pcie_state_l2l3_enable(struct rtl8169_private *tp, bool enable)
static void rtl_hw_start_8168bb(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
- struct pci_dev *pdev = tp->pci_dev;
+ RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en);
- RTL_W8(Config3, RTL_R8(Config3) & ~Beacon_en);
-
- RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) & ~R8168_CPCMD_QUIRK_MASK);
+ RTL_W16(tp, CPlusCmd, RTL_R16(tp, CPlusCmd) & ~R8168_CPCMD_QUIRK_MASK);
if (tp->dev->mtu <= ETH_DATA_LEN) {
- rtl_tx_performance_tweak(pdev, (0x5 << MAX_READ_REQUEST_SHIFT) |
+ rtl_tx_performance_tweak(tp, (0x5 << MAX_READ_REQUEST_SHIFT) |
PCI_EXP_DEVCTL_NOSNOOP_EN);
}
}
static void rtl_hw_start_8168bef(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
rtl_hw_start_8168bb(tp);
- RTL_W8(MaxTxPacketSize, TxPacketMax);
+ RTL_W8(tp, MaxTxPacketSize, TxPacketMax);
- RTL_W8(Config4, RTL_R8(Config4) & ~(1 << 0));
+ RTL_W8(tp, Config4, RTL_R8(tp, Config4) & ~(1 << 0));
}
static void __rtl_hw_start_8168cp(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
- struct pci_dev *pdev = tp->pci_dev;
-
- RTL_W8(Config1, RTL_R8(Config1) | Speed_down);
+ RTL_W8(tp, Config1, RTL_R8(tp, Config1) | Speed_down);
- RTL_W8(Config3, RTL_R8(Config3) & ~Beacon_en);
+ RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en);
if (tp->dev->mtu <= ETH_DATA_LEN)
- rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT);
+ rtl_tx_performance_tweak(tp, 0x5 << MAX_READ_REQUEST_SHIFT);
- rtl_disable_clock_request(pdev);
+ rtl_disable_clock_request(tp);
- RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) & ~R8168_CPCMD_QUIRK_MASK);
+ RTL_W16(tp, CPlusCmd, RTL_R16(tp, CPlusCmd) & ~R8168_CPCMD_QUIRK_MASK);
}
static void rtl_hw_start_8168cp_1(struct rtl8169_private *tp)
@@ -5951,42 +5783,35 @@ static void rtl_hw_start_8168cp_1(struct rtl8169_private *tp)
static void rtl_hw_start_8168cp_2(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
- struct pci_dev *pdev = tp->pci_dev;
-
rtl_csi_access_enable_2(tp);
- RTL_W8(Config3, RTL_R8(Config3) & ~Beacon_en);
+ RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en);
if (tp->dev->mtu <= ETH_DATA_LEN)
- rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT);
+ rtl_tx_performance_tweak(tp, 0x5 << MAX_READ_REQUEST_SHIFT);
- RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) & ~R8168_CPCMD_QUIRK_MASK);
+ RTL_W16(tp, CPlusCmd, RTL_R16(tp, CPlusCmd) & ~R8168_CPCMD_QUIRK_MASK);
}
static void rtl_hw_start_8168cp_3(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
- struct pci_dev *pdev = tp->pci_dev;
-
rtl_csi_access_enable_2(tp);
- RTL_W8(Config3, RTL_R8(Config3) & ~Beacon_en);
+ RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en);
/* Magic. */
- RTL_W8(DBG_REG, 0x20);
+ RTL_W8(tp, DBG_REG, 0x20);
- RTL_W8(MaxTxPacketSize, TxPacketMax);
+ RTL_W8(tp, MaxTxPacketSize, TxPacketMax);
if (tp->dev->mtu <= ETH_DATA_LEN)
- rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT);
+ rtl_tx_performance_tweak(tp, 0x5 << MAX_READ_REQUEST_SHIFT);
- RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) & ~R8168_CPCMD_QUIRK_MASK);
+ RTL_W16(tp, CPlusCmd, RTL_R16(tp, CPlusCmd) & ~R8168_CPCMD_QUIRK_MASK);
}
static void rtl_hw_start_8168c_1(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
static const struct ephy_info e_info_8168c_1[] = {
{ 0x02, 0x0800, 0x1000 },
{ 0x03, 0, 0x0002 },
@@ -5995,7 +5820,7 @@ static void rtl_hw_start_8168c_1(struct rtl8169_private *tp)
rtl_csi_access_enable_2(tp);
- RTL_W8(DBG_REG, 0x06 | FIX_NAK_1 | FIX_NAK_2);
+ RTL_W8(tp, DBG_REG, 0x06 | FIX_NAK_1 | FIX_NAK_2);
rtl_ephy_init(tp, e_info_8168c_1, ARRAY_SIZE(e_info_8168c_1));
@@ -6030,40 +5855,32 @@ static void rtl_hw_start_8168c_4(struct rtl8169_private *tp)
static void rtl_hw_start_8168d(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
- struct pci_dev *pdev = tp->pci_dev;
-
rtl_csi_access_enable_2(tp);
- rtl_disable_clock_request(pdev);
+ rtl_disable_clock_request(tp);
- RTL_W8(MaxTxPacketSize, TxPacketMax);
+ RTL_W8(tp, MaxTxPacketSize, TxPacketMax);
if (tp->dev->mtu <= ETH_DATA_LEN)
- rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT);
+ rtl_tx_performance_tweak(tp, 0x5 << MAX_READ_REQUEST_SHIFT);
- RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) & ~R8168_CPCMD_QUIRK_MASK);
+ RTL_W16(tp, CPlusCmd, RTL_R16(tp, CPlusCmd) & ~R8168_CPCMD_QUIRK_MASK);
}
static void rtl_hw_start_8168dp(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
- struct pci_dev *pdev = tp->pci_dev;
-
rtl_csi_access_enable_1(tp);
if (tp->dev->mtu <= ETH_DATA_LEN)
- rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT);
+ rtl_tx_performance_tweak(tp, 0x5 << MAX_READ_REQUEST_SHIFT);
- RTL_W8(MaxTxPacketSize, TxPacketMax);
+ RTL_W8(tp, MaxTxPacketSize, TxPacketMax);
- rtl_disable_clock_request(pdev);
+ rtl_disable_clock_request(tp);
}
static void rtl_hw_start_8168d_4(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
- struct pci_dev *pdev = tp->pci_dev;
static const struct ephy_info e_info_8168d_4[] = {
{ 0x0b, 0x0000, 0x0048 },
{ 0x19, 0x0020, 0x0050 },
@@ -6072,19 +5889,17 @@ static void rtl_hw_start_8168d_4(struct rtl8169_private *tp)
rtl_csi_access_enable_1(tp);
- rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT);
+ rtl_tx_performance_tweak(tp, 0x5 << MAX_READ_REQUEST_SHIFT);
- RTL_W8(MaxTxPacketSize, TxPacketMax);
+ RTL_W8(tp, MaxTxPacketSize, TxPacketMax);
rtl_ephy_init(tp, e_info_8168d_4, ARRAY_SIZE(e_info_8168d_4));
- rtl_enable_clock_request(pdev);
+ rtl_enable_clock_request(tp);
}
static void rtl_hw_start_8168e_1(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
- struct pci_dev *pdev = tp->pci_dev;
static const struct ephy_info e_info_8168e_1[] = {
{ 0x00, 0x0200, 0x0100 },
{ 0x00, 0x0000, 0x0004 },
@@ -6106,23 +5921,21 @@ static void rtl_hw_start_8168e_1(struct rtl8169_private *tp)
rtl_ephy_init(tp, e_info_8168e_1, ARRAY_SIZE(e_info_8168e_1));
if (tp->dev->mtu <= ETH_DATA_LEN)
- rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT);
+ rtl_tx_performance_tweak(tp, 0x5 << MAX_READ_REQUEST_SHIFT);
- RTL_W8(MaxTxPacketSize, TxPacketMax);
+ RTL_W8(tp, MaxTxPacketSize, TxPacketMax);
- rtl_disable_clock_request(pdev);
+ rtl_disable_clock_request(tp);
/* Reset tx FIFO pointer */
- RTL_W32(MISC, RTL_R32(MISC) | TXPLA_RST);
- RTL_W32(MISC, RTL_R32(MISC) & ~TXPLA_RST);
+ RTL_W32(tp, MISC, RTL_R32(tp, MISC) | TXPLA_RST);
+ RTL_W32(tp, MISC, RTL_R32(tp, MISC) & ~TXPLA_RST);
- RTL_W8(Config5, RTL_R8(Config5) & ~Spi_en);
+ RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~Spi_en);
}
static void rtl_hw_start_8168e_2(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
- struct pci_dev *pdev = tp->pci_dev;
static const struct ephy_info e_info_8168e_2[] = {
{ 0x09, 0x0000, 0x0080 },
{ 0x19, 0x0000, 0x0224 }
@@ -6133,7 +5946,7 @@ static void rtl_hw_start_8168e_2(struct rtl8169_private *tp)
rtl_ephy_init(tp, e_info_8168e_2, ARRAY_SIZE(e_info_8168e_2));
if (tp->dev->mtu <= ETH_DATA_LEN)
- rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT);
+ rtl_tx_performance_tweak(tp, 0x5 << MAX_READ_REQUEST_SHIFT);
rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000, ERIAR_EXGMAC);
rtl_eri_write(tp, 0xb8, ERIAR_MASK_0011, 0x0000, ERIAR_EXGMAC);
@@ -6144,29 +5957,26 @@ static void rtl_hw_start_8168e_2(struct rtl8169_private *tp)
rtl_w0w1_eri(tp, 0x1b0, ERIAR_MASK_0001, 0x10, 0x00, ERIAR_EXGMAC);
rtl_w0w1_eri(tp, 0x0d4, ERIAR_MASK_0011, 0x0c00, 0xff00, ERIAR_EXGMAC);
- RTL_W8(MaxTxPacketSize, EarlySize);
+ RTL_W8(tp, MaxTxPacketSize, EarlySize);
- rtl_disable_clock_request(pdev);
+ rtl_disable_clock_request(tp);
- RTL_W32(TxConfig, RTL_R32(TxConfig) | TXCFG_AUTO_FIFO);
- RTL_W8(MCU, RTL_R8(MCU) & ~NOW_IS_OOB);
+ RTL_W32(tp, TxConfig, RTL_R32(tp, TxConfig) | TXCFG_AUTO_FIFO);
+ RTL_W8(tp, MCU, RTL_R8(tp, MCU) & ~NOW_IS_OOB);
/* Adjust EEE LED frequency */
- RTL_W8(EEE_LED, RTL_R8(EEE_LED) & ~0x07);
+ RTL_W8(tp, EEE_LED, RTL_R8(tp, EEE_LED) & ~0x07);
- RTL_W8(DLLPR, RTL_R8(DLLPR) | PFM_EN);
- RTL_W32(MISC, RTL_R32(MISC) | PWM_EN);
- RTL_W8(Config5, RTL_R8(Config5) & ~Spi_en);
+ RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) | PFM_EN);
+ RTL_W32(tp, MISC, RTL_R32(tp, MISC) | PWM_EN);
+ RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~Spi_en);
}
static void rtl_hw_start_8168f(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
- struct pci_dev *pdev = tp->pci_dev;
-
rtl_csi_access_enable_2(tp);
- rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT);
+ rtl_tx_performance_tweak(tp, 0x5 << MAX_READ_REQUEST_SHIFT);
rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000, ERIAR_EXGMAC);
rtl_eri_write(tp, 0xb8, ERIAR_MASK_0011, 0x0000, ERIAR_EXGMAC);
@@ -6179,20 +5989,19 @@ static void rtl_hw_start_8168f(struct rtl8169_private *tp)
rtl_eri_write(tp, 0xcc, ERIAR_MASK_1111, 0x00000050, ERIAR_EXGMAC);
rtl_eri_write(tp, 0xd0, ERIAR_MASK_1111, 0x00000060, ERIAR_EXGMAC);
- RTL_W8(MaxTxPacketSize, EarlySize);
+ RTL_W8(tp, MaxTxPacketSize, EarlySize);
- rtl_disable_clock_request(pdev);
+ rtl_disable_clock_request(tp);
- RTL_W32(TxConfig, RTL_R32(TxConfig) | TXCFG_AUTO_FIFO);
- RTL_W8(MCU, RTL_R8(MCU) & ~NOW_IS_OOB);
- RTL_W8(DLLPR, RTL_R8(DLLPR) | PFM_EN);
- RTL_W32(MISC, RTL_R32(MISC) | PWM_EN);
- RTL_W8(Config5, RTL_R8(Config5) & ~Spi_en);
+ RTL_W32(tp, TxConfig, RTL_R32(tp, TxConfig) | TXCFG_AUTO_FIFO);
+ RTL_W8(tp, MCU, RTL_R8(tp, MCU) & ~NOW_IS_OOB);
+ RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) | PFM_EN);
+ RTL_W32(tp, MISC, RTL_R32(tp, MISC) | PWM_EN);
+ RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~Spi_en);
}
static void rtl_hw_start_8168f_1(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
static const struct ephy_info e_info_8168f_1[] = {
{ 0x06, 0x00c0, 0x0020 },
{ 0x08, 0x0001, 0x0002 },
@@ -6207,7 +6016,7 @@ static void rtl_hw_start_8168f_1(struct rtl8169_private *tp)
rtl_w0w1_eri(tp, 0x0d4, ERIAR_MASK_0011, 0x0c00, 0xff00, ERIAR_EXGMAC);
/* Adjust EEE LED frequency */
- RTL_W8(EEE_LED, RTL_R8(EEE_LED) & ~0x07);
+ RTL_W8(tp, EEE_LED, RTL_R8(tp, EEE_LED) & ~0x07);
}
static void rtl_hw_start_8411(struct rtl8169_private *tp)
@@ -6229,10 +6038,7 @@ static void rtl_hw_start_8411(struct rtl8169_private *tp)
static void rtl_hw_start_8168g(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
- struct pci_dev *pdev = tp->pci_dev;
-
- RTL_W32(TxConfig, RTL_R32(TxConfig) | TXCFG_AUTO_FIFO);
+ RTL_W32(tp, TxConfig, RTL_R32(tp, TxConfig) | TXCFG_AUTO_FIFO);
rtl_eri_write(tp, 0xc8, ERIAR_MASK_0101, 0x080002, ERIAR_EXGMAC);
rtl_eri_write(tp, 0xcc, ERIAR_MASK_0001, 0x38, ERIAR_EXGMAC);
@@ -6241,20 +6047,20 @@ static void rtl_hw_start_8168g(struct rtl8169_private *tp)
rtl_csi_access_enable_1(tp);
- rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT);
+ rtl_tx_performance_tweak(tp, 0x5 << MAX_READ_REQUEST_SHIFT);
rtl_w0w1_eri(tp, 0xdc, ERIAR_MASK_0001, 0x00, 0x01, ERIAR_EXGMAC);
rtl_w0w1_eri(tp, 0xdc, ERIAR_MASK_0001, 0x01, 0x00, ERIAR_EXGMAC);
rtl_eri_write(tp, 0x2f8, ERIAR_MASK_0011, 0x1d8f, ERIAR_EXGMAC);
- RTL_W32(MISC, RTL_R32(MISC) & ~RXDV_GATED_EN);
- RTL_W8(MaxTxPacketSize, EarlySize);
+ RTL_W32(tp, MISC, RTL_R32(tp, MISC) & ~RXDV_GATED_EN);
+ RTL_W8(tp, MaxTxPacketSize, EarlySize);
rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000, ERIAR_EXGMAC);
rtl_eri_write(tp, 0xb8, ERIAR_MASK_0011, 0x0000, ERIAR_EXGMAC);
/* Adjust EEE LED frequency */
- RTL_W8(EEE_LED, RTL_R8(EEE_LED) & ~0x07);
+ RTL_W8(tp, EEE_LED, RTL_R8(tp, EEE_LED) & ~0x07);
rtl_w0w1_eri(tp, 0x2fc, ERIAR_MASK_0001, 0x01, 0x06, ERIAR_EXGMAC);
rtl_w0w1_eri(tp, 0x1b0, ERIAR_MASK_0011, 0x0000, 0x1000, ERIAR_EXGMAC);
@@ -6264,7 +6070,6 @@ static void rtl_hw_start_8168g(struct rtl8169_private *tp)
static void rtl_hw_start_8168g_1(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
static const struct ephy_info e_info_8168g_1[] = {
{ 0x00, 0x0000, 0x0008 },
{ 0x0c, 0x37d0, 0x0820 },
@@ -6275,14 +6080,13 @@ static void rtl_hw_start_8168g_1(struct rtl8169_private *tp)
rtl_hw_start_8168g(tp);
/* disable aspm and clock request before access ephy */
- RTL_W8(Config2, RTL_R8(Config2) & ~ClkReqEn);
- RTL_W8(Config5, RTL_R8(Config5) & ~ASPM_en);
+ RTL_W8(tp, Config2, RTL_R8(tp, Config2) & ~ClkReqEn);
+ RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~ASPM_en);
rtl_ephy_init(tp, e_info_8168g_1, ARRAY_SIZE(e_info_8168g_1));
}
static void rtl_hw_start_8168g_2(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
static const struct ephy_info e_info_8168g_2[] = {
{ 0x00, 0x0000, 0x0008 },
{ 0x0c, 0x3df0, 0x0200 },
@@ -6293,14 +6097,13 @@ static void rtl_hw_start_8168g_2(struct rtl8169_private *tp)
rtl_hw_start_8168g(tp);
/* disable aspm and clock request before access ephy */
- RTL_W8(Config2, RTL_R8(Config2) & ~ClkReqEn);
- RTL_W8(Config5, RTL_R8(Config5) & ~ASPM_en);
+ RTL_W8(tp, Config2, RTL_R8(tp, Config2) & ~ClkReqEn);
+ RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~ASPM_en);
rtl_ephy_init(tp, e_info_8168g_2, ARRAY_SIZE(e_info_8168g_2));
}
static void rtl_hw_start_8411_2(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
static const struct ephy_info e_info_8411_2[] = {
{ 0x00, 0x0000, 0x0008 },
{ 0x0c, 0x3df0, 0x0200 },
@@ -6312,15 +6115,13 @@ static void rtl_hw_start_8411_2(struct rtl8169_private *tp)
rtl_hw_start_8168g(tp);
/* disable aspm and clock request before access ephy */
- RTL_W8(Config2, RTL_R8(Config2) & ~ClkReqEn);
- RTL_W8(Config5, RTL_R8(Config5) & ~ASPM_en);
+ RTL_W8(tp, Config2, RTL_R8(tp, Config2) & ~ClkReqEn);
+ RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~ASPM_en);
rtl_ephy_init(tp, e_info_8411_2, ARRAY_SIZE(e_info_8411_2));
}
static void rtl_hw_start_8168h_1(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
- struct pci_dev *pdev = tp->pci_dev;
int rg_saw_cnt;
u32 data;
static const struct ephy_info e_info_8168h_1[] = {
@@ -6333,11 +6134,11 @@ static void rtl_hw_start_8168h_1(struct rtl8169_private *tp)
};
/* disable aspm and clock request before access ephy */
- RTL_W8(Config2, RTL_R8(Config2) & ~ClkReqEn);
- RTL_W8(Config5, RTL_R8(Config5) & ~ASPM_en);
+ RTL_W8(tp, Config2, RTL_R8(tp, Config2) & ~ClkReqEn);
+ RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~ASPM_en);
rtl_ephy_init(tp, e_info_8168h_1, ARRAY_SIZE(e_info_8168h_1));
- RTL_W32(TxConfig, RTL_R32(TxConfig) | TXCFG_AUTO_FIFO);
+ RTL_W32(tp, TxConfig, RTL_R32(tp, TxConfig) | TXCFG_AUTO_FIFO);
rtl_eri_write(tp, 0xc8, ERIAR_MASK_0101, 0x00080002, ERIAR_EXGMAC);
rtl_eri_write(tp, 0xcc, ERIAR_MASK_0001, 0x38, ERIAR_EXGMAC);
@@ -6346,7 +6147,7 @@ static void rtl_hw_start_8168h_1(struct rtl8169_private *tp)
rtl_csi_access_enable_1(tp);
- rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT);
+ rtl_tx_performance_tweak(tp, 0x5 << MAX_READ_REQUEST_SHIFT);
rtl_w0w1_eri(tp, 0xdc, ERIAR_MASK_0001, 0x00, 0x01, ERIAR_EXGMAC);
rtl_w0w1_eri(tp, 0xdc, ERIAR_MASK_0001, 0x01, 0x00, ERIAR_EXGMAC);
@@ -6357,19 +6158,19 @@ static void rtl_hw_start_8168h_1(struct rtl8169_private *tp)
rtl_eri_write(tp, 0x5f0, ERIAR_MASK_0011, 0x4f87, ERIAR_EXGMAC);
- RTL_W32(MISC, RTL_R32(MISC) & ~RXDV_GATED_EN);
- RTL_W8(MaxTxPacketSize, EarlySize);
+ RTL_W32(tp, MISC, RTL_R32(tp, MISC) & ~RXDV_GATED_EN);
+ RTL_W8(tp, MaxTxPacketSize, EarlySize);
rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000, ERIAR_EXGMAC);
rtl_eri_write(tp, 0xb8, ERIAR_MASK_0011, 0x0000, ERIAR_EXGMAC);
/* Adjust EEE LED frequency */
- RTL_W8(EEE_LED, RTL_R8(EEE_LED) & ~0x07);
+ RTL_W8(tp, EEE_LED, RTL_R8(tp, EEE_LED) & ~0x07);
- RTL_W8(DLLPR, RTL_R8(DLLPR) & ~PFM_EN);
- RTL_W8(MISC_1, RTL_R8(MISC_1) & ~PFM_D3COLD_EN);
+ RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~PFM_EN);
+ RTL_W8(tp, MISC_1, RTL_R8(tp, MISC_1) & ~PFM_D3COLD_EN);
- RTL_W8(DLLPR, RTL_R8(DLLPR) & ~TX_10M_PS_EN);
+ RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~TX_10M_PS_EN);
rtl_w0w1_eri(tp, 0x1b0, ERIAR_MASK_0011, 0x0000, 0x1000, ERIAR_EXGMAC);
@@ -6417,12 +6218,9 @@ static void rtl_hw_start_8168h_1(struct rtl8169_private *tp)
static void rtl_hw_start_8168ep(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
- struct pci_dev *pdev = tp->pci_dev;
-
rtl8168ep_stop_cmac(tp);
- RTL_W32(TxConfig, RTL_R32(TxConfig) | TXCFG_AUTO_FIFO);
+ RTL_W32(tp, TxConfig, RTL_R32(tp, TxConfig) | TXCFG_AUTO_FIFO);
rtl_eri_write(tp, 0xc8, ERIAR_MASK_0101, 0x00080002, ERIAR_EXGMAC);
rtl_eri_write(tp, 0xcc, ERIAR_MASK_0001, 0x2f, ERIAR_EXGMAC);
@@ -6431,7 +6229,7 @@ static void rtl_hw_start_8168ep(struct rtl8169_private *tp)
rtl_csi_access_enable_1(tp);
- rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT);
+ rtl_tx_performance_tweak(tp, 0x5 << MAX_READ_REQUEST_SHIFT);
rtl_w0w1_eri(tp, 0xdc, ERIAR_MASK_0001, 0x00, 0x01, ERIAR_EXGMAC);
rtl_w0w1_eri(tp, 0xdc, ERIAR_MASK_0001, 0x01, 0x00, ERIAR_EXGMAC);
@@ -6440,25 +6238,24 @@ static void rtl_hw_start_8168ep(struct rtl8169_private *tp)
rtl_eri_write(tp, 0x5f0, ERIAR_MASK_0011, 0x4f87, ERIAR_EXGMAC);
- RTL_W32(MISC, RTL_R32(MISC) & ~RXDV_GATED_EN);
- RTL_W8(MaxTxPacketSize, EarlySize);
+ RTL_W32(tp, MISC, RTL_R32(tp, MISC) & ~RXDV_GATED_EN);
+ RTL_W8(tp, MaxTxPacketSize, EarlySize);
rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000, ERIAR_EXGMAC);
rtl_eri_write(tp, 0xb8, ERIAR_MASK_0011, 0x0000, ERIAR_EXGMAC);
/* Adjust EEE LED frequency */
- RTL_W8(EEE_LED, RTL_R8(EEE_LED) & ~0x07);
+ RTL_W8(tp, EEE_LED, RTL_R8(tp, EEE_LED) & ~0x07);
rtl_w0w1_eri(tp, 0x2fc, ERIAR_MASK_0001, 0x01, 0x06, ERIAR_EXGMAC);
- RTL_W8(DLLPR, RTL_R8(DLLPR) & ~TX_10M_PS_EN);
+ RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~TX_10M_PS_EN);
rtl_pcie_state_l2l3_enable(tp, false);
}
static void rtl_hw_start_8168ep_1(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
static const struct ephy_info e_info_8168ep_1[] = {
{ 0x00, 0xffff, 0x10ab },
{ 0x06, 0xffff, 0xf030 },
@@ -6468,8 +6265,8 @@ static void rtl_hw_start_8168ep_1(struct rtl8169_private *tp)
};
/* disable aspm and clock request before access ephy */
- RTL_W8(Config2, RTL_R8(Config2) & ~ClkReqEn);
- RTL_W8(Config5, RTL_R8(Config5) & ~ASPM_en);
+ RTL_W8(tp, Config2, RTL_R8(tp, Config2) & ~ClkReqEn);
+ RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~ASPM_en);
rtl_ephy_init(tp, e_info_8168ep_1, ARRAY_SIZE(e_info_8168ep_1));
rtl_hw_start_8168ep(tp);
@@ -6477,7 +6274,6 @@ static void rtl_hw_start_8168ep_1(struct rtl8169_private *tp)
static void rtl_hw_start_8168ep_2(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
static const struct ephy_info e_info_8168ep_2[] = {
{ 0x00, 0xffff, 0x10a3 },
{ 0x19, 0xffff, 0xfc00 },
@@ -6485,19 +6281,18 @@ static void rtl_hw_start_8168ep_2(struct rtl8169_private *tp)
};
/* disable aspm and clock request before access ephy */
- RTL_W8(Config2, RTL_R8(Config2) & ~ClkReqEn);
- RTL_W8(Config5, RTL_R8(Config5) & ~ASPM_en);
+ RTL_W8(tp, Config2, RTL_R8(tp, Config2) & ~ClkReqEn);
+ RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~ASPM_en);
rtl_ephy_init(tp, e_info_8168ep_2, ARRAY_SIZE(e_info_8168ep_2));
rtl_hw_start_8168ep(tp);
- RTL_W8(DLLPR, RTL_R8(DLLPR) & ~PFM_EN);
- RTL_W8(MISC_1, RTL_R8(MISC_1) & ~PFM_D3COLD_EN);
+ RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~PFM_EN);
+ RTL_W8(tp, MISC_1, RTL_R8(tp, MISC_1) & ~PFM_D3COLD_EN);
}
static void rtl_hw_start_8168ep_3(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
u32 data;
static const struct ephy_info e_info_8168ep_3[] = {
{ 0x00, 0xffff, 0x10a3 },
@@ -6507,14 +6302,14 @@ static void rtl_hw_start_8168ep_3(struct rtl8169_private *tp)
};
/* disable aspm and clock request before access ephy */
- RTL_W8(Config2, RTL_R8(Config2) & ~ClkReqEn);
- RTL_W8(Config5, RTL_R8(Config5) & ~ASPM_en);
+ RTL_W8(tp, Config2, RTL_R8(tp, Config2) & ~ClkReqEn);
+ RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~ASPM_en);
rtl_ephy_init(tp, e_info_8168ep_3, ARRAY_SIZE(e_info_8168ep_3));
rtl_hw_start_8168ep(tp);
- RTL_W8(DLLPR, RTL_R8(DLLPR) & ~PFM_EN);
- RTL_W8(MISC_1, RTL_R8(MISC_1) & ~PFM_D3COLD_EN);
+ RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~PFM_EN);
+ RTL_W8(tp, MISC_1, RTL_R8(tp, MISC_1) & ~PFM_D3COLD_EN);
data = r8168_mac_ocp_read(tp, 0xd3e2);
data &= 0xf000;
@@ -6533,19 +6328,18 @@ static void rtl_hw_start_8168ep_3(struct rtl8169_private *tp)
static void rtl_hw_start_8168(struct net_device *dev)
{
struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- RTL_W8(Cfg9346, Cfg9346_Unlock);
+ RTL_W8(tp, Cfg9346, Cfg9346_Unlock);
- RTL_W8(MaxTxPacketSize, TxPacketMax);
+ RTL_W8(tp, MaxTxPacketSize, TxPacketMax);
- rtl_set_rx_max_size(ioaddr, rx_buf_sz);
+ rtl_set_rx_max_size(tp, rx_buf_sz);
- tp->cp_cmd |= RTL_R16(CPlusCmd) | PktCntrDisable | INTT_1;
+ tp->cp_cmd |= RTL_R16(tp, CPlusCmd) | PktCntrDisable | INTT_1;
- RTL_W16(CPlusCmd, tp->cp_cmd);
+ RTL_W16(tp, CPlusCmd, tp->cp_cmd);
- RTL_W16(IntrMitigate, 0x5151);
+ RTL_W16(tp, IntrMitigate, 0x5151);
/* Work around for RxFIFO overflow. */
if (tp->mac_version == RTL_GIGA_MAC_VER_11) {
@@ -6553,11 +6347,11 @@ static void rtl_hw_start_8168(struct net_device *dev)
tp->event_slow &= ~RxOverflow;
}
- rtl_set_rx_tx_desc_registers(tp, ioaddr);
+ rtl_set_rx_tx_desc_registers(tp);
rtl_set_rx_tx_config_registers(tp);
- RTL_R8(IntrMask);
+ RTL_R8(tp, IntrMask);
switch (tp->mac_version) {
case RTL_GIGA_MAC_VER_11:
@@ -6663,13 +6457,13 @@ static void rtl_hw_start_8168(struct net_device *dev)
break;
}
- RTL_W8(Cfg9346, Cfg9346_Lock);
+ RTL_W8(tp, Cfg9346, Cfg9346_Lock);
- RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb);
+ RTL_W8(tp, ChipCmd, CmdTxEnb | CmdRxEnb);
rtl_set_rx_mode(dev);
- RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xf000);
+ RTL_W16(tp, MultiIntr, RTL_R16(tp, MultiIntr) & 0xf000);
}
#define R810X_CPCMD_QUIRK_MASK (\
@@ -6685,8 +6479,6 @@ static void rtl_hw_start_8168(struct net_device *dev)
static void rtl_hw_start_8102e_1(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
- struct pci_dev *pdev = tp->pci_dev;
static const struct ephy_info e_info_8102e_1[] = {
{ 0x01, 0, 0x6e65 },
{ 0x02, 0, 0x091f },
@@ -6701,32 +6493,29 @@ static void rtl_hw_start_8102e_1(struct rtl8169_private *tp)
rtl_csi_access_enable_2(tp);
- RTL_W8(DBG_REG, FIX_NAK_1);
+ RTL_W8(tp, DBG_REG, FIX_NAK_1);
- rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT);
+ rtl_tx_performance_tweak(tp, 0x5 << MAX_READ_REQUEST_SHIFT);
- RTL_W8(Config1,
+ RTL_W8(tp, Config1,
LEDS1 | LEDS0 | Speed_down | MEMMAP | IOMAP | VPD | PMEnable);
- RTL_W8(Config3, RTL_R8(Config3) & ~Beacon_en);
+ RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en);
- cfg1 = RTL_R8(Config1);
+ cfg1 = RTL_R8(tp, Config1);
if ((cfg1 & LEDS0) && (cfg1 & LEDS1))
- RTL_W8(Config1, cfg1 & ~LEDS0);
+ RTL_W8(tp, Config1, cfg1 & ~LEDS0);
rtl_ephy_init(tp, e_info_8102e_1, ARRAY_SIZE(e_info_8102e_1));
}
static void rtl_hw_start_8102e_2(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
- struct pci_dev *pdev = tp->pci_dev;
-
rtl_csi_access_enable_2(tp);
- rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT);
+ rtl_tx_performance_tweak(tp, 0x5 << MAX_READ_REQUEST_SHIFT);
- RTL_W8(Config1, MEMMAP | IOMAP | VPD | PMEnable);
- RTL_W8(Config3, RTL_R8(Config3) & ~Beacon_en);
+ RTL_W8(tp, Config1, MEMMAP | IOMAP | VPD | PMEnable);
+ RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en);
}
static void rtl_hw_start_8102e_3(struct rtl8169_private *tp)
@@ -6738,7 +6527,6 @@ static void rtl_hw_start_8102e_3(struct rtl8169_private *tp)
static void rtl_hw_start_8105e_1(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
static const struct ephy_info e_info_8105e_1[] = {
{ 0x07, 0, 0x4000 },
{ 0x19, 0, 0x0200 },
@@ -6751,13 +6539,13 @@ static void rtl_hw_start_8105e_1(struct rtl8169_private *tp)
};
/* Force LAN exit from ASPM if Rx/Tx are not idle */
- RTL_W32(FuncEvent, RTL_R32(FuncEvent) | 0x002800);
+ RTL_W32(tp, FuncEvent, RTL_R32(tp, FuncEvent) | 0x002800);
/* Disable Early Tally Counter */
- RTL_W32(FuncEvent, RTL_R32(FuncEvent) & ~0x010000);
+ RTL_W32(tp, FuncEvent, RTL_R32(tp, FuncEvent) & ~0x010000);
- RTL_W8(MCU, RTL_R8(MCU) | EN_NDP | EN_OOB_RESET);
- RTL_W8(DLLPR, RTL_R8(DLLPR) | PFM_EN);
+ RTL_W8(tp, MCU, RTL_R8(tp, MCU) | EN_NDP | EN_OOB_RESET);
+ RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) | PFM_EN);
rtl_ephy_init(tp, e_info_8105e_1, ARRAY_SIZE(e_info_8105e_1));
@@ -6772,7 +6560,6 @@ static void rtl_hw_start_8105e_2(struct rtl8169_private *tp)
static void rtl_hw_start_8402(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
static const struct ephy_info e_info_8402[] = {
{ 0x19, 0xffff, 0xff64 },
{ 0x1e, 0, 0x4000 }
@@ -6781,14 +6568,14 @@ static void rtl_hw_start_8402(struct rtl8169_private *tp)
rtl_csi_access_enable_2(tp);
/* Force LAN exit from ASPM if Rx/Tx are not idle */
- RTL_W32(FuncEvent, RTL_R32(FuncEvent) | 0x002800);
+ RTL_W32(tp, FuncEvent, RTL_R32(tp, FuncEvent) | 0x002800);
- RTL_W32(TxConfig, RTL_R32(TxConfig) | TXCFG_AUTO_FIFO);
- RTL_W8(MCU, RTL_R8(MCU) & ~NOW_IS_OOB);
+ RTL_W32(tp, TxConfig, RTL_R32(tp, TxConfig) | TXCFG_AUTO_FIFO);
+ RTL_W8(tp, MCU, RTL_R8(tp, MCU) & ~NOW_IS_OOB);
rtl_ephy_init(tp, e_info_8402, ARRAY_SIZE(e_info_8402));
- rtl_tx_performance_tweak(tp->pci_dev, 0x5 << MAX_READ_REQUEST_SHIFT);
+ rtl_tx_performance_tweak(tp, 0x5 << MAX_READ_REQUEST_SHIFT);
rtl_eri_write(tp, 0xc8, ERIAR_MASK_1111, 0x00000002, ERIAR_EXGMAC);
rtl_eri_write(tp, 0xe8, ERIAR_MASK_1111, 0x00000006, ERIAR_EXGMAC);
@@ -6803,14 +6590,12 @@ static void rtl_hw_start_8402(struct rtl8169_private *tp)
static void rtl_hw_start_8106(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
/* Force LAN exit from ASPM if Rx/Tx are not idle */
- RTL_W32(FuncEvent, RTL_R32(FuncEvent) | 0x002800);
+ RTL_W32(tp, FuncEvent, RTL_R32(tp, FuncEvent) | 0x002800);
- RTL_W32(MISC, (RTL_R32(MISC) | DISABLE_LAN_EN) & ~EARLY_TALLY_EN);
- RTL_W8(MCU, RTL_R8(MCU) | EN_NDP | EN_OOB_RESET);
- RTL_W8(DLLPR, RTL_R8(DLLPR) & ~PFM_EN);
+ RTL_W32(tp, MISC, (RTL_R32(tp, MISC) | DISABLE_LAN_EN) & ~EARLY_TALLY_EN);
+ RTL_W8(tp, MCU, RTL_R8(tp, MCU) | EN_NDP | EN_OOB_RESET);
+ RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~PFM_EN);
rtl_pcie_state_l2l3_enable(tp, false);
}
@@ -6818,7 +6603,6 @@ static void rtl_hw_start_8106(struct rtl8169_private *tp)
static void rtl_hw_start_8101(struct net_device *dev)
{
struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
struct pci_dev *pdev = tp->pci_dev;
if (tp->mac_version >= RTL_GIGA_MAC_VER_30)
@@ -6829,16 +6613,16 @@ static void rtl_hw_start_8101(struct net_device *dev)
pcie_capability_set_word(pdev, PCI_EXP_DEVCTL,
PCI_EXP_DEVCTL_NOSNOOP_EN);
- RTL_W8(Cfg9346, Cfg9346_Unlock);
+ RTL_W8(tp, Cfg9346, Cfg9346_Unlock);
- RTL_W8(MaxTxPacketSize, TxPacketMax);
+ RTL_W8(tp, MaxTxPacketSize, TxPacketMax);
- rtl_set_rx_max_size(ioaddr, rx_buf_sz);
+ rtl_set_rx_max_size(tp, rx_buf_sz);
tp->cp_cmd &= ~R810X_CPCMD_QUIRK_MASK;
- RTL_W16(CPlusCmd, tp->cp_cmd);
+ RTL_W16(tp, CPlusCmd, tp->cp_cmd);
- rtl_set_rx_tx_desc_registers(tp, ioaddr);
+ rtl_set_rx_tx_desc_registers(tp);
rtl_set_rx_tx_config_registers(tp);
@@ -6878,17 +6662,17 @@ static void rtl_hw_start_8101(struct net_device *dev)
break;
}
- RTL_W8(Cfg9346, Cfg9346_Lock);
+ RTL_W8(tp, Cfg9346, Cfg9346_Lock);
- RTL_W16(IntrMitigate, 0x0000);
+ RTL_W16(tp, IntrMitigate, 0x0000);
- RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb);
+ RTL_W8(tp, ChipCmd, CmdTxEnb | CmdRxEnb);
rtl_set_rx_mode(dev);
- RTL_R8(IntrMask);
+ RTL_R8(tp, IntrMask);
- RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xf000);
+ RTL_W16(tp, MultiIntr, RTL_R16(tp, MultiIntr) & 0xf000);
}
static int rtl8169_change_mtu(struct net_device *dev, int new_mtu)
@@ -6915,7 +6699,7 @@ static inline void rtl8169_make_unusable_by_asic(struct RxDesc *desc)
static void rtl8169_free_rx_databuff(struct rtl8169_private *tp,
void **data_buff, struct RxDesc *desc)
{
- dma_unmap_single(&tp->pci_dev->dev, le64_to_cpu(desc->addr), rx_buf_sz,
+ dma_unmap_single(tp_to_dev(tp), le64_to_cpu(desc->addr), rx_buf_sz,
DMA_FROM_DEVICE);
kfree(*data_buff);
@@ -6950,7 +6734,7 @@ static struct sk_buff *rtl8169_alloc_rx_data(struct rtl8169_private *tp,
{
void *data;
dma_addr_t mapping;
- struct device *d = &tp->pci_dev->dev;
+ struct device *d = tp_to_dev(tp);
struct net_device *dev = tp->dev;
int node = dev->dev.parent ? dev_to_node(dev->dev.parent) : -1;
@@ -7062,7 +6846,7 @@ static void rtl8169_tx_clear_range(struct rtl8169_private *tp, u32 start,
if (len) {
struct sk_buff *skb = tx_skb->skb;
- rtl8169_unmap_tx_skb(&tp->pci_dev->dev, tx_skb,
+ rtl8169_unmap_tx_skb(tp_to_dev(tp), tx_skb,
tp->TxDescArray + entry);
if (skb) {
dev_consume_skb_any(skb);
@@ -7098,7 +6882,7 @@ static void rtl_reset_work(struct rtl8169_private *tp)
napi_enable(&tp->napi);
rtl_hw_start(dev);
netif_wake_queue(dev);
- rtl8169_check_link_status(dev, tp, tp->mmio_addr);
+ rtl8169_check_link_status(dev, tp);
}
static void rtl8169_tx_timeout(struct net_device *dev)
@@ -7114,7 +6898,7 @@ static int rtl8169_xmit_frags(struct rtl8169_private *tp, struct sk_buff *skb,
struct skb_shared_info *info = skb_shinfo(skb);
unsigned int cur_frag, entry;
struct TxDesc *uninitialized_var(txd);
- struct device *d = &tp->pci_dev->dev;
+ struct device *d = tp_to_dev(tp);
entry = tp->cur_tx;
for (cur_frag = 0; cur_frag < info->nr_frags; cur_frag++) {
@@ -7346,8 +7130,7 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb,
struct rtl8169_private *tp = netdev_priv(dev);
unsigned int entry = tp->cur_tx % NUM_TX_DESC;
struct TxDesc *txd = tp->TxDescArray + entry;
- void __iomem *ioaddr = tp->mmio_addr;
- struct device *d = &tp->pci_dev->dev;
+ struct device *d = tp_to_dev(tp);
dma_addr_t mapping;
u32 status, len;
u32 opts[2];
@@ -7406,7 +7189,7 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb,
tp->cur_tx += frags + 1;
- RTL_W8(TxPoll, NPQ);
+ RTL_W8(tp, TxPoll, NPQ);
mmiowb();
@@ -7477,11 +7260,9 @@ static void rtl8169_pcierr_interrupt(struct net_device *dev)
/* The infamous DAC f*ckup only happens at boot time */
if ((tp->cp_cmd & PCIDAC) && !tp->cur_rx) {
- void __iomem *ioaddr = tp->mmio_addr;
-
netif_info(tp, intr, dev, "disabling PCI DAC\n");
tp->cp_cmd &= ~PCIDAC;
- RTL_W16(CPlusCmd, tp->cp_cmd);
+ RTL_W16(tp, CPlusCmd, tp->cp_cmd);
dev->features &= ~NETIF_F_HIGHDMA;
}
@@ -7513,7 +7294,7 @@ static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp)
*/
dma_rmb();
- rtl8169_unmap_tx_skb(&tp->pci_dev->dev, tx_skb,
+ rtl8169_unmap_tx_skb(tp_to_dev(tp), tx_skb,
tp->TxDescArray + entry);
if (status & LastFrag) {
u64_stats_update_begin(&tp->tx_stats.syncp);
@@ -7547,11 +7328,8 @@ static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp)
* of start_xmit activity is detected (if it is not detected,
* it is slow enough). -- FR
*/
- if (tp->cur_tx != dirty_tx) {
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W8(TxPoll, NPQ);
- }
+ if (tp->cur_tx != dirty_tx)
+ RTL_W8(tp, TxPoll, NPQ);
}
}
@@ -7577,7 +7355,7 @@ static struct sk_buff *rtl8169_try_rx_copy(void *data,
dma_addr_t addr)
{
struct sk_buff *skb;
- struct device *d = &tp->pci_dev->dev;
+ struct device *d = tp_to_dev(tp);
data = rtl8169_align(data);
dma_sync_single_for_cpu(d, addr, pkt_size, DMA_FROM_DEVICE);
@@ -7732,7 +7510,7 @@ static void rtl_slow_event_work(struct rtl8169_private *tp)
rtl8169_pcierr_interrupt(dev);
if (status & LinkChg)
- rtl8169_check_link_status(dev, tp, tp->mmio_addr);
+ rtl8169_check_link_status(dev, tp);
rtl_irq_enable_all(tp);
}
@@ -7804,21 +7582,20 @@ static int rtl8169_poll(struct napi_struct *napi, int budget)
return work_done;
}
-static void rtl8169_rx_missed(struct net_device *dev, void __iomem *ioaddr)
+static void rtl8169_rx_missed(struct net_device *dev)
{
struct rtl8169_private *tp = netdev_priv(dev);
if (tp->mac_version > RTL_GIGA_MAC_VER_06)
return;
- dev->stats.rx_missed_errors += (RTL_R32(RxMissed) & 0xffffff);
- RTL_W32(RxMissed, 0);
+ dev->stats.rx_missed_errors += RTL_R32(tp, RxMissed) & 0xffffff;
+ RTL_W32(tp, RxMissed, 0);
}
static void rtl8169_down(struct net_device *dev)
{
struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
del_timer_sync(&tp->timer);
@@ -7831,7 +7608,7 @@ static void rtl8169_down(struct net_device *dev)
* as netif_running is not true (rtl8169_interrupt, rtl8169_reset_task)
* and napi is disabled (rtl8169_poll).
*/
- rtl8169_rx_missed(dev, ioaddr);
+ rtl8169_rx_missed(dev);
/* Give a racing hard_start_xmit a few cycles to complete. */
synchronize_sched();
@@ -7861,7 +7638,7 @@ static int rtl8169_close(struct net_device *dev)
cancel_work_sync(&tp->wk.work);
- free_irq(pdev->irq, dev);
+ pci_free_irq(pdev, 0, dev);
dma_free_coherent(&pdev->dev, R8169_RX_RING_BYTES, tp->RxDescArray,
tp->RxPhyAddr);
@@ -7880,14 +7657,13 @@ static void rtl8169_netpoll(struct net_device *dev)
{
struct rtl8169_private *tp = netdev_priv(dev);
- rtl8169_interrupt(tp->pci_dev->irq, dev);
+ rtl8169_interrupt(pci_irq_vector(tp->pci_dev, 0), dev);
}
#endif
static int rtl_open(struct net_device *dev)
{
struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
struct pci_dev *pdev = tp->pci_dev;
int retval = -ENOMEM;
@@ -7917,9 +7693,8 @@ static int rtl_open(struct net_device *dev)
rtl_request_firmware(tp);
- retval = request_irq(pdev->irq, rtl8169_interrupt,
- (tp->features & RTL_FEATURE_MSI) ? 0 : IRQF_SHARED,
- dev->name, dev);
+ retval = pci_request_irq(pdev, 0, rtl8169_interrupt, NULL, dev,
+ dev->name);
if (retval < 0)
goto err_release_fw_2;
@@ -7947,7 +7722,7 @@ static int rtl_open(struct net_device *dev)
tp->saved_wolopts = 0;
pm_runtime_put_sync(&pdev->dev);
- rtl8169_check_link_status(dev, tp, ioaddr);
+ rtl8169_check_link_status(dev, tp);
out:
return retval;
@@ -7971,7 +7746,6 @@ static void
rtl8169_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
{
struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
struct pci_dev *pdev = tp->pci_dev;
struct rtl8169_counters *counters = tp->counters;
unsigned int start;
@@ -7979,7 +7753,7 @@ rtl8169_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
pm_runtime_get_noresume(&pdev->dev);
if (netif_running(dev) && pm_runtime_active(&pdev->dev))
- rtl8169_rx_missed(dev, ioaddr);
+ rtl8169_rx_missed(dev);
do {
start = u64_stats_fetch_begin_irq(&tp->rx_stats.syncp);
@@ -8102,7 +7876,7 @@ static int rtl8169_runtime_suspend(struct device *device)
rtl8169_net_suspend(dev);
/* Update counters before going runtime suspend */
- rtl8169_rx_missed(dev, tp->mmio_addr);
+ rtl8169_rx_missed(dev);
rtl8169_update_counters(dev);
return 0;
@@ -8163,8 +7937,6 @@ static const struct dev_pm_ops rtl8169_pm_ops = {
static void rtl_wol_shutdown_quirk(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
/* WoL fails with 8168b when the receiver is disabled. */
switch (tp->mac_version) {
case RTL_GIGA_MAC_VER_11:
@@ -8172,9 +7944,9 @@ static void rtl_wol_shutdown_quirk(struct rtl8169_private *tp)
case RTL_GIGA_MAC_VER_17:
pci_clear_master(tp->pci_dev);
- RTL_W8(ChipCmd, CmdRxEnb);
+ RTL_W8(tp, ChipCmd, CmdRxEnb);
/* PCI commit */
- RTL_R8(ChipCmd);
+ RTL_R8(tp, ChipCmd);
break;
default:
break;
@@ -8209,15 +7981,8 @@ static void rtl_remove_one(struct pci_dev *pdev)
struct net_device *dev = pci_get_drvdata(pdev);
struct rtl8169_private *tp = netdev_priv(dev);
- if ((tp->mac_version == RTL_GIGA_MAC_VER_27 ||
- tp->mac_version == RTL_GIGA_MAC_VER_28 ||
- tp->mac_version == RTL_GIGA_MAC_VER_31 ||
- tp->mac_version == RTL_GIGA_MAC_VER_49 ||
- tp->mac_version == RTL_GIGA_MAC_VER_50 ||
- tp->mac_version == RTL_GIGA_MAC_VER_51) &&
- r8168_check_dash(tp)) {
+ if (r8168_check_dash(tp))
rtl8168_driver_stop(tp);
- }
netif_napi_del(&tp->napi);
@@ -8256,7 +8021,7 @@ static const struct rtl_cfg_info {
unsigned int region;
unsigned int align;
u16 event_slow;
- unsigned features;
+ unsigned int has_gmii:1;
const struct rtl_coalesce_info *coalesce_info;
u8 default_ver;
} rtl_cfg_infos [] = {
@@ -8265,7 +8030,7 @@ static const struct rtl_cfg_info {
.region = 1,
.align = 0,
.event_slow = SYSErr | LinkChg | RxOverflow | RxFIFOOver,
- .features = RTL_FEATURE_GMII,
+ .has_gmii = 1,
.coalesce_info = rtl_coalesce_info_8169,
.default_ver = RTL_GIGA_MAC_VER_01,
},
@@ -8274,7 +8039,7 @@ static const struct rtl_cfg_info {
.region = 2,
.align = 8,
.event_slow = SYSErr | LinkChg | RxOverflow,
- .features = RTL_FEATURE_GMII | RTL_FEATURE_MSI,
+ .has_gmii = 1,
.coalesce_info = rtl_coalesce_info_8168_8136,
.default_ver = RTL_GIGA_MAC_VER_11,
},
@@ -8284,56 +8049,44 @@ static const struct rtl_cfg_info {
.align = 8,
.event_slow = SYSErr | LinkChg | RxOverflow | RxFIFOOver |
PCSTimeout,
- .features = RTL_FEATURE_MSI,
.coalesce_info = rtl_coalesce_info_8168_8136,
.default_ver = RTL_GIGA_MAC_VER_13,
}
};
-/* Cfg9346_Unlock assumed. */
-static unsigned rtl_try_msi(struct rtl8169_private *tp,
- const struct rtl_cfg_info *cfg)
+static int rtl_alloc_irq(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
- unsigned msi = 0;
- u8 cfg2;
+ unsigned int flags;
- cfg2 = RTL_R8(Config2) & ~MSIEnable;
- if (cfg->features & RTL_FEATURE_MSI) {
- if (pci_enable_msi(tp->pci_dev)) {
- netif_info(tp, hw, tp->dev, "no MSI. Back to INTx.\n");
- } else {
- cfg2 |= MSIEnable;
- msi = RTL_FEATURE_MSI;
- }
+ if (tp->mac_version <= RTL_GIGA_MAC_VER_06) {
+ RTL_W8(tp, Cfg9346, Cfg9346_Unlock);
+ RTL_W8(tp, Config2, RTL_R8(tp, Config2) & ~MSIEnable);
+ RTL_W8(tp, Cfg9346, Cfg9346_Lock);
+ flags = PCI_IRQ_LEGACY;
+ } else {
+ flags = PCI_IRQ_ALL_TYPES;
}
- if (tp->mac_version <= RTL_GIGA_MAC_VER_06)
- RTL_W8(Config2, cfg2);
- return msi;
+
+ return pci_alloc_irq_vectors(tp->pci_dev, 1, 1, flags);
}
DECLARE_RTL_COND(rtl_link_list_ready_cond)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- return RTL_R8(MCU) & LINK_LIST_RDY;
+ return RTL_R8(tp, MCU) & LINK_LIST_RDY;
}
DECLARE_RTL_COND(rtl_rxtx_empty_cond)
{
- void __iomem *ioaddr = tp->mmio_addr;
-
- return (RTL_R8(MCU) & RXTX_EMPTY) == RXTX_EMPTY;
+ return (RTL_R8(tp, MCU) & RXTX_EMPTY) == RXTX_EMPTY;
}
static void rtl_hw_init_8168g(struct rtl8169_private *tp)
{
- void __iomem *ioaddr = tp->mmio_addr;
u32 data;
tp->ocp_base = OCP_STD_PHY_BASE;
- RTL_W32(MISC, RTL_R32(MISC) | RXDV_GATED_EN);
+ RTL_W32(tp, MISC, RTL_R32(tp, MISC) | RXDV_GATED_EN);
if (!rtl_udelay_loop_wait_high(tp, &rtl_txcfg_empty_cond, 100, 42))
return;
@@ -8341,9 +8094,9 @@ static void rtl_hw_init_8168g(struct rtl8169_private *tp)
if (!rtl_udelay_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42))
return;
- RTL_W8(ChipCmd, RTL_R8(ChipCmd) & ~(CmdTxEnb | CmdRxEnb));
+ RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) & ~(CmdTxEnb | CmdRxEnb));
msleep(1);
- RTL_W8(MCU, RTL_R8(MCU) & ~NOW_IS_OOB);
+ RTL_W8(tp, MCU, RTL_R8(tp, MCU) & ~NOW_IS_OOB);
data = r8168_mac_ocp_read(tp, 0xe8de);
data &= ~(1 << 14);
@@ -8397,7 +8150,6 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
struct rtl8169_private *tp;
struct mii_if_info *mii;
struct net_device *dev;
- void __iomem *ioaddr;
int chipset, i;
int rc;
@@ -8423,7 +8175,7 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
mii->mdio_write = rtl_mdio_write;
mii->phy_id_mask = 0x1f;
mii->reg_num_mask = 0x1f;
- mii->supports_gmii = !!(cfg->features & RTL_FEATURE_GMII);
+ mii->supports_gmii = cfg->has_gmii;
/* disable ASPM completely as that cause random device stop working
* problems as well as full system hangs for some PCIe devices users */
@@ -8455,20 +8207,13 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
return -ENODEV;
}
- rc = pci_request_regions(pdev, MODULENAME);
+ rc = pcim_iomap_regions(pdev, BIT(region), MODULENAME);
if (rc < 0) {
- netif_err(tp, probe, dev, "could not request regions\n");
+ netif_err(tp, probe, dev, "cannot remap MMIO, aborting\n");
return rc;
}
- /* ioremap MMIO region */
- ioaddr = devm_ioremap(&pdev->dev, pci_resource_start(pdev, region),
- R8169_REGS_SIZE);
- if (!ioaddr) {
- netif_err(tp, probe, dev, "cannot remap MMIO, aborting\n");
- return -EIO;
- }
- tp->mmio_addr = ioaddr;
+ tp->mmio_addr = pcim_iomap_table(pdev)[region];
if (!pci_is_pcie(pdev))
netif_info(tp, probe, dev, "not PCI Express\n");
@@ -8518,41 +8263,14 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
chipset = tp->mac_version;
tp->txd_version = rtl_chip_infos[chipset].txd_version;
- RTL_W8(Cfg9346, Cfg9346_Unlock);
- RTL_W8(Config1, RTL_R8(Config1) | PMEnable);
- RTL_W8(Config5, RTL_R8(Config5) & (BWF | MWF | UWF | LanWake | PMEStatus));
- switch (tp->mac_version) {
- case RTL_GIGA_MAC_VER_34:
- case RTL_GIGA_MAC_VER_35:
- case RTL_GIGA_MAC_VER_36:
- case RTL_GIGA_MAC_VER_37:
- case RTL_GIGA_MAC_VER_38:
- case RTL_GIGA_MAC_VER_40:
- case RTL_GIGA_MAC_VER_41:
- case RTL_GIGA_MAC_VER_42:
- case RTL_GIGA_MAC_VER_43:
- case RTL_GIGA_MAC_VER_44:
- case RTL_GIGA_MAC_VER_45:
- case RTL_GIGA_MAC_VER_46:
- case RTL_GIGA_MAC_VER_47:
- case RTL_GIGA_MAC_VER_48:
- case RTL_GIGA_MAC_VER_49:
- case RTL_GIGA_MAC_VER_50:
- case RTL_GIGA_MAC_VER_51:
- if (rtl_eri_read(tp, 0xdc, ERIAR_EXGMAC) & MagicPacket_v2)
- tp->features |= RTL_FEATURE_WOL;
- if ((RTL_R8(Config3) & LinkUp) != 0)
- tp->features |= RTL_FEATURE_WOL;
- break;
- default:
- if ((RTL_R8(Config3) & (LinkUp | MagicPacket)) != 0)
- tp->features |= RTL_FEATURE_WOL;
- break;
+ rc = rtl_alloc_irq(tp);
+ if (rc < 0) {
+ netif_err(tp, probe, dev, "Can't allocate interrupt\n");
+ return rc;
}
- if ((RTL_R8(Config5) & (UWF | BWF | MWF)) != 0)
- tp->features |= RTL_FEATURE_WOL;
- tp->features |= rtl_try_msi(tp, cfg);
- RTL_W8(Cfg9346, Cfg9346_Lock);
+
+ /* override BIOS settings, use userspace tools to enable WOL */
+ __rtl8169_set_wol(tp, 0);
if (rtl_tbi_enabled(tp)) {
tp->set_speed = rtl8169_set_speed_tbi;
@@ -8600,7 +8318,7 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
rtl_rar_set(tp, (u8 *)mac_addr);
}
for (i = 0; i < ETH_ALEN; i++)
- dev->dev_addr[i] = RTL_R8(MAC0 + i);
+ dev->dev_addr[i] = RTL_R8(tp, MAC0 + i);
dev->ethtool_ops = &rtl8169_ethtool_ops;
dev->watchdog_timeo = RTL8169_TX_TIMEOUT;
@@ -8660,15 +8378,16 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (!tp->counters)
return -ENOMEM;
+ pci_set_drvdata(pdev, dev);
+
rc = register_netdev(dev);
if (rc < 0)
return rc;
- pci_set_drvdata(pdev, dev);
-
netif_info(tp, probe, dev, "%s at 0x%p, %pM, XID %08x IRQ %d\n",
- rtl_chip_infos[chipset].name, ioaddr, dev->dev_addr,
- (u32)(RTL_R32(TxConfig) & 0x9cf0f8ff), pdev->irq);
+ rtl_chip_infos[chipset].name, tp->mmio_addr, dev->dev_addr,
+ (u32)(RTL_R32(tp, TxConfig) & 0x9cf0f8ff),
+ pci_irq_vector(pdev, 0));
if (rtl_chip_infos[chipset].jumbo_max != JUMBO_1K) {
netif_info(tp, probe, dev, "jumbo features [frames: %d bytes, "
"tx checksumming: %s]\n",
@@ -8676,15 +8395,8 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
rtl_chip_infos[chipset].jumbo_tx_csum ? "ok" : "ko");
}
- if ((tp->mac_version == RTL_GIGA_MAC_VER_27 ||
- tp->mac_version == RTL_GIGA_MAC_VER_28 ||
- tp->mac_version == RTL_GIGA_MAC_VER_31 ||
- tp->mac_version == RTL_GIGA_MAC_VER_49 ||
- tp->mac_version == RTL_GIGA_MAC_VER_50 ||
- tp->mac_version == RTL_GIGA_MAC_VER_51) &&
- r8168_check_dash(tp)) {
+ if (r8168_check_dash(tp))
rtl8168_driver_start(tp);
- }
netif_carrier_off(dev);
diff --git a/drivers/net/ethernet/renesas/ravb.h b/drivers/net/ethernet/renesas/ravb.h
index 96a27b00c90e..b81f4faf7b10 100644
--- a/drivers/net/ethernet/renesas/ravb.h
+++ b/drivers/net/ethernet/renesas/ravb.h
@@ -1018,6 +1018,7 @@ struct ravb_private {
u32 dirty_rx[NUM_RX_QUEUE]; /* Producer ring indices */
u32 cur_tx[NUM_TX_QUEUE];
u32 dirty_tx[NUM_TX_QUEUE];
+ u32 rx_buf_sz; /* Based on MTU+slack. */
struct napi_struct napi[NUM_RX_QUEUE];
struct work_struct work;
/* MII transceiver section. */
diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c
index a95fbd5510d9..68f122140966 100644
--- a/drivers/net/ethernet/renesas/ravb_main.c
+++ b/drivers/net/ethernet/renesas/ravb_main.c
@@ -238,7 +238,7 @@ static void ravb_ring_free(struct net_device *ndev, int q)
le32_to_cpu(desc->dptr)))
dma_unmap_single(ndev->dev.parent,
le32_to_cpu(desc->dptr),
- PKT_BUF_SZ,
+ priv->rx_buf_sz,
DMA_FROM_DEVICE);
}
ring_size = sizeof(struct ravb_ex_rx_desc) *
@@ -300,9 +300,9 @@ static void ravb_ring_format(struct net_device *ndev, int q)
for (i = 0; i < priv->num_rx_ring[q]; i++) {
/* RX descriptor */
rx_desc = &priv->rx_ring[q][i];
- rx_desc->ds_cc = cpu_to_le16(PKT_BUF_SZ);
+ rx_desc->ds_cc = cpu_to_le16(priv->rx_buf_sz);
dma_addr = dma_map_single(ndev->dev.parent, priv->rx_skb[q][i]->data,
- PKT_BUF_SZ,
+ priv->rx_buf_sz,
DMA_FROM_DEVICE);
/* We just set the data size to 0 for a failed mapping which
* should prevent DMA from happening...
@@ -346,6 +346,9 @@ static int ravb_ring_init(struct net_device *ndev, int q)
int ring_size;
int i;
+ priv->rx_buf_sz = (ndev->mtu <= 1492 ? PKT_BUF_SZ : ndev->mtu) +
+ ETH_HLEN + VLAN_HLEN;
+
/* Allocate RX and TX skb rings */
priv->rx_skb[q] = kcalloc(priv->num_rx_ring[q],
sizeof(*priv->rx_skb[q]), GFP_KERNEL);
@@ -355,7 +358,7 @@ static int ravb_ring_init(struct net_device *ndev, int q)
goto error;
for (i = 0; i < priv->num_rx_ring[q]; i++) {
- skb = netdev_alloc_skb(ndev, PKT_BUF_SZ + RAVB_ALIGN - 1);
+ skb = netdev_alloc_skb(ndev, priv->rx_buf_sz + RAVB_ALIGN - 1);
if (!skb)
goto error;
ravb_set_buffer_align(skb);
@@ -586,7 +589,7 @@ static bool ravb_rx(struct net_device *ndev, int *quota, int q)
skb = priv->rx_skb[q][entry];
priv->rx_skb[q][entry] = NULL;
dma_unmap_single(ndev->dev.parent, le32_to_cpu(desc->dptr),
- PKT_BUF_SZ,
+ priv->rx_buf_sz,
DMA_FROM_DEVICE);
get_ts &= (q == RAVB_NC) ?
RAVB_RXTSTAMP_TYPE_V2_L2_EVENT :
@@ -619,11 +622,12 @@ static bool ravb_rx(struct net_device *ndev, int *quota, int q)
for (; priv->cur_rx[q] - priv->dirty_rx[q] > 0; priv->dirty_rx[q]++) {
entry = priv->dirty_rx[q] % priv->num_rx_ring[q];
desc = &priv->rx_ring[q][entry];
- desc->ds_cc = cpu_to_le16(PKT_BUF_SZ);
+ desc->ds_cc = cpu_to_le16(priv->rx_buf_sz);
if (!priv->rx_skb[q][entry]) {
skb = netdev_alloc_skb(ndev,
- PKT_BUF_SZ + RAVB_ALIGN - 1);
+ priv->rx_buf_sz +
+ RAVB_ALIGN - 1);
if (!skb)
break; /* Better luck next round. */
ravb_set_buffer_align(skb);
@@ -1854,6 +1858,17 @@ static int ravb_do_ioctl(struct net_device *ndev, struct ifreq *req, int cmd)
return phy_mii_ioctl(phydev, req, cmd);
}
+static int ravb_change_mtu(struct net_device *ndev, int new_mtu)
+{
+ if (netif_running(ndev))
+ return -EBUSY;
+
+ ndev->mtu = new_mtu;
+ netdev_update_features(ndev);
+
+ return 0;
+}
+
static void ravb_set_rx_csum(struct net_device *ndev, bool enable)
{
struct ravb_private *priv = netdev_priv(ndev);
@@ -1895,6 +1910,7 @@ static const struct net_device_ops ravb_netdev_ops = {
.ndo_set_rx_mode = ravb_set_rx_mode,
.ndo_tx_timeout = ravb_tx_timeout,
.ndo_do_ioctl = ravb_do_ioctl,
+ .ndo_change_mtu = ravb_change_mtu,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_set_features = ravb_set_features,
@@ -2117,6 +2133,9 @@ static int ravb_probe(struct platform_device *pdev)
goto out_release;
}
+ ndev->max_mtu = 2048 - (ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN);
+ ndev->min_mtu = ETH_MIN_MTU;
+
/* Set function */
ndev->netdev_ops = &ravb_netdev_ops;
ndev->ethtool_ops = &ravb_ethtool_ops;
diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c
index 14c839bb09e7..b6b90a6314e3 100644
--- a/drivers/net/ethernet/renesas/sh_eth.c
+++ b/drivers/net/ethernet/renesas/sh_eth.c
@@ -123,8 +123,8 @@ static const u16 sh_eth_offset_gigabit[SH_ETH_MAX_REGISTER_OFFSET] = {
[TSU_FWSL0] = 0x0030,
[TSU_FWSL1] = 0x0034,
[TSU_FWSLC] = 0x0038,
- [TSU_QTAG0] = 0x0040,
- [TSU_QTAG1] = 0x0044,
+ [TSU_QTAGM0] = 0x0040,
+ [TSU_QTAGM1] = 0x0044,
[TSU_FWSR] = 0x0050,
[TSU_FWINMK] = 0x0054,
[TSU_ADQT0] = 0x0048,
@@ -450,16 +450,6 @@ static u32 sh_eth_tsu_read(struct sh_eth_private *mdp, int enum_index)
return ioread32(mdp->tsu_addr + mdp->reg_offset[enum_index]);
}
-static bool sh_eth_is_gether(struct sh_eth_private *mdp)
-{
- return mdp->reg_offset == sh_eth_offset_gigabit;
-}
-
-static bool sh_eth_is_rz_fast_ether(struct sh_eth_private *mdp)
-{
- return mdp->reg_offset == sh_eth_offset_fast_rz;
-}
-
static void sh_eth_select_mii(struct net_device *ndev)
{
struct sh_eth_private *mdp = netdev_priv(ndev);
@@ -501,6 +491,62 @@ static void sh_eth_chip_reset(struct net_device *ndev)
mdelay(1);
}
+static int sh_eth_soft_reset(struct net_device *ndev)
+{
+ sh_eth_modify(ndev, EDMR, EDMR_SRST_ETHER, EDMR_SRST_ETHER);
+ mdelay(3);
+ sh_eth_modify(ndev, EDMR, EDMR_SRST_ETHER, 0);
+
+ return 0;
+}
+
+static int sh_eth_check_soft_reset(struct net_device *ndev)
+{
+ int cnt;
+
+ for (cnt = 100; cnt > 0; cnt--) {
+ if (!(sh_eth_read(ndev, EDMR) & EDMR_SRST_GETHER))
+ return 0;
+ mdelay(1);
+ }
+
+ netdev_err(ndev, "Device reset failed\n");
+ return -ETIMEDOUT;
+}
+
+static int sh_eth_soft_reset_gether(struct net_device *ndev)
+{
+ struct sh_eth_private *mdp = netdev_priv(ndev);
+ int ret;
+
+ sh_eth_write(ndev, EDSR_ENALL, EDSR);
+ sh_eth_modify(ndev, EDMR, EDMR_SRST_GETHER, EDMR_SRST_GETHER);
+
+ ret = sh_eth_check_soft_reset(ndev);
+ if (ret)
+ return ret;
+
+ /* Table Init */
+ sh_eth_write(ndev, 0, TDLAR);
+ sh_eth_write(ndev, 0, TDFAR);
+ sh_eth_write(ndev, 0, TDFXR);
+ sh_eth_write(ndev, 0, TDFFR);
+ sh_eth_write(ndev, 0, RDLAR);
+ sh_eth_write(ndev, 0, RDFAR);
+ sh_eth_write(ndev, 0, RDFXR);
+ sh_eth_write(ndev, 0, RDFFR);
+
+ /* Reset HW CRC register */
+ if (mdp->cd->hw_checksum)
+ sh_eth_write(ndev, 0, CSMR);
+
+ /* Select MII mode */
+ if (mdp->cd->select_mii)
+ sh_eth_select_mii(ndev);
+
+ return ret;
+}
+
static void sh_eth_set_rate_gether(struct net_device *ndev)
{
struct sh_eth_private *mdp = netdev_priv(ndev);
@@ -521,11 +567,14 @@ static void sh_eth_set_rate_gether(struct net_device *ndev)
#ifdef CONFIG_OF
/* R7S72100 */
static struct sh_eth_cpu_data r7s72100_data = {
+ .soft_reset = sh_eth_soft_reset_gether,
+
.chip_reset = sh_eth_chip_reset,
.set_duplex = sh_eth_set_duplex,
.register_type = SH_ETH_REG_FAST_RZ,
+ .edtrr_trns = EDTRR_TRNS_GETHER,
.ecsr_value = ECSR_ICD,
.ecsipr_value = ECSIPR_ICDIP,
.eesipr_value = EESIPR_TWB1IP | EESIPR_TWBIP | EESIPR_TC1IP |
@@ -552,8 +601,10 @@ static struct sh_eth_cpu_data r7s72100_data = {
.rpadir_value = 2 << 16,
.no_trimd = 1,
.no_ade = 1,
+ .xdfar_rw = 1,
.hw_checksum = 1,
.tsu = 1,
+ .no_tx_cntrs = 1,
};
static void sh_eth_chip_reset_r8a7740(struct net_device *ndev)
@@ -565,12 +616,15 @@ static void sh_eth_chip_reset_r8a7740(struct net_device *ndev)
/* R8A7740 */
static struct sh_eth_cpu_data r8a7740_data = {
+ .soft_reset = sh_eth_soft_reset_gether,
+
.chip_reset = sh_eth_chip_reset_r8a7740,
.set_duplex = sh_eth_set_duplex,
.set_rate = sh_eth_set_rate_gether,
.register_type = SH_ETH_REG_GIGABIT,
+ .edtrr_trns = EDTRR_TRNS_GETHER,
.ecsr_value = ECSR_ICD | ECSR_MPD,
.ecsipr_value = ECSIPR_LCHNGIP | ECSIPR_ICDIP | ECSIPR_MPDIP,
.eesipr_value = EESIPR_RFCOFIP | EESIPR_ECIIP |
@@ -597,10 +651,12 @@ static struct sh_eth_cpu_data r8a7740_data = {
.rpadir_value = 2 << 16,
.no_trimd = 1,
.no_ade = 1,
+ .xdfar_rw = 1,
.hw_checksum = 1,
.tsu = 1,
.select_mii = 1,
.magic = 1,
+ .cexcr = 1,
};
/* There is CPU dependent code */
@@ -620,11 +676,14 @@ static void sh_eth_set_rate_rcar(struct net_device *ndev)
/* R-Car Gen1 */
static struct sh_eth_cpu_data rcar_gen1_data = {
+ .soft_reset = sh_eth_soft_reset,
+
.set_duplex = sh_eth_set_duplex,
.set_rate = sh_eth_set_rate_rcar,
.register_type = SH_ETH_REG_FAST_RCAR,
+ .edtrr_trns = EDTRR_TRNS_ETHER,
.ecsr_value = ECSR_PSRTO | ECSR_LCHNG | ECSR_ICD,
.ecsipr_value = ECSIPR_PSRTOIP | ECSIPR_LCHNGIP | ECSIPR_ICDIP,
.eesipr_value = EESIPR_RFCOFIP | EESIPR_ADEIP | EESIPR_ECIIP |
@@ -643,15 +702,19 @@ static struct sh_eth_cpu_data rcar_gen1_data = {
.mpr = 1,
.tpauser = 1,
.hw_swap = 1,
+ .no_xdfar = 1,
};
/* R-Car Gen2 and RZ/G1 */
static struct sh_eth_cpu_data rcar_gen2_data = {
+ .soft_reset = sh_eth_soft_reset,
+
.set_duplex = sh_eth_set_duplex,
.set_rate = sh_eth_set_rate_rcar,
.register_type = SH_ETH_REG_FAST_RCAR,
+ .edtrr_trns = EDTRR_TRNS_ETHER,
.ecsr_value = ECSR_PSRTO | ECSR_LCHNG | ECSR_ICD | ECSR_MPD,
.ecsipr_value = ECSIPR_PSRTOIP | ECSIPR_LCHNGIP | ECSIPR_ICDIP |
ECSIPR_MPDIP,
@@ -673,6 +736,7 @@ static struct sh_eth_cpu_data rcar_gen2_data = {
.mpr = 1,
.tpauser = 1,
.hw_swap = 1,
+ .no_xdfar = 1,
.rmiimode = 1,
.magic = 1,
};
@@ -694,11 +758,14 @@ static void sh_eth_set_rate_sh7724(struct net_device *ndev)
/* SH7724 */
static struct sh_eth_cpu_data sh7724_data = {
+ .soft_reset = sh_eth_soft_reset,
+
.set_duplex = sh_eth_set_duplex,
.set_rate = sh_eth_set_rate_sh7724,
.register_type = SH_ETH_REG_FAST_SH4,
+ .edtrr_trns = EDTRR_TRNS_ETHER,
.ecsr_value = ECSR_PSRTO | ECSR_LCHNG | ECSR_ICD,
.ecsipr_value = ECSIPR_PSRTOIP | ECSIPR_LCHNGIP | ECSIPR_ICDIP,
.eesipr_value = EESIPR_RFCOFIP | EESIPR_ADEIP | EESIPR_ECIIP |
@@ -736,11 +803,14 @@ static void sh_eth_set_rate_sh7757(struct net_device *ndev)
/* SH7757 */
static struct sh_eth_cpu_data sh7757_data = {
+ .soft_reset = sh_eth_soft_reset,
+
.set_duplex = sh_eth_set_duplex,
.set_rate = sh_eth_set_rate_sh7757,
.register_type = SH_ETH_REG_FAST_SH4,
+ .edtrr_trns = EDTRR_TRNS_ETHER,
.eesipr_value = EESIPR_RFCOFIP | EESIPR_ECIIP |
EESIPR_FTCIP | EESIPR_TDEIP | EESIPR_TFUFIP |
EESIPR_FRIP | EESIPR_RDEIP | EESIPR_RFOFIP |
@@ -763,6 +833,7 @@ static struct sh_eth_cpu_data sh7757_data = {
.rpadir = 1,
.rpadir_value = 2 << 16,
.rtrate = 1,
+ .dual_port = 1,
};
#define SH_GIGA_ETH_BASE 0xfee00000UL
@@ -807,12 +878,15 @@ static void sh_eth_set_rate_giga(struct net_device *ndev)
/* SH7757(GETHERC) */
static struct sh_eth_cpu_data sh7757_data_giga = {
+ .soft_reset = sh_eth_soft_reset_gether,
+
.chip_reset = sh_eth_chip_reset_giga,
.set_duplex = sh_eth_set_duplex,
.set_rate = sh_eth_set_rate_giga,
.register_type = SH_ETH_REG_GIGABIT,
+ .edtrr_trns = EDTRR_TRNS_GETHER,
.ecsr_value = ECSR_ICD | ECSR_MPD,
.ecsipr_value = ECSIPR_LCHNGIP | ECSIPR_ICDIP | ECSIPR_MPDIP,
.eesipr_value = EESIPR_RFCOFIP | EESIPR_ECIIP |
@@ -840,17 +914,23 @@ static struct sh_eth_cpu_data sh7757_data_giga = {
.rpadir_value = 2 << 16,
.no_trimd = 1,
.no_ade = 1,
+ .xdfar_rw = 1,
.tsu = 1,
+ .cexcr = 1,
+ .dual_port = 1,
};
/* SH7734 */
static struct sh_eth_cpu_data sh7734_data = {
+ .soft_reset = sh_eth_soft_reset_gether,
+
.chip_reset = sh_eth_chip_reset,
.set_duplex = sh_eth_set_duplex,
.set_rate = sh_eth_set_rate_gether,
.register_type = SH_ETH_REG_GIGABIT,
+ .edtrr_trns = EDTRR_TRNS_GETHER,
.ecsr_value = ECSR_ICD | ECSR_MPD,
.ecsipr_value = ECSIPR_LCHNGIP | ECSIPR_ICDIP | ECSIPR_MPDIP,
.eesipr_value = EESIPR_RFCOFIP | EESIPR_ECIIP |
@@ -873,20 +953,25 @@ static struct sh_eth_cpu_data sh7734_data = {
.hw_swap = 1,
.no_trimd = 1,
.no_ade = 1,
+ .xdfar_rw = 1,
.tsu = 1,
.hw_checksum = 1,
.select_mii = 1,
.magic = 1,
+ .cexcr = 1,
};
/* SH7763 */
static struct sh_eth_cpu_data sh7763_data = {
+ .soft_reset = sh_eth_soft_reset_gether,
+
.chip_reset = sh_eth_chip_reset,
.set_duplex = sh_eth_set_duplex,
.set_rate = sh_eth_set_rate_gether,
.register_type = SH_ETH_REG_GIGABIT,
+ .edtrr_trns = EDTRR_TRNS_GETHER,
.ecsr_value = ECSR_ICD | ECSR_MPD,
.ecsipr_value = ECSIPR_LCHNGIP | ECSIPR_ICDIP | ECSIPR_MPDIP,
.eesipr_value = EESIPR_RFCOFIP | EESIPR_ECIIP |
@@ -908,14 +993,20 @@ static struct sh_eth_cpu_data sh7763_data = {
.hw_swap = 1,
.no_trimd = 1,
.no_ade = 1,
+ .xdfar_rw = 1,
.tsu = 1,
.irq_flags = IRQF_SHARED,
.magic = 1,
+ .cexcr = 1,
+ .dual_port = 1,
};
static struct sh_eth_cpu_data sh7619_data = {
+ .soft_reset = sh_eth_soft_reset,
+
.register_type = SH_ETH_REG_FAST_SH3_SH2,
+ .edtrr_trns = EDTRR_TRNS_ETHER,
.eesipr_value = EESIPR_RFCOFIP | EESIPR_ECIIP |
EESIPR_FTCIP | EESIPR_TDEIP | EESIPR_TFUFIP |
EESIPR_FRIP | EESIPR_RDEIP | EESIPR_RFOFIP |
@@ -932,8 +1023,11 @@ static struct sh_eth_cpu_data sh7619_data = {
};
static struct sh_eth_cpu_data sh771x_data = {
+ .soft_reset = sh_eth_soft_reset,
+
.register_type = SH_ETH_REG_FAST_SH3_SH2,
+ .edtrr_trns = EDTRR_TRNS_ETHER,
.eesipr_value = EESIPR_RFCOFIP | EESIPR_ECIIP |
EESIPR_FTCIP | EESIPR_TDEIP | EESIPR_TFUFIP |
EESIPR_FRIP | EESIPR_RDEIP | EESIPR_RFOFIP |
@@ -943,6 +1037,7 @@ static struct sh_eth_cpu_data sh771x_data = {
EESIPR_RRFIP | EESIPR_RTLFIP | EESIPR_RTSFIP |
EESIPR_PREIP | EESIPR_CERFIP,
.tsu = 1,
+ .dual_port = 1,
};
static void sh_eth_set_default_cpu_data(struct sh_eth_cpu_data *cd)
@@ -970,63 +1065,6 @@ static void sh_eth_set_default_cpu_data(struct sh_eth_cpu_data *cd)
cd->trscer_err_mask = DEFAULT_TRSCER_ERR_MASK;
}
-static int sh_eth_check_reset(struct net_device *ndev)
-{
- int ret = 0;
- int cnt = 100;
-
- while (cnt > 0) {
- if (!(sh_eth_read(ndev, EDMR) & EDMR_SRST_GETHER))
- break;
- mdelay(1);
- cnt--;
- }
- if (cnt <= 0) {
- netdev_err(ndev, "Device reset failed\n");
- ret = -ETIMEDOUT;
- }
- return ret;
-}
-
-static int sh_eth_reset(struct net_device *ndev)
-{
- struct sh_eth_private *mdp = netdev_priv(ndev);
- int ret = 0;
-
- if (sh_eth_is_gether(mdp) || sh_eth_is_rz_fast_ether(mdp)) {
- sh_eth_write(ndev, EDSR_ENALL, EDSR);
- sh_eth_modify(ndev, EDMR, EDMR_SRST_GETHER, EDMR_SRST_GETHER);
-
- ret = sh_eth_check_reset(ndev);
- if (ret)
- return ret;
-
- /* Table Init */
- sh_eth_write(ndev, 0x0, TDLAR);
- sh_eth_write(ndev, 0x0, TDFAR);
- sh_eth_write(ndev, 0x0, TDFXR);
- sh_eth_write(ndev, 0x0, TDFFR);
- sh_eth_write(ndev, 0x0, RDLAR);
- sh_eth_write(ndev, 0x0, RDFAR);
- sh_eth_write(ndev, 0x0, RDFXR);
- sh_eth_write(ndev, 0x0, RDFFR);
-
- /* Reset HW CRC register */
- if (mdp->cd->hw_checksum)
- sh_eth_write(ndev, 0x0, CSMR);
-
- /* Select MII mode */
- if (mdp->cd->select_mii)
- sh_eth_select_mii(ndev);
- } else {
- sh_eth_modify(ndev, EDMR, EDMR_SRST_ETHER, EDMR_SRST_ETHER);
- mdelay(3);
- sh_eth_modify(ndev, EDMR, EDMR_SRST_ETHER, 0);
- }
-
- return ret;
-}
-
static void sh_eth_set_receive_align(struct sk_buff *skb)
{
uintptr_t reserve = (uintptr_t)skb->data & (SH_ETH_RX_ALIGN - 1);
@@ -1069,14 +1107,6 @@ static void read_mac_address(struct net_device *ndev, unsigned char *mac)
}
}
-static u32 sh_eth_get_edtrr_trns(struct sh_eth_private *mdp)
-{
- if (sh_eth_is_gether(mdp) || sh_eth_is_rz_fast_ether(mdp))
- return EDTRR_TRNS_GETHER;
- else
- return EDTRR_TRNS_ETHER;
-}
-
struct bb_info {
void (*set_gate)(void *addr);
struct mdiobb_ctrl ctrl;
@@ -1273,8 +1303,7 @@ static void sh_eth_ring_format(struct net_device *ndev)
/* Rx descriptor address set */
if (i == 0) {
sh_eth_write(ndev, mdp->rx_desc_dma, RDLAR);
- if (sh_eth_is_gether(mdp) ||
- sh_eth_is_rz_fast_ether(mdp))
+ if (mdp->cd->xdfar_rw)
sh_eth_write(ndev, mdp->rx_desc_dma, RDFAR);
}
}
@@ -1296,8 +1325,7 @@ static void sh_eth_ring_format(struct net_device *ndev)
if (i == 0) {
/* Tx descriptor address set */
sh_eth_write(ndev, mdp->tx_desc_dma, TDLAR);
- if (sh_eth_is_gether(mdp) ||
- sh_eth_is_rz_fast_ether(mdp))
+ if (mdp->cd->xdfar_rw)
sh_eth_write(ndev, mdp->tx_desc_dma, TDFAR);
}
}
@@ -1362,7 +1390,7 @@ static int sh_eth_dev_init(struct net_device *ndev)
int ret;
/* Soft Reset */
- ret = sh_eth_reset(ndev);
+ ret = mdp->cd->soft_reset(ndev);
if (ret)
return ret;
@@ -1463,7 +1491,7 @@ static void sh_eth_dev_exit(struct net_device *ndev)
*/
msleep(2); /* max frame time at 10 Mbps < 1250 us */
sh_eth_get_stats(ndev);
- sh_eth_reset(ndev);
+ mdp->cd->soft_reset(ndev);
/* Set MAC address again */
update_mac_address(ndev);
@@ -1589,8 +1617,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
/* If we don't need to check status, don't. -KDU */
if (!(sh_eth_read(ndev, EDRRR) & EDRRR_R)) {
/* fix the values for the next receiving if RDE is set */
- if (intr_status & EESR_RDE &&
- mdp->reg_offset[RDFAR] != SH_ETH_OFFSET_INVALID) {
+ if (intr_status & EESR_RDE && !mdp->cd->no_xdfar) {
u32 count = (sh_eth_read(ndev, RDFAR) -
sh_eth_read(ndev, RDLAR)) >> 4;
@@ -1716,9 +1743,9 @@ static void sh_eth_error(struct net_device *ndev, u32 intr_status)
sh_eth_tx_free(ndev, true);
/* SH7712 BUG */
- if (edtrr ^ sh_eth_get_edtrr_trns(mdp)) {
+ if (edtrr ^ mdp->cd->edtrr_trns) {
/* tx dma start */
- sh_eth_write(ndev, sh_eth_get_edtrr_trns(mdp), EDTRR);
+ sh_eth_write(ndev, mdp->cd->edtrr_trns, EDTRR);
}
/* wakeup */
netif_wake_queue(ndev);
@@ -2112,8 +2139,6 @@ static size_t __sh_eth_get_regs(struct net_device *ndev, u32 *buf)
add_tsu_reg(TSU_FWSL0);
add_tsu_reg(TSU_FWSL1);
add_tsu_reg(TSU_FWSLC);
- add_tsu_reg(TSU_QTAG0);
- add_tsu_reg(TSU_QTAG1);
add_tsu_reg(TSU_QTAGM0);
add_tsu_reg(TSU_QTAGM1);
add_tsu_reg(TSU_FWSR);
@@ -2128,22 +2153,17 @@ static size_t __sh_eth_get_regs(struct net_device *ndev, u32 *buf)
add_tsu_reg(TSU_POST2);
add_tsu_reg(TSU_POST3);
add_tsu_reg(TSU_POST4);
- if (mdp->reg_offset[TSU_ADRH0] != SH_ETH_OFFSET_INVALID) {
- /* This is the start of a table, not just a single
- * register.
- */
- if (buf) {
- unsigned int i;
-
- mark_reg_valid(TSU_ADRH0);
- for (i = 0; i < SH_ETH_TSU_CAM_ENTRIES * 2; i++)
- *buf++ = ioread32(
- mdp->tsu_addr +
- mdp->reg_offset[TSU_ADRH0] +
- i * 4);
- }
- len += SH_ETH_TSU_CAM_ENTRIES * 2;
+ /* This is the start of a table, not just a single register. */
+ if (buf) {
+ unsigned int i;
+
+ mark_reg_valid(TSU_ADRH0);
+ for (i = 0; i < SH_ETH_TSU_CAM_ENTRIES * 2; i++)
+ *buf++ = ioread32(mdp->tsu_addr +
+ mdp->reg_offset[TSU_ADRH0] +
+ i * 4);
}
+ len += SH_ETH_TSU_CAM_ENTRIES * 2;
}
#undef mark_reg_valid
@@ -2479,8 +2499,8 @@ static int sh_eth_start_xmit(struct sk_buff *skb, struct net_device *ndev)
mdp->cur_tx++;
- if (!(sh_eth_read(ndev, EDTRR) & sh_eth_get_edtrr_trns(mdp)))
- sh_eth_write(ndev, sh_eth_get_edtrr_trns(mdp), EDTRR);
+ if (!(sh_eth_read(ndev, EDTRR) & mdp->cd->edtrr_trns))
+ sh_eth_write(ndev, mdp->cd->edtrr_trns, EDTRR);
return NETDEV_TX_OK;
}
@@ -2505,7 +2525,7 @@ static struct net_device_stats *sh_eth_get_stats(struct net_device *ndev)
{
struct sh_eth_private *mdp = netdev_priv(ndev);
- if (sh_eth_is_rz_fast_ether(mdp))
+ if (mdp->cd->no_tx_cntrs)
return &ndev->stats;
if (!mdp->is_opened)
@@ -2515,7 +2535,7 @@ static struct net_device_stats *sh_eth_get_stats(struct net_device *ndev)
sh_eth_update_stat(ndev, &ndev->stats.collisions, CDCR);
sh_eth_update_stat(ndev, &ndev->stats.tx_carrier_errors, LCCR);
- if (sh_eth_is_gether(mdp)) {
+ if (mdp->cd->cexcr) {
sh_eth_update_stat(ndev, &ndev->stats.tx_carrier_errors,
CERCR);
sh_eth_update_stat(ndev, &ndev->stats.tx_carrier_errors,
@@ -2932,7 +2952,7 @@ static int sh_eth_vlan_rx_kill_vid(struct net_device *ndev,
/* SuperH's TSU register init function */
static void sh_eth_tsu_init(struct sh_eth_private *mdp)
{
- if (sh_eth_is_rz_fast_ether(mdp)) {
+ if (!mdp->cd->dual_port) {
sh_eth_tsu_write(mdp, 0, TSU_TEN); /* Disable all CAM entry */
sh_eth_tsu_write(mdp, TSU_FWSLC_POSTENU | TSU_FWSLC_POSTENL,
TSU_FWSLC); /* Enable POST registers */
@@ -2949,13 +2969,8 @@ static void sh_eth_tsu_init(struct sh_eth_private *mdp)
sh_eth_tsu_write(mdp, 0, TSU_FWSL0);
sh_eth_tsu_write(mdp, 0, TSU_FWSL1);
sh_eth_tsu_write(mdp, TSU_FWSLC_POSTENU | TSU_FWSLC_POSTENL, TSU_FWSLC);
- if (sh_eth_is_gether(mdp)) {
- sh_eth_tsu_write(mdp, 0, TSU_QTAG0); /* Disable QTAG(0->1) */
- sh_eth_tsu_write(mdp, 0, TSU_QTAG1); /* Disable QTAG(1->0) */
- } else {
- sh_eth_tsu_write(mdp, 0, TSU_QTAGM0); /* Disable QTAG(0->1) */
- sh_eth_tsu_write(mdp, 0, TSU_QTAGM1); /* Disable QTAG(1->0) */
- }
+ sh_eth_tsu_write(mdp, 0, TSU_QTAGM0); /* Disable QTAG(0->1) */
+ sh_eth_tsu_write(mdp, 0, TSU_QTAGM1); /* Disable QTAG(1->0) */
sh_eth_tsu_write(mdp, 0, TSU_FWSR); /* all interrupt status clear */
sh_eth_tsu_write(mdp, 0, TSU_FWINMK); /* Disable all interrupt */
sh_eth_tsu_write(mdp, 0, TSU_TEN); /* Disable all CAM entry */
diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h
index e5fe70134690..a5b792ce2ae7 100644
--- a/drivers/net/ethernet/renesas/sh_eth.h
+++ b/drivers/net/ethernet/renesas/sh_eth.h
@@ -118,8 +118,8 @@ enum {
TSU_FWSL0,
TSU_FWSL1,
TSU_FWSLC,
- TSU_QTAG0,
- TSU_QTAG1,
+ TSU_QTAG0, /* Same as TSU_QTAGM0 */
+ TSU_QTAG1, /* Same as TSU_QTAGM1 */
TSU_QTAGM0,
TSU_QTAGM1,
TSU_FWSR,
@@ -469,6 +469,9 @@ struct sh_eth_rxdesc {
/* This structure is used by each CPU dependency handling. */
struct sh_eth_cpu_data {
+ /* mandatory functions */
+ int (*soft_reset)(struct net_device *ndev);
+
/* optional functions */
void (*chip_reset)(struct net_device *ndev);
void (*set_duplex)(struct net_device *ndev);
@@ -476,6 +479,7 @@ struct sh_eth_cpu_data {
/* mandatory initialize value */
int register_type;
+ u32 edtrr_trns;
u32 eesipr_value;
/* optional initialize value */
@@ -504,11 +508,16 @@ struct sh_eth_cpu_data {
unsigned rpadir:1; /* E-DMAC have RPADIR */
unsigned no_trimd:1; /* E-DMAC DO NOT have TRIMD */
unsigned no_ade:1; /* E-DMAC DO NOT have ADE bit in EESR */
+ unsigned no_xdfar:1; /* E-DMAC DOES NOT have RDFAR/TDFAR */
+ unsigned xdfar_rw:1; /* E-DMAC has writeable RDFAR/TDFAR */
unsigned hw_checksum:1; /* E-DMAC has CSMR */
unsigned select_mii:1; /* EtherC have RMII_MII (MII select register) */
unsigned rmiimode:1; /* EtherC has RMIIMODE register */
unsigned rtrate:1; /* EtherC has RTRATE register */
unsigned magic:1; /* EtherC has ECMR.MPDE and ECSR.MPD */
+ unsigned no_tx_cntrs:1; /* EtherC DOES NOT have TX error counters */
+ unsigned cexcr:1; /* EtherC has CERCR/CEECR */
+ unsigned dual_port:1; /* Dual EtherC/E-DMAC */
};
struct sh_eth_private {
diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
index fd35d8004a78..a9da1ad4b4f2 100644
--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
@@ -57,9 +57,9 @@
static int debug = -1;
static int eee_timer = SXGBE_DEFAULT_LPI_TIMER;
-module_param(eee_timer, int, S_IRUGO | S_IWUSR);
+module_param(eee_timer, int, 0644);
-module_param(debug, int, S_IRUGO | S_IWUSR);
+module_param(debug, int, 0644);
static const u32 default_msg_level = (NETIF_MSG_DRV | NETIF_MSG_PROBE |
NETIF_MSG_LINK | NETIF_MSG_IFUP |
NETIF_MSG_IFDOWN | NETIF_MSG_TIMER);
diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c
index 75fbf58e421c..63036d9bf3e6 100644
--- a/drivers/net/ethernet/sfc/ef10.c
+++ b/drivers/net/ethernet/sfc/ef10.c
@@ -28,9 +28,6 @@ enum {
EFX_EF10_TEST = 1,
EFX_EF10_REFILL,
};
-
-/* The reserved RSS context value */
-#define EFX_EF10_RSS_CONTEXT_INVALID 0xffffffff
/* The maximum size of a shared RSS context */
/* TODO: this should really be from the mcdi protocol export */
#define EFX_EF10_MAX_SHARED_RSS_CONTEXT_SIZE 64UL
@@ -99,17 +96,15 @@ struct efx_ef10_filter_table {
MC_CMD_GET_PARSER_DISP_INFO_OUT_SUPPORTED_MATCHES_MAXNUM * 2];
unsigned int rx_match_count;
+ struct rw_semaphore lock; /* Protects entries */
struct {
unsigned long spec; /* pointer to spec plus flag bits */
-/* BUSY flag indicates that an update is in progress. AUTO_OLD is
- * used to mark and sweep MAC filters for the device address lists.
- */
-#define EFX_EF10_FILTER_FLAG_BUSY 1UL
+/* AUTO_OLD is used to mark and sweep MAC filters for the device address lists. */
+/* unused flag 1UL */
#define EFX_EF10_FILTER_FLAG_AUTO_OLD 2UL
#define EFX_EF10_FILTER_FLAGS 3UL
u64 handle; /* firmware handle */
} *entry;
- wait_queue_head_t waitq;
/* Shadow of net_device address lists, guarded by mac_lock */
struct efx_ef10_dev_addr dev_uc_list[EFX_EF10_FILTER_DEV_UC_MAX];
struct efx_ef10_dev_addr dev_mc_list[EFX_EF10_FILTER_DEV_MC_MAX];
@@ -697,7 +692,7 @@ static int efx_ef10_probe(struct efx_nic *efx)
}
nic_data->warm_boot_count = rc;
- nic_data->rx_rss_context = EFX_EF10_RSS_CONTEXT_INVALID;
+ efx->rss_context.context_id = EFX_EF10_RSS_CONTEXT_INVALID;
nic_data->vport_id = EVB_PORT_ID_ASSIGNED;
@@ -1489,8 +1484,8 @@ static int efx_ef10_init_nic(struct efx_nic *efx)
}
/* don't fail init if RSS setup doesn't work */
- rc = efx->type->rx_push_rss_config(efx, false, efx->rx_indir_table, NULL);
- efx->rss_active = (rc == 0);
+ rc = efx->type->rx_push_rss_config(efx, false,
+ efx->rss_context.rx_indir_table, NULL);
return 0;
}
@@ -1504,10 +1499,11 @@ static void efx_ef10_reset_mc_allocations(struct efx_nic *efx)
/* All our allocations have been reset */
nic_data->must_realloc_vis = true;
+ nic_data->must_restore_rss_contexts = true;
nic_data->must_restore_filters = true;
nic_data->must_restore_piobufs = true;
efx_ef10_forget_old_piobufs(efx);
- nic_data->rx_rss_context = EFX_EF10_RSS_CONTEXT_INVALID;
+ efx->rss_context.context_id = EFX_EF10_RSS_CONTEXT_INVALID;
/* Driver-created vswitches and vports must be re-created */
nic_data->must_probe_vswitching = true;
@@ -1670,7 +1666,6 @@ static const struct efx_hw_stat_desc efx_ef10_stat_desc[EF10_STAT_COUNT] = {
EF10_DMA_STAT(fec_corrected_symbols_lane1, FEC_CORRECTED_SYMBOLS_LANE1),
EF10_DMA_STAT(fec_corrected_symbols_lane2, FEC_CORRECTED_SYMBOLS_LANE2),
EF10_DMA_STAT(fec_corrected_symbols_lane3, FEC_CORRECTED_SYMBOLS_LANE3),
- EF10_DMA_STAT(ctpio_dmabuf_start, CTPIO_DMABUF_START),
EF10_DMA_STAT(ctpio_vi_busy_fallback, CTPIO_VI_BUSY_FALLBACK),
EF10_DMA_STAT(ctpio_long_write_success, CTPIO_LONG_WRITE_SUCCESS),
EF10_DMA_STAT(ctpio_missing_dbell_fail, CTPIO_MISSING_DBELL_FAIL),
@@ -1781,7 +1776,6 @@ static const struct efx_hw_stat_desc efx_ef10_stat_desc[EF10_STAT_COUNT] = {
* These bits are in the second u64 of the raw mask.
*/
#define EF10_CTPIO_STAT_MASK ( \
- (1ULL << (EF10_STAT_ctpio_dmabuf_start - 64)) | \
(1ULL << (EF10_STAT_ctpio_vi_busy_fallback - 64)) | \
(1ULL << (EF10_STAT_ctpio_long_write_success - 64)) | \
(1ULL << (EF10_STAT_ctpio_missing_dbell_fail - 64)) | \
@@ -2703,27 +2697,30 @@ static int efx_ef10_get_rss_flags(struct efx_nic *efx, u32 context, u32 *flags)
* Defaults are 4-tuple for TCP and 2-tuple for UDP and other-IP, so we
* just need to set the UDP ports flags (for both IP versions).
*/
-static void efx_ef10_set_rss_flags(struct efx_nic *efx, u32 context)
+static void efx_ef10_set_rss_flags(struct efx_nic *efx,
+ struct efx_rss_context *ctx)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_RSS_CONTEXT_SET_FLAGS_IN_LEN);
u32 flags;
BUILD_BUG_ON(MC_CMD_RSS_CONTEXT_SET_FLAGS_OUT_LEN != 0);
- if (efx_ef10_get_rss_flags(efx, context, &flags) != 0)
+ if (efx_ef10_get_rss_flags(efx, ctx->context_id, &flags) != 0)
return;
- MCDI_SET_DWORD(inbuf, RSS_CONTEXT_SET_FLAGS_IN_RSS_CONTEXT_ID, context);
+ MCDI_SET_DWORD(inbuf, RSS_CONTEXT_SET_FLAGS_IN_RSS_CONTEXT_ID,
+ ctx->context_id);
flags |= RSS_MODE_HASH_PORTS << MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_UDP_IPV4_RSS_MODE_LBN;
flags |= RSS_MODE_HASH_PORTS << MC_CMD_RSS_CONTEXT_GET_FLAGS_OUT_UDP_IPV6_RSS_MODE_LBN;
MCDI_SET_DWORD(inbuf, RSS_CONTEXT_SET_FLAGS_IN_FLAGS, flags);
if (!efx_mcdi_rpc(efx, MC_CMD_RSS_CONTEXT_SET_FLAGS, inbuf, sizeof(inbuf),
NULL, 0, NULL))
/* Succeeded, so UDP 4-tuple is now enabled */
- efx->rx_hash_udp_4tuple = true;
+ ctx->rx_hash_udp_4tuple = true;
}
-static int efx_ef10_alloc_rss_context(struct efx_nic *efx, u32 *context,
- bool exclusive, unsigned *context_size)
+static int efx_ef10_alloc_rss_context(struct efx_nic *efx, bool exclusive,
+ struct efx_rss_context *ctx,
+ unsigned *context_size)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_RSS_CONTEXT_ALLOC_IN_LEN);
MCDI_DECLARE_BUF(outbuf, MC_CMD_RSS_CONTEXT_ALLOC_OUT_LEN);
@@ -2739,7 +2736,7 @@ static int efx_ef10_alloc_rss_context(struct efx_nic *efx, u32 *context,
EFX_EF10_MAX_SHARED_RSS_CONTEXT_SIZE);
if (!exclusive && rss_spread == 1) {
- *context = EFX_EF10_RSS_CONTEXT_INVALID;
+ ctx->context_id = EFX_EF10_RSS_CONTEXT_INVALID;
if (context_size)
*context_size = 1;
return 0;
@@ -2762,29 +2759,26 @@ static int efx_ef10_alloc_rss_context(struct efx_nic *efx, u32 *context,
if (outlen < MC_CMD_RSS_CONTEXT_ALLOC_OUT_LEN)
return -EIO;
- *context = MCDI_DWORD(outbuf, RSS_CONTEXT_ALLOC_OUT_RSS_CONTEXT_ID);
+ ctx->context_id = MCDI_DWORD(outbuf, RSS_CONTEXT_ALLOC_OUT_RSS_CONTEXT_ID);
if (context_size)
*context_size = rss_spread;
if (nic_data->datapath_caps &
1 << MC_CMD_GET_CAPABILITIES_OUT_ADDITIONAL_RSS_MODES_LBN)
- efx_ef10_set_rss_flags(efx, *context);
+ efx_ef10_set_rss_flags(efx, ctx);
return 0;
}
-static void efx_ef10_free_rss_context(struct efx_nic *efx, u32 context)
+static int efx_ef10_free_rss_context(struct efx_nic *efx, u32 context)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_RSS_CONTEXT_FREE_IN_LEN);
- int rc;
MCDI_SET_DWORD(inbuf, RSS_CONTEXT_FREE_IN_RSS_CONTEXT_ID,
context);
-
- rc = efx_mcdi_rpc(efx, MC_CMD_RSS_CONTEXT_FREE, inbuf, sizeof(inbuf),
+ return efx_mcdi_rpc(efx, MC_CMD_RSS_CONTEXT_FREE, inbuf, sizeof(inbuf),
NULL, 0, NULL);
- WARN_ON(rc != 0);
}
static int efx_ef10_populate_rss_table(struct efx_nic *efx, u32 context,
@@ -2796,15 +2790,15 @@ static int efx_ef10_populate_rss_table(struct efx_nic *efx, u32 context,
MCDI_SET_DWORD(tablebuf, RSS_CONTEXT_SET_TABLE_IN_RSS_CONTEXT_ID,
context);
- BUILD_BUG_ON(ARRAY_SIZE(efx->rx_indir_table) !=
+ BUILD_BUG_ON(ARRAY_SIZE(efx->rss_context.rx_indir_table) !=
MC_CMD_RSS_CONTEXT_SET_TABLE_IN_INDIRECTION_TABLE_LEN);
- /* This iterates over the length of efx->rx_indir_table, but copies
- * bytes from rx_indir_table. That's because the latter is a pointer
- * rather than an array, but should have the same length.
- * The efx->rx_hash_key loop below is similar.
+ /* This iterates over the length of efx->rss_context.rx_indir_table, but
+ * copies bytes from rx_indir_table. That's because the latter is a
+ * pointer rather than an array, but should have the same length.
+ * The efx->rss_context.rx_hash_key loop below is similar.
*/
- for (i = 0; i < ARRAY_SIZE(efx->rx_indir_table); ++i)
+ for (i = 0; i < ARRAY_SIZE(efx->rss_context.rx_indir_table); ++i)
MCDI_PTR(tablebuf,
RSS_CONTEXT_SET_TABLE_IN_INDIRECTION_TABLE)[i] =
(u8) rx_indir_table[i];
@@ -2816,9 +2810,9 @@ static int efx_ef10_populate_rss_table(struct efx_nic *efx, u32 context,
MCDI_SET_DWORD(keybuf, RSS_CONTEXT_SET_KEY_IN_RSS_CONTEXT_ID,
context);
- BUILD_BUG_ON(ARRAY_SIZE(efx->rx_hash_key) !=
+ BUILD_BUG_ON(ARRAY_SIZE(efx->rss_context.rx_hash_key) !=
MC_CMD_RSS_CONTEXT_SET_KEY_IN_TOEPLITZ_KEY_LEN);
- for (i = 0; i < ARRAY_SIZE(efx->rx_hash_key); ++i)
+ for (i = 0; i < ARRAY_SIZE(efx->rss_context.rx_hash_key); ++i)
MCDI_PTR(keybuf, RSS_CONTEXT_SET_KEY_IN_TOEPLITZ_KEY)[i] = key[i];
return efx_mcdi_rpc(efx, MC_CMD_RSS_CONTEXT_SET_KEY, keybuf,
@@ -2827,27 +2821,27 @@ static int efx_ef10_populate_rss_table(struct efx_nic *efx, u32 context,
static void efx_ef10_rx_free_indir_table(struct efx_nic *efx)
{
- struct efx_ef10_nic_data *nic_data = efx->nic_data;
+ int rc;
- if (nic_data->rx_rss_context != EFX_EF10_RSS_CONTEXT_INVALID)
- efx_ef10_free_rss_context(efx, nic_data->rx_rss_context);
- nic_data->rx_rss_context = EFX_EF10_RSS_CONTEXT_INVALID;
+ if (efx->rss_context.context_id != EFX_EF10_RSS_CONTEXT_INVALID) {
+ rc = efx_ef10_free_rss_context(efx, efx->rss_context.context_id);
+ WARN_ON(rc != 0);
+ }
+ efx->rss_context.context_id = EFX_EF10_RSS_CONTEXT_INVALID;
}
static int efx_ef10_rx_push_shared_rss_config(struct efx_nic *efx,
unsigned *context_size)
{
- u32 new_rx_rss_context;
struct efx_ef10_nic_data *nic_data = efx->nic_data;
- int rc = efx_ef10_alloc_rss_context(efx, &new_rx_rss_context,
- false, context_size);
+ int rc = efx_ef10_alloc_rss_context(efx, false, &efx->rss_context,
+ context_size);
if (rc != 0)
return rc;
- nic_data->rx_rss_context = new_rx_rss_context;
nic_data->rx_rss_context_exclusive = false;
- efx_set_default_rx_indir_table(efx);
+ efx_set_default_rx_indir_table(efx, &efx->rss_context);
return 0;
}
@@ -2855,65 +2849,98 @@ static int efx_ef10_rx_push_exclusive_rss_config(struct efx_nic *efx,
const u32 *rx_indir_table,
const u8 *key)
{
+ u32 old_rx_rss_context = efx->rss_context.context_id;
struct efx_ef10_nic_data *nic_data = efx->nic_data;
int rc;
- u32 new_rx_rss_context;
- if (nic_data->rx_rss_context == EFX_EF10_RSS_CONTEXT_INVALID ||
+ if (efx->rss_context.context_id == EFX_EF10_RSS_CONTEXT_INVALID ||
!nic_data->rx_rss_context_exclusive) {
- rc = efx_ef10_alloc_rss_context(efx, &new_rx_rss_context,
- true, NULL);
+ rc = efx_ef10_alloc_rss_context(efx, true, &efx->rss_context,
+ NULL);
if (rc == -EOPNOTSUPP)
return rc;
else if (rc != 0)
goto fail1;
- } else {
- new_rx_rss_context = nic_data->rx_rss_context;
}
- rc = efx_ef10_populate_rss_table(efx, new_rx_rss_context,
+ rc = efx_ef10_populate_rss_table(efx, efx->rss_context.context_id,
rx_indir_table, key);
if (rc != 0)
goto fail2;
- if (nic_data->rx_rss_context != new_rx_rss_context)
- efx_ef10_rx_free_indir_table(efx);
- nic_data->rx_rss_context = new_rx_rss_context;
+ if (efx->rss_context.context_id != old_rx_rss_context &&
+ old_rx_rss_context != EFX_EF10_RSS_CONTEXT_INVALID)
+ WARN_ON(efx_ef10_free_rss_context(efx, old_rx_rss_context) != 0);
nic_data->rx_rss_context_exclusive = true;
- if (rx_indir_table != efx->rx_indir_table)
- memcpy(efx->rx_indir_table, rx_indir_table,
- sizeof(efx->rx_indir_table));
- if (key != efx->rx_hash_key)
- memcpy(efx->rx_hash_key, key, efx->type->rx_hash_key_size);
+ if (rx_indir_table != efx->rss_context.rx_indir_table)
+ memcpy(efx->rss_context.rx_indir_table, rx_indir_table,
+ sizeof(efx->rss_context.rx_indir_table));
+ if (key != efx->rss_context.rx_hash_key)
+ memcpy(efx->rss_context.rx_hash_key, key,
+ efx->type->rx_hash_key_size);
return 0;
fail2:
- if (new_rx_rss_context != nic_data->rx_rss_context)
- efx_ef10_free_rss_context(efx, new_rx_rss_context);
+ if (old_rx_rss_context != efx->rss_context.context_id) {
+ WARN_ON(efx_ef10_free_rss_context(efx, efx->rss_context.context_id) != 0);
+ efx->rss_context.context_id = old_rx_rss_context;
+ }
fail1:
netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
return rc;
}
-static int efx_ef10_rx_pull_rss_config(struct efx_nic *efx)
+static int efx_ef10_rx_push_rss_context_config(struct efx_nic *efx,
+ struct efx_rss_context *ctx,
+ const u32 *rx_indir_table,
+ const u8 *key)
+{
+ int rc;
+
+ WARN_ON(!mutex_is_locked(&efx->rss_lock));
+
+ if (ctx->context_id == EFX_EF10_RSS_CONTEXT_INVALID) {
+ rc = efx_ef10_alloc_rss_context(efx, true, ctx, NULL);
+ if (rc)
+ return rc;
+ }
+
+ if (!rx_indir_table) /* Delete this context */
+ return efx_ef10_free_rss_context(efx, ctx->context_id);
+
+ rc = efx_ef10_populate_rss_table(efx, ctx->context_id,
+ rx_indir_table, key);
+ if (rc)
+ return rc;
+
+ memcpy(ctx->rx_indir_table, rx_indir_table,
+ sizeof(efx->rss_context.rx_indir_table));
+ memcpy(ctx->rx_hash_key, key, efx->type->rx_hash_key_size);
+
+ return 0;
+}
+
+static int efx_ef10_rx_pull_rss_context_config(struct efx_nic *efx,
+ struct efx_rss_context *ctx)
{
- struct efx_ef10_nic_data *nic_data = efx->nic_data;
MCDI_DECLARE_BUF(inbuf, MC_CMD_RSS_CONTEXT_GET_TABLE_IN_LEN);
MCDI_DECLARE_BUF(tablebuf, MC_CMD_RSS_CONTEXT_GET_TABLE_OUT_LEN);
MCDI_DECLARE_BUF(keybuf, MC_CMD_RSS_CONTEXT_GET_KEY_OUT_LEN);
size_t outlen;
int rc, i;
+ WARN_ON(!mutex_is_locked(&efx->rss_lock));
+
BUILD_BUG_ON(MC_CMD_RSS_CONTEXT_GET_TABLE_IN_LEN !=
MC_CMD_RSS_CONTEXT_GET_KEY_IN_LEN);
- if (nic_data->rx_rss_context == EFX_EF10_RSS_CONTEXT_INVALID)
+ if (ctx->context_id == EFX_EF10_RSS_CONTEXT_INVALID)
return -ENOENT;
MCDI_SET_DWORD(inbuf, RSS_CONTEXT_GET_TABLE_IN_RSS_CONTEXT_ID,
- nic_data->rx_rss_context);
- BUILD_BUG_ON(ARRAY_SIZE(efx->rx_indir_table) !=
+ ctx->context_id);
+ BUILD_BUG_ON(ARRAY_SIZE(ctx->rx_indir_table) !=
MC_CMD_RSS_CONTEXT_GET_TABLE_OUT_INDIRECTION_TABLE_LEN);
rc = efx_mcdi_rpc(efx, MC_CMD_RSS_CONTEXT_GET_TABLE, inbuf, sizeof(inbuf),
tablebuf, sizeof(tablebuf), &outlen);
@@ -2923,13 +2950,13 @@ static int efx_ef10_rx_pull_rss_config(struct efx_nic *efx)
if (WARN_ON(outlen != MC_CMD_RSS_CONTEXT_GET_TABLE_OUT_LEN))
return -EIO;
- for (i = 0; i < ARRAY_SIZE(efx->rx_indir_table); i++)
- efx->rx_indir_table[i] = MCDI_PTR(tablebuf,
+ for (i = 0; i < ARRAY_SIZE(ctx->rx_indir_table); i++)
+ ctx->rx_indir_table[i] = MCDI_PTR(tablebuf,
RSS_CONTEXT_GET_TABLE_OUT_INDIRECTION_TABLE)[i];
MCDI_SET_DWORD(inbuf, RSS_CONTEXT_GET_KEY_IN_RSS_CONTEXT_ID,
- nic_data->rx_rss_context);
- BUILD_BUG_ON(ARRAY_SIZE(efx->rx_hash_key) !=
+ ctx->context_id);
+ BUILD_BUG_ON(ARRAY_SIZE(ctx->rx_hash_key) !=
MC_CMD_RSS_CONTEXT_SET_KEY_IN_TOEPLITZ_KEY_LEN);
rc = efx_mcdi_rpc(efx, MC_CMD_RSS_CONTEXT_GET_KEY, inbuf, sizeof(inbuf),
keybuf, sizeof(keybuf), &outlen);
@@ -2939,13 +2966,50 @@ static int efx_ef10_rx_pull_rss_config(struct efx_nic *efx)
if (WARN_ON(outlen != MC_CMD_RSS_CONTEXT_GET_KEY_OUT_LEN))
return -EIO;
- for (i = 0; i < ARRAY_SIZE(efx->rx_hash_key); ++i)
- efx->rx_hash_key[i] = MCDI_PTR(
+ for (i = 0; i < ARRAY_SIZE(ctx->rx_hash_key); ++i)
+ ctx->rx_hash_key[i] = MCDI_PTR(
keybuf, RSS_CONTEXT_GET_KEY_OUT_TOEPLITZ_KEY)[i];
return 0;
}
+static int efx_ef10_rx_pull_rss_config(struct efx_nic *efx)
+{
+ int rc;
+
+ mutex_lock(&efx->rss_lock);
+ rc = efx_ef10_rx_pull_rss_context_config(efx, &efx->rss_context);
+ mutex_unlock(&efx->rss_lock);
+ return rc;
+}
+
+static void efx_ef10_rx_restore_rss_contexts(struct efx_nic *efx)
+{
+ struct efx_ef10_nic_data *nic_data = efx->nic_data;
+ struct efx_rss_context *ctx;
+ int rc;
+
+ WARN_ON(!mutex_is_locked(&efx->rss_lock));
+
+ if (!nic_data->must_restore_rss_contexts)
+ return;
+
+ list_for_each_entry(ctx, &efx->rss_context.list, list) {
+ /* previous NIC RSS context is gone */
+ ctx->context_id = EFX_EF10_RSS_CONTEXT_INVALID;
+ /* so try to allocate a new one */
+ rc = efx_ef10_rx_push_rss_context_config(efx, ctx,
+ ctx->rx_indir_table,
+ ctx->rx_hash_key);
+ if (rc)
+ netif_warn(efx, probe, efx->net_dev,
+ "failed to restore RSS context %u, rc=%d"
+ "; RSS filters may fail to be applied\n",
+ ctx->user_id, rc);
+ }
+ nic_data->must_restore_rss_contexts = false;
+}
+
static int efx_ef10_pf_rx_push_rss_config(struct efx_nic *efx, bool user,
const u32 *rx_indir_table,
const u8 *key)
@@ -2956,7 +3020,7 @@ static int efx_ef10_pf_rx_push_rss_config(struct efx_nic *efx, bool user,
return 0;
if (!key)
- key = efx->rx_hash_key;
+ key = efx->rss_context.rx_hash_key;
rc = efx_ef10_rx_push_exclusive_rss_config(efx, rx_indir_table, key);
@@ -2965,7 +3029,8 @@ static int efx_ef10_pf_rx_push_rss_config(struct efx_nic *efx, bool user,
bool mismatch = false;
size_t i;
- for (i = 0; i < ARRAY_SIZE(efx->rx_indir_table) && !mismatch;
+ for (i = 0;
+ i < ARRAY_SIZE(efx->rss_context.rx_indir_table) && !mismatch;
i++)
mismatch = rx_indir_table[i] !=
ethtool_rxfh_indir_default(i, efx->rss_spread);
@@ -3000,11 +3065,9 @@ static int efx_ef10_vf_rx_push_rss_config(struct efx_nic *efx, bool user,
const u8 *key
__attribute__ ((unused)))
{
- struct efx_ef10_nic_data *nic_data = efx->nic_data;
-
if (user)
return -EOPNOTSUPP;
- if (nic_data->rx_rss_context != EFX_EF10_RSS_CONTEXT_INVALID)
+ if (efx->rss_context.context_id != EFX_EF10_RSS_CONTEXT_INVALID)
return 0;
return efx_ef10_rx_push_shared_rss_config(efx, NULL);
}
@@ -3936,29 +3999,6 @@ static void efx_ef10_prepare_flr(struct efx_nic *efx)
atomic_set(&efx->active_queues, 0);
}
-static bool efx_ef10_filter_equal(const struct efx_filter_spec *left,
- const struct efx_filter_spec *right)
-{
- if ((left->match_flags ^ right->match_flags) |
- ((left->flags ^ right->flags) &
- (EFX_FILTER_FLAG_RX | EFX_FILTER_FLAG_TX)))
- return false;
-
- return memcmp(&left->outer_vid, &right->outer_vid,
- sizeof(struct efx_filter_spec) -
- offsetof(struct efx_filter_spec, outer_vid)) == 0;
-}
-
-static unsigned int efx_ef10_filter_hash(const struct efx_filter_spec *spec)
-{
- BUILD_BUG_ON(offsetof(struct efx_filter_spec, outer_vid) & 3);
- return jhash2((const u32 *)&spec->outer_vid,
- (sizeof(struct efx_filter_spec) -
- offsetof(struct efx_filter_spec, outer_vid)) / 4,
- 0);
- /* XXX should we randomise the initval? */
-}
-
/* Decide whether a filter should be exclusive or else should allow
* delivery to additional recipients. Currently we decide that
* filters for specific local unicast MAC and IP addresses are
@@ -4109,6 +4149,7 @@ efx_ef10_filter_push_prep_set_match_fields(struct efx_nic *efx,
static void efx_ef10_filter_push_prep(struct efx_nic *efx,
const struct efx_filter_spec *spec,
efx_dword_t *inbuf, u64 handle,
+ struct efx_rss_context *ctx,
bool replacing)
{
struct efx_ef10_nic_data *nic_data = efx->nic_data;
@@ -4116,11 +4157,16 @@ static void efx_ef10_filter_push_prep(struct efx_nic *efx,
memset(inbuf, 0, MC_CMD_FILTER_OP_EXT_IN_LEN);
- /* Remove RSS flag if we don't have an RSS context. */
- if (flags & EFX_FILTER_FLAG_RX_RSS &&
- spec->rss_context == EFX_FILTER_RSS_CONTEXT_DEFAULT &&
- nic_data->rx_rss_context == EFX_EF10_RSS_CONTEXT_INVALID)
- flags &= ~EFX_FILTER_FLAG_RX_RSS;
+ /* If RSS filter, caller better have given us an RSS context */
+ if (flags & EFX_FILTER_FLAG_RX_RSS) {
+ /* We don't have the ability to return an error, so we'll just
+ * log a warning and disable RSS for the filter.
+ */
+ if (WARN_ON_ONCE(!ctx))
+ flags &= ~EFX_FILTER_FLAG_RX_RSS;
+ else if (WARN_ON_ONCE(ctx->context_id == EFX_EF10_RSS_CONTEXT_INVALID))
+ flags &= ~EFX_FILTER_FLAG_RX_RSS;
+ }
if (replacing) {
MCDI_SET_DWORD(inbuf, FILTER_OP_IN_OP,
@@ -4146,21 +4192,18 @@ static void efx_ef10_filter_push_prep(struct efx_nic *efx,
MC_CMD_FILTER_OP_IN_RX_MODE_RSS :
MC_CMD_FILTER_OP_IN_RX_MODE_SIMPLE);
if (flags & EFX_FILTER_FLAG_RX_RSS)
- MCDI_SET_DWORD(inbuf, FILTER_OP_IN_RX_CONTEXT,
- spec->rss_context !=
- EFX_FILTER_RSS_CONTEXT_DEFAULT ?
- spec->rss_context : nic_data->rx_rss_context);
+ MCDI_SET_DWORD(inbuf, FILTER_OP_IN_RX_CONTEXT, ctx->context_id);
}
static int efx_ef10_filter_push(struct efx_nic *efx,
- const struct efx_filter_spec *spec,
- u64 *handle, bool replacing)
+ const struct efx_filter_spec *spec, u64 *handle,
+ struct efx_rss_context *ctx, bool replacing)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_FILTER_OP_EXT_IN_LEN);
MCDI_DECLARE_BUF(outbuf, MC_CMD_FILTER_OP_EXT_OUT_LEN);
int rc;
- efx_ef10_filter_push_prep(efx, spec, inbuf, *handle, replacing);
+ efx_ef10_filter_push_prep(efx, spec, inbuf, *handle, ctx, replacing);
rc = efx_mcdi_rpc(efx, MC_CMD_FILTER_OP, inbuf, sizeof(inbuf),
outbuf, sizeof(outbuf), NULL);
if (rc == 0)
@@ -4249,106 +4292,107 @@ static s32 efx_ef10_filter_insert(struct efx_nic *efx,
struct efx_filter_spec *spec,
bool replace_equal)
{
- struct efx_ef10_filter_table *table = efx->filter_state;
DECLARE_BITMAP(mc_rem_map, EFX_EF10_FILTER_SEARCH_LIMIT);
+ struct efx_ef10_nic_data *nic_data = efx->nic_data;
+ struct efx_ef10_filter_table *table;
struct efx_filter_spec *saved_spec;
+ struct efx_rss_context *ctx = NULL;
unsigned int match_pri, hash;
unsigned int priv_flags;
+ bool rss_locked = false;
bool replacing = false;
+ unsigned int depth, i;
int ins_index = -1;
DEFINE_WAIT(wait);
bool is_mc_recip;
s32 rc;
+ down_read(&efx->filter_sem);
+ table = efx->filter_state;
+ down_write(&table->lock);
+
/* For now, only support RX filters */
if ((spec->flags & (EFX_FILTER_FLAG_RX | EFX_FILTER_FLAG_TX)) !=
- EFX_FILTER_FLAG_RX)
- return -EINVAL;
+ EFX_FILTER_FLAG_RX) {
+ rc = -EINVAL;
+ goto out_unlock;
+ }
rc = efx_ef10_filter_pri(table, spec);
if (rc < 0)
- return rc;
+ goto out_unlock;
match_pri = rc;
- hash = efx_ef10_filter_hash(spec);
+ hash = efx_filter_spec_hash(spec);
is_mc_recip = efx_filter_is_mc_recipient(spec);
if (is_mc_recip)
bitmap_zero(mc_rem_map, EFX_EF10_FILTER_SEARCH_LIMIT);
+ if (spec->flags & EFX_FILTER_FLAG_RX_RSS) {
+ mutex_lock(&efx->rss_lock);
+ rss_locked = true;
+ if (spec->rss_context)
+ ctx = efx_find_rss_context_entry(efx, spec->rss_context);
+ else
+ ctx = &efx->rss_context;
+ if (!ctx) {
+ rc = -ENOENT;
+ goto out_unlock;
+ }
+ if (ctx->context_id == EFX_EF10_RSS_CONTEXT_INVALID) {
+ rc = -EOPNOTSUPP;
+ goto out_unlock;
+ }
+ }
+
/* Find any existing filters with the same match tuple or
- * else a free slot to insert at. If any of them are busy,
- * we have to wait and retry.
+ * else a free slot to insert at.
*/
- for (;;) {
- unsigned int depth = 1;
- unsigned int i;
-
- spin_lock_bh(&efx->filter_lock);
-
- for (;;) {
- i = (hash + depth) & (HUNT_FILTER_TBL_ROWS - 1);
- saved_spec = efx_ef10_filter_entry_spec(table, i);
+ for (depth = 1; depth < EFX_EF10_FILTER_SEARCH_LIMIT; depth++) {
+ i = (hash + depth) & (HUNT_FILTER_TBL_ROWS - 1);
+ saved_spec = efx_ef10_filter_entry_spec(table, i);
- if (!saved_spec) {
- if (ins_index < 0)
- ins_index = i;
- } else if (efx_ef10_filter_equal(spec, saved_spec)) {
- if (table->entry[i].spec &
- EFX_EF10_FILTER_FLAG_BUSY)
- break;
- if (spec->priority < saved_spec->priority &&
- spec->priority != EFX_FILTER_PRI_AUTO) {
- rc = -EPERM;
- goto out_unlock;
- }
- if (!is_mc_recip) {
- /* This is the only one */
- if (spec->priority ==
- saved_spec->priority &&
- !replace_equal) {
- rc = -EEXIST;
- goto out_unlock;
- }
- ins_index = i;
- goto found;
- } else if (spec->priority >
- saved_spec->priority ||
- (spec->priority ==
- saved_spec->priority &&
- replace_equal)) {
- if (ins_index < 0)
- ins_index = i;
- else
- __set_bit(depth, mc_rem_map);
- }
+ if (!saved_spec) {
+ if (ins_index < 0)
+ ins_index = i;
+ } else if (efx_filter_spec_equal(spec, saved_spec)) {
+ if (spec->priority < saved_spec->priority &&
+ spec->priority != EFX_FILTER_PRI_AUTO) {
+ rc = -EPERM;
+ goto out_unlock;
}
-
- /* Once we reach the maximum search depth, use
- * the first suitable slot or return -EBUSY if
- * there was none
- */
- if (depth == EFX_EF10_FILTER_SEARCH_LIMIT) {
- if (ins_index < 0) {
- rc = -EBUSY;
+ if (!is_mc_recip) {
+ /* This is the only one */
+ if (spec->priority ==
+ saved_spec->priority &&
+ !replace_equal) {
+ rc = -EEXIST;
goto out_unlock;
}
- goto found;
+ ins_index = i;
+ break;
+ } else if (spec->priority >
+ saved_spec->priority ||
+ (spec->priority ==
+ saved_spec->priority &&
+ replace_equal)) {
+ if (ins_index < 0)
+ ins_index = i;
+ else
+ __set_bit(depth, mc_rem_map);
}
-
- ++depth;
}
-
- prepare_to_wait(&table->waitq, &wait, TASK_UNINTERRUPTIBLE);
- spin_unlock_bh(&efx->filter_lock);
- schedule();
}
-found:
- /* Create a software table entry if necessary, and mark it
- * busy. We might yet fail to insert, but any attempt to
- * insert a conflicting filter while we're waiting for the
- * firmware must find the busy entry.
+ /* Once we reach the maximum search depth, use the first suitable
+ * slot, or return -EBUSY if there was none
*/
+ if (ins_index < 0) {
+ rc = -EBUSY;
+ goto out_unlock;
+ }
+
+ /* Create a software table entry if necessary. */
saved_spec = efx_ef10_filter_entry_spec(table, ins_index);
if (saved_spec) {
if (spec->priority == EFX_FILTER_PRI_AUTO &&
@@ -4372,28 +4416,19 @@ found:
*saved_spec = *spec;
priv_flags = 0;
}
- efx_ef10_filter_set_entry(table, ins_index, saved_spec,
- priv_flags | EFX_EF10_FILTER_FLAG_BUSY);
-
- /* Mark lower-priority multicast recipients busy prior to removal */
- if (is_mc_recip) {
- unsigned int depth, i;
-
- for (depth = 0; depth < EFX_EF10_FILTER_SEARCH_LIMIT; depth++) {
- i = (hash + depth) & (HUNT_FILTER_TBL_ROWS - 1);
- if (test_bit(depth, mc_rem_map))
- table->entry[i].spec |=
- EFX_EF10_FILTER_FLAG_BUSY;
- }
- }
-
- spin_unlock_bh(&efx->filter_lock);
+ efx_ef10_filter_set_entry(table, ins_index, saved_spec, priv_flags);
+ /* Actually insert the filter on the HW */
rc = efx_ef10_filter_push(efx, spec, &table->entry[ins_index].handle,
- replacing);
+ ctx, replacing);
+
+ if (rc == -EINVAL && nic_data->must_realloc_vis)
+ /* The MC rebooted under us, causing it to reject our filter
+ * insertion as pointing to an invalid VI (spec->dmaq_id).
+ */
+ rc = -EAGAIN;
/* Finalise the software table entry */
- spin_lock_bh(&efx->filter_lock);
if (rc == 0) {
if (replacing) {
/* Update the fields that may differ */
@@ -4409,6 +4444,12 @@ found:
} else if (!replacing) {
kfree(saved_spec);
saved_spec = NULL;
+ } else {
+ /* We failed to replace, so the old filter is still present.
+ * Roll back the software table to reflect this. In fact the
+ * efx_ef10_filter_set_entry() call below will do the right
+ * thing, so nothing extra is needed here.
+ */
}
efx_ef10_filter_set_entry(table, ins_index, saved_spec, priv_flags);
@@ -4430,7 +4471,6 @@ found:
priv_flags = efx_ef10_filter_entry_flags(table, i);
if (rc == 0) {
- spin_unlock_bh(&efx->filter_lock);
MCDI_SET_DWORD(inbuf, FILTER_OP_IN_OP,
MC_CMD_FILTER_OP_IN_OP_UNSUBSCRIBE);
MCDI_SET_QWORD(inbuf, FILTER_OP_IN_HANDLE,
@@ -4438,15 +4478,12 @@ found:
rc = efx_mcdi_rpc(efx, MC_CMD_FILTER_OP,
inbuf, sizeof(inbuf),
NULL, 0, NULL);
- spin_lock_bh(&efx->filter_lock);
}
if (rc == 0) {
kfree(saved_spec);
saved_spec = NULL;
priv_flags = 0;
- } else {
- priv_flags &= ~EFX_EF10_FILTER_FLAG_BUSY;
}
efx_ef10_filter_set_entry(table, i, saved_spec,
priv_flags);
@@ -4457,10 +4494,11 @@ found:
if (rc == 0)
rc = efx_ef10_make_filter_id(match_pri, ins_index);
- wake_up_all(&table->waitq);
out_unlock:
- spin_unlock_bh(&efx->filter_lock);
- finish_wait(&table->waitq, &wait);
+ if (rss_locked)
+ mutex_unlock(&efx->rss_lock);
+ up_write(&table->lock);
+ up_read(&efx->filter_sem);
return rc;
}
@@ -4473,6 +4511,8 @@ static void efx_ef10_filter_update_rx_scatter(struct efx_nic *efx)
* If !by_index, remove by ID
* If by_index, remove by index
* Filter ID may come from userland and must be range-checked.
+ * Caller must hold efx->filter_sem for read, and efx->filter_state->lock
+ * for write.
*/
static int efx_ef10_filter_remove_internal(struct efx_nic *efx,
unsigned int priority_mask,
@@ -4487,45 +4527,23 @@ static int efx_ef10_filter_remove_internal(struct efx_nic *efx,
DEFINE_WAIT(wait);
int rc;
- /* Find the software table entry and mark it busy. Don't
- * remove it yet; any attempt to update while we're waiting
- * for the firmware must find the busy entry.
- */
- for (;;) {
- spin_lock_bh(&efx->filter_lock);
- if (!(table->entry[filter_idx].spec &
- EFX_EF10_FILTER_FLAG_BUSY))
- break;
- prepare_to_wait(&table->waitq, &wait, TASK_UNINTERRUPTIBLE);
- spin_unlock_bh(&efx->filter_lock);
- schedule();
- }
-
spec = efx_ef10_filter_entry_spec(table, filter_idx);
if (!spec ||
(!by_index &&
efx_ef10_filter_pri(table, spec) !=
- efx_ef10_filter_get_unsafe_pri(filter_id))) {
- rc = -ENOENT;
- goto out_unlock;
- }
+ efx_ef10_filter_get_unsafe_pri(filter_id)))
+ return -ENOENT;
if (spec->flags & EFX_FILTER_FLAG_RX_OVER_AUTO &&
priority_mask == (1U << EFX_FILTER_PRI_AUTO)) {
/* Just remove flags */
spec->flags &= ~EFX_FILTER_FLAG_RX_OVER_AUTO;
table->entry[filter_idx].spec &= ~EFX_EF10_FILTER_FLAG_AUTO_OLD;
- rc = 0;
- goto out_unlock;
- }
-
- if (!(priority_mask & (1U << spec->priority))) {
- rc = -ENOENT;
- goto out_unlock;
+ return 0;
}
- table->entry[filter_idx].spec |= EFX_EF10_FILTER_FLAG_BUSY;
- spin_unlock_bh(&efx->filter_lock);
+ if (!(priority_mask & (1U << spec->priority)))
+ return -ENOENT;
if (spec->flags & EFX_FILTER_FLAG_RX_OVER_AUTO) {
/* Reset to an automatic filter */
@@ -4534,15 +4552,15 @@ static int efx_ef10_filter_remove_internal(struct efx_nic *efx,
new_spec.priority = EFX_FILTER_PRI_AUTO;
new_spec.flags = (EFX_FILTER_FLAG_RX |
- (efx_rss_enabled(efx) ?
+ (efx_rss_active(&efx->rss_context) ?
EFX_FILTER_FLAG_RX_RSS : 0));
new_spec.dmaq_id = 0;
- new_spec.rss_context = EFX_FILTER_RSS_CONTEXT_DEFAULT;
+ new_spec.rss_context = 0;
rc = efx_ef10_filter_push(efx, &new_spec,
&table->entry[filter_idx].handle,
+ &efx->rss_context,
true);
- spin_lock_bh(&efx->filter_lock);
if (rc == 0)
*spec = new_spec;
} else {
@@ -4557,7 +4575,6 @@ static int efx_ef10_filter_remove_internal(struct efx_nic *efx,
rc = efx_mcdi_rpc_quiet(efx, MC_CMD_FILTER_OP,
inbuf, sizeof(inbuf), NULL, 0, NULL);
- spin_lock_bh(&efx->filter_lock);
if ((rc == 0) || (rc == -ENOENT)) {
/* Filter removed OK or didn't actually exist */
kfree(spec);
@@ -4569,11 +4586,6 @@ static int efx_ef10_filter_remove_internal(struct efx_nic *efx,
}
}
- table->entry[filter_idx].spec &= ~EFX_EF10_FILTER_FLAG_BUSY;
- wake_up_all(&table->waitq);
-out_unlock:
- spin_unlock_bh(&efx->filter_lock);
- finish_wait(&table->waitq, &wait);
return rc;
}
@@ -4581,17 +4593,33 @@ static int efx_ef10_filter_remove_safe(struct efx_nic *efx,
enum efx_filter_priority priority,
u32 filter_id)
{
- return efx_ef10_filter_remove_internal(efx, 1U << priority,
- filter_id, false);
+ struct efx_ef10_filter_table *table;
+ int rc;
+
+ down_read(&efx->filter_sem);
+ table = efx->filter_state;
+ down_write(&table->lock);
+ rc = efx_ef10_filter_remove_internal(efx, 1U << priority, filter_id,
+ false);
+ up_write(&table->lock);
+ up_read(&efx->filter_sem);
+ return rc;
}
+/* Caller must hold efx->filter_sem for read */
static void efx_ef10_filter_remove_unsafe(struct efx_nic *efx,
enum efx_filter_priority priority,
u32 filter_id)
{
+ struct efx_ef10_filter_table *table = efx->filter_state;
+
if (filter_id == EFX_EF10_FILTER_ID_INVALID)
return;
- efx_ef10_filter_remove_internal(efx, 1U << priority, filter_id, true);
+
+ down_write(&table->lock);
+ efx_ef10_filter_remove_internal(efx, 1U << priority, filter_id,
+ true);
+ up_write(&table->lock);
}
static int efx_ef10_filter_get_safe(struct efx_nic *efx,
@@ -4599,11 +4627,13 @@ static int efx_ef10_filter_get_safe(struct efx_nic *efx,
u32 filter_id, struct efx_filter_spec *spec)
{
unsigned int filter_idx = efx_ef10_filter_get_unsafe_id(filter_id);
- struct efx_ef10_filter_table *table = efx->filter_state;
const struct efx_filter_spec *saved_spec;
+ struct efx_ef10_filter_table *table;
int rc;
- spin_lock_bh(&efx->filter_lock);
+ down_read(&efx->filter_sem);
+ table = efx->filter_state;
+ down_read(&table->lock);
saved_spec = efx_ef10_filter_entry_spec(table, filter_idx);
if (saved_spec && saved_spec->priority == priority &&
efx_ef10_filter_pri(table, saved_spec) ==
@@ -4613,13 +4643,15 @@ static int efx_ef10_filter_get_safe(struct efx_nic *efx,
} else {
rc = -ENOENT;
}
- spin_unlock_bh(&efx->filter_lock);
+ up_read(&table->lock);
+ up_read(&efx->filter_sem);
return rc;
}
static int efx_ef10_filter_clear_rx(struct efx_nic *efx,
- enum efx_filter_priority priority)
+ enum efx_filter_priority priority)
{
+ struct efx_ef10_filter_table *table;
unsigned int priority_mask;
unsigned int i;
int rc;
@@ -4627,31 +4659,40 @@ static int efx_ef10_filter_clear_rx(struct efx_nic *efx,
priority_mask = (((1U << (priority + 1)) - 1) &
~(1U << EFX_FILTER_PRI_AUTO));
+ down_read(&efx->filter_sem);
+ table = efx->filter_state;
+ down_write(&table->lock);
for (i = 0; i < HUNT_FILTER_TBL_ROWS; i++) {
rc = efx_ef10_filter_remove_internal(efx, priority_mask,
i, true);
if (rc && rc != -ENOENT)
- return rc;
+ break;
+ rc = 0;
}
- return 0;
+ up_write(&table->lock);
+ up_read(&efx->filter_sem);
+ return rc;
}
static u32 efx_ef10_filter_count_rx_used(struct efx_nic *efx,
enum efx_filter_priority priority)
{
- struct efx_ef10_filter_table *table = efx->filter_state;
+ struct efx_ef10_filter_table *table;
unsigned int filter_idx;
s32 count = 0;
- spin_lock_bh(&efx->filter_lock);
+ down_read(&efx->filter_sem);
+ table = efx->filter_state;
+ down_read(&table->lock);
for (filter_idx = 0; filter_idx < HUNT_FILTER_TBL_ROWS; filter_idx++) {
if (table->entry[filter_idx].spec &&
efx_ef10_filter_entry_spec(table, filter_idx)->priority ==
priority)
++count;
}
- spin_unlock_bh(&efx->filter_lock);
+ up_read(&table->lock);
+ up_read(&efx->filter_sem);
return count;
}
@@ -4666,12 +4707,15 @@ static s32 efx_ef10_filter_get_rx_ids(struct efx_nic *efx,
enum efx_filter_priority priority,
u32 *buf, u32 size)
{
- struct efx_ef10_filter_table *table = efx->filter_state;
+ struct efx_ef10_filter_table *table;
struct efx_filter_spec *spec;
unsigned int filter_idx;
s32 count = 0;
- spin_lock_bh(&efx->filter_lock);
+ down_read(&efx->filter_sem);
+ table = efx->filter_state;
+ down_read(&table->lock);
+
for (filter_idx = 0; filter_idx < HUNT_FILTER_TBL_ROWS; filter_idx++) {
spec = efx_ef10_filter_entry_spec(table, filter_idx);
if (spec && spec->priority == priority) {
@@ -4685,201 +4729,76 @@ static s32 efx_ef10_filter_get_rx_ids(struct efx_nic *efx,
filter_idx);
}
}
- spin_unlock_bh(&efx->filter_lock);
+ up_read(&table->lock);
+ up_read(&efx->filter_sem);
return count;
}
#ifdef CONFIG_RFS_ACCEL
-static efx_mcdi_async_completer efx_ef10_filter_rfs_insert_complete;
-
-static s32 efx_ef10_filter_rfs_insert(struct efx_nic *efx,
- struct efx_filter_spec *spec)
+static bool efx_ef10_filter_rfs_expire_one(struct efx_nic *efx, u32 flow_id,
+ unsigned int filter_idx)
{
- struct efx_ef10_filter_table *table = efx->filter_state;
- MCDI_DECLARE_BUF(inbuf, MC_CMD_FILTER_OP_EXT_IN_LEN);
- struct efx_filter_spec *saved_spec;
- unsigned int hash, i, depth = 1;
- bool replacing = false;
- int ins_index = -1;
- u64 cookie;
- s32 rc;
-
- /* Must be an RX filter without RSS and not for a multicast
- * destination address (RFS only works for connected sockets).
- * These restrictions allow us to pass only a tiny amount of
- * data through to the completion function.
- */
- EFX_WARN_ON_PARANOID(spec->flags !=
- (EFX_FILTER_FLAG_RX | EFX_FILTER_FLAG_RX_SCATTER));
- EFX_WARN_ON_PARANOID(spec->priority != EFX_FILTER_PRI_HINT);
- EFX_WARN_ON_PARANOID(efx_filter_is_mc_recipient(spec));
-
- hash = efx_ef10_filter_hash(spec);
-
- spin_lock_bh(&efx->filter_lock);
-
- /* Find any existing filter with the same match tuple or else
- * a free slot to insert at. If an existing filter is busy,
- * we have to give up.
- */
- for (;;) {
- i = (hash + depth) & (HUNT_FILTER_TBL_ROWS - 1);
- saved_spec = efx_ef10_filter_entry_spec(table, i);
-
- if (!saved_spec) {
- if (ins_index < 0)
- ins_index = i;
- } else if (efx_ef10_filter_equal(spec, saved_spec)) {
- if (table->entry[i].spec & EFX_EF10_FILTER_FLAG_BUSY) {
- rc = -EBUSY;
- goto fail_unlock;
- }
- if (spec->priority < saved_spec->priority) {
- rc = -EPERM;
- goto fail_unlock;
- }
- ins_index = i;
- break;
- }
+ struct efx_filter_spec *spec, saved_spec;
+ struct efx_ef10_filter_table *table;
+ struct efx_arfs_rule *rule = NULL;
+ bool ret = true, force = false;
+ u16 arfs_id;
- /* Once we reach the maximum search depth, use the
- * first suitable slot or return -EBUSY if there was
- * none
- */
- if (depth == EFX_EF10_FILTER_SEARCH_LIMIT) {
- if (ins_index < 0) {
- rc = -EBUSY;
- goto fail_unlock;
- }
- break;
- }
+ down_read(&efx->filter_sem);
+ table = efx->filter_state;
+ down_write(&table->lock);
+ spec = efx_ef10_filter_entry_spec(table, filter_idx);
- ++depth;
- }
+ if (!spec || spec->priority != EFX_FILTER_PRI_HINT)
+ goto out_unlock;
- /* Create a software table entry if necessary, and mark it
- * busy. We might yet fail to insert, but any attempt to
- * insert a conflicting filter while we're waiting for the
- * firmware must find the busy entry.
- */
- saved_spec = efx_ef10_filter_entry_spec(table, ins_index);
- if (saved_spec) {
- replacing = true;
+ spin_lock_bh(&efx->rps_hash_lock);
+ if (!efx->rps_hash_table) {
+ /* In the absence of the table, we always return 0 to ARFS. */
+ arfs_id = 0;
} else {
- saved_spec = kmalloc(sizeof(*spec), GFP_ATOMIC);
- if (!saved_spec) {
- rc = -ENOMEM;
- goto fail_unlock;
+ rule = efx_rps_hash_find(efx, spec);
+ if (!rule)
+ /* ARFS table doesn't know of this filter, so remove it */
+ goto expire;
+ arfs_id = rule->arfs_id;
+ ret = efx_rps_check_rule(rule, filter_idx, &force);
+ if (force)
+ goto expire;
+ if (!ret) {
+ spin_unlock_bh(&efx->rps_hash_lock);
+ goto out_unlock;
}
- *saved_spec = *spec;
}
- efx_ef10_filter_set_entry(table, ins_index, saved_spec,
- EFX_EF10_FILTER_FLAG_BUSY);
-
- spin_unlock_bh(&efx->filter_lock);
-
- /* Pack up the variables needed on completion */
- cookie = replacing << 31 | ins_index << 16 | spec->dmaq_id;
-
- efx_ef10_filter_push_prep(efx, spec, inbuf,
- table->entry[ins_index].handle, replacing);
- efx_mcdi_rpc_async(efx, MC_CMD_FILTER_OP, inbuf, sizeof(inbuf),
- MC_CMD_FILTER_OP_OUT_LEN,
- efx_ef10_filter_rfs_insert_complete, cookie);
-
- return ins_index;
-
-fail_unlock:
- spin_unlock_bh(&efx->filter_lock);
- return rc;
-}
-
-static void
-efx_ef10_filter_rfs_insert_complete(struct efx_nic *efx, unsigned long cookie,
- int rc, efx_dword_t *outbuf,
- size_t outlen_actual)
-{
- struct efx_ef10_filter_table *table = efx->filter_state;
- unsigned int ins_index, dmaq_id;
- struct efx_filter_spec *spec;
- bool replacing;
-
- /* Unpack the cookie */
- replacing = cookie >> 31;
- ins_index = (cookie >> 16) & (HUNT_FILTER_TBL_ROWS - 1);
- dmaq_id = cookie & 0xffff;
-
- spin_lock_bh(&efx->filter_lock);
- spec = efx_ef10_filter_entry_spec(table, ins_index);
- if (rc == 0) {
- table->entry[ins_index].handle =
- MCDI_QWORD(outbuf, FILTER_OP_OUT_HANDLE);
- if (replacing)
- spec->dmaq_id = dmaq_id;
- } else if (!replacing) {
- kfree(spec);
- spec = NULL;
- }
- efx_ef10_filter_set_entry(table, ins_index, spec, 0);
- spin_unlock_bh(&efx->filter_lock);
-
- wake_up_all(&table->waitq);
-}
-
-static void
-efx_ef10_filter_rfs_expire_complete(struct efx_nic *efx,
- unsigned long filter_idx,
- int rc, efx_dword_t *outbuf,
- size_t outlen_actual);
-
-static bool efx_ef10_filter_rfs_expire_one(struct efx_nic *efx, u32 flow_id,
- unsigned int filter_idx)
-{
- struct efx_ef10_filter_table *table = efx->filter_state;
- struct efx_filter_spec *spec =
- efx_ef10_filter_entry_spec(table, filter_idx);
- MCDI_DECLARE_BUF(inbuf,
- MC_CMD_FILTER_OP_IN_HANDLE_OFST +
- MC_CMD_FILTER_OP_IN_HANDLE_LEN);
-
- if (!spec ||
- (table->entry[filter_idx].spec & EFX_EF10_FILTER_FLAG_BUSY) ||
- spec->priority != EFX_FILTER_PRI_HINT ||
- !rps_may_expire_flow(efx->net_dev, spec->dmaq_id,
- flow_id, filter_idx))
- return false;
-
- MCDI_SET_DWORD(inbuf, FILTER_OP_IN_OP,
- MC_CMD_FILTER_OP_IN_OP_REMOVE);
- MCDI_SET_QWORD(inbuf, FILTER_OP_IN_HANDLE,
- table->entry[filter_idx].handle);
- if (efx_mcdi_rpc_async(efx, MC_CMD_FILTER_OP, inbuf, sizeof(inbuf), 0,
- efx_ef10_filter_rfs_expire_complete, filter_idx))
- return false;
-
- table->entry[filter_idx].spec |= EFX_EF10_FILTER_FLAG_BUSY;
- return true;
-}
-
-static void
-efx_ef10_filter_rfs_expire_complete(struct efx_nic *efx,
- unsigned long filter_idx,
- int rc, efx_dword_t *outbuf,
- size_t outlen_actual)
-{
- struct efx_ef10_filter_table *table = efx->filter_state;
- struct efx_filter_spec *spec =
- efx_ef10_filter_entry_spec(table, filter_idx);
-
- spin_lock_bh(&efx->filter_lock);
- if (rc == 0) {
- kfree(spec);
- efx_ef10_filter_set_entry(table, filter_idx, NULL, 0);
+ if (!rps_may_expire_flow(efx->net_dev, spec->dmaq_id, flow_id, arfs_id))
+ ret = false;
+ else if (rule)
+ rule->filter_id = EFX_ARFS_FILTER_ID_REMOVING;
+expire:
+ saved_spec = *spec; /* remove operation will kfree spec */
+ spin_unlock_bh(&efx->rps_hash_lock);
+ /* At this point (since we dropped the lock), another thread might queue
+ * up a fresh insertion request (but the actual insertion will be held
+ * up by our possession of the filter table lock). In that case, it
+ * will set rule->filter_id to EFX_ARFS_FILTER_ID_PENDING, meaning that
+ * the rule is not removed by efx_rps_hash_del() below.
+ */
+ ret = efx_ef10_filter_remove_internal(efx, 1U << spec->priority,
+ filter_idx, true) == 0;
+ /* While we can't safely dereference rule (we dropped the lock), we can
+ * still test it for NULL.
+ */
+ if (ret && rule) {
+ /* Expiring, so remove entry from ARFS table */
+ spin_lock_bh(&efx->rps_hash_lock);
+ efx_rps_hash_del(efx, &saved_spec);
+ spin_unlock_bh(&efx->rps_hash_lock);
}
- table->entry[filter_idx].spec &= ~EFX_EF10_FILTER_FLAG_BUSY;
- wake_up_all(&table->waitq);
- spin_unlock_bh(&efx->filter_lock);
+out_unlock:
+ up_write(&table->lock);
+ up_read(&efx->filter_sem);
+ return ret;
}
#endif /* CONFIG_RFS_ACCEL */
@@ -5074,9 +4993,9 @@ static int efx_ef10_filter_table_probe(struct efx_nic *efx)
table->vlan_filter =
!!(efx->net_dev->features & NETIF_F_HW_VLAN_CTAG_FILTER);
INIT_LIST_HEAD(&table->vlan_list);
+ init_rwsem(&table->lock);
efx->filter_state = table;
- init_waitqueue_head(&table->waitq);
list_for_each_entry(vlan, &nic_data->vlan_list, list) {
rc = efx_ef10_filter_add_vlan(efx, vlan->vid);
@@ -5104,6 +5023,7 @@ static void efx_ef10_filter_table_restore(struct efx_nic *efx)
unsigned int invalid_filters = 0, failed = 0;
struct efx_ef10_filter_vlan *vlan;
struct efx_filter_spec *spec;
+ struct efx_rss_context *ctx;
unsigned int filter_idx;
u32 mcdi_flags;
int match_pri;
@@ -5117,7 +5037,8 @@ static void efx_ef10_filter_table_restore(struct efx_nic *efx)
if (!table)
return;
- spin_lock_bh(&efx->filter_lock);
+ down_write(&table->lock);
+ mutex_lock(&efx->rss_lock);
for (filter_idx = 0; filter_idx < HUNT_FILTER_TBL_ROWS; filter_idx++) {
spec = efx_ef10_filter_entry_spec(table, filter_idx);
@@ -5133,20 +5054,32 @@ static void efx_ef10_filter_table_restore(struct efx_nic *efx)
invalid_filters++;
goto not_restored;
}
- if (spec->rss_context != EFX_FILTER_RSS_CONTEXT_DEFAULT &&
- spec->rss_context != nic_data->rx_rss_context)
- netif_warn(efx, drv, efx->net_dev,
- "Warning: unable to restore a filter with specific RSS context.\n");
-
- table->entry[filter_idx].spec |= EFX_EF10_FILTER_FLAG_BUSY;
- spin_unlock_bh(&efx->filter_lock);
+ if (spec->rss_context)
+ ctx = efx_find_rss_context_entry(efx, spec->rss_context);
+ else
+ ctx = &efx->rss_context;
+ if (spec->flags & EFX_FILTER_FLAG_RX_RSS) {
+ if (!ctx) {
+ netif_warn(efx, drv, efx->net_dev,
+ "Warning: unable to restore a filter with nonexistent RSS context %u.\n",
+ spec->rss_context);
+ invalid_filters++;
+ goto not_restored;
+ }
+ if (ctx->context_id == EFX_EF10_RSS_CONTEXT_INVALID) {
+ netif_warn(efx, drv, efx->net_dev,
+ "Warning: unable to restore a filter with RSS context %u as it was not created.\n",
+ spec->rss_context);
+ invalid_filters++;
+ goto not_restored;
+ }
+ }
rc = efx_ef10_filter_push(efx, spec,
&table->entry[filter_idx].handle,
- false);
+ ctx, false);
if (rc)
failed++;
- spin_lock_bh(&efx->filter_lock);
if (rc) {
not_restored:
@@ -5158,13 +5091,11 @@ not_restored:
kfree(spec);
efx_ef10_filter_set_entry(table, filter_idx, NULL, 0);
- } else {
- table->entry[filter_idx].spec &=
- ~EFX_EF10_FILTER_FLAG_BUSY;
}
}
- spin_unlock_bh(&efx->filter_lock);
+ mutex_unlock(&efx->rss_lock);
+ up_write(&table->lock);
/* This can happen validly if the MC's capabilities have changed, so
* is not an error.
@@ -5232,6 +5163,8 @@ static void efx_ef10_filter_mark_one_old(struct efx_nic *efx, uint16_t *id)
struct efx_ef10_filter_table *table = efx->filter_state;
unsigned int filter_idx;
+ efx_rwsem_assert_write_locked(&table->lock);
+
if (*id != EFX_EF10_FILTER_ID_INVALID) {
filter_idx = efx_ef10_filter_get_unsafe_id(*id);
if (!table->entry[filter_idx].spec)
@@ -5267,10 +5200,10 @@ static void efx_ef10_filter_mark_old(struct efx_nic *efx)
struct efx_ef10_filter_table *table = efx->filter_state;
struct efx_ef10_filter_vlan *vlan;
- spin_lock_bh(&efx->filter_lock);
+ down_write(&table->lock);
list_for_each_entry(vlan, &table->vlan_list, list)
_efx_ef10_filter_vlan_mark_old(efx, vlan);
- spin_unlock_bh(&efx->filter_lock);
+ up_write(&table->lock);
}
static void efx_ef10_filter_uc_addr_list(struct efx_nic *efx)
@@ -5343,7 +5276,7 @@ static int efx_ef10_filter_insert_addr_list(struct efx_nic *efx,
ids = vlan->uc;
}
- filter_flags = efx_rss_enabled(efx) ? EFX_FILTER_FLAG_RX_RSS : 0;
+ filter_flags = efx_rss_active(&efx->rss_context) ? EFX_FILTER_FLAG_RX_RSS : 0;
/* Insert/renew filters */
for (i = 0; i < addr_count; i++) {
@@ -5412,7 +5345,7 @@ static int efx_ef10_filter_insert_def(struct efx_nic *efx,
int rc;
u16 *id;
- filter_flags = efx_rss_enabled(efx) ? EFX_FILTER_FLAG_RX_RSS : 0;
+ filter_flags = efx_rss_active(&efx->rss_context) ? EFX_FILTER_FLAG_RX_RSS : 0;
efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO, filter_flags, 0);
@@ -5547,10 +5480,7 @@ static int efx_ef10_filter_insert_def(struct efx_nic *efx,
return rc;
}
-/* Remove filters that weren't renewed. Since nothing else changes the AUTO_OLD
- * flag or removes these filters, we don't need to hold the filter_lock while
- * scanning for these filters.
- */
+/* Remove filters that weren't renewed. */
static void efx_ef10_filter_remove_old(struct efx_nic *efx)
{
struct efx_ef10_filter_table *table = efx->filter_state;
@@ -5559,6 +5489,7 @@ static void efx_ef10_filter_remove_old(struct efx_nic *efx)
int rc;
int i;
+ down_write(&table->lock);
for (i = 0; i < HUNT_FILTER_TBL_ROWS; i++) {
if (READ_ONCE(table->entry[i].spec) &
EFX_EF10_FILTER_FLAG_AUTO_OLD) {
@@ -5570,6 +5501,7 @@ static void efx_ef10_filter_remove_old(struct efx_nic *efx)
remove_failed++;
}
}
+ up_write(&table->lock);
if (remove_failed)
netif_info(efx, drv, efx->net_dev,
@@ -6698,7 +6630,6 @@ const struct efx_nic_type efx_hunt_a0_vf_nic_type = {
.filter_get_rx_id_limit = efx_ef10_filter_get_rx_id_limit,
.filter_get_rx_ids = efx_ef10_filter_get_rx_ids,
#ifdef CONFIG_RFS_ACCEL
- .filter_rfs_insert = efx_ef10_filter_rfs_insert,
.filter_rfs_expire_one = efx_ef10_filter_rfs_expire_one,
#endif
#ifdef CONFIG_SFC_MTD
@@ -6784,6 +6715,9 @@ const struct efx_nic_type efx_hunt_a0_nic_type = {
.tx_limit_len = efx_ef10_tx_limit_len,
.rx_push_rss_config = efx_ef10_pf_rx_push_rss_config,
.rx_pull_rss_config = efx_ef10_rx_pull_rss_config,
+ .rx_push_rss_context_config = efx_ef10_rx_push_rss_context_config,
+ .rx_pull_rss_context_config = efx_ef10_rx_pull_rss_context_config,
+ .rx_restore_rss_contexts = efx_ef10_rx_restore_rss_contexts,
.rx_probe = efx_ef10_rx_probe,
.rx_init = efx_ef10_rx_init,
.rx_remove = efx_ef10_rx_remove,
@@ -6808,7 +6742,6 @@ const struct efx_nic_type efx_hunt_a0_nic_type = {
.filter_get_rx_id_limit = efx_ef10_filter_get_rx_id_limit,
.filter_get_rx_ids = efx_ef10_filter_get_rx_ids,
#ifdef CONFIG_RFS_ACCEL
- .filter_rfs_insert = efx_ef10_filter_rfs_insert,
.filter_rfs_expire_one = efx_ef10_filter_rfs_expire_one,
#endif
#ifdef CONFIG_SFC_MTD
diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c
index 16757cfc5b29..a4ebd8715494 100644
--- a/drivers/net/ethernet/sfc/efx.c
+++ b/drivers/net/ethernet/sfc/efx.c
@@ -340,7 +340,10 @@ static int efx_poll(struct napi_struct *napi, int budget)
efx_update_irq_mod(efx, channel);
}
- efx_filter_rfs_expire(channel);
+#ifdef CONFIG_RFS_ACCEL
+ /* Perhaps expire some ARFS filters */
+ schedule_work(&channel->filter_work);
+#endif
/* There is no race here; although napi_disable() will
* only wait for napi_complete(), this isn't a problem
@@ -470,6 +473,10 @@ efx_alloc_channel(struct efx_nic *efx, int i, struct efx_channel *old_channel)
tx_queue->channel = channel;
}
+#ifdef CONFIG_RFS_ACCEL
+ INIT_WORK(&channel->filter_work, efx_filter_rfs_expire);
+#endif
+
rx_queue = &channel->rx_queue;
rx_queue->efx = efx;
timer_setup(&rx_queue->slow_fill, efx_rx_slow_fill, 0);
@@ -512,6 +519,9 @@ efx_copy_channel(const struct efx_channel *old_channel)
rx_queue->buffer = NULL;
memset(&rx_queue->rxd, 0, sizeof(rx_queue->rxd));
timer_setup(&rx_queue->slow_fill, efx_rx_slow_fill, 0);
+#ifdef CONFIG_RFS_ACCEL
+ INIT_WORK(&channel->filter_work, efx_filter_rfs_expire);
+#endif
return channel;
}
@@ -1353,12 +1363,13 @@ static void efx_fini_io(struct efx_nic *efx)
pci_disable_device(efx->pci_dev);
}
-void efx_set_default_rx_indir_table(struct efx_nic *efx)
+void efx_set_default_rx_indir_table(struct efx_nic *efx,
+ struct efx_rss_context *ctx)
{
size_t i;
- for (i = 0; i < ARRAY_SIZE(efx->rx_indir_table); i++)
- efx->rx_indir_table[i] =
+ for (i = 0; i < ARRAY_SIZE(ctx->rx_indir_table); i++)
+ ctx->rx_indir_table[i] =
ethtool_rxfh_indir_default(i, efx->rss_spread);
}
@@ -1739,9 +1750,9 @@ static int efx_probe_nic(struct efx_nic *efx)
} while (rc == -EAGAIN);
if (efx->n_channels > 1)
- netdev_rss_key_fill(&efx->rx_hash_key,
- sizeof(efx->rx_hash_key));
- efx_set_default_rx_indir_table(efx);
+ netdev_rss_key_fill(efx->rss_context.rx_hash_key,
+ sizeof(efx->rss_context.rx_hash_key));
+ efx_set_default_rx_indir_table(efx, &efx->rss_context);
netif_set_real_num_tx_queues(efx->net_dev, efx->n_tx_channels);
netif_set_real_num_rx_queues(efx->net_dev, efx->n_rx_channels);
@@ -1772,7 +1783,6 @@ static int efx_probe_filters(struct efx_nic *efx)
{
int rc;
- spin_lock_init(&efx->filter_lock);
init_rwsem(&efx->filter_sem);
mutex_lock(&efx->mac_lock);
down_write(&efx->filter_sem);
@@ -2647,6 +2657,7 @@ void efx_reset_down(struct efx_nic *efx, enum reset_type method)
efx_disable_interrupts(efx);
mutex_lock(&efx->mac_lock);
+ mutex_lock(&efx->rss_lock);
if (efx->port_initialized && method != RESET_TYPE_INVISIBLE &&
method != RESET_TYPE_DATAPATH)
efx->phy_op->fini(efx);
@@ -2700,6 +2711,9 @@ int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok)
" VFs may not function\n", rc);
#endif
+ if (efx->type->rx_restore_rss_contexts)
+ efx->type->rx_restore_rss_contexts(efx);
+ mutex_unlock(&efx->rss_lock);
down_read(&efx->filter_sem);
efx_restore_filters(efx);
up_read(&efx->filter_sem);
@@ -2718,6 +2732,7 @@ int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok)
fail:
efx->port_initialized = false;
+ mutex_unlock(&efx->rss_lock);
mutex_unlock(&efx->mac_lock);
return rc;
@@ -3003,11 +3018,20 @@ static int efx_init_struct(struct efx_nic *efx,
efx->type->rx_hash_offset - efx->type->rx_prefix_size;
efx->rx_packet_ts_offset =
efx->type->rx_ts_offset - efx->type->rx_prefix_size;
+ INIT_LIST_HEAD(&efx->rss_context.list);
+ mutex_init(&efx->rss_lock);
spin_lock_init(&efx->stats_lock);
efx->vi_stride = EFX_DEFAULT_VI_STRIDE;
efx->num_mac_stats = MC_CMD_MAC_NSTATS;
BUILD_BUG_ON(MC_CMD_MAC_NSTATS - 1 != MC_CMD_MAC_GENERATION_END);
mutex_init(&efx->mac_lock);
+#ifdef CONFIG_RFS_ACCEL
+ mutex_init(&efx->rps_mutex);
+ spin_lock_init(&efx->rps_hash_lock);
+ /* Failure to allocate is not fatal, but may degrade ARFS performance */
+ efx->rps_hash_table = kcalloc(EFX_ARFS_HASH_TABLE_SIZE,
+ sizeof(*efx->rps_hash_table), GFP_KERNEL);
+#endif
efx->phy_op = &efx_dummy_phy_operations;
efx->mdio.dev = net_dev;
INIT_WORK(&efx->mac_work, efx_mac_work);
@@ -3050,6 +3074,10 @@ static void efx_fini_struct(struct efx_nic *efx)
{
int i;
+#ifdef CONFIG_RFS_ACCEL
+ kfree(efx->rps_hash_table);
+#endif
+
for (i = 0; i < EFX_MAX_CHANNELS; i++)
kfree(efx->channel[i]);
@@ -3072,6 +3100,196 @@ void efx_update_sw_stats(struct efx_nic *efx, u64 *stats)
stats[GENERIC_STAT_rx_noskb_drops] = atomic_read(&efx->n_rx_noskb_drops);
}
+bool efx_filter_spec_equal(const struct efx_filter_spec *left,
+ const struct efx_filter_spec *right)
+{
+ if ((left->match_flags ^ right->match_flags) |
+ ((left->flags ^ right->flags) &
+ (EFX_FILTER_FLAG_RX | EFX_FILTER_FLAG_TX)))
+ return false;
+
+ return memcmp(&left->outer_vid, &right->outer_vid,
+ sizeof(struct efx_filter_spec) -
+ offsetof(struct efx_filter_spec, outer_vid)) == 0;
+}
+
+u32 efx_filter_spec_hash(const struct efx_filter_spec *spec)
+{
+ BUILD_BUG_ON(offsetof(struct efx_filter_spec, outer_vid) & 3);
+ return jhash2((const u32 *)&spec->outer_vid,
+ (sizeof(struct efx_filter_spec) -
+ offsetof(struct efx_filter_spec, outer_vid)) / 4,
+ 0);
+}
+
+#ifdef CONFIG_RFS_ACCEL
+bool efx_rps_check_rule(struct efx_arfs_rule *rule, unsigned int filter_idx,
+ bool *force)
+{
+ if (rule->filter_id == EFX_ARFS_FILTER_ID_PENDING) {
+ /* ARFS is currently updating this entry, leave it */
+ return false;
+ }
+ if (rule->filter_id == EFX_ARFS_FILTER_ID_ERROR) {
+ /* ARFS tried and failed to update this, so it's probably out
+ * of date. Remove the filter and the ARFS rule entry.
+ */
+ rule->filter_id = EFX_ARFS_FILTER_ID_REMOVING;
+ *force = true;
+ return true;
+ } else if (WARN_ON(rule->filter_id != filter_idx)) { /* can't happen */
+ /* ARFS has moved on, so old filter is not needed. Since we did
+ * not mark the rule with EFX_ARFS_FILTER_ID_REMOVING, it will
+ * not be removed by efx_rps_hash_del() subsequently.
+ */
+ *force = true;
+ return true;
+ }
+ /* Remove it iff ARFS wants to. */
+ return true;
+}
+
+struct hlist_head *efx_rps_hash_bucket(struct efx_nic *efx,
+ const struct efx_filter_spec *spec)
+{
+ u32 hash = efx_filter_spec_hash(spec);
+
+ WARN_ON(!spin_is_locked(&efx->rps_hash_lock));
+ if (!efx->rps_hash_table)
+ return NULL;
+ return &efx->rps_hash_table[hash % EFX_ARFS_HASH_TABLE_SIZE];
+}
+
+struct efx_arfs_rule *efx_rps_hash_find(struct efx_nic *efx,
+ const struct efx_filter_spec *spec)
+{
+ struct efx_arfs_rule *rule;
+ struct hlist_head *head;
+ struct hlist_node *node;
+
+ head = efx_rps_hash_bucket(efx, spec);
+ if (!head)
+ return NULL;
+ hlist_for_each(node, head) {
+ rule = container_of(node, struct efx_arfs_rule, node);
+ if (efx_filter_spec_equal(spec, &rule->spec))
+ return rule;
+ }
+ return NULL;
+}
+
+struct efx_arfs_rule *efx_rps_hash_add(struct efx_nic *efx,
+ const struct efx_filter_spec *spec,
+ bool *new)
+{
+ struct efx_arfs_rule *rule;
+ struct hlist_head *head;
+ struct hlist_node *node;
+
+ head = efx_rps_hash_bucket(efx, spec);
+ if (!head)
+ return NULL;
+ hlist_for_each(node, head) {
+ rule = container_of(node, struct efx_arfs_rule, node);
+ if (efx_filter_spec_equal(spec, &rule->spec)) {
+ *new = false;
+ return rule;
+ }
+ }
+ rule = kmalloc(sizeof(*rule), GFP_ATOMIC);
+ *new = true;
+ if (rule) {
+ memcpy(&rule->spec, spec, sizeof(rule->spec));
+ hlist_add_head(&rule->node, head);
+ }
+ return rule;
+}
+
+void efx_rps_hash_del(struct efx_nic *efx, const struct efx_filter_spec *spec)
+{
+ struct efx_arfs_rule *rule;
+ struct hlist_head *head;
+ struct hlist_node *node;
+
+ head = efx_rps_hash_bucket(efx, spec);
+ if (WARN_ON(!head))
+ return;
+ hlist_for_each(node, head) {
+ rule = container_of(node, struct efx_arfs_rule, node);
+ if (efx_filter_spec_equal(spec, &rule->spec)) {
+ /* Someone already reused the entry. We know that if
+ * this check doesn't fire (i.e. filter_id == REMOVING)
+ * then the REMOVING mark was put there by our caller,
+ * because caller is holding a lock on filter table and
+ * only holders of that lock set REMOVING.
+ */
+ if (rule->filter_id != EFX_ARFS_FILTER_ID_REMOVING)
+ return;
+ hlist_del(node);
+ kfree(rule);
+ return;
+ }
+ }
+ /* We didn't find it. */
+ WARN_ON(1);
+}
+#endif
+
+/* RSS contexts. We're using linked lists and crappy O(n) algorithms, because
+ * (a) this is an infrequent control-plane operation and (b) n is small (max 64)
+ */
+struct efx_rss_context *efx_alloc_rss_context_entry(struct efx_nic *efx)
+{
+ struct list_head *head = &efx->rss_context.list;
+ struct efx_rss_context *ctx, *new;
+ u32 id = 1; /* Don't use zero, that refers to the master RSS context */
+
+ WARN_ON(!mutex_is_locked(&efx->rss_lock));
+
+ /* Search for first gap in the numbering */
+ list_for_each_entry(ctx, head, list) {
+ if (ctx->user_id != id)
+ break;
+ id++;
+ /* Check for wrap. If this happens, we have nearly 2^32
+ * allocated RSS contexts, which seems unlikely.
+ */
+ if (WARN_ON_ONCE(!id))
+ return NULL;
+ }
+
+ /* Create the new entry */
+ new = kmalloc(sizeof(struct efx_rss_context), GFP_KERNEL);
+ if (!new)
+ return NULL;
+ new->context_id = EFX_EF10_RSS_CONTEXT_INVALID;
+ new->rx_hash_udp_4tuple = false;
+
+ /* Insert the new entry into the gap */
+ new->user_id = id;
+ list_add_tail(&new->list, &ctx->list);
+ return new;
+}
+
+struct efx_rss_context *efx_find_rss_context_entry(struct efx_nic *efx, u32 id)
+{
+ struct list_head *head = &efx->rss_context.list;
+ struct efx_rss_context *ctx;
+
+ WARN_ON(!mutex_is_locked(&efx->rss_lock));
+
+ list_for_each_entry(ctx, head, list)
+ if (ctx->user_id == id)
+ return ctx;
+ return NULL;
+}
+
+void efx_free_rss_context_entry(struct efx_rss_context *ctx)
+{
+ list_del(&ctx->list);
+ kfree(ctx);
+}
+
/**************************************************************************
*
* PCI interface
diff --git a/drivers/net/ethernet/sfc/efx.h b/drivers/net/ethernet/sfc/efx.h
index 0cddc5ad77b1..3f759ebdcf10 100644
--- a/drivers/net/ethernet/sfc/efx.h
+++ b/drivers/net/ethernet/sfc/efx.h
@@ -34,7 +34,8 @@ extern unsigned int efx_piobuf_size;
extern bool efx_separate_tx_channels;
/* RX */
-void efx_set_default_rx_indir_table(struct efx_nic *efx);
+void efx_set_default_rx_indir_table(struct efx_nic *efx,
+ struct efx_rss_context *ctx);
void efx_rx_config_page_split(struct efx_nic *efx);
int efx_probe_rx_queue(struct efx_rx_queue *rx_queue);
void efx_remove_rx_queue(struct efx_rx_queue *rx_queue);
@@ -169,19 +170,52 @@ static inline s32 efx_filter_get_rx_ids(struct efx_nic *efx,
int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb,
u16 rxq_index, u32 flow_id);
bool __efx_filter_rfs_expire(struct efx_nic *efx, unsigned quota);
-static inline void efx_filter_rfs_expire(struct efx_channel *channel)
+static inline void efx_filter_rfs_expire(struct work_struct *data)
{
+ struct efx_channel *channel = container_of(data, struct efx_channel,
+ filter_work);
+
if (channel->rfs_filters_added >= 60 &&
__efx_filter_rfs_expire(channel->efx, 100))
channel->rfs_filters_added -= 60;
}
#define efx_filter_rfs_enabled() 1
#else
-static inline void efx_filter_rfs_expire(struct efx_channel *channel) {}
+static inline void efx_filter_rfs_expire(struct work_struct *data) {}
#define efx_filter_rfs_enabled() 0
#endif
bool efx_filter_is_mc_recipient(const struct efx_filter_spec *spec);
+bool efx_filter_spec_equal(const struct efx_filter_spec *left,
+ const struct efx_filter_spec *right);
+u32 efx_filter_spec_hash(const struct efx_filter_spec *spec);
+
+#ifdef CONFIG_RFS_ACCEL
+bool efx_rps_check_rule(struct efx_arfs_rule *rule, unsigned int filter_idx,
+ bool *force);
+
+struct efx_arfs_rule *efx_rps_hash_find(struct efx_nic *efx,
+ const struct efx_filter_spec *spec);
+
+/* @new is written to indicate if entry was newly added (true) or if an old
+ * entry was found and returned (false).
+ */
+struct efx_arfs_rule *efx_rps_hash_add(struct efx_nic *efx,
+ const struct efx_filter_spec *spec,
+ bool *new);
+
+void efx_rps_hash_del(struct efx_nic *efx, const struct efx_filter_spec *spec);
+#endif
+
+/* RSS contexts */
+struct efx_rss_context *efx_alloc_rss_context_entry(struct efx_nic *efx);
+struct efx_rss_context *efx_find_rss_context_entry(struct efx_nic *efx, u32 id);
+void efx_free_rss_context_entry(struct efx_rss_context *ctx);
+static inline bool efx_rss_active(struct efx_rss_context *ctx)
+{
+ return ctx->context_id != EFX_EF10_RSS_CONTEXT_INVALID;
+}
+
/* Channels */
int efx_channel_dummy_op_int(struct efx_channel *channel);
void efx_channel_dummy_op_void(struct efx_channel *channel);
diff --git a/drivers/net/ethernet/sfc/ethtool.c b/drivers/net/ethernet/sfc/ethtool.c
index 4db2dc2bf52f..3143588ffd77 100644
--- a/drivers/net/ethernet/sfc/ethtool.c
+++ b/drivers/net/ethernet/sfc/ethtool.c
@@ -808,7 +808,8 @@ static inline void ip6_fill_mask(__be32 *mask)
}
static int efx_ethtool_get_class_rule(struct efx_nic *efx,
- struct ethtool_rx_flow_spec *rule)
+ struct ethtool_rx_flow_spec *rule,
+ u32 *rss_context)
{
struct ethtool_tcpip4_spec *ip_entry = &rule->h_u.tcp_ip4_spec;
struct ethtool_tcpip4_spec *ip_mask = &rule->m_u.tcp_ip4_spec;
@@ -964,6 +965,11 @@ static int efx_ethtool_get_class_rule(struct efx_nic *efx,
rule->m_ext.vlan_tci = htons(0xfff);
}
+ if (spec.flags & EFX_FILTER_FLAG_RX_RSS) {
+ rule->flow_type |= FLOW_RSS;
+ *rss_context = spec.rss_context;
+ }
+
return rc;
}
@@ -972,6 +978,8 @@ efx_ethtool_get_rxnfc(struct net_device *net_dev,
struct ethtool_rxnfc *info, u32 *rule_locs)
{
struct efx_nic *efx = netdev_priv(net_dev);
+ u32 rss_context = 0;
+ s32 rc = 0;
switch (info->cmd) {
case ETHTOOL_GRXRINGS:
@@ -979,12 +987,22 @@ efx_ethtool_get_rxnfc(struct net_device *net_dev,
return 0;
case ETHTOOL_GRXFH: {
+ struct efx_rss_context *ctx = &efx->rss_context;
+
+ mutex_lock(&efx->rss_lock);
+ if (info->flow_type & FLOW_RSS && info->rss_context) {
+ ctx = efx_find_rss_context_entry(efx, info->rss_context);
+ if (!ctx) {
+ rc = -ENOENT;
+ goto out_unlock;
+ }
+ }
info->data = 0;
- if (!efx->rss_active) /* No RSS */
- return 0;
- switch (info->flow_type) {
+ if (!efx_rss_active(ctx)) /* No RSS */
+ goto out_unlock;
+ switch (info->flow_type & ~FLOW_RSS) {
case UDP_V4_FLOW:
- if (efx->rx_hash_udp_4tuple)
+ if (ctx->rx_hash_udp_4tuple)
/* fall through */
case TCP_V4_FLOW:
info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
@@ -995,7 +1013,7 @@ efx_ethtool_get_rxnfc(struct net_device *net_dev,
info->data |= RXH_IP_SRC | RXH_IP_DST;
break;
case UDP_V6_FLOW:
- if (efx->rx_hash_udp_4tuple)
+ if (ctx->rx_hash_udp_4tuple)
/* fall through */
case TCP_V6_FLOW:
info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
@@ -1008,7 +1026,9 @@ efx_ethtool_get_rxnfc(struct net_device *net_dev,
default:
break;
}
- return 0;
+out_unlock:
+ mutex_unlock(&efx->rss_lock);
+ return rc;
}
case ETHTOOL_GRXCLSRLCNT:
@@ -1023,10 +1043,14 @@ efx_ethtool_get_rxnfc(struct net_device *net_dev,
case ETHTOOL_GRXCLSRULE:
if (efx_filter_get_rx_id_limit(efx) == 0)
return -EOPNOTSUPP;
- return efx_ethtool_get_class_rule(efx, &info->fs);
+ rc = efx_ethtool_get_class_rule(efx, &info->fs, &rss_context);
+ if (rc < 0)
+ return rc;
+ if (info->fs.flow_type & FLOW_RSS)
+ info->rss_context = rss_context;
+ return 0;
- case ETHTOOL_GRXCLSRLALL: {
- s32 rc;
+ case ETHTOOL_GRXCLSRLALL:
info->data = efx_filter_get_rx_id_limit(efx);
if (info->data == 0)
return -EOPNOTSUPP;
@@ -1036,7 +1060,6 @@ efx_ethtool_get_rxnfc(struct net_device *net_dev,
return rc;
info->rule_cnt = rc;
return 0;
- }
default:
return -EOPNOTSUPP;
@@ -1054,7 +1077,8 @@ static inline bool ip6_mask_is_empty(__be32 mask[4])
}
static int efx_ethtool_set_class_rule(struct efx_nic *efx,
- struct ethtool_rx_flow_spec *rule)
+ struct ethtool_rx_flow_spec *rule,
+ u32 rss_context)
{
struct ethtool_tcpip4_spec *ip_entry = &rule->h_u.tcp_ip4_spec;
struct ethtool_tcpip4_spec *ip_mask = &rule->m_u.tcp_ip4_spec;
@@ -1064,8 +1088,10 @@ static int efx_ethtool_set_class_rule(struct efx_nic *efx,
struct ethtool_tcpip6_spec *ip6_mask = &rule->m_u.tcp_ip6_spec;
struct ethtool_usrip6_spec *uip6_entry = &rule->h_u.usr_ip6_spec;
struct ethtool_usrip6_spec *uip6_mask = &rule->m_u.usr_ip6_spec;
+ u32 flow_type = rule->flow_type & ~(FLOW_EXT | FLOW_RSS);
struct ethhdr *mac_entry = &rule->h_u.ether_spec;
struct ethhdr *mac_mask = &rule->m_u.ether_spec;
+ enum efx_filter_flags flags = 0;
struct efx_filter_spec spec;
int rc;
@@ -1084,19 +1110,26 @@ static int efx_ethtool_set_class_rule(struct efx_nic *efx,
rule->m_ext.data[1]))
return -EINVAL;
- efx_filter_init_rx(&spec, EFX_FILTER_PRI_MANUAL,
- efx->rx_scatter ? EFX_FILTER_FLAG_RX_SCATTER : 0,
+ if (efx->rx_scatter)
+ flags |= EFX_FILTER_FLAG_RX_SCATTER;
+ if (rule->flow_type & FLOW_RSS)
+ flags |= EFX_FILTER_FLAG_RX_RSS;
+
+ efx_filter_init_rx(&spec, EFX_FILTER_PRI_MANUAL, flags,
(rule->ring_cookie == RX_CLS_FLOW_DISC) ?
EFX_FILTER_RX_DMAQ_ID_DROP : rule->ring_cookie);
- switch (rule->flow_type & ~FLOW_EXT) {
+ if (rule->flow_type & FLOW_RSS)
+ spec.rss_context = rss_context;
+
+ switch (flow_type) {
case TCP_V4_FLOW:
case UDP_V4_FLOW:
spec.match_flags = (EFX_FILTER_MATCH_ETHER_TYPE |
EFX_FILTER_MATCH_IP_PROTO);
spec.ether_type = htons(ETH_P_IP);
- spec.ip_proto = ((rule->flow_type & ~FLOW_EXT) == TCP_V4_FLOW ?
- IPPROTO_TCP : IPPROTO_UDP);
+ spec.ip_proto = flow_type == TCP_V4_FLOW ? IPPROTO_TCP
+ : IPPROTO_UDP;
if (ip_mask->ip4dst) {
if (ip_mask->ip4dst != IP4_ADDR_FULL_MASK)
return -EINVAL;
@@ -1130,8 +1163,8 @@ static int efx_ethtool_set_class_rule(struct efx_nic *efx,
spec.match_flags = (EFX_FILTER_MATCH_ETHER_TYPE |
EFX_FILTER_MATCH_IP_PROTO);
spec.ether_type = htons(ETH_P_IPV6);
- spec.ip_proto = ((rule->flow_type & ~FLOW_EXT) == TCP_V6_FLOW ?
- IPPROTO_TCP : IPPROTO_UDP);
+ spec.ip_proto = flow_type == TCP_V6_FLOW ? IPPROTO_TCP
+ : IPPROTO_UDP;
if (!ip6_mask_is_empty(ip6_mask->ip6dst)) {
if (!ip6_mask_is_full(ip6_mask->ip6dst))
return -EINVAL;
@@ -1265,7 +1298,8 @@ static int efx_ethtool_set_rxnfc(struct net_device *net_dev,
switch (info->cmd) {
case ETHTOOL_SRXCLSRLINS:
- return efx_ethtool_set_class_rule(efx, &info->fs);
+ return efx_ethtool_set_class_rule(efx, &info->fs,
+ info->rss_context);
case ETHTOOL_SRXCLSRLDEL:
return efx_filter_remove_id_safe(efx, EFX_FILTER_PRI_MANUAL,
@@ -1280,7 +1314,9 @@ static u32 efx_ethtool_get_rxfh_indir_size(struct net_device *net_dev)
{
struct efx_nic *efx = netdev_priv(net_dev);
- return (efx->n_rx_channels == 1) ? 0 : ARRAY_SIZE(efx->rx_indir_table);
+ if (efx->n_rx_channels == 1)
+ return 0;
+ return ARRAY_SIZE(efx->rss_context.rx_indir_table);
}
static u32 efx_ethtool_get_rxfh_key_size(struct net_device *net_dev)
@@ -1303,9 +1339,11 @@ static int efx_ethtool_get_rxfh(struct net_device *net_dev, u32 *indir, u8 *key,
if (hfunc)
*hfunc = ETH_RSS_HASH_TOP;
if (indir)
- memcpy(indir, efx->rx_indir_table, sizeof(efx->rx_indir_table));
+ memcpy(indir, efx->rss_context.rx_indir_table,
+ sizeof(efx->rss_context.rx_indir_table));
if (key)
- memcpy(key, efx->rx_hash_key, efx->type->rx_hash_key_size);
+ memcpy(key, efx->rss_context.rx_hash_key,
+ efx->type->rx_hash_key_size);
return 0;
}
@@ -1321,13 +1359,109 @@ static int efx_ethtool_set_rxfh(struct net_device *net_dev, const u32 *indir,
return 0;
if (!key)
- key = efx->rx_hash_key;
+ key = efx->rss_context.rx_hash_key;
if (!indir)
- indir = efx->rx_indir_table;
+ indir = efx->rss_context.rx_indir_table;
return efx->type->rx_push_rss_config(efx, true, indir, key);
}
+static int efx_ethtool_get_rxfh_context(struct net_device *net_dev, u32 *indir,
+ u8 *key, u8 *hfunc, u32 rss_context)
+{
+ struct efx_nic *efx = netdev_priv(net_dev);
+ struct efx_rss_context *ctx;
+ int rc = 0;
+
+ if (!efx->type->rx_pull_rss_context_config)
+ return -EOPNOTSUPP;
+
+ mutex_lock(&efx->rss_lock);
+ ctx = efx_find_rss_context_entry(efx, rss_context);
+ if (!ctx) {
+ rc = -ENOENT;
+ goto out_unlock;
+ }
+ rc = efx->type->rx_pull_rss_context_config(efx, ctx);
+ if (rc)
+ goto out_unlock;
+
+ if (hfunc)
+ *hfunc = ETH_RSS_HASH_TOP;
+ if (indir)
+ memcpy(indir, ctx->rx_indir_table, sizeof(ctx->rx_indir_table));
+ if (key)
+ memcpy(key, ctx->rx_hash_key, efx->type->rx_hash_key_size);
+out_unlock:
+ mutex_unlock(&efx->rss_lock);
+ return rc;
+}
+
+static int efx_ethtool_set_rxfh_context(struct net_device *net_dev,
+ const u32 *indir, const u8 *key,
+ const u8 hfunc, u32 *rss_context,
+ bool delete)
+{
+ struct efx_nic *efx = netdev_priv(net_dev);
+ struct efx_rss_context *ctx;
+ bool allocated = false;
+ int rc;
+
+ if (!efx->type->rx_push_rss_context_config)
+ return -EOPNOTSUPP;
+ /* Hash function is Toeplitz, cannot be changed */
+ if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)
+ return -EOPNOTSUPP;
+
+ mutex_lock(&efx->rss_lock);
+
+ if (*rss_context == ETH_RXFH_CONTEXT_ALLOC) {
+ if (delete) {
+ /* alloc + delete == Nothing to do */
+ rc = -EINVAL;
+ goto out_unlock;
+ }
+ ctx = efx_alloc_rss_context_entry(efx);
+ if (!ctx) {
+ rc = -ENOMEM;
+ goto out_unlock;
+ }
+ ctx->context_id = EFX_EF10_RSS_CONTEXT_INVALID;
+ /* Initialise indir table and key to defaults */
+ efx_set_default_rx_indir_table(efx, ctx);
+ netdev_rss_key_fill(ctx->rx_hash_key, sizeof(ctx->rx_hash_key));
+ allocated = true;
+ } else {
+ ctx = efx_find_rss_context_entry(efx, *rss_context);
+ if (!ctx) {
+ rc = -ENOENT;
+ goto out_unlock;
+ }
+ }
+
+ if (delete) {
+ /* delete this context */
+ rc = efx->type->rx_push_rss_context_config(efx, ctx, NULL, NULL);
+ if (!rc)
+ efx_free_rss_context_entry(ctx);
+ goto out_unlock;
+ }
+
+ if (!key)
+ key = ctx->rx_hash_key;
+ if (!indir)
+ indir = ctx->rx_indir_table;
+
+ rc = efx->type->rx_push_rss_context_config(efx, ctx, indir, key);
+ if (rc && allocated)
+ efx_free_rss_context_entry(ctx);
+ else
+ *rss_context = ctx->user_id;
+out_unlock:
+ mutex_unlock(&efx->rss_lock);
+ return rc;
+}
+
static int efx_ethtool_get_ts_info(struct net_device *net_dev,
struct ethtool_ts_info *ts_info)
{
@@ -1375,6 +1509,36 @@ static int efx_ethtool_get_module_info(struct net_device *net_dev,
return ret;
}
+static int efx_ethtool_get_fecparam(struct net_device *net_dev,
+ struct ethtool_fecparam *fecparam)
+{
+ struct efx_nic *efx = netdev_priv(net_dev);
+ int rc;
+
+ if (!efx->phy_op || !efx->phy_op->get_fecparam)
+ return -EOPNOTSUPP;
+ mutex_lock(&efx->mac_lock);
+ rc = efx->phy_op->get_fecparam(efx, fecparam);
+ mutex_unlock(&efx->mac_lock);
+
+ return rc;
+}
+
+static int efx_ethtool_set_fecparam(struct net_device *net_dev,
+ struct ethtool_fecparam *fecparam)
+{
+ struct efx_nic *efx = netdev_priv(net_dev);
+ int rc;
+
+ if (!efx->phy_op || !efx->phy_op->get_fecparam)
+ return -EOPNOTSUPP;
+ mutex_lock(&efx->mac_lock);
+ rc = efx->phy_op->set_fecparam(efx, fecparam);
+ mutex_unlock(&efx->mac_lock);
+
+ return rc;
+}
+
const struct ethtool_ops efx_ethtool_ops = {
.get_drvinfo = efx_ethtool_get_drvinfo,
.get_regs_len = efx_ethtool_get_regs_len,
@@ -1403,9 +1567,13 @@ const struct ethtool_ops efx_ethtool_ops = {
.get_rxfh_key_size = efx_ethtool_get_rxfh_key_size,
.get_rxfh = efx_ethtool_get_rxfh,
.set_rxfh = efx_ethtool_set_rxfh,
+ .get_rxfh_context = efx_ethtool_get_rxfh_context,
+ .set_rxfh_context = efx_ethtool_set_rxfh_context,
.get_ts_info = efx_ethtool_get_ts_info,
.get_module_info = efx_ethtool_get_module_info,
.get_module_eeprom = efx_ethtool_get_module_eeprom,
.get_link_ksettings = efx_ethtool_get_link_ksettings,
.set_link_ksettings = efx_ethtool_set_link_ksettings,
+ .get_fecparam = efx_ethtool_get_fecparam,
+ .set_fecparam = efx_ethtool_set_fecparam,
};
diff --git a/drivers/net/ethernet/sfc/falcon/enum.h b/drivers/net/ethernet/sfc/falcon/enum.h
index 30a1136fc909..4824fcf5c3d4 100644
--- a/drivers/net/ethernet/sfc/falcon/enum.h
+++ b/drivers/net/ethernet/sfc/falcon/enum.h
@@ -81,7 +81,6 @@ enum ef4_loopback_mode {
(1 << LOOPBACK_XAUI) | \
(1 << LOOPBACK_GMII) | \
(1 << LOOPBACK_SGMII) | \
- (1 << LOOPBACK_SGMII) | \
(1 << LOOPBACK_XGBR) | \
(1 << LOOPBACK_XFI) | \
(1 << LOOPBACK_XAUI_FAR) | \
diff --git a/drivers/net/ethernet/sfc/falcon/mtd.c b/drivers/net/ethernet/sfc/falcon/mtd.c
index cde593cb1052..2d67e4621a3d 100644
--- a/drivers/net/ethernet/sfc/falcon/mtd.c
+++ b/drivers/net/ethernet/sfc/falcon/mtd.c
@@ -24,17 +24,8 @@
static int ef4_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)
{
struct ef4_nic *efx = mtd->priv;
- int rc;
- rc = efx->type->mtd_erase(mtd, erase->addr, erase->len);
- if (rc == 0) {
- erase->state = MTD_ERASE_DONE;
- } else {
- erase->state = MTD_ERASE_FAILED;
- erase->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
- }
- mtd_erase_callback(erase);
- return rc;
+ return efx->type->mtd_erase(mtd, erase->addr, erase->len);
}
static void ef4_mtd_sync(struct mtd_info *mtd)
diff --git a/drivers/net/ethernet/sfc/farch.c b/drivers/net/ethernet/sfc/farch.c
index 266b9bee1f3a..c72adf8b52ea 100644
--- a/drivers/net/ethernet/sfc/farch.c
+++ b/drivers/net/ethernet/sfc/farch.c
@@ -1630,12 +1630,12 @@ void efx_farch_rx_push_indir_table(struct efx_nic *efx)
size_t i = 0;
efx_dword_t dword;
- BUILD_BUG_ON(ARRAY_SIZE(efx->rx_indir_table) !=
+ BUILD_BUG_ON(ARRAY_SIZE(efx->rss_context.rx_indir_table) !=
FR_BZ_RX_INDIRECTION_TBL_ROWS);
for (i = 0; i < FR_BZ_RX_INDIRECTION_TBL_ROWS; i++) {
EFX_POPULATE_DWORD_1(dword, FRF_BZ_IT_QUEUE,
- efx->rx_indir_table[i]);
+ efx->rss_context.rx_indir_table[i]);
efx_writed(efx, &dword,
FR_BZ_RX_INDIRECTION_TBL +
FR_BZ_RX_INDIRECTION_TBL_STEP * i);
@@ -1647,14 +1647,14 @@ void efx_farch_rx_pull_indir_table(struct efx_nic *efx)
size_t i = 0;
efx_dword_t dword;
- BUILD_BUG_ON(ARRAY_SIZE(efx->rx_indir_table) !=
+ BUILD_BUG_ON(ARRAY_SIZE(efx->rss_context.rx_indir_table) !=
FR_BZ_RX_INDIRECTION_TBL_ROWS);
for (i = 0; i < FR_BZ_RX_INDIRECTION_TBL_ROWS; i++) {
efx_readd(efx, &dword,
FR_BZ_RX_INDIRECTION_TBL +
FR_BZ_RX_INDIRECTION_TBL_STEP * i);
- efx->rx_indir_table[i] = EFX_DWORD_FIELD(dword, FRF_BZ_IT_QUEUE);
+ efx->rss_context.rx_indir_table[i] = EFX_DWORD_FIELD(dword, FRF_BZ_IT_QUEUE);
}
}
@@ -1878,6 +1878,7 @@ struct efx_farch_filter_table {
};
struct efx_farch_filter_state {
+ struct rw_semaphore lock; /* Protects table contents */
struct efx_farch_filter_table table[EFX_FARCH_FILTER_TABLE_COUNT];
};
@@ -2032,8 +2033,7 @@ efx_farch_filter_from_gen_spec(struct efx_farch_filter_spec *spec,
{
bool is_full = false;
- if ((gen_spec->flags & EFX_FILTER_FLAG_RX_RSS) &&
- gen_spec->rss_context != EFX_FILTER_RSS_CONTEXT_DEFAULT)
+ if ((gen_spec->flags & EFX_FILTER_FLAG_RX_RSS) && gen_spec->rss_context)
return -EINVAL;
spec->priority = gen_spec->priority;
@@ -2398,9 +2398,13 @@ s32 efx_farch_filter_insert(struct efx_nic *efx,
if (rc)
return rc;
+ down_write(&state->lock);
+
table = &state->table[efx_farch_filter_spec_table_id(&spec)];
- if (table->size == 0)
- return -EINVAL;
+ if (table->size == 0) {
+ rc = -EINVAL;
+ goto out_unlock;
+ }
netif_vdbg(efx, hw, efx->net_dev,
"%s: type %d search_limit=%d", __func__, spec.type,
@@ -2413,8 +2417,6 @@ s32 efx_farch_filter_insert(struct efx_nic *efx,
EFX_FARCH_FILTER_MC_DEF - EFX_FARCH_FILTER_UC_DEF);
rep_index = spec.type - EFX_FARCH_FILTER_UC_DEF;
ins_index = rep_index;
-
- spin_lock_bh(&efx->filter_lock);
} else {
/* Search concurrently for
* (1) a filter to be replaced (rep_index): any filter
@@ -2444,8 +2446,6 @@ s32 efx_farch_filter_insert(struct efx_nic *efx,
ins_index = -1;
depth = 1;
- spin_lock_bh(&efx->filter_lock);
-
for (;;) {
if (!test_bit(i, table->used_bitmap)) {
if (ins_index < 0)
@@ -2464,7 +2464,7 @@ s32 efx_farch_filter_insert(struct efx_nic *efx,
/* Case (b) */
if (ins_index < 0) {
rc = -EBUSY;
- goto out;
+ goto out_unlock;
}
rep_index = -1;
break;
@@ -2484,11 +2484,11 @@ s32 efx_farch_filter_insert(struct efx_nic *efx,
if (spec.priority == saved_spec->priority && !replace_equal) {
rc = -EEXIST;
- goto out;
+ goto out_unlock;
}
if (spec.priority < saved_spec->priority) {
rc = -EPERM;
- goto out;
+ goto out_unlock;
}
if (saved_spec->priority == EFX_FILTER_PRI_AUTO ||
saved_spec->flags & EFX_FILTER_FLAG_RX_OVER_AUTO)
@@ -2529,8 +2529,8 @@ s32 efx_farch_filter_insert(struct efx_nic *efx,
__func__, spec.type, ins_index, spec.dmaq_id);
rc = efx_farch_filter_make_id(&spec, ins_index);
-out:
- spin_unlock_bh(&efx->filter_lock);
+out_unlock:
+ up_write(&state->lock);
return rc;
}
@@ -2605,11 +2605,11 @@ int efx_farch_filter_remove_safe(struct efx_nic *efx,
filter_idx = efx_farch_filter_id_index(filter_id);
if (filter_idx >= table->size)
return -ENOENT;
+ down_write(&state->lock);
spec = &table->spec[filter_idx];
- spin_lock_bh(&efx->filter_lock);
rc = efx_farch_filter_remove(efx, table, filter_idx, priority);
- spin_unlock_bh(&efx->filter_lock);
+ up_write(&state->lock);
return rc;
}
@@ -2623,30 +2623,28 @@ int efx_farch_filter_get_safe(struct efx_nic *efx,
struct efx_farch_filter_table *table;
struct efx_farch_filter_spec *spec;
unsigned int filter_idx;
- int rc;
+ int rc = -ENOENT;
+
+ down_read(&state->lock);
table_id = efx_farch_filter_id_table_id(filter_id);
if ((unsigned int)table_id >= EFX_FARCH_FILTER_TABLE_COUNT)
- return -ENOENT;
+ goto out_unlock;
table = &state->table[table_id];
filter_idx = efx_farch_filter_id_index(filter_id);
if (filter_idx >= table->size)
- return -ENOENT;
+ goto out_unlock;
spec = &table->spec[filter_idx];
- spin_lock_bh(&efx->filter_lock);
-
if (test_bit(filter_idx, table->used_bitmap) &&
spec->priority == priority) {
efx_farch_filter_to_gen_spec(spec_buf, spec);
rc = 0;
- } else {
- rc = -ENOENT;
}
- spin_unlock_bh(&efx->filter_lock);
-
+out_unlock:
+ up_read(&state->lock);
return rc;
}
@@ -2659,13 +2657,13 @@ efx_farch_filter_table_clear(struct efx_nic *efx,
struct efx_farch_filter_table *table = &state->table[table_id];
unsigned int filter_idx;
- spin_lock_bh(&efx->filter_lock);
+ down_write(&state->lock);
for (filter_idx = 0; filter_idx < table->size; ++filter_idx) {
if (table->spec[filter_idx].priority != EFX_FILTER_PRI_AUTO)
efx_farch_filter_remove(efx, table,
filter_idx, priority);
}
- spin_unlock_bh(&efx->filter_lock);
+ up_write(&state->lock);
}
int efx_farch_filter_clear_rx(struct efx_nic *efx,
@@ -2689,7 +2687,7 @@ u32 efx_farch_filter_count_rx_used(struct efx_nic *efx,
unsigned int filter_idx;
u32 count = 0;
- spin_lock_bh(&efx->filter_lock);
+ down_read(&state->lock);
for (table_id = EFX_FARCH_FILTER_TABLE_RX_IP;
table_id <= EFX_FARCH_FILTER_TABLE_RX_DEF;
@@ -2702,7 +2700,7 @@ u32 efx_farch_filter_count_rx_used(struct efx_nic *efx,
}
}
- spin_unlock_bh(&efx->filter_lock);
+ up_read(&state->lock);
return count;
}
@@ -2717,7 +2715,7 @@ s32 efx_farch_filter_get_rx_ids(struct efx_nic *efx,
unsigned int filter_idx;
s32 count = 0;
- spin_lock_bh(&efx->filter_lock);
+ down_read(&state->lock);
for (table_id = EFX_FARCH_FILTER_TABLE_RX_IP;
table_id <= EFX_FARCH_FILTER_TABLE_RX_DEF;
@@ -2736,7 +2734,7 @@ s32 efx_farch_filter_get_rx_ids(struct efx_nic *efx,
}
}
out:
- spin_unlock_bh(&efx->filter_lock);
+ up_read(&state->lock);
return count;
}
@@ -2750,7 +2748,7 @@ void efx_farch_filter_table_restore(struct efx_nic *efx)
efx_oword_t filter;
unsigned int filter_idx;
- spin_lock_bh(&efx->filter_lock);
+ down_write(&state->lock);
for (table_id = 0; table_id < EFX_FARCH_FILTER_TABLE_COUNT; table_id++) {
table = &state->table[table_id];
@@ -2771,7 +2769,7 @@ void efx_farch_filter_table_restore(struct efx_nic *efx)
efx_farch_filter_push_rx_config(efx);
efx_farch_filter_push_tx_limits(efx);
- spin_unlock_bh(&efx->filter_lock);
+ up_write(&state->lock);
}
void efx_farch_filter_table_remove(struct efx_nic *efx)
@@ -2865,7 +2863,7 @@ void efx_farch_filter_update_rx_scatter(struct efx_nic *efx)
efx_oword_t filter;
unsigned int filter_idx;
- spin_lock_bh(&efx->filter_lock);
+ down_write(&state->lock);
for (table_id = EFX_FARCH_FILTER_TABLE_RX_IP;
table_id <= EFX_FARCH_FILTER_TABLE_RX_DEF;
@@ -2897,33 +2895,57 @@ void efx_farch_filter_update_rx_scatter(struct efx_nic *efx)
efx_farch_filter_push_rx_config(efx);
- spin_unlock_bh(&efx->filter_lock);
+ up_write(&state->lock);
}
#ifdef CONFIG_RFS_ACCEL
-s32 efx_farch_filter_rfs_insert(struct efx_nic *efx,
- struct efx_filter_spec *gen_spec)
-{
- return efx_farch_filter_insert(efx, gen_spec, true);
-}
-
bool efx_farch_filter_rfs_expire_one(struct efx_nic *efx, u32 flow_id,
unsigned int index)
{
struct efx_farch_filter_state *state = efx->filter_state;
- struct efx_farch_filter_table *table =
- &state->table[EFX_FARCH_FILTER_TABLE_RX_IP];
+ struct efx_farch_filter_table *table;
+ bool ret = false, force = false;
+ u16 arfs_id;
+ down_write(&state->lock);
+ spin_lock_bh(&efx->rps_hash_lock);
+ table = &state->table[EFX_FARCH_FILTER_TABLE_RX_IP];
if (test_bit(index, table->used_bitmap) &&
- table->spec[index].priority == EFX_FILTER_PRI_HINT &&
- rps_may_expire_flow(efx->net_dev, table->spec[index].dmaq_id,
- flow_id, index)) {
- efx_farch_filter_table_clear_entry(efx, table, index);
- return true;
+ table->spec[index].priority == EFX_FILTER_PRI_HINT) {
+ struct efx_arfs_rule *rule = NULL;
+ struct efx_filter_spec spec;
+
+ efx_farch_filter_to_gen_spec(&spec, &table->spec[index]);
+ if (!efx->rps_hash_table) {
+ /* In the absence of the table, we always returned 0 to
+ * ARFS, so use the same to query it.
+ */
+ arfs_id = 0;
+ } else {
+ rule = efx_rps_hash_find(efx, &spec);
+ if (!rule) {
+ /* ARFS table doesn't know of this filter, remove it */
+ force = true;
+ } else {
+ arfs_id = rule->arfs_id;
+ if (!efx_rps_check_rule(rule, index, &force))
+ goto out_unlock;
+ }
+ }
+ if (force || rps_may_expire_flow(efx->net_dev, spec.dmaq_id,
+ flow_id, arfs_id)) {
+ if (rule)
+ rule->filter_id = EFX_ARFS_FILTER_ID_REMOVING;
+ efx_rps_hash_del(efx, &spec);
+ efx_farch_filter_table_clear_entry(efx, table, index);
+ ret = true;
+ }
}
-
- return false;
+out_unlock:
+ spin_unlock_bh(&efx->rps_hash_lock);
+ up_write(&state->lock);
+ return ret;
}
#endif /* CONFIG_RFS_ACCEL */
diff --git a/drivers/net/ethernet/sfc/filter.h b/drivers/net/ethernet/sfc/filter.h
index 8189a1cd973f..59021ad6d98d 100644
--- a/drivers/net/ethernet/sfc/filter.h
+++ b/drivers/net/ethernet/sfc/filter.h
@@ -125,7 +125,9 @@ enum efx_encap_type {
* @match_flags: Match type flags, from &enum efx_filter_match_flags
* @priority: Priority of the filter, from &enum efx_filter_priority
* @flags: Miscellaneous flags, from &enum efx_filter_flags
- * @rss_context: RSS context to use, if %EFX_FILTER_FLAG_RX_RSS is set
+ * @rss_context: RSS context to use, if %EFX_FILTER_FLAG_RX_RSS is set. This
+ * is a user_id (with 0 meaning the driver/default RSS context), not an
+ * MCFW context_id.
* @dmaq_id: Source/target queue index, or %EFX_FILTER_RX_DMAQ_ID_DROP for
* an RX drop filter
* @outer_vid: Outer VLAN ID to match, if %EFX_FILTER_MATCH_OUTER_VID is set
@@ -173,7 +175,6 @@ struct efx_filter_spec {
};
enum {
- EFX_FILTER_RSS_CONTEXT_DEFAULT = 0xffffffff,
EFX_FILTER_RX_DMAQ_ID_DROP = 0xfff
};
@@ -185,7 +186,7 @@ static inline void efx_filter_init_rx(struct efx_filter_spec *spec,
memset(spec, 0, sizeof(*spec));
spec->priority = priority;
spec->flags = EFX_FILTER_FLAG_RX | flags;
- spec->rss_context = EFX_FILTER_RSS_CONTEXT_DEFAULT;
+ spec->rss_context = 0;
spec->dmaq_id = rxq_id;
}
diff --git a/drivers/net/ethernet/sfc/mcdi.c b/drivers/net/ethernet/sfc/mcdi.c
index 9c2567b0d93e..dfad93fca0a6 100644
--- a/drivers/net/ethernet/sfc/mcdi.c
+++ b/drivers/net/ethernet/sfc/mcdi.c
@@ -375,7 +375,7 @@ static int efx_mcdi_poll(struct efx_nic *efx)
* because generally mcdi responses are fast. After that, back off
* and poll once a jiffy (approximately)
*/
- spins = TICK_USEC;
+ spins = USER_TICK_USEC;
finish = jiffies + MCDI_RPC_TIMEOUT;
while (1) {
diff --git a/drivers/net/ethernet/sfc/mcdi_mon.c b/drivers/net/ethernet/sfc/mcdi_mon.c
index f97da05952c7..f17751559ccc 100644
--- a/drivers/net/ethernet/sfc/mcdi_mon.c
+++ b/drivers/net/ethernet/sfc/mcdi_mon.c
@@ -298,7 +298,7 @@ efx_mcdi_mon_add_attr(struct efx_nic *efx, const char *name,
attr->limit_value = limit_value;
sysfs_attr_init(&attr->dev_attr.attr);
attr->dev_attr.attr.name = attr->name;
- attr->dev_attr.attr.mode = S_IRUGO;
+ attr->dev_attr.attr.mode = 0444;
attr->dev_attr.show = reader;
hwmon->group.attrs[hwmon->n_attrs++] = &attr->dev_attr.attr;
}
diff --git a/drivers/net/ethernet/sfc/mcdi_pcol.h b/drivers/net/ethernet/sfc/mcdi_pcol.h
index 869d76f8f589..3839eec783ea 100644
--- a/drivers/net/ethernet/sfc/mcdi_pcol.h
+++ b/drivers/net/ethernet/sfc/mcdi_pcol.h
@@ -273,7 +273,8 @@
#define MC_CMD_ERR_NO_PRIVILEGE 0x1013
/* Workaround 26807 could not be turned on/off because some functions
* have already installed filters. See the comment at
- * MC_CMD_WORKAROUND_BUG26807. */
+ * MC_CMD_WORKAROUND_BUG26807.
+ * May also returned for other operations such as sub-variant switching. */
#define MC_CMD_ERR_FILTERS_PRESENT 0x1014
/* The clock whose frequency you've attempted to set set
* doesn't exist on this NIC */
@@ -292,6 +293,10 @@
* away. This is distinct from MC_CMD_ERR_DATAPATH_DISABLED in that the
* datapath absence may be temporary*/
#define MC_CMD_ERR_NO_DATAPATH 0x1019
+/* The operation could not complete because some VIs are allocated */
+#define MC_CMD_ERR_VIS_PRESENT 0x101a
+/* The operation could not complete because some PIO buffers are allocated */
+#define MC_CMD_ERR_PIOBUFS_PRESENT 0x101b
#define MC_CMD_ERR_CODE_OFST 0
@@ -312,10 +317,17 @@
#define SIENA_MC_BOOTROM_COPYCODE_VEC (0x800 - 3 * 0x4)
#define HUNT_MC_BOOTROM_COPYCODE_VEC (0x8000 - 3 * 0x4)
#define MEDFORD_MC_BOOTROM_COPYCODE_VEC (0x10000 - 3 * 0x4)
-/* Points to the recovery mode entry point. */
+/* Points to the recovery mode entry point. Misnamed but kept for compatibility. */
#define SIENA_MC_BOOTROM_NOFLASH_VEC (0x800 - 2 * 0x4)
#define HUNT_MC_BOOTROM_NOFLASH_VEC (0x8000 - 2 * 0x4)
#define MEDFORD_MC_BOOTROM_NOFLASH_VEC (0x10000 - 2 * 0x4)
+/* Points to the recovery mode entry point. Same as above, but the right name. */
+#define SIENA_MC_BOOTROM_RECOVERY_VEC (0x800 - 2 * 0x4)
+#define HUNT_MC_BOOTROM_RECOVERY_VEC (0x8000 - 2 * 0x4)
+#define MEDFORD_MC_BOOTROM_RECOVERY_VEC (0x10000 - 2 * 0x4)
+
+/* Points to noflash mode entry point. */
+#define MEDFORD_MC_BOOTROM_REAL_NOFLASH_VEC (0x10000 - 4 * 0x4)
/* The command set exported by the boot ROM (MCDI v0) */
#define MC_CMD_GET_VERSION_V0_SUPPORTED_FUNCS { \
@@ -365,7 +377,7 @@
#define MCDI_EVENT_LEVEL_LBN 33
#define MCDI_EVENT_LEVEL_WIDTH 3
/* enum: Info. */
-#define MCDI_EVENT_LEVEL_INFO 0x0
+#define MCDI_EVENT_LEVEL_INFO 0x0
/* enum: Warning. */
#define MCDI_EVENT_LEVEL_WARN 0x1
/* enum: Error. */
@@ -385,21 +397,21 @@
#define MCDI_EVENT_LINKCHANGE_SPEED_LBN 16
#define MCDI_EVENT_LINKCHANGE_SPEED_WIDTH 4
/* enum: Link is down or link speed could not be determined */
-#define MCDI_EVENT_LINKCHANGE_SPEED_UNKNOWN 0x0
+#define MCDI_EVENT_LINKCHANGE_SPEED_UNKNOWN 0x0
/* enum: 100Mbs */
-#define MCDI_EVENT_LINKCHANGE_SPEED_100M 0x1
+#define MCDI_EVENT_LINKCHANGE_SPEED_100M 0x1
/* enum: 1Gbs */
-#define MCDI_EVENT_LINKCHANGE_SPEED_1G 0x2
+#define MCDI_EVENT_LINKCHANGE_SPEED_1G 0x2
/* enum: 10Gbs */
-#define MCDI_EVENT_LINKCHANGE_SPEED_10G 0x3
+#define MCDI_EVENT_LINKCHANGE_SPEED_10G 0x3
/* enum: 40Gbs */
-#define MCDI_EVENT_LINKCHANGE_SPEED_40G 0x4
+#define MCDI_EVENT_LINKCHANGE_SPEED_40G 0x4
/* enum: 25Gbs */
-#define MCDI_EVENT_LINKCHANGE_SPEED_25G 0x5
+#define MCDI_EVENT_LINKCHANGE_SPEED_25G 0x5
/* enum: 50Gbs */
-#define MCDI_EVENT_LINKCHANGE_SPEED_50G 0x6
+#define MCDI_EVENT_LINKCHANGE_SPEED_50G 0x6
/* enum: 100Gbs */
-#define MCDI_EVENT_LINKCHANGE_SPEED_100G 0x7
+#define MCDI_EVENT_LINKCHANGE_SPEED_100G 0x7
#define MCDI_EVENT_LINKCHANGE_FCNTL_LBN 20
#define MCDI_EVENT_LINKCHANGE_FCNTL_WIDTH 4
#define MCDI_EVENT_LINKCHANGE_LINK_FLAGS_LBN 24
@@ -606,23 +618,23 @@
/* enum: Transmit error */
#define MCDI_EVENT_CODE_TX_ERR 0xb
/* enum: Tx flush has completed */
-#define MCDI_EVENT_CODE_TX_FLUSH 0xc
+#define MCDI_EVENT_CODE_TX_FLUSH 0xc
/* enum: PTP packet received timestamp */
-#define MCDI_EVENT_CODE_PTP_RX 0xd
+#define MCDI_EVENT_CODE_PTP_RX 0xd
/* enum: PTP NIC failure */
-#define MCDI_EVENT_CODE_PTP_FAULT 0xe
+#define MCDI_EVENT_CODE_PTP_FAULT 0xe
/* enum: PTP PPS event */
-#define MCDI_EVENT_CODE_PTP_PPS 0xf
+#define MCDI_EVENT_CODE_PTP_PPS 0xf
/* enum: Rx flush has completed */
-#define MCDI_EVENT_CODE_RX_FLUSH 0x10
+#define MCDI_EVENT_CODE_RX_FLUSH 0x10
/* enum: Receive error */
#define MCDI_EVENT_CODE_RX_ERR 0x11
/* enum: AOE fault */
-#define MCDI_EVENT_CODE_AOE 0x12
+#define MCDI_EVENT_CODE_AOE 0x12
/* enum: Network port calibration failed (VCAL). */
-#define MCDI_EVENT_CODE_VCAL_FAIL 0x13
+#define MCDI_EVENT_CODE_VCAL_FAIL 0x13
/* enum: HW PPS event */
-#define MCDI_EVENT_CODE_HW_PPS 0x14
+#define MCDI_EVENT_CODE_HW_PPS 0x14
/* enum: The MC has rebooted (huntington and later, siena uses CODE_REBOOT and
* a different format)
*/
@@ -654,7 +666,7 @@
/* enum: Artificial event generated by host and posted via MC for test
* purposes.
*/
-#define MCDI_EVENT_CODE_TESTGEN 0xfa
+#define MCDI_EVENT_CODE_TESTGEN 0xfa
#define MCDI_EVENT_CMDDONE_DATA_OFST 0
#define MCDI_EVENT_CMDDONE_DATA_LEN 4
#define MCDI_EVENT_CMDDONE_DATA_LBN 0
@@ -784,7 +796,7 @@
#define FCDI_EVENT_LEVEL_LBN 33
#define FCDI_EVENT_LEVEL_WIDTH 3
/* enum: Info. */
-#define FCDI_EVENT_LEVEL_INFO 0x0
+#define FCDI_EVENT_LEVEL_INFO 0x0
/* enum: Warning. */
#define FCDI_EVENT_LEVEL_WARN 0x1
/* enum: Error. */
@@ -916,7 +928,7 @@
#define MUM_EVENT_LEVEL_LBN 33
#define MUM_EVENT_LEVEL_WIDTH 3
/* enum: Info. */
-#define MUM_EVENT_LEVEL_INFO 0x0
+#define MUM_EVENT_LEVEL_INFO 0x0
/* enum: Warning. */
#define MUM_EVENT_LEVEL_WARN 0x1
/* enum: Error. */
@@ -1002,7 +1014,9 @@
/***********************************/
/* MC_CMD_READ32
- * Read multiple 32byte words from MC memory.
+ * Read multiple 32byte words from MC memory. Note - this command really
+ * belongs to INSECURE category but is required by shmboot. The command handler
+ * has additional checks to reject insecure calls.
*/
#define MC_CMD_READ32 0x1
@@ -1050,7 +1064,9 @@
/***********************************/
/* MC_CMD_COPYCODE
- * Copy MC code between two locations and jump.
+ * Copy MC code between two locations and jump. Note - this command really
+ * belongs to INSECURE category but is required by shmboot. The command handler
+ * has additional checks to reject insecure calls.
*/
#define MC_CMD_COPYCODE 0x3
@@ -1139,7 +1155,7 @@
#define MC_CMD_GET_BOOT_STATUS_OUT_BOOT_OFFSET_OFST 0
#define MC_CMD_GET_BOOT_STATUS_OUT_BOOT_OFFSET_LEN 4
/* enum: indicates that the MC wasn't flash booted */
-#define MC_CMD_GET_BOOT_STATUS_OUT_BOOT_OFFSET_NULL 0xdeadbeef
+#define MC_CMD_GET_BOOT_STATUS_OUT_BOOT_OFFSET_NULL 0xdeadbeef
#define MC_CMD_GET_BOOT_STATUS_OUT_FLAGS_OFST 4
#define MC_CMD_GET_BOOT_STATUS_OUT_FLAGS_LEN 4
#define MC_CMD_GET_BOOT_STATUS_OUT_FLAGS_WATCHDOG_LBN 0
@@ -1555,11 +1571,10 @@
#define MC_CMD_PTP_IN_MANFTEST_PACKET_TEST_ENABLE_OFST 8
#define MC_CMD_PTP_IN_MANFTEST_PACKET_TEST_ENABLE_LEN 4
-/* MC_CMD_PTP_IN_RESET_STATS msgrequest */
+/* MC_CMD_PTP_IN_RESET_STATS msgrequest: Reset PTP statistics */
#define MC_CMD_PTP_IN_RESET_STATS_LEN 8
/* MC_CMD_PTP_IN_CMD_OFST 0 */
/* MC_CMD_PTP_IN_CMD_LEN 4 */
-/* Reset PTP statistics */
/* MC_CMD_PTP_IN_PERIPH_ID_OFST 4 */
/* MC_CMD_PTP_IN_PERIPH_ID_LEN 4 */
@@ -1710,11 +1725,10 @@
/* enum: External. */
#define MC_CMD_PTP_CLK_SRC_EXTERNAL 0x1
-/* MC_CMD_PTP_IN_RST_CLK msgrequest */
+/* MC_CMD_PTP_IN_RST_CLK msgrequest: Reset value of Timer Reg. */
#define MC_CMD_PTP_IN_RST_CLK_LEN 8
/* MC_CMD_PTP_IN_CMD_OFST 0 */
/* MC_CMD_PTP_IN_CMD_LEN 4 */
-/* Reset value of Timer Reg. */
/* MC_CMD_PTP_IN_PERIPH_ID_OFST 4 */
/* MC_CMD_PTP_IN_PERIPH_ID_LEN 4 */
@@ -2687,8 +2701,16 @@
#define MC_CMD_DRV_ATTACH_IN_NEW_STATE_LEN 4
#define MC_CMD_DRV_ATTACH_LBN 0
#define MC_CMD_DRV_ATTACH_WIDTH 1
+#define MC_CMD_DRV_ATTACH_IN_ATTACH_LBN 0
+#define MC_CMD_DRV_ATTACH_IN_ATTACH_WIDTH 1
#define MC_CMD_DRV_PREBOOT_LBN 1
#define MC_CMD_DRV_PREBOOT_WIDTH 1
+#define MC_CMD_DRV_ATTACH_IN_PREBOOT_LBN 1
+#define MC_CMD_DRV_ATTACH_IN_PREBOOT_WIDTH 1
+#define MC_CMD_DRV_ATTACH_IN_SUBVARIANT_AWARE_LBN 2
+#define MC_CMD_DRV_ATTACH_IN_SUBVARIANT_AWARE_WIDTH 1
+#define MC_CMD_DRV_ATTACH_IN_WANT_VI_SPREADING_LBN 3
+#define MC_CMD_DRV_ATTACH_IN_WANT_VI_SPREADING_WIDTH 1
/* 1 to set new state, or 0 to just report the existing state */
#define MC_CMD_DRV_ATTACH_IN_UPDATE_OFST 4
#define MC_CMD_DRV_ATTACH_IN_UPDATE_LEN 4
@@ -2711,8 +2733,14 @@
* support
*/
#define MC_CMD_FW_RULES_ENGINE 0x5
+/* enum: Prefer to use firmware with additional DPDK support */
+#define MC_CMD_FW_DPDK 0x6
+/* enum: Prefer to use "l3xudp" custom datapath firmware (see SF-119495-PD and
+ * bug69716)
+ */
+#define MC_CMD_FW_L3XUDP 0x7
/* enum: Only this option is allowed for non-admin functions */
-#define MC_CMD_FW_DONT_CARE 0xffffffff
+#define MC_CMD_FW_DONT_CARE 0xffffffff
/* MC_CMD_DRV_ATTACH_OUT msgresponse */
#define MC_CMD_DRV_ATTACH_OUT_LEN 4
@@ -2740,6 +2768,11 @@
* refers to the Sorrento external FPGA port.
*/
#define MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_NO_ACTIVE_PORT 0x3
+/* enum: If set, indicates that VI spreading is currently enabled. Will always
+ * indicate the current state, regardless of the value in the WANT_VI_SPREADING
+ * input.
+ */
+#define MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_VI_SPREADING_ENABLED 0x4
/***********************************/
@@ -3294,83 +3327,83 @@
#define MC_CMD_GET_LOOPBACK_MODES_OUT_100M_LO_OFST 0
#define MC_CMD_GET_LOOPBACK_MODES_OUT_100M_HI_OFST 4
/* enum: None. */
-#define MC_CMD_LOOPBACK_NONE 0x0
+#define MC_CMD_LOOPBACK_NONE 0x0
/* enum: Data. */
-#define MC_CMD_LOOPBACK_DATA 0x1
+#define MC_CMD_LOOPBACK_DATA 0x1
/* enum: GMAC. */
-#define MC_CMD_LOOPBACK_GMAC 0x2
+#define MC_CMD_LOOPBACK_GMAC 0x2
/* enum: XGMII. */
#define MC_CMD_LOOPBACK_XGMII 0x3
/* enum: XGXS. */
-#define MC_CMD_LOOPBACK_XGXS 0x4
+#define MC_CMD_LOOPBACK_XGXS 0x4
/* enum: XAUI. */
-#define MC_CMD_LOOPBACK_XAUI 0x5
+#define MC_CMD_LOOPBACK_XAUI 0x5
/* enum: GMII. */
-#define MC_CMD_LOOPBACK_GMII 0x6
+#define MC_CMD_LOOPBACK_GMII 0x6
/* enum: SGMII. */
-#define MC_CMD_LOOPBACK_SGMII 0x7
+#define MC_CMD_LOOPBACK_SGMII 0x7
/* enum: XGBR. */
-#define MC_CMD_LOOPBACK_XGBR 0x8
+#define MC_CMD_LOOPBACK_XGBR 0x8
/* enum: XFI. */
-#define MC_CMD_LOOPBACK_XFI 0x9
+#define MC_CMD_LOOPBACK_XFI 0x9
/* enum: XAUI Far. */
-#define MC_CMD_LOOPBACK_XAUI_FAR 0xa
+#define MC_CMD_LOOPBACK_XAUI_FAR 0xa
/* enum: GMII Far. */
-#define MC_CMD_LOOPBACK_GMII_FAR 0xb
+#define MC_CMD_LOOPBACK_GMII_FAR 0xb
/* enum: SGMII Far. */
-#define MC_CMD_LOOPBACK_SGMII_FAR 0xc
+#define MC_CMD_LOOPBACK_SGMII_FAR 0xc
/* enum: XFI Far. */
-#define MC_CMD_LOOPBACK_XFI_FAR 0xd
+#define MC_CMD_LOOPBACK_XFI_FAR 0xd
/* enum: GPhy. */
-#define MC_CMD_LOOPBACK_GPHY 0xe
+#define MC_CMD_LOOPBACK_GPHY 0xe
/* enum: PhyXS. */
-#define MC_CMD_LOOPBACK_PHYXS 0xf
+#define MC_CMD_LOOPBACK_PHYXS 0xf
/* enum: PCS. */
-#define MC_CMD_LOOPBACK_PCS 0x10
+#define MC_CMD_LOOPBACK_PCS 0x10
/* enum: PMA-PMD. */
-#define MC_CMD_LOOPBACK_PMAPMD 0x11
+#define MC_CMD_LOOPBACK_PMAPMD 0x11
/* enum: Cross-Port. */
-#define MC_CMD_LOOPBACK_XPORT 0x12
+#define MC_CMD_LOOPBACK_XPORT 0x12
/* enum: XGMII-Wireside. */
-#define MC_CMD_LOOPBACK_XGMII_WS 0x13
+#define MC_CMD_LOOPBACK_XGMII_WS 0x13
/* enum: XAUI Wireside. */
-#define MC_CMD_LOOPBACK_XAUI_WS 0x14
+#define MC_CMD_LOOPBACK_XAUI_WS 0x14
/* enum: XAUI Wireside Far. */
-#define MC_CMD_LOOPBACK_XAUI_WS_FAR 0x15
+#define MC_CMD_LOOPBACK_XAUI_WS_FAR 0x15
/* enum: XAUI Wireside near. */
-#define MC_CMD_LOOPBACK_XAUI_WS_NEAR 0x16
+#define MC_CMD_LOOPBACK_XAUI_WS_NEAR 0x16
/* enum: GMII Wireside. */
-#define MC_CMD_LOOPBACK_GMII_WS 0x17
+#define MC_CMD_LOOPBACK_GMII_WS 0x17
/* enum: XFI Wireside. */
-#define MC_CMD_LOOPBACK_XFI_WS 0x18
+#define MC_CMD_LOOPBACK_XFI_WS 0x18
/* enum: XFI Wireside Far. */
-#define MC_CMD_LOOPBACK_XFI_WS_FAR 0x19
+#define MC_CMD_LOOPBACK_XFI_WS_FAR 0x19
/* enum: PhyXS Wireside. */
-#define MC_CMD_LOOPBACK_PHYXS_WS 0x1a
+#define MC_CMD_LOOPBACK_PHYXS_WS 0x1a
/* enum: PMA lanes MAC-Serdes. */
-#define MC_CMD_LOOPBACK_PMA_INT 0x1b
+#define MC_CMD_LOOPBACK_PMA_INT 0x1b
/* enum: KR Serdes Parallel (Encoder). */
-#define MC_CMD_LOOPBACK_SD_NEAR 0x1c
+#define MC_CMD_LOOPBACK_SD_NEAR 0x1c
/* enum: KR Serdes Serial. */
-#define MC_CMD_LOOPBACK_SD_FAR 0x1d
+#define MC_CMD_LOOPBACK_SD_FAR 0x1d
/* enum: PMA lanes MAC-Serdes Wireside. */
-#define MC_CMD_LOOPBACK_PMA_INT_WS 0x1e
+#define MC_CMD_LOOPBACK_PMA_INT_WS 0x1e
/* enum: KR Serdes Parallel Wireside (Full PCS). */
-#define MC_CMD_LOOPBACK_SD_FEP2_WS 0x1f
+#define MC_CMD_LOOPBACK_SD_FEP2_WS 0x1f
/* enum: KR Serdes Parallel Wireside (Sym Aligner to TX). */
-#define MC_CMD_LOOPBACK_SD_FEP1_5_WS 0x20
+#define MC_CMD_LOOPBACK_SD_FEP1_5_WS 0x20
/* enum: KR Serdes Parallel Wireside (Deserializer to Serializer). */
-#define MC_CMD_LOOPBACK_SD_FEP_WS 0x21
+#define MC_CMD_LOOPBACK_SD_FEP_WS 0x21
/* enum: KR Serdes Serial Wireside. */
-#define MC_CMD_LOOPBACK_SD_FES_WS 0x22
+#define MC_CMD_LOOPBACK_SD_FES_WS 0x22
/* enum: Near side of AOE Siena side port */
-#define MC_CMD_LOOPBACK_AOE_INT_NEAR 0x23
+#define MC_CMD_LOOPBACK_AOE_INT_NEAR 0x23
/* enum: Medford Wireside datapath loopback */
-#define MC_CMD_LOOPBACK_DATA_WS 0x24
+#define MC_CMD_LOOPBACK_DATA_WS 0x24
/* enum: Force link up without setting up any physical loopback (snapper use
* only)
*/
-#define MC_CMD_LOOPBACK_FORCE_EXT_LINK 0x25
+#define MC_CMD_LOOPBACK_FORCE_EXT_LINK 0x25
/* Supported loopbacks. */
#define MC_CMD_GET_LOOPBACK_MODES_OUT_1G_OFST 8
#define MC_CMD_GET_LOOPBACK_MODES_OUT_1G_LEN 8
@@ -3410,83 +3443,83 @@
#define MC_CMD_GET_LOOPBACK_MODES_OUT_V2_100M_LO_OFST 0
#define MC_CMD_GET_LOOPBACK_MODES_OUT_V2_100M_HI_OFST 4
/* enum: None. */
-/* MC_CMD_LOOPBACK_NONE 0x0 */
+/* MC_CMD_LOOPBACK_NONE 0x0 */
/* enum: Data. */
-/* MC_CMD_LOOPBACK_DATA 0x1 */
+/* MC_CMD_LOOPBACK_DATA 0x1 */
/* enum: GMAC. */
-/* MC_CMD_LOOPBACK_GMAC 0x2 */
+/* MC_CMD_LOOPBACK_GMAC 0x2 */
/* enum: XGMII. */
/* MC_CMD_LOOPBACK_XGMII 0x3 */
/* enum: XGXS. */
-/* MC_CMD_LOOPBACK_XGXS 0x4 */
+/* MC_CMD_LOOPBACK_XGXS 0x4 */
/* enum: XAUI. */
-/* MC_CMD_LOOPBACK_XAUI 0x5 */
+/* MC_CMD_LOOPBACK_XAUI 0x5 */
/* enum: GMII. */
-/* MC_CMD_LOOPBACK_GMII 0x6 */
+/* MC_CMD_LOOPBACK_GMII 0x6 */
/* enum: SGMII. */
-/* MC_CMD_LOOPBACK_SGMII 0x7 */
+/* MC_CMD_LOOPBACK_SGMII 0x7 */
/* enum: XGBR. */
-/* MC_CMD_LOOPBACK_XGBR 0x8 */
+/* MC_CMD_LOOPBACK_XGBR 0x8 */
/* enum: XFI. */
-/* MC_CMD_LOOPBACK_XFI 0x9 */
+/* MC_CMD_LOOPBACK_XFI 0x9 */
/* enum: XAUI Far. */
-/* MC_CMD_LOOPBACK_XAUI_FAR 0xa */
+/* MC_CMD_LOOPBACK_XAUI_FAR 0xa */
/* enum: GMII Far. */
-/* MC_CMD_LOOPBACK_GMII_FAR 0xb */
+/* MC_CMD_LOOPBACK_GMII_FAR 0xb */
/* enum: SGMII Far. */
-/* MC_CMD_LOOPBACK_SGMII_FAR 0xc */
+/* MC_CMD_LOOPBACK_SGMII_FAR 0xc */
/* enum: XFI Far. */
-/* MC_CMD_LOOPBACK_XFI_FAR 0xd */
+/* MC_CMD_LOOPBACK_XFI_FAR 0xd */
/* enum: GPhy. */
-/* MC_CMD_LOOPBACK_GPHY 0xe */
+/* MC_CMD_LOOPBACK_GPHY 0xe */
/* enum: PhyXS. */
-/* MC_CMD_LOOPBACK_PHYXS 0xf */
+/* MC_CMD_LOOPBACK_PHYXS 0xf */
/* enum: PCS. */
-/* MC_CMD_LOOPBACK_PCS 0x10 */
+/* MC_CMD_LOOPBACK_PCS 0x10 */
/* enum: PMA-PMD. */
-/* MC_CMD_LOOPBACK_PMAPMD 0x11 */
+/* MC_CMD_LOOPBACK_PMAPMD 0x11 */
/* enum: Cross-Port. */
-/* MC_CMD_LOOPBACK_XPORT 0x12 */
+/* MC_CMD_LOOPBACK_XPORT 0x12 */
/* enum: XGMII-Wireside. */
-/* MC_CMD_LOOPBACK_XGMII_WS 0x13 */
+/* MC_CMD_LOOPBACK_XGMII_WS 0x13 */
/* enum: XAUI Wireside. */
-/* MC_CMD_LOOPBACK_XAUI_WS 0x14 */
+/* MC_CMD_LOOPBACK_XAUI_WS 0x14 */
/* enum: XAUI Wireside Far. */
-/* MC_CMD_LOOPBACK_XAUI_WS_FAR 0x15 */
+/* MC_CMD_LOOPBACK_XAUI_WS_FAR 0x15 */
/* enum: XAUI Wireside near. */
-/* MC_CMD_LOOPBACK_XAUI_WS_NEAR 0x16 */
+/* MC_CMD_LOOPBACK_XAUI_WS_NEAR 0x16 */
/* enum: GMII Wireside. */
-/* MC_CMD_LOOPBACK_GMII_WS 0x17 */
+/* MC_CMD_LOOPBACK_GMII_WS 0x17 */
/* enum: XFI Wireside. */
-/* MC_CMD_LOOPBACK_XFI_WS 0x18 */
+/* MC_CMD_LOOPBACK_XFI_WS 0x18 */
/* enum: XFI Wireside Far. */
-/* MC_CMD_LOOPBACK_XFI_WS_FAR 0x19 */
+/* MC_CMD_LOOPBACK_XFI_WS_FAR 0x19 */
/* enum: PhyXS Wireside. */
-/* MC_CMD_LOOPBACK_PHYXS_WS 0x1a */
+/* MC_CMD_LOOPBACK_PHYXS_WS 0x1a */
/* enum: PMA lanes MAC-Serdes. */
-/* MC_CMD_LOOPBACK_PMA_INT 0x1b */
+/* MC_CMD_LOOPBACK_PMA_INT 0x1b */
/* enum: KR Serdes Parallel (Encoder). */
-/* MC_CMD_LOOPBACK_SD_NEAR 0x1c */
+/* MC_CMD_LOOPBACK_SD_NEAR 0x1c */
/* enum: KR Serdes Serial. */
-/* MC_CMD_LOOPBACK_SD_FAR 0x1d */
+/* MC_CMD_LOOPBACK_SD_FAR 0x1d */
/* enum: PMA lanes MAC-Serdes Wireside. */
-/* MC_CMD_LOOPBACK_PMA_INT_WS 0x1e */
+/* MC_CMD_LOOPBACK_PMA_INT_WS 0x1e */
/* enum: KR Serdes Parallel Wireside (Full PCS). */
-/* MC_CMD_LOOPBACK_SD_FEP2_WS 0x1f */
+/* MC_CMD_LOOPBACK_SD_FEP2_WS 0x1f */
/* enum: KR Serdes Parallel Wireside (Sym Aligner to TX). */
-/* MC_CMD_LOOPBACK_SD_FEP1_5_WS 0x20 */
+/* MC_CMD_LOOPBACK_SD_FEP1_5_WS 0x20 */
/* enum: KR Serdes Parallel Wireside (Deserializer to Serializer). */
-/* MC_CMD_LOOPBACK_SD_FEP_WS 0x21 */
+/* MC_CMD_LOOPBACK_SD_FEP_WS 0x21 */
/* enum: KR Serdes Serial Wireside. */
-/* MC_CMD_LOOPBACK_SD_FES_WS 0x22 */
+/* MC_CMD_LOOPBACK_SD_FES_WS 0x22 */
/* enum: Near side of AOE Siena side port */
-/* MC_CMD_LOOPBACK_AOE_INT_NEAR 0x23 */
+/* MC_CMD_LOOPBACK_AOE_INT_NEAR 0x23 */
/* enum: Medford Wireside datapath loopback */
-/* MC_CMD_LOOPBACK_DATA_WS 0x24 */
+/* MC_CMD_LOOPBACK_DATA_WS 0x24 */
/* enum: Force link up without setting up any physical loopback (snapper use
* only)
*/
-/* MC_CMD_LOOPBACK_FORCE_EXT_LINK 0x25 */
+/* MC_CMD_LOOPBACK_FORCE_EXT_LINK 0x25 */
/* Supported loopbacks. */
#define MC_CMD_GET_LOOPBACK_MODES_OUT_V2_1G_OFST 8
#define MC_CMD_GET_LOOPBACK_MODES_OUT_V2_1G_LEN 8
@@ -3537,6 +3570,37 @@
/* Enum values, see field(s): */
/* 100M */
+/* AN_TYPE structuredef: Auto-negotiation types defined in IEEE802.3 */
+#define AN_TYPE_LEN 4
+#define AN_TYPE_TYPE_OFST 0
+#define AN_TYPE_TYPE_LEN 4
+/* enum: None, AN disabled or not supported */
+#define MC_CMD_AN_NONE 0x0
+/* enum: Clause 28 - BASE-T */
+#define MC_CMD_AN_CLAUSE28 0x1
+/* enum: Clause 37 - BASE-X */
+#define MC_CMD_AN_CLAUSE37 0x2
+/* enum: Clause 73 - BASE-R startup protocol for backplane and copper cable
+ * assemblies. Includes Clause 72/Clause 92 link-training.
+ */
+#define MC_CMD_AN_CLAUSE73 0x3
+#define AN_TYPE_TYPE_LBN 0
+#define AN_TYPE_TYPE_WIDTH 32
+
+/* FEC_TYPE structuredef: Forward error correction types defined in IEEE802.3
+ */
+#define FEC_TYPE_LEN 4
+#define FEC_TYPE_TYPE_OFST 0
+#define FEC_TYPE_TYPE_LEN 4
+/* enum: No FEC */
+#define MC_CMD_FEC_NONE 0x0
+/* enum: Clause 74 BASE-R FEC (a.k.a Firecode) */
+#define MC_CMD_FEC_BASER 0x1
+/* enum: Clause 91/Clause 108 Reed-Solomon FEC */
+#define MC_CMD_FEC_RS 0x2
+#define FEC_TYPE_TYPE_LBN 0
+#define FEC_TYPE_TYPE_WIDTH 32
+
/***********************************/
/* MC_CMD_GET_LINK
@@ -3552,10 +3616,14 @@
/* MC_CMD_GET_LINK_OUT msgresponse */
#define MC_CMD_GET_LINK_OUT_LEN 28
-/* near-side advertised capabilities */
+/* Near-side advertised capabilities. Refer to
+ * MC_CMD_GET_PHY_CFG_OUT/SUPPORTED_CAP for bit definitions.
+ */
#define MC_CMD_GET_LINK_OUT_CAP_OFST 0
#define MC_CMD_GET_LINK_OUT_CAP_LEN 4
-/* link-partner advertised capabilities */
+/* Link-partner advertised capabilities. Refer to
+ * MC_CMD_GET_PHY_CFG_OUT/SUPPORTED_CAP for bit definitions.
+ */
#define MC_CMD_GET_LINK_OUT_LP_CAP_OFST 4
#define MC_CMD_GET_LINK_OUT_LP_CAP_LEN 4
/* Autonegotiated speed in mbit/s. The link may still be down even if this
@@ -3598,6 +3666,97 @@
#define MC_CMD_MAC_FAULT_PENDING_RECONFIG_LBN 3
#define MC_CMD_MAC_FAULT_PENDING_RECONFIG_WIDTH 1
+/* MC_CMD_GET_LINK_OUT_V2 msgresponse: Extended link state information */
+#define MC_CMD_GET_LINK_OUT_V2_LEN 44
+/* Near-side advertised capabilities. Refer to
+ * MC_CMD_GET_PHY_CFG_OUT/SUPPORTED_CAP for bit definitions.
+ */
+#define MC_CMD_GET_LINK_OUT_V2_CAP_OFST 0
+#define MC_CMD_GET_LINK_OUT_V2_CAP_LEN 4
+/* Link-partner advertised capabilities. Refer to
+ * MC_CMD_GET_PHY_CFG_OUT/SUPPORTED_CAP for bit definitions.
+ */
+#define MC_CMD_GET_LINK_OUT_V2_LP_CAP_OFST 4
+#define MC_CMD_GET_LINK_OUT_V2_LP_CAP_LEN 4
+/* Autonegotiated speed in mbit/s. The link may still be down even if this
+ * reads non-zero.
+ */
+#define MC_CMD_GET_LINK_OUT_V2_LINK_SPEED_OFST 8
+#define MC_CMD_GET_LINK_OUT_V2_LINK_SPEED_LEN 4
+/* Current loopback setting. */
+#define MC_CMD_GET_LINK_OUT_V2_LOOPBACK_MODE_OFST 12
+#define MC_CMD_GET_LINK_OUT_V2_LOOPBACK_MODE_LEN 4
+/* Enum values, see field(s): */
+/* MC_CMD_GET_LOOPBACK_MODES/MC_CMD_GET_LOOPBACK_MODES_OUT/100M */
+#define MC_CMD_GET_LINK_OUT_V2_FLAGS_OFST 16
+#define MC_CMD_GET_LINK_OUT_V2_FLAGS_LEN 4
+#define MC_CMD_GET_LINK_OUT_V2_LINK_UP_LBN 0
+#define MC_CMD_GET_LINK_OUT_V2_LINK_UP_WIDTH 1
+#define MC_CMD_GET_LINK_OUT_V2_FULL_DUPLEX_LBN 1
+#define MC_CMD_GET_LINK_OUT_V2_FULL_DUPLEX_WIDTH 1
+#define MC_CMD_GET_LINK_OUT_V2_BPX_LINK_LBN 2
+#define MC_CMD_GET_LINK_OUT_V2_BPX_LINK_WIDTH 1
+#define MC_CMD_GET_LINK_OUT_V2_PHY_LINK_LBN 3
+#define MC_CMD_GET_LINK_OUT_V2_PHY_LINK_WIDTH 1
+#define MC_CMD_GET_LINK_OUT_V2_LINK_FAULT_RX_LBN 6
+#define MC_CMD_GET_LINK_OUT_V2_LINK_FAULT_RX_WIDTH 1
+#define MC_CMD_GET_LINK_OUT_V2_LINK_FAULT_TX_LBN 7
+#define MC_CMD_GET_LINK_OUT_V2_LINK_FAULT_TX_WIDTH 1
+/* This returns the negotiated flow control value. */
+#define MC_CMD_GET_LINK_OUT_V2_FCNTL_OFST 20
+#define MC_CMD_GET_LINK_OUT_V2_FCNTL_LEN 4
+/* Enum values, see field(s): */
+/* MC_CMD_SET_MAC/MC_CMD_SET_MAC_IN/FCNTL */
+#define MC_CMD_GET_LINK_OUT_V2_MAC_FAULT_OFST 24
+#define MC_CMD_GET_LINK_OUT_V2_MAC_FAULT_LEN 4
+/* MC_CMD_MAC_FAULT_XGMII_LOCAL_LBN 0 */
+/* MC_CMD_MAC_FAULT_XGMII_LOCAL_WIDTH 1 */
+/* MC_CMD_MAC_FAULT_XGMII_REMOTE_LBN 1 */
+/* MC_CMD_MAC_FAULT_XGMII_REMOTE_WIDTH 1 */
+/* MC_CMD_MAC_FAULT_SGMII_REMOTE_LBN 2 */
+/* MC_CMD_MAC_FAULT_SGMII_REMOTE_WIDTH 1 */
+/* MC_CMD_MAC_FAULT_PENDING_RECONFIG_LBN 3 */
+/* MC_CMD_MAC_FAULT_PENDING_RECONFIG_WIDTH 1 */
+/* True local device capabilities (taking into account currently used PMD/MDI,
+ * e.g. plugged-in module). In general, subset of
+ * MC_CMD_GET_PHY_CFG_OUT/SUPPORTED_CAP, but may include extra _FEC_REQUEST
+ * bits, if the PMD requires FEC. 0 if unknown (e.g. module unplugged). Equal
+ * to SUPPORTED_CAP for non-pluggable PMDs. Refer to
+ * MC_CMD_GET_PHY_CFG_OUT/SUPPORTED_CAP for bit definitions.
+ */
+#define MC_CMD_GET_LINK_OUT_V2_LD_CAP_OFST 28
+#define MC_CMD_GET_LINK_OUT_V2_LD_CAP_LEN 4
+/* Auto-negotiation type used on the link */
+#define MC_CMD_GET_LINK_OUT_V2_AN_TYPE_OFST 32
+#define MC_CMD_GET_LINK_OUT_V2_AN_TYPE_LEN 4
+/* Enum values, see field(s): */
+/* AN_TYPE/TYPE */
+/* Forward error correction used on the link */
+#define MC_CMD_GET_LINK_OUT_V2_FEC_TYPE_OFST 36
+#define MC_CMD_GET_LINK_OUT_V2_FEC_TYPE_LEN 4
+/* Enum values, see field(s): */
+/* FEC_TYPE/TYPE */
+#define MC_CMD_GET_LINK_OUT_V2_EXT_FLAGS_OFST 40
+#define MC_CMD_GET_LINK_OUT_V2_EXT_FLAGS_LEN 4
+#define MC_CMD_GET_LINK_OUT_V2_PMD_MDI_CONNECTED_LBN 0
+#define MC_CMD_GET_LINK_OUT_V2_PMD_MDI_CONNECTED_WIDTH 1
+#define MC_CMD_GET_LINK_OUT_V2_PMD_READY_LBN 1
+#define MC_CMD_GET_LINK_OUT_V2_PMD_READY_WIDTH 1
+#define MC_CMD_GET_LINK_OUT_V2_PMD_LINK_UP_LBN 2
+#define MC_CMD_GET_LINK_OUT_V2_PMD_LINK_UP_WIDTH 1
+#define MC_CMD_GET_LINK_OUT_V2_PMA_LINK_UP_LBN 3
+#define MC_CMD_GET_LINK_OUT_V2_PMA_LINK_UP_WIDTH 1
+#define MC_CMD_GET_LINK_OUT_V2_PCS_LOCK_LBN 4
+#define MC_CMD_GET_LINK_OUT_V2_PCS_LOCK_WIDTH 1
+#define MC_CMD_GET_LINK_OUT_V2_ALIGN_LOCK_LBN 5
+#define MC_CMD_GET_LINK_OUT_V2_ALIGN_LOCK_WIDTH 1
+#define MC_CMD_GET_LINK_OUT_V2_HI_BER_LBN 6
+#define MC_CMD_GET_LINK_OUT_V2_HI_BER_WIDTH 1
+#define MC_CMD_GET_LINK_OUT_V2_FEC_LOCK_LBN 7
+#define MC_CMD_GET_LINK_OUT_V2_FEC_LOCK_WIDTH 1
+#define MC_CMD_GET_LINK_OUT_V2_AN_DONE_LBN 8
+#define MC_CMD_GET_LINK_OUT_V2_AN_DONE_WIDTH 1
+
/***********************************/
/* MC_CMD_SET_LINK
@@ -3610,7 +3769,9 @@
/* MC_CMD_SET_LINK_IN msgrequest */
#define MC_CMD_SET_LINK_IN_LEN 16
-/* ??? */
+/* Near-side advertised capabilities. Refer to
+ * MC_CMD_GET_PHY_CFG_OUT/SUPPORTED_CAP for bit definitions.
+ */
#define MC_CMD_SET_LINK_IN_CAP_OFST 0
#define MC_CMD_SET_LINK_IN_CAP_LEN 4
/* Flags */
@@ -3650,9 +3811,9 @@
/* Set LED state. */
#define MC_CMD_SET_ID_LED_IN_STATE_OFST 0
#define MC_CMD_SET_ID_LED_IN_STATE_LEN 4
-#define MC_CMD_LED_OFF 0x0 /* enum */
-#define MC_CMD_LED_ON 0x1 /* enum */
-#define MC_CMD_LED_DEFAULT 0x2 /* enum */
+#define MC_CMD_LED_OFF 0x0 /* enum */
+#define MC_CMD_LED_ON 0x1 /* enum */
+#define MC_CMD_LED_DEFAULT 0x2 /* enum */
/* MC_CMD_SET_ID_LED_OUT msgresponse */
#define MC_CMD_SET_ID_LED_OUT_LEN 0
@@ -3802,53 +3963,53 @@
#define MC_CMD_PHY_STATS_OUT_NO_DMA_STATISTICS_LEN 4
#define MC_CMD_PHY_STATS_OUT_NO_DMA_STATISTICS_NUM MC_CMD_PHY_NSTATS
/* enum: OUI. */
-#define MC_CMD_OUI 0x0
+#define MC_CMD_OUI 0x0
/* enum: PMA-PMD Link Up. */
-#define MC_CMD_PMA_PMD_LINK_UP 0x1
+#define MC_CMD_PMA_PMD_LINK_UP 0x1
/* enum: PMA-PMD RX Fault. */
-#define MC_CMD_PMA_PMD_RX_FAULT 0x2
+#define MC_CMD_PMA_PMD_RX_FAULT 0x2
/* enum: PMA-PMD TX Fault. */
-#define MC_CMD_PMA_PMD_TX_FAULT 0x3
+#define MC_CMD_PMA_PMD_TX_FAULT 0x3
/* enum: PMA-PMD Signal */
-#define MC_CMD_PMA_PMD_SIGNAL 0x4
+#define MC_CMD_PMA_PMD_SIGNAL 0x4
/* enum: PMA-PMD SNR A. */
-#define MC_CMD_PMA_PMD_SNR_A 0x5
+#define MC_CMD_PMA_PMD_SNR_A 0x5
/* enum: PMA-PMD SNR B. */
-#define MC_CMD_PMA_PMD_SNR_B 0x6
+#define MC_CMD_PMA_PMD_SNR_B 0x6
/* enum: PMA-PMD SNR C. */
-#define MC_CMD_PMA_PMD_SNR_C 0x7
+#define MC_CMD_PMA_PMD_SNR_C 0x7
/* enum: PMA-PMD SNR D. */
-#define MC_CMD_PMA_PMD_SNR_D 0x8
+#define MC_CMD_PMA_PMD_SNR_D 0x8
/* enum: PCS Link Up. */
-#define MC_CMD_PCS_LINK_UP 0x9
+#define MC_CMD_PCS_LINK_UP 0x9
/* enum: PCS RX Fault. */
-#define MC_CMD_PCS_RX_FAULT 0xa
+#define MC_CMD_PCS_RX_FAULT 0xa
/* enum: PCS TX Fault. */
-#define MC_CMD_PCS_TX_FAULT 0xb
+#define MC_CMD_PCS_TX_FAULT 0xb
/* enum: PCS BER. */
-#define MC_CMD_PCS_BER 0xc
+#define MC_CMD_PCS_BER 0xc
/* enum: PCS Block Errors. */
-#define MC_CMD_PCS_BLOCK_ERRORS 0xd
+#define MC_CMD_PCS_BLOCK_ERRORS 0xd
/* enum: PhyXS Link Up. */
-#define MC_CMD_PHYXS_LINK_UP 0xe
+#define MC_CMD_PHYXS_LINK_UP 0xe
/* enum: PhyXS RX Fault. */
-#define MC_CMD_PHYXS_RX_FAULT 0xf
+#define MC_CMD_PHYXS_RX_FAULT 0xf
/* enum: PhyXS TX Fault. */
-#define MC_CMD_PHYXS_TX_FAULT 0x10
+#define MC_CMD_PHYXS_TX_FAULT 0x10
/* enum: PhyXS Align. */
-#define MC_CMD_PHYXS_ALIGN 0x11
+#define MC_CMD_PHYXS_ALIGN 0x11
/* enum: PhyXS Sync. */
-#define MC_CMD_PHYXS_SYNC 0x12
+#define MC_CMD_PHYXS_SYNC 0x12
/* enum: AN link-up. */
-#define MC_CMD_AN_LINK_UP 0x13
+#define MC_CMD_AN_LINK_UP 0x13
/* enum: AN Complete. */
-#define MC_CMD_AN_COMPLETE 0x14
+#define MC_CMD_AN_COMPLETE 0x14
/* enum: AN 10GBaseT Status. */
-#define MC_CMD_AN_10GBT_STATUS 0x15
+#define MC_CMD_AN_10GBT_STATUS 0x15
/* enum: Clause 22 Link-Up. */
-#define MC_CMD_CL22_LINK_UP 0x16
+#define MC_CMD_CL22_LINK_UP 0x16
/* enum: (Last entry) */
-#define MC_CMD_PHY_NSTATS 0x17
+#define MC_CMD_PHY_NSTATS 0x17
/***********************************/
@@ -3910,139 +4071,139 @@
#define MC_CMD_MAC_STATS_OUT_NO_DMA_STATISTICS_LO_OFST 0
#define MC_CMD_MAC_STATS_OUT_NO_DMA_STATISTICS_HI_OFST 4
#define MC_CMD_MAC_STATS_OUT_NO_DMA_STATISTICS_NUM MC_CMD_MAC_NSTATS
-#define MC_CMD_MAC_GENERATION_START 0x0 /* enum */
-#define MC_CMD_MAC_DMABUF_START 0x1 /* enum */
-#define MC_CMD_MAC_TX_PKTS 0x1 /* enum */
-#define MC_CMD_MAC_TX_PAUSE_PKTS 0x2 /* enum */
-#define MC_CMD_MAC_TX_CONTROL_PKTS 0x3 /* enum */
-#define MC_CMD_MAC_TX_UNICAST_PKTS 0x4 /* enum */
-#define MC_CMD_MAC_TX_MULTICAST_PKTS 0x5 /* enum */
-#define MC_CMD_MAC_TX_BROADCAST_PKTS 0x6 /* enum */
-#define MC_CMD_MAC_TX_BYTES 0x7 /* enum */
-#define MC_CMD_MAC_TX_BAD_BYTES 0x8 /* enum */
-#define MC_CMD_MAC_TX_LT64_PKTS 0x9 /* enum */
-#define MC_CMD_MAC_TX_64_PKTS 0xa /* enum */
-#define MC_CMD_MAC_TX_65_TO_127_PKTS 0xb /* enum */
-#define MC_CMD_MAC_TX_128_TO_255_PKTS 0xc /* enum */
-#define MC_CMD_MAC_TX_256_TO_511_PKTS 0xd /* enum */
-#define MC_CMD_MAC_TX_512_TO_1023_PKTS 0xe /* enum */
-#define MC_CMD_MAC_TX_1024_TO_15XX_PKTS 0xf /* enum */
-#define MC_CMD_MAC_TX_15XX_TO_JUMBO_PKTS 0x10 /* enum */
-#define MC_CMD_MAC_TX_GTJUMBO_PKTS 0x11 /* enum */
-#define MC_CMD_MAC_TX_BAD_FCS_PKTS 0x12 /* enum */
-#define MC_CMD_MAC_TX_SINGLE_COLLISION_PKTS 0x13 /* enum */
-#define MC_CMD_MAC_TX_MULTIPLE_COLLISION_PKTS 0x14 /* enum */
-#define MC_CMD_MAC_TX_EXCESSIVE_COLLISION_PKTS 0x15 /* enum */
-#define MC_CMD_MAC_TX_LATE_COLLISION_PKTS 0x16 /* enum */
-#define MC_CMD_MAC_TX_DEFERRED_PKTS 0x17 /* enum */
-#define MC_CMD_MAC_TX_EXCESSIVE_DEFERRED_PKTS 0x18 /* enum */
-#define MC_CMD_MAC_TX_NON_TCPUDP_PKTS 0x19 /* enum */
-#define MC_CMD_MAC_TX_MAC_SRC_ERR_PKTS 0x1a /* enum */
-#define MC_CMD_MAC_TX_IP_SRC_ERR_PKTS 0x1b /* enum */
-#define MC_CMD_MAC_RX_PKTS 0x1c /* enum */
-#define MC_CMD_MAC_RX_PAUSE_PKTS 0x1d /* enum */
-#define MC_CMD_MAC_RX_GOOD_PKTS 0x1e /* enum */
-#define MC_CMD_MAC_RX_CONTROL_PKTS 0x1f /* enum */
-#define MC_CMD_MAC_RX_UNICAST_PKTS 0x20 /* enum */
-#define MC_CMD_MAC_RX_MULTICAST_PKTS 0x21 /* enum */
-#define MC_CMD_MAC_RX_BROADCAST_PKTS 0x22 /* enum */
-#define MC_CMD_MAC_RX_BYTES 0x23 /* enum */
-#define MC_CMD_MAC_RX_BAD_BYTES 0x24 /* enum */
-#define MC_CMD_MAC_RX_64_PKTS 0x25 /* enum */
-#define MC_CMD_MAC_RX_65_TO_127_PKTS 0x26 /* enum */
-#define MC_CMD_MAC_RX_128_TO_255_PKTS 0x27 /* enum */
-#define MC_CMD_MAC_RX_256_TO_511_PKTS 0x28 /* enum */
-#define MC_CMD_MAC_RX_512_TO_1023_PKTS 0x29 /* enum */
-#define MC_CMD_MAC_RX_1024_TO_15XX_PKTS 0x2a /* enum */
-#define MC_CMD_MAC_RX_15XX_TO_JUMBO_PKTS 0x2b /* enum */
-#define MC_CMD_MAC_RX_GTJUMBO_PKTS 0x2c /* enum */
-#define MC_CMD_MAC_RX_UNDERSIZE_PKTS 0x2d /* enum */
-#define MC_CMD_MAC_RX_BAD_FCS_PKTS 0x2e /* enum */
-#define MC_CMD_MAC_RX_OVERFLOW_PKTS 0x2f /* enum */
-#define MC_CMD_MAC_RX_FALSE_CARRIER_PKTS 0x30 /* enum */
-#define MC_CMD_MAC_RX_SYMBOL_ERROR_PKTS 0x31 /* enum */
-#define MC_CMD_MAC_RX_ALIGN_ERROR_PKTS 0x32 /* enum */
-#define MC_CMD_MAC_RX_LENGTH_ERROR_PKTS 0x33 /* enum */
-#define MC_CMD_MAC_RX_INTERNAL_ERROR_PKTS 0x34 /* enum */
-#define MC_CMD_MAC_RX_JABBER_PKTS 0x35 /* enum */
-#define MC_CMD_MAC_RX_NODESC_DROPS 0x36 /* enum */
-#define MC_CMD_MAC_RX_LANES01_CHAR_ERR 0x37 /* enum */
-#define MC_CMD_MAC_RX_LANES23_CHAR_ERR 0x38 /* enum */
-#define MC_CMD_MAC_RX_LANES01_DISP_ERR 0x39 /* enum */
-#define MC_CMD_MAC_RX_LANES23_DISP_ERR 0x3a /* enum */
-#define MC_CMD_MAC_RX_MATCH_FAULT 0x3b /* enum */
+#define MC_CMD_MAC_GENERATION_START 0x0 /* enum */
+#define MC_CMD_MAC_DMABUF_START 0x1 /* enum */
+#define MC_CMD_MAC_TX_PKTS 0x1 /* enum */
+#define MC_CMD_MAC_TX_PAUSE_PKTS 0x2 /* enum */
+#define MC_CMD_MAC_TX_CONTROL_PKTS 0x3 /* enum */
+#define MC_CMD_MAC_TX_UNICAST_PKTS 0x4 /* enum */
+#define MC_CMD_MAC_TX_MULTICAST_PKTS 0x5 /* enum */
+#define MC_CMD_MAC_TX_BROADCAST_PKTS 0x6 /* enum */
+#define MC_CMD_MAC_TX_BYTES 0x7 /* enum */
+#define MC_CMD_MAC_TX_BAD_BYTES 0x8 /* enum */
+#define MC_CMD_MAC_TX_LT64_PKTS 0x9 /* enum */
+#define MC_CMD_MAC_TX_64_PKTS 0xa /* enum */
+#define MC_CMD_MAC_TX_65_TO_127_PKTS 0xb /* enum */
+#define MC_CMD_MAC_TX_128_TO_255_PKTS 0xc /* enum */
+#define MC_CMD_MAC_TX_256_TO_511_PKTS 0xd /* enum */
+#define MC_CMD_MAC_TX_512_TO_1023_PKTS 0xe /* enum */
+#define MC_CMD_MAC_TX_1024_TO_15XX_PKTS 0xf /* enum */
+#define MC_CMD_MAC_TX_15XX_TO_JUMBO_PKTS 0x10 /* enum */
+#define MC_CMD_MAC_TX_GTJUMBO_PKTS 0x11 /* enum */
+#define MC_CMD_MAC_TX_BAD_FCS_PKTS 0x12 /* enum */
+#define MC_CMD_MAC_TX_SINGLE_COLLISION_PKTS 0x13 /* enum */
+#define MC_CMD_MAC_TX_MULTIPLE_COLLISION_PKTS 0x14 /* enum */
+#define MC_CMD_MAC_TX_EXCESSIVE_COLLISION_PKTS 0x15 /* enum */
+#define MC_CMD_MAC_TX_LATE_COLLISION_PKTS 0x16 /* enum */
+#define MC_CMD_MAC_TX_DEFERRED_PKTS 0x17 /* enum */
+#define MC_CMD_MAC_TX_EXCESSIVE_DEFERRED_PKTS 0x18 /* enum */
+#define MC_CMD_MAC_TX_NON_TCPUDP_PKTS 0x19 /* enum */
+#define MC_CMD_MAC_TX_MAC_SRC_ERR_PKTS 0x1a /* enum */
+#define MC_CMD_MAC_TX_IP_SRC_ERR_PKTS 0x1b /* enum */
+#define MC_CMD_MAC_RX_PKTS 0x1c /* enum */
+#define MC_CMD_MAC_RX_PAUSE_PKTS 0x1d /* enum */
+#define MC_CMD_MAC_RX_GOOD_PKTS 0x1e /* enum */
+#define MC_CMD_MAC_RX_CONTROL_PKTS 0x1f /* enum */
+#define MC_CMD_MAC_RX_UNICAST_PKTS 0x20 /* enum */
+#define MC_CMD_MAC_RX_MULTICAST_PKTS 0x21 /* enum */
+#define MC_CMD_MAC_RX_BROADCAST_PKTS 0x22 /* enum */
+#define MC_CMD_MAC_RX_BYTES 0x23 /* enum */
+#define MC_CMD_MAC_RX_BAD_BYTES 0x24 /* enum */
+#define MC_CMD_MAC_RX_64_PKTS 0x25 /* enum */
+#define MC_CMD_MAC_RX_65_TO_127_PKTS 0x26 /* enum */
+#define MC_CMD_MAC_RX_128_TO_255_PKTS 0x27 /* enum */
+#define MC_CMD_MAC_RX_256_TO_511_PKTS 0x28 /* enum */
+#define MC_CMD_MAC_RX_512_TO_1023_PKTS 0x29 /* enum */
+#define MC_CMD_MAC_RX_1024_TO_15XX_PKTS 0x2a /* enum */
+#define MC_CMD_MAC_RX_15XX_TO_JUMBO_PKTS 0x2b /* enum */
+#define MC_CMD_MAC_RX_GTJUMBO_PKTS 0x2c /* enum */
+#define MC_CMD_MAC_RX_UNDERSIZE_PKTS 0x2d /* enum */
+#define MC_CMD_MAC_RX_BAD_FCS_PKTS 0x2e /* enum */
+#define MC_CMD_MAC_RX_OVERFLOW_PKTS 0x2f /* enum */
+#define MC_CMD_MAC_RX_FALSE_CARRIER_PKTS 0x30 /* enum */
+#define MC_CMD_MAC_RX_SYMBOL_ERROR_PKTS 0x31 /* enum */
+#define MC_CMD_MAC_RX_ALIGN_ERROR_PKTS 0x32 /* enum */
+#define MC_CMD_MAC_RX_LENGTH_ERROR_PKTS 0x33 /* enum */
+#define MC_CMD_MAC_RX_INTERNAL_ERROR_PKTS 0x34 /* enum */
+#define MC_CMD_MAC_RX_JABBER_PKTS 0x35 /* enum */
+#define MC_CMD_MAC_RX_NODESC_DROPS 0x36 /* enum */
+#define MC_CMD_MAC_RX_LANES01_CHAR_ERR 0x37 /* enum */
+#define MC_CMD_MAC_RX_LANES23_CHAR_ERR 0x38 /* enum */
+#define MC_CMD_MAC_RX_LANES01_DISP_ERR 0x39 /* enum */
+#define MC_CMD_MAC_RX_LANES23_DISP_ERR 0x3a /* enum */
+#define MC_CMD_MAC_RX_MATCH_FAULT 0x3b /* enum */
/* enum: PM trunc_bb_overflow counter. Valid for EF10 with PM_AND_RXDP_COUNTERS
* capability only.
*/
-#define MC_CMD_MAC_PM_TRUNC_BB_OVERFLOW 0x3c
+#define MC_CMD_MAC_PM_TRUNC_BB_OVERFLOW 0x3c
/* enum: PM discard_bb_overflow counter. Valid for EF10 with
* PM_AND_RXDP_COUNTERS capability only.
*/
-#define MC_CMD_MAC_PM_DISCARD_BB_OVERFLOW 0x3d
+#define MC_CMD_MAC_PM_DISCARD_BB_OVERFLOW 0x3d
/* enum: PM trunc_vfifo_full counter. Valid for EF10 with PM_AND_RXDP_COUNTERS
* capability only.
*/
-#define MC_CMD_MAC_PM_TRUNC_VFIFO_FULL 0x3e
+#define MC_CMD_MAC_PM_TRUNC_VFIFO_FULL 0x3e
/* enum: PM discard_vfifo_full counter. Valid for EF10 with
* PM_AND_RXDP_COUNTERS capability only.
*/
-#define MC_CMD_MAC_PM_DISCARD_VFIFO_FULL 0x3f
+#define MC_CMD_MAC_PM_DISCARD_VFIFO_FULL 0x3f
/* enum: PM trunc_qbb counter. Valid for EF10 with PM_AND_RXDP_COUNTERS
* capability only.
*/
-#define MC_CMD_MAC_PM_TRUNC_QBB 0x40
+#define MC_CMD_MAC_PM_TRUNC_QBB 0x40
/* enum: PM discard_qbb counter. Valid for EF10 with PM_AND_RXDP_COUNTERS
* capability only.
*/
-#define MC_CMD_MAC_PM_DISCARD_QBB 0x41
+#define MC_CMD_MAC_PM_DISCARD_QBB 0x41
/* enum: PM discard_mapping counter. Valid for EF10 with PM_AND_RXDP_COUNTERS
* capability only.
*/
-#define MC_CMD_MAC_PM_DISCARD_MAPPING 0x42
+#define MC_CMD_MAC_PM_DISCARD_MAPPING 0x42
/* enum: RXDP counter: Number of packets dropped due to the queue being
* disabled. Valid for EF10 with PM_AND_RXDP_COUNTERS capability only.
*/
-#define MC_CMD_MAC_RXDP_Q_DISABLED_PKTS 0x43
+#define MC_CMD_MAC_RXDP_Q_DISABLED_PKTS 0x43
/* enum: RXDP counter: Number of packets dropped by the DICPU. Valid for EF10
* with PM_AND_RXDP_COUNTERS capability only.
*/
-#define MC_CMD_MAC_RXDP_DI_DROPPED_PKTS 0x45
+#define MC_CMD_MAC_RXDP_DI_DROPPED_PKTS 0x45
/* enum: RXDP counter: Number of non-host packets. Valid for EF10 with
* PM_AND_RXDP_COUNTERS capability only.
*/
-#define MC_CMD_MAC_RXDP_STREAMING_PKTS 0x46
+#define MC_CMD_MAC_RXDP_STREAMING_PKTS 0x46
/* enum: RXDP counter: Number of times an hlb descriptor fetch was performed.
* Valid for EF10 with PM_AND_RXDP_COUNTERS capability only.
*/
-#define MC_CMD_MAC_RXDP_HLB_FETCH_CONDITIONS 0x47
+#define MC_CMD_MAC_RXDP_HLB_FETCH_CONDITIONS 0x47
/* enum: RXDP counter: Number of times the DPCPU waited for an existing
* descriptor fetch. Valid for EF10 with PM_AND_RXDP_COUNTERS capability only.
*/
-#define MC_CMD_MAC_RXDP_HLB_WAIT_CONDITIONS 0x48
-#define MC_CMD_MAC_VADAPTER_RX_DMABUF_START 0x4c /* enum */
-#define MC_CMD_MAC_VADAPTER_RX_UNICAST_PACKETS 0x4c /* enum */
-#define MC_CMD_MAC_VADAPTER_RX_UNICAST_BYTES 0x4d /* enum */
-#define MC_CMD_MAC_VADAPTER_RX_MULTICAST_PACKETS 0x4e /* enum */
-#define MC_CMD_MAC_VADAPTER_RX_MULTICAST_BYTES 0x4f /* enum */
-#define MC_CMD_MAC_VADAPTER_RX_BROADCAST_PACKETS 0x50 /* enum */
-#define MC_CMD_MAC_VADAPTER_RX_BROADCAST_BYTES 0x51 /* enum */
-#define MC_CMD_MAC_VADAPTER_RX_BAD_PACKETS 0x52 /* enum */
-#define MC_CMD_MAC_VADAPTER_RX_BAD_BYTES 0x53 /* enum */
-#define MC_CMD_MAC_VADAPTER_RX_OVERFLOW 0x54 /* enum */
-#define MC_CMD_MAC_VADAPTER_TX_DMABUF_START 0x57 /* enum */
-#define MC_CMD_MAC_VADAPTER_TX_UNICAST_PACKETS 0x57 /* enum */
-#define MC_CMD_MAC_VADAPTER_TX_UNICAST_BYTES 0x58 /* enum */
-#define MC_CMD_MAC_VADAPTER_TX_MULTICAST_PACKETS 0x59 /* enum */
-#define MC_CMD_MAC_VADAPTER_TX_MULTICAST_BYTES 0x5a /* enum */
-#define MC_CMD_MAC_VADAPTER_TX_BROADCAST_PACKETS 0x5b /* enum */
-#define MC_CMD_MAC_VADAPTER_TX_BROADCAST_BYTES 0x5c /* enum */
-#define MC_CMD_MAC_VADAPTER_TX_BAD_PACKETS 0x5d /* enum */
-#define MC_CMD_MAC_VADAPTER_TX_BAD_BYTES 0x5e /* enum */
-#define MC_CMD_MAC_VADAPTER_TX_OVERFLOW 0x5f /* enum */
+#define MC_CMD_MAC_RXDP_HLB_WAIT_CONDITIONS 0x48
+#define MC_CMD_MAC_VADAPTER_RX_DMABUF_START 0x4c /* enum */
+#define MC_CMD_MAC_VADAPTER_RX_UNICAST_PACKETS 0x4c /* enum */
+#define MC_CMD_MAC_VADAPTER_RX_UNICAST_BYTES 0x4d /* enum */
+#define MC_CMD_MAC_VADAPTER_RX_MULTICAST_PACKETS 0x4e /* enum */
+#define MC_CMD_MAC_VADAPTER_RX_MULTICAST_BYTES 0x4f /* enum */
+#define MC_CMD_MAC_VADAPTER_RX_BROADCAST_PACKETS 0x50 /* enum */
+#define MC_CMD_MAC_VADAPTER_RX_BROADCAST_BYTES 0x51 /* enum */
+#define MC_CMD_MAC_VADAPTER_RX_BAD_PACKETS 0x52 /* enum */
+#define MC_CMD_MAC_VADAPTER_RX_BAD_BYTES 0x53 /* enum */
+#define MC_CMD_MAC_VADAPTER_RX_OVERFLOW 0x54 /* enum */
+#define MC_CMD_MAC_VADAPTER_TX_DMABUF_START 0x57 /* enum */
+#define MC_CMD_MAC_VADAPTER_TX_UNICAST_PACKETS 0x57 /* enum */
+#define MC_CMD_MAC_VADAPTER_TX_UNICAST_BYTES 0x58 /* enum */
+#define MC_CMD_MAC_VADAPTER_TX_MULTICAST_PACKETS 0x59 /* enum */
+#define MC_CMD_MAC_VADAPTER_TX_MULTICAST_BYTES 0x5a /* enum */
+#define MC_CMD_MAC_VADAPTER_TX_BROADCAST_PACKETS 0x5b /* enum */
+#define MC_CMD_MAC_VADAPTER_TX_BROADCAST_BYTES 0x5c /* enum */
+#define MC_CMD_MAC_VADAPTER_TX_BAD_PACKETS 0x5d /* enum */
+#define MC_CMD_MAC_VADAPTER_TX_BAD_BYTES 0x5e /* enum */
+#define MC_CMD_MAC_VADAPTER_TX_OVERFLOW 0x5f /* enum */
/* enum: Start of GMAC stats buffer space, for Siena only. */
-#define MC_CMD_GMAC_DMABUF_START 0x40
+#define MC_CMD_GMAC_DMABUF_START 0x40
/* enum: End of GMAC stats buffer space, for Siena only. */
-#define MC_CMD_GMAC_DMABUF_END 0x5f
+#define MC_CMD_GMAC_DMABUF_END 0x5f
/* enum: GENERATION_END value, used together with GENERATION_START to verify
* consistency of DMAd data. For legacy firmware / drivers without extended
* stats (more precisely, when DMA_LEN == MC_CMD_MAC_NSTATS *
@@ -4054,7 +4215,7 @@
* sizeof(uint64_t). See SF-109306-TC, Section 9.2 for details.
*/
#define MC_CMD_MAC_GENERATION_END 0x60
-#define MC_CMD_MAC_NSTATS 0x61 /* enum */
+#define MC_CMD_MAC_NSTATS 0x61 /* enum */
/* MC_CMD_MAC_STATS_V2_OUT_DMA msgresponse */
#define MC_CMD_MAC_STATS_V2_OUT_DMA_LEN 0
@@ -4067,25 +4228,25 @@
#define MC_CMD_MAC_STATS_V2_OUT_NO_DMA_STATISTICS_HI_OFST 4
#define MC_CMD_MAC_STATS_V2_OUT_NO_DMA_STATISTICS_NUM MC_CMD_MAC_NSTATS_V2
/* enum: Start of FEC stats buffer space, Medford2 and up */
-#define MC_CMD_MAC_FEC_DMABUF_START 0x61
+#define MC_CMD_MAC_FEC_DMABUF_START 0x61
/* enum: Number of uncorrected FEC codewords on link (RS-FEC only for Medford2)
*/
-#define MC_CMD_MAC_FEC_UNCORRECTED_ERRORS 0x61
+#define MC_CMD_MAC_FEC_UNCORRECTED_ERRORS 0x61
/* enum: Number of corrected FEC codewords on link (RS-FEC only for Medford2)
*/
-#define MC_CMD_MAC_FEC_CORRECTED_ERRORS 0x62
+#define MC_CMD_MAC_FEC_CORRECTED_ERRORS 0x62
/* enum: Number of corrected 10-bit symbol errors, lane 0 (RS-FEC only) */
-#define MC_CMD_MAC_FEC_CORRECTED_SYMBOLS_LANE0 0x63
+#define MC_CMD_MAC_FEC_CORRECTED_SYMBOLS_LANE0 0x63
/* enum: Number of corrected 10-bit symbol errors, lane 1 (RS-FEC only) */
-#define MC_CMD_MAC_FEC_CORRECTED_SYMBOLS_LANE1 0x64
+#define MC_CMD_MAC_FEC_CORRECTED_SYMBOLS_LANE1 0x64
/* enum: Number of corrected 10-bit symbol errors, lane 2 (RS-FEC only) */
-#define MC_CMD_MAC_FEC_CORRECTED_SYMBOLS_LANE2 0x65
+#define MC_CMD_MAC_FEC_CORRECTED_SYMBOLS_LANE2 0x65
/* enum: Number of corrected 10-bit symbol errors, lane 3 (RS-FEC only) */
-#define MC_CMD_MAC_FEC_CORRECTED_SYMBOLS_LANE3 0x66
+#define MC_CMD_MAC_FEC_CORRECTED_SYMBOLS_LANE3 0x66
/* enum: This includes the space at offset 103 which is the final
* GENERATION_END in a MAC_STATS_V2 response and otherwise unused.
*/
-#define MC_CMD_MAC_NSTATS_V2 0x68
+#define MC_CMD_MAC_NSTATS_V2 0x68
/* Other enum values, see field(s): */
/* MC_CMD_MAC_STATS_OUT_NO_DMA/STATISTICS */
@@ -4100,66 +4261,66 @@
#define MC_CMD_MAC_STATS_V3_OUT_NO_DMA_STATISTICS_HI_OFST 4
#define MC_CMD_MAC_STATS_V3_OUT_NO_DMA_STATISTICS_NUM MC_CMD_MAC_NSTATS_V3
/* enum: Start of CTPIO stats buffer space, Medford2 and up */
-#define MC_CMD_MAC_CTPIO_DMABUF_START 0x68
+#define MC_CMD_MAC_CTPIO_DMABUF_START 0x68
/* enum: Number of CTPIO fallbacks because a DMA packet was in progress on the
* target VI
*/
-#define MC_CMD_MAC_CTPIO_VI_BUSY_FALLBACK 0x68
+#define MC_CMD_MAC_CTPIO_VI_BUSY_FALLBACK 0x68
/* enum: Number of times a CTPIO send wrote beyond frame end (informational
* only)
*/
-#define MC_CMD_MAC_CTPIO_LONG_WRITE_SUCCESS 0x69
+#define MC_CMD_MAC_CTPIO_LONG_WRITE_SUCCESS 0x69
/* enum: Number of CTPIO failures because the TX doorbell was written before
* the end of the frame data
*/
-#define MC_CMD_MAC_CTPIO_MISSING_DBELL_FAIL 0x6a
+#define MC_CMD_MAC_CTPIO_MISSING_DBELL_FAIL 0x6a
/* enum: Number of CTPIO failures because the internal FIFO overflowed */
-#define MC_CMD_MAC_CTPIO_OVERFLOW_FAIL 0x6b
+#define MC_CMD_MAC_CTPIO_OVERFLOW_FAIL 0x6b
/* enum: Number of CTPIO failures because the host did not deliver data fast
* enough to avoid MAC underflow
*/
-#define MC_CMD_MAC_CTPIO_UNDERFLOW_FAIL 0x6c
+#define MC_CMD_MAC_CTPIO_UNDERFLOW_FAIL 0x6c
/* enum: Number of CTPIO failures because the host did not deliver all the
* frame data within the timeout
*/
-#define MC_CMD_MAC_CTPIO_TIMEOUT_FAIL 0x6d
+#define MC_CMD_MAC_CTPIO_TIMEOUT_FAIL 0x6d
/* enum: Number of CTPIO failures because the frame data arrived out of order
* or with gaps
*/
-#define MC_CMD_MAC_CTPIO_NONCONTIG_WR_FAIL 0x6e
+#define MC_CMD_MAC_CTPIO_NONCONTIG_WR_FAIL 0x6e
/* enum: Number of CTPIO failures because the host started a new frame before
* completing the previous one
*/
-#define MC_CMD_MAC_CTPIO_FRM_CLOBBER_FAIL 0x6f
+#define MC_CMD_MAC_CTPIO_FRM_CLOBBER_FAIL 0x6f
/* enum: Number of CTPIO failures because a write was not a multiple of 32 bits
* or not 32-bit aligned
*/
-#define MC_CMD_MAC_CTPIO_INVALID_WR_FAIL 0x70
+#define MC_CMD_MAC_CTPIO_INVALID_WR_FAIL 0x70
/* enum: Number of CTPIO fallbacks because another VI on the same port was
* sending a CTPIO frame
*/
-#define MC_CMD_MAC_CTPIO_VI_CLOBBER_FALLBACK 0x71
+#define MC_CMD_MAC_CTPIO_VI_CLOBBER_FALLBACK 0x71
/* enum: Number of CTPIO fallbacks because target VI did not have CTPIO enabled
*/
-#define MC_CMD_MAC_CTPIO_UNQUALIFIED_FALLBACK 0x72
+#define MC_CMD_MAC_CTPIO_UNQUALIFIED_FALLBACK 0x72
/* enum: Number of CTPIO fallbacks because length in header was less than 29
* bytes
*/
-#define MC_CMD_MAC_CTPIO_RUNT_FALLBACK 0x73
+#define MC_CMD_MAC_CTPIO_RUNT_FALLBACK 0x73
/* enum: Total number of successful CTPIO sends on this port */
-#define MC_CMD_MAC_CTPIO_SUCCESS 0x74
+#define MC_CMD_MAC_CTPIO_SUCCESS 0x74
/* enum: Total number of CTPIO fallbacks on this port */
-#define MC_CMD_MAC_CTPIO_FALLBACK 0x75
+#define MC_CMD_MAC_CTPIO_FALLBACK 0x75
/* enum: Total number of CTPIO poisoned frames on this port, whether erased or
* not
*/
-#define MC_CMD_MAC_CTPIO_POISON 0x76
+#define MC_CMD_MAC_CTPIO_POISON 0x76
/* enum: Total number of CTPIO erased frames on this port */
-#define MC_CMD_MAC_CTPIO_ERASE 0x77
+#define MC_CMD_MAC_CTPIO_ERASE 0x77
/* enum: This includes the space at offset 120 which is the final
* GENERATION_END in a MAC_STATS_V3 response and otherwise unused.
*/
-#define MC_CMD_MAC_NSTATS_V3 0x79
+#define MC_CMD_MAC_NSTATS_V3 0x79
/* Other enum values, see field(s): */
/* MC_CMD_MAC_STATS_V2_OUT_NO_DMA/STATISTICS */
@@ -4268,25 +4429,25 @@
#define MC_CMD_WOL_FILTER_SET_IN_LEN 192
#define MC_CMD_WOL_FILTER_SET_IN_FILTER_MODE_OFST 0
#define MC_CMD_WOL_FILTER_SET_IN_FILTER_MODE_LEN 4
-#define MC_CMD_FILTER_MODE_SIMPLE 0x0 /* enum */
+#define MC_CMD_FILTER_MODE_SIMPLE 0x0 /* enum */
#define MC_CMD_FILTER_MODE_STRUCTURED 0xffffffff /* enum */
/* A type value of 1 is unused. */
#define MC_CMD_WOL_FILTER_SET_IN_WOL_TYPE_OFST 4
#define MC_CMD_WOL_FILTER_SET_IN_WOL_TYPE_LEN 4
/* enum: Magic */
-#define MC_CMD_WOL_TYPE_MAGIC 0x0
+#define MC_CMD_WOL_TYPE_MAGIC 0x0
/* enum: MS Windows Magic */
#define MC_CMD_WOL_TYPE_WIN_MAGIC 0x2
/* enum: IPv4 Syn */
-#define MC_CMD_WOL_TYPE_IPV4_SYN 0x3
+#define MC_CMD_WOL_TYPE_IPV4_SYN 0x3
/* enum: IPv6 Syn */
-#define MC_CMD_WOL_TYPE_IPV6_SYN 0x4
+#define MC_CMD_WOL_TYPE_IPV6_SYN 0x4
/* enum: Bitmap */
-#define MC_CMD_WOL_TYPE_BITMAP 0x5
+#define MC_CMD_WOL_TYPE_BITMAP 0x5
/* enum: Link */
-#define MC_CMD_WOL_TYPE_LINK 0x6
+#define MC_CMD_WOL_TYPE_LINK 0x6
/* enum: (Above this for future use) */
-#define MC_CMD_WOL_TYPE_MAX 0x7
+#define MC_CMD_WOL_TYPE_MAX 0x7
#define MC_CMD_WOL_FILTER_SET_IN_DATA_OFST 8
#define MC_CMD_WOL_FILTER_SET_IN_DATA_LEN 4
#define MC_CMD_WOL_FILTER_SET_IN_DATA_NUM 46
@@ -4515,6 +4676,8 @@
#define MC_CMD_NVRAM_INFO_OUT_PROTECTED_WIDTH 1
#define MC_CMD_NVRAM_INFO_OUT_TLV_LBN 1
#define MC_CMD_NVRAM_INFO_OUT_TLV_WIDTH 1
+#define MC_CMD_NVRAM_INFO_OUT_READ_ONLY_IF_TSA_BOUND_LBN 2
+#define MC_CMD_NVRAM_INFO_OUT_READ_ONLY_IF_TSA_BOUND_WIDTH 1
#define MC_CMD_NVRAM_INFO_OUT_READ_ONLY_LBN 5
#define MC_CMD_NVRAM_INFO_OUT_READ_ONLY_WIDTH 1
#define MC_CMD_NVRAM_INFO_OUT_CMAC_LBN 6
@@ -4542,6 +4705,8 @@
#define MC_CMD_NVRAM_INFO_V2_OUT_PROTECTED_WIDTH 1
#define MC_CMD_NVRAM_INFO_V2_OUT_TLV_LBN 1
#define MC_CMD_NVRAM_INFO_V2_OUT_TLV_WIDTH 1
+#define MC_CMD_NVRAM_INFO_V2_OUT_READ_ONLY_IF_TSA_BOUND_LBN 2
+#define MC_CMD_NVRAM_INFO_V2_OUT_READ_ONLY_IF_TSA_BOUND_WIDTH 1
#define MC_CMD_NVRAM_INFO_V2_OUT_READ_ONLY_LBN 5
#define MC_CMD_NVRAM_INFO_V2_OUT_READ_ONLY_WIDTH 1
#define MC_CMD_NVRAM_INFO_V2_OUT_A_B_LBN 7
@@ -4560,7 +4725,11 @@
/* MC_CMD_NVRAM_UPDATE_START
* Start a group of update operations on a virtual NVRAM partition. Locks
* required: PHY_LOCK if type==*PHY*. Returns: 0, EINVAL (bad type), EACCES (if
- * PHY_LOCK required and not held).
+ * PHY_LOCK required and not held). In an adapter bound to a TSA controller,
+ * MC_CMD_NVRAM_UPDATE_START can only be used on a subset of partition types
+ * i.e. static config, dynamic config and expansion ROM config. Attempting to
+ * perform this operation on a restricted partition will return the error
+ * EPERM.
*/
#define MC_CMD_NVRAM_UPDATE_START 0x38
@@ -4720,8 +4889,12 @@
/***********************************/
/* MC_CMD_NVRAM_UPDATE_FINISH
* Finish a group of update operations on a virtual NVRAM partition. Locks
- * required: PHY_LOCK if type==*PHY*. Returns: 0, EINVAL (bad
- * type/offset/length), EACCES (if PHY_LOCK required and not held)
+ * required: PHY_LOCK if type==*PHY*. Returns: 0, EINVAL (bad type/offset/
+ * length), EACCES (if PHY_LOCK required and not held). In an adapter bound to
+ * a TSA controller, MC_CMD_NVRAM_UPDATE_FINISH can only be used on a subset of
+ * partition types i.e. static config, dynamic config and expansion ROM config.
+ * Attempting to perform this operation on a restricted partition will return
+ * the error EPERM.
*/
#define MC_CMD_NVRAM_UPDATE_FINISH 0x3c
@@ -4958,181 +5131,181 @@
#define MC_CMD_SENSOR_INFO_OUT_MASK_OFST 0
#define MC_CMD_SENSOR_INFO_OUT_MASK_LEN 4
/* enum: Controller temperature: degC */
-#define MC_CMD_SENSOR_CONTROLLER_TEMP 0x0
+#define MC_CMD_SENSOR_CONTROLLER_TEMP 0x0
/* enum: Phy common temperature: degC */
-#define MC_CMD_SENSOR_PHY_COMMON_TEMP 0x1
+#define MC_CMD_SENSOR_PHY_COMMON_TEMP 0x1
/* enum: Controller cooling: bool */
-#define MC_CMD_SENSOR_CONTROLLER_COOLING 0x2
+#define MC_CMD_SENSOR_CONTROLLER_COOLING 0x2
/* enum: Phy 0 temperature: degC */
-#define MC_CMD_SENSOR_PHY0_TEMP 0x3
+#define MC_CMD_SENSOR_PHY0_TEMP 0x3
/* enum: Phy 0 cooling: bool */
-#define MC_CMD_SENSOR_PHY0_COOLING 0x4
+#define MC_CMD_SENSOR_PHY0_COOLING 0x4
/* enum: Phy 1 temperature: degC */
-#define MC_CMD_SENSOR_PHY1_TEMP 0x5
+#define MC_CMD_SENSOR_PHY1_TEMP 0x5
/* enum: Phy 1 cooling: bool */
-#define MC_CMD_SENSOR_PHY1_COOLING 0x6
+#define MC_CMD_SENSOR_PHY1_COOLING 0x6
/* enum: 1.0v power: mV */
-#define MC_CMD_SENSOR_IN_1V0 0x7
+#define MC_CMD_SENSOR_IN_1V0 0x7
/* enum: 1.2v power: mV */
-#define MC_CMD_SENSOR_IN_1V2 0x8
+#define MC_CMD_SENSOR_IN_1V2 0x8
/* enum: 1.8v power: mV */
-#define MC_CMD_SENSOR_IN_1V8 0x9
+#define MC_CMD_SENSOR_IN_1V8 0x9
/* enum: 2.5v power: mV */
-#define MC_CMD_SENSOR_IN_2V5 0xa
+#define MC_CMD_SENSOR_IN_2V5 0xa
/* enum: 3.3v power: mV */
-#define MC_CMD_SENSOR_IN_3V3 0xb
+#define MC_CMD_SENSOR_IN_3V3 0xb
/* enum: 12v power: mV */
-#define MC_CMD_SENSOR_IN_12V0 0xc
+#define MC_CMD_SENSOR_IN_12V0 0xc
/* enum: 1.2v analogue power: mV */
-#define MC_CMD_SENSOR_IN_1V2A 0xd
+#define MC_CMD_SENSOR_IN_1V2A 0xd
/* enum: reference voltage: mV */
-#define MC_CMD_SENSOR_IN_VREF 0xe
+#define MC_CMD_SENSOR_IN_VREF 0xe
/* enum: AOE FPGA power: mV */
-#define MC_CMD_SENSOR_OUT_VAOE 0xf
+#define MC_CMD_SENSOR_OUT_VAOE 0xf
/* enum: AOE FPGA temperature: degC */
-#define MC_CMD_SENSOR_AOE_TEMP 0x10
+#define MC_CMD_SENSOR_AOE_TEMP 0x10
/* enum: AOE FPGA PSU temperature: degC */
-#define MC_CMD_SENSOR_PSU_AOE_TEMP 0x11
+#define MC_CMD_SENSOR_PSU_AOE_TEMP 0x11
/* enum: AOE PSU temperature: degC */
-#define MC_CMD_SENSOR_PSU_TEMP 0x12
+#define MC_CMD_SENSOR_PSU_TEMP 0x12
/* enum: Fan 0 speed: RPM */
-#define MC_CMD_SENSOR_FAN_0 0x13
+#define MC_CMD_SENSOR_FAN_0 0x13
/* enum: Fan 1 speed: RPM */
-#define MC_CMD_SENSOR_FAN_1 0x14
+#define MC_CMD_SENSOR_FAN_1 0x14
/* enum: Fan 2 speed: RPM */
-#define MC_CMD_SENSOR_FAN_2 0x15
+#define MC_CMD_SENSOR_FAN_2 0x15
/* enum: Fan 3 speed: RPM */
-#define MC_CMD_SENSOR_FAN_3 0x16
+#define MC_CMD_SENSOR_FAN_3 0x16
/* enum: Fan 4 speed: RPM */
-#define MC_CMD_SENSOR_FAN_4 0x17
+#define MC_CMD_SENSOR_FAN_4 0x17
/* enum: AOE FPGA input power: mV */
-#define MC_CMD_SENSOR_IN_VAOE 0x18
+#define MC_CMD_SENSOR_IN_VAOE 0x18
/* enum: AOE FPGA current: mA */
-#define MC_CMD_SENSOR_OUT_IAOE 0x19
+#define MC_CMD_SENSOR_OUT_IAOE 0x19
/* enum: AOE FPGA input current: mA */
-#define MC_CMD_SENSOR_IN_IAOE 0x1a
+#define MC_CMD_SENSOR_IN_IAOE 0x1a
/* enum: NIC power consumption: W */
-#define MC_CMD_SENSOR_NIC_POWER 0x1b
+#define MC_CMD_SENSOR_NIC_POWER 0x1b
/* enum: 0.9v power voltage: mV */
-#define MC_CMD_SENSOR_IN_0V9 0x1c
+#define MC_CMD_SENSOR_IN_0V9 0x1c
/* enum: 0.9v power current: mA */
-#define MC_CMD_SENSOR_IN_I0V9 0x1d
+#define MC_CMD_SENSOR_IN_I0V9 0x1d
/* enum: 1.2v power current: mA */
-#define MC_CMD_SENSOR_IN_I1V2 0x1e
+#define MC_CMD_SENSOR_IN_I1V2 0x1e
/* enum: Not a sensor: reserved for the next page flag */
-#define MC_CMD_SENSOR_PAGE0_NEXT 0x1f
+#define MC_CMD_SENSOR_PAGE0_NEXT 0x1f
/* enum: 0.9v power voltage (at ADC): mV */
-#define MC_CMD_SENSOR_IN_0V9_ADC 0x20
+#define MC_CMD_SENSOR_IN_0V9_ADC 0x20
/* enum: Controller temperature 2: degC */
-#define MC_CMD_SENSOR_CONTROLLER_2_TEMP 0x21
+#define MC_CMD_SENSOR_CONTROLLER_2_TEMP 0x21
/* enum: Voltage regulator internal temperature: degC */
-#define MC_CMD_SENSOR_VREG_INTERNAL_TEMP 0x22
+#define MC_CMD_SENSOR_VREG_INTERNAL_TEMP 0x22
/* enum: 0.9V voltage regulator temperature: degC */
-#define MC_CMD_SENSOR_VREG_0V9_TEMP 0x23
+#define MC_CMD_SENSOR_VREG_0V9_TEMP 0x23
/* enum: 1.2V voltage regulator temperature: degC */
-#define MC_CMD_SENSOR_VREG_1V2_TEMP 0x24
+#define MC_CMD_SENSOR_VREG_1V2_TEMP 0x24
/* enum: controller internal temperature sensor voltage (internal ADC): mV */
-#define MC_CMD_SENSOR_CONTROLLER_VPTAT 0x25
+#define MC_CMD_SENSOR_CONTROLLER_VPTAT 0x25
/* enum: controller internal temperature (internal ADC): degC */
-#define MC_CMD_SENSOR_CONTROLLER_INTERNAL_TEMP 0x26
+#define MC_CMD_SENSOR_CONTROLLER_INTERNAL_TEMP 0x26
/* enum: controller internal temperature sensor voltage (external ADC): mV */
-#define MC_CMD_SENSOR_CONTROLLER_VPTAT_EXTADC 0x27
+#define MC_CMD_SENSOR_CONTROLLER_VPTAT_EXTADC 0x27
/* enum: controller internal temperature (external ADC): degC */
-#define MC_CMD_SENSOR_CONTROLLER_INTERNAL_TEMP_EXTADC 0x28
+#define MC_CMD_SENSOR_CONTROLLER_INTERNAL_TEMP_EXTADC 0x28
/* enum: ambient temperature: degC */
-#define MC_CMD_SENSOR_AMBIENT_TEMP 0x29
+#define MC_CMD_SENSOR_AMBIENT_TEMP 0x29
/* enum: air flow: bool */
-#define MC_CMD_SENSOR_AIRFLOW 0x2a
+#define MC_CMD_SENSOR_AIRFLOW 0x2a
/* enum: voltage between VSS08D and VSS08D at CSR: mV */
-#define MC_CMD_SENSOR_VDD08D_VSS08D_CSR 0x2b
+#define MC_CMD_SENSOR_VDD08D_VSS08D_CSR 0x2b
/* enum: voltage between VSS08D and VSS08D at CSR (external ADC): mV */
-#define MC_CMD_SENSOR_VDD08D_VSS08D_CSR_EXTADC 0x2c
+#define MC_CMD_SENSOR_VDD08D_VSS08D_CSR_EXTADC 0x2c
/* enum: Hotpoint temperature: degC */
-#define MC_CMD_SENSOR_HOTPOINT_TEMP 0x2d
+#define MC_CMD_SENSOR_HOTPOINT_TEMP 0x2d
/* enum: Port 0 PHY power switch over-current: bool */
-#define MC_CMD_SENSOR_PHY_POWER_PORT0 0x2e
+#define MC_CMD_SENSOR_PHY_POWER_PORT0 0x2e
/* enum: Port 1 PHY power switch over-current: bool */
-#define MC_CMD_SENSOR_PHY_POWER_PORT1 0x2f
-/* enum: Mop-up microcontroller reference voltage (millivolts) */
-#define MC_CMD_SENSOR_MUM_VCC 0x30
+#define MC_CMD_SENSOR_PHY_POWER_PORT1 0x2f
+/* enum: Mop-up microcontroller reference voltage: mV */
+#define MC_CMD_SENSOR_MUM_VCC 0x30
/* enum: 0.9v power phase A voltage: mV */
-#define MC_CMD_SENSOR_IN_0V9_A 0x31
+#define MC_CMD_SENSOR_IN_0V9_A 0x31
/* enum: 0.9v power phase A current: mA */
-#define MC_CMD_SENSOR_IN_I0V9_A 0x32
+#define MC_CMD_SENSOR_IN_I0V9_A 0x32
/* enum: 0.9V voltage regulator phase A temperature: degC */
-#define MC_CMD_SENSOR_VREG_0V9_A_TEMP 0x33
+#define MC_CMD_SENSOR_VREG_0V9_A_TEMP 0x33
/* enum: 0.9v power phase B voltage: mV */
-#define MC_CMD_SENSOR_IN_0V9_B 0x34
+#define MC_CMD_SENSOR_IN_0V9_B 0x34
/* enum: 0.9v power phase B current: mA */
-#define MC_CMD_SENSOR_IN_I0V9_B 0x35
+#define MC_CMD_SENSOR_IN_I0V9_B 0x35
/* enum: 0.9V voltage regulator phase B temperature: degC */
-#define MC_CMD_SENSOR_VREG_0V9_B_TEMP 0x36
+#define MC_CMD_SENSOR_VREG_0V9_B_TEMP 0x36
/* enum: CCOM AVREG 1v2 supply (interval ADC): mV */
-#define MC_CMD_SENSOR_CCOM_AVREG_1V2_SUPPLY 0x37
+#define MC_CMD_SENSOR_CCOM_AVREG_1V2_SUPPLY 0x37
/* enum: CCOM AVREG 1v2 supply (external ADC): mV */
-#define MC_CMD_SENSOR_CCOM_AVREG_1V2_SUPPLY_EXTADC 0x38
+#define MC_CMD_SENSOR_CCOM_AVREG_1V2_SUPPLY_EXTADC 0x38
/* enum: CCOM AVREG 1v8 supply (interval ADC): mV */
-#define MC_CMD_SENSOR_CCOM_AVREG_1V8_SUPPLY 0x39
+#define MC_CMD_SENSOR_CCOM_AVREG_1V8_SUPPLY 0x39
/* enum: CCOM AVREG 1v8 supply (external ADC): mV */
-#define MC_CMD_SENSOR_CCOM_AVREG_1V8_SUPPLY_EXTADC 0x3a
+#define MC_CMD_SENSOR_CCOM_AVREG_1V8_SUPPLY_EXTADC 0x3a
/* enum: CCOM RTS temperature: degC */
-#define MC_CMD_SENSOR_CONTROLLER_RTS 0x3b
+#define MC_CMD_SENSOR_CONTROLLER_RTS 0x3b
/* enum: Not a sensor: reserved for the next page flag */
-#define MC_CMD_SENSOR_PAGE1_NEXT 0x3f
+#define MC_CMD_SENSOR_PAGE1_NEXT 0x3f
/* enum: controller internal temperature sensor voltage on master core
* (internal ADC): mV
*/
-#define MC_CMD_SENSOR_CONTROLLER_MASTER_VPTAT 0x40
+#define MC_CMD_SENSOR_CONTROLLER_MASTER_VPTAT 0x40
/* enum: controller internal temperature on master core (internal ADC): degC */
-#define MC_CMD_SENSOR_CONTROLLER_MASTER_INTERNAL_TEMP 0x41
+#define MC_CMD_SENSOR_CONTROLLER_MASTER_INTERNAL_TEMP 0x41
/* enum: controller internal temperature sensor voltage on master core
* (external ADC): mV
*/
-#define MC_CMD_SENSOR_CONTROLLER_MASTER_VPTAT_EXTADC 0x42
+#define MC_CMD_SENSOR_CONTROLLER_MASTER_VPTAT_EXTADC 0x42
/* enum: controller internal temperature on master core (external ADC): degC */
-#define MC_CMD_SENSOR_CONTROLLER_MASTER_INTERNAL_TEMP_EXTADC 0x43
+#define MC_CMD_SENSOR_CONTROLLER_MASTER_INTERNAL_TEMP_EXTADC 0x43
/* enum: controller internal temperature on slave core sensor voltage (internal
* ADC): mV
*/
-#define MC_CMD_SENSOR_CONTROLLER_SLAVE_VPTAT 0x44
+#define MC_CMD_SENSOR_CONTROLLER_SLAVE_VPTAT 0x44
/* enum: controller internal temperature on slave core (internal ADC): degC */
-#define MC_CMD_SENSOR_CONTROLLER_SLAVE_INTERNAL_TEMP 0x45
+#define MC_CMD_SENSOR_CONTROLLER_SLAVE_INTERNAL_TEMP 0x45
/* enum: controller internal temperature on slave core sensor voltage (external
* ADC): mV
*/
-#define MC_CMD_SENSOR_CONTROLLER_SLAVE_VPTAT_EXTADC 0x46
+#define MC_CMD_SENSOR_CONTROLLER_SLAVE_VPTAT_EXTADC 0x46
/* enum: controller internal temperature on slave core (external ADC): degC */
-#define MC_CMD_SENSOR_CONTROLLER_SLAVE_INTERNAL_TEMP_EXTADC 0x47
+#define MC_CMD_SENSOR_CONTROLLER_SLAVE_INTERNAL_TEMP_EXTADC 0x47
/* enum: Voltage supplied to the SODIMMs from their power supply: mV */
-#define MC_CMD_SENSOR_SODIMM_VOUT 0x49
+#define MC_CMD_SENSOR_SODIMM_VOUT 0x49
/* enum: Temperature of SODIMM 0 (if installed): degC */
-#define MC_CMD_SENSOR_SODIMM_0_TEMP 0x4a
+#define MC_CMD_SENSOR_SODIMM_0_TEMP 0x4a
/* enum: Temperature of SODIMM 1 (if installed): degC */
-#define MC_CMD_SENSOR_SODIMM_1_TEMP 0x4b
+#define MC_CMD_SENSOR_SODIMM_1_TEMP 0x4b
/* enum: Voltage supplied to the QSFP #0 from their power supply: mV */
-#define MC_CMD_SENSOR_PHY0_VCC 0x4c
+#define MC_CMD_SENSOR_PHY0_VCC 0x4c
/* enum: Voltage supplied to the QSFP #1 from their power supply: mV */
-#define MC_CMD_SENSOR_PHY1_VCC 0x4d
+#define MC_CMD_SENSOR_PHY1_VCC 0x4d
/* enum: Controller die temperature (TDIODE): degC */
-#define MC_CMD_SENSOR_CONTROLLER_TDIODE_TEMP 0x4e
+#define MC_CMD_SENSOR_CONTROLLER_TDIODE_TEMP 0x4e
/* enum: Board temperature (front): degC */
-#define MC_CMD_SENSOR_BOARD_FRONT_TEMP 0x4f
+#define MC_CMD_SENSOR_BOARD_FRONT_TEMP 0x4f
/* enum: Board temperature (back): degC */
-#define MC_CMD_SENSOR_BOARD_BACK_TEMP 0x50
+#define MC_CMD_SENSOR_BOARD_BACK_TEMP 0x50
/* enum: 1.8v power current: mA */
-#define MC_CMD_SENSOR_IN_I1V8 0x51
+#define MC_CMD_SENSOR_IN_I1V8 0x51
/* enum: 2.5v power current: mA */
-#define MC_CMD_SENSOR_IN_I2V5 0x52
+#define MC_CMD_SENSOR_IN_I2V5 0x52
/* enum: 3.3v power current: mA */
-#define MC_CMD_SENSOR_IN_I3V3 0x53
+#define MC_CMD_SENSOR_IN_I3V3 0x53
/* enum: 12v power current: mA */
-#define MC_CMD_SENSOR_IN_I12V0 0x54
+#define MC_CMD_SENSOR_IN_I12V0 0x54
/* enum: 1.3v power: mV */
-#define MC_CMD_SENSOR_IN_1V3 0x55
+#define MC_CMD_SENSOR_IN_1V3 0x55
/* enum: 1.3v power current: mA */
-#define MC_CMD_SENSOR_IN_I1V3 0x56
+#define MC_CMD_SENSOR_IN_I1V3 0x56
/* enum: Not a sensor: reserved for the next page flag */
-#define MC_CMD_SENSOR_PAGE2_NEXT 0x5f
+#define MC_CMD_SENSOR_PAGE2_NEXT 0x5f
/* MC_CMD_SENSOR_INFO_ENTRY_TYPEDEF */
#define MC_CMD_SENSOR_ENTRY_OFST 4
#define MC_CMD_SENSOR_ENTRY_LEN 8
@@ -5234,17 +5407,17 @@
#define MC_CMD_SENSOR_VALUE_ENTRY_TYPEDEF_STATE_OFST 2
#define MC_CMD_SENSOR_VALUE_ENTRY_TYPEDEF_STATE_LEN 1
/* enum: Ok. */
-#define MC_CMD_SENSOR_STATE_OK 0x0
+#define MC_CMD_SENSOR_STATE_OK 0x0
/* enum: Breached warning threshold. */
-#define MC_CMD_SENSOR_STATE_WARNING 0x1
+#define MC_CMD_SENSOR_STATE_WARNING 0x1
/* enum: Breached fatal threshold. */
-#define MC_CMD_SENSOR_STATE_FATAL 0x2
+#define MC_CMD_SENSOR_STATE_FATAL 0x2
/* enum: Fault with sensor. */
-#define MC_CMD_SENSOR_STATE_BROKEN 0x3
+#define MC_CMD_SENSOR_STATE_BROKEN 0x3
/* enum: Sensor is working but does not currently have a reading. */
-#define MC_CMD_SENSOR_STATE_NO_READING 0x4
+#define MC_CMD_SENSOR_STATE_NO_READING 0x4
/* enum: Sensor initialisation failed. */
-#define MC_CMD_SENSOR_STATE_INIT_FAILED 0x5
+#define MC_CMD_SENSOR_STATE_INIT_FAILED 0x5
#define MC_CMD_SENSOR_VALUE_ENTRY_TYPEDEF_STATE_LBN 16
#define MC_CMD_SENSOR_VALUE_ENTRY_TYPEDEF_STATE_WIDTH 8
#define MC_CMD_SENSOR_VALUE_ENTRY_TYPEDEF_TYPE_OFST 3
@@ -5327,7 +5500,7 @@
#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_PROTOCOL_OFST 0
#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_PROTOCOL_LEN 4
#define MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_ARP 0x1 /* enum */
-#define MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_NS 0x2 /* enum */
+#define MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_NS 0x2 /* enum */
#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_DATA_OFST 4
#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_DATA_LEN 4
#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_DATA_MINNUM 1
@@ -5416,17 +5589,17 @@
/* enum: Assert using the FAIL_ASSERTION_WITH_USEFUL_VALUES macro. Unless
* you're testing firmware, this is what you want.
*/
-#define MC_CMD_TESTASSERT_V2_IN_FAIL_ASSERTION_WITH_USEFUL_VALUES 0x0
+#define MC_CMD_TESTASSERT_V2_IN_FAIL_ASSERTION_WITH_USEFUL_VALUES 0x0
/* enum: Assert using assert(0); */
-#define MC_CMD_TESTASSERT_V2_IN_ASSERT_FALSE 0x1
+#define MC_CMD_TESTASSERT_V2_IN_ASSERT_FALSE 0x1
/* enum: Deliberately trigger a watchdog */
-#define MC_CMD_TESTASSERT_V2_IN_WATCHDOG 0x2
+#define MC_CMD_TESTASSERT_V2_IN_WATCHDOG 0x2
/* enum: Deliberately trigger a trap by loading from an invalid address */
-#define MC_CMD_TESTASSERT_V2_IN_LOAD_TRAP 0x3
+#define MC_CMD_TESTASSERT_V2_IN_LOAD_TRAP 0x3
/* enum: Deliberately trigger a trap by storing to an invalid address */
-#define MC_CMD_TESTASSERT_V2_IN_STORE_TRAP 0x4
+#define MC_CMD_TESTASSERT_V2_IN_STORE_TRAP 0x4
/* enum: Jump to an invalid address */
-#define MC_CMD_TESTASSERT_V2_IN_JUMP_TRAP 0x5
+#define MC_CMD_TESTASSERT_V2_IN_JUMP_TRAP 0x5
/* MC_CMD_TESTASSERT_V2_OUT msgresponse */
#define MC_CMD_TESTASSERT_V2_OUT_LEN 0
@@ -5969,7 +6142,7 @@
/* MC_CMD_MUM_IN_CMD_LEN 4 */
#define MC_CMD_MUM_IN_LOG_OP_OFST 4
#define MC_CMD_MUM_IN_LOG_OP_LEN 4
-#define MC_CMD_MUM_IN_LOG_OP_UART 0x1 /* enum */
+#define MC_CMD_MUM_IN_LOG_OP_UART 0x1 /* enum */
/* MC_CMD_MUM_IN_LOG_OP_UART msgrequest */
#define MC_CMD_MUM_IN_LOG_OP_UART_LEN 12
@@ -6464,17 +6637,17 @@
#define EVB_PORT_ID_PORT_ID_OFST 0
#define EVB_PORT_ID_PORT_ID_LEN 4
/* enum: An invalid port handle. */
-#define EVB_PORT_ID_NULL 0x0
+#define EVB_PORT_ID_NULL 0x0
/* enum: The port assigned to this function.. */
-#define EVB_PORT_ID_ASSIGNED 0x1000000
+#define EVB_PORT_ID_ASSIGNED 0x1000000
/* enum: External network port 0 */
-#define EVB_PORT_ID_MAC0 0x2000000
+#define EVB_PORT_ID_MAC0 0x2000000
/* enum: External network port 1 */
-#define EVB_PORT_ID_MAC1 0x2000001
+#define EVB_PORT_ID_MAC1 0x2000001
/* enum: External network port 2 */
-#define EVB_PORT_ID_MAC2 0x2000002
+#define EVB_PORT_ID_MAC2 0x2000002
/* enum: External network port 3 */
-#define EVB_PORT_ID_MAC3 0x2000003
+#define EVB_PORT_ID_MAC3 0x2000003
#define EVB_PORT_ID_PORT_ID_LBN 0
#define EVB_PORT_ID_PORT_ID_WIDTH 32
@@ -6486,7 +6659,7 @@
#define EVB_VLAN_TAG_MODE_LBN 12
#define EVB_VLAN_TAG_MODE_WIDTH 4
/* enum: Insert the VLAN. */
-#define EVB_VLAN_TAG_INSERT 0x0
+#define EVB_VLAN_TAG_INSERT 0x0
/* enum: Replace the VLAN if already present. */
#define EVB_VLAN_TAG_REPLACE 0x1
@@ -6515,110 +6688,110 @@
#define NVRAM_PARTITION_TYPE_ID_OFST 0
#define NVRAM_PARTITION_TYPE_ID_LEN 2
/* enum: Primary MC firmware partition */
-#define NVRAM_PARTITION_TYPE_MC_FIRMWARE 0x100
+#define NVRAM_PARTITION_TYPE_MC_FIRMWARE 0x100
/* enum: Secondary MC firmware partition */
-#define NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP 0x200
+#define NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP 0x200
/* enum: Expansion ROM partition */
-#define NVRAM_PARTITION_TYPE_EXPANSION_ROM 0x300
+#define NVRAM_PARTITION_TYPE_EXPANSION_ROM 0x300
/* enum: Static configuration TLV partition */
-#define NVRAM_PARTITION_TYPE_STATIC_CONFIG 0x400
+#define NVRAM_PARTITION_TYPE_STATIC_CONFIG 0x400
/* enum: Dynamic configuration TLV partition */
-#define NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG 0x500
+#define NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG 0x500
/* enum: Expansion ROM configuration data for port 0 */
-#define NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0 0x600
+#define NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0 0x600
/* enum: Synonym for EXPROM_CONFIG_PORT0 as used in pmap files */
-#define NVRAM_PARTITION_TYPE_EXPROM_CONFIG 0x600
+#define NVRAM_PARTITION_TYPE_EXPROM_CONFIG 0x600
/* enum: Expansion ROM configuration data for port 1 */
-#define NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT1 0x601
+#define NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT1 0x601
/* enum: Expansion ROM configuration data for port 2 */
-#define NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT2 0x602
+#define NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT2 0x602
/* enum: Expansion ROM configuration data for port 3 */
-#define NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT3 0x603
+#define NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT3 0x603
/* enum: Non-volatile log output partition */
-#define NVRAM_PARTITION_TYPE_LOG 0x700
+#define NVRAM_PARTITION_TYPE_LOG 0x700
/* enum: Non-volatile log output of second core on dual-core device */
-#define NVRAM_PARTITION_TYPE_LOG_SLAVE 0x701
+#define NVRAM_PARTITION_TYPE_LOG_SLAVE 0x701
/* enum: Device state dump output partition */
-#define NVRAM_PARTITION_TYPE_DUMP 0x800
+#define NVRAM_PARTITION_TYPE_DUMP 0x800
/* enum: Application license key storage partition */
-#define NVRAM_PARTITION_TYPE_LICENSE 0x900
+#define NVRAM_PARTITION_TYPE_LICENSE 0x900
/* enum: Start of range used for PHY partitions (low 8 bits are the PHY ID) */
-#define NVRAM_PARTITION_TYPE_PHY_MIN 0xa00
+#define NVRAM_PARTITION_TYPE_PHY_MIN 0xa00
/* enum: End of range used for PHY partitions (low 8 bits are the PHY ID) */
-#define NVRAM_PARTITION_TYPE_PHY_MAX 0xaff
+#define NVRAM_PARTITION_TYPE_PHY_MAX 0xaff
/* enum: Primary FPGA partition */
-#define NVRAM_PARTITION_TYPE_FPGA 0xb00
+#define NVRAM_PARTITION_TYPE_FPGA 0xb00
/* enum: Secondary FPGA partition */
-#define NVRAM_PARTITION_TYPE_FPGA_BACKUP 0xb01
+#define NVRAM_PARTITION_TYPE_FPGA_BACKUP 0xb01
/* enum: FC firmware partition */
-#define NVRAM_PARTITION_TYPE_FC_FIRMWARE 0xb02
+#define NVRAM_PARTITION_TYPE_FC_FIRMWARE 0xb02
/* enum: FC License partition */
-#define NVRAM_PARTITION_TYPE_FC_LICENSE 0xb03
+#define NVRAM_PARTITION_TYPE_FC_LICENSE 0xb03
/* enum: Non-volatile log output partition for FC */
-#define NVRAM_PARTITION_TYPE_FC_LOG 0xb04
+#define NVRAM_PARTITION_TYPE_FC_LOG 0xb04
/* enum: MUM firmware partition */
-#define NVRAM_PARTITION_TYPE_MUM_FIRMWARE 0xc00
+#define NVRAM_PARTITION_TYPE_MUM_FIRMWARE 0xc00
/* enum: SUC firmware partition (this is intentionally an alias of
* MUM_FIRMWARE)
*/
-#define NVRAM_PARTITION_TYPE_SUC_FIRMWARE 0xc00
+#define NVRAM_PARTITION_TYPE_SUC_FIRMWARE 0xc00
/* enum: MUM Non-volatile log output partition. */
-#define NVRAM_PARTITION_TYPE_MUM_LOG 0xc01
+#define NVRAM_PARTITION_TYPE_MUM_LOG 0xc01
/* enum: MUM Application table partition. */
-#define NVRAM_PARTITION_TYPE_MUM_APPTABLE 0xc02
+#define NVRAM_PARTITION_TYPE_MUM_APPTABLE 0xc02
/* enum: MUM boot rom partition. */
-#define NVRAM_PARTITION_TYPE_MUM_BOOT_ROM 0xc03
+#define NVRAM_PARTITION_TYPE_MUM_BOOT_ROM 0xc03
/* enum: MUM production signatures & calibration rom partition. */
-#define NVRAM_PARTITION_TYPE_MUM_PROD_ROM 0xc04
+#define NVRAM_PARTITION_TYPE_MUM_PROD_ROM 0xc04
/* enum: MUM user signatures & calibration rom partition. */
-#define NVRAM_PARTITION_TYPE_MUM_USER_ROM 0xc05
+#define NVRAM_PARTITION_TYPE_MUM_USER_ROM 0xc05
/* enum: MUM fuses and lockbits partition. */
-#define NVRAM_PARTITION_TYPE_MUM_FUSELOCK 0xc06
+#define NVRAM_PARTITION_TYPE_MUM_FUSELOCK 0xc06
/* enum: UEFI expansion ROM if separate from PXE */
-#define NVRAM_PARTITION_TYPE_EXPANSION_UEFI 0xd00
+#define NVRAM_PARTITION_TYPE_EXPANSION_UEFI 0xd00
/* enum: Used by the expansion ROM for logging */
-#define NVRAM_PARTITION_TYPE_PXE_LOG 0x1000
+#define NVRAM_PARTITION_TYPE_PXE_LOG 0x1000
/* enum: Used for XIP code of shmbooted images */
-#define NVRAM_PARTITION_TYPE_XIP_SCRATCH 0x1100
+#define NVRAM_PARTITION_TYPE_XIP_SCRATCH 0x1100
/* enum: Spare partition 2 */
-#define NVRAM_PARTITION_TYPE_SPARE_2 0x1200
+#define NVRAM_PARTITION_TYPE_SPARE_2 0x1200
/* enum: Manufacturing partition. Used during manufacture to pass information
* between XJTAG and Manftest.
*/
-#define NVRAM_PARTITION_TYPE_MANUFACTURING 0x1300
+#define NVRAM_PARTITION_TYPE_MANUFACTURING 0x1300
/* enum: Spare partition 4 */
-#define NVRAM_PARTITION_TYPE_SPARE_4 0x1400
+#define NVRAM_PARTITION_TYPE_SPARE_4 0x1400
/* enum: Spare partition 5 */
-#define NVRAM_PARTITION_TYPE_SPARE_5 0x1500
+#define NVRAM_PARTITION_TYPE_SPARE_5 0x1500
/* enum: Partition for reporting MC status. See mc_flash_layout.h
* medford_mc_status_hdr_t for layout on Medford.
*/
-#define NVRAM_PARTITION_TYPE_STATUS 0x1600
+#define NVRAM_PARTITION_TYPE_STATUS 0x1600
/* enum: Spare partition 13 */
-#define NVRAM_PARTITION_TYPE_SPARE_13 0x1700
+#define NVRAM_PARTITION_TYPE_SPARE_13 0x1700
/* enum: Spare partition 14 */
-#define NVRAM_PARTITION_TYPE_SPARE_14 0x1800
+#define NVRAM_PARTITION_TYPE_SPARE_14 0x1800
/* enum: Spare partition 15 */
-#define NVRAM_PARTITION_TYPE_SPARE_15 0x1900
+#define NVRAM_PARTITION_TYPE_SPARE_15 0x1900
/* enum: Spare partition 16 */
-#define NVRAM_PARTITION_TYPE_SPARE_16 0x1a00
+#define NVRAM_PARTITION_TYPE_SPARE_16 0x1a00
/* enum: Factory defaults for dynamic configuration */
-#define NVRAM_PARTITION_TYPE_DYNCONFIG_DEFAULTS 0x1b00
+#define NVRAM_PARTITION_TYPE_DYNCONFIG_DEFAULTS 0x1b00
/* enum: Factory defaults for expansion ROM configuration */
-#define NVRAM_PARTITION_TYPE_ROMCONFIG_DEFAULTS 0x1c00
+#define NVRAM_PARTITION_TYPE_ROMCONFIG_DEFAULTS 0x1c00
/* enum: Field Replaceable Unit inventory information for use on IPMI
* platforms. See SF-119124-PS. The STATIC_CONFIG partition may contain a
* subset of the information stored in this partition.
*/
-#define NVRAM_PARTITION_TYPE_FRU_INFORMATION 0x1d00
+#define NVRAM_PARTITION_TYPE_FRU_INFORMATION 0x1d00
/* enum: Start of reserved value range (firmware may use for any purpose) */
-#define NVRAM_PARTITION_TYPE_RESERVED_VALUES_MIN 0xff00
+#define NVRAM_PARTITION_TYPE_RESERVED_VALUES_MIN 0xff00
/* enum: End of reserved value range (firmware may use for any purpose) */
-#define NVRAM_PARTITION_TYPE_RESERVED_VALUES_MAX 0xfffd
+#define NVRAM_PARTITION_TYPE_RESERVED_VALUES_MAX 0xfffd
/* enum: Recovery partition map (provided if real map is missing or corrupt) */
-#define NVRAM_PARTITION_TYPE_RECOVERY_MAP 0xfffe
+#define NVRAM_PARTITION_TYPE_RECOVERY_MAP 0xfffe
/* enum: Partition map (real map as stored in flash) */
-#define NVRAM_PARTITION_TYPE_PARTITION_MAP 0xffff
+#define NVRAM_PARTITION_TYPE_PARTITION_MAP 0xffff
#define NVRAM_PARTITION_TYPE_ID_LBN 0
#define NVRAM_PARTITION_TYPE_ID_WIDTH 16
@@ -6627,37 +6800,37 @@
#define LICENSED_APP_ID_ID_OFST 0
#define LICENSED_APP_ID_ID_LEN 4
/* enum: OpenOnload */
-#define LICENSED_APP_ID_ONLOAD 0x1
+#define LICENSED_APP_ID_ONLOAD 0x1
/* enum: PTP timestamping */
-#define LICENSED_APP_ID_PTP 0x2
+#define LICENSED_APP_ID_PTP 0x2
/* enum: SolarCapture Pro */
-#define LICENSED_APP_ID_SOLARCAPTURE_PRO 0x4
+#define LICENSED_APP_ID_SOLARCAPTURE_PRO 0x4
/* enum: SolarSecure filter engine */
-#define LICENSED_APP_ID_SOLARSECURE 0x8
+#define LICENSED_APP_ID_SOLARSECURE 0x8
/* enum: Performance monitor */
-#define LICENSED_APP_ID_PERF_MONITOR 0x10
+#define LICENSED_APP_ID_PERF_MONITOR 0x10
/* enum: SolarCapture Live */
-#define LICENSED_APP_ID_SOLARCAPTURE_LIVE 0x20
+#define LICENSED_APP_ID_SOLARCAPTURE_LIVE 0x20
/* enum: Capture SolarSystem */
-#define LICENSED_APP_ID_CAPTURE_SOLARSYSTEM 0x40
+#define LICENSED_APP_ID_CAPTURE_SOLARSYSTEM 0x40
/* enum: Network Access Control */
-#define LICENSED_APP_ID_NETWORK_ACCESS_CONTROL 0x80
+#define LICENSED_APP_ID_NETWORK_ACCESS_CONTROL 0x80
/* enum: TCP Direct */
-#define LICENSED_APP_ID_TCP_DIRECT 0x100
+#define LICENSED_APP_ID_TCP_DIRECT 0x100
/* enum: Low Latency */
-#define LICENSED_APP_ID_LOW_LATENCY 0x200
+#define LICENSED_APP_ID_LOW_LATENCY 0x200
/* enum: SolarCapture Tap */
-#define LICENSED_APP_ID_SOLARCAPTURE_TAP 0x400
+#define LICENSED_APP_ID_SOLARCAPTURE_TAP 0x400
/* enum: Capture SolarSystem 40G */
#define LICENSED_APP_ID_CAPTURE_SOLARSYSTEM_40G 0x800
/* enum: Capture SolarSystem 1G */
-#define LICENSED_APP_ID_CAPTURE_SOLARSYSTEM_1G 0x1000
+#define LICENSED_APP_ID_CAPTURE_SOLARSYSTEM_1G 0x1000
/* enum: ScaleOut Onload */
-#define LICENSED_APP_ID_SCALEOUT_ONLOAD 0x2000
+#define LICENSED_APP_ID_SCALEOUT_ONLOAD 0x2000
/* enum: SCS Network Analytics Dashboard */
-#define LICENSED_APP_ID_DSHBRD 0x4000
+#define LICENSED_APP_ID_DSHBRD 0x4000
/* enum: SolarCapture Trading Analytics */
-#define LICENSED_APP_ID_SCATRD 0x8000
+#define LICENSED_APP_ID_SCATRD 0x8000
#define LICENSED_APP_ID_ID_LBN 0
#define LICENSED_APP_ID_ID_WIDTH 32
@@ -6775,23 +6948,23 @@
#define TX_TIMESTAMP_EVENT_TX_EV_TYPE_OFST 3
#define TX_TIMESTAMP_EVENT_TX_EV_TYPE_LEN 1
/* enum: This is a TX completion event, not a timestamp */
-#define TX_TIMESTAMP_EVENT_TX_EV_COMPLETION 0x0
+#define TX_TIMESTAMP_EVENT_TX_EV_COMPLETION 0x0
/* enum: This is a TX completion event for a CTPIO transmit. The event format
* is the same as for TX_EV_COMPLETION.
*/
-#define TX_TIMESTAMP_EVENT_TX_EV_CTPIO_COMPLETION 0x11
+#define TX_TIMESTAMP_EVENT_TX_EV_CTPIO_COMPLETION 0x11
/* enum: This is the low part of a TX timestamp for a CTPIO transmission. The
* event format is the same as for TX_EV_TSTAMP_LO
*/
-#define TX_TIMESTAMP_EVENT_TX_EV_CTPIO_TS_LO 0x12
+#define TX_TIMESTAMP_EVENT_TX_EV_CTPIO_TS_LO 0x12
/* enum: This is the high part of a TX timestamp for a CTPIO transmission. The
* event format is the same as for TX_EV_TSTAMP_HI
*/
-#define TX_TIMESTAMP_EVENT_TX_EV_CTPIO_TS_HI 0x13
+#define TX_TIMESTAMP_EVENT_TX_EV_CTPIO_TS_HI 0x13
/* enum: This is the low part of a TX timestamp event */
-#define TX_TIMESTAMP_EVENT_TX_EV_TSTAMP_LO 0x51
+#define TX_TIMESTAMP_EVENT_TX_EV_TSTAMP_LO 0x51
/* enum: This is the high part of a TX timestamp event */
-#define TX_TIMESTAMP_EVENT_TX_EV_TSTAMP_HI 0x52
+#define TX_TIMESTAMP_EVENT_TX_EV_TSTAMP_HI 0x52
#define TX_TIMESTAMP_EVENT_TX_EV_TYPE_LBN 24
#define TX_TIMESTAMP_EVENT_TX_EV_TYPE_WIDTH 8
/* upper 16 bits of timestamp data */
@@ -7071,17 +7244,17 @@
#define QUEUE_CRC_MODE_MODE_LBN 0
#define QUEUE_CRC_MODE_MODE_WIDTH 4
/* enum: No CRC. */
-#define QUEUE_CRC_MODE_NONE 0x0
+#define QUEUE_CRC_MODE_NONE 0x0
/* enum: CRC Fiber channel over ethernet. */
-#define QUEUE_CRC_MODE_FCOE 0x1
+#define QUEUE_CRC_MODE_FCOE 0x1
/* enum: CRC (digest) iSCSI header only. */
-#define QUEUE_CRC_MODE_ISCSI_HDR 0x2
+#define QUEUE_CRC_MODE_ISCSI_HDR 0x2
/* enum: CRC (digest) iSCSI header and payload. */
-#define QUEUE_CRC_MODE_ISCSI 0x3
+#define QUEUE_CRC_MODE_ISCSI 0x3
/* enum: CRC Fiber channel over IP over ethernet. */
-#define QUEUE_CRC_MODE_FCOIPOE 0x4
+#define QUEUE_CRC_MODE_FCOIPOE 0x4
/* enum: CRC MPA. */
-#define QUEUE_CRC_MODE_MPA 0x5
+#define QUEUE_CRC_MODE_MPA 0x5
#define QUEUE_CRC_MODE_SPARE_LBN 4
#define QUEUE_CRC_MODE_SPARE_WIDTH 4
@@ -7157,11 +7330,15 @@
/* Size, in entries */
#define MC_CMD_INIT_RXQ_EXT_IN_SIZE_OFST 0
#define MC_CMD_INIT_RXQ_EXT_IN_SIZE_LEN 4
-/* The EVQ to send events to. This is an index originally specified to INIT_EVQ
+/* The EVQ to send events to. This is an index originally specified to
+ * INIT_EVQ. If DMA_MODE == PACKED_STREAM this must be equal to INSTANCE.
*/
#define MC_CMD_INIT_RXQ_EXT_IN_TARGET_EVQ_OFST 4
#define MC_CMD_INIT_RXQ_EXT_IN_TARGET_EVQ_LEN 4
-/* The value to put in the event data. Check hardware spec. for valid range. */
+/* The value to put in the event data. Check hardware spec. for valid range.
+ * This field is ignored if DMA_MODE == EQUAL_STRIDE_PACKED_STREAM or DMA_MODE
+ * == PACKED_STREAM.
+ */
#define MC_CMD_INIT_RXQ_EXT_IN_LABEL_OFST 8
#define MC_CMD_INIT_RXQ_EXT_IN_LABEL_LEN 4
/* Desired instance. Must be set to a specific instance, which is a function
@@ -7189,18 +7366,25 @@
#define MC_CMD_INIT_RXQ_EXT_IN_DMA_MODE_LBN 10
#define MC_CMD_INIT_RXQ_EXT_IN_DMA_MODE_WIDTH 4
/* enum: One packet per descriptor (for normal networking) */
-#define MC_CMD_INIT_RXQ_EXT_IN_SINGLE_PACKET 0x0
+#define MC_CMD_INIT_RXQ_EXT_IN_SINGLE_PACKET 0x0
/* enum: Pack multiple packets into large descriptors (for SolarCapture) */
-#define MC_CMD_INIT_RXQ_EXT_IN_PACKED_STREAM 0x1
+#define MC_CMD_INIT_RXQ_EXT_IN_PACKED_STREAM 0x1
+/* enum: Pack multiple packets into large descriptors using the format designed
+ * to maximise packet rate. This mode uses 1 "bucket" per descriptor with
+ * multiple fixed-size packet buffers within each bucket. For a full
+ * description see SF-119419-TC. This mode is only supported by "dpdk" datapath
+ * firmware.
+ */
+#define MC_CMD_INIT_RXQ_EXT_IN_EQUAL_STRIDE_PACKED_STREAM 0x2
#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_SNAPSHOT_MODE_LBN 14
#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_SNAPSHOT_MODE_WIDTH 1
#define MC_CMD_INIT_RXQ_EXT_IN_PACKED_STREAM_BUFF_SIZE_LBN 15
#define MC_CMD_INIT_RXQ_EXT_IN_PACKED_STREAM_BUFF_SIZE_WIDTH 3
-#define MC_CMD_INIT_RXQ_EXT_IN_PS_BUFF_1M 0x0 /* enum */
-#define MC_CMD_INIT_RXQ_EXT_IN_PS_BUFF_512K 0x1 /* enum */
-#define MC_CMD_INIT_RXQ_EXT_IN_PS_BUFF_256K 0x2 /* enum */
-#define MC_CMD_INIT_RXQ_EXT_IN_PS_BUFF_128K 0x3 /* enum */
-#define MC_CMD_INIT_RXQ_EXT_IN_PS_BUFF_64K 0x4 /* enum */
+#define MC_CMD_INIT_RXQ_EXT_IN_PS_BUFF_1M 0x0 /* enum */
+#define MC_CMD_INIT_RXQ_EXT_IN_PS_BUFF_512K 0x1 /* enum */
+#define MC_CMD_INIT_RXQ_EXT_IN_PS_BUFF_256K 0x2 /* enum */
+#define MC_CMD_INIT_RXQ_EXT_IN_PS_BUFF_128K 0x3 /* enum */
+#define MC_CMD_INIT_RXQ_EXT_IN_PS_BUFF_64K 0x4 /* enum */
#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_WANT_OUTER_CLASSES_LBN 18
#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_WANT_OUTER_CLASSES_WIDTH 1
#define MC_CMD_INIT_RXQ_EXT_IN_FLAG_FORCE_EV_MERGING_LBN 19
@@ -7221,12 +7405,122 @@
#define MC_CMD_INIT_RXQ_EXT_IN_SNAPSHOT_LENGTH_OFST 540
#define MC_CMD_INIT_RXQ_EXT_IN_SNAPSHOT_LENGTH_LEN 4
+/* MC_CMD_INIT_RXQ_V3_IN msgrequest */
+#define MC_CMD_INIT_RXQ_V3_IN_LEN 560
+/* Size, in entries */
+#define MC_CMD_INIT_RXQ_V3_IN_SIZE_OFST 0
+#define MC_CMD_INIT_RXQ_V3_IN_SIZE_LEN 4
+/* The EVQ to send events to. This is an index originally specified to
+ * INIT_EVQ. If DMA_MODE == PACKED_STREAM this must be equal to INSTANCE.
+ */
+#define MC_CMD_INIT_RXQ_V3_IN_TARGET_EVQ_OFST 4
+#define MC_CMD_INIT_RXQ_V3_IN_TARGET_EVQ_LEN 4
+/* The value to put in the event data. Check hardware spec. for valid range.
+ * This field is ignored if DMA_MODE == EQUAL_STRIDE_PACKED_STREAM or DMA_MODE
+ * == PACKED_STREAM.
+ */
+#define MC_CMD_INIT_RXQ_V3_IN_LABEL_OFST 8
+#define MC_CMD_INIT_RXQ_V3_IN_LABEL_LEN 4
+/* Desired instance. Must be set to a specific instance, which is a function
+ * local queue index.
+ */
+#define MC_CMD_INIT_RXQ_V3_IN_INSTANCE_OFST 12
+#define MC_CMD_INIT_RXQ_V3_IN_INSTANCE_LEN 4
+/* There will be more flags here. */
+#define MC_CMD_INIT_RXQ_V3_IN_FLAGS_OFST 16
+#define MC_CMD_INIT_RXQ_V3_IN_FLAGS_LEN 4
+#define MC_CMD_INIT_RXQ_V3_IN_FLAG_BUFF_MODE_LBN 0
+#define MC_CMD_INIT_RXQ_V3_IN_FLAG_BUFF_MODE_WIDTH 1
+#define MC_CMD_INIT_RXQ_V3_IN_FLAG_HDR_SPLIT_LBN 1
+#define MC_CMD_INIT_RXQ_V3_IN_FLAG_HDR_SPLIT_WIDTH 1
+#define MC_CMD_INIT_RXQ_V3_IN_FLAG_TIMESTAMP_LBN 2
+#define MC_CMD_INIT_RXQ_V3_IN_FLAG_TIMESTAMP_WIDTH 1
+#define MC_CMD_INIT_RXQ_V3_IN_CRC_MODE_LBN 3
+#define MC_CMD_INIT_RXQ_V3_IN_CRC_MODE_WIDTH 4
+#define MC_CMD_INIT_RXQ_V3_IN_FLAG_CHAIN_LBN 7
+#define MC_CMD_INIT_RXQ_V3_IN_FLAG_CHAIN_WIDTH 1
+#define MC_CMD_INIT_RXQ_V3_IN_FLAG_PREFIX_LBN 8
+#define MC_CMD_INIT_RXQ_V3_IN_FLAG_PREFIX_WIDTH 1
+#define MC_CMD_INIT_RXQ_V3_IN_FLAG_DISABLE_SCATTER_LBN 9
+#define MC_CMD_INIT_RXQ_V3_IN_FLAG_DISABLE_SCATTER_WIDTH 1
+#define MC_CMD_INIT_RXQ_V3_IN_DMA_MODE_LBN 10
+#define MC_CMD_INIT_RXQ_V3_IN_DMA_MODE_WIDTH 4
+/* enum: One packet per descriptor (for normal networking) */
+#define MC_CMD_INIT_RXQ_V3_IN_SINGLE_PACKET 0x0
+/* enum: Pack multiple packets into large descriptors (for SolarCapture) */
+#define MC_CMD_INIT_RXQ_V3_IN_PACKED_STREAM 0x1
+/* enum: Pack multiple packets into large descriptors using the format designed
+ * to maximise packet rate. This mode uses 1 "bucket" per descriptor with
+ * multiple fixed-size packet buffers within each bucket. For a full
+ * description see SF-119419-TC. This mode is only supported by "dpdk" datapath
+ * firmware.
+ */
+#define MC_CMD_INIT_RXQ_V3_IN_EQUAL_STRIDE_PACKED_STREAM 0x2
+#define MC_CMD_INIT_RXQ_V3_IN_FLAG_SNAPSHOT_MODE_LBN 14
+#define MC_CMD_INIT_RXQ_V3_IN_FLAG_SNAPSHOT_MODE_WIDTH 1
+#define MC_CMD_INIT_RXQ_V3_IN_PACKED_STREAM_BUFF_SIZE_LBN 15
+#define MC_CMD_INIT_RXQ_V3_IN_PACKED_STREAM_BUFF_SIZE_WIDTH 3
+#define MC_CMD_INIT_RXQ_V3_IN_PS_BUFF_1M 0x0 /* enum */
+#define MC_CMD_INIT_RXQ_V3_IN_PS_BUFF_512K 0x1 /* enum */
+#define MC_CMD_INIT_RXQ_V3_IN_PS_BUFF_256K 0x2 /* enum */
+#define MC_CMD_INIT_RXQ_V3_IN_PS_BUFF_128K 0x3 /* enum */
+#define MC_CMD_INIT_RXQ_V3_IN_PS_BUFF_64K 0x4 /* enum */
+#define MC_CMD_INIT_RXQ_V3_IN_FLAG_WANT_OUTER_CLASSES_LBN 18
+#define MC_CMD_INIT_RXQ_V3_IN_FLAG_WANT_OUTER_CLASSES_WIDTH 1
+#define MC_CMD_INIT_RXQ_V3_IN_FLAG_FORCE_EV_MERGING_LBN 19
+#define MC_CMD_INIT_RXQ_V3_IN_FLAG_FORCE_EV_MERGING_WIDTH 1
+/* Owner ID to use if in buffer mode (zero if physical) */
+#define MC_CMD_INIT_RXQ_V3_IN_OWNER_ID_OFST 20
+#define MC_CMD_INIT_RXQ_V3_IN_OWNER_ID_LEN 4
+/* The port ID associated with the v-adaptor which should contain this DMAQ. */
+#define MC_CMD_INIT_RXQ_V3_IN_PORT_ID_OFST 24
+#define MC_CMD_INIT_RXQ_V3_IN_PORT_ID_LEN 4
+/* 64-bit address of 4k of 4k-aligned host memory buffer */
+#define MC_CMD_INIT_RXQ_V3_IN_DMA_ADDR_OFST 28
+#define MC_CMD_INIT_RXQ_V3_IN_DMA_ADDR_LEN 8
+#define MC_CMD_INIT_RXQ_V3_IN_DMA_ADDR_LO_OFST 28
+#define MC_CMD_INIT_RXQ_V3_IN_DMA_ADDR_HI_OFST 32
+#define MC_CMD_INIT_RXQ_V3_IN_DMA_ADDR_NUM 64
+/* Maximum length of packet to receive, if SNAPSHOT_MODE flag is set */
+#define MC_CMD_INIT_RXQ_V3_IN_SNAPSHOT_LENGTH_OFST 540
+#define MC_CMD_INIT_RXQ_V3_IN_SNAPSHOT_LENGTH_LEN 4
+/* The number of packet buffers that will be contained within each
+ * EQUAL_STRIDE_PACKED_STREAM format bucket supplied by the driver. This field
+ * is ignored unless DMA_MODE == EQUAL_STRIDE_PACKED_STREAM.
+ */
+#define MC_CMD_INIT_RXQ_V3_IN_ES_PACKET_BUFFERS_PER_BUCKET_OFST 544
+#define MC_CMD_INIT_RXQ_V3_IN_ES_PACKET_BUFFERS_PER_BUCKET_LEN 4
+/* The length in bytes of the area in each packet buffer that can be written to
+ * by the adapter. This is used to store the packet prefix and the packet
+ * payload. This length does not include any end padding added by the driver.
+ * This field is ignored unless DMA_MODE == EQUAL_STRIDE_PACKED_STREAM.
+ */
+#define MC_CMD_INIT_RXQ_V3_IN_ES_MAX_DMA_LEN_OFST 548
+#define MC_CMD_INIT_RXQ_V3_IN_ES_MAX_DMA_LEN_LEN 4
+/* The length in bytes of a single packet buffer within a
+ * EQUAL_STRIDE_PACKED_STREAM format bucket. This field is ignored unless
+ * DMA_MODE == EQUAL_STRIDE_PACKED_STREAM.
+ */
+#define MC_CMD_INIT_RXQ_V3_IN_ES_PACKET_STRIDE_OFST 552
+#define MC_CMD_INIT_RXQ_V3_IN_ES_PACKET_STRIDE_LEN 4
+/* The maximum time in nanoseconds that the datapath will be backpressured if
+ * there are no RX descriptors available. If the timeout is reached and there
+ * are still no descriptors then the packet will be dropped. A timeout of 0
+ * means the datapath will never be blocked. This field is ignored unless
+ * DMA_MODE == EQUAL_STRIDE_PACKED_STREAM.
+ */
+#define MC_CMD_INIT_RXQ_V3_IN_ES_HEAD_OF_LINE_BLOCK_TIMEOUT_OFST 556
+#define MC_CMD_INIT_RXQ_V3_IN_ES_HEAD_OF_LINE_BLOCK_TIMEOUT_LEN 4
+
/* MC_CMD_INIT_RXQ_OUT msgresponse */
#define MC_CMD_INIT_RXQ_OUT_LEN 0
/* MC_CMD_INIT_RXQ_EXT_OUT msgresponse */
#define MC_CMD_INIT_RXQ_EXT_OUT_LEN 0
+/* MC_CMD_INIT_RXQ_V3_OUT msgresponse */
+#define MC_CMD_INIT_RXQ_V3_OUT_LEN 0
+
/***********************************/
/* MC_CMD_INIT_TXQ
@@ -7466,7 +7760,7 @@
#define MC_CMD_PROXY_CMD_IN_TARGET_PF_WIDTH 16
#define MC_CMD_PROXY_CMD_IN_TARGET_VF_LBN 16
#define MC_CMD_PROXY_CMD_IN_TARGET_VF_WIDTH 16
-#define MC_CMD_PROXY_CMD_IN_VF_NULL 0xffff /* enum */
+#define MC_CMD_PROXY_CMD_IN_VF_NULL 0xffff /* enum */
/* MC_CMD_PROXY_CMD_OUT msgresponse */
#define MC_CMD_PROXY_CMD_OUT_LEN 0
@@ -7479,7 +7773,7 @@
#define MC_PROXY_STATUS_BUFFER_HANDLE_OFST 0
#define MC_PROXY_STATUS_BUFFER_HANDLE_LEN 4
/* enum: An invalid handle. */
-#define MC_PROXY_STATUS_BUFFER_HANDLE_INVALID 0x0
+#define MC_PROXY_STATUS_BUFFER_HANDLE_INVALID 0x0
#define MC_PROXY_STATUS_BUFFER_HANDLE_LBN 0
#define MC_PROXY_STATUS_BUFFER_HANDLE_WIDTH 32
/* The requesting physical function number */
@@ -7748,17 +8042,17 @@
#define MC_CMD_FILTER_OP_IN_OP_OFST 0
#define MC_CMD_FILTER_OP_IN_OP_LEN 4
/* enum: single-recipient filter insert */
-#define MC_CMD_FILTER_OP_IN_OP_INSERT 0x0
+#define MC_CMD_FILTER_OP_IN_OP_INSERT 0x0
/* enum: single-recipient filter remove */
-#define MC_CMD_FILTER_OP_IN_OP_REMOVE 0x1
+#define MC_CMD_FILTER_OP_IN_OP_REMOVE 0x1
/* enum: multi-recipient filter subscribe */
-#define MC_CMD_FILTER_OP_IN_OP_SUBSCRIBE 0x2
+#define MC_CMD_FILTER_OP_IN_OP_SUBSCRIBE 0x2
/* enum: multi-recipient filter unsubscribe */
-#define MC_CMD_FILTER_OP_IN_OP_UNSUBSCRIBE 0x3
+#define MC_CMD_FILTER_OP_IN_OP_UNSUBSCRIBE 0x3
/* enum: replace one recipient with another (warning - the filter handle may
* change)
*/
-#define MC_CMD_FILTER_OP_IN_OP_REPLACE 0x4
+#define MC_CMD_FILTER_OP_IN_OP_REPLACE 0x4
/* filter handle (for remove / unsubscribe operations) */
#define MC_CMD_FILTER_OP_IN_HANDLE_OFST 4
#define MC_CMD_FILTER_OP_IN_HANDLE_LEN 8
@@ -7803,15 +8097,15 @@
#define MC_CMD_FILTER_OP_IN_RX_DEST_OFST 20
#define MC_CMD_FILTER_OP_IN_RX_DEST_LEN 4
/* enum: drop packets */
-#define MC_CMD_FILTER_OP_IN_RX_DEST_DROP 0x0
+#define MC_CMD_FILTER_OP_IN_RX_DEST_DROP 0x0
/* enum: receive to host */
-#define MC_CMD_FILTER_OP_IN_RX_DEST_HOST 0x1
+#define MC_CMD_FILTER_OP_IN_RX_DEST_HOST 0x1
/* enum: receive to MC */
-#define MC_CMD_FILTER_OP_IN_RX_DEST_MC 0x2
+#define MC_CMD_FILTER_OP_IN_RX_DEST_MC 0x2
/* enum: loop back to TXDP 0 */
-#define MC_CMD_FILTER_OP_IN_RX_DEST_TX0 0x3
+#define MC_CMD_FILTER_OP_IN_RX_DEST_TX0 0x3
/* enum: loop back to TXDP 1 */
-#define MC_CMD_FILTER_OP_IN_RX_DEST_TX1 0x4
+#define MC_CMD_FILTER_OP_IN_RX_DEST_TX1 0x4
/* receive queue handle (for multiple queue modes, this is the base queue) */
#define MC_CMD_FILTER_OP_IN_RX_QUEUE_OFST 24
#define MC_CMD_FILTER_OP_IN_RX_QUEUE_LEN 4
@@ -7819,14 +8113,14 @@
#define MC_CMD_FILTER_OP_IN_RX_MODE_OFST 28
#define MC_CMD_FILTER_OP_IN_RX_MODE_LEN 4
/* enum: receive to just the specified queue */
-#define MC_CMD_FILTER_OP_IN_RX_MODE_SIMPLE 0x0
+#define MC_CMD_FILTER_OP_IN_RX_MODE_SIMPLE 0x0
/* enum: receive to multiple queues using RSS context */
-#define MC_CMD_FILTER_OP_IN_RX_MODE_RSS 0x1
+#define MC_CMD_FILTER_OP_IN_RX_MODE_RSS 0x1
/* enum: receive to multiple queues using .1p mapping */
-#define MC_CMD_FILTER_OP_IN_RX_MODE_DOT1P_MAPPING 0x2
+#define MC_CMD_FILTER_OP_IN_RX_MODE_DOT1P_MAPPING 0x2
/* enum: install a filter entry that will never match; for test purposes only
*/
-#define MC_CMD_FILTER_OP_IN_RX_MODE_TEST_NEVER_MATCH 0x80000000
+#define MC_CMD_FILTER_OP_IN_RX_MODE_TEST_NEVER_MATCH 0x80000000
/* RSS context (for RX_MODE_RSS) or .1p mapping handle (for
* RX_MODE_DOT1P_MAPPING), as returned by MC_CMD_RSS_CONTEXT_ALLOC or
* MC_CMD_DOT1P_MAPPING_ALLOC.
@@ -7843,7 +8137,7 @@
#define MC_CMD_FILTER_OP_IN_TX_DEST_OFST 40
#define MC_CMD_FILTER_OP_IN_TX_DEST_LEN 4
/* enum: request default behaviour (based on filter type) */
-#define MC_CMD_FILTER_OP_IN_TX_DEST_DEFAULT 0xffffffff
+#define MC_CMD_FILTER_OP_IN_TX_DEST_DEFAULT 0xffffffff
#define MC_CMD_FILTER_OP_IN_TX_DEST_MAC_LBN 0
#define MC_CMD_FILTER_OP_IN_TX_DEST_MAC_WIDTH 1
#define MC_CMD_FILTER_OP_IN_TX_DEST_PM_LBN 1
@@ -7971,15 +8265,15 @@
#define MC_CMD_FILTER_OP_EXT_IN_RX_DEST_OFST 20
#define MC_CMD_FILTER_OP_EXT_IN_RX_DEST_LEN 4
/* enum: drop packets */
-#define MC_CMD_FILTER_OP_EXT_IN_RX_DEST_DROP 0x0
+#define MC_CMD_FILTER_OP_EXT_IN_RX_DEST_DROP 0x0
/* enum: receive to host */
-#define MC_CMD_FILTER_OP_EXT_IN_RX_DEST_HOST 0x1
+#define MC_CMD_FILTER_OP_EXT_IN_RX_DEST_HOST 0x1
/* enum: receive to MC */
-#define MC_CMD_FILTER_OP_EXT_IN_RX_DEST_MC 0x2
+#define MC_CMD_FILTER_OP_EXT_IN_RX_DEST_MC 0x2
/* enum: loop back to TXDP 0 */
-#define MC_CMD_FILTER_OP_EXT_IN_RX_DEST_TX0 0x3
+#define MC_CMD_FILTER_OP_EXT_IN_RX_DEST_TX0 0x3
/* enum: loop back to TXDP 1 */
-#define MC_CMD_FILTER_OP_EXT_IN_RX_DEST_TX1 0x4
+#define MC_CMD_FILTER_OP_EXT_IN_RX_DEST_TX1 0x4
/* receive queue handle (for multiple queue modes, this is the base queue) */
#define MC_CMD_FILTER_OP_EXT_IN_RX_QUEUE_OFST 24
#define MC_CMD_FILTER_OP_EXT_IN_RX_QUEUE_LEN 4
@@ -7987,14 +8281,14 @@
#define MC_CMD_FILTER_OP_EXT_IN_RX_MODE_OFST 28
#define MC_CMD_FILTER_OP_EXT_IN_RX_MODE_LEN 4
/* enum: receive to just the specified queue */
-#define MC_CMD_FILTER_OP_EXT_IN_RX_MODE_SIMPLE 0x0
+#define MC_CMD_FILTER_OP_EXT_IN_RX_MODE_SIMPLE 0x0
/* enum: receive to multiple queues using RSS context */
-#define MC_CMD_FILTER_OP_EXT_IN_RX_MODE_RSS 0x1
+#define MC_CMD_FILTER_OP_EXT_IN_RX_MODE_RSS 0x1
/* enum: receive to multiple queues using .1p mapping */
-#define MC_CMD_FILTER_OP_EXT_IN_RX_MODE_DOT1P_MAPPING 0x2
+#define MC_CMD_FILTER_OP_EXT_IN_RX_MODE_DOT1P_MAPPING 0x2
/* enum: install a filter entry that will never match; for test purposes only
*/
-#define MC_CMD_FILTER_OP_EXT_IN_RX_MODE_TEST_NEVER_MATCH 0x80000000
+#define MC_CMD_FILTER_OP_EXT_IN_RX_MODE_TEST_NEVER_MATCH 0x80000000
/* RSS context (for RX_MODE_RSS) or .1p mapping handle (for
* RX_MODE_DOT1P_MAPPING), as returned by MC_CMD_RSS_CONTEXT_ALLOC or
* MC_CMD_DOT1P_MAPPING_ALLOC.
@@ -8011,7 +8305,7 @@
#define MC_CMD_FILTER_OP_EXT_IN_TX_DEST_OFST 40
#define MC_CMD_FILTER_OP_EXT_IN_TX_DEST_LEN 4
/* enum: request default behaviour (based on filter type) */
-#define MC_CMD_FILTER_OP_EXT_IN_TX_DEST_DEFAULT 0xffffffff
+#define MC_CMD_FILTER_OP_EXT_IN_TX_DEST_DEFAULT 0xffffffff
#define MC_CMD_FILTER_OP_EXT_IN_TX_DEST_MAC_LBN 0
#define MC_CMD_FILTER_OP_EXT_IN_TX_DEST_MAC_WIDTH 1
#define MC_CMD_FILTER_OP_EXT_IN_TX_DEST_PM_LBN 1
@@ -8054,17 +8348,17 @@
#define MC_CMD_FILTER_OP_EXT_IN_VNI_TYPE_LBN 24
#define MC_CMD_FILTER_OP_EXT_IN_VNI_TYPE_WIDTH 8
/* enum: Match VXLAN traffic with this VNI */
-#define MC_CMD_FILTER_OP_EXT_IN_VNI_TYPE_VXLAN 0x0
+#define MC_CMD_FILTER_OP_EXT_IN_VNI_TYPE_VXLAN 0x0
/* enum: Match Geneve traffic with this VNI */
-#define MC_CMD_FILTER_OP_EXT_IN_VNI_TYPE_GENEVE 0x1
+#define MC_CMD_FILTER_OP_EXT_IN_VNI_TYPE_GENEVE 0x1
/* enum: Reserved for experimental development use */
-#define MC_CMD_FILTER_OP_EXT_IN_VNI_TYPE_EXPERIMENTAL 0xfe
+#define MC_CMD_FILTER_OP_EXT_IN_VNI_TYPE_EXPERIMENTAL 0xfe
#define MC_CMD_FILTER_OP_EXT_IN_VSID_VALUE_LBN 0
#define MC_CMD_FILTER_OP_EXT_IN_VSID_VALUE_WIDTH 24
#define MC_CMD_FILTER_OP_EXT_IN_VSID_TYPE_LBN 24
#define MC_CMD_FILTER_OP_EXT_IN_VSID_TYPE_WIDTH 8
/* enum: Match NVGRE traffic with this VSID */
-#define MC_CMD_FILTER_OP_EXT_IN_VSID_TYPE_NVGRE 0x0
+#define MC_CMD_FILTER_OP_EXT_IN_VSID_TYPE_NVGRE 0x0
/* source IP address to match (as bytes in network order; set last 12 bytes to
* 0 for IPv4 address)
*/
@@ -8131,6 +8425,273 @@
#define MC_CMD_FILTER_OP_EXT_IN_IFRM_DST_IP_OFST 156
#define MC_CMD_FILTER_OP_EXT_IN_IFRM_DST_IP_LEN 16
+/* MC_CMD_FILTER_OP_V3_IN msgrequest: FILTER_OP extension to support additional
+ * filter actions for Intel's DPDK (Data Plane Development Kit, dpdk.org) via
+ * its rte_flow API. This extension is only useful with the sfc_efx driver
+ * included as part of DPDK, used in conjunction with the dpdk datapath
+ * firmware variant.
+ */
+#define MC_CMD_FILTER_OP_V3_IN_LEN 180
+/* identifies the type of operation requested */
+#define MC_CMD_FILTER_OP_V3_IN_OP_OFST 0
+#define MC_CMD_FILTER_OP_V3_IN_OP_LEN 4
+/* Enum values, see field(s): */
+/* MC_CMD_FILTER_OP_IN/OP */
+/* filter handle (for remove / unsubscribe operations) */
+#define MC_CMD_FILTER_OP_V3_IN_HANDLE_OFST 4
+#define MC_CMD_FILTER_OP_V3_IN_HANDLE_LEN 8
+#define MC_CMD_FILTER_OP_V3_IN_HANDLE_LO_OFST 4
+#define MC_CMD_FILTER_OP_V3_IN_HANDLE_HI_OFST 8
+/* The port ID associated with the v-adaptor which should contain this filter.
+ */
+#define MC_CMD_FILTER_OP_V3_IN_PORT_ID_OFST 12
+#define MC_CMD_FILTER_OP_V3_IN_PORT_ID_LEN 4
+/* fields to include in match criteria */
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_FIELDS_OFST 16
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_FIELDS_LEN 4
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_SRC_IP_LBN 0
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_SRC_IP_WIDTH 1
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_DST_IP_LBN 1
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_DST_IP_WIDTH 1
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_SRC_MAC_LBN 2
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_SRC_MAC_WIDTH 1
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_SRC_PORT_LBN 3
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_SRC_PORT_WIDTH 1
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_DST_MAC_LBN 4
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_DST_MAC_WIDTH 1
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_DST_PORT_LBN 5
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_DST_PORT_WIDTH 1
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_ETHER_TYPE_LBN 6
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_ETHER_TYPE_WIDTH 1
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_INNER_VLAN_LBN 7
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_INNER_VLAN_WIDTH 1
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_OUTER_VLAN_LBN 8
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_OUTER_VLAN_WIDTH 1
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_IP_PROTO_LBN 9
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_IP_PROTO_WIDTH 1
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_FWDEF0_LBN 10
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_FWDEF0_WIDTH 1
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_VNI_OR_VSID_LBN 11
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_VNI_OR_VSID_WIDTH 1
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_IFRM_SRC_IP_LBN 12
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_IFRM_SRC_IP_WIDTH 1
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_IFRM_DST_IP_LBN 13
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_IFRM_DST_IP_WIDTH 1
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_IFRM_SRC_MAC_LBN 14
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_IFRM_SRC_MAC_WIDTH 1
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_IFRM_SRC_PORT_LBN 15
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_IFRM_SRC_PORT_WIDTH 1
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_IFRM_DST_MAC_LBN 16
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_IFRM_DST_MAC_WIDTH 1
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_IFRM_DST_PORT_LBN 17
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_IFRM_DST_PORT_WIDTH 1
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_IFRM_ETHER_TYPE_LBN 18
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_IFRM_ETHER_TYPE_WIDTH 1
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_IFRM_INNER_VLAN_LBN 19
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_IFRM_INNER_VLAN_WIDTH 1
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_IFRM_OUTER_VLAN_LBN 20
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_IFRM_OUTER_VLAN_WIDTH 1
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_IFRM_IP_PROTO_LBN 21
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_IFRM_IP_PROTO_WIDTH 1
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_IFRM_FWDEF0_LBN 22
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_IFRM_FWDEF0_WIDTH 1
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_IFRM_FWDEF1_LBN 23
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_IFRM_FWDEF1_WIDTH 1
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_IFRM_UNKNOWN_MCAST_DST_LBN 24
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_IFRM_UNKNOWN_MCAST_DST_WIDTH 1
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_IFRM_UNKNOWN_UCAST_DST_LBN 25
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_IFRM_UNKNOWN_UCAST_DST_WIDTH 1
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_UNKNOWN_MCAST_DST_LBN 30
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_UNKNOWN_MCAST_DST_WIDTH 1
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_UNKNOWN_UCAST_DST_LBN 31
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_UNKNOWN_UCAST_DST_WIDTH 1
+/* receive destination */
+#define MC_CMD_FILTER_OP_V3_IN_RX_DEST_OFST 20
+#define MC_CMD_FILTER_OP_V3_IN_RX_DEST_LEN 4
+/* enum: drop packets */
+#define MC_CMD_FILTER_OP_V3_IN_RX_DEST_DROP 0x0
+/* enum: receive to host */
+#define MC_CMD_FILTER_OP_V3_IN_RX_DEST_HOST 0x1
+/* enum: receive to MC */
+#define MC_CMD_FILTER_OP_V3_IN_RX_DEST_MC 0x2
+/* enum: loop back to TXDP 0 */
+#define MC_CMD_FILTER_OP_V3_IN_RX_DEST_TX0 0x3
+/* enum: loop back to TXDP 1 */
+#define MC_CMD_FILTER_OP_V3_IN_RX_DEST_TX1 0x4
+/* receive queue handle (for multiple queue modes, this is the base queue) */
+#define MC_CMD_FILTER_OP_V3_IN_RX_QUEUE_OFST 24
+#define MC_CMD_FILTER_OP_V3_IN_RX_QUEUE_LEN 4
+/* receive mode */
+#define MC_CMD_FILTER_OP_V3_IN_RX_MODE_OFST 28
+#define MC_CMD_FILTER_OP_V3_IN_RX_MODE_LEN 4
+/* enum: receive to just the specified queue */
+#define MC_CMD_FILTER_OP_V3_IN_RX_MODE_SIMPLE 0x0
+/* enum: receive to multiple queues using RSS context */
+#define MC_CMD_FILTER_OP_V3_IN_RX_MODE_RSS 0x1
+/* enum: receive to multiple queues using .1p mapping */
+#define MC_CMD_FILTER_OP_V3_IN_RX_MODE_DOT1P_MAPPING 0x2
+/* enum: install a filter entry that will never match; for test purposes only
+ */
+#define MC_CMD_FILTER_OP_V3_IN_RX_MODE_TEST_NEVER_MATCH 0x80000000
+/* RSS context (for RX_MODE_RSS) or .1p mapping handle (for
+ * RX_MODE_DOT1P_MAPPING), as returned by MC_CMD_RSS_CONTEXT_ALLOC or
+ * MC_CMD_DOT1P_MAPPING_ALLOC.
+ */
+#define MC_CMD_FILTER_OP_V3_IN_RX_CONTEXT_OFST 32
+#define MC_CMD_FILTER_OP_V3_IN_RX_CONTEXT_LEN 4
+/* transmit domain (reserved; set to 0) */
+#define MC_CMD_FILTER_OP_V3_IN_TX_DOMAIN_OFST 36
+#define MC_CMD_FILTER_OP_V3_IN_TX_DOMAIN_LEN 4
+/* transmit destination (either set the MAC and/or PM bits for explicit
+ * control, or set this field to TX_DEST_DEFAULT for sensible default
+ * behaviour)
+ */
+#define MC_CMD_FILTER_OP_V3_IN_TX_DEST_OFST 40
+#define MC_CMD_FILTER_OP_V3_IN_TX_DEST_LEN 4
+/* enum: request default behaviour (based on filter type) */
+#define MC_CMD_FILTER_OP_V3_IN_TX_DEST_DEFAULT 0xffffffff
+#define MC_CMD_FILTER_OP_V3_IN_TX_DEST_MAC_LBN 0
+#define MC_CMD_FILTER_OP_V3_IN_TX_DEST_MAC_WIDTH 1
+#define MC_CMD_FILTER_OP_V3_IN_TX_DEST_PM_LBN 1
+#define MC_CMD_FILTER_OP_V3_IN_TX_DEST_PM_WIDTH 1
+/* source MAC address to match (as bytes in network order) */
+#define MC_CMD_FILTER_OP_V3_IN_SRC_MAC_OFST 44
+#define MC_CMD_FILTER_OP_V3_IN_SRC_MAC_LEN 6
+/* source port to match (as bytes in network order) */
+#define MC_CMD_FILTER_OP_V3_IN_SRC_PORT_OFST 50
+#define MC_CMD_FILTER_OP_V3_IN_SRC_PORT_LEN 2
+/* destination MAC address to match (as bytes in network order) */
+#define MC_CMD_FILTER_OP_V3_IN_DST_MAC_OFST 52
+#define MC_CMD_FILTER_OP_V3_IN_DST_MAC_LEN 6
+/* destination port to match (as bytes in network order) */
+#define MC_CMD_FILTER_OP_V3_IN_DST_PORT_OFST 58
+#define MC_CMD_FILTER_OP_V3_IN_DST_PORT_LEN 2
+/* Ethernet type to match (as bytes in network order) */
+#define MC_CMD_FILTER_OP_V3_IN_ETHER_TYPE_OFST 60
+#define MC_CMD_FILTER_OP_V3_IN_ETHER_TYPE_LEN 2
+/* Inner VLAN tag to match (as bytes in network order) */
+#define MC_CMD_FILTER_OP_V3_IN_INNER_VLAN_OFST 62
+#define MC_CMD_FILTER_OP_V3_IN_INNER_VLAN_LEN 2
+/* Outer VLAN tag to match (as bytes in network order) */
+#define MC_CMD_FILTER_OP_V3_IN_OUTER_VLAN_OFST 64
+#define MC_CMD_FILTER_OP_V3_IN_OUTER_VLAN_LEN 2
+/* IP protocol to match (in low byte; set high byte to 0) */
+#define MC_CMD_FILTER_OP_V3_IN_IP_PROTO_OFST 66
+#define MC_CMD_FILTER_OP_V3_IN_IP_PROTO_LEN 2
+/* Firmware defined register 0 to match (reserved; set to 0) */
+#define MC_CMD_FILTER_OP_V3_IN_FWDEF0_OFST 68
+#define MC_CMD_FILTER_OP_V3_IN_FWDEF0_LEN 4
+/* VNI (for VXLAN/Geneve, when IP protocol is UDP) or VSID (for NVGRE, when IP
+ * protocol is GRE) to match (as bytes in network order; set last byte to 0 for
+ * VXLAN/NVGRE, or 1 for Geneve)
+ */
+#define MC_CMD_FILTER_OP_V3_IN_VNI_OR_VSID_OFST 72
+#define MC_CMD_FILTER_OP_V3_IN_VNI_OR_VSID_LEN 4
+#define MC_CMD_FILTER_OP_V3_IN_VNI_VALUE_LBN 0
+#define MC_CMD_FILTER_OP_V3_IN_VNI_VALUE_WIDTH 24
+#define MC_CMD_FILTER_OP_V3_IN_VNI_TYPE_LBN 24
+#define MC_CMD_FILTER_OP_V3_IN_VNI_TYPE_WIDTH 8
+/* enum: Match VXLAN traffic with this VNI */
+#define MC_CMD_FILTER_OP_V3_IN_VNI_TYPE_VXLAN 0x0
+/* enum: Match Geneve traffic with this VNI */
+#define MC_CMD_FILTER_OP_V3_IN_VNI_TYPE_GENEVE 0x1
+/* enum: Reserved for experimental development use */
+#define MC_CMD_FILTER_OP_V3_IN_VNI_TYPE_EXPERIMENTAL 0xfe
+#define MC_CMD_FILTER_OP_V3_IN_VSID_VALUE_LBN 0
+#define MC_CMD_FILTER_OP_V3_IN_VSID_VALUE_WIDTH 24
+#define MC_CMD_FILTER_OP_V3_IN_VSID_TYPE_LBN 24
+#define MC_CMD_FILTER_OP_V3_IN_VSID_TYPE_WIDTH 8
+/* enum: Match NVGRE traffic with this VSID */
+#define MC_CMD_FILTER_OP_V3_IN_VSID_TYPE_NVGRE 0x0
+/* source IP address to match (as bytes in network order; set last 12 bytes to
+ * 0 for IPv4 address)
+ */
+#define MC_CMD_FILTER_OP_V3_IN_SRC_IP_OFST 76
+#define MC_CMD_FILTER_OP_V3_IN_SRC_IP_LEN 16
+/* destination IP address to match (as bytes in network order; set last 12
+ * bytes to 0 for IPv4 address)
+ */
+#define MC_CMD_FILTER_OP_V3_IN_DST_IP_OFST 92
+#define MC_CMD_FILTER_OP_V3_IN_DST_IP_LEN 16
+/* VXLAN/NVGRE inner frame source MAC address to match (as bytes in network
+ * order)
+ */
+#define MC_CMD_FILTER_OP_V3_IN_IFRM_SRC_MAC_OFST 108
+#define MC_CMD_FILTER_OP_V3_IN_IFRM_SRC_MAC_LEN 6
+/* VXLAN/NVGRE inner frame source port to match (as bytes in network order) */
+#define MC_CMD_FILTER_OP_V3_IN_IFRM_SRC_PORT_OFST 114
+#define MC_CMD_FILTER_OP_V3_IN_IFRM_SRC_PORT_LEN 2
+/* VXLAN/NVGRE inner frame destination MAC address to match (as bytes in
+ * network order)
+ */
+#define MC_CMD_FILTER_OP_V3_IN_IFRM_DST_MAC_OFST 116
+#define MC_CMD_FILTER_OP_V3_IN_IFRM_DST_MAC_LEN 6
+/* VXLAN/NVGRE inner frame destination port to match (as bytes in network
+ * order)
+ */
+#define MC_CMD_FILTER_OP_V3_IN_IFRM_DST_PORT_OFST 122
+#define MC_CMD_FILTER_OP_V3_IN_IFRM_DST_PORT_LEN 2
+/* VXLAN/NVGRE inner frame Ethernet type to match (as bytes in network order)
+ */
+#define MC_CMD_FILTER_OP_V3_IN_IFRM_ETHER_TYPE_OFST 124
+#define MC_CMD_FILTER_OP_V3_IN_IFRM_ETHER_TYPE_LEN 2
+/* VXLAN/NVGRE inner frame Inner VLAN tag to match (as bytes in network order)
+ */
+#define MC_CMD_FILTER_OP_V3_IN_IFRM_INNER_VLAN_OFST 126
+#define MC_CMD_FILTER_OP_V3_IN_IFRM_INNER_VLAN_LEN 2
+/* VXLAN/NVGRE inner frame Outer VLAN tag to match (as bytes in network order)
+ */
+#define MC_CMD_FILTER_OP_V3_IN_IFRM_OUTER_VLAN_OFST 128
+#define MC_CMD_FILTER_OP_V3_IN_IFRM_OUTER_VLAN_LEN 2
+/* VXLAN/NVGRE inner frame IP protocol to match (in low byte; set high byte to
+ * 0)
+ */
+#define MC_CMD_FILTER_OP_V3_IN_IFRM_IP_PROTO_OFST 130
+#define MC_CMD_FILTER_OP_V3_IN_IFRM_IP_PROTO_LEN 2
+/* VXLAN/NVGRE inner frame Firmware defined register 0 to match (reserved; set
+ * to 0)
+ */
+#define MC_CMD_FILTER_OP_V3_IN_IFRM_FWDEF0_OFST 132
+#define MC_CMD_FILTER_OP_V3_IN_IFRM_FWDEF0_LEN 4
+/* VXLAN/NVGRE inner frame Firmware defined register 1 to match (reserved; set
+ * to 0)
+ */
+#define MC_CMD_FILTER_OP_V3_IN_IFRM_FWDEF1_OFST 136
+#define MC_CMD_FILTER_OP_V3_IN_IFRM_FWDEF1_LEN 4
+/* VXLAN/NVGRE inner frame source IP address to match (as bytes in network
+ * order; set last 12 bytes to 0 for IPv4 address)
+ */
+#define MC_CMD_FILTER_OP_V3_IN_IFRM_SRC_IP_OFST 140
+#define MC_CMD_FILTER_OP_V3_IN_IFRM_SRC_IP_LEN 16
+/* VXLAN/NVGRE inner frame destination IP address to match (as bytes in network
+ * order; set last 12 bytes to 0 for IPv4 address)
+ */
+#define MC_CMD_FILTER_OP_V3_IN_IFRM_DST_IP_OFST 156
+#define MC_CMD_FILTER_OP_V3_IN_IFRM_DST_IP_LEN 16
+/* Set an action for all packets matching this filter. The DPDK driver and dpdk
+ * f/w variant use their own specific delivery structures, which are documented
+ * in the DPDK Firmware Driver Interface (SF-119419-TC). Requesting anything
+ * other than MATCH_ACTION_NONE when the NIC is running another f/w variant
+ * will cause the filter insertion to fail with ENOTSUP.
+ */
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_ACTION_OFST 172
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_ACTION_LEN 4
+/* enum: do nothing extra */
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_ACTION_NONE 0x0
+/* enum: Set the match flag in the packet prefix for packets matching the
+ * filter (only with dpdk firmware, otherwise fails with ENOTSUP). Used to
+ * support the DPDK rte_flow "FLAG" action.
+ */
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_ACTION_FLAG 0x1
+/* enum: Insert MATCH_MARK_VALUE into the packet prefix for packets matching
+ * the filter (only with dpdk firmware, otherwise fails with ENOTSUP). Used to
+ * support the DPDK rte_flow "MARK" action.
+ */
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_ACTION_MARK 0x2
+/* the mark value for MATCH_ACTION_MARK */
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_MARK_VALUE_OFST 176
+#define MC_CMD_FILTER_OP_V3_IN_MATCH_MARK_VALUE_LEN 4
+
/* MC_CMD_FILTER_OP_OUT msgresponse */
#define MC_CMD_FILTER_OP_OUT_LEN 12
/* identifies the type of operation requested */
@@ -8147,9 +8708,9 @@
#define MC_CMD_FILTER_OP_OUT_HANDLE_LO_OFST 4
#define MC_CMD_FILTER_OP_OUT_HANDLE_HI_OFST 8
/* enum: guaranteed invalid filter handle (low 32 bits) */
-#define MC_CMD_FILTER_OP_OUT_HANDLE_LO_INVALID 0xffffffff
+#define MC_CMD_FILTER_OP_OUT_HANDLE_LO_INVALID 0xffffffff
/* enum: guaranteed invalid filter handle (high 32 bits) */
-#define MC_CMD_FILTER_OP_OUT_HANDLE_HI_INVALID 0xffffffff
+#define MC_CMD_FILTER_OP_OUT_HANDLE_HI_INVALID 0xffffffff
/* MC_CMD_FILTER_OP_EXT_OUT msgresponse */
#define MC_CMD_FILTER_OP_EXT_OUT_LEN 12
@@ -8184,20 +8745,20 @@
#define MC_CMD_GET_PARSER_DISP_INFO_IN_OP_OFST 0
#define MC_CMD_GET_PARSER_DISP_INFO_IN_OP_LEN 4
/* enum: read the list of supported RX filter matches */
-#define MC_CMD_GET_PARSER_DISP_INFO_IN_OP_GET_SUPPORTED_RX_MATCHES 0x1
+#define MC_CMD_GET_PARSER_DISP_INFO_IN_OP_GET_SUPPORTED_RX_MATCHES 0x1
/* enum: read flags indicating restrictions on filter insertion for the calling
* client
*/
-#define MC_CMD_GET_PARSER_DISP_INFO_IN_OP_GET_RESTRICTIONS 0x2
+#define MC_CMD_GET_PARSER_DISP_INFO_IN_OP_GET_RESTRICTIONS 0x2
/* enum: read properties relating to security rules (Medford-only; for use by
* SolarSecure apps, not directly by drivers. See SF-114946-SW.)
*/
-#define MC_CMD_GET_PARSER_DISP_INFO_IN_OP_GET_SECURITY_RULE_INFO 0x3
+#define MC_CMD_GET_PARSER_DISP_INFO_IN_OP_GET_SECURITY_RULE_INFO 0x3
/* enum: read the list of supported RX filter matches for VXLAN/NVGRE
* encapsulated frames, which follow a different match sequence to normal
* frames (Medford only)
*/
-#define MC_CMD_GET_PARSER_DISP_INFO_IN_OP_GET_SUPPORTED_ENCAP_RX_MATCHES 0x4
+#define MC_CMD_GET_PARSER_DISP_INFO_IN_OP_GET_SUPPORTED_ENCAP_RX_MATCHES 0x4
/* MC_CMD_GET_PARSER_DISP_INFO_OUT msgresponse */
#define MC_CMD_GET_PARSER_DISP_INFO_OUT_LENMIN 8
@@ -8238,7 +8799,9 @@
* Direct read/write of parser-dispatcher state (DICPUs and LUE) for debugging.
* Please note that this interface is only of use to debug tools which have
* knowledge of firmware and hardware data structures; nothing here is intended
- * for use by normal driver code.
+ * for use by normal driver code. Note that although this command is in the
+ * Admin privilege group, in tamperproof adapters, only read operations are
+ * permitted.
*/
#define MC_CMD_PARSER_DISP_RW 0xe5
@@ -8250,32 +8813,36 @@
#define MC_CMD_PARSER_DISP_RW_IN_TARGET_OFST 0
#define MC_CMD_PARSER_DISP_RW_IN_TARGET_LEN 4
/* enum: RX dispatcher CPU */
-#define MC_CMD_PARSER_DISP_RW_IN_RX_DICPU 0x0
+#define MC_CMD_PARSER_DISP_RW_IN_RX_DICPU 0x0
/* enum: TX dispatcher CPU */
-#define MC_CMD_PARSER_DISP_RW_IN_TX_DICPU 0x1
+#define MC_CMD_PARSER_DISP_RW_IN_TX_DICPU 0x1
/* enum: Lookup engine (with original metadata format). Deprecated; used only
* by cmdclient as a fallback for very old Huntington firmware, and not
* supported in firmware beyond v6.4.0.1005. Use LUE_VERSIONED_METADATA
* instead.
*/
-#define MC_CMD_PARSER_DISP_RW_IN_LUE 0x2
+#define MC_CMD_PARSER_DISP_RW_IN_LUE 0x2
/* enum: Lookup engine (with requested metadata format) */
-#define MC_CMD_PARSER_DISP_RW_IN_LUE_VERSIONED_METADATA 0x3
+#define MC_CMD_PARSER_DISP_RW_IN_LUE_VERSIONED_METADATA 0x3
/* enum: RX0 dispatcher CPU (alias for RX_DICPU; Medford has 2 RX DICPUs) */
-#define MC_CMD_PARSER_DISP_RW_IN_RX0_DICPU 0x0
+#define MC_CMD_PARSER_DISP_RW_IN_RX0_DICPU 0x0
/* enum: RX1 dispatcher CPU (only valid for Medford) */
-#define MC_CMD_PARSER_DISP_RW_IN_RX1_DICPU 0x4
+#define MC_CMD_PARSER_DISP_RW_IN_RX1_DICPU 0x4
/* enum: Miscellaneous other state (only valid for Medford) */
-#define MC_CMD_PARSER_DISP_RW_IN_MISC_STATE 0x5
+#define MC_CMD_PARSER_DISP_RW_IN_MISC_STATE 0x5
/* identifies the type of operation requested */
#define MC_CMD_PARSER_DISP_RW_IN_OP_OFST 4
#define MC_CMD_PARSER_DISP_RW_IN_OP_LEN 4
/* enum: Read a word of DICPU DMEM or a LUE entry */
-#define MC_CMD_PARSER_DISP_RW_IN_READ 0x0
-/* enum: Write a word of DICPU DMEM or a LUE entry. */
-#define MC_CMD_PARSER_DISP_RW_IN_WRITE 0x1
-/* enum: Read-modify-write a word of DICPU DMEM (not valid for LUE). */
-#define MC_CMD_PARSER_DISP_RW_IN_RMW 0x2
+#define MC_CMD_PARSER_DISP_RW_IN_READ 0x0
+/* enum: Write a word of DICPU DMEM or a LUE entry. Not permitted on
+ * tamperproof adapters.
+ */
+#define MC_CMD_PARSER_DISP_RW_IN_WRITE 0x1
+/* enum: Read-modify-write a word of DICPU DMEM (not valid for LUE). Not
+ * permitted on tamperproof adapters.
+ */
+#define MC_CMD_PARSER_DISP_RW_IN_RMW 0x2
/* data memory address (DICPU targets) or LUE index (LUE targets) */
#define MC_CMD_PARSER_DISP_RW_IN_ADDRESS_OFST 8
#define MC_CMD_PARSER_DISP_RW_IN_ADDRESS_LEN 4
@@ -8283,7 +8850,7 @@
#define MC_CMD_PARSER_DISP_RW_IN_SELECTOR_OFST 8
#define MC_CMD_PARSER_DISP_RW_IN_SELECTOR_LEN 4
/* enum: Port to datapath mapping */
-#define MC_CMD_PARSER_DISP_RW_IN_PORT_DP_MAPPING 0x1
+#define MC_CMD_PARSER_DISP_RW_IN_PORT_DP_MAPPING 0x1
/* value to write (for DMEM writes) */
#define MC_CMD_PARSER_DISP_RW_IN_DMEM_WRITE_VALUE_OFST 12
#define MC_CMD_PARSER_DISP_RW_IN_DMEM_WRITE_VALUE_LEN 4
@@ -8317,8 +8884,8 @@
#define MC_CMD_PARSER_DISP_RW_OUT_PORT_DP_MAPPING_OFST 0
#define MC_CMD_PARSER_DISP_RW_OUT_PORT_DP_MAPPING_LEN 4
#define MC_CMD_PARSER_DISP_RW_OUT_PORT_DP_MAPPING_NUM 4
-#define MC_CMD_PARSER_DISP_RW_OUT_DP0 0x1 /* enum */
-#define MC_CMD_PARSER_DISP_RW_OUT_DP1 0x2 /* enum */
+#define MC_CMD_PARSER_DISP_RW_OUT_DP0 0x1 /* enum */
+#define MC_CMD_PARSER_DISP_RW_OUT_DP1 0x2 /* enum */
/***********************************/
@@ -8783,13 +9350,13 @@
#define MC_CMD_GET_TLP_PROCESSING_GLOBALS_IN_TLP_GLOBAL_CATEGORY_OFST 0
#define MC_CMD_GET_TLP_PROCESSING_GLOBALS_IN_TLP_GLOBAL_CATEGORY_LEN 4
/* enum: MISC. */
-#define MC_CMD_GET_TLP_PROCESSING_GLOBALS_IN_TLP_GLOBAL_CATEGORY_MISC 0x0
+#define MC_CMD_GET_TLP_PROCESSING_GLOBALS_IN_TLP_GLOBAL_CATEGORY_MISC 0x0
/* enum: IDO. */
-#define MC_CMD_GET_TLP_PROCESSING_GLOBALS_IN_TLP_GLOBAL_CATEGORY_IDO 0x1
+#define MC_CMD_GET_TLP_PROCESSING_GLOBALS_IN_TLP_GLOBAL_CATEGORY_IDO 0x1
/* enum: RO. */
-#define MC_CMD_GET_TLP_PROCESSING_GLOBALS_IN_TLP_GLOBAL_CATEGORY_RO 0x2
+#define MC_CMD_GET_TLP_PROCESSING_GLOBALS_IN_TLP_GLOBAL_CATEGORY_RO 0x2
/* enum: TPH Type. */
-#define MC_CMD_GET_TLP_PROCESSING_GLOBALS_IN_TLP_GLOBAL_CATEGORY_TPH_TYPE 0x3
+#define MC_CMD_GET_TLP_PROCESSING_GLOBALS_IN_TLP_GLOBAL_CATEGORY_TPH_TYPE 0x3
/* MC_CMD_GET_TLP_PROCESSING_GLOBALS_OUT msgresponse */
#define MC_CMD_GET_TLP_PROCESSING_GLOBALS_OUT_LEN 8
@@ -8920,57 +9487,57 @@
*/
#define MC_CMD_SATELLITE_DOWNLOAD_IN_PHASE_OFST 0
#define MC_CMD_SATELLITE_DOWNLOAD_IN_PHASE_LEN 4
-#define MC_CMD_SATELLITE_DOWNLOAD_IN_PHASE_IDLE 0x0 /* enum */
-#define MC_CMD_SATELLITE_DOWNLOAD_IN_PHASE_RESET 0x1 /* enum */
-#define MC_CMD_SATELLITE_DOWNLOAD_IN_PHASE_IMEMS 0x2 /* enum */
-#define MC_CMD_SATELLITE_DOWNLOAD_IN_PHASE_VECTORS 0x3 /* enum */
-#define MC_CMD_SATELLITE_DOWNLOAD_IN_PHASE_READY 0x4 /* enum */
+#define MC_CMD_SATELLITE_DOWNLOAD_IN_PHASE_IDLE 0x0 /* enum */
+#define MC_CMD_SATELLITE_DOWNLOAD_IN_PHASE_RESET 0x1 /* enum */
+#define MC_CMD_SATELLITE_DOWNLOAD_IN_PHASE_IMEMS 0x2 /* enum */
+#define MC_CMD_SATELLITE_DOWNLOAD_IN_PHASE_VECTORS 0x3 /* enum */
+#define MC_CMD_SATELLITE_DOWNLOAD_IN_PHASE_READY 0x4 /* enum */
/* Target for download. (These match the blob numbers defined in
* mc_flash_layout.h.)
*/
#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_OFST 4
#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_LEN 4
/* enum: Valid in phase 2 (PHASE_IMEMS) only */
-#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_TXDI_TEXT 0x0
+#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_TXDI_TEXT 0x0
/* enum: Valid in phase 2 (PHASE_IMEMS) only */
-#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_RXDI_TEXT 0x1
+#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_RXDI_TEXT 0x1
/* enum: Valid in phase 2 (PHASE_IMEMS) only */
-#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_TXDP_TEXT 0x2
+#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_TXDP_TEXT 0x2
/* enum: Valid in phase 2 (PHASE_IMEMS) only */
-#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_RXDP_TEXT 0x3
+#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_RXDP_TEXT 0x3
/* enum: Valid in phase 2 (PHASE_IMEMS) only */
-#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_RXHRSL_HR_LUT 0x4
+#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_RXHRSL_HR_LUT 0x4
/* enum: Valid in phase 2 (PHASE_IMEMS) only */
-#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_RXHRSL_HR_LUT_CFG 0x5
+#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_RXHRSL_HR_LUT_CFG 0x5
/* enum: Valid in phase 2 (PHASE_IMEMS) only */
-#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_TXHRSL_HR_LUT 0x6
+#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_TXHRSL_HR_LUT 0x6
/* enum: Valid in phase 2 (PHASE_IMEMS) only */
-#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_TXHRSL_HR_LUT_CFG 0x7
+#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_TXHRSL_HR_LUT_CFG 0x7
/* enum: Valid in phase 2 (PHASE_IMEMS) only */
-#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_RXHRSL_HR_PGM 0x8
+#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_RXHRSL_HR_PGM 0x8
/* enum: Valid in phase 2 (PHASE_IMEMS) only */
-#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_RXHRSL_SL_PGM 0x9
+#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_RXHRSL_SL_PGM 0x9
/* enum: Valid in phase 2 (PHASE_IMEMS) only */
-#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_TXHRSL_HR_PGM 0xa
+#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_TXHRSL_HR_PGM 0xa
/* enum: Valid in phase 2 (PHASE_IMEMS) only */
-#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_TXHRSL_SL_PGM 0xb
+#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_TXHRSL_SL_PGM 0xb
/* enum: Valid in phase 3 (PHASE_VECTORS) only */
-#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_RXDI_VTBL0 0xc
+#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_RXDI_VTBL0 0xc
/* enum: Valid in phase 3 (PHASE_VECTORS) only */
-#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_TXDI_VTBL0 0xd
+#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_TXDI_VTBL0 0xd
/* enum: Valid in phase 3 (PHASE_VECTORS) only */
-#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_RXDI_VTBL1 0xe
+#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_RXDI_VTBL1 0xe
/* enum: Valid in phase 3 (PHASE_VECTORS) only */
-#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_TXDI_VTBL1 0xf
+#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_TXDI_VTBL1 0xf
/* enum: Valid in phases 1 (PHASE_RESET) and 4 (PHASE_READY) only */
-#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_ALL 0xffffffff
+#define MC_CMD_SATELLITE_DOWNLOAD_IN_TARGET_ALL 0xffffffff
/* Chunk ID, or CHUNK_ID_LAST or CHUNK_ID_ABORT */
#define MC_CMD_SATELLITE_DOWNLOAD_IN_CHUNK_ID_OFST 8
#define MC_CMD_SATELLITE_DOWNLOAD_IN_CHUNK_ID_LEN 4
/* enum: Last chunk, containing checksum rather than data */
-#define MC_CMD_SATELLITE_DOWNLOAD_IN_CHUNK_ID_LAST 0xffffffff
+#define MC_CMD_SATELLITE_DOWNLOAD_IN_CHUNK_ID_LAST 0xffffffff
/* enum: Abort download of this item */
-#define MC_CMD_SATELLITE_DOWNLOAD_IN_CHUNK_ID_ABORT 0xfffffffe
+#define MC_CMD_SATELLITE_DOWNLOAD_IN_CHUNK_ID_ABORT 0xfffffffe
/* Length of this chunk in bytes */
#define MC_CMD_SATELLITE_DOWNLOAD_IN_CHUNK_LEN_OFST 12
#define MC_CMD_SATELLITE_DOWNLOAD_IN_CHUNK_LEN_LEN 4
@@ -8989,21 +9556,21 @@
#define MC_CMD_SATELLITE_DOWNLOAD_OUT_INFO_OFST 4
#define MC_CMD_SATELLITE_DOWNLOAD_OUT_INFO_LEN 4
/* enum: Code download OK, completed. */
-#define MC_CMD_SATELLITE_DOWNLOAD_OUT_OK_COMPLETE 0x0
+#define MC_CMD_SATELLITE_DOWNLOAD_OUT_OK_COMPLETE 0x0
/* enum: Code download aborted as requested. */
-#define MC_CMD_SATELLITE_DOWNLOAD_OUT_OK_ABORTED 0x1
+#define MC_CMD_SATELLITE_DOWNLOAD_OUT_OK_ABORTED 0x1
/* enum: Code download OK so far, send next chunk. */
-#define MC_CMD_SATELLITE_DOWNLOAD_OUT_OK_NEXT_CHUNK 0x2
+#define MC_CMD_SATELLITE_DOWNLOAD_OUT_OK_NEXT_CHUNK 0x2
/* enum: Download phases out of sequence */
-#define MC_CMD_SATELLITE_DOWNLOAD_OUT_ERR_BAD_PHASE 0x100
+#define MC_CMD_SATELLITE_DOWNLOAD_OUT_ERR_BAD_PHASE 0x100
/* enum: Bad target for this phase */
-#define MC_CMD_SATELLITE_DOWNLOAD_OUT_ERR_BAD_TARGET 0x101
+#define MC_CMD_SATELLITE_DOWNLOAD_OUT_ERR_BAD_TARGET 0x101
/* enum: Chunk ID out of sequence */
-#define MC_CMD_SATELLITE_DOWNLOAD_OUT_ERR_BAD_CHUNK_ID 0x200
+#define MC_CMD_SATELLITE_DOWNLOAD_OUT_ERR_BAD_CHUNK_ID 0x200
/* enum: Chunk length zero or too large */
-#define MC_CMD_SATELLITE_DOWNLOAD_OUT_ERR_BAD_CHUNK_LEN 0x201
+#define MC_CMD_SATELLITE_DOWNLOAD_OUT_ERR_BAD_CHUNK_LEN 0x201
/* enum: Checksum was incorrect */
-#define MC_CMD_SATELLITE_DOWNLOAD_OUT_ERR_BAD_CHECKSUM 0x300
+#define MC_CMD_SATELLITE_DOWNLOAD_OUT_ERR_BAD_CHECKSUM 0x300
/***********************************/
@@ -9087,54 +9654,58 @@
#define MC_CMD_GET_CAPABILITIES_OUT_RX_DPCPU_FW_ID_OFST 4
#define MC_CMD_GET_CAPABILITIES_OUT_RX_DPCPU_FW_ID_LEN 2
/* enum: Standard RXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_OUT_RXDP 0x0
+#define MC_CMD_GET_CAPABILITIES_OUT_RXDP 0x0
/* enum: Low latency RXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_OUT_RXDP_LOW_LATENCY 0x1
+#define MC_CMD_GET_CAPABILITIES_OUT_RXDP_LOW_LATENCY 0x1
/* enum: Packed stream RXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_OUT_RXDP_PACKED_STREAM 0x2
+#define MC_CMD_GET_CAPABILITIES_OUT_RXDP_PACKED_STREAM 0x2
/* enum: Rules engine RXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_OUT_RXDP_RULES_ENGINE 0x5
+#define MC_CMD_GET_CAPABILITIES_OUT_RXDP_RULES_ENGINE 0x5
+/* enum: DPDK RXDP firmware */
+#define MC_CMD_GET_CAPABILITIES_OUT_RXDP_DPDK 0x6
/* enum: BIST RXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_OUT_RXDP_BIST 0x10a
+#define MC_CMD_GET_CAPABILITIES_OUT_RXDP_BIST 0x10a
/* enum: RXDP Test firmware image 1 */
-#define MC_CMD_GET_CAPABILITIES_OUT_RXDP_TEST_FW_TO_MC_CUT_THROUGH 0x101
+#define MC_CMD_GET_CAPABILITIES_OUT_RXDP_TEST_FW_TO_MC_CUT_THROUGH 0x101
/* enum: RXDP Test firmware image 2 */
-#define MC_CMD_GET_CAPABILITIES_OUT_RXDP_TEST_FW_TO_MC_STORE_FORWARD 0x102
+#define MC_CMD_GET_CAPABILITIES_OUT_RXDP_TEST_FW_TO_MC_STORE_FORWARD 0x102
/* enum: RXDP Test firmware image 3 */
-#define MC_CMD_GET_CAPABILITIES_OUT_RXDP_TEST_FW_TO_MC_STORE_FORWARD_FIRST 0x103
+#define MC_CMD_GET_CAPABILITIES_OUT_RXDP_TEST_FW_TO_MC_STORE_FORWARD_FIRST 0x103
/* enum: RXDP Test firmware image 4 */
-#define MC_CMD_GET_CAPABILITIES_OUT_RXDP_TEST_EVERY_EVENT_BATCHABLE 0x104
+#define MC_CMD_GET_CAPABILITIES_OUT_RXDP_TEST_EVERY_EVENT_BATCHABLE 0x104
/* enum: RXDP Test firmware image 5 */
-#define MC_CMD_GET_CAPABILITIES_OUT_RXDP_TEST_BACKPRESSURE 0x105
+#define MC_CMD_GET_CAPABILITIES_OUT_RXDP_TEST_BACKPRESSURE 0x105
/* enum: RXDP Test firmware image 6 */
-#define MC_CMD_GET_CAPABILITIES_OUT_RXDP_TEST_FW_PACKET_EDITS 0x106
+#define MC_CMD_GET_CAPABILITIES_OUT_RXDP_TEST_FW_PACKET_EDITS 0x106
/* enum: RXDP Test firmware image 7 */
-#define MC_CMD_GET_CAPABILITIES_OUT_RXDP_TEST_FW_RX_HDR_SPLIT 0x107
+#define MC_CMD_GET_CAPABILITIES_OUT_RXDP_TEST_FW_RX_HDR_SPLIT 0x107
/* enum: RXDP Test firmware image 8 */
-#define MC_CMD_GET_CAPABILITIES_OUT_RXDP_TEST_FW_DISABLE_DL 0x108
+#define MC_CMD_GET_CAPABILITIES_OUT_RXDP_TEST_FW_DISABLE_DL 0x108
/* enum: RXDP Test firmware image 9 */
-#define MC_CMD_GET_CAPABILITIES_OUT_RXDP_TEST_FW_DOORBELL_DELAY 0x10b
+#define MC_CMD_GET_CAPABILITIES_OUT_RXDP_TEST_FW_DOORBELL_DELAY 0x10b
/* enum: RXDP Test firmware image 10 */
-#define MC_CMD_GET_CAPABILITIES_OUT_RXDP_TEST_FW_SLOW 0x10c
+#define MC_CMD_GET_CAPABILITIES_OUT_RXDP_TEST_FW_SLOW 0x10c
/* TxDPCPU firmware id. */
#define MC_CMD_GET_CAPABILITIES_OUT_TX_DPCPU_FW_ID_OFST 6
#define MC_CMD_GET_CAPABILITIES_OUT_TX_DPCPU_FW_ID_LEN 2
/* enum: Standard TXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_OUT_TXDP 0x0
+#define MC_CMD_GET_CAPABILITIES_OUT_TXDP 0x0
/* enum: Low latency TXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_OUT_TXDP_LOW_LATENCY 0x1
+#define MC_CMD_GET_CAPABILITIES_OUT_TXDP_LOW_LATENCY 0x1
/* enum: High packet rate TXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_OUT_TXDP_HIGH_PACKET_RATE 0x3
+#define MC_CMD_GET_CAPABILITIES_OUT_TXDP_HIGH_PACKET_RATE 0x3
/* enum: Rules engine TXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_OUT_TXDP_RULES_ENGINE 0x5
+#define MC_CMD_GET_CAPABILITIES_OUT_TXDP_RULES_ENGINE 0x5
+/* enum: DPDK TXDP firmware */
+#define MC_CMD_GET_CAPABILITIES_OUT_TXDP_DPDK 0x6
/* enum: BIST TXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_OUT_TXDP_BIST 0x12d
+#define MC_CMD_GET_CAPABILITIES_OUT_TXDP_BIST 0x12d
/* enum: TXDP Test firmware image 1 */
-#define MC_CMD_GET_CAPABILITIES_OUT_TXDP_TEST_FW_TSO_EDIT 0x101
+#define MC_CMD_GET_CAPABILITIES_OUT_TXDP_TEST_FW_TSO_EDIT 0x101
/* enum: TXDP Test firmware image 2 */
-#define MC_CMD_GET_CAPABILITIES_OUT_TXDP_TEST_FW_PACKET_EDITS 0x102
+#define MC_CMD_GET_CAPABILITIES_OUT_TXDP_TEST_FW_PACKET_EDITS 0x102
/* enum: TXDP CSR bus test firmware */
-#define MC_CMD_GET_CAPABILITIES_OUT_TXDP_TEST_FW_CSR 0x103
+#define MC_CMD_GET_CAPABILITIES_OUT_TXDP_TEST_FW_CSR 0x103
#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_VERSION_OFST 8
#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_VERSION_LEN 2
#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_VERSION_REV_LBN 0
@@ -9144,41 +9715,43 @@
/* enum: reserved value - do not use (may indicate alternative interpretation
* of REV field in future)
*/
-#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_TYPE_RESERVED 0x0
+#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_TYPE_RESERVED 0x0
/* enum: Trivial RX PD firmware for early Huntington development (Huntington
* development only)
*/
-#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_TYPE_FIRST_PKT 0x1
+#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_TYPE_FIRST_PKT 0x1
/* enum: RX PD firmware with approximately Siena-compatible behaviour
* (Huntington development only)
*/
-#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_TYPE_SIENA_COMPAT 0x2
+#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_TYPE_SIENA_COMPAT 0x2
/* enum: Full featured RX PD production firmware */
-#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_TYPE_FULL_FEATURED 0x3
+#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_TYPE_FULL_FEATURED 0x3
/* enum: (deprecated original name for the FULL_FEATURED variant) */
-#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_TYPE_VSWITCH 0x3
+#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_TYPE_VSWITCH 0x3
/* enum: siena_compat variant RX PD firmware using PM rather than MAC
* (Huntington development only)
*/
-#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_TYPE_SIENA_COMPAT_PM 0x4
+#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_TYPE_SIENA_COMPAT_PM 0x4
/* enum: Low latency RX PD production firmware */
-#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_TYPE_LOW_LATENCY 0x5
+#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_TYPE_LOW_LATENCY 0x5
/* enum: Packed stream RX PD production firmware */
-#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_TYPE_PACKED_STREAM 0x6
+#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_TYPE_PACKED_STREAM 0x6
/* enum: RX PD firmware handling layer 2 only for high packet rate performance
* tests (Medford development only)
*/
-#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_TYPE_LAYER2_PERF 0x7
+#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_TYPE_LAYER2_PERF 0x7
/* enum: Rules engine RX PD production firmware */
-#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_TYPE_RULES_ENGINE 0x8
-/* enum: reserved value - do not use (bug69716) */
-#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_TYPE_RESERVED_9 0x9
+#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_TYPE_RULES_ENGINE 0x8
+/* enum: Custom firmware variant (see SF-119495-PD and bug69716) */
+#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_TYPE_L3XUDP 0x9
+/* enum: DPDK RX PD production firmware */
+#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_TYPE_DPDK 0xa
/* enum: RX PD firmware for GUE parsing prototype (Medford development only) */
-#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_TYPE_TESTFW_GUE_PROTOTYPE 0xe
+#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_TYPE_TESTFW_GUE_PROTOTYPE 0xe
/* enum: RX PD firmware parsing but not filtering network overlay tunnel
* encapsulations (Medford development only)
*/
-#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_TYPE_TESTFW_ENCAP_PARSING_ONLY 0xf
+#define MC_CMD_GET_CAPABILITIES_OUT_RXPD_FW_TYPE_TESTFW_ENCAP_PARSING_ONLY 0xf
#define MC_CMD_GET_CAPABILITIES_OUT_TXPD_FW_VERSION_OFST 10
#define MC_CMD_GET_CAPABILITIES_OUT_TXPD_FW_VERSION_LEN 2
#define MC_CMD_GET_CAPABILITIES_OUT_TXPD_FW_VERSION_REV_LBN 0
@@ -9188,34 +9761,36 @@
/* enum: reserved value - do not use (may indicate alternative interpretation
* of REV field in future)
*/
-#define MC_CMD_GET_CAPABILITIES_OUT_TXPD_FW_TYPE_RESERVED 0x0
+#define MC_CMD_GET_CAPABILITIES_OUT_TXPD_FW_TYPE_RESERVED 0x0
/* enum: Trivial TX PD firmware for early Huntington development (Huntington
* development only)
*/
-#define MC_CMD_GET_CAPABILITIES_OUT_TXPD_FW_TYPE_FIRST_PKT 0x1
+#define MC_CMD_GET_CAPABILITIES_OUT_TXPD_FW_TYPE_FIRST_PKT 0x1
/* enum: TX PD firmware with approximately Siena-compatible behaviour
* (Huntington development only)
*/
-#define MC_CMD_GET_CAPABILITIES_OUT_TXPD_FW_TYPE_SIENA_COMPAT 0x2
+#define MC_CMD_GET_CAPABILITIES_OUT_TXPD_FW_TYPE_SIENA_COMPAT 0x2
/* enum: Full featured TX PD production firmware */
-#define MC_CMD_GET_CAPABILITIES_OUT_TXPD_FW_TYPE_FULL_FEATURED 0x3
+#define MC_CMD_GET_CAPABILITIES_OUT_TXPD_FW_TYPE_FULL_FEATURED 0x3
/* enum: (deprecated original name for the FULL_FEATURED variant) */
-#define MC_CMD_GET_CAPABILITIES_OUT_TXPD_FW_TYPE_VSWITCH 0x3
+#define MC_CMD_GET_CAPABILITIES_OUT_TXPD_FW_TYPE_VSWITCH 0x3
/* enum: siena_compat variant TX PD firmware using PM rather than MAC
* (Huntington development only)
*/
-#define MC_CMD_GET_CAPABILITIES_OUT_TXPD_FW_TYPE_SIENA_COMPAT_PM 0x4
-#define MC_CMD_GET_CAPABILITIES_OUT_TXPD_FW_TYPE_LOW_LATENCY 0x5 /* enum */
+#define MC_CMD_GET_CAPABILITIES_OUT_TXPD_FW_TYPE_SIENA_COMPAT_PM 0x4
+#define MC_CMD_GET_CAPABILITIES_OUT_TXPD_FW_TYPE_LOW_LATENCY 0x5 /* enum */
/* enum: TX PD firmware handling layer 2 only for high packet rate performance
* tests (Medford development only)
*/
-#define MC_CMD_GET_CAPABILITIES_OUT_TXPD_FW_TYPE_LAYER2_PERF 0x7
+#define MC_CMD_GET_CAPABILITIES_OUT_TXPD_FW_TYPE_LAYER2_PERF 0x7
/* enum: Rules engine TX PD production firmware */
-#define MC_CMD_GET_CAPABILITIES_OUT_TXPD_FW_TYPE_RULES_ENGINE 0x8
-/* enum: reserved value - do not use (bug69716) */
-#define MC_CMD_GET_CAPABILITIES_OUT_TXPD_FW_TYPE_RESERVED_9 0x9
+#define MC_CMD_GET_CAPABILITIES_OUT_TXPD_FW_TYPE_RULES_ENGINE 0x8
+/* enum: Custom firmware variant (see SF-119495-PD and bug69716) */
+#define MC_CMD_GET_CAPABILITIES_OUT_TXPD_FW_TYPE_L3XUDP 0x9
+/* enum: DPDK TX PD production firmware */
+#define MC_CMD_GET_CAPABILITIES_OUT_TXPD_FW_TYPE_DPDK 0xa
/* enum: RX PD firmware for GUE parsing prototype (Medford development only) */
-#define MC_CMD_GET_CAPABILITIES_OUT_TXPD_FW_TYPE_TESTFW_GUE_PROTOTYPE 0xe
+#define MC_CMD_GET_CAPABILITIES_OUT_TXPD_FW_TYPE_TESTFW_GUE_PROTOTYPE 0xe
/* Hardware capabilities of NIC */
#define MC_CMD_GET_CAPABILITIES_OUT_HW_CAPABILITIES_OFST 12
#define MC_CMD_GET_CAPABILITIES_OUT_HW_CAPABILITIES_LEN 4
@@ -9293,54 +9868,58 @@
#define MC_CMD_GET_CAPABILITIES_V2_OUT_RX_DPCPU_FW_ID_OFST 4
#define MC_CMD_GET_CAPABILITIES_V2_OUT_RX_DPCPU_FW_ID_LEN 2
/* enum: Standard RXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP 0x0
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP 0x0
/* enum: Low latency RXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP_LOW_LATENCY 0x1
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP_LOW_LATENCY 0x1
/* enum: Packed stream RXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP_PACKED_STREAM 0x2
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP_PACKED_STREAM 0x2
/* enum: Rules engine RXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP_RULES_ENGINE 0x5
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP_RULES_ENGINE 0x5
+/* enum: DPDK RXDP firmware */
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP_DPDK 0x6
/* enum: BIST RXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP_BIST 0x10a
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP_BIST 0x10a
/* enum: RXDP Test firmware image 1 */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP_TEST_FW_TO_MC_CUT_THROUGH 0x101
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP_TEST_FW_TO_MC_CUT_THROUGH 0x101
/* enum: RXDP Test firmware image 2 */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP_TEST_FW_TO_MC_STORE_FORWARD 0x102
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP_TEST_FW_TO_MC_STORE_FORWARD 0x102
/* enum: RXDP Test firmware image 3 */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP_TEST_FW_TO_MC_STORE_FORWARD_FIRST 0x103
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP_TEST_FW_TO_MC_STORE_FORWARD_FIRST 0x103
/* enum: RXDP Test firmware image 4 */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP_TEST_EVERY_EVENT_BATCHABLE 0x104
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP_TEST_EVERY_EVENT_BATCHABLE 0x104
/* enum: RXDP Test firmware image 5 */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP_TEST_BACKPRESSURE 0x105
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP_TEST_BACKPRESSURE 0x105
/* enum: RXDP Test firmware image 6 */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP_TEST_FW_PACKET_EDITS 0x106
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP_TEST_FW_PACKET_EDITS 0x106
/* enum: RXDP Test firmware image 7 */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP_TEST_FW_RX_HDR_SPLIT 0x107
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP_TEST_FW_RX_HDR_SPLIT 0x107
/* enum: RXDP Test firmware image 8 */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP_TEST_FW_DISABLE_DL 0x108
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP_TEST_FW_DISABLE_DL 0x108
/* enum: RXDP Test firmware image 9 */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP_TEST_FW_DOORBELL_DELAY 0x10b
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP_TEST_FW_DOORBELL_DELAY 0x10b
/* enum: RXDP Test firmware image 10 */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP_TEST_FW_SLOW 0x10c
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXDP_TEST_FW_SLOW 0x10c
/* TxDPCPU firmware id. */
#define MC_CMD_GET_CAPABILITIES_V2_OUT_TX_DPCPU_FW_ID_OFST 6
#define MC_CMD_GET_CAPABILITIES_V2_OUT_TX_DPCPU_FW_ID_LEN 2
/* enum: Standard TXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXDP 0x0
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXDP 0x0
/* enum: Low latency TXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXDP_LOW_LATENCY 0x1
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXDP_LOW_LATENCY 0x1
/* enum: High packet rate TXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXDP_HIGH_PACKET_RATE 0x3
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXDP_HIGH_PACKET_RATE 0x3
/* enum: Rules engine TXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXDP_RULES_ENGINE 0x5
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXDP_RULES_ENGINE 0x5
+/* enum: DPDK TXDP firmware */
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXDP_DPDK 0x6
/* enum: BIST TXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXDP_BIST 0x12d
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXDP_BIST 0x12d
/* enum: TXDP Test firmware image 1 */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXDP_TEST_FW_TSO_EDIT 0x101
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXDP_TEST_FW_TSO_EDIT 0x101
/* enum: TXDP Test firmware image 2 */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXDP_TEST_FW_PACKET_EDITS 0x102
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXDP_TEST_FW_PACKET_EDITS 0x102
/* enum: TXDP CSR bus test firmware */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXDP_TEST_FW_CSR 0x103
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXDP_TEST_FW_CSR 0x103
#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_VERSION_OFST 8
#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_VERSION_LEN 2
#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_VERSION_REV_LBN 0
@@ -9350,41 +9929,43 @@
/* enum: reserved value - do not use (may indicate alternative interpretation
* of REV field in future)
*/
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_TYPE_RESERVED 0x0
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_TYPE_RESERVED 0x0
/* enum: Trivial RX PD firmware for early Huntington development (Huntington
* development only)
*/
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_TYPE_FIRST_PKT 0x1
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_TYPE_FIRST_PKT 0x1
/* enum: RX PD firmware with approximately Siena-compatible behaviour
* (Huntington development only)
*/
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_TYPE_SIENA_COMPAT 0x2
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_TYPE_SIENA_COMPAT 0x2
/* enum: Full featured RX PD production firmware */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_TYPE_FULL_FEATURED 0x3
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_TYPE_FULL_FEATURED 0x3
/* enum: (deprecated original name for the FULL_FEATURED variant) */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_TYPE_VSWITCH 0x3
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_TYPE_VSWITCH 0x3
/* enum: siena_compat variant RX PD firmware using PM rather than MAC
* (Huntington development only)
*/
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_TYPE_SIENA_COMPAT_PM 0x4
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_TYPE_SIENA_COMPAT_PM 0x4
/* enum: Low latency RX PD production firmware */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_TYPE_LOW_LATENCY 0x5
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_TYPE_LOW_LATENCY 0x5
/* enum: Packed stream RX PD production firmware */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_TYPE_PACKED_STREAM 0x6
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_TYPE_PACKED_STREAM 0x6
/* enum: RX PD firmware handling layer 2 only for high packet rate performance
* tests (Medford development only)
*/
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_TYPE_LAYER2_PERF 0x7
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_TYPE_LAYER2_PERF 0x7
/* enum: Rules engine RX PD production firmware */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_TYPE_RULES_ENGINE 0x8
-/* enum: reserved value - do not use (bug69716) */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_TYPE_RESERVED_9 0x9
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_TYPE_RULES_ENGINE 0x8
+/* enum: Custom firmware variant (see SF-119495-PD and bug69716) */
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_TYPE_L3XUDP 0x9
+/* enum: DPDK RX PD production firmware */
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_TYPE_DPDK 0xa
/* enum: RX PD firmware for GUE parsing prototype (Medford development only) */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_TYPE_TESTFW_GUE_PROTOTYPE 0xe
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_TYPE_TESTFW_GUE_PROTOTYPE 0xe
/* enum: RX PD firmware parsing but not filtering network overlay tunnel
* encapsulations (Medford development only)
*/
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_TYPE_TESTFW_ENCAP_PARSING_ONLY 0xf
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_RXPD_FW_TYPE_TESTFW_ENCAP_PARSING_ONLY 0xf
#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXPD_FW_VERSION_OFST 10
#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXPD_FW_VERSION_LEN 2
#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXPD_FW_VERSION_REV_LBN 0
@@ -9394,34 +9975,36 @@
/* enum: reserved value - do not use (may indicate alternative interpretation
* of REV field in future)
*/
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXPD_FW_TYPE_RESERVED 0x0
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXPD_FW_TYPE_RESERVED 0x0
/* enum: Trivial TX PD firmware for early Huntington development (Huntington
* development only)
*/
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXPD_FW_TYPE_FIRST_PKT 0x1
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXPD_FW_TYPE_FIRST_PKT 0x1
/* enum: TX PD firmware with approximately Siena-compatible behaviour
* (Huntington development only)
*/
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXPD_FW_TYPE_SIENA_COMPAT 0x2
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXPD_FW_TYPE_SIENA_COMPAT 0x2
/* enum: Full featured TX PD production firmware */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXPD_FW_TYPE_FULL_FEATURED 0x3
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXPD_FW_TYPE_FULL_FEATURED 0x3
/* enum: (deprecated original name for the FULL_FEATURED variant) */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXPD_FW_TYPE_VSWITCH 0x3
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXPD_FW_TYPE_VSWITCH 0x3
/* enum: siena_compat variant TX PD firmware using PM rather than MAC
* (Huntington development only)
*/
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXPD_FW_TYPE_SIENA_COMPAT_PM 0x4
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXPD_FW_TYPE_LOW_LATENCY 0x5 /* enum */
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXPD_FW_TYPE_SIENA_COMPAT_PM 0x4
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXPD_FW_TYPE_LOW_LATENCY 0x5 /* enum */
/* enum: TX PD firmware handling layer 2 only for high packet rate performance
* tests (Medford development only)
*/
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXPD_FW_TYPE_LAYER2_PERF 0x7
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXPD_FW_TYPE_LAYER2_PERF 0x7
/* enum: Rules engine TX PD production firmware */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXPD_FW_TYPE_RULES_ENGINE 0x8
-/* enum: reserved value - do not use (bug69716) */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXPD_FW_TYPE_RESERVED_9 0x9
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXPD_FW_TYPE_RULES_ENGINE 0x8
+/* enum: Custom firmware variant (see SF-119495-PD and bug69716) */
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXPD_FW_TYPE_L3XUDP 0x9
+/* enum: DPDK TX PD production firmware */
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXPD_FW_TYPE_DPDK 0xa
/* enum: RX PD firmware for GUE parsing prototype (Medford development only) */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXPD_FW_TYPE_TESTFW_GUE_PROTOTYPE 0xe
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_TXPD_FW_TYPE_TESTFW_GUE_PROTOTYPE 0xe
/* Hardware capabilities of NIC */
#define MC_CMD_GET_CAPABILITIES_V2_OUT_HW_CAPABILITIES_OFST 12
#define MC_CMD_GET_CAPABILITIES_V2_OUT_HW_CAPABILITIES_LEN 4
@@ -9469,6 +10052,18 @@
#define MC_CMD_GET_CAPABILITIES_V2_OUT_TSA_BOUND_WIDTH 1
#define MC_CMD_GET_CAPABILITIES_V2_OUT_SF_ADAPTER_AUTHENTICATION_LBN 18
#define MC_CMD_GET_CAPABILITIES_V2_OUT_SF_ADAPTER_AUTHENTICATION_WIDTH 1
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_FILTER_ACTION_FLAG_LBN 19
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_FILTER_ACTION_FLAG_WIDTH 1
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_FILTER_ACTION_MARK_LBN 20
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_FILTER_ACTION_MARK_WIDTH 1
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_EQUAL_STRIDE_PACKED_STREAM_LBN 21
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_EQUAL_STRIDE_PACKED_STREAM_WIDTH 1
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_L3XUDP_SUPPORT_LBN 22
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_L3XUDP_SUPPORT_WIDTH 1
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_FW_SUBVARIANT_NO_TX_CSUM_LBN 23
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_FW_SUBVARIANT_NO_TX_CSUM_WIDTH 1
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_VI_SPREADING_LBN 24
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_VI_SPREADING_WIDTH 1
/* Number of FATSOv2 contexts per datapath supported by this NIC. Not present
* on older firmware (check the length).
*/
@@ -9482,18 +10077,18 @@
#define MC_CMD_GET_CAPABILITIES_V2_OUT_PFS_TO_PORTS_ASSIGNMENT_LEN 1
#define MC_CMD_GET_CAPABILITIES_V2_OUT_PFS_TO_PORTS_ASSIGNMENT_NUM 16
/* enum: The caller is not permitted to access information on this PF. */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_ACCESS_NOT_PERMITTED 0xff
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_ACCESS_NOT_PERMITTED 0xff
/* enum: PF does not exist. */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_PF_NOT_PRESENT 0xfe
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_PF_NOT_PRESENT 0xfe
/* enum: PF does exist but is not assigned to any external port. */
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_PF_NOT_ASSIGNED 0xfd
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_PF_NOT_ASSIGNED 0xfd
/* enum: This value indicates that PF is assigned, but it cannot be expressed
* in this field. It is intended for a possible future situation where a more
* complex scheme of PFs to ports mapping is being used. The future driver
* should look for a new field supporting the new scheme. The current/old
* driver should treat this value as PF_NOT_ASSIGNED.
*/
-#define MC_CMD_GET_CAPABILITIES_V2_OUT_INCOMPATIBLE_ASSIGNMENT 0xfc
+#define MC_CMD_GET_CAPABILITIES_V2_OUT_INCOMPATIBLE_ASSIGNMENT 0xfc
/* One byte per PF containing the number of its VFs, indexed by PF number. A
* special value indicates that a PF is not present.
*/
@@ -9501,9 +10096,9 @@
#define MC_CMD_GET_CAPABILITIES_V2_OUT_NUM_VFS_PER_PF_LEN 1
#define MC_CMD_GET_CAPABILITIES_V2_OUT_NUM_VFS_PER_PF_NUM 16
/* enum: The caller is not permitted to access information on this PF. */
-/* MC_CMD_GET_CAPABILITIES_V2_OUT_ACCESS_NOT_PERMITTED 0xff */
+/* MC_CMD_GET_CAPABILITIES_V2_OUT_ACCESS_NOT_PERMITTED 0xff */
/* enum: PF does not exist. */
-/* MC_CMD_GET_CAPABILITIES_V2_OUT_PF_NOT_PRESENT 0xfe */
+/* MC_CMD_GET_CAPABILITIES_V2_OUT_PF_NOT_PRESENT 0xfe */
/* Number of VIs available for each external port */
#define MC_CMD_GET_CAPABILITIES_V2_OUT_NUM_VIS_PER_PORT_OFST 58
#define MC_CMD_GET_CAPABILITIES_V2_OUT_NUM_VIS_PER_PORT_LEN 2
@@ -9592,54 +10187,58 @@
#define MC_CMD_GET_CAPABILITIES_V3_OUT_RX_DPCPU_FW_ID_OFST 4
#define MC_CMD_GET_CAPABILITIES_V3_OUT_RX_DPCPU_FW_ID_LEN 2
/* enum: Standard RXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP 0x0
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP 0x0
/* enum: Low latency RXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP_LOW_LATENCY 0x1
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP_LOW_LATENCY 0x1
/* enum: Packed stream RXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP_PACKED_STREAM 0x2
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP_PACKED_STREAM 0x2
/* enum: Rules engine RXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP_RULES_ENGINE 0x5
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP_RULES_ENGINE 0x5
+/* enum: DPDK RXDP firmware */
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP_DPDK 0x6
/* enum: BIST RXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP_BIST 0x10a
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP_BIST 0x10a
/* enum: RXDP Test firmware image 1 */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP_TEST_FW_TO_MC_CUT_THROUGH 0x101
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP_TEST_FW_TO_MC_CUT_THROUGH 0x101
/* enum: RXDP Test firmware image 2 */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP_TEST_FW_TO_MC_STORE_FORWARD 0x102
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP_TEST_FW_TO_MC_STORE_FORWARD 0x102
/* enum: RXDP Test firmware image 3 */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP_TEST_FW_TO_MC_STORE_FORWARD_FIRST 0x103
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP_TEST_FW_TO_MC_STORE_FORWARD_FIRST 0x103
/* enum: RXDP Test firmware image 4 */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP_TEST_EVERY_EVENT_BATCHABLE 0x104
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP_TEST_EVERY_EVENT_BATCHABLE 0x104
/* enum: RXDP Test firmware image 5 */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP_TEST_BACKPRESSURE 0x105
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP_TEST_BACKPRESSURE 0x105
/* enum: RXDP Test firmware image 6 */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP_TEST_FW_PACKET_EDITS 0x106
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP_TEST_FW_PACKET_EDITS 0x106
/* enum: RXDP Test firmware image 7 */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP_TEST_FW_RX_HDR_SPLIT 0x107
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP_TEST_FW_RX_HDR_SPLIT 0x107
/* enum: RXDP Test firmware image 8 */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP_TEST_FW_DISABLE_DL 0x108
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP_TEST_FW_DISABLE_DL 0x108
/* enum: RXDP Test firmware image 9 */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP_TEST_FW_DOORBELL_DELAY 0x10b
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP_TEST_FW_DOORBELL_DELAY 0x10b
/* enum: RXDP Test firmware image 10 */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP_TEST_FW_SLOW 0x10c
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXDP_TEST_FW_SLOW 0x10c
/* TxDPCPU firmware id. */
#define MC_CMD_GET_CAPABILITIES_V3_OUT_TX_DPCPU_FW_ID_OFST 6
#define MC_CMD_GET_CAPABILITIES_V3_OUT_TX_DPCPU_FW_ID_LEN 2
/* enum: Standard TXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXDP 0x0
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXDP 0x0
/* enum: Low latency TXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXDP_LOW_LATENCY 0x1
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXDP_LOW_LATENCY 0x1
/* enum: High packet rate TXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXDP_HIGH_PACKET_RATE 0x3
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXDP_HIGH_PACKET_RATE 0x3
/* enum: Rules engine TXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXDP_RULES_ENGINE 0x5
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXDP_RULES_ENGINE 0x5
+/* enum: DPDK TXDP firmware */
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXDP_DPDK 0x6
/* enum: BIST TXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXDP_BIST 0x12d
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXDP_BIST 0x12d
/* enum: TXDP Test firmware image 1 */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXDP_TEST_FW_TSO_EDIT 0x101
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXDP_TEST_FW_TSO_EDIT 0x101
/* enum: TXDP Test firmware image 2 */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXDP_TEST_FW_PACKET_EDITS 0x102
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXDP_TEST_FW_PACKET_EDITS 0x102
/* enum: TXDP CSR bus test firmware */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXDP_TEST_FW_CSR 0x103
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXDP_TEST_FW_CSR 0x103
#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXPD_FW_VERSION_OFST 8
#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXPD_FW_VERSION_LEN 2
#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXPD_FW_VERSION_REV_LBN 0
@@ -9649,41 +10248,43 @@
/* enum: reserved value - do not use (may indicate alternative interpretation
* of REV field in future)
*/
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXPD_FW_TYPE_RESERVED 0x0
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXPD_FW_TYPE_RESERVED 0x0
/* enum: Trivial RX PD firmware for early Huntington development (Huntington
* development only)
*/
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXPD_FW_TYPE_FIRST_PKT 0x1
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXPD_FW_TYPE_FIRST_PKT 0x1
/* enum: RX PD firmware with approximately Siena-compatible behaviour
* (Huntington development only)
*/
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXPD_FW_TYPE_SIENA_COMPAT 0x2
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXPD_FW_TYPE_SIENA_COMPAT 0x2
/* enum: Full featured RX PD production firmware */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXPD_FW_TYPE_FULL_FEATURED 0x3
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXPD_FW_TYPE_FULL_FEATURED 0x3
/* enum: (deprecated original name for the FULL_FEATURED variant) */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXPD_FW_TYPE_VSWITCH 0x3
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXPD_FW_TYPE_VSWITCH 0x3
/* enum: siena_compat variant RX PD firmware using PM rather than MAC
* (Huntington development only)
*/
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXPD_FW_TYPE_SIENA_COMPAT_PM 0x4
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXPD_FW_TYPE_SIENA_COMPAT_PM 0x4
/* enum: Low latency RX PD production firmware */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXPD_FW_TYPE_LOW_LATENCY 0x5
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXPD_FW_TYPE_LOW_LATENCY 0x5
/* enum: Packed stream RX PD production firmware */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXPD_FW_TYPE_PACKED_STREAM 0x6
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXPD_FW_TYPE_PACKED_STREAM 0x6
/* enum: RX PD firmware handling layer 2 only for high packet rate performance
* tests (Medford development only)
*/
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXPD_FW_TYPE_LAYER2_PERF 0x7
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXPD_FW_TYPE_LAYER2_PERF 0x7
/* enum: Rules engine RX PD production firmware */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXPD_FW_TYPE_RULES_ENGINE 0x8
-/* enum: reserved value - do not use (bug69716) */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXPD_FW_TYPE_RESERVED_9 0x9
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXPD_FW_TYPE_RULES_ENGINE 0x8
+/* enum: Custom firmware variant (see SF-119495-PD and bug69716) */
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXPD_FW_TYPE_L3XUDP 0x9
+/* enum: DPDK RX PD production firmware */
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXPD_FW_TYPE_DPDK 0xa
/* enum: RX PD firmware for GUE parsing prototype (Medford development only) */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXPD_FW_TYPE_TESTFW_GUE_PROTOTYPE 0xe
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXPD_FW_TYPE_TESTFW_GUE_PROTOTYPE 0xe
/* enum: RX PD firmware parsing but not filtering network overlay tunnel
* encapsulations (Medford development only)
*/
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXPD_FW_TYPE_TESTFW_ENCAP_PARSING_ONLY 0xf
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_RXPD_FW_TYPE_TESTFW_ENCAP_PARSING_ONLY 0xf
#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXPD_FW_VERSION_OFST 10
#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXPD_FW_VERSION_LEN 2
#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXPD_FW_VERSION_REV_LBN 0
@@ -9693,34 +10294,36 @@
/* enum: reserved value - do not use (may indicate alternative interpretation
* of REV field in future)
*/
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXPD_FW_TYPE_RESERVED 0x0
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXPD_FW_TYPE_RESERVED 0x0
/* enum: Trivial TX PD firmware for early Huntington development (Huntington
* development only)
*/
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXPD_FW_TYPE_FIRST_PKT 0x1
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXPD_FW_TYPE_FIRST_PKT 0x1
/* enum: TX PD firmware with approximately Siena-compatible behaviour
* (Huntington development only)
*/
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXPD_FW_TYPE_SIENA_COMPAT 0x2
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXPD_FW_TYPE_SIENA_COMPAT 0x2
/* enum: Full featured TX PD production firmware */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXPD_FW_TYPE_FULL_FEATURED 0x3
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXPD_FW_TYPE_FULL_FEATURED 0x3
/* enum: (deprecated original name for the FULL_FEATURED variant) */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXPD_FW_TYPE_VSWITCH 0x3
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXPD_FW_TYPE_VSWITCH 0x3
/* enum: siena_compat variant TX PD firmware using PM rather than MAC
* (Huntington development only)
*/
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXPD_FW_TYPE_SIENA_COMPAT_PM 0x4
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXPD_FW_TYPE_LOW_LATENCY 0x5 /* enum */
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXPD_FW_TYPE_SIENA_COMPAT_PM 0x4
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXPD_FW_TYPE_LOW_LATENCY 0x5 /* enum */
/* enum: TX PD firmware handling layer 2 only for high packet rate performance
* tests (Medford development only)
*/
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXPD_FW_TYPE_LAYER2_PERF 0x7
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXPD_FW_TYPE_LAYER2_PERF 0x7
/* enum: Rules engine TX PD production firmware */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXPD_FW_TYPE_RULES_ENGINE 0x8
-/* enum: reserved value - do not use (bug69716) */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXPD_FW_TYPE_RESERVED_9 0x9
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXPD_FW_TYPE_RULES_ENGINE 0x8
+/* enum: Custom firmware variant (see SF-119495-PD and bug69716) */
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXPD_FW_TYPE_L3XUDP 0x9
+/* enum: DPDK TX PD production firmware */
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXPD_FW_TYPE_DPDK 0xa
/* enum: RX PD firmware for GUE parsing prototype (Medford development only) */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXPD_FW_TYPE_TESTFW_GUE_PROTOTYPE 0xe
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_TXPD_FW_TYPE_TESTFW_GUE_PROTOTYPE 0xe
/* Hardware capabilities of NIC */
#define MC_CMD_GET_CAPABILITIES_V3_OUT_HW_CAPABILITIES_OFST 12
#define MC_CMD_GET_CAPABILITIES_V3_OUT_HW_CAPABILITIES_LEN 4
@@ -9768,6 +10371,18 @@
#define MC_CMD_GET_CAPABILITIES_V3_OUT_TSA_BOUND_WIDTH 1
#define MC_CMD_GET_CAPABILITIES_V3_OUT_SF_ADAPTER_AUTHENTICATION_LBN 18
#define MC_CMD_GET_CAPABILITIES_V3_OUT_SF_ADAPTER_AUTHENTICATION_WIDTH 1
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_FILTER_ACTION_FLAG_LBN 19
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_FILTER_ACTION_FLAG_WIDTH 1
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_FILTER_ACTION_MARK_LBN 20
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_FILTER_ACTION_MARK_WIDTH 1
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_EQUAL_STRIDE_PACKED_STREAM_LBN 21
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_EQUAL_STRIDE_PACKED_STREAM_WIDTH 1
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_L3XUDP_SUPPORT_LBN 22
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_L3XUDP_SUPPORT_WIDTH 1
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_FW_SUBVARIANT_NO_TX_CSUM_LBN 23
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_FW_SUBVARIANT_NO_TX_CSUM_WIDTH 1
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_VI_SPREADING_LBN 24
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_VI_SPREADING_WIDTH 1
/* Number of FATSOv2 contexts per datapath supported by this NIC. Not present
* on older firmware (check the length).
*/
@@ -9781,18 +10396,18 @@
#define MC_CMD_GET_CAPABILITIES_V3_OUT_PFS_TO_PORTS_ASSIGNMENT_LEN 1
#define MC_CMD_GET_CAPABILITIES_V3_OUT_PFS_TO_PORTS_ASSIGNMENT_NUM 16
/* enum: The caller is not permitted to access information on this PF. */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_ACCESS_NOT_PERMITTED 0xff
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_ACCESS_NOT_PERMITTED 0xff
/* enum: PF does not exist. */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_PF_NOT_PRESENT 0xfe
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_PF_NOT_PRESENT 0xfe
/* enum: PF does exist but is not assigned to any external port. */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_PF_NOT_ASSIGNED 0xfd
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_PF_NOT_ASSIGNED 0xfd
/* enum: This value indicates that PF is assigned, but it cannot be expressed
* in this field. It is intended for a possible future situation where a more
* complex scheme of PFs to ports mapping is being used. The future driver
* should look for a new field supporting the new scheme. The current/old
* driver should treat this value as PF_NOT_ASSIGNED.
*/
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_INCOMPATIBLE_ASSIGNMENT 0xfc
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_INCOMPATIBLE_ASSIGNMENT 0xfc
/* One byte per PF containing the number of its VFs, indexed by PF number. A
* special value indicates that a PF is not present.
*/
@@ -9800,9 +10415,9 @@
#define MC_CMD_GET_CAPABILITIES_V3_OUT_NUM_VFS_PER_PF_LEN 1
#define MC_CMD_GET_CAPABILITIES_V3_OUT_NUM_VFS_PER_PF_NUM 16
/* enum: The caller is not permitted to access information on this PF. */
-/* MC_CMD_GET_CAPABILITIES_V3_OUT_ACCESS_NOT_PERMITTED 0xff */
+/* MC_CMD_GET_CAPABILITIES_V3_OUT_ACCESS_NOT_PERMITTED 0xff */
/* enum: PF does not exist. */
-/* MC_CMD_GET_CAPABILITIES_V3_OUT_PF_NOT_PRESENT 0xfe */
+/* MC_CMD_GET_CAPABILITIES_V3_OUT_PF_NOT_PRESENT 0xfe */
/* Number of VIs available for each external port */
#define MC_CMD_GET_CAPABILITIES_V3_OUT_NUM_VIS_PER_PORT_OFST 58
#define MC_CMD_GET_CAPABILITIES_V3_OUT_NUM_VIS_PER_PORT_LEN 2
@@ -9833,11 +10448,11 @@
/* enum: Each VI occupies 8k as on Huntington and Medford. PIO is at offset 4k.
* CTPIO is not mapped.
*/
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_VI_WINDOW_MODE_8K 0x0
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_VI_WINDOW_MODE_8K 0x0
/* enum: Each VI occupies 16k. PIO is at offset 4k. CTPIO is at offset 12k. */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_VI_WINDOW_MODE_16K 0x1
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_VI_WINDOW_MODE_16K 0x1
/* enum: Each VI occupies 64k. PIO is at offset 4k. CTPIO is at offset 12k. */
-#define MC_CMD_GET_CAPABILITIES_V3_OUT_VI_WINDOW_MODE_64K 0x2
+#define MC_CMD_GET_CAPABILITIES_V3_OUT_VI_WINDOW_MODE_64K 0x2
/* Number of vFIFOs per adapter that can be used for VFIFO Stuffing
* (SF-115995-SW) in the present configuration of firmware and port mode.
*/
@@ -9916,54 +10531,58 @@
#define MC_CMD_GET_CAPABILITIES_V4_OUT_RX_DPCPU_FW_ID_OFST 4
#define MC_CMD_GET_CAPABILITIES_V4_OUT_RX_DPCPU_FW_ID_LEN 2
/* enum: Standard RXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP 0x0
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP 0x0
/* enum: Low latency RXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP_LOW_LATENCY 0x1
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP_LOW_LATENCY 0x1
/* enum: Packed stream RXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP_PACKED_STREAM 0x2
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP_PACKED_STREAM 0x2
/* enum: Rules engine RXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP_RULES_ENGINE 0x5
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP_RULES_ENGINE 0x5
+/* enum: DPDK RXDP firmware */
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP_DPDK 0x6
/* enum: BIST RXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP_BIST 0x10a
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP_BIST 0x10a
/* enum: RXDP Test firmware image 1 */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP_TEST_FW_TO_MC_CUT_THROUGH 0x101
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP_TEST_FW_TO_MC_CUT_THROUGH 0x101
/* enum: RXDP Test firmware image 2 */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP_TEST_FW_TO_MC_STORE_FORWARD 0x102
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP_TEST_FW_TO_MC_STORE_FORWARD 0x102
/* enum: RXDP Test firmware image 3 */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP_TEST_FW_TO_MC_STORE_FORWARD_FIRST 0x103
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP_TEST_FW_TO_MC_STORE_FORWARD_FIRST 0x103
/* enum: RXDP Test firmware image 4 */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP_TEST_EVERY_EVENT_BATCHABLE 0x104
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP_TEST_EVERY_EVENT_BATCHABLE 0x104
/* enum: RXDP Test firmware image 5 */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP_TEST_BACKPRESSURE 0x105
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP_TEST_BACKPRESSURE 0x105
/* enum: RXDP Test firmware image 6 */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP_TEST_FW_PACKET_EDITS 0x106
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP_TEST_FW_PACKET_EDITS 0x106
/* enum: RXDP Test firmware image 7 */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP_TEST_FW_RX_HDR_SPLIT 0x107
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP_TEST_FW_RX_HDR_SPLIT 0x107
/* enum: RXDP Test firmware image 8 */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP_TEST_FW_DISABLE_DL 0x108
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP_TEST_FW_DISABLE_DL 0x108
/* enum: RXDP Test firmware image 9 */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP_TEST_FW_DOORBELL_DELAY 0x10b
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP_TEST_FW_DOORBELL_DELAY 0x10b
/* enum: RXDP Test firmware image 10 */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP_TEST_FW_SLOW 0x10c
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXDP_TEST_FW_SLOW 0x10c
/* TxDPCPU firmware id. */
#define MC_CMD_GET_CAPABILITIES_V4_OUT_TX_DPCPU_FW_ID_OFST 6
#define MC_CMD_GET_CAPABILITIES_V4_OUT_TX_DPCPU_FW_ID_LEN 2
/* enum: Standard TXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXDP 0x0
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXDP 0x0
/* enum: Low latency TXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXDP_LOW_LATENCY 0x1
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXDP_LOW_LATENCY 0x1
/* enum: High packet rate TXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXDP_HIGH_PACKET_RATE 0x3
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXDP_HIGH_PACKET_RATE 0x3
/* enum: Rules engine TXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXDP_RULES_ENGINE 0x5
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXDP_RULES_ENGINE 0x5
+/* enum: DPDK TXDP firmware */
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXDP_DPDK 0x6
/* enum: BIST TXDP firmware */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXDP_BIST 0x12d
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXDP_BIST 0x12d
/* enum: TXDP Test firmware image 1 */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXDP_TEST_FW_TSO_EDIT 0x101
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXDP_TEST_FW_TSO_EDIT 0x101
/* enum: TXDP Test firmware image 2 */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXDP_TEST_FW_PACKET_EDITS 0x102
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXDP_TEST_FW_PACKET_EDITS 0x102
/* enum: TXDP CSR bus test firmware */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXDP_TEST_FW_CSR 0x103
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXDP_TEST_FW_CSR 0x103
#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXPD_FW_VERSION_OFST 8
#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXPD_FW_VERSION_LEN 2
#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXPD_FW_VERSION_REV_LBN 0
@@ -9973,41 +10592,43 @@
/* enum: reserved value - do not use (may indicate alternative interpretation
* of REV field in future)
*/
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXPD_FW_TYPE_RESERVED 0x0
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXPD_FW_TYPE_RESERVED 0x0
/* enum: Trivial RX PD firmware for early Huntington development (Huntington
* development only)
*/
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXPD_FW_TYPE_FIRST_PKT 0x1
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXPD_FW_TYPE_FIRST_PKT 0x1
/* enum: RX PD firmware with approximately Siena-compatible behaviour
* (Huntington development only)
*/
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXPD_FW_TYPE_SIENA_COMPAT 0x2
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXPD_FW_TYPE_SIENA_COMPAT 0x2
/* enum: Full featured RX PD production firmware */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXPD_FW_TYPE_FULL_FEATURED 0x3
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXPD_FW_TYPE_FULL_FEATURED 0x3
/* enum: (deprecated original name for the FULL_FEATURED variant) */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXPD_FW_TYPE_VSWITCH 0x3
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXPD_FW_TYPE_VSWITCH 0x3
/* enum: siena_compat variant RX PD firmware using PM rather than MAC
* (Huntington development only)
*/
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXPD_FW_TYPE_SIENA_COMPAT_PM 0x4
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXPD_FW_TYPE_SIENA_COMPAT_PM 0x4
/* enum: Low latency RX PD production firmware */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXPD_FW_TYPE_LOW_LATENCY 0x5
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXPD_FW_TYPE_LOW_LATENCY 0x5
/* enum: Packed stream RX PD production firmware */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXPD_FW_TYPE_PACKED_STREAM 0x6
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXPD_FW_TYPE_PACKED_STREAM 0x6
/* enum: RX PD firmware handling layer 2 only for high packet rate performance
* tests (Medford development only)
*/
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXPD_FW_TYPE_LAYER2_PERF 0x7
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXPD_FW_TYPE_LAYER2_PERF 0x7
/* enum: Rules engine RX PD production firmware */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXPD_FW_TYPE_RULES_ENGINE 0x8
-/* enum: reserved value - do not use (bug69716) */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXPD_FW_TYPE_RESERVED_9 0x9
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXPD_FW_TYPE_RULES_ENGINE 0x8
+/* enum: Custom firmware variant (see SF-119495-PD and bug69716) */
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXPD_FW_TYPE_L3XUDP 0x9
+/* enum: DPDK RX PD production firmware */
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXPD_FW_TYPE_DPDK 0xa
/* enum: RX PD firmware for GUE parsing prototype (Medford development only) */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXPD_FW_TYPE_TESTFW_GUE_PROTOTYPE 0xe
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXPD_FW_TYPE_TESTFW_GUE_PROTOTYPE 0xe
/* enum: RX PD firmware parsing but not filtering network overlay tunnel
* encapsulations (Medford development only)
*/
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXPD_FW_TYPE_TESTFW_ENCAP_PARSING_ONLY 0xf
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_RXPD_FW_TYPE_TESTFW_ENCAP_PARSING_ONLY 0xf
#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXPD_FW_VERSION_OFST 10
#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXPD_FW_VERSION_LEN 2
#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXPD_FW_VERSION_REV_LBN 0
@@ -10017,34 +10638,36 @@
/* enum: reserved value - do not use (may indicate alternative interpretation
* of REV field in future)
*/
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXPD_FW_TYPE_RESERVED 0x0
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXPD_FW_TYPE_RESERVED 0x0
/* enum: Trivial TX PD firmware for early Huntington development (Huntington
* development only)
*/
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXPD_FW_TYPE_FIRST_PKT 0x1
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXPD_FW_TYPE_FIRST_PKT 0x1
/* enum: TX PD firmware with approximately Siena-compatible behaviour
* (Huntington development only)
*/
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXPD_FW_TYPE_SIENA_COMPAT 0x2
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXPD_FW_TYPE_SIENA_COMPAT 0x2
/* enum: Full featured TX PD production firmware */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXPD_FW_TYPE_FULL_FEATURED 0x3
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXPD_FW_TYPE_FULL_FEATURED 0x3
/* enum: (deprecated original name for the FULL_FEATURED variant) */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXPD_FW_TYPE_VSWITCH 0x3
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXPD_FW_TYPE_VSWITCH 0x3
/* enum: siena_compat variant TX PD firmware using PM rather than MAC
* (Huntington development only)
*/
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXPD_FW_TYPE_SIENA_COMPAT_PM 0x4
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXPD_FW_TYPE_LOW_LATENCY 0x5 /* enum */
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXPD_FW_TYPE_SIENA_COMPAT_PM 0x4
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXPD_FW_TYPE_LOW_LATENCY 0x5 /* enum */
/* enum: TX PD firmware handling layer 2 only for high packet rate performance
* tests (Medford development only)
*/
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXPD_FW_TYPE_LAYER2_PERF 0x7
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXPD_FW_TYPE_LAYER2_PERF 0x7
/* enum: Rules engine TX PD production firmware */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXPD_FW_TYPE_RULES_ENGINE 0x8
-/* enum: reserved value - do not use (bug69716) */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXPD_FW_TYPE_RESERVED_9 0x9
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXPD_FW_TYPE_RULES_ENGINE 0x8
+/* enum: Custom firmware variant (see SF-119495-PD and bug69716) */
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXPD_FW_TYPE_L3XUDP 0x9
+/* enum: DPDK TX PD production firmware */
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXPD_FW_TYPE_DPDK 0xa
/* enum: RX PD firmware for GUE parsing prototype (Medford development only) */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXPD_FW_TYPE_TESTFW_GUE_PROTOTYPE 0xe
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_TXPD_FW_TYPE_TESTFW_GUE_PROTOTYPE 0xe
/* Hardware capabilities of NIC */
#define MC_CMD_GET_CAPABILITIES_V4_OUT_HW_CAPABILITIES_OFST 12
#define MC_CMD_GET_CAPABILITIES_V4_OUT_HW_CAPABILITIES_LEN 4
@@ -10092,6 +10715,18 @@
#define MC_CMD_GET_CAPABILITIES_V4_OUT_TSA_BOUND_WIDTH 1
#define MC_CMD_GET_CAPABILITIES_V4_OUT_SF_ADAPTER_AUTHENTICATION_LBN 18
#define MC_CMD_GET_CAPABILITIES_V4_OUT_SF_ADAPTER_AUTHENTICATION_WIDTH 1
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_FILTER_ACTION_FLAG_LBN 19
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_FILTER_ACTION_FLAG_WIDTH 1
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_FILTER_ACTION_MARK_LBN 20
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_FILTER_ACTION_MARK_WIDTH 1
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_EQUAL_STRIDE_PACKED_STREAM_LBN 21
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_EQUAL_STRIDE_PACKED_STREAM_WIDTH 1
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_L3XUDP_SUPPORT_LBN 22
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_L3XUDP_SUPPORT_WIDTH 1
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_FW_SUBVARIANT_NO_TX_CSUM_LBN 23
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_FW_SUBVARIANT_NO_TX_CSUM_WIDTH 1
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_VI_SPREADING_LBN 24
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_VI_SPREADING_WIDTH 1
/* Number of FATSOv2 contexts per datapath supported by this NIC. Not present
* on older firmware (check the length).
*/
@@ -10105,18 +10740,18 @@
#define MC_CMD_GET_CAPABILITIES_V4_OUT_PFS_TO_PORTS_ASSIGNMENT_LEN 1
#define MC_CMD_GET_CAPABILITIES_V4_OUT_PFS_TO_PORTS_ASSIGNMENT_NUM 16
/* enum: The caller is not permitted to access information on this PF. */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_ACCESS_NOT_PERMITTED 0xff
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_ACCESS_NOT_PERMITTED 0xff
/* enum: PF does not exist. */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_PF_NOT_PRESENT 0xfe
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_PF_NOT_PRESENT 0xfe
/* enum: PF does exist but is not assigned to any external port. */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_PF_NOT_ASSIGNED 0xfd
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_PF_NOT_ASSIGNED 0xfd
/* enum: This value indicates that PF is assigned, but it cannot be expressed
* in this field. It is intended for a possible future situation where a more
* complex scheme of PFs to ports mapping is being used. The future driver
* should look for a new field supporting the new scheme. The current/old
* driver should treat this value as PF_NOT_ASSIGNED.
*/
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_INCOMPATIBLE_ASSIGNMENT 0xfc
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_INCOMPATIBLE_ASSIGNMENT 0xfc
/* One byte per PF containing the number of its VFs, indexed by PF number. A
* special value indicates that a PF is not present.
*/
@@ -10124,9 +10759,9 @@
#define MC_CMD_GET_CAPABILITIES_V4_OUT_NUM_VFS_PER_PF_LEN 1
#define MC_CMD_GET_CAPABILITIES_V4_OUT_NUM_VFS_PER_PF_NUM 16
/* enum: The caller is not permitted to access information on this PF. */
-/* MC_CMD_GET_CAPABILITIES_V4_OUT_ACCESS_NOT_PERMITTED 0xff */
+/* MC_CMD_GET_CAPABILITIES_V4_OUT_ACCESS_NOT_PERMITTED 0xff */
/* enum: PF does not exist. */
-/* MC_CMD_GET_CAPABILITIES_V4_OUT_PF_NOT_PRESENT 0xfe */
+/* MC_CMD_GET_CAPABILITIES_V4_OUT_PF_NOT_PRESENT 0xfe */
/* Number of VIs available for each external port */
#define MC_CMD_GET_CAPABILITIES_V4_OUT_NUM_VIS_PER_PORT_OFST 58
#define MC_CMD_GET_CAPABILITIES_V4_OUT_NUM_VIS_PER_PORT_LEN 2
@@ -10157,11 +10792,11 @@
/* enum: Each VI occupies 8k as on Huntington and Medford. PIO is at offset 4k.
* CTPIO is not mapped.
*/
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_VI_WINDOW_MODE_8K 0x0
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_VI_WINDOW_MODE_8K 0x0
/* enum: Each VI occupies 16k. PIO is at offset 4k. CTPIO is at offset 12k. */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_VI_WINDOW_MODE_16K 0x1
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_VI_WINDOW_MODE_16K 0x1
/* enum: Each VI occupies 64k. PIO is at offset 4k. CTPIO is at offset 12k. */
-#define MC_CMD_GET_CAPABILITIES_V4_OUT_VI_WINDOW_MODE_64K 0x2
+#define MC_CMD_GET_CAPABILITIES_V4_OUT_VI_WINDOW_MODE_64K 0x2
/* Number of vFIFOs per adapter that can be used for VFIFO Stuffing
* (SF-115995-SW) in the present configuration of firmware and port mode.
*/
@@ -10201,7 +10836,16 @@
#define MC_CMD_V2_EXTN_IN_ACTUAL_LEN_LBN 16
#define MC_CMD_V2_EXTN_IN_ACTUAL_LEN_WIDTH 10
#define MC_CMD_V2_EXTN_IN_UNUSED2_LBN 26
-#define MC_CMD_V2_EXTN_IN_UNUSED2_WIDTH 6
+#define MC_CMD_V2_EXTN_IN_UNUSED2_WIDTH 2
+/* Type of command/response */
+#define MC_CMD_V2_EXTN_IN_MESSAGE_TYPE_LBN 28
+#define MC_CMD_V2_EXTN_IN_MESSAGE_TYPE_WIDTH 4
+/* enum: MCDI command directed to or response originating from the MC. */
+#define MC_CMD_V2_EXTN_IN_MCDI_MESSAGE_TYPE_MC 0x0
+/* enum: MCDI command directed to a TSA controller. MCDI responses of this type
+ * are not defined.
+ */
+#define MC_CMD_V2_EXTN_IN_MCDI_MESSAGE_TYPE_TSA 0x1
/***********************************/
@@ -10412,15 +11056,15 @@
#define MC_CMD_VSWITCH_ALLOC_IN_TYPE_OFST 4
#define MC_CMD_VSWITCH_ALLOC_IN_TYPE_LEN 4
/* enum: VLAN */
-#define MC_CMD_VSWITCH_ALLOC_IN_VSWITCH_TYPE_VLAN 0x1
+#define MC_CMD_VSWITCH_ALLOC_IN_VSWITCH_TYPE_VLAN 0x1
/* enum: VEB */
-#define MC_CMD_VSWITCH_ALLOC_IN_VSWITCH_TYPE_VEB 0x2
+#define MC_CMD_VSWITCH_ALLOC_IN_VSWITCH_TYPE_VEB 0x2
/* enum: VEPA (obsolete) */
-#define MC_CMD_VSWITCH_ALLOC_IN_VSWITCH_TYPE_VEPA 0x3
+#define MC_CMD_VSWITCH_ALLOC_IN_VSWITCH_TYPE_VEPA 0x3
/* enum: MUX */
-#define MC_CMD_VSWITCH_ALLOC_IN_VSWITCH_TYPE_MUX 0x4
+#define MC_CMD_VSWITCH_ALLOC_IN_VSWITCH_TYPE_MUX 0x4
/* enum: Snapper specific; semantics TBD */
-#define MC_CMD_VSWITCH_ALLOC_IN_VSWITCH_TYPE_TEST 0x5
+#define MC_CMD_VSWITCH_ALLOC_IN_VSWITCH_TYPE_TEST 0x5
/* Flags controlling v-port creation */
#define MC_CMD_VSWITCH_ALLOC_IN_FLAGS_OFST 8
#define MC_CMD_VSWITCH_ALLOC_IN_FLAGS_LEN 4
@@ -10495,23 +11139,23 @@
#define MC_CMD_VPORT_ALLOC_IN_TYPE_OFST 4
#define MC_CMD_VPORT_ALLOC_IN_TYPE_LEN 4
/* enum: VLAN (obsolete) */
-#define MC_CMD_VPORT_ALLOC_IN_VPORT_TYPE_VLAN 0x1
+#define MC_CMD_VPORT_ALLOC_IN_VPORT_TYPE_VLAN 0x1
/* enum: VEB (obsolete) */
-#define MC_CMD_VPORT_ALLOC_IN_VPORT_TYPE_VEB 0x2
+#define MC_CMD_VPORT_ALLOC_IN_VPORT_TYPE_VEB 0x2
/* enum: VEPA (obsolete) */
-#define MC_CMD_VPORT_ALLOC_IN_VPORT_TYPE_VEPA 0x3
+#define MC_CMD_VPORT_ALLOC_IN_VPORT_TYPE_VEPA 0x3
/* enum: A normal v-port receives packets which match a specified MAC and/or
* VLAN.
*/
-#define MC_CMD_VPORT_ALLOC_IN_VPORT_TYPE_NORMAL 0x4
+#define MC_CMD_VPORT_ALLOC_IN_VPORT_TYPE_NORMAL 0x4
/* enum: An expansion v-port packets traffic which don't match any other
* v-port.
*/
-#define MC_CMD_VPORT_ALLOC_IN_VPORT_TYPE_EXPANSION 0x5
+#define MC_CMD_VPORT_ALLOC_IN_VPORT_TYPE_EXPANSION 0x5
/* enum: An test v-port receives packets which match any filters installed by
* its downstream components.
*/
-#define MC_CMD_VPORT_ALLOC_IN_VPORT_TYPE_TEST 0x6
+#define MC_CMD_VPORT_ALLOC_IN_VPORT_TYPE_TEST 0x6
/* Flags controlling v-port creation */
#define MC_CMD_VPORT_ALLOC_IN_FLAGS_OFST 8
#define MC_CMD_VPORT_ALLOC_IN_FLAGS_LEN 4
@@ -10595,7 +11239,7 @@
#define MC_CMD_VADAPTOR_ALLOC_IN_MACADDR_OFST 24
#define MC_CMD_VADAPTOR_ALLOC_IN_MACADDR_LEN 6
/* enum: Derive the MAC address from the upstream port */
-#define MC_CMD_VADAPTOR_ALLOC_IN_AUTO_MAC 0x0
+#define MC_CMD_VADAPTOR_ALLOC_IN_AUTO_MAC 0x0
/* MC_CMD_VADAPTOR_ALLOC_OUT msgresponse */
#define MC_CMD_VADAPTOR_ALLOC_OUT_LEN 0
@@ -10809,12 +11453,12 @@
/* enum: Allocate a context for exclusive use. The key and indirection table
* must be explicitly configured.
*/
-#define MC_CMD_RSS_CONTEXT_ALLOC_IN_TYPE_EXCLUSIVE 0x0
+#define MC_CMD_RSS_CONTEXT_ALLOC_IN_TYPE_EXCLUSIVE 0x0
/* enum: Allocate a context for shared use; this will spread across a range of
* queues, but the key and indirection table are pre-configured and may not be
* changed. For this mode, NUM_QUEUES must 2, 4, 8, 16, 32 or 64.
*/
-#define MC_CMD_RSS_CONTEXT_ALLOC_IN_TYPE_SHARED 0x1
+#define MC_CMD_RSS_CONTEXT_ALLOC_IN_TYPE_SHARED 0x1
/* Number of queues spanned by this context, in the range 1-64; valid offsets
* in the indirection table will be in the range 0 to NUM_QUEUES-1.
*/
@@ -10830,7 +11474,7 @@
#define MC_CMD_RSS_CONTEXT_ALLOC_OUT_RSS_CONTEXT_ID_OFST 0
#define MC_CMD_RSS_CONTEXT_ALLOC_OUT_RSS_CONTEXT_ID_LEN 4
/* enum: guaranteed invalid RSS context handle value */
-#define MC_CMD_RSS_CONTEXT_ALLOC_OUT_RSS_CONTEXT_ID_INVALID 0xffffffff
+#define MC_CMD_RSS_CONTEXT_ALLOC_OUT_RSS_CONTEXT_ID_INVALID 0xffffffff
/***********************************/
@@ -11073,7 +11717,7 @@
#define MC_CMD_DOT1P_MAPPING_ALLOC_OUT_DOT1P_MAPPING_ID_OFST 0
#define MC_CMD_DOT1P_MAPPING_ALLOC_OUT_DOT1P_MAPPING_ID_LEN 4
/* enum: guaranteed invalid .1p mapping handle value */
-#define MC_CMD_DOT1P_MAPPING_ALLOC_OUT_DOT1P_MAPPING_ID_INVALID 0xffffffff
+#define MC_CMD_DOT1P_MAPPING_ALLOC_OUT_DOT1P_MAPPING_ID_INVALID 0xffffffff
/***********************************/
@@ -11385,11 +12029,11 @@
#define MC_CMD_SET_RXDP_CONFIG_IN_PAD_HOST_LEN_LBN 1
#define MC_CMD_SET_RXDP_CONFIG_IN_PAD_HOST_LEN_WIDTH 2
/* enum: pad to 64 bytes */
-#define MC_CMD_SET_RXDP_CONFIG_IN_PAD_HOST_64 0x0
+#define MC_CMD_SET_RXDP_CONFIG_IN_PAD_HOST_64 0x0
/* enum: pad to 128 bytes (Medford only) */
-#define MC_CMD_SET_RXDP_CONFIG_IN_PAD_HOST_128 0x1
+#define MC_CMD_SET_RXDP_CONFIG_IN_PAD_HOST_128 0x1
/* enum: pad to 256 bytes (Medford only) */
-#define MC_CMD_SET_RXDP_CONFIG_IN_PAD_HOST_256 0x2
+#define MC_CMD_SET_RXDP_CONFIG_IN_PAD_HOST_256 0x2
/* MC_CMD_SET_RXDP_CONFIG_OUT msgresponse */
#define MC_CMD_SET_RXDP_CONFIG_OUT_LEN 0
@@ -11453,37 +12097,37 @@
#define MC_CMD_SET_CLOCK_IN_SYS_FREQ_OFST 0
#define MC_CMD_SET_CLOCK_IN_SYS_FREQ_LEN 4
/* enum: Leave the system clock domain frequency unchanged */
-#define MC_CMD_SET_CLOCK_IN_SYS_DOMAIN_DONT_CHANGE 0x0
+#define MC_CMD_SET_CLOCK_IN_SYS_DOMAIN_DONT_CHANGE 0x0
/* Requested frequency in MHz for inter-core clock domain */
#define MC_CMD_SET_CLOCK_IN_ICORE_FREQ_OFST 4
#define MC_CMD_SET_CLOCK_IN_ICORE_FREQ_LEN 4
/* enum: Leave the inter-core clock domain frequency unchanged */
-#define MC_CMD_SET_CLOCK_IN_ICORE_DOMAIN_DONT_CHANGE 0x0
+#define MC_CMD_SET_CLOCK_IN_ICORE_DOMAIN_DONT_CHANGE 0x0
/* Requested frequency in MHz for DPCPU clock domain */
#define MC_CMD_SET_CLOCK_IN_DPCPU_FREQ_OFST 8
#define MC_CMD_SET_CLOCK_IN_DPCPU_FREQ_LEN 4
/* enum: Leave the DPCPU clock domain frequency unchanged */
-#define MC_CMD_SET_CLOCK_IN_DPCPU_DOMAIN_DONT_CHANGE 0x0
+#define MC_CMD_SET_CLOCK_IN_DPCPU_DOMAIN_DONT_CHANGE 0x0
/* Requested frequency in MHz for PCS clock domain */
#define MC_CMD_SET_CLOCK_IN_PCS_FREQ_OFST 12
#define MC_CMD_SET_CLOCK_IN_PCS_FREQ_LEN 4
/* enum: Leave the PCS clock domain frequency unchanged */
-#define MC_CMD_SET_CLOCK_IN_PCS_DOMAIN_DONT_CHANGE 0x0
+#define MC_CMD_SET_CLOCK_IN_PCS_DOMAIN_DONT_CHANGE 0x0
/* Requested frequency in MHz for MC clock domain */
#define MC_CMD_SET_CLOCK_IN_MC_FREQ_OFST 16
#define MC_CMD_SET_CLOCK_IN_MC_FREQ_LEN 4
/* enum: Leave the MC clock domain frequency unchanged */
-#define MC_CMD_SET_CLOCK_IN_MC_DOMAIN_DONT_CHANGE 0x0
+#define MC_CMD_SET_CLOCK_IN_MC_DOMAIN_DONT_CHANGE 0x0
/* Requested frequency in MHz for rmon clock domain */
#define MC_CMD_SET_CLOCK_IN_RMON_FREQ_OFST 20
#define MC_CMD_SET_CLOCK_IN_RMON_FREQ_LEN 4
/* enum: Leave the rmon clock domain frequency unchanged */
-#define MC_CMD_SET_CLOCK_IN_RMON_DOMAIN_DONT_CHANGE 0x0
+#define MC_CMD_SET_CLOCK_IN_RMON_DOMAIN_DONT_CHANGE 0x0
/* Requested frequency in MHz for vswitch clock domain */
#define MC_CMD_SET_CLOCK_IN_VSWITCH_FREQ_OFST 24
#define MC_CMD_SET_CLOCK_IN_VSWITCH_FREQ_LEN 4
/* enum: Leave the vswitch clock domain frequency unchanged */
-#define MC_CMD_SET_CLOCK_IN_VSWITCH_DOMAIN_DONT_CHANGE 0x0
+#define MC_CMD_SET_CLOCK_IN_VSWITCH_DOMAIN_DONT_CHANGE 0x0
/* MC_CMD_SET_CLOCK_OUT msgresponse */
#define MC_CMD_SET_CLOCK_OUT_LEN 28
@@ -11491,37 +12135,37 @@
#define MC_CMD_SET_CLOCK_OUT_SYS_FREQ_OFST 0
#define MC_CMD_SET_CLOCK_OUT_SYS_FREQ_LEN 4
/* enum: The system clock domain doesn't exist */
-#define MC_CMD_SET_CLOCK_OUT_SYS_DOMAIN_UNSUPPORTED 0x0
+#define MC_CMD_SET_CLOCK_OUT_SYS_DOMAIN_UNSUPPORTED 0x0
/* Resulting inter-core frequency in MHz */
#define MC_CMD_SET_CLOCK_OUT_ICORE_FREQ_OFST 4
#define MC_CMD_SET_CLOCK_OUT_ICORE_FREQ_LEN 4
/* enum: The inter-core clock domain doesn't exist / isn't used */
-#define MC_CMD_SET_CLOCK_OUT_ICORE_DOMAIN_UNSUPPORTED 0x0
+#define MC_CMD_SET_CLOCK_OUT_ICORE_DOMAIN_UNSUPPORTED 0x0
/* Resulting DPCPU frequency in MHz */
#define MC_CMD_SET_CLOCK_OUT_DPCPU_FREQ_OFST 8
#define MC_CMD_SET_CLOCK_OUT_DPCPU_FREQ_LEN 4
/* enum: The dpcpu clock domain doesn't exist */
-#define MC_CMD_SET_CLOCK_OUT_DPCPU_DOMAIN_UNSUPPORTED 0x0
+#define MC_CMD_SET_CLOCK_OUT_DPCPU_DOMAIN_UNSUPPORTED 0x0
/* Resulting PCS frequency in MHz */
#define MC_CMD_SET_CLOCK_OUT_PCS_FREQ_OFST 12
#define MC_CMD_SET_CLOCK_OUT_PCS_FREQ_LEN 4
/* enum: The PCS clock domain doesn't exist / isn't controlled */
-#define MC_CMD_SET_CLOCK_OUT_PCS_DOMAIN_UNSUPPORTED 0x0
+#define MC_CMD_SET_CLOCK_OUT_PCS_DOMAIN_UNSUPPORTED 0x0
/* Resulting MC frequency in MHz */
#define MC_CMD_SET_CLOCK_OUT_MC_FREQ_OFST 16
#define MC_CMD_SET_CLOCK_OUT_MC_FREQ_LEN 4
/* enum: The MC clock domain doesn't exist / isn't controlled */
-#define MC_CMD_SET_CLOCK_OUT_MC_DOMAIN_UNSUPPORTED 0x0
+#define MC_CMD_SET_CLOCK_OUT_MC_DOMAIN_UNSUPPORTED 0x0
/* Resulting rmon frequency in MHz */
#define MC_CMD_SET_CLOCK_OUT_RMON_FREQ_OFST 20
#define MC_CMD_SET_CLOCK_OUT_RMON_FREQ_LEN 4
/* enum: The rmon clock domain doesn't exist / isn't controlled */
-#define MC_CMD_SET_CLOCK_OUT_RMON_DOMAIN_UNSUPPORTED 0x0
+#define MC_CMD_SET_CLOCK_OUT_RMON_DOMAIN_UNSUPPORTED 0x0
/* Resulting vswitch frequency in MHz */
#define MC_CMD_SET_CLOCK_OUT_VSWITCH_FREQ_OFST 24
#define MC_CMD_SET_CLOCK_OUT_VSWITCH_FREQ_LEN 4
/* enum: The vswitch clock domain doesn't exist / isn't controlled */
-#define MC_CMD_SET_CLOCK_OUT_VSWITCH_DOMAIN_UNSUPPORTED 0x0
+#define MC_CMD_SET_CLOCK_OUT_VSWITCH_DOMAIN_UNSUPPORTED 0x0
/***********************************/
@@ -11537,21 +12181,21 @@
#define MC_CMD_DPCPU_RPC_IN_CPU_OFST 0
#define MC_CMD_DPCPU_RPC_IN_CPU_LEN 4
/* enum: RxDPCPU0 */
-#define MC_CMD_DPCPU_RPC_IN_DPCPU_RX0 0x0
+#define MC_CMD_DPCPU_RPC_IN_DPCPU_RX0 0x0
/* enum: TxDPCPU0 */
-#define MC_CMD_DPCPU_RPC_IN_DPCPU_TX0 0x1
+#define MC_CMD_DPCPU_RPC_IN_DPCPU_TX0 0x1
/* enum: TxDPCPU1 */
-#define MC_CMD_DPCPU_RPC_IN_DPCPU_TX1 0x2
+#define MC_CMD_DPCPU_RPC_IN_DPCPU_TX1 0x2
/* enum: RxDPCPU1 (Medford only) */
-#define MC_CMD_DPCPU_RPC_IN_DPCPU_RX1 0x3
+#define MC_CMD_DPCPU_RPC_IN_DPCPU_RX1 0x3
/* enum: RxDPCPU (will be for the calling function; for now, just an alias of
* DPCPU_RX0)
*/
-#define MC_CMD_DPCPU_RPC_IN_DPCPU_RX 0x80
+#define MC_CMD_DPCPU_RPC_IN_DPCPU_RX 0x80
/* enum: TxDPCPU (will be for the calling function; for now, just an alias of
* DPCPU_TX0)
*/
-#define MC_CMD_DPCPU_RPC_IN_DPCPU_TX 0x81
+#define MC_CMD_DPCPU_RPC_IN_DPCPU_TX 0x81
/* First 8 bits [39:32] of DATA are consumed by MC-DPCPU protocol and must be
* initialised to zero
*/
@@ -11559,15 +12203,15 @@
#define MC_CMD_DPCPU_RPC_IN_DATA_LEN 32
#define MC_CMD_DPCPU_RPC_IN_HDR_CMD_CMDNUM_LBN 8
#define MC_CMD_DPCPU_RPC_IN_HDR_CMD_CMDNUM_WIDTH 8
-#define MC_CMD_DPCPU_RPC_IN_CMDNUM_TXDPCPU_READ 0x6 /* enum */
-#define MC_CMD_DPCPU_RPC_IN_CMDNUM_TXDPCPU_WRITE 0x7 /* enum */
-#define MC_CMD_DPCPU_RPC_IN_CMDNUM_TXDPCPU_SELF_TEST 0xc /* enum */
-#define MC_CMD_DPCPU_RPC_IN_CMDNUM_TXDPCPU_CSR_ACCESS 0xe /* enum */
-#define MC_CMD_DPCPU_RPC_IN_CMDNUM_RXDPCPU_READ 0x46 /* enum */
-#define MC_CMD_DPCPU_RPC_IN_CMDNUM_RXDPCPU_WRITE 0x47 /* enum */
-#define MC_CMD_DPCPU_RPC_IN_CMDNUM_RXDPCPU_SELF_TEST 0x4a /* enum */
-#define MC_CMD_DPCPU_RPC_IN_CMDNUM_RXDPCPU_CSR_ACCESS 0x4c /* enum */
-#define MC_CMD_DPCPU_RPC_IN_CMDNUM_RXDPCPU_SET_MC_REPLAY_CNTXT 0x4d /* enum */
+#define MC_CMD_DPCPU_RPC_IN_CMDNUM_TXDPCPU_READ 0x6 /* enum */
+#define MC_CMD_DPCPU_RPC_IN_CMDNUM_TXDPCPU_WRITE 0x7 /* enum */
+#define MC_CMD_DPCPU_RPC_IN_CMDNUM_TXDPCPU_SELF_TEST 0xc /* enum */
+#define MC_CMD_DPCPU_RPC_IN_CMDNUM_TXDPCPU_CSR_ACCESS 0xe /* enum */
+#define MC_CMD_DPCPU_RPC_IN_CMDNUM_RXDPCPU_READ 0x46 /* enum */
+#define MC_CMD_DPCPU_RPC_IN_CMDNUM_RXDPCPU_WRITE 0x47 /* enum */
+#define MC_CMD_DPCPU_RPC_IN_CMDNUM_RXDPCPU_SELF_TEST 0x4a /* enum */
+#define MC_CMD_DPCPU_RPC_IN_CMDNUM_RXDPCPU_CSR_ACCESS 0x4c /* enum */
+#define MC_CMD_DPCPU_RPC_IN_CMDNUM_RXDPCPU_SET_MC_REPLAY_CNTXT 0x4d /* enum */
#define MC_CMD_DPCPU_RPC_IN_HDR_CMD_REQ_OBJID_LBN 16
#define MC_CMD_DPCPU_RPC_IN_HDR_CMD_REQ_OBJID_WIDTH 16
#define MC_CMD_DPCPU_RPC_IN_HDR_CMD_REQ_ADDR_LBN 16
@@ -11578,11 +12222,11 @@
#define MC_CMD_DPCPU_RPC_IN_CSR_ACCESS_INFO_WIDTH 240
#define MC_CMD_DPCPU_RPC_IN_CSR_ACCESS_CMD_LBN 16
#define MC_CMD_DPCPU_RPC_IN_CSR_ACCESS_CMD_WIDTH 16
-#define MC_CMD_DPCPU_RPC_IN_CSR_ACCESS_CMD_STOP_RETURN_RESULT 0x0 /* enum */
-#define MC_CMD_DPCPU_RPC_IN_CSR_ACCESS_CMD_START_READ 0x1 /* enum */
-#define MC_CMD_DPCPU_RPC_IN_CSR_ACCESS_CMD_START_WRITE 0x2 /* enum */
-#define MC_CMD_DPCPU_RPC_IN_CSR_ACCESS_CMD_START_WRITE_READ 0x3 /* enum */
-#define MC_CMD_DPCPU_RPC_IN_CSR_ACCESS_CMD_START_PIPELINED_READ 0x4 /* enum */
+#define MC_CMD_DPCPU_RPC_IN_CSR_ACCESS_CMD_STOP_RETURN_RESULT 0x0 /* enum */
+#define MC_CMD_DPCPU_RPC_IN_CSR_ACCESS_CMD_START_READ 0x1 /* enum */
+#define MC_CMD_DPCPU_RPC_IN_CSR_ACCESS_CMD_START_WRITE 0x2 /* enum */
+#define MC_CMD_DPCPU_RPC_IN_CSR_ACCESS_CMD_START_WRITE_READ 0x3 /* enum */
+#define MC_CMD_DPCPU_RPC_IN_CSR_ACCESS_CMD_START_PIPELINED_READ 0x4 /* enum */
#define MC_CMD_DPCPU_RPC_IN_CSR_ACCESS_START_DELAY_LBN 48
#define MC_CMD_DPCPU_RPC_IN_CSR_ACCESS_START_DELAY_WIDTH 16
#define MC_CMD_DPCPU_RPC_IN_CSR_ACCESS_RPT_COUNT_LBN 64
@@ -11591,9 +12235,9 @@
#define MC_CMD_DPCPU_RPC_IN_CSR_ACCESS_GAP_DELAY_WIDTH 16
#define MC_CMD_DPCPU_RPC_IN_MC_REPLAY_MODE_LBN 16
#define MC_CMD_DPCPU_RPC_IN_MC_REPLAY_MODE_WIDTH 16
-#define MC_CMD_DPCPU_RPC_IN_MC_REPLAY_MODE_CUT_THROUGH 0x1 /* enum */
-#define MC_CMD_DPCPU_RPC_IN_MC_REPLAY_MODE_STORE_FORWARD 0x2 /* enum */
-#define MC_CMD_DPCPU_RPC_IN_MC_REPLAY_MODE_STORE_FORWARD_FIRST 0x3 /* enum */
+#define MC_CMD_DPCPU_RPC_IN_MC_REPLAY_MODE_CUT_THROUGH 0x1 /* enum */
+#define MC_CMD_DPCPU_RPC_IN_MC_REPLAY_MODE_STORE_FORWARD 0x2 /* enum */
+#define MC_CMD_DPCPU_RPC_IN_MC_REPLAY_MODE_STORE_FORWARD_FIRST 0x3 /* enum */
#define MC_CMD_DPCPU_RPC_IN_MC_REPLAY_CNTXT_LBN 64
#define MC_CMD_DPCPU_RPC_IN_MC_REPLAY_CNTXT_WIDTH 16
#define MC_CMD_DPCPU_RPC_IN_WDATA_OFST 12
@@ -11660,7 +12304,7 @@
#define MC_CMD_SHMBOOT_OP_IN_SHMBOOT_OP_OFST 0
#define MC_CMD_SHMBOOT_OP_IN_SHMBOOT_OP_LEN 4
/* enum: Copy slave_data section to the slave core. (Greenport only) */
-#define MC_CMD_SHMBOOT_OP_IN_PUSH_SLAVE_DATA 0x0
+#define MC_CMD_SHMBOOT_OP_IN_PUSH_SLAVE_DATA 0x0
/* MC_CMD_SHMBOOT_OP_OUT msgresponse */
#define MC_CMD_SHMBOOT_OP_OUT_LEN 0
@@ -11709,14 +12353,14 @@
#define MC_CMD_DUMP_DO_IN_PADDING_LEN 4
#define MC_CMD_DUMP_DO_IN_DUMPSPEC_SRC_OFST 4
#define MC_CMD_DUMP_DO_IN_DUMPSPEC_SRC_LEN 4
-#define MC_CMD_DUMP_DO_IN_DUMPSPEC_SRC_CUSTOM 0x0 /* enum */
-#define MC_CMD_DUMP_DO_IN_DUMPSPEC_SRC_DEFAULT 0x1 /* enum */
+#define MC_CMD_DUMP_DO_IN_DUMPSPEC_SRC_CUSTOM 0x0 /* enum */
+#define MC_CMD_DUMP_DO_IN_DUMPSPEC_SRC_DEFAULT 0x1 /* enum */
#define MC_CMD_DUMP_DO_IN_DUMPSPEC_SRC_CUSTOM_TYPE_OFST 8
#define MC_CMD_DUMP_DO_IN_DUMPSPEC_SRC_CUSTOM_TYPE_LEN 4
-#define MC_CMD_DUMP_DO_IN_DUMP_LOCATION_NVRAM 0x1 /* enum */
-#define MC_CMD_DUMP_DO_IN_DUMP_LOCATION_HOST_MEMORY 0x2 /* enum */
-#define MC_CMD_DUMP_DO_IN_DUMP_LOCATION_HOST_MEMORY_MLI 0x3 /* enum */
-#define MC_CMD_DUMP_DO_IN_DUMP_LOCATION_UART 0x4 /* enum */
+#define MC_CMD_DUMP_DO_IN_DUMP_LOCATION_NVRAM 0x1 /* enum */
+#define MC_CMD_DUMP_DO_IN_DUMP_LOCATION_HOST_MEMORY 0x2 /* enum */
+#define MC_CMD_DUMP_DO_IN_DUMP_LOCATION_HOST_MEMORY_MLI 0x3 /* enum */
+#define MC_CMD_DUMP_DO_IN_DUMP_LOCATION_UART 0x4 /* enum */
#define MC_CMD_DUMP_DO_IN_DUMPSPEC_SRC_CUSTOM_NVRAM_PARTITION_TYPE_ID_OFST 12
#define MC_CMD_DUMP_DO_IN_DUMPSPEC_SRC_CUSTOM_NVRAM_PARTITION_TYPE_ID_LEN 4
#define MC_CMD_DUMP_DO_IN_DUMPSPEC_SRC_CUSTOM_NVRAM_OFFSET_OFST 16
@@ -11727,24 +12371,24 @@
#define MC_CMD_DUMP_DO_IN_DUMPSPEC_SRC_CUSTOM_HOST_MEMORY_ADDR_HI_LEN 4
#define MC_CMD_DUMP_DO_IN_DUMPSPEC_SRC_CUSTOM_HOST_MEMORY_MLI_ROOT_ADDR_LO_OFST 12
#define MC_CMD_DUMP_DO_IN_DUMPSPEC_SRC_CUSTOM_HOST_MEMORY_MLI_ROOT_ADDR_LO_LEN 4
-#define MC_CMD_DUMP_DO_IN_HOST_MEMORY_MLI_PAGE_SIZE 0x1000 /* enum */
+#define MC_CMD_DUMP_DO_IN_HOST_MEMORY_MLI_PAGE_SIZE 0x1000 /* enum */
#define MC_CMD_DUMP_DO_IN_DUMPSPEC_SRC_CUSTOM_HOST_MEMORY_MLI_ROOT_ADDR_HI_OFST 16
#define MC_CMD_DUMP_DO_IN_DUMPSPEC_SRC_CUSTOM_HOST_MEMORY_MLI_ROOT_ADDR_HI_LEN 4
#define MC_CMD_DUMP_DO_IN_DUMPSPEC_SRC_CUSTOM_HOST_MEMORY_MLI_DEPTH_OFST 20
#define MC_CMD_DUMP_DO_IN_DUMPSPEC_SRC_CUSTOM_HOST_MEMORY_MLI_DEPTH_LEN 4
-#define MC_CMD_DUMP_DO_IN_HOST_MEMORY_MLI_MAX_DEPTH 0x2 /* enum */
+#define MC_CMD_DUMP_DO_IN_HOST_MEMORY_MLI_MAX_DEPTH 0x2 /* enum */
#define MC_CMD_DUMP_DO_IN_DUMPSPEC_SRC_CUSTOM_UART_PORT_OFST 12
#define MC_CMD_DUMP_DO_IN_DUMPSPEC_SRC_CUSTOM_UART_PORT_LEN 4
/* enum: The uart port this command was received over (if using a uart
* transport)
*/
-#define MC_CMD_DUMP_DO_IN_UART_PORT_SRC 0xff
+#define MC_CMD_DUMP_DO_IN_UART_PORT_SRC 0xff
#define MC_CMD_DUMP_DO_IN_DUMPSPEC_SRC_CUSTOM_SIZE_OFST 24
#define MC_CMD_DUMP_DO_IN_DUMPSPEC_SRC_CUSTOM_SIZE_LEN 4
#define MC_CMD_DUMP_DO_IN_DUMPFILE_DST_OFST 28
#define MC_CMD_DUMP_DO_IN_DUMPFILE_DST_LEN 4
-#define MC_CMD_DUMP_DO_IN_DUMPFILE_DST_CUSTOM 0x0 /* enum */
-#define MC_CMD_DUMP_DO_IN_DUMPFILE_DST_NVRAM_DUMP_PARTITION 0x1 /* enum */
+#define MC_CMD_DUMP_DO_IN_DUMPFILE_DST_CUSTOM 0x0 /* enum */
+#define MC_CMD_DUMP_DO_IN_DUMPFILE_DST_NVRAM_DUMP_PARTITION 0x1 /* enum */
#define MC_CMD_DUMP_DO_IN_DUMPFILE_DST_CUSTOM_TYPE_OFST 32
#define MC_CMD_DUMP_DO_IN_DUMPFILE_DST_CUSTOM_TYPE_LEN 4
/* Enum values, see field(s): */
@@ -11854,11 +12498,11 @@
#define MC_CMD_SET_PSU_IN_LEN 12
#define MC_CMD_SET_PSU_IN_PARAM_OFST 0
#define MC_CMD_SET_PSU_IN_PARAM_LEN 4
-#define MC_CMD_SET_PSU_IN_PARAM_SUPPLY_VOLTAGE 0x0 /* enum */
+#define MC_CMD_SET_PSU_IN_PARAM_SUPPLY_VOLTAGE 0x0 /* enum */
#define MC_CMD_SET_PSU_IN_RAIL_OFST 4
#define MC_CMD_SET_PSU_IN_RAIL_LEN 4
-#define MC_CMD_SET_PSU_IN_RAIL_0V9 0x0 /* enum */
-#define MC_CMD_SET_PSU_IN_RAIL_1V2 0x1 /* enum */
+#define MC_CMD_SET_PSU_IN_RAIL_0V9 0x0 /* enum */
+#define MC_CMD_SET_PSU_IN_RAIL_1V2 0x1 /* enum */
/* desired value, eg voltage in mV */
#define MC_CMD_SET_PSU_IN_VALUE_OFST 8
#define MC_CMD_SET_PSU_IN_VALUE_LEN 4
@@ -12031,26 +12675,30 @@
#define MC_CMD_KR_TUNE_IN_KR_TUNE_OP_OFST 0
#define MC_CMD_KR_TUNE_IN_KR_TUNE_OP_LEN 1
/* enum: Get current RXEQ settings */
-#define MC_CMD_KR_TUNE_IN_RXEQ_GET 0x0
+#define MC_CMD_KR_TUNE_IN_RXEQ_GET 0x0
/* enum: Override RXEQ settings */
-#define MC_CMD_KR_TUNE_IN_RXEQ_SET 0x1
+#define MC_CMD_KR_TUNE_IN_RXEQ_SET 0x1
/* enum: Get current TX Driver settings */
-#define MC_CMD_KR_TUNE_IN_TXEQ_GET 0x2
+#define MC_CMD_KR_TUNE_IN_TXEQ_GET 0x2
/* enum: Override TX Driver settings */
-#define MC_CMD_KR_TUNE_IN_TXEQ_SET 0x3
+#define MC_CMD_KR_TUNE_IN_TXEQ_SET 0x3
/* enum: Force KR Serdes reset / recalibration */
-#define MC_CMD_KR_TUNE_IN_RECAL 0x4
+#define MC_CMD_KR_TUNE_IN_RECAL 0x4
/* enum: Start KR Serdes Eye diagram plot on a given lane. Lane must have valid
* signal.
*/
-#define MC_CMD_KR_TUNE_IN_START_EYE_PLOT 0x5
+#define MC_CMD_KR_TUNE_IN_START_EYE_PLOT 0x5
/* enum: Poll KR Serdes Eye diagram plot. Returns one row of BER data. The
* caller should call this command repeatedly after starting eye plot, until no
* more data is returned.
*/
-#define MC_CMD_KR_TUNE_IN_POLL_EYE_PLOT 0x6
+#define MC_CMD_KR_TUNE_IN_POLL_EYE_PLOT 0x6
/* enum: Read Figure Of Merit (eye quality, higher is better). */
-#define MC_CMD_KR_TUNE_IN_READ_FOM 0x7
+#define MC_CMD_KR_TUNE_IN_READ_FOM 0x7
+/* enum: Start/stop link training frames */
+#define MC_CMD_KR_TUNE_IN_LINK_TRAIN_RUN 0x8
+/* enum: Issue KR link training command (control training coefficients) */
+#define MC_CMD_KR_TUNE_IN_LINK_TRAIN_CMD 0x9
/* Align the arguments to 32 bits */
#define MC_CMD_KR_TUNE_IN_KR_TUNE_RSVD_OFST 1
#define MC_CMD_KR_TUNE_IN_KR_TUNE_RSVD_LEN 3
@@ -12084,98 +12732,98 @@
#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_PARAM_ID_LBN 0
#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_PARAM_ID_WIDTH 8
/* enum: Attenuation (0-15, Huntington) */
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_ATT 0x0
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_ATT 0x0
/* enum: CTLE Boost (0-15, Huntington) */
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_BOOST 0x1
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_BOOST 0x1
/* enum: Edge DFE Tap1 (Huntington - 0 - max negative, 64 - zero, 127 - max
* positive, Medford - 0-31)
*/
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_EDFE_TAP1 0x2
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_EDFE_TAP1 0x2
/* enum: Edge DFE Tap2 (Huntington - 0 - max negative, 32 - zero, 63 - max
* positive, Medford - 0-31)
*/
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_EDFE_TAP2 0x3
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_EDFE_TAP2 0x3
/* enum: Edge DFE Tap3 (Huntington - 0 - max negative, 32 - zero, 63 - max
* positive, Medford - 0-16)
*/
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_EDFE_TAP3 0x4
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_EDFE_TAP3 0x4
/* enum: Edge DFE Tap4 (Huntington - 0 - max negative, 32 - zero, 63 - max
* positive, Medford - 0-16)
*/
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_EDFE_TAP4 0x5
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_EDFE_TAP4 0x5
/* enum: Edge DFE Tap5 (Huntington - 0 - max negative, 32 - zero, 63 - max
* positive, Medford - 0-16)
*/
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_EDFE_TAP5 0x6
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_EDFE_TAP5 0x6
/* enum: Edge DFE DLEV (0-128 for Medford) */
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_EDFE_DLEV 0x7
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_EDFE_DLEV 0x7
/* enum: Variable Gain Amplifier (0-15, Medford) */
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_VGA 0x8
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_VGA 0x8
/* enum: CTLE EQ Capacitor (0-15, Medford) */
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_CTLE_EQC 0x9
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_CTLE_EQC 0x9
/* enum: CTLE EQ Resistor (0-7, Medford) */
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_CTLE_EQRES 0xa
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_CTLE_EQRES 0xa
/* enum: CTLE gain (0-31, Medford2) */
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_CTLE_GAIN 0xb
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_CTLE_GAIN 0xb
/* enum: CTLE pole (0-31, Medford2) */
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_CTLE_POLE 0xc
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_CTLE_POLE 0xc
/* enum: CTLE peaking (0-31, Medford2) */
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_CTLE_PEAK 0xd
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_CTLE_PEAK 0xd
/* enum: DFE Tap1 - even path (Medford2 - 6 bit signed (-29 - +29)) */
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_DFE_TAP1_EVEN 0xe
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_DFE_TAP1_EVEN 0xe
/* enum: DFE Tap1 - odd path (Medford2 - 6 bit signed (-29 - +29)) */
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_DFE_TAP1_ODD 0xf
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_DFE_TAP1_ODD 0xf
/* enum: DFE Tap2 (Medford2 - 6 bit signed (-20 - +20)) */
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_DFE_TAP2 0x10
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_DFE_TAP2 0x10
/* enum: DFE Tap3 (Medford2 - 6 bit signed (-20 - +20)) */
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_DFE_TAP3 0x11
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_DFE_TAP3 0x11
/* enum: DFE Tap4 (Medford2 - 6 bit signed (-20 - +20)) */
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_DFE_TAP4 0x12
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_DFE_TAP4 0x12
/* enum: DFE Tap5 (Medford2 - 6 bit signed (-24 - +24)) */
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_DFE_TAP5 0x13
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_DFE_TAP5 0x13
/* enum: DFE Tap6 (Medford2 - 6 bit signed (-24 - +24)) */
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_DFE_TAP6 0x14
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_DFE_TAP6 0x14
/* enum: DFE Tap7 (Medford2 - 6 bit signed (-24 - +24)) */
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_DFE_TAP7 0x15
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_DFE_TAP7 0x15
/* enum: DFE Tap8 (Medford2 - 6 bit signed (-24 - +24)) */
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_DFE_TAP8 0x16
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_DFE_TAP8 0x16
/* enum: DFE Tap9 (Medford2 - 6 bit signed (-24 - +24)) */
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_DFE_TAP9 0x17
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_DFE_TAP9 0x17
/* enum: DFE Tap10 (Medford2 - 6 bit signed (-24 - +24)) */
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_DFE_TAP10 0x18
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_DFE_TAP10 0x18
/* enum: DFE Tap11 (Medford2 - 6 bit signed (-24 - +24)) */
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_DFE_TAP11 0x19
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_DFE_TAP11 0x19
/* enum: DFE Tap12 (Medford2 - 6 bit signed (-24 - +24)) */
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_DFE_TAP12 0x1a
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_DFE_TAP12 0x1a
/* enum: I/Q clk offset (Medford2 - 4 bit signed (-5 - +5))) */
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_IQ_OFF 0x1b
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_IQ_OFF 0x1b
/* enum: Negative h1 polarity data sampler offset calibration code, even path
* (Medford2 - 6 bit signed (-29 - +29)))
*/
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_H1N_OFF_EVEN 0x1c
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_H1N_OFF_EVEN 0x1c
/* enum: Negative h1 polarity data sampler offset calibration code, odd path
* (Medford2 - 6 bit signed (-29 - +29)))
*/
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_H1N_OFF_ODD 0x1d
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_H1N_OFF_ODD 0x1d
/* enum: Positive h1 polarity data sampler offset calibration code, even path
* (Medford2 - 6 bit signed (-29 - +29)))
*/
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_H1P_OFF_EVEN 0x1e
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_H1P_OFF_EVEN 0x1e
/* enum: Positive h1 polarity data sampler offset calibration code, odd path
* (Medford2 - 6 bit signed (-29 - +29)))
*/
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_H1P_OFF_ODD 0x1f
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_H1P_OFF_ODD 0x1f
/* enum: CDR calibration loop code (Medford2) */
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_CDR_PVT 0x20
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_CDR_PVT 0x20
/* enum: CDR integral loop code (Medford2) */
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_CDR_INTEG 0x21
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_CDR_INTEG 0x21
#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_PARAM_LANE_LBN 8
#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_PARAM_LANE_WIDTH 3
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_LANE_0 0x0 /* enum */
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_LANE_1 0x1 /* enum */
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_LANE_2 0x2 /* enum */
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_LANE_3 0x3 /* enum */
-#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_LANE_ALL 0x4 /* enum */
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_LANE_0 0x0 /* enum */
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_LANE_1 0x1 /* enum */
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_LANE_2 0x2 /* enum */
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_LANE_3 0x3 /* enum */
+#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_LANE_ALL 0x4 /* enum */
#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_PARAM_AUTOCAL_LBN 11
#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_PARAM_AUTOCAL_WIDTH 1
#define MC_CMD_KR_TUNE_RXEQ_GET_OUT_RESERVED_LBN 12
@@ -12241,38 +12889,38 @@
#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_PARAM_ID_LBN 0
#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_PARAM_ID_WIDTH 8
/* enum: TX Amplitude (Huntington, Medford, Medford2) */
-#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_TX_LEV 0x0
+#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_TX_LEV 0x0
/* enum: De-Emphasis Tap1 Magnitude (0-7) (Huntington) */
-#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_TX_MODE 0x1
+#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_TX_MODE 0x1
/* enum: De-Emphasis Tap1 Fine */
-#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_TX_DTLEV 0x2
+#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_TX_DTLEV 0x2
/* enum: De-Emphasis Tap2 Magnitude (0-6) (Huntington) */
-#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_TX_D2 0x3
+#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_TX_D2 0x3
/* enum: De-Emphasis Tap2 Fine (Huntington) */
-#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_TX_D2TLEV 0x4
+#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_TX_D2TLEV 0x4
/* enum: Pre-Emphasis Magnitude (Huntington) */
-#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_TX_E 0x5
+#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_TX_E 0x5
/* enum: Pre-Emphasis Fine (Huntington) */
-#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_TX_ETLEV 0x6
+#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_TX_ETLEV 0x6
/* enum: TX Slew Rate Coarse control (Huntington) */
-#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_TX_PREDRV_DLY 0x7
+#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_TX_PREDRV_DLY 0x7
/* enum: TX Slew Rate Fine control (Huntington) */
-#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_TX_SR_SET 0x8
+#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_TX_SR_SET 0x8
/* enum: TX Termination Impedance control (Huntington) */
-#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_TX_RT_SET 0x9
+#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_TX_RT_SET 0x9
/* enum: TX Amplitude Fine control (Medford) */
-#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_TX_LEV_FINE 0xa
+#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_TX_LEV_FINE 0xa
/* enum: Pre-shoot Tap (Medford, Medford2) */
-#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_TAP_ADV 0xb
+#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_TAP_ADV 0xb
/* enum: De-emphasis Tap (Medford, Medford2) */
-#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_TAP_DLY 0xc
+#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_TAP_DLY 0xc
#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_PARAM_LANE_LBN 8
#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_PARAM_LANE_WIDTH 3
-#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_LANE_0 0x0 /* enum */
-#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_LANE_1 0x1 /* enum */
-#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_LANE_2 0x2 /* enum */
-#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_LANE_3 0x3 /* enum */
-#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_LANE_ALL 0x4 /* enum */
+#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_LANE_0 0x0 /* enum */
+#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_LANE_1 0x1 /* enum */
+#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_LANE_2 0x2 /* enum */
+#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_LANE_3 0x3 /* enum */
+#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_LANE_ALL 0x4 /* enum */
#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_RESERVED_LBN 11
#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_RESERVED_WIDTH 5
#define MC_CMD_KR_TUNE_TXEQ_GET_OUT_PARAM_INITIAL_LBN 16
@@ -12345,9 +12993,12 @@
/* Align the arguments to 32 bits */
#define MC_CMD_KR_TUNE_START_EYE_PLOT_V2_IN_KR_TUNE_RSVD_OFST 1
#define MC_CMD_KR_TUNE_START_EYE_PLOT_V2_IN_KR_TUNE_RSVD_LEN 3
-/* Port-relative lane to scan eye on */
#define MC_CMD_KR_TUNE_START_EYE_PLOT_V2_IN_LANE_OFST 4
#define MC_CMD_KR_TUNE_START_EYE_PLOT_V2_IN_LANE_LEN 4
+#define MC_CMD_KR_TUNE_START_EYE_PLOT_V2_IN_LANE_NUM_LBN 0
+#define MC_CMD_KR_TUNE_START_EYE_PLOT_V2_IN_LANE_NUM_WIDTH 8
+#define MC_CMD_KR_TUNE_START_EYE_PLOT_V2_IN_LANE_ABS_REL_LBN 31
+#define MC_CMD_KR_TUNE_START_EYE_PLOT_V2_IN_LANE_ABS_REL_WIDTH 1
/* Scan duration / cycle count */
#define MC_CMD_KR_TUNE_START_EYE_PLOT_V2_IN_BER_OFST 8
#define MC_CMD_KR_TUNE_START_EYE_PLOT_V2_IN_BER_LEN 4
@@ -12383,12 +13034,91 @@
#define MC_CMD_KR_TUNE_READ_FOM_IN_KR_TUNE_RSVD_LEN 3
#define MC_CMD_KR_TUNE_READ_FOM_IN_LANE_OFST 4
#define MC_CMD_KR_TUNE_READ_FOM_IN_LANE_LEN 4
+#define MC_CMD_KR_TUNE_READ_FOM_IN_LANE_NUM_LBN 0
+#define MC_CMD_KR_TUNE_READ_FOM_IN_LANE_NUM_WIDTH 8
+#define MC_CMD_KR_TUNE_READ_FOM_IN_LANE_ABS_REL_LBN 31
+#define MC_CMD_KR_TUNE_READ_FOM_IN_LANE_ABS_REL_WIDTH 1
/* MC_CMD_KR_TUNE_READ_FOM_OUT msgresponse */
#define MC_CMD_KR_TUNE_READ_FOM_OUT_LEN 4
#define MC_CMD_KR_TUNE_READ_FOM_OUT_FOM_OFST 0
#define MC_CMD_KR_TUNE_READ_FOM_OUT_FOM_LEN 4
+/* MC_CMD_KR_TUNE_LINK_TRAIN_RUN_IN msgrequest */
+#define MC_CMD_KR_TUNE_LINK_TRAIN_RUN_IN_LEN 8
+/* Requested operation */
+#define MC_CMD_KR_TUNE_LINK_TRAIN_RUN_IN_KR_TUNE_OP_OFST 0
+#define MC_CMD_KR_TUNE_LINK_TRAIN_RUN_IN_KR_TUNE_OP_LEN 1
+/* Align the arguments to 32 bits */
+#define MC_CMD_KR_TUNE_LINK_TRAIN_RUN_IN_KR_TUNE_RSVD_OFST 1
+#define MC_CMD_KR_TUNE_LINK_TRAIN_RUN_IN_KR_TUNE_RSVD_LEN 3
+#define MC_CMD_KR_TUNE_LINK_TRAIN_RUN_IN_RUN_OFST 4
+#define MC_CMD_KR_TUNE_LINK_TRAIN_RUN_IN_RUN_LEN 4
+#define MC_CMD_KR_TUNE_LINK_TRAIN_RUN_IN_STOP 0x0 /* enum */
+#define MC_CMD_KR_TUNE_LINK_TRAIN_RUN_IN_START 0x1 /* enum */
+
+/* MC_CMD_KR_TUNE_LINK_TRAIN_CMD_IN msgrequest */
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_IN_LEN 28
+/* Requested operation */
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_IN_KR_TUNE_OP_OFST 0
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_IN_KR_TUNE_OP_LEN 1
+/* Align the arguments to 32 bits */
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_IN_KR_TUNE_RSVD_OFST 1
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_IN_KR_TUNE_RSVD_LEN 3
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_IN_LANE_OFST 4
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_IN_LANE_LEN 4
+/* Set INITIALIZE state */
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_IN_INITIALIZE_OFST 8
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_IN_INITIALIZE_LEN 4
+/* Set PRESET state */
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_IN_PRESET_OFST 12
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_IN_PRESET_LEN 4
+/* C(-1) request */
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_IN_CM1_OFST 16
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_IN_CM1_LEN 4
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_IN_REQ_HOLD 0x0 /* enum */
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_IN_REQ_INCREMENT 0x1 /* enum */
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_IN_REQ_DECREMENT 0x2 /* enum */
+/* C(0) request */
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_IN_C0_OFST 20
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_IN_C0_LEN 4
+/* Enum values, see field(s): */
+/* MC_CMD_KR_TUNE_LINK_TRAIN_CMD_IN/CM1 */
+/* C(+1) request */
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_IN_CP1_OFST 24
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_IN_CP1_LEN 4
+/* Enum values, see field(s): */
+/* MC_CMD_KR_TUNE_LINK_TRAIN_CMD_IN/CM1 */
+
+/* MC_CMD_KR_TUNE_LINK_TRAIN_CMD_OUT msgresponse */
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_OUT_LEN 24
+/* C(-1) status */
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_OUT_CM1_STATUS_OFST 0
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_OUT_CM1_STATUS_LEN 4
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_OUT_STATUS_NOT_UPDATED 0x0 /* enum */
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_OUT_STATUS_UPDATED 0x1 /* enum */
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_OUT_STATUS_MINIMUM 0x2 /* enum */
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_OUT_STATUS_MAXIMUM 0x3 /* enum */
+/* C(0) status */
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_OUT_C0_STATUS_OFST 4
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_OUT_C0_STATUS_LEN 4
+/* Enum values, see field(s): */
+/* MC_CMD_KR_TUNE_LINK_TRAIN_CMD_IN/CM1 */
+/* C(+1) status */
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_OUT_CP1_STATUS_OFST 8
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_OUT_CP1_STATUS_LEN 4
+/* Enum values, see field(s): */
+/* MC_CMD_KR_TUNE_LINK_TRAIN_CMD_IN/CM1 */
+/* C(-1) value */
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_OUT_CM1_VALUE_OFST 12
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_OUT_CM1_VALUE_LEN 4
+/* C(0) value */
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_OUT_C0_VALUE_OFST 16
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_OUT_C0_VALUE_LEN 4
+/* C(+1) status */
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_OUT_CP1_VALUE_OFST 20
+#define MC_CMD_KR_TUNE_LINK_TRAIN_CMD_OUT_CP1_VALUE_LEN 4
+
/***********************************/
/* MC_CMD_PCIE_TUNE
@@ -12406,22 +13136,22 @@
#define MC_CMD_PCIE_TUNE_IN_PCIE_TUNE_OP_OFST 0
#define MC_CMD_PCIE_TUNE_IN_PCIE_TUNE_OP_LEN 1
/* enum: Get current RXEQ settings */
-#define MC_CMD_PCIE_TUNE_IN_RXEQ_GET 0x0
+#define MC_CMD_PCIE_TUNE_IN_RXEQ_GET 0x0
/* enum: Override RXEQ settings */
-#define MC_CMD_PCIE_TUNE_IN_RXEQ_SET 0x1
+#define MC_CMD_PCIE_TUNE_IN_RXEQ_SET 0x1
/* enum: Get current TX Driver settings */
-#define MC_CMD_PCIE_TUNE_IN_TXEQ_GET 0x2
+#define MC_CMD_PCIE_TUNE_IN_TXEQ_GET 0x2
/* enum: Override TX Driver settings */
-#define MC_CMD_PCIE_TUNE_IN_TXEQ_SET 0x3
+#define MC_CMD_PCIE_TUNE_IN_TXEQ_SET 0x3
/* enum: Start PCIe Serdes Eye diagram plot on a given lane. */
-#define MC_CMD_PCIE_TUNE_IN_START_EYE_PLOT 0x5
+#define MC_CMD_PCIE_TUNE_IN_START_EYE_PLOT 0x5
/* enum: Poll PCIe Serdes Eye diagram plot. Returns one row of BER data. The
* caller should call this command repeatedly after starting eye plot, until no
* more data is returned.
*/
-#define MC_CMD_PCIE_TUNE_IN_POLL_EYE_PLOT 0x6
+#define MC_CMD_PCIE_TUNE_IN_POLL_EYE_PLOT 0x6
/* enum: Enable the SERDES BIST and set it to generate a 200MHz square wave */
-#define MC_CMD_PCIE_TUNE_IN_BIST_SQUARE_WAVE 0x7
+#define MC_CMD_PCIE_TUNE_IN_BIST_SQUARE_WAVE 0x7
/* Align the arguments to 32 bits */
#define MC_CMD_PCIE_TUNE_IN_PCIE_TUNE_RSVD_OFST 1
#define MC_CMD_PCIE_TUNE_IN_PCIE_TUNE_RSVD_LEN 3
@@ -12455,46 +13185,46 @@
#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_PARAM_ID_LBN 0
#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_PARAM_ID_WIDTH 8
/* enum: Attenuation (0-15) */
-#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_ATT 0x0
+#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_ATT 0x0
/* enum: CTLE Boost (0-15) */
-#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_BOOST 0x1
+#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_BOOST 0x1
/* enum: DFE Tap1 (0 - max negative, 64 - zero, 127 - max positive) */
-#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_DFE_TAP1 0x2
+#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_DFE_TAP1 0x2
/* enum: DFE Tap2 (0 - max negative, 32 - zero, 63 - max positive) */
-#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_DFE_TAP2 0x3
+#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_DFE_TAP2 0x3
/* enum: DFE Tap3 (0 - max negative, 32 - zero, 63 - max positive) */
-#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_DFE_TAP3 0x4
+#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_DFE_TAP3 0x4
/* enum: DFE Tap4 (0 - max negative, 32 - zero, 63 - max positive) */
-#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_DFE_TAP4 0x5
+#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_DFE_TAP4 0x5
/* enum: DFE Tap5 (0 - max negative, 32 - zero, 63 - max positive) */
-#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_DFE_TAP5 0x6
+#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_DFE_TAP5 0x6
/* enum: DFE DLev */
-#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_DFE_DLEV 0x7
+#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_DFE_DLEV 0x7
/* enum: Figure of Merit */
-#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_FOM 0x8
+#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_FOM 0x8
/* enum: CTLE EQ Capacitor (HF Gain) */
-#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_CTLE_EQC 0x9
+#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_CTLE_EQC 0x9
/* enum: CTLE EQ Resistor (DC Gain) */
-#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_CTLE_EQRES 0xa
+#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_CTLE_EQRES 0xa
#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_PARAM_LANE_LBN 8
#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_PARAM_LANE_WIDTH 5
-#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_0 0x0 /* enum */
-#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_1 0x1 /* enum */
-#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_2 0x2 /* enum */
-#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_3 0x3 /* enum */
-#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_4 0x4 /* enum */
-#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_5 0x5 /* enum */
-#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_6 0x6 /* enum */
-#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_7 0x7 /* enum */
-#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_8 0x8 /* enum */
-#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_9 0x9 /* enum */
-#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_10 0xa /* enum */
-#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_11 0xb /* enum */
-#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_12 0xc /* enum */
-#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_13 0xd /* enum */
-#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_14 0xe /* enum */
-#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_15 0xf /* enum */
-#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_ALL 0x10 /* enum */
+#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_0 0x0 /* enum */
+#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_1 0x1 /* enum */
+#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_2 0x2 /* enum */
+#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_3 0x3 /* enum */
+#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_4 0x4 /* enum */
+#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_5 0x5 /* enum */
+#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_6 0x6 /* enum */
+#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_7 0x7 /* enum */
+#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_8 0x8 /* enum */
+#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_9 0x9 /* enum */
+#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_10 0xa /* enum */
+#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_11 0xb /* enum */
+#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_12 0xc /* enum */
+#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_13 0xd /* enum */
+#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_14 0xe /* enum */
+#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_15 0xf /* enum */
+#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_LANE_ALL 0x10 /* enum */
#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_PARAM_AUTOCAL_LBN 13
#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_PARAM_AUTOCAL_WIDTH 1
#define MC_CMD_PCIE_TUNE_RXEQ_GET_OUT_RESERVED_LBN 14
@@ -12558,15 +13288,15 @@
#define MC_CMD_PCIE_TUNE_TXEQ_GET_OUT_PARAM_ID_LBN 0
#define MC_CMD_PCIE_TUNE_TXEQ_GET_OUT_PARAM_ID_WIDTH 8
/* enum: TxMargin (PIPE) */
-#define MC_CMD_PCIE_TUNE_TXEQ_GET_OUT_TXMARGIN 0x0
+#define MC_CMD_PCIE_TUNE_TXEQ_GET_OUT_TXMARGIN 0x0
/* enum: TxSwing (PIPE) */
-#define MC_CMD_PCIE_TUNE_TXEQ_GET_OUT_TXSWING 0x1
+#define MC_CMD_PCIE_TUNE_TXEQ_GET_OUT_TXSWING 0x1
/* enum: De-emphasis coefficient C(-1) (PIPE) */
-#define MC_CMD_PCIE_TUNE_TXEQ_GET_OUT_CM1 0x2
+#define MC_CMD_PCIE_TUNE_TXEQ_GET_OUT_CM1 0x2
/* enum: De-emphasis coefficient C(0) (PIPE) */
-#define MC_CMD_PCIE_TUNE_TXEQ_GET_OUT_C0 0x3
+#define MC_CMD_PCIE_TUNE_TXEQ_GET_OUT_C0 0x3
/* enum: De-emphasis coefficient C(+1) (PIPE) */
-#define MC_CMD_PCIE_TUNE_TXEQ_GET_OUT_CP1 0x4
+#define MC_CMD_PCIE_TUNE_TXEQ_GET_OUT_CP1 0x4
#define MC_CMD_PCIE_TUNE_TXEQ_GET_OUT_PARAM_LANE_LBN 8
#define MC_CMD_PCIE_TUNE_TXEQ_GET_OUT_PARAM_LANE_WIDTH 4
/* Enum values, see field(s): */
@@ -12632,9 +13362,9 @@
/* enum: re-read and apply licenses after a license key partition update; note
* that this operation returns a zero-length response
*/
-#define MC_CMD_LICENSING_IN_OP_UPDATE_LICENSE 0x0
+#define MC_CMD_LICENSING_IN_OP_UPDATE_LICENSE 0x0
/* enum: report counts of installed licenses */
-#define MC_CMD_LICENSING_IN_OP_GET_KEY_STATS 0x1
+#define MC_CMD_LICENSING_IN_OP_GET_KEY_STATS 0x1
/* MC_CMD_LICENSING_OUT msgresponse */
#define MC_CMD_LICENSING_OUT_LEN 28
@@ -12665,9 +13395,9 @@
#define MC_CMD_LICENSING_OUT_LICENSING_SELF_TEST_OFST 24
#define MC_CMD_LICENSING_OUT_LICENSING_SELF_TEST_LEN 4
/* enum: licensing subsystem self-test failed */
-#define MC_CMD_LICENSING_OUT_SELF_TEST_FAIL 0x0
+#define MC_CMD_LICENSING_OUT_SELF_TEST_FAIL 0x0
/* enum: licensing subsystem self-test passed */
-#define MC_CMD_LICENSING_OUT_SELF_TEST_PASS 0x1
+#define MC_CMD_LICENSING_OUT_SELF_TEST_PASS 0x1
/***********************************/
@@ -12687,11 +13417,11 @@
/* enum: re-read and apply licenses after a license key partition update; note
* that this operation returns a zero-length response
*/
-#define MC_CMD_LICENSING_V3_IN_OP_UPDATE_LICENSE 0x0
+#define MC_CMD_LICENSING_V3_IN_OP_UPDATE_LICENSE 0x0
/* enum: report counts of installed licenses Returns EAGAIN if license
* processing (updating) has been started but not yet completed.
*/
-#define MC_CMD_LICENSING_V3_IN_OP_REPORT_LICENSE 0x1
+#define MC_CMD_LICENSING_V3_IN_OP_REPORT_LICENSE 0x1
/* MC_CMD_LICENSING_V3_OUT msgresponse */
#define MC_CMD_LICENSING_V3_OUT_LEN 88
@@ -12718,9 +13448,9 @@
#define MC_CMD_LICENSING_V3_OUT_LICENSING_SELF_TEST_OFST 20
#define MC_CMD_LICENSING_V3_OUT_LICENSING_SELF_TEST_LEN 4
/* enum: licensing subsystem self-test failed */
-#define MC_CMD_LICENSING_V3_OUT_SELF_TEST_FAIL 0x0
+#define MC_CMD_LICENSING_V3_OUT_SELF_TEST_FAIL 0x0
/* enum: licensing subsystem self-test passed */
-#define MC_CMD_LICENSING_V3_OUT_SELF_TEST_PASS 0x1
+#define MC_CMD_LICENSING_V3_OUT_SELF_TEST_PASS 0x1
/* bitmask of licensed applications */
#define MC_CMD_LICENSING_V3_OUT_LICENSED_APPS_OFST 24
#define MC_CMD_LICENSING_V3_OUT_LICENSED_APPS_LEN 8
@@ -12806,9 +13536,9 @@
#define MC_CMD_GET_LICENSED_APP_STATE_OUT_STATE_OFST 0
#define MC_CMD_GET_LICENSED_APP_STATE_OUT_STATE_LEN 4
/* enum: no (or invalid) license is present for the application */
-#define MC_CMD_GET_LICENSED_APP_STATE_OUT_NOT_LICENSED 0x0
+#define MC_CMD_GET_LICENSED_APP_STATE_OUT_NOT_LICENSED 0x0
/* enum: a valid license is present for the application */
-#define MC_CMD_GET_LICENSED_APP_STATE_OUT_LICENSED 0x1
+#define MC_CMD_GET_LICENSED_APP_STATE_OUT_LICENSED 0x1
/***********************************/
@@ -12837,9 +13567,9 @@
#define MC_CMD_GET_LICENSED_V3_APP_STATE_OUT_STATE_OFST 0
#define MC_CMD_GET_LICENSED_V3_APP_STATE_OUT_STATE_LEN 4
/* enum: no (or invalid) license is present for the application */
-#define MC_CMD_GET_LICENSED_V3_APP_STATE_OUT_NOT_LICENSED 0x0
+#define MC_CMD_GET_LICENSED_V3_APP_STATE_OUT_NOT_LICENSED 0x0
/* enum: a valid license is present for the application */
-#define MC_CMD_GET_LICENSED_V3_APP_STATE_OUT_LICENSED 0x1
+#define MC_CMD_GET_LICENSED_V3_APP_STATE_OUT_LICENSED 0x1
/***********************************/
@@ -12891,9 +13621,9 @@
#define MC_CMD_LICENSED_APP_OP_IN_OP_OFST 4
#define MC_CMD_LICENSED_APP_OP_IN_OP_LEN 4
/* enum: validate application */
-#define MC_CMD_LICENSED_APP_OP_IN_OP_VALIDATE 0x0
+#define MC_CMD_LICENSED_APP_OP_IN_OP_VALIDATE 0x0
/* enum: mask application */
-#define MC_CMD_LICENSED_APP_OP_IN_OP_MASK 0x1
+#define MC_CMD_LICENSED_APP_OP_IN_OP_MASK 0x1
/* arguments specific to this particular operation */
#define MC_CMD_LICENSED_APP_OP_IN_ARGS_OFST 8
#define MC_CMD_LICENSED_APP_OP_IN_ARGS_LEN 4
@@ -12984,9 +13714,9 @@
#define MC_CMD_LICENSED_V3_VALIDATE_APP_OUT_EXPIRY_UNITS_OFST 100
#define MC_CMD_LICENSED_V3_VALIDATE_APP_OUT_EXPIRY_UNITS_LEN 4
/* enum: expiry units are accounting units */
-#define MC_CMD_LICENSED_V3_VALIDATE_APP_OUT_EXPIRY_UNIT_ACC 0x0
+#define MC_CMD_LICENSED_V3_VALIDATE_APP_OUT_EXPIRY_UNIT_ACC 0x0
/* enum: expiry units are calendar days */
-#define MC_CMD_LICENSED_V3_VALIDATE_APP_OUT_EXPIRY_UNIT_DAYS 0x1
+#define MC_CMD_LICENSED_V3_VALIDATE_APP_OUT_EXPIRY_UNIT_DAYS 0x1
/* base MAC address of the NIC stored in NVRAM (note that this is a constant
* value for a given NIC regardless which function is calling, effectively this
* is PF0 base MAC address)
@@ -13019,9 +13749,9 @@
#define MC_CMD_LICENSED_V3_MASK_FEATURES_IN_FLAG_OFST 8
#define MC_CMD_LICENSED_V3_MASK_FEATURES_IN_FLAG_LEN 4
/* enum: turn the features off */
-#define MC_CMD_LICENSED_V3_MASK_FEATURES_IN_OFF 0x0
+#define MC_CMD_LICENSED_V3_MASK_FEATURES_IN_OFF 0x0
/* enum: turn the features back on */
-#define MC_CMD_LICENSED_V3_MASK_FEATURES_IN_ON 0x1
+#define MC_CMD_LICENSED_V3_MASK_FEATURES_IN_ON 0x1
/* MC_CMD_LICENSED_V3_MASK_FEATURES_OUT msgresponse */
#define MC_CMD_LICENSED_V3_MASK_FEATURES_OUT_LEN 0
@@ -13048,15 +13778,15 @@
* This is an asynchronous operation owing to the time taken to validate an
* ECDSA license
*/
-#define MC_CMD_LICENSING_V3_TEMPORARY_SET 0x0
+#define MC_CMD_LICENSING_V3_TEMPORARY_SET 0x0
/* enum: clear the license immediately rather than waiting for the next power
* cycle
*/
-#define MC_CMD_LICENSING_V3_TEMPORARY_CLEAR 0x1
+#define MC_CMD_LICENSING_V3_TEMPORARY_CLEAR 0x1
/* enum: get the status of the asynchronous MC_CMD_LICENSING_V3_TEMPORARY_SET
* operation
*/
-#define MC_CMD_LICENSING_V3_TEMPORARY_STATUS 0x2
+#define MC_CMD_LICENSING_V3_TEMPORARY_STATUS 0x2
/* MC_CMD_LICENSING_V3_TEMPORARY_IN_SET msgrequest */
#define MC_CMD_LICENSING_V3_TEMPORARY_IN_SET_LEN 164
@@ -13082,13 +13812,13 @@
#define MC_CMD_LICENSING_V3_TEMPORARY_OUT_STATUS_STATUS_OFST 0
#define MC_CMD_LICENSING_V3_TEMPORARY_OUT_STATUS_STATUS_LEN 4
/* enum: finished validating and installing license */
-#define MC_CMD_LICENSING_V3_TEMPORARY_STATUS_OK 0x0
+#define MC_CMD_LICENSING_V3_TEMPORARY_STATUS_OK 0x0
/* enum: license validation and installation in progress */
-#define MC_CMD_LICENSING_V3_TEMPORARY_STATUS_IN_PROGRESS 0x1
+#define MC_CMD_LICENSING_V3_TEMPORARY_STATUS_IN_PROGRESS 0x1
/* enum: licensing error. More specific error messages are not provided to
* avoid exposing details of the licensing system to the client
*/
-#define MC_CMD_LICENSING_V3_TEMPORARY_STATUS_ERROR 0x2
+#define MC_CMD_LICENSING_V3_TEMPORARY_STATUS_ERROR 0x2
/* bitmask of licensed features */
#define MC_CMD_LICENSING_V3_TEMPORARY_OUT_STATUS_LICENSED_FEATURES_OFST 4
#define MC_CMD_LICENSING_V3_TEMPORARY_OUT_STATUS_LICENSED_FEATURES_LEN 8
@@ -13124,9 +13854,9 @@
#define MC_CMD_SET_PORT_SNIFF_CONFIG_IN_RX_MODE_OFST 8
#define MC_CMD_SET_PORT_SNIFF_CONFIG_IN_RX_MODE_LEN 4
/* enum: receive to just the specified queue */
-#define MC_CMD_SET_PORT_SNIFF_CONFIG_IN_RX_MODE_SIMPLE 0x0
+#define MC_CMD_SET_PORT_SNIFF_CONFIG_IN_RX_MODE_SIMPLE 0x0
/* enum: receive to multiple queues using RSS context */
-#define MC_CMD_SET_PORT_SNIFF_CONFIG_IN_RX_MODE_RSS 0x1
+#define MC_CMD_SET_PORT_SNIFF_CONFIG_IN_RX_MODE_RSS 0x1
/* RSS context (for RX_MODE_RSS) as returned by MC_CMD_RSS_CONTEXT_ALLOC. Note
* that these handles should be considered opaque to the host, although a value
* of 0xFFFFFFFF is guaranteed never to be a valid handle.
@@ -13146,7 +13876,7 @@
*/
#define MC_CMD_GET_PORT_SNIFF_CONFIG 0xf8
-#define MC_CMD_0xf8_PRIVILEGE_CTG SRIOV_CTG_ADMIN
+#define MC_CMD_0xf8_PRIVILEGE_CTG SRIOV_CTG_GENERAL
/* MC_CMD_GET_PORT_SNIFF_CONFIG_IN msgrequest */
#define MC_CMD_GET_PORT_SNIFF_CONFIG_IN_LEN 0
@@ -13167,9 +13897,9 @@
#define MC_CMD_GET_PORT_SNIFF_CONFIG_OUT_RX_MODE_OFST 8
#define MC_CMD_GET_PORT_SNIFF_CONFIG_OUT_RX_MODE_LEN 4
/* enum: receiving to just the specified queue */
-#define MC_CMD_GET_PORT_SNIFF_CONFIG_OUT_RX_MODE_SIMPLE 0x0
+#define MC_CMD_GET_PORT_SNIFF_CONFIG_OUT_RX_MODE_SIMPLE 0x0
/* enum: receiving to multiple queues using RSS context */
-#define MC_CMD_GET_PORT_SNIFF_CONFIG_OUT_RX_MODE_RSS 0x1
+#define MC_CMD_GET_PORT_SNIFF_CONFIG_OUT_RX_MODE_RSS 0x1
/* RSS context (for RX_MODE_RSS) */
#define MC_CMD_GET_PORT_SNIFF_CONFIG_OUT_RX_CONTEXT_OFST 12
#define MC_CMD_GET_PORT_SNIFF_CONFIG_OUT_RX_CONTEXT_LEN 4
@@ -13193,12 +13923,12 @@
/* enum: Per-TXQ enable for multicast UDP destination lookup for possible
* internal loopback. (ENTITY is a queue handle, VALUE is a single boolean.)
*/
-#define MC_CMD_SET_PARSER_DISP_CONFIG_IN_TXQ_MCAST_UDP_DST_LOOKUP_EN 0x0
+#define MC_CMD_SET_PARSER_DISP_CONFIG_IN_TXQ_MCAST_UDP_DST_LOOKUP_EN 0x0
/* enum: Per-v-adaptor enable for suppression of self-transmissions on the
* internal loopback path. (ENTITY is an EVB_PORT_ID, VALUE is a single
* boolean.)
*/
-#define MC_CMD_SET_PARSER_DISP_CONFIG_IN_VADAPTOR_SUPPRESS_SELF_TX 0x1
+#define MC_CMD_SET_PARSER_DISP_CONFIG_IN_VADAPTOR_SUPPRESS_SELF_TX 0x1
/* handle for the entity to update: queue handle, EVB port ID, etc. depending
* on the type of configuration setting being changed
*/
@@ -13278,9 +14008,9 @@
#define MC_CMD_SET_TX_PORT_SNIFF_CONFIG_IN_RX_MODE_OFST 8
#define MC_CMD_SET_TX_PORT_SNIFF_CONFIG_IN_RX_MODE_LEN 4
/* enum: receive to just the specified queue */
-#define MC_CMD_SET_TX_PORT_SNIFF_CONFIG_IN_RX_MODE_SIMPLE 0x0
+#define MC_CMD_SET_TX_PORT_SNIFF_CONFIG_IN_RX_MODE_SIMPLE 0x0
/* enum: receive to multiple queues using RSS context */
-#define MC_CMD_SET_TX_PORT_SNIFF_CONFIG_IN_RX_MODE_RSS 0x1
+#define MC_CMD_SET_TX_PORT_SNIFF_CONFIG_IN_RX_MODE_RSS 0x1
/* RSS context (for RX_MODE_RSS) as returned by MC_CMD_RSS_CONTEXT_ALLOC. Note
* that these handles should be considered opaque to the host, although a value
* of 0xFFFFFFFF is guaranteed never to be a valid handle.
@@ -13300,7 +14030,7 @@
*/
#define MC_CMD_GET_TX_PORT_SNIFF_CONFIG 0xfc
-#define MC_CMD_0xfc_PRIVILEGE_CTG SRIOV_CTG_ADMIN
+#define MC_CMD_0xfc_PRIVILEGE_CTG SRIOV_CTG_GENERAL
/* MC_CMD_GET_TX_PORT_SNIFF_CONFIG_IN msgrequest */
#define MC_CMD_GET_TX_PORT_SNIFF_CONFIG_IN_LEN 0
@@ -13319,9 +14049,9 @@
#define MC_CMD_GET_TX_PORT_SNIFF_CONFIG_OUT_RX_MODE_OFST 8
#define MC_CMD_GET_TX_PORT_SNIFF_CONFIG_OUT_RX_MODE_LEN 4
/* enum: receiving to just the specified queue */
-#define MC_CMD_GET_TX_PORT_SNIFF_CONFIG_OUT_RX_MODE_SIMPLE 0x0
+#define MC_CMD_GET_TX_PORT_SNIFF_CONFIG_OUT_RX_MODE_SIMPLE 0x0
/* enum: receiving to multiple queues using RSS context */
-#define MC_CMD_GET_TX_PORT_SNIFF_CONFIG_OUT_RX_MODE_RSS 0x1
+#define MC_CMD_GET_TX_PORT_SNIFF_CONFIG_OUT_RX_MODE_RSS 0x1
/* RSS context (for RX_MODE_RSS) */
#define MC_CMD_GET_TX_PORT_SNIFF_CONFIG_OUT_RX_CONTEXT_OFST 12
#define MC_CMD_GET_TX_PORT_SNIFF_CONFIG_OUT_RX_CONTEXT_LEN 4
@@ -13431,9 +14161,9 @@
#define MC_CMD_READ_ATB_IN_LEN 16
#define MC_CMD_READ_ATB_IN_SIGNAL_BUS_OFST 0
#define MC_CMD_READ_ATB_IN_SIGNAL_BUS_LEN 4
-#define MC_CMD_READ_ATB_IN_BUS_CCOM 0x0 /* enum */
-#define MC_CMD_READ_ATB_IN_BUS_CKR 0x1 /* enum */
-#define MC_CMD_READ_ATB_IN_BUS_CPCIE 0x8 /* enum */
+#define MC_CMD_READ_ATB_IN_BUS_CCOM 0x0 /* enum */
+#define MC_CMD_READ_ATB_IN_BUS_CKR 0x1 /* enum */
+#define MC_CMD_READ_ATB_IN_BUS_CPCIE 0x8 /* enum */
#define MC_CMD_READ_ATB_IN_SIGNAL_EN_BITNO_OFST 4
#define MC_CMD_READ_ATB_IN_SIGNAL_EN_BITNO_LEN 4
#define MC_CMD_READ_ATB_IN_SIGNAL_SEL_OFST 8
@@ -13503,46 +14233,46 @@
#define MC_CMD_PRIVILEGE_MASK_IN_FUNCTION_PF_WIDTH 16
#define MC_CMD_PRIVILEGE_MASK_IN_FUNCTION_VF_LBN 16
#define MC_CMD_PRIVILEGE_MASK_IN_FUNCTION_VF_WIDTH 16
-#define MC_CMD_PRIVILEGE_MASK_IN_VF_NULL 0xffff /* enum */
+#define MC_CMD_PRIVILEGE_MASK_IN_VF_NULL 0xffff /* enum */
/* New privilege mask to be set. The mask will only be changed if the MSB is
* set to 1.
*/
#define MC_CMD_PRIVILEGE_MASK_IN_NEW_MASK_OFST 4
#define MC_CMD_PRIVILEGE_MASK_IN_NEW_MASK_LEN 4
-#define MC_CMD_PRIVILEGE_MASK_IN_GRP_ADMIN 0x1 /* enum */
-#define MC_CMD_PRIVILEGE_MASK_IN_GRP_LINK 0x2 /* enum */
-#define MC_CMD_PRIVILEGE_MASK_IN_GRP_ONLOAD 0x4 /* enum */
-#define MC_CMD_PRIVILEGE_MASK_IN_GRP_PTP 0x8 /* enum */
-#define MC_CMD_PRIVILEGE_MASK_IN_GRP_INSECURE_FILTERS 0x10 /* enum */
+#define MC_CMD_PRIVILEGE_MASK_IN_GRP_ADMIN 0x1 /* enum */
+#define MC_CMD_PRIVILEGE_MASK_IN_GRP_LINK 0x2 /* enum */
+#define MC_CMD_PRIVILEGE_MASK_IN_GRP_ONLOAD 0x4 /* enum */
+#define MC_CMD_PRIVILEGE_MASK_IN_GRP_PTP 0x8 /* enum */
+#define MC_CMD_PRIVILEGE_MASK_IN_GRP_INSECURE_FILTERS 0x10 /* enum */
/* enum: Deprecated. Equivalent to MAC_SPOOFING_TX combined with CHANGE_MAC. */
-#define MC_CMD_PRIVILEGE_MASK_IN_GRP_MAC_SPOOFING 0x20
-#define MC_CMD_PRIVILEGE_MASK_IN_GRP_UNICAST 0x40 /* enum */
-#define MC_CMD_PRIVILEGE_MASK_IN_GRP_MULTICAST 0x80 /* enum */
-#define MC_CMD_PRIVILEGE_MASK_IN_GRP_BROADCAST 0x100 /* enum */
-#define MC_CMD_PRIVILEGE_MASK_IN_GRP_ALL_MULTICAST 0x200 /* enum */
-#define MC_CMD_PRIVILEGE_MASK_IN_GRP_PROMISCUOUS 0x400 /* enum */
+#define MC_CMD_PRIVILEGE_MASK_IN_GRP_MAC_SPOOFING 0x20
+#define MC_CMD_PRIVILEGE_MASK_IN_GRP_UNICAST 0x40 /* enum */
+#define MC_CMD_PRIVILEGE_MASK_IN_GRP_MULTICAST 0x80 /* enum */
+#define MC_CMD_PRIVILEGE_MASK_IN_GRP_BROADCAST 0x100 /* enum */
+#define MC_CMD_PRIVILEGE_MASK_IN_GRP_ALL_MULTICAST 0x200 /* enum */
+#define MC_CMD_PRIVILEGE_MASK_IN_GRP_PROMISCUOUS 0x400 /* enum */
/* enum: Allows to set the TX packets' source MAC address to any arbitrary MAC
* adress.
*/
-#define MC_CMD_PRIVILEGE_MASK_IN_GRP_MAC_SPOOFING_TX 0x800
+#define MC_CMD_PRIVILEGE_MASK_IN_GRP_MAC_SPOOFING_TX 0x800
/* enum: Privilege that allows a Function to change the MAC address configured
* in its associated vAdapter/vPort.
*/
-#define MC_CMD_PRIVILEGE_MASK_IN_GRP_CHANGE_MAC 0x1000
+#define MC_CMD_PRIVILEGE_MASK_IN_GRP_CHANGE_MAC 0x1000
/* enum: Privilege that allows a Function to install filters that specify VLANs
* that are not in the permit list for the associated vPort. This privilege is
* primarily to support ESX where vPorts are created that restrict traffic to
* only a set of permitted VLANs. See the vPort flag FLAG_VLAN_RESTRICT.
*/
-#define MC_CMD_PRIVILEGE_MASK_IN_GRP_UNRESTRICTED_VLAN 0x2000
+#define MC_CMD_PRIVILEGE_MASK_IN_GRP_UNRESTRICTED_VLAN 0x2000
/* enum: Privilege for insecure commands. Commands that belong to this group
* are not permitted on secure adapters regardless of the privilege mask.
*/
-#define MC_CMD_PRIVILEGE_MASK_IN_GRP_INSECURE 0x4000
+#define MC_CMD_PRIVILEGE_MASK_IN_GRP_INSECURE 0x4000
/* enum: Set this bit to indicate that a new privilege mask is to be set,
* otherwise the command will only read the existing mask.
*/
-#define MC_CMD_PRIVILEGE_MASK_IN_DO_CHANGE 0x80000000
+#define MC_CMD_PRIVILEGE_MASK_IN_DO_CHANGE 0x80000000
/* MC_CMD_PRIVILEGE_MASK_OUT msgresponse */
#define MC_CMD_PRIVILEGE_MASK_OUT_LEN 4
@@ -13573,12 +14303,12 @@
/* New link state mode to be set */
#define MC_CMD_LINK_STATE_MODE_IN_NEW_MODE_OFST 4
#define MC_CMD_LINK_STATE_MODE_IN_NEW_MODE_LEN 4
-#define MC_CMD_LINK_STATE_MODE_IN_LINK_STATE_AUTO 0x0 /* enum */
-#define MC_CMD_LINK_STATE_MODE_IN_LINK_STATE_UP 0x1 /* enum */
-#define MC_CMD_LINK_STATE_MODE_IN_LINK_STATE_DOWN 0x2 /* enum */
+#define MC_CMD_LINK_STATE_MODE_IN_LINK_STATE_AUTO 0x0 /* enum */
+#define MC_CMD_LINK_STATE_MODE_IN_LINK_STATE_UP 0x1 /* enum */
+#define MC_CMD_LINK_STATE_MODE_IN_LINK_STATE_DOWN 0x2 /* enum */
/* enum: Use this value to just read the existing setting without modifying it.
*/
-#define MC_CMD_LINK_STATE_MODE_IN_DO_NOT_CHANGE 0xffffffff
+#define MC_CMD_LINK_STATE_MODE_IN_DO_NOT_CHANGE 0xffffffff
/* MC_CMD_LINK_STATE_MODE_OUT msgresponse */
#define MC_CMD_LINK_STATE_MODE_OUT_LEN 4
@@ -13674,12 +14404,12 @@
/* The groups of functions to have their privilege masks modified. */
#define MC_CMD_PRIVILEGE_MODIFY_IN_FN_GROUP_OFST 0
#define MC_CMD_PRIVILEGE_MODIFY_IN_FN_GROUP_LEN 4
-#define MC_CMD_PRIVILEGE_MODIFY_IN_NONE 0x0 /* enum */
-#define MC_CMD_PRIVILEGE_MODIFY_IN_ALL 0x1 /* enum */
-#define MC_CMD_PRIVILEGE_MODIFY_IN_PFS_ONLY 0x2 /* enum */
-#define MC_CMD_PRIVILEGE_MODIFY_IN_VFS_ONLY 0x3 /* enum */
-#define MC_CMD_PRIVILEGE_MODIFY_IN_VFS_OF_PF 0x4 /* enum */
-#define MC_CMD_PRIVILEGE_MODIFY_IN_ONE 0x5 /* enum */
+#define MC_CMD_PRIVILEGE_MODIFY_IN_NONE 0x0 /* enum */
+#define MC_CMD_PRIVILEGE_MODIFY_IN_ALL 0x1 /* enum */
+#define MC_CMD_PRIVILEGE_MODIFY_IN_PFS_ONLY 0x2 /* enum */
+#define MC_CMD_PRIVILEGE_MODIFY_IN_VFS_ONLY 0x3 /* enum */
+#define MC_CMD_PRIVILEGE_MODIFY_IN_VFS_OF_PF 0x4 /* enum */
+#define MC_CMD_PRIVILEGE_MODIFY_IN_ONE 0x5 /* enum */
/* For VFS_OF_PF specify the PF, for ONE specify the target function */
#define MC_CMD_PRIVILEGE_MODIFY_IN_FUNCTION_OFST 4
#define MC_CMD_PRIVILEGE_MODIFY_IN_FUNCTION_LEN 4
@@ -13782,11 +14512,11 @@
/* Sector type */
#define MC_CMD_XPM_READ_SECTOR_OUT_TYPE_OFST 0
#define MC_CMD_XPM_READ_SECTOR_OUT_TYPE_LEN 4
-#define MC_CMD_XPM_READ_SECTOR_OUT_BLANK 0x0 /* enum */
-#define MC_CMD_XPM_READ_SECTOR_OUT_CRYPTO_KEY_128 0x1 /* enum */
-#define MC_CMD_XPM_READ_SECTOR_OUT_CRYPTO_KEY_256 0x2 /* enum */
-#define MC_CMD_XPM_READ_SECTOR_OUT_CRYPTO_DATA 0x3 /* enum */
-#define MC_CMD_XPM_READ_SECTOR_OUT_INVALID 0xff /* enum */
+#define MC_CMD_XPM_READ_SECTOR_OUT_BLANK 0x0 /* enum */
+#define MC_CMD_XPM_READ_SECTOR_OUT_CRYPTO_KEY_128 0x1 /* enum */
+#define MC_CMD_XPM_READ_SECTOR_OUT_CRYPTO_KEY_256 0x2 /* enum */
+#define MC_CMD_XPM_READ_SECTOR_OUT_CRYPTO_DATA 0x3 /* enum */
+#define MC_CMD_XPM_READ_SECTOR_OUT_INVALID 0xff /* enum */
/* Sector data */
#define MC_CMD_XPM_READ_SECTOR_OUT_DATA_OFST 4
#define MC_CMD_XPM_READ_SECTOR_OUT_DATA_LEN 1
@@ -14001,18 +14731,18 @@
#define TUNNEL_ENCAP_UDP_PORT_ENTRY_UDP_PORT_OFST 0
#define TUNNEL_ENCAP_UDP_PORT_ENTRY_UDP_PORT_LEN 2
/* enum: the IANA allocated UDP port for VXLAN */
-#define TUNNEL_ENCAP_UDP_PORT_ENTRY_IANA_VXLAN_UDP_PORT 0x12b5
+#define TUNNEL_ENCAP_UDP_PORT_ENTRY_IANA_VXLAN_UDP_PORT 0x12b5
/* enum: the IANA allocated UDP port for Geneve */
-#define TUNNEL_ENCAP_UDP_PORT_ENTRY_IANA_GENEVE_UDP_PORT 0x17c1
+#define TUNNEL_ENCAP_UDP_PORT_ENTRY_IANA_GENEVE_UDP_PORT 0x17c1
#define TUNNEL_ENCAP_UDP_PORT_ENTRY_UDP_PORT_LBN 0
#define TUNNEL_ENCAP_UDP_PORT_ENTRY_UDP_PORT_WIDTH 16
/* tunnel encapsulation protocol (only those named below are supported) */
#define TUNNEL_ENCAP_UDP_PORT_ENTRY_PROTOCOL_OFST 2
#define TUNNEL_ENCAP_UDP_PORT_ENTRY_PROTOCOL_LEN 2
/* enum: This port will be used for VXLAN on both IPv4 and IPv6 */
-#define TUNNEL_ENCAP_UDP_PORT_ENTRY_VXLAN 0x0
+#define TUNNEL_ENCAP_UDP_PORT_ENTRY_VXLAN 0x0
/* enum: This port will be used for Geneve on both IPv4 and IPv6 */
-#define TUNNEL_ENCAP_UDP_PORT_ENTRY_GENEVE 0x1
+#define TUNNEL_ENCAP_UDP_PORT_ENTRY_GENEVE 0x1
#define TUNNEL_ENCAP_UDP_PORT_ENTRY_PROTOCOL_LBN 16
#define TUNNEL_ENCAP_UDP_PORT_ENTRY_PROTOCOL_WIDTH 16
@@ -14180,10 +14910,10 @@
/* Timer mode. Meanings as per EVQ_TMR_REG.TC_TIMER_VAL */
#define MC_CMD_SET_EVQ_TMR_IN_TMR_MODE_OFST 12
#define MC_CMD_SET_EVQ_TMR_IN_TMR_MODE_LEN 4
-#define MC_CMD_SET_EVQ_TMR_IN_TIMER_MODE_DIS 0x0 /* enum */
-#define MC_CMD_SET_EVQ_TMR_IN_TIMER_MODE_IMMED_START 0x1 /* enum */
-#define MC_CMD_SET_EVQ_TMR_IN_TIMER_MODE_TRIG_START 0x2 /* enum */
-#define MC_CMD_SET_EVQ_TMR_IN_TIMER_MODE_INT_HLDOFF 0x3 /* enum */
+#define MC_CMD_SET_EVQ_TMR_IN_TIMER_MODE_DIS 0x0 /* enum */
+#define MC_CMD_SET_EVQ_TMR_IN_TIMER_MODE_IMMED_START 0x1 /* enum */
+#define MC_CMD_SET_EVQ_TMR_IN_TIMER_MODE_TRIG_START 0x2 /* enum */
+#define MC_CMD_SET_EVQ_TMR_IN_TIMER_MODE_INT_HLDOFF 0x3 /* enum */
/* MC_CMD_SET_EVQ_TMR_OUT msgresponse */
#define MC_CMD_SET_EVQ_TMR_OUT_LEN 8
@@ -14269,7 +14999,7 @@
*/
#define MC_CMD_ALLOCATE_TX_VFIFO_CP 0x11d
-#define MC_CMD_0x11d_PRIVILEGE_CTG SRIOV_CTG_ADMIN
+#define MC_CMD_0x11d_PRIVILEGE_CTG SRIOV_CTG_GENERAL
/* MC_CMD_ALLOCATE_TX_VFIFO_CP_IN msgrequest */
#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_LEN 20
@@ -14281,9 +15011,9 @@
/* Will the common pool be used as TX_vFIFO_ULL (1) */
#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_MODE_OFST 4
#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_MODE_LEN 4
-#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_ENABLED 0x1 /* enum */
+#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_ENABLED 0x1 /* enum */
/* enum: Using this interface without TX_vFIFO_ULL is not supported for now */
-#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_DISABLED 0x0
+#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_DISABLED 0x0
/* Number of buffers to reserve for the common pool */
#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_SIZE_OFST 8
#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_SIZE_LEN 4
@@ -14291,20 +15021,20 @@
#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_INGRESS_OFST 12
#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_INGRESS_LEN 4
/* enum: Extracts information from function */
-#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_USE_FUNCTION_VALUE -0x1
+#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_USE_FUNCTION_VALUE -0x1
/* Network port or RX Engine to which the common pool connects. */
#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_EGRESS_OFST 16
#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_EGRESS_LEN 4
/* enum: Extracts information from function */
-/* MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_USE_FUNCTION_VALUE -0x1 */
-#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_PORT0 0x0 /* enum */
-#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_PORT1 0x1 /* enum */
-#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_PORT2 0x2 /* enum */
-#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_PORT3 0x3 /* enum */
+/* MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_USE_FUNCTION_VALUE -0x1 */
+#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_PORT0 0x0 /* enum */
+#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_PORT1 0x1 /* enum */
+#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_PORT2 0x2 /* enum */
+#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_PORT3 0x3 /* enum */
/* enum: To enable Switch loopback with Rx engine 0 */
-#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_RX_ENGINE0 0x4
+#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_RX_ENGINE0 0x4
/* enum: To enable Switch loopback with Rx engine 1 */
-#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_RX_ENGINE1 0x5
+#define MC_CMD_ALLOCATE_TX_VFIFO_CP_IN_RX_ENGINE1 0x5
/* MC_CMD_ALLOCATE_TX_VFIFO_CP_OUT msgresponse */
#define MC_CMD_ALLOCATE_TX_VFIFO_CP_OUT_LEN 4
@@ -14320,7 +15050,7 @@
*/
#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO 0x11e
-#define MC_CMD_0x11e_PRIVILEGE_CTG SRIOV_CTG_ADMIN
+#define MC_CMD_0x11e_PRIVILEGE_CTG SRIOV_CTG_GENERAL
/* MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN msgrequest */
#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_LEN 20
@@ -14332,20 +15062,20 @@
#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_EGRESS_OFST 4
#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_EGRESS_LEN 4
/* enum: Extracts information from common pool */
-#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_USE_CP_VALUE -0x1
-#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_PORT0 0x0 /* enum */
-#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_PORT1 0x1 /* enum */
-#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_PORT2 0x2 /* enum */
-#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_PORT3 0x3 /* enum */
+#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_USE_CP_VALUE -0x1
+#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_PORT0 0x0 /* enum */
+#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_PORT1 0x1 /* enum */
+#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_PORT2 0x2 /* enum */
+#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_PORT3 0x3 /* enum */
/* enum: To enable Switch loopback with Rx engine 0 */
-#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_RX_ENGINE0 0x4
+#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_RX_ENGINE0 0x4
/* enum: To enable Switch loopback with Rx engine 1 */
-#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_RX_ENGINE1 0x5
+#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_RX_ENGINE1 0x5
/* Minimum number of buffers that the pool must have */
#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_SIZE_OFST 8
#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_SIZE_LEN 4
/* enum: Do not check the space available */
-#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_NO_MINIMUM 0x0
+#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_NO_MINIMUM 0x0
/* Will the vFIFO be used as TX_vFIFO_ULL */
#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_MODE_OFST 12
#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_MODE_LEN 4
@@ -14353,7 +15083,7 @@
#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_PRIORITY_OFST 16
#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_PRIORITY_LEN 4
/* enum: Search for the lowest unused priority */
-#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_LOWEST_AVAILABLE -0x1
+#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_IN_LOWEST_AVAILABLE -0x1
/* MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_OUT msgresponse */
#define MC_CMD_ALLOCATE_TX_VFIFO_VFIFO_OUT_LEN 8
@@ -14372,7 +15102,7 @@
*/
#define MC_CMD_TEARDOWN_TX_VFIFO_VF 0x11f
-#define MC_CMD_0x11f_PRIVILEGE_CTG SRIOV_CTG_ADMIN
+#define MC_CMD_0x11f_PRIVILEGE_CTG SRIOV_CTG_GENERAL
/* MC_CMD_TEARDOWN_TX_VFIFO_VF_IN msgrequest */
#define MC_CMD_TEARDOWN_TX_VFIFO_VF_IN_LEN 4
@@ -14391,7 +15121,7 @@
*/
#define MC_CMD_DEALLOCATE_TX_VFIFO_CP 0x121
-#define MC_CMD_0x121_PRIVILEGE_CTG SRIOV_CTG_ADMIN
+#define MC_CMD_0x121_PRIVILEGE_CTG SRIOV_CTG_GENERAL
/* MC_CMD_DEALLOCATE_TX_VFIFO_CP_IN msgrequest */
#define MC_CMD_DEALLOCATE_TX_VFIFO_CP_IN_LEN 4
@@ -14410,7 +15140,7 @@
*/
#define MC_CMD_SWITCH_GET_UNASSIGNED_BUFFERS 0x124
-#define MC_CMD_0x124_PRIVILEGE_CTG SRIOV_CTG_ADMIN
+#define MC_CMD_0x124_PRIVILEGE_CTG SRIOV_CTG_GENERAL
/* MC_CMD_SWITCH_GET_UNASSIGNED_BUFFERS_IN msgrequest */
#define MC_CMD_SWITCH_GET_UNASSIGNED_BUFFERS_IN_LEN 0
diff --git a/drivers/net/ethernet/sfc/mcdi_port.c b/drivers/net/ethernet/sfc/mcdi_port.c
index ce8aabf9091e..9382bb0b4d5a 100644
--- a/drivers/net/ethernet/sfc/mcdi_port.c
+++ b/drivers/net/ethernet/sfc/mcdi_port.c
@@ -352,6 +352,64 @@ static void efx_mcdi_phy_decode_link(struct efx_nic *efx,
link_state->speed = speed;
}
+/* The semantics of the ethtool FEC mode bitmask are not well defined,
+ * particularly the meaning of combinations of bits. Which means we get to
+ * define our own semantics, as follows:
+ * OFF overrides any other bits, and means "disable all FEC" (with the
+ * exception of 25G KR4/CR4, where it is not possible to reject it if AN
+ * partner requests it).
+ * AUTO on its own means use cable requirements and link partner autoneg with
+ * fw-default preferences for the cable type.
+ * AUTO and either RS or BASER means use the specified FEC type if cable and
+ * link partner support it, otherwise autoneg/fw-default.
+ * RS or BASER alone means use the specified FEC type if cable and link partner
+ * support it and either requests it, otherwise no FEC.
+ * Both RS and BASER (whether AUTO or not) means use FEC if cable and link
+ * partner support it, preferring RS to BASER.
+ */
+static u32 ethtool_fec_caps_to_mcdi(u32 ethtool_cap)
+{
+ u32 ret = 0;
+
+ if (ethtool_cap & ETHTOOL_FEC_OFF)
+ return 0;
+
+ if (ethtool_cap & ETHTOOL_FEC_AUTO)
+ ret |= (1 << MC_CMD_PHY_CAP_BASER_FEC_LBN) |
+ (1 << MC_CMD_PHY_CAP_25G_BASER_FEC_LBN) |
+ (1 << MC_CMD_PHY_CAP_RS_FEC_LBN);
+ if (ethtool_cap & ETHTOOL_FEC_RS)
+ ret |= (1 << MC_CMD_PHY_CAP_RS_FEC_LBN) |
+ (1 << MC_CMD_PHY_CAP_RS_FEC_REQUESTED_LBN);
+ if (ethtool_cap & ETHTOOL_FEC_BASER)
+ ret |= (1 << MC_CMD_PHY_CAP_BASER_FEC_LBN) |
+ (1 << MC_CMD_PHY_CAP_25G_BASER_FEC_LBN) |
+ (1 << MC_CMD_PHY_CAP_BASER_FEC_REQUESTED_LBN) |
+ (1 << MC_CMD_PHY_CAP_25G_BASER_FEC_REQUESTED_LBN);
+ return ret;
+}
+
+/* Invert ethtool_fec_caps_to_mcdi. There are two combinations that function
+ * can never produce, (baser xor rs) and neither req; the implementation below
+ * maps both of those to AUTO. This should never matter, and it's not clear
+ * what a better mapping would be anyway.
+ */
+static u32 mcdi_fec_caps_to_ethtool(u32 caps, bool is_25g)
+{
+ bool rs = caps & (1 << MC_CMD_PHY_CAP_RS_FEC_LBN),
+ rs_req = caps & (1 << MC_CMD_PHY_CAP_RS_FEC_REQUESTED_LBN),
+ baser = is_25g ? caps & (1 << MC_CMD_PHY_CAP_25G_BASER_FEC_LBN)
+ : caps & (1 << MC_CMD_PHY_CAP_BASER_FEC_LBN),
+ baser_req = is_25g ? caps & (1 << MC_CMD_PHY_CAP_25G_BASER_FEC_REQUESTED_LBN)
+ : caps & (1 << MC_CMD_PHY_CAP_BASER_FEC_REQUESTED_LBN);
+
+ if (!baser && !rs)
+ return ETHTOOL_FEC_OFF;
+ return (rs_req ? ETHTOOL_FEC_RS : 0) |
+ (baser_req ? ETHTOOL_FEC_BASER : 0) |
+ (baser == baser_req && rs == rs_req ? 0 : ETHTOOL_FEC_AUTO);
+}
+
static int efx_mcdi_phy_probe(struct efx_nic *efx)
{
struct efx_mcdi_phy_data *phy_data;
@@ -438,6 +496,13 @@ static int efx_mcdi_phy_probe(struct efx_nic *efx)
MCDI_DWORD(outbuf, GET_LINK_OUT_FLAGS),
MCDI_DWORD(outbuf, GET_LINK_OUT_FCNTL));
+ /* Record the initial FEC configuration (or nearest approximation
+ * representable in the ethtool configuration space)
+ */
+ efx->fec_config = mcdi_fec_caps_to_ethtool(caps,
+ efx->link_state.speed == 25000 ||
+ efx->link_state.speed == 50000);
+
/* Default to Autonegotiated flow control if the PHY supports it */
efx->wanted_fc = EFX_FC_RX | EFX_FC_TX;
if (phy_data->supported_cap & (1 << MC_CMD_PHY_CAP_AN_LBN))
@@ -458,6 +523,8 @@ int efx_mcdi_port_reconfigure(struct efx_nic *efx)
ethtool_linkset_to_mcdi_cap(efx->link_advertising) :
phy_cfg->forced_cap);
+ caps |= ethtool_fec_caps_to_mcdi(efx->fec_config);
+
return efx_mcdi_set_link(efx, caps, efx_get_mcdi_phy_flags(efx),
efx->loopback_mode, 0);
}
@@ -584,6 +651,8 @@ efx_mcdi_phy_set_link_ksettings(struct efx_nic *efx,
}
}
+ caps |= ethtool_fec_caps_to_mcdi(efx->fec_config);
+
rc = efx_mcdi_set_link(efx, caps, efx_get_mcdi_phy_flags(efx),
efx->loopback_mode, 0);
if (rc)
@@ -599,6 +668,85 @@ efx_mcdi_phy_set_link_ksettings(struct efx_nic *efx,
return 0;
}
+static int efx_mcdi_phy_get_fecparam(struct efx_nic *efx,
+ struct ethtool_fecparam *fec)
+{
+ MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_LINK_OUT_V2_LEN);
+ u32 caps, active, speed; /* MCDI format */
+ bool is_25g = false;
+ size_t outlen;
+ int rc;
+
+ BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0);
+ rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0,
+ outbuf, sizeof(outbuf), &outlen);
+ if (rc)
+ return rc;
+ if (outlen < MC_CMD_GET_LINK_OUT_V2_LEN)
+ return -EOPNOTSUPP;
+
+ /* behaviour for 25G/50G links depends on 25G BASER bit */
+ speed = MCDI_DWORD(outbuf, GET_LINK_OUT_V2_LINK_SPEED);
+ is_25g = speed == 25000 || speed == 50000;
+
+ caps = MCDI_DWORD(outbuf, GET_LINK_OUT_V2_CAP);
+ fec->fec = mcdi_fec_caps_to_ethtool(caps, is_25g);
+ /* BASER is never supported on 100G */
+ if (speed == 100000)
+ fec->fec &= ~ETHTOOL_FEC_BASER;
+
+ active = MCDI_DWORD(outbuf, GET_LINK_OUT_V2_FEC_TYPE);
+ switch (active) {
+ case MC_CMD_FEC_NONE:
+ fec->active_fec = ETHTOOL_FEC_OFF;
+ break;
+ case MC_CMD_FEC_BASER:
+ fec->active_fec = ETHTOOL_FEC_BASER;
+ break;
+ case MC_CMD_FEC_RS:
+ fec->active_fec = ETHTOOL_FEC_RS;
+ break;
+ default:
+ netif_warn(efx, hw, efx->net_dev,
+ "Firmware reports unrecognised FEC_TYPE %u\n",
+ active);
+ /* We don't know what firmware has picked. AUTO is as good a
+ * "can't happen" value as any other.
+ */
+ fec->active_fec = ETHTOOL_FEC_AUTO;
+ break;
+ }
+
+ return 0;
+}
+
+static int efx_mcdi_phy_set_fecparam(struct efx_nic *efx,
+ const struct ethtool_fecparam *fec)
+{
+ struct efx_mcdi_phy_data *phy_cfg = efx->phy_data;
+ u32 caps;
+ int rc;
+
+ /* Work out what efx_mcdi_phy_set_link_ksettings() would produce from
+ * saved advertising bits
+ */
+ if (test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, efx->link_advertising))
+ caps = (ethtool_linkset_to_mcdi_cap(efx->link_advertising) |
+ 1 << MC_CMD_PHY_CAP_AN_LBN);
+ else
+ caps = phy_cfg->forced_cap;
+
+ caps |= ethtool_fec_caps_to_mcdi(fec->fec);
+ rc = efx_mcdi_set_link(efx, caps, efx_get_mcdi_phy_flags(efx),
+ efx->loopback_mode, 0);
+ if (rc)
+ return rc;
+
+ /* Record the new FEC setting for subsequent set_link calls */
+ efx->fec_config = fec->fec;
+ return 0;
+}
+
static int efx_mcdi_phy_test_alive(struct efx_nic *efx)
{
MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_PHY_STATE_OUT_LEN);
@@ -977,6 +1125,8 @@ static const struct efx_phy_operations efx_mcdi_phy_ops = {
.remove = efx_mcdi_phy_remove,
.get_link_ksettings = efx_mcdi_phy_get_link_ksettings,
.set_link_ksettings = efx_mcdi_phy_set_link_ksettings,
+ .get_fecparam = efx_mcdi_phy_get_fecparam,
+ .set_fecparam = efx_mcdi_phy_set_fecparam,
.test_alive = efx_mcdi_phy_test_alive,
.run_tests = efx_mcdi_phy_run_tests,
.test_name = efx_mcdi_phy_test_name,
diff --git a/drivers/net/ethernet/sfc/mtd.c b/drivers/net/ethernet/sfc/mtd.c
index a77a8bd2dd70..4ac30b6e5dab 100644
--- a/drivers/net/ethernet/sfc/mtd.c
+++ b/drivers/net/ethernet/sfc/mtd.c
@@ -24,17 +24,8 @@
static int efx_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)
{
struct efx_nic *efx = mtd->priv;
- int rc;
- rc = efx->type->mtd_erase(mtd, erase->addr, erase->len);
- if (rc == 0) {
- erase->state = MTD_ERASE_DONE;
- } else {
- erase->state = MTD_ERASE_FAILED;
- erase->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
- }
- mtd_erase_callback(erase);
- return rc;
+ return efx->type->mtd_erase(mtd, erase->addr, erase->len);
}
static void efx_mtd_sync(struct mtd_info *mtd)
diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h
index d20a8660ee48..65568925c3ef 100644
--- a/drivers/net/ethernet/sfc/net_driver.h
+++ b/drivers/net/ethernet/sfc/net_driver.h
@@ -430,6 +430,7 @@ enum efx_sync_events_state {
* @event_test_cpu: Last CPU to handle interrupt or test event for this channel
* @irq_count: Number of IRQs since last adaptive moderation decision
* @irq_mod_score: IRQ moderation score
+ * @filter_work: Work item for efx_filter_rfs_expire()
* @rps_flow_id: Flow IDs of filters allocated for accelerated RFS,
* indexed by filter ID
* @n_rx_tobe_disc: Count of RX_TOBE_DISC errors
@@ -475,6 +476,7 @@ struct efx_channel {
unsigned int irq_mod_score;
#ifdef CONFIG_RFS_ACCEL
unsigned int rfs_filters_added;
+ struct work_struct filter_work;
#define RPS_FLOW_ID_INVALID 0xFFFFFFFF
u32 *rps_flow_id;
#endif
@@ -627,6 +629,8 @@ static inline bool efx_link_state_equal(const struct efx_link_state *left,
* Serialised by the mac_lock.
* @get_link_ksettings: Get ethtool settings. Serialised by the mac_lock.
* @set_link_ksettings: Set ethtool settings. Serialised by the mac_lock.
+ * @get_fecparam: Get Forward Error Correction settings. Serialised by mac_lock.
+ * @set_fecparam: Set Forward Error Correction settings. Serialised by mac_lock.
* @set_npage_adv: Set abilities advertised in (Extended) Next Page
* (only needed where AN bit is set in mmds)
* @test_alive: Test that PHY is 'alive' (online)
@@ -645,6 +649,9 @@ struct efx_phy_operations {
struct ethtool_link_ksettings *cmd);
int (*set_link_ksettings)(struct efx_nic *efx,
const struct ethtool_link_ksettings *cmd);
+ int (*get_fecparam)(struct efx_nic *efx, struct ethtool_fecparam *fec);
+ int (*set_fecparam)(struct efx_nic *efx,
+ const struct ethtool_fecparam *fec);
void (*set_npage_adv) (struct efx_nic *efx, u32);
int (*test_alive) (struct efx_nic *efx);
const char *(*test_name) (struct efx_nic *efx, unsigned int index);
@@ -704,6 +711,78 @@ union efx_multicast_hash {
struct vfdi_status;
+/* The reserved RSS context value */
+#define EFX_EF10_RSS_CONTEXT_INVALID 0xffffffff
+/**
+ * struct efx_rss_context - A user-defined RSS context for filtering
+ * @list: node of linked list on which this struct is stored
+ * @context_id: the RSS_CONTEXT_ID returned by MC firmware, or
+ * %EFX_EF10_RSS_CONTEXT_INVALID if this context is not present on the NIC.
+ * For Siena, 0 if RSS is active, else %EFX_EF10_RSS_CONTEXT_INVALID.
+ * @user_id: the rss_context ID exposed to userspace over ethtool.
+ * @rx_hash_udp_4tuple: UDP 4-tuple hashing enabled
+ * @rx_hash_key: Toeplitz hash key for this RSS context
+ * @indir_table: Indirection table for this RSS context
+ */
+struct efx_rss_context {
+ struct list_head list;
+ u32 context_id;
+ u32 user_id;
+ bool rx_hash_udp_4tuple;
+ u8 rx_hash_key[40];
+ u32 rx_indir_table[128];
+};
+
+#ifdef CONFIG_RFS_ACCEL
+/* Order of these is important, since filter_id >= %EFX_ARFS_FILTER_ID_PENDING
+ * is used to test if filter does or will exist.
+ */
+#define EFX_ARFS_FILTER_ID_PENDING -1
+#define EFX_ARFS_FILTER_ID_ERROR -2
+#define EFX_ARFS_FILTER_ID_REMOVING -3
+/**
+ * struct efx_arfs_rule - record of an ARFS filter and its IDs
+ * @node: linkage into hash table
+ * @spec: details of the filter (used as key for hash table). Use efx->type to
+ * determine which member to use.
+ * @rxq_index: channel to which the filter will steer traffic.
+ * @arfs_id: filter ID which was returned to ARFS
+ * @filter_id: index in software filter table. May be
+ * %EFX_ARFS_FILTER_ID_PENDING if filter was not inserted yet,
+ * %EFX_ARFS_FILTER_ID_ERROR if filter insertion failed, or
+ * %EFX_ARFS_FILTER_ID_REMOVING if expiry is currently removing the filter.
+ */
+struct efx_arfs_rule {
+ struct hlist_node node;
+ struct efx_filter_spec spec;
+ u16 rxq_index;
+ u16 arfs_id;
+ s32 filter_id;
+};
+
+/* Size chosen so that the table is one page (4kB) */
+#define EFX_ARFS_HASH_TABLE_SIZE 512
+
+/**
+ * struct efx_async_filter_insertion - Request to asynchronously insert a filter
+ * @net_dev: Reference to the netdevice
+ * @spec: The filter to insert
+ * @work: Workitem for this request
+ * @rxq_index: Identifies the channel for which this request was made
+ * @flow_id: Identifies the kernel-side flow for which this request was made
+ */
+struct efx_async_filter_insertion {
+ struct net_device *net_dev;
+ struct efx_filter_spec spec;
+ struct work_struct work;
+ u16 rxq_index;
+ u32 flow_id;
+};
+
+/* Maximum number of ARFS workitems that may be in flight on an efx_nic */
+#define EFX_RPS_MAX_IN_FLIGHT 8
+#endif /* CONFIG_RFS_ACCEL */
+
/**
* struct efx_nic - an Efx NIC
* @name: Device name (net device name or bus id before net device registered)
@@ -764,11 +843,10 @@ struct vfdi_status;
* (valid only for NICs that set %EFX_RX_PKT_PREFIX_LEN; always negative)
* @rx_packet_ts_offset: Offset of timestamp from start of packet data
* (valid only if channel->sync_timestamps_enabled; always negative)
- * @rx_hash_key: Toeplitz hash key for RSS
- * @rx_indir_table: Indirection table for RSS
* @rx_scatter: Scatter mode enabled for receives
- * @rss_active: RSS enabled on hardware
- * @rx_hash_udp_4tuple: UDP 4-tuple hashing enabled
+ * @rss_context: Main RSS context. Its @list member is the head of the list of
+ * RSS contexts created by user requests
+ * @rss_lock: Protects custom RSS context software state in @rss_context.list
* @int_error_count: Number of internal errors seen recently
* @int_error_expire: Time at which error count will be expired
* @irq_soft_enabled: Are IRQs soft-enabled? If not, IRQ handler will
@@ -800,6 +878,8 @@ struct vfdi_status;
* @mdio_bus: PHY MDIO bus ID (only used by Siena)
* @phy_mode: PHY operating mode. Serialised by @mac_lock.
* @link_advertising: Autonegotiation advertising flags
+ * @fec_config: Forward Error Correction configuration flags. For bit positions
+ * see &enum ethtool_fec_config_bits.
* @link_state: Current state of the link
* @n_link_state_changes: Number of times the link has changed state
* @unicast_filter: Flag for Falcon-arch simple unicast filter.
@@ -814,12 +894,18 @@ struct vfdi_status;
* @loopback_mode: Loopback status
* @loopback_modes: Supported loopback mode bitmask
* @loopback_selftest: Offline self-test private state
- * @filter_sem: Filter table rw_semaphore, for freeing the table
- * @filter_lock: Filter table lock, for mere content changes
+ * @filter_sem: Filter table rw_semaphore, protects existence of @filter_state
* @filter_state: Architecture-dependent filter table state
+ * @rps_mutex: Protects RPS state of all channels
* @rps_expire_channel: Next channel to check for expiry
* @rps_expire_index: Next index to check for expiry in
* @rps_expire_channel's @rps_flow_id
+ * @rps_slot_map: bitmap of in-flight entries in @rps_slot
+ * @rps_slot: array of ARFS insertion requests for efx_filter_rfs_work()
+ * @rps_hash_lock: Protects ARFS filter mapping state (@rps_hash_table and
+ * @rps_next_id).
+ * @rps_hash_table: Mapping between ARFS filters and their various IDs
+ * @rps_next_id: next arfs_id for an ARFS filter
* @active_queues: Count of RX and TX queues that haven't been flushed and drained.
* @rxq_flush_pending: Count of number of receive queues that need to be flushed.
* Decremented when the efx_flush_rx_queue() is called.
@@ -909,11 +995,9 @@ struct efx_nic {
int rx_packet_hash_offset;
int rx_packet_len_offset;
int rx_packet_ts_offset;
- u8 rx_hash_key[40];
- u32 rx_indir_table[128];
bool rx_scatter;
- bool rss_active;
- bool rx_hash_udp_4tuple;
+ struct efx_rss_context rss_context;
+ struct mutex rss_lock;
unsigned int_error_count;
unsigned long int_error_expire;
@@ -955,6 +1039,7 @@ struct efx_nic {
enum efx_phy_mode phy_mode;
__ETHTOOL_DECLARE_LINK_MODE_MASK(link_advertising);
+ u32 fec_config;
struct efx_link_state link_state;
unsigned int n_link_state_changes;
@@ -970,11 +1055,16 @@ struct efx_nic {
void *loopback_selftest;
struct rw_semaphore filter_sem;
- spinlock_t filter_lock;
void *filter_state;
#ifdef CONFIG_RFS_ACCEL
+ struct mutex rps_mutex;
unsigned int rps_expire_channel;
unsigned int rps_expire_index;
+ unsigned long rps_slot_map;
+ struct efx_async_filter_insertion rps_slot[EFX_RPS_MAX_IN_FLIGHT];
+ spinlock_t rps_hash_lock;
+ struct hlist_head *rps_hash_table;
+ u32 rps_next_id;
#endif
atomic_t active_queues;
@@ -1099,6 +1189,10 @@ struct efx_udp_tunnel {
* @tx_write: Write TX descriptors and doorbell
* @rx_push_rss_config: Write RSS hash key and indirection table to the NIC
* @rx_pull_rss_config: Read RSS hash key and indirection table back from the NIC
+ * @rx_push_rss_context_config: Write RSS hash key and indirection table for
+ * user RSS context to the NIC
+ * @rx_pull_rss_context_config: Read RSS hash key and indirection table for user
+ * RSS context back from the NIC
* @rx_probe: Allocate resources for RX queue
* @rx_init: Initialise RX queue on the NIC
* @rx_remove: Free resources for RX queue
@@ -1123,10 +1217,6 @@ struct efx_udp_tunnel {
* @filter_count_rx_used: Get the number of filters in use at a given priority
* @filter_get_rx_id_limit: Get maximum value of a filter id, plus 1
* @filter_get_rx_ids: Get list of RX filters at a given priority
- * @filter_rfs_insert: Add or replace a filter for RFS. This must be
- * atomic. The hardware change may be asynchronous but should
- * not be delayed for long. It may fail if this can't be done
- * atomically.
* @filter_rfs_expire_one: Consider expiring a filter inserted for RFS.
* This must check whether the specified table entry is used by RFS
* and that rps_may_expire_flow() returns true for it.
@@ -1237,6 +1327,13 @@ struct efx_nic_type {
int (*rx_push_rss_config)(struct efx_nic *efx, bool user,
const u32 *rx_indir_table, const u8 *key);
int (*rx_pull_rss_config)(struct efx_nic *efx);
+ int (*rx_push_rss_context_config)(struct efx_nic *efx,
+ struct efx_rss_context *ctx,
+ const u32 *rx_indir_table,
+ const u8 *key);
+ int (*rx_pull_rss_context_config)(struct efx_nic *efx,
+ struct efx_rss_context *ctx);
+ void (*rx_restore_rss_contexts)(struct efx_nic *efx);
int (*rx_probe)(struct efx_rx_queue *rx_queue);
void (*rx_init)(struct efx_rx_queue *rx_queue);
void (*rx_remove)(struct efx_rx_queue *rx_queue);
@@ -1270,8 +1367,6 @@ struct efx_nic_type {
enum efx_filter_priority priority,
u32 *buf, u32 size);
#ifdef CONFIG_RFS_ACCEL
- s32 (*filter_rfs_insert)(struct efx_nic *efx,
- struct efx_filter_spec *spec);
bool (*filter_rfs_expire_one)(struct efx_nic *efx, u32 flow_id,
unsigned int index);
#endif
diff --git a/drivers/net/ethernet/sfc/nic.h b/drivers/net/ethernet/sfc/nic.h
index 6549fc685a48..5cca0556b47f 100644
--- a/drivers/net/ethernet/sfc/nic.h
+++ b/drivers/net/ethernet/sfc/nic.h
@@ -332,7 +332,6 @@ enum {
EF10_STAT_fec_corrected_symbols_lane1,
EF10_STAT_fec_corrected_symbols_lane2,
EF10_STAT_fec_corrected_symbols_lane3,
- EF10_STAT_ctpio_dmabuf_start,
EF10_STAT_ctpio_vi_busy_fallback,
EF10_STAT_ctpio_long_write_success,
EF10_STAT_ctpio_missing_dbell_fail,
@@ -365,6 +364,8 @@ enum {
* @vi_base: Absolute index of first VI in this function
* @n_allocated_vis: Number of VIs allocated to this function
* @must_realloc_vis: Flag: VIs have yet to be reallocated after MC reboot
+ * @must_restore_rss_contexts: Flag: RSS contexts have yet to be restored after
+ * MC reboot
* @must_restore_filters: Flag: filters have yet to be restored after MC reboot
* @n_piobufs: Number of PIO buffers allocated to this function
* @wc_membase: Base address of write-combining mapping of the memory BAR
@@ -374,7 +375,6 @@ enum {
* @piobuf_size: size of a single PIO buffer
* @must_restore_piobufs: Flag: PIO buffers have yet to be restored after MC
* reboot
- * @rx_rss_context: Firmware handle for our RSS context
* @rx_rss_context_exclusive: Whether our RSS context is exclusive or shared
* @stats: Hardware statistics
* @workaround_35388: Flag: firmware supports workaround for bug 35388
@@ -408,6 +408,7 @@ struct efx_ef10_nic_data {
unsigned int vi_base;
unsigned int n_allocated_vis;
bool must_realloc_vis;
+ bool must_restore_rss_contexts;
bool must_restore_filters;
unsigned int n_piobufs;
void __iomem *wc_membase, *pio_write_base;
@@ -415,7 +416,6 @@ struct efx_ef10_nic_data {
unsigned int piobuf_handle[EF10_TX_PIOBUF_COUNT];
u16 piobuf_size;
bool must_restore_piobufs;
- u32 rx_rss_context;
bool rx_rss_context_exclusive;
u64 stats[EF10_STAT_COUNT];
bool workaround_35388;
@@ -603,8 +603,6 @@ s32 efx_farch_filter_get_rx_ids(struct efx_nic *efx,
enum efx_filter_priority priority, u32 *buf,
u32 size);
#ifdef CONFIG_RFS_ACCEL
-s32 efx_farch_filter_rfs_insert(struct efx_nic *efx,
- struct efx_filter_spec *spec);
bool efx_farch_filter_rfs_expire_one(struct efx_nic *efx, u32 flow_id,
unsigned int index);
#endif
diff --git a/drivers/net/ethernet/sfc/rx.c b/drivers/net/ethernet/sfc/rx.c
index cfe76aad79ee..64a94f242027 100644
--- a/drivers/net/ethernet/sfc/rx.c
+++ b/drivers/net/ethernet/sfc/rx.c
@@ -827,69 +827,165 @@ MODULE_PARM_DESC(rx_refill_threshold,
#ifdef CONFIG_RFS_ACCEL
+static void efx_filter_rfs_work(struct work_struct *data)
+{
+ struct efx_async_filter_insertion *req = container_of(data, struct efx_async_filter_insertion,
+ work);
+ struct efx_nic *efx = netdev_priv(req->net_dev);
+ struct efx_channel *channel = efx_get_channel(efx, req->rxq_index);
+ int slot_idx = req - efx->rps_slot;
+ struct efx_arfs_rule *rule;
+ u16 arfs_id = 0;
+ int rc;
+
+ rc = efx->type->filter_insert(efx, &req->spec, true);
+ if (efx->rps_hash_table) {
+ spin_lock_bh(&efx->rps_hash_lock);
+ rule = efx_rps_hash_find(efx, &req->spec);
+ /* The rule might have already gone, if someone else's request
+ * for the same spec was already worked and then expired before
+ * we got around to our work. In that case we have nothing
+ * tying us to an arfs_id, meaning that as soon as the filter
+ * is considered for expiry it will be removed.
+ */
+ if (rule) {
+ if (rc < 0)
+ rule->filter_id = EFX_ARFS_FILTER_ID_ERROR;
+ else
+ rule->filter_id = rc;
+ arfs_id = rule->arfs_id;
+ }
+ spin_unlock_bh(&efx->rps_hash_lock);
+ }
+ if (rc >= 0) {
+ /* Remember this so we can check whether to expire the filter
+ * later.
+ */
+ mutex_lock(&efx->rps_mutex);
+ channel->rps_flow_id[rc] = req->flow_id;
+ ++channel->rfs_filters_added;
+ mutex_unlock(&efx->rps_mutex);
+
+ if (req->spec.ether_type == htons(ETH_P_IP))
+ netif_info(efx, rx_status, efx->net_dev,
+ "steering %s %pI4:%u:%pI4:%u to queue %u [flow %u filter %d id %u]\n",
+ (req->spec.ip_proto == IPPROTO_TCP) ? "TCP" : "UDP",
+ req->spec.rem_host, ntohs(req->spec.rem_port),
+ req->spec.loc_host, ntohs(req->spec.loc_port),
+ req->rxq_index, req->flow_id, rc, arfs_id);
+ else
+ netif_info(efx, rx_status, efx->net_dev,
+ "steering %s [%pI6]:%u:[%pI6]:%u to queue %u [flow %u filter %d id %u]\n",
+ (req->spec.ip_proto == IPPROTO_TCP) ? "TCP" : "UDP",
+ req->spec.rem_host, ntohs(req->spec.rem_port),
+ req->spec.loc_host, ntohs(req->spec.loc_port),
+ req->rxq_index, req->flow_id, rc, arfs_id);
+ }
+
+ /* Release references */
+ clear_bit(slot_idx, &efx->rps_slot_map);
+ dev_put(req->net_dev);
+}
+
int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb,
u16 rxq_index, u32 flow_id)
{
struct efx_nic *efx = netdev_priv(net_dev);
- struct efx_channel *channel;
- struct efx_filter_spec spec;
+ struct efx_async_filter_insertion *req;
+ struct efx_arfs_rule *rule;
struct flow_keys fk;
+ int slot_idx;
+ bool new;
int rc;
- if (flow_id == RPS_FLOW_ID_INVALID)
- return -EINVAL;
+ /* find a free slot */
+ for (slot_idx = 0; slot_idx < EFX_RPS_MAX_IN_FLIGHT; slot_idx++)
+ if (!test_and_set_bit(slot_idx, &efx->rps_slot_map))
+ break;
+ if (slot_idx >= EFX_RPS_MAX_IN_FLIGHT)
+ return -EBUSY;
- if (!skb_flow_dissect_flow_keys(skb, &fk, 0))
- return -EPROTONOSUPPORT;
+ if (flow_id == RPS_FLOW_ID_INVALID) {
+ rc = -EINVAL;
+ goto out_clear;
+ }
- if (fk.basic.n_proto != htons(ETH_P_IP) && fk.basic.n_proto != htons(ETH_P_IPV6))
- return -EPROTONOSUPPORT;
- if (fk.control.flags & FLOW_DIS_IS_FRAGMENT)
- return -EPROTONOSUPPORT;
+ if (!skb_flow_dissect_flow_keys(skb, &fk, 0)) {
+ rc = -EPROTONOSUPPORT;
+ goto out_clear;
+ }
- efx_filter_init_rx(&spec, EFX_FILTER_PRI_HINT,
+ if (fk.basic.n_proto != htons(ETH_P_IP) && fk.basic.n_proto != htons(ETH_P_IPV6)) {
+ rc = -EPROTONOSUPPORT;
+ goto out_clear;
+ }
+ if (fk.control.flags & FLOW_DIS_IS_FRAGMENT) {
+ rc = -EPROTONOSUPPORT;
+ goto out_clear;
+ }
+
+ req = efx->rps_slot + slot_idx;
+ efx_filter_init_rx(&req->spec, EFX_FILTER_PRI_HINT,
efx->rx_scatter ? EFX_FILTER_FLAG_RX_SCATTER : 0,
rxq_index);
- spec.match_flags =
+ req->spec.match_flags =
EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT |
EFX_FILTER_MATCH_REM_HOST | EFX_FILTER_MATCH_REM_PORT;
- spec.ether_type = fk.basic.n_proto;
- spec.ip_proto = fk.basic.ip_proto;
+ req->spec.ether_type = fk.basic.n_proto;
+ req->spec.ip_proto = fk.basic.ip_proto;
if (fk.basic.n_proto == htons(ETH_P_IP)) {
- spec.rem_host[0] = fk.addrs.v4addrs.src;
- spec.loc_host[0] = fk.addrs.v4addrs.dst;
+ req->spec.rem_host[0] = fk.addrs.v4addrs.src;
+ req->spec.loc_host[0] = fk.addrs.v4addrs.dst;
} else {
- memcpy(spec.rem_host, &fk.addrs.v6addrs.src, sizeof(struct in6_addr));
- memcpy(spec.loc_host, &fk.addrs.v6addrs.dst, sizeof(struct in6_addr));
+ memcpy(req->spec.rem_host, &fk.addrs.v6addrs.src,
+ sizeof(struct in6_addr));
+ memcpy(req->spec.loc_host, &fk.addrs.v6addrs.dst,
+ sizeof(struct in6_addr));
}
- spec.rem_port = fk.ports.src;
- spec.loc_port = fk.ports.dst;
-
- rc = efx->type->filter_rfs_insert(efx, &spec);
- if (rc < 0)
- return rc;
-
- /* Remember this so we can check whether to expire the filter later */
- channel = efx_get_channel(efx, rxq_index);
- channel->rps_flow_id[rc] = flow_id;
- ++channel->rfs_filters_added;
+ req->spec.rem_port = fk.ports.src;
+ req->spec.loc_port = fk.ports.dst;
- if (spec.ether_type == htons(ETH_P_IP))
- netif_info(efx, rx_status, efx->net_dev,
- "steering %s %pI4:%u:%pI4:%u to queue %u [flow %u filter %d]\n",
- (spec.ip_proto == IPPROTO_TCP) ? "TCP" : "UDP",
- spec.rem_host, ntohs(spec.rem_port), spec.loc_host,
- ntohs(spec.loc_port), rxq_index, flow_id, rc);
- else
- netif_info(efx, rx_status, efx->net_dev,
- "steering %s [%pI6]:%u:[%pI6]:%u to queue %u [flow %u filter %d]\n",
- (spec.ip_proto == IPPROTO_TCP) ? "TCP" : "UDP",
- spec.rem_host, ntohs(spec.rem_port), spec.loc_host,
- ntohs(spec.loc_port), rxq_index, flow_id, rc);
+ if (efx->rps_hash_table) {
+ /* Add it to ARFS hash table */
+ spin_lock(&efx->rps_hash_lock);
+ rule = efx_rps_hash_add(efx, &req->spec, &new);
+ if (!rule) {
+ rc = -ENOMEM;
+ goto out_unlock;
+ }
+ if (new)
+ rule->arfs_id = efx->rps_next_id++ % RPS_NO_FILTER;
+ rc = rule->arfs_id;
+ /* Skip if existing or pending filter already does the right thing */
+ if (!new && rule->rxq_index == rxq_index &&
+ rule->filter_id >= EFX_ARFS_FILTER_ID_PENDING)
+ goto out_unlock;
+ rule->rxq_index = rxq_index;
+ rule->filter_id = EFX_ARFS_FILTER_ID_PENDING;
+ spin_unlock(&efx->rps_hash_lock);
+ } else {
+ /* Without an ARFS hash table, we just use arfs_id 0 for all
+ * filters. This means if multiple flows hash to the same
+ * flow_id, all but the most recently touched will be eligible
+ * for expiry.
+ */
+ rc = 0;
+ }
+ /* Queue the request */
+ dev_hold(req->net_dev = net_dev);
+ INIT_WORK(&req->work, efx_filter_rfs_work);
+ req->rxq_index = rxq_index;
+ req->flow_id = flow_id;
+ schedule_work(&req->work);
+ return rc;
+out_unlock:
+ spin_unlock(&efx->rps_hash_lock);
+out_clear:
+ clear_bit(slot_idx, &efx->rps_slot_map);
return rc;
}
@@ -899,9 +995,8 @@ bool __efx_filter_rfs_expire(struct efx_nic *efx, unsigned int quota)
unsigned int channel_idx, index, size;
u32 flow_id;
- if (!spin_trylock_bh(&efx->filter_lock))
+ if (!mutex_trylock(&efx->rps_mutex))
return false;
-
expire_one = efx->type->filter_rfs_expire_one;
channel_idx = efx->rps_expire_channel;
index = efx->rps_expire_index;
@@ -926,7 +1021,7 @@ bool __efx_filter_rfs_expire(struct efx_nic *efx, unsigned int quota)
efx->rps_expire_channel = channel_idx;
efx->rps_expire_index = index;
- spin_unlock_bh(&efx->filter_lock);
+ mutex_unlock(&efx->rps_mutex);
return true;
}
diff --git a/drivers/net/ethernet/sfc/siena.c b/drivers/net/ethernet/sfc/siena.c
index ae8645ae4492..65161f68265a 100644
--- a/drivers/net/ethernet/sfc/siena.c
+++ b/drivers/net/ethernet/sfc/siena.c
@@ -350,11 +350,11 @@ static int siena_rx_pull_rss_config(struct efx_nic *efx)
* siena_rx_push_rss_config, below)
*/
efx_reado(efx, &temp, FR_CZ_RX_RSS_IPV6_REG1);
- memcpy(efx->rx_hash_key, &temp, sizeof(temp));
+ memcpy(efx->rss_context.rx_hash_key, &temp, sizeof(temp));
efx_reado(efx, &temp, FR_CZ_RX_RSS_IPV6_REG2);
- memcpy(efx->rx_hash_key + sizeof(temp), &temp, sizeof(temp));
+ memcpy(efx->rss_context.rx_hash_key + sizeof(temp), &temp, sizeof(temp));
efx_reado(efx, &temp, FR_CZ_RX_RSS_IPV6_REG3);
- memcpy(efx->rx_hash_key + 2 * sizeof(temp), &temp,
+ memcpy(efx->rss_context.rx_hash_key + 2 * sizeof(temp), &temp,
FRF_CZ_RX_RSS_IPV6_TKEY_HI_WIDTH / 8);
efx_farch_rx_pull_indir_table(efx);
return 0;
@@ -367,26 +367,26 @@ static int siena_rx_push_rss_config(struct efx_nic *efx, bool user,
/* Set hash key for IPv4 */
if (key)
- memcpy(efx->rx_hash_key, key, sizeof(temp));
- memcpy(&temp, efx->rx_hash_key, sizeof(temp));
+ memcpy(efx->rss_context.rx_hash_key, key, sizeof(temp));
+ memcpy(&temp, efx->rss_context.rx_hash_key, sizeof(temp));
efx_writeo(efx, &temp, FR_BZ_RX_RSS_TKEY);
/* Enable IPv6 RSS */
- BUILD_BUG_ON(sizeof(efx->rx_hash_key) <
+ BUILD_BUG_ON(sizeof(efx->rss_context.rx_hash_key) <
2 * sizeof(temp) + FRF_CZ_RX_RSS_IPV6_TKEY_HI_WIDTH / 8 ||
FRF_CZ_RX_RSS_IPV6_TKEY_HI_LBN != 0);
- memcpy(&temp, efx->rx_hash_key, sizeof(temp));
+ memcpy(&temp, efx->rss_context.rx_hash_key, sizeof(temp));
efx_writeo(efx, &temp, FR_CZ_RX_RSS_IPV6_REG1);
- memcpy(&temp, efx->rx_hash_key + sizeof(temp), sizeof(temp));
+ memcpy(&temp, efx->rss_context.rx_hash_key + sizeof(temp), sizeof(temp));
efx_writeo(efx, &temp, FR_CZ_RX_RSS_IPV6_REG2);
EFX_POPULATE_OWORD_2(temp, FRF_CZ_RX_RSS_IPV6_THASH_ENABLE, 1,
FRF_CZ_RX_RSS_IPV6_IP_THASH_ENABLE, 1);
- memcpy(&temp, efx->rx_hash_key + 2 * sizeof(temp),
+ memcpy(&temp, efx->rss_context.rx_hash_key + 2 * sizeof(temp),
FRF_CZ_RX_RSS_IPV6_TKEY_HI_WIDTH / 8);
efx_writeo(efx, &temp, FR_CZ_RX_RSS_IPV6_REG3);
- memcpy(efx->rx_indir_table, rx_indir_table,
- sizeof(efx->rx_indir_table));
+ memcpy(efx->rss_context.rx_indir_table, rx_indir_table,
+ sizeof(efx->rss_context.rx_indir_table));
efx_farch_rx_push_indir_table(efx);
return 0;
@@ -432,8 +432,8 @@ static int siena_init_nic(struct efx_nic *efx)
EFX_RX_USR_BUF_SIZE >> 5);
efx_writeo(efx, &temp, FR_AZ_RX_CFG);
- siena_rx_push_rss_config(efx, false, efx->rx_indir_table, NULL);
- efx->rss_active = true;
+ siena_rx_push_rss_config(efx, false, efx->rss_context.rx_indir_table, NULL);
+ efx->rss_context.context_id = 0; /* indicates RSS is active */
/* Enable event logging */
rc = efx_mcdi_log_ctrl(efx, true, false, 0);
@@ -1035,7 +1035,6 @@ const struct efx_nic_type siena_a0_nic_type = {
.filter_get_rx_id_limit = efx_farch_filter_get_rx_id_limit,
.filter_get_rx_ids = efx_farch_filter_get_rx_ids,
#ifdef CONFIG_RFS_ACCEL
- .filter_rfs_insert = efx_farch_filter_rfs_insert,
.filter_rfs_expire_one = efx_farch_filter_rfs_expire_one,
#endif
#ifdef CONFIG_SFC_MTD
diff --git a/drivers/net/ethernet/smsc/Kconfig b/drivers/net/ethernet/smsc/Kconfig
index 4c2f612e4414..358820282ef0 100644
--- a/drivers/net/ethernet/smsc/Kconfig
+++ b/drivers/net/ethernet/smsc/Kconfig
@@ -5,8 +5,8 @@
config NET_VENDOR_SMSC
bool "SMC (SMSC)/Western Digital devices"
default y
- depends on ARM || ARM64 || ATARI_ETHERNAT || BLACKFIN || COLDFIRE || \
- ISA || M32R || MAC || MIPS || MN10300 || NIOS2 || PCI || \
+ depends on ARM || ARM64 || ATARI_ETHERNAT || COLDFIRE || \
+ ISA || MAC || MIPS || NIOS2 || PCI || \
PCMCIA || SUPERH || XTENSA || H8300
---help---
If you have a network (Ethernet) card belonging to this class, say Y.
@@ -37,8 +37,8 @@ config SMC91X
select CRC32
select MII
depends on !OF || GPIOLIB
- depends on ARM || ARM64 || ATARI_ETHERNAT || BLACKFIN || COLDFIRE || \
- M32R || MIPS || MN10300 || NIOS2 || SUPERH || XTENSA || H8300
+ depends on ARM || ARM64 || ATARI_ETHERNAT || COLDFIRE || \
+ MIPS || NIOS2 || SUPERH || XTENSA || H8300
---help---
This is a driver for SMC's 91x series of Ethernet chipsets,
including the SMC91C94 and the SMC91C111. Say Y if you want it
@@ -77,7 +77,7 @@ config SMC911X
tristate "SMSC LAN911[5678] support"
select CRC32
select MII
- depends on (ARM || SUPERH || MN10300)
+ depends on (ARM || SUPERH)
---help---
This is a driver for SMSC's LAN911x series of Ethernet chipsets
including the new LAN9115, LAN9116, LAN9117, and LAN9118.
diff --git a/drivers/net/ethernet/smsc/smc91x.h b/drivers/net/ethernet/smsc/smc91x.h
index 08b17adf0a65..b337ee97e0c0 100644
--- a/drivers/net/ethernet/smsc/smc91x.h
+++ b/drivers/net/ethernet/smsc/smc91x.h
@@ -144,32 +144,6 @@ static inline void _SMC_outw_align4(u16 val, void __iomem *ioaddr, int reg,
#define SMC_IRQ_FLAGS (0)
-#elif defined(CONFIG_M32R)
-
-#define SMC_CAN_USE_8BIT 0
-#define SMC_CAN_USE_16BIT 1
-#define SMC_CAN_USE_32BIT 0
-
-#define SMC_inb(a, r) inb(((u32)a) + (r))
-#define SMC_inw(a, r) inw(((u32)a) + (r))
-#define SMC_outb(v, a, r) outb(v, ((u32)a) + (r))
-#define SMC_outw(lp, v, a, r) outw(v, ((u32)a) + (r))
-#define SMC_insw(a, r, p, l) insw(((u32)a) + (r), p, l)
-#define SMC_outsw(a, r, p, l) outsw(((u32)a) + (r), p, l)
-
-#define SMC_IRQ_FLAGS (0)
-
-#define RPC_LSA_DEFAULT RPC_LED_TX_RX
-#define RPC_LSB_DEFAULT RPC_LED_100_10
-
-#elif defined(CONFIG_MN10300)
-
-/*
- * MN10300/AM33 configuration
- */
-
-#include <unit/smc91111.h>
-
#elif defined(CONFIG_ATARI)
#define SMC_CAN_USE_8BIT 1
diff --git a/drivers/net/ethernet/socionext/sni_ave.c b/drivers/net/ethernet/socionext/sni_ave.c
index f5c5984afefb..0b3b7a460641 100644
--- a/drivers/net/ethernet/socionext/sni_ave.c
+++ b/drivers/net/ethernet/socionext/sni_ave.c
@@ -1701,6 +1701,10 @@ static const struct ave_soc_data ave_ld20_data = {
.is_desc_64bit = true,
};
+static const struct ave_soc_data ave_pxs3_data = {
+ .is_desc_64bit = false,
+};
+
static const struct of_device_id of_ave_match[] = {
{
.compatible = "socionext,uniphier-pro4-ave4",
@@ -1718,6 +1722,10 @@ static const struct of_device_id of_ave_match[] = {
.compatible = "socionext,uniphier-ld20-ave4",
.data = &ave_ld20_data,
},
+ {
+ .compatible = "socionext,uniphier-pxs3-ave4",
+ .data = &ave_pxs3_data,
+ },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, of_ave_match);
diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile
index ff3f83b86d10..972e4ef6d414 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Makefile
+++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
@@ -4,7 +4,7 @@ stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o \
chain_mode.o dwmac_lib.o dwmac1000_core.o dwmac1000_dma.o \
dwmac100_core.o dwmac100_dma.o enh_desc.o norm_desc.o \
mmc_core.o stmmac_hwtstamp.o stmmac_ptp.o dwmac4_descs.o \
- dwmac4_dma.o dwmac4_lib.o dwmac4_core.o $(stmmac-y)
+ dwmac4_dma.o dwmac4_lib.o dwmac4_core.o dwmac5.o $(stmmac-y)
# Ordering matters. Generic driver must be last.
obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o
diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h
index 2ffe76c0ff74..ad2388aee463 100644
--- a/drivers/net/ethernet/stmicro/stmmac/common.h
+++ b/drivers/net/ethernet/stmicro/stmmac/common.h
@@ -38,6 +38,8 @@
#define DWMAC_CORE_3_40 0x34
#define DWMAC_CORE_3_50 0x35
#define DWMAC_CORE_4_00 0x40
+#define DWMAC_CORE_5_00 0x50
+#define DWMAC_CORE_5_10 0x51
#define STMMAC_CHAN0 0 /* Always supported and default for all chips */
/* These need to be power of two, and >= 4 */
@@ -174,6 +176,17 @@ struct stmmac_extra_stats {
unsigned long tx_tso_nfrags;
};
+/* Safety Feature statistics exposed by ethtool */
+struct stmmac_safety_stats {
+ unsigned long mac_errors[32];
+ unsigned long mtl_errors[32];
+ unsigned long dma_errors[32];
+};
+
+/* Number of fields in Safety Stats */
+#define STMMAC_SAFETY_FEAT_SIZE \
+ (sizeof(struct stmmac_safety_stats) / sizeof(unsigned long))
+
/* CSR Frequency Access Defines*/
#define CSR_F_35M 35000000
#define CSR_F_60M 60000000
@@ -336,6 +349,8 @@ struct dma_features {
/* TX and RX FIFO sizes */
unsigned int tx_fifo_size;
unsigned int rx_fifo_size;
+ /* Automotive Safety Package */
+ unsigned int asp;
};
/* GMAC TX FIFO is 8K, Rx FIFO is 16K */
@@ -532,6 +547,13 @@ struct stmmac_ops {
bool loopback);
void (*pcs_rane)(void __iomem *ioaddr, bool restart);
void (*pcs_get_adv_lp)(void __iomem *ioaddr, struct rgmii_adv *adv);
+ /* Safety Features */
+ int (*safety_feat_config)(void __iomem *ioaddr, unsigned int asp);
+ bool (*safety_feat_irq_status)(struct net_device *ndev,
+ void __iomem *ioaddr, unsigned int asp,
+ struct stmmac_safety_stats *stats);
+ const char *(*safety_feat_dump)(struct stmmac_safety_stats *stats,
+ int index, unsigned long *count);
};
/* PTP and HW Timer helpers */
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c
index 5270d26f0bc6..7cb794094a70 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c
@@ -1,5 +1,5 @@
/*
- * Amlogic Meson8b and GXBB DWMAC glue layer
+ * Amlogic Meson8b, Meson8m2 and GXBB DWMAC glue layer
*
* Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
*
@@ -48,26 +48,18 @@
#define MUX_CLK_NUM_PARENTS 2
struct meson8b_dwmac {
- struct platform_device *pdev;
-
+ struct device *dev;
void __iomem *regs;
-
phy_interface_t phy_mode;
+ struct clk *rgmii_tx_clk;
+ u32 tx_delay_ns;
+};
+struct meson8b_dwmac_clk_configs {
struct clk_mux m250_mux;
- struct clk *m250_mux_clk;
- struct clk *m250_mux_parent[MUX_CLK_NUM_PARENTS];
-
struct clk_divider m250_div;
- struct clk *m250_div_clk;
-
struct clk_fixed_factor fixed_div2;
- struct clk *fixed_div2_clk;
-
struct clk_gate rgmii_tx_en;
- struct clk *rgmii_tx_en_clk;
-
- u32 tx_delay_ns;
};
static void meson8b_dwmac_mask_bits(struct meson8b_dwmac *dwmac, u32 reg,
@@ -82,106 +74,99 @@ static void meson8b_dwmac_mask_bits(struct meson8b_dwmac *dwmac, u32 reg,
writel(data, dwmac->regs + reg);
}
-static int meson8b_init_rgmii_tx_clk(struct meson8b_dwmac *dwmac)
+static struct clk *meson8b_dwmac_register_clk(struct meson8b_dwmac *dwmac,
+ const char *name_suffix,
+ const char **parent_names,
+ int num_parents,
+ const struct clk_ops *ops,
+ struct clk_hw *hw)
{
struct clk_init_data init;
- int i, ret;
- struct device *dev = &dwmac->pdev->dev;
char clk_name[32];
- const char *clk_div_parents[1];
- const char *mux_parent_names[MUX_CLK_NUM_PARENTS];
+
+ snprintf(clk_name, sizeof(clk_name), "%s#%s", dev_name(dwmac->dev),
+ name_suffix);
+
+ init.name = clk_name;
+ init.ops = ops;
+ init.flags = CLK_SET_RATE_PARENT;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+
+ hw->init = &init;
+
+ return devm_clk_register(dwmac->dev, hw);
+}
+
+static int meson8b_init_rgmii_tx_clk(struct meson8b_dwmac *dwmac)
+{
+ int i, ret;
+ struct clk *clk;
+ struct device *dev = dwmac->dev;
+ const char *parent_name, *mux_parent_names[MUX_CLK_NUM_PARENTS];
+ struct meson8b_dwmac_clk_configs *clk_configs;
+
+ clk_configs = devm_kzalloc(dev, sizeof(*clk_configs), GFP_KERNEL);
+ if (!clk_configs)
+ return -ENOMEM;
/* get the mux parents from DT */
for (i = 0; i < MUX_CLK_NUM_PARENTS; i++) {
char name[16];
snprintf(name, sizeof(name), "clkin%d", i);
- dwmac->m250_mux_parent[i] = devm_clk_get(dev, name);
- if (IS_ERR(dwmac->m250_mux_parent[i])) {
- ret = PTR_ERR(dwmac->m250_mux_parent[i]);
+ clk = devm_clk_get(dev, name);
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
if (ret != -EPROBE_DEFER)
dev_err(dev, "Missing clock %s\n", name);
return ret;
}
- mux_parent_names[i] =
- __clk_get_name(dwmac->m250_mux_parent[i]);
+ mux_parent_names[i] = __clk_get_name(clk);
}
- /* create the m250_mux */
- snprintf(clk_name, sizeof(clk_name), "%s#m250_sel", dev_name(dev));
- init.name = clk_name;
- init.ops = &clk_mux_ops;
- init.flags = CLK_SET_RATE_PARENT;
- init.parent_names = mux_parent_names;
- init.num_parents = MUX_CLK_NUM_PARENTS;
-
- dwmac->m250_mux.reg = dwmac->regs + PRG_ETH0;
- dwmac->m250_mux.shift = PRG_ETH0_CLK_M250_SEL_SHIFT;
- dwmac->m250_mux.mask = PRG_ETH0_CLK_M250_SEL_MASK;
- dwmac->m250_mux.flags = 0;
- dwmac->m250_mux.table = NULL;
- dwmac->m250_mux.hw.init = &init;
-
- dwmac->m250_mux_clk = devm_clk_register(dev, &dwmac->m250_mux.hw);
- if (WARN_ON(IS_ERR(dwmac->m250_mux_clk)))
- return PTR_ERR(dwmac->m250_mux_clk);
-
- /* create the m250_div */
- snprintf(clk_name, sizeof(clk_name), "%s#m250_div", dev_name(dev));
- init.name = devm_kstrdup(dev, clk_name, GFP_KERNEL);
- init.ops = &clk_divider_ops;
- init.flags = CLK_SET_RATE_PARENT;
- clk_div_parents[0] = __clk_get_name(dwmac->m250_mux_clk);
- init.parent_names = clk_div_parents;
- init.num_parents = ARRAY_SIZE(clk_div_parents);
-
- dwmac->m250_div.reg = dwmac->regs + PRG_ETH0;
- dwmac->m250_div.shift = PRG_ETH0_CLK_M250_DIV_SHIFT;
- dwmac->m250_div.width = PRG_ETH0_CLK_M250_DIV_WIDTH;
- dwmac->m250_div.hw.init = &init;
- dwmac->m250_div.flags = CLK_DIVIDER_ONE_BASED |
+ clk_configs->m250_mux.reg = dwmac->regs + PRG_ETH0;
+ clk_configs->m250_mux.shift = PRG_ETH0_CLK_M250_SEL_SHIFT;
+ clk_configs->m250_mux.mask = PRG_ETH0_CLK_M250_SEL_MASK;
+ clk = meson8b_dwmac_register_clk(dwmac, "m250_sel", mux_parent_names,
+ MUX_CLK_NUM_PARENTS, &clk_mux_ops,
+ &clk_configs->m250_mux.hw);
+ if (WARN_ON(IS_ERR(clk)))
+ return PTR_ERR(clk);
+
+ parent_name = __clk_get_name(clk);
+ clk_configs->m250_div.reg = dwmac->regs + PRG_ETH0;
+ clk_configs->m250_div.shift = PRG_ETH0_CLK_M250_DIV_SHIFT;
+ clk_configs->m250_div.width = PRG_ETH0_CLK_M250_DIV_WIDTH;
+ clk_configs->m250_div.flags = CLK_DIVIDER_ONE_BASED |
CLK_DIVIDER_ALLOW_ZERO |
CLK_DIVIDER_ROUND_CLOSEST;
-
- dwmac->m250_div_clk = devm_clk_register(dev, &dwmac->m250_div.hw);
- if (WARN_ON(IS_ERR(dwmac->m250_div_clk)))
- return PTR_ERR(dwmac->m250_div_clk);
-
- /* create the fixed_div2 */
- snprintf(clk_name, sizeof(clk_name), "%s#fixed_div2", dev_name(dev));
- init.name = devm_kstrdup(dev, clk_name, GFP_KERNEL);
- init.ops = &clk_fixed_factor_ops;
- init.flags = CLK_SET_RATE_PARENT;
- clk_div_parents[0] = __clk_get_name(dwmac->m250_div_clk);
- init.parent_names = clk_div_parents;
- init.num_parents = ARRAY_SIZE(clk_div_parents);
-
- dwmac->fixed_div2.mult = 1;
- dwmac->fixed_div2.div = 2;
- dwmac->fixed_div2.hw.init = &init;
-
- dwmac->fixed_div2_clk = devm_clk_register(dev, &dwmac->fixed_div2.hw);
- if (WARN_ON(IS_ERR(dwmac->fixed_div2_clk)))
- return PTR_ERR(dwmac->fixed_div2_clk);
-
- /* create the rgmii_tx_en */
- init.name = devm_kasprintf(dev, GFP_KERNEL, "%s#rgmii_tx_en",
- dev_name(dev));
- init.ops = &clk_gate_ops;
- init.flags = CLK_SET_RATE_PARENT;
- clk_div_parents[0] = __clk_get_name(dwmac->fixed_div2_clk);
- init.parent_names = clk_div_parents;
- init.num_parents = ARRAY_SIZE(clk_div_parents);
-
- dwmac->rgmii_tx_en.reg = dwmac->regs + PRG_ETH0;
- dwmac->rgmii_tx_en.bit_idx = PRG_ETH0_RGMII_TX_CLK_EN;
- dwmac->rgmii_tx_en.hw.init = &init;
-
- dwmac->rgmii_tx_en_clk = devm_clk_register(dev,
- &dwmac->rgmii_tx_en.hw);
- if (WARN_ON(IS_ERR(dwmac->rgmii_tx_en_clk)))
- return PTR_ERR(dwmac->rgmii_tx_en_clk);
+ clk = meson8b_dwmac_register_clk(dwmac, "m250_div", &parent_name, 1,
+ &clk_divider_ops,
+ &clk_configs->m250_div.hw);
+ if (WARN_ON(IS_ERR(clk)))
+ return PTR_ERR(clk);
+
+ parent_name = __clk_get_name(clk);
+ clk_configs->fixed_div2.mult = 1;
+ clk_configs->fixed_div2.div = 2;
+ clk = meson8b_dwmac_register_clk(dwmac, "fixed_div2", &parent_name, 1,
+ &clk_fixed_factor_ops,
+ &clk_configs->fixed_div2.hw);
+ if (WARN_ON(IS_ERR(clk)))
+ return PTR_ERR(clk);
+
+ parent_name = __clk_get_name(clk);
+ clk_configs->rgmii_tx_en.reg = dwmac->regs + PRG_ETH0;
+ clk_configs->rgmii_tx_en.bit_idx = PRG_ETH0_RGMII_TX_CLK_EN;
+ clk = meson8b_dwmac_register_clk(dwmac, "rgmii_tx_en", &parent_name, 1,
+ &clk_gate_ops,
+ &clk_configs->rgmii_tx_en.hw);
+ if (WARN_ON(IS_ERR(clk)))
+ return PTR_ERR(clk);
+
+ dwmac->rgmii_tx_clk = clk;
return 0;
}
@@ -219,19 +204,23 @@ static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac)
* a register) based on the line-speed (125MHz for Gbit speeds,
* 25MHz for 100Mbit/s and 2.5MHz for 10Mbit/s).
*/
- ret = clk_set_rate(dwmac->rgmii_tx_en_clk, 125 * 1000 * 1000);
+ ret = clk_set_rate(dwmac->rgmii_tx_clk, 125 * 1000 * 1000);
if (ret) {
- dev_err(&dwmac->pdev->dev,
+ dev_err(dwmac->dev,
"failed to set RGMII TX clock\n");
return ret;
}
- ret = clk_prepare_enable(dwmac->rgmii_tx_en_clk);
+ ret = clk_prepare_enable(dwmac->rgmii_tx_clk);
if (ret) {
- dev_err(&dwmac->pdev->dev,
+ dev_err(dwmac->dev,
"failed to enable the RGMII TX clock\n");
return ret;
}
+
+ devm_add_action_or_reset(dwmac->dev,
+ (void(*)(void *))clk_disable_unprepare,
+ dwmac->rgmii_tx_clk);
break;
case PHY_INTERFACE_MODE_RMII:
@@ -251,7 +240,7 @@ static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac)
break;
default:
- dev_err(&dwmac->pdev->dev, "unsupported phy-mode %s\n",
+ dev_err(dwmac->dev, "unsupported phy-mode %s\n",
phy_modes(dwmac->phy_mode));
return -EINVAL;
}
@@ -292,7 +281,7 @@ static int meson8b_dwmac_probe(struct platform_device *pdev)
goto err_remove_config_dt;
}
- dwmac->pdev = pdev;
+ dwmac->dev = &pdev->dev;
dwmac->phy_mode = of_get_phy_mode(pdev->dev.of_node);
if (dwmac->phy_mode < 0) {
dev_err(&pdev->dev, "missing phy-mode property\n");
@@ -317,31 +306,19 @@ static int meson8b_dwmac_probe(struct platform_device *pdev)
ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
if (ret)
- goto err_clk_disable;
+ goto err_remove_config_dt;
return 0;
-err_clk_disable:
- if (phy_interface_mode_is_rgmii(dwmac->phy_mode))
- clk_disable_unprepare(dwmac->rgmii_tx_en_clk);
err_remove_config_dt:
stmmac_remove_config_dt(pdev, plat_dat);
return ret;
}
-static int meson8b_dwmac_remove(struct platform_device *pdev)
-{
- struct meson8b_dwmac *dwmac = get_stmmac_bsp_priv(&pdev->dev);
-
- if (phy_interface_mode_is_rgmii(dwmac->phy_mode))
- clk_disable_unprepare(dwmac->rgmii_tx_en_clk);
-
- return stmmac_pltfr_remove(pdev);
-}
-
static const struct of_device_id meson8b_dwmac_match[] = {
{ .compatible = "amlogic,meson8b-dwmac" },
+ { .compatible = "amlogic,meson8m2-dwmac" },
{ .compatible = "amlogic,meson-gxbb-dwmac" },
{ }
};
@@ -349,7 +326,7 @@ MODULE_DEVICE_TABLE(of, meson8b_dwmac_match);
static struct platform_driver meson8b_dwmac_driver = {
.probe = meson8b_dwmac_probe,
- .remove = meson8b_dwmac_remove,
+ .remove = stmmac_pltfr_remove,
.driver = {
.name = "meson8b-dwmac",
.pm = &stmmac_pltfr_pm_ops,
@@ -359,5 +336,5 @@ static struct platform_driver meson8b_dwmac_driver = {
module_platform_driver(meson8b_dwmac_driver);
MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
-MODULE_DESCRIPTION("Amlogic Meson8b and GXBB DWMAC glue layer");
+MODULE_DESCRIPTION("Amlogic Meson8b, Meson8m2 and GXBB DWMAC glue layer");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h
index 7761a26ec9c5..dedd40613090 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h
@@ -39,6 +39,7 @@
#define GMAC_HW_FEATURE0 0x0000011c
#define GMAC_HW_FEATURE1 0x00000120
#define GMAC_HW_FEATURE2 0x00000124
+#define GMAC_HW_FEATURE3 0x00000128
#define GMAC_MDIO_ADDR 0x00000200
#define GMAC_MDIO_DATA 0x00000204
#define GMAC_ADDR_HIGH(reg) (0x300 + reg * 8)
@@ -192,6 +193,9 @@ enum power_event {
#define GMAC_HW_FEAT_TXQCNT GENMASK(9, 6)
#define GMAC_HW_FEAT_RXQCNT GENMASK(3, 0)
+/* MAC HW features3 bitmap */
+#define GMAC_HW_FEAT_ASP GENMASK(29, 28)
+
/* MAC HW ADDR regs */
#define GMAC_HI_DCS GENMASK(18, 16)
#define GMAC_HI_DCS_SHIFT 16
@@ -343,7 +347,7 @@ enum power_event {
#define MTL_RX_OVERFLOW_INT BIT(16)
/* Default operating mode of the MAC */
-#define GMAC_CORE_INIT (GMAC_CONFIG_JD | GMAC_CONFIG_PS | GMAC_CONFIG_ACS | \
+#define GMAC_CORE_INIT (GMAC_CONFIG_JD | GMAC_CONFIG_PS | \
GMAC_CONFIG_BE | GMAC_CONFIG_DCRS)
/* To dump the core regs excluding the Address Registers */
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
index 63795ecafc8d..517b1f6736a8 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
@@ -20,6 +20,7 @@
#include <net/dsa.h>
#include "stmmac_pcs.h"
#include "dwmac4.h"
+#include "dwmac5.h"
static void dwmac4_core_init(struct mac_device_info *hw,
struct net_device *dev)
@@ -30,13 +31,6 @@ static void dwmac4_core_init(struct mac_device_info *hw,
value |= GMAC_CORE_INIT;
- /* Clear ACS bit because Ethernet switch tagging formats such as
- * Broadcom tags can look like invalid LLC/SNAP packets and cause the
- * hardware to truncate packets on reception.
- */
- if (netdev_uses_dsa(dev))
- value &= ~GMAC_CONFIG_ACS;
-
if (mtu > 1500)
value |= GMAC_CONFIG_2K;
if (mtu > 2000)
@@ -120,7 +114,7 @@ static void dwmac4_tx_queue_priority(struct mac_device_info *hw,
writel(value, ioaddr + base_register);
}
-static void dwmac4_tx_queue_routing(struct mac_device_info *hw,
+static void dwmac4_rx_queue_routing(struct mac_device_info *hw,
u8 packet, u32 queue)
{
void __iomem *ioaddr = hw->pcsr;
@@ -713,7 +707,7 @@ static const struct stmmac_ops dwmac4_ops = {
.rx_queue_enable = dwmac4_rx_queue_enable,
.rx_queue_prio = dwmac4_rx_queue_priority,
.tx_queue_prio = dwmac4_tx_queue_priority,
- .rx_queue_routing = dwmac4_tx_queue_routing,
+ .rx_queue_routing = dwmac4_rx_queue_routing,
.prog_mtl_rx_algorithms = dwmac4_prog_mtl_rx_algorithms,
.prog_mtl_tx_algorithms = dwmac4_prog_mtl_tx_algorithms,
.set_mtl_tx_queue_weight = dwmac4_set_mtl_tx_queue_weight,
@@ -744,7 +738,38 @@ static const struct stmmac_ops dwmac410_ops = {
.rx_queue_enable = dwmac4_rx_queue_enable,
.rx_queue_prio = dwmac4_rx_queue_priority,
.tx_queue_prio = dwmac4_tx_queue_priority,
- .rx_queue_routing = dwmac4_tx_queue_routing,
+ .rx_queue_routing = dwmac4_rx_queue_routing,
+ .prog_mtl_rx_algorithms = dwmac4_prog_mtl_rx_algorithms,
+ .prog_mtl_tx_algorithms = dwmac4_prog_mtl_tx_algorithms,
+ .set_mtl_tx_queue_weight = dwmac4_set_mtl_tx_queue_weight,
+ .map_mtl_to_dma = dwmac4_map_mtl_dma,
+ .config_cbs = dwmac4_config_cbs,
+ .dump_regs = dwmac4_dump_regs,
+ .host_irq_status = dwmac4_irq_status,
+ .host_mtl_irq_status = dwmac4_irq_mtl_status,
+ .flow_ctrl = dwmac4_flow_ctrl,
+ .pmt = dwmac4_pmt,
+ .set_umac_addr = dwmac4_set_umac_addr,
+ .get_umac_addr = dwmac4_get_umac_addr,
+ .set_eee_mode = dwmac4_set_eee_mode,
+ .reset_eee_mode = dwmac4_reset_eee_mode,
+ .set_eee_timer = dwmac4_set_eee_timer,
+ .set_eee_pls = dwmac4_set_eee_pls,
+ .pcs_ctrl_ane = dwmac4_ctrl_ane,
+ .pcs_rane = dwmac4_rane,
+ .pcs_get_adv_lp = dwmac4_get_adv_lp,
+ .debug = dwmac4_debug,
+ .set_filter = dwmac4_set_filter,
+};
+
+static const struct stmmac_ops dwmac510_ops = {
+ .core_init = dwmac4_core_init,
+ .set_mac = stmmac_dwmac4_set_mac,
+ .rx_ipc = dwmac4_rx_ipc_enable,
+ .rx_queue_enable = dwmac4_rx_queue_enable,
+ .rx_queue_prio = dwmac4_rx_queue_priority,
+ .tx_queue_prio = dwmac4_tx_queue_priority,
+ .rx_queue_routing = dwmac4_rx_queue_routing,
.prog_mtl_rx_algorithms = dwmac4_prog_mtl_rx_algorithms,
.prog_mtl_tx_algorithms = dwmac4_prog_mtl_tx_algorithms,
.set_mtl_tx_queue_weight = dwmac4_set_mtl_tx_queue_weight,
@@ -766,6 +791,9 @@ static const struct stmmac_ops dwmac410_ops = {
.pcs_get_adv_lp = dwmac4_get_adv_lp,
.debug = dwmac4_debug,
.set_filter = dwmac4_set_filter,
+ .safety_feat_config = dwmac5_safety_feat_config,
+ .safety_feat_irq_status = dwmac5_safety_feat_irq_status,
+ .safety_feat_dump = dwmac5_safety_feat_dump,
};
struct mac_device_info *dwmac4_setup(void __iomem *ioaddr, int mcbins,
@@ -808,7 +836,9 @@ struct mac_device_info *dwmac4_setup(void __iomem *ioaddr, int mcbins,
else
mac->dma = &dwmac4_dma_ops;
- if (*synopsys_id >= DWMAC_CORE_4_00)
+ if (*synopsys_id >= DWMAC_CORE_5_10)
+ mac->mac = &dwmac510_ops;
+ else if (*synopsys_id >= DWMAC_CORE_4_00)
mac->mac = &dwmac410_ops;
else
mac->mac = &dwmac4_ops;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c
index c728ffa095de..2a6521d33e43 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c
@@ -389,6 +389,8 @@ static void dwmac4_rd_prepare_tso_tx_desc(struct dma_desc *p, int is_fs,
static void dwmac4_release_tx_desc(struct dma_desc *p, int mode)
{
+ p->des0 = 0;
+ p->des1 = 0;
p->des2 = 0;
p->des3 = 0;
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
index c110f6850ffa..d37d457306d1 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
@@ -373,6 +373,12 @@ static void dwmac4_get_hw_feature(void __iomem *ioaddr,
/* IEEE 1588-2002 */
dma_cap->time_stamp = 0;
+
+ /* MAC HW feature3 */
+ hw_cap = readl(ioaddr + GMAC_HW_FEATURE3);
+
+ /* 5.10 Features */
+ dma_cap->asp = (hw_cap & GMAC_HW_FEAT_ASP) >> 28;
}
/* Enable/disable TSO feature and set MSS */
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac5.c b/drivers/net/ethernet/stmicro/stmmac/dwmac5.c
new file mode 100644
index 000000000000..860de39999c7
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac5.c
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+// Copyright (c) 2017 Synopsys, Inc. and/or its affiliates.
+// stmmac Support for 5.xx Ethernet QoS cores
+
+#include <linux/bitops.h>
+#include <linux/iopoll.h>
+#include "common.h"
+#include "dwmac4.h"
+#include "dwmac5.h"
+
+struct dwmac5_error_desc {
+ bool valid;
+ const char *desc;
+ const char *detailed_desc;
+};
+
+#define STAT_OFF(field) offsetof(struct stmmac_safety_stats, field)
+
+static void dwmac5_log_error(struct net_device *ndev, u32 value, bool corr,
+ const char *module_name, const struct dwmac5_error_desc *desc,
+ unsigned long field_offset, struct stmmac_safety_stats *stats)
+{
+ unsigned long loc, mask;
+ u8 *bptr = (u8 *)stats;
+ unsigned long *ptr;
+
+ ptr = (unsigned long *)(bptr + field_offset);
+
+ mask = value;
+ for_each_set_bit(loc, &mask, 32) {
+ netdev_err(ndev, "Found %s error in %s: '%s: %s'\n", corr ?
+ "correctable" : "uncorrectable", module_name,
+ desc[loc].desc, desc[loc].detailed_desc);
+
+ /* Update counters */
+ ptr[loc]++;
+ }
+}
+
+static const struct dwmac5_error_desc dwmac5_mac_errors[32]= {
+ { true, "ATPES", "Application Transmit Interface Parity Check Error" },
+ { true, "TPES", "TSO Data Path Parity Check Error" },
+ { true, "RDPES", "Read Descriptor Parity Check Error" },
+ { true, "MPES", "MTL Data Path Parity Check Error" },
+ { true, "MTSPES", "MTL TX Status Data Path Parity Check Error" },
+ { true, "ARPES", "Application Receive Interface Data Path Parity Check Error" },
+ { true, "CWPES", "CSR Write Data Path Parity Check Error" },
+ { true, "ASRPES", "AXI Slave Read Data Path Parity Check Error" },
+ { true, "TTES", "TX FSM Timeout Error" },
+ { true, "RTES", "RX FSM Timeout Error" },
+ { true, "CTES", "CSR FSM Timeout Error" },
+ { true, "ATES", "APP FSM Timeout Error" },
+ { true, "PTES", "PTP FSM Timeout Error" },
+ { true, "T125ES", "TX125 FSM Timeout Error" },
+ { true, "R125ES", "RX125 FSM Timeout Error" },
+ { true, "RVCTES", "REV MDC FSM Timeout Error" },
+ { true, "MSTTES", "Master Read/Write Timeout Error" },
+ { true, "SLVTES", "Slave Read/Write Timeout Error" },
+ { true, "ATITES", "Application Timeout on ATI Interface Error" },
+ { true, "ARITES", "Application Timeout on ARI Interface Error" },
+ { false, "UNKNOWN", "Unknown Error" }, /* 20 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 21 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 22 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 23 */
+ { true, "FSMPES", "FSM State Parity Error" },
+ { false, "UNKNOWN", "Unknown Error" }, /* 25 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 26 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 27 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 28 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 29 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 30 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 31 */
+};
+
+static void dwmac5_handle_mac_err(struct net_device *ndev,
+ void __iomem *ioaddr, bool correctable,
+ struct stmmac_safety_stats *stats)
+{
+ u32 value;
+
+ value = readl(ioaddr + MAC_DPP_FSM_INT_STATUS);
+ writel(value, ioaddr + MAC_DPP_FSM_INT_STATUS);
+
+ dwmac5_log_error(ndev, value, correctable, "MAC", dwmac5_mac_errors,
+ STAT_OFF(mac_errors), stats);
+}
+
+static const struct dwmac5_error_desc dwmac5_mtl_errors[32]= {
+ { true, "TXCES", "MTL TX Memory Error" },
+ { true, "TXAMS", "MTL TX Memory Address Mismatch Error" },
+ { true, "TXUES", "MTL TX Memory Error" },
+ { false, "UNKNOWN", "Unknown Error" }, /* 3 */
+ { true, "RXCES", "MTL RX Memory Error" },
+ { true, "RXAMS", "MTL RX Memory Address Mismatch Error" },
+ { true, "RXUES", "MTL RX Memory Error" },
+ { false, "UNKNOWN", "Unknown Error" }, /* 7 */
+ { true, "ECES", "MTL EST Memory Error" },
+ { true, "EAMS", "MTL EST Memory Address Mismatch Error" },
+ { true, "EUES", "MTL EST Memory Error" },
+ { false, "UNKNOWN", "Unknown Error" }, /* 11 */
+ { true, "RPCES", "MTL RX Parser Memory Error" },
+ { true, "RPAMS", "MTL RX Parser Memory Address Mismatch Error" },
+ { true, "RPUES", "MTL RX Parser Memory Error" },
+ { false, "UNKNOWN", "Unknown Error" }, /* 15 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 16 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 17 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 18 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 19 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 20 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 21 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 22 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 23 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 24 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 25 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 26 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 27 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 28 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 29 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 30 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 31 */
+};
+
+static void dwmac5_handle_mtl_err(struct net_device *ndev,
+ void __iomem *ioaddr, bool correctable,
+ struct stmmac_safety_stats *stats)
+{
+ u32 value;
+
+ value = readl(ioaddr + MTL_ECC_INT_STATUS);
+ writel(value, ioaddr + MTL_ECC_INT_STATUS);
+
+ dwmac5_log_error(ndev, value, correctable, "MTL", dwmac5_mtl_errors,
+ STAT_OFF(mtl_errors), stats);
+}
+
+static const struct dwmac5_error_desc dwmac5_dma_errors[32]= {
+ { true, "TCES", "DMA TSO Memory Error" },
+ { true, "TAMS", "DMA TSO Memory Address Mismatch Error" },
+ { true, "TUES", "DMA TSO Memory Error" },
+ { false, "UNKNOWN", "Unknown Error" }, /* 3 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 4 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 5 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 6 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 7 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 8 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 9 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 10 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 11 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 12 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 13 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 14 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 15 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 16 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 17 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 18 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 19 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 20 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 21 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 22 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 23 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 24 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 25 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 26 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 27 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 28 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 29 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 30 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 31 */
+};
+
+static void dwmac5_handle_dma_err(struct net_device *ndev,
+ void __iomem *ioaddr, bool correctable,
+ struct stmmac_safety_stats *stats)
+{
+ u32 value;
+
+ value = readl(ioaddr + DMA_ECC_INT_STATUS);
+ writel(value, ioaddr + DMA_ECC_INT_STATUS);
+
+ dwmac5_log_error(ndev, value, correctable, "DMA", dwmac5_dma_errors,
+ STAT_OFF(dma_errors), stats);
+}
+
+int dwmac5_safety_feat_config(void __iomem *ioaddr, unsigned int asp)
+{
+ u32 value;
+
+ if (!asp)
+ return -EINVAL;
+
+ /* 1. Enable Safety Features */
+ value = readl(ioaddr + MTL_ECC_CONTROL);
+ value |= TSOEE; /* TSO ECC */
+ value |= MRXPEE; /* MTL RX Parser ECC */
+ value |= MESTEE; /* MTL EST ECC */
+ value |= MRXEE; /* MTL RX FIFO ECC */
+ value |= MTXEE; /* MTL TX FIFO ECC */
+ writel(value, ioaddr + MTL_ECC_CONTROL);
+
+ /* 2. Enable MTL Safety Interrupts */
+ value = readl(ioaddr + MTL_ECC_INT_ENABLE);
+ value |= RPCEIE; /* RX Parser Memory Correctable Error */
+ value |= ECEIE; /* EST Memory Correctable Error */
+ value |= RXCEIE; /* RX Memory Correctable Error */
+ value |= TXCEIE; /* TX Memory Correctable Error */
+ writel(value, ioaddr + MTL_ECC_INT_ENABLE);
+
+ /* 3. Enable DMA Safety Interrupts */
+ value = readl(ioaddr + DMA_ECC_INT_ENABLE);
+ value |= TCEIE; /* TSO Memory Correctable Error */
+ writel(value, ioaddr + DMA_ECC_INT_ENABLE);
+
+ /* Only ECC Protection for External Memory feature is selected */
+ if (asp <= 0x1)
+ return 0;
+
+ /* 5. Enable Parity and Timeout for FSM */
+ value = readl(ioaddr + MAC_FSM_CONTROL);
+ value |= PRTYEN; /* FSM Parity Feature */
+ value |= TMOUTEN; /* FSM Timeout Feature */
+ writel(value, ioaddr + MAC_FSM_CONTROL);
+
+ /* 4. Enable Data Parity Protection */
+ value = readl(ioaddr + MTL_DPP_CONTROL);
+ value |= EDPP;
+ writel(value, ioaddr + MTL_DPP_CONTROL);
+
+ /*
+ * All the Automotive Safety features are selected without the "Parity
+ * Port Enable for external interface" feature.
+ */
+ if (asp <= 0x2)
+ return 0;
+
+ value |= EPSI;
+ writel(value, ioaddr + MTL_DPP_CONTROL);
+ return 0;
+}
+
+bool dwmac5_safety_feat_irq_status(struct net_device *ndev,
+ void __iomem *ioaddr, unsigned int asp,
+ struct stmmac_safety_stats *stats)
+{
+ bool ret = false, err, corr;
+ u32 mtl, dma;
+
+ if (!asp)
+ return false;
+
+ mtl = readl(ioaddr + MTL_SAFETY_INT_STATUS);
+ dma = readl(ioaddr + DMA_SAFETY_INT_STATUS);
+
+ err = (mtl & MCSIS) || (dma & MCSIS);
+ corr = false;
+ if (err) {
+ dwmac5_handle_mac_err(ndev, ioaddr, corr, stats);
+ ret |= !corr;
+ }
+
+ err = (mtl & (MEUIS | MECIS)) || (dma & (MSUIS | MSCIS));
+ corr = (mtl & MECIS) || (dma & MSCIS);
+ if (err) {
+ dwmac5_handle_mtl_err(ndev, ioaddr, corr, stats);
+ ret |= !corr;
+ }
+
+ err = dma & (DEUIS | DECIS);
+ corr = dma & DECIS;
+ if (err) {
+ dwmac5_handle_dma_err(ndev, ioaddr, corr, stats);
+ ret |= !corr;
+ }
+
+ return ret;
+}
+
+static const struct dwmac5_error {
+ const struct dwmac5_error_desc *desc;
+} dwmac5_all_errors[] = {
+ { dwmac5_mac_errors },
+ { dwmac5_mtl_errors },
+ { dwmac5_dma_errors },
+};
+
+const char *dwmac5_safety_feat_dump(struct stmmac_safety_stats *stats,
+ int index, unsigned long *count)
+{
+ int module = index / 32, offset = index % 32;
+ unsigned long *ptr = (unsigned long *)stats;
+
+ if (module >= ARRAY_SIZE(dwmac5_all_errors))
+ return NULL;
+ if (!dwmac5_all_errors[module].desc[offset].valid)
+ return NULL;
+ if (count)
+ *count = *(ptr + index);
+ return dwmac5_all_errors[module].desc[offset].desc;
+}
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac5.h b/drivers/net/ethernet/stmicro/stmmac/dwmac5.h
new file mode 100644
index 000000000000..a0d2c44711b9
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac5.h
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+// Copyright (c) 2017 Synopsys, Inc. and/or its affiliates.
+// stmmac Support for 5.xx Ethernet QoS cores
+
+#ifndef __DWMAC5_H__
+#define __DWMAC5_H__
+
+#define MAC_DPP_FSM_INT_STATUS 0x00000140
+#define MAC_AXI_SLV_DPE_ADDR_STATUS 0x00000144
+#define MAC_FSM_CONTROL 0x00000148
+#define PRTYEN BIT(1)
+#define TMOUTEN BIT(0)
+
+#define MTL_ECC_CONTROL 0x00000cc0
+#define TSOEE BIT(4)
+#define MRXPEE BIT(3)
+#define MESTEE BIT(2)
+#define MRXEE BIT(1)
+#define MTXEE BIT(0)
+
+#define MTL_SAFETY_INT_STATUS 0x00000cc4
+#define MCSIS BIT(31)
+#define MEUIS BIT(1)
+#define MECIS BIT(0)
+#define MTL_ECC_INT_ENABLE 0x00000cc8
+#define RPCEIE BIT(12)
+#define ECEIE BIT(8)
+#define RXCEIE BIT(4)
+#define TXCEIE BIT(0)
+#define MTL_ECC_INT_STATUS 0x00000ccc
+#define MTL_DPP_CONTROL 0x00000ce0
+#define EPSI BIT(2)
+#define OPE BIT(1)
+#define EDPP BIT(0)
+
+#define DMA_SAFETY_INT_STATUS 0x00001080
+#define MSUIS BIT(29)
+#define MSCIS BIT(28)
+#define DEUIS BIT(1)
+#define DECIS BIT(0)
+#define DMA_ECC_INT_ENABLE 0x00001084
+#define TCEIE BIT(0)
+#define DMA_ECC_INT_STATUS 0x00001088
+
+int dwmac5_safety_feat_config(void __iomem *ioaddr, unsigned int asp);
+bool dwmac5_safety_feat_irq_status(struct net_device *ndev,
+ void __iomem *ioaddr, unsigned int asp,
+ struct stmmac_safety_stats *stats);
+const char *dwmac5_safety_feat_dump(struct stmmac_safety_stats *stats,
+ int index, unsigned long *count);
+
+#endif /* __DWMAC5_H__ */
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
index a916e13624eb..da50451f8999 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
@@ -58,6 +58,7 @@ struct stmmac_tx_queue {
unsigned int dirty_tx;
dma_addr_t dma_tx_phy;
u32 tx_tail_addr;
+ u32 mss;
};
struct stmmac_rx_queue {
@@ -113,6 +114,7 @@ struct stmmac_priv {
int mii_irq[PHY_MAX_ADDR];
struct stmmac_extra_stats xstats ____cacheline_aligned_in_smp;
+ struct stmmac_safety_stats sstats;
struct plat_stmmacenet_data *plat;
struct dma_features dma_cap;
struct stmmac_counters mmc;
@@ -138,13 +140,23 @@ struct stmmac_priv {
spinlock_t ptp_lock;
void __iomem *mmcaddr;
void __iomem *ptpaddr;
- u32 mss;
#ifdef CONFIG_DEBUG_FS
struct dentry *dbgfs_dir;
struct dentry *dbgfs_rings_status;
struct dentry *dbgfs_dma_cap;
#endif
+
+ unsigned long state;
+ struct workqueue_struct *wq;
+ struct work_struct service_task;
+};
+
+enum stmmac_state {
+ STMMAC_DOWN,
+ STMMAC_RESET_REQUESTED,
+ STMMAC_RESETING,
+ STMMAC_SERVICE_SCHED,
};
int stmmac_mdio_unregister(struct net_device *ndev);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
index af30b4857c3b..2c6ed47704fc 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
@@ -523,11 +523,23 @@ stmmac_set_pauseparam(struct net_device *netdev,
static void stmmac_get_ethtool_stats(struct net_device *dev,
struct ethtool_stats *dummy, u64 *data)
{
+ const char *(*dump)(struct stmmac_safety_stats *stats, int index,
+ unsigned long *count);
struct stmmac_priv *priv = netdev_priv(dev);
u32 rx_queues_count = priv->plat->rx_queues_to_use;
u32 tx_queues_count = priv->plat->tx_queues_to_use;
+ unsigned long count;
int i, j = 0;
+ if (priv->dma_cap.asp && priv->hw->mac->safety_feat_dump) {
+ dump = priv->hw->mac->safety_feat_dump;
+
+ for (i = 0; i < STMMAC_SAFETY_FEAT_SIZE; i++) {
+ if (dump(&priv->sstats, i, &count))
+ data[j++] = count;
+ }
+ }
+
/* Update the DMA HW counters for dwmac10/100 */
if (priv->hw->dma->dma_diagnostic_fr)
priv->hw->dma->dma_diagnostic_fr(&dev->stats,
@@ -569,7 +581,9 @@ static void stmmac_get_ethtool_stats(struct net_device *dev,
static int stmmac_get_sset_count(struct net_device *netdev, int sset)
{
struct stmmac_priv *priv = netdev_priv(netdev);
- int len;
+ const char *(*dump)(struct stmmac_safety_stats *stats, int index,
+ unsigned long *count);
+ int i, len, safety_len = 0;
switch (sset) {
case ETH_SS_STATS:
@@ -577,6 +591,16 @@ static int stmmac_get_sset_count(struct net_device *netdev, int sset)
if (priv->dma_cap.rmon)
len += STMMAC_MMC_STATS_LEN;
+ if (priv->dma_cap.asp && priv->hw->mac->safety_feat_dump) {
+ dump = priv->hw->mac->safety_feat_dump;
+
+ for (i = 0; i < STMMAC_SAFETY_FEAT_SIZE; i++) {
+ if (dump(&priv->sstats, i, NULL))
+ safety_len++;
+ }
+
+ len += safety_len;
+ }
return len;
default:
@@ -589,9 +613,22 @@ static void stmmac_get_strings(struct net_device *dev, u32 stringset, u8 *data)
int i;
u8 *p = data;
struct stmmac_priv *priv = netdev_priv(dev);
+ const char *(*dump)(struct stmmac_safety_stats *stats, int index,
+ unsigned long *count);
switch (stringset) {
case ETH_SS_STATS:
+ if (priv->dma_cap.asp && priv->hw->mac->safety_feat_dump) {
+ dump = priv->hw->mac->safety_feat_dump;
+ for (i = 0; i < STMMAC_SAFETY_FEAT_SIZE; i++) {
+ const char *desc = dump(&priv->sstats, i, NULL);
+
+ if (desc) {
+ memcpy(p, desc, ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ }
+ }
+ }
if (priv->dma_cap.rmon)
for (i = 0; i < STMMAC_MMC_STATS_LEN; i++) {
memcpy(p, stmmac_mmc[i].stat_string,
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 7ad841434ec8..b65e2d144698 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -57,36 +57,36 @@
/* Module parameters */
#define TX_TIMEO 5000
static int watchdog = TX_TIMEO;
-module_param(watchdog, int, S_IRUGO | S_IWUSR);
+module_param(watchdog, int, 0644);
MODULE_PARM_DESC(watchdog, "Transmit timeout in milliseconds (default 5s)");
static int debug = -1;
-module_param(debug, int, S_IRUGO | S_IWUSR);
+module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Message Level (-1: default, 0: no output, 16: all)");
static int phyaddr = -1;
-module_param(phyaddr, int, S_IRUGO);
+module_param(phyaddr, int, 0444);
MODULE_PARM_DESC(phyaddr, "Physical device address");
#define STMMAC_TX_THRESH (DMA_TX_SIZE / 4)
#define STMMAC_RX_THRESH (DMA_RX_SIZE / 4)
static int flow_ctrl = FLOW_OFF;
-module_param(flow_ctrl, int, S_IRUGO | S_IWUSR);
+module_param(flow_ctrl, int, 0644);
MODULE_PARM_DESC(flow_ctrl, "Flow control ability [on/off]");
static int pause = PAUSE_TIME;
-module_param(pause, int, S_IRUGO | S_IWUSR);
+module_param(pause, int, 0644);
MODULE_PARM_DESC(pause, "Flow Control Pause Time");
#define TC_DEFAULT 64
static int tc = TC_DEFAULT;
-module_param(tc, int, S_IRUGO | S_IWUSR);
+module_param(tc, int, 0644);
MODULE_PARM_DESC(tc, "DMA threshold control value");
#define DEFAULT_BUFSIZE 1536
static int buf_sz = DEFAULT_BUFSIZE;
-module_param(buf_sz, int, S_IRUGO | S_IWUSR);
+module_param(buf_sz, int, 0644);
MODULE_PARM_DESC(buf_sz, "DMA buffer size");
#define STMMAC_RX_COPYBREAK 256
@@ -97,7 +97,7 @@ static const u32 default_msg_level = (NETIF_MSG_DRV | NETIF_MSG_PROBE |
#define STMMAC_DEFAULT_LPI_TIMER 1000
static int eee_timer = STMMAC_DEFAULT_LPI_TIMER;
-module_param(eee_timer, int, S_IRUGO | S_IWUSR);
+module_param(eee_timer, int, 0644);
MODULE_PARM_DESC(eee_timer, "LPI tx expiration time in msec");
#define STMMAC_LPI_T(x) (jiffies + msecs_to_jiffies(x))
@@ -105,7 +105,7 @@ MODULE_PARM_DESC(eee_timer, "LPI tx expiration time in msec");
* but allow user to force to use the chain instead of the ring
*/
static unsigned int chain_mode;
-module_param(chain_mode, int, S_IRUGO);
+module_param(chain_mode, int, 0444);
MODULE_PARM_DESC(chain_mode, "To use chain instead of ring mode");
static irqreturn_t stmmac_interrupt(int irq, void *dev_id);
@@ -196,6 +196,20 @@ static void stmmac_start_all_queues(struct stmmac_priv *priv)
netif_tx_start_queue(netdev_get_tx_queue(priv->dev, queue));
}
+static void stmmac_service_event_schedule(struct stmmac_priv *priv)
+{
+ if (!test_bit(STMMAC_DOWN, &priv->state) &&
+ !test_and_set_bit(STMMAC_SERVICE_SCHED, &priv->state))
+ queue_work(priv->wq, &priv->service_task);
+}
+
+static void stmmac_global_err(struct stmmac_priv *priv)
+{
+ netif_carrier_off(priv->dev);
+ set_bit(STMMAC_RESET_REQUESTED, &priv->state);
+ stmmac_service_event_schedule(priv);
+}
+
/**
* stmmac_clk_csr_set - dynamically set the MDC clock
* @priv: driver private structure
@@ -1355,6 +1369,7 @@ static int init_dma_tx_desc_rings(struct net_device *dev)
tx_q->dirty_tx = 0;
tx_q->cur_tx = 0;
+ tx_q->mss = 0;
netdev_tx_reset_queue(netdev_get_tx_queue(priv->dev, queue));
}
@@ -1843,6 +1858,11 @@ static void stmmac_tx_clean(struct stmmac_priv *priv, u32 queue)
if (unlikely(status & tx_dma_own))
break;
+ /* Make sure descriptor fields are read after reading
+ * the own bit.
+ */
+ dma_rmb();
+
/* Just consider the last segment and ...*/
if (likely(!(status & tx_not_ls))) {
/* ... verify the status error condition */
@@ -1946,6 +1966,7 @@ static void stmmac_tx_err(struct stmmac_priv *priv, u32 chan)
(i == DMA_TX_SIZE - 1));
tx_q->dirty_tx = 0;
tx_q->cur_tx = 0;
+ tx_q->mss = 0;
netdev_tx_reset_queue(netdev_get_tx_queue(priv->dev, chan));
stmmac_start_tx_dma(priv, chan);
@@ -1993,6 +2014,22 @@ static void stmmac_set_dma_operation_mode(struct stmmac_priv *priv, u32 txmode,
}
}
+static bool stmmac_safety_feat_interrupt(struct stmmac_priv *priv)
+{
+ bool ret = false;
+
+ /* Safety features are only available in cores >= 5.10 */
+ if (priv->synopsys_id < DWMAC_CORE_5_10)
+ return ret;
+ if (priv->hw->mac->safety_feat_irq_status)
+ ret = priv->hw->mac->safety_feat_irq_status(priv->dev,
+ priv->ioaddr, priv->dma_cap.asp, &priv->sstats);
+
+ if (ret)
+ stmmac_global_err(priv);
+ return ret;
+}
+
/**
* stmmac_dma_interrupt - DMA ISR
* @priv: driver private structure
@@ -2430,7 +2467,7 @@ static void stmmac_mac_config_rx_queues_routing(struct stmmac_priv *priv)
continue;
packet = priv->plat->rx_queues_cfg[queue].pkt_route;
- priv->hw->mac->rx_queue_prio(priv->hw, packet, queue);
+ priv->hw->mac->rx_queue_routing(priv->hw, packet, queue);
}
}
@@ -2482,6 +2519,17 @@ static void stmmac_mtl_configuration(struct stmmac_priv *priv)
stmmac_mac_config_rx_queues_routing(priv);
}
+static void stmmac_safety_feat_configuration(struct stmmac_priv *priv)
+{
+ if (priv->hw->mac->safety_feat_config && priv->dma_cap.asp) {
+ netdev_info(priv->dev, "Enabling Safety Features\n");
+ priv->hw->mac->safety_feat_config(priv->ioaddr,
+ priv->dma_cap.asp);
+ } else {
+ netdev_info(priv->dev, "No Safety Features support found\n");
+ }
+}
+
/**
* stmmac_hw_setup - setup mac in a usable state.
* @dev : pointer to the device structure.
@@ -2533,6 +2581,10 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp)
if (priv->synopsys_id >= DWMAC_CORE_4_00)
stmmac_mtl_configuration(priv);
+ /* Initialize Safety Features */
+ if (priv->synopsys_id >= DWMAC_CORE_5_10)
+ stmmac_safety_feat_configuration(priv);
+
ret = priv->hw->mac->rx_ipc(priv->hw);
if (!ret) {
netdev_warn(priv->dev, "RX IPC Checksum Offload disabled\n");
@@ -2632,7 +2684,6 @@ static int stmmac_open(struct net_device *dev)
priv->dma_buf_sz = STMMAC_ALIGN(buf_sz);
priv->rx_copybreak = STMMAC_RX_COPYBREAK;
- priv->mss = 0;
ret = alloc_dma_desc_resources(priv);
if (ret < 0) {
@@ -2793,6 +2844,7 @@ static void stmmac_tso_allocator(struct stmmac_priv *priv, unsigned int des,
while (tmp_len > 0) {
tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, DMA_TX_SIZE);
+ WARN_ON(tx_q->tx_skbuff[tx_q->cur_tx]);
desc = tx_q->dma_tx + tx_q->cur_tx;
desc->des0 = cpu_to_le32(des + (total_len - tmp_len));
@@ -2872,11 +2924,12 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
mss = skb_shinfo(skb)->gso_size;
/* set new MSS value if needed */
- if (mss != priv->mss) {
+ if (mss != tx_q->mss) {
mss_desc = tx_q->dma_tx + tx_q->cur_tx;
priv->hw->desc->set_mss(mss_desc, mss);
- priv->mss = mss;
+ tx_q->mss = mss;
tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, DMA_TX_SIZE);
+ WARN_ON(tx_q->tx_skbuff[tx_q->cur_tx]);
}
if (netif_msg_tx_queued(priv)) {
@@ -2887,6 +2940,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
}
first_entry = tx_q->cur_tx;
+ WARN_ON(tx_q->tx_skbuff[first_entry]);
desc = tx_q->dma_tx + first_entry;
first = desc;
@@ -2926,7 +2980,6 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
tx_q->tx_skbuff_dma[tx_q->cur_tx].buf = des;
tx_q->tx_skbuff_dma[tx_q->cur_tx].len = skb_frag_size(frag);
- tx_q->tx_skbuff[tx_q->cur_tx] = NULL;
tx_q->tx_skbuff_dma[tx_q->cur_tx].map_as_page = true;
}
@@ -2980,14 +3033,21 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
tcp_hdrlen(skb) / 4, (skb->len - proto_hdr_len));
/* If context desc is used to change MSS */
- if (mss_desc)
+ if (mss_desc) {
+ /* Make sure that first descriptor has been completely
+ * written, including its own bit. This is because MSS is
+ * actually before first descriptor, so we need to make
+ * sure that MSS's own bit is the last thing written.
+ */
+ dma_wmb();
priv->hw->desc->set_tx_owner(mss_desc);
+ }
/* The own bit must be the latest setting done when prepare the
* descriptor and then barrier is needed to make sure that
* all is coherent before granting the DMA engine.
*/
- dma_wmb();
+ wmb();
if (netif_msg_pktdata(priv)) {
pr_info("%s: curr=%d dirty=%d f=%d, e=%d, f_p=%p, nfrags %d\n",
@@ -3062,6 +3122,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
entry = tx_q->cur_tx;
first_entry = entry;
+ WARN_ON(tx_q->tx_skbuff[first_entry]);
csum_insertion = (skb->ip_summed == CHECKSUM_PARTIAL);
@@ -3090,6 +3151,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
bool last_segment = (i == (nfrags - 1));
entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE);
+ WARN_ON(tx_q->tx_skbuff[entry]);
if (likely(priv->extend_desc))
desc = (struct dma_desc *)(tx_q->dma_etx + entry);
@@ -3101,8 +3163,6 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
if (dma_mapping_error(priv->device, des))
goto dma_map_err; /* should reuse desc w/o issues */
- tx_q->tx_skbuff[entry] = NULL;
-
tx_q->tx_skbuff_dma[entry].buf = des;
if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00))
desc->des0 = cpu_to_le32(des);
@@ -3211,7 +3271,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
* descriptor and then barrier is needed to make sure that
* all is coherent before granting the DMA engine.
*/
- dma_wmb();
+ wmb();
}
netdev_tx_sent_queue(netdev_get_tx_queue(dev, queue), skb->len);
@@ -3435,8 +3495,13 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
/* ACS is set; GMAC core strips PAD/FCS for IEEE 802.3
* Type frames (LLC/LLC-SNAP)
+ *
+ * llc_snap is never checked in GMAC >= 4, so this ACS
+ * feature is always disabled and packets need to be
+ * stripped manually.
*/
- if (unlikely(status != llc_snap))
+ if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00) ||
+ unlikely(status != llc_snap))
frame_len -= ETH_FCS_LEN;
if (netif_msg_rx_status(priv)) {
@@ -3572,12 +3637,8 @@ static int stmmac_poll(struct napi_struct *napi, int budget)
static void stmmac_tx_timeout(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
- u32 tx_count = priv->plat->tx_queues_to_use;
- u32 chan;
- /* Clear Tx resources and restart transmitting again */
- for (chan = 0; chan < tx_count; chan++)
- stmmac_tx_err(priv, chan);
+ stmmac_global_err(priv);
}
/**
@@ -3701,6 +3762,13 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id)
return IRQ_NONE;
}
+ /* Check if adapter is up */
+ if (test_bit(STMMAC_DOWN, &priv->state))
+ return IRQ_HANDLED;
+ /* Check if a fatal error happened */
+ if (stmmac_safety_feat_interrupt(priv))
+ return IRQ_HANDLED;
+
/* To handle GMAC own interrupts */
if ((priv->plat->has_gmac) || (priv->plat->has_gmac4)) {
int status = priv->hw->mac->host_irq_status(priv->hw,
@@ -3986,7 +4054,7 @@ static int stmmac_init_fs(struct net_device *dev)
/* Entry to report DMA RX/TX rings */
priv->dbgfs_rings_status =
- debugfs_create_file("descriptors_status", S_IRUGO,
+ debugfs_create_file("descriptors_status", 0444,
priv->dbgfs_dir, dev,
&stmmac_rings_status_fops);
@@ -3998,9 +4066,9 @@ static int stmmac_init_fs(struct net_device *dev)
}
/* Entry to report the DMA HW features */
- priv->dbgfs_dma_cap = debugfs_create_file("dma_cap", S_IRUGO,
- priv->dbgfs_dir,
- dev, &stmmac_dma_cap_fops);
+ priv->dbgfs_dma_cap = debugfs_create_file("dma_cap", 0444,
+ priv->dbgfs_dir,
+ dev, &stmmac_dma_cap_fops);
if (!priv->dbgfs_dma_cap || IS_ERR(priv->dbgfs_dma_cap)) {
netdev_err(priv->dev, "ERROR creating stmmac MMC debugfs file\n");
@@ -4036,6 +4104,37 @@ static const struct net_device_ops stmmac_netdev_ops = {
.ndo_set_mac_address = stmmac_set_mac_address,
};
+static void stmmac_reset_subtask(struct stmmac_priv *priv)
+{
+ if (!test_and_clear_bit(STMMAC_RESET_REQUESTED, &priv->state))
+ return;
+ if (test_bit(STMMAC_DOWN, &priv->state))
+ return;
+
+ netdev_err(priv->dev, "Reset adapter.\n");
+
+ rtnl_lock();
+ netif_trans_update(priv->dev);
+ while (test_and_set_bit(STMMAC_RESETING, &priv->state))
+ usleep_range(1000, 2000);
+
+ set_bit(STMMAC_DOWN, &priv->state);
+ dev_close(priv->dev);
+ dev_open(priv->dev);
+ clear_bit(STMMAC_DOWN, &priv->state);
+ clear_bit(STMMAC_RESETING, &priv->state);
+ rtnl_unlock();
+}
+
+static void stmmac_service_task(struct work_struct *work)
+{
+ struct stmmac_priv *priv = container_of(work, struct stmmac_priv,
+ service_task);
+
+ stmmac_reset_subtask(priv);
+ clear_bit(STMMAC_SERVICE_SCHED, &priv->state);
+}
+
/**
* stmmac_hw_init - Init the MAC device
* @priv: driver private structure
@@ -4197,6 +4296,15 @@ int stmmac_dvr_probe(struct device *device,
/* Verify driver arguments */
stmmac_verify_args();
+ /* Allocate workqueue */
+ priv->wq = create_singlethread_workqueue("stmmac_wq");
+ if (!priv->wq) {
+ dev_err(priv->device, "failed to create workqueue\n");
+ goto error_wq;
+ }
+
+ INIT_WORK(&priv->service_task, stmmac_service_task);
+
/* Override with kernel parameters if supplied XXX CRS XXX
* this needs to have multiple instances
*/
@@ -4327,6 +4435,8 @@ error_mdio_register:
netif_napi_del(&rx_q->napi);
}
error_hw_init:
+ destroy_workqueue(priv->wq);
+error_wq:
free_netdev(ndev);
return ret;
@@ -4359,6 +4469,7 @@ int stmmac_dvr_remove(struct device *dev)
priv->hw->pcs != STMMAC_PCS_TBI &&
priv->hw->pcs != STMMAC_PCS_RTBI)
stmmac_mdio_unregister(ndev);
+ destroy_workqueue(priv->wq);
free_netdev(ndev);
return 0;
@@ -4436,6 +4547,7 @@ static void stmmac_reset_queues_param(struct stmmac_priv *priv)
tx_q->cur_tx = 0;
tx_q->dirty_tx = 0;
+ tx_q->mss = 0;
}
}
@@ -4481,11 +4593,6 @@ int stmmac_resume(struct device *dev)
stmmac_reset_queues_param(priv);
- /* reset private mss value to force mss context settings at
- * next tso xmit (only used for gmac4).
- */
- priv->mss = 0;
-
stmmac_clear_descriptors(priv);
stmmac_hw_setup(ndev, false);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
index 05f122b8424a..ebd3e5ffa73c 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -135,13 +135,14 @@ static struct stmmac_axi *stmmac_axi_setup(struct platform_device *pdev)
* stmmac_mtl_setup - parse DT parameters for multiple queues configuration
* @pdev: platform device
*/
-static void stmmac_mtl_setup(struct platform_device *pdev,
- struct plat_stmmacenet_data *plat)
+static int stmmac_mtl_setup(struct platform_device *pdev,
+ struct plat_stmmacenet_data *plat)
{
struct device_node *q_node;
struct device_node *rx_node;
struct device_node *tx_node;
u8 queue = 0;
+ int ret = 0;
/* For backwards-compatibility with device trees that don't have any
* snps,mtl-rx-config or snps,mtl-tx-config properties, we fall back
@@ -159,12 +160,12 @@ static void stmmac_mtl_setup(struct platform_device *pdev,
rx_node = of_parse_phandle(pdev->dev.of_node, "snps,mtl-rx-config", 0);
if (!rx_node)
- return;
+ return ret;
tx_node = of_parse_phandle(pdev->dev.of_node, "snps,mtl-tx-config", 0);
if (!tx_node) {
of_node_put(rx_node);
- return;
+ return ret;
}
/* Processing RX queues common config */
@@ -220,6 +221,11 @@ static void stmmac_mtl_setup(struct platform_device *pdev,
queue++;
}
+ if (queue != plat->rx_queues_to_use) {
+ ret = -EINVAL;
+ dev_err(&pdev->dev, "Not all RX queues were configured\n");
+ goto out;
+ }
/* Processing TX queues common config */
if (of_property_read_u32(tx_node, "snps,tx-queues-to-use",
@@ -281,10 +287,18 @@ static void stmmac_mtl_setup(struct platform_device *pdev,
queue++;
}
+ if (queue != plat->tx_queues_to_use) {
+ ret = -EINVAL;
+ dev_err(&pdev->dev, "Not all TX queues were configured\n");
+ goto out;
+ }
+out:
of_node_put(rx_node);
of_node_put(tx_node);
of_node_put(q_node);
+
+ return ret;
}
/**
@@ -376,6 +390,7 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac)
struct device_node *np = pdev->dev.of_node;
struct plat_stmmacenet_data *plat;
struct stmmac_dma_cfg *dma_cfg;
+ int rc;
plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL);
if (!plat)
@@ -402,8 +417,9 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac)
dev_warn(&pdev->dev, "snps,phy-addr property is deprecated\n");
/* To Configure PHY by using all device-tree supported properties */
- if (stmmac_dt_phy(plat, np, &pdev->dev))
- return ERR_PTR(-ENODEV);
+ rc = stmmac_dt_phy(plat, np, &pdev->dev);
+ if (rc)
+ return ERR_PTR(rc);
of_property_read_u32(np, "tx-fifo-depth", &plat->tx_fifo_size);
@@ -499,7 +515,11 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac)
plat->axi = stmmac_axi_setup(pdev);
- stmmac_mtl_setup(pdev, plat);
+ rc = stmmac_mtl_setup(pdev, plat);
+ if (rc) {
+ stmmac_remove_config_dt(pdev, plat);
+ return ERR_PTR(rc);
+ }
/* clock setup */
plat->stmmac_clk = devm_clk_get(&pdev->dev,
diff --git a/drivers/net/ethernet/sun/niu.c b/drivers/net/ethernet/sun/niu.c
index 8dd545fed30d..f081de4f38d7 100644
--- a/drivers/net/ethernet/sun/niu.c
+++ b/drivers/net/ethernet/sun/niu.c
@@ -9437,11 +9437,11 @@ static ssize_t show_num_ports(struct device *dev,
}
static struct device_attribute niu_parent_attributes[] = {
- __ATTR(port_phy, S_IRUGO, show_port_phy, NULL),
- __ATTR(plat_type, S_IRUGO, show_plat_type, NULL),
- __ATTR(rxchan_per_port, S_IRUGO, show_rxchan_per_port, NULL),
- __ATTR(txchan_per_port, S_IRUGO, show_txchan_per_port, NULL),
- __ATTR(num_ports, S_IRUGO, show_num_ports, NULL),
+ __ATTR(port_phy, 0444, show_port_phy, NULL),
+ __ATTR(plat_type, 0444, show_plat_type, NULL),
+ __ATTR(rxchan_per_port, 0444, show_rxchan_per_port, NULL),
+ __ATTR(txchan_per_port, 0444, show_txchan_per_port, NULL),
+ __ATTR(num_ports, 0444, show_num_ports, NULL),
{}
};
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index b2b30c9df037..74f828412055 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -35,6 +35,7 @@
#include <linux/of_net.h>
#include <linux/of_device.h>
#include <linux/if_vlan.h>
+#include <linux/kmemleak.h>
#include <linux/pinctrl/consumer.h>
@@ -120,14 +121,18 @@ do { \
#define CPDMA_RXCP 0x60
#define CPSW_POLL_WEIGHT 64
+#define CPSW_RX_VLAN_ENCAP_HDR_SIZE 4
#define CPSW_MIN_PACKET_SIZE (VLAN_ETH_ZLEN)
-#define CPSW_MAX_PACKET_SIZE (VLAN_ETH_FRAME_LEN + ETH_FCS_LEN)
+#define CPSW_MAX_PACKET_SIZE (VLAN_ETH_FRAME_LEN +\
+ ETH_FCS_LEN +\
+ CPSW_RX_VLAN_ENCAP_HDR_SIZE)
#define RX_PRIORITY_MAPPING 0x76543210
#define TX_PRIORITY_MAPPING 0x33221100
-#define CPDMA_TX_PRIORITY_MAP 0x01234567
+#define CPDMA_TX_PRIORITY_MAP 0x76543210
#define CPSW_VLAN_AWARE BIT(1)
+#define CPSW_RX_VLAN_ENCAP BIT(2)
#define CPSW_ALE_VLAN_AWARE 1
#define CPSW_FIFO_NORMAL_MODE (0 << 16)
@@ -148,6 +153,18 @@ do { \
#define CPSW_MAX_QUEUES 8
#define CPSW_CPDMA_DESCS_POOL_SIZE_DEFAULT 256
+#define CPSW_RX_VLAN_ENCAP_HDR_PRIO_SHIFT 29
+#define CPSW_RX_VLAN_ENCAP_HDR_PRIO_MSK GENMASK(2, 0)
+#define CPSW_RX_VLAN_ENCAP_HDR_VID_SHIFT 16
+#define CPSW_RX_VLAN_ENCAP_HDR_PKT_TYPE_SHIFT 8
+#define CPSW_RX_VLAN_ENCAP_HDR_PKT_TYPE_MSK GENMASK(1, 0)
+enum {
+ CPSW_RX_VLAN_ENCAP_HDR_PKT_VLAN_TAG = 0,
+ CPSW_RX_VLAN_ENCAP_HDR_PKT_RESERV,
+ CPSW_RX_VLAN_ENCAP_HDR_PKT_PRIO_TAG,
+ CPSW_RX_VLAN_ENCAP_HDR_PKT_UNTAG,
+};
+
static int debug_level;
module_param(debug_level, int, 0);
MODULE_PARM_DESC(debug_level, "cpsw debug level (NETIF_MSG bits)");
@@ -718,6 +735,49 @@ static void cpsw_tx_handler(void *token, int len, int status)
dev_kfree_skb_any(skb);
}
+static void cpsw_rx_vlan_encap(struct sk_buff *skb)
+{
+ struct cpsw_priv *priv = netdev_priv(skb->dev);
+ struct cpsw_common *cpsw = priv->cpsw;
+ u32 rx_vlan_encap_hdr = *((u32 *)skb->data);
+ u16 vtag, vid, prio, pkt_type;
+
+ /* Remove VLAN header encapsulation word */
+ skb_pull(skb, CPSW_RX_VLAN_ENCAP_HDR_SIZE);
+
+ pkt_type = (rx_vlan_encap_hdr >>
+ CPSW_RX_VLAN_ENCAP_HDR_PKT_TYPE_SHIFT) &
+ CPSW_RX_VLAN_ENCAP_HDR_PKT_TYPE_MSK;
+ /* Ignore unknown & Priority-tagged packets*/
+ if (pkt_type == CPSW_RX_VLAN_ENCAP_HDR_PKT_RESERV ||
+ pkt_type == CPSW_RX_VLAN_ENCAP_HDR_PKT_PRIO_TAG)
+ return;
+
+ vid = (rx_vlan_encap_hdr >>
+ CPSW_RX_VLAN_ENCAP_HDR_VID_SHIFT) &
+ VLAN_VID_MASK;
+ /* Ignore vid 0 and pass packet as is */
+ if (!vid)
+ return;
+ /* Ignore default vlans in dual mac mode */
+ if (cpsw->data.dual_emac &&
+ vid == cpsw->slaves[priv->emac_port].port_vlan)
+ return;
+
+ prio = (rx_vlan_encap_hdr >>
+ CPSW_RX_VLAN_ENCAP_HDR_PRIO_SHIFT) &
+ CPSW_RX_VLAN_ENCAP_HDR_PRIO_MSK;
+
+ vtag = (prio << VLAN_PRIO_SHIFT) | vid;
+ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vtag);
+
+ /* strip vlan tag for VLAN-tagged packet */
+ if (pkt_type == CPSW_RX_VLAN_ENCAP_HDR_PKT_VLAN_TAG) {
+ memmove(skb->data + VLAN_HLEN, skb->data, 2 * ETH_ALEN);
+ skb_pull(skb, VLAN_HLEN);
+ }
+}
+
static void cpsw_rx_handler(void *token, int len, int status)
{
struct cpdma_chan *ch;
@@ -752,6 +812,8 @@ static void cpsw_rx_handler(void *token, int len, int status)
if (new_skb) {
skb_copy_queue_mapping(new_skb, skb);
skb_put(skb, len);
+ if (status & CPDMA_RX_VLAN_ENCAP)
+ cpsw_rx_vlan_encap(skb);
cpts_rx_timestamp(cpsw->cpts, skb);
skb->protocol = eth_type_trans(skb, ndev);
netif_receive_skb(skb);
@@ -1407,7 +1469,7 @@ static void cpsw_init_host_port(struct cpsw_priv *priv)
cpsw_ale_control_set(cpsw->ale, HOST_PORT_NUM, ALE_VLAN_AWARE,
CPSW_ALE_VLAN_AWARE);
control_reg = readl(&cpsw->regs->control);
- control_reg |= CPSW_VLAN_AWARE;
+ control_reg |= CPSW_VLAN_AWARE | CPSW_RX_VLAN_ENCAP;
writel(control_reg, &cpsw->regs->control);
fifo_mode = (cpsw->data.dual_emac) ? CPSW_FIFO_DUAL_MAC_MODE :
CPSW_FIFO_NORMAL_MODE;
@@ -3123,7 +3185,7 @@ static int cpsw_probe(struct platform_device *pdev)
cpsw->quirk_irq = true;
}
- ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
+ ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_CTAG_RX;
ndev->netdev_ops = &cpsw_netdev_ops;
ndev->ethtool_ops = &cpsw_ethtool_ops;
diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c
index 6f9173ff9414..31ae04117f0a 100644
--- a/drivers/net/ethernet/ti/davinci_cpdma.c
+++ b/drivers/net/ethernet/ti/davinci_cpdma.c
@@ -1164,7 +1164,7 @@ static int __cpdma_chan_process(struct cpdma_chan *chan)
outlen -= CPDMA_DESC_CRC_LEN;
status = status & (CPDMA_DESC_EOQ | CPDMA_DESC_TD_COMPLETE |
- CPDMA_DESC_PORT_MASK);
+ CPDMA_DESC_PORT_MASK | CPDMA_RX_VLAN_ENCAP);
chan->head = desc_from_phys(pool, desc_read(desc, hw_next));
chan_write(chan, cp, desc_dma);
diff --git a/drivers/net/ethernet/ti/davinci_cpdma.h b/drivers/net/ethernet/ti/davinci_cpdma.h
index fd65ce2b83de..d399af5389b8 100644
--- a/drivers/net/ethernet/ti/davinci_cpdma.h
+++ b/drivers/net/ethernet/ti/davinci_cpdma.h
@@ -19,6 +19,8 @@
#define CPDMA_RX_SOURCE_PORT(__status__) ((__status__ >> 16) & 0x7)
+#define CPDMA_RX_VLAN_ENCAP BIT(19)
+
#define CPDMA_EOI_RX_THRESH 0x0
#define CPDMA_EOI_RX 0x1
#define CPDMA_EOI_TX 0x2
diff --git a/drivers/net/ethernet/ti/tlan.c b/drivers/net/ethernet/ti/tlan.c
index 5a4e78fde530..c769cd9d11e7 100644
--- a/drivers/net/ethernet/ti/tlan.c
+++ b/drivers/net/ethernet/ti/tlan.c
@@ -1901,7 +1901,7 @@ ThunderLAN driver adapter related routines
* Nothing
* Parms:
* dev The device structure with the list
- * stuctures to be reset.
+ * structures to be reset.
*
* This routine sets the variables associated with managing
* the TLAN lists to their initial values.
diff --git a/drivers/net/ethernet/tile/Kconfig b/drivers/net/ethernet/tile/Kconfig
deleted file mode 100644
index bdfeaf3d4fce..000000000000
--- a/drivers/net/ethernet/tile/Kconfig
+++ /dev/null
@@ -1,18 +0,0 @@
-#
-# Tilera network device configuration
-#
-
-config TILE_NET
- tristate "Tilera GBE/XGBE network driver support"
- depends on TILE
- default y
- select CRC32
- select TILE_GXIO_MPIPE if TILEGX
- select HIGH_RES_TIMERS if TILEGX
- imply PTP_1588_CLOCK if TILEGX
- ---help---
- This is a standard Linux network device driver for the
- on-chip Tilera Gigabit Ethernet and XAUI interfaces.
-
- To compile this driver as a module, choose M here: the module
- will be called tile_net.
diff --git a/drivers/net/ethernet/tile/Makefile b/drivers/net/ethernet/tile/Makefile
deleted file mode 100644
index 3d0ae1f07fc9..000000000000
--- a/drivers/net/ethernet/tile/Makefile
+++ /dev/null
@@ -1,11 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-#
-# Makefile for the TILE on-chip networking support.
-#
-
-obj-$(CONFIG_TILE_NET) += tile_net.o
-ifdef CONFIG_TILEGX
-tile_net-y := tilegx.o
-else
-tile_net-y := tilepro.o
-endif
diff --git a/drivers/net/ethernet/tile/tilegx.c b/drivers/net/ethernet/tile/tilegx.c
deleted file mode 100644
index b3e5816a4678..000000000000
--- a/drivers/net/ethernet/tile/tilegx.c
+++ /dev/null
@@ -1,2279 +0,0 @@
-/*
- * Copyright 2012 Tilera Corporation. All Rights Reserved.
- *
- * 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 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, GOOD TITLE or
- * NON INFRINGEMENT. See the GNU General Public License for
- * more details.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/moduleparam.h>
-#include <linux/sched.h>
-#include <linux/kernel.h> /* printk() */
-#include <linux/slab.h> /* kmalloc() */
-#include <linux/errno.h> /* error codes */
-#include <linux/types.h> /* size_t */
-#include <linux/interrupt.h>
-#include <linux/in.h>
-#include <linux/irq.h>
-#include <linux/netdevice.h> /* struct device, and other headers */
-#include <linux/etherdevice.h> /* eth_type_trans */
-#include <linux/skbuff.h>
-#include <linux/ioctl.h>
-#include <linux/cdev.h>
-#include <linux/hugetlb.h>
-#include <linux/in6.h>
-#include <linux/timer.h>
-#include <linux/hrtimer.h>
-#include <linux/ktime.h>
-#include <linux/io.h>
-#include <linux/ctype.h>
-#include <linux/ip.h>
-#include <linux/ipv6.h>
-#include <linux/tcp.h>
-#include <linux/net_tstamp.h>
-#include <linux/ptp_clock_kernel.h>
-#include <linux/sched/isolation.h>
-
-#include <asm/checksum.h>
-#include <asm/homecache.h>
-#include <gxio/mpipe.h>
-#include <arch/sim.h>
-
-/* Default transmit lockup timeout period, in jiffies. */
-#define TILE_NET_TIMEOUT (5 * HZ)
-
-/* The maximum number of distinct channels (idesc.channel is 5 bits). */
-#define TILE_NET_CHANNELS 32
-
-/* Maximum number of idescs to handle per "poll". */
-#define TILE_NET_BATCH 128
-
-/* Maximum number of packets to handle per "poll". */
-#define TILE_NET_WEIGHT 64
-
-/* Maximum Jumbo Packet MTU */
-#define TILE_JUMBO_MAX_MTU 9000
-
-/* Number of entries in each iqueue. */
-#define IQUEUE_ENTRIES 512
-
-/* Number of entries in each equeue. */
-#define EQUEUE_ENTRIES 2048
-
-/* Total header bytes per equeue slot. Must be big enough for 2 bytes
- * of NET_IP_ALIGN alignment, plus 14 bytes (?) of L2 header, plus up to
- * 60 bytes of actual TCP header. We round up to align to cache lines.
- */
-#define HEADER_BYTES 128
-
-/* Maximum completions per cpu per device (must be a power of two).
- * ISSUE: What is the right number here? If this is too small, then
- * egress might block waiting for free space in a completions array.
- * ISSUE: At the least, allocate these only for initialized echannels.
- */
-#define TILE_NET_MAX_COMPS 64
-
-#define MAX_FRAGS (MAX_SKB_FRAGS + 1)
-
-/* The "kinds" of buffer stacks (small/large/jumbo). */
-#define MAX_KINDS 3
-
-/* Size of completions data to allocate.
- * ISSUE: Probably more than needed since we don't use all the channels.
- */
-#define COMPS_SIZE (TILE_NET_CHANNELS * sizeof(struct tile_net_comps))
-
-/* Size of NotifRing data to allocate. */
-#define NOTIF_RING_SIZE (IQUEUE_ENTRIES * sizeof(gxio_mpipe_idesc_t))
-
-/* Timeout to wake the per-device TX timer after we stop the queue.
- * We don't want the timeout too short (adds overhead, and might end
- * up causing stop/wake/stop/wake cycles) or too long (affects performance).
- * For the 10 Gb NIC, 30 usec means roughly 30+ 1500-byte packets.
- */
-#define TX_TIMER_DELAY_USEC 30
-
-/* Timeout to wake the per-cpu egress timer to free completions. */
-#define EGRESS_TIMER_DELAY_USEC 1000
-
-MODULE_AUTHOR("Tilera Corporation");
-MODULE_LICENSE("GPL");
-
-/* A "packet fragment" (a chunk of memory). */
-struct frag {
- void *buf;
- size_t length;
-};
-
-/* A single completion. */
-struct tile_net_comp {
- /* The "complete_count" when the completion will be complete. */
- s64 when;
- /* The buffer to be freed when the completion is complete. */
- struct sk_buff *skb;
-};
-
-/* The completions for a given cpu and echannel. */
-struct tile_net_comps {
- /* The completions. */
- struct tile_net_comp comp_queue[TILE_NET_MAX_COMPS];
- /* The number of completions used. */
- unsigned long comp_next;
- /* The number of completions freed. */
- unsigned long comp_last;
-};
-
-/* The transmit wake timer for a given cpu and echannel. */
-struct tile_net_tx_wake {
- int tx_queue_idx;
- struct hrtimer timer;
- struct net_device *dev;
-};
-
-/* Info for a specific cpu. */
-struct tile_net_info {
- /* Our cpu. */
- int my_cpu;
- /* A timer for handling egress completions. */
- struct hrtimer egress_timer;
- /* True if "egress_timer" is scheduled. */
- bool egress_timer_scheduled;
- struct info_mpipe {
- /* Packet queue. */
- gxio_mpipe_iqueue_t iqueue;
- /* The NAPI struct. */
- struct napi_struct napi;
- /* Number of buffers (by kind) which must still be provided. */
- unsigned int num_needed_buffers[MAX_KINDS];
- /* instance id. */
- int instance;
- /* True if iqueue is valid. */
- bool has_iqueue;
- /* NAPI flags. */
- bool napi_added;
- bool napi_enabled;
- /* Comps for each egress channel. */
- struct tile_net_comps *comps_for_echannel[TILE_NET_CHANNELS];
- /* Transmit wake timer for each egress channel. */
- struct tile_net_tx_wake tx_wake[TILE_NET_CHANNELS];
- } mpipe[NR_MPIPE_MAX];
-};
-
-/* Info for egress on a particular egress channel. */
-struct tile_net_egress {
- /* The "equeue". */
- gxio_mpipe_equeue_t *equeue;
- /* The headers for TSO. */
- unsigned char *headers;
-};
-
-/* Info for a specific device. */
-struct tile_net_priv {
- /* Our network device. */
- struct net_device *dev;
- /* The primary link. */
- gxio_mpipe_link_t link;
- /* The primary channel, if open, else -1. */
- int channel;
- /* The "loopify" egress link, if needed. */
- gxio_mpipe_link_t loopify_link;
- /* The "loopify" egress channel, if open, else -1. */
- int loopify_channel;
- /* The egress channel (channel or loopify_channel). */
- int echannel;
- /* mPIPE instance, 0 or 1. */
- int instance;
- /* The timestamp config. */
- struct hwtstamp_config stamp_cfg;
-};
-
-static struct mpipe_data {
- /* The ingress irq. */
- int ingress_irq;
-
- /* The "context" for all devices. */
- gxio_mpipe_context_t context;
-
- /* Egress info, indexed by "priv->echannel"
- * (lazily created as needed).
- */
- struct tile_net_egress
- egress_for_echannel[TILE_NET_CHANNELS];
-
- /* Devices currently associated with each channel.
- * NOTE: The array entry can become NULL after ifconfig down, but
- * we do not free the underlying net_device structures, so it is
- * safe to use a pointer after reading it from this array.
- */
- struct net_device
- *tile_net_devs_for_channel[TILE_NET_CHANNELS];
-
- /* The actual memory allocated for the buffer stacks. */
- void *buffer_stack_vas[MAX_KINDS];
-
- /* The amount of memory allocated for each buffer stack. */
- size_t buffer_stack_bytes[MAX_KINDS];
-
- /* The first buffer stack index
- * (small = +0, large = +1, jumbo = +2).
- */
- int first_buffer_stack;
-
- /* The buckets. */
- int first_bucket;
- int num_buckets;
-
- /* PTP-specific data. */
- struct ptp_clock *ptp_clock;
- struct ptp_clock_info caps;
-
- /* Lock for ptp accessors. */
- struct mutex ptp_lock;
-
-} mpipe_data[NR_MPIPE_MAX] = {
- [0 ... (NR_MPIPE_MAX - 1)] {
- .ingress_irq = -1,
- .first_buffer_stack = -1,
- .first_bucket = -1,
- .num_buckets = 1
- }
-};
-
-/* A mutex for "tile_net_devs_for_channel". */
-static DEFINE_MUTEX(tile_net_devs_for_channel_mutex);
-
-/* The per-cpu info. */
-static DEFINE_PER_CPU(struct tile_net_info, per_cpu_info);
-
-
-/* The buffer size enums for each buffer stack.
- * See arch/tile/include/gxio/mpipe.h for the set of possible values.
- * We avoid the "10384" size because it can induce "false chaining"
- * on "cut-through" jumbo packets.
- */
-static gxio_mpipe_buffer_size_enum_t buffer_size_enums[MAX_KINDS] = {
- GXIO_MPIPE_BUFFER_SIZE_128,
- GXIO_MPIPE_BUFFER_SIZE_1664,
- GXIO_MPIPE_BUFFER_SIZE_16384
-};
-
-/* Text value of tile_net.cpus if passed as a module parameter. */
-static char *network_cpus_string;
-
-/* The actual cpus in "network_cpus". */
-static struct cpumask network_cpus_map;
-
-/* If "tile_net.loopify=LINK" was specified, this is "LINK". */
-static char *loopify_link_name;
-
-/* If "tile_net.custom" was specified, this is true. */
-static bool custom_flag;
-
-/* If "tile_net.jumbo=NUM" was specified, this is "NUM". */
-static uint jumbo_num;
-
-/* Obtain mpipe instance from struct tile_net_priv given struct net_device. */
-static inline int mpipe_instance(struct net_device *dev)
-{
- struct tile_net_priv *priv = netdev_priv(dev);
- return priv->instance;
-}
-
-/* The "tile_net.cpus" argument specifies the cpus that are dedicated
- * to handle ingress packets.
- *
- * The parameter should be in the form "tile_net.cpus=m-n[,x-y]", where
- * m, n, x, y are integer numbers that represent the cpus that can be
- * neither a dedicated cpu nor a dataplane cpu.
- */
-static bool network_cpus_init(void)
-{
- int rc;
-
- if (network_cpus_string == NULL)
- return false;
-
- rc = cpulist_parse_crop(network_cpus_string, &network_cpus_map);
- if (rc != 0) {
- pr_warn("tile_net.cpus=%s: malformed cpu list\n",
- network_cpus_string);
- return false;
- }
-
- /* Remove dedicated cpus. */
- cpumask_and(&network_cpus_map, &network_cpus_map, cpu_possible_mask);
-
- if (cpumask_empty(&network_cpus_map)) {
- pr_warn("Ignoring empty tile_net.cpus='%s'.\n",
- network_cpus_string);
- return false;
- }
-
- pr_info("Linux network CPUs: %*pbl\n",
- cpumask_pr_args(&network_cpus_map));
- return true;
-}
-
-module_param_named(cpus, network_cpus_string, charp, 0444);
-MODULE_PARM_DESC(cpus, "cpulist of cores that handle network interrupts");
-
-/* The "tile_net.loopify=LINK" argument causes the named device to
- * actually use "loop0" for ingress, and "loop1" for egress. This
- * allows an app to sit between the actual link and linux, passing
- * (some) packets along to linux, and forwarding (some) packets sent
- * out by linux.
- */
-module_param_named(loopify, loopify_link_name, charp, 0444);
-MODULE_PARM_DESC(loopify, "name the device to use loop0/1 for ingress/egress");
-
-/* The "tile_net.custom" argument causes us to ignore the "conventional"
- * classifier metadata, in particular, the "l2_offset".
- */
-module_param_named(custom, custom_flag, bool, 0444);
-MODULE_PARM_DESC(custom, "indicates a (heavily) customized classifier");
-
-/* The "tile_net.jumbo" argument causes us to support "jumbo" packets,
- * and to allocate the given number of "jumbo" buffers.
- */
-module_param_named(jumbo, jumbo_num, uint, 0444);
-MODULE_PARM_DESC(jumbo, "the number of buffers to support jumbo packets");
-
-/* Atomically update a statistics field.
- * Note that on TILE-Gx, this operation is fire-and-forget on the
- * issuing core (single-cycle dispatch) and takes only a few cycles
- * longer than a regular store when the request reaches the home cache.
- * No expensive bus management overhead is required.
- */
-static void tile_net_stats_add(unsigned long value, unsigned long *field)
-{
- BUILD_BUG_ON(sizeof(atomic_long_t) != sizeof(unsigned long));
- atomic_long_add(value, (atomic_long_t *)field);
-}
-
-/* Allocate and push a buffer. */
-static bool tile_net_provide_buffer(int instance, int kind)
-{
- struct mpipe_data *md = &mpipe_data[instance];
- gxio_mpipe_buffer_size_enum_t bse = buffer_size_enums[kind];
- size_t bs = gxio_mpipe_buffer_size_enum_to_buffer_size(bse);
- const unsigned long buffer_alignment = 128;
- struct sk_buff *skb;
- int len;
-
- len = sizeof(struct sk_buff **) + buffer_alignment + bs;
- skb = dev_alloc_skb(len);
- if (skb == NULL)
- return false;
-
- /* Make room for a back-pointer to 'skb' and guarantee alignment. */
- skb_reserve(skb, sizeof(struct sk_buff **));
- skb_reserve(skb, -(long)skb->data & (buffer_alignment - 1));
-
- /* Save a back-pointer to 'skb'. */
- *(struct sk_buff **)(skb->data - sizeof(struct sk_buff **)) = skb;
-
- /* Make sure "skb" and the back-pointer have been flushed. */
- wmb();
-
- gxio_mpipe_push_buffer(&md->context, md->first_buffer_stack + kind,
- (void *)va_to_tile_io_addr(skb->data));
-
- return true;
-}
-
-/* Convert a raw mpipe buffer to its matching skb pointer. */
-static struct sk_buff *mpipe_buf_to_skb(void *va)
-{
- /* Acquire the associated "skb". */
- struct sk_buff **skb_ptr = va - sizeof(*skb_ptr);
- struct sk_buff *skb = *skb_ptr;
-
- /* Paranoia. */
- if (skb->data != va) {
- /* Panic here since there's a reasonable chance
- * that corrupt buffers means generic memory
- * corruption, with unpredictable system effects.
- */
- panic("Corrupt linux buffer! va=%p, skb=%p, skb->data=%p",
- va, skb, skb->data);
- }
-
- return skb;
-}
-
-static void tile_net_pop_all_buffers(int instance, int stack)
-{
- struct mpipe_data *md = &mpipe_data[instance];
-
- for (;;) {
- tile_io_addr_t addr =
- (tile_io_addr_t)gxio_mpipe_pop_buffer(&md->context,
- stack);
- if (addr == 0)
- break;
- dev_kfree_skb_irq(mpipe_buf_to_skb(tile_io_addr_to_va(addr)));
- }
-}
-
-/* Provide linux buffers to mPIPE. */
-static void tile_net_provide_needed_buffers(void)
-{
- struct tile_net_info *info = this_cpu_ptr(&per_cpu_info);
- int instance, kind;
- for (instance = 0; instance < NR_MPIPE_MAX &&
- info->mpipe[instance].has_iqueue; instance++) {
- for (kind = 0; kind < MAX_KINDS; kind++) {
- while (info->mpipe[instance].num_needed_buffers[kind]
- != 0) {
- if (!tile_net_provide_buffer(instance, kind)) {
- pr_notice("Tile %d still needs"
- " some buffers\n",
- info->my_cpu);
- return;
- }
- info->mpipe[instance].
- num_needed_buffers[kind]--;
- }
- }
- }
-}
-
-/* Get RX timestamp, and store it in the skb. */
-static void tile_rx_timestamp(struct tile_net_priv *priv, struct sk_buff *skb,
- gxio_mpipe_idesc_t *idesc)
-{
- if (unlikely(priv->stamp_cfg.rx_filter != HWTSTAMP_FILTER_NONE)) {
- struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb);
- memset(shhwtstamps, 0, sizeof(*shhwtstamps));
- shhwtstamps->hwtstamp = ktime_set(idesc->time_stamp_sec,
- idesc->time_stamp_ns);
- }
-}
-
-/* Get TX timestamp, and store it in the skb. */
-static void tile_tx_timestamp(struct sk_buff *skb, int instance)
-{
- struct skb_shared_info *shtx = skb_shinfo(skb);
- if (unlikely((shtx->tx_flags & SKBTX_HW_TSTAMP) != 0)) {
- struct mpipe_data *md = &mpipe_data[instance];
- struct skb_shared_hwtstamps shhwtstamps;
- struct timespec64 ts;
-
- shtx->tx_flags |= SKBTX_IN_PROGRESS;
- gxio_mpipe_get_timestamp(&md->context, &ts);
- memset(&shhwtstamps, 0, sizeof(shhwtstamps));
- shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec);
- skb_tstamp_tx(skb, &shhwtstamps);
- }
-}
-
-/* Use ioctl() to enable or disable TX or RX timestamping. */
-static int tile_hwtstamp_set(struct net_device *dev, struct ifreq *rq)
-{
- struct hwtstamp_config config;
- struct tile_net_priv *priv = netdev_priv(dev);
-
- if (copy_from_user(&config, rq->ifr_data, sizeof(config)))
- return -EFAULT;
-
- if (config.flags) /* reserved for future extensions */
- return -EINVAL;
-
- switch (config.tx_type) {
- case HWTSTAMP_TX_OFF:
- case HWTSTAMP_TX_ON:
- break;
- default:
- return -ERANGE;
- }
-
- switch (config.rx_filter) {
- case HWTSTAMP_FILTER_NONE:
- break;
- case HWTSTAMP_FILTER_ALL:
- case HWTSTAMP_FILTER_SOME:
- case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
- case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
- case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
- case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
- case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
- case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
- case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
- case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
- case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
- case HWTSTAMP_FILTER_PTP_V2_EVENT:
- case HWTSTAMP_FILTER_PTP_V2_SYNC:
- case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
- case HWTSTAMP_FILTER_NTP_ALL:
- config.rx_filter = HWTSTAMP_FILTER_ALL;
- break;
- default:
- return -ERANGE;
- }
-
- if (copy_to_user(rq->ifr_data, &config, sizeof(config)))
- return -EFAULT;
-
- priv->stamp_cfg = config;
- return 0;
-}
-
-static int tile_hwtstamp_get(struct net_device *dev, struct ifreq *rq)
-{
- struct tile_net_priv *priv = netdev_priv(dev);
-
- if (copy_to_user(rq->ifr_data, &priv->stamp_cfg,
- sizeof(priv->stamp_cfg)))
- return -EFAULT;
-
- return 0;
-}
-
-static inline bool filter_packet(struct net_device *dev, void *buf)
-{
- /* Filter packets received before we're up. */
- if (dev == NULL || !(dev->flags & IFF_UP))
- return true;
-
- /* Filter out packets that aren't for us. */
- if (!(dev->flags & IFF_PROMISC) &&
- !is_multicast_ether_addr(buf) &&
- !ether_addr_equal(dev->dev_addr, buf))
- return true;
-
- return false;
-}
-
-static void tile_net_receive_skb(struct net_device *dev, struct sk_buff *skb,
- gxio_mpipe_idesc_t *idesc, unsigned long len)
-{
- struct tile_net_info *info = this_cpu_ptr(&per_cpu_info);
- struct tile_net_priv *priv = netdev_priv(dev);
- int instance = priv->instance;
-
- /* Encode the actual packet length. */
- skb_put(skb, len);
-
- skb->protocol = eth_type_trans(skb, dev);
-
- /* Acknowledge "good" hardware checksums. */
- if (idesc->cs && idesc->csum_seed_val == 0xFFFF)
- skb->ip_summed = CHECKSUM_UNNECESSARY;
-
- /* Get RX timestamp from idesc. */
- tile_rx_timestamp(priv, skb, idesc);
-
- napi_gro_receive(&info->mpipe[instance].napi, skb);
-
- /* Update stats. */
- tile_net_stats_add(1, &dev->stats.rx_packets);
- tile_net_stats_add(len, &dev->stats.rx_bytes);
-
- /* Need a new buffer. */
- if (idesc->size == buffer_size_enums[0])
- info->mpipe[instance].num_needed_buffers[0]++;
- else if (idesc->size == buffer_size_enums[1])
- info->mpipe[instance].num_needed_buffers[1]++;
- else
- info->mpipe[instance].num_needed_buffers[2]++;
-}
-
-/* Handle a packet. Return true if "processed", false if "filtered". */
-static bool tile_net_handle_packet(int instance, gxio_mpipe_idesc_t *idesc)
-{
- struct tile_net_info *info = this_cpu_ptr(&per_cpu_info);
- struct mpipe_data *md = &mpipe_data[instance];
- struct net_device *dev = md->tile_net_devs_for_channel[idesc->channel];
- uint8_t l2_offset;
- void *va;
- void *buf;
- unsigned long len;
- bool filter;
-
- /* Drop packets for which no buffer was available (which can
- * happen under heavy load), or for which the me/tr/ce flags
- * are set (which can happen for jumbo cut-through packets,
- * or with a customized classifier).
- */
- if (idesc->be || idesc->me || idesc->tr || idesc->ce) {
- if (dev)
- tile_net_stats_add(1, &dev->stats.rx_errors);
- goto drop;
- }
-
- /* Get the "l2_offset", if allowed. */
- l2_offset = custom_flag ? 0 : gxio_mpipe_idesc_get_l2_offset(idesc);
-
- /* Get the VA (including NET_IP_ALIGN bytes of "headroom"). */
- va = tile_io_addr_to_va((unsigned long)idesc->va);
-
- /* Get the actual packet start/length. */
- buf = va + l2_offset;
- len = idesc->l2_size - l2_offset;
-
- /* Point "va" at the raw buffer. */
- va -= NET_IP_ALIGN;
-
- filter = filter_packet(dev, buf);
- if (filter) {
- if (dev)
- tile_net_stats_add(1, &dev->stats.rx_dropped);
-drop:
- gxio_mpipe_iqueue_drop(&info->mpipe[instance].iqueue, idesc);
- } else {
- struct sk_buff *skb = mpipe_buf_to_skb(va);
-
- /* Skip headroom, and any custom header. */
- skb_reserve(skb, NET_IP_ALIGN + l2_offset);
-
- tile_net_receive_skb(dev, skb, idesc, len);
- }
-
- gxio_mpipe_iqueue_consume(&info->mpipe[instance].iqueue, idesc);
- return !filter;
-}
-
-/* Handle some packets for the current CPU.
- *
- * This function handles up to TILE_NET_BATCH idescs per call.
- *
- * ISSUE: Since we do not provide new buffers until this function is
- * complete, we must initially provide enough buffers for each network
- * cpu to fill its iqueue and also its batched idescs.
- *
- * ISSUE: The "rotting packet" race condition occurs if a packet
- * arrives after the queue appears to be empty, and before the
- * hypervisor interrupt is re-enabled.
- */
-static int tile_net_poll(struct napi_struct *napi, int budget)
-{
- struct tile_net_info *info = this_cpu_ptr(&per_cpu_info);
- unsigned int work = 0;
- gxio_mpipe_idesc_t *idesc;
- int instance, i, n;
- struct mpipe_data *md;
- struct info_mpipe *info_mpipe =
- container_of(napi, struct info_mpipe, napi);
-
- if (budget <= 0)
- goto done;
-
- instance = info_mpipe->instance;
- while ((n = gxio_mpipe_iqueue_try_peek(
- &info_mpipe->iqueue,
- &idesc)) > 0) {
- for (i = 0; i < n; i++) {
- if (i == TILE_NET_BATCH)
- goto done;
- if (tile_net_handle_packet(instance,
- idesc + i)) {
- if (++work >= budget)
- goto done;
- }
- }
- }
-
- /* There are no packets left. */
- napi_complete_done(&info_mpipe->napi, work);
-
- md = &mpipe_data[instance];
- /* Re-enable hypervisor interrupts. */
- gxio_mpipe_enable_notif_ring_interrupt(
- &md->context, info->mpipe[instance].iqueue.ring);
-
- /* HACK: Avoid the "rotting packet" problem. */
- if (gxio_mpipe_iqueue_try_peek(&info_mpipe->iqueue, &idesc) > 0)
- napi_schedule(&info_mpipe->napi);
-
- /* ISSUE: Handle completions? */
-
-done:
- tile_net_provide_needed_buffers();
-
- return work;
-}
-
-/* Handle an ingress interrupt from an instance on the current cpu. */
-static irqreturn_t tile_net_handle_ingress_irq(int irq, void *id)
-{
- struct tile_net_info *info = this_cpu_ptr(&per_cpu_info);
- napi_schedule(&info->mpipe[(uint64_t)id].napi);
- return IRQ_HANDLED;
-}
-
-/* Free some completions. This must be called with interrupts blocked. */
-static int tile_net_free_comps(gxio_mpipe_equeue_t *equeue,
- struct tile_net_comps *comps,
- int limit, bool force_update)
-{
- int n = 0;
- while (comps->comp_last < comps->comp_next) {
- unsigned int cid = comps->comp_last % TILE_NET_MAX_COMPS;
- struct tile_net_comp *comp = &comps->comp_queue[cid];
- if (!gxio_mpipe_equeue_is_complete(equeue, comp->when,
- force_update || n == 0))
- break;
- dev_kfree_skb_irq(comp->skb);
- comps->comp_last++;
- if (++n == limit)
- break;
- }
- return n;
-}
-
-/* Add a completion. This must be called with interrupts blocked.
- * tile_net_equeue_try_reserve() will have ensured a free completion entry.
- */
-static void add_comp(gxio_mpipe_equeue_t *equeue,
- struct tile_net_comps *comps,
- uint64_t when, struct sk_buff *skb)
-{
- int cid = comps->comp_next % TILE_NET_MAX_COMPS;
- comps->comp_queue[cid].when = when;
- comps->comp_queue[cid].skb = skb;
- comps->comp_next++;
-}
-
-static void tile_net_schedule_tx_wake_timer(struct net_device *dev,
- int tx_queue_idx)
-{
- struct tile_net_info *info = &per_cpu(per_cpu_info, tx_queue_idx);
- struct tile_net_priv *priv = netdev_priv(dev);
- int instance = priv->instance;
- struct tile_net_tx_wake *tx_wake =
- &info->mpipe[instance].tx_wake[priv->echannel];
-
- hrtimer_start(&tx_wake->timer,
- TX_TIMER_DELAY_USEC * 1000UL,
- HRTIMER_MODE_REL_PINNED);
-}
-
-static enum hrtimer_restart tile_net_handle_tx_wake_timer(struct hrtimer *t)
-{
- struct tile_net_tx_wake *tx_wake =
- container_of(t, struct tile_net_tx_wake, timer);
- netif_wake_subqueue(tx_wake->dev, tx_wake->tx_queue_idx);
- return HRTIMER_NORESTART;
-}
-
-/* Make sure the egress timer is scheduled. */
-static void tile_net_schedule_egress_timer(void)
-{
- struct tile_net_info *info = this_cpu_ptr(&per_cpu_info);
-
- if (!info->egress_timer_scheduled) {
- hrtimer_start(&info->egress_timer,
- EGRESS_TIMER_DELAY_USEC * 1000UL,
- HRTIMER_MODE_REL_PINNED);
- info->egress_timer_scheduled = true;
- }
-}
-
-/* The "function" for "info->egress_timer".
- *
- * This timer will reschedule itself as long as there are any pending
- * completions expected for this tile.
- */
-static enum hrtimer_restart tile_net_handle_egress_timer(struct hrtimer *t)
-{
- struct tile_net_info *info = this_cpu_ptr(&per_cpu_info);
- unsigned long irqflags;
- bool pending = false;
- int i, instance;
-
- local_irq_save(irqflags);
-
- /* The timer is no longer scheduled. */
- info->egress_timer_scheduled = false;
-
- /* Free all possible comps for this tile. */
- for (instance = 0; instance < NR_MPIPE_MAX &&
- info->mpipe[instance].has_iqueue; instance++) {
- for (i = 0; i < TILE_NET_CHANNELS; i++) {
- struct tile_net_egress *egress =
- &mpipe_data[instance].egress_for_echannel[i];
- struct tile_net_comps *comps =
- info->mpipe[instance].comps_for_echannel[i];
- if (!egress || comps->comp_last >= comps->comp_next)
- continue;
- tile_net_free_comps(egress->equeue, comps, -1, true);
- pending = pending ||
- (comps->comp_last < comps->comp_next);
- }
- }
-
- /* Reschedule timer if needed. */
- if (pending)
- tile_net_schedule_egress_timer();
-
- local_irq_restore(irqflags);
-
- return HRTIMER_NORESTART;
-}
-
-/* PTP clock operations. */
-
-static int ptp_mpipe_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
-{
- int ret = 0;
- struct mpipe_data *md = container_of(ptp, struct mpipe_data, caps);
- mutex_lock(&md->ptp_lock);
- if (gxio_mpipe_adjust_timestamp_freq(&md->context, ppb))
- ret = -EINVAL;
- mutex_unlock(&md->ptp_lock);
- return ret;
-}
-
-static int ptp_mpipe_adjtime(struct ptp_clock_info *ptp, s64 delta)
-{
- int ret = 0;
- struct mpipe_data *md = container_of(ptp, struct mpipe_data, caps);
- mutex_lock(&md->ptp_lock);
- if (gxio_mpipe_adjust_timestamp(&md->context, delta))
- ret = -EBUSY;
- mutex_unlock(&md->ptp_lock);
- return ret;
-}
-
-static int ptp_mpipe_gettime(struct ptp_clock_info *ptp,
- struct timespec64 *ts)
-{
- int ret = 0;
- struct mpipe_data *md = container_of(ptp, struct mpipe_data, caps);
- mutex_lock(&md->ptp_lock);
- if (gxio_mpipe_get_timestamp(&md->context, ts))
- ret = -EBUSY;
- mutex_unlock(&md->ptp_lock);
- return ret;
-}
-
-static int ptp_mpipe_settime(struct ptp_clock_info *ptp,
- const struct timespec64 *ts)
-{
- int ret = 0;
- struct mpipe_data *md = container_of(ptp, struct mpipe_data, caps);
- mutex_lock(&md->ptp_lock);
- if (gxio_mpipe_set_timestamp(&md->context, ts))
- ret = -EBUSY;
- mutex_unlock(&md->ptp_lock);
- return ret;
-}
-
-static int ptp_mpipe_enable(struct ptp_clock_info *ptp,
- struct ptp_clock_request *request, int on)
-{
- return -EOPNOTSUPP;
-}
-
-static const struct ptp_clock_info ptp_mpipe_caps = {
- .owner = THIS_MODULE,
- .name = "mPIPE clock",
- .max_adj = 999999999,
- .n_ext_ts = 0,
- .n_pins = 0,
- .pps = 0,
- .adjfreq = ptp_mpipe_adjfreq,
- .adjtime = ptp_mpipe_adjtime,
- .gettime64 = ptp_mpipe_gettime,
- .settime64 = ptp_mpipe_settime,
- .enable = ptp_mpipe_enable,
-};
-
-/* Sync mPIPE's timestamp up with Linux system time and register PTP clock. */
-static void register_ptp_clock(struct net_device *dev, struct mpipe_data *md)
-{
- struct timespec64 ts;
-
- ktime_get_ts64(&ts);
- gxio_mpipe_set_timestamp(&md->context, &ts);
-
- mutex_init(&md->ptp_lock);
- md->caps = ptp_mpipe_caps;
- md->ptp_clock = ptp_clock_register(&md->caps, NULL);
- if (IS_ERR(md->ptp_clock))
- netdev_err(dev, "ptp_clock_register failed %ld\n",
- PTR_ERR(md->ptp_clock));
-}
-
-/* Initialize PTP fields in a new device. */
-static void init_ptp_dev(struct tile_net_priv *priv)
-{
- priv->stamp_cfg.rx_filter = HWTSTAMP_FILTER_NONE;
- priv->stamp_cfg.tx_type = HWTSTAMP_TX_OFF;
-}
-
-/* Helper functions for "tile_net_update()". */
-static void enable_ingress_irq(void *irq)
-{
- enable_percpu_irq((long)irq, 0);
-}
-
-static void disable_ingress_irq(void *irq)
-{
- disable_percpu_irq((long)irq);
-}
-
-/* Helper function for tile_net_open() and tile_net_stop().
- * Always called under tile_net_devs_for_channel_mutex.
- */
-static int tile_net_update(struct net_device *dev)
-{
- static gxio_mpipe_rules_t rules; /* too big to fit on the stack */
- bool saw_channel = false;
- int instance = mpipe_instance(dev);
- struct mpipe_data *md = &mpipe_data[instance];
- int channel;
- int rc;
- int cpu;
-
- saw_channel = false;
- gxio_mpipe_rules_init(&rules, &md->context);
-
- for (channel = 0; channel < TILE_NET_CHANNELS; channel++) {
- if (md->tile_net_devs_for_channel[channel] == NULL)
- continue;
- if (!saw_channel) {
- saw_channel = true;
- gxio_mpipe_rules_begin(&rules, md->first_bucket,
- md->num_buckets, NULL);
- gxio_mpipe_rules_set_headroom(&rules, NET_IP_ALIGN);
- }
- gxio_mpipe_rules_add_channel(&rules, channel);
- }
-
- /* NOTE: This can fail if there is no classifier.
- * ISSUE: Can anything else cause it to fail?
- */
- rc = gxio_mpipe_rules_commit(&rules);
- if (rc != 0) {
- netdev_warn(dev, "gxio_mpipe_rules_commit: mpipe[%d] %d\n",
- instance, rc);
- return -EIO;
- }
-
- /* Update all cpus, sequentially (to protect "netif_napi_add()").
- * We use on_each_cpu to handle the IPI mask or unmask.
- */
- if (!saw_channel)
- on_each_cpu(disable_ingress_irq,
- (void *)(long)(md->ingress_irq), 1);
- for_each_online_cpu(cpu) {
- struct tile_net_info *info = &per_cpu(per_cpu_info, cpu);
-
- if (!info->mpipe[instance].has_iqueue)
- continue;
- if (saw_channel) {
- if (!info->mpipe[instance].napi_added) {
- netif_napi_add(dev, &info->mpipe[instance].napi,
- tile_net_poll, TILE_NET_WEIGHT);
- info->mpipe[instance].napi_added = true;
- }
- if (!info->mpipe[instance].napi_enabled) {
- napi_enable(&info->mpipe[instance].napi);
- info->mpipe[instance].napi_enabled = true;
- }
- } else {
- if (info->mpipe[instance].napi_enabled) {
- napi_disable(&info->mpipe[instance].napi);
- info->mpipe[instance].napi_enabled = false;
- }
- /* FIXME: Drain the iqueue. */
- }
- }
- if (saw_channel)
- on_each_cpu(enable_ingress_irq,
- (void *)(long)(md->ingress_irq), 1);
-
- /* HACK: Allow packets to flow in the simulator. */
- if (saw_channel)
- sim_enable_mpipe_links(instance, -1);
-
- return 0;
-}
-
-/* Initialize a buffer stack. */
-static int create_buffer_stack(struct net_device *dev,
- int kind, size_t num_buffers)
-{
- pte_t hash_pte = pte_set_home((pte_t) { 0 }, PAGE_HOME_HASH);
- int instance = mpipe_instance(dev);
- struct mpipe_data *md = &mpipe_data[instance];
- size_t needed = gxio_mpipe_calc_buffer_stack_bytes(num_buffers);
- int stack_idx = md->first_buffer_stack + kind;
- void *va;
- int i, rc;
-
- /* Round up to 64KB and then use alloc_pages() so we get the
- * required 64KB alignment.
- */
- md->buffer_stack_bytes[kind] =
- ALIGN(needed, 64 * 1024);
-
- va = alloc_pages_exact(md->buffer_stack_bytes[kind], GFP_KERNEL);
- if (va == NULL) {
- netdev_err(dev,
- "Could not alloc %zd bytes for buffer stack %d\n",
- md->buffer_stack_bytes[kind], kind);
- return -ENOMEM;
- }
-
- /* Initialize the buffer stack. */
- rc = gxio_mpipe_init_buffer_stack(&md->context, stack_idx,
- buffer_size_enums[kind], va,
- md->buffer_stack_bytes[kind], 0);
- if (rc != 0) {
- netdev_err(dev, "gxio_mpipe_init_buffer_stack: mpipe[%d] %d\n",
- instance, rc);
- free_pages_exact(va, md->buffer_stack_bytes[kind]);
- return rc;
- }
-
- md->buffer_stack_vas[kind] = va;
-
- rc = gxio_mpipe_register_client_memory(&md->context, stack_idx,
- hash_pte, 0);
- if (rc != 0) {
- netdev_err(dev,
- "gxio_mpipe_register_client_memory: mpipe[%d] %d\n",
- instance, rc);
- return rc;
- }
-
- /* Provide initial buffers. */
- for (i = 0; i < num_buffers; i++) {
- if (!tile_net_provide_buffer(instance, kind)) {
- netdev_err(dev, "Cannot allocate initial sk_bufs!\n");
- return -ENOMEM;
- }
- }
-
- return 0;
-}
-
-/* Allocate and initialize mpipe buffer stacks, and register them in
- * the mPIPE TLBs, for small, large, and (possibly) jumbo packet sizes.
- * This routine supports tile_net_init_mpipe(), below.
- */
-static int init_buffer_stacks(struct net_device *dev,
- int network_cpus_count)
-{
- int num_kinds = MAX_KINDS - (jumbo_num == 0);
- size_t num_buffers;
- int rc;
- int instance = mpipe_instance(dev);
- struct mpipe_data *md = &mpipe_data[instance];
-
- /* Allocate the buffer stacks. */
- rc = gxio_mpipe_alloc_buffer_stacks(&md->context, num_kinds, 0, 0);
- if (rc < 0) {
- netdev_err(dev,
- "gxio_mpipe_alloc_buffer_stacks: mpipe[%d] %d\n",
- instance, rc);
- return rc;
- }
- md->first_buffer_stack = rc;
-
- /* Enough small/large buffers to (normally) avoid buffer errors. */
- num_buffers =
- network_cpus_count * (IQUEUE_ENTRIES + TILE_NET_BATCH);
-
- /* Allocate the small memory stack. */
- if (rc >= 0)
- rc = create_buffer_stack(dev, 0, num_buffers);
-
- /* Allocate the large buffer stack. */
- if (rc >= 0)
- rc = create_buffer_stack(dev, 1, num_buffers);
-
- /* Allocate the jumbo buffer stack if needed. */
- if (rc >= 0 && jumbo_num != 0)
- rc = create_buffer_stack(dev, 2, jumbo_num);
-
- return rc;
-}
-
-/* Allocate per-cpu resources (memory for completions and idescs).
- * This routine supports tile_net_init_mpipe(), below.
- */
-static int alloc_percpu_mpipe_resources(struct net_device *dev,
- int cpu, int ring)
-{
- struct tile_net_info *info = &per_cpu(per_cpu_info, cpu);
- int order, i, rc;
- int instance = mpipe_instance(dev);
- struct mpipe_data *md = &mpipe_data[instance];
- struct page *page;
- void *addr;
-
- /* Allocate the "comps". */
- order = get_order(COMPS_SIZE);
- page = homecache_alloc_pages(GFP_KERNEL, order, cpu);
- if (page == NULL) {
- netdev_err(dev, "Failed to alloc %zd bytes comps memory\n",
- COMPS_SIZE);
- return -ENOMEM;
- }
- addr = pfn_to_kaddr(page_to_pfn(page));
- memset(addr, 0, COMPS_SIZE);
- for (i = 0; i < TILE_NET_CHANNELS; i++)
- info->mpipe[instance].comps_for_echannel[i] =
- addr + i * sizeof(struct tile_net_comps);
-
- /* If this is a network cpu, create an iqueue. */
- if (cpumask_test_cpu(cpu, &network_cpus_map)) {
- order = get_order(NOTIF_RING_SIZE);
- page = homecache_alloc_pages(GFP_KERNEL, order, cpu);
- if (page == NULL) {
- netdev_err(dev,
- "Failed to alloc %zd bytes iqueue memory\n",
- NOTIF_RING_SIZE);
- return -ENOMEM;
- }
- addr = pfn_to_kaddr(page_to_pfn(page));
- rc = gxio_mpipe_iqueue_init(&info->mpipe[instance].iqueue,
- &md->context, ring++, addr,
- NOTIF_RING_SIZE, 0);
- if (rc < 0) {
- netdev_err(dev,
- "gxio_mpipe_iqueue_init failed: %d\n", rc);
- return rc;
- }
- info->mpipe[instance].has_iqueue = true;
- }
-
- return ring;
-}
-
-/* Initialize NotifGroup and buckets.
- * This routine supports tile_net_init_mpipe(), below.
- */
-static int init_notif_group_and_buckets(struct net_device *dev,
- int ring, int network_cpus_count)
-{
- int group, rc;
- int instance = mpipe_instance(dev);
- struct mpipe_data *md = &mpipe_data[instance];
-
- /* Allocate one NotifGroup. */
- rc = gxio_mpipe_alloc_notif_groups(&md->context, 1, 0, 0);
- if (rc < 0) {
- netdev_err(dev, "gxio_mpipe_alloc_notif_groups: mpipe[%d] %d\n",
- instance, rc);
- return rc;
- }
- group = rc;
-
- /* Initialize global num_buckets value. */
- if (network_cpus_count > 4)
- md->num_buckets = 256;
- else if (network_cpus_count > 1)
- md->num_buckets = 16;
-
- /* Allocate some buckets, and set global first_bucket value. */
- rc = gxio_mpipe_alloc_buckets(&md->context, md->num_buckets, 0, 0);
- if (rc < 0) {
- netdev_err(dev, "gxio_mpipe_alloc_buckets: mpipe[%d] %d\n",
- instance, rc);
- return rc;
- }
- md->first_bucket = rc;
-
- /* Init group and buckets. */
- rc = gxio_mpipe_init_notif_group_and_buckets(
- &md->context, group, ring, network_cpus_count,
- md->first_bucket, md->num_buckets,
- GXIO_MPIPE_BUCKET_STICKY_FLOW_LOCALITY);
- if (rc != 0) {
- netdev_err(dev, "gxio_mpipe_init_notif_group_and_buckets: "
- "mpipe[%d] %d\n", instance, rc);
- return rc;
- }
-
- return 0;
-}
-
-/* Create an irq and register it, then activate the irq and request
- * interrupts on all cores. Note that "ingress_irq" being initialized
- * is how we know not to call tile_net_init_mpipe() again.
- * This routine supports tile_net_init_mpipe(), below.
- */
-static int tile_net_setup_interrupts(struct net_device *dev)
-{
- int cpu, rc, irq;
- int instance = mpipe_instance(dev);
- struct mpipe_data *md = &mpipe_data[instance];
-
- irq = md->ingress_irq;
- if (irq < 0) {
- irq = irq_alloc_hwirq(-1);
- if (!irq) {
- netdev_err(dev,
- "create_irq failed: mpipe[%d] %d\n",
- instance, irq);
- return irq;
- }
- tile_irq_activate(irq, TILE_IRQ_PERCPU);
-
- rc = request_irq(irq, tile_net_handle_ingress_irq,
- 0, "tile_net", (void *)((uint64_t)instance));
-
- if (rc != 0) {
- netdev_err(dev, "request_irq failed: mpipe[%d] %d\n",
- instance, rc);
- irq_free_hwirq(irq);
- return rc;
- }
- md->ingress_irq = irq;
- }
-
- for_each_online_cpu(cpu) {
- struct tile_net_info *info = &per_cpu(per_cpu_info, cpu);
- if (info->mpipe[instance].has_iqueue) {
- gxio_mpipe_request_notif_ring_interrupt(&md->context,
- cpu_x(cpu), cpu_y(cpu), KERNEL_PL, irq,
- info->mpipe[instance].iqueue.ring);
- }
- }
-
- return 0;
-}
-
-/* Undo any state set up partially by a failed call to tile_net_init_mpipe. */
-static void tile_net_init_mpipe_fail(int instance)
-{
- int kind, cpu;
- struct mpipe_data *md = &mpipe_data[instance];
-
- /* Do cleanups that require the mpipe context first. */
- for (kind = 0; kind < MAX_KINDS; kind++) {
- if (md->buffer_stack_vas[kind] != NULL) {
- tile_net_pop_all_buffers(instance,
- md->first_buffer_stack +
- kind);
- }
- }
-
- /* Destroy mpipe context so the hardware no longer owns any memory. */
- gxio_mpipe_destroy(&md->context);
-
- for_each_online_cpu(cpu) {
- struct tile_net_info *info = &per_cpu(per_cpu_info, cpu);
- free_pages(
- (unsigned long)(
- info->mpipe[instance].comps_for_echannel[0]),
- get_order(COMPS_SIZE));
- info->mpipe[instance].comps_for_echannel[0] = NULL;
- free_pages((unsigned long)(info->mpipe[instance].iqueue.idescs),
- get_order(NOTIF_RING_SIZE));
- info->mpipe[instance].iqueue.idescs = NULL;
- }
-
- for (kind = 0; kind < MAX_KINDS; kind++) {
- if (md->buffer_stack_vas[kind] != NULL) {
- free_pages_exact(md->buffer_stack_vas[kind],
- md->buffer_stack_bytes[kind]);
- md->buffer_stack_vas[kind] = NULL;
- }
- }
-
- md->first_buffer_stack = -1;
- md->first_bucket = -1;
-}
-
-/* The first time any tilegx network device is opened, we initialize
- * the global mpipe state. If this step fails, we fail to open the
- * device, but if it succeeds, we never need to do it again, and since
- * tile_net can't be unloaded, we never undo it.
- *
- * Note that some resources in this path (buffer stack indices,
- * bindings from init_buffer_stack, etc.) are hypervisor resources
- * that are freed implicitly by gxio_mpipe_destroy().
- */
-static int tile_net_init_mpipe(struct net_device *dev)
-{
- int rc;
- int cpu;
- int first_ring, ring;
- int instance = mpipe_instance(dev);
- struct mpipe_data *md = &mpipe_data[instance];
- int network_cpus_count = cpumask_weight(&network_cpus_map);
-
- if (!hash_default) {
- netdev_err(dev, "Networking requires hash_default!\n");
- return -EIO;
- }
-
- rc = gxio_mpipe_init(&md->context, instance);
- if (rc != 0) {
- netdev_err(dev, "gxio_mpipe_init: mpipe[%d] %d\n",
- instance, rc);
- return -EIO;
- }
-
- /* Set up the buffer stacks. */
- rc = init_buffer_stacks(dev, network_cpus_count);
- if (rc != 0)
- goto fail;
-
- /* Allocate one NotifRing for each network cpu. */
- rc = gxio_mpipe_alloc_notif_rings(&md->context,
- network_cpus_count, 0, 0);
- if (rc < 0) {
- netdev_err(dev, "gxio_mpipe_alloc_notif_rings failed %d\n",
- rc);
- goto fail;
- }
-
- /* Init NotifRings per-cpu. */
- first_ring = rc;
- ring = first_ring;
- for_each_online_cpu(cpu) {
- rc = alloc_percpu_mpipe_resources(dev, cpu, ring);
- if (rc < 0)
- goto fail;
- ring = rc;
- }
-
- /* Initialize NotifGroup and buckets. */
- rc = init_notif_group_and_buckets(dev, first_ring, network_cpus_count);
- if (rc != 0)
- goto fail;
-
- /* Create and enable interrupts. */
- rc = tile_net_setup_interrupts(dev);
- if (rc != 0)
- goto fail;
-
- /* Register PTP clock and set mPIPE timestamp, if configured. */
- register_ptp_clock(dev, md);
-
- return 0;
-
-fail:
- tile_net_init_mpipe_fail(instance);
- return rc;
-}
-
-/* Create persistent egress info for a given egress channel.
- * Note that this may be shared between, say, "gbe0" and "xgbe0".
- * ISSUE: Defer header allocation until TSO is actually needed?
- */
-static int tile_net_init_egress(struct net_device *dev, int echannel)
-{
- static int ering = -1;
- struct page *headers_page, *edescs_page, *equeue_page;
- gxio_mpipe_edesc_t *edescs;
- gxio_mpipe_equeue_t *equeue;
- unsigned char *headers;
- int headers_order, edescs_order, equeue_order;
- size_t edescs_size;
- int rc = -ENOMEM;
- int instance = mpipe_instance(dev);
- struct mpipe_data *md = &mpipe_data[instance];
-
- /* Only initialize once. */
- if (md->egress_for_echannel[echannel].equeue != NULL)
- return 0;
-
- /* Allocate memory for the "headers". */
- headers_order = get_order(EQUEUE_ENTRIES * HEADER_BYTES);
- headers_page = alloc_pages(GFP_KERNEL, headers_order);
- if (headers_page == NULL) {
- netdev_warn(dev,
- "Could not alloc %zd bytes for TSO headers.\n",
- PAGE_SIZE << headers_order);
- goto fail;
- }
- headers = pfn_to_kaddr(page_to_pfn(headers_page));
-
- /* Allocate memory for the "edescs". */
- edescs_size = EQUEUE_ENTRIES * sizeof(*edescs);
- edescs_order = get_order(edescs_size);
- edescs_page = alloc_pages(GFP_KERNEL, edescs_order);
- if (edescs_page == NULL) {
- netdev_warn(dev,
- "Could not alloc %zd bytes for eDMA ring.\n",
- edescs_size);
- goto fail_headers;
- }
- edescs = pfn_to_kaddr(page_to_pfn(edescs_page));
-
- /* Allocate memory for the "equeue". */
- equeue_order = get_order(sizeof(*equeue));
- equeue_page = alloc_pages(GFP_KERNEL, equeue_order);
- if (equeue_page == NULL) {
- netdev_warn(dev,
- "Could not alloc %zd bytes for equeue info.\n",
- PAGE_SIZE << equeue_order);
- goto fail_edescs;
- }
- equeue = pfn_to_kaddr(page_to_pfn(equeue_page));
-
- /* Allocate an edma ring (using a one entry "free list"). */
- if (ering < 0) {
- rc = gxio_mpipe_alloc_edma_rings(&md->context, 1, 0, 0);
- if (rc < 0) {
- netdev_warn(dev, "gxio_mpipe_alloc_edma_rings: "
- "mpipe[%d] %d\n", instance, rc);
- goto fail_equeue;
- }
- ering = rc;
- }
-
- /* Initialize the equeue. */
- rc = gxio_mpipe_equeue_init(equeue, &md->context, ering, echannel,
- edescs, edescs_size, 0);
- if (rc != 0) {
- netdev_err(dev, "gxio_mpipe_equeue_init: mpipe[%d] %d\n",
- instance, rc);
- goto fail_equeue;
- }
-
- /* Don't reuse the ering later. */
- ering = -1;
-
- if (jumbo_num != 0) {
- /* Make sure "jumbo" packets can be egressed safely. */
- if (gxio_mpipe_equeue_set_snf_size(equeue, 10368) < 0) {
- /* ISSUE: There is no "gxio_mpipe_equeue_destroy()". */
- netdev_warn(dev, "Jumbo packets may not be egressed"
- " properly on channel %d\n", echannel);
- }
- }
-
- /* Done. */
- md->egress_for_echannel[echannel].equeue = equeue;
- md->egress_for_echannel[echannel].headers = headers;
- return 0;
-
-fail_equeue:
- __free_pages(equeue_page, equeue_order);
-
-fail_edescs:
- __free_pages(edescs_page, edescs_order);
-
-fail_headers:
- __free_pages(headers_page, headers_order);
-
-fail:
- return rc;
-}
-
-/* Return channel number for a newly-opened link. */
-static int tile_net_link_open(struct net_device *dev, gxio_mpipe_link_t *link,
- const char *link_name)
-{
- int instance = mpipe_instance(dev);
- struct mpipe_data *md = &mpipe_data[instance];
- int rc = gxio_mpipe_link_open(link, &md->context, link_name, 0);
- if (rc < 0) {
- netdev_err(dev, "Failed to open '%s', mpipe[%d], %d\n",
- link_name, instance, rc);
- return rc;
- }
- if (jumbo_num != 0) {
- u32 attr = GXIO_MPIPE_LINK_RECEIVE_JUMBO;
- rc = gxio_mpipe_link_set_attr(link, attr, 1);
- if (rc != 0) {
- netdev_err(dev,
- "Cannot receive jumbo packets on '%s'\n",
- link_name);
- gxio_mpipe_link_close(link);
- return rc;
- }
- }
- rc = gxio_mpipe_link_channel(link);
- if (rc < 0 || rc >= TILE_NET_CHANNELS) {
- netdev_err(dev, "gxio_mpipe_link_channel bad value: %d\n", rc);
- gxio_mpipe_link_close(link);
- return -EINVAL;
- }
- return rc;
-}
-
-/* Help the kernel activate the given network interface. */
-static int tile_net_open(struct net_device *dev)
-{
- struct tile_net_priv *priv = netdev_priv(dev);
- int cpu, rc, instance;
-
- mutex_lock(&tile_net_devs_for_channel_mutex);
-
- /* Get the instance info. */
- rc = gxio_mpipe_link_instance(dev->name);
- if (rc < 0 || rc >= NR_MPIPE_MAX) {
- mutex_unlock(&tile_net_devs_for_channel_mutex);
- return -EIO;
- }
-
- priv->instance = rc;
- instance = rc;
- if (!mpipe_data[rc].context.mmio_fast_base) {
- /* Do one-time initialization per instance the first time
- * any device is opened.
- */
- rc = tile_net_init_mpipe(dev);
- if (rc != 0)
- goto fail;
- }
-
- /* Determine if this is the "loopify" device. */
- if (unlikely((loopify_link_name != NULL) &&
- !strcmp(dev->name, loopify_link_name))) {
- rc = tile_net_link_open(dev, &priv->link, "loop0");
- if (rc < 0)
- goto fail;
- priv->channel = rc;
- rc = tile_net_link_open(dev, &priv->loopify_link, "loop1");
- if (rc < 0)
- goto fail;
- priv->loopify_channel = rc;
- priv->echannel = rc;
- } else {
- rc = tile_net_link_open(dev, &priv->link, dev->name);
- if (rc < 0)
- goto fail;
- priv->channel = rc;
- priv->echannel = rc;
- }
-
- /* Initialize egress info (if needed). Once ever, per echannel. */
- rc = tile_net_init_egress(dev, priv->echannel);
- if (rc != 0)
- goto fail;
-
- mpipe_data[instance].tile_net_devs_for_channel[priv->channel] = dev;
-
- rc = tile_net_update(dev);
- if (rc != 0)
- goto fail;
-
- mutex_unlock(&tile_net_devs_for_channel_mutex);
-
- /* Initialize the transmit wake timer for this device for each cpu. */
- for_each_online_cpu(cpu) {
- struct tile_net_info *info = &per_cpu(per_cpu_info, cpu);
- struct tile_net_tx_wake *tx_wake =
- &info->mpipe[instance].tx_wake[priv->echannel];
-
- hrtimer_init(&tx_wake->timer, CLOCK_MONOTONIC,
- HRTIMER_MODE_REL);
- tx_wake->tx_queue_idx = cpu;
- tx_wake->timer.function = tile_net_handle_tx_wake_timer;
- tx_wake->dev = dev;
- }
-
- for_each_online_cpu(cpu)
- netif_start_subqueue(dev, cpu);
- netif_carrier_on(dev);
- return 0;
-
-fail:
- if (priv->loopify_channel >= 0) {
- if (gxio_mpipe_link_close(&priv->loopify_link) != 0)
- netdev_warn(dev, "Failed to close loopify link!\n");
- priv->loopify_channel = -1;
- }
- if (priv->channel >= 0) {
- if (gxio_mpipe_link_close(&priv->link) != 0)
- netdev_warn(dev, "Failed to close link!\n");
- priv->channel = -1;
- }
- priv->echannel = -1;
- mpipe_data[instance].tile_net_devs_for_channel[priv->channel] = NULL;
- mutex_unlock(&tile_net_devs_for_channel_mutex);
-
- /* Don't return raw gxio error codes to generic Linux. */
- return (rc > -512) ? rc : -EIO;
-}
-
-/* Help the kernel deactivate the given network interface. */
-static int tile_net_stop(struct net_device *dev)
-{
- struct tile_net_priv *priv = netdev_priv(dev);
- int cpu;
- int instance = priv->instance;
- struct mpipe_data *md = &mpipe_data[instance];
-
- for_each_online_cpu(cpu) {
- struct tile_net_info *info = &per_cpu(per_cpu_info, cpu);
- struct tile_net_tx_wake *tx_wake =
- &info->mpipe[instance].tx_wake[priv->echannel];
-
- hrtimer_cancel(&tx_wake->timer);
- netif_stop_subqueue(dev, cpu);
- }
-
- mutex_lock(&tile_net_devs_for_channel_mutex);
- md->tile_net_devs_for_channel[priv->channel] = NULL;
- (void)tile_net_update(dev);
- if (priv->loopify_channel >= 0) {
- if (gxio_mpipe_link_close(&priv->loopify_link) != 0)
- netdev_warn(dev, "Failed to close loopify link!\n");
- priv->loopify_channel = -1;
- }
- if (priv->channel >= 0) {
- if (gxio_mpipe_link_close(&priv->link) != 0)
- netdev_warn(dev, "Failed to close link!\n");
- priv->channel = -1;
- }
- priv->echannel = -1;
- mutex_unlock(&tile_net_devs_for_channel_mutex);
-
- return 0;
-}
-
-/* Determine the VA for a fragment. */
-static inline void *tile_net_frag_buf(skb_frag_t *f)
-{
- unsigned long pfn = page_to_pfn(skb_frag_page(f));
- return pfn_to_kaddr(pfn) + f->page_offset;
-}
-
-/* Acquire a completion entry and an egress slot, or if we can't,
- * stop the queue and schedule the tx_wake timer.
- */
-static s64 tile_net_equeue_try_reserve(struct net_device *dev,
- int tx_queue_idx,
- struct tile_net_comps *comps,
- gxio_mpipe_equeue_t *equeue,
- int num_edescs)
-{
- /* Try to acquire a completion entry. */
- if (comps->comp_next - comps->comp_last < TILE_NET_MAX_COMPS - 1 ||
- tile_net_free_comps(equeue, comps, 32, false) != 0) {
-
- /* Try to acquire an egress slot. */
- s64 slot = gxio_mpipe_equeue_try_reserve(equeue, num_edescs);
- if (slot >= 0)
- return slot;
-
- /* Freeing some completions gives the equeue time to drain. */
- tile_net_free_comps(equeue, comps, TILE_NET_MAX_COMPS, false);
-
- slot = gxio_mpipe_equeue_try_reserve(equeue, num_edescs);
- if (slot >= 0)
- return slot;
- }
-
- /* Still nothing; give up and stop the queue for a short while. */
- netif_stop_subqueue(dev, tx_queue_idx);
- tile_net_schedule_tx_wake_timer(dev, tx_queue_idx);
- return -1;
-}
-
-/* Determine how many edesc's are needed for TSO.
- *
- * Sometimes, if "sendfile()" requires copying, we will be called with
- * "data" containing the header and payload, with "frags" being empty.
- * Sometimes, for example when using NFS over TCP, a single segment can
- * span 3 fragments. This requires special care.
- */
-static int tso_count_edescs(struct sk_buff *skb)
-{
- struct skb_shared_info *sh = skb_shinfo(skb);
- unsigned int sh_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
- unsigned int data_len = skb->len - sh_len;
- unsigned int p_len = sh->gso_size;
- long f_id = -1; /* id of the current fragment */
- long f_size = skb_headlen(skb) - sh_len; /* current fragment size */
- long f_used = 0; /* bytes used from the current fragment */
- long n; /* size of the current piece of payload */
- int num_edescs = 0;
- int segment;
-
- for (segment = 0; segment < sh->gso_segs; segment++) {
-
- unsigned int p_used = 0;
-
- /* One edesc for header and for each piece of the payload. */
- for (num_edescs++; p_used < p_len; num_edescs++) {
-
- /* Advance as needed. */
- while (f_used >= f_size) {
- f_id++;
- f_size = skb_frag_size(&sh->frags[f_id]);
- f_used = 0;
- }
-
- /* Use bytes from the current fragment. */
- n = p_len - p_used;
- if (n > f_size - f_used)
- n = f_size - f_used;
- f_used += n;
- p_used += n;
- }
-
- /* The last segment may be less than gso_size. */
- data_len -= p_len;
- if (data_len < p_len)
- p_len = data_len;
- }
-
- return num_edescs;
-}
-
-/* Prepare modified copies of the skbuff headers. */
-static void tso_headers_prepare(struct sk_buff *skb, unsigned char *headers,
- s64 slot)
-{
- struct skb_shared_info *sh = skb_shinfo(skb);
- struct iphdr *ih;
- struct ipv6hdr *ih6;
- struct tcphdr *th;
- unsigned int sh_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
- unsigned int data_len = skb->len - sh_len;
- unsigned char *data = skb->data;
- unsigned int ih_off, th_off, p_len;
- unsigned int isum_seed, tsum_seed, seq;
- unsigned int uninitialized_var(id);
- int is_ipv6;
- long f_id = -1; /* id of the current fragment */
- long f_size = skb_headlen(skb) - sh_len; /* current fragment size */
- long f_used = 0; /* bytes used from the current fragment */
- long n; /* size of the current piece of payload */
- int segment;
-
- /* Locate original headers and compute various lengths. */
- is_ipv6 = skb_is_gso_v6(skb);
- if (is_ipv6) {
- ih6 = ipv6_hdr(skb);
- ih_off = skb_network_offset(skb);
- } else {
- ih = ip_hdr(skb);
- ih_off = skb_network_offset(skb);
- isum_seed = ((0xFFFF - ih->check) +
- (0xFFFF - ih->tot_len) +
- (0xFFFF - ih->id));
- id = ntohs(ih->id);
- }
-
- th = tcp_hdr(skb);
- th_off = skb_transport_offset(skb);
- p_len = sh->gso_size;
-
- tsum_seed = th->check + (0xFFFF ^ htons(skb->len));
- seq = ntohl(th->seq);
-
- /* Prepare all the headers. */
- for (segment = 0; segment < sh->gso_segs; segment++) {
- unsigned char *buf;
- unsigned int p_used = 0;
-
- /* Copy to the header memory for this segment. */
- buf = headers + (slot % EQUEUE_ENTRIES) * HEADER_BYTES +
- NET_IP_ALIGN;
- memcpy(buf, data, sh_len);
-
- /* Update copied ip header. */
- if (is_ipv6) {
- ih6 = (struct ipv6hdr *)(buf + ih_off);
- ih6->payload_len = htons(sh_len + p_len - ih_off -
- sizeof(*ih6));
- } else {
- ih = (struct iphdr *)(buf + ih_off);
- ih->tot_len = htons(sh_len + p_len - ih_off);
- ih->id = htons(id++);
- ih->check = csum_long(isum_seed + ih->tot_len +
- ih->id) ^ 0xffff;
- }
-
- /* Update copied tcp header. */
- th = (struct tcphdr *)(buf + th_off);
- th->seq = htonl(seq);
- th->check = csum_long(tsum_seed + htons(sh_len + p_len));
- if (segment != sh->gso_segs - 1) {
- th->fin = 0;
- th->psh = 0;
- }
-
- /* Skip past the header. */
- slot++;
-
- /* Skip past the payload. */
- while (p_used < p_len) {
-
- /* Advance as needed. */
- while (f_used >= f_size) {
- f_id++;
- f_size = skb_frag_size(&sh->frags[f_id]);
- f_used = 0;
- }
-
- /* Use bytes from the current fragment. */
- n = p_len - p_used;
- if (n > f_size - f_used)
- n = f_size - f_used;
- f_used += n;
- p_used += n;
-
- slot++;
- }
-
- seq += p_len;
-
- /* The last segment may be less than gso_size. */
- data_len -= p_len;
- if (data_len < p_len)
- p_len = data_len;
- }
-
- /* Flush the headers so they are ready for hardware DMA. */
- wmb();
-}
-
-/* Pass all the data to mpipe for egress. */
-static void tso_egress(struct net_device *dev, gxio_mpipe_equeue_t *equeue,
- struct sk_buff *skb, unsigned char *headers, s64 slot)
-{
- struct skb_shared_info *sh = skb_shinfo(skb);
- int instance = mpipe_instance(dev);
- struct mpipe_data *md = &mpipe_data[instance];
- unsigned int sh_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
- unsigned int data_len = skb->len - sh_len;
- unsigned int p_len = sh->gso_size;
- gxio_mpipe_edesc_t edesc_head = { { 0 } };
- gxio_mpipe_edesc_t edesc_body = { { 0 } };
- long f_id = -1; /* id of the current fragment */
- long f_size = skb_headlen(skb) - sh_len; /* current fragment size */
- long f_used = 0; /* bytes used from the current fragment */
- void *f_data = skb->data + sh_len;
- long n; /* size of the current piece of payload */
- unsigned long tx_packets = 0, tx_bytes = 0;
- unsigned int csum_start;
- int segment;
-
- /* Prepare to egress the headers: set up header edesc. */
- csum_start = skb_checksum_start_offset(skb);
- edesc_head.csum = 1;
- edesc_head.csum_start = csum_start;
- edesc_head.csum_dest = csum_start + skb->csum_offset;
- edesc_head.xfer_size = sh_len;
-
- /* This is only used to specify the TLB. */
- edesc_head.stack_idx = md->first_buffer_stack;
- edesc_body.stack_idx = md->first_buffer_stack;
-
- /* Egress all the edescs. */
- for (segment = 0; segment < sh->gso_segs; segment++) {
- unsigned char *buf;
- unsigned int p_used = 0;
-
- /* Egress the header. */
- buf = headers + (slot % EQUEUE_ENTRIES) * HEADER_BYTES +
- NET_IP_ALIGN;
- edesc_head.va = va_to_tile_io_addr(buf);
- gxio_mpipe_equeue_put_at(equeue, edesc_head, slot);
- slot++;
-
- /* Egress the payload. */
- while (p_used < p_len) {
- void *va;
-
- /* Advance as needed. */
- while (f_used >= f_size) {
- f_id++;
- f_size = skb_frag_size(&sh->frags[f_id]);
- f_data = tile_net_frag_buf(&sh->frags[f_id]);
- f_used = 0;
- }
-
- va = f_data + f_used;
-
- /* Use bytes from the current fragment. */
- n = p_len - p_used;
- if (n > f_size - f_used)
- n = f_size - f_used;
- f_used += n;
- p_used += n;
-
- /* Egress a piece of the payload. */
- edesc_body.va = va_to_tile_io_addr(va);
- edesc_body.xfer_size = n;
- edesc_body.bound = !(p_used < p_len);
- gxio_mpipe_equeue_put_at(equeue, edesc_body, slot);
- slot++;
- }
-
- tx_packets++;
- tx_bytes += sh_len + p_len;
-
- /* The last segment may be less than gso_size. */
- data_len -= p_len;
- if (data_len < p_len)
- p_len = data_len;
- }
-
- /* Update stats. */
- tile_net_stats_add(tx_packets, &dev->stats.tx_packets);
- tile_net_stats_add(tx_bytes, &dev->stats.tx_bytes);
-}
-
-/* Do "TSO" handling for egress.
- *
- * Normally drivers set NETIF_F_TSO only to support hardware TSO;
- * otherwise the stack uses scatter-gather to implement GSO in software.
- * On our testing, enabling GSO support (via NETIF_F_SG) drops network
- * performance down to around 7.5 Gbps on the 10G interfaces, although
- * also dropping cpu utilization way down, to under 8%. But
- * implementing "TSO" in the driver brings performance back up to line
- * rate, while dropping cpu usage even further, to less than 4%. In
- * practice, profiling of GSO shows that skb_segment() is what causes
- * the performance overheads; we benefit in the driver from using
- * preallocated memory to duplicate the TCP/IP headers.
- */
-static int tile_net_tx_tso(struct sk_buff *skb, struct net_device *dev)
-{
- struct tile_net_info *info = this_cpu_ptr(&per_cpu_info);
- struct tile_net_priv *priv = netdev_priv(dev);
- int channel = priv->echannel;
- int instance = priv->instance;
- struct mpipe_data *md = &mpipe_data[instance];
- struct tile_net_egress *egress = &md->egress_for_echannel[channel];
- struct tile_net_comps *comps =
- info->mpipe[instance].comps_for_echannel[channel];
- gxio_mpipe_equeue_t *equeue = egress->equeue;
- unsigned long irqflags;
- int num_edescs;
- s64 slot;
-
- /* Determine how many mpipe edesc's are needed. */
- num_edescs = tso_count_edescs(skb);
-
- local_irq_save(irqflags);
-
- /* Try to acquire a completion entry and an egress slot. */
- slot = tile_net_equeue_try_reserve(dev, skb->queue_mapping, comps,
- equeue, num_edescs);
- if (slot < 0) {
- local_irq_restore(irqflags);
- return NETDEV_TX_BUSY;
- }
-
- /* Set up copies of header data properly. */
- tso_headers_prepare(skb, egress->headers, slot);
-
- /* Actually pass the data to the network hardware. */
- tso_egress(dev, equeue, skb, egress->headers, slot);
-
- /* Add a completion record. */
- add_comp(equeue, comps, slot + num_edescs - 1, skb);
-
- local_irq_restore(irqflags);
-
- /* Make sure the egress timer is scheduled. */
- tile_net_schedule_egress_timer();
-
- return NETDEV_TX_OK;
-}
-
-/* Analyze the body and frags for a transmit request. */
-static unsigned int tile_net_tx_frags(struct frag *frags,
- struct sk_buff *skb,
- void *b_data, unsigned int b_len)
-{
- unsigned int i, n = 0;
-
- struct skb_shared_info *sh = skb_shinfo(skb);
-
- if (b_len != 0) {
- frags[n].buf = b_data;
- frags[n++].length = b_len;
- }
-
- for (i = 0; i < sh->nr_frags; i++) {
- skb_frag_t *f = &sh->frags[i];
- frags[n].buf = tile_net_frag_buf(f);
- frags[n++].length = skb_frag_size(f);
- }
-
- return n;
-}
-
-/* Help the kernel transmit a packet. */
-static int tile_net_tx(struct sk_buff *skb, struct net_device *dev)
-{
- struct tile_net_info *info = this_cpu_ptr(&per_cpu_info);
- struct tile_net_priv *priv = netdev_priv(dev);
- int instance = priv->instance;
- struct mpipe_data *md = &mpipe_data[instance];
- struct tile_net_egress *egress =
- &md->egress_for_echannel[priv->echannel];
- gxio_mpipe_equeue_t *equeue = egress->equeue;
- struct tile_net_comps *comps =
- info->mpipe[instance].comps_for_echannel[priv->echannel];
- unsigned int len = skb->len;
- unsigned char *data = skb->data;
- unsigned int num_edescs;
- struct frag frags[MAX_FRAGS];
- gxio_mpipe_edesc_t edescs[MAX_FRAGS];
- unsigned long irqflags;
- gxio_mpipe_edesc_t edesc = { { 0 } };
- unsigned int i;
- s64 slot;
-
- if (skb_is_gso(skb))
- return tile_net_tx_tso(skb, dev);
-
- num_edescs = tile_net_tx_frags(frags, skb, data, skb_headlen(skb));
-
- /* This is only used to specify the TLB. */
- edesc.stack_idx = md->first_buffer_stack;
-
- /* Prepare the edescs. */
- for (i = 0; i < num_edescs; i++) {
- edesc.xfer_size = frags[i].length;
- edesc.va = va_to_tile_io_addr(frags[i].buf);
- edescs[i] = edesc;
- }
-
- /* Mark the final edesc. */
- edescs[num_edescs - 1].bound = 1;
-
- /* Add checksum info to the initial edesc, if needed. */
- if (skb->ip_summed == CHECKSUM_PARTIAL) {
- unsigned int csum_start = skb_checksum_start_offset(skb);
- edescs[0].csum = 1;
- edescs[0].csum_start = csum_start;
- edescs[0].csum_dest = csum_start + skb->csum_offset;
- }
-
- local_irq_save(irqflags);
-
- /* Try to acquire a completion entry and an egress slot. */
- slot = tile_net_equeue_try_reserve(dev, skb->queue_mapping, comps,
- equeue, num_edescs);
- if (slot < 0) {
- local_irq_restore(irqflags);
- return NETDEV_TX_BUSY;
- }
-
- for (i = 0; i < num_edescs; i++)
- gxio_mpipe_equeue_put_at(equeue, edescs[i], slot++);
-
- /* Store TX timestamp if needed. */
- tile_tx_timestamp(skb, instance);
-
- /* Add a completion record. */
- add_comp(equeue, comps, slot - 1, skb);
-
- /* NOTE: Use ETH_ZLEN for short packets (e.g. 42 < 60). */
- tile_net_stats_add(1, &dev->stats.tx_packets);
- tile_net_stats_add(max_t(unsigned int, len, ETH_ZLEN),
- &dev->stats.tx_bytes);
-
- local_irq_restore(irqflags);
-
- /* Make sure the egress timer is scheduled. */
- tile_net_schedule_egress_timer();
-
- return NETDEV_TX_OK;
-}
-
-/* Return subqueue id on this core (one per core). */
-static u16 tile_net_select_queue(struct net_device *dev, struct sk_buff *skb,
- void *accel_priv, select_queue_fallback_t fallback)
-{
- return smp_processor_id();
-}
-
-/* Deal with a transmit timeout. */
-static void tile_net_tx_timeout(struct net_device *dev)
-{
- int cpu;
-
- for_each_online_cpu(cpu)
- netif_wake_subqueue(dev, cpu);
-}
-
-/* Ioctl commands. */
-static int tile_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
- if (cmd == SIOCSHWTSTAMP)
- return tile_hwtstamp_set(dev, rq);
- if (cmd == SIOCGHWTSTAMP)
- return tile_hwtstamp_get(dev, rq);
-
- return -EOPNOTSUPP;
-}
-
-/* Change the Ethernet address of the NIC.
- *
- * The hypervisor driver does not support changing MAC address. However,
- * the hardware does not do anything with the MAC address, so the address
- * which gets used on outgoing packets, and which is accepted on incoming
- * packets, is completely up to us.
- *
- * Returns 0 on success, negative on failure.
- */
-static int tile_net_set_mac_address(struct net_device *dev, void *p)
-{
- struct sockaddr *addr = p;
-
- if (!is_valid_ether_addr(addr->sa_data))
- return -EINVAL;
- memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
- return 0;
-}
-
-#ifdef CONFIG_NET_POLL_CONTROLLER
-/* Polling 'interrupt' - used by things like netconsole to send skbs
- * without having to re-enable interrupts. It's not called while
- * the interrupt routine is executing.
- */
-static void tile_net_netpoll(struct net_device *dev)
-{
- int instance = mpipe_instance(dev);
- struct tile_net_info *info = this_cpu_ptr(&per_cpu_info);
- struct mpipe_data *md = &mpipe_data[instance];
-
- disable_percpu_irq(md->ingress_irq);
- napi_schedule(&info->mpipe[instance].napi);
- enable_percpu_irq(md->ingress_irq, 0);
-}
-#endif
-
-static const struct net_device_ops tile_net_ops = {
- .ndo_open = tile_net_open,
- .ndo_stop = tile_net_stop,
- .ndo_start_xmit = tile_net_tx,
- .ndo_select_queue = tile_net_select_queue,
- .ndo_do_ioctl = tile_net_ioctl,
- .ndo_tx_timeout = tile_net_tx_timeout,
- .ndo_set_mac_address = tile_net_set_mac_address,
-#ifdef CONFIG_NET_POLL_CONTROLLER
- .ndo_poll_controller = tile_net_netpoll,
-#endif
-};
-
-/* The setup function.
- *
- * This uses ether_setup() to assign various fields in dev, including
- * setting IFF_BROADCAST and IFF_MULTICAST, then sets some extra fields.
- */
-static void tile_net_setup(struct net_device *dev)
-{
- netdev_features_t features = 0;
-
- ether_setup(dev);
- dev->netdev_ops = &tile_net_ops;
- dev->watchdog_timeo = TILE_NET_TIMEOUT;
-
- /* MTU range: 68 - 1500 or 9000 */
- dev->mtu = ETH_DATA_LEN;
- dev->min_mtu = ETH_MIN_MTU;
- dev->max_mtu = jumbo_num ? TILE_JUMBO_MAX_MTU : ETH_DATA_LEN;
-
- features |= NETIF_F_HW_CSUM;
- features |= NETIF_F_SG;
- features |= NETIF_F_TSO;
- features |= NETIF_F_TSO6;
-
- dev->hw_features |= features;
- dev->vlan_features |= features;
- dev->features |= features;
-}
-
-/* Allocate the device structure, register the device, and obtain the
- * MAC address from the hypervisor.
- */
-static void tile_net_dev_init(const char *name, const uint8_t *mac)
-{
- int ret;
- struct net_device *dev;
- struct tile_net_priv *priv;
-
- /* HACK: Ignore "loop" links. */
- if (strncmp(name, "loop", 4) == 0)
- return;
-
- /* Allocate the device structure. Normally, "name" is a
- * template, instantiated by register_netdev(), but not for us.
- */
- dev = alloc_netdev_mqs(sizeof(*priv), name, NET_NAME_UNKNOWN,
- tile_net_setup, NR_CPUS, 1);
- if (!dev) {
- pr_err("alloc_netdev_mqs(%s) failed\n", name);
- return;
- }
-
- /* Initialize "priv". */
- priv = netdev_priv(dev);
- priv->dev = dev;
- priv->channel = -1;
- priv->loopify_channel = -1;
- priv->echannel = -1;
- init_ptp_dev(priv);
-
- /* Get the MAC address and set it in the device struct; this must
- * be done before the device is opened. If the MAC is all zeroes,
- * we use a random address, since we're probably on the simulator.
- */
- if (!is_zero_ether_addr(mac))
- ether_addr_copy(dev->dev_addr, mac);
- else
- eth_hw_addr_random(dev);
-
- /* Register the network device. */
- ret = register_netdev(dev);
- if (ret) {
- netdev_err(dev, "register_netdev failed %d\n", ret);
- free_netdev(dev);
- return;
- }
-}
-
-/* Per-cpu module initialization. */
-static void tile_net_init_module_percpu(void *unused)
-{
- struct tile_net_info *info = this_cpu_ptr(&per_cpu_info);
- int my_cpu = smp_processor_id();
- int instance;
-
- for (instance = 0; instance < NR_MPIPE_MAX; instance++) {
- info->mpipe[instance].has_iqueue = false;
- info->mpipe[instance].instance = instance;
- }
- info->my_cpu = my_cpu;
-
- /* Initialize the egress timer. */
- hrtimer_init(&info->egress_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- info->egress_timer.function = tile_net_handle_egress_timer;
-}
-
-/* Module initialization. */
-static int __init tile_net_init_module(void)
-{
- int i;
- char name[GXIO_MPIPE_LINK_NAME_LEN];
- uint8_t mac[6];
-
- pr_info("Tilera Network Driver\n");
-
- BUILD_BUG_ON(NR_MPIPE_MAX != 2);
-
- mutex_init(&tile_net_devs_for_channel_mutex);
-
- /* Initialize each CPU. */
- on_each_cpu(tile_net_init_module_percpu, NULL, 1);
-
- /* Find out what devices we have, and initialize them. */
- for (i = 0; gxio_mpipe_link_enumerate_mac(i, name, mac) >= 0; i++)
- tile_net_dev_init(name, mac);
-
- if (!network_cpus_init())
- cpumask_and(&network_cpus_map,
- housekeeping_cpumask(HK_FLAG_MISC), cpu_online_mask);
-
- return 0;
-}
-
-module_init(tile_net_init_module);
diff --git a/drivers/net/ethernet/tile/tilepro.c b/drivers/net/ethernet/tile/tilepro.c
deleted file mode 100644
index 56d06282fbde..000000000000
--- a/drivers/net/ethernet/tile/tilepro.c
+++ /dev/null
@@ -1,2397 +0,0 @@
-/*
- * Copyright 2011 Tilera Corporation. All Rights Reserved.
- *
- * 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 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, GOOD TITLE or
- * NON INFRINGEMENT. See the GNU General Public License for
- * more details.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/moduleparam.h>
-#include <linux/sched.h>
-#include <linux/kernel.h> /* printk() */
-#include <linux/slab.h> /* kmalloc() */
-#include <linux/errno.h> /* error codes */
-#include <linux/types.h> /* size_t */
-#include <linux/interrupt.h>
-#include <linux/in.h>
-#include <linux/netdevice.h> /* struct device, and other headers */
-#include <linux/etherdevice.h> /* eth_type_trans */
-#include <linux/skbuff.h>
-#include <linux/ioctl.h>
-#include <linux/cdev.h>
-#include <linux/hugetlb.h>
-#include <linux/in6.h>
-#include <linux/timer.h>
-#include <linux/io.h>
-#include <linux/u64_stats_sync.h>
-#include <asm/checksum.h>
-#include <asm/homecache.h>
-
-#include <hv/drv_xgbe_intf.h>
-#include <hv/drv_xgbe_impl.h>
-#include <hv/hypervisor.h>
-#include <hv/netio_intf.h>
-
-/* For TSO */
-#include <linux/ip.h>
-#include <linux/tcp.h>
-
-
-/*
- * First, "tile_net_init_module()" initializes all four "devices" which
- * can be used by linux.
- *
- * Then, "ifconfig DEVICE up" calls "tile_net_open()", which analyzes
- * the network cpus, then uses "tile_net_open_aux()" to initialize
- * LIPP/LEPP, and then uses "tile_net_open_inner()" to register all
- * the tiles, provide buffers to LIPP, allow ingress to start, and
- * turn on hypervisor interrupt handling (and NAPI) on all tiles.
- *
- * If registration fails due to the link being down, then "retry_work"
- * is used to keep calling "tile_net_open_inner()" until it succeeds.
- *
- * If "ifconfig DEVICE down" is called, it uses "tile_net_stop()" to
- * stop egress, drain the LIPP buffers, unregister all the tiles, stop
- * LIPP/LEPP, and wipe the LEPP queue.
- *
- * We start out with the ingress interrupt enabled on each CPU. When
- * this interrupt fires, we disable it, and call "napi_schedule()".
- * This will cause "tile_net_poll()" to be called, which will pull
- * packets from the netio queue, filtering them out, or passing them
- * to "netif_receive_skb()". If our budget is exhausted, we will
- * return, knowing we will be called again later. Otherwise, we
- * reenable the ingress interrupt, and call "napi_complete()".
- *
- * HACK: Since disabling the ingress interrupt is not reliable, we
- * ignore the interrupt if the global "active" flag is false.
- *
- *
- * NOTE: The use of "native_driver" ensures that EPP exists, and that
- * we are using "LIPP" and "LEPP".
- *
- * NOTE: Failing to free completions for an arbitrarily long time
- * (which is defined to be illegal) does in fact cause bizarre
- * problems. The "egress_timer" helps prevent this from happening.
- */
-
-
-/* HACK: Allow use of "jumbo" packets. */
-/* This should be 1500 if "jumbo" is not set in LIPP. */
-/* This should be at most 10226 (10240 - 14) if "jumbo" is set in LIPP. */
-/* ISSUE: This has not been thoroughly tested (except at 1500). */
-#define TILE_NET_MTU ETH_DATA_LEN
-
-/* HACK: Define this to verify incoming packets. */
-/* #define TILE_NET_VERIFY_INGRESS */
-
-/* Use 3000 to enable the Linux Traffic Control (QoS) layer, else 0. */
-#define TILE_NET_TX_QUEUE_LEN 0
-
-/* Define to dump packets (prints out the whole packet on tx and rx). */
-/* #define TILE_NET_DUMP_PACKETS */
-
-/* Define to enable debug spew (all PDEBUG's are enabled). */
-/* #define TILE_NET_DEBUG */
-
-
-/* Define to activate paranoia checks. */
-/* #define TILE_NET_PARANOIA */
-
-/* Default transmit lockup timeout period, in jiffies. */
-#define TILE_NET_TIMEOUT (5 * HZ)
-
-/* Default retry interval for bringing up the NetIO interface, in jiffies. */
-#define TILE_NET_RETRY_INTERVAL (5 * HZ)
-
-/* Number of ports (xgbe0, xgbe1, gbe0, gbe1). */
-#define TILE_NET_DEVS 4
-
-
-
-/* Paranoia. */
-#if NET_IP_ALIGN != LIPP_PACKET_PADDING
-#error "NET_IP_ALIGN must match LIPP_PACKET_PADDING."
-#endif
-
-
-/* Debug print. */
-#ifdef TILE_NET_DEBUG
-#define PDEBUG(fmt, args...) net_printk(fmt, ## args)
-#else
-#define PDEBUG(fmt, args...)
-#endif
-
-
-MODULE_AUTHOR("Tilera");
-MODULE_LICENSE("GPL");
-
-
-/*
- * Queue of incoming packets for a specific cpu and device.
- *
- * Includes a pointer to the "system" data, and the actual "user" data.
- */
-struct tile_netio_queue {
- netio_queue_impl_t *__system_part;
- netio_queue_user_impl_t __user_part;
-
-};
-
-
-/*
- * Statistics counters for a specific cpu and device.
- */
-struct tile_net_stats_t {
- struct u64_stats_sync syncp;
- u64 rx_packets; /* total packets received */
- u64 tx_packets; /* total packets transmitted */
- u64 rx_bytes; /* total bytes received */
- u64 tx_bytes; /* total bytes transmitted */
- u64 rx_errors; /* packets truncated or marked bad by hw */
- u64 rx_dropped; /* packets not for us or intf not up */
-};
-
-
-/*
- * Info for a specific cpu and device.
- *
- * ISSUE: There is a "dev" pointer in "napi" as well.
- */
-struct tile_net_cpu {
- /* The NAPI struct. */
- struct napi_struct napi;
- /* Packet queue. */
- struct tile_netio_queue queue;
- /* Statistics. */
- struct tile_net_stats_t stats;
- /* True iff NAPI is enabled. */
- bool napi_enabled;
- /* True if this tile has successfully registered with the IPP. */
- bool registered;
- /* True if the link was down last time we tried to register. */
- bool link_down;
- /* True if "egress_timer" is scheduled. */
- bool egress_timer_scheduled;
- /* Number of small sk_buffs which must still be provided. */
- unsigned int num_needed_small_buffers;
- /* Number of large sk_buffs which must still be provided. */
- unsigned int num_needed_large_buffers;
- /* A timer for handling egress completions. */
- struct timer_list egress_timer;
-};
-
-
-/*
- * Info for a specific device.
- */
-struct tile_net_priv {
- /* Our network device. */
- struct net_device *dev;
- /* Pages making up the egress queue. */
- struct page *eq_pages;
- /* Address of the actual egress queue. */
- lepp_queue_t *eq;
- /* Protects "eq". */
- spinlock_t eq_lock;
- /* The hypervisor handle for this interface. */
- int hv_devhdl;
- /* The intr bit mask that IDs this device. */
- u32 intr_id;
- /* True iff "tile_net_open_aux()" has succeeded. */
- bool partly_opened;
- /* True iff the device is "active". */
- bool active;
- /* Effective network cpus. */
- struct cpumask network_cpus_map;
- /* Number of network cpus. */
- int network_cpus_count;
- /* Credits per network cpu. */
- int network_cpus_credits;
- /* For NetIO bringup retries. */
- struct delayed_work retry_work;
- /* Quick access to per cpu data. */
- struct tile_net_cpu *cpu[NR_CPUS];
-};
-
-/* Log2 of the number of small pages needed for the egress queue. */
-#define EQ_ORDER get_order(sizeof(lepp_queue_t))
-/* Size of the egress queue's pages. */
-#define EQ_SIZE (1 << (PAGE_SHIFT + EQ_ORDER))
-
-/*
- * The actual devices (xgbe0, xgbe1, gbe0, gbe1).
- */
-static struct net_device *tile_net_devs[TILE_NET_DEVS];
-
-/*
- * The "tile_net_cpu" structures for each device.
- */
-static DEFINE_PER_CPU(struct tile_net_cpu, hv_xgbe0);
-static DEFINE_PER_CPU(struct tile_net_cpu, hv_xgbe1);
-static DEFINE_PER_CPU(struct tile_net_cpu, hv_gbe0);
-static DEFINE_PER_CPU(struct tile_net_cpu, hv_gbe1);
-
-
-/*
- * True if "network_cpus" was specified.
- */
-static bool network_cpus_used;
-
-/*
- * The actual cpus in "network_cpus".
- */
-static struct cpumask network_cpus_map;
-
-
-
-#ifdef TILE_NET_DEBUG
-/*
- * printk with extra stuff.
- *
- * We print the CPU we're running in brackets.
- */
-static void net_printk(char *fmt, ...)
-{
- int i;
- int len;
- va_list args;
- static char buf[256];
-
- len = sprintf(buf, "tile_net[%2.2d]: ", smp_processor_id());
- va_start(args, fmt);
- i = vscnprintf(buf + len, sizeof(buf) - len - 1, fmt, args);
- va_end(args);
- buf[255] = '\0';
- pr_notice(buf);
-}
-#endif
-
-
-#ifdef TILE_NET_DUMP_PACKETS
-/*
- * Dump a packet.
- */
-static void dump_packet(unsigned char *data, unsigned long length, char *s)
-{
- int my_cpu = smp_processor_id();
-
- unsigned long i;
- char buf[128];
-
- static unsigned int count;
-
- pr_info("dump_packet(data %p, length 0x%lx s %s count 0x%x)\n",
- data, length, s, count++);
-
- pr_info("\n");
-
- for (i = 0; i < length; i++) {
- if ((i & 0xf) == 0)
- sprintf(buf, "[%02d] %8.8lx:", my_cpu, i);
- sprintf(buf + strlen(buf), " %2.2x", data[i]);
- if ((i & 0xf) == 0xf || i == length - 1) {
- strcat(buf, "\n");
- pr_info("%s", buf);
- }
- }
-}
-#endif
-
-
-/*
- * Provide support for the __netio_fastio1() swint
- * (see <hv/drv_xgbe_intf.h> for how it is used).
- *
- * The fastio swint2 call may clobber all the caller-saved registers.
- * It rarely clobbers memory, but we allow for the possibility in
- * the signature just to be on the safe side.
- *
- * Also, gcc doesn't seem to allow an input operand to be
- * clobbered, so we fake it with dummy outputs.
- *
- * This function can't be static because of the way it is declared
- * in the netio header.
- */
-inline int __netio_fastio1(u32 fastio_index, u32 arg0)
-{
- long result, clobber_r1, clobber_r10;
- asm volatile("swint2"
- : "=R00" (result),
- "=R01" (clobber_r1), "=R10" (clobber_r10)
- : "R10" (fastio_index), "R01" (arg0)
- : "memory", "r2", "r3", "r4",
- "r5", "r6", "r7", "r8", "r9",
- "r11", "r12", "r13", "r14",
- "r15", "r16", "r17", "r18", "r19",
- "r20", "r21", "r22", "r23", "r24",
- "r25", "r26", "r27", "r28", "r29");
- return result;
-}
-
-
-static void tile_net_return_credit(struct tile_net_cpu *info)
-{
- struct tile_netio_queue *queue = &info->queue;
- netio_queue_user_impl_t *qup = &queue->__user_part;
-
- /* Return four credits after every fourth packet. */
- if (--qup->__receive_credit_remaining == 0) {
- u32 interval = qup->__receive_credit_interval;
- qup->__receive_credit_remaining = interval;
- __netio_fastio_return_credits(qup->__fastio_index, interval);
- }
-}
-
-
-
-/*
- * Provide a linux buffer to LIPP.
- */
-static void tile_net_provide_linux_buffer(struct tile_net_cpu *info,
- void *va, bool small)
-{
- struct tile_netio_queue *queue = &info->queue;
-
- /* Convert "va" and "small" to "linux_buffer_t". */
- unsigned int buffer = ((unsigned int)(__pa(va) >> 7) << 1) + small;
-
- __netio_fastio_free_buffer(queue->__user_part.__fastio_index, buffer);
-}
-
-
-/*
- * Provide a linux buffer for LIPP.
- *
- * Note that the ACTUAL allocation for each buffer is a "struct sk_buff",
- * plus a chunk of memory that includes not only the requested bytes, but
- * also NET_SKB_PAD bytes of initial padding, and a "struct skb_shared_info".
- *
- * Note that "struct skb_shared_info" is 88 bytes with 64K pages and
- * 268 bytes with 4K pages (since the frags[] array needs 18 entries).
- *
- * Without jumbo packets, the maximum packet size will be 1536 bytes,
- * and we use 2 bytes (NET_IP_ALIGN) of padding. ISSUE: If we told
- * the hardware to clip at 1518 bytes instead of 1536 bytes, then we
- * could save an entire cache line, but in practice, we don't need it.
- *
- * Since CPAs are 38 bits, and we can only encode the high 31 bits in
- * a "linux_buffer_t", the low 7 bits must be zero, and thus, we must
- * align the actual "va" mod 128.
- *
- * We assume that the underlying "head" will be aligned mod 64. Note
- * that in practice, we have seen "head" NOT aligned mod 128 even when
- * using 2048 byte allocations, which is surprising.
- *
- * If "head" WAS always aligned mod 128, we could change LIPP to
- * assume that the low SIX bits are zero, and the 7th bit is one, that
- * is, align the actual "va" mod 128 plus 64, which would be "free".
- *
- * For now, the actual "head" pointer points at NET_SKB_PAD bytes of
- * padding, plus 28 or 92 bytes of extra padding, plus the sk_buff
- * pointer, plus the NET_IP_ALIGN padding, plus 126 or 1536 bytes for
- * the actual packet, plus 62 bytes of empty padding, plus some
- * padding and the "struct skb_shared_info".
- *
- * With 64K pages, a large buffer thus needs 32+92+4+2+1536+62+88
- * bytes, or 1816 bytes, which fits comfortably into 2048 bytes.
- *
- * With 64K pages, a small buffer thus needs 32+92+4+2+126+88
- * bytes, or 344 bytes, which means we are wasting 64+ bytes, and
- * could presumably increase the size of small buffers.
- *
- * With 4K pages, a large buffer thus needs 32+92+4+2+1536+62+268
- * bytes, or 1996 bytes, which fits comfortably into 2048 bytes.
- *
- * With 4K pages, a small buffer thus needs 32+92+4+2+126+268
- * bytes, or 524 bytes, which is annoyingly wasteful.
- *
- * Maybe we should increase LIPP_SMALL_PACKET_SIZE to 192?
- *
- * ISSUE: Maybe we should increase "NET_SKB_PAD" to 64?
- */
-static bool tile_net_provide_needed_buffer(struct tile_net_cpu *info,
- bool small)
-{
-#if TILE_NET_MTU <= 1536
- /* Without "jumbo", 2 + 1536 should be sufficient. */
- unsigned int large_size = NET_IP_ALIGN + 1536;
-#else
- /* ISSUE: This has not been tested. */
- unsigned int large_size = NET_IP_ALIGN + TILE_NET_MTU + 100;
-#endif
-
- /* Avoid "false sharing" with last cache line. */
- /* ISSUE: This is already done by "netdev_alloc_skb()". */
- unsigned int len =
- (((small ? LIPP_SMALL_PACKET_SIZE : large_size) +
- CHIP_L2_LINE_SIZE() - 1) & -CHIP_L2_LINE_SIZE());
-
- unsigned int padding = 128 - NET_SKB_PAD;
- unsigned int align;
-
- struct sk_buff *skb;
- void *va;
-
- struct sk_buff **skb_ptr;
-
- /* Request 96 extra bytes for alignment purposes. */
- skb = netdev_alloc_skb(info->napi.dev, len + padding);
- if (skb == NULL)
- return false;
-
- /* Skip 32 or 96 bytes to align "data" mod 128. */
- align = -(long)skb->data & (128 - 1);
- BUG_ON(align > padding);
- skb_reserve(skb, align);
-
- /* This address is given to IPP. */
- va = skb->data;
-
- /* Buffers must not span a huge page. */
- BUG_ON(((((long)va & ~HPAGE_MASK) + len) & HPAGE_MASK) != 0);
-
-#ifdef TILE_NET_PARANOIA
-#if CHIP_HAS_CBOX_HOME_MAP()
- if (hash_default) {
- HV_PTE pte = *virt_to_pte(current->mm, (unsigned long)va);
- if (hv_pte_get_mode(pte) != HV_PTE_MODE_CACHE_HASH_L3)
- panic("Non-HFH ingress buffer! VA=%p Mode=%d PTE=%llx",
- va, hv_pte_get_mode(pte), hv_pte_val(pte));
- }
-#endif
-#endif
-
- /* Invalidate the packet buffer. */
- if (!hash_default)
- __inv_buffer(va, len);
-
- /* Skip two bytes to satisfy LIPP assumptions. */
- /* Note that this aligns IP on a 16 byte boundary. */
- /* ISSUE: Do this when the packet arrives? */
- skb_reserve(skb, NET_IP_ALIGN);
-
- /* Save a back-pointer to 'skb'. */
- skb_ptr = va - sizeof(*skb_ptr);
- *skb_ptr = skb;
-
- /* Make sure "skb_ptr" has been flushed. */
- __insn_mf();
-
- /* Provide the new buffer. */
- tile_net_provide_linux_buffer(info, va, small);
-
- return true;
-}
-
-
-/*
- * Provide linux buffers for LIPP.
- */
-static void tile_net_provide_needed_buffers(struct tile_net_cpu *info)
-{
- while (info->num_needed_small_buffers != 0) {
- if (!tile_net_provide_needed_buffer(info, true))
- goto oops;
- info->num_needed_small_buffers--;
- }
-
- while (info->num_needed_large_buffers != 0) {
- if (!tile_net_provide_needed_buffer(info, false))
- goto oops;
- info->num_needed_large_buffers--;
- }
-
- return;
-
-oops:
-
- /* Add a description to the page allocation failure dump. */
- pr_notice("Could not provide a linux buffer to LIPP.\n");
-}
-
-
-/*
- * Grab some LEPP completions, and store them in "comps", of size
- * "comps_size", and return the number of completions which were
- * stored, so the caller can free them.
- */
-static unsigned int tile_net_lepp_grab_comps(lepp_queue_t *eq,
- struct sk_buff *comps[],
- unsigned int comps_size,
- unsigned int min_size)
-{
- unsigned int n = 0;
-
- unsigned int comp_head = eq->comp_head;
- unsigned int comp_busy = eq->comp_busy;
-
- while (comp_head != comp_busy && n < comps_size) {
- comps[n++] = eq->comps[comp_head];
- LEPP_QINC(comp_head);
- }
-
- if (n < min_size)
- return 0;
-
- eq->comp_head = comp_head;
-
- return n;
-}
-
-
-/*
- * Free some comps, and return true iff there are still some pending.
- */
-static bool tile_net_lepp_free_comps(struct net_device *dev, bool all)
-{
- struct tile_net_priv *priv = netdev_priv(dev);
-
- lepp_queue_t *eq = priv->eq;
-
- struct sk_buff *olds[64];
- unsigned int wanted = 64;
- unsigned int i, n;
- bool pending;
-
- spin_lock(&priv->eq_lock);
-
- if (all)
- eq->comp_busy = eq->comp_tail;
-
- n = tile_net_lepp_grab_comps(eq, olds, wanted, 0);
-
- pending = (eq->comp_head != eq->comp_tail);
-
- spin_unlock(&priv->eq_lock);
-
- for (i = 0; i < n; i++)
- kfree_skb(olds[i]);
-
- return pending;
-}
-
-
-/*
- * Make sure the egress timer is scheduled.
- *
- * Note that we use "schedule if not scheduled" logic instead of the more
- * obvious "reschedule" logic, because "reschedule" is fairly expensive.
- */
-static void tile_net_schedule_egress_timer(struct tile_net_cpu *info)
-{
- if (!info->egress_timer_scheduled) {
- mod_timer(&info->egress_timer, jiffies + 1);
- info->egress_timer_scheduled = true;
- }
-}
-
-
-/*
- * The "function" for "info->egress_timer".
- *
- * This timer will reschedule itself as long as there are any pending
- * completions expected (on behalf of any tile).
- *
- * ISSUE: Realistically, will the timer ever stop scheduling itself?
- *
- * ISSUE: This timer is almost never actually needed, so just use a global
- * timer that can run on any tile.
- *
- * ISSUE: Maybe instead track number of expected completions, and free
- * only that many, resetting to zero if "pending" is ever false.
- */
-static void tile_net_handle_egress_timer(struct timer_list *t)
-{
- struct tile_net_cpu *info = from_timer(info, t, egress_timer);
- struct net_device *dev = info->napi.dev;
-
- /* The timer is no longer scheduled. */
- info->egress_timer_scheduled = false;
-
- /* Free comps, and reschedule timer if more are pending. */
- if (tile_net_lepp_free_comps(dev, false))
- tile_net_schedule_egress_timer(info);
-}
-
-
-static void tile_net_discard_aux(struct tile_net_cpu *info, int index)
-{
- struct tile_netio_queue *queue = &info->queue;
- netio_queue_impl_t *qsp = queue->__system_part;
- netio_queue_user_impl_t *qup = &queue->__user_part;
-
- int index2_aux = index + sizeof(netio_pkt_t);
- int index2 =
- ((index2_aux ==
- qsp->__packet_receive_queue.__last_packet_plus_one) ?
- 0 : index2_aux);
-
- netio_pkt_t *pkt = (netio_pkt_t *)((unsigned long) &qsp[1] + index);
-
- /* Extract the "linux_buffer_t". */
- unsigned int buffer = pkt->__packet.word;
-
- /* Convert "linux_buffer_t" to "va". */
- void *va = __va((phys_addr_t)(buffer >> 1) << 7);
-
- /* Acquire the associated "skb". */
- struct sk_buff **skb_ptr = va - sizeof(*skb_ptr);
- struct sk_buff *skb = *skb_ptr;
-
- kfree_skb(skb);
-
- /* Consume this packet. */
- qup->__packet_receive_read = index2;
-}
-
-
-/*
- * Like "tile_net_poll()", but just discard packets.
- */
-static void tile_net_discard_packets(struct net_device *dev)
-{
- struct tile_net_priv *priv = netdev_priv(dev);
- int my_cpu = smp_processor_id();
- struct tile_net_cpu *info = priv->cpu[my_cpu];
- struct tile_netio_queue *queue = &info->queue;
- netio_queue_impl_t *qsp = queue->__system_part;
- netio_queue_user_impl_t *qup = &queue->__user_part;
-
- while (qup->__packet_receive_read !=
- qsp->__packet_receive_queue.__packet_write) {
- int index = qup->__packet_receive_read;
- tile_net_discard_aux(info, index);
- }
-}
-
-
-/*
- * Handle the next packet. Return true if "processed", false if "filtered".
- */
-static bool tile_net_poll_aux(struct tile_net_cpu *info, int index)
-{
- struct net_device *dev = info->napi.dev;
-
- struct tile_netio_queue *queue = &info->queue;
- netio_queue_impl_t *qsp = queue->__system_part;
- netio_queue_user_impl_t *qup = &queue->__user_part;
- struct tile_net_stats_t *stats = &info->stats;
-
- int filter;
-
- int index2_aux = index + sizeof(netio_pkt_t);
- int index2 =
- ((index2_aux ==
- qsp->__packet_receive_queue.__last_packet_plus_one) ?
- 0 : index2_aux);
-
- netio_pkt_t *pkt = (netio_pkt_t *)((unsigned long) &qsp[1] + index);
-
- netio_pkt_metadata_t *metadata = NETIO_PKT_METADATA(pkt);
- netio_pkt_status_t pkt_status = NETIO_PKT_STATUS_M(metadata, pkt);
-
- /* Extract the packet size. FIXME: Shouldn't the second line */
- /* get subtracted? Mostly moot, since it should be "zero". */
- unsigned long len =
- (NETIO_PKT_CUSTOM_LENGTH(pkt) +
- NET_IP_ALIGN - NETIO_PACKET_PADDING);
-
- /* Extract the "linux_buffer_t". */
- unsigned int buffer = pkt->__packet.word;
-
- /* Extract "small" (vs "large"). */
- bool small = ((buffer & 1) != 0);
-
- /* Convert "linux_buffer_t" to "va". */
- void *va = __va((phys_addr_t)(buffer >> 1) << 7);
-
- /* Extract the packet data pointer. */
- /* Compare to "NETIO_PKT_CUSTOM_DATA(pkt)". */
- unsigned char *buf = va + NET_IP_ALIGN;
-
- /* Invalidate the packet buffer. */
- if (!hash_default)
- __inv_buffer(buf, len);
-
-#ifdef TILE_NET_DUMP_PACKETS
- dump_packet(buf, len, "rx");
-#endif /* TILE_NET_DUMP_PACKETS */
-
-#ifdef TILE_NET_VERIFY_INGRESS
- if (pkt_status == NETIO_PKT_STATUS_OVERSIZE && len >= 64) {
- dump_packet(buf, len, "rx");
- panic("Unexpected OVERSIZE.");
- }
-#endif
-
- filter = 0;
-
- if (pkt_status == NETIO_PKT_STATUS_BAD) {
- /* Handle CRC error and hardware truncation. */
- filter = 2;
- } else if (!(dev->flags & IFF_UP)) {
- /* Filter packets received before we're up. */
- filter = 1;
- } else if (NETIO_PKT_ETHERTYPE_RECOGNIZED_M(metadata, pkt) &&
- pkt_status == NETIO_PKT_STATUS_UNDERSIZE) {
- /* Filter "truncated" packets. */
- filter = 2;
- } else if (!(dev->flags & IFF_PROMISC)) {
- if (!is_multicast_ether_addr(buf)) {
- /* Filter packets not for our address. */
- const u8 *mine = dev->dev_addr;
- filter = !ether_addr_equal(mine, buf);
- }
- }
-
- u64_stats_update_begin(&stats->syncp);
-
- if (filter != 0) {
-
- if (filter == 1)
- stats->rx_dropped++;
- else
- stats->rx_errors++;
-
- tile_net_provide_linux_buffer(info, va, small);
-
- } else {
-
- /* Acquire the associated "skb". */
- struct sk_buff **skb_ptr = va - sizeof(*skb_ptr);
- struct sk_buff *skb = *skb_ptr;
-
- /* Paranoia. */
- if (skb->data != buf)
- panic("Corrupt linux buffer from LIPP! "
- "VA=%p, skb=%p, skb->data=%p\n",
- va, skb, skb->data);
-
- /* Encode the actual packet length. */
- skb_put(skb, len);
-
- /* NOTE: This call also sets "skb->dev = dev". */
- skb->protocol = eth_type_trans(skb, dev);
-
- /* Avoid recomputing "good" TCP/UDP checksums. */
- if (NETIO_PKT_L4_CSUM_CORRECT_M(metadata, pkt))
- skb->ip_summed = CHECKSUM_UNNECESSARY;
-
- netif_receive_skb(skb);
-
- stats->rx_packets++;
- stats->rx_bytes += len;
- }
-
- u64_stats_update_end(&stats->syncp);
-
- /* ISSUE: It would be nice to defer this until the packet has */
- /* actually been processed. */
- tile_net_return_credit(info);
-
- /* Consume this packet. */
- qup->__packet_receive_read = index2;
-
- return !filter;
-}
-
-
-/*
- * Handle some packets for the given device on the current CPU.
- *
- * If "tile_net_stop()" is called on some other tile while this
- * function is running, we will return, hopefully before that
- * other tile asks us to call "napi_disable()".
- *
- * The "rotting packet" race condition occurs if a packet arrives
- * during the extremely narrow window between the queue appearing to
- * be empty, and the ingress interrupt being re-enabled. This happens
- * a LOT under heavy network load.
- */
-static int tile_net_poll(struct napi_struct *napi, int budget)
-{
- struct net_device *dev = napi->dev;
- struct tile_net_priv *priv = netdev_priv(dev);
- int my_cpu = smp_processor_id();
- struct tile_net_cpu *info = priv->cpu[my_cpu];
- struct tile_netio_queue *queue = &info->queue;
- netio_queue_impl_t *qsp = queue->__system_part;
- netio_queue_user_impl_t *qup = &queue->__user_part;
-
- unsigned int work = 0;
-
- if (budget <= 0)
- goto done;
-
- while (priv->active) {
- int index = qup->__packet_receive_read;
- if (index == qsp->__packet_receive_queue.__packet_write)
- break;
-
- if (tile_net_poll_aux(info, index)) {
- if (++work >= budget)
- goto done;
- }
- }
-
- napi_complete_done(&info->napi, work);
-
- if (!priv->active)
- goto done;
-
- /* Re-enable the ingress interrupt. */
- enable_percpu_irq(priv->intr_id, 0);
-
- /* HACK: Avoid the "rotting packet" problem (see above). */
- if (qup->__packet_receive_read !=
- qsp->__packet_receive_queue.__packet_write) {
- /* ISSUE: Sometimes this returns zero, presumably */
- /* because an interrupt was handled for this tile. */
- (void)napi_reschedule(&info->napi);
- }
-
-done:
-
- if (priv->active)
- tile_net_provide_needed_buffers(info);
-
- return work;
-}
-
-
-/*
- * Handle an ingress interrupt for the given device on the current cpu.
- *
- * ISSUE: Sometimes this gets called after "disable_percpu_irq()" has
- * been called! This is probably due to "pending hypervisor downcalls".
- *
- * ISSUE: Is there any race condition between the "napi_schedule()" here
- * and the "napi_complete()" call above?
- */
-static irqreturn_t tile_net_handle_ingress_interrupt(int irq, void *dev_ptr)
-{
- struct net_device *dev = (struct net_device *)dev_ptr;
- struct tile_net_priv *priv = netdev_priv(dev);
- int my_cpu = smp_processor_id();
- struct tile_net_cpu *info = priv->cpu[my_cpu];
-
- /* Disable the ingress interrupt. */
- disable_percpu_irq(priv->intr_id);
-
- /* Ignore unwanted interrupts. */
- if (!priv->active)
- return IRQ_HANDLED;
-
- /* ISSUE: Sometimes "info->napi_enabled" is false here. */
-
- napi_schedule(&info->napi);
-
- return IRQ_HANDLED;
-}
-
-
-/*
- * One time initialization per interface.
- */
-static int tile_net_open_aux(struct net_device *dev)
-{
- struct tile_net_priv *priv = netdev_priv(dev);
-
- int ret;
- int dummy;
- unsigned int epp_lotar;
-
- /*
- * Find out where EPP memory should be homed.
- */
- ret = hv_dev_pread(priv->hv_devhdl, 0,
- (HV_VirtAddr)&epp_lotar, sizeof(epp_lotar),
- NETIO_EPP_SHM_OFF);
- if (ret < 0) {
- pr_err("could not read epp_shm_queue lotar.\n");
- return -EIO;
- }
-
- /*
- * Home the page on the EPP.
- */
- {
- int epp_home = hv_lotar_to_cpu(epp_lotar);
- homecache_change_page_home(priv->eq_pages, EQ_ORDER, epp_home);
- }
-
- /*
- * Register the EPP shared memory queue.
- */
- {
- netio_ipp_address_t ea = {
- .va = 0,
- .pa = __pa(priv->eq),
- .pte = hv_pte(0),
- .size = EQ_SIZE,
- };
- ea.pte = hv_pte_set_lotar(ea.pte, epp_lotar);
- ea.pte = hv_pte_set_mode(ea.pte, HV_PTE_MODE_CACHE_TILE_L3);
- ret = hv_dev_pwrite(priv->hv_devhdl, 0,
- (HV_VirtAddr)&ea,
- sizeof(ea),
- NETIO_EPP_SHM_OFF);
- if (ret < 0)
- return -EIO;
- }
-
- /*
- * Start LIPP/LEPP.
- */
- if (hv_dev_pwrite(priv->hv_devhdl, 0, (HV_VirtAddr)&dummy,
- sizeof(dummy), NETIO_IPP_START_SHIM_OFF) < 0) {
- pr_warn("Failed to start LIPP/LEPP\n");
- return -EIO;
- }
-
- return 0;
-}
-
-
-/*
- * Register with hypervisor on the current CPU.
- *
- * Strangely, this function does important things even if it "fails",
- * which is especially common if the link is not up yet. Hopefully
- * these things are all "harmless" if done twice!
- */
-static void tile_net_register(void *dev_ptr)
-{
- struct net_device *dev = (struct net_device *)dev_ptr;
- struct tile_net_priv *priv = netdev_priv(dev);
- int my_cpu = smp_processor_id();
- struct tile_net_cpu *info;
-
- struct tile_netio_queue *queue;
-
- /* Only network cpus can receive packets. */
- int queue_id =
- cpumask_test_cpu(my_cpu, &priv->network_cpus_map) ? 0 : 255;
-
- netio_input_config_t config = {
- .flags = 0,
- .num_receive_packets = priv->network_cpus_credits,
- .queue_id = queue_id
- };
-
- int ret = 0;
- netio_queue_impl_t *queuep;
-
- PDEBUG("tile_net_register(queue_id %d)\n", queue_id);
-
- if (!strcmp(dev->name, "xgbe0"))
- info = this_cpu_ptr(&hv_xgbe0);
- else if (!strcmp(dev->name, "xgbe1"))
- info = this_cpu_ptr(&hv_xgbe1);
- else if (!strcmp(dev->name, "gbe0"))
- info = this_cpu_ptr(&hv_gbe0);
- else if (!strcmp(dev->name, "gbe1"))
- info = this_cpu_ptr(&hv_gbe1);
- else
- BUG();
-
- /* Initialize the egress timer. */
- timer_setup(&info->egress_timer, tile_net_handle_egress_timer,
- TIMER_PINNED);
-
- u64_stats_init(&info->stats.syncp);
-
- priv->cpu[my_cpu] = info;
-
- /*
- * Register ourselves with LIPP. This does a lot of stuff,
- * including invoking the LIPP registration code.
- */
- ret = hv_dev_pwrite(priv->hv_devhdl, 0,
- (HV_VirtAddr)&config,
- sizeof(netio_input_config_t),
- NETIO_IPP_INPUT_REGISTER_OFF);
- PDEBUG("hv_dev_pwrite(NETIO_IPP_INPUT_REGISTER_OFF) returned %d\n",
- ret);
- if (ret < 0) {
- if (ret != NETIO_LINK_DOWN) {
- printk(KERN_DEBUG "hv_dev_pwrite "
- "NETIO_IPP_INPUT_REGISTER_OFF failure %d\n",
- ret);
- }
- info->link_down = (ret == NETIO_LINK_DOWN);
- return;
- }
-
- /*
- * Get the pointer to our queue's system part.
- */
-
- ret = hv_dev_pread(priv->hv_devhdl, 0,
- (HV_VirtAddr)&queuep,
- sizeof(netio_queue_impl_t *),
- NETIO_IPP_INPUT_REGISTER_OFF);
- PDEBUG("hv_dev_pread(NETIO_IPP_INPUT_REGISTER_OFF) returned %d\n",
- ret);
- PDEBUG("queuep %p\n", queuep);
- if (ret <= 0) {
- /* ISSUE: Shouldn't this be a fatal error? */
- pr_err("hv_dev_pread NETIO_IPP_INPUT_REGISTER_OFF failure\n");
- return;
- }
-
- queue = &info->queue;
-
- queue->__system_part = queuep;
-
- memset(&queue->__user_part, 0, sizeof(netio_queue_user_impl_t));
-
- /* This is traditionally "config.num_receive_packets / 2". */
- queue->__user_part.__receive_credit_interval = 4;
- queue->__user_part.__receive_credit_remaining =
- queue->__user_part.__receive_credit_interval;
-
- /*
- * Get a fastio index from the hypervisor.
- * ISSUE: Shouldn't this check the result?
- */
- ret = hv_dev_pread(priv->hv_devhdl, 0,
- (HV_VirtAddr)&queue->__user_part.__fastio_index,
- sizeof(queue->__user_part.__fastio_index),
- NETIO_IPP_GET_FASTIO_OFF);
- PDEBUG("hv_dev_pread(NETIO_IPP_GET_FASTIO_OFF) returned %d\n", ret);
-
- /* Now we are registered. */
- info->registered = true;
-}
-
-
-/*
- * Deregister with hypervisor on the current CPU.
- *
- * This simply discards all our credits, so no more packets will be
- * delivered to this tile. There may still be packets in our queue.
- *
- * Also, disable the ingress interrupt.
- */
-static void tile_net_deregister(void *dev_ptr)
-{
- struct net_device *dev = (struct net_device *)dev_ptr;
- struct tile_net_priv *priv = netdev_priv(dev);
- int my_cpu = smp_processor_id();
- struct tile_net_cpu *info = priv->cpu[my_cpu];
-
- /* Disable the ingress interrupt. */
- disable_percpu_irq(priv->intr_id);
-
- /* Do nothing else if not registered. */
- if (info == NULL || !info->registered)
- return;
-
- {
- struct tile_netio_queue *queue = &info->queue;
- netio_queue_user_impl_t *qup = &queue->__user_part;
-
- /* Discard all our credits. */
- __netio_fastio_return_credits(qup->__fastio_index, -1);
- }
-}
-
-
-/*
- * Unregister with hypervisor on the current CPU.
- *
- * Also, disable the ingress interrupt.
- */
-static void tile_net_unregister(void *dev_ptr)
-{
- struct net_device *dev = (struct net_device *)dev_ptr;
- struct tile_net_priv *priv = netdev_priv(dev);
- int my_cpu = smp_processor_id();
- struct tile_net_cpu *info = priv->cpu[my_cpu];
-
- int ret;
- int dummy = 0;
-
- /* Disable the ingress interrupt. */
- disable_percpu_irq(priv->intr_id);
-
- /* Do nothing else if not registered. */
- if (info == NULL || !info->registered)
- return;
-
- /* Unregister ourselves with LIPP/LEPP. */
- ret = hv_dev_pwrite(priv->hv_devhdl, 0, (HV_VirtAddr)&dummy,
- sizeof(dummy), NETIO_IPP_INPUT_UNREGISTER_OFF);
- if (ret < 0)
- panic("Failed to unregister with LIPP/LEPP!\n");
-
- /* Discard all packets still in our NetIO queue. */
- tile_net_discard_packets(dev);
-
- /* Reset state. */
- info->num_needed_small_buffers = 0;
- info->num_needed_large_buffers = 0;
-
- /* Cancel egress timer. */
- del_timer(&info->egress_timer);
- info->egress_timer_scheduled = false;
-}
-
-
-/*
- * Helper function for "tile_net_stop()".
- *
- * Also used to handle registration failure in "tile_net_open_inner()",
- * when the various extra steps in "tile_net_stop()" are not necessary.
- */
-static void tile_net_stop_aux(struct net_device *dev)
-{
- struct tile_net_priv *priv = netdev_priv(dev);
- int i;
-
- int dummy = 0;
-
- /*
- * Unregister all tiles, so LIPP will stop delivering packets.
- * Also, delete all the "napi" objects (sequentially, to protect
- * "dev->napi_list").
- */
- on_each_cpu(tile_net_unregister, (void *)dev, 1);
- for_each_online_cpu(i) {
- struct tile_net_cpu *info = priv->cpu[i];
- if (info != NULL && info->registered) {
- netif_napi_del(&info->napi);
- info->registered = false;
- }
- }
-
- /* Stop LIPP/LEPP. */
- if (hv_dev_pwrite(priv->hv_devhdl, 0, (HV_VirtAddr)&dummy,
- sizeof(dummy), NETIO_IPP_STOP_SHIM_OFF) < 0)
- panic("Failed to stop LIPP/LEPP!\n");
-
- priv->partly_opened = false;
-}
-
-
-/*
- * Disable NAPI for the given device on the current cpu.
- */
-static void tile_net_stop_disable(void *dev_ptr)
-{
- struct net_device *dev = (struct net_device *)dev_ptr;
- struct tile_net_priv *priv = netdev_priv(dev);
- int my_cpu = smp_processor_id();
- struct tile_net_cpu *info = priv->cpu[my_cpu];
-
- /* Disable NAPI if needed. */
- if (info != NULL && info->napi_enabled) {
- napi_disable(&info->napi);
- info->napi_enabled = false;
- }
-}
-
-
-/*
- * Enable NAPI and the ingress interrupt for the given device
- * on the current cpu.
- *
- * ISSUE: Only do this for "network cpus"?
- */
-static void tile_net_open_enable(void *dev_ptr)
-{
- struct net_device *dev = (struct net_device *)dev_ptr;
- struct tile_net_priv *priv = netdev_priv(dev);
- int my_cpu = smp_processor_id();
- struct tile_net_cpu *info = priv->cpu[my_cpu];
-
- /* Enable NAPI. */
- napi_enable(&info->napi);
- info->napi_enabled = true;
-
- /* Enable the ingress interrupt. */
- enable_percpu_irq(priv->intr_id, 0);
-}
-
-
-/*
- * tile_net_open_inner does most of the work of bringing up the interface.
- * It's called from tile_net_open(), and also from tile_net_retry_open().
- * The return value is 0 if the interface was brought up, < 0 if
- * tile_net_open() should return the return value as an error, and > 0 if
- * tile_net_open() should return success and schedule a work item to
- * periodically retry the bringup.
- */
-static int tile_net_open_inner(struct net_device *dev)
-{
- struct tile_net_priv *priv = netdev_priv(dev);
- int my_cpu = smp_processor_id();
- struct tile_net_cpu *info;
- struct tile_netio_queue *queue;
- int result = 0;
- int i;
- int dummy = 0;
-
- /*
- * First try to register just on the local CPU, and handle any
- * semi-expected "link down" failure specially. Note that we
- * do NOT call "tile_net_stop_aux()", unlike below.
- */
- tile_net_register(dev);
- info = priv->cpu[my_cpu];
- if (!info->registered) {
- if (info->link_down)
- return 1;
- return -EAGAIN;
- }
-
- /*
- * Now register everywhere else. If any registration fails,
- * even for "link down" (which might not be possible), we
- * clean up using "tile_net_stop_aux()". Also, add all the
- * "napi" objects (sequentially, to protect "dev->napi_list").
- * ISSUE: Only use "netif_napi_add()" for "network cpus"?
- */
- smp_call_function(tile_net_register, (void *)dev, 1);
- for_each_online_cpu(i) {
- struct tile_net_cpu *info = priv->cpu[i];
- if (info->registered)
- netif_napi_add(dev, &info->napi, tile_net_poll, 64);
- else
- result = -EAGAIN;
- }
- if (result != 0) {
- tile_net_stop_aux(dev);
- return result;
- }
-
- queue = &info->queue;
-
- if (priv->intr_id == 0) {
- unsigned int irq;
-
- /*
- * Acquire the irq allocated by the hypervisor. Every
- * queue gets the same irq. The "__intr_id" field is
- * "1 << irq", so we use "__ffs()" to extract "irq".
- */
- priv->intr_id = queue->__system_part->__intr_id;
- BUG_ON(priv->intr_id == 0);
- irq = __ffs(priv->intr_id);
-
- /*
- * Register the ingress interrupt handler for this
- * device, permanently.
- *
- * We used to call "free_irq()" in "tile_net_stop()",
- * and then re-register the handler here every time,
- * but that caused DNP errors in "handle_IRQ_event()"
- * because "desc->action" was NULL. See bug 9143.
- */
- tile_irq_activate(irq, TILE_IRQ_PERCPU);
- BUG_ON(request_irq(irq, tile_net_handle_ingress_interrupt,
- 0, dev->name, (void *)dev) != 0);
- }
-
- {
- /* Allocate initial buffers. */
-
- int max_buffers =
- priv->network_cpus_count * priv->network_cpus_credits;
-
- info->num_needed_small_buffers =
- min(LIPP_SMALL_BUFFERS, max_buffers);
-
- info->num_needed_large_buffers =
- min(LIPP_LARGE_BUFFERS, max_buffers);
-
- tile_net_provide_needed_buffers(info);
-
- if (info->num_needed_small_buffers != 0 ||
- info->num_needed_large_buffers != 0)
- panic("Insufficient memory for buffer stack!");
- }
-
- /* We are about to be active. */
- priv->active = true;
-
- /* Make sure "active" is visible to all tiles. */
- mb();
-
- /* On each tile, enable NAPI and the ingress interrupt. */
- on_each_cpu(tile_net_open_enable, (void *)dev, 1);
-
- /* Start LIPP/LEPP and activate "ingress" at the shim. */
- if (hv_dev_pwrite(priv->hv_devhdl, 0, (HV_VirtAddr)&dummy,
- sizeof(dummy), NETIO_IPP_INPUT_INIT_OFF) < 0)
- panic("Failed to activate the LIPP Shim!\n");
-
- /* Start our transmit queue. */
- netif_start_queue(dev);
-
- return 0;
-}
-
-
-/*
- * Called periodically to retry bringing up the NetIO interface,
- * if it doesn't come up cleanly during tile_net_open().
- */
-static void tile_net_open_retry(struct work_struct *w)
-{
- struct delayed_work *dw = to_delayed_work(w);
-
- struct tile_net_priv *priv =
- container_of(dw, struct tile_net_priv, retry_work);
-
- /*
- * Try to bring the NetIO interface up. If it fails, reschedule
- * ourselves to try again later; otherwise, tell Linux we now have
- * a working link. ISSUE: What if the return value is negative?
- */
- if (tile_net_open_inner(priv->dev) != 0)
- schedule_delayed_work(&priv->retry_work,
- TILE_NET_RETRY_INTERVAL);
- else
- netif_carrier_on(priv->dev);
-}
-
-
-/*
- * Called when a network interface is made active.
- *
- * Returns 0 on success, negative value on failure.
- *
- * The open entry point is called when a network interface is made
- * active by the system (IFF_UP). At this point all resources needed
- * for transmit and receive operations are allocated, the interrupt
- * handler is registered with the OS (if needed), the watchdog timer
- * is started, and the stack is notified that the interface is ready.
- *
- * If the actual link is not available yet, then we tell Linux that
- * we have no carrier, and we keep checking until the link comes up.
- */
-static int tile_net_open(struct net_device *dev)
-{
- int ret = 0;
- struct tile_net_priv *priv = netdev_priv(dev);
-
- /*
- * We rely on priv->partly_opened to tell us if this is the
- * first time this interface is being brought up. If it is
- * set, the IPP was already initialized and should not be
- * initialized again.
- */
- if (!priv->partly_opened) {
-
- int count;
- int credits;
-
- /* Initialize LIPP/LEPP, and start the Shim. */
- ret = tile_net_open_aux(dev);
- if (ret < 0) {
- pr_err("tile_net_open_aux failed: %d\n", ret);
- return ret;
- }
-
- /* Analyze the network cpus. */
-
- if (network_cpus_used)
- cpumask_copy(&priv->network_cpus_map,
- &network_cpus_map);
- else
- cpumask_copy(&priv->network_cpus_map, cpu_online_mask);
-
-
- count = cpumask_weight(&priv->network_cpus_map);
-
- /* Limit credits to available buffers, and apply min. */
- credits = max(16, (LIPP_LARGE_BUFFERS / count) & ~1);
-
- /* Apply "GBE" max limit. */
- /* ISSUE: Use higher limit for XGBE? */
- credits = min(NETIO_MAX_RECEIVE_PKTS, credits);
-
- priv->network_cpus_count = count;
- priv->network_cpus_credits = credits;
-
-#ifdef TILE_NET_DEBUG
- pr_info("Using %d network cpus, with %d credits each\n",
- priv->network_cpus_count, priv->network_cpus_credits);
-#endif
-
- priv->partly_opened = true;
-
- } else {
- /* FIXME: Is this possible? */
- /* printk("Already partly opened.\n"); */
- }
-
- /*
- * Attempt to bring up the link.
- */
- ret = tile_net_open_inner(dev);
- if (ret <= 0) {
- if (ret == 0)
- netif_carrier_on(dev);
- return ret;
- }
-
- /*
- * We were unable to bring up the NetIO interface, but we want to
- * try again in a little bit. Tell Linux that we have no carrier
- * so it doesn't try to use the interface before the link comes up
- * and then remember to try again later.
- */
- netif_carrier_off(dev);
- schedule_delayed_work(&priv->retry_work, TILE_NET_RETRY_INTERVAL);
-
- return 0;
-}
-
-
-static int tile_net_drain_lipp_buffers(struct tile_net_priv *priv)
-{
- int n = 0;
-
- /* Drain all the LIPP buffers. */
- while (true) {
- unsigned int buffer;
-
- /* NOTE: This should never fail. */
- if (hv_dev_pread(priv->hv_devhdl, 0, (HV_VirtAddr)&buffer,
- sizeof(buffer), NETIO_IPP_DRAIN_OFF) < 0)
- break;
-
- /* Stop when done. */
- if (buffer == 0)
- break;
-
- {
- /* Convert "linux_buffer_t" to "va". */
- void *va = __va((phys_addr_t)(buffer >> 1) << 7);
-
- /* Acquire the associated "skb". */
- struct sk_buff **skb_ptr = va - sizeof(*skb_ptr);
- struct sk_buff *skb = *skb_ptr;
-
- kfree_skb(skb);
- }
-
- n++;
- }
-
- return n;
-}
-
-
-/*
- * Disables a network interface.
- *
- * Returns 0, this is not allowed to fail.
- *
- * The close entry point is called when an interface is de-activated
- * by the OS. The hardware is still under the drivers control, but
- * needs to be disabled. A global MAC reset is issued to stop the
- * hardware, and all transmit and receive resources are freed.
- *
- * ISSUE: How closely does "netif_running(dev)" mirror "priv->active"?
- *
- * Before we are called by "__dev_close()", "netif_running()" will
- * have been cleared, so no NEW calls to "tile_net_poll()" will be
- * made by "netpoll_poll_dev()".
- *
- * Often, this can cause some tiles to still have packets in their
- * queues, so we must call "tile_net_discard_packets()" later.
- *
- * Note that some other tile may still be INSIDE "tile_net_poll()",
- * and in fact, many will be, if there is heavy network load.
- *
- * Calling "on_each_cpu(tile_net_stop_disable, (void *)dev, 1)" when
- * any tile is still "napi_schedule()"'d will induce a horrible crash
- * when "msleep()" is called. This includes tiles which are inside
- * "tile_net_poll()" which have not yet called "napi_complete()".
- *
- * So, we must first try to wait long enough for other tiles to finish
- * with any current "tile_net_poll()" call, and, hopefully, to clear
- * the "scheduled" flag. ISSUE: It is unclear what happens to tiles
- * which have called "napi_schedule()" but which had not yet tried to
- * call "tile_net_poll()", or which exhausted their budget inside
- * "tile_net_poll()" just before this function was called.
- */
-static int tile_net_stop(struct net_device *dev)
-{
- struct tile_net_priv *priv = netdev_priv(dev);
-
- PDEBUG("tile_net_stop()\n");
-
- /* Start discarding packets. */
- priv->active = false;
-
- /* Make sure "active" is visible to all tiles. */
- mb();
-
- /*
- * On each tile, make sure no NEW packets get delivered, and
- * disable the ingress interrupt.
- *
- * Note that the ingress interrupt can fire AFTER this,
- * presumably due to packets which were recently delivered,
- * but it will have no effect.
- */
- on_each_cpu(tile_net_deregister, (void *)dev, 1);
-
- /* Optimistically drain LIPP buffers. */
- (void)tile_net_drain_lipp_buffers(priv);
-
- /* ISSUE: Only needed if not yet fully open. */
- cancel_delayed_work_sync(&priv->retry_work);
-
- /* Can't transmit any more. */
- netif_stop_queue(dev);
-
- /* Disable NAPI on each tile. */
- on_each_cpu(tile_net_stop_disable, (void *)dev, 1);
-
- /*
- * Drain any remaining LIPP buffers. NOTE: This "printk()"
- * has never been observed, but in theory it could happen.
- */
- if (tile_net_drain_lipp_buffers(priv) != 0)
- printk("Had to drain some extra LIPP buffers!\n");
-
- /* Stop LIPP/LEPP. */
- tile_net_stop_aux(dev);
-
- /*
- * ISSUE: It appears that, in practice anyway, by the time we
- * get here, there are no pending completions, but just in case,
- * we free (all of) them anyway.
- */
- while (tile_net_lepp_free_comps(dev, true))
- /* loop */;
-
- /* Wipe the EPP queue, and wait till the stores hit the EPP. */
- memset(priv->eq, 0, sizeof(lepp_queue_t));
- mb();
-
- return 0;
-}
-
-
-/*
- * Prepare the "frags" info for the resulting LEPP command.
- *
- * If needed, flush the memory used by the frags.
- */
-static unsigned int tile_net_tx_frags(lepp_frag_t *frags,
- struct sk_buff *skb,
- void *b_data, unsigned int b_len)
-{
- unsigned int i, n = 0;
-
- struct skb_shared_info *sh = skb_shinfo(skb);
-
- phys_addr_t cpa;
-
- if (b_len != 0) {
-
- if (!hash_default)
- finv_buffer_remote(b_data, b_len, 0);
-
- cpa = __pa(b_data);
- frags[n].cpa_lo = cpa;
- frags[n].cpa_hi = cpa >> 32;
- frags[n].length = b_len;
- frags[n].hash_for_home = hash_default;
- n++;
- }
-
- for (i = 0; i < sh->nr_frags; i++) {
-
- skb_frag_t *f = &sh->frags[i];
- unsigned long pfn = page_to_pfn(skb_frag_page(f));
-
- /* FIXME: Compute "hash_for_home" properly. */
- /* ISSUE: The hypervisor checks CHIP_HAS_REV1_DMA_PACKETS(). */
- int hash_for_home = hash_default;
-
- /* FIXME: Hmmm. */
- if (!hash_default) {
- void *va = pfn_to_kaddr(pfn) + f->page_offset;
- BUG_ON(PageHighMem(skb_frag_page(f)));
- finv_buffer_remote(va, skb_frag_size(f), 0);
- }
-
- cpa = ((phys_addr_t)pfn << PAGE_SHIFT) + f->page_offset;
- frags[n].cpa_lo = cpa;
- frags[n].cpa_hi = cpa >> 32;
- frags[n].length = skb_frag_size(f);
- frags[n].hash_for_home = hash_for_home;
- n++;
- }
-
- return n;
-}
-
-
-/*
- * This function takes "skb", consisting of a header template and a
- * payload, and hands it to LEPP, to emit as one or more segments,
- * each consisting of a possibly modified header, plus a piece of the
- * payload, via a process known as "tcp segmentation offload".
- *
- * Usually, "data" will contain the header template, of size "sh_len",
- * and "sh->frags" will contain "skb->data_len" bytes of payload, and
- * there will be "sh->gso_segs" segments.
- *
- * Sometimes, if "sendfile()" requires copying, we will be called with
- * "data" containing the header and payload, with "frags" being empty.
- *
- * Sometimes, for example when using NFS over TCP, a single segment can
- * span 3 fragments, which must be handled carefully in LEPP.
- *
- * See "emulate_large_send_offload()" for some reference code, which
- * does not handle checksumming.
- *
- * ISSUE: How do we make sure that high memory DMA does not migrate?
- */
-static int tile_net_tx_tso(struct sk_buff *skb, struct net_device *dev)
-{
- struct tile_net_priv *priv = netdev_priv(dev);
- int my_cpu = smp_processor_id();
- struct tile_net_cpu *info = priv->cpu[my_cpu];
- struct tile_net_stats_t *stats = &info->stats;
-
- struct skb_shared_info *sh = skb_shinfo(skb);
-
- unsigned char *data = skb->data;
-
- /* The ip header follows the ethernet header. */
- struct iphdr *ih = ip_hdr(skb);
- unsigned int ih_len = ih->ihl * 4;
-
- /* Note that "nh == ih", by definition. */
- unsigned char *nh = skb_network_header(skb);
- unsigned int eh_len = nh - data;
-
- /* The tcp header follows the ip header. */
- struct tcphdr *th = (struct tcphdr *)(nh + ih_len);
- unsigned int th_len = th->doff * 4;
-
- /* The total number of header bytes. */
- /* NOTE: This may be less than skb_headlen(skb). */
- unsigned int sh_len = eh_len + ih_len + th_len;
-
- /* The number of payload bytes at "skb->data + sh_len". */
- /* This is non-zero for sendfile() without HIGHDMA. */
- unsigned int b_len = skb_headlen(skb) - sh_len;
-
- /* The total number of payload bytes. */
- unsigned int d_len = b_len + skb->data_len;
-
- /* The maximum payload size. */
- unsigned int p_len = sh->gso_size;
-
- /* The total number of segments. */
- unsigned int num_segs = sh->gso_segs;
-
- /* The temporary copy of the command. */
- u32 cmd_body[(LEPP_MAX_CMD_SIZE + 3) / 4];
- lepp_tso_cmd_t *cmd = (lepp_tso_cmd_t *)cmd_body;
-
- /* Analyze the "frags". */
- unsigned int num_frags =
- tile_net_tx_frags(cmd->frags, skb, data + sh_len, b_len);
-
- /* The size of the command, including frags and header. */
- size_t cmd_size = LEPP_TSO_CMD_SIZE(num_frags, sh_len);
-
- /* The command header. */
- lepp_tso_cmd_t cmd_init = {
- .tso = true,
- .header_size = sh_len,
- .ip_offset = eh_len,
- .tcp_offset = eh_len + ih_len,
- .payload_size = p_len,
- .num_frags = num_frags,
- };
-
- unsigned long irqflags;
-
- lepp_queue_t *eq = priv->eq;
-
- struct sk_buff *olds[8];
- unsigned int wanted = 8;
- unsigned int i, nolds = 0;
-
- unsigned int cmd_head, cmd_tail, cmd_next;
- unsigned int comp_tail;
-
-
- /* Paranoia. */
- BUG_ON(skb->protocol != htons(ETH_P_IP));
- BUG_ON(ih->protocol != IPPROTO_TCP);
- BUG_ON(skb->ip_summed != CHECKSUM_PARTIAL);
- BUG_ON(num_frags > LEPP_MAX_FRAGS);
- /*--BUG_ON(num_segs != (d_len + (p_len - 1)) / p_len); */
- BUG_ON(num_segs <= 1);
-
-
- /* Finish preparing the command. */
-
- /* Copy the command header. */
- *cmd = cmd_init;
-
- /* Copy the "header". */
- memcpy(&cmd->frags[num_frags], data, sh_len);
-
-
- /* Prefetch and wait, to minimize time spent holding the spinlock. */
- prefetch_L1(&eq->comp_tail);
- prefetch_L1(&eq->cmd_tail);
- mb();
-
-
- /* Enqueue the command. */
-
- spin_lock_irqsave(&priv->eq_lock, irqflags);
-
- /* Handle completions if needed to make room. */
- /* NOTE: Return NETDEV_TX_BUSY if there is still no room. */
- if (lepp_num_free_comp_slots(eq) == 0) {
- nolds = tile_net_lepp_grab_comps(eq, olds, wanted, 0);
- if (nolds == 0) {
-busy:
- spin_unlock_irqrestore(&priv->eq_lock, irqflags);
- return NETDEV_TX_BUSY;
- }
- }
-
- cmd_head = eq->cmd_head;
- cmd_tail = eq->cmd_tail;
-
- /* Prepare to advance, detecting full queue. */
- /* NOTE: Return NETDEV_TX_BUSY if the queue is full. */
- cmd_next = cmd_tail + cmd_size;
- if (cmd_tail < cmd_head && cmd_next >= cmd_head)
- goto busy;
- if (cmd_next > LEPP_CMD_LIMIT) {
- cmd_next = 0;
- if (cmd_next == cmd_head)
- goto busy;
- }
-
- /* Copy the command. */
- memcpy(&eq->cmds[cmd_tail], cmd, cmd_size);
-
- /* Advance. */
- cmd_tail = cmd_next;
-
- /* Record "skb" for eventual freeing. */
- comp_tail = eq->comp_tail;
- eq->comps[comp_tail] = skb;
- LEPP_QINC(comp_tail);
- eq->comp_tail = comp_tail;
-
- /* Flush before allowing LEPP to handle the command. */
- /* ISSUE: Is this the optimal location for the flush? */
- __insn_mf();
-
- eq->cmd_tail = cmd_tail;
-
- /* NOTE: Using "4" here is more efficient than "0" or "2", */
- /* and, strangely, more efficient than pre-checking the number */
- /* of available completions, and comparing it to 4. */
- if (nolds == 0)
- nolds = tile_net_lepp_grab_comps(eq, olds, wanted, 4);
-
- spin_unlock_irqrestore(&priv->eq_lock, irqflags);
-
- /* Handle completions. */
- for (i = 0; i < nolds; i++)
- dev_consume_skb_any(olds[i]);
-
- /* Update stats. */
- u64_stats_update_begin(&stats->syncp);
- stats->tx_packets += num_segs;
- stats->tx_bytes += (num_segs * sh_len) + d_len;
- u64_stats_update_end(&stats->syncp);
-
- /* Make sure the egress timer is scheduled. */
- tile_net_schedule_egress_timer(info);
-
- return NETDEV_TX_OK;
-}
-
-
-/*
- * Transmit a packet (called by the kernel via "hard_start_xmit" hook).
- */
-static int tile_net_tx(struct sk_buff *skb, struct net_device *dev)
-{
- struct tile_net_priv *priv = netdev_priv(dev);
- int my_cpu = smp_processor_id();
- struct tile_net_cpu *info = priv->cpu[my_cpu];
- struct tile_net_stats_t *stats = &info->stats;
-
- unsigned long irqflags;
-
- struct skb_shared_info *sh = skb_shinfo(skb);
-
- unsigned int len = skb->len;
- unsigned char *data = skb->data;
-
- unsigned int csum_start = skb_checksum_start_offset(skb);
-
- lepp_frag_t frags[1 + MAX_SKB_FRAGS];
-
- unsigned int num_frags;
-
- lepp_queue_t *eq = priv->eq;
-
- struct sk_buff *olds[8];
- unsigned int wanted = 8;
- unsigned int i, nolds = 0;
-
- unsigned int cmd_size = sizeof(lepp_cmd_t);
-
- unsigned int cmd_head, cmd_tail, cmd_next;
- unsigned int comp_tail;
-
- lepp_cmd_t cmds[1 + MAX_SKB_FRAGS];
-
-
- /*
- * This is paranoia, since we think that if the link doesn't come
- * up, telling Linux we have no carrier will keep it from trying
- * to transmit. If it does, though, we can't execute this routine,
- * since data structures we depend on aren't set up yet.
- */
- if (!info->registered)
- return NETDEV_TX_BUSY;
-
-
- /* Save the timestamp. */
- netif_trans_update(dev);
-
-
-#ifdef TILE_NET_PARANOIA
-#if CHIP_HAS_CBOX_HOME_MAP()
- if (hash_default) {
- HV_PTE pte = *virt_to_pte(current->mm, (unsigned long)data);
- if (hv_pte_get_mode(pte) != HV_PTE_MODE_CACHE_HASH_L3)
- panic("Non-HFH egress buffer! VA=%p Mode=%d PTE=%llx",
- data, hv_pte_get_mode(pte), hv_pte_val(pte));
- }
-#endif
-#endif
-
-
-#ifdef TILE_NET_DUMP_PACKETS
- /* ISSUE: Does not dump the "frags". */
- dump_packet(data, skb_headlen(skb), "tx");
-#endif /* TILE_NET_DUMP_PACKETS */
-
-
- if (sh->gso_size != 0)
- return tile_net_tx_tso(skb, dev);
-
-
- /* Prepare the commands. */
-
- num_frags = tile_net_tx_frags(frags, skb, data, skb_headlen(skb));
-
- for (i = 0; i < num_frags; i++) {
-
- bool final = (i == num_frags - 1);
-
- lepp_cmd_t cmd = {
- .cpa_lo = frags[i].cpa_lo,
- .cpa_hi = frags[i].cpa_hi,
- .length = frags[i].length,
- .hash_for_home = frags[i].hash_for_home,
- .send_completion = final,
- .end_of_packet = final
- };
-
- if (i == 0 && skb->ip_summed == CHECKSUM_PARTIAL) {
- cmd.compute_checksum = 1;
- cmd.checksum_data.bits.start_byte = csum_start;
- cmd.checksum_data.bits.count = len - csum_start;
- cmd.checksum_data.bits.destination_byte =
- csum_start + skb->csum_offset;
- }
-
- cmds[i] = cmd;
- }
-
-
- /* Prefetch and wait, to minimize time spent holding the spinlock. */
- prefetch_L1(&eq->comp_tail);
- prefetch_L1(&eq->cmd_tail);
- mb();
-
-
- /* Enqueue the commands. */
-
- spin_lock_irqsave(&priv->eq_lock, irqflags);
-
- /* Handle completions if needed to make room. */
- /* NOTE: Return NETDEV_TX_BUSY if there is still no room. */
- if (lepp_num_free_comp_slots(eq) == 0) {
- nolds = tile_net_lepp_grab_comps(eq, olds, wanted, 0);
- if (nolds == 0) {
-busy:
- spin_unlock_irqrestore(&priv->eq_lock, irqflags);
- return NETDEV_TX_BUSY;
- }
- }
-
- cmd_head = eq->cmd_head;
- cmd_tail = eq->cmd_tail;
-
- /* Copy the commands, or fail. */
- /* NOTE: Return NETDEV_TX_BUSY if the queue is full. */
- for (i = 0; i < num_frags; i++) {
-
- /* Prepare to advance, detecting full queue. */
- cmd_next = cmd_tail + cmd_size;
- if (cmd_tail < cmd_head && cmd_next >= cmd_head)
- goto busy;
- if (cmd_next > LEPP_CMD_LIMIT) {
- cmd_next = 0;
- if (cmd_next == cmd_head)
- goto busy;
- }
-
- /* Copy the command. */
- *(lepp_cmd_t *)&eq->cmds[cmd_tail] = cmds[i];
-
- /* Advance. */
- cmd_tail = cmd_next;
- }
-
- /* Record "skb" for eventual freeing. */
- comp_tail = eq->comp_tail;
- eq->comps[comp_tail] = skb;
- LEPP_QINC(comp_tail);
- eq->comp_tail = comp_tail;
-
- /* Flush before allowing LEPP to handle the command. */
- /* ISSUE: Is this the optimal location for the flush? */
- __insn_mf();
-
- eq->cmd_tail = cmd_tail;
-
- /* NOTE: Using "4" here is more efficient than "0" or "2", */
- /* and, strangely, more efficient than pre-checking the number */
- /* of available completions, and comparing it to 4. */
- if (nolds == 0)
- nolds = tile_net_lepp_grab_comps(eq, olds, wanted, 4);
-
- spin_unlock_irqrestore(&priv->eq_lock, irqflags);
-
- /* Handle completions. */
- for (i = 0; i < nolds; i++)
- dev_consume_skb_any(olds[i]);
-
- /* HACK: Track "expanded" size for short packets (e.g. 42 < 60). */
- u64_stats_update_begin(&stats->syncp);
- stats->tx_packets++;
- stats->tx_bytes += ((len >= ETH_ZLEN) ? len : ETH_ZLEN);
- u64_stats_update_end(&stats->syncp);
-
- /* Make sure the egress timer is scheduled. */
- tile_net_schedule_egress_timer(info);
-
- return NETDEV_TX_OK;
-}
-
-
-/*
- * Deal with a transmit timeout.
- */
-static void tile_net_tx_timeout(struct net_device *dev)
-{
- PDEBUG("tile_net_tx_timeout()\n");
- PDEBUG("Transmit timeout at %ld, latency %ld\n", jiffies,
- jiffies - dev_trans_start(dev));
-
- /* XXX: ISSUE: This doesn't seem useful for us. */
- netif_wake_queue(dev);
-}
-
-
-/*
- * Ioctl commands.
- */
-static int tile_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
- return -EOPNOTSUPP;
-}
-
-
-/*
- * Get System Network Statistics.
- *
- * Returns the address of the device statistics structure.
- */
-static void tile_net_get_stats64(struct net_device *dev,
- struct rtnl_link_stats64 *stats)
-{
- struct tile_net_priv *priv = netdev_priv(dev);
- u64 rx_packets = 0, tx_packets = 0;
- u64 rx_bytes = 0, tx_bytes = 0;
- u64 rx_errors = 0, rx_dropped = 0;
- int i;
-
- for_each_online_cpu(i) {
- struct tile_net_stats_t *cpu_stats;
- u64 trx_packets, ttx_packets, trx_bytes, ttx_bytes;
- u64 trx_errors, trx_dropped;
- unsigned int start;
-
- if (priv->cpu[i] == NULL)
- continue;
- cpu_stats = &priv->cpu[i]->stats;
-
- do {
- start = u64_stats_fetch_begin_irq(&cpu_stats->syncp);
- trx_packets = cpu_stats->rx_packets;
- ttx_packets = cpu_stats->tx_packets;
- trx_bytes = cpu_stats->rx_bytes;
- ttx_bytes = cpu_stats->tx_bytes;
- trx_errors = cpu_stats->rx_errors;
- trx_dropped = cpu_stats->rx_dropped;
- } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start));
-
- rx_packets += trx_packets;
- tx_packets += ttx_packets;
- rx_bytes += trx_bytes;
- tx_bytes += ttx_bytes;
- rx_errors += trx_errors;
- rx_dropped += trx_dropped;
- }
-
- stats->rx_packets = rx_packets;
- stats->tx_packets = tx_packets;
- stats->rx_bytes = rx_bytes;
- stats->tx_bytes = tx_bytes;
- stats->rx_errors = rx_errors;
- stats->rx_dropped = rx_dropped;
-}
-
-/*
- * Change the Ethernet Address of the NIC.
- *
- * The hypervisor driver does not support changing MAC address. However,
- * the IPP does not do anything with the MAC address, so the address which
- * gets used on outgoing packets, and which is accepted on incoming packets,
- * is completely up to the NetIO program or kernel driver which is actually
- * handling them.
- *
- * Returns 0 on success, negative on failure.
- */
-static int tile_net_set_mac_address(struct net_device *dev, void *p)
-{
- struct sockaddr *addr = p;
-
- if (!is_valid_ether_addr(addr->sa_data))
- return -EADDRNOTAVAIL;
-
- /* ISSUE: Note that "dev_addr" is now a pointer. */
- memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
-
- return 0;
-}
-
-
-/*
- * Obtain the MAC address from the hypervisor.
- * This must be done before opening the device.
- */
-static int tile_net_get_mac(struct net_device *dev)
-{
- struct tile_net_priv *priv = netdev_priv(dev);
-
- char hv_dev_name[32];
- int len;
-
- __netio_getset_offset_t offset = { .word = NETIO_IPP_PARAM_OFF };
-
- int ret;
-
- /* For example, "xgbe0". */
- strcpy(hv_dev_name, dev->name);
- len = strlen(hv_dev_name);
-
- /* For example, "xgbe/0". */
- hv_dev_name[len] = hv_dev_name[len - 1];
- hv_dev_name[len - 1] = '/';
- len++;
-
- /* For example, "xgbe/0/native_hash". */
- strcpy(hv_dev_name + len, hash_default ? "/native_hash" : "/native");
-
- /* Get the hypervisor handle for this device. */
- priv->hv_devhdl = hv_dev_open((HV_VirtAddr)hv_dev_name, 0);
- PDEBUG("hv_dev_open(%s) returned %d %p\n",
- hv_dev_name, priv->hv_devhdl, &priv->hv_devhdl);
- if (priv->hv_devhdl < 0) {
- if (priv->hv_devhdl == HV_ENODEV)
- printk(KERN_DEBUG "Ignoring unconfigured device %s\n",
- hv_dev_name);
- else
- printk(KERN_DEBUG "hv_dev_open(%s) returned %d\n",
- hv_dev_name, priv->hv_devhdl);
- return -1;
- }
-
- /*
- * Read the hardware address from the hypervisor.
- * ISSUE: Note that "dev_addr" is now a pointer.
- */
- offset.bits.class = NETIO_PARAM;
- offset.bits.addr = NETIO_PARAM_MAC;
- ret = hv_dev_pread(priv->hv_devhdl, 0,
- (HV_VirtAddr)dev->dev_addr, dev->addr_len,
- offset.word);
- PDEBUG("hv_dev_pread(NETIO_PARAM_MAC) returned %d\n", ret);
- if (ret <= 0) {
- printk(KERN_DEBUG "hv_dev_pread(NETIO_PARAM_MAC) %s failed\n",
- dev->name);
- /*
- * Since the device is configured by the hypervisor but we
- * can't get its MAC address, we are most likely running
- * the simulator, so let's generate a random MAC address.
- */
- eth_hw_addr_random(dev);
- }
-
- return 0;
-}
-
-
-#ifdef CONFIG_NET_POLL_CONTROLLER
-/*
- * Polling 'interrupt' - used by things like netconsole to send skbs
- * without having to re-enable interrupts. It's not called while
- * the interrupt routine is executing.
- */
-static void tile_net_netpoll(struct net_device *dev)
-{
- struct tile_net_priv *priv = netdev_priv(dev);
- disable_percpu_irq(priv->intr_id);
- tile_net_handle_ingress_interrupt(priv->intr_id, dev);
- enable_percpu_irq(priv->intr_id, 0);
-}
-#endif
-
-
-static const struct net_device_ops tile_net_ops = {
- .ndo_open = tile_net_open,
- .ndo_stop = tile_net_stop,
- .ndo_start_xmit = tile_net_tx,
- .ndo_do_ioctl = tile_net_ioctl,
- .ndo_get_stats64 = tile_net_get_stats64,
- .ndo_tx_timeout = tile_net_tx_timeout,
- .ndo_set_mac_address = tile_net_set_mac_address,
-#ifdef CONFIG_NET_POLL_CONTROLLER
- .ndo_poll_controller = tile_net_netpoll,
-#endif
-};
-
-
-/*
- * The setup function.
- *
- * This uses ether_setup() to assign various fields in dev, including
- * setting IFF_BROADCAST and IFF_MULTICAST, then sets some extra fields.
- */
-static void tile_net_setup(struct net_device *dev)
-{
- netdev_features_t features = 0;
-
- ether_setup(dev);
- dev->netdev_ops = &tile_net_ops;
- dev->watchdog_timeo = TILE_NET_TIMEOUT;
- dev->tx_queue_len = TILE_NET_TX_QUEUE_LEN;
-
- /* MTU range: 68 - 1500 */
- dev->mtu = TILE_NET_MTU;
- dev->min_mtu = ETH_MIN_MTU;
- dev->max_mtu = TILE_NET_MTU;
-
- features |= NETIF_F_HW_CSUM;
- features |= NETIF_F_SG;
-
- /* We support TSO iff the HV supports sufficient frags. */
- if (LEPP_MAX_FRAGS >= 1 + MAX_SKB_FRAGS)
- features |= NETIF_F_TSO;
-
- /* We can't support HIGHDMA without hash_default, since we need
- * to be able to finv() with a VA if we don't have hash_default.
- */
- if (hash_default)
- features |= NETIF_F_HIGHDMA;
-
- dev->hw_features |= features;
- dev->vlan_features |= features;
- dev->features |= features;
-}
-
-
-/*
- * Allocate the device structure, register the device, and obtain the
- * MAC address from the hypervisor.
- */
-static struct net_device *tile_net_dev_init(const char *name)
-{
- int ret;
- struct net_device *dev;
- struct tile_net_priv *priv;
-
- /*
- * Allocate the device structure. This allocates "priv", calls
- * tile_net_setup(), and saves "name". Normally, "name" is a
- * template, instantiated by register_netdev(), but not for us.
- */
- dev = alloc_netdev(sizeof(*priv), name, NET_NAME_UNKNOWN,
- tile_net_setup);
- if (!dev) {
- pr_err("alloc_netdev(%s) failed\n", name);
- return NULL;
- }
-
- priv = netdev_priv(dev);
-
- /* Initialize "priv". */
-
- memset(priv, 0, sizeof(*priv));
-
- /* Save "dev" for "tile_net_open_retry()". */
- priv->dev = dev;
-
- INIT_DELAYED_WORK(&priv->retry_work, tile_net_open_retry);
-
- spin_lock_init(&priv->eq_lock);
-
- /* Allocate "eq". */
- priv->eq_pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, EQ_ORDER);
- if (!priv->eq_pages) {
- free_netdev(dev);
- return NULL;
- }
- priv->eq = page_address(priv->eq_pages);
-
- /* Register the network device. */
- ret = register_netdev(dev);
- if (ret) {
- pr_err("register_netdev %s failed %d\n", dev->name, ret);
- __free_pages(priv->eq_pages, EQ_ORDER);
- free_netdev(dev);
- return NULL;
- }
-
- /* Get the MAC address. */
- ret = tile_net_get_mac(dev);
- if (ret < 0) {
- unregister_netdev(dev);
- __free_pages(priv->eq_pages, EQ_ORDER);
- free_netdev(dev);
- return NULL;
- }
-
- return dev;
-}
-
-
-/*
- * Module cleanup.
- *
- * FIXME: If compiled as a module, this module cannot be "unloaded",
- * because the "ingress interrupt handler" is registered permanently.
- */
-static void tile_net_cleanup(void)
-{
- int i;
-
- for (i = 0; i < TILE_NET_DEVS; i++) {
- if (tile_net_devs[i]) {
- struct net_device *dev = tile_net_devs[i];
- struct tile_net_priv *priv = netdev_priv(dev);
- unregister_netdev(dev);
- finv_buffer_remote(priv->eq, EQ_SIZE, 0);
- __free_pages(priv->eq_pages, EQ_ORDER);
- free_netdev(dev);
- }
- }
-}
-
-
-/*
- * Module initialization.
- */
-static int tile_net_init_module(void)
-{
- pr_info("Tilera Network Driver\n");
-
- tile_net_devs[0] = tile_net_dev_init("xgbe0");
- tile_net_devs[1] = tile_net_dev_init("xgbe1");
- tile_net_devs[2] = tile_net_dev_init("gbe0");
- tile_net_devs[3] = tile_net_dev_init("gbe1");
-
- return 0;
-}
-
-
-module_init(tile_net_init_module);
-module_exit(tile_net_cleanup);
-
-
-#ifndef MODULE
-
-/*
- * The "network_cpus" boot argument specifies the cpus that are dedicated
- * to handle ingress packets.
- *
- * The parameter should be in the form "network_cpus=m-n[,x-y]", where
- * m, n, x, y are integer numbers that represent the cpus that can be
- * neither a dedicated cpu nor a dataplane cpu.
- */
-static int __init network_cpus_setup(char *str)
-{
- int rc = cpulist_parse_crop(str, &network_cpus_map);
- if (rc != 0) {
- pr_warn("network_cpus=%s: malformed cpu list\n", str);
- } else {
-
- /* Remove dedicated cpus. */
- cpumask_and(&network_cpus_map, &network_cpus_map,
- cpu_possible_mask);
-
-
- if (cpumask_empty(&network_cpus_map)) {
- pr_warn("Ignoring network_cpus='%s'\n", str);
- } else {
- pr_info("Linux network CPUs: %*pbl\n",
- cpumask_pr_args(&network_cpus_map));
- network_cpus_used = true;
- }
- }
-
- return 0;
-}
-__setup("network_cpus=", network_cpus_setup);
-
-#endif
diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c
index 78a6414c5fd9..dfabbae72efd 100644
--- a/drivers/net/hamradio/bpqether.c
+++ b/drivers/net/hamradio/bpqether.c
@@ -590,8 +590,7 @@ static int bpq_device_event(struct notifier_block *this,
static int __init bpq_init_driver(void)
{
#ifdef CONFIG_PROC_FS
- if (!proc_create("bpqether", S_IRUGO, init_net.proc_net,
- &bpq_info_fops)) {
+ if (!proc_create("bpqether", 0444, init_net.proc_net, &bpq_info_fops)) {
printk(KERN_ERR
"bpq: cannot create /proc/net/bpqether entry.\n");
return -ENOENT;
diff --git a/drivers/net/hamradio/yam.c b/drivers/net/hamradio/yam.c
index 14c3632b8cde..83034eb7ed4f 100644
--- a/drivers/net/hamradio/yam.c
+++ b/drivers/net/hamradio/yam.c
@@ -1168,7 +1168,7 @@ static int __init yam_init_driver(void)
yam_timer.expires = jiffies + HZ / 100;
add_timer(&yam_timer);
- proc_create("yam", S_IRUGO, init_net.proc_net, &yam_info_fops);
+ proc_create("yam", 0444, init_net.proc_net, &yam_info_fops);
return 0;
error:
while (--i >= 0) {
diff --git a/drivers/net/hyperv/Makefile b/drivers/net/hyperv/Makefile
index c8a66827100c..3f25b9c8ea59 100644
--- a/drivers/net/hyperv/Makefile
+++ b/drivers/net/hyperv/Makefile
@@ -1,3 +1,3 @@
obj-$(CONFIG_HYPERV_NET) += hv_netvsc.o
-hv_netvsc-y := netvsc_drv.o netvsc.o rndis_filter.o
+hv_netvsc-y := netvsc_drv.o netvsc.o rndis_filter.o netvsc_trace.o
diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h
index 32861036c3fc..960f06141472 100644
--- a/drivers/net/hyperv/hyperv_net.h
+++ b/drivers/net/hyperv/hyperv_net.h
@@ -793,6 +793,7 @@ struct netvsc_device {
/* Receive buffer allocated by us but manages by NetVSP */
void *recv_buf;
+ u32 recv_buf_size; /* allocated bytes */
u32 recv_buf_gpadl_handle;
u32 recv_section_cnt;
u32 recv_section_size;
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
index 7472172823f3..04f611e6f678 100644
--- a/drivers/net/hyperv/netvsc.c
+++ b/drivers/net/hyperv/netvsc.c
@@ -36,6 +36,7 @@
#include <asm/sync_bitops.h>
#include "hyperv_net.h"
+#include "netvsc_trace.h"
/*
* Switch the data path from the synthetic interface to the VF
@@ -57,6 +58,8 @@ void netvsc_switch_datapath(struct net_device *ndev, bool vf)
init_pkt->msg.v4_msg.active_dp.active_datapath =
NVSP_DATAPATH_SYNTHETIC;
+ trace_nvsp_send(ndev, init_pkt);
+
vmbus_sendpacket(dev->channel, init_pkt,
sizeof(struct nvsp_message),
(unsigned long)init_pkt,
@@ -106,11 +109,11 @@ static void free_netvsc_device_rcu(struct netvsc_device *nvdev)
call_rcu(&nvdev->rcu, free_netvsc_device);
}
-static void netvsc_revoke_buf(struct hv_device *device,
- struct netvsc_device *net_device)
+static void netvsc_revoke_recv_buf(struct hv_device *device,
+ struct netvsc_device *net_device,
+ struct net_device *ndev)
{
struct nvsp_message *revoke_packet;
- struct net_device *ndev = hv_get_drvdata(device);
int ret;
/*
@@ -129,6 +132,8 @@ static void netvsc_revoke_buf(struct hv_device *device,
revoke_packet->msg.v1_msg.
revoke_recv_buf.id = NETVSC_RECEIVE_BUFFER_ID;
+ trace_nvsp_send(ndev, revoke_packet);
+
ret = vmbus_sendpacket(device->channel,
revoke_packet,
sizeof(struct nvsp_message),
@@ -152,6 +157,14 @@ static void netvsc_revoke_buf(struct hv_device *device,
}
net_device->recv_section_cnt = 0;
}
+}
+
+static void netvsc_revoke_send_buf(struct hv_device *device,
+ struct netvsc_device *net_device,
+ struct net_device *ndev)
+{
+ struct nvsp_message *revoke_packet;
+ int ret;
/* Deal with the send buffer we may have setup.
* If we got a send section size, it means we received a
@@ -169,6 +182,8 @@ static void netvsc_revoke_buf(struct hv_device *device,
revoke_packet->msg.v1_msg.revoke_send_buf.id =
NETVSC_SEND_BUFFER_ID;
+ trace_nvsp_send(ndev, revoke_packet);
+
ret = vmbus_sendpacket(device->channel,
revoke_packet,
sizeof(struct nvsp_message),
@@ -195,10 +210,10 @@ static void netvsc_revoke_buf(struct hv_device *device,
}
}
-static void netvsc_teardown_gpadl(struct hv_device *device,
- struct netvsc_device *net_device)
+static void netvsc_teardown_recv_gpadl(struct hv_device *device,
+ struct netvsc_device *net_device,
+ struct net_device *ndev)
{
- struct net_device *ndev = hv_get_drvdata(device);
int ret;
if (net_device->recv_buf_gpadl_handle) {
@@ -215,6 +230,13 @@ static void netvsc_teardown_gpadl(struct hv_device *device,
}
net_device->recv_buf_gpadl_handle = 0;
}
+}
+
+static void netvsc_teardown_send_gpadl(struct hv_device *device,
+ struct netvsc_device *net_device,
+ struct net_device *ndev)
+{
+ int ret;
if (net_device->send_buf_gpadl_handle) {
ret = vmbus_teardown_gpadl(device->channel,
@@ -275,6 +297,8 @@ static int netvsc_init_buf(struct hv_device *device,
goto cleanup;
}
+ net_device->recv_buf_size = buf_size;
+
/*
* Establish the gpadl handle for this buffer on this
* channel. Note: This call uses the vmbus connection rather
@@ -298,6 +322,8 @@ static int netvsc_init_buf(struct hv_device *device,
init_packet->msg.v1_msg.
send_recv_buf.id = NETVSC_RECEIVE_BUFFER_ID;
+ trace_nvsp_send(ndev, init_packet);
+
/* Send the gpadl notification request */
ret = vmbus_sendpacket(device->channel, init_packet,
sizeof(struct nvsp_message),
@@ -377,6 +403,8 @@ static int netvsc_init_buf(struct hv_device *device,
net_device->send_buf_gpadl_handle;
init_packet->msg.v1_msg.send_send_buf.id = NETVSC_SEND_BUFFER_ID;
+ trace_nvsp_send(ndev, init_packet);
+
/* Send the gpadl notification request */
ret = vmbus_sendpacket(device->channel, init_packet,
sizeof(struct nvsp_message),
@@ -424,8 +452,10 @@ static int netvsc_init_buf(struct hv_device *device,
goto exit;
cleanup:
- netvsc_revoke_buf(device, net_device);
- netvsc_teardown_gpadl(device, net_device);
+ netvsc_revoke_recv_buf(device, net_device, ndev);
+ netvsc_revoke_send_buf(device, net_device, ndev);
+ netvsc_teardown_recv_gpadl(device, net_device, ndev);
+ netvsc_teardown_send_gpadl(device, net_device, ndev);
exit:
return ret;
@@ -444,6 +474,7 @@ static int negotiate_nvsp_ver(struct hv_device *device,
init_packet->hdr.msg_type = NVSP_MSG_TYPE_INIT;
init_packet->msg.init_msg.init.min_protocol_ver = nvsp_ver;
init_packet->msg.init_msg.init.max_protocol_ver = nvsp_ver;
+ trace_nvsp_send(ndev, init_packet);
/* Send the init request */
ret = vmbus_sendpacket(device->channel, init_packet,
@@ -477,6 +508,8 @@ static int negotiate_nvsp_ver(struct hv_device *device,
init_packet->msg.v2_msg.send_ndis_config.capability.teaming = 1;
}
+ trace_nvsp_send(ndev, init_packet);
+
ret = vmbus_sendpacket(device->channel, init_packet,
sizeof(struct nvsp_message),
(unsigned long)init_packet,
@@ -489,6 +522,7 @@ static int netvsc_connect_vsp(struct hv_device *device,
struct netvsc_device *net_device,
const struct netvsc_device_info *device_info)
{
+ struct net_device *ndev = hv_get_drvdata(device);
static const u32 ver_list[] = {
NVSP_PROTOCOL_VERSION_1, NVSP_PROTOCOL_VERSION_2,
NVSP_PROTOCOL_VERSION_4, NVSP_PROTOCOL_VERSION_5
@@ -529,6 +563,8 @@ static int netvsc_connect_vsp(struct hv_device *device,
send_ndis_ver.ndis_minor_ver =
ndis_version & 0xFFFF;
+ trace_nvsp_send(ndev, init_packet);
+
/* Send the init request */
ret = vmbus_sendpacket(device->channel, init_packet,
sizeof(struct nvsp_message),
@@ -555,7 +591,17 @@ void netvsc_device_remove(struct hv_device *device)
= rtnl_dereference(net_device_ctx->nvdev);
int i;
- netvsc_revoke_buf(device, net_device);
+ /*
+ * Revoke receive buffer. If host is pre-Win2016 then tear down
+ * receive buffer GPADL. Do the same for send buffer.
+ */
+ netvsc_revoke_recv_buf(device, net_device, ndev);
+ if (vmbus_proto_version < VERSION_WIN10)
+ netvsc_teardown_recv_gpadl(device, net_device, ndev);
+
+ netvsc_revoke_send_buf(device, net_device, ndev);
+ if (vmbus_proto_version < VERSION_WIN10)
+ netvsc_teardown_send_gpadl(device, net_device, ndev);
RCU_INIT_POINTER(net_device_ctx->nvdev, NULL);
@@ -569,15 +615,17 @@ void netvsc_device_remove(struct hv_device *device)
*/
netdev_dbg(ndev, "net device safe to remove\n");
- /* older versions require that buffer be revoked before close */
- if (net_device->nvsp_version < NVSP_PROTOCOL_VERSION_4)
- netvsc_teardown_gpadl(device, net_device);
-
/* Now, we can close the channel safely */
vmbus_close(device->channel);
- if (net_device->nvsp_version >= NVSP_PROTOCOL_VERSION_4)
- netvsc_teardown_gpadl(device, net_device);
+ /*
+ * If host is Win2016 or higher then we do the GPADL tear down
+ * here after VMBus is closed.
+ */
+ if (vmbus_proto_version >= VERSION_WIN10) {
+ netvsc_teardown_recv_gpadl(device, net_device, ndev);
+ netvsc_teardown_send_gpadl(device, net_device, ndev);
+ }
/* Release all resources */
free_netvsc_device_rcu(net_device);
@@ -747,7 +795,7 @@ static inline int netvsc_send_pkt(
struct sk_buff *skb)
{
struct nvsp_message nvmsg;
- struct nvsp_1_message_send_rndis_packet * const rpkt =
+ struct nvsp_1_message_send_rndis_packet *rpkt =
&nvmsg.msg.v1_msg.send_rndis_pkt;
struct netvsc_channel * const nvchan =
&net_device->chan_table[packet->q_idx];
@@ -776,6 +824,8 @@ static inline int netvsc_send_pkt(
if (out_channel->rescind)
return -ENODEV;
+ trace_nvsp_send_pkt(ndev, out_channel, rpkt);
+
if (packet->page_buf_cnt) {
if (packet->cp_partial)
pb += packet->rmsg_pgcnt;
@@ -1075,13 +1125,30 @@ static int netvsc_receive(struct net_device *ndev,
/* Each range represents 1 RNDIS pkt that contains 1 ethernet frame */
for (i = 0; i < count; i++) {
- void *data = recv_buf
- + vmxferpage_packet->ranges[i].byte_offset;
+ u32 offset = vmxferpage_packet->ranges[i].byte_offset;
u32 buflen = vmxferpage_packet->ranges[i].byte_count;
+ void *data;
+ int ret;
+
+ if (unlikely(offset + buflen > net_device->recv_buf_size)) {
+ status = NVSP_STAT_FAIL;
+ netif_err(net_device_ctx, rx_err, ndev,
+ "Packet offset:%u + len:%u too big\n",
+ offset, buflen);
+
+ continue;
+ }
+
+ data = recv_buf + offset;
+
+ trace_rndis_recv(ndev, q_idx, data);
/* Pass it to the upper layer */
- status = rndis_filter_receive(ndev, net_device,
- channel, data, buflen);
+ ret = rndis_filter_receive(ndev, net_device,
+ channel, data, buflen);
+
+ if (unlikely(ret != NVSP_STAT_SUCCESS))
+ status = NVSP_STAT_FAIL;
}
enq_receive_complete(ndev, net_device, q_idx,
@@ -1143,6 +1210,8 @@ static int netvsc_process_raw_pkt(struct hv_device *device,
struct net_device_context *net_device_ctx = netdev_priv(ndev);
struct nvsp_message *nvmsg = hv_pkt_data(desc);
+ trace_nvsp_recv(ndev, channel, nvmsg);
+
switch (desc->type) {
case VM_PKT_COMP:
netvsc_send_completion(net_device, channel, device,
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
index f28c85d212ce..ecc84954c511 100644
--- a/drivers/net/hyperv/netvsc_drv.c
+++ b/drivers/net/hyperv/netvsc_drv.c
@@ -55,7 +55,7 @@
#define VF_TAKEOVER_INT (HZ / 10)
static unsigned int ring_size __ro_after_init = 128;
-module_param(ring_size, uint, S_IRUGO);
+module_param(ring_size, uint, 0444);
MODULE_PARM_DESC(ring_size, "Ring buffer size (# of pages)");
unsigned int netvsc_ring_bytes __ro_after_init;
struct reciprocal_value netvsc_ring_reciprocal __ro_after_init;
@@ -66,7 +66,7 @@ static const u32 default_msg = NETIF_MSG_DRV | NETIF_MSG_PROBE |
NETIF_MSG_TX_ERR;
static int debug = -1;
-module_param(debug, int, S_IRUGO);
+module_param(debug, int, 0444);
MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
static void netvsc_change_rx_flags(struct net_device *net, int change)
@@ -831,7 +831,7 @@ int netvsc_recv_callback(struct net_device *net,
u64_stats_update_end(&rx_stats->syncp);
napi_gro_receive(&nvchan->napi, skb);
- return 0;
+ return NVSP_STAT_SUCCESS;
}
static void netvsc_get_drvinfo(struct net_device *net,
diff --git a/drivers/net/hyperv/netvsc_trace.c b/drivers/net/hyperv/netvsc_trace.c
new file mode 100644
index 000000000000..bb0ce5a2bcd5
--- /dev/null
+++ b/drivers/net/hyperv/netvsc_trace.c
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#include <linux/netdevice.h>
+
+#include "hyperv_net.h"
+
+#define CREATE_TRACE_POINTS
+#include "netvsc_trace.h"
diff --git a/drivers/net/hyperv/netvsc_trace.h b/drivers/net/hyperv/netvsc_trace.h
new file mode 100644
index 000000000000..f7585563dea5
--- /dev/null
+++ b/drivers/net/hyperv/netvsc_trace.h
@@ -0,0 +1,182 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#if !defined(_NETVSC_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _NETVSC_TRACE_H
+
+#include <linux/tracepoint.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM netvsc
+#define TRACE_INCLUDE_FILE netvsc_trace
+
+TRACE_DEFINE_ENUM(RNDIS_MSG_PACKET);
+TRACE_DEFINE_ENUM(RNDIS_MSG_INDICATE);
+TRACE_DEFINE_ENUM(RNDIS_MSG_INIT);
+TRACE_DEFINE_ENUM(RNDIS_MSG_INIT_C);
+TRACE_DEFINE_ENUM(RNDIS_MSG_HALT);
+TRACE_DEFINE_ENUM(RNDIS_MSG_QUERY);
+TRACE_DEFINE_ENUM(RNDIS_MSG_QUERY_C);
+TRACE_DEFINE_ENUM(RNDIS_MSG_SET);
+TRACE_DEFINE_ENUM(RNDIS_MSG_SET_C);
+TRACE_DEFINE_ENUM(RNDIS_MSG_RESET);
+TRACE_DEFINE_ENUM(RNDIS_MSG_RESET_C);
+TRACE_DEFINE_ENUM(RNDIS_MSG_KEEPALIVE);
+TRACE_DEFINE_ENUM(RNDIS_MSG_KEEPALIVE_C);
+
+#define show_rndis_type(type) \
+ __print_symbolic(type, \
+ { RNDIS_MSG_PACKET, "PACKET" }, \
+ { RNDIS_MSG_INDICATE, "INDICATE", }, \
+ { RNDIS_MSG_INIT, "INIT", }, \
+ { RNDIS_MSG_INIT_C, "INIT_C", }, \
+ { RNDIS_MSG_HALT, "HALT", }, \
+ { RNDIS_MSG_QUERY, "QUERY", }, \
+ { RNDIS_MSG_QUERY_C, "QUERY_C", }, \
+ { RNDIS_MSG_SET, "SET", }, \
+ { RNDIS_MSG_SET_C, "SET_C", }, \
+ { RNDIS_MSG_RESET, "RESET", }, \
+ { RNDIS_MSG_RESET_C, "RESET_C", }, \
+ { RNDIS_MSG_KEEPALIVE, "KEEPALIVE", }, \
+ { RNDIS_MSG_KEEPALIVE_C, "KEEPALIVE_C", })
+
+DECLARE_EVENT_CLASS(rndis_msg_class,
+ TP_PROTO(const struct net_device *ndev, u16 q,
+ const struct rndis_message *msg),
+ TP_ARGS(ndev, q, msg),
+ TP_STRUCT__entry(
+ __string( name, ndev->name )
+ __field( u16, queue )
+ __field( u32, req_id )
+ __field( u32, msg_type )
+ __field( u32, msg_len )
+ ),
+ TP_fast_assign(
+ __assign_str(name, ndev->name);
+ __entry->queue = q;
+ __entry->req_id = msg->msg.init_req.req_id;
+ __entry->msg_type = msg->ndis_msg_type;
+ __entry->msg_len = msg->msg_len;
+ ),
+ TP_printk("dev=%s q=%u req=%#x type=%s msg_len=%u",
+ __get_str(name), __entry->queue, __entry->req_id,
+ show_rndis_type(__entry->msg_type), __entry->msg_len)
+);
+
+DEFINE_EVENT(rndis_msg_class, rndis_send,
+ TP_PROTO(const struct net_device *ndev, u16 q,
+ const struct rndis_message *msg),
+ TP_ARGS(ndev, q, msg)
+);
+
+DEFINE_EVENT(rndis_msg_class, rndis_recv,
+ TP_PROTO(const struct net_device *ndev, u16 q,
+ const struct rndis_message *msg),
+ TP_ARGS(ndev, q, msg)
+);
+
+TRACE_DEFINE_ENUM(NVSP_MSG_TYPE_INIT);
+TRACE_DEFINE_ENUM(NVSP_MSG_TYPE_INIT_COMPLETE);
+TRACE_DEFINE_ENUM(NVSP_MSG1_TYPE_SEND_NDIS_VER);
+TRACE_DEFINE_ENUM(NVSP_MSG1_TYPE_SEND_RECV_BUF);
+TRACE_DEFINE_ENUM(NVSP_MSG1_TYPE_SEND_RECV_BUF_COMPLETE);
+TRACE_DEFINE_ENUM(NVSP_MSG1_TYPE_REVOKE_RECV_BUF);
+TRACE_DEFINE_ENUM(NVSP_MSG1_TYPE_SEND_SEND_BUF);
+TRACE_DEFINE_ENUM(NVSP_MSG1_TYPE_SEND_SEND_BUF_COMPLETE);
+TRACE_DEFINE_ENUM(NVSP_MSG1_TYPE_REVOKE_SEND_BUF);
+TRACE_DEFINE_ENUM(NVSP_MSG1_TYPE_SEND_RNDIS_PKT);
+TRACE_DEFINE_ENUM(NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE);
+TRACE_DEFINE_ENUM(NVSP_MSG2_TYPE_SEND_NDIS_CONFIG);
+
+TRACE_DEFINE_ENUM(NVSP_MSG4_TYPE_SEND_VF_ASSOCIATION);
+TRACE_DEFINE_ENUM(NVSP_MSG4_TYPE_SWITCH_DATA_PATH);
+
+TRACE_DEFINE_ENUM(NVSP_MSG5_TYPE_SUBCHANNEL);
+TRACE_DEFINE_ENUM(NVSP_MSG5_TYPE_SEND_INDIRECTION_TABLE);
+
+#define show_nvsp_type(type) \
+ __print_symbolic(type, \
+ { NVSP_MSG_TYPE_INIT, "INIT" }, \
+ { NVSP_MSG_TYPE_INIT_COMPLETE, "INIT_COMPLETE" }, \
+ { NVSP_MSG1_TYPE_SEND_NDIS_VER, "SEND_NDIS_VER" }, \
+ { NVSP_MSG1_TYPE_SEND_RECV_BUF, "SEND_RECV_BUF" }, \
+ { NVSP_MSG1_TYPE_SEND_RECV_BUF_COMPLETE, "SEND_RECV_BUF_COMPLETE" }, \
+ { NVSP_MSG1_TYPE_REVOKE_RECV_BUF, "REVOKE_RECV_BUF" }, \
+ { NVSP_MSG1_TYPE_SEND_SEND_BUF, "SEND_SEND_BUF" }, \
+ { NVSP_MSG1_TYPE_SEND_SEND_BUF_COMPLETE, "SEND_SEND_BUF_COMPLETE" }, \
+ { NVSP_MSG1_TYPE_REVOKE_SEND_BUF, "REVOKE_SEND_BUF" }, \
+ { NVSP_MSG1_TYPE_SEND_RNDIS_PKT, "SEND_RNDIS_PKT" }, \
+ { NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE, "SEND_RNDIS_PKT_COMPLETE" },\
+ { NVSP_MSG2_TYPE_SEND_NDIS_CONFIG, "SEND_NDIS_CONFIG" }, \
+ { NVSP_MSG4_TYPE_SEND_VF_ASSOCIATION, "SEND_VF_ASSOCIATION" }, \
+ { NVSP_MSG4_TYPE_SWITCH_DATA_PATH, "SWITCH_DATA_PATH" }, \
+ { NVSP_MSG5_TYPE_SUBCHANNEL, "SUBCHANNEL" }, \
+ { NVSP_MSG5_TYPE_SEND_INDIRECTION_TABLE, "SEND_INDIRECTION_TABLE" })
+
+TRACE_EVENT(nvsp_send,
+ TP_PROTO(const struct net_device *ndev,
+ const struct nvsp_message *msg),
+ TP_ARGS(ndev, msg),
+ TP_STRUCT__entry(
+ __string( name, ndev->name )
+ __field( u32, msg_type )
+ ),
+ TP_fast_assign(
+ __assign_str(name, ndev->name);
+ __entry->msg_type = msg->hdr.msg_type;
+ ),
+ TP_printk("dev=%s type=%s",
+ __get_str(name),
+ show_nvsp_type(__entry->msg_type))
+);
+
+TRACE_EVENT(nvsp_send_pkt,
+ TP_PROTO(const struct net_device *ndev,
+ const struct vmbus_channel *chan,
+ const struct nvsp_1_message_send_rndis_packet *rpkt),
+ TP_ARGS(ndev, chan, rpkt),
+ TP_STRUCT__entry(
+ __string( name, ndev->name )
+ __field( u16, qid )
+ __field( u32, channel_type )
+ __field( u32, section_index )
+ __field( u32, section_size )
+ ),
+ TP_fast_assign(
+ __assign_str(name, ndev->name);
+ __entry->qid = chan->offermsg.offer.sub_channel_index;
+ __entry->channel_type = rpkt->channel_type;
+ __entry->section_index = rpkt->send_buf_section_index;
+ __entry->section_size = rpkt->send_buf_section_size;
+ ),
+ TP_printk("dev=%s qid=%u type=%s section=%u size=%d",
+ __get_str(name), __entry->qid,
+ __entry->channel_type ? "CONTROL" : "DATA",
+ __entry->section_index, __entry->section_size)
+);
+
+TRACE_EVENT(nvsp_recv,
+ TP_PROTO(const struct net_device *ndev,
+ const struct vmbus_channel *chan,
+ const struct nvsp_message *msg),
+ TP_ARGS(ndev, chan, msg),
+ TP_STRUCT__entry(
+ __string( name, ndev->name )
+ __field( u16, qid )
+ __field( u32, msg_type )
+ ),
+ TP_fast_assign(
+ __assign_str(name, ndev->name);
+ __entry->qid = chan->offermsg.offer.sub_channel_index;
+ __entry->msg_type = msg->hdr.msg_type;
+ ),
+ TP_printk("dev=%s qid=%u type=%s",
+ __get_str(name), __entry->qid,
+ show_nvsp_type(__entry->msg_type))
+);
+
+#endif /* _NETVSC_TRACE_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH ../../drivers/net/hyperv
+#include <trace/define_trace.h>
diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c
index a6ec41c399d6..6b127be781d9 100644
--- a/drivers/net/hyperv/rndis_filter.c
+++ b/drivers/net/hyperv/rndis_filter.c
@@ -31,6 +31,7 @@
#include <linux/rtnetlink.h>
#include "hyperv_net.h"
+#include "netvsc_trace.h"
static void rndis_set_multicast(struct work_struct *w);
@@ -241,6 +242,8 @@ static int rndis_filter_send_request(struct rndis_device *dev,
pb[0].len;
}
+ trace_rndis_send(dev->ndev, 0, &req->request_msg);
+
rcu_read_lock_bh();
ret = netvsc_send(dev->ndev, packet, NULL, pb, NULL);
rcu_read_unlock_bh();
@@ -362,14 +365,15 @@ static inline void *rndis_get_ppi(struct rndis_packet *rpkt, u32 type)
static int rndis_filter_receive_data(struct net_device *ndev,
struct netvsc_device *nvdev,
- struct rndis_message *msg,
struct vmbus_channel *channel,
- void *data, u32 data_buflen)
+ struct rndis_message *msg,
+ u32 data_buflen)
{
struct rndis_packet *rndis_pkt = &msg->msg.pkt;
const struct ndis_tcp_ip_checksum_info *csum_info;
const struct ndis_pkt_8021q_info *vlan;
u32 data_offset;
+ void *data;
/* Remove the rndis header and pass it back up the stack */
data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset;
@@ -390,14 +394,15 @@ static int rndis_filter_receive_data(struct net_device *ndev,
vlan = rndis_get_ppi(rndis_pkt, IEEE_8021Q_INFO);
+ csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO);
+
+ data = (void *)msg + data_offset;
+
/*
* Remove the rndis trailer padding from rndis packet message
* rndis_pkt->data_len tell us the real data length, we only copy
* the data packet to the stack, without the rndis trailer padding
*/
- data = (void *)((unsigned long)data + data_offset);
- csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO);
-
return netvsc_recv_callback(ndev, nvdev, channel,
data, rndis_pkt->data_len,
csum_info, vlan);
@@ -416,8 +421,8 @@ int rndis_filter_receive(struct net_device *ndev,
switch (rndis_msg->ndis_msg_type) {
case RNDIS_MSG_PACKET:
- return rndis_filter_receive_data(ndev, net_dev, rndis_msg,
- channel, data, buflen);
+ return rndis_filter_receive_data(ndev, net_dev, channel,
+ rndis_msg, buflen);
case RNDIS_MSG_INIT_C:
case RNDIS_MSG_QUERY_C:
case RNDIS_MSG_SET_C:
@@ -434,10 +439,10 @@ int rndis_filter_receive(struct net_device *ndev,
"unhandled rndis message (type %u len %u)\n",
rndis_msg->ndis_msg_type,
rndis_msg->msg_len);
- break;
+ return NVSP_STAT_FAIL;
}
- return 0;
+ return NVSP_STAT_SUCCESS;
}
static int rndis_filter_query_device(struct rndis_device *dev,
@@ -858,7 +863,7 @@ static void rndis_set_multicast(struct work_struct *w)
if (flags & IFF_PROMISC) {
filter = NDIS_PACKET_TYPE_PROMISCUOUS;
} else {
- if (flags & IFF_ALLMULTI)
+ if (!netdev_mc_empty(rdev->ndev) || (flags & IFF_ALLMULTI))
filter |= NDIS_PACKET_TYPE_ALL_MULTICAST;
if (flags & IFF_BROADCAST)
filter |= NDIS_PACKET_TYPE_BROADCAST;
@@ -942,12 +947,11 @@ static bool netvsc_device_idle(const struct netvsc_device *nvdev)
return true;
}
-static void rndis_filter_halt_device(struct rndis_device *dev)
+static void rndis_filter_halt_device(struct netvsc_device *nvdev,
+ struct rndis_device *dev)
{
struct rndis_request *request;
struct rndis_halt_request *halt;
- struct net_device_context *net_device_ctx = netdev_priv(dev->ndev);
- struct netvsc_device *nvdev = rtnl_dereference(net_device_ctx->nvdev);
/* Attempt to do a rndis device halt */
request = get_rndis_request(dev, RNDIS_MSG_HALT,
@@ -1086,6 +1090,8 @@ void rndis_set_subchannel(struct work_struct *w)
init_packet->msg.v5_msg.subchn_req.op = NVSP_SUBCHANNEL_ALLOCATE;
init_packet->msg.v5_msg.subchn_req.num_subchannels =
nvdev->num_chn - 1;
+ trace_nvsp_send(ndev, init_packet);
+
ret = vmbus_sendpacket(hv_dev->channel, init_packet,
sizeof(struct nvsp_message),
(unsigned long)init_packet,
@@ -1350,7 +1356,7 @@ void rndis_filter_device_remove(struct hv_device *dev,
struct rndis_device *rndis_dev = net_dev->extension;
/* Halt and release the rndis device */
- rndis_filter_halt_device(rndis_dev);
+ rndis_filter_halt_device(net_dev, rndis_dev);
net_dev->extension = NULL;
diff --git a/drivers/net/ieee802154/Kconfig b/drivers/net/ieee802154/Kconfig
index 303ba4133920..8782f5655e3f 100644
--- a/drivers/net/ieee802154/Kconfig
+++ b/drivers/net/ieee802154/Kconfig
@@ -104,3 +104,14 @@ config IEEE802154_CA8210_DEBUGFS
exposes a debugfs node for each CA8210 instance which allows
direct use of the Cascoda API, exposing the 802.15.4 MAC
management entities.
+
+config IEEE802154_MCR20A
+ tristate "MCR20A transceiver driver"
+ depends on IEEE802154_DRIVERS && MAC802154
+ depends on SPI
+ ---help---
+ Say Y here to enable the MCR20A SPI 802.15.4 wireless
+ controller.
+
+ This driver can also be built as a module. To do so, say M here.
+ the module will be called 'mcr20a'.
diff --git a/drivers/net/ieee802154/Makefile b/drivers/net/ieee802154/Makefile
index bea1de5e726c..104744d5a668 100644
--- a/drivers/net/ieee802154/Makefile
+++ b/drivers/net/ieee802154/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_IEEE802154_CC2520) += cc2520.o
obj-$(CONFIG_IEEE802154_ATUSB) += atusb.o
obj-$(CONFIG_IEEE802154_ADF7242) += adf7242.o
obj-$(CONFIG_IEEE802154_CA8210) += ca8210.o
+obj-$(CONFIG_IEEE802154_MCR20A) += mcr20a.o
diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c
index 548d9d026a85..77abedf0b524 100644
--- a/drivers/net/ieee802154/at86rf230.c
+++ b/drivers/net/ieee802154/at86rf230.c
@@ -1661,7 +1661,7 @@ static int at86rf230_debugfs_init(struct at86rf230_local *lp)
if (!at86rf230_debugfs_root)
return -ENOMEM;
- stats = debugfs_create_file("trac_stats", S_IRUGO,
+ stats = debugfs_create_file("trac_stats", 0444,
at86rf230_debugfs_root, lp,
&at86rf230_stats_fops);
if (!stats)
diff --git a/drivers/net/ieee802154/ca8210.c b/drivers/net/ieee802154/ca8210.c
index 377af43b81b3..58299fb666ed 100644
--- a/drivers/net/ieee802154/ca8210.c
+++ b/drivers/net/ieee802154/ca8210.c
@@ -2493,13 +2493,14 @@ static ssize_t ca8210_test_int_user_write(
struct ca8210_priv *priv = filp->private_data;
u8 command[CA8210_SPI_BUF_SIZE];
- if (len > CA8210_SPI_BUF_SIZE) {
+ memset(command, SPI_IDLE, 6);
+ if (len > CA8210_SPI_BUF_SIZE || len < 2) {
dev_warn(
&priv->spi->dev,
- "userspace requested erroneously long write (%zu)\n",
+ "userspace requested erroneous write length (%zu)\n",
len
);
- return -EMSGSIZE;
+ return -EBADE;
}
ret = copy_from_user(command, in_buf, len);
@@ -2511,6 +2512,13 @@ static ssize_t ca8210_test_int_user_write(
);
return -EIO;
}
+ if (len != command[1] + 2) {
+ dev_err(
+ &priv->spi->dev,
+ "write len does not match packet length field\n"
+ );
+ return -EBADE;
+ }
ret = ca8210_test_check_upstream(command, priv->spi);
if (ret == 0) {
diff --git a/drivers/net/ieee802154/mcr20a.c b/drivers/net/ieee802154/mcr20a.c
new file mode 100644
index 000000000000..55a22c761808
--- /dev/null
+++ b/drivers/net/ieee802154/mcr20a.c
@@ -0,0 +1,1411 @@
+/*
+ * Driver for NXP MCR20A 802.15.4 Wireless-PAN Networking controller
+ *
+ * Copyright (C) 2018 Xue Liu <liuxuenetmail@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.
+ *
+ * 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.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/skbuff.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/ieee802154.h>
+#include <linux/debugfs.h>
+
+#include <net/mac802154.h>
+#include <net/cfg802154.h>
+
+#include <linux/device.h>
+
+#include "mcr20a.h"
+
+#define SPI_COMMAND_BUFFER 3
+
+#define REGISTER_READ BIT(7)
+#define REGISTER_WRITE (0 << 7)
+#define REGISTER_ACCESS (0 << 6)
+#define PACKET_BUFF_BURST_ACCESS BIT(6)
+#define PACKET_BUFF_BYTE_ACCESS BIT(5)
+
+#define MCR20A_WRITE_REG(x) (x)
+#define MCR20A_READ_REG(x) (REGISTER_READ | (x))
+#define MCR20A_BURST_READ_PACKET_BUF (0xC0)
+#define MCR20A_BURST_WRITE_PACKET_BUF (0x40)
+
+#define MCR20A_CMD_REG 0x80
+#define MCR20A_CMD_REG_MASK 0x3f
+#define MCR20A_CMD_WRITE 0x40
+#define MCR20A_CMD_FB 0x20
+
+/* Number of Interrupt Request Status Register */
+#define MCR20A_IRQSTS_NUM 2 /* only IRQ_STS1 and IRQ_STS2 */
+
+/* MCR20A CCA Type */
+enum {
+ MCR20A_CCA_ED, // energy detect - CCA bit not active,
+ // not to be used for T and CCCA sequences
+ MCR20A_CCA_MODE1, // energy detect - CCA bit ACTIVE
+ MCR20A_CCA_MODE2, // 802.15.4 compliant signal detect - CCA bit ACTIVE
+ MCR20A_CCA_MODE3
+};
+
+enum {
+ MCR20A_XCVSEQ_IDLE = 0x00,
+ MCR20A_XCVSEQ_RX = 0x01,
+ MCR20A_XCVSEQ_TX = 0x02,
+ MCR20A_XCVSEQ_CCA = 0x03,
+ MCR20A_XCVSEQ_TR = 0x04,
+ MCR20A_XCVSEQ_CCCA = 0x05,
+};
+
+/* IEEE-802.15.4 defined constants (2.4 GHz logical channels) */
+#define MCR20A_MIN_CHANNEL (11)
+#define MCR20A_MAX_CHANNEL (26)
+#define MCR20A_CHANNEL_SPACING (5)
+
+/* MCR20A CCA Threshold constans */
+#define MCR20A_MIN_CCA_THRESHOLD (0x6EU)
+#define MCR20A_MAX_CCA_THRESHOLD (0x00U)
+
+/* version 0C */
+#define MCR20A_OVERWRITE_VERSION (0x0C)
+
+/* MCR20A PLL configurations */
+static const u8 PLL_INT[16] = {
+ /* 2405 */ 0x0B, /* 2410 */ 0x0B, /* 2415 */ 0x0B,
+ /* 2420 */ 0x0B, /* 2425 */ 0x0B, /* 2430 */ 0x0B,
+ /* 2435 */ 0x0C, /* 2440 */ 0x0C, /* 2445 */ 0x0C,
+ /* 2450 */ 0x0C, /* 2455 */ 0x0C, /* 2460 */ 0x0C,
+ /* 2465 */ 0x0D, /* 2470 */ 0x0D, /* 2475 */ 0x0D,
+ /* 2480 */ 0x0D
+};
+
+static const u8 PLL_FRAC[16] = {
+ /* 2405 */ 0x28, /* 2410 */ 0x50, /* 2415 */ 0x78,
+ /* 2420 */ 0xA0, /* 2425 */ 0xC8, /* 2430 */ 0xF0,
+ /* 2435 */ 0x18, /* 2440 */ 0x40, /* 2445 */ 0x68,
+ /* 2450 */ 0x90, /* 2455 */ 0xB8, /* 2460 */ 0xE0,
+ /* 2465 */ 0x08, /* 2470 */ 0x30, /* 2475 */ 0x58,
+ /* 2480 */ 0x80
+};
+
+static const struct reg_sequence mar20a_iar_overwrites[] = {
+ { IAR_MISC_PAD_CTRL, 0x02 },
+ { IAR_VCO_CTRL1, 0xB3 },
+ { IAR_VCO_CTRL2, 0x07 },
+ { IAR_PA_TUNING, 0x71 },
+ { IAR_CHF_IBUF, 0x2F },
+ { IAR_CHF_QBUF, 0x2F },
+ { IAR_CHF_IRIN, 0x24 },
+ { IAR_CHF_QRIN, 0x24 },
+ { IAR_CHF_IL, 0x24 },
+ { IAR_CHF_QL, 0x24 },
+ { IAR_CHF_CC1, 0x32 },
+ { IAR_CHF_CCL, 0x1D },
+ { IAR_CHF_CC2, 0x2D },
+ { IAR_CHF_IROUT, 0x24 },
+ { IAR_CHF_QROUT, 0x24 },
+ { IAR_PA_CAL, 0x28 },
+ { IAR_AGC_THR1, 0x55 },
+ { IAR_AGC_THR2, 0x2D },
+ { IAR_ATT_RSSI1, 0x5F },
+ { IAR_ATT_RSSI2, 0x8F },
+ { IAR_RSSI_OFFSET, 0x61 },
+ { IAR_CHF_PMA_GAIN, 0x03 },
+ { IAR_CCA1_THRESH, 0x50 },
+ { IAR_CORR_NVAL, 0x13 },
+ { IAR_ACKDELAY, 0x3D },
+};
+
+#define MCR20A_VALID_CHANNELS (0x07FFF800)
+
+struct mcr20a_platform_data {
+ int rst_gpio;
+};
+
+#define MCR20A_MAX_BUF (127)
+
+#define printdev(X) (&X->spi->dev)
+
+/* regmap information for Direct Access Register (DAR) access */
+#define MCR20A_DAR_WRITE 0x01
+#define MCR20A_DAR_READ 0x00
+#define MCR20A_DAR_NUMREGS 0x3F
+
+/* regmap information for Indirect Access Register (IAR) access */
+#define MCR20A_IAR_ACCESS 0x80
+#define MCR20A_IAR_NUMREGS 0xBEFF
+
+/* Read/Write SPI Commands for DAR and IAR registers. */
+#define MCR20A_READSHORT(reg) ((reg) << 1)
+#define MCR20A_WRITESHORT(reg) ((reg) << 1 | 1)
+#define MCR20A_READLONG(reg) (1 << 15 | (reg) << 5)
+#define MCR20A_WRITELONG(reg) (1 << 15 | (reg) << 5 | 1 << 4)
+
+/* Type definitions for link configuration of instantiable layers */
+#define MCR20A_PHY_INDIRECT_QUEUE_SIZE (12)
+
+static bool
+mcr20a_dar_writeable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case DAR_IRQ_STS1:
+ case DAR_IRQ_STS2:
+ case DAR_IRQ_STS3:
+ case DAR_PHY_CTRL1:
+ case DAR_PHY_CTRL2:
+ case DAR_PHY_CTRL3:
+ case DAR_PHY_CTRL4:
+ case DAR_SRC_CTRL:
+ case DAR_SRC_ADDRS_SUM_LSB:
+ case DAR_SRC_ADDRS_SUM_MSB:
+ case DAR_T3CMP_LSB:
+ case DAR_T3CMP_MSB:
+ case DAR_T3CMP_USB:
+ case DAR_T2PRIMECMP_LSB:
+ case DAR_T2PRIMECMP_MSB:
+ case DAR_T1CMP_LSB:
+ case DAR_T1CMP_MSB:
+ case DAR_T1CMP_USB:
+ case DAR_T2CMP_LSB:
+ case DAR_T2CMP_MSB:
+ case DAR_T2CMP_USB:
+ case DAR_T4CMP_LSB:
+ case DAR_T4CMP_MSB:
+ case DAR_T4CMP_USB:
+ case DAR_PLL_INT0:
+ case DAR_PLL_FRAC0_LSB:
+ case DAR_PLL_FRAC0_MSB:
+ case DAR_PA_PWR:
+ /* no DAR_ACM */
+ case DAR_OVERWRITE_VER:
+ case DAR_CLK_OUT_CTRL:
+ case DAR_PWR_MODES:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool
+mcr20a_dar_readable(struct device *dev, unsigned int reg)
+{
+ bool rc;
+
+ /* all writeable are also readable */
+ rc = mcr20a_dar_writeable(dev, reg);
+ if (rc)
+ return rc;
+
+ /* readonly regs */
+ switch (reg) {
+ case DAR_RX_FRM_LEN:
+ case DAR_CCA1_ED_FNL:
+ case DAR_EVENT_TMR_LSB:
+ case DAR_EVENT_TMR_MSB:
+ case DAR_EVENT_TMR_USB:
+ case DAR_TIMESTAMP_LSB:
+ case DAR_TIMESTAMP_MSB:
+ case DAR_TIMESTAMP_USB:
+ case DAR_SEQ_STATE:
+ case DAR_LQI_VALUE:
+ case DAR_RSSI_CCA_CONT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool
+mcr20a_dar_volatile(struct device *dev, unsigned int reg)
+{
+ /* can be changed during runtime */
+ switch (reg) {
+ case DAR_IRQ_STS1:
+ case DAR_IRQ_STS2:
+ case DAR_IRQ_STS3:
+ /* use them in spi_async and regmap so it's volatile */
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool
+mcr20a_dar_precious(struct device *dev, unsigned int reg)
+{
+ /* don't clear irq line on read */
+ switch (reg) {
+ case DAR_IRQ_STS1:
+ case DAR_IRQ_STS2:
+ case DAR_IRQ_STS3:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config mcr20a_dar_regmap = {
+ .name = "mcr20a_dar",
+ .reg_bits = 8,
+ .val_bits = 8,
+ .write_flag_mask = REGISTER_ACCESS | REGISTER_WRITE,
+ .read_flag_mask = REGISTER_ACCESS | REGISTER_READ,
+ .cache_type = REGCACHE_RBTREE,
+ .writeable_reg = mcr20a_dar_writeable,
+ .readable_reg = mcr20a_dar_readable,
+ .volatile_reg = mcr20a_dar_volatile,
+ .precious_reg = mcr20a_dar_precious,
+ .fast_io = true,
+ .can_multi_write = true,
+};
+
+static bool
+mcr20a_iar_writeable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case IAR_XTAL_TRIM:
+ case IAR_PMC_LP_TRIM:
+ case IAR_MACPANID0_LSB:
+ case IAR_MACPANID0_MSB:
+ case IAR_MACSHORTADDRS0_LSB:
+ case IAR_MACSHORTADDRS0_MSB:
+ case IAR_MACLONGADDRS0_0:
+ case IAR_MACLONGADDRS0_8:
+ case IAR_MACLONGADDRS0_16:
+ case IAR_MACLONGADDRS0_24:
+ case IAR_MACLONGADDRS0_32:
+ case IAR_MACLONGADDRS0_40:
+ case IAR_MACLONGADDRS0_48:
+ case IAR_MACLONGADDRS0_56:
+ case IAR_RX_FRAME_FILTER:
+ case IAR_PLL_INT1:
+ case IAR_PLL_FRAC1_LSB:
+ case IAR_PLL_FRAC1_MSB:
+ case IAR_MACPANID1_LSB:
+ case IAR_MACPANID1_MSB:
+ case IAR_MACSHORTADDRS1_LSB:
+ case IAR_MACSHORTADDRS1_MSB:
+ case IAR_MACLONGADDRS1_0:
+ case IAR_MACLONGADDRS1_8:
+ case IAR_MACLONGADDRS1_16:
+ case IAR_MACLONGADDRS1_24:
+ case IAR_MACLONGADDRS1_32:
+ case IAR_MACLONGADDRS1_40:
+ case IAR_MACLONGADDRS1_48:
+ case IAR_MACLONGADDRS1_56:
+ case IAR_DUAL_PAN_CTRL:
+ case IAR_DUAL_PAN_DWELL:
+ case IAR_CCA1_THRESH:
+ case IAR_CCA1_ED_OFFSET_COMP:
+ case IAR_LQI_OFFSET_COMP:
+ case IAR_CCA_CTRL:
+ case IAR_CCA2_CORR_PEAKS:
+ case IAR_CCA2_CORR_THRESH:
+ case IAR_TMR_PRESCALE:
+ case IAR_ANT_PAD_CTRL:
+ case IAR_MISC_PAD_CTRL:
+ case IAR_BSM_CTRL:
+ case IAR_RNG:
+ case IAR_RX_WTR_MARK:
+ case IAR_SOFT_RESET:
+ case IAR_TXDELAY:
+ case IAR_ACKDELAY:
+ case IAR_CORR_NVAL:
+ case IAR_ANT_AGC_CTRL:
+ case IAR_AGC_THR1:
+ case IAR_AGC_THR2:
+ case IAR_PA_CAL:
+ case IAR_ATT_RSSI1:
+ case IAR_ATT_RSSI2:
+ case IAR_RSSI_OFFSET:
+ case IAR_XTAL_CTRL:
+ case IAR_CHF_PMA_GAIN:
+ case IAR_CHF_IBUF:
+ case IAR_CHF_QBUF:
+ case IAR_CHF_IRIN:
+ case IAR_CHF_QRIN:
+ case IAR_CHF_IL:
+ case IAR_CHF_QL:
+ case IAR_CHF_CC1:
+ case IAR_CHF_CCL:
+ case IAR_CHF_CC2:
+ case IAR_CHF_IROUT:
+ case IAR_CHF_QROUT:
+ case IAR_PA_TUNING:
+ case IAR_VCO_CTRL1:
+ case IAR_VCO_CTRL2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool
+mcr20a_iar_readable(struct device *dev, unsigned int reg)
+{
+ bool rc;
+
+ /* all writeable are also readable */
+ rc = mcr20a_iar_writeable(dev, reg);
+ if (rc)
+ return rc;
+
+ /* readonly regs */
+ switch (reg) {
+ case IAR_PART_ID:
+ case IAR_DUAL_PAN_STS:
+ case IAR_RX_BYTE_COUNT:
+ case IAR_FILTERFAIL_CODE1:
+ case IAR_FILTERFAIL_CODE2:
+ case IAR_RSSI:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool
+mcr20a_iar_volatile(struct device *dev, unsigned int reg)
+{
+/* can be changed during runtime */
+ switch (reg) {
+ case IAR_DUAL_PAN_STS:
+ case IAR_RX_BYTE_COUNT:
+ case IAR_FILTERFAIL_CODE1:
+ case IAR_FILTERFAIL_CODE2:
+ case IAR_RSSI:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config mcr20a_iar_regmap = {
+ .name = "mcr20a_iar",
+ .reg_bits = 16,
+ .val_bits = 8,
+ .write_flag_mask = REGISTER_ACCESS | REGISTER_WRITE | IAR_INDEX,
+ .read_flag_mask = REGISTER_ACCESS | REGISTER_READ | IAR_INDEX,
+ .cache_type = REGCACHE_RBTREE,
+ .writeable_reg = mcr20a_iar_writeable,
+ .readable_reg = mcr20a_iar_readable,
+ .volatile_reg = mcr20a_iar_volatile,
+ .fast_io = true,
+};
+
+struct mcr20a_local {
+ struct spi_device *spi;
+
+ struct ieee802154_hw *hw;
+ struct mcr20a_platform_data *pdata;
+ struct regmap *regmap_dar;
+ struct regmap *regmap_iar;
+
+ u8 *buf;
+
+ bool is_tx;
+
+ /* for writing tx buffer */
+ struct spi_message tx_buf_msg;
+ u8 tx_header[1];
+ /* burst buffer write command */
+ struct spi_transfer tx_xfer_header;
+ u8 tx_len[1];
+ /* len of tx packet */
+ struct spi_transfer tx_xfer_len;
+ /* data of tx packet */
+ struct spi_transfer tx_xfer_buf;
+ struct sk_buff *tx_skb;
+
+ /* for read length rxfifo */
+ struct spi_message reg_msg;
+ u8 reg_cmd[1];
+ u8 reg_data[MCR20A_IRQSTS_NUM];
+ struct spi_transfer reg_xfer_cmd;
+ struct spi_transfer reg_xfer_data;
+
+ /* receive handling */
+ struct spi_message rx_buf_msg;
+ u8 rx_header[1];
+ struct spi_transfer rx_xfer_header;
+ u8 rx_lqi[1];
+ struct spi_transfer rx_xfer_lqi;
+ u8 rx_buf[MCR20A_MAX_BUF];
+ struct spi_transfer rx_xfer_buf;
+
+ /* isr handling for reading intstat */
+ struct spi_message irq_msg;
+ u8 irq_header[1];
+ u8 irq_data[MCR20A_IRQSTS_NUM];
+ struct spi_transfer irq_xfer_data;
+ struct spi_transfer irq_xfer_header;
+};
+
+static void
+mcr20a_write_tx_buf_complete(void *context)
+{
+ struct mcr20a_local *lp = context;
+ int ret;
+
+ dev_dbg(printdev(lp), "%s\n", __func__);
+
+ lp->reg_msg.complete = NULL;
+ lp->reg_cmd[0] = MCR20A_WRITE_REG(DAR_PHY_CTRL1);
+ lp->reg_data[0] = MCR20A_XCVSEQ_TX;
+ lp->reg_xfer_data.len = 1;
+
+ ret = spi_async(lp->spi, &lp->reg_msg);
+ if (ret)
+ dev_err(printdev(lp), "failed to set SEQ TX\n");
+}
+
+static int
+mcr20a_xmit(struct ieee802154_hw *hw, struct sk_buff *skb)
+{
+ struct mcr20a_local *lp = hw->priv;
+
+ dev_dbg(printdev(lp), "%s\n", __func__);
+
+ lp->tx_skb = skb;
+
+ print_hex_dump_debug("mcr20a tx: ", DUMP_PREFIX_OFFSET, 16, 1,
+ skb->data, skb->len, 0);
+
+ lp->is_tx = 1;
+
+ lp->reg_msg.complete = NULL;
+ lp->reg_cmd[0] = MCR20A_WRITE_REG(DAR_PHY_CTRL1);
+ lp->reg_data[0] = MCR20A_XCVSEQ_IDLE;
+ lp->reg_xfer_data.len = 1;
+
+ return spi_async(lp->spi, &lp->reg_msg);
+}
+
+static int
+mcr20a_ed(struct ieee802154_hw *hw, u8 *level)
+{
+ WARN_ON(!level);
+ *level = 0xbe;
+ return 0;
+}
+
+static int
+mcr20a_set_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
+{
+ struct mcr20a_local *lp = hw->priv;
+ int ret;
+
+ dev_dbg(printdev(lp), "%s\n", __func__);
+
+ /* freqency = ((PLL_INT+64) + (PLL_FRAC/65536)) * 32 MHz */
+ ret = regmap_write(lp->regmap_dar, DAR_PLL_INT0, PLL_INT[channel - 11]);
+ if (ret)
+ return ret;
+ ret = regmap_write(lp->regmap_dar, DAR_PLL_FRAC0_LSB, 0x00);
+ if (ret)
+ return ret;
+ ret = regmap_write(lp->regmap_dar, DAR_PLL_FRAC0_MSB,
+ PLL_FRAC[channel - 11]);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int
+mcr20a_start(struct ieee802154_hw *hw)
+{
+ struct mcr20a_local *lp = hw->priv;
+ int ret;
+
+ dev_dbg(printdev(lp), "%s\n", __func__);
+
+ /* No slotted operation */
+ dev_dbg(printdev(lp), "no slotted operation\n");
+ ret = regmap_update_bits(lp->regmap_dar, DAR_PHY_CTRL1,
+ DAR_PHY_CTRL1_SLOTTED, 0x0);
+
+ /* enable irq */
+ enable_irq(lp->spi->irq);
+
+ /* Unmask SEQ interrupt */
+ ret = regmap_update_bits(lp->regmap_dar, DAR_PHY_CTRL2,
+ DAR_PHY_CTRL2_SEQMSK, 0x0);
+
+ /* Start the RX sequence */
+ dev_dbg(printdev(lp), "start the RX sequence\n");
+ ret = regmap_update_bits(lp->regmap_dar, DAR_PHY_CTRL1,
+ DAR_PHY_CTRL1_XCVSEQ_MASK, MCR20A_XCVSEQ_RX);
+
+ return 0;
+}
+
+static void
+mcr20a_stop(struct ieee802154_hw *hw)
+{
+ struct mcr20a_local *lp = hw->priv;
+
+ dev_dbg(printdev(lp), "%s\n", __func__);
+
+ /* stop all running sequence */
+ regmap_update_bits(lp->regmap_dar, DAR_PHY_CTRL1,
+ DAR_PHY_CTRL1_XCVSEQ_MASK, MCR20A_XCVSEQ_IDLE);
+
+ /* disable irq */
+ disable_irq(lp->spi->irq);
+}
+
+static int
+mcr20a_set_hw_addr_filt(struct ieee802154_hw *hw,
+ struct ieee802154_hw_addr_filt *filt,
+ unsigned long changed)
+{
+ struct mcr20a_local *lp = hw->priv;
+
+ dev_dbg(printdev(lp), "%s\n", __func__);
+
+ if (changed & IEEE802154_AFILT_SADDR_CHANGED) {
+ u16 addr = le16_to_cpu(filt->short_addr);
+
+ regmap_write(lp->regmap_iar, IAR_MACSHORTADDRS0_LSB, addr);
+ regmap_write(lp->regmap_iar, IAR_MACSHORTADDRS0_MSB, addr >> 8);
+ }
+
+ if (changed & IEEE802154_AFILT_PANID_CHANGED) {
+ u16 pan = le16_to_cpu(filt->pan_id);
+
+ regmap_write(lp->regmap_iar, IAR_MACPANID0_LSB, pan);
+ regmap_write(lp->regmap_iar, IAR_MACPANID0_MSB, pan >> 8);
+ }
+
+ if (changed & IEEE802154_AFILT_IEEEADDR_CHANGED) {
+ u8 addr[8], i;
+
+ memcpy(addr, &filt->ieee_addr, 8);
+ for (i = 0; i < 8; i++)
+ regmap_write(lp->regmap_iar,
+ IAR_MACLONGADDRS0_0 + i, addr[i]);
+ }
+
+ if (changed & IEEE802154_AFILT_PANC_CHANGED) {
+ if (filt->pan_coord) {
+ regmap_update_bits(lp->regmap_dar, DAR_PHY_CTRL4,
+ DAR_PHY_CTRL4_PANCORDNTR0, 0x10);
+ } else {
+ regmap_update_bits(lp->regmap_dar, DAR_PHY_CTRL4,
+ DAR_PHY_CTRL4_PANCORDNTR0, 0x00);
+ }
+ }
+
+ return 0;
+}
+
+/* -30 dBm to 10 dBm */
+#define MCR20A_MAX_TX_POWERS 0x14
+static const s32 mcr20a_powers[MCR20A_MAX_TX_POWERS + 1] = {
+ -3000, -2800, -2600, -2400, -2200, -2000, -1800, -1600, -1400,
+ -1200, -1000, -800, -600, -400, -200, 0, 200, 400, 600, 800, 1000
+};
+
+static int
+mcr20a_set_txpower(struct ieee802154_hw *hw, s32 mbm)
+{
+ struct mcr20a_local *lp = hw->priv;
+ u32 i;
+
+ dev_dbg(printdev(lp), "%s(%d)\n", __func__, mbm);
+
+ for (i = 0; i < lp->hw->phy->supported.tx_powers_size; i++) {
+ if (lp->hw->phy->supported.tx_powers[i] == mbm)
+ return regmap_write(lp->regmap_dar, DAR_PA_PWR,
+ ((i + 8) & 0x1F));
+ }
+
+ return -EINVAL;
+}
+
+#define MCR20A_MAX_ED_LEVELS MCR20A_MIN_CCA_THRESHOLD
+static s32 mcr20a_ed_levels[MCR20A_MAX_ED_LEVELS + 1];
+
+static int
+mcr20a_set_cca_mode(struct ieee802154_hw *hw,
+ const struct wpan_phy_cca *cca)
+{
+ struct mcr20a_local *lp = hw->priv;
+ unsigned int cca_mode = 0xff;
+ bool cca_mode_and = false;
+ int ret;
+
+ dev_dbg(printdev(lp), "%s\n", __func__);
+
+ /* mapping 802.15.4 to driver spec */
+ switch (cca->mode) {
+ case NL802154_CCA_ENERGY:
+ cca_mode = MCR20A_CCA_MODE1;
+ break;
+ case NL802154_CCA_CARRIER:
+ cca_mode = MCR20A_CCA_MODE2;
+ break;
+ case NL802154_CCA_ENERGY_CARRIER:
+ switch (cca->opt) {
+ case NL802154_CCA_OPT_ENERGY_CARRIER_AND:
+ cca_mode = MCR20A_CCA_MODE3;
+ cca_mode_and = true;
+ break;
+ case NL802154_CCA_OPT_ENERGY_CARRIER_OR:
+ cca_mode = MCR20A_CCA_MODE3;
+ cca_mode_and = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ ret = regmap_update_bits(lp->regmap_dar, DAR_PHY_CTRL4,
+ DAR_PHY_CTRL4_CCATYPE_MASK,
+ cca_mode << DAR_PHY_CTRL4_CCATYPE_SHIFT);
+ if (ret < 0)
+ return ret;
+
+ if (cca_mode == MCR20A_CCA_MODE3) {
+ if (cca_mode_and) {
+ ret = regmap_update_bits(lp->regmap_iar, IAR_CCA_CTRL,
+ IAR_CCA_CTRL_CCA3_AND_NOT_OR,
+ 0x08);
+ } else {
+ ret = regmap_update_bits(lp->regmap_iar,
+ IAR_CCA_CTRL,
+ IAR_CCA_CTRL_CCA3_AND_NOT_OR,
+ 0x00);
+ }
+ if (ret < 0)
+ return ret;
+ }
+
+ return ret;
+}
+
+static int
+mcr20a_set_cca_ed_level(struct ieee802154_hw *hw, s32 mbm)
+{
+ struct mcr20a_local *lp = hw->priv;
+ u32 i;
+
+ dev_dbg(printdev(lp), "%s\n", __func__);
+
+ for (i = 0; i < hw->phy->supported.cca_ed_levels_size; i++) {
+ if (hw->phy->supported.cca_ed_levels[i] == mbm)
+ return regmap_write(lp->regmap_iar, IAR_CCA1_THRESH, i);
+ }
+
+ return 0;
+}
+
+static int
+mcr20a_set_promiscuous_mode(struct ieee802154_hw *hw, const bool on)
+{
+ struct mcr20a_local *lp = hw->priv;
+ int ret;
+ u8 rx_frame_filter_reg = 0x0;
+
+ dev_dbg(printdev(lp), "%s(%d)\n", __func__, on);
+
+ if (on) {
+ /* All frame types accepted*/
+ rx_frame_filter_reg &= ~(IAR_RX_FRAME_FLT_FRM_VER);
+ rx_frame_filter_reg |= (IAR_RX_FRAME_FLT_ACK_FT |
+ IAR_RX_FRAME_FLT_NS_FT);
+
+ ret = regmap_update_bits(lp->regmap_dar, DAR_PHY_CTRL4,
+ DAR_PHY_CTRL4_PROMISCUOUS,
+ DAR_PHY_CTRL4_PROMISCUOUS);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(lp->regmap_iar, IAR_RX_FRAME_FILTER,
+ rx_frame_filter_reg);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = regmap_update_bits(lp->regmap_dar, DAR_PHY_CTRL4,
+ DAR_PHY_CTRL4_PROMISCUOUS, 0x0);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(lp->regmap_iar, IAR_RX_FRAME_FILTER,
+ IAR_RX_FRAME_FLT_FRM_VER |
+ IAR_RX_FRAME_FLT_BEACON_FT |
+ IAR_RX_FRAME_FLT_DATA_FT |
+ IAR_RX_FRAME_FLT_CMD_FT);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct ieee802154_ops mcr20a_hw_ops = {
+ .owner = THIS_MODULE,
+ .xmit_async = mcr20a_xmit,
+ .ed = mcr20a_ed,
+ .set_channel = mcr20a_set_channel,
+ .start = mcr20a_start,
+ .stop = mcr20a_stop,
+ .set_hw_addr_filt = mcr20a_set_hw_addr_filt,
+ .set_txpower = mcr20a_set_txpower,
+ .set_cca_mode = mcr20a_set_cca_mode,
+ .set_cca_ed_level = mcr20a_set_cca_ed_level,
+ .set_promiscuous_mode = mcr20a_set_promiscuous_mode,
+};
+
+static int
+mcr20a_request_rx(struct mcr20a_local *lp)
+{
+ dev_dbg(printdev(lp), "%s\n", __func__);
+
+ /* Start the RX sequence */
+ regmap_update_bits_async(lp->regmap_dar, DAR_PHY_CTRL1,
+ DAR_PHY_CTRL1_XCVSEQ_MASK, MCR20A_XCVSEQ_RX);
+
+ return 0;
+}
+
+static void
+mcr20a_handle_rx_read_buf_complete(void *context)
+{
+ struct mcr20a_local *lp = context;
+ u8 len = lp->reg_data[0] & DAR_RX_FRAME_LENGTH_MASK;
+ struct sk_buff *skb;
+
+ dev_dbg(printdev(lp), "%s\n", __func__);
+
+ dev_dbg(printdev(lp), "RX is done\n");
+
+ if (!ieee802154_is_valid_psdu_len(len)) {
+ dev_vdbg(&lp->spi->dev, "corrupted frame received\n");
+ len = IEEE802154_MTU;
+ }
+
+ len = len - 2; /* get rid of frame check field */
+
+ skb = dev_alloc_skb(len);
+ if (!skb)
+ return;
+
+ memcpy(skb_put(skb, len), lp->rx_buf, len);
+ ieee802154_rx_irqsafe(lp->hw, skb, lp->rx_lqi[0]);
+
+ print_hex_dump_debug("mcr20a rx: ", DUMP_PREFIX_OFFSET, 16, 1,
+ lp->rx_buf, len, 0);
+ pr_debug("mcr20a rx: lqi: %02hhx\n", lp->rx_lqi[0]);
+
+ /* start RX sequence */
+ mcr20a_request_rx(lp);
+}
+
+static void
+mcr20a_handle_rx_read_len_complete(void *context)
+{
+ struct mcr20a_local *lp = context;
+ u8 len;
+ int ret;
+
+ dev_dbg(printdev(lp), "%s\n", __func__);
+
+ /* get the length of received frame */
+ len = lp->reg_data[0] & DAR_RX_FRAME_LENGTH_MASK;
+ dev_dbg(printdev(lp), "frame len : %d\n", len);
+
+ /* prepare to read the rx buf */
+ lp->rx_buf_msg.complete = mcr20a_handle_rx_read_buf_complete;
+ lp->rx_header[0] = MCR20A_BURST_READ_PACKET_BUF;
+ lp->rx_xfer_buf.len = len;
+
+ ret = spi_async(lp->spi, &lp->rx_buf_msg);
+ if (ret)
+ dev_err(printdev(lp), "failed to read rx buffer length\n");
+}
+
+static int
+mcr20a_handle_rx(struct mcr20a_local *lp)
+{
+ dev_dbg(printdev(lp), "%s\n", __func__);
+ lp->reg_msg.complete = mcr20a_handle_rx_read_len_complete;
+ lp->reg_cmd[0] = MCR20A_READ_REG(DAR_RX_FRM_LEN);
+ lp->reg_xfer_data.len = 1;
+
+ return spi_async(lp->spi, &lp->reg_msg);
+}
+
+static int
+mcr20a_handle_tx_complete(struct mcr20a_local *lp)
+{
+ dev_dbg(printdev(lp), "%s\n", __func__);
+
+ ieee802154_xmit_complete(lp->hw, lp->tx_skb, false);
+
+ return mcr20a_request_rx(lp);
+}
+
+static int
+mcr20a_handle_tx(struct mcr20a_local *lp)
+{
+ int ret;
+
+ dev_dbg(printdev(lp), "%s\n", __func__);
+
+ /* write tx buffer */
+ lp->tx_header[0] = MCR20A_BURST_WRITE_PACKET_BUF;
+ /* add 2 bytes of FCS */
+ lp->tx_len[0] = lp->tx_skb->len + 2;
+ lp->tx_xfer_buf.tx_buf = lp->tx_skb->data;
+ /* add 1 byte psduLength */
+ lp->tx_xfer_buf.len = lp->tx_skb->len + 1;
+
+ ret = spi_async(lp->spi, &lp->tx_buf_msg);
+ if (ret) {
+ dev_err(printdev(lp), "SPI write Failed for TX buf\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void
+mcr20a_irq_clean_complete(void *context)
+{
+ struct mcr20a_local *lp = context;
+ u8 seq_state = lp->irq_data[DAR_IRQ_STS1] & DAR_PHY_CTRL1_XCVSEQ_MASK;
+
+ dev_dbg(printdev(lp), "%s\n", __func__);
+
+ enable_irq(lp->spi->irq);
+
+ dev_dbg(printdev(lp), "IRQ STA1 (%02x) STA2 (%02x)\n",
+ lp->irq_data[DAR_IRQ_STS1], lp->irq_data[DAR_IRQ_STS2]);
+
+ switch (seq_state) {
+ /* TX IRQ, RX IRQ and SEQ IRQ */
+ case (0x03):
+ if (lp->is_tx) {
+ lp->is_tx = 0;
+ dev_dbg(printdev(lp), "TX is done. No ACK\n");
+ mcr20a_handle_tx_complete(lp);
+ }
+ break;
+ case (0x05):
+ /* rx is starting */
+ dev_dbg(printdev(lp), "RX is starting\n");
+ mcr20a_handle_rx(lp);
+ break;
+ case (0x07):
+ if (lp->is_tx) {
+ /* tx is done */
+ lp->is_tx = 0;
+ dev_dbg(printdev(lp), "TX is done. Get ACK\n");
+ mcr20a_handle_tx_complete(lp);
+ } else {
+ /* rx is starting */
+ dev_dbg(printdev(lp), "RX is starting\n");
+ mcr20a_handle_rx(lp);
+ }
+ break;
+ case (0x01):
+ if (lp->is_tx) {
+ dev_dbg(printdev(lp), "TX is starting\n");
+ mcr20a_handle_tx(lp);
+ } else {
+ dev_dbg(printdev(lp), "MCR20A is stop\n");
+ }
+ break;
+ }
+}
+
+static void mcr20a_irq_status_complete(void *context)
+{
+ int ret;
+ struct mcr20a_local *lp = context;
+
+ dev_dbg(printdev(lp), "%s\n", __func__);
+ regmap_update_bits_async(lp->regmap_dar, DAR_PHY_CTRL1,
+ DAR_PHY_CTRL1_XCVSEQ_MASK, MCR20A_XCVSEQ_IDLE);
+
+ lp->reg_msg.complete = mcr20a_irq_clean_complete;
+ lp->reg_cmd[0] = MCR20A_WRITE_REG(DAR_IRQ_STS1);
+ memcpy(lp->reg_data, lp->irq_data, MCR20A_IRQSTS_NUM);
+ lp->reg_xfer_data.len = MCR20A_IRQSTS_NUM;
+
+ ret = spi_async(lp->spi, &lp->reg_msg);
+
+ if (ret)
+ dev_err(printdev(lp), "failed to clean irq status\n");
+}
+
+static irqreturn_t mcr20a_irq_isr(int irq, void *data)
+{
+ struct mcr20a_local *lp = data;
+ int ret;
+
+ disable_irq_nosync(irq);
+
+ lp->irq_header[0] = MCR20A_READ_REG(DAR_IRQ_STS1);
+ /* read IRQSTSx */
+ ret = spi_async(lp->spi, &lp->irq_msg);
+ if (ret) {
+ enable_irq(irq);
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int mcr20a_get_platform_data(struct spi_device *spi,
+ struct mcr20a_platform_data *pdata)
+{
+ int ret = 0;
+
+ if (!spi->dev.of_node)
+ return -EINVAL;
+
+ pdata->rst_gpio = of_get_named_gpio(spi->dev.of_node, "rst_b-gpio", 0);
+ dev_dbg(&spi->dev, "rst_b-gpio: %d\n", pdata->rst_gpio);
+
+ return ret;
+}
+
+static void mcr20a_hw_setup(struct mcr20a_local *lp)
+{
+ u8 i;
+ struct ieee802154_hw *hw = lp->hw;
+ struct wpan_phy *phy = lp->hw->phy;
+
+ dev_dbg(printdev(lp), "%s\n", __func__);
+
+ phy->symbol_duration = 16;
+ phy->lifs_period = 40;
+ phy->sifs_period = 12;
+
+ hw->flags = IEEE802154_HW_TX_OMIT_CKSUM |
+ IEEE802154_HW_AFILT |
+ IEEE802154_HW_PROMISCUOUS;
+
+ phy->flags = WPAN_PHY_FLAG_TXPOWER | WPAN_PHY_FLAG_CCA_ED_LEVEL |
+ WPAN_PHY_FLAG_CCA_MODE;
+
+ phy->supported.cca_modes = BIT(NL802154_CCA_ENERGY) |
+ BIT(NL802154_CCA_CARRIER) | BIT(NL802154_CCA_ENERGY_CARRIER);
+ phy->supported.cca_opts = BIT(NL802154_CCA_OPT_ENERGY_CARRIER_AND) |
+ BIT(NL802154_CCA_OPT_ENERGY_CARRIER_OR);
+
+ /* initiating cca_ed_levels */
+ for (i = MCR20A_MAX_CCA_THRESHOLD; i < MCR20A_MIN_CCA_THRESHOLD + 1;
+ ++i) {
+ mcr20a_ed_levels[i] = -i * 100;
+ }
+
+ phy->supported.cca_ed_levels = mcr20a_ed_levels;
+ phy->supported.cca_ed_levels_size = ARRAY_SIZE(mcr20a_ed_levels);
+
+ phy->cca.mode = NL802154_CCA_ENERGY;
+
+ phy->supported.channels[0] = MCR20A_VALID_CHANNELS;
+ phy->current_page = 0;
+ /* MCR20A default reset value */
+ phy->current_channel = 20;
+ phy->symbol_duration = 16;
+ phy->supported.tx_powers = mcr20a_powers;
+ phy->supported.tx_powers_size = ARRAY_SIZE(mcr20a_powers);
+ phy->cca_ed_level = phy->supported.cca_ed_levels[75];
+ phy->transmit_power = phy->supported.tx_powers[0x0F];
+}
+
+static void
+mcr20a_setup_tx_spi_messages(struct mcr20a_local *lp)
+{
+ spi_message_init(&lp->tx_buf_msg);
+ lp->tx_buf_msg.context = lp;
+ lp->tx_buf_msg.complete = mcr20a_write_tx_buf_complete;
+
+ lp->tx_xfer_header.len = 1;
+ lp->tx_xfer_header.tx_buf = lp->tx_header;
+
+ lp->tx_xfer_len.len = 1;
+ lp->tx_xfer_len.tx_buf = lp->tx_len;
+
+ spi_message_add_tail(&lp->tx_xfer_header, &lp->tx_buf_msg);
+ spi_message_add_tail(&lp->tx_xfer_len, &lp->tx_buf_msg);
+ spi_message_add_tail(&lp->tx_xfer_buf, &lp->tx_buf_msg);
+}
+
+static void
+mcr20a_setup_rx_spi_messages(struct mcr20a_local *lp)
+{
+ spi_message_init(&lp->reg_msg);
+ lp->reg_msg.context = lp;
+
+ lp->reg_xfer_cmd.len = 1;
+ lp->reg_xfer_cmd.tx_buf = lp->reg_cmd;
+ lp->reg_xfer_cmd.rx_buf = lp->reg_cmd;
+
+ lp->reg_xfer_data.rx_buf = lp->reg_data;
+ lp->reg_xfer_data.tx_buf = lp->reg_data;
+
+ spi_message_add_tail(&lp->reg_xfer_cmd, &lp->reg_msg);
+ spi_message_add_tail(&lp->reg_xfer_data, &lp->reg_msg);
+
+ spi_message_init(&lp->rx_buf_msg);
+ lp->rx_buf_msg.context = lp;
+ lp->rx_buf_msg.complete = mcr20a_handle_rx_read_buf_complete;
+ lp->rx_xfer_header.len = 1;
+ lp->rx_xfer_header.tx_buf = lp->rx_header;
+ lp->rx_xfer_header.rx_buf = lp->rx_header;
+
+ lp->rx_xfer_buf.rx_buf = lp->rx_buf;
+
+ lp->rx_xfer_lqi.len = 1;
+ lp->rx_xfer_lqi.rx_buf = lp->rx_lqi;
+
+ spi_message_add_tail(&lp->rx_xfer_header, &lp->rx_buf_msg);
+ spi_message_add_tail(&lp->rx_xfer_buf, &lp->rx_buf_msg);
+ spi_message_add_tail(&lp->rx_xfer_lqi, &lp->rx_buf_msg);
+}
+
+static void
+mcr20a_setup_irq_spi_messages(struct mcr20a_local *lp)
+{
+ spi_message_init(&lp->irq_msg);
+ lp->irq_msg.context = lp;
+ lp->irq_msg.complete = mcr20a_irq_status_complete;
+ lp->irq_xfer_header.len = 1;
+ lp->irq_xfer_header.tx_buf = lp->irq_header;
+ lp->irq_xfer_header.rx_buf = lp->irq_header;
+
+ lp->irq_xfer_data.len = MCR20A_IRQSTS_NUM;
+ lp->irq_xfer_data.rx_buf = lp->irq_data;
+
+ spi_message_add_tail(&lp->irq_xfer_header, &lp->irq_msg);
+ spi_message_add_tail(&lp->irq_xfer_data, &lp->irq_msg);
+}
+
+static int
+mcr20a_phy_init(struct mcr20a_local *lp)
+{
+ u8 index;
+ unsigned int phy_reg = 0;
+ int ret;
+
+ dev_dbg(printdev(lp), "%s\n", __func__);
+
+ /* Disable Tristate on COCO MISO for SPI reads */
+ ret = regmap_write(lp->regmap_iar, IAR_MISC_PAD_CTRL, 0x02);
+ if (ret)
+ goto err_ret;
+
+ /* Clear all PP IRQ bits in IRQSTS1 to avoid unexpected interrupts
+ * immediately after init
+ */
+ ret = regmap_write(lp->regmap_dar, DAR_IRQ_STS1, 0xEF);
+ if (ret)
+ goto err_ret;
+
+ /* Clear all PP IRQ bits in IRQSTS2 */
+ ret = regmap_write(lp->regmap_dar, DAR_IRQ_STS2,
+ DAR_IRQSTS2_ASM_IRQ | DAR_IRQSTS2_PB_ERR_IRQ |
+ DAR_IRQSTS2_WAKE_IRQ);
+ if (ret)
+ goto err_ret;
+
+ /* Disable all timer interrupts */
+ ret = regmap_write(lp->regmap_dar, DAR_IRQ_STS3, 0xFF);
+ if (ret)
+ goto err_ret;
+
+ /* PHY_CTRL1 : default HW settings + AUTOACK enabled */
+ ret = regmap_update_bits(lp->regmap_dar, DAR_PHY_CTRL1,
+ DAR_PHY_CTRL1_AUTOACK, DAR_PHY_CTRL1_AUTOACK);
+
+ /* PHY_CTRL2 : disable all interrupts */
+ ret = regmap_write(lp->regmap_dar, DAR_PHY_CTRL2, 0xFF);
+ if (ret)
+ goto err_ret;
+
+ /* PHY_CTRL3 : disable all timers and remaining interrupts */
+ ret = regmap_write(lp->regmap_dar, DAR_PHY_CTRL3,
+ DAR_PHY_CTRL3_ASM_MSK | DAR_PHY_CTRL3_PB_ERR_MSK |
+ DAR_PHY_CTRL3_WAKE_MSK);
+ if (ret)
+ goto err_ret;
+
+ /* SRC_CTRL : enable Acknowledge Frame Pending and
+ * Source Address Matching Enable
+ */
+ ret = regmap_write(lp->regmap_dar, DAR_SRC_CTRL,
+ DAR_SRC_CTRL_ACK_FRM_PND |
+ (DAR_SRC_CTRL_INDEX << DAR_SRC_CTRL_INDEX_SHIFT));
+ if (ret)
+ goto err_ret;
+
+ /* RX_FRAME_FILTER */
+ /* FRM_VER[1:0] = b11. Accept FrameVersion 0 and 1 packets */
+ ret = regmap_write(lp->regmap_iar, IAR_RX_FRAME_FILTER,
+ IAR_RX_FRAME_FLT_FRM_VER |
+ IAR_RX_FRAME_FLT_BEACON_FT |
+ IAR_RX_FRAME_FLT_DATA_FT |
+ IAR_RX_FRAME_FLT_CMD_FT);
+ if (ret)
+ goto err_ret;
+
+ dev_info(printdev(lp), "MCR20A DAR overwrites version: 0x%02x\n",
+ MCR20A_OVERWRITE_VERSION);
+
+ /* Overwrites direct registers */
+ ret = regmap_write(lp->regmap_dar, DAR_OVERWRITE_VER,
+ MCR20A_OVERWRITE_VERSION);
+ if (ret)
+ goto err_ret;
+
+ /* Overwrites indirect registers */
+ ret = regmap_multi_reg_write(lp->regmap_iar, mar20a_iar_overwrites,
+ ARRAY_SIZE(mar20a_iar_overwrites));
+ if (ret)
+ goto err_ret;
+
+ /* Clear HW indirect queue */
+ dev_dbg(printdev(lp), "clear HW indirect queue\n");
+ for (index = 0; index < MCR20A_PHY_INDIRECT_QUEUE_SIZE; index++) {
+ phy_reg = (u8)(((index & DAR_SRC_CTRL_INDEX) <<
+ DAR_SRC_CTRL_INDEX_SHIFT)
+ | (DAR_SRC_CTRL_SRCADDR_EN)
+ | (DAR_SRC_CTRL_INDEX_DISABLE));
+ ret = regmap_write(lp->regmap_dar, DAR_SRC_CTRL, phy_reg);
+ if (ret)
+ goto err_ret;
+ phy_reg = 0;
+ }
+
+ /* Assign HW Indirect hash table to PAN0 */
+ ret = regmap_read(lp->regmap_iar, IAR_DUAL_PAN_CTRL, &phy_reg);
+ if (ret)
+ goto err_ret;
+
+ /* Clear current lvl */
+ phy_reg &= ~IAR_DUAL_PAN_CTRL_DUAL_PAN_SAM_LVL_MSK;
+
+ /* Set new lvl */
+ phy_reg |= MCR20A_PHY_INDIRECT_QUEUE_SIZE <<
+ IAR_DUAL_PAN_CTRL_DUAL_PAN_SAM_LVL_SHIFT;
+ ret = regmap_write(lp->regmap_iar, IAR_DUAL_PAN_CTRL, phy_reg);
+ if (ret)
+ goto err_ret;
+
+ /* Set CCA threshold to -75 dBm */
+ ret = regmap_write(lp->regmap_iar, IAR_CCA1_THRESH, 0x4B);
+ if (ret)
+ goto err_ret;
+
+ /* Set prescaller to obtain 1 symbol (16us) timebase */
+ ret = regmap_write(lp->regmap_iar, IAR_TMR_PRESCALE, 0x05);
+ if (ret)
+ goto err_ret;
+
+ /* Enable autodoze mode. */
+ ret = regmap_update_bits(lp->regmap_dar, DAR_PWR_MODES,
+ DAR_PWR_MODES_AUTODOZE,
+ DAR_PWR_MODES_AUTODOZE);
+ if (ret)
+ goto err_ret;
+
+ /* Disable clk_out */
+ ret = regmap_update_bits(lp->regmap_dar, DAR_CLK_OUT_CTRL,
+ DAR_CLK_OUT_CTRL_EN, 0x0);
+ if (ret)
+ goto err_ret;
+
+ return 0;
+
+err_ret:
+ return ret;
+}
+
+static int
+mcr20a_probe(struct spi_device *spi)
+{
+ struct ieee802154_hw *hw;
+ struct mcr20a_local *lp;
+ struct mcr20a_platform_data *pdata;
+ int irq_type;
+ int ret = -ENOMEM;
+
+ dev_dbg(&spi->dev, "%s\n", __func__);
+
+ if (!spi->irq) {
+ dev_err(&spi->dev, "no IRQ specified\n");
+ return -EINVAL;
+ }
+
+ pdata = kmalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ /* set mcr20a platform data */
+ ret = mcr20a_get_platform_data(spi, pdata);
+ if (ret < 0) {
+ dev_crit(&spi->dev, "mcr20a_get_platform_data failed.\n");
+ return ret;
+ }
+
+ /* init reset gpio */
+ if (gpio_is_valid(pdata->rst_gpio)) {
+ ret = devm_gpio_request_one(&spi->dev, pdata->rst_gpio,
+ GPIOF_OUT_INIT_HIGH, "reset");
+ if (ret)
+ return ret;
+ }
+
+ /* reset mcr20a */
+ if (gpio_is_valid(pdata->rst_gpio)) {
+ usleep_range(10, 20);
+ gpio_set_value_cansleep(pdata->rst_gpio, 0);
+ usleep_range(10, 20);
+ gpio_set_value_cansleep(pdata->rst_gpio, 1);
+ usleep_range(120, 240);
+ }
+
+ /* allocate ieee802154_hw and private data */
+ hw = ieee802154_alloc_hw(sizeof(*lp), &mcr20a_hw_ops);
+ if (!hw) {
+ dev_crit(&spi->dev, "ieee802154_alloc_hw failed\n");
+ return -ENOMEM;
+ }
+
+ /* init mcr20a local data */
+ lp = hw->priv;
+ lp->hw = hw;
+ lp->spi = spi;
+ lp->spi->dev.platform_data = pdata;
+ lp->pdata = pdata;
+
+ /* init ieee802154_hw */
+ hw->parent = &spi->dev;
+ ieee802154_random_extended_addr(&hw->phy->perm_extended_addr);
+
+ /* init buf */
+ lp->buf = devm_kzalloc(&spi->dev, SPI_COMMAND_BUFFER, GFP_KERNEL);
+
+ if (!lp->buf)
+ return -ENOMEM;
+
+ mcr20a_setup_tx_spi_messages(lp);
+ mcr20a_setup_rx_spi_messages(lp);
+ mcr20a_setup_irq_spi_messages(lp);
+
+ /* setup regmap */
+ lp->regmap_dar = devm_regmap_init_spi(spi, &mcr20a_dar_regmap);
+ if (IS_ERR(lp->regmap_dar)) {
+ ret = PTR_ERR(lp->regmap_dar);
+ dev_err(&spi->dev, "Failed to allocate dar map: %d\n",
+ ret);
+ goto free_dev;
+ }
+
+ lp->regmap_iar = devm_regmap_init_spi(spi, &mcr20a_iar_regmap);
+ if (IS_ERR(lp->regmap_iar)) {
+ ret = PTR_ERR(lp->regmap_iar);
+ dev_err(&spi->dev, "Failed to allocate iar map: %d\n", ret);
+ goto free_dev;
+ }
+
+ mcr20a_hw_setup(lp);
+
+ spi_set_drvdata(spi, lp);
+
+ ret = mcr20a_phy_init(lp);
+ if (ret < 0) {
+ dev_crit(&spi->dev, "mcr20a_phy_init failed\n");
+ goto free_dev;
+ }
+
+ irq_type = irq_get_trigger_type(spi->irq);
+ if (!irq_type)
+ irq_type = IRQF_TRIGGER_FALLING;
+
+ ret = devm_request_irq(&spi->dev, spi->irq, mcr20a_irq_isr,
+ irq_type, dev_name(&spi->dev), lp);
+ if (ret) {
+ dev_err(&spi->dev, "could not request_irq for mcr20a\n");
+ ret = -ENODEV;
+ goto free_dev;
+ }
+
+ /* disable_irq by default and wait for starting hardware */
+ disable_irq(spi->irq);
+
+ ret = ieee802154_register_hw(hw);
+ if (ret) {
+ dev_crit(&spi->dev, "ieee802154_register_hw failed\n");
+ goto free_dev;
+ }
+
+ return ret;
+
+free_dev:
+ ieee802154_free_hw(lp->hw);
+
+ return ret;
+}
+
+static int mcr20a_remove(struct spi_device *spi)
+{
+ struct mcr20a_local *lp = spi_get_drvdata(spi);
+
+ dev_dbg(&spi->dev, "%s\n", __func__);
+
+ ieee802154_unregister_hw(lp->hw);
+ ieee802154_free_hw(lp->hw);
+
+ return 0;
+}
+
+static const struct of_device_id mcr20a_of_match[] = {
+ { .compatible = "nxp,mcr20a", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, mcr20a_of_match);
+
+static const struct spi_device_id mcr20a_device_id[] = {
+ { .name = "mcr20a", },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, mcr20a_device_id);
+
+static struct spi_driver mcr20a_driver = {
+ .id_table = mcr20a_device_id,
+ .driver = {
+ .of_match_table = of_match_ptr(mcr20a_of_match),
+ .name = "mcr20a",
+ },
+ .probe = mcr20a_probe,
+ .remove = mcr20a_remove,
+};
+
+module_spi_driver(mcr20a_driver);
+
+MODULE_DESCRIPTION("MCR20A Transceiver Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Xue Liu <liuxuenetmail@gmail>");
diff --git a/drivers/net/ieee802154/mcr20a.h b/drivers/net/ieee802154/mcr20a.h
new file mode 100644
index 000000000000..6da4fd00b3c5
--- /dev/null
+++ b/drivers/net/ieee802154/mcr20a.h
@@ -0,0 +1,498 @@
+/*
+ * Driver for NXP MCR20A 802.15.4 Wireless-PAN Networking controller
+ *
+ * Copyright (C) 2018 Xue Liu <liuxuenetmail@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.
+ *
+ * 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.
+ *
+ */
+#ifndef _MCR20A_H
+#define _MCR20A_H
+
+/* Direct Accress Register */
+#define DAR_IRQ_STS1 0x00
+#define DAR_IRQ_STS2 0x01
+#define DAR_IRQ_STS3 0x02
+#define DAR_PHY_CTRL1 0x03
+#define DAR_PHY_CTRL2 0x04
+#define DAR_PHY_CTRL3 0x05
+#define DAR_RX_FRM_LEN 0x06
+#define DAR_PHY_CTRL4 0x07
+#define DAR_SRC_CTRL 0x08
+#define DAR_SRC_ADDRS_SUM_LSB 0x09
+#define DAR_SRC_ADDRS_SUM_MSB 0x0A
+#define DAR_CCA1_ED_FNL 0x0B
+#define DAR_EVENT_TMR_LSB 0x0C
+#define DAR_EVENT_TMR_MSB 0x0D
+#define DAR_EVENT_TMR_USB 0x0E
+#define DAR_TIMESTAMP_LSB 0x0F
+#define DAR_TIMESTAMP_MSB 0x10
+#define DAR_TIMESTAMP_USB 0x11
+#define DAR_T3CMP_LSB 0x12
+#define DAR_T3CMP_MSB 0x13
+#define DAR_T3CMP_USB 0x14
+#define DAR_T2PRIMECMP_LSB 0x15
+#define DAR_T2PRIMECMP_MSB 0x16
+#define DAR_T1CMP_LSB 0x17
+#define DAR_T1CMP_MSB 0x18
+#define DAR_T1CMP_USB 0x19
+#define DAR_T2CMP_LSB 0x1A
+#define DAR_T2CMP_MSB 0x1B
+#define DAR_T2CMP_USB 0x1C
+#define DAR_T4CMP_LSB 0x1D
+#define DAR_T4CMP_MSB 0x1E
+#define DAR_T4CMP_USB 0x1F
+#define DAR_PLL_INT0 0x20
+#define DAR_PLL_FRAC0_LSB 0x21
+#define DAR_PLL_FRAC0_MSB 0x22
+#define DAR_PA_PWR 0x23
+#define DAR_SEQ_STATE 0x24
+#define DAR_LQI_VALUE 0x25
+#define DAR_RSSI_CCA_CONT 0x26
+/*------------------ 0x27 */
+#define DAR_ASM_CTRL1 0x28
+#define DAR_ASM_CTRL2 0x29
+#define DAR_ASM_DATA_0 0x2A
+#define DAR_ASM_DATA_1 0x2B
+#define DAR_ASM_DATA_2 0x2C
+#define DAR_ASM_DATA_3 0x2D
+#define DAR_ASM_DATA_4 0x2E
+#define DAR_ASM_DATA_5 0x2F
+#define DAR_ASM_DATA_6 0x30
+#define DAR_ASM_DATA_7 0x31
+#define DAR_ASM_DATA_8 0x32
+#define DAR_ASM_DATA_9 0x33
+#define DAR_ASM_DATA_A 0x34
+#define DAR_ASM_DATA_B 0x35
+#define DAR_ASM_DATA_C 0x36
+#define DAR_ASM_DATA_D 0x37
+#define DAR_ASM_DATA_E 0x38
+#define DAR_ASM_DATA_F 0x39
+/*----------------------- 0x3A */
+#define DAR_OVERWRITE_VER 0x3B
+#define DAR_CLK_OUT_CTRL 0x3C
+#define DAR_PWR_MODES 0x3D
+#define IAR_INDEX 0x3E
+#define IAR_DATA 0x3F
+
+/* Indirect Resgister Memory */
+#define IAR_PART_ID 0x00
+#define IAR_XTAL_TRIM 0x01
+#define IAR_PMC_LP_TRIM 0x02
+#define IAR_MACPANID0_LSB 0x03
+#define IAR_MACPANID0_MSB 0x04
+#define IAR_MACSHORTADDRS0_LSB 0x05
+#define IAR_MACSHORTADDRS0_MSB 0x06
+#define IAR_MACLONGADDRS0_0 0x07
+#define IAR_MACLONGADDRS0_8 0x08
+#define IAR_MACLONGADDRS0_16 0x09
+#define IAR_MACLONGADDRS0_24 0x0A
+#define IAR_MACLONGADDRS0_32 0x0B
+#define IAR_MACLONGADDRS0_40 0x0C
+#define IAR_MACLONGADDRS0_48 0x0D
+#define IAR_MACLONGADDRS0_56 0x0E
+#define IAR_RX_FRAME_FILTER 0x0F
+#define IAR_PLL_INT1 0x10
+#define IAR_PLL_FRAC1_LSB 0x11
+#define IAR_PLL_FRAC1_MSB 0x12
+#define IAR_MACPANID1_LSB 0x13
+#define IAR_MACPANID1_MSB 0x14
+#define IAR_MACSHORTADDRS1_LSB 0x15
+#define IAR_MACSHORTADDRS1_MSB 0x16
+#define IAR_MACLONGADDRS1_0 0x17
+#define IAR_MACLONGADDRS1_8 0x18
+#define IAR_MACLONGADDRS1_16 0x19
+#define IAR_MACLONGADDRS1_24 0x1A
+#define IAR_MACLONGADDRS1_32 0x1B
+#define IAR_MACLONGADDRS1_40 0x1C
+#define IAR_MACLONGADDRS1_48 0x1D
+#define IAR_MACLONGADDRS1_56 0x1E
+#define IAR_DUAL_PAN_CTRL 0x1F
+#define IAR_DUAL_PAN_DWELL 0x20
+#define IAR_DUAL_PAN_STS 0x21
+#define IAR_CCA1_THRESH 0x22
+#define IAR_CCA1_ED_OFFSET_COMP 0x23
+#define IAR_LQI_OFFSET_COMP 0x24
+#define IAR_CCA_CTRL 0x25
+#define IAR_CCA2_CORR_PEAKS 0x26
+#define IAR_CCA2_CORR_THRESH 0x27
+#define IAR_TMR_PRESCALE 0x28
+/*-------------------- 0x29 */
+#define IAR_GPIO_DATA 0x2A
+#define IAR_GPIO_DIR 0x2B
+#define IAR_GPIO_PUL_EN 0x2C
+#define IAR_GPIO_PUL_SEL 0x2D
+#define IAR_GPIO_DS 0x2E
+/*------------------ 0x2F */
+#define IAR_ANT_PAD_CTRL 0x30
+#define IAR_MISC_PAD_CTRL 0x31
+#define IAR_BSM_CTRL 0x32
+/*------------------- 0x33 */
+#define IAR_RNG 0x34
+#define IAR_RX_BYTE_COUNT 0x35
+#define IAR_RX_WTR_MARK 0x36
+#define IAR_SOFT_RESET 0x37
+#define IAR_TXDELAY 0x38
+#define IAR_ACKDELAY 0x39
+#define IAR_SEQ_MGR_CTRL 0x3A
+#define IAR_SEQ_MGR_STS 0x3B
+#define IAR_SEQ_T_STS 0x3C
+#define IAR_ABORT_STS 0x3D
+#define IAR_CCCA_BUSY_CNT 0x3E
+#define IAR_SRC_ADDR_CHECKSUM1 0x3F
+#define IAR_SRC_ADDR_CHECKSUM2 0x40
+#define IAR_SRC_TBL_VALID1 0x41
+#define IAR_SRC_TBL_VALID2 0x42
+#define IAR_FILTERFAIL_CODE1 0x43
+#define IAR_FILTERFAIL_CODE2 0x44
+#define IAR_SLOT_PRELOAD 0x45
+/*-------------------- 0x46 */
+#define IAR_CORR_VT 0x47
+#define IAR_SYNC_CTRL 0x48
+#define IAR_PN_LSB_0 0x49
+#define IAR_PN_LSB_1 0x4A
+#define IAR_PN_MSB_0 0x4B
+#define IAR_PN_MSB_1 0x4C
+#define IAR_CORR_NVAL 0x4D
+#define IAR_TX_MODE_CTRL 0x4E
+#define IAR_SNF_THR 0x4F
+#define IAR_FAD_THR 0x50
+#define IAR_ANT_AGC_CTRL 0x51
+#define IAR_AGC_THR1 0x52
+#define IAR_AGC_THR2 0x53
+#define IAR_AGC_HYS 0x54
+#define IAR_AFC 0x55
+/*------------------- 0x56 */
+/*------------------- 0x57 */
+#define IAR_PHY_STS 0x58
+#define IAR_RX_MAX_CORR 0x59
+#define IAR_RX_MAX_PREAMBLE 0x5A
+#define IAR_RSSI 0x5B
+/*------------------- 0x5C */
+/*------------------- 0x5D */
+#define IAR_PLL_DIG_CTRL 0x5E
+#define IAR_VCO_CAL 0x5F
+#define IAR_VCO_BEST_DIFF 0x60
+#define IAR_VCO_BIAS 0x61
+#define IAR_KMOD_CTRL 0x62
+#define IAR_KMOD_CAL 0x63
+#define IAR_PA_CAL 0x64
+#define IAR_PA_PWRCAL 0x65
+#define IAR_ATT_RSSI1 0x66
+#define IAR_ATT_RSSI2 0x67
+#define IAR_RSSI_OFFSET 0x68
+#define IAR_RSSI_SLOPE 0x69
+#define IAR_RSSI_CAL1 0x6A
+#define IAR_RSSI_CAL2 0x6B
+/*------------------- 0x6C */
+/*------------------- 0x6D */
+#define IAR_XTAL_CTRL 0x6E
+#define IAR_XTAL_COMP_MIN 0x6F
+#define IAR_XTAL_COMP_MAX 0x70
+#define IAR_XTAL_GM 0x71
+/*------------------- 0x72 */
+/*------------------- 0x73 */
+#define IAR_LNA_TUNE 0x74
+#define IAR_LNA_AGCGAIN 0x75
+/*------------------- 0x76 */
+/*------------------- 0x77 */
+#define IAR_CHF_PMA_GAIN 0x78
+#define IAR_CHF_IBUF 0x79
+#define IAR_CHF_QBUF 0x7A
+#define IAR_CHF_IRIN 0x7B
+#define IAR_CHF_QRIN 0x7C
+#define IAR_CHF_IL 0x7D
+#define IAR_CHF_QL 0x7E
+#define IAR_CHF_CC1 0x7F
+#define IAR_CHF_CCL 0x80
+#define IAR_CHF_CC2 0x81
+#define IAR_CHF_IROUT 0x82
+#define IAR_CHF_QROUT 0x83
+/*------------------- 0x84 */
+/*------------------- 0x85 */
+#define IAR_RSSI_CTRL 0x86
+/*------------------- 0x87 */
+/*------------------- 0x88 */
+#define IAR_PA_BIAS 0x89
+#define IAR_PA_TUNING 0x8A
+/*------------------- 0x8B */
+/*------------------- 0x8C */
+#define IAR_PMC_HP_TRIM 0x8D
+#define IAR_VREGA_TRIM 0x8E
+/*------------------- 0x8F */
+/*------------------- 0x90 */
+#define IAR_VCO_CTRL1 0x91
+#define IAR_VCO_CTRL2 0x92
+/*------------------- 0x93 */
+/*------------------- 0x94 */
+#define IAR_ANA_SPARE_OUT1 0x95
+#define IAR_ANA_SPARE_OUT2 0x96
+#define IAR_ANA_SPARE_IN 0x97
+#define IAR_MISCELLANEOUS 0x98
+/*------------------- 0x99 */
+#define IAR_SEQ_MGR_OVRD0 0x9A
+#define IAR_SEQ_MGR_OVRD1 0x9B
+#define IAR_SEQ_MGR_OVRD2 0x9C
+#define IAR_SEQ_MGR_OVRD3 0x9D
+#define IAR_SEQ_MGR_OVRD4 0x9E
+#define IAR_SEQ_MGR_OVRD5 0x9F
+#define IAR_SEQ_MGR_OVRD6 0xA0
+#define IAR_SEQ_MGR_OVRD7 0xA1
+/*------------------- 0xA2 */
+#define IAR_TESTMODE_CTRL 0xA3
+#define IAR_DTM_CTRL1 0xA4
+#define IAR_DTM_CTRL2 0xA5
+#define IAR_ATM_CTRL1 0xA6
+#define IAR_ATM_CTRL2 0xA7
+#define IAR_ATM_CTRL3 0xA8
+/*------------------- 0xA9 */
+#define IAR_LIM_FE_TEST_CTRL 0xAA
+#define IAR_CHF_TEST_CTRL 0xAB
+#define IAR_VCO_TEST_CTRL 0xAC
+#define IAR_PLL_TEST_CTRL 0xAD
+#define IAR_PA_TEST_CTRL 0xAE
+#define IAR_PMC_TEST_CTRL 0xAF
+#define IAR_SCAN_DTM_PROTECT_1 0xFE
+#define IAR_SCAN_DTM_PROTECT_0 0xFF
+
+/* IRQSTS1 bits */
+#define DAR_IRQSTS1_RX_FRM_PEND BIT(7)
+#define DAR_IRQSTS1_PLL_UNLOCK_IRQ BIT(6)
+#define DAR_IRQSTS1_FILTERFAIL_IRQ BIT(5)
+#define DAR_IRQSTS1_RXWTRMRKIRQ BIT(4)
+#define DAR_IRQSTS1_CCAIRQ BIT(3)
+#define DAR_IRQSTS1_RXIRQ BIT(2)
+#define DAR_IRQSTS1_TXIRQ BIT(1)
+#define DAR_IRQSTS1_SEQIRQ BIT(0)
+
+/* IRQSTS2 bits */
+#define DAR_IRQSTS2_CRCVALID BIT(7)
+#define DAR_IRQSTS2_CCA BIT(6)
+#define DAR_IRQSTS2_SRCADDR BIT(5)
+#define DAR_IRQSTS2_PI BIT(4)
+#define DAR_IRQSTS2_TMRSTATUS BIT(3)
+#define DAR_IRQSTS2_ASM_IRQ BIT(2)
+#define DAR_IRQSTS2_PB_ERR_IRQ BIT(1)
+#define DAR_IRQSTS2_WAKE_IRQ BIT(0)
+
+/* IRQSTS3 bits */
+#define DAR_IRQSTS3_TMR4MSK BIT(7)
+#define DAR_IRQSTS3_TMR3MSK BIT(6)
+#define DAR_IRQSTS3_TMR2MSK BIT(5)
+#define DAR_IRQSTS3_TMR1MSK BIT(4)
+#define DAR_IRQSTS3_TMR4IRQ BIT(3)
+#define DAR_IRQSTS3_TMR3IRQ BIT(2)
+#define DAR_IRQSTS3_TMR2IRQ BIT(1)
+#define DAR_IRQSTS3_TMR1IRQ BIT(0)
+
+/* PHY_CTRL1 bits */
+#define DAR_PHY_CTRL1_TMRTRIGEN BIT(7)
+#define DAR_PHY_CTRL1_SLOTTED BIT(6)
+#define DAR_PHY_CTRL1_CCABFRTX BIT(5)
+#define DAR_PHY_CTRL1_CCABFRTX_SHIFT 5
+#define DAR_PHY_CTRL1_RXACKRQD BIT(4)
+#define DAR_PHY_CTRL1_AUTOACK BIT(3)
+#define DAR_PHY_CTRL1_XCVSEQ_MASK 0x07
+
+/* PHY_CTRL2 bits */
+#define DAR_PHY_CTRL2_CRC_MSK BIT(7)
+#define DAR_PHY_CTRL2_PLL_UNLOCK_MSK BIT(6)
+#define DAR_PHY_CTRL2_FILTERFAIL_MSK BIT(5)
+#define DAR_PHY_CTRL2_RX_WMRK_MSK BIT(4)
+#define DAR_PHY_CTRL2_CCAMSK BIT(3)
+#define DAR_PHY_CTRL2_RXMSK BIT(2)
+#define DAR_PHY_CTRL2_TXMSK BIT(1)
+#define DAR_PHY_CTRL2_SEQMSK BIT(0)
+
+/* PHY_CTRL3 bits */
+#define DAR_PHY_CTRL3_TMR4CMP_EN BIT(7)
+#define DAR_PHY_CTRL3_TMR3CMP_EN BIT(6)
+#define DAR_PHY_CTRL3_TMR2CMP_EN BIT(5)
+#define DAR_PHY_CTRL3_TMR1CMP_EN BIT(4)
+#define DAR_PHY_CTRL3_ASM_MSK BIT(2)
+#define DAR_PHY_CTRL3_PB_ERR_MSK BIT(1)
+#define DAR_PHY_CTRL3_WAKE_MSK BIT(0)
+
+/* RX_FRM_LEN bits */
+#define DAR_RX_FRAME_LENGTH_MASK (0x7F)
+
+/* PHY_CTRL4 bits */
+#define DAR_PHY_CTRL4_TRCV_MSK BIT(7)
+#define DAR_PHY_CTRL4_TC3TMOUT BIT(6)
+#define DAR_PHY_CTRL4_PANCORDNTR0 BIT(5)
+#define DAR_PHY_CTRL4_CCATYPE (3)
+#define DAR_PHY_CTRL4_CCATYPE_SHIFT (3)
+#define DAR_PHY_CTRL4_CCATYPE_MASK (0x18)
+#define DAR_PHY_CTRL4_TMRLOAD BIT(2)
+#define DAR_PHY_CTRL4_PROMISCUOUS BIT(1)
+#define DAR_PHY_CTRL4_TC2PRIME_EN BIT(0)
+
+/* SRC_CTRL bits */
+#define DAR_SRC_CTRL_INDEX (0x0F)
+#define DAR_SRC_CTRL_INDEX_SHIFT (4)
+#define DAR_SRC_CTRL_ACK_FRM_PND BIT(3)
+#define DAR_SRC_CTRL_SRCADDR_EN BIT(2)
+#define DAR_SRC_CTRL_INDEX_EN BIT(1)
+#define DAR_SRC_CTRL_INDEX_DISABLE BIT(0)
+
+/* DAR_ASM_CTRL1 bits */
+#define DAR_ASM_CTRL1_CLEAR BIT(7)
+#define DAR_ASM_CTRL1_START BIT(6)
+#define DAR_ASM_CTRL1_SELFTST BIT(5)
+#define DAR_ASM_CTRL1_CTR BIT(4)
+#define DAR_ASM_CTRL1_CBC BIT(3)
+#define DAR_ASM_CTRL1_AES BIT(2)
+#define DAR_ASM_CTRL1_LOAD_MAC BIT(1)
+
+/* DAR_ASM_CTRL2 bits */
+#define DAR_ASM_CTRL2_DATA_REG_TYPE_SEL (7)
+#define DAR_ASM_CTRL2_DATA_REG_TYPE_SEL_SHIFT (5)
+#define DAR_ASM_CTRL2_TSTPAS BIT(1)
+
+/* DAR_CLK_OUT_CTRL bits */
+#define DAR_CLK_OUT_CTRL_EXTEND BIT(7)
+#define DAR_CLK_OUT_CTRL_HIZ BIT(6)
+#define DAR_CLK_OUT_CTRL_SR BIT(5)
+#define DAR_CLK_OUT_CTRL_DS BIT(4)
+#define DAR_CLK_OUT_CTRL_EN BIT(3)
+#define DAR_CLK_OUT_CTRL_DIV (7)
+
+/* DAR_PWR_MODES bits */
+#define DAR_PWR_MODES_XTAL_READY BIT(5)
+#define DAR_PWR_MODES_XTALEN BIT(4)
+#define DAR_PWR_MODES_ASM_CLK_EN BIT(3)
+#define DAR_PWR_MODES_AUTODOZE BIT(1)
+#define DAR_PWR_MODES_PMC_MODE BIT(0)
+
+/* RX_FRAME_FILTER bits */
+#define IAR_RX_FRAME_FLT_FRM_VER (0xC0)
+#define IAR_RX_FRAME_FLT_FRM_VER_SHIFT (6)
+#define IAR_RX_FRAME_FLT_ACTIVE_PROMISCUOUS BIT(5)
+#define IAR_RX_FRAME_FLT_NS_FT BIT(4)
+#define IAR_RX_FRAME_FLT_CMD_FT BIT(3)
+#define IAR_RX_FRAME_FLT_ACK_FT BIT(2)
+#define IAR_RX_FRAME_FLT_DATA_FT BIT(1)
+#define IAR_RX_FRAME_FLT_BEACON_FT BIT(0)
+
+/* DUAL_PAN_CTRL bits */
+#define IAR_DUAL_PAN_CTRL_DUAL_PAN_SAM_LVL_MSK (0xF0)
+#define IAR_DUAL_PAN_CTRL_DUAL_PAN_SAM_LVL_SHIFT (4)
+#define IAR_DUAL_PAN_CTRL_CURRENT_NETWORK BIT(3)
+#define IAR_DUAL_PAN_CTRL_PANCORDNTR1 BIT(2)
+#define IAR_DUAL_PAN_CTRL_DUAL_PAN_AUTO BIT(1)
+#define IAR_DUAL_PAN_CTRL_ACTIVE_NETWORK BIT(0)
+
+/* DUAL_PAN_STS bits */
+#define IAR_DUAL_PAN_STS_RECD_ON_PAN1 BIT(7)
+#define IAR_DUAL_PAN_STS_RECD_ON_PAN0 BIT(6)
+#define IAR_DUAL_PAN_STS_DUAL_PAN_REMAIN (0x3F)
+
+/* CCA_CTRL bits */
+#define IAR_CCA_CTRL_AGC_FRZ_EN BIT(6)
+#define IAR_CCA_CTRL_CONT_RSSI_EN BIT(5)
+#define IAR_CCA_CTRL_LQI_RSSI_NOT_CORR BIT(4)
+#define IAR_CCA_CTRL_CCA3_AND_NOT_OR BIT(3)
+#define IAR_CCA_CTRL_POWER_COMP_EN_LQI BIT(2)
+#define IAR_CCA_CTRL_POWER_COMP_EN_ED BIT(1)
+#define IAR_CCA_CTRL_POWER_COMP_EN_CCA1 BIT(0)
+
+/* ANT_PAD_CTRL bits */
+#define IAR_ANT_PAD_CTRL_ANTX_POL (0x0F)
+#define IAR_ANT_PAD_CTRL_ANTX_POL_SHIFT (4)
+#define IAR_ANT_PAD_CTRL_ANTX_CTRLMODE BIT(3)
+#define IAR_ANT_PAD_CTRL_ANTX_HZ BIT(2)
+#define IAR_ANT_PAD_CTRL_ANTX_EN (3)
+
+/* MISC_PAD_CTRL bits */
+#define IAR_MISC_PAD_CTRL_MISO_HIZ_EN BIT(3)
+#define IAR_MISC_PAD_CTRL_IRQ_B_OD BIT(2)
+#define IAR_MISC_PAD_CTRL_NON_GPIO_DS BIT(1)
+#define IAR_MISC_PAD_CTRL_ANTX_CURR (1)
+
+/* ANT_AGC_CTRL bits */
+#define IAR_ANT_AGC_CTRL_FAD_EN_SHIFT (0)
+#define IAR_ANT_AGC_CTRL_FAD_EN_MASK (1)
+#define IAR_ANT_AGC_CTRL_ANTX_SHIFT (1)
+#define IAR_ANT_AGC_CTRL_ANTX_MASK BIT(AR_ANT_AGC_CTRL_ANTX_SHIFT)
+
+/* BSM_CTRL bits */
+#define BSM_CTRL_BSM_EN (1)
+
+/* SOFT_RESET bits */
+#define IAR_SOFT_RESET_SOG_RST BIT(7)
+#define IAR_SOFT_RESET_REGS_RST BIT(4)
+#define IAR_SOFT_RESET_PLL_RST BIT(3)
+#define IAR_SOFT_RESET_TX_RST BIT(2)
+#define IAR_SOFT_RESET_RX_RST BIT(1)
+#define IAR_SOFT_RESET_SEQ_MGR_RST BIT(0)
+
+/* SEQ_MGR_CTRL bits */
+#define IAR_SEQ_MGR_CTRL_SEQ_STATE_CTRL (3)
+#define IAR_SEQ_MGR_CTRL_SEQ_STATE_CTRL_SHIFT (6)
+#define IAR_SEQ_MGR_CTRL_NO_RX_RECYCLE BIT(5)
+#define IAR_SEQ_MGR_CTRL_LATCH_PREAMBLE BIT(4)
+#define IAR_SEQ_MGR_CTRL_EVENT_TMR_DO_NOT_LATCH BIT(3)
+#define IAR_SEQ_MGR_CTRL_CLR_NEW_SEQ_INHIBIT BIT(2)
+#define IAR_SEQ_MGR_CTRL_PSM_LOCK_DIS BIT(1)
+#define IAR_SEQ_MGR_CTRL_PLL_ABORT_OVRD BIT(0)
+
+/* SEQ_MGR_STS bits */
+#define IAR_SEQ_MGR_STS_TMR2_SEQ_TRIG_ARMED BIT(7)
+#define IAR_SEQ_MGR_STS_RX_MODE BIT(6)
+#define IAR_SEQ_MGR_STS_RX_TIMEOUT_PENDING BIT(5)
+#define IAR_SEQ_MGR_STS_NEW_SEQ_INHIBIT BIT(4)
+#define IAR_SEQ_MGR_STS_SEQ_IDLE BIT(3)
+#define IAR_SEQ_MGR_STS_XCVSEQ_ACTUAL (7)
+
+/* ABORT_STS bits */
+#define IAR_ABORT_STS_PLL_ABORTED BIT(2)
+#define IAR_ABORT_STS_TC3_ABORTED BIT(1)
+#define IAR_ABORT_STS_SW_ABORTED BIT(0)
+
+/* IAR_FILTERFAIL_CODE2 bits */
+#define IAR_FILTERFAIL_CODE2_PAN_SEL BIT(7)
+#define IAR_FILTERFAIL_CODE2_9_8 (3)
+
+/* PHY_STS bits */
+#define IAR_PHY_STS_PLL_UNLOCK BIT(7)
+#define IAR_PHY_STS_PLL_LOCK_ERR BIT(6)
+#define IAR_PHY_STS_PLL_LOCK BIT(5)
+#define IAR_PHY_STS_CRCVALID BIT(3)
+#define IAR_PHY_STS_FILTERFAIL_FLAG_SEL BIT(2)
+#define IAR_PHY_STS_SFD_DET BIT(1)
+#define IAR_PHY_STS_PREAMBLE_DET BIT(0)
+
+/* TESTMODE_CTRL bits */
+#define IAR_TEST_MODE_CTRL_HOT_ANT BIT(4)
+#define IAR_TEST_MODE_CTRL_IDEAL_RSSI_EN BIT(3)
+#define IAR_TEST_MODE_CTRL_IDEAL_PFC_EN BIT(2)
+#define IAR_TEST_MODE_CTRL_CONTINUOUS_EN BIT(1)
+#define IAR_TEST_MODE_CTRL_FPGA_EN BIT(0)
+
+/* DTM_CTRL1 bits */
+#define IAR_DTM_CTRL1_ATM_LOCKED BIT(7)
+#define IAR_DTM_CTRL1_DTM_EN BIT(6)
+#define IAR_DTM_CTRL1_PAGE5 BIT(5)
+#define IAR_DTM_CTRL1_PAGE4 BIT(4)
+#define IAR_DTM_CTRL1_PAGE3 BIT(3)
+#define IAR_DTM_CTRL1_PAGE2 BIT(2)
+#define IAR_DTM_CTRL1_PAGE1 BIT(1)
+#define IAR_DTM_CTRL1_PAGE0 BIT(0)
+
+/* TX_MODE_CTRL */
+#define IAR_TX_MODE_CTRL_TX_INV BIT(4)
+#define IAR_TX_MODE_CTRL_BT_EN BIT(3)
+#define IAR_TX_MODE_CTRL_DTS2 BIT(2)
+#define IAR_TX_MODE_CTRL_DTS1 BIT(1)
+#define IAR_TX_MODE_CTRL_DTS0 BIT(0)
+
+#define TX_MODE_CTRL_DTS_MASK (7)
+
+#endif /* _MCR20A_H */
diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c
index 0008da7e9d4c..5f2897ec0edc 100644
--- a/drivers/net/ifb.c
+++ b/drivers/net/ifb.c
@@ -330,6 +330,7 @@ static int __init ifb_init_module(void)
{
int i, err;
+ down_write(&pernet_ops_rwsem);
rtnl_lock();
err = __rtnl_link_register(&ifb_link_ops);
if (err < 0)
@@ -344,6 +345,7 @@ static int __init ifb_init_module(void)
out:
rtnl_unlock();
+ up_write(&pernet_ops_rwsem);
return err;
}
diff --git a/drivers/net/ipvlan/ipvlan.h b/drivers/net/ipvlan/ipvlan.h
index 5166575a164d..adb826f55e60 100644
--- a/drivers/net/ipvlan/ipvlan.h
+++ b/drivers/net/ipvlan/ipvlan.h
@@ -74,6 +74,7 @@ struct ipvl_dev {
DECLARE_BITMAP(mac_filters, IPVLAN_MAC_FILTER_SIZE);
netdev_features_t sfeatures;
u32 msg_enable;
+ spinlock_t addrs_lock;
};
struct ipvl_addr {
@@ -176,4 +177,10 @@ int ipvlan_link_new(struct net *src_net, struct net_device *dev,
void ipvlan_link_delete(struct net_device *dev, struct list_head *head);
void ipvlan_link_setup(struct net_device *dev);
int ipvlan_link_register(struct rtnl_link_ops *ops);
+
+static inline bool netif_is_ipvlan_port(const struct net_device *dev)
+{
+ return rcu_access_pointer(dev->rx_handler) == ipvlan_handle_frame;
+}
+
#endif /* __IPVLAN_H */
diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c
index c1f008fe4e1d..1a8132eb2a3e 100644
--- a/drivers/net/ipvlan/ipvlan_core.c
+++ b/drivers/net/ipvlan/ipvlan_core.c
@@ -35,6 +35,7 @@ void ipvlan_count_rx(const struct ipvl_dev *ipvlan,
}
EXPORT_SYMBOL_GPL(ipvlan_count_rx);
+#if IS_ENABLED(CONFIG_IPV6)
static u8 ipvlan_get_v6_hash(const void *iaddr)
{
const struct in6_addr *ip6_addr = iaddr;
@@ -42,6 +43,12 @@ static u8 ipvlan_get_v6_hash(const void *iaddr)
return __ipv6_addr_jhash(ip6_addr, ipvlan_jhash_secret) &
IPVLAN_HASH_MASK;
}
+#else
+static u8 ipvlan_get_v6_hash(const void *iaddr)
+{
+ return 0;
+}
+#endif
static u8 ipvlan_get_v4_hash(const void *iaddr)
{
@@ -51,6 +58,23 @@ static u8 ipvlan_get_v4_hash(const void *iaddr)
IPVLAN_HASH_MASK;
}
+static bool addr_equal(bool is_v6, struct ipvl_addr *addr, const void *iaddr)
+{
+ if (!is_v6 && addr->atype == IPVL_IPV4) {
+ struct in_addr *i4addr = (struct in_addr *)iaddr;
+
+ return addr->ip4addr.s_addr == i4addr->s_addr;
+#if IS_ENABLED(CONFIG_IPV6)
+ } else if (is_v6 && addr->atype == IPVL_IPV6) {
+ struct in6_addr *i6addr = (struct in6_addr *)iaddr;
+
+ return ipv6_addr_equal(&addr->ip6addr, i6addr);
+#endif
+ }
+
+ return false;
+}
+
static struct ipvl_addr *ipvlan_ht_addr_lookup(const struct ipvl_port *port,
const void *iaddr, bool is_v6)
{
@@ -59,15 +83,9 @@ static struct ipvl_addr *ipvlan_ht_addr_lookup(const struct ipvl_port *port,
hash = is_v6 ? ipvlan_get_v6_hash(iaddr) :
ipvlan_get_v4_hash(iaddr);
- hlist_for_each_entry_rcu(addr, &port->hlhead[hash], hlnode) {
- if (is_v6 && addr->atype == IPVL_IPV6 &&
- ipv6_addr_equal(&addr->ip6addr, iaddr))
- return addr;
- else if (!is_v6 && addr->atype == IPVL_IPV4 &&
- addr->ip4addr.s_addr ==
- ((struct in_addr *)iaddr)->s_addr)
+ hlist_for_each_entry_rcu(addr, &port->hlhead[hash], hlnode)
+ if (addr_equal(is_v6, addr, iaddr))
return addr;
- }
return NULL;
}
@@ -91,29 +109,33 @@ void ipvlan_ht_addr_del(struct ipvl_addr *addr)
struct ipvl_addr *ipvlan_find_addr(const struct ipvl_dev *ipvlan,
const void *iaddr, bool is_v6)
{
- struct ipvl_addr *addr;
+ struct ipvl_addr *addr, *ret = NULL;
- list_for_each_entry(addr, &ipvlan->addrs, anode) {
- if ((is_v6 && addr->atype == IPVL_IPV6 &&
- ipv6_addr_equal(&addr->ip6addr, iaddr)) ||
- (!is_v6 && addr->atype == IPVL_IPV4 &&
- addr->ip4addr.s_addr == ((struct in_addr *)iaddr)->s_addr))
- return addr;
+ rcu_read_lock();
+ list_for_each_entry_rcu(addr, &ipvlan->addrs, anode) {
+ if (addr_equal(is_v6, addr, iaddr)) {
+ ret = addr;
+ break;
+ }
}
- return NULL;
+ rcu_read_unlock();
+ return ret;
}
bool ipvlan_addr_busy(struct ipvl_port *port, void *iaddr, bool is_v6)
{
struct ipvl_dev *ipvlan;
+ bool ret = false;
- ASSERT_RTNL();
-
- list_for_each_entry(ipvlan, &port->ipvlans, pnode) {
- if (ipvlan_find_addr(ipvlan, iaddr, is_v6))
- return true;
+ rcu_read_lock();
+ list_for_each_entry_rcu(ipvlan, &port->ipvlans, pnode) {
+ if (ipvlan_find_addr(ipvlan, iaddr, is_v6)) {
+ ret = true;
+ break;
+ }
}
- return false;
+ rcu_read_unlock();
+ return ret;
}
static void *ipvlan_get_L3_hdr(struct ipvl_port *port, struct sk_buff *skb, int *type)
@@ -150,6 +172,7 @@ static void *ipvlan_get_L3_hdr(struct ipvl_port *port, struct sk_buff *skb, int
lyr3h = ip4h;
break;
}
+#if IS_ENABLED(CONFIG_IPV6)
case htons(ETH_P_IPV6): {
struct ipv6hdr *ip6h;
@@ -188,6 +211,7 @@ static void *ipvlan_get_L3_hdr(struct ipvl_port *port, struct sk_buff *skb, int
}
break;
}
+#endif
default:
return NULL;
}
@@ -337,14 +361,18 @@ static struct ipvl_addr *ipvlan_addr_lookup(struct ipvl_port *port,
{
struct ipvl_addr *addr = NULL;
- if (addr_type == IPVL_IPV6) {
+ switch (addr_type) {
+#if IS_ENABLED(CONFIG_IPV6)
+ case IPVL_IPV6: {
struct ipv6hdr *ip6h;
struct in6_addr *i6addr;
ip6h = (struct ipv6hdr *)lyr3h;
i6addr = use_dest ? &ip6h->daddr : &ip6h->saddr;
addr = ipvlan_ht_addr_lookup(port, i6addr, true);
- } else if (addr_type == IPVL_ICMPV6) {
+ break;
+ }
+ case IPVL_ICMPV6: {
struct nd_msg *ndmh;
struct in6_addr *i6addr;
@@ -356,14 +384,19 @@ static struct ipvl_addr *ipvlan_addr_lookup(struct ipvl_port *port,
i6addr = &ndmh->target;
addr = ipvlan_ht_addr_lookup(port, i6addr, true);
}
- } else if (addr_type == IPVL_IPV4) {
+ break;
+ }
+#endif
+ case IPVL_IPV4: {
struct iphdr *ip4h;
__be32 *i4addr;
ip4h = (struct iphdr *)lyr3h;
i4addr = use_dest ? &ip4h->daddr : &ip4h->saddr;
addr = ipvlan_ht_addr_lookup(port, i4addr, false);
- } else if (addr_type == IPVL_ARP) {
+ break;
+ }
+ case IPVL_ARP: {
struct arphdr *arph;
unsigned char *arp_ptr;
__be32 dip;
@@ -377,6 +410,8 @@ static struct ipvl_addr *ipvlan_addr_lookup(struct ipvl_port *port,
memcpy(&dip, arp_ptr, 4);
addr = ipvlan_ht_addr_lookup(port, &dip, false);
+ break;
+ }
}
return addr;
@@ -420,6 +455,7 @@ out:
return ret;
}
+#if IS_ENABLED(CONFIG_IPV6)
static int ipvlan_process_v6_outbound(struct sk_buff *skb)
{
const struct ipv6hdr *ip6h = ipv6_hdr(skb);
@@ -456,6 +492,12 @@ err:
out:
return ret;
}
+#else
+static int ipvlan_process_v6_outbound(struct sk_buff *skb)
+{
+ return NET_XMIT_DROP;
+}
+#endif
static int ipvlan_process_outbound(struct sk_buff *skb)
{
@@ -464,8 +506,8 @@ static int ipvlan_process_outbound(struct sk_buff *skb)
/* In this mode we dont care about multicast and broadcast traffic */
if (is_multicast_ether_addr(ethh->h_dest)) {
- pr_warn_ratelimited("Dropped {multi|broad}cast of type= [%x]\n",
- ntohs(skb->protocol));
+ pr_debug_ratelimited("Dropped {multi|broad}cast of type=[%x]\n",
+ ntohs(skb->protocol));
kfree_skb(skb);
goto out;
}
@@ -759,6 +801,7 @@ struct sk_buff *ipvlan_l3_rcv(struct net_device *dev, struct sk_buff *skb,
goto out;
break;
}
+#if IS_ENABLED(CONFIG_IPV6)
case AF_INET6:
{
struct dst_entry *dst;
@@ -774,10 +817,12 @@ struct sk_buff *ipvlan_l3_rcv(struct net_device *dev, struct sk_buff *skb,
};
skb_dst_drop(skb);
- dst = ip6_route_input_lookup(dev_net(sdev), sdev, &fl6, flags);
+ dst = ip6_route_input_lookup(dev_net(sdev), sdev, &fl6,
+ skb, flags);
skb_dst_set(skb, dst);
break;
}
+#endif
default:
break;
}
diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c
index 2469df118fbf..450eec264a5e 100644
--- a/drivers/net/ipvlan/ipvlan_main.c
+++ b/drivers/net/ipvlan/ipvlan_main.c
@@ -22,12 +22,14 @@ static const struct nf_hook_ops ipvl_nfops[] = {
.hooknum = NF_INET_LOCAL_IN,
.priority = INT_MAX,
},
+#if IS_ENABLED(CONFIG_IPV6)
{
.hook = ipvlan_nf_input,
.pf = NFPROTO_IPV6,
.hooknum = NF_INET_LOCAL_IN,
.priority = INT_MAX,
},
+#endif
};
static const struct l3mdev_ops ipvl_l3mdev_ops = {
@@ -127,7 +129,6 @@ static int ipvlan_port_create(struct net_device *dev)
if (err)
goto err;
- dev->priv_flags |= IFF_IPVLAN_MASTER;
return 0;
err:
@@ -140,7 +141,6 @@ static void ipvlan_port_destroy(struct net_device *dev)
struct ipvl_port *port = ipvlan_port_get_rtnl(dev);
struct sk_buff *skb;
- dev->priv_flags &= ~IFF_IPVLAN_MASTER;
if (port->mode == IPVLAN_MODE_L3S) {
dev->priv_flags &= ~IFF_L3MDEV_MASTER;
ipvlan_unregister_nf_hook(dev_net(dev));
@@ -176,7 +176,7 @@ static int ipvlan_init(struct net_device *dev)
dev->state = (dev->state & ~IPVLAN_STATE_MASK) |
(phy_dev->state & IPVLAN_STATE_MASK);
dev->features = phy_dev->features & IPVLAN_FEATURES;
- dev->features |= NETIF_F_LLTX;
+ dev->features |= NETIF_F_LLTX | NETIF_F_VLAN_CHALLENGED;
dev->gso_max_size = phy_dev->gso_max_size;
dev->gso_max_segs = phy_dev->gso_max_segs;
dev->hard_header_len = phy_dev->hard_header_len;
@@ -225,8 +225,10 @@ static int ipvlan_open(struct net_device *dev)
else
dev->flags &= ~IFF_NOARP;
- list_for_each_entry(addr, &ipvlan->addrs, anode)
+ rcu_read_lock();
+ list_for_each_entry_rcu(addr, &ipvlan->addrs, anode)
ipvlan_ht_addr_add(ipvlan, addr);
+ rcu_read_unlock();
return dev_uc_add(phy_dev, phy_dev->dev_addr);
}
@@ -242,8 +244,10 @@ static int ipvlan_stop(struct net_device *dev)
dev_uc_del(phy_dev, phy_dev->dev_addr);
- list_for_each_entry(addr, &ipvlan->addrs, anode)
+ rcu_read_lock();
+ list_for_each_entry_rcu(addr, &ipvlan->addrs, anode)
ipvlan_ht_addr_del(addr);
+ rcu_read_unlock();
return 0;
}
@@ -417,6 +421,12 @@ static const struct header_ops ipvlan_header_ops = {
.cache_update = eth_header_cache_update,
};
+static bool netif_is_ipvlan(const struct net_device *dev)
+{
+ /* both ipvlan and ipvtap devices use the same netdev_ops */
+ return dev->netdev_ops == &ipvlan_netdev_ops;
+}
+
static int ipvlan_ethtool_get_link_ksettings(struct net_device *dev,
struct ethtool_link_ksettings *cmd)
{
@@ -586,6 +596,7 @@ int ipvlan_link_new(struct net *src_net, struct net_device *dev,
ipvlan->sfeatures = IPVLAN_FEATURES;
ipvlan_adjust_mtu(ipvlan, phy_dev);
INIT_LIST_HEAD(&ipvlan->addrs);
+ spin_lock_init(&ipvlan->addrs_lock);
/* TODO Probably put random address here to be presented to the
* world but keep using the physical-dev address for the outgoing
@@ -593,7 +604,7 @@ int ipvlan_link_new(struct net *src_net, struct net_device *dev,
*/
memcpy(dev->dev_addr, phy_dev->dev_addr, ETH_ALEN);
- dev->priv_flags |= IFF_IPVLAN_SLAVE;
+ dev->priv_flags |= IFF_NO_RX_HANDLER;
err = register_netdevice(dev);
if (err < 0)
@@ -663,11 +674,13 @@ void ipvlan_link_delete(struct net_device *dev, struct list_head *head)
struct ipvl_dev *ipvlan = netdev_priv(dev);
struct ipvl_addr *addr, *next;
+ spin_lock_bh(&ipvlan->addrs_lock);
list_for_each_entry_safe(addr, next, &ipvlan->addrs, anode) {
ipvlan_ht_addr_del(addr);
- list_del(&addr->anode);
+ list_del_rcu(&addr->anode);
kfree_rcu(addr, rcu);
}
+ spin_unlock_bh(&ipvlan->addrs_lock);
ida_simple_remove(&ipvlan->port->ida, dev->dev_id);
list_del_rcu(&ipvlan->pnode);
@@ -758,8 +771,7 @@ static int ipvlan_device_event(struct notifier_block *unused,
if (dev->reg_state != NETREG_UNREGISTERING)
break;
- list_for_each_entry_safe(ipvlan, next, &port->ipvlans,
- pnode)
+ list_for_each_entry_safe(ipvlan, next, &port->ipvlans, pnode)
ipvlan->dev->rtnl_link_ops->dellink(ipvlan->dev,
&lst_kill);
unregister_netdevice_many(&lst_kill);
@@ -791,6 +803,7 @@ static int ipvlan_device_event(struct notifier_block *unused,
return NOTIFY_DONE;
}
+/* the caller must held the addrs lock */
static int ipvlan_add_addr(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6)
{
struct ipvl_addr *addr;
@@ -800,14 +813,17 @@ static int ipvlan_add_addr(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6)
return -ENOMEM;
addr->master = ipvlan;
- if (is_v6) {
- memcpy(&addr->ip6addr, iaddr, sizeof(struct in6_addr));
- addr->atype = IPVL_IPV6;
- } else {
+ if (!is_v6) {
memcpy(&addr->ip4addr, iaddr, sizeof(struct in_addr));
addr->atype = IPVL_IPV4;
+#if IS_ENABLED(CONFIG_IPV6)
+ } else {
+ memcpy(&addr->ip6addr, iaddr, sizeof(struct in6_addr));
+ addr->atype = IPVL_IPV6;
+#endif
}
- list_add_tail(&addr->anode, &ipvlan->addrs);
+
+ list_add_tail_rcu(&addr->anode, &ipvlan->addrs);
/* If the interface is not up, the address will be added to the hash
* list by ipvlan_open.
@@ -822,32 +838,17 @@ static void ipvlan_del_addr(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6)
{
struct ipvl_addr *addr;
+ spin_lock_bh(&ipvlan->addrs_lock);
addr = ipvlan_find_addr(ipvlan, iaddr, is_v6);
- if (!addr)
+ if (!addr) {
+ spin_unlock_bh(&ipvlan->addrs_lock);
return;
+ }
ipvlan_ht_addr_del(addr);
- list_del(&addr->anode);
+ list_del_rcu(&addr->anode);
+ spin_unlock_bh(&ipvlan->addrs_lock);
kfree_rcu(addr, rcu);
-
- return;
-}
-
-static int ipvlan_add_addr6(struct ipvl_dev *ipvlan, struct in6_addr *ip6_addr)
-{
- if (ipvlan_addr_busy(ipvlan->port, ip6_addr, true)) {
- netif_err(ipvlan, ifup, ipvlan->dev,
- "Failed to add IPv6=%pI6c addr for %s intf\n",
- ip6_addr, ipvlan->dev->name);
- return -EINVAL;
- }
-
- return ipvlan_add_addr(ipvlan, ip6_addr, true);
-}
-
-static void ipvlan_del_addr6(struct ipvl_dev *ipvlan, struct in6_addr *ip6_addr)
-{
- return ipvlan_del_addr(ipvlan, ip6_addr, true);
}
static bool ipvlan_is_valid_dev(const struct net_device *dev)
@@ -863,6 +864,27 @@ static bool ipvlan_is_valid_dev(const struct net_device *dev)
return true;
}
+#if IS_ENABLED(CONFIG_IPV6)
+static int ipvlan_add_addr6(struct ipvl_dev *ipvlan, struct in6_addr *ip6_addr)
+{
+ int ret = -EINVAL;
+
+ spin_lock_bh(&ipvlan->addrs_lock);
+ if (ipvlan_addr_busy(ipvlan->port, ip6_addr, true))
+ netif_err(ipvlan, ifup, ipvlan->dev,
+ "Failed to add IPv6=%pI6c addr for %s intf\n",
+ ip6_addr, ipvlan->dev->name);
+ else
+ ret = ipvlan_add_addr(ipvlan, ip6_addr, true);
+ spin_unlock_bh(&ipvlan->addrs_lock);
+ return ret;
+}
+
+static void ipvlan_del_addr6(struct ipvl_dev *ipvlan, struct in6_addr *ip6_addr)
+{
+ return ipvlan_del_addr(ipvlan, ip6_addr, true);
+}
+
static int ipvlan_addr6_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
@@ -894,10 +916,6 @@ static int ipvlan_addr6_validator_event(struct notifier_block *unused,
struct net_device *dev = (struct net_device *)i6vi->i6vi_dev->dev;
struct ipvl_dev *ipvlan = netdev_priv(dev);
- /* FIXME IPv6 autoconf calls us from bh without RTNL */
- if (in_softirq())
- return NOTIFY_DONE;
-
if (!ipvlan_is_valid_dev(dev))
return NOTIFY_DONE;
@@ -913,17 +931,21 @@ static int ipvlan_addr6_validator_event(struct notifier_block *unused,
return NOTIFY_OK;
}
+#endif
static int ipvlan_add_addr4(struct ipvl_dev *ipvlan, struct in_addr *ip4_addr)
{
- if (ipvlan_addr_busy(ipvlan->port, ip4_addr, false)) {
+ int ret = -EINVAL;
+
+ spin_lock_bh(&ipvlan->addrs_lock);
+ if (ipvlan_addr_busy(ipvlan->port, ip4_addr, false))
netif_err(ipvlan, ifup, ipvlan->dev,
"Failed to add IPv4=%pI4 on %s intf.\n",
ip4_addr, ipvlan->dev->name);
- return -EINVAL;
- }
-
- return ipvlan_add_addr(ipvlan, ip4_addr, false);
+ else
+ ret = ipvlan_add_addr(ipvlan, ip4_addr, false);
+ spin_unlock_bh(&ipvlan->addrs_lock);
+ return ret;
}
static void ipvlan_del_addr4(struct ipvl_dev *ipvlan, struct in_addr *ip4_addr)
@@ -993,6 +1015,7 @@ static struct notifier_block ipvlan_notifier_block __read_mostly = {
.notifier_call = ipvlan_device_event,
};
+#if IS_ENABLED(CONFIG_IPV6)
static struct notifier_block ipvlan_addr6_notifier_block __read_mostly = {
.notifier_call = ipvlan_addr6_event,
};
@@ -1000,6 +1023,7 @@ static struct notifier_block ipvlan_addr6_notifier_block __read_mostly = {
static struct notifier_block ipvlan_addr6_vtor_notifier_block __read_mostly = {
.notifier_call = ipvlan_addr6_validator_event,
};
+#endif
static void ipvlan_ns_exit(struct net *net)
{
@@ -1024,9 +1048,11 @@ static int __init ipvlan_init_module(void)
ipvlan_init_secret();
register_netdevice_notifier(&ipvlan_notifier_block);
+#if IS_ENABLED(CONFIG_IPV6)
register_inet6addr_notifier(&ipvlan_addr6_notifier_block);
register_inet6addr_validator_notifier(
&ipvlan_addr6_vtor_notifier_block);
+#endif
register_inetaddr_notifier(&ipvlan_addr4_notifier_block);
register_inetaddr_validator_notifier(&ipvlan_addr4_vtor_notifier_block);
@@ -1045,9 +1071,11 @@ error:
unregister_inetaddr_notifier(&ipvlan_addr4_notifier_block);
unregister_inetaddr_validator_notifier(
&ipvlan_addr4_vtor_notifier_block);
+#if IS_ENABLED(CONFIG_IPV6)
unregister_inet6addr_notifier(&ipvlan_addr6_notifier_block);
unregister_inet6addr_validator_notifier(
&ipvlan_addr6_vtor_notifier_block);
+#endif
unregister_netdevice_notifier(&ipvlan_notifier_block);
return err;
}
@@ -1060,9 +1088,11 @@ static void __exit ipvlan_cleanup_module(void)
unregister_inetaddr_notifier(&ipvlan_addr4_notifier_block);
unregister_inetaddr_validator_notifier(
&ipvlan_addr4_vtor_notifier_block);
+#if IS_ENABLED(CONFIG_IPV6)
unregister_inet6addr_notifier(&ipvlan_addr6_notifier_block);
unregister_inet6addr_validator_notifier(
&ipvlan_addr6_vtor_notifier_block);
+#endif
}
module_init(ipvlan_init_module);
diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c
index 9cbb0c8a896a..7de88b33d5b9 100644
--- a/drivers/net/macsec.c
+++ b/drivers/net/macsec.c
@@ -3277,7 +3277,7 @@ static int macsec_newlink(struct net *net, struct net_device *dev,
err = netdev_upper_dev_link(real_dev, dev, extack);
if (err < 0)
- goto put_dev;
+ goto unregister;
/* need to be already registered so that ->init has run and
* the MAC addr is set
@@ -3316,8 +3316,7 @@ del_dev:
macsec_del_dev(macsec);
unlink:
netdev_upper_dev_unlink(real_dev, dev);
-put_dev:
- dev_put(real_dev);
+unregister:
unregister_netdevice(dev);
return err;
}
diff --git a/drivers/net/netdevsim/Makefile b/drivers/net/netdevsim/Makefile
index 09388c06171d..449b2a1a1800 100644
--- a/drivers/net/netdevsim/Makefile
+++ b/drivers/net/netdevsim/Makefile
@@ -9,3 +9,7 @@ ifeq ($(CONFIG_BPF_SYSCALL),y)
netdevsim-objs += \
bpf.o
endif
+
+ifneq ($(CONFIG_NET_DEVLINK),)
+netdevsim-objs += devlink.o fib.o
+endif
diff --git a/drivers/net/netdevsim/devlink.c b/drivers/net/netdevsim/devlink.c
new file mode 100644
index 000000000000..bef7db5d129a
--- /dev/null
+++ b/drivers/net/netdevsim/devlink.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) 2018 Cumulus Networks. All rights reserved.
+ * Copyright (c) 2018 David Ahern <dsa@cumulusnetworks.com>
+ *
+ * This software is licensed under the GNU General License Version 2,
+ * June 1991 as shown in the file COPYING in the top-level directory of this
+ * source tree.
+ *
+ * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
+ * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
+ * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
+ * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+ */
+
+#include <linux/device.h>
+#include <net/devlink.h>
+#include <net/netns/generic.h>
+
+#include "netdevsim.h"
+
+static unsigned int nsim_devlink_id;
+
+/* place holder until devlink and namespaces is sorted out */
+static struct net *nsim_devlink_net(struct devlink *devlink)
+{
+ return &init_net;
+}
+
+/* IPv4
+ */
+static u64 nsim_ipv4_fib_resource_occ_get(void *priv)
+{
+ struct net *net = priv;
+
+ return nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB, false);
+}
+
+static u64 nsim_ipv4_fib_rules_res_occ_get(void *priv)
+{
+ struct net *net = priv;
+
+ return nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB_RULES, false);
+}
+
+/* IPv6
+ */
+static u64 nsim_ipv6_fib_resource_occ_get(void *priv)
+{
+ struct net *net = priv;
+
+ return nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB, false);
+}
+
+static u64 nsim_ipv6_fib_rules_res_occ_get(void *priv)
+{
+ struct net *net = priv;
+
+ return nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB_RULES, false);
+}
+
+static int devlink_resources_register(struct devlink *devlink)
+{
+ struct devlink_resource_size_params params = {
+ .size_max = (u64)-1,
+ .size_granularity = 1,
+ .unit = DEVLINK_RESOURCE_UNIT_ENTRY
+ };
+ struct net *net = nsim_devlink_net(devlink);
+ int err;
+ u64 n;
+
+ /* Resources for IPv4 */
+ err = devlink_resource_register(devlink, "IPv4", (u64)-1,
+ NSIM_RESOURCE_IPV4,
+ DEVLINK_RESOURCE_ID_PARENT_TOP,
+ &params);
+ if (err) {
+ pr_err("Failed to register IPv4 top resource\n");
+ goto out;
+ }
+
+ n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB, true);
+ err = devlink_resource_register(devlink, "fib", n,
+ NSIM_RESOURCE_IPV4_FIB,
+ NSIM_RESOURCE_IPV4, &params);
+ if (err) {
+ pr_err("Failed to register IPv4 FIB resource\n");
+ return err;
+ }
+
+ n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB_RULES, true);
+ err = devlink_resource_register(devlink, "fib-rules", n,
+ NSIM_RESOURCE_IPV4_FIB_RULES,
+ NSIM_RESOURCE_IPV4, &params);
+ if (err) {
+ pr_err("Failed to register IPv4 FIB rules resource\n");
+ return err;
+ }
+
+ /* Resources for IPv6 */
+ err = devlink_resource_register(devlink, "IPv6", (u64)-1,
+ NSIM_RESOURCE_IPV6,
+ DEVLINK_RESOURCE_ID_PARENT_TOP,
+ &params);
+ if (err) {
+ pr_err("Failed to register IPv6 top resource\n");
+ goto out;
+ }
+
+ n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB, true);
+ err = devlink_resource_register(devlink, "fib", n,
+ NSIM_RESOURCE_IPV6_FIB,
+ NSIM_RESOURCE_IPV6, &params);
+ if (err) {
+ pr_err("Failed to register IPv6 FIB resource\n");
+ return err;
+ }
+
+ n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB_RULES, true);
+ err = devlink_resource_register(devlink, "fib-rules", n,
+ NSIM_RESOURCE_IPV6_FIB_RULES,
+ NSIM_RESOURCE_IPV6, &params);
+ if (err) {
+ pr_err("Failed to register IPv6 FIB rules resource\n");
+ return err;
+ }
+
+ devlink_resource_occ_get_register(devlink,
+ NSIM_RESOURCE_IPV4_FIB,
+ nsim_ipv4_fib_resource_occ_get,
+ net);
+ devlink_resource_occ_get_register(devlink,
+ NSIM_RESOURCE_IPV4_FIB_RULES,
+ nsim_ipv4_fib_rules_res_occ_get,
+ net);
+ devlink_resource_occ_get_register(devlink,
+ NSIM_RESOURCE_IPV6_FIB,
+ nsim_ipv6_fib_resource_occ_get,
+ net);
+ devlink_resource_occ_get_register(devlink,
+ NSIM_RESOURCE_IPV6_FIB_RULES,
+ nsim_ipv6_fib_rules_res_occ_get,
+ net);
+out:
+ return err;
+}
+
+static int nsim_devlink_reload(struct devlink *devlink)
+{
+ enum nsim_resource_id res_ids[] = {
+ NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES,
+ NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES
+ };
+ struct net *net = nsim_devlink_net(devlink);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(res_ids); ++i) {
+ int err;
+ u64 val;
+
+ err = devlink_resource_size_get(devlink, res_ids[i], &val);
+ if (!err) {
+ err = nsim_fib_set_max(net, res_ids[i], val);
+ if (err)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static void nsim_devlink_net_reset(struct net *net)
+{
+ enum nsim_resource_id res_ids[] = {
+ NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES,
+ NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(res_ids); ++i) {
+ if (nsim_fib_set_max(net, res_ids[i], (u64)-1)) {
+ pr_err("Failed to reset limit for resource %u\n",
+ res_ids[i]);
+ }
+ }
+}
+
+static const struct devlink_ops nsim_devlink_ops = {
+ .reload = nsim_devlink_reload,
+};
+
+/* once devlink / namespace issues are sorted out
+ * this needs to be net in which a devlink instance
+ * is to be created. e.g., dev_net(ns->netdev)
+ */
+static struct net *nsim_to_net(struct netdevsim *ns)
+{
+ return &init_net;
+}
+
+void nsim_devlink_teardown(struct netdevsim *ns)
+{
+ if (ns->devlink) {
+ struct net *net = nsim_to_net(ns);
+ bool *reg_devlink = net_generic(net, nsim_devlink_id);
+
+ devlink_unregister(ns->devlink);
+ devlink_free(ns->devlink);
+ ns->devlink = NULL;
+
+ nsim_devlink_net_reset(net);
+ *reg_devlink = true;
+ }
+}
+
+int nsim_devlink_setup(struct netdevsim *ns)
+{
+ struct net *net = nsim_to_net(ns);
+ bool *reg_devlink = net_generic(net, nsim_devlink_id);
+ struct devlink *devlink;
+ int err;
+
+ /* only one device per namespace controls devlink */
+ if (!*reg_devlink) {
+ ns->devlink = NULL;
+ return 0;
+ }
+
+ devlink = devlink_alloc(&nsim_devlink_ops, 0);
+ if (!devlink)
+ return -ENOMEM;
+
+ err = devlink_register(devlink, &ns->dev);
+ if (err)
+ goto err_devlink_free;
+
+ err = devlink_resources_register(devlink);
+ if (err)
+ goto err_dl_unregister;
+
+ ns->devlink = devlink;
+
+ *reg_devlink = false;
+
+ return 0;
+
+err_dl_unregister:
+ devlink_unregister(devlink);
+err_devlink_free:
+ devlink_free(devlink);
+
+ return err;
+}
+
+/* Initialize per network namespace state */
+static int __net_init nsim_devlink_netns_init(struct net *net)
+{
+ bool *reg_devlink = net_generic(net, nsim_devlink_id);
+
+ *reg_devlink = true;
+
+ return 0;
+}
+
+static struct pernet_operations nsim_devlink_net_ops = {
+ .init = nsim_devlink_netns_init,
+ .id = &nsim_devlink_id,
+ .size = sizeof(bool),
+};
+
+void nsim_devlink_exit(void)
+{
+ unregister_pernet_subsys(&nsim_devlink_net_ops);
+ nsim_fib_exit();
+}
+
+int nsim_devlink_init(void)
+{
+ int err;
+
+ err = nsim_fib_init();
+ if (err)
+ goto err_out;
+
+ err = register_pernet_subsys(&nsim_devlink_net_ops);
+ if (err)
+ nsim_fib_exit();
+
+err_out:
+ return err;
+}
diff --git a/drivers/net/netdevsim/fib.c b/drivers/net/netdevsim/fib.c
new file mode 100644
index 000000000000..9bfe9e151e13
--- /dev/null
+++ b/drivers/net/netdevsim/fib.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2018 Cumulus Networks. All rights reserved.
+ * Copyright (c) 2018 David Ahern <dsa@cumulusnetworks.com>
+ *
+ * This software is licensed under the GNU General License Version 2,
+ * June 1991 as shown in the file COPYING in the top-level directory of this
+ * source tree.
+ *
+ * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
+ * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
+ * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
+ * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+ */
+
+#include <net/fib_notifier.h>
+#include <net/ip_fib.h>
+#include <net/ip6_fib.h>
+#include <net/fib_rules.h>
+#include <net/netns/generic.h>
+
+#include "netdevsim.h"
+
+struct nsim_fib_entry {
+ u64 max;
+ u64 num;
+};
+
+struct nsim_per_fib_data {
+ struct nsim_fib_entry fib;
+ struct nsim_fib_entry rules;
+};
+
+struct nsim_fib_data {
+ struct nsim_per_fib_data ipv4;
+ struct nsim_per_fib_data ipv6;
+};
+
+static unsigned int nsim_fib_net_id;
+
+u64 nsim_fib_get_val(struct net *net, enum nsim_resource_id res_id, bool max)
+{
+ struct nsim_fib_data *fib_data = net_generic(net, nsim_fib_net_id);
+ struct nsim_fib_entry *entry;
+
+ switch (res_id) {
+ case NSIM_RESOURCE_IPV4_FIB:
+ entry = &fib_data->ipv4.fib;
+ break;
+ case NSIM_RESOURCE_IPV4_FIB_RULES:
+ entry = &fib_data->ipv4.rules;
+ break;
+ case NSIM_RESOURCE_IPV6_FIB:
+ entry = &fib_data->ipv6.fib;
+ break;
+ case NSIM_RESOURCE_IPV6_FIB_RULES:
+ entry = &fib_data->ipv6.rules;
+ break;
+ default:
+ return 0;
+ }
+
+ return max ? entry->max : entry->num;
+}
+
+int nsim_fib_set_max(struct net *net, enum nsim_resource_id res_id, u64 val)
+{
+ struct nsim_fib_data *fib_data = net_generic(net, nsim_fib_net_id);
+ struct nsim_fib_entry *entry;
+ int err = 0;
+
+ switch (res_id) {
+ case NSIM_RESOURCE_IPV4_FIB:
+ entry = &fib_data->ipv4.fib;
+ break;
+ case NSIM_RESOURCE_IPV4_FIB_RULES:
+ entry = &fib_data->ipv4.rules;
+ break;
+ case NSIM_RESOURCE_IPV6_FIB:
+ entry = &fib_data->ipv6.fib;
+ break;
+ case NSIM_RESOURCE_IPV6_FIB_RULES:
+ entry = &fib_data->ipv6.rules;
+ break;
+ default:
+ return 0;
+ }
+
+ /* not allowing a new max to be less than curren occupancy
+ * --> no means of evicting entries
+ */
+ if (val < entry->num)
+ err = -EINVAL;
+ else
+ entry->max = val;
+
+ return err;
+}
+
+static int nsim_fib_rule_account(struct nsim_fib_entry *entry, bool add,
+ struct netlink_ext_ack *extack)
+{
+ int err = 0;
+
+ if (add) {
+ if (entry->num < entry->max) {
+ entry->num++;
+ } else {
+ err = -ENOSPC;
+ NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib rule entries");
+ }
+ } else {
+ entry->num--;
+ }
+
+ return err;
+}
+
+static int nsim_fib_rule_event(struct fib_notifier_info *info, bool add)
+{
+ struct nsim_fib_data *data = net_generic(info->net, nsim_fib_net_id);
+ struct netlink_ext_ack *extack = info->extack;
+ int err = 0;
+
+ switch (info->family) {
+ case AF_INET:
+ err = nsim_fib_rule_account(&data->ipv4.rules, add, extack);
+ break;
+ case AF_INET6:
+ err = nsim_fib_rule_account(&data->ipv6.rules, add, extack);
+ break;
+ }
+
+ return err;
+}
+
+static int nsim_fib_account(struct nsim_fib_entry *entry, bool add,
+ struct netlink_ext_ack *extack)
+{
+ int err = 0;
+
+ if (add) {
+ if (entry->num < entry->max) {
+ entry->num++;
+ } else {
+ err = -ENOSPC;
+ NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries");
+ }
+ } else {
+ entry->num--;
+ }
+
+ return err;
+}
+
+static int nsim_fib_event(struct fib_notifier_info *info, bool add)
+{
+ struct nsim_fib_data *data = net_generic(info->net, nsim_fib_net_id);
+ struct netlink_ext_ack *extack = info->extack;
+ int err = 0;
+
+ switch (info->family) {
+ case AF_INET:
+ err = nsim_fib_account(&data->ipv4.fib, add, extack);
+ break;
+ case AF_INET6:
+ err = nsim_fib_account(&data->ipv6.fib, add, extack);
+ break;
+ }
+
+ return err;
+}
+
+static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event,
+ void *ptr)
+{
+ struct fib_notifier_info *info = ptr;
+ int err = 0;
+
+ switch (event) {
+ case FIB_EVENT_RULE_ADD: /* fall through */
+ case FIB_EVENT_RULE_DEL:
+ err = nsim_fib_rule_event(info, event == FIB_EVENT_RULE_ADD);
+ break;
+
+ case FIB_EVENT_ENTRY_ADD: /* fall through */
+ case FIB_EVENT_ENTRY_DEL:
+ err = nsim_fib_event(info, event == FIB_EVENT_ENTRY_ADD);
+ break;
+ }
+
+ return notifier_from_errno(err);
+}
+
+/* inconsistent dump, trying again */
+static void nsim_fib_dump_inconsistent(struct notifier_block *nb)
+{
+ struct nsim_fib_data *data;
+ struct net *net;
+
+ rcu_read_lock();
+ for_each_net_rcu(net) {
+ data = net_generic(net, nsim_fib_net_id);
+
+ data->ipv4.fib.num = 0ULL;
+ data->ipv4.rules.num = 0ULL;
+
+ data->ipv6.fib.num = 0ULL;
+ data->ipv6.rules.num = 0ULL;
+ }
+ rcu_read_unlock();
+}
+
+static struct notifier_block nsim_fib_nb = {
+ .notifier_call = nsim_fib_event_nb,
+};
+
+/* Initialize per network namespace state */
+static int __net_init nsim_fib_netns_init(struct net *net)
+{
+ struct nsim_fib_data *data = net_generic(net, nsim_fib_net_id);
+
+ data->ipv4.fib.max = (u64)-1;
+ data->ipv4.rules.max = (u64)-1;
+
+ data->ipv6.fib.max = (u64)-1;
+ data->ipv6.rules.max = (u64)-1;
+
+ return 0;
+}
+
+static struct pernet_operations nsim_fib_net_ops = {
+ .init = nsim_fib_netns_init,
+ .id = &nsim_fib_net_id,
+ .size = sizeof(struct nsim_fib_data),
+};
+
+void nsim_fib_exit(void)
+{
+ unregister_pernet_subsys(&nsim_fib_net_ops);
+ unregister_fib_notifier(&nsim_fib_nb);
+}
+
+int nsim_fib_init(void)
+{
+ int err;
+
+ err = register_pernet_subsys(&nsim_fib_net_ops);
+ if (err < 0) {
+ pr_err("Failed to register pernet subsystem\n");
+ goto err_out;
+ }
+
+ err = register_fib_notifier(&nsim_fib_nb, nsim_fib_dump_inconsistent);
+ if (err < 0) {
+ pr_err("Failed to register fib notifier\n");
+ goto err_out;
+ }
+
+err_out:
+ return err;
+}
diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c
index 3fd567928f3d..ec68f38213d9 100644
--- a/drivers/net/netdevsim/netdev.c
+++ b/drivers/net/netdevsim/netdev.c
@@ -167,8 +167,14 @@ static int nsim_init(struct net_device *dev)
SET_NETDEV_DEV(dev, &ns->dev);
+ err = nsim_devlink_setup(ns);
+ if (err)
+ goto err_unreg_dev;
+
return 0;
+err_unreg_dev:
+ device_unregister(&ns->dev);
err_bpf_uninit:
nsim_bpf_uninit(ns);
err_debugfs_destroy:
@@ -180,6 +186,7 @@ static void nsim_uninit(struct net_device *dev)
{
struct netdevsim *ns = netdev_priv(dev);
+ nsim_devlink_teardown(ns);
debugfs_remove_recursive(ns->ddir);
nsim_bpf_uninit(ns);
}
@@ -478,12 +485,18 @@ static int __init nsim_module_init(void)
if (err)
goto err_debugfs_destroy;
- err = rtnl_link_register(&nsim_link_ops);
+ err = nsim_devlink_init();
if (err)
goto err_unreg_bus;
+ err = rtnl_link_register(&nsim_link_ops);
+ if (err)
+ goto err_dl_fini;
+
return 0;
+err_dl_fini:
+ nsim_devlink_exit();
err_unreg_bus:
bus_unregister(&nsim_bus);
err_debugfs_destroy:
@@ -494,6 +507,7 @@ err_debugfs_destroy:
static void __exit nsim_module_exit(void)
{
rtnl_link_unregister(&nsim_link_ops);
+ nsim_devlink_exit();
bus_unregister(&nsim_bus);
debugfs_remove_recursive(nsim_ddir);
}
diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h
index ea081c10efb8..3a8581af3b85 100644
--- a/drivers/net/netdevsim/netdevsim.h
+++ b/drivers/net/netdevsim/netdevsim.h
@@ -64,6 +64,9 @@ struct netdevsim {
bool bpf_map_accept;
struct list_head bpf_bound_maps;
+#if IS_ENABLED(CONFIG_NET_DEVLINK)
+ struct devlink *devlink;
+#endif
};
extern struct dentry *nsim_ddir;
@@ -103,6 +106,47 @@ nsim_bpf_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
}
#endif
+#if IS_ENABLED(CONFIG_NET_DEVLINK)
+enum nsim_resource_id {
+ NSIM_RESOURCE_NONE, /* DEVLINK_RESOURCE_ID_PARENT_TOP */
+ NSIM_RESOURCE_IPV4,
+ NSIM_RESOURCE_IPV4_FIB,
+ NSIM_RESOURCE_IPV4_FIB_RULES,
+ NSIM_RESOURCE_IPV6,
+ NSIM_RESOURCE_IPV6_FIB,
+ NSIM_RESOURCE_IPV6_FIB_RULES,
+};
+
+int nsim_devlink_setup(struct netdevsim *ns);
+void nsim_devlink_teardown(struct netdevsim *ns);
+
+int nsim_devlink_init(void);
+void nsim_devlink_exit(void);
+
+int nsim_fib_init(void);
+void nsim_fib_exit(void);
+u64 nsim_fib_get_val(struct net *net, enum nsim_resource_id res_id, bool max);
+int nsim_fib_set_max(struct net *net, enum nsim_resource_id res_id, u64 val);
+#else
+static inline int nsim_devlink_setup(struct netdevsim *ns)
+{
+ return 0;
+}
+
+static inline void nsim_devlink_teardown(struct netdevsim *ns)
+{
+}
+
+static inline int nsim_devlink_init(void)
+{
+ return 0;
+}
+
+static inline void nsim_devlink_exit(void)
+{
+}
+#endif
+
static inline struct netdevsim *to_nsim(struct device *ptr)
{
return container_of(ptr, struct netdevsim, dev);
diff --git a/drivers/net/phy/aquantia.c b/drivers/net/phy/aquantia.c
index e8ae50e1255e..319edc9c8ec7 100644
--- a/drivers/net/phy/aquantia.c
+++ b/drivers/net/phy/aquantia.c
@@ -38,14 +38,6 @@ static int aquantia_config_aneg(struct phy_device *phydev)
return 0;
}
-static int aquantia_aneg_done(struct phy_device *phydev)
-{
- int reg;
-
- reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
- return (reg < 0) ? reg : (reg & BMSR_ANEGCOMPLETE);
-}
-
static int aquantia_config_intr(struct phy_device *phydev)
{
int err;
@@ -125,7 +117,7 @@ static struct phy_driver aquantia_driver[] = {
.name = "Aquantia AQ1202",
.features = PHY_AQUANTIA_FEATURES,
.flags = PHY_HAS_INTERRUPT,
- .aneg_done = aquantia_aneg_done,
+ .aneg_done = genphy_c45_aneg_done,
.config_aneg = aquantia_config_aneg,
.config_intr = aquantia_config_intr,
.ack_interrupt = aquantia_ack_interrupt,
@@ -137,7 +129,7 @@ static struct phy_driver aquantia_driver[] = {
.name = "Aquantia AQ2104",
.features = PHY_AQUANTIA_FEATURES,
.flags = PHY_HAS_INTERRUPT,
- .aneg_done = aquantia_aneg_done,
+ .aneg_done = genphy_c45_aneg_done,
.config_aneg = aquantia_config_aneg,
.config_intr = aquantia_config_intr,
.ack_interrupt = aquantia_ack_interrupt,
@@ -149,7 +141,7 @@ static struct phy_driver aquantia_driver[] = {
.name = "Aquantia AQR105",
.features = PHY_AQUANTIA_FEATURES,
.flags = PHY_HAS_INTERRUPT,
- .aneg_done = aquantia_aneg_done,
+ .aneg_done = genphy_c45_aneg_done,
.config_aneg = aquantia_config_aneg,
.config_intr = aquantia_config_intr,
.ack_interrupt = aquantia_ack_interrupt,
@@ -161,7 +153,7 @@ static struct phy_driver aquantia_driver[] = {
.name = "Aquantia AQR106",
.features = PHY_AQUANTIA_FEATURES,
.flags = PHY_HAS_INTERRUPT,
- .aneg_done = aquantia_aneg_done,
+ .aneg_done = genphy_c45_aneg_done,
.config_aneg = aquantia_config_aneg,
.config_intr = aquantia_config_intr,
.ack_interrupt = aquantia_ack_interrupt,
@@ -173,7 +165,7 @@ static struct phy_driver aquantia_driver[] = {
.name = "Aquantia AQR107",
.features = PHY_AQUANTIA_FEATURES,
.flags = PHY_HAS_INTERRUPT,
- .aneg_done = aquantia_aneg_done,
+ .aneg_done = genphy_c45_aneg_done,
.config_aneg = aquantia_config_aneg,
.config_intr = aquantia_config_intr,
.ack_interrupt = aquantia_ack_interrupt,
@@ -185,7 +177,7 @@ static struct phy_driver aquantia_driver[] = {
.name = "Aquantia AQR405",
.features = PHY_AQUANTIA_FEATURES,
.flags = PHY_HAS_INTERRUPT,
- .aneg_done = aquantia_aneg_done,
+ .aneg_done = genphy_c45_aneg_done,
.config_aneg = aquantia_config_aneg,
.config_intr = aquantia_config_intr,
.ack_interrupt = aquantia_ack_interrupt,
diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c
index 421feb8f92fe..29b1c88b55cc 100644
--- a/drivers/net/phy/bcm7xxx.c
+++ b/drivers/net/phy/bcm7xxx.c
@@ -565,7 +565,7 @@ static int bcm7xxx_28nm_set_tunable(struct phy_device *phydev,
if (ret)
return ret;
- /* Disable EEE advertisment since this prevents the PHY
+ /* Disable EEE advertisement since this prevents the PHY
* from successfully linking up, trigger auto-negotiation restart
* to let the MAC decide what to do.
*/
diff --git a/drivers/net/phy/cortina.c b/drivers/net/phy/cortina.c
index 9442db221834..8022cd317f62 100644
--- a/drivers/net/phy/cortina.c
+++ b/drivers/net/phy/cortina.c
@@ -30,14 +30,6 @@ static int cortina_read_reg(struct phy_device *phydev, u16 regnum)
MII_ADDR_C45 | regnum);
}
-static int cortina_config_aneg(struct phy_device *phydev)
-{
- phydev->supported = SUPPORTED_10000baseT_Full;
- phydev->advertising = SUPPORTED_10000baseT_Full;
-
- return 0;
-}
-
static int cortina_read_status(struct phy_device *phydev)
{
int gpio_int_status, ret = 0;
@@ -61,11 +53,6 @@ err:
return ret;
}
-static int cortina_soft_reset(struct phy_device *phydev)
-{
- return 0;
-}
-
static int cortina_probe(struct phy_device *phydev)
{
u32 phy_id = 0;
@@ -101,9 +88,10 @@ static struct phy_driver cortina_driver[] = {
.phy_id = PHY_ID_CS4340,
.phy_id_mask = 0xffffffff,
.name = "Cortina CS4340",
- .config_aneg = cortina_config_aneg,
+ .config_init = gen10g_config_init,
+ .config_aneg = gen10g_config_aneg,
.read_status = cortina_read_status,
- .soft_reset = cortina_soft_reset,
+ .soft_reset = gen10g_no_soft_reset,
.probe = cortina_probe,
},
};
diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c
index 654f42d00092..a6c87793d899 100644
--- a/drivers/net/phy/dp83640.c
+++ b/drivers/net/phy/dp83640.c
@@ -1207,6 +1207,23 @@ static void dp83640_remove(struct phy_device *phydev)
kfree(dp83640);
}
+static int dp83640_soft_reset(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = genphy_soft_reset(phydev);
+ if (ret < 0)
+ return ret;
+
+ /* From DP83640 datasheet: "Software driver code must wait 3 us
+ * following a software reset before allowing further serial MII
+ * operations with the DP83640."
+ */
+ udelay(10); /* Taking udelay inaccuracy into account */
+
+ return 0;
+}
+
static int dp83640_config_init(struct phy_device *phydev)
{
struct dp83640_private *dp83640 = phydev->priv;
@@ -1501,6 +1518,7 @@ static struct phy_driver dp83640_driver = {
.flags = PHY_HAS_INTERRUPT,
.probe = dp83640_probe,
.remove = dp83640_remove,
+ .soft_reset = dp83640_soft_reset,
.config_init = dp83640_config_init,
.ack_interrupt = dp83640_ack_interrupt,
.config_intr = dp83640_config_intr,
diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c
index ab58224f897f..b3935778b19f 100644
--- a/drivers/net/phy/dp83867.c
+++ b/drivers/net/phy/dp83867.c
@@ -75,6 +75,8 @@
#define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MAX 0x0
#define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MIN 0x1f
+#define DP83867_IO_MUX_CFG_CLK_O_SEL_MASK (0x1f << 8)
+#define DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT 8
/* CFG4 bits */
#define DP83867_CFG4_PORT_MIRROR_EN BIT(0)
@@ -92,6 +94,7 @@ struct dp83867_private {
int io_impedance;
int port_mirroring;
bool rxctrl_strap_quirk;
+ int clk_output_sel;
};
static int dp83867_ack_interrupt(struct phy_device *phydev)
@@ -160,6 +163,14 @@ static int dp83867_of_init(struct phy_device *phydev)
dp83867->io_impedance = -EINVAL;
/* Optional configuration */
+ ret = of_property_read_u32(of_node, "ti,clk-output-sel",
+ &dp83867->clk_output_sel);
+ if (ret || dp83867->clk_output_sel > DP83867_CLK_O_SEL_REF_CLK)
+ /* Keep the default value if ti,clk-output-sel is not set
+ * or too high
+ */
+ dp83867->clk_output_sel = DP83867_CLK_O_SEL_REF_CLK;
+
if (of_property_read_bool(of_node, "ti,max-output-impedance"))
dp83867->io_impedance = DP83867_IO_MUX_CFG_IO_IMPEDANCE_MAX;
else if (of_property_read_bool(of_node, "ti,min-output-impedance"))
@@ -295,6 +306,14 @@ static int dp83867_config_init(struct phy_device *phydev)
if (dp83867->port_mirroring != DP83867_PORT_MIRROING_KEEP)
dp83867_config_port_mirroring(phydev);
+ /* Clock output selection if muxing property is set */
+ if (dp83867->clk_output_sel != DP83867_CLK_O_SEL_REF_CLK) {
+ val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_IO_MUX_CFG);
+ val &= ~DP83867_IO_MUX_CFG_CLK_O_SEL_MASK;
+ val |= (dp83867->clk_output_sel << DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT);
+ phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_IO_MUX_CFG, val);
+ }
+
return 0;
}
diff --git a/drivers/net/phy/intel-xway.c b/drivers/net/phy/intel-xway.c
index a11f80cb5388..7d936fb61c22 100644
--- a/drivers/net/phy/intel-xway.c
+++ b/drivers/net/phy/intel-xway.c
@@ -149,8 +149,10 @@
#define PHY_ID_PHY22F_1_4 0xD565A410
#define PHY_ID_PHY11G_1_5 0xD565A401
#define PHY_ID_PHY22F_1_5 0xD565A411
-#define PHY_ID_PHY11G_VR9 0xD565A409
-#define PHY_ID_PHY22F_VR9 0xD565A419
+#define PHY_ID_PHY11G_VR9_1_1 0xD565A408
+#define PHY_ID_PHY22F_VR9_1_1 0xD565A418
+#define PHY_ID_PHY11G_VR9_1_2 0xD565A409
+#define PHY_ID_PHY22F_VR9_1_2 0xD565A419
static int xway_gphy_config_init(struct phy_device *phydev)
{
@@ -312,9 +314,9 @@ static struct phy_driver xway_gphy[] = {
.suspend = genphy_suspend,
.resume = genphy_resume,
}, {
- .phy_id = PHY_ID_PHY11G_VR9,
+ .phy_id = PHY_ID_PHY11G_VR9_1_1,
.phy_id_mask = 0xffffffff,
- .name = "Intel XWAY PHY11G (xRX integrated)",
+ .name = "Intel XWAY PHY11G (xRX v1.1 integrated)",
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
.config_init = xway_gphy_config_init,
@@ -324,9 +326,33 @@ static struct phy_driver xway_gphy[] = {
.suspend = genphy_suspend,
.resume = genphy_resume,
}, {
- .phy_id = PHY_ID_PHY22F_VR9,
+ .phy_id = PHY_ID_PHY22F_VR9_1_1,
.phy_id_mask = 0xffffffff,
- .name = "Intel XWAY PHY22F (xRX integrated)",
+ .name = "Intel XWAY PHY22F (xRX v1.1 integrated)",
+ .features = PHY_BASIC_FEATURES,
+ .flags = PHY_HAS_INTERRUPT,
+ .config_init = xway_gphy_config_init,
+ .ack_interrupt = xway_gphy_ack_interrupt,
+ .did_interrupt = xway_gphy_did_interrupt,
+ .config_intr = xway_gphy_config_intr,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ }, {
+ .phy_id = PHY_ID_PHY11G_VR9_1_2,
+ .phy_id_mask = 0xffffffff,
+ .name = "Intel XWAY PHY11G (xRX v1.2 integrated)",
+ .features = PHY_GBIT_FEATURES,
+ .flags = PHY_HAS_INTERRUPT,
+ .config_init = xway_gphy_config_init,
+ .ack_interrupt = xway_gphy_ack_interrupt,
+ .did_interrupt = xway_gphy_did_interrupt,
+ .config_intr = xway_gphy_config_intr,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ }, {
+ .phy_id = PHY_ID_PHY22F_VR9_1_2,
+ .phy_id_mask = 0xffffffff,
+ .name = "Intel XWAY PHY22F (xRX v1.2 integrated)",
.features = PHY_BASIC_FEATURES,
.flags = PHY_HAS_INTERRUPT,
.config_init = xway_gphy_config_init,
@@ -346,8 +372,10 @@ static struct mdio_device_id __maybe_unused xway_gphy_tbl[] = {
{ PHY_ID_PHY22F_1_4, 0xffffffff },
{ PHY_ID_PHY11G_1_5, 0xffffffff },
{ PHY_ID_PHY22F_1_5, 0xffffffff },
- { PHY_ID_PHY11G_VR9, 0xffffffff },
- { PHY_ID_PHY22F_VR9, 0xffffffff },
+ { PHY_ID_PHY11G_VR9_1_1, 0xffffffff },
+ { PHY_ID_PHY22F_VR9_1_1, 0xffffffff },
+ { PHY_ID_PHY11G_VR9_1_2, 0xffffffff },
+ { PHY_ID_PHY22F_VR9_1_2, 0xffffffff },
{ }
};
MODULE_DEVICE_TABLE(mdio, xway_gphy_tbl);
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
index 0e0978d8a0eb..25e2a099b71c 100644
--- a/drivers/net/phy/marvell.c
+++ b/drivers/net/phy/marvell.c
@@ -828,6 +828,22 @@ static int m88e1121_config_init(struct phy_device *phydev)
return marvell_config_init(phydev);
}
+static int m88e1318_config_init(struct phy_device *phydev)
+{
+ if (phy_interrupt_is_valid(phydev)) {
+ int err = phy_modify_paged(
+ phydev, MII_MARVELL_LED_PAGE,
+ MII_88E1318S_PHY_LED_TCR,
+ MII_88E1318S_PHY_LED_TCR_FORCE_INT,
+ MII_88E1318S_PHY_LED_TCR_INTn_ENABLE |
+ MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW);
+ if (err < 0)
+ return err;
+ }
+
+ return m88e1121_config_init(phydev);
+}
+
static int m88e1510_config_init(struct phy_device *phydev)
{
int err;
@@ -860,7 +876,7 @@ static int m88e1510_config_init(struct phy_device *phydev)
return err;
/* There appears to be a bug in the 88e1512 when used in
- * SGMII to copper mode, where the AN advertisment register
+ * SGMII to copper mode, where the AN advertisement register
* clears the pause bits each time a negotiation occurs.
* This means we can never be truely sure what was advertised,
* so disable Pause support.
@@ -870,7 +886,7 @@ static int m88e1510_config_init(struct phy_device *phydev)
phydev->advertising &= ~pause;
}
- return m88e1121_config_init(phydev);
+ return m88e1318_config_init(phydev);
}
static int m88e1118_config_aneg(struct phy_device *phydev)
@@ -1377,6 +1393,15 @@ static int m88e1318_set_wol(struct phy_device *phydev,
if (err < 0)
goto error;
+ /* If WOL event happened once, the LED[2] interrupt pin
+ * will not be cleared unless we reading the interrupt status
+ * register. If interrupts are in use, the normal interrupt
+ * handling will clear the WOL event. Clear the WOL event
+ * before enabling it if !phy_interrupt_is_valid()
+ */
+ if (!phy_interrupt_is_valid(phydev))
+ phy_read(phydev, MII_M1011_IEVENT);
+
/* Enable the WOL interrupt */
err = __phy_modify(phydev, MII_88E1318S_PHY_CSIER, 0,
MII_88E1318S_PHY_CSIER_WOL_EIE);
@@ -2086,7 +2111,7 @@ static struct phy_driver marvell_drivers[] = {
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
.probe = marvell_probe,
- .config_init = &m88e1121_config_init,
+ .config_init = &m88e1318_config_init,
.config_aneg = &m88e1318_config_aneg,
.read_status = &marvell_read_status,
.ack_interrupt = &marvell_ack_interrupt,
diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c
index 8a0bd98fdec7..f77a2d9e7f9d 100644
--- a/drivers/net/phy/marvell10g.c
+++ b/drivers/net/phy/marvell10g.c
@@ -21,8 +21,10 @@
* If both the fiber and copper ports are connected, the first to gain
* link takes priority and the other port is completely locked out.
*/
-#include <linux/phy.h>
+#include <linux/ctype.h>
+#include <linux/hwmon.h>
#include <linux/marvell_phy.h>
+#include <linux/phy.h>
enum {
MV_PCS_BASE_T = 0x0000,
@@ -40,6 +42,19 @@ enum {
*/
MV_AN_CTRL1000 = 0x8000, /* 1000base-T control register */
MV_AN_STAT1000 = 0x8001, /* 1000base-T status register */
+
+ /* Vendor2 MMD registers */
+ MV_V2_TEMP_CTRL = 0xf08a,
+ MV_V2_TEMP_CTRL_MASK = 0xc000,
+ MV_V2_TEMP_CTRL_SAMPLE = 0x0000,
+ MV_V2_TEMP_CTRL_DISABLE = 0xc000,
+ MV_V2_TEMP = 0xf08c,
+ MV_V2_TEMP_UNKNOWN = 0x9600, /* unknown function */
+};
+
+struct mv3310_priv {
+ struct device *hwmon_dev;
+ char *hwmon_name;
};
static int mv3310_modify(struct phy_device *phydev, int devad, u16 reg,
@@ -60,26 +75,180 @@ static int mv3310_modify(struct phy_device *phydev, int devad, u16 reg,
return ret < 0 ? ret : 1;
}
+#ifdef CONFIG_HWMON
+static umode_t mv3310_hwmon_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ if (type == hwmon_chip && attr == hwmon_chip_update_interval)
+ return 0444;
+ if (type == hwmon_temp && attr == hwmon_temp_input)
+ return 0444;
+ return 0;
+}
+
+static int mv3310_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *value)
+{
+ struct phy_device *phydev = dev_get_drvdata(dev);
+ int temp;
+
+ if (type == hwmon_chip && attr == hwmon_chip_update_interval) {
+ *value = MSEC_PER_SEC;
+ return 0;
+ }
+
+ if (type == hwmon_temp && attr == hwmon_temp_input) {
+ temp = phy_read_mmd(phydev, MDIO_MMD_VEND2, MV_V2_TEMP);
+ if (temp < 0)
+ return temp;
+
+ *value = ((temp & 0xff) - 75) * 1000;
+
+ return 0;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static const struct hwmon_ops mv3310_hwmon_ops = {
+ .is_visible = mv3310_hwmon_is_visible,
+ .read = mv3310_hwmon_read,
+};
+
+static u32 mv3310_hwmon_chip_config[] = {
+ HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL,
+ 0,
+};
+
+static const struct hwmon_channel_info mv3310_hwmon_chip = {
+ .type = hwmon_chip,
+ .config = mv3310_hwmon_chip_config,
+};
+
+static u32 mv3310_hwmon_temp_config[] = {
+ HWMON_T_INPUT,
+ 0,
+};
+
+static const struct hwmon_channel_info mv3310_hwmon_temp = {
+ .type = hwmon_temp,
+ .config = mv3310_hwmon_temp_config,
+};
+
+static const struct hwmon_channel_info *mv3310_hwmon_info[] = {
+ &mv3310_hwmon_chip,
+ &mv3310_hwmon_temp,
+ NULL,
+};
+
+static const struct hwmon_chip_info mv3310_hwmon_chip_info = {
+ .ops = &mv3310_hwmon_ops,
+ .info = mv3310_hwmon_info,
+};
+
+static int mv3310_hwmon_config(struct phy_device *phydev, bool enable)
+{
+ u16 val;
+ int ret;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_V2_TEMP,
+ MV_V2_TEMP_UNKNOWN);
+ if (ret < 0)
+ return ret;
+
+ val = enable ? MV_V2_TEMP_CTRL_SAMPLE : MV_V2_TEMP_CTRL_DISABLE;
+ ret = mv3310_modify(phydev, MDIO_MMD_VEND2, MV_V2_TEMP_CTRL,
+ MV_V2_TEMP_CTRL_MASK, val);
+
+ return ret < 0 ? ret : 0;
+}
+
+static void mv3310_hwmon_disable(void *data)
+{
+ struct phy_device *phydev = data;
+
+ mv3310_hwmon_config(phydev, false);
+}
+
+static int mv3310_hwmon_probe(struct phy_device *phydev)
+{
+ struct device *dev = &phydev->mdio.dev;
+ struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
+ int i, j, ret;
+
+ priv->hwmon_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL);
+ if (!priv->hwmon_name)
+ return -ENODEV;
+
+ for (i = j = 0; priv->hwmon_name[i]; i++) {
+ if (isalnum(priv->hwmon_name[i])) {
+ if (i != j)
+ priv->hwmon_name[j] = priv->hwmon_name[i];
+ j++;
+ }
+ }
+ priv->hwmon_name[j] = '\0';
+
+ ret = mv3310_hwmon_config(phydev, true);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, mv3310_hwmon_disable, phydev);
+ if (ret)
+ return ret;
+
+ priv->hwmon_dev = devm_hwmon_device_register_with_info(dev,
+ priv->hwmon_name, phydev,
+ &mv3310_hwmon_chip_info, NULL);
+
+ return PTR_ERR_OR_ZERO(priv->hwmon_dev);
+}
+#else
+static inline int mv3310_hwmon_config(struct phy_device *phydev, bool enable)
+{
+ return 0;
+}
+
+static int mv3310_hwmon_probe(struct phy_device *phydev)
+{
+ return 0;
+}
+#endif
+
static int mv3310_probe(struct phy_device *phydev)
{
+ struct mv3310_priv *priv;
u32 mmd_mask = MDIO_DEVS_PMAPMD | MDIO_DEVS_AN;
+ int ret;
if (!phydev->is_c45 ||
(phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask)
return -ENODEV;
+ priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(&phydev->mdio.dev, priv);
+
+ ret = mv3310_hwmon_probe(phydev);
+ if (ret)
+ return ret;
+
return 0;
}
-/*
- * Resetting the MV88X3310 causes it to become non-responsive. Avoid
- * setting the reset bit(s).
- */
-static int mv3310_soft_reset(struct phy_device *phydev)
+static int mv3310_suspend(struct phy_device *phydev)
{
return 0;
}
+static int mv3310_resume(struct phy_device *phydev)
+{
+ return mv3310_hwmon_config(phydev, true);
+}
+
static int mv3310_config_init(struct phy_device *phydev)
{
__ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = { 0, };
@@ -317,7 +486,7 @@ static int mv3310_read_status(struct phy_device *phydev)
if (val < 0)
return val;
- /* Read the link partner's 1G advertisment */
+ /* Read the link partner's 1G advertisement */
val = phy_read_mmd(phydev, MDIO_MMD_AN, MV_AN_STAT1000);
if (val < 0)
return val;
@@ -376,9 +545,11 @@ static struct phy_driver mv3310_drivers[] = {
SUPPORTED_FIBRE |
SUPPORTED_10000baseT_Full |
SUPPORTED_Backplane,
- .probe = mv3310_probe,
- .soft_reset = mv3310_soft_reset,
+ .soft_reset = gen10g_no_soft_reset,
.config_init = mv3310_config_init,
+ .probe = mv3310_probe,
+ .suspend = mv3310_suspend,
+ .resume = mv3310_resume,
.config_aneg = mv3310_config_aneg,
.aneg_done = mv3310_aneg_done,
.read_status = mv3310_read_status,
diff --git a/drivers/net/phy/mdio-bitbang.c b/drivers/net/phy/mdio-bitbang.c
index 61a543c788cc..403b085f0a89 100644
--- a/drivers/net/phy/mdio-bitbang.c
+++ b/drivers/net/phy/mdio-bitbang.c
@@ -113,7 +113,7 @@ static void mdiobb_cmd(struct mdiobb_ctrl *ctrl, int op, u8 phy, u8 reg)
for (i = 0; i < 32; i++)
mdiobb_send_bit(ctrl, 1);
- /* send the start bit (01) and the read opcode (10) or write (10).
+ /* send the start bit (01) and the read opcode (10) or write (01).
Clause 45 operation uses 00 for the start and 11, 10 for
read/write */
mdiobb_send_bit(ctrl, 0);
diff --git a/drivers/net/phy/mdio-mux-mmioreg.c b/drivers/net/phy/mdio-mux-mmioreg.c
index 2573ab012f16..70f6115530af 100644
--- a/drivers/net/phy/mdio-mux-mmioreg.c
+++ b/drivers/net/phy/mdio-mux-mmioreg.c
@@ -163,8 +163,9 @@ static int mdio_mux_mmioreg_probe(struct platform_device *pdev)
mdio_mux_mmioreg_switch_fn,
&s->mux_handle, s, NULL);
if (ret) {
- dev_err(&pdev->dev, "failed to register mdio-mux bus %pOF\n",
- np);
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev,
+ "failed to register mdio-mux bus %pOF\n", np);
return ret;
}
diff --git a/drivers/net/phy/microchip.c b/drivers/net/phy/microchip.c
index 0f293ef28935..a97ac8c12c4c 100644
--- a/drivers/net/phy/microchip.c
+++ b/drivers/net/phy/microchip.c
@@ -20,6 +20,7 @@
#include <linux/ethtool.h>
#include <linux/phy.h>
#include <linux/microchipphy.h>
+#include <linux/delay.h>
#define DRIVER_AUTHOR "WOOJUNG HUH <woojung.huh@microchip.com>"
#define DRIVER_DESC "Microchip LAN88XX PHY driver"
@@ -30,6 +31,16 @@ struct lan88xx_priv {
__u32 wolopts;
};
+static int lan88xx_read_page(struct phy_device *phydev)
+{
+ return __phy_read(phydev, LAN88XX_EXT_PAGE_ACCESS);
+}
+
+static int lan88xx_write_page(struct phy_device *phydev, int page)
+{
+ return __phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, page);
+}
+
static int lan88xx_phy_config_intr(struct phy_device *phydev)
{
int rc;
@@ -66,6 +77,150 @@ static int lan88xx_suspend(struct phy_device *phydev)
return 0;
}
+static int lan88xx_TR_reg_set(struct phy_device *phydev, u16 regaddr,
+ u32 data)
+{
+ int val, save_page, ret = 0;
+ u16 buf;
+
+ /* Save current page */
+ save_page = phy_save_page(phydev);
+ if (save_page < 0) {
+ pr_warn("Failed to get current page\n");
+ goto err;
+ }
+
+ /* Switch to TR page */
+ lan88xx_write_page(phydev, LAN88XX_EXT_PAGE_ACCESS_TR);
+
+ ret = __phy_write(phydev, LAN88XX_EXT_PAGE_TR_LOW_DATA,
+ (data & 0xFFFF));
+ if (ret < 0) {
+ pr_warn("Failed to write TR low data\n");
+ goto err;
+ }
+
+ ret = __phy_write(phydev, LAN88XX_EXT_PAGE_TR_HIGH_DATA,
+ (data & 0x00FF0000) >> 16);
+ if (ret < 0) {
+ pr_warn("Failed to write TR high data\n");
+ goto err;
+ }
+
+ /* Config control bits [15:13] of register */
+ buf = (regaddr & ~(0x3 << 13));/* Clr [14:13] to write data in reg */
+ buf |= 0x8000; /* Set [15] to Packet transmit */
+
+ ret = __phy_write(phydev, LAN88XX_EXT_PAGE_TR_CR, buf);
+ if (ret < 0) {
+ pr_warn("Failed to write data in reg\n");
+ goto err;
+ }
+
+ usleep_range(1000, 2000);/* Wait for Data to be written */
+ val = __phy_read(phydev, LAN88XX_EXT_PAGE_TR_CR);
+ if (!(val & 0x8000))
+ pr_warn("TR Register[0x%X] configuration failed\n", regaddr);
+err:
+ return phy_restore_page(phydev, save_page, ret);
+}
+
+static void lan88xx_config_TR_regs(struct phy_device *phydev)
+{
+ int err;
+
+ /* Get access to Channel 0x1, Node 0xF , Register 0x01.
+ * Write 24-bit value 0x12B00A to register. Setting MrvlTrFix1000Kf,
+ * MrvlTrFix1000Kp, MasterEnableTR bits.
+ */
+ err = lan88xx_TR_reg_set(phydev, 0x0F82, 0x12B00A);
+ if (err < 0)
+ pr_warn("Failed to Set Register[0x0F82]\n");
+
+ /* Get access to Channel b'10, Node b'1101, Register 0x06.
+ * Write 24-bit value 0xD2C46F to register. Setting SSTrKf1000Slv,
+ * SSTrKp1000Mas bits.
+ */
+ err = lan88xx_TR_reg_set(phydev, 0x168C, 0xD2C46F);
+ if (err < 0)
+ pr_warn("Failed to Set Register[0x168C]\n");
+
+ /* Get access to Channel b'10, Node b'1111, Register 0x11.
+ * Write 24-bit value 0x620 to register. Setting rem_upd_done_thresh
+ * bits
+ */
+ err = lan88xx_TR_reg_set(phydev, 0x17A2, 0x620);
+ if (err < 0)
+ pr_warn("Failed to Set Register[0x17A2]\n");
+
+ /* Get access to Channel b'10, Node b'1101, Register 0x10.
+ * Write 24-bit value 0xEEFFDD to register. Setting
+ * eee_TrKp1Long_1000, eee_TrKp2Long_1000, eee_TrKp3Long_1000,
+ * eee_TrKp1Short_1000,eee_TrKp2Short_1000, eee_TrKp3Short_1000 bits.
+ */
+ err = lan88xx_TR_reg_set(phydev, 0x16A0, 0xEEFFDD);
+ if (err < 0)
+ pr_warn("Failed to Set Register[0x16A0]\n");
+
+ /* Get access to Channel b'10, Node b'1101, Register 0x13.
+ * Write 24-bit value 0x071448 to register. Setting
+ * slv_lpi_tr_tmr_val1, slv_lpi_tr_tmr_val2 bits.
+ */
+ err = lan88xx_TR_reg_set(phydev, 0x16A6, 0x071448);
+ if (err < 0)
+ pr_warn("Failed to Set Register[0x16A6]\n");
+
+ /* Get access to Channel b'10, Node b'1101, Register 0x12.
+ * Write 24-bit value 0x13132F to register. Setting
+ * slv_sigdet_timer_val1, slv_sigdet_timer_val2 bits.
+ */
+ err = lan88xx_TR_reg_set(phydev, 0x16A4, 0x13132F);
+ if (err < 0)
+ pr_warn("Failed to Set Register[0x16A4]\n");
+
+ /* Get access to Channel b'10, Node b'1101, Register 0x14.
+ * Write 24-bit value 0x0 to register. Setting eee_3level_delay,
+ * eee_TrKf_freeze_delay bits.
+ */
+ err = lan88xx_TR_reg_set(phydev, 0x16A8, 0x0);
+ if (err < 0)
+ pr_warn("Failed to Set Register[0x16A8]\n");
+
+ /* Get access to Channel b'01, Node b'1111, Register 0x34.
+ * Write 24-bit value 0x91B06C to register. Setting
+ * FastMseSearchThreshLong1000, FastMseSearchThreshShort1000,
+ * FastMseSearchUpdGain1000 bits.
+ */
+ err = lan88xx_TR_reg_set(phydev, 0x0FE8, 0x91B06C);
+ if (err < 0)
+ pr_warn("Failed to Set Register[0x0FE8]\n");
+
+ /* Get access to Channel b'01, Node b'1111, Register 0x3E.
+ * Write 24-bit value 0xC0A028 to register. Setting
+ * FastMseKp2ThreshLong1000, FastMseKp2ThreshShort1000,
+ * FastMseKp2UpdGain1000, FastMseKp2ExitEn1000 bits.
+ */
+ err = lan88xx_TR_reg_set(phydev, 0x0FFC, 0xC0A028);
+ if (err < 0)
+ pr_warn("Failed to Set Register[0x0FFC]\n");
+
+ /* Get access to Channel b'01, Node b'1111, Register 0x35.
+ * Write 24-bit value 0x041600 to register. Setting
+ * FastMseSearchPhShNum1000, FastMseSearchClksPerPh1000,
+ * FastMsePhChangeDelay1000 bits.
+ */
+ err = lan88xx_TR_reg_set(phydev, 0x0FEA, 0x041600);
+ if (err < 0)
+ pr_warn("Failed to Set Register[0x0FEA]\n");
+
+ /* Get access to Channel b'10, Node b'1101, Register 0x03.
+ * Write 24-bit value 0x000004 to register. Setting TrFreeze bits.
+ */
+ err = lan88xx_TR_reg_set(phydev, 0x1686, 0x000004);
+ if (err < 0)
+ pr_warn("Failed to Set Register[0x1686]\n");
+}
+
static int lan88xx_probe(struct phy_device *phydev)
{
struct device *dev = &phydev->mdio.dev;
@@ -132,6 +287,25 @@ static void lan88xx_set_mdix(struct phy_device *phydev)
phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, LAN88XX_EXT_PAGE_SPACE_0);
}
+static int lan88xx_config_init(struct phy_device *phydev)
+{
+ int val;
+
+ genphy_config_init(phydev);
+ /*Zerodetect delay enable */
+ val = phy_read_mmd(phydev, MDIO_MMD_PCS,
+ PHY_ARDENNES_MMD_DEV_3_PHY_CFG);
+ val |= PHY_ARDENNES_MMD_DEV_3_PHY_CFG_ZD_DLY_EN_;
+
+ phy_write_mmd(phydev, MDIO_MMD_PCS, PHY_ARDENNES_MMD_DEV_3_PHY_CFG,
+ val);
+
+ /* Config DSP registers */
+ lan88xx_config_TR_regs(phydev);
+
+ return 0;
+}
+
static int lan88xx_config_aneg(struct phy_device *phydev)
{
lan88xx_set_mdix(phydev);
@@ -151,7 +325,7 @@ static struct phy_driver microchip_phy_driver[] = {
.probe = lan88xx_probe,
.remove = lan88xx_remove,
- .config_init = genphy_config_init,
+ .config_init = lan88xx_config_init,
.config_aneg = lan88xx_config_aneg,
.ack_interrupt = lan88xx_phy_ack_interrupt,
@@ -160,6 +334,8 @@ static struct phy_driver microchip_phy_driver[] = {
.suspend = lan88xx_suspend,
.resume = genphy_resume,
.set_wol = lan88xx_set_wol,
+ .read_page = lan88xx_read_page,
+ .write_page = lan88xx_write_page,
} };
module_phy_driver(microchip_phy_driver);
diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c
index a4576859afae..e1225545362d 100644
--- a/drivers/net/phy/phy-c45.c
+++ b/drivers/net/phy/phy-c45.c
@@ -163,11 +163,11 @@ int genphy_c45_read_link(struct phy_device *phydev, u32 mmd_mask)
EXPORT_SYMBOL_GPL(genphy_c45_read_link);
/**
- * genphy_c45_read_lpa - read the link partner advertisment and pause
+ * genphy_c45_read_lpa - read the link partner advertisement and pause
* @phydev: target phy_device struct
*
* Read the Clause 45 defined base (7.19) and 10G (7.33) status registers,
- * filling in the link partner advertisment, pause and asym_pause members
+ * filling in the link partner advertisement, pause and asym_pause members
* in @phydev. This assumes that the auto-negotiation MMD is present, and
* the backplane bit (7.48.0) is clear. Clause 45 PHY drivers are expected
* to fill in the remainder of the link partner advert from vendor registers.
@@ -176,7 +176,7 @@ int genphy_c45_read_lpa(struct phy_device *phydev)
{
int val;
- /* Read the link partner's base page advertisment */
+ /* Read the link partner's base page advertisement */
val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA);
if (val < 0)
return val;
@@ -185,7 +185,7 @@ int genphy_c45_read_lpa(struct phy_device *phydev)
phydev->pause = val & LPA_PAUSE_CAP ? 1 : 0;
phydev->asym_pause = val & LPA_PAUSE_ASYM ? 1 : 0;
- /* Read the link partner's 10G advertisment */
+ /* Read the link partner's 10G advertisement */
val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);
if (val < 0)
return val;
@@ -268,12 +268,13 @@ EXPORT_SYMBOL_GPL(genphy_c45_read_mdix);
/* The gen10g_* functions are the old Clause 45 stub */
-static int gen10g_config_aneg(struct phy_device *phydev)
+int gen10g_config_aneg(struct phy_device *phydev)
{
return 0;
}
+EXPORT_SYMBOL_GPL(gen10g_config_aneg);
-static int gen10g_read_status(struct phy_device *phydev)
+int gen10g_read_status(struct phy_device *phydev)
{
u32 mmd_mask = phydev->c45_ids.devices_in_package;
int ret;
@@ -291,14 +292,16 @@ static int gen10g_read_status(struct phy_device *phydev)
return 0;
}
+EXPORT_SYMBOL_GPL(gen10g_read_status);
-static int gen10g_soft_reset(struct phy_device *phydev)
+int gen10g_no_soft_reset(struct phy_device *phydev)
{
/* Do nothing for now */
return 0;
}
+EXPORT_SYMBOL_GPL(gen10g_no_soft_reset);
-static int gen10g_config_init(struct phy_device *phydev)
+int gen10g_config_init(struct phy_device *phydev)
{
/* Temporarily just say we support everything */
phydev->supported = SUPPORTED_10000baseT_Full;
@@ -306,22 +309,25 @@ static int gen10g_config_init(struct phy_device *phydev)
return 0;
}
+EXPORT_SYMBOL_GPL(gen10g_config_init);
-static int gen10g_suspend(struct phy_device *phydev)
+int gen10g_suspend(struct phy_device *phydev)
{
return 0;
}
+EXPORT_SYMBOL_GPL(gen10g_suspend);
-static int gen10g_resume(struct phy_device *phydev)
+int gen10g_resume(struct phy_device *phydev)
{
return 0;
}
+EXPORT_SYMBOL_GPL(gen10g_resume);
struct phy_driver genphy_10g_driver = {
.phy_id = 0xffffffff,
.phy_id_mask = 0xffffffff,
.name = "Generic 10G PHY",
- .soft_reset = gen10g_soft_reset,
+ .soft_reset = gen10g_no_soft_reset,
.config_init = gen10g_config_init,
.features = 0,
.config_aneg = gen10g_config_aneg,
diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c
index 4083f00c97a5..c7da4cbb1103 100644
--- a/drivers/net/phy/phy-core.c
+++ b/drivers/net/phy/phy-core.c
@@ -190,10 +190,10 @@ size_t phy_speeds(unsigned int *speeds, size_t size,
}
/**
- * phy_resolve_aneg_linkmode - resolve the advertisments into phy settings
+ * phy_resolve_aneg_linkmode - resolve the advertisements into phy settings
* @phydev: The phy_device struct
*
- * Resolve our and the link partner advertisments into their corresponding
+ * Resolve our and the link partner advertisements into their corresponding
* speed and duplex. If full duplex was negotiated, extract the pause mode
* from the link partner mask.
*/
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 9aabfa1a455a..05c1e8ef15e6 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -628,19 +628,10 @@ static int phy_disable_interrupts(struct phy_device *phydev)
/* Disable PHY interrupts */
err = phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED);
if (err)
- goto phy_err;
+ return err;
/* Clear the interrupt */
- err = phy_clear_interrupt(phydev);
- if (err)
- goto phy_err;
-
- return 0;
-
-phy_err:
- phy_error(phydev);
-
- return err;
+ return phy_clear_interrupt(phydev);
}
/**
@@ -773,13 +764,8 @@ void phy_stop(struct phy_device *phydev)
if (PHY_HALTED == phydev->state)
goto out_unlock;
- if (phy_interrupt_is_valid(phydev)) {
- /* Disable PHY Interrupts */
- phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED);
-
- /* Clear any pending interrupts */
- phy_clear_interrupt(phydev);
- }
+ if (phy_interrupt_is_valid(phydev))
+ phy_disable_interrupts(phydev);
phydev->state = PHY_HALTED;
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 74664a6c0cdc..ac23322a32e1 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -374,7 +374,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id,
dev->duplex = -1;
dev->pause = 0;
dev->asym_pause = 0;
- dev->link = 1;
+ dev->link = 0;
dev->interface = PHY_INTERFACE_MODE_GMII;
dev->autoneg = AUTONEG_ENABLE;
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index 6ac8b29b2dc3..c582b2d7546c 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -364,7 +364,7 @@ static void phylink_get_fixed_state(struct phylink *pl, struct phylink_link_stat
}
/* Flow control is resolved according to our and the link partners
- * advertisments using the following drawn from the 802.3 specs:
+ * advertisements using the following drawn from the 802.3 specs:
* Local device Link partner
* Pause AsymDir Pause AsymDir Result
* 1 X 1 X TX+RX
@@ -470,10 +470,12 @@ static void phylink_resolve(struct work_struct *w)
if (link_state.link != netif_carrier_ok(ndev)) {
if (!link_state.link) {
netif_carrier_off(ndev);
- pl->ops->mac_link_down(ndev, pl->link_an_mode);
+ pl->ops->mac_link_down(ndev, pl->link_an_mode,
+ pl->phy_state.interface);
netdev_info(ndev, "Link is Down\n");
} else {
pl->ops->mac_link_up(ndev, pl->link_an_mode,
+ pl->phy_state.interface,
pl->phydev);
netif_carrier_on(ndev);
@@ -679,12 +681,11 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy)
mutex_lock(&phy->lock);
mutex_lock(&pl->state_mutex);
- pl->netdev->phydev = phy;
pl->phydev = phy;
linkmode_copy(pl->supported, supported);
linkmode_copy(pl->link_config.advertising, config.advertising);
- /* Restrict the phy advertisment according to the MAC support. */
+ /* Restrict the phy advertisement according to the MAC support. */
ethtool_convert_link_mode_to_legacy_u32(&advertising, config.advertising);
phy->advertising = advertising;
mutex_unlock(&pl->state_mutex);
@@ -817,7 +818,6 @@ void phylink_disconnect_phy(struct phylink *pl)
if (phy) {
mutex_lock(&phy->lock);
mutex_lock(&pl->state_mutex);
- pl->netdev->phydev = NULL;
pl->phydev = NULL;
mutex_unlock(&pl->state_mutex);
mutex_unlock(&phy->lock);
@@ -889,7 +889,7 @@ void phylink_start(struct phylink *pl)
/* Apply the link configuration to the MAC when starting. This allows
* a fixed-link to start with the correct parameters, and also
- * ensures that we set the appropriate advertisment for Serdes links.
+ * ensures that we set the appropriate advertisement for Serdes links.
*/
phylink_resolve_flow(pl, &pl->link_config);
phylink_mac_config(pl, &pl->link_config);
@@ -1076,7 +1076,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
config = pl->link_config;
- /* Mask out unsupported advertisments */
+ /* Mask out unsupported advertisements */
linkmode_and(config.advertising, kset->link_modes.advertising,
pl->supported);
@@ -1121,7 +1121,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
if (phylink_validate(pl, pl->supported, &config))
return -EINVAL;
- /* If autonegotiation is enabled, we must have an advertisment */
+ /* If autonegotiation is enabled, we must have an advertisement */
if (config.an_enabled && phylink_is_empty_linkmode(config.advertising))
return -EINVAL;
@@ -1250,34 +1250,6 @@ int phylink_ethtool_set_pauseparam(struct phylink *pl,
}
EXPORT_SYMBOL_GPL(phylink_ethtool_set_pauseparam);
-int phylink_ethtool_get_module_info(struct phylink *pl,
- struct ethtool_modinfo *modinfo)
-{
- int ret = -EOPNOTSUPP;
-
- WARN_ON(!lockdep_rtnl_is_held());
-
- if (pl->sfp_bus)
- ret = sfp_get_module_info(pl->sfp_bus, modinfo);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(phylink_ethtool_get_module_info);
-
-int phylink_ethtool_get_module_eeprom(struct phylink *pl,
- struct ethtool_eeprom *ee, u8 *buf)
-{
- int ret = -EOPNOTSUPP;
-
- WARN_ON(!lockdep_rtnl_is_held());
-
- if (pl->sfp_bus)
- ret = sfp_get_module_eeprom(pl->sfp_bus, ee, buf);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(phylink_ethtool_get_module_eeprom);
-
/**
* phylink_ethtool_get_eee_err() - read the energy efficient ethernet error
* counter
@@ -1584,25 +1556,14 @@ static int phylink_sfp_module_insert(void *upstream,
bool changed;
u8 port;
- sfp_parse_support(pl->sfp_bus, id, support);
- port = sfp_parse_port(pl->sfp_bus, id, support);
- iface = sfp_parse_interface(pl->sfp_bus, id);
-
ASSERT_RTNL();
- switch (iface) {
- case PHY_INTERFACE_MODE_SGMII:
- case PHY_INTERFACE_MODE_1000BASEX:
- case PHY_INTERFACE_MODE_2500BASEX:
- case PHY_INTERFACE_MODE_10GKR:
- break;
- default:
- return -EINVAL;
- }
+ sfp_parse_support(pl->sfp_bus, id, support);
+ port = sfp_parse_port(pl->sfp_bus, id, support);
memset(&config, 0, sizeof(config));
linkmode_copy(config.advertising, support);
- config.interface = iface;
+ config.interface = PHY_INTERFACE_MODE_NA;
config.speed = SPEED_UNKNOWN;
config.duplex = DUPLEX_UNKNOWN;
config.pause = MLO_PAUSE_AN;
@@ -1611,6 +1572,22 @@ static int phylink_sfp_module_insert(void *upstream,
/* Ignore errors if we're expecting a PHY to attach later */
ret = phylink_validate(pl, support, &config);
if (ret) {
+ netdev_err(pl->netdev, "validation with support %*pb failed: %d\n",
+ __ETHTOOL_LINK_MODE_MASK_NBITS, support, ret);
+ return ret;
+ }
+
+ iface = sfp_select_interface(pl->sfp_bus, id, config.advertising);
+ if (iface == PHY_INTERFACE_MODE_NA) {
+ netdev_err(pl->netdev,
+ "selection of interface failed, advertisement %*pb\n",
+ __ETHTOOL_LINK_MODE_MASK_NBITS, config.advertising);
+ return -EINVAL;
+ }
+
+ config.interface = iface;
+ ret = phylink_validate(pl, support, &config);
+ if (ret) {
netdev_err(pl->netdev, "validation of %s/%s with support %*pb failed: %d\n",
phylink_an_mode_str(MLO_AN_INBAND),
phy_modes(config.interface),
diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c
index 8961209ee949..0381da78d228 100644
--- a/drivers/net/phy/sfp-bus.c
+++ b/drivers/net/phy/sfp-bus.c
@@ -106,68 +106,6 @@ int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
EXPORT_SYMBOL_GPL(sfp_parse_port);
/**
- * sfp_parse_interface() - Parse the phy_interface_t
- * @bus: a pointer to the &struct sfp_bus structure for the sfp module
- * @id: a pointer to the module's &struct sfp_eeprom_id
- *
- * Derive the phy_interface_t mode for the information found in the
- * module's identifying EEPROM. There is no standard or defined way
- * to derive this information, so we use some heuristics.
- *
- * If the encoding is 64b66b, then the module must be >= 10G, so
- * return %PHY_INTERFACE_MODE_10GKR.
- *
- * If it's 8b10b, then it's 1G or slower. If it's definitely a fibre
- * module, return %PHY_INTERFACE_MODE_1000BASEX mode, otherwise return
- * %PHY_INTERFACE_MODE_SGMII mode.
- *
- * If the encoding is not known, return %PHY_INTERFACE_MODE_NA.
- */
-phy_interface_t sfp_parse_interface(struct sfp_bus *bus,
- const struct sfp_eeprom_id *id)
-{
- phy_interface_t iface;
-
- /* Setting the serdes link mode is guesswork: there's no field in
- * the EEPROM which indicates what mode should be used.
- *
- * If the module wants 64b66b, then it must be >= 10G.
- *
- * If it's a gigabit-only fiber module, it probably does not have
- * a PHY, so switch to 802.3z negotiation mode. Otherwise, switch
- * to SGMII mode (which is required to support non-gigabit speeds).
- */
- switch (id->base.encoding) {
- case SFP_ENCODING_8472_64B66B:
- iface = PHY_INTERFACE_MODE_10GKR;
- break;
-
- case SFP_ENCODING_8B10B:
- if (!id->base.e1000_base_t &&
- !id->base.e100_base_lx &&
- !id->base.e100_base_fx)
- iface = PHY_INTERFACE_MODE_1000BASEX;
- else
- iface = PHY_INTERFACE_MODE_SGMII;
- break;
-
- default:
- if (id->base.e1000_base_cx) {
- iface = PHY_INTERFACE_MODE_1000BASEX;
- break;
- }
-
- iface = PHY_INTERFACE_MODE_NA;
- dev_err(bus->sfp_dev,
- "SFP module encoding does not support 8b10b nor 64b66b\n");
- break;
- }
-
- return iface;
-}
-EXPORT_SYMBOL_GPL(sfp_parse_interface);
-
-/**
* sfp_parse_support() - Parse the eeprom id for supported link modes
* @bus: a pointer to the &struct sfp_bus structure for the sfp module
* @id: a pointer to the module's &struct sfp_eeprom_id
@@ -180,10 +118,7 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
unsigned long *support)
{
unsigned int br_min, br_nom, br_max;
-
- phylink_set(support, Autoneg);
- phylink_set(support, Pause);
- phylink_set(support, Asym_Pause);
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(modes) = { 0, };
/* Decode the bitrate information to MBd */
br_min = br_nom = br_max = 0;
@@ -201,20 +136,20 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
/* Set ethtool support from the compliance fields. */
if (id->base.e10g_base_sr)
- phylink_set(support, 10000baseSR_Full);
+ phylink_set(modes, 10000baseSR_Full);
if (id->base.e10g_base_lr)
- phylink_set(support, 10000baseLR_Full);
+ phylink_set(modes, 10000baseLR_Full);
if (id->base.e10g_base_lrm)
- phylink_set(support, 10000baseLRM_Full);
+ phylink_set(modes, 10000baseLRM_Full);
if (id->base.e10g_base_er)
- phylink_set(support, 10000baseER_Full);
+ phylink_set(modes, 10000baseER_Full);
if (id->base.e1000_base_sx ||
id->base.e1000_base_lx ||
id->base.e1000_base_cx)
- phylink_set(support, 1000baseX_Full);
+ phylink_set(modes, 1000baseX_Full);
if (id->base.e1000_base_t) {
- phylink_set(support, 1000baseT_Half);
- phylink_set(support, 1000baseT_Full);
+ phylink_set(modes, 1000baseT_Half);
+ phylink_set(modes, 1000baseT_Full);
}
/* 1000Base-PX or 1000Base-BX10 */
@@ -228,20 +163,20 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
if ((id->base.sfp_ct_passive || id->base.sfp_ct_active) && br_nom) {
/* This may look odd, but some manufacturers use 12000MBd */
if (br_min <= 12000 && br_max >= 10300)
- phylink_set(support, 10000baseCR_Full);
+ phylink_set(modes, 10000baseCR_Full);
if (br_min <= 3200 && br_max >= 3100)
- phylink_set(support, 2500baseX_Full);
+ phylink_set(modes, 2500baseX_Full);
if (br_min <= 1300 && br_max >= 1200)
- phylink_set(support, 1000baseX_Full);
+ phylink_set(modes, 1000baseX_Full);
}
if (id->base.sfp_ct_passive) {
if (id->base.passive.sff8431_app_e)
- phylink_set(support, 10000baseCR_Full);
+ phylink_set(modes, 10000baseCR_Full);
}
if (id->base.sfp_ct_active) {
if (id->base.active.sff8431_app_e ||
id->base.active.sff8431_lim) {
- phylink_set(support, 10000baseCR_Full);
+ phylink_set(modes, 10000baseCR_Full);
}
}
@@ -249,18 +184,18 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
case 0x00: /* Unspecified */
break;
case 0x02: /* 100Gbase-SR4 or 25Gbase-SR */
- phylink_set(support, 100000baseSR4_Full);
- phylink_set(support, 25000baseSR_Full);
+ phylink_set(modes, 100000baseSR4_Full);
+ phylink_set(modes, 25000baseSR_Full);
break;
case 0x03: /* 100Gbase-LR4 or 25Gbase-LR */
case 0x04: /* 100Gbase-ER4 or 25Gbase-ER */
- phylink_set(support, 100000baseLR4_ER4_Full);
+ phylink_set(modes, 100000baseLR4_ER4_Full);
break;
case 0x0b: /* 100Gbase-CR4 or 25Gbase-CR CA-L */
case 0x0c: /* 25Gbase-CR CA-S */
case 0x0d: /* 25Gbase-CR CA-N */
- phylink_set(support, 100000baseCR4_Full);
- phylink_set(support, 25000baseCR_Full);
+ phylink_set(modes, 100000baseCR4_Full);
+ phylink_set(modes, 25000baseCR_Full);
break;
default:
dev_warn(bus->sfp_dev,
@@ -274,13 +209,70 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
id->base.fc_speed_200 ||
id->base.fc_speed_400) {
if (id->base.br_nominal >= 31)
- phylink_set(support, 2500baseX_Full);
+ phylink_set(modes, 2500baseX_Full);
if (id->base.br_nominal >= 12)
- phylink_set(support, 1000baseX_Full);
+ phylink_set(modes, 1000baseX_Full);
}
+
+ /* If we haven't discovered any modes that this module supports, try
+ * the encoding and bitrate to determine supported modes. Some BiDi
+ * modules (eg, 1310nm/1550nm) are not 1000BASE-BX compliant due to
+ * the differing wavelengths, so do not set any transceiver bits.
+ */
+ if (bitmap_empty(modes, __ETHTOOL_LINK_MODE_MASK_NBITS)) {
+ /* If the encoding and bit rate allows 1000baseX */
+ if (id->base.encoding == SFP_ENCODING_8B10B && br_nom &&
+ br_min <= 1300 && br_max >= 1200)
+ phylink_set(modes, 1000baseX_Full);
+ }
+
+ bitmap_or(support, support, modes, __ETHTOOL_LINK_MODE_MASK_NBITS);
+
+ phylink_set(support, Autoneg);
+ phylink_set(support, Pause);
+ phylink_set(support, Asym_Pause);
}
EXPORT_SYMBOL_GPL(sfp_parse_support);
+/**
+ * sfp_select_interface() - Select appropriate phy_interface_t mode
+ * @bus: a pointer to the &struct sfp_bus structure for the sfp module
+ * @id: a pointer to the module's &struct sfp_eeprom_id
+ * @link_modes: ethtool link modes mask
+ *
+ * Derive the phy_interface_t mode for the information found in the
+ * module's identifying EEPROM and the link modes mask. There is no
+ * standard or defined way to derive this information, so we decide
+ * based upon the link mode mask.
+ */
+phy_interface_t sfp_select_interface(struct sfp_bus *bus,
+ const struct sfp_eeprom_id *id,
+ unsigned long *link_modes)
+{
+ if (phylink_test(link_modes, 10000baseCR_Full) ||
+ phylink_test(link_modes, 10000baseSR_Full) ||
+ phylink_test(link_modes, 10000baseLR_Full) ||
+ phylink_test(link_modes, 10000baseLRM_Full) ||
+ phylink_test(link_modes, 10000baseER_Full))
+ return PHY_INTERFACE_MODE_10GKR;
+
+ if (phylink_test(link_modes, 2500baseX_Full))
+ return PHY_INTERFACE_MODE_2500BASEX;
+
+ if (id->base.e1000_base_t ||
+ id->base.e100_base_lx ||
+ id->base.e100_base_fx)
+ return PHY_INTERFACE_MODE_SGMII;
+
+ if (phylink_test(link_modes, 1000baseX_Full))
+ return PHY_INTERFACE_MODE_1000BASEX;
+
+ dev_warn(bus->sfp_dev, "Unable to ascertain link mode\n");
+
+ return PHY_INTERFACE_MODE_NA;
+}
+EXPORT_SYMBOL_GPL(sfp_select_interface);
+
static LIST_HEAD(sfp_buses);
static DEFINE_MUTEX(sfp_mutex);
@@ -350,6 +342,7 @@ static int sfp_register_bus(struct sfp_bus *bus)
}
if (bus->started)
bus->socket_ops->start(bus->sfp);
+ bus->netdev->sfp_bus = bus;
bus->registered = true;
return 0;
}
@@ -364,6 +357,7 @@ static void sfp_unregister_bus(struct sfp_bus *bus)
if (bus->phydev && ops && ops->disconnect_phy)
ops->disconnect_phy(bus->upstream);
}
+ bus->netdev->sfp_bus = NULL;
bus->registered = false;
}
@@ -379,8 +373,6 @@ static void sfp_unregister_bus(struct sfp_bus *bus)
*/
int sfp_get_module_info(struct sfp_bus *bus, struct ethtool_modinfo *modinfo)
{
- if (!bus->registered)
- return -ENOIOCTLCMD;
return bus->socket_ops->module_info(bus->sfp, modinfo);
}
EXPORT_SYMBOL_GPL(sfp_get_module_info);
@@ -399,8 +391,6 @@ EXPORT_SYMBOL_GPL(sfp_get_module_info);
int sfp_get_module_eeprom(struct sfp_bus *bus, struct ethtool_eeprom *ee,
u8 *data)
{
- if (!bus->registered)
- return -ENOIOCTLCMD;
return bus->socket_ops->module_eeprom(bus->sfp, ee, data);
}
EXPORT_SYMBOL_GPL(sfp_get_module_eeprom);
diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
index 6c7d9289078d..4ab6e9a50bbe 100644
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
@@ -42,6 +42,7 @@ enum {
SFP_MOD_EMPTY = 0,
SFP_MOD_PROBE,
+ SFP_MOD_HPOWER,
SFP_MOD_PRESENT,
SFP_MOD_ERROR,
@@ -86,6 +87,7 @@ static const enum gpiod_flags gpio_flags[] = {
* access the I2C EEPROM. However, Avago modules require 300ms.
*/
#define T_PROBE_INIT msecs_to_jiffies(300)
+#define T_HPOWER_LEVEL msecs_to_jiffies(300)
#define T_PROBE_RETRY msecs_to_jiffies(100)
/* SFP modules appear to always have their PHY configured for bus address
@@ -110,10 +112,12 @@ struct sfp {
struct sfp_bus *sfp_bus;
struct phy_device *mod_phy;
const struct sff_data *type;
+ u32 max_power_mW;
unsigned int (*get_state)(struct sfp *);
void (*set_state)(struct sfp *, unsigned int);
int (*read)(struct sfp *, bool, u8, void *, size_t);
+ int (*write)(struct sfp *, bool, u8, void *, size_t);
struct gpio_desc *gpio[GPIO_MAX];
@@ -201,10 +205,11 @@ static void sfp_gpio_set_state(struct sfp *sfp, unsigned int state)
}
}
-static int sfp__i2c_read(struct i2c_adapter *i2c, u8 bus_addr, u8 dev_addr,
- void *buf, size_t len)
+static int sfp_i2c_read(struct sfp *sfp, bool a2, u8 dev_addr, void *buf,
+ size_t len)
{
struct i2c_msg msgs[2];
+ u8 bus_addr = a2 ? 0x51 : 0x50;
int ret;
msgs[0].addr = bus_addr;
@@ -216,17 +221,38 @@ static int sfp__i2c_read(struct i2c_adapter *i2c, u8 bus_addr, u8 dev_addr,
msgs[1].len = len;
msgs[1].buf = buf;
- ret = i2c_transfer(i2c, msgs, ARRAY_SIZE(msgs));
+ ret = i2c_transfer(sfp->i2c, msgs, ARRAY_SIZE(msgs));
if (ret < 0)
return ret;
return ret == ARRAY_SIZE(msgs) ? len : 0;
}
-static int sfp_i2c_read(struct sfp *sfp, bool a2, u8 addr, void *buf,
- size_t len)
+static int sfp_i2c_write(struct sfp *sfp, bool a2, u8 dev_addr, void *buf,
+ size_t len)
{
- return sfp__i2c_read(sfp->i2c, a2 ? 0x51 : 0x50, addr, buf, len);
+ struct i2c_msg msgs[1];
+ u8 bus_addr = a2 ? 0x51 : 0x50;
+ int ret;
+
+ msgs[0].addr = bus_addr;
+ msgs[0].flags = 0;
+ msgs[0].len = 1 + len;
+ msgs[0].buf = kmalloc(1 + len, GFP_KERNEL);
+ if (!msgs[0].buf)
+ return -ENOMEM;
+
+ msgs[0].buf[0] = dev_addr;
+ memcpy(&msgs[0].buf[1], buf, len);
+
+ ret = i2c_transfer(sfp->i2c, msgs, ARRAY_SIZE(msgs));
+
+ kfree(msgs[0].buf);
+
+ if (ret < 0)
+ return ret;
+
+ return ret == ARRAY_SIZE(msgs) ? len : 0;
}
static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c)
@@ -239,6 +265,7 @@ static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c)
sfp->i2c = i2c;
sfp->read = sfp_i2c_read;
+ sfp->write = sfp_i2c_write;
i2c_mii = mdio_i2c_alloc(sfp->dev, i2c);
if (IS_ERR(i2c_mii))
@@ -274,6 +301,11 @@ static int sfp_read(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len)
return sfp->read(sfp, a2, addr, buf, len);
}
+static int sfp_write(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len)
+{
+ return sfp->write(sfp, a2, addr, buf, len);
+}
+
static unsigned int sfp_check(void *buf, size_t len)
{
u8 *p, check;
@@ -462,41 +494,124 @@ static void sfp_sm_mod_init(struct sfp *sfp)
sfp_sm_probe_phy(sfp);
}
+static int sfp_sm_mod_hpower(struct sfp *sfp)
+{
+ u32 power;
+ u8 val;
+ int err;
+
+ power = 1000;
+ if (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_POWER_DECL))
+ power = 1500;
+ if (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_HIGH_POWER_LEVEL))
+ power = 2000;
+
+ if (sfp->id.ext.sff8472_compliance == SFP_SFF8472_COMPLIANCE_NONE &&
+ (sfp->id.ext.diagmon & (SFP_DIAGMON_DDM | SFP_DIAGMON_ADDRMODE)) !=
+ SFP_DIAGMON_DDM) {
+ /* The module appears not to implement bus address 0xa2,
+ * or requires an address change sequence, so assume that
+ * the module powers up in the indicated power mode.
+ */
+ if (power > sfp->max_power_mW) {
+ dev_err(sfp->dev,
+ "Host does not support %u.%uW modules\n",
+ power / 1000, (power / 100) % 10);
+ return -EINVAL;
+ }
+ return 0;
+ }
+
+ if (power > sfp->max_power_mW) {
+ dev_warn(sfp->dev,
+ "Host does not support %u.%uW modules, module left in power mode 1\n",
+ power / 1000, (power / 100) % 10);
+ return 0;
+ }
+
+ if (power <= 1000)
+ return 0;
+
+ err = sfp_read(sfp, true, SFP_EXT_STATUS, &val, sizeof(val));
+ if (err != sizeof(val)) {
+ dev_err(sfp->dev, "Failed to read EEPROM: %d\n", err);
+ err = -EAGAIN;
+ goto err;
+ }
+
+ val |= BIT(0);
+
+ err = sfp_write(sfp, true, SFP_EXT_STATUS, &val, sizeof(val));
+ if (err != sizeof(val)) {
+ dev_err(sfp->dev, "Failed to write EEPROM: %d\n", err);
+ err = -EAGAIN;
+ goto err;
+ }
+
+ dev_info(sfp->dev, "Module switched to %u.%uW power level\n",
+ power / 1000, (power / 100) % 10);
+ return T_HPOWER_LEVEL;
+
+err:
+ return err;
+}
+
static int sfp_sm_mod_probe(struct sfp *sfp)
{
/* SFP module inserted - read I2C data */
struct sfp_eeprom_id id;
+ bool cotsworks;
u8 check;
- int err;
+ int ret;
- err = sfp_read(sfp, false, 0, &id, sizeof(id));
- if (err < 0) {
- dev_err(sfp->dev, "failed to read EEPROM: %d\n", err);
+ ret = sfp_read(sfp, false, 0, &id, sizeof(id));
+ if (ret < 0) {
+ dev_err(sfp->dev, "failed to read EEPROM: %d\n", ret);
return -EAGAIN;
}
- if (err != sizeof(id)) {
- dev_err(sfp->dev, "EEPROM short read: %d\n", err);
+ if (ret != sizeof(id)) {
+ dev_err(sfp->dev, "EEPROM short read: %d\n", ret);
return -EAGAIN;
}
+ /* Cotsworks do not seem to update the checksums when they
+ * do the final programming with the final module part number,
+ * serial number and date code.
+ */
+ cotsworks = !memcmp(id.base.vendor_name, "COTSWORKS ", 16);
+
/* Validate the checksum over the base structure */
check = sfp_check(&id.base, sizeof(id.base) - 1);
if (check != id.base.cc_base) {
- dev_err(sfp->dev,
- "EEPROM base structure checksum failure: 0x%02x\n",
- check);
- print_hex_dump(KERN_ERR, "sfp EE: ", DUMP_PREFIX_OFFSET,
- 16, 1, &id, sizeof(id.base) - 1, true);
- return -EINVAL;
+ if (cotsworks) {
+ dev_warn(sfp->dev,
+ "EEPROM base structure checksum failure (0x%02x != 0x%02x)\n",
+ check, id.base.cc_base);
+ } else {
+ dev_err(sfp->dev,
+ "EEPROM base structure checksum failure: 0x%02x != 0x%02x\n",
+ check, id.base.cc_base);
+ print_hex_dump(KERN_ERR, "sfp EE: ", DUMP_PREFIX_OFFSET,
+ 16, 1, &id, sizeof(id), true);
+ return -EINVAL;
+ }
}
check = sfp_check(&id.ext, sizeof(id.ext) - 1);
if (check != id.ext.cc_ext) {
- dev_err(sfp->dev,
- "EEPROM extended structure checksum failure: 0x%02x\n",
- check);
- memset(&id.ext, 0, sizeof(id.ext));
+ if (cotsworks) {
+ dev_warn(sfp->dev,
+ "EEPROM extended structure checksum failure (0x%02x != 0x%02x)\n",
+ check, id.ext.cc_ext);
+ } else {
+ dev_err(sfp->dev,
+ "EEPROM extended structure checksum failure: 0x%02x != 0x%02x\n",
+ check, id.ext.cc_ext);
+ print_hex_dump(KERN_ERR, "sfp EE: ", DUMP_PREFIX_OFFSET,
+ 16, 1, &id, sizeof(id), true);
+ memset(&id.ext, 0, sizeof(id.ext));
+ }
}
sfp->id = id;
@@ -521,7 +636,11 @@ static int sfp_sm_mod_probe(struct sfp *sfp)
dev_warn(sfp->dev,
"module address swap to access page 0xA2 is not supported.\n");
- return sfp_module_insert(sfp->sfp_bus, &sfp->id);
+ ret = sfp_module_insert(sfp->sfp_bus, &sfp->id);
+ if (ret < 0)
+ return ret;
+
+ return sfp_sm_mod_hpower(sfp);
}
static void sfp_sm_mod_remove(struct sfp *sfp)
@@ -560,17 +679,25 @@ static void sfp_sm_event(struct sfp *sfp, unsigned int event)
if (event == SFP_E_REMOVE) {
sfp_sm_ins_next(sfp, SFP_MOD_EMPTY, 0);
} else if (event == SFP_E_TIMEOUT) {
- int err = sfp_sm_mod_probe(sfp);
+ int val = sfp_sm_mod_probe(sfp);
- if (err == 0)
+ if (val == 0)
sfp_sm_ins_next(sfp, SFP_MOD_PRESENT, 0);
- else if (err == -EAGAIN)
- sfp_sm_set_timer(sfp, T_PROBE_RETRY);
- else
+ else if (val > 0)
+ sfp_sm_ins_next(sfp, SFP_MOD_HPOWER, val);
+ else if (val != -EAGAIN)
sfp_sm_ins_next(sfp, SFP_MOD_ERROR, 0);
+ else
+ sfp_sm_set_timer(sfp, T_PROBE_RETRY);
}
break;
+ case SFP_MOD_HPOWER:
+ if (event == SFP_E_TIMEOUT) {
+ sfp_sm_ins_next(sfp, SFP_MOD_PRESENT, 0);
+ break;
+ }
+ /* fallthrough */
case SFP_MOD_PRESENT:
case SFP_MOD_ERROR:
if (event == SFP_E_REMOVE) {
@@ -889,6 +1016,14 @@ static int sfp_probe(struct platform_device *pdev)
if (!(sfp->gpio[GPIO_MODDEF0]))
sfp->get_state = sff_gpio_get_state;
+ device_property_read_u32(&pdev->dev, "maximum-power-milliwatt",
+ &sfp->max_power_mW);
+ if (!sfp->max_power_mW)
+ sfp->max_power_mW = 1000;
+
+ dev_info(sfp->dev, "Host maximum power %u.%uW\n",
+ sfp->max_power_mW / 1000, (sfp->max_power_mW / 100) % 10);
+
sfp->sfp_bus = sfp_register_socket(sfp->dev, sfp, &sfp_module_ops);
if (!sfp->sfp_bus)
return -ENOMEM;
diff --git a/drivers/net/phy/spi_ks8995.c b/drivers/net/phy/spi_ks8995.c
index 1e2d4f1179da..f17b3441779b 100644
--- a/drivers/net/phy/spi_ks8995.c
+++ b/drivers/net/phy/spi_ks8995.c
@@ -417,7 +417,7 @@ static void ks8995_parse_dt(struct ks8995_switch *ks)
static const struct bin_attribute ks8995_registers_attr = {
.attr = {
.name = "registers",
- .mode = S_IRUSR | S_IWUSR,
+ .mode = 0600,
},
.size = KS8995_REGS_SIZE,
.read = ks8995_registers_read,
diff --git a/drivers/net/phy/teranetics.c b/drivers/net/phy/teranetics.c
index fb2cef764e9a..22f3bdd8206c 100644
--- a/drivers/net/phy/teranetics.c
+++ b/drivers/net/phy/teranetics.c
@@ -34,39 +34,17 @@ MODULE_LICENSE("GPL v2");
MDIO_PHYXS_LNSTAT_SYNC3 | \
MDIO_PHYXS_LNSTAT_ALIGN)
-static int teranetics_config_init(struct phy_device *phydev)
-{
- phydev->supported = SUPPORTED_10000baseT_Full;
- phydev->advertising = SUPPORTED_10000baseT_Full;
-
- return 0;
-}
-
-static int teranetics_soft_reset(struct phy_device *phydev)
-{
- return 0;
-}
-
static int teranetics_aneg_done(struct phy_device *phydev)
{
- int reg;
-
/* auto negotiation state can only be checked when using copper
* port, if using fiber port, just lie it's done.
*/
- if (!phy_read_mmd(phydev, MDIO_MMD_VEND1, 93)) {
- reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
- return (reg < 0) ? reg : (reg & BMSR_ANEGCOMPLETE);
- }
+ if (!phy_read_mmd(phydev, MDIO_MMD_VEND1, 93))
+ return genphy_c45_aneg_done(phydev);
return 1;
}
-static int teranetics_config_aneg(struct phy_device *phydev)
-{
- return 0;
-}
-
static int teranetics_read_status(struct phy_device *phydev)
{
int reg;
@@ -102,10 +80,10 @@ static struct phy_driver teranetics_driver[] = {
.phy_id = PHY_ID_TN2020,
.phy_id_mask = 0xffffffff,
.name = "Teranetics TN2020",
- .soft_reset = teranetics_soft_reset,
+ .soft_reset = gen10g_no_soft_reset,
.aneg_done = teranetics_aneg_done,
- .config_init = teranetics_config_init,
- .config_aneg = teranetics_config_aneg,
+ .config_init = gen10g_config_init,
+ .config_aneg = gen10g_config_aneg,
.read_status = teranetics_read_status,
.match_phy_device = teranetics_match_phy_device,
},
diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
index da1937832c99..dc7c7ec43202 100644
--- a/drivers/net/ppp/ppp_generic.c
+++ b/drivers/net/ppp/ppp_generic.c
@@ -1686,7 +1686,7 @@ ppp_push(struct ppp *ppp)
#ifdef CONFIG_PPP_MULTILINK
static bool mp_protocol_compress __read_mostly = true;
-module_param(mp_protocol_compress, bool, S_IRUGO | S_IWUSR);
+module_param(mp_protocol_compress, bool, 0644);
MODULE_PARM_DESC(mp_protocol_compress,
"compress protocol id in multilink fragments");
diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c
index 5aa59f41bf8c..7df07337d69c 100644
--- a/drivers/net/ppp/pppoe.c
+++ b/drivers/net/ppp/pppoe.c
@@ -620,6 +620,10 @@ static int pppoe_connect(struct socket *sock, struct sockaddr *uservaddr,
lock_sock(sk);
error = -EINVAL;
+
+ if (sockaddr_len != sizeof(struct sockaddr_pppox))
+ goto end;
+
if (sp->sa_protocol != PX_PROTO_OE)
goto end;
@@ -714,7 +718,7 @@ err_put:
}
static int pppoe_getname(struct socket *sock, struct sockaddr *uaddr,
- int *usockaddr_len, int peer)
+ int peer)
{
int len = sizeof(struct sockaddr_pppox);
struct sockaddr_pppox sp;
@@ -726,9 +730,7 @@ static int pppoe_getname(struct socket *sock, struct sockaddr *uaddr,
memcpy(uaddr, &sp, len);
- *usockaddr_len = len;
-
- return 0;
+ return len;
}
static int pppoe_ioctl(struct socket *sock, unsigned int cmd,
@@ -1144,7 +1146,7 @@ static __net_init int pppoe_init_net(struct net *net)
rwlock_init(&pn->hash_lock);
- pde = proc_create("pppoe", S_IRUGO, net->proc_net, &pppoe_seq_fops);
+ pde = proc_create("pppoe", 0444, net->proc_net, &pppoe_seq_fops);
#ifdef CONFIG_PROC_FS
if (!pde)
return -ENOMEM;
diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c
index 6dde9a0cfe76..c4267ecefd85 100644
--- a/drivers/net/ppp/pptp.c
+++ b/drivers/net/ppp/pptp.c
@@ -464,7 +464,6 @@ static int pptp_connect(struct socket *sock, struct sockaddr *uservaddr,
po->chan.mtu = dst_mtu(&rt->dst);
if (!po->chan.mtu)
po->chan.mtu = PPP_MRU;
- ip_rt_put(rt);
po->chan.mtu -= PPTP_HEADER_OVERHEAD;
po->chan.hdrlen = 2 + sizeof(struct pptp_gre_header);
@@ -483,7 +482,7 @@ static int pptp_connect(struct socket *sock, struct sockaddr *uservaddr,
}
static int pptp_getname(struct socket *sock, struct sockaddr *uaddr,
- int *usockaddr_len, int peer)
+ int peer)
{
int len = sizeof(struct sockaddr_pppox);
struct sockaddr_pppox sp;
@@ -496,9 +495,7 @@ static int pptp_getname(struct socket *sock, struct sockaddr *uaddr,
memcpy(uaddr, &sp, len);
- *usockaddr_len = len;
-
- return 0;
+ return len;
}
static int pptp_release(struct socket *sock)
diff --git a/drivers/net/slip/slhc.c b/drivers/net/slip/slhc.c
index 5782733959f0..f4e93f5fc204 100644
--- a/drivers/net/slip/slhc.c
+++ b/drivers/net/slip/slhc.c
@@ -509,6 +509,10 @@ slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize)
if(x < 0 || x > comp->rslot_limit)
goto bad;
+ /* Check if the cstate is initialized */
+ if (!comp->rstate[x].initialized)
+ goto bad;
+
comp->flags &=~ SLF_TOSS;
comp->recv_current = x;
} else {
@@ -673,6 +677,7 @@ slhc_remember(struct slcompress *comp, unsigned char *icp, int isize)
if (cs->cs_tcp.doff > 5)
memcpy(cs->cs_tcpopt, icp + ihl*4 + sizeof(struct tcphdr), (cs->cs_tcp.doff - 5) * 4);
cs->cs_hsize = ihl*2 + cs->cs_tcp.doff*2;
+ cs->initialized = true;
/* Put headers back on packet
* Neither header checksum is recalculated
*/
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index 56c701b73c12..ddb6bf85a59c 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -261,6 +261,17 @@ static void __team_option_inst_mark_removed_port(struct team *team,
}
}
+static bool __team_option_inst_tmp_find(const struct list_head *opts,
+ const struct team_option_inst *needle)
+{
+ struct team_option_inst *opt_inst;
+
+ list_for_each_entry(opt_inst, opts, tmp_list)
+ if (opt_inst == needle)
+ return true;
+ return false;
+}
+
static int __team_options_register(struct team *team,
const struct team_option *option,
size_t option_count)
@@ -1061,14 +1072,11 @@ static void team_port_leave(struct team *team, struct team_port *port)
}
#ifdef CONFIG_NET_POLL_CONTROLLER
-static int team_port_enable_netpoll(struct team *team, struct team_port *port)
+static int __team_port_enable_netpoll(struct team_port *port)
{
struct netpoll *np;
int err;
- if (!team->dev->npinfo)
- return 0;
-
np = kzalloc(sizeof(*np), GFP_KERNEL);
if (!np)
return -ENOMEM;
@@ -1082,6 +1090,14 @@ static int team_port_enable_netpoll(struct team *team, struct team_port *port)
return err;
}
+static int team_port_enable_netpoll(struct team_port *port)
+{
+ if (!port->team->dev->npinfo)
+ return 0;
+
+ return __team_port_enable_netpoll(port);
+}
+
static void team_port_disable_netpoll(struct team_port *port)
{
struct netpoll *np = port->np;
@@ -1096,7 +1112,7 @@ static void team_port_disable_netpoll(struct team_port *port)
kfree(np);
}
#else
-static int team_port_enable_netpoll(struct team *team, struct team_port *port)
+static int team_port_enable_netpoll(struct team_port *port)
{
return 0;
}
@@ -1105,14 +1121,15 @@ static void team_port_disable_netpoll(struct team_port *port)
}
#endif
-static int team_upper_dev_link(struct team *team, struct team_port *port)
+static int team_upper_dev_link(struct team *team, struct team_port *port,
+ struct netlink_ext_ack *extack)
{
struct netdev_lag_upper_info lag_upper_info;
int err;
lag_upper_info.tx_type = team->mode->lag_tx_type;
err = netdev_master_upper_dev_link(port->dev, team->dev, NULL,
- &lag_upper_info, NULL);
+ &lag_upper_info, extack);
if (err)
return err;
port->dev->priv_flags |= IFF_TEAM_PORT;
@@ -1129,7 +1146,8 @@ static void __team_port_change_port_added(struct team_port *port, bool linkup);
static int team_dev_type_check_change(struct net_device *dev,
struct net_device *port_dev);
-static int team_port_add(struct team *team, struct net_device *port_dev)
+static int team_port_add(struct team *team, struct net_device *port_dev,
+ struct netlink_ext_ack *extack)
{
struct net_device *dev = team->dev;
struct team_port *port;
@@ -1137,12 +1155,14 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
int err;
if (port_dev->flags & IFF_LOOPBACK) {
+ NL_SET_ERR_MSG(extack, "Loopback device can't be added as a team port");
netdev_err(dev, "Device %s is loopback device. Loopback devices can't be added as a team port\n",
portname);
return -EINVAL;
}
if (team_port_exists(port_dev)) {
+ NL_SET_ERR_MSG(extack, "Device is already a port of a team device");
netdev_err(dev, "Device %s is already a port "
"of a team device\n", portname);
return -EBUSY;
@@ -1150,6 +1170,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
if (port_dev->features & NETIF_F_VLAN_CHALLENGED &&
vlan_uses_dev(dev)) {
+ NL_SET_ERR_MSG(extack, "Device is VLAN challenged and team device has VLAN set up");
netdev_err(dev, "Device %s is VLAN challenged and team device has VLAN set up\n",
portname);
return -EPERM;
@@ -1160,6 +1181,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
return err;
if (port_dev->flags & IFF_UP) {
+ NL_SET_ERR_MSG(extack, "Device is up. Set it down before adding it as a team port");
netdev_err(dev, "Device %s is up. Set it down before adding it as a team port\n",
portname);
return -EBUSY;
@@ -1197,11 +1219,6 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
goto err_dev_open;
}
- netif_addr_lock_bh(dev);
- dev_uc_sync_multiple(port_dev, dev);
- dev_mc_sync_multiple(port_dev, dev);
- netif_addr_unlock_bh(dev);
-
err = vlan_vids_add_by_dev(port_dev, dev);
if (err) {
netdev_err(dev, "Failed to add vlan ids to device %s\n",
@@ -1209,7 +1226,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
goto err_vids_add;
}
- err = team_port_enable_netpoll(team, port);
+ err = team_port_enable_netpoll(port);
if (err) {
netdev_err(dev, "Failed to enable netpoll on device %s\n",
portname);
@@ -1227,7 +1244,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
goto err_handler_register;
}
- err = team_upper_dev_link(team, port);
+ err = team_upper_dev_link(team, port, extack);
if (err) {
netdev_err(dev, "Device %s failed to set upper link\n",
portname);
@@ -1241,6 +1258,11 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
goto err_option_port_add;
}
+ netif_addr_lock_bh(dev);
+ dev_uc_sync_multiple(port_dev, dev);
+ dev_mc_sync_multiple(port_dev, dev);
+ netif_addr_unlock_bh(dev);
+
port->index = -1;
list_add_tail_rcu(&port->list, &team->port_list);
team_port_enable(team, port);
@@ -1265,8 +1287,6 @@ err_enable_netpoll:
vlan_vids_del_by_dev(port_dev, dev);
err_vids_add:
- dev_uc_unsync(port_dev, dev);
- dev_mc_unsync(port_dev, dev);
dev_close(port_dev);
err_dev_open:
@@ -1903,7 +1923,7 @@ static int team_netpoll_setup(struct net_device *dev,
mutex_lock(&team->lock);
list_for_each_entry(port, &team->port_list, list) {
- err = team_port_enable_netpoll(team, port);
+ err = __team_port_enable_netpoll(port);
if (err) {
__team_netpoll_cleanup(team);
break;
@@ -1921,7 +1941,7 @@ static int team_add_slave(struct net_device *dev, struct net_device *port_dev,
int err;
mutex_lock(&team->lock);
- err = team_port_add(team, port_dev);
+ err = team_port_add(team, port_dev, extack);
mutex_unlock(&team->lock);
if (!err)
@@ -2564,6 +2584,14 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
if (err)
goto team_put;
opt_inst->changed = true;
+
+ /* dumb/evil user-space can send us duplicate opt,
+ * keep only the last one
+ */
+ if (__team_option_inst_tmp_find(&opt_inst_list,
+ opt_inst))
+ continue;
+
list_add(&opt_inst->tmp_list, &opt_inst_list);
}
if (!opt_found) {
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 28cfa642e39a..ef33950a45d9 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -78,6 +78,7 @@
#include <linux/mutex.h>
#include <linux/uaccess.h>
+#include <linux/proc_fs.h>
/* Uncomment to enable debugging */
/* #define TUN_DEBUG 1 */
@@ -742,8 +743,15 @@ static void __tun_detach(struct tun_file *tfile, bool clean)
static void tun_detach(struct tun_file *tfile, bool clean)
{
+ struct tun_struct *tun;
+ struct net_device *dev;
+
rtnl_lock();
+ tun = rtnl_dereference(tfile->tun);
+ dev = tun ? tun->dev : NULL;
__tun_detach(tfile, clean);
+ if (dev)
+ netdev_state_change(dev);
rtnl_unlock();
}
@@ -1094,12 +1102,7 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
goto drop;
len = run_ebpf_filter(tun, skb, len);
-
- /* Trim extra bytes since we may insert vlan proto & TCI
- * in tun_put_user().
- */
- len -= skb_vlan_tag_present(skb) ? sizeof(struct veth) : 0;
- if (len <= 0 || pskb_trim(skb, len))
+ if (len == 0 || pskb_trim(skb, len))
goto drop;
if (unlikely(skb_orphan_frags_rx(skb, GFP_ATOMIC)))
@@ -1613,7 +1616,6 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun,
unsigned int delta = 0;
char *buf;
size_t copied;
- bool xdp_xmit = false;
int err, pad = TUN_RX_PAD;
rcu_read_lock();
@@ -1671,8 +1673,14 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun,
preempt_enable();
return NULL;
case XDP_TX:
- xdp_xmit = true;
- /* fall through */
+ get_page(alloc_frag->page);
+ alloc_frag->offset += buflen;
+ if (tun_xdp_xmit(tun->dev, &xdp))
+ goto err_redirect;
+ tun_xdp_flush(tun->dev);
+ rcu_read_unlock();
+ preempt_enable();
+ return NULL;
case XDP_PASS:
delta = orig_data - xdp.data;
break;
@@ -1699,14 +1707,6 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun,
get_page(alloc_frag->page);
alloc_frag->offset += buflen;
- if (xdp_xmit) {
- skb->dev = tun->dev;
- generic_xdp_tx(skb, xdp_prog);
- rcu_read_unlock();
- preempt_enable();
- return NULL;
- }
-
rcu_read_unlock();
preempt_enable();
@@ -2287,11 +2287,67 @@ static int tun_validate(struct nlattr *tb[], struct nlattr *data[],
return -EINVAL;
}
+static size_t tun_get_size(const struct net_device *dev)
+{
+ BUILD_BUG_ON(sizeof(u32) != sizeof(uid_t));
+ BUILD_BUG_ON(sizeof(u32) != sizeof(gid_t));
+
+ return nla_total_size(sizeof(uid_t)) + /* OWNER */
+ nla_total_size(sizeof(gid_t)) + /* GROUP */
+ nla_total_size(sizeof(u8)) + /* TYPE */
+ nla_total_size(sizeof(u8)) + /* PI */
+ nla_total_size(sizeof(u8)) + /* VNET_HDR */
+ nla_total_size(sizeof(u8)) + /* PERSIST */
+ nla_total_size(sizeof(u8)) + /* MULTI_QUEUE */
+ nla_total_size(sizeof(u32)) + /* NUM_QUEUES */
+ nla_total_size(sizeof(u32)) + /* NUM_DISABLED_QUEUES */
+ 0;
+}
+
+static int tun_fill_info(struct sk_buff *skb, const struct net_device *dev)
+{
+ struct tun_struct *tun = netdev_priv(dev);
+
+ if (nla_put_u8(skb, IFLA_TUN_TYPE, tun->flags & TUN_TYPE_MASK))
+ goto nla_put_failure;
+ if (uid_valid(tun->owner) &&
+ nla_put_u32(skb, IFLA_TUN_OWNER,
+ from_kuid_munged(current_user_ns(), tun->owner)))
+ goto nla_put_failure;
+ if (gid_valid(tun->group) &&
+ nla_put_u32(skb, IFLA_TUN_GROUP,
+ from_kgid_munged(current_user_ns(), tun->group)))
+ goto nla_put_failure;
+ if (nla_put_u8(skb, IFLA_TUN_PI, !(tun->flags & IFF_NO_PI)))
+ goto nla_put_failure;
+ if (nla_put_u8(skb, IFLA_TUN_VNET_HDR, !!(tun->flags & IFF_VNET_HDR)))
+ goto nla_put_failure;
+ if (nla_put_u8(skb, IFLA_TUN_PERSIST, !!(tun->flags & IFF_PERSIST)))
+ goto nla_put_failure;
+ if (nla_put_u8(skb, IFLA_TUN_MULTI_QUEUE,
+ !!(tun->flags & IFF_MULTI_QUEUE)))
+ goto nla_put_failure;
+ if (tun->flags & IFF_MULTI_QUEUE) {
+ if (nla_put_u32(skb, IFLA_TUN_NUM_QUEUES, tun->numqueues))
+ goto nla_put_failure;
+ if (nla_put_u32(skb, IFLA_TUN_NUM_DISABLED_QUEUES,
+ tun->numdisabled))
+ goto nla_put_failure;
+ }
+
+ return 0;
+
+nla_put_failure:
+ return -EMSGSIZE;
+}
+
static struct rtnl_link_ops tun_link_ops __read_mostly = {
.kind = DRV_NAME,
.priv_size = sizeof(struct tun_struct),
.setup = tun_setup,
.validate = tun_validate,
+ .get_size = tun_get_size,
+ .fill_info = tun_fill_info,
};
static void tun_sock_write_space(struct sock *sk)
@@ -2508,10 +2564,15 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
/* One or more queue has already been attached, no need
* to initialize the device again.
*/
+ netdev_state_change(dev);
return 0;
}
- }
- else {
+
+ tun->flags = (tun->flags & ~TUN_FEATURES) |
+ (ifr->ifr_flags & TUN_FEATURES);
+
+ netdev_state_change(dev);
+ } else {
char *name;
unsigned long flags = 0;
int queues = ifr->ifr_flags & IFF_MULTI_QUEUE ?
@@ -2588,6 +2649,9 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
~(NETIF_F_HW_VLAN_CTAG_TX |
NETIF_F_HW_VLAN_STAG_TX);
+ tun->flags = (tun->flags & ~TUN_FEATURES) |
+ (ifr->ifr_flags & TUN_FEATURES);
+
INIT_LIST_HEAD(&tun->disabled);
err = tun_attach(tun, file, false, ifr->ifr_flags & IFF_NAPI);
if (err < 0)
@@ -2602,9 +2666,6 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
tun_debug(KERN_INFO, tun, "tun_set_iff\n");
- tun->flags = (tun->flags & ~TUN_FEATURES) |
- (ifr->ifr_flags & TUN_FEATURES);
-
/* Make sure persistent devices do not get stuck in
* xoff state.
*/
@@ -2751,6 +2812,9 @@ static int tun_set_queue(struct file *file, struct ifreq *ifr)
} else
ret = -EINVAL;
+ if (ret >= 0)
+ netdev_state_change(tun->dev);
+
unlock:
rtnl_unlock();
return ret;
@@ -2783,6 +2847,7 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
struct tun_struct *tun;
void __user* argp = (void __user*)arg;
struct ifreq ifr;
+ struct net *net;
kuid_t owner;
kgid_t group;
int sndbuf;
@@ -2790,8 +2855,10 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
unsigned int ifindex;
int le;
int ret;
+ bool do_notify = false;
- if (cmd == TUNSETIFF || cmd == TUNSETQUEUE || _IOC_TYPE(cmd) == SOCK_IOC_TYPE) {
+ if (cmd == TUNSETIFF || cmd == TUNSETQUEUE ||
+ (_IOC_TYPE(cmd) == SOCK_IOC_TYPE && cmd != SIOCGSKNS)) {
if (copy_from_user(&ifr, argp, ifreq_len))
return -EFAULT;
} else {
@@ -2811,6 +2878,7 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
rtnl_lock();
tun = tun_get(tfile);
+ net = sock_net(&tfile->sk);
if (cmd == TUNSETIFF) {
ret = -EEXIST;
if (tun)
@@ -2818,7 +2886,7 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
ifr.ifr_name[IFNAMSIZ-1] = '\0';
- ret = tun_set_iff(sock_net(&tfile->sk), file, &ifr);
+ ret = tun_set_iff(net, file, &ifr);
if (ret)
goto unlock;
@@ -2840,6 +2908,14 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
tfile->ifindex = ifindex;
goto unlock;
}
+ if (cmd == SIOCGSKNS) {
+ ret = -EPERM;
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
+ goto unlock;
+
+ ret = open_related_ns(&net->ns, get_net_ns);
+ goto unlock;
+ }
ret = -EBADFD;
if (!tun)
@@ -2876,10 +2952,12 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
if (arg && !(tun->flags & IFF_PERSIST)) {
tun->flags |= IFF_PERSIST;
__module_get(THIS_MODULE);
+ do_notify = true;
}
if (!arg && (tun->flags & IFF_PERSIST)) {
tun->flags &= ~IFF_PERSIST;
module_put(THIS_MODULE);
+ do_notify = true;
}
tun_debug(KERN_INFO, tun, "persist %s\n",
@@ -2894,6 +2972,7 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
break;
}
tun->owner = owner;
+ do_notify = true;
tun_debug(KERN_INFO, tun, "owner set to %u\n",
from_kuid(&init_user_ns, tun->owner));
break;
@@ -2906,6 +2985,7 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
break;
}
tun->group = group;
+ do_notify = true;
tun_debug(KERN_INFO, tun, "group set to %u\n",
from_kgid(&init_user_ns, tun->group));
break;
@@ -3065,6 +3145,9 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
break;
}
+ if (do_notify)
+ netdev_state_change(tun->dev);
+
unlock:
rtnl_unlock();
if (tun)
diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c
index f32261ecd215..a6ef75907ae9 100644
--- a/drivers/net/usb/ax88179_178a.c
+++ b/drivers/net/usb/ax88179_178a.c
@@ -1556,7 +1556,6 @@ static int ax88179_reset(struct usbnet *dev)
ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, ETH_ALEN,
dev->net->dev_addr);
- memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN);
/* RX bulk configuration */
memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5);
diff --git a/drivers/net/usb/cdc_eem.c b/drivers/net/usb/cdc_eem.c
index f7180f8db39e..61ea4eaace5d 100644
--- a/drivers/net/usb/cdc_eem.c
+++ b/drivers/net/usb/cdc_eem.c
@@ -83,11 +83,8 @@ static int eem_bind(struct usbnet *dev, struct usb_interface *intf)
int status = 0;
status = usbnet_get_endpoints(dev, intf);
- if (status < 0) {
- usb_set_intfdata(intf, NULL);
- usb_driver_release_interface(driver_of(intf), intf);
+ if (status < 0)
return status;
- }
/* no jumbogram (16K) support for now */
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index fff4b13eece2..5c42cf81a08b 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -902,6 +902,12 @@ static const struct usb_device_id products[] = {
USB_CDC_PROTO_NONE),
.driver_info = (unsigned long)&wwan_info,
}, {
+ /* Cinterion AHS3 modem by GEMALTO */
+ USB_DEVICE_AND_INTERFACE_INFO(0x1e2d, 0x0055, USB_CLASS_COMM,
+ USB_CDC_SUBCLASS_ETHERNET,
+ USB_CDC_PROTO_NONE),
+ .driver_info = (unsigned long)&wwan_info,
+}, {
USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ETHERNET,
USB_CDC_PROTO_NONE),
.driver_info = (unsigned long) &cdc_info,
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index 9e1b74590682..90d07ed224d5 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -58,7 +58,7 @@ static bool prefer_mbim = true;
#else
static bool prefer_mbim;
#endif
-module_param(prefer_mbim, bool, S_IRUGO | S_IWUSR);
+module_param(prefer_mbim, bool, 0644);
MODULE_PARM_DESC(prefer_mbim, "Prefer MBIM setting on dual NCM/MBIM functions");
static void cdc_ncm_txpath_bh(unsigned long param);
@@ -281,10 +281,10 @@ static ssize_t cdc_ncm_store_tx_timer_usecs(struct device *d, struct device_att
return len;
}
-static DEVICE_ATTR(min_tx_pkt, S_IRUGO | S_IWUSR, cdc_ncm_show_min_tx_pkt, cdc_ncm_store_min_tx_pkt);
-static DEVICE_ATTR(rx_max, S_IRUGO | S_IWUSR, cdc_ncm_show_rx_max, cdc_ncm_store_rx_max);
-static DEVICE_ATTR(tx_max, S_IRUGO | S_IWUSR, cdc_ncm_show_tx_max, cdc_ncm_store_tx_max);
-static DEVICE_ATTR(tx_timer_usecs, S_IRUGO | S_IWUSR, cdc_ncm_show_tx_timer_usecs, cdc_ncm_store_tx_timer_usecs);
+static DEVICE_ATTR(min_tx_pkt, 0644, cdc_ncm_show_min_tx_pkt, cdc_ncm_store_min_tx_pkt);
+static DEVICE_ATTR(rx_max, 0644, cdc_ncm_show_rx_max, cdc_ncm_store_rx_max);
+static DEVICE_ATTR(tx_max, 0644, cdc_ncm_show_tx_max, cdc_ncm_store_tx_max);
+static DEVICE_ATTR(tx_timer_usecs, 0644, cdc_ncm_show_tx_timer_usecs, cdc_ncm_store_tx_timer_usecs);
static ssize_t ndp_to_end_show(struct device *d, struct device_attribute *attr, char *buf)
{
@@ -335,7 +335,7 @@ static ssize_t cdc_ncm_show_##name(struct device *d, struct device_attribute *at
struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; \
return sprintf(buf, format "\n", tocpu(ctx->ncm_parm.name)); \
} \
-static DEVICE_ATTR(name, S_IRUGO, cdc_ncm_show_##name, NULL)
+static DEVICE_ATTR(name, 0444, cdc_ncm_show_##name, NULL)
NCM_PARM_ATTR(bmNtbFormatsSupported, "0x%04x", le16_to_cpu);
NCM_PARM_ATTR(dwNtbInMaxSize, "%u", le32_to_cpu);
diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c
index 981c931a7a1f..e53883ad6107 100644
--- a/drivers/net/usb/hso.c
+++ b/drivers/net/usb/hso.c
@@ -519,7 +519,7 @@ static ssize_t hso_sysfs_show_porttype(struct device *dev,
return sprintf(buf, "%s\n", port_name);
}
-static DEVICE_ATTR(hsotype, S_IRUGO, hso_sysfs_show_porttype, NULL);
+static DEVICE_ATTR(hsotype, 0444, hso_sysfs_show_porttype, NULL);
static struct attribute *hso_serial_dev_attrs[] = {
&dev_attr_hsotype.attr,
@@ -3289,12 +3289,12 @@ MODULE_LICENSE("GPL");
/* change the debug level (eg: insmod hso.ko debug=0x04) */
MODULE_PARM_DESC(debug, "debug level mask [0x01 | 0x02 | 0x04 | 0x08 | 0x10]");
-module_param(debug, int, S_IRUGO | S_IWUSR);
+module_param(debug, int, 0644);
/* set the major tty number (eg: insmod hso.ko tty_major=245) */
MODULE_PARM_DESC(tty_major, "Set the major tty number");
-module_param(tty_major, int, S_IRUGO | S_IWUSR);
+module_param(tty_major, int, 0644);
/* disable network interface (eg: insmod hso.ko disable_net=1) */
MODULE_PARM_DESC(disable_net, "Disable the network interface");
-module_param(disable_net, int, S_IRUGO | S_IWUSR);
+module_param(disable_net, int, 0644);
diff --git a/drivers/net/usb/kalmia.c b/drivers/net/usb/kalmia.c
index ce0b0b4e3a57..bd2ba3659028 100644
--- a/drivers/net/usb/kalmia.c
+++ b/drivers/net/usb/kalmia.c
@@ -114,14 +114,14 @@ kalmia_init_and_get_ethernet_addr(struct usbnet *dev, u8 *ethernet_addr)
return -ENOMEM;
memcpy(usb_buf, init_msg_1, 12);
- status = kalmia_send_init_packet(dev, usb_buf, sizeof(init_msg_1)
- / sizeof(init_msg_1[0]), usb_buf, 24);
+ status = kalmia_send_init_packet(dev, usb_buf, ARRAY_SIZE(init_msg_1),
+ usb_buf, 24);
if (status != 0)
return status;
memcpy(usb_buf, init_msg_2, 12);
- status = kalmia_send_init_packet(dev, usb_buf, sizeof(init_msg_2)
- / sizeof(init_msg_2[0]), usb_buf, 28);
+ status = kalmia_send_init_packet(dev, usb_buf, ARRAY_SIZE(init_msg_2),
+ usb_buf, 28);
if (status != 0)
return status;
@@ -150,12 +150,8 @@ kalmia_bind(struct usbnet *dev, struct usb_interface *intf)
dev->rx_urb_size = dev->hard_mtu * 10; // Found as optimal after testing
status = kalmia_init_and_get_ethernet_addr(dev, ethernet_addr);
-
- if (status) {
- usb_set_intfdata(intf, NULL);
- usb_driver_release_interface(driver_of(intf), intf);
+ if (status)
return status;
- }
memcpy(dev->net->dev_addr, ethernet_addr, ETH_ALEN);
diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c
index 60a604cc7647..0867f7275852 100644
--- a/drivers/net/usb/lan78xx.c
+++ b/drivers/net/usb/lan78xx.c
@@ -928,7 +928,8 @@ static int lan78xx_read_otp(struct lan78xx_net *dev, u32 offset,
offset += 0x100;
else
ret = -EINVAL;
- ret = lan78xx_read_raw_otp(dev, offset, length, data);
+ if (!ret)
+ ret = lan78xx_read_raw_otp(dev, offset, length, data);
}
return ret;
@@ -2082,10 +2083,6 @@ static int lan78xx_phy_init(struct lan78xx_net *dev)
dev->fc_autoneg = phydev->autoneg;
- phy_start(phydev);
-
- netif_dbg(dev, ifup, dev->net, "phy initialised successfully");
-
return 0;
error:
@@ -2351,6 +2348,7 @@ static int lan78xx_reset(struct lan78xx_net *dev)
u32 buf;
int ret = 0;
unsigned long timeout;
+ u8 sig;
ret = lan78xx_read_reg(dev, HW_CFG, &buf);
buf |= HW_CFG_LRST_;
@@ -2450,6 +2448,15 @@ static int lan78xx_reset(struct lan78xx_net *dev)
/* LAN7801 only has RGMII mode */
if (dev->chipid == ID_REV_CHIP_ID_7801_)
buf &= ~MAC_CR_GMII_EN_;
+
+ if (dev->chipid == ID_REV_CHIP_ID_7800_) {
+ ret = lan78xx_read_raw_eeprom(dev, 0, 1, &sig);
+ if (!ret && sig != EEPROM_INDICATOR) {
+ /* Implies there is no external eeprom. Set mac speed */
+ netdev_info(dev->net, "No External EEPROM. Setting MAC Speed\n");
+ buf |= MAC_CR_AUTO_DUPLEX_ | MAC_CR_AUTO_SPEED_;
+ }
+ }
ret = lan78xx_write_reg(dev, MAC_CR, buf);
ret = lan78xx_read_reg(dev, MAC_TX, &buf);
@@ -2496,7 +2503,7 @@ static void lan78xx_init_stats(struct lan78xx_net *dev)
dev->stats.rollover_max.eee_tx_lpi_transitions = 0xFFFFFFFF;
dev->stats.rollover_max.eee_tx_lpi_time = 0xFFFFFFFF;
- lan78xx_defer_kevent(dev, EVENT_STAT_UPDATE);
+ set_bit(EVENT_STAT_UPDATE, &dev->flags);
}
static int lan78xx_open(struct net_device *net)
@@ -2508,13 +2515,9 @@ static int lan78xx_open(struct net_device *net)
if (ret < 0)
goto out;
- ret = lan78xx_reset(dev);
- if (ret < 0)
- goto done;
+ phy_start(net->phydev);
- ret = lan78xx_phy_init(dev);
- if (ret < 0)
- goto done;
+ netif_dbg(dev, ifup, dev->net, "phy initialised successfully");
/* for Link Check */
if (dev->urb_intr) {
@@ -2575,13 +2578,8 @@ static int lan78xx_stop(struct net_device *net)
if (timer_pending(&dev->stat_monitor))
del_timer_sync(&dev->stat_monitor);
- phy_unregister_fixup_for_uid(PHY_KSZ9031RNX, 0xfffffff0);
- phy_unregister_fixup_for_uid(PHY_LAN8835, 0xfffffff0);
-
- phy_stop(net->phydev);
- phy_disconnect(net->phydev);
-
- net->phydev = NULL;
+ if (net->phydev)
+ phy_stop(net->phydev);
clear_bit(EVENT_DEV_OPEN, &dev->flags);
netif_stop_queue(net);
@@ -2863,8 +2861,7 @@ static int lan78xx_bind(struct lan78xx_net *dev, struct usb_interface *intf)
if (ret < 0) {
netdev_warn(dev->net,
"lan78xx_setup_irq_domain() failed : %d", ret);
- kfree(pdata);
- return ret;
+ goto out1;
}
dev->net->hard_header_len += TX_OVERHEAD;
@@ -2872,14 +2869,32 @@ static int lan78xx_bind(struct lan78xx_net *dev, struct usb_interface *intf)
/* Init all registers */
ret = lan78xx_reset(dev);
+ if (ret) {
+ netdev_warn(dev->net, "Registers INIT FAILED....");
+ goto out2;
+ }
ret = lan78xx_mdio_init(dev);
+ if (ret) {
+ netdev_warn(dev->net, "MDIO INIT FAILED.....");
+ goto out2;
+ }
dev->net->flags |= IFF_MULTICAST;
pdata->wol = WAKE_MAGIC;
return ret;
+
+out2:
+ lan78xx_remove_irq_domain(dev);
+
+out1:
+ netdev_warn(dev->net, "Bind routine FAILED");
+ cancel_work_sync(&pdata->set_multicast);
+ cancel_work_sync(&pdata->set_vlan);
+ kfree(pdata);
+ return ret;
}
static void lan78xx_unbind(struct lan78xx_net *dev, struct usb_interface *intf)
@@ -2891,6 +2906,8 @@ static void lan78xx_unbind(struct lan78xx_net *dev, struct usb_interface *intf)
lan78xx_remove_mdio(dev);
if (pdata) {
+ cancel_work_sync(&pdata->set_multicast);
+ cancel_work_sync(&pdata->set_vlan);
netif_dbg(dev, ifdown, dev->net, "free pdata");
kfree(pdata);
pdata = NULL;
@@ -3477,8 +3494,13 @@ static void lan78xx_disconnect(struct usb_interface *intf)
return;
udev = interface_to_usbdev(intf);
-
net = dev->net;
+
+ phy_unregister_fixup_for_uid(PHY_KSZ9031RNX, 0xfffffff0);
+ phy_unregister_fixup_for_uid(PHY_LAN8835, 0xfffffff0);
+
+ phy_disconnect(net->phydev);
+
unregister_netdev(net);
cancel_delayed_work_sync(&dev->wq);
@@ -3634,8 +3656,14 @@ static int lan78xx_probe(struct usb_interface *intf,
pm_runtime_set_autosuspend_delay(&udev->dev,
DEFAULT_AUTOSUSPEND_DELAY);
+ ret = lan78xx_phy_init(dev);
+ if (ret < 0)
+ goto out4;
+
return 0;
+out4:
+ unregister_netdev(netdev);
out3:
lan78xx_unbind(dev, intf);
out2:
@@ -3983,7 +4011,7 @@ static int lan78xx_reset_resume(struct usb_interface *intf)
lan78xx_reset(dev);
- lan78xx_phy_init(dev);
+ phy_start(dev->net->phydev);
return lan78xx_resume(intf);
}
diff --git a/drivers/net/usb/lg-vl600.c b/drivers/net/usb/lg-vl600.c
index dbabd7ca5268..257916f172cd 100644
--- a/drivers/net/usb/lg-vl600.c
+++ b/drivers/net/usb/lg-vl600.c
@@ -157,12 +157,8 @@ static int vl600_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
s->current_rx_buf = skb_copy_expand(skb, 0,
le32_to_cpup(&frame->len), GFP_ATOMIC);
- if (!s->current_rx_buf) {
- netif_err(dev, ifup, dev->net, "Reserving %i bytes "
- "for packet assembly failed.\n",
- le32_to_cpup(&frame->len));
+ if (!s->current_rx_buf)
dev->net->stats.rx_errors++;
- }
return 0;
}
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index 76ac48095c29..c853e7410f5a 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -1104,6 +1104,10 @@ static const struct usb_device_id products[] = {
{QMI_FIXED_INTF(0x0846, 0x68a2, 8)},
{QMI_FIXED_INTF(0x12d1, 0x140c, 1)}, /* Huawei E173 */
{QMI_FIXED_INTF(0x12d1, 0x14ac, 1)}, /* Huawei E1820 */
+ {QMI_FIXED_INTF(0x1435, 0xd181, 3)}, /* Wistron NeWeb D18Q1 */
+ {QMI_FIXED_INTF(0x1435, 0xd181, 4)}, /* Wistron NeWeb D18Q1 */
+ {QMI_FIXED_INTF(0x1435, 0xd181, 5)}, /* Wistron NeWeb D18Q1 */
+ {QMI_FIXED_INTF(0x1435, 0xd191, 4)}, /* Wistron NeWeb D19Q1 */
{QMI_FIXED_INTF(0x16d8, 0x6003, 0)}, /* CMOTech 6003 */
{QMI_FIXED_INTF(0x16d8, 0x6007, 0)}, /* CMOTech CHE-628S */
{QMI_FIXED_INTF(0x16d8, 0x6008, 0)}, /* CMOTech CMU-301 */
@@ -1180,6 +1184,7 @@ static const struct usb_device_id products[] = {
{QMI_FIXED_INTF(0x19d2, 0x2002, 4)}, /* ZTE (Vodafone) K3765-Z */
{QMI_FIXED_INTF(0x2001, 0x7e19, 4)}, /* D-Link DWM-221 B1 */
{QMI_FIXED_INTF(0x2001, 0x7e35, 4)}, /* D-Link DWM-222 */
+ {QMI_FIXED_INTF(0x2020, 0x2033, 4)}, /* BroadMobi BM806U */
{QMI_FIXED_INTF(0x0f3d, 0x68a2, 8)}, /* Sierra Wireless MC7700 */
{QMI_FIXED_INTF(0x114f, 0x68a2, 8)}, /* Sierra Wireless MC7750 */
{QMI_FIXED_INTF(0x1199, 0x68a2, 8)}, /* Sierra Wireless MC7710 in QMI mode */
@@ -1240,6 +1245,7 @@ static const struct usb_device_id products[] = {
{QMI_FIXED_INTF(0x413c, 0x81b6, 8)}, /* Dell Wireless 5811e */
{QMI_FIXED_INTF(0x413c, 0x81b6, 10)}, /* Dell Wireless 5811e */
{QMI_FIXED_INTF(0x03f0, 0x4e1d, 8)}, /* HP lt4111 LTE/EV-DO/HSPA+ Gobi 4G Module */
+ {QMI_FIXED_INTF(0x03f0, 0x9d1d, 1)}, /* HP lt4120 Snapdragon X5 LTE */
{QMI_FIXED_INTF(0x22de, 0x9061, 3)}, /* WeTelecom WPD-600N */
{QMI_FIXED_INTF(0x1e0e, 0x9001, 5)}, /* SIMCom 7230E */
{QMI_QUIRK_SET_DTR(0x2c7c, 0x0125, 4)}, /* Quectel EC25, EC20 R2.0 Mini PCIe */
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 23374603e4d9..770422e953f7 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -147,6 +147,17 @@ struct receive_queue {
struct xdp_rxq_info xdp_rxq;
};
+/* Control VQ buffers: protected by the rtnl lock */
+struct control_buf {
+ struct virtio_net_ctrl_hdr hdr;
+ virtio_net_ctrl_ack status;
+ struct virtio_net_ctrl_mq mq;
+ u8 promisc;
+ u8 allmulti;
+ __virtio16 vid;
+ __virtio64 offloads;
+};
+
struct virtnet_info {
struct virtio_device *vdev;
struct virtqueue *cvq;
@@ -192,14 +203,7 @@ struct virtnet_info {
struct hlist_node node;
struct hlist_node node_dead;
- /* Control VQ buffers: protected by the rtnl lock */
- struct virtio_net_ctrl_hdr ctrl_hdr;
- virtio_net_ctrl_ack ctrl_status;
- struct virtio_net_ctrl_mq ctrl_mq;
- u8 ctrl_promisc;
- u8 ctrl_allmulti;
- u16 ctrl_vid;
- u64 ctrl_offloads;
+ struct control_buf *ctrl;
/* Ethtool settings */
u8 duplex;
@@ -1269,7 +1273,9 @@ static int virtnet_poll(struct napi_struct *napi, int budget)
{
struct receive_queue *rq =
container_of(napi, struct receive_queue, napi);
- unsigned int received;
+ struct virtnet_info *vi = rq->vq->vdev->priv;
+ struct send_queue *sq;
+ unsigned int received, qp;
bool xdp_xmit = false;
virtnet_poll_cleantx(rq);
@@ -1280,8 +1286,13 @@ static int virtnet_poll(struct napi_struct *napi, int budget)
if (received < budget)
virtqueue_napi_complete(napi, rq->vq, received);
- if (xdp_xmit)
+ if (xdp_xmit) {
+ qp = vi->curr_queue_pairs - vi->xdp_queue_pairs +
+ smp_processor_id();
+ sq = &vi->sq[qp];
+ virtqueue_kick(sq->vq);
xdp_do_flush_map();
+ }
return received;
}
@@ -1454,25 +1465,25 @@ static bool virtnet_send_command(struct virtnet_info *vi, u8 class, u8 cmd,
/* Caller should know better */
BUG_ON(!virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ));
- vi->ctrl_status = ~0;
- vi->ctrl_hdr.class = class;
- vi->ctrl_hdr.cmd = cmd;
+ vi->ctrl->status = ~0;
+ vi->ctrl->hdr.class = class;
+ vi->ctrl->hdr.cmd = cmd;
/* Add header */
- sg_init_one(&hdr, &vi->ctrl_hdr, sizeof(vi->ctrl_hdr));
+ sg_init_one(&hdr, &vi->ctrl->hdr, sizeof(vi->ctrl->hdr));
sgs[out_num++] = &hdr;
if (out)
sgs[out_num++] = out;
/* Add return status. */
- sg_init_one(&stat, &vi->ctrl_status, sizeof(vi->ctrl_status));
+ sg_init_one(&stat, &vi->ctrl->status, sizeof(vi->ctrl->status));
sgs[out_num] = &stat;
BUG_ON(out_num + 1 > ARRAY_SIZE(sgs));
virtqueue_add_sgs(vi->cvq, sgs, out_num, 1, vi, GFP_ATOMIC);
if (unlikely(!virtqueue_kick(vi->cvq)))
- return vi->ctrl_status == VIRTIO_NET_OK;
+ return vi->ctrl->status == VIRTIO_NET_OK;
/* Spin for a response, the kick causes an ioport write, trapping
* into the hypervisor, so the request should be handled immediately.
@@ -1481,7 +1492,7 @@ static bool virtnet_send_command(struct virtnet_info *vi, u8 class, u8 cmd,
!virtqueue_is_broken(vi->cvq))
cpu_relax();
- return vi->ctrl_status == VIRTIO_NET_OK;
+ return vi->ctrl->status == VIRTIO_NET_OK;
}
static int virtnet_set_mac_address(struct net_device *dev, void *p)
@@ -1593,8 +1604,8 @@ static int _virtnet_set_queues(struct virtnet_info *vi, u16 queue_pairs)
if (!vi->has_cvq || !virtio_has_feature(vi->vdev, VIRTIO_NET_F_MQ))
return 0;
- vi->ctrl_mq.virtqueue_pairs = cpu_to_virtio16(vi->vdev, queue_pairs);
- sg_init_one(&sg, &vi->ctrl_mq, sizeof(vi->ctrl_mq));
+ vi->ctrl->mq.virtqueue_pairs = cpu_to_virtio16(vi->vdev, queue_pairs);
+ sg_init_one(&sg, &vi->ctrl->mq, sizeof(vi->ctrl->mq));
if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_MQ,
VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, &sg)) {
@@ -1653,22 +1664,22 @@ static void virtnet_set_rx_mode(struct net_device *dev)
if (!virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_RX))
return;
- vi->ctrl_promisc = ((dev->flags & IFF_PROMISC) != 0);
- vi->ctrl_allmulti = ((dev->flags & IFF_ALLMULTI) != 0);
+ vi->ctrl->promisc = ((dev->flags & IFF_PROMISC) != 0);
+ vi->ctrl->allmulti = ((dev->flags & IFF_ALLMULTI) != 0);
- sg_init_one(sg, &vi->ctrl_promisc, sizeof(vi->ctrl_promisc));
+ sg_init_one(sg, &vi->ctrl->promisc, sizeof(vi->ctrl->promisc));
if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_RX,
VIRTIO_NET_CTRL_RX_PROMISC, sg))
dev_warn(&dev->dev, "Failed to %sable promisc mode.\n",
- vi->ctrl_promisc ? "en" : "dis");
+ vi->ctrl->promisc ? "en" : "dis");
- sg_init_one(sg, &vi->ctrl_allmulti, sizeof(vi->ctrl_allmulti));
+ sg_init_one(sg, &vi->ctrl->allmulti, sizeof(vi->ctrl->allmulti));
if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_RX,
VIRTIO_NET_CTRL_RX_ALLMULTI, sg))
dev_warn(&dev->dev, "Failed to %sable allmulti mode.\n",
- vi->ctrl_allmulti ? "en" : "dis");
+ vi->ctrl->allmulti ? "en" : "dis");
uc_count = netdev_uc_count(dev);
mc_count = netdev_mc_count(dev);
@@ -1714,8 +1725,8 @@ static int virtnet_vlan_rx_add_vid(struct net_device *dev,
struct virtnet_info *vi = netdev_priv(dev);
struct scatterlist sg;
- vi->ctrl_vid = vid;
- sg_init_one(&sg, &vi->ctrl_vid, sizeof(vi->ctrl_vid));
+ vi->ctrl->vid = cpu_to_virtio16(vi->vdev, vid);
+ sg_init_one(&sg, &vi->ctrl->vid, sizeof(vi->ctrl->vid));
if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_VLAN,
VIRTIO_NET_CTRL_VLAN_ADD, &sg))
@@ -1729,8 +1740,8 @@ static int virtnet_vlan_rx_kill_vid(struct net_device *dev,
struct virtnet_info *vi = netdev_priv(dev);
struct scatterlist sg;
- vi->ctrl_vid = vid;
- sg_init_one(&sg, &vi->ctrl_vid, sizeof(vi->ctrl_vid));
+ vi->ctrl->vid = cpu_to_virtio16(vi->vdev, vid);
+ sg_init_one(&sg, &vi->ctrl->vid, sizeof(vi->ctrl->vid));
if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_VLAN,
VIRTIO_NET_CTRL_VLAN_DEL, &sg))
@@ -2126,9 +2137,9 @@ static int virtnet_restore_up(struct virtio_device *vdev)
static int virtnet_set_guest_offloads(struct virtnet_info *vi, u64 offloads)
{
struct scatterlist sg;
- vi->ctrl_offloads = cpu_to_virtio64(vi->vdev, offloads);
+ vi->ctrl->offloads = cpu_to_virtio64(vi->vdev, offloads);
- sg_init_one(&sg, &vi->ctrl_offloads, sizeof(vi->ctrl_offloads));
+ sg_init_one(&sg, &vi->ctrl->offloads, sizeof(vi->ctrl->offloads));
if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_GUEST_OFFLOADS,
VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET, &sg)) {
@@ -2351,6 +2362,7 @@ static void virtnet_free_queues(struct virtnet_info *vi)
kfree(vi->rq);
kfree(vi->sq);
+ kfree(vi->ctrl);
}
static void _free_receive_bufs(struct virtnet_info *vi)
@@ -2543,6 +2555,9 @@ static int virtnet_alloc_queues(struct virtnet_info *vi)
{
int i;
+ vi->ctrl = kzalloc(sizeof(*vi->ctrl), GFP_KERNEL);
+ if (!vi->ctrl)
+ goto err_ctrl;
vi->sq = kzalloc(sizeof(*vi->sq) * vi->max_queue_pairs, GFP_KERNEL);
if (!vi->sq)
goto err_sq;
@@ -2571,6 +2586,8 @@ static int virtnet_alloc_queues(struct virtnet_info *vi)
err_rq:
kfree(vi->sq);
err_sq:
+ kfree(vi->ctrl);
+err_ctrl:
return -ENOMEM;
}
@@ -2857,8 +2874,8 @@ static int virtnet_probe(struct virtio_device *vdev)
/* Assume link up if device can't report link status,
otherwise get link status from config. */
+ netif_carrier_off(dev);
if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS)) {
- netif_carrier_off(dev);
schedule_work(&vi->config_work);
} else {
vi->status = VIRTIO_NET_S_LINK_UP;
diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c
index e04937f44f33..9ebe2a689966 100644
--- a/drivers/net/vmxnet3/vmxnet3_drv.c
+++ b/drivers/net/vmxnet3/vmxnet3_drv.c
@@ -1218,6 +1218,7 @@ vmxnet3_get_hdr_len(struct vmxnet3_adapter *adapter, struct sk_buff *skb,
union {
void *ptr;
struct ethhdr *eth;
+ struct vlan_ethhdr *veth;
struct iphdr *ipv4;
struct ipv6hdr *ipv6;
struct tcphdr *tcp;
@@ -1228,16 +1229,24 @@ vmxnet3_get_hdr_len(struct vmxnet3_adapter *adapter, struct sk_buff *skb,
if (unlikely(sizeof(struct iphdr) + sizeof(struct tcphdr) > maplen))
return 0;
+ if (skb->protocol == cpu_to_be16(ETH_P_8021Q) ||
+ skb->protocol == cpu_to_be16(ETH_P_8021AD))
+ hlen = sizeof(struct vlan_ethhdr);
+ else
+ hlen = sizeof(struct ethhdr);
+
hdr.eth = eth_hdr(skb);
if (gdesc->rcd.v4) {
- BUG_ON(hdr.eth->h_proto != htons(ETH_P_IP));
- hdr.ptr += sizeof(struct ethhdr);
+ BUG_ON(hdr.eth->h_proto != htons(ETH_P_IP) &&
+ hdr.veth->h_vlan_encapsulated_proto != htons(ETH_P_IP));
+ hdr.ptr += hlen;
BUG_ON(hdr.ipv4->protocol != IPPROTO_TCP);
hlen = hdr.ipv4->ihl << 2;
hdr.ptr += hdr.ipv4->ihl << 2;
} else if (gdesc->rcd.v6) {
- BUG_ON(hdr.eth->h_proto != htons(ETH_P_IPV6));
- hdr.ptr += sizeof(struct ethhdr);
+ BUG_ON(hdr.eth->h_proto != htons(ETH_P_IPV6) &&
+ hdr.veth->h_vlan_encapsulated_proto != htons(ETH_P_IPV6));
+ hdr.ptr += hlen;
/* Use an estimated value, since we also need to handle
* TSO case.
*/
diff --git a/drivers/net/vmxnet3/vmxnet3_int.h b/drivers/net/vmxnet3/vmxnet3_int.h
index 59ec34052a65..a3326463b71f 100644
--- a/drivers/net/vmxnet3/vmxnet3_int.h
+++ b/drivers/net/vmxnet3/vmxnet3_int.h
@@ -69,10 +69,10 @@
/*
* Version numbers
*/
-#define VMXNET3_DRIVER_VERSION_STRING "1.4.13.0-k"
+#define VMXNET3_DRIVER_VERSION_STRING "1.4.14.0-k"
/* a 32-bit int, each byte encode a verion number in VMXNET3_DRIVER_VERSION */
-#define VMXNET3_DRIVER_VERSION_NUM 0x01040d00
+#define VMXNET3_DRIVER_VERSION_NUM 0x01040e00
#if defined(CONFIG_PCI_MSI)
/* RSS only makes sense if MSI-X is supported. */
diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c
index 139c61c8244a..0a2b180d138a 100644
--- a/drivers/net/vrf.c
+++ b/drivers/net/vrf.c
@@ -578,12 +578,13 @@ static int vrf_finish_output(struct net *net, struct sock *sk, struct sk_buff *s
if (!IS_ERR(neigh)) {
sock_confirm_neigh(skb, neigh);
ret = neigh_output(neigh, skb);
+ rcu_read_unlock_bh();
+ return ret;
}
rcu_read_unlock_bh();
err:
- if (unlikely(ret < 0))
- vrf_tx_error(skb->dev, skb);
+ vrf_tx_error(skb->dev, skb);
return ret;
}
@@ -736,7 +737,6 @@ static int vrf_rtable_create(struct net_device *dev)
return -ENOMEM;
rth->dst.output = vrf_output;
- rth->rt_table_id = vrf->tb_id;
rcu_assign_pointer(vrf->rth, rth);
@@ -942,6 +942,7 @@ static struct rt6_info *vrf_ip6_route_lookup(struct net *net,
const struct net_device *dev,
struct flowi6 *fl6,
int ifindex,
+ const struct sk_buff *skb,
int flags)
{
struct net_vrf *vrf = netdev_priv(dev);
@@ -960,7 +961,7 @@ static struct rt6_info *vrf_ip6_route_lookup(struct net *net,
if (!table)
return NULL;
- return ip6_pol_route(net, table, ifindex, fl6, flags);
+ return ip6_pol_route(net, table, ifindex, fl6, skb, flags);
}
static void vrf_ip6_input_dst(struct sk_buff *skb, struct net_device *vrf_dev,
@@ -978,7 +979,7 @@ static void vrf_ip6_input_dst(struct sk_buff *skb, struct net_device *vrf_dev,
struct net *net = dev_net(vrf_dev);
struct rt6_info *rt6;
- rt6 = vrf_ip6_route_lookup(net, vrf_dev, &fl6, ifindex,
+ rt6 = vrf_ip6_route_lookup(net, vrf_dev, &fl6, ifindex, skb,
RT6_LOOKUP_F_HAS_SADDR | RT6_LOOKUP_F_IFACE);
if (unlikely(!rt6))
return;
@@ -1111,7 +1112,7 @@ static struct dst_entry *vrf_link_scope_lookup(const struct net_device *dev,
if (!ipv6_addr_any(&fl6->saddr))
flags |= RT6_LOOKUP_F_HAS_SADDR;
- rt = vrf_ip6_route_lookup(net, dev, fl6, fl6->flowi6_oif, flags);
+ rt = vrf_ip6_route_lookup(net, dev, fl6, fl6->flowi6_oif, NULL, flags);
if (rt)
dst = &rt->dst;
@@ -1146,6 +1147,7 @@ static inline size_t vrf_fib_rule_nl_size(void)
sz = NLMSG_ALIGN(sizeof(struct fib_rule_hdr));
sz += nla_total_size(sizeof(u8)); /* FRA_L3MDEV */
sz += nla_total_size(sizeof(u32)); /* FRA_PRIORITY */
+ sz += nla_total_size(sizeof(u8)); /* FRA_PROTOCOL */
return sz;
}
@@ -1176,6 +1178,9 @@ static int vrf_fib_rule(const struct net_device *dev, __u8 family, bool add_it)
frh->family = family;
frh->action = FR_ACT_TO_TBL;
+ if (nla_put_u8(skb, FRA_PROTOCOL, RTPROT_KERNEL))
+ goto nla_put_failure;
+
if (nla_put_u8(skb, FRA_L3MDEV, 1))
goto nla_put_failure;
diff --git a/drivers/net/wimax/i2400m/usb-rx.c b/drivers/net/wimax/i2400m/usb-rx.c
index b78ee676e102..5b64bda7d9e7 100644
--- a/drivers/net/wimax/i2400m/usb-rx.c
+++ b/drivers/net/wimax/i2400m/usb-rx.c
@@ -263,9 +263,6 @@ retry:
new_skb = skb_copy_expand(rx_skb, 0, rx_size - rx_skb->len,
GFP_KERNEL);
if (new_skb == NULL) {
- if (printk_ratelimit())
- dev_err(dev, "RX: Can't reallocate skb to %d; "
- "RX dropped\n", rx_size);
kfree_skb(rx_skb);
rx_skb = NULL;
goto out; /* drop it...*/
diff --git a/drivers/net/wireless/admtek/Kconfig b/drivers/net/wireless/admtek/Kconfig
index d5a2dc728078..9317367e37f0 100644
--- a/drivers/net/wireless/admtek/Kconfig
+++ b/drivers/net/wireless/admtek/Kconfig
@@ -5,8 +5,8 @@ config WLAN_VENDOR_ADMTEK
If you have a wireless card belonging to this class, say Y.
Note that the answer to this question doesn't directly affect the
- kernel: saying N will just cause the configurator to skip all
- the questions about cards. If you say Y, you will be asked for
+ kernel: saying N will just cause the configurator to skip all the
+ questions about these cards. If you say Y, you will be asked for
your specific card in the following questions.
if WLAN_VENDOR_ADMTEK
diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig
index 44b2470af81d..82ab7c33cf97 100644
--- a/drivers/net/wireless/ath/Kconfig
+++ b/drivers/net/wireless/ath/Kconfig
@@ -8,8 +8,8 @@ config WLAN_VENDOR_ATH
If you have a wireless card belonging to this class, say Y.
Note that the answer to this question doesn't directly affect the
- kernel: saying N will just cause the configurator to skip all
- the questions about cards. If you say Y, you will be asked for
+ kernel: saying N will just cause the configurator to skip all the
+ questions about these cards. If you say Y, you will be asked for
your specific card in the following questions.
For more information and documentation on this module you can visit:
diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h
index f3f2784f6ebd..7a364eca46d6 100644
--- a/drivers/net/wireless/ath/ath.h
+++ b/drivers/net/wireless/ath/ath.h
@@ -33,8 +33,6 @@
*/
#define ATH_KEYMAX 128 /* max key cache size we handle */
-static const u8 ath_bcast_mac[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
-
struct ath_ani {
bool caldone;
unsigned int longcal_timer;
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index f3ec13b80b20..8a3020dbd4cf 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -1,6 +1,7 @@
/*
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -2040,7 +2041,8 @@ static int ath10k_core_init_firmware_features(struct ath10k *ar)
ar->max_num_vdevs = TARGET_10_4_NUM_VDEVS;
ar->num_tids = TARGET_10_4_TGT_NUM_TIDS;
ar->fw_stats_req_mask = WMI_10_4_STAT_PEER |
- WMI_10_4_STAT_PEER_EXTD;
+ WMI_10_4_STAT_PEER_EXTD |
+ WMI_10_4_STAT_VDEV_EXTD;
ar->max_spatial_stream = ar->hw_params.max_spatial_stream;
ar->max_num_tdls_vdevs = TARGET_10_4_NUM_TDLS_VDEVS;
@@ -2281,6 +2283,9 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode,
if (ath10k_peer_stats_enabled(ar))
val = WMI_10_4_PEER_STATS;
+ /* Enable vdev stats by default */
+ val |= WMI_10_4_VDEV_STATS;
+
if (test_bit(WMI_SERVICE_BSS_CHANNEL_INFO_64, ar->wmi.svc_map))
val |= WMI_10_4_BSS_CHANNEL_INFO_64;
@@ -2439,7 +2444,7 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
ret = ath10k_hif_power_up(ar);
if (ret) {
- ath10k_err(ar, "could not start pci hif (%d)\n", ret);
+ ath10k_err(ar, "could not power on hif bus (%d)\n", ret);
return ret;
}
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index fe6b30356d3b..c17d805d68cc 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -1,6 +1,7 @@
/*
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -221,6 +222,27 @@ struct ath10k_fw_stats_vdev {
u32 beacon_rssi_history[10];
};
+struct ath10k_fw_stats_vdev_extd {
+ struct list_head list;
+
+ u32 vdev_id;
+ u32 ppdu_aggr_cnt;
+ u32 ppdu_noack;
+ u32 mpdu_queued;
+ u32 ppdu_nonaggr_cnt;
+ u32 mpdu_sw_requeued;
+ u32 mpdu_suc_retry;
+ u32 mpdu_suc_multitry;
+ u32 mpdu_fail_retry;
+ u32 tx_ftm_suc;
+ u32 tx_ftm_suc_retry;
+ u32 tx_ftm_fail;
+ u32 rx_ftmr_cnt;
+ u32 rx_ftmr_dup_cnt;
+ u32 rx_iftmr_cnt;
+ u32 rx_iftmr_dup_cnt;
+};
+
struct ath10k_fw_stats_pdev {
struct list_head list;
@@ -324,6 +346,27 @@ struct ath10k_tpc_stats {
struct ath10k_tpc_table tpc_table[WMI_TPC_FLAG];
};
+struct ath10k_tpc_table_final {
+ u32 pream_idx[WMI_TPC_FINAL_RATE_MAX];
+ u8 rate_code[WMI_TPC_FINAL_RATE_MAX];
+ char tpc_value[WMI_TPC_FINAL_RATE_MAX][WMI_TPC_TX_N_CHAIN * WMI_TPC_BUF_SIZE];
+};
+
+struct ath10k_tpc_stats_final {
+ u32 reg_domain;
+ u32 chan_freq;
+ u32 phy_mode;
+ u32 twice_antenna_reduction;
+ u32 twice_max_rd_power;
+ s32 twice_antenna_gain;
+ u32 power_limit;
+ u32 num_tx_chain;
+ u32 ctl;
+ u32 rate_max;
+ u8 flag[WMI_TPC_FLAG];
+ struct ath10k_tpc_table_final tpc_table_final[WMI_TPC_FLAG];
+};
+
struct ath10k_dfs_stats {
u32 phy_errors;
u32 pulses_total;
@@ -354,6 +397,45 @@ struct ath10k_txq {
unsigned long num_push_allowed;
};
+enum ath10k_pkt_rx_err {
+ ATH10K_PKT_RX_ERR_FCS,
+ ATH10K_PKT_RX_ERR_TKIP,
+ ATH10K_PKT_RX_ERR_CRYPT,
+ ATH10K_PKT_RX_ERR_PEER_IDX_INVAL,
+ ATH10K_PKT_RX_ERR_MAX,
+};
+
+enum ath10k_ampdu_subfrm_num {
+ ATH10K_AMPDU_SUBFRM_NUM_10,
+ ATH10K_AMPDU_SUBFRM_NUM_20,
+ ATH10K_AMPDU_SUBFRM_NUM_30,
+ ATH10K_AMPDU_SUBFRM_NUM_40,
+ ATH10K_AMPDU_SUBFRM_NUM_50,
+ ATH10K_AMPDU_SUBFRM_NUM_60,
+ ATH10K_AMPDU_SUBFRM_NUM_MORE,
+ ATH10K_AMPDU_SUBFRM_NUM_MAX,
+};
+
+enum ath10k_amsdu_subfrm_num {
+ ATH10K_AMSDU_SUBFRM_NUM_1,
+ ATH10K_AMSDU_SUBFRM_NUM_2,
+ ATH10K_AMSDU_SUBFRM_NUM_3,
+ ATH10K_AMSDU_SUBFRM_NUM_4,
+ ATH10K_AMSDU_SUBFRM_NUM_MORE,
+ ATH10K_AMSDU_SUBFRM_NUM_MAX,
+};
+
+struct ath10k_sta_tid_stats {
+ unsigned long int rx_pkt_from_fw;
+ unsigned long int rx_pkt_unchained;
+ unsigned long int rx_pkt_drop_chained;
+ unsigned long int rx_pkt_drop_filter;
+ unsigned long int rx_pkt_err[ATH10K_PKT_RX_ERR_MAX];
+ unsigned long int rx_pkt_queued_for_mac;
+ unsigned long int rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_MAX];
+ unsigned long int rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_MAX];
+};
+
struct ath10k_sta {
struct ath10k_vif *arvif;
@@ -371,6 +453,9 @@ struct ath10k_sta {
#ifdef CONFIG_MAC80211_DEBUGFS
/* protected by conf_mutex */
bool aggr_mode;
+
+ /* Protected with ar->data_lock */
+ struct ath10k_sta_tid_stats tid_stats[IEEE80211_NUM_TIDS + 1];
#endif
};
@@ -487,6 +572,7 @@ struct ath10k_debug {
/* used for tpc-dump storage, protected by data-lock */
struct ath10k_tpc_stats *tpc_stats;
+ struct ath10k_tpc_stats_final *tpc_stats_final;
struct completion tpc_complete;
@@ -1019,6 +1105,8 @@ struct ath10k {
void *ce_priv;
+ u32 sta_tid_stats_mask;
+
/* must be last */
u8 drv_priv[0] __aligned(sizeof(void *));
};
diff --git a/drivers/net/wireless/ath/ath10k/coredump.c b/drivers/net/wireless/ath/ath10k/coredump.c
index 7173b3743b43..f90cec0ebb1c 100644
--- a/drivers/net/wireless/ath/ath10k/coredump.c
+++ b/drivers/net/wireless/ath/ath10k/coredump.c
@@ -701,6 +701,89 @@ static const struct ath10k_mem_region qca988x_hw20_mem_regions[] = {
},
};
+static const struct ath10k_mem_region qca9984_hw10_mem_regions[] = {
+ {
+ .type = ATH10K_MEM_REGION_TYPE_DRAM,
+ .start = 0x400000,
+ .len = 0x80000,
+ .name = "DRAM",
+ .section_table = {
+ .sections = NULL,
+ .size = 0,
+ },
+ },
+ {
+ .type = ATH10K_MEM_REGION_TYPE_REG,
+ .start = 0x98000,
+ .len = 0x50000,
+ .name = "IRAM",
+ .section_table = {
+ .sections = NULL,
+ .size = 0,
+ },
+ },
+ {
+ .type = ATH10K_MEM_REGION_TYPE_IOSRAM,
+ .start = 0xC0000,
+ .len = 0x40000,
+ .name = "SRAM",
+ .section_table = {
+ .sections = NULL,
+ .size = 0,
+ },
+ },
+ {
+ .type = ATH10K_MEM_REGION_TYPE_IOREG,
+ .start = 0x30000,
+ .len = 0x7000,
+ .name = "APB REG 1",
+ .section_table = {
+ .sections = NULL,
+ .size = 0,
+ },
+ },
+ {
+ .type = ATH10K_MEM_REGION_TYPE_IOREG,
+ .start = 0x3f000,
+ .len = 0x3000,
+ .name = "APB REG 2",
+ .section_table = {
+ .sections = NULL,
+ .size = 0,
+ },
+ },
+ {
+ .type = ATH10K_MEM_REGION_TYPE_IOREG,
+ .start = 0x43000,
+ .len = 0x3000,
+ .name = "WIFI REG",
+ .section_table = {
+ .sections = NULL,
+ .size = 0,
+ },
+ },
+ {
+ .type = ATH10K_MEM_REGION_TYPE_IOREG,
+ .start = 0x4A000,
+ .len = 0x5000,
+ .name = "CE REG",
+ .section_table = {
+ .sections = NULL,
+ .size = 0,
+ },
+ },
+ {
+ .type = ATH10K_MEM_REGION_TYPE_IOREG,
+ .start = 0x80000,
+ .len = 0x6000,
+ .name = "SOC REG",
+ .section_table = {
+ .sections = NULL,
+ .size = 0,
+ },
+ },
+};
+
static const struct ath10k_hw_mem_layout hw_mem_layouts[] = {
{
.hw_id = QCA6174_HW_1_0_VERSION,
@@ -758,6 +841,13 @@ static const struct ath10k_hw_mem_layout hw_mem_layouts[] = {
.size = ARRAY_SIZE(qca988x_hw20_mem_regions),
},
},
+ {
+ .hw_id = QCA9984_HW_1_0_DEV_VERSION,
+ .region_table = {
+ .regions = qca9984_hw10_mem_regions,
+ .size = ARRAY_SIZE(qca9984_hw10_mem_regions),
+ },
+ },
};
static u32 ath10k_coredump_get_ramdump_size(struct ath10k *ar)
diff --git a/drivers/net/wireless/ath/ath10k/coredump.h b/drivers/net/wireless/ath/ath10k/coredump.h
index bfee13038e59..3baaf9d2cbcd 100644
--- a/drivers/net/wireless/ath/ath10k/coredump.h
+++ b/drivers/net/wireless/ath/ath10k/coredump.h
@@ -124,6 +124,8 @@ enum ath10k_mem_region_type {
ATH10K_MEM_REGION_TYPE_AXI = 3,
ATH10K_MEM_REGION_TYPE_IRAM1 = 4,
ATH10K_MEM_REGION_TYPE_IRAM2 = 5,
+ ATH10K_MEM_REGION_TYPE_IOSRAM = 6,
+ ATH10K_MEM_REGION_TYPE_IOREG = 7,
};
/* Define a section of the region which should be copied. As not all parts
diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index 554cd7856cb6..bac832ce1873 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -1480,6 +1480,19 @@ void ath10k_debug_tpc_stats_process(struct ath10k *ar,
spin_unlock_bh(&ar->data_lock);
}
+void
+ath10k_debug_tpc_stats_final_process(struct ath10k *ar,
+ struct ath10k_tpc_stats_final *tpc_stats)
+{
+ spin_lock_bh(&ar->data_lock);
+
+ kfree(ar->debug.tpc_stats_final);
+ ar->debug.tpc_stats_final = tpc_stats;
+ complete(&ar->debug.tpc_complete);
+
+ spin_unlock_bh(&ar->data_lock);
+}
+
static void ath10k_tpc_stats_print(struct ath10k_tpc_stats *tpc_stats,
unsigned int j, char *buf, size_t *len)
{
@@ -2143,6 +2156,137 @@ static const struct file_operations fops_fw_checksums = {
.llseek = default_llseek,
};
+static ssize_t ath10k_sta_tid_stats_mask_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ char buf[32];
+ size_t len;
+
+ len = scnprintf(buf, sizeof(buf), "0x%08x\n", ar->sta_tid_stats_mask);
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t ath10k_sta_tid_stats_mask_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ char buf[32];
+ ssize_t len;
+ u32 mask;
+
+ len = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+ if (kstrtoint(buf, 0, &mask))
+ return -EINVAL;
+
+ ar->sta_tid_stats_mask = mask;
+
+ return len;
+}
+
+static const struct file_operations fops_sta_tid_stats_mask = {
+ .read = ath10k_sta_tid_stats_mask_read,
+ .write = ath10k_sta_tid_stats_mask_write,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static int ath10k_debug_tpc_stats_final_request(struct ath10k *ar)
+{
+ int ret;
+ unsigned long time_left;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+ reinit_completion(&ar->debug.tpc_complete);
+
+ ret = ath10k_wmi_pdev_get_tpc_table_cmdid(ar, WMI_TPC_CONFIG_PARAM);
+ if (ret) {
+ ath10k_warn(ar, "failed to request tpc table cmdid: %d\n", ret);
+ return ret;
+ }
+
+ time_left = wait_for_completion_timeout(&ar->debug.tpc_complete,
+ 1 * HZ);
+ if (time_left == 0)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int ath10k_tpc_stats_final_open(struct inode *inode, struct file *file)
+{
+ struct ath10k *ar = inode->i_private;
+ void *buf;
+ int ret;
+
+ mutex_lock(&ar->conf_mutex);
+
+ if (ar->state != ATH10K_STATE_ON) {
+ ret = -ENETDOWN;
+ goto err_unlock;
+ }
+
+ buf = vmalloc(ATH10K_TPC_CONFIG_BUF_SIZE);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto err_unlock;
+ }
+
+ ret = ath10k_debug_tpc_stats_final_request(ar);
+ if (ret) {
+ ath10k_warn(ar, "failed to request tpc stats final: %d\n",
+ ret);
+ goto err_free;
+ }
+
+ ath10k_tpc_stats_fill(ar, ar->debug.tpc_stats, buf);
+ file->private_data = buf;
+
+ mutex_unlock(&ar->conf_mutex);
+ return 0;
+
+err_free:
+ vfree(buf);
+
+err_unlock:
+ mutex_unlock(&ar->conf_mutex);
+ return ret;
+}
+
+static int ath10k_tpc_stats_final_release(struct inode *inode,
+ struct file *file)
+{
+ vfree(file->private_data);
+
+ return 0;
+}
+
+static ssize_t ath10k_tpc_stats_final_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ const char *buf = file->private_data;
+ unsigned int len = strlen(buf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_tpc_stats_final = {
+ .open = ath10k_tpc_stats_final_open,
+ .release = ath10k_tpc_stats_final_release,
+ .read = ath10k_tpc_stats_final_read,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
int ath10k_debug_create(struct ath10k *ar)
{
ar->debug.cal_data = vzalloc(ATH10K_DEBUG_CAL_DATA_LEN);
@@ -2258,6 +2402,16 @@ int ath10k_debug_register(struct ath10k *ar)
debugfs_create_file("fw_checksums", 0400, ar->debug.debugfs_phy, ar,
&fops_fw_checksums);
+ if (IS_ENABLED(CONFIG_MAC80211_DEBUGFS))
+ debugfs_create_file("sta_tid_stats_mask", 0600,
+ ar->debug.debugfs_phy,
+ ar, &fops_sta_tid_stats_mask);
+
+ if (test_bit(WMI_SERVICE_TPC_STATS_FINAL, ar->wmi.svc_map))
+ debugfs_create_file("tpc_stats_final", 0400,
+ ar->debug.debugfs_phy, ar,
+ &fops_tpc_stats_final);
+
return 0;
}
diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h
index e54308889e59..0afca5c106b6 100644
--- a/drivers/net/wireless/ath/ath10k/debug.h
+++ b/drivers/net/wireless/ath/ath10k/debug.h
@@ -1,6 +1,7 @@
/*
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -101,6 +102,9 @@ void ath10k_debug_unregister(struct ath10k *ar);
void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb);
void ath10k_debug_tpc_stats_process(struct ath10k *ar,
struct ath10k_tpc_stats *tpc_stats);
+void
+ath10k_debug_tpc_stats_final_process(struct ath10k *ar,
+ struct ath10k_tpc_stats_final *tpc_stats);
void ath10k_debug_dbglog_add(struct ath10k *ar, u8 *buffer, int len);
#define ATH10K_DFS_STAT_INC(ar, c) (ar->debug.dfs_stats.c++)
@@ -164,6 +168,13 @@ static inline void ath10k_debug_tpc_stats_process(struct ath10k *ar,
kfree(tpc_stats);
}
+static inline void
+ath10k_debug_tpc_stats_final_process(struct ath10k *ar,
+ struct ath10k_tpc_stats_final *tpc_stats)
+{
+ kfree(tpc_stats);
+}
+
static inline void ath10k_debug_dbglog_add(struct ath10k *ar, u8 *buffer,
int len)
{
@@ -191,12 +202,42 @@ void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, struct dentry *dir);
void ath10k_sta_update_rx_duration(struct ath10k *ar,
struct ath10k_fw_stats *stats);
+void ath10k_sta_update_rx_tid_stats(struct ath10k *ar, u8 *first_hdr,
+ unsigned long int num_msdus,
+ enum ath10k_pkt_rx_err err,
+ unsigned long int unchain_cnt,
+ unsigned long int drop_cnt,
+ unsigned long int drop_cnt_filter,
+ unsigned long int queued_msdus);
+void ath10k_sta_update_rx_tid_stats_ampdu(struct ath10k *ar,
+ u16 peer_id, u8 tid,
+ struct htt_rx_indication_mpdu_range *ranges,
+ int num_ranges);
#else
static inline
void ath10k_sta_update_rx_duration(struct ath10k *ar,
struct ath10k_fw_stats *stats)
{
}
+
+static inline
+void ath10k_sta_update_rx_tid_stats(struct ath10k *ar, u8 *first_hdr,
+ unsigned long int num_msdus,
+ enum ath10k_pkt_rx_err err,
+ unsigned long int unchain_cnt,
+ unsigned long int drop_cnt,
+ unsigned long int drop_cnt_filter,
+ unsigned long int queued_msdus)
+{
+}
+
+static inline
+void ath10k_sta_update_rx_tid_stats_ampdu(struct ath10k *ar,
+ u16 peer_id, u8 tid,
+ struct htt_rx_indication_mpdu_range *ranges,
+ int num_ranges)
+{
+}
#endif /* CONFIG_MAC80211_DEBUGFS */
#ifdef CONFIG_ATH10K_DEBUG
diff --git a/drivers/net/wireless/ath/ath10k/debugfs_sta.c b/drivers/net/wireless/ath/ath10k/debugfs_sta.c
index b260b09dd4d3..8f688f136c22 100644
--- a/drivers/net/wireless/ath/ath10k/debugfs_sta.c
+++ b/drivers/net/wireless/ath/ath10k/debugfs_sta.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2014-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -16,8 +17,125 @@
#include "core.h"
#include "wmi-ops.h"
+#include "txrx.h"
#include "debug.h"
+static void ath10k_rx_stats_update_amsdu_subfrm(struct ath10k *ar,
+ struct ath10k_sta_tid_stats *stats,
+ u32 msdu_count)
+{
+ if (msdu_count == 1)
+ stats->rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_1]++;
+ else if (msdu_count == 2)
+ stats->rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_2]++;
+ else if (msdu_count == 3)
+ stats->rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_3]++;
+ else if (msdu_count == 4)
+ stats->rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_4]++;
+ else if (msdu_count > 4)
+ stats->rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_MORE]++;
+}
+
+static void ath10k_rx_stats_update_ampdu_subfrm(struct ath10k *ar,
+ struct ath10k_sta_tid_stats *stats,
+ u32 mpdu_count)
+{
+ if (mpdu_count <= 10)
+ stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_10]++;
+ else if (mpdu_count <= 20)
+ stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_20]++;
+ else if (mpdu_count <= 30)
+ stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_30]++;
+ else if (mpdu_count <= 40)
+ stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_40]++;
+ else if (mpdu_count <= 50)
+ stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_50]++;
+ else if (mpdu_count <= 60)
+ stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_60]++;
+ else if (mpdu_count > 60)
+ stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_MORE]++;
+}
+
+void ath10k_sta_update_rx_tid_stats_ampdu(struct ath10k *ar, u16 peer_id, u8 tid,
+ struct htt_rx_indication_mpdu_range *ranges,
+ int num_ranges)
+{
+ struct ath10k_sta *arsta;
+ struct ath10k_peer *peer;
+ int i;
+
+ if (tid > IEEE80211_NUM_TIDS || !(ar->sta_tid_stats_mask & BIT(tid)))
+ return;
+
+ rcu_read_lock();
+ spin_lock_bh(&ar->data_lock);
+
+ peer = ath10k_peer_find_by_id(ar, peer_id);
+ if (!peer)
+ goto out;
+
+ arsta = (struct ath10k_sta *)peer->sta->drv_priv;
+
+ for (i = 0; i < num_ranges; i++)
+ ath10k_rx_stats_update_ampdu_subfrm(ar,
+ &arsta->tid_stats[tid],
+ ranges[i].mpdu_count);
+
+out:
+ spin_unlock_bh(&ar->data_lock);
+ rcu_read_unlock();
+}
+
+void ath10k_sta_update_rx_tid_stats(struct ath10k *ar, u8 *first_hdr,
+ unsigned long int num_msdus,
+ enum ath10k_pkt_rx_err err,
+ unsigned long int unchain_cnt,
+ unsigned long int drop_cnt,
+ unsigned long int drop_cnt_filter,
+ unsigned long int queued_msdus)
+{
+ struct ieee80211_sta *sta;
+ struct ath10k_sta *arsta;
+ struct ieee80211_hdr *hdr;
+ struct ath10k_sta_tid_stats *stats;
+ u8 tid = IEEE80211_NUM_TIDS;
+ bool non_data_frm = false;
+
+ hdr = (struct ieee80211_hdr *)first_hdr;
+ if (!ieee80211_is_data(hdr->frame_control))
+ non_data_frm = true;
+
+ if (ieee80211_is_data_qos(hdr->frame_control))
+ tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
+
+ if (!(ar->sta_tid_stats_mask & BIT(tid)) || non_data_frm)
+ return;
+
+ rcu_read_lock();
+
+ sta = ieee80211_find_sta_by_ifaddr(ar->hw, hdr->addr2, NULL);
+ if (!sta)
+ goto exit;
+
+ arsta = (struct ath10k_sta *)sta->drv_priv;
+
+ spin_lock_bh(&ar->data_lock);
+ stats = &arsta->tid_stats[tid];
+ stats->rx_pkt_from_fw += num_msdus;
+ stats->rx_pkt_unchained += unchain_cnt;
+ stats->rx_pkt_drop_chained += drop_cnt;
+ stats->rx_pkt_drop_filter += drop_cnt_filter;
+ if (err != ATH10K_PKT_RX_ERR_MAX)
+ stats->rx_pkt_err[err] += queued_msdus;
+ stats->rx_pkt_queued_for_mac += queued_msdus;
+ ath10k_rx_stats_update_amsdu_subfrm(ar, &arsta->tid_stats[tid],
+ num_msdus);
+ spin_unlock_bh(&ar->data_lock);
+
+exit:
+ rcu_read_unlock();
+}
+
static void ath10k_sta_update_extd_stats_rx_duration(struct ath10k *ar,
struct ath10k_fw_stats *stats)
{
@@ -342,6 +460,172 @@ static const struct file_operations fops_peer_debug_trigger = {
.llseek = default_llseek,
};
+static char *get_err_str(enum ath10k_pkt_rx_err i)
+{
+ switch (i) {
+ case ATH10K_PKT_RX_ERR_FCS:
+ return "fcs_err";
+ case ATH10K_PKT_RX_ERR_TKIP:
+ return "tkip_err";
+ case ATH10K_PKT_RX_ERR_CRYPT:
+ return "crypt_err";
+ case ATH10K_PKT_RX_ERR_PEER_IDX_INVAL:
+ return "peer_idx_inval";
+ case ATH10K_PKT_RX_ERR_MAX:
+ return "unknown";
+ }
+
+ return "unknown";
+}
+
+static char *get_num_ampdu_subfrm_str(enum ath10k_ampdu_subfrm_num i)
+{
+ switch (i) {
+ case ATH10K_AMPDU_SUBFRM_NUM_10:
+ return "upto 10";
+ case ATH10K_AMPDU_SUBFRM_NUM_20:
+ return "11-20";
+ case ATH10K_AMPDU_SUBFRM_NUM_30:
+ return "21-30";
+ case ATH10K_AMPDU_SUBFRM_NUM_40:
+ return "31-40";
+ case ATH10K_AMPDU_SUBFRM_NUM_50:
+ return "41-50";
+ case ATH10K_AMPDU_SUBFRM_NUM_60:
+ return "51-60";
+ case ATH10K_AMPDU_SUBFRM_NUM_MORE:
+ return ">60";
+ case ATH10K_AMPDU_SUBFRM_NUM_MAX:
+ return "0";
+ }
+
+ return "0";
+}
+
+static char *get_num_amsdu_subfrm_str(enum ath10k_amsdu_subfrm_num i)
+{
+ switch (i) {
+ case ATH10K_AMSDU_SUBFRM_NUM_1:
+ return "1";
+ case ATH10K_AMSDU_SUBFRM_NUM_2:
+ return "2";
+ case ATH10K_AMSDU_SUBFRM_NUM_3:
+ return "3";
+ case ATH10K_AMSDU_SUBFRM_NUM_4:
+ return "4";
+ case ATH10K_AMSDU_SUBFRM_NUM_MORE:
+ return ">4";
+ case ATH10K_AMSDU_SUBFRM_NUM_MAX:
+ return "0";
+ }
+
+ return "0";
+}
+
+#define PRINT_TID_STATS(_field, _tabs) \
+ do { \
+ int k = 0; \
+ for (j = 0; j <= IEEE80211_NUM_TIDS; j++) { \
+ if (ar->sta_tid_stats_mask & BIT(j)) { \
+ len += scnprintf(buf + len, buf_len - len, \
+ "[%02d] %-10lu ", \
+ j, stats[j]._field); \
+ k++; \
+ if (k % 8 == 0) { \
+ len += scnprintf(buf + len, \
+ buf_len - len, "\n"); \
+ len += scnprintf(buf + len, \
+ buf_len - len, \
+ _tabs); \
+ } \
+ } \
+ } \
+ len += scnprintf(buf + len, buf_len - len, "\n"); \
+ } while (0)
+
+static ssize_t ath10k_dbg_sta_read_tid_stats(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ieee80211_sta *sta = file->private_data;
+ struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
+ struct ath10k *ar = arsta->arvif->ar;
+ struct ath10k_sta_tid_stats *stats = arsta->tid_stats;
+ size_t len = 0, buf_len = 1048 * IEEE80211_NUM_TIDS;
+ char *buf;
+ int i, j;
+ ssize_t ret;
+
+ buf = kzalloc(buf_len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ mutex_lock(&ar->conf_mutex);
+
+ spin_lock_bh(&ar->data_lock);
+
+ len += scnprintf(buf + len, buf_len - len,
+ "\n\t\tDriver Rx pkt stats per tid, ([tid] count)\n");
+ len += scnprintf(buf + len, buf_len - len,
+ "\t\t------------------------------------------\n");
+ len += scnprintf(buf + len, buf_len - len, "MSDUs from FW\t\t\t");
+ PRINT_TID_STATS(rx_pkt_from_fw, "\t\t\t\t");
+
+ len += scnprintf(buf + len, buf_len - len, "MSDUs unchained\t\t\t");
+ PRINT_TID_STATS(rx_pkt_unchained, "\t\t\t\t");
+
+ len += scnprintf(buf + len, buf_len - len,
+ "MSDUs locally dropped:chained\t");
+ PRINT_TID_STATS(rx_pkt_drop_chained, "\t\t\t\t");
+
+ len += scnprintf(buf + len, buf_len - len,
+ "MSDUs locally dropped:filtered\t");
+ PRINT_TID_STATS(rx_pkt_drop_filter, "\t\t\t\t");
+
+ len += scnprintf(buf + len, buf_len - len,
+ "MSDUs queued for mac80211\t");
+ PRINT_TID_STATS(rx_pkt_queued_for_mac, "\t\t\t\t");
+
+ for (i = 0; i < ATH10K_PKT_RX_ERR_MAX; i++) {
+ len += scnprintf(buf + len, buf_len - len,
+ "MSDUs with error:%s\t", get_err_str(i));
+ PRINT_TID_STATS(rx_pkt_err[i], "\t\t\t\t");
+ }
+
+ len += scnprintf(buf + len, buf_len - len, "\n");
+ for (i = 0; i < ATH10K_AMPDU_SUBFRM_NUM_MAX; i++) {
+ len += scnprintf(buf + len, buf_len - len,
+ "A-MPDU num subframes %s\t",
+ get_num_ampdu_subfrm_str(i));
+ PRINT_TID_STATS(rx_pkt_ampdu[i], "\t\t\t\t");
+ }
+
+ len += scnprintf(buf + len, buf_len - len, "\n");
+ for (i = 0; i < ATH10K_AMSDU_SUBFRM_NUM_MAX; i++) {
+ len += scnprintf(buf + len, buf_len - len,
+ "A-MSDU num subframes %s\t\t",
+ get_num_amsdu_subfrm_str(i));
+ PRINT_TID_STATS(rx_pkt_amsdu[i], "\t\t\t\t");
+ }
+
+ spin_unlock_bh(&ar->data_lock);
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+ kfree(buf);
+
+ mutex_unlock(&ar->conf_mutex);
+
+ return ret;
+}
+
+static const struct file_operations fops_tid_stats_dump = {
+ .open = simple_open,
+ .read = ath10k_dbg_sta_read_tid_stats,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, struct dentry *dir)
{
@@ -351,4 +635,6 @@ void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
debugfs_create_file("delba", 0200, dir, sta, &fops_delba);
debugfs_create_file("peer_debug_trigger", 0600, dir, sta,
&fops_peer_debug_trigger);
+ debugfs_create_file("dump_tid_stats", 0400, dir, sta,
+ &fops_tid_stats_dump);
}
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index 6d96f9560950..5e02e26158f6 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -1,6 +1,7 @@
/*
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -723,6 +724,28 @@ struct amsdu_subframe_hdr {
#define GROUP_ID_IS_SU_MIMO(x) ((x) == 0 || (x) == 63)
+static inline u8 ath10k_bw_to_mac80211_bw(u8 bw)
+{
+ u8 ret = 0;
+
+ switch (bw) {
+ case 0:
+ ret = RATE_INFO_BW_20;
+ break;
+ case 1:
+ ret = RATE_INFO_BW_40;
+ break;
+ case 2:
+ ret = RATE_INFO_BW_80;
+ break;
+ case 3:
+ ret = RATE_INFO_BW_160;
+ break;
+ }
+
+ return ret;
+}
+
static void ath10k_htt_rx_h_rates(struct ath10k *ar,
struct ieee80211_rx_status *status,
struct htt_rx_desc *rxd)
@@ -825,23 +848,7 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar,
if (sgi)
status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
- switch (bw) {
- /* 20MHZ */
- case 0:
- break;
- /* 40MHZ */
- case 1:
- status->bw = RATE_INFO_BW_40;
- break;
- /* 80MHZ */
- case 2:
- status->bw = RATE_INFO_BW_80;
- break;
- case 3:
- status->bw = RATE_INFO_BW_160;
- break;
- }
-
+ status->bw = ath10k_bw_to_mac80211_bw(bw);
status->encoding = RX_ENC_VHT;
break;
default:
@@ -1502,7 +1509,9 @@ static void ath10k_htt_rx_h_csum_offload(struct sk_buff *msdu)
static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
struct sk_buff_head *amsdu,
struct ieee80211_rx_status *status,
- bool fill_crypt_header)
+ bool fill_crypt_header,
+ u8 *rx_hdr,
+ enum ath10k_pkt_rx_err *err)
{
struct sk_buff *first;
struct sk_buff *last;
@@ -1538,6 +1547,9 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
hdr = (void *)rxd->rx_hdr_status;
memcpy(first_hdr, hdr, RX_HTT_HDR_STATUS_LEN);
+ if (rx_hdr)
+ memcpy(rx_hdr, hdr, RX_HTT_HDR_STATUS_LEN);
+
/* Each A-MSDU subframe will use the original header as the base and be
* reported as a separate MSDU so strip the A-MSDU bit from QoS Ctl.
*/
@@ -1581,6 +1593,17 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
if (has_tkip_err)
status->flag |= RX_FLAG_MMIC_ERROR;
+ if (err) {
+ if (has_fcs_err)
+ *err = ATH10K_PKT_RX_ERR_FCS;
+ else if (has_tkip_err)
+ *err = ATH10K_PKT_RX_ERR_TKIP;
+ else if (has_crypto_err)
+ *err = ATH10K_PKT_RX_ERR_CRYPT;
+ else if (has_peer_idx_invalid)
+ *err = ATH10K_PKT_RX_ERR_PEER_IDX_INVAL;
+ }
+
/* Firmware reports all necessary management frames via WMI already.
* They are not reported to monitor interfaces at all so pass the ones
* coming via HTT to monitor interfaces instead. This simplifies
@@ -1651,11 +1674,13 @@ static void ath10k_htt_rx_h_enqueue(struct ath10k *ar,
}
}
-static int ath10k_unchain_msdu(struct sk_buff_head *amsdu)
+static int ath10k_unchain_msdu(struct sk_buff_head *amsdu,
+ unsigned long int *unchain_cnt)
{
struct sk_buff *skb, *first;
int space;
int total_len = 0;
+ int amsdu_len = skb_queue_len(amsdu);
/* TODO: Might could optimize this by using
* skb_try_coalesce or similar method to
@@ -1691,11 +1716,16 @@ static int ath10k_unchain_msdu(struct sk_buff_head *amsdu)
}
__skb_queue_head(amsdu, first);
+
+ *unchain_cnt += amsdu_len - 1;
+
return 0;
}
static void ath10k_htt_rx_h_unchain(struct ath10k *ar,
- struct sk_buff_head *amsdu)
+ struct sk_buff_head *amsdu,
+ unsigned long int *drop_cnt,
+ unsigned long int *unchain_cnt)
{
struct sk_buff *first;
struct htt_rx_desc *rxd;
@@ -1713,11 +1743,12 @@ static void ath10k_htt_rx_h_unchain(struct ath10k *ar,
*/
if (decap != RX_MSDU_DECAP_RAW ||
skb_queue_len(amsdu) != 1 + rxd->frag_info.ring2_more_count) {
+ *drop_cnt += skb_queue_len(amsdu);
__skb_queue_purge(amsdu);
return;
}
- ath10k_unchain_msdu(amsdu);
+ ath10k_unchain_msdu(amsdu, unchain_cnt);
}
static bool ath10k_htt_rx_amsdu_allowed(struct ath10k *ar,
@@ -1743,7 +1774,8 @@ static bool ath10k_htt_rx_amsdu_allowed(struct ath10k *ar,
static void ath10k_htt_rx_h_filter(struct ath10k *ar,
struct sk_buff_head *amsdu,
- struct ieee80211_rx_status *rx_status)
+ struct ieee80211_rx_status *rx_status,
+ unsigned long int *drop_cnt)
{
if (skb_queue_empty(amsdu))
return;
@@ -1751,6 +1783,9 @@ static void ath10k_htt_rx_h_filter(struct ath10k *ar,
if (ath10k_htt_rx_amsdu_allowed(ar, amsdu, rx_status))
return;
+ if (drop_cnt)
+ *drop_cnt += skb_queue_len(amsdu);
+
__skb_queue_purge(amsdu);
}
@@ -1760,6 +1795,12 @@ static int ath10k_htt_rx_handle_amsdu(struct ath10k_htt *htt)
struct ieee80211_rx_status *rx_status = &htt->rx_status;
struct sk_buff_head amsdu;
int ret;
+ unsigned long int drop_cnt = 0;
+ unsigned long int unchain_cnt = 0;
+ unsigned long int drop_cnt_filter = 0;
+ unsigned long int msdus_to_queue, num_msdus;
+ enum ath10k_pkt_rx_err err = ATH10K_PKT_RX_ERR_MAX;
+ u8 first_hdr[RX_HTT_HDR_STATUS_LEN];
__skb_queue_head_init(&amsdu);
@@ -1781,16 +1822,23 @@ static int ath10k_htt_rx_handle_amsdu(struct ath10k_htt *htt)
return ret;
}
+ num_msdus = skb_queue_len(&amsdu);
+
ath10k_htt_rx_h_ppdu(ar, &amsdu, rx_status, 0xffff);
/* only for ret = 1 indicates chained msdus */
if (ret > 0)
- ath10k_htt_rx_h_unchain(ar, &amsdu);
+ ath10k_htt_rx_h_unchain(ar, &amsdu, &drop_cnt, &unchain_cnt);
- ath10k_htt_rx_h_filter(ar, &amsdu, rx_status);
- ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status, true);
+ ath10k_htt_rx_h_filter(ar, &amsdu, rx_status, &drop_cnt_filter);
+ ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status, true, first_hdr, &err);
+ msdus_to_queue = skb_queue_len(&amsdu);
ath10k_htt_rx_h_enqueue(ar, &amsdu, rx_status);
+ ath10k_sta_update_rx_tid_stats(ar, first_hdr, num_msdus, err,
+ unchain_cnt, drop_cnt, drop_cnt_filter,
+ msdus_to_queue);
+
return 0;
}
@@ -1801,9 +1849,14 @@ static void ath10k_htt_rx_proc_rx_ind(struct ath10k_htt *htt,
struct htt_rx_indication_mpdu_range *mpdu_ranges;
int num_mpdu_ranges;
int i, mpdu_count = 0;
+ u16 peer_id;
+ u8 tid;
num_mpdu_ranges = MS(__le32_to_cpu(rx->hdr.info1),
HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES);
+ peer_id = __le16_to_cpu(rx->hdr.peer_id);
+ tid = MS(rx->hdr.info0, HTT_RX_INDICATION_INFO0_EXT_TID);
+
mpdu_ranges = htt_rx_ind_get_mpdu_ranges(rx);
ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx ind: ",
@@ -1815,6 +1868,9 @@ static void ath10k_htt_rx_proc_rx_ind(struct ath10k_htt *htt,
mpdu_count += mpdu_ranges[i].mpdu_count;
atomic_add(mpdu_count, &htt->num_mpdus_ready);
+
+ ath10k_sta_update_rx_tid_stats_ampdu(ar, peer_id, tid, mpdu_ranges,
+ num_mpdu_ranges);
}
static void ath10k_htt_rx_tx_compl_ind(struct ath10k *ar,
@@ -2124,8 +2180,9 @@ static int ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb)
* should still give an idea about rx rate to the user.
*/
ath10k_htt_rx_h_ppdu(ar, &amsdu, status, vdev_id);
- ath10k_htt_rx_h_filter(ar, &amsdu, status);
- ath10k_htt_rx_h_mpdu(ar, &amsdu, status, false);
+ ath10k_htt_rx_h_filter(ar, &amsdu, status, NULL);
+ ath10k_htt_rx_h_mpdu(ar, &amsdu, status, false, NULL,
+ NULL);
ath10k_htt_rx_h_enqueue(ar, &amsdu, status);
break;
case -EAGAIN:
@@ -2499,7 +2556,7 @@ ath10k_update_per_peer_tx_stats(struct ath10k *ar,
arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
arsta->txrate.nss = txrate.nss;
- arsta->txrate.bw = txrate.bw + RATE_INFO_BW_20;
+ arsta->txrate.bw = ath10k_bw_to_mac80211_bw(txrate.bw);
}
static void ath10k_htt_fetch_peer_stats(struct ath10k *ar,
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index ebb3f1b046f3..bf05a3689558 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -1,6 +1,7 @@
/*
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -2976,7 +2977,7 @@ static int ath10k_station_assoc(struct ath10k *ar,
}
/* Plumb cached keys only for static WEP */
- if (arvif->def_wep_key_idx != -1) {
+ if ((arvif->def_wep_key_idx != -1) && (!sta->tdls)) {
ret = ath10k_install_peer_wep_keys(arvif, sta->addr);
if (ret) {
ath10k_warn(ar, "failed to install peer wep keys for vdev %i: %d\n",
@@ -3808,6 +3809,7 @@ void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work)
{
struct ath10k *ar = container_of(work, struct ath10k, wmi_mgmt_tx_work);
struct sk_buff *skb;
+ dma_addr_t paddr;
int ret;
for (;;) {
@@ -3815,11 +3817,27 @@ void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work)
if (!skb)
break;
- ret = ath10k_wmi_mgmt_tx(ar, skb);
- if (ret) {
- ath10k_warn(ar, "failed to transmit management frame via WMI: %d\n",
- ret);
- ieee80211_free_txskb(ar->hw, skb);
+ if (test_bit(ATH10K_FW_FEATURE_MGMT_TX_BY_REF,
+ ar->running_fw->fw_file.fw_features)) {
+ paddr = dma_map_single(ar->dev, skb->data,
+ skb->len, DMA_TO_DEVICE);
+ if (!paddr)
+ continue;
+ ret = ath10k_wmi_mgmt_tx_send(ar, skb, paddr);
+ if (ret) {
+ ath10k_warn(ar, "failed to transmit management frame by ref via WMI: %d\n",
+ ret);
+ dma_unmap_single(ar->dev, paddr, skb->len,
+ DMA_FROM_DEVICE);
+ ieee80211_free_txskb(ar->hw, skb);
+ }
+ } else {
+ ret = ath10k_wmi_mgmt_tx(ar, skb);
+ if (ret) {
+ ath10k_warn(ar, "failed to transmit management frame via WMI: %d\n",
+ ret);
+ ieee80211_free_txskb(ar->hw, skb);
+ }
}
}
}
@@ -5914,6 +5932,10 @@ static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
ath10k_warn(ar, "Peer %pM disappeared!\n", peer_addr);
spin_unlock_bh(&ar->data_lock);
+ if (sta && sta->tdls)
+ ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr,
+ WMI_PEER_AUTHORIZE, 1);
+
exit:
mutex_unlock(&ar->conf_mutex);
return ret;
@@ -6028,9 +6050,8 @@ static void ath10k_sta_rc_update_wk(struct work_struct *wk)
sta->addr, smps, err);
}
- if (changed & IEEE80211_RC_SUPP_RATES_CHANGED ||
- changed & IEEE80211_RC_NSS_CHANGED) {
- ath10k_dbg(ar, ATH10K_DBG_MAC, "mac update sta %pM supp rates/nss\n",
+ if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) {
+ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac update sta %pM supp rates\n",
sta->addr);
err = ath10k_station_assoc(ar, arvif->vif, sta, true);
@@ -7085,10 +7106,20 @@ static void ath10k_sta_rc_update(struct ieee80211_hw *hw,
{
struct ath10k *ar = hw->priv;
struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
+ struct ath10k_vif *arvif = (void *)vif->drv_priv;
+ struct ath10k_peer *peer;
u32 bw, smps;
spin_lock_bh(&ar->data_lock);
+ peer = ath10k_peer_find(ar, arvif->vdev_id, sta->addr);
+ if (!peer) {
+ spin_unlock_bh(&ar->data_lock);
+ ath10k_warn(ar, "mac sta rc update failed to find peer %pM on vdev %i\n",
+ sta->addr, arvif->vdev_id);
+ return;
+ }
+
ath10k_dbg(ar, ATH10K_DBG_MAC,
"mac sta rc update for %pM changed %08x bw %d nss %d smps %d\n",
sta->addr, changed, sta->bandwidth, sta->rx_nss,
@@ -7874,6 +7905,7 @@ static const struct ieee80211_iface_combination ath10k_10x_if_comb[] = {
.max_interfaces = 8,
.num_different_channels = 1,
.beacon_int_infra_match = true,
+ .beacon_int_min_gcd = 1,
#ifdef CONFIG_ATH10K_DFS_CERTIFIED
.radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
BIT(NL80211_CHAN_WIDTH_20) |
@@ -7997,6 +8029,7 @@ static const struct ieee80211_iface_combination ath10k_10_4_if_comb[] = {
.max_interfaces = 16,
.num_different_channels = 1,
.beacon_int_infra_match = true,
+ .beacon_int_min_gcd = 1,
#ifdef CONFIG_ATH10K_DFS_CERTIFIED
.radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
BIT(NL80211_CHAN_WIDTH_20) |
@@ -8298,6 +8331,9 @@ int ath10k_mac_register(struct ath10k *ar)
ieee80211_hw_set(ar->hw, TDLS_WIDER_BW);
}
+ if (test_bit(WMI_SERVICE_TDLS_UAPSD_BUFFER_STA, ar->wmi.svc_map))
+ ieee80211_hw_set(ar->hw, SUPPORTS_TDLS_BUFFER_STA);
+
ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
ar->hw->wiphy->max_remain_on_channel_duration = 5000;
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index 1b266cd0c2ec..fd1566cd7d2b 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -57,6 +57,10 @@ MODULE_PARM_DESC(reset_mode, "0: auto, 1: warm only (default: 0)");
*/
#define ATH10K_DIAG_TRANSFER_LIMIT 0x5000
+#define QCA99X0_PCIE_BAR0_START_REG 0x81030
+#define QCA99X0_CPU_MEM_ADDR_REG 0x4d00c
+#define QCA99X0_CPU_MEM_DATA_REG 0x4d010
+
static const struct pci_device_id ath10k_pci_id_table[] = {
/* PCI-E QCA988X V2 (Ubiquiti branded) */
{ PCI_VDEVICE(UBIQUITI, QCA988X_2_0_DEVICE_ID_UBNT) },
@@ -1584,6 +1588,69 @@ static int ath10k_pci_set_ram_config(struct ath10k *ar, u32 config)
return 0;
}
+/* if an error happened returns < 0, otherwise the length */
+static int ath10k_pci_dump_memory_sram(struct ath10k *ar,
+ const struct ath10k_mem_region *region,
+ u8 *buf)
+{
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+ u32 base_addr, i;
+
+ base_addr = ioread32(ar_pci->mem + QCA99X0_PCIE_BAR0_START_REG);
+ base_addr += region->start;
+
+ for (i = 0; i < region->len; i += 4) {
+ iowrite32(base_addr + i, ar_pci->mem + QCA99X0_CPU_MEM_ADDR_REG);
+ *(u32 *)(buf + i) = ioread32(ar_pci->mem + QCA99X0_CPU_MEM_DATA_REG);
+ }
+
+ return region->len;
+}
+
+/* if an error happened returns < 0, otherwise the length */
+static int ath10k_pci_dump_memory_reg(struct ath10k *ar,
+ const struct ath10k_mem_region *region,
+ u8 *buf)
+{
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+ u32 i;
+
+ for (i = 0; i < region->len; i += 4)
+ *(u32 *)(buf + i) = ioread32(ar_pci->mem + region->start + i);
+
+ return region->len;
+}
+
+/* if an error happened returns < 0, otherwise the length */
+static int ath10k_pci_dump_memory_generic(struct ath10k *ar,
+ const struct ath10k_mem_region *current_region,
+ u8 *buf)
+{
+ int ret;
+
+ if (current_region->section_table.size > 0)
+ /* Copy each section individually. */
+ return ath10k_pci_dump_memory_section(ar,
+ current_region,
+ buf,
+ current_region->len);
+
+ /* No individiual memory sections defined so we can
+ * copy the entire memory region.
+ */
+ ret = ath10k_pci_diag_read_mem(ar,
+ current_region->start,
+ buf,
+ current_region->len);
+ if (ret) {
+ ath10k_warn(ar, "failed to copy ramdump region %s: %d\n",
+ current_region->name, ret);
+ return ret;
+ }
+
+ return current_region->len;
+}
+
static void ath10k_pci_dump_memory(struct ath10k *ar,
struct ath10k_fw_crash_data *crash_data)
{
@@ -1642,27 +1709,20 @@ static void ath10k_pci_dump_memory(struct ath10k *ar,
buf += sizeof(*hdr);
buf_len -= sizeof(*hdr);
- if (current_region->section_table.size > 0) {
- /* Copy each section individually. */
- count = ath10k_pci_dump_memory_section(ar,
- current_region,
- buf,
- current_region->len);
- } else {
- /* No individiual memory sections defined so we can
- * copy the entire memory region.
- */
- ret = ath10k_pci_diag_read_mem(ar,
- current_region->start,
- buf,
- current_region->len);
- if (ret) {
- ath10k_warn(ar, "failed to copy ramdump region %s: %d\n",
- current_region->name, ret);
+ switch (current_region->type) {
+ case ATH10K_MEM_REGION_TYPE_IOSRAM:
+ count = ath10k_pci_dump_memory_sram(ar, current_region, buf);
+ break;
+ case ATH10K_MEM_REGION_TYPE_IOREG:
+ count = ath10k_pci_dump_memory_reg(ar, current_region, buf);
+ break;
+ default:
+ ret = ath10k_pci_dump_memory_generic(ar, current_region, buf);
+ if (ret < 0)
break;
- }
- count = current_region->len;
+ count = ret;
+ break;
}
hdr->region_type = cpu_to_le32(current_region->type);
@@ -2221,7 +2281,7 @@ static int ath10k_pci_get_num_banks(struct ath10k *ar)
}
break;
case QCA9377_1_0_DEVICE_ID:
- return 4;
+ return 9;
}
ath10k_warn(ar, "unknown number of banks, assuming 1\n");
@@ -3718,5 +3778,6 @@ MODULE_FIRMWARE(QCA6174_HW_3_0_FW_DIR "/" QCA6174_HW_3_0_BOARD_DATA_FILE);
MODULE_FIRMWARE(QCA6174_HW_3_0_FW_DIR "/" ATH10K_BOARD_API2_FILE);
/* QCA9377 1.0 firmware files */
+MODULE_FIRMWARE(QCA9377_HW_1_0_FW_DIR "/" ATH10K_FW_API6_FILE);
MODULE_FIRMWARE(QCA9377_HW_1_0_FW_DIR "/" ATH10K_FW_API5_FILE);
MODULE_FIRMWARE(QCA9377_HW_1_0_FW_DIR "/" QCA9377_HW_1_0_BOARD_DATA_FILE);
diff --git a/drivers/net/wireless/ath/ath10k/trace.h b/drivers/net/wireless/ath/ath10k/trace.h
index e40edced1d82..7d2fac342150 100644
--- a/drivers/net/wireless/ath/ath10k/trace.h
+++ b/drivers/net/wireless/ath/ath10k/trace.h
@@ -152,10 +152,9 @@ TRACE_EVENT(ath10k_log_dbg_dump,
);
TRACE_EVENT(ath10k_wmi_cmd,
- TP_PROTO(struct ath10k *ar, int id, const void *buf, size_t buf_len,
- int ret),
+ TP_PROTO(struct ath10k *ar, int id, const void *buf, size_t buf_len),
- TP_ARGS(ar, id, buf, buf_len, ret),
+ TP_ARGS(ar, id, buf, buf_len),
TP_STRUCT__entry(
__string(device, dev_name(ar->dev))
@@ -163,7 +162,6 @@ TRACE_EVENT(ath10k_wmi_cmd,
__field(unsigned int, id)
__field(size_t, buf_len)
__dynamic_array(u8, buf, buf_len)
- __field(int, ret)
),
TP_fast_assign(
@@ -171,17 +169,15 @@ TRACE_EVENT(ath10k_wmi_cmd,
__assign_str(driver, dev_driver_string(ar->dev));
__entry->id = id;
__entry->buf_len = buf_len;
- __entry->ret = ret;
memcpy(__get_dynamic_array(buf), buf, buf_len);
),
TP_printk(
- "%s %s id %d len %zu ret %d",
+ "%s %s id %d len %zu",
__get_str(driver),
__get_str(device),
__entry->id,
- __entry->buf_len,
- __entry->ret
+ __entry->buf_len
)
);
diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c
index 5b3b021526ab..70e23bbf7171 100644
--- a/drivers/net/wireless/ath/ath10k/txrx.c
+++ b/drivers/net/wireless/ath/ath10k/txrx.c
@@ -102,11 +102,6 @@ int ath10k_txrx_tx_unref(struct ath10k_htt *htt,
memset(&info->status, 0, sizeof(info->status));
trace_ath10k_txrx_tx_unref(ar, tx_done->msdu_id);
- if (tx_done->status == HTT_TX_COMPL_STATE_DISCARD) {
- ieee80211_free_txskb(htt->ar->hw, msdu);
- return 0;
- }
-
if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
info->flags |= IEEE80211_TX_STAT_ACK;
@@ -117,6 +112,13 @@ int ath10k_txrx_tx_unref(struct ath10k_htt *htt,
(info->flags & IEEE80211_TX_CTL_NO_ACK))
info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED;
+ if (tx_done->status == HTT_TX_COMPL_STATE_DISCARD) {
+ if (info->flags & IEEE80211_TX_CTL_NO_ACK)
+ info->flags &= ~IEEE80211_TX_STAT_NOACK_TRANSMITTED;
+ else
+ info->flags &= ~IEEE80211_TX_STAT_ACK;
+ }
+
ieee80211_tx_status(htt->ar->hw, msdu);
/* we do not own the msdu anymore */
diff --git a/drivers/net/wireless/ath/ath10k/wmi-ops.h b/drivers/net/wireless/ath/ath10k/wmi-ops.h
index 14093cfdc505..c35e45340b4f 100644
--- a/drivers/net/wireless/ath/ath10k/wmi-ops.h
+++ b/drivers/net/wireless/ath/ath10k/wmi-ops.h
@@ -1,6 +1,7 @@
/*
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -125,6 +126,9 @@ struct wmi_ops {
enum wmi_force_fw_hang_type type,
u32 delay_ms);
struct sk_buff *(*gen_mgmt_tx)(struct ath10k *ar, struct sk_buff *skb);
+ struct sk_buff *(*gen_mgmt_tx_send)(struct ath10k *ar,
+ struct sk_buff *skb,
+ dma_addr_t paddr);
struct sk_buff *(*gen_dbglog_cfg)(struct ath10k *ar, u64 module_enable,
u32 log_level);
struct sk_buff *(*gen_pktlog_enable)(struct ath10k *ar, u32 filter);
@@ -197,6 +201,9 @@ struct wmi_ops {
(struct ath10k *ar,
enum wmi_bss_survey_req_type type);
struct sk_buff *(*gen_echo)(struct ath10k *ar, u32 value);
+ struct sk_buff *(*gen_pdev_get_tpc_table_cmdid)(struct ath10k *ar,
+ u32 param);
+
};
int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id);
@@ -372,12 +379,33 @@ ath10k_wmi_get_txbf_conf_scheme(struct ath10k *ar)
}
static inline int
+ath10k_wmi_mgmt_tx_send(struct ath10k *ar, struct sk_buff *msdu,
+ dma_addr_t paddr)
+{
+ struct sk_buff *skb;
+ int ret;
+
+ if (!ar->wmi.ops->gen_mgmt_tx_send)
+ return -EOPNOTSUPP;
+
+ skb = ar->wmi.ops->gen_mgmt_tx_send(ar, msdu, paddr);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ ret = ath10k_wmi_cmd_send(ar, skb,
+ ar->wmi.cmd->mgmt_tx_send_cmdid);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static inline int
ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(msdu);
struct sk_buff *skb;
int ret;
- u32 mgmt_tx_cmdid;
if (!ar->wmi.ops->gen_mgmt_tx)
return -EOPNOTSUPP;
@@ -386,13 +414,8 @@ ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu)
if (IS_ERR(skb))
return PTR_ERR(skb);
- if (test_bit(ATH10K_FW_FEATURE_MGMT_TX_BY_REF,
- ar->running_fw->fw_file.fw_features))
- mgmt_tx_cmdid = ar->wmi.cmd->mgmt_tx_send_cmdid;
- else
- mgmt_tx_cmdid = ar->wmi.cmd->mgmt_tx_cmdid;
-
- ret = ath10k_wmi_cmd_send(ar, skb, mgmt_tx_cmdid);
+ ret = ath10k_wmi_cmd_send(ar, skb,
+ ar->wmi.cmd->mgmt_tx_cmdid);
if (ret)
return ret;
@@ -1425,4 +1448,21 @@ ath10k_wmi_echo(struct ath10k *ar, u32 value)
return ath10k_wmi_cmd_send(ar, skb, wmi->cmd->echo_cmdid);
}
+static inline int
+ath10k_wmi_pdev_get_tpc_table_cmdid(struct ath10k *ar, u32 param)
+{
+ struct sk_buff *skb;
+
+ if (!ar->wmi.ops->gen_pdev_get_tpc_table_cmdid)
+ return -EOPNOTSUPP;
+
+ skb = ar->wmi.ops->gen_pdev_get_tpc_table_cmdid(ar, param);
+
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ return ath10k_wmi_cmd_send(ar, skb,
+ ar->wmi.cmd->pdev_get_tpc_table_cmdid);
+}
+
#endif
diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
index ae77a007ae07..9d1b0a459069 100644
--- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c
+++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
@@ -1,6 +1,7 @@
/*
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -412,6 +413,62 @@ static int ath10k_wmi_tlv_event_tx_pause(struct ath10k *ar,
return 0;
}
+static int ath10k_wmi_tlv_event_temperature(struct ath10k *ar,
+ struct sk_buff *skb)
+{
+ const struct wmi_tlv_pdev_temperature_event *ev;
+
+ ev = (struct wmi_tlv_pdev_temperature_event *)skb->data;
+ if (WARN_ON(skb->len < sizeof(*ev)))
+ return -EPROTO;
+
+ ath10k_thermal_event_temperature(ar, __le32_to_cpu(ev->temperature));
+ return 0;
+}
+
+static void ath10k_wmi_event_tdls_peer(struct ath10k *ar, struct sk_buff *skb)
+{
+ struct ieee80211_sta *station;
+ const struct wmi_tlv_tdls_peer_event *ev;
+ const void **tb;
+ struct ath10k_vif *arvif;
+
+ tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
+ if (IS_ERR(tb)) {
+ ath10k_warn(ar, "tdls peer failed to parse tlv");
+ return;
+ }
+ ev = tb[WMI_TLV_TAG_STRUCT_TDLS_PEER_EVENT];
+ if (!ev) {
+ kfree(tb);
+ ath10k_warn(ar, "tdls peer NULL event");
+ return;
+ }
+
+ switch (__le32_to_cpu(ev->peer_reason)) {
+ case WMI_TDLS_TEARDOWN_REASON_TX:
+ case WMI_TDLS_TEARDOWN_REASON_RSSI:
+ case WMI_TDLS_TEARDOWN_REASON_PTR_TIMEOUT:
+ station = ieee80211_find_sta_by_ifaddr(ar->hw,
+ ev->peer_macaddr.addr,
+ NULL);
+ if (!station) {
+ ath10k_warn(ar, "did not find station from tdls peer event");
+ kfree(tb);
+ return;
+ }
+ arvif = ath10k_get_arvif(ar, __le32_to_cpu(ev->vdev_id));
+ ieee80211_tdls_oper_request(
+ arvif->vif, station->addr,
+ NL80211_TDLS_TEARDOWN,
+ WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE,
+ GFP_ATOMIC
+ );
+ break;
+ }
+ kfree(tb);
+}
+
/***********/
/* TLV ops */
/***********/
@@ -552,6 +609,12 @@ static void ath10k_wmi_tlv_op_rx(struct ath10k *ar, struct sk_buff *skb)
case WMI_TLV_TX_PAUSE_EVENTID:
ath10k_wmi_tlv_event_tx_pause(ar, skb);
break;
+ case WMI_TLV_PDEV_TEMPERATURE_EVENTID:
+ ath10k_wmi_tlv_event_temperature(ar, skb);
+ break;
+ case WMI_TLV_TDLS_PEER_EVENTID:
+ ath10k_wmi_event_tdls_peer(ar, skb);
+ break;
default:
ath10k_warn(ar, "Unknown eventid: %d\n", id);
break;
@@ -2484,19 +2547,19 @@ ath10k_wmi_tlv_op_gen_request_stats(struct ath10k *ar, u32 stats_mask)
}
static struct sk_buff *
-ath10k_wmi_tlv_op_gen_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu)
+ath10k_wmi_tlv_op_gen_mgmt_tx_send(struct ath10k *ar, struct sk_buff *msdu,
+ dma_addr_t paddr)
{
struct ath10k_skb_cb *cb = ATH10K_SKB_CB(msdu);
struct wmi_tlv_mgmt_tx_cmd *cmd;
- struct wmi_tlv *tlv;
struct ieee80211_hdr *hdr;
+ struct ath10k_vif *arvif;
+ u32 buf_len = msdu->len;
+ struct wmi_tlv *tlv;
struct sk_buff *skb;
+ u32 vdev_id;
void *ptr;
int len;
- u32 buf_len = msdu->len;
- struct ath10k_vif *arvif;
- dma_addr_t mgmt_frame_dma;
- u32 vdev_id;
if (!cb->vif)
return ERR_PTR(-EINVAL);
@@ -2537,12 +2600,7 @@ ath10k_wmi_tlv_op_gen_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu)
cmd->chanfreq = 0;
cmd->buf_len = __cpu_to_le32(buf_len);
cmd->frame_len = __cpu_to_le32(msdu->len);
- mgmt_frame_dma = dma_map_single(arvif->ar->dev, msdu->data,
- msdu->len, DMA_TO_DEVICE);
- if (!mgmt_frame_dma)
- return ERR_PTR(-ENOMEM);
-
- cmd->paddr = __cpu_to_le64(mgmt_frame_dma);
+ cmd->paddr = __cpu_to_le64(paddr);
ptr += sizeof(*tlv);
ptr += sizeof(*cmd);
@@ -2662,6 +2720,25 @@ ath10k_wmi_tlv_op_gen_pktlog_enable(struct ath10k *ar, u32 filter)
}
static struct sk_buff *
+ath10k_wmi_tlv_op_gen_pdev_get_temperature(struct ath10k *ar)
+{
+ struct wmi_tlv_pdev_get_temp_cmd *cmd;
+ struct wmi_tlv *tlv;
+ struct sk_buff *skb;
+
+ skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
+ if (!skb)
+ return ERR_PTR(-ENOMEM);
+
+ tlv = (void *)skb->data;
+ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PDEV_GET_TEMPERATURE_CMD);
+ tlv->len = __cpu_to_le16(sizeof(*cmd));
+ cmd = (void *)tlv->value;
+ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi pdev get temperature tlv\n");
+ return skb;
+}
+
+static struct sk_buff *
ath10k_wmi_tlv_op_gen_pktlog_disable(struct ath10k *ar)
{
struct wmi_tlv_pktlog_disable *cmd;
@@ -2855,6 +2932,15 @@ ath10k_wmi_tlv_op_gen_update_fw_tdls_state(struct ath10k *ar, u32 vdev_id,
*/
u32 options = 0;
+ if (test_bit(WMI_SERVICE_TDLS_UAPSD_BUFFER_STA, ar->wmi.svc_map))
+ options |= WMI_TLV_TDLS_BUFFER_STA_EN;
+
+ /* WMI_TDLS_ENABLE_ACTIVE_EXTERNAL_CONTROL means firm will handle TDLS
+ * link inactivity detecting logic.
+ */
+ if (state == WMI_TDLS_ENABLE_ACTIVE)
+ state = WMI_TDLS_ENABLE_ACTIVE_EXTERNAL_CONTROL;
+
len = sizeof(*tlv) + sizeof(*cmd);
skb = ath10k_wmi_alloc_skb(ar, len);
if (!skb)
@@ -3443,7 +3529,7 @@ static struct wmi_cmd_map wmi_tlv_cmd_map = {
.force_fw_hang_cmdid = WMI_TLV_FORCE_FW_HANG_CMDID,
.gpio_config_cmdid = WMI_TLV_GPIO_CONFIG_CMDID,
.gpio_output_cmdid = WMI_TLV_GPIO_OUTPUT_CMDID,
- .pdev_get_temperature_cmdid = WMI_TLV_CMD_UNSUPPORTED,
+ .pdev_get_temperature_cmdid = WMI_TLV_PDEV_GET_TEMPERATURE_CMDID,
.vdev_set_wmm_params_cmdid = WMI_TLV_VDEV_SET_WMM_PARAMS_CMDID,
.tdls_set_state_cmdid = WMI_TLV_TDLS_SET_STATE_CMDID,
.tdls_peer_update_cmdid = WMI_TLV_TDLS_PEER_UPDATE_CMDID,
@@ -3701,12 +3787,12 @@ static const struct wmi_ops wmi_tlv_ops = {
.gen_request_stats = ath10k_wmi_tlv_op_gen_request_stats,
.gen_force_fw_hang = ath10k_wmi_tlv_op_gen_force_fw_hang,
/* .gen_mgmt_tx = not implemented; HTT is used */
- .gen_mgmt_tx = ath10k_wmi_tlv_op_gen_mgmt_tx,
+ .gen_mgmt_tx_send = ath10k_wmi_tlv_op_gen_mgmt_tx_send,
.gen_dbglog_cfg = ath10k_wmi_tlv_op_gen_dbglog_cfg,
.gen_pktlog_enable = ath10k_wmi_tlv_op_gen_pktlog_enable,
.gen_pktlog_disable = ath10k_wmi_tlv_op_gen_pktlog_disable,
/* .gen_pdev_set_quiet_mode not implemented */
- /* .gen_pdev_get_temperature not implemented */
+ .gen_pdev_get_temperature = ath10k_wmi_tlv_op_gen_pdev_get_temperature,
/* .gen_addba_clear_resp not implemented */
/* .gen_addba_send not implemented */
/* .gen_addba_set_resp not implemented */
diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.h b/drivers/net/wireless/ath/ath10k/wmi-tlv.h
index da89128e8dd6..fa3773ec7c68 100644
--- a/drivers/net/wireless/ath/ath10k/wmi-tlv.h
+++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.h
@@ -1340,6 +1340,17 @@ struct wmi_tlv_init_cmd {
__le32 num_host_mem_chunks;
} __packed;
+struct wmi_tlv_pdev_get_temp_cmd {
+ __le32 pdev_id; /* not used */
+} __packed;
+
+struct wmi_tlv_pdev_temperature_event {
+ __le32 tlv_hdr;
+ /* temperature value in Celcius degree */
+ __le32 temperature;
+ __le32 pdev_id;
+} __packed;
+
struct wmi_tlv_pdev_set_param_cmd {
__le32 pdev_id; /* not used yet */
__le32 param_id;
@@ -1746,6 +1757,13 @@ struct wmi_tlv_tx_pause_ev {
__le32 tid_map;
} __packed;
+struct wmi_tlv_tdls_peer_event {
+ struct wmi_mac_addr peer_macaddr;
+ __le32 peer_status;
+ __le32 peer_reason;
+ __le32 vdev_id;
+} __packed;
+
void ath10k_wmi_tlv_attach(struct ath10k *ar);
struct wmi_tlv_mgmt_tx_cmd {
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index 58dc2189ba49..c5e1ca5945db 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -1,6 +1,7 @@
/*
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -196,6 +197,7 @@ static struct wmi_cmd_map wmi_cmd_map = {
.mu_cal_start_cmdid = WMI_CMD_UNSUPPORTED,
.set_cca_params_cmdid = WMI_CMD_UNSUPPORTED,
.pdev_bss_chan_info_request_cmdid = WMI_CMD_UNSUPPORTED,
+ .pdev_get_tpc_table_cmdid = WMI_CMD_UNSUPPORTED,
};
/* 10.X WMI cmd track */
@@ -362,6 +364,7 @@ static struct wmi_cmd_map wmi_10x_cmd_map = {
.mu_cal_start_cmdid = WMI_CMD_UNSUPPORTED,
.set_cca_params_cmdid = WMI_CMD_UNSUPPORTED,
.pdev_bss_chan_info_request_cmdid = WMI_CMD_UNSUPPORTED,
+ .pdev_get_tpc_table_cmdid = WMI_CMD_UNSUPPORTED,
};
/* 10.2.4 WMI cmd track */
@@ -528,6 +531,7 @@ static struct wmi_cmd_map wmi_10_2_4_cmd_map = {
.set_cca_params_cmdid = WMI_CMD_UNSUPPORTED,
.pdev_bss_chan_info_request_cmdid =
WMI_10_2_PDEV_BSS_CHAN_INFO_REQUEST_CMDID,
+ .pdev_get_tpc_table_cmdid = WMI_CMD_UNSUPPORTED,
};
/* 10.4 WMI cmd track */
@@ -1480,6 +1484,7 @@ static struct wmi_cmd_map wmi_10_2_cmd_map = {
.pdev_get_ani_cck_config_cmdid = WMI_CMD_UNSUPPORTED,
.pdev_get_ani_ofdm_config_cmdid = WMI_CMD_UNSUPPORTED,
.pdev_reserve_ast_entry_cmdid = WMI_CMD_UNSUPPORTED,
+ .pdev_get_tpc_table_cmdid = WMI_CMD_UNSUPPORTED,
};
static struct wmi_pdev_param_map wmi_10_4_pdev_param_map = {
@@ -1742,8 +1747,8 @@ int ath10k_wmi_cmd_send_nowait(struct ath10k *ar, struct sk_buff *skb,
cmd_hdr->cmd_id = __cpu_to_le32(cmd);
memset(skb_cb, 0, sizeof(*skb_cb));
+ trace_ath10k_wmi_cmd(ar, cmd_id, skb->data, skb->len);
ret = ath10k_htc_send(&ar->htc, ar->wmi.eid, skb);
- trace_ath10k_wmi_cmd(ar, cmd_id, skb->data, skb->len, ret);
if (ret)
goto err_pull;
@@ -2703,6 +2708,28 @@ ath10k_wmi_10_4_pull_peer_stats(const struct wmi_10_4_peer_stats *src,
dst->peer_rx_rate = __le32_to_cpu(src->peer_rx_rate);
}
+static void
+ath10k_wmi_10_4_pull_vdev_stats(const struct wmi_vdev_stats_extd *src,
+ struct ath10k_fw_stats_vdev_extd *dst)
+{
+ dst->vdev_id = __le32_to_cpu(src->vdev_id);
+ dst->ppdu_aggr_cnt = __le32_to_cpu(src->ppdu_aggr_cnt);
+ dst->ppdu_noack = __le32_to_cpu(src->ppdu_noack);
+ dst->mpdu_queued = __le32_to_cpu(src->mpdu_queued);
+ dst->ppdu_nonaggr_cnt = __le32_to_cpu(src->ppdu_nonaggr_cnt);
+ dst->mpdu_sw_requeued = __le32_to_cpu(src->mpdu_sw_requeued);
+ dst->mpdu_suc_retry = __le32_to_cpu(src->mpdu_suc_retry);
+ dst->mpdu_suc_multitry = __le32_to_cpu(src->mpdu_suc_multitry);
+ dst->mpdu_fail_retry = __le32_to_cpu(src->mpdu_fail_retry);
+ dst->tx_ftm_suc = __le32_to_cpu(src->tx_ftm_suc);
+ dst->tx_ftm_suc_retry = __le32_to_cpu(src->tx_ftm_suc_retry);
+ dst->tx_ftm_fail = __le32_to_cpu(src->tx_ftm_fail);
+ dst->rx_ftmr_cnt = __le32_to_cpu(src->rx_ftmr_cnt);
+ dst->rx_ftmr_dup_cnt = __le32_to_cpu(src->rx_ftmr_dup_cnt);
+ dst->rx_iftmr_cnt = __le32_to_cpu(src->rx_iftmr_cnt);
+ dst->rx_iftmr_dup_cnt = __le32_to_cpu(src->rx_iftmr_dup_cnt);
+}
+
static int ath10k_wmi_main_op_pull_fw_stats(struct ath10k *ar,
struct sk_buff *skb,
struct ath10k_fw_stats *stats)
@@ -3042,7 +3069,16 @@ static int ath10k_wmi_10_4_op_pull_fw_stats(struct ath10k *ar,
*/
}
- /* fw doesn't implement vdev stats */
+ for (i = 0; i < num_vdev_stats; i++) {
+ const struct wmi_vdev_stats *src;
+
+ /* Ignore vdev stats here as it has only vdev id. Actual vdev
+ * stats will be retrieved from vdev extended stats.
+ */
+ src = (void *)skb->data;
+ if (!skb_pull(skb, sizeof(*src)))
+ return -EPROTO;
+ }
for (i = 0; i < num_peer_stats; i++) {
const struct wmi_10_4_peer_stats *src;
@@ -3074,26 +3110,43 @@ static int ath10k_wmi_10_4_op_pull_fw_stats(struct ath10k *ar,
*/
}
- if ((stats_id & WMI_10_4_STAT_PEER_EXTD) == 0)
- return 0;
+ if (stats_id & WMI_10_4_STAT_PEER_EXTD) {
+ stats->extended = true;
- stats->extended = true;
+ for (i = 0; i < num_peer_stats; i++) {
+ const struct wmi_10_4_peer_extd_stats *src;
+ struct ath10k_fw_extd_stats_peer *dst;
- for (i = 0; i < num_peer_stats; i++) {
- const struct wmi_10_4_peer_extd_stats *src;
- struct ath10k_fw_extd_stats_peer *dst;
+ src = (void *)skb->data;
+ if (!skb_pull(skb, sizeof(*src)))
+ return -EPROTO;
- src = (void *)skb->data;
- if (!skb_pull(skb, sizeof(*src)))
- return -EPROTO;
+ dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+ if (!dst)
+ continue;
- dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
- if (!dst)
- continue;
+ ether_addr_copy(dst->peer_macaddr,
+ src->peer_macaddr.addr);
+ dst->rx_duration = __le32_to_cpu(src->rx_duration);
+ list_add_tail(&dst->list, &stats->peers_extd);
+ }
+ }
+
+ if (stats_id & WMI_10_4_STAT_VDEV_EXTD) {
+ for (i = 0; i < num_vdev_stats; i++) {
+ const struct wmi_vdev_stats_extd *src;
+ struct ath10k_fw_stats_vdev_extd *dst;
- ether_addr_copy(dst->peer_macaddr, src->peer_macaddr.addr);
- dst->rx_duration = __le32_to_cpu(src->rx_duration);
- list_add_tail(&dst->list, &stats->peers_extd);
+ src = (void *)skb->data;
+ if (!skb_pull(skb, sizeof(*src)))
+ return -EPROTO;
+
+ dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+ if (!dst)
+ continue;
+ ath10k_wmi_10_4_pull_vdev_stats(src, dst);
+ list_add_tail(&dst->list, &stats->vdevs);
+ }
}
return 0;
@@ -4313,19 +4366,11 @@ static void ath10k_tpc_config_disp_tables(struct ath10k *ar,
}
}
-void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, struct sk_buff *skb)
+void ath10k_wmi_tpc_config_get_rate_code(u8 *rate_code, u16 *pream_table,
+ u32 num_tx_chain)
{
- u32 i, j, pream_idx, num_tx_chain;
- u8 rate_code[WMI_TPC_RATE_MAX], rate_idx;
- u16 pream_table[WMI_TPC_PREAM_TABLE_MAX];
- struct wmi_pdev_tpc_config_event *ev;
- struct ath10k_tpc_stats *tpc_stats;
-
- ev = (struct wmi_pdev_tpc_config_event *)skb->data;
-
- tpc_stats = kzalloc(sizeof(*tpc_stats), GFP_ATOMIC);
- if (!tpc_stats)
- return;
+ u32 i, j, pream_idx;
+ u8 rate_idx;
/* Create the rate code table based on the chains supported */
rate_idx = 0;
@@ -4349,8 +4394,6 @@ void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, struct sk_buff *skb)
pream_table[pream_idx] = rate_idx;
pream_idx++;
- num_tx_chain = __le32_to_cpu(ev->num_tx_chain);
-
/* Fill HT20 rate code */
for (i = 0; i < num_tx_chain; i++) {
for (j = 0; j < 8; j++) {
@@ -4374,7 +4417,7 @@ void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, struct sk_buff *skb)
pream_idx++;
/* Fill VHT20 rate code */
- for (i = 0; i < __le32_to_cpu(ev->num_tx_chain); i++) {
+ for (i = 0; i < num_tx_chain; i++) {
for (j = 0; j < 10; j++) {
rate_code[rate_idx] =
ATH10K_HW_RATECODE(j, i, WMI_RATE_PREAMBLE_VHT);
@@ -4418,6 +4461,26 @@ void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, struct sk_buff *skb)
ATH10K_HW_RATECODE(0, 0, WMI_RATE_PREAMBLE_OFDM);
pream_table[pream_idx] = ATH10K_TPC_PREAM_TABLE_END;
+}
+
+void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, struct sk_buff *skb)
+{
+ u32 num_tx_chain;
+ u8 rate_code[WMI_TPC_RATE_MAX];
+ u16 pream_table[WMI_TPC_PREAM_TABLE_MAX];
+ struct wmi_pdev_tpc_config_event *ev;
+ struct ath10k_tpc_stats *tpc_stats;
+
+ ev = (struct wmi_pdev_tpc_config_event *)skb->data;
+
+ tpc_stats = kzalloc(sizeof(*tpc_stats), GFP_ATOMIC);
+ if (!tpc_stats)
+ return;
+
+ num_tx_chain = __le32_to_cpu(ev->num_tx_chain);
+
+ ath10k_wmi_tpc_config_get_rate_code(rate_code, pream_table,
+ num_tx_chain);
tpc_stats->chan_freq = __le32_to_cpu(ev->chan_freq);
tpc_stats->phy_mode = __le32_to_cpu(ev->phy_mode);
@@ -4457,6 +4520,246 @@ void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, struct sk_buff *skb)
__le32_to_cpu(ev->rate_max));
}
+static u8
+ath10k_wmi_tpc_final_get_rate(struct ath10k *ar,
+ struct wmi_pdev_tpc_final_table_event *ev,
+ u32 rate_idx, u32 num_chains,
+ u32 rate_code, u8 type, u32 pream_idx)
+{
+ u8 tpc, num_streams, preamble, ch, stm_idx;
+ s8 pow_agcdd, pow_agstbc, pow_agtxbf;
+ int pream;
+
+ num_streams = ATH10K_HW_NSS(rate_code);
+ preamble = ATH10K_HW_PREAMBLE(rate_code);
+ ch = num_chains - 1;
+ stm_idx = num_streams - 1;
+ pream = -1;
+
+ if (__le32_to_cpu(ev->chan_freq) <= 2483) {
+ switch (pream_idx) {
+ case WMI_TPC_PREAM_2GHZ_CCK:
+ pream = 0;
+ break;
+ case WMI_TPC_PREAM_2GHZ_OFDM:
+ pream = 1;
+ break;
+ case WMI_TPC_PREAM_2GHZ_HT20:
+ case WMI_TPC_PREAM_2GHZ_VHT20:
+ pream = 2;
+ break;
+ case WMI_TPC_PREAM_2GHZ_HT40:
+ case WMI_TPC_PREAM_2GHZ_VHT40:
+ pream = 3;
+ break;
+ case WMI_TPC_PREAM_2GHZ_VHT80:
+ pream = 4;
+ break;
+ default:
+ pream = -1;
+ break;
+ }
+ }
+
+ if (__le32_to_cpu(ev->chan_freq) >= 5180) {
+ switch (pream_idx) {
+ case WMI_TPC_PREAM_5GHZ_OFDM:
+ pream = 0;
+ break;
+ case WMI_TPC_PREAM_5GHZ_HT20:
+ case WMI_TPC_PREAM_5GHZ_VHT20:
+ pream = 1;
+ break;
+ case WMI_TPC_PREAM_5GHZ_HT40:
+ case WMI_TPC_PREAM_5GHZ_VHT40:
+ pream = 2;
+ break;
+ case WMI_TPC_PREAM_5GHZ_VHT80:
+ pream = 3;
+ break;
+ case WMI_TPC_PREAM_5GHZ_HTCUP:
+ pream = 4;
+ break;
+ default:
+ pream = -1;
+ break;
+ }
+ }
+
+ if (pream == 4)
+ tpc = min_t(u8, ev->rates_array[rate_idx],
+ ev->max_reg_allow_pow[ch]);
+ else
+ tpc = min_t(u8, min_t(u8, ev->rates_array[rate_idx],
+ ev->max_reg_allow_pow[ch]),
+ ev->ctl_power_table[0][pream][stm_idx]);
+
+ if (__le32_to_cpu(ev->num_tx_chain) <= 1)
+ goto out;
+
+ if (preamble == WMI_RATE_PREAMBLE_CCK)
+ goto out;
+
+ if (num_chains <= num_streams)
+ goto out;
+
+ switch (type) {
+ case WMI_TPC_TABLE_TYPE_STBC:
+ pow_agstbc = ev->max_reg_allow_pow_agstbc[ch - 1][stm_idx];
+ if (pream == 4)
+ tpc = min_t(u8, tpc, pow_agstbc);
+ else
+ tpc = min_t(u8, min_t(u8, tpc, pow_agstbc),
+ ev->ctl_power_table[0][pream][stm_idx]);
+ break;
+ case WMI_TPC_TABLE_TYPE_TXBF:
+ pow_agtxbf = ev->max_reg_allow_pow_agtxbf[ch - 1][stm_idx];
+ if (pream == 4)
+ tpc = min_t(u8, tpc, pow_agtxbf);
+ else
+ tpc = min_t(u8, min_t(u8, tpc, pow_agtxbf),
+ ev->ctl_power_table[1][pream][stm_idx]);
+ break;
+ case WMI_TPC_TABLE_TYPE_CDD:
+ pow_agcdd = ev->max_reg_allow_pow_agcdd[ch - 1][stm_idx];
+ if (pream == 4)
+ tpc = min_t(u8, tpc, pow_agcdd);
+ else
+ tpc = min_t(u8, min_t(u8, tpc, pow_agcdd),
+ ev->ctl_power_table[0][pream][stm_idx]);
+ break;
+ default:
+ ath10k_warn(ar, "unknown wmi tpc final table type: %d\n", type);
+ tpc = 0;
+ break;
+ }
+
+out:
+ return tpc;
+}
+
+static void
+ath10k_wmi_tpc_stats_final_disp_tables(struct ath10k *ar,
+ struct wmi_pdev_tpc_final_table_event *ev,
+ struct ath10k_tpc_stats_final *tpc_stats,
+ u8 *rate_code, u16 *pream_table, u8 type)
+{
+ u32 i, j, pream_idx, flags;
+ u8 tpc[WMI_TPC_TX_N_CHAIN];
+ char tpc_value[WMI_TPC_TX_N_CHAIN * WMI_TPC_BUF_SIZE];
+ char buff[WMI_TPC_BUF_SIZE];
+
+ flags = __le32_to_cpu(ev->flags);
+
+ switch (type) {
+ case WMI_TPC_TABLE_TYPE_CDD:
+ if (!(flags & WMI_TPC_CONFIG_EVENT_FLAG_TABLE_CDD)) {
+ ath10k_dbg(ar, ATH10K_DBG_WMI, "CDD not supported\n");
+ tpc_stats->flag[type] = ATH10K_TPC_TABLE_TYPE_FLAG;
+ return;
+ }
+ break;
+ case WMI_TPC_TABLE_TYPE_STBC:
+ if (!(flags & WMI_TPC_CONFIG_EVENT_FLAG_TABLE_STBC)) {
+ ath10k_dbg(ar, ATH10K_DBG_WMI, "STBC not supported\n");
+ tpc_stats->flag[type] = ATH10K_TPC_TABLE_TYPE_FLAG;
+ return;
+ }
+ break;
+ case WMI_TPC_TABLE_TYPE_TXBF:
+ if (!(flags & WMI_TPC_CONFIG_EVENT_FLAG_TABLE_TXBF)) {
+ ath10k_dbg(ar, ATH10K_DBG_WMI, "TXBF not supported\n");
+ tpc_stats->flag[type] = ATH10K_TPC_TABLE_TYPE_FLAG;
+ return;
+ }
+ break;
+ default:
+ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "invalid table type in wmi tpc event: %d\n", type);
+ return;
+ }
+
+ pream_idx = 0;
+ for (i = 0; i < __le32_to_cpu(ev->rate_max); i++) {
+ memset(tpc_value, 0, sizeof(tpc_value));
+ memset(buff, 0, sizeof(buff));
+ if (i == pream_table[pream_idx])
+ pream_idx++;
+
+ for (j = 0; j < WMI_TPC_TX_N_CHAIN; j++) {
+ if (j >= __le32_to_cpu(ev->num_tx_chain))
+ break;
+
+ tpc[j] = ath10k_wmi_tpc_final_get_rate(ar, ev, i, j + 1,
+ rate_code[i],
+ type, pream_idx);
+ snprintf(buff, sizeof(buff), "%8d ", tpc[j]);
+ strncat(tpc_value, buff, strlen(buff));
+ }
+ tpc_stats->tpc_table_final[type].pream_idx[i] = pream_idx;
+ tpc_stats->tpc_table_final[type].rate_code[i] = rate_code[i];
+ memcpy(tpc_stats->tpc_table_final[type].tpc_value[i],
+ tpc_value, sizeof(tpc_value));
+ }
+}
+
+void ath10k_wmi_event_tpc_final_table(struct ath10k *ar, struct sk_buff *skb)
+{
+ u32 num_tx_chain;
+ u8 rate_code[WMI_TPC_FINAL_RATE_MAX];
+ u16 pream_table[WMI_TPC_PREAM_TABLE_MAX];
+ struct wmi_pdev_tpc_final_table_event *ev;
+ struct ath10k_tpc_stats_final *tpc_stats;
+
+ ev = (struct wmi_pdev_tpc_final_table_event *)skb->data;
+
+ tpc_stats = kzalloc(sizeof(*tpc_stats), GFP_ATOMIC);
+ if (!tpc_stats)
+ return;
+
+ num_tx_chain = __le32_to_cpu(ev->num_tx_chain);
+
+ ath10k_wmi_tpc_config_get_rate_code(rate_code, pream_table,
+ num_tx_chain);
+
+ tpc_stats->chan_freq = __le32_to_cpu(ev->chan_freq);
+ tpc_stats->phy_mode = __le32_to_cpu(ev->phy_mode);
+ tpc_stats->ctl = __le32_to_cpu(ev->ctl);
+ tpc_stats->reg_domain = __le32_to_cpu(ev->reg_domain);
+ tpc_stats->twice_antenna_gain = a_sle32_to_cpu(ev->twice_antenna_gain);
+ tpc_stats->twice_antenna_reduction =
+ __le32_to_cpu(ev->twice_antenna_reduction);
+ tpc_stats->power_limit = __le32_to_cpu(ev->power_limit);
+ tpc_stats->twice_max_rd_power = __le32_to_cpu(ev->twice_max_rd_power);
+ tpc_stats->num_tx_chain = __le32_to_cpu(ev->num_tx_chain);
+ tpc_stats->rate_max = __le32_to_cpu(ev->rate_max);
+
+ ath10k_wmi_tpc_stats_final_disp_tables(ar, ev, tpc_stats,
+ rate_code, pream_table,
+ WMI_TPC_TABLE_TYPE_CDD);
+ ath10k_wmi_tpc_stats_final_disp_tables(ar, ev, tpc_stats,
+ rate_code, pream_table,
+ WMI_TPC_TABLE_TYPE_STBC);
+ ath10k_wmi_tpc_stats_final_disp_tables(ar, ev, tpc_stats,
+ rate_code, pream_table,
+ WMI_TPC_TABLE_TYPE_TXBF);
+
+ ath10k_debug_tpc_stats_final_process(ar, tpc_stats);
+
+ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "wmi event tpc final table channel %d mode %d ctl %d regd %d gain %d %d limit %d max_power %d tx_chanins %d rates %d\n",
+ __le32_to_cpu(ev->chan_freq),
+ __le32_to_cpu(ev->phy_mode),
+ __le32_to_cpu(ev->ctl),
+ __le32_to_cpu(ev->reg_domain),
+ a_sle32_to_cpu(ev->twice_antenna_gain),
+ __le32_to_cpu(ev->twice_antenna_reduction),
+ __le32_to_cpu(ev->power_limit),
+ __le32_to_cpu(ev->twice_max_rd_power) / 2,
+ __le32_to_cpu(ev->num_tx_chain),
+ __le32_to_cpu(ev->rate_max));
+}
+
static void
ath10k_wmi_handle_tdls_peer_event(struct ath10k *ar, struct sk_buff *skb)
{
@@ -5531,6 +5834,7 @@ static void ath10k_wmi_10_4_op_rx(struct ath10k *ar, struct sk_buff *skb)
case WMI_10_4_WOW_WAKEUP_HOST_EVENTID:
case WMI_10_4_PEER_RATECODE_LIST_EVENTID:
case WMI_10_4_WDS_PEER_EVENTID:
+ case WMI_10_4_DEBUG_FATAL_CONDITION_EVENTID:
ath10k_dbg(ar, ATH10K_DBG_WMI,
"received event id %d not implemented\n", id);
break;
@@ -5549,6 +5853,9 @@ static void ath10k_wmi_10_4_op_rx(struct ath10k *ar, struct sk_buff *skb)
case WMI_10_4_TDLS_PEER_EVENTID:
ath10k_wmi_handle_tdls_peer_event(ar, skb);
break;
+ case WMI_10_4_PDEV_TPC_TABLE_EVENTID:
+ ath10k_wmi_event_tpc_final_table(ar, skb);
+ break;
default:
ath10k_warn(ar, "Unknown eventid: %d\n", id);
break;
@@ -7745,6 +8052,72 @@ ath10k_wmi_op_gen_pdev_enable_adaptive_cca(struct ath10k *ar, u8 enable,
return skb;
}
+static void
+ath10k_wmi_fw_vdev_stats_extd_fill(const struct ath10k_fw_stats_vdev_extd *vdev,
+ char *buf, u32 *length)
+{
+ u32 len = *length;
+ u32 buf_len = ATH10K_FW_STATS_BUF_SIZE;
+ u32 val;
+
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "vdev id", vdev->vdev_id);
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "ppdu aggr count", vdev->ppdu_aggr_cnt);
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "ppdu noack", vdev->ppdu_noack);
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "mpdu queued", vdev->mpdu_queued);
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "ppdu nonaggr count", vdev->ppdu_nonaggr_cnt);
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "mpdu sw requeued", vdev->mpdu_sw_requeued);
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "mpdu success retry", vdev->mpdu_suc_retry);
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "mpdu success multitry", vdev->mpdu_suc_multitry);
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "mpdu fail retry", vdev->mpdu_fail_retry);
+ val = vdev->tx_ftm_suc;
+ if (val & WMI_VDEV_STATS_FTM_COUNT_VALID)
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "tx ftm success",
+ MS(val, WMI_VDEV_STATS_FTM_COUNT));
+ val = vdev->tx_ftm_suc_retry;
+ if (val & WMI_VDEV_STATS_FTM_COUNT_VALID)
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "tx ftm success retry",
+ MS(val, WMI_VDEV_STATS_FTM_COUNT));
+ val = vdev->tx_ftm_fail;
+ if (val & WMI_VDEV_STATS_FTM_COUNT_VALID)
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "tx ftm fail",
+ MS(val, WMI_VDEV_STATS_FTM_COUNT));
+ val = vdev->rx_ftmr_cnt;
+ if (val & WMI_VDEV_STATS_FTM_COUNT_VALID)
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "rx ftm request count",
+ MS(val, WMI_VDEV_STATS_FTM_COUNT));
+ val = vdev->rx_ftmr_dup_cnt;
+ if (val & WMI_VDEV_STATS_FTM_COUNT_VALID)
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "rx ftm request dup count",
+ MS(val, WMI_VDEV_STATS_FTM_COUNT));
+ val = vdev->rx_iftmr_cnt;
+ if (val & WMI_VDEV_STATS_FTM_COUNT_VALID)
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "rx initial ftm req count",
+ MS(val, WMI_VDEV_STATS_FTM_COUNT));
+ val = vdev->rx_iftmr_dup_cnt;
+ if (val & WMI_VDEV_STATS_FTM_COUNT_VALID)
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "rx initial ftm req dup cnt",
+ MS(val, WMI_VDEV_STATS_FTM_COUNT));
+ len += scnprintf(buf + len, buf_len - len, "\n");
+
+ *length = len;
+}
+
void ath10k_wmi_10_4_op_fw_stats_fill(struct ath10k *ar,
struct ath10k_fw_stats *fw_stats,
char *buf)
@@ -7752,7 +8125,7 @@ void ath10k_wmi_10_4_op_fw_stats_fill(struct ath10k *ar,
u32 len = 0;
u32 buf_len = ATH10K_FW_STATS_BUF_SIZE;
const struct ath10k_fw_stats_pdev *pdev;
- const struct ath10k_fw_stats_vdev *vdev;
+ const struct ath10k_fw_stats_vdev_extd *vdev;
const struct ath10k_fw_stats_peer *peer;
size_t num_peers;
size_t num_vdevs;
@@ -7805,9 +8178,8 @@ void ath10k_wmi_10_4_op_fw_stats_fill(struct ath10k *ar,
"ath10k VDEV stats", num_vdevs);
len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
"=================");
-
list_for_each_entry(vdev, &fw_stats->vdevs, list) {
- ath10k_wmi_fw_vdev_stats_fill(vdev, buf, &len);
+ ath10k_wmi_fw_vdev_stats_extd_fill(vdev, buf, &len);
}
len += scnprintf(buf + len, buf_len - len, "\n");
@@ -7990,6 +8362,24 @@ static u32 ath10k_wmi_prepare_peer_qos(u8 uapsd_queues, u8 sp)
}
static struct sk_buff *
+ath10k_wmi_10_4_op_gen_pdev_get_tpc_table_cmdid(struct ath10k *ar, u32 param)
+{
+ struct wmi_pdev_get_tpc_table_cmd *cmd;
+ struct sk_buff *skb;
+
+ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+ if (!skb)
+ return ERR_PTR(-ENOMEM);
+
+ cmd = (struct wmi_pdev_get_tpc_table_cmd *)skb->data;
+ cmd->param = __cpu_to_le32(param);
+
+ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "wmi pdev get tpc table param:%d\n", param);
+ return skb;
+}
+
+static struct sk_buff *
ath10k_wmi_10_4_gen_tdls_peer_update(struct ath10k *ar,
const struct wmi_tdls_peer_update_cmd_arg *arg,
const struct wmi_tdls_peer_capab_arg *cap,
@@ -8430,6 +8820,8 @@ static const struct wmi_ops wmi_10_4_ops = {
.ext_resource_config = ath10k_wmi_10_4_ext_resource_config,
.gen_update_fw_tdls_state = ath10k_wmi_10_4_gen_update_fw_tdls_state,
.gen_tdls_peer_update = ath10k_wmi_10_4_gen_tdls_peer_update,
+ .gen_pdev_get_tpc_table_cmdid =
+ ath10k_wmi_10_4_op_gen_pdev_get_tpc_table_cmdid,
/* shared with 10.2 */
.pull_echo_ev = ath10k_wmi_op_pull_echo_ev,
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
index c7b30ed9015d..6fbc84c29521 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.h
+++ b/drivers/net/wireless/ath/ath10k/wmi.h
@@ -1,6 +1,7 @@
/*
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -197,6 +198,9 @@ enum wmi_service {
WMI_SERVICE_TDLS_EXPLICIT_MODE_ONLY,
WMI_SERVICE_MGMT_TX_WMI,
WMI_SERVICE_TDLS_WIDER_BANDWIDTH,
+ WMI_SERVICE_HTT_MGMT_TX_COMP_VALID_FLAGS,
+ WMI_SERVICE_HOST_DFS_CHECK_SUPPORT,
+ WMI_SERVICE_TPC_STATS_FINAL,
/* keep last */
WMI_SERVICE_MAX,
@@ -339,6 +343,9 @@ enum wmi_10_4_service {
WMI_10_4_SERVICE_TDLS_CONN_TRACKER_IN_HOST_MODE,
WMI_10_4_SERVICE_TDLS_EXPLICIT_MODE_ONLY,
WMI_10_4_SERVICE_TDLS_WIDER_BANDWIDTH,
+ WMI_10_4_SERVICE_HTT_MGMT_TX_COMP_VALID_FLAGS,
+ WMI_10_4_SERVICE_HOST_DFS_CHECK_SUPPORT,
+ WMI_10_4_SERVICE_TPC_STATS_FINAL,
};
static inline char *wmi_service_name(int service_id)
@@ -448,6 +455,9 @@ static inline char *wmi_service_name(int service_id)
SVCSTR(WMI_SERVICE_TDLS_CONN_TRACKER_IN_HOST_MODE);
SVCSTR(WMI_SERVICE_TDLS_EXPLICIT_MODE_ONLY);
SVCSTR(WMI_SERVICE_TDLS_WIDER_BANDWIDTH);
+ SVCSTR(WMI_SERVICE_HTT_MGMT_TX_COMP_VALID_FLAGS);
+ SVCSTR(WMI_SERVICE_HOST_DFS_CHECK_SUPPORT);
+ SVCSTR(WMI_SERVICE_TPC_STATS_FINAL);
default:
return NULL;
}
@@ -746,6 +756,12 @@ static inline void wmi_10_4_svc_map(const __le32 *in, unsigned long *out,
WMI_SERVICE_TDLS_EXPLICIT_MODE_ONLY, len);
SVCMAP(WMI_10_4_SERVICE_TDLS_WIDER_BANDWIDTH,
WMI_SERVICE_TDLS_WIDER_BANDWIDTH, len);
+ SVCMAP(WMI_10_4_SERVICE_HTT_MGMT_TX_COMP_VALID_FLAGS,
+ WMI_SERVICE_HTT_MGMT_TX_COMP_VALID_FLAGS, len);
+ SVCMAP(WMI_10_4_SERVICE_HOST_DFS_CHECK_SUPPORT,
+ WMI_SERVICE_HOST_DFS_CHECK_SUPPORT, len);
+ SVCMAP(WMI_10_4_SERVICE_TPC_STATS_FINAL,
+ WMI_SERVICE_TPC_STATS_FINAL, len);
}
#undef SVCMAP
@@ -3993,10 +4009,12 @@ struct wmi_pdev_get_tpc_config_cmd {
#define WMI_TPC_CONFIG_PARAM 1
#define WMI_TPC_RATE_MAX 160
+#define WMI_TPC_FINAL_RATE_MAX 240
#define WMI_TPC_TX_N_CHAIN 4
#define WMI_TPC_PREAM_TABLE_MAX 10
#define WMI_TPC_FLAG 3
#define WMI_TPC_BUF_SIZE 10
+#define WMI_TPC_BEAMFORMING 2
enum wmi_tpc_table_type {
WMI_TPC_TABLE_TYPE_CDD = 0,
@@ -4039,6 +4057,51 @@ enum wmi_tp_scale {
WMI_TP_SCALE_SIZE = 5, /* max num of enum */
};
+struct wmi_pdev_tpc_final_table_event {
+ __le32 reg_domain;
+ __le32 chan_freq;
+ __le32 phy_mode;
+ __le32 twice_antenna_reduction;
+ __le32 twice_max_rd_power;
+ a_sle32 twice_antenna_gain;
+ __le32 power_limit;
+ __le32 rate_max;
+ __le32 num_tx_chain;
+ __le32 ctl;
+ __le32 flags;
+ s8 max_reg_allow_pow[WMI_TPC_TX_N_CHAIN];
+ s8 max_reg_allow_pow_agcdd[WMI_TPC_TX_N_CHAIN][WMI_TPC_TX_N_CHAIN];
+ s8 max_reg_allow_pow_agstbc[WMI_TPC_TX_N_CHAIN][WMI_TPC_TX_N_CHAIN];
+ s8 max_reg_allow_pow_agtxbf[WMI_TPC_TX_N_CHAIN][WMI_TPC_TX_N_CHAIN];
+ u8 rates_array[WMI_TPC_FINAL_RATE_MAX];
+ u8 ctl_power_table[WMI_TPC_BEAMFORMING][WMI_TPC_TX_N_CHAIN]
+ [WMI_TPC_TX_N_CHAIN];
+} __packed;
+
+struct wmi_pdev_get_tpc_table_cmd {
+ __le32 param;
+} __packed;
+
+enum wmi_tpc_pream_2ghz {
+ WMI_TPC_PREAM_2GHZ_CCK = 0,
+ WMI_TPC_PREAM_2GHZ_OFDM,
+ WMI_TPC_PREAM_2GHZ_HT20,
+ WMI_TPC_PREAM_2GHZ_HT40,
+ WMI_TPC_PREAM_2GHZ_VHT20,
+ WMI_TPC_PREAM_2GHZ_VHT40,
+ WMI_TPC_PREAM_2GHZ_VHT80,
+};
+
+enum wmi_tpc_pream_5ghz {
+ WMI_TPC_PREAM_5GHZ_OFDM = 1,
+ WMI_TPC_PREAM_5GHZ_HT20,
+ WMI_TPC_PREAM_5GHZ_HT40,
+ WMI_TPC_PREAM_5GHZ_VHT20,
+ WMI_TPC_PREAM_5GHZ_VHT40,
+ WMI_TPC_PREAM_5GHZ_VHT80,
+ WMI_TPC_PREAM_5GHZ_HTCUP,
+};
+
struct wmi_pdev_chanlist_update_event {
/* number of channels */
__le32 num_chan;
@@ -4350,6 +4413,7 @@ enum wmi_10_4_stats_id {
WMI_10_4_STAT_AP = BIT(1),
WMI_10_4_STAT_INST = BIT(2),
WMI_10_4_STAT_PEER_EXTD = BIT(3),
+ WMI_10_4_STAT_VDEV_EXTD = BIT(4),
};
struct wlan_inst_rssi_args {
@@ -4489,12 +4553,36 @@ struct wmi_10_4_pdev_stats {
/*
* VDEV statistics
- * TODO: add all VDEV stats here
*/
+
+#define WMI_VDEV_STATS_FTM_COUNT_VALID BIT(31)
+#define WMI_VDEV_STATS_FTM_COUNT_LSB 0
+#define WMI_VDEV_STATS_FTM_COUNT_MASK 0x7fffffff
+
struct wmi_vdev_stats {
__le32 vdev_id;
} __packed;
+struct wmi_vdev_stats_extd {
+ __le32 vdev_id;
+ __le32 ppdu_aggr_cnt;
+ __le32 ppdu_noack;
+ __le32 mpdu_queued;
+ __le32 ppdu_nonaggr_cnt;
+ __le32 mpdu_sw_requeued;
+ __le32 mpdu_suc_retry;
+ __le32 mpdu_suc_multitry;
+ __le32 mpdu_fail_retry;
+ __le32 tx_ftm_suc;
+ __le32 tx_ftm_suc_retry;
+ __le32 tx_ftm_fail;
+ __le32 rx_ftmr_cnt;
+ __le32 rx_ftmr_dup_cnt;
+ __le32 rx_iftmr_cnt;
+ __le32 rx_iftmr_dup_cnt;
+ __le32 reserved[6];
+} __packed;
+
/*
* peer statistics.
* TODO: add more stats
@@ -6729,6 +6817,7 @@ enum wmi_tdls_state {
WMI_TDLS_DISABLE,
WMI_TDLS_ENABLE_PASSIVE,
WMI_TDLS_ENABLE_ACTIVE,
+ WMI_TDLS_ENABLE_ACTIVE_EXTERNAL_CONTROL,
};
enum wmi_tdls_peer_state {
@@ -6979,5 +7068,8 @@ void ath10k_wmi_10_4_op_fw_stats_fill(struct ath10k *ar,
int ath10k_wmi_op_get_vdev_subtype(struct ath10k *ar,
enum wmi_vdev_subtype subtype);
int ath10k_wmi_barrier(struct ath10k *ar);
+void ath10k_wmi_tpc_config_get_rate_code(u8 *rate_code, u16 *pream_table,
+ u32 num_tx_chain);
+void ath10k_wmi_event_tpc_final_table(struct ath10k *ar, struct sk_buff *skb);
#endif /* _WMI_H_ */
diff --git a/drivers/net/wireless/ath/ath5k/attach.c b/drivers/net/wireless/ath/ath5k/attach.c
index 233054bd6b52..12d3a6c92ba4 100644
--- a/drivers/net/wireless/ath/ath5k/attach.c
+++ b/drivers/net/wireless/ath/ath5k/attach.c
@@ -327,7 +327,7 @@ int ath5k_hw_init(struct ath5k_hw *ah)
ath5k_hw_set_lladdr(ah, zero_mac);
/* Set BSSID to bcast address: ff:ff:ff:ff:ff:ff for now */
- memcpy(common->curbssid, ath_bcast_mac, ETH_ALEN);
+ eth_broadcast_addr(common->curbssid);
ath5k_hw_set_bssid(ah);
ath5k_hw_set_opmode(ah, ah->opmode);
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index 527afcf39246..a2351ef45ae0 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -73,16 +73,16 @@
#include "trace.h"
bool ath5k_modparam_nohwcrypt;
-module_param_named(nohwcrypt, ath5k_modparam_nohwcrypt, bool, S_IRUGO);
+module_param_named(nohwcrypt, ath5k_modparam_nohwcrypt, bool, 0444);
MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
static bool modparam_fastchanswitch;
-module_param_named(fastchanswitch, modparam_fastchanswitch, bool, S_IRUGO);
+module_param_named(fastchanswitch, modparam_fastchanswitch, bool, 0444);
MODULE_PARM_DESC(fastchanswitch, "Enable fast channel switching for AR2413/AR5413 radios.");
static bool ath5k_modparam_no_hw_rfkill_switch;
module_param_named(no_hw_rfkill_switch, ath5k_modparam_no_hw_rfkill_switch,
- bool, S_IRUGO);
+ bool, 0444);
MODULE_PARM_DESC(no_hw_rfkill_switch, "Ignore the GPIO RFKill switch state");
diff --git a/drivers/net/wireless/ath/ath5k/debug.c b/drivers/net/wireless/ath/ath5k/debug.c
index bd7f6d7b199e..3513bbec4639 100644
--- a/drivers/net/wireless/ath/ath5k/debug.c
+++ b/drivers/net/wireless/ath/ath5k/debug.c
@@ -1004,32 +1004,17 @@ ath5k_debug_init_device(struct ath5k_hw *ah)
if (!phydir)
return;
- debugfs_create_file("debug", S_IWUSR | S_IRUSR, phydir, ah,
- &fops_debug);
-
- debugfs_create_file("registers", S_IRUSR, phydir, ah, &fops_registers);
-
- debugfs_create_file("beacon", S_IWUSR | S_IRUSR, phydir, ah,
- &fops_beacon);
-
- debugfs_create_file("reset", S_IWUSR, phydir, ah, &fops_reset);
-
- debugfs_create_file("antenna", S_IWUSR | S_IRUSR, phydir, ah,
- &fops_antenna);
-
- debugfs_create_file("misc", S_IRUSR, phydir, ah, &fops_misc);
-
- debugfs_create_file("eeprom", S_IRUSR, phydir, ah, &fops_eeprom);
-
- debugfs_create_file("frameerrors", S_IWUSR | S_IRUSR, phydir, ah,
- &fops_frameerrors);
-
- debugfs_create_file("ani", S_IWUSR | S_IRUSR, phydir, ah, &fops_ani);
-
- debugfs_create_file("queue", S_IWUSR | S_IRUSR, phydir, ah,
- &fops_queue);
-
- debugfs_create_bool("32khz_clock", S_IWUSR | S_IRUSR, phydir,
+ debugfs_create_file("debug", 0600, phydir, ah, &fops_debug);
+ debugfs_create_file("registers", 0400, phydir, ah, &fops_registers);
+ debugfs_create_file("beacon", 0600, phydir, ah, &fops_beacon);
+ debugfs_create_file("reset", 0200, phydir, ah, &fops_reset);
+ debugfs_create_file("antenna", 0600, phydir, ah, &fops_antenna);
+ debugfs_create_file("misc", 0400, phydir, ah, &fops_misc);
+ debugfs_create_file("eeprom", 0400, phydir, ah, &fops_eeprom);
+ debugfs_create_file("frameerrors", 0600, phydir, ah, &fops_frameerrors);
+ debugfs_create_file("ani", 0600, phydir, ah, &fops_ani);
+ debugfs_create_file("queue", 0600, phydir, ah, &fops_queue);
+ debugfs_create_bool("32khz_clock", 0600, phydir,
&ah->ah_use_32khz_clock);
}
diff --git a/drivers/net/wireless/ath/ath5k/qcu.c b/drivers/net/wireless/ath/ath5k/qcu.c
index beda11ce34a7..147947f632f7 100644
--- a/drivers/net/wireless/ath/ath5k/qcu.c
+++ b/drivers/net/wireless/ath/ath5k/qcu.c
@@ -327,8 +327,6 @@ ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue)
AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
- tq = &ah->ah_txq[queue];
-
/* Skip if queue inactive or if we are on AR5210
* that doesn't have QCU/DCU */
if ((ah->ah_version == AR5K_AR5210) ||
diff --git a/drivers/net/wireless/ath/ath5k/sysfs.c b/drivers/net/wireless/ath/ath5k/sysfs.c
index 25978c732fe1..8113baddd8fc 100644
--- a/drivers/net/wireless/ath/ath5k/sysfs.c
+++ b/drivers/net/wireless/ath/ath5k/sysfs.c
@@ -31,7 +31,7 @@ static ssize_t ath5k_attr_store_##name(struct device *dev, \
set(ah, val); \
return count; \
} \
-static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, \
+static DEVICE_ATTR(name, 0644, \
ath5k_attr_show_##name, ath5k_attr_store_##name)
#define SIMPLE_SHOW(name, get) \
@@ -43,7 +43,7 @@ static ssize_t ath5k_attr_show_##name(struct device *dev, \
struct ath5k_hw *ah = hw->priv; \
return snprintf(buf, PAGE_SIZE, "%d\n", get); \
} \
-static DEVICE_ATTR(name, S_IRUGO, ath5k_attr_show_##name, NULL)
+static DEVICE_ATTR(name, 0444, ath5k_attr_show_##name, NULL)
/*** ANI ***/
@@ -66,7 +66,7 @@ static ssize_t ath5k_attr_show_noise_immunity_level_max(struct device *dev,
{
return snprintf(buf, PAGE_SIZE, "%d\n", ATH5K_ANI_MAX_NOISE_IMM_LVL);
}
-static DEVICE_ATTR(noise_immunity_level_max, S_IRUGO,
+static DEVICE_ATTR(noise_immunity_level_max, 0444,
ath5k_attr_show_noise_immunity_level_max, NULL);
static ssize_t ath5k_attr_show_firstep_level_max(struct device *dev,
@@ -75,7 +75,7 @@ static ssize_t ath5k_attr_show_firstep_level_max(struct device *dev,
{
return snprintf(buf, PAGE_SIZE, "%d\n", ATH5K_ANI_MAX_FIRSTEP_LVL);
}
-static DEVICE_ATTR(firstep_level_max, S_IRUGO,
+static DEVICE_ATTR(firstep_level_max, 0444,
ath5k_attr_show_firstep_level_max, NULL);
static struct attribute *ath5k_sysfs_entries_ani[] = {
diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c
index 1eea6c23976f..0f965e9f38a4 100644
--- a/drivers/net/wireless/ath/ath6kl/debug.c
+++ b/drivers/net/wireless/ath/ath6kl/debug.c
@@ -1794,69 +1794,68 @@ int ath6kl_debug_init_fs(struct ath6kl *ar)
if (!ar->debugfs_phy)
return -ENOMEM;
- debugfs_create_file("tgt_stats", S_IRUSR, ar->debugfs_phy, ar,
+ debugfs_create_file("tgt_stats", 0400, ar->debugfs_phy, ar,
&fops_tgt_stats);
if (ar->hif_type == ATH6KL_HIF_TYPE_SDIO)
- debugfs_create_file("credit_dist_stats", S_IRUSR,
+ debugfs_create_file("credit_dist_stats", 0400,
ar->debugfs_phy, ar,
&fops_credit_dist_stats);
- debugfs_create_file("endpoint_stats", S_IRUSR | S_IWUSR,
+ debugfs_create_file("endpoint_stats", 0600,
ar->debugfs_phy, ar, &fops_endpoint_stats);
- debugfs_create_file("fwlog", S_IRUSR, ar->debugfs_phy, ar,
- &fops_fwlog);
+ debugfs_create_file("fwlog", 0400, ar->debugfs_phy, ar, &fops_fwlog);
- debugfs_create_file("fwlog_block", S_IRUSR, ar->debugfs_phy, ar,
+ debugfs_create_file("fwlog_block", 0400, ar->debugfs_phy, ar,
&fops_fwlog_block);
- debugfs_create_file("fwlog_mask", S_IRUSR | S_IWUSR, ar->debugfs_phy,
+ debugfs_create_file("fwlog_mask", 0600, ar->debugfs_phy,
ar, &fops_fwlog_mask);
- debugfs_create_file("reg_addr", S_IRUSR | S_IWUSR, ar->debugfs_phy, ar,
+ debugfs_create_file("reg_addr", 0600, ar->debugfs_phy, ar,
&fops_diag_reg_read);
- debugfs_create_file("reg_dump", S_IRUSR, ar->debugfs_phy, ar,
+ debugfs_create_file("reg_dump", 0400, ar->debugfs_phy, ar,
&fops_reg_dump);
- debugfs_create_file("lrssi_roam_threshold", S_IRUSR | S_IWUSR,
+ debugfs_create_file("lrssi_roam_threshold", 0600,
ar->debugfs_phy, ar, &fops_lrssi_roam_threshold);
- debugfs_create_file("reg_write", S_IRUSR | S_IWUSR,
+ debugfs_create_file("reg_write", 0600,
ar->debugfs_phy, ar, &fops_diag_reg_write);
- debugfs_create_file("war_stats", S_IRUSR, ar->debugfs_phy, ar,
+ debugfs_create_file("war_stats", 0400, ar->debugfs_phy, ar,
&fops_war_stats);
- debugfs_create_file("roam_table", S_IRUSR, ar->debugfs_phy, ar,
+ debugfs_create_file("roam_table", 0400, ar->debugfs_phy, ar,
&fops_roam_table);
- debugfs_create_file("force_roam", S_IWUSR, ar->debugfs_phy, ar,
+ debugfs_create_file("force_roam", 0200, ar->debugfs_phy, ar,
&fops_force_roam);
- debugfs_create_file("roam_mode", S_IWUSR, ar->debugfs_phy, ar,
+ debugfs_create_file("roam_mode", 0200, ar->debugfs_phy, ar,
&fops_roam_mode);
- debugfs_create_file("keepalive", S_IRUSR | S_IWUSR, ar->debugfs_phy, ar,
+ debugfs_create_file("keepalive", 0600, ar->debugfs_phy, ar,
&fops_keepalive);
- debugfs_create_file("disconnect_timeout", S_IRUSR | S_IWUSR,
+ debugfs_create_file("disconnect_timeout", 0600,
ar->debugfs_phy, ar, &fops_disconnect_timeout);
- debugfs_create_file("create_qos", S_IWUSR, ar->debugfs_phy, ar,
+ debugfs_create_file("create_qos", 0200, ar->debugfs_phy, ar,
&fops_create_qos);
- debugfs_create_file("delete_qos", S_IWUSR, ar->debugfs_phy, ar,
+ debugfs_create_file("delete_qos", 0200, ar->debugfs_phy, ar,
&fops_delete_qos);
- debugfs_create_file("bgscan_interval", S_IWUSR,
+ debugfs_create_file("bgscan_interval", 0200,
ar->debugfs_phy, ar, &fops_bgscan_int);
- debugfs_create_file("listen_interval", S_IRUSR | S_IWUSR,
+ debugfs_create_file("listen_interval", 0600,
ar->debugfs_phy, ar, &fops_listen_int);
- debugfs_create_file("power_params", S_IWUSR, ar->debugfs_phy, ar,
+ debugfs_create_file("power_params", 0200, ar->debugfs_phy, ar,
&fops_power_params);
return 0;
diff --git a/drivers/net/wireless/ath/ath9k/common-debug.c b/drivers/net/wireless/ath/ath9k/common-debug.c
index 84afcf78151f..239429f10378 100644
--- a/drivers/net/wireless/ath/ath9k/common-debug.c
+++ b/drivers/net/wireless/ath/ath9k/common-debug.c
@@ -47,7 +47,7 @@ static const struct file_operations fops_modal_eeprom = {
void ath9k_cmn_debug_modal_eeprom(struct dentry *debugfs_phy,
struct ath_hw *ah)
{
- debugfs_create_file("modal_eeprom", S_IRUSR, debugfs_phy, ah,
+ debugfs_create_file("modal_eeprom", 0400, debugfs_phy, ah,
&fops_modal_eeprom);
}
EXPORT_SYMBOL(ath9k_cmn_debug_modal_eeprom);
@@ -82,7 +82,7 @@ static const struct file_operations fops_base_eeprom = {
void ath9k_cmn_debug_base_eeprom(struct dentry *debugfs_phy,
struct ath_hw *ah)
{
- debugfs_create_file("base_eeprom", S_IRUSR, debugfs_phy, ah,
+ debugfs_create_file("base_eeprom", 0400, debugfs_phy, ah,
&fops_base_eeprom);
}
EXPORT_SYMBOL(ath9k_cmn_debug_base_eeprom);
@@ -178,8 +178,7 @@ static const struct file_operations fops_recv = {
void ath9k_cmn_debug_recv(struct dentry *debugfs_phy,
struct ath_rx_stats *rxstats)
{
- debugfs_create_file("recv", S_IRUSR, debugfs_phy, rxstats,
- &fops_recv);
+ debugfs_create_file("recv", 0400, debugfs_phy, rxstats, &fops_recv);
}
EXPORT_SYMBOL(ath9k_cmn_debug_recv);
@@ -255,7 +254,7 @@ static const struct file_operations fops_phy_err = {
void ath9k_cmn_debug_phy_err(struct dentry *debugfs_phy,
struct ath_rx_stats *rxstats)
{
- debugfs_create_file("phy_err", S_IRUSR, debugfs_phy, rxstats,
+ debugfs_create_file("phy_err", 0400, debugfs_phy, rxstats,
&fops_phy_err);
}
EXPORT_SYMBOL(ath9k_cmn_debug_phy_err);
diff --git a/drivers/net/wireless/ath/ath9k/common-init.c b/drivers/net/wireless/ath/ath9k/common-init.c
index 8b4f7fdabf58..82de0fadbc95 100644
--- a/drivers/net/wireless/ath/ath9k/common-init.c
+++ b/drivers/net/wireless/ath/ath9k/common-init.c
@@ -88,7 +88,7 @@ static const struct ieee80211_channel ath9k_5ghz_chantable[] = {
CHAN5G(5825, 37), /* Channel 165 */
};
-/* Atheros hardware rate code addition for short premble */
+/* Atheros hardware rate code addition for short preamble */
#define SHPCHECK(__hw_rate, __flags) \
((__flags & IEEE80211_RATE_SHORT_PREAMBLE) ? (__hw_rate | 0x04 ) : 0)
diff --git a/drivers/net/wireless/ath/ath9k/common-spectral.c b/drivers/net/wireless/ath/ath9k/common-spectral.c
index 5e77fe1f5b0d..440e16e641e4 100644
--- a/drivers/net/wireless/ath/ath9k/common-spectral.c
+++ b/drivers/net/wireless/ath/ath9k/common-spectral.c
@@ -479,14 +479,16 @@ ath_cmn_is_fft_buf_full(struct ath_spec_scan_priv *spec_priv)
{
int i = 0;
int ret = 0;
+ struct rchan_buf *buf;
struct rchan *rc = spec_priv->rfs_chan_spec_scan;
- for_each_online_cpu(i)
- ret += relay_buf_full(*per_cpu_ptr(rc->buf, i));
-
- i = num_online_cpus();
+ for_each_possible_cpu(i) {
+ if ((buf = *per_cpu_ptr(rc->buf, i))) {
+ ret += relay_buf_full(buf);
+ }
+ }
- if (ret == i)
+ if (ret)
return 1;
else
return 0;
@@ -1096,23 +1098,23 @@ void ath9k_cmn_spectral_init_debug(struct ath_spec_scan_priv *spec_priv,
return;
debugfs_create_file("spectral_scan_ctl",
- S_IRUSR | S_IWUSR,
+ 0600,
debugfs_phy, spec_priv,
&fops_spec_scan_ctl);
debugfs_create_file("spectral_short_repeat",
- S_IRUSR | S_IWUSR,
+ 0600,
debugfs_phy, spec_priv,
&fops_spectral_short_repeat);
debugfs_create_file("spectral_count",
- S_IRUSR | S_IWUSR,
+ 0600,
debugfs_phy, spec_priv,
&fops_spectral_count);
debugfs_create_file("spectral_period",
- S_IRUSR | S_IWUSR,
+ 0600,
debugfs_phy, spec_priv,
&fops_spectral_period);
debugfs_create_file("spectral_fft_period",
- S_IRUSR | S_IWUSR,
+ 0600,
debugfs_phy, spec_priv,
&fops_spectral_fft_period);
}
diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
index 9e8aed5c478c..f685843a2ff3 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -1385,7 +1385,7 @@ int ath9k_init_debug(struct ath_hw *ah)
return -ENOMEM;
#ifdef CONFIG_ATH_DEBUG
- debugfs_create_file("debug", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
+ debugfs_create_file("debug", 0600, sc->debug.debugfs_phy,
sc, &fops_debug);
#endif
@@ -1409,22 +1409,22 @@ int ath9k_init_debug(struct ath_hw *ah)
ath9k_cmn_debug_recv(sc->debug.debugfs_phy, &sc->debug.stats.rxstats);
ath9k_cmn_debug_phy_err(sc->debug.debugfs_phy, &sc->debug.stats.rxstats);
- debugfs_create_u8("rx_chainmask", S_IRUSR, sc->debug.debugfs_phy,
+ debugfs_create_u8("rx_chainmask", 0400, sc->debug.debugfs_phy,
&ah->rxchainmask);
- debugfs_create_u8("tx_chainmask", S_IRUSR, sc->debug.debugfs_phy,
+ debugfs_create_u8("tx_chainmask", 0400, sc->debug.debugfs_phy,
&ah->txchainmask);
- debugfs_create_file("ani", S_IRUSR | S_IWUSR,
+ debugfs_create_file("ani", 0600,
sc->debug.debugfs_phy, sc, &fops_ani);
- debugfs_create_bool("paprd", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
+ debugfs_create_bool("paprd", 0600, sc->debug.debugfs_phy,
&sc->sc_ah->config.enable_paprd);
- debugfs_create_file("regidx", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
+ debugfs_create_file("regidx", 0600, sc->debug.debugfs_phy,
sc, &fops_regidx);
- debugfs_create_file("regval", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
+ debugfs_create_file("regval", 0600, sc->debug.debugfs_phy,
sc, &fops_regval);
- debugfs_create_bool("ignore_extcca", S_IRUSR | S_IWUSR,
+ debugfs_create_bool("ignore_extcca", 0600,
sc->debug.debugfs_phy,
&ah->config.cwm_ignore_extcca);
- debugfs_create_file("regdump", S_IRUSR, sc->debug.debugfs_phy, sc,
+ debugfs_create_file("regdump", 0400, sc->debug.debugfs_phy, sc,
&fops_regdump);
debugfs_create_devm_seqfile(sc->dev, "dump_nfcal",
sc->debug.debugfs_phy,
@@ -1433,35 +1433,33 @@ int ath9k_init_debug(struct ath_hw *ah)
ath9k_cmn_debug_base_eeprom(sc->debug.debugfs_phy, sc->sc_ah);
ath9k_cmn_debug_modal_eeprom(sc->debug.debugfs_phy, sc->sc_ah);
- debugfs_create_u32("gpio_mask", S_IRUSR | S_IWUSR,
+ debugfs_create_u32("gpio_mask", 0600,
sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask);
- debugfs_create_u32("gpio_val", S_IRUSR | S_IWUSR,
+ debugfs_create_u32("gpio_val", 0600,
sc->debug.debugfs_phy, &sc->sc_ah->gpio_val);
- debugfs_create_file("antenna_diversity", S_IRUSR,
+ debugfs_create_file("antenna_diversity", 0400,
sc->debug.debugfs_phy, sc, &fops_antenna_diversity);
#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
- debugfs_create_file("bt_ant_diversity", S_IRUSR | S_IWUSR,
+ debugfs_create_file("bt_ant_diversity", 0600,
sc->debug.debugfs_phy, sc, &fops_bt_ant_diversity);
- debugfs_create_file("btcoex", S_IRUSR, sc->debug.debugfs_phy, sc,
+ debugfs_create_file("btcoex", 0400, sc->debug.debugfs_phy, sc,
&fops_btcoex);
#endif
#ifdef CONFIG_ATH9K_WOW
- debugfs_create_file("wow", S_IRUSR | S_IWUSR,
- sc->debug.debugfs_phy, sc, &fops_wow);
+ debugfs_create_file("wow", 0600, sc->debug.debugfs_phy, sc, &fops_wow);
#endif
#ifdef CONFIG_ATH9K_DYNACK
- debugfs_create_file("ack_to", S_IRUSR, sc->debug.debugfs_phy,
+ debugfs_create_file("ack_to", 0400, sc->debug.debugfs_phy,
sc, &fops_ackto);
#endif
- debugfs_create_file("tpc", S_IRUSR | S_IWUSR,
- sc->debug.debugfs_phy, sc, &fops_tpc);
+ debugfs_create_file("tpc", 0600, sc->debug.debugfs_phy, sc, &fops_tpc);
- debugfs_create_u16("airtime_flags", S_IRUSR | S_IWUSR,
+ debugfs_create_u16("airtime_flags", 0600,
sc->debug.debugfs_phy, &sc->airtime_flags);
- debugfs_create_file("nf_override", S_IRUSR | S_IWUSR,
+ debugfs_create_file("nf_override", 0600,
sc->debug.debugfs_phy, sc, &fops_nf_override);
return 0;
diff --git a/drivers/net/wireless/ath/ath9k/debug_sta.c b/drivers/net/wireless/ath/ath9k/debug_sta.c
index efc692ee67d4..a6f45f1bb5bb 100644
--- a/drivers/net/wireless/ath/ath9k/debug_sta.c
+++ b/drivers/net/wireless/ath/ath9k/debug_sta.c
@@ -302,7 +302,7 @@ void ath9k_sta_add_debugfs(struct ieee80211_hw *hw,
{
struct ath_node *an = (struct ath_node *)sta->drv_priv;
- debugfs_create_file("node_aggr", S_IRUGO, dir, an, &fops_node_aggr);
- debugfs_create_file("node_recv", S_IRUGO, dir, an, &fops_node_recv);
- debugfs_create_file("airtime", S_IRUGO, dir, an, &fops_airtime);
+ debugfs_create_file("node_aggr", 0444, dir, an, &fops_node_aggr);
+ debugfs_create_file("node_recv", 0444, dir, an, &fops_node_recv);
+ debugfs_create_file("airtime", 0444, dir, an, &fops_airtime);
}
diff --git a/drivers/net/wireless/ath/ath9k/dfs_debug.c b/drivers/net/wireless/ath/ath9k/dfs_debug.c
index 8824610c21fb..3251c9abe270 100644
--- a/drivers/net/wireless/ath/ath9k/dfs_debug.c
+++ b/drivers/net/wireless/ath/ath9k/dfs_debug.c
@@ -144,8 +144,8 @@ static const struct file_operations fops_dfs_stats = {
void ath9k_dfs_init_debug(struct ath_softc *sc)
{
- debugfs_create_file("dfs_stats", S_IRUSR,
+ debugfs_create_file("dfs_stats", 0400,
sc->debug.debugfs_phy, sc, &fops_dfs_stats);
- debugfs_create_file("dfs_simulate_radar", S_IWUSR,
+ debugfs_create_file("dfs_simulate_radar", 0200,
sc->debug.debugfs_phy, sc, &fops_simulate_radar);
}
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
index dc79afd7e151..b3ed65e5c4da 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
@@ -496,25 +496,25 @@ int ath9k_htc_init_debug(struct ath_hw *ah)
ath9k_cmn_spectral_init_debug(&priv->spec_priv, priv->debug.debugfs_phy);
- debugfs_create_file("tgt_int_stats", S_IRUSR, priv->debug.debugfs_phy,
+ debugfs_create_file("tgt_int_stats", 0400, priv->debug.debugfs_phy,
priv, &fops_tgt_int_stats);
- debugfs_create_file("tgt_tx_stats", S_IRUSR, priv->debug.debugfs_phy,
+ debugfs_create_file("tgt_tx_stats", 0400, priv->debug.debugfs_phy,
priv, &fops_tgt_tx_stats);
- debugfs_create_file("tgt_rx_stats", S_IRUSR, priv->debug.debugfs_phy,
+ debugfs_create_file("tgt_rx_stats", 0400, priv->debug.debugfs_phy,
priv, &fops_tgt_rx_stats);
- debugfs_create_file("xmit", S_IRUSR, priv->debug.debugfs_phy,
+ debugfs_create_file("xmit", 0400, priv->debug.debugfs_phy,
priv, &fops_xmit);
- debugfs_create_file("skb_rx", S_IRUSR, priv->debug.debugfs_phy,
+ debugfs_create_file("skb_rx", 0400, priv->debug.debugfs_phy,
priv, &fops_skb_rx);
ath9k_cmn_debug_recv(priv->debug.debugfs_phy, &priv->debug.rx_stats);
ath9k_cmn_debug_phy_err(priv->debug.debugfs_phy, &priv->debug.rx_stats);
- debugfs_create_file("slot", S_IRUSR, priv->debug.debugfs_phy,
+ debugfs_create_file("slot", 0400, priv->debug.debugfs_phy,
priv, &fops_slot);
- debugfs_create_file("queue", S_IRUSR, priv->debug.debugfs_phy,
+ debugfs_create_file("queue", 0400, priv->debug.debugfs_phy,
priv, &fops_queue);
- debugfs_create_file("debug", S_IRUSR | S_IWUSR, priv->debug.debugfs_phy,
+ debugfs_create_file("debug", 0600, priv->debug.debugfs_phy,
priv, &fops_debug);
ath9k_cmn_debug_base_eeprom(priv->debug.debugfs_phy, priv->ah);
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
index f246e9ed4a81..214c68269a69 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
@@ -591,7 +591,7 @@ static void ath9k_init_misc(struct ath9k_htc_priv *priv)
{
struct ath_common *common = ath9k_hw_common(priv->ah);
- memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
+ eth_broadcast_addr(common->bssidmask);
common->last_rssi = ATH_RSSI_DUMMY_MARKER;
priv->ah->opmode = NL80211_IFTYPE_STATION;
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index cd0f023ccf77..6b37036b2d36 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -184,7 +184,8 @@ u16 ath9k_hw_computetxtime(struct ath_hw *ah,
break;
case WLAN_RC_PHY_OFDM:
if (ah->curchan && IS_CHAN_QUARTER_RATE(ah->curchan)) {
- bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME_QUARTER) / 1000;
+ bitsPerSymbol =
+ ((kbps >> 2) * OFDM_SYMBOL_TIME_QUARTER) / 1000;
numBits = OFDM_PLCP_BITS + (frameLen << 3);
numSymbols = DIV_ROUND_UP(numBits, bitsPerSymbol);
txTime = OFDM_SIFS_TIME_QUARTER
@@ -192,7 +193,8 @@ u16 ath9k_hw_computetxtime(struct ath_hw *ah,
+ (numSymbols * OFDM_SYMBOL_TIME_QUARTER);
} else if (ah->curchan &&
IS_CHAN_HALF_RATE(ah->curchan)) {
- bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME_HALF) / 1000;
+ bitsPerSymbol =
+ ((kbps >> 1) * OFDM_SYMBOL_TIME_HALF) / 1000;
numBits = OFDM_PLCP_BITS + (frameLen << 3);
numSymbols = DIV_ROUND_UP(numBits, bitsPerSymbol);
txTime = OFDM_SIFS_TIME_HALF +
@@ -1036,7 +1038,7 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah)
int acktimeout, ctstimeout, ack_offset = 0;
int slottime;
int sifstime;
- int rx_lat = 0, tx_lat = 0, eifs = 0;
+ int rx_lat = 0, tx_lat = 0, eifs = 0, ack_shift = 0;
u32 reg;
ath_dbg(ath9k_hw_common(ah), RESET, "ah->misc_mode 0x%x\n",
@@ -1068,6 +1070,7 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah)
sifstime = 32;
ack_offset = 16;
+ ack_shift = 3;
slottime = 13;
} else if (IS_CHAN_QUARTER_RATE(chan)) {
eifs = 340;
@@ -1078,6 +1081,7 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah)
sifstime = 64;
ack_offset = 32;
+ ack_shift = 1;
slottime = 21;
} else {
if (AR_SREV_9287(ah) && AR_SREV_9287_13_OR_LATER(ah)) {
@@ -1134,6 +1138,10 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah)
SM(tx_lat, AR_USEC_TX_LAT),
AR_USEC_TX_LAT | AR_USEC_RX_LAT | AR_USEC_USEC);
+ if (IS_CHAN_HALF_RATE(chan) || IS_CHAN_QUARTER_RATE(chan))
+ REG_RMW(ah, AR_TXSIFS,
+ sifstime | SM(ack_shift, AR_TXSIFS_ACK_SHIFT),
+ (AR_TXSIFS_TIME | AR_TXSIFS_ACK_SHIFT));
}
EXPORT_SYMBOL(ath9k_hw_init_global_settings);
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index e479fae5aab9..c070a9e51ebf 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -257,6 +257,11 @@ static void ath9k_reg_notifier(struct wiphy *wiphy,
ath_reg_notifier_apply(wiphy, request, reg);
+ /* synchronize DFS detector if regulatory domain changed */
+ if (sc->dfs_detector != NULL)
+ sc->dfs_detector->set_dfs_domain(sc->dfs_detector,
+ request->dfs_region);
+
/* Set tx power */
if (!ah->curchan)
return;
@@ -267,10 +272,6 @@ static void ath9k_reg_notifier(struct wiphy *wiphy,
ath9k_cmn_update_txpow(ah, sc->cur_chan->cur_txpower,
sc->cur_chan->txpower,
&sc->cur_chan->cur_txpower);
- /* synchronize DFS detector if regulatory domain changed */
- if (sc->dfs_detector != NULL)
- sc->dfs_detector->set_dfs_domain(sc->dfs_detector,
- request->dfs_region);
ath9k_ps_restore(sc);
}
@@ -427,7 +428,7 @@ static void ath9k_init_misc(struct ath_softc *sc)
timer_setup(&common->ani.timer, ath_ani_calibrate, 0);
common->last_rssi = ATH_RSSI_DUMMY_MARKER;
- memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
+ eth_broadcast_addr(common->bssidmask);
sc->beacon.slottime = 9;
for (i = 0; i < ARRAY_SIZE(sc->beacon.bslot); i++)
diff --git a/drivers/net/wireless/ath/ath9k/tx99.c b/drivers/net/wireless/ath/ath9k/tx99.c
index fe3a8263b224..ce50d8f5835e 100644
--- a/drivers/net/wireless/ath/ath9k/tx99.c
+++ b/drivers/net/wireless/ath/ath9k/tx99.c
@@ -278,10 +278,10 @@ void ath9k_tx99_init_debug(struct ath_softc *sc)
if (!AR_SREV_9280_20_OR_LATER(sc->sc_ah))
return;
- debugfs_create_file("tx99", S_IRUSR | S_IWUSR,
+ debugfs_create_file("tx99", 0600,
sc->debug.debugfs_phy, sc,
&fops_tx99);
- debugfs_create_file("tx99_power", S_IRUSR | S_IWUSR,
+ debugfs_create_file("tx99_power", 0600,
sc->debug.debugfs_phy, sc,
&fops_tx99_power);
}
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 396bf05c6bf6..7fdb152be0bb 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -252,7 +252,7 @@ ath_tid_pull(struct ath_atx_tid *tid)
}
return skb;
- }
+}
static bool ath_tid_has_buffered(struct ath_atx_tid *tid)
@@ -2892,6 +2892,8 @@ void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an)
struct ath_txq *txq;
int tidno;
+ rcu_read_lock();
+
for (tidno = 0; tidno < IEEE80211_NUM_TIDS; tidno++) {
tid = ath_node_to_tid(an, tidno);
txq = tid->txq;
@@ -2909,6 +2911,8 @@ void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an)
if (!an->sta)
break; /* just one multicast ath_atx_tid */
}
+
+ rcu_read_unlock();
}
#ifdef CONFIG_ATH9K_TX99
diff --git a/drivers/net/wireless/ath/carl9170/debug.c b/drivers/net/wireless/ath/carl9170/debug.c
index ec3a64e5d2bb..a9b6dc17e408 100644
--- a/drivers/net/wireless/ath/carl9170/debug.c
+++ b/drivers/net/wireless/ath/carl9170/debug.c
@@ -187,21 +187,21 @@ static const struct carl9170_debugfs_fops carl_debugfs_##name ##_ops = {\
#define DEBUGFS_DECLARE_RO_FILE(name, _read_bufsize) \
DEBUGFS_DECLARE_FILE(name, carl9170_debugfs_##name ##_read, \
- NULL, _read_bufsize, S_IRUSR)
+ NULL, _read_bufsize, 0400)
#define DEBUGFS_DECLARE_WO_FILE(name) \
DEBUGFS_DECLARE_FILE(name, NULL, carl9170_debugfs_##name ##_write,\
- 0, S_IWUSR)
+ 0, 0200)
#define DEBUGFS_DECLARE_RW_FILE(name, _read_bufsize) \
DEBUGFS_DECLARE_FILE(name, carl9170_debugfs_##name ##_read, \
carl9170_debugfs_##name ##_write, \
- _read_bufsize, S_IRUSR | S_IWUSR)
+ _read_bufsize, 0600)
#define __DEBUGFS_DECLARE_RW_FILE(name, _read_bufsize, _dstate) \
__DEBUGFS_DECLARE_FILE(name, carl9170_debugfs_##name ##_read, \
carl9170_debugfs_##name ##_write, \
- _read_bufsize, S_IRUSR | S_IWUSR, _dstate)
+ _read_bufsize, 0600, _dstate)
#define DEBUGFS_READONLY_FILE(name, _read_bufsize, fmt, value...) \
static char *carl9170_debugfs_ ##name ## _read(struct ar9170 *ar, \
diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c
index 988c8857d78c..29e93c953d93 100644
--- a/drivers/net/wireless/ath/carl9170/main.c
+++ b/drivers/net/wireless/ath/carl9170/main.c
@@ -48,11 +48,11 @@
#include "cmd.h"
static bool modparam_nohwcrypt;
-module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO);
+module_param_named(nohwcrypt, modparam_nohwcrypt, bool, 0444);
MODULE_PARM_DESC(nohwcrypt, "Disable hardware crypto offload.");
int modparam_noht;
-module_param_named(noht, modparam_noht, int, S_IRUGO);
+module_param_named(noht, modparam_noht, int, 0444);
MODULE_PARM_DESC(noht, "Disable MPDU aggregation.");
#define RATE(_bitrate, _hw_rate, _txpidx, _flags) { \
diff --git a/drivers/net/wireless/ath/dfs_pattern_detector.c b/drivers/net/wireless/ath/dfs_pattern_detector.c
index 4100ffd42a43..448b83eea810 100644
--- a/drivers/net/wireless/ath/dfs_pattern_detector.c
+++ b/drivers/net/wireless/ath/dfs_pattern_detector.c
@@ -115,7 +115,7 @@ static const struct radar_detector_specs jp_radar_ref_types[] = {
JP_PATTERN(4, 0, 5, 150, 230, 1, 23, 50, false),
JP_PATTERN(5, 6, 10, 200, 500, 1, 16, 50, false),
JP_PATTERN(6, 11, 20, 200, 500, 1, 12, 50, false),
- JP_PATTERN(7, 50, 100, 1000, 2000, 1, 3, 50, false),
+ JP_PATTERN(7, 50, 100, 1000, 2000, 1, 3, 50, true),
JP_PATTERN(5, 0, 1, 333, 333, 1, 9, 50, false),
};
diff --git a/drivers/net/wireless/ath/wcn36xx/debug.c b/drivers/net/wireless/ath/wcn36xx/debug.c
index 2a6bb62e785c..389b5e7129a6 100644
--- a/drivers/net/wireless/ath/wcn36xx/debug.c
+++ b/drivers/net/wireless/ath/wcn36xx/debug.c
@@ -161,9 +161,8 @@ void wcn36xx_debugfs_init(struct wcn36xx *wcn)
dfs->rootdir = NULL;
}
- ADD_FILE(bmps_switcher, S_IRUSR | S_IWUSR,
- &fops_wcn36xx_bmps, wcn);
- ADD_FILE(dump, S_IWUSR, &fops_wcn36xx_dump, wcn);
+ ADD_FILE(bmps_switcher, 0600, &fops_wcn36xx_bmps, wcn);
+ ADD_FILE(dump, 0200, &fops_wcn36xx_dump, wcn);
}
void wcn36xx_debugfs_exit(struct wcn36xx *wcn)
diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.c b/drivers/net/wireless/ath/wcn36xx/dxe.c
index a3f1f7d042a4..2c3b899a88fa 100644
--- a/drivers/net/wireless/ath/wcn36xx/dxe.c
+++ b/drivers/net/wireless/ath/wcn36xx/dxe.c
@@ -27,15 +27,6 @@
#include "wcn36xx.h"
#include "txrx.h"
-void *wcn36xx_dxe_get_next_bd(struct wcn36xx *wcn, bool is_low)
-{
- struct wcn36xx_dxe_ch *ch = is_low ?
- &wcn->dxe_tx_l_ch :
- &wcn->dxe_tx_h_ch;
-
- return ch->head_blk_ctl->bd_cpu_addr;
-}
-
static void wcn36xx_ccu_write_register(struct wcn36xx *wcn, int addr, int data)
{
wcn36xx_dbg(WCN36XX_DBG_DXE,
@@ -376,7 +367,7 @@ static void reap_tx_dxes(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *ch)
spin_lock_irqsave(&ch->lock, flags);
ctl = ch->tail_blk_ctl;
do {
- if (ctl->desc->ctrl & WCN36XX_DXE_CTRL_VALID_MASK)
+ if (ctl->desc->ctrl & WCN36xx_DXE_CTRL_VLD)
break;
if (ctl->skb) {
dma_unmap_single(wcn->dev, ctl->desc->src_addr_l,
@@ -397,7 +388,7 @@ static void reap_tx_dxes(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *ch)
}
ctl = ctl->next;
} while (ctl != ch->head_blk_ctl &&
- !(ctl->desc->ctrl & WCN36XX_DXE_CTRL_VALID_MASK));
+ !(ctl->desc->ctrl & WCN36xx_DXE_CTRL_VLD));
ch->tail_blk_ctl = ctl;
spin_unlock_irqrestore(&ch->lock, flags);
@@ -415,14 +406,31 @@ static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev)
WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_H,
&int_reason);
- /* TODO: Check int_reason */
-
wcn36xx_dxe_write_register(wcn,
WCN36XX_DXE_0_INT_CLR,
WCN36XX_INT_MASK_CHAN_TX_H);
- wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_ED_CLR,
- WCN36XX_INT_MASK_CHAN_TX_H);
+ if (int_reason & WCN36XX_CH_STAT_INT_ERR_MASK ) {
+ wcn36xx_dxe_write_register(wcn,
+ WCN36XX_DXE_0_INT_ERR_CLR,
+ WCN36XX_INT_MASK_CHAN_TX_H);
+
+ wcn36xx_err("DXE IRQ reported error: 0x%x in high TX channel\n",
+ int_src);
+ }
+
+ if (int_reason & WCN36XX_CH_STAT_INT_DONE_MASK) {
+ wcn36xx_dxe_write_register(wcn,
+ WCN36XX_DXE_0_INT_DONE_CLR,
+ WCN36XX_INT_MASK_CHAN_TX_H);
+ }
+
+ if (int_reason & WCN36XX_CH_STAT_INT_ED_MASK) {
+ wcn36xx_dxe_write_register(wcn,
+ WCN36XX_DXE_0_INT_ED_CLR,
+ WCN36XX_INT_MASK_CHAN_TX_H);
+ }
+
wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ready high\n");
reap_tx_dxes(wcn, &wcn->dxe_tx_h_ch);
}
@@ -431,14 +439,33 @@ static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev)
wcn36xx_dxe_read_register(wcn,
WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_L,
&int_reason);
- /* TODO: Check int_reason */
wcn36xx_dxe_write_register(wcn,
WCN36XX_DXE_0_INT_CLR,
WCN36XX_INT_MASK_CHAN_TX_L);
- wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_ED_CLR,
- WCN36XX_INT_MASK_CHAN_TX_L);
+
+ if (int_reason & WCN36XX_CH_STAT_INT_ERR_MASK ) {
+ wcn36xx_dxe_write_register(wcn,
+ WCN36XX_DXE_0_INT_ERR_CLR,
+ WCN36XX_INT_MASK_CHAN_TX_L);
+
+ wcn36xx_err("DXE IRQ reported error: 0x%x in low TX channel\n",
+ int_src);
+ }
+
+ if (int_reason & WCN36XX_CH_STAT_INT_DONE_MASK) {
+ wcn36xx_dxe_write_register(wcn,
+ WCN36XX_DXE_0_INT_DONE_CLR,
+ WCN36XX_INT_MASK_CHAN_TX_L);
+ }
+
+ if (int_reason & WCN36XX_CH_STAT_INT_ED_MASK) {
+ wcn36xx_dxe_write_register(wcn,
+ WCN36XX_DXE_0_INT_ED_CLR,
+ WCN36XX_INT_MASK_CHAN_TX_L);
+ }
+
wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ready low\n");
reap_tx_dxes(wcn, &wcn->dxe_tx_l_ch);
}
@@ -503,7 +530,7 @@ static int wcn36xx_rx_handle_packets(struct wcn36xx *wcn,
int_mask = WCN36XX_DXE_INT_CH3_MASK;
}
- while (!(dxe->ctrl & WCN36XX_DXE_CTRL_VALID_MASK)) {
+ while (!(dxe->ctrl & WCN36xx_DXE_CTRL_VLD)) {
skb = ctl->skb;
dma_addr = dxe->dst_addr_l;
ret = wcn36xx_dxe_fill_skb(wcn->dev, ctl);
@@ -612,6 +639,7 @@ void wcn36xx_dxe_free_mem_pools(struct wcn36xx *wcn)
int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
struct wcn36xx_vif *vif_priv,
+ struct wcn36xx_tx_bd *bd,
struct sk_buff *skb,
bool is_low)
{
@@ -645,6 +673,9 @@ int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
ctl->skb = NULL;
desc = ctl->desc;
+ /* write buffer descriptor */
+ memcpy(ctl->bd_cpu_addr, bd, sizeof(*bd));
+
/* Set source address of the BD we send */
desc->src_addr_l = ctl->bd_phy_addr;
diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.h b/drivers/net/wireless/ath/wcn36xx/dxe.h
index c012e807753b..ce580960d109 100644
--- a/drivers/net/wireless/ath/wcn36xx/dxe.h
+++ b/drivers/net/wireless/ath/wcn36xx/dxe.h
@@ -33,15 +33,106 @@ H2H_TEST_RX_TX = DMA2
#define WCN36XX_CCU_DXE_INT_SELECT_RIVA 0x310
#define WCN36XX_CCU_DXE_INT_SELECT_PRONTO 0x10dc
-/* TODO This must calculated properly but not hardcoded */
-#define WCN36XX_DXE_CTRL_TX_L 0x328a44
-#define WCN36XX_DXE_CTRL_TX_H 0x32ce44
-#define WCN36XX_DXE_CTRL_RX_L 0x12ad2f
-#define WCN36XX_DXE_CTRL_RX_H 0x12d12f
-#define WCN36XX_DXE_CTRL_TX_H_BD 0x30ce45
-#define WCN36XX_DXE_CTRL_TX_H_SKB 0x32ce4d
-#define WCN36XX_DXE_CTRL_TX_L_BD 0x308a45
-#define WCN36XX_DXE_CTRL_TX_L_SKB 0x328a4d
+/* Descriptor valid */
+#define WCN36xx_DXE_CTRL_VLD BIT(0)
+/* End of packet */
+#define WCN36xx_DXE_CTRL_EOP BIT(3)
+/* BD handling bit */
+#define WCN36xx_DXE_CTRL_BDH BIT(4)
+/* Source is a queue */
+#define WCN36xx_DXE_CTRL_SIQ BIT(5)
+/* Destination is a queue */
+#define WCN36xx_DXE_CTRL_DIQ BIT(6)
+/* Pointer address is a queue */
+#define WCN36xx_DXE_CTRL_PIQ BIT(7)
+/* Release PDU when done */
+#define WCN36xx_DXE_CTRL_PDU_REL BIT(8)
+/* STOP channel processing */
+#define WCN36xx_DXE_CTRL_STOP BIT(16)
+/* INT on descriptor done */
+#define WCN36xx_DXE_CTRL_INT BIT(17)
+/* Endian byte swap enable */
+#define WCN36xx_DXE_CTRL_SWAP BIT(20)
+/* Master endianness */
+#define WCN36xx_DXE_CTRL_ENDIANNESS BIT(21)
+
+/* Transfer type */
+#define WCN36xx_DXE_CTRL_XTYPE_SHIFT 1
+#define WCN36xx_DXE_CTRL_XTYPE_MASK GENMASK(2, WCN36xx_DXE_CTRL_XTYPE_SHIFT)
+#define WCN36xx_DXE_CTRL_XTYPE_SET(x) ((x) << WCN36xx_DXE_CTRL_XTYPE_SHIFT)
+
+/* BMU Threshold select */
+#define WCN36xx_DXE_CTRL_BTHLD_SEL_SHIFT 9
+#define WCN36xx_DXE_CTRL_BTHLD_SEL_MASK GENMASK(12, WCN36xx_DXE_CTRL_BTHLD_SEL_SHIFT)
+#define WCN36xx_DXE_CTRL_BTHLD_SEL_SET(x) ((x) << WCN36xx_DXE_CTRL_BTHLD_SEL_SHIFT)
+
+/* Priority */
+#define WCN36xx_DXE_CTRL_PRIO_SHIFT 13
+#define WCN36xx_DXE_CTRL_PRIO_MASK GENMASK(15, WCN36xx_DXE_CTRL_PRIO_SHIFT)
+#define WCN36xx_DXE_CTRL_PRIO_SET(x) ((x) << WCN36xx_DXE_CTRL_PRIO_SHIFT)
+
+/* BD Template index */
+#define WCN36xx_DXE_CTRL_BDT_IDX_SHIFT 18
+#define WCN36xx_DXE_CTRL_BDT_IDX_MASK GENMASK(19, WCN36xx_DXE_CTRL_BDT_IDX_SHIFT)
+#define WCN36xx_DXE_CTRL_BDT_IDX_SET(x) ((x) << WCN36xx_DXE_CTRL_BDT_IDX_SHIFT)
+
+/* Transfer types: */
+/* Host to host */
+#define WCN36xx_DXE_XTYPE_H2H (0)
+/* Host to BMU */
+#define WCN36xx_DXE_XTYPE_H2B (2)
+/* BMU to host */
+#define WCN36xx_DXE_XTYPE_B2H (3)
+
+#define WCN36XX_DXE_CTRL_TX_L (WCN36xx_DXE_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_H2B) | \
+ WCN36xx_DXE_CTRL_DIQ | WCN36xx_DXE_CTRL_BTHLD_SEL_SET(5) | \
+ WCN36xx_DXE_CTRL_PRIO_SET(4) | WCN36xx_DXE_CTRL_INT | \
+ WCN36xx_DXE_CTRL_SWAP | WCN36xx_DXE_CTRL_ENDIANNESS)
+
+#define WCN36XX_DXE_CTRL_TX_H (WCN36xx_DXE_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_H2B) | \
+ WCN36xx_DXE_CTRL_DIQ | WCN36xx_DXE_CTRL_BTHLD_SEL_SET(7) | \
+ WCN36xx_DXE_CTRL_PRIO_SET(6) | WCN36xx_DXE_CTRL_INT | \
+ WCN36xx_DXE_CTRL_SWAP | WCN36xx_DXE_CTRL_ENDIANNESS)
+
+#define WCN36XX_DXE_CTRL_RX_L (WCN36xx_DXE_CTRL_VLD | \
+ WCN36xx_DXE_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_B2H) | \
+ WCN36xx_DXE_CTRL_EOP | WCN36xx_DXE_CTRL_SIQ | \
+ WCN36xx_DXE_CTRL_PDU_REL | WCN36xx_DXE_CTRL_BTHLD_SEL_SET(6) | \
+ WCN36xx_DXE_CTRL_PRIO_SET(5) | WCN36xx_DXE_CTRL_INT | \
+ WCN36xx_DXE_CTRL_SWAP)
+
+#define WCN36XX_DXE_CTRL_RX_H (WCN36xx_DXE_CTRL_VLD | \
+ WCN36xx_DXE_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_B2H) | \
+ WCN36xx_DXE_CTRL_EOP | WCN36xx_DXE_CTRL_SIQ | \
+ WCN36xx_DXE_CTRL_PDU_REL | WCN36xx_DXE_CTRL_BTHLD_SEL_SET(8) | \
+ WCN36xx_DXE_CTRL_PRIO_SET(6) | WCN36xx_DXE_CTRL_INT | \
+ WCN36xx_DXE_CTRL_SWAP)
+
+#define WCN36XX_DXE_CTRL_TX_H_BD (WCN36xx_DXE_CTRL_VLD | \
+ WCN36xx_DXE_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_H2B) | \
+ WCN36xx_DXE_CTRL_DIQ | WCN36xx_DXE_CTRL_BTHLD_SEL_SET(7) | \
+ WCN36xx_DXE_CTRL_PRIO_SET(6) | WCN36xx_DXE_CTRL_SWAP | \
+ WCN36xx_DXE_CTRL_ENDIANNESS)
+
+#define WCN36XX_DXE_CTRL_TX_H_SKB (WCN36xx_DXE_CTRL_VLD | \
+ WCN36xx_DXE_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_H2B) | \
+ WCN36xx_DXE_CTRL_EOP | WCN36xx_DXE_CTRL_DIQ | \
+ WCN36xx_DXE_CTRL_BTHLD_SEL_SET(7) | WCN36xx_DXE_CTRL_PRIO_SET(6) | \
+ WCN36xx_DXE_CTRL_INT | WCN36xx_DXE_CTRL_SWAP | \
+ WCN36xx_DXE_CTRL_ENDIANNESS)
+
+#define WCN36XX_DXE_CTRL_TX_L_BD (WCN36xx_DXE_CTRL_VLD | \
+ WCN36xx_DXE_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_H2B) | \
+ WCN36xx_DXE_CTRL_DIQ | WCN36xx_DXE_CTRL_BTHLD_SEL_SET(5) | \
+ WCN36xx_DXE_CTRL_PRIO_SET(4) | WCN36xx_DXE_CTRL_SWAP | \
+ WCN36xx_DXE_CTRL_ENDIANNESS)
+
+#define WCN36XX_DXE_CTRL_TX_L_SKB (WCN36xx_DXE_CTRL_VLD | \
+ WCN36xx_DXE_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_H2B) | \
+ WCN36xx_DXE_CTRL_EOP | WCN36xx_DXE_CTRL_DIQ | \
+ WCN36xx_DXE_CTRL_BTHLD_SEL_SET(5) | WCN36xx_DXE_CTRL_PRIO_SET(4) | \
+ WCN36xx_DXE_CTRL_INT | WCN36xx_DXE_CTRL_SWAP | \
+ WCN36xx_DXE_CTRL_ENDIANNESS)
/* TODO This must calculated properly but not hardcoded */
#define WCN36XX_DXE_WQ_TX_L 0x17
@@ -49,15 +140,106 @@ H2H_TEST_RX_TX = DMA2
#define WCN36XX_DXE_WQ_RX_L 0xB
#define WCN36XX_DXE_WQ_RX_H 0x4
-/* DXE descriptor control filed */
-#define WCN36XX_DXE_CTRL_VALID_MASK (0x00000001)
+/* Channel enable or restart */
+#define WCN36xx_DXE_CH_CTRL_EN BIT(0)
+/* End of packet bit */
+#define WCN36xx_DXE_CH_CTRL_EOP BIT(3)
+/* BD Handling bit */
+#define WCN36xx_DXE_CH_CTRL_BDH BIT(4)
+/* Source is queue */
+#define WCN36xx_DXE_CH_CTRL_SIQ BIT(5)
+/* Destination is queue */
+#define WCN36xx_DXE_CH_CTRL_DIQ BIT(6)
+/* Pointer descriptor is queue */
+#define WCN36xx_DXE_CH_CTRL_PIQ BIT(7)
+/* Relase PDU when done */
+#define WCN36xx_DXE_CH_CTRL_PDU_REL BIT(8)
+/* Stop channel processing */
+#define WCN36xx_DXE_CH_CTRL_STOP BIT(16)
+/* Enable external descriptor interrupt */
+#define WCN36xx_DXE_CH_CTRL_INE_ED BIT(17)
+/* Enable channel interrupt on errors */
+#define WCN36xx_DXE_CH_CTRL_INE_ERR BIT(18)
+/* Enable Channel interrupt when done */
+#define WCN36xx_DXE_CH_CTRL_INE_DONE BIT(19)
+/* External descriptor enable */
+#define WCN36xx_DXE_CH_CTRL_EDEN BIT(20)
+/* Wait for valid bit */
+#define WCN36xx_DXE_CH_CTRL_EDVEN BIT(21)
+/* Endianness is little endian*/
+#define WCN36xx_DXE_CH_CTRL_ENDIANNESS BIT(26)
+/* Abort transfer */
+#define WCN36xx_DXE_CH_CTRL_ABORT BIT(27)
+/* Long descriptor format */
+#define WCN36xx_DXE_CH_CTRL_DFMT BIT(28)
+/* Endian byte swap enable */
+#define WCN36xx_DXE_CH_CTRL_SWAP BIT(31)
+
+/* Transfer type */
+#define WCN36xx_DXE_CH_CTRL_XTYPE_SHIFT 1
+#define WCN36xx_DXE_CH_CTRL_XTYPE_MASK GENMASK(2, WCN36xx_DXE_CH_CTRL_XTYPE_SHIFT)
+#define WCN36xx_DXE_CH_CTRL_XTYPE_SET(x) ((x) << WCN36xx_DXE_CH_CTRL_XTYPE_SHIFT)
+
+/* Channel BMU Threshold select */
+#define WCN36xx_DXE_CH_CTRL_BTHLD_SEL_SHIFT 9
+#define WCN36xx_DXE_CH_CTRL_BTHLD_SEL_MASK GENMASK(12, WCN36xx_DXE_CH_CTRL_BTHLD_SEL_SHIFT)
+#define WCN36xx_DXE_CH_CTRL_BTHLD_SEL_SET(x) ((x) << WCN36xx_DXE_CH_CTRL_BTHLD_SEL_SHIFT)
+
+/* Channel Priority */
+#define WCN36xx_DXE_CH_CTRL_PRIO_SHIFT 13
+#define WCN36xx_DXE_CH_CTRL_PRIO_MASK GENMASK(15, WCN36xx_DXE_CH_CTRL_PRIO_SHIFT)
+#define WCN36xx_DXE_CH_CTRL_PRIO_SET(x) ((x) << WCN36xx_DXE_CH_CTRL_PRIO_SHIFT)
+
+/* Counter select */
+#define WCN36xx_DXE_CH_CTRL_SEL_SHIFT 22
+#define WCN36xx_DXE_CH_CTRL_SEL_MASK GENMASK(25, WCN36xx_DXE_CH_CTRL_SEL_SHIFT)
+#define WCN36xx_DXE_CH_CTRL_SEL_SET(x) ((x) << WCN36xx_DXE_CH_CTRL_SEL_SHIFT)
+
+/* Channel BD template index */
+#define WCN36xx_DXE_CH_CTRL_BDT_IDX_SHIFT 29
+#define WCN36xx_DXE_CH_CTRL_BDT_IDX_MASK GENMASK(30, WCN36xx_DXE_CH_CTRL_BDT_IDX_SHIFT)
+#define WCN36xx_DXE_CH_CTRL_BDT_IDX_SET(x) ((x) << WCN36xx_DXE_CH_CTRL_BDT_IDX_SHIFT)
-/* TODO This must calculated properly but not hardcoded */
/* DXE default control register values */
-#define WCN36XX_DXE_CH_DEFAULT_CTL_RX_L 0x847EAD2F
-#define WCN36XX_DXE_CH_DEFAULT_CTL_RX_H 0x84FED12F
-#define WCN36XX_DXE_CH_DEFAULT_CTL_TX_H 0x853ECF4D
-#define WCN36XX_DXE_CH_DEFAULT_CTL_TX_L 0x843e8b4d
+#define WCN36XX_DXE_CH_DEFAULT_CTL_RX_L (WCN36xx_DXE_CH_CTRL_EN | \
+ WCN36xx_DXE_CH_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_B2H) | \
+ WCN36xx_DXE_CH_CTRL_EOP | WCN36xx_DXE_CH_CTRL_SIQ | \
+ WCN36xx_DXE_CH_CTRL_PDU_REL | WCN36xx_DXE_CH_CTRL_BTHLD_SEL_SET(6) | \
+ WCN36xx_DXE_CH_CTRL_PRIO_SET(5) | WCN36xx_DXE_CH_CTRL_INE_ED | \
+ WCN36xx_DXE_CH_CTRL_INE_ERR | WCN36xx_DXE_CH_CTRL_INE_DONE | \
+ WCN36xx_DXE_CH_CTRL_EDEN | WCN36xx_DXE_CH_CTRL_EDVEN | \
+ WCN36xx_DXE_CH_CTRL_SEL_SET(1) | WCN36xx_DXE_CH_CTRL_ENDIANNESS | \
+ WCN36xx_DXE_CH_CTRL_SWAP)
+
+#define WCN36XX_DXE_CH_DEFAULT_CTL_RX_H (WCN36xx_DXE_CH_CTRL_EN | \
+ WCN36xx_DXE_CH_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_B2H) | \
+ WCN36xx_DXE_CH_CTRL_EOP | WCN36xx_DXE_CH_CTRL_SIQ | \
+ WCN36xx_DXE_CH_CTRL_PDU_REL | WCN36xx_DXE_CH_CTRL_BTHLD_SEL_SET(8) | \
+ WCN36xx_DXE_CH_CTRL_PRIO_SET(6) | WCN36xx_DXE_CH_CTRL_INE_ED | \
+ WCN36xx_DXE_CH_CTRL_INE_ERR | WCN36xx_DXE_CH_CTRL_INE_DONE | \
+ WCN36xx_DXE_CH_CTRL_EDEN | WCN36xx_DXE_CH_CTRL_EDVEN | \
+ WCN36xx_DXE_CH_CTRL_SEL_SET(3) | WCN36xx_DXE_CH_CTRL_ENDIANNESS | \
+ WCN36xx_DXE_CH_CTRL_SWAP)
+
+#define WCN36XX_DXE_CH_DEFAULT_CTL_TX_H (WCN36xx_DXE_CH_CTRL_EN | \
+ WCN36xx_DXE_CH_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_H2B) | \
+ WCN36xx_DXE_CH_CTRL_EOP | WCN36xx_DXE_CH_CTRL_DIQ | \
+ WCN36xx_DXE_CH_CTRL_PDU_REL | WCN36xx_DXE_CH_CTRL_BTHLD_SEL_SET(7) | \
+ WCN36xx_DXE_CH_CTRL_PRIO_SET(6) | WCN36xx_DXE_CH_CTRL_INE_ED | \
+ WCN36xx_DXE_CH_CTRL_INE_ERR | WCN36xx_DXE_CH_CTRL_INE_DONE | \
+ WCN36xx_DXE_CH_CTRL_EDEN | WCN36xx_DXE_CH_CTRL_EDVEN | \
+ WCN36xx_DXE_CH_CTRL_SEL_SET(4) | WCN36xx_DXE_CH_CTRL_ENDIANNESS | \
+ WCN36xx_DXE_CH_CTRL_SWAP)
+
+#define WCN36XX_DXE_CH_DEFAULT_CTL_TX_L (WCN36xx_DXE_CH_CTRL_EN | \
+ WCN36xx_DXE_CH_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_H2B) | \
+ WCN36xx_DXE_CH_CTRL_EOP | WCN36xx_DXE_CH_CTRL_DIQ | \
+ WCN36xx_DXE_CH_CTRL_PDU_REL | WCN36xx_DXE_CH_CTRL_BTHLD_SEL_SET(5) | \
+ WCN36xx_DXE_CH_CTRL_PRIO_SET(4) | WCN36xx_DXE_CH_CTRL_INE_ED | \
+ WCN36xx_DXE_CH_CTRL_INE_ERR | WCN36xx_DXE_CH_CTRL_INE_DONE | \
+ WCN36xx_DXE_CH_CTRL_EDEN | WCN36xx_DXE_CH_CTRL_EDVEN | \
+ WCN36xx_DXE_CH_CTRL_SEL_SET(0) | WCN36xx_DXE_CH_CTRL_ENDIANNESS | \
+ WCN36xx_DXE_CH_CTRL_SWAP)
/* Common DXE registers */
#define WCN36XX_DXE_MEM_CSR (WCN36XX_DXE_MEM_REG + 0x00)
@@ -80,6 +262,10 @@ H2H_TEST_RX_TX = DMA2
#define WCN36XX_DXE_0_INT_DONE_CLR (WCN36XX_DXE_MEM_REG + 0x38)
#define WCN36XX_DXE_0_INT_ERR_CLR (WCN36XX_DXE_MEM_REG + 0x3C)
+#define WCN36XX_CH_STAT_INT_DONE_MASK 0x00008000
+#define WCN36XX_CH_STAT_INT_ERR_MASK 0x00004000
+#define WCN36XX_CH_STAT_INT_ED_MASK 0x00002000
+
#define WCN36XX_DXE_0_CH0_STATUS (WCN36XX_DXE_MEM_REG + 0x404)
#define WCN36XX_DXE_0_CH1_STATUS (WCN36XX_DXE_MEM_REG + 0x444)
#define WCN36XX_DXE_0_CH2_STATUS (WCN36XX_DXE_MEM_REG + 0x484)
@@ -266,6 +452,7 @@ struct wcn36xx_dxe_mem_pool {
dma_addr_t phy_addr;
};
+struct wcn36xx_tx_bd;
struct wcn36xx_vif;
int wcn36xx_dxe_allocate_mem_pools(struct wcn36xx *wcn);
void wcn36xx_dxe_free_mem_pools(struct wcn36xx *wcn);
@@ -277,8 +464,8 @@ void wcn36xx_dxe_deinit(struct wcn36xx *wcn);
int wcn36xx_dxe_init_channels(struct wcn36xx *wcn);
int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
struct wcn36xx_vif *vif_priv,
+ struct wcn36xx_tx_bd *bd,
struct sk_buff *skb,
bool is_low);
void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status);
-void *wcn36xx_dxe_get_next_bd(struct wcn36xx *wcn, bool is_low);
#endif /* _DXE_H_ */
diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c
index ab5be6d2c691..69d6be59d97f 100644
--- a/drivers/net/wireless/ath/wcn36xx/main.c
+++ b/drivers/net/wireless/ath/wcn36xx/main.c
@@ -261,7 +261,7 @@ static void wcn36xx_feat_caps_info(struct wcn36xx *wcn)
for (i = 0; i < MAX_FEATURE_SUPPORTED; i++) {
if (get_feat_caps(wcn->fw_feat_caps, i))
- wcn36xx_info("FW Cap %s\n", wcn36xx_get_cap_name(i));
+ wcn36xx_dbg(WCN36XX_DBG_MAC, "FW Cap %s\n", wcn36xx_get_cap_name(i));
}
}
@@ -666,16 +666,13 @@ static void wcn36xx_cancel_hw_scan(struct ieee80211_hw *hw,
{
struct wcn36xx *wcn = hw->priv;
- if (!wcn36xx_smd_stop_hw_scan(wcn)) {
- struct cfg80211_scan_info scan_info = { .aborted = true };
-
- ieee80211_scan_completed(wcn->hw, &scan_info);
- }
-
mutex_lock(&wcn->scan_lock);
wcn->scan_aborted = true;
mutex_unlock(&wcn->scan_lock);
+ /* ieee80211_scan_completed will be called on FW scan indication */
+ wcn36xx_smd_stop_hw_scan(wcn);
+
cancel_work_sync(&wcn->scan_work);
}
@@ -1155,8 +1152,6 @@ static int wcn36xx_init_ieee80211(struct wcn36xx *wcn)
wcn->hw->wiphy->cipher_suites = cipher_suites;
wcn->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
- wcn->hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
-
#ifdef CONFIG_PM
wcn->hw->wiphy->wowlan = &wowlan_support;
#endif
@@ -1283,6 +1278,7 @@ static int wcn36xx_probe(struct platform_device *pdev)
wcn = hw->priv;
wcn->hw = hw;
wcn->dev = &pdev->dev;
+ wcn->first_boot = true;
mutex_init(&wcn->conf_mutex);
mutex_init(&wcn->hal_mutex);
mutex_init(&wcn->scan_lock);
diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c
index 2a4871ca9c72..8932af5e4d8d 100644
--- a/drivers/net/wireless/ath/wcn36xx/smd.c
+++ b/drivers/net/wireless/ath/wcn36xx/smd.c
@@ -409,15 +409,17 @@ static int wcn36xx_smd_start_rsp(struct wcn36xx *wcn, void *buf, size_t len)
wcn->fw_minor = rsp->start_rsp_params.version.minor;
wcn->fw_major = rsp->start_rsp_params.version.major;
- wcn36xx_info("firmware WLAN version '%s' and CRM version '%s'\n",
- wcn->wlan_version, wcn->crm_version);
-
- wcn36xx_info("firmware API %u.%u.%u.%u, %u stations, %u bssids\n",
- wcn->fw_major, wcn->fw_minor,
- wcn->fw_version, wcn->fw_revision,
- rsp->start_rsp_params.stations,
- rsp->start_rsp_params.bssids);
+ if (wcn->first_boot) {
+ wcn->first_boot = false;
+ wcn36xx_info("firmware WLAN version '%s' and CRM version '%s'\n",
+ wcn->wlan_version, wcn->crm_version);
+ wcn36xx_info("firmware API %u.%u.%u.%u, %u stations, %u bssids\n",
+ wcn->fw_major, wcn->fw_minor,
+ wcn->fw_version, wcn->fw_revision,
+ rsp->start_rsp_params.stations,
+ rsp->start_rsp_params.bssids);
+ }
return 0;
}
@@ -2138,6 +2140,8 @@ static int wcn36xx_smd_hw_scan_ind(struct wcn36xx *wcn, void *buf, size_t len)
case WCN36XX_HAL_SCAN_IND_COMPLETED:
mutex_lock(&wcn->scan_lock);
wcn->scan_req = NULL;
+ if (wcn->scan_aborted)
+ scan_info.aborted = true;
mutex_unlock(&wcn->scan_lock);
ieee80211_scan_completed(wcn->hw, &scan_info);
break;
@@ -2407,54 +2411,63 @@ static void wcn36xx_ind_smd_work(struct work_struct *work)
{
struct wcn36xx *wcn =
container_of(work, struct wcn36xx, hal_ind_work);
- struct wcn36xx_hal_msg_header *msg_header;
- struct wcn36xx_hal_ind_msg *hal_ind_msg;
- unsigned long flags;
- spin_lock_irqsave(&wcn->hal_ind_lock, flags);
+ for (;;) {
+ struct wcn36xx_hal_msg_header *msg_header;
+ struct wcn36xx_hal_ind_msg *hal_ind_msg;
+ unsigned long flags;
- hal_ind_msg = list_first_entry(&wcn->hal_ind_queue,
- struct wcn36xx_hal_ind_msg,
- list);
- list_del(wcn->hal_ind_queue.next);
- spin_unlock_irqrestore(&wcn->hal_ind_lock, flags);
+ spin_lock_irqsave(&wcn->hal_ind_lock, flags);
- msg_header = (struct wcn36xx_hal_msg_header *)hal_ind_msg->msg;
+ if (list_empty(&wcn->hal_ind_queue)) {
+ spin_unlock_irqrestore(&wcn->hal_ind_lock, flags);
+ return;
+ }
- switch (msg_header->msg_type) {
- case WCN36XX_HAL_COEX_IND:
- case WCN36XX_HAL_DEL_BA_IND:
- case WCN36XX_HAL_AVOID_FREQ_RANGE_IND:
- break;
- case WCN36XX_HAL_OTA_TX_COMPL_IND:
- wcn36xx_smd_tx_compl_ind(wcn,
- hal_ind_msg->msg,
- hal_ind_msg->msg_len);
- break;
- case WCN36XX_HAL_MISSED_BEACON_IND:
- wcn36xx_smd_missed_beacon_ind(wcn,
- hal_ind_msg->msg,
- hal_ind_msg->msg_len);
- break;
- case WCN36XX_HAL_DELETE_STA_CONTEXT_IND:
- wcn36xx_smd_delete_sta_context_ind(wcn,
- hal_ind_msg->msg,
- hal_ind_msg->msg_len);
- break;
- case WCN36XX_HAL_PRINT_REG_INFO_IND:
- wcn36xx_smd_print_reg_info_ind(wcn,
- hal_ind_msg->msg,
- hal_ind_msg->msg_len);
- break;
- case WCN36XX_HAL_SCAN_OFFLOAD_IND:
- wcn36xx_smd_hw_scan_ind(wcn, hal_ind_msg->msg,
- hal_ind_msg->msg_len);
- break;
- default:
- wcn36xx_err("SMD_EVENT (%d) not supported\n",
- msg_header->msg_type);
+ hal_ind_msg = list_first_entry(&wcn->hal_ind_queue,
+ struct wcn36xx_hal_ind_msg,
+ list);
+ list_del(&hal_ind_msg->list);
+ spin_unlock_irqrestore(&wcn->hal_ind_lock, flags);
+
+ msg_header = (struct wcn36xx_hal_msg_header *)hal_ind_msg->msg;
+
+ switch (msg_header->msg_type) {
+ case WCN36XX_HAL_COEX_IND:
+ case WCN36XX_HAL_DEL_BA_IND:
+ case WCN36XX_HAL_AVOID_FREQ_RANGE_IND:
+ break;
+ case WCN36XX_HAL_OTA_TX_COMPL_IND:
+ wcn36xx_smd_tx_compl_ind(wcn,
+ hal_ind_msg->msg,
+ hal_ind_msg->msg_len);
+ break;
+ case WCN36XX_HAL_MISSED_BEACON_IND:
+ wcn36xx_smd_missed_beacon_ind(wcn,
+ hal_ind_msg->msg,
+ hal_ind_msg->msg_len);
+ break;
+ case WCN36XX_HAL_DELETE_STA_CONTEXT_IND:
+ wcn36xx_smd_delete_sta_context_ind(wcn,
+ hal_ind_msg->msg,
+ hal_ind_msg->msg_len);
+ break;
+ case WCN36XX_HAL_PRINT_REG_INFO_IND:
+ wcn36xx_smd_print_reg_info_ind(wcn,
+ hal_ind_msg->msg,
+ hal_ind_msg->msg_len);
+ break;
+ case WCN36XX_HAL_SCAN_OFFLOAD_IND:
+ wcn36xx_smd_hw_scan_ind(wcn, hal_ind_msg->msg,
+ hal_ind_msg->msg_len);
+ break;
+ default:
+ wcn36xx_err("SMD_EVENT (%d) not supported\n",
+ msg_header->msg_type);
+ }
+
+ kfree(hal_ind_msg);
}
- kfree(hal_ind_msg);
}
int wcn36xx_smd_open(struct wcn36xx *wcn)
{
diff --git a/drivers/net/wireless/ath/wcn36xx/txrx.c b/drivers/net/wireless/ath/wcn36xx/txrx.c
index 22304edc5948..b1768ed6b0be 100644
--- a/drivers/net/wireless/ath/wcn36xx/txrx.c
+++ b/drivers/net/wireless/ath/wcn36xx/txrx.c
@@ -272,21 +272,9 @@ int wcn36xx_start_tx(struct wcn36xx *wcn,
bool is_low = ieee80211_is_data(hdr->frame_control);
bool bcast = is_broadcast_ether_addr(hdr->addr1) ||
is_multicast_ether_addr(hdr->addr1);
- struct wcn36xx_tx_bd *bd = wcn36xx_dxe_get_next_bd(wcn, is_low);
-
- if (!bd) {
- /*
- * TX DXE are used in pairs. One for the BD and one for the
- * actual frame. The BD DXE's has a preallocated buffer while
- * the skb ones does not. If this isn't true something is really
- * wierd. TODO: Recover from this situation
- */
-
- wcn36xx_err("bd address may not be NULL for BD DXE\n");
- return -EINVAL;
- }
+ struct wcn36xx_tx_bd bd;
- memset(bd, 0, sizeof(*bd));
+ memset(&bd, 0, sizeof(bd));
wcn36xx_dbg(WCN36XX_DBG_TX,
"tx skb %p len %d fc %04x sn %d %s %s\n",
@@ -296,10 +284,10 @@ int wcn36xx_start_tx(struct wcn36xx *wcn,
wcn36xx_dbg_dump(WCN36XX_DBG_TX_DUMP, "", skb->data, skb->len);
- bd->dpu_rf = WCN36XX_BMU_WQ_TX;
+ bd.dpu_rf = WCN36XX_BMU_WQ_TX;
- bd->tx_comp = !!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS);
- if (bd->tx_comp) {
+ bd.tx_comp = !!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS);
+ if (bd.tx_comp) {
wcn36xx_dbg(WCN36XX_DBG_DXE, "TX_ACK status requested\n");
spin_lock_irqsave(&wcn->dxe_lock, flags);
if (wcn->tx_ack_skb) {
@@ -321,13 +309,13 @@ int wcn36xx_start_tx(struct wcn36xx *wcn,
/* Data frames served first*/
if (is_low)
- wcn36xx_set_tx_data(bd, wcn, &vif_priv, sta_priv, skb, bcast);
+ wcn36xx_set_tx_data(&bd, wcn, &vif_priv, sta_priv, skb, bcast);
else
/* MGMT and CTRL frames are handeld here*/
- wcn36xx_set_tx_mgmt(bd, wcn, &vif_priv, skb, bcast);
+ wcn36xx_set_tx_mgmt(&bd, wcn, &vif_priv, skb, bcast);
- buff_to_be((u32 *)bd, sizeof(*bd)/sizeof(u32));
- bd->tx_bd_sign = 0xbdbdbdbd;
+ buff_to_be((u32 *)&bd, sizeof(bd)/sizeof(u32));
+ bd.tx_bd_sign = 0xbdbdbdbd;
- return wcn36xx_dxe_tx_frame(wcn, vif_priv, skb, is_low);
+ return wcn36xx_dxe_tx_frame(wcn, vif_priv, &bd, skb, is_low);
}
diff --git a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h
index 81017e6703b4..5854adf43f3a 100644
--- a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h
+++ b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h
@@ -192,6 +192,8 @@ struct wcn36xx {
u8 crm_version[WCN36XX_HAL_VERSION_LENGTH + 1];
u8 wlan_version[WCN36XX_HAL_VERSION_LENGTH + 1];
+ bool first_boot;
+
/* IRQs */
int tx_irq;
int rx_irq;
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 768f63f38341..cdbb393863f3 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -17,8 +18,10 @@
#include <linux/etherdevice.h>
#include <linux/moduleparam.h>
#include <net/netlink.h>
+#include <net/cfg80211.h>
#include "wil6210.h"
#include "wmi.h"
+#include "fw.h"
#define WIL_MAX_ROC_DURATION_MS 5000
@@ -258,9 +261,10 @@ int wil_iftype_nl2wmi(enum nl80211_iftype type)
return -EOPNOTSUPP;
}
-int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid,
+int wil_cid_fill_sinfo(struct wil6210_vif *vif, int cid,
struct station_info *sinfo)
{
+ struct wil6210_priv *wil = vif_to_wil(vif);
struct wmi_notify_req_cmd cmd = {
.cid = cid,
.interval_usec = 0,
@@ -272,17 +276,17 @@ int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid,
struct wil_net_stats *stats = &wil->sta[cid].stats;
int rc;
- rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, &cmd, sizeof(cmd),
+ rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, vif->mid, &cmd, sizeof(cmd),
WMI_NOTIFY_REQ_DONE_EVENTID, &reply, sizeof(reply), 20);
if (rc)
return rc;
- wil_dbg_wmi(wil, "Link status for CID %d: {\n"
+ wil_dbg_wmi(wil, "Link status for CID %d MID %d: {\n"
" MCS %d TSF 0x%016llx\n"
" BF status 0x%08x RSSI %d SQI %d%%\n"
" Tx Tpt %d goodput %d Rx goodput %d\n"
" Sectors(rx:tx) my %d:%d peer %d:%d\n""}\n",
- cid, le16_to_cpu(reply.evt.bf_mcs),
+ cid, vif->mid, le16_to_cpu(reply.evt.bf_mcs),
le64_to_cpu(reply.evt.tsf), reply.evt.status,
reply.evt.rssi,
reply.evt.sqi,
@@ -315,7 +319,7 @@ int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid,
sinfo->tx_packets = stats->tx_packets;
sinfo->tx_failed = stats->tx_errors;
- if (test_bit(wil_status_fwconnected, wil->status)) {
+ if (test_bit(wil_vif_fwconnected, vif->status)) {
sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
if (test_bit(WMI_FW_CAPABILITY_RSSI_REPORTING,
wil->fw_capabilities))
@@ -331,30 +335,34 @@ static int wil_cfg80211_get_station(struct wiphy *wiphy,
struct net_device *ndev,
const u8 *mac, struct station_info *sinfo)
{
+ struct wil6210_vif *vif = ndev_to_vif(ndev);
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
int rc;
- int cid = wil_find_cid(wil, mac);
+ int cid = wil_find_cid(wil, vif->mid, mac);
- wil_dbg_misc(wil, "get_station: %pM CID %d\n", mac, cid);
+ wil_dbg_misc(wil, "get_station: %pM CID %d MID %d\n", mac, cid,
+ vif->mid);
if (cid < 0)
return cid;
- rc = wil_cid_fill_sinfo(wil, cid, sinfo);
+ rc = wil_cid_fill_sinfo(vif, cid, sinfo);
return rc;
}
/*
- * Find @idx-th active STA for station dump.
+ * Find @idx-th active STA for specific MID for station dump.
*/
-static int wil_find_cid_by_idx(struct wil6210_priv *wil, int idx)
+static int wil_find_cid_by_idx(struct wil6210_priv *wil, u8 mid, int idx)
{
int i;
for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
if (wil->sta[i].status == wil_sta_unused)
continue;
+ if (wil->sta[i].mid != mid)
+ continue;
if (idx == 0)
return i;
idx--;
@@ -367,17 +375,19 @@ static int wil_cfg80211_dump_station(struct wiphy *wiphy,
struct net_device *dev, int idx,
u8 *mac, struct station_info *sinfo)
{
+ struct wil6210_vif *vif = ndev_to_vif(dev);
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
int rc;
- int cid = wil_find_cid_by_idx(wil, idx);
+ int cid = wil_find_cid_by_idx(wil, vif->mid, idx);
if (cid < 0)
return -ENOENT;
ether_addr_copy(mac, wil->sta[cid].addr);
- wil_dbg_misc(wil, "dump_station: %pM CID %d\n", mac, cid);
+ wil_dbg_misc(wil, "dump_station: %pM CID %d MID %d\n", mac, cid,
+ vif->mid);
- rc = wil_cid_fill_sinfo(wil, cid, sinfo);
+ rc = wil_cid_fill_sinfo(vif, cid, sinfo);
return rc;
}
@@ -388,7 +398,7 @@ static int wil_cfg80211_start_p2p_device(struct wiphy *wiphy,
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
wil_dbg_misc(wil, "start_p2p_device: entered\n");
- wil->p2p.p2p_dev_started = 1;
+ wil->p2p_dev_started = 1;
return 0;
}
@@ -396,20 +406,66 @@ static void wil_cfg80211_stop_p2p_device(struct wiphy *wiphy,
struct wireless_dev *wdev)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
- struct wil_p2p_info *p2p = &wil->p2p;
- if (!p2p->p2p_dev_started)
+ if (!wil->p2p_dev_started)
return;
wil_dbg_misc(wil, "stop_p2p_device: entered\n");
mutex_lock(&wil->mutex);
- mutex_lock(&wil->p2p_wdev_mutex);
+ mutex_lock(&wil->vif_mutex);
wil_p2p_stop_radio_operations(wil);
- p2p->p2p_dev_started = 0;
- mutex_unlock(&wil->p2p_wdev_mutex);
+ wil->p2p_dev_started = 0;
+ mutex_unlock(&wil->vif_mutex);
mutex_unlock(&wil->mutex);
}
+static int wil_cfg80211_validate_add_iface(struct wil6210_priv *wil,
+ enum nl80211_iftype new_type)
+{
+ int i;
+ struct wireless_dev *wdev;
+ struct iface_combination_params params = {
+ .num_different_channels = 1,
+ };
+
+ for (i = 0; i < wil->max_vifs; i++) {
+ if (wil->vifs[i]) {
+ wdev = vif_to_wdev(wil->vifs[i]);
+ params.iftype_num[wdev->iftype]++;
+ }
+ }
+ params.iftype_num[new_type]++;
+ return cfg80211_check_combinations(wil->wiphy, &params);
+}
+
+static int wil_cfg80211_validate_change_iface(struct wil6210_priv *wil,
+ struct wil6210_vif *vif,
+ enum nl80211_iftype new_type)
+{
+ int i, ret = 0;
+ struct wireless_dev *wdev;
+ struct iface_combination_params params = {
+ .num_different_channels = 1,
+ };
+ bool check_combos = false;
+
+ for (i = 0; i < wil->max_vifs; i++) {
+ struct wil6210_vif *vif_pos = wil->vifs[i];
+
+ if (vif_pos && vif != vif_pos) {
+ wdev = vif_to_wdev(vif_pos);
+ params.iftype_num[wdev->iftype]++;
+ check_combos = true;
+ }
+ }
+
+ if (check_combos) {
+ params.iftype_num[new_type]++;
+ ret = cfg80211_check_combinations(wil->wiphy, &params);
+ }
+ return ret;
+}
+
static struct wireless_dev *
wil_cfg80211_add_iface(struct wiphy *wiphy, const char *name,
unsigned char name_assign_type,
@@ -417,51 +473,137 @@ wil_cfg80211_add_iface(struct wiphy *wiphy, const char *name,
struct vif_params *params)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
- struct net_device *ndev = wil_to_ndev(wil);
- struct wireless_dev *p2p_wdev;
+ struct net_device *ndev_main = wil->main_ndev, *ndev;
+ struct wil6210_vif *vif;
+ struct wireless_dev *p2p_wdev, *wdev;
+ int rc;
- wil_dbg_misc(wil, "add_iface\n");
+ wil_dbg_misc(wil, "add_iface, type %d\n", type);
- if (type != NL80211_IFTYPE_P2P_DEVICE) {
- wil_err(wil, "unsupported iftype %d\n", type);
- return ERR_PTR(-EINVAL);
+ /* P2P device is not a real virtual interface, it is a management-only
+ * interface that shares the main interface.
+ * Skip concurrency checks here.
+ */
+ if (type == NL80211_IFTYPE_P2P_DEVICE) {
+ if (wil->p2p_wdev) {
+ wil_err(wil, "P2P_DEVICE interface already created\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ p2p_wdev = kzalloc(sizeof(*p2p_wdev), GFP_KERNEL);
+ if (!p2p_wdev)
+ return ERR_PTR(-ENOMEM);
+
+ p2p_wdev->iftype = type;
+ p2p_wdev->wiphy = wiphy;
+ /* use our primary ethernet address */
+ ether_addr_copy(p2p_wdev->address, ndev_main->perm_addr);
+
+ wil->p2p_wdev = p2p_wdev;
+
+ return p2p_wdev;
}
- if (wil->p2p_wdev) {
- wil_err(wil, "P2P_DEVICE interface already created\n");
+ if (!wil->wiphy->n_iface_combinations) {
+ wil_err(wil, "virtual interfaces not supported\n");
return ERR_PTR(-EINVAL);
}
- p2p_wdev = kzalloc(sizeof(*p2p_wdev), GFP_KERNEL);
- if (!p2p_wdev)
- return ERR_PTR(-ENOMEM);
+ rc = wil_cfg80211_validate_add_iface(wil, type);
+ if (rc) {
+ wil_err(wil, "iface validation failed, err=%d\n", rc);
+ return ERR_PTR(rc);
+ }
- p2p_wdev->iftype = type;
- p2p_wdev->wiphy = wiphy;
- /* use our primary ethernet address */
- ether_addr_copy(p2p_wdev->address, ndev->perm_addr);
+ vif = wil_vif_alloc(wil, name, name_assign_type, type);
+ if (IS_ERR(vif))
+ return ERR_CAST(vif);
+
+ ndev = vif_to_ndev(vif);
+ ether_addr_copy(ndev->perm_addr, ndev_main->perm_addr);
+ if (is_valid_ether_addr(params->macaddr)) {
+ ether_addr_copy(ndev->dev_addr, params->macaddr);
+ } else {
+ ether_addr_copy(ndev->dev_addr, ndev_main->perm_addr);
+ ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << vif->mid)) |
+ 0x2; /* locally administered */
+ }
+ wdev = vif_to_wdev(vif);
+ ether_addr_copy(wdev->address, ndev->dev_addr);
- wil->p2p_wdev = p2p_wdev;
+ rc = wil_vif_add(wil, vif);
+ if (rc)
+ goto out;
- return p2p_wdev;
+ wil_info(wil, "added VIF, mid %d iftype %d MAC %pM\n",
+ vif->mid, type, wdev->address);
+ return wdev;
+out:
+ wil_vif_free(vif);
+ return ERR_PTR(rc);
+}
+
+int wil_vif_prepare_stop(struct wil6210_vif *vif)
+{
+ struct wil6210_priv *wil = vif_to_wil(vif);
+ struct wireless_dev *wdev = vif_to_wdev(vif);
+ struct net_device *ndev;
+ int rc;
+
+ if (wdev->iftype != NL80211_IFTYPE_AP)
+ return 0;
+
+ ndev = vif_to_ndev(vif);
+ if (netif_carrier_ok(ndev)) {
+ rc = wmi_pcp_stop(vif);
+ if (rc) {
+ wil_info(wil, "failed to stop AP, status %d\n",
+ rc);
+ /* continue */
+ }
+ wil_bcast_fini(vif);
+ netif_carrier_off(ndev);
+ }
+
+ return 0;
}
static int wil_cfg80211_del_iface(struct wiphy *wiphy,
struct wireless_dev *wdev)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
+ int rc;
wil_dbg_misc(wil, "del_iface\n");
- if (wdev != wil->p2p_wdev) {
- wil_err(wil, "delete of incorrect interface 0x%p\n", wdev);
+ if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE) {
+ if (wdev != wil->p2p_wdev) {
+ wil_err(wil, "delete of incorrect interface 0x%p\n",
+ wdev);
+ return -EINVAL;
+ }
+
+ wil_cfg80211_stop_p2p_device(wiphy, wdev);
+ wil_p2p_wdev_free(wil);
+ return 0;
+ }
+
+ if (vif->mid == 0) {
+ wil_err(wil, "cannot remove the main interface\n");
return -EINVAL;
}
- wil_cfg80211_stop_p2p_device(wiphy, wdev);
- wil_p2p_wdev_free(wil);
+ rc = wil_vif_prepare_stop(vif);
+ if (rc)
+ goto out;
+
+ wil_info(wil, "deleted VIF, mid %d iftype %d MAC %pM\n",
+ vif->mid, wdev->iftype, wdev->address);
- return 0;
+ wil_vif_remove(wil, vif->mid);
+out:
+ return rc;
}
static int wil_cfg80211_change_iface(struct wiphy *wiphy,
@@ -470,12 +612,26 @@ static int wil_cfg80211_change_iface(struct wiphy *wiphy,
struct vif_params *params)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
- struct wireless_dev *wdev = wil_to_wdev(wil);
+ struct wil6210_vif *vif = ndev_to_vif(ndev);
+ struct wireless_dev *wdev = vif_to_wdev(vif);
int rc;
+ bool fw_reset = false;
wil_dbg_misc(wil, "change_iface: type=%d\n", type);
- if (netif_running(wil_to_ndev(wil)) && !wil_is_recovery_blocked(wil)) {
+ if (wiphy->n_iface_combinations) {
+ rc = wil_cfg80211_validate_change_iface(wil, vif, type);
+ if (rc) {
+ wil_err(wil, "iface validation failed, err=%d\n", rc);
+ return rc;
+ }
+ }
+
+ /* do not reset FW when there are active VIFs,
+ * because it can cause significant disruption
+ */
+ if (!wil_has_other_active_ifaces(wil, ndev, true, false) &&
+ netif_running(ndev) && !wil_is_recovery_blocked(wil)) {
wil_dbg_misc(wil, "interface is up. resetting...\n");
mutex_lock(&wil->mutex);
__wil_down(wil);
@@ -484,6 +640,7 @@ static int wil_cfg80211_change_iface(struct wiphy *wiphy,
if (rc)
return rc;
+ fw_reset = true;
}
switch (type) {
@@ -500,8 +657,18 @@ static int wil_cfg80211_change_iface(struct wiphy *wiphy,
return -EOPNOTSUPP;
}
- wdev->iftype = type;
+ if (vif->mid != 0 && wil_has_active_ifaces(wil, true, false)) {
+ if (!fw_reset)
+ wil_vif_prepare_stop(vif);
+ rc = wmi_port_delete(wil, vif->mid);
+ if (rc)
+ return rc;
+ rc = wmi_port_allocate(wil, vif->mid, ndev->dev_addr, type);
+ if (rc)
+ return rc;
+ }
+ wdev->iftype = type;
return 0;
}
@@ -510,6 +677,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
struct wireless_dev *wdev = request->wdev;
+ struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
struct {
struct wmi_start_scan_cmd cmd;
u16 chnl[4];
@@ -537,35 +705,38 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
mutex_lock(&wil->mutex);
- mutex_lock(&wil->p2p_wdev_mutex);
- if (wil->scan_request || wil->p2p.discovery_started) {
+ mutex_lock(&wil->vif_mutex);
+ if (vif->scan_request || vif->p2p.discovery_started) {
wil_err(wil, "Already scanning\n");
- mutex_unlock(&wil->p2p_wdev_mutex);
+ mutex_unlock(&wil->vif_mutex);
rc = -EAGAIN;
goto out;
}
- mutex_unlock(&wil->p2p_wdev_mutex);
+ mutex_unlock(&wil->vif_mutex);
if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE) {
- if (!wil->p2p.p2p_dev_started) {
+ if (!wil->p2p_dev_started) {
wil_err(wil, "P2P search requested on stopped P2P device\n");
rc = -EIO;
goto out;
}
/* social scan on P2P_DEVICE is handled as p2p search */
if (wil_p2p_is_social_scan(request)) {
- wil->scan_request = request;
- wil->radio_wdev = wdev;
- rc = wil_p2p_search(wil, request);
+ vif->scan_request = request;
+ if (vif->mid == 0)
+ wil->radio_wdev = wdev;
+ rc = wil_p2p_search(vif, request);
if (rc) {
- wil->radio_wdev = wil_to_wdev(wil);
- wil->scan_request = NULL;
+ if (vif->mid == 0)
+ wil->radio_wdev =
+ wil->main_ndev->ieee80211_ptr;
+ vif->scan_request = NULL;
}
goto out;
}
}
- (void)wil_p2p_stop_discovery(wil);
+ (void)wil_p2p_stop_discovery(vif);
wil_dbg_misc(wil, "Start scan_request 0x%p\n", request);
wil_dbg_misc(wil, "SSID count: %d", request->n_ssids);
@@ -578,18 +749,18 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
}
if (request->n_ssids)
- rc = wmi_set_ssid(wil, request->ssids[0].ssid_len,
+ rc = wmi_set_ssid(vif, request->ssids[0].ssid_len,
request->ssids[0].ssid);
else
- rc = wmi_set_ssid(wil, 0, NULL);
+ rc = wmi_set_ssid(vif, 0, NULL);
if (rc) {
wil_err(wil, "set SSID for scan request failed: %d\n", rc);
goto out;
}
- wil->scan_request = request;
- mod_timer(&wil->scan_timer, jiffies + WIL6210_SCAN_TO);
+ vif->scan_request = request;
+ mod_timer(&vif->scan_timer, jiffies + WIL6210_SCAN_TO);
memset(&cmd, 0, sizeof(cmd));
cmd.cmd.scan_type = WMI_ACTIVE_SCAN;
@@ -616,7 +787,8 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
else
wil_dbg_misc(wil, "Scan has no IE's\n");
- rc = wmi_set_ie(wil, WMI_FRAME_PROBE_REQ, request->ie_len, request->ie);
+ rc = wmi_set_ie(vif, WMI_FRAME_PROBE_REQ,
+ request->ie_len, request->ie);
if (rc)
goto out_restore;
@@ -625,15 +797,18 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
wil_dbg_misc(wil, "active scan with discovery_mode=1\n");
}
- wil->radio_wdev = wdev;
- rc = wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) +
- cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0]));
+ if (vif->mid == 0)
+ wil->radio_wdev = wdev;
+ rc = wmi_send(wil, WMI_START_SCAN_CMDID, vif->mid,
+ &cmd, sizeof(cmd.cmd) +
+ cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0]));
out_restore:
if (rc) {
- del_timer_sync(&wil->scan_timer);
- wil->radio_wdev = wil_to_wdev(wil);
- wil->scan_request = NULL;
+ del_timer_sync(&vif->scan_timer);
+ if (vif->mid == 0)
+ wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
+ vif->scan_request = NULL;
}
out:
mutex_unlock(&wil->mutex);
@@ -644,27 +819,28 @@ static void wil_cfg80211_abort_scan(struct wiphy *wiphy,
struct wireless_dev *wdev)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
wil_dbg_misc(wil, "wdev=0x%p iftype=%d\n", wdev, wdev->iftype);
mutex_lock(&wil->mutex);
- mutex_lock(&wil->p2p_wdev_mutex);
+ mutex_lock(&wil->vif_mutex);
- if (!wil->scan_request)
+ if (!vif->scan_request)
goto out;
- if (wdev != wil->scan_request->wdev) {
+ if (wdev != vif->scan_request->wdev) {
wil_dbg_misc(wil, "abort scan was called on the wrong iface\n");
goto out;
}
- if (wil->radio_wdev == wil->p2p_wdev)
+ if (wdev == wil->p2p_wdev && wil->radio_wdev == wil->p2p_wdev)
wil_p2p_stop_radio_operations(wil);
else
- wil_abort_scan(wil, true);
+ wil_abort_scan(vif, true);
out:
- mutex_unlock(&wil->p2p_wdev_mutex);
+ mutex_unlock(&wil->vif_mutex);
mutex_unlock(&wil->mutex);
}
@@ -715,6 +891,7 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
struct cfg80211_connect_params *sme)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ struct wil6210_vif *vif = ndev_to_vif(ndev);
struct cfg80211_bss *bss;
struct wmi_connect_cmd conn;
const u8 *ssid_eid;
@@ -723,11 +900,11 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
int rc = 0;
enum ieee80211_bss_type bss_type = IEEE80211_BSS_TYPE_ESS;
- wil_dbg_misc(wil, "connect\n");
+ wil_dbg_misc(wil, "connect, mid=%d\n", vif->mid);
wil_print_connect_params(wil, sme);
- if (test_bit(wil_status_fwconnecting, wil->status) ||
- test_bit(wil_status_fwconnected, wil->status))
+ if (test_bit(wil_vif_fwconnecting, vif->status) ||
+ test_bit(wil_vif_fwconnected, vif->status))
return -EALREADY;
if (sme->ie_len > WMI_MAX_IE_LEN) {
@@ -758,18 +935,18 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
rc = -ENOENT;
goto out;
}
- wil->privacy = sme->privacy;
- wil->pbss = sme->pbss;
+ vif->privacy = sme->privacy;
+ vif->pbss = sme->pbss;
- if (wil->privacy) {
+ if (vif->privacy) {
/* For secure assoc, remove old keys */
- rc = wmi_del_cipher_key(wil, 0, bss->bssid,
+ rc = wmi_del_cipher_key(vif, 0, bss->bssid,
WMI_KEY_USE_PAIRWISE);
if (rc) {
wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD(PTK) failed\n");
goto out;
}
- rc = wmi_del_cipher_key(wil, 0, bss->bssid,
+ rc = wmi_del_cipher_key(vif, 0, bss->bssid,
WMI_KEY_USE_RX_GROUP);
if (rc) {
wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD(GTK) failed\n");
@@ -781,7 +958,7 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
* elements. Send it also in case it's empty, to erase previously set
* ies in FW.
*/
- rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_REQ, sme->ie_len, sme->ie);
+ rc = wmi_set_ie(vif, WMI_FRAME_ASSOC_REQ, sme->ie_len, sme->ie);
if (rc)
goto out;
@@ -799,7 +976,7 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
bss->capability);
goto out;
}
- if (wil->privacy) {
+ if (vif->privacy) {
if (rsn_eid) { /* regular secure connection */
conn.dot11_auth_mode = WMI_AUTH11_SHARED;
conn.auth_mode = WMI_AUTH_WPA2_PSK;
@@ -831,18 +1008,19 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
ether_addr_copy(conn.bssid, bss->bssid);
ether_addr_copy(conn.dst_mac, bss->bssid);
- set_bit(wil_status_fwconnecting, wil->status);
+ set_bit(wil_vif_fwconnecting, vif->status);
- rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn));
+ rc = wmi_send(wil, WMI_CONNECT_CMDID, vif->mid, &conn, sizeof(conn));
if (rc == 0) {
netif_carrier_on(ndev);
- wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS);
- wil->bss = bss;
+ if (!wil_has_other_active_ifaces(wil, ndev, false, true))
+ wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS);
+ vif->bss = bss;
/* Connect can take lots of time */
- mod_timer(&wil->connect_timer,
+ mod_timer(&vif->connect_timer,
jiffies + msecs_to_jiffies(5000));
} else {
- clear_bit(wil_status_fwconnecting, wil->status);
+ clear_bit(wil_vif_fwconnecting, vif->status);
}
out:
@@ -857,17 +1035,19 @@ static int wil_cfg80211_disconnect(struct wiphy *wiphy,
{
int rc;
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ struct wil6210_vif *vif = ndev_to_vif(ndev);
- wil_dbg_misc(wil, "disconnect: reason=%d\n", reason_code);
+ wil_dbg_misc(wil, "disconnect: reason=%d, mid=%d\n",
+ reason_code, vif->mid);
- if (!(test_bit(wil_status_fwconnecting, wil->status) ||
- test_bit(wil_status_fwconnected, wil->status))) {
+ if (!(test_bit(wil_vif_fwconnecting, vif->status) ||
+ test_bit(wil_vif_fwconnected, vif->status))) {
wil_err(wil, "Disconnect was called while disconnected\n");
return 0;
}
- wil->locally_generated_disc = true;
- rc = wmi_call(wil, WMI_DISCONNECT_CMDID, NULL, 0,
+ vif->locally_generated_disc = true;
+ rc = wmi_call(wil, WMI_DISCONNECT_CMDID, vif->mid, NULL, 0,
WMI_DISCONNECT_EVENTID, NULL, 0,
WIL6210_DISCONNECT_TO_MS);
if (rc)
@@ -903,6 +1083,7 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
const u8 *buf = params->buf;
size_t len = params->len, total;
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
int rc;
bool tx_status = false;
struct ieee80211_mgmt *mgmt_frame = (void *)buf;
@@ -919,7 +1100,7 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
* different from currently "listened" channel and fail if it is.
*/
- wil_dbg_misc(wil, "mgmt_tx\n");
+ wil_dbg_misc(wil, "mgmt_tx mid %d\n", vif->mid);
wil_hex_dump_misc("mgmt tx frame ", DUMP_PREFIX_OFFSET, 16, 1, buf,
len, true);
@@ -940,7 +1121,7 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
cmd->len = cpu_to_le16(len);
memcpy(cmd->payload, buf, len);
- rc = wmi_call(wil, WMI_SW_TX_REQ_CMDID, cmd, total,
+ rc = wmi_call(wil, WMI_SW_TX_REQ_CMDID, vif->mid, cmd, total,
WMI_SW_TX_COMPLETE_EVENTID, &evt, sizeof(evt), 2000);
if (rc == 0)
tx_status = !evt.evt.status;
@@ -962,10 +1143,10 @@ static int wil_cfg80211_set_channel(struct wiphy *wiphy,
return 0;
}
-static enum wmi_key_usage wil_detect_key_usage(struct wil6210_priv *wil,
+static enum wmi_key_usage wil_detect_key_usage(struct wireless_dev *wdev,
bool pairwise)
{
- struct wireless_dev *wdev = wil_to_wdev(wil);
+ struct wil6210_priv *wil = wdev_to_wil(wdev);
enum wmi_key_usage rc;
if (pairwise) {
@@ -993,7 +1174,7 @@ static enum wmi_key_usage wil_detect_key_usage(struct wil6210_priv *wil,
}
static struct wil_sta_info *
-wil_find_sta_by_key_usage(struct wil6210_priv *wil,
+wil_find_sta_by_key_usage(struct wil6210_priv *wil, u8 mid,
enum wmi_key_usage key_usage, const u8 *mac_addr)
{
int cid = -EINVAL;
@@ -1003,9 +1184,9 @@ wil_find_sta_by_key_usage(struct wil6210_priv *wil,
/* supplicant provides Rx group key in STA mode with NULL MAC address */
if (mac_addr)
- cid = wil_find_cid(wil, mac_addr);
+ cid = wil_find_cid(wil, mid, mac_addr);
else if (key_usage == WMI_KEY_USE_RX_GROUP)
- cid = wil_find_cid_by_idx(wil, 0);
+ cid = wil_find_cid_by_idx(wil, mid, 0);
if (cid < 0) {
wil_err(wil, "No CID for %pM %s\n", mac_addr,
key_usage_str[key_usage]);
@@ -1082,9 +1263,12 @@ static int wil_cfg80211_add_key(struct wiphy *wiphy,
struct key_params *params)
{
int rc;
+ struct wil6210_vif *vif = ndev_to_vif(ndev);
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
- enum wmi_key_usage key_usage = wil_detect_key_usage(wil, pairwise);
- struct wil_sta_info *cs = wil_find_sta_by_key_usage(wil, key_usage,
+ struct wireless_dev *wdev = vif_to_wdev(vif);
+ enum wmi_key_usage key_usage = wil_detect_key_usage(wdev, pairwise);
+ struct wil_sta_info *cs = wil_find_sta_by_key_usage(wil, vif->mid,
+ key_usage,
mac_addr);
if (!params) {
@@ -1114,7 +1298,7 @@ static int wil_cfg80211_add_key(struct wiphy *wiphy,
return -EINVAL;
}
- rc = wmi_add_cipher_key(wil, key_index, mac_addr, params->key_len,
+ rc = wmi_add_cipher_key(vif, key_index, mac_addr, params->key_len,
params->key, key_usage);
if (!rc)
wil_set_crypto_rx(key_index, key_usage, cs, params);
@@ -1127,9 +1311,12 @@ static int wil_cfg80211_del_key(struct wiphy *wiphy,
u8 key_index, bool pairwise,
const u8 *mac_addr)
{
+ struct wil6210_vif *vif = ndev_to_vif(ndev);
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
- enum wmi_key_usage key_usage = wil_detect_key_usage(wil, pairwise);
- struct wil_sta_info *cs = wil_find_sta_by_key_usage(wil, key_usage,
+ struct wireless_dev *wdev = vif_to_wdev(vif);
+ enum wmi_key_usage key_usage = wil_detect_key_usage(wdev, pairwise);
+ struct wil_sta_info *cs = wil_find_sta_by_key_usage(wil, vif->mid,
+ key_usage,
mac_addr);
wil_dbg_misc(wil, "del_key: %pM %s[%d]\n", mac_addr,
@@ -1142,7 +1329,7 @@ static int wil_cfg80211_del_key(struct wiphy *wiphy,
if (!IS_ERR_OR_NULL(cs))
wil_del_rx_key(key_index, key_usage, cs);
- return wmi_del_cipher_key(wil, key_index, mac_addr, key_usage);
+ return wmi_del_cipher_key(vif, key_index, mac_addr, key_usage);
}
/* Need to be present or wiphy_new() will WARN */
@@ -1179,10 +1366,11 @@ static int wil_cancel_remain_on_channel(struct wiphy *wiphy,
u64 cookie)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
wil_dbg_misc(wil, "cancel_remain_on_channel\n");
- return wil_p2p_cancel_listen(wil, cookie);
+ return wil_p2p_cancel_listen(vif, cookie);
}
/**
@@ -1275,11 +1463,10 @@ static void wil_print_bcon_data(struct cfg80211_beacon_data *b)
}
/* internal functions for device reset and starting AP */
-static int _wil_cfg80211_set_ies(struct wiphy *wiphy,
+static int _wil_cfg80211_set_ies(struct wil6210_vif *vif,
struct cfg80211_beacon_data *bcon)
{
int rc;
- struct wil6210_priv *wil = wiphy_to_wil(wiphy);
u16 len = 0, proberesp_len = 0;
u8 *ies = NULL, *proberesp = NULL;
@@ -1300,20 +1487,21 @@ static int _wil_cfg80211_set_ies(struct wiphy *wiphy,
if (rc)
goto out;
- rc = wmi_set_ie(wil, WMI_FRAME_PROBE_RESP, len, ies);
+ rc = wmi_set_ie(vif, WMI_FRAME_PROBE_RESP, len, ies);
if (rc)
goto out;
if (bcon->assocresp_ies)
- rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP,
+ rc = wmi_set_ie(vif, WMI_FRAME_ASSOC_RESP,
bcon->assocresp_ies_len, bcon->assocresp_ies);
else
- rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP, len, ies);
+ rc = wmi_set_ie(vif, WMI_FRAME_ASSOC_RESP, len, ies);
#if 0 /* to use beacon IE's, remove this #if 0 */
if (rc)
goto out;
- rc = wmi_set_ie(wil, WMI_FRAME_BEACON, bcon->tail_len, bcon->tail);
+ rc = wmi_set_ie(vif, WMI_FRAME_BEACON,
+ bcon->tail_len, bcon->tail);
#endif
out:
kfree(ies);
@@ -1328,6 +1516,7 @@ static int _wil_cfg80211_start_ap(struct wiphy *wiphy,
u8 hidden_ssid, u32 pbss)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ struct wil6210_vif *vif = ndev_to_vif(ndev);
int rc;
struct wireless_dev *wdev = ndev->ieee80211_ptr;
u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype);
@@ -1336,7 +1525,7 @@ static int _wil_cfg80211_start_ap(struct wiphy *wiphy,
if (pbss)
wmi_nettype = WMI_NETTYPE_P2P;
- wil_dbg_misc(wil, "start_ap: is_go=%d\n", is_go);
+ wil_dbg_misc(wil, "start_ap: mid=%d, is_go=%d\n", vif->mid, is_go);
if (is_go && !pbss) {
wil_err(wil, "P2P GO must be in PBSS\n");
return -ENOTSUPP;
@@ -1346,42 +1535,46 @@ static int _wil_cfg80211_start_ap(struct wiphy *wiphy,
mutex_lock(&wil->mutex);
- __wil_down(wil);
- rc = __wil_up(wil);
- if (rc)
- goto out;
+ if (!wil_has_other_active_ifaces(wil, ndev, true, false)) {
+ __wil_down(wil);
+ rc = __wil_up(wil);
+ if (rc)
+ goto out;
+ }
- rc = wmi_set_ssid(wil, ssid_len, ssid);
+ rc = wmi_set_ssid(vif, ssid_len, ssid);
if (rc)
goto out;
- rc = _wil_cfg80211_set_ies(wiphy, bcon);
+ rc = _wil_cfg80211_set_ies(vif, bcon);
if (rc)
goto out;
- wil->privacy = privacy;
- wil->channel = chan;
- wil->hidden_ssid = hidden_ssid;
- wil->pbss = pbss;
+ vif->privacy = privacy;
+ vif->channel = chan;
+ vif->hidden_ssid = hidden_ssid;
+ vif->pbss = pbss;
netif_carrier_on(ndev);
- wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS);
+ if (!wil_has_other_active_ifaces(wil, ndev, false, true))
+ wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS);
- rc = wmi_pcp_start(wil, bi, wmi_nettype, chan, hidden_ssid, is_go);
+ rc = wmi_pcp_start(vif, bi, wmi_nettype, chan, hidden_ssid, is_go);
if (rc)
goto err_pcp_start;
- rc = wil_bcast_init(wil);
+ rc = wil_bcast_init(vif);
if (rc)
goto err_bcast;
goto out; /* success */
err_bcast:
- wmi_pcp_stop(wil);
+ wmi_pcp_stop(vif);
err_pcp_start:
netif_carrier_off(ndev);
- wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS);
+ if (!wil_has_other_active_ifaces(wil, ndev, false, true))
+ wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS);
out:
mutex_unlock(&wil->mutex);
return rc;
@@ -1392,10 +1585,11 @@ static int wil_cfg80211_change_beacon(struct wiphy *wiphy,
struct cfg80211_beacon_data *bcon)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ struct wil6210_vif *vif = ndev_to_vif(ndev);
int rc;
u32 privacy = 0;
- wil_dbg_misc(wil, "change_beacon\n");
+ wil_dbg_misc(wil, "change_beacon, mid=%d\n", vif->mid);
wil_print_bcon_data(bcon);
if (bcon->tail &&
@@ -1404,20 +1598,20 @@ static int wil_cfg80211_change_beacon(struct wiphy *wiphy,
privacy = 1;
/* in case privacy has changed, need to restart the AP */
- if (wil->privacy != privacy) {
+ if (vif->privacy != privacy) {
struct wireless_dev *wdev = ndev->ieee80211_ptr;
wil_dbg_misc(wil, "privacy changed %d=>%d. Restarting AP\n",
- wil->privacy, privacy);
+ vif->privacy, privacy);
rc = _wil_cfg80211_start_ap(wiphy, ndev, wdev->ssid,
wdev->ssid_len, privacy,
wdev->beacon_interval,
- wil->channel, bcon,
- wil->hidden_ssid,
- wil->pbss);
+ vif->channel, bcon,
+ vif->hidden_ssid,
+ vif->pbss);
} else {
- rc = _wil_cfg80211_set_ies(wiphy, bcon);
+ rc = _wil_cfg80211_set_ies(vif, bcon);
}
return rc;
@@ -1484,20 +1678,27 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
struct net_device *ndev)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ struct wil6210_vif *vif = ndev_to_vif(ndev);
+ bool last;
- wil_dbg_misc(wil, "stop_ap\n");
+ wil_dbg_misc(wil, "stop_ap, mid=%d\n", vif->mid);
netif_carrier_off(ndev);
- wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS);
- wil_set_recovery_state(wil, fw_recovery_idle);
-
- set_bit(wil_status_resetting, wil->status);
+ last = !wil_has_other_active_ifaces(wil, ndev, false, true);
+ if (last) {
+ wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS);
+ wil_set_recovery_state(wil, fw_recovery_idle);
+ set_bit(wil_status_resetting, wil->status);
+ }
mutex_lock(&wil->mutex);
- wmi_pcp_stop(wil);
+ wmi_pcp_stop(vif);
- __wil_down(wil);
+ if (last)
+ __wil_down(wil);
+ else
+ wil_bcast_fini(vif);
mutex_unlock(&wil->mutex);
@@ -1509,9 +1710,11 @@ static int wil_cfg80211_add_station(struct wiphy *wiphy,
const u8 *mac,
struct station_parameters *params)
{
+ struct wil6210_vif *vif = ndev_to_vif(dev);
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
- wil_dbg_misc(wil, "add station %pM aid %d\n", mac, params->aid);
+ wil_dbg_misc(wil, "add station %pM aid %d mid %d\n",
+ mac, params->aid, vif->mid);
if (!disable_ap_sme) {
wil_err(wil, "not supported with AP SME enabled\n");
@@ -1523,20 +1726,21 @@ static int wil_cfg80211_add_station(struct wiphy *wiphy,
return -EINVAL;
}
- return wmi_new_sta(wil, mac, params->aid);
+ return wmi_new_sta(vif, mac, params->aid);
}
static int wil_cfg80211_del_station(struct wiphy *wiphy,
struct net_device *dev,
struct station_del_parameters *params)
{
+ struct wil6210_vif *vif = ndev_to_vif(dev);
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
- wil_dbg_misc(wil, "del_station: %pM, reason=%d\n", params->mac,
- params->reason_code);
+ wil_dbg_misc(wil, "del_station: %pM, reason=%d mid=%d\n",
+ params->mac, params->reason_code, vif->mid);
mutex_lock(&wil->mutex);
- wil6210_disconnect(wil, params->mac, params->reason_code, false);
+ wil6210_disconnect(vif, params->mac, params->reason_code, false);
mutex_unlock(&wil->mutex);
return 0;
@@ -1547,13 +1751,15 @@ static int wil_cfg80211_change_station(struct wiphy *wiphy,
const u8 *mac,
struct station_parameters *params)
{
+ struct wil6210_vif *vif = ndev_to_vif(dev);
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
int authorize;
int cid, i;
struct vring_tx_data *txdata = NULL;
- wil_dbg_misc(wil, "change station %pM mask 0x%x set 0x%x\n", mac,
- params->sta_flags_mask, params->sta_flags_set);
+ wil_dbg_misc(wil, "change station %pM mask 0x%x set 0x%x mid %d\n",
+ mac, params->sta_flags_mask, params->sta_flags_set,
+ vif->mid);
if (!disable_ap_sme) {
wil_dbg_misc(wil, "not supported with AP SME enabled\n");
@@ -1563,7 +1769,7 @@ static int wil_cfg80211_change_station(struct wiphy *wiphy,
if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
return 0;
- cid = wil_find_cid(wil, mac);
+ cid = wil_find_cid(wil, vif->mid, mac);
if (cid < 0) {
wil_err(wil, "station not found\n");
return -ENOLINK;
@@ -1590,63 +1796,67 @@ static int wil_cfg80211_change_station(struct wiphy *wiphy,
/* probe_client handling */
static void wil_probe_client_handle(struct wil6210_priv *wil,
+ struct wil6210_vif *vif,
struct wil_probe_client_req *req)
{
- struct net_device *ndev = wil_to_ndev(wil);
+ struct net_device *ndev = vif_to_ndev(vif);
struct wil_sta_info *sta = &wil->sta[req->cid];
/* assume STA is alive if it is still connected,
* else FW will disconnect it
*/
bool alive = (sta->status == wil_sta_connected);
- cfg80211_probe_status(ndev, sta->addr, req->cookie, alive, GFP_KERNEL);
+ cfg80211_probe_status(ndev, sta->addr, req->cookie, alive,
+ 0, false, GFP_KERNEL);
}
-static struct list_head *next_probe_client(struct wil6210_priv *wil)
+static struct list_head *next_probe_client(struct wil6210_vif *vif)
{
struct list_head *ret = NULL;
- mutex_lock(&wil->probe_client_mutex);
+ mutex_lock(&vif->probe_client_mutex);
- if (!list_empty(&wil->probe_client_pending)) {
- ret = wil->probe_client_pending.next;
+ if (!list_empty(&vif->probe_client_pending)) {
+ ret = vif->probe_client_pending.next;
list_del(ret);
}
- mutex_unlock(&wil->probe_client_mutex);
+ mutex_unlock(&vif->probe_client_mutex);
return ret;
}
void wil_probe_client_worker(struct work_struct *work)
{
- struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
- probe_client_worker);
+ struct wil6210_vif *vif = container_of(work, struct wil6210_vif,
+ probe_client_worker);
+ struct wil6210_priv *wil = vif_to_wil(vif);
struct wil_probe_client_req *req;
struct list_head *lh;
- while ((lh = next_probe_client(wil)) != NULL) {
+ while ((lh = next_probe_client(vif)) != NULL) {
req = list_entry(lh, struct wil_probe_client_req, list);
- wil_probe_client_handle(wil, req);
+ wil_probe_client_handle(wil, vif, req);
kfree(req);
}
}
-void wil_probe_client_flush(struct wil6210_priv *wil)
+void wil_probe_client_flush(struct wil6210_vif *vif)
{
struct wil_probe_client_req *req, *t;
+ struct wil6210_priv *wil = vif_to_wil(vif);
wil_dbg_misc(wil, "probe_client_flush\n");
- mutex_lock(&wil->probe_client_mutex);
+ mutex_lock(&vif->probe_client_mutex);
- list_for_each_entry_safe(req, t, &wil->probe_client_pending, list) {
+ list_for_each_entry_safe(req, t, &vif->probe_client_pending, list) {
list_del(&req->list);
kfree(req);
}
- mutex_unlock(&wil->probe_client_mutex);
+ mutex_unlock(&vif->probe_client_mutex);
}
static int wil_cfg80211_probe_client(struct wiphy *wiphy,
@@ -1654,10 +1864,12 @@ static int wil_cfg80211_probe_client(struct wiphy *wiphy,
const u8 *peer, u64 *cookie)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ struct wil6210_vif *vif = ndev_to_vif(dev);
struct wil_probe_client_req *req;
- int cid = wil_find_cid(wil, peer);
+ int cid = wil_find_cid(wil, vif->mid, peer);
- wil_dbg_misc(wil, "probe_client: %pM => CID %d\n", peer, cid);
+ wil_dbg_misc(wil, "probe_client: %pM => CID %d MID %d\n",
+ peer, cid, vif->mid);
if (cid < 0)
return -ENOLINK;
@@ -1669,12 +1881,12 @@ static int wil_cfg80211_probe_client(struct wiphy *wiphy,
req->cid = cid;
req->cookie = cid;
- mutex_lock(&wil->probe_client_mutex);
- list_add_tail(&req->list, &wil->probe_client_pending);
- mutex_unlock(&wil->probe_client_mutex);
+ mutex_lock(&vif->probe_client_mutex);
+ list_add_tail(&req->list, &vif->probe_client_pending);
+ mutex_unlock(&vif->probe_client_mutex);
*cookie = req->cookie;
- queue_work(wil->wq_service, &wil->probe_client_worker);
+ queue_work(wil->wq_service, &vif->probe_client_worker);
return 0;
}
@@ -1683,11 +1895,12 @@ static int wil_cfg80211_change_bss(struct wiphy *wiphy,
struct bss_parameters *params)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ struct wil6210_vif *vif = ndev_to_vif(dev);
if (params->ap_isolate >= 0) {
- wil_dbg_misc(wil, "change_bss: ap_isolate %d => %d\n",
- wil->ap_isolate, params->ap_isolate);
- wil->ap_isolate = params->ap_isolate;
+ wil_dbg_misc(wil, "change_bss: ap_isolate MID %d, %d => %d\n",
+ vif->mid, vif->ap_isolate, params->ap_isolate);
+ vif->ap_isolate = params->ap_isolate;
}
return 0;
@@ -1731,10 +1944,10 @@ static int wil_cfg80211_suspend(struct wiphy *wiphy,
wil_dbg_pm(wil, "suspending\n");
mutex_lock(&wil->mutex);
- mutex_lock(&wil->p2p_wdev_mutex);
+ mutex_lock(&wil->vif_mutex);
wil_p2p_stop_radio_operations(wil);
- wil_abort_scan(wil, true);
- mutex_unlock(&wil->p2p_wdev_mutex);
+ wil_abort_scan_all_vifs(wil, true);
+ mutex_unlock(&wil->vif_mutex);
mutex_unlock(&wil->mutex);
out:
@@ -1756,8 +1969,12 @@ wil_cfg80211_sched_scan_start(struct wiphy *wiphy,
struct cfg80211_sched_scan_request *request)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ struct wil6210_vif *vif = ndev_to_vif(dev);
int i, rc;
+ if (vif->mid != 0)
+ return -EOPNOTSUPP;
+
wil_dbg_misc(wil,
"sched scan start: n_ssids %d, ie_len %zu, flags 0x%x\n",
request->n_ssids, request->ie_len, request->flags);
@@ -1791,7 +2008,8 @@ wil_cfg80211_sched_scan_start(struct wiphy *wiphy,
i, sp->interval, sp->iterations);
}
- rc = wmi_set_ie(wil, WMI_FRAME_PROBE_REQ, request->ie_len, request->ie);
+ rc = wmi_set_ie(vif, WMI_FRAME_PROBE_REQ,
+ request->ie_len, request->ie);
if (rc)
return rc;
return wmi_start_sched_scan(wil, request);
@@ -1802,8 +2020,12 @@ wil_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev,
u64 reqid)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ struct wil6210_vif *vif = ndev_to_vif(dev);
int rc;
+ if (vif->mid != 0)
+ return -EOPNOTSUPP;
+
rc = wmi_stop_sched_scan(wil);
/* device would return error if it thinks PNO is already stopped.
* ignore the return code so user space and driver gets back in-sync
@@ -1892,57 +2114,132 @@ static void wil_wiphy_init(struct wiphy *wiphy)
#endif
}
-struct wireless_dev *wil_cfg80211_init(struct device *dev)
+int wil_cfg80211_iface_combinations_from_fw(
+ struct wil6210_priv *wil, const struct wil_fw_record_concurrency *conc)
{
- int rc = 0;
- struct wireless_dev *wdev;
+ struct wiphy *wiphy = wil_to_wiphy(wil);
+ u32 total_limits = 0;
+ u16 n_combos;
+ const struct wil_fw_concurrency_combo *combo;
+ const struct wil_fw_concurrency_limit *limit;
+ struct ieee80211_iface_combination *iface_combinations;
+ struct ieee80211_iface_limit *iface_limit;
+ int i, j;
+
+ if (wiphy->iface_combinations) {
+ wil_dbg_misc(wil, "iface_combinations already set, skipping\n");
+ return 0;
+ }
- dev_dbg(dev, "%s()\n", __func__);
+ combo = conc->combos;
+ n_combos = le16_to_cpu(conc->n_combos);
+ for (i = 0; i < n_combos; i++) {
+ total_limits += combo->n_limits;
+ limit = combo->limits + combo->n_limits;
+ combo = (struct wil_fw_concurrency_combo *)limit;
+ }
- wdev = kzalloc(sizeof(*wdev), GFP_KERNEL);
- if (!wdev)
- return ERR_PTR(-ENOMEM);
+ iface_combinations =
+ kzalloc(n_combos * sizeof(struct ieee80211_iface_combination) +
+ total_limits * sizeof(struct ieee80211_iface_limit),
+ GFP_KERNEL);
+ if (!iface_combinations)
+ return -ENOMEM;
+ iface_limit = (struct ieee80211_iface_limit *)(iface_combinations +
+ n_combos);
+ combo = conc->combos;
+ for (i = 0; i < n_combos; i++) {
+ iface_combinations[i].max_interfaces = combo->max_interfaces;
+ iface_combinations[i].num_different_channels =
+ combo->n_diff_channels;
+ iface_combinations[i].beacon_int_infra_match =
+ combo->same_bi;
+ iface_combinations[i].n_limits = combo->n_limits;
+ wil_dbg_misc(wil,
+ "iface_combination %d: max_if %d, num_ch %d, bi_match %d\n",
+ i, iface_combinations[i].max_interfaces,
+ iface_combinations[i].num_different_channels,
+ iface_combinations[i].beacon_int_infra_match);
+ limit = combo->limits;
+ for (j = 0; j < combo->n_limits; j++) {
+ iface_limit[j].max = le16_to_cpu(limit[j].max);
+ iface_limit[j].types = le16_to_cpu(limit[j].types);
+ wil_dbg_misc(wil,
+ "limit %d: max %d types 0x%x\n", j,
+ iface_limit[j].max, iface_limit[j].types);
+ }
+ iface_combinations[i].limits = iface_limit;
+ iface_limit += combo->n_limits;
+ limit += combo->n_limits;
+ combo = (struct wil_fw_concurrency_combo *)limit;
+ }
- wdev->wiphy = wiphy_new(&wil_cfg80211_ops,
- sizeof(struct wil6210_priv));
- if (!wdev->wiphy) {
- rc = -ENOMEM;
- goto out;
+ wil_dbg_misc(wil, "multiple VIFs supported, n_mids %d\n", conc->n_mids);
+ wil->max_vifs = conc->n_mids + 1; /* including main interface */
+ if (wil->max_vifs > WIL_MAX_VIFS) {
+ wil_info(wil, "limited number of VIFs supported(%d, FW %d)\n",
+ WIL_MAX_VIFS, wil->max_vifs);
+ wil->max_vifs = WIL_MAX_VIFS;
}
+ wiphy->n_iface_combinations = n_combos;
+ wiphy->iface_combinations = iface_combinations;
+ return 0;
+}
- set_wiphy_dev(wdev->wiphy, dev);
- wil_wiphy_init(wdev->wiphy);
+struct wil6210_priv *wil_cfg80211_init(struct device *dev)
+{
+ struct wiphy *wiphy;
+ struct wil6210_priv *wil;
+ struct ieee80211_channel *ch;
- return wdev;
+ dev_dbg(dev, "%s()\n", __func__);
-out:
- kfree(wdev);
+ /* Note: the wireless_dev structure is no longer allocated here.
+ * Instead, it is allocated as part of the net_device structure
+ * for main interface and each VIF.
+ */
+ wiphy = wiphy_new(&wil_cfg80211_ops, sizeof(struct wil6210_priv));
+ if (!wiphy)
+ return ERR_PTR(-ENOMEM);
- return ERR_PTR(rc);
+ set_wiphy_dev(wiphy, dev);
+ wil_wiphy_init(wiphy);
+
+ wil = wiphy_to_wil(wiphy);
+ wil->wiphy = wiphy;
+
+ /* default monitor channel */
+ ch = wiphy->bands[NL80211_BAND_60GHZ]->channels;
+ cfg80211_chandef_create(&wil->monitor_chandef, ch, NL80211_CHAN_NO_HT);
+
+ return wil;
}
-void wil_wdev_free(struct wil6210_priv *wil)
+void wil_cfg80211_deinit(struct wil6210_priv *wil)
{
- struct wireless_dev *wdev = wil_to_wdev(wil);
+ struct wiphy *wiphy = wil_to_wiphy(wil);
dev_dbg(wil_to_dev(wil), "%s()\n", __func__);
- if (!wdev)
+ if (!wiphy)
return;
- wiphy_free(wdev->wiphy);
- kfree(wdev);
+ kfree(wiphy->iface_combinations);
+ wiphy->iface_combinations = NULL;
+
+ wiphy_free(wiphy);
+ /* do not access wil6210_priv after returning from here */
}
void wil_p2p_wdev_free(struct wil6210_priv *wil)
{
struct wireless_dev *p2p_wdev;
- mutex_lock(&wil->p2p_wdev_mutex);
+ mutex_lock(&wil->vif_mutex);
p2p_wdev = wil->p2p_wdev;
wil->p2p_wdev = NULL;
- wil->radio_wdev = wil_to_wdev(wil);
- mutex_unlock(&wil->p2p_wdev_mutex);
+ wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
+ mutex_unlock(&wil->vif_mutex);
if (p2p_wdev) {
cfg80211_unregister_wdev(p2p_wdev);
kfree(p2p_wdev);
@@ -1970,6 +2267,7 @@ static int wil_rf_sector_get_cfg(struct wiphy *wiphy,
const void *data, int data_len)
{
struct wil6210_priv *wil = wdev_to_wil(wdev);
+ struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
int rc;
struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
u16 sector_index;
@@ -2026,8 +2324,8 @@ static int wil_rf_sector_get_cfg(struct wiphy *wiphy,
cmd.sector_type = sector_type;
cmd.rf_modules_vec = rf_modules_vec & 0xFF;
memset(&reply, 0, sizeof(reply));
- rc = wmi_call(wil, WMI_GET_RF_SECTOR_PARAMS_CMDID, &cmd, sizeof(cmd),
- WMI_GET_RF_SECTOR_PARAMS_DONE_EVENTID,
+ rc = wmi_call(wil, WMI_GET_RF_SECTOR_PARAMS_CMDID, vif->mid,
+ &cmd, sizeof(cmd), WMI_GET_RF_SECTOR_PARAMS_DONE_EVENTID,
&reply, sizeof(reply),
500);
if (rc)
@@ -2089,6 +2387,7 @@ static int wil_rf_sector_set_cfg(struct wiphy *wiphy,
const void *data, int data_len)
{
struct wil6210_priv *wil = wdev_to_wil(wdev);
+ struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
int rc, tmp;
struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
struct nlattr *tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MAX + 1];
@@ -2183,8 +2482,8 @@ static int wil_rf_sector_set_cfg(struct wiphy *wiphy,
cmd.rf_modules_vec = rf_modules_vec & 0xFF;
memset(&reply, 0, sizeof(reply));
- rc = wmi_call(wil, WMI_SET_RF_SECTOR_PARAMS_CMDID, &cmd, sizeof(cmd),
- WMI_SET_RF_SECTOR_PARAMS_DONE_EVENTID,
+ rc = wmi_call(wil, WMI_SET_RF_SECTOR_PARAMS_CMDID, vif->mid,
+ &cmd, sizeof(cmd), WMI_SET_RF_SECTOR_PARAMS_DONE_EVENTID,
&reply, sizeof(reply),
500);
if (rc)
@@ -2197,6 +2496,7 @@ static int wil_rf_sector_get_selected(struct wiphy *wiphy,
const void *data, int data_len)
{
struct wil6210_priv *wil = wdev_to_wil(wdev);
+ struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
int rc;
struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
u8 sector_type, mac_addr[ETH_ALEN];
@@ -2230,13 +2530,13 @@ static int wil_rf_sector_get_selected(struct wiphy *wiphy,
if (tb[QCA_ATTR_MAC_ADDR]) {
ether_addr_copy(mac_addr, nla_data(tb[QCA_ATTR_MAC_ADDR]));
- cid = wil_find_cid(wil, mac_addr);
+ cid = wil_find_cid(wil, vif->mid, mac_addr);
if (cid < 0) {
wil_err(wil, "invalid MAC address %pM\n", mac_addr);
return -ENOENT;
}
} else {
- if (test_bit(wil_status_fwconnected, wil->status)) {
+ if (test_bit(wil_vif_fwconnected, vif->status)) {
wil_err(wil, "must specify MAC address when connected\n");
return -EINVAL;
}
@@ -2246,7 +2546,7 @@ static int wil_rf_sector_get_selected(struct wiphy *wiphy,
cmd.cid = (u8)cid;
cmd.sector_type = sector_type;
memset(&reply, 0, sizeof(reply));
- rc = wmi_call(wil, WMI_GET_SELECTED_RF_SECTOR_INDEX_CMDID,
+ rc = wmi_call(wil, WMI_GET_SELECTED_RF_SECTOR_INDEX_CMDID, vif->mid,
&cmd, sizeof(cmd),
WMI_GET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID,
&reply, sizeof(reply),
@@ -2279,7 +2579,7 @@ nla_put_failure:
}
static int wil_rf_sector_wmi_set_selected(struct wil6210_priv *wil,
- u16 sector_index,
+ u8 mid, u16 sector_index,
u8 sector_type, u8 cid)
{
struct wmi_set_selected_rf_sector_index_cmd cmd;
@@ -2294,7 +2594,7 @@ static int wil_rf_sector_wmi_set_selected(struct wil6210_priv *wil,
cmd.sector_type = sector_type;
cmd.cid = (u8)cid;
memset(&reply, 0, sizeof(reply));
- rc = wmi_call(wil, WMI_SET_SELECTED_RF_SECTOR_INDEX_CMDID,
+ rc = wmi_call(wil, WMI_SET_SELECTED_RF_SECTOR_INDEX_CMDID, mid,
&cmd, sizeof(cmd),
WMI_SET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID,
&reply, sizeof(reply),
@@ -2309,6 +2609,7 @@ static int wil_rf_sector_set_selected(struct wiphy *wiphy,
const void *data, int data_len)
{
struct wil6210_priv *wil = wdev_to_wil(wdev);
+ struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
int rc;
struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
u16 sector_index;
@@ -2348,7 +2649,7 @@ static int wil_rf_sector_set_selected(struct wiphy *wiphy,
if (tb[QCA_ATTR_MAC_ADDR]) {
ether_addr_copy(mac_addr, nla_data(tb[QCA_ATTR_MAC_ADDR]));
if (!is_broadcast_ether_addr(mac_addr)) {
- cid = wil_find_cid(wil, mac_addr);
+ cid = wil_find_cid(wil, vif->mid, mac_addr);
if (cid < 0) {
wil_err(wil, "invalid MAC address %pM\n",
mac_addr);
@@ -2362,7 +2663,7 @@ static int wil_rf_sector_set_selected(struct wiphy *wiphy,
cid = -1;
}
} else {
- if (test_bit(wil_status_fwconnected, wil->status)) {
+ if (test_bit(wil_vif_fwconnected, vif->status)) {
wil_err(wil, "must specify MAC address when connected\n");
return -EINVAL;
}
@@ -2370,17 +2671,20 @@ static int wil_rf_sector_set_selected(struct wiphy *wiphy,
}
if (cid >= 0) {
- rc = wil_rf_sector_wmi_set_selected(wil, sector_index,
+ rc = wil_rf_sector_wmi_set_selected(wil, vif->mid, sector_index,
sector_type, cid);
} else {
/* unlock all cids */
rc = wil_rf_sector_wmi_set_selected(
- wil, WMI_INVALID_RF_SECTOR_INDEX, sector_type,
- WIL_CID_ALL);
+ wil, vif->mid, WMI_INVALID_RF_SECTOR_INDEX,
+ sector_type, WIL_CID_ALL);
if (rc == -EINVAL) {
for (i = 0; i < WIL6210_MAX_CID; i++) {
+ if (wil->sta[i].mid != vif->mid)
+ continue;
rc = wil_rf_sector_wmi_set_selected(
- wil, WMI_INVALID_RF_SECTOR_INDEX,
+ wil, vif->mid,
+ WMI_INVALID_RF_SECTOR_INDEX,
sector_type, i);
/* the FW will silently ignore and return
* success for unused cid, so abort the loop
diff --git a/drivers/net/wireless/ath/wil6210/debug.c b/drivers/net/wireless/ath/wil6210/debug.c
index 217a4591bde4..a9befb971cc4 100644
--- a/drivers/net/wireless/ath/wil6210/debug.c
+++ b/drivers/net/wireless/ath/wil6210/debug.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2013,2016 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -25,7 +26,7 @@ void __wil_err(struct wil6210_priv *wil, const char *fmt, ...)
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
- netdev_err(wil_to_ndev(wil), "%pV", &vaf);
+ netdev_err(wil->main_ndev, "%pV", &vaf);
trace_wil6210_log_err(&vaf);
va_end(args);
}
@@ -41,7 +42,7 @@ void __wil_err_ratelimited(struct wil6210_priv *wil, const char *fmt, ...)
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
- netdev_err(wil_to_ndev(wil), "%pV", &vaf);
+ netdev_err(wil->main_ndev, "%pV", &vaf);
trace_wil6210_log_err(&vaf);
va_end(args);
}
@@ -57,7 +58,7 @@ void wil_dbg_ratelimited(const struct wil6210_priv *wil, const char *fmt, ...)
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
- netdev_dbg(wil_to_ndev(wil), "%pV", &vaf);
+ netdev_dbg(wil->main_ndev, "%pV", &vaf);
trace_wil6210_log_dbg(&vaf);
va_end(args);
}
@@ -70,7 +71,7 @@ void __wil_info(struct wil6210_priv *wil, const char *fmt, ...)
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
- netdev_info(wil_to_ndev(wil), "%pV", &vaf);
+ netdev_info(wil->main_ndev, "%pV", &vaf);
trace_wil6210_log_info(&vaf);
va_end(args);
}
diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c
index 4a4888246e8c..8c90b3111f0b 100644
--- a/drivers/net/wireless/ath/wil6210/debugfs.c
+++ b/drivers/net/wireless/ath/wil6210/debugfs.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -621,7 +622,7 @@ static ssize_t wil_write_file_reset(struct file *file, const char __user *buf,
size_t len, loff_t *ppos)
{
struct wil6210_priv *wil = file->private_data;
- struct net_device *ndev = wil_to_ndev(wil);
+ struct net_device *ndev = wil->main_ndev;
/**
* BUG:
@@ -716,27 +717,44 @@ static ssize_t wil_write_back(struct file *file, const char __user *buf,
if (rc < 2)
return -EINVAL;
- if (0 == strcmp(cmd, "add")) {
- if (rc < 3) {
- wil_err(wil, "BACK: add require at least 2 params\n");
+ if ((strcmp(cmd, "add") == 0) ||
+ (strcmp(cmd, "del_tx") == 0)) {
+ struct vring_tx_data *txdata;
+
+ if (p1 < 0 || p1 >= WIL6210_MAX_TX_RINGS) {
+ wil_err(wil, "BACK: invalid ring id %d\n", p1);
return -EINVAL;
}
- if (rc < 4)
- p3 = 0;
- wmi_addba(wil, p1, p2, p3);
- } else if (0 == strcmp(cmd, "del_tx")) {
- if (rc < 3)
- p2 = WLAN_REASON_QSTA_LEAVE_QBSS;
- wmi_delba_tx(wil, p1, p2);
- } else if (0 == strcmp(cmd, "del_rx")) {
+ txdata = &wil->vring_tx_data[p1];
+ if (strcmp(cmd, "add") == 0) {
+ if (rc < 3) {
+ wil_err(wil, "BACK: add require at least 2 params\n");
+ return -EINVAL;
+ }
+ if (rc < 4)
+ p3 = 0;
+ wmi_addba(wil, txdata->mid, p1, p2, p3);
+ } else {
+ if (rc < 3)
+ p2 = WLAN_REASON_QSTA_LEAVE_QBSS;
+ wmi_delba_tx(wil, txdata->mid, p1, p2);
+ }
+ } else if (strcmp(cmd, "del_rx") == 0) {
+ struct wil_sta_info *sta;
+
if (rc < 3) {
wil_err(wil,
"BACK: del_rx require at least 2 params\n");
return -EINVAL;
}
+ if (p1 < 0 || p1 >= WIL6210_MAX_CID) {
+ wil_err(wil, "BACK: invalid CID %d\n", p1);
+ return -EINVAL;
+ }
if (rc < 4)
p3 = WLAN_REASON_QSTA_LEAVE_QBSS;
- wmi_delba_rx(wil, mk_cidxtid(p1, p2), p3);
+ sta = &wil->sta[p1];
+ wmi_delba_rx(wil, sta->mid, mk_cidxtid(p1, p2), p3);
} else {
wil_err(wil, "BACK: Unrecognized command \"%s\"\n", cmd);
return -EINVAL;
@@ -855,7 +873,7 @@ static ssize_t wil_write_file_txmgmt(struct file *file, const char __user *buf,
{
struct wil6210_priv *wil = file->private_data;
struct wiphy *wiphy = wil_to_wiphy(wil);
- struct wireless_dev *wdev = wil_to_wdev(wil);
+ struct wireless_dev *wdev = wil->main_ndev->ieee80211_ptr;
struct cfg80211_mgmt_tx_params params;
int rc;
void *frame;
@@ -890,6 +908,7 @@ static ssize_t wil_write_file_wmi(struct file *file, const char __user *buf,
size_t len, loff_t *ppos)
{
struct wil6210_priv *wil = file->private_data;
+ struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
struct wmi_cmd_hdr *wmi;
void *cmd;
int cmdlen = len - sizeof(struct wmi_cmd_hdr);
@@ -912,7 +931,7 @@ static ssize_t wil_write_file_wmi(struct file *file, const char __user *buf,
cmd = (cmdlen > 0) ? &wmi[1] : NULL;
cmdid = le16_to_cpu(wmi->command_id);
- rc1 = wmi_send(wil, cmdid, cmd, cmdlen);
+ rc1 = wmi_send(wil, cmdid, vif->mid, cmd, cmdlen);
kfree(wmi);
wil_info(wil, "0x%04x[%d] -> %d\n", cmdid, cmdlen, rc1);
@@ -1050,6 +1069,7 @@ static int wil_bf_debugfs_show(struct seq_file *s, void *data)
int rc;
int i;
struct wil6210_priv *wil = s->private;
+ struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
struct wmi_notify_req_cmd cmd = {
.interval_usec = 0,
};
@@ -1062,7 +1082,8 @@ static int wil_bf_debugfs_show(struct seq_file *s, void *data)
u32 status;
cmd.cid = i;
- rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, &cmd, sizeof(cmd),
+ rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, vif->mid,
+ &cmd, sizeof(cmd),
WMI_NOTIFY_REQ_DONE_EVENTID, &reply,
sizeof(reply), 20);
/* if reply is all-0, ignore this CID */
@@ -1155,7 +1176,7 @@ static const struct file_operations fops_temp = {
static int wil_freq_debugfs_show(struct seq_file *s, void *data)
{
struct wil6210_priv *wil = s->private;
- struct wireless_dev *wdev = wil_to_wdev(wil);
+ struct wireless_dev *wdev = wil->main_ndev->ieee80211_ptr;
u16 freq = wdev->chandef.chan ? wdev->chandef.chan->center_freq : 0;
seq_printf(s, "Freq = %d\n", freq);
@@ -1185,6 +1206,8 @@ static int wil_link_debugfs_show(struct seq_file *s, void *data)
for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
struct wil_sta_info *p = &wil->sta[i];
char *status = "unknown";
+ struct wil6210_vif *vif;
+ u8 mid;
switch (p->status) {
case wil_sta_unused:
@@ -1197,16 +1220,24 @@ static int wil_link_debugfs_show(struct seq_file *s, void *data)
status = "connected";
break;
}
- seq_printf(s, "[%d] %pM %s\n", i, p->addr, status);
+ mid = (p->status != wil_sta_unused) ? p->mid : U8_MAX;
+ seq_printf(s, "[%d][MID %d] %pM %s\n",
+ i, mid, p->addr, status);
- if (p->status == wil_sta_connected) {
- rc = wil_cid_fill_sinfo(wil, i, &sinfo);
+ if (p->status != wil_sta_connected)
+ continue;
+
+ vif = (mid < wil->max_vifs) ? wil->vifs[mid] : NULL;
+ if (vif) {
+ rc = wil_cid_fill_sinfo(vif, i, &sinfo);
if (rc)
return rc;
seq_printf(s, " Tx_mcs = %d\n", sinfo.txrate.mcs);
seq_printf(s, " Rx_mcs = %d\n", sinfo.rxrate.mcs);
seq_printf(s, " SQ = %d\n", sinfo.signal);
+ } else {
+ seq_puts(s, " INVALID MID\n");
}
}
@@ -1229,7 +1260,7 @@ static const struct file_operations fops_link = {
static int wil_info_debugfs_show(struct seq_file *s, void *data)
{
struct wil6210_priv *wil = s->private;
- struct net_device *ndev = wil_to_ndev(wil);
+ struct net_device *ndev = wil->main_ndev;
int is_ac = power_supply_is_system_supplied();
int rx = atomic_xchg(&wil->isr_count_rx, 0);
int tx = atomic_xchg(&wil->isr_count_tx, 0);
@@ -1398,6 +1429,7 @@ __acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock)
struct wil_sta_info *p = &wil->sta[i];
char *status = "unknown";
u8 aid = 0;
+ u8 mid;
switch (p->status) {
case wil_sta_unused:
@@ -1411,7 +1443,9 @@ __acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock)
aid = p->aid;
break;
}
- seq_printf(s, "[%d] %pM %s AID %d\n", i, p->addr, status, aid);
+ mid = (p->status != wil_sta_unused) ? p->mid : U8_MAX;
+ seq_printf(s, "[%d] %pM %s MID %d AID %d\n", i, p->addr, status,
+ mid, aid);
if (p->status == wil_sta_connected) {
spin_lock_bh(&p->tid_rx_lock);
@@ -1461,6 +1495,42 @@ static const struct file_operations fops_sta = {
.llseek = seq_lseek,
};
+static int wil_mids_debugfs_show(struct seq_file *s, void *data)
+{
+ struct wil6210_priv *wil = s->private;
+ struct wil6210_vif *vif;
+ struct net_device *ndev;
+ int i;
+
+ mutex_lock(&wil->vif_mutex);
+ for (i = 0; i < wil->max_vifs; i++) {
+ vif = wil->vifs[i];
+
+ if (vif) {
+ ndev = vif_to_ndev(vif);
+ seq_printf(s, "[%d] %pM %s\n", i, ndev->dev_addr,
+ ndev->name);
+ } else {
+ seq_printf(s, "[%d] unused\n", i);
+ }
+ }
+ mutex_unlock(&wil->vif_mutex);
+
+ return 0;
+}
+
+static int wil_mids_seq_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, wil_mids_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations fops_mids = {
+ .open = wil_mids_seq_open,
+ .release = single_release,
+ .read = seq_read,
+ .llseek = seq_lseek,
+};
+
static ssize_t wil_read_file_led_cfg(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
@@ -1715,6 +1785,7 @@ static const struct {
{"mbox", 0444, &fops_mbox},
{"vrings", 0444, &fops_vring},
{"stations", 0444, &fops_sta},
+ {"mids", 0444, &fops_mids},
{"desc", 0444, &fops_txdesc},
{"bf", 0444, &fops_bf},
{"mem_val", 0644, &fops_memread},
@@ -1773,11 +1844,9 @@ static void wil6210_debugfs_init_isr(struct wil6210_priv *wil,
/* fields in struct wil6210_priv */
static const struct dbg_off dbg_wil_off[] = {
- WIL_FIELD(privacy, 0444, doff_u32),
WIL_FIELD(status[0], 0644, doff_ulong),
WIL_FIELD(hw_version, 0444, doff_x32),
WIL_FIELD(recovery_count, 0444, doff_u32),
- WIL_FIELD(ap_isolate, 0444, doff_u32),
WIL_FIELD(discovery_mode, 0644, doff_u8),
WIL_FIELD(chip_revision, 0444, doff_u8),
WIL_FIELD(abft_len, 0644, doff_u8),
diff --git a/drivers/net/wireless/ath/wil6210/ethtool.c b/drivers/net/wireless/ath/wil6210/ethtool.c
index 66200f616a37..e7ff41e623d2 100644
--- a/drivers/net/wireless/ath/wil6210/ethtool.c
+++ b/drivers/net/wireless/ath/wil6210/ethtool.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2014,2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -74,12 +75,13 @@ static int wil_ethtoolops_set_coalesce(struct net_device *ndev,
struct ethtool_coalesce *cp)
{
struct wil6210_priv *wil = ndev_to_wil(ndev);
+ struct wireless_dev *wdev = ndev->ieee80211_ptr;
int ret;
wil_dbg_misc(wil, "ethtoolops_set_coalesce: rx %d usec, tx %d usec\n",
cp->rx_coalesce_usecs, cp->tx_coalesce_usecs);
- if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) {
+ if (wdev->iftype == NL80211_IFTYPE_MONITOR) {
wil_dbg_misc(wil, "No IRQ coalescing in monitor mode\n");
return -EINVAL;
}
diff --git a/drivers/net/wireless/ath/wil6210/fw.h b/drivers/net/wireless/ath/wil6210/fw.h
index 2c7b24f61587..3e7a28045cab 100644
--- a/drivers/net/wireless/ath/wil6210/fw.h
+++ b/drivers/net/wireless/ath/wil6210/fw.h
@@ -14,6 +14,8 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#ifndef __WIL_FW_H__
+#define __WIL_FW_H__
#define WIL_FW_SIGNATURE (0x36323130) /* '0126' */
#define WIL_FW_FMT_VERSION (1) /* format version driver supports */
@@ -71,7 +73,39 @@ struct wil_fw_record_capabilities { /* type == wil_fw_type_comment */
struct wil_fw_record_comment_hdr hdr;
/* capabilities (variable size), see enum wmi_fw_capability */
u8 capabilities[0];
-};
+} __packed;
+
+/* FW VIF concurrency encoded inside a comment record
+ * Format is similar to wiphy->iface_combinations
+ */
+#define WIL_FW_CONCURRENCY_MAGIC (0xfedccdef)
+#define WIL_FW_CONCURRENCY_REC_VER 1
+struct wil_fw_concurrency_limit {
+ __le16 max; /* maximum number of interfaces of these types */
+ __le16 types; /* interface types (bit mask of enum nl80211_iftype) */
+} __packed;
+
+struct wil_fw_concurrency_combo {
+ u8 n_limits; /* number of wil_fw_concurrency_limit entries */
+ u8 max_interfaces; /* max number of concurrent interfaces allowed */
+ u8 n_diff_channels; /* total number of different channels allowed */
+ u8 same_bi; /* for APs, 1 if all APs must have same BI */
+ /* keep last - concurrency limits, variable size by n_limits */
+ struct wil_fw_concurrency_limit limits[0];
+} __packed;
+
+struct wil_fw_record_concurrency { /* type == wil_fw_type_comment */
+ /* identifies concurrency record */
+ __le32 magic;
+ /* structure version, currently always 1 */
+ u8 version;
+ /* maximum number of supported MIDs _in addition_ to MID 0 */
+ u8 n_mids;
+ /* number of concurrency combinations that follow */
+ __le16 n_combos;
+ /* keep last - combinations, variable size by n_combos */
+ struct wil_fw_concurrency_combo combos[0];
+} __packed;
/* brd file info encoded inside a comment record */
#define WIL_BRD_FILE_MAGIC (0xabcddcbb)
@@ -175,3 +209,5 @@ struct wil_fw_record_gateway_data4 { /* type == wil_fw_type_gateway_data4 */
__le32 command;
struct wil_fw_data_gw4 data[0]; /* total size [data_size], see above */
} __packed;
+
+#endif /* __WIL_FW_H__ */
diff --git a/drivers/net/wireless/ath/wil6210/fw_inc.c b/drivers/net/wireless/ath/wil6210/fw_inc.c
index 914c0106e94b..718161b829c2 100644
--- a/drivers/net/wireless/ath/wil6210/fw_inc.c
+++ b/drivers/net/wireless/ath/wil6210/fw_inc.c
@@ -136,8 +136,8 @@ fw_handle_capabilities(struct wil6210_priv *wil, const void *data,
size_t capa_size;
if (size < sizeof(*rec)) {
- wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1,
- data, size, true);
+ wil_err_fw(wil, "capabilities record too short: %zu\n", size);
+ /* let the FW load anyway */
return 0;
}
@@ -158,8 +158,7 @@ fw_handle_brd_file(struct wil6210_priv *wil, const void *data,
const struct wil_fw_record_brd_file *rec = data;
if (size < sizeof(*rec)) {
- wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1,
- data, size, true);
+ wil_err_fw(wil, "brd_file record too short: %zu\n", size);
return 0;
}
@@ -173,6 +172,44 @@ fw_handle_brd_file(struct wil6210_priv *wil, const void *data,
}
static int
+fw_handle_concurrency(struct wil6210_priv *wil, const void *data,
+ size_t size)
+{
+ const struct wil_fw_record_concurrency *rec = data;
+ const struct wil_fw_concurrency_combo *combo;
+ const struct wil_fw_concurrency_limit *limit;
+ size_t remain, lsize;
+ int i, n_combos;
+
+ if (size < sizeof(*rec)) {
+ wil_err_fw(wil, "concurrency record too short: %zu\n", size);
+ /* continue, let the FW load anyway */
+ return 0;
+ }
+
+ n_combos = le16_to_cpu(rec->n_combos);
+ remain = size - offsetof(struct wil_fw_record_concurrency, combos);
+ combo = rec->combos;
+ for (i = 0; i < n_combos; i++) {
+ if (remain < sizeof(*combo))
+ goto out_short;
+ remain -= sizeof(*combo);
+ limit = combo->limits;
+ lsize = combo->n_limits * sizeof(*limit);
+ if (remain < lsize)
+ goto out_short;
+ remain -= lsize;
+ limit += combo->n_limits;
+ combo = (struct wil_fw_concurrency_combo *)limit;
+ }
+
+ return wil_cfg80211_iface_combinations_from_fw(wil, rec);
+out_short:
+ wil_err_fw(wil, "concurrency record truncated\n");
+ return 0;
+}
+
+static int
fw_handle_comment(struct wil6210_priv *wil, const void *data,
size_t size)
{
@@ -194,6 +231,13 @@ fw_handle_comment(struct wil6210_priv *wil, const void *data,
wil_dbg_fw(wil, "magic is WIL_BRD_FILE_MAGIC\n");
rc = fw_handle_brd_file(wil, data, size);
break;
+ case WIL_FW_CONCURRENCY_MAGIC:
+ wil_dbg_fw(wil, "magic is WIL_FW_CONCURRENCY_MAGIC\n");
+ rc = fw_handle_concurrency(wil, data, size);
+ break;
+ default:
+ wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1,
+ data, size, true);
}
return rc;
diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c
index 1835187ea075..84e9840c1752 100644
--- a/drivers/net/wireless/ath/wil6210/interrupt.c
+++ b/drivers/net/wireless/ath/wil6210/interrupt.c
@@ -127,7 +127,7 @@ void wil6210_unmask_irq_tx(struct wil6210_priv *wil)
void wil6210_unmask_irq_rx(struct wil6210_priv *wil)
{
- bool unmask_rx_htrsh = test_bit(wil_status_fwconnected, wil->status);
+ bool unmask_rx_htrsh = atomic_read(&wil->connected_vifs) > 0;
wil_w(wil, RGF_DMA_EP_RX_ICR + offsetof(struct RGF_ICR, IMC),
unmask_rx_htrsh ? WIL6210_IMC_RX : WIL6210_IMC_RX_NO_RX_HTRSH);
@@ -188,12 +188,14 @@ void wil_unmask_irq(struct wil6210_priv *wil)
void wil_configure_interrupt_moderation(struct wil6210_priv *wil)
{
+ struct wireless_dev *wdev = wil->main_ndev->ieee80211_ptr;
+
wil_dbg_irq(wil, "configure_interrupt_moderation\n");
/* disable interrupt moderation for monitor
* to get better timestamp precision
*/
- if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR)
+ if (wdev->iftype == NL80211_IFTYPE_MONITOR)
return;
/* Disable and clear tx counter before (re)configuration */
@@ -340,7 +342,7 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
static void wil_notify_fw_error(struct wil6210_priv *wil)
{
- struct device *dev = &wil_to_ndev(wil)->dev;
+ struct device *dev = &wil->main_ndev->dev;
char *envp[3] = {
[0] = "SOURCE=wil6210",
[1] = "EVENT=FW_ERROR",
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 0c61a6c13991..a4b413e8d55a 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -160,24 +160,34 @@ void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src,
}
}
-static void wil_disconnect_cid(struct wil6210_priv *wil, int cid,
+static void wil_disconnect_cid(struct wil6210_vif *vif, int cid,
u16 reason_code, bool from_event)
__acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
{
uint i;
- struct net_device *ndev = wil_to_ndev(wil);
- struct wireless_dev *wdev = wil->wdev;
+ struct wil6210_priv *wil = vif_to_wil(vif);
+ struct net_device *ndev = vif_to_ndev(vif);
+ struct wireless_dev *wdev = vif_to_wdev(vif);
struct wil_sta_info *sta = &wil->sta[cid];
might_sleep();
- wil_dbg_misc(wil, "disconnect_cid: CID %d, status %d\n",
- cid, sta->status);
+ wil_dbg_misc(wil, "disconnect_cid: CID %d, MID %d, status %d\n",
+ cid, sta->mid, sta->status);
/* inform upper/lower layers */
if (sta->status != wil_sta_unused) {
+ if (vif->mid != sta->mid) {
+ wil_err(wil, "STA MID mismatch with VIF MID(%d)\n",
+ vif->mid);
+ /* let FW override sta->mid but be more strict with
+ * user space requests
+ */
+ if (!from_event)
+ return;
+ }
if (!from_event) {
bool del_sta = (wdev->iftype == NL80211_IFTYPE_AP) ?
disable_ap_sme : false;
- wmi_disconnect_sta(wil, sta->addr, reason_code,
+ wmi_disconnect_sta(vif, sta->addr, reason_code,
true, del_sta);
}
@@ -191,6 +201,7 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
break;
}
sta->status = wil_sta_unused;
+ sta->mid = U8_MAX;
}
/* reorder buffers */
for (i = 0; i < WIL_STA_TID_NUM; i++) {
@@ -216,28 +227,33 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
memset(&sta->stats, 0, sizeof(sta->stats));
}
-static bool wil_is_connected(struct wil6210_priv *wil)
+static bool wil_vif_is_connected(struct wil6210_priv *wil, u8 mid)
{
int i;
for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
- if (wil->sta[i].status == wil_sta_connected)
+ if (wil->sta[i].mid == mid &&
+ wil->sta[i].status == wil_sta_connected)
return true;
}
return false;
}
-static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
+static void _wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid,
u16 reason_code, bool from_event)
{
+ struct wil6210_priv *wil = vif_to_wil(vif);
int cid = -ENOENT;
- struct net_device *ndev = wil_to_ndev(wil);
- struct wireless_dev *wdev = wil->wdev;
+ struct net_device *ndev;
+ struct wireless_dev *wdev;
- if (unlikely(!ndev))
+ if (unlikely(!vif))
return;
+ ndev = vif_to_ndev(vif);
+ wdev = vif_to_wdev(vif);
+
might_sleep();
wil_info(wil, "bssid=%pM, reason=%d, ev%s\n", bssid,
reason_code, from_event ? "+" : "-");
@@ -254,48 +270,51 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
*/
if (bssid && !is_broadcast_ether_addr(bssid) &&
!ether_addr_equal_unaligned(ndev->dev_addr, bssid)) {
- cid = wil_find_cid(wil, bssid);
+ cid = wil_find_cid(wil, vif->mid, bssid);
wil_dbg_misc(wil, "Disconnect %pM, CID=%d, reason=%d\n",
bssid, cid, reason_code);
if (cid >= 0) /* disconnect 1 peer */
- wil_disconnect_cid(wil, cid, reason_code, from_event);
+ wil_disconnect_cid(vif, cid, reason_code, from_event);
} else { /* all */
wil_dbg_misc(wil, "Disconnect all\n");
for (cid = 0; cid < WIL6210_MAX_CID; cid++)
- wil_disconnect_cid(wil, cid, reason_code, from_event);
+ wil_disconnect_cid(vif, cid, reason_code, from_event);
}
/* link state */
switch (wdev->iftype) {
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_P2P_CLIENT:
- wil_bcast_fini(wil);
- wil_update_net_queues_bh(wil, NULL, true);
+ wil_bcast_fini(vif);
+ wil_update_net_queues_bh(wil, vif, NULL, true);
netif_carrier_off(ndev);
- wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS);
+ if (!wil_has_other_active_ifaces(wil, ndev, false, true))
+ wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS);
- if (test_bit(wil_status_fwconnected, wil->status)) {
- clear_bit(wil_status_fwconnected, wil->status);
+ if (test_and_clear_bit(wil_vif_fwconnected, vif->status)) {
+ atomic_dec(&wil->connected_vifs);
cfg80211_disconnected(ndev, reason_code,
NULL, 0,
- wil->locally_generated_disc,
+ vif->locally_generated_disc,
GFP_KERNEL);
- wil->locally_generated_disc = false;
- } else if (test_bit(wil_status_fwconnecting, wil->status)) {
+ vif->locally_generated_disc = false;
+ } else if (test_bit(wil_vif_fwconnecting, vif->status)) {
cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0,
WLAN_STATUS_UNSPECIFIED_FAILURE,
GFP_KERNEL);
- wil->bss = NULL;
+ vif->bss = NULL;
}
- clear_bit(wil_status_fwconnecting, wil->status);
+ clear_bit(wil_vif_fwconnecting, vif->status);
break;
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
- if (!wil_is_connected(wil)) {
- wil_update_net_queues_bh(wil, NULL, true);
- clear_bit(wil_status_fwconnected, wil->status);
+ if (!wil_vif_is_connected(wil, vif->mid)) {
+ wil_update_net_queues_bh(wil, vif, NULL, true);
+ if (test_and_clear_bit(wil_vif_fwconnected,
+ vif->status))
+ atomic_dec(&wil->connected_vifs);
} else {
- wil_update_net_queues_bh(wil, NULL, false);
+ wil_update_net_queues_bh(wil, vif, NULL, false);
}
break;
default:
@@ -303,26 +322,27 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
}
}
-static void wil_disconnect_worker(struct work_struct *work)
+void wil_disconnect_worker(struct work_struct *work)
{
- struct wil6210_priv *wil = container_of(work,
- struct wil6210_priv, disconnect_worker);
- struct net_device *ndev = wil_to_ndev(wil);
+ struct wil6210_vif *vif = container_of(work,
+ struct wil6210_vif, disconnect_worker);
+ struct wil6210_priv *wil = vif_to_wil(vif);
+ struct net_device *ndev = vif_to_ndev(vif);
int rc;
struct {
struct wmi_cmd_hdr wmi;
struct wmi_disconnect_event evt;
} __packed reply;
- if (test_bit(wil_status_fwconnected, wil->status))
+ if (test_bit(wil_vif_fwconnected, vif->status))
/* connect succeeded after all */
return;
- if (!test_bit(wil_status_fwconnecting, wil->status))
+ if (!test_bit(wil_vif_fwconnecting, vif->status))
/* already disconnected */
return;
- rc = wmi_call(wil, WMI_DISCONNECT_CMDID, NULL, 0,
+ rc = wmi_call(wil, WMI_DISCONNECT_CMDID, vif->mid, NULL, 0,
WMI_DISCONNECT_EVENTID, &reply, sizeof(reply),
WIL6210_DISCONNECT_TO_MS);
if (rc) {
@@ -330,35 +350,11 @@ static void wil_disconnect_worker(struct work_struct *work)
return;
}
- wil_update_net_queues_bh(wil, NULL, true);
+ wil_update_net_queues_bh(wil, vif, NULL, true);
netif_carrier_off(ndev);
cfg80211_connect_result(ndev, NULL, NULL, 0, NULL, 0,
WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_KERNEL);
- clear_bit(wil_status_fwconnecting, wil->status);
-}
-
-static void wil_connect_timer_fn(struct timer_list *t)
-{
- struct wil6210_priv *wil = from_timer(wil, t, connect_timer);
- bool q;
-
- wil_err(wil, "Connect timeout detected, disconnect station\n");
-
- /* reschedule to thread context - disconnect won't
- * run from atomic context.
- * queue on wmi_wq to prevent race with connect event.
- */
- q = queue_work(wil->wmi_wq, &wil->disconnect_worker);
- wil_dbg_wmi(wil, "queue_work of disconnect_worker -> %d\n", q);
-}
-
-static void wil_scan_timer_fn(struct timer_list *t)
-{
- struct wil6210_priv *wil = from_timer(wil, t, scan_timer);
-
- clear_bit(wil_status_fwready, wil->status);
- wil_err(wil, "Scan timeout detected, start fw error recovery\n");
- wil_fw_error_recovery(wil);
+ clear_bit(wil_vif_fwconnecting, vif->status);
}
static int wil_wait_for_recovery(struct wil6210_priv *wil)
@@ -394,12 +390,12 @@ static void wil_fw_error_worker(struct work_struct *work)
{
struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
fw_error_worker);
- struct wireless_dev *wdev = wil->wdev;
- struct net_device *ndev = wil_to_ndev(wil);
+ struct net_device *ndev = wil->main_ndev;
+ struct wireless_dev *wdev = ndev->ieee80211_ptr;
wil_dbg_misc(wil, "fw error worker\n");
- if (!(ndev->flags & IFF_UP)) {
+ if (!ndev || !(ndev->flags & IFF_UP)) {
wil_info(wil, "No recovery - interface is down\n");
return;
}
@@ -429,6 +425,10 @@ static void wil_fw_error_worker(struct work_struct *work)
return;
mutex_lock(&wil->mutex);
+ /* Needs adaptation for multiple VIFs
+ * need to go over all VIFs and consider the appropriate
+ * recovery.
+ */
switch (wdev->iftype) {
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_P2P_CLIENT:
@@ -461,8 +461,9 @@ static int wil_find_free_vring(struct wil6210_priv *wil)
return -EINVAL;
}
-int wil_tx_init(struct wil6210_priv *wil, int cid)
+int wil_tx_init(struct wil6210_vif *vif, int cid)
{
+ struct wil6210_priv *wil = vif_to_wil(vif);
int rc = -EINVAL, ringid;
if (cid < 0) {
@@ -475,21 +476,22 @@ int wil_tx_init(struct wil6210_priv *wil, int cid)
goto out;
}
- wil_dbg_wmi(wil, "Configure for connection CID %d vring %d\n",
- cid, ringid);
+ wil_dbg_wmi(wil, "Configure for connection CID %d MID %d vring %d\n",
+ cid, vif->mid, ringid);
- rc = wil_vring_init_tx(wil, ringid, 1 << tx_ring_order, cid, 0);
+ rc = wil_vring_init_tx(vif, ringid, 1 << tx_ring_order, cid, 0);
if (rc)
- wil_err(wil, "wil_vring_init_tx for CID %d vring %d failed\n",
- cid, ringid);
+ wil_err(wil, "init TX for CID %d MID %d vring %d failed\n",
+ cid, vif->mid, ringid);
out:
return rc;
}
-int wil_bcast_init(struct wil6210_priv *wil)
+int wil_bcast_init(struct wil6210_vif *vif)
{
- int ri = wil->bcast_vring, rc;
+ struct wil6210_priv *wil = vif_to_wil(vif);
+ int ri = vif->bcast_vring, rc;
if ((ri >= 0) && wil->vring_tx[ri].va)
return 0;
@@ -498,25 +500,38 @@ int wil_bcast_init(struct wil6210_priv *wil)
if (ri < 0)
return ri;
- wil->bcast_vring = ri;
- rc = wil_vring_init_bcast(wil, ri, 1 << bcast_ring_order);
+ vif->bcast_vring = ri;
+ rc = wil_vring_init_bcast(vif, ri, 1 << bcast_ring_order);
if (rc)
- wil->bcast_vring = -1;
+ vif->bcast_vring = -1;
return rc;
}
-void wil_bcast_fini(struct wil6210_priv *wil)
+void wil_bcast_fini(struct wil6210_vif *vif)
{
- int ri = wil->bcast_vring;
+ struct wil6210_priv *wil = vif_to_wil(vif);
+ int ri = vif->bcast_vring;
if (ri < 0)
return;
- wil->bcast_vring = -1;
+ vif->bcast_vring = -1;
wil_vring_fini_tx(wil, ri);
}
+void wil_bcast_fini_all(struct wil6210_priv *wil)
+{
+ int i;
+ struct wil6210_vif *vif;
+
+ for (i = 0; i < wil->max_vifs; i++) {
+ vif = wil->vifs[i];
+ if (vif)
+ wil_bcast_fini(vif);
+ }
+}
+
int wil_priv_init(struct wil6210_priv *wil)
{
uint i;
@@ -524,38 +539,29 @@ int wil_priv_init(struct wil6210_priv *wil)
wil_dbg_misc(wil, "priv_init\n");
memset(wil->sta, 0, sizeof(wil->sta));
- for (i = 0; i < WIL6210_MAX_CID; i++)
+ for (i = 0; i < WIL6210_MAX_CID; i++) {
spin_lock_init(&wil->sta[i].tid_rx_lock);
+ wil->sta[i].mid = U8_MAX;
+ }
for (i = 0; i < WIL6210_MAX_TX_RINGS; i++)
spin_lock_init(&wil->vring_tx_data[i].lock);
mutex_init(&wil->mutex);
+ mutex_init(&wil->vif_mutex);
mutex_init(&wil->wmi_mutex);
- mutex_init(&wil->probe_client_mutex);
- mutex_init(&wil->p2p_wdev_mutex);
mutex_init(&wil->halp.lock);
init_completion(&wil->wmi_ready);
init_completion(&wil->wmi_call);
init_completion(&wil->halp.comp);
- wil->bcast_vring = -1;
- timer_setup(&wil->connect_timer, wil_connect_timer_fn, 0);
- timer_setup(&wil->scan_timer, wil_scan_timer_fn, 0);
- timer_setup(&wil->p2p.discovery_timer, wil_p2p_discovery_timer_fn, 0);
-
- INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker);
INIT_WORK(&wil->wmi_event_worker, wmi_event_worker);
INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker);
- INIT_WORK(&wil->probe_client_worker, wil_probe_client_worker);
- INIT_WORK(&wil->p2p.delayed_listen_work, wil_p2p_delayed_listen_work);
INIT_LIST_HEAD(&wil->pending_wmi_ev);
- INIT_LIST_HEAD(&wil->probe_client_pending);
spin_lock_init(&wil->wmi_ev_lock);
spin_lock_init(&wil->net_queue_lock);
- wil->net_queue_stopped = 1;
init_waitqueue_head(&wil->wq);
wil->wmi_wq = create_singlethread_workqueue(WIL_NAME "_wmi");
@@ -582,6 +588,9 @@ int wil_priv_init(struct wil6210_priv *wil)
memset(&wil->suspend_stats, 0, sizeof(wil->suspend_stats));
wil->vring_idle_trsh = 16;
+ wil->reply_mid = U8_MAX;
+ wil->max_vifs = 1;
+
return 0;
out_wmi_wq:
@@ -600,7 +609,7 @@ void wil6210_bus_request(struct wil6210_priv *wil, u32 kbps)
/**
* wil6210_disconnect - disconnect one connection
- * @wil: driver context
+ * @vif: virtual interface context
* @bssid: peer to disconnect, NULL to disconnect all
* @reason_code: Reason code for the Disassociation frame
* @from_event: whether is invoked from FW event handler
@@ -608,13 +617,15 @@ void wil6210_bus_request(struct wil6210_priv *wil, u32 kbps)
* Disconnect and release associated resources. If invoked not from the
* FW event handler, issue WMI command(s) to trigger MAC disconnect.
*/
-void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
+void wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid,
u16 reason_code, bool from_event)
{
+ struct wil6210_priv *wil = vif_to_wil(vif);
+
wil_dbg_misc(wil, "disconnect\n");
- del_timer_sync(&wil->connect_timer);
- _wil6210_disconnect(wil, bssid, reason_code, from_event);
+ del_timer_sync(&vif->connect_timer);
+ _wil6210_disconnect(vif, bssid, reason_code, from_event);
}
void wil_priv_deinit(struct wil6210_priv *wil)
@@ -622,18 +633,8 @@ void wil_priv_deinit(struct wil6210_priv *wil)
wil_dbg_misc(wil, "priv_deinit\n");
wil_set_recovery_state(wil, fw_recovery_idle);
- del_timer_sync(&wil->scan_timer);
- del_timer_sync(&wil->p2p.discovery_timer);
- cancel_work_sync(&wil->disconnect_worker);
cancel_work_sync(&wil->fw_error_worker);
- cancel_work_sync(&wil->p2p.discovery_expired_work);
- cancel_work_sync(&wil->p2p.delayed_listen_work);
- mutex_lock(&wil->mutex);
- wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
- mutex_unlock(&wil->mutex);
wmi_event_flush(wil);
- wil_probe_client_flush(wil);
- cancel_work_sync(&wil->probe_client_worker);
destroy_workqueue(wil->wq_service);
destroy_workqueue(wil->wmi_wq);
}
@@ -715,7 +716,7 @@ static void wil_bl_prepare_halt(struct wil6210_priv *wil)
offsetof(struct bl_dedicated_registers_v0,
boot_loader_struct_version));
if (!tmp) {
- wil_dbg_misc(wil, "old BL, skipping halt preperation\n");
+ wil_dbg_misc(wil, "old BL, skipping halt preparation\n");
return;
}
@@ -943,7 +944,7 @@ void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r)
static int wil_get_bl_info(struct wil6210_priv *wil)
{
- struct net_device *ndev = wil_to_ndev(wil);
+ struct net_device *ndev = wil->main_ndev;
struct wiphy *wiphy = wil_to_wiphy(wil);
union {
struct bl_dedicated_registers_v0 bl0;
@@ -1035,7 +1036,7 @@ static void wil_bl_crash_info(struct wil6210_priv *wil, bool is_err)
static int wil_get_otp_info(struct wil6210_priv *wil)
{
- struct net_device *ndev = wil_to_ndev(wil);
+ struct net_device *ndev = wil->main_ndev;
struct wiphy *wiphy = wil_to_wiphy(wil);
u8 mac[8];
@@ -1069,31 +1070,46 @@ static int wil_wait_for_fw_ready(struct wil6210_priv *wil)
return 0;
}
-void wil_abort_scan(struct wil6210_priv *wil, bool sync)
+void wil_abort_scan(struct wil6210_vif *vif, bool sync)
{
+ struct wil6210_priv *wil = vif_to_wil(vif);
int rc;
struct cfg80211_scan_info info = {
.aborted = true,
};
- lockdep_assert_held(&wil->p2p_wdev_mutex);
+ lockdep_assert_held(&wil->vif_mutex);
- if (!wil->scan_request)
+ if (!vif->scan_request)
return;
- wil_dbg_misc(wil, "Abort scan_request 0x%p\n", wil->scan_request);
- del_timer_sync(&wil->scan_timer);
- mutex_unlock(&wil->p2p_wdev_mutex);
- rc = wmi_abort_scan(wil);
+ wil_dbg_misc(wil, "Abort scan_request 0x%p\n", vif->scan_request);
+ del_timer_sync(&vif->scan_timer);
+ mutex_unlock(&wil->vif_mutex);
+ rc = wmi_abort_scan(vif);
if (!rc && sync)
- wait_event_interruptible_timeout(wil->wq, !wil->scan_request,
+ wait_event_interruptible_timeout(wil->wq, !vif->scan_request,
msecs_to_jiffies(
WAIT_FOR_SCAN_ABORT_MS));
- mutex_lock(&wil->p2p_wdev_mutex);
- if (wil->scan_request) {
- cfg80211_scan_done(wil->scan_request, &info);
- wil->scan_request = NULL;
+ mutex_lock(&wil->vif_mutex);
+ if (vif->scan_request) {
+ cfg80211_scan_done(vif->scan_request, &info);
+ vif->scan_request = NULL;
+ }
+}
+
+void wil_abort_scan_all_vifs(struct wil6210_priv *wil, bool sync)
+{
+ int i;
+
+ lockdep_assert_held(&wil->vif_mutex);
+
+ for (i = 0; i < wil->max_vifs; i++) {
+ struct wil6210_vif *vif = wil->vifs[i];
+
+ if (vif)
+ wil_abort_scan(vif, sync);
}
}
@@ -1138,6 +1154,34 @@ static void wil_pre_fw_config(struct wil6210_priv *wil)
}
}
+static int wil_restore_vifs(struct wil6210_priv *wil)
+{
+ struct wil6210_vif *vif;
+ struct net_device *ndev;
+ struct wireless_dev *wdev;
+ int i, rc;
+
+ for (i = 0; i < wil->max_vifs; i++) {
+ vif = wil->vifs[i];
+ if (!vif)
+ continue;
+ vif->ap_isolate = 0;
+ if (vif->mid) {
+ ndev = vif_to_ndev(vif);
+ wdev = vif_to_wdev(vif);
+ rc = wmi_port_allocate(wil, vif->mid, ndev->dev_addr,
+ wdev->iftype);
+ if (rc) {
+ wil_err(wil, "fail to restore VIF %d type %d, rc %d\n",
+ i, wdev->iftype, rc);
+ return rc;
+ }
+ }
+ }
+
+ return 0;
+}
+
/*
* We reset all the structures, and we reset the UMAC.
* After calling this routine, you're expected to reload
@@ -1145,9 +1189,10 @@ static void wil_pre_fw_config(struct wil6210_priv *wil)
*/
int wil_reset(struct wil6210_priv *wil, bool load_fw)
{
- int rc;
+ int rc, i;
unsigned long status_flags = BIT(wil_status_resetting);
int no_flash;
+ struct wil6210_vif *vif;
wil_dbg_misc(wil, "reset\n");
@@ -1158,7 +1203,7 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
static const u8 mac[ETH_ALEN] = {
0x00, 0xde, 0xad, 0x12, 0x34, 0x56,
};
- struct net_device *ndev = wil_to_ndev(wil);
+ struct net_device *ndev = wil->main_ndev;
ether_addr_copy(ndev->perm_addr, mac);
ether_addr_copy(ndev->dev_addr, ndev->perm_addr);
@@ -1196,17 +1241,23 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
goto out;
}
- cancel_work_sync(&wil->disconnect_worker);
- wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
- wil_bcast_fini(wil);
+ mutex_lock(&wil->vif_mutex);
+ wil_abort_scan_all_vifs(wil, false);
+ mutex_unlock(&wil->vif_mutex);
+
+ for (i = 0; i < wil->max_vifs; i++) {
+ vif = wil->vifs[i];
+ if (vif) {
+ cancel_work_sync(&vif->disconnect_worker);
+ wil6210_disconnect(vif, NULL,
+ WLAN_REASON_DEAUTH_LEAVING, false);
+ }
+ }
+ wil_bcast_fini_all(wil);
/* Disable device led before reset*/
wmi_led_cfg(wil, false);
- mutex_lock(&wil->p2p_wdev_mutex);
- wil_abort_scan(wil, false);
- mutex_unlock(&wil->p2p_wdev_mutex);
-
/* prevent NAPI from being scheduled and prevent wmi commands */
mutex_lock(&wil->wmi_mutex);
if (test_bit(wil_status_suspending, wil->status))
@@ -1276,7 +1327,6 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
}
/* init after reset */
- wil->ap_isolate = 0;
reinit_completion(&wil->wmi_ready);
reinit_completion(&wil->wmi_call);
reinit_completion(&wil->halp.comp);
@@ -1299,6 +1349,12 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
return rc;
}
+ rc = wil_restore_vifs(wil);
+ if (rc) {
+ wil_err(wil, "failed to restore vifs, rc %d\n", rc);
+ return rc;
+ }
+
wil_collect_fw_info(wil);
if (wil->ps_profile != WMI_PS_PROFILE_TYPE_DEFAULT)
@@ -1337,8 +1393,8 @@ void wil_fw_error_recovery(struct wil6210_priv *wil)
int __wil_up(struct wil6210_priv *wil)
{
- struct net_device *ndev = wil_to_ndev(wil);
- struct wireless_dev *wdev = wil->wdev;
+ struct net_device *ndev = wil->main_ndev;
+ struct wireless_dev *wdev = ndev->ieee80211_ptr;
int rc;
WARN_ON(!mutex_is_locked(&wil->mutex));
@@ -1420,10 +1476,10 @@ int __wil_down(struct wil6210_priv *wil)
}
wil_enable_irq(wil);
- mutex_lock(&wil->p2p_wdev_mutex);
+ mutex_lock(&wil->vif_mutex);
wil_p2p_stop_radio_operations(wil);
- wil_abort_scan(wil, false);
- mutex_unlock(&wil->p2p_wdev_mutex);
+ wil_abort_scan_all_vifs(wil, false);
+ mutex_unlock(&wil->vif_mutex);
return wil_reset(wil, false);
}
@@ -1442,13 +1498,14 @@ int wil_down(struct wil6210_priv *wil)
return rc;
}
-int wil_find_cid(struct wil6210_priv *wil, const u8 *mac)
+int wil_find_cid(struct wil6210_priv *wil, u8 mid, const u8 *mac)
{
int i;
int rc = -ENOENT;
for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
- if ((wil->sta[i].status != wil_sta_unused) &&
+ if (wil->sta[i].mid == mid &&
+ wil->sta[i].status != wil_sta_unused &&
ether_addr_equal(wil->sta[i].addr, mac)) {
rc = i;
break;
diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c
index 7ba4e0af8f57..05e9408e7ea3 100644
--- a/drivers/net/wireless/ath/wil6210/netdev.c
+++ b/drivers/net/wireless/ath/wil6210/netdev.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -15,13 +16,41 @@
*/
#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
#include "wil6210.h"
#include "txrx.h"
+bool wil_has_other_active_ifaces(struct wil6210_priv *wil,
+ struct net_device *ndev, bool up, bool ok)
+{
+ int i;
+ struct wil6210_vif *vif;
+ struct net_device *ndev_i;
+
+ for (i = 0; i < wil->max_vifs; i++) {
+ vif = wil->vifs[i];
+ if (vif) {
+ ndev_i = vif_to_ndev(vif);
+ if (ndev_i != ndev)
+ if ((up && (ndev_i->flags & IFF_UP)) ||
+ (ok && netif_carrier_ok(ndev_i)))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool wil_has_active_ifaces(struct wil6210_priv *wil, bool up, bool ok)
+{
+ /* use NULL ndev argument to check all interfaces */
+ return wil_has_other_active_ifaces(wil, NULL, up, ok);
+}
+
static int wil_open(struct net_device *ndev)
{
struct wil6210_priv *wil = ndev_to_wil(ndev);
- int rc;
+ int rc = 0;
wil_dbg_misc(wil, "open\n");
@@ -31,13 +60,16 @@ static int wil_open(struct net_device *ndev)
return -EINVAL;
}
- rc = wil_pm_runtime_get(wil);
- if (rc < 0)
- return rc;
+ if (!wil_has_other_active_ifaces(wil, ndev, true, false)) {
+ wil_dbg_misc(wil, "open, first iface\n");
+ rc = wil_pm_runtime_get(wil);
+ if (rc < 0)
+ return rc;
- rc = wil_up(wil);
- if (rc)
- wil_pm_runtime_put(wil);
+ rc = wil_up(wil);
+ if (rc)
+ wil_pm_runtime_put(wil);
+ }
return rc;
}
@@ -45,13 +77,16 @@ static int wil_open(struct net_device *ndev)
static int wil_stop(struct net_device *ndev)
{
struct wil6210_priv *wil = ndev_to_wil(ndev);
- int rc;
+ int rc = 0;
wil_dbg_misc(wil, "stop\n");
- rc = wil_down(wil);
- if (!rc)
- wil_pm_runtime_put(wil);
+ if (!wil_has_other_active_ifaces(wil, ndev, true, false)) {
+ wil_dbg_misc(wil, "stop, last iface\n");
+ rc = wil_down(wil);
+ if (!rc)
+ wil_pm_runtime_put(wil);
+ }
return rc;
}
@@ -96,11 +131,19 @@ static int wil6210_netdev_poll_tx(struct napi_struct *napi, int budget)
for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
struct vring *vring = &wil->vring_tx[i];
struct vring_tx_data *txdata = &wil->vring_tx_data[i];
+ struct wil6210_vif *vif;
- if (!vring->va || !txdata->enabled)
+ if (!vring->va || !txdata->enabled ||
+ txdata->mid >= wil->max_vifs)
continue;
- tx_done += wil_tx_complete(wil, i);
+ vif = wil->vifs[txdata->mid];
+ if (unlikely(!vif)) {
+ wil_dbg_txrx(wil, "Invalid MID %d\n", txdata->mid);
+ continue;
+ }
+
+ tx_done += wil_tx_complete(vif, i);
}
if (tx_done < budget) {
@@ -121,44 +164,137 @@ static void wil_dev_setup(struct net_device *dev)
dev->tx_queue_len = WIL_TX_Q_LEN_DEFAULT;
}
-void *wil_if_alloc(struct device *dev)
+static void wil_vif_deinit(struct wil6210_vif *vif)
{
- struct net_device *ndev;
- struct wireless_dev *wdev;
- struct wil6210_priv *wil;
- struct ieee80211_channel *ch;
- int rc = 0;
+ del_timer_sync(&vif->scan_timer);
+ del_timer_sync(&vif->p2p.discovery_timer);
+ cancel_work_sync(&vif->disconnect_worker);
+ cancel_work_sync(&vif->p2p.discovery_expired_work);
+ cancel_work_sync(&vif->p2p.delayed_listen_work);
+ wil_probe_client_flush(vif);
+ cancel_work_sync(&vif->probe_client_worker);
+}
- wdev = wil_cfg80211_init(dev);
- if (IS_ERR(wdev)) {
- dev_err(dev, "wil_cfg80211_init failed\n");
- return wdev;
- }
+void wil_vif_free(struct wil6210_vif *vif)
+{
+ struct net_device *ndev = vif_to_ndev(vif);
- wil = wdev_to_wil(wdev);
- wil->wdev = wdev;
- wil->radio_wdev = wdev;
+ wil_vif_deinit(vif);
+ free_netdev(ndev);
+}
- wil_dbg_misc(wil, "if_alloc\n");
+static void wil_ndev_destructor(struct net_device *ndev)
+{
+ struct wil6210_vif *vif = ndev_to_vif(ndev);
- rc = wil_priv_init(wil);
- if (rc) {
- dev_err(dev, "wil_priv_init failed\n");
- goto out_wdev;
+ wil_vif_deinit(vif);
+}
+
+static void wil_connect_timer_fn(struct timer_list *t)
+{
+ struct wil6210_vif *vif = from_timer(vif, t, connect_timer);
+ struct wil6210_priv *wil = vif_to_wil(vif);
+ bool q;
+
+ wil_err(wil, "Connect timeout detected, disconnect station\n");
+
+ /* reschedule to thread context - disconnect won't
+ * run from atomic context.
+ * queue on wmi_wq to prevent race with connect event.
+ */
+ q = queue_work(wil->wmi_wq, &vif->disconnect_worker);
+ wil_dbg_wmi(wil, "queue_work of disconnect_worker -> %d\n", q);
+}
+
+static void wil_scan_timer_fn(struct timer_list *t)
+{
+ struct wil6210_vif *vif = from_timer(vif, t, scan_timer);
+ struct wil6210_priv *wil = vif_to_wil(vif);
+
+ clear_bit(wil_status_fwready, wil->status);
+ wil_err(wil, "Scan timeout detected, start fw error recovery\n");
+ wil_fw_error_recovery(wil);
+}
+
+static void wil_p2p_discovery_timer_fn(struct timer_list *t)
+{
+ struct wil6210_vif *vif = from_timer(vif, t, p2p.discovery_timer);
+ struct wil6210_priv *wil = vif_to_wil(vif);
+
+ wil_dbg_misc(wil, "p2p_discovery_timer_fn\n");
+
+ schedule_work(&vif->p2p.discovery_expired_work);
+}
+
+static void wil_vif_init(struct wil6210_vif *vif)
+{
+ vif->bcast_vring = -1;
+
+ mutex_init(&vif->probe_client_mutex);
+
+ timer_setup(&vif->connect_timer, wil_connect_timer_fn, 0);
+ timer_setup(&vif->scan_timer, wil_scan_timer_fn, 0);
+ timer_setup(&vif->p2p.discovery_timer, wil_p2p_discovery_timer_fn, 0);
+
+ INIT_WORK(&vif->probe_client_worker, wil_probe_client_worker);
+ INIT_WORK(&vif->disconnect_worker, wil_disconnect_worker);
+ INIT_WORK(&vif->p2p.delayed_listen_work, wil_p2p_delayed_listen_work);
+
+ INIT_LIST_HEAD(&vif->probe_client_pending);
+
+ vif->net_queue_stopped = 1;
+}
+
+static u8 wil_vif_find_free_mid(struct wil6210_priv *wil)
+{
+ u8 i;
+
+ for (i = 0; i < wil->max_vifs; i++) {
+ if (!wil->vifs[i])
+ return i;
}
- wdev->iftype = NL80211_IFTYPE_STATION; /* TODO */
- /* default monitor channel */
- ch = wdev->wiphy->bands[NL80211_BAND_60GHZ]->channels;
- cfg80211_chandef_create(&wil->monitor_chandef, ch, NL80211_CHAN_NO_HT);
+ return U8_MAX;
+}
+
+struct wil6210_vif *
+wil_vif_alloc(struct wil6210_priv *wil, const char *name,
+ unsigned char name_assign_type, enum nl80211_iftype iftype)
+{
+ struct net_device *ndev;
+ struct wireless_dev *wdev;
+ struct wil6210_vif *vif;
+ u8 mid;
+
+ mid = wil_vif_find_free_mid(wil);
+ if (mid == U8_MAX) {
+ wil_err(wil, "no available virtual interface\n");
+ return ERR_PTR(-EINVAL);
+ }
- ndev = alloc_netdev(0, "wlan%d", NET_NAME_UNKNOWN, wil_dev_setup);
+ ndev = alloc_netdev(sizeof(*vif), name, name_assign_type,
+ wil_dev_setup);
if (!ndev) {
- dev_err(dev, "alloc_netdev_mqs failed\n");
- rc = -ENOMEM;
- goto out_priv;
+ dev_err(wil_to_dev(wil), "alloc_netdev failed\n");
+ return ERR_PTR(-ENOMEM);
+ }
+ if (mid == 0) {
+ wil->main_ndev = ndev;
+ } else {
+ ndev->priv_destructor = wil_ndev_destructor;
+ ndev->needs_free_netdev = true;
}
+ vif = ndev_to_vif(ndev);
+ vif->ndev = ndev;
+ vif->wil = wil;
+ vif->mid = mid;
+ wil_vif_init(vif);
+
+ wdev = &vif->wdev;
+ wdev->wiphy = wil->wiphy;
+ wdev->iftype = iftype;
+
ndev->netdev_ops = &wil_netdev_ops;
wil_set_ethtoolops(ndev);
ndev->ieee80211_ptr = wdev;
@@ -170,21 +306,53 @@ void *wil_if_alloc(struct device *dev)
ndev->features |= ndev->hw_features;
SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
wdev->netdev = ndev;
+ return vif;
+}
+
+void *wil_if_alloc(struct device *dev)
+{
+ struct wil6210_priv *wil;
+ struct wil6210_vif *vif;
+ int rc = 0;
+
+ wil = wil_cfg80211_init(dev);
+ if (IS_ERR(wil)) {
+ dev_err(dev, "wil_cfg80211_init failed\n");
+ return wil;
+ }
+
+ rc = wil_priv_init(wil);
+ if (rc) {
+ dev_err(dev, "wil_priv_init failed\n");
+ goto out_cfg;
+ }
+
+ wil_dbg_misc(wil, "if_alloc\n");
+
+ vif = wil_vif_alloc(wil, "wlan%d", NET_NAME_UNKNOWN,
+ NL80211_IFTYPE_STATION);
+ if (IS_ERR(vif)) {
+ dev_err(dev, "wil_vif_alloc failed\n");
+ rc = -ENOMEM;
+ goto out_priv;
+ }
+
+ wil->radio_wdev = vif_to_wdev(vif);
return wil;
- out_priv:
+out_priv:
wil_priv_deinit(wil);
- out_wdev:
- wil_wdev_free(wil);
+out_cfg:
+ wil_cfg80211_deinit(wil);
return ERR_PTR(rc);
}
void wil_if_free(struct wil6210_priv *wil)
{
- struct net_device *ndev = wil_to_ndev(wil);
+ struct net_device *ndev = wil->main_ndev;
wil_dbg_misc(wil, "if_free\n");
@@ -193,17 +361,50 @@ void wil_if_free(struct wil6210_priv *wil)
wil_priv_deinit(wil);
- wil_to_ndev(wil) = NULL;
+ wil->main_ndev = NULL;
+ wil_ndev_destructor(ndev);
free_netdev(ndev);
- wil_wdev_free(wil);
+ wil_cfg80211_deinit(wil);
+}
+
+int wil_vif_add(struct wil6210_priv *wil, struct wil6210_vif *vif)
+{
+ struct net_device *ndev = vif_to_ndev(vif);
+ struct wireless_dev *wdev = vif_to_wdev(vif);
+ bool any_active = wil_has_active_ifaces(wil, true, false);
+ int rc;
+
+ ASSERT_RTNL();
+
+ if (wil->vifs[vif->mid]) {
+ dev_err(&ndev->dev, "VIF with mid %d already in use\n",
+ vif->mid);
+ return -EEXIST;
+ }
+ if (any_active && vif->mid != 0) {
+ rc = wmi_port_allocate(wil, vif->mid, ndev->dev_addr,
+ wdev->iftype);
+ if (rc)
+ return rc;
+ }
+ rc = register_netdevice(ndev);
+ if (rc < 0) {
+ dev_err(&ndev->dev, "Failed to register netdev: %d\n", rc);
+ if (any_active && vif->mid != 0)
+ wmi_port_delete(wil, vif->mid);
+ return rc;
+ }
+
+ wil->vifs[vif->mid] = vif;
+ return 0;
}
int wil_if_add(struct wil6210_priv *wil)
{
- struct wireless_dev *wdev = wil_to_wdev(wil);
- struct wiphy *wiphy = wdev->wiphy;
- struct net_device *ndev = wil_to_ndev(wil);
+ struct wiphy *wiphy = wil->wiphy;
+ struct net_device *ndev = wil->main_ndev;
+ struct wil6210_vif *vif = ndev_to_vif(ndev);
int rc;
wil_dbg_misc(wil, "entered");
@@ -216,33 +417,94 @@ int wil_if_add(struct wil6210_priv *wil)
return rc;
}
- netif_napi_add(ndev, &wil->napi_rx, wil6210_netdev_poll_rx,
+ init_dummy_netdev(&wil->napi_ndev);
+ netif_napi_add(&wil->napi_ndev, &wil->napi_rx, wil6210_netdev_poll_rx,
WIL6210_NAPI_BUDGET);
- netif_tx_napi_add(ndev, &wil->napi_tx, wil6210_netdev_poll_tx,
+ netif_tx_napi_add(&wil->napi_ndev,
+ &wil->napi_tx, wil6210_netdev_poll_tx,
WIL6210_NAPI_BUDGET);
- wil_update_net_queues_bh(wil, NULL, true);
+ wil_update_net_queues_bh(wil, vif, NULL, true);
- rc = register_netdev(ndev);
- if (rc < 0) {
- dev_err(&ndev->dev, "Failed to register netdev: %d\n", rc);
+ rtnl_lock();
+ rc = wil_vif_add(wil, vif);
+ rtnl_unlock();
+ if (rc < 0)
goto out_wiphy;
- }
return 0;
out_wiphy:
- wiphy_unregister(wdev->wiphy);
+ wiphy_unregister(wiphy);
return rc;
}
+void wil_vif_remove(struct wil6210_priv *wil, u8 mid)
+{
+ struct wil6210_vif *vif;
+ struct net_device *ndev;
+ bool any_active = wil_has_active_ifaces(wil, true, false);
+
+ ASSERT_RTNL();
+ if (mid >= wil->max_vifs) {
+ wil_err(wil, "invalid MID: %d\n", mid);
+ return;
+ }
+
+ vif = wil->vifs[mid];
+ if (!vif) {
+ wil_err(wil, "MID %d not registered\n", mid);
+ return;
+ }
+
+ ndev = vif_to_ndev(vif);
+ /* during unregister_netdevice cfg80211_leave may perform operations
+ * such as stop AP, disconnect, so we only clear the VIF afterwards
+ */
+ unregister_netdevice(ndev);
+
+ mutex_lock(&wil->mutex);
+ wil6210_disconnect(vif, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
+ mutex_unlock(&wil->mutex);
+
+ if (any_active && vif->mid != 0)
+ wmi_port_delete(wil, vif->mid);
+
+ /* make sure no one is accessing the VIF before removing */
+ mutex_lock(&wil->vif_mutex);
+ wil->vifs[mid] = NULL;
+ /* ensure NAPI code will see the NULL VIF */
+ wmb();
+ if (test_bit(wil_status_napi_en, wil->status)) {
+ napi_synchronize(&wil->napi_rx);
+ napi_synchronize(&wil->napi_tx);
+ }
+ mutex_unlock(&wil->vif_mutex);
+
+ flush_work(&wil->wmi_event_worker);
+ del_timer_sync(&vif->connect_timer);
+ cancel_work_sync(&vif->disconnect_worker);
+ wil_probe_client_flush(vif);
+ cancel_work_sync(&vif->probe_client_worker);
+ /* for VIFs, ndev will be freed by destructor after RTNL is unlocked.
+ * the main interface will be freed in wil_if_free, we need to keep it
+ * a bit longer so logging macros will work.
+ */
+}
+
void wil_if_remove(struct wil6210_priv *wil)
{
- struct net_device *ndev = wil_to_ndev(wil);
- struct wireless_dev *wdev = wil_to_wdev(wil);
+ struct net_device *ndev = wil->main_ndev;
+ struct wireless_dev *wdev = ndev->ieee80211_ptr;
wil_dbg_misc(wil, "if_remove\n");
- unregister_netdev(ndev);
+ rtnl_lock();
+ wil_vif_remove(wil, 0);
+ rtnl_unlock();
+
+ netif_napi_del(&wil->napi_tx);
+ netif_napi_del(&wil->napi_rx);
+
wiphy_unregister(wdev->wiphy);
}
diff --git a/drivers/net/wireless/ath/wil6210/p2p.c b/drivers/net/wireless/ath/wil6210/p2p.c
index 7dbee2c3e482..db087ea58ddf 100644
--- a/drivers/net/wireless/ath/wil6210/p2p.c
+++ b/drivers/net/wireless/ath/wil6210/p2p.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2014-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -22,27 +23,28 @@
#define P2P_SEARCH_DURATION_MS 500
#define P2P_DEFAULT_BI 100
-static int wil_p2p_start_listen(struct wil6210_priv *wil)
+static int wil_p2p_start_listen(struct wil6210_vif *vif)
{
- struct wil_p2p_info *p2p = &wil->p2p;
+ struct wil6210_priv *wil = vif_to_wil(vif);
+ struct wil_p2p_info *p2p = &vif->p2p;
u8 channel = p2p->listen_chan.hw_value;
int rc;
lockdep_assert_held(&wil->mutex);
- rc = wmi_p2p_cfg(wil, channel, P2P_DEFAULT_BI);
+ rc = wmi_p2p_cfg(vif, channel, P2P_DEFAULT_BI);
if (rc) {
wil_err(wil, "wmi_p2p_cfg failed\n");
goto out;
}
- rc = wmi_set_ssid(wil, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID);
+ rc = wmi_set_ssid(vif, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID);
if (rc) {
wil_err(wil, "wmi_set_ssid failed\n");
goto out_stop;
}
- rc = wmi_start_listen(wil);
+ rc = wmi_start_listen(vif);
if (rc) {
wil_err(wil, "wmi_start_listen failed\n");
goto out_stop;
@@ -53,7 +55,7 @@ static int wil_p2p_start_listen(struct wil6210_priv *wil)
jiffies + msecs_to_jiffies(p2p->listen_duration));
out_stop:
if (rc)
- wmi_stop_discovery(wil);
+ wmi_stop_discovery(vif);
out:
return rc;
@@ -65,20 +67,12 @@ bool wil_p2p_is_social_scan(struct cfg80211_scan_request *request)
(request->channels[0]->hw_value == P2P_DMG_SOCIAL_CHANNEL);
}
-void wil_p2p_discovery_timer_fn(struct timer_list *t)
-{
- struct wil6210_priv *wil = from_timer(wil, t, p2p.discovery_timer);
-
- wil_dbg_misc(wil, "p2p_discovery_timer_fn\n");
-
- schedule_work(&wil->p2p.discovery_expired_work);
-}
-
-int wil_p2p_search(struct wil6210_priv *wil,
+int wil_p2p_search(struct wil6210_vif *vif,
struct cfg80211_scan_request *request)
{
+ struct wil6210_priv *wil = vif_to_wil(vif);
int rc;
- struct wil_p2p_info *p2p = &wil->p2p;
+ struct wil_p2p_info *p2p = &vif->p2p;
wil_dbg_misc(wil, "p2p_search: channel %d\n", P2P_DMG_SOCIAL_CHANNEL);
@@ -90,20 +84,20 @@ int wil_p2p_search(struct wil6210_priv *wil,
goto out;
}
- rc = wmi_p2p_cfg(wil, P2P_DMG_SOCIAL_CHANNEL, P2P_DEFAULT_BI);
+ rc = wmi_p2p_cfg(vif, P2P_DMG_SOCIAL_CHANNEL, P2P_DEFAULT_BI);
if (rc) {
wil_err(wil, "wmi_p2p_cfg failed\n");
goto out;
}
- rc = wmi_set_ssid(wil, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID);
+ rc = wmi_set_ssid(vif, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID);
if (rc) {
wil_err(wil, "wmi_set_ssid failed\n");
goto out_stop;
}
/* Set application IE to probe request and probe response */
- rc = wmi_set_ie(wil, WMI_FRAME_PROBE_REQ,
+ rc = wmi_set_ie(vif, WMI_FRAME_PROBE_REQ,
request->ie_len, request->ie);
if (rc) {
wil_err(wil, "wmi_set_ie(WMI_FRAME_PROBE_REQ) failed\n");
@@ -113,14 +107,14 @@ int wil_p2p_search(struct wil6210_priv *wil,
/* supplicant doesn't provide Probe Response IEs. As a workaround -
* re-use Probe Request IEs
*/
- rc = wmi_set_ie(wil, WMI_FRAME_PROBE_RESP,
+ rc = wmi_set_ie(vif, WMI_FRAME_PROBE_RESP,
request->ie_len, request->ie);
if (rc) {
wil_err(wil, "wmi_set_ie(WMI_FRAME_PROBE_RESP) failed\n");
goto out_stop;
}
- rc = wmi_start_search(wil);
+ rc = wmi_start_search(vif);
if (rc) {
wil_err(wil, "wmi_start_search failed\n");
goto out_stop;
@@ -133,7 +127,7 @@ int wil_p2p_search(struct wil6210_priv *wil,
out_stop:
if (rc)
- wmi_stop_discovery(wil);
+ wmi_stop_discovery(vif);
out:
return rc;
@@ -143,7 +137,8 @@ int wil_p2p_listen(struct wil6210_priv *wil, struct wireless_dev *wdev,
unsigned int duration, struct ieee80211_channel *chan,
u64 *cookie)
{
- struct wil_p2p_info *p2p = &wil->p2p;
+ struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
+ struct wil_p2p_info *p2p = &vif->p2p;
int rc;
if (!chan)
@@ -163,23 +158,24 @@ int wil_p2p_listen(struct wil6210_priv *wil, struct wireless_dev *wdev,
*cookie = ++p2p->cookie;
p2p->listen_duration = duration;
- mutex_lock(&wil->p2p_wdev_mutex);
- if (wil->scan_request) {
+ mutex_lock(&wil->vif_mutex);
+ if (vif->scan_request) {
wil_dbg_misc(wil, "Delaying p2p listen until scan done\n");
p2p->pending_listen_wdev = wdev;
p2p->discovery_started = 1;
rc = 0;
- mutex_unlock(&wil->p2p_wdev_mutex);
+ mutex_unlock(&wil->vif_mutex);
goto out;
}
- mutex_unlock(&wil->p2p_wdev_mutex);
+ mutex_unlock(&wil->vif_mutex);
- rc = wil_p2p_start_listen(wil);
+ rc = wil_p2p_start_listen(vif);
if (rc)
goto out;
p2p->discovery_started = 1;
- wil->radio_wdev = wdev;
+ if (vif->mid == 0)
+ wil->radio_wdev = wdev;
cfg80211_ready_on_channel(wdev, *cookie, chan, duration,
GFP_KERNEL);
@@ -189,9 +185,9 @@ out:
return rc;
}
-u8 wil_p2p_stop_discovery(struct wil6210_priv *wil)
+u8 wil_p2p_stop_discovery(struct wil6210_vif *vif)
{
- struct wil_p2p_info *p2p = &wil->p2p;
+ struct wil_p2p_info *p2p = &vif->p2p;
u8 started = p2p->discovery_started;
if (p2p->discovery_started) {
@@ -200,7 +196,7 @@ u8 wil_p2p_stop_discovery(struct wil6210_priv *wil)
p2p->pending_listen_wdev = NULL;
} else {
del_timer_sync(&p2p->discovery_timer);
- wmi_stop_discovery(wil);
+ wmi_stop_discovery(vif);
}
p2p->discovery_started = 0;
}
@@ -208,9 +204,10 @@ u8 wil_p2p_stop_discovery(struct wil6210_priv *wil)
return started;
}
-int wil_p2p_cancel_listen(struct wil6210_priv *wil, u64 cookie)
+int wil_p2p_cancel_listen(struct wil6210_vif *vif, u64 cookie)
{
- struct wil_p2p_info *p2p = &wil->p2p;
+ struct wil6210_priv *wil = vif_to_wil(vif);
+ struct wil_p2p_info *p2p = &vif->p2p;
u8 started;
mutex_lock(&wil->mutex);
@@ -222,7 +219,7 @@ int wil_p2p_cancel_listen(struct wil6210_priv *wil, u64 cookie)
return -ENOENT;
}
- started = wil_p2p_stop_discovery(wil);
+ started = wil_p2p_stop_discovery(vif);
mutex_unlock(&wil->mutex);
@@ -231,13 +228,14 @@ int wil_p2p_cancel_listen(struct wil6210_priv *wil, u64 cookie)
return -ENOENT;
}
- mutex_lock(&wil->p2p_wdev_mutex);
- cfg80211_remain_on_channel_expired(wil->radio_wdev,
+ mutex_lock(&wil->vif_mutex);
+ cfg80211_remain_on_channel_expired(vif_to_radio_wdev(wil, vif),
p2p->cookie,
&p2p->listen_chan,
GFP_KERNEL);
- wil->radio_wdev = wil->wdev;
- mutex_unlock(&wil->p2p_wdev_mutex);
+ if (vif->mid == 0)
+ wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
+ mutex_unlock(&wil->vif_mutex);
return 0;
}
@@ -245,40 +243,43 @@ void wil_p2p_listen_expired(struct work_struct *work)
{
struct wil_p2p_info *p2p = container_of(work,
struct wil_p2p_info, discovery_expired_work);
- struct wil6210_priv *wil = container_of(p2p,
- struct wil6210_priv, p2p);
+ struct wil6210_vif *vif = container_of(p2p,
+ struct wil6210_vif, p2p);
+ struct wil6210_priv *wil = vif_to_wil(vif);
u8 started;
wil_dbg_misc(wil, "p2p_listen_expired\n");
mutex_lock(&wil->mutex);
- started = wil_p2p_stop_discovery(wil);
+ started = wil_p2p_stop_discovery(vif);
mutex_unlock(&wil->mutex);
- if (started) {
- mutex_lock(&wil->p2p_wdev_mutex);
- cfg80211_remain_on_channel_expired(wil->radio_wdev,
- p2p->cookie,
- &p2p->listen_chan,
- GFP_KERNEL);
- wil->radio_wdev = wil->wdev;
- mutex_unlock(&wil->p2p_wdev_mutex);
- }
+ if (!started)
+ return;
+ mutex_lock(&wil->vif_mutex);
+ cfg80211_remain_on_channel_expired(vif_to_radio_wdev(wil, vif),
+ p2p->cookie,
+ &p2p->listen_chan,
+ GFP_KERNEL);
+ if (vif->mid == 0)
+ wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
+ mutex_unlock(&wil->vif_mutex);
}
void wil_p2p_search_expired(struct work_struct *work)
{
struct wil_p2p_info *p2p = container_of(work,
struct wil_p2p_info, discovery_expired_work);
- struct wil6210_priv *wil = container_of(p2p,
- struct wil6210_priv, p2p);
+ struct wil6210_vif *vif = container_of(p2p,
+ struct wil6210_vif, p2p);
+ struct wil6210_priv *wil = vif_to_wil(vif);
u8 started;
wil_dbg_misc(wil, "p2p_search_expired\n");
mutex_lock(&wil->mutex);
- started = wil_p2p_stop_discovery(wil);
+ started = wil_p2p_stop_discovery(vif);
mutex_unlock(&wil->mutex);
if (started) {
@@ -286,13 +287,15 @@ void wil_p2p_search_expired(struct work_struct *work)
.aborted = false,
};
- mutex_lock(&wil->p2p_wdev_mutex);
- if (wil->scan_request) {
- cfg80211_scan_done(wil->scan_request, &info);
- wil->scan_request = NULL;
- wil->radio_wdev = wil->wdev;
+ mutex_lock(&wil->vif_mutex);
+ if (vif->scan_request) {
+ cfg80211_scan_done(vif->scan_request, &info);
+ vif->scan_request = NULL;
+ if (vif->mid == 0)
+ wil->radio_wdev =
+ wil->main_ndev->ieee80211_ptr;
}
- mutex_unlock(&wil->p2p_wdev_mutex);
+ mutex_unlock(&wil->vif_mutex);
}
}
@@ -300,8 +303,9 @@ void wil_p2p_delayed_listen_work(struct work_struct *work)
{
struct wil_p2p_info *p2p = container_of(work,
struct wil_p2p_info, delayed_listen_work);
- struct wil6210_priv *wil = container_of(p2p,
- struct wil6210_priv, p2p);
+ struct wil6210_vif *vif = container_of(p2p,
+ struct wil6210_vif, p2p);
+ struct wil6210_priv *wil = vif_to_wil(vif);
int rc;
mutex_lock(&wil->mutex);
@@ -310,31 +314,33 @@ void wil_p2p_delayed_listen_work(struct work_struct *work)
if (!p2p->discovery_started || !p2p->pending_listen_wdev)
goto out;
- mutex_lock(&wil->p2p_wdev_mutex);
- if (wil->scan_request) {
+ mutex_lock(&wil->vif_mutex);
+ if (vif->scan_request) {
/* another scan started, wait again... */
- mutex_unlock(&wil->p2p_wdev_mutex);
+ mutex_unlock(&wil->vif_mutex);
goto out;
}
- mutex_unlock(&wil->p2p_wdev_mutex);
+ mutex_unlock(&wil->vif_mutex);
- rc = wil_p2p_start_listen(wil);
+ rc = wil_p2p_start_listen(vif);
- mutex_lock(&wil->p2p_wdev_mutex);
+ mutex_lock(&wil->vif_mutex);
if (rc) {
cfg80211_remain_on_channel_expired(p2p->pending_listen_wdev,
p2p->cookie,
&p2p->listen_chan,
GFP_KERNEL);
- wil->radio_wdev = wil->wdev;
+ if (vif->mid == 0)
+ wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
} else {
cfg80211_ready_on_channel(p2p->pending_listen_wdev, p2p->cookie,
&p2p->listen_chan,
p2p->listen_duration, GFP_KERNEL);
- wil->radio_wdev = p2p->pending_listen_wdev;
+ if (vif->mid == 0)
+ wil->radio_wdev = p2p->pending_listen_wdev;
}
p2p->pending_listen_wdev = NULL;
- mutex_unlock(&wil->p2p_wdev_mutex);
+ mutex_unlock(&wil->vif_mutex);
out:
mutex_unlock(&wil->mutex);
@@ -342,34 +348,35 @@ out:
void wil_p2p_stop_radio_operations(struct wil6210_priv *wil)
{
- struct wil_p2p_info *p2p = &wil->p2p;
+ struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
+ struct wil_p2p_info *p2p = &vif->p2p;
struct cfg80211_scan_info info = {
.aborted = true,
};
lockdep_assert_held(&wil->mutex);
- lockdep_assert_held(&wil->p2p_wdev_mutex);
+ lockdep_assert_held(&wil->vif_mutex);
if (wil->radio_wdev != wil->p2p_wdev)
goto out;
if (!p2p->discovery_started) {
/* Regular scan on the p2p device */
- if (wil->scan_request &&
- wil->scan_request->wdev == wil->p2p_wdev)
- wil_abort_scan(wil, true);
+ if (vif->scan_request &&
+ vif->scan_request->wdev == wil->p2p_wdev)
+ wil_abort_scan(vif, true);
goto out;
}
/* Search or listen on p2p device */
- mutex_unlock(&wil->p2p_wdev_mutex);
- wil_p2p_stop_discovery(wil);
- mutex_lock(&wil->p2p_wdev_mutex);
+ mutex_unlock(&wil->vif_mutex);
+ wil_p2p_stop_discovery(vif);
+ mutex_lock(&wil->vif_mutex);
- if (wil->scan_request) {
+ if (vif->scan_request) {
/* search */
- cfg80211_scan_done(wil->scan_request, &info);
- wil->scan_request = NULL;
+ cfg80211_scan_done(vif->scan_request, &info);
+ vif->scan_request = NULL;
} else {
/* listen */
cfg80211_remain_on_channel_expired(wil->radio_wdev,
@@ -379,5 +386,5 @@ void wil_p2p_stop_radio_operations(struct wil6210_priv *wil)
}
out:
- wil->radio_wdev = wil->wdev;
+ wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
}
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index 809092a49192..19cbc6add637 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -137,6 +137,20 @@ void wil_enable_irq(struct wil6210_priv *wil)
enable_irq(wil->pdev->irq);
}
+static void wil_remove_all_additional_vifs(struct wil6210_priv *wil)
+{
+ struct wil6210_vif *vif;
+ int i;
+
+ for (i = 1; i < wil->max_vifs; i++) {
+ vif = wil->vifs[i];
+ if (vif) {
+ wil_vif_prepare_stop(vif);
+ wil_vif_remove(wil, vif->mid);
+ }
+ }
+}
+
/* Bus ops */
static int wil_if_pcie_enable(struct wil6210_priv *wil)
{
@@ -148,10 +162,8 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil)
*/
int msi_only = pdev->msi_enabled;
bool _use_msi = use_msi;
- bool wmi_only = test_bit(WMI_FW_CAPABILITY_WMI_ONLY,
- wil->fw_capabilities);
- wil_dbg_misc(wil, "if_pcie_enable, wmi_only %d\n", wmi_only);
+ wil_dbg_misc(wil, "if_pcie_enable\n");
pci_set_master(pdev);
@@ -172,11 +184,9 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil)
if (rc)
goto stop_master;
- /* need reset here to obtain MAC or in case of WMI-only FW, full reset
- * and fw loading takes place
- */
+ /* need reset here to obtain MAC */
mutex_lock(&wil->mutex);
- rc = wil_reset(wil, wmi_only);
+ rc = wil_reset(wil, false);
mutex_unlock(&wil->mutex);
if (rc)
goto release_irq;
@@ -356,6 +366,18 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto bus_disable;
}
+ /* in case of WMI-only FW, perform full reset and FW loading */
+ if (test_bit(WMI_FW_CAPABILITY_WMI_ONLY, wil->fw_capabilities)) {
+ wil_dbg_misc(wil, "Loading WMI only FW\n");
+ mutex_lock(&wil->mutex);
+ rc = wil_reset(wil, true);
+ mutex_unlock(&wil->mutex);
+ if (rc) {
+ wil_err(wil, "failed to load WMI only FW\n");
+ goto if_remove;
+ }
+ }
+
if (IS_ENABLED(CONFIG_PM))
wil->pm_notify.notifier_call = wil6210_pm_notify;
@@ -372,6 +394,8 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return 0;
+if_remove:
+ wil_if_remove(wil);
bus_disable:
wil_if_pcie_disable(wil);
err_iounmap:
@@ -402,6 +426,7 @@ static void wil_pcie_remove(struct pci_dev *pdev)
wil6210_debugfs_remove(wil);
rtnl_lock();
wil_p2p_wdev_free(wil);
+ wil_remove_all_additional_vifs(wil);
rtnl_unlock();
wil_if_remove(wil);
wil_if_pcie_disable(wil);
@@ -425,12 +450,15 @@ static int wil6210_suspend(struct device *dev, bool is_runtime)
int rc = 0;
struct pci_dev *pdev = to_pci_dev(dev);
struct wil6210_priv *wil = pci_get_drvdata(pdev);
- struct net_device *ndev = wil_to_ndev(wil);
- bool keep_radio_on = ndev->flags & IFF_UP &&
- wil->keep_radio_on_during_sleep;
+ bool keep_radio_on, active_ifaces;
wil_dbg_pm(wil, "suspend: %s\n", is_runtime ? "runtime" : "system");
+ mutex_lock(&wil->vif_mutex);
+ active_ifaces = wil_has_active_ifaces(wil, true, false);
+ mutex_unlock(&wil->vif_mutex);
+ keep_radio_on = active_ifaces && wil->keep_radio_on_during_sleep;
+
rc = wil_can_suspend(wil, is_runtime);
if (rc)
goto out;
@@ -457,12 +485,15 @@ static int wil6210_resume(struct device *dev, bool is_runtime)
int rc = 0;
struct pci_dev *pdev = to_pci_dev(dev);
struct wil6210_priv *wil = pci_get_drvdata(pdev);
- struct net_device *ndev = wil_to_ndev(wil);
- bool keep_radio_on = ndev->flags & IFF_UP &&
- wil->keep_radio_on_during_sleep;
+ bool keep_radio_on, active_ifaces;
wil_dbg_pm(wil, "resume: %s\n", is_runtime ? "runtime" : "system");
+ mutex_lock(&wil->vif_mutex);
+ active_ifaces = wil_has_active_ifaces(wil, true, false);
+ mutex_unlock(&wil->vif_mutex);
+ keep_radio_on = active_ifaces && wil->keep_radio_on_during_sleep;
+
/* In case radio stays on, platform device will control
* PCIe master
*/
diff --git a/drivers/net/wireless/ath/wil6210/pm.c b/drivers/net/wireless/ath/wil6210/pm.c
index 0a96518a566f..ba81fb3ac96f 100644
--- a/drivers/net/wireless/ath/wil6210/pm.c
+++ b/drivers/net/wireless/ath/wil6210/pm.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2014,2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -20,13 +21,72 @@
#define WIL6210_AUTOSUSPEND_DELAY_MS (1000)
+static void wil_pm_wake_connected_net_queues(struct wil6210_priv *wil)
+{
+ int i;
+
+ mutex_lock(&wil->vif_mutex);
+ for (i = 0; i < wil->max_vifs; i++) {
+ struct wil6210_vif *vif = wil->vifs[i];
+
+ if (vif && test_bit(wil_vif_fwconnected, vif->status))
+ wil_update_net_queues_bh(wil, vif, NULL, false);
+ }
+ mutex_unlock(&wil->vif_mutex);
+}
+
+static void wil_pm_stop_all_net_queues(struct wil6210_priv *wil)
+{
+ int i;
+
+ mutex_lock(&wil->vif_mutex);
+ for (i = 0; i < wil->max_vifs; i++) {
+ struct wil6210_vif *vif = wil->vifs[i];
+
+ if (vif)
+ wil_update_net_queues_bh(wil, vif, NULL, true);
+ }
+ mutex_unlock(&wil->vif_mutex);
+}
+
+static bool
+wil_can_suspend_vif(struct wil6210_priv *wil, struct wil6210_vif *vif,
+ bool is_runtime)
+{
+ struct wireless_dev *wdev = vif_to_wdev(vif);
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_MONITOR:
+ wil_dbg_pm(wil, "Sniffer\n");
+ return false;
+
+ /* for STA-like interface, don't runtime suspend */
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ if (test_bit(wil_vif_fwconnecting, vif->status)) {
+ wil_dbg_pm(wil, "Delay suspend when connecting\n");
+ return false;
+ }
+ if (is_runtime) {
+ wil_dbg_pm(wil, "STA-like interface\n");
+ return false;
+ }
+ break;
+ /* AP-like interface - can't suspend */
+ default:
+ wil_dbg_pm(wil, "AP-like interface\n");
+ return false;
+ }
+
+ return true;
+}
+
int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime)
{
- int rc = 0;
- struct wireless_dev *wdev = wil->wdev;
- struct net_device *ndev = wil_to_ndev(wil);
+ int rc = 0, i;
bool wmi_only = test_bit(WMI_FW_CAPABILITY_WMI_ONLY,
wil->fw_capabilities);
+ bool active_ifaces;
wil_dbg_pm(wil, "can_suspend: %s\n", is_runtime ? "runtime" : "system");
@@ -40,7 +100,12 @@ int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime)
rc = -EBUSY;
goto out;
}
- if (!(ndev->flags & IFF_UP)) {
+
+ mutex_lock(&wil->vif_mutex);
+ active_ifaces = wil_has_active_ifaces(wil, true, false);
+ mutex_unlock(&wil->vif_mutex);
+
+ if (!active_ifaces) {
/* can always sleep when down */
wil_dbg_pm(wil, "Interface is down\n");
goto out;
@@ -57,32 +122,19 @@ int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime)
}
/* interface is running */
- switch (wdev->iftype) {
- case NL80211_IFTYPE_MONITOR:
- wil_dbg_pm(wil, "Sniffer\n");
- rc = -EBUSY;
- goto out;
- /* for STA-like interface, don't runtime suspend */
- case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_P2P_CLIENT:
- if (test_bit(wil_status_fwconnecting, wil->status)) {
- wil_dbg_pm(wil, "Delay suspend when connecting\n");
- rc = -EBUSY;
- goto out;
- }
- /* Runtime pm not supported in case the interface is up */
- if (is_runtime) {
- wil_dbg_pm(wil, "STA-like interface\n");
+ mutex_lock(&wil->vif_mutex);
+ for (i = 0; i < wil->max_vifs; i++) {
+ struct wil6210_vif *vif = wil->vifs[i];
+
+ if (!vif)
+ continue;
+ if (!wil_can_suspend_vif(wil, vif, is_runtime)) {
rc = -EBUSY;
+ mutex_unlock(&wil->vif_mutex);
goto out;
}
- break;
- /* AP-like interface - can't suspend */
- default:
- wil_dbg_pm(wil, "AP-like interface\n");
- rc = -EBUSY;
- break;
}
+ mutex_unlock(&wil->vif_mutex);
out:
wil_dbg_pm(wil, "can_suspend: %s => %s (%d)\n",
@@ -127,8 +179,7 @@ static int wil_resume_keep_radio_on(struct wil6210_priv *wil)
}
/* Wake all queues */
- if (test_bit(wil_status_fwconnected, wil->status))
- wil_update_net_queues_bh(wil, NULL, false);
+ wil_pm_wake_connected_net_queues(wil);
out:
if (rc)
@@ -152,7 +203,7 @@ static int wil_suspend_keep_radio_on(struct wil6210_priv *wil)
wil->suspend_stats.rejected_by_host++;
return -EBUSY;
}
- wil_update_net_queues_bh(wil, NULL, true);
+ wil_pm_stop_all_net_queues(wil);
if (!wil_is_tx_idle(wil)) {
wil_dbg_pm(wil, "Pending TX data, reject suspend\n");
@@ -243,22 +294,20 @@ resume_after_fail:
/* if resume succeeded, reject the suspend */
if (!rc) {
rc = -EBUSY;
- if (test_bit(wil_status_fwconnected, wil->status))
- wil_update_net_queues_bh(wil, NULL, false);
+ wil_pm_wake_connected_net_queues(wil);
}
return rc;
reject_suspend:
clear_bit(wil_status_suspending, wil->status);
- if (test_bit(wil_status_fwconnected, wil->status))
- wil_update_net_queues_bh(wil, NULL, false);
+ wil_pm_wake_connected_net_queues(wil);
return -EBUSY;
}
static int wil_suspend_radio_off(struct wil6210_priv *wil)
{
int rc = 0;
- struct net_device *ndev = wil_to_ndev(wil);
+ bool active_ifaces;
wil_dbg_pm(wil, "suspend radio off\n");
@@ -272,7 +321,11 @@ static int wil_suspend_radio_off(struct wil6210_priv *wil)
}
/* if netif up, hardware is alive, shut it down */
- if (ndev->flags & IFF_UP) {
+ mutex_lock(&wil->vif_mutex);
+ active_ifaces = wil_has_active_ifaces(wil, true, false);
+ mutex_unlock(&wil->vif_mutex);
+
+ if (active_ifaces) {
rc = wil_down(wil);
if (rc) {
wil_err(wil, "wil_down : %d\n", rc);
@@ -306,16 +359,19 @@ out:
static int wil_resume_radio_off(struct wil6210_priv *wil)
{
int rc = 0;
- struct net_device *ndev = wil_to_ndev(wil);
+ bool active_ifaces;
wil_dbg_pm(wil, "Enabling PCIe IRQ\n");
wil_enable_irq(wil);
- /* if netif up, bring hardware up
+ /* if any netif up, bring hardware up
* During open(), IFF_UP set after actual device method
* invocation. This prevent recursive call to wil_up()
* wil_status_suspended will be cleared in wil_reset
*/
- if (ndev->flags & IFF_UP)
+ mutex_lock(&wil->vif_mutex);
+ active_ifaces = wil_has_active_ifaces(wil, true, false);
+ mutex_unlock(&wil->vif_mutex);
+ if (active_ifaces)
rc = wil_up(wil);
else
clear_bit(wil_status_suspended, wil->status);
diff --git a/drivers/net/wireless/ath/wil6210/pmc.c b/drivers/net/wireless/ath/wil6210/pmc.c
index 4ea27b0bd278..c49f7988369e 100644
--- a/drivers/net/wireless/ath/wil6210/pmc.c
+++ b/drivers/net/wireless/ath/wil6210/pmc.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2012-2015,2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -53,6 +54,7 @@ void wil_pmc_alloc(struct wil6210_priv *wil,
u32 i;
struct pmc_ctx *pmc = &wil->pmc;
struct device *dev = wil_to_dev(wil);
+ struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
struct wmi_pmc_cmd pmc_cmd = {0};
int last_cmd_err = -ENOMEM;
@@ -186,6 +188,7 @@ void wil_pmc_alloc(struct wil6210_priv *wil,
wil_dbg_misc(wil, "pmc_alloc: send WMI_PMC_CMD with ALLOCATE op\n");
pmc->last_cmd_status = wmi_send(wil,
WMI_PMC_CMDID,
+ vif->mid,
&pmc_cmd,
sizeof(pmc_cmd));
if (pmc->last_cmd_status) {
@@ -236,6 +239,7 @@ void wil_pmc_free(struct wil6210_priv *wil, int send_pmc_cmd)
{
struct pmc_ctx *pmc = &wil->pmc;
struct device *dev = wil_to_dev(wil);
+ struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
struct wmi_pmc_cmd pmc_cmd = {0};
mutex_lock(&pmc->lock);
@@ -254,8 +258,8 @@ void wil_pmc_free(struct wil6210_priv *wil, int send_pmc_cmd)
wil_dbg_misc(wil, "send WMI_PMC_CMD with RELEASE op\n");
pmc_cmd.op = WMI_PMC_RELEASE;
pmc->last_cmd_status =
- wmi_send(wil, WMI_PMC_CMDID, &pmc_cmd,
- sizeof(pmc_cmd));
+ wmi_send(wil, WMI_PMC_CMDID, vif->mid,
+ &pmc_cmd, sizeof(pmc_cmd));
if (pmc->last_cmd_status) {
wil_err(wil,
"WMI_PMC_CMD with RELEASE op failed, status %d",
diff --git a/drivers/net/wireless/ath/wil6210/rx_reorder.c b/drivers/net/wireless/ath/wil6210/rx_reorder.c
index a43cffcf1bbf..14dcb0698dee 100644
--- a/drivers/net/wireless/ath/wil6210/rx_reorder.c
+++ b/drivers/net/wireless/ath/wil6210/rx_reorder.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2014-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -40,11 +41,10 @@ static inline int reorder_index(struct wil_tid_ampdu_rx *r, u16 seq)
return seq_sub(seq, r->ssn) % r->buf_size;
}
-static void wil_release_reorder_frame(struct wil6210_priv *wil,
+static void wil_release_reorder_frame(struct net_device *ndev,
struct wil_tid_ampdu_rx *r,
int index)
{
- struct net_device *ndev = wil_to_ndev(wil);
struct sk_buff *skb = r->reorder_buf[index];
if (!skb)
@@ -59,7 +59,7 @@ no_frame:
r->head_seq_num = seq_inc(r->head_seq_num);
}
-static void wil_release_reorder_frames(struct wil6210_priv *wil,
+static void wil_release_reorder_frames(struct net_device *ndev,
struct wil_tid_ampdu_rx *r,
u16 hseq)
{
@@ -73,18 +73,18 @@ static void wil_release_reorder_frames(struct wil6210_priv *wil,
*/
while (seq_less(r->head_seq_num, hseq) && r->stored_mpdu_num) {
index = reorder_index(r, r->head_seq_num);
- wil_release_reorder_frame(wil, r, index);
+ wil_release_reorder_frame(ndev, r, index);
}
r->head_seq_num = hseq;
}
-static void wil_reorder_release(struct wil6210_priv *wil,
+static void wil_reorder_release(struct net_device *ndev,
struct wil_tid_ampdu_rx *r)
{
int index = reorder_index(r, r->head_seq_num);
while (r->reorder_buf[index]) {
- wil_release_reorder_frame(wil, r, index);
+ wil_release_reorder_frame(ndev, r, index);
index = reorder_index(r, r->head_seq_num);
}
}
@@ -93,7 +93,8 @@ static void wil_reorder_release(struct wil6210_priv *wil,
void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb)
__acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
{
- struct net_device *ndev = wil_to_ndev(wil);
+ struct wil6210_vif *vif;
+ struct net_device *ndev;
struct vring_rx_desc *d = wil_skb_rxdesc(skb);
int tid = wil_rxdesc_tid(d);
int cid = wil_rxdesc_cid(d);
@@ -108,6 +109,14 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
wil_dbg_txrx(wil, "MID %d CID %d TID %d Seq 0x%03x mcast %01x\n",
mid, cid, tid, seq, mcast);
+ vif = wil->vifs[mid];
+ if (unlikely(!vif)) {
+ wil_dbg_txrx(wil, "invalid VIF, mid %d\n", mid);
+ dev_kfree_skb(skb);
+ return;
+ }
+ ndev = vif_to_ndev(vif);
+
if (unlikely(mcast)) {
wil_netif_rx_any(skb, ndev);
return;
@@ -168,7 +177,7 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
if (!seq_less(seq, r->head_seq_num + r->buf_size)) {
hseq = seq_inc(seq_sub(seq, r->buf_size));
/* release stored frames up to new head to stack */
- wil_release_reorder_frames(wil, r, hseq);
+ wil_release_reorder_frames(ndev, r, hseq);
}
/* Now the new frame is always in the range of the reordering buffer */
@@ -199,16 +208,18 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
r->reorder_buf[index] = skb;
r->reorder_time[index] = jiffies;
r->stored_mpdu_num++;
- wil_reorder_release(wil, r);
+ wil_reorder_release(ndev, r);
out:
spin_unlock(&sta->tid_rx_lock);
}
/* process BAR frame, called in NAPI context */
-void wil_rx_bar(struct wil6210_priv *wil, u8 cid, u8 tid, u16 seq)
+void wil_rx_bar(struct wil6210_priv *wil, struct wil6210_vif *vif,
+ u8 cid, u8 tid, u16 seq)
{
struct wil_sta_info *sta = &wil->sta[cid];
+ struct net_device *ndev = vif_to_ndev(vif);
struct wil_tid_ampdu_rx *r;
spin_lock(&sta->tid_rx_lock);
@@ -223,9 +234,9 @@ void wil_rx_bar(struct wil6210_priv *wil, u8 cid, u8 tid, u16 seq)
seq, r->head_seq_num);
goto out;
}
- wil_dbg_txrx(wil, "BAR: CID %d TID %d Seq 0x%03x head 0x%03x\n",
- cid, tid, seq, r->head_seq_num);
- wil_release_reorder_frames(wil, r, seq);
+ wil_dbg_txrx(wil, "BAR: CID %d MID %d TID %d Seq 0x%03x head 0x%03x\n",
+ cid, vif->mid, tid, seq, r->head_seq_num);
+ wil_release_reorder_frames(ndev, r, seq);
out:
spin_unlock(&sta->tid_rx_lock);
@@ -292,8 +303,8 @@ static u16 wil_agg_size(struct wil6210_priv *wil, u16 req_agg_wsize)
}
/* Block Ack - Rx side (recipient) */
-int wil_addba_rx_request(struct wil6210_priv *wil, u8 cidxtid,
- u8 dialog_token, __le16 ba_param_set,
+int wil_addba_rx_request(struct wil6210_priv *wil, u8 mid,
+ u8 cidxtid, u8 dialog_token, __le16 ba_param_set,
__le16 ba_timeout, __le16 ba_seq_ctrl)
__acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
{
@@ -354,7 +365,7 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
}
}
- rc = wmi_addba_rx_resp(wil, cid, tid, dialog_token, status,
+ rc = wmi_addba_rx_resp(wil, mid, cid, tid, dialog_token, status,
agg_amsdu, agg_wsize, agg_timeout);
if (rc || (status != WLAN_STATUS_SUCCESS)) {
wil_err(wil, "do not apply ba, rc(%d), status(%d)\n", rc,
@@ -393,7 +404,7 @@ int wil_addba_tx_request(struct wil6210_priv *wil, u8 ringid, u16 wsize)
goto out;
}
txdata->addba_in_progress = true;
- rc = wmi_addba(wil, ringid, agg_wsize, agg_timeout);
+ rc = wmi_addba(wil, txdata->mid, ringid, agg_wsize, agg_timeout);
if (rc) {
wil_err(wil, "wmi_addba failed, rc (%d)", rc);
txdata->addba_in_progress = false;
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index 16b8a4e5201f..b60b9fcaaebd 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -474,7 +475,8 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
struct vring *vring)
{
struct device *dev = wil_to_dev(wil);
- struct net_device *ndev = wil_to_ndev(wil);
+ struct wil6210_vif *vif;
+ struct net_device *ndev;
volatile struct vring_rx_desc *_d;
struct vring_rx_desc *d;
struct sk_buff *skb;
@@ -483,7 +485,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
unsigned int sz = wil->rx_buf_len + ETH_HLEN + snaplen;
u16 dmalen;
u8 ftype;
- int cid;
+ int cid, mid;
int i;
struct wil_net_stats *stats;
@@ -520,6 +522,16 @@ again:
(const void *)d, sizeof(*d), false);
cid = wil_rxdesc_cid(d);
+ mid = wil_rxdesc_mid(d);
+ vif = wil->vifs[mid];
+
+ if (unlikely(!vif)) {
+ wil_dbg_txrx(wil, "skipped RX descriptor with invalid mid %d",
+ mid);
+ kfree_skb(skb);
+ goto again;
+ }
+ ndev = vif_to_ndev(vif);
stats = &wil->sta[cid].stats;
if (unlikely(dmalen > sz)) {
@@ -553,7 +565,6 @@ again:
ftype = wil_rxdesc_ftype(d) << 2;
if (unlikely(ftype != IEEE80211_FTYPE_DATA)) {
u8 fc1 = wil_rxdesc_fc1(d);
- int mid = wil_rxdesc_mid(d);
int tid = wil_rxdesc_tid(d);
u16 seq = wil_rxdesc_seq(d);
@@ -565,7 +576,7 @@ again:
wil_dbg_txrx(wil,
"BAR: MID %d CID %d TID %d Seq 0x%03x\n",
mid, cid, tid, seq);
- wil_rx_bar(wil, cid, tid, seq);
+ wil_rx_bar(wil, vif, cid, tid, seq);
} else {
/* print again all info. One can enable only this
* without overhead for printing every Rx frame
@@ -621,10 +632,15 @@ again:
/**
* allocate and fill up to @count buffers in rx ring
* buffers posted at @swtail
+ * Note: we have a single RX queue for servicing all VIFs, but we
+ * allocate skbs with headroom according to main interface only. This
+ * means it will not work with monitor interface together with other VIFs.
+ * Currently we only support monitor interface on its own without other VIFs,
+ * and we will need to fix this code once we add support.
*/
static int wil_rx_refill(struct wil6210_priv *wil, int count)
{
- struct net_device *ndev = wil_to_ndev(wil);
+ struct net_device *ndev = wil->main_ndev;
struct vring *v = &wil->vring_rx;
u32 next_tail;
int rc = 0;
@@ -713,8 +729,9 @@ static int wil_rx_crypto_check(struct wil6210_priv *wil, struct sk_buff *skb)
void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
{
gro_result_t rc = GRO_NORMAL;
+ struct wil6210_vif *vif = ndev_to_vif(ndev);
struct wil6210_priv *wil = ndev_to_wil(ndev);
- struct wireless_dev *wdev = wil_to_wdev(wil);
+ struct wireless_dev *wdev = vif_to_wdev(vif);
unsigned int len = skb->len;
struct vring_rx_desc *d = wil_skb_rxdesc(skb);
int cid = wil_rxdesc_cid(d); /* always 0..7, no need to check */
@@ -751,14 +768,15 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
goto stats;
}
- if (wdev->iftype == NL80211_IFTYPE_AP && !wil->ap_isolate) {
+ if (wdev->iftype == NL80211_IFTYPE_AP && !vif->ap_isolate) {
if (mcast) {
/* send multicast frames both to higher layers in
* local net stack and back to the wireless medium
*/
xmit_skb = skb_copy(skb, GFP_ATOMIC);
} else {
- int xmit_cid = wil_find_cid(wil, eth->h_dest);
+ int xmit_cid = wil_find_cid(wil, vif->mid,
+ eth->h_dest);
if (xmit_cid >= 0) {
/* The destination station is associated to
@@ -786,8 +804,8 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
}
if (skb) { /* deliver to local stack */
-
skb->protocol = eth_type_trans(skb, ndev);
+ skb->dev = ndev;
rc = napi_gro_receive(&wil->napi_rx, skb);
wil_dbg_txrx(wil, "Rx complete %d bytes => %s\n",
len, gro_res_str[rc]);
@@ -815,7 +833,8 @@ stats:
*/
void wil_rx_handle(struct wil6210_priv *wil, int *quota)
{
- struct net_device *ndev = wil_to_ndev(wil);
+ struct net_device *ndev = wil->main_ndev;
+ struct wireless_dev *wdev = ndev->ieee80211_ptr;
struct vring *v = &wil->vring_rx;
struct sk_buff *skb;
@@ -827,7 +846,8 @@ void wil_rx_handle(struct wil6210_priv *wil, int *quota)
while ((*quota > 0) && (NULL != (skb = wil_vring_reap_rx(wil, v)))) {
(*quota)--;
- if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) {
+ /* monitor is currently supported on main interface only */
+ if (wdev->iftype == NL80211_IFTYPE_MONITOR) {
skb->dev = ndev;
skb_reset_mac_header(skb);
skb->ip_summed = CHECKSUM_UNNECESSARY;
@@ -911,12 +931,14 @@ static inline void wil_tx_data_init(struct vring_tx_data *txdata)
txdata->agg_timeout = 0;
txdata->agg_amsdu = 0;
txdata->addba_in_progress = false;
+ txdata->mid = U8_MAX;
spin_unlock_bh(&txdata->lock);
}
-int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
+int wil_vring_init_tx(struct wil6210_vif *vif, int id, int size,
int cid, int tid)
{
+ struct wil6210_priv *wil = vif_to_wil(vif);
int rc;
struct wmi_vring_cfg_cmd cmd = {
.action = cpu_to_le32(WMI_VRING_CMD_ADD),
@@ -966,9 +988,9 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
cmd.vring_cfg.tx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa);
- if (!wil->privacy)
+ if (!vif->privacy)
txdata->dot1x_open = true;
- rc = wmi_call(wil, WMI_VRING_CFG_CMDID, &cmd, sizeof(cmd),
+ rc = wmi_call(wil, WMI_VRING_CFG_CMDID, vif->mid, &cmd, sizeof(cmd),
WMI_VRING_CFG_DONE_EVENTID, &reply, sizeof(reply), 100);
if (rc)
goto out_free;
@@ -982,6 +1004,7 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
spin_lock_bh(&txdata->lock);
vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr);
+ txdata->mid = vif->mid;
txdata->enabled = 1;
spin_unlock_bh(&txdata->lock);
@@ -1003,8 +1026,9 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
return rc;
}
-int wil_vring_init_bcast(struct wil6210_priv *wil, int id, int size)
+int wil_vring_init_bcast(struct wil6210_vif *vif, int id, int size)
{
+ struct wil6210_priv *wil = vif_to_wil(vif);
int rc;
struct wmi_bcast_vring_cfg_cmd cmd = {
.action = cpu_to_le32(WMI_VRING_CMD_ADD),
@@ -1046,9 +1070,10 @@ int wil_vring_init_bcast(struct wil6210_priv *wil, int id, int size)
cmd.vring_cfg.tx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa);
- if (!wil->privacy)
+ if (!vif->privacy)
txdata->dot1x_open = true;
- rc = wmi_call(wil, WMI_BCAST_VRING_CFG_CMDID, &cmd, sizeof(cmd),
+ rc = wmi_call(wil, WMI_BCAST_VRING_CFG_CMDID, vif->mid,
+ &cmd, sizeof(cmd),
WMI_VRING_CFG_DONE_EVENTID, &reply, sizeof(reply), 100);
if (rc)
goto out_free;
@@ -1062,6 +1087,7 @@ int wil_vring_init_bcast(struct wil6210_priv *wil, int id, int size)
spin_lock_bh(&txdata->lock);
vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr);
+ txdata->mid = vif->mid;
txdata->enabled = 1;
spin_unlock_bh(&txdata->lock);
@@ -1091,6 +1117,7 @@ void wil_vring_fini_tx(struct wil6210_priv *wil, int id)
spin_lock_bh(&txdata->lock);
txdata->dot1x_open = false;
+ txdata->mid = U8_MAX;
txdata->enabled = 0; /* no Tx can be in progress or start anew */
spin_unlock_bh(&txdata->lock);
/* napi_synchronize waits for completion of the current NAPI but will
@@ -1108,11 +1135,12 @@ void wil_vring_fini_tx(struct wil6210_priv *wil, int id)
}
static struct vring *wil_find_tx_ucast(struct wil6210_priv *wil,
+ struct wil6210_vif *vif,
struct sk_buff *skb)
{
int i;
struct ethhdr *eth = (void *)skb->data;
- int cid = wil_find_cid(wil, eth->h_dest);
+ int cid = wil_find_cid(wil, vif->mid, eth->h_dest);
if (cid < 0)
return NULL;
@@ -1142,10 +1170,11 @@ static struct vring *wil_find_tx_ucast(struct wil6210_priv *wil,
return NULL;
}
-static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
- struct sk_buff *skb);
+static int wil_tx_vring(struct wil6210_priv *wil, struct wil6210_vif *vif,
+ struct vring *vring, struct sk_buff *skb);
static struct vring *wil_find_tx_vring_sta(struct wil6210_priv *wil,
+ struct wil6210_vif *vif,
struct sk_buff *skb)
{
struct vring *v;
@@ -1160,7 +1189,7 @@ static struct vring *wil_find_tx_vring_sta(struct wil6210_priv *wil,
for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
v = &wil->vring_tx[i];
txdata = &wil->vring_tx_data[i];
- if (!v->va || !txdata->enabled)
+ if (!v->va || !txdata->enabled || txdata->mid != vif->mid)
continue;
cid = wil->vring2cid_tid[i][0];
@@ -1193,11 +1222,12 @@ static struct vring *wil_find_tx_vring_sta(struct wil6210_priv *wil,
* - for PBSS
*/
static struct vring *wil_find_tx_bcast_1(struct wil6210_priv *wil,
+ struct wil6210_vif *vif,
struct sk_buff *skb)
{
struct vring *v;
struct vring_tx_data *txdata;
- int i = wil->bcast_vring;
+ int i = vif->bcast_vring;
if (i < 0)
return NULL;
@@ -1222,6 +1252,7 @@ static void wil_set_da_for_vring(struct wil6210_priv *wil,
}
static struct vring *wil_find_tx_bcast_2(struct wil6210_priv *wil,
+ struct wil6210_vif *vif,
struct sk_buff *skb)
{
struct vring *v, *v2;
@@ -1230,13 +1261,13 @@ static struct vring *wil_find_tx_bcast_2(struct wil6210_priv *wil,
u8 cid;
struct ethhdr *eth = (void *)skb->data;
char *src = eth->h_source;
- struct vring_tx_data *txdata;
+ struct vring_tx_data *txdata, *txdata2;
/* find 1-st vring eligible for data */
for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
v = &wil->vring_tx[i];
txdata = &wil->vring_tx_data[i];
- if (!v->va || !txdata->enabled)
+ if (!v->va || !txdata->enabled || txdata->mid != vif->mid)
continue;
cid = wil->vring2cid_tid[i][0];
@@ -1264,7 +1295,8 @@ found:
/* find other active vrings and duplicate skb for each */
for (i++; i < WIL6210_MAX_TX_RINGS; i++) {
v2 = &wil->vring_tx[i];
- if (!v2->va)
+ txdata2 = &wil->vring_tx_data[i];
+ if (!v2->va || txdata2->mid != vif->mid)
continue;
cid = wil->vring2cid_tid[i][0];
if (cid >= WIL6210_MAX_CID) /* skip BCAST */
@@ -1280,7 +1312,7 @@ found:
if (skb2) {
wil_dbg_txrx(wil, "BCAST DUP -> ring %d\n", i);
wil_set_da_for_vring(wil, skb2, i);
- wil_tx_vring(wil, v2, skb2);
+ wil_tx_vring(wil, vif, v2, skb2);
} else {
wil_err(wil, "skb_copy failed\n");
}
@@ -1417,8 +1449,8 @@ static inline void wil_set_tx_desc_last_tso(volatile struct vring_tx_desc *d)
DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_POS;
}
-static int __wil_tx_vring_tso(struct wil6210_priv *wil, struct vring *vring,
- struct sk_buff *skb)
+static int __wil_tx_vring_tso(struct wil6210_priv *wil, struct wil6210_vif *vif,
+ struct vring *vring, struct sk_buff *skb)
{
struct device *dev = wil_to_dev(wil);
@@ -1710,8 +1742,8 @@ err_exit:
return rc;
}
-static int __wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
- struct sk_buff *skb)
+static int __wil_tx_vring(struct wil6210_priv *wil, struct wil6210_vif *vif,
+ struct vring *vring, struct sk_buff *skb)
{
struct device *dev = wil_to_dev(wil);
struct vring_tx_desc dd, *d = &dd;
@@ -1725,7 +1757,7 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
uint i = swhead;
dma_addr_t pa;
int used;
- bool mcast = (vring_index == wil->bcast_vring);
+ bool mcast = (vring_index == vif->bcast_vring);
uint len = skb_headlen(skb);
wil_dbg_txrx(wil, "tx_vring: %d bytes to vring %d\n", skb->len,
@@ -1860,8 +1892,8 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
return -EINVAL;
}
-static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
- struct sk_buff *skb)
+static int wil_tx_vring(struct wil6210_priv *wil, struct wil6210_vif *vif,
+ struct vring *vring, struct sk_buff *skb)
{
int vring_index = vring - wil->vring_tx;
struct vring_tx_data *txdata = &wil->vring_tx_data[vring_index];
@@ -1879,7 +1911,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
}
rc = (skb_is_gso(skb) ? __wil_tx_vring_tso : __wil_tx_vring)
- (wil, vring, skb);
+ (wil, vif, vring, skb);
spin_unlock(&txdata->lock);
@@ -1888,6 +1920,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
/**
* Check status of tx vrings and stop/wake net queues if needed
+ * It will start/stop net queues of a specific VIF net_device.
*
* This function does one of two checks:
* In case check_stop is true, will check if net queues need to be stopped. If
@@ -1903,28 +1936,32 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
* availability and modified vring has high descriptor availability.
*/
static inline void __wil_update_net_queues(struct wil6210_priv *wil,
+ struct wil6210_vif *vif,
struct vring *vring,
bool check_stop)
{
int i;
+ if (unlikely(!vif))
+ return;
+
if (vring)
- wil_dbg_txrx(wil, "vring %d, check_stop=%d, stopped=%d",
- (int)(vring - wil->vring_tx), check_stop,
- wil->net_queue_stopped);
+ wil_dbg_txrx(wil, "vring %d, mid %d, check_stop=%d, stopped=%d",
+ (int)(vring - wil->vring_tx), vif->mid, check_stop,
+ vif->net_queue_stopped);
else
- wil_dbg_txrx(wil, "check_stop=%d, stopped=%d",
- check_stop, wil->net_queue_stopped);
+ wil_dbg_txrx(wil, "check_stop=%d, mid=%d, stopped=%d",
+ check_stop, vif->mid, vif->net_queue_stopped);
- if (check_stop == wil->net_queue_stopped)
+ if (check_stop == vif->net_queue_stopped)
/* net queues already in desired state */
return;
if (check_stop) {
if (!vring || unlikely(wil_vring_avail_low(vring))) {
/* not enough room in the vring */
- netif_tx_stop_all_queues(wil_to_ndev(wil));
- wil->net_queue_stopped = true;
+ netif_tx_stop_all_queues(vif_to_ndev(vif));
+ vif->net_queue_stopped = true;
wil_dbg_txrx(wil, "netif_tx_stop called\n");
}
return;
@@ -1940,7 +1977,8 @@ static inline void __wil_update_net_queues(struct wil6210_priv *wil,
struct vring *cur_vring = &wil->vring_tx[i];
struct vring_tx_data *txdata = &wil->vring_tx_data[i];
- if (!cur_vring->va || !txdata->enabled || cur_vring == vring)
+ if (txdata->mid != vif->mid || !cur_vring->va ||
+ !txdata->enabled || cur_vring == vring)
continue;
if (wil_vring_avail_low(cur_vring)) {
@@ -1953,30 +1991,31 @@ static inline void __wil_update_net_queues(struct wil6210_priv *wil,
if (!vring || wil_vring_avail_high(vring)) {
/* enough room in the vring */
wil_dbg_txrx(wil, "calling netif_tx_wake\n");
- netif_tx_wake_all_queues(wil_to_ndev(wil));
- wil->net_queue_stopped = false;
+ netif_tx_wake_all_queues(vif_to_ndev(vif));
+ vif->net_queue_stopped = false;
}
}
-void wil_update_net_queues(struct wil6210_priv *wil, struct vring *vring,
- bool check_stop)
+void wil_update_net_queues(struct wil6210_priv *wil, struct wil6210_vif *vif,
+ struct vring *vring, bool check_stop)
{
spin_lock(&wil->net_queue_lock);
- __wil_update_net_queues(wil, vring, check_stop);
+ __wil_update_net_queues(wil, vif, vring, check_stop);
spin_unlock(&wil->net_queue_lock);
}
-void wil_update_net_queues_bh(struct wil6210_priv *wil, struct vring *vring,
- bool check_stop)
+void wil_update_net_queues_bh(struct wil6210_priv *wil, struct wil6210_vif *vif,
+ struct vring *vring, bool check_stop)
{
spin_lock_bh(&wil->net_queue_lock);
- __wil_update_net_queues(wil, vring, check_stop);
+ __wil_update_net_queues(wil, vif, vring, check_stop);
spin_unlock_bh(&wil->net_queue_lock);
}
netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
- struct wil6210_priv *wil = ndev_to_wil(ndev);
+ struct wil6210_vif *vif = ndev_to_vif(ndev);
+ struct wil6210_priv *wil = vif_to_wil(vif);
struct ethhdr *eth = (void *)skb->data;
bool bcast = is_multicast_ether_addr(eth->h_dest);
struct vring *vring;
@@ -1991,49 +2030,50 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
}
goto drop;
}
- if (unlikely(!test_bit(wil_status_fwconnected, wil->status))) {
- wil_dbg_ratelimited(wil, "FW not connected, packet dropped\n");
+ if (unlikely(!test_bit(wil_vif_fwconnected, vif->status))) {
+ wil_dbg_ratelimited(wil,
+ "VIF not connected, packet dropped\n");
goto drop;
}
- if (unlikely(wil->wdev->iftype == NL80211_IFTYPE_MONITOR)) {
+ if (unlikely(vif->wdev.iftype == NL80211_IFTYPE_MONITOR)) {
wil_err(wil, "Xmit in monitor mode not supported\n");
goto drop;
}
pr_once_fw = false;
/* find vring */
- if (wil->wdev->iftype == NL80211_IFTYPE_STATION && !wil->pbss) {
+ if (vif->wdev.iftype == NL80211_IFTYPE_STATION && !vif->pbss) {
/* in STA mode (ESS), all to same VRING (to AP) */
- vring = wil_find_tx_vring_sta(wil, skb);
+ vring = wil_find_tx_vring_sta(wil, vif, skb);
} else if (bcast) {
- if (wil->pbss)
+ if (vif->pbss)
/* in pbss, no bcast VRING - duplicate skb in
* all stations VRINGs
*/
- vring = wil_find_tx_bcast_2(wil, skb);
- else if (wil->wdev->iftype == NL80211_IFTYPE_AP)
+ vring = wil_find_tx_bcast_2(wil, vif, skb);
+ else if (vif->wdev.iftype == NL80211_IFTYPE_AP)
/* AP has a dedicated bcast VRING */
- vring = wil_find_tx_bcast_1(wil, skb);
+ vring = wil_find_tx_bcast_1(wil, vif, skb);
else
/* unexpected combination, fallback to duplicating
* the skb in all stations VRINGs
*/
- vring = wil_find_tx_bcast_2(wil, skb);
+ vring = wil_find_tx_bcast_2(wil, vif, skb);
} else {
/* unicast, find specific VRING by dest. address */
- vring = wil_find_tx_ucast(wil, skb);
+ vring = wil_find_tx_ucast(wil, vif, skb);
}
if (unlikely(!vring)) {
wil_dbg_txrx(wil, "No Tx VRING found for %pM\n", eth->h_dest);
goto drop;
}
/* set up vring entry */
- rc = wil_tx_vring(wil, vring, skb);
+ rc = wil_tx_vring(wil, vif, vring, skb);
switch (rc) {
case 0:
/* shall we stop net queues? */
- wil_update_net_queues_bh(wil, vring, true);
+ wil_update_net_queues_bh(wil, vif, vring, true);
/* statistics will be updated on the tx_complete */
dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
@@ -2072,9 +2112,10 @@ static inline void wil_consume_skb(struct sk_buff *skb, bool acked)
*
* Safe to call from IRQ
*/
-int wil_tx_complete(struct wil6210_priv *wil, int ringid)
+int wil_tx_complete(struct wil6210_vif *vif, int ringid)
{
- struct net_device *ndev = wil_to_ndev(wil);
+ struct wil6210_priv *wil = vif_to_wil(vif);
+ struct net_device *ndev = vif_to_ndev(vif);
struct device *dev = wil_to_dev(wil);
struct vring *vring = &wil->vring_tx[ringid];
struct vring_tx_data *txdata = &wil->vring_tx_data[ringid];
@@ -2184,7 +2225,7 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
/* shall we wake net queues? */
if (done)
- wil_update_net_queues(wil, vring, false);
+ wil_update_net_queues(wil, vif, vring, false);
return done;
}
diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h
index fcdffaa8251b..5f07717acc2c 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.h
+++ b/drivers/net/wireless/ath/wil6210/txrx.h
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2012-2016 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -63,7 +64,9 @@ static inline void wil_desc_addr_set(struct vring_dma_addr *addr,
* [dword 1]
* bit 0.. 3 : pkt_mode:4
* bit 4 : pkt_mode_en:1
- * bit 5..14 : reserved0:10
+ * bit 5 : mac_id_en:1
+ * bit 6..7 : mac_id:2
+ * bit 8..14 : reserved0:7
* bit 15 : ack_policy_en:1
* bit 16..19 : dst_index:4
* bit 20 : dst_index_en:1
@@ -132,6 +135,14 @@ struct vring_tx_mac {
#define MAC_CFG_DESC_TX_1_PKT_MODE_EN_LEN 1
#define MAC_CFG_DESC_TX_1_PKT_MODE_EN_MSK 0x10
+#define MAC_CFG_DESC_TX_1_MAC_ID_EN_POS 5
+#define MAC_CFG_DESC_TX_1_MAC_ID_EN_LEN 1
+#define MAC_CFG_DESC_TX_1_MAC_ID_EN_MSK 0x20
+
+#define MAC_CFG_DESC_TX_1_MAC_ID_POS 6
+#define MAC_CFG_DESC_TX_1_MAC_ID_LEN 2
+#define MAC_CFG_DESC_TX_1_MAC_ID_MSK 0xc0
+
#define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_POS 15
#define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_LEN 1
#define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_MSK 0x8000
@@ -304,7 +315,7 @@ enum {
* bit 0.. 3 : tid:4 The QoS (b3-0) TID Field
* bit 4.. 6 : cid:3 The Source index that was found during parsing the TA.
* This field is used to define the source of the packet
- * bit 7 : reserved:1
+ * bit 7 : MAC_id_valid:1, 1 if MAC virtual number is valid.
* bit 8.. 9 : mid:2 The MAC virtual number
* bit 10..11 : frame_type:2 : The FC (b3-2) - MPDU Type
* (management, data, control and extension)
@@ -395,6 +406,7 @@ struct vring_rx_mac {
#define RX_DMA_D0_CMD_DMA_EOP BIT(8)
#define RX_DMA_D0_CMD_DMA_RT BIT(9) /* always 1 */
#define RX_DMA_D0_CMD_DMA_IT BIT(10) /* interrupt */
+#define RX_MAC_D0_MAC_ID_VALID BIT(7)
/* Error field */
#define RX_DMA_ERROR_FCS BIT(0)
@@ -451,7 +463,8 @@ static inline int wil_rxdesc_cid(struct vring_rx_desc *d)
static inline int wil_rxdesc_mid(struct vring_rx_desc *d)
{
- return WIL_GET_BITS(d->mac.d0, 8, 9);
+ return (d->mac.d0 & RX_MAC_D0_MAC_ID_VALID) ?
+ WIL_GET_BITS(d->mac.d0, 8, 9) : 0;
}
static inline int wil_rxdesc_ftype(struct vring_rx_desc *d)
@@ -517,7 +530,8 @@ static inline struct vring_rx_desc *wil_skb_rxdesc(struct sk_buff *skb)
void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev);
void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb);
-void wil_rx_bar(struct wil6210_priv *wil, u8 cid, u8 tid, u16 seq);
+void wil_rx_bar(struct wil6210_priv *wil, struct wil6210_vif *vif,
+ u8 cid, u8 tid, u16 seq);
struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil,
int size, u16 ssn);
void wil_tid_ampdu_rx_free(struct wil6210_priv *wil,
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 0df2aada6659..f9c5155025bc 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -26,6 +26,7 @@
#include <linux/types.h>
#include "wmi.h"
#include "wil_platform.h"
+#include "fw.h"
extern bool no_fw_recovery;
extern unsigned int mtu_max;
@@ -49,6 +50,11 @@ extern bool disable_ap_sme;
#define WIL_DEFAULT_BUS_REQUEST_KBPS 128000 /* ~1Gbps */
#define WIL_MAX_BUS_REQUEST_KBPS 800000 /* ~6.1Gbps */
+/* maximum number of virtual interfaces the driver supports
+ * (including the main interface)
+ */
+#define WIL_MAX_VIFS 4
+
/**
* extract bits [@b0:@b1] (inclusive) from the value @x
* it should be @b0 <= @b1, or result is incorrect
@@ -463,13 +469,12 @@ struct vring_tx_data {
u16 agg_timeout;
u8 agg_amsdu;
bool addba_in_progress; /* if set, agg_xxx is for request in progress */
+ u8 mid;
spinlock_t lock;
};
enum { /* for wil6210_priv.status */
wil_status_fwready = 0, /* FW operational */
- wil_status_fwconnecting,
- wil_status_fwconnected,
wil_status_dontscan,
wil_status_mbox_ready, /* MBOX structures ready */
wil_status_irqen, /* interrupts enabled - for debug */
@@ -541,7 +546,6 @@ struct wil_tid_crypto_rx {
struct wil_p2p_info {
struct ieee80211_channel listen_chan;
u8 discovery_started;
- u8 p2p_dev_started;
u64 cookie;
struct wireless_dev *pending_listen_wdev;
unsigned int listen_duration;
@@ -584,6 +588,7 @@ struct wil_net_stats {
*/
struct wil_sta_info {
u8 addr[ETH_ALEN];
+ u8 mid;
enum wil_sta_status status;
struct wil_net_stats stats;
/* Rx BACK */
@@ -669,10 +674,44 @@ extern struct blink_on_off_time led_blink_time[WIL_LED_TIME_LAST];
extern u8 led_id;
extern u8 led_polarity;
+enum wil6210_vif_status {
+ wil_vif_fwconnecting,
+ wil_vif_fwconnected,
+ wil_vif_status_last /* keep last */
+};
+
+struct wil6210_vif {
+ struct wireless_dev wdev;
+ struct net_device *ndev;
+ struct wil6210_priv *wil;
+ u8 mid;
+ DECLARE_BITMAP(status, wil_vif_status_last);
+ u32 privacy; /* secure connection? */
+ u16 channel; /* relevant in AP mode */
+ u8 hidden_ssid; /* relevant in AP mode */
+ u32 ap_isolate; /* no intra-BSS communication */
+ bool pbss;
+ int bcast_vring;
+ struct cfg80211_bss *bss; /* connected bss, relevant in STA mode */
+ int locally_generated_disc; /* relevant in STA mode */
+ struct timer_list connect_timer;
+ struct work_struct disconnect_worker;
+ /* scan */
+ struct cfg80211_scan_request *scan_request;
+ struct timer_list scan_timer; /* detect scan timeout */
+ struct wil_p2p_info p2p;
+ /* keep alive */
+ struct list_head probe_client_pending;
+ struct mutex probe_client_mutex; /* protect @probe_client_pending */
+ struct work_struct probe_client_worker;
+ int net_queue_stopped; /* netif_tx_stop_all_queues invoked */
+};
+
struct wil6210_priv {
struct pci_dev *pdev;
u32 bar_size;
- struct wireless_dev *wdev;
+ struct wiphy *wiphy;
+ struct net_device *main_ndev;
void __iomem *csr;
DECLARE_BITMAP(status, wil_status_last);
u8 fw_version[ETHTOOL_FWVERS_LEN];
@@ -686,21 +725,18 @@ struct wil6210_priv {
DECLARE_BITMAP(hw_capa, hw_capa_last);
DECLARE_BITMAP(fw_capabilities, WMI_FW_CAPABILITY_MAX);
DECLARE_BITMAP(platform_capa, WIL_PLATFORM_CAPA_MAX);
- u8 n_mids; /* number of additional MIDs as reported by FW */
u32 recovery_count; /* num of FW recovery attempts in a short time */
u32 recovery_state; /* FW recovery state machine */
unsigned long last_fw_recovery; /* jiffies of last fw recovery */
wait_queue_head_t wq; /* for all wait_event() use */
+ u8 max_vifs; /* maximum number of interfaces, including main */
+ struct wil6210_vif *vifs[WIL_MAX_VIFS];
+ struct mutex vif_mutex; /* protects access to VIF entries */
+ atomic_t connected_vifs;
/* profile */
struct cfg80211_chan_def monitor_chandef;
u32 monitor_flags;
- u32 privacy; /* secure connection? */
- u8 hidden_ssid; /* relevant in AP mode */
- u16 channel; /* relevant in AP mode */
int sinfo_gen;
- u32 ap_isolate; /* no intra-BSS communication */
- struct cfg80211_bss *bss; /* connected bss, relevant in STA mode */
- int locally_generated_disc; /* relevant in STA mode */
/* interrupt moderation */
u32 tx_max_burst_duration;
u32 tx_interframe_timeout;
@@ -715,15 +751,13 @@ struct wil6210_priv {
struct completion wmi_call;
u16 wmi_seq;
u16 reply_id; /**< wait for this WMI event */
+ u8 reply_mid;
void *reply_buf;
u16 reply_size;
struct workqueue_struct *wmi_wq; /* for deferred calls */
struct work_struct wmi_event_worker;
struct workqueue_struct *wq_service;
- struct work_struct disconnect_worker;
struct work_struct fw_error_worker; /* for FW error recovery */
- struct timer_list connect_timer;
- struct timer_list scan_timer; /* detect scan timeout */
struct list_head pending_wmi_ev;
/*
* protect pending_wmi_ev
@@ -732,13 +766,10 @@ struct wil6210_priv {
*/
spinlock_t wmi_ev_lock;
spinlock_t net_queue_lock; /* guarding stop/wake netif queue */
- int net_queue_stopped; /* netif_tx_stop_all_queues invoked */
struct napi_struct napi_rx;
struct napi_struct napi_tx;
- /* keep alive */
- struct list_head probe_client_pending;
- struct mutex probe_client_mutex; /* protect @probe_client_pending */
- struct work_struct probe_client_worker;
+ struct net_device napi_ndev; /* dummy net_device serving all VIFs */
+
/* DMA related */
struct vring vring_rx;
unsigned int rx_buf_len;
@@ -746,11 +777,8 @@ struct wil6210_priv {
struct vring_tx_data vring_tx_data[WIL6210_MAX_TX_RINGS];
u8 vring2cid_tid[WIL6210_MAX_TX_RINGS][2]; /* [0] - CID, [1] - TID */
struct wil_sta_info sta[WIL6210_MAX_CID];
- int bcast_vring;
u32 vring_idle_trsh; /* HW fetches up to 16 descriptors at once */
u32 dma_addr_size; /* indicates dma addr size */
- /* scan */
- struct cfg80211_scan_request *scan_request;
struct mutex mutex; /* for wil6210_priv access in wil_{up|down} */
/* statistics */
@@ -770,13 +798,10 @@ struct wil6210_priv {
struct pmc_ctx pmc;
- bool pbss;
-
- struct wil_p2p_info p2p;
+ u8 p2p_dev_started;
/* P2P_DEVICE vif */
struct wireless_dev *p2p_wdev;
- struct mutex p2p_wdev_mutex; /* protect @p2p_wdev and @scan_request */
struct wireless_dev *radio_wdev;
/* High Access Latency Policy voting */
@@ -798,13 +823,35 @@ struct wil6210_priv {
u32 iccm_base;
};
-#define wil_to_wiphy(i) (i->wdev->wiphy)
+#define wil_to_wiphy(i) (i->wiphy)
#define wil_to_dev(i) (wiphy_dev(wil_to_wiphy(i)))
#define wiphy_to_wil(w) (struct wil6210_priv *)(wiphy_priv(w))
-#define wil_to_wdev(i) (i->wdev)
#define wdev_to_wil(w) (struct wil6210_priv *)(wdev_priv(w))
-#define wil_to_ndev(i) (wil_to_wdev(i)->netdev)
#define ndev_to_wil(n) (wdev_to_wil(n->ieee80211_ptr))
+#define ndev_to_vif(n) (struct wil6210_vif *)(netdev_priv(n))
+#define vif_to_wil(v) (v->wil)
+#define vif_to_ndev(v) (v->ndev)
+#define vif_to_wdev(v) (&v->wdev)
+
+static inline struct wil6210_vif *wdev_to_vif(struct wil6210_priv *wil,
+ struct wireless_dev *wdev)
+{
+ /* main interface is shared with P2P device */
+ if (wdev == wil->p2p_wdev)
+ return ndev_to_vif(wil->main_ndev);
+ else
+ return container_of(wdev, struct wil6210_vif, wdev);
+}
+
+static inline struct wireless_dev *
+vif_to_radio_wdev(struct wil6210_priv *wil, struct wil6210_vif *vif)
+{
+ /* main interface is shared with P2P device */
+ if (vif->mid)
+ return vif_to_wdev(vif);
+ else
+ return wil->radio_wdev;
+}
__printf(2, 3)
void wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...);
@@ -817,7 +864,7 @@ void __wil_info(struct wil6210_priv *wil, const char *fmt, ...);
__printf(2, 3)
void wil_dbg_ratelimited(const struct wil6210_priv *wil, const char *fmt, ...);
#define wil_dbg(wil, fmt, arg...) do { \
- netdev_dbg(wil_to_ndev(wil), fmt, ##arg); \
+ netdev_dbg(wil->main_ndev, fmt, ##arg); \
wil_dbg_trace(wil, fmt, ##arg); \
} while (0)
@@ -900,9 +947,18 @@ void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src,
void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src,
size_t count);
+struct wil6210_vif *
+wil_vif_alloc(struct wil6210_priv *wil, const char *name,
+ unsigned char name_assign_type, enum nl80211_iftype iftype);
+void wil_vif_free(struct wil6210_vif *vif);
void *wil_if_alloc(struct device *dev);
+bool wil_has_other_active_ifaces(struct wil6210_priv *wil,
+ struct net_device *ndev, bool up, bool ok);
+bool wil_has_active_ifaces(struct wil6210_priv *wil, bool up, bool ok);
void wil_if_free(struct wil6210_priv *wil);
+int wil_vif_add(struct wil6210_priv *wil, struct wil6210_vif *vif);
int wil_if_add(struct wil6210_priv *wil);
+void wil_vif_remove(struct wil6210_priv *wil, u8 mid);
void wil_if_remove(struct wil6210_priv *wil);
int wil_priv_init(struct wil6210_priv *wil);
void wil_priv_deinit(struct wil6210_priv *wil);
@@ -918,7 +974,7 @@ int wil_down(struct wil6210_priv *wil);
int __wil_down(struct wil6210_priv *wil);
void wil_refresh_fw_capabilities(struct wil6210_priv *wil);
void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r);
-int wil_find_cid(struct wil6210_priv *wil, const u8 *mac);
+int wil_find_cid(struct wil6210_priv *wil, u8 mid, const u8 *mac);
void wil_set_ethtoolops(struct net_device *ndev);
struct fw_map *wil_find_fw_mapping(const char *section);
@@ -927,40 +983,45 @@ void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr);
void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr);
int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr,
struct wil6210_mbox_hdr *hdr);
-int wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len);
+int wmi_send(struct wil6210_priv *wil, u16 cmdid, u8 mid, void *buf, u16 len);
void wmi_recv_cmd(struct wil6210_priv *wil);
-int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len,
+int wmi_call(struct wil6210_priv *wil, u16 cmdid, u8 mid, void *buf, u16 len,
u16 reply_id, void *reply, u8 reply_size, int to_msec);
void wmi_event_worker(struct work_struct *work);
void wmi_event_flush(struct wil6210_priv *wil);
-int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid);
-int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid);
+int wmi_set_ssid(struct wil6210_vif *vif, u8 ssid_len, const void *ssid);
+int wmi_get_ssid(struct wil6210_vif *vif, u8 *ssid_len, void *ssid);
int wmi_set_channel(struct wil6210_priv *wil, int channel);
int wmi_get_channel(struct wil6210_priv *wil, int *channel);
-int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index,
+int wmi_del_cipher_key(struct wil6210_vif *vif, u8 key_index,
const void *mac_addr, int key_usage);
-int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index,
+int wmi_add_cipher_key(struct wil6210_vif *vif, u8 key_index,
const void *mac_addr, int key_len, const void *key,
int key_usage);
int wmi_echo(struct wil6210_priv *wil);
-int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie);
+int wmi_set_ie(struct wil6210_vif *vif, u8 type, u16 ie_len, const void *ie);
int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring);
int wmi_rxon(struct wil6210_priv *wil, bool on);
int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_m, u32 *t_r);
-int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac,
+int wmi_disconnect_sta(struct wil6210_vif *vif, const u8 *mac,
u16 reason, bool full_disconnect, bool del_sta);
-int wmi_addba(struct wil6210_priv *wil, u8 ringid, u8 size, u16 timeout);
-int wmi_delba_tx(struct wil6210_priv *wil, u8 ringid, u16 reason);
-int wmi_delba_rx(struct wil6210_priv *wil, u8 cidxtid, u16 reason);
-int wmi_addba_rx_resp(struct wil6210_priv *wil, u8 cid, u8 tid, u8 token,
+int wmi_addba(struct wil6210_priv *wil, u8 mid,
+ u8 ringid, u8 size, u16 timeout);
+int wmi_delba_tx(struct wil6210_priv *wil, u8 mid, u8 ringid, u16 reason);
+int wmi_delba_rx(struct wil6210_priv *wil, u8 mid, u8 cidxtid, u16 reason);
+int wmi_addba_rx_resp(struct wil6210_priv *wil,
+ u8 mid, u8 cid, u8 tid, u8 token,
u16 status, bool amsdu, u16 agg_wsize, u16 timeout);
int wmi_ps_dev_profile_cfg(struct wil6210_priv *wil,
enum wmi_ps_profile_type ps_profile);
int wmi_set_mgmt_retry(struct wil6210_priv *wil, u8 retry_short);
int wmi_get_mgmt_retry(struct wil6210_priv *wil, u8 *retry_short);
-int wmi_new_sta(struct wil6210_priv *wil, const u8 *mac, u8 aid);
-int wil_addba_rx_request(struct wil6210_priv *wil, u8 cidxtid,
- u8 dialog_token, __le16 ba_param_set,
+int wmi_new_sta(struct wil6210_vif *vif, const u8 *mac, u8 aid);
+int wmi_port_allocate(struct wil6210_priv *wil, u8 mid,
+ const u8 *mac, enum nl80211_iftype iftype);
+int wmi_port_delete(struct wil6210_priv *wil, u8 mid);
+int wil_addba_rx_request(struct wil6210_priv *wil, u8 mid,
+ u8 cidxtid, u8 dialog_token, __le16 ba_param_set,
__le16 ba_timeout, __le16 ba_seq_ctrl);
int wil_addba_tx_request(struct wil6210_priv *wil, u8 ringid, u16 wsize);
@@ -976,28 +1037,31 @@ void wil6210_mask_halp(struct wil6210_priv *wil);
/* P2P */
bool wil_p2p_is_social_scan(struct cfg80211_scan_request *request);
-void wil_p2p_discovery_timer_fn(struct timer_list *t);
-int wil_p2p_search(struct wil6210_priv *wil,
+int wil_p2p_search(struct wil6210_vif *vif,
struct cfg80211_scan_request *request);
int wil_p2p_listen(struct wil6210_priv *wil, struct wireless_dev *wdev,
unsigned int duration, struct ieee80211_channel *chan,
u64 *cookie);
-u8 wil_p2p_stop_discovery(struct wil6210_priv *wil);
-int wil_p2p_cancel_listen(struct wil6210_priv *wil, u64 cookie);
+u8 wil_p2p_stop_discovery(struct wil6210_vif *vif);
+int wil_p2p_cancel_listen(struct wil6210_vif *vif, u64 cookie);
void wil_p2p_listen_expired(struct work_struct *work);
void wil_p2p_search_expired(struct work_struct *work);
void wil_p2p_stop_radio_operations(struct wil6210_priv *wil);
void wil_p2p_delayed_listen_work(struct work_struct *work);
/* WMI for P2P */
-int wmi_p2p_cfg(struct wil6210_priv *wil, int channel, int bi);
-int wmi_start_listen(struct wil6210_priv *wil);
-int wmi_start_search(struct wil6210_priv *wil);
-int wmi_stop_discovery(struct wil6210_priv *wil);
+int wmi_p2p_cfg(struct wil6210_vif *vif, int channel, int bi);
+int wmi_start_listen(struct wil6210_vif *vif);
+int wmi_start_search(struct wil6210_vif *vif);
+int wmi_stop_discovery(struct wil6210_vif *vif);
int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
struct cfg80211_mgmt_tx_params *params,
u64 *cookie);
+int wil_cfg80211_iface_combinations_from_fw(
+ struct wil6210_priv *wil,
+ const struct wil_fw_record_concurrency *conc);
+int wil_vif_prepare_stop(struct wil6210_vif *vif);
#if defined(CONFIG_WIL6210_DEBUGFS)
int wil6210_debugfs_init(struct wil6210_priv *wil);
@@ -1007,44 +1071,47 @@ static inline int wil6210_debugfs_init(struct wil6210_priv *wil) { return 0; }
static inline void wil6210_debugfs_remove(struct wil6210_priv *wil) {}
#endif
-int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid,
+int wil_cid_fill_sinfo(struct wil6210_vif *vif, int cid,
struct station_info *sinfo);
-struct wireless_dev *wil_cfg80211_init(struct device *dev);
-void wil_wdev_free(struct wil6210_priv *wil);
+struct wil6210_priv *wil_cfg80211_init(struct device *dev);
+void wil_cfg80211_deinit(struct wil6210_priv *wil);
void wil_p2p_wdev_free(struct wil6210_priv *wil);
int wmi_set_mac_address(struct wil6210_priv *wil, void *addr);
-int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype,
- u8 chan, u8 hidden_ssid, u8 is_go);
-int wmi_pcp_stop(struct wil6210_priv *wil);
+int wmi_pcp_start(struct wil6210_vif *vif, int bi, u8 wmi_nettype, u8 chan,
+ u8 hidden_ssid, u8 is_go);
+int wmi_pcp_stop(struct wil6210_vif *vif);
int wmi_led_cfg(struct wil6210_priv *wil, bool enable);
-int wmi_abort_scan(struct wil6210_priv *wil);
-void wil_abort_scan(struct wil6210_priv *wil, bool sync);
+int wmi_abort_scan(struct wil6210_vif *vif);
+void wil_abort_scan(struct wil6210_vif *vif, bool sync);
+void wil_abort_scan_all_vifs(struct wil6210_priv *wil, bool sync);
void wil6210_bus_request(struct wil6210_priv *wil, u32 kbps);
-void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
+void wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid,
u16 reason_code, bool from_event);
-void wil_probe_client_flush(struct wil6210_priv *wil);
+void wil_probe_client_flush(struct wil6210_vif *vif);
void wil_probe_client_worker(struct work_struct *work);
+void wil_disconnect_worker(struct work_struct *work);
int wil_rx_init(struct wil6210_priv *wil, u16 size);
void wil_rx_fini(struct wil6210_priv *wil);
/* TX API */
-int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
+int wil_vring_init_tx(struct wil6210_vif *vif, int id, int size,
int cid, int tid);
void wil_vring_fini_tx(struct wil6210_priv *wil, int id);
-int wil_tx_init(struct wil6210_priv *wil, int cid);
-int wil_vring_init_bcast(struct wil6210_priv *wil, int id, int size);
-int wil_bcast_init(struct wil6210_priv *wil);
-void wil_bcast_fini(struct wil6210_priv *wil);
-
-void wil_update_net_queues(struct wil6210_priv *wil, struct vring *vring,
- bool should_stop);
-void wil_update_net_queues_bh(struct wil6210_priv *wil, struct vring *vring,
- bool check_stop);
+int wil_tx_init(struct wil6210_vif *vif, int cid);
+int wil_vring_init_bcast(struct wil6210_vif *vif, int id, int size);
+int wil_bcast_init(struct wil6210_vif *vif);
+void wil_bcast_fini(struct wil6210_vif *vif);
+void wil_bcast_fini_all(struct wil6210_priv *wil);
+
+void wil_update_net_queues(struct wil6210_priv *wil, struct wil6210_vif *vif,
+ struct vring *vring, bool should_stop);
+void wil_update_net_queues_bh(struct wil6210_priv *wil, struct wil6210_vif *vif,
+ struct vring *vring, bool check_stop);
netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev);
-int wil_tx_complete(struct wil6210_priv *wil, int ringid);
+int wil_tx_complete(struct wil6210_vif *vif, int ringid);
void wil6210_unmask_irq_tx(struct wil6210_priv *wil);
/* RX API */
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index b31e2514f8c2..a3dda9a97c1f 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -341,6 +341,10 @@ static const char *cmdid2name(u16 cmdid)
return "WMI_GET_PCP_CHANNEL_CMD";
case WMI_P2P_CFG_CMDID:
return "WMI_P2P_CFG_CMD";
+ case WMI_PORT_ALLOCATE_CMDID:
+ return "WMI_PORT_ALLOCATE_CMD";
+ case WMI_PORT_DELETE_CMDID:
+ return "WMI_PORT_DELETE_CMD";
case WMI_START_LISTEN_CMDID:
return "WMI_START_LISTEN_CMD";
case WMI_START_SEARCH_CMDID:
@@ -479,6 +483,10 @@ static const char *eventid2name(u16 eventid)
return "WMI_GET_PCP_CHANNEL_EVENT";
case WMI_P2P_CFG_DONE_EVENTID:
return "WMI_P2P_CFG_DONE_EVENT";
+ case WMI_PORT_ALLOCATED_EVENTID:
+ return "WMI_PORT_ALLOCATED_EVENT";
+ case WMI_PORT_DELETED_EVENTID:
+ return "WMI_PORT_DELETED_EVENT";
case WMI_LISTEN_STARTED_EVENTID:
return "WMI_LISTEN_STARTED_EVENT";
case WMI_SEARCH_STARTED_EVENTID:
@@ -516,7 +524,8 @@ static const char *eventid2name(u16 eventid)
}
}
-static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
+static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, u8 mid,
+ void *buf, u16 len)
{
struct {
struct wil6210_mbox_hdr hdr;
@@ -528,7 +537,7 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
.len = cpu_to_le16(sizeof(cmd.wmi) + len),
},
.wmi = {
- .mid = 0,
+ .mid = mid,
.command_id = cpu_to_le16(cmdid),
},
};
@@ -612,8 +621,8 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
}
cmd.hdr.seq = cpu_to_le16(++wil->wmi_seq);
/* set command */
- wil_dbg_wmi(wil, "sending %s (0x%04x) [%d]\n",
- cmdid2name(cmdid), cmdid, len);
+ wil_dbg_wmi(wil, "sending %s (0x%04x) [%d] mid %d\n",
+ cmdid2name(cmdid), cmdid, len, mid);
wil_hex_dump_wmi("Cmd ", DUMP_PREFIX_OFFSET, 16, 1, &cmd,
sizeof(cmd), true);
wil_hex_dump_wmi("cmd ", DUMP_PREFIX_OFFSET, 16, 1, buf,
@@ -637,31 +646,34 @@ out:
return rc;
}
-int wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
+int wmi_send(struct wil6210_priv *wil, u16 cmdid, u8 mid, void *buf, u16 len)
{
int rc;
mutex_lock(&wil->wmi_mutex);
- rc = __wmi_send(wil, cmdid, buf, len);
+ rc = __wmi_send(wil, cmdid, mid, buf, len);
mutex_unlock(&wil->wmi_mutex);
return rc;
}
/*=== Event handlers ===*/
-static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len)
+static void wmi_evt_ready(struct wil6210_vif *vif, int id, void *d, int len)
{
- struct wireless_dev *wdev = wil->wdev;
+ struct wil6210_priv *wil = vif_to_wil(vif);
+ struct wiphy *wiphy = wil_to_wiphy(wil);
struct wmi_ready_event *evt = d;
- wil->n_mids = evt->numof_additional_mids;
-
wil_info(wil, "FW ver. %s(SW %d); MAC %pM; %d MID's\n",
wil->fw_version, le32_to_cpu(evt->sw_version),
- evt->mac, wil->n_mids);
+ evt->mac, evt->numof_additional_mids);
+ if (evt->numof_additional_mids + 1 < wil->max_vifs) {
+ wil_err(wil, "FW does not support enough MIDs (need %d)",
+ wil->max_vifs - 1);
+ return; /* FW load will fail after timeout */
+ }
/* ignore MAC address, we already have it from the boot loader */
- strlcpy(wdev->wiphy->fw_version, wil->fw_version,
- sizeof(wdev->wiphy->fw_version));
+ strlcpy(wiphy->fw_version, wil->fw_version, sizeof(wiphy->fw_version));
if (len > offsetof(struct wmi_ready_event, rfc_read_calib_result)) {
wil_dbg_wmi(wil, "rfc calibration result %d\n",
@@ -674,8 +686,9 @@ static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len)
complete(&wil->wmi_ready);
}
-static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
+static void wmi_evt_rx_mgmt(struct wil6210_vif *vif, int id, void *d, int len)
{
+ struct wil6210_priv *wil = vif_to_wil(vif);
struct wmi_rx_mgmt_packet_event *data = d;
struct wiphy *wiphy = wil_to_wiphy(wil);
struct ieee80211_mgmt *rx_mgmt_frame =
@@ -753,14 +766,14 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
wil_err(wil, "cfg80211_inform_bss_frame() failed\n");
}
} else {
- mutex_lock(&wil->p2p_wdev_mutex);
- cfg80211_rx_mgmt(wil->radio_wdev, freq, signal,
+ mutex_lock(&wil->vif_mutex);
+ cfg80211_rx_mgmt(vif_to_radio_wdev(wil, vif), freq, signal,
(void *)rx_mgmt_frame, d_len, 0);
- mutex_unlock(&wil->p2p_wdev_mutex);
+ mutex_unlock(&wil->vif_mutex);
}
}
-static void wmi_evt_tx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
+static void wmi_evt_tx_mgmt(struct wil6210_vif *vif, int id, void *d, int len)
{
struct wmi_tx_mgmt_packet_event *data = d;
struct ieee80211_mgmt *mgmt_frame =
@@ -771,11 +784,13 @@ static void wmi_evt_tx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
flen, true);
}
-static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id,
+static void wmi_evt_scan_complete(struct wil6210_vif *vif, int id,
void *d, int len)
{
- mutex_lock(&wil->p2p_wdev_mutex);
- if (wil->scan_request) {
+ struct wil6210_priv *wil = vif_to_wil(vif);
+
+ mutex_lock(&wil->vif_mutex);
+ if (vif->scan_request) {
struct wmi_scan_complete_event *data = d;
int status = le32_to_cpu(data->status);
struct cfg80211_scan_info info = {
@@ -785,26 +800,28 @@ static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id,
wil_dbg_wmi(wil, "SCAN_COMPLETE(0x%08x)\n", status);
wil_dbg_misc(wil, "Complete scan_request 0x%p aborted %d\n",
- wil->scan_request, info.aborted);
- del_timer_sync(&wil->scan_timer);
- cfg80211_scan_done(wil->scan_request, &info);
- wil->radio_wdev = wil->wdev;
- wil->scan_request = NULL;
+ vif->scan_request, info.aborted);
+ del_timer_sync(&vif->scan_timer);
+ cfg80211_scan_done(vif->scan_request, &info);
+ if (vif->mid == 0)
+ wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
+ vif->scan_request = NULL;
wake_up_interruptible(&wil->wq);
- if (wil->p2p.pending_listen_wdev) {
+ if (vif->p2p.pending_listen_wdev) {
wil_dbg_misc(wil, "Scheduling delayed listen\n");
- schedule_work(&wil->p2p.delayed_listen_work);
+ schedule_work(&vif->p2p.delayed_listen_work);
}
} else {
wil_err(wil, "SCAN_COMPLETE while not scanning\n");
}
- mutex_unlock(&wil->p2p_wdev_mutex);
+ mutex_unlock(&wil->vif_mutex);
}
-static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
+static void wmi_evt_connect(struct wil6210_vif *vif, int id, void *d, int len)
{
- struct net_device *ndev = wil_to_ndev(wil);
- struct wireless_dev *wdev = wil->wdev;
+ struct wil6210_priv *wil = vif_to_wil(vif);
+ struct net_device *ndev = vif_to_ndev(vif);
+ struct wireless_dev *wdev = vif_to_wdev(vif);
struct wmi_connect_event *evt = d;
int ch; /* channel number */
struct station_info sinfo;
@@ -869,12 +886,12 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
if ((wdev->iftype == NL80211_IFTYPE_STATION) ||
(wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) {
- if (!test_bit(wil_status_fwconnecting, wil->status)) {
+ if (!test_bit(wil_vif_fwconnecting, vif->status)) {
wil_err(wil, "Not in connecting state\n");
mutex_unlock(&wil->mutex);
return;
}
- del_timer_sync(&wil->connect_timer);
+ del_timer_sync(&vif->connect_timer);
} else if ((wdev->iftype == NL80211_IFTYPE_AP) ||
(wdev->iftype == NL80211_IFTYPE_P2P_GO)) {
if (wil->sta[evt->cid].status != wil_sta_unused) {
@@ -886,13 +903,14 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
}
ether_addr_copy(wil->sta[evt->cid].addr, evt->bssid);
+ wil->sta[evt->cid].mid = vif->mid;
wil->sta[evt->cid].status = wil_sta_conn_pending;
- rc = wil_tx_init(wil, evt->cid);
+ rc = wil_tx_init(vif, evt->cid);
if (rc) {
wil_err(wil, "config tx vring failed for CID %d, rc (%d)\n",
evt->cid, rc);
- wmi_disconnect_sta(wil, wil->sta[evt->cid].addr,
+ wmi_disconnect_sta(vif, wil->sta[evt->cid].addr,
WLAN_REASON_UNSPECIFIED, false, false);
} else {
wil_info(wil, "successful connection to CID %d\n", evt->cid);
@@ -912,14 +930,14 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
} else {
struct wiphy *wiphy = wil_to_wiphy(wil);
- cfg80211_ref_bss(wiphy, wil->bss);
- cfg80211_connect_bss(ndev, evt->bssid, wil->bss,
+ cfg80211_ref_bss(wiphy, vif->bss);
+ cfg80211_connect_bss(ndev, evt->bssid, vif->bss,
assoc_req_ie, assoc_req_ielen,
assoc_resp_ie, assoc_resp_ielen,
WLAN_STATUS_SUCCESS, GFP_KERNEL,
NL80211_TIMEOUT_UNSPECIFIED);
}
- wil->bss = NULL;
+ vif->bss = NULL;
} else if ((wdev->iftype == NL80211_IFTYPE_AP) ||
(wdev->iftype == NL80211_IFTYPE_P2P_GO)) {
if (rc) {
@@ -947,19 +965,23 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
wil->sta[evt->cid].status = wil_sta_connected;
wil->sta[evt->cid].aid = evt->aid;
- set_bit(wil_status_fwconnected, wil->status);
- wil_update_net_queues_bh(wil, NULL, false);
+ if (!test_and_set_bit(wil_vif_fwconnected, vif->status))
+ atomic_inc(&wil->connected_vifs);
+ wil_update_net_queues_bh(wil, vif, NULL, false);
out:
- if (rc)
+ if (rc) {
wil->sta[evt->cid].status = wil_sta_unused;
- clear_bit(wil_status_fwconnecting, wil->status);
+ wil->sta[evt->cid].mid = U8_MAX;
+ }
+ clear_bit(wil_vif_fwconnecting, vif->status);
mutex_unlock(&wil->mutex);
}
-static void wmi_evt_disconnect(struct wil6210_priv *wil, int id,
+static void wmi_evt_disconnect(struct wil6210_vif *vif, int id,
void *d, int len)
{
+ struct wil6210_priv *wil = vif_to_wil(vif);
struct wmi_disconnect_event *evt = d;
u16 reason_code = le16_to_cpu(evt->protocol_reason_status);
@@ -976,7 +998,7 @@ static void wmi_evt_disconnect(struct wil6210_priv *wil, int id,
}
mutex_lock(&wil->mutex);
- wil6210_disconnect(wil, evt->bssid, reason_code, true);
+ wil6210_disconnect(vif, evt->bssid, reason_code, true);
mutex_unlock(&wil->mutex);
}
@@ -984,10 +1006,10 @@ static void wmi_evt_disconnect(struct wil6210_priv *wil, int id,
* Firmware reports EAPOL frame using WME event.
* Reconstruct Ethernet frame and deliver it via normal Rx
*/
-static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id,
- void *d, int len)
+static void wmi_evt_eapol_rx(struct wil6210_vif *vif, int id, void *d, int len)
{
- struct net_device *ndev = wil_to_ndev(wil);
+ struct wil6210_priv *wil = vif_to_wil(vif);
+ struct net_device *ndev = vif_to_ndev(vif);
struct wmi_eapol_rx_event *evt = d;
u16 eapol_len = le16_to_cpu(evt->eapol_len);
int sz = eapol_len + ETH_HLEN;
@@ -996,10 +1018,10 @@ static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id,
int cid;
struct wil_net_stats *stats = NULL;
- wil_dbg_wmi(wil, "EAPOL len %d from %pM\n", eapol_len,
- evt->src_mac);
+ wil_dbg_wmi(wil, "EAPOL len %d from %pM MID %d\n", eapol_len,
+ evt->src_mac, vif->mid);
- cid = wil_find_cid(wil, evt->src_mac);
+ cid = wil_find_cid(wil, vif->mid, evt->src_mac);
if (cid >= 0)
stats = &wil->sta[cid].stats;
@@ -1034,13 +1056,14 @@ static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id,
}
}
-static void wmi_evt_vring_en(struct wil6210_priv *wil, int id, void *d, int len)
+static void wmi_evt_vring_en(struct wil6210_vif *vif, int id, void *d, int len)
{
+ struct wil6210_priv *wil = vif_to_wil(vif);
struct wmi_vring_en_event *evt = d;
u8 vri = evt->vring_index;
- struct wireless_dev *wdev = wil_to_wdev(wil);
+ struct wireless_dev *wdev = vif_to_wdev(vif);
- wil_dbg_wmi(wil, "Enable vring %d\n", vri);
+ wil_dbg_wmi(wil, "Enable vring %d MID %d\n", vri, vif->mid);
if (vri >= ARRAY_SIZE(wil->vring_tx)) {
wil_err(wil, "Enable for invalid vring %d\n", vri);
@@ -1052,15 +1075,16 @@ static void wmi_evt_vring_en(struct wil6210_priv *wil, int id, void *d, int len)
* wil_cfg80211_change_station()
*/
wil->vring_tx_data[vri].dot1x_open = true;
- if (vri == wil->bcast_vring) /* no BA for bcast */
+ if (vri == vif->bcast_vring) /* no BA for bcast */
return;
if (agg_wsize >= 0)
wil_addba_tx_request(wil, vri, agg_wsize);
}
-static void wmi_evt_ba_status(struct wil6210_priv *wil, int id, void *d,
- int len)
+static void wmi_evt_ba_status(struct wil6210_vif *vif, int id,
+ void *d, int len)
{
+ struct wil6210_priv *wil = vif_to_wil(vif);
struct wmi_ba_status_event *evt = d;
struct vring_tx_data *txdata;
@@ -1089,19 +1113,21 @@ static void wmi_evt_ba_status(struct wil6210_priv *wil, int id, void *d,
txdata->addba_in_progress = false;
}
-static void wmi_evt_addba_rx_req(struct wil6210_priv *wil, int id, void *d,
- int len)
+static void wmi_evt_addba_rx_req(struct wil6210_vif *vif, int id,
+ void *d, int len)
{
+ struct wil6210_priv *wil = vif_to_wil(vif);
struct wmi_rcp_addba_req_event *evt = d;
- wil_addba_rx_request(wil, evt->cidxtid, evt->dialog_token,
+ wil_addba_rx_request(wil, vif->mid, evt->cidxtid, evt->dialog_token,
evt->ba_param_set, evt->ba_timeout,
evt->ba_seq_ctrl);
}
-static void wmi_evt_delba(struct wil6210_priv *wil, int id, void *d, int len)
+static void wmi_evt_delba(struct wil6210_vif *vif, int id, void *d, int len)
__acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
{
+ struct wil6210_priv *wil = vif_to_wil(vif);
struct wmi_delba_event *evt = d;
u8 cid, tid;
u16 reason = __le16_to_cpu(evt->reason);
@@ -1110,8 +1136,8 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
might_sleep();
parse_cidxtid(evt->cidxtid, &cid, &tid);
- wil_dbg_wmi(wil, "DELBA CID %d TID %d from %s reason %d\n",
- cid, tid,
+ wil_dbg_wmi(wil, "DELBA MID %d CID %d TID %d from %s reason %d\n",
+ vif->mid, cid, tid,
evt->from_initiator ? "originator" : "recipient",
reason);
if (!evt->from_initiator) {
@@ -1148,8 +1174,9 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
}
static void
-wmi_evt_sched_scan_result(struct wil6210_priv *wil, int id, void *d, int len)
+wmi_evt_sched_scan_result(struct wil6210_vif *vif, int id, void *d, int len)
{
+ struct wil6210_priv *wil = vif_to_wil(vif);
struct wmi_sched_scan_result_event *data = d;
struct wiphy *wiphy = wil_to_wiphy(wil);
struct ieee80211_mgmt *rx_mgmt_frame =
@@ -1220,15 +1247,17 @@ wmi_evt_sched_scan_result(struct wil6210_priv *wil, int id, void *d, int len)
* Some events are ignored for purpose; and need not be interpreted as
* "unhandled events"
*/
-static void wmi_evt_ignore(struct wil6210_priv *wil, int id, void *d, int len)
+static void wmi_evt_ignore(struct wil6210_vif *vif, int id, void *d, int len)
{
+ struct wil6210_priv *wil = vif_to_wil(vif);
+
wil_dbg_wmi(wil, "Ignore event 0x%04x len %d\n", id, len);
}
static const struct {
int eventid;
- void (*handler)(struct wil6210_priv *wil, int eventid,
- void *data, int data_len);
+ void (*handler)(struct wil6210_vif *vif,
+ int eventid, void *data, int data_len);
} wmi_evt_handlers[] = {
{WMI_READY_EVENTID, wmi_evt_ready},
{WMI_FW_READY_EVENTID, wmi_evt_ignore},
@@ -1325,6 +1354,7 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
(len >= sizeof(struct wmi_cmd_hdr))) {
struct wmi_cmd_hdr *wmi = &evt->event.wmi;
u16 id = le16_to_cpu(wmi->command_id);
+ u8 mid = wmi->mid;
u32 tstamp = le32_to_cpu(wmi->fw_timestamp);
if (test_bit(wil_status_resuming, wil->status)) {
if (id == WMI_TRAFFIC_RESUME_EVENTID)
@@ -1336,7 +1366,8 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
id);
}
spin_lock_irqsave(&wil->wmi_ev_lock, flags);
- if (wil->reply_id && wil->reply_id == id) {
+ if (wil->reply_id && wil->reply_id == id &&
+ wil->reply_mid == mid) {
if (wil->reply_buf) {
memcpy(wil->reply_buf, wmi,
min(len, wil->reply_size));
@@ -1384,7 +1415,7 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
n - num_immed_reply, num_immed_reply);
}
-int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len,
+int wmi_call(struct wil6210_priv *wil, u16 cmdid, u8 mid, void *buf, u16 len,
u16 reply_id, void *reply, u8 reply_size, int to_msec)
{
int rc;
@@ -1394,12 +1425,13 @@ int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len,
spin_lock(&wil->wmi_ev_lock);
wil->reply_id = reply_id;
+ wil->reply_mid = mid;
wil->reply_buf = reply;
wil->reply_size = reply_size;
reinit_completion(&wil->wmi_call);
spin_unlock(&wil->wmi_ev_lock);
- rc = __wmi_send(wil, cmdid, buf, len);
+ rc = __wmi_send(wil, cmdid, mid, buf, len);
if (rc)
goto out;
@@ -1419,6 +1451,7 @@ int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len,
out:
spin_lock(&wil->wmi_ev_lock);
wil->reply_id = 0;
+ wil->reply_mid = U8_MAX;
wil->reply_buf = NULL;
wil->reply_size = 0;
spin_unlock(&wil->wmi_ev_lock);
@@ -1430,27 +1463,31 @@ out:
int wmi_echo(struct wil6210_priv *wil)
{
+ struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
struct wmi_echo_cmd cmd = {
.value = cpu_to_le32(0x12345678),
};
- return wmi_call(wil, WMI_ECHO_CMDID, &cmd, sizeof(cmd),
+ return wmi_call(wil, WMI_ECHO_CMDID, vif->mid, &cmd, sizeof(cmd),
WMI_ECHO_RSP_EVENTID, NULL, 0, 50);
}
int wmi_set_mac_address(struct wil6210_priv *wil, void *addr)
{
+ struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
struct wmi_set_mac_address_cmd cmd;
ether_addr_copy(cmd.mac, addr);
wil_dbg_wmi(wil, "Set MAC %pM\n", addr);
- return wmi_send(wil, WMI_SET_MAC_ADDRESS_CMDID, &cmd, sizeof(cmd));
+ return wmi_send(wil, WMI_SET_MAC_ADDRESS_CMDID, vif->mid,
+ &cmd, sizeof(cmd));
}
int wmi_led_cfg(struct wil6210_priv *wil, bool enable)
{
+ struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
int rc = 0;
struct wmi_led_cfg_cmd cmd = {
.led_mode = enable,
@@ -1487,7 +1524,7 @@ int wmi_led_cfg(struct wil6210_priv *wil, bool enable)
"%s led %d\n",
enable ? "enabling" : "disabling", led_id);
- rc = wmi_call(wil, WMI_LED_CFG_CMDID, &cmd, sizeof(cmd),
+ rc = wmi_call(wil, WMI_LED_CFG_CMDID, vif->mid, &cmd, sizeof(cmd),
WMI_LED_CFG_DONE_EVENTID, &reply, sizeof(reply),
100);
if (rc)
@@ -1503,9 +1540,10 @@ out:
return rc;
}
-int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype,
- u8 chan, u8 hidden_ssid, u8 is_go)
+int wmi_pcp_start(struct wil6210_vif *vif,
+ int bi, u8 wmi_nettype, u8 chan, u8 hidden_ssid, u8 is_go)
{
+ struct wil6210_priv *wil = vif_to_wil(vif);
int rc;
struct wmi_pcp_start_cmd cmd = {
@@ -1524,7 +1562,7 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype,
struct wmi_pcp_started_event evt;
} __packed reply;
- if (!wil->privacy)
+ if (!vif->privacy)
cmd.disable_sec = 1;
if ((cmd.pcp_max_assoc_sta > WIL6210_MAX_CID) ||
@@ -1546,7 +1584,7 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype,
* Processing time may be huge, in case of secure AP it takes about
* 3500ms for FW to start AP
*/
- rc = wmi_call(wil, WMI_PCP_START_CMDID, &cmd, sizeof(cmd),
+ rc = wmi_call(wil, WMI_PCP_START_CMDID, vif->mid, &cmd, sizeof(cmd),
WMI_PCP_STARTED_EVENTID, &reply, sizeof(reply), 5000);
if (rc)
return rc;
@@ -1561,20 +1599,22 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype,
return rc;
}
-int wmi_pcp_stop(struct wil6210_priv *wil)
+int wmi_pcp_stop(struct wil6210_vif *vif)
{
+ struct wil6210_priv *wil = vif_to_wil(vif);
int rc;
rc = wmi_led_cfg(wil, false);
if (rc)
return rc;
- return wmi_call(wil, WMI_PCP_STOP_CMDID, NULL, 0,
+ return wmi_call(wil, WMI_PCP_STOP_CMDID, vif->mid, NULL, 0,
WMI_PCP_STOPPED_EVENTID, NULL, 0, 20);
}
-int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid)
+int wmi_set_ssid(struct wil6210_vif *vif, u8 ssid_len, const void *ssid)
{
+ struct wil6210_priv *wil = vif_to_wil(vif);
struct wmi_set_ssid_cmd cmd = {
.ssid_len = cpu_to_le32(ssid_len),
};
@@ -1584,11 +1624,12 @@ int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid)
memcpy(cmd.ssid, ssid, ssid_len);
- return wmi_send(wil, WMI_SET_SSID_CMDID, &cmd, sizeof(cmd));
+ return wmi_send(wil, WMI_SET_SSID_CMDID, vif->mid, &cmd, sizeof(cmd));
}
-int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid)
+int wmi_get_ssid(struct wil6210_vif *vif, u8 *ssid_len, void *ssid)
{
+ struct wil6210_priv *wil = vif_to_wil(vif);
int rc;
struct {
struct wmi_cmd_hdr wmi;
@@ -1596,8 +1637,8 @@ int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid)
} __packed reply;
int len; /* reply.cmd.ssid_len in CPU order */
- rc = wmi_call(wil, WMI_GET_SSID_CMDID, NULL, 0, WMI_GET_SSID_EVENTID,
- &reply, sizeof(reply), 20);
+ rc = wmi_call(wil, WMI_GET_SSID_CMDID, vif->mid, NULL, 0,
+ WMI_GET_SSID_EVENTID, &reply, sizeof(reply), 20);
if (rc)
return rc;
@@ -1613,22 +1654,25 @@ int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid)
int wmi_set_channel(struct wil6210_priv *wil, int channel)
{
+ struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
struct wmi_set_pcp_channel_cmd cmd = {
.channel = channel - 1,
};
- return wmi_send(wil, WMI_SET_PCP_CHANNEL_CMDID, &cmd, sizeof(cmd));
+ return wmi_send(wil, WMI_SET_PCP_CHANNEL_CMDID, vif->mid,
+ &cmd, sizeof(cmd));
}
int wmi_get_channel(struct wil6210_priv *wil, int *channel)
{
+ struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
int rc;
struct {
struct wmi_cmd_hdr wmi;
struct wmi_set_pcp_channel_cmd cmd;
} __packed reply;
- rc = wmi_call(wil, WMI_GET_PCP_CHANNEL_CMDID, NULL, 0,
+ rc = wmi_call(wil, WMI_GET_PCP_CHANNEL_CMDID, vif->mid, NULL, 0,
WMI_GET_PCP_CHANNEL_EVENTID, &reply, sizeof(reply), 20);
if (rc)
return rc;
@@ -1641,8 +1685,9 @@ int wmi_get_channel(struct wil6210_priv *wil, int *channel)
return 0;
}
-int wmi_p2p_cfg(struct wil6210_priv *wil, int channel, int bi)
+int wmi_p2p_cfg(struct wil6210_vif *vif, int channel, int bi)
{
+ struct wil6210_priv *wil = vif_to_wil(vif);
int rc;
struct wmi_p2p_cfg_cmd cmd = {
.discovery_mode = WMI_DISCOVERY_MODE_PEER2PEER,
@@ -1656,7 +1701,7 @@ int wmi_p2p_cfg(struct wil6210_priv *wil, int channel, int bi)
wil_dbg_wmi(wil, "sending WMI_P2P_CFG_CMDID\n");
- rc = wmi_call(wil, WMI_P2P_CFG_CMDID, &cmd, sizeof(cmd),
+ rc = wmi_call(wil, WMI_P2P_CFG_CMDID, vif->mid, &cmd, sizeof(cmd),
WMI_P2P_CFG_DONE_EVENTID, &reply, sizeof(reply), 300);
if (!rc && reply.evt.status != WMI_FW_STATUS_SUCCESS) {
wil_err(wil, "P2P_CFG failed. status %d\n", reply.evt.status);
@@ -1666,8 +1711,9 @@ int wmi_p2p_cfg(struct wil6210_priv *wil, int channel, int bi)
return rc;
}
-int wmi_start_listen(struct wil6210_priv *wil)
+int wmi_start_listen(struct wil6210_vif *vif)
{
+ struct wil6210_priv *wil = vif_to_wil(vif);
int rc;
struct {
struct wmi_cmd_hdr wmi;
@@ -1676,7 +1722,7 @@ int wmi_start_listen(struct wil6210_priv *wil)
wil_dbg_wmi(wil, "sending WMI_START_LISTEN_CMDID\n");
- rc = wmi_call(wil, WMI_START_LISTEN_CMDID, NULL, 0,
+ rc = wmi_call(wil, WMI_START_LISTEN_CMDID, vif->mid, NULL, 0,
WMI_LISTEN_STARTED_EVENTID, &reply, sizeof(reply), 300);
if (!rc && reply.evt.status != WMI_FW_STATUS_SUCCESS) {
wil_err(wil, "device failed to start listen. status %d\n",
@@ -1687,8 +1733,9 @@ int wmi_start_listen(struct wil6210_priv *wil)
return rc;
}
-int wmi_start_search(struct wil6210_priv *wil)
+int wmi_start_search(struct wil6210_vif *vif)
{
+ struct wil6210_priv *wil = vif_to_wil(vif);
int rc;
struct {
struct wmi_cmd_hdr wmi;
@@ -1697,7 +1744,7 @@ int wmi_start_search(struct wil6210_priv *wil)
wil_dbg_wmi(wil, "sending WMI_START_SEARCH_CMDID\n");
- rc = wmi_call(wil, WMI_START_SEARCH_CMDID, NULL, 0,
+ rc = wmi_call(wil, WMI_START_SEARCH_CMDID, vif->mid, NULL, 0,
WMI_SEARCH_STARTED_EVENTID, &reply, sizeof(reply), 300);
if (!rc && reply.evt.status != WMI_FW_STATUS_SUCCESS) {
wil_err(wil, "device failed to start search. status %d\n",
@@ -1708,13 +1755,14 @@ int wmi_start_search(struct wil6210_priv *wil)
return rc;
}
-int wmi_stop_discovery(struct wil6210_priv *wil)
+int wmi_stop_discovery(struct wil6210_vif *vif)
{
+ struct wil6210_priv *wil = vif_to_wil(vif);
int rc;
wil_dbg_wmi(wil, "sending WMI_DISCOVERY_STOP_CMDID\n");
- rc = wmi_call(wil, WMI_DISCOVERY_STOP_CMDID, NULL, 0,
+ rc = wmi_call(wil, WMI_DISCOVERY_STOP_CMDID, vif->mid, NULL, 0,
WMI_DISCOVERY_STOPPED_EVENTID, NULL, 0, 100);
if (rc)
@@ -1723,9 +1771,10 @@ int wmi_stop_discovery(struct wil6210_priv *wil)
return rc;
}
-int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index,
+int wmi_del_cipher_key(struct wil6210_vif *vif, u8 key_index,
const void *mac_addr, int key_usage)
{
+ struct wil6210_priv *wil = vif_to_wil(vif);
struct wmi_delete_cipher_key_cmd cmd = {
.key_index = key_index,
};
@@ -1733,13 +1782,15 @@ int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index,
if (mac_addr)
memcpy(cmd.mac, mac_addr, WMI_MAC_LEN);
- return wmi_send(wil, WMI_DELETE_CIPHER_KEY_CMDID, &cmd, sizeof(cmd));
+ return wmi_send(wil, WMI_DELETE_CIPHER_KEY_CMDID, vif->mid,
+ &cmd, sizeof(cmd));
}
-int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index,
+int wmi_add_cipher_key(struct wil6210_vif *vif, u8 key_index,
const void *mac_addr, int key_len, const void *key,
int key_usage)
{
+ struct wil6210_priv *wil = vif_to_wil(vif);
struct wmi_add_cipher_key_cmd cmd = {
.key_index = key_index,
.key_usage = key_usage,
@@ -1753,11 +1804,13 @@ int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index,
if (mac_addr)
memcpy(cmd.mac, mac_addr, WMI_MAC_LEN);
- return wmi_send(wil, WMI_ADD_CIPHER_KEY_CMDID, &cmd, sizeof(cmd));
+ return wmi_send(wil, WMI_ADD_CIPHER_KEY_CMDID, vif->mid,
+ &cmd, sizeof(cmd));
}
-int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie)
+int wmi_set_ie(struct wil6210_vif *vif, u8 type, u16 ie_len, const void *ie)
{
+ struct wil6210_priv *wil = vif_to_wil(vif);
static const char *const names[] = {
[WMI_FRAME_BEACON] = "BEACON",
[WMI_FRAME_PROBE_REQ] = "PROBE_REQ",
@@ -1786,7 +1839,7 @@ int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie)
/* BUG: FW API define ieLen as u8. Will fix FW */
cmd->ie_len = cpu_to_le16(ie_len);
memcpy(cmd->ie_info, ie, ie_len);
- rc = wmi_send(wil, WMI_SET_APPIE_CMDID, cmd, len);
+ rc = wmi_send(wil, WMI_SET_APPIE_CMDID, vif->mid, cmd, len);
kfree(cmd);
out:
if (rc) {
@@ -1808,6 +1861,7 @@ out:
*/
int wmi_rxon(struct wil6210_priv *wil, bool on)
{
+ struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
int rc;
struct {
struct wmi_cmd_hdr wmi;
@@ -1817,13 +1871,13 @@ int wmi_rxon(struct wil6210_priv *wil, bool on)
wil_info(wil, "(%s)\n", on ? "on" : "off");
if (on) {
- rc = wmi_call(wil, WMI_START_LISTEN_CMDID, NULL, 0,
+ rc = wmi_call(wil, WMI_START_LISTEN_CMDID, vif->mid, NULL, 0,
WMI_LISTEN_STARTED_EVENTID,
&reply, sizeof(reply), 100);
if ((rc == 0) && (reply.evt.status != WMI_FW_STATUS_SUCCESS))
rc = -EINVAL;
} else {
- rc = wmi_call(wil, WMI_DISCOVERY_STOP_CMDID, NULL, 0,
+ rc = wmi_call(wil, WMI_DISCOVERY_STOP_CMDID, vif->mid, NULL, 0,
WMI_DISCOVERY_STOPPED_EVENTID, NULL, 0, 20);
}
@@ -1832,8 +1886,9 @@ int wmi_rxon(struct wil6210_priv *wil, bool on)
int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring)
{
- struct wireless_dev *wdev = wil->wdev;
- struct net_device *ndev = wil_to_ndev(wil);
+ struct net_device *ndev = wil->main_ndev;
+ struct wireless_dev *wdev = ndev->ieee80211_ptr;
+ struct wil6210_vif *vif = ndev_to_vif(ndev);
struct wmi_cfg_rx_chain_cmd cmd = {
.action = WMI_RX_CHAIN_ADD,
.rx_sw_ring = {
@@ -1877,7 +1932,7 @@ int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring)
L2_802_3_OFFLOAD_CTRL_SNAP_KEEP_MSK;
/* typical time for secure PCP is 840ms */
- rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd),
+ rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, vif->mid, &cmd, sizeof(cmd),
WMI_CFG_RX_CHAIN_DONE_EVENTID, &evt, sizeof(evt), 2000);
if (rc)
return rc;
@@ -1895,6 +1950,7 @@ int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring)
int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_bb, u32 *t_rf)
{
+ struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
int rc;
struct wmi_temp_sense_cmd cmd = {
.measure_baseband_en = cpu_to_le32(!!t_bb),
@@ -1906,7 +1962,7 @@ int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_bb, u32 *t_rf)
struct wmi_temp_sense_done_event evt;
} __packed reply;
- rc = wmi_call(wil, WMI_TEMP_SENSE_CMDID, &cmd, sizeof(cmd),
+ rc = wmi_call(wil, WMI_TEMP_SENSE_CMDID, vif->mid, &cmd, sizeof(cmd),
WMI_TEMP_SENSE_DONE_EVENTID, &reply, sizeof(reply), 100);
if (rc)
return rc;
@@ -1919,9 +1975,10 @@ int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_bb, u32 *t_rf)
return 0;
}
-int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac,
+int wmi_disconnect_sta(struct wil6210_vif *vif, const u8 *mac,
u16 reason, bool full_disconnect, bool del_sta)
{
+ struct wil6210_priv *wil = vif_to_wil(vif);
int rc;
u16 reason_code;
struct wmi_disconnect_sta_cmd disc_sta_cmd = {
@@ -1937,16 +1994,17 @@ int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac,
wil_dbg_wmi(wil, "disconnect_sta: (%pM, reason %d)\n", mac, reason);
- wil->locally_generated_disc = true;
+ vif->locally_generated_disc = true;
if (del_sta) {
ether_addr_copy(del_sta_cmd.dst_mac, mac);
- rc = wmi_call(wil, WMI_DEL_STA_CMDID, &del_sta_cmd,
+ rc = wmi_call(wil, WMI_DEL_STA_CMDID, vif->mid, &del_sta_cmd,
sizeof(del_sta_cmd), WMI_DISCONNECT_EVENTID,
&reply, sizeof(reply), 1000);
} else {
ether_addr_copy(disc_sta_cmd.dst_mac, mac);
- rc = wmi_call(wil, WMI_DISCONNECT_STA_CMDID, &disc_sta_cmd,
- sizeof(disc_sta_cmd), WMI_DISCONNECT_EVENTID,
+ rc = wmi_call(wil, WMI_DISCONNECT_STA_CMDID, vif->mid,
+ &disc_sta_cmd, sizeof(disc_sta_cmd),
+ WMI_DISCONNECT_EVENTID,
&reply, sizeof(reply), 1000);
}
/* failure to disconnect in reasonable time treated as FW error */
@@ -1967,12 +2025,13 @@ int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac,
reply.evt.disconnect_reason);
wil->sinfo_gen++;
- wil6210_disconnect(wil, reply.evt.bssid, reason_code, true);
+ wil6210_disconnect(vif, reply.evt.bssid, reason_code, true);
}
return 0;
}
-int wmi_addba(struct wil6210_priv *wil, u8 ringid, u8 size, u16 timeout)
+int wmi_addba(struct wil6210_priv *wil, u8 mid,
+ u8 ringid, u8 size, u16 timeout)
{
struct wmi_vring_ba_en_cmd cmd = {
.ringid = ringid,
@@ -1984,10 +2043,10 @@ int wmi_addba(struct wil6210_priv *wil, u8 ringid, u8 size, u16 timeout)
wil_dbg_wmi(wil, "addba: (ring %d size %d timeout %d)\n", ringid, size,
timeout);
- return wmi_send(wil, WMI_VRING_BA_EN_CMDID, &cmd, sizeof(cmd));
+ return wmi_send(wil, WMI_VRING_BA_EN_CMDID, mid, &cmd, sizeof(cmd));
}
-int wmi_delba_tx(struct wil6210_priv *wil, u8 ringid, u16 reason)
+int wmi_delba_tx(struct wil6210_priv *wil, u8 mid, u8 ringid, u16 reason)
{
struct wmi_vring_ba_dis_cmd cmd = {
.ringid = ringid,
@@ -1996,10 +2055,10 @@ int wmi_delba_tx(struct wil6210_priv *wil, u8 ringid, u16 reason)
wil_dbg_wmi(wil, "delba_tx: (ring %d reason %d)\n", ringid, reason);
- return wmi_send(wil, WMI_VRING_BA_DIS_CMDID, &cmd, sizeof(cmd));
+ return wmi_send(wil, WMI_VRING_BA_DIS_CMDID, mid, &cmd, sizeof(cmd));
}
-int wmi_delba_rx(struct wil6210_priv *wil, u8 cidxtid, u16 reason)
+int wmi_delba_rx(struct wil6210_priv *wil, u8 mid, u8 cidxtid, u16 reason)
{
struct wmi_rcp_delba_cmd cmd = {
.cidxtid = cidxtid,
@@ -2009,10 +2068,11 @@ int wmi_delba_rx(struct wil6210_priv *wil, u8 cidxtid, u16 reason)
wil_dbg_wmi(wil, "delba_rx: (CID %d TID %d reason %d)\n", cidxtid & 0xf,
(cidxtid >> 4) & 0xf, reason);
- return wmi_send(wil, WMI_RCP_DELBA_CMDID, &cmd, sizeof(cmd));
+ return wmi_send(wil, WMI_RCP_DELBA_CMDID, mid, &cmd, sizeof(cmd));
}
-int wmi_addba_rx_resp(struct wil6210_priv *wil, u8 cid, u8 tid, u8 token,
+int wmi_addba_rx_resp(struct wil6210_priv *wil,
+ u8 mid, u8 cid, u8 tid, u8 token,
u16 status, bool amsdu, u16 agg_wsize, u16 timeout)
{
int rc;
@@ -2035,10 +2095,11 @@ int wmi_addba_rx_resp(struct wil6210_priv *wil, u8 cid, u8 tid, u8 token,
} __packed reply;
wil_dbg_wmi(wil,
- "ADDBA response for CID %d TID %d size %d timeout %d status %d AMSDU%s\n",
- cid, tid, agg_wsize, timeout, status, amsdu ? "+" : "-");
+ "ADDBA response for MID %d CID %d TID %d size %d timeout %d status %d AMSDU%s\n",
+ mid, cid, tid, agg_wsize,
+ timeout, status, amsdu ? "+" : "-");
- rc = wmi_call(wil, WMI_RCP_ADDBA_RESP_CMDID, &cmd, sizeof(cmd),
+ rc = wmi_call(wil, WMI_RCP_ADDBA_RESP_CMDID, mid, &cmd, sizeof(cmd),
WMI_RCP_ADDBA_RESP_SENT_EVENTID, &reply, sizeof(reply),
100);
if (rc)
@@ -2056,6 +2117,7 @@ int wmi_addba_rx_resp(struct wil6210_priv *wil, u8 cid, u8 tid, u8 token,
int wmi_ps_dev_profile_cfg(struct wil6210_priv *wil,
enum wmi_ps_profile_type ps_profile)
{
+ struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
int rc;
struct wmi_ps_dev_profile_cfg_cmd cmd = {
.ps_profile = ps_profile,
@@ -2070,7 +2132,8 @@ int wmi_ps_dev_profile_cfg(struct wil6210_priv *wil,
reply.evt.status = cpu_to_le32(WMI_PS_CFG_CMD_STATUS_ERROR);
- rc = wmi_call(wil, WMI_PS_DEV_PROFILE_CFG_CMDID, &cmd, sizeof(cmd),
+ rc = wmi_call(wil, WMI_PS_DEV_PROFILE_CFG_CMDID, vif->mid,
+ &cmd, sizeof(cmd),
WMI_PS_DEV_PROFILE_CFG_EVENTID, &reply, sizeof(reply),
100);
if (rc)
@@ -2089,6 +2152,7 @@ int wmi_ps_dev_profile_cfg(struct wil6210_priv *wil,
int wmi_set_mgmt_retry(struct wil6210_priv *wil, u8 retry_short)
{
+ struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
int rc;
struct wmi_set_mgmt_retry_limit_cmd cmd = {
.mgmt_retry_limit = retry_short,
@@ -2105,7 +2169,8 @@ int wmi_set_mgmt_retry(struct wil6210_priv *wil, u8 retry_short)
reply.evt.status = WMI_FW_STATUS_FAILURE;
- rc = wmi_call(wil, WMI_SET_MGMT_RETRY_LIMIT_CMDID, &cmd, sizeof(cmd),
+ rc = wmi_call(wil, WMI_SET_MGMT_RETRY_LIMIT_CMDID, vif->mid,
+ &cmd, sizeof(cmd),
WMI_SET_MGMT_RETRY_LIMIT_EVENTID, &reply, sizeof(reply),
100);
if (rc)
@@ -2122,6 +2187,7 @@ int wmi_set_mgmt_retry(struct wil6210_priv *wil, u8 retry_short)
int wmi_get_mgmt_retry(struct wil6210_priv *wil, u8 *retry_short)
{
+ struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
int rc;
struct {
struct wmi_cmd_hdr wmi;
@@ -2134,7 +2200,7 @@ int wmi_get_mgmt_retry(struct wil6210_priv *wil, u8 *retry_short)
return -ENOTSUPP;
reply.evt.mgmt_retry_limit = 0;
- rc = wmi_call(wil, WMI_GET_MGMT_RETRY_LIMIT_CMDID, NULL, 0,
+ rc = wmi_call(wil, WMI_GET_MGMT_RETRY_LIMIT_CMDID, vif->mid, NULL, 0,
WMI_GET_MGMT_RETRY_LIMIT_EVENTID, &reply, sizeof(reply),
100);
if (rc)
@@ -2146,21 +2212,23 @@ int wmi_get_mgmt_retry(struct wil6210_priv *wil, u8 *retry_short)
return 0;
}
-int wmi_abort_scan(struct wil6210_priv *wil)
+int wmi_abort_scan(struct wil6210_vif *vif)
{
+ struct wil6210_priv *wil = vif_to_wil(vif);
int rc;
wil_dbg_wmi(wil, "sending WMI_ABORT_SCAN_CMDID\n");
- rc = wmi_send(wil, WMI_ABORT_SCAN_CMDID, NULL, 0);
+ rc = wmi_send(wil, WMI_ABORT_SCAN_CMDID, vif->mid, NULL, 0);
if (rc)
wil_err(wil, "Failed to abort scan (%d)\n", rc);
return rc;
}
-int wmi_new_sta(struct wil6210_priv *wil, const u8 *mac, u8 aid)
+int wmi_new_sta(struct wil6210_vif *vif, const u8 *mac, u8 aid)
{
+ struct wil6210_priv *wil = vif_to_wil(vif);
int rc;
struct wmi_new_sta_cmd cmd = {
.aid = aid,
@@ -2170,7 +2238,7 @@ int wmi_new_sta(struct wil6210_priv *wil, const u8 *mac, u8 aid)
ether_addr_copy(cmd.dst_mac, mac);
- rc = wmi_send(wil, WMI_NEW_STA_CMDID, &cmd, sizeof(cmd));
+ rc = wmi_send(wil, WMI_NEW_STA_CMDID, vif->mid, &cmd, sizeof(cmd));
if (rc)
wil_err(wil, "Failed to send new sta (%d)\n", rc);
@@ -2206,6 +2274,7 @@ static const char *suspend_status2name(u8 status)
int wmi_suspend(struct wil6210_priv *wil)
{
+ struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
int rc;
struct wmi_traffic_suspend_cmd cmd = {
.wakeup_trigger = wil->wakeup_trigger,
@@ -2221,7 +2290,8 @@ int wmi_suspend(struct wil6210_priv *wil)
reply.evt.status = WMI_TRAFFIC_SUSPEND_REJECTED_LINK_NOT_IDLE;
- rc = wmi_call(wil, WMI_TRAFFIC_SUSPEND_CMDID, &cmd, sizeof(cmd),
+ rc = wmi_call(wil, WMI_TRAFFIC_SUSPEND_CMDID, vif->mid,
+ &cmd, sizeof(cmd),
WMI_TRAFFIC_SUSPEND_EVENTID, &reply, sizeof(reply),
suspend_to);
if (rc) {
@@ -2289,6 +2359,7 @@ static void resume_triggers2string(u32 triggers, char *string, int str_size)
int wmi_resume(struct wil6210_priv *wil)
{
+ struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
int rc;
char string[100];
struct {
@@ -2299,7 +2370,7 @@ int wmi_resume(struct wil6210_priv *wil)
reply.evt.status = WMI_TRAFFIC_RESUME_FAILED;
reply.evt.resume_triggers = WMI_RESUME_TRIGGER_UNKNOWN;
- rc = wmi_call(wil, WMI_TRAFFIC_RESUME_CMDID, NULL, 0,
+ rc = wmi_call(wil, WMI_TRAFFIC_RESUME_CMDID, vif->mid, NULL, 0,
WMI_TRAFFIC_RESUME_EVENTID, &reply, sizeof(reply),
WIL_WAIT_FOR_SUSPEND_RESUME_COMP);
if (rc)
@@ -2313,14 +2384,100 @@ int wmi_resume(struct wil6210_priv *wil)
return reply.evt.status;
}
-static bool wmi_evt_call_handler(struct wil6210_priv *wil, int id,
+int wmi_port_allocate(struct wil6210_priv *wil, u8 mid,
+ const u8 *mac, enum nl80211_iftype iftype)
+{
+ int rc;
+ struct wmi_port_allocate_cmd cmd = {
+ .mid = mid,
+ };
+ struct {
+ struct wmi_cmd_hdr wmi;
+ struct wmi_port_allocated_event evt;
+ } __packed reply;
+
+ wil_dbg_misc(wil, "port allocate, mid %d iftype %d, mac %pM\n",
+ mid, iftype, mac);
+
+ ether_addr_copy(cmd.mac, mac);
+ switch (iftype) {
+ case NL80211_IFTYPE_STATION:
+ cmd.port_role = WMI_PORT_STA;
+ break;
+ case NL80211_IFTYPE_AP:
+ cmd.port_role = WMI_PORT_AP;
+ break;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ cmd.port_role = WMI_PORT_P2P_CLIENT;
+ break;
+ case NL80211_IFTYPE_P2P_GO:
+ cmd.port_role = WMI_PORT_P2P_GO;
+ break;
+ /* what about monitor??? */
+ default:
+ wil_err(wil, "unsupported iftype: %d\n", iftype);
+ return -EINVAL;
+ }
+
+ reply.evt.status = WMI_FW_STATUS_FAILURE;
+
+ rc = wmi_call(wil, WMI_PORT_ALLOCATE_CMDID, mid,
+ &cmd, sizeof(cmd),
+ WMI_PORT_ALLOCATED_EVENTID, &reply,
+ sizeof(reply), 300);
+ if (rc) {
+ wil_err(wil, "failed to allocate port, status %d\n", rc);
+ return rc;
+ }
+ if (reply.evt.status != WMI_FW_STATUS_SUCCESS) {
+ wil_err(wil, "WMI_PORT_ALLOCATE returned status %d\n",
+ reply.evt.status);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int wmi_port_delete(struct wil6210_priv *wil, u8 mid)
+{
+ int rc;
+ struct wmi_port_delete_cmd cmd = {
+ .mid = mid,
+ };
+ struct {
+ struct wmi_cmd_hdr wmi;
+ struct wmi_port_deleted_event evt;
+ } __packed reply;
+
+ wil_dbg_misc(wil, "port delete, mid %d\n", mid);
+
+ reply.evt.status = WMI_FW_STATUS_FAILURE;
+
+ rc = wmi_call(wil, WMI_PORT_DELETE_CMDID, mid,
+ &cmd, sizeof(cmd),
+ WMI_PORT_DELETED_EVENTID, &reply,
+ sizeof(reply), 2000);
+ if (rc) {
+ wil_err(wil, "failed to delete port, status %d\n", rc);
+ return rc;
+ }
+ if (reply.evt.status != WMI_FW_STATUS_SUCCESS) {
+ wil_err(wil, "WMI_PORT_DELETE returned status %d\n",
+ reply.evt.status);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static bool wmi_evt_call_handler(struct wil6210_vif *vif, int id,
void *d, int len)
{
uint i;
for (i = 0; i < ARRAY_SIZE(wmi_evt_handlers); i++) {
if (wmi_evt_handlers[i].eventid == id) {
- wmi_evt_handlers[i].handler(wil, id, d, len);
+ wmi_evt_handlers[i].handler(vif, id, d, len);
return true;
}
}
@@ -2332,19 +2489,39 @@ static void wmi_event_handle(struct wil6210_priv *wil,
struct wil6210_mbox_hdr *hdr)
{
u16 len = le16_to_cpu(hdr->len);
+ struct wil6210_vif *vif;
if ((hdr->type == WIL_MBOX_HDR_TYPE_WMI) &&
(len >= sizeof(struct wmi_cmd_hdr))) {
struct wmi_cmd_hdr *wmi = (void *)(&hdr[1]);
void *evt_data = (void *)(&wmi[1]);
u16 id = le16_to_cpu(wmi->command_id);
+ u8 mid = wmi->mid;
+
+ wil_dbg_wmi(wil, "Handle %s (0x%04x) (reply_id 0x%04x,%d)\n",
+ eventid2name(id), id, wil->reply_id,
+ wil->reply_mid);
+
+ if (mid == MID_BROADCAST)
+ mid = 0;
+ if (mid >= wil->max_vifs) {
+ wil_dbg_wmi(wil, "invalid mid %d, event skipped\n",
+ mid);
+ return;
+ }
+ vif = wil->vifs[mid];
+ if (!vif) {
+ wil_dbg_wmi(wil, "event for empty VIF(%d), skipped\n",
+ mid);
+ return;
+ }
- wil_dbg_wmi(wil, "Handle %s (0x%04x) (reply_id 0x%04x)\n",
- eventid2name(id), id, wil->reply_id);
/* check if someone waits for this event */
- if (wil->reply_id && wil->reply_id == id) {
+ if (wil->reply_id && wil->reply_id == id &&
+ wil->reply_mid == mid) {
WARN_ON(wil->reply_buf);
- wmi_evt_call_handler(wil, id, evt_data,
+
+ wmi_evt_call_handler(vif, id, evt_data,
len - sizeof(*wmi));
wil_dbg_wmi(wil, "event_handle: Complete WMI 0x%04x\n",
id);
@@ -2353,7 +2530,7 @@ static void wmi_event_handle(struct wil6210_priv *wil,
}
/* unsolicited event */
/* search for handler */
- if (!wmi_evt_call_handler(wil, id, evt_data,
+ if (!wmi_evt_call_handler(vif, id, evt_data,
len - sizeof(*wmi))) {
wil_info(wil, "Unhandled event 0x%04x\n", id);
}
@@ -2523,6 +2700,7 @@ wmi_sched_scan_set_plans(struct wil6210_priv *wil,
int wmi_start_sched_scan(struct wil6210_priv *wil,
struct cfg80211_sched_scan_request *request)
{
+ struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
int rc;
struct wmi_start_sched_scan_cmd cmd = {
.min_rssi_threshold = S8_MIN,
@@ -2549,7 +2727,8 @@ int wmi_start_sched_scan(struct wil6210_priv *wil,
reply.evt.result = WMI_PNO_REJECT;
- rc = wmi_call(wil, WMI_START_SCHED_SCAN_CMDID, &cmd, sizeof(cmd),
+ rc = wmi_call(wil, WMI_START_SCHED_SCAN_CMDID, vif->mid,
+ &cmd, sizeof(cmd),
WMI_START_SCHED_SCAN_EVENTID, &reply, sizeof(reply),
WIL_WMI_CALL_GENERAL_TO_MS);
if (rc)
@@ -2566,6 +2745,7 @@ int wmi_start_sched_scan(struct wil6210_priv *wil,
int wmi_stop_sched_scan(struct wil6210_priv *wil)
{
+ struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
int rc;
struct {
struct wmi_cmd_hdr wmi;
@@ -2577,7 +2757,7 @@ int wmi_stop_sched_scan(struct wil6210_priv *wil)
reply.evt.result = WMI_PNO_REJECT;
- rc = wmi_call(wil, WMI_STOP_SCHED_SCAN_CMDID, NULL, 0,
+ rc = wmi_call(wil, WMI_STOP_SCHED_SCAN_CMDID, vif->mid, NULL, 0,
WMI_STOP_SCHED_SCAN_EVENTID, &reply, sizeof(reply),
WIL_WMI_CALL_GENERAL_TO_MS);
if (rc)
diff --git a/drivers/net/wireless/atmel/Kconfig b/drivers/net/wireless/atmel/Kconfig
index a43cfd163254..3e684f8c1f93 100644
--- a/drivers/net/wireless/atmel/Kconfig
+++ b/drivers/net/wireless/atmel/Kconfig
@@ -5,8 +5,8 @@ config WLAN_VENDOR_ATMEL
If you have a wireless card belonging to this class, say Y.
Note that the answer to this question doesn't directly affect the
- kernel: saying N will just cause the configurator to skip all
- the questions about cards. If you say Y, you will be asked for
+ kernel: saying N will just cause the configurator to skip all the
+ questions about these cards. If you say Y, you will be asked for
your specific card in the following questions.
if WLAN_VENDOR_ATMEL
diff --git a/drivers/net/wireless/atmel/atmel.c b/drivers/net/wireless/atmel/atmel.c
index c9dd5e44c9c6..d122386c382b 100644
--- a/drivers/net/wireless/atmel/atmel.c
+++ b/drivers/net/wireless/atmel/atmel.c
@@ -3861,7 +3861,7 @@ static int reset_atmel_card(struct net_device *dev)
set all the Mib values which matter in the card to match
their settings in the atmel_private structure. Some of these
- can be altered on the fly, but many (WEP, infrastucture or ad-hoc)
+ can be altered on the fly, but many (WEP, infrastructure or ad-hoc)
can only be changed by tearing down the world and coming back through
here.
diff --git a/drivers/net/wireless/broadcom/Kconfig b/drivers/net/wireless/broadcom/Kconfig
index d3651ceb5046..eebe2864835f 100644
--- a/drivers/net/wireless/broadcom/Kconfig
+++ b/drivers/net/wireless/broadcom/Kconfig
@@ -5,8 +5,8 @@ config WLAN_VENDOR_BROADCOM
If you have a wireless card belonging to this class, say Y.
Note that the answer to this question doesn't directly affect the
- kernel: saying N will just cause the configurator to skip all
- the questions about cards. If you say Y, you will be asked for
+ kernel: saying N will just cause the configurator to skip all the
+ questions about these cards. If you say Y, you will be asked for
your specific card in the following questions.
if WLAN_VENDOR_BROADCOM
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c
index 2d3a5dd07a3f..1068a2a4494c 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c
@@ -445,6 +445,11 @@ brcmf_proto_bcdc_init_done(struct brcmf_pub *drvr)
return 0;
}
+static void brcmf_proto_bcdc_debugfs_create(struct brcmf_pub *drvr)
+{
+ brcmf_fws_debugfs_create(drvr);
+}
+
int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr)
{
struct brcmf_bcdc *bcdc;
@@ -472,6 +477,7 @@ int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr)
drvr->proto->del_if = brcmf_proto_bcdc_del_if;
drvr->proto->reset_if = brcmf_proto_bcdc_reset_if;
drvr->proto->init_done = brcmf_proto_bcdc_init_done;
+ drvr->proto->debugfs_create = brcmf_proto_bcdc_debugfs_create;
drvr->proto->pd = bcdc;
drvr->hdrlen += BCDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c
index 03aae6bc1838..372363a6e752 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c
@@ -462,7 +462,7 @@ static void brcmf_btcoex_dhcp_end(struct brcmf_btcoex_info *btci)
int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif,
enum brcmf_btcoex_mode mode, u16 duration)
{
- struct brcmf_cfg80211_info *cfg = wiphy_priv(vif->wdev.wiphy);
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);
struct brcmf_btcoex_info *btci = cfg->btcoex;
struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
index 0b76a615708e..27e693e93f21 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
@@ -88,7 +88,7 @@ struct brcmf_bus_ops {
void (*wowl_config)(struct device *dev, bool enabled);
size_t (*get_ramsize)(struct device *dev);
int (*get_memdump)(struct device *dev, void *data, size_t len);
- int (*get_fwname)(struct device *dev, uint chip, uint chiprev,
+ int (*get_fwname)(struct device *dev, const char *ext,
unsigned char *fw_name);
};
@@ -140,6 +140,7 @@ struct brcmf_bus_stats {
* @always_use_fws_queue: bus wants use queue also when fwsignal is inactive.
* @wowl_supported: is wowl supported by bus driver.
* @chiprev: revision of the dongle chip.
+ * @msgbuf: msgbuf protocol parameters provided by bus layer.
*/
struct brcmf_bus {
union {
@@ -228,10 +229,10 @@ int brcmf_bus_get_memdump(struct brcmf_bus *bus, void *data, size_t len)
}
static inline
-int brcmf_bus_get_fwname(struct brcmf_bus *bus, uint chip, uint chiprev,
+int brcmf_bus_get_fwname(struct brcmf_bus *bus, const char *ext,
unsigned char *fw_name)
{
- return bus->ops->get_fwname(bus->dev, chip, chiprev, fw_name);
+ return bus->ops->get_fwname(bus->dev, ext, fw_name);
}
/*
@@ -253,7 +254,6 @@ void brcmf_dev_reset(struct device *dev);
/* Configure the "global" bus state used by upper layers */
void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state);
-int brcmf_bus_started(struct device *dev);
s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data, u32 len);
void brcmf_bus_add_txhdrlen(struct device *dev, uint len);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index 15fa00d79fc6..89b86251910e 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -753,7 +753,7 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
static int brcmf_cfg80211_del_ap_iface(struct wiphy *wiphy,
struct wireless_dev *wdev)
{
- struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
struct net_device *ndev = wdev->netdev;
struct brcmf_if *ifp = netdev_priv(ndev);
int ret;
@@ -786,7 +786,7 @@ err_unarm:
static
int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
{
- struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
struct net_device *ndev = wdev->netdev;
if (ndev && ndev == cfg_to_ndev(cfg))
@@ -831,7 +831,7 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
enum nl80211_iftype type,
struct vif_params *params)
{
- struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
struct brcmf_if *ifp = netdev_priv(ndev);
struct brcmf_cfg80211_vif *vif = ifp->vif;
s32 infra = 0;
@@ -2127,17 +2127,15 @@ static s32
brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
s32 *dbm)
{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct net_device *ndev = cfg_to_ndev(cfg);
- struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_cfg80211_vif *vif = wdev_to_vif(wdev);
s32 qdbm = 0;
s32 err;
brcmf_dbg(TRACE, "Enter\n");
- if (!check_vif_up(ifp->vif))
+ if (!check_vif_up(vif))
return -EIO;
- err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &qdbm);
+ err = brcmf_fil_iovar_int_get(vif->ifp, "qtxpower", &qdbm);
if (err) {
brcmf_err("error (%d)\n", err);
goto done;
@@ -3358,7 +3356,7 @@ brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
struct cfg80211_sched_scan_request *req)
{
struct brcmf_if *ifp = netdev_priv(ndev);
- struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
brcmf_dbg(SCAN, "Enter: n_match_sets=%d n_ssids=%d\n",
req->n_match_sets, req->n_ssids);
@@ -5124,6 +5122,9 @@ static int brcmf_cfg80211_set_pmk(struct wiphy *wiphy, struct net_device *dev,
if (WARN_ON(ifp->vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_1X))
return -EINVAL;
+ if (conf->pmk_len > BRCMF_WSEC_MAX_PSK_LEN)
+ return -ERANGE;
+
return brcmf_set_pmk(ifp, conf->pmk, conf->pmk_len);
}
@@ -5187,6 +5188,12 @@ static struct cfg80211_ops brcmf_cfg80211_ops = {
.del_pmk = brcmf_cfg80211_del_pmk,
};
+struct cfg80211_ops *brcmf_cfg80211_get_ops(void)
+{
+ return kmemdup(&brcmf_cfg80211_ops, sizeof(brcmf_cfg80211_ops),
+ GFP_KERNEL);
+}
+
struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
enum nl80211_iftype type)
{
@@ -5894,7 +5901,7 @@ static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel,
static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
u32 bw_cap[])
{
- struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
+ struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0);
struct ieee80211_supported_band *band;
struct ieee80211_channel *channel;
struct wiphy *wiphy;
@@ -6009,7 +6016,7 @@ fail_pbuf:
static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg)
{
- struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
+ struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0);
struct ieee80211_supported_band *band;
struct brcmf_fil_bwcap_le band_bwcap;
struct brcmf_chanspec_list *list;
@@ -6194,10 +6201,10 @@ static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
}
}
-static int brcmf_setup_wiphybands(struct wiphy *wiphy)
+static int brcmf_setup_wiphybands(struct brcmf_cfg80211_info *cfg)
{
- struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
- struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
+ struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0);
+ struct wiphy *wiphy;
u32 nmode = 0;
u32 vhtmode = 0;
u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT };
@@ -6791,8 +6798,8 @@ static s32 brcmf_translate_country_code(struct brcmf_pub *drvr, char alpha2[2],
static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy,
struct regulatory_request *req)
{
- struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
- struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0);
struct brcmf_fil_country_le ccreq;
s32 err;
int i;
@@ -6802,7 +6809,7 @@ static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy,
return;
/* ignore non-ISO3166 country codes */
- for (i = 0; i < sizeof(req->alpha2); i++)
+ for (i = 0; i < 2; i++)
if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') {
brcmf_err("not an ISO3166 code (0x%02x 0x%02x)\n",
req->alpha2[0], req->alpha2[1]);
@@ -6827,7 +6834,7 @@ static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy,
brcmf_err("Firmware rejected country setting\n");
return;
}
- brcmf_setup_wiphybands(wiphy);
+ brcmf_setup_wiphybands(cfg);
}
static void brcmf_free_wiphy(struct wiphy *wiphy)
@@ -6854,17 +6861,15 @@ static void brcmf_free_wiphy(struct wiphy *wiphy)
if (wiphy->wowlan != &brcmf_wowlan_support)
kfree(wiphy->wowlan);
#endif
- wiphy_free(wiphy);
}
struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
- struct device *busdev,
+ struct cfg80211_ops *ops,
bool p2pdev_forced)
{
+ struct wiphy *wiphy = drvr->wiphy;
struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev;
struct brcmf_cfg80211_info *cfg;
- struct wiphy *wiphy;
- struct cfg80211_ops *ops;
struct brcmf_cfg80211_vif *vif;
struct brcmf_if *ifp;
s32 err = 0;
@@ -6876,26 +6881,13 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
return NULL;
}
- ops = kmemdup(&brcmf_cfg80211_ops, sizeof(*ops), GFP_KERNEL);
- if (!ops)
- return NULL;
-
- ifp = netdev_priv(ndev);
-#ifdef CONFIG_PM
- if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK))
- ops->set_rekey_data = brcmf_cfg80211_set_rekey_data;
-#endif
- wiphy = wiphy_new(ops, sizeof(struct brcmf_cfg80211_info));
- if (!wiphy) {
+ cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+ if (!cfg) {
brcmf_err("Could not allocate wiphy device\n");
- goto ops_out;
+ return NULL;
}
- memcpy(wiphy->perm_addr, drvr->mac, ETH_ALEN);
- set_wiphy_dev(wiphy, busdev);
- cfg = wiphy_priv(wiphy);
cfg->wiphy = wiphy;
- cfg->ops = ops;
cfg->pub = drvr;
init_vif_event(&cfg->vif_event);
INIT_LIST_HEAD(&cfg->vif_list);
@@ -6904,6 +6896,7 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
if (IS_ERR(vif))
goto wiphy_out;
+ ifp = netdev_priv(ndev);
vif->ifp = ifp;
vif->wdev.netdev = ndev;
ndev->ieee80211_ptr = &vif->wdev;
@@ -6930,6 +6923,11 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
if (err < 0)
goto priv_out;
+ /* regulatory notifer below needs access to cfg so
+ * assign it now.
+ */
+ drvr->config = cfg;
+
brcmf_dbg(INFO, "Registering custom regulatory\n");
wiphy->reg_notifier = brcmf_cfg80211_reg_notifier;
wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
@@ -6943,13 +6941,17 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
cap = &wiphy->bands[NL80211_BAND_2GHZ]->ht_cap.cap;
*cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
}
+#ifdef CONFIG_PM
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK))
+ ops->set_rekey_data = brcmf_cfg80211_set_rekey_data;
+#endif
err = wiphy_register(wiphy);
if (err < 0) {
brcmf_err("Could not register wiphy device (%d)\n", err);
goto priv_out;
}
- err = brcmf_setup_wiphybands(wiphy);
+ err = brcmf_setup_wiphybands(cfg);
if (err) {
brcmf_err("Setting wiphy bands failed (%d)\n", err);
goto wiphy_unreg_out;
@@ -6966,12 +6968,7 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
else
*cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
}
- /* p2p might require that "if-events" get processed by fweh. So
- * activate the already registered event handlers now and activate
- * the rest when initialization has completed. drvr->config needs to
- * be assigned before activating events.
- */
- drvr->config = cfg;
+
err = brcmf_fweh_activate_events(ifp);
if (err) {
brcmf_err("FWEH activation failed (%d)\n", err);
@@ -7039,8 +7036,7 @@ priv_out:
ifp->vif = NULL;
wiphy_out:
brcmf_free_wiphy(wiphy);
-ops_out:
- kfree(ops);
+ kfree(cfg);
return NULL;
}
@@ -7055,4 +7051,5 @@ void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
kfree(cfg->ops);
wl_deinit_priv(cfg);
brcmf_free_wiphy(cfg->wiphy);
+ kfree(cfg);
}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
index b5b5f0f10b63..a4aec0004e4f 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
@@ -355,20 +355,24 @@ static inline struct wiphy *cfg_to_wiphy(struct brcmf_cfg80211_info *cfg)
static inline struct brcmf_cfg80211_info *wiphy_to_cfg(struct wiphy *w)
{
- return (struct brcmf_cfg80211_info *)(wiphy_priv(w));
+ struct brcmf_pub *drvr = wiphy_priv(w);
+ return drvr->config;
}
static inline struct brcmf_cfg80211_info *wdev_to_cfg(struct wireless_dev *wd)
{
- return (struct brcmf_cfg80211_info *)(wdev_priv(wd));
+ return wiphy_to_cfg(wd->wiphy);
+}
+
+static inline struct brcmf_cfg80211_vif *wdev_to_vif(struct wireless_dev *wdev)
+{
+ return container_of(wdev, struct brcmf_cfg80211_vif, wdev);
}
static inline
struct net_device *cfg_to_ndev(struct brcmf_cfg80211_info *cfg)
{
- struct brcmf_cfg80211_vif *vif;
- vif = list_first_entry(&cfg->vif_list, struct brcmf_cfg80211_vif, list);
- return vif->wdev.netdev;
+ return brcmf_get_ifp(cfg->pub, 0)->ndev;
}
static inline struct brcmf_cfg80211_info *ndev_to_cfg(struct net_device *ndev)
@@ -395,11 +399,12 @@ brcmf_cfg80211_connect_info *cfg_to_conn(struct brcmf_cfg80211_info *cfg)
}
struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
- struct device *busdev,
+ struct cfg80211_ops *ops,
bool p2pdev_forced);
void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg);
s32 brcmf_cfg80211_up(struct net_device *ndev);
s32 brcmf_cfg80211_down(struct net_device *ndev);
+struct cfg80211_ops *brcmf_cfg80211_get_ops(void);
enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp);
struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
index f7b30ce2300d..3b829fed8631 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
@@ -464,12 +464,12 @@ static void brcmf_chip_ai_resetcore(struct brcmf_core_priv *core, u32 prereset,
ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL);
}
-static char *brcmf_chip_name(uint chipid, char *buf, uint len)
+char *brcmf_chip_name(u32 id, u32 rev, char *buf, uint len)
{
const char *fmt;
- fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x";
- snprintf(buf, len, fmt, chipid);
+ fmt = ((id > 0xa000) || (id < 0x4000)) ? "BCM%d/%u" : "BCM%x/%u";
+ snprintf(buf, len, fmt, id, rev);
return buf;
}
@@ -924,10 +924,10 @@ static int brcmf_chip_recognition(struct brcmf_chip_priv *ci)
ci->pub.chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT;
socitype = (regdata & CID_TYPE_MASK) >> CID_TYPE_SHIFT;
- brcmf_chip_name(ci->pub.chip, ci->pub.name, sizeof(ci->pub.name));
- brcmf_dbg(INFO, "found %s chip: BCM%s, rev=%d\n",
- socitype == SOCI_SB ? "SB" : "AXI", ci->pub.name,
- ci->pub.chiprev);
+ brcmf_chip_name(ci->pub.chip, ci->pub.chiprev,
+ ci->pub.name, sizeof(ci->pub.name));
+ brcmf_dbg(INFO, "found %s chip: %s\n",
+ socitype == SOCI_SB ? "SB" : "AXI", ci->pub.name);
if (socitype == SOCI_SB) {
if (ci->pub.chip != BRCM_CC_4329_CHIP_ID) {
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h
index dd0ec3eba6a9..0ae3b33bab62 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h
@@ -45,7 +45,7 @@ struct brcmf_chip {
u32 rambase;
u32 ramsize;
u32 srsize;
- char name[8];
+ char name[12];
};
/**
@@ -93,5 +93,6 @@ void brcmf_chip_resetcore(struct brcmf_core *core, u32 prereset, u32 reset,
void brcmf_chip_set_passive(struct brcmf_chip *ci);
bool brcmf_chip_set_active(struct brcmf_chip *ci, u32 rstvec);
bool brcmf_chip_sr_capable(struct brcmf_chip *pub);
+char *brcmf_chip_name(u32 chipid, u32 chiprev, char *buf, uint len);
#endif /* BRCMF_AXIDMP_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
index 9be0b051066a..105b8774fca9 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
@@ -30,6 +30,7 @@
#include "common.h"
#include "of.h"
#include "firmware.h"
+#include "chip.h"
MODULE_AUTHOR("Broadcom Corporation");
MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver.");
@@ -51,7 +52,7 @@ MODULE_PARM_DESC(txglomsz, "Maximum tx packet chain size [SDIO]");
/* Debug level configuration. See debug.h for bits, sysfs modifiable */
int brcmf_msg_level;
-module_param_named(debug, brcmf_msg_level, int, S_IRUSR | S_IWUSR);
+module_param_named(debug, brcmf_msg_level, int, 0600);
MODULE_PARM_DESC(debug, "Level of debug output");
static int brcmf_p2p_enable;
@@ -64,7 +65,7 @@ MODULE_PARM_DESC(feature_disable, "Disable features");
static char brcmf_firmware_path[BRCMF_FW_ALTPATH_LEN];
module_param_string(alternative_fw_path, brcmf_firmware_path,
- BRCMF_FW_ALTPATH_LEN, S_IRUSR);
+ BRCMF_FW_ALTPATH_LEN, 0400);
MODULE_PARM_DESC(alternative_fw_path, "Alternative firmware path");
static int brcmf_fcmode;
@@ -72,9 +73,13 @@ module_param_named(fcmode, brcmf_fcmode, int, 0);
MODULE_PARM_DESC(fcmode, "Mode of firmware signalled flow control");
static int brcmf_roamoff;
-module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR);
+module_param_named(roamoff, brcmf_roamoff, int, 0400);
MODULE_PARM_DESC(roamoff, "Do not use internal roaming engine");
+static int brcmf_iapp_enable;
+module_param_named(iapp, brcmf_iapp_enable, int, 0);
+MODULE_PARM_DESC(iapp, "Enable partial support for the obsoleted Inter-Access Point Protocol");
+
#ifdef DEBUG
/* always succeed brcmf_bus_started() */
static int brcmf_ignore_probe_fail;
@@ -124,43 +129,9 @@ static int brcmf_c_download(struct brcmf_if *ifp, u16 flag,
return err;
}
-static int brcmf_c_get_clm_name(struct brcmf_if *ifp, u8 *clm_name)
-{
- struct brcmf_bus *bus = ifp->drvr->bus_if;
- struct brcmf_rev_info *ri = &ifp->drvr->revinfo;
- u8 fw_name[BRCMF_FW_NAME_LEN];
- u8 *ptr;
- size_t len;
- s32 err;
-
- memset(fw_name, 0, BRCMF_FW_NAME_LEN);
- err = brcmf_bus_get_fwname(bus, ri->chipnum, ri->chiprev, fw_name);
- if (err) {
- brcmf_err("get firmware name failed (%d)\n", err);
- goto done;
- }
-
- /* generate CLM blob file name */
- ptr = strrchr(fw_name, '.');
- if (!ptr) {
- err = -ENOENT;
- goto done;
- }
-
- len = ptr - fw_name + 1;
- if (len + strlen(".clm_blob") > BRCMF_FW_NAME_LEN) {
- err = -E2BIG;
- } else {
- strlcpy(clm_name, fw_name, len);
- strlcat(clm_name, ".clm_blob", BRCMF_FW_NAME_LEN);
- }
-done:
- return err;
-}
-
static int brcmf_c_process_clm_blob(struct brcmf_if *ifp)
{
- struct device *dev = ifp->drvr->bus_if->dev;
+ struct brcmf_bus *bus = ifp->drvr->bus_if;
struct brcmf_dload_data_le *chunk_buf;
const struct firmware *clm = NULL;
u8 clm_name[BRCMF_FW_NAME_LEN];
@@ -173,16 +144,16 @@ static int brcmf_c_process_clm_blob(struct brcmf_if *ifp)
brcmf_dbg(TRACE, "Enter\n");
- memset(clm_name, 0, BRCMF_FW_NAME_LEN);
- err = brcmf_c_get_clm_name(ifp, clm_name);
+ memset(clm_name, 0, sizeof(clm_name));
+ err = brcmf_bus_get_fwname(bus, ".clm_blob", clm_name);
if (err) {
brcmf_err("get CLM blob file name failed (%d)\n", err);
return err;
}
- err = request_firmware(&clm, clm_name, dev);
+ err = request_firmware(&clm, clm_name, bus->dev);
if (err) {
- brcmf_info("no clm_blob available(err=%d), device may have limited channels available\n",
+ brcmf_info("no clm_blob available (err=%d), device may have limited channels available\n",
err);
return 0;
}
@@ -234,6 +205,7 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
{
s8 eventmask[BRCMF_EVENTING_MASK_LEN];
u8 buf[BRCMF_DCMD_SMLEN];
+ struct brcmf_bus *bus;
struct brcmf_rev_info_le revinfo;
struct brcmf_rev_info *ri;
char *clmver;
@@ -247,18 +219,21 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
brcmf_err("Retreiving cur_etheraddr failed, %d\n", err);
goto done;
}
+ memcpy(ifp->drvr->wiphy->perm_addr, ifp->drvr->mac, ETH_ALEN);
memcpy(ifp->drvr->mac, ifp->mac_addr, sizeof(ifp->drvr->mac));
+ bus = ifp->drvr->bus_if;
+ ri = &ifp->drvr->revinfo;
+
err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_REVINFO,
&revinfo, sizeof(revinfo));
- ri = &ifp->drvr->revinfo;
if (err < 0) {
brcmf_err("retrieving revision info failed, %d\n", err);
+ strlcpy(ri->chipname, "UNKNOWN", sizeof(ri->chipname));
} else {
ri->vendorid = le32_to_cpu(revinfo.vendorid);
ri->deviceid = le32_to_cpu(revinfo.deviceid);
ri->radiorev = le32_to_cpu(revinfo.radiorev);
- ri->chiprev = le32_to_cpu(revinfo.chiprev);
ri->corerev = le32_to_cpu(revinfo.corerev);
ri->boardid = le32_to_cpu(revinfo.boardid);
ri->boardvendor = le32_to_cpu(revinfo.boardvendor);
@@ -266,15 +241,24 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
ri->driverrev = le32_to_cpu(revinfo.driverrev);
ri->ucoderev = le32_to_cpu(revinfo.ucoderev);
ri->bus = le32_to_cpu(revinfo.bus);
- ri->chipnum = le32_to_cpu(revinfo.chipnum);
ri->phytype = le32_to_cpu(revinfo.phytype);
ri->phyrev = le32_to_cpu(revinfo.phyrev);
ri->anarev = le32_to_cpu(revinfo.anarev);
ri->chippkg = le32_to_cpu(revinfo.chippkg);
ri->nvramrev = le32_to_cpu(revinfo.nvramrev);
+
+ /* use revinfo if not known yet */
+ if (!bus->chip) {
+ bus->chip = le32_to_cpu(revinfo.chipnum);
+ bus->chiprev = le32_to_cpu(revinfo.chiprev);
+ }
}
ri->result = err;
+ if (bus->chip)
+ brcmf_chip_name(bus->chip, bus->chiprev,
+ ri->chipname, sizeof(ri->chipname));
+
/* Do any CLM downloading */
err = brcmf_c_process_clm_blob(ifp);
if (err < 0) {
@@ -295,7 +279,7 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
strsep(&ptr, "\n");
/* Print fw version info */
- brcmf_info("Firmware version = %s\n", buf);
+ brcmf_info("Firmware: %s %s\n", ri->chipname, buf);
/* locate firmware version number for ethtool */
ptr = strrchr(buf, ' ') + 1;
@@ -365,9 +349,6 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
/* Enable tx beamforming, errors can be ignored (not supported) */
(void)brcmf_fil_iovar_int_set(ifp, "txbf", 1);
-
- /* do bus specific preinit here */
- err = brcmf_bus_preinit(ifp->drvr->bus_if);
done:
return err;
}
@@ -441,6 +422,7 @@ struct brcmf_mp_device *brcmf_get_module_param(struct device *dev,
settings->feature_disable = brcmf_feature_disable;
settings->fcmode = brcmf_fcmode;
settings->roamoff = !!brcmf_roamoff;
+ settings->iapp = !!brcmf_iapp_enable;
#ifdef DEBUG
settings->ignore_probe_fail = !!brcmf_ignore_probe_fail;
#endif
@@ -514,9 +496,6 @@ static int __init brcmfmac_module_init(void)
{
int err;
- /* Initialize debug system first */
- brcmf_debugfs_init();
-
/* Get the platform data (if available) for our devices */
err = platform_driver_probe(&brcmf_pd, brcmf_common_pd_probe);
if (err == -ENODEV)
@@ -528,7 +507,6 @@ static int __init brcmfmac_module_init(void)
/* Continue the initialization by registering the different busses */
err = brcmf_core_init();
if (err) {
- brcmf_debugfs_exit();
if (brcmfmac_pdata)
platform_driver_unregister(&brcmf_pd);
}
@@ -541,7 +519,6 @@ static void __exit brcmfmac_module_exit(void)
brcmf_core_exit();
if (brcmfmac_pdata)
platform_driver_unregister(&brcmf_pd);
- brcmf_debugfs_exit();
}
module_init(brcmfmac_module_init);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
index a62f8e70b320..ef914619e8e1 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
@@ -58,6 +58,7 @@ struct brcmf_mp_device {
unsigned int feature_disable;
int fcmode;
bool roamoff;
+ bool iapp;
bool ignore_probe_fail;
struct brcmfmac_pd_cc *country_codes;
union {
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
index 930e423f83a8..8d4511eaa9b9 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
@@ -230,6 +230,37 @@ static void brcmf_netdev_set_multicast_list(struct net_device *ndev)
schedule_work(&ifp->multicast_work);
}
+/**
+ * brcmf_skb_is_iapp - checks if skb is an IAPP packet
+ *
+ * @skb: skb to check
+ */
+static bool brcmf_skb_is_iapp(struct sk_buff *skb)
+{
+ static const u8 iapp_l2_update_packet[6] __aligned(2) = {
+ 0x00, 0x01, 0xaf, 0x81, 0x01, 0x00,
+ };
+ unsigned char *eth_data;
+#if !defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
+ const u16 *a, *b;
+#endif
+
+ if (skb->len - skb->mac_len != 6 ||
+ !is_multicast_ether_addr(eth_hdr(skb)->h_dest))
+ return false;
+
+ eth_data = skb_mac_header(skb) + ETH_HLEN;
+#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
+ return !(((*(const u32 *)eth_data) ^ (*(const u32 *)iapp_l2_update_packet)) |
+ ((*(const u16 *)(eth_data + 4)) ^ (*(const u16 *)(iapp_l2_update_packet + 4))));
+#else
+ a = (const u16 *)eth_data;
+ b = (const u16 *)iapp_l2_update_packet;
+
+ return !((a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2]));
+#endif
+}
+
static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb,
struct net_device *ndev)
{
@@ -250,6 +281,23 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb,
goto done;
}
+ /* Some recent Broadcom's firmwares disassociate STA when they receive
+ * an 802.11f ADD frame. This behavior can lead to a local DoS security
+ * issue. Attacker may trigger disassociation of any STA by sending a
+ * proper Ethernet frame to the wireless interface.
+ *
+ * Moreover this feature may break AP interfaces in some specific
+ * setups. This applies e.g. to the bridge with hairpin mode enabled and
+ * IFLA_BRPORT_MCAST_TO_UCAST set. IAPP packet generated by a firmware
+ * will get passed back to the wireless interface and cause immediate
+ * disassociation of a just-connected STA.
+ */
+ if (!drvr->settings->iapp && brcmf_skb_is_iapp(skb)) {
+ dev_kfree_skb(skb);
+ ret = -EINVAL;
+ goto done;
+ }
+
/* Make sure there's enough writeable headroom */
if (skb_headroom(skb) < drvr->hdrlen || skb_header_cloned(skb)) {
head_delta = max_t(int, drvr->hdrlen - skb_headroom(skb), 0);
@@ -325,6 +373,15 @@ void brcmf_txflowblock_if(struct brcmf_if *ifp,
void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb)
{
+ /* Most of Broadcom's firmwares send 802.11f ADD frame every time a new
+ * STA connects to the AP interface. This is an obsoleted standard most
+ * users don't use, so don't pass these frames up unless requested.
+ */
+ if (!ifp->drvr->settings->iapp && brcmf_skb_is_iapp(skb)) {
+ brcmu_pkt_buf_free_skb(skb);
+ return;
+ }
+
if (skb->pkt_type == PACKET_MULTICAST)
ifp->ndev->stats.multicast++;
@@ -914,55 +971,6 @@ static int brcmf_inet6addr_changed(struct notifier_block *nb,
}
#endif
-int brcmf_attach(struct device *dev, struct brcmf_mp_device *settings)
-{
- struct brcmf_pub *drvr = NULL;
- int ret = 0;
- int i;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- /* Allocate primary brcmf_info */
- drvr = kzalloc(sizeof(struct brcmf_pub), GFP_ATOMIC);
- if (!drvr)
- return -ENOMEM;
-
- for (i = 0; i < ARRAY_SIZE(drvr->if2bss); i++)
- drvr->if2bss[i] = BRCMF_BSSIDX_INVALID;
-
- mutex_init(&drvr->proto_block);
-
- /* Link to bus module */
- drvr->hdrlen = 0;
- drvr->bus_if = dev_get_drvdata(dev);
- drvr->bus_if->drvr = drvr;
- drvr->settings = settings;
-
- /* attach debug facilities */
- brcmf_debug_attach(drvr);
-
- /* Attach and link in the protocol */
- ret = brcmf_proto_attach(drvr);
- if (ret != 0) {
- brcmf_err("brcmf_prot_attach failed\n");
- goto fail;
- }
-
- /* Attach to events important for core code */
- brcmf_fweh_register(drvr, BRCMF_E_PSM_WATCHDOG,
- brcmf_psm_watchdog_notify);
-
- /* attach firmware event handler */
- brcmf_fweh_attach(drvr);
-
- return ret;
-
-fail:
- brcmf_detach(dev);
-
- return ret;
-}
-
static int brcmf_revinfo_read(struct seq_file *s, void *data)
{
struct brcmf_bus *bus_if = dev_get_drvdata(s->private);
@@ -973,8 +981,7 @@ static int brcmf_revinfo_read(struct seq_file *s, void *data)
seq_printf(s, "vendorid: 0x%04x\n", ri->vendorid);
seq_printf(s, "deviceid: 0x%04x\n", ri->deviceid);
seq_printf(s, "radiorev: %s\n", brcmu_dotrev_str(ri->radiorev, drev));
- seq_printf(s, "chipnum: %u (%x)\n", ri->chipnum, ri->chipnum);
- seq_printf(s, "chiprev: %u\n", ri->chiprev);
+ seq_printf(s, "chip: %s\n", ri->chipname);
seq_printf(s, "chippkg: %u\n", ri->chippkg);
seq_printf(s, "corerev: %u\n", ri->corerev);
seq_printf(s, "boardid: 0x%04x\n", ri->boardid);
@@ -993,11 +1000,10 @@ static int brcmf_revinfo_read(struct seq_file *s, void *data)
return 0;
}
-int brcmf_bus_started(struct device *dev)
+static int brcmf_bus_started(struct brcmf_pub *drvr, struct cfg80211_ops *ops)
{
int ret = -1;
- struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- struct brcmf_pub *drvr = bus_if->drvr;
+ struct brcmf_bus *bus_if = drvr->bus_if;
struct brcmf_if *ifp;
struct brcmf_if *p2p_ifp;
@@ -1013,20 +1019,16 @@ int brcmf_bus_started(struct device *dev)
/* signal bus ready */
brcmf_bus_change_state(bus_if, BRCMF_BUS_UP);
+ /* do bus specific preinit here */
+ ret = brcmf_bus_preinit(bus_if);
+ if (ret < 0)
+ goto fail;
+
/* Bus is ready, do any initialization */
ret = brcmf_c_preinit_dcmds(ifp);
if (ret < 0)
goto fail;
- brcmf_debugfs_add_entry(drvr, "revinfo", brcmf_revinfo_read);
-
- /* assure we have chipid before feature attach */
- if (!bus_if->chip) {
- bus_if->chip = drvr->revinfo.chipnum;
- bus_if->chiprev = drvr->revinfo.chiprev;
- brcmf_dbg(INFO, "firmware revinfo: chip %x (%d) rev %d\n",
- bus_if->chip, bus_if->chip, bus_if->chiprev);
- }
brcmf_feat_attach(drvr);
ret = brcmf_proto_init_done(drvr);
@@ -1035,7 +1037,7 @@ int brcmf_bus_started(struct device *dev)
brcmf_proto_add_if(drvr, ifp);
- drvr->config = brcmf_cfg80211_attach(drvr, bus_if->dev,
+ drvr->config = brcmf_cfg80211_attach(drvr, ops,
drvr->settings->p2p_enable);
if (drvr->config == NULL) {
ret = -ENOMEM;
@@ -1069,6 +1071,11 @@ int brcmf_bus_started(struct device *dev)
#endif
#endif /* CONFIG_INET */
+ /* populate debugfs */
+ brcmf_debugfs_add_entry(drvr, "revinfo", brcmf_revinfo_read);
+ brcmf_feat_debugfs_create(drvr);
+ brcmf_proto_debugfs_create(drvr);
+
return 0;
fail:
@@ -1088,6 +1095,69 @@ fail:
return ret;
}
+int brcmf_attach(struct device *dev, struct brcmf_mp_device *settings)
+{
+ struct wiphy *wiphy;
+ struct cfg80211_ops *ops;
+ struct brcmf_pub *drvr = NULL;
+ int ret = 0;
+ int i;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ ops = brcmf_cfg80211_get_ops();
+ if (!ops)
+ return -ENOMEM;
+
+ wiphy = wiphy_new(ops, sizeof(*drvr));
+ if (!wiphy)
+ return -ENOMEM;
+
+ set_wiphy_dev(wiphy, dev);
+ drvr = wiphy_priv(wiphy);
+ drvr->wiphy = wiphy;
+
+ for (i = 0; i < ARRAY_SIZE(drvr->if2bss); i++)
+ drvr->if2bss[i] = BRCMF_BSSIDX_INVALID;
+
+ mutex_init(&drvr->proto_block);
+
+ /* Link to bus module */
+ drvr->hdrlen = 0;
+ drvr->bus_if = dev_get_drvdata(dev);
+ drvr->bus_if->drvr = drvr;
+ drvr->settings = settings;
+
+ /* Attach and link in the protocol */
+ ret = brcmf_proto_attach(drvr);
+ if (ret != 0) {
+ brcmf_err("brcmf_prot_attach failed\n");
+ goto fail;
+ }
+
+ /* Attach to events important for core code */
+ brcmf_fweh_register(drvr, BRCMF_E_PSM_WATCHDOG,
+ brcmf_psm_watchdog_notify);
+
+ /* attach firmware event handler */
+ brcmf_fweh_attach(drvr);
+
+ ret = brcmf_bus_started(drvr, ops);
+ if (ret != 0) {
+ brcmf_err("dongle is not responding: err=%d\n", ret);
+ goto fail;
+ }
+
+ drvr->config->ops = ops;
+ return 0;
+
+fail:
+ brcmf_detach(dev);
+ kfree(ops);
+
+ return ret;
+}
+
void brcmf_bus_add_txhdrlen(struct device *dev, uint len)
{
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
@@ -1141,14 +1211,14 @@ void brcmf_detach(struct device *dev)
brcmf_remove_interface(drvr->iflist[i], false);
brcmf_cfg80211_detach(drvr->config);
+ drvr->config = NULL;
brcmf_bus_stop(drvr->bus_if);
brcmf_proto_detach(drvr);
- brcmf_debug_detach(drvr);
bus_if->drvr = NULL;
- kfree(drvr);
+ wiphy_free(drvr->wiphy);
}
s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data, u32 len)
@@ -1185,6 +1255,12 @@ void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state)
int ifidx;
brcmf_dbg(TRACE, "%d -> %d\n", bus->state, state);
+
+ if (!drvr) {
+ brcmf_dbg(INFO, "ignoring transition, bus not attached yet\n");
+ return;
+ }
+
bus->state = state;
if (state == BRCMF_BUS_UP) {
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
index 232dcbb83311..401f50458686 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
@@ -87,7 +87,6 @@ struct brcmf_rev_info {
u32 vendorid;
u32 deviceid;
u32 radiorev;
- u32 chiprev;
u32 corerev;
u32 boardid;
u32 boardvendor;
@@ -95,7 +94,7 @@ struct brcmf_rev_info {
u32 driverrev;
u32 ucoderev;
u32 bus;
- u32 chipnum;
+ char chipname[12];
u32 phytype;
u32 phyrev;
u32 anarev;
@@ -108,6 +107,7 @@ struct brcmf_pub {
/* Linkage ponters */
struct brcmf_bus *bus_if;
struct brcmf_proto *proto;
+ struct wiphy *wiphy;
struct brcmf_cfg80211_info *config;
/* Internal brcmf items */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c
index 2d3e5e263a32..504832084eca 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c
@@ -25,8 +25,6 @@
#include "fweh.h"
#include "debug.h"
-static struct dentry *root_folder;
-
int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data,
size_t len)
{
@@ -54,44 +52,9 @@ int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data,
return 0;
}
-void brcmf_debugfs_init(void)
-{
- root_folder = debugfs_create_dir(KBUILD_MODNAME, NULL);
- if (IS_ERR(root_folder))
- root_folder = NULL;
-}
-
-void brcmf_debugfs_exit(void)
-{
- if (!root_folder)
- return;
-
- debugfs_remove_recursive(root_folder);
- root_folder = NULL;
-}
-
-int brcmf_debug_attach(struct brcmf_pub *drvr)
-{
- struct device *dev = drvr->bus_if->dev;
-
- if (!root_folder)
- return -ENODEV;
-
- drvr->dbgfs_dir = debugfs_create_dir(dev_name(dev), root_folder);
- return PTR_ERR_OR_ZERO(drvr->dbgfs_dir);
-}
-
-void brcmf_debug_detach(struct brcmf_pub *drvr)
-{
- brcmf_fweh_unregister(drvr, BRCMF_E_PSM_WATCHDOG);
-
- if (!IS_ERR_OR_NULL(drvr->dbgfs_dir))
- debugfs_remove_recursive(drvr->dbgfs_dir);
-}
-
struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr)
{
- return drvr->dbgfs_dir;
+ return drvr->wiphy->debugfsdir;
}
int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn,
@@ -99,7 +62,8 @@ int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn,
{
struct dentry *e;
+ WARN(!drvr->wiphy->debugfsdir, "wiphy not (yet) registered\n");
e = debugfs_create_devm_seqfile(drvr->bus_if->dev, fn,
- drvr->dbgfs_dir, read_fn);
+ drvr->wiphy->debugfsdir, read_fn);
return PTR_ERR_OR_ZERO(e);
}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h
index 35919d9e8e13..cfed0626bf5a 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h
@@ -113,29 +113,12 @@ extern int brcmf_msg_level;
struct brcmf_bus;
struct brcmf_pub;
#ifdef DEBUG
-void brcmf_debugfs_init(void);
-void brcmf_debugfs_exit(void);
-int brcmf_debug_attach(struct brcmf_pub *drvr);
-void brcmf_debug_detach(struct brcmf_pub *drvr);
struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr);
int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn,
int (*read_fn)(struct seq_file *seq, void *data));
int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data,
size_t len);
#else
-static inline void brcmf_debugfs_init(void)
-{
-}
-static inline void brcmf_debugfs_exit(void)
-{
-}
-static inline int brcmf_debug_attach(struct brcmf_pub *drvr)
-{
- return 0;
-}
-static inline void brcmf_debug_detach(struct brcmf_pub *drvr)
-{
-}
static inline
int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn,
int (*read_fn)(struct seq_file *seq, void *data))
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
index bede7b7fd996..876731c57bf5 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
@@ -228,7 +228,10 @@ void brcmf_feat_attach(struct brcmf_pub *drvr)
/* no quirks */
break;
}
+}
+void brcmf_feat_debugfs_create(struct brcmf_pub *drvr)
+{
brcmf_debugfs_add_entry(drvr, "features", brcmf_feat_debugfs_read);
}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
index 1ab4f1617112..d1193825e559 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
@@ -90,6 +90,13 @@ enum brcmf_feat_quirk {
void brcmf_feat_attach(struct brcmf_pub *drvr);
/**
+ * brcmf_feat_debugfs_create() - create debugfs entries.
+ *
+ * @drvr: driver instance.
+ */
+void brcmf_feat_debugfs_create(struct brcmf_pub *drvr);
+
+/**
* brcmf_feat_is_enabled() - query feature.
*
* @ifp: interface instance.
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
index 091b52979e03..9277f4c2bfeb 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
@@ -25,6 +25,7 @@
#include "firmware.h"
#include "core.h"
#include "common.h"
+#include "chip.h"
#define BRCMF_FW_MAX_NVRAM_SIZE 64000
#define BRCMF_FW_NVRAM_DEVPATH_LEN 19 /* devpath0=pcie/1/4/ */
@@ -437,18 +438,31 @@ void brcmf_fw_nvram_free(void *nvram)
struct brcmf_fw {
struct device *dev;
- u16 flags;
- const struct firmware *code;
- const char *nvram_name;
- u16 domain_nr;
- u16 bus_nr;
- void (*done)(struct device *dev, int err, const struct firmware *fw,
- void *nvram_image, u32 nvram_len);
+ struct brcmf_fw_request *req;
+ u32 curpos;
+ void (*done)(struct device *dev, int err, struct brcmf_fw_request *req);
};
+static void brcmf_fw_request_done(const struct firmware *fw, void *ctx);
+
+static void brcmf_fw_free_request(struct brcmf_fw_request *req)
+{
+ struct brcmf_fw_item *item;
+ int i;
+
+ for (i = 0, item = &req->items[0]; i < req->n_items; i++, item++) {
+ if (item->type == BRCMF_FW_TYPE_BINARY)
+ release_firmware(item->binary);
+ else if (item->type == BRCMF_FW_TYPE_NVRAM)
+ brcmf_fw_nvram_free(item->nv_data.data);
+ }
+ kfree(req);
+}
+
static void brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx)
{
struct brcmf_fw *fwctx = ctx;
+ struct brcmf_fw_item *cur;
u32 nvram_length = 0;
void *nvram = NULL;
u8 *data = NULL;
@@ -456,83 +470,150 @@ static void brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx)
bool raw_nvram;
brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev));
+
+ cur = &fwctx->req->items[fwctx->curpos];
+
if (fw && fw->data) {
data = (u8 *)fw->data;
data_len = fw->size;
raw_nvram = false;
} else {
data = bcm47xx_nvram_get_contents(&data_len);
- if (!data && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL))
+ if (!data && !(cur->flags & BRCMF_FW_REQF_OPTIONAL))
goto fail;
raw_nvram = true;
}
if (data)
nvram = brcmf_fw_nvram_strip(data, data_len, &nvram_length,
- fwctx->domain_nr, fwctx->bus_nr);
+ fwctx->req->domain_nr,
+ fwctx->req->bus_nr);
if (raw_nvram)
bcm47xx_nvram_release_contents(data);
release_firmware(fw);
- if (!nvram && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL))
+ if (!nvram && !(cur->flags & BRCMF_FW_REQF_OPTIONAL))
goto fail;
- fwctx->done(fwctx->dev, 0, fwctx->code, nvram, nvram_length);
- kfree(fwctx);
+ brcmf_dbg(TRACE, "nvram %p len %d\n", nvram, nvram_length);
+ cur->nv_data.data = nvram;
+ cur->nv_data.len = nvram_length;
return;
fail:
brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev));
- release_firmware(fwctx->code);
- fwctx->done(fwctx->dev, -ENOENT, NULL, NULL, 0);
+ fwctx->done(fwctx->dev, -ENOENT, NULL);
+ brcmf_fw_free_request(fwctx->req);
kfree(fwctx);
}
-static void brcmf_fw_request_code_done(const struct firmware *fw, void *ctx)
+static int brcmf_fw_request_next_item(struct brcmf_fw *fwctx, bool async)
+{
+ struct brcmf_fw_item *cur;
+ const struct firmware *fw = NULL;
+ int ret;
+
+ cur = &fwctx->req->items[fwctx->curpos];
+
+ brcmf_dbg(TRACE, "%srequest for %s\n", async ? "async " : "",
+ cur->path);
+
+ if (async)
+ ret = request_firmware_nowait(THIS_MODULE, true, cur->path,
+ fwctx->dev, GFP_KERNEL, fwctx,
+ brcmf_fw_request_done);
+ else
+ ret = request_firmware(&fw, cur->path, fwctx->dev);
+
+ if (ret < 0) {
+ brcmf_fw_request_done(NULL, fwctx);
+ } else if (!async && fw) {
+ brcmf_dbg(TRACE, "firmware %s %sfound\n", cur->path,
+ fw ? "" : "not ");
+ if (cur->type == BRCMF_FW_TYPE_BINARY)
+ cur->binary = fw;
+ else if (cur->type == BRCMF_FW_TYPE_NVRAM)
+ brcmf_fw_request_nvram_done(fw, fwctx);
+ else
+ release_firmware(fw);
+
+ return -EAGAIN;
+ }
+ return 0;
+}
+
+static void brcmf_fw_request_done(const struct firmware *fw, void *ctx)
{
struct brcmf_fw *fwctx = ctx;
+ struct brcmf_fw_item *cur;
int ret = 0;
- brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev));
- if (!fw) {
+ cur = &fwctx->req->items[fwctx->curpos];
+
+ brcmf_dbg(TRACE, "enter: firmware %s %sfound\n", cur->path,
+ fw ? "" : "not ");
+
+ if (fw) {
+ if (cur->type == BRCMF_FW_TYPE_BINARY)
+ cur->binary = fw;
+ else if (cur->type == BRCMF_FW_TYPE_NVRAM)
+ brcmf_fw_request_nvram_done(fw, fwctx);
+ else
+ release_firmware(fw);
+ } else if (cur->type == BRCMF_FW_TYPE_NVRAM) {
+ brcmf_fw_request_nvram_done(NULL, fwctx);
+ } else if (!(cur->flags & BRCMF_FW_REQF_OPTIONAL)) {
ret = -ENOENT;
goto fail;
}
- /* only requested code so done here */
- if (!(fwctx->flags & BRCMF_FW_REQUEST_NVRAM))
- goto done;
- fwctx->code = fw;
- ret = request_firmware_nowait(THIS_MODULE, true, fwctx->nvram_name,
- fwctx->dev, GFP_KERNEL, fwctx,
- brcmf_fw_request_nvram_done);
+ do {
+ if (++fwctx->curpos == fwctx->req->n_items) {
+ ret = 0;
+ goto done;
+ }
+
+ ret = brcmf_fw_request_next_item(fwctx, false);
+ } while (ret == -EAGAIN);
- /* pass NULL to nvram callback for bcm47xx fallback */
- if (ret)
- brcmf_fw_request_nvram_done(NULL, fwctx);
return;
fail:
- brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev));
+ brcmf_dbg(TRACE, "failed err=%d: dev=%s, fw=%s\n", ret,
+ dev_name(fwctx->dev), cur->path);
+ brcmf_fw_free_request(fwctx->req);
+ fwctx->req = NULL;
done:
- fwctx->done(fwctx->dev, ret, fw, NULL, 0);
+ fwctx->done(fwctx->dev, ret, fwctx->req);
kfree(fwctx);
}
-int brcmf_fw_get_firmwares_pcie(struct device *dev, u16 flags,
- const char *code, const char *nvram,
- void (*fw_cb)(struct device *dev, int err,
- const struct firmware *fw,
- void *nvram_image, u32 nvram_len),
- u16 domain_nr, u16 bus_nr)
+static bool brcmf_fw_request_is_valid(struct brcmf_fw_request *req)
+{
+ struct brcmf_fw_item *item;
+ int i;
+
+ if (!req->n_items)
+ return false;
+
+ for (i = 0, item = &req->items[0]; i < req->n_items; i++, item++) {
+ if (!item->path)
+ return false;
+ }
+ return true;
+}
+
+int brcmf_fw_get_firmwares(struct device *dev, struct brcmf_fw_request *req,
+ void (*fw_cb)(struct device *dev, int err,
+ struct brcmf_fw_request *req))
{
struct brcmf_fw *fwctx;
brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(dev));
- if (!fw_cb || !code)
+ if (!fw_cb)
return -EINVAL;
- if ((flags & BRCMF_FW_REQUEST_NVRAM) && !nvram)
+ if (!brcmf_fw_request_is_valid(req))
return -EINVAL;
fwctx = kzalloc(sizeof(*fwctx), GFP_KERNEL);
@@ -540,35 +621,25 @@ int brcmf_fw_get_firmwares_pcie(struct device *dev, u16 flags,
return -ENOMEM;
fwctx->dev = dev;
- fwctx->flags = flags;
+ fwctx->req = req;
fwctx->done = fw_cb;
- if (flags & BRCMF_FW_REQUEST_NVRAM)
- fwctx->nvram_name = nvram;
- fwctx->domain_nr = domain_nr;
- fwctx->bus_nr = bus_nr;
-
- return request_firmware_nowait(THIS_MODULE, true, code, dev,
- GFP_KERNEL, fwctx,
- brcmf_fw_request_code_done);
-}
-int brcmf_fw_get_firmwares(struct device *dev, u16 flags,
- const char *code, const char *nvram,
- void (*fw_cb)(struct device *dev, int err,
- const struct firmware *fw,
- void *nvram_image, u32 nvram_len))
-{
- return brcmf_fw_get_firmwares_pcie(dev, flags, code, nvram, fw_cb, 0,
- 0);
+ brcmf_fw_request_next_item(fwctx, true);
+ return 0;
}
-int brcmf_fw_map_chip_to_name(u32 chip, u32 chiprev,
- struct brcmf_firmware_mapping mapping_table[],
- u32 table_size, char fw_name[BRCMF_FW_NAME_LEN],
- char nvram_name[BRCMF_FW_NAME_LEN])
+struct brcmf_fw_request *
+brcmf_fw_alloc_request(u32 chip, u32 chiprev,
+ struct brcmf_firmware_mapping mapping_table[],
+ u32 table_size, struct brcmf_fw_name *fwnames,
+ u32 n_fwnames)
{
- u32 i;
+ struct brcmf_fw_request *fwreq;
+ char chipname[12];
+ const char *mp_path;
+ u32 i, j;
char end;
+ size_t reqsz;
for (i = 0; i < table_size; i++) {
if (mapping_table[i].chipid == chip &&
@@ -578,32 +649,41 @@ int brcmf_fw_map_chip_to_name(u32 chip, u32 chiprev,
if (i == table_size) {
brcmf_err("Unknown chipid %d [%d]\n", chip, chiprev);
- return -ENODEV;
+ return NULL;
}
- /* check if firmware path is provided by module parameter */
- if (brcmf_mp_global.firmware_path[0] != '\0') {
- strlcpy(fw_name, brcmf_mp_global.firmware_path,
- BRCMF_FW_NAME_LEN);
- if ((nvram_name) && (mapping_table[i].nvram))
- strlcpy(nvram_name, brcmf_mp_global.firmware_path,
+ reqsz = sizeof(*fwreq) + n_fwnames * sizeof(struct brcmf_fw_item);
+ fwreq = kzalloc(reqsz, GFP_KERNEL);
+ if (!fwreq)
+ return NULL;
+
+ brcmf_chip_name(chip, chiprev, chipname, sizeof(chipname));
+
+ brcmf_info("using %s for chip %s\n",
+ mapping_table[i].fw_base, chipname);
+
+ mp_path = brcmf_mp_global.firmware_path;
+ end = mp_path[strlen(mp_path) - 1];
+ fwreq->n_items = n_fwnames;
+
+ for (j = 0; j < n_fwnames; j++) {
+ fwreq->items[j].path = fwnames[j].path;
+ /* check if firmware path is provided by module parameter */
+ if (brcmf_mp_global.firmware_path[0] != '\0') {
+ strlcpy(fwnames[j].path, mp_path,
BRCMF_FW_NAME_LEN);
- end = brcmf_mp_global.firmware_path[
- strlen(brcmf_mp_global.firmware_path) - 1];
- if (end != '/') {
- strlcat(fw_name, "/", BRCMF_FW_NAME_LEN);
- if ((nvram_name) && (mapping_table[i].nvram))
- strlcat(nvram_name, "/", BRCMF_FW_NAME_LEN);
+ if (end != '/') {
+ strlcat(fwnames[j].path, "/",
+ BRCMF_FW_NAME_LEN);
+ }
}
+ strlcat(fwnames[j].path, mapping_table[i].fw_base,
+ BRCMF_FW_NAME_LEN);
+ strlcat(fwnames[j].path, fwnames[j].extension,
+ BRCMF_FW_NAME_LEN);
+ fwreq->items[j].path = fwnames[j].path;
}
- strlcat(fw_name, mapping_table[i].fw, BRCMF_FW_NAME_LEN);
- if ((nvram_name) && (mapping_table[i].nvram))
- strlcat(nvram_name, mapping_table[i].nvram, BRCMF_FW_NAME_LEN);
- brcmf_info("using %s for chip %#08x(%d) rev %#08x\n",
- fw_name, chip, chip, chiprev);
-
- return 0;
+ return fwreq;
}
-
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h
index 8fa4b7e1ab3d..79a21095c349 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h
@@ -16,10 +16,7 @@
#ifndef BRCMFMAC_FIRMWARE_H
#define BRCMFMAC_FIRMWARE_H
-#define BRCMF_FW_REQUEST 0x000F
-#define BRCMF_FW_REQUEST_NVRAM 0x0001
-#define BRCMF_FW_REQ_FLAGS 0x00F0
-#define BRCMF_FW_REQ_NV_OPTIONAL 0x0010
+#define BRCMF_FW_REQF_OPTIONAL 0x0001
#define BRCMF_FW_NAME_LEN 320
@@ -38,49 +35,62 @@
struct brcmf_firmware_mapping {
u32 chipid;
u32 revmask;
- const char *fw;
- const char *nvram;
+ const char *fw_base;
};
-#define BRCMF_FW_NVRAM_DEF(fw_nvram_name, fw, nvram) \
-static const char BRCM_ ## fw_nvram_name ## _FIRMWARE_NAME[] = \
- BRCMF_FW_DEFAULT_PATH fw; \
-static const char BRCM_ ## fw_nvram_name ## _NVRAM_NAME[] = \
- BRCMF_FW_DEFAULT_PATH nvram; \
-MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH fw);
-
-#define BRCMF_FW_DEF(fw_name, fw) \
-static const char BRCM_ ## fw_name ## _FIRMWARE_NAME[] = \
- BRCMF_FW_DEFAULT_PATH fw; \
-MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH fw) \
-
-#define BRCMF_FW_NVRAM_ENTRY(chipid, mask, name) \
- { chipid, mask, \
- BRCM_ ## name ## _FIRMWARE_NAME, BRCM_ ## name ## _NVRAM_NAME }
+#define BRCMF_FW_DEF(fw_name, fw_base) \
+static const char BRCM_ ## fw_name ## _FIRMWARE_BASENAME[] = \
+ BRCMF_FW_DEFAULT_PATH fw_base; \
+MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH fw_base ".bin")
#define BRCMF_FW_ENTRY(chipid, mask, name) \
- { chipid, mask, BRCM_ ## name ## _FIRMWARE_NAME, NULL }
+ { chipid, mask, BRCM_ ## name ## _FIRMWARE_BASENAME }
-int brcmf_fw_map_chip_to_name(u32 chip, u32 chiprev,
- struct brcmf_firmware_mapping mapping_table[],
- u32 table_size, char fw_name[BRCMF_FW_NAME_LEN],
- char nvram_name[BRCMF_FW_NAME_LEN]);
void brcmf_fw_nvram_free(void *nvram);
+
+enum brcmf_fw_type {
+ BRCMF_FW_TYPE_BINARY,
+ BRCMF_FW_TYPE_NVRAM
+};
+
+struct brcmf_fw_item {
+ const char *path;
+ enum brcmf_fw_type type;
+ u16 flags;
+ union {
+ const struct firmware *binary;
+ struct {
+ void *data;
+ u32 len;
+ } nv_data;
+ };
+};
+
+struct brcmf_fw_request {
+ u16 domain_nr;
+ u16 bus_nr;
+ u32 n_items;
+ struct brcmf_fw_item items[0];
+};
+
+struct brcmf_fw_name {
+ const char *extension;
+ char *path;
+};
+
+struct brcmf_fw_request *
+brcmf_fw_alloc_request(u32 chip, u32 chiprev,
+ struct brcmf_firmware_mapping mapping_table[],
+ u32 table_size, struct brcmf_fw_name *fwnames,
+ u32 n_fwnames);
+
/*
* Request firmware(s) asynchronously. When the asynchronous request
* fails it will not use the callback, but call device_release_driver()
* instead which will call the driver .remove() callback.
*/
-int brcmf_fw_get_firmwares_pcie(struct device *dev, u16 flags,
- const char *code, const char *nvram,
- void (*fw_cb)(struct device *dev, int err,
- const struct firmware *fw,
- void *nvram_image, u32 nvram_len),
- u16 domain_nr, u16 bus_nr);
-int brcmf_fw_get_firmwares(struct device *dev, u16 flags,
- const char *code, const char *nvram,
+int brcmf_fw_get_firmwares(struct device *dev, struct brcmf_fw_request *req,
void (*fw_cb)(struct device *dev, int err,
- const struct firmware *fw,
- void *nvram_image, u32 nvram_len));
+ struct brcmf_fw_request *req));
#endif /* BRCMFMAC_FIRMWARE_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c
index fc5751116d99..802d7cb73b80 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c
@@ -124,8 +124,7 @@ brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set)
data, len, &fwerr);
if (err) {
- brcmf_dbg(FIL, "Failed: %s (%d)\n",
- brcmf_fil_get_errstr((u32)(-err)), err);
+ brcmf_dbg(FIL, "Failed: error=%d\n", err);
} else if (fwerr < 0) {
brcmf_dbg(FIL, "Firmware error: %s (%d)\n",
brcmf_fil_get_errstr((u32)(-fwerr)), fwerr);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c
index f59642b2c935..f3cbf78c8899 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c
@@ -2399,10 +2399,6 @@ struct brcmf_fws_info *brcmf_fws_attach(struct brcmf_pub *drvr)
brcmu_pktq_init(&fws->desc.other.psq, BRCMF_FWS_PSQ_PREC_COUNT,
BRCMF_FWS_PSQ_LEN);
- /* create debugfs file for statistics */
- brcmf_debugfs_add_entry(drvr, "fws_stats",
- brcmf_debugfs_fws_stats_read);
-
brcmf_dbg(INFO, "%s bdcv2 tlv signaling [%x]\n",
fws->fw_signals ? "enabled" : "disabled", tlv);
return fws;
@@ -2429,6 +2425,13 @@ void brcmf_fws_detach(struct brcmf_fws_info *fws)
kfree(fws);
}
+void brcmf_fws_debugfs_create(struct brcmf_pub *drvr)
+{
+ /* create debugfs file for statistics */
+ brcmf_debugfs_add_entry(drvr, "fws_stats",
+ brcmf_debugfs_fws_stats_read);
+}
+
bool brcmf_fws_queue_skbs(struct brcmf_fws_info *fws)
{
return !fws->avoid_queueing;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h
index ba07bd972002..4e6835766d5d 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h
@@ -20,6 +20,7 @@
struct brcmf_fws_info *brcmf_fws_attach(struct brcmf_pub *drvr);
void brcmf_fws_detach(struct brcmf_fws_info *fws);
+void brcmf_fws_debugfs_create(struct brcmf_pub *drvr);
bool brcmf_fws_queue_skbs(struct brcmf_fws_info *fws);
bool brcmf_fws_fc_active(struct brcmf_fws_info *fws);
void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
index e212a791a072..49d37ad96958 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
@@ -1418,6 +1418,11 @@ static int brcmf_msgbuf_stats_read(struct seq_file *seq, void *data)
}
#endif
+static void brcmf_msgbuf_debugfs_create(struct brcmf_pub *drvr)
+{
+ brcmf_debugfs_add_entry(drvr, "msgbuf_stats", brcmf_msgbuf_stats_read);
+}
+
int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr)
{
struct brcmf_bus_msgbuf *if_msgbuf;
@@ -1472,6 +1477,7 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr)
drvr->proto->delete_peer = brcmf_msgbuf_delete_peer;
drvr->proto->add_tdls_peer = brcmf_msgbuf_add_tdls_peer;
drvr->proto->rxreorder = brcmf_msgbuf_rxreorder;
+ drvr->proto->debugfs_create = brcmf_msgbuf_debugfs_create;
drvr->proto->pd = msgbuf;
init_waitqueue_head(&msgbuf->ioctl_resp_wait);
@@ -1525,8 +1531,6 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr)
spin_lock_init(&msgbuf->flowring_work_lock);
INIT_LIST_HEAD(&msgbuf->work_queue);
- brcmf_debugfs_add_entry(drvr, "msgbuf_stats", brcmf_msgbuf_stats_read);
-
return 0;
fail:
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
index 82064e909784..bcef208a81a5 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
@@ -2227,7 +2227,7 @@ fail:
*/
int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev)
{
- struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
struct brcmf_p2p_info *p2p = &cfg->p2p;
struct brcmf_cfg80211_vif *vif;
enum nl80211_iftype iftype;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
index 8752707557bf..091c191ce259 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
@@ -46,36 +46,36 @@ enum brcmf_pcie_state {
BRCMFMAC_PCIE_STATE_UP
};
-BRCMF_FW_NVRAM_DEF(43602, "brcmfmac43602-pcie.bin", "brcmfmac43602-pcie.txt");
-BRCMF_FW_NVRAM_DEF(4350, "brcmfmac4350-pcie.bin", "brcmfmac4350-pcie.txt");
-BRCMF_FW_NVRAM_DEF(4350C, "brcmfmac4350c2-pcie.bin", "brcmfmac4350c2-pcie.txt");
-BRCMF_FW_NVRAM_DEF(4356, "brcmfmac4356-pcie.bin", "brcmfmac4356-pcie.txt");
-BRCMF_FW_NVRAM_DEF(43570, "brcmfmac43570-pcie.bin", "brcmfmac43570-pcie.txt");
-BRCMF_FW_NVRAM_DEF(4358, "brcmfmac4358-pcie.bin", "brcmfmac4358-pcie.txt");
-BRCMF_FW_NVRAM_DEF(4359, "brcmfmac4359-pcie.bin", "brcmfmac4359-pcie.txt");
-BRCMF_FW_NVRAM_DEF(4365B, "brcmfmac4365b-pcie.bin", "brcmfmac4365b-pcie.txt");
-BRCMF_FW_NVRAM_DEF(4365C, "brcmfmac4365c-pcie.bin", "brcmfmac4365c-pcie.txt");
-BRCMF_FW_NVRAM_DEF(4366B, "brcmfmac4366b-pcie.bin", "brcmfmac4366b-pcie.txt");
-BRCMF_FW_NVRAM_DEF(4366C, "brcmfmac4366c-pcie.bin", "brcmfmac4366c-pcie.txt");
-BRCMF_FW_NVRAM_DEF(4371, "brcmfmac4371-pcie.bin", "brcmfmac4371-pcie.txt");
+BRCMF_FW_DEF(43602, "brcmfmac43602-pcie");
+BRCMF_FW_DEF(4350, "brcmfmac4350-pcie");
+BRCMF_FW_DEF(4350C, "brcmfmac4350c2-pcie");
+BRCMF_FW_DEF(4356, "brcmfmac4356-pcie");
+BRCMF_FW_DEF(43570, "brcmfmac43570-pcie");
+BRCMF_FW_DEF(4358, "brcmfmac4358-pcie");
+BRCMF_FW_DEF(4359, "brcmfmac4359-pcie");
+BRCMF_FW_DEF(4365B, "brcmfmac4365b-pcie");
+BRCMF_FW_DEF(4365C, "brcmfmac4365c-pcie");
+BRCMF_FW_DEF(4366B, "brcmfmac4366b-pcie");
+BRCMF_FW_DEF(4366C, "brcmfmac4366c-pcie");
+BRCMF_FW_DEF(4371, "brcmfmac4371-pcie");
static struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = {
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43602_CHIP_ID, 0xFFFFFFFF, 43602),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43465_CHIP_ID, 0xFFFFFFF0, 4366C),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4350_CHIP_ID, 0x000000FF, 4350C),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4350_CHIP_ID, 0xFFFFFF00, 4350),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43525_CHIP_ID, 0xFFFFFFF0, 4365C),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4356_CHIP_ID, 0xFFFFFFFF, 4356),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43567_CHIP_ID, 0xFFFFFFFF, 43570),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43569_CHIP_ID, 0xFFFFFFFF, 43570),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43570_CHIP_ID, 0xFFFFFFFF, 43570),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4358_CHIP_ID, 0xFFFFFFFF, 4358),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4359_CHIP_ID, 0xFFFFFFFF, 4359),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4365_CHIP_ID, 0x0000000F, 4365B),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4365_CHIP_ID, 0xFFFFFFF0, 4365C),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4366_CHIP_ID, 0x0000000F, 4366B),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4366_CHIP_ID, 0xFFFFFFF0, 4366C),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4371_CHIP_ID, 0xFFFFFFFF, 4371),
+ BRCMF_FW_ENTRY(BRCM_CC_43602_CHIP_ID, 0xFFFFFFFF, 43602),
+ BRCMF_FW_ENTRY(BRCM_CC_43465_CHIP_ID, 0xFFFFFFF0, 4366C),
+ BRCMF_FW_ENTRY(BRCM_CC_4350_CHIP_ID, 0x000000FF, 4350C),
+ BRCMF_FW_ENTRY(BRCM_CC_4350_CHIP_ID, 0xFFFFFF00, 4350),
+ BRCMF_FW_ENTRY(BRCM_CC_43525_CHIP_ID, 0xFFFFFFF0, 4365C),
+ BRCMF_FW_ENTRY(BRCM_CC_4356_CHIP_ID, 0xFFFFFFFF, 4356),
+ BRCMF_FW_ENTRY(BRCM_CC_43567_CHIP_ID, 0xFFFFFFFF, 43570),
+ BRCMF_FW_ENTRY(BRCM_CC_43569_CHIP_ID, 0xFFFFFFFF, 43570),
+ BRCMF_FW_ENTRY(BRCM_CC_43570_CHIP_ID, 0xFFFFFFFF, 43570),
+ BRCMF_FW_ENTRY(BRCM_CC_4358_CHIP_ID, 0xFFFFFFFF, 4358),
+ BRCMF_FW_ENTRY(BRCM_CC_4359_CHIP_ID, 0xFFFFFFFF, 4359),
+ BRCMF_FW_ENTRY(BRCM_CC_4365_CHIP_ID, 0x0000000F, 4365B),
+ BRCMF_FW_ENTRY(BRCM_CC_4365_CHIP_ID, 0xFFFFFFF0, 4365C),
+ BRCMF_FW_ENTRY(BRCM_CC_4366_CHIP_ID, 0x0000000F, 4366B),
+ BRCMF_FW_ENTRY(BRCM_CC_4366_CHIP_ID, 0xFFFFFFF0, 4366C),
+ BRCMF_FW_ENTRY(BRCM_CC_4371_CHIP_ID, 0xFFFFFFFF, 4371),
};
#define BRCMF_PCIE_FW_UP_TIMEOUT 2000 /* msec */
@@ -1350,23 +1350,24 @@ static int brcmf_pcie_get_memdump(struct device *dev, void *data, size_t len)
return 0;
}
-static int brcmf_pcie_get_fwname(struct device *dev, u32 chip, u32 chiprev,
- u8 *fw_name)
+static
+int brcmf_pcie_get_fwname(struct device *dev, const char *ext, u8 *fw_name)
{
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie;
- struct brcmf_pciedev_info *devinfo = buspub->devinfo;
- int ret = 0;
-
- if (devinfo->fw_name[0] != '\0')
- strlcpy(fw_name, devinfo->fw_name, BRCMF_FW_NAME_LEN);
- else
- ret = brcmf_fw_map_chip_to_name(chip, chiprev,
- brcmf_pcie_fwnames,
- ARRAY_SIZE(brcmf_pcie_fwnames),
- fw_name, NULL);
-
- return ret;
+ struct brcmf_fw_request *fwreq;
+ struct brcmf_fw_name fwnames[] = {
+ { ext, fw_name },
+ };
+
+ fwreq = brcmf_fw_alloc_request(bus_if->chip, bus_if->chiprev,
+ brcmf_pcie_fwnames,
+ ARRAY_SIZE(brcmf_pcie_fwnames),
+ fwnames, ARRAY_SIZE(fwnames));
+ if (!fwreq)
+ return -ENOMEM;
+
+ kfree(fwreq);
+ return 0;
}
static const struct brcmf_bus_ops brcmf_pcie_bus_ops = {
@@ -1581,24 +1582,6 @@ static void brcmf_pcie_release_resource(struct brcmf_pciedev_info *devinfo)
}
-static int brcmf_pcie_attach_bus(struct brcmf_pciedev_info *devinfo)
-{
- int ret;
-
- /* Attach to the common driver interface */
- ret = brcmf_attach(&devinfo->pdev->dev, devinfo->settings);
- if (ret) {
- brcmf_err("brcmf_attach failed\n");
- } else {
- ret = brcmf_bus_started(&devinfo->pdev->dev);
- if (ret)
- brcmf_err("dongle is not responding\n");
- }
-
- return ret;
-}
-
-
static u32 brcmf_pcie_buscore_prep_addr(const struct pci_dev *pdev, u32 addr)
{
u32 ret_addr;
@@ -1669,15 +1652,19 @@ static const struct brcmf_buscore_ops brcmf_pcie_buscore_ops = {
.write32 = brcmf_pcie_buscore_write32,
};
+#define BRCMF_PCIE_FW_CODE 0
+#define BRCMF_PCIE_FW_NVRAM 1
+
static void brcmf_pcie_setup(struct device *dev, int ret,
- const struct firmware *fw,
- void *nvram, u32 nvram_len)
+ struct brcmf_fw_request *fwreq)
{
+ const struct firmware *fw;
+ void *nvram;
struct brcmf_bus *bus;
struct brcmf_pciedev *pcie_bus_dev;
struct brcmf_pciedev_info *devinfo;
struct brcmf_commonring **flowrings;
- u32 i;
+ u32 i, nvram_len;
/* check firmware loading result */
if (ret)
@@ -1688,6 +1675,11 @@ static void brcmf_pcie_setup(struct device *dev, int ret,
devinfo = pcie_bus_dev->devinfo;
brcmf_pcie_attach(devinfo);
+ fw = fwreq->items[BRCMF_PCIE_FW_CODE].binary;
+ nvram = fwreq->items[BRCMF_PCIE_FW_NVRAM].nv_data.data;
+ nvram_len = fwreq->items[BRCMF_PCIE_FW_NVRAM].nv_data.len;
+ kfree(fwreq);
+
/* Some of the firmwares have the size of the memory of the device
* defined inside the firmware. This is because part of the memory in
* the device is shared and the devision is determined by FW. Parse
@@ -1735,7 +1727,7 @@ static void brcmf_pcie_setup(struct device *dev, int ret,
init_waitqueue_head(&devinfo->mbdata_resp_wait);
brcmf_pcie_intr_enable(devinfo);
- if (brcmf_pcie_attach_bus(devinfo) == 0)
+ if (brcmf_attach(&devinfo->pdev->dev, devinfo->settings) == 0)
return;
brcmf_pcie_bus_console_read(devinfo);
@@ -1744,20 +1736,41 @@ fail:
device_release_driver(dev);
}
+static struct brcmf_fw_request *
+brcmf_pcie_prepare_fw_request(struct brcmf_pciedev_info *devinfo)
+{
+ struct brcmf_fw_request *fwreq;
+ struct brcmf_fw_name fwnames[] = {
+ { ".bin", devinfo->fw_name },
+ { ".txt", devinfo->nvram_name },
+ };
+
+ fwreq = brcmf_fw_alloc_request(devinfo->ci->chip, devinfo->ci->chiprev,
+ brcmf_pcie_fwnames,
+ ARRAY_SIZE(brcmf_pcie_fwnames),
+ fwnames, ARRAY_SIZE(fwnames));
+ if (!fwreq)
+ return NULL;
+
+ fwreq->items[BRCMF_PCIE_FW_CODE].type = BRCMF_FW_TYPE_BINARY;
+ fwreq->items[BRCMF_PCIE_FW_NVRAM].type = BRCMF_FW_TYPE_NVRAM;
+ fwreq->items[BRCMF_PCIE_FW_NVRAM].flags = BRCMF_FW_REQF_OPTIONAL;
+ fwreq->domain_nr = pci_domain_nr(devinfo->pdev->bus);
+ fwreq->bus_nr = devinfo->pdev->bus->number;
+
+ return fwreq;
+}
+
static int
brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
int ret;
+ struct brcmf_fw_request *fwreq;
struct brcmf_pciedev_info *devinfo;
struct brcmf_pciedev *pcie_bus_dev;
struct brcmf_bus *bus;
- u16 domain_nr;
- u16 bus_nr;
- domain_nr = pci_domain_nr(pdev->bus) + 1;
- bus_nr = pdev->bus->number;
- brcmf_dbg(PCIE, "Enter %x:%x (%d/%d)\n", pdev->vendor, pdev->device,
- domain_nr, bus_nr);
+ brcmf_dbg(PCIE, "Enter %x:%x\n", pdev->vendor, pdev->device);
ret = -ENOMEM;
devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
@@ -1811,19 +1824,19 @@ brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
bus->wowl_supported = pci_pme_capable(pdev, PCI_D3hot);
dev_set_drvdata(&pdev->dev, bus);
- ret = brcmf_fw_map_chip_to_name(devinfo->ci->chip, devinfo->ci->chiprev,
- brcmf_pcie_fwnames,
- ARRAY_SIZE(brcmf_pcie_fwnames),
- devinfo->fw_name, devinfo->nvram_name);
- if (ret)
+ fwreq = brcmf_pcie_prepare_fw_request(devinfo);
+ if (!fwreq) {
+ ret = -ENOMEM;
goto fail_bus;
+ }
+
+ ret = brcmf_fw_get_firmwares(bus->dev, fwreq, brcmf_pcie_setup);
+ if (ret < 0) {
+ kfree(fwreq);
+ goto fail_bus;
+ }
+ return 0;
- ret = brcmf_fw_get_firmwares_pcie(bus->dev, BRCMF_FW_REQUEST_NVRAM |
- BRCMF_FW_REQ_NV_OPTIONAL,
- devinfo->fw_name, devinfo->nvram_name,
- brcmf_pcie_setup, domain_nr, bus_nr);
- if (ret == 0)
- return 0;
fail_bus:
kfree(bus->msgbuf);
kfree(bus);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c
index d26ff219ef66..c5ff551ec659 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c
@@ -54,7 +54,8 @@ int brcmf_proto_attach(struct brcmf_pub *drvr)
if (!proto->tx_queue_data || (proto->hdrpull == NULL) ||
(proto->query_dcmd == NULL) || (proto->set_dcmd == NULL) ||
(proto->configure_addr_mode == NULL) ||
- (proto->delete_peer == NULL) || (proto->add_tdls_peer == NULL)) {
+ (proto->delete_peer == NULL) || (proto->add_tdls_peer == NULL) ||
+ (proto->debugfs_create == NULL)) {
brcmf_err("Not all proto handlers have been installed\n");
goto fail;
}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h
index 8a8e08f09ea0..d3c3b9a815ad 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h
@@ -48,6 +48,7 @@ struct brcmf_proto {
void (*del_if)(struct brcmf_if *ifp);
void (*reset_if)(struct brcmf_if *ifp);
int (*init_done)(struct brcmf_pub *drvr);
+ void (*debugfs_create)(struct brcmf_pub *drvr);
void *pd;
};
@@ -156,4 +157,10 @@ brcmf_proto_init_done(struct brcmf_pub *drvr)
return drvr->proto->init_done(drvr);
}
+static inline void
+brcmf_proto_debugfs_create(struct brcmf_pub *drvr)
+{
+ drvr->proto->debugfs_create(drvr);
+}
+
#endif /* BRCMFMAC_PROTO_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
index 08686147b59d..1037df7297bb 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
@@ -600,47 +600,44 @@ static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = {
{4, 0x1}
};
-BRCMF_FW_NVRAM_DEF(43143, "brcmfmac43143-sdio.bin", "brcmfmac43143-sdio.txt");
-BRCMF_FW_NVRAM_DEF(43241B0, "brcmfmac43241b0-sdio.bin",
- "brcmfmac43241b0-sdio.txt");
-BRCMF_FW_NVRAM_DEF(43241B4, "brcmfmac43241b4-sdio.bin",
- "brcmfmac43241b4-sdio.txt");
-BRCMF_FW_NVRAM_DEF(43241B5, "brcmfmac43241b5-sdio.bin",
- "brcmfmac43241b5-sdio.txt");
-BRCMF_FW_NVRAM_DEF(4329, "brcmfmac4329-sdio.bin", "brcmfmac4329-sdio.txt");
-BRCMF_FW_NVRAM_DEF(4330, "brcmfmac4330-sdio.bin", "brcmfmac4330-sdio.txt");
-BRCMF_FW_NVRAM_DEF(4334, "brcmfmac4334-sdio.bin", "brcmfmac4334-sdio.txt");
-BRCMF_FW_NVRAM_DEF(43340, "brcmfmac43340-sdio.bin", "brcmfmac43340-sdio.txt");
-BRCMF_FW_NVRAM_DEF(4335, "brcmfmac4335-sdio.bin", "brcmfmac4335-sdio.txt");
-BRCMF_FW_NVRAM_DEF(43362, "brcmfmac43362-sdio.bin", "brcmfmac43362-sdio.txt");
-BRCMF_FW_NVRAM_DEF(4339, "brcmfmac4339-sdio.bin", "brcmfmac4339-sdio.txt");
-BRCMF_FW_NVRAM_DEF(43430A0, "brcmfmac43430a0-sdio.bin", "brcmfmac43430a0-sdio.txt");
+BRCMF_FW_DEF(43143, "brcmfmac43143-sdio");
+BRCMF_FW_DEF(43241B0, "brcmfmac43241b0-sdio");
+BRCMF_FW_DEF(43241B4, "brcmfmac43241b4-sdio");
+BRCMF_FW_DEF(43241B5, "brcmfmac43241b5-sdio");
+BRCMF_FW_DEF(4329, "brcmfmac4329-sdio");
+BRCMF_FW_DEF(4330, "brcmfmac4330-sdio");
+BRCMF_FW_DEF(4334, "brcmfmac4334-sdio");
+BRCMF_FW_DEF(43340, "brcmfmac43340-sdio");
+BRCMF_FW_DEF(4335, "brcmfmac4335-sdio");
+BRCMF_FW_DEF(43362, "brcmfmac43362-sdio");
+BRCMF_FW_DEF(4339, "brcmfmac4339-sdio");
+BRCMF_FW_DEF(43430A0, "brcmfmac43430a0-sdio");
/* Note the names are not postfixed with a1 for backward compatibility */
-BRCMF_FW_NVRAM_DEF(43430A1, "brcmfmac43430-sdio.bin", "brcmfmac43430-sdio.txt");
-BRCMF_FW_NVRAM_DEF(43455, "brcmfmac43455-sdio.bin", "brcmfmac43455-sdio.txt");
-BRCMF_FW_NVRAM_DEF(4354, "brcmfmac4354-sdio.bin", "brcmfmac4354-sdio.txt");
-BRCMF_FW_NVRAM_DEF(4356, "brcmfmac4356-sdio.bin", "brcmfmac4356-sdio.txt");
-BRCMF_FW_NVRAM_DEF(4373, "brcmfmac4373-sdio.bin", "brcmfmac4373-sdio.txt");
+BRCMF_FW_DEF(43430A1, "brcmfmac43430-sdio");
+BRCMF_FW_DEF(43455, "brcmfmac43455-sdio");
+BRCMF_FW_DEF(4354, "brcmfmac4354-sdio");
+BRCMF_FW_DEF(4356, "brcmfmac4356-sdio");
+BRCMF_FW_DEF(4373, "brcmfmac4373-sdio");
static struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = {
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, 43143),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43241_CHIP_ID, 0x0000001F, 43241B0),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43241_CHIP_ID, 0x00000020, 43241B4),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43241_CHIP_ID, 0xFFFFFFC0, 43241B5),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4329_CHIP_ID, 0xFFFFFFFF, 4329),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4330_CHIP_ID, 0xFFFFFFFF, 4330),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4334_CHIP_ID, 0xFFFFFFFF, 4334),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43340_CHIP_ID, 0xFFFFFFFF, 43340),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43341_CHIP_ID, 0xFFFFFFFF, 43340),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, 4335),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, 43362),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, 4339),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43430_CHIP_ID, 0x00000001, 43430A0),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43430_CHIP_ID, 0xFFFFFFFE, 43430A1),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4345_CHIP_ID, 0xFFFFFFC0, 43455),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, 4354),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4356_CHIP_ID, 0xFFFFFFFF, 4356),
- BRCMF_FW_NVRAM_ENTRY(CY_CC_4373_CHIP_ID, 0xFFFFFFFF, 4373)
+ BRCMF_FW_ENTRY(BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, 43143),
+ BRCMF_FW_ENTRY(BRCM_CC_43241_CHIP_ID, 0x0000001F, 43241B0),
+ BRCMF_FW_ENTRY(BRCM_CC_43241_CHIP_ID, 0x00000020, 43241B4),
+ BRCMF_FW_ENTRY(BRCM_CC_43241_CHIP_ID, 0xFFFFFFC0, 43241B5),
+ BRCMF_FW_ENTRY(BRCM_CC_4329_CHIP_ID, 0xFFFFFFFF, 4329),
+ BRCMF_FW_ENTRY(BRCM_CC_4330_CHIP_ID, 0xFFFFFFFF, 4330),
+ BRCMF_FW_ENTRY(BRCM_CC_4334_CHIP_ID, 0xFFFFFFFF, 4334),
+ BRCMF_FW_ENTRY(BRCM_CC_43340_CHIP_ID, 0xFFFFFFFF, 43340),
+ BRCMF_FW_ENTRY(BRCM_CC_43341_CHIP_ID, 0xFFFFFFFF, 43340),
+ BRCMF_FW_ENTRY(BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, 4335),
+ BRCMF_FW_ENTRY(BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, 43362),
+ BRCMF_FW_ENTRY(BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, 4339),
+ BRCMF_FW_ENTRY(BRCM_CC_43430_CHIP_ID, 0x00000001, 43430A0),
+ BRCMF_FW_ENTRY(BRCM_CC_43430_CHIP_ID, 0xFFFFFFFE, 43430A1),
+ BRCMF_FW_ENTRY(BRCM_CC_4345_CHIP_ID, 0xFFFFFFC0, 43455),
+ BRCMF_FW_ENTRY(BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, 4354),
+ BRCMF_FW_ENTRY(BRCM_CC_4356_CHIP_ID, 0xFFFFFFFF, 4356),
+ BRCMF_FW_ENTRY(CY_CC_4373_CHIP_ID, 0xFFFFFFFF, 4373)
};
static void pkt_align(struct sk_buff *p, int len, int align)
@@ -1706,8 +1703,7 @@ brcmf_sdio_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff)
u8 *buf = NULL, *rbuf;
int sdret;
- brcmf_dbg(TRACE, "Enter\n");
-
+ brcmf_dbg(SDIO, "Enter\n");
if (bus->rxblen)
buf = vzalloc(bus->rxblen);
if (!buf)
@@ -1810,7 +1806,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
struct brcmf_sdio_hdrinfo *rd = &bus->cur_read, rd_new;
u8 head_read = 0;
- brcmf_dbg(TRACE, "Enter\n");
+ brcmf_dbg(SDIO, "Enter\n");
/* Not finished unless we encounter no more frames indication */
bus->rxpending = true;
@@ -2345,7 +2341,7 @@ static int brcmf_sdio_tx_ctrlframe(struct brcmf_sdio *bus, u8 *frame, u16 len)
struct brcmf_sdio_hdrinfo hd_info = {0};
int ret;
- brcmf_dbg(TRACE, "Enter\n");
+ brcmf_dbg(SDIO, "Enter\n");
/* Back the pointer to make room for bus header */
frame -= bus->tx_hdrlen;
@@ -2521,7 +2517,7 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus)
uint framecnt; /* Temporary counter of tx/rx frames */
int err = 0;
- brcmf_dbg(TRACE, "Enter\n");
+ brcmf_dbg(SDIO, "Enter\n");
sdio_claim_host(bus->sdiodev->func1);
@@ -2606,7 +2602,7 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus)
/* Would be active due to wake-wlan in gSPI */
if (intstatus & I_CHIPACTIVE) {
- brcmf_dbg(INFO, "Dongle reports CHIPACTIVE\n");
+ brcmf_dbg(SDIO, "Dongle reports CHIPACTIVE\n");
intstatus &= ~I_CHIPACTIVE;
}
@@ -3411,6 +3407,20 @@ static int brcmf_sdio_bus_preinit(struct device *dev)
u32 value;
int err;
+ /* maxctl provided by common layer */
+ if (WARN_ON(!bus_if->maxctl))
+ return -EINVAL;
+
+ /* Allocate control receive buffer */
+ bus_if->maxctl += bus->roundup;
+ value = roundup((bus_if->maxctl + SDPCM_HDRLEN), ALIGNMENT);
+ value += bus->head_align;
+ bus->rxbuf = kmalloc(value, GFP_ATOMIC);
+ if (bus->rxbuf)
+ bus->rxblen = value;
+
+ brcmf_sdio_debugfs_create(bus);
+
/* the commands below use the terms tx and rx from
* a device perspective, ie. bus:txglom affects the
* bus transfers from device to host.
@@ -3990,22 +4000,24 @@ brcmf_sdio_watchdog(struct timer_list *t)
}
}
-static int brcmf_sdio_get_fwname(struct device *dev, u32 chip, u32 chiprev,
- u8 *fw_name)
+static
+int brcmf_sdio_get_fwname(struct device *dev, const char *ext, u8 *fw_name)
{
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
- int ret = 0;
-
- if (sdiodev->fw_name[0] != '\0')
- strlcpy(fw_name, sdiodev->fw_name, BRCMF_FW_NAME_LEN);
- else
- ret = brcmf_fw_map_chip_to_name(chip, chiprev,
- brcmf_sdio_fwnames,
- ARRAY_SIZE(brcmf_sdio_fwnames),
- fw_name, NULL);
+ struct brcmf_fw_request *fwreq;
+ struct brcmf_fw_name fwnames[] = {
+ { ext, fw_name },
+ };
+
+ fwreq = brcmf_fw_alloc_request(bus_if->chip, bus_if->chiprev,
+ brcmf_sdio_fwnames,
+ ARRAY_SIZE(brcmf_sdio_fwnames),
+ fwnames, ARRAY_SIZE(fwnames));
+ if (!fwreq)
+ return -ENOMEM;
- return ret;
+ kfree(fwreq);
+ return 0;
}
static const struct brcmf_bus_ops brcmf_sdio_bus_ops = {
@@ -4021,15 +4033,19 @@ static const struct brcmf_bus_ops brcmf_sdio_bus_ops = {
.get_fwname = brcmf_sdio_get_fwname,
};
+#define BRCMF_SDIO_FW_CODE 0
+#define BRCMF_SDIO_FW_NVRAM 1
+
static void brcmf_sdio_firmware_callback(struct device *dev, int err,
- const struct firmware *code,
- void *nvram, u32 nvram_len)
+ struct brcmf_fw_request *fwreq)
{
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
- struct brcmf_sdio *bus = sdiodev->bus;
- struct brcmf_sdio_dev *sdiod = bus->sdiodev;
+ struct brcmf_sdio_dev *sdiod = bus_if->bus_priv.sdio;
+ struct brcmf_sdio *bus = sdiod->bus;
struct brcmf_core *core = bus->sdio_core;
+ const struct firmware *code;
+ void *nvram;
+ u32 nvram_len;
u8 saveclk;
brcmf_dbg(TRACE, "Enter: dev=%s, err=%d\n", dev_name(dev), err);
@@ -4037,8 +4053,10 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
if (err)
goto fail;
- if (!bus_if->drvr)
- return;
+ code = fwreq->items[BRCMF_SDIO_FW_CODE].binary;
+ nvram = fwreq->items[BRCMF_SDIO_FW_NVRAM].nv_data.data;
+ nvram_len = fwreq->items[BRCMF_SDIO_FW_NVRAM].nv_data.len;
+ kfree(fwreq);
/* try to download image and nvram to the dongle */
bus->alp_only = true;
@@ -4051,7 +4069,7 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
bus->sdcnt.tickcnt = 0;
brcmf_sdio_wd_timer(bus, true);
- sdio_claim_host(sdiodev->func1);
+ sdio_claim_host(sdiod->func1);
/* Make sure backplane clock is on, needed to generate F2 interrupt */
brcmf_sdio_clkctl(bus, CLK_AVAIL, false);
@@ -4059,9 +4077,9 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
goto release;
/* Force clocks on backplane to be sure F2 interrupt propagates */
- saveclk = brcmf_sdiod_readb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, &err);
+ saveclk = brcmf_sdiod_readb(sdiod, SBSDIO_FUNC1_CHIPCLKCSR, &err);
if (!err) {
- brcmf_sdiod_writeb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+ brcmf_sdiod_writeb(sdiod, SBSDIO_FUNC1_CHIPCLKCSR,
(saveclk | SBSDIO_FORCE_HT), &err);
}
if (err) {
@@ -4073,7 +4091,7 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
brcmf_sdiod_writel(sdiod, core->base + SD_REG(tosbmailboxdata),
SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT, NULL);
- err = sdio_enable_func(sdiodev->func2);
+ err = sdio_enable_func(sdiod->func2);
brcmf_dbg(INFO, "enable F2: err=%d\n", err);
@@ -4085,10 +4103,10 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
bus->hostintmask, NULL);
- brcmf_sdiod_writeb(sdiodev, SBSDIO_WATERMARK, 8, &err);
+ brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK, 8, &err);
} else {
/* Disable F2 again */
- sdio_disable_func(sdiodev->func2);
+ sdio_disable_func(sdiod->func2);
goto release;
}
@@ -4096,7 +4114,7 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
brcmf_sdio_sr_init(bus);
} else {
/* Restore previous clock setting */
- brcmf_sdiod_writeb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+ brcmf_sdiod_writeb(sdiod, SBSDIO_FUNC1_CHIPCLKCSR,
saveclk, &err);
}
@@ -4104,7 +4122,7 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
/* Allow full data communication using DPC from now on. */
brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DATA);
- err = brcmf_sdiod_intr_register(sdiodev);
+ err = brcmf_sdiod_intr_register(sdiod);
if (err != 0)
brcmf_err("intr register failed:%d\n", err);
}
@@ -4113,28 +4131,60 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
if (err != 0)
brcmf_sdio_clkctl(bus, CLK_NONE, false);
- sdio_release_host(sdiodev->func1);
+ sdio_release_host(sdiod->func1);
+
+ /* Assign bus interface call back */
+ sdiod->bus_if->dev = sdiod->dev;
+ sdiod->bus_if->ops = &brcmf_sdio_bus_ops;
+ sdiod->bus_if->chip = bus->ci->chip;
+ sdiod->bus_if->chiprev = bus->ci->chiprev;
- err = brcmf_bus_started(dev);
+ /* Attach to the common layer, reserve hdr space */
+ err = brcmf_attach(sdiod->dev, sdiod->settings);
if (err != 0) {
- brcmf_err("dongle is not responding\n");
+ brcmf_err("brcmf_attach failed\n");
goto fail;
}
+
+ /* ready */
return;
release:
- sdio_release_host(sdiodev->func1);
+ sdio_release_host(sdiod->func1);
fail:
brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), err);
- device_release_driver(&sdiodev->func2->dev);
+ device_release_driver(&sdiod->func2->dev);
device_release_driver(dev);
}
+static struct brcmf_fw_request *
+brcmf_sdio_prepare_fw_request(struct brcmf_sdio *bus)
+{
+ struct brcmf_fw_request *fwreq;
+ struct brcmf_fw_name fwnames[] = {
+ { ".bin", bus->sdiodev->fw_name },
+ { ".txt", bus->sdiodev->nvram_name },
+ };
+
+ fwreq = brcmf_fw_alloc_request(bus->ci->chip, bus->ci->chiprev,
+ brcmf_sdio_fwnames,
+ ARRAY_SIZE(brcmf_sdio_fwnames),
+ fwnames, ARRAY_SIZE(fwnames));
+ if (!fwreq)
+ return NULL;
+
+ fwreq->items[BRCMF_SDIO_FW_CODE].type = BRCMF_FW_TYPE_BINARY;
+ fwreq->items[BRCMF_SDIO_FW_NVRAM].type = BRCMF_FW_TYPE_NVRAM;
+
+ return fwreq;
+}
+
struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
{
int ret;
struct brcmf_sdio *bus;
struct workqueue_struct *wq;
+ struct brcmf_fw_request *fwreq;
brcmf_dbg(TRACE, "Enter\n");
@@ -4188,39 +4238,13 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
bus->dpc_triggered = false;
bus->dpc_running = false;
- /* Assign bus interface call back */
- bus->sdiodev->bus_if->dev = bus->sdiodev->dev;
- bus->sdiodev->bus_if->ops = &brcmf_sdio_bus_ops;
- bus->sdiodev->bus_if->chip = bus->ci->chip;
- bus->sdiodev->bus_if->chiprev = bus->ci->chiprev;
-
/* default sdio bus header length for tx packet */
bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN;
- /* Attach to the common layer, reserve hdr space */
- ret = brcmf_attach(bus->sdiodev->dev, bus->sdiodev->settings);
- if (ret != 0) {
- brcmf_err("brcmf_attach failed\n");
- goto fail;
- }
-
/* Query the F2 block size, set roundup accordingly */
bus->blocksize = bus->sdiodev->func2->cur_blksize;
bus->roundup = min(max_roundup, bus->blocksize);
- /* Allocate buffers */
- if (bus->sdiodev->bus_if->maxctl) {
- bus->sdiodev->bus_if->maxctl += bus->roundup;
- bus->rxblen =
- roundup((bus->sdiodev->bus_if->maxctl + SDPCM_HDRLEN),
- ALIGNMENT) + bus->head_align;
- bus->rxbuf = kmalloc(bus->rxblen, GFP_ATOMIC);
- if (!(bus->rxbuf)) {
- brcmf_err("rxbuf allocation failed\n");
- goto fail;
- }
- }
-
sdio_claim_host(bus->sdiodev->func1);
/* Disable F2 to clear any intermediate frame state on the dongle */
@@ -4241,21 +4265,19 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
/* SR state */
bus->sr_enabled = false;
- brcmf_sdio_debugfs_create(bus);
brcmf_dbg(INFO, "completed!!\n");
- ret = brcmf_fw_map_chip_to_name(bus->ci->chip, bus->ci->chiprev,
- brcmf_sdio_fwnames,
- ARRAY_SIZE(brcmf_sdio_fwnames),
- sdiodev->fw_name, sdiodev->nvram_name);
- if (ret)
+ fwreq = brcmf_sdio_prepare_fw_request(bus);
+ if (!fwreq) {
+ ret = -ENOMEM;
goto fail;
+ }
- ret = brcmf_fw_get_firmwares(sdiodev->dev, BRCMF_FW_REQUEST_NVRAM,
- sdiodev->fw_name, sdiodev->nvram_name,
+ ret = brcmf_fw_get_firmwares(sdiodev->dev, fwreq,
brcmf_sdio_firmware_callback);
if (ret != 0) {
brcmf_err("async firmware request failed: %d\n", ret);
+ kfree(fwreq);
goto fail;
}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
index b27170c12482..a0873adcc01c 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
@@ -46,11 +46,11 @@
#define BRCMF_USB_CBCTL_READ 1
#define BRCMF_USB_MAX_PKT_SIZE 1600
-BRCMF_FW_DEF(43143, "brcmfmac43143.bin");
-BRCMF_FW_DEF(43236B, "brcmfmac43236b.bin");
-BRCMF_FW_DEF(43242A, "brcmfmac43242a.bin");
-BRCMF_FW_DEF(43569, "brcmfmac43569.bin");
-BRCMF_FW_DEF(4373, "brcmfmac4373.bin");
+BRCMF_FW_DEF(43143, "brcmfmac43143");
+BRCMF_FW_DEF(43236B, "brcmfmac43236b");
+BRCMF_FW_DEF(43242A, "brcmfmac43242a");
+BRCMF_FW_DEF(43569, "brcmfmac43569");
+BRCMF_FW_DEF(4373, "brcmfmac4373");
static struct brcmf_firmware_mapping brcmf_usb_fwnames[] = {
BRCMF_FW_ENTRY(BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, 43143),
@@ -1128,69 +1128,53 @@ static void brcmf_usb_wowl_config(struct device *dev, bool enabled)
device_set_wakeup_enable(devinfo->dev, false);
}
-static int brcmf_usb_get_fwname(struct device *dev, u32 chip, u32 chiprev,
- u8 *fw_name)
+static
+int brcmf_usb_get_fwname(struct device *dev, const char *ext, u8 *fw_name)
{
- struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
- int ret = 0;
-
- if (devinfo->fw_name[0] != '\0')
- strlcpy(fw_name, devinfo->fw_name, BRCMF_FW_NAME_LEN);
- else
- ret = brcmf_fw_map_chip_to_name(chip, chiprev,
- brcmf_usb_fwnames,
- ARRAY_SIZE(brcmf_usb_fwnames),
- fw_name, NULL);
+ struct brcmf_bus *bus = dev_get_drvdata(dev);
+ struct brcmf_fw_request *fwreq;
+ struct brcmf_fw_name fwnames[] = {
+ { ext, fw_name },
+ };
+
+ fwreq = brcmf_fw_alloc_request(bus->chip, bus->chiprev,
+ brcmf_usb_fwnames,
+ ARRAY_SIZE(brcmf_usb_fwnames),
+ fwnames, ARRAY_SIZE(fwnames));
+ if (!fwreq)
+ return -ENOMEM;
- return ret;
+ kfree(fwreq);
+ return 0;
}
static const struct brcmf_bus_ops brcmf_usb_bus_ops = {
- .txdata = brcmf_usb_tx,
+ .preinit = brcmf_usb_up,
.stop = brcmf_usb_down,
+ .txdata = brcmf_usb_tx,
.txctl = brcmf_usb_tx_ctlpkt,
.rxctl = brcmf_usb_rx_ctlpkt,
.wowl_config = brcmf_usb_wowl_config,
.get_fwname = brcmf_usb_get_fwname,
};
-static int brcmf_usb_bus_setup(struct brcmf_usbdev_info *devinfo)
-{
- int ret;
-
- /* Attach to the common driver interface */
- ret = brcmf_attach(devinfo->dev, devinfo->settings);
- if (ret) {
- brcmf_err("brcmf_attach failed\n");
- return ret;
- }
-
- ret = brcmf_usb_up(devinfo->dev);
- if (ret)
- goto fail;
-
- ret = brcmf_bus_started(devinfo->dev);
- if (ret)
- goto fail;
-
- return 0;
-fail:
- brcmf_detach(devinfo->dev);
- return ret;
-}
+#define BRCMF_USB_FW_CODE 0
static void brcmf_usb_probe_phase2(struct device *dev, int ret,
- const struct firmware *fw,
- void *nvram, u32 nvlen)
+ struct brcmf_fw_request *fwreq)
{
struct brcmf_bus *bus = dev_get_drvdata(dev);
struct brcmf_usbdev_info *devinfo = bus->bus_priv.usb->devinfo;
+ const struct firmware *fw;
if (ret)
goto error;
brcmf_dbg(USB, "Start fw downloading\n");
+ fw = fwreq->items[BRCMF_USB_FW_CODE].binary;
+ kfree(fwreq);
+
ret = check_file(fw->data);
if (ret < 0) {
brcmf_err("invalid firmware\n");
@@ -1206,7 +1190,8 @@ static void brcmf_usb_probe_phase2(struct device *dev, int ret,
if (ret)
goto error;
- ret = brcmf_usb_bus_setup(devinfo);
+ /* Attach to the common driver interface */
+ ret = brcmf_attach(devinfo->dev, devinfo->settings);
if (ret)
goto error;
@@ -1218,11 +1203,33 @@ error:
device_release_driver(dev);
}
+static struct brcmf_fw_request *
+brcmf_usb_prepare_fw_request(struct brcmf_usbdev_info *devinfo)
+{
+ struct brcmf_fw_request *fwreq;
+ struct brcmf_fw_name fwnames[] = {
+ { ".bin", devinfo->fw_name },
+ };
+
+ fwreq = brcmf_fw_alloc_request(devinfo->bus_pub.devid,
+ devinfo->bus_pub.chiprev,
+ brcmf_usb_fwnames,
+ ARRAY_SIZE(brcmf_usb_fwnames),
+ fwnames, ARRAY_SIZE(fwnames));
+ if (!fwreq)
+ return NULL;
+
+ fwreq->items[BRCMF_USB_FW_CODE].type = BRCMF_FW_TYPE_BINARY;
+
+ return fwreq;
+}
+
static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo)
{
struct brcmf_bus *bus = NULL;
struct brcmf_usbdev *bus_pub = NULL;
struct device *dev = devinfo->dev;
+ struct brcmf_fw_request *fwreq;
int ret;
brcmf_dbg(USB, "Enter\n");
@@ -1256,7 +1263,7 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo)
}
if (!brcmf_usb_dlneeded(devinfo)) {
- ret = brcmf_usb_bus_setup(devinfo);
+ ret = brcmf_attach(devinfo->dev, devinfo->settings);
if (ret)
goto fail;
/* we are done */
@@ -1266,18 +1273,17 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo)
bus->chip = bus_pub->devid;
bus->chiprev = bus_pub->chiprev;
- ret = brcmf_fw_map_chip_to_name(bus_pub->devid, bus_pub->chiprev,
- brcmf_usb_fwnames,
- ARRAY_SIZE(brcmf_usb_fwnames),
- devinfo->fw_name, NULL);
- if (ret)
+ fwreq = brcmf_usb_prepare_fw_request(devinfo);
+ if (!fwreq) {
+ ret = -ENOMEM;
goto fail;
+ }
/* request firmware here */
- ret = brcmf_fw_get_firmwares(dev, 0, devinfo->fw_name, NULL,
- brcmf_usb_probe_phase2);
+ ret = brcmf_fw_get_firmwares(dev, fwreq, brcmf_usb_probe_phase2);
if (ret) {
brcmf_err("firmware request failed: %d\n", ret);
+ kfree(fwreq);
goto fail;
}
@@ -1459,7 +1465,7 @@ static int brcmf_usb_resume(struct usb_interface *intf)
brcmf_dbg(USB, "Enter\n");
if (!devinfo->wowl_enabled)
- return brcmf_usb_bus_setup(devinfo);
+ return brcmf_attach(devinfo->dev, devinfo->settings);
devinfo->bus_pub.state = BRCMFMAC_USB_STATE_UP;
brcmf_usb_rx_fill_all(devinfo);
@@ -1470,11 +1476,20 @@ static int brcmf_usb_reset_resume(struct usb_interface *intf)
{
struct usb_device *usb = interface_to_usbdev(intf);
struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev);
+ struct brcmf_fw_request *fwreq;
+ int ret;
brcmf_dbg(USB, "Enter\n");
- return brcmf_fw_get_firmwares(&usb->dev, 0, devinfo->fw_name, NULL,
- brcmf_usb_probe_phase2);
+ fwreq = brcmf_usb_prepare_fw_request(devinfo);
+ if (!fwreq)
+ return -ENOMEM;
+
+ ret = brcmf_fw_get_firmwares(&usb->dev, fwreq, brcmf_usb_probe_phase2);
+ if (ret < 0)
+ kfree(fwreq);
+
+ return ret;
}
#define BRCMF_USB_DEVICE(dev_id) \
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c
index 3a03287fa912..db783e94f929 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c
@@ -652,7 +652,6 @@ static void brcms_reg_apply_radar_flags(struct wiphy *wiphy)
*/
if (!(ch->flags & IEEE80211_CHAN_DISABLED))
ch->flags |= IEEE80211_CHAN_RADAR |
- IEEE80211_CHAN_NO_IR |
IEEE80211_CHAN_NO_IR;
}
}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.c
index 7a1fbb2e3a71..2fe1f6863278 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.c
@@ -214,7 +214,7 @@ brcms_debugfs_add_entry(struct brcms_pub *drvr, const char *fn,
entry->read = read_fn;
entry->drvr = drvr;
- dentry = debugfs_create_file(fn, S_IRUGO, dentry, entry,
+ dentry = debugfs_create_file(fn, 0444, dentry, entry,
&brcms_debugfs_def_ops);
return PTR_ERR_OR_ZERO(dentry);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c
index ddfdfe177e24..ecc89e718b9c 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c
@@ -108,7 +108,7 @@ MODULE_DEVICE_TABLE(bcma, brcms_coreid_table);
* flags are specified by the BRCM_DL_* macros in
* drivers/net/wireless/brcm80211/include/defs.h.
*/
-module_param_named(debug, brcm_msg_level, uint, S_IRUGO | S_IWUSR);
+module_param_named(debug, brcm_msg_level, uint, 0644);
#endif
static struct ieee80211_channel brcms_2ghz_chantable[] = {
@@ -1563,7 +1563,7 @@ void brcms_free_timer(struct brcms_timer *t)
}
/*
- * precondition: perimeter lock has been acquired
+ * precondition: no locking required
*/
int brcms_ucode_init_buf(struct brcms_info *wl, void **pbuf, u32 idx)
{
@@ -1578,7 +1578,7 @@ int brcms_ucode_init_buf(struct brcms_info *wl, void **pbuf, u32 idx)
if (le32_to_cpu(hdr->idx) == idx) {
pdata = wl->fw.fw_bin[i]->data +
le32_to_cpu(hdr->offset);
- *pbuf = kmemdup(pdata, len, GFP_ATOMIC);
+ *pbuf = kmemdup(pdata, len, GFP_KERNEL);
if (*pbuf == NULL)
goto fail;
diff --git a/drivers/net/wireless/cisco/Kconfig b/drivers/net/wireless/cisco/Kconfig
index b22567dff893..e210ee8aa63b 100644
--- a/drivers/net/wireless/cisco/Kconfig
+++ b/drivers/net/wireless/cisco/Kconfig
@@ -5,8 +5,8 @@ config WLAN_VENDOR_CISCO
If you have a wireless card belonging to this class, say Y.
Note that the answer to this question doesn't directly affect the
- kernel: saying N will just cause the configurator to skip all
- the questions about cards. If you say Y, you will be asked for
+ kernel: saying N will just cause the configurator to skip all the
+ questions about these cards. If you say Y, you will be asked for
your specific card in the following questions.
if WLAN_VENDOR_CISCO
@@ -33,7 +33,7 @@ config AIRO
config AIRO_CS
tristate "Cisco/Aironet 34X/35X/4500/4800 PCMCIA cards"
- depends on CFG80211 && PCMCIA && (BROKEN || !M32R)
+ depends on CFG80211 && PCMCIA
select WIRELESS_EXT
select WEXT_SPY
select WEXT_PRIV
diff --git a/drivers/net/wireless/cisco/airo.c b/drivers/net/wireless/cisco/airo.c
index 54201c02fdb8..ce0fbf83285f 100644
--- a/drivers/net/wireless/cisco/airo.c
+++ b/drivers/net/wireless/cisco/airo.c
@@ -4519,21 +4519,21 @@ static int setup_proc_entry( struct net_device *dev,
proc_set_user(apriv->proc_entry, proc_kuid, proc_kgid);
/* Setup the StatsDelta */
- entry = proc_create_data("StatsDelta", S_IRUGO & proc_perm,
+ entry = proc_create_data("StatsDelta", 0444 & proc_perm,
apriv->proc_entry, &proc_statsdelta_ops, dev);
if (!entry)
goto fail;
proc_set_user(entry, proc_kuid, proc_kgid);
/* Setup the Stats */
- entry = proc_create_data("Stats", S_IRUGO & proc_perm,
+ entry = proc_create_data("Stats", 0444 & proc_perm,
apriv->proc_entry, &proc_stats_ops, dev);
if (!entry)
goto fail;
proc_set_user(entry, proc_kuid, proc_kgid);
/* Setup the Status */
- entry = proc_create_data("Status", S_IRUGO & proc_perm,
+ entry = proc_create_data("Status", 0444 & proc_perm,
apriv->proc_entry, &proc_status_ops, dev);
if (!entry)
goto fail;
diff --git a/drivers/net/wireless/intel/Kconfig b/drivers/net/wireless/intel/Kconfig
index 5b14f2f64a8a..6fdc14b08b8e 100644
--- a/drivers/net/wireless/intel/Kconfig
+++ b/drivers/net/wireless/intel/Kconfig
@@ -5,8 +5,8 @@ config WLAN_VENDOR_INTEL
If you have a wireless card belonging to this class, say Y.
Note that the answer to this question doesn't directly affect the
- kernel: saying N will just cause the configurator to skip all
- the questions about cards. If you say Y, you will be asked for
+ kernel: saying N will just cause the configurator to skip all the
+ questions about these cards. If you say Y, you will be asked for
your specific card in the following questions.
if WLAN_VENDOR_INTEL
diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2100.c b/drivers/net/wireless/intel/ipw2x00/ipw2100.c
index 19c442cb93e4..236b52423506 100644
--- a/drivers/net/wireless/intel/ipw2x00/ipw2100.c
+++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.c
@@ -3538,7 +3538,7 @@ static ssize_t show_pci(struct device *d, struct device_attribute *attr,
return out - buf;
}
-static DEVICE_ATTR(pci, S_IRUGO, show_pci, NULL);
+static DEVICE_ATTR(pci, 0444, show_pci, NULL);
static ssize_t show_cfg(struct device *d, struct device_attribute *attr,
char *buf)
@@ -3547,7 +3547,7 @@ static ssize_t show_cfg(struct device *d, struct device_attribute *attr,
return sprintf(buf, "0x%08x\n", (int)p->config);
}
-static DEVICE_ATTR(cfg, S_IRUGO, show_cfg, NULL);
+static DEVICE_ATTR(cfg, 0444, show_cfg, NULL);
static ssize_t show_status(struct device *d, struct device_attribute *attr,
char *buf)
@@ -3556,7 +3556,7 @@ static ssize_t show_status(struct device *d, struct device_attribute *attr,
return sprintf(buf, "0x%08x\n", (int)p->status);
}
-static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
+static DEVICE_ATTR(status, 0444, show_status, NULL);
static ssize_t show_capability(struct device *d, struct device_attribute *attr,
char *buf)
@@ -3565,7 +3565,7 @@ static ssize_t show_capability(struct device *d, struct device_attribute *attr,
return sprintf(buf, "0x%08x\n", (int)p->capability);
}
-static DEVICE_ATTR(capability, S_IRUGO, show_capability, NULL);
+static DEVICE_ATTR(capability, 0444, show_capability, NULL);
#define IPW2100_REG(x) { IPW_ ##x, #x }
static const struct {
@@ -3822,7 +3822,7 @@ static ssize_t show_registers(struct device *d, struct device_attribute *attr,
return out - buf;
}
-static DEVICE_ATTR(registers, S_IRUGO, show_registers, NULL);
+static DEVICE_ATTR(registers, 0444, show_registers, NULL);
static ssize_t show_hardware(struct device *d, struct device_attribute *attr,
char *buf)
@@ -3863,7 +3863,7 @@ static ssize_t show_hardware(struct device *d, struct device_attribute *attr,
return out - buf;
}
-static DEVICE_ATTR(hardware, S_IRUGO, show_hardware, NULL);
+static DEVICE_ATTR(hardware, 0444, show_hardware, NULL);
static ssize_t show_memory(struct device *d, struct device_attribute *attr,
char *buf)
@@ -3957,7 +3957,7 @@ static ssize_t store_memory(struct device *d, struct device_attribute *attr,
return count;
}
-static DEVICE_ATTR(memory, S_IWUSR | S_IRUGO, show_memory, store_memory);
+static DEVICE_ATTR(memory, 0644, show_memory, store_memory);
static ssize_t show_ordinals(struct device *d, struct device_attribute *attr,
char *buf)
@@ -3993,7 +3993,7 @@ static ssize_t show_ordinals(struct device *d, struct device_attribute *attr,
return len;
}
-static DEVICE_ATTR(ordinals, S_IRUGO, show_ordinals, NULL);
+static DEVICE_ATTR(ordinals, 0444, show_ordinals, NULL);
static ssize_t show_stats(struct device *d, struct device_attribute *attr,
char *buf)
@@ -4014,7 +4014,7 @@ static ssize_t show_stats(struct device *d, struct device_attribute *attr,
return out - buf;
}
-static DEVICE_ATTR(stats, S_IRUGO, show_stats, NULL);
+static DEVICE_ATTR(stats, 0444, show_stats, NULL);
static int ipw2100_switch_mode(struct ipw2100_priv *priv, u32 mode)
{
@@ -4112,7 +4112,7 @@ static ssize_t show_internals(struct device *d, struct device_attribute *attr,
return len;
}
-static DEVICE_ATTR(internals, S_IRUGO, show_internals, NULL);
+static DEVICE_ATTR(internals, 0444, show_internals, NULL);
static ssize_t show_bssinfo(struct device *d, struct device_attribute *attr,
char *buf)
@@ -4157,7 +4157,7 @@ static ssize_t show_bssinfo(struct device *d, struct device_attribute *attr,
return out - buf;
}
-static DEVICE_ATTR(bssinfo, S_IRUGO, show_bssinfo, NULL);
+static DEVICE_ATTR(bssinfo, 0444, show_bssinfo, NULL);
#ifdef CONFIG_IPW2100_DEBUG
static ssize_t debug_level_show(struct device_driver *d, char *buf)
@@ -4216,8 +4216,7 @@ static ssize_t store_fatal_error(struct device *d,
return count;
}
-static DEVICE_ATTR(fatal_error, S_IWUSR | S_IRUGO, show_fatal_error,
- store_fatal_error);
+static DEVICE_ATTR(fatal_error, 0644, show_fatal_error, store_fatal_error);
static ssize_t show_scan_age(struct device *d, struct device_attribute *attr,
char *buf)
@@ -4250,7 +4249,7 @@ static ssize_t store_scan_age(struct device *d, struct device_attribute *attr,
return strnlen(buf, count);
}
-static DEVICE_ATTR(scan_age, S_IWUSR | S_IRUGO, show_scan_age, store_scan_age);
+static DEVICE_ATTR(scan_age, 0644, show_scan_age, store_scan_age);
static ssize_t show_rf_kill(struct device *d, struct device_attribute *attr,
char *buf)
@@ -4304,7 +4303,7 @@ static ssize_t store_rf_kill(struct device *d, struct device_attribute *attr,
return count;
}
-static DEVICE_ATTR(rf_kill, S_IWUSR | S_IRUGO, show_rf_kill, store_rf_kill);
+static DEVICE_ATTR(rf_kill, 0644, show_rf_kill, store_rf_kill);
static struct attribute *ipw2100_sysfs_entries[] = {
&dev_attr_hardware.attr,
diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2200.c b/drivers/net/wireless/intel/ipw2x00/ipw2200.c
index 8da87496cb58..87a5e414c2f7 100644
--- a/drivers/net/wireless/intel/ipw2x00/ipw2200.c
+++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.c
@@ -1303,7 +1303,7 @@ static ssize_t show_event_log(struct device *d,
return len;
}
-static DEVICE_ATTR(event_log, S_IRUGO, show_event_log, NULL);
+static DEVICE_ATTR(event_log, 0444, show_event_log, NULL);
static ssize_t show_error(struct device *d,
struct device_attribute *attr, char *buf)
@@ -1351,7 +1351,7 @@ static ssize_t clear_error(struct device *d,
return count;
}
-static DEVICE_ATTR(error, S_IRUGO | S_IWUSR, show_error, clear_error);
+static DEVICE_ATTR(error, 0644, show_error, clear_error);
static ssize_t show_cmd_log(struct device *d,
struct device_attribute *attr, char *buf)
@@ -1378,7 +1378,7 @@ static ssize_t show_cmd_log(struct device *d,
return len;
}
-static DEVICE_ATTR(cmd_log, S_IRUGO, show_cmd_log, NULL);
+static DEVICE_ATTR(cmd_log, 0444, show_cmd_log, NULL);
#ifdef CONFIG_IPW2200_PROMISCUOUS
static void ipw_prom_free(struct ipw_priv *priv);
@@ -1443,8 +1443,7 @@ static ssize_t show_rtap_iface(struct device *d,
}
}
-static DEVICE_ATTR(rtap_iface, S_IWUSR | S_IRUSR, show_rtap_iface,
- store_rtap_iface);
+static DEVICE_ATTR(rtap_iface, 0600, show_rtap_iface, store_rtap_iface);
static ssize_t store_rtap_filter(struct device *d,
struct device_attribute *attr,
@@ -1475,8 +1474,7 @@ static ssize_t show_rtap_filter(struct device *d,
priv->prom_priv ? priv->prom_priv->filter : 0);
}
-static DEVICE_ATTR(rtap_filter, S_IWUSR | S_IRUSR, show_rtap_filter,
- store_rtap_filter);
+static DEVICE_ATTR(rtap_filter, 0600, show_rtap_filter, store_rtap_filter);
#endif
static ssize_t show_scan_age(struct device *d, struct device_attribute *attr,
@@ -1520,7 +1518,7 @@ static ssize_t store_scan_age(struct device *d, struct device_attribute *attr,
return len;
}
-static DEVICE_ATTR(scan_age, S_IWUSR | S_IRUGO, show_scan_age, store_scan_age);
+static DEVICE_ATTR(scan_age, 0644, show_scan_age, store_scan_age);
static ssize_t show_led(struct device *d, struct device_attribute *attr,
char *buf)
@@ -1553,7 +1551,7 @@ static ssize_t store_led(struct device *d, struct device_attribute *attr,
return count;
}
-static DEVICE_ATTR(led, S_IWUSR | S_IRUGO, show_led, store_led);
+static DEVICE_ATTR(led, 0644, show_led, store_led);
static ssize_t show_status(struct device *d,
struct device_attribute *attr, char *buf)
@@ -1562,7 +1560,7 @@ static ssize_t show_status(struct device *d,
return sprintf(buf, "0x%08x\n", (int)p->status);
}
-static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
+static DEVICE_ATTR(status, 0444, show_status, NULL);
static ssize_t show_cfg(struct device *d, struct device_attribute *attr,
char *buf)
@@ -1571,7 +1569,7 @@ static ssize_t show_cfg(struct device *d, struct device_attribute *attr,
return sprintf(buf, "0x%08x\n", (int)p->config);
}
-static DEVICE_ATTR(cfg, S_IRUGO, show_cfg, NULL);
+static DEVICE_ATTR(cfg, 0444, show_cfg, NULL);
static ssize_t show_nic_type(struct device *d,
struct device_attribute *attr, char *buf)
@@ -1580,7 +1578,7 @@ static ssize_t show_nic_type(struct device *d,
return sprintf(buf, "TYPE: %d\n", priv->nic_type);
}
-static DEVICE_ATTR(nic_type, S_IRUGO, show_nic_type, NULL);
+static DEVICE_ATTR(nic_type, 0444, show_nic_type, NULL);
static ssize_t show_ucode_version(struct device *d,
struct device_attribute *attr, char *buf)
@@ -1594,7 +1592,7 @@ static ssize_t show_ucode_version(struct device *d,
return sprintf(buf, "0x%08x\n", tmp);
}
-static DEVICE_ATTR(ucode_version, S_IWUSR | S_IRUGO, show_ucode_version, NULL);
+static DEVICE_ATTR(ucode_version, 0644, show_ucode_version, NULL);
static ssize_t show_rtc(struct device *d, struct device_attribute *attr,
char *buf)
@@ -1608,7 +1606,7 @@ static ssize_t show_rtc(struct device *d, struct device_attribute *attr,
return sprintf(buf, "0x%08x\n", tmp);
}
-static DEVICE_ATTR(rtc, S_IWUSR | S_IRUGO, show_rtc, NULL);
+static DEVICE_ATTR(rtc, 0644, show_rtc, NULL);
/*
* Add a device attribute to view/control the delay between eeprom
@@ -1630,8 +1628,7 @@ static ssize_t store_eeprom_delay(struct device *d,
return strnlen(buf, count);
}
-static DEVICE_ATTR(eeprom_delay, S_IWUSR | S_IRUGO,
- show_eeprom_delay, store_eeprom_delay);
+static DEVICE_ATTR(eeprom_delay, 0644, show_eeprom_delay, store_eeprom_delay);
static ssize_t show_command_event_reg(struct device *d,
struct device_attribute *attr, char *buf)
@@ -1654,7 +1651,7 @@ static ssize_t store_command_event_reg(struct device *d,
return strnlen(buf, count);
}
-static DEVICE_ATTR(command_event_reg, S_IWUSR | S_IRUGO,
+static DEVICE_ATTR(command_event_reg, 0644,
show_command_event_reg, store_command_event_reg);
static ssize_t show_mem_gpio_reg(struct device *d,
@@ -1678,8 +1675,7 @@ static ssize_t store_mem_gpio_reg(struct device *d,
return strnlen(buf, count);
}
-static DEVICE_ATTR(mem_gpio_reg, S_IWUSR | S_IRUGO,
- show_mem_gpio_reg, store_mem_gpio_reg);
+static DEVICE_ATTR(mem_gpio_reg, 0644, show_mem_gpio_reg, store_mem_gpio_reg);
static ssize_t show_indirect_dword(struct device *d,
struct device_attribute *attr, char *buf)
@@ -1705,7 +1701,7 @@ static ssize_t store_indirect_dword(struct device *d,
return strnlen(buf, count);
}
-static DEVICE_ATTR(indirect_dword, S_IWUSR | S_IRUGO,
+static DEVICE_ATTR(indirect_dword, 0644,
show_indirect_dword, store_indirect_dword);
static ssize_t show_indirect_byte(struct device *d,
@@ -1732,7 +1728,7 @@ static ssize_t store_indirect_byte(struct device *d,
return strnlen(buf, count);
}
-static DEVICE_ATTR(indirect_byte, S_IWUSR | S_IRUGO,
+static DEVICE_ATTR(indirect_byte, 0644,
show_indirect_byte, store_indirect_byte);
static ssize_t show_direct_dword(struct device *d,
@@ -1759,8 +1755,7 @@ static ssize_t store_direct_dword(struct device *d,
return strnlen(buf, count);
}
-static DEVICE_ATTR(direct_dword, S_IWUSR | S_IRUGO,
- show_direct_dword, store_direct_dword);
+static DEVICE_ATTR(direct_dword, 0644, show_direct_dword, store_direct_dword);
static int rf_kill_active(struct ipw_priv *priv)
{
@@ -1831,7 +1826,7 @@ static ssize_t store_rf_kill(struct device *d, struct device_attribute *attr,
return count;
}
-static DEVICE_ATTR(rf_kill, S_IWUSR | S_IRUGO, show_rf_kill, store_rf_kill);
+static DEVICE_ATTR(rf_kill, 0644, show_rf_kill, store_rf_kill);
static ssize_t show_speed_scan(struct device *d, struct device_attribute *attr,
char *buf)
@@ -1884,8 +1879,7 @@ static ssize_t store_speed_scan(struct device *d, struct device_attribute *attr,
return count;
}
-static DEVICE_ATTR(speed_scan, S_IWUSR | S_IRUGO, show_speed_scan,
- store_speed_scan);
+static DEVICE_ATTR(speed_scan, 0644, show_speed_scan, store_speed_scan);
static ssize_t show_net_stats(struct device *d, struct device_attribute *attr,
char *buf)
@@ -1906,8 +1900,7 @@ static ssize_t store_net_stats(struct device *d, struct device_attribute *attr,
return count;
}
-static DEVICE_ATTR(net_stats, S_IWUSR | S_IRUGO,
- show_net_stats, store_net_stats);
+static DEVICE_ATTR(net_stats, 0644, show_net_stats, store_net_stats);
static ssize_t show_channels(struct device *d,
struct device_attribute *attr,
@@ -1953,7 +1946,7 @@ static ssize_t show_channels(struct device *d,
return len;
}
-static DEVICE_ATTR(channels, S_IRUSR, show_channels, NULL);
+static DEVICE_ATTR(channels, 0400, show_channels, NULL);
static void notify_wx_assoc_event(struct ipw_priv *priv)
{
diff --git a/drivers/net/wireless/intel/ipw2x00/libipw_module.c b/drivers/net/wireless/intel/ipw2x00/libipw_module.c
index c58c5b2dcce5..f00d45f54c76 100644
--- a/drivers/net/wireless/intel/ipw2x00/libipw_module.c
+++ b/drivers/net/wireless/intel/ipw2x00/libipw_module.c
@@ -276,7 +276,7 @@ static int __init libipw_init(void)
" proc directory\n");
return -EIO;
}
- e = proc_create("debug_level", S_IRUGO | S_IWUSR, libipw_proc,
+ e = proc_create("debug_level", 0644, libipw_proc,
&debug_level_proc_fops);
if (!e) {
remove_proc_entry(DRV_PROCNAME, init_net.proc_net);
diff --git a/drivers/net/wireless/intel/iwlegacy/3945-mac.c b/drivers/net/wireless/intel/iwlegacy/3945-mac.c
index 4b53ebf00c7f..62a9794f952b 100644
--- a/drivers/net/wireless/intel/iwlegacy/3945-mac.c
+++ b/drivers/net/wireless/intel/iwlegacy/3945-mac.c
@@ -3122,7 +3122,7 @@ il3945_store_debug_level(struct device *d, struct device_attribute *attr,
return strnlen(buf, count);
}
-static DEVICE_ATTR(debug_level, S_IWUSR | S_IRUGO, il3945_show_debug_level,
+static DEVICE_ATTR(debug_level, 0644, il3945_show_debug_level,
il3945_store_debug_level);
#endif /* CONFIG_IWLEGACY_DEBUG */
@@ -3139,7 +3139,7 @@ il3945_show_temperature(struct device *d, struct device_attribute *attr,
return sprintf(buf, "%d\n", il3945_hw_get_temperature(il));
}
-static DEVICE_ATTR(temperature, S_IRUGO, il3945_show_temperature, NULL);
+static DEVICE_ATTR(temperature, 0444, il3945_show_temperature, NULL);
static ssize_t
il3945_show_tx_power(struct device *d, struct device_attribute *attr, char *buf)
@@ -3165,8 +3165,7 @@ il3945_store_tx_power(struct device *d, struct device_attribute *attr,
return count;
}
-static DEVICE_ATTR(tx_power, S_IWUSR | S_IRUGO, il3945_show_tx_power,
- il3945_store_tx_power);
+static DEVICE_ATTR(tx_power, 0644, il3945_show_tx_power, il3945_store_tx_power);
static ssize_t
il3945_show_flags(struct device *d, struct device_attribute *attr, char *buf)
@@ -3199,8 +3198,7 @@ il3945_store_flags(struct device *d, struct device_attribute *attr,
return count;
}
-static DEVICE_ATTR(flags, S_IWUSR | S_IRUGO, il3945_show_flags,
- il3945_store_flags);
+static DEVICE_ATTR(flags, 0644, il3945_show_flags, il3945_store_flags);
static ssize_t
il3945_show_filter_flags(struct device *d, struct device_attribute *attr,
@@ -3235,7 +3233,7 @@ il3945_store_filter_flags(struct device *d, struct device_attribute *attr,
return count;
}
-static DEVICE_ATTR(filter_flags, S_IWUSR | S_IRUGO, il3945_show_filter_flags,
+static DEVICE_ATTR(filter_flags, 0644, il3945_show_filter_flags,
il3945_store_filter_flags);
static ssize_t
@@ -3306,7 +3304,7 @@ il3945_store_measurement(struct device *d, struct device_attribute *attr,
return count;
}
-static DEVICE_ATTR(measurement, S_IRUSR | S_IWUSR, il3945_show_measurement,
+static DEVICE_ATTR(measurement, 0600, il3945_show_measurement,
il3945_store_measurement);
static ssize_t
@@ -3330,7 +3328,7 @@ il3945_show_retry_rate(struct device *d, struct device_attribute *attr,
return sprintf(buf, "%d", il->retry_rate);
}
-static DEVICE_ATTR(retry_rate, S_IWUSR | S_IRUSR, il3945_show_retry_rate,
+static DEVICE_ATTR(retry_rate, 0600, il3945_show_retry_rate,
il3945_store_retry_rate);
static ssize_t
@@ -3340,7 +3338,7 @@ il3945_show_channels(struct device *d, struct device_attribute *attr, char *buf)
return 0;
}
-static DEVICE_ATTR(channels, S_IRUSR, il3945_show_channels, NULL);
+static DEVICE_ATTR(channels, 0400, il3945_show_channels, NULL);
static ssize_t
il3945_show_antenna(struct device *d, struct device_attribute *attr, char *buf)
@@ -3377,8 +3375,7 @@ il3945_store_antenna(struct device *d, struct device_attribute *attr,
return count;
}
-static DEVICE_ATTR(antenna, S_IWUSR | S_IRUGO, il3945_show_antenna,
- il3945_store_antenna);
+static DEVICE_ATTR(antenna, 0644, il3945_show_antenna, il3945_store_antenna);
static ssize_t
il3945_show_status(struct device *d, struct device_attribute *attr, char *buf)
@@ -3389,7 +3386,7 @@ il3945_show_status(struct device *d, struct device_attribute *attr, char *buf)
return sprintf(buf, "0x%08x\n", (int)il->status);
}
-static DEVICE_ATTR(status, S_IRUGO, il3945_show_status, NULL);
+static DEVICE_ATTR(status, 0444, il3945_show_status, NULL);
static ssize_t
il3945_dump_error_log(struct device *d, struct device_attribute *attr,
@@ -3404,7 +3401,7 @@ il3945_dump_error_log(struct device *d, struct device_attribute *attr,
return strnlen(buf, count);
}
-static DEVICE_ATTR(dump_errors, S_IWUSR, NULL, il3945_dump_error_log);
+static DEVICE_ATTR(dump_errors, 0200, NULL, il3945_dump_error_log);
/*****************************************************************************
*
@@ -3943,18 +3940,18 @@ il3945_exit(void)
MODULE_FIRMWARE(IL3945_MODULE_FIRMWARE(IL3945_UCODE_API_MAX));
-module_param_named(antenna, il3945_mod_params.antenna, int, S_IRUGO);
+module_param_named(antenna, il3945_mod_params.antenna, int, 0444);
MODULE_PARM_DESC(antenna, "select antenna (1=Main, 2=Aux, default 0 [both])");
-module_param_named(swcrypto, il3945_mod_params.sw_crypto, int, S_IRUGO);
+module_param_named(swcrypto, il3945_mod_params.sw_crypto, int, 0444);
MODULE_PARM_DESC(swcrypto, "using software crypto (default 1 [software])");
module_param_named(disable_hw_scan, il3945_mod_params.disable_hw_scan, int,
- S_IRUGO);
+ 0444);
MODULE_PARM_DESC(disable_hw_scan, "disable hardware scanning (default 1)");
#ifdef CONFIG_IWLEGACY_DEBUG
-module_param_named(debug, il_debug_level, uint, S_IRUGO | S_IWUSR);
+module_param_named(debug, il_debug_level, uint, 0644);
MODULE_PARM_DESC(debug, "debug output mask");
#endif
-module_param_named(fw_restart, il3945_mod_params.restart_fw, int, S_IRUGO);
+module_param_named(fw_restart, il3945_mod_params.restart_fw, int, 0444);
MODULE_PARM_DESC(fw_restart, "restart firmware in case of error");
module_exit(il3945_exit);
diff --git a/drivers/net/wireless/intel/iwlegacy/4965-mac.c b/drivers/net/wireless/intel/iwlegacy/4965-mac.c
index de63f2518f23..562e94870a9c 100644
--- a/drivers/net/wireless/intel/iwlegacy/4965-mac.c
+++ b/drivers/net/wireless/intel/iwlegacy/4965-mac.c
@@ -4591,7 +4591,7 @@ il4965_store_debug_level(struct device *d, struct device_attribute *attr,
return strnlen(buf, count);
}
-static DEVICE_ATTR(debug_level, S_IWUSR | S_IRUGO, il4965_show_debug_level,
+static DEVICE_ATTR(debug_level, 0644, il4965_show_debug_level,
il4965_store_debug_level);
#endif /* CONFIG_IWLEGACY_DEBUG */
@@ -4608,7 +4608,7 @@ il4965_show_temperature(struct device *d, struct device_attribute *attr,
return sprintf(buf, "%d\n", il->temperature);
}
-static DEVICE_ATTR(temperature, S_IRUGO, il4965_show_temperature, NULL);
+static DEVICE_ATTR(temperature, 0444, il4965_show_temperature, NULL);
static ssize_t
il4965_show_tx_power(struct device *d, struct device_attribute *attr, char *buf)
@@ -4642,7 +4642,7 @@ il4965_store_tx_power(struct device *d, struct device_attribute *attr,
return ret;
}
-static DEVICE_ATTR(tx_power, S_IWUSR | S_IRUGO, il4965_show_tx_power,
+static DEVICE_ATTR(tx_power, 0644, il4965_show_tx_power,
il4965_store_tx_power);
static struct attribute *il_sysfs_entries[] = {
@@ -6859,18 +6859,17 @@ module_exit(il4965_exit);
module_init(il4965_init);
#ifdef CONFIG_IWLEGACY_DEBUG
-module_param_named(debug, il_debug_level, uint, S_IRUGO | S_IWUSR);
+module_param_named(debug, il_debug_level, uint, 0644);
MODULE_PARM_DESC(debug, "debug output mask");
#endif
-module_param_named(swcrypto, il4965_mod_params.sw_crypto, int, S_IRUGO);
+module_param_named(swcrypto, il4965_mod_params.sw_crypto, int, 0444);
MODULE_PARM_DESC(swcrypto, "using crypto in software (default 0 [hardware])");
-module_param_named(queues_num, il4965_mod_params.num_of_queues, int, S_IRUGO);
+module_param_named(queues_num, il4965_mod_params.num_of_queues, int, 0444);
MODULE_PARM_DESC(queues_num, "number of hw queues.");
-module_param_named(11n_disable, il4965_mod_params.disable_11n, int, S_IRUGO);
+module_param_named(11n_disable, il4965_mod_params.disable_11n, int, 0444);
MODULE_PARM_DESC(11n_disable, "disable 11n functionality");
-module_param_named(amsdu_size_8K, il4965_mod_params.amsdu_size_8K, int,
- S_IRUGO);
+module_param_named(amsdu_size_8K, il4965_mod_params.amsdu_size_8K, int, 0444);
MODULE_PARM_DESC(amsdu_size_8K, "enable 8K amsdu size (default 0 [disabled])");
-module_param_named(fw_restart, il4965_mod_params.restart_fw, int, S_IRUGO);
+module_param_named(fw_restart, il4965_mod_params.restart_fw, int, 0444);
MODULE_PARM_DESC(fw_restart, "restart firmware in case of error");
diff --git a/drivers/net/wireless/intel/iwlegacy/4965-rs.c b/drivers/net/wireless/intel/iwlegacy/4965-rs.c
index 365a4187fc37..54ff83829afb 100644
--- a/drivers/net/wireless/intel/iwlegacy/4965-rs.c
+++ b/drivers/net/wireless/intel/iwlegacy/4965-rs.c
@@ -2768,16 +2768,16 @@ il4965_rs_add_debugfs(void *il, void *il_sta, struct dentry *dir)
{
struct il_lq_sta *lq_sta = il_sta;
lq_sta->rs_sta_dbgfs_scale_table_file =
- debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir,
+ debugfs_create_file("rate_scale_table", 0600, dir,
lq_sta, &rs_sta_dbgfs_scale_table_ops);
lq_sta->rs_sta_dbgfs_stats_table_file =
- debugfs_create_file("rate_stats_table", S_IRUSR, dir, lq_sta,
+ debugfs_create_file("rate_stats_table", 0400, dir, lq_sta,
&rs_sta_dbgfs_stats_table_ops);
lq_sta->rs_sta_dbgfs_rate_scale_data_file =
- debugfs_create_file("rate_scale_data", S_IRUSR, dir, lq_sta,
+ debugfs_create_file("rate_scale_data", 0400, dir, lq_sta,
&rs_sta_dbgfs_rate_scale_data_ops);
lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file =
- debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir,
+ debugfs_create_u8("tx_agg_tid_enable", 0600, dir,
&lq_sta->tx_agg_tid_en);
}
diff --git a/drivers/net/wireless/intel/iwlegacy/common.c b/drivers/net/wireless/intel/iwlegacy/common.c
index 558bb16bfd46..063e19ced7c8 100644
--- a/drivers/net/wireless/intel/iwlegacy/common.c
+++ b/drivers/net/wireless/intel/iwlegacy/common.c
@@ -435,7 +435,7 @@ EXPORT_SYMBOL(il_send_cmd_pdu_async);
/* default: IL_LED_BLINK(0) using blinking idx table */
static int led_mode;
-module_param(led_mode, int, S_IRUGO);
+module_param(led_mode, int, 0444);
MODULE_PARM_DESC(led_mode,
"0=system default, " "1=On(RF On)/Off(RF Off), 2=blinking");
@@ -3372,7 +3372,7 @@ MODULE_LICENSE("GPL");
* default: bt_coex_active = true (BT_COEX_ENABLE)
*/
static bool bt_coex_active = true;
-module_param(bt_coex_active, bool, S_IRUGO);
+module_param(bt_coex_active, bool, 0444);
MODULE_PARM_DESC(bt_coex_active, "enable wifi/bluetooth co-exist");
u32 il_debug_level;
diff --git a/drivers/net/wireless/intel/iwlegacy/debug.c b/drivers/net/wireless/intel/iwlegacy/debug.c
index 6fc6b7ff9849..d76073def677 100644
--- a/drivers/net/wireless/intel/iwlegacy/debug.c
+++ b/drivers/net/wireless/intel/iwlegacy/debug.c
@@ -135,16 +135,14 @@ EXPORT_SYMBOL(il_update_stats);
#define DEBUGFS_ADD_BOOL(name, parent, ptr) do { \
struct dentry *__tmp; \
- __tmp = debugfs_create_bool(#name, S_IWUSR | S_IRUSR, \
- parent, ptr); \
+ __tmp = debugfs_create_bool(#name, 0600, parent, ptr); \
if (IS_ERR(__tmp) || !__tmp) \
goto err; \
} while (0)
#define DEBUGFS_ADD_X32(name, parent, ptr) do { \
struct dentry *__tmp; \
- __tmp = debugfs_create_x32(#name, S_IWUSR | S_IRUSR, \
- parent, ptr); \
+ __tmp = debugfs_create_x32(#name, 0600, parent, ptr); \
if (IS_ERR(__tmp) || !__tmp) \
goto err; \
} while (0)
@@ -1365,35 +1363,35 @@ il_dbgfs_register(struct il_priv *il, const char *name)
if (!dir_debug)
goto err;
- DEBUGFS_ADD_FILE(nvm, dir_data, S_IRUSR);
- DEBUGFS_ADD_FILE(sram, dir_data, S_IWUSR | S_IRUSR);
- DEBUGFS_ADD_FILE(stations, dir_data, S_IRUSR);
- DEBUGFS_ADD_FILE(channels, dir_data, S_IRUSR);
- DEBUGFS_ADD_FILE(status, dir_data, S_IRUSR);
- DEBUGFS_ADD_FILE(interrupt, dir_data, S_IWUSR | S_IRUSR);
- DEBUGFS_ADD_FILE(qos, dir_data, S_IRUSR);
- DEBUGFS_ADD_FILE(disable_ht40, dir_data, S_IWUSR | S_IRUSR);
- DEBUGFS_ADD_FILE(rx_stats, dir_debug, S_IRUSR);
- DEBUGFS_ADD_FILE(tx_stats, dir_debug, S_IRUSR);
- DEBUGFS_ADD_FILE(rx_queue, dir_debug, S_IRUSR);
- DEBUGFS_ADD_FILE(tx_queue, dir_debug, S_IRUSR);
- DEBUGFS_ADD_FILE(power_save_status, dir_debug, S_IRUSR);
- DEBUGFS_ADD_FILE(clear_ucode_stats, dir_debug, S_IWUSR);
- DEBUGFS_ADD_FILE(clear_traffic_stats, dir_debug, S_IWUSR);
- DEBUGFS_ADD_FILE(fh_reg, dir_debug, S_IRUSR);
- DEBUGFS_ADD_FILE(missed_beacon, dir_debug, S_IWUSR);
- DEBUGFS_ADD_FILE(force_reset, dir_debug, S_IWUSR | S_IRUSR);
- DEBUGFS_ADD_FILE(ucode_rx_stats, dir_debug, S_IRUSR);
- DEBUGFS_ADD_FILE(ucode_tx_stats, dir_debug, S_IRUSR);
- DEBUGFS_ADD_FILE(ucode_general_stats, dir_debug, S_IRUSR);
+ DEBUGFS_ADD_FILE(nvm, dir_data, 0400);
+ DEBUGFS_ADD_FILE(sram, dir_data, 0600);
+ DEBUGFS_ADD_FILE(stations, dir_data, 0400);
+ DEBUGFS_ADD_FILE(channels, dir_data, 0400);
+ DEBUGFS_ADD_FILE(status, dir_data, 0400);
+ DEBUGFS_ADD_FILE(interrupt, dir_data, 0600);
+ DEBUGFS_ADD_FILE(qos, dir_data, 0400);
+ DEBUGFS_ADD_FILE(disable_ht40, dir_data, 0600);
+ DEBUGFS_ADD_FILE(rx_stats, dir_debug, 0400);
+ DEBUGFS_ADD_FILE(tx_stats, dir_debug, 0400);
+ DEBUGFS_ADD_FILE(rx_queue, dir_debug, 0400);
+ DEBUGFS_ADD_FILE(tx_queue, dir_debug, 0400);
+ DEBUGFS_ADD_FILE(power_save_status, dir_debug, 0400);
+ DEBUGFS_ADD_FILE(clear_ucode_stats, dir_debug, 0200);
+ DEBUGFS_ADD_FILE(clear_traffic_stats, dir_debug, 0200);
+ DEBUGFS_ADD_FILE(fh_reg, dir_debug, 0400);
+ DEBUGFS_ADD_FILE(missed_beacon, dir_debug, 0200);
+ DEBUGFS_ADD_FILE(force_reset, dir_debug, 0600);
+ DEBUGFS_ADD_FILE(ucode_rx_stats, dir_debug, 0400);
+ DEBUGFS_ADD_FILE(ucode_tx_stats, dir_debug, 0400);
+ DEBUGFS_ADD_FILE(ucode_general_stats, dir_debug, 0400);
if (il->cfg->sensitivity_calib_by_driver)
- DEBUGFS_ADD_FILE(sensitivity, dir_debug, S_IRUSR);
+ DEBUGFS_ADD_FILE(sensitivity, dir_debug, 0400);
if (il->cfg->chain_noise_calib_by_driver)
- DEBUGFS_ADD_FILE(chain_noise, dir_debug, S_IRUSR);
- DEBUGFS_ADD_FILE(rxon_flags, dir_debug, S_IWUSR);
- DEBUGFS_ADD_FILE(rxon_filter_flags, dir_debug, S_IWUSR);
- DEBUGFS_ADD_FILE(wd_timeout, dir_debug, S_IWUSR);
+ DEBUGFS_ADD_FILE(chain_noise, dir_debug, 0400);
+ DEBUGFS_ADD_FILE(rxon_flags, dir_debug, 0200);
+ DEBUGFS_ADD_FILE(rxon_filter_flags, dir_debug, 0200);
+ DEBUGFS_ADD_FILE(wd_timeout, dir_debug, 0200);
if (il->cfg->sensitivity_calib_by_driver)
DEBUGFS_ADD_BOOL(disable_sensitivity, dir_rf,
&il->disable_sens_cal);
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c
index 48f6f80eb24b..dffd9df782b0 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c
@@ -6,6 +6,7 @@
* GPL LICENSE SUMMARY
*
* Copyright(c) 2015-2017 Intel Deutschland GmbH
+ * Copyright (C) 2018 Intel Corporation
*
* 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
@@ -19,6 +20,7 @@
* BSD LICENSE
*
* Copyright(c) 2015-2017 Intel Deutschland GmbH
+ * Copyright (C) 2018 Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -55,7 +57,7 @@
#include "iwl-agn-hw.h"
/* Highest firmware API version supported */
-#define IWL_22000_UCODE_API_MAX 36
+#define IWL_22000_UCODE_API_MAX 38
/* Lowest firmware API version supported */
#define IWL_22000_UCODE_API_MIN 24
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/9000.c b/drivers/net/wireless/intel/iwlwifi/cfg/9000.c
index 90a1d14cf7d2..e1c869a1f8cc 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/9000.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/9000.c
@@ -6,6 +6,7 @@
* GPL LICENSE SUMMARY
*
* Copyright(c) 2015-2017 Intel Deutschland GmbH
+ * Copyright (C) 2018 Intel Corporation
*
* 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
@@ -19,6 +20,7 @@
* BSD LICENSE
*
* Copyright(c) 2015-2017 Intel Deutschland GmbH
+ * Copyright (C) 2018 Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -53,9 +55,10 @@
#include <linux/stringify.h>
#include "iwl-config.h"
#include "iwl-agn-hw.h"
+#include "fw/file.h"
/* Highest firmware API version supported */
-#define IWL9000_UCODE_API_MAX 36
+#define IWL9000_UCODE_API_MAX 38
/* Lowest firmware API version supported */
#define IWL9000_UCODE_API_MIN 30
@@ -265,6 +268,67 @@ const struct iwl_cfg iwl9560_2ac_cfg_soc = {
.integrated = true,
.soc_latency = 5000,
};
+
+const struct iwl_cfg iwl9460_2ac_cfg_shared_clk = {
+ .name = "Intel(R) Dual Band Wireless AC 9460",
+ .fw_name_pre = IWL9000A_FW_PRE,
+ .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE,
+ .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE,
+ IWL_DEVICE_9000,
+ .ht_params = &iwl9000_ht_params,
+ .nvm_ver = IWL9000_NVM_VERSION,
+ .nvm_calib_ver = IWL9000_TX_POWER_VERSION,
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
+ .integrated = true,
+ .soc_latency = 5000,
+ .extra_phy_cfg_flags = FW_PHY_CFG_SHARED_CLK
+};
+
+const struct iwl_cfg iwl9461_2ac_cfg_shared_clk = {
+ .name = "Intel(R) Dual Band Wireless AC 9461",
+ .fw_name_pre = IWL9000A_FW_PRE,
+ .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE,
+ .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE,
+ IWL_DEVICE_9000,
+ .ht_params = &iwl9000_ht_params,
+ .nvm_ver = IWL9000_NVM_VERSION,
+ .nvm_calib_ver = IWL9000_TX_POWER_VERSION,
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
+ .integrated = true,
+ .soc_latency = 5000,
+ .extra_phy_cfg_flags = FW_PHY_CFG_SHARED_CLK
+};
+
+const struct iwl_cfg iwl9462_2ac_cfg_shared_clk = {
+ .name = "Intel(R) Dual Band Wireless AC 9462",
+ .fw_name_pre = IWL9000A_FW_PRE,
+ .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE,
+ .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE,
+ IWL_DEVICE_9000,
+ .ht_params = &iwl9000_ht_params,
+ .nvm_ver = IWL9000_NVM_VERSION,
+ .nvm_calib_ver = IWL9000_TX_POWER_VERSION,
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
+ .integrated = true,
+ .soc_latency = 5000,
+ .extra_phy_cfg_flags = FW_PHY_CFG_SHARED_CLK
+};
+
+const struct iwl_cfg iwl9560_2ac_cfg_shared_clk = {
+ .name = "Intel(R) Dual Band Wireless AC 9560",
+ .fw_name_pre = IWL9000A_FW_PRE,
+ .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE,
+ .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE,
+ IWL_DEVICE_9000,
+ .ht_params = &iwl9000_ht_params,
+ .nvm_ver = IWL9000_NVM_VERSION,
+ .nvm_calib_ver = IWL9000_TX_POWER_VERSION,
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
+ .integrated = true,
+ .soc_latency = 5000,
+ .extra_phy_cfg_flags = FW_PHY_CFG_SHARED_CLK
+};
+
MODULE_FIRMWARE(IWL9000A_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL9000B_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL9000RFB_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX));
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c
index 482ac8fdc67b..096a07c5a33f 100644
--- a/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c
@@ -48,16 +48,14 @@
#define DEBUGFS_ADD_BOOL(name, parent, ptr) do { \
struct dentry *__tmp; \
- __tmp = debugfs_create_bool(#name, S_IWUSR | S_IRUSR, \
- parent, ptr); \
+ __tmp = debugfs_create_bool(#name, 0600, parent, ptr); \
if (IS_ERR(__tmp) || !__tmp) \
goto err; \
} while (0)
#define DEBUGFS_ADD_X32(name, parent, ptr) do { \
struct dentry *__tmp; \
- __tmp = debugfs_create_x32(#name, S_IWUSR | S_IRUSR, \
- parent, ptr); \
+ __tmp = debugfs_create_x32(#name, 0600, parent, ptr); \
if (IS_ERR(__tmp) || !__tmp) \
goto err; \
} while (0)
@@ -2370,48 +2368,48 @@ int iwl_dbgfs_register(struct iwl_priv *priv, struct dentry *dbgfs_dir)
if (!dir_debug)
goto err;
- DEBUGFS_ADD_FILE(nvm, dir_data, S_IRUSR);
- DEBUGFS_ADD_FILE(sram, dir_data, S_IWUSR | S_IRUSR);
- DEBUGFS_ADD_FILE(wowlan_sram, dir_data, S_IRUSR);
- DEBUGFS_ADD_FILE(stations, dir_data, S_IRUSR);
- DEBUGFS_ADD_FILE(channels, dir_data, S_IRUSR);
- DEBUGFS_ADD_FILE(status, dir_data, S_IRUSR);
- DEBUGFS_ADD_FILE(rx_handlers, dir_data, S_IWUSR | S_IRUSR);
- DEBUGFS_ADD_FILE(qos, dir_data, S_IRUSR);
- DEBUGFS_ADD_FILE(sleep_level_override, dir_data, S_IWUSR | S_IRUSR);
- DEBUGFS_ADD_FILE(current_sleep_command, dir_data, S_IRUSR);
- DEBUGFS_ADD_FILE(thermal_throttling, dir_data, S_IRUSR);
- DEBUGFS_ADD_FILE(disable_ht40, dir_data, S_IWUSR | S_IRUSR);
- DEBUGFS_ADD_FILE(temperature, dir_data, S_IRUSR);
-
- DEBUGFS_ADD_FILE(power_save_status, dir_debug, S_IRUSR);
- DEBUGFS_ADD_FILE(clear_ucode_statistics, dir_debug, S_IWUSR);
- DEBUGFS_ADD_FILE(missed_beacon, dir_debug, S_IWUSR);
- DEBUGFS_ADD_FILE(plcp_delta, dir_debug, S_IWUSR | S_IRUSR);
- DEBUGFS_ADD_FILE(rf_reset, dir_debug, S_IWUSR | S_IRUSR);
- DEBUGFS_ADD_FILE(ucode_rx_stats, dir_debug, S_IRUSR);
- DEBUGFS_ADD_FILE(ucode_tx_stats, dir_debug, S_IRUSR);
- DEBUGFS_ADD_FILE(ucode_general_stats, dir_debug, S_IRUSR);
- DEBUGFS_ADD_FILE(txfifo_flush, dir_debug, S_IWUSR);
- DEBUGFS_ADD_FILE(protection_mode, dir_debug, S_IWUSR | S_IRUSR);
- DEBUGFS_ADD_FILE(sensitivity, dir_debug, S_IRUSR);
- DEBUGFS_ADD_FILE(chain_noise, dir_debug, S_IRUSR);
- DEBUGFS_ADD_FILE(ucode_tracing, dir_debug, S_IWUSR | S_IRUSR);
- DEBUGFS_ADD_FILE(ucode_bt_stats, dir_debug, S_IRUSR);
- DEBUGFS_ADD_FILE(reply_tx_error, dir_debug, S_IRUSR);
- DEBUGFS_ADD_FILE(rxon_flags, dir_debug, S_IWUSR);
- DEBUGFS_ADD_FILE(rxon_filter_flags, dir_debug, S_IWUSR);
- DEBUGFS_ADD_FILE(echo_test, dir_debug, S_IWUSR);
- DEBUGFS_ADD_FILE(fw_restart, dir_debug, S_IWUSR);
+ DEBUGFS_ADD_FILE(nvm, dir_data, 0400);
+ DEBUGFS_ADD_FILE(sram, dir_data, 0600);
+ DEBUGFS_ADD_FILE(wowlan_sram, dir_data, 0400);
+ DEBUGFS_ADD_FILE(stations, dir_data, 0400);
+ DEBUGFS_ADD_FILE(channels, dir_data, 0400);
+ DEBUGFS_ADD_FILE(status, dir_data, 0400);
+ DEBUGFS_ADD_FILE(rx_handlers, dir_data, 0600);
+ DEBUGFS_ADD_FILE(qos, dir_data, 0400);
+ DEBUGFS_ADD_FILE(sleep_level_override, dir_data, 0600);
+ DEBUGFS_ADD_FILE(current_sleep_command, dir_data, 0400);
+ DEBUGFS_ADD_FILE(thermal_throttling, dir_data, 0400);
+ DEBUGFS_ADD_FILE(disable_ht40, dir_data, 0600);
+ DEBUGFS_ADD_FILE(temperature, dir_data, 0400);
+
+ DEBUGFS_ADD_FILE(power_save_status, dir_debug, 0400);
+ DEBUGFS_ADD_FILE(clear_ucode_statistics, dir_debug, 0200);
+ DEBUGFS_ADD_FILE(missed_beacon, dir_debug, 0200);
+ DEBUGFS_ADD_FILE(plcp_delta, dir_debug, 0600);
+ DEBUGFS_ADD_FILE(rf_reset, dir_debug, 0600);
+ DEBUGFS_ADD_FILE(ucode_rx_stats, dir_debug, 0400);
+ DEBUGFS_ADD_FILE(ucode_tx_stats, dir_debug, 0400);
+ DEBUGFS_ADD_FILE(ucode_general_stats, dir_debug, 0400);
+ DEBUGFS_ADD_FILE(txfifo_flush, dir_debug, 0200);
+ DEBUGFS_ADD_FILE(protection_mode, dir_debug, 0600);
+ DEBUGFS_ADD_FILE(sensitivity, dir_debug, 0400);
+ DEBUGFS_ADD_FILE(chain_noise, dir_debug, 0400);
+ DEBUGFS_ADD_FILE(ucode_tracing, dir_debug, 0600);
+ DEBUGFS_ADD_FILE(ucode_bt_stats, dir_debug, 0400);
+ DEBUGFS_ADD_FILE(reply_tx_error, dir_debug, 0400);
+ DEBUGFS_ADD_FILE(rxon_flags, dir_debug, 0200);
+ DEBUGFS_ADD_FILE(rxon_filter_flags, dir_debug, 0200);
+ DEBUGFS_ADD_FILE(echo_test, dir_debug, 0200);
+ DEBUGFS_ADD_FILE(fw_restart, dir_debug, 0200);
#ifdef CONFIG_IWLWIFI_DEBUG
- DEBUGFS_ADD_FILE(log_event, dir_debug, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(log_event, dir_debug, 0600);
#endif
if (iwl_advanced_bt_coexist(priv))
- DEBUGFS_ADD_FILE(bt_traffic, dir_debug, S_IRUSR);
+ DEBUGFS_ADD_FILE(bt_traffic, dir_debug, 0400);
/* Calibrations disabled/enabled status*/
- DEBUGFS_ADD_FILE(calib_disabled, dir_rf, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(calib_disabled, dir_rf, 0600);
/*
* Create a symlink with mac80211. This is not very robust, as it does
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/main.c b/drivers/net/wireless/intel/iwlwifi/dvm/main.c
index d11d72615de2..e68254e12764 100644
--- a/drivers/net/wireless/intel/iwlwifi/dvm/main.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/main.c
@@ -1651,12 +1651,7 @@ static void iwl_dump_nic_error_log(struct iwl_priv *priv)
priv->status, table.valid);
}
- trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low,
- table.data1, table.data2, table.line,
- table.blink2, table.ilink1, table.ilink2,
- table.bcon_time, table.gp1, table.gp2,
- table.gp3, table.ucode_ver, table.hw_ver,
- 0, table.brd_ver);
+ trace_iwlwifi_dev_ucode_error(trans->dev, &table, 0, table.brd_ver);
IWL_ERR(priv, "0x%08X | %-28s\n", table.error_id,
desc_lookup(table.error_id));
IWL_ERR(priv, "0x%08X | uPc\n", table.pc);
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/rs.c b/drivers/net/wireless/intel/iwlwifi/dvm/rs.c
index ddcd8c2d66cd..98050d7be411 100644
--- a/drivers/net/wireless/intel/iwlwifi/dvm/rs.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/rs.c
@@ -3276,17 +3276,17 @@ static void rs_add_debugfs(void *priv, void *priv_sta,
{
struct iwl_lq_sta *lq_sta = priv_sta;
lq_sta->rs_sta_dbgfs_scale_table_file =
- debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir,
- lq_sta, &rs_sta_dbgfs_scale_table_ops);
+ debugfs_create_file("rate_scale_table", 0600, dir,
+ lq_sta, &rs_sta_dbgfs_scale_table_ops);
lq_sta->rs_sta_dbgfs_stats_table_file =
- debugfs_create_file("rate_stats_table", S_IRUSR, dir,
- lq_sta, &rs_sta_dbgfs_stats_table_ops);
+ debugfs_create_file("rate_stats_table", 0400, dir,
+ lq_sta, &rs_sta_dbgfs_stats_table_ops);
lq_sta->rs_sta_dbgfs_rate_scale_data_file =
- debugfs_create_file("rate_scale_data", S_IRUSR, dir,
- lq_sta, &rs_sta_dbgfs_rate_scale_data_ops);
+ debugfs_create_file("rate_scale_data", 0400, dir,
+ lq_sta, &rs_sta_dbgfs_rate_scale_data_ops);
lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file =
- debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir,
- &lq_sta->tx_agg_tid_en);
+ debugfs_create_u8("tx_agg_tid_enable", 0600, dir,
+ &lq_sta->tx_agg_tid_en);
}
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h
index 3fd07bc80f54..37c57bcbfb4a 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h
@@ -8,6 +8,7 @@
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
* Copyright(c) 2016 - 2017 Intel Deutschland GmbH
+ * Copyright (C) 2018 Intel Corporation
*
* 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
@@ -30,6 +31,7 @@
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
* Copyright(c) 2016 - 2017 Intel Deutschland GmbH
+ * Copyright (C) 2018 Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -311,6 +313,17 @@ struct iwl_mcc_update_resp_v1 {
} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S_VER_1 */
/**
+ * enum iwl_geo_information - geographic information.
+ * @GEO_NO_INFO: no special info for this geo profile.
+ * @GEO_WMM_ETSI_5GHZ_INFO: this geo profile limits the WMM params
+ * for the 5 GHz band.
+ */
+enum iwl_geo_information {
+ GEO_NO_INFO = 0,
+ GEO_WMM_ETSI_5GHZ_INFO = BIT(0),
+};
+
+/**
* struct iwl_mcc_update_resp - response to MCC_UPDATE_CMD.
* Contains the new channel control profile map, if changed, and the new MCC
* (mobile country code).
@@ -320,7 +333,8 @@ struct iwl_mcc_update_resp_v1 {
* @cap: capabilities for all channels which matches the MCC
* @source_id: the MCC source, see iwl_mcc_source
* @time: time elapsed from the MCC test start (in 30 seconds TU)
- * @reserved: reserved.
+ * @geo_info: geographic specific profile information
+ * see &enum iwl_geo_information.
* @n_channels: number of channels in @channels_data (may be 14, 39, 50 or 51
* channels, depending on platform)
* @channels: channel control data map, DWORD for each channel. Only the first
@@ -332,10 +346,10 @@ struct iwl_mcc_update_resp {
u8 cap;
u8 source_id;
__le16 time;
- __le16 reserved;
+ __le16 geo_info;
__le32 n_channels;
__le32 channels[0];
-} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S_VER_2 */
+} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S_VER_3 */
/**
* struct iwl_mcc_chub_notif - chub notifies of mcc change
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h
index 3bfc657f6b42..7af3a0f51b77 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h
@@ -30,6 +30,7 @@
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
* Copyright(c) 2016 - 2017 Intel Deutschland GmbH
+ * Copyright(c) 2018 Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -579,8 +580,23 @@ enum iwl_umac_scan_general_flags {
IWL_UMAC_SCAN_GEN_FLAGS_RRM_ENABLED = BIT(8),
IWL_UMAC_SCAN_GEN_FLAGS_MATCH = BIT(9),
IWL_UMAC_SCAN_GEN_FLAGS_EXTENDED_DWELL = BIT(10),
+ /* Extended dwell is obselete when adaptive dwell is used, making this
+ * bit reusable. Hence, probe request defer is used only when adaptive
+ * dwell is supported. */
+ IWL_UMAC_SCAN_GEN_FLAGS_PROB_REQ_DEFER_SUPP = BIT(10),
IWL_UMAC_SCAN_GEN_FLAGS_LMAC2_FRAGMENTED = BIT(11),
IWL_UMAC_SCAN_GEN_FLAGS_ADAPTIVE_DWELL = BIT(13),
+ IWL_UMAC_SCAN_GEN_FLAGS_MAX_CHNL_TIME = BIT(14),
+ IWL_UMAC_SCAN_GEN_FLAGS_PROB_REQ_HIGH_TX_RATE = BIT(15),
+};
+
+/**
+ * enum iwl_umac_scan_general_flags2 - UMAC scan general flags #2
+ * @IWL_UMAC_SCAN_GEN_FLAGS2_NOTIF_PER_CHNL: Whether to send a complete
+ * notification per channel or not.
+ */
+enum iwl_umac_scan_general_flags2 {
+ IWL_UMAC_SCAN_GEN_FLAGS2_NOTIF_PER_CHNL = BIT(0),
};
/**
@@ -629,6 +645,18 @@ struct iwl_scan_req_umac_tail {
} __packed;
/**
+ * struct iwl_scan_umac_chan_param
+ * @flags: channel flags &enum iwl_scan_channel_flags
+ * @count: num of channels in scan request
+ * @reserved: for future use and alignment
+ */
+struct iwl_scan_umac_chan_param {
+ u8 flags;
+ u8 count;
+ __le16 reserved;
+} __packed; /*SCAN_CHANNEL_PARAMS_API_S_VER_1 */
+
+/**
* struct iwl_scan_req_umac
* @flags: &enum iwl_umac_scan_flags
* @uid: scan id, &enum iwl_umac_scan_uid_offsets
@@ -636,23 +664,24 @@ struct iwl_scan_req_umac_tail {
* @general_flags: &enum iwl_umac_scan_general_flags
* @scan_start_mac_id: report the scan start TSF time according to this mac TSF
* @extended_dwell: dwell time for channels 1, 6 and 11
- * @active_dwell: dwell time for active scan
- * @passive_dwell: dwell time for passive scan
+ * @active_dwell: dwell time for active scan per LMAC
+ * @passive_dwell: dwell time for passive scan per LMAC
* @fragmented_dwell: dwell time for fragmented passive scan
* @adwell_default_n_aps: for adaptive dwell the default number of APs
* per channel
* @adwell_default_n_aps_social: for adaptive dwell the default
* number of APs per social (1,6,11) channel
+ * @general_flags2: &enum iwl_umac_scan_general_flags2
* @adwell_max_budget: for adaptive dwell the maximal budget of TU to be added
* to total scan time
* @max_out_time: max out of serving channel time, per LMAC - for CDB there
* are 2 LMACs
* @suspend_time: max suspend time, per LMAC - for CDB there are 2 LMACs
* @scan_priority: scan internal prioritization &enum iwl_scan_priority
- * @channel_flags: &enum iwl_scan_channel_flags
- * @n_channels: num of channels in scan request
+ * @num_of_fragments: Number of fragments needed for full coverage per band.
+ * Relevant only for fragmented scan.
+ * @channel: &struct iwl_scan_umac_chan_param
* @reserved: for future use and alignment
- * @reserved2: for future use and alignment
* @reserved3: for future use and alignment
* @data: &struct iwl_scan_channel_cfg_umac and
* &struct iwl_scan_req_umac_tail
@@ -673,10 +702,7 @@ struct iwl_scan_req_umac {
__le32 max_out_time;
__le32 suspend_time;
__le32 scan_priority;
- /* SCAN_CHANNEL_PARAMS_API_S_VER_1 */
- u8 channel_flags;
- u8 n_channels;
- __le16 reserved2;
+ struct iwl_scan_umac_chan_param channel;
u8 data[];
} v1; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_1 */
struct {
@@ -687,10 +713,7 @@ struct iwl_scan_req_umac {
__le32 max_out_time[SCAN_TWO_LMACS];
__le32 suspend_time[SCAN_TWO_LMACS];
__le32 scan_priority;
- /* SCAN_CHANNEL_PARAMS_API_S_VER_1 */
- u8 channel_flags;
- u8 n_channels;
- __le16 reserved2;
+ struct iwl_scan_umac_chan_param channel;
u8 data[];
} v6; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_6 */
struct {
@@ -704,16 +727,30 @@ struct iwl_scan_req_umac {
__le32 max_out_time[SCAN_TWO_LMACS];
__le32 suspend_time[SCAN_TWO_LMACS];
__le32 scan_priority;
- /* SCAN_CHANNEL_PARAMS_API_S_VER_1 */
- u8 channel_flags;
- u8 n_channels;
- __le16 reserved2;
+ struct iwl_scan_umac_chan_param channel;
u8 data[];
} v7; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_7 */
+ struct {
+ u8 active_dwell[SCAN_TWO_LMACS];
+ u8 reserved2;
+ u8 adwell_default_n_aps;
+ u8 adwell_default_n_aps_social;
+ u8 general_flags2;
+ __le16 adwell_max_budget;
+ __le32 max_out_time[SCAN_TWO_LMACS];
+ __le32 suspend_time[SCAN_TWO_LMACS];
+ __le32 scan_priority;
+ u8 passive_dwell[SCAN_TWO_LMACS];
+ u8 num_of_fragments[SCAN_TWO_LMACS];
+ struct iwl_scan_umac_chan_param channel;
+ u8 data[];
+ } v8; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_8 */
};
} __packed;
-#define IWL_SCAN_REQ_UMAC_SIZE_V7 sizeof(struct iwl_scan_req_umac)
+#define IWL_SCAN_REQ_UMAC_SIZE_V8 sizeof(struct iwl_scan_req_umac)
+#define IWL_SCAN_REQ_UMAC_SIZE_V7 (sizeof(struct iwl_scan_req_umac) - \
+ 4 * sizeof(u8))
#define IWL_SCAN_REQ_UMAC_SIZE_V6 (sizeof(struct iwl_scan_req_umac) - \
2 * sizeof(u8) - sizeof(__le16))
#define IWL_SCAN_REQ_UMAC_SIZE_V1 (sizeof(struct iwl_scan_req_umac) - \
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c
index 7bd704a3e640..fa283285fcbe 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c
@@ -68,6 +68,7 @@
#include "iwl-drv.h"
#include "runtime.h"
#include "dbg.h"
+#include "debugfs.h"
#include "iwl-io.h"
#include "iwl-prph.h"
#include "iwl-csr.h"
@@ -1007,6 +1008,12 @@ int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt,
{
struct iwl_fw_dump_desc *desc;
+ if (trigger && trigger->flags & IWL_FW_DBG_FORCE_RESTART) {
+ IWL_WARN(fwrt, "Force restart: trigger %d fired.\n", trig);
+ iwl_force_nmi(fwrt->trans);
+ return 0;
+ }
+
desc = kzalloc(sizeof(*desc) + len, GFP_ATOMIC);
if (!desc)
return -ENOMEM;
@@ -1080,6 +1087,9 @@ int iwl_fw_start_dbg_conf(struct iwl_fw_runtime *fwrt, u8 conf_id)
IWL_WARN(fwrt, "FW already configured (%d) - re-configuring\n",
fwrt->dump.conf);
+ /* start default config marker cmd for syncing logs */
+ iwl_fw_trigger_timestamp(fwrt, 1);
+
/* Send all HCMDs for configuring the FW debug */
ptr = (void *)&fwrt->fw->dbg_conf_tlv[conf_id]->hcmd;
for (i = 0; i < fwrt->fw->dbg_conf_tlv[conf_id]->num_of_hcmds; i++) {
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c b/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c
index e2ded29a145d..8f005cd69559 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c
@@ -157,6 +157,20 @@ static void iwl_fw_timestamp_marker_wk(struct work_struct *work)
ret, jiffies_to_msecs(delay) / 1000);
}
+void iwl_fw_trigger_timestamp(struct iwl_fw_runtime *fwrt, u32 delay)
+{
+ IWL_INFO(fwrt,
+ "starting timestamp_marker trigger with delay: %us\n",
+ delay);
+
+ iwl_fw_cancel_timestamp(fwrt);
+
+ fwrt->timestamp.delay = msecs_to_jiffies(delay * 1000);
+
+ schedule_delayed_work(&fwrt->timestamp.wk,
+ round_jiffies_relative(fwrt->timestamp.delay));
+}
+
static ssize_t iwl_dbgfs_timestamp_marker_write(struct iwl_fw_runtime *fwrt,
char *buf, size_t count,
loff_t *ppos)
@@ -168,16 +182,8 @@ static ssize_t iwl_dbgfs_timestamp_marker_write(struct iwl_fw_runtime *fwrt,
if (ret < 0)
return ret;
- IWL_INFO(fwrt,
- "starting timestamp_marker trigger with delay: %us\n",
- delay);
+ iwl_fw_trigger_timestamp(fwrt, delay);
- iwl_fw_cancel_timestamp(fwrt);
-
- fwrt->timestamp.delay = msecs_to_jiffies(delay * 1000);
-
- schedule_delayed_work(&fwrt->timestamp.wk,
- round_jiffies_relative(fwrt->timestamp.delay));
return count;
}
@@ -187,7 +193,7 @@ int iwl_fwrt_dbgfs_register(struct iwl_fw_runtime *fwrt,
struct dentry *dbgfs_dir)
{
INIT_DELAYED_WORK(&fwrt->timestamp.wk, iwl_fw_timestamp_marker_wk);
- FWRT_DEBUGFS_ADD_FILE(timestamp_marker, dbgfs_dir, S_IWUSR);
+ FWRT_DEBUGFS_ADD_FILE(timestamp_marker, dbgfs_dir, 0200);
return 0;
err:
IWL_ERR(fwrt, "Can't create the fwrt debugfs directory\n");
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/debugfs.h b/drivers/net/wireless/intel/iwlwifi/fw/debugfs.h
index 3da468d2cc92..d93f6a4bb22d 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/debugfs.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/debugfs.h
@@ -89,6 +89,8 @@ static inline void iwl_fw_resume_timestamp(struct iwl_fw_runtime *fwrt)
round_jiffies_relative(fwrt->timestamp.delay));
}
+void iwl_fw_trigger_timestamp(struct iwl_fw_runtime *fwrt, u32 delay);
+
#else
static inline int iwl_fwrt_dbgfs_register(struct iwl_fw_runtime *fwrt,
struct dentry *dbgfs_dir)
@@ -102,4 +104,7 @@ static inline void iwl_fw_suspend_timestamp(struct iwl_fw_runtime *fwrt) {}
static inline void iwl_fw_resume_timestamp(struct iwl_fw_runtime *fwrt) {}
+static inline void iwl_fw_trigger_timestamp(struct iwl_fw_runtime *fwrt,
+ u32 delay) {}
+
#endif /* CONFIG_IWLWIFI_DEBUGFS */
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h
index 1a05d506ac9a..9b2805e1e3b1 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/file.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h
@@ -250,6 +250,8 @@ typedef unsigned int __bitwise iwl_ucode_tlv_api_t;
* indicating low latency direction.
* @IWL_UCODE_TLV_API_DEPRECATE_TTAK: RX status flag TTAK ok (bit 7) is
* deprecated.
+ * @IWL_UCODE_TLV_API_ADAPTIVE_DWELL_V2: This ucode supports version 8
+ * of scan request: SCAN_REQUEST_CMD_UMAC_API_S_VER_8
*
* @NUM_IWL_UCODE_TLV_API: number of bits used
*/
@@ -265,10 +267,12 @@ enum iwl_ucode_tlv_api {
IWL_UCODE_TLV_API_NAN2_VER2 = (__force iwl_ucode_tlv_api_t)31,
/* API Set 1 */
IWL_UCODE_TLV_API_ADAPTIVE_DWELL = (__force iwl_ucode_tlv_api_t)32,
+ IWL_UCODE_TLV_API_OCE = (__force iwl_ucode_tlv_api_t)33,
IWL_UCODE_TLV_API_NEW_BEACON_TEMPLATE = (__force iwl_ucode_tlv_api_t)34,
IWL_UCODE_TLV_API_NEW_RX_STATS = (__force iwl_ucode_tlv_api_t)35,
IWL_UCODE_TLV_API_QUOTA_LOW_LATENCY = (__force iwl_ucode_tlv_api_t)38,
IWL_UCODE_TLV_API_DEPRECATE_TTAK = (__force iwl_ucode_tlv_api_t)41,
+ IWL_UCODE_TLV_API_ADAPTIVE_DWELL_V2 = (__force iwl_ucode_tlv_api_t)42,
NUM_IWL_UCODE_TLV_API
#ifdef __CHECKER__
@@ -441,6 +445,7 @@ enum iwl_fw_phy_cfg {
FW_PHY_CFG_TX_CHAIN = 0xf << FW_PHY_CFG_TX_CHAIN_POS,
FW_PHY_CFG_RX_CHAIN_POS = 20,
FW_PHY_CFG_RX_CHAIN = 0xf << FW_PHY_CFG_RX_CHAIN_POS,
+ FW_PHY_CFG_SHARED_CLK = BIT(31),
};
#define IWL_UCODE_MAX_CS 1
@@ -616,6 +621,14 @@ enum iwl_fw_dbg_trigger_mode {
};
/**
+ * enum iwl_fw_dbg_trigger_flags - the flags supported by wrt triggers
+ * @IWL_FW_DBG_FORCE_RESTART: force a firmware restart
+ */
+enum iwl_fw_dbg_trigger_flags {
+ IWL_FW_DBG_FORCE_RESTART = BIT(0),
+};
+
+/**
* enum iwl_fw_dbg_trigger_vif_type - define the VIF type for a trigger
* @IWL_FW_DBG_CONF_VIF_ANY: any vif type
* @IWL_FW_DBG_CONF_VIF_IBSS: IBSS mode
@@ -651,6 +664,7 @@ enum iwl_fw_dbg_trigger_vif_type {
* @occurrences: number of occurrences. 0 means the trigger will never fire.
* @trig_dis_ms: the time, in milliseconds, after an occurrence of this
* trigger in which another occurrence should be ignored.
+ * @flags: &enum iwl_fw_dbg_trigger_flags
*/
struct iwl_fw_dbg_trigger_tlv {
__le32 id;
@@ -661,7 +675,8 @@ struct iwl_fw_dbg_trigger_tlv {
u8 start_conf_id;
__le16 occurrences;
__le16 trig_dis_ms;
- __le16 reserved[3];
+ u8 flags;
+ u8 reserved[5];
u8 data[0];
} __packed;
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h
index 258d439bb0a9..f0f5636dd3ea 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h
@@ -398,6 +398,7 @@ struct iwl_cfg {
u8 ucode_api_max;
u8 ucode_api_min;
u32 min_umac_error_event_table;
+ u32 extra_phy_cfg_flags;
};
/*
@@ -477,6 +478,10 @@ extern const struct iwl_cfg iwl9460_2ac_cfg_soc;
extern const struct iwl_cfg iwl9461_2ac_cfg_soc;
extern const struct iwl_cfg iwl9462_2ac_cfg_soc;
extern const struct iwl_cfg iwl9560_2ac_cfg_soc;
+extern const struct iwl_cfg iwl9460_2ac_cfg_shared_clk;
+extern const struct iwl_cfg iwl9461_2ac_cfg_shared_clk;
+extern const struct iwl_cfg iwl9462_2ac_cfg_shared_clk;
+extern const struct iwl_cfg iwl9560_2ac_cfg_shared_clk;
extern const struct iwl_cfg iwl22000_2ac_cfg_hr;
extern const struct iwl_cfg iwl22000_2ac_cfg_hr_cdb;
extern const struct iwl_cfg iwl22000_2ac_cfg_jf;
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h
index 9518a82f44c2..27e3e4e96aa2 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h
@@ -126,14 +126,11 @@ TRACE_EVENT(iwlwifi_dev_tx,
__entry->framelen, __entry->skbaddr)
);
+struct iwl_error_event_table;
TRACE_EVENT(iwlwifi_dev_ucode_error,
- TP_PROTO(const struct device *dev, u32 desc, u32 tsf_low,
- u32 data1, u32 data2, u32 line, u32 blink2, u32 ilink1,
- u32 ilink2, u32 bcon_time, u32 gp1, u32 gp2, u32 rev_type,
- u32 major, u32 minor, u32 hw_ver, u32 brd_ver),
- TP_ARGS(dev, desc, tsf_low, data1, data2, line,
- blink2, ilink1, ilink2, bcon_time, gp1, gp2,
- rev_type, major, minor, hw_ver, brd_ver),
+ TP_PROTO(const struct device *dev, const struct iwl_error_event_table *table,
+ u32 hw_ver, u32 brd_ver),
+ TP_ARGS(dev, table, hw_ver, brd_ver),
TP_STRUCT__entry(
DEV_ENTRY
__field(u32, desc)
@@ -155,20 +152,20 @@ TRACE_EVENT(iwlwifi_dev_ucode_error,
),
TP_fast_assign(
DEV_ASSIGN;
- __entry->desc = desc;
- __entry->tsf_low = tsf_low;
- __entry->data1 = data1;
- __entry->data2 = data2;
- __entry->line = line;
- __entry->blink2 = blink2;
- __entry->ilink1 = ilink1;
- __entry->ilink2 = ilink2;
- __entry->bcon_time = bcon_time;
- __entry->gp1 = gp1;
- __entry->gp2 = gp2;
- __entry->rev_type = rev_type;
- __entry->major = major;
- __entry->minor = minor;
+ __entry->desc = table->error_id;
+ __entry->tsf_low = table->tsf_low;
+ __entry->data1 = table->data1;
+ __entry->data2 = table->data2;
+ __entry->line = table->line;
+ __entry->blink2 = table->blink2;
+ __entry->ilink1 = table->ilink1;
+ __entry->ilink2 = table->ilink2;
+ __entry->bcon_time = table->bcon_time;
+ __entry->gp1 = table->gp1;
+ __entry->gp2 = table->gp2;
+ __entry->rev_type = table->gp3;
+ __entry->major = table->ucode_ver;
+ __entry->minor = table->hw_ver;
__entry->hw_ver = hw_ver;
__entry->brd_ver = brd_ver;
),
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c
index 50510fb6ab8c..6aa719865a58 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c
@@ -30,6 +30,7 @@
#ifndef __CHECKER__
#include "iwl-trans.h"
+#include "dvm/commands.h"
#define CREATE_TRACE_POINTS
#include "iwl-devtrace.h"
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
index 9c4a7f648a44..aa2d5c14e202 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
@@ -1768,41 +1768,36 @@ static void __exit iwl_drv_exit(void)
module_exit(iwl_drv_exit);
#ifdef CONFIG_IWLWIFI_DEBUG
-module_param_named(debug, iwlwifi_mod_params.debug_level, uint,
- S_IRUGO | S_IWUSR);
+module_param_named(debug, iwlwifi_mod_params.debug_level, uint, 0644);
MODULE_PARM_DESC(debug, "debug output mask");
#endif
-module_param_named(swcrypto, iwlwifi_mod_params.swcrypto, int, S_IRUGO);
+module_param_named(swcrypto, iwlwifi_mod_params.swcrypto, int, 0444);
MODULE_PARM_DESC(swcrypto, "using crypto in software (default 0 [hardware])");
-module_param_named(11n_disable, iwlwifi_mod_params.disable_11n, uint, S_IRUGO);
+module_param_named(11n_disable, iwlwifi_mod_params.disable_11n, uint, 0444);
MODULE_PARM_DESC(11n_disable,
"disable 11n functionality, bitmap: 1: full, 2: disable agg TX, 4: disable agg RX, 8 enable agg TX");
-module_param_named(amsdu_size, iwlwifi_mod_params.amsdu_size,
- int, S_IRUGO);
+module_param_named(amsdu_size, iwlwifi_mod_params.amsdu_size, int, 0444);
MODULE_PARM_DESC(amsdu_size,
"amsdu size 0: 12K for multi Rx queue devices, 4K for other devices 1:4K 2:8K 3:12K (default 0)");
-module_param_named(fw_restart, iwlwifi_mod_params.fw_restart, bool, S_IRUGO);
+module_param_named(fw_restart, iwlwifi_mod_params.fw_restart, bool, 0444);
MODULE_PARM_DESC(fw_restart, "restart firmware in case of error (default true)");
module_param_named(antenna_coupling, iwlwifi_mod_params.antenna_coupling,
- int, S_IRUGO);
+ int, 0444);
MODULE_PARM_DESC(antenna_coupling,
"specify antenna coupling in dB (default: 0 dB)");
-module_param_named(nvm_file, iwlwifi_mod_params.nvm_file, charp, S_IRUGO);
+module_param_named(nvm_file, iwlwifi_mod_params.nvm_file, charp, 0444);
MODULE_PARM_DESC(nvm_file, "NVM file name");
-module_param_named(d0i3_disable, iwlwifi_mod_params.d0i3_disable,
- bool, S_IRUGO);
+module_param_named(d0i3_disable, iwlwifi_mod_params.d0i3_disable, bool, 0444);
MODULE_PARM_DESC(d0i3_disable, "disable d0i3 functionality (default: Y)");
-module_param_named(lar_disable, iwlwifi_mod_params.lar_disable,
- bool, S_IRUGO);
+module_param_named(lar_disable, iwlwifi_mod_params.lar_disable, bool, 0444);
MODULE_PARM_DESC(lar_disable, "disable LAR functionality (default: N)");
-module_param_named(uapsd_disable, iwlwifi_mod_params.uapsd_disable,
- uint, S_IRUGO | S_IWUSR);
+module_param_named(uapsd_disable, iwlwifi_mod_params.uapsd_disable, uint, 0644);
MODULE_PARM_DESC(uapsd_disable,
"disable U-APSD functionality bitmap 1: BSS 2: P2P Client (default: 3)");
@@ -1823,31 +1818,27 @@ MODULE_PARM_DESC(uapsd_disable,
* default: bt_coex_active = true (BT_COEX_ENABLE)
*/
module_param_named(bt_coex_active, iwlwifi_mod_params.bt_coex_active,
- bool, S_IRUGO);
+ bool, 0444);
MODULE_PARM_DESC(bt_coex_active, "enable wifi/bt co-exist (default: enable)");
-module_param_named(led_mode, iwlwifi_mod_params.led_mode, int, S_IRUGO);
+module_param_named(led_mode, iwlwifi_mod_params.led_mode, int, 0444);
MODULE_PARM_DESC(led_mode, "0=system default, "
"1=On(RF On)/Off(RF Off), 2=blinking, 3=Off (default: 0)");
-module_param_named(power_save, iwlwifi_mod_params.power_save,
- bool, S_IRUGO);
+module_param_named(power_save, iwlwifi_mod_params.power_save, bool, 0444);
MODULE_PARM_DESC(power_save,
"enable WiFi power management (default: disable)");
-module_param_named(power_level, iwlwifi_mod_params.power_level,
- int, S_IRUGO);
+module_param_named(power_level, iwlwifi_mod_params.power_level, int, 0444);
MODULE_PARM_DESC(power_level,
"default power save level (range from 1 - 5, default: 1)");
-module_param_named(fw_monitor, iwlwifi_mod_params.fw_monitor, bool, S_IRUGO);
+module_param_named(fw_monitor, iwlwifi_mod_params.fw_monitor, bool, 0444);
MODULE_PARM_DESC(fw_monitor,
"firmware monitor - to debug FW (default: false - needs lots of memory)");
-module_param_named(d0i3_timeout, iwlwifi_mod_params.d0i3_timeout,
- uint, S_IRUGO);
+module_param_named(d0i3_timeout, iwlwifi_mod_params.d0i3_timeout, uint, 0444);
MODULE_PARM_DESC(d0i3_timeout, "Timeout to D0i3 entry when idle (ms)");
-module_param_named(disable_11ac, iwlwifi_mod_params.disable_11ac, bool,
- S_IRUGO);
+module_param_named(disable_11ac, iwlwifi_mod_params.disable_11ac, bool, 0444);
MODULE_PARM_DESC(disable_11ac, "Disable VHT capabilities (default: false)");
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/constants.h b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h
index 976640fed334..96b52a275ee3 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/constants.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h
@@ -110,6 +110,8 @@
#define IWL_MVM_SW_TX_CSUM_OFFLOAD 0
#define IWL_MVM_HW_CSUM_DISABLE 0
#define IWL_MVM_PARSE_NVM 0
+#define IWL_MVM_ADWELL_ENABLE 1
+#define IWL_MVM_ADWELL_MAX_BUDGET 0
#define IWL_MVM_RS_NUM_TRY_BEFORE_ANT_TOGGLE 1
#define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE 2
#define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE_TW 1
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c
index 4228fac77f41..f7fcf700196b 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c
@@ -1276,7 +1276,6 @@ static ssize_t iwl_dbgfs_low_latency_write(struct ieee80211_vif *vif, char *buf,
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm *mvm = mvmvif->mvm;
- bool prev;
u8 value;
int ret;
@@ -1287,9 +1286,7 @@ static ssize_t iwl_dbgfs_low_latency_write(struct ieee80211_vif *vif, char *buf,
return -EINVAL;
mutex_lock(&mvm->mutex);
- prev = iwl_mvm_vif_low_latency(mvmvif);
- mvmvif->low_latency_dbgfs = value;
- iwl_mvm_update_low_latency(mvm, vif, prev);
+ iwl_mvm_update_low_latency(mvm, vif, value, LOW_LATENCY_DEBUGFS);
mutex_unlock(&mvm->mutex);
return count;
@@ -1306,9 +1303,9 @@ static ssize_t iwl_dbgfs_low_latency_read(struct file *file,
len = scnprintf(buf, sizeof(buf) - 1,
"traffic=%d\ndbgfs=%d\nvcmd=%d\n",
- mvmvif->low_latency_traffic,
- mvmvif->low_latency_dbgfs,
- mvmvif->low_latency_vcmd);
+ !!(mvmvif->low_latency & LOW_LATENCY_TRAFFIC),
+ !!(mvmvif->low_latency & LOW_LATENCY_DEBUGFS),
+ !!(mvmvif->low_latency & LOW_LATENCY_VCMD));
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
@@ -1506,44 +1503,36 @@ void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM &&
((vif->type == NL80211_IFTYPE_STATION && !vif->p2p) ||
(vif->type == NL80211_IFTYPE_STATION && vif->p2p)))
- MVM_DEBUGFS_ADD_FILE_VIF(pm_params, mvmvif->dbgfs_dir, S_IWUSR |
- S_IRUSR);
-
- MVM_DEBUGFS_ADD_FILE_VIF(tx_pwr_lmt, mvmvif->dbgfs_dir, S_IRUSR);
- MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir, S_IRUSR);
- MVM_DEBUGFS_ADD_FILE_VIF(low_latency, mvmvif->dbgfs_dir,
- S_IRUSR | S_IWUSR);
- MVM_DEBUGFS_ADD_FILE_VIF(uapsd_misbehaving, mvmvif->dbgfs_dir,
- S_IRUSR | S_IWUSR);
- MVM_DEBUGFS_ADD_FILE_VIF(rx_phyinfo, mvmvif->dbgfs_dir,
- S_IRUSR | S_IWUSR);
- MVM_DEBUGFS_ADD_FILE_VIF(quota_min, mvmvif->dbgfs_dir,
- S_IRUSR | S_IWUSR);
- MVM_DEBUGFS_ADD_FILE_VIF(os_device_timediff,
- mvmvif->dbgfs_dir, S_IRUSR);
+ MVM_DEBUGFS_ADD_FILE_VIF(pm_params, mvmvif->dbgfs_dir, 0600);
+
+ MVM_DEBUGFS_ADD_FILE_VIF(tx_pwr_lmt, mvmvif->dbgfs_dir, 0400);
+ MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir, 0400);
+ MVM_DEBUGFS_ADD_FILE_VIF(low_latency, mvmvif->dbgfs_dir, 0600);
+ MVM_DEBUGFS_ADD_FILE_VIF(uapsd_misbehaving, mvmvif->dbgfs_dir, 0600);
+ MVM_DEBUGFS_ADD_FILE_VIF(rx_phyinfo, mvmvif->dbgfs_dir, 0600);
+ MVM_DEBUGFS_ADD_FILE_VIF(quota_min, mvmvif->dbgfs_dir, 0600);
+ MVM_DEBUGFS_ADD_FILE_VIF(os_device_timediff, mvmvif->dbgfs_dir, 0400);
if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p &&
mvmvif == mvm->bf_allowed_vif)
- MVM_DEBUGFS_ADD_FILE_VIF(bf_params, mvmvif->dbgfs_dir,
- S_IRUSR | S_IWUSR);
+ MVM_DEBUGFS_ADD_FILE_VIF(bf_params, mvmvif->dbgfs_dir, 0600);
if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT) &&
!vif->p2p && (vif->type != NL80211_IFTYPE_P2P_DEVICE)) {
if (IWL_MVM_TOF_IS_RESPONDER && vif->type == NL80211_IFTYPE_AP)
MVM_DEBUGFS_ADD_FILE_VIF(tof_responder_params,
- mvmvif->dbgfs_dir,
- S_IRUSR | S_IWUSR);
+ mvmvif->dbgfs_dir, 0600);
MVM_DEBUGFS_ADD_FILE_VIF(tof_range_request, mvmvif->dbgfs_dir,
- S_IRUSR | S_IWUSR);
+ 0600);
MVM_DEBUGFS_ADD_FILE_VIF(tof_range_req_ext, mvmvif->dbgfs_dir,
- S_IRUSR | S_IWUSR);
+ 0600);
MVM_DEBUGFS_ADD_FILE_VIF(tof_enable, mvmvif->dbgfs_dir,
- S_IRUSR | S_IWUSR);
+ 0600);
MVM_DEBUGFS_ADD_FILE_VIF(tof_range_abort, mvmvif->dbgfs_dir,
- S_IRUSR | S_IWUSR);
+ 0600);
MVM_DEBUGFS_ADD_FILE_VIF(tof_range_response, mvmvif->dbgfs_dir,
- S_IRUSR);
+ 0400);
}
/*
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c
index 9c436d8d001d..0e6401cd7ccc 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c
@@ -1914,7 +1914,7 @@ void iwl_mvm_sta_add_debugfs(struct ieee80211_hw *hw,
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
if (iwl_mvm_has_tlc_offload(mvm))
- MVM_DEBUGFS_ADD_STA_FILE(rs_data, dir, S_IRUSR);
+ MVM_DEBUGFS_ADD_STA_FILE(rs_data, dir, 0400);
return;
err:
@@ -1930,48 +1930,45 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
mvm->debugfs_dir = dbgfs_dir;
- MVM_DEBUGFS_ADD_FILE(tx_flush, mvm->debugfs_dir, S_IWUSR);
- MVM_DEBUGFS_ADD_FILE(sta_drain, mvm->debugfs_dir, S_IWUSR);
- MVM_DEBUGFS_ADD_FILE(sram, mvm->debugfs_dir, S_IWUSR | S_IRUSR);
- MVM_DEBUGFS_ADD_FILE(set_nic_temperature, mvm->debugfs_dir,
- S_IWUSR | S_IRUSR);
- MVM_DEBUGFS_ADD_FILE(nic_temp, dbgfs_dir, S_IRUSR);
- MVM_DEBUGFS_ADD_FILE(ctdp_budget, dbgfs_dir, S_IRUSR);
- MVM_DEBUGFS_ADD_FILE(stop_ctdp, dbgfs_dir, S_IWUSR);
- MVM_DEBUGFS_ADD_FILE(force_ctkill, dbgfs_dir, S_IWUSR);
- MVM_DEBUGFS_ADD_FILE(stations, dbgfs_dir, S_IRUSR);
- MVM_DEBUGFS_ADD_FILE(bt_notif, dbgfs_dir, S_IRUSR);
- MVM_DEBUGFS_ADD_FILE(bt_cmd, dbgfs_dir, S_IRUSR);
- MVM_DEBUGFS_ADD_FILE(disable_power_off, mvm->debugfs_dir,
- S_IRUSR | S_IWUSR);
- MVM_DEBUGFS_ADD_FILE(fw_ver, mvm->debugfs_dir, S_IRUSR);
- MVM_DEBUGFS_ADD_FILE(fw_rx_stats, mvm->debugfs_dir, S_IRUSR);
- MVM_DEBUGFS_ADD_FILE(drv_rx_stats, mvm->debugfs_dir, S_IRUSR);
- MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR);
- MVM_DEBUGFS_ADD_FILE(fw_nmi, mvm->debugfs_dir, S_IWUSR);
- MVM_DEBUGFS_ADD_FILE(bt_tx_prio, mvm->debugfs_dir, S_IWUSR);
- MVM_DEBUGFS_ADD_FILE(bt_force_ant, mvm->debugfs_dir, S_IWUSR);
- MVM_DEBUGFS_ADD_FILE(scan_ant_rxchain, mvm->debugfs_dir,
- S_IWUSR | S_IRUSR);
- MVM_DEBUGFS_ADD_FILE(prph_reg, mvm->debugfs_dir, S_IWUSR | S_IRUSR);
- MVM_DEBUGFS_ADD_FILE(d0i3_refs, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
- MVM_DEBUGFS_ADD_FILE(fw_dbg_conf, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
- MVM_DEBUGFS_ADD_FILE(fw_dbg_collect, mvm->debugfs_dir, S_IWUSR);
- MVM_DEBUGFS_ADD_FILE(max_amsdu_len, mvm->debugfs_dir, S_IWUSR);
- MVM_DEBUGFS_ADD_FILE(send_echo_cmd, mvm->debugfs_dir, S_IWUSR);
- MVM_DEBUGFS_ADD_FILE(cont_recording, mvm->debugfs_dir, S_IWUSR);
- MVM_DEBUGFS_ADD_FILE(indirection_tbl, mvm->debugfs_dir, S_IWUSR);
- MVM_DEBUGFS_ADD_FILE(inject_packet, mvm->debugfs_dir, S_IWUSR);
+ MVM_DEBUGFS_ADD_FILE(tx_flush, mvm->debugfs_dir, 0200);
+ MVM_DEBUGFS_ADD_FILE(sta_drain, mvm->debugfs_dir, 0200);
+ MVM_DEBUGFS_ADD_FILE(sram, mvm->debugfs_dir, 0600);
+ MVM_DEBUGFS_ADD_FILE(set_nic_temperature, mvm->debugfs_dir, 0600);
+ MVM_DEBUGFS_ADD_FILE(nic_temp, dbgfs_dir, 0400);
+ MVM_DEBUGFS_ADD_FILE(ctdp_budget, dbgfs_dir, 0400);
+ MVM_DEBUGFS_ADD_FILE(stop_ctdp, dbgfs_dir, 0200);
+ MVM_DEBUGFS_ADD_FILE(force_ctkill, dbgfs_dir, 0200);
+ MVM_DEBUGFS_ADD_FILE(stations, dbgfs_dir, 0400);
+ MVM_DEBUGFS_ADD_FILE(bt_notif, dbgfs_dir, 0400);
+ MVM_DEBUGFS_ADD_FILE(bt_cmd, dbgfs_dir, 0400);
+ MVM_DEBUGFS_ADD_FILE(disable_power_off, mvm->debugfs_dir, 0600);
+ MVM_DEBUGFS_ADD_FILE(fw_ver, mvm->debugfs_dir, 0400);
+ MVM_DEBUGFS_ADD_FILE(fw_rx_stats, mvm->debugfs_dir, 0400);
+ MVM_DEBUGFS_ADD_FILE(drv_rx_stats, mvm->debugfs_dir, 0400);
+ MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, 0200);
+ MVM_DEBUGFS_ADD_FILE(fw_nmi, mvm->debugfs_dir, 0200);
+ MVM_DEBUGFS_ADD_FILE(bt_tx_prio, mvm->debugfs_dir, 0200);
+ MVM_DEBUGFS_ADD_FILE(bt_force_ant, mvm->debugfs_dir, 0200);
+ MVM_DEBUGFS_ADD_FILE(scan_ant_rxchain, mvm->debugfs_dir, 0600);
+ MVM_DEBUGFS_ADD_FILE(prph_reg, mvm->debugfs_dir, 0600);
+ MVM_DEBUGFS_ADD_FILE(d0i3_refs, mvm->debugfs_dir, 0600);
+ MVM_DEBUGFS_ADD_FILE(fw_dbg_conf, mvm->debugfs_dir, 0600);
+ MVM_DEBUGFS_ADD_FILE(fw_dbg_collect, mvm->debugfs_dir, 0200);
+ MVM_DEBUGFS_ADD_FILE(max_amsdu_len, mvm->debugfs_dir, 0200);
+ MVM_DEBUGFS_ADD_FILE(send_echo_cmd, mvm->debugfs_dir, 0200);
+ MVM_DEBUGFS_ADD_FILE(cont_recording, mvm->debugfs_dir, 0200);
+ MVM_DEBUGFS_ADD_FILE(indirection_tbl, mvm->debugfs_dir, 0200);
+ MVM_DEBUGFS_ADD_FILE(inject_packet, mvm->debugfs_dir, 0200);
#ifdef CONFIG_ACPI
- MVM_DEBUGFS_ADD_FILE(sar_geo_profile, dbgfs_dir, S_IRUSR);
+ MVM_DEBUGFS_ADD_FILE(sar_geo_profile, dbgfs_dir, 0400);
#endif
if (!debugfs_create_bool("enable_scan_iteration_notif",
- S_IRUSR | S_IWUSR,
+ 0600,
mvm->debugfs_dir,
&mvm->scan_iter_notif_enabled))
goto err;
- if (!debugfs_create_bool("drop_bcn_ap_mode", S_IRUSR | S_IWUSR,
+ if (!debugfs_create_bool("drop_bcn_ap_mode", 0600,
mvm->debugfs_dir, &mvm->drop_bcn_ap_mode))
goto err;
@@ -1982,50 +1979,49 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
if (!bcast_dir)
goto err;
- if (!debugfs_create_bool("override", S_IRUSR | S_IWUSR,
- bcast_dir,
- &mvm->dbgfs_bcast_filtering.override))
+ if (!debugfs_create_bool("override", 0600,
+ bcast_dir,
+ &mvm->dbgfs_bcast_filtering.override))
goto err;
MVM_DEBUGFS_ADD_FILE_ALIAS("filters", bcast_filters,
- bcast_dir, S_IWUSR | S_IRUSR);
+ bcast_dir, 0600);
MVM_DEBUGFS_ADD_FILE_ALIAS("macs", bcast_filters_macs,
- bcast_dir, S_IWUSR | S_IRUSR);
+ bcast_dir, 0600);
}
#endif
#ifdef CONFIG_PM_SLEEP
- MVM_DEBUGFS_ADD_FILE(d3_sram, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
- MVM_DEBUGFS_ADD_FILE(d3_test, mvm->debugfs_dir, S_IRUSR);
- if (!debugfs_create_bool("d3_wake_sysassert", S_IRUSR | S_IWUSR,
+ MVM_DEBUGFS_ADD_FILE(d3_sram, mvm->debugfs_dir, 0600);
+ MVM_DEBUGFS_ADD_FILE(d3_test, mvm->debugfs_dir, 0400);
+ if (!debugfs_create_bool("d3_wake_sysassert", 0600,
mvm->debugfs_dir, &mvm->d3_wake_sysassert))
goto err;
- if (!debugfs_create_u32("last_netdetect_scans", S_IRUSR,
+ if (!debugfs_create_u32("last_netdetect_scans", 0400,
mvm->debugfs_dir, &mvm->last_netdetect_scans))
goto err;
#endif
- if (!debugfs_create_u8("ps_disabled", S_IRUSR,
+ if (!debugfs_create_u8("ps_disabled", 0400,
mvm->debugfs_dir, &mvm->ps_disabled))
goto err;
- if (!debugfs_create_blob("nvm_hw", S_IRUSR,
- mvm->debugfs_dir, &mvm->nvm_hw_blob))
+ if (!debugfs_create_blob("nvm_hw", 0400,
+ mvm->debugfs_dir, &mvm->nvm_hw_blob))
goto err;
- if (!debugfs_create_blob("nvm_sw", S_IRUSR,
- mvm->debugfs_dir, &mvm->nvm_sw_blob))
+ if (!debugfs_create_blob("nvm_sw", 0400,
+ mvm->debugfs_dir, &mvm->nvm_sw_blob))
goto err;
- if (!debugfs_create_blob("nvm_calib", S_IRUSR,
- mvm->debugfs_dir, &mvm->nvm_calib_blob))
+ if (!debugfs_create_blob("nvm_calib", 0400,
+ mvm->debugfs_dir, &mvm->nvm_calib_blob))
goto err;
- if (!debugfs_create_blob("nvm_prod", S_IRUSR,
- mvm->debugfs_dir, &mvm->nvm_prod_blob))
+ if (!debugfs_create_blob("nvm_prod", 0400,
+ mvm->debugfs_dir, &mvm->nvm_prod_blob))
goto err;
- if (!debugfs_create_blob("nvm_phy_sku", S_IRUSR,
+ if (!debugfs_create_blob("nvm_phy_sku", 0400,
mvm->debugfs_dir, &mvm->nvm_phy_sku_blob))
goto err;
- debugfs_create_file("mem", S_IRUSR | S_IWUSR, dbgfs_dir, mvm,
- &iwl_dbgfs_mem_ops);
+ debugfs_create_file("mem", 0600, dbgfs_dir, mvm, &iwl_dbgfs_mem_ops);
/*
* Create a symlink with mac80211. It will be removed when mac80211
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
index 0920be637b57..3c59109bea20 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
@@ -433,6 +433,10 @@ static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm)
/* Set parameters */
phy_cfg_cmd.phy_cfg = cpu_to_le32(iwl_mvm_get_phy_config(mvm));
+
+ /* set flags extra PHY configuration flags from the device's cfg */
+ phy_cfg_cmd.phy_cfg |= cpu_to_le32(mvm->cfg->extra_phy_cfg_flags);
+
phy_cfg_cmd.calib_control.event_trigger =
mvm->fw->default_calib[ucode_type].event_trigger;
phy_cfg_cmd.calib_control.flow_trigger =
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
index ebf511150f4d..51b30424575b 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
@@ -421,6 +421,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS);
ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
ieee80211_hw_set(hw, NEEDS_UNIQUE_STA_ADDR);
+ ieee80211_hw_set(hw, DEAUTH_NEED_MGD_TX_PREP);
if (iwl_mvm_has_tlc_offload(mvm)) {
ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW);
@@ -661,6 +662,17 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
NL80211_EXT_FEATURE_SET_SCAN_DWELL);
}
+ if (iwl_mvm_is_oce_supported(mvm)) {
+ wiphy_ext_feature_set(hw->wiphy,
+ NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP);
+ wiphy_ext_feature_set(hw->wiphy,
+ NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME);
+ wiphy_ext_feature_set(hw->wiphy,
+ NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION);
+ wiphy_ext_feature_set(hw->wiphy,
+ NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE);
+ }
+
mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
#ifdef CONFIG_PM_SLEEP
@@ -2132,10 +2144,10 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
* Send the bcast station. At this stage the TBTT and DTIM time
* events are added and applied to the scheduler
*/
- iwl_mvm_send_add_bcast_sta(mvm, vif);
+ ret = iwl_mvm_send_add_bcast_sta(mvm, vif);
if (ret)
goto out_unbind;
- iwl_mvm_add_mcast_sta(mvm, vif);
+ ret = iwl_mvm_add_mcast_sta(mvm, vif);
if (ret) {
iwl_mvm_send_rm_bcast_sta(mvm, vif);
goto out_unbind;
@@ -2804,9 +2816,6 @@ static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw,
u32 duration = IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS;
u32 min_duration = IWL_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS;
- if (WARN_ON_ONCE(vif->bss_conf.assoc))
- return;
-
/*
* iwl_mvm_protect_session() reads directly from the device
* (the system time), so make sure it is available.
@@ -3494,6 +3503,7 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm,
ret = 0;
goto out;
case NL80211_IFTYPE_STATION:
+ mvmvif->csa_bcn_pending = false;
break;
case NL80211_IFTYPE_MONITOR:
/* always disable PS when a monitor interface is active */
@@ -3537,7 +3547,7 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm,
}
if (switching_chanctx && vif->type == NL80211_IFTYPE_STATION) {
- u32 duration = 2 * vif->bss_conf.beacon_int;
+ u32 duration = 3 * vif->bss_conf.beacon_int;
/* iwl_mvm_protect_session() reads directly from the
* device (the system time), so make sure it is
@@ -3550,6 +3560,7 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm,
/* Protect the session to make sure we hear the first
* beacon on the new channel.
*/
+ mvmvif->csa_bcn_pending = true;
iwl_mvm_protect_session(mvm, vif, duration, duration,
vif->bss_conf.beacon_int / 2,
true);
@@ -3988,6 +3999,7 @@ static int iwl_mvm_post_channel_switch(struct ieee80211_hw *hw,
if (vif->type == NL80211_IFTYPE_STATION) {
struct iwl_mvm_sta *mvmsta;
+ mvmvif->csa_bcn_pending = false;
mvmsta = iwl_mvm_sta_from_staid_protected(mvm,
mvmvif->ap_sta_id);
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
index 89ff02d7c876..d2cf751db68d 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
@@ -300,6 +300,18 @@ enum iwl_bt_force_ant_mode {
};
/**
+* struct iwl_mvm_low_latency_cause - low latency set causes
+* @LOW_LATENCY_TRAFFIC: indicates low latency traffic was detected
+* @LOW_LATENCY_DEBUGFS: low latency mode set from debugfs
+* @LOW_LATENCY_VCMD: low latency mode set from vendor command
+*/
+enum iwl_mvm_low_latency_cause {
+ LOW_LATENCY_TRAFFIC = BIT(0),
+ LOW_LATENCY_DEBUGFS = BIT(1),
+ LOW_LATENCY_VCMD = BIT(2),
+};
+
+/**
* struct iwl_mvm_vif_bf_data - beacon filtering related data
* @bf_enabled: indicates if beacon filtering is enabled
* @ba_enabled: indicated if beacon abort is enabled
@@ -335,9 +347,8 @@ struct iwl_mvm_vif_bf_data {
* @pm_enabled - Indicate if MAC power management is allowed
* @monitor_active: indicates that monitor context is configured, and that the
* interface should get quota etc.
- * @low_latency_traffic: indicates low latency traffic was detected
- * @low_latency_dbgfs: low latency mode set from debugfs
- * @low_latency_vcmd: low latency mode set from vendor command
+ * @low_latency: indicates low latency is set, see
+ * enum &iwl_mvm_low_latency_cause for causes.
* @ps_disabled: indicates that this interface requires PS to be disabled
* @queue_params: QoS params for this MAC
* @bcast_sta: station used for broadcast packets. Used by the following
@@ -367,7 +378,7 @@ struct iwl_mvm_vif {
bool ap_ibss_active;
bool pm_enabled;
bool monitor_active;
- bool low_latency_traffic, low_latency_dbgfs, low_latency_vcmd;
+ u8 low_latency;
bool ps_disabled;
struct iwl_mvm_vif_bf_data bf_data;
@@ -438,6 +449,9 @@ struct iwl_mvm_vif {
bool csa_failed;
u16 csa_target_freq;
+ /* Indicates that we are waiting for a beacon on a new channel */
+ bool csa_bcn_pending;
+
/* TCP Checksum Offload */
netdev_features_t features;
};
@@ -1152,6 +1166,18 @@ static inline bool iwl_mvm_is_adaptive_dwell_supported(struct iwl_mvm *mvm)
IWL_UCODE_TLV_API_ADAPTIVE_DWELL);
}
+static inline bool iwl_mvm_is_adaptive_dwell_v2_supported(struct iwl_mvm *mvm)
+{
+ return fw_has_api(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_API_ADAPTIVE_DWELL_V2);
+}
+
+static inline bool iwl_mvm_is_oce_supported(struct iwl_mvm *mvm)
+{
+ /* OCE should never be enabled for LMAC scan FWs */
+ return fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_OCE);
+}
+
static inline bool iwl_mvm_enter_d0i3_on_suspend(struct iwl_mvm *mvm)
{
/* For now we only use this mode to differentiate between
@@ -1741,7 +1767,8 @@ bool iwl_mvm_rx_diversity_allowed(struct iwl_mvm *mvm);
/* Low latency */
int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- bool value);
+ bool low_latency,
+ enum iwl_mvm_low_latency_cause cause);
/* get SystemLowLatencyMode - only needed for beacon threshold? */
bool iwl_mvm_low_latency(struct iwl_mvm *mvm);
/* get VMACLowLatencyMode */
@@ -1757,9 +1784,17 @@ static inline bool iwl_mvm_vif_low_latency(struct iwl_mvm_vif *mvmvif)
* binding, so this has no real impact. For now, just return
* the current desired low-latency state.
*/
- return mvmvif->low_latency_dbgfs ||
- mvmvif->low_latency_traffic ||
- mvmvif->low_latency_vcmd;
+ return mvmvif->low_latency;
+}
+
+static inline
+void iwl_mvm_vif_set_low_latency(struct iwl_mvm_vif *mvmvif, bool set,
+ enum iwl_mvm_low_latency_cause cause)
+{
+ if (set)
+ mvmvif->low_latency |= cause;
+ else
+ mvmvif->low_latency &= ~cause;
}
/* hw scheduler queue config */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
index ab7fb5aad984..224bfa1bcf53 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
@@ -104,14 +104,14 @@ struct iwl_mvm_mod_params iwlmvm_mod_params = {
/* rest of fields are 0 by default */
};
-module_param_named(init_dbg, iwlmvm_mod_params.init_dbg, bool, S_IRUGO);
+module_param_named(init_dbg, iwlmvm_mod_params.init_dbg, bool, 0444);
MODULE_PARM_DESC(init_dbg,
"set to true to debug an ASSERT in INIT fw (default: false");
-module_param_named(power_scheme, iwlmvm_mod_params.power_scheme, int, S_IRUGO);
+module_param_named(power_scheme, iwlmvm_mod_params.power_scheme, int, 0444);
MODULE_PARM_DESC(power_scheme,
"power management scheme: 1-active, 2-balanced, 3-low power, default: 2");
module_param_named(tfd_q_hang_detect, iwlmvm_mod_params.tfd_q_hang_detect,
- bool, S_IRUGO);
+ bool, 0444);
MODULE_PARM_DESC(tfd_q_hang_detect,
"TFD queues hang detection (default: true");
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c
index 305cd56bf746..7f5434b34d0d 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c
@@ -8,6 +8,7 @@
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
* Copyright(c) 2017 Intel Deutschland GmbH
+ * Copyright(c) 2018 Intel Corporation
*
* 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
@@ -18,11 +19,6 @@
* 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,
- * USA
- *
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
@@ -34,6 +30,7 @@
*
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2018 Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -286,6 +283,20 @@ void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
return;
ctxt->ref--;
+
+ /*
+ * Move unused phy's to a default channel. When the phy is moved the,
+ * fw will cleanup immediate quiet bit if it was previously set,
+ * otherwise we might not be able to reuse this phy.
+ */
+ if (ctxt->ref == 0) {
+ struct ieee80211_channel *chan;
+ struct cfg80211_chan_def chandef;
+
+ chan = &mvm->hw->wiphy->bands[NL80211_BAND_2GHZ]->channels[0];
+ cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
+ iwl_mvm_phy_ctxt_changed(mvm, ctxt, &chandef, 1, 1);
+ }
}
static void iwl_mvm_binding_iterator(void *_data, u8 *mac,
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c
index 55d1274c6092..fb5745660509 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c
@@ -234,13 +234,15 @@ void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt)
struct iwl_mvm_sta *mvmsta;
struct iwl_lq_sta_rs_fw *lq_sta;
+ rcu_read_lock();
+
notif = (void *)pkt->data;
mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, notif->sta_id);
if (!mvmsta) {
IWL_ERR(mvm, "Invalid sta id (%d) in FW TLC notification\n",
notif->sta_id);
- return;
+ goto out;
}
lq_sta = &mvmsta->lq_sta.rs_fw;
@@ -251,6 +253,8 @@ void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt)
IWL_DEBUG_RATE(mvm, "new rate_n_flags: 0x%X\n",
lq_sta->last_rate_n_flags);
}
+out:
+ rcu_read_unlock();
}
void rs_fw_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c
index 47f4c7a1d80d..5d776ec1840f 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c
@@ -4010,18 +4010,18 @@ static void rs_drv_add_sta_debugfs(void *mvm, void *priv_sta,
if (!mvmsta->vif)
return;
- debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir,
+ debugfs_create_file("rate_scale_table", 0600, dir,
lq_sta, &rs_sta_dbgfs_scale_table_ops);
- debugfs_create_file("rate_stats_table", S_IRUSR, dir,
+ debugfs_create_file("rate_stats_table", 0400, dir,
lq_sta, &rs_sta_dbgfs_stats_table_ops);
- debugfs_create_file("drv_tx_stats", S_IRUSR | S_IWUSR, dir,
+ debugfs_create_file("drv_tx_stats", 0600, dir,
lq_sta, &rs_sta_dbgfs_drv_tx_stats_ops);
- debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir,
+ debugfs_create_u8("tx_agg_tid_enable", 0600, dir,
&lq_sta->tx_agg_tid_en);
- debugfs_create_u8("reduced_tpc", S_IRUSR | S_IWUSR, dir,
+ debugfs_create_u8("reduced_tpc", 0600, dir,
&lq_sta->pers.dbg_fixed_txp_reduction);
- MVM_DEBUGFS_ADD_FILE_RS(ss_force, dir, S_IRUSR | S_IWUSR);
+ MVM_DEBUGFS_ADD_FILE_RS(ss_force, dir, 0600);
return;
err:
IWL_ERR((struct iwl_mvm *)mvm, "Can't create debugfs entity\n");
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
index 580de5851fc7..4a4ccfd11e5b 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
@@ -831,6 +831,16 @@ out:
rcu_read_unlock();
}
+static void iwl_mvm_flip_address(u8 *addr)
+{
+ int i;
+ u8 mac_addr[ETH_ALEN];
+
+ for (i = 0; i < ETH_ALEN; i++)
+ mac_addr[i] = addr[ETH_ALEN - i - 1];
+ ether_addr_copy(addr, mac_addr);
+}
+
void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
struct iwl_rx_cmd_buffer *rxb, int queue)
{
@@ -985,21 +995,16 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
*/
if ((desc->mac_flags2 & IWL_RX_MPDU_MFLG2_AMSDU) &&
!WARN_ON(!ieee80211_is_data_qos(hdr->frame_control))) {
- int i;
u8 *qc = ieee80211_get_qos_ctl(hdr);
- u8 mac_addr[ETH_ALEN];
*qc &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
- for (i = 0; i < ETH_ALEN; i++)
- mac_addr[i] = hdr->addr3[ETH_ALEN - i - 1];
- ether_addr_copy(hdr->addr3, mac_addr);
+ if (mvm->trans->cfg->device_family ==
+ IWL_DEVICE_FAMILY_9000) {
+ iwl_mvm_flip_address(hdr->addr3);
- if (ieee80211_has_a4(hdr->frame_control)) {
- for (i = 0; i < ETH_ALEN; i++)
- mac_addr[i] =
- hdr->addr4[ETH_ALEN - i - 1];
- ether_addr_copy(hdr->addr4, mac_addr);
+ if (ieee80211_has_a4(hdr->frame_control))
+ iwl_mvm_flip_address(hdr->addr4);
}
}
if (baid != IWL_RX_REORDER_DATA_INVALID_BAID) {
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c
index 356b16f40e78..b31f0ffbbbf0 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c
@@ -35,6 +35,7 @@
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
* Copyright(c) 2016 - 2017 Intel Deutschland GmbH
+ * Copyright(c) 2018 Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -85,6 +86,17 @@ enum iwl_mvm_traffic_load {
#define IWL_SCAN_DWELL_PASSIVE 110
#define IWL_SCAN_DWELL_FRAGMENTED 44
#define IWL_SCAN_DWELL_EXTENDED 90
+#define IWL_SCAN_NUM_OF_FRAGS 3
+
+
+/* adaptive dwell max budget time [TU] for full scan */
+#define IWL_SCAN_ADWELL_MAX_BUDGET_FULL_SCAN 300
+/* adaptive dwell max budget time [TU] for directed scan */
+#define IWL_SCAN_ADWELL_MAX_BUDGET_DIRECTED_SCAN 100
+/* adaptive dwell default APs number */
+#define IWL_SCAN_ADWELL_DEFAULT_N_APS 2
+/* adaptive dwell default APs number in social channels (1, 6, 11) */
+#define IWL_SCAN_ADWELL_DEFAULT_N_APS_SOCIAL 10
struct iwl_mvm_scan_timing_params {
u32 suspend_time;
@@ -134,6 +146,9 @@ static inline void *iwl_mvm_get_scan_req_umac_data(struct iwl_mvm *mvm)
{
struct iwl_scan_req_umac *cmd = mvm->scan_cmd;
+ if (iwl_mvm_is_adaptive_dwell_v2_supported(mvm))
+ return (void *)&cmd->v8.data;
+
if (iwl_mvm_is_adaptive_dwell_supported(mvm))
return (void *)&cmd->v7.data;
@@ -143,6 +158,23 @@ static inline void *iwl_mvm_get_scan_req_umac_data(struct iwl_mvm *mvm)
return (void *)&cmd->v1.data;
}
+static inline struct iwl_scan_umac_chan_param *
+iwl_mvm_get_scan_req_umac_channel(struct iwl_mvm *mvm)
+{
+ struct iwl_scan_req_umac *cmd = mvm->scan_cmd;
+
+ if (iwl_mvm_is_adaptive_dwell_v2_supported(mvm))
+ return &cmd->v8.channel;
+
+ if (iwl_mvm_is_adaptive_dwell_supported(mvm))
+ return &cmd->v7.channel;
+
+ if (iwl_mvm_has_new_tx_api(mvm))
+ return &cmd->v6.channel;
+
+ return &cmd->v1.channel;
+}
+
static u8 iwl_mvm_scan_rx_ant(struct iwl_mvm *mvm)
{
if (mvm->scan_rx_ant != ANT_NONE)
@@ -1113,66 +1145,92 @@ static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm,
struct iwl_scan_req_umac *cmd,
struct iwl_mvm_scan_params *params)
{
- struct iwl_mvm_scan_timing_params *timing = &scan_timing[params->type];
+ struct iwl_mvm_scan_timing_params *timing, *hb_timing;
+ u8 active_dwell, passive_dwell;
- if (iwl_mvm_is_regular_scan(params))
- cmd->ooc_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6);
- else
- cmd->ooc_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_2);
+ timing = &scan_timing[params->type];
+ active_dwell = params->measurement_dwell ?
+ params->measurement_dwell : IWL_SCAN_DWELL_ACTIVE;
+ passive_dwell = params->measurement_dwell ?
+ params->measurement_dwell : IWL_SCAN_DWELL_PASSIVE;
if (iwl_mvm_is_adaptive_dwell_supported(mvm)) {
- if (params->measurement_dwell) {
- cmd->v7.active_dwell = params->measurement_dwell;
- cmd->v7.passive_dwell = params->measurement_dwell;
- } else {
- cmd->v7.active_dwell = IWL_SCAN_DWELL_ACTIVE;
- cmd->v7.passive_dwell = IWL_SCAN_DWELL_PASSIVE;
- }
- cmd->v7.fragmented_dwell = IWL_SCAN_DWELL_FRAGMENTED;
+ cmd->v7.adwell_default_n_aps_social =
+ IWL_SCAN_ADWELL_DEFAULT_N_APS_SOCIAL;
+ cmd->v7.adwell_default_n_aps =
+ IWL_SCAN_ADWELL_DEFAULT_N_APS;
+
+ /* if custom max budget was configured with debugfs */
+ if (IWL_MVM_ADWELL_MAX_BUDGET)
+ cmd->v7.adwell_max_budget =
+ cpu_to_le16(IWL_MVM_ADWELL_MAX_BUDGET);
+ else if (params->ssids && params->ssids[0].ssid_len)
+ cmd->v7.adwell_max_budget =
+ cpu_to_le16(IWL_SCAN_ADWELL_MAX_BUDGET_DIRECTED_SCAN);
+ else
+ cmd->v7.adwell_max_budget =
+ cpu_to_le16(IWL_SCAN_ADWELL_MAX_BUDGET_FULL_SCAN);
cmd->v7.scan_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6);
cmd->v7.max_out_time[SCAN_LB_LMAC_IDX] =
cpu_to_le32(timing->max_out_time);
cmd->v7.suspend_time[SCAN_LB_LMAC_IDX] =
cpu_to_le32(timing->suspend_time);
+
if (iwl_mvm_is_cdb_supported(mvm)) {
+ hb_timing = &scan_timing[params->type];
+
cmd->v7.max_out_time[SCAN_HB_LMAC_IDX] =
- cpu_to_le32(timing->max_out_time);
+ cpu_to_le32(hb_timing->max_out_time);
cmd->v7.suspend_time[SCAN_HB_LMAC_IDX] =
- cpu_to_le32(timing->suspend_time);
+ cpu_to_le32(hb_timing->suspend_time);
}
- return;
- }
-
- if (params->measurement_dwell) {
- cmd->v1.active_dwell = params->measurement_dwell;
- cmd->v1.passive_dwell = params->measurement_dwell;
- cmd->v1.extended_dwell = params->measurement_dwell;
+ if (!iwl_mvm_is_adaptive_dwell_v2_supported(mvm)) {
+ cmd->v7.active_dwell = active_dwell;
+ cmd->v7.passive_dwell = passive_dwell;
+ cmd->v7.fragmented_dwell = IWL_SCAN_DWELL_FRAGMENTED;
+ } else {
+ cmd->v8.active_dwell[SCAN_LB_LMAC_IDX] = active_dwell;
+ cmd->v8.passive_dwell[SCAN_LB_LMAC_IDX] = passive_dwell;
+ if (iwl_mvm_is_cdb_supported(mvm)) {
+ cmd->v8.active_dwell[SCAN_HB_LMAC_IDX] =
+ active_dwell;
+ cmd->v8.passive_dwell[SCAN_HB_LMAC_IDX] =
+ passive_dwell;
+ }
+ }
} else {
- cmd->v1.active_dwell = IWL_SCAN_DWELL_ACTIVE;
- cmd->v1.passive_dwell = IWL_SCAN_DWELL_PASSIVE;
- cmd->v1.extended_dwell = IWL_SCAN_DWELL_EXTENDED;
- }
- cmd->v1.fragmented_dwell = IWL_SCAN_DWELL_FRAGMENTED;
+ cmd->v1.extended_dwell = params->measurement_dwell ?
+ params->measurement_dwell : IWL_SCAN_DWELL_EXTENDED;
+ cmd->v1.active_dwell = active_dwell;
+ cmd->v1.passive_dwell = passive_dwell;
+ cmd->v1.fragmented_dwell = IWL_SCAN_DWELL_FRAGMENTED;
- if (iwl_mvm_has_new_tx_api(mvm)) {
- cmd->v6.scan_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6);
- cmd->v6.max_out_time[SCAN_LB_LMAC_IDX] =
- cpu_to_le32(timing->max_out_time);
- cmd->v6.suspend_time[SCAN_LB_LMAC_IDX] =
- cpu_to_le32(timing->suspend_time);
if (iwl_mvm_is_cdb_supported(mvm)) {
+ hb_timing = &scan_timing[params->type];
+
cmd->v6.max_out_time[SCAN_HB_LMAC_IDX] =
- cpu_to_le32(timing->max_out_time);
+ cpu_to_le32(hb_timing->max_out_time);
cmd->v6.suspend_time[SCAN_HB_LMAC_IDX] =
+ cpu_to_le32(hb_timing->suspend_time);
+ }
+
+ if (iwl_mvm_has_new_tx_api(mvm)) {
+ cmd->v6.scan_priority =
+ cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6);
+ cmd->v6.max_out_time[SCAN_LB_LMAC_IDX] =
+ cpu_to_le32(timing->max_out_time);
+ cmd->v6.suspend_time[SCAN_LB_LMAC_IDX] =
+ cpu_to_le32(timing->suspend_time);
+ } else {
+ cmd->v1.scan_priority =
+ cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6);
+ cmd->v1.max_out_time =
+ cpu_to_le32(timing->max_out_time);
+ cmd->v1.suspend_time =
cpu_to_le32(timing->suspend_time);
}
- } else {
- cmd->v1.max_out_time = cpu_to_le32(timing->max_out_time);
- cmd->v1.suspend_time = cpu_to_le32(timing->suspend_time);
- cmd->v1.scan_priority =
- cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6);
}
}
@@ -1234,11 +1292,39 @@ static u16 iwl_mvm_scan_umac_flags(struct iwl_mvm *mvm,
if (mvm->sched_scan_pass_all == SCHED_SCAN_PASS_ALL_ENABLED)
flags |= IWL_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE;
+ if (iwl_mvm_is_adaptive_dwell_supported(mvm) && IWL_MVM_ADWELL_ENABLE &&
+ vif->type != NL80211_IFTYPE_P2P_DEVICE)
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_ADAPTIVE_DWELL;
+
+ /*
+ * Extended dwell is relevant only for low band to start with, as it is
+ * being used for social channles only (1, 6, 11), so we can check
+ * only scan type on low band also for CDB.
+ */
if (iwl_mvm_is_regular_scan(params) &&
vif->type != NL80211_IFTYPE_P2P_DEVICE &&
- params->type != IWL_SCAN_TYPE_FRAGMENTED)
+ params->type != IWL_SCAN_TYPE_FRAGMENTED &&
+ !iwl_mvm_is_adaptive_dwell_supported(mvm) &&
+ !iwl_mvm_is_oce_supported(mvm))
flags |= IWL_UMAC_SCAN_GEN_FLAGS_EXTENDED_DWELL;
+ if (iwl_mvm_is_oce_supported(mvm)) {
+ if ((params->flags &
+ NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE))
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_PROB_REQ_HIGH_TX_RATE;
+ /* Since IWL_UMAC_SCAN_GEN_FLAGS_EXTENDED_DWELL and
+ * NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION shares
+ * the same bit, we need to make sure that we use this bit here
+ * only when IWL_UMAC_SCAN_GEN_FLAGS_EXTENDED_DWELL cannot be
+ * used. */
+ if ((params->flags &
+ NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION) &&
+ !WARN_ON_ONCE(!iwl_mvm_is_adaptive_dwell_supported(mvm)))
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_PROB_REQ_DEFER_SUPP;
+ if ((params->flags & NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME))
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_MAX_CHNL_TIME;
+ }
+
return flags;
}
@@ -1247,6 +1333,7 @@ static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
int type)
{
struct iwl_scan_req_umac *cmd = mvm->scan_cmd;
+ struct iwl_scan_umac_chan_param *chan_param;
void *cmd_data = iwl_mvm_get_scan_req_umac_data(mvm);
struct iwl_scan_req_umac_tail *sec_part = cmd_data +
sizeof(struct iwl_scan_channel_cfg_umac) *
@@ -1254,8 +1341,11 @@ static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
int uid, i;
u32 ssid_bitmap = 0;
u8 channel_flags = 0;
+ u16 gen_flags;
struct iwl_mvm_vif *scan_vif = iwl_mvm_vif_from_mac80211(vif);
+ chan_param = iwl_mvm_get_scan_req_umac_channel(mvm);
+
lockdep_assert_held(&mvm->mutex);
if (WARN_ON(params->n_scan_plans > IWL_MAX_SCHED_SCAN_PLANS))
@@ -1272,8 +1362,17 @@ static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
mvm->scan_uid_status[uid] = type;
cmd->uid = cpu_to_le32(uid);
- cmd->general_flags = cpu_to_le16(iwl_mvm_scan_umac_flags(mvm, params,
- vif));
+ gen_flags = iwl_mvm_scan_umac_flags(mvm, params, vif);
+ cmd->general_flags = cpu_to_le16(gen_flags);
+ if (iwl_mvm_is_adaptive_dwell_v2_supported(mvm)) {
+ if (gen_flags & IWL_UMAC_SCAN_GEN_FLAGS_FRAGMENTED)
+ cmd->v8.num_of_fragments[SCAN_LB_LMAC_IDX] =
+ IWL_SCAN_NUM_OF_FRAGS;
+ if (gen_flags & IWL_UMAC_SCAN_GEN_FLAGS_LMAC2_FRAGMENTED)
+ cmd->v8.num_of_fragments[SCAN_HB_LMAC_IDX] =
+ IWL_SCAN_NUM_OF_FRAGS;
+ }
+
cmd->scan_start_mac_id = scan_vif->id;
if (type == IWL_MVM_SCAN_SCHED || type == IWL_MVM_SCAN_NETDETECT)
@@ -1284,16 +1383,8 @@ static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
IWL_SCAN_CHANNEL_FLAG_CACHE_ADD;
- if (iwl_mvm_is_adaptive_dwell_supported(mvm)) {
- cmd->v7.channel_flags = channel_flags;
- cmd->v7.n_channels = params->n_channels;
- } else if (iwl_mvm_has_new_tx_api(mvm)) {
- cmd->v6.channel_flags = channel_flags;
- cmd->v6.n_channels = params->n_channels;
- } else {
- cmd->v1.channel_flags = channel_flags;
- cmd->v1.n_channels = params->n_channels;
- }
+ chan_param->flags = channel_flags;
+ chan_param->count = params->n_channels;
iwl_scan_build_ssids(params, sec_part->direct_scan, &ssid_bitmap);
@@ -1732,7 +1823,9 @@ int iwl_mvm_scan_size(struct iwl_mvm *mvm)
{
int base_size = IWL_SCAN_REQ_UMAC_SIZE_V1;
- if (iwl_mvm_is_adaptive_dwell_supported(mvm))
+ if (iwl_mvm_is_adaptive_dwell_v2_supported(mvm))
+ base_size = IWL_SCAN_REQ_UMAC_SIZE_V8;
+ else if (iwl_mvm_is_adaptive_dwell_supported(mvm))
base_size = IWL_SCAN_REQ_UMAC_SIZE_V7;
else if (iwl_mvm_has_new_tx_api(mvm))
base_size = IWL_SCAN_REQ_UMAC_SIZE_V6;
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c
index 630e23cb0ffb..80067eb9ea05 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c
@@ -1695,7 +1695,8 @@ int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm,
u32 qmask, enum nl80211_iftype iftype,
enum iwl_sta_type type)
{
- if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
+ if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) ||
+ sta->sta_id == IWL_MVM_INVALID_STA) {
sta->sta_id = iwl_mvm_find_free_sta_id(mvm, iftype);
if (WARN_ON_ONCE(sta->sta_id == IWL_MVM_INVALID_STA))
return -ENOSPC;
@@ -2478,28 +2479,12 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
/*
* Note the possible cases:
- * 1. In DQA mode with an enabled TXQ - TXQ needs to become agg'ed
- * 2. Non-DQA mode: the TXQ hasn't yet been enabled, so find a free
- * one and mark it as reserved
- * 3. In DQA mode, but no traffic yet on this TID: same treatment as in
- * non-DQA mode, since the TXQ hasn't yet been allocated
- * Don't support case 3 for new TX path as it is not expected to happen
- * and aggregation will be offloaded soon anyway
+ * 1. An enabled TXQ - TXQ needs to become agg'ed
+ * 2. The TXQ hasn't yet been enabled, so find a free one and mark
+ * it as reserved
*/
txq_id = mvmsta->tid_data[tid].txq_id;
- if (iwl_mvm_has_new_tx_api(mvm)) {
- if (txq_id == IWL_MVM_INVALID_QUEUE) {
- ret = -ENXIO;
- goto release_locks;
- }
- } else if (unlikely(mvm->queue_info[txq_id].status ==
- IWL_MVM_QUEUE_SHARED)) {
- ret = -ENXIO;
- IWL_DEBUG_TX_QUEUES(mvm,
- "Can't start tid %d agg on shared queue!\n",
- tid);
- goto release_locks;
- } else if (mvm->queue_info[txq_id].status != IWL_MVM_QUEUE_READY) {
+ if (txq_id == IWL_MVM_INVALID_QUEUE) {
txq_id = iwl_mvm_find_free_queue(mvm, mvmsta->sta_id,
IWL_MVM_DQA_MIN_DATA_QUEUE,
IWL_MVM_DQA_MAX_DATA_QUEUE);
@@ -2508,16 +2493,16 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
IWL_ERR(mvm, "Failed to allocate agg queue\n");
goto release_locks;
}
- /*
- * TXQ shouldn't be in inactive mode for non-DQA, so getting
- * an inactive queue from iwl_mvm_find_free_queue() is
- * certainly a bug
- */
- WARN_ON(mvm->queue_info[txq_id].status ==
- IWL_MVM_QUEUE_INACTIVE);
/* TXQ hasn't yet been enabled, so mark it only as reserved */
mvm->queue_info[txq_id].status = IWL_MVM_QUEUE_RESERVED;
+ } else if (unlikely(mvm->queue_info[txq_id].status ==
+ IWL_MVM_QUEUE_SHARED)) {
+ ret = -ENXIO;
+ IWL_DEBUG_TX_QUEUES(mvm,
+ "Can't start tid %d agg on shared queue!\n",
+ tid);
+ goto release_locks;
}
spin_unlock(&mvm->queue_info_lock);
@@ -2696,8 +2681,10 @@ out:
static void iwl_mvm_unreserve_agg_queue(struct iwl_mvm *mvm,
struct iwl_mvm_sta *mvmsta,
- u16 txq_id)
+ struct iwl_mvm_tid_data *tid_data)
{
+ u16 txq_id = tid_data->txq_id;
+
if (iwl_mvm_has_new_tx_api(mvm))
return;
@@ -2709,8 +2696,10 @@ static void iwl_mvm_unreserve_agg_queue(struct iwl_mvm *mvm,
* allocated through iwl_mvm_enable_txq, so we can just mark it back as
* free.
*/
- if (mvm->queue_info[txq_id].status == IWL_MVM_QUEUE_RESERVED)
+ if (mvm->queue_info[txq_id].status == IWL_MVM_QUEUE_RESERVED) {
mvm->queue_info[txq_id].status = IWL_MVM_QUEUE_FREE;
+ tid_data->txq_id = IWL_MVM_INVALID_QUEUE;
+ }
spin_unlock_bh(&mvm->queue_info_lock);
}
@@ -2741,7 +2730,7 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
mvmsta->agg_tids &= ~BIT(tid);
- iwl_mvm_unreserve_agg_queue(mvm, mvmsta, txq_id);
+ iwl_mvm_unreserve_agg_queue(mvm, mvmsta, tid_data);
switch (tid_data->state) {
case IWL_AGG_ON:
@@ -2808,7 +2797,7 @@ int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
mvmsta->agg_tids &= ~BIT(tid);
spin_unlock_bh(&mvmsta->lock);
- iwl_mvm_unreserve_agg_queue(mvm, mvmsta, txq_id);
+ iwl_mvm_unreserve_agg_queue(mvm, mvmsta, tid_data);
if (old_state >= IWL_AGG_ON) {
iwl_mvm_drain_sta(mvm, mvmsta, true);
@@ -3233,17 +3222,9 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
}
sta_id = mvm_sta->sta_id;
- if (keyconf->cipher == WLAN_CIPHER_SUITE_AES_CMAC ||
- keyconf->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 ||
- keyconf->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256) {
- ret = iwl_mvm_send_sta_igtk(mvm, keyconf, sta_id,
- false);
- goto end;
- }
-
/*
* It is possible that the 'sta' parameter is NULL, and thus
- * there is a need to retrieve the sta from the local station
+ * there is a need to retrieve the sta from the local station
* table.
*/
if (!sta) {
@@ -3258,6 +3239,17 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
if (WARN_ON_ONCE(iwl_mvm_sta_from_mac80211(sta)->vif != vif))
return -EINVAL;
+ } else {
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ sta_id = mvmvif->mcast_sta.sta_id;
+ }
+
+ if (keyconf->cipher == WLAN_CIPHER_SUITE_AES_CMAC ||
+ keyconf->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 ||
+ keyconf->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256) {
+ ret = iwl_mvm_send_sta_igtk(mvm, keyconf, sta_id, false);
+ goto end;
}
/* If the key_offset is not pre-assigned, we need to find a
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c
index acb217e666db..cd91bc44259c 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c
@@ -8,6 +8,7 @@
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
* Copyright(c) 2017 Intel Deutschland GmbH
+ * Copyright(c) 2018 Intel Corporation
*
* 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
@@ -18,11 +19,6 @@
* 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,
- * USA
- *
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
@@ -35,6 +31,7 @@
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
* Copyright(c) 2017 Intel Deutschland GmbH
+ * Copyright(c) 2018 Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -198,9 +195,13 @@ static bool iwl_mvm_te_check_disconnect(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
const char *errmsg)
{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
if (vif->type != NL80211_IFTYPE_STATION)
return false;
- if (vif->bss_conf.assoc && vif->bss_conf.dtim_period)
+
+ if (!mvmvif->csa_bcn_pending && vif->bss_conf.assoc &&
+ vif->bss_conf.dtim_period)
return false;
if (errmsg)
IWL_ERR(mvm, "%s\n", errmsg);
@@ -344,7 +345,7 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
* and know the dtim period.
*/
iwl_mvm_te_check_disconnect(mvm, te_data->vif,
- "No association and the time event is over already...");
+ "No beacon heard and the time event is over already...");
break;
default:
break;
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
index af6dfceab6b8..795065974d78 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
@@ -687,6 +687,74 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb)
}
#ifdef CONFIG_INET
+
+static int
+iwl_mvm_tx_tso_segment(struct sk_buff *skb, unsigned int num_subframes,
+ netdev_features_t netdev_flags,
+ struct sk_buff_head *mpdus_skb)
+{
+ struct sk_buff *tmp, *next;
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+ char cb[sizeof(skb->cb)];
+ u16 i = 0;
+ unsigned int tcp_payload_len;
+ unsigned int mss = skb_shinfo(skb)->gso_size;
+ bool ipv4 = (skb->protocol == htons(ETH_P_IP));
+ u16 ip_base_id = ipv4 ? ntohs(ip_hdr(skb)->id) : 0;
+
+ skb_shinfo(skb)->gso_size = num_subframes * mss;
+ memcpy(cb, skb->cb, sizeof(cb));
+
+ next = skb_gso_segment(skb, netdev_flags);
+ skb_shinfo(skb)->gso_size = mss;
+ if (WARN_ON_ONCE(IS_ERR(next)))
+ return -EINVAL;
+ else if (next)
+ consume_skb(skb);
+
+ while (next) {
+ tmp = next;
+ next = tmp->next;
+
+ memcpy(tmp->cb, cb, sizeof(tmp->cb));
+ /*
+ * Compute the length of all the data added for the A-MSDU.
+ * This will be used to compute the length to write in the TX
+ * command. We have: SNAP + IP + TCP for n -1 subframes and
+ * ETH header for n subframes.
+ */
+ tcp_payload_len = skb_tail_pointer(tmp) -
+ skb_transport_header(tmp) -
+ tcp_hdrlen(tmp) + tmp->data_len;
+
+ if (ipv4)
+ ip_hdr(tmp)->id = htons(ip_base_id + i * num_subframes);
+
+ if (tcp_payload_len > mss) {
+ skb_shinfo(tmp)->gso_size = mss;
+ } else {
+ if (ieee80211_is_data_qos(hdr->frame_control)) {
+ u8 *qc;
+
+ if (ipv4)
+ ip_send_check(ip_hdr(tmp));
+
+ qc = ieee80211_get_qos_ctl((void *)tmp->data);
+ *qc &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
+ }
+ skb_shinfo(tmp)->gso_size = 0;
+ }
+
+ tmp->prev = NULL;
+ tmp->next = NULL;
+
+ __skb_queue_tail(mpdus_skb, tmp);
+ i++;
+ }
+
+ return 0;
+}
+
static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb,
struct ieee80211_tx_info *info,
struct ieee80211_sta *sta,
@@ -695,14 +763,10 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb,
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
struct ieee80211_hdr *hdr = (void *)skb->data;
unsigned int mss = skb_shinfo(skb)->gso_size;
- struct sk_buff *tmp, *next;
- char cb[sizeof(skb->cb)];
unsigned int num_subframes, tcp_payload_len, subf_len, max_amsdu_len;
- bool ipv4 = (skb->protocol == htons(ETH_P_IP));
- u16 ip_base_id = ipv4 ? ntohs(ip_hdr(skb)->id) : 0;
- u16 snap_ip_tcp, pad, i = 0;
+ u16 snap_ip_tcp, pad;
unsigned int dbg_max_amsdu_len;
- netdev_features_t netdev_features = NETIF_F_CSUM_MASK | NETIF_F_SG;
+ netdev_features_t netdev_flags = NETIF_F_CSUM_MASK | NETIF_F_SG;
u8 *qc, tid, txf;
snap_ip_tcp = 8 + skb_transport_header(skb) - skb_network_header(skb) +
@@ -712,16 +776,8 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb,
if (!sta->max_amsdu_len ||
!ieee80211_is_data_qos(hdr->frame_control) ||
- (!mvmsta->tlc_amsdu && !dbg_max_amsdu_len)) {
- num_subframes = 1;
- pad = 0;
- goto segment;
- }
-
- qc = ieee80211_get_qos_ctl(hdr);
- tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
- if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT))
- return -EINVAL;
+ (!mvmsta->tlc_amsdu && !dbg_max_amsdu_len))
+ return iwl_mvm_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb);
/*
* Do not build AMSDU for IPv6 with extension headers.
@@ -730,22 +786,22 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb,
if (skb->protocol == htons(ETH_P_IPV6) &&
((struct ipv6hdr *)skb_network_header(skb))->nexthdr !=
IPPROTO_TCP) {
- num_subframes = 1;
- pad = 0;
- netdev_features &= ~NETIF_F_CSUM_MASK;
- goto segment;
+ netdev_flags &= ~NETIF_F_CSUM_MASK;
+ return iwl_mvm_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb);
}
+ qc = ieee80211_get_qos_ctl(hdr);
+ tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
+ if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT))
+ return -EINVAL;
+
/*
* No need to lock amsdu_in_ampdu_allowed since it can't be modified
* during an BA session.
*/
if (info->flags & IEEE80211_TX_CTL_AMPDU &&
- !mvmsta->tid_data[tid].amsdu_in_ampdu_allowed) {
- num_subframes = 1;
- pad = 0;
- goto segment;
- }
+ !mvmsta->tid_data[tid].amsdu_in_ampdu_allowed)
+ return iwl_mvm_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb);
max_amsdu_len = sta->max_amsdu_len;
@@ -811,56 +867,8 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb,
* Trick the segmentation function to make it
* create SKBs that can fit into one A-MSDU.
*/
-segment:
- skb_shinfo(skb)->gso_size = num_subframes * mss;
- memcpy(cb, skb->cb, sizeof(cb));
-
- next = skb_gso_segment(skb, netdev_features);
- skb_shinfo(skb)->gso_size = mss;
- if (WARN_ON_ONCE(IS_ERR(next)))
- return -EINVAL;
- else if (next)
- consume_skb(skb);
-
- while (next) {
- tmp = next;
- next = tmp->next;
-
- memcpy(tmp->cb, cb, sizeof(tmp->cb));
- /*
- * Compute the length of all the data added for the A-MSDU.
- * This will be used to compute the length to write in the TX
- * command. We have: SNAP + IP + TCP for n -1 subframes and
- * ETH header for n subframes.
- */
- tcp_payload_len = skb_tail_pointer(tmp) -
- skb_transport_header(tmp) -
- tcp_hdrlen(tmp) + tmp->data_len;
-
- if (ipv4)
- ip_hdr(tmp)->id = htons(ip_base_id + i * num_subframes);
-
- if (tcp_payload_len > mss) {
- skb_shinfo(tmp)->gso_size = mss;
- } else {
- if (ieee80211_is_data_qos(hdr->frame_control)) {
- qc = ieee80211_get_qos_ctl((void *)tmp->data);
-
- if (ipv4)
- ip_send_check(ip_hdr(tmp));
- *qc &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
- }
- skb_shinfo(tmp)->gso_size = 0;
- }
-
- tmp->prev = NULL;
- tmp->next = NULL;
-
- __skb_queue_tail(mpdus_skb, tmp);
- i++;
- }
-
- return 0;
+ return iwl_mvm_tx_tso_segment(skb, num_subframes, netdev_flags,
+ mpdus_skb);
}
#else /* CONFIG_INET */
static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb,
@@ -1894,14 +1902,12 @@ int iwl_mvm_flush_sta(struct iwl_mvm *mvm, void *sta, bool internal, u32 flags)
struct iwl_mvm_int_sta *int_sta = sta;
struct iwl_mvm_sta *mvm_sta = sta;
- if (iwl_mvm_has_new_tx_api(mvm)) {
- if (internal)
- return iwl_mvm_flush_sta_tids(mvm, int_sta->sta_id,
- BIT(IWL_MGMT_TID), flags);
+ BUILD_BUG_ON(offsetof(struct iwl_mvm_int_sta, sta_id) !=
+ offsetof(struct iwl_mvm_sta, sta_id));
+ if (iwl_mvm_has_new_tx_api(mvm))
return iwl_mvm_flush_sta_tids(mvm, mvm_sta->sta_id,
- 0xFF, flags);
- }
+ 0xff | BIT(IWL_MGMT_TID), flags);
if (internal)
return iwl_mvm_flush_tx_path(mvm, int_sta->tfd_queue_msk,
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
index d65e1db7c097..d99d9ea78e4c 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
@@ -549,12 +549,7 @@ static void iwl_mvm_dump_lmac_error_log(struct iwl_mvm *mvm, u32 base)
IWL_ERR(mvm, "Loaded firmware version: %s\n", mvm->fw->fw_version);
- trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low,
- table.data1, table.data2, table.data3,
- table.blink2, table.ilink1,
- table.ilink2, table.bcon_time, table.gp1,
- table.gp2, table.fw_rev_type, table.major,
- table.minor, table.hw_ver, table.brd_ver);
+ trace_iwlwifi_dev_ucode_error(trans->dev, &table, table.hw_ver, table.brd_ver);
IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id,
desc_lookup(table.error_id));
IWL_ERR(mvm, "0x%08X | trm_hw_status0\n", table.trm_hw_status0);
@@ -800,12 +795,19 @@ int iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue,
.scd_queue = queue,
.action = SCD_CFG_DISABLE_QUEUE,
};
- bool remove_mac_queue = true;
+ bool remove_mac_queue = mac80211_queue != IEEE80211_INVAL_HW_QUEUE;
int ret;
+ if (WARN_ON(remove_mac_queue && mac80211_queue >= IEEE80211_MAX_QUEUES))
+ return -EINVAL;
+
if (iwl_mvm_has_new_tx_api(mvm)) {
spin_lock_bh(&mvm->queue_info_lock);
- mvm->hw_queue_to_mac80211[queue] &= ~BIT(mac80211_queue);
+
+ if (remove_mac_queue)
+ mvm->hw_queue_to_mac80211[queue] &=
+ ~BIT(mac80211_queue);
+
spin_unlock_bh(&mvm->queue_info_lock);
iwl_trans_txq_free(mvm->trans, queue);
@@ -1027,14 +1029,18 @@ bool iwl_mvm_rx_diversity_allowed(struct iwl_mvm *mvm)
}
int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- bool prev)
+ bool low_latency,
+ enum iwl_mvm_low_latency_cause cause)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
int res;
- bool low_latency;
+ bool prev;
lockdep_assert_held(&mvm->mutex);
+ prev = iwl_mvm_vif_low_latency(mvmvif);
+ iwl_mvm_vif_set_low_latency(mvmvif, low_latency, cause);
+
low_latency = iwl_mvm_vif_low_latency(mvmvif);
if (low_latency == prev)
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
index 56fc28750a41..959de2f8bb28 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
@@ -8,6 +8,7 @@
* Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
* Copyright(c) 2016-2017 Intel Deutschland GmbH
+ * Copyright(c) 2018 Intel Corporation
*
* 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
@@ -36,6 +37,7 @@
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
* All rights reserved.
* Copyright(c) 2017 Intel Deutschland GmbH
+ * Copyright(c) 2018 Intel Corporation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -517,9 +519,9 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
{IWL_PCI_DEVICE(0x24FD, 0x9074, iwl8265_2ac_cfg)},
/* 9000 Series */
- {IWL_PCI_DEVICE(0x2526, 0x0000, iwl9260_2ac_cfg)},
{IWL_PCI_DEVICE(0x2526, 0x0010, iwl9260_2ac_cfg)},
{IWL_PCI_DEVICE(0x2526, 0x0014, iwl9260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x2526, 0x0018, iwl9260_2ac_cfg)},
{IWL_PCI_DEVICE(0x2526, 0x0030, iwl9560_2ac_cfg)},
{IWL_PCI_DEVICE(0x2526, 0x0034, iwl9560_2ac_cfg)},
{IWL_PCI_DEVICE(0x2526, 0x0038, iwl9560_2ac_cfg)},
@@ -544,11 +546,15 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
{IWL_PCI_DEVICE(0x2526, 0x1410, iwl9270_2ac_cfg)},
{IWL_PCI_DEVICE(0x2526, 0x1420, iwl9460_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0x2526, 0x1610, iwl9270_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x2526, 0x2030, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x2526, 0x2034, iwl9560_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0x2526, 0x4010, iwl9260_2ac_cfg)},
{IWL_PCI_DEVICE(0x2526, 0x4030, iwl9560_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x2526, 0x4034, iwl9560_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0x2526, 0x40A4, iwl9460_2ac_cfg)},
- {IWL_PCI_DEVICE(0x2526, 0xA014, iwl9260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x2526, 0x4234, iwl9560_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0x2526, 0x42A4, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x2526, 0xA014, iwl9260_2ac_cfg)},
{IWL_PCI_DEVICE(0x271B, 0x0010, iwl9160_2ac_cfg)},
{IWL_PCI_DEVICE(0x271B, 0x0014, iwl9160_2ac_cfg)},
{IWL_PCI_DEVICE(0x271B, 0x0210, iwl9160_2ac_cfg)},
@@ -569,38 +575,146 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
{IWL_PCI_DEVICE(0x2720, 0x0264, iwl9461_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0x2720, 0x02A0, iwl9462_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0x2720, 0x02A4, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x2720, 0x1010, iwl9260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x2720, 0x1030, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x2720, 0x1210, iwl9260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x2720, 0x2030, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x2720, 0x2034, iwl9560_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0x2720, 0x4030, iwl9560_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x2720, 0x4034, iwl9560_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0x2720, 0x40A4, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x2720, 0x4234, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x2720, 0x42A4, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x30DC, 0x0030, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x30DC, 0x0034, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x30DC, 0x0038, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x30DC, 0x003C, iwl9560_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0x30DC, 0x0060, iwl9460_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0x30DC, 0x0064, iwl9461_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0x30DC, 0x00A0, iwl9462_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0x30DC, 0x00A4, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x30DC, 0x0230, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x30DC, 0x0234, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x30DC, 0x0238, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x30DC, 0x023C, iwl9560_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0x30DC, 0x0260, iwl9461_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0x30DC, 0x0264, iwl9461_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0x30DC, 0x02A0, iwl9462_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0x30DC, 0x02A4, iwl9462_2ac_cfg_soc)},
- {IWL_PCI_DEVICE(0x31DC, 0x0030, iwl9560_2ac_cfg_soc)},
- {IWL_PCI_DEVICE(0x31DC, 0x0034, iwl9560_2ac_cfg_soc)},
- {IWL_PCI_DEVICE(0x31DC, 0x0038, iwl9560_2ac_cfg_soc)},
- {IWL_PCI_DEVICE(0x31DC, 0x003C, iwl9560_2ac_cfg_soc)},
- {IWL_PCI_DEVICE(0x31DC, 0x0060, iwl9460_2ac_cfg_soc)},
- {IWL_PCI_DEVICE(0x31DC, 0x0064, iwl9461_2ac_cfg_soc)},
- {IWL_PCI_DEVICE(0x31DC, 0x00A0, iwl9462_2ac_cfg_soc)},
- {IWL_PCI_DEVICE(0x31DC, 0x00A4, iwl9462_2ac_cfg_soc)},
- {IWL_PCI_DEVICE(0x31DC, 0x0230, iwl9560_2ac_cfg_soc)},
- {IWL_PCI_DEVICE(0x31DC, 0x0234, iwl9560_2ac_cfg_soc)},
- {IWL_PCI_DEVICE(0x31DC, 0x0238, iwl9560_2ac_cfg_soc)},
- {IWL_PCI_DEVICE(0x31DC, 0x023C, iwl9560_2ac_cfg_soc)},
- {IWL_PCI_DEVICE(0x31DC, 0x0260, iwl9461_2ac_cfg_soc)},
- {IWL_PCI_DEVICE(0x31DC, 0x0264, iwl9461_2ac_cfg_soc)},
- {IWL_PCI_DEVICE(0x31DC, 0x02A0, iwl9462_2ac_cfg_soc)},
- {IWL_PCI_DEVICE(0x31DC, 0x02A4, iwl9462_2ac_cfg_soc)},
- {IWL_PCI_DEVICE(0x31DC, 0x4030, iwl9560_2ac_cfg_soc)},
- {IWL_PCI_DEVICE(0x31DC, 0x4034, iwl9560_2ac_cfg_soc)},
- {IWL_PCI_DEVICE(0x31DC, 0x40A4, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x30DC, 0x1010, iwl9260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x30DC, 0x1030, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x30DC, 0x1210, iwl9260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x30DC, 0x2030, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x30DC, 0x2034, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x30DC, 0x4030, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x30DC, 0x4034, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x30DC, 0x40A4, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x30DC, 0x4234, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x30DC, 0x42A4, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x31DC, 0x0030, iwl9560_2ac_cfg_shared_clk)},
+ {IWL_PCI_DEVICE(0x31DC, 0x0034, iwl9560_2ac_cfg_shared_clk)},
+ {IWL_PCI_DEVICE(0x31DC, 0x0038, iwl9560_2ac_cfg_shared_clk)},
+ {IWL_PCI_DEVICE(0x31DC, 0x003C, iwl9560_2ac_cfg_shared_clk)},
+ {IWL_PCI_DEVICE(0x31DC, 0x0060, iwl9460_2ac_cfg_shared_clk)},
+ {IWL_PCI_DEVICE(0x31DC, 0x0064, iwl9461_2ac_cfg_shared_clk)},
+ {IWL_PCI_DEVICE(0x31DC, 0x00A0, iwl9462_2ac_cfg_shared_clk)},
+ {IWL_PCI_DEVICE(0x31DC, 0x00A4, iwl9462_2ac_cfg_shared_clk)},
+ {IWL_PCI_DEVICE(0x31DC, 0x0230, iwl9560_2ac_cfg_shared_clk)},
+ {IWL_PCI_DEVICE(0x31DC, 0x0234, iwl9560_2ac_cfg_shared_clk)},
+ {IWL_PCI_DEVICE(0x31DC, 0x0238, iwl9560_2ac_cfg_shared_clk)},
+ {IWL_PCI_DEVICE(0x31DC, 0x023C, iwl9560_2ac_cfg_shared_clk)},
+ {IWL_PCI_DEVICE(0x31DC, 0x0260, iwl9461_2ac_cfg_shared_clk)},
+ {IWL_PCI_DEVICE(0x31DC, 0x0264, iwl9461_2ac_cfg_shared_clk)},
+ {IWL_PCI_DEVICE(0x31DC, 0x02A0, iwl9462_2ac_cfg_shared_clk)},
+ {IWL_PCI_DEVICE(0x31DC, 0x02A4, iwl9462_2ac_cfg_shared_clk)},
+ {IWL_PCI_DEVICE(0x31DC, 0x1010, iwl9260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x31DC, 0x1030, iwl9560_2ac_cfg_shared_clk)},
+ {IWL_PCI_DEVICE(0x31DC, 0x1210, iwl9260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x31DC, 0x2030, iwl9560_2ac_cfg_shared_clk)},
+ {IWL_PCI_DEVICE(0x31DC, 0x2034, iwl9560_2ac_cfg_shared_clk)},
+ {IWL_PCI_DEVICE(0x31DC, 0x4030, iwl9560_2ac_cfg_shared_clk)},
+ {IWL_PCI_DEVICE(0x31DC, 0x4034, iwl9560_2ac_cfg_shared_clk)},
+ {IWL_PCI_DEVICE(0x31DC, 0x40A4, iwl9462_2ac_cfg_shared_clk)},
+ {IWL_PCI_DEVICE(0x31DC, 0x4234, iwl9560_2ac_cfg_shared_clk)},
+ {IWL_PCI_DEVICE(0x31DC, 0x42A4, iwl9462_2ac_cfg_shared_clk)},
{IWL_PCI_DEVICE(0x34F0, 0x0030, iwl9560_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0x34F0, 0x0034, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x34F0, 0x0038, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x34F0, 0x003C, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x34F0, 0x0060, iwl9461_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x34F0, 0x0064, iwl9461_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x34F0, 0x00A0, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x34F0, 0x00A4, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x34F0, 0x0230, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x34F0, 0x0234, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x34F0, 0x0238, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x34F0, 0x023C, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x34F0, 0x0260, iwl9461_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x34F0, 0x0264, iwl9461_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x34F0, 0x02A0, iwl9462_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0x34F0, 0x02A4, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x34F0, 0x1010, iwl9260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x34F0, 0x1030, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x34F0, 0x1210, iwl9260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x34F0, 0x2030, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x34F0, 0x2034, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x34F0, 0x4030, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x34F0, 0x4034, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x34F0, 0x40A4, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x34F0, 0x4234, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x34F0, 0x42A4, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x3DF0, 0x0030, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x3DF0, 0x0034, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x3DF0, 0x0038, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x3DF0, 0x003C, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x3DF0, 0x0060, iwl9461_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x3DF0, 0x0064, iwl9461_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x3DF0, 0x00A0, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x3DF0, 0x00A4, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x3DF0, 0x0230, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x3DF0, 0x0234, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x3DF0, 0x0238, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x3DF0, 0x023C, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x3DF0, 0x0260, iwl9461_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x3DF0, 0x0264, iwl9461_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x3DF0, 0x02A0, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x3DF0, 0x02A4, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x3DF0, 0x1010, iwl9260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x3DF0, 0x1030, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x3DF0, 0x1210, iwl9260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x3DF0, 0x2030, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x3DF0, 0x2034, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x3DF0, 0x4030, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x3DF0, 0x4034, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x3DF0, 0x40A4, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x3DF0, 0x4234, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x3DF0, 0x42A4, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x43F0, 0x0030, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x43F0, 0x0034, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x43F0, 0x0038, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x43F0, 0x003C, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x43F0, 0x0060, iwl9461_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x43F0, 0x0064, iwl9461_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x43F0, 0x00A0, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x43F0, 0x00A4, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x43F0, 0x0230, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x43F0, 0x0234, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x43F0, 0x0238, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x43F0, 0x023C, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x43F0, 0x0260, iwl9461_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x43F0, 0x0264, iwl9461_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x43F0, 0x02A0, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x43F0, 0x02A4, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x43F0, 0x1010, iwl9260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x43F0, 0x1030, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x43F0, 0x1210, iwl9260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x43F0, 0x2030, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x43F0, 0x2034, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x43F0, 0x4030, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x43F0, 0x4034, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x43F0, 0x40A4, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x43F0, 0x4234, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x43F0, 0x42A4, iwl9462_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0x9DF0, 0x0000, iwl9460_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0x9DF0, 0x0010, iwl9460_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0x9DF0, 0x0030, iwl9560_2ac_cfg_soc)},
@@ -626,11 +740,44 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
{IWL_PCI_DEVICE(0x9DF0, 0x0610, iwl9460_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0x9DF0, 0x0710, iwl9460_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0x9DF0, 0x0A10, iwl9460_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x9DF0, 0x1010, iwl9260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x9DF0, 0x1030, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x9DF0, 0x1210, iwl9260_2ac_cfg)},
{IWL_PCI_DEVICE(0x9DF0, 0x2010, iwl9460_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x9DF0, 0x2030, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x9DF0, 0x2034, iwl9560_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0x9DF0, 0x2A10, iwl9460_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0x9DF0, 0x4030, iwl9560_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0x9DF0, 0x4034, iwl9560_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0x9DF0, 0x40A4, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x9DF0, 0x4234, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0x9DF0, 0x42A4, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0xA0F0, 0x0030, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0xA0F0, 0x0034, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0xA0F0, 0x0038, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0xA0F0, 0x003C, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0xA0F0, 0x0060, iwl9461_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0xA0F0, 0x0064, iwl9461_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0xA0F0, 0x00A0, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0xA0F0, 0x00A4, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0xA0F0, 0x0230, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0xA0F0, 0x0234, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0xA0F0, 0x0238, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0xA0F0, 0x023C, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0xA0F0, 0x0260, iwl9461_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0xA0F0, 0x0264, iwl9461_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0xA0F0, 0x02A0, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0xA0F0, 0x02A4, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0xA0F0, 0x1010, iwl9260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0xA0F0, 0x1030, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0xA0F0, 0x1210, iwl9260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0xA0F0, 0x2030, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0xA0F0, 0x2034, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0xA0F0, 0x4030, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0xA0F0, 0x4034, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0xA0F0, 0x40A4, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0xA0F0, 0x4234, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0xA0F0, 0x42A4, iwl9462_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0xA370, 0x0030, iwl9560_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0xA370, 0x0034, iwl9560_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0xA370, 0x0038, iwl9560_2ac_cfg_soc)},
@@ -647,10 +794,16 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
{IWL_PCI_DEVICE(0xA370, 0x0264, iwl9461_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0xA370, 0x02A0, iwl9462_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0xA370, 0x02A4, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0xA370, 0x1010, iwl9260_2ac_cfg)},
{IWL_PCI_DEVICE(0xA370, 0x1030, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0xA370, 0x1210, iwl9260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0xA370, 0x2030, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0xA370, 0x2034, iwl9560_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0xA370, 0x4030, iwl9560_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0xA370, 0x4034, iwl9560_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0xA370, 0x40A4, iwl9462_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0xA370, 0x4234, iwl9560_2ac_cfg_soc)},
+ {IWL_PCI_DEVICE(0xA370, 0x42A4, iwl9462_2ac_cfg_soc)},
/* 22000 Series */
{IWL_PCI_DEVICE(0x2720, 0x0A10, iwl22000_2ac_cfg_hr_cdb)},
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
index b406b536c850..f8a0234d332c 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
@@ -2616,12 +2616,12 @@ int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans)
{
struct dentry *dir = trans->dbgfs_dir;
- DEBUGFS_ADD_FILE(rx_queue, dir, S_IRUSR);
- DEBUGFS_ADD_FILE(tx_queue, dir, S_IRUSR);
- DEBUGFS_ADD_FILE(interrupt, dir, S_IWUSR | S_IRUSR);
- DEBUGFS_ADD_FILE(csr, dir, S_IWUSR);
- DEBUGFS_ADD_FILE(fh_reg, dir, S_IRUSR);
- DEBUGFS_ADD_FILE(rfkill, dir, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(rx_queue, dir, 0400);
+ DEBUGFS_ADD_FILE(tx_queue, dir, 0400);
+ DEBUGFS_ADD_FILE(interrupt, dir, 0600);
+ DEBUGFS_ADD_FILE(csr, dir, 0200);
+ DEBUGFS_ADD_FILE(fh_reg, dir, 0400);
+ DEBUGFS_ADD_FILE(rfkill, dir, 0600);
return 0;
err:
diff --git a/drivers/net/wireless/intersil/Kconfig b/drivers/net/wireless/intersil/Kconfig
index 9da136049955..e89fce1d4f27 100644
--- a/drivers/net/wireless/intersil/Kconfig
+++ b/drivers/net/wireless/intersil/Kconfig
@@ -5,8 +5,8 @@ config WLAN_VENDOR_INTERSIL
If you have a wireless card belonging to this class, say Y.
Note that the answer to this question doesn't directly affect the
- kernel: saying N will just cause the configurator to skip all
- the questions about cards. If you say Y, you will be asked for
+ kernel: saying N will just cause the configurator to skip all the
+ questions about these cards. If you say Y, you will be asked for
your specific card in the following questions.
if WLAN_VENDOR_INTERSIL
diff --git a/drivers/net/wireless/intersil/p54/main.c b/drivers/net/wireless/intersil/p54/main.c
index ab6d39e12069..1c6d428515a4 100644
--- a/drivers/net/wireless/intersil/p54/main.c
+++ b/drivers/net/wireless/intersil/p54/main.c
@@ -27,7 +27,7 @@
#include "lmac.h"
static bool modparam_nohwcrypt;
-module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO);
+module_param_named(nohwcrypt, modparam_nohwcrypt, bool, 0444);
MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>");
MODULE_DESCRIPTION("Softmac Prism54 common code");
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 35b21f8152bb..96d26cfae90b 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -253,7 +253,7 @@ static inline void hwsim_clear_chanctx_magic(struct ieee80211_chanctx_conf *c)
static unsigned int hwsim_net_id;
-static int hwsim_netgroup;
+static DEFINE_IDA(hwsim_netgroup_ida);
struct hwsim_net {
int netgroup;
@@ -267,11 +267,13 @@ static inline int hwsim_net_get_netgroup(struct net *net)
return hwsim_net->netgroup;
}
-static inline void hwsim_net_set_netgroup(struct net *net)
+static inline int hwsim_net_set_netgroup(struct net *net)
{
struct hwsim_net *hwsim_net = net_generic(net, hwsim_net_id);
- hwsim_net->netgroup = hwsim_netgroup++;
+ hwsim_net->netgroup = ida_simple_get(&hwsim_netgroup_ida,
+ 0, 0, GFP_KERNEL);
+ return hwsim_net->netgroup >= 0 ? 0 : -ENOMEM;
}
static inline u32 hwsim_net_get_wmediumd(struct net *net)
@@ -493,6 +495,7 @@ static LIST_HEAD(hwsim_radios);
static struct workqueue_struct *hwsim_wq;
static struct rhashtable hwsim_radios_rht;
static int hwsim_radio_idx;
+static int hwsim_radios_generation = 1;
static struct platform_driver mac80211_hwsim_driver = {
.driver = {
@@ -637,6 +640,7 @@ static const struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
[HWSIM_ATTR_RADIO_NAME] = { .type = NLA_STRING },
[HWSIM_ATTR_NO_VIF] = { .type = NLA_FLAG },
[HWSIM_ATTR_FREQ] = { .type = NLA_U32 },
+ [HWSIM_ATTR_PERM_ADDR] = { .type = NLA_UNSPEC, .len = ETH_ALEN },
};
static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
@@ -2408,6 +2412,7 @@ struct hwsim_new_radio_params {
bool destroy_on_close;
const char *hwname;
bool no_vif;
+ const u8 *perm_addr;
};
static void hwsim_mcast_config_msg(struct sk_buff *mcast_skb,
@@ -2572,15 +2577,25 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
skb_queue_head_init(&data->pending);
SET_IEEE80211_DEV(hw, data->dev);
- eth_zero_addr(addr);
- addr[0] = 0x02;
- addr[3] = idx >> 8;
- addr[4] = idx;
- memcpy(data->addresses[0].addr, addr, ETH_ALEN);
- memcpy(data->addresses[1].addr, addr, ETH_ALEN);
- data->addresses[1].addr[0] |= 0x40;
- hw->wiphy->n_addresses = 2;
- hw->wiphy->addresses = data->addresses;
+ if (!param->perm_addr) {
+ eth_zero_addr(addr);
+ addr[0] = 0x02;
+ addr[3] = idx >> 8;
+ addr[4] = idx;
+ memcpy(data->addresses[0].addr, addr, ETH_ALEN);
+ /* Why need here second address ? */
+ memcpy(data->addresses[1].addr, addr, ETH_ALEN);
+ data->addresses[1].addr[0] |= 0x40;
+ hw->wiphy->n_addresses = 2;
+ hw->wiphy->addresses = data->addresses;
+ /* possible address clash is checked at hash table insertion */
+ } else {
+ memcpy(data->addresses[0].addr, param->perm_addr, ETH_ALEN);
+ /* compatibility with automatically generated mac addr */
+ memcpy(data->addresses[1].addr, param->perm_addr, ETH_ALEN);
+ hw->wiphy->n_addresses = 2;
+ hw->wiphy->addresses = data->addresses;
+ }
data->channels = param->channels;
data->use_chanctx = param->use_chanctx;
@@ -2786,13 +2801,17 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
err = rhashtable_insert_fast(&hwsim_radios_rht, &data->rht,
hwsim_rht_params);
if (err < 0) {
- pr_debug("mac80211_hwsim: radio index %d already present\n",
- idx);
+ if (info) {
+ GENL_SET_ERR_MSG(info, "perm addr already present");
+ NL_SET_BAD_ATTR(info->extack,
+ info->attrs[HWSIM_ATTR_PERM_ADDR]);
+ }
spin_unlock_bh(&hwsim_radio_lock);
goto failed_final_insert;
}
list_add_tail(&data->list, &hwsim_radios);
+ hwsim_radios_generation++;
spin_unlock_bh(&hwsim_radio_lock);
if (idx > 0)
@@ -3211,6 +3230,19 @@ static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
param.regd = hwsim_world_regdom_custom[idx];
}
+ if (info->attrs[HWSIM_ATTR_PERM_ADDR]) {
+ if (!is_valid_ether_addr(
+ nla_data(info->attrs[HWSIM_ATTR_PERM_ADDR]))) {
+ GENL_SET_ERR_MSG(info,"MAC is no valid source addr");
+ NL_SET_BAD_ATTR(info->extack,
+ info->attrs[HWSIM_ATTR_PERM_ADDR]);
+ return -EINVAL;
+ }
+
+
+ param.perm_addr = nla_data(info->attrs[HWSIM_ATTR_PERM_ADDR]);
+ }
+
ret = mac80211_hwsim_new_radio(info, &param);
kfree(hwname);
return ret;
@@ -3250,6 +3282,7 @@ static int hwsim_del_radio_nl(struct sk_buff *msg, struct genl_info *info)
list_del(&data->list);
rhashtable_remove_fast(&hwsim_radios_rht, &data->rht,
hwsim_rht_params);
+ hwsim_radios_generation++;
spin_unlock_bh(&hwsim_radio_lock);
mac80211_hwsim_del_radio(data, wiphy_name(data->hw->wiphy),
info);
@@ -3306,17 +3339,19 @@ out_err:
static int hwsim_dump_radio_nl(struct sk_buff *skb,
struct netlink_callback *cb)
{
- int idx = cb->args[0];
+ int last_idx = cb->args[0];
struct mac80211_hwsim_data *data = NULL;
- int res;
+ int res = 0;
+ void *hdr;
spin_lock_bh(&hwsim_radio_lock);
+ cb->seq = hwsim_radios_generation;
- if (idx == hwsim_radio_idx)
+ if (last_idx >= hwsim_radio_idx-1)
goto done;
list_for_each_entry(data, &hwsim_radios, list) {
- if (data->idx < idx)
+ if (data->idx <= last_idx)
continue;
if (!net_eq(wiphy_net(data->hw->wiphy), sock_net(skb->sk)))
@@ -3329,14 +3364,25 @@ static int hwsim_dump_radio_nl(struct sk_buff *skb,
if (res < 0)
break;
- idx = data->idx + 1;
+ last_idx = data->idx;
}
- cb->args[0] = idx;
+ cb->args[0] = last_idx;
+
+ /* list changed, but no new element sent, set interrupted flag */
+ if (skb->len == 0 && cb->prev_seq && cb->seq != cb->prev_seq) {
+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq, &hwsim_genl_family,
+ NLM_F_MULTI, HWSIM_CMD_GET_RADIO);
+ if (!hdr)
+ res = -EMSGSIZE;
+ genl_dump_check_consistent(cb, hdr);
+ genlmsg_end(skb, hdr);
+ }
done:
spin_unlock_bh(&hwsim_radio_lock);
- return skb->len;
+ return res ?: skb->len;
}
/* Generic Netlink operations array */
@@ -3394,6 +3440,7 @@ static void destroy_radio(struct work_struct *work)
struct mac80211_hwsim_data *data =
container_of(work, struct mac80211_hwsim_data, destroy_work);
+ hwsim_radios_generation++;
mac80211_hwsim_del_radio(data, wiphy_name(data->hw->wiphy), NULL);
}
@@ -3463,9 +3510,7 @@ failure:
static __net_init int hwsim_init_net(struct net *net)
{
- hwsim_net_set_netgroup(net);
-
- return 0;
+ return hwsim_net_set_netgroup(net);
}
static void __net_exit hwsim_exit_net(struct net *net)
@@ -3484,10 +3529,16 @@ static void __net_exit hwsim_exit_net(struct net *net)
list_del(&data->list);
rhashtable_remove_fast(&hwsim_radios_rht, &data->rht,
hwsim_rht_params);
- INIT_WORK(&data->destroy_work, destroy_radio);
- queue_work(hwsim_wq, &data->destroy_work);
+ hwsim_radios_generation++;
+ spin_unlock_bh(&hwsim_radio_lock);
+ mac80211_hwsim_del_radio(data,
+ wiphy_name(data->hw->wiphy),
+ NULL);
+ spin_lock_bh(&hwsim_radio_lock);
}
spin_unlock_bh(&hwsim_radio_lock);
+
+ ida_simple_remove(&hwsim_netgroup_ida, hwsim_net_get_netgroup(net));
}
static struct pernet_operations hwsim_net_ops = {
diff --git a/drivers/net/wireless/mac80211_hwsim.h b/drivers/net/wireless/mac80211_hwsim.h
index a96a79c1eff5..0fe3199f8c72 100644
--- a/drivers/net/wireless/mac80211_hwsim.h
+++ b/drivers/net/wireless/mac80211_hwsim.h
@@ -68,7 +68,12 @@ enum hwsim_tx_control_flags {
* %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE
* @HWSIM_CMD_NEW_RADIO: create a new radio with the given parameters,
* returns the radio ID (>= 0) or negative on errors, if successful
- * then multicast the result
+ * then multicast the result, uses optional parameter:
+ * %HWSIM_ATTR_REG_STRICT_REG, %HWSIM_ATTR_SUPPORT_P2P_DEVICE,
+ * %HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE, %HWSIM_ATTR_CHANNELS,
+ * %HWSIM_ATTR_NO_VIF, %HWSIM_ATTR_RADIO_NAME, %HWSIM_ATTR_USE_CHANCTX,
+ * %HWSIM_ATTR_REG_HINT_ALPHA2, %HWSIM_ATTR_REG_CUSTOM_REG,
+ * %HWSIM_ATTR_PERM_ADDR
* @HWSIM_CMD_DEL_RADIO: destroy a radio, reply is multicasted
* @HWSIM_CMD_GET_RADIO: fetch information about existing radios, uses:
* %HWSIM_ATTR_RADIO_ID
@@ -126,6 +131,7 @@ enum {
* @HWSIM_ATTR_FREQ: Frequency at which packet is transmitted or received.
* @HWSIM_ATTR_TX_INFO_FLAGS: additional flags for corresponding
* rates of %HWSIM_ATTR_TX_INFO
+ * @HWSIM_ATTR_PERM_ADDR: permanent mac address of new radio
* @__HWSIM_ATTR_MAX: enum limit
*/
@@ -153,6 +159,7 @@ enum {
HWSIM_ATTR_FREQ,
HWSIM_ATTR_PAD,
HWSIM_ATTR_TX_INFO_FLAGS,
+ HWSIM_ATTR_PERM_ADDR,
__HWSIM_ATTR_MAX,
};
#define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1)
diff --git a/drivers/net/wireless/marvell/Kconfig b/drivers/net/wireless/marvell/Kconfig
index 4938c7ec0009..27038901d3ee 100644
--- a/drivers/net/wireless/marvell/Kconfig
+++ b/drivers/net/wireless/marvell/Kconfig
@@ -5,8 +5,8 @@ config WLAN_VENDOR_MARVELL
If you have a wireless card belonging to this class, say Y.
Note that the answer to this question doesn't directly affect the
- kernel: saying N will just cause the configurator to skip all
- the questions about cards. If you say Y, you will be asked for
+ kernel: saying N will just cause the configurator to skip all the
+ questions about these cards. If you say Y, you will be asked for
your specific card in the following questions.
if WLAN_VENDOR_MARVELL
diff --git a/drivers/net/wireless/marvell/mwifiex/11n.c b/drivers/net/wireless/marvell/mwifiex/11n.c
index 8772e3949327..5d75c971004b 100644
--- a/drivers/net/wireless/marvell/mwifiex/11n.c
+++ b/drivers/net/wireless/marvell/mwifiex/11n.c
@@ -341,6 +341,38 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv,
le16_to_cpu(ht_cap->header.len));
mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap);
+ /* Update HT40 capability from current channel information */
+ if (bss_desc->bcn_ht_oper) {
+ u8 ht_param = bss_desc->bcn_ht_oper->ht_param;
+ u8 radio =
+ mwifiex_band_to_radio_type(bss_desc->bss_band);
+ int freq =
+ ieee80211_channel_to_frequency(bss_desc->channel,
+ radio);
+ struct ieee80211_channel *chan =
+ ieee80211_get_channel(priv->adapter->wiphy, freq);
+
+ switch (ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+ if (chan->flags & IEEE80211_CHAN_NO_HT40PLUS) {
+ ht_cap->ht_cap.cap_info &=
+ cpu_to_le16
+ (~IEEE80211_HT_CAP_SUP_WIDTH_20_40);
+ ht_cap->ht_cap.cap_info &=
+ cpu_to_le16(~IEEE80211_HT_CAP_SGI_40);
+ }
+ break;
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+ if (chan->flags & IEEE80211_CHAN_NO_HT40MINUS) {
+ ht_cap->ht_cap.cap_info &=
+ cpu_to_le16
+ (~IEEE80211_HT_CAP_SUP_WIDTH_20_40);
+ ht_cap->ht_cap.cap_info &=
+ cpu_to_le16(~IEEE80211_HT_CAP_SGI_40);
+ }
+ break;
+ }
+ }
*buffer += sizeof(struct mwifiex_ie_types_htcap);
ret_len += sizeof(struct mwifiex_ie_types_htcap);
diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
index ce4432c535f0..7f7e9de2db1c 100644
--- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
@@ -95,18 +95,32 @@ u8 mwifiex_chan_type_to_sec_chan_offset(enum nl80211_channel_type chan_type)
/* This function maps IEEE HT secondary channel type to NL80211 channel type
*/
-u8 mwifiex_sec_chan_offset_to_chan_type(u8 second_chan_offset)
+u8 mwifiex_get_chan_type(struct mwifiex_private *priv)
{
- switch (second_chan_offset) {
- case IEEE80211_HT_PARAM_CHA_SEC_NONE:
- return NL80211_CHAN_HT20;
- case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
- return NL80211_CHAN_HT40PLUS;
- case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
- return NL80211_CHAN_HT40MINUS;
- default:
- return NL80211_CHAN_HT20;
+ struct mwifiex_channel_band channel_band;
+ int ret;
+
+ ret = mwifiex_get_chan_info(priv, &channel_band);
+
+ if (!ret) {
+ switch (channel_band.band_config.chan_width) {
+ case CHAN_BW_20MHZ:
+ if (IS_11N_ENABLED(priv))
+ return NL80211_CHAN_HT20;
+ else
+ return NL80211_CHAN_NO_HT;
+ case CHAN_BW_40MHZ:
+ if (channel_band.band_config.chan2_offset ==
+ SEC_CHAN_ABOVE)
+ return NL80211_CHAN_HT40PLUS;
+ else
+ return NL80211_CHAN_HT40MINUS;
+ default:
+ return NL80211_CHAN_HT20;
+ }
}
+
+ return NL80211_CHAN_HT20;
}
/*
@@ -3937,7 +3951,6 @@ static int mwifiex_cfg80211_get_channel(struct wiphy *wiphy,
struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev);
struct mwifiex_bssdescriptor *curr_bss;
struct ieee80211_channel *chan;
- u8 second_chan_offset;
enum nl80211_channel_type chan_type;
enum nl80211_band band;
int freq;
@@ -3954,10 +3967,7 @@ static int mwifiex_cfg80211_get_channel(struct wiphy *wiphy,
chan = ieee80211_get_channel(wiphy, freq);
if (priv->ht_param_present) {
- second_chan_offset = priv->assoc_resp_ht_param &
- IEEE80211_HT_PARAM_CHA_SEC_OFFSET;
- chan_type = mwifiex_sec_chan_offset_to_chan_type
- (second_chan_offset);
+ chan_type = mwifiex_get_chan_type(priv);
cfg80211_chandef_create(chandef, chan, chan_type);
} else {
cfg80211_chandef_create(chandef, chan,
diff --git a/drivers/net/wireless/marvell/mwifiex/cmdevt.c b/drivers/net/wireless/marvell/mwifiex/cmdevt.c
index 874660052055..7014f440e6f8 100644
--- a/drivers/net/wireless/marvell/mwifiex/cmdevt.c
+++ b/drivers/net/wireless/marvell/mwifiex/cmdevt.c
@@ -1529,7 +1529,8 @@ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv,
adapter->fw_release_number = le32_to_cpu(hw_spec->fw_release_number);
adapter->fw_api_ver = (adapter->fw_release_number >> 16) & 0xff;
- adapter->number_of_antenna = le16_to_cpu(hw_spec->number_of_antenna);
+ adapter->number_of_antenna =
+ le16_to_cpu(hw_spec->number_of_antenna) & 0xf;
if (le32_to_cpu(hw_spec->dot_11ac_dev_cap)) {
adapter->is_hw_11ac_capable = true;
diff --git a/drivers/net/wireless/marvell/mwifiex/decl.h b/drivers/net/wireless/marvell/mwifiex/decl.h
index 188e4c370836..46696ea0b23e 100644
--- a/drivers/net/wireless/marvell/mwifiex/decl.h
+++ b/drivers/net/wireless/marvell/mwifiex/decl.h
@@ -294,4 +294,21 @@ enum rdwr_status {
RDWR_STATUS_DONE = 2
};
+enum mwifiex_chan_width {
+ CHAN_BW_20MHZ = 0,
+ CHAN_BW_10MHZ,
+ CHAN_BW_40MHZ,
+ CHAN_BW_80MHZ,
+ CHAN_BW_8080MHZ,
+ CHAN_BW_160MHZ,
+ CHAN_BW_5MHZ,
+};
+
+enum mwifiex_chan_offset {
+ SEC_CHAN_NONE = 0,
+ SEC_CHAN_ABOVE = 1,
+ SEC_CHAN_5MHZ = 2,
+ SEC_CHAN_BELOW = 3
+};
+
#endif /* !_MWIFIEX_DECL_H_ */
diff --git a/drivers/net/wireless/marvell/mwifiex/fw.h b/drivers/net/wireless/marvell/mwifiex/fw.h
index 9c2cdef54074..c5dc518f768b 100644
--- a/drivers/net/wireless/marvell/mwifiex/fw.h
+++ b/drivers/net/wireless/marvell/mwifiex/fw.h
@@ -411,6 +411,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
#define HostCmd_CMD_TDLS_OPER 0x0122
#define HostCmd_CMD_FW_DUMP_EVENT 0x0125
#define HostCmd_CMD_SDIO_SP_RX_AGGR_CFG 0x0223
+#define HostCmd_CMD_STA_CONFIGURE 0x023f
#define HostCmd_CMD_CHAN_REGION_CFG 0x0242
#define HostCmd_CMD_PACKET_AGGR_CTRL 0x0251
@@ -2285,6 +2286,11 @@ struct host_cmd_ds_pkt_aggr_ctrl {
__le16 tx_aggr_align;
} __packed;
+struct host_cmd_ds_sta_configure {
+ __le16 action;
+ u8 tlv_buffer[0];
+} __packed;
+
struct host_cmd_ds_command {
__le16 command;
__le16 size;
@@ -2361,6 +2367,7 @@ struct host_cmd_ds_command {
struct host_cmd_ds_gtk_rekey_params rekey;
struct host_cmd_ds_chan_region_cfg reg_cfg;
struct host_cmd_ds_pkt_aggr_ctrl pkt_aggr_ctrl;
+ struct host_cmd_ds_sta_configure sta_cfg;
} params;
} __packed;
diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c
index 12e739950332..b6484582845a 100644
--- a/drivers/net/wireless/marvell/mwifiex/main.c
+++ b/drivers/net/wireless/marvell/mwifiex/main.c
@@ -943,13 +943,26 @@ int mwifiex_set_mac_address(struct mwifiex_private *priv,
struct net_device *dev)
{
int ret;
- u64 mac_addr;
+ u64 mac_addr, old_mac_addr;
- if (priv->bss_type != MWIFIEX_BSS_TYPE_P2P)
- goto done;
+ if (priv->bss_type == MWIFIEX_BSS_TYPE_ANY)
+ return -ENOTSUPP;
mac_addr = ether_addr_to_u64(priv->curr_addr);
- mac_addr |= BIT_ULL(MWIFIEX_MAC_LOCAL_ADMIN_BIT);
+ old_mac_addr = mac_addr;
+
+ if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P)
+ mac_addr |= BIT_ULL(MWIFIEX_MAC_LOCAL_ADMIN_BIT);
+
+ if (mwifiex_get_intf_num(priv->adapter, priv->bss_type) > 1) {
+ /* Set mac address based on bss_type/bss_num */
+ mac_addr ^= BIT_ULL(priv->bss_type + 8);
+ mac_addr += priv->bss_num;
+ }
+
+ if (mac_addr == old_mac_addr)
+ goto done;
+
u64_to_ether_addr(mac_addr, priv->curr_addr);
/* Send request to firmware */
@@ -957,13 +970,14 @@ int mwifiex_set_mac_address(struct mwifiex_private *priv,
HostCmd_ACT_GEN_SET, 0, NULL, true);
if (ret) {
+ u64_to_ether_addr(old_mac_addr, priv->curr_addr);
mwifiex_dbg(priv->adapter, ERROR,
"set mac address failed: ret=%d\n", ret);
return ret;
}
done:
- memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN);
+ ether_addr_copy(dev->dev_addr, priv->curr_addr);
return 0;
}
diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h
index 6b5539b1f4d8..9bde181700dc 100644
--- a/drivers/net/wireless/marvell/mwifiex/main.h
+++ b/drivers/net/wireless/marvell/mwifiex/main.h
@@ -517,6 +517,18 @@ enum mwifiex_iface_work_flags {
MWIFIEX_IFACE_WORK_CARD_RESET,
};
+struct mwifiex_band_config {
+ u8 chan_band:2;
+ u8 chan_width:2;
+ u8 chan2_offset:2;
+ u8 scan_mode:2;
+} __packed;
+
+struct mwifiex_channel_band {
+ struct mwifiex_band_config band_config;
+ u8 channel;
+};
+
struct mwifiex_private {
struct mwifiex_adapter *adapter;
u8 bss_type;
@@ -1280,6 +1292,19 @@ mwifiex_copy_rates(u8 *dest, u32 pos, u8 *src, int len)
return pos;
}
+/* This function return interface number with the same bss_type.
+ */
+static inline u8
+mwifiex_get_intf_num(struct mwifiex_adapter *adapter, u8 bss_type)
+{
+ u8 i, num = 0;
+
+ for (i = 0; i < adapter->priv_num; i++)
+ if (adapter->priv[i] && adapter->priv[i]->bss_type == bss_type)
+ num++;
+ return num;
+}
+
/*
* This function returns the correct private structure pointer based
* upon the BSS type and BSS number.
@@ -1544,7 +1569,7 @@ int mwifiex_check_network_compatibility(struct mwifiex_private *priv,
struct mwifiex_bssdescriptor *bss_desc);
u8 mwifiex_chan_type_to_sec_chan_offset(enum nl80211_channel_type chan_type);
-u8 mwifiex_sec_chan_offset_to_chan_type(u8 second_chan_offset);
+u8 mwifiex_get_chan_type(struct mwifiex_private *priv);
struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
const char *name,
@@ -1670,6 +1695,8 @@ void mwifiex_queue_main_work(struct mwifiex_adapter *adapter);
int mwifiex_get_wakeup_reason(struct mwifiex_private *priv, u16 action,
int cmd_type,
struct mwifiex_ds_wakeup_reason *wakeup_reason);
+int mwifiex_get_chan_info(struct mwifiex_private *priv,
+ struct mwifiex_channel_band *channel_band);
int mwifiex_ret_wakeup_reason(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp,
struct host_cmd_ds_wakeup_reason *wakeup_reason);
diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c
index 211e47d8b318..4ed10cf82f9a 100644
--- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c
+++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c
@@ -1898,6 +1898,25 @@ static int mwifiex_cmd_get_wakeup_reason(struct mwifiex_private *priv,
return 0;
}
+static int mwifiex_cmd_get_chan_info(struct host_cmd_ds_command *cmd,
+ u16 cmd_action)
+{
+ struct host_cmd_ds_sta_configure *sta_cfg_cmd = &cmd->params.sta_cfg;
+ struct host_cmd_tlv_channel_band *tlv_band_channel =
+ (struct host_cmd_tlv_channel_band *)sta_cfg_cmd->tlv_buffer;
+
+ cmd->command = cpu_to_le16(HostCmd_CMD_STA_CONFIGURE);
+ cmd->size = cpu_to_le16(sizeof(*sta_cfg_cmd) +
+ sizeof(*tlv_band_channel) + S_DS_GEN);
+ sta_cfg_cmd->action = cpu_to_le16(cmd_action);
+ memset(tlv_band_channel, 0, sizeof(*tlv_band_channel));
+ tlv_band_channel->header.type = cpu_to_le16(TLV_TYPE_CHANNELBANDLIST);
+ tlv_band_channel->header.len = cpu_to_le16(sizeof(*tlv_band_channel) -
+ sizeof(struct mwifiex_ie_types_header));
+
+ return 0;
+}
+
/* This function check if the command is supported by firmware */
static int mwifiex_is_cmd_supported(struct mwifiex_private *priv, u16 cmd_no)
{
@@ -2210,6 +2229,9 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
cmd_ptr->command = cpu_to_le16(cmd_no);
cmd_ptr->size = cpu_to_le16(S_DS_GEN);
break;
+ case HostCmd_CMD_STA_CONFIGURE:
+ ret = mwifiex_cmd_get_chan_info(cmd_ptr, cmd_action);
+ break;
default:
mwifiex_dbg(priv->adapter, ERROR,
"PREP_CMD: unknown cmd- %#x\n", cmd_no);
diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c
index 1bd4e13b8449..69e3b624adbb 100644
--- a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c
+++ b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c
@@ -1170,6 +1170,22 @@ static int mwifiex_ret_pkt_aggr_ctrl(struct mwifiex_private *priv,
return 0;
}
+static int mwifiex_ret_get_chan_info(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *resp,
+ struct mwifiex_channel_band *channel_band)
+{
+ struct host_cmd_ds_sta_configure *sta_cfg_cmd = &resp->params.sta_cfg;
+ struct host_cmd_tlv_channel_band *tlv_band_channel;
+
+ tlv_band_channel =
+ (struct host_cmd_tlv_channel_band *)sta_cfg_cmd->tlv_buffer;
+ memcpy(&channel_band->band_config, &tlv_band_channel->band_config,
+ sizeof(struct mwifiex_band_config));
+ channel_band->channel = tlv_band_channel->channel;
+
+ return 0;
+}
+
/*
* This function handles the command responses.
*
@@ -1393,6 +1409,9 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
case HostCmd_CMD_CHAN_REGION_CFG:
ret = mwifiex_ret_chan_region_cfg(priv, resp);
break;
+ case HostCmd_CMD_STA_CONFIGURE:
+ ret = mwifiex_ret_get_chan_info(priv, resp, data_buf);
+ break;
default:
mwifiex_dbg(adapter, ERROR,
"CMD_RESP: unknown cmd response %#x\n",
diff --git a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c
index a6077ab3efc3..5414b755cf82 100644
--- a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c
+++ b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c
@@ -146,7 +146,6 @@ int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv,
size_t beacon_ie_len;
struct mwifiex_bss_priv *bss_priv = (void *)bss->priv;
const struct cfg80211_bss_ies *ies;
- int ret;
rcu_read_lock();
ies = rcu_dereference(bss->ies);
@@ -190,48 +189,7 @@ int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv,
if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_SPECTRUM_MGMT)
bss_desc->sensed_11h = true;
- ret = mwifiex_update_bss_desc_with_ie(priv->adapter, bss_desc);
- if (ret)
- return ret;
-
- /* Update HT40 capability based on current channel information */
- if (bss_desc->bcn_ht_oper && bss_desc->bcn_ht_cap) {
- u8 ht_param = bss_desc->bcn_ht_oper->ht_param;
- u8 radio = mwifiex_band_to_radio_type(bss_desc->bss_band);
- struct ieee80211_supported_band *sband =
- priv->wdev.wiphy->bands[radio];
- int freq = ieee80211_channel_to_frequency(bss_desc->channel,
- radio);
- struct ieee80211_channel *chan =
- ieee80211_get_channel(priv->adapter->wiphy, freq);
-
- switch (ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
- case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
- if (chan->flags & IEEE80211_CHAN_NO_HT40PLUS) {
- sband->ht_cap.cap &=
- ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
- sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_40;
- } else {
- sband->ht_cap.cap |=
- IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
- IEEE80211_HT_CAP_SGI_40;
- }
- break;
- case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
- if (chan->flags & IEEE80211_CHAN_NO_HT40MINUS) {
- sband->ht_cap.cap &=
- ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
- sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_40;
- } else {
- sband->ht_cap.cap |=
- IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
- IEEE80211_HT_CAP_SGI_40;
- }
- break;
- }
- }
-
- return 0;
+ return mwifiex_update_bss_desc_with_ie(priv->adapter, bss_desc);
}
void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv)
@@ -1523,3 +1481,15 @@ int mwifiex_get_wakeup_reason(struct mwifiex_private *priv, u16 action,
return status;
}
+
+int mwifiex_get_chan_info(struct mwifiex_private *priv,
+ struct mwifiex_channel_band *channel_band)
+{
+ int status = 0;
+
+ status = mwifiex_send_cmd(priv, HostCmd_CMD_STA_CONFIGURE,
+ HostCmd_ACT_GEN_GET, 0, channel_band,
+ MWIFIEX_SYNC_CMD);
+
+ return status;
+}
diff --git a/drivers/net/wireless/mediatek/Kconfig b/drivers/net/wireless/mediatek/Kconfig
index 92ce4062f307..ff5fc8987b0a 100644
--- a/drivers/net/wireless/mediatek/Kconfig
+++ b/drivers/net/wireless/mediatek/Kconfig
@@ -5,8 +5,8 @@ config WLAN_VENDOR_MEDIATEK
If you have a wireless card belonging to this class, say Y.
Note that the answer to this question doesn't directly affect the
- kernel: saying N will just cause the configurator to skip all
- the questions about cards. If you say Y, you will be asked for
+ kernel: saying N will just cause the configurator to skip all the
+ questions about these cards. If you say Y, you will be asked for
your specific card in the following questions.
if WLAN_VENDOR_MEDIATEK
diff --git a/drivers/net/wireless/mediatek/mt76/debugfs.c b/drivers/net/wireless/mediatek/mt76/debugfs.c
index c121b502a462..a38d05dea599 100644
--- a/drivers/net/wireless/mediatek/mt76/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/debugfs.c
@@ -64,13 +64,13 @@ struct dentry *mt76_register_debugfs(struct mt76_dev *dev)
if (!dir)
return NULL;
- debugfs_create_u8("led_pin", S_IRUSR | S_IWUSR, dir, &dev->led_pin);
- debugfs_create_u32("regidx", S_IRUSR | S_IWUSR, dir, &dev->debugfs_reg);
- debugfs_create_file_unsafe("regval", S_IRUSR | S_IWUSR, dir, dev,
+ debugfs_create_u8("led_pin", 0600, dir, &dev->led_pin);
+ debugfs_create_u32("regidx", 0600, dir, &dev->debugfs_reg);
+ debugfs_create_file_unsafe("regval", 0600, dir, dev,
&fops_regval);
- debugfs_create_blob("eeprom", S_IRUSR, dir, &dev->eeprom);
+ debugfs_create_blob("eeprom", 0400, dir, &dev->eeprom);
if (dev->otp.data)
- debugfs_create_blob("otp", S_IRUSR, dir, &dev->otp);
+ debugfs_create_blob("otp", 0400, dir, &dev->otp);
debugfs_create_devm_seqfile(dev->dev, "queues", dir, mt76_queues_read);
return dir;
diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
index 85f8d324ebf8..4f30cdcd2b53 100644
--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -119,6 +119,52 @@ static int mt76_led_init(struct mt76_dev *dev)
return devm_led_classdev_register(dev->dev, &dev->led_cdev);
}
+static void mt76_init_stream_cap(struct mt76_dev *dev,
+ struct ieee80211_supported_band *sband,
+ bool vht)
+{
+ struct ieee80211_sta_ht_cap *ht_cap = &sband->ht_cap;
+ int i, nstream = __sw_hweight8(dev->antenna_mask);
+ struct ieee80211_sta_vht_cap *vht_cap;
+ u16 mcs_map = 0;
+
+ if (nstream > 1)
+ ht_cap->cap |= IEEE80211_HT_CAP_TX_STBC;
+ else
+ ht_cap->cap &= ~IEEE80211_HT_CAP_TX_STBC;
+
+ for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
+ ht_cap->mcs.rx_mask[i] = i < nstream ? 0xff : 0;
+
+ if (!vht)
+ return;
+
+ vht_cap = &sband->vht_cap;
+ if (nstream > 1)
+ vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC;
+ else
+ vht_cap->cap &= ~IEEE80211_VHT_CAP_TXSTBC;
+
+ for (i = 0; i < 8; i++) {
+ if (i < nstream)
+ mcs_map |= (IEEE80211_VHT_MCS_SUPPORT_0_9 << (i * 2));
+ else
+ mcs_map |=
+ (IEEE80211_VHT_MCS_NOT_SUPPORTED << (i * 2));
+ }
+ vht_cap->vht_mcs.rx_mcs_map = cpu_to_le16(mcs_map);
+ vht_cap->vht_mcs.tx_mcs_map = cpu_to_le16(mcs_map);
+}
+
+void mt76_set_stream_caps(struct mt76_dev *dev, bool vht)
+{
+ if (dev->cap.has_2ghz)
+ mt76_init_stream_cap(dev, &dev->sband_2g.sband, false);
+ if (dev->cap.has_5ghz)
+ mt76_init_stream_cap(dev, &dev->sband_5g.sband, vht);
+}
+EXPORT_SYMBOL_GPL(mt76_set_stream_caps);
+
static int
mt76_init_sband(struct mt76_dev *dev, struct mt76_sband *msband,
const struct ieee80211_channel *chan, int n_chan,
@@ -128,7 +174,6 @@ mt76_init_sband(struct mt76_dev *dev, struct mt76_sband *msband,
struct ieee80211_sta_ht_cap *ht_cap;
struct ieee80211_sta_vht_cap *vht_cap;
void *chanlist;
- u16 mcs_map;
int size;
size = n_chan * sizeof(*chan);
@@ -153,34 +198,20 @@ mt76_init_sband(struct mt76_dev *dev, struct mt76_sband *msband,
IEEE80211_HT_CAP_GRN_FLD |
IEEE80211_HT_CAP_SGI_20 |
IEEE80211_HT_CAP_SGI_40 |
- IEEE80211_HT_CAP_TX_STBC |
(1 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
- ht_cap->mcs.rx_mask[0] = 0xff;
- ht_cap->mcs.rx_mask[1] = 0xff;
ht_cap->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
ht_cap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
ht_cap->ampdu_density = IEEE80211_HT_MPDU_DENSITY_4;
+ mt76_init_stream_cap(dev, sband, vht);
+
if (!vht)
return 0;
vht_cap = &sband->vht_cap;
vht_cap->vht_supported = true;
-
- mcs_map = (IEEE80211_VHT_MCS_SUPPORT_0_9 << (0 * 2)) |
- (IEEE80211_VHT_MCS_SUPPORT_0_9 << (1 * 2)) |
- (IEEE80211_VHT_MCS_NOT_SUPPORTED << (2 * 2)) |
- (IEEE80211_VHT_MCS_NOT_SUPPORTED << (3 * 2)) |
- (IEEE80211_VHT_MCS_NOT_SUPPORTED << (4 * 2)) |
- (IEEE80211_VHT_MCS_NOT_SUPPORTED << (5 * 2)) |
- (IEEE80211_VHT_MCS_NOT_SUPPORTED << (6 * 2)) |
- (IEEE80211_VHT_MCS_NOT_SUPPORTED << (7 * 2));
-
- vht_cap->vht_mcs.rx_mcs_map = cpu_to_le16(mcs_map);
- vht_cap->vht_mcs.tx_mcs_map = cpu_to_le16(mcs_map);
vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC |
- IEEE80211_VHT_CAP_TXSTBC |
IEEE80211_VHT_CAP_RXSTBC_1 |
IEEE80211_VHT_CAP_SHORT_GI_80;
@@ -262,6 +293,9 @@ int mt76_register_device(struct mt76_dev *dev, bool vht,
wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
+ wiphy->available_antennas_tx = dev->antenna_mask;
+ wiphy->available_antennas_rx = dev->antenna_mask;
+
hw->txq_data_size = sizeof(struct mt76_txq);
hw->max_tx_fragments = 16;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index d2ce15093edd..065ff78059c3 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -253,6 +253,8 @@ struct mt76_dev {
u32 rev;
unsigned long state;
+ u8 antenna_mask;
+
struct mt76_sband sband_2g;
struct mt76_sband sband_5g;
struct debugfs_blob_wrapper eeprom;
@@ -423,6 +425,7 @@ void mt76_release_buffered_frames(struct ieee80211_hw *hw,
void mt76_set_channel(struct mt76_dev *dev);
int mt76_get_survey(struct ieee80211_hw *hw, int idx,
struct survey_info *survey);
+void mt76_set_stream_caps(struct mt76_dev *dev, bool vht);
int mt76_rx_aggr_start(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tid,
u16 ssn, u8 size);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2.h b/drivers/net/wireless/mediatek/mt76/mt76x2.h
index e62131b88102..783b8122ec3c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2.h
@@ -180,6 +180,7 @@ int mt76x2_eeprom_init(struct mt76x2_dev *dev);
int mt76x2_apply_calibration_data(struct mt76x2_dev *dev, int channel);
void mt76x2_set_tx_ackto(struct mt76x2_dev *dev);
+void mt76x2_phy_set_antenna(struct mt76x2_dev *dev);
int mt76x2_phy_start(struct mt76x2_dev *dev);
int mt76x2_phy_set_channel(struct mt76x2_dev *dev,
struct cfg80211_chan_def *chandef);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_debugfs.c b/drivers/net/wireless/mediatek/mt76/mt76x2_debugfs.c
index 612feb593d7d..955ea3e692dd 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_debugfs.c
@@ -123,11 +123,11 @@ void mt76x2_init_debugfs(struct mt76x2_dev *dev)
if (!dir)
return;
- debugfs_create_u8("temperature", S_IRUSR, dir, &dev->cal.temp);
- debugfs_create_bool("tpc", S_IRUSR | S_IWUSR, dir, &dev->enable_tpc);
+ debugfs_create_u8("temperature", 0400, dir, &dev->cal.temp);
+ debugfs_create_bool("tpc", 0600, dir, &dev->enable_tpc);
- debugfs_create_file("ampdu_stat", S_IRUSR, dir, dev, &fops_ampdu_stat);
- debugfs_create_file("dfs_stats", S_IRUSR, dir, dev, &fops_dfs_stat);
+ debugfs_create_file("ampdu_stat", 0400, dir, dev, &fops_ampdu_stat);
+ debugfs_create_file("dfs_stats", 0400, dir, dev, &fops_dfs_stat);
debugfs_create_devm_seqfile(dev->mt76.dev, "txpower", dir,
read_txpower);
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c b/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c
index 9c9bf3e785ba..5bb50027c1e8 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c
@@ -222,11 +222,10 @@ static int
mt76x2_eeprom_load(struct mt76x2_dev *dev)
{
void *efuse;
- int len = MT7662_EEPROM_SIZE;
bool found;
int ret;
- ret = mt76_eeprom_init(&dev->mt76, len);
+ ret = mt76_eeprom_init(&dev->mt76, MT7662_EEPROM_SIZE);
if (ret < 0)
return ret;
@@ -234,14 +233,15 @@ mt76x2_eeprom_load(struct mt76x2_dev *dev)
if (found)
found = !mt76x2_check_eeprom(dev);
- dev->mt76.otp.data = devm_kzalloc(dev->mt76.dev, len, GFP_KERNEL);
- dev->mt76.otp.size = len;
+ dev->mt76.otp.data = devm_kzalloc(dev->mt76.dev, MT7662_EEPROM_SIZE,
+ GFP_KERNEL);
+ dev->mt76.otp.size = MT7662_EEPROM_SIZE;
if (!dev->mt76.otp.data)
return -ENOMEM;
efuse = dev->mt76.otp.data;
- if (mt76x2_get_efuse_data(dev, efuse, len))
+ if (mt76x2_get_efuse_data(dev, efuse, MT7662_EEPROM_SIZE))
goto out;
if (found) {
@@ -249,7 +249,7 @@ mt76x2_eeprom_load(struct mt76x2_dev *dev)
} else {
/* FIXME: check if efuse data is complete */
found = true;
- memcpy(dev->mt76.eeprom.data, efuse, len);
+ memcpy(dev->mt76.eeprom.data, efuse, MT7662_EEPROM_SIZE);
}
out:
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_init.c b/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
index 9dbf94947324..934c331d995e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
@@ -857,6 +857,9 @@ int mt76x2_register_device(struct mt76x2_dev *dev)
dev->mt76.led_cdev.brightness_set = mt76x2_led_set_brightness;
dev->mt76.led_cdev.blink_set = mt76x2_led_set_blink;
+ /* init antenna configuration */
+ dev->mt76.antenna_mask = 3;
+
ret = mt76_register_device(&dev->mt76, true, mt76x2_rates,
ARRAY_SIZE(mt76x2_rates));
if (ret)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
index 7ea3d841918e..d18315652583 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
@@ -198,8 +198,8 @@ void mt76x2_mac_write_txwi(struct mt76x2_dev *dev, struct mt76x2_txwi *txwi,
ccmp_pn[5] = pn >> 24;
ccmp_pn[6] = pn >> 32;
ccmp_pn[7] = pn >> 40;
- txwi->iv = *((u32 *) &ccmp_pn[0]);
- txwi->eiv = *((u32 *) &ccmp_pn[1]);
+ txwi->iv = *((__le32 *)&ccmp_pn[0]);
+ txwi->eiv = *((__le32 *)&ccmp_pn[1]);
}
spin_lock_bh(&dev->mt76.lock);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_main.c b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
index 205043b470b2..73c127f92613 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
@@ -336,6 +336,17 @@ mt76x2_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
int idx = key->keyidx;
int ret;
+ /* fall back to sw encryption for unsupported ciphers */
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ case WLAN_CIPHER_SUITE_TKIP:
+ case WLAN_CIPHER_SUITE_CCMP:
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
/*
* The hardware does not support per-STA RX GTK, fall back
* to software mode for these.
@@ -549,6 +560,40 @@ mt76x2_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set)
return 0;
}
+static int mt76x2_set_antenna(struct ieee80211_hw *hw, u32 tx_ant,
+ u32 rx_ant)
+{
+ struct mt76x2_dev *dev = hw->priv;
+
+ if (!tx_ant || tx_ant > 3 || tx_ant != rx_ant)
+ return -EINVAL;
+
+ mutex_lock(&dev->mutex);
+
+ dev->chainmask = (tx_ant == 3) ? 0x202 : 0x101;
+ dev->mt76.antenna_mask = tx_ant;
+
+ mt76_set_stream_caps(&dev->mt76, true);
+ mt76x2_phy_set_antenna(dev);
+
+ mutex_unlock(&dev->mutex);
+
+ return 0;
+}
+
+static int mt76x2_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant,
+ u32 *rx_ant)
+{
+ struct mt76x2_dev *dev = hw->priv;
+
+ mutex_lock(&dev->mutex);
+ *tx_ant = dev->mt76.antenna_mask;
+ *rx_ant = dev->mt76.antenna_mask;
+ mutex_unlock(&dev->mutex);
+
+ return 0;
+}
+
const struct ieee80211_ops mt76x2_ops = {
.tx = mt76x2_tx,
.start = mt76x2_start,
@@ -573,5 +618,7 @@ const struct ieee80211_ops mt76x2_ops = {
.set_coverage_class = mt76x2_set_coverage_class,
.get_survey = mt76_get_survey,
.set_tim = mt76x2_set_tim,
+ .set_antenna = mt76x2_set_antenna,
+ .get_antenna = mt76x2_get_antenna,
};
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76x2_mcu.c
index 15820b11f9db..dfd36d736b06 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_mcu.c
@@ -187,7 +187,7 @@ mt76pci_load_firmware(struct mt76x2_dev *dev)
{
const struct firmware *fw;
const struct mt76x2_fw_header *hdr;
- int i, len, ret;
+ int len, ret;
__le32 *cur;
u32 offset, val;
@@ -240,16 +240,7 @@ mt76pci_load_firmware(struct mt76x2_dev *dev)
/* trigger firmware */
mt76_wr(dev, MT_MCU_INT_LEVEL, 2);
- for (i = 200; i > 0; i--) {
- val = mt76_rr(dev, MT_MCU_COM_REG0);
-
- if (val & 1)
- break;
-
- msleep(10);
- }
-
- if (!i) {
+ if (!mt76_poll_msec(dev, MT_MCU_COM_REG0, 1, 1, 200)) {
dev_err(dev->mt76.dev, "Firmware failed to start\n");
release_firmware(fw);
return -ETIMEDOUT;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c b/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
index 5b742749d5de..fcc37eb7ce0b 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
@@ -361,30 +361,53 @@ mt76x2_phy_set_band(struct mt76x2_dev *dev, int band, bool primary_upper)
primary_upper);
}
-static void
-mt76x2_set_rx_chains(struct mt76x2_dev *dev)
+void mt76x2_phy_set_antenna(struct mt76x2_dev *dev)
{
u32 val;
val = mt76_rr(dev, MT_BBP(AGC, 0));
- val &= ~(BIT(3) | BIT(4));
+ val &= ~(BIT(4) | BIT(1));
+ switch (dev->mt76.antenna_mask) {
+ case 1:
+ /* disable mac DAC control */
+ mt76_clear(dev, MT_BBP(IBI, 9), BIT(11));
+ mt76_clear(dev, MT_BBP(TXBE, 5), 3);
+ mt76_rmw_field(dev, MT_TX_PIN_CFG, MT_TX_PIN_CFG_TXANT, 0x3);
+ mt76_rmw_field(dev, MT_BBP(CORE, 32), GENMASK(21, 20), 2);
+ /* disable DAC 1 */
+ mt76_rmw_field(dev, MT_BBP(CORE, 33), GENMASK(12, 9), 4);
- if (dev->chainmask & BIT(1))
- val |= BIT(3);
+ val &= ~(BIT(3) | BIT(0));
+ break;
+ case 2:
+ /* disable mac DAC control */
+ mt76_clear(dev, MT_BBP(IBI, 9), BIT(11));
+ mt76_rmw_field(dev, MT_BBP(TXBE, 5), 3, 1);
+ mt76_rmw_field(dev, MT_TX_PIN_CFG, MT_TX_PIN_CFG_TXANT, 0xc);
+ mt76_rmw_field(dev, MT_BBP(CORE, 32), GENMASK(21, 20), 1);
+ /* disable DAC 0 */
+ mt76_rmw_field(dev, MT_BBP(CORE, 33), GENMASK(12, 9), 1);
+
+ val &= ~BIT(3);
+ val |= BIT(0);
+ break;
+ case 3:
+ default:
+ /* enable mac DAC control */
+ mt76_set(dev, MT_BBP(IBI, 9), BIT(11));
+ mt76_set(dev, MT_BBP(TXBE, 5), 3);
+ mt76_rmw_field(dev, MT_TX_PIN_CFG, MT_TX_PIN_CFG_TXANT, 0xf);
+ mt76_clear(dev, MT_BBP(CORE, 32), GENMASK(21, 20));
+ mt76_clear(dev, MT_BBP(CORE, 33), GENMASK(12, 9));
+ val &= ~BIT(0);
+ val |= BIT(3);
+ break;
+ }
mt76_wr(dev, MT_BBP(AGC, 0), val);
}
static void
-mt76x2_set_tx_dac(struct mt76x2_dev *dev)
-{
- if (dev->chainmask & BIT(1))
- mt76_set(dev, MT_BBP(TXBE, 5), 3);
- else
- mt76_clear(dev, MT_BBP(TXBE, 5), 3);
-}
-
-static void
mt76x2_get_agc_gain(struct mt76x2_dev *dev, u8 *dest)
{
dest[0] = mt76_get_field(dev, MT_BBP(AGC, 8), MT_BBP_AGC_GAIN);
@@ -585,10 +608,8 @@ int mt76x2_phy_set_channel(struct mt76x2_dev *dev,
mt76x2_configure_tx_delay(dev, band, bw);
mt76x2_phy_set_txpower(dev);
- mt76x2_set_rx_chains(dev);
mt76x2_phy_set_band(dev, chan->band, ch_group_index & 1);
mt76x2_phy_set_bw(dev, chandef->width, ch_group_index);
- mt76x2_set_tx_dac(dev);
mt76_rmw(dev, MT_EXT_CCA_CFG,
(MT_EXT_CCA_CFG_CCA0 |
@@ -604,6 +625,8 @@ int mt76x2_phy_set_channel(struct mt76x2_dev *dev,
mt76x2_mcu_init_gain(dev, channel, dev->cal.rx.mcu_gain, true);
+ mt76x2_phy_set_antenna(dev);
+
/* Enable LDPC Rx */
if (mt76xx_rev(dev) >= MT76XX_REV_E3)
mt76_set(dev, MT_BBP(RXO, 13), BIT(10));
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_regs.h b/drivers/net/wireless/mediatek/mt76/mt76x2_regs.h
index ce3ab85c8b0f..b9c334d9e5b8 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_regs.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_regs.h
@@ -321,6 +321,8 @@
#define MT_TX_PWR_CFG_2 0x131c
#define MT_TX_PWR_CFG_3 0x1320
#define MT_TX_PWR_CFG_4 0x1324
+#define MT_TX_PIN_CFG 0x1328
+#define MT_TX_PIN_CFG_TXANT GENMASK(3, 0)
#define MT_TX_BAND_CFG 0x132c
#define MT_TX_BAND_CFG_UPPER_40M BIT(0)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_tx.c b/drivers/net/wireless/mediatek/mt76/mt76x2_tx.c
index 534e4bf9a34c..e46eafc4c436 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_tx.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_tx.c
@@ -36,9 +36,12 @@ void mt76x2_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
msta = (struct mt76x2_sta *) control->sta->drv_priv;
wcid = &msta->wcid;
+ /* sw encrypted frames */
+ if (!info->control.hw_key && wcid->hw_key_idx != -1)
+ control->sta = NULL;
}
- if (vif || (!info->control.hw_key && wcid->hw_key_idx != -1)) {
+ if (vif && !control->sta) {
struct mt76x2_vif *mvif;
mvif = (struct mt76x2_vif *) vif->drv_priv;
diff --git a/drivers/net/wireless/mediatek/mt7601u/debugfs.c b/drivers/net/wireless/mediatek/mt7601u/debugfs.c
index fc008475a03b..991a6a729b1e 100644
--- a/drivers/net/wireless/mediatek/mt7601u/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt7601u/debugfs.c
@@ -160,13 +160,11 @@ void mt7601u_init_debugfs(struct mt7601u_dev *dev)
if (!dir)
return;
- debugfs_create_u8("temperature", S_IRUSR, dir, &dev->raw_temp);
- debugfs_create_u32("temp_mode", S_IRUSR, dir, &dev->temp_mode);
-
- debugfs_create_u32("regidx", S_IRUSR | S_IWUSR, dir, &dev->debugfs_reg);
- debugfs_create_file("regval", S_IRUSR | S_IWUSR, dir, dev,
- &fops_regval);
- debugfs_create_file("ampdu_stat", S_IRUSR, dir, dev, &fops_ampdu_stat);
- debugfs_create_file("eeprom_param", S_IRUSR, dir, dev,
- &fops_eeprom_param);
+ debugfs_create_u8("temperature", 0400, dir, &dev->raw_temp);
+ debugfs_create_u32("temp_mode", 0400, dir, &dev->temp_mode);
+
+ debugfs_create_u32("regidx", 0600, dir, &dev->debugfs_reg);
+ debugfs_create_file("regval", 0600, dir, dev, &fops_regval);
+ debugfs_create_file("ampdu_stat", 0400, dir, dev, &fops_ampdu_stat);
+ debugfs_create_file("eeprom_param", 0400, dir, dev, &fops_eeprom_param);
}
diff --git a/drivers/net/wireless/mediatek/mt7601u/eeprom.c b/drivers/net/wireless/mediatek/mt7601u/eeprom.c
index da6faea092d6..76117b402880 100644
--- a/drivers/net/wireless/mediatek/mt7601u/eeprom.c
+++ b/drivers/net/wireless/mediatek/mt7601u/eeprom.c
@@ -19,6 +19,7 @@
#include <asm/unaligned.h>
#include "mt7601u.h"
#include "eeprom.h"
+#include "mac.h"
static bool
field_valid(u8 val)
@@ -74,7 +75,7 @@ static int
mt7601u_efuse_physical_size_check(struct mt7601u_dev *dev)
{
const int map_reads = DIV_ROUND_UP(MT_EFUSE_USAGE_MAP_SIZE, 16);
- u8 data[map_reads * 16];
+ u8 data[round_up(MT_EFUSE_USAGE_MAP_SIZE, 16)];
int ret, i;
u32 start = 0, end = 0, cnt_free;
@@ -134,27 +135,6 @@ mt7601u_set_chip_cap(struct mt7601u_dev *dev, u8 *eeprom)
"Error: device has more than 1 RX/TX stream!\n");
}
-static int
-mt7601u_set_macaddr(struct mt7601u_dev *dev, const u8 *eeprom)
-{
- const void *src = eeprom + MT_EE_MAC_ADDR;
-
- ether_addr_copy(dev->macaddr, src);
-
- if (!is_valid_ether_addr(dev->macaddr)) {
- eth_random_addr(dev->macaddr);
- dev_info(dev->dev,
- "Invalid MAC address, using random address %pM\n",
- dev->macaddr);
- }
-
- mt76_wr(dev, MT_MAC_ADDR_DW0, get_unaligned_le32(dev->macaddr));
- mt76_wr(dev, MT_MAC_ADDR_DW1, get_unaligned_le16(dev->macaddr + 4) |
- FIELD_PREP(MT_MAC_ADDR_DW1_U2ME_MASK, 0xff));
-
- return 0;
-}
-
static void mt7601u_set_channel_target_power(struct mt7601u_dev *dev,
u8 *eeprom, u8 max_pwr)
{
@@ -400,7 +380,7 @@ mt7601u_eeprom_init(struct mt7601u_dev *dev)
dev_info(dev->dev, "EEPROM ver:%02hhx fae:%02hhx\n",
eeprom[MT_EE_VERSION_EE], eeprom[MT_EE_VERSION_FAE]);
- mt7601u_set_macaddr(dev, eeprom);
+ mt7601u_set_macaddr(dev, eeprom + MT_EE_MAC_ADDR);
mt7601u_set_chip_cap(dev, eeprom);
mt7601u_set_channel_power(dev, eeprom);
mt7601u_set_country_reg(dev, eeprom);
diff --git a/drivers/net/wireless/mediatek/mt7601u/initvals.h b/drivers/net/wireless/mediatek/mt7601u/initvals.h
index ec11ff66969d..2dc6b68e7fb9 100644
--- a/drivers/net/wireless/mediatek/mt7601u/initvals.h
+++ b/drivers/net/wireless/mediatek/mt7601u/initvals.h
@@ -139,6 +139,7 @@ static const struct mt76_reg_pair mac_common_vals[] = {
{ MT_TXOP_HLDR_ET, 0x00000002 },
{ MT_XIFS_TIME_CFG, 0x33a41010 },
{ MT_PWR_PIN_CFG, 0x00000000 },
+ { MT_PN_PAD_MODE, 0x00000001 },
};
static const struct mt76_reg_pair mac_chip_vals[] = {
diff --git a/drivers/net/wireless/mediatek/mt7601u/mac.c b/drivers/net/wireless/mediatek/mt7601u/mac.c
index d6dc59bb00df..d55d7040a56d 100644
--- a/drivers/net/wireless/mediatek/mt7601u/mac.c
+++ b/drivers/net/wireless/mediatek/mt7601u/mac.c
@@ -16,6 +16,22 @@
#include "trace.h"
#include <linux/etherdevice.h>
+void mt7601u_set_macaddr(struct mt7601u_dev *dev, const u8 *addr)
+{
+ ether_addr_copy(dev->macaddr, addr);
+
+ if (!is_valid_ether_addr(dev->macaddr)) {
+ eth_random_addr(dev->macaddr);
+ dev_info(dev->dev,
+ "Invalid MAC address, using random address %pM\n",
+ dev->macaddr);
+ }
+
+ mt76_wr(dev, MT_MAC_ADDR_DW0, get_unaligned_le32(dev->macaddr));
+ mt76_wr(dev, MT_MAC_ADDR_DW1, get_unaligned_le16(dev->macaddr + 4) |
+ FIELD_PREP(MT_MAC_ADDR_DW1_U2ME_MASK, 0xff));
+}
+
static void
mt76_mac_process_tx_rate(struct ieee80211_tx_rate *txrate, u16 rate)
{
@@ -464,8 +480,16 @@ u32 mt76_mac_process_rx(struct mt7601u_dev *dev, struct sk_buff *skb,
if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_DECRYPT)) {
status->flag |= RX_FLAG_DECRYPTED;
- status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED;
+ status->flag |= RX_FLAG_MMIC_STRIPPED;
+ status->flag |= RX_FLAG_MIC_STRIPPED;
+ status->flag |= RX_FLAG_ICV_STRIPPED;
+ status->flag |= RX_FLAG_IV_STRIPPED;
}
+ /* let mac80211 take care of PN validation since apparently
+ * the hardware does not support it
+ */
+ if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_PN_LEN))
+ status->flag &= ~RX_FLAG_IV_STRIPPED;
status->chains = BIT(0);
rssi = mt7601u_phy_get_rssi(dev, rxwi, rate);
diff --git a/drivers/net/wireless/mediatek/mt7601u/mac.h b/drivers/net/wireless/mediatek/mt7601u/mac.h
index 2c22d63c63a2..b7aa24656d0e 100644
--- a/drivers/net/wireless/mediatek/mt7601u/mac.h
+++ b/drivers/net/wireless/mediatek/mt7601u/mac.h
@@ -174,5 +174,6 @@ u16 mt76_mac_tx_rate_val(struct mt7601u_dev *dev,
struct mt76_tx_status
mt7601u_mac_fetch_tx_status(struct mt7601u_dev *dev);
void mt76_send_tx_status(struct mt7601u_dev *dev, struct mt76_tx_status *stat);
+void mt7601u_set_macaddr(struct mt7601u_dev *dev, const u8 *addr);
#endif
diff --git a/drivers/net/wireless/mediatek/mt7601u/main.c b/drivers/net/wireless/mediatek/mt7601u/main.c
index 43ebd460ba86..3c9ea40d9584 100644
--- a/drivers/net/wireless/mediatek/mt7601u/main.c
+++ b/drivers/net/wireless/mediatek/mt7601u/main.c
@@ -64,6 +64,9 @@ static int mt7601u_add_interface(struct ieee80211_hw *hw,
*/
mvif->idx = idx;
+ if (!ether_addr_equal(dev->macaddr, vif->addr))
+ mt7601u_set_macaddr(dev, vif->addr);
+
if (dev->wcid_mask[wcid / BITS_PER_LONG] & BIT(wcid % BITS_PER_LONG))
return -ENOSPC;
dev->wcid_mask[wcid / BITS_PER_LONG] |= BIT(wcid % BITS_PER_LONG);
diff --git a/drivers/net/wireless/mediatek/mt7601u/mcu.c b/drivers/net/wireless/mediatek/mt7601u/mcu.c
index 65a8004418ea..61705f679856 100644
--- a/drivers/net/wireless/mediatek/mt7601u/mcu.c
+++ b/drivers/net/wireless/mediatek/mt7601u/mcu.c
@@ -58,8 +58,7 @@ static inline void trace_mt_mcu_msg_send_cs(struct mt7601u_dev *dev,
trace_mt_mcu_msg_send(dev, skb, csum, need_resp);
}
-static struct sk_buff *
-mt7601u_mcu_msg_alloc(struct mt7601u_dev *dev, const void *data, int len)
+static struct sk_buff *mt7601u_mcu_msg_alloc(const void *data, int len)
{
struct sk_buff *skb;
@@ -171,7 +170,7 @@ static int mt7601u_mcu_function_select(struct mt7601u_dev *dev,
.value = cpu_to_le32(val),
};
- skb = mt7601u_mcu_msg_alloc(dev, &msg, sizeof(msg));
+ skb = mt7601u_mcu_msg_alloc(&msg, sizeof(msg));
if (!skb)
return -ENOMEM;
return mt7601u_mcu_msg_send(dev, skb, CMD_FUN_SET_OP, func == 5);
@@ -208,7 +207,7 @@ mt7601u_mcu_calibrate(struct mt7601u_dev *dev, enum mcu_calibrate cal, u32 val)
.value = cpu_to_le32(val),
};
- skb = mt7601u_mcu_msg_alloc(dev, &msg, sizeof(msg));
+ skb = mt7601u_mcu_msg_alloc(&msg, sizeof(msg));
if (!skb)
return -ENOMEM;
return mt7601u_mcu_msg_send(dev, skb, CMD_CALIBRATION_OP, true);
@@ -421,7 +420,7 @@ static int mt7601u_load_firmware(struct mt7601u_dev *dev)
MT_USB_DMA_CFG_TX_BULK_EN));
if (firmware_running(dev))
- return 0;
+ return firmware_request_cache(dev->dev, MT7601U_FIRMWARE);
ret = request_firmware(&fw, MT7601U_FIRMWARE, dev->dev);
if (ret)
diff --git a/drivers/net/wireless/mediatek/mt7601u/mt7601u.h b/drivers/net/wireless/mediatek/mt7601u/mt7601u.h
index c7ec40475a5f..9233744451a9 100644
--- a/drivers/net/wireless/mediatek/mt7601u/mt7601u.h
+++ b/drivers/net/wireless/mediatek/mt7601u/mt7601u.h
@@ -147,7 +147,8 @@ enum {
* @rx_lock: protects @rx_q.
* @con_mon_lock: protects @ap_bssid, @bcn_*, @avg_rssi.
* @mutex: ensures exclusive access from mac80211 callbacks.
- * @vendor_req_mutex: protects @vend_buf, ensures atomicity of split writes.
+ * @vendor_req_mutex: protects @vend_buf, ensures atomicity of read/write
+ * accesses
* @reg_atomic_mutex: ensures atomicity of indirect register accesses
* (accesses to RF and BBP).
* @hw_atomic_mutex: ensures exclusive access to HW during critical
diff --git a/drivers/net/wireless/mediatek/mt7601u/trace.h b/drivers/net/wireless/mediatek/mt7601u/trace.h
index 289897300ef0..82c8898b9076 100644
--- a/drivers/net/wireless/mediatek/mt7601u/trace.h
+++ b/drivers/net/wireless/mediatek/mt7601u/trace.h
@@ -34,7 +34,7 @@
#define REG_PR_FMT "%04x=%08x"
#define REG_PR_ARG __entry->reg, __entry->val
-DECLARE_EVENT_CLASS(dev_reg_evt,
+DECLARE_EVENT_CLASS(dev_reg_evtu,
TP_PROTO(struct mt7601u_dev *dev, u32 reg, u32 val),
TP_ARGS(dev, reg, val),
TP_STRUCT__entry(
@@ -51,12 +51,12 @@ DECLARE_EVENT_CLASS(dev_reg_evt,
)
);
-DEFINE_EVENT(dev_reg_evt, reg_read,
+DEFINE_EVENT(dev_reg_evtu, reg_read,
TP_PROTO(struct mt7601u_dev *dev, u32 reg, u32 val),
TP_ARGS(dev, reg, val)
);
-DEFINE_EVENT(dev_reg_evt, reg_write,
+DEFINE_EVENT(dev_reg_evtu, reg_write,
TP_PROTO(struct mt7601u_dev *dev, u32 reg, u32 val),
TP_ARGS(dev, reg, val)
);
diff --git a/drivers/net/wireless/mediatek/mt7601u/usb.c b/drivers/net/wireless/mediatek/mt7601u/usb.c
index b9e4f6793138..d8b7863f7926 100644
--- a/drivers/net/wireless/mediatek/mt7601u/usb.c
+++ b/drivers/net/wireless/mediatek/mt7601u/usb.c
@@ -129,15 +129,14 @@ void mt7601u_vendor_reset(struct mt7601u_dev *dev)
MT_VEND_DEV_MODE_RESET, 0, NULL, 0);
}
-u32 mt7601u_rr(struct mt7601u_dev *dev, u32 offset)
+/* should be called with vendor_req_mutex held */
+static u32 __mt7601u_rr(struct mt7601u_dev *dev, u32 offset)
{
int ret;
u32 val = ~0;
WARN_ONCE(offset > USHRT_MAX, "read high off:%08x", offset);
- mutex_lock(&dev->vendor_req_mutex);
-
ret = mt7601u_vendor_request(dev, MT_VEND_MULTI_READ, USB_DIR_IN,
0, offset, dev->vend_buf, MT_VEND_BUF);
if (ret == MT_VEND_BUF)
@@ -146,25 +145,41 @@ u32 mt7601u_rr(struct mt7601u_dev *dev, u32 offset)
dev_err(dev->dev, "Error: wrong size read:%d off:%08x\n",
ret, offset);
- mutex_unlock(&dev->vendor_req_mutex);
-
trace_reg_read(dev, offset, val);
return val;
}
-int mt7601u_vendor_single_wr(struct mt7601u_dev *dev, const u8 req,
- const u16 offset, const u32 val)
+u32 mt7601u_rr(struct mt7601u_dev *dev, u32 offset)
{
- int ret;
+ u32 ret;
mutex_lock(&dev->vendor_req_mutex);
+ ret = __mt7601u_rr(dev, offset);
+ mutex_unlock(&dev->vendor_req_mutex);
- ret = mt7601u_vendor_request(dev, req, USB_DIR_OUT,
- val & 0xffff, offset, NULL, 0);
+ return ret;
+}
+
+/* should be called with vendor_req_mutex held */
+static int __mt7601u_vendor_single_wr(struct mt7601u_dev *dev, const u8 req,
+ const u16 offset, const u32 val)
+{
+ int ret = mt7601u_vendor_request(dev, req, USB_DIR_OUT,
+ val & 0xffff, offset, NULL, 0);
if (!ret)
ret = mt7601u_vendor_request(dev, req, USB_DIR_OUT,
val >> 16, offset + 2, NULL, 0);
+ trace_reg_write(dev, offset, val);
+ return ret;
+}
+
+int mt7601u_vendor_single_wr(struct mt7601u_dev *dev, const u8 req,
+ const u16 offset, const u32 val)
+{
+ int ret;
+ mutex_lock(&dev->vendor_req_mutex);
+ ret = __mt7601u_vendor_single_wr(dev, req, offset, val);
mutex_unlock(&dev->vendor_req_mutex);
return ret;
@@ -175,23 +190,30 @@ void mt7601u_wr(struct mt7601u_dev *dev, u32 offset, u32 val)
WARN_ONCE(offset > USHRT_MAX, "write high off:%08x", offset);
mt7601u_vendor_single_wr(dev, MT_VEND_WRITE, offset, val);
- trace_reg_write(dev, offset, val);
}
u32 mt7601u_rmw(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val)
{
- val |= mt7601u_rr(dev, offset) & ~mask;
- mt7601u_wr(dev, offset, val);
+ mutex_lock(&dev->vendor_req_mutex);
+ val |= __mt7601u_rr(dev, offset) & ~mask;
+ __mt7601u_vendor_single_wr(dev, MT_VEND_WRITE, offset, val);
+ mutex_unlock(&dev->vendor_req_mutex);
+
return val;
}
u32 mt7601u_rmc(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val)
{
- u32 reg = mt7601u_rr(dev, offset);
+ u32 reg;
+ mutex_lock(&dev->vendor_req_mutex);
+ reg = __mt7601u_rr(dev, offset);
val |= reg & ~mask;
if (reg != val)
- mt7601u_wr(dev, offset, val);
+ __mt7601u_vendor_single_wr(dev, MT_VEND_WRITE,
+ offset, val);
+ mutex_unlock(&dev->vendor_req_mutex);
+
return val;
}
diff --git a/drivers/net/wireless/quantenna/Kconfig b/drivers/net/wireless/quantenna/Kconfig
index 30943656e989..de84ce125c26 100644
--- a/drivers/net/wireless/quantenna/Kconfig
+++ b/drivers/net/wireless/quantenna/Kconfig
@@ -5,8 +5,8 @@ config WLAN_VENDOR_QUANTENNA
If you have a wireless card belonging to this class, say Y.
Note that the answer to this question doesn't directly affect the
- kernel: saying N will just cause the configurator to skip all
- the questions about cards. If you say Y, you will be asked for
+ kernel: saying N will just cause the configurator to skip all the
+ questions about these cards. If you say Y, you will be asked for
your specific card in the following questions.
if WLAN_VENDOR_QUANTENNA
diff --git a/drivers/net/wireless/quantenna/qtnfmac/bus.h b/drivers/net/wireless/quantenna/qtnfmac/bus.h
index 56e5fed92a2a..0a1604683bab 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/bus.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/bus.h
@@ -59,8 +59,9 @@ struct qtnf_bus {
char fwname[32];
struct napi_struct mux_napi;
struct net_device mux_dev;
- struct completion request_firmware_complete;
+ struct completion firmware_init_complete;
struct workqueue_struct *workqueue;
+ struct work_struct fw_work;
struct work_struct event_work;
struct mutex bus_lock; /* lock during command/event processing */
struct dentry *dbg_dir;
diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c
index 6f6190964320..f117904d9120 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c
@@ -127,7 +127,7 @@ static inline void qtnf_dis_txdone_irq(struct qtnf_pcie_bus_priv *priv)
spin_unlock_irqrestore(&priv->irq_lock, flags);
}
-static int qtnf_pcie_init_irq(struct qtnf_pcie_bus_priv *priv)
+static void qtnf_pcie_init_irq(struct qtnf_pcie_bus_priv *priv)
{
struct pci_dev *pdev = priv->pdev;
@@ -148,8 +148,6 @@ static int qtnf_pcie_init_irq(struct qtnf_pcie_bus_priv *priv)
pr_warn("legacy PCIE interrupts enabled\n");
pci_intx(pdev, 1);
}
-
- return 0;
}
static void qtnf_deassert_intx(struct qtnf_pcie_bus_priv *priv)
@@ -162,6 +160,17 @@ static void qtnf_deassert_intx(struct qtnf_pcie_bus_priv *priv)
qtnf_non_posted_write(cfg, reg);
}
+static void qtnf_reset_card(struct qtnf_pcie_bus_priv *priv)
+{
+ const u32 data = QTN_PEARL_IPC_IRQ_WORD(QTN_PEARL_LHOST_EP_RESET);
+ void __iomem *reg = priv->sysctl_bar +
+ QTN_PEARL_SYSCTL_LHOST_IRQ_OFFSET;
+
+ qtnf_non_posted_write(data, reg);
+ msleep(QTN_EP_RESET_WAIT_MS);
+ pci_restore_state(priv->pdev);
+}
+
static void qtnf_ipc_gen_ep_int(void *arg)
{
const struct qtnf_pcie_bus_priv *priv = arg;
@@ -478,10 +487,11 @@ static int alloc_rx_buffers(struct qtnf_pcie_bus_priv *priv)
}
/* all rx/tx activity should have ceased before calling this function */
-static void free_xfer_buffers(void *data)
+static void qtnf_free_xfer_buffers(struct qtnf_pcie_bus_priv *priv)
{
- struct qtnf_pcie_bus_priv *priv = (struct qtnf_pcie_bus_priv *)data;
+ struct qtnf_tx_bd *txbd;
struct qtnf_rx_bd *rxbd;
+ struct sk_buff *skb;
dma_addr_t paddr;
int i;
@@ -489,19 +499,26 @@ static void free_xfer_buffers(void *data)
for (i = 0; i < priv->rx_bd_num; i++) {
if (priv->rx_skb && priv->rx_skb[i]) {
rxbd = &priv->rx_bd_vbase[i];
+ skb = priv->rx_skb[i];
paddr = QTN_HOST_ADDR(le32_to_cpu(rxbd->addr_h),
le32_to_cpu(rxbd->addr));
pci_unmap_single(priv->pdev, paddr, SKB_BUF_SIZE,
PCI_DMA_FROMDEVICE);
-
- dev_kfree_skb_any(priv->rx_skb[i]);
+ dev_kfree_skb_any(skb);
+ priv->rx_skb[i] = NULL;
}
}
/* free tx buffers */
for (i = 0; i < priv->tx_bd_num; i++) {
if (priv->tx_skb && priv->tx_skb[i]) {
- dev_kfree_skb_any(priv->tx_skb[i]);
+ txbd = &priv->tx_bd_vbase[i];
+ skb = priv->tx_skb[i];
+ paddr = QTN_HOST_ADDR(le32_to_cpu(txbd->addr_h),
+ le32_to_cpu(txbd->addr));
+ pci_unmap_single(priv->pdev, paddr, skb->len,
+ PCI_DMA_TODEVICE);
+ dev_kfree_skb_any(skb);
priv->tx_skb[i] = NULL;
}
}
@@ -937,6 +954,98 @@ static const struct qtnf_bus_ops qtnf_pcie_bus_ops = {
.data_rx_stop = qtnf_pcie_data_rx_stop,
};
+static int qtnf_dbg_mps_show(struct seq_file *s, void *data)
+{
+ struct qtnf_bus *bus = dev_get_drvdata(s->private);
+ struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus);
+
+ seq_printf(s, "%d\n", priv->mps);
+
+ return 0;
+}
+
+static int qtnf_dbg_msi_show(struct seq_file *s, void *data)
+{
+ struct qtnf_bus *bus = dev_get_drvdata(s->private);
+ struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus);
+
+ seq_printf(s, "%u\n", priv->msi_enabled);
+
+ return 0;
+}
+
+static int qtnf_dbg_irq_stats(struct seq_file *s, void *data)
+{
+ struct qtnf_bus *bus = dev_get_drvdata(s->private);
+ struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus);
+ u32 reg = readl(PCIE_HDP_INT_EN(priv->pcie_reg_base));
+ u32 status;
+
+ seq_printf(s, "pcie_irq_count(%u)\n", priv->pcie_irq_count);
+ seq_printf(s, "pcie_irq_tx_count(%u)\n", priv->pcie_irq_tx_count);
+ status = reg & PCIE_HDP_INT_TX_BITS;
+ seq_printf(s, "pcie_irq_tx_status(%s)\n",
+ (status == PCIE_HDP_INT_TX_BITS) ? "EN" : "DIS");
+ seq_printf(s, "pcie_irq_rx_count(%u)\n", priv->pcie_irq_rx_count);
+ status = reg & PCIE_HDP_INT_RX_BITS;
+ seq_printf(s, "pcie_irq_rx_status(%s)\n",
+ (status == PCIE_HDP_INT_RX_BITS) ? "EN" : "DIS");
+ seq_printf(s, "pcie_irq_uf_count(%u)\n", priv->pcie_irq_uf_count);
+ status = reg & PCIE_HDP_INT_HHBM_UF;
+ seq_printf(s, "pcie_irq_hhbm_uf_status(%s)\n",
+ (status == PCIE_HDP_INT_HHBM_UF) ? "EN" : "DIS");
+
+ return 0;
+}
+
+static int qtnf_dbg_hdp_stats(struct seq_file *s, void *data)
+{
+ struct qtnf_bus *bus = dev_get_drvdata(s->private);
+ struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus);
+
+ seq_printf(s, "tx_full_count(%u)\n", priv->tx_full_count);
+ seq_printf(s, "tx_done_count(%u)\n", priv->tx_done_count);
+ seq_printf(s, "tx_reclaim_done(%u)\n", priv->tx_reclaim_done);
+ seq_printf(s, "tx_reclaim_req(%u)\n", priv->tx_reclaim_req);
+
+ seq_printf(s, "tx_bd_r_index(%u)\n", priv->tx_bd_r_index);
+ seq_printf(s, "tx_bd_p_index(%u)\n",
+ readl(PCIE_HDP_RX0DMA_CNT(priv->pcie_reg_base))
+ & (priv->tx_bd_num - 1));
+ seq_printf(s, "tx_bd_w_index(%u)\n", priv->tx_bd_w_index);
+ seq_printf(s, "tx queue len(%u)\n",
+ CIRC_CNT(priv->tx_bd_w_index, priv->tx_bd_r_index,
+ priv->tx_bd_num));
+
+ seq_printf(s, "rx_bd_r_index(%u)\n", priv->rx_bd_r_index);
+ seq_printf(s, "rx_bd_p_index(%u)\n",
+ readl(PCIE_HDP_TX0DMA_CNT(priv->pcie_reg_base))
+ & (priv->rx_bd_num - 1));
+ seq_printf(s, "rx_bd_w_index(%u)\n", priv->rx_bd_w_index);
+ seq_printf(s, "rx alloc queue len(%u)\n",
+ CIRC_SPACE(priv->rx_bd_w_index, priv->rx_bd_r_index,
+ priv->rx_bd_num));
+
+ return 0;
+}
+
+static int qtnf_dbg_shm_stats(struct seq_file *s, void *data)
+{
+ struct qtnf_bus *bus = dev_get_drvdata(s->private);
+ struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus);
+
+ seq_printf(s, "shm_ipc_ep_in.tx_packet_count(%zu)\n",
+ priv->shm_ipc_ep_in.tx_packet_count);
+ seq_printf(s, "shm_ipc_ep_in.rx_packet_count(%zu)\n",
+ priv->shm_ipc_ep_in.rx_packet_count);
+ seq_printf(s, "shm_ipc_ep_out.tx_packet_count(%zu)\n",
+ priv->shm_ipc_ep_out.tx_timeout_count);
+ seq_printf(s, "shm_ipc_ep_out.rx_packet_count(%zu)\n",
+ priv->shm_ipc_ep_out.rx_packet_count);
+
+ return 0;
+}
+
static int qtnf_ep_fw_send(struct qtnf_pcie_bus_priv *priv, uint32_t size,
int blk, const u8 *pblk, const u8 *fw)
{
@@ -1052,181 +1161,102 @@ qtnf_ep_fw_load(struct qtnf_pcie_bus_priv *priv, const u8 *fw, u32 fw_size)
return 0;
}
-static void qtnf_firmware_load(const struct firmware *fw, void *context)
-{
- struct qtnf_pcie_bus_priv *priv = (void *)context;
- struct pci_dev *pdev = priv->pdev;
- struct qtnf_bus *bus = pci_get_drvdata(pdev);
- int ret;
-
- if (!fw) {
- pr_err("failed to get firmware %s\n", bus->fwname);
- goto fw_load_err;
- }
-
- ret = qtnf_ep_fw_load(priv, fw->data, fw->size);
- if (ret) {
- pr_err("FW upload error\n");
- goto fw_load_err;
- }
-
- if (qtnf_poll_state(&priv->bda->bda_ep_state, QTN_EP_FW_DONE,
- QTN_FW_DL_TIMEOUT_MS)) {
- pr_err("FW bringup timed out\n");
- goto fw_load_err;
- }
-
- bus->fw_state = QTNF_FW_STATE_FW_DNLD_DONE;
- pr_info("firmware is up and running\n");
-
-fw_load_err:
-
- if (fw)
- release_firmware(fw);
-
- complete(&bus->request_firmware_complete);
-}
-
-static int qtnf_bringup_fw(struct qtnf_bus *bus)
+static void qtnf_fw_work_handler(struct work_struct *work)
{
+ struct qtnf_bus *bus = container_of(work, struct qtnf_bus, fw_work);
struct qtnf_pcie_bus_priv *priv = (void *)get_bus_priv(bus);
struct pci_dev *pdev = priv->pdev;
+ const struct firmware *fw;
int ret;
u32 state = QTN_RC_FW_LOADRDY | QTN_RC_FW_QLINK;
- if (flashboot)
+ if (flashboot) {
state |= QTN_RC_FW_FLASHBOOT;
+ } else {
+ ret = request_firmware(&fw, bus->fwname, &pdev->dev);
+ if (ret < 0) {
+ pr_err("failed to get firmware %s\n", bus->fwname);
+ goto fw_load_fail;
+ }
+ }
qtnf_set_state(&priv->bda->bda_rc_state, state);
if (qtnf_poll_state(&priv->bda->bda_ep_state, QTN_EP_FW_LOADRDY,
QTN_FW_DL_TIMEOUT_MS)) {
pr_err("card is not ready\n");
- return -ETIMEDOUT;
+ goto fw_load_fail;
}
qtnf_clear_state(&priv->bda->bda_ep_state, QTN_EP_FW_LOADRDY);
if (flashboot) {
- pr_info("Booting FW from flash\n");
-
- if (!qtnf_poll_state(&priv->bda->bda_ep_state, QTN_EP_FW_DONE,
- QTN_FW_DL_TIMEOUT_MS))
- bus->fw_state = QTNF_FW_STATE_FW_DNLD_DONE;
+ pr_info("booting firmware from flash\n");
+ } else {
+ pr_info("starting firmware upload: %s\n", bus->fwname);
- return 0;
+ ret = qtnf_ep_fw_load(priv, fw->data, fw->size);
+ release_firmware(fw);
+ if (ret) {
+ pr_err("firmware upload error\n");
+ goto fw_load_fail;
+ }
}
- pr_info("starting firmware upload: %s\n", bus->fwname);
-
- ret = request_firmware_nowait(THIS_MODULE, 1, bus->fwname, &pdev->dev,
- GFP_KERNEL, priv, qtnf_firmware_load);
- if (ret < 0)
- pr_err("request_firmware_nowait error %d\n", ret);
- else
- ret = 1;
-
- return ret;
-}
-
-static void qtnf_reclaim_tasklet_fn(unsigned long data)
-{
- struct qtnf_pcie_bus_priv *priv = (void *)data;
-
- qtnf_pcie_data_tx_reclaim(priv);
- qtnf_en_txdone_irq(priv);
-}
-
-static int qtnf_dbg_mps_show(struct seq_file *s, void *data)
-{
- struct qtnf_bus *bus = dev_get_drvdata(s->private);
- struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus);
+ if (qtnf_poll_state(&priv->bda->bda_ep_state, QTN_EP_FW_DONE,
+ QTN_FW_DL_TIMEOUT_MS)) {
+ pr_err("firmware bringup timed out\n");
+ goto fw_load_fail;
+ }
- seq_printf(s, "%d\n", priv->mps);
+ bus->fw_state = QTNF_FW_STATE_FW_DNLD_DONE;
+ pr_info("firmware is up and running\n");
- return 0;
-}
+ if (qtnf_poll_state(&priv->bda->bda_ep_state,
+ QTN_EP_FW_QLINK_DONE, QTN_FW_QLINK_TIMEOUT_MS)) {
+ pr_err("firmware runtime failure\n");
+ goto fw_load_fail;
+ }
-static int qtnf_dbg_msi_show(struct seq_file *s, void *data)
-{
- struct qtnf_bus *bus = dev_get_drvdata(s->private);
- struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus);
+ ret = qtnf_core_attach(bus);
+ if (ret) {
+ pr_err("failed to attach core\n");
+ goto fw_load_fail;
+ }
- seq_printf(s, "%u\n", priv->msi_enabled);
+ qtnf_debugfs_init(bus, DRV_NAME);
+ qtnf_debugfs_add_entry(bus, "mps", qtnf_dbg_mps_show);
+ qtnf_debugfs_add_entry(bus, "msi_enabled", qtnf_dbg_msi_show);
+ qtnf_debugfs_add_entry(bus, "hdp_stats", qtnf_dbg_hdp_stats);
+ qtnf_debugfs_add_entry(bus, "irq_stats", qtnf_dbg_irq_stats);
+ qtnf_debugfs_add_entry(bus, "shm_stats", qtnf_dbg_shm_stats);
- return 0;
-}
+ goto fw_load_exit;
-static int qtnf_dbg_irq_stats(struct seq_file *s, void *data)
-{
- struct qtnf_bus *bus = dev_get_drvdata(s->private);
- struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus);
- u32 reg = readl(PCIE_HDP_INT_EN(priv->pcie_reg_base));
- u32 status;
+fw_load_fail:
+ bus->fw_state = QTNF_FW_STATE_DEAD;
- seq_printf(s, "pcie_irq_count(%u)\n", priv->pcie_irq_count);
- seq_printf(s, "pcie_irq_tx_count(%u)\n", priv->pcie_irq_tx_count);
- status = reg & PCIE_HDP_INT_TX_BITS;
- seq_printf(s, "pcie_irq_tx_status(%s)\n",
- (status == PCIE_HDP_INT_TX_BITS) ? "EN" : "DIS");
- seq_printf(s, "pcie_irq_rx_count(%u)\n", priv->pcie_irq_rx_count);
- status = reg & PCIE_HDP_INT_RX_BITS;
- seq_printf(s, "pcie_irq_rx_status(%s)\n",
- (status == PCIE_HDP_INT_RX_BITS) ? "EN" : "DIS");
- seq_printf(s, "pcie_irq_uf_count(%u)\n", priv->pcie_irq_uf_count);
- status = reg & PCIE_HDP_INT_HHBM_UF;
- seq_printf(s, "pcie_irq_hhbm_uf_status(%s)\n",
- (status == PCIE_HDP_INT_HHBM_UF) ? "EN" : "DIS");
-
- return 0;
+fw_load_exit:
+ complete(&bus->firmware_init_complete);
+ put_device(&pdev->dev);
}
-static int qtnf_dbg_hdp_stats(struct seq_file *s, void *data)
+static void qtnf_bringup_fw_async(struct qtnf_bus *bus)
{
- struct qtnf_bus *bus = dev_get_drvdata(s->private);
- struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus);
-
- seq_printf(s, "tx_full_count(%u)\n", priv->tx_full_count);
- seq_printf(s, "tx_done_count(%u)\n", priv->tx_done_count);
- seq_printf(s, "tx_reclaim_done(%u)\n", priv->tx_reclaim_done);
- seq_printf(s, "tx_reclaim_req(%u)\n", priv->tx_reclaim_req);
-
- seq_printf(s, "tx_bd_r_index(%u)\n", priv->tx_bd_r_index);
- seq_printf(s, "tx_bd_p_index(%u)\n",
- readl(PCIE_HDP_RX0DMA_CNT(priv->pcie_reg_base))
- & (priv->tx_bd_num - 1));
- seq_printf(s, "tx_bd_w_index(%u)\n", priv->tx_bd_w_index);
- seq_printf(s, "tx queue len(%u)\n",
- CIRC_CNT(priv->tx_bd_w_index, priv->tx_bd_r_index,
- priv->tx_bd_num));
-
- seq_printf(s, "rx_bd_r_index(%u)\n", priv->rx_bd_r_index);
- seq_printf(s, "rx_bd_p_index(%u)\n",
- readl(PCIE_HDP_TX0DMA_CNT(priv->pcie_reg_base))
- & (priv->rx_bd_num - 1));
- seq_printf(s, "rx_bd_w_index(%u)\n", priv->rx_bd_w_index);
- seq_printf(s, "rx alloc queue len(%u)\n",
- CIRC_SPACE(priv->rx_bd_w_index, priv->rx_bd_r_index,
- priv->rx_bd_num));
+ struct qtnf_pcie_bus_priv *priv = (void *)get_bus_priv(bus);
+ struct pci_dev *pdev = priv->pdev;
- return 0;
+ get_device(&pdev->dev);
+ INIT_WORK(&bus->fw_work, qtnf_fw_work_handler);
+ schedule_work(&bus->fw_work);
}
-static int qtnf_dbg_shm_stats(struct seq_file *s, void *data)
+static void qtnf_reclaim_tasklet_fn(unsigned long data)
{
- struct qtnf_bus *bus = dev_get_drvdata(s->private);
- struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus);
-
- seq_printf(s, "shm_ipc_ep_in.tx_packet_count(%zu)\n",
- priv->shm_ipc_ep_in.tx_packet_count);
- seq_printf(s, "shm_ipc_ep_in.rx_packet_count(%zu)\n",
- priv->shm_ipc_ep_in.rx_packet_count);
- seq_printf(s, "shm_ipc_ep_out.tx_packet_count(%zu)\n",
- priv->shm_ipc_ep_out.tx_timeout_count);
- seq_printf(s, "shm_ipc_ep_out.rx_packet_count(%zu)\n",
- priv->shm_ipc_ep_out.rx_packet_count);
+ struct qtnf_pcie_bus_priv *priv = (void *)data;
- return 0;
+ qtnf_pcie_data_tx_reclaim(priv);
+ qtnf_en_txdone_irq(priv);
}
static int qtnf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
@@ -1237,10 +1267,8 @@ static int qtnf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
bus = devm_kzalloc(&pdev->dev,
sizeof(*bus) + sizeof(*pcie_priv), GFP_KERNEL);
- if (!bus) {
- ret = -ENOMEM;
- goto err_init;
- }
+ if (!bus)
+ return -ENOMEM;
pcie_priv = get_bus_priv(bus);
@@ -1251,7 +1279,7 @@ static int qtnf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
pcie_priv->pdev = pdev;
strcpy(bus->fwname, QTN_PCI_PEARL_FW_NAME);
- init_completion(&bus->request_firmware_complete);
+ init_completion(&bus->firmware_init_complete);
mutex_init(&bus->bus_lock);
spin_lock_init(&pcie_priv->tx0_lock);
spin_lock_init(&pcie_priv->irq_lock);
@@ -1267,11 +1295,18 @@ static int qtnf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
pcie_priv->tx_reclaim_done = 0;
pcie_priv->tx_reclaim_req = 0;
+ tasklet_init(&pcie_priv->reclaim_tq, qtnf_reclaim_tasklet_fn,
+ (unsigned long)pcie_priv);
+
+ init_dummy_netdev(&bus->mux_dev);
+ netif_napi_add(&bus->mux_dev, &bus->mux_napi,
+ qtnf_rx_poll, 10);
+
pcie_priv->workqueue = create_singlethread_workqueue("QTNF_PEARL_PCIE");
if (!pcie_priv->workqueue) {
pr_err("failed to alloc bus workqueue\n");
ret = -ENODEV;
- goto err_priv;
+ goto err_init;
}
if (!pci_is_pcie(pdev)) {
@@ -1300,14 +1335,8 @@ static int qtnf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto err_base;
}
- pcim_pin_device(pdev);
pci_set_master(pdev);
-
- ret = qtnf_pcie_init_irq(pcie_priv);
- if (ret < 0) {
- pr_err("irq init failed\n");
- goto err_base;
- }
+ qtnf_pcie_init_irq(pcie_priv);
ret = qtnf_pcie_init_memory(pcie_priv);
if (ret < 0) {
@@ -1315,22 +1344,18 @@ static int qtnf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto err_base;
}
+ pci_save_state(pdev);
+
ret = qtnf_pcie_init_shm_ipc(pcie_priv);
if (ret < 0) {
pr_err("PCIE SHM IPC init failed\n");
goto err_base;
}
- ret = devm_add_action(&pdev->dev, free_xfer_buffers, (void *)pcie_priv);
- if (ret) {
- pr_err("custom release callback init failed\n");
- goto err_base;
- }
-
ret = qtnf_pcie_init_xfer(pcie_priv);
if (ret) {
pr_err("PCIE xfer init failed\n");
- goto err_base;
+ goto err_ipc;
}
/* init default irq settings */
@@ -1343,58 +1368,28 @@ static int qtnf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
"qtnf_pcie_irq", (void *)bus);
if (ret) {
pr_err("failed to request pcie irq %d\n", pdev->irq);
- goto err_base;
- }
-
- tasklet_init(&pcie_priv->reclaim_tq, qtnf_reclaim_tasklet_fn,
- (unsigned long)pcie_priv);
- init_dummy_netdev(&bus->mux_dev);
- netif_napi_add(&bus->mux_dev, &bus->mux_napi,
- qtnf_rx_poll, 10);
-
- ret = qtnf_bringup_fw(bus);
- if (ret < 0)
- goto err_bringup_fw;
- else if (ret)
- wait_for_completion(&bus->request_firmware_complete);
-
- if (bus->fw_state != QTNF_FW_STATE_FW_DNLD_DONE) {
- pr_err("failed to start FW\n");
- goto err_bringup_fw;
- }
-
- if (qtnf_poll_state(&pcie_priv->bda->bda_ep_state, QTN_EP_FW_QLINK_DONE,
- QTN_FW_QLINK_TIMEOUT_MS)) {
- pr_err("FW runtime failure\n");
- goto err_bringup_fw;
+ goto err_xfer;
}
- ret = qtnf_core_attach(bus);
- if (ret) {
- pr_err("failed to attach core\n");
- goto err_bringup_fw;
- }
-
- qtnf_debugfs_init(bus, DRV_NAME);
- qtnf_debugfs_add_entry(bus, "mps", qtnf_dbg_mps_show);
- qtnf_debugfs_add_entry(bus, "msi_enabled", qtnf_dbg_msi_show);
- qtnf_debugfs_add_entry(bus, "hdp_stats", qtnf_dbg_hdp_stats);
- qtnf_debugfs_add_entry(bus, "irq_stats", qtnf_dbg_irq_stats);
- qtnf_debugfs_add_entry(bus, "shm_stats", qtnf_dbg_shm_stats);
+ qtnf_bringup_fw_async(bus);
return 0;
-err_bringup_fw:
- netif_napi_del(&bus->mux_napi);
+err_xfer:
+ qtnf_free_xfer_buffers(pcie_priv);
+
+err_ipc:
+ qtnf_pcie_free_shm_ipc(pcie_priv);
err_base:
flush_workqueue(pcie_priv->workqueue);
destroy_workqueue(pcie_priv->workqueue);
+ netif_napi_del(&bus->mux_napi);
-err_priv:
+err_init:
+ tasklet_kill(&pcie_priv->reclaim_tq);
pci_set_drvdata(pdev, NULL);
-err_init:
return ret;
}
@@ -1407,18 +1402,23 @@ static void qtnf_pcie_remove(struct pci_dev *pdev)
if (!bus)
return;
+ wait_for_completion(&bus->firmware_init_complete);
+
+ if (bus->fw_state == QTNF_FW_STATE_ACTIVE)
+ qtnf_core_detach(bus);
+
priv = get_bus_priv(bus);
- qtnf_core_detach(bus);
netif_napi_del(&bus->mux_napi);
-
flush_workqueue(priv->workqueue);
destroy_workqueue(priv->workqueue);
tasklet_kill(&priv->reclaim_tq);
+ qtnf_free_xfer_buffers(priv);
qtnf_debugfs_remove(bus);
qtnf_pcie_free_shm_ipc(priv);
+ qtnf_reset_card(priv);
}
#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_ipc.h b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_ipc.h
index c5a4e46d26ef..00bb21a1c47a 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_ipc.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_ipc.h
@@ -46,6 +46,7 @@
/* state transition timeouts */
#define QTN_FW_DL_TIMEOUT_MS 3000
#define QTN_FW_QLINK_TIMEOUT_MS 30000
+#define QTN_EP_RESET_WAIT_MS 1000
#define PCIE_HDP_INT_RX_BITS (0 \
| PCIE_HDP_INT_EP_TXDMA \
diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_regs_pearl.h b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_regs_pearl.h
index 5b48b425fa7f..0bfe285b6b48 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_regs_pearl.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_regs_pearl.h
@@ -351,5 +351,6 @@
#define QTN_PEARL_IPC_IRQ_WORD(irq) (BIT(irq) | BIT(irq + 16))
#define QTN_PEARL_LHOST_IPC_IRQ (6)
+#define QTN_PEARL_LHOST_EP_RESET (7)
#endif /* __PEARL_PCIE_H */
diff --git a/drivers/net/wireless/ralink/Kconfig b/drivers/net/wireless/ralink/Kconfig
index 41dbf3130e2b..9b79e59ee97b 100644
--- a/drivers/net/wireless/ralink/Kconfig
+++ b/drivers/net/wireless/ralink/Kconfig
@@ -5,8 +5,8 @@ config WLAN_VENDOR_RALINK
If you have a wireless card belonging to this class, say Y.
Note that the answer to this question doesn't directly affect the
- kernel: saying N will just cause the configurator to skip all
- the questions about cards. If you say Y, you will be asked for
+ kernel: saying N will just cause the configurator to skip all the
+ questions about these cards. If you say Y, you will be asked for
your specific card in the following questions.
if WLAN_VENDOR_RALINK
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2500usb.c b/drivers/net/wireless/ralink/rt2x00/rt2500usb.c
index f4b48b77c491..3df8c4b895e7 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2500usb.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2500usb.c
@@ -37,7 +37,7 @@
* Allow hardware encryption to be disabled.
*/
static bool modparam_nohwcrypt;
-module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO);
+module_param_named(nohwcrypt, modparam_nohwcrypt, bool, 0444);
MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
/*
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800pci.c b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
index 5cf655ff1430..1172eefd1c1a 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
@@ -49,7 +49,7 @@
* Allow hardware encryption to be disabled.
*/
static bool modparam_nohwcrypt = false;
-module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO);
+module_param_named(nohwcrypt, modparam_nohwcrypt, bool, 0444);
MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
static bool rt2800pci_hwcrypt_disabled(struct rt2x00_dev *rt2x00dev)
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800soc.c b/drivers/net/wireless/ralink/rt2x00/rt2800soc.c
index a985a5a7945e..6848ebc83534 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800soc.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800soc.c
@@ -41,7 +41,7 @@
/* Allow hardware encryption to be disabled. */
static bool modparam_nohwcrypt;
-module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO);
+module_param_named(nohwcrypt, modparam_nohwcrypt, bool, 0444);
MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
static bool rt2800soc_hwcrypt_disabled(struct rt2x00_dev *rt2x00dev)
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
index 24fc6d2045ef..d901a41d36e4 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
@@ -43,7 +43,7 @@
* Allow hardware encryption to be disabled.
*/
static bool modparam_nohwcrypt;
-module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO);
+module_param_named(nohwcrypt, modparam_nohwcrypt, bool, 0444);
MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
static bool rt2800usb_hwcrypt_disabled(struct rt2x00_dev *rt2x00dev)
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00debug.c b/drivers/net/wireless/ralink/rt2x00/rt2x00debug.c
index ac2572943ed0..0eee479583b8 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00debug.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00debug.c
@@ -606,7 +606,7 @@ static struct dentry *rt2x00debug_create_file_driver(const char *name,
data += sprintf(data, "version:\t%s\n", DRV_VERSION);
blob->size = strlen(blob->data);
- return debugfs_create_blob(name, S_IRUSR, intf->driver_folder, blob);
+ return debugfs_create_blob(name, 0400, intf->driver_folder, blob);
}
static struct dentry *rt2x00debug_create_file_chipset(const char *name,
@@ -647,7 +647,7 @@ static struct dentry *rt2x00debug_create_file_chipset(const char *name,
blob->size = strlen(blob->data);
- return debugfs_create_blob(name, S_IRUSR, intf->driver_folder, blob);
+ return debugfs_create_blob(name, 0400, intf->driver_folder, blob);
}
void rt2x00debug_register(struct rt2x00_dev *rt2x00dev)
@@ -682,13 +682,13 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev)
if (IS_ERR(intf->chipset_entry) || !intf->chipset_entry)
goto exit;
- intf->dev_flags = debugfs_create_file("dev_flags", S_IRUSR,
+ intf->dev_flags = debugfs_create_file("dev_flags", 0400,
intf->driver_folder, intf,
&rt2x00debug_fop_dev_flags);
if (IS_ERR(intf->dev_flags) || !intf->dev_flags)
goto exit;
- intf->cap_flags = debugfs_create_file("cap_flags", S_IRUSR,
+ intf->cap_flags = debugfs_create_file("cap_flags", 0400,
intf->driver_folder, intf,
&rt2x00debug_fop_cap_flags);
if (IS_ERR(intf->cap_flags) || !intf->cap_flags)
@@ -699,27 +699,28 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev)
if (IS_ERR(intf->register_folder) || !intf->register_folder)
goto exit;
-#define RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(__intf, __name) \
-({ \
- if (debug->__name.read) { \
- (__intf)->__name##_off_entry = \
- debugfs_create_u32(__stringify(__name) "_offset", \
- S_IRUSR | S_IWUSR, \
- (__intf)->register_folder, \
- &(__intf)->offset_##__name); \
- if (IS_ERR((__intf)->__name##_off_entry) \
- || !(__intf)->__name##_off_entry) \
- goto exit; \
- \
- (__intf)->__name##_val_entry = \
- debugfs_create_file(__stringify(__name) "_value", \
- S_IRUSR | S_IWUSR, \
- (__intf)->register_folder, \
- (__intf), &rt2x00debug_fop_##__name); \
- if (IS_ERR((__intf)->__name##_val_entry) \
- || !(__intf)->__name##_val_entry) \
- goto exit; \
- } \
+#define RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(__intf, __name) \
+({ \
+ if (debug->__name.read) { \
+ (__intf)->__name##_off_entry = \
+ debugfs_create_u32(__stringify(__name) "_offset", \
+ 0600, \
+ (__intf)->register_folder, \
+ &(__intf)->offset_##__name); \
+ if (IS_ERR((__intf)->__name##_off_entry) || \
+ !(__intf)->__name##_off_entry) \
+ goto exit; \
+ \
+ (__intf)->__name##_val_entry = \
+ debugfs_create_file(__stringify(__name) "_value", \
+ 0600, \
+ (__intf)->register_folder, \
+ (__intf), \
+ &rt2x00debug_fop_##__name); \
+ if (IS_ERR((__intf)->__name##_val_entry) || \
+ !(__intf)->__name##_val_entry) \
+ goto exit; \
+ } \
})
RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, csr);
@@ -736,8 +737,8 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev)
goto exit;
intf->queue_frame_dump_entry =
- debugfs_create_file("dump", S_IRUSR, intf->queue_folder,
- intf, &rt2x00debug_fop_queue_dump);
+ debugfs_create_file("dump", 0400, intf->queue_folder,
+ intf, &rt2x00debug_fop_queue_dump);
if (IS_ERR(intf->queue_frame_dump_entry)
|| !intf->queue_frame_dump_entry)
goto exit;
@@ -746,14 +747,15 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev)
init_waitqueue_head(&intf->frame_dump_waitqueue);
intf->queue_stats_entry =
- debugfs_create_file("queue", S_IRUSR, intf->queue_folder,
- intf, &rt2x00debug_fop_queue_stats);
+ debugfs_create_file("queue", 0400, intf->queue_folder,
+ intf, &rt2x00debug_fop_queue_stats);
#ifdef CONFIG_RT2X00_LIB_CRYPTO
if (rt2x00_has_cap_hw_crypto(rt2x00dev))
intf->crypto_stats_entry =
- debugfs_create_file("crypto", S_IRUGO, intf->queue_folder,
- intf, &rt2x00debug_fop_crypto_stats);
+ debugfs_create_file("crypto", 0444, intf->queue_folder,
+ intf,
+ &rt2x00debug_fop_crypto_stats);
#endif
return;
diff --git a/drivers/net/wireless/ralink/rt2x00/rt61pci.c b/drivers/net/wireless/ralink/rt2x00/rt61pci.c
index 234310200759..cb0e1196f2c2 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt61pci.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt61pci.c
@@ -40,7 +40,7 @@
* Allow hardware encryption to be disabled.
*/
static bool modparam_nohwcrypt = false;
-module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO);
+module_param_named(nohwcrypt, modparam_nohwcrypt, bool, 0444);
MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
/*
diff --git a/drivers/net/wireless/ralink/rt2x00/rt73usb.c b/drivers/net/wireless/ralink/rt2x00/rt73usb.c
index 9a212823f42c..319ec4f2d9d2 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt73usb.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt73usb.c
@@ -38,7 +38,7 @@
* Allow hardware encryption to be disabled.
*/
static bool modparam_nohwcrypt;
-module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO);
+module_param_named(nohwcrypt, modparam_nohwcrypt, bool, 0444);
MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
/*
diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c
index 0133fcd4601b..7f9b16b97ea3 100644
--- a/drivers/net/wireless/ray_cs.c
+++ b/drivers/net/wireless/ray_cs.c
@@ -2815,9 +2815,11 @@ static int __init init_ray_cs(void)
proc_mkdir("driver/ray_cs", NULL);
proc_create("driver/ray_cs/ray_cs", 0, NULL, &ray_cs_proc_fops);
- proc_create("driver/ray_cs/essid", S_IWUSR, NULL, &ray_cs_essid_proc_fops);
- proc_create_data("driver/ray_cs/net_type", S_IWUSR, NULL, &int_proc_fops, &net_type);
- proc_create_data("driver/ray_cs/translate", S_IWUSR, NULL, &int_proc_fops, &translate);
+ proc_create("driver/ray_cs/essid", 0200, NULL, &ray_cs_essid_proc_fops);
+ proc_create_data("driver/ray_cs/net_type", 0200, NULL, &int_proc_fops,
+ &net_type);
+ proc_create_data("driver/ray_cs/translate", 0200, NULL, &int_proc_fops,
+ &translate);
#endif
if (translate != 0)
translate = 1;
diff --git a/drivers/net/wireless/realtek/Kconfig b/drivers/net/wireless/realtek/Kconfig
index 8a8ba2003964..3db988e689d7 100644
--- a/drivers/net/wireless/realtek/Kconfig
+++ b/drivers/net/wireless/realtek/Kconfig
@@ -5,8 +5,8 @@ config WLAN_VENDOR_REALTEK
If you have a wireless card belonging to this class, say Y.
Note that the answer to this question doesn't directly affect the
- kernel: saying N will just cause the configurator to skip all
- the questions about cards. If you say Y, you will be asked for
+ kernel: saying N will just cause the configurator to skip all the
+ questions about these cards. If you say Y, you will be asked for
your specific card in the following questions.
if WLAN_VENDOR_REALTEK
diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c
index 121b94f09714..9a1d15b3ce45 100644
--- a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c
+++ b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c
@@ -1450,6 +1450,7 @@ static int rtl8187_probe(struct usb_interface *intf,
goto err_free_dev;
}
mutex_init(&priv->io_mutex);
+ mutex_init(&priv->conf_mutex);
SET_IEEE80211_DEV(dev, &intf->dev);
usb_set_intfdata(intf, dev);
@@ -1625,7 +1626,6 @@ static int rtl8187_probe(struct usb_interface *intf,
printk(KERN_ERR "rtl8187: Cannot register device\n");
goto err_free_dmabuf;
}
- mutex_init(&priv->conf_mutex);
skb_queue_head_init(&priv->b_tx_status.queue);
wiphy_info(dev->wiphy, "hwaddr %pM, %s V%d + %s, rfkill mask %d\n",
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
index 7806a4d2b1fc..718a73c623a7 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
@@ -4431,7 +4431,7 @@ void rtl8xxxu_gen1_init_aggregation(struct rtl8xxxu_priv *priv)
timeout = page_thresh;
else if (rtl8xxxu_dma_agg_pages <= 6)
dev_err(&priv->udev->dev,
- "%s: dma_agg_pages=%i too small, minium is 6\n",
+ "%s: dma_agg_pages=%i too small, minimum is 6\n",
__func__, rtl8xxxu_dma_agg_pages);
else
dev_err(&priv->udev->dev,
diff --git a/drivers/net/wireless/realtek/rtlwifi/base.c b/drivers/net/wireless/realtek/rtlwifi/base.c
index d6c03bd5cc65..762a29cdf7ad 100644
--- a/drivers/net/wireless/realtek/rtlwifi/base.c
+++ b/drivers/net/wireless/realtek/rtlwifi/base.c
@@ -244,6 +244,9 @@ static void _rtl_init_hw_vht_capab(struct ieee80211_hw *hw,
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
+ if (!(rtlpriv->cfg->spec_ver & RTL_SPEC_SUPPORT_VHT))
+ return;
+
if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE ||
rtlhal->hw_type == HARDWARE_TYPE_RTL8822BE) {
u16 mcs_map;
@@ -397,6 +400,7 @@ static void _rtl_init_mac80211(struct ieee80211_hw *hw)
ieee80211_hw_set(hw, MFP_CAPABLE);
ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
+ ieee80211_hw_set(hw, SUPPORT_FAST_XMIT);
/* swlps or hwlps has been set in diff chip in init_sw_vars */
if (rtlpriv->psc.swctrl_lps) {
@@ -886,8 +890,7 @@ static void _rtl_query_bandwidth_mode(struct ieee80211_hw *hw,
tcb_desc->packet_bw = HT_CHANNEL_WIDTH_20_40;
- if (rtlpriv->rtlhal.hw_type == HARDWARE_TYPE_RTL8812AE ||
- rtlpriv->rtlhal.hw_type == HARDWARE_TYPE_RTL8821AE) {
+ if (rtlpriv->cfg->spec_ver & RTL_SPEC_SUPPORT_VHT) {
if (mac->opmode == NL80211_IFTYPE_AP ||
mac->opmode == NL80211_IFTYPE_ADHOC ||
mac->opmode == NL80211_IFTYPE_MESH_POINT) {
@@ -1546,7 +1549,6 @@ u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx,
/* EAPOL is seens as in-4way */
rtlpriv->btcoexist.btc_info.in_4way = true;
rtlpriv->btcoexist.btc_info.in_4way_ts = jiffies;
- rtlpriv->btcoexist.btc_info.in_4way_ts = jiffies;
RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), DBG_DMESG,
"802.1X %s EAPOL pkt!!\n", (is_tx) ? "Tx" : "Rx");
@@ -1594,7 +1596,11 @@ static u16 rtl_get_tx_report_sn(struct ieee80211_hw *hw)
struct rtl_tx_report *tx_report = &rtlpriv->tx_report;
u16 sn;
- sn = atomic_inc_return(&tx_report->sn) & 0x0FFF;
+ /* SW_DEFINE[11:8] are reserved (driver fills zeros)
+ * SW_DEFINE[7:2] are used by driver
+ * SW_DEFINE[1:0] are reserved for firmware (driver fills zeros)
+ */
+ sn = (atomic_inc_return(&tx_report->sn) & 0x003F) << 2;
tx_report->last_sent_sn = sn;
tx_report->last_sent_time = jiffies;
@@ -1622,14 +1628,23 @@ void rtl_tx_report_handler(struct ieee80211_hw *hw, u8 *tmp_buf, u8 c2h_cmd_len)
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_tx_report *tx_report = &rtlpriv->tx_report;
u16 sn;
+ u8 st, retry;
- sn = ((tmp_buf[7] & 0x0F) << 8) | tmp_buf[6];
+ if (rtlpriv->cfg->spec_ver & RTL_SPEC_EXT_C2H) {
+ sn = GET_TX_REPORT_SN_V2(tmp_buf);
+ st = GET_TX_REPORT_ST_V2(tmp_buf);
+ retry = GET_TX_REPORT_RETRY_V2(tmp_buf);
+ } else {
+ sn = GET_TX_REPORT_SN_V1(tmp_buf);
+ st = GET_TX_REPORT_ST_V1(tmp_buf);
+ retry = GET_TX_REPORT_RETRY_V1(tmp_buf);
+ }
tx_report->last_recv_sn = sn;
RT_TRACE(rtlpriv, COMP_TX_REPORT, DBG_DMESG,
"Recv TX-Report st=0x%02X sn=0x%X retry=0x%X\n",
- tmp_buf[0], sn, tmp_buf[2]);
+ st, sn, retry);
}
EXPORT_SYMBOL_GPL(rtl_tx_report_handler);
@@ -1643,7 +1658,8 @@ bool rtl_check_tx_report_acked(struct ieee80211_hw *hw)
if (time_before(tx_report->last_sent_time + 3 * HZ, jiffies)) {
RT_TRACE(rtlpriv, COMP_TX_REPORT, DBG_WARNING,
- "Check TX-Report timeout!!\n");
+ "Check TX-Report timeout!! s_sn=0x%X r_sn=0x%X\n",
+ tx_report->last_sent_sn, tx_report->last_recv_sn);
return true; /* 3 sec. (timeout) seen as acked */
}
@@ -2629,6 +2645,11 @@ EXPORT_SYMBOL_GPL(rtl_global_var);
static int __init rtl_core_module_init(void)
{
+ BUILD_BUG_ON(TX_PWR_BY_RATE_NUM_RATE < TX_PWR_BY_RATE_NUM_SECTION);
+ BUILD_BUG_ON(MAX_RATE_SECTION_NUM != MAX_RATE_SECTION);
+ BUILD_BUG_ON(MAX_BASE_NUM_IN_PHY_REG_PG_24G != MAX_RATE_SECTION);
+ BUILD_BUG_ON(MAX_BASE_NUM_IN_PHY_REG_PG_5G != (MAX_RATE_SECTION - 1));
+
if (rtl_rate_control_register())
pr_err("rtl: Unable to register rtl_rc, use default RC !!\n");
diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c
index fd3b1fb35dff..59553db020ef 100644
--- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c
+++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c
@@ -1104,7 +1104,7 @@ static void halbtc8723b1ant_ps_tdma(struct btc_coexist *btcoexist,
}
if ((type == 1) || (type == 2) || (type == 9) || (type == 11) ||
- (type == 101) || (type == 102) || (type == 109) || (type == 101)) {
+ (type == 101) || (type == 102) || (type == 109) || (type == 111)) {
if (!coex_sta->force_lps_on) {
/* Native power save TDMA, only for A2DP-only case
* 1/2/9/11 while wifi noisy threshold > 30
@@ -1436,6 +1436,7 @@ static void halbtc8723b1ant_ps_tdma(struct btc_coexist *btcoexist,
coex_dm->pre_ps_tdma = coex_dm->cur_ps_tdma;
}
+static
void btc8723b1ant_tdma_dur_adj_for_acl(struct btc_coexist *btcoexist,
u8 wifi_status)
{
diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c
index 4907c2ffadfe..73ec31972944 100644
--- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c
+++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c
@@ -833,9 +833,9 @@ static void btc8723b2ant_set_sw_fulltime_dac_swing(struct btc_coexist *btcoex,
btc8723b2ant_set_dac_swing_reg(btcoex, 0x18);
}
-void btc8723b2ant_dac_swing(struct btc_coexist *btcoexist,
- bool force_exec, bool dac_swing_on,
- u32 dac_swing_lvl)
+static void btc8723b2ant_dac_swing(struct btc_coexist *btcoexist,
+ bool force_exec, bool dac_swing_on,
+ u32 dac_swing_lvl)
{
struct rtl_priv *rtlpriv = btcoexist->adapter;
diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c
index 0b26419881c0..202597cf8915 100644
--- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c
+++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c
@@ -426,39 +426,6 @@ static void btc8821a1ant_query_bt_info(struct btc_coexist *btcoexist)
btcoexist->btc_fill_h2c(btcoexist, 0x61, 1, h2c_parameter);
}
-bool btc8821a1ant_is_wifi_status_changed(struct btc_coexist *btcoexist)
-{
- static bool pre_wifi_busy = true;
- static bool pre_under_4way = true;
- static bool pre_bt_hs_on = true;
- bool wifi_busy = false, under_4way = false, bt_hs_on = false;
- bool wifi_connected = false;
-
- btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED,
- &wifi_connected);
- btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_BUSY, &wifi_busy);
- btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on);
- btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_4_WAY_PROGRESS,
- &under_4way);
-
- if (wifi_connected) {
- if (wifi_busy != pre_wifi_busy) {
- pre_wifi_busy = wifi_busy;
- return true;
- }
- if (under_4way != pre_under_4way) {
- pre_under_4way = under_4way;
- return true;
- }
- if (bt_hs_on != pre_bt_hs_on) {
- pre_bt_hs_on = bt_hs_on;
- return true;
- }
- }
-
- return false;
-}
-
static void btc8821a1ant_update_bt_link_info(struct btc_coexist *btcoexist)
{
struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info;
diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c
index d5f282cb9d24..2202d5e18977 100644
--- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c
+++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c
@@ -359,7 +359,7 @@ static void btc8821a2ant_query_bt_info(struct btc_coexist *btcoexist)
btcoexist->btc_fill_h2c(btcoexist, 0x61, 1, h2c_parameter);
}
-bool btc8821a2ant_is_wifi_status_changed(struct btc_coexist *btcoexist)
+static bool btc8821a2ant_is_wifi_status_changed(struct btc_coexist *btcoexist)
{
static bool pre_wifi_busy = true;
static bool pre_under_4way = true;
@@ -1517,7 +1517,7 @@ static void btc8821a2ant_action_bt_inquiry(struct btc_coexist *btcoexist)
btc8821a2ant_sw_mechanism2(btcoexist, false, false, false, 0x18);
}
-void btc8821a2ant_action_wifi_link_process(struct btc_coexist *btcoexist)
+static void btc8821a2ant_action_wifi_link_process(struct btc_coexist *btcoexist)
{
struct rtl_priv *rtlpriv = btcoexist->adapter;
u8 u8tmpa, u8tmpb;
diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8822bwifionly.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8822bwifionly.c
new file mode 100644
index 000000000000..951b8c1e0153
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8822bwifionly.c
@@ -0,0 +1,55 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2016-2017 Realtek Corporation.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ *****************************************************************************/
+#include "halbt_precomp.h"
+
+void ex_hal8822b_wifi_only_hw_config(struct wifi_only_cfg *wifionlycfg)
+{
+ /*BB control*/
+ halwifionly_phy_set_bb_reg(wifionlycfg, 0x4c, 0x01800000, 0x2);
+ /*SW control*/
+ halwifionly_phy_set_bb_reg(wifionlycfg, 0xcb4, 0xff, 0x77);
+ /*antenna mux switch */
+ halwifionly_phy_set_bb_reg(wifionlycfg, 0x974, 0x300, 0x3);
+
+ halwifionly_phy_set_bb_reg(wifionlycfg, 0x1990, 0x300, 0x0);
+
+ halwifionly_phy_set_bb_reg(wifionlycfg, 0xcbc, 0x80000, 0x0);
+ /*switch to WL side controller and gnt_wl gnt_bt debug signal */
+ halwifionly_phy_set_bb_reg(wifionlycfg, 0x70, 0xff000000, 0x0e);
+ /*gnt_wl=1 , gnt_bt=0*/
+ halwifionly_phy_set_bb_reg(wifionlycfg, 0x1704, 0xffffffff, 0x7700);
+ halwifionly_phy_set_bb_reg(wifionlycfg, 0x1700, 0xffffffff, 0xc00f0038);
+}
+
+void ex_hal8822b_wifi_only_scannotify(struct wifi_only_cfg *wifionlycfg,
+ u8 is_5g)
+{
+ hal8822b_wifi_only_switch_antenna(wifionlycfg, is_5g);
+}
+
+void ex_hal8822b_wifi_only_switchbandnotify(struct wifi_only_cfg *wifionlycfg,
+ u8 is_5g)
+{
+ hal8822b_wifi_only_switch_antenna(wifionlycfg, is_5g);
+}
+
+void hal8822b_wifi_only_switch_antenna(struct wifi_only_cfg *wifionlycfg,
+ u8 is_5g)
+{
+ if (is_5g)
+ halwifionly_phy_set_bb_reg(wifionlycfg, 0xcbc, 0x300, 0x1);
+ else
+ halwifionly_phy_set_bb_reg(wifionlycfg, 0xcbc, 0x300, 0x2);
+}
diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8822bwifionly.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8822bwifionly.h
new file mode 100644
index 000000000000..6ec356542eea
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8822bwifionly.h
@@ -0,0 +1,25 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2016-2017 Realtek Corporation.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ *****************************************************************************/
+#ifndef __INC_HAL8822BWIFIONLYHWCFG_H
+#define __INC_HAL8822BWIFIONLYHWCFG_H
+
+void ex_hal8822b_wifi_only_hw_config(struct wifi_only_cfg *wifionlycfg);
+void ex_hal8822b_wifi_only_scannotify(struct wifi_only_cfg *wifionlycfg,
+ u8 is_5g);
+void ex_hal8822b_wifi_only_switchbandnotify(struct wifi_only_cfg *wifionlycfg,
+ u8 is_5g);
+void hal8822b_wifi_only_switch_antenna(struct wifi_only_cfg *wifionlycfg,
+ u8 is_5g);
+#endif
diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c
index 1404729441a2..8b6b07a936f5 100644
--- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c
+++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c
@@ -363,6 +363,22 @@ static void halbtc_normal_lps(struct btc_coexist *btcoexist)
}
}
+static void halbtc_pre_normal_lps(struct btc_coexist *btcoexist)
+{
+ struct rtl_priv *rtlpriv = btcoexist->adapter;
+
+ if (btcoexist->bt_info.bt_ctrl_lps) {
+ btcoexist->bt_info.bt_lps_on = false;
+ rtl_lps_leave(rtlpriv->mac80211.hw);
+ }
+}
+
+static void halbtc_post_normal_lps(struct btc_coexist *btcoexist)
+{
+ if (btcoexist->bt_info.bt_ctrl_lps)
+ btcoexist->bt_info.bt_ctrl_lps = false;
+}
+
static void halbtc_leave_low_power(struct btc_coexist *btcoexist)
{
}
@@ -577,6 +593,9 @@ static bool halbtc_get(void *void_btcoexist, u8 get_type, void *out_buf)
tmp = true;
*bool_tmp = tmp;
break;
+ case BTC_GET_BL_WIFI_DUAL_BAND_CONNECTED:
+ *u8_tmp = BTC_MULTIPORT_SCC;
+ break;
case BTC_GET_BL_WIFI_BUSY:
if (halbtc_is_wifi_busy(rtlpriv))
*bool_tmp = true;
@@ -637,6 +656,9 @@ static bool halbtc_get(void *void_btcoexist, u8 get_type, void *out_buf)
case BTC_GET_BL_IS_ASUS_8723B:
*bool_tmp = false;
break;
+ case BTC_GET_BL_RF4CE_CONNECTED:
+ *bool_tmp = false;
+ break;
case BTC_GET_S4_WIFI_RSSI:
*s32_tmp = halbtc_get_wifi_rssi(rtlpriv);
break;
@@ -677,6 +699,21 @@ static bool halbtc_get(void *void_btcoexist, u8 get_type, void *out_buf)
case BTC_GET_U4_BT_FORBIDDEN_SLOT_VAL:
*u32_tmp = halbtc_get_bt_forbidden_slot_val(btcoexist);
break;
+ case BTC_GET_U4_WIFI_IQK_TOTAL:
+ *u32_tmp =
+ btcoexist->btc_phydm_query_phy_counter(btcoexist,
+ DM_INFO_IQK_ALL);
+ break;
+ case BTC_GET_U4_WIFI_IQK_OK:
+ *u32_tmp =
+ btcoexist->btc_phydm_query_phy_counter(btcoexist,
+ DM_INFO_IQK_OK);
+ break;
+ case BTC_GET_U4_WIFI_IQK_FAIL:
+ *u32_tmp =
+ btcoexist->btc_phydm_query_phy_counter(btcoexist,
+ DM_INFO_IQK_NG);
+ break;
case BTC_GET_U1_WIFI_DOT11_CHNL:
*u8_tmp = rtlphy->current_channel;
break;
@@ -788,6 +825,12 @@ static bool halbtc_set(void *void_btcoexist, u8 set_type, void *in_buf)
case BTC_SET_ACT_NORMAL_LPS:
halbtc_normal_lps(btcoexist);
break;
+ case BTC_SET_ACT_PRE_NORMAL_LPS:
+ halbtc_pre_normal_lps(btcoexist);
+ break;
+ case BTC_SET_ACT_POST_NORMAL_LPS:
+ halbtc_post_normal_lps(btcoexist);
+ break;
case BTC_SET_ACT_DISABLE_LOW_POWER:
halbtc_disable_low_power(btcoexist, *bool_tmp);
break;
@@ -1039,6 +1082,28 @@ static void halbtc_fill_h2c_cmd(void *bt_context, u8 element_id,
cmd_len, cmd_buf);
}
+void halbtc_send_wifi_port_id_cmd(void *bt_context)
+{
+ struct btc_coexist *btcoexist = (struct btc_coexist *)bt_context;
+ struct rtl_priv *rtlpriv = btcoexist->adapter;
+ u8 cmd_buf[1] = {0}; /* port id [2:0] = 0 */
+
+ rtlpriv->cfg->ops->fill_h2c_cmd(rtlpriv->mac80211.hw, H2C_BT_PORT_ID,
+ 1, cmd_buf);
+}
+
+void halbtc_set_default_port_id_cmd(void *bt_context)
+{
+ struct btc_coexist *btcoexist = (struct btc_coexist *)bt_context;
+ struct rtl_priv *rtlpriv = btcoexist->adapter;
+ struct ieee80211_hw *hw = rtlpriv->mac80211.hw;
+
+ if (!rtlpriv->cfg->ops->set_default_port_id_cmd)
+ return;
+
+ rtlpriv->cfg->ops->set_default_port_id_cmd(hw);
+}
+
static
void halbtc_set_bt_reg(void *btc_context, u8 reg_type, u32 offset, u32 set_val)
{
@@ -1079,6 +1144,11 @@ static void halbtc_display_dbg_msg(void *bt_context, u8 disp_type,
}
}
+static u32 halbtc_get_bt_reg(void *btc_context, u8 reg_type, u32 offset)
+{
+ return 0;
+}
+
static bool halbtc_under_ips(struct btc_coexist *btcoexist)
{
struct rtl_priv *rtlpriv = btcoexist->adapter;
@@ -1097,6 +1167,25 @@ static bool halbtc_under_ips(struct btc_coexist *btcoexist)
return false;
}
+static
+u32 halbtc_get_phydm_version(void *btc_context)
+{
+ return 0;
+}
+
+static
+void halbtc_phydm_modify_ra_pcr_threshold(void *btc_context,
+ u8 ra_offset_direction,
+ u8 ra_threshold_offset)
+{
+}
+
+static
+u32 halbtc_phydm_query_phy_counter(void *btc_context, enum dm_info_query dm_id)
+{
+ return 0;
+}
+
static u8 halbtc_get_ant_det_val_from_bt(void *btc_context)
{
struct btc_coexist *btcoexist = (struct btc_coexist *)btc_context;
@@ -1210,6 +1299,7 @@ bool exhalbtc_initlize_variables(struct rtl_priv *rtlpriv)
btcoexist->btc_get = halbtc_get;
btcoexist->btc_set = halbtc_set;
btcoexist->btc_set_bt_reg = halbtc_set_bt_reg;
+ btcoexist->btc_get_bt_reg = halbtc_get_bt_reg;
btcoexist->bt_info.bt_ctrl_buf_size = false;
btcoexist->bt_info.agg_buf_size = 5;
@@ -1220,6 +1310,10 @@ bool exhalbtc_initlize_variables(struct rtl_priv *rtlpriv)
halbtc_get_bt_coex_supported_feature;
btcoexist->btc_get_bt_coex_supported_version =
halbtc_get_bt_coex_supported_version;
+ btcoexist->btc_get_bt_phydm_version = halbtc_get_phydm_version;
+ btcoexist->btc_phydm_modify_ra_pcr_threshold =
+ halbtc_phydm_modify_ra_pcr_threshold;
+ btcoexist->btc_phydm_query_phy_counter = halbtc_phydm_query_phy_counter;
btcoexist->btc_get_ant_det_val_from_bt = halbtc_get_ant_det_val_from_bt;
btcoexist->btc_get_ble_scan_type_from_bt =
halbtc_get_ble_scan_type_from_bt;
@@ -1525,7 +1619,8 @@ void exhalbtc_scan_notify_wifi_only(struct wifi_only_cfg *wifionly_cfg,
void exhalbtc_connect_notify(struct btc_coexist *btcoexist, u8 action)
{
- u8 asso_type;
+ u8 asso_type, asso_type_v2;
+ bool wifi_under_5g;
if (!halbtc_is_bt_coexist_available(btcoexist))
return;
@@ -1533,10 +1628,17 @@ void exhalbtc_connect_notify(struct btc_coexist *btcoexist, u8 action)
if (btcoexist->manual_control)
return;
- if (action)
+ btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_UNDER_5G, &wifi_under_5g);
+
+ if (action) {
asso_type = BTC_ASSOCIATE_START;
- else
+ asso_type_v2 = wifi_under_5g ? BTC_ASSOCIATE_5G_START :
+ BTC_ASSOCIATE_START;
+ } else {
asso_type = BTC_ASSOCIATE_FINISH;
+ asso_type_v2 = wifi_under_5g ? BTC_ASSOCIATE_5G_FINISH :
+ BTC_ASSOCIATE_FINISH;
+ }
halbtc_leave_low_power(btcoexist);
diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h
index 8ed217656539..9eae87d19120 100644
--- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h
+++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h
@@ -62,6 +62,8 @@
#define BTC_ANT_PATH_WIFI 0
#define BTC_ANT_PATH_BT 1
#define BTC_ANT_PATH_PTA 2
+#define BTC_ANT_PATH_WIFI5G 3
+#define BTC_ANT_PATH_AUTO 4
/* dual Antenna definition */
#define BTC_ANT_WIFI_AT_MAIN 0
#define BTC_ANT_WIFI_AT_AUX 1
@@ -154,6 +156,7 @@ struct btc_board_info {
u8 rfe_type;
u8 ant_div_cfg;
+ u8 customer_id;
};
enum btc_dbg_opcode {
@@ -204,6 +207,7 @@ enum btc_wifi_traffic_dir {
enum btc_wifi_pnp {
BTC_WIFI_PNP_WAKE_UP = 0x0,
BTC_WIFI_PNP_SLEEP = 0x1,
+ BTC_WIFI_PNP_SLEEP_KEEP_ANT = 0x2,
BTC_WIFI_PNP_MAX
};
@@ -250,6 +254,7 @@ enum btc_get_type {
BTC_GET_BL_HS_OPERATION,
BTC_GET_BL_HS_CONNECTING,
BTC_GET_BL_WIFI_CONNECTED,
+ BTC_GET_BL_WIFI_DUAL_BAND_CONNECTED,
BTC_GET_BL_WIFI_BUSY,
BTC_GET_BL_WIFI_SCAN,
BTC_GET_BL_WIFI_LINK,
@@ -333,6 +338,7 @@ enum btc_set_type {
BTC_SET_ACT_GET_BT_RSSI,
BTC_SET_ACT_AGGREGATE_CTRL,
BTC_SET_ACT_ANTPOSREGRISTRY_CTRL,
+ BTC_SET_MIMO_PS_MODE,
/********* for 1Ant **********/
/* type bool */
@@ -347,8 +353,11 @@ enum btc_set_type {
BTC_SET_ACT_LEAVE_LPS,
BTC_SET_ACT_ENTER_LPS,
BTC_SET_ACT_NORMAL_LPS,
+ BTC_SET_ACT_PRE_NORMAL_LPS,
+ BTC_SET_ACT_POST_NORMAL_LPS,
BTC_SET_ACT_INC_FORCE_EXEC_PWR_CMD_CNT,
BTC_SET_ACT_DISABLE_LOW_POWER,
+ BTC_SET_BL_BT_LNA_CONSTRAIN_LEVEL,
BTC_SET_ACT_UPDATE_RAMASK,
BTC_SET_ACT_SEND_MIMO_PS,
/* BT Coex related */
@@ -383,6 +392,7 @@ enum btc_notify_type_lps {
enum btc_notify_type_scan {
BTC_SCAN_FINISH = 0x0,
BTC_SCAN_START = 0x1,
+ BTC_SCAN_START_2G = 0x2,
BTC_SCAN_MAX
};
@@ -397,6 +407,8 @@ enum btc_notify_type_switchband {
enum btc_notify_type_associate {
BTC_ASSOCIATE_FINISH = 0x0,
BTC_ASSOCIATE_START = 0x1,
+ BTC_ASSOCIATE_5G_FINISH = 0x2,
+ BTC_ASSOCIATE_5G_START = 0x3,
BTC_ASSOCIATE_MAX
};
@@ -435,6 +447,107 @@ enum btc_notify_type_stack_operation {
BTC_STACK_OP_MAX
};
+enum {
+ BTC_CCK_1,
+ BTC_CCK_2,
+ BTC_CCK_5_5,
+ BTC_CCK_11,
+ BTC_OFDM_6,
+ BTC_OFDM_9,
+ BTC_OFDM_12,
+ BTC_OFDM_18,
+ BTC_OFDM_24,
+ BTC_OFDM_36,
+ BTC_OFDM_48,
+ BTC_OFDM_54,
+ BTC_MCS_0,
+ BTC_MCS_1,
+ BTC_MCS_2,
+ BTC_MCS_3,
+ BTC_MCS_4,
+ BTC_MCS_5,
+ BTC_MCS_6,
+ BTC_MCS_7,
+ BTC_MCS_8,
+ BTC_MCS_9,
+ BTC_MCS_10,
+ BTC_MCS_11,
+ BTC_MCS_12,
+ BTC_MCS_13,
+ BTC_MCS_14,
+ BTC_MCS_15,
+ BTC_MCS_16,
+ BTC_MCS_17,
+ BTC_MCS_18,
+ BTC_MCS_19,
+ BTC_MCS_20,
+ BTC_MCS_21,
+ BTC_MCS_22,
+ BTC_MCS_23,
+ BTC_MCS_24,
+ BTC_MCS_25,
+ BTC_MCS_26,
+ BTC_MCS_27,
+ BTC_MCS_28,
+ BTC_MCS_29,
+ BTC_MCS_30,
+ BTC_MCS_31,
+ BTC_VHT_1SS_MCS_0,
+ BTC_VHT_1SS_MCS_1,
+ BTC_VHT_1SS_MCS_2,
+ BTC_VHT_1SS_MCS_3,
+ BTC_VHT_1SS_MCS_4,
+ BTC_VHT_1SS_MCS_5,
+ BTC_VHT_1SS_MCS_6,
+ BTC_VHT_1SS_MCS_7,
+ BTC_VHT_1SS_MCS_8,
+ BTC_VHT_1SS_MCS_9,
+ BTC_VHT_2SS_MCS_0,
+ BTC_VHT_2SS_MCS_1,
+ BTC_VHT_2SS_MCS_2,
+ BTC_VHT_2SS_MCS_3,
+ BTC_VHT_2SS_MCS_4,
+ BTC_VHT_2SS_MCS_5,
+ BTC_VHT_2SS_MCS_6,
+ BTC_VHT_2SS_MCS_7,
+ BTC_VHT_2SS_MCS_8,
+ BTC_VHT_2SS_MCS_9,
+ BTC_VHT_3SS_MCS_0,
+ BTC_VHT_3SS_MCS_1,
+ BTC_VHT_3SS_MCS_2,
+ BTC_VHT_3SS_MCS_3,
+ BTC_VHT_3SS_MCS_4,
+ BTC_VHT_3SS_MCS_5,
+ BTC_VHT_3SS_MCS_6,
+ BTC_VHT_3SS_MCS_7,
+ BTC_VHT_3SS_MCS_8,
+ BTC_VHT_3SS_MCS_9,
+ BTC_VHT_4SS_MCS_0,
+ BTC_VHT_4SS_MCS_1,
+ BTC_VHT_4SS_MCS_2,
+ BTC_VHT_4SS_MCS_3,
+ BTC_VHT_4SS_MCS_4,
+ BTC_VHT_4SS_MCS_5,
+ BTC_VHT_4SS_MCS_6,
+ BTC_VHT_4SS_MCS_7,
+ BTC_VHT_4SS_MCS_8,
+ BTC_VHT_4SS_MCS_9,
+ BTC_MCS_32,
+ BTC_UNKNOWN,
+ BTC_PKT_MGNT,
+ BTC_PKT_CTRL,
+ BTC_PKT_UNKNOWN,
+ BTC_PKT_NOT_FOR_ME,
+ BTC_RATE_MAX
+};
+
+enum {
+ BTC_MULTIPORT_SCC,
+ BTC_MULTIPORT_MCC_2CHANNEL,
+ BTC_MULTIPORT_MCC_2BAND,
+ BTC_MULTIPORT_MAX
+};
+
struct btc_bt_info {
bool bt_disabled;
u8 rssi_adjust_for_agc_table_on;
@@ -454,6 +567,7 @@ struct btc_bt_info {
u16 bt_hci_ver;
u16 bt_real_fw_ver;
u8 bt_fw_ver;
+ u32 bt_get_fw_ver;
bool bt_disable_low_pwr;
@@ -525,6 +639,7 @@ struct btc_bt_link_info {
bool pan_exist;
bool pan_only;
bool slave_role;
+ bool acl_busy;
};
enum btc_antenna_pos {
@@ -625,8 +740,15 @@ struct btc_coexist {
void (*btc_set_bt_reg)(void *btc_context, u8 reg_type, u32 offset,
u32 value);
+ u32 (*btc_get_bt_reg)(void *btc_context, u8 reg_type, u32 offset);
u32 (*btc_get_bt_coex_supported_feature)(void *btcoexist);
u32 (*btc_get_bt_coex_supported_version)(void *btcoexist);
+ u32 (*btc_get_bt_phydm_version)(void *btcoexist);
+ void (*btc_phydm_modify_ra_pcr_threshold)(void *btcoexist,
+ u8 ra_offset_direction,
+ u8 ra_threshold_offset);
+ u32 (*btc_phydm_query_phy_counter)(void *btcoexist,
+ enum dm_info_query dm_id);
u8 (*btc_get_ant_det_val_from_bt)(void *btcoexist);
u8 (*btc_get_ble_scan_type_from_bt)(void *btcoexist);
u32 (*btc_get_ble_scan_para_from_bt)(void *btcoexist, u8 scan_type);
@@ -691,6 +813,8 @@ void exhalbtc_lps_leave(struct btc_coexist *btcoexist);
void exhalbtc_low_wifi_traffic_notify(struct btc_coexist *btcoexist);
void exhalbtc_set_single_ant_path(struct btc_coexist *btcoexist,
u8 single_ant_path);
+void halbtc_send_wifi_port_id_cmd(void *bt_context);
+void halbtc_set_default_port_id_cmd(void *bt_context);
/* The following are used by wifi_only case */
enum wifionly_chip_interface {
diff --git a/drivers/net/wireless/realtek/rtlwifi/efuse.c b/drivers/net/wireless/realtek/rtlwifi/efuse.c
index 35b50be633f1..fd13d4ef53b8 100644
--- a/drivers/net/wireless/realtek/rtlwifi/efuse.c
+++ b/drivers/net/wireless/realtek/rtlwifi/efuse.c
@@ -50,6 +50,11 @@ static const struct efuse_map RTL8712_SDIO_EFUSE_TABLE[] = {
{11, 0, 0, 28}
};
+static const struct rtl_efuse_ops efuse_ops = {
+ .efuse_onebyte_read = efuse_one_byte_read,
+ .efuse_logical_map_read = efuse_shadow_read,
+};
+
static void efuse_shadow_read_1byte(struct ieee80211_hw *hw, u16 offset,
u8 *value);
static void efuse_shadow_read_2byte(struct ieee80211_hw *hw, u16 offset,
@@ -1364,3 +1369,11 @@ void rtl_fill_dummy(u8 *pfwbuf, u32 *pfwlen)
*pfwlen = fwlen;
}
EXPORT_SYMBOL_GPL(rtl_fill_dummy);
+
+void rtl_efuse_ops_init(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ rtlpriv->efuse.efuse_ops = &efuse_ops;
+}
+EXPORT_SYMBOL_GPL(rtl_efuse_ops_init);
diff --git a/drivers/net/wireless/realtek/rtlwifi/efuse.h b/drivers/net/wireless/realtek/rtlwifi/efuse.h
index 952fdc288f0e..dfa31c13fc7a 100644
--- a/drivers/net/wireless/realtek/rtlwifi/efuse.h
+++ b/drivers/net/wireless/realtek/rtlwifi/efuse.h
@@ -116,5 +116,5 @@ void rtl_fill_dummy(u8 *pfwbuf, u32 *pfwlen);
void rtl_fw_page_write(struct ieee80211_hw *hw, u32 page, const u8 *buffer,
u32 size);
void rtl_fw_block_write(struct ieee80211_hw *hw, const u8 *buffer, u32 size);
-
+void rtl_efuse_ops_init(struct ieee80211_hw *hw);
#endif
diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c
index 01ccf8884831..57bb8f049e59 100644
--- a/drivers/net/wireless/realtek/rtlwifi/pci.c
+++ b/drivers/net/wireless/realtek/rtlwifi/pci.c
@@ -31,7 +31,6 @@
#include "efuse.h"
#include <linux/interrupt.h>
#include <linux/export.h>
-#include <linux/kmemleak.h>
#include <linux/module.h>
MODULE_AUTHOR("lizhaoming <chaoming_li@realsil.com.cn>");
@@ -2238,6 +2237,7 @@ int rtl_pci_probe(struct pci_dev *pdev,
rtlpriv->cfg = (struct rtl_hal_cfg *)(id->driver_data);
rtlpriv->intf_ops = &rtl_pci_ops;
rtlpriv->glb_var = &rtl_global_var;
+ rtl_efuse_ops_init(hw);
/* MEM map */
err = pci_request_regions(pdev, KBUILD_MODNAME);
diff --git a/drivers/net/wireless/realtek/rtlwifi/rc.c b/drivers/net/wireless/realtek/rtlwifi/rc.c
index d1cb7d405618..6c78c6dabbdf 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rc.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rc.c
@@ -42,6 +42,23 @@ static u8 _rtl_rc_get_highest_rix(struct rtl_priv *rtlpriv,
struct rtl_phy *rtlphy = &(rtlpriv->phy);
struct rtl_sta_info *sta_entry = NULL;
u16 wireless_mode = 0;
+ u8 nss;
+ struct ieee80211_tx_rate rate;
+
+ switch (get_rf_type(rtlphy)) {
+ case RF_4T4R:
+ nss = 4;
+ break;
+ case RF_3T3R:
+ nss = 3;
+ break;
+ case RF_2T2R:
+ nss = 2;
+ break;
+ default:
+ nss = 1;
+ break;
+ }
/*
*this rate is no use for true rate, firmware
@@ -66,28 +83,51 @@ static u8 _rtl_rc_get_highest_rix(struct rtl_priv *rtlpriv,
} else if (wireless_mode == WIRELESS_MODE_G) {
return G_MODE_MAX_RIX;
} else if (wireless_mode == WIRELESS_MODE_N_24G) {
- if (get_rf_type(rtlphy) != RF_2T2R)
+ if (nss == 1)
return N_MODE_MCS7_RIX;
else
return N_MODE_MCS15_RIX;
} else if (wireless_mode == WIRELESS_MODE_AC_24G) {
- return AC_MODE_MCS9_RIX;
+ if (sta->bandwidth == IEEE80211_STA_RX_BW_20) {
+ ieee80211_rate_set_vht(&rate,
+ AC_MODE_MCS8_RIX,
+ nss);
+ goto out;
+ } else {
+ ieee80211_rate_set_vht(&rate,
+ AC_MODE_MCS9_RIX,
+ nss);
+ goto out;
+ }
}
return 0;
} else {
if (wireless_mode == WIRELESS_MODE_A) {
return A_MODE_MAX_RIX;
} else if (wireless_mode == WIRELESS_MODE_N_5G) {
- if (get_rf_type(rtlphy) != RF_2T2R)
+ if (nss == 1)
return N_MODE_MCS7_RIX;
else
return N_MODE_MCS15_RIX;
} else if (wireless_mode == WIRELESS_MODE_AC_5G) {
- return AC_MODE_MCS9_RIX;
+ if (sta->bandwidth == IEEE80211_STA_RX_BW_20) {
+ ieee80211_rate_set_vht(&rate,
+ AC_MODE_MCS8_RIX,
+ nss);
+ goto out;
+ } else {
+ ieee80211_rate_set_vht(&rate,
+ AC_MODE_MCS9_RIX,
+ nss);
+ goto out;
+ }
}
return 0;
}
}
+
+out:
+ return rate.idx;
}
static void _rtl_rc_rate_set_series(struct rtl_priv *rtlpriv,
@@ -111,9 +151,6 @@ static void _rtl_rc_rate_set_series(struct rtl_priv *rtlpriv,
}
rate->count = tries;
rate->idx = rix >= 0x00 ? rix : 0x00;
- if (rtlpriv->rtlhal.hw_type == HARDWARE_TYPE_RTL8812AE &&
- wireless_mode == WIRELESS_MODE_AC_5G)
- rate->idx += 0x10;/*2NSS for 8812AE*/
if (!not_data) {
if (txrc->short_preamble)
@@ -126,10 +163,10 @@ static void _rtl_rc_rate_set_series(struct rtl_priv *rtlpriv,
if (sta && sta->vht_cap.vht_supported)
rate->flags |= IEEE80211_TX_RC_80_MHZ_WIDTH;
} else {
- if (mac->bw_40)
- rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
if (mac->bw_80)
rate->flags |= IEEE80211_TX_RC_80_MHZ_WIDTH;
+ else if (mac->bw_40)
+ rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
}
if (sgi_20 || sgi_40 || sgi_80)
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/pwrseq.h b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/pwrseq.h
index f2d9c6116e5c..8379a3e5198c 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/pwrseq.h
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/pwrseq.h
@@ -142,7 +142,7 @@
/*wait power state to suspend*/}, \
{0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3) | BIT(4), 0 \
- /*0x04[12:11] = 2b'01enable WL suspend*/},
+ /*0x04[12:11] = 2b'00 disable WL suspend*/},
#define RTL8188EE_TRANS_CARDEMU_TO_CARDDIS \
{0x0026, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
@@ -179,7 +179,7 @@
/*wait power state to suspend*/}, \
{0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3)|BIT(4), 0 \
- /*0x04[12:11] = 2b'01enable WL suspend*/},
+ /*0x04[12:11] = 2b'00 disable WL suspend*/},
#define RTL8188EE_TRANS_CARDEMU_TO_PDN \
{0x0006, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192c/fw_common.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192c/fw_common.c
index 015476e3f7e5..f3bff66e85d0 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192c/fw_common.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192c/fw_common.c
@@ -32,7 +32,6 @@
#include "../rtl8192ce/def.h"
#include "fw_common.h"
#include <linux/export.h>
-#include <linux/kmemleak.h>
static void _rtl92c_enable_fw_download(struct ieee80211_hw *hw, bool enable)
{
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/rf.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/rf.c
index 9cff6bc4049c..cf551785eb08 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/rf.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/rf.c
@@ -299,9 +299,6 @@ static void _rtl92c_get_txpower_writeval_by_regulatory(struct ieee80211_hw *hw,
writeVal = 0x00000000;
if (rtlpriv->dm.dynamic_txhighpower_lvl == TXHIGHPWRLEVEL_BT1)
writeVal = writeVal - 0x06060606;
- else if (rtlpriv->dm.dynamic_txhighpower_lvl ==
- TXHIGHPWRLEVEL_BT2)
- writeVal = writeVal;
*(p_outwriteval + rf) = writeVal;
}
}
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c
index ac4a82de40c7..9ab56827124e 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c
@@ -427,7 +427,6 @@ static void _rtl_rx_process(struct ieee80211_hw *hw, struct sk_buff *skb)
(u32)hdr->addr1[0], (u32)hdr->addr1[1],
(u32)hdr->addr1[2], (u32)hdr->addr1[3],
(u32)hdr->addr1[4], (u32)hdr->addr1[5]);
- memcpy(IEEE80211_SKB_RXCB(skb), rx_status, sizeof(*rx_status));
ieee80211_rx(hw, skb);
}
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/pwrseq.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/pwrseq.h
index 781eeaa6af49..c570801508cc 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/pwrseq.h
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/pwrseq.h
@@ -134,7 +134,7 @@
/*wait power state to suspend*/ \
{0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \
PWR_BASEADDR_SDIO , PWR_CMD_POLLING, BIT(1), BIT(1)}, \
- /*0x04[12:11] = 2b'01enable WL suspend*/ \
+ /*0x04[12:11] = 2b'00 disable WL suspend*/ \
{0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
PWR_BASEADDR_MAC , PWR_CMD_WRITE, BIT(3) | BIT(4), 0},
@@ -181,7 +181,7 @@
/*Lock small LDO Register*/ \
{0x00CC, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \
PWR_BASEADDR_MAC , PWR_CMD_WRITE, BIT(2), 0}, \
- /*0x04[12:11] = 2b'01enable WL suspend*/ \
+ /*0x04[12:11] = 2b'00 disable WL suspend*/ \
{0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
PWR_BASEADDR_MAC , PWR_CMD_WRITE, BIT(3) | BIT(4), 0},
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/pwrseq.h b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/pwrseq.h
index 4ac7db526f15..e6c3aac3e9fd 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/pwrseq.h
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/pwrseq.h
@@ -135,7 +135,7 @@
/*wait power state to suspend*/ \
{0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK,\
PWR_BASEADDR_SDIO, PWR_CMD_POLLING, BIT(1), BIT(1)},\
- /*0x04[12:11] = 2b'01enable WL suspend*/ \
+ /*0x04[12:11] = 2b'00 disable WL suspend*/ \
{0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,\
PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3)|BIT(4), 0},
@@ -172,7 +172,7 @@
{0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, \
PWR_INTF_SDIO_MSK, PWR_BASEADDR_SDIO,\
PWR_CMD_POLLING, BIT(1), BIT(1)},\
- /*0x04[12:11] = 2b'00enable WL suspend*/ \
+ /*0x04[12:11] = 2b'00 disable WL suspend*/ \
{0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, \
PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC,\
PWR_CMD_WRITE, BIT(3)|BIT(4), 0},\
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/pwrseq.h b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/pwrseq.h
index 0fee5e0e55c2..3367cfbc9502 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/pwrseq.h
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/pwrseq.h
@@ -204,7 +204,7 @@
/*0x23[4] = 1b'0 12H LDO enter normal mode*/ \
{0x0023, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \
PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), 0}, \
- /*0x04[12:11] = 2b'01enable WL suspend*/ \
+ /*0x04[12:11] = 2b'00 disable WL suspend*/ \
{0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3)|BIT(4), 0},
@@ -251,7 +251,7 @@
/*0x48[16] = 0 to disable GPIO9 as EXT WAKEUP*/ \
{0x004A, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK, \
PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 0}, \
- /*0x04[12:11] = 2b'01enable WL suspend*/ \
+ /*0x04[12:11] = 2b'00 disable WL suspend*/ \
{0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3)|BIT(4), 0}, \
/*0x23[4] = 1b'0 12H LDO enter normal mode*/ \
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/dm.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/dm.c
index b11365a5ee1f..9111ba7ff0a1 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/dm.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/dm.c
@@ -1475,7 +1475,7 @@ void rtl8812ae_dm_txpwr_track_set_pwr(struct ieee80211_hw *hw,
}
} else if (method == MIX_MODE) {
RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
- "pDM_Odm->DefaultOfdmIndex=%d, pDM_Odm->Aboslute_OFDMSwingIdx[RFPath]=%d, RF_Path = %d\n",
+ "pDM_Odm->DefaultOfdmIndex=%d, pDM_Odm->Absolute_OFDMSwingIdx[RFPath]=%d, RF_Path = %d\n",
rtldm->default_ofdm_index,
rtldm->absolute_ofdm_swing_idx[rf_path],
rf_path);
@@ -1750,7 +1750,7 @@ void rtl8812ae_dm_txpower_tracking_callback_thermalmeter(
/*Record delta swing for mix mode power tracking*/
RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
- "******Temp is higher and pDM_Odm->Aboslute_OFDMSwingIdx[ODM_RF_PATH_A] = %d\n",
+ "******Temp is higher and pDM_Odm->Absolute_OFDMSwingIdx[ODM_RF_PATH_A] = %d\n",
rtldm->absolute_ofdm_swing_idx[RF90_PATH_A]);
RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
@@ -1766,7 +1766,7 @@ void rtl8812ae_dm_txpower_tracking_callback_thermalmeter(
/*Record delta swing for mix mode power tracking*/
RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
- "******Temp is higher and pDM_Odm->Aboslute_OFDMSwingIdx[ODM_RF_PATH_B] = %d\n",
+ "******Temp is higher and pDM_Odm->Absolute_OFDMSwingIdx[ODM_RF_PATH_B] = %d\n",
rtldm->absolute_ofdm_swing_idx[RF90_PATH_B]);
} else {
RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
@@ -1782,7 +1782,7 @@ void rtl8812ae_dm_txpower_tracking_callback_thermalmeter(
-1 * delta_swing_table_idx_tdown_a[delta];
/* Record delta swing for mix mode power tracking*/
RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
- "******Temp is lower and pDM_Odm->Aboslute_OFDMSwingIdx[ODM_RF_PATH_A] = %d\n",
+ "******Temp is lower and pDM_Odm->Absolute_OFDMSwingIdx[ODM_RF_PATH_A] = %d\n",
rtldm->absolute_ofdm_swing_idx[RF90_PATH_A]);
RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
@@ -1799,7 +1799,7 @@ void rtl8812ae_dm_txpower_tracking_callback_thermalmeter(
/*Record delta swing for mix mode power tracking*/
RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
- "******Temp is lower and pDM_Odm->Aboslute_OFDMSwingIdx[ODM_RF_PATH_B] = %d\n",
+ "******Temp is lower and pDM_Odm->Absolute_OFDMSwingIdx[ODM_RF_PATH_B] = %d\n",
rtldm->absolute_ofdm_swing_idx[RF90_PATH_B]);
}
@@ -2115,7 +2115,7 @@ void rtl8821ae_dm_txpwr_track_set_pwr(struct ieee80211_hw *hw,
}
} else if (method == MIX_MODE) {
RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
- "pDM_Odm->DefaultOfdmIndex=%d,pDM_Odm->Aboslute_OFDMSwingIdx[RFPath]=%d, RF_Path = %d\n",
+ "pDM_Odm->DefaultOfdmIndex=%d,pDM_Odm->Absolute_OFDMSwingIdx[RFPath]=%d, RF_Path = %d\n",
rtldm->default_ofdm_index,
rtldm->absolute_ofdm_swing_idx[rf_path],
rf_path);
@@ -2329,7 +2329,7 @@ void rtl8821ae_dm_txpower_tracking_callback_thermalmeter(
/*Record delta swing for mix mode power tracking*/
RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
- "******Temp is higher and pDM_Odm->Aboslute_OFDMSwingIdx[ODM_RF_PATH_A] = %d\n",
+ "******Temp is higher and pDM_Odm->Absolute_OFDMSwingIdx[ODM_RF_PATH_A] = %d\n",
rtldm->absolute_ofdm_swing_idx[RF90_PATH_A]);
} else {
RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
@@ -2345,7 +2345,7 @@ void rtl8821ae_dm_txpower_tracking_callback_thermalmeter(
-1 * delta_swing_table_idx_tdown_a[delta];
/* Record delta swing for mix mode power tracking*/
RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
- "******Temp is lower and pDM_Odm->Aboslute_OFDMSwingIdx[ODM_RF_PATH_A] = %d\n",
+ "******Temp is lower and pDM_Odm->Absolute_OFDMSwingIdx[ODM_RF_PATH_A] = %d\n",
rtldm->absolute_ofdm_swing_idx[RF90_PATH_A]);
}
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/pwrseq.h b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/pwrseq.h
index 36b3e91d996e..6dd575435c63 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/pwrseq.h
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/pwrseq.h
@@ -531,7 +531,7 @@ extern struct wlan_pwr_cfg rtl8812_leave_lps_flow
/*0x23[4] = 1b'0 12H LDO enter normal mode*/}, \
{0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,\
PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT3|BIT4, 0 \
- /*0x04[12:11] = 2b'01enable WL suspend*/},
+ /*0x04[12:11] = 2b'00 disable WL suspend*/},
#define RTL8821A_TRANS_CARDEMU_TO_CARDDIS \
{0x0007, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK,\
@@ -572,7 +572,7 @@ extern struct wlan_pwr_cfg rtl8812_leave_lps_flow
/*0x48[16] = 0 to disable GPIO9 as EXT WAKEUP*/}, \
{0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,\
PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT3|BIT4, 0 \
- /*0x04[12:11] = 2b'01enable WL suspend*/},\
+ /*0x04[12:11] = 2b'00 disable WL suspend*/},\
{0x0023, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK,\
PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT4, 0 \
/*0x23[4] = 1b'0 12H LDO enter normal mode*/}, \
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c
index ab5d462b1a3a..9bb3d9dfce79 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c
@@ -328,6 +328,7 @@ static const struct rtl_hal_cfg rtl8821ae_hal_cfg = {
.alt_fw_name = "rtlwifi/rtl8821aefw.bin",
.ops = &rtl8821ae_hal_ops,
.mod_params = &rtl8821ae_mod_params,
+ .spec_ver = RTL_SPEC_SUPPORT_VHT,
.maps[SYS_ISO_CTRL] = REG_SYS_ISO_CTRL,
.maps[SYS_FUNC_EN] = REG_SYS_FUNC_EN,
.maps[SYS_CLK] = REG_SYS_CLKR,
diff --git a/drivers/net/wireless/realtek/rtlwifi/wifi.h b/drivers/net/wireless/realtek/rtlwifi/wifi.h
index 46dcb7fef195..d27e33960e77 100644
--- a/drivers/net/wireless/realtek/rtlwifi/wifi.h
+++ b/drivers/net/wireless/realtek/rtlwifi/wifi.h
@@ -154,10 +154,21 @@ enum rtl8192c_h2c_cmd {
MAX_H2CCMD
};
+enum {
+ H2C_BT_PORT_ID = 0x71,
+};
+
+#define GET_TX_REPORT_SN_V1(c2h) (c2h[6])
+#define GET_TX_REPORT_ST_V1(c2h) (c2h[0] & 0xC0)
+#define GET_TX_REPORT_RETRY_V1(c2h) (c2h[2] & 0x3F)
+#define GET_TX_REPORT_SN_V2(c2h) (c2h[6])
+#define GET_TX_REPORT_ST_V2(c2h) (c2h[7] & 0xC0)
+#define GET_TX_REPORT_RETRY_V2(c2h) (c2h[8] & 0x3F)
+
#define MAX_TX_COUNT 4
#define MAX_REGULATION_NUM 4
#define MAX_RF_PATH_NUM 4
-#define MAX_RATE_SECTION_NUM 6
+#define MAX_RATE_SECTION_NUM 6 /* = MAX_RATE_SECTION */
#define MAX_2_4G_BANDWIDTH_NUM 4
#define MAX_5G_BANDWIDTH_NUM 4
#define MAX_RF_PATH 4
@@ -167,8 +178,9 @@ enum rtl8192c_h2c_cmd {
#define TX_PWR_BY_RATE_NUM_BAND 2
#define TX_PWR_BY_RATE_NUM_RF 4
#define TX_PWR_BY_RATE_NUM_SECTION 12
-#define MAX_BASE_NUM_IN_PHY_REG_PG_24G 6
-#define MAX_BASE_NUM_IN_PHY_REG_PG_5G 5
+#define TX_PWR_BY_RATE_NUM_RATE 84 /* >= TX_PWR_BY_RATE_NUM_SECTION */
+#define MAX_BASE_NUM_IN_PHY_REG_PG_24G 6 /* MAX_RATE_SECTION */
+#define MAX_BASE_NUM_IN_PHY_REG_PG_5G 5 /* MAX_RATE_SECTION -1 */
#define BUFDESC_SEG_NUM 1 /* 0:2 seg, 1: 4 seg, 2: 8 seg */
@@ -264,6 +276,7 @@ enum rate_section {
HT_MCS8_MCS15,
VHT_1SSMCS0_1SSMCS9,
VHT_2SSMCS0_2SSMCS9,
+ MAX_RATE_SECTION,
};
enum intf_type {
@@ -278,6 +291,13 @@ enum radio_path {
RF90_PATH_D = 3,
};
+enum radio_mask {
+ RF_MASK_A = BIT(0),
+ RF_MASK_B = BIT(1),
+ RF_MASK_C = BIT(2),
+ RF_MASK_D = BIT(3),
+};
+
enum regulation_txpwr_lmt {
TXPWR_LMT_FCC = 0,
TXPWR_LMT_MKK = 1,
@@ -536,6 +556,7 @@ enum rt_oem_id {
RT_CID_NETGEAR = 36,
RT_CID_PLANEX = 37,
RT_CID_CC_C = 38,
+ RT_CID_LENOVO_CHINA = 40,
};
enum hw_descs {
@@ -571,6 +592,7 @@ enum ht_channel_width {
HT_CHANNEL_WIDTH_20 = 0,
HT_CHANNEL_WIDTH_20_40 = 1,
HT_CHANNEL_WIDTH_80 = 2,
+ HT_CHANNEL_WIDTH_MAX,
};
/* Ref: 802.11i sepc D10.0 7.3.2.25.1
@@ -952,6 +974,40 @@ enum package_type {
enum rtl_spec_ver {
RTL_SPEC_NEW_RATEID = BIT(0), /* use ratr_table_mode_new */
+ RTL_SPEC_SUPPORT_VHT = BIT(1), /* support VHT */
+ RTL_SPEC_EXT_C2H = BIT(2), /* extend FW C2H (e.g. TX REPORT) */
+};
+
+enum dm_info_query {
+ DM_INFO_FA_OFDM,
+ DM_INFO_FA_CCK,
+ DM_INFO_FA_TOTAL,
+ DM_INFO_CCA_OFDM,
+ DM_INFO_CCA_CCK,
+ DM_INFO_CCA_ALL,
+ DM_INFO_CRC32_OK_VHT,
+ DM_INFO_CRC32_OK_HT,
+ DM_INFO_CRC32_OK_LEGACY,
+ DM_INFO_CRC32_OK_CCK,
+ DM_INFO_CRC32_ERROR_VHT,
+ DM_INFO_CRC32_ERROR_HT,
+ DM_INFO_CRC32_ERROR_LEGACY,
+ DM_INFO_CRC32_ERROR_CCK,
+ DM_INFO_EDCCA_FLAG,
+ DM_INFO_OFDM_ENABLE,
+ DM_INFO_CCK_ENABLE,
+ DM_INFO_CRC32_OK_HT_AGG,
+ DM_INFO_CRC32_ERROR_HT_AGG,
+ DM_INFO_DBG_PORT_0,
+ DM_INFO_CURR_IGI,
+ DM_INFO_RSSI_MIN,
+ DM_INFO_RSSI_MAX,
+ DM_INFO_CLM_RATIO,
+ DM_INFO_NHM_RATIO,
+ DM_INFO_IQK_ALL,
+ DM_INFO_IQK_OK,
+ DM_INFO_IQK_NG,
+ DM_INFO_SIZE,
};
struct octet_string {
@@ -1277,7 +1333,7 @@ struct rtl_phy {
u32 tx_power_by_rate_offset[TX_PWR_BY_RATE_NUM_BAND]
[TX_PWR_BY_RATE_NUM_RF]
[TX_PWR_BY_RATE_NUM_RF]
- [TX_PWR_BY_RATE_NUM_SECTION];
+ [TX_PWR_BY_RATE_NUM_RATE];
u8 txpwr_by_rate_base_24g[TX_PWR_BY_RATE_NUM_RF]
[TX_PWR_BY_RATE_NUM_RF]
[MAX_BASE_NUM_IN_PHY_REG_PG_24G];
@@ -1794,6 +1850,7 @@ struct rtl_dm {
#define EFUSE_MAX_LOGICAL_SIZE 512
struct rtl_efuse {
+ const struct rtl_efuse_ops *efuse_ops;
bool autoLoad_ok;
bool bootfromefuse;
u16 max_physical_size;
@@ -1899,6 +1956,12 @@ struct rtl_efuse {
u8 channel_plan;
};
+struct rtl_efuse_ops {
+ int (*efuse_onebyte_read)(struct ieee80211_hw *hw, u16 addr, u8 *data);
+ void (*efuse_logical_map_read)(struct ieee80211_hw *hw, u8 type,
+ u16 offset, u32 *value);
+};
+
struct rtl_tx_report {
atomic_t sn;
u16 last_sent_sn;
@@ -2231,6 +2294,7 @@ struct rtl_hal_ops {
void (*bt_coex_off_before_lps) (struct ieee80211_hw *hw);
void (*fill_h2c_cmd) (struct ieee80211_hw *hw, u8 element_id,
u32 cmd_len, u8 *p_cmdbuffer);
+ void (*set_default_port_id_cmd)(struct ieee80211_hw *hw);
bool (*get_btc_status) (void);
bool (*is_fw_header)(struct rtlwifi_firmware_header *hdr);
u32 (*rx_command_packet)(struct ieee80211_hw *hw,
diff --git a/drivers/net/wireless/rsi/Kconfig b/drivers/net/wireless/rsi/Kconfig
index 7c5e4ca4e3d0..976c21866230 100644
--- a/drivers/net/wireless/rsi/Kconfig
+++ b/drivers/net/wireless/rsi/Kconfig
@@ -5,14 +5,15 @@ config WLAN_VENDOR_RSI
If you have a wireless card belonging to this class, say Y.
Note that the answer to this question doesn't directly affect the
- kernel: saying N will just cause the configurator to skip all
- the questions about cards. If you say Y, you will be asked for
+ kernel: saying N will just cause the configurator to skip all the
+ questions about these cards. If you say Y, you will be asked for
your specific card in the following questions.
if WLAN_VENDOR_RSI
config RSI_91X
tristate "Redpine Signals Inc 91x WLAN driver support"
+ select BT_HCIRSI if RSI_COEX
depends on MAC80211
---help---
This option enabes support for RSI 1x1 devices.
@@ -42,4 +43,14 @@ config RSI_USB
This option enables the USB bus support in rsi drivers.
Select M (recommended), if you have a RSI 1x1 wireless module.
+config RSI_COEX
+ bool "Redpine Signals WLAN BT Coexistence support"
+ depends on BT && RSI_91X
+ depends on !(BT=m && RSI_91X=y)
+ default y
+ ---help---
+ This option enables the WLAN BT coex support in rsi drivers.
+ Select M (recommended), if you have want to use this feature
+ and you have RS9113 module.
+
endif # WLAN_VENDOR_RSI
diff --git a/drivers/net/wireless/rsi/Makefile b/drivers/net/wireless/rsi/Makefile
index 47c45908d894..ff87121a5928 100644
--- a/drivers/net/wireless/rsi/Makefile
+++ b/drivers/net/wireless/rsi/Makefile
@@ -5,6 +5,7 @@ rsi_91x-y += rsi_91x_mac80211.o
rsi_91x-y += rsi_91x_mgmt.o
rsi_91x-y += rsi_91x_hal.o
rsi_91x-y += rsi_91x_ps.o
+rsi_91x-$(CONFIG_RSI_COEX) += rsi_91x_coex.o
rsi_91x-$(CONFIG_RSI_DEBUGFS) += rsi_91x_debugfs.o
rsi_usb-y += rsi_91x_usb.o rsi_91x_usb_ops.o
diff --git a/drivers/net/wireless/rsi/rsi_91x_coex.c b/drivers/net/wireless/rsi/rsi_91x_coex.c
new file mode 100644
index 000000000000..d055099dadf1
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_91x_coex.c
@@ -0,0 +1,179 @@
+/**
+ * Copyright (c) 2018 Redpine Signals Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "rsi_main.h"
+#include "rsi_coex.h"
+#include "rsi_mgmt.h"
+#include "rsi_hal.h"
+
+static enum rsi_coex_queues rsi_coex_determine_coex_q
+ (struct rsi_coex_ctrl_block *coex_cb)
+{
+ enum rsi_coex_queues q_num = RSI_COEX_Q_INVALID;
+
+ if (skb_queue_len(&coex_cb->coex_tx_qs[RSI_COEX_Q_COMMON]) > 0)
+ q_num = RSI_COEX_Q_COMMON;
+ if (skb_queue_len(&coex_cb->coex_tx_qs[RSI_COEX_Q_BT]) > 0)
+ q_num = RSI_COEX_Q_BT;
+ if (skb_queue_len(&coex_cb->coex_tx_qs[RSI_COEX_Q_WLAN]) > 0)
+ q_num = RSI_COEX_Q_WLAN;
+
+ return q_num;
+}
+
+static void rsi_coex_sched_tx_pkts(struct rsi_coex_ctrl_block *coex_cb)
+{
+ enum rsi_coex_queues coex_q = RSI_COEX_Q_INVALID;
+ struct sk_buff *skb;
+
+ do {
+ coex_q = rsi_coex_determine_coex_q(coex_cb);
+ rsi_dbg(INFO_ZONE, "queue = %d\n", coex_q);
+
+ if (coex_q == RSI_COEX_Q_BT) {
+ skb = skb_dequeue(&coex_cb->coex_tx_qs[RSI_COEX_Q_BT]);
+ rsi_send_bt_pkt(coex_cb->priv, skb);
+ }
+ } while (coex_q != RSI_COEX_Q_INVALID);
+}
+
+static void rsi_coex_scheduler_thread(struct rsi_common *common)
+{
+ struct rsi_coex_ctrl_block *coex_cb =
+ (struct rsi_coex_ctrl_block *)common->coex_cb;
+ u32 timeout = EVENT_WAIT_FOREVER;
+
+ do {
+ rsi_wait_event(&coex_cb->coex_tx_thread.event, timeout);
+ rsi_reset_event(&coex_cb->coex_tx_thread.event);
+
+ rsi_coex_sched_tx_pkts(coex_cb);
+ } while (atomic_read(&coex_cb->coex_tx_thread.thread_done) == 0);
+
+ complete_and_exit(&coex_cb->coex_tx_thread.completion, 0);
+}
+
+int rsi_coex_recv_pkt(struct rsi_common *common, u8 *msg)
+{
+ u8 msg_type = msg[RSI_RX_DESC_MSG_TYPE_OFFSET];
+
+ switch (msg_type) {
+ case COMMON_CARD_READY_IND:
+ rsi_dbg(INFO_ZONE, "common card ready received\n");
+ rsi_handle_card_ready(common, msg);
+ break;
+ case SLEEP_NOTIFY_IND:
+ rsi_dbg(INFO_ZONE, "sleep notify received\n");
+ rsi_mgmt_pkt_recv(common, msg);
+ break;
+ }
+
+ return 0;
+}
+
+static inline int rsi_map_coex_q(u8 hal_queue)
+{
+ switch (hal_queue) {
+ case RSI_COEX_Q:
+ return RSI_COEX_Q_COMMON;
+ case RSI_WLAN_Q:
+ return RSI_COEX_Q_WLAN;
+ case RSI_BT_Q:
+ return RSI_COEX_Q_BT;
+ }
+ return RSI_COEX_Q_INVALID;
+}
+
+int rsi_coex_send_pkt(void *priv, struct sk_buff *skb, u8 hal_queue)
+{
+ struct rsi_common *common = (struct rsi_common *)priv;
+ struct rsi_coex_ctrl_block *coex_cb =
+ (struct rsi_coex_ctrl_block *)common->coex_cb;
+ struct skb_info *tx_params = NULL;
+ enum rsi_coex_queues coex_q;
+ int status;
+
+ coex_q = rsi_map_coex_q(hal_queue);
+ if (coex_q == RSI_COEX_Q_INVALID) {
+ rsi_dbg(ERR_ZONE, "Invalid coex queue\n");
+ return -EINVAL;
+ }
+ if (coex_q != RSI_COEX_Q_COMMON &&
+ coex_q != RSI_COEX_Q_WLAN) {
+ skb_queue_tail(&coex_cb->coex_tx_qs[coex_q], skb);
+ rsi_set_event(&coex_cb->coex_tx_thread.event);
+ return 0;
+ }
+ if (common->iface_down) {
+ tx_params =
+ (struct skb_info *)&IEEE80211_SKB_CB(skb)->driver_data;
+
+ if (!(tx_params->flags & INTERNAL_MGMT_PKT)) {
+ rsi_indicate_tx_status(common->priv, skb, -EINVAL);
+ return 0;
+ }
+ }
+
+ /* Send packet to hal */
+ if (skb->priority == MGMT_SOFT_Q)
+ status = rsi_send_mgmt_pkt(common, skb);
+ else
+ status = rsi_send_data_pkt(common, skb);
+
+ return status;
+}
+
+int rsi_coex_attach(struct rsi_common *common)
+{
+ struct rsi_coex_ctrl_block *coex_cb;
+ int cnt;
+
+ coex_cb = kzalloc(sizeof(*coex_cb), GFP_KERNEL);
+ if (!coex_cb)
+ return -ENOMEM;
+
+ common->coex_cb = (void *)coex_cb;
+ coex_cb->priv = common;
+
+ /* Initialize co-ex queues */
+ for (cnt = 0; cnt < NUM_COEX_TX_QUEUES; cnt++)
+ skb_queue_head_init(&coex_cb->coex_tx_qs[cnt]);
+ rsi_init_event(&coex_cb->coex_tx_thread.event);
+
+ /* Initialize co-ex thread */
+ if (rsi_create_kthread(common,
+ &coex_cb->coex_tx_thread,
+ rsi_coex_scheduler_thread,
+ "Coex-Tx-Thread")) {
+ rsi_dbg(ERR_ZONE, "%s: Unable to init tx thrd\n", __func__);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+void rsi_coex_detach(struct rsi_common *common)
+{
+ struct rsi_coex_ctrl_block *coex_cb =
+ (struct rsi_coex_ctrl_block *)common->coex_cb;
+ int cnt;
+
+ rsi_kill_thread(&coex_cb->coex_tx_thread);
+
+ for (cnt = 0; cnt < NUM_COEX_TX_QUEUES; cnt++)
+ skb_queue_purge(&coex_cb->coex_tx_qs[cnt]);
+
+ kfree(coex_cb);
+}
diff --git a/drivers/net/wireless/rsi/rsi_91x_core.c b/drivers/net/wireless/rsi/rsi_91x_core.c
index d0d2201830e8..5dafd2e1306c 100644
--- a/drivers/net/wireless/rsi/rsi_91x_core.c
+++ b/drivers/net/wireless/rsi/rsi_91x_core.c
@@ -17,6 +17,7 @@
#include "rsi_mgmt.h"
#include "rsi_common.h"
#include "rsi_hal.h"
+#include "rsi_coex.h"
/**
* rsi_determine_min_weight_queue() - This function determines the queue with
@@ -301,14 +302,23 @@ void rsi_core_qos_processor(struct rsi_common *common)
mutex_unlock(&common->tx_lock);
break;
}
-
- if (q_num == MGMT_SOFT_Q) {
- status = rsi_send_mgmt_pkt(common, skb);
- } else if (q_num == MGMT_BEACON_Q) {
+ if (q_num == MGMT_BEACON_Q) {
status = rsi_send_pkt_to_bus(common, skb);
dev_kfree_skb(skb);
} else {
- status = rsi_send_data_pkt(common, skb);
+#ifdef CONFIG_RSI_COEX
+ if (common->coex_mode > 1) {
+ status = rsi_coex_send_pkt(common, skb,
+ RSI_WLAN_Q);
+ } else {
+#endif
+ if (q_num == MGMT_SOFT_Q)
+ status = rsi_send_mgmt_pkt(common, skb);
+ else
+ status = rsi_send_data_pkt(common, skb);
+#ifdef CONFIG_RSI_COEX
+ }
+#endif
}
if (status) {
diff --git a/drivers/net/wireless/rsi/rsi_91x_hal.c b/drivers/net/wireless/rsi/rsi_91x_hal.c
index 1176de646942..de608ae365a4 100644
--- a/drivers/net/wireless/rsi/rsi_91x_hal.c
+++ b/drivers/net/wireless/rsi/rsi_91x_hal.c
@@ -15,6 +15,7 @@
*/
#include <linux/firmware.h>
+#include <net/bluetooth/bluetooth.h>
#include "rsi_mgmt.h"
#include "rsi_hal.h"
#include "rsi_sdio.h"
@@ -24,6 +25,7 @@
static struct ta_metadata metadata_flash_content[] = {
{"flash_content", 0x00010000},
{"rsi/rs9113_wlan_qspi.rps", 0x00010000},
+ {"rsi/rs9113_wlan_bt_dual_mode.rps", 0x00010000},
};
int rsi_send_pkt_to_bus(struct rsi_common *common, struct sk_buff *skb)
@@ -31,8 +33,15 @@ int rsi_send_pkt_to_bus(struct rsi_common *common, struct sk_buff *skb)
struct rsi_hw *adapter = common->priv;
int status;
+ if (common->coex_mode > 1)
+ mutex_lock(&common->tx_bus_mutex);
+
status = adapter->host_intf_ops->write_pkt(common->priv,
skb->data, skb->len);
+
+ if (common->coex_mode > 1)
+ mutex_unlock(&common->tx_bus_mutex);
+
return status;
}
@@ -296,8 +305,7 @@ int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb)
if (status)
goto err;
- status = adapter->host_intf_ops->write_pkt(common->priv, skb->data,
- skb->len);
+ status = rsi_send_pkt_to_bus(common, skb);
if (status)
rsi_dbg(ERR_ZONE, "%s: Failed to write pkt\n", __func__);
@@ -342,8 +350,7 @@ int rsi_send_mgmt_pkt(struct rsi_common *common,
goto err;
rsi_prepare_mgmt_desc(common, skb);
- status = adapter->host_intf_ops->write_pkt(common->priv,
- (u8 *)skb->data, skb->len);
+ status = rsi_send_pkt_to_bus(common, skb);
if (status)
rsi_dbg(ERR_ZONE, "%s: Failed to write the packet\n", __func__);
@@ -352,6 +359,43 @@ err:
return status;
}
+int rsi_send_bt_pkt(struct rsi_common *common, struct sk_buff *skb)
+{
+ int status = -EINVAL;
+ u8 header_size = 0;
+ struct rsi_bt_desc *bt_desc;
+ u8 queueno = ((skb->data[1] >> 4) & 0xf);
+
+ if (queueno == RSI_BT_MGMT_Q) {
+ status = rsi_send_pkt_to_bus(common, skb);
+ if (status)
+ rsi_dbg(ERR_ZONE, "%s: Failed to write bt mgmt pkt\n",
+ __func__);
+ goto out;
+ }
+ header_size = FRAME_DESC_SZ;
+ if (header_size > skb_headroom(skb)) {
+ rsi_dbg(ERR_ZONE, "%s: Not enough headroom\n", __func__);
+ status = -ENOSPC;
+ goto out;
+ }
+ skb_push(skb, header_size);
+ memset(skb->data, 0, header_size);
+ bt_desc = (struct rsi_bt_desc *)skb->data;
+
+ rsi_set_len_qno(&bt_desc->len_qno, (skb->len - FRAME_DESC_SZ),
+ RSI_BT_DATA_Q);
+ bt_desc->bt_pkt_type = cpu_to_le16(bt_cb(skb)->pkt_type);
+
+ status = rsi_send_pkt_to_bus(common, skb);
+ if (status)
+ rsi_dbg(ERR_ZONE, "%s: Failed to write bt pkt\n", __func__);
+
+out:
+ dev_kfree_skb(skb);
+ return status;
+}
+
int rsi_prepare_beacon(struct rsi_common *common, struct sk_buff *skb)
{
struct rsi_hw *adapter = (struct rsi_hw *)common->priv;
@@ -926,10 +970,6 @@ int rsi_hal_device_init(struct rsi_hw *adapter)
{
struct rsi_common *common = adapter->priv;
- common->coex_mode = RSI_DEV_COEX_MODE_WIFI_ALONE;
- common->oper_mode = RSI_DEV_OPMODE_WIFI_ALONE;
- adapter->device_model = RSI_DEV_9113;
-
switch (adapter->device_model) {
case RSI_DEV_9113:
if (rsi_load_firmware(adapter)) {
diff --git a/drivers/net/wireless/rsi/rsi_91x_main.c b/drivers/net/wireless/rsi/rsi_91x_main.c
index 0cb8e68bab58..1485a0c89df2 100644
--- a/drivers/net/wireless/rsi/rsi_91x_main.c
+++ b/drivers/net/wireless/rsi/rsi_91x_main.c
@@ -18,8 +18,10 @@
#include <linux/module.h>
#include <linux/firmware.h>
+#include <net/rsi_91x.h>
#include "rsi_mgmt.h"
#include "rsi_common.h"
+#include "rsi_coex.h"
#include "rsi_hal.h"
u32 rsi_zone_enabled = /* INFO_ZONE |
@@ -34,6 +36,14 @@ u32 rsi_zone_enabled = /* INFO_ZONE |
0;
EXPORT_SYMBOL_GPL(rsi_zone_enabled);
+#ifdef CONFIG_RSI_COEX
+static struct rsi_proto_ops g_proto_ops = {
+ .coex_send_pkt = rsi_coex_send_pkt,
+ .get_host_intf = rsi_get_host_intf,
+ .set_bt_context = rsi_set_bt_context,
+};
+#endif
+
/**
* rsi_dbg() - This function outputs informational messages.
* @zone: Zone of interest for output message.
@@ -60,8 +70,24 @@ EXPORT_SYMBOL_GPL(rsi_dbg);
static char *opmode_str(int oper_mode)
{
switch (oper_mode) {
- case RSI_DEV_OPMODE_WIFI_ALONE:
+ case DEV_OPMODE_WIFI_ALONE:
return "Wi-Fi alone";
+ case DEV_OPMODE_BT_ALONE:
+ return "BT EDR alone";
+ case DEV_OPMODE_BT_LE_ALONE:
+ return "BT LE alone";
+ case DEV_OPMODE_BT_DUAL:
+ return "BT Dual";
+ case DEV_OPMODE_STA_BT:
+ return "Wi-Fi STA + BT EDR";
+ case DEV_OPMODE_STA_BT_LE:
+ return "Wi-Fi STA + BT LE";
+ case DEV_OPMODE_STA_BT_DUAL:
+ return "Wi-Fi STA + BT DUAL";
+ case DEV_OPMODE_AP_BT:
+ return "Wi-Fi AP + BT EDR";
+ case DEV_OPMODE_AP_BT_DUAL:
+ return "Wi-Fi AP + BT DUAL";
}
return "Unknown";
@@ -137,16 +163,19 @@ static struct sk_buff *rsi_prepare_skb(struct rsi_common *common,
*
* Return: 0 on success, -1 on failure.
*/
-int rsi_read_pkt(struct rsi_common *common, s32 rcv_pkt_len)
+int rsi_read_pkt(struct rsi_common *common, u8 *rx_pkt, s32 rcv_pkt_len)
{
u8 *frame_desc = NULL, extended_desc = 0;
u32 index, length = 0, queueno = 0;
u16 actual_length = 0, offset;
struct sk_buff *skb = NULL;
+#ifdef CONFIG_RSI_COEX
+ u8 bt_pkt_type;
+#endif
index = 0;
do {
- frame_desc = &common->rx_data_pkt[index];
+ frame_desc = &rx_pkt[index];
actual_length = *(u16 *)&frame_desc[0];
offset = *(u16 *)&frame_desc[2];
@@ -160,8 +189,15 @@ int rsi_read_pkt(struct rsi_common *common, s32 rcv_pkt_len)
switch (queueno) {
case RSI_COEX_Q:
- rsi_mgmt_pkt_recv(common, (frame_desc + offset));
+#ifdef CONFIG_RSI_COEX
+ if (common->coex_mode > 1)
+ rsi_coex_recv_pkt(common, frame_desc + offset);
+ else
+#endif
+ rsi_mgmt_pkt_recv(common,
+ (frame_desc + offset));
break;
+
case RSI_WIFI_DATA_Q:
skb = rsi_prepare_skb(common,
(frame_desc + offset),
@@ -177,6 +213,25 @@ int rsi_read_pkt(struct rsi_common *common, s32 rcv_pkt_len)
rsi_mgmt_pkt_recv(common, (frame_desc + offset));
break;
+#ifdef CONFIG_RSI_COEX
+ case RSI_BT_MGMT_Q:
+ case RSI_BT_DATA_Q:
+#define BT_RX_PKT_TYPE_OFST 14
+#define BT_CARD_READY_IND 0x89
+ bt_pkt_type = frame_desc[offset + BT_RX_PKT_TYPE_OFST];
+ if (bt_pkt_type == BT_CARD_READY_IND) {
+ rsi_dbg(INFO_ZONE, "BT Card ready recvd\n");
+ if (rsi_bt_ops.attach(common, &g_proto_ops))
+ rsi_dbg(ERR_ZONE,
+ "Failed to attach BT module\n");
+ } else {
+ if (common->bt_adapter)
+ rsi_bt_ops.recv_pkt(common->bt_adapter,
+ frame_desc + offset);
+ }
+ break;
+#endif
+
default:
rsi_dbg(ERR_ZONE, "%s: pkt from invalid queue: %d\n",
__func__, queueno);
@@ -217,13 +272,29 @@ static void rsi_tx_scheduler_thread(struct rsi_common *common)
complete_and_exit(&common->tx_thread.completion, 0);
}
+#ifdef CONFIG_RSI_COEX
+enum rsi_host_intf rsi_get_host_intf(void *priv)
+{
+ struct rsi_common *common = (struct rsi_common *)priv;
+
+ return common->priv->rsi_host_intf;
+}
+
+void rsi_set_bt_context(void *priv, void *bt_context)
+{
+ struct rsi_common *common = (struct rsi_common *)priv;
+
+ common->bt_adapter = bt_context;
+}
+#endif
+
/**
* rsi_91x_init() - This function initializes os interface operations.
* @void: Void.
*
* Return: Pointer to the adapter structure on success, NULL on failure .
*/
-struct rsi_hw *rsi_91x_init(void)
+struct rsi_hw *rsi_91x_init(u16 oper_mode)
{
struct rsi_hw *adapter = NULL;
struct rsi_common *common = NULL;
@@ -251,6 +322,7 @@ struct rsi_hw *rsi_91x_init(void)
mutex_init(&common->mutex);
mutex_init(&common->tx_lock);
mutex_init(&common->rx_lock);
+ mutex_init(&common->tx_bus_mutex);
if (rsi_create_kthread(common,
&common->tx_thread,
@@ -265,6 +337,43 @@ struct rsi_hw *rsi_91x_init(void)
timer_setup(&common->roc_timer, rsi_roc_timeout, 0);
init_completion(&common->wlan_init_completion);
common->init_done = true;
+ adapter->device_model = RSI_DEV_9113;
+ common->oper_mode = oper_mode;
+
+ /* Determine coex mode */
+ switch (common->oper_mode) {
+ case DEV_OPMODE_STA_BT_DUAL:
+ case DEV_OPMODE_STA_BT:
+ case DEV_OPMODE_STA_BT_LE:
+ case DEV_OPMODE_BT_ALONE:
+ case DEV_OPMODE_BT_LE_ALONE:
+ case DEV_OPMODE_BT_DUAL:
+ common->coex_mode = 2;
+ break;
+ case DEV_OPMODE_AP_BT_DUAL:
+ case DEV_OPMODE_AP_BT:
+ common->coex_mode = 4;
+ break;
+ case DEV_OPMODE_WIFI_ALONE:
+ common->coex_mode = 1;
+ break;
+ default:
+ common->oper_mode = 1;
+ common->coex_mode = 1;
+ }
+ rsi_dbg(INFO_ZONE, "%s: oper_mode = %d, coex_mode = %d\n",
+ __func__, common->oper_mode, common->coex_mode);
+
+ adapter->device_model = RSI_DEV_9113;
+#ifdef CONFIG_RSI_COEX
+ if (common->coex_mode > 1) {
+ if (rsi_coex_attach(common)) {
+ rsi_dbg(ERR_ZONE, "Failed to init coex module\n");
+ goto err;
+ }
+ }
+#endif
+
return adapter;
err:
@@ -292,6 +401,16 @@ void rsi_91x_deinit(struct rsi_hw *adapter)
for (ii = 0; ii < NUM_SOFT_QUEUES; ii++)
skb_queue_purge(&common->tx_queue[ii]);
+#ifdef CONFIG_RSI_COEX
+ if (common->coex_mode > 1) {
+ if (common->bt_adapter) {
+ rsi_bt_ops.detach(common->bt_adapter);
+ common->bt_adapter = NULL;
+ }
+ rsi_coex_detach(common);
+ }
+#endif
+
common->init_done = false;
kfree(common);
diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c
index 46c9d5470dfb..c21fca750fd4 100644
--- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c
+++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c
@@ -1791,7 +1791,7 @@ out:
return -EINVAL;
}
-static int rsi_handle_card_ready(struct rsi_common *common, u8 *msg)
+int rsi_handle_card_ready(struct rsi_common *common, u8 *msg)
{
switch (common->fsm_state) {
case FSM_CARD_NOT_READY:
diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio.c b/drivers/net/wireless/rsi/rsi_91x_sdio.c
index b0cf41195051..d76e69c0beaa 100644
--- a/drivers/net/wireless/rsi/rsi_91x_sdio.c
+++ b/drivers/net/wireless/rsi/rsi_91x_sdio.c
@@ -18,8 +18,17 @@
#include <linux/module.h>
#include "rsi_sdio.h"
#include "rsi_common.h"
+#include "rsi_coex.h"
#include "rsi_hal.h"
+/* Default operating mode is wlan STA + BT */
+static u16 dev_oper_mode = DEV_OPMODE_STA_BT_DUAL;
+module_param(dev_oper_mode, ushort, 0444);
+MODULE_PARM_DESC(dev_oper_mode,
+ "1[Wi-Fi], 4[BT], 8[BT LE], 5[Wi-Fi STA + BT classic]\n"
+ "9[Wi-Fi STA + BT LE], 13[Wi-Fi STA + BT classic + BT LE]\n"
+ "6[AP + BT classic], 14[AP + BT classic + BT LE]");
+
/**
* rsi_sdio_set_cmd52_arg() - This function prepares cmd 52 read/write arg.
* @rw: Read/write
@@ -567,7 +576,7 @@ static int rsi_sdio_load_data_master_write(struct rsi_hw *adapter,
{
u32 num_blocks, offset, i;
u16 msb_address, lsb_address;
- u8 temp_buf[block_size];
+ u8 *temp_buf;
int status;
num_blocks = instructions_sz / block_size;
@@ -576,11 +585,15 @@ static int rsi_sdio_load_data_master_write(struct rsi_hw *adapter,
rsi_dbg(INFO_ZONE, "ins_size: %d, num_blocks: %d\n",
instructions_sz, num_blocks);
+ temp_buf = kmalloc(block_size, GFP_KERNEL);
+ if (!temp_buf)
+ return -ENOMEM;
+
/* Loading DM ms word in the sdio slave */
status = rsi_sdio_master_access_msword(adapter, msb_address);
if (status < 0) {
rsi_dbg(ERR_ZONE, "%s: Unable to set ms word reg\n", __func__);
- return status;
+ goto out_free;
}
for (offset = 0, i = 0; i < num_blocks; i++, offset += block_size) {
@@ -592,7 +605,7 @@ static int rsi_sdio_load_data_master_write(struct rsi_hw *adapter,
temp_buf, block_size);
if (status < 0) {
rsi_dbg(ERR_ZONE, "%s: failed to write\n", __func__);
- return status;
+ goto out_free;
}
rsi_dbg(INFO_ZONE, "%s: loading block: %d\n", __func__, i);
base_address += block_size;
@@ -607,7 +620,7 @@ static int rsi_sdio_load_data_master_write(struct rsi_hw *adapter,
rsi_dbg(ERR_ZONE,
"%s: Unable to set ms word reg\n",
__func__);
- return status;
+ goto out_free;
}
}
}
@@ -623,12 +636,16 @@ static int rsi_sdio_load_data_master_write(struct rsi_hw *adapter,
temp_buf,
instructions_sz % block_size);
if (status < 0)
- return status;
+ goto out_free;
rsi_dbg(INFO_ZONE,
"Written Last Block in Address 0x%x Successfully\n",
offset | RSI_SD_REQUEST_MASTER);
}
- return 0;
+
+ status = 0;
+out_free:
+ kfree(temp_buf);
+ return status;
}
#define FLASH_SIZE_ADDR 0x04000016
@@ -636,11 +653,14 @@ static int rsi_sdio_master_reg_read(struct rsi_hw *adapter, u32 addr,
u32 *read_buf, u16 size)
{
u32 addr_on_bus, *data;
- u32 align[2] = {};
u16 ms_addr;
int status;
- data = PTR_ALIGN(&align[0], 8);
+ data = kzalloc(RSI_MASTER_REG_BUF_SIZE, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data = PTR_ALIGN(data, 8);
ms_addr = (addr >> 16);
status = rsi_sdio_master_access_msword(adapter, ms_addr);
@@ -648,7 +668,7 @@ static int rsi_sdio_master_reg_read(struct rsi_hw *adapter, u32 addr,
rsi_dbg(ERR_ZONE,
"%s: Unable to set ms word to common reg\n",
__func__);
- return status;
+ goto err;
}
addr &= 0xFFFF;
@@ -666,7 +686,7 @@ static int rsi_sdio_master_reg_read(struct rsi_hw *adapter, u32 addr,
(u8 *)data, 4);
if (status < 0) {
rsi_dbg(ERR_ZONE, "%s: AHB register read failed\n", __func__);
- return status;
+ goto err;
}
if (size == 2) {
if ((addr & 0x3) == 0)
@@ -688,17 +708,23 @@ static int rsi_sdio_master_reg_read(struct rsi_hw *adapter, u32 addr,
*read_buf = *data;
}
- return 0;
+err:
+ kfree(data);
+ return status;
}
static int rsi_sdio_master_reg_write(struct rsi_hw *adapter,
unsigned long addr,
unsigned long data, u16 size)
{
- unsigned long data1[2], *data_aligned;
+ unsigned long *data_aligned;
int status;
- data_aligned = PTR_ALIGN(&data1[0], 8);
+ data_aligned = kzalloc(RSI_MASTER_REG_BUF_SIZE, GFP_KERNEL);
+ if (!data_aligned)
+ return -ENOMEM;
+
+ data_aligned = PTR_ALIGN(data_aligned, 8);
if (size == 2) {
*data_aligned = ((data << 16) | (data & 0xFFFF));
@@ -717,6 +743,7 @@ static int rsi_sdio_master_reg_write(struct rsi_hw *adapter,
rsi_dbg(ERR_ZONE,
"%s: Unable to set ms word to common reg\n",
__func__);
+ kfree(data_aligned);
return -EIO;
}
addr = addr & 0xFFFF;
@@ -726,12 +753,12 @@ static int rsi_sdio_master_reg_write(struct rsi_hw *adapter,
(adapter,
(addr | RSI_SD_REQUEST_MASTER),
(u8 *)data_aligned, size);
- if (status < 0) {
+ if (status < 0)
rsi_dbg(ERR_ZONE,
"%s: Unable to do AHB reg write\n", __func__);
- return status;
- }
- return 0;
+
+ kfree(data_aligned);
+ return status;
}
/**
@@ -754,6 +781,8 @@ static int rsi_sdio_host_intf_write_pkt(struct rsi_hw *adapter,
int status;
queueno = ((pkt[1] >> 4) & 0xf);
+ if (queueno == RSI_BT_MGMT_Q || queueno == RSI_BT_DATA_Q)
+ queueno = RSI_BT_Q;
num_blocks = len / block_size;
@@ -922,14 +951,16 @@ static int rsi_probe(struct sdio_func *pfunction,
const struct sdio_device_id *id)
{
struct rsi_hw *adapter;
+ struct rsi_91x_sdiodev *sdev;
+ int status;
rsi_dbg(INIT_ZONE, "%s: Init function called\n", __func__);
- adapter = rsi_91x_init();
+ adapter = rsi_91x_init(dev_oper_mode);
if (!adapter) {
rsi_dbg(ERR_ZONE, "%s: Failed to init os intf ops\n",
__func__);
- return 1;
+ return -EINVAL;
}
adapter->rsi_host_intf = RSI_HOST_INTF_SDIO;
adapter->host_intf_ops = &sdio_host_intf_ops;
@@ -937,39 +968,61 @@ static int rsi_probe(struct sdio_func *pfunction,
if (rsi_init_sdio_interface(adapter, pfunction)) {
rsi_dbg(ERR_ZONE, "%s: Failed to init sdio interface\n",
__func__);
- goto fail;
+ status = -EIO;
+ goto fail_free_adapter;
}
+ sdev = (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+ rsi_init_event(&sdev->rx_thread.event);
+ status = rsi_create_kthread(adapter->priv, &sdev->rx_thread,
+ rsi_sdio_rx_thread, "SDIO-RX-Thread");
+ if (status) {
+ rsi_dbg(ERR_ZONE, "%s: Unable to init rx thrd\n", __func__);
+ goto fail_kill_thread;
+ }
+ skb_queue_head_init(&sdev->rx_q.head);
+ sdev->rx_q.num_rx_pkts = 0;
+
sdio_claim_host(pfunction);
if (sdio_claim_irq(pfunction, rsi_handle_interrupt)) {
rsi_dbg(ERR_ZONE, "%s: Failed to request IRQ\n", __func__);
sdio_release_host(pfunction);
- goto fail;
+ status = -EIO;
+ goto fail_claim_irq;
}
sdio_release_host(pfunction);
rsi_dbg(INIT_ZONE, "%s: Registered Interrupt handler\n", __func__);
if (rsi_hal_device_init(adapter)) {
rsi_dbg(ERR_ZONE, "%s: Failed in device init\n", __func__);
- sdio_claim_host(pfunction);
- sdio_release_irq(pfunction);
- sdio_disable_func(pfunction);
- sdio_release_host(pfunction);
- goto fail;
+ status = -EINVAL;
+ goto fail_dev_init;
}
rsi_dbg(INFO_ZONE, "===> RSI Device Init Done <===\n");
if (rsi_sdio_master_access_msword(adapter, MISC_CFG_BASE_ADDR)) {
rsi_dbg(ERR_ZONE, "%s: Unable to set ms word reg\n", __func__);
- return -EIO;
+ status = -EIO;
+ goto fail_dev_init;
}
adapter->priv->hibernate_resume = false;
adapter->priv->reinit_hw = false;
return 0;
-fail:
+
+fail_dev_init:
+ sdio_claim_host(pfunction);
+ sdio_release_irq(pfunction);
+ sdio_release_host(pfunction);
+fail_claim_irq:
+ rsi_kill_thread(&sdev->rx_thread);
+fail_kill_thread:
+ sdio_claim_host(pfunction);
+ sdio_disable_func(pfunction);
+ sdio_release_host(pfunction);
+fail_free_adapter:
rsi_91x_deinit(adapter);
rsi_dbg(ERR_ZONE, "%s: Failed in probe...Exiting\n", __func__);
- return 1;
+ return status;
}
static void ulp_read_write(struct rsi_hw *adapter, u16 addr, u32 data,
@@ -1065,6 +1118,8 @@ static void rsi_disconnect(struct sdio_func *pfunction)
return;
dev = (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+
+ rsi_kill_thread(&dev->rx_thread);
sdio_claim_host(pfunction);
sdio_release_irq(pfunction);
sdio_release_host(pfunction);
diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c
index 8e2a95c486b0..612c211e21a1 100644
--- a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c
+++ b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c
@@ -16,6 +16,7 @@
*/
#include <linux/firmware.h>
+#include <net/rsi_91x.h>
#include "rsi_sdio.h"
#include "rsi_common.h"
@@ -59,6 +60,43 @@ int rsi_sdio_master_access_msword(struct rsi_hw *adapter, u16 ms_word)
return status;
}
+void rsi_sdio_rx_thread(struct rsi_common *common)
+{
+ struct rsi_hw *adapter = common->priv;
+ struct rsi_91x_sdiodev *sdev = adapter->rsi_dev;
+ struct sk_buff *skb;
+ int status;
+
+ do {
+ rsi_wait_event(&sdev->rx_thread.event, EVENT_WAIT_FOREVER);
+ rsi_reset_event(&sdev->rx_thread.event);
+
+ while (true) {
+ if (atomic_read(&sdev->rx_thread.thread_done))
+ goto out;
+
+ skb = skb_dequeue(&sdev->rx_q.head);
+ if (!skb)
+ break;
+ if (sdev->rx_q.num_rx_pkts > 0)
+ sdev->rx_q.num_rx_pkts--;
+ status = rsi_read_pkt(common, skb->data, skb->len);
+ if (status) {
+ rsi_dbg(ERR_ZONE, "Failed to read the packet\n");
+ dev_kfree_skb(skb);
+ break;
+ }
+ dev_kfree_skb(skb);
+ }
+ } while (1);
+
+out:
+ rsi_dbg(INFO_ZONE, "%s: Terminated SDIO RX thread\n", __func__);
+ skb_queue_purge(&sdev->rx_q.head);
+ atomic_inc(&sdev->rx_thread.thread_done);
+ complete_and_exit(&sdev->rx_thread.completion, 0);
+}
+
/**
* rsi_process_pkt() - This Function reads rx_blocks register and figures out
* the size of the rx pkt.
@@ -75,6 +113,10 @@ static int rsi_process_pkt(struct rsi_common *common)
u32 rcv_pkt_len = 0;
int status = 0;
u8 value = 0;
+ struct sk_buff *skb;
+
+ if (dev->rx_q.num_rx_pkts >= RSI_MAX_RX_PKTS)
+ return 0;
num_blks = ((adapter->interrupt_status & 1) |
((adapter->interrupt_status >> RECV_NUM_BLOCKS) << 1));
@@ -102,27 +144,24 @@ static int rsi_process_pkt(struct rsi_common *common)
rcv_pkt_len = (num_blks * 256);
- common->rx_data_pkt = kmalloc(rcv_pkt_len, GFP_KERNEL);
- if (!common->rx_data_pkt) {
- rsi_dbg(ERR_ZONE, "%s: Failed in memory allocation\n",
- __func__);
+ skb = dev_alloc_skb(rcv_pkt_len);
+ if (!skb)
return -ENOMEM;
- }
- status = rsi_sdio_host_intf_read_pkt(adapter,
- common->rx_data_pkt,
- rcv_pkt_len);
+ status = rsi_sdio_host_intf_read_pkt(adapter, skb->data, rcv_pkt_len);
if (status) {
rsi_dbg(ERR_ZONE, "%s: Failed to read packet from card\n",
__func__);
- goto fail;
+ dev_kfree_skb(skb);
+ return status;
}
+ skb_put(skb, rcv_pkt_len);
+ skb_queue_tail(&dev->rx_q.head, skb);
+ dev->rx_q.num_rx_pkts++;
- status = rsi_read_pkt(common, rcv_pkt_len);
+ rsi_set_event(&dev->rx_thread.event);
-fail:
- kfree(common->rx_data_pkt);
- return status;
+ return 0;
}
/**
diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c
index 8f8443833348..7b8bae313aa9 100644
--- a/drivers/net/wireless/rsi/rsi_91x_usb.c
+++ b/drivers/net/wireless/rsi/rsi_91x_usb.c
@@ -16,8 +16,20 @@
*/
#include <linux/module.h>
+#include <net/rsi_91x.h>
#include "rsi_usb.h"
#include "rsi_hal.h"
+#include "rsi_coex.h"
+
+/* Default operating mode is wlan STA + BT */
+static u16 dev_oper_mode = DEV_OPMODE_STA_BT_DUAL;
+module_param(dev_oper_mode, ushort, 0444);
+MODULE_PARM_DESC(dev_oper_mode,
+ "1[Wi-Fi], 4[BT], 8[BT LE], 5[Wi-Fi STA + BT classic]\n"
+ "9[Wi-Fi STA + BT LE], 13[Wi-Fi STA + BT classic + BT LE]\n"
+ "6[AP + BT classic], 14[AP + BT classic + BT LE]");
+
+static int rsi_rx_urb_submit(struct rsi_hw *adapter, u8 ep_num);
/**
* rsi_usb_card_write() - This function writes to the USB Card.
@@ -103,41 +115,41 @@ static int rsi_find_bulk_in_and_out_endpoints(struct usb_interface *interface,
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
__le16 buffer_size;
- int ii, bep_found = 0;
+ int ii, bin_found = 0, bout_found = 0;
iface_desc = &(interface->altsetting[0]);
for (ii = 0; ii < iface_desc->desc.bNumEndpoints; ++ii) {
endpoint = &(iface_desc->endpoint[ii].desc);
- if ((!(dev->bulkin_endpoint_addr)) &&
+ if (!dev->bulkin_endpoint_addr[bin_found] &&
(endpoint->bEndpointAddress & USB_DIR_IN) &&
- ((endpoint->bmAttributes &
- USB_ENDPOINT_XFERTYPE_MASK) ==
+ ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
USB_ENDPOINT_XFER_BULK)) {
buffer_size = endpoint->wMaxPacketSize;
- dev->bulkin_size = buffer_size;
- dev->bulkin_endpoint_addr =
+ dev->bulkin_size[bin_found] = buffer_size;
+ dev->bulkin_endpoint_addr[bin_found] =
endpoint->bEndpointAddress;
+ bin_found++;
}
- if (!dev->bulkout_endpoint_addr[bep_found] &&
+ if (!dev->bulkout_endpoint_addr[bout_found] &&
!(endpoint->bEndpointAddress & USB_DIR_IN) &&
((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
- USB_ENDPOINT_XFER_BULK)) {
- dev->bulkout_endpoint_addr[bep_found] =
- endpoint->bEndpointAddress;
+ USB_ENDPOINT_XFER_BULK)) {
buffer_size = endpoint->wMaxPacketSize;
- dev->bulkout_size[bep_found] = buffer_size;
- bep_found++;
+ dev->bulkout_endpoint_addr[bout_found] =
+ endpoint->bEndpointAddress;
+ dev->bulkout_size[bout_found] = buffer_size;
+ bout_found++;
}
- if (bep_found >= MAX_BULK_EP)
+ if (bin_found >= MAX_BULK_EP || bout_found >= MAX_BULK_EP)
break;
}
- if (!(dev->bulkin_endpoint_addr) &&
- (dev->bulkout_endpoint_addr[0]))
+ if (!(dev->bulkin_endpoint_addr[0]) &&
+ dev->bulkout_endpoint_addr[0])
return -EINVAL;
return 0;
@@ -247,13 +259,33 @@ static int rsi_usb_reg_write(struct usb_device *usbdev,
*/
static void rsi_rx_done_handler(struct urb *urb)
{
- struct rsi_hw *adapter = urb->context;
- struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
+ struct rx_usb_ctrl_block *rx_cb = urb->context;
+ struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)rx_cb->data;
+ int status = -EINVAL;
if (urb->status)
- return;
+ goto out;
+
+ if (urb->actual_length <= 0) {
+ rsi_dbg(INFO_ZONE, "%s: Zero length packet\n", __func__);
+ goto out;
+ }
+ if (skb_queue_len(&dev->rx_q) >= RSI_MAX_RX_PKTS) {
+ rsi_dbg(INFO_ZONE, "Max RX packets reached\n");
+ goto out;
+ }
+ skb_put(rx_cb->rx_skb, urb->actual_length);
+ skb_queue_tail(&dev->rx_q, rx_cb->rx_skb);
rsi_set_event(&dev->rx_thread.event);
+ status = 0;
+
+out:
+ if (rsi_rx_urb_submit(dev->priv, rx_cb->ep_num))
+ rsi_dbg(ERR_ZONE, "%s: Failed in urb submission", __func__);
+
+ if (status)
+ dev_kfree_skb(rx_cb->rx_skb);
}
/**
@@ -262,20 +294,34 @@ static void rsi_rx_done_handler(struct urb *urb)
*
* Return: 0 on success, a negative error code on failure.
*/
-static int rsi_rx_urb_submit(struct rsi_hw *adapter)
+static int rsi_rx_urb_submit(struct rsi_hw *adapter, u8 ep_num)
{
struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
- struct urb *urb = dev->rx_usb_urb[0];
+ struct rx_usb_ctrl_block *rx_cb = &dev->rx_cb[ep_num - 1];
+ struct urb *urb = rx_cb->rx_urb;
int status;
+ struct sk_buff *skb;
+ u8 dword_align_bytes = 0;
+
+#define RSI_MAX_RX_USB_PKT_SIZE 3000
+ skb = dev_alloc_skb(RSI_MAX_RX_USB_PKT_SIZE);
+ if (!skb)
+ return -ENOMEM;
+ skb_reserve(skb, MAX_DWORD_ALIGN_BYTES);
+ dword_align_bytes = (unsigned long)skb->data & 0x3f;
+ if (dword_align_bytes > 0)
+ skb_push(skb, dword_align_bytes);
+ urb->transfer_buffer = skb->data;
+ rx_cb->rx_skb = skb;
usb_fill_bulk_urb(urb,
dev->usbdev,
usb_rcvbulkpipe(dev->usbdev,
- dev->bulkin_endpoint_addr),
+ dev->bulkin_endpoint_addr[ep_num - 1]),
urb->transfer_buffer,
- 3000,
+ RSI_MAX_RX_USB_PKT_SIZE,
rsi_rx_done_handler,
- adapter);
+ rx_cb);
status = usb_submit_urb(urb, GFP_KERNEL);
if (status)
@@ -487,11 +533,51 @@ static void rsi_deinit_usb_interface(struct rsi_hw *adapter)
struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
rsi_kill_thread(&dev->rx_thread);
- usb_free_urb(dev->rx_usb_urb[0]);
- kfree(adapter->priv->rx_data_pkt);
+
+ usb_free_urb(dev->rx_cb[0].rx_urb);
+ if (adapter->priv->coex_mode > 1)
+ usb_free_urb(dev->rx_cb[1].rx_urb);
+
kfree(dev->tx_buffer);
}
+static int rsi_usb_init_rx(struct rsi_hw *adapter)
+{
+ struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
+ struct rx_usb_ctrl_block *rx_cb;
+ u8 idx, num_rx_cb;
+
+ num_rx_cb = (adapter->priv->coex_mode > 1 ? 2 : 1);
+
+ for (idx = 0; idx < num_rx_cb; idx++) {
+ rx_cb = &dev->rx_cb[idx];
+
+ rx_cb->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!rx_cb->rx_urb) {
+ rsi_dbg(ERR_ZONE, "Failed alloc rx urb[%d]\n", idx);
+ goto err;
+ }
+ rx_cb->ep_num = idx + 1;
+ rx_cb->data = (void *)dev;
+ }
+ skb_queue_head_init(&dev->rx_q);
+ rsi_init_event(&dev->rx_thread.event);
+ if (rsi_create_kthread(adapter->priv, &dev->rx_thread,
+ rsi_usb_rx_thread, "RX-Thread")) {
+ rsi_dbg(ERR_ZONE, "%s: Unable to init rx thrd\n", __func__);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ usb_free_urb(dev->rx_cb[0].rx_urb);
+ if (adapter->priv->coex_mode > 1)
+ usb_free_urb(dev->rx_cb[1].rx_urb);
+
+ return -1;
+}
+
/**
* rsi_init_usb_interface() - This function initializes the usb interface.
* @adapter: Pointer to the adapter structure.
@@ -503,7 +589,6 @@ static int rsi_init_usb_interface(struct rsi_hw *adapter,
struct usb_interface *pfunction)
{
struct rsi_91x_usbdev *rsi_dev;
- struct rsi_common *common = adapter->priv;
int status;
rsi_dev = kzalloc(sizeof(*rsi_dev), GFP_KERNEL);
@@ -512,49 +597,37 @@ static int rsi_init_usb_interface(struct rsi_hw *adapter,
adapter->rsi_dev = rsi_dev;
rsi_dev->usbdev = interface_to_usbdev(pfunction);
+ rsi_dev->priv = (void *)adapter;
- if (rsi_find_bulk_in_and_out_endpoints(pfunction, adapter))
- return -EINVAL;
+ if (rsi_find_bulk_in_and_out_endpoints(pfunction, adapter)) {
+ status = -EINVAL;
+ goto fail_eps;
+ }
adapter->device = &pfunction->dev;
usb_set_intfdata(pfunction, adapter);
- common->rx_data_pkt = kmalloc(2048, GFP_KERNEL);
- if (!common->rx_data_pkt) {
- rsi_dbg(ERR_ZONE, "%s: Failed to allocate memory\n",
- __func__);
- return -ENOMEM;
- }
-
rsi_dev->tx_buffer = kmalloc(2048, GFP_KERNEL);
if (!rsi_dev->tx_buffer) {
status = -ENOMEM;
- goto fail_tx;
+ goto fail_eps;
}
- rsi_dev->rx_usb_urb[0] = usb_alloc_urb(0, GFP_KERNEL);
- if (!rsi_dev->rx_usb_urb[0]) {
+
+ if (rsi_usb_init_rx(adapter)) {
+ rsi_dbg(ERR_ZONE, "Failed to init RX handle\n");
status = -ENOMEM;
goto fail_rx;
}
- rsi_dev->rx_usb_urb[0]->transfer_buffer = adapter->priv->rx_data_pkt;
+
rsi_dev->tx_blk_size = 252;
adapter->block_size = rsi_dev->tx_blk_size;
/* Initializing function callbacks */
- adapter->rx_urb_submit = rsi_rx_urb_submit;
adapter->check_hw_queue_status = rsi_usb_check_queue_status;
adapter->determine_event_timeout = rsi_usb_event_timeout;
adapter->rsi_host_intf = RSI_HOST_INTF_USB;
adapter->host_intf_ops = &usb_host_intf_ops;
- rsi_init_event(&rsi_dev->rx_thread.event);
- status = rsi_create_kthread(common, &rsi_dev->rx_thread,
- rsi_usb_rx_thread, "RX-Thread");
- if (status) {
- rsi_dbg(ERR_ZONE, "%s: Unable to init rx thrd\n", __func__);
- goto fail_thread;
- }
-
#ifdef CONFIG_RSI_DEBUGFS
/* In USB, one less than the MAX_DEBUGFS_ENTRIES entries is required */
adapter->num_debugfs_entries = (MAX_DEBUGFS_ENTRIES - 1);
@@ -563,12 +636,12 @@ static int rsi_init_usb_interface(struct rsi_hw *adapter,
rsi_dbg(INIT_ZONE, "%s: Enabled the interface\n", __func__);
return 0;
-fail_thread:
- usb_free_urb(rsi_dev->rx_usb_urb[0]);
fail_rx:
kfree(rsi_dev->tx_buffer);
-fail_tx:
- kfree(common->rx_data_pkt);
+
+fail_eps:
+ kfree(rsi_dev);
+
return status;
}
@@ -662,7 +735,7 @@ static int rsi_probe(struct usb_interface *pfunction,
rsi_dbg(INIT_ZONE, "%s: Init function called\n", __func__);
- adapter = rsi_91x_init();
+ adapter = rsi_91x_init(dev_oper_mode);
if (!adapter) {
rsi_dbg(ERR_ZONE, "%s: Failed to init os intf ops\n",
__func__);
@@ -698,10 +771,16 @@ static int rsi_probe(struct usb_interface *pfunction,
rsi_dbg(INIT_ZONE, "%s: Device Init Done\n", __func__);
}
- status = rsi_rx_urb_submit(adapter);
+ status = rsi_rx_urb_submit(adapter, WLAN_EP);
if (status)
goto err1;
+ if (adapter->priv->coex_mode > 1) {
+ status = rsi_rx_urb_submit(adapter, BT_EP);
+ if (status)
+ goto err1;
+ }
+
return 0;
err1:
rsi_deinit_usb_interface(adapter);
diff --git a/drivers/net/wireless/rsi/rsi_91x_usb_ops.c b/drivers/net/wireless/rsi/rsi_91x_usb_ops.c
index 465692b3c351..b1687d22f73f 100644
--- a/drivers/net/wireless/rsi/rsi_91x_usb_ops.c
+++ b/drivers/net/wireless/rsi/rsi_91x_usb_ops.c
@@ -30,31 +30,32 @@ void rsi_usb_rx_thread(struct rsi_common *common)
struct rsi_hw *adapter = common->priv;
struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
int status;
+ struct sk_buff *skb;
do {
rsi_wait_event(&dev->rx_thread.event, EVENT_WAIT_FOREVER);
+ rsi_reset_event(&dev->rx_thread.event);
- if (atomic_read(&dev->rx_thread.thread_done))
- goto out;
+ while (true) {
+ if (atomic_read(&dev->rx_thread.thread_done))
+ goto out;
- mutex_lock(&common->rx_lock);
- status = rsi_read_pkt(common, 0);
- if (status) {
- rsi_dbg(ERR_ZONE, "%s: Failed To read data", __func__);
- mutex_unlock(&common->rx_lock);
- return;
- }
- mutex_unlock(&common->rx_lock);
- rsi_reset_event(&dev->rx_thread.event);
- if (adapter->rx_urb_submit(adapter)) {
- rsi_dbg(ERR_ZONE,
- "%s: Failed in urb submission", __func__);
- return;
+ skb = skb_dequeue(&dev->rx_q);
+ if (!skb)
+ break;
+ status = rsi_read_pkt(common, skb->data, 0);
+ if (status) {
+ rsi_dbg(ERR_ZONE, "%s: Failed To read data",
+ __func__);
+ break;
+ }
+ dev_kfree_skb(skb);
}
} while (1);
out:
rsi_dbg(INFO_ZONE, "%s: Terminated thread\n", __func__);
+ skb_queue_purge(&dev->rx_q);
complete_and_exit(&dev->rx_thread.completion, 0);
}
diff --git a/drivers/net/wireless/rsi/rsi_coex.h b/drivers/net/wireless/rsi/rsi_coex.h
new file mode 100644
index 000000000000..0fdc67f37a56
--- /dev/null
+++ b/drivers/net/wireless/rsi/rsi_coex.h
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2018 Redpine Signals Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __RSI_COEX_H__
+#define __RSI_COEX_H__
+
+#include "rsi_common.h"
+
+#ifdef CONFIG_RSI_COEX
+#define COMMON_CARD_READY_IND 0
+#define NUM_COEX_TX_QUEUES 5
+
+struct rsi_coex_ctrl_block {
+ struct rsi_common *priv;
+ struct sk_buff_head coex_tx_qs[NUM_COEX_TX_QUEUES];
+ struct rsi_thread coex_tx_thread;
+};
+
+int rsi_coex_attach(struct rsi_common *common);
+void rsi_coex_detach(struct rsi_common *common);
+int rsi_coex_send_pkt(void *priv, struct sk_buff *skb, u8 proto_type);
+int rsi_coex_recv_pkt(struct rsi_common *common, u8 *msg);
+#endif
+#endif
diff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h
index d07dbba61727..d9ff3b8be86e 100644
--- a/drivers/net/wireless/rsi/rsi_common.h
+++ b/drivers/net/wireless/rsi/rsi_common.h
@@ -62,6 +62,7 @@ static inline int rsi_create_kthread(struct rsi_common *common,
u8 *name)
{
init_completion(&thread->completion);
+ atomic_set(&thread->thread_done, 0);
thread->task = kthread_run(func_ptr, common, "%s", name);
if (IS_ERR(thread->task))
return (int)PTR_ERR(thread->task);
@@ -80,9 +81,9 @@ static inline int rsi_kill_thread(struct rsi_thread *handle)
void rsi_mac80211_detach(struct rsi_hw *hw);
u16 rsi_get_connected_channel(struct ieee80211_vif *vif);
-struct rsi_hw *rsi_91x_init(void);
+struct rsi_hw *rsi_91x_init(u16 oper_mode);
void rsi_91x_deinit(struct rsi_hw *adapter);
-int rsi_read_pkt(struct rsi_common *common, s32 rcv_pkt_len);
+int rsi_read_pkt(struct rsi_common *common, u8 *rx_pkt, s32 rcv_pkt_len);
#ifdef CONFIG_PM
int rsi_config_wowlan(struct rsi_hw *adapter, struct cfg80211_wowlan *wowlan);
#endif
diff --git a/drivers/net/wireless/rsi/rsi_hal.h b/drivers/net/wireless/rsi/rsi_hal.h
index a09d36b6b765..786dccd0b732 100644
--- a/drivers/net/wireless/rsi/rsi_hal.h
+++ b/drivers/net/wireless/rsi/rsi_hal.h
@@ -17,6 +17,17 @@
#ifndef __RSI_HAL_H__
#define __RSI_HAL_H__
+/* Device Operating modes */
+#define DEV_OPMODE_WIFI_ALONE 1
+#define DEV_OPMODE_BT_ALONE 4
+#define DEV_OPMODE_BT_LE_ALONE 8
+#define DEV_OPMODE_BT_DUAL 12
+#define DEV_OPMODE_STA_BT 5
+#define DEV_OPMODE_STA_BT_LE 9
+#define DEV_OPMODE_STA_BT_DUAL 13
+#define DEV_OPMODE_AP_BT 6
+#define DEV_OPMODE_AP_BT_DUAL 14
+
#define FLASH_WRITE_CHUNK_SIZE (4 * 1024)
#define FLASH_SECTOR_SIZE (4 * 1024)
@@ -103,6 +114,7 @@
#define FW_FLASH_OFFSET 0x820
#define LMAC_VER_OFFSET (FW_FLASH_OFFSET + 0x200)
+#define MAX_DWORD_ALIGN_BYTES 64
struct bl_header {
__le32 flags;
@@ -145,8 +157,18 @@ struct rsi_data_desc {
u8 sta_id;
} __packed;
+struct rsi_bt_desc {
+ __le16 len_qno;
+ __le16 reserved1;
+ __le32 reserved2;
+ __le32 reserved3;
+ __le16 reserved4;
+ __le16 bt_pkt_type;
+} __packed;
+
int rsi_hal_device_init(struct rsi_hw *adapter);
int rsi_prepare_beacon(struct rsi_common *common, struct sk_buff *skb);
int rsi_send_pkt_to_bus(struct rsi_common *common, struct sk_buff *skb);
+int rsi_send_bt_pkt(struct rsi_common *common, struct sk_buff *skb);
#endif
diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h
index 8cab630af4a5..ef4fa323694b 100644
--- a/drivers/net/wireless/rsi/rsi_main.h
+++ b/drivers/net/wireless/rsi/rsi_main.h
@@ -20,6 +20,7 @@
#include <linux/string.h>
#include <linux/skbuff.h>
#include <net/mac80211.h>
+#include <net/rsi_91x.h>
struct rsi_sta {
struct ieee80211_sta *sta;
@@ -85,10 +86,6 @@ extern __printf(2, 3) void rsi_dbg(u32 zone, const char *fmt, ...);
#define MGMT_HW_Q 10
#define BEACON_HW_Q 11
-/* Queue information */
-#define RSI_COEX_Q 0x0
-#define RSI_WIFI_MGMT_Q 0x4
-#define RSI_WIFI_DATA_Q 0x5
#define IEEE80211_MGMT_FRAME 0x00
#define IEEE80211_CTL_FRAME 0x04
@@ -115,6 +112,7 @@ extern __printf(2, 3) void rsi_dbg(u32 zone, const char *fmt, ...);
#define RSI_WOW_NO_CONNECTION BIT(1)
#define RSI_DEV_9113 1
+#define RSI_MAX_RX_PKTS 64
struct version_info {
u16 major;
@@ -209,6 +207,7 @@ struct rsi_common {
struct rsi_hw *priv;
struct vif_priv vif_info[RSI_MAX_VIFS];
+ void *coex_cb;
bool mgmt_q_block;
struct version_info lmac_ver;
@@ -273,6 +272,8 @@ struct rsi_common {
u8 obm_ant_sel_val;
int tx_power;
u8 ant_in_use;
+ /* Mutex used for writing packet to bus */
+ struct mutex tx_bus_mutex;
bool hibernate_resume;
bool reinit_hw;
u8 wow_flags;
@@ -291,11 +292,8 @@ struct rsi_common {
bool p2p_enabled;
struct timer_list roc_timer;
struct ieee80211_vif *roc_vif;
-};
-enum host_intf {
- RSI_HOST_INTF_SDIO = 0,
- RSI_HOST_INTF_USB
+ void *bt_adapter;
};
struct eepromrw_info {
@@ -322,7 +320,7 @@ struct rsi_hw {
struct device *device;
u8 sc_nvifs;
- enum host_intf rsi_host_intf;
+ enum rsi_host_intf rsi_host_intf;
u16 block_size;
enum ps_state ps_state;
struct rsi_ps_info ps_info;
@@ -343,7 +341,6 @@ struct rsi_hw {
void *rsi_dev;
struct rsi_host_intf_ops *host_intf_ops;
int (*check_hw_queue_status)(struct rsi_hw *adapter, u8 q_num);
- int (*rx_urb_submit)(struct rsi_hw *adapter);
int (*determine_event_timeout)(struct rsi_hw *adapter);
};
@@ -367,4 +364,8 @@ struct rsi_host_intf_ops {
u8 *fw);
int (*reinit_device)(struct rsi_hw *adapter);
};
+
+enum rsi_host_intf rsi_get_host_intf(void *priv);
+void rsi_set_bt_context(void *priv, void *bt_context);
+
#endif
diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h
index 389094a3f91c..cf6567ae5bbe 100644
--- a/drivers/net/wireless/rsi/rsi_mgmt.h
+++ b/drivers/net/wireless/rsi/rsi_mgmt.h
@@ -57,12 +57,14 @@
#define WOW_PATTERN_SIZE 256
/* Receive Frame Types */
+#define RSI_RX_DESC_MSG_TYPE_OFFSET 2
#define TA_CONFIRM_TYPE 0x01
#define RX_DOT11_MGMT 0x02
#define TX_STATUS_IND 0x04
#define BEACON_EVENT_IND 0x08
#define PROBEREQ_CONFIRM 2
#define CARD_READY_IND 0x00
+#define SLEEP_NOTIFY_IND 0x06
#define RSI_DELETE_PEER 0x0
#define RSI_ADD_PEER 0x1
@@ -638,6 +640,7 @@ static inline void rsi_set_len_qno(__le16 *addr, u16 len, u8 qno)
*addr = cpu_to_le16(len | ((qno & 7) << 12));
}
+int rsi_handle_card_ready(struct rsi_common *common, u8 *msg);
int rsi_mgmt_pkt_recv(struct rsi_common *common, u8 *msg);
int rsi_set_vap_capabilities(struct rsi_common *common, enum opmode mode,
u8 *mac_addr, u8 vap_id, u8 vap_status);
diff --git a/drivers/net/wireless/rsi/rsi_sdio.h b/drivers/net/wireless/rsi/rsi_sdio.h
index 49c549ba6682..ead8e7c4df3a 100644
--- a/drivers/net/wireless/rsi/rsi_sdio.h
+++ b/drivers/net/wireless/rsi/rsi_sdio.h
@@ -46,6 +46,8 @@ enum sdio_interrupt_type {
#define PKT_BUFF_AVAILABLE 1
#define FW_ASSERT_IND 2
+#define RSI_MASTER_REG_BUF_SIZE 12
+
#define RSI_DEVICE_BUFFER_STATUS_REGISTER 0xf3
#define RSI_FN1_INT_REGISTER 0xf9
#define RSI_INT_ENABLE_REGISTER 0x04
@@ -105,6 +107,11 @@ struct receive_info {
u32 buf_available_counter;
};
+struct rsi_sdio_rx_q {
+ u8 num_rx_pkts;
+ struct sk_buff_head head;
+};
+
struct rsi_91x_sdiodev {
struct sdio_func *pfunction;
struct task_struct *sdio_irq_task;
@@ -117,6 +124,8 @@ struct rsi_91x_sdiodev {
u16 tx_blk_size;
u8 write_fail;
bool buff_status_updated;
+ struct rsi_sdio_rx_q rx_q;
+ struct rsi_thread rx_thread;
};
void rsi_interrupt_handler(struct rsi_hw *adapter);
@@ -131,4 +140,5 @@ int rsi_sdio_master_access_msword(struct rsi_hw *adapter, u16 ms_word);
void rsi_sdio_ack_intr(struct rsi_hw *adapter, u8 int_bit);
int rsi_sdio_determine_event_timeout(struct rsi_hw *adapter);
int rsi_sdio_check_buffer_status(struct rsi_hw *adapter, u8 q_num);
+void rsi_sdio_rx_thread(struct rsi_common *common);
#endif
diff --git a/drivers/net/wireless/rsi/rsi_usb.h b/drivers/net/wireless/rsi/rsi_usb.h
index 891daea2d932..a88d59295a98 100644
--- a/drivers/net/wireless/rsi/rsi_usb.h
+++ b/drivers/net/wireless/rsi/rsi_usb.h
@@ -31,7 +31,7 @@
#define USB_VENDOR_REGISTER_WRITE 0x16
#define RSI_USB_TX_HEAD_ROOM 128
-#define MAX_RX_URBS 1
+#define MAX_RX_URBS 2
#define MAX_BULK_EP 8
#define WLAN_EP 1
#define BT_EP 2
@@ -39,19 +39,28 @@
#define RSI_USB_BUF_SIZE 4096
#define RSI_USB_CTRL_BUF_SIZE 0x04
+struct rx_usb_ctrl_block {
+ u8 *data;
+ struct urb *rx_urb;
+ struct sk_buff *rx_skb;
+ u8 ep_num;
+};
+
struct rsi_91x_usbdev {
+ void *priv;
struct rsi_thread rx_thread;
u8 endpoint;
struct usb_device *usbdev;
struct usb_interface *pfunction;
- struct urb *rx_usb_urb[MAX_RX_URBS];
+ struct rx_usb_ctrl_block rx_cb[MAX_RX_URBS];
u8 *tx_buffer;
- __le16 bulkin_size;
- u8 bulkin_endpoint_addr;
+ __le16 bulkin_size[MAX_BULK_EP];
+ u8 bulkin_endpoint_addr[MAX_BULK_EP];
__le16 bulkout_size[MAX_BULK_EP];
u8 bulkout_endpoint_addr[MAX_BULK_EP];
u32 tx_blk_size;
u8 write_fail;
+ struct sk_buff_head rx_q;
};
static inline int rsi_usb_check_queue_status(struct rsi_hw *adapter, u8 q_num)
diff --git a/drivers/net/wireless/st/Kconfig b/drivers/net/wireless/st/Kconfig
index 969b4f6e53b5..ff69a80a9633 100644
--- a/drivers/net/wireless/st/Kconfig
+++ b/drivers/net/wireless/st/Kconfig
@@ -5,8 +5,8 @@ config WLAN_VENDOR_ST
If you have a wireless card belonging to this class, say Y.
Note that the answer to this question doesn't directly affect the
- kernel: saying N will just cause the configurator to skip all
- the questions about cards. If you say Y, you will be asked for
+ kernel: saying N will just cause the configurator to skip all the
+ questions about these cards. If you say Y, you will be asked for
your specific card in the following questions.
if WLAN_VENDOR_ST
diff --git a/drivers/net/wireless/st/cw1200/debug.c b/drivers/net/wireless/st/cw1200/debug.c
index 34f97c31eecf..295cb1a29f25 100644
--- a/drivers/net/wireless/st/cw1200/debug.c
+++ b/drivers/net/wireless/st/cw1200/debug.c
@@ -398,15 +398,15 @@ int cw1200_debug_init(struct cw1200_common *priv)
if (!d->debugfs_phy)
goto err;
- if (!debugfs_create_file("status", S_IRUSR, d->debugfs_phy,
+ if (!debugfs_create_file("status", 0400, d->debugfs_phy,
priv, &fops_status))
goto err;
- if (!debugfs_create_file("counters", S_IRUSR, d->debugfs_phy,
+ if (!debugfs_create_file("counters", 0400, d->debugfs_phy,
priv, &fops_counters))
goto err;
- if (!debugfs_create_file("wsm_dumps", S_IWUSR, d->debugfs_phy,
+ if (!debugfs_create_file("wsm_dumps", 0200, d->debugfs_phy,
priv, &fops_wsm_dumps))
goto err;
diff --git a/drivers/net/wireless/st/cw1200/main.c b/drivers/net/wireless/st/cw1200/main.c
index a186d1df1f29..90dc979f260b 100644
--- a/drivers/net/wireless/st/cw1200/main.c
+++ b/drivers/net/wireless/st/cw1200/main.c
@@ -46,7 +46,7 @@ MODULE_ALIAS("cw1200_core");
/* Accept MAC address of the form macaddr=0x00,0x80,0xE1,0x30,0x40,0x50 */
static u8 cw1200_mac_template[ETH_ALEN] = {0x02, 0x80, 0xe1, 0x00, 0x00, 0x00};
-module_param_array_named(macaddr, cw1200_mac_template, byte, NULL, S_IRUGO);
+module_param_array_named(macaddr, cw1200_mac_template, byte, NULL, 0444);
MODULE_PARM_DESC(macaddr, "Override platform_data MAC address");
static char *cw1200_sdd_path;
diff --git a/drivers/net/wireless/ti/Kconfig b/drivers/net/wireless/ti/Kconfig
index 92fbd6597e34..366c687445ad 100644
--- a/drivers/net/wireless/ti/Kconfig
+++ b/drivers/net/wireless/ti/Kconfig
@@ -5,8 +5,8 @@ config WLAN_VENDOR_TI
If you have a wireless card belonging to this class, say Y.
Note that the answer to this question doesn't directly affect the
- kernel: saying N will just cause the configurator to skip all
- the questions about cards. If you say Y, you will be asked for
+ kernel: saying N will just cause the configurator to skip all the
+ questions about these cards. If you say Y, you will be asked for
your specific card in the following questions.
if WLAN_VENDOR_TI
diff --git a/drivers/net/wireless/ti/wl1251/main.c b/drivers/net/wireless/ti/wl1251/main.c
index 037defd10b91..bd8641ad953b 100644
--- a/drivers/net/wireless/ti/wl1251/main.c
+++ b/drivers/net/wireless/ti/wl1251/main.c
@@ -122,8 +122,7 @@ static int wl1251_fetch_nvs(struct wl1251 *wl)
goto out;
}
- wl->nvs_len = fw->size;
- wl->nvs = kmemdup(fw->data, wl->nvs_len, GFP_KERNEL);
+ wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL);
if (!wl->nvs) {
wl1251_error("could not allocate memory for the nvs file");
@@ -131,6 +130,8 @@ static int wl1251_fetch_nvs(struct wl1251 *wl)
goto out;
}
+ wl->nvs_len = fw->size;
+
ret = 0;
out:
@@ -202,13 +203,6 @@ static int wl1251_chip_wakeup(struct wl1251 *wl)
goto out;
}
- if (wl->nvs == NULL && !wl->use_eeprom) {
- /* No NVS from netlink, try to get it from the filesystem */
- ret = wl1251_fetch_nvs(wl);
- if (ret < 0)
- goto out;
- }
-
out:
return ret;
}
@@ -1446,6 +1440,61 @@ static int wl1251_read_eeprom_mac(struct wl1251 *wl)
return 0;
}
+#define NVS_OFF_MAC_LEN 0x19
+#define NVS_OFF_MAC_ADDR_LO 0x1a
+#define NVS_OFF_MAC_ADDR_HI 0x1b
+#define NVS_OFF_MAC_DATA 0x1c
+
+static int wl1251_check_nvs_mac(struct wl1251 *wl)
+{
+ if (wl->nvs_len < 0x24)
+ return -ENODATA;
+
+ /* length is 2 and data address is 0x546c (ANDed with 0xfffe) */
+ if (wl->nvs[NVS_OFF_MAC_LEN] != 2 ||
+ wl->nvs[NVS_OFF_MAC_ADDR_LO] != 0x6d ||
+ wl->nvs[NVS_OFF_MAC_ADDR_HI] != 0x54)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int wl1251_read_nvs_mac(struct wl1251 *wl)
+{
+ u8 mac[ETH_ALEN];
+ int i, ret;
+
+ ret = wl1251_check_nvs_mac(wl);
+ if (ret)
+ return ret;
+
+ /* MAC is stored in reverse order */
+ for (i = 0; i < ETH_ALEN; i++)
+ mac[i] = wl->nvs[NVS_OFF_MAC_DATA + ETH_ALEN - i - 1];
+
+ /* 00:00:20:07:03:09 is in example file wl1251-nvs.bin, so invalid */
+ if (ether_addr_equal_unaligned(mac, "\x00\x00\x20\x07\x03\x09"))
+ return -EINVAL;
+
+ memcpy(wl->mac_addr, mac, ETH_ALEN);
+ return 0;
+}
+
+static int wl1251_write_nvs_mac(struct wl1251 *wl)
+{
+ int i, ret;
+
+ ret = wl1251_check_nvs_mac(wl);
+ if (ret)
+ return ret;
+
+ /* MAC is stored in reverse order */
+ for (i = 0; i < ETH_ALEN; i++)
+ wl->nvs[NVS_OFF_MAC_DATA + i] = wl->mac_addr[ETH_ALEN - i - 1];
+
+ return 0;
+}
+
static int wl1251_register_hw(struct wl1251 *wl)
{
int ret;
@@ -1489,8 +1538,33 @@ int wl1251_init_ieee80211(struct wl1251 *wl)
wl->hw->queues = 4;
+ if (wl->nvs == NULL && !wl->use_eeprom) {
+ ret = wl1251_fetch_nvs(wl);
+ if (ret < 0)
+ goto out;
+ }
+
if (wl->use_eeprom)
- wl1251_read_eeprom_mac(wl);
+ ret = wl1251_read_eeprom_mac(wl);
+ else
+ ret = wl1251_read_nvs_mac(wl);
+
+ if (ret == 0 && !is_valid_ether_addr(wl->mac_addr))
+ ret = -EINVAL;
+
+ if (ret < 0) {
+ /*
+ * In case our MAC address is not correctly set,
+ * we use a random but Nokia MAC.
+ */
+ static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf};
+ memcpy(wl->mac_addr, nokia_oui, 3);
+ get_random_bytes(wl->mac_addr + 3, 3);
+ if (!wl->use_eeprom)
+ wl1251_write_nvs_mac(wl);
+ wl1251_warning("MAC address in eeprom or nvs data is not valid");
+ wl1251_warning("Setting random MAC address: %pM", wl->mac_addr);
+ }
ret = wl1251_register_hw(wl);
if (ret)
@@ -1511,7 +1585,6 @@ struct ieee80211_hw *wl1251_alloc_hw(void)
struct ieee80211_hw *hw;
struct wl1251 *wl;
int i;
- static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf};
hw = ieee80211_alloc_hw(sizeof(*wl), &wl1251_ops);
if (!hw) {
@@ -1561,13 +1634,6 @@ struct ieee80211_hw *wl1251_alloc_hw(void)
INIT_WORK(&wl->irq_work, wl1251_irq_work);
INIT_WORK(&wl->tx_work, wl1251_tx_work);
- /*
- * In case our MAC address is not correctly set,
- * we use a random but Nokia MAC.
- */
- memcpy(wl->mac_addr, nokia_oui, 3);
- get_random_bytes(wl->mac_addr + 3, 3);
-
wl->state = WL1251_STATE_OFF;
mutex_init(&wl->mutex);
spin_lock_init(&wl->wl_lock);
diff --git a/drivers/net/wireless/ti/wl1251/tx.c b/drivers/net/wireless/ti/wl1251/tx.c
index de2fa6705574..12ed14ebc307 100644
--- a/drivers/net/wireless/ti/wl1251/tx.c
+++ b/drivers/net/wireless/ti/wl1251/tx.c
@@ -221,10 +221,8 @@ static int wl1251_tx_send_packet(struct wl1251 *wl, struct sk_buff *skb,
struct sk_buff *newskb = skb_copy_expand(skb, 0, 3,
GFP_KERNEL);
- if (unlikely(newskb == NULL)) {
- wl1251_error("Can't allocate skb!");
+ if (unlikely(newskb == NULL))
return -EINVAL;
- }
tx_hdr = (struct tx_double_buffer_desc *) newskb->data;
diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c
index 0cf3b4013dd6..ca0f936fc119 100644
--- a/drivers/net/wireless/ti/wl18xx/main.c
+++ b/drivers/net/wireless/ti/wl18xx/main.c
@@ -2092,54 +2092,51 @@ static struct platform_driver wl18xx_driver = {
};
module_platform_driver(wl18xx_driver);
-module_param_named(ht_mode, ht_mode_param, charp, S_IRUSR);
+module_param_named(ht_mode, ht_mode_param, charp, 0400);
MODULE_PARM_DESC(ht_mode, "Force HT mode: wide or siso20");
-module_param_named(board_type, board_type_param, charp, S_IRUSR);
+module_param_named(board_type, board_type_param, charp, 0400);
MODULE_PARM_DESC(board_type, "Board type: fpga, hdk (default), evb, com8 or "
"dvp");
-module_param_named(checksum, checksum_param, bool, S_IRUSR);
+module_param_named(checksum, checksum_param, bool, 0400);
MODULE_PARM_DESC(checksum, "Enable TCP checksum: boolean (defaults to false)");
-module_param_named(dc2dc, dc2dc_param, int, S_IRUSR);
+module_param_named(dc2dc, dc2dc_param, int, 0400);
MODULE_PARM_DESC(dc2dc, "External DC2DC: u8 (defaults to 0)");
-module_param_named(n_antennas_2, n_antennas_2_param, int, S_IRUSR);
+module_param_named(n_antennas_2, n_antennas_2_param, int, 0400);
MODULE_PARM_DESC(n_antennas_2,
"Number of installed 2.4GHz antennas: 1 (default) or 2");
-module_param_named(n_antennas_5, n_antennas_5_param, int, S_IRUSR);
+module_param_named(n_antennas_5, n_antennas_5_param, int, 0400);
MODULE_PARM_DESC(n_antennas_5,
"Number of installed 5GHz antennas: 1 (default) or 2");
-module_param_named(low_band_component, low_band_component_param, int,
- S_IRUSR);
+module_param_named(low_band_component, low_band_component_param, int, 0400);
MODULE_PARM_DESC(low_band_component, "Low band component: u8 "
"(default is 0x01)");
module_param_named(low_band_component_type, low_band_component_type_param,
- int, S_IRUSR);
+ int, 0400);
MODULE_PARM_DESC(low_band_component_type, "Low band component type: u8 "
"(default is 0x05 or 0x06 depending on the board_type)");
-module_param_named(high_band_component, high_band_component_param, int,
- S_IRUSR);
+module_param_named(high_band_component, high_band_component_param, int, 0400);
MODULE_PARM_DESC(high_band_component, "High band component: u8, "
"(default is 0x01)");
module_param_named(high_band_component_type, high_band_component_type_param,
- int, S_IRUSR);
+ int, 0400);
MODULE_PARM_DESC(high_band_component_type, "High band component type: u8 "
"(default is 0x09)");
module_param_named(pwr_limit_reference_11_abg,
- pwr_limit_reference_11_abg_param, int, S_IRUSR);
+ pwr_limit_reference_11_abg_param, int, 0400);
MODULE_PARM_DESC(pwr_limit_reference_11_abg, "Power limit reference: u8 "
"(default is 0xc8)");
-module_param_named(num_rx_desc,
- num_rx_desc_param, int, S_IRUSR);
+module_param_named(num_rx_desc, num_rx_desc_param, int, 0400);
MODULE_PARM_DESC(num_rx_desc_param,
"Number of Rx descriptors: u8 (default is 32)");
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 09714034dbf1..3a51ab116e79 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -6630,20 +6630,20 @@ EXPORT_SYMBOL_GPL(wlcore_remove);
u32 wl12xx_debug_level = DEBUG_NONE;
EXPORT_SYMBOL_GPL(wl12xx_debug_level);
-module_param_named(debug_level, wl12xx_debug_level, uint, S_IRUSR | S_IWUSR);
+module_param_named(debug_level, wl12xx_debug_level, uint, 0600);
MODULE_PARM_DESC(debug_level, "wl12xx debugging level");
module_param_named(fwlog, fwlog_param, charp, 0);
MODULE_PARM_DESC(fwlog,
"FW logger options: continuous, dbgpins or disable");
-module_param(fwlog_mem_blocks, int, S_IRUSR | S_IWUSR);
+module_param(fwlog_mem_blocks, int, 0600);
MODULE_PARM_DESC(fwlog_mem_blocks, "fwlog mem_blocks");
-module_param(bug_on_recovery, int, S_IRUSR | S_IWUSR);
+module_param(bug_on_recovery, int, 0600);
MODULE_PARM_DESC(bug_on_recovery, "BUG() on fw recovery");
-module_param(no_recovery, int, S_IRUSR | S_IWUSR);
+module_param(no_recovery, int, 0600);
MODULE_PARM_DESC(no_recovery, "Prevent HW recovery. FW will remain stuck.");
MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/ti/wlcore/sdio.c b/drivers/net/wireless/ti/wlcore/sdio.c
index f8a1fea64e25..1f727babbea0 100644
--- a/drivers/net/wireless/ti/wlcore/sdio.c
+++ b/drivers/net/wireless/ti/wlcore/sdio.c
@@ -469,7 +469,7 @@ static void __exit wl1271_exit(void)
module_init(wl1271_init);
module_exit(wl1271_exit);
-module_param(dump, bool, S_IRUSR | S_IWUSR);
+module_param(dump, bool, 0600);
MODULE_PARM_DESC(dump, "Enable sdio read/write dumps.");
MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/ti/wlcore/sysfs.c b/drivers/net/wireless/ti/wlcore/sysfs.c
index b72e2101488b..d31eb775e023 100644
--- a/drivers/net/wireless/ti/wlcore/sysfs.c
+++ b/drivers/net/wireless/ti/wlcore/sysfs.c
@@ -80,7 +80,7 @@ static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev,
return count;
}
-static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR,
+static DEVICE_ATTR(bt_coex_state, 0644,
wl1271_sysfs_show_bt_coex_state,
wl1271_sysfs_store_bt_coex_state);
@@ -103,8 +103,7 @@ static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev,
return len;
}
-static DEVICE_ATTR(hw_pg_ver, S_IRUGO,
- wl1271_sysfs_show_hw_pg_ver, NULL);
+static DEVICE_ATTR(hw_pg_ver, 0444, wl1271_sysfs_show_hw_pg_ver, NULL);
static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
@@ -139,7 +138,7 @@ static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj,
}
static const struct bin_attribute fwlog_attr = {
- .attr = {.name = "fwlog", .mode = S_IRUSR},
+ .attr = { .name = "fwlog", .mode = 0400 },
.read = wl1271_sysfs_read_fwlog,
};
diff --git a/drivers/net/wireless/zydas/Kconfig b/drivers/net/wireless/zydas/Kconfig
index a58c0f65e376..b327f86f05be 100644
--- a/drivers/net/wireless/zydas/Kconfig
+++ b/drivers/net/wireless/zydas/Kconfig
@@ -5,8 +5,8 @@ config WLAN_VENDOR_ZYDAS
If you have a wireless card belonging to this class, say Y.
Note that the answer to this question doesn't directly affect the
- kernel: saying N will just cause the configurator to skip all
- the questions about cards. If you say Y, you will be asked for
+ kernel: saying N will just cause the configurator to skip all the
+ questions about these cards. If you say Y, you will be asked for
your specific card in the following questions.
if WLAN_VENDOR_ZYDAS
diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_mac.c b/drivers/net/wireless/zydas/zd1211rw/zd_mac.c
index b785742bfd9e..b01b44a5d16e 100644
--- a/drivers/net/wireless/zydas/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_mac.c
@@ -509,7 +509,6 @@ void zd_mac_tx_failed(struct urb *urb)
int found = 0;
int i, position = 0;
- q = &mac->ack_wait_queue;
spin_lock_irqsave(&q->lock, flags);
skb_queue_walk(q, skb) {
diff --git a/drivers/net/xen-netback/rx.c b/drivers/net/xen-netback/rx.c
index b1cf7c6f407a..ef5887037b22 100644
--- a/drivers/net/xen-netback/rx.c
+++ b/drivers/net/xen-netback/rx.c
@@ -419,7 +419,7 @@ static void xenvif_rx_extra_slot(struct xenvif_queue *queue,
BUG();
}
-void xenvif_rx_skb(struct xenvif_queue *queue)
+static void xenvif_rx_skb(struct xenvif_queue *queue)
{
struct xenvif_pkt_state pkt;
diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c
index a56d3eab35dd..e1aef253601e 100644
--- a/drivers/net/xen-netback/xenbus.c
+++ b/drivers/net/xen-netback/xenbus.c
@@ -224,7 +224,7 @@ static void xenvif_debugfs_addif(struct xenvif *vif)
snprintf(filename, sizeof(filename), "io_ring_q%d", i);
pfile = debugfs_create_file(filename,
- S_IRUSR | S_IWUSR,
+ 0600,
vif->xenvif_dbg_root,
&vif->queues[i],
&xenvif_dbg_io_ring_ops_fops);
@@ -235,7 +235,7 @@ static void xenvif_debugfs_addif(struct xenvif *vif)
if (vif->ctrl_irq) {
pfile = debugfs_create_file("ctrl",
- S_IRUSR,
+ 0400,
vif->xenvif_dbg_root,
vif,
&xenvif_dbg_ctrl_ops_fops);
diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c
index 3127bc8633ca..4dd0668003e7 100644
--- a/drivers/net/xen-netfront.c
+++ b/drivers/net/xen-netfront.c
@@ -2113,9 +2113,9 @@ static ssize_t store_rxbuf(struct device *dev,
return len;
}
-static DEVICE_ATTR(rxbuf_min, S_IRUGO|S_IWUSR, show_rxbuf, store_rxbuf);
-static DEVICE_ATTR(rxbuf_max, S_IRUGO|S_IWUSR, show_rxbuf, store_rxbuf);
-static DEVICE_ATTR(rxbuf_cur, S_IRUGO, show_rxbuf, NULL);
+static DEVICE_ATTR(rxbuf_min, 0644, show_rxbuf, store_rxbuf);
+static DEVICE_ATTR(rxbuf_max, 0644, show_rxbuf, store_rxbuf);
+static DEVICE_ATTR(rxbuf_cur, 0444, show_rxbuf, NULL);
static struct attribute *xennet_dev_attrs[] = {
&dev_attr_rxbuf_min.attr,
diff --git a/drivers/nvdimm/Kconfig b/drivers/nvdimm/Kconfig
index a65f2e1d9f53..9d36473dc2a2 100644
--- a/drivers/nvdimm/Kconfig
+++ b/drivers/nvdimm/Kconfig
@@ -20,7 +20,7 @@ if LIBNVDIMM
config BLK_DEV_PMEM
tristate "PMEM: Persistent memory block device support"
default LIBNVDIMM
- select DAX
+ select DAX_DRIVER
select ND_BTT if BTT
select ND_PFN if NVDIMM_PFN
help
@@ -102,4 +102,14 @@ config NVDIMM_DAX
Select Y if unsure
+config OF_PMEM
+ tristate "Device-tree support for persistent memory regions"
+ depends on OF
+ default LIBNVDIMM
+ help
+ Allows regions of persistent memory to be described in the
+ device-tree.
+
+ Select Y if unsure.
+
endif
diff --git a/drivers/nvdimm/Makefile b/drivers/nvdimm/Makefile
index 70d5f3ad9909..e8847045dac0 100644
--- a/drivers/nvdimm/Makefile
+++ b/drivers/nvdimm/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_BLK_DEV_PMEM) += nd_pmem.o
obj-$(CONFIG_ND_BTT) += nd_btt.o
obj-$(CONFIG_ND_BLK) += nd_blk.o
obj-$(CONFIG_X86_PMEM_LEGACY) += nd_e820.o
+obj-$(CONFIG_OF_PMEM) += of_pmem.o
nd_pmem-y := pmem.o
diff --git a/drivers/nvdimm/blk.c b/drivers/nvdimm/blk.c
index 1bd7b3734751..62e9cb167aad 100644
--- a/drivers/nvdimm/blk.c
+++ b/drivers/nvdimm/blk.c
@@ -266,7 +266,7 @@ static int nsblk_attach_disk(struct nd_namespace_blk *nsblk)
blk_queue_make_request(q, nd_blk_make_request);
blk_queue_max_hw_sectors(q, UINT_MAX);
blk_queue_logical_block_size(q, nsblk_sector_size(nsblk));
- queue_flag_set_unlocked(QUEUE_FLAG_NONROT, q);
+ blk_queue_flag_set(QUEUE_FLAG_NONROT, q);
q->queuedata = nsblk;
disk = alloc_disk(0);
diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c
index 4b95ac513de2..85de8053aa34 100644
--- a/drivers/nvdimm/btt.c
+++ b/drivers/nvdimm/btt.c
@@ -1542,7 +1542,7 @@ static int btt_blk_init(struct btt *btt)
blk_queue_make_request(btt->btt_queue, btt_make_request);
blk_queue_logical_block_size(btt->btt_queue, btt->sector_size);
blk_queue_max_hw_sectors(btt->btt_queue, UINT_MAX);
- queue_flag_set_unlocked(QUEUE_FLAG_NONROT, btt->btt_queue);
+ blk_queue_flag_set(QUEUE_FLAG_NONROT, btt->btt_queue);
btt->btt_queue->queuedata = btt;
if (btt_meta_size(btt)) {
diff --git a/drivers/nvdimm/btt_devs.c b/drivers/nvdimm/btt_devs.c
index d58925295aa7..795ad4ff35ca 100644
--- a/drivers/nvdimm/btt_devs.c
+++ b/drivers/nvdimm/btt_devs.c
@@ -26,7 +26,7 @@ static void nd_btt_release(struct device *dev)
struct nd_region *nd_region = to_nd_region(dev->parent);
struct nd_btt *nd_btt = to_nd_btt(dev);
- dev_dbg(dev, "%s\n", __func__);
+ dev_dbg(dev, "trace\n");
nd_detach_ndns(&nd_btt->dev, &nd_btt->ndns);
ida_simple_remove(&nd_region->btt_ida, nd_btt->id);
kfree(nd_btt->uuid);
@@ -74,8 +74,8 @@ static ssize_t sector_size_store(struct device *dev,
nvdimm_bus_lock(dev);
rc = nd_size_select_store(dev, buf, &nd_btt->lbasize,
btt_lbasize_supported);
- dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__,
- rc, buf, buf[len - 1] == '\n' ? "" : "\n");
+ dev_dbg(dev, "result: %zd wrote: %s%s", rc, buf,
+ buf[len - 1] == '\n' ? "" : "\n");
nvdimm_bus_unlock(dev);
device_unlock(dev);
@@ -101,8 +101,8 @@ static ssize_t uuid_store(struct device *dev,
device_lock(dev);
rc = nd_uuid_store(dev, &nd_btt->uuid, buf, len);
- dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__,
- rc, buf, buf[len - 1] == '\n' ? "" : "\n");
+ dev_dbg(dev, "result: %zd wrote: %s%s", rc, buf,
+ buf[len - 1] == '\n' ? "" : "\n");
device_unlock(dev);
return rc ? rc : len;
@@ -131,8 +131,8 @@ static ssize_t namespace_store(struct device *dev,
device_lock(dev);
nvdimm_bus_lock(dev);
rc = nd_namespace_store(dev, &nd_btt->ndns, buf, len);
- dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__,
- rc, buf, buf[len - 1] == '\n' ? "" : "\n");
+ dev_dbg(dev, "result: %zd wrote: %s%s", rc, buf,
+ buf[len - 1] == '\n' ? "" : "\n");
nvdimm_bus_unlock(dev);
device_unlock(dev);
@@ -206,8 +206,8 @@ static struct device *__nd_btt_create(struct nd_region *nd_region,
dev->groups = nd_btt_attribute_groups;
device_initialize(&nd_btt->dev);
if (ndns && !__nd_attach_ndns(&nd_btt->dev, ndns, &nd_btt->ndns)) {
- dev_dbg(&ndns->dev, "%s failed, already claimed by %s\n",
- __func__, dev_name(ndns->claim));
+ dev_dbg(&ndns->dev, "failed, already claimed by %s\n",
+ dev_name(ndns->claim));
put_device(dev);
return NULL;
}
@@ -346,8 +346,7 @@ int nd_btt_probe(struct device *dev, struct nd_namespace_common *ndns)
return -ENOMEM;
btt_sb = devm_kzalloc(dev, sizeof(*btt_sb), GFP_KERNEL);
rc = __nd_btt_probe(to_nd_btt(btt_dev), ndns, btt_sb);
- dev_dbg(dev, "%s: btt: %s\n", __func__,
- rc == 0 ? dev_name(btt_dev) : "<none>");
+ dev_dbg(dev, "btt: %s\n", rc == 0 ? dev_name(btt_dev) : "<none>");
if (rc < 0) {
struct nd_btt *nd_btt = to_nd_btt(btt_dev);
diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c
index 78eabc3a1ab1..a64023690cad 100644
--- a/drivers/nvdimm/bus.c
+++ b/drivers/nvdimm/bus.c
@@ -358,6 +358,7 @@ struct nvdimm_bus *nvdimm_bus_register(struct device *parent,
nvdimm_bus->dev.release = nvdimm_bus_release;
nvdimm_bus->dev.groups = nd_desc->attr_groups;
nvdimm_bus->dev.bus = &nvdimm_bus_type;
+ nvdimm_bus->dev.of_node = nd_desc->of_node;
dev_set_name(&nvdimm_bus->dev, "ndbus%d", nvdimm_bus->id);
rc = device_register(&nvdimm_bus->dev);
if (rc) {
@@ -984,8 +985,8 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,
if (cmd == ND_CMD_CALL) {
func = pkg.nd_command;
- dev_dbg(dev, "%s:%s, idx: %llu, in: %u, out: %u, len %llu\n",
- __func__, dimm_name, pkg.nd_command,
+ dev_dbg(dev, "%s, idx: %llu, in: %u, out: %u, len %llu\n",
+ dimm_name, pkg.nd_command,
in_len, out_len, buf_len);
}
@@ -996,8 +997,8 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,
u32 copy;
if (out_size == UINT_MAX) {
- dev_dbg(dev, "%s:%s unknown output size cmd: %s field: %d\n",
- __func__, dimm_name, cmd_name, i);
+ dev_dbg(dev, "%s unknown output size cmd: %s field: %d\n",
+ dimm_name, cmd_name, i);
return -EFAULT;
}
if (out_len < sizeof(out_env))
@@ -1012,9 +1013,8 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,
buf_len = (u64) out_len + (u64) in_len;
if (buf_len > ND_IOCTL_MAX_BUFLEN) {
- dev_dbg(dev, "%s:%s cmd: %s buf_len: %llu > %d\n", __func__,
- dimm_name, cmd_name, buf_len,
- ND_IOCTL_MAX_BUFLEN);
+ dev_dbg(dev, "%s cmd: %s buf_len: %llu > %d\n", dimm_name,
+ cmd_name, buf_len, ND_IOCTL_MAX_BUFLEN);
return -EINVAL;
}
diff --git a/drivers/nvdimm/claim.c b/drivers/nvdimm/claim.c
index b2fc29b8279b..30852270484f 100644
--- a/drivers/nvdimm/claim.c
+++ b/drivers/nvdimm/claim.c
@@ -148,7 +148,7 @@ ssize_t nd_namespace_store(struct device *dev,
char *name;
if (dev->driver) {
- dev_dbg(dev, "%s: -EBUSY\n", __func__);
+ dev_dbg(dev, "namespace already active\n");
return -EBUSY;
}
diff --git a/drivers/nvdimm/core.c b/drivers/nvdimm/core.c
index 1dc527660637..acce050856a8 100644
--- a/drivers/nvdimm/core.c
+++ b/drivers/nvdimm/core.c
@@ -134,7 +134,7 @@ static void nvdimm_map_release(struct kref *kref)
nvdimm_map = container_of(kref, struct nvdimm_map, kref);
nvdimm_bus = nvdimm_map->nvdimm_bus;
- dev_dbg(&nvdimm_bus->dev, "%s: %pa\n", __func__, &nvdimm_map->offset);
+ dev_dbg(&nvdimm_bus->dev, "%pa\n", &nvdimm_map->offset);
list_del(&nvdimm_map->list);
if (nvdimm_map->flags)
memunmap(nvdimm_map->mem);
@@ -230,8 +230,8 @@ static int nd_uuid_parse(struct device *dev, u8 *uuid_out, const char *buf,
for (i = 0; i < 16; i++) {
if (!isxdigit(str[0]) || !isxdigit(str[1])) {
- dev_dbg(dev, "%s: pos: %d buf[%zd]: %c buf[%zd]: %c\n",
- __func__, i, str - buf, str[0],
+ dev_dbg(dev, "pos: %d buf[%zd]: %c buf[%zd]: %c\n",
+ i, str - buf, str[0],
str + 1 - buf, str[1]);
return -EINVAL;
}
diff --git a/drivers/nvdimm/dax_devs.c b/drivers/nvdimm/dax_devs.c
index 1bf2bd318371..0453f49dc708 100644
--- a/drivers/nvdimm/dax_devs.c
+++ b/drivers/nvdimm/dax_devs.c
@@ -24,7 +24,7 @@ static void nd_dax_release(struct device *dev)
struct nd_dax *nd_dax = to_nd_dax(dev);
struct nd_pfn *nd_pfn = &nd_dax->nd_pfn;
- dev_dbg(dev, "%s\n", __func__);
+ dev_dbg(dev, "trace\n");
nd_detach_ndns(dev, &nd_pfn->ndns);
ida_simple_remove(&nd_region->dax_ida, nd_pfn->id);
kfree(nd_pfn->uuid);
@@ -129,8 +129,7 @@ int nd_dax_probe(struct device *dev, struct nd_namespace_common *ndns)
pfn_sb = devm_kzalloc(dev, sizeof(*pfn_sb), GFP_KERNEL);
nd_pfn->pfn_sb = pfn_sb;
rc = nd_pfn_validate(nd_pfn, DAX_SIG);
- dev_dbg(dev, "%s: dax: %s\n", __func__,
- rc == 0 ? dev_name(dax_dev) : "<none>");
+ dev_dbg(dev, "dax: %s\n", rc == 0 ? dev_name(dax_dev) : "<none>");
if (rc < 0) {
nd_detach_ndns(dax_dev, &nd_pfn->ndns);
put_device(dax_dev);
diff --git a/drivers/nvdimm/dimm.c b/drivers/nvdimm/dimm.c
index f8913b8124b6..233907889f96 100644
--- a/drivers/nvdimm/dimm.c
+++ b/drivers/nvdimm/dimm.c
@@ -67,9 +67,11 @@ static int nvdimm_probe(struct device *dev)
ndd->ns_next = nd_label_next_nsindex(ndd->ns_current);
nd_label_copy(ndd, to_next_namespace_index(ndd),
to_current_namespace_index(ndd));
- rc = nd_label_reserve_dpa(ndd);
- if (ndd->ns_current >= 0)
- nvdimm_set_aliasing(dev);
+ if (ndd->ns_current >= 0) {
+ rc = nd_label_reserve_dpa(ndd);
+ if (rc == 0)
+ nvdimm_set_aliasing(dev);
+ }
nvdimm_clear_locked(dev);
nvdimm_bus_unlock(dev);
diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c
index 097794d9f786..8d348b22ba45 100644
--- a/drivers/nvdimm/dimm_devs.c
+++ b/drivers/nvdimm/dimm_devs.c
@@ -88,9 +88,9 @@ int nvdimm_init_nsarea(struct nvdimm_drvdata *ndd)
int nvdimm_init_config_data(struct nvdimm_drvdata *ndd)
{
struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev);
+ int rc = validate_dimm(ndd), cmd_rc = 0;
struct nd_cmd_get_config_data_hdr *cmd;
struct nvdimm_bus_descriptor *nd_desc;
- int rc = validate_dimm(ndd);
u32 max_cmd_size, config_size;
size_t offset;
@@ -124,14 +124,16 @@ int nvdimm_init_config_data(struct nvdimm_drvdata *ndd)
cmd->in_offset = offset;
rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev),
ND_CMD_GET_CONFIG_DATA, cmd,
- cmd->in_length + sizeof(*cmd), NULL);
- if (rc || cmd->status) {
- rc = -ENXIO;
+ cmd->in_length + sizeof(*cmd), &cmd_rc);
+ if (rc < 0)
+ break;
+ if (cmd_rc < 0) {
+ rc = cmd_rc;
break;
}
memcpy(ndd->data + offset, cmd->out_buf, cmd->in_length);
}
- dev_dbg(ndd->dev, "%s: len: %zu rc: %d\n", __func__, offset, rc);
+ dev_dbg(ndd->dev, "len: %zu rc: %d\n", offset, rc);
kfree(cmd);
return rc;
@@ -140,9 +142,9 @@ int nvdimm_init_config_data(struct nvdimm_drvdata *ndd)
int nvdimm_set_config_data(struct nvdimm_drvdata *ndd, size_t offset,
void *buf, size_t len)
{
- int rc = validate_dimm(ndd);
size_t max_cmd_size, buf_offset;
struct nd_cmd_set_config_hdr *cmd;
+ int rc = validate_dimm(ndd), cmd_rc = 0;
struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev);
struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
@@ -164,7 +166,6 @@ int nvdimm_set_config_data(struct nvdimm_drvdata *ndd, size_t offset,
for (buf_offset = 0; len; len -= cmd->in_length,
buf_offset += cmd->in_length) {
size_t cmd_size;
- u32 *status;
cmd->in_offset = offset + buf_offset;
cmd->in_length = min(max_cmd_size, len);
@@ -172,12 +173,13 @@ int nvdimm_set_config_data(struct nvdimm_drvdata *ndd, size_t offset,
/* status is output in the last 4-bytes of the command buffer */
cmd_size = sizeof(*cmd) + cmd->in_length + sizeof(u32);
- status = ((void *) cmd) + cmd_size - sizeof(u32);
rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev),
- ND_CMD_SET_CONFIG_DATA, cmd, cmd_size, NULL);
- if (rc || *status) {
- rc = rc ? rc : -ENXIO;
+ ND_CMD_SET_CONFIG_DATA, cmd, cmd_size, &cmd_rc);
+ if (rc < 0)
+ break;
+ if (cmd_rc < 0) {
+ rc = cmd_rc;
break;
}
}
@@ -266,8 +268,7 @@ void nvdimm_drvdata_release(struct kref *kref)
struct device *dev = ndd->dev;
struct resource *res, *_r;
- dev_dbg(dev, "%s\n", __func__);
-
+ dev_dbg(dev, "trace\n");
nvdimm_bus_lock(dev);
for_each_dpa_resource_safe(ndd, res, _r)
nvdimm_free_dpa(ndd, res);
@@ -660,7 +661,7 @@ int nvdimm_bus_check_dimm_count(struct nvdimm_bus *nvdimm_bus, int dimm_count)
nd_synchronize();
device_for_each_child(&nvdimm_bus->dev, &count, count_dimms);
- dev_dbg(&nvdimm_bus->dev, "%s: count: %d\n", __func__, count);
+ dev_dbg(&nvdimm_bus->dev, "count: %d\n", count);
if (count != dimm_count)
return -ENXIO;
return 0;
diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c
index de66c02f6140..1d28cd656536 100644
--- a/drivers/nvdimm/label.c
+++ b/drivers/nvdimm/label.c
@@ -45,9 +45,27 @@ unsigned sizeof_namespace_label(struct nvdimm_drvdata *ndd)
return ndd->nslabel_size;
}
+static size_t __sizeof_namespace_index(u32 nslot)
+{
+ return ALIGN(sizeof(struct nd_namespace_index) + DIV_ROUND_UP(nslot, 8),
+ NSINDEX_ALIGN);
+}
+
+static int __nvdimm_num_label_slots(struct nvdimm_drvdata *ndd,
+ size_t index_size)
+{
+ return (ndd->nsarea.config_size - index_size * 2) /
+ sizeof_namespace_label(ndd);
+}
+
int nvdimm_num_label_slots(struct nvdimm_drvdata *ndd)
{
- return ndd->nsarea.config_size / (sizeof_namespace_label(ndd) + 1);
+ u32 tmp_nslot, n;
+
+ tmp_nslot = ndd->nsarea.config_size / sizeof_namespace_label(ndd);
+ n = __sizeof_namespace_index(tmp_nslot) / NSINDEX_ALIGN;
+
+ return __nvdimm_num_label_slots(ndd, NSINDEX_ALIGN * n);
}
size_t sizeof_namespace_index(struct nvdimm_drvdata *ndd)
@@ -55,18 +73,14 @@ size_t sizeof_namespace_index(struct nvdimm_drvdata *ndd)
u32 nslot, space, size;
/*
- * The minimum index space is 512 bytes, with that amount of
- * index we can describe ~1400 labels which is less than a byte
- * of overhead per label. Round up to a byte of overhead per
- * label and determine the size of the index region. Yes, this
- * starts to waste space at larger config_sizes, but it's
- * unlikely we'll ever see anything but 128K.
+ * Per UEFI 2.7, the minimum size of the Label Storage Area is large
+ * enough to hold 2 index blocks and 2 labels. The minimum index
+ * block size is 256 bytes, and the minimum label size is 256 bytes.
*/
nslot = nvdimm_num_label_slots(ndd);
space = ndd->nsarea.config_size - nslot * sizeof_namespace_label(ndd);
- size = ALIGN(sizeof(struct nd_namespace_index) + DIV_ROUND_UP(nslot, 8),
- NSINDEX_ALIGN) * 2;
- if (size <= space)
+ size = __sizeof_namespace_index(nslot) * 2;
+ if (size <= space && nslot >= 2)
return size / 2;
dev_err(ndd->dev, "label area (%d) too small to host (%d byte) labels\n",
@@ -121,8 +135,7 @@ static int __nd_label_validate(struct nvdimm_drvdata *ndd)
memcpy(sig, nsindex[i]->sig, NSINDEX_SIG_LEN);
if (memcmp(sig, NSINDEX_SIGNATURE, NSINDEX_SIG_LEN) != 0) {
- dev_dbg(dev, "%s: nsindex%d signature invalid\n",
- __func__, i);
+ dev_dbg(dev, "nsindex%d signature invalid\n", i);
continue;
}
@@ -135,8 +148,8 @@ static int __nd_label_validate(struct nvdimm_drvdata *ndd)
labelsize = 128;
if (labelsize != sizeof_namespace_label(ndd)) {
- dev_dbg(dev, "%s: nsindex%d labelsize %d invalid\n",
- __func__, i, nsindex[i]->labelsize);
+ dev_dbg(dev, "nsindex%d labelsize %d invalid\n",
+ i, nsindex[i]->labelsize);
continue;
}
@@ -145,30 +158,28 @@ static int __nd_label_validate(struct nvdimm_drvdata *ndd)
sum = nd_fletcher64(nsindex[i], sizeof_namespace_index(ndd), 1);
nsindex[i]->checksum = __cpu_to_le64(sum_save);
if (sum != sum_save) {
- dev_dbg(dev, "%s: nsindex%d checksum invalid\n",
- __func__, i);
+ dev_dbg(dev, "nsindex%d checksum invalid\n", i);
continue;
}
seq = __le32_to_cpu(nsindex[i]->seq);
if ((seq & NSINDEX_SEQ_MASK) == 0) {
- dev_dbg(dev, "%s: nsindex%d sequence: %#x invalid\n",
- __func__, i, seq);
+ dev_dbg(dev, "nsindex%d sequence: %#x invalid\n", i, seq);
continue;
}
/* sanity check the index against expected values */
if (__le64_to_cpu(nsindex[i]->myoff)
!= i * sizeof_namespace_index(ndd)) {
- dev_dbg(dev, "%s: nsindex%d myoff: %#llx invalid\n",
- __func__, i, (unsigned long long)
+ dev_dbg(dev, "nsindex%d myoff: %#llx invalid\n",
+ i, (unsigned long long)
__le64_to_cpu(nsindex[i]->myoff));
continue;
}
if (__le64_to_cpu(nsindex[i]->otheroff)
!= (!i) * sizeof_namespace_index(ndd)) {
- dev_dbg(dev, "%s: nsindex%d otheroff: %#llx invalid\n",
- __func__, i, (unsigned long long)
+ dev_dbg(dev, "nsindex%d otheroff: %#llx invalid\n",
+ i, (unsigned long long)
__le64_to_cpu(nsindex[i]->otheroff));
continue;
}
@@ -176,8 +187,7 @@ static int __nd_label_validate(struct nvdimm_drvdata *ndd)
size = __le64_to_cpu(nsindex[i]->mysize);
if (size > sizeof_namespace_index(ndd)
|| size < sizeof(struct nd_namespace_index)) {
- dev_dbg(dev, "%s: nsindex%d mysize: %#llx invalid\n",
- __func__, i, size);
+ dev_dbg(dev, "nsindex%d mysize: %#llx invalid\n", i, size);
continue;
}
@@ -185,9 +195,8 @@ static int __nd_label_validate(struct nvdimm_drvdata *ndd)
if (nslot * sizeof_namespace_label(ndd)
+ 2 * sizeof_namespace_index(ndd)
> ndd->nsarea.config_size) {
- dev_dbg(dev, "%s: nsindex%d nslot: %u invalid, config_size: %#x\n",
- __func__, i, nslot,
- ndd->nsarea.config_size);
+ dev_dbg(dev, "nsindex%d nslot: %u invalid, config_size: %#x\n",
+ i, nslot, ndd->nsarea.config_size);
continue;
}
valid[i] = true;
@@ -356,8 +365,8 @@ static bool slot_valid(struct nvdimm_drvdata *ndd,
sum = nd_fletcher64(nd_label, sizeof_namespace_label(ndd), 1);
nd_label->checksum = __cpu_to_le64(sum_save);
if (sum != sum_save) {
- dev_dbg(ndd->dev, "%s fail checksum. slot: %d expect: %#llx\n",
- __func__, slot, sum);
+ dev_dbg(ndd->dev, "fail checksum. slot: %d expect: %#llx\n",
+ slot, sum);
return false;
}
}
@@ -422,8 +431,8 @@ int nd_label_active_count(struct nvdimm_drvdata *ndd)
u64 dpa = __le64_to_cpu(nd_label->dpa);
dev_dbg(ndd->dev,
- "%s: slot%d invalid slot: %d dpa: %llx size: %llx\n",
- __func__, slot, label_slot, dpa, size);
+ "slot%d invalid slot: %d dpa: %llx size: %llx\n",
+ slot, label_slot, dpa, size);
continue;
}
count++;
@@ -650,7 +659,7 @@ static int __pmem_label_update(struct nd_region *nd_region,
slot = nd_label_alloc_slot(ndd);
if (slot == UINT_MAX)
return -ENXIO;
- dev_dbg(ndd->dev, "%s: allocated: %d\n", __func__, slot);
+ dev_dbg(ndd->dev, "allocated: %d\n", slot);
nd_label = to_label(ndd, slot);
memset(nd_label, 0, sizeof_namespace_label(ndd));
@@ -678,7 +687,7 @@ static int __pmem_label_update(struct nd_region *nd_region,
sum = nd_fletcher64(nd_label, sizeof_namespace_label(ndd), 1);
nd_label->checksum = __cpu_to_le64(sum);
}
- nd_dbg_dpa(nd_region, ndd, res, "%s\n", __func__);
+ nd_dbg_dpa(nd_region, ndd, res, "\n");
/* update label */
offset = nd_label_offset(ndd, nd_label);
@@ -700,7 +709,7 @@ static int __pmem_label_update(struct nd_region *nd_region,
break;
}
if (victim) {
- dev_dbg(ndd->dev, "%s: free: %d\n", __func__, slot);
+ dev_dbg(ndd->dev, "free: %d\n", slot);
slot = to_slot(ndd, victim->label);
nd_label_free_slot(ndd, slot);
victim->label = NULL;
@@ -868,7 +877,7 @@ static int __blk_label_update(struct nd_region *nd_region,
slot = nd_label_alloc_slot(ndd);
if (slot == UINT_MAX)
goto abort;
- dev_dbg(ndd->dev, "%s: allocated: %d\n", __func__, slot);
+ dev_dbg(ndd->dev, "allocated: %d\n", slot);
nd_label = to_label(ndd, slot);
memset(nd_label, 0, sizeof_namespace_label(ndd));
@@ -928,7 +937,7 @@ static int __blk_label_update(struct nd_region *nd_region,
/* free up now unused slots in the new index */
for_each_set_bit(slot, victim_map, victim_map ? nslot : 0) {
- dev_dbg(ndd->dev, "%s: free: %d\n", __func__, slot);
+ dev_dbg(ndd->dev, "free: %d\n", slot);
nd_label_free_slot(ndd, slot);
}
@@ -1092,7 +1101,7 @@ static int del_labels(struct nd_mapping *nd_mapping, u8 *uuid)
active--;
slot = to_slot(ndd, nd_label);
nd_label_free_slot(ndd, slot);
- dev_dbg(ndd->dev, "%s: free: %d\n", __func__, slot);
+ dev_dbg(ndd->dev, "free: %d\n", slot);
list_move_tail(&label_ent->list, &list);
label_ent->label = NULL;
}
@@ -1100,7 +1109,7 @@ static int del_labels(struct nd_mapping *nd_mapping, u8 *uuid)
if (active == 0) {
nd_mapping_free_labels(nd_mapping);
- dev_dbg(ndd->dev, "%s: no more active labels\n", __func__);
+ dev_dbg(ndd->dev, "no more active labels\n");
}
mutex_unlock(&nd_mapping->lock);
diff --git a/drivers/nvdimm/label.h b/drivers/nvdimm/label.h
index 1ebf4d3d01ba..18bbe183b3a9 100644
--- a/drivers/nvdimm/label.h
+++ b/drivers/nvdimm/label.h
@@ -33,7 +33,7 @@ enum {
BTTINFO_UUID_LEN = 16,
BTTINFO_FLAG_ERROR = 0x1, /* error state (read-only) */
BTTINFO_MAJOR_VERSION = 1,
- ND_LABEL_MIN_SIZE = 512 * 129, /* see sizeof_namespace_index() */
+ ND_LABEL_MIN_SIZE = 256 * 4, /* see sizeof_namespace_index() */
ND_LABEL_ID_SIZE = 50,
ND_NSINDEX_INIT = 0x1,
};
diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c
index 658ada497be0..28afdd668905 100644
--- a/drivers/nvdimm/namespace_devs.c
+++ b/drivers/nvdimm/namespace_devs.c
@@ -421,7 +421,7 @@ static ssize_t alt_name_store(struct device *dev,
rc = __alt_name_store(dev, buf, len);
if (rc >= 0)
rc = nd_namespace_label_update(nd_region, dev);
- dev_dbg(dev, "%s: %s(%zd)\n", __func__, rc < 0 ? "fail " : "", rc);
+ dev_dbg(dev, "%s(%zd)\n", rc < 0 ? "fail " : "", rc);
nvdimm_bus_unlock(dev);
device_unlock(dev);
@@ -1007,7 +1007,7 @@ static ssize_t __size_store(struct device *dev, unsigned long long val)
if (uuid_not_set(uuid, dev, __func__))
return -ENXIO;
if (nd_region->ndr_mappings == 0) {
- dev_dbg(dev, "%s: not associated with dimm(s)\n", __func__);
+ dev_dbg(dev, "not associated with dimm(s)\n");
return -ENXIO;
}
@@ -1105,8 +1105,7 @@ static ssize_t size_store(struct device *dev,
*uuid = NULL;
}
- dev_dbg(dev, "%s: %llx %s (%d)\n", __func__, val, rc < 0
- ? "fail" : "success", rc);
+ dev_dbg(dev, "%llx %s (%d)\n", val, rc < 0 ? "fail" : "success", rc);
nvdimm_bus_unlock(dev);
device_unlock(dev);
@@ -1270,8 +1269,8 @@ static ssize_t uuid_store(struct device *dev,
rc = nd_namespace_label_update(nd_region, dev);
else
kfree(uuid);
- dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__,
- rc, buf, buf[len - 1] == '\n' ? "" : "\n");
+ dev_dbg(dev, "result: %zd wrote: %s%s", rc, buf,
+ buf[len - 1] == '\n' ? "" : "\n");
nvdimm_bus_unlock(dev);
device_unlock(dev);
@@ -1355,9 +1354,8 @@ static ssize_t sector_size_store(struct device *dev,
rc = nd_size_select_store(dev, buf, lbasize, supported);
if (rc >= 0)
rc = nd_namespace_label_update(nd_region, dev);
- dev_dbg(dev, "%s: result: %zd %s: %s%s", __func__,
- rc, rc < 0 ? "tried" : "wrote", buf,
- buf[len - 1] == '\n' ? "" : "\n");
+ dev_dbg(dev, "result: %zd %s: %s%s", rc, rc < 0 ? "tried" : "wrote",
+ buf, buf[len - 1] == '\n' ? "" : "\n");
nvdimm_bus_unlock(dev);
device_unlock(dev);
@@ -1519,7 +1517,7 @@ static ssize_t holder_class_store(struct device *dev,
rc = __holder_class_store(dev, buf);
if (rc >= 0)
rc = nd_namespace_label_update(nd_region, dev);
- dev_dbg(dev, "%s: %s(%zd)\n", __func__, rc < 0 ? "fail " : "", rc);
+ dev_dbg(dev, "%s(%zd)\n", rc < 0 ? "fail " : "", rc);
nvdimm_bus_unlock(dev);
device_unlock(dev);
@@ -1717,8 +1715,7 @@ struct nd_namespace_common *nvdimm_namespace_common_probe(struct device *dev)
if (uuid_not_set(nsblk->uuid, &ndns->dev, __func__))
return ERR_PTR(-ENODEV);
if (!nsblk->lbasize) {
- dev_dbg(&ndns->dev, "%s: sector size not set\n",
- __func__);
+ dev_dbg(&ndns->dev, "sector size not set\n");
return ERR_PTR(-ENODEV);
}
if (!nd_namespace_blk_validate(nsblk))
@@ -1798,9 +1795,7 @@ static bool has_uuid_at_pos(struct nd_region *nd_region, u8 *uuid,
}
if (found_uuid) {
- dev_dbg(ndd->dev,
- "%s duplicate entry for uuid\n",
- __func__);
+ dev_dbg(ndd->dev, "duplicate entry for uuid\n");
return false;
}
found_uuid = true;
@@ -1926,7 +1921,7 @@ static struct device *create_namespace_pmem(struct nd_region *nd_region,
}
if (i < nd_region->ndr_mappings) {
- struct nvdimm_drvdata *ndd = to_ndd(&nd_region->mapping[i]);
+ struct nvdimm *nvdimm = nd_region->mapping[i].nvdimm;
/*
* Give up if we don't find an instance of a uuid at each
@@ -1934,7 +1929,7 @@ static struct device *create_namespace_pmem(struct nd_region *nd_region,
* find a dimm with two instances of the same uuid.
*/
dev_err(&nd_region->dev, "%s missing label for %pUb\n",
- dev_name(ndd->dev), nd_label->uuid);
+ nvdimm_name(nvdimm), nd_label->uuid);
rc = -EINVAL;
goto err;
}
@@ -1994,14 +1989,13 @@ static struct device *create_namespace_pmem(struct nd_region *nd_region,
namespace_pmem_release(dev);
switch (rc) {
case -EINVAL:
- dev_dbg(&nd_region->dev, "%s: invalid label(s)\n", __func__);
+ dev_dbg(&nd_region->dev, "invalid label(s)\n");
break;
case -ENODEV:
- dev_dbg(&nd_region->dev, "%s: label not found\n", __func__);
+ dev_dbg(&nd_region->dev, "label not found\n");
break;
default:
- dev_dbg(&nd_region->dev, "%s: unexpected err: %d\n",
- __func__, rc);
+ dev_dbg(&nd_region->dev, "unexpected err: %d\n", rc);
break;
}
return ERR_PTR(rc);
@@ -2334,8 +2328,8 @@ static struct device **scan_labels(struct nd_region *nd_region)
}
- dev_dbg(&nd_region->dev, "%s: discovered %d %s namespace%s\n",
- __func__, count, is_nd_blk(&nd_region->dev)
+ dev_dbg(&nd_region->dev, "discovered %d %s namespace%s\n",
+ count, is_nd_blk(&nd_region->dev)
? "blk" : "pmem", count == 1 ? "" : "s");
if (count == 0) {
@@ -2467,7 +2461,7 @@ static int init_active_labels(struct nd_region *nd_region)
get_ndd(ndd);
count = nd_label_active_count(ndd);
- dev_dbg(ndd->dev, "%s: %d\n", __func__, count);
+ dev_dbg(ndd->dev, "count: %d\n", count);
if (!count)
continue;
for (j = 0; j < count; j++) {
diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h
index 8d6375ee0fda..32e0364b48b9 100644
--- a/drivers/nvdimm/nd.h
+++ b/drivers/nvdimm/nd.h
@@ -29,7 +29,6 @@ enum {
* BTT instance
*/
ND_MAX_LANES = 256,
- SECTOR_SHIFT = 9,
INT_LBASIZE_ALIGNMENT = 64,
NVDIMM_IO_ATOMIC = 1,
};
@@ -341,7 +340,6 @@ static inline struct device *nd_dax_create(struct nd_region *nd_region)
}
#endif
-struct nd_region *to_nd_region(struct device *dev);
int nd_region_to_nstype(struct nd_region *nd_region);
int nd_region_register_namespaces(struct nd_region *nd_region, int *err);
u64 nd_region_interleave_set_cookie(struct nd_region *nd_region,
diff --git a/drivers/nvdimm/of_pmem.c b/drivers/nvdimm/of_pmem.c
new file mode 100644
index 000000000000..0a701837dfc0
--- /dev/null
+++ b/drivers/nvdimm/of_pmem.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#define pr_fmt(fmt) "of_pmem: " fmt
+
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/libnvdimm.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+
+static const struct attribute_group *region_attr_groups[] = {
+ &nd_region_attribute_group,
+ &nd_device_attribute_group,
+ NULL,
+};
+
+static const struct attribute_group *bus_attr_groups[] = {
+ &nvdimm_bus_attribute_group,
+ NULL,
+};
+
+struct of_pmem_private {
+ struct nvdimm_bus_descriptor bus_desc;
+ struct nvdimm_bus *bus;
+};
+
+static int of_pmem_region_probe(struct platform_device *pdev)
+{
+ struct of_pmem_private *priv;
+ struct device_node *np;
+ struct nvdimm_bus *bus;
+ bool is_volatile;
+ int i;
+
+ np = dev_of_node(&pdev->dev);
+ if (!np)
+ return -ENXIO;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->bus_desc.attr_groups = bus_attr_groups;
+ priv->bus_desc.provider_name = "of_pmem";
+ priv->bus_desc.module = THIS_MODULE;
+ priv->bus_desc.of_node = np;
+
+ priv->bus = bus = nvdimm_bus_register(&pdev->dev, &priv->bus_desc);
+ if (!bus) {
+ kfree(priv);
+ return -ENODEV;
+ }
+ platform_set_drvdata(pdev, priv);
+
+ is_volatile = !!of_find_property(np, "volatile", NULL);
+ dev_dbg(&pdev->dev, "Registering %s regions from %pOF\n",
+ is_volatile ? "volatile" : "non-volatile", np);
+
+ for (i = 0; i < pdev->num_resources; i++) {
+ struct nd_region_desc ndr_desc;
+ struct nd_region *region;
+
+ /*
+ * NB: libnvdimm copies the data from ndr_desc into it's own
+ * structures so passing a stack pointer is fine.
+ */
+ memset(&ndr_desc, 0, sizeof(ndr_desc));
+ ndr_desc.attr_groups = region_attr_groups;
+ ndr_desc.numa_node = dev_to_node(&pdev->dev);
+ ndr_desc.res = &pdev->resource[i];
+ ndr_desc.of_node = np;
+ set_bit(ND_REGION_PAGEMAP, &ndr_desc.flags);
+
+ if (is_volatile)
+ region = nvdimm_volatile_region_create(bus, &ndr_desc);
+ else
+ region = nvdimm_pmem_region_create(bus, &ndr_desc);
+
+ if (!region)
+ dev_warn(&pdev->dev, "Unable to register region %pR from %pOF\n",
+ ndr_desc.res, np);
+ else
+ dev_dbg(&pdev->dev, "Registered region %pR from %pOF\n",
+ ndr_desc.res, np);
+ }
+
+ return 0;
+}
+
+static int of_pmem_region_remove(struct platform_device *pdev)
+{
+ struct of_pmem_private *priv = platform_get_drvdata(pdev);
+
+ nvdimm_bus_unregister(priv->bus);
+ kfree(priv);
+
+ return 0;
+}
+
+static const struct of_device_id of_pmem_region_match[] = {
+ { .compatible = "pmem-region" },
+ { },
+};
+
+static struct platform_driver of_pmem_region_driver = {
+ .probe = of_pmem_region_probe,
+ .remove = of_pmem_region_remove,
+ .driver = {
+ .name = "of_pmem",
+ .owner = THIS_MODULE,
+ .of_match_table = of_pmem_region_match,
+ },
+};
+
+module_platform_driver(of_pmem_region_driver);
+MODULE_DEVICE_TABLE(of, of_pmem_region_match);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("IBM Corporation");
diff --git a/drivers/nvdimm/pfn_devs.c b/drivers/nvdimm/pfn_devs.c
index 2f4d18752c97..30b08791597d 100644
--- a/drivers/nvdimm/pfn_devs.c
+++ b/drivers/nvdimm/pfn_devs.c
@@ -27,7 +27,7 @@ static void nd_pfn_release(struct device *dev)
struct nd_region *nd_region = to_nd_region(dev->parent);
struct nd_pfn *nd_pfn = to_nd_pfn(dev);
- dev_dbg(dev, "%s\n", __func__);
+ dev_dbg(dev, "trace\n");
nd_detach_ndns(&nd_pfn->dev, &nd_pfn->ndns);
ida_simple_remove(&nd_region->pfn_ida, nd_pfn->id);
kfree(nd_pfn->uuid);
@@ -94,8 +94,8 @@ static ssize_t mode_store(struct device *dev,
else
rc = -EINVAL;
}
- dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__,
- rc, buf, buf[len - 1] == '\n' ? "" : "\n");
+ dev_dbg(dev, "result: %zd wrote: %s%s", rc, buf,
+ buf[len - 1] == '\n' ? "" : "\n");
nvdimm_bus_unlock(dev);
device_unlock(dev);
@@ -144,8 +144,8 @@ static ssize_t align_store(struct device *dev,
nvdimm_bus_lock(dev);
rc = nd_size_select_store(dev, buf, &nd_pfn->align,
nd_pfn_supported_alignments());
- dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__,
- rc, buf, buf[len - 1] == '\n' ? "" : "\n");
+ dev_dbg(dev, "result: %zd wrote: %s%s", rc, buf,
+ buf[len - 1] == '\n' ? "" : "\n");
nvdimm_bus_unlock(dev);
device_unlock(dev);
@@ -171,8 +171,8 @@ static ssize_t uuid_store(struct device *dev,
device_lock(dev);
rc = nd_uuid_store(dev, &nd_pfn->uuid, buf, len);
- dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__,
- rc, buf, buf[len - 1] == '\n' ? "" : "\n");
+ dev_dbg(dev, "result: %zd wrote: %s%s", rc, buf,
+ buf[len - 1] == '\n' ? "" : "\n");
device_unlock(dev);
return rc ? rc : len;
@@ -201,8 +201,8 @@ static ssize_t namespace_store(struct device *dev,
device_lock(dev);
nvdimm_bus_lock(dev);
rc = nd_namespace_store(dev, &nd_pfn->ndns, buf, len);
- dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__,
- rc, buf, buf[len - 1] == '\n' ? "" : "\n");
+ dev_dbg(dev, "result: %zd wrote: %s%s", rc, buf,
+ buf[len - 1] == '\n' ? "" : "\n");
nvdimm_bus_unlock(dev);
device_unlock(dev);
@@ -314,8 +314,8 @@ struct device *nd_pfn_devinit(struct nd_pfn *nd_pfn,
dev = &nd_pfn->dev;
device_initialize(&nd_pfn->dev);
if (ndns && !__nd_attach_ndns(&nd_pfn->dev, ndns, &nd_pfn->ndns)) {
- dev_dbg(&ndns->dev, "%s failed, already claimed by %s\n",
- __func__, dev_name(ndns->claim));
+ dev_dbg(&ndns->dev, "failed, already claimed by %s\n",
+ dev_name(ndns->claim));
put_device(dev);
return NULL;
}
@@ -510,8 +510,7 @@ int nd_pfn_probe(struct device *dev, struct nd_namespace_common *ndns)
nd_pfn = to_nd_pfn(pfn_dev);
nd_pfn->pfn_sb = pfn_sb;
rc = nd_pfn_validate(nd_pfn, PFN_SIG);
- dev_dbg(dev, "%s: pfn: %s\n", __func__,
- rc == 0 ? dev_name(pfn_dev) : "<none>");
+ dev_dbg(dev, "pfn: %s\n", rc == 0 ? dev_name(pfn_dev) : "<none>");
if (rc < 0) {
nd_detach_ndns(pfn_dev, &nd_pfn->ndns);
put_device(pfn_dev);
diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c
index 06f8dcc52ca6..9d714926ecf5 100644
--- a/drivers/nvdimm/pmem.c
+++ b/drivers/nvdimm/pmem.c
@@ -66,7 +66,7 @@ static blk_status_t pmem_clear_poison(struct pmem_device *pmem,
rc = BLK_STS_IOERR;
if (cleared > 0 && cleared / 512) {
cleared /= 512;
- dev_dbg(dev, "%s: %#llx clear %ld sector%s\n", __func__,
+ dev_dbg(dev, "%#llx clear %ld sector%s\n",
(unsigned long long) sector, cleared,
cleared > 1 ? "s" : "");
badblocks_clear(&pmem->bb, sector, cleared);
@@ -343,7 +343,7 @@ static int pmem_attach_disk(struct device *dev,
return -EBUSY;
}
- q = blk_alloc_queue_node(GFP_KERNEL, dev_to_node(dev));
+ q = blk_alloc_queue_node(GFP_KERNEL, dev_to_node(dev), NULL);
if (!q)
return -ENOMEM;
@@ -387,8 +387,8 @@ static int pmem_attach_disk(struct device *dev,
blk_queue_physical_block_size(q, PAGE_SIZE);
blk_queue_logical_block_size(q, pmem_sector_size(ndns));
blk_queue_max_hw_sectors(q, UINT_MAX);
- queue_flag_set_unlocked(QUEUE_FLAG_NONROT, q);
- queue_flag_set_unlocked(QUEUE_FLAG_DAX, q);
+ blk_queue_flag_set(QUEUE_FLAG_NONROT, q);
+ blk_queue_flag_set(QUEUE_FLAG_DAX, q);
q->queuedata = pmem;
disk = alloc_disk_node(0, nid);
@@ -547,17 +547,7 @@ static struct nd_device_driver nd_pmem_driver = {
.type = ND_DRIVER_NAMESPACE_IO | ND_DRIVER_NAMESPACE_PMEM,
};
-static int __init pmem_init(void)
-{
- return nd_driver_register(&nd_pmem_driver);
-}
-module_init(pmem_init);
-
-static void pmem_exit(void)
-{
- driver_unregister(&nd_pmem_driver.drv);
-}
-module_exit(pmem_exit);
+module_nd_driver(nd_pmem_driver);
MODULE_AUTHOR("Ross Zwisler <ross.zwisler@linux.intel.com>");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/nvdimm/region.c b/drivers/nvdimm/region.c
index 034f0a07d627..b9ca0033cc99 100644
--- a/drivers/nvdimm/region.c
+++ b/drivers/nvdimm/region.c
@@ -27,10 +27,10 @@ static int nd_region_probe(struct device *dev)
if (nd_region->num_lanes > num_online_cpus()
&& nd_region->num_lanes < num_possible_cpus()
&& !test_and_set_bit(0, &once)) {
- dev_info(dev, "online cpus (%d) < concurrent i/o lanes (%d) < possible cpus (%d)\n",
+ dev_dbg(dev, "online cpus (%d) < concurrent i/o lanes (%d) < possible cpus (%d)\n",
num_online_cpus(), nd_region->num_lanes,
num_possible_cpus());
- dev_info(dev, "setting nr_cpus=%d may yield better libnvdimm device performance\n",
+ dev_dbg(dev, "setting nr_cpus=%d may yield better libnvdimm device performance\n",
nd_region->num_lanes);
}
diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c
index 1593e1806b16..a612be6f019d 100644
--- a/drivers/nvdimm/region_devs.c
+++ b/drivers/nvdimm/region_devs.c
@@ -182,6 +182,14 @@ struct nd_region *to_nd_region(struct device *dev)
}
EXPORT_SYMBOL_GPL(to_nd_region);
+struct device *nd_region_dev(struct nd_region *nd_region)
+{
+ if (!nd_region)
+ return NULL;
+ return &nd_region->dev;
+}
+EXPORT_SYMBOL_GPL(nd_region_dev);
+
struct nd_blk_region *to_nd_blk_region(struct device *dev)
{
struct nd_region *nd_region = to_nd_region(dev);
@@ -1014,6 +1022,7 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus,
dev->parent = &nvdimm_bus->dev;
dev->type = dev_type;
dev->groups = ndr_desc->attr_groups;
+ dev->of_node = ndr_desc->of_node;
nd_region->ndr_size = resource_size(ndr_desc->res);
nd_region->ndr_start = ndr_desc->res->start;
nd_device_register(dev);
diff --git a/drivers/nvme/host/Makefile b/drivers/nvme/host/Makefile
index 441e67e3a9d7..aea459c65ae1 100644
--- a/drivers/nvme/host/Makefile
+++ b/drivers/nvme/host/Makefile
@@ -12,6 +12,7 @@ nvme-core-y := core.o
nvme-core-$(CONFIG_TRACING) += trace.o
nvme-core-$(CONFIG_NVME_MULTIPATH) += multipath.o
nvme-core-$(CONFIG_NVM) += lightnvm.o
+nvme-core-$(CONFIG_FAULT_INJECTION_DEBUG_FS) += fault_inject.o
nvme-y += pci.o
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 7aeca5db7916..9df4f71e58ca 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -100,11 +100,6 @@ static struct class *nvme_subsys_class;
static void nvme_ns_remove(struct nvme_ns *ns);
static int nvme_revalidate_disk(struct gendisk *disk);
-static __le32 nvme_get_log_dw10(u8 lid, size_t size)
-{
- return cpu_to_le32((((size / 4) - 1) << 16) | lid);
-}
-
int nvme_reset_ctrl(struct nvme_ctrl *ctrl)
{
if (!nvme_change_ctrl_state(ctrl, NVME_CTRL_RESETTING))
@@ -135,6 +130,9 @@ static void nvme_delete_ctrl_work(struct work_struct *work)
struct nvme_ctrl *ctrl =
container_of(work, struct nvme_ctrl, delete_work);
+ dev_info(ctrl->device,
+ "Removing ctrl: NQN \"%s\"\n", ctrl->opts->subsysnqn);
+
flush_work(&ctrl->reset_work);
nvme_stop_ctrl(ctrl);
nvme_remove_namespaces(ctrl);
@@ -378,6 +376,15 @@ static void nvme_put_ns(struct nvme_ns *ns)
kref_put(&ns->kref, nvme_free_ns);
}
+static inline void nvme_clear_nvme_request(struct request *req)
+{
+ if (!(req->rq_flags & RQF_DONTPREP)) {
+ nvme_req(req)->retries = 0;
+ nvme_req(req)->flags = 0;
+ req->rq_flags |= RQF_DONTPREP;
+ }
+}
+
struct request *nvme_alloc_request(struct request_queue *q,
struct nvme_command *cmd, blk_mq_req_flags_t flags, int qid)
{
@@ -394,6 +401,7 @@ struct request *nvme_alloc_request(struct request_queue *q,
return req;
req->cmd_flags |= REQ_FAILFAST_DRIVER;
+ nvme_clear_nvme_request(req);
nvme_req(req)->cmd = cmd;
return req;
@@ -610,11 +618,7 @@ blk_status_t nvme_setup_cmd(struct nvme_ns *ns, struct request *req,
{
blk_status_t ret = BLK_STS_OK;
- if (!(req->rq_flags & RQF_DONTPREP)) {
- nvme_req(req)->retries = 0;
- nvme_req(req)->flags = 0;
- req->rq_flags |= RQF_DONTPREP;
- }
+ nvme_clear_nvme_request(req);
switch (req_op(req)) {
case REQ_OP_DRV_IN:
@@ -744,6 +748,7 @@ static int nvme_submit_user_cmd(struct request_queue *q,
return PTR_ERR(req);
req->timeout = timeout ? timeout : ADMIN_TIMEOUT;
+ nvme_req(req)->flags |= NVME_REQ_USERCMD;
if (ubuffer && bufflen) {
ret = blk_rq_map_user(q, req, NULL, ubuffer, bufflen,
@@ -828,7 +833,7 @@ static void nvme_keep_alive_work(struct work_struct *work)
}
}
-void nvme_start_keep_alive(struct nvme_ctrl *ctrl)
+static void nvme_start_keep_alive(struct nvme_ctrl *ctrl)
{
if (unlikely(ctrl->kato == 0))
return;
@@ -838,7 +843,6 @@ void nvme_start_keep_alive(struct nvme_ctrl *ctrl)
ctrl->ka_cmd.common.opcode = nvme_admin_keep_alive;
schedule_delayed_work(&ctrl->ka_work, ctrl->kato * HZ);
}
-EXPORT_SYMBOL_GPL(nvme_start_keep_alive);
void nvme_stop_keep_alive(struct nvme_ctrl *ctrl)
{
@@ -948,7 +952,8 @@ static int nvme_identify_ns_list(struct nvme_ctrl *dev, unsigned nsid, __le32 *n
c.identify.opcode = nvme_admin_identify;
c.identify.cns = NVME_ID_CNS_NS_ACTIVE_LIST;
c.identify.nsid = cpu_to_le32(nsid);
- return nvme_submit_sync_cmd(dev->admin_q, &c, ns_list, 0x1000);
+ return nvme_submit_sync_cmd(dev->admin_q, &c, ns_list,
+ NVME_IDENTIFY_DATA_SIZE);
}
static struct nvme_id_ns *nvme_identify_ns(struct nvme_ctrl *ctrl,
@@ -1104,7 +1109,7 @@ static u32 nvme_passthru_start(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
}
if (ctrl->effects)
- effects = le32_to_cpu(ctrl->effects->iocs[opcode]);
+ effects = le32_to_cpu(ctrl->effects->acs[opcode]);
else
effects = nvme_known_admin_effects(opcode);
@@ -1124,13 +1129,13 @@ static void nvme_update_formats(struct nvme_ctrl *ctrl)
struct nvme_ns *ns, *next;
LIST_HEAD(rm_list);
- mutex_lock(&ctrl->namespaces_mutex);
+ down_write(&ctrl->namespaces_rwsem);
list_for_each_entry(ns, &ctrl->namespaces, list) {
if (ns->disk && nvme_revalidate_disk(ns->disk)) {
list_move_tail(&ns->list, &rm_list);
}
}
- mutex_unlock(&ctrl->namespaces_mutex);
+ up_write(&ctrl->namespaces_rwsem);
list_for_each_entry_safe(ns, next, &rm_list, list)
nvme_ns_remove(ns);
@@ -1358,7 +1363,7 @@ static void nvme_config_discard(struct nvme_ctrl *ctrl,
blk_queue_max_discard_sectors(queue, UINT_MAX);
blk_queue_max_discard_segments(queue, NVME_DSM_MAX_RANGES);
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, queue);
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, queue);
if (ctrl->quirks & NVME_QUIRK_DEALLOCATE_ZEROES)
blk_queue_max_write_zeroes_sectors(queue, UINT_MAX);
@@ -1449,6 +1454,8 @@ static void __nvme_revalidate_disk(struct gendisk *disk, struct nvme_id_ns *id)
if (ns->noiob)
nvme_set_chunk_size(ns);
nvme_update_disk_info(disk, ns, id);
+ if (ns->ndev)
+ nvme_nvm_update_nvm_info(ns);
#ifdef CONFIG_NVME_MULTIPATH
if (ns->head->disk)
nvme_update_disk_info(ns->head->disk, ns, id);
@@ -2217,18 +2224,35 @@ out_unlock:
return ret;
}
-static int nvme_get_log(struct nvme_ctrl *ctrl, u8 log_page, void *log,
- size_t size)
+int nvme_get_log_ext(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
+ u8 log_page, void *log,
+ size_t size, u64 offset)
{
struct nvme_command c = { };
+ unsigned long dwlen = size / 4 - 1;
+
+ c.get_log_page.opcode = nvme_admin_get_log_page;
+
+ if (ns)
+ c.get_log_page.nsid = cpu_to_le32(ns->head->ns_id);
+ else
+ c.get_log_page.nsid = cpu_to_le32(NVME_NSID_ALL);
- c.common.opcode = nvme_admin_get_log_page;
- c.common.nsid = cpu_to_le32(NVME_NSID_ALL);
- c.common.cdw10[0] = nvme_get_log_dw10(log_page, size);
+ c.get_log_page.lid = log_page;
+ c.get_log_page.numdl = cpu_to_le16(dwlen & ((1 << 16) - 1));
+ c.get_log_page.numdu = cpu_to_le16(dwlen >> 16);
+ c.get_log_page.lpol = cpu_to_le32(lower_32_bits(offset));
+ c.get_log_page.lpou = cpu_to_le32(upper_32_bits(offset));
return nvme_submit_sync_cmd(ctrl->admin_q, &c, log, size);
}
+static int nvme_get_log(struct nvme_ctrl *ctrl, u8 log_page, void *log,
+ size_t size)
+{
+ return nvme_get_log_ext(ctrl, NULL, log_page, log, size, 0);
+}
+
static int nvme_get_effects_log(struct nvme_ctrl *ctrl)
{
int ret;
@@ -2440,7 +2464,7 @@ static int nvme_dev_user_cmd(struct nvme_ctrl *ctrl, void __user *argp)
struct nvme_ns *ns;
int ret;
- mutex_lock(&ctrl->namespaces_mutex);
+ down_read(&ctrl->namespaces_rwsem);
if (list_empty(&ctrl->namespaces)) {
ret = -ENOTTY;
goto out_unlock;
@@ -2457,14 +2481,14 @@ static int nvme_dev_user_cmd(struct nvme_ctrl *ctrl, void __user *argp)
dev_warn(ctrl->device,
"using deprecated NVME_IOCTL_IO_CMD ioctl on the char device!\n");
kref_get(&ns->kref);
- mutex_unlock(&ctrl->namespaces_mutex);
+ up_read(&ctrl->namespaces_rwsem);
ret = nvme_user_cmd(ctrl, ns, argp);
nvme_put_ns(ns);
return ret;
out_unlock:
- mutex_unlock(&ctrl->namespaces_mutex);
+ up_read(&ctrl->namespaces_rwsem);
return ret;
}
@@ -2793,6 +2817,7 @@ static int __nvme_check_ids(struct nvme_subsystem *subsys,
list_for_each_entry(h, &subsys->nsheads, entry) {
if (nvme_ns_ids_valid(&new->ids) &&
+ !list_empty(&h->list) &&
nvme_ns_ids_equal(&new->ids, &h->ids))
return -EINVAL;
}
@@ -2814,7 +2839,9 @@ static struct nvme_ns_head *nvme_alloc_ns_head(struct nvme_ctrl *ctrl,
goto out_free_head;
head->instance = ret;
INIT_LIST_HEAD(&head->list);
- init_srcu_struct(&head->srcu);
+ ret = init_srcu_struct(&head->srcu);
+ if (ret)
+ goto out_ida_remove;
head->subsys = ctrl->subsys;
head->ns_id = nsid;
kref_init(&head->ref);
@@ -2836,6 +2863,7 @@ static struct nvme_ns_head *nvme_alloc_ns_head(struct nvme_ctrl *ctrl,
return head;
out_cleanup_srcu:
cleanup_srcu_struct(&head->srcu);
+out_ida_remove:
ida_simple_remove(&ctrl->subsys->ns_ida, head->instance);
out_free_head:
kfree(head);
@@ -2893,7 +2921,7 @@ static struct nvme_ns *nvme_find_get_ns(struct nvme_ctrl *ctrl, unsigned nsid)
{
struct nvme_ns *ns, *ret = NULL;
- mutex_lock(&ctrl->namespaces_mutex);
+ down_read(&ctrl->namespaces_rwsem);
list_for_each_entry(ns, &ctrl->namespaces, list) {
if (ns->head->ns_id == nsid) {
if (!kref_get_unless_zero(&ns->kref))
@@ -2904,7 +2932,7 @@ static struct nvme_ns *nvme_find_get_ns(struct nvme_ctrl *ctrl, unsigned nsid)
if (ns->head->ns_id > nsid)
break;
}
- mutex_unlock(&ctrl->namespaces_mutex);
+ up_read(&ctrl->namespaces_rwsem);
return ret;
}
@@ -2949,7 +2977,7 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid)
ns->queue = blk_mq_init_queue(ctrl->tagset);
if (IS_ERR(ns->queue))
goto out_free_ns;
- queue_flag_set_unlocked(QUEUE_FLAG_NONROT, ns->queue);
+ blk_queue_flag_set(QUEUE_FLAG_NONROT, ns->queue);
ns->queue->queuedata = ns;
ns->ctrl = ctrl;
@@ -3015,9 +3043,9 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid)
__nvme_revalidate_disk(disk, id);
- mutex_lock(&ctrl->namespaces_mutex);
+ down_write(&ctrl->namespaces_rwsem);
list_add_tail(&ns->list, &ctrl->namespaces);
- mutex_unlock(&ctrl->namespaces_mutex);
+ up_write(&ctrl->namespaces_rwsem);
nvme_get_ctrl(ctrl);
@@ -3033,6 +3061,7 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid)
ns->disk->disk_name);
nvme_mpath_add_disk(ns->head);
+ nvme_fault_inject_init(ns);
return;
out_unlink_ns:
mutex_lock(&ctrl->subsys->lock);
@@ -3051,6 +3080,7 @@ static void nvme_ns_remove(struct nvme_ns *ns)
if (test_and_set_bit(NVME_NS_REMOVING, &ns->flags))
return;
+ nvme_fault_inject_fini(ns);
if (ns->disk && ns->disk->flags & GENHD_FL_UP) {
sysfs_remove_group(&disk_to_dev(ns->disk)->kobj,
&nvme_ns_id_attr_group);
@@ -3067,9 +3097,9 @@ static void nvme_ns_remove(struct nvme_ns *ns)
list_del_rcu(&ns->siblings);
mutex_unlock(&ns->ctrl->subsys->lock);
- mutex_lock(&ns->ctrl->namespaces_mutex);
+ down_write(&ns->ctrl->namespaces_rwsem);
list_del_init(&ns->list);
- mutex_unlock(&ns->ctrl->namespaces_mutex);
+ up_write(&ns->ctrl->namespaces_rwsem);
synchronize_srcu(&ns->head->srcu);
nvme_mpath_check_last_path(ns);
@@ -3093,11 +3123,18 @@ static void nvme_remove_invalid_namespaces(struct nvme_ctrl *ctrl,
unsigned nsid)
{
struct nvme_ns *ns, *next;
+ LIST_HEAD(rm_list);
+ down_write(&ctrl->namespaces_rwsem);
list_for_each_entry_safe(ns, next, &ctrl->namespaces, list) {
if (ns->head->ns_id > nsid)
- nvme_ns_remove(ns);
+ list_move_tail(&ns->list, &rm_list);
}
+ up_write(&ctrl->namespaces_rwsem);
+
+ list_for_each_entry_safe(ns, next, &rm_list, list)
+ nvme_ns_remove(ns);
+
}
static int nvme_scan_ns_list(struct nvme_ctrl *ctrl, unsigned nn)
@@ -3107,7 +3144,7 @@ static int nvme_scan_ns_list(struct nvme_ctrl *ctrl, unsigned nn)
unsigned i, j, nsid, prev = 0, num_lists = DIV_ROUND_UP(nn, 1024);
int ret = 0;
- ns_list = kzalloc(0x1000, GFP_KERNEL);
+ ns_list = kzalloc(NVME_IDENTIFY_DATA_SIZE, GFP_KERNEL);
if (!ns_list)
return -ENOMEM;
@@ -3173,9 +3210,9 @@ static void nvme_scan_work(struct work_struct *work)
}
nvme_scan_ns_sequential(ctrl, nn);
done:
- mutex_lock(&ctrl->namespaces_mutex);
+ down_write(&ctrl->namespaces_rwsem);
list_sort(NULL, &ctrl->namespaces, ns_cmp);
- mutex_unlock(&ctrl->namespaces_mutex);
+ up_write(&ctrl->namespaces_rwsem);
kfree(id);
}
@@ -3197,6 +3234,7 @@ EXPORT_SYMBOL_GPL(nvme_queue_scan);
void nvme_remove_namespaces(struct nvme_ctrl *ctrl)
{
struct nvme_ns *ns, *next;
+ LIST_HEAD(ns_list);
/*
* The dead states indicates the controller was not gracefully
@@ -3207,7 +3245,11 @@ void nvme_remove_namespaces(struct nvme_ctrl *ctrl)
if (ctrl->state == NVME_CTRL_DEAD)
nvme_kill_queues(ctrl);
- list_for_each_entry_safe(ns, next, &ctrl->namespaces, list)
+ down_write(&ctrl->namespaces_rwsem);
+ list_splice_init(&ctrl->namespaces, &ns_list);
+ up_write(&ctrl->namespaces_rwsem);
+
+ list_for_each_entry_safe(ns, next, &ns_list, list)
nvme_ns_remove(ns);
}
EXPORT_SYMBOL_GPL(nvme_remove_namespaces);
@@ -3337,6 +3379,8 @@ void nvme_stop_ctrl(struct nvme_ctrl *ctrl)
flush_work(&ctrl->async_event_work);
flush_work(&ctrl->scan_work);
cancel_work_sync(&ctrl->fw_act_work);
+ if (ctrl->ops->stop_ctrl)
+ ctrl->ops->stop_ctrl(ctrl);
}
EXPORT_SYMBOL_GPL(nvme_stop_ctrl);
@@ -3394,7 +3438,7 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev,
ctrl->state = NVME_CTRL_NEW;
spin_lock_init(&ctrl->lock);
INIT_LIST_HEAD(&ctrl->namespaces);
- mutex_init(&ctrl->namespaces_mutex);
+ init_rwsem(&ctrl->namespaces_rwsem);
ctrl->dev = dev;
ctrl->ops = ops;
ctrl->quirks = quirks;
@@ -3455,7 +3499,7 @@ void nvme_kill_queues(struct nvme_ctrl *ctrl)
{
struct nvme_ns *ns;
- mutex_lock(&ctrl->namespaces_mutex);
+ down_read(&ctrl->namespaces_rwsem);
/* Forcibly unquiesce queues to avoid blocking dispatch */
if (ctrl->admin_q)
@@ -3474,7 +3518,7 @@ void nvme_kill_queues(struct nvme_ctrl *ctrl)
/* Forcibly unquiesce queues to avoid blocking dispatch */
blk_mq_unquiesce_queue(ns->queue);
}
- mutex_unlock(&ctrl->namespaces_mutex);
+ up_read(&ctrl->namespaces_rwsem);
}
EXPORT_SYMBOL_GPL(nvme_kill_queues);
@@ -3482,10 +3526,10 @@ void nvme_unfreeze(struct nvme_ctrl *ctrl)
{
struct nvme_ns *ns;
- mutex_lock(&ctrl->namespaces_mutex);
+ down_read(&ctrl->namespaces_rwsem);
list_for_each_entry(ns, &ctrl->namespaces, list)
blk_mq_unfreeze_queue(ns->queue);
- mutex_unlock(&ctrl->namespaces_mutex);
+ up_read(&ctrl->namespaces_rwsem);
}
EXPORT_SYMBOL_GPL(nvme_unfreeze);
@@ -3493,13 +3537,13 @@ void nvme_wait_freeze_timeout(struct nvme_ctrl *ctrl, long timeout)
{
struct nvme_ns *ns;
- mutex_lock(&ctrl->namespaces_mutex);
+ down_read(&ctrl->namespaces_rwsem);
list_for_each_entry(ns, &ctrl->namespaces, list) {
timeout = blk_mq_freeze_queue_wait_timeout(ns->queue, timeout);
if (timeout <= 0)
break;
}
- mutex_unlock(&ctrl->namespaces_mutex);
+ up_read(&ctrl->namespaces_rwsem);
}
EXPORT_SYMBOL_GPL(nvme_wait_freeze_timeout);
@@ -3507,10 +3551,10 @@ void nvme_wait_freeze(struct nvme_ctrl *ctrl)
{
struct nvme_ns *ns;
- mutex_lock(&ctrl->namespaces_mutex);
+ down_read(&ctrl->namespaces_rwsem);
list_for_each_entry(ns, &ctrl->namespaces, list)
blk_mq_freeze_queue_wait(ns->queue);
- mutex_unlock(&ctrl->namespaces_mutex);
+ up_read(&ctrl->namespaces_rwsem);
}
EXPORT_SYMBOL_GPL(nvme_wait_freeze);
@@ -3518,10 +3562,10 @@ void nvme_start_freeze(struct nvme_ctrl *ctrl)
{
struct nvme_ns *ns;
- mutex_lock(&ctrl->namespaces_mutex);
+ down_read(&ctrl->namespaces_rwsem);
list_for_each_entry(ns, &ctrl->namespaces, list)
blk_freeze_queue_start(ns->queue);
- mutex_unlock(&ctrl->namespaces_mutex);
+ up_read(&ctrl->namespaces_rwsem);
}
EXPORT_SYMBOL_GPL(nvme_start_freeze);
@@ -3529,10 +3573,10 @@ void nvme_stop_queues(struct nvme_ctrl *ctrl)
{
struct nvme_ns *ns;
- mutex_lock(&ctrl->namespaces_mutex);
+ down_read(&ctrl->namespaces_rwsem);
list_for_each_entry(ns, &ctrl->namespaces, list)
blk_mq_quiesce_queue(ns->queue);
- mutex_unlock(&ctrl->namespaces_mutex);
+ up_read(&ctrl->namespaces_rwsem);
}
EXPORT_SYMBOL_GPL(nvme_stop_queues);
@@ -3540,10 +3584,10 @@ void nvme_start_queues(struct nvme_ctrl *ctrl)
{
struct nvme_ns *ns;
- mutex_lock(&ctrl->namespaces_mutex);
+ down_read(&ctrl->namespaces_rwsem);
list_for_each_entry(ns, &ctrl->namespaces, list)
blk_mq_unquiesce_queue(ns->queue);
- mutex_unlock(&ctrl->namespaces_mutex);
+ up_read(&ctrl->namespaces_rwsem);
}
EXPORT_SYMBOL_GPL(nvme_start_queues);
diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c
index 8f0f34d06d46..124c458806df 100644
--- a/drivers/nvme/host/fabrics.c
+++ b/drivers/nvme/host/fabrics.c
@@ -536,6 +536,85 @@ static struct nvmf_transport_ops *nvmf_lookup_transport(
return NULL;
}
+blk_status_t nvmf_check_if_ready(struct nvme_ctrl *ctrl, struct request *rq,
+ bool queue_live, bool is_connected)
+{
+ struct nvme_command *cmd = nvme_req(rq)->cmd;
+
+ if (likely(ctrl->state == NVME_CTRL_LIVE && is_connected))
+ return BLK_STS_OK;
+
+ switch (ctrl->state) {
+ case NVME_CTRL_DELETING:
+ goto reject_io;
+
+ case NVME_CTRL_NEW:
+ case NVME_CTRL_CONNECTING:
+ if (!is_connected)
+ /*
+ * This is the case of starting a new
+ * association but connectivity was lost
+ * before it was fully created. We need to
+ * error the commands used to initialize the
+ * controller so the reconnect can go into a
+ * retry attempt. The commands should all be
+ * marked REQ_FAILFAST_DRIVER, which will hit
+ * the reject path below. Anything else will
+ * be queued while the state settles.
+ */
+ goto reject_or_queue_io;
+
+ if ((queue_live &&
+ !(nvme_req(rq)->flags & NVME_REQ_USERCMD)) ||
+ (!queue_live && blk_rq_is_passthrough(rq) &&
+ cmd->common.opcode == nvme_fabrics_command &&
+ cmd->fabrics.fctype == nvme_fabrics_type_connect))
+ /*
+ * If queue is live, allow only commands that
+ * are internally generated pass through. These
+ * are commands on the admin queue to initialize
+ * the controller. This will reject any ioctl
+ * admin cmds received while initializing.
+ *
+ * If the queue is not live, allow only a
+ * connect command. This will reject any ioctl
+ * admin cmd as well as initialization commands
+ * if the controller reverted the queue to non-live.
+ */
+ return BLK_STS_OK;
+
+ /*
+ * fall-thru to the reject_or_queue_io clause
+ */
+ break;
+
+ /* these cases fall-thru
+ * case NVME_CTRL_LIVE:
+ * case NVME_CTRL_RESETTING:
+ */
+ default:
+ break;
+ }
+
+reject_or_queue_io:
+ /*
+ * Any other new io is something we're not in a state to send
+ * to the device. Default action is to busy it and retry it
+ * after the controller state is recovered. However, anything
+ * marked for failfast or nvme multipath is immediately failed.
+ * Note: commands used to initialize the controller will be
+ * marked for failfast.
+ * Note: nvme cli/ioctl commands are marked for failfast.
+ */
+ if (!blk_noretry_request(rq) && !(rq->cmd_flags & REQ_NVME_MPATH))
+ return BLK_STS_RESOURCE;
+
+reject_io:
+ nvme_req(rq)->status = NVME_SC_ABORT_REQ;
+ return BLK_STS_IOERR;
+}
+EXPORT_SYMBOL_GPL(nvmf_check_if_ready);
+
static const match_table_t opt_tokens = {
{ NVMF_OPT_TRANSPORT, "transport=%s" },
{ NVMF_OPT_TRADDR, "traddr=%s" },
@@ -608,8 +687,10 @@ static int nvmf_parse_options(struct nvmf_ctrl_options *opts,
opts->discovery_nqn =
!(strcmp(opts->subsysnqn,
NVME_DISC_SUBSYS_NAME));
- if (opts->discovery_nqn)
+ if (opts->discovery_nqn) {
+ opts->kato = 0;
opts->nr_io_queues = 0;
+ }
break;
case NVMF_OPT_TRADDR:
p = match_strdup(args);
diff --git a/drivers/nvme/host/fabrics.h b/drivers/nvme/host/fabrics.h
index a3145d90c1d2..ef46c915b7b5 100644
--- a/drivers/nvme/host/fabrics.h
+++ b/drivers/nvme/host/fabrics.h
@@ -157,36 +157,7 @@ void nvmf_unregister_transport(struct nvmf_transport_ops *ops);
void nvmf_free_options(struct nvmf_ctrl_options *opts);
int nvmf_get_address(struct nvme_ctrl *ctrl, char *buf, int size);
bool nvmf_should_reconnect(struct nvme_ctrl *ctrl);
-
-static inline blk_status_t nvmf_check_init_req(struct nvme_ctrl *ctrl,
- struct request *rq)
-{
- struct nvme_command *cmd = nvme_req(rq)->cmd;
-
- /*
- * We cannot accept any other command until the connect command has
- * completed, so only allow connect to pass.
- */
- if (!blk_rq_is_passthrough(rq) ||
- cmd->common.opcode != nvme_fabrics_command ||
- cmd->fabrics.fctype != nvme_fabrics_type_connect) {
- /*
- * Connecting state means transport disruption or initial
- * establishment, which can take a long time and even might
- * fail permanently, fail fast to give upper layers a chance
- * to failover.
- * Deleting state means that the ctrl will never accept commands
- * again, fail it permanently.
- */
- if (ctrl->state == NVME_CTRL_CONNECTING ||
- ctrl->state == NVME_CTRL_DELETING) {
- nvme_req(rq)->status = NVME_SC_ABORT_REQ;
- return BLK_STS_IOERR;
- }
- return BLK_STS_RESOURCE; /* try again later */
- }
-
- return BLK_STS_OK;
-}
+blk_status_t nvmf_check_if_ready(struct nvme_ctrl *ctrl,
+ struct request *rq, bool queue_live, bool is_connected);
#endif /* _NVME_FABRICS_H */
diff --git a/drivers/nvme/host/fault_inject.c b/drivers/nvme/host/fault_inject.c
new file mode 100644
index 000000000000..02632266ac06
--- /dev/null
+++ b/drivers/nvme/host/fault_inject.c
@@ -0,0 +1,79 @@
+/*
+ * fault injection support for nvme.
+ *
+ * Copyright (c) 2018, Oracle and/or its affiliates
+ *
+ */
+
+#include <linux/moduleparam.h>
+#include "nvme.h"
+
+static DECLARE_FAULT_ATTR(fail_default_attr);
+/* optional fault injection attributes boot time option:
+ * nvme_core.fail_request=<interval>,<probability>,<space>,<times>
+ */
+static char *fail_request;
+module_param(fail_request, charp, 0000);
+
+void nvme_fault_inject_init(struct nvme_ns *ns)
+{
+ struct dentry *dir, *parent;
+ char *name = ns->disk->disk_name;
+ struct nvme_fault_inject *fault_inj = &ns->fault_inject;
+ struct fault_attr *attr = &fault_inj->attr;
+
+ /* set default fault injection attribute */
+ if (fail_request)
+ setup_fault_attr(&fail_default_attr, fail_request);
+
+ /* create debugfs directory and attribute */
+ parent = debugfs_create_dir(name, NULL);
+ if (!parent) {
+ pr_warn("%s: failed to create debugfs directory\n", name);
+ return;
+ }
+
+ *attr = fail_default_attr;
+ dir = fault_create_debugfs_attr("fault_inject", parent, attr);
+ if (IS_ERR(dir)) {
+ pr_warn("%s: failed to create debugfs attr\n", name);
+ debugfs_remove_recursive(parent);
+ return;
+ }
+ ns->fault_inject.parent = parent;
+
+ /* create debugfs for status code and dont_retry */
+ fault_inj->status = NVME_SC_INVALID_OPCODE;
+ fault_inj->dont_retry = true;
+ debugfs_create_x16("status", 0600, dir, &fault_inj->status);
+ debugfs_create_bool("dont_retry", 0600, dir, &fault_inj->dont_retry);
+}
+
+void nvme_fault_inject_fini(struct nvme_ns *ns)
+{
+ /* remove debugfs directories */
+ debugfs_remove_recursive(ns->fault_inject.parent);
+}
+
+void nvme_should_fail(struct request *req)
+{
+ struct gendisk *disk = req->rq_disk;
+ struct nvme_ns *ns = NULL;
+ u16 status;
+
+ /*
+ * make sure this request is coming from a valid namespace
+ */
+ if (!disk)
+ return;
+
+ ns = disk->private_data;
+ if (ns && should_fail(&ns->fault_inject.attr, 1)) {
+ /* inject status code and DNR bit */
+ status = ns->fault_inject.status;
+ if (ns->fault_inject.dont_retry)
+ status |= NVME_SC_DNR;
+ nvme_req(req)->status = status;
+ }
+}
+EXPORT_SYMBOL_GPL(nvme_should_fail);
diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c
index 1dc1387b7134..6cb26bcf6ec0 100644
--- a/drivers/nvme/host/fc.c
+++ b/drivers/nvme/host/fc.c
@@ -588,6 +588,8 @@ nvme_fc_attach_to_suspended_rport(struct nvme_fc_lport *lport,
return ERR_PTR(-ESTALE);
}
+ rport->remoteport.port_role = pinfo->port_role;
+ rport->remoteport.port_id = pinfo->port_id;
rport->remoteport.port_state = FC_OBJSTATE_ONLINE;
rport->dev_loss_end = 0;
@@ -768,8 +770,7 @@ nvme_fc_ctrl_connectivity_loss(struct nvme_fc_ctrl *ctrl)
*/
if (nvme_reset_ctrl(&ctrl->ctrl)) {
dev_warn(ctrl->ctrl.device,
- "NVME-FC{%d}: Couldn't schedule reset. "
- "Deleting controller.\n",
+ "NVME-FC{%d}: Couldn't schedule reset.\n",
ctrl->cnum);
nvme_delete_ctrl(&ctrl->ctrl);
}
@@ -836,8 +837,7 @@ nvme_fc_unregister_remoteport(struct nvme_fc_remote_port *portptr)
/* if dev_loss_tmo==0, dev loss is immediate */
if (!portptr->dev_loss_tmo) {
dev_warn(ctrl->ctrl.device,
- "NVME-FC{%d}: controller connectivity lost. "
- "Deleting controller.\n",
+ "NVME-FC{%d}: controller connectivity lost.\n",
ctrl->cnum);
nvme_delete_ctrl(&ctrl->ctrl);
} else
@@ -2076,20 +2076,10 @@ nvme_fc_timeout(struct request *rq, bool reserved)
{
struct nvme_fc_fcp_op *op = blk_mq_rq_to_pdu(rq);
struct nvme_fc_ctrl *ctrl = op->ctrl;
- int ret;
-
- if (ctrl->rport->remoteport.port_state != FC_OBJSTATE_ONLINE ||
- atomic_read(&op->state) == FCPOP_STATE_ABORTED)
- return BLK_EH_RESET_TIMER;
-
- ret = __nvme_fc_abort_op(ctrl, op);
- if (ret)
- /* io wasn't active to abort */
- return BLK_EH_NOT_HANDLED;
/*
* we can't individually ABTS an io without affecting the queue,
- * thus killing the queue, adn thus the association.
+ * thus killing the queue, and thus the association.
* So resolve by performing a controller reset, which will stop
* the host/io stack, terminate the association on the link,
* and recreate an association on the link.
@@ -2191,7 +2181,7 @@ nvme_fc_start_fcp_op(struct nvme_fc_ctrl *ctrl, struct nvme_fc_queue *queue,
struct nvme_fc_cmd_iu *cmdiu = &op->cmd_iu;
struct nvme_command *sqe = &cmdiu->sqe;
u32 csn;
- int ret;
+ int ret, opstate;
/*
* before attempting to send the io, check to see if we believe
@@ -2269,6 +2259,9 @@ nvme_fc_start_fcp_op(struct nvme_fc_ctrl *ctrl, struct nvme_fc_queue *queue,
queue->lldd_handle, &op->fcp_req);
if (ret) {
+ opstate = atomic_xchg(&op->state, FCPOP_STATE_COMPLETE);
+ __nvme_fc_fcpop_chk_teardowns(ctrl, op, opstate);
+
if (!(op->flags & FCOP_FLAGS_AEN))
nvme_fc_unmap_data(ctrl, op->rq, op);
@@ -2284,14 +2277,6 @@ nvme_fc_start_fcp_op(struct nvme_fc_ctrl *ctrl, struct nvme_fc_queue *queue,
return BLK_STS_OK;
}
-static inline blk_status_t nvme_fc_is_ready(struct nvme_fc_queue *queue,
- struct request *rq)
-{
- if (unlikely(!test_bit(NVME_FC_Q_LIVE, &queue->flags)))
- return nvmf_check_init_req(&queue->ctrl->ctrl, rq);
- return BLK_STS_OK;
-}
-
static blk_status_t
nvme_fc_queue_rq(struct blk_mq_hw_ctx *hctx,
const struct blk_mq_queue_data *bd)
@@ -2307,7 +2292,9 @@ nvme_fc_queue_rq(struct blk_mq_hw_ctx *hctx,
u32 data_len;
blk_status_t ret;
- ret = nvme_fc_is_ready(queue, rq);
+ ret = nvmf_check_if_ready(&queue->ctrl->ctrl, rq,
+ test_bit(NVME_FC_Q_LIVE, &queue->flags),
+ ctrl->rport->remoteport.port_state == FC_OBJSTATE_ONLINE);
if (unlikely(ret))
return ret;
@@ -2889,14 +2876,13 @@ nvme_fc_reconnect_or_delete(struct nvme_fc_ctrl *ctrl, int status)
if (portptr->port_state == FC_OBJSTATE_ONLINE)
dev_warn(ctrl->ctrl.device,
"NVME-FC{%d}: Max reconnect attempts (%d) "
- "reached. Removing controller\n",
+ "reached.\n",
ctrl->cnum, ctrl->ctrl.nr_reconnects);
else
dev_warn(ctrl->ctrl.device,
"NVME-FC{%d}: dev_loss_tmo (%d) expired "
- "while waiting for remoteport connectivity. "
- "Removing controller\n", ctrl->cnum,
- portptr->dev_loss_tmo);
+ "while waiting for remoteport connectivity.\n",
+ ctrl->cnum, portptr->dev_loss_tmo);
WARN_ON(nvme_delete_ctrl(&ctrl->ctrl));
}
}
@@ -3133,6 +3119,10 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts,
}
if (ret) {
+ nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_DELETING);
+ cancel_work_sync(&ctrl->ctrl.reset_work);
+ cancel_delayed_work_sync(&ctrl->connect_work);
+
/* couldn't schedule retry - fail out */
dev_err(ctrl->ctrl.device,
"NVME-FC{%d}: Connect retry failed\n", ctrl->cnum);
diff --git a/drivers/nvme/host/lightnvm.c b/drivers/nvme/host/lightnvm.c
index 50ef71ee3d86..41279da799ed 100644
--- a/drivers/nvme/host/lightnvm.c
+++ b/drivers/nvme/host/lightnvm.c
@@ -35,6 +35,10 @@ enum nvme_nvm_admin_opcode {
nvme_nvm_admin_set_bb_tbl = 0xf1,
};
+enum nvme_nvm_log_page {
+ NVME_NVM_LOG_REPORT_CHUNK = 0xca,
+};
+
struct nvme_nvm_ph_rw {
__u8 opcode;
__u8 flags;
@@ -51,6 +55,21 @@ struct nvme_nvm_ph_rw {
__le64 resv;
};
+struct nvme_nvm_erase_blk {
+ __u8 opcode;
+ __u8 flags;
+ __u16 command_id;
+ __le32 nsid;
+ __u64 rsvd[2];
+ __le64 prp1;
+ __le64 prp2;
+ __le64 spba;
+ __le16 length;
+ __le16 control;
+ __le32 dsmgmt;
+ __le64 resv;
+};
+
struct nvme_nvm_identity {
__u8 opcode;
__u8 flags;
@@ -59,8 +78,7 @@ struct nvme_nvm_identity {
__u64 rsvd[2];
__le64 prp1;
__le64 prp2;
- __le32 chnl_off;
- __u32 rsvd11[5];
+ __u32 rsvd11[6];
};
struct nvme_nvm_getbbtbl {
@@ -90,44 +108,18 @@ struct nvme_nvm_setbbtbl {
__u32 rsvd4[3];
};
-struct nvme_nvm_erase_blk {
- __u8 opcode;
- __u8 flags;
- __u16 command_id;
- __le32 nsid;
- __u64 rsvd[2];
- __le64 prp1;
- __le64 prp2;
- __le64 spba;
- __le16 length;
- __le16 control;
- __le32 dsmgmt;
- __le64 resv;
-};
-
struct nvme_nvm_command {
union {
struct nvme_common_command common;
- struct nvme_nvm_identity identity;
struct nvme_nvm_ph_rw ph_rw;
+ struct nvme_nvm_erase_blk erase;
+ struct nvme_nvm_identity identity;
struct nvme_nvm_getbbtbl get_bb;
struct nvme_nvm_setbbtbl set_bb;
- struct nvme_nvm_erase_blk erase;
};
};
-#define NVME_NVM_LP_MLC_PAIRS 886
-struct nvme_nvm_lp_mlc {
- __le16 num_pairs;
- __u8 pairs[NVME_NVM_LP_MLC_PAIRS];
-};
-
-struct nvme_nvm_lp_tbl {
- __u8 id[8];
- struct nvme_nvm_lp_mlc mlc;
-};
-
-struct nvme_nvm_id_group {
+struct nvme_nvm_id12_grp {
__u8 mtype;
__u8 fmtype;
__le16 res16;
@@ -150,11 +142,10 @@ struct nvme_nvm_id_group {
__le32 mpos;
__le32 mccap;
__le16 cpar;
- __u8 reserved[10];
- struct nvme_nvm_lp_tbl lptbl;
+ __u8 reserved[906];
} __packed;
-struct nvme_nvm_addr_format {
+struct nvme_nvm_id12_addrf {
__u8 ch_offset;
__u8 ch_len;
__u8 lun_offset;
@@ -165,21 +156,22 @@ struct nvme_nvm_addr_format {
__u8 blk_len;
__u8 pg_offset;
__u8 pg_len;
- __u8 sect_offset;
- __u8 sect_len;
+ __u8 sec_offset;
+ __u8 sec_len;
__u8 res[4];
} __packed;
-struct nvme_nvm_id {
+struct nvme_nvm_id12 {
__u8 ver_id;
__u8 vmnt;
__u8 cgrps;
__u8 res;
__le32 cap;
__le32 dom;
- struct nvme_nvm_addr_format ppaf;
+ struct nvme_nvm_id12_addrf ppaf;
__u8 resv[228];
- struct nvme_nvm_id_group groups[4];
+ struct nvme_nvm_id12_grp grp;
+ __u8 resv2[2880];
} __packed;
struct nvme_nvm_bb_tbl {
@@ -196,6 +188,68 @@ struct nvme_nvm_bb_tbl {
__u8 blk[0];
};
+struct nvme_nvm_id20_addrf {
+ __u8 grp_len;
+ __u8 pu_len;
+ __u8 chk_len;
+ __u8 lba_len;
+ __u8 resv[4];
+};
+
+struct nvme_nvm_id20 {
+ __u8 mjr;
+ __u8 mnr;
+ __u8 resv[6];
+
+ struct nvme_nvm_id20_addrf lbaf;
+
+ __le32 mccap;
+ __u8 resv2[12];
+
+ __u8 wit;
+ __u8 resv3[31];
+
+ /* Geometry */
+ __le16 num_grp;
+ __le16 num_pu;
+ __le32 num_chk;
+ __le32 clba;
+ __u8 resv4[52];
+
+ /* Write data requirements */
+ __le32 ws_min;
+ __le32 ws_opt;
+ __le32 mw_cunits;
+ __le32 maxoc;
+ __le32 maxocpu;
+ __u8 resv5[44];
+
+ /* Performance related metrics */
+ __le32 trdt;
+ __le32 trdm;
+ __le32 twrt;
+ __le32 twrm;
+ __le32 tcrst;
+ __le32 tcrsm;
+ __u8 resv6[40];
+
+ /* Reserved area */
+ __u8 resv7[2816];
+
+ /* Vendor specific */
+ __u8 vs[1024];
+};
+
+struct nvme_nvm_chk_meta {
+ __u8 state;
+ __u8 type;
+ __u8 wi;
+ __u8 rsvd[5];
+ __le64 slba;
+ __le64 cnlb;
+ __le64 wp;
+};
+
/*
* Check we didn't inadvertently grow the command struct
*/
@@ -203,105 +257,238 @@ static inline void _nvme_nvm_check_size(void)
{
BUILD_BUG_ON(sizeof(struct nvme_nvm_identity) != 64);
BUILD_BUG_ON(sizeof(struct nvme_nvm_ph_rw) != 64);
+ BUILD_BUG_ON(sizeof(struct nvme_nvm_erase_blk) != 64);
BUILD_BUG_ON(sizeof(struct nvme_nvm_getbbtbl) != 64);
BUILD_BUG_ON(sizeof(struct nvme_nvm_setbbtbl) != 64);
- BUILD_BUG_ON(sizeof(struct nvme_nvm_erase_blk) != 64);
- BUILD_BUG_ON(sizeof(struct nvme_nvm_id_group) != 960);
- BUILD_BUG_ON(sizeof(struct nvme_nvm_addr_format) != 16);
- BUILD_BUG_ON(sizeof(struct nvme_nvm_id) != NVME_IDENTIFY_DATA_SIZE);
+ BUILD_BUG_ON(sizeof(struct nvme_nvm_id12_grp) != 960);
+ BUILD_BUG_ON(sizeof(struct nvme_nvm_id12_addrf) != 16);
+ BUILD_BUG_ON(sizeof(struct nvme_nvm_id12) != NVME_IDENTIFY_DATA_SIZE);
BUILD_BUG_ON(sizeof(struct nvme_nvm_bb_tbl) != 64);
+ BUILD_BUG_ON(sizeof(struct nvme_nvm_id20_addrf) != 8);
+ BUILD_BUG_ON(sizeof(struct nvme_nvm_id20) != NVME_IDENTIFY_DATA_SIZE);
+ BUILD_BUG_ON(sizeof(struct nvme_nvm_chk_meta) != 32);
+ BUILD_BUG_ON(sizeof(struct nvme_nvm_chk_meta) !=
+ sizeof(struct nvm_chk_meta));
+}
+
+static void nvme_nvm_set_addr_12(struct nvm_addrf_12 *dst,
+ struct nvme_nvm_id12_addrf *src)
+{
+ dst->ch_len = src->ch_len;
+ dst->lun_len = src->lun_len;
+ dst->blk_len = src->blk_len;
+ dst->pg_len = src->pg_len;
+ dst->pln_len = src->pln_len;
+ dst->sec_len = src->sec_len;
+
+ dst->ch_offset = src->ch_offset;
+ dst->lun_offset = src->lun_offset;
+ dst->blk_offset = src->blk_offset;
+ dst->pg_offset = src->pg_offset;
+ dst->pln_offset = src->pln_offset;
+ dst->sec_offset = src->sec_offset;
+
+ dst->ch_mask = ((1ULL << dst->ch_len) - 1) << dst->ch_offset;
+ dst->lun_mask = ((1ULL << dst->lun_len) - 1) << dst->lun_offset;
+ dst->blk_mask = ((1ULL << dst->blk_len) - 1) << dst->blk_offset;
+ dst->pg_mask = ((1ULL << dst->pg_len) - 1) << dst->pg_offset;
+ dst->pln_mask = ((1ULL << dst->pln_len) - 1) << dst->pln_offset;
+ dst->sec_mask = ((1ULL << dst->sec_len) - 1) << dst->sec_offset;
}
-static int init_grps(struct nvm_id *nvm_id, struct nvme_nvm_id *nvme_nvm_id)
+static int nvme_nvm_setup_12(struct nvme_nvm_id12 *id,
+ struct nvm_geo *geo)
{
- struct nvme_nvm_id_group *src;
- struct nvm_id_group *grp;
+ struct nvme_nvm_id12_grp *src;
int sec_per_pg, sec_per_pl, pg_per_blk;
- if (nvme_nvm_id->cgrps != 1)
+ if (id->cgrps != 1)
+ return -EINVAL;
+
+ src = &id->grp;
+
+ if (src->mtype != 0) {
+ pr_err("nvm: memory type not supported\n");
return -EINVAL;
+ }
+
+ /* 1.2 spec. only reports a single version id - unfold */
+ geo->major_ver_id = id->ver_id;
+ geo->minor_ver_id = 2;
- src = &nvme_nvm_id->groups[0];
- grp = &nvm_id->grp;
+ /* Set compacted version for upper layers */
+ geo->version = NVM_OCSSD_SPEC_12;
- grp->mtype = src->mtype;
- grp->fmtype = src->fmtype;
+ geo->num_ch = src->num_ch;
+ geo->num_lun = src->num_lun;
+ geo->all_luns = geo->num_ch * geo->num_lun;
- grp->num_ch = src->num_ch;
- grp->num_lun = src->num_lun;
+ geo->num_chk = le16_to_cpu(src->num_chk);
- grp->num_chk = le16_to_cpu(src->num_chk);
- grp->csecs = le16_to_cpu(src->csecs);
- grp->sos = le16_to_cpu(src->sos);
+ geo->csecs = le16_to_cpu(src->csecs);
+ geo->sos = le16_to_cpu(src->sos);
pg_per_blk = le16_to_cpu(src->num_pg);
- sec_per_pg = le16_to_cpu(src->fpg_sz) / grp->csecs;
+ sec_per_pg = le16_to_cpu(src->fpg_sz) / geo->csecs;
sec_per_pl = sec_per_pg * src->num_pln;
- grp->clba = sec_per_pl * pg_per_blk;
- grp->ws_per_chk = pg_per_blk;
-
- grp->mpos = le32_to_cpu(src->mpos);
- grp->cpar = le16_to_cpu(src->cpar);
- grp->mccap = le32_to_cpu(src->mccap);
-
- grp->ws_opt = grp->ws_min = sec_per_pg;
- grp->ws_seq = NVM_IO_SNGL_ACCESS;
-
- if (grp->mpos & 0x020202) {
- grp->ws_seq = NVM_IO_DUAL_ACCESS;
- grp->ws_opt <<= 1;
- } else if (grp->mpos & 0x040404) {
- grp->ws_seq = NVM_IO_QUAD_ACCESS;
- grp->ws_opt <<= 2;
- }
+ geo->clba = sec_per_pl * pg_per_blk;
+
+ geo->all_chunks = geo->all_luns * geo->num_chk;
+ geo->total_secs = geo->clba * geo->all_chunks;
+
+ geo->ws_min = sec_per_pg;
+ geo->ws_opt = sec_per_pg;
+ geo->mw_cunits = geo->ws_opt << 3; /* default to MLC safe values */
- grp->trdt = le32_to_cpu(src->trdt);
- grp->trdm = le32_to_cpu(src->trdm);
- grp->tprt = le32_to_cpu(src->tprt);
- grp->tprm = le32_to_cpu(src->tprm);
- grp->tbet = le32_to_cpu(src->tbet);
- grp->tbem = le32_to_cpu(src->tbem);
+ /* Do not impose values for maximum number of open blocks as it is
+ * unspecified in 1.2. Users of 1.2 must be aware of this and eventually
+ * specify these values through a quirk if restrictions apply.
+ */
+ geo->maxoc = geo->all_luns * geo->num_chk;
+ geo->maxocpu = geo->num_chk;
+
+ geo->mccap = le32_to_cpu(src->mccap);
+
+ geo->trdt = le32_to_cpu(src->trdt);
+ geo->trdm = le32_to_cpu(src->trdm);
+ geo->tprt = le32_to_cpu(src->tprt);
+ geo->tprm = le32_to_cpu(src->tprm);
+ geo->tbet = le32_to_cpu(src->tbet);
+ geo->tbem = le32_to_cpu(src->tbem);
/* 1.2 compatibility */
- grp->num_pln = src->num_pln;
- grp->num_pg = le16_to_cpu(src->num_pg);
- grp->fpg_sz = le16_to_cpu(src->fpg_sz);
+ geo->vmnt = id->vmnt;
+ geo->cap = le32_to_cpu(id->cap);
+ geo->dom = le32_to_cpu(id->dom);
+
+ geo->mtype = src->mtype;
+ geo->fmtype = src->fmtype;
+
+ geo->cpar = le16_to_cpu(src->cpar);
+ geo->mpos = le32_to_cpu(src->mpos);
+
+ geo->pln_mode = NVM_PLANE_SINGLE;
+
+ if (geo->mpos & 0x020202) {
+ geo->pln_mode = NVM_PLANE_DOUBLE;
+ geo->ws_opt <<= 1;
+ } else if (geo->mpos & 0x040404) {
+ geo->pln_mode = NVM_PLANE_QUAD;
+ geo->ws_opt <<= 2;
+ }
+
+ geo->num_pln = src->num_pln;
+ geo->num_pg = le16_to_cpu(src->num_pg);
+ geo->fpg_sz = le16_to_cpu(src->fpg_sz);
+
+ nvme_nvm_set_addr_12((struct nvm_addrf_12 *)&geo->addrf, &id->ppaf);
return 0;
}
-static int nvme_nvm_identity(struct nvm_dev *nvmdev, struct nvm_id *nvm_id)
+static void nvme_nvm_set_addr_20(struct nvm_addrf *dst,
+ struct nvme_nvm_id20_addrf *src)
+{
+ dst->ch_len = src->grp_len;
+ dst->lun_len = src->pu_len;
+ dst->chk_len = src->chk_len;
+ dst->sec_len = src->lba_len;
+
+ dst->sec_offset = 0;
+ dst->chk_offset = dst->sec_len;
+ dst->lun_offset = dst->chk_offset + dst->chk_len;
+ dst->ch_offset = dst->lun_offset + dst->lun_len;
+
+ dst->ch_mask = ((1ULL << dst->ch_len) - 1) << dst->ch_offset;
+ dst->lun_mask = ((1ULL << dst->lun_len) - 1) << dst->lun_offset;
+ dst->chk_mask = ((1ULL << dst->chk_len) - 1) << dst->chk_offset;
+ dst->sec_mask = ((1ULL << dst->sec_len) - 1) << dst->sec_offset;
+}
+
+static int nvme_nvm_setup_20(struct nvme_nvm_id20 *id,
+ struct nvm_geo *geo)
+{
+ geo->major_ver_id = id->mjr;
+ geo->minor_ver_id = id->mnr;
+
+ /* Set compacted version for upper layers */
+ geo->version = NVM_OCSSD_SPEC_20;
+
+ if (!(geo->major_ver_id == 2 && geo->minor_ver_id == 0)) {
+ pr_err("nvm: OCSSD version not supported (v%d.%d)\n",
+ geo->major_ver_id, geo->minor_ver_id);
+ return -EINVAL;
+ }
+
+ geo->num_ch = le16_to_cpu(id->num_grp);
+ geo->num_lun = le16_to_cpu(id->num_pu);
+ geo->all_luns = geo->num_ch * geo->num_lun;
+
+ geo->num_chk = le32_to_cpu(id->num_chk);
+ geo->clba = le32_to_cpu(id->clba);
+
+ geo->all_chunks = geo->all_luns * geo->num_chk;
+ geo->total_secs = geo->clba * geo->all_chunks;
+
+ geo->ws_min = le32_to_cpu(id->ws_min);
+ geo->ws_opt = le32_to_cpu(id->ws_opt);
+ geo->mw_cunits = le32_to_cpu(id->mw_cunits);
+ geo->maxoc = le32_to_cpu(id->maxoc);
+ geo->maxocpu = le32_to_cpu(id->maxocpu);
+
+ geo->trdt = le32_to_cpu(id->trdt);
+ geo->trdm = le32_to_cpu(id->trdm);
+ geo->tprt = le32_to_cpu(id->twrt);
+ geo->tprm = le32_to_cpu(id->twrm);
+ geo->tbet = le32_to_cpu(id->tcrst);
+ geo->tbem = le32_to_cpu(id->tcrsm);
+
+ nvme_nvm_set_addr_20(&geo->addrf, &id->lbaf);
+
+ return 0;
+}
+
+static int nvme_nvm_identity(struct nvm_dev *nvmdev)
{
struct nvme_ns *ns = nvmdev->q->queuedata;
- struct nvme_nvm_id *nvme_nvm_id;
+ struct nvme_nvm_id12 *id;
struct nvme_nvm_command c = {};
int ret;
c.identity.opcode = nvme_nvm_admin_identity;
c.identity.nsid = cpu_to_le32(ns->head->ns_id);
- c.identity.chnl_off = 0;
- nvme_nvm_id = kmalloc(sizeof(struct nvme_nvm_id), GFP_KERNEL);
- if (!nvme_nvm_id)
+ id = kmalloc(sizeof(struct nvme_nvm_id12), GFP_KERNEL);
+ if (!id)
return -ENOMEM;
ret = nvme_submit_sync_cmd(ns->ctrl->admin_q, (struct nvme_command *)&c,
- nvme_nvm_id, sizeof(struct nvme_nvm_id));
+ id, sizeof(struct nvme_nvm_id12));
if (ret) {
ret = -EIO;
goto out;
}
- nvm_id->ver_id = nvme_nvm_id->ver_id;
- nvm_id->vmnt = nvme_nvm_id->vmnt;
- nvm_id->cap = le32_to_cpu(nvme_nvm_id->cap);
- nvm_id->dom = le32_to_cpu(nvme_nvm_id->dom);
- memcpy(&nvm_id->ppaf, &nvme_nvm_id->ppaf,
- sizeof(struct nvm_addr_format));
+ /*
+ * The 1.2 and 2.0 specifications share the first byte in their geometry
+ * command to make it possible to know what version a device implements.
+ */
+ switch (id->ver_id) {
+ case 1:
+ ret = nvme_nvm_setup_12(id, &nvmdev->geo);
+ break;
+ case 2:
+ ret = nvme_nvm_setup_20((struct nvme_nvm_id20 *)id,
+ &nvmdev->geo);
+ break;
+ default:
+ dev_err(ns->ctrl->device, "OCSSD revision not supported (%d)\n",
+ id->ver_id);
+ ret = -EINVAL;
+ }
- ret = init_grps(nvm_id, nvme_nvm_id);
out:
- kfree(nvme_nvm_id);
+ kfree(id);
return ret;
}
@@ -314,7 +501,7 @@ static int nvme_nvm_get_bb_tbl(struct nvm_dev *nvmdev, struct ppa_addr ppa,
struct nvme_ctrl *ctrl = ns->ctrl;
struct nvme_nvm_command c = {};
struct nvme_nvm_bb_tbl *bb_tbl;
- int nr_blks = geo->nr_chks * geo->plane_mode;
+ int nr_blks = geo->num_chk * geo->num_pln;
int tblsz = sizeof(struct nvme_nvm_bb_tbl) + nr_blks;
int ret = 0;
@@ -355,7 +542,7 @@ static int nvme_nvm_get_bb_tbl(struct nvm_dev *nvmdev, struct ppa_addr ppa,
goto out;
}
- memcpy(blks, bb_tbl->blk, geo->nr_chks * geo->plane_mode);
+ memcpy(blks, bb_tbl->blk, geo->num_chk * geo->num_pln);
out:
kfree(bb_tbl);
return ret;
@@ -382,6 +569,61 @@ static int nvme_nvm_set_bb_tbl(struct nvm_dev *nvmdev, struct ppa_addr *ppas,
return ret;
}
+/*
+ * Expect the lba in device format
+ */
+static int nvme_nvm_get_chk_meta(struct nvm_dev *ndev,
+ struct nvm_chk_meta *meta,
+ sector_t slba, int nchks)
+{
+ struct nvm_geo *geo = &ndev->geo;
+ struct nvme_ns *ns = ndev->q->queuedata;
+ struct nvme_ctrl *ctrl = ns->ctrl;
+ struct nvme_nvm_chk_meta *dev_meta = (struct nvme_nvm_chk_meta *)meta;
+ struct ppa_addr ppa;
+ size_t left = nchks * sizeof(struct nvme_nvm_chk_meta);
+ size_t log_pos, offset, len;
+ int ret, i;
+
+ /* Normalize lba address space to obtain log offset */
+ ppa.ppa = slba;
+ ppa = dev_to_generic_addr(ndev, ppa);
+
+ log_pos = ppa.m.chk;
+ log_pos += ppa.m.pu * geo->num_chk;
+ log_pos += ppa.m.grp * geo->num_lun * geo->num_chk;
+
+ offset = log_pos * sizeof(struct nvme_nvm_chk_meta);
+
+ while (left) {
+ len = min_t(unsigned int, left, ctrl->max_hw_sectors << 9);
+
+ ret = nvme_get_log_ext(ctrl, ns, NVME_NVM_LOG_REPORT_CHUNK,
+ dev_meta, len, offset);
+ if (ret) {
+ dev_err(ctrl->device, "Get REPORT CHUNK log error\n");
+ break;
+ }
+
+ for (i = 0; i < len; i += sizeof(struct nvme_nvm_chk_meta)) {
+ meta->state = dev_meta->state;
+ meta->type = dev_meta->type;
+ meta->wi = dev_meta->wi;
+ meta->slba = le64_to_cpu(dev_meta->slba);
+ meta->cnlb = le64_to_cpu(dev_meta->cnlb);
+ meta->wp = le64_to_cpu(dev_meta->wp);
+
+ meta++;
+ dev_meta++;
+ }
+
+ offset += len;
+ left -= len;
+ }
+
+ return ret;
+}
+
static inline void nvme_nvm_rqtocmd(struct nvm_rq *rqd, struct nvme_ns *ns,
struct nvme_nvm_command *c)
{
@@ -513,6 +755,8 @@ static struct nvm_dev_ops nvme_nvm_dev_ops = {
.get_bb_tbl = nvme_nvm_get_bb_tbl,
.set_bb_tbl = nvme_nvm_set_bb_tbl,
+ .get_chk_meta = nvme_nvm_get_chk_meta,
+
.submit_io = nvme_nvm_submit_io,
.submit_io_sync = nvme_nvm_submit_io_sync,
@@ -520,8 +764,6 @@ static struct nvm_dev_ops nvme_nvm_dev_ops = {
.destroy_dma_pool = nvme_nvm_destroy_dma_pool,
.dev_dma_alloc = nvme_nvm_dev_dma_alloc,
.dev_dma_free = nvme_nvm_dev_dma_free,
-
- .max_phys_sect = 64,
};
static int nvme_nvm_submit_user_cmd(struct request_queue *q,
@@ -722,6 +964,15 @@ int nvme_nvm_ioctl(struct nvme_ns *ns, unsigned int cmd, unsigned long arg)
}
}
+void nvme_nvm_update_nvm_info(struct nvme_ns *ns)
+{
+ struct nvm_dev *ndev = ns->ndev;
+ struct nvm_geo *geo = &ndev->geo;
+
+ geo->csecs = 1 << ns->lba_shift;
+ geo->sos = ns->ms;
+}
+
int nvme_nvm_register(struct nvme_ns *ns, char *disk_name, int node)
{
struct request_queue *q = ns->queue;
@@ -748,125 +999,205 @@ void nvme_nvm_unregister(struct nvme_ns *ns)
}
static ssize_t nvm_dev_attr_show(struct device *dev,
- struct device_attribute *dattr, char *page)
+ struct device_attribute *dattr, char *page)
{
struct nvme_ns *ns = nvme_get_ns_from_dev(dev);
struct nvm_dev *ndev = ns->ndev;
- struct nvm_id *id;
- struct nvm_id_group *grp;
+ struct nvm_geo *geo = &ndev->geo;
struct attribute *attr;
if (!ndev)
return 0;
- id = &ndev->identity;
- grp = &id->grp;
attr = &dattr->attr;
if (strcmp(attr->name, "version") == 0) {
- return scnprintf(page, PAGE_SIZE, "%u\n", id->ver_id);
- } else if (strcmp(attr->name, "vendor_opcode") == 0) {
- return scnprintf(page, PAGE_SIZE, "%u\n", id->vmnt);
+ if (geo->major_ver_id == 1)
+ return scnprintf(page, PAGE_SIZE, "%u\n",
+ geo->major_ver_id);
+ else
+ return scnprintf(page, PAGE_SIZE, "%u.%u\n",
+ geo->major_ver_id,
+ geo->minor_ver_id);
} else if (strcmp(attr->name, "capabilities") == 0) {
- return scnprintf(page, PAGE_SIZE, "%u\n", id->cap);
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->cap);
+ } else if (strcmp(attr->name, "read_typ") == 0) {
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->trdt);
+ } else if (strcmp(attr->name, "read_max") == 0) {
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->trdm);
+ } else {
+ return scnprintf(page,
+ PAGE_SIZE,
+ "Unhandled attr(%s) in `%s`\n",
+ attr->name, __func__);
+ }
+}
+
+static ssize_t nvm_dev_attr_show_ppaf(struct nvm_addrf_12 *ppaf, char *page)
+{
+ return scnprintf(page, PAGE_SIZE,
+ "0x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
+ ppaf->ch_offset, ppaf->ch_len,
+ ppaf->lun_offset, ppaf->lun_len,
+ ppaf->pln_offset, ppaf->pln_len,
+ ppaf->blk_offset, ppaf->blk_len,
+ ppaf->pg_offset, ppaf->pg_len,
+ ppaf->sec_offset, ppaf->sec_len);
+}
+
+static ssize_t nvm_dev_attr_show_12(struct device *dev,
+ struct device_attribute *dattr, char *page)
+{
+ struct nvme_ns *ns = nvme_get_ns_from_dev(dev);
+ struct nvm_dev *ndev = ns->ndev;
+ struct nvm_geo *geo = &ndev->geo;
+ struct attribute *attr;
+
+ if (!ndev)
+ return 0;
+
+ attr = &dattr->attr;
+
+ if (strcmp(attr->name, "vendor_opcode") == 0) {
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->vmnt);
} else if (strcmp(attr->name, "device_mode") == 0) {
- return scnprintf(page, PAGE_SIZE, "%u\n", id->dom);
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->dom);
/* kept for compatibility */
} else if (strcmp(attr->name, "media_manager") == 0) {
return scnprintf(page, PAGE_SIZE, "%s\n", "gennvm");
} else if (strcmp(attr->name, "ppa_format") == 0) {
- return scnprintf(page, PAGE_SIZE,
- "0x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
- id->ppaf.ch_offset, id->ppaf.ch_len,
- id->ppaf.lun_offset, id->ppaf.lun_len,
- id->ppaf.pln_offset, id->ppaf.pln_len,
- id->ppaf.blk_offset, id->ppaf.blk_len,
- id->ppaf.pg_offset, id->ppaf.pg_len,
- id->ppaf.sect_offset, id->ppaf.sect_len);
+ return nvm_dev_attr_show_ppaf((void *)&geo->addrf, page);
} else if (strcmp(attr->name, "media_type") == 0) { /* u8 */
- return scnprintf(page, PAGE_SIZE, "%u\n", grp->mtype);
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->mtype);
} else if (strcmp(attr->name, "flash_media_type") == 0) {
- return scnprintf(page, PAGE_SIZE, "%u\n", grp->fmtype);
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->fmtype);
} else if (strcmp(attr->name, "num_channels") == 0) {
- return scnprintf(page, PAGE_SIZE, "%u\n", grp->num_ch);
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->num_ch);
} else if (strcmp(attr->name, "num_luns") == 0) {
- return scnprintf(page, PAGE_SIZE, "%u\n", grp->num_lun);
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->num_lun);
} else if (strcmp(attr->name, "num_planes") == 0) {
- return scnprintf(page, PAGE_SIZE, "%u\n", grp->num_pln);
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->num_pln);
} else if (strcmp(attr->name, "num_blocks") == 0) { /* u16 */
- return scnprintf(page, PAGE_SIZE, "%u\n", grp->num_chk);
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->num_chk);
} else if (strcmp(attr->name, "num_pages") == 0) {
- return scnprintf(page, PAGE_SIZE, "%u\n", grp->num_pg);
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->num_pg);
} else if (strcmp(attr->name, "page_size") == 0) {
- return scnprintf(page, PAGE_SIZE, "%u\n", grp->fpg_sz);
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->fpg_sz);
} else if (strcmp(attr->name, "hw_sector_size") == 0) {
- return scnprintf(page, PAGE_SIZE, "%u\n", grp->csecs);
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->csecs);
} else if (strcmp(attr->name, "oob_sector_size") == 0) {/* u32 */
- return scnprintf(page, PAGE_SIZE, "%u\n", grp->sos);
- } else if (strcmp(attr->name, "read_typ") == 0) {
- return scnprintf(page, PAGE_SIZE, "%u\n", grp->trdt);
- } else if (strcmp(attr->name, "read_max") == 0) {
- return scnprintf(page, PAGE_SIZE, "%u\n", grp->trdm);
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->sos);
} else if (strcmp(attr->name, "prog_typ") == 0) {
- return scnprintf(page, PAGE_SIZE, "%u\n", grp->tprt);
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->tprt);
} else if (strcmp(attr->name, "prog_max") == 0) {
- return scnprintf(page, PAGE_SIZE, "%u\n", grp->tprm);
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->tprm);
} else if (strcmp(attr->name, "erase_typ") == 0) {
- return scnprintf(page, PAGE_SIZE, "%u\n", grp->tbet);
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->tbet);
} else if (strcmp(attr->name, "erase_max") == 0) {
- return scnprintf(page, PAGE_SIZE, "%u\n", grp->tbem);
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->tbem);
} else if (strcmp(attr->name, "multiplane_modes") == 0) {
- return scnprintf(page, PAGE_SIZE, "0x%08x\n", grp->mpos);
+ return scnprintf(page, PAGE_SIZE, "0x%08x\n", geo->mpos);
} else if (strcmp(attr->name, "media_capabilities") == 0) {
- return scnprintf(page, PAGE_SIZE, "0x%08x\n", grp->mccap);
+ return scnprintf(page, PAGE_SIZE, "0x%08x\n", geo->mccap);
} else if (strcmp(attr->name, "max_phys_secs") == 0) {
- return scnprintf(page, PAGE_SIZE, "%u\n",
- ndev->ops->max_phys_sect);
+ return scnprintf(page, PAGE_SIZE, "%u\n", NVM_MAX_VLBA);
} else {
- return scnprintf(page,
- PAGE_SIZE,
- "Unhandled attr(%s) in `nvm_dev_attr_show`\n",
- attr->name);
+ return scnprintf(page, PAGE_SIZE,
+ "Unhandled attr(%s) in `%s`\n",
+ attr->name, __func__);
+ }
+}
+
+static ssize_t nvm_dev_attr_show_20(struct device *dev,
+ struct device_attribute *dattr, char *page)
+{
+ struct nvme_ns *ns = nvme_get_ns_from_dev(dev);
+ struct nvm_dev *ndev = ns->ndev;
+ struct nvm_geo *geo = &ndev->geo;
+ struct attribute *attr;
+
+ if (!ndev)
+ return 0;
+
+ attr = &dattr->attr;
+
+ if (strcmp(attr->name, "groups") == 0) {
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->num_ch);
+ } else if (strcmp(attr->name, "punits") == 0) {
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->num_lun);
+ } else if (strcmp(attr->name, "chunks") == 0) {
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->num_chk);
+ } else if (strcmp(attr->name, "clba") == 0) {
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->clba);
+ } else if (strcmp(attr->name, "ws_min") == 0) {
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->ws_min);
+ } else if (strcmp(attr->name, "ws_opt") == 0) {
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->ws_opt);
+ } else if (strcmp(attr->name, "maxoc") == 0) {
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->maxoc);
+ } else if (strcmp(attr->name, "maxocpu") == 0) {
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->maxocpu);
+ } else if (strcmp(attr->name, "mw_cunits") == 0) {
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->mw_cunits);
+ } else if (strcmp(attr->name, "write_typ") == 0) {
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->tprt);
+ } else if (strcmp(attr->name, "write_max") == 0) {
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->tprm);
+ } else if (strcmp(attr->name, "reset_typ") == 0) {
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->tbet);
+ } else if (strcmp(attr->name, "reset_max") == 0) {
+ return scnprintf(page, PAGE_SIZE, "%u\n", geo->tbem);
+ } else {
+ return scnprintf(page, PAGE_SIZE,
+ "Unhandled attr(%s) in `%s`\n",
+ attr->name, __func__);
}
}
-#define NVM_DEV_ATTR_RO(_name) \
+#define NVM_DEV_ATTR_RO(_name) \
DEVICE_ATTR(_name, S_IRUGO, nvm_dev_attr_show, NULL)
+#define NVM_DEV_ATTR_12_RO(_name) \
+ DEVICE_ATTR(_name, S_IRUGO, nvm_dev_attr_show_12, NULL)
+#define NVM_DEV_ATTR_20_RO(_name) \
+ DEVICE_ATTR(_name, S_IRUGO, nvm_dev_attr_show_20, NULL)
+/* general attributes */
static NVM_DEV_ATTR_RO(version);
-static NVM_DEV_ATTR_RO(vendor_opcode);
static NVM_DEV_ATTR_RO(capabilities);
-static NVM_DEV_ATTR_RO(device_mode);
-static NVM_DEV_ATTR_RO(ppa_format);
-static NVM_DEV_ATTR_RO(media_manager);
-
-static NVM_DEV_ATTR_RO(media_type);
-static NVM_DEV_ATTR_RO(flash_media_type);
-static NVM_DEV_ATTR_RO(num_channels);
-static NVM_DEV_ATTR_RO(num_luns);
-static NVM_DEV_ATTR_RO(num_planes);
-static NVM_DEV_ATTR_RO(num_blocks);
-static NVM_DEV_ATTR_RO(num_pages);
-static NVM_DEV_ATTR_RO(page_size);
-static NVM_DEV_ATTR_RO(hw_sector_size);
-static NVM_DEV_ATTR_RO(oob_sector_size);
+
static NVM_DEV_ATTR_RO(read_typ);
static NVM_DEV_ATTR_RO(read_max);
-static NVM_DEV_ATTR_RO(prog_typ);
-static NVM_DEV_ATTR_RO(prog_max);
-static NVM_DEV_ATTR_RO(erase_typ);
-static NVM_DEV_ATTR_RO(erase_max);
-static NVM_DEV_ATTR_RO(multiplane_modes);
-static NVM_DEV_ATTR_RO(media_capabilities);
-static NVM_DEV_ATTR_RO(max_phys_secs);
-
-static struct attribute *nvm_dev_attrs[] = {
+
+/* 1.2 values */
+static NVM_DEV_ATTR_12_RO(vendor_opcode);
+static NVM_DEV_ATTR_12_RO(device_mode);
+static NVM_DEV_ATTR_12_RO(ppa_format);
+static NVM_DEV_ATTR_12_RO(media_manager);
+static NVM_DEV_ATTR_12_RO(media_type);
+static NVM_DEV_ATTR_12_RO(flash_media_type);
+static NVM_DEV_ATTR_12_RO(num_channels);
+static NVM_DEV_ATTR_12_RO(num_luns);
+static NVM_DEV_ATTR_12_RO(num_planes);
+static NVM_DEV_ATTR_12_RO(num_blocks);
+static NVM_DEV_ATTR_12_RO(num_pages);
+static NVM_DEV_ATTR_12_RO(page_size);
+static NVM_DEV_ATTR_12_RO(hw_sector_size);
+static NVM_DEV_ATTR_12_RO(oob_sector_size);
+static NVM_DEV_ATTR_12_RO(prog_typ);
+static NVM_DEV_ATTR_12_RO(prog_max);
+static NVM_DEV_ATTR_12_RO(erase_typ);
+static NVM_DEV_ATTR_12_RO(erase_max);
+static NVM_DEV_ATTR_12_RO(multiplane_modes);
+static NVM_DEV_ATTR_12_RO(media_capabilities);
+static NVM_DEV_ATTR_12_RO(max_phys_secs);
+
+static struct attribute *nvm_dev_attrs_12[] = {
&dev_attr_version.attr,
- &dev_attr_vendor_opcode.attr,
&dev_attr_capabilities.attr,
+
+ &dev_attr_vendor_opcode.attr,
&dev_attr_device_mode.attr,
&dev_attr_media_manager.attr,
-
&dev_attr_ppa_format.attr,
&dev_attr_media_type.attr,
&dev_attr_flash_media_type.attr,
@@ -887,22 +1218,92 @@ static struct attribute *nvm_dev_attrs[] = {
&dev_attr_multiplane_modes.attr,
&dev_attr_media_capabilities.attr,
&dev_attr_max_phys_secs.attr,
+
NULL,
};
-static const struct attribute_group nvm_dev_attr_group = {
+static const struct attribute_group nvm_dev_attr_group_12 = {
.name = "lightnvm",
- .attrs = nvm_dev_attrs,
+ .attrs = nvm_dev_attrs_12,
+};
+
+/* 2.0 values */
+static NVM_DEV_ATTR_20_RO(groups);
+static NVM_DEV_ATTR_20_RO(punits);
+static NVM_DEV_ATTR_20_RO(chunks);
+static NVM_DEV_ATTR_20_RO(clba);
+static NVM_DEV_ATTR_20_RO(ws_min);
+static NVM_DEV_ATTR_20_RO(ws_opt);
+static NVM_DEV_ATTR_20_RO(maxoc);
+static NVM_DEV_ATTR_20_RO(maxocpu);
+static NVM_DEV_ATTR_20_RO(mw_cunits);
+static NVM_DEV_ATTR_20_RO(write_typ);
+static NVM_DEV_ATTR_20_RO(write_max);
+static NVM_DEV_ATTR_20_RO(reset_typ);
+static NVM_DEV_ATTR_20_RO(reset_max);
+
+static struct attribute *nvm_dev_attrs_20[] = {
+ &dev_attr_version.attr,
+ &dev_attr_capabilities.attr,
+
+ &dev_attr_groups.attr,
+ &dev_attr_punits.attr,
+ &dev_attr_chunks.attr,
+ &dev_attr_clba.attr,
+ &dev_attr_ws_min.attr,
+ &dev_attr_ws_opt.attr,
+ &dev_attr_maxoc.attr,
+ &dev_attr_maxocpu.attr,
+ &dev_attr_mw_cunits.attr,
+
+ &dev_attr_read_typ.attr,
+ &dev_attr_read_max.attr,
+ &dev_attr_write_typ.attr,
+ &dev_attr_write_max.attr,
+ &dev_attr_reset_typ.attr,
+ &dev_attr_reset_max.attr,
+
+ NULL,
+};
+
+static const struct attribute_group nvm_dev_attr_group_20 = {
+ .name = "lightnvm",
+ .attrs = nvm_dev_attrs_20,
};
int nvme_nvm_register_sysfs(struct nvme_ns *ns)
{
- return sysfs_create_group(&disk_to_dev(ns->disk)->kobj,
- &nvm_dev_attr_group);
+ struct nvm_dev *ndev = ns->ndev;
+ struct nvm_geo *geo = &ndev->geo;
+
+ if (!ndev)
+ return -EINVAL;
+
+ switch (geo->major_ver_id) {
+ case 1:
+ return sysfs_create_group(&disk_to_dev(ns->disk)->kobj,
+ &nvm_dev_attr_group_12);
+ case 2:
+ return sysfs_create_group(&disk_to_dev(ns->disk)->kobj,
+ &nvm_dev_attr_group_20);
+ }
+
+ return -EINVAL;
}
void nvme_nvm_unregister_sysfs(struct nvme_ns *ns)
{
- sysfs_remove_group(&disk_to_dev(ns->disk)->kobj,
- &nvm_dev_attr_group);
+ struct nvm_dev *ndev = ns->ndev;
+ struct nvm_geo *geo = &ndev->geo;
+
+ switch (geo->major_ver_id) {
+ case 1:
+ sysfs_remove_group(&disk_to_dev(ns->disk)->kobj,
+ &nvm_dev_attr_group_12);
+ break;
+ case 2:
+ sysfs_remove_group(&disk_to_dev(ns->disk)->kobj,
+ &nvm_dev_attr_group_20);
+ break;
+ }
}
diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c
index 060f69e03427..956e0b8e9c4d 100644
--- a/drivers/nvme/host/multipath.c
+++ b/drivers/nvme/host/multipath.c
@@ -44,12 +44,12 @@ void nvme_kick_requeue_lists(struct nvme_ctrl *ctrl)
{
struct nvme_ns *ns;
- mutex_lock(&ctrl->namespaces_mutex);
+ down_read(&ctrl->namespaces_rwsem);
list_for_each_entry(ns, &ctrl->namespaces, list) {
if (ns->head->disk)
kblockd_schedule_work(&ns->head->requeue_work);
}
- mutex_unlock(&ctrl->namespaces_mutex);
+ up_read(&ctrl->namespaces_rwsem);
}
static struct nvme_ns *__nvme_find_path(struct nvme_ns_head *head)
@@ -162,13 +162,13 @@ int nvme_mpath_alloc_disk(struct nvme_ctrl *ctrl, struct nvme_ns_head *head)
if (!(ctrl->subsys->cmic & (1 << 1)) || !multipath)
return 0;
- q = blk_alloc_queue_node(GFP_KERNEL, NUMA_NO_NODE);
+ q = blk_alloc_queue_node(GFP_KERNEL, NUMA_NO_NODE, NULL);
if (!q)
goto out;
q->queuedata = head;
blk_queue_make_request(q, nvme_ns_head_make_request);
q->poll_fn = nvme_ns_head_poll;
- queue_flag_set_unlocked(QUEUE_FLAG_NONROT, q);
+ blk_queue_flag_set(QUEUE_FLAG_NONROT, q);
/* set to a default value for 512 until disk is validated */
blk_queue_logical_block_size(q, 512);
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
index d733b14ede9d..061fecfd44f5 100644
--- a/drivers/nvme/host/nvme.h
+++ b/drivers/nvme/host/nvme.h
@@ -21,6 +21,7 @@
#include <linux/blk-mq.h>
#include <linux/lightnvm.h>
#include <linux/sed-opal.h>
+#include <linux/fault-inject.h>
extern unsigned int nvme_io_timeout;
#define NVME_IO_TIMEOUT (nvme_io_timeout * HZ)
@@ -104,6 +105,7 @@ struct nvme_request {
enum {
NVME_REQ_CANCELLED = (1 << 0),
+ NVME_REQ_USERCMD = (1 << 1),
};
static inline struct nvme_request *nvme_req(struct request *req)
@@ -140,7 +142,7 @@ struct nvme_ctrl {
struct blk_mq_tag_set *tagset;
struct blk_mq_tag_set *admin_tagset;
struct list_head namespaces;
- struct mutex namespaces_mutex;
+ struct rw_semaphore namespaces_rwsem;
struct device ctrl_device;
struct device *device; /* char device */
struct cdev cdev;
@@ -261,6 +263,15 @@ struct nvme_ns_head {
int instance;
};
+#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
+struct nvme_fault_inject {
+ struct fault_attr attr;
+ struct dentry *parent;
+ bool dont_retry; /* DNR, do not retry */
+ u16 status; /* status code */
+};
+#endif
+
struct nvme_ns {
struct list_head list;
@@ -282,6 +293,11 @@ struct nvme_ns {
#define NVME_NS_REMOVING 0
#define NVME_NS_DEAD 1
u16 noiob;
+
+#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
+ struct nvme_fault_inject fault_inject;
+#endif
+
};
struct nvme_ctrl_ops {
@@ -298,8 +314,19 @@ struct nvme_ctrl_ops {
void (*delete_ctrl)(struct nvme_ctrl *ctrl);
int (*get_address)(struct nvme_ctrl *ctrl, char *buf, int size);
int (*reinit_request)(void *data, struct request *rq);
+ void (*stop_ctrl)(struct nvme_ctrl *ctrl);
};
+#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
+void nvme_fault_inject_init(struct nvme_ns *ns);
+void nvme_fault_inject_fini(struct nvme_ns *ns);
+void nvme_should_fail(struct request *req);
+#else
+static inline void nvme_fault_inject_init(struct nvme_ns *ns) {}
+static inline void nvme_fault_inject_fini(struct nvme_ns *ns) {}
+static inline void nvme_should_fail(struct request *req) {}
+#endif
+
static inline bool nvme_ctrl_ready(struct nvme_ctrl *ctrl)
{
u32 val = 0;
@@ -336,6 +363,8 @@ static inline void nvme_end_request(struct request *req, __le16 status,
rq->status = le16_to_cpu(status) >> 1;
rq->result = result;
+ /* inject error when permitted by fault injection framework */
+ nvme_should_fail(req);
blk_mq_complete_request(req);
}
@@ -394,13 +423,15 @@ int __nvme_submit_sync_cmd(struct request_queue *q, struct nvme_command *cmd,
unsigned timeout, int qid, int at_head,
blk_mq_req_flags_t flags);
int nvme_set_queue_count(struct nvme_ctrl *ctrl, int *count);
-void nvme_start_keep_alive(struct nvme_ctrl *ctrl);
void nvme_stop_keep_alive(struct nvme_ctrl *ctrl);
int nvme_reset_ctrl(struct nvme_ctrl *ctrl);
int nvme_reset_ctrl_sync(struct nvme_ctrl *ctrl);
int nvme_delete_ctrl(struct nvme_ctrl *ctrl);
int nvme_delete_ctrl_sync(struct nvme_ctrl *ctrl);
+int nvme_get_log_ext(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
+ u8 log_page, void *log, size_t size, u64 offset);
+
extern const struct attribute_group nvme_ns_id_attr_group;
extern const struct block_device_operations nvme_ns_head_ops;
@@ -461,12 +492,14 @@ static inline void nvme_mpath_check_last_path(struct nvme_ns *ns)
#endif /* CONFIG_NVME_MULTIPATH */
#ifdef CONFIG_NVM
+void nvme_nvm_update_nvm_info(struct nvme_ns *ns);
int nvme_nvm_register(struct nvme_ns *ns, char *disk_name, int node);
void nvme_nvm_unregister(struct nvme_ns *ns);
int nvme_nvm_register_sysfs(struct nvme_ns *ns);
void nvme_nvm_unregister_sysfs(struct nvme_ns *ns);
int nvme_nvm_ioctl(struct nvme_ns *ns, unsigned int cmd, unsigned long arg);
#else
+static inline void nvme_nvm_update_nvm_info(struct nvme_ns *ns) {};
static inline int nvme_nvm_register(struct nvme_ns *ns, char *disk_name,
int node)
{
diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
index b6f43b738f03..fbc71fac6f1e 100644
--- a/drivers/nvme/host/pci.c
+++ b/drivers/nvme/host/pci.c
@@ -84,6 +84,7 @@ struct nvme_dev {
struct dma_pool *prp_small_pool;
unsigned online_queues;
unsigned max_qid;
+ unsigned int num_vecs;
int q_depth;
u32 db_stride;
void __iomem *bar;
@@ -414,7 +415,8 @@ static int nvme_pci_map_queues(struct blk_mq_tag_set *set)
{
struct nvme_dev *dev = set->driver_data;
- return blk_mq_pci_map_queues(set, to_pci_dev(dev->dev));
+ return blk_mq_pci_map_queues(set, to_pci_dev(dev->dev),
+ dev->num_vecs > 1 ? 1 /* admin queue */ : 0);
}
/**
@@ -1380,8 +1382,7 @@ static int nvme_alloc_sq_cmds(struct nvme_dev *dev, struct nvme_queue *nvmeq,
return 0;
}
-static int nvme_alloc_queue(struct nvme_dev *dev, int qid,
- int depth, int node)
+static int nvme_alloc_queue(struct nvme_dev *dev, int qid, int depth)
{
struct nvme_queue *nvmeq = &dev->queues[qid];
@@ -1457,7 +1458,11 @@ static int nvme_create_queue(struct nvme_queue *nvmeq, int qid)
nvmeq->sq_cmds_io = dev->cmb + offset;
}
- nvmeq->cq_vector = qid - 1;
+ /*
+ * A queue's vector matches the queue identifier unless the controller
+ * has only one vector available.
+ */
+ nvmeq->cq_vector = dev->num_vecs == 1 ? 0 : qid;
result = adapter_alloc_cq(dev, qid, nvmeq);
if (result < 0)
goto release_vector;
@@ -1596,8 +1601,7 @@ static int nvme_pci_configure_admin_queue(struct nvme_dev *dev)
if (result < 0)
return result;
- result = nvme_alloc_queue(dev, 0, NVME_AQ_DEPTH,
- dev_to_node(dev->dev));
+ result = nvme_alloc_queue(dev, 0, NVME_AQ_DEPTH);
if (result)
return result;
@@ -1630,9 +1634,7 @@ static int nvme_create_io_queues(struct nvme_dev *dev)
int ret = 0;
for (i = dev->ctrl.queue_count; i <= dev->max_qid; i++) {
- /* vector == qid - 1, match nvme_create_queue */
- if (nvme_alloc_queue(dev, i, dev->q_depth,
- pci_irq_get_node(to_pci_dev(dev->dev), i - 1))) {
+ if (nvme_alloc_queue(dev, i, dev->q_depth)) {
ret = -ENOMEM;
break;
}
@@ -1914,6 +1916,10 @@ static int nvme_setup_io_queues(struct nvme_dev *dev)
int result, nr_io_queues;
unsigned long size;
+ struct irq_affinity affd = {
+ .pre_vectors = 1
+ };
+
nr_io_queues = num_possible_cpus();
result = nvme_set_queue_count(&dev->ctrl, &nr_io_queues);
if (result < 0)
@@ -1949,11 +1955,12 @@ static int nvme_setup_io_queues(struct nvme_dev *dev)
* setting up the full range we need.
*/
pci_free_irq_vectors(pdev);
- nr_io_queues = pci_alloc_irq_vectors(pdev, 1, nr_io_queues,
- PCI_IRQ_ALL_TYPES | PCI_IRQ_AFFINITY);
- if (nr_io_queues <= 0)
+ result = pci_alloc_irq_vectors_affinity(pdev, 1, nr_io_queues + 1,
+ PCI_IRQ_ALL_TYPES | PCI_IRQ_AFFINITY, &affd);
+ if (result <= 0)
return -EIO;
- dev->max_qid = nr_io_queues;
+ dev->num_vecs = result;
+ dev->max_qid = max(result - 1, 1);
/*
* Should investigate if there's a performance win from allocating
@@ -2197,7 +2204,11 @@ static void nvme_dev_disable(struct nvme_dev *dev, bool shutdown)
if (!dead) {
if (shutdown)
nvme_wait_freeze_timeout(&dev->ctrl, NVME_IO_TIMEOUT);
+ }
+ nvme_stop_queues(&dev->ctrl);
+
+ if (!dead && dev->ctrl.queue_count > 0) {
/*
* If the controller is still alive tell it to stop using the
* host memory buffer. In theory the shutdown / reset should
@@ -2206,11 +2217,6 @@ static void nvme_dev_disable(struct nvme_dev *dev, bool shutdown)
*/
if (dev->host_mem_descs)
nvme_set_host_mem(dev, 0);
-
- }
- nvme_stop_queues(&dev->ctrl);
-
- if (!dead) {
nvme_disable_io_queues(dev);
nvme_disable_admin_queue(dev, shutdown);
}
@@ -2416,6 +2422,13 @@ static int nvme_pci_reg_read64(struct nvme_ctrl *ctrl, u32 off, u64 *val)
return 0;
}
+static int nvme_pci_get_address(struct nvme_ctrl *ctrl, char *buf, int size)
+{
+ struct pci_dev *pdev = to_pci_dev(to_nvme_dev(ctrl)->dev);
+
+ return snprintf(buf, size, "%s", dev_name(&pdev->dev));
+}
+
static const struct nvme_ctrl_ops nvme_pci_ctrl_ops = {
.name = "pcie",
.module = THIS_MODULE,
@@ -2425,6 +2438,7 @@ static const struct nvme_ctrl_ops nvme_pci_ctrl_ops = {
.reg_read64 = nvme_pci_reg_read64,
.free_ctrl = nvme_pci_free_ctrl,
.submit_async_event = nvme_pci_submit_async_event,
+ .get_address = nvme_pci_get_address,
};
static int nvme_dev_map(struct nvme_dev *dev)
@@ -2461,10 +2475,13 @@ static unsigned long check_vendor_combination_bug(struct pci_dev *pdev)
} else if (pdev->vendor == 0x144d && pdev->device == 0xa804) {
/*
* Samsung SSD 960 EVO drops off the PCIe bus after system
- * suspend on a Ryzen board, ASUS PRIME B350M-A.
+ * suspend on a Ryzen board, ASUS PRIME B350M-A, as well as
+ * within few minutes after bootup on a Coffee Lake board -
+ * ASUS PRIME Z370-A
*/
if (dmi_match(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC.") &&
- dmi_match(DMI_BOARD_NAME, "PRIME B350M-A"))
+ (dmi_match(DMI_BOARD_NAME, "PRIME B350M-A") ||
+ dmi_match(DMI_BOARD_NAME, "PRIME Z370-A")))
return NVME_QUIRK_NO_APST;
}
diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c
index 4d84a73ee12d..1eb4438a8763 100644
--- a/drivers/nvme/host/rdma.c
+++ b/drivers/nvme/host/rdma.c
@@ -867,6 +867,14 @@ out_free_io_queues:
return ret;
}
+static void nvme_rdma_stop_ctrl(struct nvme_ctrl *nctrl)
+{
+ struct nvme_rdma_ctrl *ctrl = to_rdma_ctrl(nctrl);
+
+ cancel_work_sync(&ctrl->err_work);
+ cancel_delayed_work_sync(&ctrl->reconnect_work);
+}
+
static void nvme_rdma_free_ctrl(struct nvme_ctrl *nctrl)
{
struct nvme_rdma_ctrl *ctrl = to_rdma_ctrl(nctrl);
@@ -899,7 +907,6 @@ static void nvme_rdma_reconnect_or_remove(struct nvme_rdma_ctrl *ctrl)
queue_delayed_work(nvme_wq, &ctrl->reconnect_work,
ctrl->ctrl.opts->reconnect_delay * HZ);
} else {
- dev_info(ctrl->ctrl.device, "Removing controller...\n");
nvme_delete_ctrl(&ctrl->ctrl);
}
}
@@ -974,8 +981,8 @@ static void nvme_rdma_error_recovery_work(struct work_struct *work)
nvme_start_queues(&ctrl->ctrl);
if (!nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_CONNECTING)) {
- /* state change failure should never happen */
- WARN_ON_ONCE(1);
+ /* state change failure is ok if we're in DELETING state */
+ WARN_ON_ONCE(ctrl->ctrl.state != NVME_CTRL_DELETING);
return;
}
@@ -1594,17 +1601,6 @@ nvme_rdma_timeout(struct request *rq, bool reserved)
return BLK_EH_HANDLED;
}
-/*
- * We cannot accept any other command until the Connect command has completed.
- */
-static inline blk_status_t
-nvme_rdma_is_ready(struct nvme_rdma_queue *queue, struct request *rq)
-{
- if (unlikely(!test_bit(NVME_RDMA_Q_LIVE, &queue->flags)))
- return nvmf_check_init_req(&queue->ctrl->ctrl, rq);
- return BLK_STS_OK;
-}
-
static blk_status_t nvme_rdma_queue_rq(struct blk_mq_hw_ctx *hctx,
const struct blk_mq_queue_data *bd)
{
@@ -1620,7 +1616,8 @@ static blk_status_t nvme_rdma_queue_rq(struct blk_mq_hw_ctx *hctx,
WARN_ON_ONCE(rq->tag < 0);
- ret = nvme_rdma_is_ready(queue, rq);
+ ret = nvmf_check_if_ready(&queue->ctrl->ctrl, rq,
+ test_bit(NVME_RDMA_Q_LIVE, &queue->flags), true);
if (unlikely(ret))
return ret;
@@ -1719,9 +1716,6 @@ static const struct blk_mq_ops nvme_rdma_admin_mq_ops = {
static void nvme_rdma_shutdown_ctrl(struct nvme_rdma_ctrl *ctrl, bool shutdown)
{
- cancel_work_sync(&ctrl->err_work);
- cancel_delayed_work_sync(&ctrl->reconnect_work);
-
if (ctrl->ctrl.queue_count > 1) {
nvme_stop_queues(&ctrl->ctrl);
blk_mq_tagset_busy_iter(&ctrl->tag_set,
@@ -1799,6 +1793,7 @@ static const struct nvme_ctrl_ops nvme_rdma_ctrl_ops = {
.submit_async_event = nvme_rdma_submit_async_event,
.delete_ctrl = nvme_rdma_delete_ctrl,
.get_address = nvmf_get_address,
+ .stop_ctrl = nvme_rdma_stop_ctrl,
};
static inline bool
@@ -2025,15 +2020,26 @@ static struct nvmf_transport_ops nvme_rdma_transport = {
static void nvme_rdma_remove_one(struct ib_device *ib_device, void *client_data)
{
struct nvme_rdma_ctrl *ctrl;
+ struct nvme_rdma_device *ndev;
+ bool found = false;
+
+ mutex_lock(&device_list_mutex);
+ list_for_each_entry(ndev, &device_list, entry) {
+ if (ndev->dev == ib_device) {
+ found = true;
+ break;
+ }
+ }
+ mutex_unlock(&device_list_mutex);
+
+ if (!found)
+ return;
/* Delete all controllers using this device */
mutex_lock(&nvme_rdma_ctrl_mutex);
list_for_each_entry(ctrl, &nvme_rdma_ctrl_list, list) {
if (ctrl->device->dev != ib_device)
continue;
- dev_info(ctrl->ctrl.device,
- "Removing ctrl: NQN \"%s\", addr %pISp\n",
- ctrl->ctrl.opts->subsysnqn, &ctrl->addr);
nvme_delete_ctrl(&ctrl->ctrl);
}
mutex_unlock(&nvme_rdma_ctrl_mutex);
diff --git a/drivers/nvme/target/admin-cmd.c b/drivers/nvme/target/admin-cmd.c
index 90dcdc40ac71..5e0e9fcc0d4d 100644
--- a/drivers/nvme/target/admin-cmd.c
+++ b/drivers/nvme/target/admin-cmd.c
@@ -178,6 +178,7 @@ static void nvmet_execute_identify_ctrl(struct nvmet_req *req)
id->vid = 0;
id->ssvid = 0;
+ memset(id->sn, ' ', sizeof(id->sn));
bin2hex(id->sn, &ctrl->subsys->serial,
min(sizeof(ctrl->subsys->serial), sizeof(id->sn) / 2));
memcpy_and_pad(id->mn, sizeof(id->mn), model, sizeof(model) - 1, ' ');
diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c
index e6b2d2af81b6..ad9ff27234b5 100644
--- a/drivers/nvme/target/configfs.c
+++ b/drivers/nvme/target/configfs.c
@@ -23,6 +23,15 @@
static const struct config_item_type nvmet_host_type;
static const struct config_item_type nvmet_subsys_type;
+static const struct nvmet_transport_name {
+ u8 type;
+ const char *name;
+} nvmet_transport_names[] = {
+ { NVMF_TRTYPE_RDMA, "rdma" },
+ { NVMF_TRTYPE_FC, "fc" },
+ { NVMF_TRTYPE_LOOP, "loop" },
+};
+
/*
* nvmet_port Generic ConfigFS definitions.
* Used in any place in the ConfigFS tree that refers to an address.
@@ -208,43 +217,30 @@ CONFIGFS_ATTR(nvmet_, addr_trsvcid);
static ssize_t nvmet_addr_trtype_show(struct config_item *item,
char *page)
{
- switch (to_nvmet_port(item)->disc_addr.trtype) {
- case NVMF_TRTYPE_RDMA:
- return sprintf(page, "rdma\n");
- case NVMF_TRTYPE_LOOP:
- return sprintf(page, "loop\n");
- case NVMF_TRTYPE_FC:
- return sprintf(page, "fc\n");
- default:
- return sprintf(page, "\n");
+ struct nvmet_port *port = to_nvmet_port(item);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(nvmet_transport_names); i++) {
+ if (port->disc_addr.trtype != nvmet_transport_names[i].type)
+ continue;
+ return sprintf(page, "%s\n", nvmet_transport_names[i].name);
}
+
+ return sprintf(page, "\n");
}
static void nvmet_port_init_tsas_rdma(struct nvmet_port *port)
{
- port->disc_addr.trtype = NVMF_TRTYPE_RDMA;
- memset(&port->disc_addr.tsas.rdma, 0, NVMF_TSAS_SIZE);
port->disc_addr.tsas.rdma.qptype = NVMF_RDMA_QPTYPE_CONNECTED;
port->disc_addr.tsas.rdma.prtype = NVMF_RDMA_PRTYPE_NOT_SPECIFIED;
port->disc_addr.tsas.rdma.cms = NVMF_RDMA_CMS_RDMA_CM;
}
-static void nvmet_port_init_tsas_loop(struct nvmet_port *port)
-{
- port->disc_addr.trtype = NVMF_TRTYPE_LOOP;
- memset(&port->disc_addr.tsas, 0, NVMF_TSAS_SIZE);
-}
-
-static void nvmet_port_init_tsas_fc(struct nvmet_port *port)
-{
- port->disc_addr.trtype = NVMF_TRTYPE_FC;
- memset(&port->disc_addr.tsas, 0, NVMF_TSAS_SIZE);
-}
-
static ssize_t nvmet_addr_trtype_store(struct config_item *item,
const char *page, size_t count)
{
struct nvmet_port *port = to_nvmet_port(item);
+ int i;
if (port->enabled) {
pr_err("Cannot modify address while enabled\n");
@@ -252,17 +248,18 @@ static ssize_t nvmet_addr_trtype_store(struct config_item *item,
return -EACCES;
}
- if (sysfs_streq(page, "rdma")) {
- nvmet_port_init_tsas_rdma(port);
- } else if (sysfs_streq(page, "loop")) {
- nvmet_port_init_tsas_loop(port);
- } else if (sysfs_streq(page, "fc")) {
- nvmet_port_init_tsas_fc(port);
- } else {
- pr_err("Invalid value '%s' for trtype\n", page);
- return -EINVAL;
+ for (i = 0; i < ARRAY_SIZE(nvmet_transport_names); i++) {
+ if (sysfs_streq(page, nvmet_transport_names[i].name))
+ goto found;
}
+ pr_err("Invalid value '%s' for trtype\n", page);
+ return -EINVAL;
+found:
+ memset(&port->disc_addr.tsas, 0, NVMF_TSAS_SIZE);
+ port->disc_addr.trtype = nvmet_transport_names[i].type;
+ if (port->disc_addr.trtype == NVMF_TRTYPE_RDMA)
+ nvmet_port_init_tsas_rdma(port);
return count;
}
@@ -333,13 +330,13 @@ out_unlock:
return ret ? ret : count;
}
+CONFIGFS_ATTR(nvmet_ns_, device_uuid);
+
static ssize_t nvmet_ns_device_nguid_show(struct config_item *item, char *page)
{
return sprintf(page, "%pUb\n", &to_nvmet_ns(item)->nguid);
}
-CONFIGFS_ATTR(nvmet_ns_, device_uuid);
-
static ssize_t nvmet_ns_device_nguid_store(struct config_item *item,
const char *page, size_t count)
{
diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c
index a78029e4e5f4..e95424f172fd 100644
--- a/drivers/nvme/target/core.c
+++ b/drivers/nvme/target/core.c
@@ -18,7 +18,7 @@
#include "nvmet.h"
-static struct nvmet_fabrics_ops *nvmet_transports[NVMF_TRTYPE_MAX];
+static const struct nvmet_fabrics_ops *nvmet_transports[NVMF_TRTYPE_MAX];
static DEFINE_IDA(cntlid_ida);
/*
@@ -137,7 +137,7 @@ static void nvmet_add_async_event(struct nvmet_ctrl *ctrl, u8 event_type,
schedule_work(&ctrl->async_event_work);
}
-int nvmet_register_transport(struct nvmet_fabrics_ops *ops)
+int nvmet_register_transport(const struct nvmet_fabrics_ops *ops)
{
int ret = 0;
@@ -152,7 +152,7 @@ int nvmet_register_transport(struct nvmet_fabrics_ops *ops)
}
EXPORT_SYMBOL_GPL(nvmet_register_transport);
-void nvmet_unregister_transport(struct nvmet_fabrics_ops *ops)
+void nvmet_unregister_transport(const struct nvmet_fabrics_ops *ops)
{
down_write(&nvmet_config_sem);
nvmet_transports[ops->type] = NULL;
@@ -162,7 +162,7 @@ EXPORT_SYMBOL_GPL(nvmet_unregister_transport);
int nvmet_enable_port(struct nvmet_port *port)
{
- struct nvmet_fabrics_ops *ops;
+ const struct nvmet_fabrics_ops *ops;
int ret;
lockdep_assert_held(&nvmet_config_sem);
@@ -195,7 +195,7 @@ int nvmet_enable_port(struct nvmet_port *port)
void nvmet_disable_port(struct nvmet_port *port)
{
- struct nvmet_fabrics_ops *ops;
+ const struct nvmet_fabrics_ops *ops;
lockdep_assert_held(&nvmet_config_sem);
@@ -500,7 +500,7 @@ int nvmet_sq_init(struct nvmet_sq *sq)
EXPORT_SYMBOL_GPL(nvmet_sq_init);
bool nvmet_req_init(struct nvmet_req *req, struct nvmet_cq *cq,
- struct nvmet_sq *sq, struct nvmet_fabrics_ops *ops)
+ struct nvmet_sq *sq, const struct nvmet_fabrics_ops *ops)
{
u8 flags = req->cmd->common.flags;
u16 status;
diff --git a/drivers/nvme/target/discovery.c b/drivers/nvme/target/discovery.c
index 8f3b57b4c97b..231e04e0a496 100644
--- a/drivers/nvme/target/discovery.c
+++ b/drivers/nvme/target/discovery.c
@@ -43,7 +43,8 @@ void nvmet_referral_disable(struct nvmet_port *port)
}
static void nvmet_format_discovery_entry(struct nvmf_disc_rsp_page_hdr *hdr,
- struct nvmet_port *port, char *subsys_nqn, u8 type, u32 numrec)
+ struct nvmet_port *port, char *subsys_nqn, char *traddr,
+ u8 type, u32 numrec)
{
struct nvmf_disc_rsp_page_entry *e = &hdr->entries[numrec];
@@ -56,9 +57,28 @@ static void nvmet_format_discovery_entry(struct nvmf_disc_rsp_page_hdr *hdr,
e->asqsz = cpu_to_le16(NVME_AQ_DEPTH);
e->subtype = type;
memcpy(e->trsvcid, port->disc_addr.trsvcid, NVMF_TRSVCID_SIZE);
- memcpy(e->traddr, port->disc_addr.traddr, NVMF_TRADDR_SIZE);
+ memcpy(e->traddr, traddr, NVMF_TRADDR_SIZE);
memcpy(e->tsas.common, port->disc_addr.tsas.common, NVMF_TSAS_SIZE);
- memcpy(e->subnqn, subsys_nqn, NVMF_NQN_SIZE);
+ strncpy(e->subnqn, subsys_nqn, NVMF_NQN_SIZE);
+}
+
+/*
+ * nvmet_set_disc_traddr - set a correct discovery log entry traddr
+ *
+ * IP based transports (e.g RDMA) can listen on "any" ipv4/ipv6 addresses
+ * (INADDR_ANY or IN6ADDR_ANY_INIT). The discovery log page traddr reply
+ * must not contain that "any" IP address. If the transport implements
+ * .disc_traddr, use it. this callback will set the discovery traddr
+ * from the req->port address in case the port in question listens
+ * "any" IP address.
+ */
+static void nvmet_set_disc_traddr(struct nvmet_req *req, struct nvmet_port *port,
+ char *traddr)
+{
+ if (req->ops->disc_traddr)
+ req->ops->disc_traddr(req, port, traddr);
+ else
+ memcpy(traddr, port->disc_addr.traddr, NVMF_TRADDR_SIZE);
}
static void nvmet_execute_get_disc_log_page(struct nvmet_req *req)
@@ -90,8 +110,11 @@ static void nvmet_execute_get_disc_log_page(struct nvmet_req *req)
if (!nvmet_host_allowed(req, p->subsys, ctrl->hostnqn))
continue;
if (residual_len >= entry_size) {
+ char traddr[NVMF_TRADDR_SIZE];
+
+ nvmet_set_disc_traddr(req, req->port, traddr);
nvmet_format_discovery_entry(hdr, req->port,
- p->subsys->subsysnqn,
+ p->subsys->subsysnqn, traddr,
NVME_NQN_NVME, numrec);
residual_len -= entry_size;
}
@@ -102,6 +125,7 @@ static void nvmet_execute_get_disc_log_page(struct nvmet_req *req)
if (residual_len >= entry_size) {
nvmet_format_discovery_entry(hdr, r,
NVME_DISC_SUBSYS_NAME,
+ r->disc_addr.traddr,
NVME_NQN_DISC, numrec);
residual_len -= entry_size;
}
diff --git a/drivers/nvme/target/fc.c b/drivers/nvme/target/fc.c
index 9b39a6cb1935..33ee8d3145f8 100644
--- a/drivers/nvme/target/fc.c
+++ b/drivers/nvme/target/fc.c
@@ -87,6 +87,7 @@ struct nvmet_fc_fcp_iod {
struct nvmet_req req;
struct work_struct work;
struct work_struct done_work;
+ struct work_struct defer_work;
struct nvmet_fc_tgtport *tgtport;
struct nvmet_fc_tgt_queue *queue;
@@ -224,6 +225,7 @@ static DEFINE_IDA(nvmet_fc_tgtport_cnt);
static void nvmet_fc_handle_ls_rqst_work(struct work_struct *work);
static void nvmet_fc_handle_fcp_rqst_work(struct work_struct *work);
static void nvmet_fc_fcp_rqst_op_done_work(struct work_struct *work);
+static void nvmet_fc_fcp_rqst_op_defer_work(struct work_struct *work);
static void nvmet_fc_tgt_a_put(struct nvmet_fc_tgt_assoc *assoc);
static int nvmet_fc_tgt_a_get(struct nvmet_fc_tgt_assoc *assoc);
static void nvmet_fc_tgt_q_put(struct nvmet_fc_tgt_queue *queue);
@@ -429,6 +431,7 @@ nvmet_fc_prep_fcp_iodlist(struct nvmet_fc_tgtport *tgtport,
for (i = 0; i < queue->sqsize; fod++, i++) {
INIT_WORK(&fod->work, nvmet_fc_handle_fcp_rqst_work);
INIT_WORK(&fod->done_work, nvmet_fc_fcp_rqst_op_done_work);
+ INIT_WORK(&fod->defer_work, nvmet_fc_fcp_rqst_op_defer_work);
fod->tgtport = tgtport;
fod->queue = queue;
fod->active = false;
@@ -512,6 +515,17 @@ nvmet_fc_queue_fcp_req(struct nvmet_fc_tgtport *tgtport,
}
static void
+nvmet_fc_fcp_rqst_op_defer_work(struct work_struct *work)
+{
+ struct nvmet_fc_fcp_iod *fod =
+ container_of(work, struct nvmet_fc_fcp_iod, defer_work);
+
+ /* Submit deferred IO for processing */
+ nvmet_fc_queue_fcp_req(fod->tgtport, fod->queue, fod->fcpreq);
+
+}
+
+static void
nvmet_fc_free_fcp_iod(struct nvmet_fc_tgt_queue *queue,
struct nvmet_fc_fcp_iod *fod)
{
@@ -568,13 +582,12 @@ nvmet_fc_free_fcp_iod(struct nvmet_fc_tgt_queue *queue,
/* inform LLDD IO is now being processed */
tgtport->ops->defer_rcv(&tgtport->fc_target_port, fcpreq);
- /* Submit deferred IO for processing */
- nvmet_fc_queue_fcp_req(tgtport, queue, fcpreq);
-
/*
* Leave the queue lookup get reference taken when
* fod was originally allocated.
*/
+
+ queue_work(queue->work_q, &fod->defer_work);
}
static int
@@ -1550,7 +1563,7 @@ nvmet_fc_ls_disconnect(struct nvmet_fc_tgtport *tgtport,
static void nvmet_fc_fcp_nvme_cmd_done(struct nvmet_req *nvme_req);
-static struct nvmet_fabrics_ops nvmet_fc_tgt_fcp_ops;
+static const struct nvmet_fabrics_ops nvmet_fc_tgt_fcp_ops;
static void
nvmet_fc_xmt_ls_rsp_done(struct nvmefc_tgt_ls_req *lsreq)
@@ -2505,7 +2518,7 @@ nvmet_fc_remove_port(struct nvmet_port *port)
/* nothing to do */
}
-static struct nvmet_fabrics_ops nvmet_fc_tgt_fcp_ops = {
+static const struct nvmet_fabrics_ops nvmet_fc_tgt_fcp_ops = {
.owner = THIS_MODULE,
.type = NVMF_TRTYPE_FC,
.msdbd = 1,
diff --git a/drivers/nvme/target/io-cmd.c b/drivers/nvme/target/io-cmd.c
index 28bbdff4a88b..cd2344179673 100644
--- a/drivers/nvme/target/io-cmd.c
+++ b/drivers/nvme/target/io-cmd.c
@@ -173,8 +173,8 @@ static void nvmet_execute_write_zeroes(struct nvmet_req *req)
sector = le64_to_cpu(write_zeroes->slba) <<
(req->ns->blksize_shift - 9);
- nr_sector = (((sector_t)le16_to_cpu(write_zeroes->length)) <<
- (req->ns->blksize_shift - 9)) + 1;
+ nr_sector = (((sector_t)le16_to_cpu(write_zeroes->length) + 1) <<
+ (req->ns->blksize_shift - 9));
if (__blkdev_issue_zeroout(req->ns->bdev, sector, nr_sector,
GFP_KERNEL, &bio, 0))
diff --git a/drivers/nvme/target/loop.c b/drivers/nvme/target/loop.c
index 861d1509b22b..31fdfba556a8 100644
--- a/drivers/nvme/target/loop.c
+++ b/drivers/nvme/target/loop.c
@@ -71,7 +71,7 @@ static DEFINE_MUTEX(nvme_loop_ctrl_mutex);
static void nvme_loop_queue_response(struct nvmet_req *nvme_req);
static void nvme_loop_delete_ctrl(struct nvmet_ctrl *ctrl);
-static struct nvmet_fabrics_ops nvme_loop_ops;
+static const struct nvmet_fabrics_ops nvme_loop_ops;
static inline int nvme_loop_queue_idx(struct nvme_loop_queue *queue)
{
@@ -149,14 +149,6 @@ nvme_loop_timeout(struct request *rq, bool reserved)
return BLK_EH_HANDLED;
}
-static inline blk_status_t nvme_loop_is_ready(struct nvme_loop_queue *queue,
- struct request *rq)
-{
- if (unlikely(!test_bit(NVME_LOOP_Q_LIVE, &queue->flags)))
- return nvmf_check_init_req(&queue->ctrl->ctrl, rq);
- return BLK_STS_OK;
-}
-
static blk_status_t nvme_loop_queue_rq(struct blk_mq_hw_ctx *hctx,
const struct blk_mq_queue_data *bd)
{
@@ -166,7 +158,8 @@ static blk_status_t nvme_loop_queue_rq(struct blk_mq_hw_ctx *hctx,
struct nvme_loop_iod *iod = blk_mq_rq_to_pdu(req);
blk_status_t ret;
- ret = nvme_loop_is_ready(queue, req);
+ ret = nvmf_check_if_ready(&queue->ctrl->ctrl, req,
+ test_bit(NVME_LOOP_Q_LIVE, &queue->flags), true);
if (unlikely(ret))
return ret;
@@ -174,15 +167,12 @@ static blk_status_t nvme_loop_queue_rq(struct blk_mq_hw_ctx *hctx,
if (ret)
return ret;
+ blk_mq_start_request(req);
iod->cmd.common.flags |= NVME_CMD_SGL_METABUF;
iod->req.port = nvmet_loop_port;
if (!nvmet_req_init(&iod->req, &queue->nvme_cq,
- &queue->nvme_sq, &nvme_loop_ops)) {
- nvme_cleanup_cmd(req);
- blk_mq_start_request(req);
- nvme_loop_queue_response(&iod->req);
+ &queue->nvme_sq, &nvme_loop_ops))
return BLK_STS_OK;
- }
if (blk_rq_payload_bytes(req)) {
iod->sg_table.sgl = iod->first_sgl;
@@ -196,8 +186,6 @@ static blk_status_t nvme_loop_queue_rq(struct blk_mq_hw_ctx *hctx,
iod->req.transfer_len = blk_rq_payload_bytes(req);
}
- blk_mq_start_request(req);
-
schedule_work(&iod->work);
return BLK_STS_OK;
}
@@ -675,7 +663,7 @@ static void nvme_loop_remove_port(struct nvmet_port *port)
nvmet_loop_port = NULL;
}
-static struct nvmet_fabrics_ops nvme_loop_ops = {
+static const struct nvmet_fabrics_ops nvme_loop_ops = {
.owner = THIS_MODULE,
.type = NVMF_TRTYPE_LOOP,
.add_port = nvme_loop_add_port,
diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h
index 417f6c0331cc..15fd84ab21f8 100644
--- a/drivers/nvme/target/nvmet.h
+++ b/drivers/nvme/target/nvmet.h
@@ -130,7 +130,7 @@ struct nvmet_ctrl {
struct delayed_work ka_work;
struct work_struct fatal_err_work;
- struct nvmet_fabrics_ops *ops;
+ const struct nvmet_fabrics_ops *ops;
char subsysnqn[NVMF_NQN_FIELD_LEN];
char hostnqn[NVMF_NQN_FIELD_LEN];
@@ -209,6 +209,8 @@ struct nvmet_fabrics_ops {
int (*add_port)(struct nvmet_port *port);
void (*remove_port)(struct nvmet_port *port);
void (*delete_ctrl)(struct nvmet_ctrl *ctrl);
+ void (*disc_traddr)(struct nvmet_req *req,
+ struct nvmet_port *port, char *traddr);
};
#define NVMET_MAX_INLINE_BIOVEC 8
@@ -231,7 +233,7 @@ struct nvmet_req {
struct nvmet_port *port;
void (*execute)(struct nvmet_req *req);
- struct nvmet_fabrics_ops *ops;
+ const struct nvmet_fabrics_ops *ops;
};
static inline void nvmet_set_status(struct nvmet_req *req, u16 status)
@@ -267,7 +269,7 @@ u16 nvmet_parse_discovery_cmd(struct nvmet_req *req);
u16 nvmet_parse_fabrics_cmd(struct nvmet_req *req);
bool nvmet_req_init(struct nvmet_req *req, struct nvmet_cq *cq,
- struct nvmet_sq *sq, struct nvmet_fabrics_ops *ops);
+ struct nvmet_sq *sq, const struct nvmet_fabrics_ops *ops);
void nvmet_req_uninit(struct nvmet_req *req);
void nvmet_req_execute(struct nvmet_req *req);
void nvmet_req_complete(struct nvmet_req *req, u16 status);
@@ -301,8 +303,8 @@ void nvmet_ns_disable(struct nvmet_ns *ns);
struct nvmet_ns *nvmet_ns_alloc(struct nvmet_subsys *subsys, u32 nsid);
void nvmet_ns_free(struct nvmet_ns *ns);
-int nvmet_register_transport(struct nvmet_fabrics_ops *ops);
-void nvmet_unregister_transport(struct nvmet_fabrics_ops *ops);
+int nvmet_register_transport(const struct nvmet_fabrics_ops *ops);
+void nvmet_unregister_transport(const struct nvmet_fabrics_ops *ops);
int nvmet_enable_port(struct nvmet_port *port);
void nvmet_disable_port(struct nvmet_port *port);
diff --git a/drivers/nvme/target/rdma.c b/drivers/nvme/target/rdma.c
index 978e169c11bf..52e0c5d579a7 100644
--- a/drivers/nvme/target/rdma.c
+++ b/drivers/nvme/target/rdma.c
@@ -77,7 +77,6 @@ enum nvmet_rdma_queue_state {
NVMET_RDMA_Q_CONNECTING,
NVMET_RDMA_Q_LIVE,
NVMET_RDMA_Q_DISCONNECTING,
- NVMET_RDMA_IN_DEVICE_REMOVAL,
};
struct nvmet_rdma_queue {
@@ -137,7 +136,7 @@ static void nvmet_rdma_read_data_done(struct ib_cq *cq, struct ib_wc *wc);
static void nvmet_rdma_qp_event(struct ib_event *event, void *priv);
static void nvmet_rdma_queue_disconnect(struct nvmet_rdma_queue *queue);
-static struct nvmet_fabrics_ops nvmet_rdma_ops;
+static const struct nvmet_fabrics_ops nvmet_rdma_ops;
/* XXX: really should move to a generic header sooner or later.. */
static inline u32 get_unaligned_le24(const u8 *p)
@@ -914,8 +913,11 @@ err_destroy_cq:
static void nvmet_rdma_destroy_queue_ib(struct nvmet_rdma_queue *queue)
{
- ib_drain_qp(queue->cm_id->qp);
- rdma_destroy_qp(queue->cm_id);
+ struct ib_qp *qp = queue->cm_id->qp;
+
+ ib_drain_qp(qp);
+ rdma_destroy_id(queue->cm_id);
+ ib_destroy_qp(qp);
ib_free_cq(queue->cq);
}
@@ -940,15 +942,10 @@ static void nvmet_rdma_release_queue_work(struct work_struct *w)
{
struct nvmet_rdma_queue *queue =
container_of(w, struct nvmet_rdma_queue, release_work);
- struct rdma_cm_id *cm_id = queue->cm_id;
struct nvmet_rdma_device *dev = queue->dev;
- enum nvmet_rdma_queue_state state = queue->state;
nvmet_rdma_free_queue(queue);
- if (state != NVMET_RDMA_IN_DEVICE_REMOVAL)
- rdma_destroy_id(cm_id);
-
kref_put(&dev->ref, nvmet_rdma_free_dev);
}
@@ -1153,8 +1150,11 @@ static int nvmet_rdma_queue_connect(struct rdma_cm_id *cm_id,
}
ret = nvmet_rdma_cm_accept(cm_id, queue, &event->param.conn);
- if (ret)
- goto release_queue;
+ if (ret) {
+ schedule_work(&queue->release_work);
+ /* Destroying rdma_cm id is not needed here */
+ return 0;
+ }
mutex_lock(&nvmet_rdma_queue_mutex);
list_add_tail(&queue->queue_list, &nvmet_rdma_queue_list);
@@ -1162,8 +1162,6 @@ static int nvmet_rdma_queue_connect(struct rdma_cm_id *cm_id,
return 0;
-release_queue:
- nvmet_rdma_free_queue(queue);
put_device:
kref_put(&ndev->ref, nvmet_rdma_free_dev);
@@ -1209,7 +1207,6 @@ static void __nvmet_rdma_queue_disconnect(struct nvmet_rdma_queue *queue)
case NVMET_RDMA_Q_CONNECTING:
case NVMET_RDMA_Q_LIVE:
queue->state = NVMET_RDMA_Q_DISCONNECTING;
- case NVMET_RDMA_IN_DEVICE_REMOVAL:
disconnect = true;
break;
case NVMET_RDMA_Q_DISCONNECTING:
@@ -1322,13 +1319,7 @@ static int nvmet_rdma_cm_handler(struct rdma_cm_id *cm_id,
case RDMA_CM_EVENT_ADDR_CHANGE:
case RDMA_CM_EVENT_DISCONNECTED:
case RDMA_CM_EVENT_TIMEWAIT_EXIT:
- /*
- * We might end up here when we already freed the qp
- * which means queue release sequence is in progress,
- * so don't get in the way...
- */
- if (queue)
- nvmet_rdma_queue_disconnect(queue);
+ nvmet_rdma_queue_disconnect(queue);
break;
case RDMA_CM_EVENT_DEVICE_REMOVAL:
ret = nvmet_rdma_device_removal(cm_id, queue);
@@ -1445,7 +1436,24 @@ static void nvmet_rdma_remove_port(struct nvmet_port *port)
rdma_destroy_id(cm_id);
}
-static struct nvmet_fabrics_ops nvmet_rdma_ops = {
+static void nvmet_rdma_disc_port_addr(struct nvmet_req *req,
+ struct nvmet_port *port, char *traddr)
+{
+ struct rdma_cm_id *cm_id = port->priv;
+
+ if (inet_addr_is_any((struct sockaddr *)&cm_id->route.addr.src_addr)) {
+ struct nvmet_rdma_rsp *rsp =
+ container_of(req, struct nvmet_rdma_rsp, req);
+ struct rdma_cm_id *req_cm_id = rsp->queue->cm_id;
+ struct sockaddr *addr = (void *)&req_cm_id->route.addr.src_addr;
+
+ sprintf(traddr, "%pISc", addr);
+ } else {
+ memcpy(traddr, port->disc_addr.traddr, NVMF_TRADDR_SIZE);
+ }
+}
+
+static const struct nvmet_fabrics_ops nvmet_rdma_ops = {
.owner = THIS_MODULE,
.type = NVMF_TRTYPE_RDMA,
.sqe_inline_size = NVMET_RDMA_INLINE_DATA_SIZE,
@@ -1455,13 +1463,31 @@ static struct nvmet_fabrics_ops nvmet_rdma_ops = {
.remove_port = nvmet_rdma_remove_port,
.queue_response = nvmet_rdma_queue_response,
.delete_ctrl = nvmet_rdma_delete_ctrl,
+ .disc_traddr = nvmet_rdma_disc_port_addr,
};
static void nvmet_rdma_remove_one(struct ib_device *ib_device, void *client_data)
{
struct nvmet_rdma_queue *queue, *tmp;
+ struct nvmet_rdma_device *ndev;
+ bool found = false;
- /* Device is being removed, delete all queues using this device */
+ mutex_lock(&device_list_mutex);
+ list_for_each_entry(ndev, &device_list, entry) {
+ if (ndev->device == ib_device) {
+ found = true;
+ break;
+ }
+ }
+ mutex_unlock(&device_list_mutex);
+
+ if (!found)
+ return;
+
+ /*
+ * IB Device that is used by nvmet controllers is being removed,
+ * delete all queues using this device.
+ */
mutex_lock(&nvmet_rdma_queue_mutex);
list_for_each_entry_safe(queue, tmp, &nvmet_rdma_queue_list,
queue_list) {
diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
index ff505af064ba..1090924efdb1 100644
--- a/drivers/nvmem/Kconfig
+++ b/drivers/nvmem/Kconfig
@@ -1,5 +1,5 @@
menuconfig NVMEM
- tristate "NVMEM Support"
+ bool "NVMEM Support"
help
Support for NVMEM(Non Volatile Memory) devices like EEPROM, EFUSES...
@@ -167,10 +167,10 @@ config MESON_MX_EFUSE
config NVMEM_SNVS_LPGPR
tristate "Support for Low Power General Purpose Register"
- depends on SOC_IMX6 || COMPILE_TEST
+ depends on SOC_IMX6 || SOC_IMX7D || COMPILE_TEST
help
This is a driver for Low Power General Purpose Register (LPGPR) available on
- i.MX6 SoCs in Secure Non-Volatile Storage (SNVS) of this chip.
+ i.MX6 and i.MX7 SoCs in Secure Non-Volatile Storage (SNVS) of this chip.
This driver can also be built as a module. If so, the module
will be called nvmem-snvs-lpgpr.
diff --git a/drivers/nvmem/bcm-ocotp.c b/drivers/nvmem/bcm-ocotp.c
index 5e9e324427f9..4159b3f41d79 100644
--- a/drivers/nvmem/bcm-ocotp.c
+++ b/drivers/nvmem/bcm-ocotp.c
@@ -262,8 +262,7 @@ static int bcm_otpc_probe(struct platform_device *pdev)
else if (of_device_is_compatible(dev->of_node, "brcm,ocotp-v2"))
priv->map = &otp_map_v2;
else {
- dev_err(&pdev->dev,
- "%s otpc config map not defined\n", __func__);
+ dev_err(dev, "%s otpc config map not defined\n", __func__);
return -EINVAL;
}
@@ -302,27 +301,17 @@ static int bcm_otpc_probe(struct platform_device *pdev)
priv->config = &bcm_otpc_nvmem_config;
- nvmem = nvmem_register(&bcm_otpc_nvmem_config);
+ nvmem = devm_nvmem_register(dev, &bcm_otpc_nvmem_config);
if (IS_ERR(nvmem)) {
dev_err(dev, "error registering nvmem config\n");
return PTR_ERR(nvmem);
}
- platform_set_drvdata(pdev, nvmem);
-
return 0;
}
-static int bcm_otpc_remove(struct platform_device *pdev)
-{
- struct nvmem_device *nvmem = platform_get_drvdata(pdev);
-
- return nvmem_unregister(nvmem);
-}
-
static struct platform_driver bcm_otpc_driver = {
.probe = bcm_otpc_probe,
- .remove = bcm_otpc_remove,
.driver = {
.name = "brcm-otpc",
.of_match_table = bcm_otpc_dt_ids,
diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index 35a3dbeea324..b05aa8e81303 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -473,9 +473,14 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
nvmem->reg_read = config->reg_read;
nvmem->reg_write = config->reg_write;
nvmem->dev.of_node = config->dev->of_node;
- dev_set_name(&nvmem->dev, "%s%d",
- config->name ? : "nvmem",
- config->name ? config->id : nvmem->id);
+
+ if (config->id == -1 && config->name) {
+ dev_set_name(&nvmem->dev, "%s", config->name);
+ } else {
+ dev_set_name(&nvmem->dev, "%s%d",
+ config->name ? : "nvmem",
+ config->name ? config->id : nvmem->id);
+ }
nvmem->read_only = device_property_present(config->dev, "read-only") |
config->read_only;
@@ -544,6 +549,65 @@ int nvmem_unregister(struct nvmem_device *nvmem)
}
EXPORT_SYMBOL_GPL(nvmem_unregister);
+static void devm_nvmem_release(struct device *dev, void *res)
+{
+ WARN_ON(nvmem_unregister(*(struct nvmem_device **)res));
+}
+
+/**
+ * devm_nvmem_register() - Register a managed nvmem device for given
+ * nvmem_config.
+ * Also creates an binary entry in /sys/bus/nvmem/devices/dev-name/nvmem
+ *
+ * @config: nvmem device configuration with which nvmem device is created.
+ *
+ * Return: Will be an ERR_PTR() on error or a valid pointer to nvmem_device
+ * on success.
+ */
+struct nvmem_device *devm_nvmem_register(struct device *dev,
+ const struct nvmem_config *config)
+{
+ struct nvmem_device **ptr, *nvmem;
+
+ ptr = devres_alloc(devm_nvmem_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ nvmem = nvmem_register(config);
+
+ if (!IS_ERR(nvmem)) {
+ *ptr = nvmem;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return nvmem;
+}
+EXPORT_SYMBOL_GPL(devm_nvmem_register);
+
+static int devm_nvmem_match(struct device *dev, void *res, void *data)
+{
+ struct nvmem_device **r = res;
+
+ return *r == data;
+}
+
+/**
+ * devm_nvmem_unregister() - Unregister previously registered managed nvmem
+ * device.
+ *
+ * @nvmem: Pointer to previously registered nvmem device.
+ *
+ * Return: Will be an negative on error or a zero on success.
+ */
+int devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem)
+{
+ return devres_release(dev, devm_nvmem_release, devm_nvmem_match, nvmem);
+}
+EXPORT_SYMBOL(devm_nvmem_unregister);
+
+
static struct nvmem_device *__nvmem_device_get(struct device_node *np,
struct nvmem_cell **cellp,
const char *cell_id)
diff --git a/drivers/nvmem/imx-iim.c b/drivers/nvmem/imx-iim.c
index 52cfe91d9762..6651e4cdc002 100644
--- a/drivers/nvmem/imx-iim.c
+++ b/drivers/nvmem/imx-iim.c
@@ -125,7 +125,7 @@ static int imx_iim_probe(struct platform_device *pdev)
drvdata = of_id->data;
- iim->clk = devm_clk_get(&pdev->dev, NULL);
+ iim->clk = devm_clk_get(dev, NULL);
if (IS_ERR(iim->clk))
return PTR_ERR(iim->clk);
@@ -138,25 +138,13 @@ static int imx_iim_probe(struct platform_device *pdev)
cfg.size = drvdata->nregs;
cfg.priv = iim;
- nvmem = nvmem_register(&cfg);
- if (IS_ERR(nvmem))
- return PTR_ERR(nvmem);
+ nvmem = devm_nvmem_register(dev, &cfg);
- platform_set_drvdata(pdev, nvmem);
-
- return 0;
-}
-
-static int imx_iim_remove(struct platform_device *pdev)
-{
- struct nvmem_device *nvmem = platform_get_drvdata(pdev);
-
- return nvmem_unregister(nvmem);
+ return PTR_ERR_OR_ZERO(nvmem);
}
static struct platform_driver imx_iim_driver = {
.probe = imx_iim_probe,
- .remove = imx_iim_remove,
.driver = {
.name = "imx-iim",
.of_match_table = imx_iim_dt_ids,
diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c
index d7ba351a70c9..60816c856dd6 100644
--- a/drivers/nvmem/imx-ocotp.c
+++ b/drivers/nvmem/imx-ocotp.c
@@ -439,7 +439,6 @@ MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids);
static int imx_ocotp_probe(struct platform_device *pdev)
{
- const struct of_device_id *of_id;
struct device *dev = &pdev->dev;
struct resource *res;
struct ocotp_priv *priv;
@@ -460,32 +459,19 @@ static int imx_ocotp_probe(struct platform_device *pdev)
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
- of_id = of_match_device(imx_ocotp_dt_ids, dev);
priv->params = of_device_get_match_data(&pdev->dev);
imx_ocotp_nvmem_config.size = 4 * priv->params->nregs;
imx_ocotp_nvmem_config.dev = dev;
imx_ocotp_nvmem_config.priv = priv;
priv->config = &imx_ocotp_nvmem_config;
- nvmem = nvmem_register(&imx_ocotp_nvmem_config);
+ nvmem = devm_nvmem_register(dev, &imx_ocotp_nvmem_config);
- if (IS_ERR(nvmem))
- return PTR_ERR(nvmem);
- platform_set_drvdata(pdev, nvmem);
-
- return 0;
-}
-
-static int imx_ocotp_remove(struct platform_device *pdev)
-{
- struct nvmem_device *nvmem = platform_get_drvdata(pdev);
-
- return nvmem_unregister(nvmem);
+ return PTR_ERR_OR_ZERO(nvmem);
}
static struct platform_driver imx_ocotp_driver = {
.probe = imx_ocotp_probe,
- .remove = imx_ocotp_remove,
.driver = {
.name = "imx_ocotp",
.of_match_table = imx_ocotp_dt_ids,
diff --git a/drivers/nvmem/lpc18xx_otp.c b/drivers/nvmem/lpc18xx_otp.c
index 95268db155e9..549b5298ac4c 100644
--- a/drivers/nvmem/lpc18xx_otp.c
+++ b/drivers/nvmem/lpc18xx_otp.c
@@ -86,20 +86,9 @@ static int lpc18xx_otp_probe(struct platform_device *pdev)
lpc18xx_otp_nvmem_config.dev = &pdev->dev;
lpc18xx_otp_nvmem_config.priv = otp;
- nvmem = nvmem_register(&lpc18xx_otp_nvmem_config);
- if (IS_ERR(nvmem))
- return PTR_ERR(nvmem);
+ nvmem = devm_nvmem_register(&pdev->dev, &lpc18xx_otp_nvmem_config);
- platform_set_drvdata(pdev, nvmem);
-
- return 0;
-}
-
-static int lpc18xx_otp_remove(struct platform_device *pdev)
-{
- struct nvmem_device *nvmem = platform_get_drvdata(pdev);
-
- return nvmem_unregister(nvmem);
+ return PTR_ERR_OR_ZERO(nvmem);
}
static const struct of_device_id lpc18xx_otp_dt_ids[] = {
@@ -110,7 +99,6 @@ MODULE_DEVICE_TABLE(of, lpc18xx_otp_dt_ids);
static struct platform_driver lpc18xx_otp_driver = {
.probe = lpc18xx_otp_probe,
- .remove = lpc18xx_otp_remove,
.driver = {
.name = "lpc18xx_otp",
.of_match_table = lpc18xx_otp_dt_ids,
diff --git a/drivers/nvmem/meson-efuse.c b/drivers/nvmem/meson-efuse.c
index a43c68f90937..71823d1403c5 100644
--- a/drivers/nvmem/meson-efuse.c
+++ b/drivers/nvmem/meson-efuse.c
@@ -60,25 +60,13 @@ static int meson_efuse_probe(struct platform_device *pdev)
econfig.reg_read = meson_efuse_read;
econfig.size = size;
- nvmem = nvmem_register(&econfig);
- if (IS_ERR(nvmem))
- return PTR_ERR(nvmem);
+ nvmem = devm_nvmem_register(&pdev->dev, &econfig);
- platform_set_drvdata(pdev, nvmem);
-
- return 0;
-}
-
-static int meson_efuse_remove(struct platform_device *pdev)
-{
- struct nvmem_device *nvmem = platform_get_drvdata(pdev);
-
- return nvmem_unregister(nvmem);
+ return PTR_ERR_OR_ZERO(nvmem);
}
static struct platform_driver meson_efuse_driver = {
.probe = meson_efuse_probe,
- .remove = meson_efuse_remove,
.driver = {
.name = "meson-efuse",
.of_match_table = meson_efuse_match,
diff --git a/drivers/nvmem/meson-mx-efuse.c b/drivers/nvmem/meson-mx-efuse.c
index 41d3a3c1104e..a085563e39e3 100644
--- a/drivers/nvmem/meson-mx-efuse.c
+++ b/drivers/nvmem/meson-mx-efuse.c
@@ -233,25 +233,13 @@ static int meson_mx_efuse_probe(struct platform_device *pdev)
return PTR_ERR(efuse->core_clk);
}
- efuse->nvmem = nvmem_register(&efuse->config);
- if (IS_ERR(efuse->nvmem))
- return PTR_ERR(efuse->nvmem);
+ efuse->nvmem = devm_nvmem_register(&pdev->dev, &efuse->config);
- platform_set_drvdata(pdev, efuse);
-
- return 0;
-}
-
-static int meson_mx_efuse_remove(struct platform_device *pdev)
-{
- struct meson_mx_efuse *efuse = platform_get_drvdata(pdev);
-
- return nvmem_unregister(efuse->nvmem);
+ return PTR_ERR_OR_ZERO(efuse->nvmem);
}
static struct platform_driver meson_mx_efuse_driver = {
.probe = meson_mx_efuse_probe,
- .remove = meson_mx_efuse_remove,
.driver = {
.name = "meson-mx-efuse",
.of_match_table = meson_mx_efuse_match,
diff --git a/drivers/nvmem/mtk-efuse.c b/drivers/nvmem/mtk-efuse.c
index 9ee3479cfc7b..e66adf17a747 100644
--- a/drivers/nvmem/mtk-efuse.c
+++ b/drivers/nvmem/mtk-efuse.c
@@ -72,20 +72,9 @@ static int mtk_efuse_probe(struct platform_device *pdev)
econfig.size = resource_size(res);
econfig.priv = priv;
econfig.dev = dev;
- nvmem = nvmem_register(&econfig);
- if (IS_ERR(nvmem))
- return PTR_ERR(nvmem);
+ nvmem = devm_nvmem_register(dev, &econfig);
- platform_set_drvdata(pdev, nvmem);
-
- return 0;
-}
-
-static int mtk_efuse_remove(struct platform_device *pdev)
-{
- struct nvmem_device *nvmem = platform_get_drvdata(pdev);
-
- return nvmem_unregister(nvmem);
+ return PTR_ERR_OR_ZERO(nvmem);
}
static const struct of_device_id mtk_efuse_of_match[] = {
@@ -97,7 +86,6 @@ MODULE_DEVICE_TABLE(of, mtk_efuse_of_match);
static struct platform_driver mtk_efuse_driver = {
.probe = mtk_efuse_probe,
- .remove = mtk_efuse_remove,
.driver = {
.name = "mediatek,efuse",
.of_match_table = mtk_efuse_of_match,
diff --git a/drivers/nvmem/qfprom.c b/drivers/nvmem/qfprom.c
index cb3b48b47d64..4f650baad983 100644
--- a/drivers/nvmem/qfprom.c
+++ b/drivers/nvmem/qfprom.c
@@ -47,13 +47,6 @@ static int qfprom_reg_write(void *context,
return 0;
}
-static int qfprom_remove(struct platform_device *pdev)
-{
- struct nvmem_device *nvmem = platform_get_drvdata(pdev);
-
- return nvmem_unregister(nvmem);
-}
-
static struct nvmem_config econfig = {
.name = "qfprom",
.stride = 1,
@@ -82,13 +75,9 @@ static int qfprom_probe(struct platform_device *pdev)
econfig.dev = dev;
econfig.priv = priv;
- nvmem = nvmem_register(&econfig);
- if (IS_ERR(nvmem))
- return PTR_ERR(nvmem);
-
- platform_set_drvdata(pdev, nvmem);
+ nvmem = devm_nvmem_register(dev, &econfig);
- return 0;
+ return PTR_ERR_OR_ZERO(nvmem);
}
static const struct of_device_id qfprom_of_match[] = {
@@ -99,7 +88,6 @@ MODULE_DEVICE_TABLE(of, qfprom_of_match);
static struct platform_driver qfprom_driver = {
.probe = qfprom_probe,
- .remove = qfprom_remove,
.driver = {
.name = "qcom,qfprom",
.of_match_table = qfprom_of_match,
diff --git a/drivers/nvmem/rockchip-efuse.c b/drivers/nvmem/rockchip-efuse.c
index f13a8335f364..b3b0b648be62 100644
--- a/drivers/nvmem/rockchip-efuse.c
+++ b/drivers/nvmem/rockchip-efuse.c
@@ -259,55 +259,43 @@ static int rockchip_efuse_probe(struct platform_device *pdev)
struct resource *res;
struct nvmem_device *nvmem;
struct rockchip_efuse_chip *efuse;
- const struct of_device_id *match;
+ const void *data;
struct device *dev = &pdev->dev;
- match = of_match_device(dev->driver->of_match_table, dev);
- if (!match || !match->data) {
+ data = of_device_get_match_data(dev);
+ if (!data) {
dev_err(dev, "failed to get match data\n");
return -EINVAL;
}
- efuse = devm_kzalloc(&pdev->dev, sizeof(struct rockchip_efuse_chip),
+ efuse = devm_kzalloc(dev, sizeof(struct rockchip_efuse_chip),
GFP_KERNEL);
if (!efuse)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- efuse->base = devm_ioremap_resource(&pdev->dev, res);
+ efuse->base = devm_ioremap_resource(dev, res);
if (IS_ERR(efuse->base))
return PTR_ERR(efuse->base);
- efuse->clk = devm_clk_get(&pdev->dev, "pclk_efuse");
+ efuse->clk = devm_clk_get(dev, "pclk_efuse");
if (IS_ERR(efuse->clk))
return PTR_ERR(efuse->clk);
- efuse->dev = &pdev->dev;
+ efuse->dev = dev;
if (of_property_read_u32(dev->of_node, "rockchip,efuse-size",
&econfig.size))
econfig.size = resource_size(res);
- econfig.reg_read = match->data;
+ econfig.reg_read = data;
econfig.priv = efuse;
econfig.dev = efuse->dev;
- nvmem = nvmem_register(&econfig);
- if (IS_ERR(nvmem))
- return PTR_ERR(nvmem);
+ nvmem = devm_nvmem_register(dev, &econfig);
- platform_set_drvdata(pdev, nvmem);
-
- return 0;
-}
-
-static int rockchip_efuse_remove(struct platform_device *pdev)
-{
- struct nvmem_device *nvmem = platform_get_drvdata(pdev);
-
- return nvmem_unregister(nvmem);
+ return PTR_ERR_OR_ZERO(nvmem);
}
static struct platform_driver rockchip_efuse_driver = {
.probe = rockchip_efuse_probe,
- .remove = rockchip_efuse_remove,
.driver = {
.name = "rockchip-efuse",
.of_match_table = rockchip_efuse_match,
diff --git a/drivers/nvmem/snvs_lpgpr.c b/drivers/nvmem/snvs_lpgpr.c
index e5c2a4a17f03..c050a23a9f2b 100644
--- a/drivers/nvmem/snvs_lpgpr.c
+++ b/drivers/nvmem/snvs_lpgpr.c
@@ -14,15 +14,21 @@
#include <linux/regmap.h>
#define IMX6Q_SNVS_HPLR 0x00
-#define IMX6Q_GPR_SL BIT(5)
#define IMX6Q_SNVS_LPLR 0x34
-#define IMX6Q_GPR_HL BIT(5)
#define IMX6Q_SNVS_LPGPR 0x68
+#define IMX7D_SNVS_HPLR 0x00
+#define IMX7D_SNVS_LPLR 0x34
+#define IMX7D_SNVS_LPGPR 0x90
+
+#define IMX_GPR_SL BIT(5)
+#define IMX_GPR_HL BIT(5)
+
struct snvs_lpgpr_cfg {
int offset;
int offset_hplr;
int offset_lplr;
+ int size;
};
struct snvs_lpgpr_priv {
@@ -36,6 +42,14 @@ static const struct snvs_lpgpr_cfg snvs_lpgpr_cfg_imx6q = {
.offset = IMX6Q_SNVS_LPGPR,
.offset_hplr = IMX6Q_SNVS_HPLR,
.offset_lplr = IMX6Q_SNVS_LPLR,
+ .size = 4,
+};
+
+static const struct snvs_lpgpr_cfg snvs_lpgpr_cfg_imx7d = {
+ .offset = IMX7D_SNVS_LPGPR,
+ .offset_hplr = IMX7D_SNVS_HPLR,
+ .offset_lplr = IMX7D_SNVS_LPLR,
+ .size = 16,
};
static int snvs_lpgpr_write(void *context, unsigned int offset, void *val,
@@ -50,14 +64,14 @@ static int snvs_lpgpr_write(void *context, unsigned int offset, void *val,
if (ret < 0)
return ret;
- if (lock_reg & IMX6Q_GPR_SL)
+ if (lock_reg & IMX_GPR_SL)
return -EPERM;
ret = regmap_read(priv->regmap, dcfg->offset_lplr, &lock_reg);
if (ret < 0)
return ret;
- if (lock_reg & IMX6Q_GPR_HL)
+ if (lock_reg & IMX_GPR_HL)
return -EPERM;
return regmap_bulk_write(priv->regmap, dcfg->offset + offset, val,
@@ -110,40 +124,29 @@ static int snvs_lpgpr_probe(struct platform_device *pdev)
cfg->priv = priv;
cfg->name = dev_name(dev);
cfg->dev = dev;
- cfg->stride = 4,
- cfg->word_size = 4,
- cfg->size = 4,
- cfg->owner = THIS_MODULE,
- cfg->reg_read = snvs_lpgpr_read,
- cfg->reg_write = snvs_lpgpr_write,
-
- nvmem = nvmem_register(cfg);
- if (IS_ERR(nvmem))
- return PTR_ERR(nvmem);
+ cfg->stride = 4;
+ cfg->word_size = 4;
+ cfg->size = dcfg->size,
+ cfg->owner = THIS_MODULE;
+ cfg->reg_read = snvs_lpgpr_read;
+ cfg->reg_write = snvs_lpgpr_write;
- platform_set_drvdata(pdev, nvmem);
-
- return 0;
-}
-
-static int snvs_lpgpr_remove(struct platform_device *pdev)
-{
- struct nvmem_device *nvmem = platform_get_drvdata(pdev);
+ nvmem = devm_nvmem_register(dev, cfg);
- return nvmem_unregister(nvmem);
+ return PTR_ERR_OR_ZERO(nvmem);
}
static const struct of_device_id snvs_lpgpr_dt_ids[] = {
{ .compatible = "fsl,imx6q-snvs-lpgpr", .data = &snvs_lpgpr_cfg_imx6q },
{ .compatible = "fsl,imx6ul-snvs-lpgpr",
.data = &snvs_lpgpr_cfg_imx6q },
+ { .compatible = "fsl,imx7d-snvs-lpgpr", .data = &snvs_lpgpr_cfg_imx7d },
{ },
};
MODULE_DEVICE_TABLE(of, snvs_lpgpr_dt_ids);
static struct platform_driver snvs_lpgpr_driver = {
.probe = snvs_lpgpr_probe,
- .remove = snvs_lpgpr_remove,
.driver = {
.name = "snvs_lpgpr",
.of_match_table = snvs_lpgpr_dt_ids,
@@ -152,5 +155,5 @@ static struct platform_driver snvs_lpgpr_driver = {
module_platform_driver(snvs_lpgpr_driver);
MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>");
-MODULE_DESCRIPTION("Low Power General Purpose Register in i.MX6 Secure Non-Volatile Storage");
+MODULE_DESCRIPTION("Low Power General Purpose Register in i.MX6 and i.MX7 Secure Non-Volatile Storage");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/nvmem/sunxi_sid.c b/drivers/nvmem/sunxi_sid.c
index 99bd54d85fcb..26bb637afe92 100644
--- a/drivers/nvmem/sunxi_sid.c
+++ b/drivers/nvmem/sunxi_sid.c
@@ -85,13 +85,14 @@ static int sunxi_sid_read(void *context, unsigned int offset,
}
static int sun8i_sid_register_readout(const struct sunxi_sid *sid,
- const unsigned int word)
+ const unsigned int offset,
+ u32 *out)
{
u32 reg_val;
int ret;
/* Set word, lock access, and set read command */
- reg_val = (word & SUN8I_SID_OFFSET_MASK)
+ reg_val = (offset & SUN8I_SID_OFFSET_MASK)
<< SUN8I_SID_OFFSET_SHIFT;
reg_val |= SUN8I_SID_OP_LOCK | SUN8I_SID_READ;
writel(reg_val, sid->base + SUN8I_SID_PRCTL);
@@ -101,7 +102,49 @@ static int sun8i_sid_register_readout(const struct sunxi_sid *sid,
if (ret)
return ret;
+ if (out)
+ *out = readl(sid->base + SUN8I_SID_RDKEY);
+
writel(0, sid->base + SUN8I_SID_PRCTL);
+
+ return 0;
+}
+
+/*
+ * On Allwinner H3, the value on the 0x200 offset of the SID controller seems
+ * to be not reliable at all.
+ * Read by the registers instead.
+ */
+static int sun8i_sid_read_byte_by_reg(const struct sunxi_sid *sid,
+ const unsigned int offset,
+ u8 *out)
+{
+ u32 word;
+ int ret;
+
+ ret = sun8i_sid_register_readout(sid, offset & ~0x03, &word);
+
+ if (ret)
+ return ret;
+
+ *out = (word >> ((offset & 0x3) * 8)) & 0xff;
+
+ return 0;
+}
+
+static int sun8i_sid_read_by_reg(void *context, unsigned int offset,
+ void *val, size_t bytes)
+{
+ struct sunxi_sid *sid = context;
+ u8 *buf = val;
+ int ret;
+
+ while (bytes--) {
+ ret = sun8i_sid_read_byte_by_reg(sid, offset++, buf++);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
@@ -131,26 +174,12 @@ static int sunxi_sid_probe(struct platform_device *pdev)
size = cfg->size;
- if (cfg->need_register_readout) {
- /*
- * H3's SID controller have a bug that the value at 0x200
- * offset is not the correct value when the hardware is reseted.
- * However, after doing a register-based read operation, the
- * value become right.
- * Do a full read operation here, but ignore its value
- * (as it's more fast to read by direct MMIO value than
- * with registers)
- */
- for (i = 0; i < (size >> 2); i++) {
- ret = sun8i_sid_register_readout(sid, i);
- if (ret)
- return ret;
- }
- }
-
econfig.size = size;
econfig.dev = dev;
- econfig.reg_read = sunxi_sid_read;
+ if (cfg->need_register_readout)
+ econfig.reg_read = sun8i_sid_read_by_reg;
+ else
+ econfig.reg_read = sunxi_sid_read;
econfig.priv = sid;
nvmem = nvmem_register(&econfig);
if (IS_ERR(nvmem))
@@ -163,7 +192,7 @@ static int sunxi_sid_probe(struct platform_device *pdev)
}
for (i = 0; i < size; i++)
- randomness[i] = sunxi_sid_read_byte(sid, i);
+ econfig.reg_read(sid, i, &randomness[i], 1);
add_device_randomness(randomness, size);
kfree(randomness);
diff --git a/drivers/nvmem/uniphier-efuse.c b/drivers/nvmem/uniphier-efuse.c
index be11880a1358..271f0b2ff86a 100644
--- a/drivers/nvmem/uniphier-efuse.c
+++ b/drivers/nvmem/uniphier-efuse.c
@@ -60,20 +60,9 @@ static int uniphier_efuse_probe(struct platform_device *pdev)
econfig.size = resource_size(res);
econfig.priv = priv;
econfig.dev = dev;
- nvmem = nvmem_register(&econfig);
- if (IS_ERR(nvmem))
- return PTR_ERR(nvmem);
+ nvmem = devm_nvmem_register(dev, &econfig);
- platform_set_drvdata(pdev, nvmem);
-
- return 0;
-}
-
-static int uniphier_efuse_remove(struct platform_device *pdev)
-{
- struct nvmem_device *nvmem = platform_get_drvdata(pdev);
-
- return nvmem_unregister(nvmem);
+ return PTR_ERR_OR_ZERO(nvmem);
}
static const struct of_device_id uniphier_efuse_of_match[] = {
@@ -84,7 +73,6 @@ MODULE_DEVICE_TABLE(of, uniphier_efuse_of_match);
static struct platform_driver uniphier_efuse_driver = {
.probe = uniphier_efuse_probe,
- .remove = uniphier_efuse_remove,
.driver = {
.name = "uniphier-efuse",
.of_match_table = uniphier_efuse_of_match,
diff --git a/drivers/nvmem/vf610-ocotp.c b/drivers/nvmem/vf610-ocotp.c
index 5ae9e002f195..4662309489db 100644
--- a/drivers/nvmem/vf610-ocotp.c
+++ b/drivers/nvmem/vf610-ocotp.c
@@ -217,21 +217,13 @@ static const struct of_device_id ocotp_of_match[] = {
};
MODULE_DEVICE_TABLE(of, ocotp_of_match);
-static int vf610_ocotp_remove(struct platform_device *pdev)
-{
- struct vf610_ocotp *ocotp_dev = platform_get_drvdata(pdev);
-
- return nvmem_unregister(ocotp_dev->nvmem);
-}
-
static int vf610_ocotp_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res;
struct vf610_ocotp *ocotp_dev;
- ocotp_dev = devm_kzalloc(&pdev->dev,
- sizeof(struct vf610_ocotp), GFP_KERNEL);
+ ocotp_dev = devm_kzalloc(dev, sizeof(struct vf610_ocotp), GFP_KERNEL);
if (!ocotp_dev)
return -ENOMEM;
@@ -246,26 +238,20 @@ static int vf610_ocotp_probe(struct platform_device *pdev)
PTR_ERR(ocotp_dev->clk));
return PTR_ERR(ocotp_dev->clk);
}
+ ocotp_dev->dev = dev;
+ ocotp_dev->timing = vf610_ocotp_calculate_timing(ocotp_dev);
ocotp_config.size = resource_size(res);
ocotp_config.priv = ocotp_dev;
ocotp_config.dev = dev;
- ocotp_dev->nvmem = nvmem_register(&ocotp_config);
- if (IS_ERR(ocotp_dev->nvmem))
- return PTR_ERR(ocotp_dev->nvmem);
+ ocotp_dev->nvmem = devm_nvmem_register(dev, &ocotp_config);
- ocotp_dev->dev = dev;
- platform_set_drvdata(pdev, ocotp_dev);
-
- ocotp_dev->timing = vf610_ocotp_calculate_timing(ocotp_dev);
-
- return 0;
+ return PTR_ERR_OR_ZERO(ocotp_dev->nvmem);
}
static struct platform_driver vf610_ocotp_driver = {
.probe = vf610_ocotp_probe,
- .remove = vf610_ocotp_remove,
.driver = {
.name = "vf610-ocotp",
.of_match_table = ocotp_of_match,
diff --git a/drivers/of/address.c b/drivers/of/address.c
index ce4d3d8b85de..53349912ac75 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -2,8 +2,10 @@
#define pr_fmt(fmt) "OF: " fmt
#include <linux/device.h>
+#include <linux/fwnode.h>
#include <linux/io.h>
#include <linux/ioport.h>
+#include <linux/logic_pio.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/pci.h>
@@ -333,7 +335,8 @@ int of_pci_range_to_resource(struct of_pci_range *range,
if (res->flags & IORESOURCE_IO) {
unsigned long port;
- err = pci_register_io_range(range->cpu_addr, range->size);
+ err = pci_register_io_range(&np->fwnode, range->cpu_addr,
+ range->size);
if (err)
goto invalid_range;
port = pci_address_to_pio(range->cpu_addr);
@@ -560,9 +563,14 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus,
* that translation is impossible (that is we are not dealing with a value
* that can be mapped to a cpu physical address). This is not really specified
* that way, but this is traditionally the way IBM at least do things
+ *
+ * Whenever the translation fails, the *host pointer will be set to the
+ * device that had registered logical PIO mapping, and the return code is
+ * relative to that node.
*/
static u64 __of_translate_address(struct device_node *dev,
- const __be32 *in_addr, const char *rprop)
+ const __be32 *in_addr, const char *rprop,
+ struct device_node **host)
{
struct device_node *parent = NULL;
struct of_bus *bus, *pbus;
@@ -575,6 +583,7 @@ static u64 __of_translate_address(struct device_node *dev,
/* Increase refcount at current level */
of_node_get(dev);
+ *host = NULL;
/* Get parent & match bus type */
parent = of_get_parent(dev);
if (parent == NULL)
@@ -595,6 +604,8 @@ static u64 __of_translate_address(struct device_node *dev,
/* Translate */
for (;;) {
+ struct logic_pio_hwaddr *iorange;
+
/* Switch to parent bus */
of_node_put(dev);
dev = parent;
@@ -607,6 +618,19 @@ static u64 __of_translate_address(struct device_node *dev,
break;
}
+ /*
+ * For indirectIO device which has no ranges property, get
+ * the address from reg directly.
+ */
+ iorange = find_io_range_by_fwnode(&dev->fwnode);
+ if (iorange && (iorange->flags != LOGIC_PIO_CPU_MMIO)) {
+ result = of_read_number(addr + 1, na - 1);
+ pr_debug("indirectIO matched(%pOF) 0x%llx\n",
+ dev, result);
+ *host = of_node_get(dev);
+ break;
+ }
+
/* Get new parent bus and counts */
pbus = of_match_bus(parent);
pbus->count_cells(dev, &pna, &pns);
@@ -638,13 +662,32 @@ static u64 __of_translate_address(struct device_node *dev,
u64 of_translate_address(struct device_node *dev, const __be32 *in_addr)
{
- return __of_translate_address(dev, in_addr, "ranges");
+ struct device_node *host;
+ u64 ret;
+
+ ret = __of_translate_address(dev, in_addr, "ranges", &host);
+ if (host) {
+ of_node_put(host);
+ return OF_BAD_ADDR;
+ }
+
+ return ret;
}
EXPORT_SYMBOL(of_translate_address);
u64 of_translate_dma_address(struct device_node *dev, const __be32 *in_addr)
{
- return __of_translate_address(dev, in_addr, "dma-ranges");
+ struct device_node *host;
+ u64 ret;
+
+ ret = __of_translate_address(dev, in_addr, "dma-ranges", &host);
+
+ if (host) {
+ of_node_put(host);
+ return OF_BAD_ADDR;
+ }
+
+ return ret;
}
EXPORT_SYMBOL(of_translate_dma_address);
@@ -686,29 +729,48 @@ const __be32 *of_get_address(struct device_node *dev, int index, u64 *size,
}
EXPORT_SYMBOL(of_get_address);
+static u64 of_translate_ioport(struct device_node *dev, const __be32 *in_addr,
+ u64 size)
+{
+ u64 taddr;
+ unsigned long port;
+ struct device_node *host;
+
+ taddr = __of_translate_address(dev, in_addr, "ranges", &host);
+ if (host) {
+ /* host-specific port access */
+ port = logic_pio_trans_hwaddr(&host->fwnode, taddr, size);
+ of_node_put(host);
+ } else {
+ /* memory-mapped I/O range */
+ port = pci_address_to_pio(taddr);
+ }
+
+ if (port == (unsigned long)-1)
+ return OF_BAD_ADDR;
+
+ return port;
+}
+
static int __of_address_to_resource(struct device_node *dev,
const __be32 *addrp, u64 size, unsigned int flags,
const char *name, struct resource *r)
{
u64 taddr;
- if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0)
+ if (flags & IORESOURCE_MEM)
+ taddr = of_translate_address(dev, addrp);
+ else if (flags & IORESOURCE_IO)
+ taddr = of_translate_ioport(dev, addrp, size);
+ else
return -EINVAL;
- taddr = of_translate_address(dev, addrp);
+
if (taddr == OF_BAD_ADDR)
return -EINVAL;
memset(r, 0, sizeof(struct resource));
- if (flags & IORESOURCE_IO) {
- unsigned long port;
- port = pci_address_to_pio(taddr);
- if (port == (unsigned long)-1)
- return -EINVAL;
- r->start = port;
- r->end = port + size - 1;
- } else {
- r->start = taddr;
- r->end = taddr + size - 1;
- }
+
+ r->start = taddr;
+ r->end = taddr + size - 1;
r->flags = flags;
r->name = name ? name : dev->full_name;
diff --git a/drivers/of/base.c b/drivers/of/base.c
index ad28de96e13f..848f549164cd 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -91,10 +91,72 @@ int __weak of_node_to_nid(struct device_node *np)
}
#endif
+static struct device_node **phandle_cache;
+static u32 phandle_cache_mask;
+
+/*
+ * Assumptions behind phandle_cache implementation:
+ * - phandle property values are in a contiguous range of 1..n
+ *
+ * If the assumptions do not hold, then
+ * - the phandle lookup overhead reduction provided by the cache
+ * will likely be less
+ */
+static void of_populate_phandle_cache(void)
+{
+ unsigned long flags;
+ u32 cache_entries;
+ struct device_node *np;
+ u32 phandles = 0;
+
+ raw_spin_lock_irqsave(&devtree_lock, flags);
+
+ kfree(phandle_cache);
+ phandle_cache = NULL;
+
+ for_each_of_allnodes(np)
+ if (np->phandle && np->phandle != OF_PHANDLE_ILLEGAL)
+ phandles++;
+
+ cache_entries = roundup_pow_of_two(phandles);
+ phandle_cache_mask = cache_entries - 1;
+
+ phandle_cache = kcalloc(cache_entries, sizeof(*phandle_cache),
+ GFP_ATOMIC);
+ if (!phandle_cache)
+ goto out;
+
+ for_each_of_allnodes(np)
+ if (np->phandle && np->phandle != OF_PHANDLE_ILLEGAL)
+ phandle_cache[np->phandle & phandle_cache_mask] = np;
+
+out:
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
+}
+
+#ifndef CONFIG_MODULES
+static int __init of_free_phandle_cache(void)
+{
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&devtree_lock, flags);
+
+ kfree(phandle_cache);
+ phandle_cache = NULL;
+
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
+
+ return 0;
+}
+late_initcall_sync(of_free_phandle_cache);
+#endif
+
void __init of_core_init(void)
{
struct device_node *np;
+ of_populate_phandle_cache();
+
/* Create the kset, and register existing nodes */
mutex_lock(&of_mutex);
of_kset = kset_create_and_add("devicetree", NULL, firmware_kobj);
@@ -1021,16 +1083,32 @@ EXPORT_SYMBOL_GPL(of_modalias_node);
*/
struct device_node *of_find_node_by_phandle(phandle handle)
{
- struct device_node *np;
+ struct device_node *np = NULL;
unsigned long flags;
+ phandle masked_handle;
if (!handle)
return NULL;
raw_spin_lock_irqsave(&devtree_lock, flags);
- for_each_of_allnodes(np)
- if (np->phandle == handle)
- break;
+
+ masked_handle = handle & phandle_cache_mask;
+
+ if (phandle_cache) {
+ if (phandle_cache[masked_handle] &&
+ handle == phandle_cache[masked_handle]->phandle)
+ np = phandle_cache[masked_handle];
+ }
+
+ if (!np) {
+ for_each_of_allnodes(np)
+ if (np->phandle == handle) {
+ if (phandle_cache)
+ phandle_cache[masked_handle] = np;
+ break;
+ }
+ }
+
of_node_get(np);
raw_spin_unlock_irqrestore(&devtree_lock, flags);
return np;
@@ -1284,6 +1362,190 @@ int of_parse_phandle_with_args(const struct device_node *np, const char *list_na
EXPORT_SYMBOL(of_parse_phandle_with_args);
/**
+ * of_parse_phandle_with_args_map() - Find a node pointed by phandle in a list and remap it
+ * @np: pointer to a device tree node containing a list
+ * @list_name: property name that contains a list
+ * @stem_name: stem of property names that specify phandles' arguments count
+ * @index: index of a phandle to parse out
+ * @out_args: optional pointer to output arguments structure (will be filled)
+ *
+ * This function is useful to parse lists of phandles and their arguments.
+ * Returns 0 on success and fills out_args, on error returns appropriate errno
+ * value. The difference between this function and of_parse_phandle_with_args()
+ * is that this API remaps a phandle if the node the phandle points to has
+ * a <@stem_name>-map property.
+ *
+ * Caller is responsible to call of_node_put() on the returned out_args->np
+ * pointer.
+ *
+ * Example:
+ *
+ * phandle1: node1 {
+ * #list-cells = <2>;
+ * }
+ *
+ * phandle2: node2 {
+ * #list-cells = <1>;
+ * }
+ *
+ * phandle3: node3 {
+ * #list-cells = <1>;
+ * list-map = <0 &phandle2 3>,
+ * <1 &phandle2 2>,
+ * <2 &phandle1 5 1>;
+ * list-map-mask = <0x3>;
+ * };
+ *
+ * node4 {
+ * list = <&phandle1 1 2 &phandle3 0>;
+ * }
+ *
+ * To get a device_node of the `node2' node you may call this:
+ * of_parse_phandle_with_args(node4, "list", "list", 1, &args);
+ */
+int of_parse_phandle_with_args_map(const struct device_node *np,
+ const char *list_name,
+ const char *stem_name,
+ int index, struct of_phandle_args *out_args)
+{
+ char *cells_name, *map_name = NULL, *mask_name = NULL;
+ char *pass_name = NULL;
+ struct device_node *cur, *new = NULL;
+ const __be32 *map, *mask, *pass;
+ static const __be32 dummy_mask[] = { [0 ... MAX_PHANDLE_ARGS] = ~0 };
+ static const __be32 dummy_pass[] = { [0 ... MAX_PHANDLE_ARGS] = 0 };
+ __be32 initial_match_array[MAX_PHANDLE_ARGS];
+ const __be32 *match_array = initial_match_array;
+ int i, ret, map_len, match;
+ u32 list_size, new_size;
+
+ if (index < 0)
+ return -EINVAL;
+
+ cells_name = kasprintf(GFP_KERNEL, "#%s-cells", stem_name);
+ if (!cells_name)
+ return -ENOMEM;
+
+ ret = -ENOMEM;
+ map_name = kasprintf(GFP_KERNEL, "%s-map", stem_name);
+ if (!map_name)
+ goto free;
+
+ mask_name = kasprintf(GFP_KERNEL, "%s-map-mask", stem_name);
+ if (!mask_name)
+ goto free;
+
+ pass_name = kasprintf(GFP_KERNEL, "%s-map-pass-thru", stem_name);
+ if (!pass_name)
+ goto free;
+
+ ret = __of_parse_phandle_with_args(np, list_name, cells_name, 0, index,
+ out_args);
+ if (ret)
+ goto free;
+
+ /* Get the #<list>-cells property */
+ cur = out_args->np;
+ ret = of_property_read_u32(cur, cells_name, &list_size);
+ if (ret < 0)
+ goto put;
+
+ /* Precalculate the match array - this simplifies match loop */
+ for (i = 0; i < list_size; i++)
+ initial_match_array[i] = cpu_to_be32(out_args->args[i]);
+
+ ret = -EINVAL;
+ while (cur) {
+ /* Get the <list>-map property */
+ map = of_get_property(cur, map_name, &map_len);
+ if (!map) {
+ ret = 0;
+ goto free;
+ }
+ map_len /= sizeof(u32);
+
+ /* Get the <list>-map-mask property (optional) */
+ mask = of_get_property(cur, mask_name, NULL);
+ if (!mask)
+ mask = dummy_mask;
+ /* Iterate through <list>-map property */
+ match = 0;
+ while (map_len > (list_size + 1) && !match) {
+ /* Compare specifiers */
+ match = 1;
+ for (i = 0; i < list_size; i++, map_len--)
+ match &= !((match_array[i] ^ *map++) & mask[i]);
+
+ of_node_put(new);
+ new = of_find_node_by_phandle(be32_to_cpup(map));
+ map++;
+ map_len--;
+
+ /* Check if not found */
+ if (!new)
+ goto put;
+
+ if (!of_device_is_available(new))
+ match = 0;
+
+ ret = of_property_read_u32(new, cells_name, &new_size);
+ if (ret)
+ goto put;
+
+ /* Check for malformed properties */
+ if (WARN_ON(new_size > MAX_PHANDLE_ARGS))
+ goto put;
+ if (map_len < new_size)
+ goto put;
+
+ /* Move forward by new node's #<list>-cells amount */
+ map += new_size;
+ map_len -= new_size;
+ }
+ if (!match)
+ goto put;
+
+ /* Get the <list>-map-pass-thru property (optional) */
+ pass = of_get_property(cur, pass_name, NULL);
+ if (!pass)
+ pass = dummy_pass;
+
+ /*
+ * Successfully parsed a <list>-map translation; copy new
+ * specifier into the out_args structure, keeping the
+ * bits specified in <list>-map-pass-thru.
+ */
+ match_array = map - new_size;
+ for (i = 0; i < new_size; i++) {
+ __be32 val = *(map - new_size + i);
+
+ if (i < list_size) {
+ val &= ~pass[i];
+ val |= cpu_to_be32(out_args->args[i]) & pass[i];
+ }
+
+ out_args->args[i] = be32_to_cpu(val);
+ }
+ out_args->args_count = list_size = new_size;
+ /* Iterate again with new provider */
+ out_args->np = new;
+ of_node_put(cur);
+ cur = new;
+ }
+put:
+ of_node_put(cur);
+ of_node_put(new);
+free:
+ kfree(mask_name);
+ kfree(map_name);
+ kfree(cells_name);
+ kfree(pass_name);
+
+ return ret;
+}
+EXPORT_SYMBOL(of_parse_phandle_with_args_map);
+
+/**
* of_parse_phandle_with_fixed_args() - Find a node pointed by phandle in a list
* @np: pointer to a device tree node containing a list
* @list_name: property name that contains a list
diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c
index 7bb33d22b4e2..f4f8ed9b5454 100644
--- a/drivers/of/dynamic.c
+++ b/drivers/of/dynamic.c
@@ -383,25 +383,24 @@ struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags)
/**
* __of_node_dup() - Duplicate or create an empty device node dynamically.
- * @fmt: Format string (plus vargs) for new full name of the device node
+ * @np: if not NULL, contains properties to be duplicated in new node
+ * @full_name: string value to be duplicated into new node's full_name field
*
- * Create an device tree node, either by duplicating an empty node or by allocating
- * an empty one suitable for further modification. The node data are
- * dynamically allocated and all the node flags have the OF_DYNAMIC &
- * OF_DETACHED bits set. Returns the newly allocated node or NULL on out of
- * memory error.
+ * Create a device tree node, optionally duplicating the properties of
+ * another node. The node data are dynamically allocated and all the node
+ * flags have the OF_DYNAMIC & OF_DETACHED bits set.
+ *
+ * Returns the newly allocated node or NULL on out of memory error.
*/
-struct device_node *__of_node_dup(const struct device_node *np, const char *fmt, ...)
+struct device_node *__of_node_dup(const struct device_node *np,
+ const char *full_name)
{
- va_list vargs;
struct device_node *node;
node = kzalloc(sizeof(*node), GFP_KERNEL);
if (!node)
return NULL;
- va_start(vargs, fmt);
- node->full_name = kvasprintf(GFP_KERNEL, fmt, vargs);
- va_end(vargs);
+ node->full_name = kstrdup(full_name, GFP_KERNEL);
if (!node->full_name) {
kfree(node);
return NULL;
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index 84aa9d676375..6da20b9688f7 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -942,7 +942,7 @@ int __init early_init_dt_scan_chosen_stdout(void)
int offset;
const char *p, *q, *options = NULL;
int l;
- const struct earlycon_id *match;
+ const struct earlycon_id **p_match;
const void *fdt = initial_boot_params;
offset = fdt_path_offset(fdt, "/chosen");
@@ -969,7 +969,10 @@ int __init early_init_dt_scan_chosen_stdout(void)
return 0;
}
- for (match = __earlycon_table; match < __earlycon_table_end; match++) {
+ for (p_match = __earlycon_table; p_match < __earlycon_table_end;
+ p_match++) {
+ const struct earlycon_id *match = *p_match;
+
if (!match->compatible[0])
continue;
diff --git a/drivers/of/of_net.c b/drivers/of/of_net.c
index d820f3edd431..53189d4022a6 100644
--- a/drivers/of/of_net.c
+++ b/drivers/of/of_net.c
@@ -7,6 +7,7 @@
*/
#include <linux/etherdevice.h>
#include <linux/kernel.h>
+#include <linux/nvmem-consumer.h>
#include <linux/of_net.h>
#include <linux/phy.h>
#include <linux/export.h>
@@ -80,3 +81,42 @@ const void *of_get_mac_address(struct device_node *np)
return of_get_mac_addr(np, "address");
}
EXPORT_SYMBOL(of_get_mac_address);
+
+/**
+ * Obtain the MAC address from an nvmem provider named 'mac-address' through
+ * device tree.
+ * On success, copies the new address into memory pointed to by addr and
+ * returns 0. Returns a negative error code otherwise.
+ * @np: Device tree node containing the nvmem-cells phandle
+ * @addr: Pointer to receive the MAC address using ether_addr_copy()
+ */
+int of_get_nvmem_mac_address(struct device_node *np, void *addr)
+{
+ struct nvmem_cell *cell;
+ const void *mac;
+ size_t len;
+ int ret;
+
+ cell = of_nvmem_cell_get(np, "mac-address");
+ if (IS_ERR(cell))
+ return PTR_ERR(cell);
+
+ mac = nvmem_cell_read(cell, &len);
+
+ nvmem_cell_put(cell);
+
+ if (IS_ERR(mac))
+ return PTR_ERR(mac);
+
+ if (len < ETH_ALEN || !is_valid_ether_addr(mac)) {
+ ret = -EINVAL;
+ } else {
+ ether_addr_copy(addr, mac);
+ ret = 0;
+ }
+
+ kfree(mac);
+
+ return ret;
+}
+EXPORT_SYMBOL(of_get_nvmem_mac_address);
diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h
index 0c609e7d0334..891d780c076a 100644
--- a/drivers/of/of_private.h
+++ b/drivers/of/of_private.h
@@ -104,7 +104,8 @@ extern void *__unflatten_device_tree(const void *blob,
* own the devtree lock or work on detached trees only.
*/
struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags);
-__printf(2, 3) struct device_node *__of_node_dup(const struct device_node *np, const char *fmt, ...);
+struct device_node *__of_node_dup(const struct device_node *np,
+ const char *full_name);
struct device_node *__of_find_node_by_path(struct device_node *parent,
const char *path);
@@ -131,6 +132,9 @@ extern void __of_detach_node_sysfs(struct device_node *np);
extern void __of_sysfs_remove_bin_file(struct device_node *np,
struct property *prop);
+/* illegal phandle value (set when unresolved) */
+#define OF_PHANDLE_ILLEGAL 0xdeadbeef
+
/* iterators for transactions, used for overlays */
/* forward iterator */
#define for_each_transaction_entry(_oft, _te) \
diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
index b930e05d1215..b35fe88f1851 100644
--- a/drivers/of/overlay.c
+++ b/drivers/of/overlay.c
@@ -312,7 +312,20 @@ static int add_changeset_property(struct overlay_changeset *ovcs,
* If @node has child nodes, add the children recursively via
* build_changeset_next_level().
*
- * NOTE: Multiple mods of created nodes not supported.
+ * NOTE_1: A live devicetree created from a flattened device tree (FDT) will
+ * not contain the full path in node->full_name. Thus an overlay
+ * created from an FDT also will not contain the full path in
+ * node->full_name. However, a live devicetree created from Open
+ * Firmware may have the full path in node->full_name.
+ *
+ * add_changeset_node() follows the FDT convention and does not include
+ * the full path in node->full_name. Even though it expects the overlay
+ * to not contain the full path, it uses kbasename() to remove the
+ * full path should it exist. It also uses kbasename() in comparisons
+ * to nodes in the live devicetree so that it can apply an overlay to
+ * a live devicetree created from Open Firmware.
+ *
+ * NOTE_2: Multiple mods of created nodes not supported.
* If more than one fragment contains a node that does not already exist
* in the live tree, then for each fragment of_changeset_attach_node()
* will add a changeset entry to add the node. When the changeset is
@@ -339,8 +352,7 @@ static int add_changeset_node(struct overlay_changeset *ovcs,
break;
if (!tchild) {
- tchild = __of_node_dup(node, "%pOF/%s",
- target_node, node_kbasename);
+ tchild = __of_node_dup(node, node_kbasename);
if (!tchild)
return -ENOMEM;
@@ -958,7 +970,7 @@ static int overlay_removal_is_ok(struct overlay_changeset *remove_ovcs)
* @ovcs_id: Pointer to overlay changeset id
*
* Removes an overlay if it is permissible. @ovcs_id was previously returned
- * by of_overlay_apply().
+ * by of_overlay_fdt_apply().
*
* If an error occurred while attempting to revert the overlay changeset,
* then an attempt is made to re-apply any changeset entry that was
diff --git a/drivers/of/resolver.c b/drivers/of/resolver.c
index b2f645187213..65d0b7adfcd4 100644
--- a/drivers/of/resolver.c
+++ b/drivers/of/resolver.c
@@ -19,9 +19,6 @@
#include "of_private.h"
-/* illegal phandle value (set when unresolved) */
-#define OF_PHANDLE_ILLEGAL 0xdeadbeef
-
static phandle live_tree_max_phandle(void)
{
struct device_node *node;
diff --git a/drivers/of/unittest-data/Makefile b/drivers/of/unittest-data/Makefile
index 8fd0ea4b92b0..013d85e694c6 100644
--- a/drivers/of/unittest-data/Makefile
+++ b/drivers/of/unittest-data/Makefile
@@ -21,8 +21,6 @@ obj-$(CONFIG_OF_OVERLAY) += overlay.dtb.o \
overlay_bad_symbol.dtb.o \
overlay_base.dtb.o
-targets += $(foreach suffix, dtb dtb.S, $(patsubst %.dtb.o,%.$(suffix),$(obj-y)))
-
# enable creation of __symbols__ node
DTC_FLAGS_overlay += -@
DTC_FLAGS_overlay_bad_phandle += -@
@@ -32,7 +30,3 @@ DTC_FLAGS_testcases += -@
# suppress warnings about intentional errors
DTC_FLAGS_testcases += -Wno-interrupts_property
-
-.PRECIOUS: \
- $(obj)/%.dtb.S \
- $(obj)/%.dtb
diff --git a/drivers/of/unittest-data/tests-phandle.dtsi b/drivers/of/unittest-data/tests-phandle.dtsi
index 3c2f09e56b61..6b33be4c4416 100644
--- a/drivers/of/unittest-data/tests-phandle.dtsi
+++ b/drivers/of/unittest-data/tests-phandle.dtsi
@@ -26,6 +26,18 @@
#phandle-cells = <3>;
};
+ provider4: provider4 {
+ #phandle-cells = <2>;
+ phandle-map = <0 1 &provider1 3>,
+ <4 0 &provider0>,
+ <16 5 &provider3 3 5 0>,
+ <200 8 &provider2 23 6>,
+ <19 0 &provider2 15 0>,
+ <2 3 &provider3 2 5 3>;
+ phandle-map-mask = <0xff 0xf>;
+ phandle-map-pass-thru = <0x0 0xf0>;
+ };
+
consumer-a {
phandle-list = <&provider1 1>,
<&provider2 2 0>,
@@ -44,6 +56,19 @@
unterminated-string = [40 41 42 43];
unterminated-string-list = "first", "second", [40 41 42 43];
};
+
+ consumer-b {
+ phandle-list = <&provider1 1>,
+ <&provider4 2 3>,
+ <0>,
+ <&provider4 4 0x100>,
+ <&provider4 0 0x61>,
+ <&provider0>,
+ <&provider4 19 0x20>;
+ phandle-list-bad-phandle = <12345678 0 0>;
+ phandle-list-bad-args = <&provider2 1 0>,
+ <&provider4 0>;
+ };
};
};
};
diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c
index a23b54780c7d..6bb37c18292a 100644
--- a/drivers/of/unittest.c
+++ b/drivers/of/unittest.c
@@ -45,8 +45,6 @@ static struct unittest_results {
failed; \
})
-static int __init overlay_data_apply(const char *overlay_name, int *overlay_id);
-
static void __init of_unittest_find_node_by_name(void)
{
struct device_node *np;
@@ -254,12 +252,18 @@ static void __init of_unittest_check_tree_linkage(void)
static void __init of_unittest_printf_one(struct device_node *np, const char *fmt,
const char *expected)
{
- unsigned char buf[strlen(expected)+10];
+ unsigned char *buf;
+ int buf_size;
int size, i;
+ buf_size = strlen(expected) + 10;
+ buf = kmalloc(buf_size, GFP_KERNEL);
+ if (!buf)
+ return;
+
/* Baseline; check conversion with a large size limit */
- memset(buf, 0xff, sizeof(buf));
- size = snprintf(buf, sizeof(buf) - 2, fmt, np);
+ memset(buf, 0xff, buf_size);
+ size = snprintf(buf, buf_size - 2, fmt, np);
/* use strcmp() instead of strncmp() here to be absolutely sure strings match */
unittest((strcmp(buf, expected) == 0) && (buf[size+1] == 0xff),
@@ -270,12 +274,13 @@ static void __init of_unittest_printf_one(struct device_node *np, const char *fm
size++;
for (i = 0; i < 2; i++, size--) {
/* Clear the buffer, and make sure it works correctly still */
- memset(buf, 0xff, sizeof(buf));
+ memset(buf, 0xff, buf_size);
snprintf(buf, size+1, fmt, np);
unittest(strncmp(buf, expected, size) == 0 && (buf[size+1] == 0xff),
"snprintf failed; size=%i fmt='%s' expected='%s' rslt='%s'\n",
size, fmt, expected, buf);
}
+ kfree(buf);
}
static void __init of_unittest_printf(void)
@@ -290,7 +295,7 @@ static void __init of_unittest_printf(void)
return;
}
- num_to_str(phandle_str, sizeof(phandle_str), np->phandle);
+ num_to_str(phandle_str, sizeof(phandle_str), np->phandle, 0);
of_unittest_printf_one(np, "%pOF", full_name);
of_unittest_printf_one(np, "%pOFf", full_name);
@@ -455,6 +460,125 @@ static void __init of_unittest_parse_phandle_with_args(void)
unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
}
+static void __init of_unittest_parse_phandle_with_args_map(void)
+{
+ struct device_node *np, *p0, *p1, *p2, *p3;
+ struct of_phandle_args args;
+ int i, rc;
+
+ np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-b");
+ if (!np) {
+ pr_err("missing testcase data\n");
+ return;
+ }
+
+ p0 = of_find_node_by_path("/testcase-data/phandle-tests/provider0");
+ if (!p0) {
+ pr_err("missing testcase data\n");
+ return;
+ }
+
+ p1 = of_find_node_by_path("/testcase-data/phandle-tests/provider1");
+ if (!p1) {
+ pr_err("missing testcase data\n");
+ return;
+ }
+
+ p2 = of_find_node_by_path("/testcase-data/phandle-tests/provider2");
+ if (!p2) {
+ pr_err("missing testcase data\n");
+ return;
+ }
+
+ p3 = of_find_node_by_path("/testcase-data/phandle-tests/provider3");
+ if (!p3) {
+ pr_err("missing testcase data\n");
+ return;
+ }
+
+ rc = of_count_phandle_with_args(np, "phandle-list", "#phandle-cells");
+ unittest(rc == 7, "of_count_phandle_with_args() returned %i, expected 7\n", rc);
+
+ for (i = 0; i < 8; i++) {
+ bool passed = true;
+
+ rc = of_parse_phandle_with_args_map(np, "phandle-list",
+ "phandle", i, &args);
+
+ /* Test the values from tests-phandle.dtsi */
+ switch (i) {
+ case 0:
+ passed &= !rc;
+ passed &= (args.np == p1);
+ passed &= (args.args_count == 1);
+ passed &= (args.args[0] == 1);
+ break;
+ case 1:
+ passed &= !rc;
+ passed &= (args.np == p3);
+ passed &= (args.args_count == 3);
+ passed &= (args.args[0] == 2);
+ passed &= (args.args[1] == 5);
+ passed &= (args.args[2] == 3);
+ break;
+ case 2:
+ passed &= (rc == -ENOENT);
+ break;
+ case 3:
+ passed &= !rc;
+ passed &= (args.np == p0);
+ passed &= (args.args_count == 0);
+ break;
+ case 4:
+ passed &= !rc;
+ passed &= (args.np == p1);
+ passed &= (args.args_count == 1);
+ passed &= (args.args[0] == 3);
+ break;
+ case 5:
+ passed &= !rc;
+ passed &= (args.np == p0);
+ passed &= (args.args_count == 0);
+ break;
+ case 6:
+ passed &= !rc;
+ passed &= (args.np == p2);
+ passed &= (args.args_count == 2);
+ passed &= (args.args[0] == 15);
+ passed &= (args.args[1] == 0x20);
+ break;
+ case 7:
+ passed &= (rc == -ENOENT);
+ break;
+ default:
+ passed = false;
+ }
+
+ unittest(passed, "index %i - data error on node %s rc=%i\n",
+ i, args.np->full_name, rc);
+ }
+
+ /* Check for missing list property */
+ rc = of_parse_phandle_with_args_map(np, "phandle-list-missing",
+ "phandle", 0, &args);
+ unittest(rc == -ENOENT, "expected:%i got:%i\n", -ENOENT, rc);
+
+ /* Check for missing cells,map,mask property */
+ rc = of_parse_phandle_with_args_map(np, "phandle-list",
+ "phandle-missing", 0, &args);
+ unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
+
+ /* Check for bad phandle in list */
+ rc = of_parse_phandle_with_args_map(np, "phandle-list-bad-phandle",
+ "phandle", 0, &args);
+ unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
+
+ /* Check for incorrectly formed argument list */
+ rc = of_parse_phandle_with_args_map(np, "phandle-list-bad-args",
+ "phandle", 1, &args);
+ unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
+}
+
static void __init of_unittest_property_string(void)
{
const char *strings[4];
@@ -564,42 +688,72 @@ static void __init of_unittest_property_copy(void)
static void __init of_unittest_changeset(void)
{
#ifdef CONFIG_OF_DYNAMIC
- struct property *ppadd, padd = { .name = "prop-add", .length = 0, .value = "" };
+ struct property *ppadd, padd = { .name = "prop-add", .length = 1, .value = "" };
+ struct property *ppname_n1, pname_n1 = { .name = "name", .length = 3, .value = "n1" };
+ struct property *ppname_n2, pname_n2 = { .name = "name", .length = 3, .value = "n2" };
+ struct property *ppname_n21, pname_n21 = { .name = "name", .length = 3, .value = "n21" };
struct property *ppupdate, pupdate = { .name = "prop-update", .length = 5, .value = "abcd" };
struct property *ppremove;
- struct device_node *n1, *n2, *n21, *nremove, *parent, *np;
+ struct device_node *n1, *n2, *n21, *nchangeset, *nremove, *parent, *np;
struct of_changeset chgset;
- n1 = __of_node_dup(NULL, "/testcase-data/changeset/n1");
+ n1 = __of_node_dup(NULL, "n1");
unittest(n1, "testcase setup failure\n");
- n2 = __of_node_dup(NULL, "/testcase-data/changeset/n2");
+
+ n2 = __of_node_dup(NULL, "n2");
unittest(n2, "testcase setup failure\n");
- n21 = __of_node_dup(NULL, "%s/%s", "/testcase-data/changeset/n2", "n21");
+
+ n21 = __of_node_dup(NULL, "n21");
unittest(n21, "testcase setup failure %p\n", n21);
- nremove = of_find_node_by_path("/testcase-data/changeset/node-remove");
+
+ nchangeset = of_find_node_by_path("/testcase-data/changeset");
+ nremove = of_get_child_by_name(nchangeset, "node-remove");
unittest(nremove, "testcase setup failure\n");
+
ppadd = __of_prop_dup(&padd, GFP_KERNEL);
unittest(ppadd, "testcase setup failure\n");
+
+ ppname_n1 = __of_prop_dup(&pname_n1, GFP_KERNEL);
+ unittest(ppname_n1, "testcase setup failure\n");
+
+ ppname_n2 = __of_prop_dup(&pname_n2, GFP_KERNEL);
+ unittest(ppname_n2, "testcase setup failure\n");
+
+ ppname_n21 = __of_prop_dup(&pname_n21, GFP_KERNEL);
+ unittest(ppname_n21, "testcase setup failure\n");
+
ppupdate = __of_prop_dup(&pupdate, GFP_KERNEL);
unittest(ppupdate, "testcase setup failure\n");
- parent = nremove->parent;
+
+ parent = nchangeset;
n1->parent = parent;
n2->parent = parent;
n21->parent = n2;
- n2->child = n21;
+
ppremove = of_find_property(parent, "prop-remove", NULL);
unittest(ppremove, "failed to find removal prop");
of_changeset_init(&chgset);
+
unittest(!of_changeset_attach_node(&chgset, n1), "fail attach n1\n");
+ unittest(!of_changeset_add_property(&chgset, n1, ppname_n1), "fail add prop name\n");
+
unittest(!of_changeset_attach_node(&chgset, n2), "fail attach n2\n");
+ unittest(!of_changeset_add_property(&chgset, n2, ppname_n2), "fail add prop name\n");
+
unittest(!of_changeset_detach_node(&chgset, nremove), "fail remove node\n");
+ unittest(!of_changeset_add_property(&chgset, n21, ppname_n21), "fail add prop name\n");
+
unittest(!of_changeset_attach_node(&chgset, n21), "fail attach n21\n");
- unittest(!of_changeset_add_property(&chgset, parent, ppadd), "fail add prop\n");
+
+ unittest(!of_changeset_add_property(&chgset, parent, ppadd), "fail add prop prop-add\n");
unittest(!of_changeset_update_property(&chgset, parent, ppupdate), "fail update prop\n");
unittest(!of_changeset_remove_property(&chgset, parent, ppremove), "fail remove prop\n");
+
unittest(!of_changeset_apply(&chgset), "apply failed\n");
+ of_node_put(nchangeset);
+
/* Make sure node names are constructed correctly */
unittest((np = of_find_node_by_path("/testcase-data/changeset/n2/n21")),
"'%pOF' not added\n", n21);
@@ -1036,6 +1190,7 @@ static int __init unittest_data_add(void)
}
#ifdef CONFIG_OF_OVERLAY
+static int __init overlay_data_apply(const char *overlay_name, int *overlay_id);
static int unittest_probe(struct platform_device *pdev)
{
@@ -1267,26 +1422,18 @@ static void of_unittest_destroy_tracked_overlays(void)
static int __init of_unittest_apply_overlay(int overlay_nr, int unittest_nr,
int *overlay_id)
{
- struct device_node *np = NULL;
const char *overlay_name;
- int ret;
overlay_name = overlay_name_from_nr(overlay_nr);
- ret = overlay_data_apply(overlay_name, overlay_id);
- if (!ret) {
+ if (!overlay_data_apply(overlay_name, overlay_id)) {
unittest(0, "could not apply overlay \"%s\"\n",
overlay_name);
- goto out;
+ return -EFAULT;
}
of_unittest_track_overlay(*overlay_id);
- ret = 0;
-
-out:
- of_node_put(np);
-
- return ret;
+ return 0;
}
/* apply an overlay while checking before and after states */
@@ -1380,11 +1527,8 @@ static int __init of_unittest_apply_revert_overlay_check(int overlay_nr,
/* test activation of device */
static void __init of_unittest_overlay_0(void)
{
- int ret;
-
/* device should enable */
- ret = of_unittest_apply_overlay_check(0, 0, 0, 1, PDEV_OVERLAY);
- if (ret != 0)
+ if (of_unittest_apply_overlay_check(0, 0, 0, 1, PDEV_OVERLAY))
return;
unittest(1, "overlay test %d passed\n", 0);
@@ -1393,11 +1537,8 @@ static void __init of_unittest_overlay_0(void)
/* test deactivation of device */
static void __init of_unittest_overlay_1(void)
{
- int ret;
-
/* device should disable */
- ret = of_unittest_apply_overlay_check(1, 1, 1, 0, PDEV_OVERLAY);
- if (ret != 0)
+ if (of_unittest_apply_overlay_check(1, 1, 1, 0, PDEV_OVERLAY))
return;
unittest(1, "overlay test %d passed\n", 1);
@@ -1406,11 +1547,8 @@ static void __init of_unittest_overlay_1(void)
/* test activation of device */
static void __init of_unittest_overlay_2(void)
{
- int ret;
-
/* device should enable */
- ret = of_unittest_apply_overlay_check(2, 2, 0, 1, PDEV_OVERLAY);
- if (ret != 0)
+ if (of_unittest_apply_overlay_check(2, 2, 0, 1, PDEV_OVERLAY))
return;
unittest(1, "overlay test %d passed\n", 2);
@@ -1419,11 +1557,8 @@ static void __init of_unittest_overlay_2(void)
/* test deactivation of device */
static void __init of_unittest_overlay_3(void)
{
- int ret;
-
/* device should disable */
- ret = of_unittest_apply_overlay_check(3, 3, 1, 0, PDEV_OVERLAY);
- if (ret != 0)
+ if (of_unittest_apply_overlay_check(3, 3, 1, 0, PDEV_OVERLAY))
return;
unittest(1, "overlay test %d passed\n", 3);
@@ -1432,11 +1567,8 @@ static void __init of_unittest_overlay_3(void)
/* test activation of a full device node */
static void __init of_unittest_overlay_4(void)
{
- int ret;
-
/* device should disable */
- ret = of_unittest_apply_overlay_check(4, 4, 0, 1, PDEV_OVERLAY);
- if (ret != 0)
+ if (of_unittest_apply_overlay_check(4, 4, 0, 1, PDEV_OVERLAY))
return;
unittest(1, "overlay test %d passed\n", 4);
@@ -1445,11 +1577,8 @@ static void __init of_unittest_overlay_4(void)
/* test overlay apply/revert sequence */
static void __init of_unittest_overlay_5(void)
{
- int ret;
-
/* device should disable */
- ret = of_unittest_apply_revert_overlay_check(5, 5, 0, 1, PDEV_OVERLAY);
- if (ret != 0)
+ if (of_unittest_apply_revert_overlay_check(5, 5, 0, 1, PDEV_OVERLAY))
return;
unittest(1, "overlay test %d passed\n", 5);
@@ -1458,7 +1587,7 @@ static void __init of_unittest_overlay_5(void)
/* test overlay application in sequence */
static void __init of_unittest_overlay_6(void)
{
- int ret, i, ov_id[2], ovcs_id;
+ int i, ov_id[2], ovcs_id;
int overlay_nr = 6, unittest_nr = 6;
int before = 0, after = 1;
const char *overlay_name;
@@ -1481,8 +1610,7 @@ static void __init of_unittest_overlay_6(void)
overlay_name = overlay_name_from_nr(overlay_nr + i);
- ret = overlay_data_apply(overlay_name, &ovcs_id);
- if (!ret) {
+ if (!overlay_data_apply(overlay_name, &ovcs_id)) {
unittest(0, "could not apply overlay \"%s\"\n",
overlay_name);
return;
@@ -1506,8 +1634,7 @@ static void __init of_unittest_overlay_6(void)
for (i = 1; i >= 0; i--) {
ovcs_id = ov_id[i];
- ret = of_overlay_remove(&ovcs_id);
- if (ret != 0) {
+ if (of_overlay_remove(&ovcs_id)) {
unittest(0, "%s failed destroy @\"%s\"\n",
overlay_name_from_nr(overlay_nr + i),
unittest_path(unittest_nr + i,
@@ -1536,7 +1663,7 @@ static void __init of_unittest_overlay_6(void)
/* test overlay application in sequence */
static void __init of_unittest_overlay_8(void)
{
- int ret, i, ov_id[2], ovcs_id;
+ int i, ov_id[2], ovcs_id;
int overlay_nr = 8, unittest_nr = 8;
const char *overlay_name;
@@ -1547,8 +1674,7 @@ static void __init of_unittest_overlay_8(void)
overlay_name = overlay_name_from_nr(overlay_nr + i);
- ret = overlay_data_apply(overlay_name, &ovcs_id);
- if (ret < 0) {
+ if (!overlay_data_apply(overlay_name, &ovcs_id)) {
unittest(0, "could not apply overlay \"%s\"\n",
overlay_name);
return;
@@ -1559,8 +1685,7 @@ static void __init of_unittest_overlay_8(void)
/* now try to remove first overlay (it should fail) */
ovcs_id = ov_id[0];
- ret = of_overlay_remove(&ovcs_id);
- if (ret == 0) {
+ if (!of_overlay_remove(&ovcs_id)) {
unittest(0, "%s was destroyed @\"%s\"\n",
overlay_name_from_nr(overlay_nr + 0),
unittest_path(unittest_nr,
@@ -1571,8 +1696,7 @@ static void __init of_unittest_overlay_8(void)
/* removing them in order should work */
for (i = 1; i >= 0; i--) {
ovcs_id = ov_id[i];
- ret = of_overlay_remove(&ovcs_id);
- if (ret != 0) {
+ if (of_overlay_remove(&ovcs_id)) {
unittest(0, "%s not destroyed @\"%s\"\n",
overlay_name_from_nr(overlay_nr + i),
unittest_path(unittest_nr,
@@ -1604,8 +1728,8 @@ static void __init of_unittest_overlay_10(void)
ret = of_path_device_type_exists(child_path, PDEV_OVERLAY);
kfree(child_path);
- if (unittest(ret, "overlay test %d failed; no child device\n", 10))
- return;
+
+ unittest(ret, "overlay test %d failed; no child device\n", 10);
}
/* test insertion of a bus with parent devices (and revert) */
@@ -1616,9 +1740,7 @@ static void __init of_unittest_overlay_11(void)
/* device should disable */
ret = of_unittest_apply_revert_overlay_check(11, 11, 0, 1,
PDEV_OVERLAY);
- if (unittest(ret == 0,
- "overlay test %d failed; overlay application\n", 11))
- return;
+ unittest(ret == 0, "overlay test %d failed; overlay apply\n", 11);
}
#if IS_BUILTIN(CONFIG_I2C) && IS_ENABLED(CONFIG_OF_OVERLAY)
@@ -1769,7 +1891,7 @@ static int unittest_i2c_mux_select_chan(struct i2c_mux_core *muxc, u32 chan)
static int unittest_i2c_mux_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- int ret, i, nchans;
+ int i, nchans;
struct device *dev = &client->dev;
struct i2c_adapter *adap = to_i2c_adapter(dev->parent);
struct device_node *np = client->dev.of_node, *child;
@@ -1785,8 +1907,7 @@ static int unittest_i2c_mux_probe(struct i2c_client *client,
max_reg = (u32)-1;
for_each_child_of_node(np, child) {
- ret = of_property_read_u32(child, "reg", &reg);
- if (ret)
+ if (of_property_read_u32(child, "reg", &reg))
continue;
if (max_reg == (u32)-1 || reg > max_reg)
max_reg = reg;
@@ -1802,8 +1923,7 @@ static int unittest_i2c_mux_probe(struct i2c_client *client,
if (!muxc)
return -ENOMEM;
for (i = 0; i < nchans; i++) {
- ret = i2c_mux_add_adapter(muxc, 0, i, 0);
- if (ret) {
+ if (i2c_mux_add_adapter(muxc, 0, i, 0)) {
dev_err(dev, "Failed to register mux #%d\n", i);
i2c_mux_del_adapters(muxc);
return -ENODEV;
@@ -1877,11 +1997,8 @@ static void of_unittest_overlay_i2c_cleanup(void)
static void __init of_unittest_overlay_i2c_12(void)
{
- int ret;
-
/* device should enable */
- ret = of_unittest_apply_overlay_check(12, 12, 0, 1, I2C_OVERLAY);
- if (ret != 0)
+ if (of_unittest_apply_overlay_check(12, 12, 0, 1, I2C_OVERLAY))
return;
unittest(1, "overlay test %d passed\n", 12);
@@ -1890,11 +2007,8 @@ static void __init of_unittest_overlay_i2c_12(void)
/* test deactivation of device */
static void __init of_unittest_overlay_i2c_13(void)
{
- int ret;
-
/* device should disable */
- ret = of_unittest_apply_overlay_check(13, 13, 1, 0, I2C_OVERLAY);
- if (ret != 0)
+ if (of_unittest_apply_overlay_check(13, 13, 1, 0, I2C_OVERLAY))
return;
unittest(1, "overlay test %d passed\n", 13);
@@ -1907,11 +2021,8 @@ static void of_unittest_overlay_i2c_14(void)
static void __init of_unittest_overlay_i2c_15(void)
{
- int ret;
-
/* device should enable */
- ret = of_unittest_apply_overlay_check(15, 15, 0, 1, I2C_OVERLAY);
- if (ret != 0)
+ if (of_unittest_apply_overlay_check(15, 15, 0, 1, I2C_OVERLAY))
return;
unittest(1, "overlay test %d passed\n", 15);
@@ -1927,10 +2038,8 @@ static inline void of_unittest_overlay_i2c_15(void) { }
static void __init of_unittest_overlay(void)
{
struct device_node *bus_np = NULL;
- int ret;
- ret = platform_driver_register(&unittest_driver);
- if (ret != 0) {
+ if (platform_driver_register(&unittest_driver)) {
unittest(0, "could not register unittest driver\n");
goto out;
}
@@ -1941,8 +2050,7 @@ static void __init of_unittest_overlay(void)
goto out;
}
- ret = of_platform_default_populate(bus_np, NULL, NULL);
- if (ret != 0) {
+ if (of_platform_default_populate(bus_np, NULL, NULL)) {
unittest(0, "could not populate bus @ \"%s\"\n", bus_path);
goto out;
}
@@ -2156,10 +2264,8 @@ static int __init overlay_data_apply(const char *overlay_name, int *overlay_id)
}
size = info->dtb_end - info->dtb_begin;
- if (!size) {
+ if (!size)
pr_err("no overlay data for %s\n", overlay_name);
- ret = 0;
- }
ret = of_overlay_fdt_apply(info->dtb_begin, size, &info->overlay_id);
if (overlay_id)
@@ -2193,7 +2299,6 @@ static __init void of_unittest_overlay_high_level(void)
struct device_node *overlay_base_symbols;
struct device_node **pprev;
struct property *prop;
- int ret;
if (!overlay_base_root) {
unittest(0, "overlay_base_root not initialized\n");
@@ -2284,19 +2389,15 @@ static __init void of_unittest_overlay_high_level(void)
prop->name);
goto err_unlock;
}
- ret = __of_add_property(of_symbols, new_prop);
- if (ret) {
- if (!strcmp(new_prop->name, "name")) {
- /* auto-generated by unflatten */
- ret = 0;
+ if (__of_add_property(of_symbols, new_prop)) {
+ /* "name" auto-generated by unflatten */
+ if (!strcmp(new_prop->name, "name"))
continue;
- }
unittest(0, "duplicate property '%s' in overlay_base node __symbols__",
prop->name);
goto err_unlock;
}
- ret = __of_add_property_sysfs(of_symbols, new_prop);
- if (ret) {
+ if (__of_add_property_sysfs(of_symbols, new_prop)) {
unittest(0, "unable to add property '%s' in overlay_base node __symbols__ to sysfs",
prop->name);
goto err_unlock;
@@ -2355,6 +2456,7 @@ static int __init of_unittest(void)
of_unittest_find_node_by_name();
of_unittest_dynamic();
of_unittest_parse_phandle_with_args();
+ of_unittest_parse_phandle_with_args_map();
of_unittest_printf();
of_unittest_property_string();
of_unittest_property_copy();
diff --git a/drivers/oprofile/oprofilefs.c b/drivers/oprofile/oprofilefs.c
index d77ebbfc67c9..4ea08979312c 100644
--- a/drivers/oprofile/oprofilefs.c
+++ b/drivers/oprofile/oprofilefs.c
@@ -138,6 +138,9 @@ static int __oprofilefs_create_file(struct dentry *root, char const *name,
struct dentry *dentry;
struct inode *inode;
+ if (!root)
+ return -ENOMEM;
+
inode_lock(d_inode(root));
dentry = d_alloc_name(root, name);
if (!dentry) {
diff --git a/drivers/parisc/lba_pci.c b/drivers/parisc/lba_pci.c
index 41b740aed3a3..69bd98421eb1 100644
--- a/drivers/parisc/lba_pci.c
+++ b/drivers/parisc/lba_pci.c
@@ -1403,9 +1403,27 @@ lba_hw_init(struct lba_device *d)
WRITE_REG32(stat, d->hba.base_addr + LBA_ERROR_CONFIG);
}
- /* Set HF mode as the default (vs. -1 mode). */
+
+ /*
+ * Hard Fail vs. Soft Fail on PCI "Master Abort".
+ *
+ * "Master Abort" means the MMIO transaction timed out - usually due to
+ * the device not responding to an MMIO read. We would like HF to be
+ * enabled to find driver problems, though it means the system will
+ * crash with a HPMC.
+ *
+ * In SoftFail mode "~0L" is returned as a result of a timeout on the
+ * pci bus. This is like how PCI busses on x86 and most other
+ * architectures behave. In order to increase compatibility with
+ * existing (x86) PCI hardware and existing Linux drivers we enable
+ * Soft Faul mode on PA-RISC now too.
+ */
stat = READ_REG32(d->hba.base_addr + LBA_STAT_CTL);
+#if defined(ENABLE_HARDFAIL)
WRITE_REG32(stat | HF_ENABLE, d->hba.base_addr + LBA_STAT_CTL);
+#else
+ WRITE_REG32(stat & ~HF_ENABLE, d->hba.base_addr + LBA_STAT_CTL);
+#endif
/*
** Writing a zero to STAT_CTL.rf (bit 0) will clear reset signal
diff --git a/drivers/parisc/led.c b/drivers/parisc/led.c
index ff1a332d76e4..0c6e8b44b4ed 100644
--- a/drivers/parisc/led.c
+++ b/drivers/parisc/led.c
@@ -176,7 +176,7 @@ static int led_proc_open(struct inode *inode, struct file *file)
}
-static ssize_t led_proc_write(struct file *file, const char *buf,
+static ssize_t led_proc_write(struct file *file, const char __user *buf,
size_t count, loff_t *pos)
{
void *data = PDE_DATA(file_inode(file));
@@ -250,7 +250,7 @@ static int __init led_create_procfs(void)
if (led_type == -1) return -1;
- proc_pdc_root = proc_mkdir("pdc", 0);
+ proc_pdc_root = proc_mkdir("pdc", NULL);
if (!proc_pdc_root) return -1;
if (!lcd_no_led_support)
diff --git a/drivers/parisc/power.c b/drivers/parisc/power.c
index e2a3112f1c98..ebaf6867b457 100644
--- a/drivers/parisc/power.c
+++ b/drivers/parisc/power.c
@@ -95,8 +95,7 @@ static void process_shutdown(void)
/* send kill signal */
if (kill_cad_pid(SIGINT, 1)) {
/* just in case killing init process failed */
- if (pm_power_off)
- pm_power_off();
+ machine_power_off();
}
}
}
diff --git a/drivers/parport/parport_ax88796.c b/drivers/parport/parport_ax88796.c
index 2fc91edb058d..bfe97c2a8d4c 100644
--- a/drivers/parport/parport_ax88796.c
+++ b/drivers/parport/parport_ax88796.c
@@ -273,18 +273,16 @@ static int parport_ax88796_probe(struct platform_device *pdev)
{
struct device *_dev = &pdev->dev;
struct ax_drvdata *dd;
- struct parport *pp = NULL;
+ struct parport *pp;
struct resource *res;
unsigned long size;
int spacing;
int irq;
int ret;
- dd = kzalloc(sizeof(struct ax_drvdata), GFP_KERNEL);
- if (dd == NULL) {
- dev_err(_dev, "no memory for private data\n");
+ dd = kzalloc(sizeof(*dd), GFP_KERNEL);
+ if (!dd)
return -ENOMEM;
- }
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
diff --git a/drivers/parport/parport_gsc.c b/drivers/parport/parport_gsc.c
index 5f710aaaf3da..190c0a7a1c52 100644
--- a/drivers/parport/parport_gsc.c
+++ b/drivers/parport/parport_gsc.c
@@ -256,7 +256,7 @@ struct parport *parport_gsc_probe_port(unsigned long base,
}
priv->ctr = 0xc;
priv->ctr_writable = 0xff;
- priv->dma_buf = 0;
+ priv->dma_buf = NULL;
priv->dma_handle = 0;
p->base = base;
p->base_hi = base_hi;
diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c
index 489492b608cf..380916bff9e0 100644
--- a/drivers/parport/parport_pc.c
+++ b/drivers/parport/parport_pc.c
@@ -2646,6 +2646,7 @@ enum parport_pc_pci_cards {
netmos_9901,
netmos_9865,
quatech_sppxp100,
+ wch_ch382l,
};
@@ -2708,6 +2709,7 @@ static struct parport_pc_pci {
/* netmos_9901 */ { 1, { { 0, -1 }, } },
/* netmos_9865 */ { 1, { { 0, -1 }, } },
/* quatech_sppxp100 */ { 1, { { 0, 1 }, } },
+ /* wch_ch382l */ { 1, { { 2, -1 }, } },
};
static const struct pci_device_id parport_pc_pci_tbl[] = {
@@ -2797,6 +2799,8 @@ static const struct pci_device_id parport_pc_pci_tbl[] = {
/* Quatech SPPXP-100 Parallel port PCI ExpressCard */
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_SPPXP_100,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, quatech_sppxp100 },
+ /* WCH CH382L PCI-E single parallel port card */
+ { 0x1c00, 0x3050, 0x1c00, 0x3050, 0, 0, wch_ch382l },
{ 0, } /* terminate list */
};
MODULE_DEVICE_TABLE(pci, parport_pc_pci_tbl);
diff --git a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c
index e15b4845f7c6..ae9e01ef7599 100644
--- a/drivers/parport/parport_serial.c
+++ b/drivers/parport/parport_serial.c
@@ -1,30 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Support for common PCI multi-I/O cards (which is most of them)
*
* Copyright (C) 2001 Tim Waugh <twaugh@redhat.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.
- *
- *
* Multi-function PCI cards are supposed to present separate logical
* devices on the bus. A common thing to do seems to be to just use
* one logical device with lots of base address registers for both
* parallel ports and serial ports. This driver is for dealing with
* that.
- *
*/
-#include <linux/types.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/pci.h>
#include <linux/interrupt.h>
+#include <linux/module.h>
#include <linux/parport.h>
#include <linux/parport_pc.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
#include <linux/8250_pci.h>
enum parport_pc_pci_cards {
@@ -65,6 +59,7 @@ enum parport_pc_pci_cards {
wch_ch353_1s1p,
wch_ch353_2s1p,
wch_ch382_2s1p,
+ brainboxes_5s1p,
sunix_2s1p,
};
@@ -153,12 +148,10 @@ static struct parport_pc_pci cards[] = {
/* wch_ch353_1s1p*/ { 1, { { 1, -1}, } },
/* wch_ch353_2s1p*/ { 1, { { 2, -1}, } },
/* wch_ch382_2s1p*/ { 1, { { 2, -1}, } },
+ /* brainboxes_5s1p */ { 1, { { 3, -1 }, } },
/* sunix_2s1p */ { 1, { { 3, -1 }, } },
};
-#define PCI_VENDOR_ID_SUNIX 0x1fd4
-#define PCI_DEVICE_ID_SUNIX_1999 0x1999
-
static struct pci_device_id parport_serial_pci_tbl[] = {
/* PCI cards */
{ PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_110L,
@@ -261,6 +254,10 @@ static struct pci_device_id parport_serial_pci_tbl[] = {
{ 0x4348, 0x7053, 0x4348, 0x3253, 0, 0, wch_ch353_2s1p},
{ 0x1c00, 0x3250, 0x1c00, 0x3250, 0, 0, wch_ch382_2s1p},
+ /* BrainBoxes PX272/PX306 MIO card */
+ { PCI_VENDOR_ID_INTASHIELD, 0x4100,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_5s1p },
+
/*
* More SUNIX variations. At least one of these has part number
* '5079A but subdevice 0x102. That board reports 0x0708 as
@@ -504,6 +501,12 @@ static struct pciserial_board pci_parport_serial_boards[] = {
.uart_offset = 8,
.first_offset = 0xC0,
},
+ [brainboxes_5s1p] = {
+ .flags = FL_BASE2,
+ .num_ports = 5,
+ .base_baud = 921600,
+ .uart_offset = 8,
+ },
[sunix_2s1p] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 2,
@@ -527,12 +530,10 @@ static int serial_register(struct pci_dev *dev, const struct pci_device_id *id)
struct serial_private *serial;
board = &pci_parport_serial_boards[id->driver_data];
-
if (board->num_ports == 0)
return 0;
serial = pciserial_init_ports(dev, board);
-
if (IS_ERR(serial))
return PTR_ERR(serial);
@@ -561,10 +562,9 @@ static int parport_register(struct pci_dev *dev, const struct pci_device_id *id)
int irq;
if (priv->num_par == ARRAY_SIZE (priv->port)) {
- printk (KERN_WARNING
- "parport_serial: %s: only %zu parallel ports "
- "supported (%d reported)\n", pci_name (dev),
- ARRAY_SIZE(priv->port), card->numports);
+ dev_warn(&dev->dev,
+ "only %zu parallel ports supported (%d reported)\n",
+ ARRAY_SIZE(priv->port), card->numports);
break;
}
@@ -580,12 +580,12 @@ static int parport_register(struct pci_dev *dev, const struct pci_device_id *id)
irq = dev->irq;
if (irq == IRQ_NONE) {
dev_dbg(&dev->dev,
- "PCI parallel port detected: I/O at %#lx(%#lx)\n",
+ "PCI parallel port detected: I/O at %#lx(%#lx)\n",
io_lo, io_hi);
irq = PARPORT_IRQ_NONE;
} else {
dev_dbg(&dev->dev,
- "PCI parallel port detected: I/O at %#lx(%#lx), IRQ %d\n",
+ "PCI parallel port detected: I/O at %#lx(%#lx), IRQ %d\n",
io_lo, io_hi, irq);
}
port = parport_pc_probe_port (io_lo, io_hi, irq,
@@ -608,28 +608,26 @@ static int parport_serial_pci_probe(struct pci_dev *dev,
struct parport_serial_private *priv;
int err;
- priv = kzalloc (sizeof *priv, GFP_KERNEL);
+ priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
+
pci_set_drvdata (dev, priv);
- err = pci_enable_device (dev);
- if (err) {
- kfree (priv);
+ err = pcim_enable_device(dev);
+ if (err)
return err;
- }
- if (parport_register (dev, id)) {
- kfree (priv);
- return -ENODEV;
- }
+ err = parport_register(dev, id);
+ if (err)
+ return err;
- if (serial_register (dev, id)) {
+ err = serial_register(dev, id);
+ if (err) {
int i;
for (i = 0; i < priv->num_par; i++)
parport_pc_unregister_port (priv->port[i]);
- kfree (priv);
- return -ENODEV;
+ return err;
}
return 0;
@@ -648,78 +646,47 @@ static void parport_serial_pci_remove(struct pci_dev *dev)
for (i = 0; i < priv->num_par; i++)
parport_pc_unregister_port (priv->port[i]);
- kfree (priv);
return;
}
-#ifdef CONFIG_PM
-static int parport_serial_pci_suspend(struct pci_dev *dev, pm_message_t state)
+static int __maybe_unused parport_serial_pci_suspend(struct device *dev)
{
- struct parport_serial_private *priv = pci_get_drvdata(dev);
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct parport_serial_private *priv = pci_get_drvdata(pdev);
if (priv->serial)
pciserial_suspend_ports(priv->serial);
/* FIXME: What about parport? */
-
- pci_save_state(dev);
- pci_set_power_state(dev, pci_choose_state(dev, state));
return 0;
}
-static int parport_serial_pci_resume(struct pci_dev *dev)
+static int __maybe_unused parport_serial_pci_resume(struct device *dev)
{
- struct parport_serial_private *priv = pci_get_drvdata(dev);
- int err;
-
- pci_set_power_state(dev, PCI_D0);
- pci_restore_state(dev);
-
- /*
- * The device may have been disabled. Re-enable it.
- */
- err = pci_enable_device(dev);
- if (err) {
- printk(KERN_ERR "parport_serial: %s: error enabling "
- "device for resume (%d)\n", pci_name(dev), err);
- return err;
- }
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct parport_serial_private *priv = pci_get_drvdata(pdev);
if (priv->serial)
pciserial_resume_ports(priv->serial);
/* FIXME: What about parport? */
-
return 0;
}
-#endif
+
+static SIMPLE_DEV_PM_OPS(parport_serial_pm_ops,
+ parport_serial_pci_suspend, parport_serial_pci_resume);
static struct pci_driver parport_serial_pci_driver = {
.name = "parport_serial",
.id_table = parport_serial_pci_tbl,
.probe = parport_serial_pci_probe,
.remove = parport_serial_pci_remove,
-#ifdef CONFIG_PM
- .suspend = parport_serial_pci_suspend,
- .resume = parport_serial_pci_resume,
-#endif
+ .driver = {
+ .pm = &parport_serial_pm_ops,
+ },
};
-
-
-static int __init parport_serial_init (void)
-{
- return pci_register_driver (&parport_serial_pci_driver);
-}
-
-static void __exit parport_serial_exit (void)
-{
- pci_unregister_driver (&parport_serial_pci_driver);
- return;
-}
+module_pci_driver(parport_serial_pci_driver);
MODULE_AUTHOR("Tim Waugh <twaugh@redhat.com>");
MODULE_DESCRIPTION("Driver for common parallel+serial multi-I/O PCI cards");
MODULE_LICENSE("GPL");
-
-module_init(parport_serial_init);
-module_exit(parport_serial_exit);
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 941970936840..952addc7bacf 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -1,61 +1,40 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the PCI bus specific drivers.
-#
-obj-$(CONFIG_PCI) += access.o bus.o probe.o host-bridge.o remove.o pci.o \
- pci-driver.o search.o pci-sysfs.o rom.o setup-res.o \
- irq.o vpd.o setup-bus.o vc.o mmap.o setup-irq.o
+obj-$(CONFIG_PCI) += access.o bus.o probe.o host-bridge.o \
+ remove.o pci.o pci-driver.o search.o \
+ pci-sysfs.o rom.o setup-res.o irq.o vpd.o \
+ setup-bus.o vc.o mmap.o setup-irq.o
ifdef CONFIG_PCI
-obj-$(CONFIG_PROC_FS) += proc.o
-obj-$(CONFIG_SYSFS) += slot.o
-obj-$(CONFIG_OF) += of.o
+obj-$(CONFIG_PROC_FS) += proc.o
+obj-$(CONFIG_SYSFS) += slot.o
+obj-$(CONFIG_OF) += of.o
endif
-obj-$(CONFIG_PCI_QUIRKS) += quirks.o
-
-# Build PCI Express stuff if needed
-obj-$(CONFIG_PCIEPORTBUS) += pcie/
-
-# Build the PCI Hotplug drivers if we were asked to
-obj-$(CONFIG_HOTPLUG_PCI) += hotplug/
-
-# Build the PCI MSI interrupt support
-obj-$(CONFIG_PCI_MSI) += msi.o
-
-obj-$(CONFIG_PCI_ATS) += ats.o
-obj-$(CONFIG_PCI_IOV) += iov.o
-
-#
-# ACPI Related PCI FW Functions
-# ACPI _DSM provided firmware instance and string name
-#
-obj-$(CONFIG_ACPI) += pci-acpi.o
-
-# SMBIOS provided firmware instance and labels
-obj-$(CONFIG_PCI_LABEL) += pci-label.o
-
-# Intel MID platform PM support
-obj-$(CONFIG_X86_INTEL_MID) += pci-mid.o
-
-obj-$(CONFIG_PCI_SYSCALL) += syscall.o
-
-obj-$(CONFIG_PCI_STUB) += pci-stub.o
-
-obj-$(CONFIG_PCI_ECAM) += ecam.o
-
+obj-$(CONFIG_PCI_QUIRKS) += quirks.o
+obj-$(CONFIG_PCIEPORTBUS) += pcie/
+obj-$(CONFIG_HOTPLUG_PCI) += hotplug/
+obj-$(CONFIG_PCI_MSI) += msi.o
+obj-$(CONFIG_PCI_ATS) += ats.o
+obj-$(CONFIG_PCI_IOV) += iov.o
+obj-$(CONFIG_ACPI) += pci-acpi.o
+obj-$(CONFIG_PCI_LABEL) += pci-label.o
+obj-$(CONFIG_X86_INTEL_MID) += pci-mid.o
+obj-$(CONFIG_PCI_SYSCALL) += syscall.o
+obj-$(CONFIG_PCI_STUB) += pci-stub.o
+obj-$(CONFIG_PCI_ECAM) += ecam.o
obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o
-ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG
-
-# PCI host controller drivers
-obj-y += host/
-obj-y += switch/
+obj-y += host/
+obj-y += switch/
+# Endpoint library must be initialized before its users
obj-$(CONFIG_PCI_ENDPOINT) += endpoint/
-# Endpoint library must be initialized before its users
obj-$(CONFIG_PCIE_CADENCE) += cadence/
# pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW
obj-y += dwc/
+
+ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG
diff --git a/drivers/pci/access.c b/drivers/pci/access.c
index 5e9a9822d9d4..a3ad2fe185b9 100644
--- a/drivers/pci/access.c
+++ b/drivers/pci/access.c
@@ -1,8 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
-#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/module.h>
-#include <linux/sched/signal.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/wait.h>
@@ -17,9 +15,9 @@
DEFINE_RAW_SPINLOCK(pci_lock);
/*
- * Wrappers for all PCI configuration access functions. They just check
- * alignment, do locking and call the low-level functions pointed to
- * by pci_dev->ops.
+ * Wrappers for all PCI configuration access functions. They just check
+ * alignment, do locking and call the low-level functions pointed to
+ * by pci_dev->ops.
*/
#define PCI_byte_BAD 0
@@ -264,372 +262,6 @@ PCI_USER_WRITE_CONFIG(byte, u8)
PCI_USER_WRITE_CONFIG(word, u16)
PCI_USER_WRITE_CONFIG(dword, u32)
-/* VPD access through PCI 2.2+ VPD capability */
-
-/**
- * pci_read_vpd - Read one entry from Vital Product Data
- * @dev: pci device struct
- * @pos: offset in vpd space
- * @count: number of bytes to read
- * @buf: pointer to where to store result
- */
-ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf)
-{
- if (!dev->vpd || !dev->vpd->ops)
- return -ENODEV;
- return dev->vpd->ops->read(dev, pos, count, buf);
-}
-EXPORT_SYMBOL(pci_read_vpd);
-
-/**
- * pci_write_vpd - Write entry to Vital Product Data
- * @dev: pci device struct
- * @pos: offset in vpd space
- * @count: number of bytes to write
- * @buf: buffer containing write data
- */
-ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf)
-{
- if (!dev->vpd || !dev->vpd->ops)
- return -ENODEV;
- return dev->vpd->ops->write(dev, pos, count, buf);
-}
-EXPORT_SYMBOL(pci_write_vpd);
-
-/**
- * pci_set_vpd_size - Set size of Vital Product Data space
- * @dev: pci device struct
- * @len: size of vpd space
- */
-int pci_set_vpd_size(struct pci_dev *dev, size_t len)
-{
- if (!dev->vpd || !dev->vpd->ops)
- return -ENODEV;
- return dev->vpd->ops->set_size(dev, len);
-}
-EXPORT_SYMBOL(pci_set_vpd_size);
-
-#define PCI_VPD_MAX_SIZE (PCI_VPD_ADDR_MASK + 1)
-
-/**
- * pci_vpd_size - determine actual size of Vital Product Data
- * @dev: pci device struct
- * @old_size: current assumed size, also maximum allowed size
- */
-static size_t pci_vpd_size(struct pci_dev *dev, size_t old_size)
-{
- size_t off = 0;
- unsigned char header[1+2]; /* 1 byte tag, 2 bytes length */
-
- while (off < old_size &&
- pci_read_vpd(dev, off, 1, header) == 1) {
- unsigned char tag;
-
- if (header[0] & PCI_VPD_LRDT) {
- /* Large Resource Data Type Tag */
- tag = pci_vpd_lrdt_tag(header);
- /* Only read length from known tag items */
- if ((tag == PCI_VPD_LTIN_ID_STRING) ||
- (tag == PCI_VPD_LTIN_RO_DATA) ||
- (tag == PCI_VPD_LTIN_RW_DATA)) {
- if (pci_read_vpd(dev, off+1, 2,
- &header[1]) != 2) {
- pci_warn(dev, "invalid large VPD tag %02x size at offset %zu",
- tag, off + 1);
- return 0;
- }
- off += PCI_VPD_LRDT_TAG_SIZE +
- pci_vpd_lrdt_size(header);
- }
- } else {
- /* Short Resource Data Type Tag */
- off += PCI_VPD_SRDT_TAG_SIZE +
- pci_vpd_srdt_size(header);
- tag = pci_vpd_srdt_tag(header);
- }
-
- if (tag == PCI_VPD_STIN_END) /* End tag descriptor */
- return off;
-
- if ((tag != PCI_VPD_LTIN_ID_STRING) &&
- (tag != PCI_VPD_LTIN_RO_DATA) &&
- (tag != PCI_VPD_LTIN_RW_DATA)) {
- pci_warn(dev, "invalid %s VPD tag %02x at offset %zu",
- (header[0] & PCI_VPD_LRDT) ? "large" : "short",
- tag, off);
- return 0;
- }
- }
- return 0;
-}
-
-/*
- * Wait for last operation to complete.
- * This code has to spin since there is no other notification from the PCI
- * hardware. Since the VPD is often implemented by serial attachment to an
- * EEPROM, it may take many milliseconds to complete.
- *
- * Returns 0 on success, negative values indicate error.
- */
-static int pci_vpd_wait(struct pci_dev *dev)
-{
- struct pci_vpd *vpd = dev->vpd;
- unsigned long timeout = jiffies + msecs_to_jiffies(125);
- unsigned long max_sleep = 16;
- u16 status;
- int ret;
-
- if (!vpd->busy)
- return 0;
-
- while (time_before(jiffies, timeout)) {
- ret = pci_user_read_config_word(dev, vpd->cap + PCI_VPD_ADDR,
- &status);
- if (ret < 0)
- return ret;
-
- if ((status & PCI_VPD_ADDR_F) == vpd->flag) {
- vpd->busy = 0;
- return 0;
- }
-
- if (fatal_signal_pending(current))
- return -EINTR;
-
- usleep_range(10, max_sleep);
- if (max_sleep < 1024)
- max_sleep *= 2;
- }
-
- pci_warn(dev, "VPD access failed. This is likely a firmware bug on this device. Contact the card vendor for a firmware update\n");
- return -ETIMEDOUT;
-}
-
-static ssize_t pci_vpd_read(struct pci_dev *dev, loff_t pos, size_t count,
- void *arg)
-{
- struct pci_vpd *vpd = dev->vpd;
- int ret;
- loff_t end = pos + count;
- u8 *buf = arg;
-
- if (pos < 0)
- return -EINVAL;
-
- if (!vpd->valid) {
- vpd->valid = 1;
- vpd->len = pci_vpd_size(dev, vpd->len);
- }
-
- if (vpd->len == 0)
- return -EIO;
-
- if (pos > vpd->len)
- return 0;
-
- if (end > vpd->len) {
- end = vpd->len;
- count = end - pos;
- }
-
- if (mutex_lock_killable(&vpd->lock))
- return -EINTR;
-
- ret = pci_vpd_wait(dev);
- if (ret < 0)
- goto out;
-
- while (pos < end) {
- u32 val;
- unsigned int i, skip;
-
- ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR,
- pos & ~3);
- if (ret < 0)
- break;
- vpd->busy = 1;
- vpd->flag = PCI_VPD_ADDR_F;
- ret = pci_vpd_wait(dev);
- if (ret < 0)
- break;
-
- ret = pci_user_read_config_dword(dev, vpd->cap + PCI_VPD_DATA, &val);
- if (ret < 0)
- break;
-
- skip = pos & 3;
- for (i = 0; i < sizeof(u32); i++) {
- if (i >= skip) {
- *buf++ = val;
- if (++pos == end)
- break;
- }
- val >>= 8;
- }
- }
-out:
- mutex_unlock(&vpd->lock);
- return ret ? ret : count;
-}
-
-static ssize_t pci_vpd_write(struct pci_dev *dev, loff_t pos, size_t count,
- const void *arg)
-{
- struct pci_vpd *vpd = dev->vpd;
- const u8 *buf = arg;
- loff_t end = pos + count;
- int ret = 0;
-
- if (pos < 0 || (pos & 3) || (count & 3))
- return -EINVAL;
-
- if (!vpd->valid) {
- vpd->valid = 1;
- vpd->len = pci_vpd_size(dev, vpd->len);
- }
-
- if (vpd->len == 0)
- return -EIO;
-
- if (end > vpd->len)
- return -EINVAL;
-
- if (mutex_lock_killable(&vpd->lock))
- return -EINTR;
-
- ret = pci_vpd_wait(dev);
- if (ret < 0)
- goto out;
-
- while (pos < end) {
- u32 val;
-
- val = *buf++;
- val |= *buf++ << 8;
- val |= *buf++ << 16;
- val |= *buf++ << 24;
-
- ret = pci_user_write_config_dword(dev, vpd->cap + PCI_VPD_DATA, val);
- if (ret < 0)
- break;
- ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR,
- pos | PCI_VPD_ADDR_F);
- if (ret < 0)
- break;
-
- vpd->busy = 1;
- vpd->flag = 0;
- ret = pci_vpd_wait(dev);
- if (ret < 0)
- break;
-
- pos += sizeof(u32);
- }
-out:
- mutex_unlock(&vpd->lock);
- return ret ? ret : count;
-}
-
-static int pci_vpd_set_size(struct pci_dev *dev, size_t len)
-{
- struct pci_vpd *vpd = dev->vpd;
-
- if (len == 0 || len > PCI_VPD_MAX_SIZE)
- return -EIO;
-
- vpd->valid = 1;
- vpd->len = len;
-
- return 0;
-}
-
-static const struct pci_vpd_ops pci_vpd_ops = {
- .read = pci_vpd_read,
- .write = pci_vpd_write,
- .set_size = pci_vpd_set_size,
-};
-
-static ssize_t pci_vpd_f0_read(struct pci_dev *dev, loff_t pos, size_t count,
- void *arg)
-{
- struct pci_dev *tdev = pci_get_slot(dev->bus,
- PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
- ssize_t ret;
-
- if (!tdev)
- return -ENODEV;
-
- ret = pci_read_vpd(tdev, pos, count, arg);
- pci_dev_put(tdev);
- return ret;
-}
-
-static ssize_t pci_vpd_f0_write(struct pci_dev *dev, loff_t pos, size_t count,
- const void *arg)
-{
- struct pci_dev *tdev = pci_get_slot(dev->bus,
- PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
- ssize_t ret;
-
- if (!tdev)
- return -ENODEV;
-
- ret = pci_write_vpd(tdev, pos, count, arg);
- pci_dev_put(tdev);
- return ret;
-}
-
-static int pci_vpd_f0_set_size(struct pci_dev *dev, size_t len)
-{
- struct pci_dev *tdev = pci_get_slot(dev->bus,
- PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
- int ret;
-
- if (!tdev)
- return -ENODEV;
-
- ret = pci_set_vpd_size(tdev, len);
- pci_dev_put(tdev);
- return ret;
-}
-
-static const struct pci_vpd_ops pci_vpd_f0_ops = {
- .read = pci_vpd_f0_read,
- .write = pci_vpd_f0_write,
- .set_size = pci_vpd_f0_set_size,
-};
-
-int pci_vpd_init(struct pci_dev *dev)
-{
- struct pci_vpd *vpd;
- u8 cap;
-
- cap = pci_find_capability(dev, PCI_CAP_ID_VPD);
- if (!cap)
- return -ENODEV;
-
- vpd = kzalloc(sizeof(*vpd), GFP_ATOMIC);
- if (!vpd)
- return -ENOMEM;
-
- vpd->len = PCI_VPD_MAX_SIZE;
- if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0)
- vpd->ops = &pci_vpd_f0_ops;
- else
- vpd->ops = &pci_vpd_ops;
- mutex_init(&vpd->lock);
- vpd->cap = cap;
- vpd->busy = 0;
- vpd->valid = 0;
- dev->vpd = vpd;
- return 0;
-}
-
-void pci_vpd_release(struct pci_dev *dev)
-{
- kfree(dev->vpd);
-}
-
/**
* pci_cfg_access_lock - Lock PCI config reads/writes
* @dev: pci device struct
@@ -686,8 +318,10 @@ void pci_cfg_access_unlock(struct pci_dev *dev)
raw_spin_lock_irqsave(&pci_lock, flags);
- /* This indicates a problem in the caller, but we don't need
- * to kill them, unlike a double-block above. */
+ /*
+ * This indicates a problem in the caller, but we don't need
+ * to kill them, unlike a double-block above.
+ */
WARN_ON(!dev->block_cfg_access);
dev->block_cfg_access = 0;
diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c
index 6ad80a1fd5a7..89305b569d3d 100644
--- a/drivers/pci/ats.c
+++ b/drivers/pci/ats.c
@@ -1,14 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * drivers/pci/ats.c
- *
- * Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com>
- * Copyright (C) 2011 Advanced Micro Devices,
- *
- * PCI Express I/O Virtualization (IOV) support.
+ * PCI Express I/O Virtualization (IOV) support
* Address Translation Service 1.0
* Page Request Interface added by Joerg Roedel <joerg.roedel@amd.com>
* PASID support added by Joerg Roedel <joerg.roedel@amd.com>
+ *
+ * Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com>
+ * Copyright (C) 2011 Advanced Micro Devices,
*/
#include <linux/export.h>
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 737d1c52f002..bc2ded4c451f 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -1,7 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * drivers/pci/bus.c
- *
* From setup-res.c, by:
* Dave Rusling (david.rusling@reo.mts.dec.com)
* David Mosberger (davidm@cs.arizona.edu)
diff --git a/drivers/pci/cadence/pcie-cadence-ep.c b/drivers/pci/cadence/pcie-cadence-ep.c
index 3c3a97743453..3d8283e450a9 100644
--- a/drivers/pci/cadence/pcie-cadence-ep.c
+++ b/drivers/pci/cadence/pcie-cadence-ep.c
@@ -77,16 +77,19 @@ static int cdns_pcie_ep_write_header(struct pci_epc *epc, u8 fn,
return 0;
}
-static int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, enum pci_barno bar,
- dma_addr_t bar_phys, size_t size, int flags)
+static int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 fn,
+ struct pci_epf_bar *epf_bar)
{
struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
struct cdns_pcie *pcie = &ep->pcie;
+ dma_addr_t bar_phys = epf_bar->phys_addr;
+ enum pci_barno bar = epf_bar->barno;
+ int flags = epf_bar->flags;
u32 addr0, addr1, reg, cfg, b, aperture, ctrl;
u64 sz;
/* BAR size is 2^(aperture + 7) */
- sz = max_t(size_t, size, CDNS_PCIE_EP_MIN_APERTURE);
+ sz = max_t(size_t, epf_bar->size, CDNS_PCIE_EP_MIN_APERTURE);
/*
* roundup_pow_of_two() returns an unsigned long, which is not suited
* for 64bit values.
@@ -103,6 +106,9 @@ static int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, enum pci_barno bar,
if (is_64bits && (bar & 1))
return -EINVAL;
+ if (is_64bits && !(flags & PCI_BASE_ADDRESS_MEM_TYPE_64))
+ epf_bar->flags |= PCI_BASE_ADDRESS_MEM_TYPE_64;
+
if (is_64bits && is_prefetch)
ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_64BITS;
else if (is_prefetch)
@@ -139,10 +145,11 @@ static int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, enum pci_barno bar,
}
static void cdns_pcie_ep_clear_bar(struct pci_epc *epc, u8 fn,
- enum pci_barno bar)
+ struct pci_epf_bar *epf_bar)
{
struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
struct cdns_pcie *pcie = &ep->pcie;
+ enum pci_barno bar = epf_bar->barno;
u32 reg, cfg, b, ctrl;
if (bar < BAR_4) {
diff --git a/drivers/pci/dwc/Kconfig b/drivers/pci/dwc/Kconfig
index 0f666b1ce289..2f3f5c50aa48 100644
--- a/drivers/pci/dwc/Kconfig
+++ b/drivers/pci/dwc/Kconfig
@@ -176,6 +176,7 @@ config PCIE_ARTPEC6_EP
config PCIE_KIRIN
depends on OF && ARM64
bool "HiSilicon Kirin series SoCs PCIe controllers"
+ depends on PCI_MSI_IRQ_DOMAIN
depends on PCI
select PCIEPORTBUS
select PCIE_DW_HOST
diff --git a/drivers/pci/dwc/pci-exynos.c b/drivers/pci/dwc/pci-exynos.c
index ca6278113936..4cc1e5df8c79 100644
--- a/drivers/pci/dwc/pci-exynos.c
+++ b/drivers/pci/dwc/pci-exynos.c
@@ -294,15 +294,6 @@ static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg)
return IRQ_HANDLED;
}
-static irqreturn_t exynos_pcie_msi_irq_handler(int irq, void *arg)
-{
- struct exynos_pcie *ep = arg;
- struct dw_pcie *pci = ep->pci;
- struct pcie_port *pp = &pci->pp;
-
- return dw_handle_msi_irq(pp);
-}
-
static void exynos_pcie_msi_init(struct exynos_pcie *ep)
{
struct dw_pcie *pci = ep->pci;
@@ -428,15 +419,6 @@ static int __init exynos_add_pcie_port(struct exynos_pcie *ep,
dev_err(dev, "failed to get msi irq\n");
return pp->msi_irq;
}
-
- ret = devm_request_irq(dev, pp->msi_irq,
- exynos_pcie_msi_irq_handler,
- IRQF_SHARED | IRQF_NO_THREAD,
- "exynos-pcie", ep);
- if (ret) {
- dev_err(dev, "failed to request msi irq\n");
- return ret;
- }
}
pp->root_bus_nr = -1;
diff --git a/drivers/pci/dwc/pci-imx6.c b/drivers/pci/dwc/pci-imx6.c
index 4fddbd08b089..4818ef875f8a 100644
--- a/drivers/pci/dwc/pci-imx6.c
+++ b/drivers/pci/dwc/pci-imx6.c
@@ -542,15 +542,6 @@ static int imx6_pcie_wait_for_speed_change(struct imx6_pcie *imx6_pcie)
return -EINVAL;
}
-static irqreturn_t imx6_pcie_msi_handler(int irq, void *arg)
-{
- struct imx6_pcie *imx6_pcie = arg;
- struct dw_pcie *pci = imx6_pcie->pci;
- struct pcie_port *pp = &pci->pp;
-
- return dw_handle_msi_irq(pp);
-}
-
static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
{
struct dw_pcie *pci = imx6_pcie->pci;
@@ -674,15 +665,6 @@ static int imx6_add_pcie_port(struct imx6_pcie *imx6_pcie,
dev_err(dev, "failed to get MSI irq\n");
return -ENODEV;
}
-
- ret = devm_request_irq(dev, pp->msi_irq,
- imx6_pcie_msi_handler,
- IRQF_SHARED | IRQF_NO_THREAD,
- "mx6-pcie-msi", imx6_pcie);
- if (ret) {
- dev_err(dev, "failed to request MSI irq\n");
- return ret;
- }
}
pp->root_bus_nr = -1;
diff --git a/drivers/pci/dwc/pci-keystone-dw.c b/drivers/pci/dwc/pci-keystone-dw.c
index 99a0e7076221..0682213328e9 100644
--- a/drivers/pci/dwc/pci-keystone-dw.c
+++ b/drivers/pci/dwc/pci-keystone-dw.c
@@ -120,20 +120,15 @@ void ks_dw_pcie_handle_msi_irq(struct keystone_pcie *ks_pcie, int offset)
}
}
-static void ks_dw_pcie_msi_irq_ack(struct irq_data *d)
+void ks_dw_pcie_msi_irq_ack(int irq, struct pcie_port *pp)
{
- u32 offset, reg_offset, bit_pos;
+ u32 reg_offset, bit_pos;
struct keystone_pcie *ks_pcie;
- struct msi_desc *msi;
- struct pcie_port *pp;
struct dw_pcie *pci;
- msi = irq_data_get_msi_desc(d);
- pp = (struct pcie_port *) msi_desc_to_pci_sysdata(msi);
pci = to_dw_pcie_from_pp(pp);
ks_pcie = to_keystone_pcie(pci);
- offset = d->irq - irq_linear_revmap(pp->irq_domain, 0);
- update_reg_offset_bit_pos(offset, &reg_offset, &bit_pos);
+ update_reg_offset_bit_pos(irq, &reg_offset, &bit_pos);
ks_dw_app_writel(ks_pcie, MSI0_IRQ_STATUS + (reg_offset << 4),
BIT(bit_pos));
@@ -162,85 +157,9 @@ void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq)
BIT(bit_pos));
}
-static void ks_dw_pcie_msi_irq_mask(struct irq_data *d)
-{
- struct msi_desc *msi;
- struct pcie_port *pp;
- u32 offset;
-
- msi = irq_data_get_msi_desc(d);
- pp = (struct pcie_port *) msi_desc_to_pci_sysdata(msi);
- offset = d->irq - irq_linear_revmap(pp->irq_domain, 0);
-
- /* Mask the end point if PVM implemented */
- if (IS_ENABLED(CONFIG_PCI_MSI)) {
- if (msi->msi_attrib.maskbit)
- pci_msi_mask_irq(d);
- }
-
- ks_dw_pcie_msi_clear_irq(pp, offset);
-}
-
-static void ks_dw_pcie_msi_irq_unmask(struct irq_data *d)
-{
- struct msi_desc *msi;
- struct pcie_port *pp;
- u32 offset;
-
- msi = irq_data_get_msi_desc(d);
- pp = (struct pcie_port *) msi_desc_to_pci_sysdata(msi);
- offset = d->irq - irq_linear_revmap(pp->irq_domain, 0);
-
- /* Mask the end point if PVM implemented */
- if (IS_ENABLED(CONFIG_PCI_MSI)) {
- if (msi->msi_attrib.maskbit)
- pci_msi_unmask_irq(d);
- }
-
- ks_dw_pcie_msi_set_irq(pp, offset);
-}
-
-static struct irq_chip ks_dw_pcie_msi_irq_chip = {
- .name = "Keystone-PCIe-MSI-IRQ",
- .irq_ack = ks_dw_pcie_msi_irq_ack,
- .irq_mask = ks_dw_pcie_msi_irq_mask,
- .irq_unmask = ks_dw_pcie_msi_irq_unmask,
-};
-
-static int ks_dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq,
- irq_hw_number_t hwirq)
+int ks_dw_pcie_msi_host_init(struct pcie_port *pp)
{
- irq_set_chip_and_handler(irq, &ks_dw_pcie_msi_irq_chip,
- handle_level_irq);
- irq_set_chip_data(irq, domain->host_data);
-
- return 0;
-}
-
-static const struct irq_domain_ops ks_dw_pcie_msi_domain_ops = {
- .map = ks_dw_pcie_msi_map,
-};
-
-int ks_dw_pcie_msi_host_init(struct pcie_port *pp, struct msi_controller *chip)
-{
- struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
- struct keystone_pcie *ks_pcie = to_keystone_pcie(pci);
- struct device *dev = pci->dev;
- int i;
-
- pp->irq_domain = irq_domain_add_linear(ks_pcie->msi_intc_np,
- MAX_MSI_IRQS,
- &ks_dw_pcie_msi_domain_ops,
- chip);
- if (!pp->irq_domain) {
- dev_err(dev, "irq domain init failed\n");
- return -ENXIO;
- }
-
- for (i = 0; i < MAX_MSI_IRQS; i++)
- irq_create_mapping(pp->irq_domain, i);
-
- return 0;
+ return dw_pcie_allocate_domains(pp);
}
void ks_dw_pcie_enable_legacy_irqs(struct keystone_pcie *ks_pcie)
diff --git a/drivers/pci/dwc/pci-keystone.c b/drivers/pci/dwc/pci-keystone.c
index d4f8ab90c018..d55ae0716adf 100644
--- a/drivers/pci/dwc/pci-keystone.c
+++ b/drivers/pci/dwc/pci-keystone.c
@@ -297,6 +297,7 @@ static const struct dw_pcie_host_ops keystone_pcie_host_ops = {
.msi_clear_irq = ks_dw_pcie_msi_clear_irq,
.get_msi_addr = ks_dw_pcie_get_msi_addr,
.msi_host_init = ks_dw_pcie_msi_host_init,
+ .msi_irq_ack = ks_dw_pcie_msi_irq_ack,
.scan_bus = ks_dw_pcie_v3_65_scan_bus,
};
diff --git a/drivers/pci/dwc/pci-keystone.h b/drivers/pci/dwc/pci-keystone.h
index 1dd1f3ef98e7..8a13da391543 100644
--- a/drivers/pci/dwc/pci-keystone.h
+++ b/drivers/pci/dwc/pci-keystone.h
@@ -49,9 +49,9 @@ int ks_dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
unsigned int devfn, int where, int size, u32 *val);
void ks_dw_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie);
void ks_dw_pcie_initiate_link_train(struct keystone_pcie *ks_pcie);
+void ks_dw_pcie_msi_irq_ack(int i, struct pcie_port *pp);
void ks_dw_pcie_msi_set_irq(struct pcie_port *pp, int irq);
void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq);
void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp);
-int ks_dw_pcie_msi_host_init(struct pcie_port *pp,
- struct msi_controller *chip);
+int ks_dw_pcie_msi_host_init(struct pcie_port *pp);
int ks_dw_pcie_link_up(struct dw_pcie *pci);
diff --git a/drivers/pci/dwc/pci-layerscape.c b/drivers/pci/dwc/pci-layerscape.c
index a7b4159631ae..3724d3ef7008 100644
--- a/drivers/pci/dwc/pci-layerscape.c
+++ b/drivers/pci/dwc/pci-layerscape.c
@@ -182,8 +182,7 @@ static int ls1021_pcie_host_init(struct pcie_port *pp)
return ls_pcie_host_init(pp);
}
-static int ls_pcie_msi_host_init(struct pcie_port *pp,
- struct msi_controller *chip)
+static int ls_pcie_msi_host_init(struct pcie_port *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
struct device *dev = pci->dev;
diff --git a/drivers/pci/dwc/pcie-artpec6.c b/drivers/pci/dwc/pcie-artpec6.c
index 93b3df9ed1b5..e66cede2b5b7 100644
--- a/drivers/pci/dwc/pcie-artpec6.c
+++ b/drivers/pci/dwc/pcie-artpec6.c
@@ -383,15 +383,6 @@ static const struct dw_pcie_host_ops artpec6_pcie_host_ops = {
.host_init = artpec6_pcie_host_init,
};
-static irqreturn_t artpec6_pcie_msi_handler(int irq, void *arg)
-{
- struct artpec6_pcie *artpec6_pcie = arg;
- struct dw_pcie *pci = artpec6_pcie->pci;
- struct pcie_port *pp = &pci->pp;
-
- return dw_handle_msi_irq(pp);
-}
-
static int artpec6_add_pcie_port(struct artpec6_pcie *artpec6_pcie,
struct platform_device *pdev)
{
@@ -406,15 +397,6 @@ static int artpec6_add_pcie_port(struct artpec6_pcie *artpec6_pcie,
dev_err(dev, "failed to get MSI irq\n");
return pp->msi_irq;
}
-
- ret = devm_request_irq(dev, pp->msi_irq,
- artpec6_pcie_msi_handler,
- IRQF_SHARED | IRQF_NO_THREAD,
- "artpec6-pcie-msi", artpec6_pcie);
- if (ret) {
- dev_err(dev, "failed to request MSI irq\n");
- return ret;
- }
}
pp->root_bus_nr = -1;
diff --git a/drivers/pci/dwc/pcie-designware-ep.c b/drivers/pci/dwc/pcie-designware-ep.c
index 3a6feeff5f5b..f07678bf7cfc 100644
--- a/drivers/pci/dwc/pcie-designware-ep.c
+++ b/drivers/pci/dwc/pcie-designware-ep.c
@@ -19,7 +19,8 @@ void dw_pcie_ep_linkup(struct dw_pcie_ep *ep)
pci_epc_linkup(epc);
}
-void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar)
+static void __dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar,
+ int flags)
{
u32 reg;
@@ -27,9 +28,18 @@ void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar)
dw_pcie_dbi_ro_wr_en(pci);
dw_pcie_writel_dbi2(pci, reg, 0x0);
dw_pcie_writel_dbi(pci, reg, 0x0);
+ if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+ dw_pcie_writel_dbi2(pci, reg + 4, 0x0);
+ dw_pcie_writel_dbi(pci, reg + 4, 0x0);
+ }
dw_pcie_dbi_ro_wr_dis(pci);
}
+void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar)
+{
+ __dw_pcie_ep_reset_bar(pci, bar, 0);
+}
+
static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no,
struct pci_epf_header *hdr)
{
@@ -104,25 +114,28 @@ static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, phys_addr_t phys_addr,
}
static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no,
- enum pci_barno bar)
+ struct pci_epf_bar *epf_bar)
{
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+ enum pci_barno bar = epf_bar->barno;
u32 atu_index = ep->bar_to_atu[bar];
- dw_pcie_ep_reset_bar(pci, bar);
+ __dw_pcie_ep_reset_bar(pci, bar, epf_bar->flags);
dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_INBOUND);
clear_bit(atu_index, ep->ib_window_map);
}
static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no,
- enum pci_barno bar,
- dma_addr_t bar_phys, size_t size, int flags)
+ struct pci_epf_bar *epf_bar)
{
int ret;
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+ enum pci_barno bar = epf_bar->barno;
+ size_t size = epf_bar->size;
+ int flags = epf_bar->flags;
enum dw_pcie_as_type as_type;
u32 reg = PCI_BASE_ADDRESS_0 + (4 * bar);
@@ -131,13 +144,20 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no,
else
as_type = DW_PCIE_AS_IO;
- ret = dw_pcie_ep_inbound_atu(ep, bar, bar_phys, as_type);
+ ret = dw_pcie_ep_inbound_atu(ep, bar, epf_bar->phys_addr, as_type);
if (ret)
return ret;
dw_pcie_dbi_ro_wr_en(pci);
- dw_pcie_writel_dbi2(pci, reg, size - 1);
+
+ dw_pcie_writel_dbi2(pci, reg, lower_32_bits(size - 1));
dw_pcie_writel_dbi(pci, reg, flags);
+
+ if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+ dw_pcie_writel_dbi2(pci, reg + 4, upper_32_bits(size - 1));
+ dw_pcie_writel_dbi(pci, reg + 4, 0);
+ }
+
dw_pcie_dbi_ro_wr_dis(pci);
return 0;
@@ -322,7 +342,7 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
struct device_node *np = dev->of_node;
if (!pci->dbi_base || !pci->dbi_base2) {
- dev_err(dev, "dbi_base/deb_base2 is not populated\n");
+ dev_err(dev, "dbi_base/dbi_base2 is not populated\n");
return -EINVAL;
}
diff --git a/drivers/pci/dwc/pcie-designware-host.c b/drivers/pci/dwc/pcie-designware-host.c
index dc9303abda42..6c409079d514 100644
--- a/drivers/pci/dwc/pcie-designware-host.c
+++ b/drivers/pci/dwc/pcie-designware-host.c
@@ -8,6 +8,7 @@
* Author: Jingoo Han <jg1.han@samsung.com>
*/
+#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
#include <linux/of_address.h>
#include <linux/of_pci.h>
@@ -42,22 +43,46 @@ static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
return dw_pcie_write(pci->dbi_base + where, size, val);
}
-static struct irq_chip dw_msi_irq_chip = {
+static void dw_msi_ack_irq(struct irq_data *d)
+{
+ irq_chip_ack_parent(d);
+}
+
+static void dw_msi_mask_irq(struct irq_data *d)
+{
+ pci_msi_mask_irq(d);
+ irq_chip_mask_parent(d);
+}
+
+static void dw_msi_unmask_irq(struct irq_data *d)
+{
+ pci_msi_unmask_irq(d);
+ irq_chip_unmask_parent(d);
+}
+
+static struct irq_chip dw_pcie_msi_irq_chip = {
.name = "PCI-MSI",
- .irq_enable = pci_msi_unmask_irq,
- .irq_disable = pci_msi_mask_irq,
- .irq_mask = pci_msi_mask_irq,
- .irq_unmask = pci_msi_unmask_irq,
+ .irq_ack = dw_msi_ack_irq,
+ .irq_mask = dw_msi_mask_irq,
+ .irq_unmask = dw_msi_unmask_irq,
+};
+
+static struct msi_domain_info dw_pcie_msi_domain_info = {
+ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+ MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI),
+ .chip = &dw_pcie_msi_irq_chip,
};
/* MSI int handler */
irqreturn_t dw_handle_msi_irq(struct pcie_port *pp)
{
- u32 val;
int i, pos, irq;
+ u32 val, num_ctrls;
irqreturn_t ret = IRQ_NONE;
- for (i = 0; i < MAX_MSI_CTRLS; i++) {
+ num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL;
+
+ for (i = 0; i < num_ctrls; i++) {
dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4,
&val);
if (!val)
@@ -78,206 +103,216 @@ irqreturn_t dw_handle_msi_irq(struct pcie_port *pp)
return ret;
}
-void dw_pcie_msi_init(struct pcie_port *pp)
+/* Chained MSI interrupt service routine */
+static void dw_chained_msi_isr(struct irq_desc *desc)
{
- struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
- struct device *dev = pci->dev;
- struct page *page;
- u64 msi_target;
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct pcie_port *pp;
- page = alloc_page(GFP_KERNEL);
- pp->msi_data = dma_map_page(dev, page, 0, PAGE_SIZE, DMA_FROM_DEVICE);
- if (dma_mapping_error(dev, pp->msi_data)) {
- dev_err(dev, "failed to map MSI data\n");
- __free_page(page);
- return;
- }
- msi_target = (u64)pp->msi_data;
+ chained_irq_enter(chip, desc);
- /* program the msi_data */
- dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_LO, 4,
- (u32)(msi_target & 0xffffffff));
- dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4,
- (u32)(msi_target >> 32 & 0xffffffff));
+ pp = irq_desc_get_handler_data(desc);
+ dw_handle_msi_irq(pp);
+
+ chained_irq_exit(chip, desc);
}
-static void dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq)
+static void dw_pci_setup_msi_msg(struct irq_data *data, struct msi_msg *msg)
{
- unsigned int res, bit, val;
+ struct pcie_port *pp = irq_data_get_irq_chip_data(data);
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ u64 msi_target;
- res = (irq / 32) * 12;
- bit = irq % 32;
- dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val);
- val &= ~(1 << bit);
- dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val);
-}
+ if (pp->ops->get_msi_addr)
+ msi_target = pp->ops->get_msi_addr(pp);
+ else
+ msi_target = (u64)pp->msi_data;
-static void clear_irq_range(struct pcie_port *pp, unsigned int irq_base,
- unsigned int nvec, unsigned int pos)
-{
- unsigned int i;
-
- for (i = 0; i < nvec; i++) {
- irq_set_msi_desc_off(irq_base, i, NULL);
- /* Disable corresponding interrupt on MSI controller */
- if (pp->ops->msi_clear_irq)
- pp->ops->msi_clear_irq(pp, pos + i);
- else
- dw_pcie_msi_clear_irq(pp, pos + i);
- }
+ msg->address_lo = lower_32_bits(msi_target);
+ msg->address_hi = upper_32_bits(msi_target);
- bitmap_release_region(pp->msi_irq_in_use, pos, order_base_2(nvec));
+ if (pp->ops->get_msi_data)
+ msg->data = pp->ops->get_msi_data(pp, data->hwirq);
+ else
+ msg->data = data->hwirq;
+
+ dev_dbg(pci->dev, "msi#%d address_hi %#x address_lo %#x\n",
+ (int)data->hwirq, msg->address_hi, msg->address_lo);
}
-static void dw_pcie_msi_set_irq(struct pcie_port *pp, int irq)
+static int dw_pci_msi_set_affinity(struct irq_data *irq_data,
+ const struct cpumask *mask, bool force)
{
- unsigned int res, bit, val;
-
- res = (irq / 32) * 12;
- bit = irq % 32;
- dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val);
- val |= 1 << bit;
- dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val);
+ return -EINVAL;
}
-static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos)
+static void dw_pci_bottom_mask(struct irq_data *data)
{
- int irq, pos0, i;
- struct pcie_port *pp;
-
- pp = (struct pcie_port *)msi_desc_to_pci_sysdata(desc);
- pos0 = bitmap_find_free_region(pp->msi_irq_in_use, MAX_MSI_IRQS,
- order_base_2(no_irqs));
- if (pos0 < 0)
- goto no_valid_irq;
+ struct pcie_port *pp = irq_data_get_irq_chip_data(data);
+ unsigned int res, bit, ctrl;
+ unsigned long flags;
- irq = irq_find_mapping(pp->irq_domain, pos0);
- if (!irq)
- goto no_valid_irq;
+ raw_spin_lock_irqsave(&pp->lock, flags);
- /*
- * irq_create_mapping (called from dw_pcie_host_init) pre-allocates
- * descs so there is no need to allocate descs here. We can therefore
- * assume that if irq_find_mapping above returns non-zero, then the
- * descs are also successfully allocated.
- */
+ if (pp->ops->msi_clear_irq) {
+ pp->ops->msi_clear_irq(pp, data->hwirq);
+ } else {
+ ctrl = data->hwirq / 32;
+ res = ctrl * 12;
+ bit = data->hwirq % 32;
- for (i = 0; i < no_irqs; i++) {
- if (irq_set_msi_desc_off(irq, i, desc) != 0) {
- clear_irq_range(pp, irq, i, pos0);
- goto no_valid_irq;
- }
- /*Enable corresponding interrupt in MSI interrupt controller */
- if (pp->ops->msi_set_irq)
- pp->ops->msi_set_irq(pp, pos0 + i);
- else
- dw_pcie_msi_set_irq(pp, pos0 + i);
+ pp->irq_status[ctrl] &= ~(1 << bit);
+ dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4,
+ pp->irq_status[ctrl]);
}
- *pos = pos0;
- desc->nvec_used = no_irqs;
- desc->msi_attrib.multiple = order_base_2(no_irqs);
-
- return irq;
-
-no_valid_irq:
- *pos = pos0;
- return -ENOSPC;
+ raw_spin_unlock_irqrestore(&pp->lock, flags);
}
-static void dw_msi_setup_msg(struct pcie_port *pp, unsigned int irq, u32 pos)
+static void dw_pci_bottom_unmask(struct irq_data *data)
{
- struct msi_msg msg;
- u64 msi_target;
+ struct pcie_port *pp = irq_data_get_irq_chip_data(data);
+ unsigned int res, bit, ctrl;
+ unsigned long flags;
- if (pp->ops->get_msi_addr)
- msi_target = pp->ops->get_msi_addr(pp);
- else
- msi_target = (u64)pp->msi_data;
+ raw_spin_lock_irqsave(&pp->lock, flags);
- msg.address_lo = (u32)(msi_target & 0xffffffff);
- msg.address_hi = (u32)(msi_target >> 32 & 0xffffffff);
+ if (pp->ops->msi_set_irq) {
+ pp->ops->msi_set_irq(pp, data->hwirq);
+ } else {
+ ctrl = data->hwirq / 32;
+ res = ctrl * 12;
+ bit = data->hwirq % 32;
- if (pp->ops->get_msi_data)
- msg.data = pp->ops->get_msi_data(pp, pos);
- else
- msg.data = pos;
+ pp->irq_status[ctrl] |= 1 << bit;
+ dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4,
+ pp->irq_status[ctrl]);
+ }
- pci_write_msi_msg(irq, &msg);
+ raw_spin_unlock_irqrestore(&pp->lock, flags);
}
-static int dw_msi_setup_irq(struct msi_controller *chip, struct pci_dev *pdev,
- struct msi_desc *desc)
+static void dw_pci_bottom_ack(struct irq_data *d)
{
- int irq, pos;
- struct pcie_port *pp = pdev->bus->sysdata;
-
- if (desc->msi_attrib.is_msix)
- return -EINVAL;
-
- irq = assign_irq(1, desc, &pos);
- if (irq < 0)
- return irq;
+ struct msi_desc *msi = irq_data_get_msi_desc(d);
+ struct pcie_port *pp;
- dw_msi_setup_msg(pp, irq, pos);
+ pp = msi_desc_to_pci_sysdata(msi);
- return 0;
+ if (pp->ops->msi_irq_ack)
+ pp->ops->msi_irq_ack(d->hwirq, pp);
}
-static int dw_msi_setup_irqs(struct msi_controller *chip, struct pci_dev *pdev,
- int nvec, int type)
+static struct irq_chip dw_pci_msi_bottom_irq_chip = {
+ .name = "DWPCI-MSI",
+ .irq_ack = dw_pci_bottom_ack,
+ .irq_compose_msi_msg = dw_pci_setup_msi_msg,
+ .irq_set_affinity = dw_pci_msi_set_affinity,
+ .irq_mask = dw_pci_bottom_mask,
+ .irq_unmask = dw_pci_bottom_unmask,
+};
+
+static int dw_pcie_irq_domain_alloc(struct irq_domain *domain,
+ unsigned int virq, unsigned int nr_irqs,
+ void *args)
{
-#ifdef CONFIG_PCI_MSI
- int irq, pos;
- struct msi_desc *desc;
- struct pcie_port *pp = pdev->bus->sysdata;
+ struct pcie_port *pp = domain->host_data;
+ unsigned long flags;
+ u32 i;
+ int bit;
+
+ raw_spin_lock_irqsave(&pp->lock, flags);
- /* MSI-X interrupts are not supported */
- if (type == PCI_CAP_ID_MSIX)
- return -EINVAL;
+ bit = bitmap_find_free_region(pp->msi_irq_in_use, pp->num_vectors,
+ order_base_2(nr_irqs));
- WARN_ON(!list_is_singular(&pdev->dev.msi_list));
- desc = list_entry(pdev->dev.msi_list.next, struct msi_desc, list);
+ raw_spin_unlock_irqrestore(&pp->lock, flags);
- irq = assign_irq(nvec, desc, &pos);
- if (irq < 0)
- return irq;
+ if (bit < 0)
+ return -ENOSPC;
- dw_msi_setup_msg(pp, irq, pos);
+ for (i = 0; i < nr_irqs; i++)
+ irq_domain_set_info(domain, virq + i, bit + i,
+ &dw_pci_msi_bottom_irq_chip,
+ pp, handle_edge_irq,
+ NULL, NULL);
return 0;
-#else
- return -EINVAL;
-#endif
}
-static void dw_msi_teardown_irq(struct msi_controller *chip, unsigned int irq)
+static void dw_pcie_irq_domain_free(struct irq_domain *domain,
+ unsigned int virq, unsigned int nr_irqs)
{
- struct irq_data *data = irq_get_irq_data(irq);
- struct msi_desc *msi = irq_data_get_msi_desc(data);
- struct pcie_port *pp = (struct pcie_port *)msi_desc_to_pci_sysdata(msi);
-
- clear_irq_range(pp, irq, 1, data->hwirq);
+ struct irq_data *data = irq_domain_get_irq_data(domain, virq);
+ struct pcie_port *pp = irq_data_get_irq_chip_data(data);
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&pp->lock, flags);
+ bitmap_release_region(pp->msi_irq_in_use, data->hwirq,
+ order_base_2(nr_irqs));
+ raw_spin_unlock_irqrestore(&pp->lock, flags);
}
-static struct msi_controller dw_pcie_msi_chip = {
- .setup_irq = dw_msi_setup_irq,
- .setup_irqs = dw_msi_setup_irqs,
- .teardown_irq = dw_msi_teardown_irq,
+static const struct irq_domain_ops dw_pcie_msi_domain_ops = {
+ .alloc = dw_pcie_irq_domain_alloc,
+ .free = dw_pcie_irq_domain_free,
};
-static int dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq,
- irq_hw_number_t hwirq)
+int dw_pcie_allocate_domains(struct pcie_port *pp)
{
- irq_set_chip_and_handler(irq, &dw_msi_irq_chip, handle_simple_irq);
- irq_set_chip_data(irq, domain->host_data);
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct fwnode_handle *fwnode = of_node_to_fwnode(pci->dev->of_node);
+
+ pp->irq_domain = irq_domain_create_linear(fwnode, pp->num_vectors,
+ &dw_pcie_msi_domain_ops, pp);
+ if (!pp->irq_domain) {
+ dev_err(pci->dev, "failed to create IRQ domain\n");
+ return -ENOMEM;
+ }
+
+ pp->msi_domain = pci_msi_create_irq_domain(fwnode,
+ &dw_pcie_msi_domain_info,
+ pp->irq_domain);
+ if (!pp->msi_domain) {
+ dev_err(pci->dev, "failed to create MSI domain\n");
+ irq_domain_remove(pp->irq_domain);
+ return -ENOMEM;
+ }
return 0;
}
-static const struct irq_domain_ops msi_domain_ops = {
- .map = dw_pcie_msi_map,
-};
+void dw_pcie_free_msi(struct pcie_port *pp)
+{
+ irq_set_chained_handler(pp->msi_irq, NULL);
+ irq_set_handler_data(pp->msi_irq, NULL);
+
+ irq_domain_remove(pp->msi_domain);
+ irq_domain_remove(pp->irq_domain);
+}
+
+void dw_pcie_msi_init(struct pcie_port *pp)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct device *dev = pci->dev;
+ struct page *page;
+ u64 msi_target;
+
+ page = alloc_page(GFP_KERNEL);
+ pp->msi_data = dma_map_page(dev, page, 0, PAGE_SIZE, DMA_FROM_DEVICE);
+ if (dma_mapping_error(dev, pp->msi_data)) {
+ dev_err(dev, "failed to map MSI data\n");
+ __free_page(page);
+ return;
+ }
+ msi_target = (u64)pp->msi_data;
+
+ /* program the msi_data */
+ dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_LO, 4,
+ lower_32_bits(msi_target));
+ dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4,
+ upper_32_bits(msi_target));
+}
int dw_pcie_host_init(struct pcie_port *pp)
{
@@ -285,11 +320,13 @@ int dw_pcie_host_init(struct pcie_port *pp)
struct device *dev = pci->dev;
struct device_node *np = dev->of_node;
struct platform_device *pdev = to_platform_device(dev);
+ struct resource_entry *win, *tmp;
struct pci_bus *bus, *child;
struct pci_host_bridge *bridge;
struct resource *cfg_res;
- int i, ret;
- struct resource_entry *win, *tmp;
+ int ret;
+
+ raw_spin_lock_init(&pci->pp.lock);
cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config");
if (cfg_res) {
@@ -388,20 +425,35 @@ int dw_pcie_host_init(struct pcie_port *pp)
pci->num_viewport = 2;
if (IS_ENABLED(CONFIG_PCI_MSI)) {
- if (!pp->ops->msi_host_init) {
- pp->irq_domain = irq_domain_add_linear(dev->of_node,
- MAX_MSI_IRQS, &msi_domain_ops,
- &dw_pcie_msi_chip);
- if (!pp->irq_domain) {
- dev_err(dev, "irq domain init failed\n");
- ret = -ENXIO;
+ /*
+ * If a specific SoC driver needs to change the
+ * default number of vectors, it needs to implement
+ * the set_num_vectors callback.
+ */
+ if (!pp->ops->set_num_vectors) {
+ pp->num_vectors = MSI_DEF_NUM_VECTORS;
+ } else {
+ pp->ops->set_num_vectors(pp);
+
+ if (pp->num_vectors > MAX_MSI_IRQS ||
+ pp->num_vectors == 0) {
+ dev_err(dev,
+ "Invalid number of vectors\n");
goto error;
}
+ }
- for (i = 0; i < MAX_MSI_IRQS; i++)
- irq_create_mapping(pp->irq_domain, i);
+ if (!pp->ops->msi_host_init) {
+ ret = dw_pcie_allocate_domains(pp);
+ if (ret)
+ goto error;
+
+ if (pp->msi_irq)
+ irq_set_chained_handler_and_data(pp->msi_irq,
+ dw_chained_msi_isr,
+ pp);
} else {
- ret = pp->ops->msi_host_init(pp, &dw_pcie_msi_chip);
+ ret = pp->ops->msi_host_init(pp);
if (ret < 0)
goto error;
}
@@ -421,10 +473,6 @@ int dw_pcie_host_init(struct pcie_port *pp)
bridge->ops = &dw_pcie_ops;
bridge->map_irq = of_irq_parse_and_map_pci;
bridge->swizzle_irq = pci_common_swizzle;
- if (IS_ENABLED(CONFIG_PCI_MSI)) {
- bridge->msi = &dw_pcie_msi_chip;
- dw_pcie_msi_chip.dev = dev;
- }
ret = pci_scan_root_bus_bridge(bridge);
if (ret)
@@ -593,11 +641,17 @@ static u8 dw_pcie_iatu_unroll_enabled(struct dw_pcie *pci)
void dw_pcie_setup_rc(struct pcie_port *pp)
{
- u32 val;
+ u32 val, ctrl, num_ctrls;
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
dw_pcie_setup(pci);
+ num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL;
+
+ /* Initialize IRQ Status array */
+ for (ctrl = 0; ctrl < num_ctrls; ctrl++)
+ dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + (ctrl * 12), 4,
+ &pp->irq_status[ctrl]);
/* setup RC BARs */
dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0x00000004);
dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_1, 0x00000000);
diff --git a/drivers/pci/dwc/pcie-designware-plat.c b/drivers/pci/dwc/pcie-designware-plat.c
index ebdf28bcd67d..5416aa8a07a5 100644
--- a/drivers/pci/dwc/pcie-designware-plat.c
+++ b/drivers/pci/dwc/pcie-designware-plat.c
@@ -25,13 +25,6 @@ struct dw_plat_pcie {
struct dw_pcie *pci;
};
-static irqreturn_t dw_plat_pcie_msi_irq_handler(int irq, void *arg)
-{
- struct pcie_port *pp = arg;
-
- return dw_handle_msi_irq(pp);
-}
-
static int dw_plat_pcie_host_init(struct pcie_port *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
@@ -63,15 +56,6 @@ static int dw_plat_add_pcie_port(struct pcie_port *pp,
pp->msi_irq = platform_get_irq(pdev, 0);
if (pp->msi_irq < 0)
return pp->msi_irq;
-
- ret = devm_request_irq(dev, pp->msi_irq,
- dw_plat_pcie_msi_irq_handler,
- IRQF_SHARED | IRQF_NO_THREAD,
- "dw-plat-pcie-msi", pp);
- if (ret) {
- dev_err(dev, "failed to request MSI IRQ\n");
- return ret;
- }
}
pp->root_bus_nr = -1;
diff --git a/drivers/pci/dwc/pcie-designware.h b/drivers/pci/dwc/pcie-designware.h
index 11b13864a406..fe811dbc12cf 100644
--- a/drivers/pci/dwc/pcie-designware.h
+++ b/drivers/pci/dwc/pcie-designware.h
@@ -107,13 +107,10 @@
#define MSI_MESSAGE_DATA_32 0x58
#define MSI_MESSAGE_DATA_64 0x5C
-/*
- * Maximum number of MSI IRQs can be 256 per controller. But keep
- * it 32 as of now. Probably we will never need more than 32. If needed,
- * then increment it in multiple of 32.
- */
-#define MAX_MSI_IRQS 32
-#define MAX_MSI_CTRLS (MAX_MSI_IRQS / 32)
+#define MAX_MSI_IRQS 256
+#define MAX_MSI_IRQS_PER_CTRL 32
+#define MAX_MSI_CTRLS (MAX_MSI_IRQS / MAX_MSI_IRQS_PER_CTRL)
+#define MSI_DEF_NUM_VECTORS 32
/* Maximum number of inbound/outbound iATUs */
#define MAX_IATU_IN 256
@@ -149,7 +146,9 @@ struct dw_pcie_host_ops {
phys_addr_t (*get_msi_addr)(struct pcie_port *pp);
u32 (*get_msi_data)(struct pcie_port *pp, int pos);
void (*scan_bus)(struct pcie_port *pp);
- int (*msi_host_init)(struct pcie_port *pp, struct msi_controller *chip);
+ void (*set_num_vectors)(struct pcie_port *pp);
+ int (*msi_host_init)(struct pcie_port *pp);
+ void (*msi_irq_ack)(int irq, struct pcie_port *pp);
};
struct pcie_port {
@@ -174,7 +173,11 @@ struct pcie_port {
const struct dw_pcie_host_ops *ops;
int msi_irq;
struct irq_domain *irq_domain;
+ struct irq_domain *msi_domain;
dma_addr_t msi_data;
+ u32 num_vectors;
+ u32 irq_status[MAX_MSI_CTRLS];
+ raw_spinlock_t lock;
DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS);
};
@@ -316,8 +319,10 @@ static inline void dw_pcie_dbi_ro_wr_dis(struct dw_pcie *pci)
#ifdef CONFIG_PCIE_DW_HOST
irqreturn_t dw_handle_msi_irq(struct pcie_port *pp);
void dw_pcie_msi_init(struct pcie_port *pp);
+void dw_pcie_free_msi(struct pcie_port *pp);
void dw_pcie_setup_rc(struct pcie_port *pp);
int dw_pcie_host_init(struct pcie_port *pp);
+int dw_pcie_allocate_domains(struct pcie_port *pp);
#else
static inline irqreturn_t dw_handle_msi_irq(struct pcie_port *pp)
{
@@ -328,6 +333,10 @@ static inline void dw_pcie_msi_init(struct pcie_port *pp)
{
}
+static inline void dw_pcie_free_msi(struct pcie_port *pp)
+{
+}
+
static inline void dw_pcie_setup_rc(struct pcie_port *pp)
{
}
@@ -336,6 +345,11 @@ static inline int dw_pcie_host_init(struct pcie_port *pp)
{
return 0;
}
+
+static inline int dw_pcie_allocate_domains(struct pcie_port *pp)
+{
+ return 0;
+}
#endif
#ifdef CONFIG_PCIE_DW_EP
diff --git a/drivers/pci/dwc/pcie-histb.c b/drivers/pci/dwc/pcie-histb.c
index 70b5c0b108bf..3611d6ce9a92 100644
--- a/drivers/pci/dwc/pcie-histb.c
+++ b/drivers/pci/dwc/pcie-histb.c
@@ -61,6 +61,7 @@ struct histb_pcie {
struct reset_control *bus_reset;
void __iomem *ctrl;
int reset_gpio;
+ struct regulator *vpcie;
};
static u32 histb_pcie_readl(struct histb_pcie *histb_pcie, u32 reg)
@@ -207,13 +208,6 @@ static struct dw_pcie_host_ops histb_pcie_host_ops = {
.host_init = histb_pcie_host_init,
};
-static irqreturn_t histb_pcie_msi_irq_handler(int irq, void *arg)
-{
- struct pcie_port *pp = arg;
-
- return dw_handle_msi_irq(pp);
-}
-
static void histb_pcie_host_disable(struct histb_pcie *hipcie)
{
reset_control_assert(hipcie->soft_reset);
@@ -227,6 +221,9 @@ static void histb_pcie_host_disable(struct histb_pcie *hipcie)
if (gpio_is_valid(hipcie->reset_gpio))
gpio_set_value_cansleep(hipcie->reset_gpio, 0);
+
+ if (hipcie->vpcie)
+ regulator_disable(hipcie->vpcie);
}
static int histb_pcie_host_enable(struct pcie_port *pp)
@@ -237,6 +234,14 @@ static int histb_pcie_host_enable(struct pcie_port *pp)
int ret;
/* power on PCIe device if have */
+ if (hipcie->vpcie) {
+ ret = regulator_enable(hipcie->vpcie);
+ if (ret) {
+ dev_err(dev, "failed to enable regulator: %d\n", ret);
+ return ret;
+ }
+ }
+
if (gpio_is_valid(hipcie->reset_gpio))
gpio_set_value_cansleep(hipcie->reset_gpio, 1);
@@ -276,13 +281,14 @@ static int histb_pcie_host_enable(struct pcie_port *pp)
return 0;
err_aux_clk:
- clk_disable_unprepare(hipcie->aux_clk);
-err_pipe_clk:
clk_disable_unprepare(hipcie->pipe_clk);
-err_sys_clk:
+err_pipe_clk:
clk_disable_unprepare(hipcie->sys_clk);
-err_bus_clk:
+err_sys_clk:
clk_disable_unprepare(hipcie->bus_clk);
+err_bus_clk:
+ if (hipcie->vpcie)
+ regulator_disable(hipcie->vpcie);
return ret;
}
@@ -332,6 +338,13 @@ static int histb_pcie_probe(struct platform_device *pdev)
return PTR_ERR(pci->dbi_base);
}
+ hipcie->vpcie = devm_regulator_get_optional(dev, "vpcie");
+ if (IS_ERR(hipcie->vpcie)) {
+ if (PTR_ERR(hipcie->vpcie) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ hipcie->vpcie = NULL;
+ }
+
hipcie->reset_gpio = of_get_named_gpio_flags(np,
"reset-gpios", 0, &of_flags);
if (of_flags & OF_GPIO_ACTIVE_LOW)
@@ -393,14 +406,6 @@ static int histb_pcie_probe(struct platform_device *pdev)
dev_err(dev, "Failed to get MSI IRQ\n");
return pp->msi_irq;
}
-
- ret = devm_request_irq(dev, pp->msi_irq,
- histb_pcie_msi_irq_handler,
- IRQF_SHARED, "histb-pcie-msi", pp);
- if (ret) {
- dev_err(dev, "cannot request MSI IRQ\n");
- return ret;
- }
}
hipcie->phy = devm_phy_get(dev, "phy");
diff --git a/drivers/pci/dwc/pcie-kirin.c b/drivers/pci/dwc/pcie-kirin.c
index 13d839bd6160..d2970a009eb5 100644
--- a/drivers/pci/dwc/pcie-kirin.c
+++ b/drivers/pci/dwc/pcie-kirin.c
@@ -8,7 +8,6 @@
* Author: Xiaowei Song <songxiaowei@huawei.com>
*/
-#include <asm/compiler.h>
#include <linux/compiler.h>
#include <linux/clk.h>
#include <linux/delay.h>
@@ -487,7 +486,7 @@ static int kirin_pcie_probe(struct platform_device *pdev)
return ret;
kirin_pcie->gpio_id_reset = of_get_named_gpio(dev->of_node,
- "reset-gpio", 0);
+ "reset-gpios", 0);
if (kirin_pcie->gpio_id_reset < 0)
return -ENODEV;
@@ -505,7 +504,7 @@ static const struct of_device_id kirin_pcie_match[] = {
{},
};
-struct platform_driver kirin_pcie_driver = {
+static struct platform_driver kirin_pcie_driver = {
.probe = kirin_pcie_probe,
.driver = {
.name = "kirin-pcie",
diff --git a/drivers/pci/dwc/pcie-qcom.c b/drivers/pci/dwc/pcie-qcom.c
index 6310c66e265c..5897af7d3355 100644
--- a/drivers/pci/dwc/pcie-qcom.c
+++ b/drivers/pci/dwc/pcie-qcom.c
@@ -79,6 +79,7 @@
#define PCIE20_v3_PARF_SLV_ADDR_SPACE_SIZE 0x358
#define SLV_ADDR_SPACE_SZ 0x10000000
+#define QCOM_PCIE_2_1_0_MAX_SUPPLY 3
struct qcom_pcie_resources_2_1_0 {
struct clk *iface_clk;
struct clk *core_clk;
@@ -88,9 +89,7 @@ struct qcom_pcie_resources_2_1_0 {
struct reset_control *ahb_reset;
struct reset_control *por_reset;
struct reset_control *phy_reset;
- struct regulator *vdda;
- struct regulator *vdda_phy;
- struct regulator *vdda_refclk;
+ struct regulator_bulk_data supplies[QCOM_PCIE_2_1_0_MAX_SUPPLY];
};
struct qcom_pcie_resources_1_0_0 {
@@ -102,12 +101,14 @@ struct qcom_pcie_resources_1_0_0 {
struct regulator *vdda;
};
+#define QCOM_PCIE_2_3_2_MAX_SUPPLY 2
struct qcom_pcie_resources_2_3_2 {
struct clk *aux_clk;
struct clk *master_clk;
struct clk *slave_clk;
struct clk *cfg_clk;
struct clk *pipe_clk;
+ struct regulator_bulk_data supplies[QCOM_PCIE_2_3_2_MAX_SUPPLY];
};
struct qcom_pcie_resources_2_4_0 {
@@ -180,13 +181,6 @@ static void qcom_ep_reset_deassert(struct qcom_pcie *pcie)
usleep_range(PERST_DELAY_US, PERST_DELAY_US + 500);
}
-static irqreturn_t qcom_pcie_msi_irq_handler(int irq, void *arg)
-{
- struct pcie_port *pp = arg;
-
- return dw_handle_msi_irq(pp);
-}
-
static int qcom_pcie_establish_link(struct qcom_pcie *pcie)
{
struct dw_pcie *pci = pcie->pci;
@@ -216,18 +210,15 @@ static int qcom_pcie_get_resources_2_1_0(struct qcom_pcie *pcie)
struct qcom_pcie_resources_2_1_0 *res = &pcie->res.v2_1_0;
struct dw_pcie *pci = pcie->pci;
struct device *dev = pci->dev;
+ int ret;
- res->vdda = devm_regulator_get(dev, "vdda");
- if (IS_ERR(res->vdda))
- return PTR_ERR(res->vdda);
-
- res->vdda_phy = devm_regulator_get(dev, "vdda_phy");
- if (IS_ERR(res->vdda_phy))
- return PTR_ERR(res->vdda_phy);
-
- res->vdda_refclk = devm_regulator_get(dev, "vdda_refclk");
- if (IS_ERR(res->vdda_refclk))
- return PTR_ERR(res->vdda_refclk);
+ res->supplies[0].supply = "vdda";
+ res->supplies[1].supply = "vdda_phy";
+ res->supplies[2].supply = "vdda_refclk";
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(res->supplies),
+ res->supplies);
+ if (ret)
+ return ret;
res->iface_clk = devm_clk_get(dev, "iface");
if (IS_ERR(res->iface_clk))
@@ -273,9 +264,7 @@ static void qcom_pcie_deinit_2_1_0(struct qcom_pcie *pcie)
clk_disable_unprepare(res->iface_clk);
clk_disable_unprepare(res->core_clk);
clk_disable_unprepare(res->phy_clk);
- regulator_disable(res->vdda);
- regulator_disable(res->vdda_phy);
- regulator_disable(res->vdda_refclk);
+ regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies);
}
static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie)
@@ -286,24 +275,12 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie)
u32 val;
int ret;
- ret = regulator_enable(res->vdda);
- if (ret) {
- dev_err(dev, "cannot enable vdda regulator\n");
+ ret = regulator_bulk_enable(ARRAY_SIZE(res->supplies), res->supplies);
+ if (ret < 0) {
+ dev_err(dev, "cannot enable regulators\n");
return ret;
}
- ret = regulator_enable(res->vdda_refclk);
- if (ret) {
- dev_err(dev, "cannot enable vdda_refclk regulator\n");
- goto err_refclk;
- }
-
- ret = regulator_enable(res->vdda_phy);
- if (ret) {
- dev_err(dev, "cannot enable vdda_phy regulator\n");
- goto err_vdda_phy;
- }
-
ret = reset_control_assert(res->ahb_reset);
if (ret) {
dev_err(dev, "cannot assert ahb reset\n");
@@ -387,11 +364,7 @@ err_clk_core:
err_clk_phy:
clk_disable_unprepare(res->iface_clk);
err_assert_ahb:
- regulator_disable(res->vdda_phy);
-err_vdda_phy:
- regulator_disable(res->vdda_refclk);
-err_refclk:
- regulator_disable(res->vdda);
+ regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies);
return ret;
}
@@ -521,6 +494,14 @@ static int qcom_pcie_get_resources_2_3_2(struct qcom_pcie *pcie)
struct qcom_pcie_resources_2_3_2 *res = &pcie->res.v2_3_2;
struct dw_pcie *pci = pcie->pci;
struct device *dev = pci->dev;
+ int ret;
+
+ res->supplies[0].supply = "vdda";
+ res->supplies[1].supply = "vddpe-3v3";
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(res->supplies),
+ res->supplies);
+ if (ret)
+ return ret;
res->aux_clk = devm_clk_get(dev, "aux");
if (IS_ERR(res->aux_clk))
@@ -550,6 +531,8 @@ static void qcom_pcie_deinit_2_3_2(struct qcom_pcie *pcie)
clk_disable_unprepare(res->master_clk);
clk_disable_unprepare(res->cfg_clk);
clk_disable_unprepare(res->aux_clk);
+
+ regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies);
}
static void qcom_pcie_post_deinit_2_3_2(struct qcom_pcie *pcie)
@@ -567,10 +550,16 @@ static int qcom_pcie_init_2_3_2(struct qcom_pcie *pcie)
u32 val;
int ret;
+ ret = regulator_bulk_enable(ARRAY_SIZE(res->supplies), res->supplies);
+ if (ret < 0) {
+ dev_err(dev, "cannot enable regulators\n");
+ return ret;
+ }
+
ret = clk_prepare_enable(res->aux_clk);
if (ret) {
dev_err(dev, "cannot prepare/enable aux clock\n");
- return ret;
+ goto err_aux_clk;
}
ret = clk_prepare_enable(res->cfg_clk);
@@ -621,6 +610,9 @@ err_master_clk:
err_cfg_clk:
clk_disable_unprepare(res->aux_clk);
+err_aux_clk:
+ regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies);
+
return ret;
}
@@ -1262,15 +1254,6 @@ static int qcom_pcie_probe(struct platform_device *pdev)
pp->msi_irq = platform_get_irq_byname(pdev, "msi");
if (pp->msi_irq < 0)
return pp->msi_irq;
-
- ret = devm_request_irq(dev, pp->msi_irq,
- qcom_pcie_msi_irq_handler,
- IRQF_SHARED | IRQF_NO_THREAD,
- "qcom-pcie-msi", pp);
- if (ret) {
- dev_err(dev, "cannot request msi irq\n");
- return ret;
- }
}
ret = phy_init(pcie->phy);
diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c
index 64d8a17f8094..7cef85124325 100644
--- a/drivers/pci/endpoint/functions/pci-epf-test.c
+++ b/drivers/pci/endpoint/functions/pci-epf-test.c
@@ -70,7 +70,7 @@ struct pci_epf_test_data {
bool linkup_notifier;
};
-static int bar_size[] = { 512, 512, 1024, 16384, 131072, 1048576 };
+static size_t bar_size[] = { 512, 512, 1024, 16384, 131072, 1048576 };
static int pci_epf_test_copy(struct pci_epf_test *epf_test)
{
@@ -344,21 +344,23 @@ static void pci_epf_test_unbind(struct pci_epf *epf)
{
struct pci_epf_test *epf_test = epf_get_drvdata(epf);
struct pci_epc *epc = epf->epc;
+ struct pci_epf_bar *epf_bar;
int bar;
cancel_delayed_work(&epf_test->cmd_handler);
pci_epc_stop(epc);
for (bar = BAR_0; bar <= BAR_5; bar++) {
+ epf_bar = &epf->bar[bar];
+
if (epf_test->reg[bar]) {
pci_epf_free_space(epf, epf_test->reg[bar], bar);
- pci_epc_clear_bar(epc, epf->func_no, bar);
+ pci_epc_clear_bar(epc, epf->func_no, epf_bar);
}
}
}
static int pci_epf_test_set_bar(struct pci_epf *epf)
{
- int flags;
int bar;
int ret;
struct pci_epf_bar *epf_bar;
@@ -367,21 +369,27 @@ static int pci_epf_test_set_bar(struct pci_epf *epf)
struct pci_epf_test *epf_test = epf_get_drvdata(epf);
enum pci_barno test_reg_bar = epf_test->test_reg_bar;
- flags = PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_32;
- if (sizeof(dma_addr_t) == 0x8)
- flags |= PCI_BASE_ADDRESS_MEM_TYPE_64;
-
for (bar = BAR_0; bar <= BAR_5; bar++) {
epf_bar = &epf->bar[bar];
- ret = pci_epc_set_bar(epc, epf->func_no, bar,
- epf_bar->phys_addr,
- epf_bar->size, flags);
+
+ epf_bar->flags |= upper_32_bits(epf_bar->size) ?
+ PCI_BASE_ADDRESS_MEM_TYPE_64 :
+ PCI_BASE_ADDRESS_MEM_TYPE_32;
+
+ ret = pci_epc_set_bar(epc, epf->func_no, epf_bar);
if (ret) {
pci_epf_free_space(epf, epf_test->reg[bar], bar);
dev_err(dev, "failed to set BAR%d\n", bar);
if (bar == test_reg_bar)
return ret;
}
+ /*
+ * pci_epc_set_bar() sets PCI_BASE_ADDRESS_MEM_TYPE_64
+ * if the specific implementation required a 64-bit BAR,
+ * even if we only requested a 32-bit BAR.
+ */
+ if (epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64)
+ bar++;
}
return 0;
diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c
index e245bba0ab53..b0ee42739c3c 100644
--- a/drivers/pci/endpoint/pci-epc-core.c
+++ b/drivers/pci/endpoint/pci-epc-core.c
@@ -276,22 +276,25 @@ EXPORT_SYMBOL_GPL(pci_epc_map_addr);
* pci_epc_clear_bar() - reset the BAR
* @epc: the EPC device for which the BAR has to be cleared
* @func_no: the endpoint function number in the EPC device
- * @bar: the BAR number that has to be reset
+ * @epf_bar: the struct epf_bar that contains the BAR information
*
* Invoke to reset the BAR of the endpoint device.
*/
-void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, int bar)
+void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no,
+ struct pci_epf_bar *epf_bar)
{
unsigned long flags;
- if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
+ if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions ||
+ (epf_bar->barno == BAR_5 &&
+ epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64))
return;
if (!epc->ops->clear_bar)
return;
spin_lock_irqsave(&epc->lock, flags);
- epc->ops->clear_bar(epc, func_no, bar);
+ epc->ops->clear_bar(epc, func_no, epf_bar);
spin_unlock_irqrestore(&epc->lock, flags);
}
EXPORT_SYMBOL_GPL(pci_epc_clear_bar);
@@ -300,26 +303,31 @@ EXPORT_SYMBOL_GPL(pci_epc_clear_bar);
* pci_epc_set_bar() - configure BAR in order for host to assign PCI addr space
* @epc: the EPC device on which BAR has to be configured
* @func_no: the endpoint function number in the EPC device
- * @bar: the BAR number that has to be configured
- * @size: the size of the addr space
- * @flags: specify memory allocation/io allocation/32bit address/64 bit address
+ * @epf_bar: the struct epf_bar that contains the BAR information
*
* Invoke to configure the BAR of the endpoint device.
*/
-int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, enum pci_barno bar,
- dma_addr_t bar_phys, size_t size, int flags)
+int pci_epc_set_bar(struct pci_epc *epc, u8 func_no,
+ struct pci_epf_bar *epf_bar)
{
int ret;
unsigned long irq_flags;
-
- if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
+ int flags = epf_bar->flags;
+
+ if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions ||
+ (epf_bar->barno == BAR_5 &&
+ flags & PCI_BASE_ADDRESS_MEM_TYPE_64) ||
+ (flags & PCI_BASE_ADDRESS_SPACE_IO &&
+ flags & PCI_BASE_ADDRESS_IO_MASK) ||
+ (upper_32_bits(epf_bar->size) &&
+ !(flags & PCI_BASE_ADDRESS_MEM_TYPE_64)))
return -EINVAL;
if (!epc->ops->set_bar)
return 0;
spin_lock_irqsave(&epc->lock, irq_flags);
- ret = epc->ops->set_bar(epc, func_no, bar, bar_phys, size, flags);
+ ret = epc->ops->set_bar(epc, func_no, epf_bar);
spin_unlock_irqrestore(&epc->lock, irq_flags);
return ret;
diff --git a/drivers/pci/endpoint/pci-epf-core.c b/drivers/pci/endpoint/pci-epf-core.c
index 766ce1dca2ec..465b5f058b6d 100644
--- a/drivers/pci/endpoint/pci-epf-core.c
+++ b/drivers/pci/endpoint/pci-epf-core.c
@@ -98,6 +98,8 @@ void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar)
epf->bar[bar].phys_addr = 0;
epf->bar[bar].size = 0;
+ epf->bar[bar].barno = 0;
+ epf->bar[bar].flags = 0;
}
EXPORT_SYMBOL_GPL(pci_epf_free_space);
@@ -126,6 +128,8 @@ void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar)
epf->bar[bar].phys_addr = phys_addr;
epf->bar[bar].size = size;
+ epf->bar[bar].barno = bar;
+ epf->bar[bar].flags = PCI_BASE_ADDRESS_SPACE_MEMORY;
return space;
}
@@ -200,29 +204,17 @@ struct pci_epf *pci_epf_create(const char *name)
int ret;
struct pci_epf *epf;
struct device *dev;
- char *func_name;
- char *buf;
+ int len;
epf = kzalloc(sizeof(*epf), GFP_KERNEL);
- if (!epf) {
- ret = -ENOMEM;
- goto err_ret;
- }
-
- buf = kstrdup(name, GFP_KERNEL);
- if (!buf) {
- ret = -ENOMEM;
- goto free_epf;
- }
-
- func_name = buf;
- buf = strchrnul(buf, '.');
- *buf = '\0';
+ if (!epf)
+ return ERR_PTR(-ENOMEM);
- epf->name = kstrdup(func_name, GFP_KERNEL);
+ len = strchrnul(name, '.') - name;
+ epf->name = kstrndup(name, len, GFP_KERNEL);
if (!epf->name) {
- ret = -ENOMEM;
- goto free_func_name;
+ kfree(epf);
+ return ERR_PTR(-ENOMEM);
}
dev = &epf->dev;
@@ -231,28 +223,18 @@ struct pci_epf *pci_epf_create(const char *name)
dev->type = &pci_epf_type;
ret = dev_set_name(dev, "%s", name);
- if (ret)
- goto put_dev;
+ if (ret) {
+ put_device(dev);
+ return ERR_PTR(ret);
+ }
ret = device_add(dev);
- if (ret)
- goto put_dev;
+ if (ret) {
+ put_device(dev);
+ return ERR_PTR(ret);
+ }
- kfree(func_name);
return epf;
-
-put_dev:
- put_device(dev);
- kfree(epf->name);
-
-free_func_name:
- kfree(func_name);
-
-free_epf:
- kfree(epf);
-
-err_ret:
- return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(pci_epf_create);
diff --git a/drivers/pci/host-bridge.c b/drivers/pci/host-bridge.c
index ac8d81268296..e01d53f5b32f 100644
--- a/drivers/pci/host-bridge.c
+++ b/drivers/pci/host-bridge.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * host bridge related code
+ * Host bridge related code
*/
#include <linux/kernel.h>
diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index a4ed7484d127..0d0177ce436c 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -38,6 +38,7 @@ config PCI_FTPCI100
config PCI_TEGRA
bool "NVIDIA Tegra PCIe controller"
depends on ARCH_TEGRA
+ depends on PCI_MSI_IRQ_DOMAIN
help
Say Y here if you want support for the PCIe host controller found
on NVIDIA Tegra SoCs.
@@ -215,7 +216,6 @@ config PCIE_TANGO_SMP8759
config VMD
depends on PCI_MSI && X86_64 && SRCU
tristate "Intel Volume Management Device Driver"
- default N
---help---
Adds support for the Intel Volume Management Device (VMD). VMD is a
secondary PCI host bridge that allows PCI Express root ports,
diff --git a/drivers/pci/host/pci-aardvark.c b/drivers/pci/host/pci-aardvark.c
index b04d37b3c5de..9abf549631b4 100644
--- a/drivers/pci/host/pci-aardvark.c
+++ b/drivers/pci/host/pci-aardvark.c
@@ -29,6 +29,7 @@
#define PCIE_CORE_DEV_CTRL_STATS_MAX_PAYLOAD_SZ_SHIFT 5
#define PCIE_CORE_DEV_CTRL_STATS_SNOOP_DISABLE (0 << 11)
#define PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SIZE_SHIFT 12
+#define PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SZ 0x2
#define PCIE_CORE_LINK_CTRL_STAT_REG 0xd0
#define PCIE_CORE_LINK_L0S_ENTRY BIT(0)
#define PCIE_CORE_LINK_TRAINING BIT(5)
@@ -100,7 +101,8 @@
#define PCIE_ISR1_MASK_REG (CONTROL_BASE_ADDR + 0x4C)
#define PCIE_ISR1_POWER_STATE_CHANGE BIT(4)
#define PCIE_ISR1_FLUSH BIT(5)
-#define PCIE_ISR1_ALL_MASK GENMASK(5, 4)
+#define PCIE_ISR1_INTX_ASSERT(val) BIT(8 + (val))
+#define PCIE_ISR1_ALL_MASK GENMASK(11, 4)
#define PCIE_MSI_ADDR_LOW_REG (CONTROL_BASE_ADDR + 0x50)
#define PCIE_MSI_ADDR_HIGH_REG (CONTROL_BASE_ADDR + 0x54)
#define PCIE_MSI_STATUS_REG (CONTROL_BASE_ADDR + 0x58)
@@ -172,8 +174,6 @@
#define PCIE_CONFIG_WR_TYPE0 0xa
#define PCIE_CONFIG_WR_TYPE1 0xb
-/* PCI_BDF shifts 8bit, so we need extra 4bit shift */
-#define PCIE_BDF(dev) (dev << 4)
#define PCIE_CONF_BUS(bus) (((bus) & 0xff) << 20)
#define PCIE_CONF_DEV(dev) (((dev) & 0x1f) << 15)
#define PCIE_CONF_FUNC(fun) (((fun) & 0x7) << 12)
@@ -296,7 +296,8 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie)
reg = PCIE_CORE_DEV_CTRL_STATS_RELAX_ORDER_DISABLE |
(7 << PCIE_CORE_DEV_CTRL_STATS_MAX_PAYLOAD_SZ_SHIFT) |
PCIE_CORE_DEV_CTRL_STATS_SNOOP_DISABLE |
- PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SIZE_SHIFT;
+ (PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SZ <<
+ PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SIZE_SHIFT);
advk_writel(pcie, reg, PCIE_CORE_DEV_CTRL_STATS_REG);
/* Program PCIe Control 2 to disable strict ordering */
@@ -437,7 +438,7 @@ static int advk_pcie_rd_conf(struct pci_bus *bus, u32 devfn,
u32 reg;
int ret;
- if (PCI_SLOT(devfn) != 0) {
+ if ((bus->number == pcie->root_bus_nr) && PCI_SLOT(devfn) != 0) {
*val = 0xffffffff;
return PCIBIOS_DEVICE_NOT_FOUND;
}
@@ -456,7 +457,7 @@ static int advk_pcie_rd_conf(struct pci_bus *bus, u32 devfn,
advk_writel(pcie, reg, PIO_CTRL);
/* Program the address registers */
- reg = PCIE_BDF(devfn) | PCIE_CONF_REG(where);
+ reg = PCIE_CONF_ADDR(bus->number, devfn, where);
advk_writel(pcie, reg, PIO_ADDR_LS);
advk_writel(pcie, 0, PIO_ADDR_MS);
@@ -491,7 +492,7 @@ static int advk_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
int offset;
int ret;
- if (PCI_SLOT(devfn) != 0)
+ if ((bus->number == pcie->root_bus_nr) && PCI_SLOT(devfn) != 0)
return PCIBIOS_DEVICE_NOT_FOUND;
if (where % size)
@@ -609,9 +610,9 @@ static void advk_pcie_irq_mask(struct irq_data *d)
irq_hw_number_t hwirq = irqd_to_hwirq(d);
u32 mask;
- mask = advk_readl(pcie, PCIE_ISR0_MASK_REG);
- mask |= PCIE_ISR0_INTX_ASSERT(hwirq);
- advk_writel(pcie, mask, PCIE_ISR0_MASK_REG);
+ mask = advk_readl(pcie, PCIE_ISR1_MASK_REG);
+ mask |= PCIE_ISR1_INTX_ASSERT(hwirq);
+ advk_writel(pcie, mask, PCIE_ISR1_MASK_REG);
}
static void advk_pcie_irq_unmask(struct irq_data *d)
@@ -620,9 +621,9 @@ static void advk_pcie_irq_unmask(struct irq_data *d)
irq_hw_number_t hwirq = irqd_to_hwirq(d);
u32 mask;
- mask = advk_readl(pcie, PCIE_ISR0_MASK_REG);
- mask &= ~PCIE_ISR0_INTX_ASSERT(hwirq);
- advk_writel(pcie, mask, PCIE_ISR0_MASK_REG);
+ mask = advk_readl(pcie, PCIE_ISR1_MASK_REG);
+ mask &= ~PCIE_ISR1_INTX_ASSERT(hwirq);
+ advk_writel(pcie, mask, PCIE_ISR1_MASK_REG);
}
static int advk_pcie_irq_map(struct irq_domain *h,
@@ -765,29 +766,35 @@ static void advk_pcie_handle_msi(struct advk_pcie *pcie)
static void advk_pcie_handle_int(struct advk_pcie *pcie)
{
- u32 val, mask, status;
+ u32 isr0_val, isr0_mask, isr0_status;
+ u32 isr1_val, isr1_mask, isr1_status;
int i, virq;
- val = advk_readl(pcie, PCIE_ISR0_REG);
- mask = advk_readl(pcie, PCIE_ISR0_MASK_REG);
- status = val & ((~mask) & PCIE_ISR0_ALL_MASK);
+ isr0_val = advk_readl(pcie, PCIE_ISR0_REG);
+ isr0_mask = advk_readl(pcie, PCIE_ISR0_MASK_REG);
+ isr0_status = isr0_val & ((~isr0_mask) & PCIE_ISR0_ALL_MASK);
+
+ isr1_val = advk_readl(pcie, PCIE_ISR1_REG);
+ isr1_mask = advk_readl(pcie, PCIE_ISR1_MASK_REG);
+ isr1_status = isr1_val & ((~isr1_mask) & PCIE_ISR1_ALL_MASK);
- if (!status) {
- advk_writel(pcie, val, PCIE_ISR0_REG);
+ if (!isr0_status && !isr1_status) {
+ advk_writel(pcie, isr0_val, PCIE_ISR0_REG);
+ advk_writel(pcie, isr1_val, PCIE_ISR1_REG);
return;
}
/* Process MSI interrupts */
- if (status & PCIE_ISR0_MSI_INT_PENDING)
+ if (isr0_status & PCIE_ISR0_MSI_INT_PENDING)
advk_pcie_handle_msi(pcie);
/* Process legacy interrupts */
for (i = 0; i < PCI_NUM_INTX; i++) {
- if (!(status & PCIE_ISR0_INTX_ASSERT(i)))
+ if (!(isr1_status & PCIE_ISR1_INTX_ASSERT(i)))
continue;
- advk_writel(pcie, PCIE_ISR0_INTX_ASSERT(i),
- PCIE_ISR0_REG);
+ advk_writel(pcie, PCIE_ISR1_INTX_ASSERT(i),
+ PCIE_ISR1_REG);
virq = irq_find_mapping(pcie->irq_domain, i);
generic_handle_irq(virq);
diff --git a/drivers/pci/host/pci-ftpci100.c b/drivers/pci/host/pci-ftpci100.c
index b9617d1c1d48..5008fd87956a 100644
--- a/drivers/pci/host/pci-ftpci100.c
+++ b/drivers/pci/host/pci-ftpci100.c
@@ -586,11 +586,11 @@ static int faraday_pci_probe(struct platform_device *pdev)
* We encode bridge variants here, we have at least two so it doesn't
* hurt to have infrastructure to encompass future variants as well.
*/
-const struct faraday_pci_variant faraday_regular = {
+static const struct faraday_pci_variant faraday_regular = {
.cascaded_irq = true,
};
-const struct faraday_pci_variant faraday_dual = {
+static const struct faraday_pci_variant faraday_dual = {
.cascaded_irq = false,
};
diff --git a/drivers/pci/host/pci-hyperv.c b/drivers/pci/host/pci-hyperv.c
index 2faf38eab785..50cdefe3f6d3 100644
--- a/drivers/pci/host/pci-hyperv.c
+++ b/drivers/pci/host/pci-hyperv.c
@@ -447,7 +447,6 @@ struct hv_pcibus_device {
spinlock_t device_list_lock; /* Protect lists below */
void __iomem *cfg_addr;
- struct semaphore enum_sem;
struct list_head resources_for_children;
struct list_head children;
@@ -461,6 +460,8 @@ struct hv_pcibus_device {
struct retarget_msi_interrupt retarget_msi_interrupt_params;
spinlock_t retarget_msi_interrupt_lock;
+
+ struct workqueue_struct *wq;
};
/*
@@ -520,6 +521,8 @@ struct hv_pci_compl {
s32 completion_status;
};
+static void hv_pci_onchannelcallback(void *context);
+
/**
* hv_pci_generic_compl() - Invoked for a completion packet
* @context: Set up by the sender of the packet.
@@ -653,7 +656,7 @@ static void _hv_pcifront_read_config(struct hv_pci_dev *hpdev, int where,
break;
}
/*
- * Make sure the write was done before we release the spinlock
+ * Make sure the read was done before we release the spinlock
* allowing consecutive reads/writes.
*/
mb();
@@ -664,6 +667,31 @@ static void _hv_pcifront_read_config(struct hv_pci_dev *hpdev, int where,
}
}
+static u16 hv_pcifront_get_vendor_id(struct hv_pci_dev *hpdev)
+{
+ u16 ret;
+ unsigned long flags;
+ void __iomem *addr = hpdev->hbus->cfg_addr + CFG_PAGE_OFFSET +
+ PCI_VENDOR_ID;
+
+ spin_lock_irqsave(&hpdev->hbus->config_lock, flags);
+
+ /* Choose the function to be read. (See comment above) */
+ writel(hpdev->desc.win_slot.slot, hpdev->hbus->cfg_addr);
+ /* Make sure the function was chosen before we start reading. */
+ mb();
+ /* Read from that function's config space. */
+ ret = readw(addr);
+ /*
+ * mb() is not required here, because the spin_unlock_irqrestore()
+ * is a barrier.
+ */
+
+ spin_unlock_irqrestore(&hpdev->hbus->config_lock, flags);
+
+ return ret;
+}
+
/**
* _hv_pcifront_write_config() - Internal PCI config write
* @hpdev: The PCI driver's representation of the device
@@ -1106,8 +1134,37 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
* Since this function is called with IRQ locks held, can't
* do normal wait for completion; instead poll.
*/
- while (!try_wait_for_completion(&comp.comp_pkt.host_event))
+ while (!try_wait_for_completion(&comp.comp_pkt.host_event)) {
+ /* 0xFFFF means an invalid PCI VENDOR ID. */
+ if (hv_pcifront_get_vendor_id(hpdev) == 0xFFFF) {
+ dev_err_once(&hbus->hdev->device,
+ "the device has gone\n");
+ goto free_int_desc;
+ }
+
+ /*
+ * When the higher level interrupt code calls us with
+ * interrupt disabled, we must poll the channel by calling
+ * the channel callback directly when channel->target_cpu is
+ * the current CPU. When the higher level interrupt code
+ * calls us with interrupt enabled, let's add the
+ * local_bh_disable()/enable() to avoid race.
+ */
+ local_bh_disable();
+
+ if (hbus->hdev->channel->target_cpu == smp_processor_id())
+ hv_pci_onchannelcallback(hbus);
+
+ local_bh_enable();
+
+ if (hpdev->state == hv_pcichild_ejecting) {
+ dev_err_once(&hbus->hdev->device,
+ "the device is being ejected\n");
+ goto free_int_desc;
+ }
+
udelay(100);
+ }
if (comp.comp_pkt.completion_status < 0) {
dev_err(&hbus->hdev->device,
@@ -1590,12 +1647,8 @@ static struct hv_pci_dev *get_pcichild_wslot(struct hv_pcibus_device *hbus,
* It must also treat the omission of a previously observed device as
* notification that the device no longer exists.
*
- * Note that this function is a work item, and it may not be
- * invoked in the order that it was queued. Back to back
- * updates of the list of present devices may involve queuing
- * multiple work items, and this one may run before ones that
- * were sent later. As such, this function only does something
- * if is the last one in the queue.
+ * Note that this function is serialized with hv_eject_device_work(),
+ * because both are pushed to the ordered workqueue hbus->wq.
*/
static void pci_devices_present_work(struct work_struct *work)
{
@@ -1616,11 +1669,6 @@ static void pci_devices_present_work(struct work_struct *work)
INIT_LIST_HEAD(&removed);
- if (down_interruptible(&hbus->enum_sem)) {
- put_hvpcibus(hbus);
- return;
- }
-
/* Pull this off the queue and process it if it was the last one. */
spin_lock_irqsave(&hbus->device_list_lock, flags);
while (!list_empty(&hbus->dr_list)) {
@@ -1637,7 +1685,6 @@ static void pci_devices_present_work(struct work_struct *work)
spin_unlock_irqrestore(&hbus->device_list_lock, flags);
if (!dr) {
- up(&hbus->enum_sem);
put_hvpcibus(hbus);
return;
}
@@ -1724,7 +1771,6 @@ static void pci_devices_present_work(struct work_struct *work)
break;
}
- up(&hbus->enum_sem);
put_hvpcibus(hbus);
kfree(dr);
}
@@ -1743,6 +1789,7 @@ static void hv_pci_devices_present(struct hv_pcibus_device *hbus,
struct hv_dr_state *dr;
struct hv_dr_work *dr_wrk;
unsigned long flags;
+ bool pending_dr;
dr_wrk = kzalloc(sizeof(*dr_wrk), GFP_NOWAIT);
if (!dr_wrk)
@@ -1766,11 +1813,21 @@ static void hv_pci_devices_present(struct hv_pcibus_device *hbus,
}
spin_lock_irqsave(&hbus->device_list_lock, flags);
+ /*
+ * If pending_dr is true, we have already queued a work,
+ * which will see the new dr. Otherwise, we need to
+ * queue a new work.
+ */
+ pending_dr = !list_empty(&hbus->dr_list);
list_add_tail(&dr->list_entry, &hbus->dr_list);
spin_unlock_irqrestore(&hbus->device_list_lock, flags);
- get_hvpcibus(hbus);
- schedule_work(&dr_wrk->wrk);
+ if (pending_dr) {
+ kfree(dr_wrk);
+ } else {
+ get_hvpcibus(hbus);
+ queue_work(hbus->wq, &dr_wrk->wrk);
+ }
}
/**
@@ -1796,10 +1853,7 @@ static void hv_eject_device_work(struct work_struct *work)
hpdev = container_of(work, struct hv_pci_dev, wrk);
- if (hpdev->state != hv_pcichild_ejecting) {
- put_pcichild(hpdev, hv_pcidev_ref_pnp);
- return;
- }
+ WARN_ON(hpdev->state != hv_pcichild_ejecting);
/*
* Ejection can come before or after the PCI bus has been set up, so
@@ -1848,7 +1902,7 @@ static void hv_pci_eject_device(struct hv_pci_dev *hpdev)
get_pcichild(hpdev, hv_pcidev_ref_pnp);
INIT_WORK(&hpdev->wrk, hv_eject_device_work);
get_hvpcibus(hpdev->hbus);
- schedule_work(&hpdev->wrk);
+ queue_work(hpdev->hbus->wq, &hpdev->wrk);
}
/**
@@ -2461,13 +2515,18 @@ static int hv_pci_probe(struct hv_device *hdev,
spin_lock_init(&hbus->config_lock);
spin_lock_init(&hbus->device_list_lock);
spin_lock_init(&hbus->retarget_msi_interrupt_lock);
- sema_init(&hbus->enum_sem, 1);
init_completion(&hbus->remove_event);
+ hbus->wq = alloc_ordered_workqueue("hv_pci_%x", 0,
+ hbus->sysdata.domain);
+ if (!hbus->wq) {
+ ret = -ENOMEM;
+ goto free_bus;
+ }
ret = vmbus_open(hdev->channel, pci_ring_size, pci_ring_size, NULL, 0,
hv_pci_onchannelcallback, hbus);
if (ret)
- goto free_bus;
+ goto destroy_wq;
hv_set_drvdata(hdev, hbus);
@@ -2536,6 +2595,8 @@ free_config:
hv_free_config_window(hbus);
close:
vmbus_close(hdev->channel);
+destroy_wq:
+ destroy_workqueue(hbus->wq);
free_bus:
free_page((unsigned long)hbus);
return ret;
@@ -2615,6 +2676,7 @@ static int hv_pci_remove(struct hv_device *hdev)
irq_domain_free_fwnode(hbus->sysdata.fwnode);
put_hvpcibus(hbus);
wait_for_completion(&hbus->remove_event);
+ destroy_workqueue(hbus->wq);
free_page((unsigned long)hbus);
return 0;
}
diff --git a/drivers/pci/host/pci-rcar-gen2.c b/drivers/pci/host/pci-rcar-gen2.c
index a28370bb2b2a..dd4f1a6b57c5 100644
--- a/drivers/pci/host/pci-rcar-gen2.c
+++ b/drivers/pci/host/pci-rcar-gen2.c
@@ -53,7 +53,6 @@
#define RCAR_PCI_INT_PME (1 << 19)
#define RCAR_PCI_INT_ALLERRORS (RCAR_PCI_INT_SIGTABORT | \
RCAR_PCI_INT_SIGRETABORT | \
- RCAR_PCI_INT_SIGRETABORT | \
RCAR_PCI_INT_REMABORT | \
RCAR_PCI_INT_PERR | \
RCAR_PCI_INT_SIGSERR | \
diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c
index dd9b3bcc41c3..389e74be846c 100644
--- a/drivers/pci/host/pci-tegra.c
+++ b/drivers/pci/host/pci-tegra.c
@@ -18,10 +18,12 @@
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/interrupt.h>
+#include <linux/iopoll.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/module.h>
#include <linux/msi.h>
#include <linux/of_address.h>
#include <linux/of_pci.h>
@@ -139,6 +141,8 @@
#define AFI_INTR_EN_FPCI_TIMEOUT (1 << 7)
#define AFI_INTR_EN_PRSNT_SENSE (1 << 8)
+#define AFI_PCIE_PME 0xf0
+
#define AFI_PCIE_CONFIG 0x0f8
#define AFI_PCIE_CONFIG_PCIE_DISABLE(x) (1 << ((x) + 1))
#define AFI_PCIE_CONFIG_PCIE_DISABLE_ALL 0xe
@@ -219,6 +223,8 @@
#define PADS_REFCLK_CFG_PREDI_SHIFT 8 /* 11:8 */
#define PADS_REFCLK_CFG_DRVI_SHIFT 12 /* 15:12 */
+#define PME_ACK_TIMEOUT 10000
+
struct tegra_msi {
struct msi_controller chip;
DECLARE_BITMAP(used, INT_PCI_MSI_NR);
@@ -230,8 +236,16 @@ struct tegra_msi {
};
/* used to differentiate between Tegra SoC generations */
+struct tegra_pcie_port_soc {
+ struct {
+ u8 turnoff_bit;
+ u8 ack_bit;
+ } pme;
+};
+
struct tegra_pcie_soc {
unsigned int num_ports;
+ const struct tegra_pcie_port_soc *ports;
unsigned int msi_base_shift;
u32 pads_pll_ctl;
u32 tx_ref_sel;
@@ -549,14 +563,25 @@ static int tegra_pcie_request_resources(struct tegra_pcie *pcie)
pci_add_resource(windows, &pcie->busn);
err = devm_request_pci_bus_resources(dev, windows);
- if (err < 0)
+ if (err < 0) {
+ pci_free_resource_list(windows);
return err;
+ }
pci_remap_iospace(&pcie->pio, pcie->io.start);
return 0;
}
+static void tegra_pcie_free_resources(struct tegra_pcie *pcie)
+{
+ struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie);
+ struct list_head *windows = &host->windows;
+
+ pci_unmap_iospace(&pcie->pio);
+ pci_free_resource_list(windows);
+}
+
static int tegra_pcie_map_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
{
struct tegra_pcie *pcie = pdev->bus->sysdata;
@@ -966,24 +991,35 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
return 0;
}
-static void tegra_pcie_power_off(struct tegra_pcie *pcie)
+static void tegra_pcie_disable_controller(struct tegra_pcie *pcie)
{
- struct device *dev = pcie->dev;
- const struct tegra_pcie_soc *soc = pcie->soc;
int err;
- /* TODO: disable and unprepare clocks? */
+ reset_control_assert(pcie->pcie_xrst);
- if (soc->program_uphy) {
+ if (pcie->soc->program_uphy) {
err = tegra_pcie_phy_power_off(pcie);
if (err < 0)
- dev_err(dev, "failed to power off PHY(s): %d\n", err);
+ dev_err(pcie->dev, "failed to power off PHY(s): %d\n",
+ err);
}
+}
+
+static void tegra_pcie_power_off(struct tegra_pcie *pcie)
+{
+ struct device *dev = pcie->dev;
+ const struct tegra_pcie_soc *soc = pcie->soc;
+ int err;
- reset_control_assert(pcie->pcie_xrst);
reset_control_assert(pcie->afi_rst);
reset_control_assert(pcie->pex_rst);
+ clk_disable_unprepare(pcie->pll_e);
+ if (soc->has_cml_clk)
+ clk_disable_unprepare(pcie->cml_clk);
+ clk_disable_unprepare(pcie->afi_clk);
+ clk_disable_unprepare(pcie->pex_clk);
+
if (!dev->pm_domain)
tegra_powergate_power_off(TEGRA_POWERGATE_PCIE);
@@ -1192,6 +1228,30 @@ static int tegra_pcie_phys_get(struct tegra_pcie *pcie)
return 0;
}
+static void tegra_pcie_phys_put(struct tegra_pcie *pcie)
+{
+ struct tegra_pcie_port *port;
+ struct device *dev = pcie->dev;
+ int err, i;
+
+ if (pcie->legacy_phy) {
+ err = phy_exit(pcie->phy);
+ if (err < 0)
+ dev_err(dev, "failed to teardown PHY: %d\n", err);
+ return;
+ }
+
+ list_for_each_entry(port, &pcie->ports, list) {
+ for (i = 0; i < port->lanes; i++) {
+ err = phy_exit(port->phys[i]);
+ if (err < 0)
+ dev_err(dev, "failed to teardown PHY#%u: %d\n",
+ i, err);
+ }
+ }
+}
+
+
static int tegra_pcie_get_resources(struct tegra_pcie *pcie)
{
struct device *dev = pcie->dev;
@@ -1220,31 +1280,25 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie)
}
}
- err = tegra_pcie_power_on(pcie);
- if (err) {
- dev_err(dev, "failed to power up: %d\n", err);
- return err;
- }
-
pads = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pads");
pcie->pads = devm_ioremap_resource(dev, pads);
if (IS_ERR(pcie->pads)) {
err = PTR_ERR(pcie->pads);
- goto poweroff;
+ goto phys_put;
}
afi = platform_get_resource_byname(pdev, IORESOURCE_MEM, "afi");
pcie->afi = devm_ioremap_resource(dev, afi);
if (IS_ERR(pcie->afi)) {
err = PTR_ERR(pcie->afi);
- goto poweroff;
+ goto phys_put;
}
/* request configuration space, but remap later, on demand */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cs");
if (!res) {
err = -EADDRNOTAVAIL;
- goto poweroff;
+ goto phys_put;
}
pcie->cs = *res;
@@ -1255,14 +1309,14 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie)
pcie->cfg = devm_ioremap_resource(dev, &pcie->cs);
if (IS_ERR(pcie->cfg)) {
err = PTR_ERR(pcie->cfg);
- goto poweroff;
+ goto phys_put;
}
/* request interrupt */
err = platform_get_irq_byname(pdev, "intr");
if (err < 0) {
dev_err(dev, "failed to get IRQ: %d\n", err);
- goto poweroff;
+ goto phys_put;
}
pcie->irq = err;
@@ -1270,36 +1324,56 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie)
err = request_irq(pcie->irq, tegra_pcie_isr, IRQF_SHARED, "PCIE", pcie);
if (err) {
dev_err(dev, "failed to register IRQ: %d\n", err);
- goto poweroff;
+ goto phys_put;
}
return 0;
-poweroff:
- tegra_pcie_power_off(pcie);
+phys_put:
+ if (soc->program_uphy)
+ tegra_pcie_phys_put(pcie);
return err;
}
static int tegra_pcie_put_resources(struct tegra_pcie *pcie)
{
- struct device *dev = pcie->dev;
const struct tegra_pcie_soc *soc = pcie->soc;
- int err;
if (pcie->irq > 0)
free_irq(pcie->irq, pcie);
- tegra_pcie_power_off(pcie);
-
- if (soc->program_uphy) {
- err = phy_exit(pcie->phy);
- if (err < 0)
- dev_err(dev, "failed to teardown PHY: %d\n", err);
- }
+ if (soc->program_uphy)
+ tegra_pcie_phys_put(pcie);
return 0;
}
+static void tegra_pcie_pme_turnoff(struct tegra_pcie_port *port)
+{
+ struct tegra_pcie *pcie = port->pcie;
+ const struct tegra_pcie_soc *soc = pcie->soc;
+ int err;
+ u32 val;
+ u8 ack_bit;
+
+ val = afi_readl(pcie, AFI_PCIE_PME);
+ val |= (0x1 << soc->ports[port->index].pme.turnoff_bit);
+ afi_writel(pcie, val, AFI_PCIE_PME);
+
+ ack_bit = soc->ports[port->index].pme.ack_bit;
+ err = readl_poll_timeout(pcie->afi + AFI_PCIE_PME, val,
+ val & (0x1 << ack_bit), 1, PME_ACK_TIMEOUT);
+ if (err)
+ dev_err(pcie->dev, "PME Ack is not received on port: %d\n",
+ port->index);
+
+ usleep_range(10000, 11000);
+
+ val = afi_readl(pcie, AFI_PCIE_PME);
+ val &= ~(0x1 << soc->ports[port->index].pme.turnoff_bit);
+ afi_writel(pcie, val, AFI_PCIE_PME);
+}
+
static int tegra_msi_alloc(struct tegra_msi *chip)
{
int msi;
@@ -1436,15 +1510,13 @@ static const struct irq_domain_ops msi_domain_ops = {
.map = tegra_msi_map,
};
-static int tegra_pcie_enable_msi(struct tegra_pcie *pcie)
+static int tegra_pcie_msi_setup(struct tegra_pcie *pcie)
{
struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie);
struct platform_device *pdev = to_platform_device(pcie->dev);
- const struct tegra_pcie_soc *soc = pcie->soc;
struct tegra_msi *msi = &pcie->msi;
struct device *dev = pcie->dev;
int err;
- u32 reg;
mutex_init(&msi->lock);
@@ -1477,6 +1549,20 @@ static int tegra_pcie_enable_msi(struct tegra_pcie *pcie)
/* setup AFI/FPCI range */
msi->pages = __get_free_pages(GFP_KERNEL, 0);
msi->phys = virt_to_phys((void *)msi->pages);
+ host->msi = &msi->chip;
+
+ return 0;
+
+err:
+ irq_domain_remove(msi->domain);
+ return err;
+}
+
+static void tegra_pcie_enable_msi(struct tegra_pcie *pcie)
+{
+ const struct tegra_pcie_soc *soc = pcie->soc;
+ struct tegra_msi *msi = &pcie->msi;
+ u32 reg;
afi_writel(pcie, msi->phys >> soc->msi_base_shift, AFI_MSI_FPCI_BAR_ST);
afi_writel(pcie, msi->phys, AFI_MSI_AXI_BAR_ST);
@@ -1497,20 +1583,29 @@ static int tegra_pcie_enable_msi(struct tegra_pcie *pcie)
reg = afi_readl(pcie, AFI_INTR_MASK);
reg |= AFI_INTR_MASK_MSI_MASK;
afi_writel(pcie, reg, AFI_INTR_MASK);
+}
- host->msi = &msi->chip;
+static void tegra_pcie_msi_teardown(struct tegra_pcie *pcie)
+{
+ struct tegra_msi *msi = &pcie->msi;
+ unsigned int i, irq;
- return 0;
+ free_pages(msi->pages, 0);
+
+ if (msi->irq > 0)
+ free_irq(msi->irq, pcie);
+
+ for (i = 0; i < INT_PCI_MSI_NR; i++) {
+ irq = irq_find_mapping(msi->domain, i);
+ if (irq > 0)
+ irq_dispose_mapping(irq);
+ }
-err:
irq_domain_remove(msi->domain);
- return err;
}
static int tegra_pcie_disable_msi(struct tegra_pcie *pcie)
{
- struct tegra_msi *msi = &pcie->msi;
- unsigned int i, irq;
u32 value;
/* mask the MSI interrupt */
@@ -1528,19 +1623,6 @@ static int tegra_pcie_disable_msi(struct tegra_pcie *pcie)
afi_writel(pcie, 0, AFI_MSI_EN_VEC6);
afi_writel(pcie, 0, AFI_MSI_EN_VEC7);
- free_pages(msi->pages, 0);
-
- if (msi->irq > 0)
- free_irq(msi->irq, pcie);
-
- for (i = 0; i < INT_PCI_MSI_NR; i++) {
- irq = irq_find_mapping(msi->domain, i);
- if (irq > 0)
- irq_dispose_mapping(irq);
- }
-
- irq_domain_remove(msi->domain);
-
return 0;
}
@@ -2035,8 +2117,22 @@ static void tegra_pcie_enable_ports(struct tegra_pcie *pcie)
}
}
+static void tegra_pcie_disable_ports(struct tegra_pcie *pcie)
+{
+ struct tegra_pcie_port *port, *tmp;
+
+ list_for_each_entry_safe(port, tmp, &pcie->ports, list)
+ tegra_pcie_port_disable(port);
+}
+
+static const struct tegra_pcie_port_soc tegra20_pcie_ports[] = {
+ { .pme.turnoff_bit = 0, .pme.ack_bit = 5 },
+ { .pme.turnoff_bit = 8, .pme.ack_bit = 10 },
+};
+
static const struct tegra_pcie_soc tegra20_pcie = {
.num_ports = 2,
+ .ports = tegra20_pcie_ports,
.msi_base_shift = 0,
.pads_pll_ctl = PADS_PLL_CTL_TEGRA20,
.tx_ref_sel = PADS_PLL_CTL_TXCLKREF_DIV10,
@@ -2050,8 +2146,15 @@ static const struct tegra_pcie_soc tegra20_pcie = {
.program_uphy = true,
};
+static const struct tegra_pcie_port_soc tegra30_pcie_ports[] = {
+ { .pme.turnoff_bit = 0, .pme.ack_bit = 5 },
+ { .pme.turnoff_bit = 8, .pme.ack_bit = 10 },
+ { .pme.turnoff_bit = 16, .pme.ack_bit = 18 },
+};
+
static const struct tegra_pcie_soc tegra30_pcie = {
.num_ports = 3,
+ .ports = tegra30_pcie_ports,
.msi_base_shift = 8,
.pads_pll_ctl = PADS_PLL_CTL_TEGRA30,
.tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN,
@@ -2068,6 +2171,7 @@ static const struct tegra_pcie_soc tegra30_pcie = {
static const struct tegra_pcie_soc tegra124_pcie = {
.num_ports = 2,
+ .ports = tegra20_pcie_ports,
.msi_base_shift = 8,
.pads_pll_ctl = PADS_PLL_CTL_TEGRA30,
.tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN,
@@ -2083,6 +2187,7 @@ static const struct tegra_pcie_soc tegra124_pcie = {
static const struct tegra_pcie_soc tegra210_pcie = {
.num_ports = 2,
+ .ports = tegra20_pcie_ports,
.msi_base_shift = 8,
.pads_pll_ctl = PADS_PLL_CTL_TEGRA30,
.tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN,
@@ -2096,8 +2201,15 @@ static const struct tegra_pcie_soc tegra210_pcie = {
.program_uphy = true,
};
+static const struct tegra_pcie_port_soc tegra186_pcie_ports[] = {
+ { .pme.turnoff_bit = 0, .pme.ack_bit = 5 },
+ { .pme.turnoff_bit = 8, .pme.ack_bit = 10 },
+ { .pme.turnoff_bit = 12, .pme.ack_bit = 14 },
+};
+
static const struct tegra_pcie_soc tegra186_pcie = {
.num_ports = 3,
+ .ports = tegra186_pcie_ports,
.msi_base_shift = 8,
.pads_pll_ctl = PADS_PLL_CTL_TEGRA30,
.tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN,
@@ -2209,6 +2321,12 @@ static const struct file_operations tegra_pcie_ports_ops = {
.release = seq_release,
};
+static void tegra_pcie_debugfs_exit(struct tegra_pcie *pcie)
+{
+ debugfs_remove_recursive(pcie->debugfs);
+ pcie->debugfs = NULL;
+}
+
static int tegra_pcie_debugfs_init(struct tegra_pcie *pcie)
{
struct dentry *file;
@@ -2225,8 +2343,7 @@ static int tegra_pcie_debugfs_init(struct tegra_pcie *pcie)
return 0;
remove:
- debugfs_remove_recursive(pcie->debugfs);
- pcie->debugfs = NULL;
+ tegra_pcie_debugfs_exit(pcie);
return -ENOMEM;
}
@@ -2244,6 +2361,7 @@ static int tegra_pcie_probe(struct platform_device *pdev)
pcie = pci_host_bridge_priv(host);
host->sysdata = pcie;
+ platform_set_drvdata(pdev, pcie);
pcie->soc = of_device_get_match_data(dev);
INIT_LIST_HEAD(&pcie->ports);
@@ -2259,26 +2377,22 @@ static int tegra_pcie_probe(struct platform_device *pdev)
return err;
}
- err = tegra_pcie_enable_controller(pcie);
- if (err)
- goto put_resources;
-
- err = tegra_pcie_request_resources(pcie);
- if (err)
+ err = tegra_pcie_msi_setup(pcie);
+ if (err < 0) {
+ dev_err(dev, "failed to enable MSI support: %d\n", err);
goto put_resources;
+ }
- /* setup the AFI address translations */
- tegra_pcie_setup_translations(pcie);
-
- if (IS_ENABLED(CONFIG_PCI_MSI)) {
- err = tegra_pcie_enable_msi(pcie);
- if (err < 0) {
- dev_err(dev, "failed to enable MSI support: %d\n", err);
- goto put_resources;
- }
+ pm_runtime_enable(pcie->dev);
+ err = pm_runtime_get_sync(pcie->dev);
+ if (err) {
+ dev_err(dev, "fail to enable pcie controller: %d\n", err);
+ goto teardown_msi;
}
- tegra_pcie_enable_ports(pcie);
+ err = tegra_pcie_request_resources(pcie);
+ if (err)
+ goto pm_runtime_put;
host->busnr = pcie->busn.start;
host->dev.parent = &pdev->dev;
@@ -2289,7 +2403,7 @@ static int tegra_pcie_probe(struct platform_device *pdev)
err = pci_scan_root_bus_bridge(host);
if (err < 0) {
dev_err(dev, "failed to register host: %d\n", err);
- goto disable_msi;
+ goto free_resources;
}
pci_bus_size_bridges(host->bus);
@@ -2308,20 +2422,108 @@ static int tegra_pcie_probe(struct platform_device *pdev)
return 0;
-disable_msi:
- if (IS_ENABLED(CONFIG_PCI_MSI))
- tegra_pcie_disable_msi(pcie);
+free_resources:
+ tegra_pcie_free_resources(pcie);
+pm_runtime_put:
+ pm_runtime_put_sync(pcie->dev);
+ pm_runtime_disable(pcie->dev);
+teardown_msi:
+ tegra_pcie_msi_teardown(pcie);
put_resources:
tegra_pcie_put_resources(pcie);
return err;
}
+static int tegra_pcie_remove(struct platform_device *pdev)
+{
+ struct tegra_pcie *pcie = platform_get_drvdata(pdev);
+ struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie);
+ struct tegra_pcie_port *port, *tmp;
+
+ if (IS_ENABLED(CONFIG_DEBUG_FS))
+ tegra_pcie_debugfs_exit(pcie);
+
+ pci_stop_root_bus(host->bus);
+ pci_remove_root_bus(host->bus);
+ tegra_pcie_free_resources(pcie);
+ pm_runtime_put_sync(pcie->dev);
+ pm_runtime_disable(pcie->dev);
+
+ if (IS_ENABLED(CONFIG_PCI_MSI))
+ tegra_pcie_msi_teardown(pcie);
+
+ tegra_pcie_put_resources(pcie);
+
+ list_for_each_entry_safe(port, tmp, &pcie->ports, list)
+ tegra_pcie_port_free(port);
+
+ return 0;
+}
+
+static int __maybe_unused tegra_pcie_pm_suspend(struct device *dev)
+{
+ struct tegra_pcie *pcie = dev_get_drvdata(dev);
+ struct tegra_pcie_port *port;
+
+ list_for_each_entry(port, &pcie->ports, list)
+ tegra_pcie_pme_turnoff(port);
+
+ tegra_pcie_disable_ports(pcie);
+
+ if (IS_ENABLED(CONFIG_PCI_MSI))
+ tegra_pcie_disable_msi(pcie);
+
+ tegra_pcie_disable_controller(pcie);
+ tegra_pcie_power_off(pcie);
+
+ return 0;
+}
+
+static int __maybe_unused tegra_pcie_pm_resume(struct device *dev)
+{
+ struct tegra_pcie *pcie = dev_get_drvdata(dev);
+ int err;
+
+ err = tegra_pcie_power_on(pcie);
+ if (err) {
+ dev_err(dev, "tegra pcie power on fail: %d\n", err);
+ return err;
+ }
+ err = tegra_pcie_enable_controller(pcie);
+ if (err) {
+ dev_err(dev, "tegra pcie controller enable fail: %d\n", err);
+ goto poweroff;
+ }
+ tegra_pcie_setup_translations(pcie);
+
+ if (IS_ENABLED(CONFIG_PCI_MSI))
+ tegra_pcie_enable_msi(pcie);
+
+ tegra_pcie_enable_ports(pcie);
+
+ return 0;
+
+poweroff:
+ tegra_pcie_power_off(pcie);
+
+ return err;
+}
+
+static const struct dev_pm_ops tegra_pcie_pm_ops = {
+ SET_RUNTIME_PM_OPS(tegra_pcie_pm_suspend, tegra_pcie_pm_resume, NULL)
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_pcie_pm_suspend,
+ tegra_pcie_pm_resume)
+};
+
static struct platform_driver tegra_pcie_driver = {
.driver = {
.name = "tegra-pcie",
.of_match_table = tegra_pcie_of_match,
.suppress_bind_attrs = true,
+ .pm = &tegra_pcie_pm_ops,
},
.probe = tegra_pcie_probe,
+ .remove = tegra_pcie_remove,
};
-builtin_platform_driver(tegra_pcie_driver);
+module_platform_driver(tegra_pcie_driver);
+MODULE_LICENSE("GPL");
diff --git a/drivers/pci/host/pci-v3-semi.c b/drivers/pci/host/pci-v3-semi.c
index 7fef64869d19..0a4dea796663 100644
--- a/drivers/pci/host/pci-v3-semi.c
+++ b/drivers/pci/host/pci-v3-semi.c
@@ -673,7 +673,7 @@ static int v3_get_dma_range_config(struct v3_pci *v3,
dev_err(v3->dev, "illegal dma memory chunk size\n");
return -EINVAL;
break;
- };
+ }
val |= V3_PCI_MAP_M_REG_EN | V3_PCI_MAP_M_ENABLE;
*pci_map = val;
diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
index df8e4bd5ddb2..f4c02da84e59 100644
--- a/drivers/pci/host/pci-xgene-msi.c
+++ b/drivers/pci/host/pci-xgene-msi.c
@@ -456,7 +456,7 @@ static int xgene_msi_probe(struct platform_device *pdev)
xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(xgene_msi->msi_regs)) {
dev_err(&pdev->dev, "no reg space\n");
- rc = -EINVAL;
+ rc = PTR_ERR(xgene_msi->msi_regs);
goto error;
}
xgene_msi->msi_addr = res->start;
diff --git a/drivers/pci/host/pcie-altera.c b/drivers/pci/host/pcie-altera.c
index 2235f4760951..a6af62e0256d 100644
--- a/drivers/pci/host/pcie-altera.c
+++ b/drivers/pci/host/pcie-altera.c
@@ -145,7 +145,7 @@ static bool altera_pcie_valid_device(struct altera_pcie *pcie,
static int tlp_read_packet(struct altera_pcie *pcie, u32 *value)
{
int i;
- bool sop = 0;
+ bool sop = false;
u32 ctrl;
u32 reg0, reg1;
u32 comp_status = 1;
diff --git a/drivers/pci/host/pcie-iproc-bcma.c b/drivers/pci/host/pcie-iproc-bcma.c
index 603c83429cb3..aa55b064f64d 100644
--- a/drivers/pci/host/pcie-iproc-bcma.c
+++ b/drivers/pci/host/pcie-iproc-bcma.c
@@ -25,8 +25,7 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x8012, bcma_pcie2_fixup_class);
static int iproc_pcie_bcma_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
- struct pci_sys_data *sys = dev->sysdata;
- struct iproc_pcie *pcie = sys->private_data;
+ struct iproc_pcie *pcie = dev->sysdata;
struct bcma_device *bdev = container_of(pcie->dev, struct bcma_device, dev);
return bcma_core_irq(bdev, 5);
diff --git a/drivers/pci/host/pcie-iproc.c b/drivers/pci/host/pcie-iproc.c
index cbb095481cdc..3c76c5fa4f32 100644
--- a/drivers/pci/host/pcie-iproc.c
+++ b/drivers/pci/host/pcie-iproc.c
@@ -377,14 +377,7 @@ static const u16 iproc_pcie_reg_paxc_v2[] = {
static inline struct iproc_pcie *iproc_data(struct pci_bus *bus)
{
- struct iproc_pcie *pcie;
-#ifdef CONFIG_ARM
- struct pci_sys_data *sys = bus->sysdata;
-
- pcie = sys->private_data;
-#else
- pcie = bus->sysdata;
-#endif
+ struct iproc_pcie *pcie = bus->sysdata;
return pcie;
}
@@ -1331,7 +1324,6 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
{
struct device *dev;
int ret;
- void *sysdata;
struct pci_bus *child;
struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie);
@@ -1376,13 +1368,6 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
goto err_power_off_phy;
}
-#ifdef CONFIG_ARM
- pcie->sysdata.private_data = pcie;
- sysdata = &pcie->sysdata;
-#else
- sysdata = pcie;
-#endif
-
ret = iproc_pcie_check_link(pcie);
if (ret) {
dev_err(dev, "no PCIe EP device detected\n");
@@ -1399,7 +1384,7 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
host->busnr = 0;
host->dev.parent = dev;
host->ops = &iproc_pcie_ops;
- host->sysdata = sysdata;
+ host->sysdata = pcie;
host->map_irq = pcie->map_irq;
host->swizzle_irq = pci_common_swizzle;
diff --git a/drivers/pci/host/pcie-iproc.h b/drivers/pci/host/pcie-iproc.h
index d55f56a186cd..814b600b383a 100644
--- a/drivers/pci/host/pcie-iproc.h
+++ b/drivers/pci/host/pcie-iproc.h
@@ -54,7 +54,6 @@ struct iproc_msi;
* @reg_offsets: register offsets
* @base: PCIe host controller I/O register base
* @base_addr: PCIe host controller register base physical address
- * @sysdata: Per PCI controller data (ARM-specific)
* @root_bus: pointer to root bus
* @phy: optional PHY device that controls the Serdes
* @map_irq: function callback to map interrupts
@@ -80,9 +79,6 @@ struct iproc_pcie {
u16 *reg_offsets;
void __iomem *base;
phys_addr_t base_addr;
-#ifdef CONFIG_ARM
- struct pci_sys_data sysdata;
-#endif
struct resource mem;
struct pci_bus *root_bus;
struct phy *phy;
diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c
index b4c4aad2cf66..6ab28f29ac6a 100644
--- a/drivers/pci/host/pcie-rcar.c
+++ b/drivers/pci/host/pcie-rcar.c
@@ -435,7 +435,7 @@ static void rcar_pcie_force_speedup(struct rcar_pcie *pcie)
}
msleep(1);
- };
+ }
dev_err(dev, "Speed change timed out\n");
diff --git a/drivers/pci/host/pcie-xilinx-nwl.c b/drivers/pci/host/pcie-xilinx-nwl.c
index 0acaf483d031..4839ae578711 100644
--- a/drivers/pci/host/pcie-xilinx-nwl.c
+++ b/drivers/pci/host/pcie-xilinx-nwl.c
@@ -630,7 +630,7 @@ static int nwl_pcie_enable_msi(struct nwl_pcie *pcie)
* For high range MSI interrupts: disable, clear any pending,
* and enable
*/
- nwl_bridge_writel(pcie, (u32)~MSGF_MSI_SR_HI_MASK, MSGF_MSI_MASK_HI);
+ nwl_bridge_writel(pcie, 0, MSGF_MSI_MASK_HI);
nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, MSGF_MSI_STATUS_HI) &
MSGF_MSI_SR_HI_MASK, MSGF_MSI_STATUS_HI);
@@ -641,7 +641,7 @@ static int nwl_pcie_enable_msi(struct nwl_pcie *pcie)
* For low range MSI interrupts: disable, clear any pending,
* and enable
*/
- nwl_bridge_writel(pcie, (u32)~MSGF_MSI_SR_LO_MASK, MSGF_MSI_MASK_LO);
+ nwl_bridge_writel(pcie, 0, MSGF_MSI_MASK_LO);
nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, MSGF_MSI_STATUS_LO) &
MSGF_MSI_SR_LO_MASK, MSGF_MSI_STATUS_LO);
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index e2198a2feeca..b45b375c0e6c 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -541,6 +541,7 @@ static unsigned int get_slot_status(struct acpiphp_slot *slot)
{
unsigned long long sta = 0;
struct acpiphp_func *func;
+ u32 dvid;
list_for_each_entry(func, &slot->funcs, sibling) {
if (func->flags & FUNC_HAS_STA) {
@@ -551,19 +552,27 @@ static unsigned int get_slot_status(struct acpiphp_slot *slot)
if (ACPI_SUCCESS(status) && sta)
break;
} else {
- u32 dvid;
-
- pci_bus_read_config_dword(slot->bus,
- PCI_DEVFN(slot->device,
- func->function),
- PCI_VENDOR_ID, &dvid);
- if (dvid != 0xffffffff) {
+ if (pci_bus_read_dev_vendor_id(slot->bus,
+ PCI_DEVFN(slot->device, func->function),
+ &dvid, 0)) {
sta = ACPI_STA_ALL;
break;
}
}
}
+ if (!sta) {
+ /*
+ * Check for the slot itself since it may be that the
+ * ACPI slot is a device below PCIe upstream port so in
+ * that case it may not even be reachable yet.
+ */
+ if (pci_bus_read_dev_vendor_id(slot->bus,
+ PCI_DEVFN(slot->device, 0), &dvid, 0)) {
+ sta = ACPI_STA_ALL;
+ }
+ }
+
return (unsigned int)sta;
}
diff --git a/drivers/pci/hotplug/cpqphp_ctrl.c b/drivers/pci/hotplug/cpqphp_ctrl.c
index b1b6e45253b2..616df442520b 100644
--- a/drivers/pci/hotplug/cpqphp_ctrl.c
+++ b/drivers/pci/hotplug/cpqphp_ctrl.c
@@ -2812,18 +2812,16 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func
dbg("CND: length = 0x%x\n", base);
io_node = get_io_resource(&(resources->io_head), base);
+ if (!io_node)
+ return -ENOMEM;
dbg("Got io_node start = %8.8x, length = %8.8x next (%p)\n",
io_node->base, io_node->length, io_node->next);
dbg("func (%p) io_head (%p)\n", func, func->io_head);
/* allocate the resource to the board */
- if (io_node) {
- base = io_node->base;
-
- io_node->next = func->io_head;
- func->io_head = io_node;
- } else
- return -ENOMEM;
+ base = io_node->base;
+ io_node->next = func->io_head;
+ func->io_head = io_node;
} else if ((temp_register & 0x0BL) == 0x08) {
/* Map prefetchable memory */
base = temp_register & 0xFFFFFFF0;
diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h
index 636ed8f4b869..88e917c9120f 100644
--- a/drivers/pci/hotplug/pciehp.h
+++ b/drivers/pci/hotplug/pciehp.h
@@ -20,10 +20,11 @@
#include <linux/pci_hotplug.h>
#include <linux/delay.h>
#include <linux/sched/signal.h> /* signal_pending() */
-#include <linux/pcieport_if.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
+#include "../pcie/portdrv.h"
+
#define MY_NAME "pciehp"
extern bool pciehp_poll_mode;
diff --git a/drivers/pci/hotplug/pnv_php.c b/drivers/pci/hotplug/pnv_php.c
index 23da3046f160..d44100687dfe 100644
--- a/drivers/pci/hotplug/pnv_php.c
+++ b/drivers/pci/hotplug/pnv_php.c
@@ -919,8 +919,8 @@ static void pnv_php_unregister_one(struct device_node *dn)
return;
php_slot->state = PNV_PHP_STATE_OFFLINE;
- pnv_php_put_slot(php_slot);
pci_hp_deregister(&php_slot->slot);
+ pnv_php_put_slot(php_slot);
}
static void pnv_php_unregister(struct device_node *dn)
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index 677924ae0350..8adf4a64f291 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -1,12 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * drivers/pci/iov.c
- *
- * Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com>
- *
- * PCI Express I/O Virtualization (IOV) support.
+ * PCI Express I/O Virtualization (IOV) support
* Single Root IOV 1.0
* Address Translation Service 1.0
+ *
+ * Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com>
*/
#include <linux/pci.h>
@@ -114,6 +112,29 @@ resource_size_t pci_iov_resource_size(struct pci_dev *dev, int resno)
return dev->sriov->barsz[resno - PCI_IOV_RESOURCES];
}
+static void pci_read_vf_config_common(struct pci_dev *virtfn)
+{
+ struct pci_dev *physfn = virtfn->physfn;
+
+ /*
+ * Some config registers are the same across all associated VFs.
+ * Read them once from VF0 so we can skip reading them from the
+ * other VFs.
+ *
+ * PCIe r4.0, sec 9.3.4.1, technically doesn't require all VFs to
+ * have the same Revision ID and Subsystem ID, but we assume they
+ * do.
+ */
+ pci_read_config_dword(virtfn, PCI_CLASS_REVISION,
+ &physfn->sriov->class);
+ pci_read_config_byte(virtfn, PCI_HEADER_TYPE,
+ &physfn->sriov->hdr_type);
+ pci_read_config_word(virtfn, PCI_SUBSYSTEM_VENDOR_ID,
+ &physfn->sriov->subsystem_vendor);
+ pci_read_config_word(virtfn, PCI_SUBSYSTEM_ID,
+ &physfn->sriov->subsystem_device);
+}
+
int pci_iov_add_virtfn(struct pci_dev *dev, int id)
{
int i;
@@ -136,13 +157,17 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id)
virtfn->devfn = pci_iov_virtfn_devfn(dev, id);
virtfn->vendor = dev->vendor;
virtfn->device = iov->vf_device;
+ virtfn->is_virtfn = 1;
+ virtfn->physfn = pci_dev_get(dev);
+
+ if (id == 0)
+ pci_read_vf_config_common(virtfn);
+
rc = pci_setup_device(virtfn);
if (rc)
- goto failed0;
+ goto failed1;
virtfn->dev.parent = dev->dev.parent;
- virtfn->physfn = pci_dev_get(dev);
- virtfn->is_virtfn = 1;
virtfn->multifunction = 0;
for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
@@ -163,10 +188,10 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id)
sprintf(buf, "virtfn%u", id);
rc = sysfs_create_link(&dev->dev.kobj, &virtfn->dev.kobj, buf);
if (rc)
- goto failed1;
+ goto failed2;
rc = sysfs_create_link(&virtfn->dev.kobj, &dev->dev.kobj, "physfn");
if (rc)
- goto failed2;
+ goto failed3;
kobject_uevent(&virtfn->dev.kobj, KOBJ_CHANGE);
@@ -174,11 +199,12 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id)
return 0;
-failed2:
+failed3:
sysfs_remove_link(&dev->dev.kobj, buf);
+failed2:
+ pci_stop_and_remove_bus_device(virtfn);
failed1:
pci_dev_put(dev);
- pci_stop_and_remove_bus_device(virtfn);
failed0:
virtfn_remove_bus(dev->bus, bus);
failed:
diff --git a/drivers/pci/mmap.c b/drivers/pci/mmap.c
index 814a3ce341fc..24505b08de40 100644
--- a/drivers/pci/mmap.c
+++ b/drivers/pci/mmap.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * mmap.c — generic PCI resource mmap helper
+ * Generic PCI resource mmap helper
*
* Copyright © 2017 Amazon.com, Inc. or its affiliates.
*
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index 8b0729c94bb7..30250631efe7 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * File: msi.c
- * Purpose: PCI Message Signaled Interrupt (MSI)
+ * PCI Message Signaled Interrupt (MSI)
*
* Copyright (C) 2003-2004 Intel
* Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index 78157688dcc9..1abdbf267c19 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * File: pci-acpi.c
- * Purpose: Provide PCI support in ACPI
+ * PCI support in ACPI
*
* Copyright (C) 2005 David Shaohua Li <shaohua.li@intel.com>
* Copyright (C) 2004 Tom Long Nguyen <tom.l.nguyen@intel.com>
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 6a67cdbd0e6a..b9a131137e64 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -1,7 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * drivers/pci/pci-driver.c
- *
* (C) Copyright 2002-2004, 2007 Greg Kroah-Hartman <greg@kroah.com>
* (C) Copyright 2007 Novell Inc.
*/
@@ -19,6 +17,7 @@
#include <linux/suspend.h>
#include <linux/kexec.h>
#include "pci.h"
+#include "pcie/portdrv.h"
struct pci_dynid {
struct list_head node;
@@ -714,6 +713,18 @@ static void pci_pm_complete(struct device *dev)
#endif /* !CONFIG_PM_SLEEP */
#ifdef CONFIG_SUSPEND
+static void pcie_pme_root_status_cleanup(struct pci_dev *pci_dev)
+{
+ /*
+ * Some BIOSes forget to clear Root PME Status bits after system
+ * wakeup, which breaks ACPI-based runtime wakeup on PCI Express.
+ * Clear those bits now just in case (shouldn't hurt).
+ */
+ if (pci_is_pcie(pci_dev) &&
+ (pci_pcie_type(pci_dev) == PCI_EXP_TYPE_ROOT_PORT ||
+ pci_pcie_type(pci_dev) == PCI_EXP_TYPE_RC_EC))
+ pcie_clear_root_pme_status(pci_dev);
+}
static int pci_pm_suspend(struct device *dev)
{
@@ -873,6 +884,8 @@ static int pci_pm_resume_noirq(struct device *dev)
if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_resume_early(dev);
+ pcie_pme_root_status_cleanup(pci_dev);
+
if (drv && drv->pm && drv->pm->resume_noirq)
error = drv->pm->resume_noirq(dev);
@@ -945,10 +958,11 @@ static int pci_pm_freeze(struct device *dev)
* devices should not be touched during freeze/thaw transitions,
* however.
*/
- if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND))
+ if (!dev_pm_smart_suspend_and_suspended(dev)) {
pm_runtime_resume(dev);
+ pci_dev->state_saved = false;
+ }
- pci_dev->state_saved = false;
if (pm->freeze) {
int error;
@@ -1522,6 +1536,42 @@ static int pci_uevent(struct device *dev, struct kobj_uevent_env *env)
return 0;
}
+#if defined(CONFIG_PCIEAER) || defined(CONFIG_EEH)
+/**
+ * pci_uevent_ers - emit a uevent during recovery path of PCI device
+ * @pdev: PCI device undergoing error recovery
+ * @err_type: type of error event
+ */
+void pci_uevent_ers(struct pci_dev *pdev, enum pci_ers_result err_type)
+{
+ int idx = 0;
+ char *envp[3];
+
+ switch (err_type) {
+ case PCI_ERS_RESULT_NONE:
+ case PCI_ERS_RESULT_CAN_RECOVER:
+ envp[idx++] = "ERROR_EVENT=BEGIN_RECOVERY";
+ envp[idx++] = "DEVICE_ONLINE=0";
+ break;
+ case PCI_ERS_RESULT_RECOVERED:
+ envp[idx++] = "ERROR_EVENT=SUCCESSFUL_RECOVERY";
+ envp[idx++] = "DEVICE_ONLINE=1";
+ break;
+ case PCI_ERS_RESULT_DISCONNECT:
+ envp[idx++] = "ERROR_EVENT=FAILED_RECOVERY";
+ envp[idx++] = "DEVICE_ONLINE=0";
+ break;
+ default:
+ break;
+ }
+
+ if (idx > 0) {
+ envp[idx++] = NULL;
+ kobject_uevent_env(&pdev->dev.kobj, KOBJ_CHANGE, envp);
+ }
+}
+#endif
+
static int pci_bus_num_vf(struct device *dev)
{
return pci_num_vf(to_pci_dev(dev));
@@ -1543,8 +1593,49 @@ struct bus_type pci_bus_type = {
};
EXPORT_SYMBOL(pci_bus_type);
+#ifdef CONFIG_PCIEPORTBUS
+static int pcie_port_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct pcie_device *pciedev;
+ struct pcie_port_service_driver *driver;
+
+ if (drv->bus != &pcie_port_bus_type || dev->bus != &pcie_port_bus_type)
+ return 0;
+
+ pciedev = to_pcie_device(dev);
+ driver = to_service_driver(drv);
+
+ if (driver->service != pciedev->service)
+ return 0;
+
+ if (driver->port_type != PCIE_ANY_PORT &&
+ driver->port_type != pci_pcie_type(pciedev->port))
+ return 0;
+
+ return 1;
+}
+
+struct bus_type pcie_port_bus_type = {
+ .name = "pci_express",
+ .match = pcie_port_bus_match,
+};
+EXPORT_SYMBOL_GPL(pcie_port_bus_type);
+#endif
+
static int __init pci_driver_init(void)
{
- return bus_register(&pci_bus_type);
+ int ret;
+
+ ret = bus_register(&pci_bus_type);
+ if (ret)
+ return ret;
+
+#ifdef CONFIG_PCIEPORTBUS
+ ret = bus_register(&pcie_port_bus_type);
+ if (ret)
+ return ret;
+#endif
+
+ return 0;
}
postcore_initcall(pci_driver_init);
diff --git a/drivers/pci/pci-label.c b/drivers/pci/pci-label.c
index a961a71d950f..a5910f942857 100644
--- a/drivers/pci/pci-label.c
+++ b/drivers/pci/pci-label.c
@@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Purpose: Export the firmware instance and label associated with
- * a pci device to sysfs
+ * Export the firmware instance and label associated with a PCI device to
+ * sysfs
+ *
* Copyright (C) 2010 Dell Inc.
* by Narendra K <Narendra_K@dell.com>,
* Jordan Hargrave <Jordan_Hargrave@dell.com>
diff --git a/drivers/pci/pci-stub.c b/drivers/pci/pci-stub.c
index 10d54f939048..66f8a59fadbd 100644
--- a/drivers/pci/pci-stub.c
+++ b/drivers/pci/pci-stub.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
-/* pci-stub - simple stub driver to reserve a pci device
+/*
+ * Simple stub driver to reserve a PCI device
*
* Copyright (C) 2008 Red Hat, Inc.
* Author:
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index eb6bee8724cc..366d93af051d 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -1,7 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * drivers/pci/pci-sysfs.c
- *
* (C) Copyright 2002-2004 Greg Kroah-Hartman <greg@kroah.com>
* (C) Copyright 2002-2004 IBM Corp.
* (C) Copyright 2003 Matthew Wilcox
@@ -12,7 +10,6 @@
* File attributes for PCI devices
*
* Modeled after usb's driverfs.c
- *
*/
@@ -158,45 +155,18 @@ static DEVICE_ATTR_RO(resource);
static ssize_t max_link_speed_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct pci_dev *pci_dev = to_pci_dev(dev);
- u32 linkcap;
- int err;
- const char *speed;
-
- err = pcie_capability_read_dword(pci_dev, PCI_EXP_LNKCAP, &linkcap);
- if (err)
- return -EINVAL;
-
- switch (linkcap & PCI_EXP_LNKCAP_SLS) {
- case PCI_EXP_LNKCAP_SLS_8_0GB:
- speed = "8 GT/s";
- break;
- case PCI_EXP_LNKCAP_SLS_5_0GB:
- speed = "5 GT/s";
- break;
- case PCI_EXP_LNKCAP_SLS_2_5GB:
- speed = "2.5 GT/s";
- break;
- default:
- speed = "Unknown speed";
- }
+ struct pci_dev *pdev = to_pci_dev(dev);
- return sprintf(buf, "%s\n", speed);
+ return sprintf(buf, "%s\n", PCIE_SPEED2STR(pcie_get_speed_cap(pdev)));
}
static DEVICE_ATTR_RO(max_link_speed);
static ssize_t max_link_width_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct pci_dev *pci_dev = to_pci_dev(dev);
- u32 linkcap;
- int err;
-
- err = pcie_capability_read_dword(pci_dev, PCI_EXP_LNKCAP, &linkcap);
- if (err)
- return -EINVAL;
+ struct pci_dev *pdev = to_pci_dev(dev);
- return sprintf(buf, "%u\n", (linkcap & PCI_EXP_LNKCAP_MLW) >> 4);
+ return sprintf(buf, "%u\n", pcie_get_width_cap(pdev));
}
static DEVICE_ATTR_RO(max_link_width);
@@ -213,6 +183,9 @@ static ssize_t current_link_speed_show(struct device *dev,
return -EINVAL;
switch (linkstat & PCI_EXP_LNKSTA_CLS) {
+ case PCI_EXP_LNKSTA_CLS_16_0GB:
+ speed = "16 GT/s";
+ break;
case PCI_EXP_LNKSTA_CLS_8_0GB:
speed = "8 GT/s";
break;
@@ -982,38 +955,6 @@ static ssize_t pci_write_config(struct file *filp, struct kobject *kobj,
return count;
}
-static ssize_t read_vpd_attr(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr, char *buf,
- loff_t off, size_t count)
-{
- struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj));
-
- if (bin_attr->size > 0) {
- if (off > bin_attr->size)
- count = 0;
- else if (count > bin_attr->size - off)
- count = bin_attr->size - off;
- }
-
- return pci_read_vpd(dev, off, count, buf);
-}
-
-static ssize_t write_vpd_attr(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr, char *buf,
- loff_t off, size_t count)
-{
- struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj));
-
- if (bin_attr->size > 0) {
- if (off > bin_attr->size)
- count = 0;
- else if (count > bin_attr->size - off)
- count = bin_attr->size - off;
- }
-
- return pci_write_vpd(dev, off, count, buf);
-}
-
#ifdef HAVE_PCI_LEGACY
/**
* pci_read_legacy_io - read byte(s) from legacy I/O port space
@@ -1517,46 +1458,20 @@ static struct device_attribute reset_attr = __ATTR(reset, 0200, NULL, reset_stor
static int pci_create_capabilities_sysfs(struct pci_dev *dev)
{
int retval;
- struct bin_attribute *attr;
- /* If the device has VPD, try to expose it in sysfs. */
- if (dev->vpd) {
- attr = kzalloc(sizeof(*attr), GFP_ATOMIC);
- if (!attr)
- return -ENOMEM;
-
- sysfs_bin_attr_init(attr);
- attr->size = 0;
- attr->attr.name = "vpd";
- attr->attr.mode = S_IRUSR | S_IWUSR;
- attr->read = read_vpd_attr;
- attr->write = write_vpd_attr;
- retval = sysfs_create_bin_file(&dev->dev.kobj, attr);
- if (retval) {
- kfree(attr);
- return retval;
- }
- dev->vpd->attr = attr;
- }
-
- /* Active State Power Management */
+ pcie_vpd_create_sysfs_dev_files(dev);
pcie_aspm_create_sysfs_dev_files(dev);
- if (!pci_probe_reset_function(dev)) {
+ if (dev->reset_fn) {
retval = device_create_file(&dev->dev, &reset_attr);
if (retval)
goto error;
- dev->reset_fn = 1;
}
return 0;
error:
pcie_aspm_remove_sysfs_dev_files(dev);
- if (dev->vpd && dev->vpd->attr) {
- sysfs_remove_bin_file(&dev->dev.kobj, dev->vpd->attr);
- kfree(dev->vpd->attr);
- }
-
+ pcie_vpd_remove_sysfs_dev_files(dev);
return retval;
}
@@ -1630,11 +1545,7 @@ err:
static void pci_remove_capabilities_sysfs(struct pci_dev *dev)
{
- if (dev->vpd && dev->vpd->attr) {
- sysfs_remove_bin_file(&dev->dev.kobj, dev->vpd->attr);
- kfree(dev->vpd->attr);
- }
-
+ pcie_vpd_remove_sysfs_dev_files(dev);
pcie_aspm_remove_sysfs_dev_files(dev);
if (dev->reset_fn) {
device_remove_file(&dev->dev, &reset_attr);
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index bd6f156dc3cf..a04197ce767d 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1,11 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * PCI Bus Services, see include/linux/pci.h for further explanation.
+ * PCI Bus Services, see include/linux/pci.h for further explanation.
*
- * Copyright 1993 -- 1997 Drew Eckhardt, Frederic Potter,
- * David Mosberger-Tang
+ * Copyright 1993 -- 1997 Drew Eckhardt, Frederic Potter,
+ * David Mosberger-Tang
*
- * Copyright 1997 -- 2000 Martin Mares <mj@ucw.cz>
+ * Copyright 1997 -- 2000 Martin Mares <mj@ucw.cz>
*/
#include <linux/acpi.h>
@@ -22,6 +22,7 @@
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/log2.h>
+#include <linux/logic_pio.h>
#include <linux/pci-aspm.h>
#include <linux/pm_wakeup.h>
#include <linux/interrupt.h>
@@ -126,6 +127,9 @@ static int __init pcie_port_pm_setup(char *str)
}
__setup("pcie_port_pm=", pcie_port_pm_setup);
+/* Time to wait after a reset for device to become responsive */
+#define PCIE_RESET_READY_POLL_MS 60000
+
/**
* pci_bus_max_busnr - returns maximum PCI bus number of given bus' children
* @bus: pointer to PCI bus structure to search
@@ -1684,6 +1688,15 @@ int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state)
EXPORT_SYMBOL_GPL(pci_set_pcie_reset_state);
/**
+ * pcie_clear_root_pme_status - Clear root port PME interrupt status.
+ * @dev: PCIe root port or event collector.
+ */
+void pcie_clear_root_pme_status(struct pci_dev *dev)
+{
+ pcie_capability_set_dword(dev, PCI_EXP_RTSTA, PCI_EXP_RTSTA_PME);
+}
+
+/**
* pci_check_pme_status - Check if given device has generated PME.
* @dev: Device to check.
*
@@ -2258,8 +2271,6 @@ void pci_config_pm_runtime_put(struct pci_dev *pdev)
*/
bool pci_bridge_d3_possible(struct pci_dev *bridge)
{
- unsigned int year;
-
if (!pci_is_pcie(bridge))
return false;
@@ -2287,10 +2298,8 @@ bool pci_bridge_d3_possible(struct pci_dev *bridge)
* It should be safe to put PCIe ports from 2015 or newer
* to D3.
*/
- if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) &&
- year >= 2015) {
+ if (dmi_get_bios_year() >= 2015)
return true;
- }
break;
}
@@ -3440,68 +3449,35 @@ int pci_request_regions_exclusive(struct pci_dev *pdev, const char *res_name)
}
EXPORT_SYMBOL(pci_request_regions_exclusive);
-#ifdef PCI_IOBASE
-struct io_range {
- struct list_head list;
- phys_addr_t start;
- resource_size_t size;
-};
-
-static LIST_HEAD(io_range_list);
-static DEFINE_SPINLOCK(io_range_lock);
-#endif
-
/*
* Record the PCI IO range (expressed as CPU physical address + size).
* Return a negative value if an error has occured, zero otherwise
*/
-int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size)
+int pci_register_io_range(struct fwnode_handle *fwnode, phys_addr_t addr,
+ resource_size_t size)
{
- int err = 0;
-
+ int ret = 0;
#ifdef PCI_IOBASE
- struct io_range *range;
- resource_size_t allocated_size = 0;
-
- /* check if the range hasn't been previously recorded */
- spin_lock(&io_range_lock);
- list_for_each_entry(range, &io_range_list, list) {
- if (addr >= range->start && addr + size <= range->start + size) {
- /* range already registered, bail out */
- goto end_register;
- }
- allocated_size += range->size;
- }
-
- /* range not registed yet, check for available space */
- if (allocated_size + size - 1 > IO_SPACE_LIMIT) {
- /* if it's too big check if 64K space can be reserved */
- if (allocated_size + SZ_64K - 1 > IO_SPACE_LIMIT) {
- err = -E2BIG;
- goto end_register;
- }
+ struct logic_pio_hwaddr *range;
- size = SZ_64K;
- pr_warn("Requested IO range too big, new size set to 64K\n");
- }
+ if (!size || addr + size < addr)
+ return -EINVAL;
- /* add the range to the list */
range = kzalloc(sizeof(*range), GFP_ATOMIC);
- if (!range) {
- err = -ENOMEM;
- goto end_register;
- }
+ if (!range)
+ return -ENOMEM;
- range->start = addr;
+ range->fwnode = fwnode;
range->size = size;
+ range->hw_start = addr;
+ range->flags = LOGIC_PIO_CPU_MMIO;
- list_add_tail(&range->list, &io_range_list);
-
-end_register:
- spin_unlock(&io_range_lock);
+ ret = logic_pio_register_range(range);
+ if (ret)
+ kfree(range);
#endif
- return err;
+ return ret;
}
phys_addr_t pci_pio_to_address(unsigned long pio)
@@ -3509,21 +3485,10 @@ phys_addr_t pci_pio_to_address(unsigned long pio)
phys_addr_t address = (phys_addr_t)OF_BAD_ADDR;
#ifdef PCI_IOBASE
- struct io_range *range;
- resource_size_t allocated_size = 0;
-
- if (pio > IO_SPACE_LIMIT)
+ if (pio >= MMIO_UPPER_LIMIT)
return address;
- spin_lock(&io_range_lock);
- list_for_each_entry(range, &io_range_list, list) {
- if (pio >= allocated_size && pio < allocated_size + range->size) {
- address = range->start + pio - allocated_size;
- break;
- }
- allocated_size += range->size;
- }
- spin_unlock(&io_range_lock);
+ address = logic_pio_to_hwaddr(pio);
#endif
return address;
@@ -3532,21 +3497,7 @@ phys_addr_t pci_pio_to_address(unsigned long pio)
unsigned long __weak pci_address_to_pio(phys_addr_t address)
{
#ifdef PCI_IOBASE
- struct io_range *res;
- resource_size_t offset = 0;
- unsigned long addr = -1;
-
- spin_lock(&io_range_lock);
- list_for_each_entry(res, &io_range_list, list) {
- if (address >= res->start && address < res->start + res->size) {
- addr = address - res->start + offset;
- break;
- }
- offset += res->size;
- }
- spin_unlock(&io_range_lock);
-
- return addr;
+ return logic_pio_trans_cpuaddr(address);
#else
if (address > IO_SPACE_LIMIT)
return (unsigned long)-1;
@@ -4017,20 +3968,13 @@ int pci_wait_for_pending_transaction(struct pci_dev *dev)
}
EXPORT_SYMBOL(pci_wait_for_pending_transaction);
-static void pci_flr_wait(struct pci_dev *dev)
+static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout)
{
- int delay = 1, timeout = 60000;
+ int delay = 1;
u32 id;
/*
- * Per PCIe r3.1, sec 6.6.2, a device must complete an FLR within
- * 100ms, but may silently discard requests while the FLR is in
- * progress. Wait 100ms before trying to access the device.
- */
- msleep(100);
-
- /*
- * After 100ms, the device should not silently discard config
+ * After reset, the device should not silently discard config
* requests, but it may still indicate that it needs more time by
* responding to them with CRS completions. The Root Port will
* generally synthesize ~0 data to complete the read (except when
@@ -4044,14 +3988,14 @@ static void pci_flr_wait(struct pci_dev *dev)
pci_read_config_dword(dev, PCI_COMMAND, &id);
while (id == ~0) {
if (delay > timeout) {
- pci_warn(dev, "not ready %dms after FLR; giving up\n",
- 100 + delay - 1);
- return;
+ pci_warn(dev, "not ready %dms after %s; giving up\n",
+ delay - 1, reset_type);
+ return -ENOTTY;
}
if (delay > 1000)
- pci_info(dev, "not ready %dms after FLR; waiting\n",
- 100 + delay - 1);
+ pci_info(dev, "not ready %dms after %s; waiting\n",
+ delay - 1, reset_type);
msleep(delay);
delay *= 2;
@@ -4059,7 +4003,10 @@ static void pci_flr_wait(struct pci_dev *dev)
}
if (delay > 1000)
- pci_info(dev, "ready %dms after FLR\n", 100 + delay - 1);
+ pci_info(dev, "ready %dms after %s\n", delay - 1,
+ reset_type);
+
+ return 0;
}
/**
@@ -4088,13 +4035,21 @@ static bool pcie_has_flr(struct pci_dev *dev)
* device supports FLR before calling this function, e.g. by using the
* pcie_has_flr() helper.
*/
-void pcie_flr(struct pci_dev *dev)
+int pcie_flr(struct pci_dev *dev)
{
if (!pci_wait_for_pending_transaction(dev))
pci_err(dev, "timed out waiting for pending transaction; performing function level reset anyway\n");
pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_BCR_FLR);
- pci_flr_wait(dev);
+
+ /*
+ * Per PCIe r4.0, sec 6.6.2, a device must complete an FLR within
+ * 100ms, but may silently discard requests while the FLR is in
+ * progress. Wait 100ms before trying to access the device.
+ */
+ msleep(100);
+
+ return pci_dev_wait(dev, "FLR", PCIE_RESET_READY_POLL_MS);
}
EXPORT_SYMBOL_GPL(pcie_flr);
@@ -4127,8 +4082,16 @@ static int pci_af_flr(struct pci_dev *dev, int probe)
pci_err(dev, "timed out waiting for pending transaction; performing AF function level reset anyway\n");
pci_write_config_byte(dev, pos + PCI_AF_CTRL, PCI_AF_CTRL_FLR);
- pci_flr_wait(dev);
- return 0;
+
+ /*
+ * Per Advanced Capabilities for Conventional PCI ECN, 13 April 2006,
+ * updated 27 July 2006; a device must complete an FLR within
+ * 100ms, but may silently discard requests while the FLR is in
+ * progress. Wait 100ms before trying to access the device.
+ */
+ msleep(100);
+
+ return pci_dev_wait(dev, "AF_FLR", PCIE_RESET_READY_POLL_MS);
}
/**
@@ -4173,7 +4136,7 @@ static int pci_pm_reset(struct pci_dev *dev, int probe)
pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, csr);
pci_dev_d3_sleep(dev);
- return 0;
+ return pci_dev_wait(dev, "PM D3->D0", PCIE_RESET_READY_POLL_MS);
}
void pci_reset_secondary_bus(struct pci_dev *dev)
@@ -4183,6 +4146,7 @@ void pci_reset_secondary_bus(struct pci_dev *dev)
pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &ctrl);
ctrl |= PCI_BRIDGE_CTL_BUS_RESET;
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl);
+
/*
* PCI spec v3.0 7.6.4.2 requires minimum Trst of 1ms. Double
* this to 2ms to ensure that we meet the minimum requirement.
@@ -4214,9 +4178,11 @@ void __weak pcibios_reset_secondary_bus(struct pci_dev *dev)
* Use the bridge control register to assert reset on the secondary bus.
* Devices on the secondary bus are left in power-on state.
*/
-void pci_reset_bridge_secondary_bus(struct pci_dev *dev)
+int pci_reset_bridge_secondary_bus(struct pci_dev *dev)
{
pcibios_reset_secondary_bus(dev);
+
+ return pci_dev_wait(dev, "bus reset", PCIE_RESET_READY_POLL_MS);
}
EXPORT_SYMBOL_GPL(pci_reset_bridge_secondary_bus);
@@ -4379,8 +4345,9 @@ int __pci_reset_function_locked(struct pci_dev *dev)
if (rc != -ENOTTY)
return rc;
if (pcie_has_flr(dev)) {
- pcie_flr(dev);
- return 0;
+ rc = pcie_flr(dev);
+ if (rc != -ENOTTY)
+ return rc;
}
rc = pci_af_flr(dev, 0);
if (rc != -ENOTTY)
@@ -4450,9 +4417,8 @@ int pci_reset_function(struct pci_dev *dev)
{
int rc;
- rc = pci_probe_reset_function(dev);
- if (rc)
- return rc;
+ if (!dev->reset_fn)
+ return -ENOTTY;
pci_dev_lock(dev);
pci_dev_save_and_disable(dev);
@@ -4487,9 +4453,8 @@ int pci_reset_function_locked(struct pci_dev *dev)
{
int rc;
- rc = pci_probe_reset_function(dev);
- if (rc)
- return rc;
+ if (!dev->reset_fn)
+ return -ENOTTY;
pci_dev_save_and_disable(dev);
@@ -4511,18 +4476,17 @@ int pci_try_reset_function(struct pci_dev *dev)
{
int rc;
- rc = pci_probe_reset_function(dev);
- if (rc)
- return rc;
+ if (!dev->reset_fn)
+ return -ENOTTY;
if (!pci_dev_trylock(dev))
return -EAGAIN;
pci_dev_save_and_disable(dev);
rc = __pci_reset_function_locked(dev);
+ pci_dev_restore(dev);
pci_dev_unlock(dev);
- pci_dev_restore(dev);
return rc;
}
EXPORT_SYMBOL_GPL(pci_try_reset_function);
@@ -4730,7 +4694,9 @@ static void pci_slot_restore(struct pci_slot *slot)
list_for_each_entry(dev, &slot->bus->devices, bus_list) {
if (!dev->slot || dev->slot != slot)
continue;
+ pci_dev_lock(dev);
pci_dev_restore(dev);
+ pci_dev_unlock(dev);
if (dev->subordinate)
pci_bus_restore(dev->subordinate);
}
@@ -5147,6 +5113,180 @@ int pcie_get_minimum_link(struct pci_dev *dev, enum pci_bus_speed *speed,
EXPORT_SYMBOL(pcie_get_minimum_link);
/**
+ * pcie_bandwidth_available - determine minimum link settings of a PCIe
+ * device and its bandwidth limitation
+ * @dev: PCI device to query
+ * @limiting_dev: storage for device causing the bandwidth limitation
+ * @speed: storage for speed of limiting device
+ * @width: storage for width of limiting device
+ *
+ * Walk up the PCI device chain and find the point where the minimum
+ * bandwidth is available. Return the bandwidth available there and (if
+ * limiting_dev, speed, and width pointers are supplied) information about
+ * that point. The bandwidth returned is in Mb/s, i.e., megabits/second of
+ * raw bandwidth.
+ */
+u32 pcie_bandwidth_available(struct pci_dev *dev, struct pci_dev **limiting_dev,
+ enum pci_bus_speed *speed,
+ enum pcie_link_width *width)
+{
+ u16 lnksta;
+ enum pci_bus_speed next_speed;
+ enum pcie_link_width next_width;
+ u32 bw, next_bw;
+
+ if (speed)
+ *speed = PCI_SPEED_UNKNOWN;
+ if (width)
+ *width = PCIE_LNK_WIDTH_UNKNOWN;
+
+ bw = 0;
+
+ while (dev) {
+ pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta);
+
+ next_speed = pcie_link_speed[lnksta & PCI_EXP_LNKSTA_CLS];
+ next_width = (lnksta & PCI_EXP_LNKSTA_NLW) >>
+ PCI_EXP_LNKSTA_NLW_SHIFT;
+
+ next_bw = next_width * PCIE_SPEED2MBS_ENC(next_speed);
+
+ /* Check if current device limits the total bandwidth */
+ if (!bw || next_bw <= bw) {
+ bw = next_bw;
+
+ if (limiting_dev)
+ *limiting_dev = dev;
+ if (speed)
+ *speed = next_speed;
+ if (width)
+ *width = next_width;
+ }
+
+ dev = pci_upstream_bridge(dev);
+ }
+
+ return bw;
+}
+EXPORT_SYMBOL(pcie_bandwidth_available);
+
+/**
+ * pcie_get_speed_cap - query for the PCI device's link speed capability
+ * @dev: PCI device to query
+ *
+ * Query the PCI device speed capability. Return the maximum link speed
+ * supported by the device.
+ */
+enum pci_bus_speed pcie_get_speed_cap(struct pci_dev *dev)
+{
+ u32 lnkcap2, lnkcap;
+
+ /*
+ * PCIe r4.0 sec 7.5.3.18 recommends using the Supported Link
+ * Speeds Vector in Link Capabilities 2 when supported, falling
+ * back to Max Link Speed in Link Capabilities otherwise.
+ */
+ pcie_capability_read_dword(dev, PCI_EXP_LNKCAP2, &lnkcap2);
+ if (lnkcap2) { /* PCIe r3.0-compliant */
+ if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_16_0GB)
+ return PCIE_SPEED_16_0GT;
+ else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_8_0GB)
+ return PCIE_SPEED_8_0GT;
+ else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_5_0GB)
+ return PCIE_SPEED_5_0GT;
+ else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_2_5GB)
+ return PCIE_SPEED_2_5GT;
+ return PCI_SPEED_UNKNOWN;
+ }
+
+ pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnkcap);
+ if (lnkcap) {
+ if (lnkcap & PCI_EXP_LNKCAP_SLS_16_0GB)
+ return PCIE_SPEED_16_0GT;
+ else if (lnkcap & PCI_EXP_LNKCAP_SLS_8_0GB)
+ return PCIE_SPEED_8_0GT;
+ else if (lnkcap & PCI_EXP_LNKCAP_SLS_5_0GB)
+ return PCIE_SPEED_5_0GT;
+ else if (lnkcap & PCI_EXP_LNKCAP_SLS_2_5GB)
+ return PCIE_SPEED_2_5GT;
+ }
+
+ return PCI_SPEED_UNKNOWN;
+}
+
+/**
+ * pcie_get_width_cap - query for the PCI device's link width capability
+ * @dev: PCI device to query
+ *
+ * Query the PCI device width capability. Return the maximum link width
+ * supported by the device.
+ */
+enum pcie_link_width pcie_get_width_cap(struct pci_dev *dev)
+{
+ u32 lnkcap;
+
+ pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnkcap);
+ if (lnkcap)
+ return (lnkcap & PCI_EXP_LNKCAP_MLW) >> 4;
+
+ return PCIE_LNK_WIDTH_UNKNOWN;
+}
+
+/**
+ * pcie_bandwidth_capable - calculate a PCI device's link bandwidth capability
+ * @dev: PCI device
+ * @speed: storage for link speed
+ * @width: storage for link width
+ *
+ * Calculate a PCI device's link bandwidth by querying for its link speed
+ * and width, multiplying them, and applying encoding overhead. The result
+ * is in Mb/s, i.e., megabits/second of raw bandwidth.
+ */
+u32 pcie_bandwidth_capable(struct pci_dev *dev, enum pci_bus_speed *speed,
+ enum pcie_link_width *width)
+{
+ *speed = pcie_get_speed_cap(dev);
+ *width = pcie_get_width_cap(dev);
+
+ if (*speed == PCI_SPEED_UNKNOWN || *width == PCIE_LNK_WIDTH_UNKNOWN)
+ return 0;
+
+ return *width * PCIE_SPEED2MBS_ENC(*speed);
+}
+
+/**
+ * pcie_print_link_status - Report the PCI device's link speed and width
+ * @dev: PCI device to query
+ *
+ * Report the available bandwidth at the device. If this is less than the
+ * device is capable of, report the device's maximum possible bandwidth and
+ * the upstream link that limits its performance to less than that.
+ */
+void pcie_print_link_status(struct pci_dev *dev)
+{
+ enum pcie_link_width width, width_cap;
+ enum pci_bus_speed speed, speed_cap;
+ struct pci_dev *limiting_dev = NULL;
+ u32 bw_avail, bw_cap;
+
+ bw_cap = pcie_bandwidth_capable(dev, &speed_cap, &width_cap);
+ bw_avail = pcie_bandwidth_available(dev, &limiting_dev, &speed, &width);
+
+ if (bw_avail >= bw_cap)
+ pci_info(dev, "%u.%03u Gb/s available PCIe bandwidth (%s x%d link)\n",
+ bw_cap / 1000, bw_cap % 1000,
+ PCIE_SPEED2STR(speed_cap), width_cap);
+ else
+ pci_info(dev, "%u.%03u Gb/s available PCIe bandwidth, limited by %s x%d link at %s (capable of %u.%03u Gb/s with %s x%d link)\n",
+ bw_avail / 1000, bw_avail % 1000,
+ PCIE_SPEED2STR(speed), width,
+ limiting_dev ? pci_name(limiting_dev) : "<unknown>",
+ bw_cap / 1000, bw_cap % 1000,
+ PCIE_SPEED2STR(speed_cap), width_cap);
+}
+EXPORT_SYMBOL(pcie_print_link_status);
+
+/**
* pci_select_bars - Make BAR mask from the type of resource
* @dev: the PCI device for which BAR mask is made
* @flags: resource type mask to be selected
@@ -5488,7 +5628,6 @@ void pci_reassigndev_resource_alignment(struct pci_dev *dev)
return;
}
- pci_info(dev, "Disabling memory decoding and releasing memory resources\n");
pci_read_config_word(dev, PCI_COMMAND, &command);
command &= ~PCI_COMMAND_MEMORY;
pci_write_config_word(dev, PCI_COMMAND, command);
@@ -5611,8 +5750,9 @@ static int of_pci_bus_find_domain_nr(struct device *parent)
use_dt_domains = 0;
domain = pci_get_new_domain_nr();
} else {
- dev_err(parent, "Node %pOF has inconsistent \"linux,pci-domain\" property in DT\n",
- parent->of_node);
+ if (parent)
+ pr_err("Node %pOF has ", parent->of_node);
+ pr_err("Inconsistent \"linux,pci-domain\" property in DT\n");
domain = -1;
}
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index fcd81911b127..023f7cf25bff 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -71,6 +71,7 @@ void pci_update_current_state(struct pci_dev *dev, pci_power_t state);
void pci_power_up(struct pci_dev *dev);
void pci_disable_enabled_device(struct pci_dev *dev);
int pci_finish_runtime_suspend(struct pci_dev *dev);
+void pcie_clear_root_pme_status(struct pci_dev *dev);
int __pci_pme_wakeup(struct pci_dev *dev, void *ign);
void pci_pme_restore(struct pci_dev *dev);
bool pci_dev_keep_suspended(struct pci_dev *dev);
@@ -104,25 +105,10 @@ static inline bool pci_power_manageable(struct pci_dev *pci_dev)
return !pci_has_subordinate(pci_dev) || pci_dev->bridge_d3;
}
-struct pci_vpd_ops {
- ssize_t (*read)(struct pci_dev *dev, loff_t pos, size_t count, void *buf);
- ssize_t (*write)(struct pci_dev *dev, loff_t pos, size_t count, const void *buf);
- int (*set_size)(struct pci_dev *dev, size_t len);
-};
-
-struct pci_vpd {
- const struct pci_vpd_ops *ops;
- struct bin_attribute *attr; /* Descriptor for sysfs VPD entry */
- struct mutex lock;
- unsigned int len;
- u16 flag;
- u8 cap;
- u8 busy:1;
- u8 valid:1;
-};
-
int pci_vpd_init(struct pci_dev *dev);
void pci_vpd_release(struct pci_dev *dev);
+void pcie_vpd_create_sysfs_dev_files(struct pci_dev *dev);
+void pcie_vpd_remove_sysfs_dev_files(struct pci_dev *dev);
/* PCI /proc functions */
#ifdef CONFIG_PROC_FS
@@ -253,6 +239,27 @@ bool pci_bus_clip_resource(struct pci_dev *dev, int idx);
void pci_reassigndev_resource_alignment(struct pci_dev *dev);
void pci_disable_bridge_window(struct pci_dev *dev);
+/* PCIe link information */
+#define PCIE_SPEED2STR(speed) \
+ ((speed) == PCIE_SPEED_16_0GT ? "16 GT/s" : \
+ (speed) == PCIE_SPEED_8_0GT ? "8 GT/s" : \
+ (speed) == PCIE_SPEED_5_0GT ? "5 GT/s" : \
+ (speed) == PCIE_SPEED_2_5GT ? "2.5 GT/s" : \
+ "Unknown speed")
+
+/* PCIe speed to Mb/s reduced by encoding overhead */
+#define PCIE_SPEED2MBS_ENC(speed) \
+ ((speed) == PCIE_SPEED_16_0GT ? 16000*128/130 : \
+ (speed) == PCIE_SPEED_8_0GT ? 8000*128/130 : \
+ (speed) == PCIE_SPEED_5_0GT ? 5000*8/10 : \
+ (speed) == PCIE_SPEED_2_5GT ? 2500*8/10 : \
+ 0)
+
+enum pci_bus_speed pcie_get_speed_cap(struct pci_dev *dev);
+enum pcie_link_width pcie_get_width_cap(struct pci_dev *dev);
+u32 pcie_bandwidth_capable(struct pci_dev *dev, enum pci_bus_speed *speed,
+ enum pcie_link_width *width);
+
/* Single Root I/O Virtualization */
struct pci_sriov {
int pos; /* Capability position */
@@ -271,6 +278,10 @@ struct pci_sriov {
u16 driver_max_VFs; /* Max num VFs driver supports */
struct pci_dev *dev; /* Lowest numbered PF */
struct pci_dev *self; /* This PF */
+ u32 class; /* VF device */
+ u8 hdr_type; /* VF header type */
+ u16 subsystem_vendor; /* VF subsystem vendor */
+ u16 subsystem_device; /* VF subsystem device */
resource_size_t barsz[PCI_SRIOV_NUM_BARS]; /* VF BAR size */
bool drivers_autoprobe; /* Auto probing of VFs by driver */
};
diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile
index 223e4c34c29a..800e1d404a45 100644
--- a/drivers/pci/pcie/Makefile
+++ b/drivers/pci/pcie/Makefile
@@ -1,20 +1,13 @@
# SPDX-License-Identifier: GPL-2.0
#
-# Makefile for PCI-Express PORT Driver
-#
-
-# Build PCI Express ASPM if needed
-obj-$(CONFIG_PCIEASPM) += aspm.o
+# Makefile for PCI Express features and port driver
-pcieportdrv-y := portdrv_core.o portdrv_pci.o portdrv_bus.o
-pcieportdrv-$(CONFIG_ACPI) += portdrv_acpi.o
+pcieportdrv-y := portdrv_core.o portdrv_pci.o
obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o
-# Build PCI Express AER if needed
+obj-$(CONFIG_PCIEASPM) += aspm.o
obj-$(CONFIG_PCIEAER) += aer/
-
-obj-$(CONFIG_PCIE_PME) += pme.o
-
-obj-$(CONFIG_PCIE_DPC) += pcie-dpc.o
-obj-$(CONFIG_PCIE_PTM) += ptm.o
+obj-$(CONFIG_PCIE_PME) += pme.o
+obj-$(CONFIG_PCIE_DPC) += dpc.o
+obj-$(CONFIG_PCIE_PTM) += ptm.o
diff --git a/drivers/pci/pcie/aer/aer_inject.c b/drivers/pci/pcie/aer/aer_inject.c
index 25e1feb962c5..a49090935303 100644
--- a/drivers/pci/pcie/aer/aer_inject.c
+++ b/drivers/pci/pcie/aer/aer_inject.c
@@ -344,7 +344,7 @@ static int aer_inject(struct aer_error_inj *einj)
goto out_put;
}
- pos_cap_err = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
+ pos_cap_err = dev->aer_cap;
if (!pos_cap_err) {
pci_err(dev, "aer_inject: Device doesn't support AER\n");
ret = -EPROTONOSUPPORT;
@@ -355,7 +355,7 @@ static int aer_inject(struct aer_error_inj *einj)
pci_read_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_MASK,
&uncor_mask);
- rp_pos_cap_err = pci_find_ext_capability(rpdev, PCI_EXT_CAP_ID_ERR);
+ rp_pos_cap_err = rpdev->aer_cap;
if (!rp_pos_cap_err) {
pci_err(rpdev, "aer_inject: Root port doesn't support AER\n");
ret = -EPROTONOSUPPORT;
diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c
index da8331f5684d..779b3879b1b5 100644
--- a/drivers/pci/pcie/aer/aerdrv.c
+++ b/drivers/pci/pcie/aer/aerdrv.c
@@ -1,15 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * drivers/pci/pcie/aer/aerdrv.c
- *
- * 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.
+ * Implement the AER root port service driver. The driver registers an IRQ
+ * handler. When a root port triggers an AER interrupt, the IRQ handler
+ * collects root port status and schedules work.
*
* Copyright (C) 2006 Intel Corp.
* Tom Long Nguyen (tom.l.nguyen@intel.com)
* Zhang Yanmin (yanmin.zhang@intel.com)
- *
*/
#include <linux/pci.h>
@@ -21,7 +18,6 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
-#include <linux/pcieport_if.h>
#include <linux/slab.h>
#include "aerdrv.h"
diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h
index 5449e5ce139d..08b4584f62fe 100644
--- a/drivers/pci/pcie/aer/aerdrv.h
+++ b/drivers/pci/pcie/aer/aerdrv.h
@@ -3,17 +3,17 @@
* 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/workqueue.h>
-#include <linux/pcieport_if.h>
#include <linux/aer.h>
#include <linux/interrupt.h>
+#include "../portdrv.h"
+
#define SYSTEM_ERROR_INTR_ON_MESG_MASK (PCI_EXP_RTCTL_SECEE| \
PCI_EXP_RTCTL_SENFEE| \
PCI_EXP_RTCTL_SEFEE)
diff --git a/drivers/pci/pcie/aer/aerdrv_acpi.c b/drivers/pci/pcie/aer/aerdrv_acpi.c
index b2019440e882..08c87de13cb8 100644
--- a/drivers/pci/pcie/aer/aerdrv_acpi.c
+++ b/drivers/pci/pcie/aer/aerdrv_acpi.c
@@ -5,7 +5,6 @@
* Copyright (C) 2006 Intel Corp.
* Tom Long Nguyen (tom.l.nguyen@intel.com)
* Zhang Yanmin (yanmin.zhang@intel.com)
- *
*/
#include <linux/module.h>
diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c
index a4bfea52e7d4..0ea5acc40323 100644
--- a/drivers/pci/pcie/aer/aerdrv_core.c
+++ b/drivers/pci/pcie/aer/aerdrv_core.c
@@ -1,16 +1,13 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * drivers/pci/pcie/aer/aerdrv_core.c
- *
- * This file implements the core part of PCIe AER. When a PCIe
- * 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.
+ * Implement the core part of PCIe AER. When a PCIe 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>
diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c
index 6a352e638699..cfc89dd57831 100644
--- a/drivers/pci/pcie/aer/aerdrv_errprint.c
+++ b/drivers/pci/pcie/aer/aerdrv_errprint.c
@@ -1,13 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * drivers/pci/pcie/aer/aerdrv_errprint.c
- *
* 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>
diff --git a/drivers/pci/pcie/aer/ecrc.c b/drivers/pci/pcie/aer/ecrc.c
index 26d3cac9e635..039efb606e31 100644
--- a/drivers/pci/pcie/aer/ecrc.c
+++ b/drivers/pci/pcie/aer/ecrc.c
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Enables/disables PCIe ECRC checking.
+ * Enable/disable PCIe ECRC checking
*
- * (C) Copyright 2009 Hewlett-Packard Development Company, L.P.
+ * (C) Copyright 2009 Hewlett-Packard Development Company, L.P.
* Andrew Patterson <andrew.patterson@hp.com>
*/
@@ -40,7 +40,7 @@ static int enable_ecrc_checking(struct pci_dev *dev)
if (!pci_is_pcie(dev))
return -ENODEV;
- pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
+ pos = dev->aer_cap;
if (!pos)
return -ENODEV;
@@ -68,7 +68,7 @@ static int disable_ecrc_checking(struct pci_dev *dev)
if (!pci_is_pcie(dev))
return -ENODEV;
- pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
+ pos = dev->aer_cap;
if (!pos)
return -ENODEV;
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index 57feef2ecfe7..f76eb7704f64 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * File: drivers/pci/pcie/aspm.c
- * Enabling PCIe link L0s/L1 state and Clock Power Management
+ * Enable PCIe link L0s/L1 state and Clock Power Management
*
* Copyright (C) 2007 Intel
* Copyright (C) Zhang Yanmin (yanmin.zhang@intel.com)
@@ -228,6 +227,24 @@ static void pcie_aspm_configure_common_clock(struct pcie_link_state *link)
if (!(reg16 & PCI_EXP_LNKSTA_SLC))
same_clock = 0;
+ /* Port might be already in common clock mode */
+ pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &reg16);
+ if (same_clock && (reg16 & PCI_EXP_LNKCTL_CCC)) {
+ bool consistent = true;
+
+ list_for_each_entry(child, &linkbus->devices, bus_list) {
+ pcie_capability_read_word(child, PCI_EXP_LNKCTL,
+ &reg16);
+ if (!(reg16 & PCI_EXP_LNKCTL_CCC)) {
+ consistent = false;
+ break;
+ }
+ }
+ if (consistent)
+ return;
+ pci_warn(parent, "ASPM: current common clock configuration is broken, reconfiguring\n");
+ }
+
/* Configure downstream component, all functions */
list_for_each_entry(child, &linkbus->devices, bus_list) {
pcie_capability_read_word(child, PCI_EXP_LNKCTL, &reg16);
@@ -322,7 +339,7 @@ static u32 calc_l1ss_pwron(struct pci_dev *pdev, u32 scale, u32 val)
static void encode_l12_threshold(u32 threshold_us, u32 *scale, u32 *value)
{
- u64 threshold_ns = threshold_us * 1000;
+ u32 threshold_ns = threshold_us * 1000;
/* See PCIe r3.1, sec 7.33.3 and sec 6.18 */
if (threshold_ns < 32) {
diff --git a/drivers/pci/pcie/dpc.c b/drivers/pci/pcie/dpc.c
new file mode 100644
index 000000000000..8c57d607e603
--- /dev/null
+++ b/drivers/pci/pcie/dpc.c
@@ -0,0 +1,307 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCI Express Downstream Port Containment services driver
+ * Author: Keith Busch <keith.busch@intel.com>
+ *
+ * Copyright (C) 2016 Intel Corp.
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+
+#include "portdrv.h"
+#include "../pci.h"
+#include "aer/aerdrv.h"
+
+struct dpc_dev {
+ struct pcie_device *dev;
+ struct work_struct work;
+ u16 cap_pos;
+ bool rp_extensions;
+ u32 rp_pio_status;
+ u8 rp_log_size;
+};
+
+static const char * const rp_pio_error_string[] = {
+ "Configuration Request received UR Completion", /* Bit Position 0 */
+ "Configuration Request received CA Completion", /* Bit Position 1 */
+ "Configuration Request Completion Timeout", /* Bit Position 2 */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "I/O Request received UR Completion", /* Bit Position 8 */
+ "I/O Request received CA Completion", /* Bit Position 9 */
+ "I/O Request Completion Timeout", /* Bit Position 10 */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Memory Request received UR Completion", /* Bit Position 16 */
+ "Memory Request received CA Completion", /* Bit Position 17 */
+ "Memory Request Completion Timeout", /* Bit Position 18 */
+};
+
+static int dpc_wait_rp_inactive(struct dpc_dev *dpc)
+{
+ unsigned long timeout = jiffies + HZ;
+ struct pci_dev *pdev = dpc->dev->port;
+ struct device *dev = &dpc->dev->device;
+ u16 cap = dpc->cap_pos, status;
+
+ pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status);
+ while (status & PCI_EXP_DPC_RP_BUSY &&
+ !time_after(jiffies, timeout)) {
+ msleep(10);
+ pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status);
+ }
+ if (status & PCI_EXP_DPC_RP_BUSY) {
+ dev_warn(dev, "DPC root port still busy\n");
+ return -EBUSY;
+ }
+ return 0;
+}
+
+static void dpc_wait_link_inactive(struct dpc_dev *dpc)
+{
+ unsigned long timeout = jiffies + HZ;
+ struct pci_dev *pdev = dpc->dev->port;
+ struct device *dev = &dpc->dev->device;
+ u16 lnk_status;
+
+ pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
+ while (lnk_status & PCI_EXP_LNKSTA_DLLLA &&
+ !time_after(jiffies, timeout)) {
+ msleep(10);
+ pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
+ }
+ if (lnk_status & PCI_EXP_LNKSTA_DLLLA)
+ dev_warn(dev, "Link state not disabled for DPC event\n");
+}
+
+static void dpc_work(struct work_struct *work)
+{
+ struct dpc_dev *dpc = container_of(work, struct dpc_dev, work);
+ struct pci_dev *dev, *temp, *pdev = dpc->dev->port;
+ struct pci_bus *parent = pdev->subordinate;
+ u16 cap = dpc->cap_pos, ctl;
+
+ pci_lock_rescan_remove();
+ list_for_each_entry_safe_reverse(dev, temp, &parent->devices,
+ bus_list) {
+ pci_dev_get(dev);
+ pci_dev_set_disconnected(dev, NULL);
+ if (pci_has_subordinate(dev))
+ pci_walk_bus(dev->subordinate,
+ pci_dev_set_disconnected, NULL);
+ pci_stop_and_remove_bus_device(dev);
+ pci_dev_put(dev);
+ }
+ pci_unlock_rescan_remove();
+
+ dpc_wait_link_inactive(dpc);
+ if (dpc->rp_extensions && dpc_wait_rp_inactive(dpc))
+ return;
+ if (dpc->rp_extensions && dpc->rp_pio_status) {
+ pci_write_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS,
+ dpc->rp_pio_status);
+ dpc->rp_pio_status = 0;
+ }
+
+ pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS,
+ PCI_EXP_DPC_STATUS_TRIGGER | PCI_EXP_DPC_STATUS_INTERRUPT);
+
+ pci_read_config_word(pdev, cap + PCI_EXP_DPC_CTL, &ctl);
+ pci_write_config_word(pdev, cap + PCI_EXP_DPC_CTL,
+ ctl | PCI_EXP_DPC_CTL_INT_EN);
+}
+
+static void dpc_process_rp_pio_error(struct dpc_dev *dpc)
+{
+ struct device *dev = &dpc->dev->device;
+ struct pci_dev *pdev = dpc->dev->port;
+ u16 cap = dpc->cap_pos, dpc_status, first_error;
+ u32 status, mask, sev, syserr, exc, dw0, dw1, dw2, dw3, log, prefix;
+ int i;
+
+ pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS, &status);
+ pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_MASK, &mask);
+ dev_err(dev, "rp_pio_status: %#010x, rp_pio_mask: %#010x\n",
+ status, mask);
+
+ dpc->rp_pio_status = status;
+
+ pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_SEVERITY, &sev);
+ pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_SYSERROR, &syserr);
+ pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_EXCEPTION, &exc);
+ dev_err(dev, "RP PIO severity=%#010x, syserror=%#010x, exception=%#010x\n",
+ sev, syserr, exc);
+
+ /* Get First Error Pointer */
+ pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &dpc_status);
+ first_error = (dpc_status & 0x1f00) >> 8;
+
+ status &= ~mask;
+ for (i = 0; i < ARRAY_SIZE(rp_pio_error_string); i++) {
+ if (status & (1 << i))
+ dev_err(dev, "[%2d] %s%s\n", i, rp_pio_error_string[i],
+ first_error == i ? " (First)" : "");
+ }
+
+ if (dpc->rp_log_size < 4)
+ return;
+ pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG,
+ &dw0);
+ pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 4,
+ &dw1);
+ pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 8,
+ &dw2);
+ pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 12,
+ &dw3);
+ dev_err(dev, "TLP Header: %#010x %#010x %#010x %#010x\n",
+ dw0, dw1, dw2, dw3);
+
+ if (dpc->rp_log_size < 5)
+ return;
+ pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_IMPSPEC_LOG, &log);
+ dev_err(dev, "RP PIO ImpSpec Log %#010x\n", log);
+
+ for (i = 0; i < dpc->rp_log_size - 5; i++) {
+ pci_read_config_dword(pdev,
+ cap + PCI_EXP_DPC_RP_PIO_TLPPREFIX_LOG, &prefix);
+ dev_err(dev, "TLP Prefix Header: dw%d, %#010x\n", i, prefix);
+ }
+}
+
+static irqreturn_t dpc_irq(int irq, void *context)
+{
+ struct dpc_dev *dpc = (struct dpc_dev *)context;
+ struct pci_dev *pdev = dpc->dev->port;
+ struct device *dev = &dpc->dev->device;
+ u16 cap = dpc->cap_pos, ctl, status, source, reason, ext_reason;
+
+ pci_read_config_word(pdev, cap + PCI_EXP_DPC_CTL, &ctl);
+
+ if (!(ctl & PCI_EXP_DPC_CTL_INT_EN) || ctl == (u16)(~0))
+ return IRQ_NONE;
+
+ pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status);
+
+ if (!(status & PCI_EXP_DPC_STATUS_INTERRUPT))
+ return IRQ_NONE;
+
+ if (!(status & PCI_EXP_DPC_STATUS_TRIGGER)) {
+ pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS,
+ PCI_EXP_DPC_STATUS_INTERRUPT);
+ return IRQ_HANDLED;
+ }
+
+ pci_write_config_word(pdev, cap + PCI_EXP_DPC_CTL,
+ ctl & ~PCI_EXP_DPC_CTL_INT_EN);
+
+ pci_read_config_word(pdev, cap + PCI_EXP_DPC_SOURCE_ID,
+ &source);
+
+ dev_info(dev, "DPC containment event, status:%#06x source:%#06x\n",
+ status, source);
+
+ reason = (status & PCI_EXP_DPC_STATUS_TRIGGER_RSN) >> 1;
+ ext_reason = (status & PCI_EXP_DPC_STATUS_TRIGGER_RSN_EXT) >> 5;
+
+ dev_warn(dev, "DPC %s detected, remove downstream devices\n",
+ (reason == 0) ? "unmasked uncorrectable error" :
+ (reason == 1) ? "ERR_NONFATAL" :
+ (reason == 2) ? "ERR_FATAL" :
+ (ext_reason == 0) ? "RP PIO error" :
+ (ext_reason == 1) ? "software trigger" :
+ "reserved error");
+ /* show RP PIO error detail information */
+ if (dpc->rp_extensions && reason == 3 && ext_reason == 0)
+ dpc_process_rp_pio_error(dpc);
+
+ schedule_work(&dpc->work);
+
+ return IRQ_HANDLED;
+}
+
+#define FLAG(x, y) (((x) & (y)) ? '+' : '-')
+static int dpc_probe(struct pcie_device *dev)
+{
+ struct dpc_dev *dpc;
+ struct pci_dev *pdev = dev->port;
+ struct device *device = &dev->device;
+ int status;
+ u16 ctl, cap;
+
+ if (pcie_aer_get_firmware_first(pdev))
+ return -ENOTSUPP;
+
+ dpc = devm_kzalloc(device, sizeof(*dpc), GFP_KERNEL);
+ if (!dpc)
+ return -ENOMEM;
+
+ dpc->cap_pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DPC);
+ dpc->dev = dev;
+ INIT_WORK(&dpc->work, dpc_work);
+ set_service_data(dev, dpc);
+
+ status = devm_request_irq(device, dev->irq, dpc_irq, IRQF_SHARED,
+ "pcie-dpc", dpc);
+ if (status) {
+ dev_warn(device, "request IRQ%d failed: %d\n", dev->irq,
+ status);
+ return status;
+ }
+
+ pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CAP, &cap);
+ pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl);
+
+ dpc->rp_extensions = (cap & PCI_EXP_DPC_CAP_RP_EXT);
+ if (dpc->rp_extensions) {
+ dpc->rp_log_size = (cap & PCI_EXP_DPC_RP_PIO_LOG_SIZE) >> 8;
+ if (dpc->rp_log_size < 4 || dpc->rp_log_size > 9) {
+ dev_err(device, "RP PIO log size %u is invalid\n",
+ dpc->rp_log_size);
+ dpc->rp_log_size = 0;
+ }
+ }
+
+ ctl = (ctl & 0xfff4) | PCI_EXP_DPC_CTL_EN_NONFATAL | PCI_EXP_DPC_CTL_INT_EN;
+ pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl);
+
+ dev_info(device, "DPC error containment capabilities: Int Msg #%d, RPExt%c PoisonedTLP%c SwTrigger%c RP PIO Log %d, DL_ActiveErr%c\n",
+ cap & PCI_EXP_DPC_IRQ, FLAG(cap, PCI_EXP_DPC_CAP_RP_EXT),
+ FLAG(cap, PCI_EXP_DPC_CAP_POISONED_TLP),
+ FLAG(cap, PCI_EXP_DPC_CAP_SW_TRIGGER), dpc->rp_log_size,
+ FLAG(cap, PCI_EXP_DPC_CAP_DL_ACTIVE));
+ return status;
+}
+
+static void dpc_remove(struct pcie_device *dev)
+{
+ struct dpc_dev *dpc = get_service_data(dev);
+ struct pci_dev *pdev = dev->port;
+ u16 ctl;
+
+ pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl);
+ ctl &= ~(PCI_EXP_DPC_CTL_EN_NONFATAL | PCI_EXP_DPC_CTL_INT_EN);
+ pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl);
+}
+
+static struct pcie_port_service_driver dpcdriver = {
+ .name = "dpc",
+ .port_type = PCIE_ANY_PORT,
+ .service = PCIE_PORT_SERVICE_DPC,
+ .probe = dpc_probe,
+ .remove = dpc_remove,
+};
+
+static int __init dpc_service_init(void)
+{
+ return pcie_port_service_register(&dpcdriver);
+}
+device_initcall(dpc_service_init);
diff --git a/drivers/pci/pcie/pcie-dpc.c b/drivers/pci/pcie/pcie-dpc.c
deleted file mode 100644
index 38e40c6c576f..000000000000
--- a/drivers/pci/pcie/pcie-dpc.c
+++ /dev/null
@@ -1,306 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * PCI Express Downstream Port Containment services driver
- * Author: Keith Busch <keith.busch@intel.com>
- *
- * Copyright (C) 2016 Intel Corp.
- */
-
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/pcieport_if.h>
-#include "../pci.h"
-#include "aer/aerdrv.h"
-
-struct dpc_dev {
- struct pcie_device *dev;
- struct work_struct work;
- u16 cap_pos;
- bool rp_extensions;
- u32 rp_pio_status;
- u8 rp_log_size;
-};
-
-static const char * const rp_pio_error_string[] = {
- "Configuration Request received UR Completion", /* Bit Position 0 */
- "Configuration Request received CA Completion", /* Bit Position 1 */
- "Configuration Request Completion Timeout", /* Bit Position 2 */
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- "I/O Request received UR Completion", /* Bit Position 8 */
- "I/O Request received CA Completion", /* Bit Position 9 */
- "I/O Request Completion Timeout", /* Bit Position 10 */
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- "Memory Request received UR Completion", /* Bit Position 16 */
- "Memory Request received CA Completion", /* Bit Position 17 */
- "Memory Request Completion Timeout", /* Bit Position 18 */
-};
-
-static int dpc_wait_rp_inactive(struct dpc_dev *dpc)
-{
- unsigned long timeout = jiffies + HZ;
- struct pci_dev *pdev = dpc->dev->port;
- struct device *dev = &dpc->dev->device;
- u16 cap = dpc->cap_pos, status;
-
- pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status);
- while (status & PCI_EXP_DPC_RP_BUSY &&
- !time_after(jiffies, timeout)) {
- msleep(10);
- pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status);
- }
- if (status & PCI_EXP_DPC_RP_BUSY) {
- dev_warn(dev, "DPC root port still busy\n");
- return -EBUSY;
- }
- return 0;
-}
-
-static void dpc_wait_link_inactive(struct dpc_dev *dpc)
-{
- unsigned long timeout = jiffies + HZ;
- struct pci_dev *pdev = dpc->dev->port;
- struct device *dev = &dpc->dev->device;
- u16 lnk_status;
-
- pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
- while (lnk_status & PCI_EXP_LNKSTA_DLLLA &&
- !time_after(jiffies, timeout)) {
- msleep(10);
- pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
- }
- if (lnk_status & PCI_EXP_LNKSTA_DLLLA)
- dev_warn(dev, "Link state not disabled for DPC event\n");
-}
-
-static void dpc_work(struct work_struct *work)
-{
- struct dpc_dev *dpc = container_of(work, struct dpc_dev, work);
- struct pci_dev *dev, *temp, *pdev = dpc->dev->port;
- struct pci_bus *parent = pdev->subordinate;
- u16 cap = dpc->cap_pos, ctl;
-
- pci_lock_rescan_remove();
- list_for_each_entry_safe_reverse(dev, temp, &parent->devices,
- bus_list) {
- pci_dev_get(dev);
- pci_dev_set_disconnected(dev, NULL);
- if (pci_has_subordinate(dev))
- pci_walk_bus(dev->subordinate,
- pci_dev_set_disconnected, NULL);
- pci_stop_and_remove_bus_device(dev);
- pci_dev_put(dev);
- }
- pci_unlock_rescan_remove();
-
- dpc_wait_link_inactive(dpc);
- if (dpc->rp_extensions && dpc_wait_rp_inactive(dpc))
- return;
- if (dpc->rp_extensions && dpc->rp_pio_status) {
- pci_write_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS,
- dpc->rp_pio_status);
- dpc->rp_pio_status = 0;
- }
-
- pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS,
- PCI_EXP_DPC_STATUS_TRIGGER | PCI_EXP_DPC_STATUS_INTERRUPT);
-
- pci_read_config_word(pdev, cap + PCI_EXP_DPC_CTL, &ctl);
- pci_write_config_word(pdev, cap + PCI_EXP_DPC_CTL,
- ctl | PCI_EXP_DPC_CTL_INT_EN);
-}
-
-static void dpc_process_rp_pio_error(struct dpc_dev *dpc)
-{
- struct device *dev = &dpc->dev->device;
- struct pci_dev *pdev = dpc->dev->port;
- u16 cap = dpc->cap_pos, dpc_status, first_error;
- u32 status, mask, sev, syserr, exc, dw0, dw1, dw2, dw3, log, prefix;
- int i;
-
- pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS, &status);
- pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_MASK, &mask);
- dev_err(dev, "rp_pio_status: %#010x, rp_pio_mask: %#010x\n",
- status, mask);
-
- dpc->rp_pio_status = status;
-
- pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_SEVERITY, &sev);
- pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_SYSERROR, &syserr);
- pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_EXCEPTION, &exc);
- dev_err(dev, "RP PIO severity=%#010x, syserror=%#010x, exception=%#010x\n",
- sev, syserr, exc);
-
- /* Get First Error Pointer */
- pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &dpc_status);
- first_error = (dpc_status & 0x1f00) >> 8;
-
- status &= ~mask;
- for (i = 0; i < ARRAY_SIZE(rp_pio_error_string); i++) {
- if (status & (1 << i))
- dev_err(dev, "[%2d] %s%s\n", i, rp_pio_error_string[i],
- first_error == i ? " (First)" : "");
- }
-
- if (dpc->rp_log_size < 4)
- return;
- pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG,
- &dw0);
- pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 4,
- &dw1);
- pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 8,
- &dw2);
- pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 12,
- &dw3);
- dev_err(dev, "TLP Header: %#010x %#010x %#010x %#010x\n",
- dw0, dw1, dw2, dw3);
-
- if (dpc->rp_log_size < 5)
- return;
- pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_IMPSPEC_LOG, &log);
- dev_err(dev, "RP PIO ImpSpec Log %#010x\n", log);
-
- for (i = 0; i < dpc->rp_log_size - 5; i++) {
- pci_read_config_dword(pdev,
- cap + PCI_EXP_DPC_RP_PIO_TLPPREFIX_LOG, &prefix);
- dev_err(dev, "TLP Prefix Header: dw%d, %#010x\n", i, prefix);
- }
-}
-
-static irqreturn_t dpc_irq(int irq, void *context)
-{
- struct dpc_dev *dpc = (struct dpc_dev *)context;
- struct pci_dev *pdev = dpc->dev->port;
- struct device *dev = &dpc->dev->device;
- u16 cap = dpc->cap_pos, ctl, status, source, reason, ext_reason;
-
- pci_read_config_word(pdev, cap + PCI_EXP_DPC_CTL, &ctl);
-
- if (!(ctl & PCI_EXP_DPC_CTL_INT_EN) || ctl == (u16)(~0))
- return IRQ_NONE;
-
- pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status);
-
- if (!(status & PCI_EXP_DPC_STATUS_INTERRUPT))
- return IRQ_NONE;
-
- if (!(status & PCI_EXP_DPC_STATUS_TRIGGER)) {
- pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS,
- PCI_EXP_DPC_STATUS_INTERRUPT);
- return IRQ_HANDLED;
- }
-
- pci_write_config_word(pdev, cap + PCI_EXP_DPC_CTL,
- ctl & ~PCI_EXP_DPC_CTL_INT_EN);
-
- pci_read_config_word(pdev, cap + PCI_EXP_DPC_SOURCE_ID,
- &source);
-
- dev_info(dev, "DPC containment event, status:%#06x source:%#06x\n",
- status, source);
-
- reason = (status & PCI_EXP_DPC_STATUS_TRIGGER_RSN) >> 1;
- ext_reason = (status & PCI_EXP_DPC_STATUS_TRIGGER_RSN_EXT) >> 5;
-
- dev_warn(dev, "DPC %s detected, remove downstream devices\n",
- (reason == 0) ? "unmasked uncorrectable error" :
- (reason == 1) ? "ERR_NONFATAL" :
- (reason == 2) ? "ERR_FATAL" :
- (ext_reason == 0) ? "RP PIO error" :
- (ext_reason == 1) ? "software trigger" :
- "reserved error");
- /* show RP PIO error detail information */
- if (dpc->rp_extensions && reason == 3 && ext_reason == 0)
- dpc_process_rp_pio_error(dpc);
-
- schedule_work(&dpc->work);
-
- return IRQ_HANDLED;
-}
-
-#define FLAG(x, y) (((x) & (y)) ? '+' : '-')
-static int dpc_probe(struct pcie_device *dev)
-{
- struct dpc_dev *dpc;
- struct pci_dev *pdev = dev->port;
- struct device *device = &dev->device;
- int status;
- u16 ctl, cap;
-
- if (pcie_aer_get_firmware_first(pdev))
- return -ENOTSUPP;
-
- dpc = devm_kzalloc(device, sizeof(*dpc), GFP_KERNEL);
- if (!dpc)
- return -ENOMEM;
-
- dpc->cap_pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DPC);
- dpc->dev = dev;
- INIT_WORK(&dpc->work, dpc_work);
- set_service_data(dev, dpc);
-
- status = devm_request_irq(device, dev->irq, dpc_irq, IRQF_SHARED,
- "pcie-dpc", dpc);
- if (status) {
- dev_warn(device, "request IRQ%d failed: %d\n", dev->irq,
- status);
- return status;
- }
-
- pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CAP, &cap);
- pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl);
-
- dpc->rp_extensions = (cap & PCI_EXP_DPC_CAP_RP_EXT);
- if (dpc->rp_extensions) {
- dpc->rp_log_size = (cap & PCI_EXP_DPC_RP_PIO_LOG_SIZE) >> 8;
- if (dpc->rp_log_size < 4 || dpc->rp_log_size > 9) {
- dev_err(device, "RP PIO log size %u is invalid\n",
- dpc->rp_log_size);
- dpc->rp_log_size = 0;
- }
- }
-
- ctl = (ctl & 0xfff4) | PCI_EXP_DPC_CTL_EN_NONFATAL | PCI_EXP_DPC_CTL_INT_EN;
- pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl);
-
- dev_info(device, "DPC error containment capabilities: Int Msg #%d, RPExt%c PoisonedTLP%c SwTrigger%c RP PIO Log %d, DL_ActiveErr%c\n",
- cap & PCI_EXP_DPC_IRQ, FLAG(cap, PCI_EXP_DPC_CAP_RP_EXT),
- FLAG(cap, PCI_EXP_DPC_CAP_POISONED_TLP),
- FLAG(cap, PCI_EXP_DPC_CAP_SW_TRIGGER), dpc->rp_log_size,
- FLAG(cap, PCI_EXP_DPC_CAP_DL_ACTIVE));
- return status;
-}
-
-static void dpc_remove(struct pcie_device *dev)
-{
- struct dpc_dev *dpc = get_service_data(dev);
- struct pci_dev *pdev = dev->port;
- u16 ctl;
-
- pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl);
- ctl &= ~(PCI_EXP_DPC_CTL_EN_NONFATAL | PCI_EXP_DPC_CTL_INT_EN);
- pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl);
-}
-
-static struct pcie_port_service_driver dpcdriver = {
- .name = "dpc",
- .port_type = PCIE_ANY_PORT,
- .service = PCIE_PORT_SERVICE_DPC,
- .probe = dpc_probe,
- .remove = dpc_remove,
-};
-
-static int __init dpc_service_init(void)
-{
- return pcie_port_service_register(&dpcdriver);
-}
-device_initcall(dpc_service_init);
diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c
index 5480f54f7612..3ed67676ea2a 100644
--- a/drivers/pci/pcie/pme.c
+++ b/drivers/pci/pcie/pme.c
@@ -14,7 +14,6 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/device.h>
-#include <linux/pcieport_if.h>
#include <linux/pm_runtime.h>
#include "../pci.h"
diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h
index a854bc569117..d0c6783dbfe3 100644
--- a/drivers/pci/pcie/portdrv.h
+++ b/drivers/pci/pcie/portdrv.h
@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
- * File: portdrv.h
* Purpose: PCI Express Port Bus Driver's Internal Data Structures
*
* Copyright (C) 2004 Intel
@@ -12,7 +11,66 @@
#include <linux/compiler.h>
-#define PCIE_PORT_DEVICE_MAXSERVICES 5
+extern bool pcie_ports_native;
+
+/* Service Type */
+#define PCIE_PORT_SERVICE_PME_SHIFT 0 /* Power Management Event */
+#define PCIE_PORT_SERVICE_PME (1 << PCIE_PORT_SERVICE_PME_SHIFT)
+#define PCIE_PORT_SERVICE_AER_SHIFT 1 /* Advanced Error Reporting */
+#define PCIE_PORT_SERVICE_AER (1 << PCIE_PORT_SERVICE_AER_SHIFT)
+#define PCIE_PORT_SERVICE_HP_SHIFT 2 /* Native Hotplug */
+#define PCIE_PORT_SERVICE_HP (1 << PCIE_PORT_SERVICE_HP_SHIFT)
+#define PCIE_PORT_SERVICE_DPC_SHIFT 3 /* Downstream Port Containment */
+#define PCIE_PORT_SERVICE_DPC (1 << PCIE_PORT_SERVICE_DPC_SHIFT)
+
+#define PCIE_PORT_DEVICE_MAXSERVICES 4
+
+/* Port Type */
+#define PCIE_ANY_PORT (~0)
+
+struct pcie_device {
+ int irq; /* Service IRQ/MSI/MSI-X Vector */
+ struct pci_dev *port; /* Root/Upstream/Downstream Port */
+ u32 service; /* Port service this device represents */
+ void *priv_data; /* Service Private Data */
+ struct device device; /* Generic Device Interface */
+};
+#define to_pcie_device(d) container_of(d, struct pcie_device, device)
+
+static inline void set_service_data(struct pcie_device *dev, void *data)
+{
+ dev->priv_data = data;
+}
+
+static inline void *get_service_data(struct pcie_device *dev)
+{
+ return dev->priv_data;
+}
+
+struct pcie_port_service_driver {
+ const char *name;
+ int (*probe) (struct pcie_device *dev);
+ void (*remove) (struct pcie_device *dev);
+ int (*suspend) (struct pcie_device *dev);
+ int (*resume) (struct pcie_device *dev);
+
+ /* Device driver may resume normal operations */
+ void (*error_resume)(struct pci_dev *dev);
+
+ /* Link Reset Capability - AER service driver specific */
+ pci_ers_result_t (*reset_link) (struct pci_dev *dev);
+
+ int port_type; /* Type of the port this driver can handle */
+ u32 service; /* Port service this device represents */
+
+ struct device_driver driver;
+};
+#define to_service_driver(d) \
+ container_of(d, struct pcie_port_service_driver, driver)
+
+int pcie_port_service_register(struct pcie_port_service_driver *new);
+void pcie_port_service_unregister(struct pcie_port_service_driver *new);
+
/*
* The PCIe Capability Interrupt Message Number (PCIe r3.1, sec 7.8.2) must
* be one of the first 32 MSI-X entries. Per PCI r3.0, sec 6.8.3.1, MSI
@@ -34,20 +92,6 @@ void pcie_port_bus_unregister(void);
struct pci_dev;
-void pcie_clear_root_pme_status(struct pci_dev *dev);
-
-#ifdef CONFIG_HOTPLUG_PCI_PCIE
-extern bool pciehp_msi_disabled;
-
-static inline bool pciehp_no_msi(void)
-{
- return pciehp_msi_disabled;
-}
-
-#else /* !CONFIG_HOTPLUG_PCI_PCIE */
-static inline bool pciehp_no_msi(void) { return false; }
-#endif /* !CONFIG_HOTPLUG_PCI_PCIE */
-
#ifdef CONFIG_PCIE_PME
extern bool pcie_pme_msi_disabled;
@@ -68,15 +112,4 @@ static inline bool pcie_pme_no_msi(void) { return false; }
static inline void pcie_pme_interrupt_enable(struct pci_dev *dev, bool en) {}
#endif /* !CONFIG_PCIE_PME */
-#ifdef CONFIG_ACPI
-void pcie_port_acpi_setup(struct pci_dev *port, int *mask);
-
-static inline void pcie_port_platform_notify(struct pci_dev *port, int *mask)
-{
- pcie_port_acpi_setup(port, mask);
-}
-#else /* !CONFIG_ACPI */
-static inline void pcie_port_platform_notify(struct pci_dev *port, int *mask){}
-#endif /* !CONFIG_ACPI */
-
#endif /* _PORTDRV_H_ */
diff --git a/drivers/pci/pcie/portdrv_acpi.c b/drivers/pci/pcie/portdrv_acpi.c
index 319c94976873..8ab5d434b9c6 100644
--- a/drivers/pci/pcie/portdrv_acpi.c
+++ b/drivers/pci/pcie/portdrv_acpi.c
@@ -10,7 +10,6 @@
#include <linux/errno.h>
#include <linux/acpi.h>
#include <linux/pci-acpi.h>
-#include <linux/pcieport_if.h>
#include "aer/aerdrv.h"
#include "../pci.h"
@@ -48,11 +47,11 @@ void pcie_port_acpi_setup(struct pci_dev *port, int *srv_mask)
flags = root->osc_control_set;
- *srv_mask = PCIE_PORT_SERVICE_VC | PCIE_PORT_SERVICE_DPC;
+ *srv_mask = 0;
if (flags & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL)
*srv_mask |= PCIE_PORT_SERVICE_HP;
if (flags & OSC_PCI_EXPRESS_PME_CONTROL)
*srv_mask |= PCIE_PORT_SERVICE_PME;
if (flags & OSC_PCI_EXPRESS_AER_CONTROL)
- *srv_mask |= PCIE_PORT_SERVICE_AER;
+ *srv_mask |= PCIE_PORT_SERVICE_AER | PCIE_PORT_SERVICE_DPC;
}
diff --git a/drivers/pci/pcie/portdrv_bus.c b/drivers/pci/pcie/portdrv_bus.c
deleted file mode 100644
index f0fba552a0e2..000000000000
--- a/drivers/pci/pcie/portdrv_bus.c
+++ /dev/null
@@ -1,56 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * File: portdrv_bus.c
- * Purpose: PCI Express Port Bus Driver's Bus Overloading Functions
- *
- * Copyright (C) 2004 Intel
- * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
- */
-
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/pm.h>
-
-#include <linux/pcieport_if.h>
-#include "portdrv.h"
-
-static int pcie_port_bus_match(struct device *dev, struct device_driver *drv);
-
-struct bus_type pcie_port_bus_type = {
- .name = "pci_express",
- .match = pcie_port_bus_match,
-};
-EXPORT_SYMBOL_GPL(pcie_port_bus_type);
-
-static int pcie_port_bus_match(struct device *dev, struct device_driver *drv)
-{
- struct pcie_device *pciedev;
- struct pcie_port_service_driver *driver;
-
- if (drv->bus != &pcie_port_bus_type || dev->bus != &pcie_port_bus_type)
- return 0;
-
- pciedev = to_pcie_device(dev);
- driver = to_service_driver(drv);
-
- if (driver->service != pciedev->service)
- return 0;
-
- if ((driver->port_type != PCIE_ANY_PORT) &&
- (driver->port_type != pci_pcie_type(pciedev->port)))
- return 0;
-
- return 1;
-}
-
-int pcie_port_bus_register(void)
-{
- return bus_register(&pcie_port_bus_type);
-}
-
-void pcie_port_bus_unregister(void)
-{
- bus_unregister(&pcie_port_bus_type);
-}
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c
index ef3bad4ad010..c9c0663db282 100644
--- a/drivers/pci/pcie/portdrv_core.c
+++ b/drivers/pci/pcie/portdrv_core.c
@@ -1,6 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * File: portdrv_core.c
* Purpose: PCI Express Port Bus Driver's Core Functions
*
* Copyright (C) 2004 Intel
@@ -15,23 +14,11 @@
#include <linux/pm_runtime.h>
#include <linux/string.h>
#include <linux/slab.h>
-#include <linux/pcieport_if.h>
#include <linux/aer.h>
#include "../pci.h"
#include "portdrv.h"
-bool pciehp_msi_disabled;
-
-static int __init pciehp_setup(char *str)
-{
- if (!strncmp(str, "nomsi", 5))
- pciehp_msi_disabled = true;
-
- return 1;
-}
-__setup("pcie_hp=", pciehp_setup);
-
/**
* release_pcie_device - free PCI Express port service device structure
* @dev: Port service device to release
@@ -52,7 +39,7 @@ static void release_pcie_device(struct device *dev)
static int pcie_message_numbers(struct pci_dev *dev, int mask,
u32 *pme, u32 *aer, u32 *dpc)
{
- u32 nvec = 0, pos, reg32;
+ u32 nvec = 0, pos;
u16 reg16;
/*
@@ -68,8 +55,11 @@ static int pcie_message_numbers(struct pci_dev *dev, int mask,
nvec = *pme + 1;
}
+#ifdef CONFIG_PCIEAER
if (mask & PCIE_PORT_SERVICE_AER) {
- pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
+ u32 reg32;
+
+ pos = dev->aer_cap;
if (pos) {
pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS,
&reg32);
@@ -77,6 +67,7 @@ static int pcie_message_numbers(struct pci_dev *dev, int mask,
nvec = max(nvec, *aer + 1);
}
}
+#endif
if (mask & PCIE_PORT_SERVICE_DPC) {
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC);
@@ -169,16 +160,13 @@ static int pcie_init_service_irqs(struct pci_dev *dev, int *irqs, int mask)
irqs[i] = -1;
/*
- * If we support PME or hotplug, but we can't use MSI/MSI-X for
- * them, we have to fall back to INTx or other interrupts, e.g., a
- * system shared interrupt.
+ * If we support PME but can't use MSI/MSI-X for it, we have to
+ * fall back to INTx or other interrupts, e.g., a system shared
+ * interrupt.
*/
if ((mask & PCIE_PORT_SERVICE_PME) && pcie_pme_no_msi())
goto legacy_irq;
- if ((mask & PCIE_PORT_SERVICE_HP) && pciehp_no_msi())
- goto legacy_irq;
-
/* Try to use MSI-X or MSI if supported */
if (pcie_port_enable_irq_vec(dev, irqs, mask) == 0)
return 0;
@@ -189,10 +177,8 @@ legacy_irq:
if (ret < 0)
return -ENODEV;
- for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
- if (i != PCIE_PORT_SERVICE_VC_SHIFT)
- irqs[i] = pci_irq_vector(dev, 0);
- }
+ for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
+ irqs[i] = pci_irq_vector(dev, 0);
return 0;
}
@@ -209,23 +195,13 @@ legacy_irq:
*/
static int get_port_device_capability(struct pci_dev *dev)
{
+ struct pci_host_bridge *host = pci_find_host_bridge(dev->bus);
int services = 0;
- int cap_mask = 0;
-
- if (pcie_ports_disabled)
- return 0;
-
- cap_mask = PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP
- | PCIE_PORT_SERVICE_VC;
- if (pci_aer_available())
- cap_mask |= PCIE_PORT_SERVICE_AER | PCIE_PORT_SERVICE_DPC;
- if (pcie_ports_auto)
- pcie_port_platform_notify(dev, &cap_mask);
-
- /* Hot-Plug Capable */
- if ((cap_mask & PCIE_PORT_SERVICE_HP) && dev->is_hotplug_bridge) {
+ if (dev->is_hotplug_bridge &&
+ (pcie_ports_native || host->native_hotplug)) {
services |= PCIE_PORT_SERVICE_HP;
+
/*
* Disable hot-plug interrupts in case they have been enabled
* by the BIOS and the hot-plug service driver is not loaded.
@@ -233,23 +209,29 @@ static int get_port_device_capability(struct pci_dev *dev)
pcie_capability_clear_word(dev, PCI_EXP_SLTCTL,
PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_HPIE);
}
- /* AER capable */
- if ((cap_mask & PCIE_PORT_SERVICE_AER)
- && pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR)) {
+
+#ifdef CONFIG_PCIEAER
+ if (dev->aer_cap && pci_aer_available() &&
+ (pcie_ports_native || host->native_aer)) {
services |= PCIE_PORT_SERVICE_AER;
+
/*
* Disable AER on this port in case it's been enabled by the
* BIOS (the AER service driver will enable it when necessary).
*/
pci_disable_pcie_error_reporting(dev);
}
- /* VC support */
- if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_VC))
- services |= PCIE_PORT_SERVICE_VC;
- /* Root ports are capable of generating PME too */
- if ((cap_mask & PCIE_PORT_SERVICE_PME)
- && pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) {
+#endif
+
+ /*
+ * Root ports are capable of generating PME too. Root Complex
+ * Event Collectors can also generate PMEs, but we don't handle
+ * those yet.
+ */
+ if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT &&
+ (pcie_ports_native || host->native_pme)) {
services |= PCIE_PORT_SERVICE_PME;
+
/*
* Disable PME interrupt on this port in case it's been enabled
* by the BIOS (the PME service driver will enable it when
@@ -257,7 +239,9 @@ static int get_port_device_capability(struct pci_dev *dev)
*/
pcie_pme_interrupt_enable(dev, false);
}
- if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC))
+
+ if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC) &&
+ pci_aer_available() && services & PCIE_PORT_SERVICE_AER)
services |= PCIE_PORT_SERVICE_DPC;
return services;
@@ -335,7 +319,7 @@ int pcie_port_device_register(struct pci_dev *dev)
*/
status = pcie_init_service_irqs(dev, irqs, capabilities);
if (status) {
- capabilities &= PCIE_PORT_SERVICE_VC | PCIE_PORT_SERVICE_HP;
+ capabilities &= PCIE_PORT_SERVICE_HP;
if (!capabilities)
goto error_disable;
}
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c
index fb1c1bb87316..973f1b80a038 100644
--- a/drivers/pci/pcie/portdrv_pci.c
+++ b/drivers/pci/pcie/portdrv_pci.c
@@ -1,9 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * File: portdrv_pci.c
* Purpose: PCI Express Port Bus Driver
* Author: Tom Nguyen <tom.l.nguyen@intel.com>
- * Version: v1.0
*
* Copyright (C) 2004 Intel
* Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
@@ -15,10 +13,8 @@
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/init.h>
-#include <linux/pcieport_if.h>
#include <linux/aer.h>
#include <linux/dmi.h>
-#include <linux/pci-aspm.h>
#include "../pci.h"
#include "portdrv.h"
@@ -27,22 +23,18 @@
bool pcie_ports_disabled;
/*
- * If this switch is set, ACPI _OSC will be used to determine whether or not to
- * enable PCIe port native services.
+ * If the user specified "pcie_ports=native", use the PCIe services regardless
+ * of whether the platform has given us permission. On ACPI systems, this
+ * means we ignore _OSC.
*/
-bool pcie_ports_auto = true;
+bool pcie_ports_native;
static int __init pcie_port_setup(char *str)
{
- if (!strncmp(str, "compat", 6)) {
+ if (!strncmp(str, "compat", 6))
pcie_ports_disabled = true;
- } else if (!strncmp(str, "native", 6)) {
- pcie_ports_disabled = false;
- pcie_ports_auto = false;
- } else if (!strncmp(str, "auto", 4)) {
- pcie_ports_disabled = false;
- pcie_ports_auto = true;
- }
+ else if (!strncmp(str, "native", 6))
+ pcie_ports_native = true;
return 1;
}
@@ -50,15 +42,6 @@ __setup("pcie_ports=", pcie_port_setup);
/* global data */
-/**
- * pcie_clear_root_pme_status - Clear root port PME interrupt status.
- * @dev: PCIe root port or event collector.
- */
-void pcie_clear_root_pme_status(struct pci_dev *dev)
-{
- pcie_capability_set_dword(dev, PCI_EXP_RTSTA, PCI_EXP_RTSTA_PME);
-}
-
static int pcie_portdrv_restore_config(struct pci_dev *dev)
{
int retval;
@@ -71,20 +54,6 @@ static int pcie_portdrv_restore_config(struct pci_dev *dev)
}
#ifdef CONFIG_PM
-static int pcie_port_resume_noirq(struct device *dev)
-{
- struct pci_dev *pdev = to_pci_dev(dev);
-
- /*
- * Some BIOSes forget to clear Root PME Status bits after system wakeup
- * which breaks ACPI-based runtime wakeup on PCI Express, so clear those
- * bits now just in case (shouldn't hurt).
- */
- if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT)
- pcie_clear_root_pme_status(pdev);
- return 0;
-}
-
static int pcie_port_runtime_suspend(struct device *dev)
{
return to_pci_dev(dev)->bridge_d3 ? 0 : -EBUSY;
@@ -112,7 +81,6 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = {
.thaw = pcie_port_device_resume,
.poweroff = pcie_port_device_suspend,
.restore = pcie_port_device_resume,
- .resume_noirq = pcie_port_resume_noirq,
.runtime_suspend = pcie_port_runtime_suspend,
.runtime_resume = pcie_port_runtime_resume,
.runtime_idle = pcie_port_runtime_idle,
@@ -283,22 +251,11 @@ static const struct dmi_system_id pcie_portdrv_dmi_table[] __initconst = {
static int __init pcie_portdrv_init(void)
{
- int retval;
-
if (pcie_ports_disabled)
- return pci_register_driver(&pcie_portdriver);
+ return -EACCES;
dmi_check_system(pcie_portdrv_dmi_table);
- retval = pcie_port_bus_register();
- if (retval) {
- printk(KERN_WARNING "PCIE: bus_register error: %d\n", retval);
- goto out;
- }
- retval = pci_register_driver(&pcie_portdriver);
- if (retval)
- pcie_port_bus_unregister();
- out:
- return retval;
+ return pci_register_driver(&pcie_portdriver);
}
device_initcall(pcie_portdrv_init);
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index ef5377438a1e..ac91b6fd0bcd 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * probe.c - PCI detection and setup code
+ * PCI detection and setup code
*/
#include <linux/kernel.h>
@@ -16,6 +16,7 @@
#include <linux/pci-aspm.h>
#include <linux/aer.h>
#include <linux/acpi.h>
+#include <linux/hypervisor.h>
#include <linux/irqdomain.h>
#include <linux/pm_runtime.h>
#include "pci.h"
@@ -329,6 +330,10 @@ static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom)
if (dev->non_compliant_bars)
return;
+ /* Per PCIe r4.0, sec 9.3.4.1.11, the VF BARs are all RO Zero */
+ if (dev->is_virtfn)
+ return;
+
for (pos = 0; pos < howmany; pos++) {
struct resource *res = &dev->resource[pos];
reg = PCI_BASE_ADDRESS_0 + (pos << 2);
@@ -540,6 +545,16 @@ struct pci_host_bridge *pci_alloc_host_bridge(size_t priv)
INIT_LIST_HEAD(&bridge->windows);
bridge->dev.release = pci_release_host_bridge_dev;
+ /*
+ * We assume we can manage these PCIe features. Some systems may
+ * reserve these for use by the platform itself, e.g., an ACPI BIOS
+ * may implement its own AER handling and use _OSC to prevent the
+ * OS from interfering.
+ */
+ bridge->native_aer = 1;
+ bridge->native_hotplug = 1;
+ bridge->native_pme = 1;
+
return bridge;
}
EXPORT_SYMBOL(pci_alloc_host_bridge);
@@ -592,7 +607,7 @@ const unsigned char pcie_link_speed[] = {
PCIE_SPEED_2_5GT, /* 1 */
PCIE_SPEED_5_0GT, /* 2 */
PCIE_SPEED_8_0GT, /* 3 */
- PCI_SPEED_UNKNOWN, /* 4 */
+ PCIE_SPEED_16_0GT, /* 4 */
PCI_SPEED_UNKNOWN, /* 5 */
PCI_SPEED_UNKNOWN, /* 6 */
PCI_SPEED_UNKNOWN, /* 7 */
@@ -1230,6 +1245,13 @@ static void pci_read_irq(struct pci_dev *dev)
{
unsigned char irq;
+ /* VFs are not allowed to use INTx, so skip the config reads */
+ if (dev->is_virtfn) {
+ dev->pin = 0;
+ dev->irq = 0;
+ return;
+ }
+
pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq);
dev->pin = irq;
if (irq)
@@ -1389,6 +1411,43 @@ int pci_cfg_space_size(struct pci_dev *dev)
return PCI_CFG_SPACE_SIZE;
}
+static u32 pci_class(struct pci_dev *dev)
+{
+ u32 class;
+
+#ifdef CONFIG_PCI_IOV
+ if (dev->is_virtfn)
+ return dev->physfn->sriov->class;
+#endif
+ pci_read_config_dword(dev, PCI_CLASS_REVISION, &class);
+ return class;
+}
+
+static void pci_subsystem_ids(struct pci_dev *dev, u16 *vendor, u16 *device)
+{
+#ifdef CONFIG_PCI_IOV
+ if (dev->is_virtfn) {
+ *vendor = dev->physfn->sriov->subsystem_vendor;
+ *device = dev->physfn->sriov->subsystem_device;
+ return;
+ }
+#endif
+ pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, vendor);
+ pci_read_config_word(dev, PCI_SUBSYSTEM_ID, device);
+}
+
+static u8 pci_hdr_type(struct pci_dev *dev)
+{
+ u8 hdr_type;
+
+#ifdef CONFIG_PCI_IOV
+ if (dev->is_virtfn)
+ return dev->physfn->sriov->hdr_type;
+#endif
+ pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type);
+ return hdr_type;
+}
+
#define LEGACY_IO_RESOURCE (IORESOURCE_IO | IORESOURCE_PCI_FIXED)
static void pci_msi_setup_pci_dev(struct pci_dev *dev)
@@ -1454,8 +1513,7 @@ int pci_setup_device(struct pci_dev *dev)
struct pci_bus_region region;
struct resource *res;
- if (pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type))
- return -EIO;
+ hdr_type = pci_hdr_type(dev);
dev->sysdata = dev->bus->sysdata;
dev->dev.parent = dev->bus->bridge;
@@ -1477,7 +1535,8 @@ int pci_setup_device(struct pci_dev *dev)
dev->bus->number, PCI_SLOT(dev->devfn),
PCI_FUNC(dev->devfn));
- pci_read_config_dword(dev, PCI_CLASS_REVISION, &class);
+ class = pci_class(dev);
+
dev->revision = class & 0xff;
dev->class = class >> 8; /* upper 3 bytes */
@@ -1517,8 +1576,8 @@ int pci_setup_device(struct pci_dev *dev)
goto bad;
pci_read_irq(dev);
pci_read_bases(dev, 6, PCI_ROM_ADDRESS);
- pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor);
- pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &dev->subsystem_device);
+
+ pci_subsystem_ids(dev, &dev->subsystem_vendor, &dev->subsystem_device);
/*
* Do the ugly legacy mode stuff here rather than broken chip
@@ -2121,6 +2180,9 @@ static void pci_init_capabilities(struct pci_dev *dev)
/* Advanced Error Reporting */
pci_aer_init(dev);
+
+ if (pci_probe_reset_function(dev) == 0)
+ dev->reset_fn = 1;
}
/*
@@ -2518,14 +2580,29 @@ static unsigned int pci_scan_child_bus_extend(struct pci_bus *bus,
{
unsigned int used_buses, normal_bridges = 0, hotplug_bridges = 0;
unsigned int start = bus->busn_res.start;
- unsigned int devfn, cmax, max = start;
+ unsigned int devfn, fn, cmax, max = start;
struct pci_dev *dev;
+ int nr_devs;
dev_dbg(&bus->dev, "scanning bus\n");
/* Go find them, Rover! */
- for (devfn = 0; devfn < 0x100; devfn += 8)
- pci_scan_slot(bus, devfn);
+ for (devfn = 0; devfn < 256; devfn += 8) {
+ nr_devs = pci_scan_slot(bus, devfn);
+
+ /*
+ * The Jailhouse hypervisor may pass individual functions of a
+ * multi-function device to a guest without passing function 0.
+ * Look for them as well.
+ */
+ if (jailhouse_paravirt() && nr_devs == 0) {
+ for (fn = 1; fn < 8; fn++) {
+ dev = pci_scan_single_device(bus, devfn + fn);
+ if (dev)
+ dev->multifunction = 1;
+ }
+ }
+ }
/* Reserve buses for SR-IOV capability */
used_buses = pci_iov_bus_range(bus);
diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c
index 58a662e3c4a6..1ee8927a0635 100644
--- a/drivers/pci/proc.c
+++ b/drivers/pci/proc.c
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Procfs interface for the PCI bus.
+ * Procfs interface for the PCI bus
*
- * Copyright (c) 1997--1999 Martin Mares <mj@ucw.cz>
+ * Copyright (c) 1997--1999 Martin Mares <mj@ucw.cz>
*/
#include <linux/init.h>
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index c3ba14f6444e..2990ad1e7c99 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -1,15 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * This file contains work-arounds for many known PCI hardware
- * bugs. Devices present only on certain architectures (host
- * bridges et cetera) should be handled in arch-specific code.
+ * This file contains work-arounds for many known PCI hardware bugs.
+ * Devices present only on certain architectures (host bridges et cetera)
+ * should be handled in arch-specific code.
*
- * Note: any quirks for hotpluggable devices must _NOT_ be declared __init.
+ * Note: any quirks for hotpluggable devices must _NOT_ be declared __init.
*
- * Copyright (c) 1999 Martin Mares <mj@ucw.cz>
+ * Copyright (c) 1999 Martin Mares <mj@ucw.cz>
*
- * Init/reset quirks for USB host controllers should be in the
- * USB quirks file, where their drivers can access reuse it.
+ * Init/reset quirks for USB host controllers should be in the USB quirks
+ * file, where their drivers can use them.
*/
#include <linux/types.h>
@@ -1968,31 +1968,6 @@ static void quirk_netmos(struct pci_dev *dev)
DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_VENDOR_ID_NETMOS, PCI_ANY_ID,
PCI_CLASS_COMMUNICATION_SERIAL, 8, quirk_netmos);
-/*
- * Quirk non-zero PCI functions to route VPD access through function 0 for
- * devices that share VPD resources between functions. The functions are
- * expected to be identical devices.
- */
-static void quirk_f0_vpd_link(struct pci_dev *dev)
-{
- struct pci_dev *f0;
-
- if (!PCI_FUNC(dev->devfn))
- return;
-
- f0 = pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
- if (!f0)
- return;
-
- if (f0->vpd && dev->class == f0->class &&
- dev->vendor == f0->vendor && dev->device == f0->device)
- dev->dev_flags |= PCI_DEV_FLAGS_VPD_REF_F0;
-
- pci_dev_put(f0);
-}
-DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, PCI_ANY_ID,
- PCI_CLASS_NETWORK_ETHERNET, 8, quirk_f0_vpd_link);
-
static void quirk_e100_interrupt(struct pci_dev *dev)
{
u16 command, pmcsr;
@@ -2183,83 +2158,6 @@ static void quirk_via_cx700_pci_parking_caching(struct pci_dev *dev)
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, 0x324e, quirk_via_cx700_pci_parking_caching);
-/*
- * If a device follows the VPD format spec, the PCI core will not read or
- * write past the VPD End Tag. But some vendors do not follow the VPD
- * format spec, so we can't tell how much data is safe to access. Devices
- * may behave unpredictably if we access too much. Blacklist these devices
- * so we don't touch VPD at all.
- */
-static void quirk_blacklist_vpd(struct pci_dev *dev)
-{
- if (dev->vpd) {
- dev->vpd->len = 0;
- pci_warn(dev, FW_BUG "disabling VPD access (can't determine size of non-standard VPD format)\n");
- }
-}
-
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0060, quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x007c, quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0413, quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0078, quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0079, quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0073, quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0071, quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005b, quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x002f, quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005d, quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005f, quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, PCI_ANY_ID,
- quirk_blacklist_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_QLOGIC, 0x2261, quirk_blacklist_vpd);
-
-/*
- * For Broadcom 5706, 5708, 5709 rev. A nics, any read beyond the
- * VPD end tag will hang the device. This problem was initially
- * observed when a vpd entry was created in sysfs
- * ('/sys/bus/pci/devices/<id>/vpd'). A read to this sysfs entry
- * will dump 32k of data. Reading a full 32k will cause an access
- * beyond the VPD end tag causing the device to hang. Once the device
- * is hung, the bnx2 driver will not be able to reset the device.
- * We believe that it is legal to read beyond the end tag and
- * therefore the solution is to limit the read/write length.
- */
-static void quirk_brcm_570x_limit_vpd(struct pci_dev *dev)
-{
- /*
- * Only disable the VPD capability for 5706, 5706S, 5708,
- * 5708S and 5709 rev. A
- */
- if ((dev->device == PCI_DEVICE_ID_NX2_5706) ||
- (dev->device == PCI_DEVICE_ID_NX2_5706S) ||
- (dev->device == PCI_DEVICE_ID_NX2_5708) ||
- (dev->device == PCI_DEVICE_ID_NX2_5708S) ||
- ((dev->device == PCI_DEVICE_ID_NX2_5709) &&
- (dev->revision & 0xf0) == 0x0)) {
- if (dev->vpd)
- dev->vpd->len = 0x80;
- }
-}
-
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
- PCI_DEVICE_ID_NX2_5706,
- quirk_brcm_570x_limit_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
- PCI_DEVICE_ID_NX2_5706S,
- quirk_brcm_570x_limit_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
- PCI_DEVICE_ID_NX2_5708,
- quirk_brcm_570x_limit_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
- PCI_DEVICE_ID_NX2_5708S,
- quirk_brcm_570x_limit_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
- PCI_DEVICE_ID_NX2_5709,
- quirk_brcm_570x_limit_vpd);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
- PCI_DEVICE_ID_NX2_5709S,
- quirk_brcm_570x_limit_vpd);
-
static void quirk_brcm_5719_limit_mrrs(struct pci_dev *dev)
{
u32 rev;
@@ -2320,25 +2218,6 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82865_HB,
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82875_HB,
quirk_unhide_mch_dev6);
-#ifdef CONFIG_TILEPRO
-/*
- * The Tilera TILEmpower tilepro platform needs to set the link speed
- * to 2.5GT(Giga-Transfers)/s (Gen 1). The default link speed
- * setting is 5GT/s (Gen 2). 0x98 is the Link Control2 PCIe
- * capability register of the PEX8624 PCIe switch. The switch
- * supports link speed auto negotiation, but falsely sets
- * the link speed to 5GT/s.
- */
-static void quirk_tile_plx_gen1(struct pci_dev *dev)
-{
- if (tile_plx_gen1) {
- pci_write_config_dword(dev, 0x98, 0x1);
- mdelay(50);
- }
-}
-DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_PLX, 0x8624, quirk_tile_plx_gen1);
-#endif /* CONFIG_TILEPRO */
-
#ifdef CONFIG_PCI_MSI
/* Some chipsets do not support MSI. We cannot easily rely on setting
* PCI_BUS_FLAGS_NO_MSI in its bus flags because there are actually
@@ -3105,16 +2984,10 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e0d, quirk_intel_ntb);
static ktime_t fixup_debug_start(struct pci_dev *dev,
void (*fn)(struct pci_dev *dev))
{
- ktime_t calltime = 0;
-
- pci_dbg(dev, "calling %pF\n", fn);
- if (initcall_debug) {
- pr_debug("calling %pF @ %i for %s\n",
- fn, task_pid_nr(current), dev_name(&dev->dev));
- calltime = ktime_get();
- }
+ if (initcall_debug)
+ pci_info(dev, "calling %pF @ %i\n", fn, task_pid_nr(current));
- return calltime;
+ return ktime_get();
}
static void fixup_debug_report(struct pci_dev *dev, ktime_t calltime,
@@ -3123,13 +2996,11 @@ static void fixup_debug_report(struct pci_dev *dev, ktime_t calltime,
ktime_t delta, rettime;
unsigned long long duration;
- if (initcall_debug) {
- rettime = ktime_get();
- delta = ktime_sub(rettime, calltime);
- duration = (unsigned long long) ktime_to_ns(delta) >> 10;
- pr_debug("pci fixup %pF returned after %lld usecs for %s\n",
- fn, duration, dev_name(&dev->dev));
- }
+ rettime = ktime_get();
+ delta = ktime_sub(rettime, calltime);
+ duration = (unsigned long long) ktime_to_ns(delta) >> 10;
+ if (initcall_debug || duration > 10000)
+ pci_info(dev, "%pF took %lld usecs\n", fn, duration);
}
/*
@@ -3418,32 +3289,6 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PORT_RIDGE,
quirk_thunderbolt_hotplug_msi);
-static void quirk_chelsio_extend_vpd(struct pci_dev *dev)
-{
- int chip = (dev->device & 0xf000) >> 12;
- int func = (dev->device & 0x0f00) >> 8;
- int prod = (dev->device & 0x00ff) >> 0;
-
- /*
- * If this is a T3-based adapter, there's a 1KB VPD area at offset
- * 0xc00 which contains the preferred VPD values. If this is a T4 or
- * later based adapter, the special VPD is at offset 0x400 for the
- * Physical Functions (the SR-IOV Virtual Functions have no VPD
- * Capabilities). The PCI VPD Access core routines will normally
- * compute the size of the VPD by parsing the VPD Data Structure at
- * offset 0x000. This will result in silent failures when attempting
- * to accesses these other VPD areas which are beyond those computed
- * limits.
- */
- if (chip == 0x0 && prod >= 0x20)
- pci_set_vpd_size(dev, 8192);
- else if (chip >= 0x4 && func < 0x8)
- pci_set_vpd_size(dev, 2048);
-}
-
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, PCI_ANY_ID,
- quirk_chelsio_extend_vpd);
-
#ifdef CONFIG_ACPI
/*
* Apple: Shutdown Cactus Ridge Thunderbolt controller.
@@ -3904,6 +3749,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9182,
/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c46 */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x91a0,
quirk_dma_func1_alias);
+/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c127 */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9220,
+ quirk_dma_func1_alias);
/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c49 */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9230,
quirk_dma_func1_alias);
@@ -4524,6 +4372,15 @@ static const struct pci_dev_acs_enabled {
{ PCI_VENDOR_ID_CAVIUM, PCI_ANY_ID, pci_quirk_cavium_acs },
/* APM X-Gene */
{ PCI_VENDOR_ID_AMCC, 0xE004, pci_quirk_xgene_acs },
+ /* Ampere Computing */
+ { PCI_VENDOR_ID_AMPERE, 0xE005, pci_quirk_xgene_acs },
+ { PCI_VENDOR_ID_AMPERE, 0xE006, pci_quirk_xgene_acs },
+ { PCI_VENDOR_ID_AMPERE, 0xE007, pci_quirk_xgene_acs },
+ { PCI_VENDOR_ID_AMPERE, 0xE008, pci_quirk_xgene_acs },
+ { PCI_VENDOR_ID_AMPERE, 0xE009, pci_quirk_xgene_acs },
+ { PCI_VENDOR_ID_AMPERE, 0xE00A, pci_quirk_xgene_acs },
+ { PCI_VENDOR_ID_AMPERE, 0xE00B, pci_quirk_xgene_acs },
+ { PCI_VENDOR_ID_AMPERE, 0xE00C, pci_quirk_xgene_acs },
{ 0 }
};
@@ -4816,9 +4673,13 @@ static void quirk_no_ext_tags(struct pci_dev *pdev)
pci_walk_bus(bridge->bus, pci_configure_extended_tags, NULL);
}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, 0x0132, quirk_no_ext_tags);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, 0x0140, quirk_no_ext_tags);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, 0x0141, quirk_no_ext_tags);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, 0x0142, quirk_no_ext_tags);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, 0x0144, quirk_no_ext_tags);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, 0x0420, quirk_no_ext_tags);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, 0x0422, quirk_no_ext_tags);
#ifdef CONFIG_PCI_ATS
/*
diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c
index 374a33443be9..a7b5c37a85ec 100644
--- a/drivers/pci/rom.c
+++ b/drivers/pci/rom.c
@@ -1,11 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * drivers/pci/rom.c
+ * PCI ROM access routines
*
* (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
* (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
- *
- * PCI ROM access routines
*/
#include <linux/kernel.h>
#include <linux/export.h>
diff --git a/drivers/pci/search.c b/drivers/pci/search.c
index bc1e023f1353..2b5f720862d3 100644
--- a/drivers/pci/search.c
+++ b/drivers/pci/search.c
@@ -1,11 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * PCI searching functions.
+ * PCI searching functions
*
- * Copyright (C) 1993 -- 1997 Drew Eckhardt, Frederic Potter,
+ * Copyright (C) 1993 -- 1997 Drew Eckhardt, Frederic Potter,
* David Mosberger-Tang
- * Copyright (C) 1997 -- 2000 Martin Mares <mj@ucw.cz>
- * Copyright (C) 2003 -- 2004 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (C) 1997 -- 2000 Martin Mares <mj@ucw.cz>
+ * Copyright (C) 2003 -- 2004 Greg Kroah-Hartman <greg@kroah.com>
*/
#include <linux/pci.h>
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index 3cce29a069e6..072784f55ea5 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -1,16 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * drivers/pci/setup-bus.c
+ * Support routines for initializing a PCI subsystem
*
* Extruded from code written by
* Dave Rusling (david.rusling@reo.mts.dec.com)
* David Mosberger (davidm@cs.arizona.edu)
* David Miller (davem@redhat.com)
*
- * Support routines for initializing a PCI subsystem.
- */
-
-/*
* Nov 2000, Ivan Kokshaysky <ink@jurassic.park.msu.ru>
* PCI-PCI bridges cleanup, sorted resource allocation.
* Feb 2002, Ivan Kokshaysky <ink@jurassic.park.msu.ru>
diff --git a/drivers/pci/setup-irq.c b/drivers/pci/setup-irq.c
index 5ad4ee7d7b1e..7129494754dd 100644
--- a/drivers/pci/setup-irq.c
+++ b/drivers/pci/setup-irq.c
@@ -1,13 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * drivers/pci/setup-irq.c
+ * Support routines for initializing a PCI subsystem
*
* Extruded from code written by
* Dave Rusling (david.rusling@reo.mts.dec.com)
* David Mosberger (davidm@cs.arizona.edu)
* David Miller (davem@redhat.com)
- *
- * Support routines for initializing a PCI subsystem.
*/
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index 365447240d95..d8ca40a97693 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -1,18 +1,14 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * drivers/pci/setup-res.c
+ * Support routines for initializing a PCI subsystem
*
* Extruded from code written by
* Dave Rusling (david.rusling@reo.mts.dec.com)
* David Mosberger (davidm@cs.arizona.edu)
* David Miller (davem@redhat.com)
*
- * Support routines for initializing a PCI subsystem.
- */
-
-/* fixed for multiple pci buses, 1999 Andrea Arcangeli <andrea@suse.de> */
-
-/*
+ * Fixed for multiple PCI buses, 1999 Andrea Arcangeli <andrea@suse.de>
+ *
* Nov 2000, Ivan Kokshaysky <ink@jurassic.park.msu.ru>
* Resource sorting
*/
@@ -172,8 +168,6 @@ EXPORT_SYMBOL(pci_claim_resource);
void pci_disable_bridge_window(struct pci_dev *dev)
{
- pci_info(dev, "disabling bridge mem windows\n");
-
/* MMIO Base/Limit */
pci_write_config_dword(dev, PCI_MEMORY_BASE, 0x0000fff0);
diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c
index d10f556dc03e..e634229ece89 100644
--- a/drivers/pci/slot.c
+++ b/drivers/pci/slot.c
@@ -1,6 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * drivers/pci/slot.c
* Copyright (C) 2006 Matthew Wilcox <matthew@wil.cx>
* Copyright (C) 2006-2009 Hewlett-Packard Development Company, L.P.
* Alex Chiang <achiang@hp.com>
@@ -76,6 +75,7 @@ static const char *pci_bus_speed_strings[] = {
"2.5 GT/s PCIe", /* 0x14 */
"5.0 GT/s PCIe", /* 0x15 */
"8.0 GT/s PCIe", /* 0x16 */
+ "16.0 GT/s PCIe", /* 0x17 */
};
static ssize_t bus_speed_read(enum pci_bus_speed speed, char *buf)
diff --git a/drivers/pci/syscall.c b/drivers/pci/syscall.c
index e725f99b5479..d96626c614f5 100644
--- a/drivers/pci/syscall.c
+++ b/drivers/pci/syscall.c
@@ -1,11 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * pci_syscall.c
- *
- * For architectures where we want to allow direct access
- * to the PCI config stuff - it would probably be preferable
- * on PCs too, but there people just do it by hand with the
- * magic northbridge registers..
+ * For architectures where we want to allow direct access to the PCI config
+ * stuff - it would probably be preferable on PCs too, but there people
+ * just do it by hand with the magic northbridge registers.
*/
#include <linux/errno.h>
diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c
index 70fba57d6103..8617565ba561 100644
--- a/drivers/pci/vpd.c
+++ b/drivers/pci/vpd.c
@@ -1,13 +1,465 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * File: vpd.c
- * Purpose: Provide PCI VPD support
+ * PCI VPD support
*
* Copyright (C) 2010 Broadcom Corporation.
*/
#include <linux/pci.h>
+#include <linux/delay.h>
#include <linux/export.h>
+#include <linux/sched/signal.h>
+#include "pci.h"
+
+/* VPD access through PCI 2.2+ VPD capability */
+
+struct pci_vpd_ops {
+ ssize_t (*read)(struct pci_dev *dev, loff_t pos, size_t count, void *buf);
+ ssize_t (*write)(struct pci_dev *dev, loff_t pos, size_t count, const void *buf);
+ int (*set_size)(struct pci_dev *dev, size_t len);
+};
+
+struct pci_vpd {
+ const struct pci_vpd_ops *ops;
+ struct bin_attribute *attr; /* Descriptor for sysfs VPD entry */
+ struct mutex lock;
+ unsigned int len;
+ u16 flag;
+ u8 cap;
+ unsigned int busy:1;
+ unsigned int valid:1;
+};
+
+/**
+ * pci_read_vpd - Read one entry from Vital Product Data
+ * @dev: pci device struct
+ * @pos: offset in vpd space
+ * @count: number of bytes to read
+ * @buf: pointer to where to store result
+ */
+ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf)
+{
+ if (!dev->vpd || !dev->vpd->ops)
+ return -ENODEV;
+ return dev->vpd->ops->read(dev, pos, count, buf);
+}
+EXPORT_SYMBOL(pci_read_vpd);
+
+/**
+ * pci_write_vpd - Write entry to Vital Product Data
+ * @dev: pci device struct
+ * @pos: offset in vpd space
+ * @count: number of bytes to write
+ * @buf: buffer containing write data
+ */
+ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf)
+{
+ if (!dev->vpd || !dev->vpd->ops)
+ return -ENODEV;
+ return dev->vpd->ops->write(dev, pos, count, buf);
+}
+EXPORT_SYMBOL(pci_write_vpd);
+
+/**
+ * pci_set_vpd_size - Set size of Vital Product Data space
+ * @dev: pci device struct
+ * @len: size of vpd space
+ */
+int pci_set_vpd_size(struct pci_dev *dev, size_t len)
+{
+ if (!dev->vpd || !dev->vpd->ops)
+ return -ENODEV;
+ return dev->vpd->ops->set_size(dev, len);
+}
+EXPORT_SYMBOL(pci_set_vpd_size);
+
+#define PCI_VPD_MAX_SIZE (PCI_VPD_ADDR_MASK + 1)
+
+/**
+ * pci_vpd_size - determine actual size of Vital Product Data
+ * @dev: pci device struct
+ * @old_size: current assumed size, also maximum allowed size
+ */
+static size_t pci_vpd_size(struct pci_dev *dev, size_t old_size)
+{
+ size_t off = 0;
+ unsigned char header[1+2]; /* 1 byte tag, 2 bytes length */
+
+ while (off < old_size &&
+ pci_read_vpd(dev, off, 1, header) == 1) {
+ unsigned char tag;
+
+ if (header[0] & PCI_VPD_LRDT) {
+ /* Large Resource Data Type Tag */
+ tag = pci_vpd_lrdt_tag(header);
+ /* Only read length from known tag items */
+ if ((tag == PCI_VPD_LTIN_ID_STRING) ||
+ (tag == PCI_VPD_LTIN_RO_DATA) ||
+ (tag == PCI_VPD_LTIN_RW_DATA)) {
+ if (pci_read_vpd(dev, off+1, 2,
+ &header[1]) != 2) {
+ pci_warn(dev, "invalid large VPD tag %02x size at offset %zu",
+ tag, off + 1);
+ return 0;
+ }
+ off += PCI_VPD_LRDT_TAG_SIZE +
+ pci_vpd_lrdt_size(header);
+ }
+ } else {
+ /* Short Resource Data Type Tag */
+ off += PCI_VPD_SRDT_TAG_SIZE +
+ pci_vpd_srdt_size(header);
+ tag = pci_vpd_srdt_tag(header);
+ }
+
+ if (tag == PCI_VPD_STIN_END) /* End tag descriptor */
+ return off;
+
+ if ((tag != PCI_VPD_LTIN_ID_STRING) &&
+ (tag != PCI_VPD_LTIN_RO_DATA) &&
+ (tag != PCI_VPD_LTIN_RW_DATA)) {
+ pci_warn(dev, "invalid %s VPD tag %02x at offset %zu",
+ (header[0] & PCI_VPD_LRDT) ? "large" : "short",
+ tag, off);
+ return 0;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Wait for last operation to complete.
+ * This code has to spin since there is no other notification from the PCI
+ * hardware. Since the VPD is often implemented by serial attachment to an
+ * EEPROM, it may take many milliseconds to complete.
+ *
+ * Returns 0 on success, negative values indicate error.
+ */
+static int pci_vpd_wait(struct pci_dev *dev)
+{
+ struct pci_vpd *vpd = dev->vpd;
+ unsigned long timeout = jiffies + msecs_to_jiffies(125);
+ unsigned long max_sleep = 16;
+ u16 status;
+ int ret;
+
+ if (!vpd->busy)
+ return 0;
+
+ while (time_before(jiffies, timeout)) {
+ ret = pci_user_read_config_word(dev, vpd->cap + PCI_VPD_ADDR,
+ &status);
+ if (ret < 0)
+ return ret;
+
+ if ((status & PCI_VPD_ADDR_F) == vpd->flag) {
+ vpd->busy = 0;
+ return 0;
+ }
+
+ if (fatal_signal_pending(current))
+ return -EINTR;
+
+ usleep_range(10, max_sleep);
+ if (max_sleep < 1024)
+ max_sleep *= 2;
+ }
+
+ pci_warn(dev, "VPD access failed. This is likely a firmware bug on this device. Contact the card vendor for a firmware update\n");
+ return -ETIMEDOUT;
+}
+
+static ssize_t pci_vpd_read(struct pci_dev *dev, loff_t pos, size_t count,
+ void *arg)
+{
+ struct pci_vpd *vpd = dev->vpd;
+ int ret;
+ loff_t end = pos + count;
+ u8 *buf = arg;
+
+ if (pos < 0)
+ return -EINVAL;
+
+ if (!vpd->valid) {
+ vpd->valid = 1;
+ vpd->len = pci_vpd_size(dev, vpd->len);
+ }
+
+ if (vpd->len == 0)
+ return -EIO;
+
+ if (pos > vpd->len)
+ return 0;
+
+ if (end > vpd->len) {
+ end = vpd->len;
+ count = end - pos;
+ }
+
+ if (mutex_lock_killable(&vpd->lock))
+ return -EINTR;
+
+ ret = pci_vpd_wait(dev);
+ if (ret < 0)
+ goto out;
+
+ while (pos < end) {
+ u32 val;
+ unsigned int i, skip;
+
+ ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR,
+ pos & ~3);
+ if (ret < 0)
+ break;
+ vpd->busy = 1;
+ vpd->flag = PCI_VPD_ADDR_F;
+ ret = pci_vpd_wait(dev);
+ if (ret < 0)
+ break;
+
+ ret = pci_user_read_config_dword(dev, vpd->cap + PCI_VPD_DATA, &val);
+ if (ret < 0)
+ break;
+
+ skip = pos & 3;
+ for (i = 0; i < sizeof(u32); i++) {
+ if (i >= skip) {
+ *buf++ = val;
+ if (++pos == end)
+ break;
+ }
+ val >>= 8;
+ }
+ }
+out:
+ mutex_unlock(&vpd->lock);
+ return ret ? ret : count;
+}
+
+static ssize_t pci_vpd_write(struct pci_dev *dev, loff_t pos, size_t count,
+ const void *arg)
+{
+ struct pci_vpd *vpd = dev->vpd;
+ const u8 *buf = arg;
+ loff_t end = pos + count;
+ int ret = 0;
+
+ if (pos < 0 || (pos & 3) || (count & 3))
+ return -EINVAL;
+
+ if (!vpd->valid) {
+ vpd->valid = 1;
+ vpd->len = pci_vpd_size(dev, vpd->len);
+ }
+
+ if (vpd->len == 0)
+ return -EIO;
+
+ if (end > vpd->len)
+ return -EINVAL;
+
+ if (mutex_lock_killable(&vpd->lock))
+ return -EINTR;
+
+ ret = pci_vpd_wait(dev);
+ if (ret < 0)
+ goto out;
+
+ while (pos < end) {
+ u32 val;
+
+ val = *buf++;
+ val |= *buf++ << 8;
+ val |= *buf++ << 16;
+ val |= *buf++ << 24;
+
+ ret = pci_user_write_config_dword(dev, vpd->cap + PCI_VPD_DATA, val);
+ if (ret < 0)
+ break;
+ ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR,
+ pos | PCI_VPD_ADDR_F);
+ if (ret < 0)
+ break;
+
+ vpd->busy = 1;
+ vpd->flag = 0;
+ ret = pci_vpd_wait(dev);
+ if (ret < 0)
+ break;
+
+ pos += sizeof(u32);
+ }
+out:
+ mutex_unlock(&vpd->lock);
+ return ret ? ret : count;
+}
+
+static int pci_vpd_set_size(struct pci_dev *dev, size_t len)
+{
+ struct pci_vpd *vpd = dev->vpd;
+
+ if (len == 0 || len > PCI_VPD_MAX_SIZE)
+ return -EIO;
+
+ vpd->valid = 1;
+ vpd->len = len;
+
+ return 0;
+}
+
+static const struct pci_vpd_ops pci_vpd_ops = {
+ .read = pci_vpd_read,
+ .write = pci_vpd_write,
+ .set_size = pci_vpd_set_size,
+};
+
+static ssize_t pci_vpd_f0_read(struct pci_dev *dev, loff_t pos, size_t count,
+ void *arg)
+{
+ struct pci_dev *tdev = pci_get_slot(dev->bus,
+ PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
+ ssize_t ret;
+
+ if (!tdev)
+ return -ENODEV;
+
+ ret = pci_read_vpd(tdev, pos, count, arg);
+ pci_dev_put(tdev);
+ return ret;
+}
+
+static ssize_t pci_vpd_f0_write(struct pci_dev *dev, loff_t pos, size_t count,
+ const void *arg)
+{
+ struct pci_dev *tdev = pci_get_slot(dev->bus,
+ PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
+ ssize_t ret;
+
+ if (!tdev)
+ return -ENODEV;
+
+ ret = pci_write_vpd(tdev, pos, count, arg);
+ pci_dev_put(tdev);
+ return ret;
+}
+
+static int pci_vpd_f0_set_size(struct pci_dev *dev, size_t len)
+{
+ struct pci_dev *tdev = pci_get_slot(dev->bus,
+ PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
+ int ret;
+
+ if (!tdev)
+ return -ENODEV;
+
+ ret = pci_set_vpd_size(tdev, len);
+ pci_dev_put(tdev);
+ return ret;
+}
+
+static const struct pci_vpd_ops pci_vpd_f0_ops = {
+ .read = pci_vpd_f0_read,
+ .write = pci_vpd_f0_write,
+ .set_size = pci_vpd_f0_set_size,
+};
+
+int pci_vpd_init(struct pci_dev *dev)
+{
+ struct pci_vpd *vpd;
+ u8 cap;
+
+ cap = pci_find_capability(dev, PCI_CAP_ID_VPD);
+ if (!cap)
+ return -ENODEV;
+
+ vpd = kzalloc(sizeof(*vpd), GFP_ATOMIC);
+ if (!vpd)
+ return -ENOMEM;
+
+ vpd->len = PCI_VPD_MAX_SIZE;
+ if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0)
+ vpd->ops = &pci_vpd_f0_ops;
+ else
+ vpd->ops = &pci_vpd_ops;
+ mutex_init(&vpd->lock);
+ vpd->cap = cap;
+ vpd->busy = 0;
+ vpd->valid = 0;
+ dev->vpd = vpd;
+ return 0;
+}
+
+void pci_vpd_release(struct pci_dev *dev)
+{
+ kfree(dev->vpd);
+}
+
+static ssize_t read_vpd_attr(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj));
+
+ if (bin_attr->size > 0) {
+ if (off > bin_attr->size)
+ count = 0;
+ else if (count > bin_attr->size - off)
+ count = bin_attr->size - off;
+ }
+
+ return pci_read_vpd(dev, off, count, buf);
+}
+
+static ssize_t write_vpd_attr(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj));
+
+ if (bin_attr->size > 0) {
+ if (off > bin_attr->size)
+ count = 0;
+ else if (count > bin_attr->size - off)
+ count = bin_attr->size - off;
+ }
+
+ return pci_write_vpd(dev, off, count, buf);
+}
+
+void pcie_vpd_create_sysfs_dev_files(struct pci_dev *dev)
+{
+ int retval;
+ struct bin_attribute *attr;
+
+ if (!dev->vpd)
+ return;
+
+ attr = kzalloc(sizeof(*attr), GFP_ATOMIC);
+ if (!attr)
+ return;
+
+ sysfs_bin_attr_init(attr);
+ attr->size = 0;
+ attr->attr.name = "vpd";
+ attr->attr.mode = S_IRUSR | S_IWUSR;
+ attr->read = read_vpd_attr;
+ attr->write = write_vpd_attr;
+ retval = sysfs_create_bin_file(&dev->dev.kobj, attr);
+ if (retval) {
+ kfree(attr);
+ return;
+ }
+
+ dev->vpd->attr = attr;
+}
+
+void pcie_vpd_remove_sysfs_dev_files(struct pci_dev *dev)
+{
+ if (dev->vpd && dev->vpd->attr) {
+ sysfs_remove_bin_file(&dev->dev.kobj, dev->vpd->attr);
+ kfree(dev->vpd->attr);
+ }
+}
int pci_vpd_find_tag(const u8 *buf, unsigned int off, unsigned int len, u8 rdt)
{
@@ -61,3 +513,132 @@ int pci_vpd_find_info_keyword(const u8 *buf, unsigned int off,
return -ENOENT;
}
EXPORT_SYMBOL_GPL(pci_vpd_find_info_keyword);
+
+#ifdef CONFIG_PCI_QUIRKS
+/*
+ * Quirk non-zero PCI functions to route VPD access through function 0 for
+ * devices that share VPD resources between functions. The functions are
+ * expected to be identical devices.
+ */
+static void quirk_f0_vpd_link(struct pci_dev *dev)
+{
+ struct pci_dev *f0;
+
+ if (!PCI_FUNC(dev->devfn))
+ return;
+
+ f0 = pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
+ if (!f0)
+ return;
+
+ if (f0->vpd && dev->class == f0->class &&
+ dev->vendor == f0->vendor && dev->device == f0->device)
+ dev->dev_flags |= PCI_DEV_FLAGS_VPD_REF_F0;
+
+ pci_dev_put(f0);
+}
+DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, PCI_ANY_ID,
+ PCI_CLASS_NETWORK_ETHERNET, 8, quirk_f0_vpd_link);
+
+/*
+ * If a device follows the VPD format spec, the PCI core will not read or
+ * write past the VPD End Tag. But some vendors do not follow the VPD
+ * format spec, so we can't tell how much data is safe to access. Devices
+ * may behave unpredictably if we access too much. Blacklist these devices
+ * so we don't touch VPD at all.
+ */
+static void quirk_blacklist_vpd(struct pci_dev *dev)
+{
+ if (dev->vpd) {
+ dev->vpd->len = 0;
+ pci_warn(dev, FW_BUG "disabling VPD access (can't determine size of non-standard VPD format)\n");
+ }
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0060, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x007c, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0413, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0078, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0079, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0073, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0071, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005b, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x002f, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005d, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005f, quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, PCI_ANY_ID,
+ quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_QLOGIC, 0x2261, quirk_blacklist_vpd);
+
+/*
+ * For Broadcom 5706, 5708, 5709 rev. A nics, any read beyond the
+ * VPD end tag will hang the device. This problem was initially
+ * observed when a vpd entry was created in sysfs
+ * ('/sys/bus/pci/devices/<id>/vpd'). A read to this sysfs entry
+ * will dump 32k of data. Reading a full 32k will cause an access
+ * beyond the VPD end tag causing the device to hang. Once the device
+ * is hung, the bnx2 driver will not be able to reset the device.
+ * We believe that it is legal to read beyond the end tag and
+ * therefore the solution is to limit the read/write length.
+ */
+static void quirk_brcm_570x_limit_vpd(struct pci_dev *dev)
+{
+ /*
+ * Only disable the VPD capability for 5706, 5706S, 5708,
+ * 5708S and 5709 rev. A
+ */
+ if ((dev->device == PCI_DEVICE_ID_NX2_5706) ||
+ (dev->device == PCI_DEVICE_ID_NX2_5706S) ||
+ (dev->device == PCI_DEVICE_ID_NX2_5708) ||
+ (dev->device == PCI_DEVICE_ID_NX2_5708S) ||
+ ((dev->device == PCI_DEVICE_ID_NX2_5709) &&
+ (dev->revision & 0xf0) == 0x0)) {
+ if (dev->vpd)
+ dev->vpd->len = 0x80;
+ }
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_NX2_5706,
+ quirk_brcm_570x_limit_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_NX2_5706S,
+ quirk_brcm_570x_limit_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_NX2_5708,
+ quirk_brcm_570x_limit_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_NX2_5708S,
+ quirk_brcm_570x_limit_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_NX2_5709,
+ quirk_brcm_570x_limit_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_NX2_5709S,
+ quirk_brcm_570x_limit_vpd);
+
+static void quirk_chelsio_extend_vpd(struct pci_dev *dev)
+{
+ int chip = (dev->device & 0xf000) >> 12;
+ int func = (dev->device & 0x0f00) >> 8;
+ int prod = (dev->device & 0x00ff) >> 0;
+
+ /*
+ * If this is a T3-based adapter, there's a 1KB VPD area at offset
+ * 0xc00 which contains the preferred VPD values. If this is a T4 or
+ * later based adapter, the special VPD is at offset 0x400 for the
+ * Physical Functions (the SR-IOV Virtual Functions have no VPD
+ * Capabilities). The PCI VPD Access core routines will normally
+ * compute the size of the VPD by parsing the VPD Data Structure at
+ * offset 0x000. This will result in silent failures when attempting
+ * to accesses these other VPD areas which are beyond those computed
+ * limits.
+ */
+ if (chip == 0x0 && prod >= 0x20)
+ pci_set_vpd_size(dev, 8192);
+ else if (chip >= 0x4 && func < 0x8)
+ pci_set_vpd_size(dev, 2048);
+}
+
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, PCI_ANY_ID,
+ quirk_chelsio_extend_vpd);
+
+#endif
diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c
index 8785014f656e..eba6e33147a2 100644
--- a/drivers/pci/xen-pcifront.c
+++ b/drivers/pci/xen-pcifront.c
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Xen PCI Frontend.
+ * Xen PCI Frontend
*
- * Author: Ryan Wilson <hap9@epoch.ncsc.mil>
+ * Author: Ryan Wilson <hap9@epoch.ncsc.mil>
*/
#include <linux/module.h>
#include <linux/init.h>
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig
index d3c378b4db6c..cbbe4a285b48 100644
--- a/drivers/pcmcia/Kconfig
+++ b/drivers/pcmcia/Kconfig
@@ -232,25 +232,6 @@ config PCMCIA_PROBE
bool
default y if ISA && !ARCH_SA1100 && !PARISC
-config M32R_PCC
- bool "M32R PCMCIA I/F"
- depends on M32R && CHIP_M32700 && PCMCIA
- help
- Say Y here to use the M32R PCMCIA controller.
-
-config M32R_CFC
- bool "M32R CF I/F Controller"
- depends on M32R && (PLAT_USRV || PLAT_M32700UT || PLAT_MAPPI2 || PLAT_MAPPI3 || PLAT_OPSPUT)
- help
- Say Y here to use the M32R CompactFlash controller.
-
-config M32R_CFC_NUM
- int "M32R CF I/F number"
- depends on M32R_CFC
- default "1" if PLAT_USRV || PLAT_M32700UT || PLAT_MAPPI2 || PLAT_MAPPI3 || PLAT_OPSPUT
- help
- Set the number of M32R CF slots.
-
config PCMCIA_VRC4171
tristate "NEC VRC4171 Card Controllers support"
depends on CPU_VR41XX && ISA && PCMCIA
@@ -266,13 +247,6 @@ config OMAP_CF
Say Y here to support the CompactFlash controller on OMAP.
Note that this doesn't support "True IDE" mode.
-config BFIN_CFPCMCIA
- tristate "Blackfin CompactFlash PCMCIA Driver"
- depends on PCMCIA && BLACKFIN
- help
- Say Y here to support the CompactFlash PCMCIA driver for Blackfin.
-
-
config AT91_CF
tristate "AT91 CompactFlash Controller"
depends on PCI
diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile
index e7dae16b9a43..28502bd159e0 100644
--- a/drivers/pcmcia/Makefile
+++ b/drivers/pcmcia/Makefile
@@ -28,13 +28,10 @@ obj-$(CONFIG_PCMCIA_SOC_COMMON) += soc_common.o
obj-$(CONFIG_PCMCIA_SA11XX_BASE) += sa11xx_base.o
obj-$(CONFIG_PCMCIA_SA1100) += sa1100_cs.o
obj-$(CONFIG_PCMCIA_SA1111) += sa1111_cs.o
-obj-$(CONFIG_M32R_PCC) += m32r_pcc.o
-obj-$(CONFIG_M32R_CFC) += m32r_cfc.o
obj-$(CONFIG_PCMCIA_BCM63XX) += bcm63xx_pcmcia.o
obj-$(CONFIG_PCMCIA_VRC4171) += vrc4171_card.o
obj-$(CONFIG_PCMCIA_VRC4173) += vrc4173_cardu.o
obj-$(CONFIG_OMAP_CF) += omap_cf.o
-obj-$(CONFIG_BFIN_CFPCMCIA) += bfin_cf_pcmcia.o
obj-$(CONFIG_AT91_CF) += at91_cf.o
obj-$(CONFIG_ELECTRA_CF) += electra_cf.o
obj-$(CONFIG_PCMCIA_ALCHEMY_DEVBOARD) += db1xxx_ss.o
@@ -46,13 +43,9 @@ sa1111_cs-$(CONFIG_SA1100_JORNADA720) += sa1111_jornada720.o
sa1111_cs-$(CONFIG_ARCH_LUBBOCK) += sa1111_lubbock.o
sa1100_cs-y += sa1100_generic.o
-sa1100_cs-$(CONFIG_SA1100_ASSABET) += sa1100_assabet.o
-sa1100_cs-$(CONFIG_SA1100_CERF) += sa1100_cerf.o
sa1100_cs-$(CONFIG_SA1100_COLLIE) += pxa2xx_sharpsl.o
sa1100_cs-$(CONFIG_SA1100_H3100) += sa1100_h3600.o
sa1100_cs-$(CONFIG_SA1100_H3600) += sa1100_h3600.o
-sa1100_cs-$(CONFIG_SA1100_NANOENGINE) += sa1100_nanoengine.o
-sa1100_cs-$(CONFIG_SA1100_SHANNON) += sa1100_shannon.o
sa1100_cs-$(CONFIG_SA1100_SIMPAD) += sa1100_simpad.o
pxa2xx_cm_x2xx_cs-y += pxa2xx_cm_x2xx.o pxa2xx_cm_x255.o pxa2xx_cm_x270.o
diff --git a/drivers/pcmcia/bfin_cf_pcmcia.c b/drivers/pcmcia/bfin_cf_pcmcia.c
deleted file mode 100644
index 00a296d431ba..000000000000
--- a/drivers/pcmcia/bfin_cf_pcmcia.c
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- * file: drivers/pcmcia/bfin_cf.c
- *
- * based on: drivers/pcmcia/omap_cf.c
- * omap_cf.c -- OMAP 16xx CompactFlash controller driver
- *
- * Copyright (c) 2005 David Brownell
- * Copyright (c) 2006-2008 Michael Hennerich Analog Devices Inc.
- *
- * bugs: enter bugs at http://blackfin.uclinux.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, 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; see the file copying.
- * if not, write to the free software foundation,
- * 59 temple place - suite 330, boston, ma 02111-1307, usa.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/platform_device.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/io.h>
-#include <linux/gpio.h>
-
-#include <pcmcia/ss.h>
-#include <pcmcia/cisreg.h>
-
-#define SZ_1K 0x00000400
-#define SZ_8K 0x00002000
-#define SZ_2K (2 * SZ_1K)
-
-#define POLL_INTERVAL (2 * HZ)
-
-#define CF_ATASEL_ENA 0x20311802 /* Inverts RESET */
-#define CF_ATASEL_DIS 0x20311800
-
-#define bfin_cf_present(pfx) (gpio_get_value(pfx))
-
-/*--------------------------------------------------------------------------*/
-
-static const char driver_name[] = "bfin_cf_pcmcia";
-
-struct bfin_cf_socket {
- struct pcmcia_socket socket;
-
- struct timer_list timer;
- unsigned present:1;
- unsigned active:1;
-
- struct platform_device *pdev;
- unsigned long phys_cf_io;
- unsigned long phys_cf_attr;
- u_int irq;
- u_short cd_pfx;
-};
-
-/*--------------------------------------------------------------------------*/
-static int bfin_cf_reset(void)
-{
- outw(0, CF_ATASEL_ENA);
- mdelay(200);
- outw(0, CF_ATASEL_DIS);
-
- return 0;
-}
-
-static int bfin_cf_ss_init(struct pcmcia_socket *s)
-{
- return 0;
-}
-
-/* the timer is primarily to kick this socket's pccardd */
-static void bfin_cf_timer(struct timer_list *t)
-{
- struct bfin_cf_socket *cf = from_timer(cf, t, timer);
- unsigned short present = bfin_cf_present(cf->cd_pfx);
-
- if (present != cf->present) {
- cf->present = present;
- dev_dbg(&cf->pdev->dev, ": card %s\n",
- present ? "present" : "gone");
- pcmcia_parse_events(&cf->socket, SS_DETECT);
- }
-
- if (cf->active)
- mod_timer(&cf->timer, jiffies + POLL_INTERVAL);
-}
-
-static int bfin_cf_get_status(struct pcmcia_socket *s, u_int *sp)
-{
- struct bfin_cf_socket *cf;
-
- if (!sp)
- return -EINVAL;
-
- cf = container_of(s, struct bfin_cf_socket, socket);
-
- if (bfin_cf_present(cf->cd_pfx)) {
- *sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD;
- s->pcmcia_irq = 0;
- s->pci_irq = cf->irq;
-
- } else
- *sp = 0;
- return 0;
-}
-
-static int
-bfin_cf_set_socket(struct pcmcia_socket *sock, struct socket_state_t *s)
-{
-
- struct bfin_cf_socket *cf;
- cf = container_of(sock, struct bfin_cf_socket, socket);
-
- switch (s->Vcc) {
- case 0:
- case 33:
- break;
- case 50:
- break;
- default:
- return -EINVAL;
- }
-
- if (s->flags & SS_RESET) {
- disable_irq(cf->irq);
- bfin_cf_reset();
- enable_irq(cf->irq);
- }
-
- dev_dbg(&cf->pdev->dev, ": Vcc %d, io_irq %d, flags %04x csc %04x\n",
- s->Vcc, s->io_irq, s->flags, s->csc_mask);
-
- return 0;
-}
-
-static int bfin_cf_ss_suspend(struct pcmcia_socket *s)
-{
- return bfin_cf_set_socket(s, &dead_socket);
-}
-
-/* regions are 2K each: mem, attrib, io (and reserved-for-ide) */
-
-static int bfin_cf_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
-{
- struct bfin_cf_socket *cf;
-
- cf = container_of(s, struct bfin_cf_socket, socket);
- io->flags &= MAP_ACTIVE | MAP_ATTRIB | MAP_16BIT;
- io->start = cf->phys_cf_io;
- io->stop = io->start + SZ_2K - 1;
- return 0;
-}
-
-static int
-bfin_cf_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *map)
-{
- struct bfin_cf_socket *cf;
-
- if (map->card_start)
- return -EINVAL;
- cf = container_of(s, struct bfin_cf_socket, socket);
- map->static_start = cf->phys_cf_io;
- map->flags &= MAP_ACTIVE | MAP_ATTRIB | MAP_16BIT;
- if (map->flags & MAP_ATTRIB)
- map->static_start = cf->phys_cf_attr;
-
- return 0;
-}
-
-static struct pccard_operations bfin_cf_ops = {
- .init = bfin_cf_ss_init,
- .suspend = bfin_cf_ss_suspend,
- .get_status = bfin_cf_get_status,
- .set_socket = bfin_cf_set_socket,
- .set_io_map = bfin_cf_set_io_map,
- .set_mem_map = bfin_cf_set_mem_map,
-};
-
-/*--------------------------------------------------------------------------*/
-
-static int bfin_cf_probe(struct platform_device *pdev)
-{
- struct bfin_cf_socket *cf;
- struct resource *io_mem, *attr_mem;
- int irq;
- unsigned short cd_pfx;
- int status = 0;
-
- dev_info(&pdev->dev, "Blackfin CompactFlash/PCMCIA Socket Driver\n");
-
- irq = platform_get_irq(pdev, 0);
- if (irq <= 0)
- return -EINVAL;
-
- cd_pfx = platform_get_irq(pdev, 1); /*Card Detect GPIO PIN */
-
- if (gpio_request(cd_pfx, "pcmcia: CD")) {
- dev_err(&pdev->dev,
- "Failed ro request Card Detect GPIO_%d\n",
- cd_pfx);
- return -EBUSY;
- }
- gpio_direction_input(cd_pfx);
-
- cf = kzalloc(sizeof *cf, GFP_KERNEL);
- if (!cf) {
- gpio_free(cd_pfx);
- return -ENOMEM;
- }
-
- cf->cd_pfx = cd_pfx;
-
- timer_setup(&cf->timer, bfin_cf_timer, 0);
-
- cf->pdev = pdev;
- platform_set_drvdata(pdev, cf);
-
- cf->irq = irq;
- cf->socket.pci_irq = irq;
-
- irq_set_irq_type(irq, IRQF_TRIGGER_LOW);
-
- io_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- attr_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-
- if (!io_mem || !attr_mem)
- goto fail0;
-
- cf->phys_cf_io = io_mem->start;
- cf->phys_cf_attr = attr_mem->start;
-
- /* pcmcia layer only remaps "real" memory */
- cf->socket.io_offset = (unsigned long)
- ioremap(cf->phys_cf_io, SZ_2K);
-
- if (!cf->socket.io_offset)
- goto fail0;
-
- dev_err(&pdev->dev, ": on irq %d\n", irq);
-
- dev_dbg(&pdev->dev, ": %s\n",
- bfin_cf_present(cf->cd_pfx) ? "present" : "(not present)");
-
- cf->socket.owner = THIS_MODULE;
- cf->socket.dev.parent = &pdev->dev;
- cf->socket.ops = &bfin_cf_ops;
- cf->socket.resource_ops = &pccard_static_ops;
- cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP
- | SS_CAP_MEM_ALIGN;
- cf->socket.map_size = SZ_2K;
-
- status = pcmcia_register_socket(&cf->socket);
- if (status < 0)
- goto fail2;
-
- cf->active = 1;
- mod_timer(&cf->timer, jiffies + POLL_INTERVAL);
- return 0;
-
-fail2:
- iounmap((void __iomem *)cf->socket.io_offset);
- release_mem_region(cf->phys_cf_io, SZ_8K);
-
-fail0:
- gpio_free(cf->cd_pfx);
- kfree(cf);
- platform_set_drvdata(pdev, NULL);
-
- return status;
-}
-
-static int bfin_cf_remove(struct platform_device *pdev)
-{
- struct bfin_cf_socket *cf = platform_get_drvdata(pdev);
-
- gpio_free(cf->cd_pfx);
- cf->active = 0;
- pcmcia_unregister_socket(&cf->socket);
- del_timer_sync(&cf->timer);
- iounmap((void __iomem *)cf->socket.io_offset);
- release_mem_region(cf->phys_cf_io, SZ_8K);
- platform_set_drvdata(pdev, NULL);
- kfree(cf);
- return 0;
-}
-
-static struct platform_driver bfin_cf_driver = {
- .driver = {
- .name = driver_name,
- },
- .probe = bfin_cf_probe,
- .remove = bfin_cf_remove,
-};
-
-module_platform_driver(bfin_cf_driver);
-
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
-MODULE_DESCRIPTION("BFIN CF/PCMCIA Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c
index c3b615c94b4b..8c8caec3a72c 100644
--- a/drivers/pcmcia/cs.c
+++ b/drivers/pcmcia/cs.c
@@ -452,17 +452,20 @@ static int socket_insert(struct pcmcia_socket *skt)
static int socket_suspend(struct pcmcia_socket *skt)
{
- if (skt->state & SOCKET_SUSPEND)
+ if ((skt->state & SOCKET_SUSPEND) && !(skt->state & SOCKET_IN_RESUME))
return -EBUSY;
mutex_lock(&skt->ops_mutex);
- skt->suspended_state = skt->state;
+ /* store state on first suspend, but not after spurious wakeups */
+ if (!(skt->state & SOCKET_IN_RESUME))
+ skt->suspended_state = skt->state;
skt->socket = dead_socket;
skt->ops->set_socket(skt, &skt->socket);
if (skt->ops->suspend)
skt->ops->suspend(skt);
skt->state |= SOCKET_SUSPEND;
+ skt->state &= ~SOCKET_IN_RESUME;
mutex_unlock(&skt->ops_mutex);
return 0;
}
@@ -475,6 +478,7 @@ static int socket_early_resume(struct pcmcia_socket *skt)
skt->ops->set_socket(skt, &skt->socket);
if (skt->state & SOCKET_PRESENT)
skt->resume_status = socket_setup(skt, resume_delay);
+ skt->state |= SOCKET_IN_RESUME;
mutex_unlock(&skt->ops_mutex);
return 0;
}
@@ -484,7 +488,7 @@ static int socket_late_resume(struct pcmcia_socket *skt)
int ret = 0;
mutex_lock(&skt->ops_mutex);
- skt->state &= ~SOCKET_SUSPEND;
+ skt->state &= ~(SOCKET_SUSPEND | SOCKET_IN_RESUME);
mutex_unlock(&skt->ops_mutex);
if (!(skt->state & SOCKET_PRESENT)) {
diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h
index 6765beadea95..03ec43802909 100644
--- a/drivers/pcmcia/cs_internal.h
+++ b/drivers/pcmcia/cs_internal.h
@@ -70,6 +70,7 @@ struct pccard_resource_ops {
/* Flags in socket state */
#define SOCKET_PRESENT 0x0008
#define SOCKET_INUSE 0x0010
+#define SOCKET_IN_RESUME 0x0040
#define SOCKET_SUSPEND 0x0080
#define SOCKET_WIN_REQ(i) (0x0100<<(i))
#define SOCKET_CARDBUS 0x8000
diff --git a/drivers/pcmcia/m32r_cfc.c b/drivers/pcmcia/m32r_cfc.c
deleted file mode 100644
index 9a4940e56e2f..000000000000
--- a/drivers/pcmcia/m32r_cfc.c
+++ /dev/null
@@ -1,786 +0,0 @@
-/*
- * drivers/pcmcia/m32r_cfc.c
- *
- * Device driver for the CFC functionality of M32R.
- *
- * Copyright (c) 2001, 2002, 2003, 2004
- * Hiroyuki Kondo, Naoto Sugai, Hayato Fujiwara
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/timer.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/workqueue.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/bitops.h>
-#include <asm/irq.h>
-#include <asm/io.h>
-
-#include <pcmcia/ss.h>
-
-#undef MAX_IO_WIN /* FIXME */
-#define MAX_IO_WIN 1
-#undef MAX_WIN /* FIXME */
-#define MAX_WIN 1
-
-#include "m32r_cfc.h"
-
-/* Poll status interval -- 0 means default to interrupt */
-static int poll_interval = 0;
-
-typedef enum pcc_space { as_none = 0, as_comm, as_attr, as_io } pcc_as_t;
-
-typedef struct pcc_socket {
- u_short type, flags;
- struct pcmcia_socket socket;
- unsigned int number;
- unsigned int ioaddr;
- u_long mapaddr;
- u_long base; /* PCC register base */
- u_char cs_irq1, cs_irq2, intr;
- pccard_io_map io_map[MAX_IO_WIN];
- pccard_mem_map mem_map[MAX_WIN];
- u_char io_win;
- u_char mem_win;
- pcc_as_t current_space;
- u_char last_iodbex;
-#ifdef CONFIG_PROC_FS
- struct proc_dir_entry *proc;
-#endif
-} pcc_socket_t;
-
-static int pcc_sockets = 0;
-static pcc_socket_t socket[M32R_MAX_PCC] = {
- { 0, }, /* ... */
-};
-
-/*====================================================================*/
-
-static unsigned int pcc_get(u_short, unsigned int);
-static void pcc_set(u_short, unsigned int , unsigned int );
-
-static DEFINE_SPINLOCK(pcc_lock);
-
-#if !defined(CONFIG_PLAT_USRV)
-static inline u_long pcc_port2addr(unsigned long port, int size) {
- u_long addr = 0;
- u_long odd;
-
- if (size == 1) { /* byte access */
- odd = (port&1) << 11;
- port -= port & 1;
- addr = CFC_IO_MAPBASE_BYTE - CFC_IOPORT_BASE + odd + port;
- } else if (size == 2)
- addr = CFC_IO_MAPBASE_WORD - CFC_IOPORT_BASE + port;
-
- return addr;
-}
-#else /* CONFIG_PLAT_USRV */
-static inline u_long pcc_port2addr(unsigned long port, int size) {
- u_long odd;
- u_long addr = ((port - CFC_IOPORT_BASE) & 0xf000) << 8;
-
- if (size == 1) { /* byte access */
- odd = port & 1;
- port -= odd;
- odd <<= 11;
- addr = (addr | CFC_IO_MAPBASE_BYTE) + odd + (port & 0xfff);
- } else if (size == 2) /* word access */
- addr = (addr | CFC_IO_MAPBASE_WORD) + (port & 0xfff);
-
- return addr;
-}
-#endif /* CONFIG_PLAT_USRV */
-
-void pcc_ioread_byte(int sock, unsigned long port, void *buf, size_t size,
- size_t nmemb, int flag)
-{
- u_long addr;
- unsigned char *bp = (unsigned char *)buf;
- unsigned long flags;
-
- pr_debug("m32r_cfc: pcc_ioread_byte: sock=%d, port=%#lx, buf=%p, "
- "size=%u, nmemb=%d, flag=%d\n",
- sock, port, buf, size, nmemb, flag);
-
- addr = pcc_port2addr(port, 1);
- if (!addr) {
- printk("m32r_cfc:ioread_byte null port :%#lx\n",port);
- return;
- }
- pr_debug("m32r_cfc: pcc_ioread_byte: addr=%#lx\n", addr);
-
- spin_lock_irqsave(&pcc_lock, flags);
- /* read Byte */
- while (nmemb--)
- *bp++ = readb(addr);
- spin_unlock_irqrestore(&pcc_lock, flags);
-}
-
-void pcc_ioread_word(int sock, unsigned long port, void *buf, size_t size,
- size_t nmemb, int flag)
-{
- u_long addr;
- unsigned short *bp = (unsigned short *)buf;
- unsigned long flags;
-
- pr_debug("m32r_cfc: pcc_ioread_word: sock=%d, port=%#lx, "
- "buf=%p, size=%u, nmemb=%d, flag=%d\n",
- sock, port, buf, size, nmemb, flag);
-
- if (size != 2)
- printk("m32r_cfc: ioread_word :illigal size %u : %#lx\n", size,
- port);
- if (size == 9)
- printk("m32r_cfc: ioread_word :insw \n");
-
- addr = pcc_port2addr(port, 2);
- if (!addr) {
- printk("m32r_cfc:ioread_word null port :%#lx\n",port);
- return;
- }
- pr_debug("m32r_cfc: pcc_ioread_word: addr=%#lx\n", addr);
-
- spin_lock_irqsave(&pcc_lock, flags);
- /* read Word */
- while (nmemb--)
- *bp++ = readw(addr);
- spin_unlock_irqrestore(&pcc_lock, flags);
-}
-
-void pcc_iowrite_byte(int sock, unsigned long port, void *buf, size_t size,
- size_t nmemb, int flag)
-{
- u_long addr;
- unsigned char *bp = (unsigned char *)buf;
- unsigned long flags;
-
- pr_debug("m32r_cfc: pcc_iowrite_byte: sock=%d, port=%#lx, "
- "buf=%p, size=%u, nmemb=%d, flag=%d\n",
- sock, port, buf, size, nmemb, flag);
-
- /* write Byte */
- addr = pcc_port2addr(port, 1);
- if (!addr) {
- printk("m32r_cfc:iowrite_byte null port:%#lx\n",port);
- return;
- }
- pr_debug("m32r_cfc: pcc_iowrite_byte: addr=%#lx\n", addr);
-
- spin_lock_irqsave(&pcc_lock, flags);
- while (nmemb--)
- writeb(*bp++, addr);
- spin_unlock_irqrestore(&pcc_lock, flags);
-}
-
-void pcc_iowrite_word(int sock, unsigned long port, void *buf, size_t size,
- size_t nmemb, int flag)
-{
- u_long addr;
- unsigned short *bp = (unsigned short *)buf;
- unsigned long flags;
-
- pr_debug("m32r_cfc: pcc_iowrite_word: sock=%d, port=%#lx, "
- "buf=%p, size=%u, nmemb=%d, flag=%d\n",
- sock, port, buf, size, nmemb, flag);
-
- if(size != 2)
- printk("m32r_cfc: iowrite_word :illigal size %u : %#lx\n",
- size, port);
- if(size == 9)
- printk("m32r_cfc: iowrite_word :outsw \n");
-
- addr = pcc_port2addr(port, 2);
- if (!addr) {
- printk("m32r_cfc:iowrite_word null addr :%#lx\n",port);
- return;
- }
-#if 1
- if (addr & 1) {
- printk("m32r_cfc:iowrite_word port addr (%#lx):%#lx\n", port,
- addr);
- return;
- }
-#endif
- pr_debug("m32r_cfc: pcc_iowrite_word: addr=%#lx\n", addr);
-
- spin_lock_irqsave(&pcc_lock, flags);
- while (nmemb--)
- writew(*bp++, addr);
- spin_unlock_irqrestore(&pcc_lock, flags);
-}
-
-/*====================================================================*/
-
-#define IS_REGISTERED 0x2000
-#define IS_ALIVE 0x8000
-
-typedef struct pcc_t {
- char *name;
- u_short flags;
-} pcc_t;
-
-static pcc_t pcc[] = {
-#if !defined(CONFIG_PLAT_USRV)
- { "m32r_cfc", 0 }, { "", 0 },
-#else /* CONFIG_PLAT_USRV */
- { "m32r_cfc", 0 }, { "m32r_cfc", 0 }, { "m32r_cfc", 0 },
- { "m32r_cfc", 0 }, { "m32r_cfc", 0 }, { "", 0 },
-#endif /* CONFIG_PLAT_USRV */
-};
-
-static irqreturn_t pcc_interrupt(int, void *);
-
-/*====================================================================*/
-
-static struct timer_list poll_timer;
-
-static unsigned int pcc_get(u_short sock, unsigned int reg)
-{
- unsigned int val = inw(reg);
- pr_debug("m32r_cfc: pcc_get: reg(0x%08x)=0x%04x\n", reg, val);
- return val;
-}
-
-
-static void pcc_set(u_short sock, unsigned int reg, unsigned int data)
-{
- outw(data, reg);
- pr_debug("m32r_cfc: pcc_set: reg(0x%08x)=0x%04x\n", reg, data);
-}
-
-/*======================================================================
-
- See if a card is present, powered up, in IO mode, and already
- bound to a (non PC Card) Linux driver. We leave these alone.
-
- We make an exception for cards that seem to be serial devices.
-
-======================================================================*/
-
-static int __init is_alive(u_short sock)
-{
- unsigned int stat;
-
- pr_debug("m32r_cfc: is_alive:\n");
-
- printk("CF: ");
- stat = pcc_get(sock, (unsigned int)PLD_CFSTS);
- if (!stat)
- printk("No ");
- printk("Card is detected at socket %d : stat = 0x%08x\n", sock, stat);
- pr_debug("m32r_cfc: is_alive: sock stat is 0x%04x\n", stat);
-
- return 0;
-}
-
-static void add_pcc_socket(ulong base, int irq, ulong mapaddr,
- unsigned int ioaddr)
-{
- pcc_socket_t *t = &socket[pcc_sockets];
-
- pr_debug("m32r_cfc: add_pcc_socket: base=%#lx, irq=%d, "
- "mapaddr=%#lx, ioaddr=%08x\n",
- base, irq, mapaddr, ioaddr);
-
- /* add sockets */
- t->ioaddr = ioaddr;
- t->mapaddr = mapaddr;
-#if !defined(CONFIG_PLAT_USRV)
- t->base = 0;
- t->flags = 0;
- t->cs_irq1 = irq; // insert irq
- t->cs_irq2 = irq + 1; // eject irq
-#else /* CONFIG_PLAT_USRV */
- t->base = base;
- t->flags = 0;
- t->cs_irq1 = 0; // insert irq
- t->cs_irq2 = 0; // eject irq
-#endif /* CONFIG_PLAT_USRV */
-
- if (is_alive(pcc_sockets))
- t->flags |= IS_ALIVE;
-
- /* add pcc */
-#if !defined(CONFIG_PLAT_USRV)
- request_region((unsigned int)PLD_CFRSTCR, 0x20, "m32r_cfc");
-#else /* CONFIG_PLAT_USRV */
- {
- unsigned int reg_base;
-
- reg_base = (unsigned int)PLD_CFRSTCR;
- reg_base |= pcc_sockets << 8;
- request_region(reg_base, 0x20, "m32r_cfc");
- }
-#endif /* CONFIG_PLAT_USRV */
- printk(KERN_INFO " %s ", pcc[pcc_sockets].name);
- printk("pcc at 0x%08lx\n", t->base);
-
- /* Update socket interrupt information, capabilities */
- t->socket.features |= (SS_CAP_PCCARD | SS_CAP_STATIC_MAP);
- t->socket.map_size = M32R_PCC_MAPSIZE;
- t->socket.io_offset = ioaddr; /* use for io access offset */
- t->socket.irq_mask = 0;
-#if !defined(CONFIG_PLAT_USRV)
- t->socket.pci_irq = PLD_IRQ_CFIREQ ; /* card interrupt */
-#else /* CONFIG_PLAT_USRV */
- t->socket.pci_irq = PLD_IRQ_CF0 + pcc_sockets;
-#endif /* CONFIG_PLAT_USRV */
-
-#ifndef CONFIG_PLAT_USRV
- /* insert interrupt */
- request_irq(irq, pcc_interrupt, 0, "m32r_cfc", pcc_interrupt);
-#ifndef CONFIG_PLAT_MAPPI3
- /* eject interrupt */
- request_irq(irq+1, pcc_interrupt, 0, "m32r_cfc", pcc_interrupt);
-#endif
- pr_debug("m32r_cfc: enable CFMSK, RDYSEL\n");
- pcc_set(pcc_sockets, (unsigned int)PLD_CFIMASK, 0x01);
-#endif /* CONFIG_PLAT_USRV */
-#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_USRV) || defined(CONFIG_PLAT_OPSPUT)
- pcc_set(pcc_sockets, (unsigned int)PLD_CFCR1, 0x0200);
-#endif
- pcc_sockets++;
-
- return;
-}
-
-
-/*====================================================================*/
-
-static irqreturn_t pcc_interrupt(int irq, void *dev)
-{
- int i;
- u_int events = 0;
- int handled = 0;
-
- pr_debug("m32r_cfc: pcc_interrupt: irq=%d, dev=%p\n", irq, dev);
- for (i = 0; i < pcc_sockets; i++) {
- if (socket[i].cs_irq1 != irq && socket[i].cs_irq2 != irq)
- continue;
-
- handled = 1;
- pr_debug("m32r_cfc: pcc_interrupt: socket %d irq 0x%02x ",
- i, irq);
- events |= SS_DETECT; /* insert or eject */
- if (events)
- pcmcia_parse_events(&socket[i].socket, events);
- }
- pr_debug("m32r_cfc: pcc_interrupt: done\n");
-
- return IRQ_RETVAL(handled);
-} /* pcc_interrupt */
-
-static void pcc_interrupt_wrapper(struct timer_list *unused)
-{
- pr_debug("m32r_cfc: pcc_interrupt_wrapper:\n");
- pcc_interrupt(0, NULL);
- poll_timer.expires = jiffies + poll_interval;
- add_timer(&poll_timer);
-}
-
-/*====================================================================*/
-
-static int _pcc_get_status(u_short sock, u_int *value)
-{
- u_int status;
-
- pr_debug("m32r_cfc: _pcc_get_status:\n");
- status = pcc_get(sock, (unsigned int)PLD_CFSTS);
- *value = (status) ? SS_DETECT : 0;
- pr_debug("m32r_cfc: _pcc_get_status: status=0x%08x\n", status);
-
-#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_USRV) || defined(CONFIG_PLAT_OPSPUT)
- if ( status ) {
- /* enable CF power */
- status = inw((unsigned int)PLD_CPCR);
- if (!(status & PLD_CPCR_CF)) {
- pr_debug("m32r_cfc: _pcc_get_status: "
- "power on (CPCR=0x%08x)\n", status);
- status |= PLD_CPCR_CF;
- outw(status, (unsigned int)PLD_CPCR);
- udelay(100);
- }
- *value |= SS_POWERON;
-
- pcc_set(sock, (unsigned int)PLD_CFBUFCR,0);/* enable buffer */
- udelay(100);
-
- *value |= SS_READY; /* always ready */
- *value |= SS_3VCARD;
- } else {
- /* disable CF power */
- status = inw((unsigned int)PLD_CPCR);
- status &= ~PLD_CPCR_CF;
- outw(status, (unsigned int)PLD_CPCR);
- udelay(100);
- pr_debug("m32r_cfc: _pcc_get_status: "
- "power off (CPCR=0x%08x)\n", status);
- }
-#elif defined(CONFIG_PLAT_MAPPI2) || defined(CONFIG_PLAT_MAPPI3)
- if ( status ) {
- status = pcc_get(sock, (unsigned int)PLD_CPCR);
- if (status == 0) { /* power off */
- pcc_set(sock, (unsigned int)PLD_CPCR, 1);
- pcc_set(sock, (unsigned int)PLD_CFBUFCR,0); /* force buffer off for ZA-36 */
- udelay(50);
- }
- *value |= SS_POWERON;
-
- pcc_set(sock, (unsigned int)PLD_CFBUFCR,0);
- udelay(50);
- pcc_set(sock, (unsigned int)PLD_CFRSTCR, 0x0101);
- udelay(25); /* for IDE reset */
- pcc_set(sock, (unsigned int)PLD_CFRSTCR, 0x0100);
- mdelay(2); /* for IDE reset */
-
- *value |= SS_READY;
- *value |= SS_3VCARD;
- } else {
- /* disable CF power */
- pcc_set(sock, (unsigned int)PLD_CPCR, 0);
- udelay(100);
- pr_debug("m32r_cfc: _pcc_get_status: "
- "power off (CPCR=0x%08x)\n", status);
- }
-#else
-#error no platform configuration
-#endif
- pr_debug("m32r_cfc: _pcc_get_status: GetStatus(%d) = %#4.4x\n",
- sock, *value);
- return 0;
-} /* _get_status */
-
-/*====================================================================*/
-
-static int _pcc_set_socket(u_short sock, socket_state_t *state)
-{
- pr_debug("m32r_cfc: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
- "io_irq %d, csc_mask %#2.2x)\n", sock, state->flags,
- state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
-
-#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_USRV) || defined(CONFIG_PLAT_OPSPUT) || defined(CONFIG_PLAT_MAPPI2) || defined(CONFIG_PLAT_MAPPI3)
- if (state->Vcc) {
- if ((state->Vcc != 50) && (state->Vcc != 33))
- return -EINVAL;
- /* accept 5V and 3.3V */
- }
-#endif
- if (state->flags & SS_RESET) {
- pr_debug(":RESET\n");
- pcc_set(sock,(unsigned int)PLD_CFRSTCR,0x101);
- }else{
- pcc_set(sock,(unsigned int)PLD_CFRSTCR,0x100);
- }
- if (state->flags & SS_OUTPUT_ENA){
- pr_debug(":OUTPUT_ENA\n");
- /* bit clear */
- pcc_set(sock,(unsigned int)PLD_CFBUFCR,0);
- } else {
- pcc_set(sock,(unsigned int)PLD_CFBUFCR,1);
- }
-
- if(state->flags & SS_IOCARD){
- pr_debug(":IOCARD");
- }
- if (state->flags & SS_PWR_AUTO) {
- pr_debug(":PWR_AUTO");
- }
- if (state->csc_mask & SS_DETECT)
- pr_debug(":csc-SS_DETECT");
- if (state->flags & SS_IOCARD) {
- if (state->csc_mask & SS_STSCHG)
- pr_debug(":STSCHG");
- } else {
- if (state->csc_mask & SS_BATDEAD)
- pr_debug(":BATDEAD");
- if (state->csc_mask & SS_BATWARN)
- pr_debug(":BATWARN");
- if (state->csc_mask & SS_READY)
- pr_debug(":READY");
- }
- pr_debug("\n");
- return 0;
-} /* _set_socket */
-
-/*====================================================================*/
-
-static int _pcc_set_io_map(u_short sock, struct pccard_io_map *io)
-{
- u_char map;
-
- pr_debug("m32r_cfc: SetIOMap(%d, %d, %#2.2x, %d ns, "
- "%#llx-%#llx)\n", sock, io->map, io->flags,
- io->speed, (unsigned long long)io->start,
- (unsigned long long)io->stop);
- map = io->map;
-
- return 0;
-} /* _set_io_map */
-
-/*====================================================================*/
-
-static int _pcc_set_mem_map(u_short sock, struct pccard_mem_map *mem)
-{
-
- u_char map = mem->map;
- u_long addr;
- pcc_socket_t *t = &socket[sock];
-
- pr_debug("m32r_cfc: SetMemMap(%d, %d, %#2.2x, %d ns, "
- "%#llx, %#x)\n", sock, map, mem->flags,
- mem->speed, (unsigned long long)mem->static_start,
- mem->card_start);
-
- /*
- * sanity check
- */
- if ((map > MAX_WIN) || (mem->card_start > 0x3ffffff)){
- return -EINVAL;
- }
-
- /*
- * de-activate
- */
- if ((mem->flags & MAP_ACTIVE) == 0) {
- t->current_space = as_none;
- return 0;
- }
-
- /*
- * Set mode
- */
- if (mem->flags & MAP_ATTRIB) {
- t->current_space = as_attr;
- } else {
- t->current_space = as_comm;
- }
-
- /*
- * Set address
- */
- addr = t->mapaddr + (mem->card_start & M32R_PCC_MAPMASK);
- mem->static_start = addr + mem->card_start;
-
- return 0;
-
-} /* _set_mem_map */
-
-#if 0 /* driver model ordering issue */
-/*======================================================================
-
- Routines for accessing socket information and register dumps via
- /proc/bus/pccard/...
-
-======================================================================*/
-
-static ssize_t show_info(struct class_device *class_dev, char *buf)
-{
- pcc_socket_t *s = container_of(class_dev, struct pcc_socket,
- socket.dev);
-
- return sprintf(buf, "type: %s\nbase addr: 0x%08lx\n",
- pcc[s->type].name, s->base);
-}
-
-static ssize_t show_exca(struct class_device *class_dev, char *buf)
-{
- /* FIXME */
-
- return 0;
-}
-
-static CLASS_DEVICE_ATTR(info, S_IRUGO, show_info, NULL);
-static CLASS_DEVICE_ATTR(exca, S_IRUGO, show_exca, NULL);
-#endif
-
-/*====================================================================*/
-
-/* this is horribly ugly... proper locking needs to be done here at
- * some time... */
-#define LOCKED(x) do { \
- int retval; \
- unsigned long flags; \
- spin_lock_irqsave(&pcc_lock, flags); \
- retval = x; \
- spin_unlock_irqrestore(&pcc_lock, flags); \
- return retval; \
-} while (0)
-
-
-static int pcc_get_status(struct pcmcia_socket *s, u_int *value)
-{
- unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
-
- if (socket[sock].flags & IS_ALIVE) {
- dev_dbg(&s->dev, "pcc_get_status: sock(%d) -EINVAL\n", sock);
- *value = 0;
- return -EINVAL;
- }
- dev_dbg(&s->dev, "pcc_get_status: sock(%d)\n", sock);
- LOCKED(_pcc_get_status(sock, value));
-}
-
-static int pcc_set_socket(struct pcmcia_socket *s, socket_state_t *state)
-{
- unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
-
- if (socket[sock].flags & IS_ALIVE) {
- dev_dbg(&s->dev, "pcc_set_socket: sock(%d) -EINVAL\n", sock);
- return -EINVAL;
- }
- dev_dbg(&s->dev, "pcc_set_socket: sock(%d)\n", sock);
- LOCKED(_pcc_set_socket(sock, state));
-}
-
-static int pcc_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
-{
- unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
-
- if (socket[sock].flags & IS_ALIVE) {
- dev_dbg(&s->dev, "pcc_set_io_map: sock(%d) -EINVAL\n", sock);
- return -EINVAL;
- }
- dev_dbg(&s->dev, "pcc_set_io_map: sock(%d)\n", sock);
- LOCKED(_pcc_set_io_map(sock, io));
-}
-
-static int pcc_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *mem)
-{
- unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
-
- if (socket[sock].flags & IS_ALIVE) {
- dev_dbg(&s->dev, "pcc_set_mem_map: sock(%d) -EINVAL\n", sock);
- return -EINVAL;
- }
- dev_dbg(&s->dev, "pcc_set_mem_map: sock(%d)\n", sock);
- LOCKED(_pcc_set_mem_map(sock, mem));
-}
-
-static int pcc_init(struct pcmcia_socket *s)
-{
- dev_dbg(&s->dev, "pcc_init()\n");
- return 0;
-}
-
-static struct pccard_operations pcc_operations = {
- .init = pcc_init,
- .get_status = pcc_get_status,
- .set_socket = pcc_set_socket,
- .set_io_map = pcc_set_io_map,
- .set_mem_map = pcc_set_mem_map,
-};
-
-
-/*====================================================================*/
-
-static struct platform_driver pcc_driver = {
- .driver = {
- .name = "cfc",
- },
-};
-
-static struct platform_device pcc_device = {
- .name = "cfc",
- .id = 0,
-};
-
-/*====================================================================*/
-
-static int __init init_m32r_pcc(void)
-{
- int i, ret;
-
- ret = platform_driver_register(&pcc_driver);
- if (ret)
- return ret;
-
- ret = platform_device_register(&pcc_device);
- if (ret){
- platform_driver_unregister(&pcc_driver);
- return ret;
- }
-
-#if defined(CONFIG_PLAT_MAPPI2) || defined(CONFIG_PLAT_MAPPI3)
- pcc_set(0, (unsigned int)PLD_CFCR0, 0x0f0f);
- pcc_set(0, (unsigned int)PLD_CFCR1, 0x0200);
-#endif
-
- pcc_sockets = 0;
-
-#if !defined(CONFIG_PLAT_USRV)
- add_pcc_socket(M32R_PCC0_BASE, PLD_IRQ_CFC_INSERT, CFC_ATTR_MAPBASE,
- CFC_IOPORT_BASE);
-#else /* CONFIG_PLAT_USRV */
- {
- ulong base, mapaddr;
- unsigned int ioaddr;
-
- for (i = 0 ; i < M32R_MAX_PCC ; i++) {
- base = (ulong)PLD_CFRSTCR;
- base = base | (i << 8);
- ioaddr = (i + 1) << 12;
- mapaddr = CFC_ATTR_MAPBASE | (i << 20);
- add_pcc_socket(base, 0, mapaddr, ioaddr);
- }
- }
-#endif /* CONFIG_PLAT_USRV */
-
- if (pcc_sockets == 0) {
- printk("socket is not found.\n");
- platform_device_unregister(&pcc_device);
- platform_driver_unregister(&pcc_driver);
- return -ENODEV;
- }
-
- /* Set up interrupt handler(s) */
-
- for (i = 0 ; i < pcc_sockets ; i++) {
- socket[i].socket.dev.parent = &pcc_device.dev;
- socket[i].socket.ops = &pcc_operations;
- socket[i].socket.resource_ops = &pccard_static_ops;
- socket[i].socket.owner = THIS_MODULE;
- socket[i].number = i;
- ret = pcmcia_register_socket(&socket[i].socket);
- if (!ret)
- socket[i].flags |= IS_REGISTERED;
- }
-
- /* Finally, schedule a polling interrupt */
- if (poll_interval != 0) {
- timer_setup(&poll_timer, pcc_interrupt_wrapper, 0);
- poll_timer.expires = jiffies + poll_interval;
- add_timer(&poll_timer);
- }
-
- return 0;
-} /* init_m32r_pcc */
-
-static void __exit exit_m32r_pcc(void)
-{
- int i;
-
- for (i = 0; i < pcc_sockets; i++)
- if (socket[i].flags & IS_REGISTERED)
- pcmcia_unregister_socket(&socket[i].socket);
-
- platform_device_unregister(&pcc_device);
- if (poll_interval != 0)
- del_timer_sync(&poll_timer);
-
- platform_driver_unregister(&pcc_driver);
-} /* exit_m32r_pcc */
-
-module_init(init_m32r_pcc);
-module_exit(exit_m32r_pcc);
-MODULE_LICENSE("Dual MPL/GPL");
-/*====================================================================*/
diff --git a/drivers/pcmcia/m32r_cfc.h b/drivers/pcmcia/m32r_cfc.h
deleted file mode 100644
index 05fec98617d0..000000000000
--- a/drivers/pcmcia/m32r_cfc.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Copyright (C) 2001 by Hiroyuki Kondo
- */
-
-#if !defined(CONFIG_M32R_CFC_NUM)
-#define M32R_MAX_PCC 2
-#else
-#define M32R_MAX_PCC CONFIG_M32R_CFC_NUM
-#endif
-
-/*
- * M32R PC Card Controller
- */
-#define M32R_PCC0_BASE 0x00ef7000
-#define M32R_PCC1_BASE 0x00ef7020
-
-/*
- * Register offsets
- */
-#define PCCR 0x00
-#define PCADR 0x04
-#define PCMOD 0x08
-#define PCIRC 0x0c
-#define PCCSIGCR 0x10
-#define PCATCR 0x14
-
-/*
- * PCCR
- */
-#define PCCR_PCEN (1UL<<(31-31))
-
-/*
- * PCIRC
- */
-#define PCIRC_BWERR (1UL<<(31-7))
-#define PCIRC_CDIN1 (1UL<<(31-14))
-#define PCIRC_CDIN2 (1UL<<(31-15))
-#define PCIRC_BEIEN (1UL<<(31-23))
-#define PCIRC_CIIEN (1UL<<(31-30))
-#define PCIRC_COIEN (1UL<<(31-31))
-
-/*
- * PCCSIGCR
- */
-#define PCCSIGCR_SEN (1UL<<(31-3))
-#define PCCSIGCR_VEN (1UL<<(31-7))
-#define PCCSIGCR_CRST (1UL<<(31-15))
-#define PCCSIGCR_COCR (1UL<<(31-31))
-
-/*
- *
- */
-#define PCMOD_AS_ATTRIB (1UL<<(31-19))
-#define PCMOD_AS_IO (1UL<<(31-18))
-
-#define PCMOD_CBSZ (1UL<<(31-23)) /* set for 8bit */
-
-#define PCMOD_DBEX (1UL<<(31-31)) /* set for excahnge */
-
-/*
- * M32R PCC Map addr
- */
-
-#define M32R_PCC0_MAPBASE 0x14000000
-#define M32R_PCC1_MAPBASE 0x16000000
-
-#define M32R_PCC_MAPMAX 0x02000000
-
-#define M32R_PCC_MAPSIZE 0x00001000 /* XXX */
-#define M32R_PCC_MAPMASK (~(M32R_PCC_MAPMAX-1))
-
-#define CFC_IOPORT_BASE 0x1000
-
-#if defined(CONFIG_PLAT_MAPPI3)
-#define CFC_ATTR_MAPBASE 0x14014000
-#define CFC_IO_MAPBASE_BYTE 0xb4012000
-#define CFC_IO_MAPBASE_WORD 0xb4002000
-#elif !defined(CONFIG_PLAT_USRV)
-#define CFC_ATTR_MAPBASE 0x0c014000
-#define CFC_IO_MAPBASE_BYTE 0xac012000
-#define CFC_IO_MAPBASE_WORD 0xac002000
-#else
-#define CFC_ATTR_MAPBASE 0x04014000
-#define CFC_IO_MAPBASE_BYTE 0xa4012000
-#define CFC_IO_MAPBASE_WORD 0xa4002000
-#endif /* CONFIG_PLAT_USRV */
-
diff --git a/drivers/pcmcia/m32r_pcc.c b/drivers/pcmcia/m32r_pcc.c
deleted file mode 100644
index c2239a7e383a..000000000000
--- a/drivers/pcmcia/m32r_pcc.c
+++ /dev/null
@@ -1,763 +0,0 @@
-/*
- * drivers/pcmcia/m32r_pcc.c
- *
- * Device driver for the PCMCIA functionality of M32R.
- *
- * Copyright (c) 2001, 2002, 2003, 2004
- * Hiroyuki Kondo, Naoto Sugai, Hayato Fujiwara
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/timer.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/workqueue.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/bitops.h>
-#include <asm/irq.h>
-#include <asm/io.h>
-#include <asm/addrspace.h>
-
-#include <pcmcia/ss.h>
-
-/* XXX: should be moved into asm/irq.h */
-#define PCC0_IRQ 24
-#define PCC1_IRQ 25
-
-#include "m32r_pcc.h"
-
-#define CHAOS_PCC_DEBUG
-#ifdef CHAOS_PCC_DEBUG
- static volatile u_short dummy_readbuf;
-#endif
-
-#define PCC_DEBUG_DBEX
-
-
-/* Poll status interval -- 0 means default to interrupt */
-static int poll_interval = 0;
-
-typedef enum pcc_space { as_none = 0, as_comm, as_attr, as_io } pcc_as_t;
-
-typedef struct pcc_socket {
- u_short type, flags;
- struct pcmcia_socket socket;
- unsigned int number;
- unsigned int ioaddr;
- u_long mapaddr;
- u_long base; /* PCC register base */
- u_char cs_irq, intr;
- pccard_io_map io_map[MAX_IO_WIN];
- pccard_mem_map mem_map[MAX_WIN];
- u_char io_win;
- u_char mem_win;
- pcc_as_t current_space;
- u_char last_iodbex;
-#ifdef CHAOS_PCC_DEBUG
- u_char last_iosize;
-#endif
-#ifdef CONFIG_PROC_FS
- struct proc_dir_entry *proc;
-#endif
-} pcc_socket_t;
-
-static int pcc_sockets = 0;
-static pcc_socket_t socket[M32R_MAX_PCC] = {
- { 0, }, /* ... */
-};
-
-/*====================================================================*/
-
-static unsigned int pcc_get(u_short, unsigned int);
-static void pcc_set(u_short, unsigned int , unsigned int );
-
-static DEFINE_SPINLOCK(pcc_lock);
-
-void pcc_iorw(int sock, unsigned long port, void *buf, size_t size, size_t nmemb, int wr, int flag)
-{
- u_long addr;
- u_long flags;
- int need_ex;
-#ifdef PCC_DEBUG_DBEX
- int _dbex;
-#endif
- pcc_socket_t *t = &socket[sock];
-#ifdef CHAOS_PCC_DEBUG
- int map_changed = 0;
-#endif
-
- /* Need lock ? */
- spin_lock_irqsave(&pcc_lock, flags);
-
- /*
- * Check if need dbex
- */
- need_ex = (size > 1 && flag == 0) ? PCMOD_DBEX : 0;
-#ifdef PCC_DEBUG_DBEX
- _dbex = need_ex;
- need_ex = 0;
-#endif
-
- /*
- * calculate access address
- */
- addr = t->mapaddr + port - t->ioaddr + KSEG1; /* XXX */
-
- /*
- * Check current mapping
- */
- if (t->current_space != as_io || t->last_iodbex != need_ex) {
-
- u_long cbsz;
-
- /*
- * Disable first
- */
- pcc_set(sock, PCCR, 0);
-
- /*
- * Set mode and io address
- */
- cbsz = (t->flags & MAP_16BIT) ? 0 : PCMOD_CBSZ;
- pcc_set(sock, PCMOD, PCMOD_AS_IO | cbsz | need_ex);
- pcc_set(sock, PCADR, addr & 0x1ff00000);
-
- /*
- * Enable and read it
- */
- pcc_set(sock, PCCR, 1);
-
-#ifdef CHAOS_PCC_DEBUG
-#if 0
- map_changed = (t->current_space == as_attr && size == 2); /* XXX */
-#else
- map_changed = 1;
-#endif
-#endif
- t->current_space = as_io;
- }
-
- /*
- * access to IO space
- */
- if (size == 1) {
- /* Byte */
- unsigned char *bp = (unsigned char *)buf;
-
-#ifdef CHAOS_DEBUG
- if (map_changed) {
- dummy_readbuf = readb(addr);
- }
-#endif
- if (wr) {
- /* write Byte */
- while (nmemb--) {
- writeb(*bp++, addr);
- }
- } else {
- /* read Byte */
- while (nmemb--) {
- *bp++ = readb(addr);
- }
- }
- } else {
- /* Word */
- unsigned short *bp = (unsigned short *)buf;
-
-#ifdef CHAOS_PCC_DEBUG
- if (map_changed) {
- dummy_readbuf = readw(addr);
- }
-#endif
- if (wr) {
- /* write Word */
- while (nmemb--) {
-#ifdef PCC_DEBUG_DBEX
- if (_dbex) {
- unsigned char *cp = (unsigned char *)bp;
- unsigned short tmp;
- tmp = cp[1] << 8 | cp[0];
- writew(tmp, addr);
- bp++;
- } else
-#endif
- writew(*bp++, addr);
- }
- } else {
- /* read Word */
- while (nmemb--) {
-#ifdef PCC_DEBUG_DBEX
- if (_dbex) {
- unsigned char *cp = (unsigned char *)bp;
- unsigned short tmp;
- tmp = readw(addr);
- cp[0] = tmp & 0xff;
- cp[1] = (tmp >> 8) & 0xff;
- bp++;
- } else
-#endif
- *bp++ = readw(addr);
- }
- }
- }
-
-#if 1
- /* addr is no longer used */
- if ((addr = pcc_get(sock, PCIRC)) & PCIRC_BWERR) {
- printk("m32r_pcc: BWERR detected : port 0x%04lx : iosize %dbit\n",
- port, size * 8);
- pcc_set(sock, PCIRC, addr);
- }
-#endif
- /*
- * save state
- */
- t->last_iosize = size;
- t->last_iodbex = need_ex;
-
- /* Need lock ? */
-
- spin_unlock_irqrestore(&pcc_lock,flags);
-
- return;
-}
-
-void pcc_ioread(int sock, unsigned long port, void *buf, size_t size, size_t nmemb, int flag) {
- pcc_iorw(sock, port, buf, size, nmemb, 0, flag);
-}
-
-void pcc_iowrite(int sock, unsigned long port, void *buf, size_t size, size_t nmemb, int flag) {
- pcc_iorw(sock, port, buf, size, nmemb, 1, flag);
-}
-
-/*====================================================================*/
-
-#define IS_REGISTERED 0x2000
-#define IS_ALIVE 0x8000
-
-typedef struct pcc_t {
- char *name;
- u_short flags;
-} pcc_t;
-
-static pcc_t pcc[] = {
- { "xnux2", 0 }, { "xnux2", 0 },
-};
-
-static irqreturn_t pcc_interrupt(int, void *);
-
-/*====================================================================*/
-
-static struct timer_list poll_timer;
-
-static unsigned int pcc_get(u_short sock, unsigned int reg)
-{
- return inl(socket[sock].base + reg);
-}
-
-
-static void pcc_set(u_short sock, unsigned int reg, unsigned int data)
-{
- outl(data, socket[sock].base + reg);
-}
-
-/*======================================================================
-
- See if a card is present, powered up, in IO mode, and already
- bound to a (non PC Card) Linux driver. We leave these alone.
-
- We make an exception for cards that seem to be serial devices.
-
-======================================================================*/
-
-static int __init is_alive(u_short sock)
-{
- unsigned int stat;
- unsigned int f;
-
- stat = pcc_get(sock, PCIRC);
- f = (stat & (PCIRC_CDIN1 | PCIRC_CDIN2)) >> 16;
- if(!f){
- printk("m32r_pcc: No Card is detected at socket %d : stat = 0x%08x\n",stat,sock);
- return 0;
- }
- if(f!=3)
- printk("m32r_pcc: Insertion fail (%.8x) at socket %d\n",stat,sock);
- else
- printk("m32r_pcc: Card is Inserted at socket %d(%.8x)\n",sock,stat);
- return 0;
-}
-
-static int add_pcc_socket(ulong base, int irq, ulong mapaddr,
- unsigned int ioaddr)
-{
- pcc_socket_t *t = &socket[pcc_sockets];
- int err;
-
- /* add sockets */
- t->ioaddr = ioaddr;
- t->mapaddr = mapaddr;
- t->base = base;
-#ifdef CHAOS_PCC_DEBUG
- t->flags = MAP_16BIT;
-#else
- t->flags = 0;
-#endif
- if (is_alive(pcc_sockets))
- t->flags |= IS_ALIVE;
-
- /* add pcc */
- if (t->base > 0) {
- request_region(t->base, 0x20, "m32r-pcc");
- }
-
- printk(KERN_INFO " %s ", pcc[pcc_sockets].name);
- printk("pcc at 0x%08lx\n", t->base);
-
- /* Update socket interrupt information, capabilities */
- t->socket.features |= (SS_CAP_PCCARD | SS_CAP_STATIC_MAP);
- t->socket.map_size = M32R_PCC_MAPSIZE;
- t->socket.io_offset = ioaddr; /* use for io access offset */
- t->socket.irq_mask = 0;
- t->socket.pci_irq = 2 + pcc_sockets; /* XXX */
-
- err = request_irq(irq, pcc_interrupt, 0, "m32r-pcc", pcc_interrupt);
- if (err) {
- if (t->base > 0)
- release_region(t->base, 0x20);
- return err;
- }
-
- pcc_sockets++;
-
- return 0;
-}
-
-
-/*====================================================================*/
-
-static irqreturn_t pcc_interrupt(int irq, void *dev)
-{
- int i, j, irc;
- u_int events, active;
- int handled = 0;
-
- pr_debug("m32r_pcc: pcc_interrupt(%d)\n", irq);
-
- for (j = 0; j < 20; j++) {
- active = 0;
- for (i = 0; i < pcc_sockets; i++) {
- if ((socket[i].cs_irq != irq) &&
- (socket[i].socket.pci_irq != irq))
- continue;
- handled = 1;
- irc = pcc_get(i, PCIRC);
- irc >>=16;
- pr_debug("m32r_pcc: interrupt: socket %d pcirc 0x%02x ",
- i, irc);
- if (!irc)
- continue;
-
- events = (irc) ? SS_DETECT : 0;
- events |= (pcc_get(i,PCCR) & PCCR_PCEN) ? SS_READY : 0;
- pr_debug("m32r_pcc: event 0x%02x\n", events);
-
- if (events)
- pcmcia_parse_events(&socket[i].socket, events);
-
- active |= events;
- active = 0;
- }
- if (!active) break;
- }
- if (j == 20)
- printk(KERN_NOTICE "m32r-pcc: infinite loop in interrupt handler\n");
-
- pr_debug("m32r_pcc: interrupt done\n");
-
- return IRQ_RETVAL(handled);
-} /* pcc_interrupt */
-
-static void pcc_interrupt_wrapper(struct timer_list *unused)
-{
- pcc_interrupt(0, NULL);
- poll_timer.expires = jiffies + poll_interval;
- add_timer(&poll_timer);
-}
-
-/*====================================================================*/
-
-static int _pcc_get_status(u_short sock, u_int *value)
-{
- u_int status;
-
- status = pcc_get(sock,PCIRC);
- *value = ((status & PCIRC_CDIN1) && (status & PCIRC_CDIN2))
- ? SS_DETECT : 0;
-
- status = pcc_get(sock,PCCR);
-
-#if 0
- *value |= (status & PCCR_PCEN) ? SS_READY : 0;
-#else
- *value |= SS_READY; /* XXX: always */
-#endif
-
- status = pcc_get(sock,PCCSIGCR);
- *value |= (status & PCCSIGCR_VEN) ? SS_POWERON : 0;
-
- pr_debug("m32r_pcc: GetStatus(%d) = %#4.4x\n", sock, *value);
- return 0;
-} /* _get_status */
-
-/*====================================================================*/
-
-static int _pcc_set_socket(u_short sock, socket_state_t *state)
-{
- u_long reg = 0;
-
- pr_debug("m32r_pcc: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
- "io_irq %d, csc_mask %#2.2x)", sock, state->flags,
- state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
-
- if (state->Vcc) {
- /*
- * 5V only
- */
- if (state->Vcc == 50) {
- reg |= PCCSIGCR_VEN;
- } else {
- return -EINVAL;
- }
- }
-
- if (state->flags & SS_RESET) {
- pr_debug("m32r_pcc: :RESET\n");
- reg |= PCCSIGCR_CRST;
- }
- if (state->flags & SS_OUTPUT_ENA){
- pr_debug("m32r_pcc: :OUTPUT_ENA\n");
- /* bit clear */
- } else {
- reg |= PCCSIGCR_SEN;
- }
-
- pcc_set(sock,PCCSIGCR,reg);
-
- if(state->flags & SS_IOCARD){
- pr_debug("m32r_pcc: :IOCARD");
- }
- if (state->flags & SS_PWR_AUTO) {
- pr_debug("m32r_pcc: :PWR_AUTO");
- }
- if (state->csc_mask & SS_DETECT)
- pr_debug("m32r_pcc: :csc-SS_DETECT");
- if (state->flags & SS_IOCARD) {
- if (state->csc_mask & SS_STSCHG)
- pr_debug("m32r_pcc: :STSCHG");
- } else {
- if (state->csc_mask & SS_BATDEAD)
- pr_debug("m32r_pcc: :BATDEAD");
- if (state->csc_mask & SS_BATWARN)
- pr_debug("m32r_pcc: :BATWARN");
- if (state->csc_mask & SS_READY)
- pr_debug("m32r_pcc: :READY");
- }
- pr_debug("m32r_pcc: \n");
- return 0;
-} /* _set_socket */
-
-/*====================================================================*/
-
-static int _pcc_set_io_map(u_short sock, struct pccard_io_map *io)
-{
- u_char map;
-
- pr_debug("m32r_pcc: SetIOMap(%d, %d, %#2.2x, %d ns, "
- "%#llx-%#llx)\n", sock, io->map, io->flags,
- io->speed, (unsigned long long)io->start,
- (unsigned long long)io->stop);
- map = io->map;
-
- return 0;
-} /* _set_io_map */
-
-/*====================================================================*/
-
-static int _pcc_set_mem_map(u_short sock, struct pccard_mem_map *mem)
-{
-
- u_char map = mem->map;
- u_long mode;
- u_long addr;
- pcc_socket_t *t = &socket[sock];
-#ifdef CHAOS_PCC_DEBUG
-#if 0
- pcc_as_t last = t->current_space;
-#endif
-#endif
-
- pr_debug("m32r_pcc: SetMemMap(%d, %d, %#2.2x, %d ns, "
- "%#llx, %#x)\n", sock, map, mem->flags,
- mem->speed, (unsigned long long)mem->static_start,
- mem->card_start);
-
- /*
- * sanity check
- */
- if ((map > MAX_WIN) || (mem->card_start > 0x3ffffff)){
- return -EINVAL;
- }
-
- /*
- * de-activate
- */
- if ((mem->flags & MAP_ACTIVE) == 0) {
- t->current_space = as_none;
- return 0;
- }
-
- /*
- * Disable first
- */
- pcc_set(sock, PCCR, 0);
-
- /*
- * Set mode
- */
- if (mem->flags & MAP_ATTRIB) {
- mode = PCMOD_AS_ATTRIB | PCMOD_CBSZ;
- t->current_space = as_attr;
- } else {
- mode = 0; /* common memory */
- t->current_space = as_comm;
- }
- pcc_set(sock, PCMOD, mode);
-
- /*
- * Set address
- */
- addr = t->mapaddr + (mem->card_start & M32R_PCC_MAPMASK);
- pcc_set(sock, PCADR, addr);
-
- mem->static_start = addr + mem->card_start;
-
- /*
- * Enable again
- */
- pcc_set(sock, PCCR, 1);
-
-#ifdef CHAOS_PCC_DEBUG
-#if 0
- if (last != as_attr) {
-#else
- if (1) {
-#endif
- dummy_readbuf = *(u_char *)(addr + KSEG1);
- }
-#endif
-
- return 0;
-
-} /* _set_mem_map */
-
-#if 0 /* driver model ordering issue */
-/*======================================================================
-
- Routines for accessing socket information and register dumps via
- /proc/bus/pccard/...
-
-======================================================================*/
-
-static ssize_t show_info(struct class_device *class_dev, char *buf)
-{
- pcc_socket_t *s = container_of(class_dev, struct pcc_socket,
- socket.dev);
-
- return sprintf(buf, "type: %s\nbase addr: 0x%08lx\n",
- pcc[s->type].name, s->base);
-}
-
-static ssize_t show_exca(struct class_device *class_dev, char *buf)
-{
- /* FIXME */
-
- return 0;
-}
-
-static CLASS_DEVICE_ATTR(info, S_IRUGO, show_info, NULL);
-static CLASS_DEVICE_ATTR(exca, S_IRUGO, show_exca, NULL);
-#endif
-
-/*====================================================================*/
-
-/* this is horribly ugly... proper locking needs to be done here at
- * some time... */
-#define LOCKED(x) do { \
- int retval; \
- unsigned long flags; \
- spin_lock_irqsave(&pcc_lock, flags); \
- retval = x; \
- spin_unlock_irqrestore(&pcc_lock, flags); \
- return retval; \
-} while (0)
-
-
-static int pcc_get_status(struct pcmcia_socket *s, u_int *value)
-{
- unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
-
- if (socket[sock].flags & IS_ALIVE) {
- *value = 0;
- return -EINVAL;
- }
- LOCKED(_pcc_get_status(sock, value));
-}
-
-static int pcc_set_socket(struct pcmcia_socket *s, socket_state_t *state)
-{
- unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
-
- if (socket[sock].flags & IS_ALIVE)
- return -EINVAL;
-
- LOCKED(_pcc_set_socket(sock, state));
-}
-
-static int pcc_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
-{
- unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
-
- if (socket[sock].flags & IS_ALIVE)
- return -EINVAL;
- LOCKED(_pcc_set_io_map(sock, io));
-}
-
-static int pcc_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *mem)
-{
- unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
-
- if (socket[sock].flags & IS_ALIVE)
- return -EINVAL;
- LOCKED(_pcc_set_mem_map(sock, mem));
-}
-
-static int pcc_init(struct pcmcia_socket *s)
-{
- pr_debug("m32r_pcc: init call\n");
- return 0;
-}
-
-static struct pccard_operations pcc_operations = {
- .init = pcc_init,
- .get_status = pcc_get_status,
- .set_socket = pcc_set_socket,
- .set_io_map = pcc_set_io_map,
- .set_mem_map = pcc_set_mem_map,
-};
-
-/*====================================================================*/
-
-static struct platform_driver pcc_driver = {
- .driver = {
- .name = "pcc",
- },
-};
-
-static struct platform_device pcc_device = {
- .name = "pcc",
- .id = 0,
-};
-
-/*====================================================================*/
-
-static int __init init_m32r_pcc(void)
-{
- int i, ret;
-
- ret = platform_driver_register(&pcc_driver);
- if (ret)
- return ret;
-
- ret = platform_device_register(&pcc_device);
- if (ret)
- goto unreg_driv;
-
- printk(KERN_INFO "m32r PCC probe:\n");
-
- pcc_sockets = 0;
-
- ret = add_pcc_socket(M32R_PCC0_BASE, PCC0_IRQ, M32R_PCC0_MAPBASE,
- 0x1000);
- if (ret)
- goto unreg_dev;
-
-#ifdef CONFIG_M32RPCC_SLOT2
- ret = add_pcc_socket(M32R_PCC1_BASE, PCC1_IRQ, M32R_PCC1_MAPBASE,
- 0x2000);
- if (ret)
- goto unreg_dev;
-#endif
-
- if (pcc_sockets == 0) {
- printk("socket is not found.\n");
- ret = -ENODEV;
- goto unreg_dev;
- }
-
- /* Set up interrupt handler(s) */
-
- for (i = 0 ; i < pcc_sockets ; i++) {
- socket[i].socket.dev.parent = &pcc_device.dev;
- socket[i].socket.ops = &pcc_operations;
- socket[i].socket.resource_ops = &pccard_static_ops;
- socket[i].socket.owner = THIS_MODULE;
- socket[i].number = i;
- ret = pcmcia_register_socket(&socket[i].socket);
- if (!ret)
- socket[i].flags |= IS_REGISTERED;
- }
-
- /* Finally, schedule a polling interrupt */
- if (poll_interval != 0) {
- timer_setup(&poll_timer, pcc_interrupt_wrapper, 0);
- poll_timer.expires = jiffies + poll_interval;
- add_timer(&poll_timer);
- }
-
- return 0;
-
-unreg_dev:
- platform_device_unregister(&pcc_device);
-unreg_driv:
- platform_driver_unregister(&pcc_driver);
- return ret;
-} /* init_m32r_pcc */
-
-static void __exit exit_m32r_pcc(void)
-{
- int i;
-
- for (i = 0; i < pcc_sockets; i++)
- if (socket[i].flags & IS_REGISTERED)
- pcmcia_unregister_socket(&socket[i].socket);
-
- platform_device_unregister(&pcc_device);
- if (poll_interval != 0)
- del_timer_sync(&poll_timer);
-
- platform_driver_unregister(&pcc_driver);
-} /* exit_m32r_pcc */
-
-module_init(init_m32r_pcc);
-module_exit(exit_m32r_pcc);
-MODULE_LICENSE("Dual MPL/GPL");
-/*====================================================================*/
diff --git a/drivers/pcmcia/m32r_pcc.h b/drivers/pcmcia/m32r_pcc.h
deleted file mode 100644
index d99ad3864ff3..000000000000
--- a/drivers/pcmcia/m32r_pcc.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Copyright (C) 2001 by Hiroyuki Kondo
- */
-
-#define M32R_MAX_PCC 2
-
-/*
- * M32R PC Card Controller
- */
-#define M32R_PCC0_BASE 0x00ef7000
-#define M32R_PCC1_BASE 0x00ef7020
-
-/*
- * Register offsets
- */
-#define PCCR 0x00
-#define PCADR 0x04
-#define PCMOD 0x08
-#define PCIRC 0x0c
-#define PCCSIGCR 0x10
-#define PCATCR 0x14
-
-/*
- * PCCR
- */
-#define PCCR_PCEN (1UL<<(31-31))
-
-/*
- * PCIRC
- */
-#define PCIRC_BWERR (1UL<<(31-7))
-#define PCIRC_CDIN1 (1UL<<(31-14))
-#define PCIRC_CDIN2 (1UL<<(31-15))
-#define PCIRC_BEIEN (1UL<<(31-23))
-#define PCIRC_CIIEN (1UL<<(31-30))
-#define PCIRC_COIEN (1UL<<(31-31))
-
-/*
- * PCCSIGCR
- */
-#define PCCSIGCR_SEN (1UL<<(31-3))
-#define PCCSIGCR_VEN (1UL<<(31-7))
-#define PCCSIGCR_CRST (1UL<<(31-15))
-#define PCCSIGCR_COCR (1UL<<(31-31))
-
-/*
- *
- */
-#define PCMOD_AS_ATTRIB (1UL<<(31-19))
-#define PCMOD_AS_IO (1UL<<(31-18))
-
-#define PCMOD_CBSZ (1UL<<(31-23)) /* set for 8bit */
-
-#define PCMOD_DBEX (1UL<<(31-31)) /* set for excahnge */
-
-/*
- * M32R PCC Map addr
- */
-#define M32R_PCC0_MAPBASE 0x14000000
-#define M32R_PCC1_MAPBASE 0x16000000
-
-#define M32R_PCC_MAPMAX 0x02000000
-
-#define M32R_PCC_MAPSIZE 0x00001000 /* XXX */
-#define M32R_PCC_MAPMASK (~(M32R_PCC_MAPMAX-1))
diff --git a/drivers/pcmcia/sa1100_assabet.c b/drivers/pcmcia/sa1100_assabet.c
deleted file mode 100644
index 78ad2bba76db..000000000000
--- a/drivers/pcmcia/sa1100_assabet.c
+++ /dev/null
@@ -1,100 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * drivers/pcmcia/sa1100_assabet.c
- *
- * PCMCIA implementation routines for Assabet
- *
- */
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/init.h>
-#include <linux/gpio.h>
-
-#include <asm/mach-types.h>
-#include <mach/assabet.h>
-
-#include "sa1100_generic.h"
-
-static int assabet_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
-{
- skt->stat[SOC_STAT_CD].gpio = ASSABET_GPIO_CF_CD;
- skt->stat[SOC_STAT_CD].name = "CF CD";
- skt->stat[SOC_STAT_BVD1].gpio = ASSABET_GPIO_CF_BVD1;
- skt->stat[SOC_STAT_BVD1].name = "CF BVD1";
- skt->stat[SOC_STAT_BVD2].gpio = ASSABET_GPIO_CF_BVD2;
- skt->stat[SOC_STAT_BVD2].name = "CF BVD2";
- skt->stat[SOC_STAT_RDY].gpio = ASSABET_GPIO_CF_IRQ;
- skt->stat[SOC_STAT_RDY].name = "CF RDY";
-
- return 0;
-}
-
-static int
-assabet_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state)
-{
- unsigned int mask;
-
- switch (state->Vcc) {
- case 0:
- mask = 0;
- break;
-
- case 50:
- printk(KERN_WARNING "%s(): CS asked for 5V, applying 3.3V...\n",
- __func__);
-
- case 33: /* Can only apply 3.3V to the CF slot. */
- mask = ASSABET_BCR_CF_PWR;
- break;
-
- default:
- printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __func__,
- state->Vcc);
- return -1;
- }
-
- /* Silently ignore Vpp, speaker enable. */
-
- if (state->flags & SS_RESET)
- mask |= ASSABET_BCR_CF_RST;
- if (!(state->flags & SS_OUTPUT_ENA))
- mask |= ASSABET_BCR_CF_BUS_OFF;
-
- ASSABET_BCR_frob(ASSABET_BCR_CF_RST | ASSABET_BCR_CF_PWR |
- ASSABET_BCR_CF_BUS_OFF, mask);
-
- return 0;
-}
-
-/*
- * Disable card status IRQs on suspend.
- */
-static void assabet_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
-{
- /*
- * Tristate the CF bus signals. Also assert CF
- * reset as per user guide page 4-11.
- */
- ASSABET_BCR_set(ASSABET_BCR_CF_BUS_OFF | ASSABET_BCR_CF_RST);
-}
-
-static struct pcmcia_low_level assabet_pcmcia_ops = {
- .owner = THIS_MODULE,
- .hw_init = assabet_pcmcia_hw_init,
- .socket_state = soc_common_cf_socket_state,
- .configure_socket = assabet_pcmcia_configure_socket,
- .socket_suspend = assabet_pcmcia_socket_suspend,
-};
-
-int pcmcia_assabet_init(struct device *dev)
-{
- int ret = -ENODEV;
-
- if (machine_is_assabet() && !machine_has_neponset())
- ret = sa11xx_drv_pcmcia_probe(dev, &assabet_pcmcia_ops, 1, 1);
-
- return ret;
-}
diff --git a/drivers/pcmcia/sa1100_cerf.c b/drivers/pcmcia/sa1100_cerf.c
deleted file mode 100644
index 2a54081d161d..000000000000
--- a/drivers/pcmcia/sa1100_cerf.c
+++ /dev/null
@@ -1,86 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * drivers/pcmcia/sa1100_cerf.c
- *
- * PCMCIA implementation routines for CerfBoard
- * Based off the Assabet.
- *
- */
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/device.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/gpio.h>
-
-#include <mach/hardware.h>
-#include <asm/mach-types.h>
-#include <asm/irq.h>
-#include <mach/cerf.h>
-#include "sa1100_generic.h"
-
-#define CERF_SOCKET 1
-
-static int cerf_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
-{
- int ret;
-
- ret = gpio_request_one(CERF_GPIO_CF_RESET, GPIOF_OUT_INIT_LOW, "CF_RESET");
- if (ret)
- return ret;
-
- skt->stat[SOC_STAT_CD].gpio = CERF_GPIO_CF_CD;
- skt->stat[SOC_STAT_CD].name = "CF_CD";
- skt->stat[SOC_STAT_BVD1].gpio = CERF_GPIO_CF_BVD1;
- skt->stat[SOC_STAT_BVD1].name = "CF_BVD1";
- skt->stat[SOC_STAT_BVD2].gpio = CERF_GPIO_CF_BVD2;
- skt->stat[SOC_STAT_BVD2].name = "CF_BVD2";
- skt->stat[SOC_STAT_RDY].gpio = CERF_GPIO_CF_IRQ;
- skt->stat[SOC_STAT_RDY].name = "CF_IRQ";
-
- return 0;
-}
-
-static void cerf_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
-{
- gpio_free(CERF_GPIO_CF_RESET);
-}
-
-static int
-cerf_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
- const socket_state_t *state)
-{
- switch (state->Vcc) {
- case 0:
- case 50:
- case 33:
- break;
-
- default:
- printk(KERN_ERR "%s(): unrecognized Vcc %u\n",
- __func__, state->Vcc);
- return -1;
- }
-
- gpio_set_value(CERF_GPIO_CF_RESET, !!(state->flags & SS_RESET));
-
- return 0;
-}
-
-static struct pcmcia_low_level cerf_pcmcia_ops = {
- .owner = THIS_MODULE,
- .hw_init = cerf_pcmcia_hw_init,
- .hw_shutdown = cerf_pcmcia_hw_shutdown,
- .socket_state = soc_common_cf_socket_state,
- .configure_socket = cerf_pcmcia_configure_socket,
-};
-
-int pcmcia_cerf_init(struct device *dev)
-{
- int ret = -ENODEV;
-
- if (machine_is_cerf())
- ret = sa11xx_drv_pcmcia_probe(dev, &cerf_pcmcia_ops, CERF_SOCKET, 1);
-
- return ret;
-}
diff --git a/drivers/pcmcia/sa1100_generic.c b/drivers/pcmcia/sa1100_generic.c
index 66acdc85727c..47b060c57418 100644
--- a/drivers/pcmcia/sa1100_generic.c
+++ b/drivers/pcmcia/sa1100_generic.c
@@ -31,7 +31,9 @@
======================================================================*/
#include <linux/module.h>
+#include <linux/gpio/consumer.h>
#include <linux/init.h>
+#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
@@ -41,24 +43,64 @@
#include "sa1100_generic.h"
+static const char *sa11x0_cf_gpio_names[] = {
+ [SOC_STAT_CD] = "detect",
+ [SOC_STAT_BVD1] = "bvd1",
+ [SOC_STAT_BVD2] = "bvd2",
+ [SOC_STAT_RDY] = "ready",
+};
+
+static int sa11x0_cf_hw_init(struct soc_pcmcia_socket *skt)
+{
+ struct device *dev = skt->socket.dev.parent;
+ int i;
+
+ skt->gpio_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(skt->gpio_reset))
+ return PTR_ERR(skt->gpio_reset);
+
+ skt->gpio_bus_enable = devm_gpiod_get_optional(dev, "bus-enable",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(skt->gpio_bus_enable))
+ return PTR_ERR(skt->gpio_bus_enable);
+
+ skt->vcc.reg = devm_regulator_get_optional(dev, "vcc");
+ if (IS_ERR(skt->vcc.reg))
+ return PTR_ERR(skt->vcc.reg);
+
+ if (!skt->vcc.reg)
+ dev_warn(dev,
+ "no Vcc regulator provided, ignoring Vcc controls\n");
+
+ for (i = 0; i < ARRAY_SIZE(sa11x0_cf_gpio_names); i++) {
+ skt->stat[i].name = sa11x0_cf_gpio_names[i];
+ skt->stat[i].desc = devm_gpiod_get_optional(dev,
+ sa11x0_cf_gpio_names[i], GPIOD_IN);
+ if (IS_ERR(skt->stat[i].desc))
+ return PTR_ERR(skt->stat[i].desc);
+ }
+ return 0;
+}
+
+static int sa11x0_cf_configure_socket(struct soc_pcmcia_socket *skt,
+ const socket_state_t *state)
+{
+ return soc_pcmcia_regulator_set(skt, &skt->vcc, state->Vcc);
+}
+
+static struct pcmcia_low_level sa11x0_cf_ops = {
+ .owner = THIS_MODULE,
+ .hw_init = sa11x0_cf_hw_init,
+ .socket_state = soc_common_cf_socket_state,
+ .configure_socket = sa11x0_cf_configure_socket,
+};
+
int __init pcmcia_collie_init(struct device *dev);
-static int (*sa11x0_pcmcia_hw_init[])(struct device *dev) = {
-#ifdef CONFIG_SA1100_ASSABET
- pcmcia_assabet_init,
-#endif
-#ifdef CONFIG_SA1100_CERF
- pcmcia_cerf_init,
-#endif
+static int (*sa11x0_pcmcia_legacy_hw_init[])(struct device *dev) = {
#if defined(CONFIG_SA1100_H3100) || defined(CONFIG_SA1100_H3600)
pcmcia_h3600_init,
#endif
-#ifdef CONFIG_SA1100_NANOENGINE
- pcmcia_nanoengine_init,
-#endif
-#ifdef CONFIG_SA1100_SHANNON
- pcmcia_shannon_init,
-#endif
#ifdef CONFIG_SA1100_SIMPAD
pcmcia_simpad_init,
#endif
@@ -67,15 +109,15 @@ static int (*sa11x0_pcmcia_hw_init[])(struct device *dev) = {
#endif
};
-static int sa11x0_drv_pcmcia_probe(struct platform_device *dev)
+static int sa11x0_drv_pcmcia_legacy_probe(struct platform_device *dev)
{
int i, ret = -ENODEV;
/*
* Initialise any "on-board" PCMCIA sockets.
*/
- for (i = 0; i < ARRAY_SIZE(sa11x0_pcmcia_hw_init); i++) {
- ret = sa11x0_pcmcia_hw_init[i](&dev->dev);
+ for (i = 0; i < ARRAY_SIZE(sa11x0_pcmcia_legacy_hw_init); i++) {
+ ret = sa11x0_pcmcia_legacy_hw_init[i](&dev->dev);
if (ret == 0)
break;
}
@@ -83,7 +125,7 @@ static int sa11x0_drv_pcmcia_probe(struct platform_device *dev)
return ret;
}
-static int sa11x0_drv_pcmcia_remove(struct platform_device *dev)
+static int sa11x0_drv_pcmcia_legacy_remove(struct platform_device *dev)
{
struct skt_dev_info *sinfo = platform_get_drvdata(dev);
int i;
@@ -96,6 +138,45 @@ static int sa11x0_drv_pcmcia_remove(struct platform_device *dev)
return 0;
}
+static int sa11x0_drv_pcmcia_probe(struct platform_device *pdev)
+{
+ struct soc_pcmcia_socket *skt;
+ struct device *dev = &pdev->dev;
+
+ if (pdev->id == -1)
+ return sa11x0_drv_pcmcia_legacy_probe(pdev);
+
+ skt = devm_kzalloc(dev, sizeof(*skt), GFP_KERNEL);
+ if (!skt)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, skt);
+
+ skt->nr = pdev->id;
+ skt->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(skt->clk))
+ return PTR_ERR(skt->clk);
+
+ sa11xx_drv_pcmcia_ops(&sa11x0_cf_ops);
+ soc_pcmcia_init_one(skt, &sa11x0_cf_ops, dev);
+
+ return sa11xx_drv_pcmcia_add_one(skt);
+}
+
+static int sa11x0_drv_pcmcia_remove(struct platform_device *dev)
+{
+ struct soc_pcmcia_socket *skt;
+
+ if (dev->id == -1)
+ return sa11x0_drv_pcmcia_legacy_remove(dev);
+
+ skt = platform_get_drvdata(dev);
+
+ soc_pcmcia_remove_one(skt);
+
+ return 0;
+}
+
static struct platform_driver sa11x0_pcmcia_driver = {
.driver = {
.name = "sa11x0-pcmcia",
diff --git a/drivers/pcmcia/sa1100_generic.h b/drivers/pcmcia/sa1100_generic.h
index a5f1f1dd63cb..7b7cdcd20187 100644
--- a/drivers/pcmcia/sa1100_generic.h
+++ b/drivers/pcmcia/sa1100_generic.h
@@ -6,18 +6,14 @@
* Declaration for all machine specific init/exit functions.
*/
extern int pcmcia_adsbitsy_init(struct device *);
-extern int pcmcia_assabet_init(struct device *);
extern int pcmcia_badge4_init(struct device *);
-extern int pcmcia_cerf_init(struct device *);
extern int pcmcia_flexanet_init(struct device *);
extern int pcmcia_freebird_init(struct device *);
extern int pcmcia_gcplus_init(struct device *);
extern int pcmcia_graphicsmaster_init(struct device *);
extern int pcmcia_h3600_init(struct device *);
-extern int pcmcia_nanoengine_init(struct device *);
extern int pcmcia_pangolin_init(struct device *);
extern int pcmcia_pfs168_init(struct device *);
-extern int pcmcia_shannon_init(struct device *);
extern int pcmcia_simpad_init(struct device *);
extern int pcmcia_stork_init(struct device *);
extern int pcmcia_system3_init(struct device *);
diff --git a/drivers/pcmcia/sa1100_h3600.c b/drivers/pcmcia/sa1100_h3600.c
index aebf9a66fdde..a91222bc3824 100644
--- a/drivers/pcmcia/sa1100_h3600.c
+++ b/drivers/pcmcia/sa1100_h3600.c
@@ -24,13 +24,15 @@ static int h3600_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
int err;
+ skt->stat[SOC_STAT_CD].name = skt->nr ? "pcmcia1-detect" : "pcmcia0-detect";
+ skt->stat[SOC_STAT_RDY].name = skt->nr ? "pcmcia1-ready" : "pcmcia0-ready";
+
+ err = soc_pcmcia_request_gpiods(skt);
+ if (err)
+ return err;
+
switch (skt->nr) {
case 0:
- skt->stat[SOC_STAT_CD].gpio = H3XXX_GPIO_PCMCIA_CD0;
- skt->stat[SOC_STAT_CD].name = "PCMCIA CD0";
- skt->stat[SOC_STAT_RDY].gpio = H3XXX_GPIO_PCMCIA_IRQ0;
- skt->stat[SOC_STAT_RDY].name = "PCMCIA IRQ0";
-
err = gpio_request(H3XXX_EGPIO_OPT_NVRAM_ON, "OPT NVRAM ON");
if (err)
goto err01;
@@ -57,10 +59,6 @@ static int h3600_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
goto err06;
break;
case 1:
- skt->stat[SOC_STAT_CD].gpio = H3XXX_GPIO_PCMCIA_CD1;
- skt->stat[SOC_STAT_CD].name = "PCMCIA CD1";
- skt->stat[SOC_STAT_RDY].gpio = H3XXX_GPIO_PCMCIA_IRQ1;
- skt->stat[SOC_STAT_RDY].name = "PCMCIA IRQ1";
break;
}
return 0;
diff --git a/drivers/pcmcia/sa1100_nanoengine.c b/drivers/pcmcia/sa1100_nanoengine.c
deleted file mode 100644
index 35c30ff41e81..000000000000
--- a/drivers/pcmcia/sa1100_nanoengine.c
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * drivers/pcmcia/sa1100_nanoengine.c
- *
- * PCMCIA implementation routines for BSI nanoEngine.
- *
- * In order to have a fully functional pcmcia subsystem in a BSE nanoEngine
- * board you should carefully read this:
- * http://cambuca.ldhs.cetuc.puc-rio.br/nanoengine/
- *
- * Copyright (C) 2010 Marcelo Roberto Jimenez <mroberto@cpti.cetuc.puc-rio.br>
- *
- * Based on original work for kernel 2.4 by
- * Miguel Freitas <miguel@cpti.cetuc.puc-rio.br>
- *
- * 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.
- *
- */
-#include <linux/device.h>
-#include <linux/errno.h>
-#include <linux/gpio.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/signal.h>
-
-#include <asm/mach-types.h>
-#include <asm/irq.h>
-
-#include <mach/hardware.h>
-#include <mach/nanoengine.h>
-
-#include "sa1100_generic.h"
-
-struct nanoengine_pins {
- unsigned output_pins;
- unsigned clear_outputs;
- int gpio_rst;
- int gpio_cd;
- int gpio_rdy;
-};
-
-static struct nanoengine_pins nano_skts[] = {
- {
- .gpio_rst = GPIO_PC_RESET0,
- .gpio_cd = GPIO_PC_CD0,
- .gpio_rdy = GPIO_PC_READY0,
- }, {
- .gpio_rst = GPIO_PC_RESET1,
- .gpio_cd = GPIO_PC_CD1,
- .gpio_rdy = GPIO_PC_READY1,
- }
-};
-
-unsigned num_nano_pcmcia_sockets = ARRAY_SIZE(nano_skts);
-
-static int nanoengine_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
-{
- unsigned i = skt->nr;
- int ret;
-
- if (i >= num_nano_pcmcia_sockets)
- return -ENXIO;
-
- ret = gpio_request_one(nano_skts[i].gpio_rst, GPIOF_OUT_INIT_LOW,
- i ? "PC RST1" : "PC RST0");
- if (ret)
- return ret;
-
- skt->stat[SOC_STAT_CD].gpio = nano_skts[i].gpio_cd;
- skt->stat[SOC_STAT_CD].name = i ? "PC CD1" : "PC CD0";
- skt->stat[SOC_STAT_RDY].gpio = nano_skts[i].gpio_rdy;
- skt->stat[SOC_STAT_RDY].name = i ? "PC RDY1" : "PC RDY0";
-
- return 0;
-}
-
-static void nanoengine_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
-{
- gpio_free(nano_skts[skt->nr].gpio_rst);
-}
-
-static int nanoengine_pcmcia_configure_socket(
- struct soc_pcmcia_socket *skt, const socket_state_t *state)
-{
- unsigned i = skt->nr;
-
- if (i >= num_nano_pcmcia_sockets)
- return -ENXIO;
-
- gpio_set_value(nano_skts[skt->nr].gpio_rst, !!(state->flags & SS_RESET));
-
- return 0;
-}
-
-static void nanoengine_pcmcia_socket_state(
- struct soc_pcmcia_socket *skt, struct pcmcia_state *state)
-{
- unsigned i = skt->nr;
-
- if (i >= num_nano_pcmcia_sockets)
- return;
-
- state->bvd1 = 1;
- state->bvd2 = 1;
- state->vs_3v = 1; /* Can only apply 3.3V */
- state->vs_Xv = 0;
-}
-
-static struct pcmcia_low_level nanoengine_pcmcia_ops = {
- .owner = THIS_MODULE,
-
- .hw_init = nanoengine_pcmcia_hw_init,
- .hw_shutdown = nanoengine_pcmcia_hw_shutdown,
-
- .configure_socket = nanoengine_pcmcia_configure_socket,
- .socket_state = nanoengine_pcmcia_socket_state,
-};
-
-int pcmcia_nanoengine_init(struct device *dev)
-{
- int ret = -ENODEV;
-
- if (machine_is_nanoengine())
- ret = sa11xx_drv_pcmcia_probe(
- dev, &nanoengine_pcmcia_ops, 0, 2);
-
- return ret;
-}
-
diff --git a/drivers/pcmcia/sa1100_shannon.c b/drivers/pcmcia/sa1100_shannon.c
deleted file mode 100644
index 0e52a575986e..000000000000
--- a/drivers/pcmcia/sa1100_shannon.c
+++ /dev/null
@@ -1,104 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * drivers/pcmcia/sa1100_shannon.c
- *
- * PCMCIA implementation routines for Shannon
- *
- */
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/device.h>
-#include <linux/init.h>
-#include <linux/io.h>
-
-#include <mach/hardware.h>
-#include <asm/mach-types.h>
-#include <mach/shannon.h>
-#include <asm/irq.h>
-#include "sa1100_generic.h"
-
-static int shannon_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
-{
- /* All those are inputs */
- GAFR &= ~(GPIO_GPIO(SHANNON_GPIO_EJECT_0) |
- GPIO_GPIO(SHANNON_GPIO_EJECT_1) |
- GPIO_GPIO(SHANNON_GPIO_RDY_0) |
- GPIO_GPIO(SHANNON_GPIO_RDY_1));
-
- if (skt->nr == 0) {
- skt->stat[SOC_STAT_CD].gpio = SHANNON_GPIO_EJECT_0;
- skt->stat[SOC_STAT_CD].name = "PCMCIA_CD_0";
- skt->stat[SOC_STAT_RDY].gpio = SHANNON_GPIO_RDY_0;
- skt->stat[SOC_STAT_RDY].name = "PCMCIA_RDY_0";
- } else {
- skt->stat[SOC_STAT_CD].gpio = SHANNON_GPIO_EJECT_1;
- skt->stat[SOC_STAT_CD].name = "PCMCIA_CD_1";
- skt->stat[SOC_STAT_RDY].gpio = SHANNON_GPIO_RDY_1;
- skt->stat[SOC_STAT_RDY].name = "PCMCIA_RDY_1";
- }
-
- return 0;
-}
-
-static void
-shannon_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
- struct pcmcia_state *state)
-{
- switch (skt->nr) {
- case 0:
- state->bvd1 = 1;
- state->bvd2 = 1;
- state->vs_3v = 1; /* FIXME Can only apply 3.3V on Shannon. */
- state->vs_Xv = 0;
- break;
-
- case 1:
- state->bvd1 = 1;
- state->bvd2 = 1;
- state->vs_3v = 1; /* FIXME Can only apply 3.3V on Shannon. */
- state->vs_Xv = 0;
- break;
- }
-}
-
-static int
-shannon_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
- const socket_state_t *state)
-{
- switch (state->Vcc) {
- case 0: /* power off */
- printk(KERN_WARNING "%s(): CS asked for 0V, still applying 3.3V..\n", __func__);
- break;
- case 50:
- printk(KERN_WARNING "%s(): CS asked for 5V, applying 3.3V..\n", __func__);
- case 33:
- break;
- default:
- printk(KERN_ERR "%s(): unrecognized Vcc %u\n",
- __func__, state->Vcc);
- return -1;
- }
-
- printk(KERN_WARNING "%s(): Warning, Can't perform reset\n", __func__);
-
- /* Silently ignore Vpp, output enable, speaker enable. */
-
- return 0;
-}
-
-static struct pcmcia_low_level shannon_pcmcia_ops = {
- .owner = THIS_MODULE,
- .hw_init = shannon_pcmcia_hw_init,
- .socket_state = shannon_pcmcia_socket_state,
- .configure_socket = shannon_pcmcia_configure_socket,
-};
-
-int pcmcia_shannon_init(struct device *dev)
-{
- int ret = -ENODEV;
-
- if (machine_is_shannon())
- ret = sa11xx_drv_pcmcia_probe(dev, &shannon_pcmcia_ops, 0, 2);
-
- return ret;
-}
diff --git a/drivers/pcmcia/sa1100_simpad.c b/drivers/pcmcia/sa1100_simpad.c
index 7ce65bb23a8e..e235ee14eaa6 100644
--- a/drivers/pcmcia/sa1100_simpad.c
+++ b/drivers/pcmcia/sa1100_simpad.c
@@ -12,7 +12,6 @@
#include <mach/hardware.h>
#include <asm/mach-types.h>
-#include <asm/irq.h>
#include <mach/simpad.h>
#include "sa1100_generic.h"
@@ -21,12 +20,10 @@ static int simpad_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
simpad_clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1);
- skt->stat[SOC_STAT_CD].gpio = GPIO_CF_CD;
- skt->stat[SOC_STAT_CD].name = "CF_CD";
- skt->stat[SOC_STAT_RDY].gpio = GPIO_CF_IRQ;
- skt->stat[SOC_STAT_RDY].name = "CF_RDY";
+ skt->stat[SOC_STAT_CD].name = "cf-detect";
+ skt->stat[SOC_STAT_RDY].name = "cf-ready";
- return 0;
+ return soc_pcmcia_request_gpiods(skt);
}
static void simpad_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
@@ -42,9 +39,6 @@ simpad_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
{
long cs3reg = simpad_get_cs3_ro();
- /* the detect signal is inverted - fix that up here */
- state->detect = !state->detect;
-
state->bvd1 = 1; /* Might be cs3reg & PCMCIA_BVD1 */
state->bvd2 = 1; /* Might be cs3reg & PCMCIA_BVD2 */
diff --git a/drivers/perf/Kconfig b/drivers/perf/Kconfig
index da5724cd89cf..28bb5a029558 100644
--- a/drivers/perf/Kconfig
+++ b/drivers/perf/Kconfig
@@ -5,6 +5,39 @@
menu "Performance monitor support"
depends on PERF_EVENTS
+config ARM_CCI_PMU
+ bool
+ select ARM_CCI
+
+config ARM_CCI400_PMU
+ bool "ARM CCI400 PMU support"
+ depends on (ARM && CPU_V7) || ARM64
+ select ARM_CCI400_COMMON
+ select ARM_CCI_PMU
+ help
+ Support for PMU events monitoring on the ARM CCI-400 (cache coherent
+ interconnect). CCI-400 supports counting events related to the
+ connected slave/master interfaces.
+
+config ARM_CCI5xx_PMU
+ bool "ARM CCI-500/CCI-550 PMU support"
+ depends on (ARM && CPU_V7) || ARM64
+ select ARM_CCI_PMU
+ help
+ Support for PMU events monitoring on the ARM CCI-500/CCI-550 cache
+ coherent interconnects. Both of them provide 8 independent event counters,
+ which can count events pertaining to the slave/master interfaces as well
+ as the internal events to the CCI.
+
+ If unsure, say Y
+
+config ARM_CCN
+ tristate "ARM CCN driver support"
+ depends on ARM || ARM64
+ help
+ PMU (perf) driver supporting the ARM CCN (Cache Coherent Network)
+ interconnect.
+
config ARM_PMU
depends on ARM || ARM64
bool "ARM PMU framework"
diff --git a/drivers/perf/Makefile b/drivers/perf/Makefile
index c2f27419bdf0..b3902bd37d53 100644
--- a/drivers/perf/Makefile
+++ b/drivers/perf/Makefile
@@ -1,4 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_ARM_CCI_PMU) += arm-cci.o
+obj-$(CONFIG_ARM_CCN) += arm-ccn.o
obj-$(CONFIG_ARM_DSU_PMU) += arm_dsu_pmu.o
obj-$(CONFIG_ARM_PMU) += arm_pmu.o arm_pmu_platform.o
obj-$(CONFIG_ARM_PMU_ACPI) += arm_pmu_acpi.o
diff --git a/drivers/perf/arm-cci.c b/drivers/perf/arm-cci.c
new file mode 100644
index 000000000000..383b2d3dcbc6
--- /dev/null
+++ b/drivers/perf/arm-cci.c
@@ -0,0 +1,1722 @@
+// SPDX-License-Identifier: GPL-2.0
+// CCI Cache Coherent Interconnect PMU driver
+// Copyright (C) 2013-2018 Arm Ltd.
+// Author: Punit Agrawal <punit.agrawal@arm.com>, Suzuki Poulose <suzuki.poulose@arm.com>
+
+#include <linux/arm-cci.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/perf_event.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#define DRIVER_NAME "ARM-CCI PMU"
+
+#define CCI_PMCR 0x0100
+#define CCI_PID2 0x0fe8
+
+#define CCI_PMCR_CEN 0x00000001
+#define CCI_PMCR_NCNT_MASK 0x0000f800
+#define CCI_PMCR_NCNT_SHIFT 11
+
+#define CCI_PID2_REV_MASK 0xf0
+#define CCI_PID2_REV_SHIFT 4
+
+#define CCI_PMU_EVT_SEL 0x000
+#define CCI_PMU_CNTR 0x004
+#define CCI_PMU_CNTR_CTRL 0x008
+#define CCI_PMU_OVRFLW 0x00c
+
+#define CCI_PMU_OVRFLW_FLAG 1
+
+#define CCI_PMU_CNTR_SIZE(model) ((model)->cntr_size)
+#define CCI_PMU_CNTR_BASE(model, idx) ((idx) * CCI_PMU_CNTR_SIZE(model))
+#define CCI_PMU_CNTR_MASK ((1ULL << 32) -1)
+#define CCI_PMU_CNTR_LAST(cci_pmu) (cci_pmu->num_cntrs - 1)
+
+#define CCI_PMU_MAX_HW_CNTRS(model) \
+ ((model)->num_hw_cntrs + (model)->fixed_hw_cntrs)
+
+/* Types of interfaces that can generate events */
+enum {
+ CCI_IF_SLAVE,
+ CCI_IF_MASTER,
+#ifdef CONFIG_ARM_CCI5xx_PMU
+ CCI_IF_GLOBAL,
+#endif
+ CCI_IF_MAX,
+};
+
+struct event_range {
+ u32 min;
+ u32 max;
+};
+
+struct cci_pmu_hw_events {
+ struct perf_event **events;
+ unsigned long *used_mask;
+ raw_spinlock_t pmu_lock;
+};
+
+struct cci_pmu;
+/*
+ * struct cci_pmu_model:
+ * @fixed_hw_cntrs - Number of fixed event counters
+ * @num_hw_cntrs - Maximum number of programmable event counters
+ * @cntr_size - Size of an event counter mapping
+ */
+struct cci_pmu_model {
+ char *name;
+ u32 fixed_hw_cntrs;
+ u32 num_hw_cntrs;
+ u32 cntr_size;
+ struct attribute **format_attrs;
+ struct attribute **event_attrs;
+ struct event_range event_ranges[CCI_IF_MAX];
+ int (*validate_hw_event)(struct cci_pmu *, unsigned long);
+ int (*get_event_idx)(struct cci_pmu *, struct cci_pmu_hw_events *, unsigned long);
+ void (*write_counters)(struct cci_pmu *, unsigned long *);
+};
+
+static struct cci_pmu_model cci_pmu_models[];
+
+struct cci_pmu {
+ void __iomem *base;
+ void __iomem *ctrl_base;
+ struct pmu pmu;
+ int cpu;
+ int nr_irqs;
+ int *irqs;
+ unsigned long active_irqs;
+ const struct cci_pmu_model *model;
+ struct cci_pmu_hw_events hw_events;
+ struct platform_device *plat_device;
+ int num_cntrs;
+ atomic_t active_events;
+ struct mutex reserve_mutex;
+};
+
+#define to_cci_pmu(c) (container_of(c, struct cci_pmu, pmu))
+
+static struct cci_pmu *g_cci_pmu;
+
+enum cci_models {
+#ifdef CONFIG_ARM_CCI400_PMU
+ CCI400_R0,
+ CCI400_R1,
+#endif
+#ifdef CONFIG_ARM_CCI5xx_PMU
+ CCI500_R0,
+ CCI550_R0,
+#endif
+ CCI_MODEL_MAX
+};
+
+static void pmu_write_counters(struct cci_pmu *cci_pmu,
+ unsigned long *mask);
+static ssize_t cci_pmu_format_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t cci_pmu_event_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+#define CCI_EXT_ATTR_ENTRY(_name, _func, _config) \
+ &((struct dev_ext_attribute[]) { \
+ { __ATTR(_name, S_IRUGO, _func, NULL), (void *)_config } \
+ })[0].attr.attr
+
+#define CCI_FORMAT_EXT_ATTR_ENTRY(_name, _config) \
+ CCI_EXT_ATTR_ENTRY(_name, cci_pmu_format_show, (char *)_config)
+#define CCI_EVENT_EXT_ATTR_ENTRY(_name, _config) \
+ CCI_EXT_ATTR_ENTRY(_name, cci_pmu_event_show, (unsigned long)_config)
+
+/* CCI400 PMU Specific definitions */
+
+#ifdef CONFIG_ARM_CCI400_PMU
+
+/* Port ids */
+#define CCI400_PORT_S0 0
+#define CCI400_PORT_S1 1
+#define CCI400_PORT_S2 2
+#define CCI400_PORT_S3 3
+#define CCI400_PORT_S4 4
+#define CCI400_PORT_M0 5
+#define CCI400_PORT_M1 6
+#define CCI400_PORT_M2 7
+
+#define CCI400_R1_PX 5
+
+/*
+ * Instead of an event id to monitor CCI cycles, a dedicated counter is
+ * provided. Use 0xff to represent CCI cycles and hope that no future revisions
+ * make use of this event in hardware.
+ */
+enum cci400_perf_events {
+ CCI400_PMU_CYCLES = 0xff
+};
+
+#define CCI400_PMU_CYCLE_CNTR_IDX 0
+#define CCI400_PMU_CNTR0_IDX 1
+
+/*
+ * CCI PMU event id is an 8-bit value made of two parts - bits 7:5 for one of 8
+ * ports and bits 4:0 are event codes. There are different event codes
+ * associated with each port type.
+ *
+ * Additionally, the range of events associated with the port types changed
+ * between Rev0 and Rev1.
+ *
+ * The constants below define the range of valid codes for each port type for
+ * the different revisions and are used to validate the event to be monitored.
+ */
+
+#define CCI400_PMU_EVENT_MASK 0xffUL
+#define CCI400_PMU_EVENT_SOURCE_SHIFT 5
+#define CCI400_PMU_EVENT_SOURCE_MASK 0x7
+#define CCI400_PMU_EVENT_CODE_SHIFT 0
+#define CCI400_PMU_EVENT_CODE_MASK 0x1f
+#define CCI400_PMU_EVENT_SOURCE(event) \
+ ((event >> CCI400_PMU_EVENT_SOURCE_SHIFT) & \
+ CCI400_PMU_EVENT_SOURCE_MASK)
+#define CCI400_PMU_EVENT_CODE(event) \
+ ((event >> CCI400_PMU_EVENT_CODE_SHIFT) & CCI400_PMU_EVENT_CODE_MASK)
+
+#define CCI400_R0_SLAVE_PORT_MIN_EV 0x00
+#define CCI400_R0_SLAVE_PORT_MAX_EV 0x13
+#define CCI400_R0_MASTER_PORT_MIN_EV 0x14
+#define CCI400_R0_MASTER_PORT_MAX_EV 0x1a
+
+#define CCI400_R1_SLAVE_PORT_MIN_EV 0x00
+#define CCI400_R1_SLAVE_PORT_MAX_EV 0x14
+#define CCI400_R1_MASTER_PORT_MIN_EV 0x00
+#define CCI400_R1_MASTER_PORT_MAX_EV 0x11
+
+#define CCI400_CYCLE_EVENT_EXT_ATTR_ENTRY(_name, _config) \
+ CCI_EXT_ATTR_ENTRY(_name, cci400_pmu_cycle_event_show, \
+ (unsigned long)_config)
+
+static ssize_t cci400_pmu_cycle_event_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static struct attribute *cci400_pmu_format_attrs[] = {
+ CCI_FORMAT_EXT_ATTR_ENTRY(event, "config:0-4"),
+ CCI_FORMAT_EXT_ATTR_ENTRY(source, "config:5-7"),
+ NULL
+};
+
+static struct attribute *cci400_r0_pmu_event_attrs[] = {
+ /* Slave events */
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_any, 0x0),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_device, 0x01),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_normal_or_nonshareable, 0x2),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_inner_or_outershareable, 0x3),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_cache_maintenance, 0x4),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_mem_barrier, 0x5),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_sync_barrier, 0x6),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_dvm_msg, 0x7),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_dvm_msg_sync, 0x8),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_stall_tt_full, 0x9),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_r_data_last_hs_snoop, 0xA),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_r_data_stall_rvalids_h_rready_l, 0xB),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_any, 0xC),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_device, 0xD),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_normal_or_nonshareable, 0xE),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_inner_or_outershare_wback_wclean, 0xF),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_write_unique, 0x10),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_write_line_unique, 0x11),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_evict, 0x12),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_stall_tt_full, 0x13),
+ /* Master events */
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_retry_speculative_fetch, 0x14),
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_addr_hazard, 0x15),
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_id_hazard, 0x16),
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_tt_full, 0x17),
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_barrier_hazard, 0x18),
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_wrq_stall_barrier_hazard, 0x19),
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_wrq_stall_tt_full, 0x1A),
+ /* Special event for cycles counter */
+ CCI400_CYCLE_EVENT_EXT_ATTR_ENTRY(cycles, 0xff),
+ NULL
+};
+
+static struct attribute *cci400_r1_pmu_event_attrs[] = {
+ /* Slave events */
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_any, 0x0),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_device, 0x01),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_normal_or_nonshareable, 0x2),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_inner_or_outershareable, 0x3),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_cache_maintenance, 0x4),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_mem_barrier, 0x5),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_sync_barrier, 0x6),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_dvm_msg, 0x7),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_dvm_msg_sync, 0x8),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_stall_tt_full, 0x9),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_r_data_last_hs_snoop, 0xA),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_r_data_stall_rvalids_h_rready_l, 0xB),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_any, 0xC),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_device, 0xD),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_normal_or_nonshareable, 0xE),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_inner_or_outershare_wback_wclean, 0xF),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_write_unique, 0x10),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_write_line_unique, 0x11),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_evict, 0x12),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_stall_tt_full, 0x13),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_stall_slave_id_hazard, 0x14),
+ /* Master events */
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_retry_speculative_fetch, 0x0),
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_stall_cycle_addr_hazard, 0x1),
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_master_id_hazard, 0x2),
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_hi_prio_rtq_full, 0x3),
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_barrier_hazard, 0x4),
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_wrq_stall_barrier_hazard, 0x5),
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_wrq_stall_wtq_full, 0x6),
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_low_prio_rtq_full, 0x7),
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_mid_prio_rtq_full, 0x8),
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_qvn_vn0, 0x9),
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_qvn_vn1, 0xA),
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_qvn_vn2, 0xB),
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall_qvn_vn3, 0xC),
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_wrq_stall_qvn_vn0, 0xD),
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_wrq_stall_qvn_vn1, 0xE),
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_wrq_stall_qvn_vn2, 0xF),
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_wrq_stall_qvn_vn3, 0x10),
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_wrq_unique_or_line_unique_addr_hazard, 0x11),
+ /* Special event for cycles counter */
+ CCI400_CYCLE_EVENT_EXT_ATTR_ENTRY(cycles, 0xff),
+ NULL
+};
+
+static ssize_t cci400_pmu_cycle_event_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dev_ext_attribute *eattr = container_of(attr,
+ struct dev_ext_attribute, attr);
+ return snprintf(buf, PAGE_SIZE, "config=0x%lx\n", (unsigned long)eattr->var);
+}
+
+static int cci400_get_event_idx(struct cci_pmu *cci_pmu,
+ struct cci_pmu_hw_events *hw,
+ unsigned long cci_event)
+{
+ int idx;
+
+ /* cycles event idx is fixed */
+ if (cci_event == CCI400_PMU_CYCLES) {
+ if (test_and_set_bit(CCI400_PMU_CYCLE_CNTR_IDX, hw->used_mask))
+ return -EAGAIN;
+
+ return CCI400_PMU_CYCLE_CNTR_IDX;
+ }
+
+ for (idx = CCI400_PMU_CNTR0_IDX; idx <= CCI_PMU_CNTR_LAST(cci_pmu); ++idx)
+ if (!test_and_set_bit(idx, hw->used_mask))
+ return idx;
+
+ /* No counters available */
+ return -EAGAIN;
+}
+
+static int cci400_validate_hw_event(struct cci_pmu *cci_pmu, unsigned long hw_event)
+{
+ u8 ev_source = CCI400_PMU_EVENT_SOURCE(hw_event);
+ u8 ev_code = CCI400_PMU_EVENT_CODE(hw_event);
+ int if_type;
+
+ if (hw_event & ~CCI400_PMU_EVENT_MASK)
+ return -ENOENT;
+
+ if (hw_event == CCI400_PMU_CYCLES)
+ return hw_event;
+
+ switch (ev_source) {
+ case CCI400_PORT_S0:
+ case CCI400_PORT_S1:
+ case CCI400_PORT_S2:
+ case CCI400_PORT_S3:
+ case CCI400_PORT_S4:
+ /* Slave Interface */
+ if_type = CCI_IF_SLAVE;
+ break;
+ case CCI400_PORT_M0:
+ case CCI400_PORT_M1:
+ case CCI400_PORT_M2:
+ /* Master Interface */
+ if_type = CCI_IF_MASTER;
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ if (ev_code >= cci_pmu->model->event_ranges[if_type].min &&
+ ev_code <= cci_pmu->model->event_ranges[if_type].max)
+ return hw_event;
+
+ return -ENOENT;
+}
+
+static int probe_cci400_revision(struct cci_pmu *cci_pmu)
+{
+ int rev;
+ rev = readl_relaxed(cci_pmu->ctrl_base + CCI_PID2) & CCI_PID2_REV_MASK;
+ rev >>= CCI_PID2_REV_SHIFT;
+
+ if (rev < CCI400_R1_PX)
+ return CCI400_R0;
+ else
+ return CCI400_R1;
+}
+
+static const struct cci_pmu_model *probe_cci_model(struct cci_pmu *cci_pmu)
+{
+ if (platform_has_secure_cci_access())
+ return &cci_pmu_models[probe_cci400_revision(cci_pmu)];
+ return NULL;
+}
+#else /* !CONFIG_ARM_CCI400_PMU */
+static inline struct cci_pmu_model *probe_cci_model(struct cci_pmu *cci_pmu)
+{
+ return NULL;
+}
+#endif /* CONFIG_ARM_CCI400_PMU */
+
+#ifdef CONFIG_ARM_CCI5xx_PMU
+
+/*
+ * CCI5xx PMU event id is an 9-bit value made of two parts.
+ * bits [8:5] - Source for the event
+ * bits [4:0] - Event code (specific to type of interface)
+ *
+ *
+ */
+
+/* Port ids */
+#define CCI5xx_PORT_S0 0x0
+#define CCI5xx_PORT_S1 0x1
+#define CCI5xx_PORT_S2 0x2
+#define CCI5xx_PORT_S3 0x3
+#define CCI5xx_PORT_S4 0x4
+#define CCI5xx_PORT_S5 0x5
+#define CCI5xx_PORT_S6 0x6
+
+#define CCI5xx_PORT_M0 0x8
+#define CCI5xx_PORT_M1 0x9
+#define CCI5xx_PORT_M2 0xa
+#define CCI5xx_PORT_M3 0xb
+#define CCI5xx_PORT_M4 0xc
+#define CCI5xx_PORT_M5 0xd
+#define CCI5xx_PORT_M6 0xe
+
+#define CCI5xx_PORT_GLOBAL 0xf
+
+#define CCI5xx_PMU_EVENT_MASK 0x1ffUL
+#define CCI5xx_PMU_EVENT_SOURCE_SHIFT 0x5
+#define CCI5xx_PMU_EVENT_SOURCE_MASK 0xf
+#define CCI5xx_PMU_EVENT_CODE_SHIFT 0x0
+#define CCI5xx_PMU_EVENT_CODE_MASK 0x1f
+
+#define CCI5xx_PMU_EVENT_SOURCE(event) \
+ ((event >> CCI5xx_PMU_EVENT_SOURCE_SHIFT) & CCI5xx_PMU_EVENT_SOURCE_MASK)
+#define CCI5xx_PMU_EVENT_CODE(event) \
+ ((event >> CCI5xx_PMU_EVENT_CODE_SHIFT) & CCI5xx_PMU_EVENT_CODE_MASK)
+
+#define CCI5xx_SLAVE_PORT_MIN_EV 0x00
+#define CCI5xx_SLAVE_PORT_MAX_EV 0x1f
+#define CCI5xx_MASTER_PORT_MIN_EV 0x00
+#define CCI5xx_MASTER_PORT_MAX_EV 0x06
+#define CCI5xx_GLOBAL_PORT_MIN_EV 0x00
+#define CCI5xx_GLOBAL_PORT_MAX_EV 0x0f
+
+
+#define CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(_name, _config) \
+ CCI_EXT_ATTR_ENTRY(_name, cci5xx_pmu_global_event_show, \
+ (unsigned long) _config)
+
+static ssize_t cci5xx_pmu_global_event_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static struct attribute *cci5xx_pmu_format_attrs[] = {
+ CCI_FORMAT_EXT_ATTR_ENTRY(event, "config:0-4"),
+ CCI_FORMAT_EXT_ATTR_ENTRY(source, "config:5-8"),
+ NULL,
+};
+
+static struct attribute *cci5xx_pmu_event_attrs[] = {
+ /* Slave events */
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_arvalid, 0x0),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_dev, 0x1),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_nonshareable, 0x2),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_shareable_non_alloc, 0x3),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_shareable_alloc, 0x4),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_invalidate, 0x5),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_cache_maint, 0x6),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_dvm_msg, 0x7),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_rval, 0x8),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_hs_rlast_snoop, 0x9),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_hs_awalid, 0xA),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_dev, 0xB),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_non_shareable, 0xC),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_share_wb, 0xD),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_share_wlu, 0xE),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_share_wunique, 0xF),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_evict, 0x10),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_wrevict, 0x11),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_w_data_beat, 0x12),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_srq_acvalid, 0x13),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_srq_read, 0x14),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_srq_clean, 0x15),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_srq_data_transfer_low, 0x16),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rrq_stall_arvalid, 0x17),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_r_data_stall, 0x18),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_wrq_stall, 0x19),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_w_data_stall, 0x1A),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_w_resp_stall, 0x1B),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_srq_stall, 0x1C),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_s_data_stall, 0x1D),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_rq_stall_ot_limit, 0x1E),
+ CCI_EVENT_EXT_ATTR_ENTRY(si_r_stall_arbit, 0x1F),
+
+ /* Master events */
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_r_data_beat_any, 0x0),
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_w_data_beat_any, 0x1),
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_rrq_stall, 0x2),
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_r_data_stall, 0x3),
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_wrq_stall, 0x4),
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_w_data_stall, 0x5),
+ CCI_EVENT_EXT_ATTR_ENTRY(mi_w_resp_stall, 0x6),
+
+ /* Global events */
+ CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_access_filter_bank_0_1, 0x0),
+ CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_access_filter_bank_2_3, 0x1),
+ CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_access_filter_bank_4_5, 0x2),
+ CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_access_filter_bank_6_7, 0x3),
+ CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_access_miss_filter_bank_0_1, 0x4),
+ CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_access_miss_filter_bank_2_3, 0x5),
+ CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_access_miss_filter_bank_4_5, 0x6),
+ CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_access_miss_filter_bank_6_7, 0x7),
+ CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_back_invalidation, 0x8),
+ CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_stall_alloc_busy, 0x9),
+ CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_stall_tt_full, 0xA),
+ CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_wrq, 0xB),
+ CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_cd_hs, 0xC),
+ CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_rq_stall_addr_hazard, 0xD),
+ CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_rq_stall_tt_full, 0xE),
+ CCI5xx_GLOBAL_EVENT_EXT_ATTR_ENTRY(cci_snoop_rq_tzmp1_prot, 0xF),
+ NULL
+};
+
+static ssize_t cci5xx_pmu_global_event_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dev_ext_attribute *eattr = container_of(attr,
+ struct dev_ext_attribute, attr);
+ /* Global events have single fixed source code */
+ return snprintf(buf, PAGE_SIZE, "event=0x%lx,source=0x%x\n",
+ (unsigned long)eattr->var, CCI5xx_PORT_GLOBAL);
+}
+
+/*
+ * CCI500 provides 8 independent event counters that can count
+ * any of the events available.
+ * CCI500 PMU event source ids
+ * 0x0-0x6 - Slave interfaces
+ * 0x8-0xD - Master interfaces
+ * 0xf - Global Events
+ * 0x7,0xe - Reserved
+ */
+static int cci500_validate_hw_event(struct cci_pmu *cci_pmu,
+ unsigned long hw_event)
+{
+ u32 ev_source = CCI5xx_PMU_EVENT_SOURCE(hw_event);
+ u32 ev_code = CCI5xx_PMU_EVENT_CODE(hw_event);
+ int if_type;
+
+ if (hw_event & ~CCI5xx_PMU_EVENT_MASK)
+ return -ENOENT;
+
+ switch (ev_source) {
+ case CCI5xx_PORT_S0:
+ case CCI5xx_PORT_S1:
+ case CCI5xx_PORT_S2:
+ case CCI5xx_PORT_S3:
+ case CCI5xx_PORT_S4:
+ case CCI5xx_PORT_S5:
+ case CCI5xx_PORT_S6:
+ if_type = CCI_IF_SLAVE;
+ break;
+ case CCI5xx_PORT_M0:
+ case CCI5xx_PORT_M1:
+ case CCI5xx_PORT_M2:
+ case CCI5xx_PORT_M3:
+ case CCI5xx_PORT_M4:
+ case CCI5xx_PORT_M5:
+ if_type = CCI_IF_MASTER;
+ break;
+ case CCI5xx_PORT_GLOBAL:
+ if_type = CCI_IF_GLOBAL;
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ if (ev_code >= cci_pmu->model->event_ranges[if_type].min &&
+ ev_code <= cci_pmu->model->event_ranges[if_type].max)
+ return hw_event;
+
+ return -ENOENT;
+}
+
+/*
+ * CCI550 provides 8 independent event counters that can count
+ * any of the events available.
+ * CCI550 PMU event source ids
+ * 0x0-0x6 - Slave interfaces
+ * 0x8-0xe - Master interfaces
+ * 0xf - Global Events
+ * 0x7 - Reserved
+ */
+static int cci550_validate_hw_event(struct cci_pmu *cci_pmu,
+ unsigned long hw_event)
+{
+ u32 ev_source = CCI5xx_PMU_EVENT_SOURCE(hw_event);
+ u32 ev_code = CCI5xx_PMU_EVENT_CODE(hw_event);
+ int if_type;
+
+ if (hw_event & ~CCI5xx_PMU_EVENT_MASK)
+ return -ENOENT;
+
+ switch (ev_source) {
+ case CCI5xx_PORT_S0:
+ case CCI5xx_PORT_S1:
+ case CCI5xx_PORT_S2:
+ case CCI5xx_PORT_S3:
+ case CCI5xx_PORT_S4:
+ case CCI5xx_PORT_S5:
+ case CCI5xx_PORT_S6:
+ if_type = CCI_IF_SLAVE;
+ break;
+ case CCI5xx_PORT_M0:
+ case CCI5xx_PORT_M1:
+ case CCI5xx_PORT_M2:
+ case CCI5xx_PORT_M3:
+ case CCI5xx_PORT_M4:
+ case CCI5xx_PORT_M5:
+ case CCI5xx_PORT_M6:
+ if_type = CCI_IF_MASTER;
+ break;
+ case CCI5xx_PORT_GLOBAL:
+ if_type = CCI_IF_GLOBAL;
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ if (ev_code >= cci_pmu->model->event_ranges[if_type].min &&
+ ev_code <= cci_pmu->model->event_ranges[if_type].max)
+ return hw_event;
+
+ return -ENOENT;
+}
+
+#endif /* CONFIG_ARM_CCI5xx_PMU */
+
+/*
+ * Program the CCI PMU counters which have PERF_HES_ARCH set
+ * with the event period and mark them ready before we enable
+ * PMU.
+ */
+static void cci_pmu_sync_counters(struct cci_pmu *cci_pmu)
+{
+ int i;
+ struct cci_pmu_hw_events *cci_hw = &cci_pmu->hw_events;
+
+ DECLARE_BITMAP(mask, cci_pmu->num_cntrs);
+
+ bitmap_zero(mask, cci_pmu->num_cntrs);
+ for_each_set_bit(i, cci_pmu->hw_events.used_mask, cci_pmu->num_cntrs) {
+ struct perf_event *event = cci_hw->events[i];
+
+ if (WARN_ON(!event))
+ continue;
+
+ /* Leave the events which are not counting */
+ if (event->hw.state & PERF_HES_STOPPED)
+ continue;
+ if (event->hw.state & PERF_HES_ARCH) {
+ set_bit(i, mask);
+ event->hw.state &= ~PERF_HES_ARCH;
+ }
+ }
+
+ pmu_write_counters(cci_pmu, mask);
+}
+
+/* Should be called with cci_pmu->hw_events->pmu_lock held */
+static void __cci_pmu_enable_nosync(struct cci_pmu *cci_pmu)
+{
+ u32 val;
+
+ /* Enable all the PMU counters. */
+ val = readl_relaxed(cci_pmu->ctrl_base + CCI_PMCR) | CCI_PMCR_CEN;
+ writel(val, cci_pmu->ctrl_base + CCI_PMCR);
+}
+
+/* Should be called with cci_pmu->hw_events->pmu_lock held */
+static void __cci_pmu_enable_sync(struct cci_pmu *cci_pmu)
+{
+ cci_pmu_sync_counters(cci_pmu);
+ __cci_pmu_enable_nosync(cci_pmu);
+}
+
+/* Should be called with cci_pmu->hw_events->pmu_lock held */
+static void __cci_pmu_disable(struct cci_pmu *cci_pmu)
+{
+ u32 val;
+
+ /* Disable all the PMU counters. */
+ val = readl_relaxed(cci_pmu->ctrl_base + CCI_PMCR) & ~CCI_PMCR_CEN;
+ writel(val, cci_pmu->ctrl_base + CCI_PMCR);
+}
+
+static ssize_t cci_pmu_format_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dev_ext_attribute *eattr = container_of(attr,
+ struct dev_ext_attribute, attr);
+ return snprintf(buf, PAGE_SIZE, "%s\n", (char *)eattr->var);
+}
+
+static ssize_t cci_pmu_event_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dev_ext_attribute *eattr = container_of(attr,
+ struct dev_ext_attribute, attr);
+ /* source parameter is mandatory for normal PMU events */
+ return snprintf(buf, PAGE_SIZE, "source=?,event=0x%lx\n",
+ (unsigned long)eattr->var);
+}
+
+static int pmu_is_valid_counter(struct cci_pmu *cci_pmu, int idx)
+{
+ return 0 <= idx && idx <= CCI_PMU_CNTR_LAST(cci_pmu);
+}
+
+static u32 pmu_read_register(struct cci_pmu *cci_pmu, int idx, unsigned int offset)
+{
+ return readl_relaxed(cci_pmu->base +
+ CCI_PMU_CNTR_BASE(cci_pmu->model, idx) + offset);
+}
+
+static void pmu_write_register(struct cci_pmu *cci_pmu, u32 value,
+ int idx, unsigned int offset)
+{
+ writel_relaxed(value, cci_pmu->base +
+ CCI_PMU_CNTR_BASE(cci_pmu->model, idx) + offset);
+}
+
+static void pmu_disable_counter(struct cci_pmu *cci_pmu, int idx)
+{
+ pmu_write_register(cci_pmu, 0, idx, CCI_PMU_CNTR_CTRL);
+}
+
+static void pmu_enable_counter(struct cci_pmu *cci_pmu, int idx)
+{
+ pmu_write_register(cci_pmu, 1, idx, CCI_PMU_CNTR_CTRL);
+}
+
+static bool __maybe_unused
+pmu_counter_is_enabled(struct cci_pmu *cci_pmu, int idx)
+{
+ return (pmu_read_register(cci_pmu, idx, CCI_PMU_CNTR_CTRL) & 0x1) != 0;
+}
+
+static void pmu_set_event(struct cci_pmu *cci_pmu, int idx, unsigned long event)
+{
+ pmu_write_register(cci_pmu, event, idx, CCI_PMU_EVT_SEL);
+}
+
+/*
+ * For all counters on the CCI-PMU, disable any 'enabled' counters,
+ * saving the changed counters in the mask, so that we can restore
+ * it later using pmu_restore_counters. The mask is private to the
+ * caller. We cannot rely on the used_mask maintained by the CCI_PMU
+ * as it only tells us if the counter is assigned to perf_event or not.
+ * The state of the perf_event cannot be locked by the PMU layer, hence
+ * we check the individual counter status (which can be locked by
+ * cci_pm->hw_events->pmu_lock).
+ *
+ * @mask should be initialised to empty by the caller.
+ */
+static void __maybe_unused
+pmu_save_counters(struct cci_pmu *cci_pmu, unsigned long *mask)
+{
+ int i;
+
+ for (i = 0; i < cci_pmu->num_cntrs; i++) {
+ if (pmu_counter_is_enabled(cci_pmu, i)) {
+ set_bit(i, mask);
+ pmu_disable_counter(cci_pmu, i);
+ }
+ }
+}
+
+/*
+ * Restore the status of the counters. Reversal of the pmu_save_counters().
+ * For each counter set in the mask, enable the counter back.
+ */
+static void __maybe_unused
+pmu_restore_counters(struct cci_pmu *cci_pmu, unsigned long *mask)
+{
+ int i;
+
+ for_each_set_bit(i, mask, cci_pmu->num_cntrs)
+ pmu_enable_counter(cci_pmu, i);
+}
+
+/*
+ * Returns the number of programmable counters actually implemented
+ * by the cci
+ */
+static u32 pmu_get_max_counters(struct cci_pmu *cci_pmu)
+{
+ return (readl_relaxed(cci_pmu->ctrl_base + CCI_PMCR) &
+ CCI_PMCR_NCNT_MASK) >> CCI_PMCR_NCNT_SHIFT;
+}
+
+static int pmu_get_event_idx(struct cci_pmu_hw_events *hw, struct perf_event *event)
+{
+ struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu);
+ unsigned long cci_event = event->hw.config_base;
+ int idx;
+
+ if (cci_pmu->model->get_event_idx)
+ return cci_pmu->model->get_event_idx(cci_pmu, hw, cci_event);
+
+ /* Generic code to find an unused idx from the mask */
+ for(idx = 0; idx <= CCI_PMU_CNTR_LAST(cci_pmu); idx++)
+ if (!test_and_set_bit(idx, hw->used_mask))
+ return idx;
+
+ /* No counters available */
+ return -EAGAIN;
+}
+
+static int pmu_map_event(struct perf_event *event)
+{
+ struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu);
+
+ if (event->attr.type < PERF_TYPE_MAX ||
+ !cci_pmu->model->validate_hw_event)
+ return -ENOENT;
+
+ return cci_pmu->model->validate_hw_event(cci_pmu, event->attr.config);
+}
+
+static int pmu_request_irq(struct cci_pmu *cci_pmu, irq_handler_t handler)
+{
+ int i;
+ struct platform_device *pmu_device = cci_pmu->plat_device;
+
+ if (unlikely(!pmu_device))
+ return -ENODEV;
+
+ if (cci_pmu->nr_irqs < 1) {
+ dev_err(&pmu_device->dev, "no irqs for CCI PMUs defined\n");
+ return -ENODEV;
+ }
+
+ /*
+ * Register all available CCI PMU interrupts. In the interrupt handler
+ * we iterate over the counters checking for interrupt source (the
+ * overflowing counter) and clear it.
+ *
+ * This should allow handling of non-unique interrupt for the counters.
+ */
+ for (i = 0; i < cci_pmu->nr_irqs; i++) {
+ int err = request_irq(cci_pmu->irqs[i], handler, IRQF_SHARED,
+ "arm-cci-pmu", cci_pmu);
+ if (err) {
+ dev_err(&pmu_device->dev, "unable to request IRQ%d for ARM CCI PMU counters\n",
+ cci_pmu->irqs[i]);
+ return err;
+ }
+
+ set_bit(i, &cci_pmu->active_irqs);
+ }
+
+ return 0;
+}
+
+static void pmu_free_irq(struct cci_pmu *cci_pmu)
+{
+ int i;
+
+ for (i = 0; i < cci_pmu->nr_irqs; i++) {
+ if (!test_and_clear_bit(i, &cci_pmu->active_irqs))
+ continue;
+
+ free_irq(cci_pmu->irqs[i], cci_pmu);
+ }
+}
+
+static u32 pmu_read_counter(struct perf_event *event)
+{
+ struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu);
+ struct hw_perf_event *hw_counter = &event->hw;
+ int idx = hw_counter->idx;
+ u32 value;
+
+ if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) {
+ dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx);
+ return 0;
+ }
+ value = pmu_read_register(cci_pmu, idx, CCI_PMU_CNTR);
+
+ return value;
+}
+
+static void pmu_write_counter(struct cci_pmu *cci_pmu, u32 value, int idx)
+{
+ pmu_write_register(cci_pmu, value, idx, CCI_PMU_CNTR);
+}
+
+static void __pmu_write_counters(struct cci_pmu *cci_pmu, unsigned long *mask)
+{
+ int i;
+ struct cci_pmu_hw_events *cci_hw = &cci_pmu->hw_events;
+
+ for_each_set_bit(i, mask, cci_pmu->num_cntrs) {
+ struct perf_event *event = cci_hw->events[i];
+
+ if (WARN_ON(!event))
+ continue;
+ pmu_write_counter(cci_pmu, local64_read(&event->hw.prev_count), i);
+ }
+}
+
+static void pmu_write_counters(struct cci_pmu *cci_pmu, unsigned long *mask)
+{
+ if (cci_pmu->model->write_counters)
+ cci_pmu->model->write_counters(cci_pmu, mask);
+ else
+ __pmu_write_counters(cci_pmu, mask);
+}
+
+#ifdef CONFIG_ARM_CCI5xx_PMU
+
+/*
+ * CCI-500/CCI-550 has advanced power saving policies, which could gate the
+ * clocks to the PMU counters, which makes the writes to them ineffective.
+ * The only way to write to those counters is when the global counters
+ * are enabled and the particular counter is enabled.
+ *
+ * So we do the following :
+ *
+ * 1) Disable all the PMU counters, saving their current state
+ * 2) Enable the global PMU profiling, now that all counters are
+ * disabled.
+ *
+ * For each counter to be programmed, repeat steps 3-7:
+ *
+ * 3) Write an invalid event code to the event control register for the
+ counter, so that the counters are not modified.
+ * 4) Enable the counter control for the counter.
+ * 5) Set the counter value
+ * 6) Disable the counter
+ * 7) Restore the event in the target counter
+ *
+ * 8) Disable the global PMU.
+ * 9) Restore the status of the rest of the counters.
+ *
+ * We choose an event which for CCI-5xx is guaranteed not to count.
+ * We use the highest possible event code (0x1f) for the master interface 0.
+ */
+#define CCI5xx_INVALID_EVENT ((CCI5xx_PORT_M0 << CCI5xx_PMU_EVENT_SOURCE_SHIFT) | \
+ (CCI5xx_PMU_EVENT_CODE_MASK << CCI5xx_PMU_EVENT_CODE_SHIFT))
+static void cci5xx_pmu_write_counters(struct cci_pmu *cci_pmu, unsigned long *mask)
+{
+ int i;
+ DECLARE_BITMAP(saved_mask, cci_pmu->num_cntrs);
+
+ bitmap_zero(saved_mask, cci_pmu->num_cntrs);
+ pmu_save_counters(cci_pmu, saved_mask);
+
+ /*
+ * Now that all the counters are disabled, we can safely turn the PMU on,
+ * without syncing the status of the counters
+ */
+ __cci_pmu_enable_nosync(cci_pmu);
+
+ for_each_set_bit(i, mask, cci_pmu->num_cntrs) {
+ struct perf_event *event = cci_pmu->hw_events.events[i];
+
+ if (WARN_ON(!event))
+ continue;
+
+ pmu_set_event(cci_pmu, i, CCI5xx_INVALID_EVENT);
+ pmu_enable_counter(cci_pmu, i);
+ pmu_write_counter(cci_pmu, local64_read(&event->hw.prev_count), i);
+ pmu_disable_counter(cci_pmu, i);
+ pmu_set_event(cci_pmu, i, event->hw.config_base);
+ }
+
+ __cci_pmu_disable(cci_pmu);
+
+ pmu_restore_counters(cci_pmu, saved_mask);
+}
+
+#endif /* CONFIG_ARM_CCI5xx_PMU */
+
+static u64 pmu_event_update(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ u64 delta, prev_raw_count, new_raw_count;
+
+ do {
+ prev_raw_count = local64_read(&hwc->prev_count);
+ new_raw_count = pmu_read_counter(event);
+ } while (local64_cmpxchg(&hwc->prev_count, prev_raw_count,
+ new_raw_count) != prev_raw_count);
+
+ delta = (new_raw_count - prev_raw_count) & CCI_PMU_CNTR_MASK;
+
+ local64_add(delta, &event->count);
+
+ return new_raw_count;
+}
+
+static void pmu_read(struct perf_event *event)
+{
+ pmu_event_update(event);
+}
+
+static void pmu_event_set_period(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ /*
+ * The CCI PMU counters have a period of 2^32. To account for the
+ * possiblity of extreme interrupt latency we program for a period of
+ * half that. Hopefully we can handle the interrupt before another 2^31
+ * events occur and the counter overtakes its previous value.
+ */
+ u64 val = 1ULL << 31;
+ local64_set(&hwc->prev_count, val);
+
+ /*
+ * CCI PMU uses PERF_HES_ARCH to keep track of the counters, whose
+ * values needs to be sync-ed with the s/w state before the PMU is
+ * enabled.
+ * Mark this counter for sync.
+ */
+ hwc->state |= PERF_HES_ARCH;
+}
+
+static irqreturn_t pmu_handle_irq(int irq_num, void *dev)
+{
+ unsigned long flags;
+ struct cci_pmu *cci_pmu = dev;
+ struct cci_pmu_hw_events *events = &cci_pmu->hw_events;
+ int idx, handled = IRQ_NONE;
+
+ raw_spin_lock_irqsave(&events->pmu_lock, flags);
+
+ /* Disable the PMU while we walk through the counters */
+ __cci_pmu_disable(cci_pmu);
+ /*
+ * Iterate over counters and update the corresponding perf events.
+ * This should work regardless of whether we have per-counter overflow
+ * interrupt or a combined overflow interrupt.
+ */
+ for (idx = 0; idx <= CCI_PMU_CNTR_LAST(cci_pmu); idx++) {
+ struct perf_event *event = events->events[idx];
+
+ if (!event)
+ continue;
+
+ /* Did this counter overflow? */
+ if (!(pmu_read_register(cci_pmu, idx, CCI_PMU_OVRFLW) &
+ CCI_PMU_OVRFLW_FLAG))
+ continue;
+
+ pmu_write_register(cci_pmu, CCI_PMU_OVRFLW_FLAG, idx,
+ CCI_PMU_OVRFLW);
+
+ pmu_event_update(event);
+ pmu_event_set_period(event);
+ handled = IRQ_HANDLED;
+ }
+
+ /* Enable the PMU and sync possibly overflowed counters */
+ __cci_pmu_enable_sync(cci_pmu);
+ raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
+
+ return IRQ_RETVAL(handled);
+}
+
+static int cci_pmu_get_hw(struct cci_pmu *cci_pmu)
+{
+ int ret = pmu_request_irq(cci_pmu, pmu_handle_irq);
+ if (ret) {
+ pmu_free_irq(cci_pmu);
+ return ret;
+ }
+ return 0;
+}
+
+static void cci_pmu_put_hw(struct cci_pmu *cci_pmu)
+{
+ pmu_free_irq(cci_pmu);
+}
+
+static void hw_perf_event_destroy(struct perf_event *event)
+{
+ struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu);
+ atomic_t *active_events = &cci_pmu->active_events;
+ struct mutex *reserve_mutex = &cci_pmu->reserve_mutex;
+
+ if (atomic_dec_and_mutex_lock(active_events, reserve_mutex)) {
+ cci_pmu_put_hw(cci_pmu);
+ mutex_unlock(reserve_mutex);
+ }
+}
+
+static void cci_pmu_enable(struct pmu *pmu)
+{
+ struct cci_pmu *cci_pmu = to_cci_pmu(pmu);
+ struct cci_pmu_hw_events *hw_events = &cci_pmu->hw_events;
+ int enabled = bitmap_weight(hw_events->used_mask, cci_pmu->num_cntrs);
+ unsigned long flags;
+
+ if (!enabled)
+ return;
+
+ raw_spin_lock_irqsave(&hw_events->pmu_lock, flags);
+ __cci_pmu_enable_sync(cci_pmu);
+ raw_spin_unlock_irqrestore(&hw_events->pmu_lock, flags);
+
+}
+
+static void cci_pmu_disable(struct pmu *pmu)
+{
+ struct cci_pmu *cci_pmu = to_cci_pmu(pmu);
+ struct cci_pmu_hw_events *hw_events = &cci_pmu->hw_events;
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&hw_events->pmu_lock, flags);
+ __cci_pmu_disable(cci_pmu);
+ raw_spin_unlock_irqrestore(&hw_events->pmu_lock, flags);
+}
+
+/*
+ * Check if the idx represents a non-programmable counter.
+ * All the fixed event counters are mapped before the programmable
+ * counters.
+ */
+static bool pmu_fixed_hw_idx(struct cci_pmu *cci_pmu, int idx)
+{
+ return (idx >= 0) && (idx < cci_pmu->model->fixed_hw_cntrs);
+}
+
+static void cci_pmu_start(struct perf_event *event, int pmu_flags)
+{
+ struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu);
+ struct cci_pmu_hw_events *hw_events = &cci_pmu->hw_events;
+ struct hw_perf_event *hwc = &event->hw;
+ int idx = hwc->idx;
+ unsigned long flags;
+
+ /*
+ * To handle interrupt latency, we always reprogram the period
+ * regardlesss of PERF_EF_RELOAD.
+ */
+ if (pmu_flags & PERF_EF_RELOAD)
+ WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
+
+ hwc->state = 0;
+
+ if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) {
+ dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx);
+ return;
+ }
+
+ raw_spin_lock_irqsave(&hw_events->pmu_lock, flags);
+
+ /* Configure the counter unless you are counting a fixed event */
+ if (!pmu_fixed_hw_idx(cci_pmu, idx))
+ pmu_set_event(cci_pmu, idx, hwc->config_base);
+
+ pmu_event_set_period(event);
+ pmu_enable_counter(cci_pmu, idx);
+
+ raw_spin_unlock_irqrestore(&hw_events->pmu_lock, flags);
+}
+
+static void cci_pmu_stop(struct perf_event *event, int pmu_flags)
+{
+ struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu);
+ struct hw_perf_event *hwc = &event->hw;
+ int idx = hwc->idx;
+
+ if (hwc->state & PERF_HES_STOPPED)
+ return;
+
+ if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) {
+ dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx);
+ return;
+ }
+
+ /*
+ * We always reprogram the counter, so ignore PERF_EF_UPDATE. See
+ * cci_pmu_start()
+ */
+ pmu_disable_counter(cci_pmu, idx);
+ pmu_event_update(event);
+ hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;
+}
+
+static int cci_pmu_add(struct perf_event *event, int flags)
+{
+ struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu);
+ struct cci_pmu_hw_events *hw_events = &cci_pmu->hw_events;
+ struct hw_perf_event *hwc = &event->hw;
+ int idx;
+ int err = 0;
+
+ perf_pmu_disable(event->pmu);
+
+ /* If we don't have a space for the counter then finish early. */
+ idx = pmu_get_event_idx(hw_events, event);
+ if (idx < 0) {
+ err = idx;
+ goto out;
+ }
+
+ event->hw.idx = idx;
+ hw_events->events[idx] = event;
+
+ hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE;
+ if (flags & PERF_EF_START)
+ cci_pmu_start(event, PERF_EF_RELOAD);
+
+ /* Propagate our changes to the userspace mapping. */
+ perf_event_update_userpage(event);
+
+out:
+ perf_pmu_enable(event->pmu);
+ return err;
+}
+
+static void cci_pmu_del(struct perf_event *event, int flags)
+{
+ struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu);
+ struct cci_pmu_hw_events *hw_events = &cci_pmu->hw_events;
+ struct hw_perf_event *hwc = &event->hw;
+ int idx = hwc->idx;
+
+ cci_pmu_stop(event, PERF_EF_UPDATE);
+ hw_events->events[idx] = NULL;
+ clear_bit(idx, hw_events->used_mask);
+
+ perf_event_update_userpage(event);
+}
+
+static int validate_event(struct pmu *cci_pmu,
+ struct cci_pmu_hw_events *hw_events,
+ struct perf_event *event)
+{
+ if (is_software_event(event))
+ return 1;
+
+ /*
+ * Reject groups spanning multiple HW PMUs (e.g. CPU + CCI). The
+ * core perf code won't check that the pmu->ctx == leader->ctx
+ * until after pmu->event_init(event).
+ */
+ if (event->pmu != cci_pmu)
+ return 0;
+
+ if (event->state < PERF_EVENT_STATE_OFF)
+ return 1;
+
+ if (event->state == PERF_EVENT_STATE_OFF && !event->attr.enable_on_exec)
+ return 1;
+
+ return pmu_get_event_idx(hw_events, event) >= 0;
+}
+
+static int validate_group(struct perf_event *event)
+{
+ struct perf_event *sibling, *leader = event->group_leader;
+ struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu);
+ unsigned long mask[BITS_TO_LONGS(cci_pmu->num_cntrs)];
+ struct cci_pmu_hw_events fake_pmu = {
+ /*
+ * Initialise the fake PMU. We only need to populate the
+ * used_mask for the purposes of validation.
+ */
+ .used_mask = mask,
+ };
+ memset(mask, 0, BITS_TO_LONGS(cci_pmu->num_cntrs) * sizeof(unsigned long));
+
+ if (!validate_event(event->pmu, &fake_pmu, leader))
+ return -EINVAL;
+
+ for_each_sibling_event(sibling, leader) {
+ if (!validate_event(event->pmu, &fake_pmu, sibling))
+ return -EINVAL;
+ }
+
+ if (!validate_event(event->pmu, &fake_pmu, event))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int __hw_perf_event_init(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ int mapping;
+
+ mapping = pmu_map_event(event);
+
+ if (mapping < 0) {
+ pr_debug("event %x:%llx not supported\n", event->attr.type,
+ event->attr.config);
+ return mapping;
+ }
+
+ /*
+ * We don't assign an index until we actually place the event onto
+ * hardware. Use -1 to signify that we haven't decided where to put it
+ * yet.
+ */
+ hwc->idx = -1;
+ hwc->config_base = 0;
+ hwc->config = 0;
+ hwc->event_base = 0;
+
+ /*
+ * Store the event encoding into the config_base field.
+ */
+ hwc->config_base |= (unsigned long)mapping;
+
+ /*
+ * Limit the sample_period to half of the counter width. That way, the
+ * new counter value is far less likely to overtake the previous one
+ * unless you have some serious IRQ latency issues.
+ */
+ hwc->sample_period = CCI_PMU_CNTR_MASK >> 1;
+ hwc->last_period = hwc->sample_period;
+ local64_set(&hwc->period_left, hwc->sample_period);
+
+ if (event->group_leader != event) {
+ if (validate_group(event) != 0)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cci_pmu_event_init(struct perf_event *event)
+{
+ struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu);
+ atomic_t *active_events = &cci_pmu->active_events;
+ int err = 0;
+
+ if (event->attr.type != event->pmu->type)
+ return -ENOENT;
+
+ /* Shared by all CPUs, no meaningful state to sample */
+ if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK)
+ return -EOPNOTSUPP;
+
+ /* We have no filtering of any kind */
+ if (event->attr.exclude_user ||
+ event->attr.exclude_kernel ||
+ event->attr.exclude_hv ||
+ event->attr.exclude_idle ||
+ event->attr.exclude_host ||
+ event->attr.exclude_guest)
+ return -EINVAL;
+
+ /*
+ * Following the example set by other "uncore" PMUs, we accept any CPU
+ * and rewrite its affinity dynamically rather than having perf core
+ * handle cpu == -1 and pid == -1 for this case.
+ *
+ * The perf core will pin online CPUs for the duration of this call and
+ * the event being installed into its context, so the PMU's CPU can't
+ * change under our feet.
+ */
+ if (event->cpu < 0)
+ return -EINVAL;
+ event->cpu = cci_pmu->cpu;
+
+ event->destroy = hw_perf_event_destroy;
+ if (!atomic_inc_not_zero(active_events)) {
+ mutex_lock(&cci_pmu->reserve_mutex);
+ if (atomic_read(active_events) == 0)
+ err = cci_pmu_get_hw(cci_pmu);
+ if (!err)
+ atomic_inc(active_events);
+ mutex_unlock(&cci_pmu->reserve_mutex);
+ }
+ if (err)
+ return err;
+
+ err = __hw_perf_event_init(event);
+ if (err)
+ hw_perf_event_destroy(event);
+
+ return err;
+}
+
+static ssize_t pmu_cpumask_attr_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pmu *pmu = dev_get_drvdata(dev);
+ struct cci_pmu *cci_pmu = to_cci_pmu(pmu);
+
+ return cpumap_print_to_pagebuf(true, buf, cpumask_of(cci_pmu->cpu));
+}
+
+static struct device_attribute pmu_cpumask_attr =
+ __ATTR(cpumask, S_IRUGO, pmu_cpumask_attr_show, NULL);
+
+static struct attribute *pmu_attrs[] = {
+ &pmu_cpumask_attr.attr,
+ NULL,
+};
+
+static struct attribute_group pmu_attr_group = {
+ .attrs = pmu_attrs,
+};
+
+static struct attribute_group pmu_format_attr_group = {
+ .name = "format",
+ .attrs = NULL, /* Filled in cci_pmu_init_attrs */
+};
+
+static struct attribute_group pmu_event_attr_group = {
+ .name = "events",
+ .attrs = NULL, /* Filled in cci_pmu_init_attrs */
+};
+
+static const struct attribute_group *pmu_attr_groups[] = {
+ &pmu_attr_group,
+ &pmu_format_attr_group,
+ &pmu_event_attr_group,
+ NULL
+};
+
+static int cci_pmu_init(struct cci_pmu *cci_pmu, struct platform_device *pdev)
+{
+ const struct cci_pmu_model *model = cci_pmu->model;
+ char *name = model->name;
+ u32 num_cntrs;
+
+ pmu_event_attr_group.attrs = model->event_attrs;
+ pmu_format_attr_group.attrs = model->format_attrs;
+
+ cci_pmu->pmu = (struct pmu) {
+ .name = cci_pmu->model->name,
+ .task_ctx_nr = perf_invalid_context,
+ .pmu_enable = cci_pmu_enable,
+ .pmu_disable = cci_pmu_disable,
+ .event_init = cci_pmu_event_init,
+ .add = cci_pmu_add,
+ .del = cci_pmu_del,
+ .start = cci_pmu_start,
+ .stop = cci_pmu_stop,
+ .read = pmu_read,
+ .attr_groups = pmu_attr_groups,
+ };
+
+ cci_pmu->plat_device = pdev;
+ num_cntrs = pmu_get_max_counters(cci_pmu);
+ if (num_cntrs > cci_pmu->model->num_hw_cntrs) {
+ dev_warn(&pdev->dev,
+ "PMU implements more counters(%d) than supported by"
+ " the model(%d), truncated.",
+ num_cntrs, cci_pmu->model->num_hw_cntrs);
+ num_cntrs = cci_pmu->model->num_hw_cntrs;
+ }
+ cci_pmu->num_cntrs = num_cntrs + cci_pmu->model->fixed_hw_cntrs;
+
+ return perf_pmu_register(&cci_pmu->pmu, name, -1);
+}
+
+static int cci_pmu_offline_cpu(unsigned int cpu)
+{
+ int target;
+
+ if (!g_cci_pmu || cpu != g_cci_pmu->cpu)
+ return 0;
+
+ target = cpumask_any_but(cpu_online_mask, cpu);
+ if (target >= nr_cpu_ids)
+ return 0;
+
+ perf_pmu_migrate_context(&g_cci_pmu->pmu, cpu, target);
+ g_cci_pmu->cpu = target;
+ return 0;
+}
+
+static struct cci_pmu_model cci_pmu_models[] = {
+#ifdef CONFIG_ARM_CCI400_PMU
+ [CCI400_R0] = {
+ .name = "CCI_400",
+ .fixed_hw_cntrs = 1, /* Cycle counter */
+ .num_hw_cntrs = 4,
+ .cntr_size = SZ_4K,
+ .format_attrs = cci400_pmu_format_attrs,
+ .event_attrs = cci400_r0_pmu_event_attrs,
+ .event_ranges = {
+ [CCI_IF_SLAVE] = {
+ CCI400_R0_SLAVE_PORT_MIN_EV,
+ CCI400_R0_SLAVE_PORT_MAX_EV,
+ },
+ [CCI_IF_MASTER] = {
+ CCI400_R0_MASTER_PORT_MIN_EV,
+ CCI400_R0_MASTER_PORT_MAX_EV,
+ },
+ },
+ .validate_hw_event = cci400_validate_hw_event,
+ .get_event_idx = cci400_get_event_idx,
+ },
+ [CCI400_R1] = {
+ .name = "CCI_400_r1",
+ .fixed_hw_cntrs = 1, /* Cycle counter */
+ .num_hw_cntrs = 4,
+ .cntr_size = SZ_4K,
+ .format_attrs = cci400_pmu_format_attrs,
+ .event_attrs = cci400_r1_pmu_event_attrs,
+ .event_ranges = {
+ [CCI_IF_SLAVE] = {
+ CCI400_R1_SLAVE_PORT_MIN_EV,
+ CCI400_R1_SLAVE_PORT_MAX_EV,
+ },
+ [CCI_IF_MASTER] = {
+ CCI400_R1_MASTER_PORT_MIN_EV,
+ CCI400_R1_MASTER_PORT_MAX_EV,
+ },
+ },
+ .validate_hw_event = cci400_validate_hw_event,
+ .get_event_idx = cci400_get_event_idx,
+ },
+#endif
+#ifdef CONFIG_ARM_CCI5xx_PMU
+ [CCI500_R0] = {
+ .name = "CCI_500",
+ .fixed_hw_cntrs = 0,
+ .num_hw_cntrs = 8,
+ .cntr_size = SZ_64K,
+ .format_attrs = cci5xx_pmu_format_attrs,
+ .event_attrs = cci5xx_pmu_event_attrs,
+ .event_ranges = {
+ [CCI_IF_SLAVE] = {
+ CCI5xx_SLAVE_PORT_MIN_EV,
+ CCI5xx_SLAVE_PORT_MAX_EV,
+ },
+ [CCI_IF_MASTER] = {
+ CCI5xx_MASTER_PORT_MIN_EV,
+ CCI5xx_MASTER_PORT_MAX_EV,
+ },
+ [CCI_IF_GLOBAL] = {
+ CCI5xx_GLOBAL_PORT_MIN_EV,
+ CCI5xx_GLOBAL_PORT_MAX_EV,
+ },
+ },
+ .validate_hw_event = cci500_validate_hw_event,
+ .write_counters = cci5xx_pmu_write_counters,
+ },
+ [CCI550_R0] = {
+ .name = "CCI_550",
+ .fixed_hw_cntrs = 0,
+ .num_hw_cntrs = 8,
+ .cntr_size = SZ_64K,
+ .format_attrs = cci5xx_pmu_format_attrs,
+ .event_attrs = cci5xx_pmu_event_attrs,
+ .event_ranges = {
+ [CCI_IF_SLAVE] = {
+ CCI5xx_SLAVE_PORT_MIN_EV,
+ CCI5xx_SLAVE_PORT_MAX_EV,
+ },
+ [CCI_IF_MASTER] = {
+ CCI5xx_MASTER_PORT_MIN_EV,
+ CCI5xx_MASTER_PORT_MAX_EV,
+ },
+ [CCI_IF_GLOBAL] = {
+ CCI5xx_GLOBAL_PORT_MIN_EV,
+ CCI5xx_GLOBAL_PORT_MAX_EV,
+ },
+ },
+ .validate_hw_event = cci550_validate_hw_event,
+ .write_counters = cci5xx_pmu_write_counters,
+ },
+#endif
+};
+
+static const struct of_device_id arm_cci_pmu_matches[] = {
+#ifdef CONFIG_ARM_CCI400_PMU
+ {
+ .compatible = "arm,cci-400-pmu",
+ .data = NULL,
+ },
+ {
+ .compatible = "arm,cci-400-pmu,r0",
+ .data = &cci_pmu_models[CCI400_R0],
+ },
+ {
+ .compatible = "arm,cci-400-pmu,r1",
+ .data = &cci_pmu_models[CCI400_R1],
+ },
+#endif
+#ifdef CONFIG_ARM_CCI5xx_PMU
+ {
+ .compatible = "arm,cci-500-pmu,r0",
+ .data = &cci_pmu_models[CCI500_R0],
+ },
+ {
+ .compatible = "arm,cci-550-pmu,r0",
+ .data = &cci_pmu_models[CCI550_R0],
+ },
+#endif
+ {},
+};
+
+static bool is_duplicate_irq(int irq, int *irqs, int nr_irqs)
+{
+ int i;
+
+ for (i = 0; i < nr_irqs; i++)
+ if (irq == irqs[i])
+ return true;
+
+ return false;
+}
+
+static struct cci_pmu *cci_pmu_alloc(struct device *dev)
+{
+ struct cci_pmu *cci_pmu;
+ const struct cci_pmu_model *model;
+
+ /*
+ * All allocations are devm_* hence we don't have to free
+ * them explicitly on an error, as it would end up in driver
+ * detach.
+ */
+ cci_pmu = devm_kzalloc(dev, sizeof(*cci_pmu), GFP_KERNEL);
+ if (!cci_pmu)
+ return ERR_PTR(-ENOMEM);
+
+ cci_pmu->ctrl_base = *(void __iomem **)dev->platform_data;
+
+ model = of_device_get_match_data(dev);
+ if (!model) {
+ dev_warn(dev,
+ "DEPRECATED compatible property, requires secure access to CCI registers");
+ model = probe_cci_model(cci_pmu);
+ }
+ if (!model) {
+ dev_warn(dev, "CCI PMU version not supported\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ cci_pmu->model = model;
+ cci_pmu->irqs = devm_kcalloc(dev, CCI_PMU_MAX_HW_CNTRS(model),
+ sizeof(*cci_pmu->irqs), GFP_KERNEL);
+ if (!cci_pmu->irqs)
+ return ERR_PTR(-ENOMEM);
+ cci_pmu->hw_events.events = devm_kcalloc(dev,
+ CCI_PMU_MAX_HW_CNTRS(model),
+ sizeof(*cci_pmu->hw_events.events),
+ GFP_KERNEL);
+ if (!cci_pmu->hw_events.events)
+ return ERR_PTR(-ENOMEM);
+ cci_pmu->hw_events.used_mask = devm_kcalloc(dev,
+ BITS_TO_LONGS(CCI_PMU_MAX_HW_CNTRS(model)),
+ sizeof(*cci_pmu->hw_events.used_mask),
+ GFP_KERNEL);
+ if (!cci_pmu->hw_events.used_mask)
+ return ERR_PTR(-ENOMEM);
+
+ return cci_pmu;
+}
+
+static int cci_pmu_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct cci_pmu *cci_pmu;
+ int i, ret, irq;
+
+ cci_pmu = cci_pmu_alloc(&pdev->dev);
+ if (IS_ERR(cci_pmu))
+ return PTR_ERR(cci_pmu);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ cci_pmu->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(cci_pmu->base))
+ return -ENOMEM;
+
+ /*
+ * CCI PMU has one overflow interrupt per counter; but some may be tied
+ * together to a common interrupt.
+ */
+ cci_pmu->nr_irqs = 0;
+ for (i = 0; i < CCI_PMU_MAX_HW_CNTRS(cci_pmu->model); i++) {
+ irq = platform_get_irq(pdev, i);
+ if (irq < 0)
+ break;
+
+ if (is_duplicate_irq(irq, cci_pmu->irqs, cci_pmu->nr_irqs))
+ continue;
+
+ cci_pmu->irqs[cci_pmu->nr_irqs++] = irq;
+ }
+
+ /*
+ * Ensure that the device tree has as many interrupts as the number
+ * of counters.
+ */
+ if (i < CCI_PMU_MAX_HW_CNTRS(cci_pmu->model)) {
+ dev_warn(&pdev->dev, "In-correct number of interrupts: %d, should be %d\n",
+ i, CCI_PMU_MAX_HW_CNTRS(cci_pmu->model));
+ return -EINVAL;
+ }
+
+ raw_spin_lock_init(&cci_pmu->hw_events.pmu_lock);
+ mutex_init(&cci_pmu->reserve_mutex);
+ atomic_set(&cci_pmu->active_events, 0);
+ cci_pmu->cpu = get_cpu();
+
+ ret = cci_pmu_init(cci_pmu, pdev);
+ if (ret) {
+ put_cpu();
+ return ret;
+ }
+
+ cpuhp_setup_state_nocalls(CPUHP_AP_PERF_ARM_CCI_ONLINE,
+ "perf/arm/cci:online", NULL,
+ cci_pmu_offline_cpu);
+ put_cpu();
+ g_cci_pmu = cci_pmu;
+ pr_info("ARM %s PMU driver probed", cci_pmu->model->name);
+ return 0;
+}
+
+static struct platform_driver cci_pmu_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = arm_cci_pmu_matches,
+ },
+ .probe = cci_pmu_probe,
+};
+
+builtin_platform_driver(cci_pmu_driver);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ARM CCI PMU support");
diff --git a/drivers/perf/arm-ccn.c b/drivers/perf/arm-ccn.c
new file mode 100644
index 000000000000..65b7e4042ece
--- /dev/null
+++ b/drivers/perf/arm-ccn.c
@@ -0,0 +1,1597 @@
+/*
+ * 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.
+ *
+ * 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.
+ *
+ * Copyright (C) 2014 ARM Limited
+ */
+
+#include <linux/ctype.h>
+#include <linux/hrtimer.h>
+#include <linux/idr.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/perf_event.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define CCN_NUM_XP_PORTS 2
+#define CCN_NUM_VCS 4
+#define CCN_NUM_REGIONS 256
+#define CCN_REGION_SIZE 0x10000
+
+#define CCN_ALL_OLY_ID 0xff00
+#define CCN_ALL_OLY_ID__OLY_ID__SHIFT 0
+#define CCN_ALL_OLY_ID__OLY_ID__MASK 0x1f
+#define CCN_ALL_OLY_ID__NODE_ID__SHIFT 8
+#define CCN_ALL_OLY_ID__NODE_ID__MASK 0x3f
+
+#define CCN_MN_ERRINT_STATUS 0x0008
+#define CCN_MN_ERRINT_STATUS__INTREQ__DESSERT 0x11
+#define CCN_MN_ERRINT_STATUS__ALL_ERRORS__ENABLE 0x02
+#define CCN_MN_ERRINT_STATUS__ALL_ERRORS__DISABLED 0x20
+#define CCN_MN_ERRINT_STATUS__ALL_ERRORS__DISABLE 0x22
+#define CCN_MN_ERRINT_STATUS__CORRECTED_ERRORS_ENABLE 0x04
+#define CCN_MN_ERRINT_STATUS__CORRECTED_ERRORS_DISABLED 0x40
+#define CCN_MN_ERRINT_STATUS__CORRECTED_ERRORS_DISABLE 0x44
+#define CCN_MN_ERRINT_STATUS__PMU_EVENTS__ENABLE 0x08
+#define CCN_MN_ERRINT_STATUS__PMU_EVENTS__DISABLED 0x80
+#define CCN_MN_ERRINT_STATUS__PMU_EVENTS__DISABLE 0x88
+#define CCN_MN_OLY_COMP_LIST_63_0 0x01e0
+#define CCN_MN_ERR_SIG_VAL_63_0 0x0300
+#define CCN_MN_ERR_SIG_VAL_63_0__DT (1 << 1)
+
+#define CCN_DT_ACTIVE_DSM 0x0000
+#define CCN_DT_ACTIVE_DSM__DSM_ID__SHIFT(n) ((n) * 8)
+#define CCN_DT_ACTIVE_DSM__DSM_ID__MASK 0xff
+#define CCN_DT_CTL 0x0028
+#define CCN_DT_CTL__DT_EN (1 << 0)
+#define CCN_DT_PMEVCNT(n) (0x0100 + (n) * 0x8)
+#define CCN_DT_PMCCNTR 0x0140
+#define CCN_DT_PMCCNTRSR 0x0190
+#define CCN_DT_PMOVSR 0x0198
+#define CCN_DT_PMOVSR_CLR 0x01a0
+#define CCN_DT_PMOVSR_CLR__MASK 0x1f
+#define CCN_DT_PMCR 0x01a8
+#define CCN_DT_PMCR__OVFL_INTR_EN (1 << 6)
+#define CCN_DT_PMCR__PMU_EN (1 << 0)
+#define CCN_DT_PMSR 0x01b0
+#define CCN_DT_PMSR_REQ 0x01b8
+#define CCN_DT_PMSR_CLR 0x01c0
+
+#define CCN_HNF_PMU_EVENT_SEL 0x0600
+#define CCN_HNF_PMU_EVENT_SEL__ID__SHIFT(n) ((n) * 4)
+#define CCN_HNF_PMU_EVENT_SEL__ID__MASK 0xf
+
+#define CCN_XP_DT_CONFIG 0x0300
+#define CCN_XP_DT_CONFIG__DT_CFG__SHIFT(n) ((n) * 4)
+#define CCN_XP_DT_CONFIG__DT_CFG__MASK 0xf
+#define CCN_XP_DT_CONFIG__DT_CFG__PASS_THROUGH 0x0
+#define CCN_XP_DT_CONFIG__DT_CFG__WATCHPOINT_0_OR_1 0x1
+#define CCN_XP_DT_CONFIG__DT_CFG__WATCHPOINT(n) (0x2 + (n))
+#define CCN_XP_DT_CONFIG__DT_CFG__XP_PMU_EVENT(n) (0x4 + (n))
+#define CCN_XP_DT_CONFIG__DT_CFG__DEVICE_PMU_EVENT(d, n) (0x8 + (d) * 4 + (n))
+#define CCN_XP_DT_INTERFACE_SEL 0x0308
+#define CCN_XP_DT_INTERFACE_SEL__DT_IO_SEL__SHIFT(n) (0 + (n) * 8)
+#define CCN_XP_DT_INTERFACE_SEL__DT_IO_SEL__MASK 0x1
+#define CCN_XP_DT_INTERFACE_SEL__DT_DEV_SEL__SHIFT(n) (1 + (n) * 8)
+#define CCN_XP_DT_INTERFACE_SEL__DT_DEV_SEL__MASK 0x1
+#define CCN_XP_DT_INTERFACE_SEL__DT_VC_SEL__SHIFT(n) (2 + (n) * 8)
+#define CCN_XP_DT_INTERFACE_SEL__DT_VC_SEL__MASK 0x3
+#define CCN_XP_DT_CMP_VAL_L(n) (0x0310 + (n) * 0x40)
+#define CCN_XP_DT_CMP_VAL_H(n) (0x0318 + (n) * 0x40)
+#define CCN_XP_DT_CMP_MASK_L(n) (0x0320 + (n) * 0x40)
+#define CCN_XP_DT_CMP_MASK_H(n) (0x0328 + (n) * 0x40)
+#define CCN_XP_DT_CONTROL 0x0370
+#define CCN_XP_DT_CONTROL__DT_ENABLE (1 << 0)
+#define CCN_XP_DT_CONTROL__WP_ARM_SEL__SHIFT(n) (12 + (n) * 4)
+#define CCN_XP_DT_CONTROL__WP_ARM_SEL__MASK 0xf
+#define CCN_XP_DT_CONTROL__WP_ARM_SEL__ALWAYS 0xf
+#define CCN_XP_PMU_EVENT_SEL 0x0600
+#define CCN_XP_PMU_EVENT_SEL__ID__SHIFT(n) ((n) * 7)
+#define CCN_XP_PMU_EVENT_SEL__ID__MASK 0x3f
+
+#define CCN_SBAS_PMU_EVENT_SEL 0x0600
+#define CCN_SBAS_PMU_EVENT_SEL__ID__SHIFT(n) ((n) * 4)
+#define CCN_SBAS_PMU_EVENT_SEL__ID__MASK 0xf
+
+#define CCN_RNI_PMU_EVENT_SEL 0x0600
+#define CCN_RNI_PMU_EVENT_SEL__ID__SHIFT(n) ((n) * 4)
+#define CCN_RNI_PMU_EVENT_SEL__ID__MASK 0xf
+
+#define CCN_TYPE_MN 0x01
+#define CCN_TYPE_DT 0x02
+#define CCN_TYPE_HNF 0x04
+#define CCN_TYPE_HNI 0x05
+#define CCN_TYPE_XP 0x08
+#define CCN_TYPE_SBSX 0x0c
+#define CCN_TYPE_SBAS 0x10
+#define CCN_TYPE_RNI_1P 0x14
+#define CCN_TYPE_RNI_2P 0x15
+#define CCN_TYPE_RNI_3P 0x16
+#define CCN_TYPE_RND_1P 0x18 /* RN-D = RN-I + DVM */
+#define CCN_TYPE_RND_2P 0x19
+#define CCN_TYPE_RND_3P 0x1a
+#define CCN_TYPE_CYCLES 0xff /* Pseudotype */
+
+#define CCN_EVENT_WATCHPOINT 0xfe /* Pseudoevent */
+
+#define CCN_NUM_PMU_EVENTS 4
+#define CCN_NUM_XP_WATCHPOINTS 2 /* See DT.dbg_id.num_watchpoints */
+#define CCN_NUM_PMU_EVENT_COUNTERS 8 /* See DT.dbg_id.num_pmucntr */
+#define CCN_IDX_PMU_CYCLE_COUNTER CCN_NUM_PMU_EVENT_COUNTERS
+
+#define CCN_NUM_PREDEFINED_MASKS 4
+#define CCN_IDX_MASK_ANY (CCN_NUM_PMU_EVENT_COUNTERS + 0)
+#define CCN_IDX_MASK_EXACT (CCN_NUM_PMU_EVENT_COUNTERS + 1)
+#define CCN_IDX_MASK_ORDER (CCN_NUM_PMU_EVENT_COUNTERS + 2)
+#define CCN_IDX_MASK_OPCODE (CCN_NUM_PMU_EVENT_COUNTERS + 3)
+
+struct arm_ccn_component {
+ void __iomem *base;
+ u32 type;
+
+ DECLARE_BITMAP(pmu_events_mask, CCN_NUM_PMU_EVENTS);
+ union {
+ struct {
+ DECLARE_BITMAP(dt_cmp_mask, CCN_NUM_XP_WATCHPOINTS);
+ } xp;
+ };
+};
+
+#define pmu_to_arm_ccn(_pmu) container_of(container_of(_pmu, \
+ struct arm_ccn_dt, pmu), struct arm_ccn, dt)
+
+struct arm_ccn_dt {
+ int id;
+ void __iomem *base;
+
+ spinlock_t config_lock;
+
+ DECLARE_BITMAP(pmu_counters_mask, CCN_NUM_PMU_EVENT_COUNTERS + 1);
+ struct {
+ struct arm_ccn_component *source;
+ struct perf_event *event;
+ } pmu_counters[CCN_NUM_PMU_EVENT_COUNTERS + 1];
+
+ struct {
+ u64 l, h;
+ } cmp_mask[CCN_NUM_PMU_EVENT_COUNTERS + CCN_NUM_PREDEFINED_MASKS];
+
+ struct hrtimer hrtimer;
+
+ cpumask_t cpu;
+ struct hlist_node node;
+
+ struct pmu pmu;
+};
+
+struct arm_ccn {
+ struct device *dev;
+ void __iomem *base;
+ unsigned int irq;
+
+ unsigned sbas_present:1;
+ unsigned sbsx_present:1;
+
+ int num_nodes;
+ struct arm_ccn_component *node;
+
+ int num_xps;
+ struct arm_ccn_component *xp;
+
+ struct arm_ccn_dt dt;
+ int mn_id;
+};
+
+static int arm_ccn_node_to_xp(int node)
+{
+ return node / CCN_NUM_XP_PORTS;
+}
+
+static int arm_ccn_node_to_xp_port(int node)
+{
+ return node % CCN_NUM_XP_PORTS;
+}
+
+
+/*
+ * Bit shifts and masks in these defines must be kept in sync with
+ * arm_ccn_pmu_config_set() and CCN_FORMAT_ATTRs below!
+ */
+#define CCN_CONFIG_NODE(_config) (((_config) >> 0) & 0xff)
+#define CCN_CONFIG_XP(_config) (((_config) >> 0) & 0xff)
+#define CCN_CONFIG_TYPE(_config) (((_config) >> 8) & 0xff)
+#define CCN_CONFIG_EVENT(_config) (((_config) >> 16) & 0xff)
+#define CCN_CONFIG_PORT(_config) (((_config) >> 24) & 0x3)
+#define CCN_CONFIG_BUS(_config) (((_config) >> 24) & 0x3)
+#define CCN_CONFIG_VC(_config) (((_config) >> 26) & 0x7)
+#define CCN_CONFIG_DIR(_config) (((_config) >> 29) & 0x1)
+#define CCN_CONFIG_MASK(_config) (((_config) >> 30) & 0xf)
+
+static void arm_ccn_pmu_config_set(u64 *config, u32 node_xp, u32 type, u32 port)
+{
+ *config &= ~((0xff << 0) | (0xff << 8) | (0x3 << 24));
+ *config |= (node_xp << 0) | (type << 8) | (port << 24);
+}
+
+static ssize_t arm_ccn_pmu_format_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dev_ext_attribute *ea = container_of(attr,
+ struct dev_ext_attribute, attr);
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", (char *)ea->var);
+}
+
+#define CCN_FORMAT_ATTR(_name, _config) \
+ struct dev_ext_attribute arm_ccn_pmu_format_attr_##_name = \
+ { __ATTR(_name, S_IRUGO, arm_ccn_pmu_format_show, \
+ NULL), _config }
+
+static CCN_FORMAT_ATTR(node, "config:0-7");
+static CCN_FORMAT_ATTR(xp, "config:0-7");
+static CCN_FORMAT_ATTR(type, "config:8-15");
+static CCN_FORMAT_ATTR(event, "config:16-23");
+static CCN_FORMAT_ATTR(port, "config:24-25");
+static CCN_FORMAT_ATTR(bus, "config:24-25");
+static CCN_FORMAT_ATTR(vc, "config:26-28");
+static CCN_FORMAT_ATTR(dir, "config:29-29");
+static CCN_FORMAT_ATTR(mask, "config:30-33");
+static CCN_FORMAT_ATTR(cmp_l, "config1:0-62");
+static CCN_FORMAT_ATTR(cmp_h, "config2:0-59");
+
+static struct attribute *arm_ccn_pmu_format_attrs[] = {
+ &arm_ccn_pmu_format_attr_node.attr.attr,
+ &arm_ccn_pmu_format_attr_xp.attr.attr,
+ &arm_ccn_pmu_format_attr_type.attr.attr,
+ &arm_ccn_pmu_format_attr_event.attr.attr,
+ &arm_ccn_pmu_format_attr_port.attr.attr,
+ &arm_ccn_pmu_format_attr_bus.attr.attr,
+ &arm_ccn_pmu_format_attr_vc.attr.attr,
+ &arm_ccn_pmu_format_attr_dir.attr.attr,
+ &arm_ccn_pmu_format_attr_mask.attr.attr,
+ &arm_ccn_pmu_format_attr_cmp_l.attr.attr,
+ &arm_ccn_pmu_format_attr_cmp_h.attr.attr,
+ NULL
+};
+
+static const struct attribute_group arm_ccn_pmu_format_attr_group = {
+ .name = "format",
+ .attrs = arm_ccn_pmu_format_attrs,
+};
+
+
+struct arm_ccn_pmu_event {
+ struct device_attribute attr;
+ u32 type;
+ u32 event;
+ int num_ports;
+ int num_vcs;
+ const char *def;
+ int mask;
+};
+
+#define CCN_EVENT_ATTR(_name) \
+ __ATTR(_name, S_IRUGO, arm_ccn_pmu_event_show, NULL)
+
+/*
+ * Events defined in TRM for MN, HN-I and SBSX are actually watchpoints set on
+ * their ports in XP they are connected to. For the sake of usability they are
+ * explicitly defined here (and translated into a relevant watchpoint in
+ * arm_ccn_pmu_event_init()) so the user can easily request them without deep
+ * knowledge of the flit format.
+ */
+
+#define CCN_EVENT_MN(_name, _def, _mask) { .attr = CCN_EVENT_ATTR(mn_##_name), \
+ .type = CCN_TYPE_MN, .event = CCN_EVENT_WATCHPOINT, \
+ .num_ports = CCN_NUM_XP_PORTS, .num_vcs = CCN_NUM_VCS, \
+ .def = _def, .mask = _mask, }
+
+#define CCN_EVENT_HNI(_name, _def, _mask) { \
+ .attr = CCN_EVENT_ATTR(hni_##_name), .type = CCN_TYPE_HNI, \
+ .event = CCN_EVENT_WATCHPOINT, .num_ports = CCN_NUM_XP_PORTS, \
+ .num_vcs = CCN_NUM_VCS, .def = _def, .mask = _mask, }
+
+#define CCN_EVENT_SBSX(_name, _def, _mask) { \
+ .attr = CCN_EVENT_ATTR(sbsx_##_name), .type = CCN_TYPE_SBSX, \
+ .event = CCN_EVENT_WATCHPOINT, .num_ports = CCN_NUM_XP_PORTS, \
+ .num_vcs = CCN_NUM_VCS, .def = _def, .mask = _mask, }
+
+#define CCN_EVENT_HNF(_name, _event) { .attr = CCN_EVENT_ATTR(hnf_##_name), \
+ .type = CCN_TYPE_HNF, .event = _event, }
+
+#define CCN_EVENT_XP(_name, _event) { .attr = CCN_EVENT_ATTR(xp_##_name), \
+ .type = CCN_TYPE_XP, .event = _event, \
+ .num_ports = CCN_NUM_XP_PORTS, .num_vcs = CCN_NUM_VCS, }
+
+/*
+ * RN-I & RN-D (RN-D = RN-I + DVM) nodes have different type ID depending
+ * on configuration. One of them is picked to represent the whole group,
+ * as they all share the same event types.
+ */
+#define CCN_EVENT_RNI(_name, _event) { .attr = CCN_EVENT_ATTR(rni_##_name), \
+ .type = CCN_TYPE_RNI_3P, .event = _event, }
+
+#define CCN_EVENT_SBAS(_name, _event) { .attr = CCN_EVENT_ATTR(sbas_##_name), \
+ .type = CCN_TYPE_SBAS, .event = _event, }
+
+#define CCN_EVENT_CYCLES(_name) { .attr = CCN_EVENT_ATTR(_name), \
+ .type = CCN_TYPE_CYCLES }
+
+
+static ssize_t arm_ccn_pmu_event_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct arm_ccn *ccn = pmu_to_arm_ccn(dev_get_drvdata(dev));
+ struct arm_ccn_pmu_event *event = container_of(attr,
+ struct arm_ccn_pmu_event, attr);
+ ssize_t res;
+
+ res = snprintf(buf, PAGE_SIZE, "type=0x%x", event->type);
+ if (event->event)
+ res += snprintf(buf + res, PAGE_SIZE - res, ",event=0x%x",
+ event->event);
+ if (event->def)
+ res += snprintf(buf + res, PAGE_SIZE - res, ",%s",
+ event->def);
+ if (event->mask)
+ res += snprintf(buf + res, PAGE_SIZE - res, ",mask=0x%x",
+ event->mask);
+
+ /* Arguments required by an event */
+ switch (event->type) {
+ case CCN_TYPE_CYCLES:
+ break;
+ case CCN_TYPE_XP:
+ res += snprintf(buf + res, PAGE_SIZE - res,
+ ",xp=?,vc=?");
+ if (event->event == CCN_EVENT_WATCHPOINT)
+ res += snprintf(buf + res, PAGE_SIZE - res,
+ ",port=?,dir=?,cmp_l=?,cmp_h=?,mask=?");
+ else
+ res += snprintf(buf + res, PAGE_SIZE - res,
+ ",bus=?");
+
+ break;
+ case CCN_TYPE_MN:
+ res += snprintf(buf + res, PAGE_SIZE - res, ",node=%d", ccn->mn_id);
+ break;
+ default:
+ res += snprintf(buf + res, PAGE_SIZE - res, ",node=?");
+ break;
+ }
+
+ res += snprintf(buf + res, PAGE_SIZE - res, "\n");
+
+ return res;
+}
+
+static umode_t arm_ccn_pmu_events_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct arm_ccn *ccn = pmu_to_arm_ccn(dev_get_drvdata(dev));
+ struct device_attribute *dev_attr = container_of(attr,
+ struct device_attribute, attr);
+ struct arm_ccn_pmu_event *event = container_of(dev_attr,
+ struct arm_ccn_pmu_event, attr);
+
+ if (event->type == CCN_TYPE_SBAS && !ccn->sbas_present)
+ return 0;
+ if (event->type == CCN_TYPE_SBSX && !ccn->sbsx_present)
+ return 0;
+
+ return attr->mode;
+}
+
+static struct arm_ccn_pmu_event arm_ccn_pmu_events[] = {
+ CCN_EVENT_MN(eobarrier, "dir=1,vc=0,cmp_h=0x1c00", CCN_IDX_MASK_OPCODE),
+ CCN_EVENT_MN(ecbarrier, "dir=1,vc=0,cmp_h=0x1e00", CCN_IDX_MASK_OPCODE),
+ CCN_EVENT_MN(dvmop, "dir=1,vc=0,cmp_h=0x2800", CCN_IDX_MASK_OPCODE),
+ CCN_EVENT_HNI(txdatflits, "dir=1,vc=3", CCN_IDX_MASK_ANY),
+ CCN_EVENT_HNI(rxdatflits, "dir=0,vc=3", CCN_IDX_MASK_ANY),
+ CCN_EVENT_HNI(txreqflits, "dir=1,vc=0", CCN_IDX_MASK_ANY),
+ CCN_EVENT_HNI(rxreqflits, "dir=0,vc=0", CCN_IDX_MASK_ANY),
+ CCN_EVENT_HNI(rxreqflits_order, "dir=0,vc=0,cmp_h=0x8000",
+ CCN_IDX_MASK_ORDER),
+ CCN_EVENT_SBSX(txdatflits, "dir=1,vc=3", CCN_IDX_MASK_ANY),
+ CCN_EVENT_SBSX(rxdatflits, "dir=0,vc=3", CCN_IDX_MASK_ANY),
+ CCN_EVENT_SBSX(txreqflits, "dir=1,vc=0", CCN_IDX_MASK_ANY),
+ CCN_EVENT_SBSX(rxreqflits, "dir=0,vc=0", CCN_IDX_MASK_ANY),
+ CCN_EVENT_SBSX(rxreqflits_order, "dir=0,vc=0,cmp_h=0x8000",
+ CCN_IDX_MASK_ORDER),
+ CCN_EVENT_HNF(cache_miss, 0x1),
+ CCN_EVENT_HNF(l3_sf_cache_access, 0x02),
+ CCN_EVENT_HNF(cache_fill, 0x3),
+ CCN_EVENT_HNF(pocq_retry, 0x4),
+ CCN_EVENT_HNF(pocq_reqs_recvd, 0x5),
+ CCN_EVENT_HNF(sf_hit, 0x6),
+ CCN_EVENT_HNF(sf_evictions, 0x7),
+ CCN_EVENT_HNF(snoops_sent, 0x8),
+ CCN_EVENT_HNF(snoops_broadcast, 0x9),
+ CCN_EVENT_HNF(l3_eviction, 0xa),
+ CCN_EVENT_HNF(l3_fill_invalid_way, 0xb),
+ CCN_EVENT_HNF(mc_retries, 0xc),
+ CCN_EVENT_HNF(mc_reqs, 0xd),
+ CCN_EVENT_HNF(qos_hh_retry, 0xe),
+ CCN_EVENT_RNI(rdata_beats_p0, 0x1),
+ CCN_EVENT_RNI(rdata_beats_p1, 0x2),
+ CCN_EVENT_RNI(rdata_beats_p2, 0x3),
+ CCN_EVENT_RNI(rxdat_flits, 0x4),
+ CCN_EVENT_RNI(txdat_flits, 0x5),
+ CCN_EVENT_RNI(txreq_flits, 0x6),
+ CCN_EVENT_RNI(txreq_flits_retried, 0x7),
+ CCN_EVENT_RNI(rrt_full, 0x8),
+ CCN_EVENT_RNI(wrt_full, 0x9),
+ CCN_EVENT_RNI(txreq_flits_replayed, 0xa),
+ CCN_EVENT_XP(upload_starvation, 0x1),
+ CCN_EVENT_XP(download_starvation, 0x2),
+ CCN_EVENT_XP(respin, 0x3),
+ CCN_EVENT_XP(valid_flit, 0x4),
+ CCN_EVENT_XP(watchpoint, CCN_EVENT_WATCHPOINT),
+ CCN_EVENT_SBAS(rdata_beats_p0, 0x1),
+ CCN_EVENT_SBAS(rxdat_flits, 0x4),
+ CCN_EVENT_SBAS(txdat_flits, 0x5),
+ CCN_EVENT_SBAS(txreq_flits, 0x6),
+ CCN_EVENT_SBAS(txreq_flits_retried, 0x7),
+ CCN_EVENT_SBAS(rrt_full, 0x8),
+ CCN_EVENT_SBAS(wrt_full, 0x9),
+ CCN_EVENT_SBAS(txreq_flits_replayed, 0xa),
+ CCN_EVENT_CYCLES(cycles),
+};
+
+/* Populated in arm_ccn_init() */
+static struct attribute
+ *arm_ccn_pmu_events_attrs[ARRAY_SIZE(arm_ccn_pmu_events) + 1];
+
+static const struct attribute_group arm_ccn_pmu_events_attr_group = {
+ .name = "events",
+ .is_visible = arm_ccn_pmu_events_is_visible,
+ .attrs = arm_ccn_pmu_events_attrs,
+};
+
+
+static u64 *arm_ccn_pmu_get_cmp_mask(struct arm_ccn *ccn, const char *name)
+{
+ unsigned long i;
+
+ if (WARN_ON(!name || !name[0] || !isxdigit(name[0]) || !name[1]))
+ return NULL;
+ i = isdigit(name[0]) ? name[0] - '0' : 0xa + tolower(name[0]) - 'a';
+
+ switch (name[1]) {
+ case 'l':
+ return &ccn->dt.cmp_mask[i].l;
+ case 'h':
+ return &ccn->dt.cmp_mask[i].h;
+ default:
+ return NULL;
+ }
+}
+
+static ssize_t arm_ccn_pmu_cmp_mask_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct arm_ccn *ccn = pmu_to_arm_ccn(dev_get_drvdata(dev));
+ u64 *mask = arm_ccn_pmu_get_cmp_mask(ccn, attr->attr.name);
+
+ return mask ? snprintf(buf, PAGE_SIZE, "0x%016llx\n", *mask) : -EINVAL;
+}
+
+static ssize_t arm_ccn_pmu_cmp_mask_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct arm_ccn *ccn = pmu_to_arm_ccn(dev_get_drvdata(dev));
+ u64 *mask = arm_ccn_pmu_get_cmp_mask(ccn, attr->attr.name);
+ int err = -EINVAL;
+
+ if (mask)
+ err = kstrtoull(buf, 0, mask);
+
+ return err ? err : count;
+}
+
+#define CCN_CMP_MASK_ATTR(_name) \
+ struct device_attribute arm_ccn_pmu_cmp_mask_attr_##_name = \
+ __ATTR(_name, S_IRUGO | S_IWUSR, \
+ arm_ccn_pmu_cmp_mask_show, arm_ccn_pmu_cmp_mask_store)
+
+#define CCN_CMP_MASK_ATTR_RO(_name) \
+ struct device_attribute arm_ccn_pmu_cmp_mask_attr_##_name = \
+ __ATTR(_name, S_IRUGO, arm_ccn_pmu_cmp_mask_show, NULL)
+
+static CCN_CMP_MASK_ATTR(0l);
+static CCN_CMP_MASK_ATTR(0h);
+static CCN_CMP_MASK_ATTR(1l);
+static CCN_CMP_MASK_ATTR(1h);
+static CCN_CMP_MASK_ATTR(2l);
+static CCN_CMP_MASK_ATTR(2h);
+static CCN_CMP_MASK_ATTR(3l);
+static CCN_CMP_MASK_ATTR(3h);
+static CCN_CMP_MASK_ATTR(4l);
+static CCN_CMP_MASK_ATTR(4h);
+static CCN_CMP_MASK_ATTR(5l);
+static CCN_CMP_MASK_ATTR(5h);
+static CCN_CMP_MASK_ATTR(6l);
+static CCN_CMP_MASK_ATTR(6h);
+static CCN_CMP_MASK_ATTR(7l);
+static CCN_CMP_MASK_ATTR(7h);
+static CCN_CMP_MASK_ATTR_RO(8l);
+static CCN_CMP_MASK_ATTR_RO(8h);
+static CCN_CMP_MASK_ATTR_RO(9l);
+static CCN_CMP_MASK_ATTR_RO(9h);
+static CCN_CMP_MASK_ATTR_RO(al);
+static CCN_CMP_MASK_ATTR_RO(ah);
+static CCN_CMP_MASK_ATTR_RO(bl);
+static CCN_CMP_MASK_ATTR_RO(bh);
+
+static struct attribute *arm_ccn_pmu_cmp_mask_attrs[] = {
+ &arm_ccn_pmu_cmp_mask_attr_0l.attr, &arm_ccn_pmu_cmp_mask_attr_0h.attr,
+ &arm_ccn_pmu_cmp_mask_attr_1l.attr, &arm_ccn_pmu_cmp_mask_attr_1h.attr,
+ &arm_ccn_pmu_cmp_mask_attr_2l.attr, &arm_ccn_pmu_cmp_mask_attr_2h.attr,
+ &arm_ccn_pmu_cmp_mask_attr_3l.attr, &arm_ccn_pmu_cmp_mask_attr_3h.attr,
+ &arm_ccn_pmu_cmp_mask_attr_4l.attr, &arm_ccn_pmu_cmp_mask_attr_4h.attr,
+ &arm_ccn_pmu_cmp_mask_attr_5l.attr, &arm_ccn_pmu_cmp_mask_attr_5h.attr,
+ &arm_ccn_pmu_cmp_mask_attr_6l.attr, &arm_ccn_pmu_cmp_mask_attr_6h.attr,
+ &arm_ccn_pmu_cmp_mask_attr_7l.attr, &arm_ccn_pmu_cmp_mask_attr_7h.attr,
+ &arm_ccn_pmu_cmp_mask_attr_8l.attr, &arm_ccn_pmu_cmp_mask_attr_8h.attr,
+ &arm_ccn_pmu_cmp_mask_attr_9l.attr, &arm_ccn_pmu_cmp_mask_attr_9h.attr,
+ &arm_ccn_pmu_cmp_mask_attr_al.attr, &arm_ccn_pmu_cmp_mask_attr_ah.attr,
+ &arm_ccn_pmu_cmp_mask_attr_bl.attr, &arm_ccn_pmu_cmp_mask_attr_bh.attr,
+ NULL
+};
+
+static const struct attribute_group arm_ccn_pmu_cmp_mask_attr_group = {
+ .name = "cmp_mask",
+ .attrs = arm_ccn_pmu_cmp_mask_attrs,
+};
+
+static ssize_t arm_ccn_pmu_cpumask_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct arm_ccn *ccn = pmu_to_arm_ccn(dev_get_drvdata(dev));
+
+ return cpumap_print_to_pagebuf(true, buf, &ccn->dt.cpu);
+}
+
+static struct device_attribute arm_ccn_pmu_cpumask_attr =
+ __ATTR(cpumask, S_IRUGO, arm_ccn_pmu_cpumask_show, NULL);
+
+static struct attribute *arm_ccn_pmu_cpumask_attrs[] = {
+ &arm_ccn_pmu_cpumask_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group arm_ccn_pmu_cpumask_attr_group = {
+ .attrs = arm_ccn_pmu_cpumask_attrs,
+};
+
+/*
+ * Default poll period is 10ms, which is way over the top anyway,
+ * as in the worst case scenario (an event every cycle), with 1GHz
+ * clocked bus, the smallest, 32 bit counter will overflow in
+ * more than 4s.
+ */
+static unsigned int arm_ccn_pmu_poll_period_us = 10000;
+module_param_named(pmu_poll_period_us, arm_ccn_pmu_poll_period_us, uint,
+ S_IRUGO | S_IWUSR);
+
+static ktime_t arm_ccn_pmu_timer_period(void)
+{
+ return ns_to_ktime((u64)arm_ccn_pmu_poll_period_us * 1000);
+}
+
+
+static const struct attribute_group *arm_ccn_pmu_attr_groups[] = {
+ &arm_ccn_pmu_events_attr_group,
+ &arm_ccn_pmu_format_attr_group,
+ &arm_ccn_pmu_cmp_mask_attr_group,
+ &arm_ccn_pmu_cpumask_attr_group,
+ NULL
+};
+
+
+static int arm_ccn_pmu_alloc_bit(unsigned long *bitmap, unsigned long size)
+{
+ int bit;
+
+ do {
+ bit = find_first_zero_bit(bitmap, size);
+ if (bit >= size)
+ return -EAGAIN;
+ } while (test_and_set_bit(bit, bitmap));
+
+ return bit;
+}
+
+/* All RN-I and RN-D nodes have identical PMUs */
+static int arm_ccn_pmu_type_eq(u32 a, u32 b)
+{
+ if (a == b)
+ return 1;
+
+ switch (a) {
+ case CCN_TYPE_RNI_1P:
+ case CCN_TYPE_RNI_2P:
+ case CCN_TYPE_RNI_3P:
+ case CCN_TYPE_RND_1P:
+ case CCN_TYPE_RND_2P:
+ case CCN_TYPE_RND_3P:
+ switch (b) {
+ case CCN_TYPE_RNI_1P:
+ case CCN_TYPE_RNI_2P:
+ case CCN_TYPE_RNI_3P:
+ case CCN_TYPE_RND_1P:
+ case CCN_TYPE_RND_2P:
+ case CCN_TYPE_RND_3P:
+ return 1;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int arm_ccn_pmu_event_alloc(struct perf_event *event)
+{
+ struct arm_ccn *ccn = pmu_to_arm_ccn(event->pmu);
+ struct hw_perf_event *hw = &event->hw;
+ u32 node_xp, type, event_id;
+ struct arm_ccn_component *source;
+ int bit;
+
+ node_xp = CCN_CONFIG_NODE(event->attr.config);
+ type = CCN_CONFIG_TYPE(event->attr.config);
+ event_id = CCN_CONFIG_EVENT(event->attr.config);
+
+ /* Allocate the cycle counter */
+ if (type == CCN_TYPE_CYCLES) {
+ if (test_and_set_bit(CCN_IDX_PMU_CYCLE_COUNTER,
+ ccn->dt.pmu_counters_mask))
+ return -EAGAIN;
+
+ hw->idx = CCN_IDX_PMU_CYCLE_COUNTER;
+ ccn->dt.pmu_counters[CCN_IDX_PMU_CYCLE_COUNTER].event = event;
+
+ return 0;
+ }
+
+ /* Allocate an event counter */
+ hw->idx = arm_ccn_pmu_alloc_bit(ccn->dt.pmu_counters_mask,
+ CCN_NUM_PMU_EVENT_COUNTERS);
+ if (hw->idx < 0) {
+ dev_dbg(ccn->dev, "No more counters available!\n");
+ return -EAGAIN;
+ }
+
+ if (type == CCN_TYPE_XP)
+ source = &ccn->xp[node_xp];
+ else
+ source = &ccn->node[node_xp];
+ ccn->dt.pmu_counters[hw->idx].source = source;
+
+ /* Allocate an event source or a watchpoint */
+ if (type == CCN_TYPE_XP && event_id == CCN_EVENT_WATCHPOINT)
+ bit = arm_ccn_pmu_alloc_bit(source->xp.dt_cmp_mask,
+ CCN_NUM_XP_WATCHPOINTS);
+ else
+ bit = arm_ccn_pmu_alloc_bit(source->pmu_events_mask,
+ CCN_NUM_PMU_EVENTS);
+ if (bit < 0) {
+ dev_dbg(ccn->dev, "No more event sources/watchpoints on node/XP %d!\n",
+ node_xp);
+ clear_bit(hw->idx, ccn->dt.pmu_counters_mask);
+ return -EAGAIN;
+ }
+ hw->config_base = bit;
+
+ ccn->dt.pmu_counters[hw->idx].event = event;
+
+ return 0;
+}
+
+static void arm_ccn_pmu_event_release(struct perf_event *event)
+{
+ struct arm_ccn *ccn = pmu_to_arm_ccn(event->pmu);
+ struct hw_perf_event *hw = &event->hw;
+
+ if (hw->idx == CCN_IDX_PMU_CYCLE_COUNTER) {
+ clear_bit(CCN_IDX_PMU_CYCLE_COUNTER, ccn->dt.pmu_counters_mask);
+ } else {
+ struct arm_ccn_component *source =
+ ccn->dt.pmu_counters[hw->idx].source;
+
+ if (CCN_CONFIG_TYPE(event->attr.config) == CCN_TYPE_XP &&
+ CCN_CONFIG_EVENT(event->attr.config) ==
+ CCN_EVENT_WATCHPOINT)
+ clear_bit(hw->config_base, source->xp.dt_cmp_mask);
+ else
+ clear_bit(hw->config_base, source->pmu_events_mask);
+ clear_bit(hw->idx, ccn->dt.pmu_counters_mask);
+ }
+
+ ccn->dt.pmu_counters[hw->idx].source = NULL;
+ ccn->dt.pmu_counters[hw->idx].event = NULL;
+}
+
+static int arm_ccn_pmu_event_init(struct perf_event *event)
+{
+ struct arm_ccn *ccn;
+ struct hw_perf_event *hw = &event->hw;
+ u32 node_xp, type, event_id;
+ int valid;
+ int i;
+ struct perf_event *sibling;
+
+ if (event->attr.type != event->pmu->type)
+ return -ENOENT;
+
+ ccn = pmu_to_arm_ccn(event->pmu);
+
+ if (hw->sample_period) {
+ dev_warn(ccn->dev, "Sampling not supported!\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (has_branch_stack(event) || event->attr.exclude_user ||
+ event->attr.exclude_kernel || event->attr.exclude_hv ||
+ event->attr.exclude_idle || event->attr.exclude_host ||
+ event->attr.exclude_guest) {
+ dev_warn(ccn->dev, "Can't exclude execution levels!\n");
+ return -EINVAL;
+ }
+
+ if (event->cpu < 0) {
+ dev_warn(ccn->dev, "Can't provide per-task data!\n");
+ return -EOPNOTSUPP;
+ }
+ /*
+ * Many perf core operations (eg. events rotation) operate on a
+ * single CPU context. This is obvious for CPU PMUs, where one
+ * expects the same sets of events being observed on all CPUs,
+ * but can lead to issues for off-core PMUs, like CCN, where each
+ * event could be theoretically assigned to a different CPU. To
+ * mitigate this, we enforce CPU assignment to one, selected
+ * processor (the one described in the "cpumask" attribute).
+ */
+ event->cpu = cpumask_first(&ccn->dt.cpu);
+
+ node_xp = CCN_CONFIG_NODE(event->attr.config);
+ type = CCN_CONFIG_TYPE(event->attr.config);
+ event_id = CCN_CONFIG_EVENT(event->attr.config);
+
+ /* Validate node/xp vs topology */
+ switch (type) {
+ case CCN_TYPE_MN:
+ if (node_xp != ccn->mn_id) {
+ dev_warn(ccn->dev, "Invalid MN ID %d!\n", node_xp);
+ return -EINVAL;
+ }
+ break;
+ case CCN_TYPE_XP:
+ if (node_xp >= ccn->num_xps) {
+ dev_warn(ccn->dev, "Invalid XP ID %d!\n", node_xp);
+ return -EINVAL;
+ }
+ break;
+ case CCN_TYPE_CYCLES:
+ break;
+ default:
+ if (node_xp >= ccn->num_nodes) {
+ dev_warn(ccn->dev, "Invalid node ID %d!\n", node_xp);
+ return -EINVAL;
+ }
+ if (!arm_ccn_pmu_type_eq(type, ccn->node[node_xp].type)) {
+ dev_warn(ccn->dev, "Invalid type 0x%x for node %d!\n",
+ type, node_xp);
+ return -EINVAL;
+ }
+ break;
+ }
+
+ /* Validate event ID vs available for the type */
+ for (i = 0, valid = 0; i < ARRAY_SIZE(arm_ccn_pmu_events) && !valid;
+ i++) {
+ struct arm_ccn_pmu_event *e = &arm_ccn_pmu_events[i];
+ u32 port = CCN_CONFIG_PORT(event->attr.config);
+ u32 vc = CCN_CONFIG_VC(event->attr.config);
+
+ if (!arm_ccn_pmu_type_eq(type, e->type))
+ continue;
+ if (event_id != e->event)
+ continue;
+ if (e->num_ports && port >= e->num_ports) {
+ dev_warn(ccn->dev, "Invalid port %d for node/XP %d!\n",
+ port, node_xp);
+ return -EINVAL;
+ }
+ if (e->num_vcs && vc >= e->num_vcs) {
+ dev_warn(ccn->dev, "Invalid vc %d for node/XP %d!\n",
+ vc, node_xp);
+ return -EINVAL;
+ }
+ valid = 1;
+ }
+ if (!valid) {
+ dev_warn(ccn->dev, "Invalid event 0x%x for node/XP %d!\n",
+ event_id, node_xp);
+ return -EINVAL;
+ }
+
+ /* Watchpoint-based event for a node is actually set on XP */
+ if (event_id == CCN_EVENT_WATCHPOINT && type != CCN_TYPE_XP) {
+ u32 port;
+
+ type = CCN_TYPE_XP;
+ port = arm_ccn_node_to_xp_port(node_xp);
+ node_xp = arm_ccn_node_to_xp(node_xp);
+
+ arm_ccn_pmu_config_set(&event->attr.config,
+ node_xp, type, port);
+ }
+
+ /*
+ * We must NOT create groups containing mixed PMUs, although software
+ * events are acceptable (for example to create a CCN group
+ * periodically read when a hrtimer aka cpu-clock leader triggers).
+ */
+ if (event->group_leader->pmu != event->pmu &&
+ !is_software_event(event->group_leader))
+ return -EINVAL;
+
+ for_each_sibling_event(sibling, event->group_leader) {
+ if (sibling->pmu != event->pmu &&
+ !is_software_event(sibling))
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static u64 arm_ccn_pmu_read_counter(struct arm_ccn *ccn, int idx)
+{
+ u64 res;
+
+ if (idx == CCN_IDX_PMU_CYCLE_COUNTER) {
+#ifdef readq
+ res = readq(ccn->dt.base + CCN_DT_PMCCNTR);
+#else
+ /* 40 bit counter, can do snapshot and read in two parts */
+ writel(0x1, ccn->dt.base + CCN_DT_PMSR_REQ);
+ while (!(readl(ccn->dt.base + CCN_DT_PMSR) & 0x1))
+ ;
+ writel(0x1, ccn->dt.base + CCN_DT_PMSR_CLR);
+ res = readl(ccn->dt.base + CCN_DT_PMCCNTRSR + 4) & 0xff;
+ res <<= 32;
+ res |= readl(ccn->dt.base + CCN_DT_PMCCNTRSR);
+#endif
+ } else {
+ res = readl(ccn->dt.base + CCN_DT_PMEVCNT(idx));
+ }
+
+ return res;
+}
+
+static void arm_ccn_pmu_event_update(struct perf_event *event)
+{
+ struct arm_ccn *ccn = pmu_to_arm_ccn(event->pmu);
+ struct hw_perf_event *hw = &event->hw;
+ u64 prev_count, new_count, mask;
+
+ do {
+ prev_count = local64_read(&hw->prev_count);
+ new_count = arm_ccn_pmu_read_counter(ccn, hw->idx);
+ } while (local64_xchg(&hw->prev_count, new_count) != prev_count);
+
+ mask = (1LLU << (hw->idx == CCN_IDX_PMU_CYCLE_COUNTER ? 40 : 32)) - 1;
+
+ local64_add((new_count - prev_count) & mask, &event->count);
+}
+
+static void arm_ccn_pmu_xp_dt_config(struct perf_event *event, int enable)
+{
+ struct arm_ccn *ccn = pmu_to_arm_ccn(event->pmu);
+ struct hw_perf_event *hw = &event->hw;
+ struct arm_ccn_component *xp;
+ u32 val, dt_cfg;
+
+ /* Nothing to do for cycle counter */
+ if (hw->idx == CCN_IDX_PMU_CYCLE_COUNTER)
+ return;
+
+ if (CCN_CONFIG_TYPE(event->attr.config) == CCN_TYPE_XP)
+ xp = &ccn->xp[CCN_CONFIG_XP(event->attr.config)];
+ else
+ xp = &ccn->xp[arm_ccn_node_to_xp(
+ CCN_CONFIG_NODE(event->attr.config))];
+
+ if (enable)
+ dt_cfg = hw->event_base;
+ else
+ dt_cfg = CCN_XP_DT_CONFIG__DT_CFG__PASS_THROUGH;
+
+ spin_lock(&ccn->dt.config_lock);
+
+ val = readl(xp->base + CCN_XP_DT_CONFIG);
+ val &= ~(CCN_XP_DT_CONFIG__DT_CFG__MASK <<
+ CCN_XP_DT_CONFIG__DT_CFG__SHIFT(hw->idx));
+ val |= dt_cfg << CCN_XP_DT_CONFIG__DT_CFG__SHIFT(hw->idx);
+ writel(val, xp->base + CCN_XP_DT_CONFIG);
+
+ spin_unlock(&ccn->dt.config_lock);
+}
+
+static void arm_ccn_pmu_event_start(struct perf_event *event, int flags)
+{
+ struct arm_ccn *ccn = pmu_to_arm_ccn(event->pmu);
+ struct hw_perf_event *hw = &event->hw;
+
+ local64_set(&event->hw.prev_count,
+ arm_ccn_pmu_read_counter(ccn, hw->idx));
+ hw->state = 0;
+
+ /* Set the DT bus input, engaging the counter */
+ arm_ccn_pmu_xp_dt_config(event, 1);
+}
+
+static void arm_ccn_pmu_event_stop(struct perf_event *event, int flags)
+{
+ struct hw_perf_event *hw = &event->hw;
+
+ /* Disable counting, setting the DT bus to pass-through mode */
+ arm_ccn_pmu_xp_dt_config(event, 0);
+
+ if (flags & PERF_EF_UPDATE)
+ arm_ccn_pmu_event_update(event);
+
+ hw->state |= PERF_HES_STOPPED;
+}
+
+static void arm_ccn_pmu_xp_watchpoint_config(struct perf_event *event)
+{
+ struct arm_ccn *ccn = pmu_to_arm_ccn(event->pmu);
+ struct hw_perf_event *hw = &event->hw;
+ struct arm_ccn_component *source =
+ ccn->dt.pmu_counters[hw->idx].source;
+ unsigned long wp = hw->config_base;
+ u32 val;
+ u64 cmp_l = event->attr.config1;
+ u64 cmp_h = event->attr.config2;
+ u64 mask_l = ccn->dt.cmp_mask[CCN_CONFIG_MASK(event->attr.config)].l;
+ u64 mask_h = ccn->dt.cmp_mask[CCN_CONFIG_MASK(event->attr.config)].h;
+
+ hw->event_base = CCN_XP_DT_CONFIG__DT_CFG__WATCHPOINT(wp);
+
+ /* Direction (RX/TX), device (port) & virtual channel */
+ val = readl(source->base + CCN_XP_DT_INTERFACE_SEL);
+ val &= ~(CCN_XP_DT_INTERFACE_SEL__DT_IO_SEL__MASK <<
+ CCN_XP_DT_INTERFACE_SEL__DT_IO_SEL__SHIFT(wp));
+ val |= CCN_CONFIG_DIR(event->attr.config) <<
+ CCN_XP_DT_INTERFACE_SEL__DT_IO_SEL__SHIFT(wp);
+ val &= ~(CCN_XP_DT_INTERFACE_SEL__DT_DEV_SEL__MASK <<
+ CCN_XP_DT_INTERFACE_SEL__DT_DEV_SEL__SHIFT(wp));
+ val |= CCN_CONFIG_PORT(event->attr.config) <<
+ CCN_XP_DT_INTERFACE_SEL__DT_DEV_SEL__SHIFT(wp);
+ val &= ~(CCN_XP_DT_INTERFACE_SEL__DT_VC_SEL__MASK <<
+ CCN_XP_DT_INTERFACE_SEL__DT_VC_SEL__SHIFT(wp));
+ val |= CCN_CONFIG_VC(event->attr.config) <<
+ CCN_XP_DT_INTERFACE_SEL__DT_VC_SEL__SHIFT(wp);
+ writel(val, source->base + CCN_XP_DT_INTERFACE_SEL);
+
+ /* Comparison values */
+ writel(cmp_l & 0xffffffff, source->base + CCN_XP_DT_CMP_VAL_L(wp));
+ writel((cmp_l >> 32) & 0x7fffffff,
+ source->base + CCN_XP_DT_CMP_VAL_L(wp) + 4);
+ writel(cmp_h & 0xffffffff, source->base + CCN_XP_DT_CMP_VAL_H(wp));
+ writel((cmp_h >> 32) & 0x0fffffff,
+ source->base + CCN_XP_DT_CMP_VAL_H(wp) + 4);
+
+ /* Mask */
+ writel(mask_l & 0xffffffff, source->base + CCN_XP_DT_CMP_MASK_L(wp));
+ writel((mask_l >> 32) & 0x7fffffff,
+ source->base + CCN_XP_DT_CMP_MASK_L(wp) + 4);
+ writel(mask_h & 0xffffffff, source->base + CCN_XP_DT_CMP_MASK_H(wp));
+ writel((mask_h >> 32) & 0x0fffffff,
+ source->base + CCN_XP_DT_CMP_MASK_H(wp) + 4);
+}
+
+static void arm_ccn_pmu_xp_event_config(struct perf_event *event)
+{
+ struct arm_ccn *ccn = pmu_to_arm_ccn(event->pmu);
+ struct hw_perf_event *hw = &event->hw;
+ struct arm_ccn_component *source =
+ ccn->dt.pmu_counters[hw->idx].source;
+ u32 val, id;
+
+ hw->event_base = CCN_XP_DT_CONFIG__DT_CFG__XP_PMU_EVENT(hw->config_base);
+
+ id = (CCN_CONFIG_VC(event->attr.config) << 4) |
+ (CCN_CONFIG_BUS(event->attr.config) << 3) |
+ (CCN_CONFIG_EVENT(event->attr.config) << 0);
+
+ val = readl(source->base + CCN_XP_PMU_EVENT_SEL);
+ val &= ~(CCN_XP_PMU_EVENT_SEL__ID__MASK <<
+ CCN_XP_PMU_EVENT_SEL__ID__SHIFT(hw->config_base));
+ val |= id << CCN_XP_PMU_EVENT_SEL__ID__SHIFT(hw->config_base);
+ writel(val, source->base + CCN_XP_PMU_EVENT_SEL);
+}
+
+static void arm_ccn_pmu_node_event_config(struct perf_event *event)
+{
+ struct arm_ccn *ccn = pmu_to_arm_ccn(event->pmu);
+ struct hw_perf_event *hw = &event->hw;
+ struct arm_ccn_component *source =
+ ccn->dt.pmu_counters[hw->idx].source;
+ u32 type = CCN_CONFIG_TYPE(event->attr.config);
+ u32 val, port;
+
+ port = arm_ccn_node_to_xp_port(CCN_CONFIG_NODE(event->attr.config));
+ hw->event_base = CCN_XP_DT_CONFIG__DT_CFG__DEVICE_PMU_EVENT(port,
+ hw->config_base);
+
+ /* These *_event_sel regs should be identical, but let's make sure... */
+ BUILD_BUG_ON(CCN_HNF_PMU_EVENT_SEL != CCN_SBAS_PMU_EVENT_SEL);
+ BUILD_BUG_ON(CCN_SBAS_PMU_EVENT_SEL != CCN_RNI_PMU_EVENT_SEL);
+ BUILD_BUG_ON(CCN_HNF_PMU_EVENT_SEL__ID__SHIFT(1) !=
+ CCN_SBAS_PMU_EVENT_SEL__ID__SHIFT(1));
+ BUILD_BUG_ON(CCN_SBAS_PMU_EVENT_SEL__ID__SHIFT(1) !=
+ CCN_RNI_PMU_EVENT_SEL__ID__SHIFT(1));
+ BUILD_BUG_ON(CCN_HNF_PMU_EVENT_SEL__ID__MASK !=
+ CCN_SBAS_PMU_EVENT_SEL__ID__MASK);
+ BUILD_BUG_ON(CCN_SBAS_PMU_EVENT_SEL__ID__MASK !=
+ CCN_RNI_PMU_EVENT_SEL__ID__MASK);
+ if (WARN_ON(type != CCN_TYPE_HNF && type != CCN_TYPE_SBAS &&
+ !arm_ccn_pmu_type_eq(type, CCN_TYPE_RNI_3P)))
+ return;
+
+ /* Set the event id for the pre-allocated counter */
+ val = readl(source->base + CCN_HNF_PMU_EVENT_SEL);
+ val &= ~(CCN_HNF_PMU_EVENT_SEL__ID__MASK <<
+ CCN_HNF_PMU_EVENT_SEL__ID__SHIFT(hw->config_base));
+ val |= CCN_CONFIG_EVENT(event->attr.config) <<
+ CCN_HNF_PMU_EVENT_SEL__ID__SHIFT(hw->config_base);
+ writel(val, source->base + CCN_HNF_PMU_EVENT_SEL);
+}
+
+static void arm_ccn_pmu_event_config(struct perf_event *event)
+{
+ struct arm_ccn *ccn = pmu_to_arm_ccn(event->pmu);
+ struct hw_perf_event *hw = &event->hw;
+ u32 xp, offset, val;
+
+ /* Cycle counter requires no setup */
+ if (hw->idx == CCN_IDX_PMU_CYCLE_COUNTER)
+ return;
+
+ if (CCN_CONFIG_TYPE(event->attr.config) == CCN_TYPE_XP)
+ xp = CCN_CONFIG_XP(event->attr.config);
+ else
+ xp = arm_ccn_node_to_xp(CCN_CONFIG_NODE(event->attr.config));
+
+ spin_lock(&ccn->dt.config_lock);
+
+ /* Set the DT bus "distance" register */
+ offset = (hw->idx / 4) * 4;
+ val = readl(ccn->dt.base + CCN_DT_ACTIVE_DSM + offset);
+ val &= ~(CCN_DT_ACTIVE_DSM__DSM_ID__MASK <<
+ CCN_DT_ACTIVE_DSM__DSM_ID__SHIFT(hw->idx % 4));
+ val |= xp << CCN_DT_ACTIVE_DSM__DSM_ID__SHIFT(hw->idx % 4);
+ writel(val, ccn->dt.base + CCN_DT_ACTIVE_DSM + offset);
+
+ if (CCN_CONFIG_TYPE(event->attr.config) == CCN_TYPE_XP) {
+ if (CCN_CONFIG_EVENT(event->attr.config) ==
+ CCN_EVENT_WATCHPOINT)
+ arm_ccn_pmu_xp_watchpoint_config(event);
+ else
+ arm_ccn_pmu_xp_event_config(event);
+ } else {
+ arm_ccn_pmu_node_event_config(event);
+ }
+
+ spin_unlock(&ccn->dt.config_lock);
+}
+
+static int arm_ccn_pmu_active_counters(struct arm_ccn *ccn)
+{
+ return bitmap_weight(ccn->dt.pmu_counters_mask,
+ CCN_NUM_PMU_EVENT_COUNTERS + 1);
+}
+
+static int arm_ccn_pmu_event_add(struct perf_event *event, int flags)
+{
+ int err;
+ struct hw_perf_event *hw = &event->hw;
+ struct arm_ccn *ccn = pmu_to_arm_ccn(event->pmu);
+
+ err = arm_ccn_pmu_event_alloc(event);
+ if (err)
+ return err;
+
+ /*
+ * Pin the timer, so that the overflows are handled by the chosen
+ * event->cpu (this is the same one as presented in "cpumask"
+ * attribute).
+ */
+ if (!ccn->irq && arm_ccn_pmu_active_counters(ccn) == 1)
+ hrtimer_start(&ccn->dt.hrtimer, arm_ccn_pmu_timer_period(),
+ HRTIMER_MODE_REL_PINNED);
+
+ arm_ccn_pmu_event_config(event);
+
+ hw->state = PERF_HES_STOPPED;
+
+ if (flags & PERF_EF_START)
+ arm_ccn_pmu_event_start(event, PERF_EF_UPDATE);
+
+ return 0;
+}
+
+static void arm_ccn_pmu_event_del(struct perf_event *event, int flags)
+{
+ struct arm_ccn *ccn = pmu_to_arm_ccn(event->pmu);
+
+ arm_ccn_pmu_event_stop(event, PERF_EF_UPDATE);
+
+ arm_ccn_pmu_event_release(event);
+
+ if (!ccn->irq && arm_ccn_pmu_active_counters(ccn) == 0)
+ hrtimer_cancel(&ccn->dt.hrtimer);
+}
+
+static void arm_ccn_pmu_event_read(struct perf_event *event)
+{
+ arm_ccn_pmu_event_update(event);
+}
+
+static void arm_ccn_pmu_enable(struct pmu *pmu)
+{
+ struct arm_ccn *ccn = pmu_to_arm_ccn(pmu);
+
+ u32 val = readl(ccn->dt.base + CCN_DT_PMCR);
+ val |= CCN_DT_PMCR__PMU_EN;
+ writel(val, ccn->dt.base + CCN_DT_PMCR);
+}
+
+static void arm_ccn_pmu_disable(struct pmu *pmu)
+{
+ struct arm_ccn *ccn = pmu_to_arm_ccn(pmu);
+
+ u32 val = readl(ccn->dt.base + CCN_DT_PMCR);
+ val &= ~CCN_DT_PMCR__PMU_EN;
+ writel(val, ccn->dt.base + CCN_DT_PMCR);
+}
+
+static irqreturn_t arm_ccn_pmu_overflow_handler(struct arm_ccn_dt *dt)
+{
+ u32 pmovsr = readl(dt->base + CCN_DT_PMOVSR);
+ int idx;
+
+ if (!pmovsr)
+ return IRQ_NONE;
+
+ writel(pmovsr, dt->base + CCN_DT_PMOVSR_CLR);
+
+ BUILD_BUG_ON(CCN_IDX_PMU_CYCLE_COUNTER != CCN_NUM_PMU_EVENT_COUNTERS);
+
+ for (idx = 0; idx < CCN_NUM_PMU_EVENT_COUNTERS + 1; idx++) {
+ struct perf_event *event = dt->pmu_counters[idx].event;
+ int overflowed = pmovsr & BIT(idx);
+
+ WARN_ON_ONCE(overflowed && !event &&
+ idx != CCN_IDX_PMU_CYCLE_COUNTER);
+
+ if (!event || !overflowed)
+ continue;
+
+ arm_ccn_pmu_event_update(event);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static enum hrtimer_restart arm_ccn_pmu_timer_handler(struct hrtimer *hrtimer)
+{
+ struct arm_ccn_dt *dt = container_of(hrtimer, struct arm_ccn_dt,
+ hrtimer);
+ unsigned long flags;
+
+ local_irq_save(flags);
+ arm_ccn_pmu_overflow_handler(dt);
+ local_irq_restore(flags);
+
+ hrtimer_forward_now(hrtimer, arm_ccn_pmu_timer_period());
+ return HRTIMER_RESTART;
+}
+
+
+static int arm_ccn_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node)
+{
+ struct arm_ccn_dt *dt = hlist_entry_safe(node, struct arm_ccn_dt, node);
+ struct arm_ccn *ccn = container_of(dt, struct arm_ccn, dt);
+ unsigned int target;
+
+ if (!cpumask_test_and_clear_cpu(cpu, &dt->cpu))
+ return 0;
+ target = cpumask_any_but(cpu_online_mask, cpu);
+ if (target >= nr_cpu_ids)
+ return 0;
+ perf_pmu_migrate_context(&dt->pmu, cpu, target);
+ cpumask_set_cpu(target, &dt->cpu);
+ if (ccn->irq)
+ WARN_ON(irq_set_affinity_hint(ccn->irq, &dt->cpu) != 0);
+ return 0;
+}
+
+static DEFINE_IDA(arm_ccn_pmu_ida);
+
+static int arm_ccn_pmu_init(struct arm_ccn *ccn)
+{
+ int i;
+ char *name;
+ int err;
+
+ /* Initialize DT subsystem */
+ ccn->dt.base = ccn->base + CCN_REGION_SIZE;
+ spin_lock_init(&ccn->dt.config_lock);
+ writel(CCN_DT_PMOVSR_CLR__MASK, ccn->dt.base + CCN_DT_PMOVSR_CLR);
+ writel(CCN_DT_CTL__DT_EN, ccn->dt.base + CCN_DT_CTL);
+ writel(CCN_DT_PMCR__OVFL_INTR_EN | CCN_DT_PMCR__PMU_EN,
+ ccn->dt.base + CCN_DT_PMCR);
+ writel(0x1, ccn->dt.base + CCN_DT_PMSR_CLR);
+ for (i = 0; i < ccn->num_xps; i++) {
+ writel(0, ccn->xp[i].base + CCN_XP_DT_CONFIG);
+ writel((CCN_XP_DT_CONTROL__WP_ARM_SEL__ALWAYS <<
+ CCN_XP_DT_CONTROL__WP_ARM_SEL__SHIFT(0)) |
+ (CCN_XP_DT_CONTROL__WP_ARM_SEL__ALWAYS <<
+ CCN_XP_DT_CONTROL__WP_ARM_SEL__SHIFT(1)) |
+ CCN_XP_DT_CONTROL__DT_ENABLE,
+ ccn->xp[i].base + CCN_XP_DT_CONTROL);
+ }
+ ccn->dt.cmp_mask[CCN_IDX_MASK_ANY].l = ~0;
+ ccn->dt.cmp_mask[CCN_IDX_MASK_ANY].h = ~0;
+ ccn->dt.cmp_mask[CCN_IDX_MASK_EXACT].l = 0;
+ ccn->dt.cmp_mask[CCN_IDX_MASK_EXACT].h = 0;
+ ccn->dt.cmp_mask[CCN_IDX_MASK_ORDER].l = ~0;
+ ccn->dt.cmp_mask[CCN_IDX_MASK_ORDER].h = ~(0x1 << 15);
+ ccn->dt.cmp_mask[CCN_IDX_MASK_OPCODE].l = ~0;
+ ccn->dt.cmp_mask[CCN_IDX_MASK_OPCODE].h = ~(0x1f << 9);
+
+ /* Get a convenient /sys/event_source/devices/ name */
+ ccn->dt.id = ida_simple_get(&arm_ccn_pmu_ida, 0, 0, GFP_KERNEL);
+ if (ccn->dt.id == 0) {
+ name = "ccn";
+ } else {
+ name = devm_kasprintf(ccn->dev, GFP_KERNEL, "ccn_%d",
+ ccn->dt.id);
+ if (!name) {
+ err = -ENOMEM;
+ goto error_choose_name;
+ }
+ }
+
+ /* Perf driver registration */
+ ccn->dt.pmu = (struct pmu) {
+ .module = THIS_MODULE,
+ .attr_groups = arm_ccn_pmu_attr_groups,
+ .task_ctx_nr = perf_invalid_context,
+ .event_init = arm_ccn_pmu_event_init,
+ .add = arm_ccn_pmu_event_add,
+ .del = arm_ccn_pmu_event_del,
+ .start = arm_ccn_pmu_event_start,
+ .stop = arm_ccn_pmu_event_stop,
+ .read = arm_ccn_pmu_event_read,
+ .pmu_enable = arm_ccn_pmu_enable,
+ .pmu_disable = arm_ccn_pmu_disable,
+ };
+
+ /* No overflow interrupt? Have to use a timer instead. */
+ if (!ccn->irq) {
+ dev_info(ccn->dev, "No access to interrupts, using timer.\n");
+ hrtimer_init(&ccn->dt.hrtimer, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+ ccn->dt.hrtimer.function = arm_ccn_pmu_timer_handler;
+ }
+
+ /* Pick one CPU which we will use to collect data from CCN... */
+ cpumask_set_cpu(get_cpu(), &ccn->dt.cpu);
+
+ /* Also make sure that the overflow interrupt is handled by this CPU */
+ if (ccn->irq) {
+ err = irq_set_affinity_hint(ccn->irq, &ccn->dt.cpu);
+ if (err) {
+ dev_err(ccn->dev, "Failed to set interrupt affinity!\n");
+ goto error_set_affinity;
+ }
+ }
+
+ err = perf_pmu_register(&ccn->dt.pmu, name, -1);
+ if (err)
+ goto error_pmu_register;
+
+ cpuhp_state_add_instance_nocalls(CPUHP_AP_PERF_ARM_CCN_ONLINE,
+ &ccn->dt.node);
+ put_cpu();
+ return 0;
+
+error_pmu_register:
+error_set_affinity:
+ put_cpu();
+error_choose_name:
+ ida_simple_remove(&arm_ccn_pmu_ida, ccn->dt.id);
+ for (i = 0; i < ccn->num_xps; i++)
+ writel(0, ccn->xp[i].base + CCN_XP_DT_CONTROL);
+ writel(0, ccn->dt.base + CCN_DT_PMCR);
+ return err;
+}
+
+static void arm_ccn_pmu_cleanup(struct arm_ccn *ccn)
+{
+ int i;
+
+ cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_CCN_ONLINE,
+ &ccn->dt.node);
+ if (ccn->irq)
+ irq_set_affinity_hint(ccn->irq, NULL);
+ for (i = 0; i < ccn->num_xps; i++)
+ writel(0, ccn->xp[i].base + CCN_XP_DT_CONTROL);
+ writel(0, ccn->dt.base + CCN_DT_PMCR);
+ perf_pmu_unregister(&ccn->dt.pmu);
+ ida_simple_remove(&arm_ccn_pmu_ida, ccn->dt.id);
+}
+
+static int arm_ccn_for_each_valid_region(struct arm_ccn *ccn,
+ int (*callback)(struct arm_ccn *ccn, int region,
+ void __iomem *base, u32 type, u32 id))
+{
+ int region;
+
+ for (region = 0; region < CCN_NUM_REGIONS; region++) {
+ u32 val, type, id;
+ void __iomem *base;
+ int err;
+
+ val = readl(ccn->base + CCN_MN_OLY_COMP_LIST_63_0 +
+ 4 * (region / 32));
+ if (!(val & (1 << (region % 32))))
+ continue;
+
+ base = ccn->base + region * CCN_REGION_SIZE;
+ val = readl(base + CCN_ALL_OLY_ID);
+ type = (val >> CCN_ALL_OLY_ID__OLY_ID__SHIFT) &
+ CCN_ALL_OLY_ID__OLY_ID__MASK;
+ id = (val >> CCN_ALL_OLY_ID__NODE_ID__SHIFT) &
+ CCN_ALL_OLY_ID__NODE_ID__MASK;
+
+ err = callback(ccn, region, base, type, id);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int arm_ccn_get_nodes_num(struct arm_ccn *ccn, int region,
+ void __iomem *base, u32 type, u32 id)
+{
+
+ if (type == CCN_TYPE_XP && id >= ccn->num_xps)
+ ccn->num_xps = id + 1;
+ else if (id >= ccn->num_nodes)
+ ccn->num_nodes = id + 1;
+
+ return 0;
+}
+
+static int arm_ccn_init_nodes(struct arm_ccn *ccn, int region,
+ void __iomem *base, u32 type, u32 id)
+{
+ struct arm_ccn_component *component;
+
+ dev_dbg(ccn->dev, "Region %d: id=%u, type=0x%02x\n", region, id, type);
+
+ switch (type) {
+ case CCN_TYPE_MN:
+ ccn->mn_id = id;
+ return 0;
+ case CCN_TYPE_DT:
+ return 0;
+ case CCN_TYPE_XP:
+ component = &ccn->xp[id];
+ break;
+ case CCN_TYPE_SBSX:
+ ccn->sbsx_present = 1;
+ component = &ccn->node[id];
+ break;
+ case CCN_TYPE_SBAS:
+ ccn->sbas_present = 1;
+ /* Fall-through */
+ default:
+ component = &ccn->node[id];
+ break;
+ }
+
+ component->base = base;
+ component->type = type;
+
+ return 0;
+}
+
+
+static irqreturn_t arm_ccn_error_handler(struct arm_ccn *ccn,
+ const u32 *err_sig_val)
+{
+ /* This should be really handled by firmware... */
+ dev_err(ccn->dev, "Error reported in %08x%08x%08x%08x%08x%08x.\n",
+ err_sig_val[5], err_sig_val[4], err_sig_val[3],
+ err_sig_val[2], err_sig_val[1], err_sig_val[0]);
+ dev_err(ccn->dev, "Disabling interrupt generation for all errors.\n");
+ writel(CCN_MN_ERRINT_STATUS__ALL_ERRORS__DISABLE,
+ ccn->base + CCN_MN_ERRINT_STATUS);
+
+ return IRQ_HANDLED;
+}
+
+
+static irqreturn_t arm_ccn_irq_handler(int irq, void *dev_id)
+{
+ irqreturn_t res = IRQ_NONE;
+ struct arm_ccn *ccn = dev_id;
+ u32 err_sig_val[6];
+ u32 err_or;
+ int i;
+
+ /* PMU overflow is a special case */
+ err_or = err_sig_val[0] = readl(ccn->base + CCN_MN_ERR_SIG_VAL_63_0);
+ if (err_or & CCN_MN_ERR_SIG_VAL_63_0__DT) {
+ err_or &= ~CCN_MN_ERR_SIG_VAL_63_0__DT;
+ res = arm_ccn_pmu_overflow_handler(&ccn->dt);
+ }
+
+ /* Have to read all err_sig_vals to clear them */
+ for (i = 1; i < ARRAY_SIZE(err_sig_val); i++) {
+ err_sig_val[i] = readl(ccn->base +
+ CCN_MN_ERR_SIG_VAL_63_0 + i * 4);
+ err_or |= err_sig_val[i];
+ }
+ if (err_or)
+ res |= arm_ccn_error_handler(ccn, err_sig_val);
+
+ if (res != IRQ_NONE)
+ writel(CCN_MN_ERRINT_STATUS__INTREQ__DESSERT,
+ ccn->base + CCN_MN_ERRINT_STATUS);
+
+ return res;
+}
+
+
+static int arm_ccn_probe(struct platform_device *pdev)
+{
+ struct arm_ccn *ccn;
+ struct resource *res;
+ unsigned int irq;
+ int err;
+
+ ccn = devm_kzalloc(&pdev->dev, sizeof(*ccn), GFP_KERNEL);
+ if (!ccn)
+ return -ENOMEM;
+ ccn->dev = &pdev->dev;
+ platform_set_drvdata(pdev, ccn);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+
+ if (!devm_request_mem_region(ccn->dev, res->start,
+ resource_size(res), pdev->name))
+ return -EBUSY;
+
+ ccn->base = devm_ioremap(ccn->dev, res->start,
+ resource_size(res));
+ if (!ccn->base)
+ return -EFAULT;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res)
+ return -EINVAL;
+ irq = res->start;
+
+ /* Check if we can use the interrupt */
+ writel(CCN_MN_ERRINT_STATUS__PMU_EVENTS__DISABLE,
+ ccn->base + CCN_MN_ERRINT_STATUS);
+ if (readl(ccn->base + CCN_MN_ERRINT_STATUS) &
+ CCN_MN_ERRINT_STATUS__PMU_EVENTS__DISABLED) {
+ /* Can set 'disable' bits, so can acknowledge interrupts */
+ writel(CCN_MN_ERRINT_STATUS__PMU_EVENTS__ENABLE,
+ ccn->base + CCN_MN_ERRINT_STATUS);
+ err = devm_request_irq(ccn->dev, irq, arm_ccn_irq_handler,
+ IRQF_NOBALANCING | IRQF_NO_THREAD,
+ dev_name(ccn->dev), ccn);
+ if (err)
+ return err;
+
+ ccn->irq = irq;
+ }
+
+
+ /* Build topology */
+
+ err = arm_ccn_for_each_valid_region(ccn, arm_ccn_get_nodes_num);
+ if (err)
+ return err;
+
+ ccn->node = devm_kcalloc(ccn->dev, ccn->num_nodes, sizeof(*ccn->node),
+ GFP_KERNEL);
+ ccn->xp = devm_kcalloc(ccn->dev, ccn->num_xps, sizeof(*ccn->node),
+ GFP_KERNEL);
+ if (!ccn->node || !ccn->xp)
+ return -ENOMEM;
+
+ err = arm_ccn_for_each_valid_region(ccn, arm_ccn_init_nodes);
+ if (err)
+ return err;
+
+ return arm_ccn_pmu_init(ccn);
+}
+
+static int arm_ccn_remove(struct platform_device *pdev)
+{
+ struct arm_ccn *ccn = platform_get_drvdata(pdev);
+
+ arm_ccn_pmu_cleanup(ccn);
+
+ return 0;
+}
+
+static const struct of_device_id arm_ccn_match[] = {
+ { .compatible = "arm,ccn-502", },
+ { .compatible = "arm,ccn-504", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, arm_ccn_match);
+
+static struct platform_driver arm_ccn_driver = {
+ .driver = {
+ .name = "arm-ccn",
+ .of_match_table = arm_ccn_match,
+ },
+ .probe = arm_ccn_probe,
+ .remove = arm_ccn_remove,
+};
+
+static int __init arm_ccn_init(void)
+{
+ int i, ret;
+
+ ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_CCN_ONLINE,
+ "perf/arm/ccn:online", NULL,
+ arm_ccn_pmu_offline_cpu);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(arm_ccn_pmu_events); i++)
+ arm_ccn_pmu_events_attrs[i] = &arm_ccn_pmu_events[i].attr.attr;
+
+ ret = platform_driver_register(&arm_ccn_driver);
+ if (ret)
+ cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_CCN_ONLINE);
+ return ret;
+}
+
+static void __exit arm_ccn_exit(void)
+{
+ platform_driver_unregister(&arm_ccn_driver);
+ cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_CCN_ONLINE);
+}
+
+module_init(arm_ccn_init);
+module_exit(arm_ccn_exit);
+
+MODULE_AUTHOR("Pawel Moll <pawel.moll@arm.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/perf/arm_dsu_pmu.c b/drivers/perf/arm_dsu_pmu.c
index 38f2cc2a6c74..660cb8ac886a 100644
--- a/drivers/perf/arm_dsu_pmu.c
+++ b/drivers/perf/arm_dsu_pmu.c
@@ -536,7 +536,7 @@ static bool dsu_pmu_validate_group(struct perf_event *event)
memset(fake_hw.used_mask, 0, sizeof(fake_hw.used_mask));
if (!dsu_pmu_validate_event(event->pmu, &fake_hw, leader))
return false;
- list_for_each_entry(sibling, &leader->sibling_list, group_entry) {
+ for_each_sibling_event(sibling, leader) {
if (!dsu_pmu_validate_event(event->pmu, &fake_hw, sibling))
return false;
}
diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index f63db346c219..1a0d340b65cf 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -311,7 +311,7 @@ validate_group(struct perf_event *event)
if (!validate_event(event->pmu, &fake_pmu, leader))
return -EINVAL;
- list_for_each_entry(sibling, &leader->sibling_list, group_entry) {
+ for_each_sibling_event(sibling, leader) {
if (!validate_event(event->pmu, &fake_pmu, sibling))
return -EINVAL;
}
diff --git a/drivers/perf/arm_pmu_platform.c b/drivers/perf/arm_pmu_platform.c
index 7729eda5909d..971ff336494a 100644
--- a/drivers/perf/arm_pmu_platform.c
+++ b/drivers/perf/arm_pmu_platform.c
@@ -122,7 +122,7 @@ static int pmu_parse_irqs(struct arm_pmu *pmu)
return pmu_parse_percpu_irq(pmu, irq);
}
- if (!pmu_has_irq_affinity(pdev->dev.of_node)) {
+ if (nr_cpu_ids != 1 && !pmu_has_irq_affinity(pdev->dev.of_node)) {
pr_warn("no interrupt-affinity property for %pOF, guessing.\n",
pdev->dev.of_node);
}
diff --git a/drivers/perf/arm_spe_pmu.c b/drivers/perf/arm_spe_pmu.c
index 51b40aecb776..28bb642af18b 100644
--- a/drivers/perf/arm_spe_pmu.c
+++ b/drivers/perf/arm_spe_pmu.c
@@ -23,16 +23,30 @@
#define DRVNAME PMUNAME "_pmu"
#define pr_fmt(fmt) DRVNAME ": " fmt
+#include <linux/bitops.h>
+#include <linux/bug.h>
+#include <linux/capability.h>
#include <linux/cpuhotplug.h>
+#include <linux/cpumask.h>
+#include <linux/device.h>
+#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/perf_event.h>
#include <linux/platform_device.h>
+#include <linux/printk.h>
#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/vmalloc.h>
+#include <asm/barrier.h>
+#include <asm/cpufeature.h>
+#include <asm/mmu.h>
#include <asm/sysreg.h>
#define ARM_SPE_BUF_PAD_BYTE 0
diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pmu.c
index 7ed24b954422..44df61397a38 100644
--- a/drivers/perf/hisilicon/hisi_uncore_pmu.c
+++ b/drivers/perf/hisilicon/hisi_uncore_pmu.c
@@ -82,8 +82,7 @@ static bool hisi_validate_event_group(struct perf_event *event)
counters++;
}
- list_for_each_entry(sibling, &event->group_leader->sibling_list,
- group_entry) {
+ for_each_sibling_event(sibling, event->group_leader) {
if (is_software_event(sibling))
continue;
if (sibling->pmu != event->pmu)
diff --git a/drivers/perf/qcom_l2_pmu.c b/drivers/perf/qcom_l2_pmu.c
index 4fdc8486a8e4..842135cf35a3 100644
--- a/drivers/perf/qcom_l2_pmu.c
+++ b/drivers/perf/qcom_l2_pmu.c
@@ -534,14 +534,14 @@ static int l2_cache_event_init(struct perf_event *event)
return -EINVAL;
}
- list_for_each_entry(sibling, &event->group_leader->sibling_list,
- group_entry)
+ for_each_sibling_event(sibling, event->group_leader) {
if (sibling->pmu != event->pmu &&
!is_software_event(sibling)) {
dev_dbg_ratelimited(&l2cache_pmu->pdev->dev,
"Can't create mixed PMU group\n");
return -EINVAL;
}
+ }
cluster = get_cluster_pmu(l2cache_pmu, event->cpu);
if (!cluster) {
@@ -571,8 +571,7 @@ static int l2_cache_event_init(struct perf_event *event)
return -EINVAL;
}
- list_for_each_entry(sibling, &event->group_leader->sibling_list,
- group_entry) {
+ for_each_sibling_event(sibling, event->group_leader) {
if ((sibling != event) &&
!is_software_event(sibling) &&
(L2_EVT_GROUP(sibling->attr.config) ==
diff --git a/drivers/perf/qcom_l3_pmu.c b/drivers/perf/qcom_l3_pmu.c
index 7f6b62b29e9d..2dc63d61f2ea 100644
--- a/drivers/perf/qcom_l3_pmu.c
+++ b/drivers/perf/qcom_l3_pmu.c
@@ -468,7 +468,7 @@ static bool qcom_l3_cache__validate_event_group(struct perf_event *event)
counters = event_num_counters(event);
counters += event_num_counters(leader);
- list_for_each_entry(sibling, &leader->sibling_list, group_entry) {
+ for_each_sibling_event(sibling, leader) {
if (is_software_event(sibling))
continue;
if (sibling->pmu != event->pmu)
diff --git a/drivers/perf/xgene_pmu.c b/drivers/perf/xgene_pmu.c
index eb23311bc70c..6bdb1dad805f 100644
--- a/drivers/perf/xgene_pmu.c
+++ b/drivers/perf/xgene_pmu.c
@@ -949,11 +949,11 @@ static int xgene_perf_event_init(struct perf_event *event)
!is_software_event(event->group_leader))
return -EINVAL;
- list_for_each_entry(sibling, &event->group_leader->sibling_list,
- group_entry)
+ for_each_sibling_event(sibling, event->group_leader) {
if (sibling->pmu != event->pmu &&
!is_software_event(sibling))
return -EINVAL;
+ }
return 0;
}
diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c
index aa857be692cf..d4dcd39b8d76 100644
--- a/drivers/phy/allwinner/phy-sun4i-usb.c
+++ b/drivers/phy/allwinner/phy-sun4i-usb.c
@@ -112,6 +112,7 @@ enum sun4i_usb_phy_type {
sun8i_a33_phy,
sun8i_a83t_phy,
sun8i_h3_phy,
+ sun8i_r40_phy,
sun8i_v3s_phy,
sun50i_a64_phy,
};
@@ -410,11 +411,13 @@ static bool sun4i_usb_phy0_poll(struct sun4i_usb_phy_data *data)
return true;
/*
- * The A31 companion pmic (axp221) does not generate vbus change
- * interrupts when the board is driving vbus, so we must poll
+ * The A31/A23/A33 companion pmics (AXP221/AXP223) do not
+ * generate vbus change interrupts when the board is driving
+ * vbus using the N_VBUSEN pin on the pmic, so we must poll
* when using the pmic for vbus-det _and_ we're driving vbus.
*/
- if (data->cfg->type == sun6i_a31_phy &&
+ if ((data->cfg->type == sun6i_a31_phy ||
+ data->cfg->type == sun8i_a33_phy) &&
data->vbus_power_supply && data->phys[0].regulator_on)
return true;
@@ -885,7 +888,7 @@ static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = {
static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = {
.num_phys = 2,
- .type = sun4i_a10_phy,
+ .type = sun6i_a31_phy,
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A10,
.dedicated_clocks = true,
@@ -919,6 +922,16 @@ static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = {
.phy0_dual_route = true,
};
+static const struct sun4i_usb_phy_cfg sun8i_r40_cfg = {
+ .num_phys = 3,
+ .type = sun8i_r40_phy,
+ .disc_thresh = 3,
+ .phyctl_offset = REG_PHYCTL_A33,
+ .dedicated_clocks = true,
+ .enable_pmu_unk1 = true,
+ .phy0_dual_route = true,
+};
+
static const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = {
.num_phys = 1,
.type = sun8i_v3s_phy,
@@ -948,6 +961,7 @@ static const struct of_device_id sun4i_usb_phy_of_match[] = {
{ .compatible = "allwinner,sun8i-a33-usb-phy", .data = &sun8i_a33_cfg },
{ .compatible = "allwinner,sun8i-a83t-usb-phy", .data = &sun8i_a83t_cfg },
{ .compatible = "allwinner,sun8i-h3-usb-phy", .data = &sun8i_h3_cfg },
+ { .compatible = "allwinner,sun8i-r40-usb-phy", .data = &sun8i_r40_cfg },
{ .compatible = "allwinner,sun8i-v3s-usb-phy", .data = &sun8i_v3s_cfg },
{ .compatible = "allwinner,sun50i-a64-usb-phy",
.data = &sun50i_a64_cfg},
diff --git a/drivers/phy/amlogic/Kconfig b/drivers/phy/amlogic/Kconfig
index cb8f4501652b..23fe1cda2f70 100644
--- a/drivers/phy/amlogic/Kconfig
+++ b/drivers/phy/amlogic/Kconfig
@@ -18,10 +18,21 @@ config PHY_MESON_GXL_USB2
default ARCH_MESON
depends on OF && (ARCH_MESON || COMPILE_TEST)
depends on USB_SUPPORT
- select USB_COMMON
select GENERIC_PHY
select REGMAP_MMIO
help
Enable this to support the Meson USB2 PHYs found in Meson
GXL and GXM SoCs.
If unsure, say N.
+
+config PHY_MESON_GXL_USB3
+ tristate "Meson GXL and GXM USB3 PHY drivers"
+ default ARCH_MESON
+ depends on OF && (ARCH_MESON || COMPILE_TEST)
+ depends on USB_SUPPORT
+ select GENERIC_PHY
+ select REGMAP_MMIO
+ help
+ Enable this to support the Meson USB3 PHY and OTG detection
+ IP block found in Meson GXL and GXM SoCs.
+ If unsure, say N.
diff --git a/drivers/phy/amlogic/Makefile b/drivers/phy/amlogic/Makefile
index cfdc98715c30..4fd8848c194d 100644
--- a/drivers/phy/amlogic/Makefile
+++ b/drivers/phy/amlogic/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_PHY_MESON8B_USB2) += phy-meson8b-usb2.o
obj-$(CONFIG_PHY_MESON_GXL_USB2) += phy-meson-gxl-usb2.o
+obj-$(CONFIG_PHY_MESON_GXL_USB3) += phy-meson-gxl-usb3.o
diff --git a/drivers/phy/amlogic/phy-meson-gxl-usb2.c b/drivers/phy/amlogic/phy-meson-gxl-usb2.c
index e90c4ee25dfe..9f9b5414b97a 100644
--- a/drivers/phy/amlogic/phy-meson-gxl-usb2.c
+++ b/drivers/phy/amlogic/phy-meson-gxl-usb2.c
@@ -11,14 +11,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
+#include <linux/reset.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
-#include <linux/usb/of.h>
/* bits [31:27] are read-only */
#define U2P_R0 0x0
@@ -70,12 +71,11 @@
/* bits [31:14] are read-only */
#define U2P_R2 0x8
- #define U2P_R2_DATA_IN_MASK GENMASK(3, 0)
- #define U2P_R2_DATA_IN_EN_MASK GENMASK(7, 4)
- #define U2P_R2_ADDR_MASK GENMASK(11, 8)
- #define U2P_R2_DATA_OUT_SEL BIT(12)
- #define U2P_R2_CLK BIT(13)
- #define U2P_R2_DATA_OUT_MASK GENMASK(17, 14)
+ #define U2P_R2_TESTDATA_IN_MASK GENMASK(7, 0)
+ #define U2P_R2_TESTADDR_MASK GENMASK(11, 8)
+ #define U2P_R2_TESTDATA_OUT_SEL BIT(12)
+ #define U2P_R2_TESTCLK BIT(13)
+ #define U2P_R2_TESTDATA_OUT_MASK GENMASK(17, 14)
#define U2P_R2_ACA_PIN_RANGE_C BIT(18)
#define U2P_R2_ACA_PIN_RANGE_B BIT(19)
#define U2P_R2_ACA_PIN_RANGE_A BIT(20)
@@ -99,6 +99,8 @@ struct phy_meson_gxl_usb2_priv {
struct regmap *regmap;
enum phy_mode mode;
int is_enabled;
+ struct clk *clk;
+ struct reset_control *reset;
};
static const struct regmap_config phy_meson_gxl_usb2_regmap_conf = {
@@ -108,6 +110,31 @@ static const struct regmap_config phy_meson_gxl_usb2_regmap_conf = {
.max_register = U2P_R3,
};
+static int phy_meson_gxl_usb2_init(struct phy *phy)
+{
+ struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
+ int ret;
+
+ ret = reset_control_reset(priv->reset);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int phy_meson_gxl_usb2_exit(struct phy *phy)
+{
+ struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
+
+ clk_disable_unprepare(priv->clk);
+
+ return 0;
+}
+
static int phy_meson_gxl_usb2_reset(struct phy *phy)
{
struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
@@ -195,6 +222,8 @@ static int phy_meson_gxl_usb2_power_on(struct phy *phy)
}
static const struct phy_ops phy_meson_gxl_usb2_ops = {
+ .init = phy_meson_gxl_usb2_init,
+ .exit = phy_meson_gxl_usb2_exit,
.power_on = phy_meson_gxl_usb2_power_on,
.power_off = phy_meson_gxl_usb2_power_off,
.set_mode = phy_meson_gxl_usb2_set_mode,
@@ -210,6 +239,7 @@ static int phy_meson_gxl_usb2_probe(struct platform_device *pdev)
struct phy_meson_gxl_usb2_priv *priv;
struct phy *phy;
void __iomem *base;
+ int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -222,28 +252,34 @@ static int phy_meson_gxl_usb2_probe(struct platform_device *pdev)
if (IS_ERR(base))
return PTR_ERR(base);
- switch (of_usb_get_dr_mode_by_phy(dev->of_node, -1)) {
- case USB_DR_MODE_PERIPHERAL:
- priv->mode = PHY_MODE_USB_DEVICE;
- break;
- case USB_DR_MODE_OTG:
- priv->mode = PHY_MODE_USB_OTG;
- break;
- case USB_DR_MODE_HOST:
- default:
- priv->mode = PHY_MODE_USB_HOST;
- break;
- }
+ /* start in host mode */
+ priv->mode = PHY_MODE_USB_HOST;
priv->regmap = devm_regmap_init_mmio(dev, base,
&phy_meson_gxl_usb2_regmap_conf);
if (IS_ERR(priv->regmap))
return PTR_ERR(priv->regmap);
+ priv->clk = devm_clk_get(dev, "phy");
+ if (IS_ERR(priv->clk)) {
+ ret = PTR_ERR(priv->clk);
+ if (ret == -ENOENT)
+ priv->clk = NULL;
+ else
+ return ret;
+ }
+
+ priv->reset = devm_reset_control_get_optional_shared(dev, "phy");
+ if (IS_ERR(priv->reset))
+ return PTR_ERR(priv->reset);
+
phy = devm_phy_create(dev, NULL, &phy_meson_gxl_usb2_ops);
if (IS_ERR(phy)) {
- dev_err(dev, "failed to create PHY\n");
- return PTR_ERR(phy);
+ ret = PTR_ERR(phy);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "failed to create PHY\n");
+
+ return ret;
}
phy_set_drvdata(phy, priv);
diff --git a/drivers/phy/amlogic/phy-meson-gxl-usb3.c b/drivers/phy/amlogic/phy-meson-gxl-usb3.c
new file mode 100644
index 000000000000..d37d94ddf9c0
--- /dev/null
+++ b/drivers/phy/amlogic/phy-meson-gxl-usb3.c
@@ -0,0 +1,282 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Meson GXL USB3 PHY and OTG mode detection driver
+ *
+ * Copyright (C) 2018 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/platform_device.h>
+
+#define USB_R0 0x00
+ #define USB_R0_P30_FSEL_MASK GENMASK(5, 0)
+ #define USB_R0_P30_PHY_RESET BIT(6)
+ #define USB_R0_P30_TEST_POWERDOWN_HSP BIT(7)
+ #define USB_R0_P30_TEST_POWERDOWN_SSP BIT(8)
+ #define USB_R0_P30_ACJT_LEVEL_MASK GENMASK(13, 9)
+ #define USB_R0_P30_TX_BOOST_LEVEL_MASK GENMASK(16, 14)
+ #define USB_R0_P30_LANE0_TX2RX_LOOPBACK BIT(17)
+ #define USB_R0_P30_LANE0_EXT_PCLK_REQ BIT(18)
+ #define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK GENMASK(28, 19)
+ #define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK GENMASK(30, 29)
+ #define USB_R0_U2D_ACT BIT(31)
+
+#define USB_R1 0x04
+ #define USB_R1_U3H_BIGENDIAN_GS BIT(0)
+ #define USB_R1_U3H_PME_ENABLE BIT(1)
+ #define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK GENMASK(6, 2)
+ #define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK GENMASK(11, 7)
+ #define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK GENMASK(15, 12)
+ #define USB_R1_U3H_HOST_U3_PORT_DISABLE BIT(16)
+ #define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT BIT(17)
+ #define USB_R1_U3H_HOST_MSI_ENABLE BIT(18)
+ #define USB_R1_U3H_FLADJ_30MHZ_REG_MASK GENMASK(24, 19)
+ #define USB_R1_P30_PCS_TX_SWING_FULL_MASK GENMASK(31, 25)
+
+#define USB_R2 0x08
+ #define USB_R2_P30_CR_DATA_IN_MASK GENMASK(15, 0)
+ #define USB_R2_P30_CR_READ BIT(16)
+ #define USB_R2_P30_CR_WRITE BIT(17)
+ #define USB_R2_P30_CR_CAP_ADDR BIT(18)
+ #define USB_R2_P30_CR_CAP_DATA BIT(19)
+ #define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(25, 20)
+ #define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK GENMASK(31, 26)
+
+#define USB_R3 0x0c
+ #define USB_R3_P30_SSC_ENABLE BIT(0)
+ #define USB_R3_P30_SSC_RANGE_MASK GENMASK(3, 1)
+ #define USB_R3_P30_SSC_REF_CLK_SEL_MASK GENMASK(12, 4)
+ #define USB_R3_P30_REF_SSP_EN BIT(13)
+ #define USB_R3_P30_LOS_BIAS_MASK GENMASK(18, 16)
+ #define USB_R3_P30_LOS_LEVEL_MASK GENMASK(23, 19)
+ #define USB_R3_P30_MPLL_MULTIPLIER_MASK GENMASK(30, 24)
+
+#define USB_R4 0x10
+ #define USB_R4_P21_PORT_RESET_0 BIT(0)
+ #define USB_R4_P21_SLEEP_M0 BIT(1)
+ #define USB_R4_MEM_PD_MASK GENMASK(3, 2)
+ #define USB_R4_P21_ONLY BIT(4)
+
+#define USB_R5 0x14
+ #define USB_R5_ID_DIG_SYNC BIT(0)
+ #define USB_R5_ID_DIG_REG BIT(1)
+ #define USB_R5_ID_DIG_CFG_MASK GENMASK(3, 2)
+ #define USB_R5_ID_DIG_EN_0 BIT(4)
+ #define USB_R5_ID_DIG_EN_1 BIT(5)
+ #define USB_R5_ID_DIG_CURR BIT(6)
+ #define USB_R5_ID_DIG_IRQ BIT(7)
+ #define USB_R5_ID_DIG_TH_MASK GENMASK(15, 8)
+ #define USB_R5_ID_DIG_CNT_MASK GENMASK(23, 16)
+
+/* read-only register */
+#define USB_R6 0x18
+ #define USB_R6_P30_CR_DATA_OUT_MASK GENMASK(15, 0)
+ #define USB_R6_P30_CR_ACK BIT(16)
+
+struct phy_meson_gxl_usb3_priv {
+ struct regmap *regmap;
+ enum phy_mode mode;
+ struct clk *clk_phy;
+ struct clk *clk_peripheral;
+ struct reset_control *reset;
+};
+
+static const struct regmap_config phy_meson_gxl_usb3_regmap_conf = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = USB_R6,
+};
+
+static int phy_meson_gxl_usb3_power_on(struct phy *phy)
+{
+ struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
+
+ regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_0,
+ USB_R5_ID_DIG_EN_0);
+ regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_1,
+ USB_R5_ID_DIG_EN_1);
+ regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_TH_MASK,
+ FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff));
+
+ return 0;
+}
+
+static int phy_meson_gxl_usb3_power_off(struct phy *phy)
+{
+ struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
+
+ regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_0, 0);
+ regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_1, 0);
+
+ return 0;
+}
+
+static int phy_meson_gxl_usb3_set_mode(struct phy *phy, enum phy_mode mode)
+{
+ struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
+
+ switch (mode) {
+ case PHY_MODE_USB_HOST:
+ regmap_update_bits(priv->regmap, USB_R0, USB_R0_U2D_ACT, 0);
+ regmap_update_bits(priv->regmap, USB_R4, USB_R4_P21_SLEEP_M0,
+ 0);
+ break;
+
+ case PHY_MODE_USB_DEVICE:
+ regmap_update_bits(priv->regmap, USB_R0, USB_R0_U2D_ACT,
+ USB_R0_U2D_ACT);
+ regmap_update_bits(priv->regmap, USB_R4, USB_R4_P21_SLEEP_M0,
+ USB_R4_P21_SLEEP_M0);
+ break;
+
+ default:
+ dev_err(&phy->dev, "unsupported PHY mode %d\n", mode);
+ return -EINVAL;
+ }
+
+ priv->mode = mode;
+
+ return 0;
+}
+
+static int phy_meson_gxl_usb3_init(struct phy *phy)
+{
+ struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
+ int ret;
+
+ ret = reset_control_reset(priv->reset);
+ if (ret)
+ goto err;
+
+ ret = clk_prepare_enable(priv->clk_phy);
+ if (ret)
+ goto err;
+
+ ret = clk_prepare_enable(priv->clk_peripheral);
+ if (ret)
+ goto err_disable_clk_phy;
+
+ ret = phy_meson_gxl_usb3_set_mode(phy, priv->mode);
+ if (ret)
+ goto err_disable_clk_peripheral;
+
+ regmap_update_bits(priv->regmap, USB_R1,
+ USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
+ FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20));
+
+ return 0;
+
+err_disable_clk_peripheral:
+ clk_disable_unprepare(priv->clk_peripheral);
+err_disable_clk_phy:
+ clk_disable_unprepare(priv->clk_phy);
+err:
+ return ret;
+}
+
+static int phy_meson_gxl_usb3_exit(struct phy *phy)
+{
+ struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
+
+ clk_disable_unprepare(priv->clk_peripheral);
+ clk_disable_unprepare(priv->clk_phy);
+
+ return 0;
+}
+
+static const struct phy_ops phy_meson_gxl_usb3_ops = {
+ .power_on = phy_meson_gxl_usb3_power_on,
+ .power_off = phy_meson_gxl_usb3_power_off,
+ .set_mode = phy_meson_gxl_usb3_set_mode,
+ .init = phy_meson_gxl_usb3_init,
+ .exit = phy_meson_gxl_usb3_exit,
+ .owner = THIS_MODULE,
+};
+
+static int phy_meson_gxl_usb3_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct phy_meson_gxl_usb3_priv *priv;
+ struct resource *res;
+ struct phy *phy;
+ struct phy_provider *phy_provider;
+ void __iomem *base;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ priv->regmap = devm_regmap_init_mmio(dev, base,
+ &phy_meson_gxl_usb3_regmap_conf);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ priv->clk_phy = devm_clk_get(dev, "phy");
+ if (IS_ERR(priv->clk_phy))
+ return PTR_ERR(priv->clk_phy);
+
+ priv->clk_peripheral = devm_clk_get(dev, "peripheral");
+ if (IS_ERR(priv->clk_peripheral))
+ return PTR_ERR(priv->clk_peripheral);
+
+ priv->reset = devm_reset_control_array_get_shared(dev);
+ if (IS_ERR(priv->reset))
+ return PTR_ERR(priv->reset);
+
+ /*
+ * default to host mode as hardware defaults and/or boot-loader
+ * behavior can result in this PHY starting up in device mode. this
+ * default and the initialization in phy_meson_gxl_usb3_init ensure
+ * that we reproducibly start in a known mode on all devices.
+ */
+ priv->mode = PHY_MODE_USB_HOST;
+
+ phy = devm_phy_create(dev, np, &phy_meson_gxl_usb3_ops);
+ if (IS_ERR(phy)) {
+ ret = PTR_ERR(phy);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "failed to create PHY\n");
+
+ return ret;
+ }
+
+ phy_set_drvdata(phy, priv);
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+ return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id phy_meson_gxl_usb3_of_match[] = {
+ { .compatible = "amlogic,meson-gxl-usb3-phy", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, phy_meson_gxl_usb3_of_match);
+
+static struct platform_driver phy_meson_gxl_usb3_driver = {
+ .probe = phy_meson_gxl_usb3_probe,
+ .driver = {
+ .name = "phy-meson-gxl-usb3",
+ .of_match_table = phy_meson_gxl_usb3_of_match,
+ },
+};
+module_platform_driver(phy_meson_gxl_usb3_driver);
+
+MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
+MODULE_DESCRIPTION("Meson GXL USB3 PHY and OTG detection driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/hisilicon/Kconfig b/drivers/phy/hisilicon/Kconfig
index 6164c4cd0f65..b40ee54a1a50 100644
--- a/drivers/phy/hisilicon/Kconfig
+++ b/drivers/phy/hisilicon/Kconfig
@@ -4,6 +4,7 @@
config PHY_HI6220_USB
tristate "hi6220 USB PHY support"
depends on (ARCH_HISI && ARM64) || COMPILE_TEST
+ depends on HAS_IOMEM
select GENERIC_PHY
select MFD_SYSCON
help
@@ -11,6 +12,25 @@ config PHY_HI6220_USB
To compile this driver as a module, choose M here.
+config PHY_HISTB_COMBPHY
+ tristate "HiSilicon STB SoCs COMBPHY support"
+ depends on (ARCH_HISI && ARM64) || COMPILE_TEST
+ select GENERIC_PHY
+ select MFD_SYSCON
+ help
+ Enable this to support the HISILICON STB SoCs COMBPHY.
+ If unsure, say N.
+
+config PHY_HISI_INNO_USB2
+ tristate "HiSilicon INNO USB2 PHY support"
+ depends on (ARCH_HISI && ARM64) || COMPILE_TEST
+ select GENERIC_PHY
+ select MFD_SYSCON
+ help
+ Support for INNO USB2 PHY on HiSilicon SoCs. This Phy supports
+ USB 1.5Mb/s, USB 12Mb/s, USB 480Mb/s speeds. It supports one
+ USB host port to accept one USB device.
+
config PHY_HIX5HD2_SATA
tristate "HIX5HD2 SATA PHY Driver"
depends on ARCH_HIX5HD2 && OF && HAS_IOMEM
diff --git a/drivers/phy/hisilicon/Makefile b/drivers/phy/hisilicon/Makefile
index 541b348187a8..f662a4fe18d8 100644
--- a/drivers/phy/hisilicon/Makefile
+++ b/drivers/phy/hisilicon/Makefile
@@ -1,2 +1,4 @@
obj-$(CONFIG_PHY_HI6220_USB) += phy-hi6220-usb.o
+obj-$(CONFIG_PHY_HISTB_COMBPHY) += phy-histb-combphy.o
+obj-$(CONFIG_PHY_HISI_INNO_USB2) += phy-hisi-inno-usb2.o
obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o
diff --git a/drivers/phy/hisilicon/phy-hisi-inno-usb2.c b/drivers/phy/hisilicon/phy-hisi-inno-usb2.c
new file mode 100644
index 000000000000..524381249a2b
--- /dev/null
+++ b/drivers/phy/hisilicon/phy-hisi-inno-usb2.c
@@ -0,0 +1,197 @@
+/*
+ * HiSilicon INNO USB2 PHY Driver.
+ *
+ * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+#include <linux/reset.h>
+
+#define INNO_PHY_PORT_NUM 2
+#define REF_CLK_STABLE_TIME 100 /* unit:us */
+#define UTMI_CLK_STABLE_TIME 200 /* unit:us */
+#define TEST_CLK_STABLE_TIME 2 /* unit:ms */
+#define PHY_CLK_STABLE_TIME 2 /* unit:ms */
+#define UTMI_RST_COMPLETE_TIME 2 /* unit:ms */
+#define POR_RST_COMPLETE_TIME 300 /* unit:us */
+#define PHY_TEST_DATA GENMASK(7, 0)
+#define PHY_TEST_ADDR GENMASK(15, 8)
+#define PHY_TEST_PORT GENMASK(18, 16)
+#define PHY_TEST_WREN BIT(21)
+#define PHY_TEST_CLK BIT(22) /* rising edge active */
+#define PHY_TEST_RST BIT(23) /* low active */
+#define PHY_CLK_ENABLE BIT(2)
+
+struct hisi_inno_phy_port {
+ struct reset_control *utmi_rst;
+ struct hisi_inno_phy_priv *priv;
+};
+
+struct hisi_inno_phy_priv {
+ void __iomem *mmio;
+ struct clk *ref_clk;
+ struct reset_control *por_rst;
+ struct hisi_inno_phy_port ports[INNO_PHY_PORT_NUM];
+};
+
+static void hisi_inno_phy_write_reg(struct hisi_inno_phy_priv *priv,
+ u8 port, u32 addr, u32 data)
+{
+ void __iomem *reg = priv->mmio;
+ u32 val;
+
+ val = (data & PHY_TEST_DATA) |
+ ((addr << 8) & PHY_TEST_ADDR) |
+ ((port << 16) & PHY_TEST_PORT) |
+ PHY_TEST_WREN | PHY_TEST_RST;
+ writel(val, reg);
+
+ val |= PHY_TEST_CLK;
+ writel(val, reg);
+
+ val &= ~PHY_TEST_CLK;
+ writel(val, reg);
+}
+
+static void hisi_inno_phy_setup(struct hisi_inno_phy_priv *priv)
+{
+ /* The phy clk is controlled by the port0 register 0x06. */
+ hisi_inno_phy_write_reg(priv, 0, 0x06, PHY_CLK_ENABLE);
+ msleep(PHY_CLK_STABLE_TIME);
+}
+
+static int hisi_inno_phy_init(struct phy *phy)
+{
+ struct hisi_inno_phy_port *port = phy_get_drvdata(phy);
+ struct hisi_inno_phy_priv *priv = port->priv;
+ int ret;
+
+ ret = clk_prepare_enable(priv->ref_clk);
+ if (ret)
+ return ret;
+ udelay(REF_CLK_STABLE_TIME);
+
+ reset_control_deassert(priv->por_rst);
+ udelay(POR_RST_COMPLETE_TIME);
+
+ /* Set up phy registers */
+ hisi_inno_phy_setup(priv);
+
+ reset_control_deassert(port->utmi_rst);
+ udelay(UTMI_RST_COMPLETE_TIME);
+
+ return 0;
+}
+
+static int hisi_inno_phy_exit(struct phy *phy)
+{
+ struct hisi_inno_phy_port *port = phy_get_drvdata(phy);
+ struct hisi_inno_phy_priv *priv = port->priv;
+
+ reset_control_assert(port->utmi_rst);
+ reset_control_assert(priv->por_rst);
+ clk_disable_unprepare(priv->ref_clk);
+
+ return 0;
+}
+
+static const struct phy_ops hisi_inno_phy_ops = {
+ .init = hisi_inno_phy_init,
+ .exit = hisi_inno_phy_exit,
+ .owner = THIS_MODULE,
+};
+
+static int hisi_inno_phy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct hisi_inno_phy_priv *priv;
+ struct phy_provider *provider;
+ struct device_node *child;
+ struct resource *res;
+ int i = 0;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->mmio = devm_ioremap_resource(dev, res);
+ if (IS_ERR(priv->mmio)) {
+ ret = PTR_ERR(priv->mmio);
+ return ret;
+ }
+
+ priv->ref_clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(priv->ref_clk))
+ return PTR_ERR(priv->ref_clk);
+
+ priv->por_rst = devm_reset_control_get_exclusive(dev, NULL);
+ if (IS_ERR(priv->por_rst))
+ return PTR_ERR(priv->por_rst);
+
+ for_each_child_of_node(np, child) {
+ struct reset_control *rst;
+ struct phy *phy;
+
+ rst = of_reset_control_get_exclusive(child, NULL);
+ if (IS_ERR(rst))
+ return PTR_ERR(rst);
+ priv->ports[i].utmi_rst = rst;
+ priv->ports[i].priv = priv;
+
+ phy = devm_phy_create(dev, child, &hisi_inno_phy_ops);
+ if (IS_ERR(phy))
+ return PTR_ERR(phy);
+
+ phy_set_bus_width(phy, 8);
+ phy_set_drvdata(phy, &priv->ports[i]);
+ i++;
+
+ if (i > INNO_PHY_PORT_NUM) {
+ dev_warn(dev, "Support %d ports in maximum\n", i);
+ break;
+ }
+ }
+
+ provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ return PTR_ERR_OR_ZERO(provider);
+}
+
+static const struct of_device_id hisi_inno_phy_of_match[] = {
+ { .compatible = "hisilicon,inno-usb2-phy", },
+ { .compatible = "hisilicon,hi3798cv200-usb2-phy", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, hisi_inno_phy_of_match);
+
+static struct platform_driver hisi_inno_phy_driver = {
+ .probe = hisi_inno_phy_probe,
+ .driver = {
+ .name = "hisi-inno-phy",
+ .of_match_table = hisi_inno_phy_of_match,
+ }
+};
+module_platform_driver(hisi_inno_phy_driver);
+
+MODULE_DESCRIPTION("HiSilicon INNO USB2 PHY Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/hisilicon/phy-histb-combphy.c b/drivers/phy/hisilicon/phy-histb-combphy.c
new file mode 100644
index 000000000000..5777b3120017
--- /dev/null
+++ b/drivers/phy/hisilicon/phy-histb-combphy.c
@@ -0,0 +1,289 @@
+/*
+ * COMBPHY driver for HiSilicon STB SoCs
+ *
+ * Copyright (C) 2016-2017 HiSilicon Co., Ltd. http://www.hisilicon.com
+ *
+ * Authors: Jianguo Sun <sunjianguo1@huawei.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.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <dt-bindings/phy/phy.h>
+
+#define COMBPHY_MODE_PCIE 0
+#define COMBPHY_MODE_USB3 1
+#define COMBPHY_MODE_SATA 2
+
+#define COMBPHY_CFG_REG 0x0
+#define COMBPHY_BYPASS_CODEC BIT(31)
+#define COMBPHY_TEST_WRITE BIT(24)
+#define COMBPHY_TEST_DATA_SHIFT 20
+#define COMBPHY_TEST_DATA_MASK GENMASK(23, 20)
+#define COMBPHY_TEST_ADDR_SHIFT 12
+#define COMBPHY_TEST_ADDR_MASK GENMASK(16, 12)
+#define COMBPHY_CLKREF_OUT_OEN BIT(0)
+
+struct histb_combphy_mode {
+ int fixed;
+ int select;
+ u32 reg;
+ u32 shift;
+ u32 mask;
+};
+
+struct histb_combphy_priv {
+ void __iomem *mmio;
+ struct regmap *syscon;
+ struct reset_control *por_rst;
+ struct clk *ref_clk;
+ struct phy *phy;
+ struct histb_combphy_mode mode;
+};
+
+static void nano_register_write(struct histb_combphy_priv *priv,
+ u32 addr, u32 data)
+{
+ void __iomem *reg = priv->mmio + COMBPHY_CFG_REG;
+ u32 val;
+
+ /* Set up address and data for the write */
+ val = readl(reg);
+ val &= ~COMBPHY_TEST_ADDR_MASK;
+ val |= addr << COMBPHY_TEST_ADDR_SHIFT;
+ val &= ~COMBPHY_TEST_DATA_MASK;
+ val |= data << COMBPHY_TEST_DATA_SHIFT;
+ writel(val, reg);
+
+ /* Flip strobe control to trigger the write */
+ val &= ~COMBPHY_TEST_WRITE;
+ writel(val, reg);
+ val |= COMBPHY_TEST_WRITE;
+ writel(val, reg);
+}
+
+static int is_mode_fixed(struct histb_combphy_mode *mode)
+{
+ return (mode->fixed != PHY_NONE) ? true : false;
+}
+
+static int histb_combphy_set_mode(struct histb_combphy_priv *priv)
+{
+ struct histb_combphy_mode *mode = &priv->mode;
+ struct regmap *syscon = priv->syscon;
+ u32 hw_sel;
+
+ if (is_mode_fixed(mode))
+ return 0;
+
+ switch (mode->select) {
+ case PHY_TYPE_SATA:
+ hw_sel = COMBPHY_MODE_SATA;
+ break;
+ case PHY_TYPE_PCIE:
+ hw_sel = COMBPHY_MODE_PCIE;
+ break;
+ case PHY_TYPE_USB3:
+ hw_sel = COMBPHY_MODE_USB3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(syscon, mode->reg, mode->mask,
+ hw_sel << mode->shift);
+}
+
+static int histb_combphy_init(struct phy *phy)
+{
+ struct histb_combphy_priv *priv = phy_get_drvdata(phy);
+ u32 val;
+ int ret;
+
+ ret = histb_combphy_set_mode(priv);
+ if (ret)
+ return ret;
+
+ /* Clear bypass bit to enable encoding/decoding */
+ val = readl(priv->mmio + COMBPHY_CFG_REG);
+ val &= ~COMBPHY_BYPASS_CODEC;
+ writel(val, priv->mmio + COMBPHY_CFG_REG);
+
+ ret = clk_prepare_enable(priv->ref_clk);
+ if (ret)
+ return ret;
+
+ reset_control_deassert(priv->por_rst);
+
+ /* Enable EP clock */
+ val = readl(priv->mmio + COMBPHY_CFG_REG);
+ val |= COMBPHY_CLKREF_OUT_OEN;
+ writel(val, priv->mmio + COMBPHY_CFG_REG);
+
+ /* Need to wait for EP clock stable */
+ mdelay(5);
+
+ /* Configure nano phy registers as suggested by vendor */
+ nano_register_write(priv, 0x1, 0x8);
+ nano_register_write(priv, 0xc, 0x9);
+ nano_register_write(priv, 0x1a, 0x4);
+
+ return 0;
+}
+
+static int histb_combphy_exit(struct phy *phy)
+{
+ struct histb_combphy_priv *priv = phy_get_drvdata(phy);
+ u32 val;
+
+ /* Disable EP clock */
+ val = readl(priv->mmio + COMBPHY_CFG_REG);
+ val &= ~COMBPHY_CLKREF_OUT_OEN;
+ writel(val, priv->mmio + COMBPHY_CFG_REG);
+
+ reset_control_assert(priv->por_rst);
+ clk_disable_unprepare(priv->ref_clk);
+
+ return 0;
+}
+
+static const struct phy_ops histb_combphy_ops = {
+ .init = histb_combphy_init,
+ .exit = histb_combphy_exit,
+ .owner = THIS_MODULE,
+};
+
+static struct phy *histb_combphy_xlate(struct device *dev,
+ struct of_phandle_args *args)
+{
+ struct histb_combphy_priv *priv = dev_get_drvdata(dev);
+ struct histb_combphy_mode *mode = &priv->mode;
+
+ if (args->args_count < 1) {
+ dev_err(dev, "invalid number of arguments\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ mode->select = args->args[0];
+
+ if (mode->select < PHY_TYPE_SATA || mode->select > PHY_TYPE_USB3) {
+ dev_err(dev, "invalid phy mode select argument\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (is_mode_fixed(mode) && mode->select != mode->fixed) {
+ dev_err(dev, "mode select %d mismatch fixed phy mode %d\n",
+ mode->select, mode->fixed);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return priv->phy;
+}
+
+static int histb_combphy_probe(struct platform_device *pdev)
+{
+ struct phy_provider *phy_provider;
+ struct device *dev = &pdev->dev;
+ struct histb_combphy_priv *priv;
+ struct device_node *np = dev->of_node;
+ struct histb_combphy_mode *mode;
+ struct resource *res;
+ u32 vals[3];
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->mmio = devm_ioremap_resource(dev, res);
+ if (IS_ERR(priv->mmio)) {
+ ret = PTR_ERR(priv->mmio);
+ return ret;
+ }
+
+ priv->syscon = syscon_node_to_regmap(np->parent);
+ if (IS_ERR(priv->syscon)) {
+ dev_err(dev, "failed to find peri_ctrl syscon regmap\n");
+ return PTR_ERR(priv->syscon);
+ }
+
+ mode = &priv->mode;
+ mode->fixed = PHY_NONE;
+
+ ret = of_property_read_u32(np, "hisilicon,fixed-mode", &mode->fixed);
+ if (ret == 0)
+ dev_dbg(dev, "found fixed phy mode %d\n", mode->fixed);
+
+ ret = of_property_read_u32_array(np, "hisilicon,mode-select-bits",
+ vals, ARRAY_SIZE(vals));
+ if (ret == 0) {
+ if (is_mode_fixed(mode)) {
+ dev_err(dev, "found select bits for fixed mode phy\n");
+ return -EINVAL;
+ }
+
+ mode->reg = vals[0];
+ mode->shift = vals[1];
+ mode->mask = vals[2];
+ dev_dbg(dev, "found mode select bits\n");
+ } else {
+ if (!is_mode_fixed(mode)) {
+ dev_err(dev, "no valid select bits found for non-fixed phy\n");
+ return -ENODEV;
+ }
+ }
+
+ priv->ref_clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(priv->ref_clk)) {
+ dev_err(dev, "failed to find ref clock\n");
+ return PTR_ERR(priv->ref_clk);
+ }
+
+ priv->por_rst = devm_reset_control_get(dev, NULL);
+ if (IS_ERR(priv->por_rst)) {
+ dev_err(dev, "failed to get poweron reset\n");
+ return PTR_ERR(priv->por_rst);
+ }
+
+ priv->phy = devm_phy_create(dev, NULL, &histb_combphy_ops);
+ if (IS_ERR(priv->phy)) {
+ dev_err(dev, "failed to create combphy\n");
+ return PTR_ERR(priv->phy);
+ }
+
+ dev_set_drvdata(dev, priv);
+ phy_set_drvdata(priv->phy, priv);
+
+ phy_provider = devm_of_phy_provider_register(dev, histb_combphy_xlate);
+ return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id histb_combphy_of_match[] = {
+ { .compatible = "hisilicon,hi3798cv200-combphy" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, histb_combphy_of_match);
+
+static struct platform_driver histb_combphy_driver = {
+ .probe = histb_combphy_probe,
+ .driver = {
+ .name = "combphy",
+ .of_match_table = histb_combphy_of_match,
+ },
+};
+module_platform_driver(histb_combphy_driver);
+
+MODULE_DESCRIPTION("HiSilicon STB COMBPHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/marvell/phy-berlin-usb.c b/drivers/phy/marvell/phy-berlin-usb.c
index 2017751ede26..8f2b5cae360f 100644
--- a/drivers/phy/marvell/phy-berlin-usb.c
+++ b/drivers/phy/marvell/phy-berlin-usb.c
@@ -127,7 +127,7 @@ static int phy_berlin_usb_power_on(struct phy *phy)
writel(V2I_VCO_RATIO(0x5) | R_ROTATE_0 | ANA_TEST_DC_CTRL(0x5),
priv->base + USB_PHY_ANALOG);
writel(PHASE_FREEZE_DLY_4_CL | ACK_LENGTH_16_CL | SQ_LENGTH_12 |
- DISCON_THRESHOLD_260 | SQ_THRESHOLD(0xa) | LPF_COEF(0x2) |
+ DISCON_THRESHOLD_270 | SQ_THRESHOLD(0xa) | LPF_COEF(0x2) |
INTPL_CUR_30, priv->base + USB_PHY_RX_CTRL);
writel(TX_VDD12_13 | TX_OUT_AMP(0x3), priv->base + USB_PHY_TX_CTRL1);
diff --git a/drivers/phy/mediatek/phy-mtk-tphy.c b/drivers/phy/mediatek/phy-mtk-tphy.c
index 1e96d0740ef5..38c281b5abbb 100644
--- a/drivers/phy/mediatek/phy-mtk-tphy.c
+++ b/drivers/phy/mediatek/phy-mtk-tphy.c
@@ -306,6 +306,8 @@ struct mtk_tphy {
const struct mtk_phy_pdata *pdata;
struct mtk_phy_instance **phys;
int nphys;
+ int src_ref_clk; /* MHZ, reference clock for slew rate calibrate */
+ int src_coef; /* coefficient for slew rate calibrate */
};
static void hs_slew_rate_calibrate(struct mtk_tphy *tphy,
@@ -360,16 +362,17 @@ static void hs_slew_rate_calibrate(struct mtk_tphy *tphy,
writel(tmp, fmreg + U3P_U2FREQ_FMMONR1);
if (fm_out) {
- /* ( 1024 / FM_OUT ) x reference clock frequency x 0.028 */
- tmp = U3P_FM_DET_CYCLE_CNT * U3P_REF_CLK * U3P_SLEW_RATE_COEF;
- tmp /= fm_out;
+ /* ( 1024 / FM_OUT ) x reference clock frequency x coef */
+ tmp = tphy->src_ref_clk * tphy->src_coef;
+ tmp = (tmp * U3P_FM_DET_CYCLE_CNT) / fm_out;
calibration_val = DIV_ROUND_CLOSEST(tmp, U3P_SR_COEF_DIVISOR);
} else {
/* if FM detection fail, set default value */
calibration_val = 4;
}
- dev_dbg(tphy->dev, "phy:%d, fm_out:%d, calib:%d\n",
- instance->index, fm_out, calibration_val);
+ dev_dbg(tphy->dev, "phy:%d, fm_out:%d, calib:%d (clk:%d, coef:%d)\n",
+ instance->index, fm_out, calibration_val,
+ tphy->src_ref_clk, tphy->src_coef);
/* set HS slew rate */
tmp = readl(com + U3P_USBPHYACR5);
@@ -688,8 +691,7 @@ static void pcie_phy_instance_power_on(struct mtk_tphy *tphy,
u32 tmp;
tmp = readl(bank->chip + U3P_U3_CHIP_GPIO_CTLD);
- tmp &= ~(P3C_FORCE_IP_SW_RST | P3C_MCU_BUS_CK_GATE_EN |
- P3C_REG_IP_SW_RST);
+ tmp &= ~(P3C_FORCE_IP_SW_RST | P3C_REG_IP_SW_RST);
writel(tmp, bank->chip + U3P_U3_CHIP_GPIO_CTLD);
tmp = readl(bank->chip + U3P_U3_CHIP_GPIO_CTLE);
@@ -1042,6 +1044,13 @@ static int mtk_tphy_probe(struct platform_device *pdev)
tphy->u3phya_ref = NULL;
}
+ tphy->src_ref_clk = U3P_REF_CLK;
+ tphy->src_coef = U3P_SLEW_RATE_COEF;
+ /* update parameters of slew rate calibrate if exist */
+ device_property_read_u32(dev, "mediatek,src-ref-clk-mhz",
+ &tphy->src_ref_clk);
+ device_property_read_u32(dev, "mediatek,src-coef", &tphy->src_coef);
+
port = 0;
for_each_child_of_node(np, child_np) {
struct mtk_phy_instance *instance;
diff --git a/drivers/phy/motorola/Kconfig b/drivers/phy/motorola/Kconfig
index 6bb7d6bdf1bf..82651524ffb9 100644
--- a/drivers/phy/motorola/Kconfig
+++ b/drivers/phy/motorola/Kconfig
@@ -10,3 +10,11 @@ config PHY_CPCAP_USB
help
Enable this for USB to work on Motorola phones and tablets
such as Droid 4.
+
+config PHY_MAPPHONE_MDM6600
+ tristate "Motorola Mapphone MDM6600 modem USB PHY driver"
+ depends on OF && USB_SUPPORT
+ select GENERIC_PHY
+ help
+ Enable this for MDM6600 USB modem to work on Motorola phones
+ and tablets such as Droid 4.
diff --git a/drivers/phy/motorola/Makefile b/drivers/phy/motorola/Makefile
index b6cd618d671a..3514f985b355 100644
--- a/drivers/phy/motorola/Makefile
+++ b/drivers/phy/motorola/Makefile
@@ -3,3 +3,4 @@
#
obj-$(CONFIG_PHY_CPCAP_USB) += phy-cpcap-usb.o
+obj-$(CONFIG_PHY_MAPPHONE_MDM6600) += phy-mapphone-mdm6600.o
diff --git a/drivers/phy/motorola/phy-mapphone-mdm6600.c b/drivers/phy/motorola/phy-mapphone-mdm6600.c
new file mode 100644
index 000000000000..5439dd90d0dd
--- /dev/null
+++ b/drivers/phy/motorola/phy-mapphone-mdm6600.c
@@ -0,0 +1,542 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Motorola Mapphone MDM6600 modem GPIO controlled USB PHY driver
+ * Copyright (C) 2018 Tony Lindgren <tony@atomide.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <linux/gpio/consumer.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+
+#define PHY_MDM6600_PHY_DELAY_MS 4000 /* PHY enable 2.2s to 3.5s */
+#define PHY_MDM6600_ENABLED_DELAY_MS 8000 /* 8s more total for MDM6600 */
+
+enum phy_mdm6600_ctrl_lines {
+ PHY_MDM6600_ENABLE, /* USB PHY enable */
+ PHY_MDM6600_POWER, /* Device power */
+ PHY_MDM6600_RESET, /* Device reset */
+ PHY_MDM6600_NR_CTRL_LINES,
+};
+
+enum phy_mdm6600_bootmode_lines {
+ PHY_MDM6600_MODE0, /* out USB mode0 and OOB wake */
+ PHY_MDM6600_MODE1, /* out USB mode1, in OOB wake */
+ PHY_MDM6600_NR_MODE_LINES,
+};
+
+enum phy_mdm6600_cmd_lines {
+ PHY_MDM6600_CMD0,
+ PHY_MDM6600_CMD1,
+ PHY_MDM6600_CMD2,
+ PHY_MDM6600_NR_CMD_LINES,
+};
+
+enum phy_mdm6600_status_lines {
+ PHY_MDM6600_STATUS0,
+ PHY_MDM6600_STATUS1,
+ PHY_MDM6600_STATUS2,
+ PHY_MDM6600_NR_STATUS_LINES,
+};
+
+/*
+ * MDM6600 command codes. These are based on Motorola Mapphone Linux
+ * kernel tree.
+ */
+enum phy_mdm6600_cmd {
+ PHY_MDM6600_CMD_BP_PANIC_ACK,
+ PHY_MDM6600_CMD_DATA_ONLY_BYPASS, /* Reroute USB to CPCAP PHY */
+ PHY_MDM6600_CMD_FULL_BYPASS, /* Reroute USB to CPCAP PHY */
+ PHY_MDM6600_CMD_NO_BYPASS, /* Request normal USB mode */
+ PHY_MDM6600_CMD_BP_SHUTDOWN_REQ, /* Request device power off */
+ PHY_MDM6600_CMD_BP_UNKNOWN_5,
+ PHY_MDM6600_CMD_BP_UNKNOWN_6,
+ PHY_MDM6600_CMD_UNDEFINED,
+};
+
+/*
+ * MDM6600 status codes. These are based on Motorola Mapphone Linux
+ * kernel tree.
+ */
+enum phy_mdm6600_status {
+ PHY_MDM6600_STATUS_PANIC, /* Seems to be really off */
+ PHY_MDM6600_STATUS_PANIC_BUSY_WAIT,
+ PHY_MDM6600_STATUS_QC_DLOAD,
+ PHY_MDM6600_STATUS_RAM_DOWNLOADER, /* MDM6600 USB flashing mode */
+ PHY_MDM6600_STATUS_PHONE_CODE_AWAKE, /* MDM6600 normal USB mode */
+ PHY_MDM6600_STATUS_PHONE_CODE_ASLEEP,
+ PHY_MDM6600_STATUS_SHUTDOWN_ACK,
+ PHY_MDM6600_STATUS_UNDEFINED,
+};
+
+static const char * const
+phy_mdm6600_status_name[] = {
+ "off", "busy", "qc_dl", "ram_dl", "awake",
+ "asleep", "shutdown", "undefined",
+};
+
+struct phy_mdm6600 {
+ struct device *dev;
+ struct phy *generic_phy;
+ struct phy_provider *phy_provider;
+ struct gpio_desc *ctrl_gpios[PHY_MDM6600_NR_CTRL_LINES];
+ struct gpio_descs *mode_gpios;
+ struct gpio_descs *status_gpios;
+ struct gpio_descs *cmd_gpios;
+ struct delayed_work bootup_work;
+ struct delayed_work status_work;
+ struct completion ack;
+ bool enabled; /* mdm6600 phy enabled */
+ bool running; /* mdm6600 boot done */
+ int status;
+};
+
+static int phy_mdm6600_init(struct phy *x)
+{
+ struct phy_mdm6600 *ddata = phy_get_drvdata(x);
+ struct gpio_desc *enable_gpio = ddata->ctrl_gpios[PHY_MDM6600_ENABLE];
+
+ if (!ddata->enabled)
+ return -EPROBE_DEFER;
+
+ gpiod_set_value_cansleep(enable_gpio, 0);
+
+ return 0;
+}
+
+static int phy_mdm6600_power_on(struct phy *x)
+{
+ struct phy_mdm6600 *ddata = phy_get_drvdata(x);
+ struct gpio_desc *enable_gpio = ddata->ctrl_gpios[PHY_MDM6600_ENABLE];
+
+ if (!ddata->enabled)
+ return -ENODEV;
+
+ gpiod_set_value_cansleep(enable_gpio, 1);
+
+ return 0;
+}
+
+static int phy_mdm6600_power_off(struct phy *x)
+{
+ struct phy_mdm6600 *ddata = phy_get_drvdata(x);
+ struct gpio_desc *enable_gpio = ddata->ctrl_gpios[PHY_MDM6600_ENABLE];
+
+ if (!ddata->enabled)
+ return -ENODEV;
+
+ gpiod_set_value_cansleep(enable_gpio, 0);
+
+ return 0;
+}
+
+static const struct phy_ops gpio_usb_ops = {
+ .init = phy_mdm6600_init,
+ .power_on = phy_mdm6600_power_on,
+ .power_off = phy_mdm6600_power_off,
+ .owner = THIS_MODULE,
+};
+
+/**
+ * phy_mdm6600_cmd() - send a command request to mdm6600
+ * @ddata: device driver data
+ *
+ * Configures the three command request GPIOs to the specified value.
+ */
+static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val)
+{
+ int values[PHY_MDM6600_NR_CMD_LINES];
+ int i;
+
+ val &= (1 << PHY_MDM6600_NR_CMD_LINES) - 1;
+ for (i = 0; i < PHY_MDM6600_NR_CMD_LINES; i++)
+ values[i] = (val & BIT(i)) >> i;
+
+ gpiod_set_array_value_cansleep(PHY_MDM6600_NR_CMD_LINES,
+ ddata->cmd_gpios->desc, values);
+}
+
+/**
+ * phy_mdm6600_status() - read mdm6600 status lines
+ * @ddata: device driver data
+ */
+static void phy_mdm6600_status(struct work_struct *work)
+{
+ struct phy_mdm6600 *ddata;
+ struct device *dev;
+ int values[PHY_MDM6600_NR_STATUS_LINES];
+ int error, i, val = 0;
+
+ ddata = container_of(work, struct phy_mdm6600, status_work.work);
+ dev = ddata->dev;
+
+ error = gpiod_get_array_value_cansleep(PHY_MDM6600_NR_CMD_LINES,
+ ddata->status_gpios->desc,
+ values);
+ if (error)
+ return;
+
+ for (i = 0; i < PHY_MDM6600_NR_CMD_LINES; i++) {
+ val |= values[i] << i;
+ dev_dbg(ddata->dev, "XXX %s: i: %i values[i]: %i val: %i\n",
+ __func__, i, values[i], val);
+ }
+ ddata->status = val;
+
+ dev_info(dev, "modem status: %i %s\n",
+ ddata->status,
+ phy_mdm6600_status_name[ddata->status & 7]);
+ complete(&ddata->ack);
+}
+
+static irqreturn_t phy_mdm6600_irq_thread(int irq, void *data)
+{
+ struct phy_mdm6600 *ddata = data;
+
+ schedule_delayed_work(&ddata->status_work, msecs_to_jiffies(10));
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * phy_mdm6600_wakeirq_thread - handle mode1 line OOB wake after booting
+ * @irq: interrupt
+ * @data: interrupt handler data
+ *
+ * GPIO mode1 is used initially as output to configure the USB boot
+ * mode for mdm6600. After booting it is used as input for OOB wake
+ * signal from mdm6600 to the SoC. Just use it for debug info only
+ * for now.
+ */
+static irqreturn_t phy_mdm6600_wakeirq_thread(int irq, void *data)
+{
+ struct phy_mdm6600 *ddata = data;
+ struct gpio_desc *mode_gpio1;
+
+ mode_gpio1 = ddata->mode_gpios->desc[PHY_MDM6600_MODE1];
+ dev_dbg(ddata->dev, "OOB wake on mode_gpio1: %i\n",
+ gpiod_get_value(mode_gpio1));
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * phy_mdm6600_init_irq() - initialize mdm6600 status IRQ lines
+ * @ddata: device driver data
+ */
+static void phy_mdm6600_init_irq(struct phy_mdm6600 *ddata)
+{
+ struct device *dev = ddata->dev;
+ int i, error, irq;
+
+ for (i = PHY_MDM6600_STATUS0;
+ i <= PHY_MDM6600_STATUS2; i++) {
+ struct gpio_desc *gpio = ddata->status_gpios->desc[i];
+
+ irq = gpiod_to_irq(gpio);
+ if (irq <= 0)
+ continue;
+
+ error = devm_request_threaded_irq(dev, irq, NULL,
+ phy_mdm6600_irq_thread,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ "mdm6600",
+ ddata);
+ if (error)
+ dev_warn(dev, "no modem status irq%i: %i\n",
+ irq, error);
+ }
+}
+
+struct phy_mdm6600_map {
+ const char *name;
+ int direction;
+};
+
+static const struct phy_mdm6600_map
+phy_mdm6600_ctrl_gpio_map[PHY_MDM6600_NR_CTRL_LINES] = {
+ { "enable", GPIOD_OUT_LOW, }, /* low = phy disabled */
+ { "power", GPIOD_OUT_LOW, }, /* low = off */
+ { "reset", GPIOD_OUT_HIGH, }, /* high = reset */
+};
+
+/**
+ * phy_mdm6600_init_lines() - initialize mdm6600 GPIO lines
+ * @ddata: device driver data
+ */
+static int phy_mdm6600_init_lines(struct phy_mdm6600 *ddata)
+{
+ struct device *dev = ddata->dev;
+ int i;
+
+ /* MDM6600 control lines */
+ for (i = 0; i < ARRAY_SIZE(phy_mdm6600_ctrl_gpio_map); i++) {
+ const struct phy_mdm6600_map *map =
+ &phy_mdm6600_ctrl_gpio_map[i];
+ struct gpio_desc **gpio = &ddata->ctrl_gpios[i];
+
+ *gpio = devm_gpiod_get(dev, map->name, map->direction);
+ if (IS_ERR(*gpio)) {
+ dev_info(dev, "gpio %s error %li\n",
+ map->name, PTR_ERR(*gpio));
+ return PTR_ERR(*gpio);
+ }
+ }
+
+ /* MDM6600 USB start-up mode output lines */
+ ddata->mode_gpios = devm_gpiod_get_array(dev, "motorola,mode",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(ddata->mode_gpios))
+ return PTR_ERR(ddata->mode_gpios);
+
+ if (ddata->mode_gpios->ndescs != PHY_MDM6600_NR_MODE_LINES)
+ return -EINVAL;
+
+ /* MDM6600 status input lines */
+ ddata->status_gpios = devm_gpiod_get_array(dev, "motorola,status",
+ GPIOD_IN);
+ if (IS_ERR(ddata->status_gpios))
+ return PTR_ERR(ddata->status_gpios);
+
+ if (ddata->status_gpios->ndescs != PHY_MDM6600_NR_STATUS_LINES)
+ return -EINVAL;
+
+ /* MDM6600 cmd output lines */
+ ddata->cmd_gpios = devm_gpiod_get_array(dev, "motorola,cmd",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(ddata->cmd_gpios))
+ return PTR_ERR(ddata->cmd_gpios);
+
+ if (ddata->cmd_gpios->ndescs != PHY_MDM6600_NR_CMD_LINES)
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * phy_mdm6600_device_power_on() - power on mdm6600 device
+ * @ddata: device driver data
+ *
+ * To get the integrated USB phy in MDM6600 takes some hoops. We must ensure
+ * the shared USB bootmode GPIOs are configured, then request modem start-up,
+ * reset and power-up.. And then we need to recycle the shared USB bootmode
+ * GPIOs as they are also used for Out of Band (OOB) wake for the USB and
+ * TS 27.010 serial mux.
+ */
+static int phy_mdm6600_device_power_on(struct phy_mdm6600 *ddata)
+{
+ struct gpio_desc *mode_gpio0, *mode_gpio1, *reset_gpio, *power_gpio;
+ int error = 0, wakeirq;
+
+ mode_gpio0 = ddata->mode_gpios->desc[PHY_MDM6600_MODE0];
+ mode_gpio1 = ddata->mode_gpios->desc[PHY_MDM6600_MODE1];
+ reset_gpio = ddata->ctrl_gpios[PHY_MDM6600_RESET];
+ power_gpio = ddata->ctrl_gpios[PHY_MDM6600_POWER];
+
+ /*
+ * Shared GPIOs must be low for normal USB mode. After booting
+ * they are used for OOB wake signaling. These can be also used
+ * to configure USB flashing mode later on based on a module
+ * parameter.
+ */
+ gpiod_set_value_cansleep(mode_gpio0, 0);
+ gpiod_set_value_cansleep(mode_gpio1, 0);
+
+ /* Request start-up mode */
+ phy_mdm6600_cmd(ddata, PHY_MDM6600_CMD_NO_BYPASS);
+
+ /* Request a reset first */
+ gpiod_set_value_cansleep(reset_gpio, 0);
+ msleep(100);
+
+ /* Toggle power GPIO to request mdm6600 to start */
+ gpiod_set_value_cansleep(power_gpio, 1);
+ msleep(100);
+ gpiod_set_value_cansleep(power_gpio, 0);
+
+ /*
+ * Looks like the USB PHY needs between 2.2 to 4 seconds.
+ * If we try to use it before that, we will get L3 errors
+ * from omap-usb-host trying to access the PHY. See also
+ * phy_mdm6600_init() for -EPROBE_DEFER.
+ */
+ msleep(PHY_MDM6600_PHY_DELAY_MS);
+ ddata->enabled = true;
+
+ /* Booting up the rest of MDM6600 will take total about 8 seconds */
+ dev_info(ddata->dev, "Waiting for power up request to complete..\n");
+ if (wait_for_completion_timeout(&ddata->ack,
+ msecs_to_jiffies(PHY_MDM6600_ENABLED_DELAY_MS))) {
+ if (ddata->status > PHY_MDM6600_STATUS_PANIC &&
+ ddata->status < PHY_MDM6600_STATUS_SHUTDOWN_ACK)
+ dev_info(ddata->dev, "Powered up OK\n");
+ } else {
+ ddata->enabled = false;
+ error = -ETIMEDOUT;
+ dev_err(ddata->dev, "Timed out powering up\n");
+ }
+
+ /* Reconfigure mode1 GPIO as input for OOB wake */
+ gpiod_direction_input(mode_gpio1);
+
+ wakeirq = gpiod_to_irq(mode_gpio1);
+ if (wakeirq <= 0)
+ return wakeirq;
+
+ error = devm_request_threaded_irq(ddata->dev, wakeirq, NULL,
+ phy_mdm6600_wakeirq_thread,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ "mdm6600-wake",
+ ddata);
+ if (error)
+ dev_warn(ddata->dev, "no modem wakeirq irq%i: %i\n",
+ wakeirq, error);
+
+ ddata->running = true;
+
+ return error;
+}
+
+/**
+ * phy_mdm6600_device_power_off() - power off mdm6600 device
+ * @ddata: device driver data
+ */
+static void phy_mdm6600_device_power_off(struct phy_mdm6600 *ddata)
+{
+ struct gpio_desc *reset_gpio =
+ ddata->ctrl_gpios[PHY_MDM6600_RESET];
+
+ ddata->enabled = false;
+ phy_mdm6600_cmd(ddata, PHY_MDM6600_CMD_BP_SHUTDOWN_REQ);
+ msleep(100);
+
+ gpiod_set_value_cansleep(reset_gpio, 1);
+
+ dev_info(ddata->dev, "Waiting for power down request to complete.. ");
+ if (wait_for_completion_timeout(&ddata->ack,
+ msecs_to_jiffies(5000))) {
+ if (ddata->status == PHY_MDM6600_STATUS_PANIC)
+ dev_info(ddata->dev, "Powered down OK\n");
+ } else {
+ dev_err(ddata->dev, "Timed out powering down\n");
+ }
+}
+
+static void phy_mdm6600_deferred_power_on(struct work_struct *work)
+{
+ struct phy_mdm6600 *ddata;
+ int error;
+
+ ddata = container_of(work, struct phy_mdm6600, bootup_work.work);
+
+ error = phy_mdm6600_device_power_on(ddata);
+ if (error)
+ dev_err(ddata->dev, "Device not functional\n");
+}
+
+static const struct of_device_id phy_mdm6600_id_table[] = {
+ { .compatible = "motorola,mapphone-mdm6600", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, phy_mdm6600_id_table);
+
+static int phy_mdm6600_probe(struct platform_device *pdev)
+{
+ struct phy_mdm6600 *ddata;
+ int error;
+
+ ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
+ if (!ddata)
+ return -ENOMEM;
+
+ INIT_DELAYED_WORK(&ddata->bootup_work,
+ phy_mdm6600_deferred_power_on);
+ INIT_DELAYED_WORK(&ddata->status_work, phy_mdm6600_status);
+ init_completion(&ddata->ack);
+
+ ddata->dev = &pdev->dev;
+ platform_set_drvdata(pdev, ddata);
+
+ error = phy_mdm6600_init_lines(ddata);
+ if (error)
+ return error;
+
+ phy_mdm6600_init_irq(ddata);
+
+ ddata->generic_phy = devm_phy_create(ddata->dev, NULL, &gpio_usb_ops);
+ if (IS_ERR(ddata->generic_phy)) {
+ error = PTR_ERR(ddata->generic_phy);
+ goto cleanup;
+ }
+
+ phy_set_drvdata(ddata->generic_phy, ddata);
+
+ ddata->phy_provider =
+ devm_of_phy_provider_register(ddata->dev,
+ of_phy_simple_xlate);
+ if (IS_ERR(ddata->phy_provider)) {
+ error = PTR_ERR(ddata->phy_provider);
+ goto cleanup;
+ }
+
+ schedule_delayed_work(&ddata->bootup_work, 0);
+
+ /*
+ * See phy_mdm6600_device_power_on(). We should be able
+ * to remove this eventually when ohci-platform can deal
+ * with -EPROBE_DEFER.
+ */
+ msleep(PHY_MDM6600_PHY_DELAY_MS + 500);
+
+ return 0;
+
+cleanup:
+ phy_mdm6600_device_power_off(ddata);
+ return error;
+}
+
+static int phy_mdm6600_remove(struct platform_device *pdev)
+{
+ struct phy_mdm6600 *ddata = platform_get_drvdata(pdev);
+ struct gpio_desc *reset_gpio = ddata->ctrl_gpios[PHY_MDM6600_RESET];
+
+ if (!ddata->running)
+ wait_for_completion_timeout(&ddata->ack,
+ msecs_to_jiffies(PHY_MDM6600_ENABLED_DELAY_MS));
+
+ gpiod_set_value_cansleep(reset_gpio, 1);
+ phy_mdm6600_device_power_off(ddata);
+
+ cancel_delayed_work_sync(&ddata->bootup_work);
+ cancel_delayed_work_sync(&ddata->status_work);
+
+ return 0;
+}
+
+static struct platform_driver phy_mdm6600_driver = {
+ .probe = phy_mdm6600_probe,
+ .remove = phy_mdm6600_remove,
+ .driver = {
+ .name = "phy-mapphone-mdm6600",
+ .of_match_table = of_match_ptr(phy_mdm6600_id_table),
+ },
+};
+
+module_platform_driver(phy_mdm6600_driver);
+
+MODULE_ALIAS("platform:gpio_usb");
+MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
+MODULE_DESCRIPTION("mdm6600 gpio usb phy driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index 8f6e8e28996d..09ac8afb97ac 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -351,6 +351,8 @@ int phy_set_mode(struct phy *phy, enum phy_mode mode)
mutex_lock(&phy->mutex);
ret = phy->ops->set_mode(phy, mode);
+ if (!ret)
+ phy->attrs.mode = mode;
mutex_unlock(&phy->mutex);
return ret;
diff --git a/drivers/phy/phy-lpc18xx-usb-otg.c b/drivers/phy/phy-lpc18xx-usb-otg.c
index 3b7a71eb5b7e..7de280a45421 100644
--- a/drivers/phy/phy-lpc18xx-usb-otg.c
+++ b/drivers/phy/phy-lpc18xx-usb-otg.c
@@ -60,8 +60,14 @@ static int lpc18xx_usb_otg_phy_power_on(struct phy *phy)
return ret;
/* The bit in CREG is cleared to enable the PHY */
- return regmap_update_bits(lpc->reg, LPC18XX_CREG_CREG0,
+ ret = regmap_update_bits(lpc->reg, LPC18XX_CREG_CREG0,
LPC18XX_CREG_CREG0_USB0PHY, 0);
+ if (ret) {
+ clk_disable(lpc->clk);
+ return ret;
+ }
+
+ return 0;
}
static int lpc18xx_usb_otg_phy_power_off(struct phy *phy)
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c
index e17f0351ccc2..6470c5d61d1c 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp.c
@@ -1,15 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2017, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * 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.
- *
*/
#include <linux/clk.h>
@@ -31,124 +22,7 @@
#include <dt-bindings/phy/phy.h>
-/* QMP PHY QSERDES COM registers */
-#define QSERDES_COM_BG_TIMER 0x00c
-#define QSERDES_COM_SSC_EN_CENTER 0x010
-#define QSERDES_COM_SSC_ADJ_PER1 0x014
-#define QSERDES_COM_SSC_ADJ_PER2 0x018
-#define QSERDES_COM_SSC_PER1 0x01c
-#define QSERDES_COM_SSC_PER2 0x020
-#define QSERDES_COM_SSC_STEP_SIZE1 0x024
-#define QSERDES_COM_SSC_STEP_SIZE2 0x028
-#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN 0x034
-#define QSERDES_COM_CLK_ENABLE1 0x038
-#define QSERDES_COM_SYS_CLK_CTRL 0x03c
-#define QSERDES_COM_SYSCLK_BUF_ENABLE 0x040
-#define QSERDES_COM_PLL_IVCO 0x048
-#define QSERDES_COM_LOCK_CMP1_MODE0 0x04c
-#define QSERDES_COM_LOCK_CMP2_MODE0 0x050
-#define QSERDES_COM_LOCK_CMP3_MODE0 0x054
-#define QSERDES_COM_LOCK_CMP1_MODE1 0x058
-#define QSERDES_COM_LOCK_CMP2_MODE1 0x05c
-#define QSERDES_COM_LOCK_CMP3_MODE1 0x060
-#define QSERDES_COM_BG_TRIM 0x070
-#define QSERDES_COM_CLK_EP_DIV 0x074
-#define QSERDES_COM_CP_CTRL_MODE0 0x078
-#define QSERDES_COM_CP_CTRL_MODE1 0x07c
-#define QSERDES_COM_PLL_RCTRL_MODE0 0x084
-#define QSERDES_COM_PLL_RCTRL_MODE1 0x088
-#define QSERDES_COM_PLL_CCTRL_MODE0 0x090
-#define QSERDES_COM_PLL_CCTRL_MODE1 0x094
-#define QSERDES_COM_BIAS_EN_CTRL_BY_PSM 0x0a8
-#define QSERDES_COM_SYSCLK_EN_SEL 0x0ac
-#define QSERDES_COM_RESETSM_CNTRL 0x0b4
-#define QSERDES_COM_RESTRIM_CTRL 0x0bc
-#define QSERDES_COM_RESCODE_DIV_NUM 0x0c4
-#define QSERDES_COM_LOCK_CMP_EN 0x0c8
-#define QSERDES_COM_LOCK_CMP_CFG 0x0cc
-#define QSERDES_COM_DEC_START_MODE0 0x0d0
-#define QSERDES_COM_DEC_START_MODE1 0x0d4
-#define QSERDES_COM_DIV_FRAC_START1_MODE0 0x0dc
-#define QSERDES_COM_DIV_FRAC_START2_MODE0 0x0e0
-#define QSERDES_COM_DIV_FRAC_START3_MODE0 0x0e4
-#define QSERDES_COM_DIV_FRAC_START1_MODE1 0x0e8
-#define QSERDES_COM_DIV_FRAC_START2_MODE1 0x0ec
-#define QSERDES_COM_DIV_FRAC_START3_MODE1 0x0f0
-#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 0x108
-#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 0x10c
-#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 0x110
-#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 0x114
-#define QSERDES_COM_VCO_TUNE_CTRL 0x124
-#define QSERDES_COM_VCO_TUNE_MAP 0x128
-#define QSERDES_COM_VCO_TUNE1_MODE0 0x12c
-#define QSERDES_COM_VCO_TUNE2_MODE0 0x130
-#define QSERDES_COM_VCO_TUNE1_MODE1 0x134
-#define QSERDES_COM_VCO_TUNE2_MODE1 0x138
-#define QSERDES_COM_VCO_TUNE_TIMER1 0x144
-#define QSERDES_COM_VCO_TUNE_TIMER2 0x148
-#define QSERDES_COM_BG_CTRL 0x170
-#define QSERDES_COM_CLK_SELECT 0x174
-#define QSERDES_COM_HSCLK_SEL 0x178
-#define QSERDES_COM_CORECLK_DIV 0x184
-#define QSERDES_COM_CORE_CLK_EN 0x18c
-#define QSERDES_COM_C_READY_STATUS 0x190
-#define QSERDES_COM_CMN_CONFIG 0x194
-#define QSERDES_COM_SVS_MODE_CLK_SEL 0x19c
-#define QSERDES_COM_DEBUG_BUS0 0x1a0
-#define QSERDES_COM_DEBUG_BUS1 0x1a4
-#define QSERDES_COM_DEBUG_BUS2 0x1a8
-#define QSERDES_COM_DEBUG_BUS3 0x1ac
-#define QSERDES_COM_DEBUG_BUS_SEL 0x1b0
-#define QSERDES_COM_CORECLK_DIV_MODE1 0x1bc
-
-/* QMP PHY TX registers */
-#define QSERDES_TX_RES_CODE_LANE_OFFSET 0x054
-#define QSERDES_TX_DEBUG_BUS_SEL 0x064
-#define QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN 0x068
-#define QSERDES_TX_LANE_MODE 0x094
-#define QSERDES_TX_RCV_DETECT_LVL_2 0x0ac
-
-/* QMP PHY RX registers */
-#define QSERDES_RX_UCDR_SO_GAIN_HALF 0x010
-#define QSERDES_RX_UCDR_SO_GAIN 0x01c
-#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN 0x040
-#define QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE 0x048
-#define QSERDES_RX_RX_TERM_BW 0x090
-#define QSERDES_RX_RX_EQ_GAIN1_LSB 0x0c4
-#define QSERDES_RX_RX_EQ_GAIN1_MSB 0x0c8
-#define QSERDES_RX_RX_EQ_GAIN2_LSB 0x0cc
-#define QSERDES_RX_RX_EQ_GAIN2_MSB 0x0d0
-#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 0x0d8
-#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3 0x0dc
-#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4 0x0e0
-#define QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x108
-#define QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x10c
-#define QSERDES_RX_SIGDET_ENABLES 0x110
-#define QSERDES_RX_SIGDET_CNTRL 0x114
-#define QSERDES_RX_SIGDET_LVL 0x118
-#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL 0x11c
-#define QSERDES_RX_RX_BAND 0x120
-#define QSERDES_RX_RX_INTERFACE_MODE 0x12c
-
-/* QMP PHY PCS registers */
-#define QPHY_POWER_DOWN_CONTROL 0x04
-#define QPHY_TXDEEMPH_M6DB_V0 0x24
-#define QPHY_TXDEEMPH_M3P5DB_V0 0x28
-#define QPHY_ENDPOINT_REFCLK_DRIVE 0x54
-#define QPHY_RX_IDLE_DTCT_CNTRL 0x58
-#define QPHY_POWER_STATE_CONFIG1 0x60
-#define QPHY_POWER_STATE_CONFIG2 0x64
-#define QPHY_POWER_STATE_CONFIG4 0x6c
-#define QPHY_LOCK_DETECT_CONFIG1 0x80
-#define QPHY_LOCK_DETECT_CONFIG2 0x84
-#define QPHY_LOCK_DETECT_CONFIG3 0x88
-#define QPHY_PWRUP_RESET_DLY_TIME_AUXCLK 0xa0
-#define QPHY_LP_WAKEUP_DLY_TIME_AUXCLK 0xa4
-#define QPHY_PLL_LOCK_CHK_DLY_TIME_AUXCLK_LSB 0x1A8
-#define QPHY_OSC_DTCT_ACTIONS 0x1AC
-#define QPHY_RX_SIGDET_LVL 0x1D8
-#define QPHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB 0x1DC
-#define QPHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB 0x1E0
+#include "phy-qcom-qmp.h"
/* QPHY_SW_RESET bit */
#define SW_RESET BIT(0)
@@ -164,6 +38,34 @@
/* QPHY_COM_PCS_READY_STATUS bit */
#define PCS_READY BIT(0)
+/* QPHY_V3_DP_COM_RESET_OVRD_CTRL register bits */
+/* DP PHY soft reset */
+#define SW_DPPHY_RESET BIT(0)
+/* mux to select DP PHY reset control, 0:HW control, 1: software reset */
+#define SW_DPPHY_RESET_MUX BIT(1)
+/* USB3 PHY soft reset */
+#define SW_USB3PHY_RESET BIT(2)
+/* mux to select USB3 PHY reset control, 0:HW control, 1: software reset */
+#define SW_USB3PHY_RESET_MUX BIT(3)
+
+/* QPHY_V3_DP_COM_PHY_MODE_CTRL register bits */
+#define USB3_MODE BIT(0) /* enables USB3 mode */
+#define DP_MODE BIT(1) /* enables DP mode */
+
+/* QPHY_PCS_AUTONOMOUS_MODE_CTRL register bits */
+#define ARCVR_DTCT_EN BIT(0)
+#define ALFPS_DTCT_EN BIT(1)
+#define ARCVR_DTCT_EVENT_SEL BIT(4)
+
+/* QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR register bits */
+#define IRQ_CLEAR BIT(0)
+
+/* QPHY_PCS_LFPS_RXTERM_IRQ_STATUS register bits */
+#define RCVR_DETECT BIT(0)
+
+/* QPHY_V3_PCS_MISC_CLAMP_ENABLE register bits */
+#define CLAMP_EN BIT(0) /* enables i/o clamp_n */
+
#define PHY_INIT_COMPLETE_TIMEOUT 1000
#define POWER_DOWN_DELAY_US_MIN 10
#define POWER_DOWN_DELAY_US_MAX 11
@@ -210,6 +112,9 @@ enum qphy_reg_layout {
QPHY_SW_RESET,
QPHY_START_CTRL,
QPHY_PCS_READY_STATUS,
+ QPHY_PCS_AUTONOMOUS_MODE_CTRL,
+ QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR,
+ QPHY_PCS_LFPS_RXTERM_IRQ_STATUS,
};
static const unsigned int pciephy_regs_layout[] = {
@@ -237,6 +142,18 @@ static const unsigned int usb3phy_regs_layout[] = {
[QPHY_SW_RESET] = 0x00,
[QPHY_START_CTRL] = 0x08,
[QPHY_PCS_READY_STATUS] = 0x17c,
+ [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = 0x0d4,
+ [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0x0d8,
+ [QPHY_PCS_LFPS_RXTERM_IRQ_STATUS] = 0x178,
+};
+
+static const unsigned int qmp_v3_usb3phy_regs_layout[] = {
+ [QPHY_SW_RESET] = 0x00,
+ [QPHY_START_CTRL] = 0x08,
+ [QPHY_PCS_READY_STATUS] = 0x174,
+ [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = 0x0d8,
+ [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0x0dc,
+ [QPHY_PCS_LFPS_RXTERM_IRQ_STATUS] = 0x170,
};
static const struct qmp_phy_init_tbl msm8996_pcie_serdes_tbl[] = {
@@ -467,6 +384,112 @@ static const struct qmp_phy_init_tbl ipq8074_pcie_pcs_tbl[] = {
QMP_PHY_INIT_CFG_L(QPHY_START_CTRL, 0x3),
};
+static const struct qmp_phy_init_tbl qmp_v3_usb3_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL2, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0xab),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0xea),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x34),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x15),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_CFG, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_BUF_ENABLE, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x31),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x85),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x07),
+};
+
+static const struct qmp_phy_init_tbl qmp_v3_usb3_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x09),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x06),
+};
+
+static const struct qmp_phy_init_tbl qmp_v3_usb3_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4e),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x75),
+};
+
+static const struct qmp_phy_init_tbl qmp_v3_usb3_pcs_tbl[] = {
+ /* FLL settings */
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x40),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02),
+
+ /* Lock Det settings */
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG2, 0x1b),
+
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0xba),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V0, 0x9f),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V1, 0x9f),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V2, 0xb7),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V3, 0x4e),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V4, 0x65),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_LS, 0x6b),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V1, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V1, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V2, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V2, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V3, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V3, 0x1d),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V4, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V4, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_LS, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_LS, 0x0d),
+
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RATE_SLEW_CNTRL, 0x02),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13),
+};
+
/* struct qmp_phy_cfg - per-PHY initialization config */
struct qmp_phy_cfg {
/* phy-type - PCIE/UFS/USB */
@@ -511,6 +534,12 @@ struct qmp_phy_cfg {
/* power_down delay in usec */
int pwrdn_delay_min;
int pwrdn_delay_max;
+
+ /* true, if PHY has a separate DP_COM control block */
+ bool has_phy_dp_com_ctrl;
+ /* Register offset of secondary tx/rx lanes for USB DP combo PHY */
+ unsigned int tx_b_lane_offset;
+ unsigned int rx_b_lane_offset;
};
/**
@@ -520,6 +549,7 @@ struct qmp_phy_cfg {
* @tx: iomapped memory space for lane's tx
* @rx: iomapped memory space for lane's rx
* @pcs: iomapped memory space for lane's pcs
+ * @pcs_misc: iomapped memory space for lane's pcs_misc
* @pipe_clk: pipe lock
* @index: lane index
* @qmp: QMP phy to which this lane belongs
@@ -530,6 +560,7 @@ struct qmp_phy {
void __iomem *tx;
void __iomem *rx;
void __iomem *pcs;
+ void __iomem *pcs_misc;
struct clk *pipe_clk;
unsigned int index;
struct qcom_qmp *qmp;
@@ -541,6 +572,7 @@ struct qmp_phy {
*
* @dev: device
* @serdes: iomapped memory space for phy's serdes
+ * @dp_com: iomapped memory space for phy's dp_com control block
*
* @clks: array of clocks required by phy
* @resets: array of resets required by phy
@@ -550,12 +582,15 @@ struct qmp_phy {
* @phys: array of per-lane phy descriptors
* @phy_mutex: mutex lock for PHY common block initialization
* @init_count: phy common block initialization count
+ * @phy_initialized: indicate if PHY has been initialized
+ * @mode: current PHY mode
*/
struct qcom_qmp {
struct device *dev;
void __iomem *serdes;
+ void __iomem *dp_com;
- struct clk **clks;
+ struct clk_bulk_data *clks;
struct reset_control **resets;
struct regulator_bulk_data *vregs;
@@ -564,6 +599,8 @@ struct qcom_qmp {
struct mutex phy_mutex;
int init_count;
+ bool phy_initialized;
+ enum phy_mode mode;
};
static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
@@ -595,6 +632,10 @@ static const char * const msm8996_phy_clk_l[] = {
"aux", "cfg_ahb", "ref",
};
+static const char * const qmp_v3_phy_clk_l[] = {
+ "aux", "cfg_ahb", "ref", "com_aux",
+};
+
/* list of resets */
static const char * const msm8996_pciephy_reset_l[] = {
"phy", "common", "cfg",
@@ -701,6 +742,38 @@ static const struct qmp_phy_cfg ipq8074_pciephy_cfg = {
.pwrdn_delay_max = 1005, /* us */
};
+static const struct qmp_phy_cfg qmp_v3_usb3phy_cfg = {
+ .type = PHY_TYPE_USB3,
+ .nlanes = 1,
+
+ .serdes_tbl = qmp_v3_usb3_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(qmp_v3_usb3_serdes_tbl),
+ .tx_tbl = qmp_v3_usb3_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_tx_tbl),
+ .rx_tbl = qmp_v3_usb3_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_rx_tbl),
+ .pcs_tbl = qmp_v3_usb3_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(qmp_v3_usb3_pcs_tbl),
+ .clk_list = qmp_v3_phy_clk_l,
+ .num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l),
+ .reset_list = msm8996_usb3phy_reset_l,
+ .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
+ .vreg_list = msm8996_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(msm8996_phy_vreg_l),
+ .regs = qmp_v3_usb3phy_regs_layout,
+
+ .start_ctrl = SERDES_START | PCS_START,
+ .pwrdn_ctrl = SW_PWRDN,
+ .mask_pcs_ready = PHYSTATUS,
+
+ .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN,
+ .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX,
+
+ .has_phy_dp_com_ctrl = true,
+ .tx_b_lane_offset = 0x400,
+ .rx_b_lane_offset = 0x400,
+};
+
static void qcom_qmp_phy_configure(void __iomem *base,
const unsigned int *regs,
const struct qmp_phy_init_tbl tbl[],
@@ -724,44 +797,20 @@ static int qcom_qmp_phy_poweron(struct phy *phy)
{
struct qmp_phy *qphy = phy_get_drvdata(phy);
struct qcom_qmp *qmp = qphy->qmp;
- int num = qmp->cfg->num_vregs;
int ret;
- dev_vdbg(&phy->dev, "Powering on QMP phy\n");
-
- /* turn on regulator supplies */
- ret = regulator_bulk_enable(num, qmp->vregs);
- if (ret) {
- dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret);
- return ret;
- }
-
ret = clk_prepare_enable(qphy->pipe_clk);
- if (ret) {
+ if (ret)
dev_err(qmp->dev, "pipe_clk enable failed, err=%d\n", ret);
- regulator_bulk_disable(num, qmp->vregs);
- return ret;
- }
- return 0;
-}
-
-static int qcom_qmp_phy_poweroff(struct phy *phy)
-{
- struct qmp_phy *qphy = phy_get_drvdata(phy);
- struct qcom_qmp *qmp = qphy->qmp;
-
- clk_disable_unprepare(qphy->pipe_clk);
-
- regulator_bulk_disable(qmp->cfg->num_vregs, qmp->vregs);
-
- return 0;
+ return ret;
}
static int qcom_qmp_phy_com_init(struct qcom_qmp *qmp)
{
const struct qmp_phy_cfg *cfg = qmp->cfg;
void __iomem *serdes = qmp->serdes;
+ void __iomem *dp_com = qmp->dp_com;
int ret, i;
mutex_lock(&qmp->phy_mutex);
@@ -770,7 +819,23 @@ static int qcom_qmp_phy_com_init(struct qcom_qmp *qmp)
return 0;
}
+ /* turn on regulator supplies */
+ ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs);
+ if (ret) {
+ dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret);
+ goto err_reg_enable;
+ }
+
for (i = 0; i < cfg->num_resets; i++) {
+ ret = reset_control_assert(qmp->resets[i]);
+ if (ret) {
+ dev_err(qmp->dev, "%s reset assert failed\n",
+ cfg->reset_list[i]);
+ goto err_rst_assert;
+ }
+ }
+
+ for (i = cfg->num_resets - 1; i >= 0; i--) {
ret = reset_control_deassert(qmp->resets[i]);
if (ret) {
dev_err(qmp->dev, "%s reset deassert failed\n",
@@ -779,10 +844,33 @@ static int qcom_qmp_phy_com_init(struct qcom_qmp *qmp)
}
}
+ ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks);
+ if (ret) {
+ dev_err(qmp->dev, "failed to enable clks, err=%d\n", ret);
+ goto err_rst;
+ }
+
if (cfg->has_phy_com_ctrl)
qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL],
SW_PWRDN);
+ if (cfg->has_phy_dp_com_ctrl) {
+ qphy_setbits(dp_com, QPHY_V3_DP_COM_POWER_DOWN_CTRL,
+ SW_PWRDN);
+ /* override hardware control for reset of qmp phy */
+ qphy_setbits(dp_com, QPHY_V3_DP_COM_RESET_OVRD_CTRL,
+ SW_DPPHY_RESET_MUX | SW_DPPHY_RESET |
+ SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET);
+
+ qphy_setbits(dp_com, QPHY_V3_DP_COM_PHY_MODE_CTRL,
+ USB3_MODE | DP_MODE);
+
+ /* bring both QMP USB and QMP DP PHYs PCS block out of reset */
+ qphy_clrbits(dp_com, QPHY_V3_DP_COM_RESET_OVRD_CTRL,
+ SW_DPPHY_RESET_MUX | SW_DPPHY_RESET |
+ SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET);
+ }
+
/* Serdes configuration */
qcom_qmp_phy_configure(serdes, cfg->regs, cfg->serdes_tbl,
cfg->serdes_tbl_num);
@@ -803,7 +891,7 @@ static int qcom_qmp_phy_com_init(struct qcom_qmp *qmp)
if (ret) {
dev_err(qmp->dev,
"phy common block init timed-out\n");
- goto err_rst;
+ goto err_com_init;
}
}
@@ -811,9 +899,14 @@ static int qcom_qmp_phy_com_init(struct qcom_qmp *qmp)
return 0;
+err_com_init:
+ clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks);
err_rst:
- while (--i >= 0)
+ while (++i < cfg->num_resets)
reset_control_assert(qmp->resets[i]);
+err_rst_assert:
+ regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
+err_reg_enable:
mutex_unlock(&qmp->phy_mutex);
return ret;
@@ -843,6 +936,10 @@ static int qcom_qmp_phy_com_exit(struct qcom_qmp *qmp)
while (--i >= 0)
reset_control_assert(qmp->resets[i]);
+ clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks);
+
+ regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
+
mutex_unlock(&qmp->phy_mutex);
return 0;
@@ -857,24 +954,16 @@ static int qcom_qmp_phy_init(struct phy *phy)
void __iomem *tx = qphy->tx;
void __iomem *rx = qphy->rx;
void __iomem *pcs = qphy->pcs;
+ void __iomem *dp_com = qmp->dp_com;
void __iomem *status;
unsigned int mask, val;
- int ret, i;
+ int ret;
dev_vdbg(qmp->dev, "Initializing QMP phy\n");
- for (i = 0; i < qmp->cfg->num_clks; i++) {
- ret = clk_prepare_enable(qmp->clks[i]);
- if (ret) {
- dev_err(qmp->dev, "failed to enable %s clk, err=%d\n",
- qmp->cfg->clk_list[i], ret);
- goto err_clk;
- }
- }
-
ret = qcom_qmp_phy_com_init(qmp);
if (ret)
- goto err_clk;
+ return ret;
if (cfg->has_lane_rst) {
ret = reset_control_deassert(qphy->lane_rst);
@@ -887,7 +976,16 @@ static int qcom_qmp_phy_init(struct phy *phy)
/* Tx, Rx, and PCS configurations */
qcom_qmp_phy_configure(tx, cfg->regs, cfg->tx_tbl, cfg->tx_tbl_num);
+ /* Configuration for other LANE for USB-DP combo PHY */
+ if (cfg->has_phy_dp_com_ctrl)
+ qcom_qmp_phy_configure(tx + cfg->tx_b_lane_offset, cfg->regs,
+ cfg->tx_tbl, cfg->tx_tbl_num);
+
qcom_qmp_phy_configure(rx, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num);
+ if (cfg->has_phy_dp_com_ctrl)
+ qcom_qmp_phy_configure(rx + cfg->rx_b_lane_offset, cfg->regs,
+ cfg->rx_tbl, cfg->rx_tbl_num);
+
qcom_qmp_phy_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num);
/*
@@ -899,11 +997,13 @@ static int qcom_qmp_phy_init(struct phy *phy)
if (cfg->has_pwrdn_delay)
usleep_range(cfg->pwrdn_delay_min, cfg->pwrdn_delay_max);
- /* start SerDes and Phy-Coding-Sublayer */
- qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
-
/* Pull PHY out of reset state */
qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
+ if (cfg->has_phy_dp_com_ctrl)
+ qphy_clrbits(dp_com, QPHY_V3_DP_COM_SW_RESET, SW_RESET);
+
+ /* start SerDes and Phy-Coding-Sublayer */
+ qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
status = pcs + cfg->regs[QPHY_PCS_READY_STATUS];
mask = cfg->mask_pcs_ready;
@@ -914,6 +1014,7 @@ static int qcom_qmp_phy_init(struct phy *phy)
dev_err(qmp->dev, "phy initialization timed-out\n");
goto err_pcs_ready;
}
+ qmp->phy_initialized = true;
return ret;
@@ -922,9 +1023,6 @@ err_pcs_ready:
reset_control_assert(qphy->lane_rst);
err_lane_rst:
qcom_qmp_phy_com_exit(qmp);
-err_clk:
- while (--i >= 0)
- clk_disable_unprepare(qmp->clks[i]);
return ret;
}
@@ -934,7 +1032,8 @@ static int qcom_qmp_phy_exit(struct phy *phy)
struct qmp_phy *qphy = phy_get_drvdata(phy);
struct qcom_qmp *qmp = qphy->qmp;
const struct qmp_phy_cfg *cfg = qmp->cfg;
- int i = cfg->num_clks;
+
+ clk_disable_unprepare(qphy->pipe_clk);
/* PHY reset */
qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
@@ -950,8 +1049,127 @@ static int qcom_qmp_phy_exit(struct phy *phy)
qcom_qmp_phy_com_exit(qmp);
- while (--i >= 0)
- clk_disable_unprepare(qmp->clks[i]);
+ qmp->phy_initialized = false;
+
+ return 0;
+}
+
+static int qcom_qmp_phy_set_mode(struct phy *phy, enum phy_mode mode)
+{
+ struct qmp_phy *qphy = phy_get_drvdata(phy);
+ struct qcom_qmp *qmp = qphy->qmp;
+
+ qmp->mode = mode;
+
+ return 0;
+}
+
+static void qcom_qmp_phy_enable_autonomous_mode(struct qmp_phy *qphy)
+{
+ struct qcom_qmp *qmp = qphy->qmp;
+ const struct qmp_phy_cfg *cfg = qmp->cfg;
+ void __iomem *pcs = qphy->pcs;
+ void __iomem *pcs_misc = qphy->pcs_misc;
+ u32 intr_mask;
+
+ if (qmp->mode == PHY_MODE_USB_HOST_SS ||
+ qmp->mode == PHY_MODE_USB_DEVICE_SS)
+ intr_mask = ARCVR_DTCT_EN | ALFPS_DTCT_EN;
+ else
+ intr_mask = ARCVR_DTCT_EN | ARCVR_DTCT_EVENT_SEL;
+
+ /* Clear any pending interrupts status */
+ qphy_setbits(pcs, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR);
+ /* Writing 1 followed by 0 clears the interrupt */
+ qphy_clrbits(pcs, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR);
+
+ qphy_clrbits(pcs, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL],
+ ARCVR_DTCT_EN | ALFPS_DTCT_EN | ARCVR_DTCT_EVENT_SEL);
+
+ /* Enable required PHY autonomous mode interrupts */
+ qphy_setbits(pcs, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL], intr_mask);
+
+ /* Enable i/o clamp_n for autonomous mode */
+ if (pcs_misc)
+ qphy_clrbits(pcs_misc, QPHY_V3_PCS_MISC_CLAMP_ENABLE, CLAMP_EN);
+}
+
+static void qcom_qmp_phy_disable_autonomous_mode(struct qmp_phy *qphy)
+{
+ struct qcom_qmp *qmp = qphy->qmp;
+ const struct qmp_phy_cfg *cfg = qmp->cfg;
+ void __iomem *pcs = qphy->pcs;
+ void __iomem *pcs_misc = qphy->pcs_misc;
+
+ /* Disable i/o clamp_n on resume for normal mode */
+ if (pcs_misc)
+ qphy_setbits(pcs_misc, QPHY_V3_PCS_MISC_CLAMP_ENABLE, CLAMP_EN);
+
+ qphy_clrbits(pcs, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL],
+ ARCVR_DTCT_EN | ARCVR_DTCT_EVENT_SEL | ALFPS_DTCT_EN);
+
+ qphy_setbits(pcs, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR);
+ /* Writing 1 followed by 0 clears the interrupt */
+ qphy_clrbits(pcs, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR);
+}
+
+static int __maybe_unused qcom_qmp_phy_runtime_suspend(struct device *dev)
+{
+ struct qcom_qmp *qmp = dev_get_drvdata(dev);
+ struct qmp_phy *qphy = qmp->phys[0];
+ const struct qmp_phy_cfg *cfg = qmp->cfg;
+
+ dev_vdbg(dev, "Suspending QMP phy, mode:%d\n", qmp->mode);
+
+ /* Supported only for USB3 PHY */
+ if (cfg->type != PHY_TYPE_USB3)
+ return 0;
+
+ if (!qmp->phy_initialized) {
+ dev_vdbg(dev, "PHY not initialized, bailing out\n");
+ return 0;
+ }
+
+ qcom_qmp_phy_enable_autonomous_mode(qphy);
+
+ clk_disable_unprepare(qphy->pipe_clk);
+ clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks);
+
+ return 0;
+}
+
+static int __maybe_unused qcom_qmp_phy_runtime_resume(struct device *dev)
+{
+ struct qcom_qmp *qmp = dev_get_drvdata(dev);
+ struct qmp_phy *qphy = qmp->phys[0];
+ const struct qmp_phy_cfg *cfg = qmp->cfg;
+ int ret = 0;
+
+ dev_vdbg(dev, "Resuming QMP phy, mode:%d\n", qmp->mode);
+
+ /* Supported only for USB3 PHY */
+ if (cfg->type != PHY_TYPE_USB3)
+ return 0;
+
+ if (!qmp->phy_initialized) {
+ dev_vdbg(dev, "PHY not initialized, bailing out\n");
+ return 0;
+ }
+
+ ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks);
+ if (ret) {
+ dev_err(qmp->dev, "failed to enable clks, err=%d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(qphy->pipe_clk);
+ if (ret) {
+ dev_err(dev, "pipe_clk enable failed, err=%d\n", ret);
+ clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks);
+ return ret;
+ }
+
+ qcom_qmp_phy_disable_autonomous_mode(qphy);
return 0;
}
@@ -1000,29 +1218,17 @@ static int qcom_qmp_phy_reset_init(struct device *dev)
static int qcom_qmp_phy_clk_init(struct device *dev)
{
struct qcom_qmp *qmp = dev_get_drvdata(dev);
- int ret, i;
+ int num = qmp->cfg->num_clks;
+ int i;
- qmp->clks = devm_kcalloc(dev, qmp->cfg->num_clks,
- sizeof(*qmp->clks), GFP_KERNEL);
+ qmp->clks = devm_kcalloc(dev, num, sizeof(*qmp->clks), GFP_KERNEL);
if (!qmp->clks)
return -ENOMEM;
- for (i = 0; i < qmp->cfg->num_clks; i++) {
- struct clk *_clk;
- const char *name = qmp->cfg->clk_list[i];
-
- _clk = devm_clk_get(dev, name);
- if (IS_ERR(_clk)) {
- ret = PTR_ERR(_clk);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get %s clk, %d\n",
- name, ret);
- return ret;
- }
- qmp->clks[i] = _clk;
- }
+ for (i = 0; i < num; i++)
+ qmp->clks[i].id = qmp->cfg->clk_list[i];
- return 0;
+ return devm_clk_bulk_get(dev, num, qmp->clks);
}
/*
@@ -1078,7 +1284,7 @@ static const struct phy_ops qcom_qmp_phy_gen_ops = {
.init = qcom_qmp_phy_init,
.exit = qcom_qmp_phy_exit,
.power_on = qcom_qmp_phy_poweron,
- .power_off = qcom_qmp_phy_poweroff,
+ .set_mode = qcom_qmp_phy_set_mode,
.owner = THIS_MODULE,
};
@@ -1097,7 +1303,8 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id)
/*
* Get memory resources for each phy lane:
- * Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2.
+ * Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2; and
+ * pcs_misc (optional) -> 3.
*/
qphy->tx = of_iomap(np, 0);
if (!qphy->tx)
@@ -1111,6 +1318,10 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id)
if (!qphy->pcs)
return -ENOMEM;
+ qphy->pcs_misc = of_iomap(np, 3);
+ if (!qphy->pcs_misc)
+ dev_vdbg(dev, "PHY pcs_misc-reg not used\n");
+
/*
* Get PHY's Pipe clock, if any. USB3 and PCIe are PIPE3
* based phys, so they essentially have pipe clock. So,
@@ -1169,11 +1380,19 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = {
}, {
.compatible = "qcom,ipq8074-qmp-pcie-phy",
.data = &ipq8074_pciephy_cfg,
+ }, {
+ .compatible = "qcom,qmp-v3-usb3-phy",
+ .data = &qmp_v3_usb3phy_cfg,
},
{ },
};
MODULE_DEVICE_TABLE(of, qcom_qmp_phy_of_match_table);
+static const struct dev_pm_ops qcom_qmp_phy_pm_ops = {
+ SET_RUNTIME_PM_OPS(qcom_qmp_phy_runtime_suspend,
+ qcom_qmp_phy_runtime_resume, NULL)
+};
+
static int qcom_qmp_phy_probe(struct platform_device *pdev)
{
struct qcom_qmp *qmp;
@@ -1192,6 +1411,11 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev)
qmp->dev = dev;
dev_set_drvdata(dev, qmp);
+ /* Get the specific init parameters of QMP phy */
+ qmp->cfg = of_device_get_match_data(dev);
+ if (!qmp->cfg)
+ return -EINVAL;
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base))
@@ -1200,10 +1424,18 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev)
/* per PHY serdes; usually located at base address */
qmp->serdes = base;
- mutex_init(&qmp->phy_mutex);
+ /* per PHY dp_com; if PHY has dp_com control block */
+ if (qmp->cfg->has_phy_dp_com_ctrl) {
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "dp_com");
+ base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
- /* Get the specific init parameters of QMP phy */
- qmp->cfg = of_device_get_match_data(dev);
+ qmp->dp_com = base;
+ }
+
+ mutex_init(&qmp->phy_mutex);
ret = qcom_qmp_phy_clk_init(dev);
if (ret)
@@ -1229,12 +1461,21 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev)
return -ENOMEM;
id = 0;
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ /*
+ * Prevent runtime pm from being ON by default. Users can enable
+ * it using power/control in sysfs.
+ */
+ pm_runtime_forbid(dev);
+
for_each_available_child_of_node(dev->of_node, child) {
/* Create per-lane phy */
ret = qcom_qmp_phy_create(dev, child, id);
if (ret) {
dev_err(dev, "failed to create lane%d phy, %d\n",
id, ret);
+ pm_runtime_disable(dev);
return ret;
}
@@ -1246,6 +1487,7 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev)
if (ret) {
dev_err(qmp->dev,
"failed to register pipe clock source\n");
+ pm_runtime_disable(dev);
return ret;
}
id++;
@@ -1254,6 +1496,8 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev)
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (!IS_ERR(phy_provider))
dev_info(dev, "Registered Qcom-QMP phy\n");
+ else
+ pm_runtime_disable(dev);
return PTR_ERR_OR_ZERO(phy_provider);
}
@@ -1262,6 +1506,7 @@ static struct platform_driver qcom_qmp_phy_driver = {
.probe = qcom_qmp_phy_probe,
.driver = {
.name = "qcom-qmp-phy",
+ .pm = &qcom_qmp_phy_pm_ops,
.of_match_table = qcom_qmp_phy_of_match_table,
},
};
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h
new file mode 100644
index 000000000000..d1c6905d0439
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp.h
@@ -0,0 +1,280 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_H_
+#define QCOM_PHY_QMP_H_
+
+/* Only for QMP V2 PHY - QSERDES COM registers */
+#define QSERDES_COM_BG_TIMER 0x00c
+#define QSERDES_COM_SSC_EN_CENTER 0x010
+#define QSERDES_COM_SSC_ADJ_PER1 0x014
+#define QSERDES_COM_SSC_ADJ_PER2 0x018
+#define QSERDES_COM_SSC_PER1 0x01c
+#define QSERDES_COM_SSC_PER2 0x020
+#define QSERDES_COM_SSC_STEP_SIZE1 0x024
+#define QSERDES_COM_SSC_STEP_SIZE2 0x028
+#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN 0x034
+#define QSERDES_COM_CLK_ENABLE1 0x038
+#define QSERDES_COM_SYS_CLK_CTRL 0x03c
+#define QSERDES_COM_SYSCLK_BUF_ENABLE 0x040
+#define QSERDES_COM_PLL_IVCO 0x048
+#define QSERDES_COM_LOCK_CMP1_MODE0 0x04c
+#define QSERDES_COM_LOCK_CMP2_MODE0 0x050
+#define QSERDES_COM_LOCK_CMP3_MODE0 0x054
+#define QSERDES_COM_LOCK_CMP1_MODE1 0x058
+#define QSERDES_COM_LOCK_CMP2_MODE1 0x05c
+#define QSERDES_COM_LOCK_CMP3_MODE1 0x060
+#define QSERDES_COM_BG_TRIM 0x070
+#define QSERDES_COM_CLK_EP_DIV 0x074
+#define QSERDES_COM_CP_CTRL_MODE0 0x078
+#define QSERDES_COM_CP_CTRL_MODE1 0x07c
+#define QSERDES_COM_PLL_RCTRL_MODE0 0x084
+#define QSERDES_COM_PLL_RCTRL_MODE1 0x088
+#define QSERDES_COM_PLL_CCTRL_MODE0 0x090
+#define QSERDES_COM_PLL_CCTRL_MODE1 0x094
+#define QSERDES_COM_BIAS_EN_CTRL_BY_PSM 0x0a8
+#define QSERDES_COM_SYSCLK_EN_SEL 0x0ac
+#define QSERDES_COM_RESETSM_CNTRL 0x0b4
+#define QSERDES_COM_RESTRIM_CTRL 0x0bc
+#define QSERDES_COM_RESCODE_DIV_NUM 0x0c4
+#define QSERDES_COM_LOCK_CMP_EN 0x0c8
+#define QSERDES_COM_LOCK_CMP_CFG 0x0cc
+#define QSERDES_COM_DEC_START_MODE0 0x0d0
+#define QSERDES_COM_DEC_START_MODE1 0x0d4
+#define QSERDES_COM_DIV_FRAC_START1_MODE0 0x0dc
+#define QSERDES_COM_DIV_FRAC_START2_MODE0 0x0e0
+#define QSERDES_COM_DIV_FRAC_START3_MODE0 0x0e4
+#define QSERDES_COM_DIV_FRAC_START1_MODE1 0x0e8
+#define QSERDES_COM_DIV_FRAC_START2_MODE1 0x0ec
+#define QSERDES_COM_DIV_FRAC_START3_MODE1 0x0f0
+#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 0x108
+#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 0x10c
+#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 0x110
+#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 0x114
+#define QSERDES_COM_VCO_TUNE_CTRL 0x124
+#define QSERDES_COM_VCO_TUNE_MAP 0x128
+#define QSERDES_COM_VCO_TUNE1_MODE0 0x12c
+#define QSERDES_COM_VCO_TUNE2_MODE0 0x130
+#define QSERDES_COM_VCO_TUNE1_MODE1 0x134
+#define QSERDES_COM_VCO_TUNE2_MODE1 0x138
+#define QSERDES_COM_VCO_TUNE_TIMER1 0x144
+#define QSERDES_COM_VCO_TUNE_TIMER2 0x148
+#define QSERDES_COM_BG_CTRL 0x170
+#define QSERDES_COM_CLK_SELECT 0x174
+#define QSERDES_COM_HSCLK_SEL 0x178
+#define QSERDES_COM_CORECLK_DIV 0x184
+#define QSERDES_COM_CORE_CLK_EN 0x18c
+#define QSERDES_COM_C_READY_STATUS 0x190
+#define QSERDES_COM_CMN_CONFIG 0x194
+#define QSERDES_COM_SVS_MODE_CLK_SEL 0x19c
+#define QSERDES_COM_DEBUG_BUS0 0x1a0
+#define QSERDES_COM_DEBUG_BUS1 0x1a4
+#define QSERDES_COM_DEBUG_BUS2 0x1a8
+#define QSERDES_COM_DEBUG_BUS3 0x1ac
+#define QSERDES_COM_DEBUG_BUS_SEL 0x1b0
+#define QSERDES_COM_CORECLK_DIV_MODE1 0x1bc
+
+/* Only for QMP V2 PHY - TX registers */
+#define QSERDES_TX_RES_CODE_LANE_OFFSET 0x054
+#define QSERDES_TX_DEBUG_BUS_SEL 0x064
+#define QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN 0x068
+#define QSERDES_TX_LANE_MODE 0x094
+#define QSERDES_TX_RCV_DETECT_LVL_2 0x0ac
+
+/* Only for QMP V2 PHY - RX registers */
+#define QSERDES_RX_UCDR_SO_GAIN_HALF 0x010
+#define QSERDES_RX_UCDR_SO_GAIN 0x01c
+#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN 0x040
+#define QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE 0x048
+#define QSERDES_RX_RX_TERM_BW 0x090
+#define QSERDES_RX_RX_EQ_GAIN1_LSB 0x0c4
+#define QSERDES_RX_RX_EQ_GAIN1_MSB 0x0c8
+#define QSERDES_RX_RX_EQ_GAIN2_LSB 0x0cc
+#define QSERDES_RX_RX_EQ_GAIN2_MSB 0x0d0
+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 0x0d8
+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3 0x0dc
+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4 0x0e0
+#define QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x108
+#define QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x10c
+#define QSERDES_RX_SIGDET_ENABLES 0x110
+#define QSERDES_RX_SIGDET_CNTRL 0x114
+#define QSERDES_RX_SIGDET_LVL 0x118
+#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL 0x11c
+#define QSERDES_RX_RX_BAND 0x120
+#define QSERDES_RX_RX_INTERFACE_MODE 0x12c
+
+/* Only for QMP V2 PHY - PCS registers */
+#define QPHY_POWER_DOWN_CONTROL 0x04
+#define QPHY_TXDEEMPH_M6DB_V0 0x24
+#define QPHY_TXDEEMPH_M3P5DB_V0 0x28
+#define QPHY_ENDPOINT_REFCLK_DRIVE 0x54
+#define QPHY_RX_IDLE_DTCT_CNTRL 0x58
+#define QPHY_POWER_STATE_CONFIG1 0x60
+#define QPHY_POWER_STATE_CONFIG2 0x64
+#define QPHY_POWER_STATE_CONFIG4 0x6c
+#define QPHY_LOCK_DETECT_CONFIG1 0x80
+#define QPHY_LOCK_DETECT_CONFIG2 0x84
+#define QPHY_LOCK_DETECT_CONFIG3 0x88
+#define QPHY_PWRUP_RESET_DLY_TIME_AUXCLK 0xa0
+#define QPHY_LP_WAKEUP_DLY_TIME_AUXCLK 0xa4
+#define QPHY_PLL_LOCK_CHK_DLY_TIME_AUXCLK_LSB 0x1A8
+#define QPHY_OSC_DTCT_ACTIONS 0x1AC
+#define QPHY_RX_SIGDET_LVL 0x1D8
+#define QPHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB 0x1DC
+#define QPHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB 0x1E0
+
+/* Only for QMP V3 PHY - DP COM registers */
+#define QPHY_V3_DP_COM_PHY_MODE_CTRL 0x00
+#define QPHY_V3_DP_COM_SW_RESET 0x04
+#define QPHY_V3_DP_COM_POWER_DOWN_CTRL 0x08
+#define QPHY_V3_DP_COM_SWI_CTRL 0x0c
+#define QPHY_V3_DP_COM_TYPEC_CTRL 0x10
+#define QPHY_V3_DP_COM_TYPEC_PWRDN_CTRL 0x14
+#define QPHY_V3_DP_COM_RESET_OVRD_CTRL 0x1c
+
+/* Only for QMP V3 PHY - QSERDES COM registers */
+#define QSERDES_V3_COM_BG_TIMER 0x00c
+#define QSERDES_V3_COM_SSC_EN_CENTER 0x010
+#define QSERDES_V3_COM_SSC_ADJ_PER1 0x014
+#define QSERDES_V3_COM_SSC_ADJ_PER2 0x018
+#define QSERDES_V3_COM_SSC_PER1 0x01c
+#define QSERDES_V3_COM_SSC_PER2 0x020
+#define QSERDES_V3_COM_SSC_STEP_SIZE1 0x024
+#define QSERDES_V3_COM_SSC_STEP_SIZE2 0x028
+#define QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN 0x034
+#define QSERDES_V3_COM_CLK_ENABLE1 0x038
+#define QSERDES_V3_COM_SYS_CLK_CTRL 0x03c
+#define QSERDES_V3_COM_SYSCLK_BUF_ENABLE 0x040
+#define QSERDES_V3_COM_PLL_IVCO 0x048
+#define QSERDES_V3_COM_LOCK_CMP1_MODE0 0x098
+#define QSERDES_V3_COM_LOCK_CMP2_MODE0 0x09c
+#define QSERDES_V3_COM_LOCK_CMP3_MODE0 0x0a0
+#define QSERDES_V3_COM_LOCK_CMP1_MODE1 0x0a4
+#define QSERDES_V3_COM_LOCK_CMP2_MODE1 0x0a8
+#define QSERDES_V3_COM_LOCK_CMP3_MODE1 0x0ac
+#define QSERDES_V3_COM_CLK_EP_DIV 0x05c
+#define QSERDES_V3_COM_CP_CTRL_MODE0 0x060
+#define QSERDES_V3_COM_CP_CTRL_MODE1 0x064
+#define QSERDES_V3_COM_PLL_RCTRL_MODE0 0x068
+#define QSERDES_V3_COM_PLL_RCTRL_MODE1 0x06c
+#define QSERDES_V3_COM_PLL_CCTRL_MODE0 0x070
+#define QSERDES_V3_COM_PLL_CCTRL_MODE1 0x074
+#define QSERDES_V3_COM_SYSCLK_EN_SEL 0x080
+#define QSERDES_V3_COM_RESETSM_CNTRL 0x088
+#define QSERDES_V3_COM_RESETSM_CNTRL2 0x08c
+#define QSERDES_V3_COM_LOCK_CMP_EN 0x090
+#define QSERDES_V3_COM_LOCK_CMP_CFG 0x094
+#define QSERDES_V3_COM_DEC_START_MODE0 0x0b0
+#define QSERDES_V3_COM_DEC_START_MODE1 0x0b4
+#define QSERDES_V3_COM_DIV_FRAC_START1_MODE0 0x0b8
+#define QSERDES_V3_COM_DIV_FRAC_START2_MODE0 0x0bc
+#define QSERDES_V3_COM_DIV_FRAC_START3_MODE0 0x0c0
+#define QSERDES_V3_COM_DIV_FRAC_START1_MODE1 0x0c4
+#define QSERDES_V3_COM_DIV_FRAC_START2_MODE1 0x0c8
+#define QSERDES_V3_COM_DIV_FRAC_START3_MODE1 0x0cc
+#define QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0 0x0d8
+#define QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0 0x0dc
+#define QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE1 0x0e0
+#define QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE1 0x0e4
+#define QSERDES_V3_COM_VCO_TUNE_CTRL 0x0ec
+#define QSERDES_V3_COM_VCO_TUNE_MAP 0x0f0
+#define QSERDES_V3_COM_VCO_TUNE1_MODE0 0x0f4
+#define QSERDES_V3_COM_VCO_TUNE2_MODE0 0x0f8
+#define QSERDES_V3_COM_VCO_TUNE1_MODE1 0x0fc
+#define QSERDES_V3_COM_VCO_TUNE2_MODE1 0x100
+#define QSERDES_V3_COM_VCO_TUNE_TIMER1 0x11c
+#define QSERDES_V3_COM_VCO_TUNE_TIMER2 0x120
+#define QSERDES_V3_COM_CLK_SELECT 0x138
+#define QSERDES_V3_COM_HSCLK_SEL 0x13c
+#define QSERDES_V3_COM_CORECLK_DIV_MODE0 0x148
+#define QSERDES_V3_COM_CORECLK_DIV_MODE1 0x14c
+#define QSERDES_V3_COM_CORE_CLK_EN 0x154
+#define QSERDES_V3_COM_C_READY_STATUS 0x158
+#define QSERDES_V3_COM_CMN_CONFIG 0x15c
+#define QSERDES_V3_COM_SVS_MODE_CLK_SEL 0x164
+#define QSERDES_V3_COM_DEBUG_BUS0 0x168
+#define QSERDES_V3_COM_DEBUG_BUS1 0x16c
+#define QSERDES_V3_COM_DEBUG_BUS2 0x170
+#define QSERDES_V3_COM_DEBUG_BUS3 0x174
+#define QSERDES_V3_COM_DEBUG_BUS_SEL 0x178
+
+/* Only for QMP V3 PHY - TX registers */
+#define QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX 0x044
+#define QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX 0x048
+#define QSERDES_V3_TX_DEBUG_BUS_SEL 0x058
+#define QSERDES_V3_TX_HIGHZ_DRVR_EN 0x060
+#define QSERDES_V3_TX_LANE_MODE_1 0x08c
+#define QSERDES_V3_TX_RCV_DETECT_LVL_2 0x0a4
+
+/* Only for QMP V3 PHY - RX registers */
+#define QSERDES_V3_RX_UCDR_SO_GAIN_HALF 0x00c
+#define QSERDES_V3_RX_UCDR_SO_GAIN 0x014
+#define QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN 0x030
+#define QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE 0x034
+#define QSERDES_V3_RX_RX_TERM_BW 0x07c
+#define QSERDES_V3_RX_RX_EQ_GAIN2_LSB 0x0c8
+#define QSERDES_V3_RX_RX_EQ_GAIN2_MSB 0x0cc
+#define QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2 0x0d4
+#define QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3 0x0d8
+#define QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4 0x0dc
+#define QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x0f8
+#define QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x0fc
+#define QSERDES_V3_RX_SIGDET_ENABLES 0x100
+#define QSERDES_V3_RX_SIGDET_CNTRL 0x104
+#define QSERDES_V3_RX_SIGDET_LVL 0x108
+#define QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL 0x10c
+#define QSERDES_V3_RX_RX_BAND 0x110
+#define QSERDES_V3_RX_RX_INTERFACE_MODE 0x11c
+
+/* Only for QMP V3 PHY - PCS registers */
+#define QPHY_V3_PCS_POWER_DOWN_CONTROL 0x004
+#define QPHY_V3_PCS_TXMGN_V0 0x00c
+#define QPHY_V3_PCS_TXMGN_V1 0x010
+#define QPHY_V3_PCS_TXMGN_V2 0x014
+#define QPHY_V3_PCS_TXMGN_V3 0x018
+#define QPHY_V3_PCS_TXMGN_V4 0x01c
+#define QPHY_V3_PCS_TXMGN_LS 0x020
+#define QPHY_V3_PCS_TXDEEMPH_M6DB_V0 0x024
+#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0 0x028
+#define QPHY_V3_PCS_TXDEEMPH_M6DB_V1 0x02c
+#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V1 0x030
+#define QPHY_V3_PCS_TXDEEMPH_M6DB_V2 0x034
+#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V2 0x038
+#define QPHY_V3_PCS_TXDEEMPH_M6DB_V3 0x03c
+#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V3 0x040
+#define QPHY_V3_PCS_TXDEEMPH_M6DB_V4 0x044
+#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V4 0x048
+#define QPHY_V3_PCS_TXDEEMPH_M6DB_LS 0x04c
+#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_LS 0x050
+#define QPHY_V3_PCS_ENDPOINT_REFCLK_DRIVE 0x054
+#define QPHY_V3_PCS_RX_IDLE_DTCT_CNTRL 0x058
+#define QPHY_V3_PCS_RATE_SLEW_CNTRL 0x05c
+#define QPHY_V3_PCS_POWER_STATE_CONFIG1 0x060
+#define QPHY_V3_PCS_POWER_STATE_CONFIG2 0x064
+#define QPHY_V3_PCS_POWER_STATE_CONFIG4 0x06c
+#define QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L 0x070
+#define QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H 0x074
+#define QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L 0x078
+#define QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H 0x07c
+#define QPHY_V3_PCS_LOCK_DETECT_CONFIG1 0x080
+#define QPHY_V3_PCS_LOCK_DETECT_CONFIG2 0x084
+#define QPHY_V3_PCS_LOCK_DETECT_CONFIG3 0x088
+#define QPHY_V3_PCS_TSYNC_RSYNC_TIME 0x08c
+#define QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK 0x0a0
+#define QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK 0x0a4
+#define QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK 0x0b0
+#define QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME 0x0b8
+#define QPHY_V3_PCS_RXEQTRAINING_RUN_TIME 0x0bc
+#define QPHY_V3_PCS_FLL_CNTRL1 0x0c4
+#define QPHY_V3_PCS_FLL_CNTRL2 0x0c8
+#define QPHY_V3_PCS_FLL_CNT_VAL_L 0x0cc
+#define QPHY_V3_PCS_FLL_CNT_VAL_H_TOL 0x0d0
+#define QPHY_V3_PCS_FLL_MAN_CODE 0x0d4
+#define QPHY_V3_PCS_RX_SIGDET_LVL 0x1d8
+
+/* Only for QMP V3 PHY - PCS_MISC registers */
+#define QPHY_V3_PCS_MISC_CLAMP_ENABLE 0x0c
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qusb2.c b/drivers/phy/qualcomm/phy-qcom-qusb2.c
index 6c575244c0fb..94afeac1a19e 100644
--- a/drivers/phy/qualcomm/phy-qcom-qusb2.c
+++ b/drivers/phy/qualcomm/phy-qcom-qusb2.c
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2017, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * 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.
*/
#include <linux/clk.h>
@@ -37,28 +29,57 @@
#define QUSB2PHY_PLL_AUTOPGM_CTL1 0x1c
#define QUSB2PHY_PLL_PWR_CTRL 0x18
-#define QUSB2PHY_PLL_STATUS 0x38
+/* QUSB2PHY_PLL_STATUS register bits */
#define PLL_LOCKED BIT(5)
-#define QUSB2PHY_PORT_TUNE1 0x80
-#define QUSB2PHY_PORT_TUNE2 0x84
-#define QUSB2PHY_PORT_TUNE3 0x88
-#define QUSB2PHY_PORT_TUNE4 0x8c
-#define QUSB2PHY_PORT_TUNE5 0x90
-#define QUSB2PHY_PORT_TEST2 0x9c
+/* QUSB2PHY_PLL_COMMON_STATUS_ONE register bits */
+#define CORE_READY_STATUS BIT(0)
-#define QUSB2PHY_PORT_POWERDOWN 0xb4
+/* QUSB2PHY_PORT_POWERDOWN register bits */
#define CLAMP_N_EN BIT(5)
#define FREEZIO_N BIT(1)
#define POWER_DOWN BIT(0)
+/* QUSB2PHY_PWR_CTRL1 register bits */
+#define PWR_CTRL1_VREF_SUPPLY_TRIM BIT(5)
+#define PWR_CTRL1_CLAMP_N_EN BIT(1)
+
#define QUSB2PHY_REFCLK_ENABLE BIT(0)
#define PHY_CLK_SCHEME_SEL BIT(0)
+/* QUSB2PHY_INTR_CTRL register bits */
+#define DMSE_INTR_HIGH_SEL BIT(4)
+#define DPSE_INTR_HIGH_SEL BIT(3)
+#define CHG_DET_INTR_EN BIT(2)
+#define DMSE_INTR_EN BIT(1)
+#define DPSE_INTR_EN BIT(0)
+
+/* QUSB2PHY_PLL_CORE_INPUT_OVERRIDE register bits */
+#define CORE_PLL_EN_FROM_RESET BIT(4)
+#define CORE_RESET BIT(5)
+#define CORE_RESET_MUX BIT(6)
+
+#define QUSB2PHY_PLL_ANALOG_CONTROLS_TWO 0x04
+#define QUSB2PHY_PLL_CLOCK_INVERTERS 0x18c
+#define QUSB2PHY_PLL_CMODE 0x2c
+#define QUSB2PHY_PLL_LOCK_DELAY 0x184
+#define QUSB2PHY_PLL_DIGITAL_TIMERS_TWO 0xb4
+#define QUSB2PHY_PLL_BIAS_CONTROL_1 0x194
+#define QUSB2PHY_PLL_BIAS_CONTROL_2 0x198
+#define QUSB2PHY_PWR_CTRL2 0x214
+#define QUSB2PHY_IMP_CTRL1 0x220
+#define QUSB2PHY_IMP_CTRL2 0x224
+#define QUSB2PHY_CHG_CTRL2 0x23c
+
struct qusb2_phy_init_tbl {
unsigned int offset;
unsigned int val;
+ /*
+ * register part of layout ?
+ * if yes, then offset gives index in the reg-layout
+ */
+ int in_layout;
};
#define QUSB2_PHY_INIT_CFG(o, v) \
@@ -67,30 +88,136 @@ struct qusb2_phy_init_tbl {
.val = v, \
}
+#define QUSB2_PHY_INIT_CFG_L(o, v) \
+ { \
+ .offset = o, \
+ .val = v, \
+ .in_layout = 1, \
+ }
+
+/* set of registers with offsets different per-PHY */
+enum qusb2phy_reg_layout {
+ QUSB2PHY_PLL_CORE_INPUT_OVERRIDE,
+ QUSB2PHY_PLL_STATUS,
+ QUSB2PHY_PORT_TUNE1,
+ QUSB2PHY_PORT_TUNE2,
+ QUSB2PHY_PORT_TUNE3,
+ QUSB2PHY_PORT_TUNE4,
+ QUSB2PHY_PORT_TUNE5,
+ QUSB2PHY_PORT_TEST1,
+ QUSB2PHY_PORT_TEST2,
+ QUSB2PHY_PORT_POWERDOWN,
+ QUSB2PHY_INTR_CTRL,
+};
+
+static const unsigned int msm8996_regs_layout[] = {
+ [QUSB2PHY_PLL_STATUS] = 0x38,
+ [QUSB2PHY_PORT_TUNE1] = 0x80,
+ [QUSB2PHY_PORT_TUNE2] = 0x84,
+ [QUSB2PHY_PORT_TUNE3] = 0x88,
+ [QUSB2PHY_PORT_TUNE4] = 0x8c,
+ [QUSB2PHY_PORT_TUNE5] = 0x90,
+ [QUSB2PHY_PORT_TEST1] = 0xb8,
+ [QUSB2PHY_PORT_TEST2] = 0x9c,
+ [QUSB2PHY_PORT_POWERDOWN] = 0xb4,
+ [QUSB2PHY_INTR_CTRL] = 0xbc,
+};
+
static const struct qusb2_phy_init_tbl msm8996_init_tbl[] = {
- QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE1, 0xf8),
- QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE2, 0xb3),
- QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE3, 0x83),
- QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE4, 0xc0),
+ QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE1, 0xf8),
+ QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE2, 0xb3),
+ QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE3, 0x83),
+ QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE4, 0xc0),
+
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_TUNE, 0x30),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL1, 0x79),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL2, 0x21),
- QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TEST2, 0x14),
+
+ QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TEST2, 0x14),
+
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_AUTOPGM_CTL1, 0x9f),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00),
};
+static const unsigned int qusb2_v2_regs_layout[] = {
+ [QUSB2PHY_PLL_CORE_INPUT_OVERRIDE] = 0xa8,
+ [QUSB2PHY_PLL_STATUS] = 0x1a0,
+ [QUSB2PHY_PORT_TUNE1] = 0x240,
+ [QUSB2PHY_PORT_TUNE2] = 0x244,
+ [QUSB2PHY_PORT_TUNE3] = 0x248,
+ [QUSB2PHY_PORT_TUNE4] = 0x24c,
+ [QUSB2PHY_PORT_TUNE5] = 0x250,
+ [QUSB2PHY_PORT_TEST1] = 0x254,
+ [QUSB2PHY_PORT_TEST2] = 0x258,
+ [QUSB2PHY_PORT_POWERDOWN] = 0x210,
+ [QUSB2PHY_INTR_CTRL] = 0x230,
+};
+
+static const struct qusb2_phy_init_tbl qusb2_v2_init_tbl[] = {
+ QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_ANALOG_CONTROLS_TWO, 0x03),
+ QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_CLOCK_INVERTERS, 0x7c),
+ QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_CMODE, 0x80),
+ QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_LOCK_DELAY, 0x0a),
+ QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_DIGITAL_TIMERS_TWO, 0x19),
+ QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_BIAS_CONTROL_1, 0x40),
+ QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_BIAS_CONTROL_2, 0x20),
+ QUSB2_PHY_INIT_CFG(QUSB2PHY_PWR_CTRL2, 0x21),
+ QUSB2_PHY_INIT_CFG(QUSB2PHY_IMP_CTRL1, 0x0),
+ QUSB2_PHY_INIT_CFG(QUSB2PHY_IMP_CTRL2, 0x58),
+
+ QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE1, 0x30),
+ QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE2, 0x29),
+ QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE3, 0xca),
+ QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE4, 0x04),
+ QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE5, 0x03),
+
+ QUSB2_PHY_INIT_CFG(QUSB2PHY_CHG_CTRL2, 0x0),
+};
+
struct qusb2_phy_cfg {
const struct qusb2_phy_init_tbl *tbl;
/* number of entries in the table */
unsigned int tbl_num;
/* offset to PHY_CLK_SCHEME register in TCSR map */
unsigned int clk_scheme_offset;
+
+ /* array of registers with different offsets */
+ const unsigned int *regs;
+ unsigned int mask_core_ready;
+ unsigned int disable_ctrl;
+ unsigned int autoresume_en;
+
+ /* true if PHY has PLL_TEST register to select clk_scheme */
+ bool has_pll_test;
+
+ /* true if TUNE1 register must be updated by fused value, else TUNE2 */
+ bool update_tune1_with_efuse;
+
+ /* true if PHY has PLL_CORE_INPUT_OVERRIDE register to reset PLL */
+ bool has_pll_override;
};
static const struct qusb2_phy_cfg msm8996_phy_cfg = {
- .tbl = msm8996_init_tbl,
- .tbl_num = ARRAY_SIZE(msm8996_init_tbl),
+ .tbl = msm8996_init_tbl,
+ .tbl_num = ARRAY_SIZE(msm8996_init_tbl),
+ .regs = msm8996_regs_layout,
+
+ .has_pll_test = true,
+ .disable_ctrl = (CLAMP_N_EN | FREEZIO_N | POWER_DOWN),
+ .mask_core_ready = PLL_LOCKED,
+ .autoresume_en = BIT(3),
+};
+
+static const struct qusb2_phy_cfg qusb2_v2_phy_cfg = {
+ .tbl = qusb2_v2_init_tbl,
+ .tbl_num = ARRAY_SIZE(qusb2_v2_init_tbl),
+ .regs = qusb2_v2_regs_layout,
+
+ .disable_ctrl = (PWR_CTRL1_VREF_SUPPLY_TRIM | PWR_CTRL1_CLAMP_N_EN |
+ POWER_DOWN),
+ .mask_core_ready = CORE_READY_STATUS,
+ .has_pll_override = true,
+ .autoresume_en = BIT(0),
};
static const char * const qusb2_phy_vreg_names[] = {
@@ -116,6 +243,8 @@ static const char * const qusb2_phy_vreg_names[] = {
*
* @cfg: phy config data
* @has_se_clk_scheme: indicate if PHY has single-ended ref clock scheme
+ * @phy_initialized: indicate if PHY has been initialized
+ * @mode: current PHY mode
*/
struct qusb2_phy {
struct phy *phy;
@@ -132,6 +261,8 @@ struct qusb2_phy {
const struct qusb2_phy_cfg *cfg;
bool has_se_clk_scheme;
+ bool phy_initialized;
+ enum phy_mode mode;
};
static inline void qusb2_setbits(void __iomem *base, u32 offset, u32 val)
@@ -160,26 +291,32 @@ static inline void qusb2_clrbits(void __iomem *base, u32 offset, u32 val)
static inline
void qcom_qusb2_phy_configure(void __iomem *base,
+ const unsigned int *regs,
const struct qusb2_phy_init_tbl tbl[], int num)
{
int i;
- for (i = 0; i < num; i++)
- writel(tbl[i].val, base + tbl[i].offset);
+ for (i = 0; i < num; i++) {
+ if (tbl[i].in_layout)
+ writel(tbl[i].val, base + regs[tbl[i].offset]);
+ else
+ writel(tbl[i].val, base + tbl[i].offset);
+ }
}
/*
* Fetches HS Tx tuning value from nvmem and sets the
- * QUSB2PHY_PORT_TUNE2 register.
+ * QUSB2PHY_PORT_TUNE1/2 register.
* For error case, skip setting the value and use the default value.
*/
static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy)
{
struct device *dev = &qphy->phy->dev;
+ const struct qusb2_phy_cfg *cfg = qphy->cfg;
u8 *val;
/*
- * Read efuse register having TUNE2 parameter's high nibble.
+ * Read efuse register having TUNE2/1 parameter's high nibble.
* If efuse register shows value as 0x0, or if we fail to find
* a valid efuse register settings, then use default value
* as 0xB for high nibble that we have already set while
@@ -191,58 +328,169 @@ static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy)
return;
}
- /* Fused TUNE2 value is the higher nibble only */
- qusb2_setbits(qphy->base, QUSB2PHY_PORT_TUNE2, val[0] << 0x4);
+ /* Fused TUNE1/2 value is the higher nibble only */
+ if (cfg->update_tune1_with_efuse)
+ qusb2_setbits(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE1],
+ val[0] << 0x4);
+ else
+ qusb2_setbits(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE2],
+ val[0] << 0x4);
+
}
-static int qusb2_phy_poweron(struct phy *phy)
+static int qusb2_phy_set_mode(struct phy *phy, enum phy_mode mode)
{
struct qusb2_phy *qphy = phy_get_drvdata(phy);
- int num = ARRAY_SIZE(qphy->vregs);
+
+ qphy->mode = mode;
+
+ return 0;
+}
+
+static int __maybe_unused qusb2_phy_runtime_suspend(struct device *dev)
+{
+ struct qusb2_phy *qphy = dev_get_drvdata(dev);
+ const struct qusb2_phy_cfg *cfg = qphy->cfg;
+ u32 intr_mask;
+
+ dev_vdbg(dev, "Suspending QUSB2 Phy, mode:%d\n", qphy->mode);
+
+ if (!qphy->phy_initialized) {
+ dev_vdbg(dev, "PHY not initialized, bailing out\n");
+ return 0;
+ }
+
+ /*
+ * Enable DP/DM interrupts to detect line state changes based on current
+ * speed. In other words, enable the triggers _opposite_ of what the
+ * current D+/D- levels are e.g. if currently D+ high, D- low
+ * (HS 'J'/Suspend), configure the mask to trigger on D+ low OR D- high
+ */
+ intr_mask = DPSE_INTR_EN | DMSE_INTR_EN;
+ switch (qphy->mode) {
+ case PHY_MODE_USB_HOST_HS:
+ case PHY_MODE_USB_HOST_FS:
+ case PHY_MODE_USB_DEVICE_HS:
+ case PHY_MODE_USB_DEVICE_FS:
+ intr_mask |= DMSE_INTR_HIGH_SEL;
+ break;
+ case PHY_MODE_USB_HOST_LS:
+ case PHY_MODE_USB_DEVICE_LS:
+ intr_mask |= DPSE_INTR_HIGH_SEL;
+ break;
+ default:
+ /* No device connected, enable both DP/DM high interrupt */
+ intr_mask |= DMSE_INTR_HIGH_SEL;
+ intr_mask |= DPSE_INTR_HIGH_SEL;
+ break;
+ }
+
+ writel(intr_mask, qphy->base + cfg->regs[QUSB2PHY_INTR_CTRL]);
+
+ /* hold core PLL into reset */
+ if (cfg->has_pll_override) {
+ qusb2_setbits(qphy->base,
+ cfg->regs[QUSB2PHY_PLL_CORE_INPUT_OVERRIDE],
+ CORE_PLL_EN_FROM_RESET | CORE_RESET |
+ CORE_RESET_MUX);
+ }
+
+ /* enable phy auto-resume only if device is connected on bus */
+ if (qphy->mode != PHY_MODE_INVALID) {
+ qusb2_setbits(qphy->base, cfg->regs[QUSB2PHY_PORT_TEST1],
+ cfg->autoresume_en);
+ /* Autoresume bit has to be toggled in order to enable it */
+ qusb2_clrbits(qphy->base, cfg->regs[QUSB2PHY_PORT_TEST1],
+ cfg->autoresume_en);
+ }
+
+ if (!qphy->has_se_clk_scheme)
+ clk_disable_unprepare(qphy->ref_clk);
+
+ clk_disable_unprepare(qphy->cfg_ahb_clk);
+ clk_disable_unprepare(qphy->iface_clk);
+
+ return 0;
+}
+
+static int __maybe_unused qusb2_phy_runtime_resume(struct device *dev)
+{
+ struct qusb2_phy *qphy = dev_get_drvdata(dev);
+ const struct qusb2_phy_cfg *cfg = qphy->cfg;
int ret;
- dev_vdbg(&phy->dev, "%s(): Powering-on QUSB2 phy\n", __func__);
+ dev_vdbg(dev, "Resuming QUSB2 phy, mode:%d\n", qphy->mode);
- /* turn on regulator supplies */
- ret = regulator_bulk_enable(num, qphy->vregs);
- if (ret)
- return ret;
+ if (!qphy->phy_initialized) {
+ dev_vdbg(dev, "PHY not initialized, bailing out\n");
+ return 0;
+ }
ret = clk_prepare_enable(qphy->iface_clk);
if (ret) {
- dev_err(&phy->dev, "failed to enable iface_clk, %d\n", ret);
- regulator_bulk_disable(num, qphy->vregs);
+ dev_err(dev, "failed to enable iface_clk, %d\n", ret);
return ret;
}
- return 0;
-}
+ ret = clk_prepare_enable(qphy->cfg_ahb_clk);
+ if (ret) {
+ dev_err(dev, "failed to enable cfg ahb clock, %d\n", ret);
+ goto disable_iface_clk;
+ }
-static int qusb2_phy_poweroff(struct phy *phy)
-{
- struct qusb2_phy *qphy = phy_get_drvdata(phy);
+ if (!qphy->has_se_clk_scheme) {
+ clk_prepare_enable(qphy->ref_clk);
+ if (ret) {
+ dev_err(dev, "failed to enable ref clk, %d\n", ret);
+ goto disable_ahb_clk;
+ }
+ }
- clk_disable_unprepare(qphy->iface_clk);
+ writel(0x0, qphy->base + cfg->regs[QUSB2PHY_INTR_CTRL]);
- regulator_bulk_disable(ARRAY_SIZE(qphy->vregs), qphy->vregs);
+ /* bring core PLL out of reset */
+ if (cfg->has_pll_override) {
+ qusb2_clrbits(qphy->base,
+ cfg->regs[QUSB2PHY_PLL_CORE_INPUT_OVERRIDE],
+ CORE_RESET | CORE_RESET_MUX);
+ }
return 0;
+
+disable_ahb_clk:
+ clk_disable_unprepare(qphy->cfg_ahb_clk);
+disable_iface_clk:
+ clk_disable_unprepare(qphy->iface_clk);
+
+ return ret;
}
static int qusb2_phy_init(struct phy *phy)
{
struct qusb2_phy *qphy = phy_get_drvdata(phy);
- unsigned int val;
+ const struct qusb2_phy_cfg *cfg = qphy->cfg;
+ unsigned int val = 0;
unsigned int clk_scheme;
int ret;
dev_vdbg(&phy->dev, "%s(): Initializing QUSB2 phy\n", __func__);
+ /* turn on regulator supplies */
+ ret = regulator_bulk_enable(ARRAY_SIZE(qphy->vregs), qphy->vregs);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(qphy->iface_clk);
+ if (ret) {
+ dev_err(&phy->dev, "failed to enable iface_clk, %d\n", ret);
+ goto poweroff_phy;
+ }
+
/* enable ahb interface clock to program phy */
ret = clk_prepare_enable(qphy->cfg_ahb_clk);
if (ret) {
dev_err(&phy->dev, "failed to enable cfg ahb clock, %d\n", ret);
- return ret;
+ goto disable_iface_clk;
}
/* Perform phy reset */
@@ -262,20 +510,23 @@ static int qusb2_phy_init(struct phy *phy)
}
/* Disable the PHY */
- qusb2_setbits(qphy->base, QUSB2PHY_PORT_POWERDOWN,
- CLAMP_N_EN | FREEZIO_N | POWER_DOWN);
+ qusb2_setbits(qphy->base, cfg->regs[QUSB2PHY_PORT_POWERDOWN],
+ qphy->cfg->disable_ctrl);
- /* save reset value to override reference clock scheme later */
- val = readl(qphy->base + QUSB2PHY_PLL_TEST);
+ if (cfg->has_pll_test) {
+ /* save reset value to override reference clock scheme later */
+ val = readl(qphy->base + QUSB2PHY_PLL_TEST);
+ }
- qcom_qusb2_phy_configure(qphy->base, qphy->cfg->tbl,
- qphy->cfg->tbl_num);
+ qcom_qusb2_phy_configure(qphy->base, cfg->regs, cfg->tbl,
+ cfg->tbl_num);
/* Set efuse value for tuning the PHY */
qusb2_phy_set_tune2_param(qphy);
/* Enable the PHY */
- qusb2_clrbits(qphy->base, QUSB2PHY_PORT_POWERDOWN, POWER_DOWN);
+ qusb2_clrbits(qphy->base, cfg->regs[QUSB2PHY_PORT_POWERDOWN],
+ POWER_DOWN);
/* Required to get phy pll lock successfully */
usleep_range(150, 160);
@@ -308,32 +559,37 @@ static int qusb2_phy_init(struct phy *phy)
}
if (!qphy->has_se_clk_scheme) {
- val &= ~CLK_REF_SEL;
ret = clk_prepare_enable(qphy->ref_clk);
if (ret) {
dev_err(&phy->dev, "failed to enable ref clk, %d\n",
ret);
goto assert_phy_reset;
}
- } else {
- val |= CLK_REF_SEL;
}
- writel(val, qphy->base + QUSB2PHY_PLL_TEST);
+ if (cfg->has_pll_test) {
+ if (!qphy->has_se_clk_scheme)
+ val &= ~CLK_REF_SEL;
+ else
+ val |= CLK_REF_SEL;
- /* ensure above write is through */
- readl(qphy->base + QUSB2PHY_PLL_TEST);
+ writel(val, qphy->base + QUSB2PHY_PLL_TEST);
+
+ /* ensure above write is through */
+ readl(qphy->base + QUSB2PHY_PLL_TEST);
+ }
/* Required to get phy pll lock successfully */
usleep_range(100, 110);
- val = readb(qphy->base + QUSB2PHY_PLL_STATUS);
- if (!(val & PLL_LOCKED)) {
+ val = readb(qphy->base + cfg->regs[QUSB2PHY_PLL_STATUS]);
+ if (!(val & cfg->mask_core_ready)) {
dev_err(&phy->dev,
"QUSB2PHY pll lock failed: status reg = %x\n", val);
ret = -EBUSY;
goto disable_ref_clk;
}
+ qphy->phy_initialized = true;
return 0;
@@ -344,6 +600,11 @@ assert_phy_reset:
reset_control_assert(qphy->phy_reset);
disable_ahb_clk:
clk_disable_unprepare(qphy->cfg_ahb_clk);
+disable_iface_clk:
+ clk_disable_unprepare(qphy->iface_clk);
+poweroff_phy:
+ regulator_bulk_disable(ARRAY_SIZE(qphy->vregs), qphy->vregs);
+
return ret;
}
@@ -352,8 +613,8 @@ static int qusb2_phy_exit(struct phy *phy)
struct qusb2_phy *qphy = phy_get_drvdata(phy);
/* Disable the PHY */
- qusb2_setbits(qphy->base, QUSB2PHY_PORT_POWERDOWN,
- CLAMP_N_EN | FREEZIO_N | POWER_DOWN);
+ qusb2_setbits(qphy->base, qphy->cfg->regs[QUSB2PHY_PORT_POWERDOWN],
+ qphy->cfg->disable_ctrl);
if (!qphy->has_se_clk_scheme)
clk_disable_unprepare(qphy->ref_clk);
@@ -361,6 +622,11 @@ static int qusb2_phy_exit(struct phy *phy)
reset_control_assert(qphy->phy_reset);
clk_disable_unprepare(qphy->cfg_ahb_clk);
+ clk_disable_unprepare(qphy->iface_clk);
+
+ regulator_bulk_disable(ARRAY_SIZE(qphy->vregs), qphy->vregs);
+
+ qphy->phy_initialized = false;
return 0;
}
@@ -368,8 +634,7 @@ static int qusb2_phy_exit(struct phy *phy)
static const struct phy_ops qusb2_phy_gen_ops = {
.init = qusb2_phy_init,
.exit = qusb2_phy_exit,
- .power_on = qusb2_phy_poweron,
- .power_off = qusb2_phy_poweroff,
+ .set_mode = qusb2_phy_set_mode,
.owner = THIS_MODULE,
};
@@ -377,11 +642,19 @@ static const struct of_device_id qusb2_phy_of_match_table[] = {
{
.compatible = "qcom,msm8996-qusb2-phy",
.data = &msm8996_phy_cfg,
+ }, {
+ .compatible = "qcom,qusb2-v2-phy",
+ .data = &qusb2_v2_phy_cfg,
},
{ },
};
MODULE_DEVICE_TABLE(of, qusb2_phy_of_match_table);
+static const struct dev_pm_ops qusb2_phy_pm_ops = {
+ SET_RUNTIME_PM_OPS(qusb2_phy_runtime_suspend,
+ qusb2_phy_runtime_resume, NULL)
+};
+
static int qusb2_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -459,11 +732,19 @@ static int qusb2_phy_probe(struct platform_device *pdev)
qphy->cell = NULL;
dev_dbg(dev, "failed to lookup tune2 hstx trim value\n");
}
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ /*
+ * Prevent runtime pm from being ON by default. Users can enable
+ * it using power/control in sysfs.
+ */
+ pm_runtime_forbid(dev);
generic_phy = devm_phy_create(dev, NULL, &qusb2_phy_gen_ops);
if (IS_ERR(generic_phy)) {
ret = PTR_ERR(generic_phy);
dev_err(dev, "failed to create phy, %d\n", ret);
+ pm_runtime_disable(dev);
return ret;
}
qphy->phy = generic_phy;
@@ -474,6 +755,8 @@ static int qusb2_phy_probe(struct platform_device *pdev)
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (!IS_ERR(phy_provider))
dev_info(dev, "Registered Qcom-QUSB2 phy\n");
+ else
+ pm_runtime_disable(dev);
return PTR_ERR_OR_ZERO(phy_provider);
}
@@ -482,6 +765,7 @@ static struct platform_driver qusb2_phy_driver = {
.probe = qusb2_phy_probe,
.driver = {
.name = "qcom-qusb2-phy",
+ .pm = &qusb2_phy_pm_ops,
.of_match_table = qusb2_phy_of_match_table,
},
};
diff --git a/drivers/phy/ralink/Kconfig b/drivers/phy/ralink/Kconfig
index b17635b407bc..14fd219535ef 100644
--- a/drivers/phy/ralink/Kconfig
+++ b/drivers/phy/ralink/Kconfig
@@ -4,6 +4,7 @@
config PHY_RALINK_USB
tristate "Ralink USB PHY driver"
depends on RALINK || COMPILE_TEST
+ depends on HAS_IOMEM
select GENERIC_PHY
select MFD_SYSCON
help
diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c
index 9c90e7d67e0a..fb8f05e39cf7 100644
--- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c
+++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c
@@ -397,6 +397,10 @@ static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = {
.data = (void *)RCAR_GEN3_PHY_HAS_DEDICATED_PINS,
},
{
+ .compatible = "renesas,usb2-phy-r8a77965",
+ .data = (void *)RCAR_GEN3_PHY_HAS_DEDICATED_PINS,
+ },
+ {
.compatible = "renesas,rcar-gen3-usb2-phy",
},
{ }
diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig
index f5325b2b679e..0e15119ddfc6 100644
--- a/drivers/phy/rockchip/Kconfig
+++ b/drivers/phy/rockchip/Kconfig
@@ -29,6 +29,7 @@ config PHY_ROCKCHIP_INNO_USB2
config PHY_ROCKCHIP_PCIE
tristate "Rockchip PCIe PHY Driver"
depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST
+ depends on HAS_IOMEM
select GENERIC_PHY
select MFD_SYSCON
help
diff --git a/drivers/phy/rockchip/phy-rockchip-emmc.c b/drivers/phy/rockchip/phy-rockchip-emmc.c
index f1b24f18e9b2..b237360f95f6 100644
--- a/drivers/phy/rockchip/phy-rockchip-emmc.c
+++ b/drivers/phy/rockchip/phy-rockchip-emmc.c
@@ -76,6 +76,13 @@
#define PHYCTRL_OTAPDLYSEL_MASK 0xf
#define PHYCTRL_OTAPDLYSEL_SHIFT 0x7
+#define PHYCTRL_IS_CALDONE(x) \
+ ((((x) >> PHYCTRL_CALDONE_SHIFT) & \
+ PHYCTRL_CALDONE_MASK) == PHYCTRL_CALDONE_DONE)
+#define PHYCTRL_IS_DLLRDY(x) \
+ ((((x) >> PHYCTRL_DLLRDY_SHIFT) & \
+ PHYCTRL_DLLRDY_MASK) == PHYCTRL_DLLRDY_DONE)
+
struct rockchip_emmc_phy {
unsigned int reg_offset;
struct regmap *reg_base;
@@ -89,7 +96,7 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off)
unsigned int dllrdy;
unsigned int freqsel = PHYCTRL_FREQSEL_200M;
unsigned long rate;
- unsigned long timeout;
+ int ret;
/*
* Keep phyctrl_pdb and phyctrl_endll low to allow
@@ -160,17 +167,19 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off)
PHYCTRL_PDB_SHIFT));
/*
- * According to the user manual, it asks driver to
- * wait 5us for calpad busy trimming
+ * According to the user manual, it asks driver to wait 5us for
+ * calpad busy trimming. However it is documented that this value is
+ * PVT(A.K.A process,voltage and temperature) relevant, so some
+ * failure cases are found which indicates we should be more tolerant
+ * to calpad busy trimming.
*/
- udelay(5);
- regmap_read(rk_phy->reg_base,
- rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
- &caldone);
- caldone = (caldone >> PHYCTRL_CALDONE_SHIFT) & PHYCTRL_CALDONE_MASK;
- if (caldone != PHYCTRL_CALDONE_DONE) {
- pr_err("rockchip_emmc_phy_power: caldone timeout.\n");
- return -ETIMEDOUT;
+ ret = regmap_read_poll_timeout(rk_phy->reg_base,
+ rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
+ caldone, PHYCTRL_IS_CALDONE(caldone),
+ 0, 50);
+ if (ret) {
+ pr_err("%s: caldone failed, ret=%d\n", __func__, ret);
+ return ret;
}
/* Set the frequency of the DLL operation */
@@ -210,28 +219,15 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off)
* NOTE: There appear to be corner cases where the DLL seems to take
* extra long to lock for reasons that aren't understood. In some
* extreme cases we've seen it take up to over 10ms (!). We'll be
- * generous and give it 50ms. We still busy wait here because:
- * - In most cases it should be super fast.
- * - This is not called lots during normal operation so it shouldn't
- * be a power or performance problem to busy wait. We expect it
- * only at boot / resume. In both cases, eMMC is probably on the
- * critical path so busy waiting a little extra time should be OK.
+ * generous and give it 50ms.
*/
- timeout = jiffies + msecs_to_jiffies(50);
- do {
- udelay(1);
-
- regmap_read(rk_phy->reg_base,
- rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
- &dllrdy);
- dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK;
- if (dllrdy == PHYCTRL_DLLRDY_DONE)
- break;
- } while (!time_after(jiffies, timeout));
-
- if (dllrdy != PHYCTRL_DLLRDY_DONE) {
- pr_err("rockchip_emmc_phy_power: dllrdy timeout.\n");
- return -ETIMEDOUT;
+ ret = regmap_read_poll_timeout(rk_phy->reg_base,
+ rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
+ dllrdy, PHYCTRL_IS_DLLRDY(dllrdy),
+ 0, 50 * USEC_PER_MSEC);
+ if (ret) {
+ pr_err("%s: dllrdy failed. ret=%d\n", __func__, ret);
+ return ret;
}
return 0;
diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c
index 7492c8978217..76a4b58ec771 100644
--- a/drivers/phy/rockchip/phy-rockchip-typec.c
+++ b/drivers/phy/rockchip/phy-rockchip-typec.c
@@ -355,11 +355,26 @@ struct usb3phy_reg {
u32 write_enable;
};
+/**
+ * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration.
+ * @reg: the base address for usb3-phy config.
+ * @typec_conn_dir: the register of type-c connector direction.
+ * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable.
+ * @external_psm: the register of type-c phy external psm clock.
+ * @pipe_status: the register of type-c phy pipe status.
+ * @usb3_host_disable: the register of type-c usb3 host disable.
+ * @usb3_host_port: the register of type-c usb3 host port.
+ * @uphy_dp_sel: the register of type-c phy DP select control.
+ */
struct rockchip_usb3phy_port_cfg {
+ unsigned int reg;
struct usb3phy_reg typec_conn_dir;
struct usb3phy_reg usb3tousb2_en;
struct usb3phy_reg external_psm;
struct usb3phy_reg pipe_status;
+ struct usb3phy_reg usb3_host_disable;
+ struct usb3phy_reg usb3_host_port;
+ struct usb3phy_reg uphy_dp_sel;
};
struct rockchip_typec_phy {
@@ -372,7 +387,7 @@ struct rockchip_typec_phy {
struct reset_control *uphy_rst;
struct reset_control *pipe_rst;
struct reset_control *tcphy_rst;
- struct rockchip_usb3phy_port_cfg port_cfgs;
+ const struct rockchip_usb3phy_port_cfg *port_cfgs;
/* mutex to protect access to individual PHYs */
struct mutex lock;
@@ -424,6 +439,30 @@ struct phy_reg dp_pll_cfg[] = {
{ 0x4, CMN_DIAG_PLL1_INCLK_CTRL },
};
+static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = {
+ {
+ .reg = 0xff7c0000,
+ .typec_conn_dir = { 0xe580, 0, 16 },
+ .usb3tousb2_en = { 0xe580, 3, 19 },
+ .external_psm = { 0xe588, 14, 30 },
+ .pipe_status = { 0xe5c0, 0, 0 },
+ .usb3_host_disable = { 0x2434, 0, 16 },
+ .usb3_host_port = { 0x2434, 12, 28 },
+ .uphy_dp_sel = { 0x6268, 19, 19 },
+ },
+ {
+ .reg = 0xff800000,
+ .typec_conn_dir = { 0xe58c, 0, 16 },
+ .usb3tousb2_en = { 0xe58c, 3, 19 },
+ .external_psm = { 0xe594, 14, 30 },
+ .pipe_status = { 0xe5c0, 16, 16 },
+ .usb3_host_disable = { 0x2444, 0, 16 },
+ .usb3_host_port = { 0x2444, 12, 28 },
+ .uphy_dp_sel = { 0x6268, 3, 19 },
+ },
+ { /* sentinel */ }
+};
+
static void tcphy_cfg_24m(struct rockchip_typec_phy *tcphy)
{
u32 i, rdata;
@@ -691,7 +730,7 @@ static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy)
static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode)
{
- struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+ const struct rockchip_usb3phy_port_cfg *cfg = tcphy->port_cfgs;
int ret, i;
u32 val;
@@ -782,6 +821,9 @@ static int tcphy_get_mode(struct rockchip_typec_phy *tcphy)
u8 mode;
int ret;
+ if (!edev)
+ return MODE_DFP_USB;
+
ufp = extcon_get_state(edev, EXTCON_USB);
dp = extcon_get_state(edev, EXTCON_DISP_DP);
@@ -818,10 +860,22 @@ static int tcphy_get_mode(struct rockchip_typec_phy *tcphy)
return mode;
}
+static int tcphy_cfg_usb3_to_usb2_only(struct rockchip_typec_phy *tcphy,
+ bool value)
+{
+ const struct rockchip_usb3phy_port_cfg *cfg = tcphy->port_cfgs;
+
+ property_enable(tcphy, &cfg->usb3tousb2_en, value);
+ property_enable(tcphy, &cfg->usb3_host_disable, value);
+ property_enable(tcphy, &cfg->usb3_host_port, !value);
+
+ return 0;
+}
+
static int rockchip_usb3_phy_power_on(struct phy *phy)
{
struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
- struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+ const struct rockchip_usb3phy_port_cfg *cfg = tcphy->port_cfgs;
const struct usb3phy_reg *reg = &cfg->pipe_status;
int timeout, new_mode, ret = 0;
u32 val;
@@ -835,8 +889,10 @@ static int rockchip_usb3_phy_power_on(struct phy *phy)
}
/* DP-only mode; fall back to USB2 */
- if (!(new_mode & (MODE_DFP_USB | MODE_UFP_USB)))
+ if (!(new_mode & (MODE_DFP_USB | MODE_UFP_USB))) {
+ tcphy_cfg_usb3_to_usb2_only(tcphy, true);
goto unlock_ret;
+ }
if (tcphy->mode == new_mode)
goto unlock_ret;
@@ -852,6 +908,9 @@ static int rockchip_usb3_phy_power_on(struct phy *phy)
regmap_read(tcphy->grf_regs, reg->offset, &val);
if (!(val & BIT(reg->enable_bit))) {
tcphy->mode |= new_mode & (MODE_DFP_USB | MODE_UFP_USB);
+
+ /* enable usb3 host */
+ tcphy_cfg_usb3_to_usb2_only(tcphy, false);
goto unlock_ret;
}
usleep_range(10, 20);
@@ -872,6 +931,7 @@ static int rockchip_usb3_phy_power_off(struct phy *phy)
struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
mutex_lock(&tcphy->lock);
+ tcphy_cfg_usb3_to_usb2_only(tcphy, false);
if (tcphy->mode == MODE_DISCONNECT)
goto unlock;
@@ -894,6 +954,7 @@ static const struct phy_ops rockchip_usb3_phy_ops = {
static int rockchip_dp_phy_power_on(struct phy *phy)
{
struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+ const struct rockchip_usb3phy_port_cfg *cfg = tcphy->port_cfgs;
int new_mode, ret = 0;
u32 val;
@@ -926,6 +987,8 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
if (ret)
goto unlock_ret;
+ property_enable(tcphy, &cfg->uphy_dp_sel, 1);
+
ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
val, val & DP_MODE_A2, 1000,
PHY_MODE_SET_TIMEOUT);
@@ -984,51 +1047,9 @@ static const struct phy_ops rockchip_dp_phy_ops = {
.owner = THIS_MODULE,
};
-static int tcphy_get_param(struct device *dev,
- struct usb3phy_reg *reg,
- const char *name)
-{
- u32 buffer[3];
- int ret;
-
- ret = of_property_read_u32_array(dev->of_node, name, buffer, 3);
- if (ret) {
- dev_err(dev, "Can not parse %s\n", name);
- return ret;
- }
-
- reg->offset = buffer[0];
- reg->enable_bit = buffer[1];
- reg->write_enable = buffer[2];
- return 0;
-}
-
static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
struct device *dev)
{
- struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
- int ret;
-
- ret = tcphy_get_param(dev, &cfg->typec_conn_dir,
- "rockchip,typec-conn-dir");
- if (ret)
- return ret;
-
- ret = tcphy_get_param(dev, &cfg->usb3tousb2_en,
- "rockchip,usb3tousb2-en");
- if (ret)
- return ret;
-
- ret = tcphy_get_param(dev, &cfg->external_psm,
- "rockchip,external-psm");
- if (ret)
- return ret;
-
- ret = tcphy_get_param(dev, &cfg->pipe_status,
- "rockchip,pipe-status");
- if (ret)
- return ret;
-
tcphy->grf_regs = syscon_regmap_lookup_by_phandle(dev->of_node,
"rockchip,grf");
if (IS_ERR(tcphy->grf_regs)) {
@@ -1071,7 +1092,7 @@ static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
static void typec_phy_pre_init(struct rockchip_typec_phy *tcphy)
{
- struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+ const struct rockchip_usb3phy_port_cfg *cfg = tcphy->port_cfgs;
reset_control_assert(tcphy->tcphy_rst);
reset_control_assert(tcphy->uphy_rst);
@@ -1092,17 +1113,43 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
struct rockchip_typec_phy *tcphy;
struct phy_provider *phy_provider;
struct resource *res;
- int ret;
+ const struct rockchip_usb3phy_port_cfg *phy_cfgs;
+ const struct of_device_id *match;
+ int index, ret;
tcphy = devm_kzalloc(dev, sizeof(*tcphy), GFP_KERNEL);
if (!tcphy)
return -ENOMEM;
+ match = of_match_device(dev->driver->of_match_table, dev);
+ if (!match || !match->data) {
+ dev_err(dev, "phy configs are not assigned!\n");
+ return -EINVAL;
+ }
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
tcphy->base = devm_ioremap_resource(dev, res);
if (IS_ERR(tcphy->base))
return PTR_ERR(tcphy->base);
+ phy_cfgs = match->data;
+ /* find out a proper config which can be matched with dt. */
+ index = 0;
+ while (phy_cfgs[index].reg) {
+ if (phy_cfgs[index].reg == res->start) {
+ tcphy->port_cfgs = &phy_cfgs[index];
+ break;
+ }
+
+ ++index;
+ }
+
+ if (!tcphy->port_cfgs) {
+ dev_err(dev, "no phy-config can be matched with %s node\n",
+ np->name);
+ return -EINVAL;
+ }
+
ret = tcphy_parse_dt(tcphy, dev);
if (ret)
return ret;
@@ -1115,9 +1162,13 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
tcphy->extcon = extcon_get_edev_by_phandle(dev, 0);
if (IS_ERR(tcphy->extcon)) {
- if (PTR_ERR(tcphy->extcon) != -EPROBE_DEFER)
- dev_err(dev, "Invalid or missing extcon\n");
- return PTR_ERR(tcphy->extcon);
+ if (PTR_ERR(tcphy->extcon) == -ENODEV) {
+ tcphy->extcon = NULL;
+ } else {
+ if (PTR_ERR(tcphy->extcon) != -EPROBE_DEFER)
+ dev_err(dev, "Invalid or missing extcon\n");
+ return PTR_ERR(tcphy->extcon);
+ }
}
pm_runtime_enable(dev);
@@ -1162,8 +1213,11 @@ static int rockchip_typec_phy_remove(struct platform_device *pdev)
}
static const struct of_device_id rockchip_typec_phy_dt_ids[] = {
- { .compatible = "rockchip,rk3399-typec-phy" },
- {}
+ {
+ .compatible = "rockchip,rk3399-typec-phy",
+ .data = &rk3399_usb3phy_port_cfgs
+ },
+ { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, rockchip_typec_phy_dt_ids);
diff --git a/drivers/phy/samsung/Kconfig b/drivers/phy/samsung/Kconfig
index b7e0645a7bd9..2a5d33cb0e7e 100644
--- a/drivers/phy/samsung/Kconfig
+++ b/drivers/phy/samsung/Kconfig
@@ -49,7 +49,7 @@ config PHY_EXYNOS4210_USB2
config PHY_EXYNOS4X12_USB2
bool
depends on PHY_SAMSUNG_USB2
- default SOC_EXYNOS3250 || SOC_EXYNOS4212 || SOC_EXYNOS4412
+ default SOC_EXYNOS3250 || SOC_EXYNOS4412
config PHY_EXYNOS5250_USB2
bool
diff --git a/drivers/phy/st/Kconfig b/drivers/phy/st/Kconfig
index 0814d3f87ec6..609719bdfa50 100644
--- a/drivers/phy/st/Kconfig
+++ b/drivers/phy/st/Kconfig
@@ -31,3 +31,17 @@ config PHY_STIH407_USB
help
Enable this support to enable the picoPHY device used by USB2
and USB3 controllers on STMicroelectronics STiH407 SoC families.
+
+config PHY_STM32_USBPHYC
+ tristate "STMicroelectronics STM32 USB HS PHY Controller driver"
+ depends on ARCH_STM32 || COMPILE_TEST
+ select GENERIC_PHY
+ help
+ Enable this to support the High-Speed USB transceivers that are part
+ of some STMicroelectronics STM32 SoCs.
+
+ This driver controls the entire USB PHY block: the USB PHY controller
+ (USBPHYC) and the two 8-bit wide UTMI+ interfaces. First interface is
+ used by an HS USB Host controller, and the second one is shared
+ between an HS USB OTG controller and an HS USB Host controller,
+ selected by a USB switch.
diff --git a/drivers/phy/st/Makefile b/drivers/phy/st/Makefile
index e2adfe2166d2..c0091ad1fd48 100644
--- a/drivers/phy/st/Makefile
+++ b/drivers/phy/st/Makefile
@@ -2,3 +2,4 @@ obj-$(CONFIG_PHY_MIPHY28LP) += phy-miphy28lp.o
obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY) += phy-spear1310-miphy.o
obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY) += phy-spear1340-miphy.o
obj-$(CONFIG_PHY_STIH407_USB) += phy-stih407-usb.o
+obj-$(CONFIG_PHY_STM32_USBPHYC) += phy-stm32-usbphyc.o
diff --git a/drivers/phy/st/phy-stm32-usbphyc.c b/drivers/phy/st/phy-stm32-usbphyc.c
new file mode 100644
index 000000000000..bc4e78a19c91
--- /dev/null
+++ b/drivers/phy/st/phy-stm32-usbphyc.c
@@ -0,0 +1,461 @@
+// SPDX-Licence-Identifier: GPL-2.0
+/*
+ * STMicroelectronics STM32 USB PHY Controller driver
+ *
+ * Copyright (C) 2018 STMicroelectronics
+ * Author(s): Amelie Delaunay <amelie.delaunay@st.com>.
+ */
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/reset.h>
+
+#define STM32_USBPHYC_PLL 0x0
+#define STM32_USBPHYC_MISC 0x8
+#define STM32_USBPHYC_VERSION 0x3F4
+
+/* STM32_USBPHYC_PLL bit fields */
+#define PLLNDIV GENMASK(6, 0)
+#define PLLFRACIN GENMASK(25, 10)
+#define PLLEN BIT(26)
+#define PLLSTRB BIT(27)
+#define PLLSTRBYP BIT(28)
+#define PLLFRACCTL BIT(29)
+#define PLLDITHEN0 BIT(30)
+#define PLLDITHEN1 BIT(31)
+
+/* STM32_USBPHYC_MISC bit fields */
+#define SWITHOST BIT(0)
+
+/* STM32_USBPHYC_VERSION bit fields */
+#define MINREV GENMASK(3, 0)
+#define MAJREV GENMASK(7, 4)
+
+static const char * const supplies_names[] = {
+ "vdda1v1", /* 1V1 */
+ "vdda1v8", /* 1V8 */
+};
+
+#define NUM_SUPPLIES ARRAY_SIZE(supplies_names)
+
+#define PLL_LOCK_TIME_US 100
+#define PLL_PWR_DOWN_TIME_US 5
+#define PLL_FVCO_MHZ 2880
+#define PLL_INFF_MIN_RATE_HZ 19200000
+#define PLL_INFF_MAX_RATE_HZ 38400000
+#define HZ_PER_MHZ 1000000L
+
+struct pll_params {
+ u8 ndiv;
+ u16 frac;
+};
+
+struct stm32_usbphyc_phy {
+ struct phy *phy;
+ struct stm32_usbphyc *usbphyc;
+ struct regulator_bulk_data supplies[NUM_SUPPLIES];
+ u32 index;
+ bool active;
+};
+
+struct stm32_usbphyc {
+ struct device *dev;
+ void __iomem *base;
+ struct clk *clk;
+ struct reset_control *rst;
+ struct stm32_usbphyc_phy **phys;
+ int nphys;
+ int switch_setup;
+ bool pll_enabled;
+};
+
+static inline void stm32_usbphyc_set_bits(void __iomem *reg, u32 bits)
+{
+ writel_relaxed(readl_relaxed(reg) | bits, reg);
+}
+
+static inline void stm32_usbphyc_clr_bits(void __iomem *reg, u32 bits)
+{
+ writel_relaxed(readl_relaxed(reg) & ~bits, reg);
+}
+
+static void stm32_usbphyc_get_pll_params(u32 clk_rate, struct pll_params *pll_params)
+{
+ unsigned long long fvco, ndiv, frac;
+
+ /* _
+ * | FVCO = INFF*2*(NDIV + FRACT/2^16) when DITHER_DISABLE[1] = 1
+ * | FVCO = 2880MHz
+ * <
+ * | NDIV = integer part of input bits to set the LDF
+ * |_FRACT = fractional part of input bits to set the LDF
+ * => PLLNDIV = integer part of (FVCO / (INFF*2))
+ * => PLLFRACIN = fractional part of(FVCO / INFF*2) * 2^16
+ * <=> PLLFRACIN = ((FVCO / (INFF*2)) - PLLNDIV) * 2^16
+ */
+ fvco = (unsigned long long)PLL_FVCO_MHZ * HZ_PER_MHZ;
+
+ ndiv = fvco;
+ do_div(ndiv, (clk_rate * 2));
+ pll_params->ndiv = (u8)ndiv;
+
+ frac = fvco * (1 << 16);
+ do_div(frac, (clk_rate * 2));
+ frac = frac - (ndiv * (1 << 16));
+ pll_params->frac = (u16)frac;
+}
+
+static int stm32_usbphyc_pll_init(struct stm32_usbphyc *usbphyc)
+{
+ struct pll_params pll_params;
+ u32 clk_rate = clk_get_rate(usbphyc->clk);
+ u32 ndiv, frac;
+ u32 usbphyc_pll;
+
+ if ((clk_rate < PLL_INFF_MIN_RATE_HZ) ||
+ (clk_rate > PLL_INFF_MAX_RATE_HZ)) {
+ dev_err(usbphyc->dev, "input clk freq (%dHz) out of range\n",
+ clk_rate);
+ return -EINVAL;
+ }
+
+ stm32_usbphyc_get_pll_params(clk_rate, &pll_params);
+ ndiv = FIELD_PREP(PLLNDIV, pll_params.ndiv);
+ frac = FIELD_PREP(PLLFRACIN, pll_params.frac);
+
+ usbphyc_pll = PLLDITHEN1 | PLLDITHEN0 | PLLSTRBYP | ndiv;
+
+ if (pll_params.frac)
+ usbphyc_pll |= PLLFRACCTL | frac;
+
+ writel_relaxed(usbphyc_pll, usbphyc->base + STM32_USBPHYC_PLL);
+
+ dev_dbg(usbphyc->dev, "input clk freq=%dHz, ndiv=%lu, frac=%lu\n",
+ clk_rate, FIELD_GET(PLLNDIV, usbphyc_pll),
+ FIELD_GET(PLLFRACIN, usbphyc_pll));
+
+ return 0;
+}
+
+static bool stm32_usbphyc_has_one_phy_active(struct stm32_usbphyc *usbphyc)
+{
+ int i;
+
+ for (i = 0; i < usbphyc->nphys; i++)
+ if (usbphyc->phys[i]->active)
+ return true;
+
+ return false;
+}
+
+static int stm32_usbphyc_pll_enable(struct stm32_usbphyc *usbphyc)
+{
+ void __iomem *pll_reg = usbphyc->base + STM32_USBPHYC_PLL;
+ bool pllen = (readl_relaxed(pll_reg) & PLLEN);
+ int ret;
+
+ /* Check if one phy port has already configured the pll */
+ if (pllen && stm32_usbphyc_has_one_phy_active(usbphyc))
+ return 0;
+
+ if (pllen) {
+ stm32_usbphyc_clr_bits(pll_reg, PLLEN);
+ /* Wait for minimum width of powerdown pulse (ENABLE = Low) */
+ udelay(PLL_PWR_DOWN_TIME_US);
+ }
+
+ ret = stm32_usbphyc_pll_init(usbphyc);
+ if (ret)
+ return ret;
+
+ stm32_usbphyc_set_bits(pll_reg, PLLEN);
+
+ /* Wait for maximum lock time */
+ udelay(PLL_LOCK_TIME_US);
+
+ if (!(readl_relaxed(pll_reg) & PLLEN)) {
+ dev_err(usbphyc->dev, "PLLEN not set\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int stm32_usbphyc_pll_disable(struct stm32_usbphyc *usbphyc)
+{
+ void __iomem *pll_reg = usbphyc->base + STM32_USBPHYC_PLL;
+
+ /* Check if other phy port active */
+ if (stm32_usbphyc_has_one_phy_active(usbphyc))
+ return 0;
+
+ stm32_usbphyc_clr_bits(pll_reg, PLLEN);
+ /* Wait for minimum width of powerdown pulse (ENABLE = Low) */
+ udelay(PLL_PWR_DOWN_TIME_US);
+
+ if (readl_relaxed(pll_reg) & PLLEN) {
+ dev_err(usbphyc->dev, "PLL not reset\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int stm32_usbphyc_phy_init(struct phy *phy)
+{
+ struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy);
+ struct stm32_usbphyc *usbphyc = usbphyc_phy->usbphyc;
+ int ret;
+
+ ret = stm32_usbphyc_pll_enable(usbphyc);
+ if (ret)
+ return ret;
+
+ usbphyc_phy->active = true;
+
+ return 0;
+}
+
+static int stm32_usbphyc_phy_exit(struct phy *phy)
+{
+ struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy);
+ struct stm32_usbphyc *usbphyc = usbphyc_phy->usbphyc;
+
+ usbphyc_phy->active = false;
+
+ return stm32_usbphyc_pll_disable(usbphyc);
+}
+
+static int stm32_usbphyc_phy_power_on(struct phy *phy)
+{
+ struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy);
+
+ return regulator_bulk_enable(NUM_SUPPLIES, usbphyc_phy->supplies);
+}
+
+static int stm32_usbphyc_phy_power_off(struct phy *phy)
+{
+ struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy);
+
+ return regulator_bulk_disable(NUM_SUPPLIES, usbphyc_phy->supplies);
+}
+
+static const struct phy_ops stm32_usbphyc_phy_ops = {
+ .init = stm32_usbphyc_phy_init,
+ .exit = stm32_usbphyc_phy_exit,
+ .power_on = stm32_usbphyc_phy_power_on,
+ .power_off = stm32_usbphyc_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static void stm32_usbphyc_switch_setup(struct stm32_usbphyc *usbphyc,
+ u32 utmi_switch)
+{
+ if (!utmi_switch)
+ stm32_usbphyc_clr_bits(usbphyc->base + STM32_USBPHYC_MISC,
+ SWITHOST);
+ else
+ stm32_usbphyc_set_bits(usbphyc->base + STM32_USBPHYC_MISC,
+ SWITHOST);
+ usbphyc->switch_setup = utmi_switch;
+}
+
+static struct phy *stm32_usbphyc_of_xlate(struct device *dev,
+ struct of_phandle_args *args)
+{
+ struct stm32_usbphyc *usbphyc = dev_get_drvdata(dev);
+ struct stm32_usbphyc_phy *usbphyc_phy = NULL;
+ struct device_node *phynode = args->np;
+
+ int port = 0;
+
+ for (port = 0; port < usbphyc->nphys; port++) {
+ if (phynode == usbphyc->phys[port]->phy->dev.of_node) {
+ usbphyc_phy = usbphyc->phys[port];
+ break;
+ }
+ }
+ if (!usbphyc_phy) {
+ dev_err(dev, "failed to find phy\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (((usbphyc_phy->index == 0) && (args->args_count != 0)) ||
+ ((usbphyc_phy->index == 1) && (args->args_count != 1))) {
+ dev_err(dev, "invalid number of cells for phy port%d\n",
+ usbphyc_phy->index);
+ return ERR_PTR(-EINVAL);
+ }
+
+ /* Configure the UTMI switch for PHY port#2 */
+ if (usbphyc_phy->index == 1) {
+ if (usbphyc->switch_setup < 0) {
+ stm32_usbphyc_switch_setup(usbphyc, args->args[0]);
+ } else {
+ if (args->args[0] != usbphyc->switch_setup) {
+ dev_err(dev, "phy port1 already used\n");
+ return ERR_PTR(-EBUSY);
+ }
+ }
+ }
+
+ return usbphyc_phy->phy;
+}
+
+static int stm32_usbphyc_probe(struct platform_device *pdev)
+{
+ struct stm32_usbphyc *usbphyc;
+ struct device *dev = &pdev->dev;
+ struct device_node *child, *np = dev->of_node;
+ struct resource *res;
+ struct phy_provider *phy_provider;
+ u32 version;
+ int ret, port = 0;
+
+ usbphyc = devm_kzalloc(dev, sizeof(*usbphyc), GFP_KERNEL);
+ if (!usbphyc)
+ return -ENOMEM;
+ usbphyc->dev = dev;
+ dev_set_drvdata(dev, usbphyc);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ usbphyc->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(usbphyc->base))
+ return PTR_ERR(usbphyc->base);
+
+ usbphyc->clk = devm_clk_get(dev, 0);
+ if (IS_ERR(usbphyc->clk)) {
+ ret = PTR_ERR(usbphyc->clk);
+ dev_err(dev, "clk get failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(usbphyc->clk);
+ if (ret) {
+ dev_err(dev, "clk enable failed: %d\n", ret);
+ return ret;
+ }
+
+ usbphyc->rst = devm_reset_control_get(dev, 0);
+ if (!IS_ERR(usbphyc->rst)) {
+ reset_control_assert(usbphyc->rst);
+ udelay(2);
+ reset_control_deassert(usbphyc->rst);
+ }
+
+ usbphyc->switch_setup = -EINVAL;
+ usbphyc->nphys = of_get_child_count(np);
+ usbphyc->phys = devm_kcalloc(dev, usbphyc->nphys,
+ sizeof(*usbphyc->phys), GFP_KERNEL);
+ if (!usbphyc->phys) {
+ ret = -ENOMEM;
+ goto clk_disable;
+ }
+
+ for_each_child_of_node(np, child) {
+ struct stm32_usbphyc_phy *usbphyc_phy;
+ struct phy *phy;
+ u32 index;
+ int i;
+
+ phy = devm_phy_create(dev, child, &stm32_usbphyc_phy_ops);
+ if (IS_ERR(phy)) {
+ ret = PTR_ERR(phy);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev,
+ "failed to create phy%d: %d\n", i, ret);
+ goto put_child;
+ }
+
+ usbphyc_phy = devm_kzalloc(dev, sizeof(*usbphyc_phy),
+ GFP_KERNEL);
+ if (!usbphyc_phy) {
+ ret = -ENOMEM;
+ goto put_child;
+ }
+
+ for (i = 0; i < NUM_SUPPLIES; i++)
+ usbphyc_phy->supplies[i].supply = supplies_names[i];
+
+ ret = devm_regulator_bulk_get(&phy->dev, NUM_SUPPLIES,
+ usbphyc_phy->supplies);
+ if (ret) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(&phy->dev,
+ "failed to get regulators: %d\n", ret);
+ goto put_child;
+ }
+
+ ret = of_property_read_u32(child, "reg", &index);
+ if (ret || index > usbphyc->nphys) {
+ dev_err(&phy->dev, "invalid reg property: %d\n", ret);
+ goto put_child;
+ }
+
+ usbphyc->phys[port] = usbphyc_phy;
+ phy_set_bus_width(phy, 8);
+ phy_set_drvdata(phy, usbphyc_phy);
+
+ usbphyc->phys[port]->phy = phy;
+ usbphyc->phys[port]->usbphyc = usbphyc;
+ usbphyc->phys[port]->index = index;
+ usbphyc->phys[port]->active = false;
+
+ port++;
+ }
+
+ phy_provider = devm_of_phy_provider_register(dev,
+ stm32_usbphyc_of_xlate);
+ if (IS_ERR(phy_provider)) {
+ ret = PTR_ERR(phy_provider);
+ dev_err(dev, "failed to register phy provider: %d\n", ret);
+ goto clk_disable;
+ }
+
+ version = readl_relaxed(usbphyc->base + STM32_USBPHYC_VERSION);
+ dev_info(dev, "registered rev:%lu.%lu\n",
+ FIELD_GET(MAJREV, version), FIELD_GET(MINREV, version));
+
+ return 0;
+
+put_child:
+ of_node_put(child);
+clk_disable:
+ clk_disable_unprepare(usbphyc->clk);
+
+ return ret;
+}
+
+static int stm32_usbphyc_remove(struct platform_device *pdev)
+{
+ struct stm32_usbphyc *usbphyc = dev_get_drvdata(&pdev->dev);
+
+ clk_disable_unprepare(usbphyc->clk);
+
+ return 0;
+}
+
+static const struct of_device_id stm32_usbphyc_of_match[] = {
+ { .compatible = "st,stm32mp1-usbphyc", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, stm32_usbphyc_of_match);
+
+static struct platform_driver stm32_usbphyc_driver = {
+ .probe = stm32_usbphyc_probe,
+ .remove = stm32_usbphyc_remove,
+ .driver = {
+ .of_match_table = stm32_usbphyc_of_match,
+ .name = "stm32-usbphyc",
+ }
+};
+module_platform_driver(stm32_usbphyc_driver);
+
+MODULE_DESCRIPTION("STMicroelectronics STM32 USBPHYC driver");
+MODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/tegra/xusb-tegra210.c b/drivers/phy/tegra/xusb-tegra210.c
index 9d0689ebd28c..05bee32a3a4d 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -169,6 +169,7 @@
#define XUSB_PADCTL_UPHY_PLL_CTL2_CAL_EN (1 << 0)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL4 0x36c
+#define XUSB_PADCTL_UPHY_PLL_CTL4_XDIGCLK_EN (1 << 19)
#define XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_EN (1 << 15)
#define XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_SHIFT 12
#define XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_MASK 0x3
@@ -537,11 +538,8 @@ static int tegra210_sata_uphy_enable(struct tegra_xusb_padctl *padctl, bool usb)
value |= (XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_SATA_VAL <<
XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_SHIFT);
- /* XXX PLL0_XDIGCLK_EN */
- /*
- value &= ~(1 << 19);
+ value &= ~XUSB_PADCTL_UPHY_PLL_CTL4_XDIGCLK_EN;
padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL4);
- */
value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL1);
value &= ~((XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_MDIV_MASK <<
diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c
index 63e916d4d069..11aa5902a9ac 100644
--- a/drivers/phy/tegra/xusb.c
+++ b/drivers/phy/tegra/xusb.c
@@ -418,7 +418,7 @@ tegra_xusb_port_find_lane(struct tegra_xusb_port *port,
{
struct tegra_xusb_lane *lane, *match = ERR_PTR(-ENODEV);
- for (map = map; map->type; map++) {
+ for (; map->type; map++) {
if (port->index != map->port)
continue;
diff --git a/drivers/phy/ti/phy-da8xx-usb.c b/drivers/phy/ti/phy-da8xx-usb.c
index 1b82bff6330f..befb886ff121 100644
--- a/drivers/phy/ti/phy-da8xx-usb.c
+++ b/drivers/phy/ti/phy-da8xx-usb.c
@@ -20,6 +20,7 @@
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/phy/phy.h>
+#include <linux/platform_data/phy-da8xx-usb.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
@@ -145,6 +146,7 @@ static struct phy *da8xx_usb_phy_of_xlate(struct device *dev,
static int da8xx_usb_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ struct da8xx_usb_phy_platform_data *pdata = dev->platform_data;
struct device_node *node = dev->of_node;
struct da8xx_usb_phy *d_phy;
@@ -152,25 +154,25 @@ static int da8xx_usb_phy_probe(struct platform_device *pdev)
if (!d_phy)
return -ENOMEM;
- if (node)
+ if (pdata)
+ d_phy->regmap = pdata->cfgchip;
+ else
d_phy->regmap = syscon_regmap_lookup_by_compatible(
"ti,da830-cfgchip");
- else
- d_phy->regmap = syscon_regmap_lookup_by_pdevname("syscon");
if (IS_ERR(d_phy->regmap)) {
dev_err(dev, "Failed to get syscon\n");
return PTR_ERR(d_phy->regmap);
}
- d_phy->usb11_clk = devm_clk_get(dev, "usb11_phy");
+ d_phy->usb11_clk = devm_clk_get(dev, "usb1_clk48");
if (IS_ERR(d_phy->usb11_clk)) {
- dev_err(dev, "Failed to get usb11_phy clock\n");
+ dev_err(dev, "Failed to get usb1_clk48\n");
return PTR_ERR(d_phy->usb11_clk);
}
- d_phy->usb20_clk = devm_clk_get(dev, "usb20_phy");
+ d_phy->usb20_clk = devm_clk_get(dev, "usb0_clk48");
if (IS_ERR(d_phy->usb20_clk)) {
- dev_err(dev, "Failed to get usb20_phy clock\n");
+ dev_err(dev, "Failed to get usb0_clk48\n");
return PTR_ERR(d_phy->usb20_clk);
}
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 0f254b35c378..01fe8e0455a0 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -30,17 +30,6 @@ config DEBUG_PINCTRL
help
Say Y here to add some extra checks and diagnostics to PINCTRL calls.
-config PINCTRL_ADI2
- bool "ADI pin controller driver"
- depends on (BF54x || BF60x)
- depends on !GPIO_ADI
- select PINMUX
- select IRQ_DOMAIN
- help
- This is the pin controller and gpio driver for ADI BF54x, BF60x and
- future processors. This option is selected automatically when specific
- machine and arch are selected to build.
-
config PINCTRL_ARTPEC6
bool "Axis ARTPEC-6 pin controller driver"
depends on MACH_ARTPEC6
@@ -77,14 +66,6 @@ config PINCTRL_AXP209
selected.
Say yes to enable pinctrl and GPIO support for the AXP209 PMIC
-config PINCTRL_BF54x
- def_bool y if BF54x
- select PINCTRL_ADI2
-
-config PINCTRL_BF60x
- def_bool y if BF60x
- select PINCTRL_ADI2
-
config PINCTRL_AT91
bool "AT91 pinctrl driver"
depends on OF
@@ -263,18 +244,6 @@ config PINCTRL_ST
select PINCONF
select GPIOLIB_IRQCHIP
-config PINCTRL_TZ1090
- bool "Toumaz Xenif TZ1090 pin control driver"
- depends on SOC_TZ1090
- select PINMUX
- select GENERIC_PINCONF
-
-config PINCTRL_TZ1090_PDC
- bool "Toumaz Xenif TZ1090 PDC pin control driver"
- depends on SOC_TZ1090
- select PINMUX
- select PINCONF
-
config PINCTRL_U300
bool "U300 pin controller driver"
depends on ARCH_U300
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index d3692633e9ed..657332b121fb 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -8,12 +8,9 @@ obj-$(CONFIG_PINMUX) += pinmux.o
obj-$(CONFIG_PINCONF) += pinconf.o
obj-$(CONFIG_OF) += devicetree.o
obj-$(CONFIG_GENERIC_PINCONF) += pinconf-generic.o
-obj-$(CONFIG_PINCTRL_ADI2) += pinctrl-adi2.o
obj-$(CONFIG_PINCTRL_ARTPEC6) += pinctrl-artpec6.o
obj-$(CONFIG_PINCTRL_AS3722) += pinctrl-as3722.o
obj-$(CONFIG_PINCTRL_AXP209) += pinctrl-axp209.o
-obj-$(CONFIG_PINCTRL_BF54x) += pinctrl-adi2-bf54x.o
-obj-$(CONFIG_PINCTRL_BF60x) += pinctrl-adi2-bf60x.o
obj-$(CONFIG_PINCTRL_AT91) += pinctrl-at91.o
obj-$(CONFIG_PINCTRL_AT91PIO4) += pinctrl-at91-pio4.o
obj-$(CONFIG_PINCTRL_AMD) += pinctrl-amd.o
@@ -34,8 +31,6 @@ obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o
obj-$(CONFIG_PINCTRL_SIRF) += sirf/
obj-$(CONFIG_PINCTRL_SX150X) += pinctrl-sx150x.o
obj-$(CONFIG_ARCH_TEGRA) += tegra/
-obj-$(CONFIG_PINCTRL_TZ1090) += pinctrl-tz1090.o
-obj-$(CONFIG_PINCTRL_TZ1090_PDC) += pinctrl-tz1090-pdc.o
obj-$(CONFIG_PINCTRL_U300) += pinctrl-u300.o
obj-$(CONFIG_PINCTRL_COH901) += pinctrl-coh901.o
obj-$(CONFIG_PINCTRL_XWAY) += pinctrl-xway.o
diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c
index 2c0dbfcff3e6..e5a303002021 100644
--- a/drivers/pinctrl/core.c
+++ b/drivers/pinctrl/core.c
@@ -1416,6 +1416,7 @@ int pinctrl_register_mappings(const struct pinctrl_map *maps,
{
return pinctrl_register_map(maps, num_maps, true);
}
+EXPORT_SYMBOL_GPL(pinctrl_register_mappings);
void pinctrl_unregister_map(const struct pinctrl_map *map)
{
@@ -1586,6 +1587,7 @@ static int pinctrl_pins_show(struct seq_file *s, void *what)
return 0;
}
+DEFINE_SHOW_ATTRIBUTE(pinctrl_pins);
static int pinctrl_groups_show(struct seq_file *s, void *what)
{
@@ -1631,6 +1633,7 @@ static int pinctrl_groups_show(struct seq_file *s, void *what)
return 0;
}
+DEFINE_SHOW_ATTRIBUTE(pinctrl_groups);
static int pinctrl_gpioranges_show(struct seq_file *s, void *what)
{
@@ -1664,6 +1667,7 @@ static int pinctrl_gpioranges_show(struct seq_file *s, void *what)
return 0;
}
+DEFINE_SHOW_ATTRIBUTE(pinctrl_gpioranges);
static int pinctrl_devices_show(struct seq_file *s, void *what)
{
@@ -1690,6 +1694,7 @@ static int pinctrl_devices_show(struct seq_file *s, void *what)
return 0;
}
+DEFINE_SHOW_ATTRIBUTE(pinctrl_devices);
static inline const char *map_type(enum pinctrl_map_type type)
{
@@ -1743,6 +1748,7 @@ static int pinctrl_maps_show(struct seq_file *s, void *what)
return 0;
}
+DEFINE_SHOW_ATTRIBUTE(pinctrl_maps);
static int pinctrl_show(struct seq_file *s, void *what)
{
@@ -1788,87 +1794,30 @@ static int pinctrl_show(struct seq_file *s, void *what)
return 0;
}
-
-static int pinctrl_pins_open(struct inode *inode, struct file *file)
-{
- return single_open(file, pinctrl_pins_show, inode->i_private);
-}
-
-static int pinctrl_groups_open(struct inode *inode, struct file *file)
-{
- return single_open(file, pinctrl_groups_show, inode->i_private);
-}
-
-static int pinctrl_gpioranges_open(struct inode *inode, struct file *file)
-{
- return single_open(file, pinctrl_gpioranges_show, inode->i_private);
-}
-
-static int pinctrl_devices_open(struct inode *inode, struct file *file)
-{
- return single_open(file, pinctrl_devices_show, NULL);
-}
-
-static int pinctrl_maps_open(struct inode *inode, struct file *file)
-{
- return single_open(file, pinctrl_maps_show, NULL);
-}
-
-static int pinctrl_open(struct inode *inode, struct file *file)
-{
- return single_open(file, pinctrl_show, NULL);
-}
-
-static const struct file_operations pinctrl_pins_ops = {
- .open = pinctrl_pins_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static const struct file_operations pinctrl_groups_ops = {
- .open = pinctrl_groups_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static const struct file_operations pinctrl_gpioranges_ops = {
- .open = pinctrl_gpioranges_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static const struct file_operations pinctrl_devices_ops = {
- .open = pinctrl_devices_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static const struct file_operations pinctrl_maps_ops = {
- .open = pinctrl_maps_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static const struct file_operations pinctrl_ops = {
- .open = pinctrl_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(pinctrl);
static struct dentry *debugfs_root;
static void pinctrl_init_device_debugfs(struct pinctrl_dev *pctldev)
{
struct dentry *device_root;
+ const char *debugfs_name;
+
+ if (pctldev->desc->name &&
+ strcmp(dev_name(pctldev->dev), pctldev->desc->name)) {
+ debugfs_name = devm_kasprintf(pctldev->dev, GFP_KERNEL,
+ "%s-%s", dev_name(pctldev->dev),
+ pctldev->desc->name);
+ if (!debugfs_name) {
+ pr_warn("failed to determine debugfs dir name for %s\n",
+ dev_name(pctldev->dev));
+ return;
+ }
+ } else {
+ debugfs_name = dev_name(pctldev->dev);
+ }
- device_root = debugfs_create_dir(dev_name(pctldev->dev),
- debugfs_root);
+ device_root = debugfs_create_dir(debugfs_name, debugfs_root);
pctldev->device_root = device_root;
if (IS_ERR(device_root) || !device_root) {
@@ -1877,11 +1826,11 @@ static void pinctrl_init_device_debugfs(struct pinctrl_dev *pctldev)
return;
}
debugfs_create_file("pins", S_IFREG | S_IRUGO,
- device_root, pctldev, &pinctrl_pins_ops);
+ device_root, pctldev, &pinctrl_pins_fops);
debugfs_create_file("pingroups", S_IFREG | S_IRUGO,
- device_root, pctldev, &pinctrl_groups_ops);
+ device_root, pctldev, &pinctrl_groups_fops);
debugfs_create_file("gpio-ranges", S_IFREG | S_IRUGO,
- device_root, pctldev, &pinctrl_gpioranges_ops);
+ device_root, pctldev, &pinctrl_gpioranges_fops);
if (pctldev->desc->pmxops)
pinmux_init_device_debugfs(device_root, pctldev);
if (pctldev->desc->confops)
@@ -1903,11 +1852,11 @@ static void pinctrl_init_debugfs(void)
}
debugfs_create_file("pinctrl-devices", S_IFREG | S_IRUGO,
- debugfs_root, NULL, &pinctrl_devices_ops);
+ debugfs_root, NULL, &pinctrl_devices_fops);
debugfs_create_file("pinctrl-maps", S_IFREG | S_IRUGO,
- debugfs_root, NULL, &pinctrl_maps_ops);
+ debugfs_root, NULL, &pinctrl_maps_fops);
debugfs_create_file("pinctrl-handles", S_IFREG | S_IRUGO,
- debugfs_root, NULL, &pinctrl_ops);
+ debugfs_root, NULL, &pinctrl_fops);
}
#else /* CONFIG_DEBUG_FS */
diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c
index 1ff6c3573493..b601039d6c69 100644
--- a/drivers/pinctrl/devicetree.c
+++ b/drivers/pinctrl/devicetree.c
@@ -122,8 +122,10 @@ static int dt_to_map_one_config(struct pinctrl *p,
/* OK let's just assume this will appear later then */
return -EPROBE_DEFER;
}
- if (!pctldev)
- pctldev = get_pinctrl_dev_from_of_node(np_pctldev);
+ /* If we're creating a hog we can use the passed pctldev */
+ if (pctldev && (np_pctldev == p->dev->of_node))
+ break;
+ pctldev = get_pinctrl_dev_from_of_node(np_pctldev);
if (pctldev)
break;
/* Do not defer probing of hogs (circular loop) */
diff --git a/drivers/pinctrl/freescale/Kconfig b/drivers/pinctrl/freescale/Kconfig
index 4dbc576ae27c..0d8ba1ef5329 100644
--- a/drivers/pinctrl/freescale/Kconfig
+++ b/drivers/pinctrl/freescale/Kconfig
@@ -82,6 +82,13 @@ config PINCTRL_IMX6SL
help
Say Y here to enable the imx6sl pinctrl driver
+config PINCTRL_IMX6SLL
+ bool "IMX6SLL pinctrl driver"
+ depends on SOC_IMX6SLL
+ select PINCTRL_IMX
+ help
+ Say Y here to enable the imx6sll pinctrl driver
+
config PINCTRL_IMX6SX
bool "IMX6SX pinctrl driver"
depends on SOC_IMX6SX
diff --git a/drivers/pinctrl/freescale/Makefile b/drivers/pinctrl/freescale/Makefile
index 19bb9a55a567..368be8cfc9b1 100644
--- a/drivers/pinctrl/freescale/Makefile
+++ b/drivers/pinctrl/freescale/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_PINCTRL_IMX53) += pinctrl-imx53.o
obj-$(CONFIG_PINCTRL_IMX6Q) += pinctrl-imx6q.o
obj-$(CONFIG_PINCTRL_IMX6Q) += pinctrl-imx6dl.o
obj-$(CONFIG_PINCTRL_IMX6SL) += pinctrl-imx6sl.o
+obj-$(CONFIG_PINCTRL_IMX6SLL) += pinctrl-imx6sll.o
obj-$(CONFIG_PINCTRL_IMX6SX) += pinctrl-imx6sx.o
obj-$(CONFIG_PINCTRL_IMX6UL) += pinctrl-imx6ul.o
obj-$(CONFIG_PINCTRL_IMX7D) += pinctrl-imx7d.o
diff --git a/drivers/pinctrl/freescale/pinctrl-imx6sll.c b/drivers/pinctrl/freescale/pinctrl-imx6sll.c
new file mode 100644
index 000000000000..0fbea9cf536d
--- /dev/null
+++ b/drivers/pinctrl/freescale/pinctrl-imx6sll.c
@@ -0,0 +1,360 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
+ * Copyright 2017-2018 NXP.
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/pinctrl.h>
+
+#include "pinctrl-imx.h"
+
+enum imx6sll_pads {
+ MX6SLL_PAD_RESERVE0 = 0,
+ MX6SLL_PAD_RESERVE1 = 1,
+ MX6SLL_PAD_RESERVE2 = 2,
+ MX6SLL_PAD_RESERVE3 = 3,
+ MX6SLL_PAD_RESERVE4 = 4,
+ MX6SLL_PAD_WDOG_B = 5,
+ MX6SLL_PAD_REF_CLK_24M = 6,
+ MX6SLL_PAD_REF_CLK_32K = 7,
+ MX6SLL_PAD_PWM1 = 8,
+ MX6SLL_PAD_KEY_COL0 = 9,
+ MX6SLL_PAD_KEY_ROW0 = 10,
+ MX6SLL_PAD_KEY_COL1 = 11,
+ MX6SLL_PAD_KEY_ROW1 = 12,
+ MX6SLL_PAD_KEY_COL2 = 13,
+ MX6SLL_PAD_KEY_ROW2 = 14,
+ MX6SLL_PAD_KEY_COL3 = 15,
+ MX6SLL_PAD_KEY_ROW3 = 16,
+ MX6SLL_PAD_KEY_COL4 = 17,
+ MX6SLL_PAD_KEY_ROW4 = 18,
+ MX6SLL_PAD_KEY_COL5 = 19,
+ MX6SLL_PAD_KEY_ROW5 = 20,
+ MX6SLL_PAD_KEY_COL6 = 21,
+ MX6SLL_PAD_KEY_ROW6 = 22,
+ MX6SLL_PAD_KEY_COL7 = 23,
+ MX6SLL_PAD_KEY_ROW7 = 24,
+ MX6SLL_PAD_EPDC_DATA00 = 25,
+ MX6SLL_PAD_EPDC_DATA01 = 26,
+ MX6SLL_PAD_EPDC_DATA02 = 27,
+ MX6SLL_PAD_EPDC_DATA03 = 28,
+ MX6SLL_PAD_EPDC_DATA04 = 29,
+ MX6SLL_PAD_EPDC_DATA05 = 30,
+ MX6SLL_PAD_EPDC_DATA06 = 31,
+ MX6SLL_PAD_EPDC_DATA07 = 32,
+ MX6SLL_PAD_EPDC_DATA08 = 33,
+ MX6SLL_PAD_EPDC_DATA09 = 34,
+ MX6SLL_PAD_EPDC_DATA10 = 35,
+ MX6SLL_PAD_EPDC_DATA11 = 36,
+ MX6SLL_PAD_EPDC_DATA12 = 37,
+ MX6SLL_PAD_EPDC_DATA13 = 38,
+ MX6SLL_PAD_EPDC_DATA14 = 39,
+ MX6SLL_PAD_EPDC_DATA15 = 40,
+ MX6SLL_PAD_EPDC_SDCLK = 41,
+ MX6SLL_PAD_EPDC_SDLE = 42,
+ MX6SLL_PAD_EPDC_SDOE = 43,
+ MX6SLL_PAD_EPDC_SDSHR = 44,
+ MX6SLL_PAD_EPDC_SDCE0 = 45,
+ MX6SLL_PAD_EPDC_SDCE1 = 46,
+ MX6SLL_PAD_EPDC_SDCE2 = 47,
+ MX6SLL_PAD_EPDC_SDCE3 = 48,
+ MX6SLL_PAD_EPDC_GDCLK = 49,
+ MX6SLL_PAD_EPDC_GDOE = 50,
+ MX6SLL_PAD_EPDC_GDRL = 51,
+ MX6SLL_PAD_EPDC_GDSP = 52,
+ MX6SLL_PAD_EPDC_VCOM0 = 53,
+ MX6SLL_PAD_EPDC_VCOM1 = 54,
+ MX6SLL_PAD_EPDC_BDR0 = 55,
+ MX6SLL_PAD_EPDC_BDR1 = 56,
+ MX6SLL_PAD_EPDC_PWR_CTRL0 = 57,
+ MX6SLL_PAD_EPDC_PWR_CTRL1 = 58,
+ MX6SLL_PAD_EPDC_PWR_CTRL2 = 59,
+ MX6SLL_PAD_EPDC_PWR_CTRL3 = 60,
+ MX6SLL_PAD_EPDC_PWR_COM = 61,
+ MX6SLL_PAD_EPDC_PWR_INT = 62,
+ MX6SLL_PAD_EPDC_PWR_STAT = 63,
+ MX6SLL_PAD_EPDC_PWR_WAKE = 64,
+ MX6SLL_PAD_LCD_CLK = 65,
+ MX6SLL_PAD_LCD_ENABLE = 66,
+ MX6SLL_PAD_LCD_HSYNC = 67,
+ MX6SLL_PAD_LCD_VSYNC = 68,
+ MX6SLL_PAD_LCD_RESET = 69,
+ MX6SLL_PAD_LCD_DATA00 = 70,
+ MX6SLL_PAD_LCD_DATA01 = 71,
+ MX6SLL_PAD_LCD_DATA02 = 72,
+ MX6SLL_PAD_LCD_DATA03 = 73,
+ MX6SLL_PAD_LCD_DATA04 = 74,
+ MX6SLL_PAD_LCD_DATA05 = 75,
+ MX6SLL_PAD_LCD_DATA06 = 76,
+ MX6SLL_PAD_LCD_DATA07 = 77,
+ MX6SLL_PAD_LCD_DATA08 = 78,
+ MX6SLL_PAD_LCD_DATA09 = 79,
+ MX6SLL_PAD_LCD_DATA10 = 80,
+ MX6SLL_PAD_LCD_DATA11 = 81,
+ MX6SLL_PAD_LCD_DATA12 = 82,
+ MX6SLL_PAD_LCD_DATA13 = 83,
+ MX6SLL_PAD_LCD_DATA14 = 84,
+ MX6SLL_PAD_LCD_DATA15 = 85,
+ MX6SLL_PAD_LCD_DATA16 = 86,
+ MX6SLL_PAD_LCD_DATA17 = 87,
+ MX6SLL_PAD_LCD_DATA18 = 88,
+ MX6SLL_PAD_LCD_DATA19 = 89,
+ MX6SLL_PAD_LCD_DATA20 = 90,
+ MX6SLL_PAD_LCD_DATA21 = 91,
+ MX6SLL_PAD_LCD_DATA22 = 92,
+ MX6SLL_PAD_LCD_DATA23 = 93,
+ MX6SLL_PAD_AUD_RXFS = 94,
+ MX6SLL_PAD_AUD_RXC = 95,
+ MX6SLL_PAD_AUD_RXD = 96,
+ MX6SLL_PAD_AUD_TXC = 97,
+ MX6SLL_PAD_AUD_TXFS = 98,
+ MX6SLL_PAD_AUD_TXD = 99,
+ MX6SLL_PAD_AUD_MCLK = 100,
+ MX6SLL_PAD_UART1_RXD = 101,
+ MX6SLL_PAD_UART1_TXD = 102,
+ MX6SLL_PAD_I2C1_SCL = 103,
+ MX6SLL_PAD_I2C1_SDA = 104,
+ MX6SLL_PAD_I2C2_SCL = 105,
+ MX6SLL_PAD_I2C2_SDA = 106,
+ MX6SLL_PAD_ECSPI1_SCLK = 107,
+ MX6SLL_PAD_ECSPI1_MOSI = 108,
+ MX6SLL_PAD_ECSPI1_MISO = 109,
+ MX6SLL_PAD_ECSPI1_SS0 = 110,
+ MX6SLL_PAD_ECSPI2_SCLK = 111,
+ MX6SLL_PAD_ECSPI2_MOSI = 112,
+ MX6SLL_PAD_ECSPI2_MISO = 113,
+ MX6SLL_PAD_ECSPI2_SS0 = 114,
+ MX6SLL_PAD_SD1_CLK = 115,
+ MX6SLL_PAD_SD1_CMD = 116,
+ MX6SLL_PAD_SD1_DATA0 = 117,
+ MX6SLL_PAD_SD1_DATA1 = 118,
+ MX6SLL_PAD_SD1_DATA2 = 119,
+ MX6SLL_PAD_SD1_DATA3 = 120,
+ MX6SLL_PAD_SD1_DATA4 = 121,
+ MX6SLL_PAD_SD1_DATA5 = 122,
+ MX6SLL_PAD_SD1_DATA6 = 123,
+ MX6SLL_PAD_SD1_DATA7 = 124,
+ MX6SLL_PAD_SD2_RESET = 125,
+ MX6SLL_PAD_SD2_CLK = 126,
+ MX6SLL_PAD_SD2_CMD = 127,
+ MX6SLL_PAD_SD2_DATA0 = 128,
+ MX6SLL_PAD_SD2_DATA1 = 129,
+ MX6SLL_PAD_SD2_DATA2 = 130,
+ MX6SLL_PAD_SD2_DATA3 = 131,
+ MX6SLL_PAD_SD2_DATA4 = 132,
+ MX6SLL_PAD_SD2_DATA5 = 133,
+ MX6SLL_PAD_SD2_DATA6 = 134,
+ MX6SLL_PAD_SD2_DATA7 = 135,
+ MX6SLL_PAD_SD3_CLK = 136,
+ MX6SLL_PAD_SD3_CMD = 137,
+ MX6SLL_PAD_SD3_DATA0 = 138,
+ MX6SLL_PAD_SD3_DATA1 = 139,
+ MX6SLL_PAD_SD3_DATA2 = 140,
+ MX6SLL_PAD_SD3_DATA3 = 141,
+ MX6SLL_PAD_GPIO4_IO20 = 142,
+ MX6SLL_PAD_GPIO4_IO21 = 143,
+ MX6SLL_PAD_GPIO4_IO19 = 144,
+ MX6SLL_PAD_GPIO4_IO25 = 145,
+ MX6SLL_PAD_GPIO4_IO18 = 146,
+ MX6SLL_PAD_GPIO4_IO24 = 147,
+ MX6SLL_PAD_GPIO4_IO23 = 148,
+ MX6SLL_PAD_GPIO4_IO17 = 149,
+ MX6SLL_PAD_GPIO4_IO22 = 150,
+ MX6SLL_PAD_GPIO4_IO16 = 151,
+ MX6SLL_PAD_GPIO4_IO26 = 152,
+};
+
+/* Pad names for the pinmux subsystem */
+static const struct pinctrl_pin_desc imx6sll_pinctrl_pads[] = {
+ IMX_PINCTRL_PIN(MX6SLL_PAD_RESERVE0),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_RESERVE1),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_RESERVE2),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_RESERVE3),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_RESERVE4),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_WDOG_B),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_REF_CLK_24M),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_REF_CLK_32K),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_PWM1),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_KEY_COL0),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_KEY_ROW0),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_KEY_COL1),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_KEY_ROW1),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_KEY_COL2),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_KEY_ROW2),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_KEY_COL3),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_KEY_ROW3),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_KEY_COL4),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_KEY_ROW4),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_KEY_COL5),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_KEY_ROW5),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_KEY_COL6),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_KEY_ROW6),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_KEY_COL7),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_KEY_ROW7),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_DATA00),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_DATA01),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_DATA02),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_DATA03),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_DATA04),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_DATA05),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_DATA06),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_DATA07),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_DATA08),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_DATA09),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_DATA10),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_DATA11),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_DATA12),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_DATA13),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_DATA14),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_DATA15),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_SDCLK),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_SDLE),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_SDOE),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_SDSHR),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_SDCE0),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_SDCE1),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_SDCE2),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_SDCE3),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_GDCLK),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_GDOE),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_GDRL),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_GDSP),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_VCOM0),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_VCOM1),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_BDR0),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_BDR1),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_PWR_CTRL0),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_PWR_CTRL1),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_PWR_CTRL2),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_PWR_CTRL3),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_PWR_COM),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_PWR_INT),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_PWR_STAT),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_EPDC_PWR_WAKE),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_LCD_CLK),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_LCD_ENABLE),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_LCD_HSYNC),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_LCD_VSYNC),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_LCD_RESET),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_LCD_DATA00),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_LCD_DATA01),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_LCD_DATA02),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_LCD_DATA03),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_LCD_DATA04),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_LCD_DATA05),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_LCD_DATA06),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_LCD_DATA07),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_LCD_DATA08),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_LCD_DATA09),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_LCD_DATA10),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_LCD_DATA11),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_LCD_DATA12),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_LCD_DATA13),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_LCD_DATA14),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_LCD_DATA15),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_LCD_DATA16),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_LCD_DATA17),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_LCD_DATA18),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_LCD_DATA19),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_LCD_DATA20),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_LCD_DATA21),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_LCD_DATA22),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_LCD_DATA23),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_AUD_RXFS),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_AUD_RXC),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_AUD_RXD),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_AUD_TXC),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_AUD_TXFS),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_AUD_TXD),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_AUD_MCLK),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_UART1_RXD),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_UART1_TXD),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_I2C1_SCL),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_I2C1_SDA),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_I2C2_SCL),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_I2C2_SDA),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_ECSPI1_SCLK),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_ECSPI1_MOSI),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_ECSPI1_MISO),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_ECSPI1_SS0),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_ECSPI2_SCLK),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_ECSPI2_MOSI),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_ECSPI2_MISO),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_ECSPI2_SS0),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_SD1_CLK),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_SD1_CMD),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_SD1_DATA0),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_SD1_DATA1),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_SD1_DATA2),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_SD1_DATA3),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_SD1_DATA4),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_SD1_DATA5),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_SD1_DATA6),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_SD1_DATA7),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_SD2_RESET),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_SD2_CLK),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_SD2_CMD),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_SD2_DATA0),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_SD2_DATA1),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_SD2_DATA2),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_SD2_DATA3),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_SD2_DATA4),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_SD2_DATA5),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_SD2_DATA6),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_SD2_DATA7),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_SD3_CLK),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_SD3_CMD),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_SD3_DATA0),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_SD3_DATA1),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_SD3_DATA2),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_SD3_DATA3),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_GPIO4_IO20),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_GPIO4_IO21),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_GPIO4_IO19),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_GPIO4_IO25),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_GPIO4_IO18),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_GPIO4_IO24),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_GPIO4_IO23),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_GPIO4_IO17),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_GPIO4_IO22),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_GPIO4_IO16),
+ IMX_PINCTRL_PIN(MX6SLL_PAD_GPIO4_IO26),
+};
+
+static const struct imx_pinctrl_soc_info imx6sll_pinctrl_info = {
+ .pins = imx6sll_pinctrl_pads,
+ .npins = ARRAY_SIZE(imx6sll_pinctrl_pads),
+ .gpr_compatible = "fsl,imx6sll-iomuxc-gpr",
+};
+
+static const struct of_device_id imx6sll_pinctrl_of_match[] = {
+ { .compatible = "fsl,imx6sll-iomuxc", .data = &imx6sll_pinctrl_info, },
+ { /* sentinel */ }
+};
+
+static int imx6sll_pinctrl_probe(struct platform_device *pdev)
+{
+ return imx_pinctrl_probe(pdev, &imx6sll_pinctrl_info);
+}
+
+static struct platform_driver imx6sll_pinctrl_driver = {
+ .driver = {
+ .name = "imx6sll-pinctrl",
+ .of_match_table = of_match_ptr(imx6sll_pinctrl_of_match),
+ .suppress_bind_attrs = true,
+ },
+ .probe = imx6sll_pinctrl_probe,
+};
+
+static int __init imx6sll_pinctrl_init(void)
+{
+ return platform_driver_register(&imx6sll_pinctrl_driver);
+}
+arch_initcall(imx6sll_pinctrl_init);
diff --git a/drivers/pinctrl/intel/pinctrl-intel.c b/drivers/pinctrl/intel/pinctrl-intel.c
index 96e73e30204e..1e24a6b8a64e 100644
--- a/drivers/pinctrl/intel/pinctrl-intel.c
+++ b/drivers/pinctrl/intel/pinctrl-intel.c
@@ -788,6 +788,24 @@ static void intel_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
raw_spin_unlock_irqrestore(&pctrl->lock, flags);
}
+static int intel_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
+{
+ struct intel_pinctrl *pctrl = gpiochip_get_data(chip);
+ void __iomem *reg;
+ u32 padcfg0;
+
+ reg = intel_get_padcfg(pctrl, offset, PADCFG0);
+ if (!reg)
+ return -EINVAL;
+
+ padcfg0 = readl(reg);
+
+ if (padcfg0 & PADCFG0_PMODE_MASK)
+ return -EINVAL;
+
+ return !!(padcfg0 & PADCFG0_GPIOTXDIS);
+}
+
static int intel_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
{
return pinctrl_gpio_direction_input(chip->base + offset);
@@ -804,6 +822,7 @@ static const struct gpio_chip intel_gpio_chip = {
.owner = THIS_MODULE,
.request = gpiochip_generic_request,
.free = gpiochip_generic_free,
+ .get_direction = intel_gpio_get_direction,
.direction_input = intel_gpio_direction_input,
.direction_output = intel_gpio_direction_output,
.get = intel_gpio_get,
diff --git a/drivers/pinctrl/mediatek/Kconfig b/drivers/pinctrl/mediatek/Kconfig
index 3e598740b379..862c5dbc6977 100644
--- a/drivers/pinctrl/mediatek/Kconfig
+++ b/drivers/pinctrl/mediatek/Kconfig
@@ -32,6 +32,13 @@ config PINCTRL_MT8127
select PINCTRL_MTK
# For ARMv8 SoCs
+config PINCTRL_MT2712
+ bool "MediaTek MT2712 pin control"
+ depends on OF
+ depends on ARM64 || COMPILE_TEST
+ default ARM64 && ARCH_MEDIATEK
+ select PINCTRL_MTK
+
config PINCTRL_MT7622
bool "MediaTek MT7622 pin control"
depends on OF
diff --git a/drivers/pinctrl/mediatek/Makefile b/drivers/pinctrl/mediatek/Makefile
index ed7d2b2cc6e9..7959e773533f 100644
--- a/drivers/pinctrl/mediatek/Makefile
+++ b/drivers/pinctrl/mediatek/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_PINCTRL_MTK) += pinctrl-mtk-common.o
# SoC Drivers
obj-$(CONFIG_PINCTRL_MT2701) += pinctrl-mt2701.o
+obj-$(CONFIG_PINCTRL_MT2712) += pinctrl-mt2712.o
obj-$(CONFIG_PINCTRL_MT8135) += pinctrl-mt8135.o
obj-$(CONFIG_PINCTRL_MT8127) += pinctrl-mt8127.o
obj-$(CONFIG_PINCTRL_MT7622) += pinctrl-mt7622.o
diff --git a/drivers/pinctrl/mediatek/pinctrl-mt2712.c b/drivers/pinctrl/mediatek/pinctrl-mt2712.c
new file mode 100644
index 000000000000..81e11f9e70f1
--- /dev/null
+++ b/drivers/pinctrl/mediatek/pinctrl-mt2712.c
@@ -0,0 +1,633 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Zhiyong Tao <zhiyong.tao@mediatek.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/regmap.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <dt-bindings/pinctrl/mt65xx.h>
+
+#include "pinctrl-mtk-common.h"
+#include "pinctrl-mtk-mt2712.h"
+
+static const struct mtk_pin_spec_pupd_set_samereg mt2712_spec_pupd[] = {
+ MTK_PIN_PUPD_SPEC_SR(18, 0xe50, 2, 1, 0),
+ MTK_PIN_PUPD_SPEC_SR(19, 0xe60, 12, 11, 10),
+ MTK_PIN_PUPD_SPEC_SR(20, 0xe50, 5, 4, 3),
+ MTK_PIN_PUPD_SPEC_SR(21, 0xe60, 15, 14, 13),
+ MTK_PIN_PUPD_SPEC_SR(22, 0xe50, 8, 7, 6),
+ MTK_PIN_PUPD_SPEC_SR(23, 0xe70, 2, 1, 0),
+
+ MTK_PIN_PUPD_SPEC_SR(30, 0xf30, 2, 1, 0),
+ MTK_PIN_PUPD_SPEC_SR(31, 0xf30, 6, 5, 4),
+ MTK_PIN_PUPD_SPEC_SR(32, 0xf30, 10, 9, 8),
+ MTK_PIN_PUPD_SPEC_SR(33, 0xf30, 14, 13, 12),
+ MTK_PIN_PUPD_SPEC_SR(34, 0xf40, 2, 1, 0),
+ MTK_PIN_PUPD_SPEC_SR(35, 0xf40, 6, 5, 4),
+ MTK_PIN_PUPD_SPEC_SR(36, 0xf40, 10, 9, 8),
+ MTK_PIN_PUPD_SPEC_SR(37, 0xc40, 2, 1, 0),
+ MTK_PIN_PUPD_SPEC_SR(38, 0xc60, 2, 1, 0),
+ MTK_PIN_PUPD_SPEC_SR(39, 0xc60, 2, 1, 0),
+ MTK_PIN_PUPD_SPEC_SR(40, 0xc60, 2, 1, 0),
+ MTK_PIN_PUPD_SPEC_SR(41, 0xc60, 2, 1, 0),
+ MTK_PIN_PUPD_SPEC_SR(42, 0xc60, 2, 1, 0),
+ MTK_PIN_PUPD_SPEC_SR(43, 0xc60, 2, 1, 0),
+ MTK_PIN_PUPD_SPEC_SR(44, 0xc60, 2, 1, 0),
+ MTK_PIN_PUPD_SPEC_SR(45, 0xc60, 2, 1, 0),
+ MTK_PIN_PUPD_SPEC_SR(46, 0xc50, 2, 1, 0),
+ MTK_PIN_PUPD_SPEC_SR(47, 0xda0, 2, 1, 0),
+ MTK_PIN_PUPD_SPEC_SR(48, 0xd90, 2, 1, 0),
+ MTK_PIN_PUPD_SPEC_SR(49, 0xdf0, 14, 13, 12),
+ MTK_PIN_PUPD_SPEC_SR(50, 0xdf0, 10, 9, 8),
+ MTK_PIN_PUPD_SPEC_SR(51, 0xdf0, 6, 5, 4),
+ MTK_PIN_PUPD_SPEC_SR(52, 0xdf0, 2, 1, 0),
+ MTK_PIN_PUPD_SPEC_SR(53, 0xd50, 2, 1, 0),
+ MTK_PIN_PUPD_SPEC_SR(54, 0xd80, 2, 1, 0),
+ MTK_PIN_PUPD_SPEC_SR(55, 0xe00, 2, 1, 0),
+ MTK_PIN_PUPD_SPEC_SR(56, 0xd40, 2, 1, 0),
+
+ MTK_PIN_PUPD_SPEC_SR(63, 0xc80, 2, 1, 0),
+ MTK_PIN_PUPD_SPEC_SR(64, 0xdb0, 14, 13, 12),
+ MTK_PIN_PUPD_SPEC_SR(65, 0xdb0, 6, 5, 4),
+ MTK_PIN_PUPD_SPEC_SR(66, 0xdb0, 10, 9, 8),
+ MTK_PIN_PUPD_SPEC_SR(67, 0xcd0, 2, 1, 0),
+ MTK_PIN_PUPD_SPEC_SR(68, 0xdb0, 2, 1, 0),
+ MTK_PIN_PUPD_SPEC_SR(69, 0xc90, 2, 1, 0),
+ MTK_PIN_PUPD_SPEC_SR(70, 0xcc0, 2, 1, 0),
+
+ MTK_PIN_PUPD_SPEC_SR(89, 0xce0, 2, 1, 0),
+ MTK_PIN_PUPD_SPEC_SR(90, 0xdd0, 14, 13, 12),
+ MTK_PIN_PUPD_SPEC_SR(91, 0xdd0, 10, 9, 8),
+ MTK_PIN_PUPD_SPEC_SR(92, 0xdd0, 6, 5, 4),
+ MTK_PIN_PUPD_SPEC_SR(93, 0xdd0, 2, 1, 0),
+ MTK_PIN_PUPD_SPEC_SR(94, 0xd20, 2, 1, 0),
+ MTK_PIN_PUPD_SPEC_SR(95, 0xcf0, 2, 1, 0),
+ MTK_PIN_PUPD_SPEC_SR(96, 0xd30, 2, 1, 0),
+
+ MTK_PIN_PUPD_SPEC_SR(135, 0xe50, 11, 10, 9),
+ MTK_PIN_PUPD_SPEC_SR(136, 0xe50, 14, 13, 12),
+ MTK_PIN_PUPD_SPEC_SR(137, 0xe70, 5, 4, 3),
+ MTK_PIN_PUPD_SPEC_SR(138, 0xe70, 8, 7, 6),
+ MTK_PIN_PUPD_SPEC_SR(139, 0xe70, 11, 10, 9),
+ MTK_PIN_PUPD_SPEC_SR(140, 0xe70, 14, 13, 12),
+ MTK_PIN_PUPD_SPEC_SR(141, 0xe60, 2, 1, 0),
+ MTK_PIN_PUPD_SPEC_SR(142, 0xe60, 5, 4, 3)
+};
+
+static int mt2712_spec_pull_set(struct regmap *regmap,
+ unsigned int pin,
+ unsigned char align,
+ bool isup,
+ unsigned int r1r0)
+{
+ return mtk_pctrl_spec_pull_set_samereg(regmap, mt2712_spec_pupd,
+ ARRAY_SIZE(mt2712_spec_pupd), pin, align, isup, r1r0);
+}
+
+static const struct mtk_pin_ies_smt_set mt2712_smt_set[] = {
+ MTK_PIN_IES_SMT_SPEC(0, 3, 0x900, 2),
+ MTK_PIN_IES_SMT_SPEC(4, 7, 0x900, 0),
+ MTK_PIN_IES_SMT_SPEC(8, 11, 0x900, 1),
+ MTK_PIN_IES_SMT_SPEC(12, 12, 0x8d0, 6),
+ MTK_PIN_IES_SMT_SPEC(13, 13, 0x8d0, 7),
+ MTK_PIN_IES_SMT_SPEC(14, 14, 0x8d0, 6),
+ MTK_PIN_IES_SMT_SPEC(15, 15, 0x8d0, 7),
+ MTK_PIN_IES_SMT_SPEC(18, 23, 0x8d0, 1),
+ MTK_PIN_IES_SMT_SPEC(24, 25, 0x8d0, 2),
+ MTK_PIN_IES_SMT_SPEC(26, 26, 0x8d0, 3),
+ MTK_PIN_IES_SMT_SPEC(27, 27, 0x8d0, 4),
+ MTK_PIN_IES_SMT_SPEC(28, 29, 0x8d0, 3),
+ MTK_PIN_IES_SMT_SPEC(30, 36, 0xf50, 13),
+ MTK_PIN_IES_SMT_SPEC(37, 37, 0xc40, 13),
+ MTK_PIN_IES_SMT_SPEC(38, 45, 0xc60, 13),
+ MTK_PIN_IES_SMT_SPEC(46, 46, 0xc50, 13),
+ MTK_PIN_IES_SMT_SPEC(47, 47, 0xda0, 13),
+ MTK_PIN_IES_SMT_SPEC(48, 48, 0xd90, 13),
+ MTK_PIN_IES_SMT_SPEC(49, 52, 0xd60, 13),
+ MTK_PIN_IES_SMT_SPEC(53, 53, 0xd50, 13),
+ MTK_PIN_IES_SMT_SPEC(54, 54, 0xd80, 13),
+ MTK_PIN_IES_SMT_SPEC(55, 55, 0xe00, 13),
+ MTK_PIN_IES_SMT_SPEC(56, 56, 0xd40, 13),
+ MTK_PIN_IES_SMT_SPEC(57, 62, 0x900, 3),
+ MTK_PIN_IES_SMT_SPEC(63, 63, 0xc80, 13),
+ MTK_PIN_IES_SMT_SPEC(64, 66, 0xca0, 13),
+ MTK_PIN_IES_SMT_SPEC(67, 67, 0xc80, 13),
+ MTK_PIN_IES_SMT_SPEC(68, 68, 0xca0, 13),
+ MTK_PIN_IES_SMT_SPEC(69, 69, 0xc90, 13),
+ MTK_PIN_IES_SMT_SPEC(70, 70, 0xc80, 13),
+ MTK_PIN_IES_SMT_SPEC(71, 74, 0x8d0, 8),
+ MTK_PIN_IES_SMT_SPEC(75, 77, 0x8d0, 9),
+ MTK_PIN_IES_SMT_SPEC(78, 81, 0x8d0, 10),
+ MTK_PIN_IES_SMT_SPEC(82, 88, 0x8d0, 9),
+ MTK_PIN_IES_SMT_SPEC(89, 89, 0xce0, 13),
+ MTK_PIN_IES_SMT_SPEC(90, 93, 0xd00, 13),
+ MTK_PIN_IES_SMT_SPEC(94, 94, 0xce0, 13),
+ MTK_PIN_IES_SMT_SPEC(95, 96, 0xcf0, 13),
+ MTK_PIN_IES_SMT_SPEC(97, 100, 0x8d0, 11),
+ MTK_PIN_IES_SMT_SPEC(101, 104, 0x8d0, 12),
+ MTK_PIN_IES_SMT_SPEC(105, 105, 0x8d0, 13),
+ MTK_PIN_IES_SMT_SPEC(106, 106, 0x8d0, 14),
+ MTK_PIN_IES_SMT_SPEC(107, 107, 0x8d0, 15),
+ MTK_PIN_IES_SMT_SPEC(108, 108, 0x8e0, 0),
+ MTK_PIN_IES_SMT_SPEC(109, 109, 0x8e0, 1),
+ MTK_PIN_IES_SMT_SPEC(110, 110, 0x8e0, 2),
+ MTK_PIN_IES_SMT_SPEC(111, 111, 0x8d0, 13),
+ MTK_PIN_IES_SMT_SPEC(112, 112, 0x8d0, 14),
+ MTK_PIN_IES_SMT_SPEC(113, 113, 0x8d0, 15),
+ MTK_PIN_IES_SMT_SPEC(114, 114, 0x8e0, 0),
+ MTK_PIN_IES_SMT_SPEC(115, 115, 0x8e0, 1),
+ MTK_PIN_IES_SMT_SPEC(116, 116, 0x8e0, 2),
+ MTK_PIN_IES_SMT_SPEC(117, 117, 0x8e0, 3),
+ MTK_PIN_IES_SMT_SPEC(118, 118, 0x8e0, 4),
+ MTK_PIN_IES_SMT_SPEC(119, 119, 0x8e0, 5),
+ MTK_PIN_IES_SMT_SPEC(120, 120, 0x8e0, 3),
+ MTK_PIN_IES_SMT_SPEC(121, 121, 0x8e0, 4),
+ MTK_PIN_IES_SMT_SPEC(122, 122, 0x8e0, 5),
+ MTK_PIN_IES_SMT_SPEC(123, 126, 0x8e0, 6),
+ MTK_PIN_IES_SMT_SPEC(127, 130, 0x8e0, 7),
+ MTK_PIN_IES_SMT_SPEC(131, 134, 0x8e0, 8),
+ MTK_PIN_IES_SMT_SPEC(135, 142, 0x8d0, 1),
+ MTK_PIN_IES_SMT_SPEC(143, 147, 0x8e0, 9),
+ MTK_PIN_IES_SMT_SPEC(148, 152, 0x8e0, 10),
+ MTK_PIN_IES_SMT_SPEC(153, 156, 0x8e0, 11),
+ MTK_PIN_IES_SMT_SPEC(157, 160, 0x8e0, 12),
+ MTK_PIN_IES_SMT_SPEC(161, 164, 0x8e0, 13),
+ MTK_PIN_IES_SMT_SPEC(165, 168, 0x8e0, 14),
+ MTK_PIN_IES_SMT_SPEC(169, 170, 0x8e0, 15),
+ MTK_PIN_IES_SMT_SPEC(171, 172, 0x8f0, 0),
+ MTK_PIN_IES_SMT_SPEC(173, 173, 0x8f0, 1),
+ MTK_PIN_IES_SMT_SPEC(174, 175, 0x8f0, 2),
+ MTK_PIN_IES_SMT_SPEC(176, 176, 0x8f0, 1),
+ MTK_PIN_IES_SMT_SPEC(177, 177, 0x8f0, 3),
+ MTK_PIN_IES_SMT_SPEC(178, 178, 0x8f0, 4),
+ MTK_PIN_IES_SMT_SPEC(179, 179, 0x8f0, 3),
+ MTK_PIN_IES_SMT_SPEC(180, 180, 0x8f0, 4),
+ MTK_PIN_IES_SMT_SPEC(181, 181, 0x8f0, 5),
+ MTK_PIN_IES_SMT_SPEC(182, 182, 0x8f0, 6),
+ MTK_PIN_IES_SMT_SPEC(183, 183, 0x8f0, 5),
+ MTK_PIN_IES_SMT_SPEC(184, 184, 0x8f0, 6),
+ MTK_PIN_IES_SMT_SPEC(185, 186, 0x8f0, 7),
+ MTK_PIN_IES_SMT_SPEC(187, 187, 0x8f0, 8),
+ MTK_PIN_IES_SMT_SPEC(188, 188, 0x8f0, 9),
+ MTK_PIN_IES_SMT_SPEC(189, 189, 0x8f0, 8),
+ MTK_PIN_IES_SMT_SPEC(190, 190, 0x8f0, 9),
+ MTK_PIN_IES_SMT_SPEC(191, 191, 0x8f0, 10),
+ MTK_PIN_IES_SMT_SPEC(192, 192, 0x8f0, 11),
+ MTK_PIN_IES_SMT_SPEC(193, 194, 0x8f0, 10),
+ MTK_PIN_IES_SMT_SPEC(195, 195, 0x8f0, 11),
+ MTK_PIN_IES_SMT_SPEC(196, 199, 0x8f0, 12),
+ MTK_PIN_IES_SMT_SPEC(200, 203, 0x8f0, 13),
+ MTK_PIN_IES_SMT_SPEC(204, 206, 0x8f0, 14),
+ MTK_PIN_IES_SMT_SPEC(207, 209, 0x8f0, 15)
+};
+
+static const struct mtk_pin_ies_smt_set mt2712_ies_set[] = {
+ MTK_PIN_IES_SMT_SPEC(0, 3, 0x8c0, 2),
+ MTK_PIN_IES_SMT_SPEC(4, 7, 0x8c0, 0),
+ MTK_PIN_IES_SMT_SPEC(8, 9, 0x8c0, 1),
+ MTK_PIN_IES_SMT_SPEC(10, 11, 0x8c0, 4),
+ MTK_PIN_IES_SMT_SPEC(12, 12, 0x890, 6),
+ MTK_PIN_IES_SMT_SPEC(13, 13, 0x890, 7),
+ MTK_PIN_IES_SMT_SPEC(14, 14, 0x890, 6),
+ MTK_PIN_IES_SMT_SPEC(15, 15, 0x890, 7),
+ MTK_PIN_IES_SMT_SPEC(18, 23, 0x890, 1),
+ MTK_PIN_IES_SMT_SPEC(24, 25, 0x890, 2),
+ MTK_PIN_IES_SMT_SPEC(26, 26, 0x890, 3),
+ MTK_PIN_IES_SMT_SPEC(27, 27, 0x890, 4),
+ MTK_PIN_IES_SMT_SPEC(28, 29, 0x890, 3),
+ MTK_PIN_IES_SMT_SPEC(30, 36, 0xf50, 14),
+ MTK_PIN_IES_SMT_SPEC(37, 37, 0xc40, 14),
+ MTK_PIN_IES_SMT_SPEC(38, 45, 0xc60, 14),
+ MTK_PIN_IES_SMT_SPEC(46, 46, 0xc50, 14),
+ MTK_PIN_IES_SMT_SPEC(47, 47, 0xda0, 14),
+ MTK_PIN_IES_SMT_SPEC(48, 48, 0xd90, 14),
+ MTK_PIN_IES_SMT_SPEC(49, 52, 0xd60, 14),
+ MTK_PIN_IES_SMT_SPEC(53, 53, 0xd50, 14),
+ MTK_PIN_IES_SMT_SPEC(54, 54, 0xd80, 14),
+ MTK_PIN_IES_SMT_SPEC(55, 55, 0xe00, 14),
+ MTK_PIN_IES_SMT_SPEC(56, 56, 0xd40, 14),
+ MTK_PIN_IES_SMT_SPEC(57, 62, 0x8c0, 3),
+ MTK_PIN_IES_SMT_SPEC(63, 63, 0xc80, 14),
+ MTK_PIN_IES_SMT_SPEC(64, 66, 0xca0, 14),
+ MTK_PIN_IES_SMT_SPEC(67, 68, 0xc80, 14),
+ MTK_PIN_IES_SMT_SPEC(69, 69, 0xc90, 14),
+ MTK_PIN_IES_SMT_SPEC(70, 70, 0xc80, 14),
+ MTK_PIN_IES_SMT_SPEC(71, 74, 0x890, 8),
+ MTK_PIN_IES_SMT_SPEC(75, 77, 0x890, 9),
+ MTK_PIN_IES_SMT_SPEC(78, 81, 0x890, 10),
+ MTK_PIN_IES_SMT_SPEC(82, 88, 0x890, 9),
+ MTK_PIN_IES_SMT_SPEC(89, 89, 0xce0, 14),
+ MTK_PIN_IES_SMT_SPEC(90, 93, 0xd00, 14),
+ MTK_PIN_IES_SMT_SPEC(94, 94, 0xce0, 14),
+ MTK_PIN_IES_SMT_SPEC(95, 96, 0xcf0, 14),
+ MTK_PIN_IES_SMT_SPEC(97, 100, 0x890, 11),
+ MTK_PIN_IES_SMT_SPEC(101, 104, 0x890, 12),
+ MTK_PIN_IES_SMT_SPEC(105, 105, 0x890, 13),
+ MTK_PIN_IES_SMT_SPEC(106, 106, 0x890, 14),
+ MTK_PIN_IES_SMT_SPEC(107, 107, 0x890, 15),
+ MTK_PIN_IES_SMT_SPEC(108, 108, 0x8a0, 0),
+ MTK_PIN_IES_SMT_SPEC(109, 109, 0x8a0, 1),
+ MTK_PIN_IES_SMT_SPEC(110, 110, 0x8a0, 2),
+ MTK_PIN_IES_SMT_SPEC(111, 111, 0x890, 13),
+ MTK_PIN_IES_SMT_SPEC(112, 112, 0x890, 14),
+ MTK_PIN_IES_SMT_SPEC(113, 113, 0x890, 15),
+ MTK_PIN_IES_SMT_SPEC(114, 114, 0x8a0, 0),
+ MTK_PIN_IES_SMT_SPEC(115, 115, 0x8a0, 1),
+ MTK_PIN_IES_SMT_SPEC(116, 116, 0x8a0, 2),
+ MTK_PIN_IES_SMT_SPEC(117, 117, 0x8a0, 3),
+ MTK_PIN_IES_SMT_SPEC(118, 118, 0x8a0, 4),
+ MTK_PIN_IES_SMT_SPEC(119, 119, 0x8a0, 5),
+ MTK_PIN_IES_SMT_SPEC(120, 120, 0x8a0, 3),
+ MTK_PIN_IES_SMT_SPEC(121, 121, 0x8a0, 4),
+ MTK_PIN_IES_SMT_SPEC(122, 122, 0x8a0, 5),
+ MTK_PIN_IES_SMT_SPEC(123, 126, 0x8a0, 6),
+ MTK_PIN_IES_SMT_SPEC(127, 130, 0x8a0, 7),
+ MTK_PIN_IES_SMT_SPEC(131, 135, 0x8a0, 8),
+ MTK_PIN_IES_SMT_SPEC(136, 142, 0x890, 1),
+ MTK_PIN_IES_SMT_SPEC(143, 147, 0x8a0, 9),
+ MTK_PIN_IES_SMT_SPEC(148, 152, 0x8a0, 10),
+ MTK_PIN_IES_SMT_SPEC(153, 156, 0x8a0, 11),
+ MTK_PIN_IES_SMT_SPEC(157, 160, 0x8a0, 12),
+ MTK_PIN_IES_SMT_SPEC(161, 164, 0x8a0, 13),
+ MTK_PIN_IES_SMT_SPEC(165, 168, 0x8a0, 14),
+ MTK_PIN_IES_SMT_SPEC(169, 170, 0x8a0, 15),
+ MTK_PIN_IES_SMT_SPEC(171, 172, 0x8b0, 0),
+ MTK_PIN_IES_SMT_SPEC(173, 173, 0x8b0, 1),
+ MTK_PIN_IES_SMT_SPEC(174, 175, 0x8b0, 2),
+ MTK_PIN_IES_SMT_SPEC(176, 176, 0x8b0, 1),
+ MTK_PIN_IES_SMT_SPEC(177, 177, 0x8b0, 3),
+ MTK_PIN_IES_SMT_SPEC(178, 178, 0x8b0, 4),
+ MTK_PIN_IES_SMT_SPEC(179, 179, 0x8b0, 3),
+ MTK_PIN_IES_SMT_SPEC(180, 180, 0x8b0, 4),
+ MTK_PIN_IES_SMT_SPEC(181, 181, 0x8b0, 5),
+ MTK_PIN_IES_SMT_SPEC(182, 182, 0x8b0, 6),
+ MTK_PIN_IES_SMT_SPEC(183, 183, 0x8b0, 5),
+ MTK_PIN_IES_SMT_SPEC(184, 184, 0x8b0, 6),
+ MTK_PIN_IES_SMT_SPEC(185, 186, 0x8b0, 7),
+ MTK_PIN_IES_SMT_SPEC(187, 187, 0x8b0, 8),
+ MTK_PIN_IES_SMT_SPEC(188, 188, 0x8b0, 9),
+ MTK_PIN_IES_SMT_SPEC(189, 189, 0x8b0, 8),
+ MTK_PIN_IES_SMT_SPEC(190, 190, 0x8b0, 9),
+ MTK_PIN_IES_SMT_SPEC(191, 191, 0x8b0, 10),
+ MTK_PIN_IES_SMT_SPEC(192, 192, 0x8b0, 11),
+ MTK_PIN_IES_SMT_SPEC(193, 194, 0x8b0, 10),
+ MTK_PIN_IES_SMT_SPEC(195, 195, 0x8b0, 11),
+ MTK_PIN_IES_SMT_SPEC(196, 199, 0x8b0, 12),
+ MTK_PIN_IES_SMT_SPEC(200, 203, 0x8b0, 13),
+ MTK_PIN_IES_SMT_SPEC(204, 206, 0x8b0, 14),
+ MTK_PIN_IES_SMT_SPEC(207, 209, 0x8b0, 15)
+};
+
+static int mt2712_ies_smt_set(struct regmap *regmap, unsigned int pin,
+ unsigned char align,
+ int value, enum pin_config_param arg)
+{
+ if (arg == PIN_CONFIG_INPUT_ENABLE)
+ return mtk_pconf_spec_set_ies_smt_range(regmap, mt2712_ies_set,
+ ARRAY_SIZE(mt2712_ies_set), pin, align, value);
+ if (arg == PIN_CONFIG_INPUT_SCHMITT_ENABLE)
+ return mtk_pconf_spec_set_ies_smt_range(regmap, mt2712_smt_set,
+ ARRAY_SIZE(mt2712_smt_set), pin, align, value);
+ return -EINVAL;
+}
+
+static const struct mtk_drv_group_desc mt2712_drv_grp[] = {
+ /* 0E4E8SR 4/8/12/16 */
+ MTK_DRV_GRP(4, 16, 1, 2, 4),
+ /* 0E2E4SR 2/4/6/8 */
+ MTK_DRV_GRP(2, 8, 1, 2, 2),
+ /* E8E4E2 2/4/6/8/10/12/14/16 */
+ MTK_DRV_GRP(2, 16, 0, 2, 2)
+};
+
+static const struct mtk_pin_drv_grp mt2712_pin_drv[] = {
+ MTK_PIN_DRV_GRP(0, 0xc10, 4, 0),
+ MTK_PIN_DRV_GRP(1, 0xc10, 4, 0),
+ MTK_PIN_DRV_GRP(2, 0xc10, 4, 0),
+ MTK_PIN_DRV_GRP(3, 0xc10, 4, 0),
+
+ MTK_PIN_DRV_GRP(4, 0xc00, 12, 0),
+ MTK_PIN_DRV_GRP(5, 0xc00, 12, 0),
+ MTK_PIN_DRV_GRP(6, 0xc00, 12, 0),
+ MTK_PIN_DRV_GRP(7, 0xc00, 12, 0),
+
+ MTK_PIN_DRV_GRP(8, 0xc10, 0, 0),
+ MTK_PIN_DRV_GRP(9, 0xc10, 0, 0),
+ MTK_PIN_DRV_GRP(10, 0xc10, 0, 0),
+ MTK_PIN_DRV_GRP(11, 0xc10, 0, 0),
+
+ MTK_PIN_DRV_GRP(12, 0xb60, 0, 0),
+
+ MTK_PIN_DRV_GRP(13, 0xb60, 4, 0),
+
+ MTK_PIN_DRV_GRP(14, 0xb60, 0, 0),
+
+ MTK_PIN_DRV_GRP(15, 0xb60, 4, 0),
+
+ MTK_PIN_DRV_GRP(18, 0xb40, 0, 1),
+ MTK_PIN_DRV_GRP(19, 0xb40, 0, 1),
+ MTK_PIN_DRV_GRP(20, 0xb40, 0, 1),
+ MTK_PIN_DRV_GRP(21, 0xb40, 0, 1),
+ MTK_PIN_DRV_GRP(22, 0xb40, 0, 1),
+ MTK_PIN_DRV_GRP(23, 0xb40, 0, 1),
+
+ MTK_PIN_DRV_GRP(24, 0xb40, 4, 0),
+
+ MTK_PIN_DRV_GRP(25, 0xb40, 8, 0),
+
+ MTK_PIN_DRV_GRP(26, 0xb40, 12, 0),
+
+ MTK_PIN_DRV_GRP(27, 0xb50, 0, 0),
+
+ MTK_PIN_DRV_GRP(28, 0xb40, 12, 0),
+ MTK_PIN_DRV_GRP(29, 0xb40, 12, 0),
+
+ MTK_PIN_DRV_GRP(30, 0xf50, 8, 2),
+ MTK_PIN_DRV_GRP(31, 0xf50, 8, 2),
+ MTK_PIN_DRV_GRP(32, 0xf50, 8, 2),
+ MTK_PIN_DRV_GRP(33, 0xf50, 8, 2),
+ MTK_PIN_DRV_GRP(34, 0xf50, 8, 2),
+ MTK_PIN_DRV_GRP(35, 0xf50, 8, 2),
+ MTK_PIN_DRV_GRP(36, 0xf50, 8, 2),
+
+ MTK_PIN_DRV_GRP(37, 0xc40, 8, 2),
+
+ MTK_PIN_DRV_GRP(38, 0xc60, 8, 2),
+ MTK_PIN_DRV_GRP(39, 0xc60, 8, 2),
+ MTK_PIN_DRV_GRP(40, 0xc60, 8, 2),
+ MTK_PIN_DRV_GRP(41, 0xc60, 8, 2),
+ MTK_PIN_DRV_GRP(42, 0xc60, 8, 2),
+ MTK_PIN_DRV_GRP(43, 0xc60, 8, 2),
+ MTK_PIN_DRV_GRP(44, 0xc60, 8, 2),
+ MTK_PIN_DRV_GRP(45, 0xc60, 8, 2),
+
+ MTK_PIN_DRV_GRP(46, 0xc50, 8, 2),
+
+ MTK_PIN_DRV_GRP(47, 0xda0, 8, 2),
+
+ MTK_PIN_DRV_GRP(48, 0xd90, 8, 2),
+
+ MTK_PIN_DRV_GRP(49, 0xd60, 8, 2),
+ MTK_PIN_DRV_GRP(50, 0xd60, 8, 2),
+ MTK_PIN_DRV_GRP(51, 0xd60, 8, 2),
+ MTK_PIN_DRV_GRP(52, 0xd60, 8, 2),
+
+ MTK_PIN_DRV_GRP(53, 0xd50, 8, 2),
+
+ MTK_PIN_DRV_GRP(54, 0xd80, 8, 2),
+
+ MTK_PIN_DRV_GRP(55, 0xe00, 8, 2),
+
+ MTK_PIN_DRV_GRP(56, 0xd40, 8, 2),
+
+ MTK_PIN_DRV_GRP(63, 0xc80, 8, 2),
+
+ MTK_PIN_DRV_GRP(64, 0xca0, 8, 2),
+ MTK_PIN_DRV_GRP(65, 0xca0, 8, 2),
+ MTK_PIN_DRV_GRP(66, 0xca0, 8, 2),
+
+ MTK_PIN_DRV_GRP(67, 0xcd0, 8, 2),
+
+ MTK_PIN_DRV_GRP(68, 0xca0, 8, 2),
+
+ MTK_PIN_DRV_GRP(69, 0xc90, 8, 2),
+
+ MTK_PIN_DRV_GRP(70, 0xcc0, 8, 2),
+
+ MTK_PIN_DRV_GRP(71, 0xb60, 8, 1),
+ MTK_PIN_DRV_GRP(72, 0xb60, 8, 1),
+ MTK_PIN_DRV_GRP(73, 0xb60, 8, 1),
+ MTK_PIN_DRV_GRP(74, 0xb60, 8, 1),
+
+ MTK_PIN_DRV_GRP(75, 0xb60, 12, 1),
+ MTK_PIN_DRV_GRP(76, 0xb60, 12, 1),
+ MTK_PIN_DRV_GRP(77, 0xb60, 12, 1),
+
+ MTK_PIN_DRV_GRP(78, 0xb70, 0, 1),
+ MTK_PIN_DRV_GRP(79, 0xb70, 0, 1),
+ MTK_PIN_DRV_GRP(80, 0xb70, 0, 1),
+ MTK_PIN_DRV_GRP(81, 0xb70, 0, 1),
+
+ MTK_PIN_DRV_GRP(82, 0xb60, 12, 1),
+ MTK_PIN_DRV_GRP(83, 0xb60, 12, 1),
+ MTK_PIN_DRV_GRP(84, 0xb60, 12, 1),
+ MTK_PIN_DRV_GRP(85, 0xb60, 12, 1),
+ MTK_PIN_DRV_GRP(86, 0xb60, 12, 1),
+ MTK_PIN_DRV_GRP(87, 0xb60, 12, 1),
+ MTK_PIN_DRV_GRP(88, 0xb60, 12, 1),
+
+ MTK_PIN_DRV_GRP(89, 0xce0, 8, 2),
+
+ MTK_PIN_DRV_GRP(90, 0xd00, 8, 2),
+ MTK_PIN_DRV_GRP(91, 0xd00, 8, 2),
+ MTK_PIN_DRV_GRP(92, 0xd00, 8, 2),
+ MTK_PIN_DRV_GRP(93, 0xd00, 8, 2),
+
+ MTK_PIN_DRV_GRP(94, 0xd20, 8, 2),
+
+ MTK_PIN_DRV_GRP(95, 0xcf0, 8, 2),
+
+ MTK_PIN_DRV_GRP(96, 0xd30, 8, 2),
+
+ MTK_PIN_DRV_GRP(97, 0xb70, 4, 0),
+ MTK_PIN_DRV_GRP(98, 0xb70, 4, 0),
+ MTK_PIN_DRV_GRP(99, 0xb70, 4, 0),
+ MTK_PIN_DRV_GRP(100, 0xb70, 4, 0),
+
+ MTK_PIN_DRV_GRP(101, 0xb70, 8, 0),
+ MTK_PIN_DRV_GRP(102, 0xb70, 8, 0),
+ MTK_PIN_DRV_GRP(103, 0xb70, 8, 0),
+ MTK_PIN_DRV_GRP(104, 0xb70, 8, 0),
+
+ MTK_PIN_DRV_GRP(135, 0xb40, 0, 1),
+ MTK_PIN_DRV_GRP(136, 0xb40, 0, 1),
+ MTK_PIN_DRV_GRP(137, 0xb40, 0, 1),
+ MTK_PIN_DRV_GRP(138, 0xb40, 0, 1),
+ MTK_PIN_DRV_GRP(139, 0xb40, 0, 1),
+ MTK_PIN_DRV_GRP(140, 0xb40, 0, 1),
+ MTK_PIN_DRV_GRP(141, 0xb40, 0, 1),
+ MTK_PIN_DRV_GRP(142, 0xb40, 0, 1),
+
+ MTK_PIN_DRV_GRP(143, 0xba0, 12, 0),
+ MTK_PIN_DRV_GRP(144, 0xba0, 12, 0),
+ MTK_PIN_DRV_GRP(145, 0xba0, 12, 0),
+ MTK_PIN_DRV_GRP(146, 0xba0, 12, 0),
+ MTK_PIN_DRV_GRP(147, 0xba0, 12, 0),
+
+ MTK_PIN_DRV_GRP(148, 0xbb0, 0, 0),
+ MTK_PIN_DRV_GRP(149, 0xbb0, 0, 0),
+ MTK_PIN_DRV_GRP(150, 0xbb0, 0, 0),
+ MTK_PIN_DRV_GRP(151, 0xbb0, 0, 0),
+ MTK_PIN_DRV_GRP(152, 0xbb0, 0, 0),
+
+ MTK_PIN_DRV_GRP(153, 0xbb0, 4, 0),
+ MTK_PIN_DRV_GRP(154, 0xbb0, 4, 0),
+ MTK_PIN_DRV_GRP(155, 0xbb0, 4, 0),
+ MTK_PIN_DRV_GRP(156, 0xbb0, 4, 0),
+
+ MTK_PIN_DRV_GRP(157, 0xbb0, 8, 0),
+ MTK_PIN_DRV_GRP(158, 0xbb0, 8, 0),
+ MTK_PIN_DRV_GRP(159, 0xbb0, 8, 0),
+ MTK_PIN_DRV_GRP(160, 0xbb0, 8, 0),
+
+ MTK_PIN_DRV_GRP(161, 0xbb0, 12, 0),
+ MTK_PIN_DRV_GRP(162, 0xbb0, 12, 0),
+ MTK_PIN_DRV_GRP(163, 0xbb0, 12, 0),
+ MTK_PIN_DRV_GRP(164, 0xbb0, 12, 0),
+
+ MTK_PIN_DRV_GRP(165, 0xbc0, 0, 0),
+ MTK_PIN_DRV_GRP(166, 0xbc0, 0, 0),
+ MTK_PIN_DRV_GRP(167, 0xbc0, 0, 0),
+ MTK_PIN_DRV_GRP(168, 0xbc0, 0, 0),
+
+ MTK_PIN_DRV_GRP(169, 0xbc0, 4, 0),
+ MTK_PIN_DRV_GRP(170, 0xbc0, 4, 0),
+
+ MTK_PIN_DRV_GRP(171, 0xbc0, 8, 0),
+ MTK_PIN_DRV_GRP(172, 0xbc0, 8, 0),
+
+ MTK_PIN_DRV_GRP(173, 0xbc0, 12, 0),
+
+ MTK_PIN_DRV_GRP(174, 0xbd0, 0, 0),
+ MTK_PIN_DRV_GRP(175, 0xbd0, 0, 0),
+
+ MTK_PIN_DRV_GRP(176, 0xbc0, 12, 0),
+
+ MTK_PIN_DRV_GRP(177, 0xbd0, 4, 0),
+
+ MTK_PIN_DRV_GRP(178, 0xbd0, 8, 0),
+
+ MTK_PIN_DRV_GRP(179, 0xbd0, 4, 0),
+
+ MTK_PIN_DRV_GRP(180, 0xbd0, 8, 0),
+
+ MTK_PIN_DRV_GRP(181, 0xbd0, 12, 0),
+
+ MTK_PIN_DRV_GRP(182, 0xbe0, 0, 0),
+
+ MTK_PIN_DRV_GRP(183, 0xbd0, 12, 0),
+
+ MTK_PIN_DRV_GRP(184, 0xbe0, 0, 0),
+
+ MTK_PIN_DRV_GRP(185, 0xbe0, 4, 0),
+
+ MTK_PIN_DRV_GRP(186, 0xbe0, 8, 0),
+
+ MTK_PIN_DRV_GRP(187, 0xbe0, 12, 0),
+
+ MTK_PIN_DRV_GRP(188, 0xbf0, 0, 0),
+
+ MTK_PIN_DRV_GRP(189, 0xbe0, 12, 0),
+
+ MTK_PIN_DRV_GRP(190, 0xbf0, 0, 0),
+
+ MTK_PIN_DRV_GRP(191, 0xbf0, 4, 0),
+
+ MTK_PIN_DRV_GRP(192, 0xbf0, 8, 0),
+
+ MTK_PIN_DRV_GRP(193, 0xbf0, 4, 0),
+ MTK_PIN_DRV_GRP(194, 0xbf0, 4, 0),
+
+ MTK_PIN_DRV_GRP(195, 0xbf0, 8, 0),
+
+ MTK_PIN_DRV_GRP(196, 0xbf0, 12, 0),
+ MTK_PIN_DRV_GRP(197, 0xbf0, 12, 0),
+ MTK_PIN_DRV_GRP(198, 0xbf0, 12, 0),
+ MTK_PIN_DRV_GRP(199, 0xbf0, 12, 0),
+
+ MTK_PIN_DRV_GRP(200, 0xc00, 0, 0),
+ MTK_PIN_DRV_GRP(201, 0xc00, 0, 0),
+ MTK_PIN_DRV_GRP(202, 0xc00, 0, 0),
+ MTK_PIN_DRV_GRP(203, 0xc00, 0, 0),
+
+ MTK_PIN_DRV_GRP(204, 0xc00, 4, 0),
+ MTK_PIN_DRV_GRP(205, 0xc00, 4, 0),
+ MTK_PIN_DRV_GRP(206, 0xc00, 4, 0),
+
+ MTK_PIN_DRV_GRP(207, 0xc00, 8, 0),
+ MTK_PIN_DRV_GRP(208, 0xc00, 8, 0),
+ MTK_PIN_DRV_GRP(209, 0xc00, 8, 0),
+};
+
+static const struct mtk_pinctrl_devdata mt2712_pinctrl_data = {
+ .pins = mtk_pins_mt2712,
+ .npins = ARRAY_SIZE(mtk_pins_mt2712),
+ .grp_desc = mt2712_drv_grp,
+ .n_grp_cls = ARRAY_SIZE(mt2712_drv_grp),
+ .pin_drv_grp = mt2712_pin_drv,
+ .n_pin_drv_grps = ARRAY_SIZE(mt2712_pin_drv),
+ .spec_pull_set = mt2712_spec_pull_set,
+ .spec_ies_smt_set = mt2712_ies_smt_set,
+ .dir_offset = 0x0000,
+ .pullen_offset = 0x0100,
+ .pullsel_offset = 0x0200,
+ .dout_offset = 0x0300,
+ .din_offset = 0x0400,
+ .pinmux_offset = 0x0500,
+ .type1_start = 210,
+ .type1_end = 210,
+ .port_shf = 4,
+ .port_mask = 0xf,
+ .port_align = 4,
+ .eint_offsets = {
+ .name = "mt2712_eint",
+ .stat = 0x000,
+ .ack = 0x040,
+ .mask = 0x080,
+ .mask_set = 0x0c0,
+ .mask_clr = 0x100,
+ .sens = 0x140,
+ .sens_set = 0x180,
+ .sens_clr = 0x1c0,
+ .soft = 0x200,
+ .soft_set = 0x240,
+ .soft_clr = 0x280,
+ .pol = 0x300,
+ .pol_set = 0x340,
+ .pol_clr = 0x380,
+ .dom_en = 0x400,
+ .dbnc_ctrl = 0x500,
+ .dbnc_set = 0x600,
+ .dbnc_clr = 0x700,
+ .port_mask = 0xf,
+ .ports = 8,
+ },
+ .ap_num = 229,
+ .db_cnt = 40,
+};
+
+static int mt2712_pinctrl_probe(struct platform_device *pdev)
+{
+ return mtk_pctrl_init(pdev, &mt2712_pinctrl_data, NULL);
+}
+
+static const struct of_device_id mt2712_pctrl_match[] = {
+ {
+ .compatible = "mediatek,mt2712-pinctrl",
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mt2712_pctrl_match);
+
+static struct platform_driver mtk_pinctrl_driver = {
+ .probe = mt2712_pinctrl_probe,
+ .driver = {
+ .name = "mediatek-mt2712-pinctrl",
+ .of_match_table = mt2712_pctrl_match,
+ .pm = &mtk_eint_pm_ops,
+ },
+};
+
+static int __init mtk_pinctrl_init(void)
+{
+ return platform_driver_register(&mtk_pinctrl_driver);
+}
+
+arch_initcall(mtk_pinctrl_init);
diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
index 3cf384f8b122..c3975a04d1cd 100644
--- a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
+++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
@@ -293,7 +293,7 @@ static int mtk_pconf_set_pull_select(struct mtk_pinctrl *pctl,
unsigned int pin, bool enable, bool isup, unsigned int arg)
{
unsigned int bit;
- unsigned int reg_pullen, reg_pullsel;
+ unsigned int reg_pullen, reg_pullsel, r1r0;
int ret;
/* Some pins' pull setting are very different,
@@ -301,8 +301,12 @@ static int mtk_pconf_set_pull_select(struct mtk_pinctrl *pctl,
* resistor bit, so we need this special handle.
*/
if (pctl->devdata->spec_pull_set) {
+ /* For special pins, bias-disable is set by R1R0,
+ * the parameter should be "MTK_PUPD_SET_R1R0_00".
+ */
+ r1r0 = enable ? arg : MTK_PUPD_SET_R1R0_00;
ret = pctl->devdata->spec_pull_set(mtk_get_regmap(pctl, pin),
- pin, pctl->devdata->port_align, isup, arg);
+ pin, pctl->devdata->port_align, isup, r1r0);
if (!ret)
return 0;
}
@@ -501,7 +505,7 @@ static int mtk_pctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev,
int num_pins, num_funcs, maps_per_pin;
unsigned long *configs;
unsigned int num_configs;
- bool has_config = 0;
+ bool has_config = false;
int i, err;
unsigned reserve = 0;
struct mtk_pinctrl_group *grp;
@@ -520,7 +524,7 @@ static int mtk_pctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev,
return err;
if (num_configs)
- has_config = 1;
+ has_config = true;
num_pins = pins->length / sizeof(u32);
num_funcs = num_pins;
diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-mt2701.h b/drivers/pinctrl/mediatek/pinctrl-mtk-mt2701.h
index 1035df49301f..940f7678f09b 100644
--- a/drivers/pinctrl/mediatek/pinctrl-mtk-mt2701.h
+++ b/drivers/pinctrl/mediatek/pinctrl-mtk-mt2701.h
@@ -19,53 +19,46 @@
#include "pinctrl-mtk-common.h"
static const struct mtk_desc_pin mtk_pins_mt2701[] = {
- MTK_PIN(
- PINCTRL_PIN(0, "PWRAP_SPI0_MI"),
+ MTK_PIN(PINCTRL_PIN(0, "PWRAP_SPI0_MI"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 148),
MTK_FUNCTION(0, "GPIO0"),
MTK_FUNCTION(1, "PWRAP_SPIDO"),
MTK_FUNCTION(2, "PWRAP_SPIDI")
),
- MTK_PIN(
- PINCTRL_PIN(1, "PWRAP_SPI0_MO"),
+ MTK_PIN(PINCTRL_PIN(1, "PWRAP_SPI0_MO"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 149),
MTK_FUNCTION(0, "GPIO1"),
MTK_FUNCTION(1, "PWRAP_SPIDI"),
MTK_FUNCTION(2, "PWRAP_SPIDO")
),
- MTK_PIN(
- PINCTRL_PIN(2, "PWRAP_INT"),
+ MTK_PIN(PINCTRL_PIN(2, "PWRAP_INT"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 150),
MTK_FUNCTION(0, "GPIO2"),
MTK_FUNCTION(1, "PWRAP_INT")
),
- MTK_PIN(
- PINCTRL_PIN(3, "PWRAP_SPI0_CK"),
+ MTK_PIN(PINCTRL_PIN(3, "PWRAP_SPI0_CK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 151),
MTK_FUNCTION(0, "GPIO3"),
MTK_FUNCTION(1, "PWRAP_SPICK_I")
),
- MTK_PIN(
- PINCTRL_PIN(4, "PWRAP_SPI0_CSN"),
+ MTK_PIN(PINCTRL_PIN(4, "PWRAP_SPI0_CSN"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 152),
MTK_FUNCTION(0, "GPIO4"),
MTK_FUNCTION(1, "PWRAP_SPICS_B_I")
),
- MTK_PIN(
- PINCTRL_PIN(5, "PWRAP_SPI0_CK2"),
+ MTK_PIN(PINCTRL_PIN(5, "PWRAP_SPI0_CK2"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 153),
MTK_FUNCTION(0, "GPIO5"),
MTK_FUNCTION(1, "PWRAP_SPICK2_I"),
MTK_FUNCTION(5, "ANT_SEL1")
),
- MTK_PIN(
- PINCTRL_PIN(6, "PWRAP_SPI0_CSN2"),
+ MTK_PIN(PINCTRL_PIN(6, "PWRAP_SPI0_CSN2"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 154),
MTK_FUNCTION(0, "GPIO6"),
@@ -73,8 +66,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(5, "ANT_SEL0"),
MTK_FUNCTION(7, "DBG_MON_A[0]")
),
- MTK_PIN(
- PINCTRL_PIN(7, "SPI1_CSN"),
+ MTK_PIN(PINCTRL_PIN(7, "SPI1_CSN"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 155),
MTK_FUNCTION(0, "GPIO7"),
@@ -82,8 +74,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(4, "KCOL0"),
MTK_FUNCTION(7, "DBG_MON_B[12]")
),
- MTK_PIN(
- PINCTRL_PIN(8, "SPI1_MI"),
+ MTK_PIN(PINCTRL_PIN(8, "SPI1_MI"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 156),
MTK_FUNCTION(0, "GPIO8"),
@@ -92,8 +83,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(4, "KCOL1"),
MTK_FUNCTION(7, "DBG_MON_B[13]")
),
- MTK_PIN(
- PINCTRL_PIN(9, "SPI1_MO"),
+ MTK_PIN(PINCTRL_PIN(9, "SPI1_MO"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 157),
MTK_FUNCTION(0, "GPIO9"),
@@ -103,36 +93,31 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(4, "KCOL2"),
MTK_FUNCTION(7, "DBG_MON_B[14]")
),
- MTK_PIN(
- PINCTRL_PIN(10, "RTC32K_CK"),
+ MTK_PIN(PINCTRL_PIN(10, "RTC32K_CK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 158),
MTK_FUNCTION(0, "GPIO10"),
MTK_FUNCTION(1, "RTC32K_CK")
),
- MTK_PIN(
- PINCTRL_PIN(11, "WATCHDOG"),
+ MTK_PIN(PINCTRL_PIN(11, "WATCHDOG"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 159),
MTK_FUNCTION(0, "GPIO11"),
MTK_FUNCTION(1, "WATCHDOG")
),
- MTK_PIN(
- PINCTRL_PIN(12, "SRCLKENA"),
+ MTK_PIN(PINCTRL_PIN(12, "SRCLKENA"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 160),
MTK_FUNCTION(0, "GPIO12"),
MTK_FUNCTION(1, "SRCLKENA")
),
- MTK_PIN(
- PINCTRL_PIN(13, "SRCLKENAI"),
+ MTK_PIN(PINCTRL_PIN(13, "SRCLKENAI"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 161),
MTK_FUNCTION(0, "GPIO13"),
MTK_FUNCTION(1, "SRCLKENAI")
),
- MTK_PIN(
- PINCTRL_PIN(14, "URXD2"),
+ MTK_PIN(PINCTRL_PIN(14, "URXD2"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 162),
MTK_FUNCTION(0, "GPIO14"),
@@ -141,8 +126,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(5, "SRCCLKENAI2"),
MTK_FUNCTION(7, "DBG_MON_B[30]")
),
- MTK_PIN(
- PINCTRL_PIN(15, "UTXD2"),
+ MTK_PIN(PINCTRL_PIN(15, "UTXD2"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 163),
MTK_FUNCTION(0, "GPIO15"),
@@ -150,8 +134,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(2, "URXD2"),
MTK_FUNCTION(7, "DBG_MON_B[31]")
),
- MTK_PIN(
- PINCTRL_PIN(16, "I2S5_DATA_IN"),
+ MTK_PIN(PINCTRL_PIN(16, "I2S5_DATA_IN"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 164),
MTK_FUNCTION(0, "GPIO16"),
@@ -159,8 +142,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(3, "PCM_RX"),
MTK_FUNCTION(4, "ANT_SEL4")
),
- MTK_PIN(
- PINCTRL_PIN(17, "I2S5_BCK"),
+ MTK_PIN(PINCTRL_PIN(17, "I2S5_BCK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 165),
MTK_FUNCTION(0, "GPIO17"),
@@ -168,8 +150,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(3, "PCM_CLK0"),
MTK_FUNCTION(4, "ANT_SEL2")
),
- MTK_PIN(
- PINCTRL_PIN(18, "PCM_CLK"),
+ MTK_PIN(PINCTRL_PIN(18, "PCM_CLK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 166),
MTK_FUNCTION(0, "GPIO18"),
@@ -180,8 +161,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(6, "WCN_PCM_CLKO"),
MTK_FUNCTION(7, "DBG_MON_A[3]")
),
- MTK_PIN(
- PINCTRL_PIN(19, "PCM_SYNC"),
+ MTK_PIN(PINCTRL_PIN(19, "PCM_SYNC"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 167),
MTK_FUNCTION(0, "GPIO19"),
@@ -191,8 +171,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(6, "WCN_PCM_SYNC"),
MTK_FUNCTION(7, "DBG_MON_A[5]")
),
- MTK_PIN(
- PINCTRL_PIN(20, "PCM_RX"),
+ MTK_PIN(PINCTRL_PIN(20, "PCM_RX"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO20"),
@@ -204,8 +183,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(6, "WCN_PCM_RX"),
MTK_FUNCTION(7, "DBG_MON_A[4]")
),
- MTK_PIN(
- PINCTRL_PIN(21, "PCM_TX"),
+ MTK_PIN(PINCTRL_PIN(21, "PCM_TX"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO21"),
@@ -217,8 +195,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(6, "WCN_PCM_TX"),
MTK_FUNCTION(7, "DBG_MON_A[2]")
),
- MTK_PIN(
- PINCTRL_PIN(22, "EINT0"),
+ MTK_PIN(PINCTRL_PIN(22, "EINT0"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 0),
MTK_FUNCTION(0, "GPIO22"),
@@ -231,8 +208,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(7, "DBG_MON_A[30]"),
MTK_FUNCTION(10, "PCIE0_PERST_N")
),
- MTK_PIN(
- PINCTRL_PIN(23, "EINT1"),
+ MTK_PIN(PINCTRL_PIN(23, "EINT1"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 1),
MTK_FUNCTION(0, "GPIO23"),
@@ -245,8 +221,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(7, "DBG_MON_A[29]"),
MTK_FUNCTION(10, "PCIE1_PERST_N")
),
- MTK_PIN(
- PINCTRL_PIN(24, "EINT2"),
+ MTK_PIN(PINCTRL_PIN(24, "EINT2"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 2),
MTK_FUNCTION(0, "GPIO24"),
@@ -258,8 +233,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(7, "DBG_MON_A[28]"),
MTK_FUNCTION(10, "PCIE2_PERST_N")
),
- MTK_PIN(
- PINCTRL_PIN(25, "EINT3"),
+ MTK_PIN(PINCTRL_PIN(25, "EINT3"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 3),
MTK_FUNCTION(0, "GPIO25"),
@@ -268,8 +242,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(4, "CONN_MCU_DBGI_N"),
MTK_FUNCTION(7, "DBG_MON_A[27]")
),
- MTK_PIN(
- PINCTRL_PIN(26, "EINT4"),
+ MTK_PIN(PINCTRL_PIN(26, "EINT4"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 4),
MTK_FUNCTION(0, "GPIO26"),
@@ -281,8 +254,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(6, "PCIE2_WAKE_N"),
MTK_FUNCTION(7, "DBG_MON_A[26]")
),
- MTK_PIN(
- PINCTRL_PIN(27, "EINT5"),
+ MTK_PIN(PINCTRL_PIN(27, "EINT5"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 5),
MTK_FUNCTION(0, "GPIO27"),
@@ -293,8 +265,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(6, "PCIE1_WAKE_N"),
MTK_FUNCTION(7, "DBG_MON_A[25]")
),
- MTK_PIN(
- PINCTRL_PIN(28, "EINT6"),
+ MTK_PIN(PINCTRL_PIN(28, "EINT6"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 6),
MTK_FUNCTION(0, "GPIO28"),
@@ -304,8 +275,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(6, "PCIE0_WAKE_N"),
MTK_FUNCTION(7, "DBG_MON_A[24]")
),
- MTK_PIN(
- PINCTRL_PIN(29, "EINT7"),
+ MTK_PIN(PINCTRL_PIN(29, "EINT7"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 7),
MTK_FUNCTION(0, "GPIO29"),
@@ -319,8 +289,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(7, "DBG_MON_A[23]"),
MTK_FUNCTION(14, "PCIE2_PERST_N")
),
- MTK_PIN(
- PINCTRL_PIN(30, "I2S5_LRCK"),
+ MTK_PIN(PINCTRL_PIN(30, "I2S5_LRCK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 12),
MTK_FUNCTION(0, "GPIO30"),
@@ -328,16 +297,14 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(3, "PCM_SYNC"),
MTK_FUNCTION(4, "ANT_SEL1")
),
- MTK_PIN(
- PINCTRL_PIN(31, "I2S5_MCLK"),
+ MTK_PIN(PINCTRL_PIN(31, "I2S5_MCLK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 13),
MTK_FUNCTION(0, "GPIO31"),
MTK_FUNCTION(1, "I2S5_MCLK"),
MTK_FUNCTION(4, "ANT_SEL0")
),
- MTK_PIN(
- PINCTRL_PIN(32, "I2S5_DATA"),
+ MTK_PIN(PINCTRL_PIN(32, "I2S5_DATA"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 14),
MTK_FUNCTION(0, "GPIO32"),
@@ -346,8 +313,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(3, "PCM_TX"),
MTK_FUNCTION(4, "ANT_SEL3")
),
- MTK_PIN(
- PINCTRL_PIN(33, "I2S1_DATA"),
+ MTK_PIN(PINCTRL_PIN(33, "I2S1_DATA"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 15),
MTK_FUNCTION(0, "GPIO33"),
@@ -359,8 +325,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(6, "WCN_PCM_TX"),
MTK_FUNCTION(7, "DBG_MON_B[8]")
),
- MTK_PIN(
- PINCTRL_PIN(34, "I2S1_DATA_IN"),
+ MTK_PIN(PINCTRL_PIN(34, "I2S1_DATA_IN"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 16),
MTK_FUNCTION(0, "GPIO34"),
@@ -371,8 +336,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(6, "WCN_PCM_RX"),
MTK_FUNCTION(7, "DBG_MON_B[7]")
),
- MTK_PIN(
- PINCTRL_PIN(35, "I2S1_BCK"),
+ MTK_PIN(PINCTRL_PIN(35, "I2S1_BCK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 17),
MTK_FUNCTION(0, "GPIO35"),
@@ -382,8 +346,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(6, "WCN_PCM_CLKO"),
MTK_FUNCTION(7, "DBG_MON_B[9]")
),
- MTK_PIN(
- PINCTRL_PIN(36, "I2S1_LRCK"),
+ MTK_PIN(PINCTRL_PIN(36, "I2S1_LRCK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 18),
MTK_FUNCTION(0, "GPIO36"),
@@ -393,8 +356,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(6, "WCN_PCM_SYNC"),
MTK_FUNCTION(7, "DBG_MON_B[10]")
),
- MTK_PIN(
- PINCTRL_PIN(37, "I2S1_MCLK"),
+ MTK_PIN(PINCTRL_PIN(37, "I2S1_MCLK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 19),
MTK_FUNCTION(0, "GPIO37"),
@@ -402,8 +364,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(5, "G1_RXDV"),
MTK_FUNCTION(7, "DBG_MON_B[11]")
),
- MTK_PIN(
- PINCTRL_PIN(38, "I2S2_DATA"),
+ MTK_PIN(PINCTRL_PIN(38, "I2S2_DATA"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 20),
MTK_FUNCTION(0, "GPIO38"),
@@ -411,8 +372,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(3, "PCM_TX"),
MTK_FUNCTION(4, "DMIC_DAT0")
),
- MTK_PIN(
- PINCTRL_PIN(39, "JTMS"),
+ MTK_PIN(PINCTRL_PIN(39, "JTMS"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 21),
MTK_FUNCTION(0, "GPIO39"),
@@ -421,8 +381,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(3, "CONN_MCU_AICE_JMSC"),
MTK_FUNCTION(4, "DFD_TMS_XI")
),
- MTK_PIN(
- PINCTRL_PIN(40, "JTCK"),
+ MTK_PIN(PINCTRL_PIN(40, "JTCK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 22),
MTK_FUNCTION(0, "GPIO40"),
@@ -431,8 +390,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(3, "CONN_MCU_AICE_JCKC"),
MTK_FUNCTION(4, "DFD_TCK_XI")
),
- MTK_PIN(
- PINCTRL_PIN(41, "JTDI"),
+ MTK_PIN(PINCTRL_PIN(41, "JTDI"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 23),
MTK_FUNCTION(0, "GPIO41"),
@@ -440,8 +398,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(2, "CONN_MCU_TDI"),
MTK_FUNCTION(4, "DFD_TDI_XI")
),
- MTK_PIN(
- PINCTRL_PIN(42, "JTDO"),
+ MTK_PIN(PINCTRL_PIN(42, "JTDO"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 24),
MTK_FUNCTION(0, "GPIO42"),
@@ -449,55 +406,48 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(2, "CONN_MCU_TDO"),
MTK_FUNCTION(4, "DFD_TDO")
),
- MTK_PIN(
- PINCTRL_PIN(43, "NCLE"),
+ MTK_PIN(PINCTRL_PIN(43, "NCLE"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 25),
MTK_FUNCTION(0, "GPIO43"),
MTK_FUNCTION(1, "NCLE"),
MTK_FUNCTION(2, "EXT_XCS2")
),
- MTK_PIN(
- PINCTRL_PIN(44, "NCEB1"),
+ MTK_PIN(PINCTRL_PIN(44, "NCEB1"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 26),
MTK_FUNCTION(0, "GPIO44"),
MTK_FUNCTION(1, "NCEB1"),
MTK_FUNCTION(2, "IDDIG")
),
- MTK_PIN(
- PINCTRL_PIN(45, "NCEB0"),
+ MTK_PIN(PINCTRL_PIN(45, "NCEB0"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 27),
MTK_FUNCTION(0, "GPIO45"),
MTK_FUNCTION(1, "NCEB0"),
MTK_FUNCTION(2, "DRV_VBUS")
),
- MTK_PIN(
- PINCTRL_PIN(46, "IR"),
+ MTK_PIN(PINCTRL_PIN(46, "IR"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 28),
MTK_FUNCTION(0, "GPIO46"),
MTK_FUNCTION(1, "IR")
),
- MTK_PIN(
- PINCTRL_PIN(47, "NREB"),
+ MTK_PIN(PINCTRL_PIN(47, "NREB"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 29),
MTK_FUNCTION(0, "GPIO47"),
MTK_FUNCTION(1, "NREB"),
MTK_FUNCTION(2, "IDDIG_P1")
),
- MTK_PIN(
- PINCTRL_PIN(48, "NRNB"),
+ MTK_PIN(PINCTRL_PIN(48, "NRNB"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 30),
MTK_FUNCTION(0, "GPIO48"),
MTK_FUNCTION(1, "NRNB"),
MTK_FUNCTION(2, "DRV_VBUS_P1")
),
- MTK_PIN(
- PINCTRL_PIN(49, "I2S0_DATA"),
+ MTK_PIN(PINCTRL_PIN(49, "I2S0_DATA"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 31),
MTK_FUNCTION(0, "GPIO49"),
@@ -507,8 +457,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(6, "WCN_I2S_DO"),
MTK_FUNCTION(7, "DBG_MON_B[3]")
),
- MTK_PIN(
- PINCTRL_PIN(50, "I2S2_BCK"),
+ MTK_PIN(PINCTRL_PIN(50, "I2S2_BCK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 32),
MTK_FUNCTION(0, "GPIO50"),
@@ -516,8 +465,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(3, "PCM_CLK0"),
MTK_FUNCTION(4, "DMIC_SCK1")
),
- MTK_PIN(
- PINCTRL_PIN(51, "I2S2_DATA_IN"),
+ MTK_PIN(PINCTRL_PIN(51, "I2S2_DATA_IN"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 33),
MTK_FUNCTION(0, "GPIO51"),
@@ -525,8 +473,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(3, "PCM_RX"),
MTK_FUNCTION(4, "DMIC_SCK0")
),
- MTK_PIN(
- PINCTRL_PIN(52, "I2S2_LRCK"),
+ MTK_PIN(PINCTRL_PIN(52, "I2S2_LRCK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 34),
MTK_FUNCTION(0, "GPIO52"),
@@ -534,8 +481,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(3, "PCM_SYNC"),
MTK_FUNCTION(4, "DMIC_DAT1")
),
- MTK_PIN(
- PINCTRL_PIN(53, "SPI0_CSN"),
+ MTK_PIN(PINCTRL_PIN(53, "SPI0_CSN"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 35),
MTK_FUNCTION(0, "GPIO53"),
@@ -545,8 +491,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(5, "PWM1"),
MTK_FUNCTION(7, "DBG_MON_A[7]")
),
- MTK_PIN(
- PINCTRL_PIN(54, "SPI0_CK"),
+ MTK_PIN(PINCTRL_PIN(54, "SPI0_CK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 36),
MTK_FUNCTION(0, "GPIO54"),
@@ -555,8 +500,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(4, "ADC_DAT_IN"),
MTK_FUNCTION(7, "DBG_MON_A[10]")
),
- MTK_PIN(
- PINCTRL_PIN(55, "SPI0_MI"),
+ MTK_PIN(PINCTRL_PIN(55, "SPI0_MI"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 37),
MTK_FUNCTION(0, "GPIO55"),
@@ -567,8 +511,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(5, "PWM2"),
MTK_FUNCTION(7, "DBG_MON_A[8]")
),
- MTK_PIN(
- PINCTRL_PIN(56, "SPI0_MO"),
+ MTK_PIN(PINCTRL_PIN(56, "SPI0_MO"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 38),
MTK_FUNCTION(0, "GPIO56"),
@@ -577,77 +520,67 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(3, "SPDIF_IN0"),
MTK_FUNCTION(7, "DBG_MON_A[9]")
),
- MTK_PIN(
- PINCTRL_PIN(57, "SDA1"),
+ MTK_PIN(PINCTRL_PIN(57, "SDA1"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 39),
MTK_FUNCTION(0, "GPIO57"),
MTK_FUNCTION(1, "SDA1")
),
- MTK_PIN(
- PINCTRL_PIN(58, "SCL1"),
+ MTK_PIN(PINCTRL_PIN(58, "SCL1"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 40),
MTK_FUNCTION(0, "GPIO58"),
MTK_FUNCTION(1, "SCL1")
),
- MTK_PIN(
- PINCTRL_PIN(59, "RAMBUF_I_CLK"),
+ MTK_PIN(PINCTRL_PIN(59, "RAMBUF_I_CLK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO59"),
MTK_FUNCTION(1, "RAMBUF_I_CLK")
),
- MTK_PIN(
- PINCTRL_PIN(60, "WB_RSTB"),
+ MTK_PIN(PINCTRL_PIN(60, "WB_RSTB"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 41),
MTK_FUNCTION(0, "GPIO60"),
MTK_FUNCTION(1, "WB_RSTB"),
MTK_FUNCTION(7, "DBG_MON_A[11]")
),
- MTK_PIN(
- PINCTRL_PIN(61, "F2W_DATA"),
+ MTK_PIN(PINCTRL_PIN(61, "F2W_DATA"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 42),
MTK_FUNCTION(0, "GPIO61"),
MTK_FUNCTION(1, "F2W_DATA"),
MTK_FUNCTION(7, "DBG_MON_A[16]")
),
- MTK_PIN(
- PINCTRL_PIN(62, "F2W_CLK"),
+ MTK_PIN(PINCTRL_PIN(62, "F2W_CLK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 43),
MTK_FUNCTION(0, "GPIO62"),
MTK_FUNCTION(1, "F2W_CK"),
MTK_FUNCTION(7, "DBG_MON_A[15]")
),
- MTK_PIN(
- PINCTRL_PIN(63, "WB_SCLK"),
+ MTK_PIN(PINCTRL_PIN(63, "WB_SCLK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 44),
MTK_FUNCTION(0, "GPIO63"),
MTK_FUNCTION(1, "WB_SCLK"),
MTK_FUNCTION(7, "DBG_MON_A[13]")
),
- MTK_PIN(
- PINCTRL_PIN(64, "WB_SDATA"),
+ MTK_PIN(PINCTRL_PIN(64, "WB_SDATA"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 45),
MTK_FUNCTION(0, "GPIO64"),
MTK_FUNCTION(1, "WB_SDATA"),
MTK_FUNCTION(7, "DBG_MON_A[12]")
),
- MTK_PIN(
- PINCTRL_PIN(65, "WB_SEN"),
+ MTK_PIN(PINCTRL_PIN(65, "WB_SEN"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 46),
MTK_FUNCTION(0, "GPIO65"),
MTK_FUNCTION(1, "WB_SEN"),
MTK_FUNCTION(7, "DBG_MON_A[14]")
),
- MTK_PIN(
- PINCTRL_PIN(66, "WB_CRTL0"),
+ MTK_PIN(PINCTRL_PIN(66, "WB_CRTL0"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 47),
MTK_FUNCTION(0, "GPIO66"),
@@ -655,8 +588,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(5, "DFD_NTRST_XI"),
MTK_FUNCTION(7, "DBG_MON_A[17]")
),
- MTK_PIN(
- PINCTRL_PIN(67, "WB_CRTL1"),
+ MTK_PIN(PINCTRL_PIN(67, "WB_CRTL1"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 48),
MTK_FUNCTION(0, "GPIO67"),
@@ -664,8 +596,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(5, "DFD_TMS_XI"),
MTK_FUNCTION(7, "DBG_MON_A[18]")
),
- MTK_PIN(
- PINCTRL_PIN(68, "WB_CRTL2"),
+ MTK_PIN(PINCTRL_PIN(68, "WB_CRTL2"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 49),
MTK_FUNCTION(0, "GPIO68"),
@@ -673,8 +604,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(5, "DFD_TCK_XI"),
MTK_FUNCTION(7, "DBG_MON_A[19]")
),
- MTK_PIN(
- PINCTRL_PIN(69, "WB_CRTL3"),
+ MTK_PIN(PINCTRL_PIN(69, "WB_CRTL3"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 50),
MTK_FUNCTION(0, "GPIO69"),
@@ -682,8 +612,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(5, "DFD_TDI_XI"),
MTK_FUNCTION(7, "DBG_MON_A[20]")
),
- MTK_PIN(
- PINCTRL_PIN(70, "WB_CRTL4"),
+ MTK_PIN(PINCTRL_PIN(70, "WB_CRTL4"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 51),
MTK_FUNCTION(0, "GPIO70"),
@@ -691,16 +620,14 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(5, "DFD_TDO"),
MTK_FUNCTION(7, "DBG_MON_A[21]")
),
- MTK_PIN(
- PINCTRL_PIN(71, "WB_CRTL5"),
+ MTK_PIN(PINCTRL_PIN(71, "WB_CRTL5"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 52),
MTK_FUNCTION(0, "GPIO71"),
MTK_FUNCTION(1, "WB_CRTL5"),
MTK_FUNCTION(7, "DBG_MON_A[22]")
),
- MTK_PIN(
- PINCTRL_PIN(72, "I2S0_DATA_IN"),
+ MTK_PIN(PINCTRL_PIN(72, "I2S0_DATA_IN"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 53),
MTK_FUNCTION(0, "GPIO72"),
@@ -711,8 +638,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(6, "WCN_I2S_DI"),
MTK_FUNCTION(7, "DBG_MON_B[2]")
),
- MTK_PIN(
- PINCTRL_PIN(73, "I2S0_LRCK"),
+ MTK_PIN(PINCTRL_PIN(73, "I2S0_LRCK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 54),
MTK_FUNCTION(0, "GPIO73"),
@@ -721,8 +647,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(6, "WCN_I2S_LRCK"),
MTK_FUNCTION(7, "DBG_MON_B[5]")
),
- MTK_PIN(
- PINCTRL_PIN(74, "I2S0_BCK"),
+ MTK_PIN(PINCTRL_PIN(74, "I2S0_BCK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 55),
MTK_FUNCTION(0, "GPIO74"),
@@ -731,68 +656,59 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(6, "WCN_I2S_BCK"),
MTK_FUNCTION(7, "DBG_MON_B[4]")
),
- MTK_PIN(
- PINCTRL_PIN(75, "SDA0"),
+ MTK_PIN(PINCTRL_PIN(75, "SDA0"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 56),
MTK_FUNCTION(0, "GPIO75"),
MTK_FUNCTION(1, "SDA0")
),
- MTK_PIN(
- PINCTRL_PIN(76, "SCL0"),
+ MTK_PIN(PINCTRL_PIN(76, "SCL0"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 57),
MTK_FUNCTION(0, "GPIO76"),
MTK_FUNCTION(1, "SCL0")
),
- MTK_PIN(
- PINCTRL_PIN(77, "SDA2"),
+ MTK_PIN(PINCTRL_PIN(77, "SDA2"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 58),
MTK_FUNCTION(0, "GPIO77"),
MTK_FUNCTION(1, "SDA2")
),
- MTK_PIN(
- PINCTRL_PIN(78, "SCL2"),
+ MTK_PIN(PINCTRL_PIN(78, "SCL2"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 59),
MTK_FUNCTION(0, "GPIO78"),
MTK_FUNCTION(1, "SCL2")
),
- MTK_PIN(
- PINCTRL_PIN(79, "URXD0"),
+ MTK_PIN(PINCTRL_PIN(79, "URXD0"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 60),
MTK_FUNCTION(0, "GPIO79"),
MTK_FUNCTION(1, "URXD0"),
MTK_FUNCTION(2, "UTXD0")
),
- MTK_PIN(
- PINCTRL_PIN(80, "UTXD0"),
+ MTK_PIN(PINCTRL_PIN(80, "UTXD0"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 61),
MTK_FUNCTION(0, "GPIO80"),
MTK_FUNCTION(1, "UTXD0"),
MTK_FUNCTION(2, "URXD0")
),
- MTK_PIN(
- PINCTRL_PIN(81, "URXD1"),
+ MTK_PIN(PINCTRL_PIN(81, "URXD1"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 62),
MTK_FUNCTION(0, "GPIO81"),
MTK_FUNCTION(1, "URXD1"),
MTK_FUNCTION(2, "UTXD1")
),
- MTK_PIN(
- PINCTRL_PIN(82, "UTXD1"),
+ MTK_PIN(PINCTRL_PIN(82, "UTXD1"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 63),
MTK_FUNCTION(0, "GPIO82"),
MTK_FUNCTION(1, "UTXD1"),
MTK_FUNCTION(2, "URXD1")
),
- MTK_PIN(
- PINCTRL_PIN(83, "LCM_RST"),
+ MTK_PIN(PINCTRL_PIN(83, "LCM_RST"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 64),
MTK_FUNCTION(0, "GPIO83"),
@@ -800,16 +716,14 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(2, "VDAC_CK_XI"),
MTK_FUNCTION(7, "DBG_MON_B[1]")
),
- MTK_PIN(
- PINCTRL_PIN(84, "DSI_TE"),
+ MTK_PIN(PINCTRL_PIN(84, "DSI_TE"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 65),
MTK_FUNCTION(0, "GPIO84"),
MTK_FUNCTION(1, "DSI_TE"),
MTK_FUNCTION(7, "DBG_MON_B[0]")
),
- MTK_PIN(
- PINCTRL_PIN(85, "MSDC2_CMD"),
+ MTK_PIN(PINCTRL_PIN(85, "MSDC2_CMD"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 66),
MTK_FUNCTION(0, "GPIO85"),
@@ -818,8 +732,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(3, "SDA1"),
MTK_FUNCTION(6, "I2SOUT_BCK")
),
- MTK_PIN(
- PINCTRL_PIN(86, "MSDC2_CLK"),
+ MTK_PIN(PINCTRL_PIN(86, "MSDC2_CLK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 67),
MTK_FUNCTION(0, "GPIO86"),
@@ -828,8 +741,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(3, "SCL1"),
MTK_FUNCTION(6, "I2SOUT_LRCK")
),
- MTK_PIN(
- PINCTRL_PIN(87, "MSDC2_DAT0"),
+ MTK_PIN(PINCTRL_PIN(87, "MSDC2_DAT0"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 68),
MTK_FUNCTION(0, "GPIO87"),
@@ -838,8 +750,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(5, "UTXD0"),
MTK_FUNCTION(6, "I2SOUT_DATA_OUT")
),
- MTK_PIN(
- PINCTRL_PIN(88, "MSDC2_DAT1"),
+ MTK_PIN(PINCTRL_PIN(88, "MSDC2_DAT1"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 71),
MTK_FUNCTION(0, "GPIO88"),
@@ -849,8 +760,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(5, "URXD0"),
MTK_FUNCTION(6, "PWM1")
),
- MTK_PIN(
- PINCTRL_PIN(89, "MSDC2_DAT2"),
+ MTK_PIN(PINCTRL_PIN(89, "MSDC2_DAT2"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 72),
MTK_FUNCTION(0, "GPIO89"),
@@ -860,8 +770,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(5, "UTXD1"),
MTK_FUNCTION(6, "PWM2")
),
- MTK_PIN(
- PINCTRL_PIN(90, "MSDC2_DAT3"),
+ MTK_PIN(PINCTRL_PIN(90, "MSDC2_DAT3"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 73),
MTK_FUNCTION(0, "GPIO90"),
@@ -872,78 +781,67 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(5, "URXD1"),
MTK_FUNCTION(6, "PWM3")
),
- MTK_PIN(
- PINCTRL_PIN(91, "TDN3"),
+ MTK_PIN(PINCTRL_PIN(91, "TDN3"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPI91"),
MTK_FUNCTION(1, "TDN3")
),
- MTK_PIN(
- PINCTRL_PIN(92, "TDP3"),
+ MTK_PIN(PINCTRL_PIN(92, "TDP3"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPI92"),
MTK_FUNCTION(1, "TDP3")
),
- MTK_PIN(
- PINCTRL_PIN(93, "TDN2"),
+ MTK_PIN(PINCTRL_PIN(93, "TDN2"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPI93"),
MTK_FUNCTION(1, "TDN2")
),
- MTK_PIN(
- PINCTRL_PIN(94, "TDP2"),
+ MTK_PIN(PINCTRL_PIN(94, "TDP2"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPI94"),
MTK_FUNCTION(1, "TDP2")
),
- MTK_PIN(
- PINCTRL_PIN(95, "TCN"),
+ MTK_PIN(PINCTRL_PIN(95, "TCN"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPI95"),
MTK_FUNCTION(1, "TCN")
),
- MTK_PIN(
- PINCTRL_PIN(96, "TCP"),
+ MTK_PIN(PINCTRL_PIN(96, "TCP"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPI96"),
MTK_FUNCTION(1, "TCP")
),
- MTK_PIN(
- PINCTRL_PIN(97, "TDN1"),
+ MTK_PIN(PINCTRL_PIN(97, "TDN1"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPI97"),
MTK_FUNCTION(1, "TDN1")
),
- MTK_PIN(
- PINCTRL_PIN(98, "TDP1"),
+ MTK_PIN(PINCTRL_PIN(98, "TDP1"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPI98"),
MTK_FUNCTION(1, "TDP1")
),
- MTK_PIN(
- PINCTRL_PIN(99, "TDN0"),
+ MTK_PIN(PINCTRL_PIN(99, "TDN0"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPI99"),
MTK_FUNCTION(1, "TDN0")
),
- MTK_PIN(
- PINCTRL_PIN(100, "TDP0"),
+ MTK_PIN(PINCTRL_PIN(100, "TDP0"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPI100"),
MTK_FUNCTION(1, "TDP0")
),
- MTK_PIN(
- PINCTRL_PIN(101, "SPI2_CSN"),
+ MTK_PIN(PINCTRL_PIN(101, "SPI2_CSN"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 74),
MTK_FUNCTION(0, "GPIO101"),
@@ -951,8 +849,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(3, "SCL3"),
MTK_FUNCTION(4, "KROW0")
),
- MTK_PIN(
- PINCTRL_PIN(102, "SPI2_MI"),
+ MTK_PIN(PINCTRL_PIN(102, "SPI2_MI"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 75),
MTK_FUNCTION(0, "GPIO102"),
@@ -961,8 +858,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(3, "SDA3"),
MTK_FUNCTION(4, "KROW1")
),
- MTK_PIN(
- PINCTRL_PIN(103, "SPI2_MO"),
+ MTK_PIN(PINCTRL_PIN(103, "SPI2_MO"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 76),
MTK_FUNCTION(0, "GPIO103"),
@@ -971,8 +867,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(3, "SCL3"),
MTK_FUNCTION(4, "KROW2")
),
- MTK_PIN(
- PINCTRL_PIN(104, "SPI2_CLK"),
+ MTK_PIN(PINCTRL_PIN(104, "SPI2_CLK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 77),
MTK_FUNCTION(0, "GPIO104"),
@@ -980,8 +875,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(3, "SDA3"),
MTK_FUNCTION(4, "KROW3")
),
- MTK_PIN(
- PINCTRL_PIN(105, "MSDC1_CMD"),
+ MTK_PIN(PINCTRL_PIN(105, "MSDC1_CMD"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 78),
MTK_FUNCTION(0, "GPIO105"),
@@ -991,8 +885,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(6, "I2SOUT_BCK"),
MTK_FUNCTION(7, "DBG_MON_B[27]")
),
- MTK_PIN(
- PINCTRL_PIN(106, "MSDC1_CLK"),
+ MTK_PIN(PINCTRL_PIN(106, "MSDC1_CLK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 79),
MTK_FUNCTION(0, "GPIO106"),
@@ -1002,8 +895,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(6, "I2SOUT_LRCK"),
MTK_FUNCTION(7, "DBG_MON_B[28]")
),
- MTK_PIN(
- PINCTRL_PIN(107, "MSDC1_DAT0"),
+ MTK_PIN(PINCTRL_PIN(107, "MSDC1_DAT0"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 80),
MTK_FUNCTION(0, "GPIO107"),
@@ -1013,8 +905,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(6, "I2SOUT_DATA_OUT"),
MTK_FUNCTION(7, "DBG_MON_B[26]")
),
- MTK_PIN(
- PINCTRL_PIN(108, "MSDC1_DAT1"),
+ MTK_PIN(PINCTRL_PIN(108, "MSDC1_DAT1"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 81),
MTK_FUNCTION(0, "GPIO108"),
@@ -1025,8 +916,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(6, "PWM1"),
MTK_FUNCTION(7, "DBG_MON_B[25]")
),
- MTK_PIN(
- PINCTRL_PIN(109, "MSDC1_DAT2"),
+ MTK_PIN(PINCTRL_PIN(109, "MSDC1_DAT2"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 82),
MTK_FUNCTION(0, "GPIO109"),
@@ -1037,8 +927,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(6, "PWM2"),
MTK_FUNCTION(7, "DBG_MON_B[24]")
),
- MTK_PIN(
- PINCTRL_PIN(110, "MSDC1_DAT3"),
+ MTK_PIN(PINCTRL_PIN(110, "MSDC1_DAT3"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 83),
MTK_FUNCTION(0, "GPIO110"),
@@ -1050,88 +939,77 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(6, "PWM3"),
MTK_FUNCTION(7, "DBG_MON_B[23]")
),
- MTK_PIN(
- PINCTRL_PIN(111, "MSDC0_DAT7"),
+ MTK_PIN(PINCTRL_PIN(111, "MSDC0_DAT7"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 84),
MTK_FUNCTION(0, "GPIO111"),
MTK_FUNCTION(1, "MSDC0_DAT7"),
MTK_FUNCTION(4, "NLD7")
),
- MTK_PIN(
- PINCTRL_PIN(112, "MSDC0_DAT6"),
+ MTK_PIN(PINCTRL_PIN(112, "MSDC0_DAT6"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 85),
MTK_FUNCTION(0, "GPIO112"),
MTK_FUNCTION(1, "MSDC0_DAT6"),
MTK_FUNCTION(4, "NLD6")
),
- MTK_PIN(
- PINCTRL_PIN(113, "MSDC0_DAT5"),
+ MTK_PIN(PINCTRL_PIN(113, "MSDC0_DAT5"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 86),
MTK_FUNCTION(0, "GPIO113"),
MTK_FUNCTION(1, "MSDC0_DAT5"),
MTK_FUNCTION(4, "NLD5")
),
- MTK_PIN(
- PINCTRL_PIN(114, "MSDC0_DAT4"),
+ MTK_PIN(PINCTRL_PIN(114, "MSDC0_DAT4"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 87),
MTK_FUNCTION(0, "GPIO114"),
MTK_FUNCTION(1, "MSDC0_DAT4"),
MTK_FUNCTION(4, "NLD4")
),
- MTK_PIN(
- PINCTRL_PIN(115, "MSDC0_RSTB"),
+ MTK_PIN(PINCTRL_PIN(115, "MSDC0_RSTB"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 88),
MTK_FUNCTION(0, "GPIO115"),
MTK_FUNCTION(1, "MSDC0_RSTB"),
MTK_FUNCTION(4, "NLD8")
),
- MTK_PIN(
- PINCTRL_PIN(116, "MSDC0_CMD"),
+ MTK_PIN(PINCTRL_PIN(116, "MSDC0_CMD"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 89),
MTK_FUNCTION(0, "GPIO116"),
MTK_FUNCTION(1, "MSDC0_CMD"),
MTK_FUNCTION(4, "NALE")
),
- MTK_PIN(
- PINCTRL_PIN(117, "MSDC0_CLK"),
+ MTK_PIN(PINCTRL_PIN(117, "MSDC0_CLK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 90),
MTK_FUNCTION(0, "GPIO117"),
MTK_FUNCTION(1, "MSDC0_CLK"),
MTK_FUNCTION(4, "NWEB")
),
- MTK_PIN(
- PINCTRL_PIN(118, "MSDC0_DAT3"),
+ MTK_PIN(PINCTRL_PIN(118, "MSDC0_DAT3"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 91),
MTK_FUNCTION(0, "GPIO118"),
MTK_FUNCTION(1, "MSDC0_DAT3"),
MTK_FUNCTION(4, "NLD3")
),
- MTK_PIN(
- PINCTRL_PIN(119, "MSDC0_DAT2"),
+ MTK_PIN(PINCTRL_PIN(119, "MSDC0_DAT2"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 92),
MTK_FUNCTION(0, "GPIO119"),
MTK_FUNCTION(1, "MSDC0_DAT2"),
MTK_FUNCTION(4, "NLD2")
),
- MTK_PIN(
- PINCTRL_PIN(120, "MSDC0_DAT1"),
+ MTK_PIN(PINCTRL_PIN(120, "MSDC0_DAT1"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 93),
MTK_FUNCTION(0, "GPIO120"),
MTK_FUNCTION(1, "MSDC0_DAT1"),
MTK_FUNCTION(4, "NLD1")
),
- MTK_PIN(
- PINCTRL_PIN(121, "MSDC0_DAT0"),
+ MTK_PIN(PINCTRL_PIN(121, "MSDC0_DAT0"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 94),
MTK_FUNCTION(0, "GPIO121"),
@@ -1139,8 +1017,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(4, "NLD0"),
MTK_FUNCTION(5, "WATCHDOG")
),
- MTK_PIN(
- PINCTRL_PIN(122, "CEC"),
+ MTK_PIN(PINCTRL_PIN(122, "CEC"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 95),
MTK_FUNCTION(0, "GPIO122"),
@@ -1148,8 +1025,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(4, "SDA2"),
MTK_FUNCTION(5, "URXD0")
),
- MTK_PIN(
- PINCTRL_PIN(123, "HTPLG"),
+ MTK_PIN(PINCTRL_PIN(123, "HTPLG"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 96),
MTK_FUNCTION(0, "GPIO123"),
@@ -1157,8 +1033,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(4, "SCL2"),
MTK_FUNCTION(5, "UTXD0")
),
- MTK_PIN(
- PINCTRL_PIN(124, "HDMISCK"),
+ MTK_PIN(PINCTRL_PIN(124, "HDMISCK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 97),
MTK_FUNCTION(0, "GPIO124"),
@@ -1166,8 +1041,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(4, "SDA1"),
MTK_FUNCTION(5, "PWM3")
),
- MTK_PIN(
- PINCTRL_PIN(125, "HDMISD"),
+ MTK_PIN(PINCTRL_PIN(125, "HDMISD"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 98),
MTK_FUNCTION(0, "GPIO125"),
@@ -1175,8 +1049,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(4, "SCL1"),
MTK_FUNCTION(5, "PWM4")
),
- MTK_PIN(
- PINCTRL_PIN(126, "I2S0_MCLK"),
+ MTK_PIN(PINCTRL_PIN(126, "I2S0_MCLK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 99),
MTK_FUNCTION(0, "GPIO126"),
@@ -1184,481 +1057,413 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(6, "WCN_I2S_MCLK"),
MTK_FUNCTION(7, "DBG_MON_B[6]")
),
- MTK_PIN(
- PINCTRL_PIN(127, "RAMBUF_IDATA0"),
+ MTK_PIN(PINCTRL_PIN(127, "RAMBUF_IDATA0"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO127"),
MTK_FUNCTION(1, "RAMBUF_IDATA0")
),
- MTK_PIN(
- PINCTRL_PIN(128, "RAMBUF_IDATA1"),
+ MTK_PIN(PINCTRL_PIN(128, "RAMBUF_IDATA1"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO128"),
MTK_FUNCTION(1, "RAMBUF_IDATA1")
),
- MTK_PIN(
- PINCTRL_PIN(129, "RAMBUF_IDATA2"),
+ MTK_PIN(PINCTRL_PIN(129, "RAMBUF_IDATA2"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO129"),
MTK_FUNCTION(1, "RAMBUF_IDATA2")
),
- MTK_PIN(
- PINCTRL_PIN(130, "RAMBUF_IDATA3"),
+ MTK_PIN(PINCTRL_PIN(130, "RAMBUF_IDATA3"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO130"),
MTK_FUNCTION(1, "RAMBUF_IDATA3")
),
- MTK_PIN(
- PINCTRL_PIN(131, "RAMBUF_IDATA4"),
+ MTK_PIN(PINCTRL_PIN(131, "RAMBUF_IDATA4"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO131"),
MTK_FUNCTION(1, "RAMBUF_IDATA4")
),
- MTK_PIN(
- PINCTRL_PIN(132, "RAMBUF_IDATA5"),
+ MTK_PIN(PINCTRL_PIN(132, "RAMBUF_IDATA5"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO132"),
MTK_FUNCTION(1, "RAMBUF_IDATA5")
),
- MTK_PIN(
- PINCTRL_PIN(133, "RAMBUF_IDATA6"),
+ MTK_PIN(PINCTRL_PIN(133, "RAMBUF_IDATA6"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO133"),
MTK_FUNCTION(1, "RAMBUF_IDATA6")
),
- MTK_PIN(
- PINCTRL_PIN(134, "RAMBUF_IDATA7"),
+ MTK_PIN(PINCTRL_PIN(134, "RAMBUF_IDATA7"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO134"),
MTK_FUNCTION(1, "RAMBUF_IDATA7")
),
- MTK_PIN(
- PINCTRL_PIN(135, "RAMBUF_IDATA8"),
+ MTK_PIN(PINCTRL_PIN(135, "RAMBUF_IDATA8"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO135"),
MTK_FUNCTION(1, "RAMBUF_IDATA8")
),
- MTK_PIN(
- PINCTRL_PIN(136, "RAMBUF_IDATA9"),
+ MTK_PIN(PINCTRL_PIN(136, "RAMBUF_IDATA9"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO136"),
MTK_FUNCTION(1, "RAMBUF_IDATA9")
),
- MTK_PIN(
- PINCTRL_PIN(137, "RAMBUF_IDATA10"),
+ MTK_PIN(PINCTRL_PIN(137, "RAMBUF_IDATA10"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO137"),
MTK_FUNCTION(1, "RAMBUF_IDATA10")
),
- MTK_PIN(
- PINCTRL_PIN(138, "RAMBUF_IDATA11"),
+ MTK_PIN(PINCTRL_PIN(138, "RAMBUF_IDATA11"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO138"),
MTK_FUNCTION(1, "RAMBUF_IDATA11")
),
- MTK_PIN(
- PINCTRL_PIN(139, "RAMBUF_IDATA12"),
+ MTK_PIN(PINCTRL_PIN(139, "RAMBUF_IDATA12"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO139"),
MTK_FUNCTION(1, "RAMBUF_IDATA12")
),
- MTK_PIN(
- PINCTRL_PIN(140, "RAMBUF_IDATA13"),
+ MTK_PIN(PINCTRL_PIN(140, "RAMBUF_IDATA13"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO140"),
MTK_FUNCTION(1, "RAMBUF_IDATA13")
),
- MTK_PIN(
- PINCTRL_PIN(141, "RAMBUF_IDATA14"),
+ MTK_PIN(PINCTRL_PIN(141, "RAMBUF_IDATA14"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO141"),
MTK_FUNCTION(1, "RAMBUF_IDATA14")
),
- MTK_PIN(
- PINCTRL_PIN(142, "RAMBUF_IDATA15"),
+ MTK_PIN(PINCTRL_PIN(142, "RAMBUF_IDATA15"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO142"),
MTK_FUNCTION(1, "RAMBUF_IDATA15")
),
- MTK_PIN(
- PINCTRL_PIN(143, "RAMBUF_ODATA0"),
+ MTK_PIN(PINCTRL_PIN(143, "RAMBUF_ODATA0"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO143"),
MTK_FUNCTION(1, "RAMBUF_ODATA0")
),
- MTK_PIN(
- PINCTRL_PIN(144, "RAMBUF_ODATA1"),
+ MTK_PIN(PINCTRL_PIN(144, "RAMBUF_ODATA1"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO144"),
MTK_FUNCTION(1, "RAMBUF_ODATA1")
),
- MTK_PIN(
- PINCTRL_PIN(145, "RAMBUF_ODATA2"),
+ MTK_PIN(PINCTRL_PIN(145, "RAMBUF_ODATA2"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO145"),
MTK_FUNCTION(1, "RAMBUF_ODATA2")
),
- MTK_PIN(
- PINCTRL_PIN(146, "RAMBUF_ODATA3"),
+ MTK_PIN(PINCTRL_PIN(146, "RAMBUF_ODATA3"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO146"),
MTK_FUNCTION(1, "RAMBUF_ODATA3")
),
- MTK_PIN(
- PINCTRL_PIN(147, "RAMBUF_ODATA4"),
+ MTK_PIN(PINCTRL_PIN(147, "RAMBUF_ODATA4"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO147"),
MTK_FUNCTION(1, "RAMBUF_ODATA4")
),
- MTK_PIN(
- PINCTRL_PIN(148, "RAMBUF_ODATA5"),
+ MTK_PIN(PINCTRL_PIN(148, "RAMBUF_ODATA5"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO148"),
MTK_FUNCTION(1, "RAMBUF_ODATA5")
),
- MTK_PIN(
- PINCTRL_PIN(149, "RAMBUF_ODATA6"),
+ MTK_PIN(PINCTRL_PIN(149, "RAMBUF_ODATA6"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO149"),
MTK_FUNCTION(1, "RAMBUF_ODATA6")
),
- MTK_PIN(
- PINCTRL_PIN(150, "RAMBUF_ODATA7"),
+ MTK_PIN(PINCTRL_PIN(150, "RAMBUF_ODATA7"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO150"),
MTK_FUNCTION(1, "RAMBUF_ODATA7")
),
- MTK_PIN(
- PINCTRL_PIN(151, "RAMBUF_ODATA8"),
+ MTK_PIN(PINCTRL_PIN(151, "RAMBUF_ODATA8"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO151"),
MTK_FUNCTION(1, "RAMBUF_ODATA8")
),
- MTK_PIN(
- PINCTRL_PIN(152, "RAMBUF_ODATA9"),
+ MTK_PIN(PINCTRL_PIN(152, "RAMBUF_ODATA9"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO152"),
MTK_FUNCTION(1, "RAMBUF_ODATA9")
),
- MTK_PIN(
- PINCTRL_PIN(153, "RAMBUF_ODATA10"),
+ MTK_PIN(PINCTRL_PIN(153, "RAMBUF_ODATA10"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO153"),
MTK_FUNCTION(1, "RAMBUF_ODATA10")
),
- MTK_PIN(
- PINCTRL_PIN(154, "RAMBUF_ODATA11"),
+ MTK_PIN(PINCTRL_PIN(154, "RAMBUF_ODATA11"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO154"),
MTK_FUNCTION(1, "RAMBUF_ODATA11")
),
- MTK_PIN(
- PINCTRL_PIN(155, "RAMBUF_ODATA12"),
+ MTK_PIN(PINCTRL_PIN(155, "RAMBUF_ODATA12"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO155"),
MTK_FUNCTION(1, "RAMBUF_ODATA12")
),
- MTK_PIN(
- PINCTRL_PIN(156, "RAMBUF_ODATA13"),
+ MTK_PIN(PINCTRL_PIN(156, "RAMBUF_ODATA13"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO156"),
MTK_FUNCTION(1, "RAMBUF_ODATA13")
),
- MTK_PIN(
- PINCTRL_PIN(157, "RAMBUF_ODATA14"),
+ MTK_PIN(PINCTRL_PIN(157, "RAMBUF_ODATA14"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO157"),
MTK_FUNCTION(1, "RAMBUF_ODATA14")
),
- MTK_PIN(
- PINCTRL_PIN(158, "RAMBUF_ODATA15"),
+ MTK_PIN(PINCTRL_PIN(158, "RAMBUF_ODATA15"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO158"),
MTK_FUNCTION(1, "RAMBUF_ODATA15")
),
- MTK_PIN(
- PINCTRL_PIN(159, "RAMBUF_BE0"),
+ MTK_PIN(PINCTRL_PIN(159, "RAMBUF_BE0"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO159"),
MTK_FUNCTION(1, "RAMBUF_BE0")
),
- MTK_PIN(
- PINCTRL_PIN(160, "RAMBUF_BE1"),
+ MTK_PIN(PINCTRL_PIN(160, "RAMBUF_BE1"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO160"),
MTK_FUNCTION(1, "RAMBUF_BE1")
),
- MTK_PIN(
- PINCTRL_PIN(161, "AP2PT_INT"),
+ MTK_PIN(PINCTRL_PIN(161, "AP2PT_INT"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO161"),
MTK_FUNCTION(1, "AP2PT_INT")
),
- MTK_PIN(
- PINCTRL_PIN(162, "AP2PT_INT_CLR"),
+ MTK_PIN(PINCTRL_PIN(162, "AP2PT_INT_CLR"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO162"),
MTK_FUNCTION(1, "AP2PT_INT_CLR")
),
- MTK_PIN(
- PINCTRL_PIN(163, "PT2AP_INT"),
+ MTK_PIN(PINCTRL_PIN(163, "PT2AP_INT"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO163"),
MTK_FUNCTION(1, "PT2AP_INT")
),
- MTK_PIN(
- PINCTRL_PIN(164, "PT2AP_INT_CLR"),
+ MTK_PIN(PINCTRL_PIN(164, "PT2AP_INT_CLR"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO164"),
MTK_FUNCTION(1, "PT2AP_INT_CLR")
),
- MTK_PIN(
- PINCTRL_PIN(165, "AP2UP_INT"),
+ MTK_PIN(PINCTRL_PIN(165, "AP2UP_INT"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO165"),
MTK_FUNCTION(1, "AP2UP_INT")
),
- MTK_PIN(
- PINCTRL_PIN(166, "AP2UP_INT_CLR"),
+ MTK_PIN(PINCTRL_PIN(166, "AP2UP_INT_CLR"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO166"),
MTK_FUNCTION(1, "AP2UP_INT_CLR")
),
- MTK_PIN(
- PINCTRL_PIN(167, "UP2AP_INT"),
+ MTK_PIN(PINCTRL_PIN(167, "UP2AP_INT"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO167"),
MTK_FUNCTION(1, "UP2AP_INT")
),
- MTK_PIN(
- PINCTRL_PIN(168, "UP2AP_INT_CLR"),
+ MTK_PIN(PINCTRL_PIN(168, "UP2AP_INT_CLR"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO168"),
MTK_FUNCTION(1, "UP2AP_INT_CLR")
),
- MTK_PIN(
- PINCTRL_PIN(169, "RAMBUF_ADDR0"),
+ MTK_PIN(PINCTRL_PIN(169, "RAMBUF_ADDR0"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO169"),
MTK_FUNCTION(1, "RAMBUF_ADDR0")
),
- MTK_PIN(
- PINCTRL_PIN(170, "RAMBUF_ADDR1"),
+ MTK_PIN(PINCTRL_PIN(170, "RAMBUF_ADDR1"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO170"),
MTK_FUNCTION(1, "RAMBUF_ADDR1")
),
- MTK_PIN(
- PINCTRL_PIN(171, "RAMBUF_ADDR2"),
+ MTK_PIN(PINCTRL_PIN(171, "RAMBUF_ADDR2"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO171"),
MTK_FUNCTION(1, "RAMBUF_ADDR2")
),
- MTK_PIN(
- PINCTRL_PIN(172, "RAMBUF_ADDR3"),
+ MTK_PIN(PINCTRL_PIN(172, "RAMBUF_ADDR3"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO172"),
MTK_FUNCTION(1, "RAMBUF_ADDR3")
),
- MTK_PIN(
- PINCTRL_PIN(173, "RAMBUF_ADDR4"),
+ MTK_PIN(PINCTRL_PIN(173, "RAMBUF_ADDR4"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO173"),
MTK_FUNCTION(1, "RAMBUF_ADDR4")
),
- MTK_PIN(
- PINCTRL_PIN(174, "RAMBUF_ADDR5"),
+ MTK_PIN(PINCTRL_PIN(174, "RAMBUF_ADDR5"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO174"),
MTK_FUNCTION(1, "RAMBUF_ADDR5")
),
- MTK_PIN(
- PINCTRL_PIN(175, "RAMBUF_ADDR6"),
+ MTK_PIN(PINCTRL_PIN(175, "RAMBUF_ADDR6"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO175"),
MTK_FUNCTION(1, "RAMBUF_ADDR6")
),
- MTK_PIN(
- PINCTRL_PIN(176, "RAMBUF_ADDR7"),
+ MTK_PIN(PINCTRL_PIN(176, "RAMBUF_ADDR7"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO176"),
MTK_FUNCTION(1, "RAMBUF_ADDR7")
),
- MTK_PIN(
- PINCTRL_PIN(177, "RAMBUF_ADDR8"),
+ MTK_PIN(PINCTRL_PIN(177, "RAMBUF_ADDR8"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO177"),
MTK_FUNCTION(1, "RAMBUF_ADDR8")
),
- MTK_PIN(
- PINCTRL_PIN(178, "RAMBUF_ADDR9"),
+ MTK_PIN(PINCTRL_PIN(178, "RAMBUF_ADDR9"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO178"),
MTK_FUNCTION(1, "RAMBUF_ADDR9")
),
- MTK_PIN(
- PINCTRL_PIN(179, "RAMBUF_ADDR10"),
+ MTK_PIN(PINCTRL_PIN(179, "RAMBUF_ADDR10"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO179"),
MTK_FUNCTION(1, "RAMBUF_ADDR10")
),
- MTK_PIN(
- PINCTRL_PIN(180, "RAMBUF_RW"),
+ MTK_PIN(PINCTRL_PIN(180, "RAMBUF_RW"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO180"),
MTK_FUNCTION(1, "RAMBUF_RW")
),
- MTK_PIN(
- PINCTRL_PIN(181, "RAMBUF_LAST"),
+ MTK_PIN(PINCTRL_PIN(181, "RAMBUF_LAST"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO181"),
MTK_FUNCTION(1, "RAMBUF_LAST")
),
- MTK_PIN(
- PINCTRL_PIN(182, "RAMBUF_HP"),
+ MTK_PIN(PINCTRL_PIN(182, "RAMBUF_HP"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO182"),
MTK_FUNCTION(1, "RAMBUF_HP")
),
- MTK_PIN(
- PINCTRL_PIN(183, "RAMBUF_REQ"),
+ MTK_PIN(PINCTRL_PIN(183, "RAMBUF_REQ"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO183"),
MTK_FUNCTION(1, "RAMBUF_REQ")
),
- MTK_PIN(
- PINCTRL_PIN(184, "RAMBUF_ALE"),
+ MTK_PIN(PINCTRL_PIN(184, "RAMBUF_ALE"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO184"),
MTK_FUNCTION(1, "RAMBUF_ALE")
),
- MTK_PIN(
- PINCTRL_PIN(185, "RAMBUF_DLE"),
+ MTK_PIN(PINCTRL_PIN(185, "RAMBUF_DLE"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO185"),
MTK_FUNCTION(1, "RAMBUF_DLE")
),
- MTK_PIN(
- PINCTRL_PIN(186, "RAMBUF_WDLE"),
+ MTK_PIN(PINCTRL_PIN(186, "RAMBUF_WDLE"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO186"),
MTK_FUNCTION(1, "RAMBUF_WDLE")
),
- MTK_PIN(
- PINCTRL_PIN(187, "RAMBUF_O_CLK"),
+ MTK_PIN(PINCTRL_PIN(187, "RAMBUF_O_CLK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO187"),
MTK_FUNCTION(1, "RAMBUF_O_CLK")
),
- MTK_PIN(
- PINCTRL_PIN(188, "I2S2_MCLK"),
+ MTK_PIN(PINCTRL_PIN(188, "I2S2_MCLK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 100),
MTK_FUNCTION(0, "GPIO188"),
MTK_FUNCTION(1, "I2S2_MCLK")
),
- MTK_PIN(
- PINCTRL_PIN(189, "I2S3_DATA"),
+ MTK_PIN(PINCTRL_PIN(189, "I2S3_DATA"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 101),
MTK_FUNCTION(0, "GPIO189"),
MTK_FUNCTION(2, "I2S3_DATA_BYPS"),
MTK_FUNCTION(3, "PCM_TX")
),
- MTK_PIN(
- PINCTRL_PIN(190, "I2S3_DATA_IN"),
+ MTK_PIN(PINCTRL_PIN(190, "I2S3_DATA_IN"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 102),
MTK_FUNCTION(0, "GPIO190"),
MTK_FUNCTION(1, "I2S3_DATA_IN"),
MTK_FUNCTION(3, "PCM_RX")
),
- MTK_PIN(
- PINCTRL_PIN(191, "I2S3_BCK"),
+ MTK_PIN(PINCTRL_PIN(191, "I2S3_BCK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 103),
MTK_FUNCTION(0, "GPIO191"),
MTK_FUNCTION(1, "I2S3_BCK"),
MTK_FUNCTION(3, "PCM_CLK0")
),
- MTK_PIN(
- PINCTRL_PIN(192, "I2S3_LRCK"),
+ MTK_PIN(PINCTRL_PIN(192, "I2S3_LRCK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 104),
MTK_FUNCTION(0, "GPIO192"),
MTK_FUNCTION(1, "I2S3_LRCK"),
MTK_FUNCTION(3, "PCM_SYNC")
),
- MTK_PIN(
- PINCTRL_PIN(193, "I2S3_MCLK"),
+ MTK_PIN(PINCTRL_PIN(193, "I2S3_MCLK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 105),
MTK_FUNCTION(0, "GPIO193"),
MTK_FUNCTION(1, "I2S3_MCLK")
),
- MTK_PIN(
- PINCTRL_PIN(194, "I2S4_DATA"),
+ MTK_PIN(PINCTRL_PIN(194, "I2S4_DATA"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 106),
MTK_FUNCTION(0, "GPIO194"),
@@ -1666,39 +1471,34 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(2, "I2S4_DATA_BYPS"),
MTK_FUNCTION(3, "PCM_TX")
),
- MTK_PIN(
- PINCTRL_PIN(195, "I2S4_DATA_IN"),
+ MTK_PIN(PINCTRL_PIN(195, "I2S4_DATA_IN"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 107),
MTK_FUNCTION(0, "GPIO195"),
MTK_FUNCTION(1, "I2S4_DATA_IN"),
MTK_FUNCTION(3, "PCM_RX")
),
- MTK_PIN(
- PINCTRL_PIN(196, "I2S4_BCK"),
+ MTK_PIN(PINCTRL_PIN(196, "I2S4_BCK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 108),
MTK_FUNCTION(0, "GPIO196"),
MTK_FUNCTION(1, "I2S4_BCK"),
MTK_FUNCTION(3, "PCM_CLK0")
),
- MTK_PIN(
- PINCTRL_PIN(197, "I2S4_LRCK"),
+ MTK_PIN(PINCTRL_PIN(197, "I2S4_LRCK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 109),
MTK_FUNCTION(0, "GPIO197"),
MTK_FUNCTION(1, "I2S4_LRCK"),
MTK_FUNCTION(3, "PCM_SYNC")
),
- MTK_PIN(
- PINCTRL_PIN(198, "I2S4_MCLK"),
+ MTK_PIN(PINCTRL_PIN(198, "I2S4_MCLK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 110),
MTK_FUNCTION(0, "GPIO198"),
MTK_FUNCTION(1, "I2S4_MCLK")
),
- MTK_PIN(
- PINCTRL_PIN(199, "SPI1_CLK"),
+ MTK_PIN(PINCTRL_PIN(199, "SPI1_CLK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 111),
MTK_FUNCTION(0, "GPIO199"),
@@ -1707,8 +1507,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(4, "KCOL3"),
MTK_FUNCTION(7, "DBG_MON_B[15]")
),
- MTK_PIN(
- PINCTRL_PIN(200, "SPDIF_OUT"),
+ MTK_PIN(PINCTRL_PIN(200, "SPDIF_OUT"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 112),
MTK_FUNCTION(0, "GPIO200"),
@@ -1717,8 +1516,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(6, "URXD2"),
MTK_FUNCTION(7, "DBG_MON_B[16]")
),
- MTK_PIN(
- PINCTRL_PIN(201, "SPDIF_IN0"),
+ MTK_PIN(PINCTRL_PIN(201, "SPDIF_IN0"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 113),
MTK_FUNCTION(0, "GPIO201"),
@@ -1727,15 +1525,13 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(6, "UTXD2"),
MTK_FUNCTION(7, "DBG_MON_B[17]")
),
- MTK_PIN(
- PINCTRL_PIN(202, "SPDIF_IN1"),
+ MTK_PIN(PINCTRL_PIN(202, "SPDIF_IN1"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 114),
MTK_FUNCTION(0, "GPIO202"),
MTK_FUNCTION(1, "SPDIF_IN1")
),
- MTK_PIN(
- PINCTRL_PIN(203, "PWM0"),
+ MTK_PIN(PINCTRL_PIN(203, "PWM0"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 115),
MTK_FUNCTION(0, "GPIO203"),
@@ -1745,8 +1541,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(7, "DBG_MON_B[18]"),
MTK_FUNCTION(9, "I2S2_DATA")
),
- MTK_PIN(
- PINCTRL_PIN(204, "PWM1"),
+ MTK_PIN(PINCTRL_PIN(204, "PWM1"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 116),
MTK_FUNCTION(0, "GPIO204"),
@@ -1756,8 +1551,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(7, "DBG_MON_B[19]"),
MTK_FUNCTION(9, "I2S3_DATA")
),
- MTK_PIN(
- PINCTRL_PIN(205, "PWM2"),
+ MTK_PIN(PINCTRL_PIN(205, "PWM2"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 117),
MTK_FUNCTION(0, "GPIO205"),
@@ -1766,8 +1560,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(5, "G1_TXD0"),
MTK_FUNCTION(7, "DBG_MON_B[20]")
),
- MTK_PIN(
- PINCTRL_PIN(206, "PWM3"),
+ MTK_PIN(PINCTRL_PIN(206, "PWM3"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 118),
MTK_FUNCTION(0, "GPIO206"),
@@ -1777,8 +1570,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(5, "G1_TXC"),
MTK_FUNCTION(7, "DBG_MON_B[21]")
),
- MTK_PIN(
- PINCTRL_PIN(207, "PWM4"),
+ MTK_PIN(PINCTRL_PIN(207, "PWM4"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 119),
MTK_FUNCTION(0, "GPIO207"),
@@ -1788,8 +1580,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(5, "G1_RXC"),
MTK_FUNCTION(7, "DBG_MON_B[22]")
),
- MTK_PIN(
- PINCTRL_PIN(208, "AUD_EXT_CK1"),
+ MTK_PIN(PINCTRL_PIN(208, "AUD_EXT_CK1"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 120),
MTK_FUNCTION(0, "GPIO208"),
@@ -1802,8 +1593,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(7, "DBG_MON_A[31]"),
MTK_FUNCTION(11, "PCIE0_PERST_N")
),
- MTK_PIN(
- PINCTRL_PIN(209, "AUD_EXT_CK2"),
+ MTK_PIN(PINCTRL_PIN(209, "AUD_EXT_CK2"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 121),
MTK_FUNCTION(0, "GPIO209"),
@@ -1815,190 +1605,163 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(7, "DBG_MON_A[32]"),
MTK_FUNCTION(11, "PCIE1_PERST_N")
),
- MTK_PIN(
- PINCTRL_PIN(210, "AUD_CLOCK"),
+ MTK_PIN(PINCTRL_PIN(210, "AUD_CLOCK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO210"),
MTK_FUNCTION(1, "AUD_CLOCK")
),
- MTK_PIN(
- PINCTRL_PIN(211, "DVP_RESET"),
+ MTK_PIN(PINCTRL_PIN(211, "DVP_RESET"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO211"),
MTK_FUNCTION(1, "DVP_RESET")
),
- MTK_PIN(
- PINCTRL_PIN(212, "DVP_CLOCK"),
+ MTK_PIN(PINCTRL_PIN(212, "DVP_CLOCK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO212"),
MTK_FUNCTION(1, "DVP_CLOCK")
),
- MTK_PIN(
- PINCTRL_PIN(213, "DVP_CS"),
+ MTK_PIN(PINCTRL_PIN(213, "DVP_CS"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO213"),
MTK_FUNCTION(1, "DVP_CS")
),
- MTK_PIN(
- PINCTRL_PIN(214, "DVP_CK"),
+ MTK_PIN(PINCTRL_PIN(214, "DVP_CK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO214"),
MTK_FUNCTION(1, "DVP_CK")
),
- MTK_PIN(
- PINCTRL_PIN(215, "DVP_DI"),
+ MTK_PIN(PINCTRL_PIN(215, "DVP_DI"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO215"),
MTK_FUNCTION(1, "DVP_DI")
),
- MTK_PIN(
- PINCTRL_PIN(216, "DVP_DO"),
+ MTK_PIN(PINCTRL_PIN(216, "DVP_DO"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO216"),
MTK_FUNCTION(1, "DVP_DO")
),
- MTK_PIN(
- PINCTRL_PIN(217, "AP_CS"),
+ MTK_PIN(PINCTRL_PIN(217, "AP_CS"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO217"),
MTK_FUNCTION(1, "AP_CS")
),
- MTK_PIN(
- PINCTRL_PIN(218, "AP_CK"),
+ MTK_PIN(PINCTRL_PIN(218, "AP_CK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO218"),
MTK_FUNCTION(1, "AP_CK")
),
- MTK_PIN(
- PINCTRL_PIN(219, "AP_DI"),
+ MTK_PIN(PINCTRL_PIN(219, "AP_DI"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO219"),
MTK_FUNCTION(1, "AP_DI")
),
- MTK_PIN(
- PINCTRL_PIN(220, "AP_DO"),
+ MTK_PIN(PINCTRL_PIN(220, "AP_DO"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO220"),
MTK_FUNCTION(1, "AP_DO")
),
- MTK_PIN(
- PINCTRL_PIN(221, "DVD_BCLK"),
+ MTK_PIN(PINCTRL_PIN(221, "DVD_BCLK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO221"),
MTK_FUNCTION(1, "DVD_BCLK")
),
- MTK_PIN(
- PINCTRL_PIN(222, "T8032_CLK"),
+ MTK_PIN(PINCTRL_PIN(222, "T8032_CLK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO222"),
MTK_FUNCTION(1, "T8032_CLK")
),
- MTK_PIN(
- PINCTRL_PIN(223, "AP_BCLK"),
+ MTK_PIN(PINCTRL_PIN(223, "AP_BCLK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO223"),
MTK_FUNCTION(1, "AP_BCLK")
),
- MTK_PIN(
- PINCTRL_PIN(224, "HOST_CS"),
+ MTK_PIN(PINCTRL_PIN(224, "HOST_CS"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO224"),
MTK_FUNCTION(1, "HOST_CS")
),
- MTK_PIN(
- PINCTRL_PIN(225, "HOST_CK"),
+ MTK_PIN(PINCTRL_PIN(225, "HOST_CK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO225"),
MTK_FUNCTION(1, "HOST_CK")
),
- MTK_PIN(
- PINCTRL_PIN(226, "HOST_DO0"),
+ MTK_PIN(PINCTRL_PIN(226, "HOST_DO0"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO226"),
MTK_FUNCTION(1, "HOST_DO0")
),
- MTK_PIN(
- PINCTRL_PIN(227, "HOST_DO1"),
+ MTK_PIN(PINCTRL_PIN(227, "HOST_DO1"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO227"),
MTK_FUNCTION(1, "HOST_DO1")
),
- MTK_PIN(
- PINCTRL_PIN(228, "SLV_CS"),
+ MTK_PIN(PINCTRL_PIN(228, "SLV_CS"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO228"),
MTK_FUNCTION(1, "SLV_CS")
),
- MTK_PIN(
- PINCTRL_PIN(229, "SLV_CK"),
+ MTK_PIN(PINCTRL_PIN(229, "SLV_CK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO229"),
MTK_FUNCTION(1, "SLV_CK")
),
- MTK_PIN(
- PINCTRL_PIN(230, "SLV_DI0"),
+ MTK_PIN(PINCTRL_PIN(230, "SLV_DI0"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO230"),
MTK_FUNCTION(1, "SLV_DI0")
),
- MTK_PIN(
- PINCTRL_PIN(231, "SLV_DI1"),
+ MTK_PIN(PINCTRL_PIN(231, "SLV_DI1"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO231"),
MTK_FUNCTION(1, "SLV_DI1")
),
- MTK_PIN(
- PINCTRL_PIN(232, "AP2DSP_INT"),
+ MTK_PIN(PINCTRL_PIN(232, "AP2DSP_INT"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO232"),
MTK_FUNCTION(1, "AP2DSP_INT")
),
- MTK_PIN(
- PINCTRL_PIN(233, "AP2DSP_INT_CLR"),
+ MTK_PIN(PINCTRL_PIN(233, "AP2DSP_INT_CLR"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO233"),
MTK_FUNCTION(1, "AP2DSP_INT_CLR")
),
- MTK_PIN(
- PINCTRL_PIN(234, "DSP2AP_INT"),
+ MTK_PIN(PINCTRL_PIN(234, "DSP2AP_INT"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO234"),
MTK_FUNCTION(1, "DSP2AP_INT")
),
- MTK_PIN(
- PINCTRL_PIN(235, "DSP2AP_INT_CLR"),
+ MTK_PIN(PINCTRL_PIN(235, "DSP2AP_INT_CLR"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO235"),
MTK_FUNCTION(1, "DSP2AP_INT_CLR")
),
- MTK_PIN(
- PINCTRL_PIN(236, "EXT_SDIO3"),
+ MTK_PIN(PINCTRL_PIN(236, "EXT_SDIO3"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 122),
MTK_FUNCTION(0, "GPIO236"),
@@ -2006,46 +1769,40 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(2, "IDDIG"),
MTK_FUNCTION(7, "DBG_MON_A[1]")
),
- MTK_PIN(
- PINCTRL_PIN(237, "EXT_SDIO2"),
+ MTK_PIN(PINCTRL_PIN(237, "EXT_SDIO2"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 123),
MTK_FUNCTION(0, "GPIO237"),
MTK_FUNCTION(1, "EXT_SDIO2"),
MTK_FUNCTION(2, "DRV_VBUS")
),
- MTK_PIN(
- PINCTRL_PIN(238, "EXT_SDIO1"),
+ MTK_PIN(PINCTRL_PIN(238, "EXT_SDIO1"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 124),
MTK_FUNCTION(0, "GPIO238"),
MTK_FUNCTION(1, "EXT_SDIO1"),
MTK_FUNCTION(2, "IDDIG_P1")
),
- MTK_PIN(
- PINCTRL_PIN(239, "EXT_SDIO0"),
+ MTK_PIN(PINCTRL_PIN(239, "EXT_SDIO0"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 125),
MTK_FUNCTION(0, "GPIO239"),
MTK_FUNCTION(1, "EXT_SDIO0"),
MTK_FUNCTION(2, "DRV_VBUS_P1")
),
- MTK_PIN(
- PINCTRL_PIN(240, "EXT_XCS"),
+ MTK_PIN(PINCTRL_PIN(240, "EXT_XCS"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 126),
MTK_FUNCTION(0, "GPIO240"),
MTK_FUNCTION(1, "EXT_XCS")
),
- MTK_PIN(
- PINCTRL_PIN(241, "EXT_SCK"),
+ MTK_PIN(PINCTRL_PIN(241, "EXT_SCK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 127),
MTK_FUNCTION(0, "GPIO241"),
MTK_FUNCTION(1, "EXT_SCK")
),
- MTK_PIN(
- PINCTRL_PIN(242, "URTS2"),
+ MTK_PIN(PINCTRL_PIN(242, "URTS2"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 128),
MTK_FUNCTION(0, "GPIO242"),
@@ -2055,8 +1812,7 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(4, "SCL1"),
MTK_FUNCTION(7, "DBG_MON_B[32]")
),
- MTK_PIN(
- PINCTRL_PIN(243, "UCTS2"),
+ MTK_PIN(PINCTRL_PIN(243, "UCTS2"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 129),
MTK_FUNCTION(0, "GPIO243"),
@@ -2066,265 +1822,229 @@ static const struct mtk_desc_pin mtk_pins_mt2701[] = {
MTK_FUNCTION(4, "SDA1"),
MTK_FUNCTION(7, "DBG_MON_A[6]")
),
- MTK_PIN(
- PINCTRL_PIN(244, "HDMI_SDA_RX"),
+ MTK_PIN(PINCTRL_PIN(244, "HDMI_SDA_RX"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 130),
MTK_FUNCTION(0, "GPIO244"),
MTK_FUNCTION(1, "HDMI_SDA_RX")
),
- MTK_PIN(
- PINCTRL_PIN(245, "HDMI_SCL_RX"),
+ MTK_PIN(PINCTRL_PIN(245, "HDMI_SCL_RX"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 131),
MTK_FUNCTION(0, "GPIO245"),
MTK_FUNCTION(1, "HDMI_SCL_RX")
),
- MTK_PIN(
- PINCTRL_PIN(246, "MHL_SENCE"),
+ MTK_PIN(PINCTRL_PIN(246, "MHL_SENCE"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 132),
MTK_FUNCTION(0, "GPIO246")
),
- MTK_PIN(
- PINCTRL_PIN(247, "HDMI_HPD_CBUS_RX"),
+ MTK_PIN(PINCTRL_PIN(247, "HDMI_HPD_CBUS_RX"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 69),
MTK_FUNCTION(0, "GPIO247"),
MTK_FUNCTION(1, "HDMI_HPD_RX")
),
- MTK_PIN(
- PINCTRL_PIN(248, "HDMI_TESTOUTP_RX"),
+ MTK_PIN(PINCTRL_PIN(248, "HDMI_TESTOUTP_RX"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 133),
MTK_FUNCTION(0, "GPIO248"),
MTK_FUNCTION(1, "HDMI_TESTOUTP_RX")
),
- MTK_PIN(
- PINCTRL_PIN(249, "MSDC0E_RSTB"),
+ MTK_PIN(PINCTRL_PIN(249, "MSDC0E_RSTB"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 134),
MTK_FUNCTION(0, "GPIO249"),
MTK_FUNCTION(1, "MSDC0E_RSTB")
),
- MTK_PIN(
- PINCTRL_PIN(250, "MSDC0E_DAT7"),
+ MTK_PIN(PINCTRL_PIN(250, "MSDC0E_DAT7"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 135),
MTK_FUNCTION(0, "GPIO250"),
MTK_FUNCTION(1, "MSDC3_DAT7"),
MTK_FUNCTION(6, "PCIE0_CLKREQ_N")
),
- MTK_PIN(
- PINCTRL_PIN(251, "MSDC0E_DAT6"),
+ MTK_PIN(PINCTRL_PIN(251, "MSDC0E_DAT6"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 136),
MTK_FUNCTION(0, "GPIO251"),
MTK_FUNCTION(1, "MSDC3_DAT6"),
MTK_FUNCTION(6, "PCIE0_WAKE_N")
),
- MTK_PIN(
- PINCTRL_PIN(252, "MSDC0E_DAT5"),
+ MTK_PIN(PINCTRL_PIN(252, "MSDC0E_DAT5"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 137),
MTK_FUNCTION(0, "GPIO252"),
MTK_FUNCTION(1, "MSDC3_DAT5"),
MTK_FUNCTION(6, "PCIE1_CLKREQ_N")
),
- MTK_PIN(
- PINCTRL_PIN(253, "MSDC0E_DAT4"),
+ MTK_PIN(PINCTRL_PIN(253, "MSDC0E_DAT4"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 138),
MTK_FUNCTION(0, "GPIO253"),
MTK_FUNCTION(1, "MSDC3_DAT4"),
MTK_FUNCTION(6, "PCIE1_WAKE_N")
),
- MTK_PIN(
- PINCTRL_PIN(254, "MSDC0E_DAT3"),
+ MTK_PIN(PINCTRL_PIN(254, "MSDC0E_DAT3"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 139),
MTK_FUNCTION(0, "GPIO254"),
MTK_FUNCTION(1, "MSDC3_DAT3"),
MTK_FUNCTION(6, "PCIE2_CLKREQ_N")
),
- MTK_PIN(
- PINCTRL_PIN(255, "MSDC0E_DAT2"),
+ MTK_PIN(PINCTRL_PIN(255, "MSDC0E_DAT2"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 140),
MTK_FUNCTION(0, "GPIO255"),
MTK_FUNCTION(1, "MSDC3_DAT2"),
MTK_FUNCTION(6, "PCIE2_WAKE_N")
),
- MTK_PIN(
- PINCTRL_PIN(256, "MSDC0E_DAT1"),
+ MTK_PIN(PINCTRL_PIN(256, "MSDC0E_DAT1"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 141),
MTK_FUNCTION(0, "GPIO256"),
MTK_FUNCTION(1, "MSDC3_DAT1")
),
- MTK_PIN(
- PINCTRL_PIN(257, "MSDC0E_DAT0"),
+ MTK_PIN(PINCTRL_PIN(257, "MSDC0E_DAT0"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 142),
MTK_FUNCTION(0, "GPIO257"),
MTK_FUNCTION(1, "MSDC3_DAT0")
),
- MTK_PIN(
- PINCTRL_PIN(258, "MSDC0E_CMD"),
+ MTK_PIN(PINCTRL_PIN(258, "MSDC0E_CMD"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 143),
MTK_FUNCTION(0, "GPIO258"),
MTK_FUNCTION(1, "MSDC3_CMD")
),
- MTK_PIN(
- PINCTRL_PIN(259, "MSDC0E_CLK"),
+ MTK_PIN(PINCTRL_PIN(259, "MSDC0E_CLK"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 144),
MTK_FUNCTION(0, "GPIO259"),
MTK_FUNCTION(1, "MSDC3_CLK")
),
- MTK_PIN(
- PINCTRL_PIN(260, "MSDC0E_DSL"),
+ MTK_PIN(PINCTRL_PIN(260, "MSDC0E_DSL"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 145),
MTK_FUNCTION(0, "GPIO260"),
MTK_FUNCTION(1, "MSDC3_DSL")
),
- MTK_PIN(
- PINCTRL_PIN(261, "MSDC1_INS"),
+ MTK_PIN(PINCTRL_PIN(261, "MSDC1_INS"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 146),
MTK_FUNCTION(0, "GPIO261"),
MTK_FUNCTION(1, "MSDC1_INS"),
MTK_FUNCTION(7, "DBG_MON_B[29]")
),
- MTK_PIN(
- PINCTRL_PIN(262, "G2_TXEN"),
+ MTK_PIN(PINCTRL_PIN(262, "G2_TXEN"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 8),
MTK_FUNCTION(0, "GPIO262"),
MTK_FUNCTION(1, "G2_TXEN")
),
- MTK_PIN(
- PINCTRL_PIN(263, "G2_TXD3"),
+ MTK_PIN(PINCTRL_PIN(263, "G2_TXD3"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 9),
MTK_FUNCTION(0, "GPIO263"),
MTK_FUNCTION(1, "G2_TXD3"),
MTK_FUNCTION(6, "ANT_SEL5")
),
- MTK_PIN(
- PINCTRL_PIN(264, "G2_TXD2"),
+ MTK_PIN(PINCTRL_PIN(264, "G2_TXD2"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 10),
MTK_FUNCTION(0, "GPIO264"),
MTK_FUNCTION(1, "G2_TXD2"),
MTK_FUNCTION(6, "ANT_SEL4")
),
- MTK_PIN(
- PINCTRL_PIN(265, "G2_TXD1"),
+ MTK_PIN(PINCTRL_PIN(265, "G2_TXD1"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 11),
MTK_FUNCTION(0, "GPIO265"),
MTK_FUNCTION(1, "G2_TXD1"),
MTK_FUNCTION(6, "ANT_SEL3")
),
- MTK_PIN(
- PINCTRL_PIN(266, "G2_TXD0"),
+ MTK_PIN(PINCTRL_PIN(266, "G2_TXD0"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO266"),
MTK_FUNCTION(1, "G2_TXD0"),
MTK_FUNCTION(6, "ANT_SEL2")
),
- MTK_PIN(
- PINCTRL_PIN(267, "G2_TXC"),
+ MTK_PIN(PINCTRL_PIN(267, "G2_TXC"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO267"),
MTK_FUNCTION(1, "G2_TXC")
),
- MTK_PIN(
- PINCTRL_PIN(268, "G2_RXC"),
+ MTK_PIN(PINCTRL_PIN(268, "G2_RXC"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO268"),
MTK_FUNCTION(1, "G2_RXC")
),
- MTK_PIN(
- PINCTRL_PIN(269, "G2_RXD0"),
+ MTK_PIN(PINCTRL_PIN(269, "G2_RXD0"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO269"),
MTK_FUNCTION(1, "G2_RXD0")
),
- MTK_PIN(
- PINCTRL_PIN(270, "G2_RXD1"),
+ MTK_PIN(PINCTRL_PIN(270, "G2_RXD1"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO270"),
MTK_FUNCTION(1, "G2_RXD1")
),
- MTK_PIN(
- PINCTRL_PIN(271, "G2_RXD2"),
+ MTK_PIN(PINCTRL_PIN(271, "G2_RXD2"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO271"),
MTK_FUNCTION(1, "G2_RXD2")
),
- MTK_PIN(
- PINCTRL_PIN(272, "G2_RXD3"),
+ MTK_PIN(PINCTRL_PIN(272, "G2_RXD3"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO272"),
MTK_FUNCTION(1, "G2_RXD3")
),
- MTK_PIN(
- PINCTRL_PIN(273, "ESW_INT"),
+ MTK_PIN(PINCTRL_PIN(273, "ESW_INT"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 168),
MTK_FUNCTION(0, "GPIO273"),
MTK_FUNCTION(1, "ESW_INT")
),
- MTK_PIN(
- PINCTRL_PIN(274, "G2_RXDV"),
+ MTK_PIN(PINCTRL_PIN(274, "G2_RXDV"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO274"),
MTK_FUNCTION(1, "G2_RXDV")
),
- MTK_PIN(
- PINCTRL_PIN(275, "MDC"),
+ MTK_PIN(PINCTRL_PIN(275, "MDC"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO275"),
MTK_FUNCTION(1, "MDC"),
MTK_FUNCTION(6, "ANT_SEL0")
),
- MTK_PIN(
- PINCTRL_PIN(276, "MDIO"),
+ MTK_PIN(PINCTRL_PIN(276, "MDIO"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO276"),
MTK_FUNCTION(1, "MDIO"),
MTK_FUNCTION(6, "ANT_SEL1")
),
- MTK_PIN(
- PINCTRL_PIN(277, "ESW_RST"),
+ MTK_PIN(PINCTRL_PIN(277, "ESW_RST"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO277"),
MTK_FUNCTION(1, "ESW_RST")
),
- MTK_PIN(
- PINCTRL_PIN(278, "JTAG_RESET"),
+ MTK_PIN(PINCTRL_PIN(278, "JTAG_RESET"),
NULL, "mt2701",
MTK_EINT_FUNCTION(0, 147),
MTK_FUNCTION(0, "GPIO278"),
MTK_FUNCTION(1, "JTAG_RESET")
),
- MTK_PIN(
- PINCTRL_PIN(279, "USB3_RES_BOND"),
+ MTK_PIN(PINCTRL_PIN(279, "USB3_RES_BOND"),
NULL, "mt2701",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO279"),
diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-mt2712.h b/drivers/pinctrl/mediatek/pinctrl-mtk-mt2712.h
new file mode 100644
index 000000000000..ba2356a8ab89
--- /dev/null
+++ b/drivers/pinctrl/mediatek/pinctrl-mtk-mt2712.h
@@ -0,0 +1,1757 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ * Author: Zhiyong Tao <zhiyong.tao@mediatek.com>
+ *
+ */
+#ifndef PINCTRL_MTK_MT2712_H
+#define PINCTRL_MTK_MT2712_H
+
+#include <linux/pinctrl/pinctrl.h>
+#include "pinctrl-mtk-common.h"
+
+static const struct mtk_desc_pin mtk_pins_mt2712[] = {
+ MTK_PIN(PINCTRL_PIN(0, "EINT0"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 6),
+ MTK_FUNCTION(0, "GPIO0"),
+ MTK_FUNCTION(1, "EINT0"),
+ MTK_FUNCTION(2, "MBIST_DIAG_SCANOUT"),
+ MTK_FUNCTION(3, "DSIA_TE"),
+ MTK_FUNCTION(4, "DSIC_TE"),
+ MTK_FUNCTION(5, "DIN_D3"),
+ MTK_FUNCTION(6, "PURE_HW_PROTECT")
+ ),
+ MTK_PIN(PINCTRL_PIN(1, "EINT1"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 7),
+ MTK_FUNCTION(0, "GPIO1"),
+ MTK_FUNCTION(1, "EINT1"),
+ MTK_FUNCTION(2, "IR_IN"),
+ MTK_FUNCTION(3, "DSIB_TE"),
+ MTK_FUNCTION(4, "DSID_TE"),
+ MTK_FUNCTION(5, "DIN_D4")
+ ),
+ MTK_PIN(PINCTRL_PIN(2, "EINT2"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 8),
+ MTK_FUNCTION(0, "GPIO2"),
+ MTK_FUNCTION(1, "EINT2"),
+ MTK_FUNCTION(2, "IR_IN"),
+ MTK_FUNCTION(3, "LCM_RST1"),
+ MTK_FUNCTION(5, "DIN_D5")
+ ),
+ MTK_PIN(PINCTRL_PIN(3, "EINT3"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 9),
+ MTK_FUNCTION(0, "GPIO3"),
+ MTK_FUNCTION(1, "EINT3"),
+ MTK_FUNCTION(2, "IR_IN"),
+ MTK_FUNCTION(3, "LCM_RST0"),
+ MTK_FUNCTION(5, "DIN_D6")
+ ),
+ MTK_PIN(PINCTRL_PIN(4, "PWM0"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 10),
+ MTK_FUNCTION(0, "GPIO4"),
+ MTK_FUNCTION(1, "PWM0"),
+ MTK_FUNCTION(2, "DISP0_PWM"),
+ MTK_FUNCTION(3, "DISP1_PWM"),
+ MTK_FUNCTION(5, "DIN_CLK")
+ ),
+ MTK_PIN(PINCTRL_PIN(5, "PWM1"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 11),
+ MTK_FUNCTION(0, "GPIO5"),
+ MTK_FUNCTION(1, "PWM1"),
+ MTK_FUNCTION(2, "DISP1_PWM"),
+ MTK_FUNCTION(3, "DISP0_PWM"),
+ MTK_FUNCTION(5, "DIN_VSYNC")
+ ),
+ MTK_PIN(PINCTRL_PIN(6, "PWM2"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 12),
+ MTK_FUNCTION(0, "GPIO6"),
+ MTK_FUNCTION(1, "PWM2"),
+ MTK_FUNCTION(2, "DISP0_PWM"),
+ MTK_FUNCTION(3, "DISP1_PWM"),
+ MTK_FUNCTION(4, "DISP2_PWM"),
+ MTK_FUNCTION(5, "DIN_HSYNC")
+ ),
+ MTK_PIN(PINCTRL_PIN(7, "PWM3"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 13),
+ MTK_FUNCTION(0, "GPIO7"),
+ MTK_FUNCTION(1, "PWM3"),
+ MTK_FUNCTION(2, "DISP1_PWM"),
+ MTK_FUNCTION(3, "DISP0_PWM"),
+ MTK_FUNCTION(4, "LCM_RST2"),
+ MTK_FUNCTION(5, "DIN_D0")
+ ),
+ MTK_PIN(PINCTRL_PIN(8, "PWM4"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 14),
+ MTK_FUNCTION(0, "GPIO8"),
+ MTK_FUNCTION(1, "PWM4"),
+ MTK_FUNCTION(2, "DISP0_PWM"),
+ MTK_FUNCTION(3, "DISP1_PWM"),
+ MTK_FUNCTION(4, "DSIA_TE"),
+ MTK_FUNCTION(5, "DIN_D1")
+ ),
+ MTK_PIN(PINCTRL_PIN(9, "PWM5"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 15),
+ MTK_FUNCTION(0, "GPIO9"),
+ MTK_FUNCTION(1, "PWM5"),
+ MTK_FUNCTION(2, "DISP1_PWM"),
+ MTK_FUNCTION(3, "DISP0_PWM"),
+ MTK_FUNCTION(4, "DSIB_TE"),
+ MTK_FUNCTION(5, "DIN_D2")
+ ),
+ MTK_PIN(PINCTRL_PIN(10, "PWM6"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 16),
+ MTK_FUNCTION(0, "GPIO10"),
+ MTK_FUNCTION(1, "PWM6"),
+ MTK_FUNCTION(2, "DISP0_PWM"),
+ MTK_FUNCTION(3, "DISP1_PWM"),
+ MTK_FUNCTION(4, "LCM_RST0")
+ ),
+ MTK_PIN(PINCTRL_PIN(11, "PWM7"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 17),
+ MTK_FUNCTION(0, "GPIO11"),
+ MTK_FUNCTION(1, "PWM7"),
+ MTK_FUNCTION(2, "DISP1_PWM"),
+ MTK_FUNCTION(3, "DISP0_PWM"),
+ MTK_FUNCTION(4, "LCM_RST1")
+ ),
+ MTK_PIN(PINCTRL_PIN(12, "IDDIG_P0"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(1, 22),
+ MTK_FUNCTION(0, "GPIO12"),
+ MTK_FUNCTION(1, "IDDIG_A"),
+ MTK_FUNCTION(5, "DIN_D7")
+ ),
+ MTK_PIN(PINCTRL_PIN(13, "DRV_VBUS_P0"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 43),
+ MTK_FUNCTION(0, "GPIO13"),
+ MTK_FUNCTION(1, "DRV_VBUS_A")
+ ),
+ MTK_PIN(PINCTRL_PIN(14, "IDDIG_P1"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(1, 44),
+ MTK_FUNCTION(0, "GPIO14"),
+ MTK_FUNCTION(1, "IDDIG_B")
+ ),
+ MTK_PIN(PINCTRL_PIN(15, "DRV_VBUS_P1"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 45),
+ MTK_FUNCTION(0, "GPIO15"),
+ MTK_FUNCTION(1, "DRV_VBUS_B")
+ ),
+ MTK_PIN(PINCTRL_PIN(16, "DRV_VBUS_P2"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 46),
+ MTK_FUNCTION(0, "GPIO16"),
+ MTK_FUNCTION(1, "DRV_VBUS_C")
+ ),
+ MTK_PIN(PINCTRL_PIN(17, "DRV_VBUS_P3"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 47),
+ MTK_FUNCTION(0, "GPIO17"),
+ MTK_FUNCTION(1, "DRV_VBUS_D")
+ ),
+ MTK_PIN(PINCTRL_PIN(18, "KPROW0"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 18),
+ MTK_FUNCTION(0, "GPIO18"),
+ MTK_FUNCTION(1, "KROW0")
+ ),
+ MTK_PIN(PINCTRL_PIN(19, "KPCOL0"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 19),
+ MTK_FUNCTION(0, "GPIO19"),
+ MTK_FUNCTION(1, "KCOL0")
+ ),
+ MTK_PIN(PINCTRL_PIN(20, "KPROW1"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 48),
+ MTK_FUNCTION(0, "GPIO20"),
+ MTK_FUNCTION(1, "KROW1")
+ ),
+ MTK_PIN(PINCTRL_PIN(21, "KPCOL1"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 49),
+ MTK_FUNCTION(0, "GPIO21"),
+ MTK_FUNCTION(1, "KCOL1")
+ ),
+ MTK_PIN(PINCTRL_PIN(22, "KPROW2"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 50),
+ MTK_FUNCTION(0, "GPIO22"),
+ MTK_FUNCTION(1, "KROW2"),
+ MTK_FUNCTION(2, "DISP1_PWM")
+ ),
+ MTK_PIN(PINCTRL_PIN(23, "KPCOL2"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 51),
+ MTK_FUNCTION(0, "GPIO23"),
+ MTK_FUNCTION(1, "KCOL2"),
+ MTK_FUNCTION(2, "DISP0_PWM")
+ ),
+ MTK_PIN(PINCTRL_PIN(24, "CMMCLK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 52),
+ MTK_FUNCTION(0, "GPIO24"),
+ MTK_FUNCTION(1, "CMMCLK"),
+ MTK_FUNCTION(7, "DBG_MON_A_1_")
+ ),
+ MTK_PIN(PINCTRL_PIN(25, "CM2MCLK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 53),
+ MTK_FUNCTION(0, "GPIO25"),
+ MTK_FUNCTION(1, "CM2MCLK"),
+ MTK_FUNCTION(7, "DBG_MON_A_2_")
+ ),
+ MTK_PIN(PINCTRL_PIN(26, "PCM_TX"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 54),
+ MTK_FUNCTION(0, "GPIO26"),
+ MTK_FUNCTION(1, "PCM1_DO"),
+ MTK_FUNCTION(2, "MRG_TX"),
+ MTK_FUNCTION(3, "DAI_TX"),
+ MTK_FUNCTION(4, "MRG_RX"),
+ MTK_FUNCTION(5, "DAI_RX"),
+ MTK_FUNCTION(6, "PCM1_DI"),
+ MTK_FUNCTION(7, "DBG_MON_A_3_")
+ ),
+ MTK_PIN(PINCTRL_PIN(27, "PCM_CLK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 55),
+ MTK_FUNCTION(0, "GPIO27"),
+ MTK_FUNCTION(1, "PCM1_CLK"),
+ MTK_FUNCTION(2, "MRG_CLK"),
+ MTK_FUNCTION(3, "DAI_CLK"),
+ MTK_FUNCTION(7, "DBG_MON_A_4_")
+ ),
+ MTK_PIN(PINCTRL_PIN(28, "PCM_RX"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 56),
+ MTK_FUNCTION(0, "GPIO28"),
+ MTK_FUNCTION(1, "PCM1_DI"),
+ MTK_FUNCTION(2, "MRG_RX"),
+ MTK_FUNCTION(3, "DAI_RX"),
+ MTK_FUNCTION(4, "MRG_TX"),
+ MTK_FUNCTION(5, "DAI_TX"),
+ MTK_FUNCTION(6, "PCM1_DO"),
+ MTK_FUNCTION(7, "DBG_MON_A_5_")
+ ),
+ MTK_PIN(PINCTRL_PIN(29, "PCM_SYNC"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 57),
+ MTK_FUNCTION(0, "GPIO29"),
+ MTK_FUNCTION(1, "PCM1_SYNC"),
+ MTK_FUNCTION(2, "MRG_SYNC"),
+ MTK_FUNCTION(3, "DAI_SYNC"),
+ MTK_FUNCTION(7, "DBG_MON_A_6_")
+ ),
+ MTK_PIN(PINCTRL_PIN(30, "NCEB0"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 58),
+ MTK_FUNCTION(0, "GPIO30"),
+ MTK_FUNCTION(1, "NCEB0"),
+ MTK_FUNCTION(2, "USB0_FT_SDA"),
+ MTK_FUNCTION(7, "DBG_MON_A_7_")
+ ),
+ MTK_PIN(PINCTRL_PIN(31, "NCEB1"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 59),
+ MTK_FUNCTION(0, "GPIO31"),
+ MTK_FUNCTION(1, "NCEB1"),
+ MTK_FUNCTION(2, "USB1_FT_SCL"),
+ MTK_FUNCTION(7, "DBG_MON_A_8_")
+ ),
+ MTK_PIN(PINCTRL_PIN(32, "NF_DQS"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 60),
+ MTK_FUNCTION(0, "GPIO32"),
+ MTK_FUNCTION(1, "NF_DQS"),
+ MTK_FUNCTION(2, "USB1_FT_SDA"),
+ MTK_FUNCTION(7, "DBG_MON_A_9_")
+ ),
+ MTK_PIN(PINCTRL_PIN(33, "NWEB"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 61),
+ MTK_FUNCTION(0, "GPIO33"),
+ MTK_FUNCTION(1, "NWEB"),
+ MTK_FUNCTION(2, "USB2_FT_SCL"),
+ MTK_FUNCTION(7, "DBG_MON_A_10_")
+ ),
+ MTK_PIN(PINCTRL_PIN(34, "NREB"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 62),
+ MTK_FUNCTION(0, "GPIO34"),
+ MTK_FUNCTION(1, "NREB"),
+ MTK_FUNCTION(2, "USB2_FT_SDA"),
+ MTK_FUNCTION(7, "DBG_MON_A_11_")
+ ),
+ MTK_PIN(PINCTRL_PIN(35, "NCLE"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 63),
+ MTK_FUNCTION(0, "GPIO35"),
+ MTK_FUNCTION(1, "NCLE"),
+ MTK_FUNCTION(2, "USB3_FT_SCL"),
+ MTK_FUNCTION(7, "DBG_MON_A_12_")
+ ),
+ MTK_PIN(PINCTRL_PIN(36, "NALE"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 64),
+ MTK_FUNCTION(0, "GPIO36"),
+ MTK_FUNCTION(1, "NALE"),
+ MTK_FUNCTION(2, "USB3_FT_SDA"),
+ MTK_FUNCTION(7, "DBG_MON_A_13_")
+ ),
+ MTK_PIN(PINCTRL_PIN(37, "MSDC0E_CLK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
+ MTK_FUNCTION(0, "GPIO37"),
+ MTK_FUNCTION(1, "MSDC0_CLK"),
+ MTK_FUNCTION(2, "USB0_FT_SCL"),
+ MTK_FUNCTION(7, "DBG_MON_A_0_")
+ ),
+ MTK_PIN(PINCTRL_PIN(38, "MSDC0E_DAT7"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
+ MTK_FUNCTION(0, "GPIO38"),
+ MTK_FUNCTION(1, "MSDC0_DAT7"),
+ MTK_FUNCTION(2, "NAND_ND7"),
+ MTK_FUNCTION(7, "DBG_MON_A_14_")
+ ),
+ MTK_PIN(PINCTRL_PIN(39, "MSDC0E_DAT6"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
+ MTK_FUNCTION(0, "GPIO39"),
+ MTK_FUNCTION(1, "MSDC0_DAT6"),
+ MTK_FUNCTION(2, "NAND_ND6"),
+ MTK_FUNCTION(7, "DBG_MON_A_15_")
+ ),
+ MTK_PIN(PINCTRL_PIN(40, "MSDC0E_DAT5"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
+ MTK_FUNCTION(0, "GPIO40"),
+ MTK_FUNCTION(1, "MSDC0_DAT5"),
+ MTK_FUNCTION(2, "NAND_ND5"),
+ MTK_FUNCTION(7, "DBG_MON_A_16_")
+ ),
+ MTK_PIN(PINCTRL_PIN(41, "MSDC0E_DAT4"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
+ MTK_FUNCTION(0, "GPIO41"),
+ MTK_FUNCTION(1, "MSDC0_DAT4"),
+ MTK_FUNCTION(2, "NAND_ND4"),
+ MTK_FUNCTION(7, "DBG_MON_A_17_")
+ ),
+ MTK_PIN(PINCTRL_PIN(42, "MSDC0E_DAT3"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
+ MTK_FUNCTION(0, "GPIO42"),
+ MTK_FUNCTION(1, "MSDC0_DAT3"),
+ MTK_FUNCTION(2, "NAND_ND3"),
+ MTK_FUNCTION(7, "DBG_MON_A_18_")
+ ),
+ MTK_PIN(PINCTRL_PIN(43, "MSDC0E_DAT2"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
+ MTK_FUNCTION(0, "GPIO43"),
+ MTK_FUNCTION(1, "MSDC0_DAT2"),
+ MTK_FUNCTION(2, "NAND_ND2"),
+ MTK_FUNCTION(7, "DBG_MON_A_19_")
+ ),
+ MTK_PIN(PINCTRL_PIN(44, "MSDC0E_DAT1"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
+ MTK_FUNCTION(0, "GPIO44"),
+ MTK_FUNCTION(1, "MSDC0_DAT1"),
+ MTK_FUNCTION(2, "NAND_ND1"),
+ MTK_FUNCTION(7, "DBG_MON_A_20_")
+ ),
+ MTK_PIN(PINCTRL_PIN(45, "MSDC0E_DAT0"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
+ MTK_FUNCTION(0, "GPIO45"),
+ MTK_FUNCTION(1, "MSDC0_DAT0"),
+ MTK_FUNCTION(2, "NAND_ND0"),
+ MTK_FUNCTION(7, "DBG_MON_A_21_")
+ ),
+ MTK_PIN(PINCTRL_PIN(46, "MSDC0E_CMD"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
+ MTK_FUNCTION(0, "GPIO46"),
+ MTK_FUNCTION(1, "MSDC0_CMD"),
+ MTK_FUNCTION(2, "NAND_NRNB"),
+ MTK_FUNCTION(7, "DBG_MON_A_22_")
+ ),
+ MTK_PIN(PINCTRL_PIN(47, "MSDC0E_DSL"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
+ MTK_FUNCTION(0, "GPIO47"),
+ MTK_FUNCTION(1, "MSDC0_DSL"),
+ MTK_FUNCTION(7, "DBG_MON_A_23_")
+ ),
+ MTK_PIN(PINCTRL_PIN(48, "MSDC0E_RSTB"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 142),
+ MTK_FUNCTION(0, "GPIO48"),
+ MTK_FUNCTION(1, "MSDC0_RSTB"),
+ MTK_FUNCTION(7, "DBG_MON_A_24_")
+ ),
+ MTK_PIN(PINCTRL_PIN(49, "MSDC3_DAT3"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 65),
+ MTK_FUNCTION(0, "GPIO49"),
+ MTK_FUNCTION(1, "MSDC3_DAT3"),
+ MTK_FUNCTION(7, "DBG_MON_A_25_")
+ ),
+ MTK_PIN(PINCTRL_PIN(50, "MSDC3_DAT2"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 66),
+ MTK_FUNCTION(0, "GPIO50"),
+ MTK_FUNCTION(1, "MSDC3_DAT2"),
+ MTK_FUNCTION(7, "DBG_MON_A_26_")
+ ),
+ MTK_PIN(PINCTRL_PIN(51, "MSDC3_DAT1"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 67),
+ MTK_FUNCTION(0, "GPIO51"),
+ MTK_FUNCTION(1, "MSDC3_DAT1"),
+ MTK_FUNCTION(7, "DBG_MON_A_27_")
+ ),
+ MTK_PIN(PINCTRL_PIN(52, "MSDC3_DAT0"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 68),
+ MTK_FUNCTION(0, "GPIO52"),
+ MTK_FUNCTION(1, "MSDC3_DAT0"),
+ MTK_FUNCTION(7, "DBG_MON_A_28_")
+ ),
+ MTK_PIN(PINCTRL_PIN(53, "MSDC3_CMD"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 69),
+ MTK_FUNCTION(0, "GPIO53"),
+ MTK_FUNCTION(1, "MSDC3_CMD"),
+ MTK_FUNCTION(7, "DBG_MON_A_29_")
+ ),
+ MTK_PIN(PINCTRL_PIN(54, "MSDC3_INS"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 20),
+ MTK_FUNCTION(0, "GPIO54"),
+ MTK_FUNCTION(1, "MSDC3_INS"),
+ MTK_FUNCTION(7, "DBG_MON_A_30_")
+ ),
+ MTK_PIN(PINCTRL_PIN(55, "MSDC3_DSL"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 70),
+ MTK_FUNCTION(0, "GPIO55"),
+ MTK_FUNCTION(1, "MSDC3_DSL"),
+ MTK_FUNCTION(7, "DBG_MON_A_31_")
+ ),
+ MTK_PIN(PINCTRL_PIN(56, "MSDC3_CLK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 71),
+ MTK_FUNCTION(0, "GPIO56"),
+ MTK_FUNCTION(1, "MSDC3_CLK"),
+ MTK_FUNCTION(7, "DBG_MON_A_32_")
+ ),
+ MTK_PIN(PINCTRL_PIN(57, "NOR_CS"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 72),
+ MTK_FUNCTION(0, "GPIO57"),
+ MTK_FUNCTION(1, "NOR_CS")
+ ),
+ MTK_PIN(PINCTRL_PIN(58, "NOR_CK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 73),
+ MTK_FUNCTION(0, "GPIO58"),
+ MTK_FUNCTION(1, "NOR_CK")
+ ),
+ MTK_PIN(PINCTRL_PIN(59, "NOR_IO0"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 74),
+ MTK_FUNCTION(0, "GPIO59"),
+ MTK_FUNCTION(1, "NOR_IO0")
+ ),
+ MTK_PIN(PINCTRL_PIN(60, "NOR_IO1"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 75),
+ MTK_FUNCTION(0, "GPIO60"),
+ MTK_FUNCTION(1, "NOR_IO1")
+ ),
+ MTK_PIN(PINCTRL_PIN(61, "NOR_IO2"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 76),
+ MTK_FUNCTION(0, "GPIO61"),
+ MTK_FUNCTION(1, "NOR_IO2")
+ ),
+ MTK_PIN(PINCTRL_PIN(62, "NOR_IO3"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 77),
+ MTK_FUNCTION(0, "GPIO62"),
+ MTK_FUNCTION(1, "NOR_IO3")
+ ),
+ MTK_PIN(PINCTRL_PIN(63, "MSDC1_CLK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 78),
+ MTK_FUNCTION(0, "GPIO63"),
+ MTK_FUNCTION(1, "MSDC1_CLK"),
+ MTK_FUNCTION(2, "UDI_TCK")
+ ),
+ MTK_PIN(PINCTRL_PIN(64, "MSDC1_DAT3"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 79),
+ MTK_FUNCTION(0, "GPIO64"),
+ MTK_FUNCTION(1, "MSDC1_DAT3"),
+ MTK_FUNCTION(2, "UDI_TDI")
+ ),
+ MTK_PIN(PINCTRL_PIN(65, "MSDC1_DAT1"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 80),
+ MTK_FUNCTION(0, "GPIO65"),
+ MTK_FUNCTION(1, "MSDC1_DAT1"),
+ MTK_FUNCTION(2, "UDI_TMS")
+ ),
+ MTK_PIN(PINCTRL_PIN(66, "MSDC1_DAT2"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 81),
+ MTK_FUNCTION(0, "GPIO66"),
+ MTK_FUNCTION(1, "MSDC1_DAT2"),
+ MTK_FUNCTION(2, "UDI_TDO")
+ ),
+ MTK_PIN(PINCTRL_PIN(67, "MSDC1_PSW"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 82),
+ MTK_FUNCTION(0, "GPIO67"),
+ MTK_FUNCTION(2, "UDI_NTRST")
+ ),
+ MTK_PIN(PINCTRL_PIN(68, "MSDC1_DAT0"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 83),
+ MTK_FUNCTION(0, "GPIO68"),
+ MTK_FUNCTION(1, "MSDC1_DAT0")
+ ),
+ MTK_PIN(PINCTRL_PIN(69, "MSDC1_CMD"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 84),
+ MTK_FUNCTION(0, "GPIO69"),
+ MTK_FUNCTION(1, "MSDC1_CMD")
+ ),
+ MTK_PIN(PINCTRL_PIN(70, "MSDC1_INS"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 85),
+ MTK_FUNCTION(0, "GPIO70")
+ ),
+ MTK_PIN(PINCTRL_PIN(71, "GBE_TXD3"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 86),
+ MTK_FUNCTION(0, "GPIO71"),
+ MTK_FUNCTION(1, "GBE_TXD3"),
+ MTK_FUNCTION(7, "DBG_MON_B_0_")
+ ),
+ MTK_PIN(PINCTRL_PIN(72, "GBE_TXD2"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 87),
+ MTK_FUNCTION(0, "GPIO72"),
+ MTK_FUNCTION(1, "GBE_TXD2"),
+ MTK_FUNCTION(7, "DBG_MON_B_1_")
+ ),
+ MTK_PIN(PINCTRL_PIN(73, "GBE_TXD1"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 88),
+ MTK_FUNCTION(0, "GPIO73"),
+ MTK_FUNCTION(1, "GBE_TXD1"),
+ MTK_FUNCTION(7, "DBG_MON_B_2_")
+ ),
+ MTK_PIN(PINCTRL_PIN(74, "GBE_TXD0"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 89),
+ MTK_FUNCTION(0, "GPIO74"),
+ MTK_FUNCTION(1, "GBE_TXD0"),
+ MTK_FUNCTION(7, "DBG_MON_B_3_")
+ ),
+ MTK_PIN(PINCTRL_PIN(75, "GBE_TXC"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 90),
+ MTK_FUNCTION(0, "GPIO75"),
+ MTK_FUNCTION(1, "GBE_TXC"),
+ MTK_FUNCTION(7, "DBG_MON_B_4_")
+ ),
+ MTK_PIN(PINCTRL_PIN(76, "GBE_TXEN"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 91),
+ MTK_FUNCTION(0, "GPIO76"),
+ MTK_FUNCTION(1, "GBE_TXEN"),
+ MTK_FUNCTION(7, "DBG_MON_B_5_")
+ ),
+ MTK_PIN(PINCTRL_PIN(77, "GBE_TXER"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 92),
+ MTK_FUNCTION(0, "GPIO77"),
+ MTK_FUNCTION(1, "GBE_TXER"),
+ MTK_FUNCTION(7, "DBG_MON_B_6_")
+ ),
+ MTK_PIN(PINCTRL_PIN(78, "GBE_RXD3"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 93),
+ MTK_FUNCTION(0, "GPIO78"),
+ MTK_FUNCTION(1, "GBE_RXD3"),
+ MTK_FUNCTION(7, "DBG_MON_B_7_")
+ ),
+ MTK_PIN(PINCTRL_PIN(79, "GBE_RXD2"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 94),
+ MTK_FUNCTION(0, "GPIO79"),
+ MTK_FUNCTION(1, "GBE_RXD2"),
+ MTK_FUNCTION(7, "DBG_MON_B_8_")
+ ),
+ MTK_PIN(PINCTRL_PIN(80, "GBE_RXD1"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 95),
+ MTK_FUNCTION(0, "GPIO80"),
+ MTK_FUNCTION(1, "GBE_RXD1"),
+ MTK_FUNCTION(7, "DBG_MON_B_9_")
+ ),
+ MTK_PIN(PINCTRL_PIN(81, "GBE_RXD0"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 96),
+ MTK_FUNCTION(0, "GPIO81"),
+ MTK_FUNCTION(1, "GBE_RXD0"),
+ MTK_FUNCTION(7, "DBG_MON_B_10_")
+ ),
+ MTK_PIN(PINCTRL_PIN(82, "GBE_RXDV"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 97),
+ MTK_FUNCTION(0, "GPIO82"),
+ MTK_FUNCTION(1, "GBE_RXDV"),
+ MTK_FUNCTION(7, "DBG_MON_B_11_")
+ ),
+ MTK_PIN(PINCTRL_PIN(83, "GBE_RXER"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 98),
+ MTK_FUNCTION(0, "GPIO83"),
+ MTK_FUNCTION(1, "GBE_RXER"),
+ MTK_FUNCTION(7, "DBG_MON_B_12_")
+ ),
+ MTK_PIN(PINCTRL_PIN(84, "GBE_RXC"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 99),
+ MTK_FUNCTION(0, "GPIO84"),
+ MTK_FUNCTION(1, "GBE_RXC"),
+ MTK_FUNCTION(7, "DBG_MON_B_13_")
+ ),
+ MTK_PIN(PINCTRL_PIN(85, "GBE_MDC"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 100),
+ MTK_FUNCTION(0, "GPIO85"),
+ MTK_FUNCTION(1, "GBE_MDC"),
+ MTK_FUNCTION(7, "DBG_MON_B_14_")
+ ),
+ MTK_PIN(PINCTRL_PIN(86, "GBE_MDIO"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 101),
+ MTK_FUNCTION(0, "GPIO86"),
+ MTK_FUNCTION(1, "GBE_MDIO"),
+ MTK_FUNCTION(7, "DBG_MON_B_15_")
+ ),
+ MTK_PIN(PINCTRL_PIN(87, "GBE_COL"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 102),
+ MTK_FUNCTION(0, "GPIO87"),
+ MTK_FUNCTION(1, "GBE_COL"),
+ MTK_FUNCTION(7, "DBG_MON_B_16_")
+ ),
+ MTK_PIN(PINCTRL_PIN(88, "GBE_INTR"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 21),
+ MTK_FUNCTION(0, "GPIO88"),
+ MTK_FUNCTION(1, "GBE_INTR"),
+ MTK_FUNCTION(2, "GBE_CRS"),
+ MTK_FUNCTION(7, "DBG_MON_B_17_")
+ ),
+ MTK_PIN(PINCTRL_PIN(89, "MSDC2_CLK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 103),
+ MTK_FUNCTION(0, "GPIO89"),
+ MTK_FUNCTION(1, "MSDC2_CLK"),
+ MTK_FUNCTION(7, "DBG_MON_B_18_")
+ ),
+ MTK_PIN(PINCTRL_PIN(90, "MSDC2_DAT3"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 104),
+ MTK_FUNCTION(0, "GPIO90"),
+ MTK_FUNCTION(1, "MSDC2_DAT3"),
+ MTK_FUNCTION(7, "DBG_MON_B_19_")
+ ),
+ MTK_PIN(PINCTRL_PIN(91, "MSDC2_DAT2"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 105),
+ MTK_FUNCTION(0, "GPIO91"),
+ MTK_FUNCTION(1, "MSDC2_DAT2"),
+ MTK_FUNCTION(7, "DBG_MON_B_20_")
+ ),
+ MTK_PIN(PINCTRL_PIN(92, "MSDC2_DAT1"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 106),
+ MTK_FUNCTION(0, "GPIO92"),
+ MTK_FUNCTION(1, "MSDC2_DAT1"),
+ MTK_FUNCTION(7, "DBG_MON_B_21_")
+ ),
+ MTK_PIN(PINCTRL_PIN(93, "MSDC2_DAT0"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 107),
+ MTK_FUNCTION(0, "GPIO93"),
+ MTK_FUNCTION(1, "MSDC2_DAT0"),
+ MTK_FUNCTION(7, "DBG_MON_B_22_")
+ ),
+ MTK_PIN(PINCTRL_PIN(94, "MSDC2_INS"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 108),
+ MTK_FUNCTION(0, "GPIO94"),
+ MTK_FUNCTION(7, "DBG_MON_B_23_")
+ ),
+ MTK_PIN(PINCTRL_PIN(95, "MSDC2_CMD"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 109),
+ MTK_FUNCTION(0, "GPIO95"),
+ MTK_FUNCTION(1, "MSDC2_CMD"),
+ MTK_FUNCTION(7, "DBG_MON_B_24_")
+ ),
+ MTK_PIN(PINCTRL_PIN(96, "MSDC2_PSW"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 110),
+ MTK_FUNCTION(0, "GPIO96"),
+ MTK_FUNCTION(7, "DBG_MON_B_25_")
+ ),
+ MTK_PIN(PINCTRL_PIN(97, "URXD4"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 111),
+ MTK_FUNCTION(0, "GPIO97"),
+ MTK_FUNCTION(1, "URXD4"),
+ MTK_FUNCTION(2, "UTXD4"),
+ MTK_FUNCTION(3, "MRG_CLK"),
+ MTK_FUNCTION(4, "PCM1_CLK"),
+ MTK_FUNCTION(5, "I2S_IQ2_SDQB"),
+ MTK_FUNCTION(6, "I2SO1_WS"),
+ MTK_FUNCTION(7, "DBG_MON_B_26_")
+ ),
+ MTK_PIN(PINCTRL_PIN(98, "URTS4"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 112),
+ MTK_FUNCTION(0, "GPIO98"),
+ MTK_FUNCTION(1, "URTS4"),
+ MTK_FUNCTION(2, "UCTS4"),
+ MTK_FUNCTION(3, "MRG_RX"),
+ MTK_FUNCTION(4, "PCM1_DI"),
+ MTK_FUNCTION(5, "I2S_IQ1_SDIB"),
+ MTK_FUNCTION(6, "I2SO1_MCK"),
+ MTK_FUNCTION(7, "DBG_MON_B_27_")
+ ),
+ MTK_PIN(PINCTRL_PIN(99, "UTXD4"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 113),
+ MTK_FUNCTION(0, "GPIO99"),
+ MTK_FUNCTION(1, "UTXD4"),
+ MTK_FUNCTION(2, "URXD4"),
+ MTK_FUNCTION(3, "MRG_SYNC"),
+ MTK_FUNCTION(4, "PCM1_SYNC"),
+ MTK_FUNCTION(5, "I2S_IQ0_SDQB"),
+ MTK_FUNCTION(6, "I2SO1_BCK"),
+ MTK_FUNCTION(7, "DBG_MON_B_28_")
+ ),
+ MTK_PIN(PINCTRL_PIN(100, "UCTS4"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 114),
+ MTK_FUNCTION(0, "GPIO100"),
+ MTK_FUNCTION(1, "UCTS4"),
+ MTK_FUNCTION(2, "URTS4"),
+ MTK_FUNCTION(3, "MRG_TX"),
+ MTK_FUNCTION(4, "PCM1_DO"),
+ MTK_FUNCTION(5, "I2S_IQ0_SDIB"),
+ MTK_FUNCTION(6, "I2SO1_DO"),
+ MTK_FUNCTION(7, "DBG_MON_B_29_")
+ ),
+ MTK_PIN(PINCTRL_PIN(101, "URXD5"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 30),
+ MTK_FUNCTION(0, "GPIO101"),
+ MTK_FUNCTION(1, "URXD5"),
+ MTK_FUNCTION(2, "UTXD5"),
+ MTK_FUNCTION(3, "I2SO3_WS"),
+ MTK_FUNCTION(4, "TDMIN_LRCK"),
+ MTK_FUNCTION(6, "I2SO0_WS"),
+ MTK_FUNCTION(7, "DBG_MON_B_30_")
+ ),
+ MTK_PIN(PINCTRL_PIN(102, "URTS5"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 31),
+ MTK_FUNCTION(0, "GPIO102"),
+ MTK_FUNCTION(1, "URTS5"),
+ MTK_FUNCTION(2, "UCTS5"),
+ MTK_FUNCTION(3, "I2SO3_MCK"),
+ MTK_FUNCTION(4, "TDMIN_MCLK"),
+ MTK_FUNCTION(5, "IR_IN"),
+ MTK_FUNCTION(6, "I2SO0_MCK"),
+ MTK_FUNCTION(7, "DBG_MON_B_31_")
+ ),
+ MTK_PIN(PINCTRL_PIN(103, "UTXD5"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 32),
+ MTK_FUNCTION(0, "GPIO103"),
+ MTK_FUNCTION(1, "UTXD5"),
+ MTK_FUNCTION(2, "URXD5"),
+ MTK_FUNCTION(3, "I2SO3_BCK"),
+ MTK_FUNCTION(4, "TDMIN_BCK"),
+ MTK_FUNCTION(6, "I2SO0_BCK"),
+ MTK_FUNCTION(7, "DBG_MON_B_32_")
+ ),
+ MTK_PIN(PINCTRL_PIN(104, "UCTS5"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 33),
+ MTK_FUNCTION(0, "GPIO104"),
+ MTK_FUNCTION(1, "UCTS5"),
+ MTK_FUNCTION(2, "URTS5"),
+ MTK_FUNCTION(3, "I2SO0_DO1"),
+ MTK_FUNCTION(4, "TDMIN_DI"),
+ MTK_FUNCTION(5, "IR_IN"),
+ MTK_FUNCTION(6, "I2SO0_DO0")
+ ),
+ MTK_PIN(PINCTRL_PIN(105, "I2C_SDA0"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 115),
+ MTK_FUNCTION(0, "GPIO105"),
+ MTK_FUNCTION(1, "SDA0")
+ ),
+ MTK_PIN(PINCTRL_PIN(106, "I2C_SDA1"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 116),
+ MTK_FUNCTION(0, "GPIO106"),
+ MTK_FUNCTION(1, "SDA1")
+ ),
+ MTK_PIN(PINCTRL_PIN(107, "I2C_SDA2"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 117),
+ MTK_FUNCTION(0, "GPIO107"),
+ MTK_FUNCTION(1, "SDA2")
+ ),
+ MTK_PIN(PINCTRL_PIN(108, "I2C_SDA3"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 118),
+ MTK_FUNCTION(0, "GPIO108"),
+ MTK_FUNCTION(1, "SDA3")
+ ),
+ MTK_PIN(PINCTRL_PIN(109, "I2C_SDA4"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 119),
+ MTK_FUNCTION(0, "GPIO109"),
+ MTK_FUNCTION(1, "SDA4")
+ ),
+ MTK_PIN(PINCTRL_PIN(110, "I2C_SDA5"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 34),
+ MTK_FUNCTION(0, "GPIO110"),
+ MTK_FUNCTION(1, "SDA5")
+ ),
+ MTK_PIN(PINCTRL_PIN(111, "I2C_SCL0"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 120),
+ MTK_FUNCTION(0, "GPIO111"),
+ MTK_FUNCTION(1, "SCL0")
+ ),
+ MTK_PIN(PINCTRL_PIN(112, "I2C_SCL1"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 121),
+ MTK_FUNCTION(0, "GPIO112"),
+ MTK_FUNCTION(1, "SCL1")
+ ),
+ MTK_PIN(PINCTRL_PIN(113, "I2C_SCL2"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 122),
+ MTK_FUNCTION(0, "GPIO113"),
+ MTK_FUNCTION(1, "SCL2")
+ ),
+ MTK_PIN(PINCTRL_PIN(114, "I2C_SCL3"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 123),
+ MTK_FUNCTION(0, "GPIO114"),
+ MTK_FUNCTION(1, "SCL3")
+ ),
+ MTK_PIN(PINCTRL_PIN(115, "I2C_SCL4"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 124),
+ MTK_FUNCTION(0, "GPIO115"),
+ MTK_FUNCTION(1, "SCL4")
+ ),
+ MTK_PIN(PINCTRL_PIN(116, "I2C_SCL5"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 35),
+ MTK_FUNCTION(0, "GPIO116"),
+ MTK_FUNCTION(1, "SCL5")
+ ),
+ MTK_PIN(PINCTRL_PIN(117, "URXD0"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 125),
+ MTK_FUNCTION(0, "GPIO117"),
+ MTK_FUNCTION(1, "URXD0"),
+ MTK_FUNCTION(2, "UTXD0")
+ ),
+ MTK_PIN(PINCTRL_PIN(118, "URXD1"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 126),
+ MTK_FUNCTION(0, "GPIO118"),
+ MTK_FUNCTION(1, "URXD1"),
+ MTK_FUNCTION(2, "UTXD1")
+ ),
+ MTK_PIN(PINCTRL_PIN(119, "URXD2"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 127),
+ MTK_FUNCTION(0, "GPIO119"),
+ MTK_FUNCTION(1, "URXD2"),
+ MTK_FUNCTION(2, "UTXD2")
+ ),
+ MTK_PIN(PINCTRL_PIN(120, "UTXD0"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 128),
+ MTK_FUNCTION(0, "GPIO120"),
+ MTK_FUNCTION(1, "UTXD0"),
+ MTK_FUNCTION(2, "URXD0")
+ ),
+ MTK_PIN(PINCTRL_PIN(121, "UTXD1"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 129),
+ MTK_FUNCTION(0, "GPIO121"),
+ MTK_FUNCTION(1, "UTXD1"),
+ MTK_FUNCTION(2, "URXD1")
+ ),
+ MTK_PIN(PINCTRL_PIN(122, "UTXD2"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 130),
+ MTK_FUNCTION(0, "GPIO122"),
+ MTK_FUNCTION(1, "UTXD2"),
+ MTK_FUNCTION(2, "URXD2")
+ ),
+ MTK_PIN(PINCTRL_PIN(123, "URXD3"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 131),
+ MTK_FUNCTION(0, "GPIO123"),
+ MTK_FUNCTION(1, "URXD3"),
+ MTK_FUNCTION(2, "UTXD3"),
+ MTK_FUNCTION(3, "PURE_HW_PROTECT")
+ ),
+ MTK_PIN(PINCTRL_PIN(124, "UTXD3"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 132),
+ MTK_FUNCTION(0, "GPIO124"),
+ MTK_FUNCTION(1, "UTXD3"),
+ MTK_FUNCTION(2, "URXD3"),
+ MTK_FUNCTION(3, "PURE_HW_PROTECT")
+ ),
+ MTK_PIN(PINCTRL_PIN(125, "URTS3"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 133),
+ MTK_FUNCTION(0, "GPIO125"),
+ MTK_FUNCTION(1, "URTS3"),
+ MTK_FUNCTION(2, "UCTS3"),
+ MTK_FUNCTION(3, "WATCH_DOG")
+ ),
+ MTK_PIN(PINCTRL_PIN(126, "UCTS3"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 134),
+ MTK_FUNCTION(0, "GPIO126"),
+ MTK_FUNCTION(1, "UCTS3"),
+ MTK_FUNCTION(2, "URTS3"),
+ MTK_FUNCTION(3, "SRCLKENA0")
+ ),
+ MTK_PIN(PINCTRL_PIN(127, "SPI2_CSN"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 135),
+ MTK_FUNCTION(0, "GPIO127"),
+ MTK_FUNCTION(1, "SPI_CS_2_"),
+ MTK_FUNCTION(2, "SPI_CS_1_")
+ ),
+ MTK_PIN(PINCTRL_PIN(128, "SPI2_MO"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 136),
+ MTK_FUNCTION(0, "GPIO128"),
+ MTK_FUNCTION(1, "SPI_MO_2_"),
+ MTK_FUNCTION(2, "SPI_SO_1_")
+ ),
+ MTK_PIN(PINCTRL_PIN(129, "SPI2_MI"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 137),
+ MTK_FUNCTION(0, "GPIO129"),
+ MTK_FUNCTION(1, "SPI_MI_2_"),
+ MTK_FUNCTION(2, "SPI_SI_1_")
+ ),
+ MTK_PIN(PINCTRL_PIN(130, "SPI2_CK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 138),
+ MTK_FUNCTION(0, "GPIO130"),
+ MTK_FUNCTION(1, "SPI_CK_2_"),
+ MTK_FUNCTION(2, "SPI_CK_1_")
+ ),
+ MTK_PIN(PINCTRL_PIN(131, "SPI3_CSN"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 139),
+ MTK_FUNCTION(0, "GPIO131"),
+ MTK_FUNCTION(1, "SPI_CS_3_")
+ ),
+ MTK_PIN(PINCTRL_PIN(132, "SPI3_MO"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 143),
+ MTK_FUNCTION(0, "GPIO132"),
+ MTK_FUNCTION(1, "SPI_MO_3_")
+ ),
+ MTK_PIN(PINCTRL_PIN(133, "SPI3_MI"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 144),
+ MTK_FUNCTION(0, "GPIO133"),
+ MTK_FUNCTION(1, "SPI_MI_3_")
+ ),
+ MTK_PIN(PINCTRL_PIN(134, "SPI3_CK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 145),
+ MTK_FUNCTION(0, "GPIO134"),
+ MTK_FUNCTION(1, "SPI_CK_3_")
+ ),
+ MTK_PIN(PINCTRL_PIN(135, "KPROW3"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 146),
+ MTK_FUNCTION(0, "GPIO135"),
+ MTK_FUNCTION(1, "KROW3"),
+ MTK_FUNCTION(2, "DSIC_TE")
+ ),
+ MTK_PIN(PINCTRL_PIN(136, "KPROW4"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 36),
+ MTK_FUNCTION(0, "GPIO136"),
+ MTK_FUNCTION(1, "KROW4"),
+ MTK_FUNCTION(2, "DSID_TE")
+ ),
+ MTK_PIN(PINCTRL_PIN(137, "KPCOL3"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 147),
+ MTK_FUNCTION(0, "GPIO137"),
+ MTK_FUNCTION(1, "KCOL3"),
+ MTK_FUNCTION(2, "DISP2_PWM")
+ ),
+ MTK_PIN(PINCTRL_PIN(138, "KPCOL4"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 37),
+ MTK_FUNCTION(0, "GPIO138"),
+ MTK_FUNCTION(1, "KCOL4"),
+ MTK_FUNCTION(2, "LCM_RST2")
+ ),
+ MTK_PIN(PINCTRL_PIN(139, "KPCOL5"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 38),
+ MTK_FUNCTION(0, "GPIO139"),
+ MTK_FUNCTION(1, "KCOL5"),
+ MTK_FUNCTION(3, "DSIA_TE"),
+ MTK_FUNCTION(4, "PURE_HW_PROTECT")
+ ),
+ MTK_PIN(PINCTRL_PIN(140, "KPCOL6"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 39),
+ MTK_FUNCTION(0, "GPIO140"),
+ MTK_FUNCTION(1, "KCOL6"),
+ MTK_FUNCTION(2, "WATCH_DOG"),
+ MTK_FUNCTION(3, "LCM_RST1")
+ ),
+ MTK_PIN(PINCTRL_PIN(141, "KPROW5"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 40),
+ MTK_FUNCTION(0, "GPIO141"),
+ MTK_FUNCTION(1, "KROW5"),
+ MTK_FUNCTION(3, "LCM_RST0"),
+ MTK_FUNCTION(4, "PURE_HW_PROTECT")
+ ),
+ MTK_PIN(PINCTRL_PIN(142, "KPROW6"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 41),
+ MTK_FUNCTION(0, "GPIO142"),
+ MTK_FUNCTION(1, "KROW6"),
+ MTK_FUNCTION(2, "SRCLKENA0"),
+ MTK_FUNCTION(3, "DSIB_TE")
+ ),
+ MTK_PIN(PINCTRL_PIN(143, "JTDO_ICE"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 148),
+ MTK_FUNCTION(0, "GPIO143"),
+ MTK_FUNCTION(1, "JTDO_ICE"),
+ MTK_FUNCTION(3, "DFD_TDO")
+ ),
+ MTK_PIN(PINCTRL_PIN(144, "JTCK_ICE"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 149),
+ MTK_FUNCTION(0, "GPIO144"),
+ MTK_FUNCTION(1, "JTCK_ICE"),
+ MTK_FUNCTION(3, "DFD_TCK")
+ ),
+ MTK_PIN(PINCTRL_PIN(145, "JTDI_ICE"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 150),
+ MTK_FUNCTION(0, "GPIO145"),
+ MTK_FUNCTION(1, "JTDI_ICE"),
+ MTK_FUNCTION(3, "DFD_TDI")
+ ),
+ MTK_PIN(PINCTRL_PIN(146, "JTMS_ICE"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 151),
+ MTK_FUNCTION(0, "GPIO146"),
+ MTK_FUNCTION(1, "JTMS_ICE"),
+ MTK_FUNCTION(3, "DFD_TMS")
+ ),
+ MTK_PIN(PINCTRL_PIN(147, "JTRSTB_ICE"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 152),
+ MTK_FUNCTION(0, "GPIO147"),
+ MTK_FUNCTION(1, "JTRST_B_ICE"),
+ MTK_FUNCTION(3, "DFD_NTRST")
+ ),
+ MTK_PIN(PINCTRL_PIN(148, "GPIO148"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 153),
+ MTK_FUNCTION(0, "GPIO148"),
+ MTK_FUNCTION(1, "JTRSTB_CM4"),
+ MTK_FUNCTION(3, "DFD_NTRST")
+ ),
+ MTK_PIN(PINCTRL_PIN(149, "GPIO149"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 154),
+ MTK_FUNCTION(0, "GPIO149"),
+ MTK_FUNCTION(1, "JTCK_CM4"),
+ MTK_FUNCTION(3, "DFD_TCK")
+ ),
+ MTK_PIN(PINCTRL_PIN(150, "GPIO150"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 155),
+ MTK_FUNCTION(0, "GPIO150"),
+ MTK_FUNCTION(1, "JTMS_CM4"),
+ MTK_FUNCTION(3, "DFD_TMS")
+ ),
+ MTK_PIN(PINCTRL_PIN(151, "GPIO151"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 156),
+ MTK_FUNCTION(0, "GPIO151"),
+ MTK_FUNCTION(1, "JTDI_CM4"),
+ MTK_FUNCTION(3, "DFD_TDI")
+ ),
+ MTK_PIN(PINCTRL_PIN(152, "GPIO152"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 157),
+ MTK_FUNCTION(0, "GPIO152"),
+ MTK_FUNCTION(1, "JTDO_CM4"),
+ MTK_FUNCTION(3, "DFD_TDO")
+ ),
+ MTK_PIN(PINCTRL_PIN(153, "SPI0_CSN"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 158),
+ MTK_FUNCTION(0, "GPIO153"),
+ MTK_FUNCTION(1, "SPI_CS_0_"),
+ MTK_FUNCTION(2, "SRCLKENA0"),
+ MTK_FUNCTION(3, "UTXD0"),
+ MTK_FUNCTION(4, "I2SO0_DO1"),
+ MTK_FUNCTION(6, "TDMO0_DATA1"),
+ MTK_FUNCTION(7, "I2S_IQ2_SDQB")
+ ),
+ MTK_PIN(PINCTRL_PIN(154, "SPI0_MI"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 159),
+ MTK_FUNCTION(0, "GPIO154"),
+ MTK_FUNCTION(1, "SPI_MI_0_"),
+ MTK_FUNCTION(2, "SRCLKENA0"),
+ MTK_FUNCTION(3, "URXD0"),
+ MTK_FUNCTION(4, "I2SO0_DO0"),
+ MTK_FUNCTION(5, "I2SO1_DO"),
+ MTK_FUNCTION(6, "TDMO0_DATA"),
+ MTK_FUNCTION(7, "I2S_IQ1_SDIB")
+ ),
+ MTK_PIN(PINCTRL_PIN(155, "SPI0_CK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 160),
+ MTK_FUNCTION(0, "GPIO155"),
+ MTK_FUNCTION(1, "SPI_CK_0_"),
+ MTK_FUNCTION(2, "SC_APBIAS_OFF"),
+ MTK_FUNCTION(3, "UTXD1"),
+ MTK_FUNCTION(4, "I2SO0_BCK"),
+ MTK_FUNCTION(5, "I2SO1_BCK"),
+ MTK_FUNCTION(6, "TDMO0_BCK"),
+ MTK_FUNCTION(7, "I2S_IQ0_SDQB")
+ ),
+ MTK_PIN(PINCTRL_PIN(156, "SPI0_MO"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 161),
+ MTK_FUNCTION(0, "GPIO156"),
+ MTK_FUNCTION(1, "SPI_MO_0_"),
+ MTK_FUNCTION(2, "SC_APBIAS_OFF"),
+ MTK_FUNCTION(3, "URXD1"),
+ MTK_FUNCTION(4, "I2SO0_WS"),
+ MTK_FUNCTION(5, "I2SO1_WS"),
+ MTK_FUNCTION(6, "TDMO0_LRCK"),
+ MTK_FUNCTION(7, "I2S_IQ0_SDIB")
+ ),
+ MTK_PIN(PINCTRL_PIN(157, "SPI5_CSN"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 162),
+ MTK_FUNCTION(0, "GPIO157"),
+ MTK_FUNCTION(1, "SPI_CS_5_"),
+ MTK_FUNCTION(2, "LCM_RST0"),
+ MTK_FUNCTION(3, "UTXD2"),
+ MTK_FUNCTION(4, "I2SO0_MCK"),
+ MTK_FUNCTION(5, "I2SO1_MCK"),
+ MTK_FUNCTION(6, "TDMO0_MCLK")
+ ),
+ MTK_PIN(PINCTRL_PIN(158, "SPI5_MI"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 163),
+ MTK_FUNCTION(0, "GPIO158"),
+ MTK_FUNCTION(1, "SPI_MI_5_"),
+ MTK_FUNCTION(2, "DSIA_TE"),
+ MTK_FUNCTION(3, "URXD2")
+ ),
+ MTK_PIN(PINCTRL_PIN(159, "SPI5_MO"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 164),
+ MTK_FUNCTION(0, "GPIO159"),
+ MTK_FUNCTION(1, "SPI_MO_5_"),
+ MTK_FUNCTION(2, "DSIB_TE"),
+ MTK_FUNCTION(3, "UTXD3")
+ ),
+ MTK_PIN(PINCTRL_PIN(160, "SPI5_CK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 165),
+ MTK_FUNCTION(0, "GPIO160"),
+ MTK_FUNCTION(1, "SPI_CK_5_"),
+ MTK_FUNCTION(2, "LCM_RST1"),
+ MTK_FUNCTION(3, "URXD3")
+ ),
+ MTK_PIN(PINCTRL_PIN(161, "SPI1_CSN"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 166),
+ MTK_FUNCTION(0, "GPIO161"),
+ MTK_FUNCTION(1, "SPI_CS_1_"),
+ MTK_FUNCTION(2, "SPI_CS_4_"),
+ MTK_FUNCTION(4, "I2S_IQ2_SDQB"),
+ MTK_FUNCTION(5, "I2SO2_DO"),
+ MTK_FUNCTION(6, "TDMO0_DATA1"),
+ MTK_FUNCTION(7, "I2SO0_DO1")
+ ),
+ MTK_PIN(PINCTRL_PIN(162, "SPI1_SI"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 167),
+ MTK_FUNCTION(0, "GPIO162"),
+ MTK_FUNCTION(1, "SPI_SI_1_"),
+ MTK_FUNCTION(2, "SPI_MI_4_"),
+ MTK_FUNCTION(4, "I2S_IQ1_SDIB"),
+ MTK_FUNCTION(5, "I2SO2_BCK"),
+ MTK_FUNCTION(6, "TDMO0_DATA"),
+ MTK_FUNCTION(7, "I2SO0_DO0")
+ ),
+ MTK_PIN(PINCTRL_PIN(163, "SPI1_CK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 168),
+ MTK_FUNCTION(0, "GPIO163"),
+ MTK_FUNCTION(1, "SPI_CK_1_"),
+ MTK_FUNCTION(2, "SPI_CK_4_"),
+ MTK_FUNCTION(4, "I2S_IQ0_SDQB"),
+ MTK_FUNCTION(5, "I2SO2_WS"),
+ MTK_FUNCTION(6, "TDMO0_BCK"),
+ MTK_FUNCTION(7, "I2SO0_BCK")
+ ),
+ MTK_PIN(PINCTRL_PIN(164, "SPI1_SO"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 169),
+ MTK_FUNCTION(0, "GPIO164"),
+ MTK_FUNCTION(1, "SPI_SO_1_"),
+ MTK_FUNCTION(2, "SPI_MO_4_"),
+ MTK_FUNCTION(4, "I2S_IQ0_SDIB"),
+ MTK_FUNCTION(5, "I2SO2_MCK"),
+ MTK_FUNCTION(6, "TDMO0_LRCK"),
+ MTK_FUNCTION(7, "I2SO0_WS")
+ ),
+ MTK_PIN(PINCTRL_PIN(165, "SPI4_CSN"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 170),
+ MTK_FUNCTION(0, "GPIO165"),
+ MTK_FUNCTION(1, "SPI_CS_4_"),
+ MTK_FUNCTION(2, "LCM_RST0"),
+ MTK_FUNCTION(3, "SPI_CS_1_"),
+ MTK_FUNCTION(4, "UTXD4"),
+ MTK_FUNCTION(5, "I2SO1_DO"),
+ MTK_FUNCTION(6, "TDMO0_MCLK"),
+ MTK_FUNCTION(7, "I2SO0_MCK")
+ ),
+ MTK_PIN(PINCTRL_PIN(166, "SPI4_MI"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 171),
+ MTK_FUNCTION(0, "GPIO166"),
+ MTK_FUNCTION(1, "SPI_MI_4_"),
+ MTK_FUNCTION(2, "DSIA_TE"),
+ MTK_FUNCTION(3, "SPI_SI_1_"),
+ MTK_FUNCTION(4, "URXD4"),
+ MTK_FUNCTION(5, "I2SO1_BCK")
+ ),
+ MTK_PIN(PINCTRL_PIN(167, "SPI4_MO"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 172),
+ MTK_FUNCTION(0, "GPIO167"),
+ MTK_FUNCTION(1, "SPI_MO_4_"),
+ MTK_FUNCTION(2, "DSIB_TE"),
+ MTK_FUNCTION(3, "SPI_SO_1_"),
+ MTK_FUNCTION(4, "UTXD5"),
+ MTK_FUNCTION(5, "I2SO1_WS")
+ ),
+ MTK_PIN(PINCTRL_PIN(168, "SPI4_CK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 173),
+ MTK_FUNCTION(0, "GPIO168"),
+ MTK_FUNCTION(1, "SPI_CK_4_"),
+ MTK_FUNCTION(2, "LCM_RST1"),
+ MTK_FUNCTION(3, "SPI_CK_1_"),
+ MTK_FUNCTION(4, "URXD5"),
+ MTK_FUNCTION(5, "I2SO1_MCK")
+ ),
+ MTK_PIN(PINCTRL_PIN(169, "I2SI0_DATA"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 174),
+ MTK_FUNCTION(0, "GPIO169"),
+ MTK_FUNCTION(1, "I2SI0_DI"),
+ MTK_FUNCTION(2, "I2SI1_DI"),
+ MTK_FUNCTION(3, "I2SI2_DI"),
+ MTK_FUNCTION(4, "TDMIN_DI")
+ ),
+ MTK_PIN(PINCTRL_PIN(170, "I2SI0_LRCK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 175),
+ MTK_FUNCTION(0, "GPIO170"),
+ MTK_FUNCTION(1, "I2SI0_WS"),
+ MTK_FUNCTION(2, "I2SI1_WS"),
+ MTK_FUNCTION(3, "I2SI2_WS"),
+ MTK_FUNCTION(4, "TDMIN_LRCK"),
+ MTK_FUNCTION(5, "TDMO0_DATA3"),
+ MTK_FUNCTION(6, "TDMO1_DATA3")
+ ),
+ MTK_PIN(PINCTRL_PIN(171, "I2SI0_MCLK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 176),
+ MTK_FUNCTION(0, "GPIO171"),
+ MTK_FUNCTION(1, "I2SI0_MCK"),
+ MTK_FUNCTION(2, "I2SI1_MCK"),
+ MTK_FUNCTION(3, "I2SI2_MCK"),
+ MTK_FUNCTION(4, "TDMIN_MCLK"),
+ MTK_FUNCTION(5, "TDMO0_DATA2"),
+ MTK_FUNCTION(6, "TDMO1_DATA2")
+ ),
+ MTK_PIN(PINCTRL_PIN(172, "I2SI0_BCK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 177),
+ MTK_FUNCTION(0, "GPIO172"),
+ MTK_FUNCTION(1, "I2SI0_BCK"),
+ MTK_FUNCTION(2, "I2SI1_BCK"),
+ MTK_FUNCTION(3, "I2SI2_BCK"),
+ MTK_FUNCTION(4, "TDMIN_BCK"),
+ MTK_FUNCTION(5, "TDMO0_DATA1"),
+ MTK_FUNCTION(6, "TDMO1_DATA1")
+ ),
+ MTK_PIN(PINCTRL_PIN(173, "I2SI2_DATA"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 178),
+ MTK_FUNCTION(0, "GPIO173"),
+ MTK_FUNCTION(1, "I2SI2_DI"),
+ MTK_FUNCTION(2, "I2SI0_DI"),
+ MTK_FUNCTION(3, "I2SI1_DI"),
+ MTK_FUNCTION(4, "PCM1_DI"),
+ MTK_FUNCTION(5, "TDMIN_DI"),
+ MTK_FUNCTION(6, "PCM1_DO")
+ ),
+ MTK_PIN(PINCTRL_PIN(174, "I2SI2_MCLK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 179),
+ MTK_FUNCTION(0, "GPIO174"),
+ MTK_FUNCTION(1, "I2SI2_MCK"),
+ MTK_FUNCTION(2, "I2SI0_MCK"),
+ MTK_FUNCTION(3, "I2SI1_MCK"),
+ MTK_FUNCTION(4, "PCM1_DO"),
+ MTK_FUNCTION(5, "TDMIN_MCLK"),
+ MTK_FUNCTION(6, "PCM1_DI"),
+ MTK_FUNCTION(7, "I2S_IQ2_SDQB")
+ ),
+ MTK_PIN(PINCTRL_PIN(175, "I2SI2_BCK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 180),
+ MTK_FUNCTION(0, "GPIO175"),
+ MTK_FUNCTION(1, "I2SI2_BCK"),
+ MTK_FUNCTION(2, "I2SI0_BCK"),
+ MTK_FUNCTION(3, "I2SI1_BCK"),
+ MTK_FUNCTION(4, "PCM1_CLK"),
+ MTK_FUNCTION(5, "TDMIN_BCK")
+ ),
+ MTK_PIN(PINCTRL_PIN(176, "I2SI2_LRCK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 181),
+ MTK_FUNCTION(0, "GPIO176"),
+ MTK_FUNCTION(1, "I2SI2_WS"),
+ MTK_FUNCTION(2, "I2SI0_WS"),
+ MTK_FUNCTION(3, "I2SI1_WS"),
+ MTK_FUNCTION(4, "PCM1_SYNC"),
+ MTK_FUNCTION(5, "TDMIN_LRCK")
+ ),
+ MTK_PIN(PINCTRL_PIN(177, "I2SI1_DATA"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 182),
+ MTK_FUNCTION(0, "GPIO177"),
+ MTK_FUNCTION(1, "I2SI1_DI"),
+ MTK_FUNCTION(2, "I2SI0_DI"),
+ MTK_FUNCTION(3, "I2SI2_DI"),
+ MTK_FUNCTION(4, "TDMIN_DI")
+ ),
+ MTK_PIN(PINCTRL_PIN(178, "I2SI1_BCK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 183),
+ MTK_FUNCTION(0, "GPIO178"),
+ MTK_FUNCTION(1, "I2SI1_BCK"),
+ MTK_FUNCTION(2, "I2SI0_BCK"),
+ MTK_FUNCTION(3, "I2SI2_BCK"),
+ MTK_FUNCTION(4, "TDMIN_BCK"),
+ MTK_FUNCTION(5, "TDMO0_DATA3"),
+ MTK_FUNCTION(6, "TDMO1_DATA3")
+ ),
+ MTK_PIN(PINCTRL_PIN(179, "I2SI1_LRCK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 184),
+ MTK_FUNCTION(0, "GPIO179"),
+ MTK_FUNCTION(1, "I2SI1_WS"),
+ MTK_FUNCTION(2, "I2SI0_WS"),
+ MTK_FUNCTION(3, "I2SI2_WS"),
+ MTK_FUNCTION(4, "TDMIN_LRCK"),
+ MTK_FUNCTION(5, "TDMO0_DATA2"),
+ MTK_FUNCTION(6, "TDMO1_DATA2")
+ ),
+ MTK_PIN(PINCTRL_PIN(180, "I2SI1_MCLK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 185),
+ MTK_FUNCTION(0, "GPIO180"),
+ MTK_FUNCTION(1, "I2SI1_MCK"),
+ MTK_FUNCTION(2, "I2SI0_MCK"),
+ MTK_FUNCTION(3, "I2SI2_MCK"),
+ MTK_FUNCTION(4, "TDMIN_MCLK"),
+ MTK_FUNCTION(5, "TDMO0_DATA1"),
+ MTK_FUNCTION(6, "TDMO1_DATA1"),
+ MTK_FUNCTION(7, "I2S_IQ2_SDIB")
+ ),
+ MTK_PIN(PINCTRL_PIN(181, "I2SO1_DATA0"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 186),
+ MTK_FUNCTION(0, "GPIO181"),
+ MTK_FUNCTION(1, "I2SO1_DO"),
+ MTK_FUNCTION(2, "I2SO0_DO0"),
+ MTK_FUNCTION(3, "I2SO2_DO"),
+ MTK_FUNCTION(4, "DAI_TX"),
+ MTK_FUNCTION(5, "TDMIN_MCLK"),
+ MTK_FUNCTION(7, "I2S_IQ2_SDIA")
+ ),
+ MTK_PIN(PINCTRL_PIN(182, "I2SO1_BCK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 187),
+ MTK_FUNCTION(0, "GPIO182"),
+ MTK_FUNCTION(1, "I2SO1_BCK"),
+ MTK_FUNCTION(2, "I2SO0_BCK"),
+ MTK_FUNCTION(3, "I2SO2_BCK"),
+ MTK_FUNCTION(4, "DAI_SYNC"),
+ MTK_FUNCTION(5, "TDMIN_BCK"),
+ MTK_FUNCTION(6, "TDMO0_DATA3"),
+ MTK_FUNCTION(7, "I2S_IQ2_BCK")
+ ),
+ MTK_PIN(PINCTRL_PIN(183, "I2SO1_LRCK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 188),
+ MTK_FUNCTION(0, "GPIO183"),
+ MTK_FUNCTION(1, "I2SO1_WS"),
+ MTK_FUNCTION(2, "I2SO0_WS"),
+ MTK_FUNCTION(3, "I2SO2_WS"),
+ MTK_FUNCTION(4, "DAI_CLK"),
+ MTK_FUNCTION(5, "TDMIN_DI"),
+ MTK_FUNCTION(6, "TDMO0_DATA2"),
+ MTK_FUNCTION(7, "I2S_IQ2_WS")
+ ),
+ MTK_PIN(PINCTRL_PIN(184, "I2SO1_MCLK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 189),
+ MTK_FUNCTION(0, "GPIO184"),
+ MTK_FUNCTION(1, "I2SO1_MCK"),
+ MTK_FUNCTION(2, "I2SO0_MCK"),
+ MTK_FUNCTION(3, "I2SO2_MCK"),
+ MTK_FUNCTION(4, "DAI_RX"),
+ MTK_FUNCTION(5, "TDMIN_LRCK"),
+ MTK_FUNCTION(6, "TDMO0_DATA1"),
+ MTK_FUNCTION(7, "I2S_IQ2_SDQA")
+ ),
+ MTK_PIN(PINCTRL_PIN(185, "AUD_EXT_CK2"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 190),
+ MTK_FUNCTION(0, "GPIO185"),
+ MTK_FUNCTION(1, "AUD_EXT_CK2"),
+ MTK_FUNCTION(2, "AUD_EXT_CK1"),
+ MTK_FUNCTION(3, "I2SO1_DO"),
+ MTK_FUNCTION(4, "I2SI2_DI"),
+ MTK_FUNCTION(5, "MRG_RX"),
+ MTK_FUNCTION(6, "PCM1_DI"),
+ MTK_FUNCTION(7, "I2S_IQ0_SDQB")
+ ),
+ MTK_PIN(PINCTRL_PIN(186, "AUD_EXT_CK1"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 191),
+ MTK_FUNCTION(0, "GPIO186"),
+ MTK_FUNCTION(1, "AUD_EXT_CK1"),
+ MTK_FUNCTION(2, "AUD_EXT_CK2"),
+ MTK_FUNCTION(3, "I2SO0_DO1"),
+ MTK_FUNCTION(4, "I2SI1_DI"),
+ MTK_FUNCTION(5, "MRG_TX"),
+ MTK_FUNCTION(6, "PCM1_DO"),
+ MTK_FUNCTION(7, "I2S_IQ0_SDIB")
+ ),
+ MTK_PIN(PINCTRL_PIN(187, "I2SO2_BCK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 192),
+ MTK_FUNCTION(0, "GPIO187"),
+ MTK_FUNCTION(1, "I2SO2_BCK"),
+ MTK_FUNCTION(2, "I2SO0_BCK"),
+ MTK_FUNCTION(3, "I2SO1_BCK"),
+ MTK_FUNCTION(4, "PCM1_CLK"),
+ MTK_FUNCTION(5, "MRG_SYNC"),
+ MTK_FUNCTION(6, "TDMO1_DATA3"),
+ MTK_FUNCTION(7, "I2S_IQ0_BCK")
+ ),
+ MTK_PIN(PINCTRL_PIN(188, "I2SO2_LRCK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 193),
+ MTK_FUNCTION(0, "GPIO188"),
+ MTK_FUNCTION(1, "I2SO2_WS"),
+ MTK_FUNCTION(2, "I2SO0_WS"),
+ MTK_FUNCTION(3, "I2SO1_WS"),
+ MTK_FUNCTION(4, "PCM1_SYNC"),
+ MTK_FUNCTION(5, "MRG_CLK"),
+ MTK_FUNCTION(6, "TDMO1_DATA2"),
+ MTK_FUNCTION(7, "I2S_IQ0_WS")
+ ),
+ MTK_PIN(PINCTRL_PIN(189, "I2SO2_MCLK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 194),
+ MTK_FUNCTION(0, "GPIO189"),
+ MTK_FUNCTION(1, "I2SO2_MCK"),
+ MTK_FUNCTION(2, "I2SO0_MCK"),
+ MTK_FUNCTION(3, "I2SO1_MCK"),
+ MTK_FUNCTION(4, "PCM1_DO"),
+ MTK_FUNCTION(5, "MRG_RX"),
+ MTK_FUNCTION(6, "TDMO1_DATA1"),
+ MTK_FUNCTION(7, "I2S_IQ0_SDQA")
+ ),
+ MTK_PIN(PINCTRL_PIN(190, "I2SO2_DATA0"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 195),
+ MTK_FUNCTION(0, "GPIO190"),
+ MTK_FUNCTION(1, "I2SO2_DO"),
+ MTK_FUNCTION(2, "I2SO0_DO0"),
+ MTK_FUNCTION(3, "I2SO1_DO"),
+ MTK_FUNCTION(4, "PCM1_DI"),
+ MTK_FUNCTION(5, "MRG_TX"),
+ MTK_FUNCTION(6, "PCM1_DO"),
+ MTK_FUNCTION(7, "I2S_IQ0_SDIA")
+ ),
+ MTK_PIN(PINCTRL_PIN(191, "I2SO0_DATA1"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 196),
+ MTK_FUNCTION(0, "GPIO191"),
+ MTK_FUNCTION(1, "I2SO0_DO1"),
+ MTK_FUNCTION(2, "I2SI0_DI"),
+ MTK_FUNCTION(3, "I2SI1_DI"),
+ MTK_FUNCTION(4, "I2SI2_DI"),
+ MTK_FUNCTION(5, "DAI_TX"),
+ MTK_FUNCTION(6, "I2S_IQ0_SDQB"),
+ MTK_FUNCTION(7, "I2S_IQ1_SDQB")
+ ),
+ MTK_PIN(PINCTRL_PIN(192, "I2SO0_MCLK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 197),
+ MTK_FUNCTION(0, "GPIO192"),
+ MTK_FUNCTION(1, "I2SO0_MCK"),
+ MTK_FUNCTION(2, "I2SO1_MCK"),
+ MTK_FUNCTION(3, "I2SO2_MCK"),
+ MTK_FUNCTION(4, "USB4_FT_SCL"),
+ MTK_FUNCTION(5, "TDMO1_DATA3"),
+ MTK_FUNCTION(6, "I2S_IQ0_SDIB"),
+ MTK_FUNCTION(7, "I2S_IQ1_SDQA")
+ ),
+ MTK_PIN(PINCTRL_PIN(193, "I2SO0_DATA0"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 198),
+ MTK_FUNCTION(0, "GPIO193"),
+ MTK_FUNCTION(1, "I2SO0_DO0"),
+ MTK_FUNCTION(2, "I2SO1_DO"),
+ MTK_FUNCTION(3, "I2SO2_DO"),
+ MTK_FUNCTION(4, "USB4_FT_SDA"),
+ MTK_FUNCTION(7, "I2S_IQ1_SDIA")
+ ),
+ MTK_PIN(PINCTRL_PIN(194, "I2SO0_LRCK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 199),
+ MTK_FUNCTION(0, "GPIO194"),
+ MTK_FUNCTION(1, "I2SO0_WS"),
+ MTK_FUNCTION(2, "I2SO1_WS"),
+ MTK_FUNCTION(3, "I2SO2_WS"),
+ MTK_FUNCTION(4, "USB5_FT_SCL"),
+ MTK_FUNCTION(5, "TDMO1_DATA2"),
+ MTK_FUNCTION(7, "I2S_IQ1_WS")
+ ),
+ MTK_PIN(PINCTRL_PIN(195, "I2SO0_BCK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 200),
+ MTK_FUNCTION(0, "GPIO195"),
+ MTK_FUNCTION(1, "I2SO0_BCK"),
+ MTK_FUNCTION(2, "I2SO1_BCK"),
+ MTK_FUNCTION(3, "I2SO2_BCK"),
+ MTK_FUNCTION(4, "USB5_FT_SDA"),
+ MTK_FUNCTION(5, "TDMO1_DATA1"),
+ MTK_FUNCTION(7, "I2S_IQ1_BCK")
+ ),
+ MTK_PIN(PINCTRL_PIN(196, "TDMO1_MCLK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 201),
+ MTK_FUNCTION(0, "GPIO196"),
+ MTK_FUNCTION(1, "TDMO1_MCLK"),
+ MTK_FUNCTION(2, "TDMO0_MCLK"),
+ MTK_FUNCTION(3, "TDMIN_MCLK"),
+ MTK_FUNCTION(6, "I2SO0_DO1"),
+ MTK_FUNCTION(7, "I2S_IQ1_SDIB")
+ ),
+ MTK_PIN(PINCTRL_PIN(197, "TDMO1_LRCK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 202),
+ MTK_FUNCTION(0, "GPIO197"),
+ MTK_FUNCTION(1, "TDMO1_LRCK"),
+ MTK_FUNCTION(2, "TDMO0_LRCK"),
+ MTK_FUNCTION(3, "TDMIN_LRCK"),
+ MTK_FUNCTION(4, "TDMO0_DATA3"),
+ MTK_FUNCTION(5, "TDMO1_DATA3"),
+ MTK_FUNCTION(6, "I2SO3_MCK"),
+ MTK_FUNCTION(7, "TDMO1_DATA2")
+ ),
+ MTK_PIN(PINCTRL_PIN(198, "TDMO1_BCK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 203),
+ MTK_FUNCTION(0, "GPIO198"),
+ MTK_FUNCTION(1, "TDMO1_BCK"),
+ MTK_FUNCTION(2, "TDMO0_BCK"),
+ MTK_FUNCTION(3, "TDMIN_BCK"),
+ MTK_FUNCTION(4, "TDMO0_DATA2"),
+ MTK_FUNCTION(5, "TDMO1_DATA2"),
+ MTK_FUNCTION(6, "I2SO3_BCK"),
+ MTK_FUNCTION(7, "TDMO1_DATA1")
+ ),
+ MTK_PIN(PINCTRL_PIN(199, "TDMO1_DATA"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 204),
+ MTK_FUNCTION(0, "GPIO199"),
+ MTK_FUNCTION(1, "TDMO1_DATA"),
+ MTK_FUNCTION(2, "TDMO0_DATA"),
+ MTK_FUNCTION(3, "TDMIN_DI"),
+ MTK_FUNCTION(4, "TDMO0_DATA1"),
+ MTK_FUNCTION(5, "TDMO1_DATA1"),
+ MTK_FUNCTION(6, "I2SO3_WS")
+ ),
+ MTK_PIN(PINCTRL_PIN(200, "TDMO0_MCLK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 205),
+ MTK_FUNCTION(0, "GPIO200"),
+ MTK_FUNCTION(1, "TDMO0_MCLK"),
+ MTK_FUNCTION(2, "TDMO1_MCLK"),
+ MTK_FUNCTION(3, "PCM1_DI"),
+ MTK_FUNCTION(4, "TDMO0_MCLK"),
+ MTK_FUNCTION(5, "TDMO1_MCLK"),
+ MTK_FUNCTION(6, "MRG_TX"),
+ MTK_FUNCTION(7, "I2SO2_MCK")
+ ),
+ MTK_PIN(PINCTRL_PIN(201, "TDMO0_LRCK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 206),
+ MTK_FUNCTION(0, "GPIO201"),
+ MTK_FUNCTION(1, "TDMO0_LRCK"),
+ MTK_FUNCTION(2, "TDMO1_LRCK"),
+ MTK_FUNCTION(3, "PCM1_SYNC"),
+ MTK_FUNCTION(4, "TDMO0_LRCK"),
+ MTK_FUNCTION(5, "TDMO1_LRCK"),
+ MTK_FUNCTION(6, "MRG_RX"),
+ MTK_FUNCTION(7, "I2SO2_WS")
+ ),
+ MTK_PIN(PINCTRL_PIN(202, "TDMO0_BCK"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 207),
+ MTK_FUNCTION(0, "GPIO202"),
+ MTK_FUNCTION(1, "TDMO0_BCK"),
+ MTK_FUNCTION(2, "TDMO1_BCK"),
+ MTK_FUNCTION(3, "PCM1_CLK"),
+ MTK_FUNCTION(4, "TDMO0_BCK"),
+ MTK_FUNCTION(5, "TDMO1_BCK"),
+ MTK_FUNCTION(6, "MRG_SYNC"),
+ MTK_FUNCTION(7, "I2SO2_BCK")
+ ),
+ MTK_PIN(PINCTRL_PIN(203, "TDMO0_DATA"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 208),
+ MTK_FUNCTION(0, "GPIO203"),
+ MTK_FUNCTION(1, "TDMO0_DATA"),
+ MTK_FUNCTION(2, "TDMO1_DATA"),
+ MTK_FUNCTION(3, "PCM1_DO"),
+ MTK_FUNCTION(4, "TDMO0_DATA"),
+ MTK_FUNCTION(5, "TDMO1_DATA"),
+ MTK_FUNCTION(6, "MRG_CLK"),
+ MTK_FUNCTION(7, "I2SO2_DO")
+ ),
+ MTK_PIN(PINCTRL_PIN(204, "PERSTB_P0"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 209),
+ MTK_FUNCTION(0, "GPIO204"),
+ MTK_FUNCTION(1, "PERST_B_P0")
+ ),
+ MTK_PIN(PINCTRL_PIN(205, "CLKREQN_P0"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 210),
+ MTK_FUNCTION(0, "GPIO205"),
+ MTK_FUNCTION(1, "CLKREQ_N_P0")
+ ),
+ MTK_PIN(PINCTRL_PIN(206, "WAKEEN_P0"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 211),
+ MTK_FUNCTION(0, "GPIO206"),
+ MTK_FUNCTION(1, "WAKE_EN_P0")
+ ),
+ MTK_PIN(PINCTRL_PIN(207, "PERSTB_P1"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 212),
+ MTK_FUNCTION(0, "GPIO207"),
+ MTK_FUNCTION(1, "PERST_B_P1")
+ ),
+ MTK_PIN(PINCTRL_PIN(208, "CLKREQN_P1"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 213),
+ MTK_FUNCTION(0, "GPIO208"),
+ MTK_FUNCTION(1, "CLKREQ_N_P1")
+ ),
+ MTK_PIN(PINCTRL_PIN(209, "WAKEEN_P1"),
+ NULL, "mt2712",
+ MTK_EINT_FUNCTION(0, 214),
+ MTK_FUNCTION(0, "GPIO209"),
+ MTK_FUNCTION(1, "WAKE_EN_P1")
+ ),
+};
+
+#endif /* __PINCTRL_MTK_MT2712_H */
diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-mt6397.h b/drivers/pinctrl/mediatek/pinctrl-mtk-mt6397.h
index 17df4cfbde4e..0622293ab7b8 100644
--- a/drivers/pinctrl/mediatek/pinctrl-mtk-mt6397.h
+++ b/drivers/pinctrl/mediatek/pinctrl-mtk-mt6397.h
@@ -6,74 +6,64 @@
#include "pinctrl-mtk-common.h"
static const struct mtk_desc_pin mtk_pins_mt6397[] = {
- MTK_PIN(
- PINCTRL_PIN(0, "INT"),
+ MTK_PIN(PINCTRL_PIN(0, "INT"),
"N2", "mt6397",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO0"),
MTK_FUNCTION(1, "INT")
),
- MTK_PIN(
- PINCTRL_PIN(1, "SRCVOLTEN"),
+ MTK_PIN(PINCTRL_PIN(1, "SRCVOLTEN"),
"M4", "mt6397",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO1"),
MTK_FUNCTION(1, "SRCVOLTEN"),
MTK_FUNCTION(6, "TEST_CK1")
),
- MTK_PIN(
- PINCTRL_PIN(2, "SRCLKEN_PERI"),
+ MTK_PIN(PINCTRL_PIN(2, "SRCLKEN_PERI"),
"M2", "mt6397",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO2"),
MTK_FUNCTION(1, "SRCLKEN_PERI"),
MTK_FUNCTION(6, "TEST_CK2")
),
- MTK_PIN(
- PINCTRL_PIN(3, "RTC_32K1V8"),
+ MTK_PIN(PINCTRL_PIN(3, "RTC_32K1V8"),
"K3", "mt6397",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO3"),
MTK_FUNCTION(1, "RTC_32K1V8"),
MTK_FUNCTION(6, "TEST_CK3")
),
- MTK_PIN(
- PINCTRL_PIN(4, "WRAP_EVENT"),
+ MTK_PIN(PINCTRL_PIN(4, "WRAP_EVENT"),
"J2", "mt6397",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO4"),
MTK_FUNCTION(1, "WRAP_EVENT")
),
- MTK_PIN(
- PINCTRL_PIN(5, "SPI_CLK"),
+ MTK_PIN(PINCTRL_PIN(5, "SPI_CLK"),
"L4", "mt6397",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO5"),
MTK_FUNCTION(1, "SPI_CLK")
),
- MTK_PIN(
- PINCTRL_PIN(6, "SPI_CSN"),
+ MTK_PIN(PINCTRL_PIN(6, "SPI_CSN"),
"J3", "mt6397",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO6"),
MTK_FUNCTION(1, "SPI_CSN")
),
- MTK_PIN(
- PINCTRL_PIN(7, "SPI_MOSI"),
+ MTK_PIN(PINCTRL_PIN(7, "SPI_MOSI"),
"J1", "mt6397",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO7"),
MTK_FUNCTION(1, "SPI_MOSI")
),
- MTK_PIN(
- PINCTRL_PIN(8, "SPI_MISO"),
+ MTK_PIN(PINCTRL_PIN(8, "SPI_MISO"),
"L3", "mt6397",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO8"),
MTK_FUNCTION(1, "SPI_MISO")
),
- MTK_PIN(
- PINCTRL_PIN(9, "AUD_CLK_MOSI"),
+ MTK_PIN(PINCTRL_PIN(9, "AUD_CLK_MOSI"),
"H2", "mt6397",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO9"),
@@ -81,8 +71,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN0"),
MTK_FUNCTION(7, "TEST_OUT0")
),
- MTK_PIN(
- PINCTRL_PIN(10, "AUD_DAT_MISO"),
+ MTK_PIN(PINCTRL_PIN(10, "AUD_DAT_MISO"),
"H3", "mt6397",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO10"),
@@ -90,8 +79,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN1"),
MTK_FUNCTION(7, "TEST_OUT1")
),
- MTK_PIN(
- PINCTRL_PIN(11, "AUD_DAT_MOSI"),
+ MTK_PIN(PINCTRL_PIN(11, "AUD_DAT_MOSI"),
"H1", "mt6397",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO11"),
@@ -99,8 +87,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN2"),
MTK_FUNCTION(7, "TEST_OUT2")
),
- MTK_PIN(
- PINCTRL_PIN(12, "COL0"),
+ MTK_PIN(PINCTRL_PIN(12, "COL0"),
"F3", "mt6397",
MTK_EINT_FUNCTION(2, 10),
MTK_FUNCTION(0, "GPIO12"),
@@ -110,8 +97,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN3"),
MTK_FUNCTION(7, "TEST_OUT3")
),
- MTK_PIN(
- PINCTRL_PIN(13, "COL1"),
+ MTK_PIN(PINCTRL_PIN(13, "COL1"),
"G8", "mt6397",
MTK_EINT_FUNCTION(2, 11),
MTK_FUNCTION(0, "GPIO13"),
@@ -121,8 +107,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN4"),
MTK_FUNCTION(7, "TEST_OUT4")
),
- MTK_PIN(
- PINCTRL_PIN(14, "COL2"),
+ MTK_PIN(PINCTRL_PIN(14, "COL2"),
"H4", "mt6397",
MTK_EINT_FUNCTION(2, 12),
MTK_FUNCTION(0, "GPIO14"),
@@ -132,8 +117,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN5"),
MTK_FUNCTION(7, "TEST_OUT5")
),
- MTK_PIN(
- PINCTRL_PIN(15, "COL3"),
+ MTK_PIN(PINCTRL_PIN(15, "COL3"),
"G2", "mt6397",
MTK_EINT_FUNCTION(2, 13),
MTK_FUNCTION(0, "GPIO15"),
@@ -143,8 +127,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN6"),
MTK_FUNCTION(7, "TEST_OUT6")
),
- MTK_PIN(
- PINCTRL_PIN(16, "COL4"),
+ MTK_PIN(PINCTRL_PIN(16, "COL4"),
"F2", "mt6397",
MTK_EINT_FUNCTION(2, 14),
MTK_FUNCTION(0, "GPIO16"),
@@ -154,8 +137,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN7"),
MTK_FUNCTION(7, "TEST_OUT7")
),
- MTK_PIN(
- PINCTRL_PIN(17, "COL5"),
+ MTK_PIN(PINCTRL_PIN(17, "COL5"),
"G7", "mt6397",
MTK_EINT_FUNCTION(2, 15),
MTK_FUNCTION(0, "GPIO17"),
@@ -165,8 +147,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN8"),
MTK_FUNCTION(7, "TEST_OUT8")
),
- MTK_PIN(
- PINCTRL_PIN(18, "COL6"),
+ MTK_PIN(PINCTRL_PIN(18, "COL6"),
"J6", "mt6397",
MTK_EINT_FUNCTION(2, 16),
MTK_FUNCTION(0, "GPIO18"),
@@ -178,8 +159,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN9"),
MTK_FUNCTION(7, "TEST_OUT9")
),
- MTK_PIN(
- PINCTRL_PIN(19, "COL7"),
+ MTK_PIN(PINCTRL_PIN(19, "COL7"),
"J5", "mt6397",
MTK_EINT_FUNCTION(2, 17),
MTK_FUNCTION(0, "GPIO19"),
@@ -191,8 +171,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN10"),
MTK_FUNCTION(7, "TEST_OUT10")
),
- MTK_PIN(
- PINCTRL_PIN(20, "ROW0"),
+ MTK_PIN(PINCTRL_PIN(20, "ROW0"),
"L7", "mt6397",
MTK_EINT_FUNCTION(2, 18),
MTK_FUNCTION(0, "GPIO20"),
@@ -202,8 +181,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN11"),
MTK_FUNCTION(7, "TEST_OUT11")
),
- MTK_PIN(
- PINCTRL_PIN(21, "ROW1"),
+ MTK_PIN(PINCTRL_PIN(21, "ROW1"),
"P1", "mt6397",
MTK_EINT_FUNCTION(2, 19),
MTK_FUNCTION(0, "GPIO21"),
@@ -214,8 +192,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN12"),
MTK_FUNCTION(7, "TEST_OUT12")
),
- MTK_PIN(
- PINCTRL_PIN(22, "ROW2"),
+ MTK_PIN(PINCTRL_PIN(22, "ROW2"),
"J8", "mt6397",
MTK_EINT_FUNCTION(2, 20),
MTK_FUNCTION(0, "GPIO22"),
@@ -225,8 +202,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN13"),
MTK_FUNCTION(7, "TEST_OUT13")
),
- MTK_PIN(
- PINCTRL_PIN(23, "ROW3"),
+ MTK_PIN(PINCTRL_PIN(23, "ROW3"),
"J7", "mt6397",
MTK_EINT_FUNCTION(2, 21),
MTK_FUNCTION(0, "GPIO23"),
@@ -236,8 +212,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN14"),
MTK_FUNCTION(7, "TEST_OUT14")
),
- MTK_PIN(
- PINCTRL_PIN(24, "ROW4"),
+ MTK_PIN(PINCTRL_PIN(24, "ROW4"),
"L5", "mt6397",
MTK_EINT_FUNCTION(2, 22),
MTK_FUNCTION(0, "GPIO24"),
@@ -247,8 +222,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN15"),
MTK_FUNCTION(7, "TEST_OUT15")
),
- MTK_PIN(
- PINCTRL_PIN(25, "ROW5"),
+ MTK_PIN(PINCTRL_PIN(25, "ROW5"),
"N6", "mt6397",
MTK_EINT_FUNCTION(2, 23),
MTK_FUNCTION(0, "GPIO25"),
@@ -258,8 +232,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN16"),
MTK_FUNCTION(7, "TEST_OUT16")
),
- MTK_PIN(
- PINCTRL_PIN(26, "ROW6"),
+ MTK_PIN(PINCTRL_PIN(26, "ROW6"),
"L6", "mt6397",
MTK_EINT_FUNCTION(2, 24),
MTK_FUNCTION(0, "GPIO26"),
@@ -271,8 +244,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN17"),
MTK_FUNCTION(7, "TEST_OUT17")
),
- MTK_PIN(
- PINCTRL_PIN(27, "ROW7"),
+ MTK_PIN(PINCTRL_PIN(27, "ROW7"),
"P2", "mt6397",
MTK_EINT_FUNCTION(2, 3),
MTK_FUNCTION(0, "GPIO27"),
@@ -284,8 +256,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN18"),
MTK_FUNCTION(7, "TEST_OUT18")
),
- MTK_PIN(
- PINCTRL_PIN(28, "PWM1(VMSEL1)"),
+ MTK_PIN(PINCTRL_PIN(28, "PWM1(VMSEL1)"),
"J4", "mt6397",
MTK_EINT_FUNCTION(2, 4),
MTK_FUNCTION(0, "GPIO28"),
@@ -296,8 +267,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN19"),
MTK_FUNCTION(7, "TEST_OUT19")
),
- MTK_PIN(
- PINCTRL_PIN(29, "PWM2(VMSEL2)"),
+ MTK_PIN(PINCTRL_PIN(29, "PWM2(VMSEL2)"),
"N5", "mt6397",
MTK_EINT_FUNCTION(2, 5),
MTK_FUNCTION(0, "GPIO29"),
@@ -308,8 +278,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN20"),
MTK_FUNCTION(7, "TEST_OUT20")
),
- MTK_PIN(
- PINCTRL_PIN(30, "PWM3(PWM)"),
+ MTK_PIN(PINCTRL_PIN(30, "PWM3(PWM)"),
"R3", "mt6397",
MTK_EINT_FUNCTION(2, 6),
MTK_FUNCTION(0, "GPIO30"),
@@ -321,8 +290,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN21"),
MTK_FUNCTION(7, "TEST_OUT21")
),
- MTK_PIN(
- PINCTRL_PIN(31, "SCL0"),
+ MTK_PIN(PINCTRL_PIN(31, "SCL0"),
"N1", "mt6397",
MTK_EINT_FUNCTION(2, 7),
MTK_FUNCTION(0, "GPIO31"),
@@ -332,8 +300,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN22"),
MTK_FUNCTION(7, "TEST_OUT22")
),
- MTK_PIN(
- PINCTRL_PIN(32, "SDA0"),
+ MTK_PIN(PINCTRL_PIN(32, "SDA0"),
"N3", "mt6397",
MTK_EINT_FUNCTION(2, 8),
MTK_FUNCTION(0, "GPIO32"),
@@ -342,8 +309,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN23"),
MTK_FUNCTION(7, "TEST_OUT23")
),
- MTK_PIN(
- PINCTRL_PIN(33, "SCL1"),
+ MTK_PIN(PINCTRL_PIN(33, "SCL1"),
"T1", "mt6397",
MTK_EINT_FUNCTION(2, 9),
MTK_FUNCTION(0, "GPIO33"),
@@ -353,8 +319,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN24"),
MTK_FUNCTION(7, "TEST_OUT24")
),
- MTK_PIN(
- PINCTRL_PIN(34, "SDA1"),
+ MTK_PIN(PINCTRL_PIN(34, "SDA1"),
"T2", "mt6397",
MTK_EINT_FUNCTION(2, 0),
MTK_FUNCTION(0, "GPIO34"),
@@ -363,8 +328,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN25"),
MTK_FUNCTION(7, "TEST_OUT25")
),
- MTK_PIN(
- PINCTRL_PIN(35, "SCL2"),
+ MTK_PIN(PINCTRL_PIN(35, "SCL2"),
"T3", "mt6397",
MTK_EINT_FUNCTION(2, 1),
MTK_FUNCTION(0, "GPIO35"),
@@ -374,8 +338,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN26"),
MTK_FUNCTION(7, "TEST_OUT26")
),
- MTK_PIN(
- PINCTRL_PIN(36, "SDA2"),
+ MTK_PIN(PINCTRL_PIN(36, "SDA2"),
"U2", "mt6397",
MTK_EINT_FUNCTION(2, 2),
MTK_FUNCTION(0, "GPIO36"),
@@ -384,8 +347,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN27"),
MTK_FUNCTION(7, "TEST_OUT27")
),
- MTK_PIN(
- PINCTRL_PIN(37, "HDMISD"),
+ MTK_PIN(PINCTRL_PIN(37, "HDMISD"),
"H6", "mt6397",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO37"),
@@ -393,8 +355,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN28"),
MTK_FUNCTION(7, "TEST_OUT28")
),
- MTK_PIN(
- PINCTRL_PIN(38, "HDMISCK"),
+ MTK_PIN(PINCTRL_PIN(38, "HDMISCK"),
"H5", "mt6397",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO38"),
@@ -402,8 +363,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN29"),
MTK_FUNCTION(7, "TEST_OUT29")
),
- MTK_PIN(
- PINCTRL_PIN(39, "HTPLG"),
+ MTK_PIN(PINCTRL_PIN(39, "HTPLG"),
"H7", "mt6397",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO39"),
@@ -411,8 +371,7 @@ static const struct mtk_desc_pin mtk_pins_mt6397[] = {
MTK_FUNCTION(6, "TEST_IN30"),
MTK_FUNCTION(7, "TEST_OUT30")
),
- MTK_PIN(
- PINCTRL_PIN(40, "CEC"),
+ MTK_PIN(PINCTRL_PIN(40, "CEC"),
"J9", "mt6397",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO40"),
diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-mt8127.h b/drivers/pinctrl/mediatek/pinctrl-mtk-mt8127.h
index 850483d7d9be..0a1254f96e95 100644
--- a/drivers/pinctrl/mediatek/pinctrl-mtk-mt8127.h
+++ b/drivers/pinctrl/mediatek/pinctrl-mtk-mt8127.h
@@ -6,44 +6,38 @@
#include "pinctrl-mtk-common.h"
static const struct mtk_desc_pin mtk_pins_mt8127[] = {
- MTK_PIN(
- PINCTRL_PIN(0, "PWRAP_SPI0_MI"),
+ MTK_PIN(PINCTRL_PIN(0, "PWRAP_SPI0_MI"),
"P22", "mt8127",
MTK_EINT_FUNCTION(0, 22),
MTK_FUNCTION(0, "GPIO0"),
MTK_FUNCTION(1, "PWRAP_SPIDO"),
MTK_FUNCTION(2, "PWRAP_SPIDI")
),
- MTK_PIN(
- PINCTRL_PIN(1, "PWRAP_SPI0_MO"),
+ MTK_PIN(PINCTRL_PIN(1, "PWRAP_SPI0_MO"),
"M22", "mt8127",
MTK_EINT_FUNCTION(0, 23),
MTK_FUNCTION(0, "GPIO1"),
MTK_FUNCTION(1, "PWRAP_SPIDI"),
MTK_FUNCTION(2, "PWRAP_SPIDO")
),
- MTK_PIN(
- PINCTRL_PIN(2, "PWRAP_INT"),
+ MTK_PIN(PINCTRL_PIN(2, "PWRAP_INT"),
"L23", "mt8127",
MTK_EINT_FUNCTION(0, 24),
MTK_FUNCTION(0, "GPIO2")
),
- MTK_PIN(
- PINCTRL_PIN(3, "PWRAP_SPI0_CK"),
+ MTK_PIN(PINCTRL_PIN(3, "PWRAP_SPI0_CK"),
"N23", "mt8127",
MTK_EINT_FUNCTION(0, 25),
MTK_FUNCTION(0, "GPIO3"),
MTK_FUNCTION(1, "PWRAP_SPICK_I")
),
- MTK_PIN(
- PINCTRL_PIN(4, "PWRAP_SPI0_CSN"),
+ MTK_PIN(PINCTRL_PIN(4, "PWRAP_SPI0_CSN"),
"N22", "mt8127",
MTK_EINT_FUNCTION(0, 26),
MTK_FUNCTION(0, "GPIO4"),
MTK_FUNCTION(1, "PWRAP_SPICS_B_I")
),
- MTK_PIN(
- PINCTRL_PIN(5, "PWRAP_SPI0_CK2"),
+ MTK_PIN(PINCTRL_PIN(5, "PWRAP_SPI0_CK2"),
"L19", "mt8127",
MTK_EINT_FUNCTION(0, 27),
MTK_FUNCTION(0, "GPIO5"),
@@ -52,8 +46,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(3, "VDEC_TEST_CK"),
MTK_FUNCTION(7, "DBG_MON_B[0]")
),
- MTK_PIN(
- PINCTRL_PIN(6, "PWRAP_SPI0_CSN2"),
+ MTK_PIN(PINCTRL_PIN(6, "PWRAP_SPI0_CSN2"),
"M23", "mt8127",
MTK_EINT_FUNCTION(0, 28),
MTK_FUNCTION(0, "GPIO6"),
@@ -62,16 +55,14 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(3, "MM_TEST_CK"),
MTK_FUNCTION(7, "DBG_MON_B[1]")
),
- MTK_PIN(
- PINCTRL_PIN(7, "AUD_CLK_MOSI"),
+ MTK_PIN(PINCTRL_PIN(7, "AUD_CLK_MOSI"),
"K23", "mt8127",
MTK_EINT_FUNCTION(0, 29),
MTK_FUNCTION(0, "GPIO7"),
MTK_FUNCTION(1, "AUD_CLK"),
MTK_FUNCTION(2, "ADC_CK")
),
- MTK_PIN(
- PINCTRL_PIN(8, "AUD_DAT_MISO"),
+ MTK_PIN(PINCTRL_PIN(8, "AUD_DAT_MISO"),
"K24", "mt8127",
MTK_EINT_FUNCTION(0, 30),
MTK_FUNCTION(0, "GPIO8"),
@@ -79,8 +70,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(2, "ADC_DAT_IN"),
MTK_FUNCTION(3, "AUD_MOSI")
),
- MTK_PIN(
- PINCTRL_PIN(9, "AUD_DAT_MOSI"),
+ MTK_PIN(PINCTRL_PIN(9, "AUD_DAT_MOSI"),
"K22", "mt8127",
MTK_EINT_FUNCTION(0, 31),
MTK_FUNCTION(0, "GPIO9"),
@@ -88,36 +78,31 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(2, "ADC_WS"),
MTK_FUNCTION(3, "AUD_MISO")
),
- MTK_PIN(
- PINCTRL_PIN(10, "RTC32K_CK"),
+ MTK_PIN(PINCTRL_PIN(10, "RTC32K_CK"),
"R21", "mt8127",
MTK_EINT_FUNCTION(0, 32),
MTK_FUNCTION(0, "GPIO10"),
MTK_FUNCTION(1, "RTC32K_CK")
),
- MTK_PIN(
- PINCTRL_PIN(11, "WATCHDOG"),
+ MTK_PIN(PINCTRL_PIN(11, "WATCHDOG"),
"P24", "mt8127",
MTK_EINT_FUNCTION(0, 33),
MTK_FUNCTION(0, "GPIO11"),
MTK_FUNCTION(1, "WATCHDOG")
),
- MTK_PIN(
- PINCTRL_PIN(12, "SRCLKENA"),
+ MTK_PIN(PINCTRL_PIN(12, "SRCLKENA"),
"R22", "mt8127",
MTK_EINT_FUNCTION(0, 34),
MTK_FUNCTION(0, "GPIO12"),
MTK_FUNCTION(1, "SRCLKENA")
),
- MTK_PIN(
- PINCTRL_PIN(13, "SRCLKENAI"),
+ MTK_PIN(PINCTRL_PIN(13, "SRCLKENAI"),
"P23", "mt8127",
MTK_EINT_FUNCTION(0, 35),
MTK_FUNCTION(0, "GPIO13"),
MTK_FUNCTION(1, "SRCLKENAI")
),
- MTK_PIN(
- PINCTRL_PIN(14, "URXD2"),
+ MTK_PIN(PINCTRL_PIN(14, "URXD2"),
"U19", "mt8127",
MTK_EINT_FUNCTION(0, 36),
MTK_FUNCTION(0, "GPIO14"),
@@ -127,8 +112,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(5, "SRCCLKENAI2"),
MTK_FUNCTION(6, "KROW4")
),
- MTK_PIN(
- PINCTRL_PIN(15, "UTXD2"),
+ MTK_PIN(PINCTRL_PIN(15, "UTXD2"),
"U20", "mt8127",
MTK_EINT_FUNCTION(0, 37),
MTK_FUNCTION(0, "GPIO15"),
@@ -137,8 +121,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(3, "URXD2"),
MTK_FUNCTION(6, "KROW5")
),
- MTK_PIN(
- PINCTRL_PIN(16, "URXD3"),
+ MTK_PIN(PINCTRL_PIN(16, "URXD3"),
"U18", "mt8127",
MTK_EINT_FUNCTION(0, 38),
MTK_FUNCTION(0, "GPIO16"),
@@ -149,8 +132,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(5, "PWM3"),
MTK_FUNCTION(6, "KROW6")
),
- MTK_PIN(
- PINCTRL_PIN(17, "UTXD3"),
+ MTK_PIN(PINCTRL_PIN(17, "UTXD3"),
"R18", "mt8127",
MTK_EINT_FUNCTION(0, 39),
MTK_FUNCTION(0, "GPIO17"),
@@ -161,8 +143,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(5, "PWM4"),
MTK_FUNCTION(6, "KROW7")
),
- MTK_PIN(
- PINCTRL_PIN(18, "PCM_CLK"),
+ MTK_PIN(PINCTRL_PIN(18, "PCM_CLK"),
"U22", "mt8127",
MTK_EINT_FUNCTION(0, 40),
MTK_FUNCTION(0, "GPIO18"),
@@ -174,8 +155,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "IR"),
MTK_FUNCTION(7, "DBG_MON_A[0]")
),
- MTK_PIN(
- PINCTRL_PIN(19, "PCM_SYNC"),
+ MTK_PIN(PINCTRL_PIN(19, "PCM_SYNC"),
"U23", "mt8127",
MTK_EINT_FUNCTION(0, 41),
MTK_FUNCTION(0, "GPIO19"),
@@ -187,8 +167,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "EXT_COL"),
MTK_FUNCTION(7, "DBG_MON_A[1]")
),
- MTK_PIN(
- PINCTRL_PIN(20, "PCM_RX"),
+ MTK_PIN(PINCTRL_PIN(20, "PCM_RX"),
"V22", "mt8127",
MTK_EINT_FUNCTION(0, 42),
MTK_FUNCTION(0, "GPIO20"),
@@ -200,8 +179,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "EXT_MDIO"),
MTK_FUNCTION(7, "DBG_MON_A[2]")
),
- MTK_PIN(
- PINCTRL_PIN(21, "PCM_TX"),
+ MTK_PIN(PINCTRL_PIN(21, "PCM_TX"),
"U21", "mt8127",
MTK_EINT_FUNCTION(0, 43),
MTK_FUNCTION(0, "GPIO21"),
@@ -213,8 +191,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "EXT_MDC"),
MTK_FUNCTION(7, "DBG_MON_A[3]")
),
- MTK_PIN(
- PINCTRL_PIN(22, "EINT0"),
+ MTK_PIN(PINCTRL_PIN(22, "EINT0"),
"AB19", "mt8127",
MTK_EINT_FUNCTION(0, 0),
MTK_FUNCTION(0, "GPIO22"),
@@ -224,8 +201,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(5, "CONN_DSP_JDO"),
MTK_FUNCTION(7, "DBG_MON_A[4]")
),
- MTK_PIN(
- PINCTRL_PIN(23, "EINT1"),
+ MTK_PIN(PINCTRL_PIN(23, "EINT1"),
"AA21", "mt8127",
MTK_EINT_FUNCTION(0, 1),
MTK_FUNCTION(0, "GPIO23"),
@@ -235,8 +211,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(5, "CONN_MCU_TDO"),
MTK_FUNCTION(7, "DBG_MON_A[5]")
),
- MTK_PIN(
- PINCTRL_PIN(24, "EINT2"),
+ MTK_PIN(PINCTRL_PIN(24, "EINT2"),
"AA19", "mt8127",
MTK_EINT_FUNCTION(0, 2),
MTK_FUNCTION(0, "GPIO24"),
@@ -247,8 +222,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "KCOL4"),
MTK_FUNCTION(7, "DBG_MON_A[6]")
),
- MTK_PIN(
- PINCTRL_PIN(25, "EINT3"),
+ MTK_PIN(PINCTRL_PIN(25, "EINT3"),
"Y19", "mt8127",
MTK_EINT_FUNCTION(0, 3),
MTK_FUNCTION(0, "GPIO25"),
@@ -260,8 +234,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "KCOL5"),
MTK_FUNCTION(7, "DBG_MON_A[7]")
),
- MTK_PIN(
- PINCTRL_PIN(26, "EINT4"),
+ MTK_PIN(PINCTRL_PIN(26, "EINT4"),
"V21", "mt8127",
MTK_EINT_FUNCTION(0, 4),
MTK_FUNCTION(0, "GPIO26"),
@@ -273,8 +246,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "CONN_MCU_AICE_JCKC"),
MTK_FUNCTION(7, "DBG_MON_A[8]")
),
- MTK_PIN(
- PINCTRL_PIN(27, "EINT5"),
+ MTK_PIN(PINCTRL_PIN(27, "EINT5"),
"AB22", "mt8127",
MTK_EINT_FUNCTION(0, 5),
MTK_FUNCTION(0, "GPIO27"),
@@ -286,8 +258,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "KCOL6"),
MTK_FUNCTION(7, "DBG_MON_A[9]")
),
- MTK_PIN(
- PINCTRL_PIN(28, "EINT6"),
+ MTK_PIN(PINCTRL_PIN(28, "EINT6"),
"AA23", "mt8127",
MTK_EINT_FUNCTION(0, 6),
MTK_FUNCTION(0, "GPIO28"),
@@ -299,8 +270,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "KCOL7"),
MTK_FUNCTION(7, "DBG_MON_A[10]")
),
- MTK_PIN(
- PINCTRL_PIN(29, "EINT7"),
+ MTK_PIN(PINCTRL_PIN(29, "EINT7"),
"Y23", "mt8127",
MTK_EINT_FUNCTION(0, 7),
MTK_FUNCTION(0, "GPIO29"),
@@ -312,8 +282,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "CONN_MCU_AICE_JMSC"),
MTK_FUNCTION(7, "DBG_MON_A[11]")
),
- MTK_PIN(
- PINCTRL_PIN(30, "EINT8"),
+ MTK_PIN(PINCTRL_PIN(30, "EINT8"),
"Y24", "mt8127",
MTK_EINT_FUNCTION(0, 8),
MTK_FUNCTION(0, "GPIO30"),
@@ -325,8 +294,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "DPI_D7"),
MTK_FUNCTION(7, "DBG_MON_B[2]")
),
- MTK_PIN(
- PINCTRL_PIN(31, "EINT9"),
+ MTK_PIN(PINCTRL_PIN(31, "EINT9"),
"W23", "mt8127",
MTK_EINT_FUNCTION(0, 9),
MTK_FUNCTION(0, "GPIO31"),
@@ -338,8 +306,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "DPI_D8"),
MTK_FUNCTION(7, "DBG_MON_B[3]")
),
- MTK_PIN(
- PINCTRL_PIN(32, "EINT10"),
+ MTK_PIN(PINCTRL_PIN(32, "EINT10"),
"W24", "mt8127",
MTK_EINT_FUNCTION(0, 10),
MTK_FUNCTION(0, "GPIO32"),
@@ -351,8 +318,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "DPI_D9"),
MTK_FUNCTION(7, "DBG_MON_B[4]")
),
- MTK_PIN(
- PINCTRL_PIN(33, "KPROW0"),
+ MTK_PIN(PINCTRL_PIN(33, "KPROW0"),
"AB24", "mt8127",
MTK_EINT_FUNCTION(0, 44),
MTK_FUNCTION(0, "GPIO33"),
@@ -360,8 +326,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(4, "IMG_TEST_CK"),
MTK_FUNCTION(7, "DBG_MON_A[12]")
),
- MTK_PIN(
- PINCTRL_PIN(34, "KPROW1"),
+ MTK_PIN(PINCTRL_PIN(34, "KPROW1"),
"AC24", "mt8127",
MTK_EINT_FUNCTION(0, 45),
MTK_FUNCTION(0, "GPIO34"),
@@ -371,8 +336,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(4, "MFG_TEST_CK"),
MTK_FUNCTION(7, "DBG_MON_B[5]")
),
- MTK_PIN(
- PINCTRL_PIN(35, "KPROW2"),
+ MTK_PIN(PINCTRL_PIN(35, "KPROW2"),
"AD24", "mt8127",
MTK_EINT_FUNCTION(0, 46),
MTK_FUNCTION(0, "GPIO35"),
@@ -382,24 +346,21 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(4, "CONN_TEST_CK"),
MTK_FUNCTION(7, "DBG_MON_B[6]")
),
- MTK_PIN(
- PINCTRL_PIN(36, "KPCOL0"),
+ MTK_PIN(PINCTRL_PIN(36, "KPCOL0"),
"AB23", "mt8127",
MTK_EINT_FUNCTION(0, 47),
MTK_FUNCTION(0, "GPIO36"),
MTK_FUNCTION(1, "KCOL0"),
MTK_FUNCTION(7, "DBG_MON_A[13]")
),
- MTK_PIN(
- PINCTRL_PIN(37, "KPCOL1"),
+ MTK_PIN(PINCTRL_PIN(37, "KPCOL1"),
"AC22", "mt8127",
MTK_EINT_FUNCTION(0, 48),
MTK_FUNCTION(0, "GPIO37"),
MTK_FUNCTION(1, "KCOL1"),
MTK_FUNCTION(7, "DBG_MON_B[7]")
),
- MTK_PIN(
- PINCTRL_PIN(38, "KPCOL2"),
+ MTK_PIN(PINCTRL_PIN(38, "KPCOL2"),
"AC23", "mt8127",
MTK_EINT_FUNCTION(0, 49),
MTK_FUNCTION(0, "GPIO38"),
@@ -408,8 +369,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(3, "EXT_FRAME_SYNC"),
MTK_FUNCTION(7, "DBG_MON_B[8]")
),
- MTK_PIN(
- PINCTRL_PIN(39, "JTMS"),
+ MTK_PIN(PINCTRL_PIN(39, "JTMS"),
"V18", "mt8127",
MTK_EINT_FUNCTION(0, 50),
MTK_FUNCTION(0, "GPIO39"),
@@ -417,8 +377,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(2, "CONN_MCU_TMS"),
MTK_FUNCTION(3, "CONN_MCU_AICE_JMSC")
),
- MTK_PIN(
- PINCTRL_PIN(40, "JTCK"),
+ MTK_PIN(PINCTRL_PIN(40, "JTCK"),
"AA18", "mt8127",
MTK_EINT_FUNCTION(0, 51),
MTK_FUNCTION(0, "GPIO40"),
@@ -426,24 +385,21 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(2, "CONN_MCU_TCK1"),
MTK_FUNCTION(3, "CONN_MCU_AICE_JCKC")
),
- MTK_PIN(
- PINCTRL_PIN(41, "JTDI"),
+ MTK_PIN(PINCTRL_PIN(41, "JTDI"),
"W18", "mt8127",
MTK_EINT_FUNCTION(0, 52),
MTK_FUNCTION(0, "GPIO41"),
MTK_FUNCTION(1, "JTDI"),
MTK_FUNCTION(2, "CONN_MCU_TDI")
),
- MTK_PIN(
- PINCTRL_PIN(42, "JTDO"),
+ MTK_PIN(PINCTRL_PIN(42, "JTDO"),
"Y18", "mt8127",
MTK_EINT_FUNCTION(0, 53),
MTK_FUNCTION(0, "GPIO42"),
MTK_FUNCTION(1, "JTDO"),
MTK_FUNCTION(2, "CONN_MCU_TDO")
),
- MTK_PIN(
- PINCTRL_PIN(43, "EINT11"),
+ MTK_PIN(PINCTRL_PIN(43, "EINT11"),
"W22", "mt8127",
MTK_EINT_FUNCTION(0, 11),
MTK_FUNCTION(0, "GPIO43"),
@@ -455,8 +411,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "EXT_RXD3"),
MTK_FUNCTION(7, "DBG_MON_B[9]")
),
- MTK_PIN(
- PINCTRL_PIN(44, "EINT12"),
+ MTK_PIN(PINCTRL_PIN(44, "EINT12"),
"V23", "mt8127",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO44"),
@@ -468,8 +423,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "EXT_TXEN"),
MTK_FUNCTION(7, "DBG_MON_B[10]")
),
- MTK_PIN(
- PINCTRL_PIN(45, "EINT13"),
+ MTK_PIN(PINCTRL_PIN(45, "EINT13"),
"Y21", "mt8127",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO45"),
@@ -478,8 +432,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "SPDIF"),
MTK_FUNCTION(7, "DBG_MON_B[11]")
),
- MTK_PIN(
- PINCTRL_PIN(46, "EINT14"),
+ MTK_PIN(PINCTRL_PIN(46, "EINT14"),
"F23", "mt8127",
MTK_EINT_FUNCTION(0, 14),
MTK_FUNCTION(0, "GPIO46"),
@@ -489,8 +442,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "NCLE"),
MTK_FUNCTION(7, "DBG_MON_A[14]")
),
- MTK_PIN(
- PINCTRL_PIN(47, "EINT15"),
+ MTK_PIN(PINCTRL_PIN(47, "EINT15"),
"G23", "mt8127",
MTK_EINT_FUNCTION(0, 15),
MTK_FUNCTION(0, "GPIO47"),
@@ -500,8 +452,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "NCEB1"),
MTK_FUNCTION(7, "DBG_MON_A[15]")
),
- MTK_PIN(
- PINCTRL_PIN(48, "EINT16"),
+ MTK_PIN(PINCTRL_PIN(48, "EINT16"),
"H23", "mt8127",
MTK_EINT_FUNCTION(0, 16),
MTK_FUNCTION(0, "GPIO48"),
@@ -511,8 +462,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "NCEB0"),
MTK_FUNCTION(7, "DBG_MON_A[16]")
),
- MTK_PIN(
- PINCTRL_PIN(49, "EINT17"),
+ MTK_PIN(PINCTRL_PIN(49, "EINT17"),
"J22", "mt8127",
MTK_EINT_FUNCTION(0, 17),
MTK_FUNCTION(0, "GPIO49"),
@@ -523,8 +473,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "NREB"),
MTK_FUNCTION(7, "DBG_MON_A[17]")
),
- MTK_PIN(
- PINCTRL_PIN(50, "EINT18"),
+ MTK_PIN(PINCTRL_PIN(50, "EINT18"),
"AD20", "mt8127",
MTK_EINT_FUNCTION(0, 18),
MTK_FUNCTION(0, "GPIO50"),
@@ -536,8 +485,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "ADC_CK"),
MTK_FUNCTION(7, "DBG_MON_B[12]")
),
- MTK_PIN(
- PINCTRL_PIN(51, "EINT19"),
+ MTK_PIN(PINCTRL_PIN(51, "EINT19"),
"AC21", "mt8127",
MTK_EINT_FUNCTION(0, 19),
MTK_FUNCTION(0, "GPIO51"),
@@ -548,8 +496,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "ADC_DAT_IN"),
MTK_FUNCTION(7, "DBG_MON_B[13]")
),
- MTK_PIN(
- PINCTRL_PIN(52, "EINT20"),
+ MTK_PIN(PINCTRL_PIN(52, "EINT20"),
"V20", "mt8127",
MTK_EINT_FUNCTION(0, 20),
MTK_FUNCTION(0, "GPIO52"),
@@ -561,8 +508,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "ADC_WS"),
MTK_FUNCTION(7, "DBG_MON_B[14]")
),
- MTK_PIN(
- PINCTRL_PIN(53, "SPI_CS"),
+ MTK_PIN(PINCTRL_PIN(53, "SPI_CS"),
"AD19", "mt8127",
MTK_EINT_FUNCTION(0, 54),
MTK_FUNCTION(0, "GPIO53"),
@@ -571,8 +517,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(4, "ADC_CK"),
MTK_FUNCTION(7, "DBG_MON_B[15]")
),
- MTK_PIN(
- PINCTRL_PIN(54, "SPI_CK"),
+ MTK_PIN(PINCTRL_PIN(54, "SPI_CK"),
"AC18", "mt8127",
MTK_EINT_FUNCTION(0, 55),
MTK_FUNCTION(0, "GPIO54"),
@@ -581,8 +526,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(4, "ADC_DAT_IN"),
MTK_FUNCTION(7, "DBG_MON_B[16]")
),
- MTK_PIN(
- PINCTRL_PIN(55, "SPI_MI"),
+ MTK_PIN(PINCTRL_PIN(55, "SPI_MI"),
"AC19", "mt8127",
MTK_EINT_FUNCTION(0, 56),
MTK_FUNCTION(0, "GPIO55"),
@@ -592,8 +536,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(4, "ADC_WS"),
MTK_FUNCTION(7, "DBG_MON_B[17]")
),
- MTK_PIN(
- PINCTRL_PIN(56, "SPI_MO"),
+ MTK_PIN(PINCTRL_PIN(56, "SPI_MO"),
"AD18", "mt8127",
MTK_EINT_FUNCTION(0, 57),
MTK_FUNCTION(0, "GPIO56"),
@@ -601,22 +544,19 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(2, "SPI_MI"),
MTK_FUNCTION(7, "DBG_MON_B[18]")
),
- MTK_PIN(
- PINCTRL_PIN(57, "SDA1"),
+ MTK_PIN(PINCTRL_PIN(57, "SDA1"),
"AE23", "mt8127",
MTK_EINT_FUNCTION(0, 58),
MTK_FUNCTION(0, "GPIO57"),
MTK_FUNCTION(1, "SDA1")
),
- MTK_PIN(
- PINCTRL_PIN(58, "SCL1"),
+ MTK_PIN(PINCTRL_PIN(58, "SCL1"),
"AD23", "mt8127",
MTK_EINT_FUNCTION(0, 59),
MTK_FUNCTION(0, "GPIO58"),
MTK_FUNCTION(1, "SCL1")
),
- MTK_PIN(
- PINCTRL_PIN(59, "DISP_PWM"),
+ MTK_PIN(PINCTRL_PIN(59, "DISP_PWM"),
"AC20", "mt8127",
MTK_EINT_FUNCTION(0, 60),
MTK_FUNCTION(0, "GPIO59"),
@@ -624,56 +564,49 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(2, "PWM1"),
MTK_FUNCTION(7, "DBG_MON_A[18]")
),
- MTK_PIN(
- PINCTRL_PIN(60, "WB_RSTB"),
+ MTK_PIN(PINCTRL_PIN(60, "WB_RSTB"),
"AD7", "mt8127",
MTK_EINT_FUNCTION(0, 61),
MTK_FUNCTION(0, "GPIO60"),
MTK_FUNCTION(1, "WB_RSTB"),
MTK_FUNCTION(7, "DBG_MON_A[19]")
),
- MTK_PIN(
- PINCTRL_PIN(61, "F2W_DATA"),
+ MTK_PIN(PINCTRL_PIN(61, "F2W_DATA"),
"Y10", "mt8127",
MTK_EINT_FUNCTION(0, 62),
MTK_FUNCTION(0, "GPIO61"),
MTK_FUNCTION(1, "F2W_DATA"),
MTK_FUNCTION(7, "DBG_MON_A[20]")
),
- MTK_PIN(
- PINCTRL_PIN(62, "F2W_CLK"),
+ MTK_PIN(PINCTRL_PIN(62, "F2W_CLK"),
"W10", "mt8127",
MTK_EINT_FUNCTION(0, 63),
MTK_FUNCTION(0, "GPIO62"),
MTK_FUNCTION(1, "F2W_CK"),
MTK_FUNCTION(7, "DBG_MON_A[21]")
),
- MTK_PIN(
- PINCTRL_PIN(63, "WB_SCLK"),
+ MTK_PIN(PINCTRL_PIN(63, "WB_SCLK"),
"AB7", "mt8127",
MTK_EINT_FUNCTION(0, 64),
MTK_FUNCTION(0, "GPIO63"),
MTK_FUNCTION(1, "WB_SCLK"),
MTK_FUNCTION(7, "DBG_MON_A[22]")
),
- MTK_PIN(
- PINCTRL_PIN(64, "WB_SDATA"),
+ MTK_PIN(PINCTRL_PIN(64, "WB_SDATA"),
"AA7", "mt8127",
MTK_EINT_FUNCTION(0, 65),
MTK_FUNCTION(0, "GPIO64"),
MTK_FUNCTION(1, "WB_SDATA"),
MTK_FUNCTION(7, "DBG_MON_A[23]")
),
- MTK_PIN(
- PINCTRL_PIN(65, "WB_SEN"),
+ MTK_PIN(PINCTRL_PIN(65, "WB_SEN"),
"Y7", "mt8127",
MTK_EINT_FUNCTION(0, 66),
MTK_FUNCTION(0, "GPIO65"),
MTK_FUNCTION(1, "WB_SEN"),
MTK_FUNCTION(7, "DBG_MON_A[24]")
),
- MTK_PIN(
- PINCTRL_PIN(66, "WB_CRTL0"),
+ MTK_PIN(PINCTRL_PIN(66, "WB_CRTL0"),
"AA1", "mt8127",
MTK_EINT_FUNCTION(0, 67),
MTK_FUNCTION(0, "GPIO66"),
@@ -681,8 +614,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(2, "DFD_NTRST_XI"),
MTK_FUNCTION(7, "DBG_MON_A[25]")
),
- MTK_PIN(
- PINCTRL_PIN(67, "WB_CRTL1"),
+ MTK_PIN(PINCTRL_PIN(67, "WB_CRTL1"),
"AA2", "mt8127",
MTK_EINT_FUNCTION(0, 68),
MTK_FUNCTION(0, "GPIO67"),
@@ -690,8 +622,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(2, "DFD_TMS_XI"),
MTK_FUNCTION(7, "DBG_MON_A[26]")
),
- MTK_PIN(
- PINCTRL_PIN(68, "WB_CRTL2"),
+ MTK_PIN(PINCTRL_PIN(68, "WB_CRTL2"),
"Y1", "mt8127",
MTK_EINT_FUNCTION(0, 69),
MTK_FUNCTION(0, "GPIO68"),
@@ -699,8 +630,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(2, "DFD_TCK_XI"),
MTK_FUNCTION(7, "DBG_MON_A[27]")
),
- MTK_PIN(
- PINCTRL_PIN(69, "WB_CRTL3"),
+ MTK_PIN(PINCTRL_PIN(69, "WB_CRTL3"),
"Y2", "mt8127",
MTK_EINT_FUNCTION(0, 70),
MTK_FUNCTION(0, "GPIO69"),
@@ -708,8 +638,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(2, "DFD_TDI_XI"),
MTK_FUNCTION(7, "DBG_MON_A[28]")
),
- MTK_PIN(
- PINCTRL_PIN(70, "WB_CRTL4"),
+ MTK_PIN(PINCTRL_PIN(70, "WB_CRTL4"),
"Y3", "mt8127",
MTK_EINT_FUNCTION(0, 71),
MTK_FUNCTION(0, "GPIO70"),
@@ -717,16 +646,14 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(2, "DFD_TDO"),
MTK_FUNCTION(7, "DBG_MON_A[29]")
),
- MTK_PIN(
- PINCTRL_PIN(71, "WB_CRTL5"),
+ MTK_PIN(PINCTRL_PIN(71, "WB_CRTL5"),
"Y4", "mt8127",
MTK_EINT_FUNCTION(0, 72),
MTK_FUNCTION(0, "GPIO71"),
MTK_FUNCTION(1, "WB_CRTL5"),
MTK_FUNCTION(7, "DBG_MON_A[30]")
),
- MTK_PIN(
- PINCTRL_PIN(72, "I2S_DATA_IN"),
+ MTK_PIN(PINCTRL_PIN(72, "I2S_DATA_IN"),
"K21", "mt8127",
MTK_EINT_FUNCTION(0, 73),
MTK_FUNCTION(0, "GPIO72"),
@@ -738,8 +665,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "ADC_CK"),
MTK_FUNCTION(7, "DBG_MON_B[19]")
),
- MTK_PIN(
- PINCTRL_PIN(73, "I2S_LRCK"),
+ MTK_PIN(PINCTRL_PIN(73, "I2S_LRCK"),
"L21", "mt8127",
MTK_EINT_FUNCTION(0, 74),
MTK_FUNCTION(0, "GPIO73"),
@@ -751,8 +677,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "ADC_DAT_IN"),
MTK_FUNCTION(7, "DBG_MON_B[20]")
),
- MTK_PIN(
- PINCTRL_PIN(74, "I2S_BCK"),
+ MTK_PIN(PINCTRL_PIN(74, "I2S_BCK"),
"L20", "mt8127",
MTK_EINT_FUNCTION(0, 75),
MTK_FUNCTION(0, "GPIO74"),
@@ -764,70 +689,61 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "ADC_WS"),
MTK_FUNCTION(7, "DBG_MON_B[21]")
),
- MTK_PIN(
- PINCTRL_PIN(75, "SDA0"),
+ MTK_PIN(PINCTRL_PIN(75, "SDA0"),
"W3", "mt8127",
MTK_EINT_FUNCTION(0, 76),
MTK_FUNCTION(0, "GPIO75"),
MTK_FUNCTION(1, "SDA0")
),
- MTK_PIN(
- PINCTRL_PIN(76, "SCL0"),
+ MTK_PIN(PINCTRL_PIN(76, "SCL0"),
"W4", "mt8127",
MTK_EINT_FUNCTION(0, 77),
MTK_FUNCTION(0, "GPIO76"),
MTK_FUNCTION(1, "SCL0")
),
- MTK_PIN(
- PINCTRL_PIN(77, "SDA2"),
+ MTK_PIN(PINCTRL_PIN(77, "SDA2"),
"K19", "mt8127",
MTK_EINT_FUNCTION(0, 78),
MTK_FUNCTION(0, "GPIO77"),
MTK_FUNCTION(1, "SDA2"),
MTK_FUNCTION(2, "PWM1")
),
- MTK_PIN(
- PINCTRL_PIN(78, "SCL2"),
+ MTK_PIN(PINCTRL_PIN(78, "SCL2"),
"K20", "mt8127",
MTK_EINT_FUNCTION(0, 79),
MTK_FUNCTION(0, "GPIO78"),
MTK_FUNCTION(1, "SCL2"),
MTK_FUNCTION(2, "PWM2")
),
- MTK_PIN(
- PINCTRL_PIN(79, "URXD0"),
+ MTK_PIN(PINCTRL_PIN(79, "URXD0"),
"K18", "mt8127",
MTK_EINT_FUNCTION(0, 80),
MTK_FUNCTION(0, "GPIO79"),
MTK_FUNCTION(1, "URXD0"),
MTK_FUNCTION(2, "UTXD0")
),
- MTK_PIN(
- PINCTRL_PIN(80, "UTXD0"),
+ MTK_PIN(PINCTRL_PIN(80, "UTXD0"),
"K17", "mt8127",
MTK_EINT_FUNCTION(0, 81),
MTK_FUNCTION(0, "GPIO80"),
MTK_FUNCTION(1, "UTXD0"),
MTK_FUNCTION(2, "URXD0")
),
- MTK_PIN(
- PINCTRL_PIN(81, "URXD1"),
+ MTK_PIN(PINCTRL_PIN(81, "URXD1"),
"L17", "mt8127",
MTK_EINT_FUNCTION(0, 82),
MTK_FUNCTION(0, "GPIO81"),
MTK_FUNCTION(1, "URXD1"),
MTK_FUNCTION(2, "UTXD1")
),
- MTK_PIN(
- PINCTRL_PIN(82, "UTXD1"),
+ MTK_PIN(PINCTRL_PIN(82, "UTXD1"),
"L18", "mt8127",
MTK_EINT_FUNCTION(0, 83),
MTK_FUNCTION(0, "GPIO82"),
MTK_FUNCTION(1, "UTXD1"),
MTK_FUNCTION(2, "URXD1")
),
- MTK_PIN(
- PINCTRL_PIN(83, "LCM_RST"),
+ MTK_PIN(PINCTRL_PIN(83, "LCM_RST"),
"W5", "mt8127",
MTK_EINT_FUNCTION(0, 84),
MTK_FUNCTION(0, "GPIO83"),
@@ -835,16 +751,14 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(2, "VDAC_CK_XI"),
MTK_FUNCTION(7, "DBG_MON_A[31]")
),
- MTK_PIN(
- PINCTRL_PIN(84, "DSI_TE"),
+ MTK_PIN(PINCTRL_PIN(84, "DSI_TE"),
"W6", "mt8127",
MTK_EINT_FUNCTION(0, 85),
MTK_FUNCTION(0, "GPIO84"),
MTK_FUNCTION(1, "DSI_TE"),
MTK_FUNCTION(7, "DBG_MON_A[32]")
),
- MTK_PIN(
- PINCTRL_PIN(85, "MSDC2_CMD"),
+ MTK_PIN(PINCTRL_PIN(85, "MSDC2_CMD"),
"U7", "mt8127",
MTK_EINT_FUNCTION(0, 86),
MTK_FUNCTION(0, "GPIO85"),
@@ -854,8 +768,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "I2SOUT_BCK"),
MTK_FUNCTION(7, "DBG_MON_B[22]")
),
- MTK_PIN(
- PINCTRL_PIN(86, "MSDC2_CLK"),
+ MTK_PIN(PINCTRL_PIN(86, "MSDC2_CLK"),
"T8", "mt8127",
MTK_EINT_FUNCTION(0, 87),
MTK_FUNCTION(0, "GPIO86"),
@@ -865,8 +778,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "I2SOUT_LRCK"),
MTK_FUNCTION(7, "DBG_MON_B[23]")
),
- MTK_PIN(
- PINCTRL_PIN(87, "MSDC2_DAT0"),
+ MTK_PIN(PINCTRL_PIN(87, "MSDC2_DAT0"),
"V3", "mt8127",
MTK_EINT_FUNCTION(0, 88),
MTK_FUNCTION(0, "GPIO87"),
@@ -876,8 +788,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "I2SOUT_DATA_OUT"),
MTK_FUNCTION(7, "DBG_MON_B[24]")
),
- MTK_PIN(
- PINCTRL_PIN(88, "MSDC2_DAT1"),
+ MTK_PIN(PINCTRL_PIN(88, "MSDC2_DAT1"),
"V4", "mt8127",
MTK_EINT_FUNCTION(0, 89),
MTK_FUNCTION(0, "GPIO88"),
@@ -888,8 +799,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "PWM1"),
MTK_FUNCTION(7, "DBG_MON_B[25]")
),
- MTK_PIN(
- PINCTRL_PIN(89, "MSDC2_DAT2"),
+ MTK_PIN(PINCTRL_PIN(89, "MSDC2_DAT2"),
"U5", "mt8127",
MTK_EINT_FUNCTION(0, 90),
MTK_FUNCTION(0, "GPIO89"),
@@ -900,8 +810,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "PWM2"),
MTK_FUNCTION(7, "DBG_MON_B[26]")
),
- MTK_PIN(
- PINCTRL_PIN(90, "MSDC2_DAT3"),
+ MTK_PIN(PINCTRL_PIN(90, "MSDC2_DAT3"),
"U6", "mt8127",
MTK_EINT_FUNCTION(0, 91),
MTK_FUNCTION(0, "GPIO90"),
@@ -913,168 +822,145 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(6, "PWM3"),
MTK_FUNCTION(7, "DBG_MON_B[27]")
),
- MTK_PIN(
- PINCTRL_PIN(91, "TDN3"),
+ MTK_PIN(PINCTRL_PIN(91, "TDN3"),
"U2", "mt8127",
MTK_EINT_FUNCTION(0, 92),
MTK_FUNCTION(0, "GPI91"),
MTK_FUNCTION(1, "TDN3")
),
- MTK_PIN(
- PINCTRL_PIN(92, "TDP3"),
+ MTK_PIN(PINCTRL_PIN(92, "TDP3"),
"U1", "mt8127",
MTK_EINT_FUNCTION(0, 93),
MTK_FUNCTION(0, "GPI92"),
MTK_FUNCTION(1, "TDP3")
),
- MTK_PIN(
- PINCTRL_PIN(93, "TDN2"),
+ MTK_PIN(PINCTRL_PIN(93, "TDN2"),
"T2", "mt8127",
MTK_EINT_FUNCTION(0, 94),
MTK_FUNCTION(0, "GPI93"),
MTK_FUNCTION(1, "TDN2")
),
- MTK_PIN(
- PINCTRL_PIN(94, "TDP2"),
+ MTK_PIN(PINCTRL_PIN(94, "TDP2"),
"T1", "mt8127",
MTK_EINT_FUNCTION(0, 95),
MTK_FUNCTION(0, "GPI94"),
MTK_FUNCTION(1, "TDP2")
),
- MTK_PIN(
- PINCTRL_PIN(95, "TCN"),
+ MTK_PIN(PINCTRL_PIN(95, "TCN"),
"R5", "mt8127",
MTK_EINT_FUNCTION(0, 96),
MTK_FUNCTION(0, "GPI95"),
MTK_FUNCTION(1, "TCN")
),
- MTK_PIN(
- PINCTRL_PIN(96, "TCP"),
+ MTK_PIN(PINCTRL_PIN(96, "TCP"),
"R4", "mt8127",
MTK_EINT_FUNCTION(0, 97),
MTK_FUNCTION(0, "GPI96"),
MTK_FUNCTION(1, "TCP")
),
- MTK_PIN(
- PINCTRL_PIN(97, "TDN1"),
+ MTK_PIN(PINCTRL_PIN(97, "TDN1"),
"R3", "mt8127",
MTK_EINT_FUNCTION(0, 98),
MTK_FUNCTION(0, "GPI97"),
MTK_FUNCTION(1, "TDN1")
),
- MTK_PIN(
- PINCTRL_PIN(98, "TDP1"),
+ MTK_PIN(PINCTRL_PIN(98, "TDP1"),
"R2", "mt8127",
MTK_EINT_FUNCTION(0, 99),
MTK_FUNCTION(0, "GPI98"),
MTK_FUNCTION(1, "TDP1")
),
- MTK_PIN(
- PINCTRL_PIN(99, "TDN0"),
+ MTK_PIN(PINCTRL_PIN(99, "TDN0"),
"P3", "mt8127",
MTK_EINT_FUNCTION(0, 100),
MTK_FUNCTION(0, "GPI99"),
MTK_FUNCTION(1, "TDN0")
),
- MTK_PIN(
- PINCTRL_PIN(100, "TDP0"),
+ MTK_PIN(PINCTRL_PIN(100, "TDP0"),
"P2", "mt8127",
MTK_EINT_FUNCTION(0, 101),
MTK_FUNCTION(0, "GPI100"),
MTK_FUNCTION(1, "TDP0")
),
- MTK_PIN(
- PINCTRL_PIN(101, "RDN0"),
+ MTK_PIN(PINCTRL_PIN(101, "RDN0"),
"K1", "mt8127",
MTK_EINT_FUNCTION(0, 102),
MTK_FUNCTION(0, "GPI101"),
MTK_FUNCTION(1, "RDN0")
),
- MTK_PIN(
- PINCTRL_PIN(102, "RDP0"),
+ MTK_PIN(PINCTRL_PIN(102, "RDP0"),
"K2", "mt8127",
MTK_EINT_FUNCTION(0, 103),
MTK_FUNCTION(0, "GPI102"),
MTK_FUNCTION(1, "RDP0")
),
- MTK_PIN(
- PINCTRL_PIN(103, "RDN1"),
+ MTK_PIN(PINCTRL_PIN(103, "RDN1"),
"L2", "mt8127",
MTK_EINT_FUNCTION(0, 104),
MTK_FUNCTION(0, "GPI103"),
MTK_FUNCTION(1, "RDN1")
),
- MTK_PIN(
- PINCTRL_PIN(104, "RDP1"),
+ MTK_PIN(PINCTRL_PIN(104, "RDP1"),
"L3", "mt8127",
MTK_EINT_FUNCTION(0, 105),
MTK_FUNCTION(0, "GPI104"),
MTK_FUNCTION(1, "RDP1")
),
- MTK_PIN(
- PINCTRL_PIN(105, "RCN"),
+ MTK_PIN(PINCTRL_PIN(105, "RCN"),
"M4", "mt8127",
MTK_EINT_FUNCTION(0, 106),
MTK_FUNCTION(0, "GPI105"),
MTK_FUNCTION(1, "RCN")
),
- MTK_PIN(
- PINCTRL_PIN(106, "RCP"),
+ MTK_PIN(PINCTRL_PIN(106, "RCP"),
"M5", "mt8127",
MTK_EINT_FUNCTION(0, 107),
MTK_FUNCTION(0, "GPI106"),
MTK_FUNCTION(1, "RCP")
),
- MTK_PIN(
- PINCTRL_PIN(107, "RDN2"),
+ MTK_PIN(PINCTRL_PIN(107, "RDN2"),
"M2", "mt8127",
MTK_EINT_FUNCTION(0, 108),
MTK_FUNCTION(0, "GPI107"),
MTK_FUNCTION(1, "RDN2"),
MTK_FUNCTION(2, "CMDAT8")
),
- MTK_PIN(
- PINCTRL_PIN(108, "RDP2"),
+ MTK_PIN(PINCTRL_PIN(108, "RDP2"),
"M3", "mt8127",
MTK_EINT_FUNCTION(0, 109),
MTK_FUNCTION(0, "GPI108"),
MTK_FUNCTION(1, "RDP2"),
MTK_FUNCTION(2, "CMDAT9")
),
- MTK_PIN(
- PINCTRL_PIN(109, "RDN3"),
+ MTK_PIN(PINCTRL_PIN(109, "RDN3"),
"N2", "mt8127",
MTK_EINT_FUNCTION(0, 110),
MTK_FUNCTION(0, "GPI109"),
MTK_FUNCTION(1, "RDN3"),
MTK_FUNCTION(2, "CMDAT4")
),
- MTK_PIN(
- PINCTRL_PIN(110, "RDP3"),
+ MTK_PIN(PINCTRL_PIN(110, "RDP3"),
"N3", "mt8127",
MTK_EINT_FUNCTION(0, 111),
MTK_FUNCTION(0, "GPI110"),
MTK_FUNCTION(1, "RDP3"),
MTK_FUNCTION(2, "CMDAT5")
),
- MTK_PIN(
- PINCTRL_PIN(111, "RCN_A"),
+ MTK_PIN(PINCTRL_PIN(111, "RCN_A"),
"J5", "mt8127",
MTK_EINT_FUNCTION(0, 112),
MTK_FUNCTION(0, "GPI111"),
MTK_FUNCTION(1, "RCN_A"),
MTK_FUNCTION(2, "CMDAT6")
),
- MTK_PIN(
- PINCTRL_PIN(112, "RCP_A"),
+ MTK_PIN(PINCTRL_PIN(112, "RCP_A"),
"J4", "mt8127",
MTK_EINT_FUNCTION(0, 113),
MTK_FUNCTION(0, "GPI112"),
MTK_FUNCTION(1, "RCP_A"),
MTK_FUNCTION(2, "CMDAT7")
),
- MTK_PIN(
- PINCTRL_PIN(113, "RDN1_A"),
+ MTK_PIN(PINCTRL_PIN(113, "RDN1_A"),
"J2", "mt8127",
MTK_EINT_FUNCTION(0, 114),
MTK_FUNCTION(0, "GPI113"),
@@ -1082,8 +968,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(2, "CMDAT2"),
MTK_FUNCTION(3, "CMCSD2")
),
- MTK_PIN(
- PINCTRL_PIN(114, "RDP1_A"),
+ MTK_PIN(PINCTRL_PIN(114, "RDP1_A"),
"J3", "mt8127",
MTK_EINT_FUNCTION(0, 115),
MTK_FUNCTION(0, "GPI114"),
@@ -1091,24 +976,21 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(2, "CMDAT3"),
MTK_FUNCTION(3, "CMCSD3")
),
- MTK_PIN(
- PINCTRL_PIN(115, "RDN0_A"),
+ MTK_PIN(PINCTRL_PIN(115, "RDN0_A"),
"H2", "mt8127",
MTK_EINT_FUNCTION(0, 116),
MTK_FUNCTION(0, "GPI115"),
MTK_FUNCTION(1, "RDN0_A"),
MTK_FUNCTION(2, "CMHSYNC")
),
- MTK_PIN(
- PINCTRL_PIN(116, "RDP0_A"),
+ MTK_PIN(PINCTRL_PIN(116, "RDP0_A"),
"H3", "mt8127",
MTK_EINT_FUNCTION(0, 117),
MTK_FUNCTION(0, "GPI116"),
MTK_FUNCTION(1, "RDP0_A"),
MTK_FUNCTION(2, "CMVSYNC")
),
- MTK_PIN(
- PINCTRL_PIN(117, "CMDAT0"),
+ MTK_PIN(PINCTRL_PIN(117, "CMDAT0"),
"G5", "mt8127",
MTK_EINT_FUNCTION(0, 118),
MTK_FUNCTION(0, "GPIO117"),
@@ -1117,8 +999,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(3, "ANT_SEL2"),
MTK_FUNCTION(7, "DBG_MON_B[28]")
),
- MTK_PIN(
- PINCTRL_PIN(118, "CMDAT1"),
+ MTK_PIN(PINCTRL_PIN(118, "CMDAT1"),
"G4", "mt8127",
MTK_EINT_FUNCTION(0, 119),
MTK_FUNCTION(0, "GPIO118"),
@@ -1127,8 +1008,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(3, "ANT_SEL3"),
MTK_FUNCTION(7, "DBG_MON_B[29]")
),
- MTK_PIN(
- PINCTRL_PIN(119, "CMMCLK"),
+ MTK_PIN(PINCTRL_PIN(119, "CMMCLK"),
"F3", "mt8127",
MTK_EINT_FUNCTION(0, 120),
MTK_FUNCTION(0, "GPIO119"),
@@ -1136,8 +1016,7 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(3, "ANT_SEL4"),
MTK_FUNCTION(7, "DBG_MON_B[30]")
),
- MTK_PIN(
- PINCTRL_PIN(120, "CMPCLK"),
+ MTK_PIN(PINCTRL_PIN(120, "CMPCLK"),
"G6", "mt8127",
MTK_EINT_FUNCTION(0, 121),
MTK_FUNCTION(0, "GPIO120"),
@@ -1146,130 +1025,113 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(3, "ANT_SEL5"),
MTK_FUNCTION(7, "DBG_MON_B[31]")
),
- MTK_PIN(
- PINCTRL_PIN(121, "MSDC1_CMD"),
+ MTK_PIN(PINCTRL_PIN(121, "MSDC1_CMD"),
"E3", "mt8127",
MTK_EINT_FUNCTION(0, 122),
MTK_FUNCTION(0, "GPIO121"),
MTK_FUNCTION(1, "MSDC1_CMD")
),
- MTK_PIN(
- PINCTRL_PIN(122, "MSDC1_CLK"),
+ MTK_PIN(PINCTRL_PIN(122, "MSDC1_CLK"),
"D1", "mt8127",
MTK_EINT_FUNCTION(0, 123),
MTK_FUNCTION(0, "GPIO122"),
MTK_FUNCTION(1, "MSDC1_CLK")
),
- MTK_PIN(
- PINCTRL_PIN(123, "MSDC1_DAT0"),
+ MTK_PIN(PINCTRL_PIN(123, "MSDC1_DAT0"),
"D2", "mt8127",
MTK_EINT_FUNCTION(0, 124),
MTK_FUNCTION(0, "GPIO123"),
MTK_FUNCTION(1, "MSDC1_DAT0")
),
- MTK_PIN(
- PINCTRL_PIN(124, "MSDC1_DAT1"),
+ MTK_PIN(PINCTRL_PIN(124, "MSDC1_DAT1"),
"D3", "mt8127",
MTK_EINT_FUNCTION(0, 125),
MTK_FUNCTION(0, "GPIO124"),
MTK_FUNCTION(1, "MSDC1_DAT1")
),
- MTK_PIN(
- PINCTRL_PIN(125, "MSDC1_DAT2"),
+ MTK_PIN(PINCTRL_PIN(125, "MSDC1_DAT2"),
"F2", "mt8127",
MTK_EINT_FUNCTION(0, 126),
MTK_FUNCTION(0, "GPIO125"),
MTK_FUNCTION(1, "MSDC1_DAT2")
),
- MTK_PIN(
- PINCTRL_PIN(126, "MSDC1_DAT3"),
+ MTK_PIN(PINCTRL_PIN(126, "MSDC1_DAT3"),
"E2", "mt8127",
MTK_EINT_FUNCTION(0, 127),
MTK_FUNCTION(0, "GPIO126"),
MTK_FUNCTION(1, "MSDC1_DAT3")
),
- MTK_PIN(
- PINCTRL_PIN(127, "MSDC0_DAT7"),
+ MTK_PIN(PINCTRL_PIN(127, "MSDC0_DAT7"),
"C23", "mt8127",
MTK_EINT_FUNCTION(0, 128),
MTK_FUNCTION(0, "GPIO127"),
MTK_FUNCTION(1, "MSDC0_DAT7"),
MTK_FUNCTION(4, "NLD7")
),
- MTK_PIN(
- PINCTRL_PIN(128, "MSDC0_DAT6"),
+ MTK_PIN(PINCTRL_PIN(128, "MSDC0_DAT6"),
"C24", "mt8127",
MTK_EINT_FUNCTION(0, 129),
MTK_FUNCTION(0, "GPIO128"),
MTK_FUNCTION(1, "MSDC0_DAT6"),
MTK_FUNCTION(4, "NLD6")
),
- MTK_PIN(
- PINCTRL_PIN(129, "MSDC0_DAT5"),
+ MTK_PIN(PINCTRL_PIN(129, "MSDC0_DAT5"),
"D22", "mt8127",
MTK_EINT_FUNCTION(0, 130),
MTK_FUNCTION(0, "GPIO129"),
MTK_FUNCTION(1, "MSDC0_DAT5"),
MTK_FUNCTION(4, "NLD4")
),
- MTK_PIN(
- PINCTRL_PIN(130, "MSDC0_DAT4"),
+ MTK_PIN(PINCTRL_PIN(130, "MSDC0_DAT4"),
"D24", "mt8127",
MTK_EINT_FUNCTION(0, 131),
MTK_FUNCTION(0, "GPIO130"),
MTK_FUNCTION(1, "MSDC0_DAT4"),
MTK_FUNCTION(4, "NLD3")
),
- MTK_PIN(
- PINCTRL_PIN(131, "MSDC0_RSTB"),
+ MTK_PIN(PINCTRL_PIN(131, "MSDC0_RSTB"),
"F24", "mt8127",
MTK_EINT_FUNCTION(0, 132),
MTK_FUNCTION(0, "GPIO131"),
MTK_FUNCTION(1, "MSDC0_RSTB"),
MTK_FUNCTION(4, "NLD0")
),
- MTK_PIN(
- PINCTRL_PIN(132, "MSDC0_CMD"),
+ MTK_PIN(PINCTRL_PIN(132, "MSDC0_CMD"),
"G20", "mt8127",
MTK_EINT_FUNCTION(0, 133),
MTK_FUNCTION(0, "GPIO132"),
MTK_FUNCTION(1, "MSDC0_CMD"),
MTK_FUNCTION(4, "NALE")
),
- MTK_PIN(
- PINCTRL_PIN(133, "MSDC0_CLK"),
+ MTK_PIN(PINCTRL_PIN(133, "MSDC0_CLK"),
"G21", "mt8127",
MTK_EINT_FUNCTION(0, 134),
MTK_FUNCTION(0, "GPIO133"),
MTK_FUNCTION(1, "MSDC0_CLK"),
MTK_FUNCTION(4, "NWEB")
),
- MTK_PIN(
- PINCTRL_PIN(134, "MSDC0_DAT3"),
+ MTK_PIN(PINCTRL_PIN(134, "MSDC0_DAT3"),
"D23", "mt8127",
MTK_EINT_FUNCTION(0, 135),
MTK_FUNCTION(0, "GPIO134"),
MTK_FUNCTION(1, "MSDC0_DAT3"),
MTK_FUNCTION(4, "NLD1")
),
- MTK_PIN(
- PINCTRL_PIN(135, "MSDC0_DAT2"),
+ MTK_PIN(PINCTRL_PIN(135, "MSDC0_DAT2"),
"E22", "mt8127",
MTK_EINT_FUNCTION(0, 136),
MTK_FUNCTION(0, "GPIO135"),
MTK_FUNCTION(1, "MSDC0_DAT2"),
MTK_FUNCTION(4, "NLD5")
),
- MTK_PIN(
- PINCTRL_PIN(136, "MSDC0_DAT1"),
+ MTK_PIN(PINCTRL_PIN(136, "MSDC0_DAT1"),
"E23", "mt8127",
MTK_EINT_FUNCTION(0, 137),
MTK_FUNCTION(0, "GPIO136"),
MTK_FUNCTION(1, "MSDC0_DAT1"),
MTK_FUNCTION(4, "NLD8")
),
- MTK_PIN(
- PINCTRL_PIN(137, "MSDC0_DAT0"),
+ MTK_PIN(PINCTRL_PIN(137, "MSDC0_DAT0"),
"F22", "mt8127",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO137"),
@@ -1277,36 +1139,31 @@ static const struct mtk_desc_pin mtk_pins_mt8127[] = {
MTK_FUNCTION(4, "WATCHDOG"),
MTK_FUNCTION(5, "NLD2")
),
- MTK_PIN(
- PINCTRL_PIN(138, "CEC"),
+ MTK_PIN(PINCTRL_PIN(138, "CEC"),
"AE21", "mt8127",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO138"),
MTK_FUNCTION(1, "CEC")
),
- MTK_PIN(
- PINCTRL_PIN(139, "HTPLG"),
+ MTK_PIN(PINCTRL_PIN(139, "HTPLG"),
"AD21", "mt8127",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO139"),
MTK_FUNCTION(1, "HTPLG")
),
- MTK_PIN(
- PINCTRL_PIN(140, "HDMISCK"),
+ MTK_PIN(PINCTRL_PIN(140, "HDMISCK"),
"AE22", "mt8127",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO140"),
MTK_FUNCTION(1, "HDMISCK")
),
- MTK_PIN(
- PINCTRL_PIN(141, "HDMISD"),
+ MTK_PIN(PINCTRL_PIN(141, "HDMISD"),
"AD22", "mt8127",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO141"),
MTK_FUNCTION(1, "HDMISD")
),
- MTK_PIN(
- PINCTRL_PIN(142, "EINT21"),
+ MTK_PIN(PINCTRL_PIN(142, "EINT21"),
"J23", "mt8127",
MTK_EINT_FUNCTION(0, 21),
MTK_FUNCTION(0, "GPIO142"),
diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-mt8135.h b/drivers/pinctrl/mediatek/pinctrl-mtk-mt8135.h
index e17aedb73c8d..b2b390da6597 100644
--- a/drivers/pinctrl/mediatek/pinctrl-mtk-mt8135.h
+++ b/drivers/pinctrl/mediatek/pinctrl-mtk-mt8135.h
@@ -19,8 +19,7 @@
#include "pinctrl-mtk-common.h"
static const struct mtk_desc_pin mtk_pins_mt8135[] = {
- MTK_PIN(
- PINCTRL_PIN(0, "MSDC0_DAT7"),
+ MTK_PIN(PINCTRL_PIN(0, "MSDC0_DAT7"),
"D21", "mt8135",
MTK_EINT_FUNCTION(2, 49),
MTK_FUNCTION(0, "GPIO0"),
@@ -32,8 +31,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "SPI1_MO"),
MTK_FUNCTION(7, "NALE")
),
- MTK_PIN(
- PINCTRL_PIN(1, "MSDC0_DAT6"),
+ MTK_PIN(PINCTRL_PIN(1, "MSDC0_DAT6"),
"D22", "mt8135",
MTK_EINT_FUNCTION(2, 48),
MTK_FUNCTION(0, "GPIO1"),
@@ -45,8 +43,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "SPI1_CSN"),
MTK_FUNCTION(7, "NCLE")
),
- MTK_PIN(
- PINCTRL_PIN(2, "MSDC0_DAT5"),
+ MTK_PIN(PINCTRL_PIN(2, "MSDC0_DAT5"),
"E22", "mt8135",
MTK_EINT_FUNCTION(2, 47),
MTK_FUNCTION(0, "GPIO2"),
@@ -58,8 +55,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "SPI1_CLK"),
MTK_FUNCTION(7, "NLD4")
),
- MTK_PIN(
- PINCTRL_PIN(3, "MSDC0_DAT4"),
+ MTK_PIN(PINCTRL_PIN(3, "MSDC0_DAT4"),
"F21", "mt8135",
MTK_EINT_FUNCTION(2, 46),
MTK_FUNCTION(0, "GPIO3"),
@@ -69,8 +65,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "LSCE1B_2X"),
MTK_FUNCTION(7, "NLD5")
),
- MTK_PIN(
- PINCTRL_PIN(4, "MSDC0_CMD"),
+ MTK_PIN(PINCTRL_PIN(4, "MSDC0_CMD"),
"F20", "mt8135",
MTK_EINT_FUNCTION(2, 41),
MTK_FUNCTION(0, "GPIO4"),
@@ -81,8 +76,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "LRSTB_2X"),
MTK_FUNCTION(7, "NRNB")
),
- MTK_PIN(
- PINCTRL_PIN(5, "MSDC0_CLK"),
+ MTK_PIN(PINCTRL_PIN(5, "MSDC0_CLK"),
"G18", "mt8135",
MTK_EINT_FUNCTION(2, 40),
MTK_FUNCTION(0, "GPIO5"),
@@ -93,8 +87,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "LPTE"),
MTK_FUNCTION(7, "NREB")
),
- MTK_PIN(
- PINCTRL_PIN(6, "MSDC0_DAT3"),
+ MTK_PIN(PINCTRL_PIN(6, "MSDC0_DAT3"),
"G21", "mt8135",
MTK_EINT_FUNCTION(2, 45),
MTK_FUNCTION(0, "GPIO6"),
@@ -105,8 +98,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "LSCE0B_2X"),
MTK_FUNCTION(7, "NLD7")
),
- MTK_PIN(
- PINCTRL_PIN(7, "MSDC0_DAT2"),
+ MTK_PIN(PINCTRL_PIN(7, "MSDC0_DAT2"),
"E21", "mt8135",
MTK_EINT_FUNCTION(2, 44),
MTK_FUNCTION(0, "GPIO7"),
@@ -117,8 +109,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "LSA0_2X"),
MTK_FUNCTION(7, "NLD14")
),
- MTK_PIN(
- PINCTRL_PIN(8, "MSDC0_DAT1"),
+ MTK_PIN(PINCTRL_PIN(8, "MSDC0_DAT1"),
"E23", "mt8135",
MTK_EINT_FUNCTION(2, 43),
MTK_FUNCTION(0, "GPIO8"),
@@ -128,8 +119,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "LSCK_2X"),
MTK_FUNCTION(7, "NLD11")
),
- MTK_PIN(
- PINCTRL_PIN(9, "MSDC0_DAT0"),
+ MTK_PIN(PINCTRL_PIN(9, "MSDC0_DAT0"),
"F22", "mt8135",
MTK_EINT_FUNCTION(2, 42),
MTK_FUNCTION(0, "GPIO9"),
@@ -138,8 +128,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "USB_TEST_IO[5]"),
MTK_FUNCTION(6, "LSDA_2X")
),
- MTK_PIN(
- PINCTRL_PIN(10, "NCEB0"),
+ MTK_PIN(PINCTRL_PIN(10, "NCEB0"),
"G20", "mt8135",
MTK_EINT_FUNCTION(2, 139),
MTK_FUNCTION(0, "GPIO10"),
@@ -147,8 +136,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(2, "EINT139"),
MTK_FUNCTION(7, "TESTA_OUT4")
),
- MTK_PIN(
- PINCTRL_PIN(11, "NCEB1"),
+ MTK_PIN(PINCTRL_PIN(11, "NCEB1"),
"L17", "mt8135",
MTK_EINT_FUNCTION(2, 140),
MTK_FUNCTION(0, "GPIO11"),
@@ -157,8 +145,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "USB_DRVVBUS"),
MTK_FUNCTION(7, "TESTA_OUT5")
),
- MTK_PIN(
- PINCTRL_PIN(12, "NRNB"),
+ MTK_PIN(PINCTRL_PIN(12, "NRNB"),
"G19", "mt8135",
MTK_EINT_FUNCTION(2, 141),
MTK_FUNCTION(0, "GPIO12"),
@@ -167,8 +154,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(3, "A_FUNC_DOUT[4]"),
MTK_FUNCTION(7, "TESTA_OUT6")
),
- MTK_PIN(
- PINCTRL_PIN(13, "NCLE"),
+ MTK_PIN(PINCTRL_PIN(13, "NCLE"),
"J18", "mt8135",
MTK_EINT_FUNCTION(2, 142),
MTK_FUNCTION(0, "GPIO13"),
@@ -179,8 +165,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "NALE"),
MTK_FUNCTION(7, "TESTA_OUT7")
),
- MTK_PIN(
- PINCTRL_PIN(14, "NALE"),
+ MTK_PIN(PINCTRL_PIN(14, "NALE"),
"J19", "mt8135",
MTK_EINT_FUNCTION(2, 143),
MTK_FUNCTION(0, "GPIO14"),
@@ -192,8 +177,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "NCLE"),
MTK_FUNCTION(7, "TESTA_OUT8")
),
- MTK_PIN(
- PINCTRL_PIN(15, "NREB"),
+ MTK_PIN(PINCTRL_PIN(15, "NREB"),
"L18", "mt8135",
MTK_EINT_FUNCTION(2, 144),
MTK_FUNCTION(0, "GPIO15"),
@@ -204,8 +188,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "IRDA_TXD"),
MTK_FUNCTION(7, "TESTA_OUT9")
),
- MTK_PIN(
- PINCTRL_PIN(16, "NWEB"),
+ MTK_PIN(PINCTRL_PIN(16, "NWEB"),
"J20", "mt8135",
MTK_EINT_FUNCTION(2, 145),
MTK_FUNCTION(0, "GPIO16"),
@@ -216,8 +199,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "IRDA_PDN"),
MTK_FUNCTION(7, "TESTA_OUT10")
),
- MTK_PIN(
- PINCTRL_PIN(17, "NLD0"),
+ MTK_PIN(PINCTRL_PIN(17, "NLD0"),
"K21", "mt8135",
MTK_EINT_FUNCTION(2, 146),
MTK_FUNCTION(0, "GPIO17"),
@@ -229,8 +211,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "DAC_CK"),
MTK_FUNCTION(7, "TESTA_OUT11")
),
- MTK_PIN(
- PINCTRL_PIN(18, "NLD1"),
+ MTK_PIN(PINCTRL_PIN(18, "NLD1"),
"K22", "mt8135",
MTK_EINT_FUNCTION(2, 147),
MTK_FUNCTION(0, "GPIO18"),
@@ -242,8 +223,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "DAC_WS"),
MTK_FUNCTION(7, "TESTA_OUT12")
),
- MTK_PIN(
- PINCTRL_PIN(19, "NLD2"),
+ MTK_PIN(PINCTRL_PIN(19, "NLD2"),
"J21", "mt8135",
MTK_EINT_FUNCTION(2, 148),
MTK_FUNCTION(0, "GPIO19"),
@@ -255,8 +235,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "DAC_DAT_OUT"),
MTK_FUNCTION(7, "TESTA_OUT13")
),
- MTK_PIN(
- PINCTRL_PIN(20, "NLD3"),
+ MTK_PIN(PINCTRL_PIN(20, "NLD3"),
"J23", "mt8135",
MTK_EINT_FUNCTION(2, 149),
MTK_FUNCTION(0, "GPIO20"),
@@ -266,8 +245,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "CM2DAT_1X[3]"),
MTK_FUNCTION(7, "TESTA_OUT14")
),
- MTK_PIN(
- PINCTRL_PIN(21, "NLD4"),
+ MTK_PIN(PINCTRL_PIN(21, "NLD4"),
"J22", "mt8135",
MTK_EINT_FUNCTION(2, 150),
MTK_FUNCTION(0, "GPIO21"),
@@ -277,8 +255,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "CM2DAT_1X[4]"),
MTK_FUNCTION(7, "TESTA_OUT15")
),
- MTK_PIN(
- PINCTRL_PIN(22, "NLD5"),
+ MTK_PIN(PINCTRL_PIN(22, "NLD5"),
"H21", "mt8135",
MTK_EINT_FUNCTION(2, 151),
MTK_FUNCTION(0, "GPIO22"),
@@ -288,8 +265,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "CM2DAT_1X[5]"),
MTK_FUNCTION(7, "TESTA_OUT16")
),
- MTK_PIN(
- PINCTRL_PIN(23, "NLD6"),
+ MTK_PIN(PINCTRL_PIN(23, "NLD6"),
"H22", "mt8135",
MTK_EINT_FUNCTION(2, 152),
MTK_FUNCTION(0, "GPIO23"),
@@ -299,8 +275,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "CM2DAT_1X[6]"),
MTK_FUNCTION(7, "TESTA_OUT17")
),
- MTK_PIN(
- PINCTRL_PIN(24, "NLD7"),
+ MTK_PIN(PINCTRL_PIN(24, "NLD7"),
"H20", "mt8135",
MTK_EINT_FUNCTION(2, 153),
MTK_FUNCTION(0, "GPIO24"),
@@ -310,8 +285,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "CM2DAT_1X[7]"),
MTK_FUNCTION(7, "TESTA_OUT18")
),
- MTK_PIN(
- PINCTRL_PIN(25, "NLD8"),
+ MTK_PIN(PINCTRL_PIN(25, "NLD8"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 154),
MTK_FUNCTION(0, "GPIO25"),
@@ -319,8 +293,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(2, "EINT154"),
MTK_FUNCTION(4, "CM2DAT_1X[8]")
),
- MTK_PIN(
- PINCTRL_PIN(26, "NLD9"),
+ MTK_PIN(PINCTRL_PIN(26, "NLD9"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 155),
MTK_FUNCTION(0, "GPIO26"),
@@ -329,8 +302,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "CM2DAT_1X[9]"),
MTK_FUNCTION(5, "PWM1")
),
- MTK_PIN(
- PINCTRL_PIN(27, "NLD10"),
+ MTK_PIN(PINCTRL_PIN(27, "NLD10"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 156),
MTK_FUNCTION(0, "GPIO27"),
@@ -339,8 +311,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "CM2VSYNC_1X"),
MTK_FUNCTION(5, "PWM2")
),
- MTK_PIN(
- PINCTRL_PIN(28, "NLD11"),
+ MTK_PIN(PINCTRL_PIN(28, "NLD11"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 157),
MTK_FUNCTION(0, "GPIO28"),
@@ -349,8 +320,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "CM2HSYNC_1X"),
MTK_FUNCTION(5, "PWM3")
),
- MTK_PIN(
- PINCTRL_PIN(29, "NLD12"),
+ MTK_PIN(PINCTRL_PIN(29, "NLD12"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 158),
MTK_FUNCTION(0, "GPIO29"),
@@ -360,8 +330,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "DAC_CK"),
MTK_FUNCTION(5, "PCM1_CK")
),
- MTK_PIN(
- PINCTRL_PIN(30, "NLD13"),
+ MTK_PIN(PINCTRL_PIN(30, "NLD13"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 159),
MTK_FUNCTION(0, "GPIO30"),
@@ -371,8 +340,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "DAC_WS"),
MTK_FUNCTION(5, "PCM1_WS")
),
- MTK_PIN(
- PINCTRL_PIN(31, "NLD14"),
+ MTK_PIN(PINCTRL_PIN(31, "NLD14"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 160),
MTK_FUNCTION(0, "GPIO31"),
@@ -382,8 +350,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "DAC_DAT_OUT"),
MTK_FUNCTION(5, "PCM1_DO")
),
- MTK_PIN(
- PINCTRL_PIN(32, "NLD15"),
+ MTK_PIN(PINCTRL_PIN(32, "NLD15"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 161),
MTK_FUNCTION(0, "GPIO32"),
@@ -393,8 +360,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "PWM4"),
MTK_FUNCTION(5, "PCM1_DI")
),
- MTK_PIN(
- PINCTRL_PIN(33, "MSDC0_RSTB"),
+ MTK_PIN(PINCTRL_PIN(33, "MSDC0_RSTB"),
"G22", "mt8135",
MTK_EINT_FUNCTION(2, 50),
MTK_FUNCTION(0, "GPIO33"),
@@ -405,16 +371,14 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "SPI1_MI"),
MTK_FUNCTION(7, "NLD10")
),
- MTK_PIN(
- PINCTRL_PIN(34, "IDDIG"),
+ MTK_PIN(PINCTRL_PIN(34, "IDDIG"),
"N17", "mt8135",
MTK_EINT_FUNCTION(2, 34),
MTK_FUNCTION(0, "GPIO34"),
MTK_FUNCTION(1, "IDDIG"),
MTK_FUNCTION(2, "EINT34")
),
- MTK_PIN(
- PINCTRL_PIN(35, "SCL3"),
+ MTK_PIN(PINCTRL_PIN(35, "SCL3"),
"L19", "mt8135",
MTK_EINT_FUNCTION(2, 96),
MTK_FUNCTION(0, "GPIO35"),
@@ -423,16 +387,14 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(3, "CLKM6"),
MTK_FUNCTION(4, "PWM6")
),
- MTK_PIN(
- PINCTRL_PIN(36, "SDA3"),
+ MTK_PIN(PINCTRL_PIN(36, "SDA3"),
"L20", "mt8135",
MTK_EINT_FUNCTION(2, 97),
MTK_FUNCTION(0, "GPIO36"),
MTK_FUNCTION(1, "SDA3"),
MTK_FUNCTION(2, "EINT97")
),
- MTK_PIN(
- PINCTRL_PIN(37, "AUD_CLK_MOSI"),
+ MTK_PIN(PINCTRL_PIN(37, "AUD_CLK_MOSI"),
"L21", "mt8135",
MTK_EINT_FUNCTION(4, 19),
MTK_FUNCTION(0, "GPIO37"),
@@ -443,8 +405,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "USB_TEST_IO[6]"),
MTK_FUNCTION(7, "TESTA_OUT19")
),
- MTK_PIN(
- PINCTRL_PIN(38, "AUD_DAT_MOSI"),
+ MTK_PIN(PINCTRL_PIN(38, "AUD_DAT_MOSI"),
"L23", "mt8135",
MTK_EINT_FUNCTION(4, 21),
MTK_FUNCTION(0, "GPIO38"),
@@ -455,8 +416,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "USB_TEST_IO[7]"),
MTK_FUNCTION(7, "TESTA_OUT20")
),
- MTK_PIN(
- PINCTRL_PIN(39, "AUD_DAT_MISO"),
+ MTK_PIN(PINCTRL_PIN(39, "AUD_DAT_MISO"),
"L22", "mt8135",
MTK_EINT_FUNCTION(4, 20),
MTK_FUNCTION(0, "GPIO39"),
@@ -467,8 +427,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "USB_TEST_IO[8]"),
MTK_FUNCTION(7, "TESTA_OUT21")
),
- MTK_PIN(
- PINCTRL_PIN(40, "DAC_CLK"),
+ MTK_PIN(PINCTRL_PIN(40, "DAC_CLK"),
"P21", "mt8135",
MTK_EINT_FUNCTION(2, 22),
MTK_FUNCTION(0, "GPIO40"),
@@ -478,8 +437,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "USB_TEST_IO[9]"),
MTK_FUNCTION(7, "TESTA_OUT22")
),
- MTK_PIN(
- PINCTRL_PIN(41, "DAC_WS"),
+ MTK_PIN(PINCTRL_PIN(41, "DAC_WS"),
"N18", "mt8135",
MTK_EINT_FUNCTION(2, 24),
MTK_FUNCTION(0, "GPIO41"),
@@ -489,8 +447,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "USB_TEST_IO[10]"),
MTK_FUNCTION(7, "TESTA_OUT23")
),
- MTK_PIN(
- PINCTRL_PIN(42, "DAC_DAT_OUT"),
+ MTK_PIN(PINCTRL_PIN(42, "DAC_DAT_OUT"),
"N22", "mt8135",
MTK_EINT_FUNCTION(2, 23),
MTK_FUNCTION(0, "GPIO42"),
@@ -500,40 +457,35 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "USB_TEST_IO[11]"),
MTK_FUNCTION(7, "TESTA_OUT24")
),
- MTK_PIN(
- PINCTRL_PIN(43, "PWRAP_SPI0_MO"),
+ MTK_PIN(PINCTRL_PIN(43, "PWRAP_SPI0_MO"),
"M22", "mt8135",
MTK_EINT_FUNCTION(2, 29),
MTK_FUNCTION(0, "GPIO43"),
MTK_FUNCTION(1, "PWRAP_SPIDI"),
MTK_FUNCTION(2, "EINT29")
),
- MTK_PIN(
- PINCTRL_PIN(44, "PWRAP_SPI0_MI"),
+ MTK_PIN(PINCTRL_PIN(44, "PWRAP_SPI0_MI"),
"P23", "mt8135",
MTK_EINT_FUNCTION(2, 28),
MTK_FUNCTION(0, "GPIO44"),
MTK_FUNCTION(1, "PWRAP_SPIDO"),
MTK_FUNCTION(2, "EINT28")
),
- MTK_PIN(
- PINCTRL_PIN(45, "PWRAP_SPI0_CSN"),
+ MTK_PIN(PINCTRL_PIN(45, "PWRAP_SPI0_CSN"),
"M21", "mt8135",
MTK_EINT_FUNCTION(2, 27),
MTK_FUNCTION(0, "GPIO45"),
MTK_FUNCTION(1, "PWRAP_SPICS_B_I"),
MTK_FUNCTION(2, "EINT27")
),
- MTK_PIN(
- PINCTRL_PIN(46, "PWRAP_SPI0_CLK"),
+ MTK_PIN(PINCTRL_PIN(46, "PWRAP_SPI0_CLK"),
"P22", "mt8135",
MTK_EINT_FUNCTION(2, 26),
MTK_FUNCTION(0, "GPIO46"),
MTK_FUNCTION(1, "PWRAP_SPICK_I"),
MTK_FUNCTION(2, "EINT26")
),
- MTK_PIN(
- PINCTRL_PIN(47, "PWRAP_EVENT"),
+ MTK_PIN(PINCTRL_PIN(47, "PWRAP_EVENT"),
"M23", "mt8135",
MTK_EINT_FUNCTION(2, 25),
MTK_FUNCTION(0, "GPIO47"),
@@ -541,39 +493,34 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(2, "EINT25"),
MTK_FUNCTION(7, "TESTA_OUT2")
),
- MTK_PIN(
- PINCTRL_PIN(48, "RTC32K_CK"),
+ MTK_PIN(PINCTRL_PIN(48, "RTC32K_CK"),
"N20", "mt8135",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO48"),
MTK_FUNCTION(1, "RTC32K_CK")
),
- MTK_PIN(
- PINCTRL_PIN(49, "WATCHDOG"),
+ MTK_PIN(PINCTRL_PIN(49, "WATCHDOG"),
"R22", "mt8135",
MTK_EINT_FUNCTION(2, 36),
MTK_FUNCTION(0, "GPIO49"),
MTK_FUNCTION(1, "WATCHDOG"),
MTK_FUNCTION(2, "EINT36")
),
- MTK_PIN(
- PINCTRL_PIN(50, "SRCLKENA"),
+ MTK_PIN(PINCTRL_PIN(50, "SRCLKENA"),
"T22", "mt8135",
MTK_EINT_FUNCTION(2, 38),
MTK_FUNCTION(0, "GPIO50"),
MTK_FUNCTION(1, "SRCLKENA"),
MTK_FUNCTION(2, "EINT38")
),
- MTK_PIN(
- PINCTRL_PIN(51, "SRCVOLTEN"),
+ MTK_PIN(PINCTRL_PIN(51, "SRCVOLTEN"),
"T23", "mt8135",
MTK_EINT_FUNCTION(2, 37),
MTK_FUNCTION(0, "GPIO51"),
MTK_FUNCTION(1, "SRCVOLTEN"),
MTK_FUNCTION(2, "EINT37")
),
- MTK_PIN(
- PINCTRL_PIN(52, "EINT0"),
+ MTK_PIN(PINCTRL_PIN(52, "EINT0"),
"T21", "mt8135",
MTK_EINT_FUNCTION(1, 0),
MTK_FUNCTION(0, "GPIO52"),
@@ -584,8 +531,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "USB_TEST_IO[12]"),
MTK_FUNCTION(7, "USB_SCL")
),
- MTK_PIN(
- PINCTRL_PIN(53, "URXD2"),
+ MTK_PIN(PINCTRL_PIN(53, "URXD2"),
"R18", "mt8135",
MTK_EINT_FUNCTION(2, 83),
MTK_FUNCTION(0, "GPIO53"),
@@ -595,8 +541,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "CLKM3"),
MTK_FUNCTION(7, "UTXD2")
),
- MTK_PIN(
- PINCTRL_PIN(54, "UTXD2"),
+ MTK_PIN(PINCTRL_PIN(54, "UTXD2"),
"R17", "mt8135",
MTK_EINT_FUNCTION(2, 82),
MTK_FUNCTION(0, "GPIO54"),
@@ -606,8 +551,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "CLKM2"),
MTK_FUNCTION(7, "URXD2")
),
- MTK_PIN(
- PINCTRL_PIN(55, "UCTS2"),
+ MTK_PIN(PINCTRL_PIN(55, "UCTS2"),
"R20", "mt8135",
MTK_EINT_FUNCTION(2, 84),
MTK_FUNCTION(0, "GPIO55"),
@@ -616,8 +560,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "PWM1"),
MTK_FUNCTION(7, "URTS2")
),
- MTK_PIN(
- PINCTRL_PIN(56, "URTS2"),
+ MTK_PIN(PINCTRL_PIN(56, "URTS2"),
"R19", "mt8135",
MTK_EINT_FUNCTION(2, 85),
MTK_FUNCTION(0, "GPIO56"),
@@ -626,8 +569,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "PWM2"),
MTK_FUNCTION(7, "UCTS2")
),
- MTK_PIN(
- PINCTRL_PIN(57, "JTCK"),
+ MTK_PIN(PINCTRL_PIN(57, "JTCK"),
"V17", "mt8135",
MTK_EINT_FUNCTION(2, 188),
MTK_FUNCTION(0, "GPIO57"),
@@ -635,8 +577,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(2, "EINT188"),
MTK_FUNCTION(3, "DSP1_ICK")
),
- MTK_PIN(
- PINCTRL_PIN(58, "JTDO"),
+ MTK_PIN(PINCTRL_PIN(58, "JTDO"),
"T16", "mt8135",
MTK_EINT_FUNCTION(2, 190),
MTK_FUNCTION(0, "GPIO58"),
@@ -644,8 +585,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(2, "EINT190"),
MTK_FUNCTION(3, "DSP2_IMS")
),
- MTK_PIN(
- PINCTRL_PIN(59, "JTRST_B"),
+ MTK_PIN(PINCTRL_PIN(59, "JTRST_B"),
"T19", "mt8135",
MTK_EINT_FUNCTION(2, 0),
MTK_FUNCTION(0, "GPIO59"),
@@ -653,8 +593,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(2, "EINT0"),
MTK_FUNCTION(3, "DSP2_ICK")
),
- MTK_PIN(
- PINCTRL_PIN(60, "JTDI"),
+ MTK_PIN(PINCTRL_PIN(60, "JTDI"),
"T18", "mt8135",
MTK_EINT_FUNCTION(2, 189),
MTK_FUNCTION(0, "GPIO60"),
@@ -662,8 +601,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(2, "EINT189"),
MTK_FUNCTION(3, "DSP1_IMS")
),
- MTK_PIN(
- PINCTRL_PIN(61, "JRTCK"),
+ MTK_PIN(PINCTRL_PIN(61, "JRTCK"),
"T20", "mt8135",
MTK_EINT_FUNCTION(2, 187),
MTK_FUNCTION(0, "GPIO61"),
@@ -671,8 +609,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(2, "EINT187"),
MTK_FUNCTION(3, "DSP1_ID")
),
- MTK_PIN(
- PINCTRL_PIN(62, "JTMS"),
+ MTK_PIN(PINCTRL_PIN(62, "JTMS"),
"T17", "mt8135",
MTK_EINT_FUNCTION(2, 191),
MTK_FUNCTION(0, "GPIO62"),
@@ -680,8 +617,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(2, "EINT191"),
MTK_FUNCTION(3, "DSP2_ID")
),
- MTK_PIN(
- PINCTRL_PIN(63, "MSDC1_INSI"),
+ MTK_PIN(PINCTRL_PIN(63, "MSDC1_INSI"),
"V18", "mt8135",
MTK_EINT_FUNCTION(1, 15),
MTK_FUNCTION(0, "GPIO63"),
@@ -691,8 +627,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "CLKM5"),
MTK_FUNCTION(7, "TESTB_OUT6")
),
- MTK_PIN(
- PINCTRL_PIN(64, "MSDC1_SDWPI"),
+ MTK_PIN(PINCTRL_PIN(64, "MSDC1_SDWPI"),
"W18", "mt8135",
MTK_EINT_FUNCTION(2, 58),
MTK_FUNCTION(0, "GPIO64"),
@@ -703,8 +638,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "CLKM6"),
MTK_FUNCTION(7, "TESTB_OUT7")
),
- MTK_PIN(
- PINCTRL_PIN(65, "MSDC2_INSI"),
+ MTK_PIN(PINCTRL_PIN(65, "MSDC2_INSI"),
"U22", "mt8135",
MTK_EINT_FUNCTION(1, 14),
MTK_FUNCTION(0, "GPIO65"),
@@ -712,8 +646,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "USB_TEST_IO[27]"),
MTK_FUNCTION(7, "TESTA_OUT3")
),
- MTK_PIN(
- PINCTRL_PIN(66, "MSDC2_SDWPI"),
+ MTK_PIN(PINCTRL_PIN(66, "MSDC2_SDWPI"),
"U21", "mt8135",
MTK_EINT_FUNCTION(2, 66),
MTK_FUNCTION(0, "GPIO66"),
@@ -721,8 +654,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(2, "EINT66"),
MTK_FUNCTION(5, "USB_TEST_IO[28]")
),
- MTK_PIN(
- PINCTRL_PIN(67, "URXD4"),
+ MTK_PIN(PINCTRL_PIN(67, "URXD4"),
"V23", "mt8135",
MTK_EINT_FUNCTION(2, 89),
MTK_FUNCTION(0, "GPIO67"),
@@ -732,8 +664,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "UTXD4"),
MTK_FUNCTION(7, "TESTB_OUT10")
),
- MTK_PIN(
- PINCTRL_PIN(68, "UTXD4"),
+ MTK_PIN(PINCTRL_PIN(68, "UTXD4"),
"V22", "mt8135",
MTK_EINT_FUNCTION(2, 88),
MTK_FUNCTION(0, "GPIO68"),
@@ -743,8 +674,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "URXD4"),
MTK_FUNCTION(7, "TESTB_OUT11")
),
- MTK_PIN(
- PINCTRL_PIN(69, "URXD1"),
+ MTK_PIN(PINCTRL_PIN(69, "URXD1"),
"W22", "mt8135",
MTK_EINT_FUNCTION(2, 79),
MTK_FUNCTION(0, "GPIO69"),
@@ -754,8 +684,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "UTXD1"),
MTK_FUNCTION(7, "TESTB_OUT24")
),
- MTK_PIN(
- PINCTRL_PIN(70, "UTXD1"),
+ MTK_PIN(PINCTRL_PIN(70, "UTXD1"),
"V21", "mt8135",
MTK_EINT_FUNCTION(2, 78),
MTK_FUNCTION(0, "GPIO70"),
@@ -765,8 +694,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "URXD1"),
MTK_FUNCTION(7, "TESTB_OUT25")
),
- MTK_PIN(
- PINCTRL_PIN(71, "UCTS1"),
+ MTK_PIN(PINCTRL_PIN(71, "UCTS1"),
"V19", "mt8135",
MTK_EINT_FUNCTION(2, 80),
MTK_FUNCTION(0, "GPIO71"),
@@ -776,8 +704,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "URTS1"),
MTK_FUNCTION(7, "TESTB_OUT31")
),
- MTK_PIN(
- PINCTRL_PIN(72, "URTS1"),
+ MTK_PIN(PINCTRL_PIN(72, "URTS1"),
"V20", "mt8135",
MTK_EINT_FUNCTION(2, 81),
MTK_FUNCTION(0, "GPIO72"),
@@ -787,8 +714,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "UCTS1"),
MTK_FUNCTION(7, "TESTB_OUT21")
),
- MTK_PIN(
- PINCTRL_PIN(73, "PWM1"),
+ MTK_PIN(PINCTRL_PIN(73, "PWM1"),
"W17", "mt8135",
MTK_EINT_FUNCTION(2, 73),
MTK_FUNCTION(0, "GPIO73"),
@@ -798,8 +724,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "DISP_PWM"),
MTK_FUNCTION(7, "TESTB_OUT8")
),
- MTK_PIN(
- PINCTRL_PIN(74, "PWM2"),
+ MTK_PIN(PINCTRL_PIN(74, "PWM2"),
"Y17", "mt8135",
MTK_EINT_FUNCTION(2, 74),
MTK_FUNCTION(0, "GPIO74"),
@@ -811,8 +736,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "DISP_PWM"),
MTK_FUNCTION(7, "TESTB_OUT9")
),
- MTK_PIN(
- PINCTRL_PIN(75, "PWM3"),
+ MTK_PIN(PINCTRL_PIN(75, "PWM3"),
"Y19", "mt8135",
MTK_EINT_FUNCTION(2, 75),
MTK_FUNCTION(0, "GPIO75"),
@@ -824,8 +748,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "DISP_PWM"),
MTK_FUNCTION(7, "TESTB_OUT12")
),
- MTK_PIN(
- PINCTRL_PIN(76, "PWM4"),
+ MTK_PIN(PINCTRL_PIN(76, "PWM4"),
"W19", "mt8135",
MTK_EINT_FUNCTION(2, 76),
MTK_FUNCTION(0, "GPIO76"),
@@ -836,8 +759,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "DISP_PWM"),
MTK_FUNCTION(7, "TESTB_OUT13")
),
- MTK_PIN(
- PINCTRL_PIN(77, "MSDC2_DAT2"),
+ MTK_PIN(PINCTRL_PIN(77, "MSDC2_DAT2"),
"W21", "mt8135",
MTK_EINT_FUNCTION(2, 63),
MTK_FUNCTION(0, "GPIO77"),
@@ -847,8 +769,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "DPI33_D6"),
MTK_FUNCTION(7, "TESTA_OUT25")
),
- MTK_PIN(
- PINCTRL_PIN(78, "MSDC2_DAT3"),
+ MTK_PIN(PINCTRL_PIN(78, "MSDC2_DAT3"),
"AA23", "mt8135",
MTK_EINT_FUNCTION(2, 64),
MTK_FUNCTION(0, "GPIO78"),
@@ -858,8 +779,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "DPI33_D7"),
MTK_FUNCTION(7, "TESTA_OUT26")
),
- MTK_PIN(
- PINCTRL_PIN(79, "MSDC2_CMD"),
+ MTK_PIN(PINCTRL_PIN(79, "MSDC2_CMD"),
"Y22", "mt8135",
MTK_EINT_FUNCTION(2, 60),
MTK_FUNCTION(0, "GPIO79"),
@@ -870,8 +790,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "DPI33_D3"),
MTK_FUNCTION(7, "TESTA_OUT0")
),
- MTK_PIN(
- PINCTRL_PIN(80, "MSDC2_CLK"),
+ MTK_PIN(PINCTRL_PIN(80, "MSDC2_CLK"),
"AA22", "mt8135",
MTK_EINT_FUNCTION(2, 59),
MTK_FUNCTION(0, "GPIO80"),
@@ -882,8 +801,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "DPI33_D2"),
MTK_FUNCTION(7, "TESTA_OUT1")
),
- MTK_PIN(
- PINCTRL_PIN(81, "MSDC2_DAT1"),
+ MTK_PIN(PINCTRL_PIN(81, "MSDC2_DAT1"),
"Y21", "mt8135",
MTK_EINT_FUNCTION(2, 62),
MTK_FUNCTION(0, "GPIO81"),
@@ -893,8 +811,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "PCM1_DO"),
MTK_FUNCTION(6, "DPI33_D5")
),
- MTK_PIN(
- PINCTRL_PIN(82, "MSDC2_DAT0"),
+ MTK_PIN(PINCTRL_PIN(82, "MSDC2_DAT0"),
"AB22", "mt8135",
MTK_EINT_FUNCTION(2, 61),
MTK_FUNCTION(0, "GPIO82"),
@@ -904,8 +821,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "PCM1_DI"),
MTK_FUNCTION(6, "DPI33_D4")
),
- MTK_PIN(
- PINCTRL_PIN(83, "MSDC1_DAT0"),
+ MTK_PIN(PINCTRL_PIN(83, "MSDC1_DAT0"),
"AC19", "mt8135",
MTK_EINT_FUNCTION(2, 53),
MTK_FUNCTION(0, "GPIO83"),
@@ -916,8 +832,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "CLKM1"),
MTK_FUNCTION(7, "TESTB_OUT2")
),
- MTK_PIN(
- PINCTRL_PIN(84, "MSDC1_DAT1"),
+ MTK_PIN(PINCTRL_PIN(84, "MSDC1_DAT1"),
"AA19", "mt8135",
MTK_EINT_FUNCTION(2, 54),
MTK_FUNCTION(0, "GPIO84"),
@@ -928,8 +843,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "CLKM2"),
MTK_FUNCTION(7, "TESTB_OUT3")
),
- MTK_PIN(
- PINCTRL_PIN(85, "MSDC1_CMD"),
+ MTK_PIN(PINCTRL_PIN(85, "MSDC1_CMD"),
"AA20", "mt8135",
MTK_EINT_FUNCTION(2, 52),
MTK_FUNCTION(0, "GPIO85"),
@@ -940,8 +854,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "CLKM0"),
MTK_FUNCTION(7, "TESTB_OUT1")
),
- MTK_PIN(
- PINCTRL_PIN(86, "MSDC1_CLK"),
+ MTK_PIN(PINCTRL_PIN(86, "MSDC1_CLK"),
"AB19", "mt8135",
MTK_EINT_FUNCTION(2, 51),
MTK_FUNCTION(0, "GPIO86"),
@@ -951,8 +864,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "DISP_PWM"),
MTK_FUNCTION(7, "TESTB_OUT0")
),
- MTK_PIN(
- PINCTRL_PIN(87, "MSDC1_DAT2"),
+ MTK_PIN(PINCTRL_PIN(87, "MSDC1_DAT2"),
"AA21", "mt8135",
MTK_EINT_FUNCTION(2, 55),
MTK_FUNCTION(0, "GPIO87"),
@@ -963,8 +875,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "CLKM3"),
MTK_FUNCTION(7, "TESTB_OUT4")
),
- MTK_PIN(
- PINCTRL_PIN(88, "MSDC1_DAT3"),
+ MTK_PIN(PINCTRL_PIN(88, "MSDC1_DAT3"),
"AB20", "mt8135",
MTK_EINT_FUNCTION(2, 56),
MTK_FUNCTION(0, "GPIO88"),
@@ -975,8 +886,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "CLKM4"),
MTK_FUNCTION(7, "TESTB_OUT5")
),
- MTK_PIN(
- PINCTRL_PIN(89, "MSDC4_DAT0"),
+ MTK_PIN(PINCTRL_PIN(89, "MSDC4_DAT0"),
"AB8", "mt8135",
MTK_EINT_FUNCTION(2, 133),
MTK_FUNCTION(0, "GPIO89"),
@@ -987,8 +897,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "A_FUNC_DIN[9]"),
MTK_FUNCTION(7, "LPTE")
),
- MTK_PIN(
- PINCTRL_PIN(90, "MSDC4_DAT1"),
+ MTK_PIN(PINCTRL_PIN(90, "MSDC4_DAT1"),
"AB7", "mt8135",
MTK_EINT_FUNCTION(2, 134),
MTK_FUNCTION(0, "GPIO90"),
@@ -997,8 +906,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "A_FUNC_DIN[10]"),
MTK_FUNCTION(7, "LRSTB_1X")
),
- MTK_PIN(
- PINCTRL_PIN(91, "MSDC4_DAT5"),
+ MTK_PIN(PINCTRL_PIN(91, "MSDC4_DAT5"),
"AA8", "mt8135",
MTK_EINT_FUNCTION(2, 136),
MTK_FUNCTION(0, "GPIO91"),
@@ -1010,8 +918,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "A_FUNC_DIN[11]"),
MTK_FUNCTION(7, "SPI1_CSN")
),
- MTK_PIN(
- PINCTRL_PIN(92, "MSDC4_DAT6"),
+ MTK_PIN(PINCTRL_PIN(92, "MSDC4_DAT6"),
"AC4", "mt8135",
MTK_EINT_FUNCTION(2, 137),
MTK_FUNCTION(0, "GPIO92"),
@@ -1023,8 +930,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "A_FUNC_DIN[12]"),
MTK_FUNCTION(7, "SPI1_MO")
),
- MTK_PIN(
- PINCTRL_PIN(93, "MSDC4_DAT7"),
+ MTK_PIN(PINCTRL_PIN(93, "MSDC4_DAT7"),
"AC6", "mt8135",
MTK_EINT_FUNCTION(2, 138),
MTK_FUNCTION(0, "GPIO93"),
@@ -1035,8 +941,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "A_FUNC_DIN[13]"),
MTK_FUNCTION(7, "SPI1_MI")
),
- MTK_PIN(
- PINCTRL_PIN(94, "MSDC4_DAT4"),
+ MTK_PIN(PINCTRL_PIN(94, "MSDC4_DAT4"),
"AA7", "mt8135",
MTK_EINT_FUNCTION(2, 135),
MTK_FUNCTION(0, "GPIO94"),
@@ -1048,8 +953,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "A_FUNC_DIN[14]"),
MTK_FUNCTION(7, "SPI1_CLK")
),
- MTK_PIN(
- PINCTRL_PIN(95, "MSDC4_DAT2"),
+ MTK_PIN(PINCTRL_PIN(95, "MSDC4_DAT2"),
"AB6", "mt8135",
MTK_EINT_FUNCTION(2, 131),
MTK_FUNCTION(0, "GPIO95"),
@@ -1061,8 +965,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "PCM1_WS"),
MTK_FUNCTION(7, "LSCE0B_1X")
),
- MTK_PIN(
- PINCTRL_PIN(96, "MSDC4_CLK"),
+ MTK_PIN(PINCTRL_PIN(96, "MSDC4_CLK"),
"AB5", "mt8135",
MTK_EINT_FUNCTION(2, 129),
MTK_FUNCTION(0, "GPIO96"),
@@ -1074,8 +977,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "PCM1_DI"),
MTK_FUNCTION(7, "LSCK_1X")
),
- MTK_PIN(
- PINCTRL_PIN(97, "MSDC4_DAT3"),
+ MTK_PIN(PINCTRL_PIN(97, "MSDC4_DAT3"),
"Y8", "mt8135",
MTK_EINT_FUNCTION(2, 132),
MTK_FUNCTION(0, "GPIO97"),
@@ -1087,8 +989,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "PCM1_DO"),
MTK_FUNCTION(7, "LSCE1B_1X")
),
- MTK_PIN(
- PINCTRL_PIN(98, "MSDC4_CMD"),
+ MTK_PIN(PINCTRL_PIN(98, "MSDC4_CMD"),
"AC3", "mt8135",
MTK_EINT_FUNCTION(2, 128),
MTK_FUNCTION(0, "GPIO98"),
@@ -1098,8 +999,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "PWM3"),
MTK_FUNCTION(7, "LSDA_1X")
),
- MTK_PIN(
- PINCTRL_PIN(99, "MSDC4_RSTB"),
+ MTK_PIN(PINCTRL_PIN(99, "MSDC4_RSTB"),
"AB4", "mt8135",
MTK_EINT_FUNCTION(2, 130),
MTK_FUNCTION(0, "GPIO99"),
@@ -1111,8 +1011,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "PCM1_CK"),
MTK_FUNCTION(7, "LSA0_1X")
),
- MTK_PIN(
- PINCTRL_PIN(100, "SDA0"),
+ MTK_PIN(PINCTRL_PIN(100, "SDA0"),
"W9", "mt8135",
MTK_EINT_FUNCTION(2, 91),
MTK_FUNCTION(0, "GPIO100"),
@@ -1122,8 +1021,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "PWM1"),
MTK_FUNCTION(7, "A_FUNC_DIN[15]")
),
- MTK_PIN(
- PINCTRL_PIN(101, "SCL0"),
+ MTK_PIN(PINCTRL_PIN(101, "SCL0"),
"W11", "mt8135",
MTK_EINT_FUNCTION(2, 90),
MTK_FUNCTION(0, "GPIO101"),
@@ -1133,8 +1031,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "DISP_PWM"),
MTK_FUNCTION(7, "A_FUNC_DIN[16]")
),
- MTK_PIN(
- PINCTRL_PIN(102, "EINT10_AUXIN2"),
+ MTK_PIN(PINCTRL_PIN(102, "EINT10_AUXIN2"),
"AA3", "mt8135",
MTK_EINT_FUNCTION(1, 10),
MTK_FUNCTION(0, "GPIO102"),
@@ -1143,8 +1040,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "TESTB_OUT16"),
MTK_FUNCTION(7, "A_FUNC_DIN[17]")
),
- MTK_PIN(
- PINCTRL_PIN(103, "EINT11_AUXIN3"),
+ MTK_PIN(PINCTRL_PIN(103, "EINT11_AUXIN3"),
"AB2", "mt8135",
MTK_EINT_FUNCTION(1, 11),
MTK_FUNCTION(0, "GPIO103"),
@@ -1153,8 +1049,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "TESTB_OUT17"),
MTK_FUNCTION(7, "A_FUNC_DIN[18]")
),
- MTK_PIN(
- PINCTRL_PIN(104, "EINT16_AUXIN4"),
+ MTK_PIN(PINCTRL_PIN(104, "EINT16_AUXIN4"),
"AB3", "mt8135",
MTK_EINT_FUNCTION(1, 16),
MTK_FUNCTION(0, "GPIO104"),
@@ -1163,8 +1058,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "TESTB_OUT18"),
MTK_FUNCTION(7, "A_FUNC_DIN[19]")
),
- MTK_PIN(
- PINCTRL_PIN(105, "I2S_CLK"),
+ MTK_PIN(PINCTRL_PIN(105, "I2S_CLK"),
"W6", "mt8135",
MTK_EINT_FUNCTION(2, 10),
MTK_FUNCTION(0, "GPIO105"),
@@ -1176,8 +1070,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "TESTB_OUT19"),
MTK_FUNCTION(7, "A_FUNC_DIN[20]")
),
- MTK_PIN(
- PINCTRL_PIN(106, "I2S_WS"),
+ MTK_PIN(PINCTRL_PIN(106, "I2S_WS"),
"AA6", "mt8135",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO106"),
@@ -1188,8 +1081,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "TESTB_OUT20"),
MTK_FUNCTION(7, "A_FUNC_DIN[21]")
),
- MTK_PIN(
- PINCTRL_PIN(107, "I2S_DATA_IN"),
+ MTK_PIN(PINCTRL_PIN(107, "I2S_DATA_IN"),
"AA5", "mt8135",
MTK_EINT_FUNCTION(2, 11),
MTK_FUNCTION(0, "GPIO107"),
@@ -1200,8 +1092,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "TESTB_OUT22"),
MTK_FUNCTION(7, "A_FUNC_DIN[22]")
),
- MTK_PIN(
- PINCTRL_PIN(108, "I2S_DATA_OUT"),
+ MTK_PIN(PINCTRL_PIN(108, "I2S_DATA_OUT"),
"AA4", "mt8135",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO108"),
@@ -1212,8 +1103,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "TESTB_OUT23"),
MTK_FUNCTION(7, "A_FUNC_DIN[23]")
),
- MTK_PIN(
- PINCTRL_PIN(109, "EINT5"),
+ MTK_PIN(PINCTRL_PIN(109, "EINT5"),
"W5", "mt8135",
MTK_EINT_FUNCTION(1, 5),
MTK_FUNCTION(0, "GPIO109"),
@@ -1225,8 +1115,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "TESTB_OUT26"),
MTK_FUNCTION(7, "A_FUNC_DIN[24]")
),
- MTK_PIN(
- PINCTRL_PIN(110, "EINT6"),
+ MTK_PIN(PINCTRL_PIN(110, "EINT6"),
"V5", "mt8135",
MTK_EINT_FUNCTION(1, 6),
MTK_FUNCTION(0, "GPIO110"),
@@ -1238,8 +1127,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "TESTB_OUT27"),
MTK_FUNCTION(7, "A_FUNC_DIN[25]")
),
- MTK_PIN(
- PINCTRL_PIN(111, "EINT7"),
+ MTK_PIN(PINCTRL_PIN(111, "EINT7"),
"W3", "mt8135",
MTK_EINT_FUNCTION(1, 7),
MTK_FUNCTION(0, "GPIO111"),
@@ -1251,8 +1139,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "TESTB_OUT28"),
MTK_FUNCTION(7, "A_FUNC_DIN[26]")
),
- MTK_PIN(
- PINCTRL_PIN(112, "EINT8"),
+ MTK_PIN(PINCTRL_PIN(112, "EINT8"),
"V6", "mt8135",
MTK_EINT_FUNCTION(1, 8),
MTK_FUNCTION(0, "GPIO112"),
@@ -1264,8 +1151,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "TESTB_OUT29"),
MTK_FUNCTION(7, "EXT_FRAME_SYNC")
),
- MTK_PIN(
- PINCTRL_PIN(113, "EINT9"),
+ MTK_PIN(PINCTRL_PIN(113, "EINT9"),
"W8", "mt8135",
MTK_EINT_FUNCTION(1, 9),
MTK_FUNCTION(0, "GPIO113"),
@@ -1275,8 +1161,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "TESTB_OUT30"),
MTK_FUNCTION(7, "A_FUNC_DIN[27]")
),
- MTK_PIN(
- PINCTRL_PIN(114, "LPCE1B"),
+ MTK_PIN(PINCTRL_PIN(114, "LPCE1B"),
"W4", "mt8135",
MTK_EINT_FUNCTION(2, 127),
MTK_FUNCTION(0, "GPIO114"),
@@ -1286,8 +1171,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "TESTB_OUT14"),
MTK_FUNCTION(7, "A_FUNC_DIN[28]")
),
- MTK_PIN(
- PINCTRL_PIN(115, "LPCE0B"),
+ MTK_PIN(PINCTRL_PIN(115, "LPCE0B"),
"T5", "mt8135",
MTK_EINT_FUNCTION(2, 126),
MTK_FUNCTION(0, "GPIO115"),
@@ -1297,8 +1181,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "TESTB_OUT15"),
MTK_FUNCTION(7, "A_FUNC_DIN[29]")
),
- MTK_PIN(
- PINCTRL_PIN(116, "DISP_PWM"),
+ MTK_PIN(PINCTRL_PIN(116, "DISP_PWM"),
"V4", "mt8135",
MTK_EINT_FUNCTION(2, 77),
MTK_FUNCTION(0, "GPIO116"),
@@ -1309,8 +1192,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "PWM2"),
MTK_FUNCTION(7, "PWM3")
),
- MTK_PIN(
- PINCTRL_PIN(117, "EINT1"),
+ MTK_PIN(PINCTRL_PIN(117, "EINT1"),
"T6", "mt8135",
MTK_EINT_FUNCTION(1, 1),
MTK_FUNCTION(0, "GPIO117"),
@@ -1320,8 +1202,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "USB_TEST_IO[13]"),
MTK_FUNCTION(7, "USB_SDA")
),
- MTK_PIN(
- PINCTRL_PIN(118, "EINT2"),
+ MTK_PIN(PINCTRL_PIN(118, "EINT2"),
"T4", "mt8135",
MTK_EINT_FUNCTION(1, 2),
MTK_FUNCTION(0, "GPIO118"),
@@ -1332,8 +1213,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "SRCLKENAI2"),
MTK_FUNCTION(7, "A_FUNC_DIN[30]")
),
- MTK_PIN(
- PINCTRL_PIN(119, "EINT3"),
+ MTK_PIN(PINCTRL_PIN(119, "EINT3"),
"R4", "mt8135",
MTK_EINT_FUNCTION(1, 3),
MTK_FUNCTION(0, "GPIO119"),
@@ -1342,8 +1222,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "SRCLKENAI1"),
MTK_FUNCTION(7, "EXT_26M_CK")
),
- MTK_PIN(
- PINCTRL_PIN(120, "EINT4"),
+ MTK_PIN(PINCTRL_PIN(120, "EINT4"),
"R5", "mt8135",
MTK_EINT_FUNCTION(1, 4),
MTK_FUNCTION(0, "GPIO120"),
@@ -1352,8 +1231,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "USB_DRVVBUS"),
MTK_FUNCTION(7, "A_FUNC_DIN[31]")
),
- MTK_PIN(
- PINCTRL_PIN(121, "DPIDE"),
+ MTK_PIN(PINCTRL_PIN(121, "DPIDE"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 100),
MTK_FUNCTION(0, "GPIO121"),
@@ -1364,8 +1242,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "PCM1_DO"),
MTK_FUNCTION(6, "IRDA_TXD")
),
- MTK_PIN(
- PINCTRL_PIN(122, "DPICK"),
+ MTK_PIN(PINCTRL_PIN(122, "DPICK"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 101),
MTK_FUNCTION(0, "GPIO122"),
@@ -1375,8 +1252,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "PCM1_DI"),
MTK_FUNCTION(6, "IRDA_PDN")
),
- MTK_PIN(
- PINCTRL_PIN(123, "DPIG4"),
+ MTK_PIN(PINCTRL_PIN(123, "DPIG4"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 114),
MTK_FUNCTION(0, "GPIO123"),
@@ -1385,8 +1261,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "CM2DAT_2X[0]"),
MTK_FUNCTION(5, "DSP2_ID")
),
- MTK_PIN(
- PINCTRL_PIN(124, "DPIG5"),
+ MTK_PIN(PINCTRL_PIN(124, "DPIG5"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 115),
MTK_FUNCTION(0, "GPIO124"),
@@ -1395,8 +1270,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "CM2DAT_2X[1]"),
MTK_FUNCTION(5, "DSP2_ICK")
),
- MTK_PIN(
- PINCTRL_PIN(125, "DPIR3"),
+ MTK_PIN(PINCTRL_PIN(125, "DPIR3"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 121),
MTK_FUNCTION(0, "GPIO125"),
@@ -1404,8 +1278,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(2, "EINT121"),
MTK_FUNCTION(4, "CM2DAT_2X[7]")
),
- MTK_PIN(
- PINCTRL_PIN(126, "DPIG1"),
+ MTK_PIN(PINCTRL_PIN(126, "DPIG1"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 111),
MTK_FUNCTION(0, "GPIO126"),
@@ -1413,8 +1286,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(2, "EINT111"),
MTK_FUNCTION(5, "DSP1_ICK")
),
- MTK_PIN(
- PINCTRL_PIN(127, "DPIVSYNC"),
+ MTK_PIN(PINCTRL_PIN(127, "DPIVSYNC"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 98),
MTK_FUNCTION(0, "GPIO127"),
@@ -1424,8 +1296,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "DAC_CK"),
MTK_FUNCTION(5, "PCM1_CK")
),
- MTK_PIN(
- PINCTRL_PIN(128, "DPIHSYNC"),
+ MTK_PIN(PINCTRL_PIN(128, "DPIHSYNC"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 99),
MTK_FUNCTION(0, "GPIO128"),
@@ -1436,8 +1307,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "PCM1_WS"),
MTK_FUNCTION(6, "IRDA_RXD")
),
- MTK_PIN(
- PINCTRL_PIN(129, "DPIB0"),
+ MTK_PIN(PINCTRL_PIN(129, "DPIB0"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 102),
MTK_FUNCTION(0, "GPIO129"),
@@ -1446,8 +1316,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "SCL0"),
MTK_FUNCTION(5, "DISP_PWM")
),
- MTK_PIN(
- PINCTRL_PIN(130, "DPIB1"),
+ MTK_PIN(PINCTRL_PIN(130, "DPIB1"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 103),
MTK_FUNCTION(0, "GPIO130"),
@@ -1457,8 +1326,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "SDA0"),
MTK_FUNCTION(5, "PWM1")
),
- MTK_PIN(
- PINCTRL_PIN(131, "DPIB2"),
+ MTK_PIN(PINCTRL_PIN(131, "DPIB2"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 104),
MTK_FUNCTION(0, "GPIO131"),
@@ -1468,8 +1336,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "SCL1"),
MTK_FUNCTION(5, "PWM2")
),
- MTK_PIN(
- PINCTRL_PIN(132, "DPIB3"),
+ MTK_PIN(PINCTRL_PIN(132, "DPIB3"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 105),
MTK_FUNCTION(0, "GPIO132"),
@@ -1479,8 +1346,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "SDA1"),
MTK_FUNCTION(5, "PWM3")
),
- MTK_PIN(
- PINCTRL_PIN(133, "DPIB4"),
+ MTK_PIN(PINCTRL_PIN(133, "DPIB4"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 106),
MTK_FUNCTION(0, "GPIO133"),
@@ -1490,8 +1356,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "SCL2"),
MTK_FUNCTION(5, "PWM4")
),
- MTK_PIN(
- PINCTRL_PIN(134, "DPIB5"),
+ MTK_PIN(PINCTRL_PIN(134, "DPIB5"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 107),
MTK_FUNCTION(0, "GPIO134"),
@@ -1501,8 +1366,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "SDA2"),
MTK_FUNCTION(5, "PWM5")
),
- MTK_PIN(
- PINCTRL_PIN(135, "DPIB6"),
+ MTK_PIN(PINCTRL_PIN(135, "DPIB6"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 108),
MTK_FUNCTION(0, "GPIO135"),
@@ -1512,8 +1376,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "SCL3"),
MTK_FUNCTION(5, "PWM6")
),
- MTK_PIN(
- PINCTRL_PIN(136, "DPIB7"),
+ MTK_PIN(PINCTRL_PIN(136, "DPIB7"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 109),
MTK_FUNCTION(0, "GPIO136"),
@@ -1523,8 +1386,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "SDA3"),
MTK_FUNCTION(5, "PWM7")
),
- MTK_PIN(
- PINCTRL_PIN(137, "DPIG0"),
+ MTK_PIN(PINCTRL_PIN(137, "DPIG0"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 110),
MTK_FUNCTION(0, "GPIO137"),
@@ -1532,8 +1394,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(2, "EINT110"),
MTK_FUNCTION(5, "DSP1_ID")
),
- MTK_PIN(
- PINCTRL_PIN(138, "DPIG2"),
+ MTK_PIN(PINCTRL_PIN(138, "DPIG2"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 112),
MTK_FUNCTION(0, "GPIO138"),
@@ -1541,8 +1402,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(2, "EINT112"),
MTK_FUNCTION(5, "DSP1_IMS")
),
- MTK_PIN(
- PINCTRL_PIN(139, "DPIG3"),
+ MTK_PIN(PINCTRL_PIN(139, "DPIG3"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 113),
MTK_FUNCTION(0, "GPIO139"),
@@ -1550,8 +1410,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(2, "EINT113"),
MTK_FUNCTION(5, "DSP2_IMS")
),
- MTK_PIN(
- PINCTRL_PIN(140, "DPIG6"),
+ MTK_PIN(PINCTRL_PIN(140, "DPIG6"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 116),
MTK_FUNCTION(0, "GPIO140"),
@@ -1559,8 +1418,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(2, "EINT116"),
MTK_FUNCTION(4, "CM2DAT_2X[2]")
),
- MTK_PIN(
- PINCTRL_PIN(141, "DPIG7"),
+ MTK_PIN(PINCTRL_PIN(141, "DPIG7"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 117),
MTK_FUNCTION(0, "GPIO141"),
@@ -1568,8 +1426,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(2, "EINT117"),
MTK_FUNCTION(4, "CM2DAT_2X[3]")
),
- MTK_PIN(
- PINCTRL_PIN(142, "DPIR0"),
+ MTK_PIN(PINCTRL_PIN(142, "DPIR0"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 118),
MTK_FUNCTION(0, "GPIO142"),
@@ -1577,8 +1434,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(2, "EINT118"),
MTK_FUNCTION(4, "CM2DAT_2X[4]")
),
- MTK_PIN(
- PINCTRL_PIN(143, "DPIR1"),
+ MTK_PIN(PINCTRL_PIN(143, "DPIR1"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 119),
MTK_FUNCTION(0, "GPIO143"),
@@ -1586,8 +1442,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(2, "EINT119"),
MTK_FUNCTION(4, "CM2DAT_2X[5]")
),
- MTK_PIN(
- PINCTRL_PIN(144, "DPIR2"),
+ MTK_PIN(PINCTRL_PIN(144, "DPIR2"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 120),
MTK_FUNCTION(0, "GPIO144"),
@@ -1595,8 +1450,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(2, "EINT120"),
MTK_FUNCTION(4, "CM2DAT_2X[6]")
),
- MTK_PIN(
- PINCTRL_PIN(145, "DPIR4"),
+ MTK_PIN(PINCTRL_PIN(145, "DPIR4"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 122),
MTK_FUNCTION(0, "GPIO145"),
@@ -1604,8 +1458,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(2, "EINT122"),
MTK_FUNCTION(4, "CM2DAT_2X[8]")
),
- MTK_PIN(
- PINCTRL_PIN(146, "DPIR5"),
+ MTK_PIN(PINCTRL_PIN(146, "DPIR5"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 123),
MTK_FUNCTION(0, "GPIO146"),
@@ -1613,8 +1466,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(2, "EINT123"),
MTK_FUNCTION(4, "CM2DAT_2X[9]")
),
- MTK_PIN(
- PINCTRL_PIN(147, "DPIR6"),
+ MTK_PIN(PINCTRL_PIN(147, "DPIR6"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 124),
MTK_FUNCTION(0, "GPIO147"),
@@ -1622,8 +1474,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(2, "EINT124"),
MTK_FUNCTION(4, "CM2VSYNC_2X")
),
- MTK_PIN(
- PINCTRL_PIN(148, "DPIR7"),
+ MTK_PIN(PINCTRL_PIN(148, "DPIR7"),
NULL, "mt8135",
MTK_EINT_FUNCTION(2, 125),
MTK_FUNCTION(0, "GPIO148"),
@@ -1631,192 +1482,165 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(2, "EINT125"),
MTK_FUNCTION(4, "CM2HSYNC_2X")
),
- MTK_PIN(
- PINCTRL_PIN(149, "TDN3/LVDS(TDN3)"),
+ MTK_PIN(PINCTRL_PIN(149, "TDN3/LVDS(TDN3)"),
"AA2", "mt8135",
MTK_EINT_FUNCTION(2, 36),
MTK_FUNCTION(0, "GPIO149"),
MTK_FUNCTION(2, "EINT36")
),
- MTK_PIN(
- PINCTRL_PIN(150, "TDP3/LVDS(TDP3)"),
+ MTK_PIN(PINCTRL_PIN(150, "TDP3/LVDS(TDP3)"),
"AA1", "mt8135",
MTK_EINT_FUNCTION(2, 35),
MTK_FUNCTION(0, "GPIO150"),
MTK_FUNCTION(2, "EINT35")
),
- MTK_PIN(
- PINCTRL_PIN(151, "TDN2/LVDS(TCN)"),
+ MTK_PIN(PINCTRL_PIN(151, "TDN2/LVDS(TCN)"),
"Y2", "mt8135",
MTK_EINT_FUNCTION(2, 169),
MTK_FUNCTION(0, "GPIO151"),
MTK_FUNCTION(2, "EINT169")
),
- MTK_PIN(
- PINCTRL_PIN(152, "TDP2/LVDS(TCP)"),
+ MTK_PIN(PINCTRL_PIN(152, "TDP2/LVDS(TCP)"),
"Y1", "mt8135",
MTK_EINT_FUNCTION(2, 168),
MTK_FUNCTION(0, "GPIO152"),
MTK_FUNCTION(2, "EINT168")
),
- MTK_PIN(
- PINCTRL_PIN(153, "TCN/LVDS(TDN2)"),
+ MTK_PIN(PINCTRL_PIN(153, "TCN/LVDS(TDN2)"),
"W2", "mt8135",
MTK_EINT_FUNCTION(2, 163),
MTK_FUNCTION(0, "GPIO153"),
MTK_FUNCTION(2, "EINT163")
),
- MTK_PIN(
- PINCTRL_PIN(154, "TCP/LVDS(TDP2)"),
+ MTK_PIN(PINCTRL_PIN(154, "TCP/LVDS(TDP2)"),
"W1", "mt8135",
MTK_EINT_FUNCTION(2, 162),
MTK_FUNCTION(0, "GPIO154"),
MTK_FUNCTION(2, "EINT162")
),
- MTK_PIN(
- PINCTRL_PIN(155, "TDN1/LVDS(TDN1)"),
+ MTK_PIN(PINCTRL_PIN(155, "TDN1/LVDS(TDN1)"),
"V3", "mt8135",
MTK_EINT_FUNCTION(2, 167),
MTK_FUNCTION(0, "GPIO155"),
MTK_FUNCTION(2, "EINT167")
),
- MTK_PIN(
- PINCTRL_PIN(156, "TDP1/LVDS(TDP1)"),
+ MTK_PIN(PINCTRL_PIN(156, "TDP1/LVDS(TDP1)"),
"V2", "mt8135",
MTK_EINT_FUNCTION(2, 166),
MTK_FUNCTION(0, "GPIO156"),
MTK_FUNCTION(2, "EINT166")
),
- MTK_PIN(
- PINCTRL_PIN(157, "TDN0/LVDS(TDN0)"),
+ MTK_PIN(PINCTRL_PIN(157, "TDN0/LVDS(TDN0)"),
"U3", "mt8135",
MTK_EINT_FUNCTION(2, 165),
MTK_FUNCTION(0, "GPIO157"),
MTK_FUNCTION(2, "EINT165")
),
- MTK_PIN(
- PINCTRL_PIN(158, "TDP0/LVDS(TDP0)"),
+ MTK_PIN(PINCTRL_PIN(158, "TDP0/LVDS(TDP0)"),
"U2", "mt8135",
MTK_EINT_FUNCTION(2, 164),
MTK_FUNCTION(0, "GPIO158"),
MTK_FUNCTION(2, "EINT164")
),
- MTK_PIN(
- PINCTRL_PIN(159, "RDN3"),
+ MTK_PIN(PINCTRL_PIN(159, "RDN3"),
"N5", "mt8135",
MTK_EINT_FUNCTION(2, 18),
MTK_FUNCTION(0, "GPIO159"),
MTK_FUNCTION(2, "EINT18")
),
- MTK_PIN(
- PINCTRL_PIN(160, "RDP3"),
+ MTK_PIN(PINCTRL_PIN(160, "RDP3"),
"N4", "mt8135",
MTK_EINT_FUNCTION(2, 30),
MTK_FUNCTION(0, "GPIO160"),
MTK_FUNCTION(2, "EINT30")
),
- MTK_PIN(
- PINCTRL_PIN(161, "RDN2"),
+ MTK_PIN(PINCTRL_PIN(161, "RDN2"),
"T2", "mt8135",
MTK_EINT_FUNCTION(2, 31),
MTK_FUNCTION(0, "GPIO161"),
MTK_FUNCTION(2, "EINT31")
),
- MTK_PIN(
- PINCTRL_PIN(162, "RDP2"),
+ MTK_PIN(PINCTRL_PIN(162, "RDP2"),
"T3", "mt8135",
MTK_EINT_FUNCTION(2, 32),
MTK_FUNCTION(0, "GPIO162"),
MTK_FUNCTION(2, "EINT32")
),
- MTK_PIN(
- PINCTRL_PIN(163, "RCN"),
+ MTK_PIN(PINCTRL_PIN(163, "RCN"),
"P2", "mt8135",
MTK_EINT_FUNCTION(2, 33),
MTK_FUNCTION(0, "GPIO163"),
MTK_FUNCTION(2, "EINT33")
),
- MTK_PIN(
- PINCTRL_PIN(164, "RCP"),
+ MTK_PIN(PINCTRL_PIN(164, "RCP"),
"P3", "mt8135",
MTK_EINT_FUNCTION(2, 39),
MTK_FUNCTION(0, "GPIO164"),
MTK_FUNCTION(2, "EINT39")
),
- MTK_PIN(
- PINCTRL_PIN(165, "RDN1"),
+ MTK_PIN(PINCTRL_PIN(165, "RDN1"),
"R3", "mt8135",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO165")
),
- MTK_PIN(
- PINCTRL_PIN(166, "RDP1"),
+ MTK_PIN(PINCTRL_PIN(166, "RDP1"),
"R2", "mt8135",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO166")
),
- MTK_PIN(
- PINCTRL_PIN(167, "RDN0"),
+ MTK_PIN(PINCTRL_PIN(167, "RDN0"),
"N3", "mt8135",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO167")
),
- MTK_PIN(
- PINCTRL_PIN(168, "RDP0"),
+ MTK_PIN(PINCTRL_PIN(168, "RDP0"),
"N2", "mt8135",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO168")
),
- MTK_PIN(
- PINCTRL_PIN(169, "RDN1_A"),
+ MTK_PIN(PINCTRL_PIN(169, "RDN1_A"),
"M4", "mt8135",
MTK_EINT_FUNCTION(2, 175),
MTK_FUNCTION(0, "GPIO169"),
MTK_FUNCTION(1, "CMDAT6"),
MTK_FUNCTION(2, "EINT175")
),
- MTK_PIN(
- PINCTRL_PIN(170, "RDP1_A"),
+ MTK_PIN(PINCTRL_PIN(170, "RDP1_A"),
"M3", "mt8135",
MTK_EINT_FUNCTION(2, 174),
MTK_FUNCTION(0, "GPIO170"),
MTK_FUNCTION(1, "CMDAT7"),
MTK_FUNCTION(2, "EINT174")
),
- MTK_PIN(
- PINCTRL_PIN(171, "RCN_A"),
+ MTK_PIN(PINCTRL_PIN(171, "RCN_A"),
"L3", "mt8135",
MTK_EINT_FUNCTION(2, 171),
MTK_FUNCTION(0, "GPIO171"),
MTK_FUNCTION(1, "CMDAT8"),
MTK_FUNCTION(2, "EINT171")
),
- MTK_PIN(
- PINCTRL_PIN(172, "RCP_A"),
+ MTK_PIN(PINCTRL_PIN(172, "RCP_A"),
"L2", "mt8135",
MTK_EINT_FUNCTION(2, 170),
MTK_FUNCTION(0, "GPIO172"),
MTK_FUNCTION(1, "CMDAT9"),
MTK_FUNCTION(2, "EINT170")
),
- MTK_PIN(
- PINCTRL_PIN(173, "RDN0_A"),
+ MTK_PIN(PINCTRL_PIN(173, "RDN0_A"),
"M2", "mt8135",
MTK_EINT_FUNCTION(2, 173),
MTK_FUNCTION(0, "GPIO173"),
MTK_FUNCTION(1, "CMHSYNC"),
MTK_FUNCTION(2, "EINT173")
),
- MTK_PIN(
- PINCTRL_PIN(174, "RDP0_A"),
+ MTK_PIN(PINCTRL_PIN(174, "RDP0_A"),
"M1", "mt8135",
MTK_EINT_FUNCTION(2, 172),
MTK_FUNCTION(0, "GPIO174"),
MTK_FUNCTION(1, "CMVSYNC"),
MTK_FUNCTION(2, "EINT172")
),
- MTK_PIN(
- PINCTRL_PIN(175, "RDN1_B"),
+ MTK_PIN(PINCTRL_PIN(175, "RDN1_B"),
"H2", "mt8135",
MTK_EINT_FUNCTION(2, 181),
MTK_FUNCTION(0, "GPIO175"),
@@ -1824,8 +1648,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(2, "EINT181"),
MTK_FUNCTION(3, "CMCSD2")
),
- MTK_PIN(
- PINCTRL_PIN(176, "RDP1_B"),
+ MTK_PIN(PINCTRL_PIN(176, "RDP1_B"),
"H1", "mt8135",
MTK_EINT_FUNCTION(2, 180),
MTK_FUNCTION(0, "GPIO176"),
@@ -1833,24 +1656,21 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(2, "EINT180"),
MTK_FUNCTION(3, "CMCSD3")
),
- MTK_PIN(
- PINCTRL_PIN(177, "RCN_B"),
+ MTK_PIN(PINCTRL_PIN(177, "RCN_B"),
"K3", "mt8135",
MTK_EINT_FUNCTION(2, 177),
MTK_FUNCTION(0, "GPIO177"),
MTK_FUNCTION(1, "CMDAT4"),
MTK_FUNCTION(2, "EINT177")
),
- MTK_PIN(
- PINCTRL_PIN(178, "RCP_B"),
+ MTK_PIN(PINCTRL_PIN(178, "RCP_B"),
"K2", "mt8135",
MTK_EINT_FUNCTION(2, 176),
MTK_FUNCTION(0, "GPIO178"),
MTK_FUNCTION(1, "CMDAT5"),
MTK_FUNCTION(2, "EINT176")
),
- MTK_PIN(
- PINCTRL_PIN(179, "RDN0_B"),
+ MTK_PIN(PINCTRL_PIN(179, "RDN0_B"),
"J3", "mt8135",
MTK_EINT_FUNCTION(2, 179),
MTK_FUNCTION(0, "GPIO179"),
@@ -1858,8 +1678,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(2, "EINT179"),
MTK_FUNCTION(3, "CMCSD0")
),
- MTK_PIN(
- PINCTRL_PIN(180, "RDP0_B"),
+ MTK_PIN(PINCTRL_PIN(180, "RDP0_B"),
"J2", "mt8135",
MTK_EINT_FUNCTION(2, 178),
MTK_FUNCTION(0, "GPIO180"),
@@ -1867,8 +1686,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(2, "EINT178"),
MTK_FUNCTION(3, "CMCSD1")
),
- MTK_PIN(
- PINCTRL_PIN(181, "CMPCLK"),
+ MTK_PIN(PINCTRL_PIN(181, "CMPCLK"),
"K4", "mt8135",
MTK_EINT_FUNCTION(2, 182),
MTK_FUNCTION(0, "GPIO181"),
@@ -1880,8 +1698,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "VENC_TEST_CK"),
MTK_FUNCTION(7, "TESTA_OUT27")
),
- MTK_PIN(
- PINCTRL_PIN(182, "CMMCLK"),
+ MTK_PIN(PINCTRL_PIN(182, "CMMCLK"),
"J5", "mt8135",
MTK_EINT_FUNCTION(2, 183),
MTK_FUNCTION(0, "GPIO182"),
@@ -1890,8 +1707,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "TS_AUXADC_SEL[2]"),
MTK_FUNCTION(7, "TESTA_OUT28")
),
- MTK_PIN(
- PINCTRL_PIN(183, "CMRST"),
+ MTK_PIN(PINCTRL_PIN(183, "CMRST"),
"J6", "mt8135",
MTK_EINT_FUNCTION(2, 185),
MTK_FUNCTION(0, "GPIO183"),
@@ -1900,8 +1716,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "TS_AUXADC_SEL[1]"),
MTK_FUNCTION(7, "TESTA_OUT30")
),
- MTK_PIN(
- PINCTRL_PIN(184, "CMPDN"),
+ MTK_PIN(PINCTRL_PIN(184, "CMPDN"),
"J4", "mt8135",
MTK_EINT_FUNCTION(2, 184),
MTK_FUNCTION(0, "GPIO184"),
@@ -1910,8 +1725,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "TS_AUXADC_SEL[0]"),
MTK_FUNCTION(7, "TESTA_OUT29")
),
- MTK_PIN(
- PINCTRL_PIN(185, "CMFLASH"),
+ MTK_PIN(PINCTRL_PIN(185, "CMFLASH"),
"G4", "mt8135",
MTK_EINT_FUNCTION(2, 186),
MTK_FUNCTION(0, "GPIO185"),
@@ -1921,8 +1735,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "MFG_TEST_CK_1"),
MTK_FUNCTION(7, "TESTA_OUT31")
),
- MTK_PIN(
- PINCTRL_PIN(186, "MRG_I2S_PCM_CLK"),
+ MTK_PIN(PINCTRL_PIN(186, "MRG_I2S_PCM_CLK"),
"F5", "mt8135",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO186"),
@@ -1933,8 +1746,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "IMG_TEST_CK"),
MTK_FUNCTION(7, "USB_SCL")
),
- MTK_PIN(
- PINCTRL_PIN(187, "MRG_I2S_PCM_SYNC"),
+ MTK_PIN(PINCTRL_PIN(187, "MRG_I2S_PCM_SYNC"),
"G6", "mt8135",
MTK_EINT_FUNCTION(2, 16),
MTK_FUNCTION(0, "GPIO187"),
@@ -1944,8 +1756,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "PCM0_WS"),
MTK_FUNCTION(6, "DISP_TEST_CK")
),
- MTK_PIN(
- PINCTRL_PIN(188, "MRG_I2S_PCM_RX"),
+ MTK_PIN(PINCTRL_PIN(188, "MRG_I2S_PCM_RX"),
"G3", "mt8135",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO188"),
@@ -1956,8 +1767,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(6, "MFG_TEST_CK"),
MTK_FUNCTION(7, "USB_SDA")
),
- MTK_PIN(
- PINCTRL_PIN(189, "MRG_I2S_PCM_TX"),
+ MTK_PIN(PINCTRL_PIN(189, "MRG_I2S_PCM_TX"),
"G5", "mt8135",
MTK_EINT_FUNCTION(2, 17),
MTK_FUNCTION(0, "GPIO189"),
@@ -1967,15 +1777,13 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "PCM0_DO"),
MTK_FUNCTION(6, "VDEC_TEST_CK")
),
- MTK_PIN(
- PINCTRL_PIN(190, "SRCLKENAI"),
+ MTK_PIN(PINCTRL_PIN(190, "SRCLKENAI"),
"K5", "mt8135",
MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT),
MTK_FUNCTION(0, "GPIO190"),
MTK_FUNCTION(1, "SRCLKENAI")
),
- MTK_PIN(
- PINCTRL_PIN(191, "URXD3"),
+ MTK_PIN(PINCTRL_PIN(191, "URXD3"),
"C3", "mt8135",
MTK_EINT_FUNCTION(2, 87),
MTK_FUNCTION(0, "GPIO191"),
@@ -1985,8 +1793,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "TS_AUX_ST"),
MTK_FUNCTION(6, "PWM4")
),
- MTK_PIN(
- PINCTRL_PIN(192, "UTXD3"),
+ MTK_PIN(PINCTRL_PIN(192, "UTXD3"),
"B2", "mt8135",
MTK_EINT_FUNCTION(2, 86),
MTK_FUNCTION(0, "GPIO192"),
@@ -1996,8 +1803,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "TS_AUX_CS_B"),
MTK_FUNCTION(6, "PWM3")
),
- MTK_PIN(
- PINCTRL_PIN(193, "SDA2"),
+ MTK_PIN(PINCTRL_PIN(193, "SDA2"),
"G2", "mt8135",
MTK_EINT_FUNCTION(2, 95),
MTK_FUNCTION(0, "GPIO193"),
@@ -2007,8 +1813,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "PWM5"),
MTK_FUNCTION(5, "TS_AUX_PWDB")
),
- MTK_PIN(
- PINCTRL_PIN(194, "SCL2"),
+ MTK_PIN(PINCTRL_PIN(194, "SCL2"),
"F4", "mt8135",
MTK_EINT_FUNCTION(2, 94),
MTK_FUNCTION(0, "GPIO194"),
@@ -2018,8 +1823,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "PWM4"),
MTK_FUNCTION(5, "TS_AUXADC_TEST_CK")
),
- MTK_PIN(
- PINCTRL_PIN(195, "SDA1"),
+ MTK_PIN(PINCTRL_PIN(195, "SDA1"),
"F2", "mt8135",
MTK_EINT_FUNCTION(2, 93),
MTK_FUNCTION(0, "GPIO195"),
@@ -2029,8 +1833,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "PWM3"),
MTK_FUNCTION(5, "TS_AUX_SCLK_PWDB")
),
- MTK_PIN(
- PINCTRL_PIN(196, "SCL1"),
+ MTK_PIN(PINCTRL_PIN(196, "SCL1"),
"F3", "mt8135",
MTK_EINT_FUNCTION(2, 92),
MTK_FUNCTION(0, "GPIO196"),
@@ -2040,8 +1843,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "PWM2"),
MTK_FUNCTION(5, "TS_AUX_DIN")
),
- MTK_PIN(
- PINCTRL_PIN(197, "MSDC3_DAT2"),
+ MTK_PIN(PINCTRL_PIN(197, "MSDC3_DAT2"),
"E1", "mt8135",
MTK_EINT_FUNCTION(2, 71),
MTK_FUNCTION(0, "GPIO197"),
@@ -2052,8 +1854,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "CLKM4"),
MTK_FUNCTION(6, "MFG_TEST_CK_2")
),
- MTK_PIN(
- PINCTRL_PIN(198, "MSDC3_DAT3"),
+ MTK_PIN(PINCTRL_PIN(198, "MSDC3_DAT3"),
"C2", "mt8135",
MTK_EINT_FUNCTION(2, 72),
MTK_FUNCTION(0, "GPIO198"),
@@ -2064,8 +1865,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "CLKM5"),
MTK_FUNCTION(6, "MFG_TEST_CK_3")
),
- MTK_PIN(
- PINCTRL_PIN(199, "MSDC3_CMD"),
+ MTK_PIN(PINCTRL_PIN(199, "MSDC3_CMD"),
"D2", "mt8135",
MTK_EINT_FUNCTION(2, 68),
MTK_FUNCTION(0, "GPIO199"),
@@ -2076,8 +1876,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(5, "CLKM1"),
MTK_FUNCTION(6, "MFG_TEST_CK_4")
),
- MTK_PIN(
- PINCTRL_PIN(200, "MSDC3_CLK"),
+ MTK_PIN(PINCTRL_PIN(200, "MSDC3_CLK"),
"E2", "mt8135",
MTK_EINT_FUNCTION(2, 67),
MTK_FUNCTION(0, "GPIO200"),
@@ -2087,8 +1886,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "PWM1"),
MTK_FUNCTION(5, "CLKM0")
),
- MTK_PIN(
- PINCTRL_PIN(201, "MSDC3_DAT1"),
+ MTK_PIN(PINCTRL_PIN(201, "MSDC3_DAT1"),
"D3", "mt8135",
MTK_EINT_FUNCTION(2, 70),
MTK_FUNCTION(0, "GPIO201"),
@@ -2098,8 +1896,7 @@ static const struct mtk_desc_pin mtk_pins_mt8135[] = {
MTK_FUNCTION(4, "PWM4"),
MTK_FUNCTION(5, "CLKM3")
),
- MTK_PIN(
- PINCTRL_PIN(202, "MSDC3_DAT0"),
+ MTK_PIN(PINCTRL_PIN(202, "MSDC3_DAT0"),
"E3", "mt8135",
MTK_EINT_FUNCTION(2, 69),
MTK_FUNCTION(0, "GPIO202"),
diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-mt8173.h b/drivers/pinctrl/mediatek/pinctrl-mtk-mt8173.h
index 9b018fdbeb51..8bd0c10753f5 100644
--- a/drivers/pinctrl/mediatek/pinctrl-mtk-mt8173.h
+++ b/drivers/pinctrl/mediatek/pinctrl-mtk-mt8173.h
@@ -19,8 +19,7 @@
#include "pinctrl-mtk-common.h"
static const struct mtk_desc_pin mtk_pins_mt8173[] = {
- MTK_PIN(
- PINCTRL_PIN(0, "EINT0"),
+ MTK_PIN(PINCTRL_PIN(0, "EINT0"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 0),
MTK_FUNCTION(0, "GPIO0"),
@@ -30,8 +29,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(4, "UTXD0"),
MTK_FUNCTION(7, "DBG_MON_A_20_")
),
- MTK_PIN(
- PINCTRL_PIN(1, "EINT1"),
+ MTK_PIN(PINCTRL_PIN(1, "EINT1"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 1),
MTK_FUNCTION(0, "GPIO1"),
@@ -41,8 +39,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(4, "URXD0"),
MTK_FUNCTION(7, "DBG_MON_A_21_")
),
- MTK_PIN(
- PINCTRL_PIN(2, "EINT2"),
+ MTK_PIN(PINCTRL_PIN(2, "EINT2"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 2),
MTK_FUNCTION(0, "GPIO2"),
@@ -52,8 +49,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(4, "UTXD3"),
MTK_FUNCTION(7, "DBG_MON_A_22_")
),
- MTK_PIN(
- PINCTRL_PIN(3, "EINT3"),
+ MTK_PIN(PINCTRL_PIN(3, "EINT3"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 3),
MTK_FUNCTION(0, "GPIO3"),
@@ -63,8 +59,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(4, "URXD3"),
MTK_FUNCTION(7, "DBG_MON_A_23_")
),
- MTK_PIN(
- PINCTRL_PIN(4, "EINT4"),
+ MTK_PIN(PINCTRL_PIN(4, "EINT4"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 4),
MTK_FUNCTION(0, "GPIO4"),
@@ -74,8 +69,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(4, "UCTS3"),
MTK_FUNCTION(6, "SFWP_B")
),
- MTK_PIN(
- PINCTRL_PIN(5, "EINT5"),
+ MTK_PIN(PINCTRL_PIN(5, "EINT5"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 5),
MTK_FUNCTION(0, "GPIO5"),
@@ -86,8 +80,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(5, "AP_MD32_JTAG_TMS"),
MTK_FUNCTION(6, "SFOUT")
),
- MTK_PIN(
- PINCTRL_PIN(6, "EINT6"),
+ MTK_PIN(PINCTRL_PIN(6, "EINT6"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 6),
MTK_FUNCTION(0, "GPIO6"),
@@ -97,8 +90,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(5, "AP_MD32_JTAG_TCK"),
MTK_FUNCTION(6, "SFCS0")
),
- MTK_PIN(
- PINCTRL_PIN(7, "EINT7"),
+ MTK_PIN(PINCTRL_PIN(7, "EINT7"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 7),
MTK_FUNCTION(0, "GPIO7"),
@@ -108,8 +100,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(5, "AP_MD32_JTAG_TDI"),
MTK_FUNCTION(6, "SFHOLD")
),
- MTK_PIN(
- PINCTRL_PIN(8, "EINT8"),
+ MTK_PIN(PINCTRL_PIN(8, "EINT8"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 8),
MTK_FUNCTION(0, "GPIO8"),
@@ -120,8 +111,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(5, "AP_MD32_JTAG_TDO"),
MTK_FUNCTION(6, "SFIN")
),
- MTK_PIN(
- PINCTRL_PIN(9, "EINT9"),
+ MTK_PIN(PINCTRL_PIN(9, "EINT9"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 9),
MTK_FUNCTION(0, "GPIO9"),
@@ -131,8 +121,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(5, "AP_MD32_JTAG_TRST"),
MTK_FUNCTION(6, "SFCK")
),
- MTK_PIN(
- PINCTRL_PIN(10, "EINT10"),
+ MTK_PIN(PINCTRL_PIN(10, "EINT10"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 10),
MTK_FUNCTION(0, "GPIO10"),
@@ -142,8 +131,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(4, "PWM4"),
MTK_FUNCTION(5, "IRDA_RXD")
),
- MTK_PIN(
- PINCTRL_PIN(11, "EINT11"),
+ MTK_PIN(PINCTRL_PIN(11, "EINT11"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 11),
MTK_FUNCTION(0, "GPIO11"),
@@ -155,8 +143,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(6, "USB_DRVVBUS_P1"),
MTK_FUNCTION(7, "DBG_MON_B_30_")
),
- MTK_PIN(
- PINCTRL_PIN(12, "EINT12"),
+ MTK_PIN(PINCTRL_PIN(12, "EINT12"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 12),
MTK_FUNCTION(0, "GPIO12"),
@@ -166,8 +153,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(5, "I2S2_WS"),
MTK_FUNCTION(7, "DBG_MON_B_32_")
),
- MTK_PIN(
- PINCTRL_PIN(13, "EINT13"),
+ MTK_PIN(PINCTRL_PIN(13, "EINT13"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 13),
MTK_FUNCTION(0, "GPIO13"),
@@ -177,8 +163,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(5, "I2S2_BCK"),
MTK_FUNCTION(7, "DBG_MON_A_32_")
),
- MTK_PIN(
- PINCTRL_PIN(14, "EINT14"),
+ MTK_PIN(PINCTRL_PIN(14, "EINT14"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 14),
MTK_FUNCTION(0, "GPIO14"),
@@ -187,8 +172,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(4, "CLKM2"),
MTK_FUNCTION(7, "DBG_MON_B_6_")
),
- MTK_PIN(
- PINCTRL_PIN(15, "EINT15"),
+ MTK_PIN(PINCTRL_PIN(15, "EINT15"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 15),
MTK_FUNCTION(0, "GPIO15"),
@@ -198,8 +182,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(4, "CLKM3"),
MTK_FUNCTION(7, "DBG_MON_B_29_")
),
- MTK_PIN(
- PINCTRL_PIN(16, "IDDIG"),
+ MTK_PIN(PINCTRL_PIN(16, "IDDIG"),
NULL, "mt8173",
MTK_EINT_FUNCTION(1, 16),
MTK_FUNCTION(0, "GPIO16"),
@@ -207,156 +190,135 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(2, "CMFLASH"),
MTK_FUNCTION(4, "PWM5")
),
- MTK_PIN(
- PINCTRL_PIN(17, "WATCHDOG"),
+ MTK_PIN(PINCTRL_PIN(17, "WATCHDOG"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 17),
MTK_FUNCTION(0, "GPIO17"),
MTK_FUNCTION(1, "WATCHDOG_AO")
),
- MTK_PIN(
- PINCTRL_PIN(18, "CEC"),
+ MTK_PIN(PINCTRL_PIN(18, "CEC"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 18),
MTK_FUNCTION(0, "GPIO18"),
MTK_FUNCTION(1, "CEC")
),
- MTK_PIN(
- PINCTRL_PIN(19, "HDMISCK"),
+ MTK_PIN(PINCTRL_PIN(19, "HDMISCK"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 19),
MTK_FUNCTION(0, "GPIO19"),
MTK_FUNCTION(1, "HDMISCK"),
MTK_FUNCTION(2, "HDCP_SCL")
),
- MTK_PIN(
- PINCTRL_PIN(20, "HDMISD"),
+ MTK_PIN(PINCTRL_PIN(20, "HDMISD"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 20),
MTK_FUNCTION(0, "GPIO20"),
MTK_FUNCTION(1, "HDMISD"),
MTK_FUNCTION(2, "HDCP_SDA")
),
- MTK_PIN(
- PINCTRL_PIN(21, "HTPLG"),
+ MTK_PIN(PINCTRL_PIN(21, "HTPLG"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 21),
MTK_FUNCTION(0, "GPIO21"),
MTK_FUNCTION(1, "HTPLG")
),
- MTK_PIN(
- PINCTRL_PIN(22, "MSDC3_DAT0"),
+ MTK_PIN(PINCTRL_PIN(22, "MSDC3_DAT0"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 22),
MTK_FUNCTION(0, "GPIO22"),
MTK_FUNCTION(1, "MSDC3_DAT0")
),
- MTK_PIN(
- PINCTRL_PIN(23, "MSDC3_DAT1"),
+ MTK_PIN(PINCTRL_PIN(23, "MSDC3_DAT1"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 23),
MTK_FUNCTION(0, "GPIO23"),
MTK_FUNCTION(1, "MSDC3_DAT1")
),
- MTK_PIN(
- PINCTRL_PIN(24, "MSDC3_DAT2"),
+ MTK_PIN(PINCTRL_PIN(24, "MSDC3_DAT2"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 24),
MTK_FUNCTION(0, "GPIO24"),
MTK_FUNCTION(1, "MSDC3_DAT2")
),
- MTK_PIN(
- PINCTRL_PIN(25, "MSDC3_DAT3"),
+ MTK_PIN(PINCTRL_PIN(25, "MSDC3_DAT3"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 25),
MTK_FUNCTION(0, "GPIO25"),
MTK_FUNCTION(1, "MSDC3_DAT3")
),
- MTK_PIN(
- PINCTRL_PIN(26, "MSDC3_CLK"),
+ MTK_PIN(PINCTRL_PIN(26, "MSDC3_CLK"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 26),
MTK_FUNCTION(0, "GPIO26"),
MTK_FUNCTION(1, "MSDC3_CLK")
),
- MTK_PIN(
- PINCTRL_PIN(27, "MSDC3_CMD"),
+ MTK_PIN(PINCTRL_PIN(27, "MSDC3_CMD"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 27),
MTK_FUNCTION(0, "GPIO27"),
MTK_FUNCTION(1, "MSDC3_CMD")
),
- MTK_PIN(
- PINCTRL_PIN(28, "MSDC3_DSL"),
+ MTK_PIN(PINCTRL_PIN(28, "MSDC3_DSL"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 28),
MTK_FUNCTION(0, "GPIO28"),
MTK_FUNCTION(1, "MSDC3_DSL")
),
- MTK_PIN(
- PINCTRL_PIN(29, "UCTS2"),
+ MTK_PIN(PINCTRL_PIN(29, "UCTS2"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 29),
MTK_FUNCTION(0, "GPIO29"),
MTK_FUNCTION(1, "UCTS2")
),
- MTK_PIN(
- PINCTRL_PIN(30, "URTS2"),
+ MTK_PIN(PINCTRL_PIN(30, "URTS2"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 30),
MTK_FUNCTION(0, "GPIO30"),
MTK_FUNCTION(1, "URTS2")
),
- MTK_PIN(
- PINCTRL_PIN(31, "URXD2"),
+ MTK_PIN(PINCTRL_PIN(31, "URXD2"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 31),
MTK_FUNCTION(0, "GPIO31"),
MTK_FUNCTION(1, "URXD2"),
MTK_FUNCTION(2, "UTXD2")
),
- MTK_PIN(
- PINCTRL_PIN(32, "UTXD2"),
+ MTK_PIN(PINCTRL_PIN(32, "UTXD2"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 32),
MTK_FUNCTION(0, "GPIO32"),
MTK_FUNCTION(1, "UTXD2"),
MTK_FUNCTION(2, "URXD2")
),
- MTK_PIN(
- PINCTRL_PIN(33, "DAICLK"),
+ MTK_PIN(PINCTRL_PIN(33, "DAICLK"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 33),
MTK_FUNCTION(0, "GPIO33"),
MTK_FUNCTION(1, " MRG_CLK"),
MTK_FUNCTION(2, "PCM0_CLK")
),
- MTK_PIN(
- PINCTRL_PIN(34, "DAIPCMIN"),
+ MTK_PIN(PINCTRL_PIN(34, "DAIPCMIN"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 34),
MTK_FUNCTION(0, "GPIO34"),
MTK_FUNCTION(1, " MRG_DI"),
MTK_FUNCTION(2, "PCM0_DI")
),
- MTK_PIN(
- PINCTRL_PIN(35, "DAIPCMOUT"),
+ MTK_PIN(PINCTRL_PIN(35, "DAIPCMOUT"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 35),
MTK_FUNCTION(0, "GPIO35"),
MTK_FUNCTION(1, " MRG_DO"),
MTK_FUNCTION(2, "PCM0_DO")
),
- MTK_PIN(
- PINCTRL_PIN(36, "DAISYNC"),
+ MTK_PIN(PINCTRL_PIN(36, "DAISYNC"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 36),
MTK_FUNCTION(0, "GPIO36"),
MTK_FUNCTION(1, " MRG_SYNC"),
MTK_FUNCTION(2, "PCM0_SYNC")
),
- MTK_PIN(
- PINCTRL_PIN(37, "EINT16"),
+ MTK_PIN(PINCTRL_PIN(37, "EINT16"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 37),
MTK_FUNCTION(0, "GPIO37"),
@@ -367,8 +329,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(5, "PWM2"),
MTK_FUNCTION(6, "CLKM0")
),
- MTK_PIN(
- PINCTRL_PIN(38, "CONN_RST"),
+ MTK_PIN(PINCTRL_PIN(38, "CONN_RST"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 38),
MTK_FUNCTION(0, "GPIO38"),
@@ -376,8 +337,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(2, "USB_DRVVBUS_P1"),
MTK_FUNCTION(6, "CLKM1")
),
- MTK_PIN(
- PINCTRL_PIN(39, "CM2MCLK"),
+ MTK_PIN(PINCTRL_PIN(39, "CM2MCLK"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 39),
MTK_FUNCTION(0, "GPIO39"),
@@ -385,8 +345,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(2, "CMCSD0"),
MTK_FUNCTION(7, "DBG_MON_A_17_")
),
- MTK_PIN(
- PINCTRL_PIN(40, "CMPCLK"),
+ MTK_PIN(PINCTRL_PIN(40, "CMPCLK"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 40),
MTK_FUNCTION(0, "GPIO40"),
@@ -395,125 +354,108 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(3, "CMCSD2"),
MTK_FUNCTION(7, "DBG_MON_A_18_")
),
- MTK_PIN(
- PINCTRL_PIN(41, "CMMCLK"),
+ MTK_PIN(PINCTRL_PIN(41, "CMMCLK"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 41),
MTK_FUNCTION(0, "GPIO41"),
MTK_FUNCTION(1, "CMMCLK"),
MTK_FUNCTION(7, "DBG_MON_A_19_")
),
- MTK_PIN(
- PINCTRL_PIN(42, "DSI_TE"),
+ MTK_PIN(PINCTRL_PIN(42, "DSI_TE"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 42),
MTK_FUNCTION(0, "GPIO42"),
MTK_FUNCTION(1, "DSI_TE")
),
- MTK_PIN(
- PINCTRL_PIN(43, "SDA2"),
+ MTK_PIN(PINCTRL_PIN(43, "SDA2"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 43),
MTK_FUNCTION(0, "GPIO43"),
MTK_FUNCTION(1, "SDA2")
),
- MTK_PIN(
- PINCTRL_PIN(44, "SCL2"),
+ MTK_PIN(PINCTRL_PIN(44, "SCL2"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 44),
MTK_FUNCTION(0, "GPIO44"),
MTK_FUNCTION(1, "SCL2")
),
- MTK_PIN(
- PINCTRL_PIN(45, "SDA0"),
+ MTK_PIN(PINCTRL_PIN(45, "SDA0"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 45),
MTK_FUNCTION(0, "GPIO45"),
MTK_FUNCTION(1, "SDA0")
),
- MTK_PIN(
- PINCTRL_PIN(46, "SCL0"),
+ MTK_PIN(PINCTRL_PIN(46, "SCL0"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 46),
MTK_FUNCTION(0, "GPIO46"),
MTK_FUNCTION(1, "SCL0")
),
- MTK_PIN(
- PINCTRL_PIN(47, "RDN0_A"),
+ MTK_PIN(PINCTRL_PIN(47, "RDN0_A"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 47),
MTK_FUNCTION(0, "GPIO47"),
MTK_FUNCTION(1, "CMDAT2")
),
- MTK_PIN(
- PINCTRL_PIN(48, "RDP0_A"),
+ MTK_PIN(PINCTRL_PIN(48, "RDP0_A"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 48),
MTK_FUNCTION(0, "GPIO48"),
MTK_FUNCTION(1, "CMDAT3")
),
- MTK_PIN(
- PINCTRL_PIN(49, "RDN1_A"),
+ MTK_PIN(PINCTRL_PIN(49, "RDN1_A"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 49),
MTK_FUNCTION(0, "GPIO49"),
MTK_FUNCTION(1, "CMDAT4")
),
- MTK_PIN(
- PINCTRL_PIN(50, "RDP1_A"),
+ MTK_PIN(PINCTRL_PIN(50, "RDP1_A"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 50),
MTK_FUNCTION(0, "GPIO50"),
MTK_FUNCTION(1, "CMDAT5")
),
- MTK_PIN(
- PINCTRL_PIN(51, "RCN_A"),
+ MTK_PIN(PINCTRL_PIN(51, "RCN_A"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 51),
MTK_FUNCTION(0, "GPIO51"),
MTK_FUNCTION(1, "CMDAT6")
),
- MTK_PIN(
- PINCTRL_PIN(52, "RCP_A"),
+ MTK_PIN(PINCTRL_PIN(52, "RCP_A"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 52),
MTK_FUNCTION(0, "GPIO52"),
MTK_FUNCTION(1, "CMDAT7")
),
- MTK_PIN(
- PINCTRL_PIN(53, "RDN2_A"),
+ MTK_PIN(PINCTRL_PIN(53, "RDN2_A"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 53),
MTK_FUNCTION(0, "GPIO53"),
MTK_FUNCTION(1, "CMDAT8"),
MTK_FUNCTION(2, "CMCSD3")
),
- MTK_PIN(
- PINCTRL_PIN(54, "RDP2_A"),
+ MTK_PIN(PINCTRL_PIN(54, "RDP2_A"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 54),
MTK_FUNCTION(0, "GPIO54"),
MTK_FUNCTION(1, "CMDAT9"),
MTK_FUNCTION(2, "CMCSD2")
),
- MTK_PIN(
- PINCTRL_PIN(55, "RDN3_A"),
+ MTK_PIN(PINCTRL_PIN(55, "RDN3_A"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 55),
MTK_FUNCTION(0, "GPIO55"),
MTK_FUNCTION(1, "CMHSYNC"),
MTK_FUNCTION(2, "CMCSD1")
),
- MTK_PIN(
- PINCTRL_PIN(56, "RDP3_A"),
+ MTK_PIN(PINCTRL_PIN(56, "RDP3_A"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 56),
MTK_FUNCTION(0, "GPIO56"),
MTK_FUNCTION(1, "CMVSYNC"),
MTK_FUNCTION(2, "CMCSD0")
),
- MTK_PIN(
- PINCTRL_PIN(57, "MSDC0_DAT0"),
+ MTK_PIN(PINCTRL_PIN(57, "MSDC0_DAT0"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 57),
MTK_FUNCTION(0, "GPIO57"),
@@ -521,8 +463,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(2, "I2S1_WS"),
MTK_FUNCTION(7, "DBG_MON_B_7_")
),
- MTK_PIN(
- PINCTRL_PIN(58, "MSDC0_DAT1"),
+ MTK_PIN(PINCTRL_PIN(58, "MSDC0_DAT1"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 58),
MTK_FUNCTION(0, "GPIO58"),
@@ -530,8 +471,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(2, "I2S1_BCK"),
MTK_FUNCTION(7, "DBG_MON_B_8_")
),
- MTK_PIN(
- PINCTRL_PIN(59, "MSDC0_DAT2"),
+ MTK_PIN(PINCTRL_PIN(59, "MSDC0_DAT2"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 59),
MTK_FUNCTION(0, "GPIO59"),
@@ -539,8 +479,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(2, "I2S1_MCK"),
MTK_FUNCTION(7, "DBG_MON_B_9_")
),
- MTK_PIN(
- PINCTRL_PIN(60, "MSDC0_DAT3"),
+ MTK_PIN(PINCTRL_PIN(60, "MSDC0_DAT3"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 60),
MTK_FUNCTION(0, "GPIO60"),
@@ -548,8 +487,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(2, "I2S1_DO_1"),
MTK_FUNCTION(7, "DBG_MON_B_10_")
),
- MTK_PIN(
- PINCTRL_PIN(61, "MSDC0_DAT4"),
+ MTK_PIN(PINCTRL_PIN(61, "MSDC0_DAT4"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 61),
MTK_FUNCTION(0, "GPIO61"),
@@ -557,8 +495,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(2, "I2S1_DO_2"),
MTK_FUNCTION(7, "DBG_MON_B_11_")
),
- MTK_PIN(
- PINCTRL_PIN(62, "MSDC0_DAT5"),
+ MTK_PIN(PINCTRL_PIN(62, "MSDC0_DAT5"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 62),
MTK_FUNCTION(0, "GPIO62"),
@@ -566,8 +503,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(2, "I2S2_WS"),
MTK_FUNCTION(7, "DBG_MON_B_12_")
),
- MTK_PIN(
- PINCTRL_PIN(63, "MSDC0_DAT6"),
+ MTK_PIN(PINCTRL_PIN(63, "MSDC0_DAT6"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 63),
MTK_FUNCTION(0, "GPIO63"),
@@ -575,8 +511,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(2, "I2S2_BCK"),
MTK_FUNCTION(7, "DBG_MON_B_13_")
),
- MTK_PIN(
- PINCTRL_PIN(64, "MSDC0_DAT7"),
+ MTK_PIN(PINCTRL_PIN(64, "MSDC0_DAT7"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 64),
MTK_FUNCTION(0, "GPIO64"),
@@ -584,16 +519,14 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(2, "I2S2_DI_1"),
MTK_FUNCTION(7, "DBG_MON_B_14_")
),
- MTK_PIN(
- PINCTRL_PIN(65, "MSDC0_CLK"),
+ MTK_PIN(PINCTRL_PIN(65, "MSDC0_CLK"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 65),
MTK_FUNCTION(0, "GPIO65"),
MTK_FUNCTION(1, "MSDC0_CLK"),
MTK_FUNCTION(7, "DBG_MON_B_16_")
),
- MTK_PIN(
- PINCTRL_PIN(66, "MSDC0_CMD"),
+ MTK_PIN(PINCTRL_PIN(66, "MSDC0_CMD"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 66),
MTK_FUNCTION(0, "GPIO66"),
@@ -601,16 +534,14 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(2, "I2S2_DI_2"),
MTK_FUNCTION(7, "DBG_MON_B_15_")
),
- MTK_PIN(
- PINCTRL_PIN(67, "MSDC0_DSL"),
+ MTK_PIN(PINCTRL_PIN(67, "MSDC0_DSL"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 67),
MTK_FUNCTION(0, "GPIO67"),
MTK_FUNCTION(1, "MSDC0_DSL"),
MTK_FUNCTION(7, "DBG_MON_B_17_")
),
- MTK_PIN(
- PINCTRL_PIN(68, "MSDC0_RST_"),
+ MTK_PIN(PINCTRL_PIN(68, "MSDC0_RST_"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 68),
MTK_FUNCTION(0, "GPIO68"),
@@ -618,8 +549,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(2, "I2S2_MCK"),
MTK_FUNCTION(7, "DBG_MON_B_18_")
),
- MTK_PIN(
- PINCTRL_PIN(69, "SPI_CK"),
+ MTK_PIN(PINCTRL_PIN(69, "SPI_CK"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 69),
MTK_FUNCTION(0, "GPIO69"),
@@ -630,8 +560,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(5, "I2S2_MCK"),
MTK_FUNCTION(7, "DBG_MON_B_19_")
),
- MTK_PIN(
- PINCTRL_PIN(70, "SPI_MI"),
+ MTK_PIN(PINCTRL_PIN(70, "SPI_MI"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 70),
MTK_FUNCTION(0, "GPIO70"),
@@ -643,8 +572,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(6, "DSI1_TE"),
MTK_FUNCTION(7, "DBG_MON_B_20_")
),
- MTK_PIN(
- PINCTRL_PIN(71, "SPI_MO"),
+ MTK_PIN(PINCTRL_PIN(71, "SPI_MO"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 71),
MTK_FUNCTION(0, "GPIO71"),
@@ -655,8 +583,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(5, "I2S2_DI_2"),
MTK_FUNCTION(7, "DBG_MON_B_21_")
),
- MTK_PIN(
- PINCTRL_PIN(72, "SPI_CS"),
+ MTK_PIN(PINCTRL_PIN(72, "SPI_CS"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 72),
MTK_FUNCTION(0, "GPIO72"),
@@ -667,116 +594,101 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(5, "DISP_PWM1"),
MTK_FUNCTION(7, "DBG_MON_B_22_")
),
- MTK_PIN(
- PINCTRL_PIN(73, "MSDC1_DAT0"),
+ MTK_PIN(PINCTRL_PIN(73, "MSDC1_DAT0"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 73),
MTK_FUNCTION(0, "GPIO73"),
MTK_FUNCTION(1, "MSDC1_DAT0"),
MTK_FUNCTION(7, "DBG_MON_B_24_")
),
- MTK_PIN(
- PINCTRL_PIN(74, "MSDC1_DAT1"),
+ MTK_PIN(PINCTRL_PIN(74, "MSDC1_DAT1"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 74),
MTK_FUNCTION(0, "GPIO74"),
MTK_FUNCTION(1, "MSDC1_DAT1"),
MTK_FUNCTION(7, "DBG_MON_B_25_")
),
- MTK_PIN(
- PINCTRL_PIN(75, "MSDC1_DAT2"),
+ MTK_PIN(PINCTRL_PIN(75, "MSDC1_DAT2"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 75),
MTK_FUNCTION(0, "GPIO75"),
MTK_FUNCTION(1, "MSDC1_DAT2"),
MTK_FUNCTION(7, "DBG_MON_B_26_")
),
- MTK_PIN(
- PINCTRL_PIN(76, "MSDC1_DAT3"),
+ MTK_PIN(PINCTRL_PIN(76, "MSDC1_DAT3"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 76),
MTK_FUNCTION(0, "GPIO76"),
MTK_FUNCTION(1, "MSDC1_DAT3"),
MTK_FUNCTION(7, "DBG_MON_B_27_")
),
- MTK_PIN(
- PINCTRL_PIN(77, "MSDC1_CLK"),
+ MTK_PIN(PINCTRL_PIN(77, "MSDC1_CLK"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 77),
MTK_FUNCTION(0, "GPIO77"),
MTK_FUNCTION(1, "MSDC1_CLK"),
MTK_FUNCTION(7, "DBG_MON_B_28_")
),
- MTK_PIN(
- PINCTRL_PIN(78, "MSDC1_CMD"),
+ MTK_PIN(PINCTRL_PIN(78, "MSDC1_CMD"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 78),
MTK_FUNCTION(0, "GPIO78"),
MTK_FUNCTION(1, "MSDC1_CMD"),
MTK_FUNCTION(7, "DBG_MON_B_23_")
),
- MTK_PIN(
- PINCTRL_PIN(79, "PWRAP_SPI0_MI"),
+ MTK_PIN(PINCTRL_PIN(79, "PWRAP_SPI0_MI"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 79),
MTK_FUNCTION(0, "GPIO79"),
MTK_FUNCTION(1, "PWRAP_SPIMI"),
MTK_FUNCTION(2, "PWRAP_SPIMO")
),
- MTK_PIN(
- PINCTRL_PIN(80, "PWRAP_SPI0_MO"),
+ MTK_PIN(PINCTRL_PIN(80, "PWRAP_SPI0_MO"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 80),
MTK_FUNCTION(0, "GPIO80"),
MTK_FUNCTION(1, "PWRAP_SPIMO"),
MTK_FUNCTION(2, "PWRAP_SPIMI")
),
- MTK_PIN(
- PINCTRL_PIN(81, "PWRAP_SPI0_CK"),
+ MTK_PIN(PINCTRL_PIN(81, "PWRAP_SPI0_CK"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 81),
MTK_FUNCTION(0, "GPIO81"),
MTK_FUNCTION(1, "PWRAP_SPICK")
),
- MTK_PIN(
- PINCTRL_PIN(82, "PWRAP_SPI0_CSN"),
+ MTK_PIN(PINCTRL_PIN(82, "PWRAP_SPI0_CSN"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 82),
MTK_FUNCTION(0, "GPIO82"),
MTK_FUNCTION(1, "PWRAP_SPICS")
),
- MTK_PIN(
- PINCTRL_PIN(83, "AUD_CLK_MOSI"),
+ MTK_PIN(PINCTRL_PIN(83, "AUD_CLK_MOSI"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 83),
MTK_FUNCTION(0, "GPIO83"),
MTK_FUNCTION(1, "AUD_CLK_MOSI")
),
- MTK_PIN(
- PINCTRL_PIN(84, "AUD_DAT_MISO"),
+ MTK_PIN(PINCTRL_PIN(84, "AUD_DAT_MISO"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 84),
MTK_FUNCTION(0, "GPIO84"),
MTK_FUNCTION(1, "AUD_DAT_MISO"),
MTK_FUNCTION(2, "AUD_DAT_MOSI")
),
- MTK_PIN(
- PINCTRL_PIN(85, "AUD_DAT_MOSI"),
+ MTK_PIN(PINCTRL_PIN(85, "AUD_DAT_MOSI"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 85),
MTK_FUNCTION(0, "GPIO85"),
MTK_FUNCTION(1, "AUD_DAT_MOSI"),
MTK_FUNCTION(2, "AUD_DAT_MISO")
),
- MTK_PIN(
- PINCTRL_PIN(86, "RTC32K_CK"),
+ MTK_PIN(PINCTRL_PIN(86, "RTC32K_CK"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 86),
MTK_FUNCTION(0, "GPIO86"),
MTK_FUNCTION(1, "RTC32K_CK")
),
- MTK_PIN(
- PINCTRL_PIN(87, "DISP_PWM0"),
+ MTK_PIN(PINCTRL_PIN(87, "DISP_PWM0"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 87),
MTK_FUNCTION(0, "GPIO87"),
@@ -784,36 +696,31 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(2, "DISP_PWM1"),
MTK_FUNCTION(7, "DBG_MON_B_31_")
),
- MTK_PIN(
- PINCTRL_PIN(88, "SRCLKENAI"),
+ MTK_PIN(PINCTRL_PIN(88, "SRCLKENAI"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 88),
MTK_FUNCTION(0, "GPIO88"),
MTK_FUNCTION(1, "SRCLKENAI")
),
- MTK_PIN(
- PINCTRL_PIN(89, "SRCLKENAI2"),
+ MTK_PIN(PINCTRL_PIN(89, "SRCLKENAI2"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 89),
MTK_FUNCTION(0, "GPIO89"),
MTK_FUNCTION(1, "SRCLKENAI2")
),
- MTK_PIN(
- PINCTRL_PIN(90, "SRCLKENA0"),
+ MTK_PIN(PINCTRL_PIN(90, "SRCLKENA0"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 90),
MTK_FUNCTION(0, "GPIO90"),
MTK_FUNCTION(1, "SRCLKENA0")
),
- MTK_PIN(
- PINCTRL_PIN(91, "SRCLKENA1"),
+ MTK_PIN(PINCTRL_PIN(91, "SRCLKENA1"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 91),
MTK_FUNCTION(0, "GPIO91"),
MTK_FUNCTION(1, "SRCLKENA1")
),
- MTK_PIN(
- PINCTRL_PIN(92, "PCM_CLK"),
+ MTK_PIN(PINCTRL_PIN(92, "PCM_CLK"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 92),
MTK_FUNCTION(0, "GPIO92"),
@@ -821,8 +728,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(2, "I2S0_BCK"),
MTK_FUNCTION(7, "DBG_MON_A_24_")
),
- MTK_PIN(
- PINCTRL_PIN(93, "PCM_SYNC"),
+ MTK_PIN(PINCTRL_PIN(93, "PCM_SYNC"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 93),
MTK_FUNCTION(0, "GPIO93"),
@@ -830,8 +736,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(2, "I2S0_WS"),
MTK_FUNCTION(7, "DBG_MON_A_25_")
),
- MTK_PIN(
- PINCTRL_PIN(94, "PCM_RX"),
+ MTK_PIN(PINCTRL_PIN(94, "PCM_RX"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 94),
MTK_FUNCTION(0, "GPIO94"),
@@ -839,8 +744,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(2, "I2S0_DI"),
MTK_FUNCTION(7, "DBG_MON_A_26_")
),
- MTK_PIN(
- PINCTRL_PIN(95, "PCM_TX"),
+ MTK_PIN(PINCTRL_PIN(95, "PCM_TX"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 95),
MTK_FUNCTION(0, "GPIO95"),
@@ -848,8 +752,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(2, "I2S0_DO"),
MTK_FUNCTION(7, "DBG_MON_A_27_")
),
- MTK_PIN(
- PINCTRL_PIN(96, "URXD1"),
+ MTK_PIN(PINCTRL_PIN(96, "URXD1"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 96),
MTK_FUNCTION(0, "GPIO96"),
@@ -857,8 +760,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(2, "UTXD1"),
MTK_FUNCTION(7, "DBG_MON_A_28_")
),
- MTK_PIN(
- PINCTRL_PIN(97, "UTXD1"),
+ MTK_PIN(PINCTRL_PIN(97, "UTXD1"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 97),
MTK_FUNCTION(0, "GPIO97"),
@@ -866,8 +768,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(2, "URXD1"),
MTK_FUNCTION(7, "DBG_MON_A_29_")
),
- MTK_PIN(
- PINCTRL_PIN(98, "URTS1"),
+ MTK_PIN(PINCTRL_PIN(98, "URTS1"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 98),
MTK_FUNCTION(0, "GPIO98"),
@@ -875,8 +776,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(2, "UCTS1"),
MTK_FUNCTION(7, "DBG_MON_A_30_")
),
- MTK_PIN(
- PINCTRL_PIN(99, "UCTS1"),
+ MTK_PIN(PINCTRL_PIN(99, "UCTS1"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 99),
MTK_FUNCTION(0, "GPIO99"),
@@ -884,8 +784,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(2, "URTS1"),
MTK_FUNCTION(7, "DBG_MON_A_31_")
),
- MTK_PIN(
- PINCTRL_PIN(100, "MSDC2_DAT0"),
+ MTK_PIN(PINCTRL_PIN(100, "MSDC2_DAT0"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 100),
MTK_FUNCTION(0, "GPIO100"),
@@ -895,8 +794,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(5, "USB_DRVVBUS_P1"),
MTK_FUNCTION(7, "DBG_MON_B_0_")
),
- MTK_PIN(
- PINCTRL_PIN(101, "MSDC2_DAT1"),
+ MTK_PIN(PINCTRL_PIN(101, "MSDC2_DAT1"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 101),
MTK_FUNCTION(0, "GPIO101"),
@@ -905,8 +803,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(4, "SCL5"),
MTK_FUNCTION(7, "DBG_MON_B_1_")
),
- MTK_PIN(
- PINCTRL_PIN(102, "MSDC2_DAT2"),
+ MTK_PIN(PINCTRL_PIN(102, "MSDC2_DAT2"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 102),
MTK_FUNCTION(0, "GPIO102"),
@@ -916,8 +813,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(6, "SPI_CK_1_"),
MTK_FUNCTION(7, "DBG_MON_B_2_")
),
- MTK_PIN(
- PINCTRL_PIN(103, "MSDC2_DAT3"),
+ MTK_PIN(PINCTRL_PIN(103, "MSDC2_DAT3"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 103),
MTK_FUNCTION(0, "GPIO103"),
@@ -927,8 +823,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(6, "SPI_MI_1_"),
MTK_FUNCTION(7, "DBG_MON_B_3_")
),
- MTK_PIN(
- PINCTRL_PIN(104, "MSDC2_CLK"),
+ MTK_PIN(PINCTRL_PIN(104, "MSDC2_CLK"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 104),
MTK_FUNCTION(0, "GPIO104"),
@@ -939,8 +834,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(6, "SPI_MO_1_"),
MTK_FUNCTION(7, "DBG_MON_B_4_")
),
- MTK_PIN(
- PINCTRL_PIN(105, "MSDC2_CMD"),
+ MTK_PIN(PINCTRL_PIN(105, "MSDC2_CMD"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 105),
MTK_FUNCTION(0, "GPIO105"),
@@ -951,22 +845,19 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(6, "SPI_CS_1_"),
MTK_FUNCTION(7, "DBG_MON_B_5_")
),
- MTK_PIN(
- PINCTRL_PIN(106, "SDA3"),
+ MTK_PIN(PINCTRL_PIN(106, "SDA3"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 106),
MTK_FUNCTION(0, "GPIO106"),
MTK_FUNCTION(1, "SDA3")
),
- MTK_PIN(
- PINCTRL_PIN(107, "SCL3"),
+ MTK_PIN(PINCTRL_PIN(107, "SCL3"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 107),
MTK_FUNCTION(0, "GPIO107"),
MTK_FUNCTION(1, "SCL3")
),
- MTK_PIN(
- PINCTRL_PIN(108, "JTMS"),
+ MTK_PIN(PINCTRL_PIN(108, "JTMS"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 108),
MTK_FUNCTION(0, "GPIO108"),
@@ -975,8 +866,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(5, "AP_MD32_JTAG_TMS"),
MTK_FUNCTION(6, "DFD_TMS")
),
- MTK_PIN(
- PINCTRL_PIN(109, "JTCK"),
+ MTK_PIN(PINCTRL_PIN(109, "JTCK"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 109),
MTK_FUNCTION(0, "GPIO109"),
@@ -985,8 +875,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(5, "AP_MD32_JTAG_TCK"),
MTK_FUNCTION(6, "DFD_TCK")
),
- MTK_PIN(
- PINCTRL_PIN(110, "JTDI"),
+ MTK_PIN(PINCTRL_PIN(110, "JTDI"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 110),
MTK_FUNCTION(0, "GPIO110"),
@@ -995,8 +884,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(5, "AP_MD32_JTAG_TDI"),
MTK_FUNCTION(6, "DFD_TDI")
),
- MTK_PIN(
- PINCTRL_PIN(111, "JTDO"),
+ MTK_PIN(PINCTRL_PIN(111, "JTDO"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 111),
MTK_FUNCTION(0, "GPIO111"),
@@ -1005,8 +893,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(5, "AP_MD32_JTAG_TDO"),
MTK_FUNCTION(6, "DFD_TDO")
),
- MTK_PIN(
- PINCTRL_PIN(112, "JTRST_B"),
+ MTK_PIN(PINCTRL_PIN(112, "JTRST_B"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 112),
MTK_FUNCTION(0, "GPIO112"),
@@ -1015,8 +902,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(5, "AP_MD32_JTAG_TRST"),
MTK_FUNCTION(6, "DFD_NTRST")
),
- MTK_PIN(
- PINCTRL_PIN(113, "URXD0"),
+ MTK_PIN(PINCTRL_PIN(113, "URXD0"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 113),
MTK_FUNCTION(0, "GPIO113"),
@@ -1025,8 +911,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(6, "I2S2_WS"),
MTK_FUNCTION(7, "DBG_MON_A_0_")
),
- MTK_PIN(
- PINCTRL_PIN(114, "UTXD0"),
+ MTK_PIN(PINCTRL_PIN(114, "UTXD0"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 114),
MTK_FUNCTION(0, "GPIO114"),
@@ -1035,8 +920,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(6, "I2S2_BCK"),
MTK_FUNCTION(7, "DBG_MON_A_1_")
),
- MTK_PIN(
- PINCTRL_PIN(115, "URTS0"),
+ MTK_PIN(PINCTRL_PIN(115, "URTS0"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 115),
MTK_FUNCTION(0, "GPIO115"),
@@ -1045,8 +929,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(6, "I2S2_MCK"),
MTK_FUNCTION(7, "DBG_MON_A_2_")
),
- MTK_PIN(
- PINCTRL_PIN(116, "UCTS0"),
+ MTK_PIN(PINCTRL_PIN(116, "UCTS0"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 116),
MTK_FUNCTION(0, "GPIO116"),
@@ -1055,8 +938,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(6, "I2S2_DI_1"),
MTK_FUNCTION(7, "DBG_MON_A_3_")
),
- MTK_PIN(
- PINCTRL_PIN(117, "URXD3"),
+ MTK_PIN(PINCTRL_PIN(117, "URXD3"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 117),
MTK_FUNCTION(0, "GPIO117"),
@@ -1064,8 +946,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(2, "UTXD3"),
MTK_FUNCTION(7, "DBG_MON_A_9_")
),
- MTK_PIN(
- PINCTRL_PIN(118, "UTXD3"),
+ MTK_PIN(PINCTRL_PIN(118, "UTXD3"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 118),
MTK_FUNCTION(0, "GPIO118"),
@@ -1073,16 +954,14 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(2, "URXD3"),
MTK_FUNCTION(7, "DBG_MON_A_10_")
),
- MTK_PIN(
- PINCTRL_PIN(119, "KPROW0"),
+ MTK_PIN(PINCTRL_PIN(119, "KPROW0"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 119),
MTK_FUNCTION(0, "GPIO119"),
MTK_FUNCTION(1, "KROW0"),
MTK_FUNCTION(7, "DBG_MON_A_11_")
),
- MTK_PIN(
- PINCTRL_PIN(120, "KPROW1"),
+ MTK_PIN(PINCTRL_PIN(120, "KPROW1"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 120),
MTK_FUNCTION(0, "GPIO120"),
@@ -1090,8 +969,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(3, "PWM6"),
MTK_FUNCTION(7, "DBG_MON_A_12_")
),
- MTK_PIN(
- PINCTRL_PIN(121, "KPROW2"),
+ MTK_PIN(PINCTRL_PIN(121, "KPROW2"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 121),
MTK_FUNCTION(0, "GPIO121"),
@@ -1102,16 +980,14 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(5, "USB_DRVVBUS_P1"),
MTK_FUNCTION(7, "DBG_MON_A_13_")
),
- MTK_PIN(
- PINCTRL_PIN(122, "KPCOL0"),
+ MTK_PIN(PINCTRL_PIN(122, "KPCOL0"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 122),
MTK_FUNCTION(0, "GPIO122"),
MTK_FUNCTION(1, "KCOL0"),
MTK_FUNCTION(7, "DBG_MON_A_14_")
),
- MTK_PIN(
- PINCTRL_PIN(123, "KPCOL1"),
+ MTK_PIN(PINCTRL_PIN(123, "KPCOL1"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 123),
MTK_FUNCTION(0, "GPIO123"),
@@ -1120,8 +996,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(3, "PWM5"),
MTK_FUNCTION(7, "DBG_MON_A_15_")
),
- MTK_PIN(
- PINCTRL_PIN(124, "KPCOL2"),
+ MTK_PIN(PINCTRL_PIN(124, "KPCOL2"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 124),
MTK_FUNCTION(0, "GPIO124"),
@@ -1132,29 +1007,25 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(5, "USB_DRVVBUS_P1"),
MTK_FUNCTION(7, "DBG_MON_A_16_")
),
- MTK_PIN(
- PINCTRL_PIN(125, "SDA1"),
+ MTK_PIN(PINCTRL_PIN(125, "SDA1"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 125),
MTK_FUNCTION(0, "GPIO125"),
MTK_FUNCTION(1, "SDA1")
),
- MTK_PIN(
- PINCTRL_PIN(126, "SCL1"),
+ MTK_PIN(PINCTRL_PIN(126, "SCL1"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 126),
MTK_FUNCTION(0, "GPIO126"),
MTK_FUNCTION(1, "SCL1")
),
- MTK_PIN(
- PINCTRL_PIN(127, "LCM_RST"),
+ MTK_PIN(PINCTRL_PIN(127, "LCM_RST"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 127),
MTK_FUNCTION(0, "GPIO127"),
MTK_FUNCTION(1, "LCM_RST")
),
- MTK_PIN(
- PINCTRL_PIN(128, "I2S0_LRCK"),
+ MTK_PIN(PINCTRL_PIN(128, "I2S0_LRCK"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 128),
MTK_FUNCTION(0, "GPIO128"),
@@ -1164,8 +1035,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(5, "SPI_CK_2_"),
MTK_FUNCTION(7, "DBG_MON_A_4_")
),
- MTK_PIN(
- PINCTRL_PIN(129, "I2S0_BCK"),
+ MTK_PIN(PINCTRL_PIN(129, "I2S0_BCK"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 129),
MTK_FUNCTION(0, "GPIO129"),
@@ -1175,8 +1045,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(5, "SPI_MI_2_"),
MTK_FUNCTION(7, "DBG_MON_A_5_")
),
- MTK_PIN(
- PINCTRL_PIN(130, "I2S0_MCK"),
+ MTK_PIN(PINCTRL_PIN(130, "I2S0_MCK"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 130),
MTK_FUNCTION(0, "GPIO130"),
@@ -1186,8 +1055,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(5, "SPI_MO_2_"),
MTK_FUNCTION(7, "DBG_MON_A_6_")
),
- MTK_PIN(
- PINCTRL_PIN(131, "I2S0_DATA0"),
+ MTK_PIN(PINCTRL_PIN(131, "I2S0_DATA0"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 131),
MTK_FUNCTION(0, "GPIO131"),
@@ -1197,8 +1065,7 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(5, "SPI_CS_2_"),
MTK_FUNCTION(7, "DBG_MON_A_7_")
),
- MTK_PIN(
- PINCTRL_PIN(132, "I2S0_DATA1"),
+ MTK_PIN(PINCTRL_PIN(132, "I2S0_DATA1"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 132),
MTK_FUNCTION(0, "GPIO132"),
@@ -1207,15 +1074,13 @@ static const struct mtk_desc_pin mtk_pins_mt8173[] = {
MTK_FUNCTION(3, "I2S2_DI_2"),
MTK_FUNCTION(7, "DBG_MON_A_8_")
),
- MTK_PIN(
- PINCTRL_PIN(133, "SDA4"),
+ MTK_PIN(PINCTRL_PIN(133, "SDA4"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 133),
MTK_FUNCTION(0, "GPIO133"),
MTK_FUNCTION(1, "SDA4")
),
- MTK_PIN(
- PINCTRL_PIN(134, "SCL4"),
+ MTK_PIN(PINCTRL_PIN(134, "SCL4"),
NULL, "mt8173",
MTK_EINT_FUNCTION(0, 134),
MTK_FUNCTION(0, "GPIO134"),
diff --git a/drivers/pinctrl/meson/pinctrl-meson8b.c b/drivers/pinctrl/meson/pinctrl-meson8b.c
index 5bd808dc81e1..bb2a30964fc6 100644
--- a/drivers/pinctrl/meson/pinctrl-meson8b.c
+++ b/drivers/pinctrl/meson/pinctrl-meson8b.c
@@ -884,20 +884,24 @@ static struct meson_pmx_func meson8b_aobus_functions[] = {
};
static struct meson_bank meson8b_cbus_banks[] = {
- /* name first last irq pullen pull dir out in */
- BANK("X", GPIOX_0, GPIOX_21, 97, 118, 4, 0, 4, 0, 0, 0, 1, 0, 2, 0),
- BANK("Y", GPIOY_0, GPIOY_14, 80, 96, 3, 0, 3, 0, 3, 0, 4, 0, 5, 0),
- BANK("DV", GPIODV_9, GPIODV_29, 59, 79, 0, 0, 0, 0, 7, 0, 8, 0, 9, 0),
- BANK("H", GPIOH_0, GPIOH_9, 14, 23, 1, 16, 1, 16, 9, 19, 10, 19, 11, 19),
- BANK("CARD", CARD_0, CARD_6, 43, 49, 2, 20, 2, 20, 0, 22, 1, 22, 2, 22),
- BANK("BOOT", BOOT_0, BOOT_18, 24, 42, 2, 0, 2, 0, 9, 0, 10, 0, 11, 0),
+ /* name first last irq pullen pull dir out in */
+ BANK("X0..11", GPIOX_0, GPIOX_11, 97, 108, 4, 0, 4, 0, 0, 0, 1, 0, 2, 0),
+ BANK("X16..21", GPIOX_16, GPIOX_21, 113, 118, 4, 16, 4, 16, 0, 16, 1, 16, 2, 16),
+ BANK("Y0..1", GPIOY_0, GPIOY_1, 80, 81, 3, 0, 3, 0, 3, 0, 4, 0, 5, 0),
+ BANK("Y3", GPIOY_3, GPIOY_3, 83, 83, 3, 3, 3, 3, 3, 3, 4, 3, 5, 3),
+ BANK("Y6..14", GPIOY_6, GPIOY_14, 86, 94, 3, 6, 3, 6, 3, 6, 4, 6, 5, 6),
+ BANK("DV9", GPIODV_9, GPIODV_9, 59, 59, 0, 9, 0, 9, 7, 9, 8, 9, 9, 9),
+ BANK("DV24..29", GPIODV_24, GPIODV_29, 74, 79, 0, 24, 0, 24, 7, 24, 8, 24, 9, 24),
+ BANK("H", GPIOH_0, GPIOH_9, 14, 23, 1, 16, 1, 16, 9, 19, 10, 19, 11, 19),
+ BANK("CARD", CARD_0, CARD_6, 43, 49, 2, 20, 2, 20, 0, 22, 1, 22, 2, 22),
+ BANK("BOOT", BOOT_0, BOOT_18, 24, 42, 2, 0, 2, 0, 9, 0, 10, 0, 11, 0),
/*
* The following bank is not mentionned in the public datasheet
* There is no information whether it can be used with the gpio
* interrupt controller
*/
- BANK("DIF", DIF_0_P, DIF_4_N, -1, -1, 5, 8, 5, 8, 12, 12, 13, 12, 14, 12),
+ BANK("DIF", DIF_0_P, DIF_4_N, -1, -1, 5, 8, 5, 8, 12, 12, 13, 12, 14, 12),
};
static struct meson_bank meson8b_aobus_banks[] = {
diff --git a/drivers/pinctrl/nomadik/Kconfig b/drivers/pinctrl/nomadik/Kconfig
index f4fcebfce68c..c3efe7d7e91f 100644
--- a/drivers/pinctrl/nomadik/Kconfig
+++ b/drivers/pinctrl/nomadik/Kconfig
@@ -11,14 +11,6 @@ config PINCTRL_AB8500
bool "AB8500 pin controller driver"
depends on PINCTRL_ABX500 && ARCH_U8500
-config PINCTRL_AB8540
- bool "AB8540 pin controller driver"
- depends on PINCTRL_ABX500 && ARCH_U8500
-
-config PINCTRL_AB9540
- bool "AB9540 pin controller driver"
- depends on PINCTRL_ABX500 && ARCH_U8500
-
config PINCTRL_AB8505
bool "AB8505 pin controller driver"
depends on PINCTRL_ABX500 && ARCH_U8500
@@ -44,8 +36,4 @@ config PINCTRL_DB8500
bool "DB8500 pin controller driver"
depends on PINCTRL_NOMADIK && ARCH_U8500
-config PINCTRL_DB8540
- bool "DB8540 pin controller driver"
- depends on PINCTRL_NOMADIK && ARCH_U8500
-
endif
diff --git a/drivers/pinctrl/nomadik/Makefile b/drivers/pinctrl/nomadik/Makefile
index bf8b7517ee4a..dd10d49daf80 100644
--- a/drivers/pinctrl/nomadik/Makefile
+++ b/drivers/pinctrl/nomadik/Makefile
@@ -2,10 +2,7 @@
# Nomadik family pin control drivers
obj-$(CONFIG_PINCTRL_ABX500) += pinctrl-abx500.o
obj-$(CONFIG_PINCTRL_AB8500) += pinctrl-ab8500.o
-obj-$(CONFIG_PINCTRL_AB8540) += pinctrl-ab8540.o
-obj-$(CONFIG_PINCTRL_AB9540) += pinctrl-ab9540.o
obj-$(CONFIG_PINCTRL_AB8505) += pinctrl-ab8505.o
obj-$(CONFIG_PINCTRL_NOMADIK) += pinctrl-nomadik.o
obj-$(CONFIG_PINCTRL_STN8815) += pinctrl-nomadik-stn8815.o
obj-$(CONFIG_PINCTRL_DB8500) += pinctrl-nomadik-db8500.o
-obj-$(CONFIG_PINCTRL_DB8540) += pinctrl-nomadik-db8540.o
diff --git a/drivers/pinctrl/nomadik/pinctrl-ab8540.c b/drivers/pinctrl/nomadik/pinctrl-ab8540.c
deleted file mode 100644
index 9867535d49c1..000000000000
--- a/drivers/pinctrl/nomadik/pinctrl-ab8540.c
+++ /dev/null
@@ -1,408 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson SA 2012
- *
- * Author: Patrice Chotard <patrice.chotard@stericsson.com> for ST-Ericsson.
- *
- * 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.
- */
-
-#include <linux/kernel.h>
-#include <linux/gpio.h>
-#include <linux/pinctrl/pinctrl.h>
-#include <linux/mfd/abx500/ab8500.h>
-#include "pinctrl-abx500.h"
-
-/* All the pins that can be used for GPIO and some other functions */
-#define ABX500_GPIO(offset) (offset)
-
-#define AB8540_PIN_J16 ABX500_GPIO(1)
-#define AB8540_PIN_D17 ABX500_GPIO(2)
-#define AB8540_PIN_C12 ABX500_GPIO(3)
-#define AB8540_PIN_G12 ABX500_GPIO(4)
-/* hole */
-#define AB8540_PIN_D16 ABX500_GPIO(14)
-#define AB8540_PIN_F15 ABX500_GPIO(15)
-#define AB8540_PIN_J8 ABX500_GPIO(16)
-#define AB8540_PIN_K16 ABX500_GPIO(17)
-#define AB8540_PIN_G15 ABX500_GPIO(18)
-#define AB8540_PIN_F17 ABX500_GPIO(19)
-#define AB8540_PIN_E17 ABX500_GPIO(20)
-/* hole */
-#define AB8540_PIN_AA16 ABX500_GPIO(27)
-#define AB8540_PIN_W18 ABX500_GPIO(28)
-#define AB8540_PIN_Y15 ABX500_GPIO(29)
-#define AB8540_PIN_W16 ABX500_GPIO(30)
-#define AB8540_PIN_V15 ABX500_GPIO(31)
-#define AB8540_PIN_W17 ABX500_GPIO(32)
-/* hole */
-#define AB8540_PIN_D12 ABX500_GPIO(42)
-#define AB8540_PIN_P4 ABX500_GPIO(43)
-#define AB8540_PIN_AB1 ABX500_GPIO(44)
-#define AB8540_PIN_K7 ABX500_GPIO(45)
-#define AB8540_PIN_L7 ABX500_GPIO(46)
-#define AB8540_PIN_G10 ABX500_GPIO(47)
-#define AB8540_PIN_K12 ABX500_GPIO(48)
-/* hole */
-#define AB8540_PIN_N8 ABX500_GPIO(51)
-#define AB8540_PIN_P12 ABX500_GPIO(52)
-#define AB8540_PIN_K8 ABX500_GPIO(53)
-#define AB8540_PIN_J11 ABX500_GPIO(54)
-#define AB8540_PIN_AC2 ABX500_GPIO(55)
-#define AB8540_PIN_AB2 ABX500_GPIO(56)
-
-/* indicates the highest GPIO number */
-#define AB8540_GPIO_MAX_NUMBER 56
-
-/*
- * The names of the pins are denoted by GPIO number and ball name, even
- * though they can be used for other things than GPIO, this is the first
- * column in the table of the data sheet and often used on schematics and
- * such.
- */
-static const struct pinctrl_pin_desc ab8540_pins[] = {
- PINCTRL_PIN(AB8540_PIN_J16, "GPIO1_J16"),
- PINCTRL_PIN(AB8540_PIN_D17, "GPIO2_D17"),
- PINCTRL_PIN(AB8540_PIN_C12, "GPIO3_C12"),
- PINCTRL_PIN(AB8540_PIN_G12, "GPIO4_G12"),
- /* hole */
- PINCTRL_PIN(AB8540_PIN_D16, "GPIO14_D16"),
- PINCTRL_PIN(AB8540_PIN_F15, "GPIO15_F15"),
- PINCTRL_PIN(AB8540_PIN_J8, "GPIO16_J8"),
- PINCTRL_PIN(AB8540_PIN_K16, "GPIO17_K16"),
- PINCTRL_PIN(AB8540_PIN_G15, "GPIO18_G15"),
- PINCTRL_PIN(AB8540_PIN_F17, "GPIO19_F17"),
- PINCTRL_PIN(AB8540_PIN_E17, "GPIO20_E17"),
- /* hole */
- PINCTRL_PIN(AB8540_PIN_AA16, "GPIO27_AA16"),
- PINCTRL_PIN(AB8540_PIN_W18, "GPIO28_W18"),
- PINCTRL_PIN(AB8540_PIN_Y15, "GPIO29_Y15"),
- PINCTRL_PIN(AB8540_PIN_W16, "GPIO30_W16"),
- PINCTRL_PIN(AB8540_PIN_V15, "GPIO31_V15"),
- PINCTRL_PIN(AB8540_PIN_W17, "GPIO32_W17"),
- /* hole */
- PINCTRL_PIN(AB8540_PIN_D12, "GPIO42_D12"),
- PINCTRL_PIN(AB8540_PIN_P4, "GPIO43_P4"),
- PINCTRL_PIN(AB8540_PIN_AB1, "GPIO44_AB1"),
- PINCTRL_PIN(AB8540_PIN_K7, "GPIO45_K7"),
- PINCTRL_PIN(AB8540_PIN_L7, "GPIO46_L7"),
- PINCTRL_PIN(AB8540_PIN_G10, "GPIO47_G10"),
- PINCTRL_PIN(AB8540_PIN_K12, "GPIO48_K12"),
- /* hole */
- PINCTRL_PIN(AB8540_PIN_N8, "GPIO51_N8"),
- PINCTRL_PIN(AB8540_PIN_P12, "GPIO52_P12"),
- PINCTRL_PIN(AB8540_PIN_K8, "GPIO53_K8"),
- PINCTRL_PIN(AB8540_PIN_J11, "GPIO54_J11"),
- PINCTRL_PIN(AB8540_PIN_AC2, "GPIO55_AC2"),
- PINCTRL_PIN(AB8540_PIN_AB2, "GPIO56_AB2"),
-};
-
-/*
- * Maps local GPIO offsets to local pin numbers
- */
-static const struct abx500_pinrange ab8540_pinranges[] = {
- ABX500_PINRANGE(1, 4, ABX500_ALT_A),
- ABX500_PINRANGE(14, 7, ABX500_ALT_A),
- ABX500_PINRANGE(27, 6, ABX500_ALT_A),
- ABX500_PINRANGE(42, 7, ABX500_ALT_A),
- ABX500_PINRANGE(51, 6, ABX500_ALT_A),
-};
-
-/*
- * Read the pin group names like this:
- * sysclkreq2_d_1 = first groups of pins for sysclkreq2 on default function
- *
- * The groups are arranged as sets per altfunction column, so we can
- * mux in one group at a time by selecting the same altfunction for them
- * all. When functions require pins on different altfunctions, you need
- * to combine several groups.
- */
-
-/* default column */
-static const unsigned sysclkreq2_d_1_pins[] = { AB8540_PIN_J16 };
-static const unsigned sysclkreq3_d_1_pins[] = { AB8540_PIN_D17 };
-static const unsigned sysclkreq4_d_1_pins[] = { AB8540_PIN_C12 };
-static const unsigned sysclkreq6_d_1_pins[] = { AB8540_PIN_G12 };
-static const unsigned pwmout1_d_1_pins[] = { AB8540_PIN_D16 };
-static const unsigned pwmout2_d_1_pins[] = { AB8540_PIN_F15 };
-static const unsigned pwmout3_d_1_pins[] = { AB8540_PIN_J8 };
-
-/* audio data interface 1*/
-static const unsigned adi1_d_1_pins[] = { AB8540_PIN_K16, AB8540_PIN_G15,
- AB8540_PIN_F17, AB8540_PIN_E17 };
-/* Digital microphone 1 and 2 */
-static const unsigned dmic12_d_1_pins[] = { AB8540_PIN_AA16, AB8540_PIN_W18 };
-/* Digital microphone 3 and 4 */
-static const unsigned dmic34_d_1_pins[] = { AB8540_PIN_Y15, AB8540_PIN_W16 };
-/* Digital microphone 5 and 6 */
-static const unsigned dmic56_d_1_pins[] = { AB8540_PIN_V15, AB8540_PIN_W17 };
-static const unsigned sysclkreq5_d_1_pins[] = { AB8540_PIN_D12 };
-static const unsigned batremn_d_1_pins[] = { AB8540_PIN_P4 };
-static const unsigned service_d_1_pins[] = { AB8540_PIN_AB1 };
-static const unsigned pwrctrl0_d_1_pins[] = { AB8540_PIN_K7 };
-static const unsigned pwrctrl1_d_1_pins[] = { AB8540_PIN_L7 };
-static const unsigned pwmextvibra1_d_1_pins[] = { AB8540_PIN_G10 };
-static const unsigned pwmextvibra2_d_1_pins[] = { AB8540_PIN_K12 };
-static const unsigned gpio1_vbat_d_1_pins[] = { AB8540_PIN_N8 };
-static const unsigned gpio2_vbat_d_1_pins[] = { AB8540_PIN_P12 };
-static const unsigned gpio3_vbat_d_1_pins[] = { AB8540_PIN_K8 };
-static const unsigned gpio4_vbat_d_1_pins[] = { AB8540_PIN_J11 };
-static const unsigned pdmclkdat_d_1_pins[] = { AB8540_PIN_AC2, AB8540_PIN_AB2 };
-
-/* Altfunction A column */
-static const unsigned gpio1_a_1_pins[] = { AB8540_PIN_J16 };
-static const unsigned gpio2_a_1_pins[] = { AB8540_PIN_D17 };
-static const unsigned gpio3_a_1_pins[] = { AB8540_PIN_C12 };
-static const unsigned gpio4_a_1_pins[] = { AB8540_PIN_G12 };
-static const unsigned gpio14_a_1_pins[] = { AB8540_PIN_D16 };
-static const unsigned gpio15_a_1_pins[] = { AB8540_PIN_F15 };
-static const unsigned gpio16_a_1_pins[] = { AB8540_PIN_J8 };
-static const unsigned gpio17_a_1_pins[] = { AB8540_PIN_K16 };
-static const unsigned gpio18_a_1_pins[] = { AB8540_PIN_G15 };
-static const unsigned gpio19_a_1_pins[] = { AB8540_PIN_F17 };
-static const unsigned gpio20_a_1_pins[] = { AB8540_PIN_E17 };
-static const unsigned gpio27_a_1_pins[] = { AB8540_PIN_AA16 };
-static const unsigned gpio28_a_1_pins[] = { AB8540_PIN_W18 };
-static const unsigned gpio29_a_1_pins[] = { AB8540_PIN_Y15 };
-static const unsigned gpio30_a_1_pins[] = { AB8540_PIN_W16 };
-static const unsigned gpio31_a_1_pins[] = { AB8540_PIN_V15 };
-static const unsigned gpio32_a_1_pins[] = { AB8540_PIN_W17 };
-static const unsigned gpio42_a_1_pins[] = { AB8540_PIN_D12 };
-static const unsigned gpio43_a_1_pins[] = { AB8540_PIN_P4 };
-static const unsigned gpio44_a_1_pins[] = { AB8540_PIN_AB1 };
-static const unsigned gpio45_a_1_pins[] = { AB8540_PIN_K7 };
-static const unsigned gpio46_a_1_pins[] = { AB8540_PIN_L7 };
-static const unsigned gpio47_a_1_pins[] = { AB8540_PIN_G10 };
-static const unsigned gpio48_a_1_pins[] = { AB8540_PIN_K12 };
-static const unsigned gpio51_a_1_pins[] = { AB8540_PIN_N8 };
-static const unsigned gpio52_a_1_pins[] = { AB8540_PIN_P12 };
-static const unsigned gpio53_a_1_pins[] = { AB8540_PIN_K8 };
-static const unsigned gpio54_a_1_pins[] = { AB8540_PIN_J11 };
-static const unsigned gpio55_a_1_pins[] = { AB8540_PIN_AC2 };
-static const unsigned gpio56_a_1_pins[] = { AB8540_PIN_AB2 };
-
-#define AB8540_PIN_GROUP(a, b) { .name = #a, .pins = a##_pins, \
- .npins = ARRAY_SIZE(a##_pins), .altsetting = b }
-
-static const struct abx500_pingroup ab8540_groups[] = {
- /* default column */
- AB8540_PIN_GROUP(sysclkreq2_d_1, ABX500_DEFAULT),
- AB8540_PIN_GROUP(sysclkreq3_d_1, ABX500_DEFAULT),
- AB8540_PIN_GROUP(sysclkreq4_d_1, ABX500_DEFAULT),
- AB8540_PIN_GROUP(sysclkreq6_d_1, ABX500_DEFAULT),
- AB8540_PIN_GROUP(pwmout1_d_1, ABX500_DEFAULT),
- AB8540_PIN_GROUP(pwmout2_d_1, ABX500_DEFAULT),
- AB8540_PIN_GROUP(pwmout3_d_1, ABX500_DEFAULT),
- AB8540_PIN_GROUP(adi1_d_1, ABX500_DEFAULT),
- AB8540_PIN_GROUP(dmic12_d_1, ABX500_DEFAULT),
- AB8540_PIN_GROUP(dmic34_d_1, ABX500_DEFAULT),
- AB8540_PIN_GROUP(dmic56_d_1, ABX500_DEFAULT),
- AB8540_PIN_GROUP(sysclkreq5_d_1, ABX500_DEFAULT),
- AB8540_PIN_GROUP(batremn_d_1, ABX500_DEFAULT),
- AB8540_PIN_GROUP(service_d_1, ABX500_DEFAULT),
- AB8540_PIN_GROUP(pwrctrl0_d_1, ABX500_DEFAULT),
- AB8540_PIN_GROUP(pwrctrl1_d_1, ABX500_DEFAULT),
- AB8540_PIN_GROUP(pwmextvibra1_d_1, ABX500_DEFAULT),
- AB8540_PIN_GROUP(pwmextvibra2_d_1, ABX500_DEFAULT),
- AB8540_PIN_GROUP(gpio1_vbat_d_1, ABX500_DEFAULT),
- AB8540_PIN_GROUP(gpio2_vbat_d_1, ABX500_DEFAULT),
- AB8540_PIN_GROUP(gpio3_vbat_d_1, ABX500_DEFAULT),
- AB8540_PIN_GROUP(gpio4_vbat_d_1, ABX500_DEFAULT),
- AB8540_PIN_GROUP(pdmclkdat_d_1, ABX500_DEFAULT),
- /* Altfunction A column */
- AB8540_PIN_GROUP(gpio1_a_1, ABX500_ALT_A),
- AB8540_PIN_GROUP(gpio2_a_1, ABX500_ALT_A),
- AB8540_PIN_GROUP(gpio3_a_1, ABX500_ALT_A),
- AB8540_PIN_GROUP(gpio4_a_1, ABX500_ALT_A),
- AB8540_PIN_GROUP(gpio14_a_1, ABX500_ALT_A),
- AB8540_PIN_GROUP(gpio15_a_1, ABX500_ALT_A),
- AB8540_PIN_GROUP(gpio16_a_1, ABX500_ALT_A),
- AB8540_PIN_GROUP(gpio17_a_1, ABX500_ALT_A),
- AB8540_PIN_GROUP(gpio18_a_1, ABX500_ALT_A),
- AB8540_PIN_GROUP(gpio19_a_1, ABX500_ALT_A),
- AB8540_PIN_GROUP(gpio20_a_1, ABX500_ALT_A),
- AB8540_PIN_GROUP(gpio27_a_1, ABX500_ALT_A),
- AB8540_PIN_GROUP(gpio28_a_1, ABX500_ALT_A),
- AB8540_PIN_GROUP(gpio29_a_1, ABX500_ALT_A),
- AB8540_PIN_GROUP(gpio30_a_1, ABX500_ALT_A),
- AB8540_PIN_GROUP(gpio31_a_1, ABX500_ALT_A),
- AB8540_PIN_GROUP(gpio32_a_1, ABX500_ALT_A),
- AB8540_PIN_GROUP(gpio42_a_1, ABX500_ALT_A),
- AB8540_PIN_GROUP(gpio43_a_1, ABX500_ALT_A),
- AB8540_PIN_GROUP(gpio44_a_1, ABX500_ALT_A),
- AB8540_PIN_GROUP(gpio45_a_1, ABX500_ALT_A),
- AB8540_PIN_GROUP(gpio46_a_1, ABX500_ALT_A),
- AB8540_PIN_GROUP(gpio47_a_1, ABX500_ALT_A),
- AB8540_PIN_GROUP(gpio48_a_1, ABX500_ALT_A),
- AB8540_PIN_GROUP(gpio51_a_1, ABX500_ALT_A),
- AB8540_PIN_GROUP(gpio52_a_1, ABX500_ALT_A),
- AB8540_PIN_GROUP(gpio53_a_1, ABX500_ALT_A),
- AB8540_PIN_GROUP(gpio54_a_1, ABX500_ALT_A),
- AB8540_PIN_GROUP(gpio55_a_1, ABX500_ALT_A),
- AB8540_PIN_GROUP(gpio56_a_1, ABX500_ALT_A),
-};
-
-/* We use this macro to define the groups applicable to a function */
-#define AB8540_FUNC_GROUPS(a, b...) \
-static const char * const a##_groups[] = { b };
-
-AB8540_FUNC_GROUPS(sysclkreq, "sysclkreq2_d_1", "sysclkreq3_d_1",
- "sysclkreq4_d_1", "sysclkreq5_d_1", "sysclkreq6_d_1");
-AB8540_FUNC_GROUPS(gpio, "gpio1_a_1", "gpio2_a_1", "gpio3_a_1", "gpio4_a_1",
- "gpio14_a_1", "gpio15_a_1", "gpio16_a_1", "gpio17_a_1",
- "gpio18_a_1", "gpio19_a_1", "gpio20_a_1", "gpio27_a_1",
- "gpio28_a_1", "gpio29_a_1", "gpio30_a_1", "gpio31_a_1",
- "gpio32_a_1", "gpio42_a_1", "gpio43_a_1", "gpio44_a_1",
- "gpio45_a_1", "gpio46_a_1", "gpio47_a_1", "gpio48_a_1",
- "gpio51_a_1", "gpio52_a_1", "gpio53_a_1", "gpio54_a_1",
- "gpio55_a_1", "gpio56_a_1");
-AB8540_FUNC_GROUPS(pwmout, "pwmout1_d_1", "pwmout2_d_1", "pwmout3_d_1");
-AB8540_FUNC_GROUPS(adi1, "adi1_d_1");
-AB8540_FUNC_GROUPS(dmic, "dmic12_d_1", "dmic34_d_1", "dmic56_d_1");
-AB8540_FUNC_GROUPS(batremn, "batremn_d_1");
-AB8540_FUNC_GROUPS(service, "service_d_1");
-AB8540_FUNC_GROUPS(pwrctrl, "pwrctrl0_d_1", "pwrctrl1_d_1");
-AB8540_FUNC_GROUPS(pwmextvibra, "pwmextvibra1_d_1", "pwmextvibra2_d_1");
-AB8540_FUNC_GROUPS(gpio_vbat, "gpio1_vbat_d_1", "gpio2_vbat_d_1",
- "gpio3_vbat_d_1", "gpio4_vbat_d_1");
-AB8540_FUNC_GROUPS(pdm, "pdmclkdat_d_1");
-
-#define FUNCTION(fname) \
- { \
- .name = #fname, \
- .groups = fname##_groups, \
- .ngroups = ARRAY_SIZE(fname##_groups), \
- }
-
-static const struct abx500_function ab8540_functions[] = {
- FUNCTION(sysclkreq),
- FUNCTION(gpio),
- FUNCTION(pwmout),
- FUNCTION(adi1),
- FUNCTION(dmic),
- FUNCTION(batremn),
- FUNCTION(service),
- FUNCTION(pwrctrl),
- FUNCTION(pwmextvibra),
- FUNCTION(gpio_vbat),
- FUNCTION(pdm),
-};
-
-/*
- * this table translates what's is in the AB8540 specification regarding the
- * balls alternate functions (as for DB, default, ALT_A, ALT_B and ALT_C).
- * ALTERNATE_FUNCTIONS(GPIO_NUMBER, GPIOSEL bit, ALTERNATFUNC bit1,
- * ALTERNATEFUNC bit2, ALTA val, ALTB val, ALTC val),
- * AB8540 only supports DEFAULT and ALTA functions, so ALTERNATFUNC
- * registers is not used
- *
- */
-
-static struct
-alternate_functions ab8540_alternate_functions[AB8540_GPIO_MAX_NUMBER + 1] = {
- /* GPIOSEL1 - bit 4-7 reserved */
- ALTERNATE_FUNCTIONS(0, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO0 */
- ALTERNATE_FUNCTIONS(1, 0, UNUSED, UNUSED, 0, 0, 0), /* GPIO1, altA controlled by bit 0 */
- ALTERNATE_FUNCTIONS(2, 1, UNUSED, UNUSED, 0, 0, 0), /* GPIO2, altA controlled by bit 1 */
- ALTERNATE_FUNCTIONS(3, 2, UNUSED, UNUSED, 0, 0, 0), /* GPIO3, altA controlled by bit 2*/
- ALTERNATE_FUNCTIONS(4, 3, UNUSED, UNUSED, 0, 0, 0), /* GPIO4, altA controlled by bit 3*/
- ALTERNATE_FUNCTIONS(5, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO5 */
- ALTERNATE_FUNCTIONS(6, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO6 */
- ALTERNATE_FUNCTIONS(7, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO7 */
- ALTERNATE_FUNCTIONS(8, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO8 */
- /* GPIOSEL2 - bit 0-4 reserved */
- ALTERNATE_FUNCTIONS(9, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO9 */
- ALTERNATE_FUNCTIONS(10, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO10 */
- ALTERNATE_FUNCTIONS(11, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO11 */
- ALTERNATE_FUNCTIONS(12, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO12 */
- ALTERNATE_FUNCTIONS(13, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO13 */
- ALTERNATE_FUNCTIONS(14, 5, UNUSED, UNUSED, 0, 0, 0), /* GPIO14, altA controlled by bit 5 */
- ALTERNATE_FUNCTIONS(15, 6, UNUSED, UNUSED, 0, 0, 0), /* GPIO15, altA controlled by bit 6 */
- ALTERNATE_FUNCTIONS(16, 7, UNUSED, UNUSED, 0, 0, 0), /* GPIO16, altA controlled by bit 7 */
- /* GPIOSEL3 - bit 4-7 reserved */
- ALTERNATE_FUNCTIONS(17, 0, UNUSED, UNUSED, 0, 0, 0), /* GPIO17, altA controlled by bit 0 */
- ALTERNATE_FUNCTIONS(18, 1, UNUSED, UNUSED, 0, 0, 0), /* GPIO18, altA controlled by bit 1 */
- ALTERNATE_FUNCTIONS(19, 2, UNUSED, UNUSED, 0, 0, 0), /* GPIO19, altA controlled by bit 2 */
- ALTERNATE_FUNCTIONS(20, 3, UNUSED, UNUSED, 0, 0, 0), /* GPIO20, altA controlled by bit 3 */
- ALTERNATE_FUNCTIONS(21, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO21 */
- ALTERNATE_FUNCTIONS(22, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO22 */
- ALTERNATE_FUNCTIONS(23, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO23 */
- ALTERNATE_FUNCTIONS(24, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO24 */
- /* GPIOSEL4 - bit 0-1 reserved */
- ALTERNATE_FUNCTIONS(25, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO25 */
- ALTERNATE_FUNCTIONS(26, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO26 */
- ALTERNATE_FUNCTIONS(27, 2, UNUSED, UNUSED, 0, 0, 0), /* GPIO27, altA controlled by bit 2 */
- ALTERNATE_FUNCTIONS(28, 3, UNUSED, UNUSED, 0, 0, 0), /* GPIO28, altA controlled by bit 3 */
- ALTERNATE_FUNCTIONS(29, 4, UNUSED, UNUSED, 0, 0, 0), /* GPIO29, altA controlled by bit 4 */
- ALTERNATE_FUNCTIONS(30, 5, UNUSED, UNUSED, 0, 0, 0), /* GPIO30, altA controlled by bit 5 */
- ALTERNATE_FUNCTIONS(31, 6, UNUSED, UNUSED, 0, 0, 0), /* GPIO31, altA controlled by bit 6 */
- ALTERNATE_FUNCTIONS(32, 7, UNUSED, UNUSED, 0, 0, 0), /* GPIO32, altA controlled by bit 7 */
- /* GPIOSEL5 - bit 0-7 reserved */
- ALTERNATE_FUNCTIONS(33, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO33 */
- ALTERNATE_FUNCTIONS(34, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO34 */
- ALTERNATE_FUNCTIONS(35, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO35 */
- ALTERNATE_FUNCTIONS(36, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO36 */
- ALTERNATE_FUNCTIONS(37, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO37 */
- ALTERNATE_FUNCTIONS(38, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO38 */
- ALTERNATE_FUNCTIONS(39, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO39 */
- ALTERNATE_FUNCTIONS(40, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO40 */
- /* GPIOSEL6 - bit 0 reserved */
- ALTERNATE_FUNCTIONS(41, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO41 */
- ALTERNATE_FUNCTIONS(42, 1, UNUSED, UNUSED, 0, 0, 0), /* GPIO42, altA controlled by bit 1 */
- ALTERNATE_FUNCTIONS(43, 2, UNUSED, UNUSED, 0, 0, 0), /* GPIO43, altA controlled by bit 2 */
- ALTERNATE_FUNCTIONS(44, 3, UNUSED, UNUSED, 0, 0, 0), /* GPIO44, altA controlled by bit 3 */
- ALTERNATE_FUNCTIONS(45, 4, UNUSED, UNUSED, 0, 0, 0), /* GPIO45, altA controlled by bit 4 */
- ALTERNATE_FUNCTIONS(46, 5, UNUSED, UNUSED, 0, 0, 0), /* GPIO46, altA controlled by bit 5 */
- ALTERNATE_FUNCTIONS(47, 6, UNUSED, UNUSED, 0, 0, 0), /* GPIO47, altA controlled by bit 6 */
- ALTERNATE_FUNCTIONS(48, 7, UNUSED, UNUSED, 0, 0, 0), /* GPIO48, altA controlled by bit 7 */
- /* GPIOSEL7 - bit 0-1 reserved */
- ALTERNATE_FUNCTIONS(49, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO49 */
- ALTERNATE_FUNCTIONS(50, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO50 */
- ALTERNATE_FUNCTIONS(51, 2, UNUSED, UNUSED, 0, 0, 0), /* GPIO51, altA controlled by bit 2 */
- ALTERNATE_FUNCTIONS(52, 3, UNUSED, UNUSED, 0, 0, 0), /* GPIO52, altA controlled by bit 3 */
- ALTERNATE_FUNCTIONS(53, 4, UNUSED, UNUSED, 0, 0, 0), /* GPIO53, altA controlled by bit 4 */
- ALTERNATE_FUNCTIONS(54, 5, UNUSED, UNUSED, 0, 0, 0), /* GPIO54, altA controlled by bit 5 */
- ALTERNATE_FUNCTIONS(55, 6, UNUSED, UNUSED, 0, 0, 0), /* GPIO55, altA controlled by bit 6 */
- ALTERNATE_FUNCTIONS(56, 7, UNUSED, UNUSED, 0, 0, 0), /* GPIO56, altA controlled by bit 7 */
-};
-
-static struct pullud ab8540_pullud = {
- .first_pin = 51, /* GPIO1_VBAT */
- .last_pin = 54, /* GPIO4_VBAT */
-};
-
-/*
- * For AB8540 Only some GPIOs are interrupt capable:
- * GPIO43 to GPIO44
- * GPIO51 to GPIO54
- */
-static struct abx500_gpio_irq_cluster ab8540_gpio_irq_cluster[] = {
- GPIO_IRQ_CLUSTER(43, 43, AB8540_INT_GPIO43F),
- GPIO_IRQ_CLUSTER(44, 44, AB8540_INT_GPIO44F),
- GPIO_IRQ_CLUSTER(51, 54, AB9540_INT_GPIO51R),
-};
-
-static struct abx500_pinctrl_soc_data ab8540_soc = {
- .gpio_ranges = ab8540_pinranges,
- .gpio_num_ranges = ARRAY_SIZE(ab8540_pinranges),
- .pins = ab8540_pins,
- .npins = ARRAY_SIZE(ab8540_pins),
- .functions = ab8540_functions,
- .nfunctions = ARRAY_SIZE(ab8540_functions),
- .groups = ab8540_groups,
- .ngroups = ARRAY_SIZE(ab8540_groups),
- .alternate_functions = ab8540_alternate_functions,
- .pullud = &ab8540_pullud,
- .gpio_irq_cluster = ab8540_gpio_irq_cluster,
- .ngpio_irq_cluster = ARRAY_SIZE(ab8540_gpio_irq_cluster),
- .irq_gpio_rising_offset = AB8540_INT_GPIO43R,
- .irq_gpio_falling_offset = AB8540_INT_GPIO43F,
- .irq_gpio_factor = 2,
-};
-
-void
-abx500_pinctrl_ab8540_init(struct abx500_pinctrl_soc_data **soc)
-{
- *soc = &ab8540_soc;
-}
diff --git a/drivers/pinctrl/nomadik/pinctrl-ab9540.c b/drivers/pinctrl/nomadik/pinctrl-ab9540.c
deleted file mode 100644
index 1a281ca95dac..000000000000
--- a/drivers/pinctrl/nomadik/pinctrl-ab9540.c
+++ /dev/null
@@ -1,486 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson SA 2012
- *
- * Author: Patrice Chotard <patrice.chotard@stericsson.com> for ST-Ericsson.
- *
- * 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.
- */
-
-#include <linux/kernel.h>
-#include <linux/gpio.h>
-#include <linux/pinctrl/pinctrl.h>
-#include <linux/mfd/abx500/ab8500.h>
-#include "pinctrl-abx500.h"
-
-/* All the pins that can be used for GPIO and some other functions */
-#define ABX500_GPIO(offset) (offset)
-
-#define AB9540_PIN_R4 ABX500_GPIO(1)
-#define AB9540_PIN_V3 ABX500_GPIO(2)
-#define AB9540_PIN_T4 ABX500_GPIO(3)
-#define AB9540_PIN_T5 ABX500_GPIO(4)
-/* hole */
-#define AB9540_PIN_B18 ABX500_GPIO(10)
-#define AB9540_PIN_C18 ABX500_GPIO(11)
-/* hole */
-#define AB9540_PIN_D18 ABX500_GPIO(13)
-#define AB9540_PIN_B19 ABX500_GPIO(14)
-#define AB9540_PIN_C19 ABX500_GPIO(15)
-#define AB9540_PIN_D19 ABX500_GPIO(16)
-#define AB9540_PIN_R3 ABX500_GPIO(17)
-#define AB9540_PIN_T2 ABX500_GPIO(18)
-#define AB9540_PIN_U2 ABX500_GPIO(19)
-#define AB9540_PIN_V2 ABX500_GPIO(20)
-#define AB9540_PIN_N17 ABX500_GPIO(21)
-#define AB9540_PIN_N16 ABX500_GPIO(22)
-#define AB9540_PIN_M19 ABX500_GPIO(23)
-#define AB9540_PIN_T3 ABX500_GPIO(24)
-#define AB9540_PIN_W2 ABX500_GPIO(25)
-/* hole */
-#define AB9540_PIN_H4 ABX500_GPIO(27)
-#define AB9540_PIN_F1 ABX500_GPIO(28)
-#define AB9540_PIN_F4 ABX500_GPIO(29)
-#define AB9540_PIN_F2 ABX500_GPIO(30)
-#define AB9540_PIN_E4 ABX500_GPIO(31)
-#define AB9540_PIN_F3 ABX500_GPIO(32)
-/* hole */
-#define AB9540_PIN_J13 ABX500_GPIO(34)
-/* hole */
-#define AB9540_PIN_L17 ABX500_GPIO(40)
-#define AB9540_PIN_L16 ABX500_GPIO(41)
-#define AB9540_PIN_W3 ABX500_GPIO(42)
-#define AB9540_PIN_N4 ABX500_GPIO(50)
-#define AB9540_PIN_G12 ABX500_GPIO(51)
-#define AB9540_PIN_E17 ABX500_GPIO(52)
-#define AB9540_PIN_D11 ABX500_GPIO(53)
-#define AB9540_PIN_M18 ABX500_GPIO(54)
-
-/* indicates the highest GPIO number */
-#define AB9540_GPIO_MAX_NUMBER 54
-
-/*
- * The names of the pins are denoted by GPIO number and ball name, even
- * though they can be used for other things than GPIO, this is the first
- * column in the table of the data sheet and often used on schematics and
- * such.
- */
-static const struct pinctrl_pin_desc ab9540_pins[] = {
- PINCTRL_PIN(AB9540_PIN_R4, "GPIO1_R4"),
- PINCTRL_PIN(AB9540_PIN_V3, "GPIO2_V3"),
- PINCTRL_PIN(AB9540_PIN_T4, "GPIO3_T4"),
- PINCTRL_PIN(AB9540_PIN_T5, "GPIO4_T5"),
- /* hole */
- PINCTRL_PIN(AB9540_PIN_B18, "GPIO10_B18"),
- PINCTRL_PIN(AB9540_PIN_C18, "GPIO11_C18"),
- /* hole */
- PINCTRL_PIN(AB9540_PIN_D18, "GPIO13_D18"),
- PINCTRL_PIN(AB9540_PIN_B19, "GPIO14_B19"),
- PINCTRL_PIN(AB9540_PIN_C19, "GPIO15_C19"),
- PINCTRL_PIN(AB9540_PIN_D19, "GPIO16_D19"),
- PINCTRL_PIN(AB9540_PIN_R3, "GPIO17_R3"),
- PINCTRL_PIN(AB9540_PIN_T2, "GPIO18_T2"),
- PINCTRL_PIN(AB9540_PIN_U2, "GPIO19_U2"),
- PINCTRL_PIN(AB9540_PIN_V2, "GPIO20_V2"),
- PINCTRL_PIN(AB9540_PIN_N17, "GPIO21_N17"),
- PINCTRL_PIN(AB9540_PIN_N16, "GPIO22_N16"),
- PINCTRL_PIN(AB9540_PIN_M19, "GPIO23_M19"),
- PINCTRL_PIN(AB9540_PIN_T3, "GPIO24_T3"),
- PINCTRL_PIN(AB9540_PIN_W2, "GPIO25_W2"),
- /* hole */
- PINCTRL_PIN(AB9540_PIN_H4, "GPIO27_H4"),
- PINCTRL_PIN(AB9540_PIN_F1, "GPIO28_F1"),
- PINCTRL_PIN(AB9540_PIN_F4, "GPIO29_F4"),
- PINCTRL_PIN(AB9540_PIN_F2, "GPIO30_F2"),
- PINCTRL_PIN(AB9540_PIN_E4, "GPIO31_E4"),
- PINCTRL_PIN(AB9540_PIN_F3, "GPIO32_F3"),
- /* hole */
- PINCTRL_PIN(AB9540_PIN_J13, "GPIO34_J13"),
- /* hole */
- PINCTRL_PIN(AB9540_PIN_L17, "GPIO40_L17"),
- PINCTRL_PIN(AB9540_PIN_L16, "GPIO41_L16"),
- PINCTRL_PIN(AB9540_PIN_W3, "GPIO42_W3"),
- PINCTRL_PIN(AB9540_PIN_N4, "GPIO50_N4"),
- PINCTRL_PIN(AB9540_PIN_G12, "GPIO51_G12"),
- PINCTRL_PIN(AB9540_PIN_E17, "GPIO52_E17"),
- PINCTRL_PIN(AB9540_PIN_D11, "GPIO53_D11"),
- PINCTRL_PIN(AB9540_PIN_M18, "GPIO60_M18"),
-};
-
-/*
- * Maps local GPIO offsets to local pin numbers
- */
-static const struct abx500_pinrange ab9540_pinranges[] = {
- ABX500_PINRANGE(1, 4, ABX500_ALT_A),
- ABX500_PINRANGE(10, 2, ABX500_DEFAULT),
- ABX500_PINRANGE(13, 1, ABX500_DEFAULT),
- ABX500_PINRANGE(14, 12, ABX500_ALT_A),
- ABX500_PINRANGE(27, 6, ABX500_ALT_A),
- ABX500_PINRANGE(34, 1, ABX500_ALT_A),
- ABX500_PINRANGE(40, 3, ABX500_ALT_A),
- ABX500_PINRANGE(50, 1, ABX500_DEFAULT),
- ABX500_PINRANGE(51, 3, ABX500_ALT_A),
- ABX500_PINRANGE(54, 1, ABX500_DEFAULT),
-};
-
-/*
- * Read the pin group names like this:
- * sysclkreq2_d_1 = first groups of pins for sysclkreq2 on default function
- *
- * The groups are arranged as sets per altfunction column, so we can
- * mux in one group at a time by selecting the same altfunction for them
- * all. When functions require pins on different altfunctions, you need
- * to combine several groups.
- */
-
-/* default column */
-static const unsigned sysclkreq2_d_1_pins[] = { AB9540_PIN_R4 };
-static const unsigned sysclkreq3_d_1_pins[] = { AB9540_PIN_V3 };
-static const unsigned sysclkreq4_d_1_pins[] = { AB9540_PIN_T4 };
-static const unsigned sysclkreq6_d_1_pins[] = { AB9540_PIN_T5 };
-static const unsigned gpio10_d_1_pins[] = { AB9540_PIN_B18 };
-static const unsigned gpio11_d_1_pins[] = { AB9540_PIN_C18 };
-static const unsigned gpio13_d_1_pins[] = { AB9540_PIN_D18 };
-static const unsigned pwmout1_d_1_pins[] = { AB9540_PIN_B19 };
-static const unsigned pwmout2_d_1_pins[] = { AB9540_PIN_C19 };
-static const unsigned pwmout3_d_1_pins[] = { AB9540_PIN_D19 };
-/* audio data interface 1*/
-static const unsigned adi1_d_1_pins[] = { AB9540_PIN_R3, AB9540_PIN_T2,
- AB9540_PIN_U2, AB9540_PIN_V2 };
-/* USBUICC */
-static const unsigned usbuicc_d_1_pins[] = { AB9540_PIN_N17, AB9540_PIN_N16,
- AB9540_PIN_M19 };
-static const unsigned sysclkreq7_d_1_pins[] = { AB9540_PIN_T3 };
-static const unsigned sysclkreq8_d_1_pins[] = { AB9540_PIN_W2 };
-/* Digital microphone 1 and 2 */
-static const unsigned dmic12_d_1_pins[] = { AB9540_PIN_H4, AB9540_PIN_F1 };
-/* Digital microphone 3 and 4 */
-static const unsigned dmic34_d_1_pins[] = { AB9540_PIN_F4, AB9540_PIN_F2 };
-/* Digital microphone 5 and 6 */
-static const unsigned dmic56_d_1_pins[] = { AB9540_PIN_E4, AB9540_PIN_F3 };
-static const unsigned extcpena_d_1_pins[] = { AB9540_PIN_J13 };
-/* modem SDA/SCL */
-static const unsigned modsclsda_d_1_pins[] = { AB9540_PIN_L17, AB9540_PIN_L16 };
-static const unsigned sysclkreq5_d_1_pins[] = { AB9540_PIN_W3 };
-static const unsigned gpio50_d_1_pins[] = { AB9540_PIN_N4 };
-static const unsigned batremn_d_1_pins[] = { AB9540_PIN_G12 };
-static const unsigned resethw_d_1_pins[] = { AB9540_PIN_E17 };
-static const unsigned service_d_1_pins[] = { AB9540_PIN_D11 };
-static const unsigned gpio60_d_1_pins[] = { AB9540_PIN_M18 };
-
-/* Altfunction A column */
-static const unsigned gpio1_a_1_pins[] = { AB9540_PIN_R4 };
-static const unsigned gpio2_a_1_pins[] = { AB9540_PIN_V3 };
-static const unsigned gpio3_a_1_pins[] = { AB9540_PIN_T4 };
-static const unsigned gpio4_a_1_pins[] = { AB9540_PIN_T5 };
-static const unsigned hiqclkena_a_1_pins[] = { AB9540_PIN_B18 };
-static const unsigned pdmclk_a_1_pins[] = { AB9540_PIN_C18 };
-static const unsigned uartdata_a_1_pins[] = { AB9540_PIN_D18, AB9540_PIN_N4 };
-static const unsigned gpio14_a_1_pins[] = { AB9540_PIN_B19 };
-static const unsigned gpio15_a_1_pins[] = { AB9540_PIN_C19 };
-static const unsigned gpio16_a_1_pins[] = { AB9540_PIN_D19 };
-static const unsigned gpio17_a_1_pins[] = { AB9540_PIN_R3 };
-static const unsigned gpio18_a_1_pins[] = { AB9540_PIN_T2 };
-static const unsigned gpio19_a_1_pins[] = { AB9540_PIN_U2 };
-static const unsigned gpio20_a_1_pins[] = { AB9540_PIN_V2 };
-static const unsigned gpio21_a_1_pins[] = { AB9540_PIN_N17 };
-static const unsigned gpio22_a_1_pins[] = { AB9540_PIN_N16 };
-static const unsigned gpio23_a_1_pins[] = { AB9540_PIN_M19 };
-static const unsigned gpio24_a_1_pins[] = { AB9540_PIN_T3 };
-static const unsigned gpio25_a_1_pins[] = { AB9540_PIN_W2 };
-static const unsigned gpio27_a_1_pins[] = { AB9540_PIN_H4 };
-static const unsigned gpio28_a_1_pins[] = { AB9540_PIN_F1 };
-static const unsigned gpio29_a_1_pins[] = { AB9540_PIN_F4 };
-static const unsigned gpio30_a_1_pins[] = { AB9540_PIN_F2 };
-static const unsigned gpio31_a_1_pins[] = { AB9540_PIN_E4 };
-static const unsigned gpio32_a_1_pins[] = { AB9540_PIN_F3 };
-static const unsigned gpio34_a_1_pins[] = { AB9540_PIN_J13 };
-static const unsigned gpio40_a_1_pins[] = { AB9540_PIN_L17 };
-static const unsigned gpio41_a_1_pins[] = { AB9540_PIN_L16 };
-static const unsigned gpio42_a_1_pins[] = { AB9540_PIN_W3 };
-static const unsigned gpio51_a_1_pins[] = { AB9540_PIN_G12 };
-static const unsigned gpio52_a_1_pins[] = { AB9540_PIN_E17 };
-static const unsigned gpio53_a_1_pins[] = { AB9540_PIN_D11 };
-static const unsigned usbuiccpd_a_1_pins[] = { AB9540_PIN_M18 };
-
-/* Altfunction B colum */
-static const unsigned pdmdata_b_1_pins[] = { AB9540_PIN_B18 };
-static const unsigned pwmextvibra1_b_1_pins[] = { AB9540_PIN_D18 };
-static const unsigned pwmextvibra2_b_1_pins[] = { AB9540_PIN_N4 };
-
-/* Altfunction C column */
-static const unsigned usbvdat_c_1_pins[] = { AB9540_PIN_D18 };
-
-#define AB9540_PIN_GROUP(a, b) { .name = #a, .pins = a##_pins, \
- .npins = ARRAY_SIZE(a##_pins), .altsetting = b }
-
-static const struct abx500_pingroup ab9540_groups[] = {
- /* default column */
- AB9540_PIN_GROUP(sysclkreq2_d_1, ABX500_DEFAULT),
- AB9540_PIN_GROUP(sysclkreq3_d_1, ABX500_DEFAULT),
- AB9540_PIN_GROUP(sysclkreq4_d_1, ABX500_DEFAULT),
- AB9540_PIN_GROUP(sysclkreq6_d_1, ABX500_DEFAULT),
- AB9540_PIN_GROUP(gpio10_d_1, ABX500_DEFAULT),
- AB9540_PIN_GROUP(gpio11_d_1, ABX500_DEFAULT),
- AB9540_PIN_GROUP(gpio13_d_1, ABX500_DEFAULT),
- AB9540_PIN_GROUP(pwmout1_d_1, ABX500_DEFAULT),
- AB9540_PIN_GROUP(pwmout2_d_1, ABX500_DEFAULT),
- AB9540_PIN_GROUP(pwmout3_d_1, ABX500_DEFAULT),
- AB9540_PIN_GROUP(adi1_d_1, ABX500_DEFAULT),
- AB9540_PIN_GROUP(usbuicc_d_1, ABX500_DEFAULT),
- AB9540_PIN_GROUP(sysclkreq7_d_1, ABX500_DEFAULT),
- AB9540_PIN_GROUP(sysclkreq8_d_1, ABX500_DEFAULT),
- AB9540_PIN_GROUP(dmic12_d_1, ABX500_DEFAULT),
- AB9540_PIN_GROUP(dmic34_d_1, ABX500_DEFAULT),
- AB9540_PIN_GROUP(dmic56_d_1, ABX500_DEFAULT),
- AB9540_PIN_GROUP(extcpena_d_1, ABX500_DEFAULT),
- AB9540_PIN_GROUP(modsclsda_d_1, ABX500_DEFAULT),
- AB9540_PIN_GROUP(sysclkreq5_d_1, ABX500_DEFAULT),
- AB9540_PIN_GROUP(gpio50_d_1, ABX500_DEFAULT),
- AB9540_PIN_GROUP(batremn_d_1, ABX500_DEFAULT),
- AB9540_PIN_GROUP(resethw_d_1, ABX500_DEFAULT),
- AB9540_PIN_GROUP(service_d_1, ABX500_DEFAULT),
- AB9540_PIN_GROUP(gpio60_d_1, ABX500_DEFAULT),
-
- /* Altfunction A column */
- AB9540_PIN_GROUP(gpio1_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(gpio2_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(gpio3_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(gpio4_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(hiqclkena_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(pdmclk_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(uartdata_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(gpio14_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(gpio15_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(gpio16_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(gpio17_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(gpio18_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(gpio19_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(gpio20_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(gpio21_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(gpio22_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(gpio23_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(gpio24_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(gpio25_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(gpio27_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(gpio28_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(gpio29_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(gpio30_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(gpio31_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(gpio32_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(gpio34_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(gpio40_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(gpio41_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(gpio42_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(gpio51_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(gpio52_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(gpio53_a_1, ABX500_ALT_A),
- AB9540_PIN_GROUP(usbuiccpd_a_1, ABX500_ALT_A),
-
- /* Altfunction B column */
- AB9540_PIN_GROUP(pdmdata_b_1, ABX500_ALT_B),
- AB9540_PIN_GROUP(pwmextvibra1_b_1, ABX500_ALT_B),
- AB9540_PIN_GROUP(pwmextvibra2_b_1, ABX500_ALT_B),
-
- /* Altfunction C column */
- AB9540_PIN_GROUP(usbvdat_c_1, ABX500_ALT_C),
-};
-
-/* We use this macro to define the groups applicable to a function */
-#define AB9540_FUNC_GROUPS(a, b...) \
-static const char * const a##_groups[] = { b };
-
-AB9540_FUNC_GROUPS(sysclkreq, "sysclkreq2_d_1", "sysclkreq3_d_1",
- "sysclkreq4_d_1", "sysclkreq5_d_1", "sysclkreq6_d_1",
- "sysclkreq7_d_1", "sysclkreq8_d_1");
-AB9540_FUNC_GROUPS(gpio, "gpio1_a_1", "gpio2_a_1", "gpio3_a_1", "gpio4_a_1",
- "gpio10_d_1", "gpio11_d_1", "gpio13_d_1", "gpio14_a_1",
- "gpio15_a_1", "gpio16_a_1", "gpio17_a_1", "gpio18_a_1",
- "gpio19_a_1", "gpio20_a_1", "gpio21_a_1", "gpio22_a_1",
- "gpio23_a_1", "gpio24_a_1", "gpio25_a_1", "gpio27_a_1",
- "gpio28_a_1", "gpio29_a_1", "gpio30_a_1", "gpio31_a_1",
- "gpio32_a_1", "gpio34_a_1", "gpio40_a_1", "gpio41_a_1",
- "gpio42_a_1", "gpio50_d_1", "gpio51_a_1", "gpio52_a_1",
- "gpio53_a_1", "gpio60_d_1");
-AB9540_FUNC_GROUPS(pwmout, "pwmout1_d_1", "pwmout2_d_1", "pwmout3_d_1");
-AB9540_FUNC_GROUPS(adi1, "adi1_d_1");
-AB9540_FUNC_GROUPS(usbuicc, "usbuicc_d_1", "usbuiccpd_a_1");
-AB9540_FUNC_GROUPS(dmic, "dmic12_d_1", "dmic34_d_1", "dmic56_d_1");
-AB9540_FUNC_GROUPS(extcpena, "extcpena_d_1");
-AB9540_FUNC_GROUPS(modsclsda, "modsclsda_d_1");
-AB9540_FUNC_GROUPS(batremn, "batremn_d_1");
-AB9540_FUNC_GROUPS(resethw, "resethw_d_1");
-AB9540_FUNC_GROUPS(service, "service_d_1");
-AB9540_FUNC_GROUPS(hiqclkena, "hiqclkena_a_1");
-AB9540_FUNC_GROUPS(pdm, "pdmdata_b_1", "pdmclk_a_1");
-AB9540_FUNC_GROUPS(uartdata, "uartdata_a_1");
-AB9540_FUNC_GROUPS(pwmextvibra, "pwmextvibra1_b_1", "pwmextvibra2_b_1");
-AB9540_FUNC_GROUPS(usbvdat, "usbvdat_c_1");
-
-#define FUNCTION(fname) \
- { \
- .name = #fname, \
- .groups = fname##_groups, \
- .ngroups = ARRAY_SIZE(fname##_groups), \
- }
-
-static const struct abx500_function ab9540_functions[] = {
- FUNCTION(sysclkreq),
- FUNCTION(gpio),
- FUNCTION(pwmout),
- FUNCTION(adi1),
- FUNCTION(usbuicc),
- FUNCTION(dmic),
- FUNCTION(extcpena),
- FUNCTION(modsclsda),
- FUNCTION(batremn),
- FUNCTION(resethw),
- FUNCTION(service),
- FUNCTION(hiqclkena),
- FUNCTION(pdm),
- FUNCTION(uartdata),
- FUNCTION(pwmextvibra),
- FUNCTION(usbvdat),
-};
-
-/*
- * this table translates what's is in the AB9540 specification regarding the
- * balls alternate functions (as for DB, default, ALT_A, ALT_B and ALT_C).
- * ALTERNATE_FUNCTIONS(GPIO_NUMBER, GPIOSEL bit, ALTERNATFUNC bit1,
- * ALTERNATEFUNC bit2, ALTA val, ALTB val, ALTC val),
- *
- * example :
- *
- * ALTERNATE_FUNCTIONS(13, 4, 3, 4, 1, 0, 2),
- * means that pin AB9540_PIN_D18 (pin 13) supports 4 mux (default/ALT_A,
- * ALT_B and ALT_C), so GPIOSEL and ALTERNATFUNC registers are used to
- * select the mux. ALTA, ALTB and ALTC val indicates values to write in
- * ALTERNATFUNC register. We need to specifies these values as SOC
- * designers didn't apply the same logic on how to select mux in the
- * ABx500 family.
- *
- * As this pins supports at least ALT_B mux, default mux is
- * selected by writing 1 in GPIOSEL bit :
- *
- * | GPIOSEL bit=4 | alternatfunc bit2=4 | alternatfunc bit1=3
- * default | 1 | 0 | 0
- * alt_A | 0 | 0 | 1
- * alt_B | 0 | 0 | 0
- * alt_C | 0 | 1 | 0
- *
- * ALTERNATE_FUNCTIONS(1, 0, UNUSED, UNUSED),
- * means that pin AB9540_PIN_R4 (pin 1) supports 2 mux, so only GPIOSEL
- * register is used to select the mux. As this pins doesn't support at
- * least ALT_B mux, default mux is by writing 0 in GPIOSEL bit :
- *
- * | GPIOSEL bit=0 | alternatfunc bit2= | alternatfunc bit1=
- * default | 0 | 0 | 0
- * alt_A | 1 | 0 | 0
- */
-
-static struct
-alternate_functions ab9540alternate_functions[AB9540_GPIO_MAX_NUMBER + 1] = {
- /* GPIOSEL1 - bits 4-7 are reserved */
- ALTERNATE_FUNCTIONS(0, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO0 */
- ALTERNATE_FUNCTIONS(1, 0, UNUSED, UNUSED, 0, 0, 0), /* GPIO1, altA controlled by bit 0 */
- ALTERNATE_FUNCTIONS(2, 1, UNUSED, UNUSED, 0, 0, 0), /* GPIO2, altA controlled by bit 1 */
- ALTERNATE_FUNCTIONS(3, 2, UNUSED, UNUSED, 0, 0, 0), /* GPIO3, altA controlled by bit 2*/
- ALTERNATE_FUNCTIONS(4, 3, UNUSED, UNUSED, 0, 0, 0), /* GPIO4, altA controlled by bit 3*/
- ALTERNATE_FUNCTIONS(5, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO5 */
- ALTERNATE_FUNCTIONS(6, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO6 */
- ALTERNATE_FUNCTIONS(7, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO7 */
- ALTERNATE_FUNCTIONS(8, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO8 */
- /* GPIOSEL2 - bits 0 and 3 are reserved */
- ALTERNATE_FUNCTIONS(9, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO9 */
- ALTERNATE_FUNCTIONS(10, 1, 0, UNUSED, 1, 0, 0), /* GPIO10, altA and altB controlled by bit 0 */
- ALTERNATE_FUNCTIONS(11, 2, 1, UNUSED, 0, 0, 0), /* GPIO11, altA controlled by bit 1 */
- ALTERNATE_FUNCTIONS(12, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO12 */
- ALTERNATE_FUNCTIONS(13, 4, 3, 4, 1, 0, 2), /* GPIO13, altA altB and altC controlled by bit 3 and 4 */
- ALTERNATE_FUNCTIONS(14, 5, UNUSED, UNUSED, 0, 0, 0), /* GPIO14, altA controlled by bit 5 */
- ALTERNATE_FUNCTIONS(15, 6, UNUSED, UNUSED, 0, 0, 0), /* GPIO15, altA controlled by bit 6 */
- ALTERNATE_FUNCTIONS(16, 7, UNUSED, UNUSED, 0, 0, 0), /* GPIO16, altA controlled by bit 7 */
- /* GPIOSEL3 - bit 1-3 reserved
- * pins 17 to 20 are special case, only bit 0 is used to select
- * alternate function for these 4 pins.
- * bits 1 to 3 are reserved
- */
- ALTERNATE_FUNCTIONS(17, 0, UNUSED, UNUSED, 0, 0, 0), /* GPIO17, altA controlled by bit 0 */
- ALTERNATE_FUNCTIONS(18, 0, UNUSED, UNUSED, 0, 0, 0), /* GPIO18, altA controlled by bit 0 */
- ALTERNATE_FUNCTIONS(19, 0, UNUSED, UNUSED, 0, 0, 0), /* GPIO19, altA controlled by bit 0 */
- ALTERNATE_FUNCTIONS(20, 0, UNUSED, UNUSED, 0, 0, 0), /* GPIO20, altA controlled by bit 0 */
- ALTERNATE_FUNCTIONS(21, 4, UNUSED, UNUSED, 0, 0, 0), /* GPIO21, altA controlled by bit 4 */
- ALTERNATE_FUNCTIONS(22, 5, UNUSED, UNUSED, 0, 0, 0), /* GPIO22, altA controlled by bit 5 */
- ALTERNATE_FUNCTIONS(23, 6, UNUSED, UNUSED, 0, 0, 0), /* GPIO23, altA controlled by bit 6 */
- ALTERNATE_FUNCTIONS(24, 7, UNUSED, UNUSED, 0, 0, 0), /* GPIO24, altA controlled by bit 7 */
- /* GPIOSEL4 - bit 1 reserved */
- ALTERNATE_FUNCTIONS(25, 0, UNUSED, UNUSED, 0, 0, 0), /* GPIO25, altA controlled by bit 0 */
- ALTERNATE_FUNCTIONS(26, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO26 */
- ALTERNATE_FUNCTIONS(27, 2, UNUSED, UNUSED, 0, 0, 0), /* GPIO27, altA controlled by bit 2 */
- ALTERNATE_FUNCTIONS(28, 3, UNUSED, UNUSED, 0, 0, 0), /* GPIO28, altA controlled by bit 3 */
- ALTERNATE_FUNCTIONS(29, 4, UNUSED, UNUSED, 0, 0, 0), /* GPIO29, altA controlled by bit 4 */
- ALTERNATE_FUNCTIONS(30, 5, UNUSED, UNUSED, 0, 0, 0), /* GPIO30, altA controlled by bit 5 */
- ALTERNATE_FUNCTIONS(31, 6, UNUSED, UNUSED, 0, 0, 0), /* GPIO31, altA controlled by bit 6 */
- ALTERNATE_FUNCTIONS(32, 7, UNUSED, UNUSED, 0, 0, 0), /* GPIO32, altA controlled by bit 7 */
- /* GPIOSEL5 - bit 0, 2-6 are reserved */
- ALTERNATE_FUNCTIONS(33, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO33 */
- ALTERNATE_FUNCTIONS(34, 1, UNUSED, UNUSED, 0, 0, 0), /* GPIO34, altA controlled by bit 1 */
- ALTERNATE_FUNCTIONS(35, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO35 */
- ALTERNATE_FUNCTIONS(36, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO36 */
- ALTERNATE_FUNCTIONS(37, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO37 */
- ALTERNATE_FUNCTIONS(38, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO38 */
- ALTERNATE_FUNCTIONS(39, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO39 */
- ALTERNATE_FUNCTIONS(40, 7, UNUSED, UNUSED, 0, 0, 0), /* GPIO40, altA controlled by bit 7 */
- /* GPIOSEL6 - bit 2-7 are reserved */
- ALTERNATE_FUNCTIONS(41, 0, UNUSED, UNUSED, 0, 0, 0), /* GPIO41, altA controlled by bit 0 */
- ALTERNATE_FUNCTIONS(42, 1, UNUSED, UNUSED, 0, 0, 0), /* GPIO42, altA controlled by bit 1 */
- ALTERNATE_FUNCTIONS(43, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO43 */
- ALTERNATE_FUNCTIONS(44, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO44 */
- ALTERNATE_FUNCTIONS(45, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO45 */
- ALTERNATE_FUNCTIONS(46, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO46 */
- ALTERNATE_FUNCTIONS(47, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO47 */
- ALTERNATE_FUNCTIONS(48, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO48 */
- /*
- * GPIOSEL7 - bit 0 and 6-7 are reserved
- * special case with GPIO60, wich is located at offset 5 of gpiosel7
- * don't know why it has been called GPIO60 in AB9540 datasheet,
- * GPIO54 would be logical..., so at SOC point of view we consider
- * GPIO60 = GPIO54
- */
- ALTERNATE_FUNCTIONS(49, 0, UNUSED, UNUSED, 0, 0, 0), /* no GPIO49 */
- ALTERNATE_FUNCTIONS(50, 1, 2, UNUSED, 1, 0, 0), /* GPIO50, altA and altB controlled by bit 1 */
- ALTERNATE_FUNCTIONS(51, 2, UNUSED, UNUSED, 0, 0, 0), /* GPIO51, altA controlled by bit 2 */
- ALTERNATE_FUNCTIONS(52, 3, UNUSED, UNUSED, 0, 0, 0), /* GPIO52, altA controlled by bit 3 */
- ALTERNATE_FUNCTIONS(53, 4, UNUSED, UNUSED, 0, 0, 0), /* GPIO53, altA controlled by bit 4 */
- ALTERNATE_FUNCTIONS(54, 5, UNUSED, UNUSED, 0, 0, 0), /* GPIO54 = GPIO60, altA controlled by bit 5 */
-};
-
-static struct abx500_gpio_irq_cluster ab9540_gpio_irq_cluster[] = {
- GPIO_IRQ_CLUSTER(10, 13, AB8500_INT_GPIO10R),
- GPIO_IRQ_CLUSTER(24, 25, AB8500_INT_GPIO24R),
- GPIO_IRQ_CLUSTER(40, 41, AB8500_INT_GPIO40R),
- GPIO_IRQ_CLUSTER(50, 54, AB9540_INT_GPIO50R),
-};
-
-static struct abx500_pinctrl_soc_data ab9540_soc = {
- .gpio_ranges = ab9540_pinranges,
- .gpio_num_ranges = ARRAY_SIZE(ab9540_pinranges),
- .pins = ab9540_pins,
- .npins = ARRAY_SIZE(ab9540_pins),
- .functions = ab9540_functions,
- .nfunctions = ARRAY_SIZE(ab9540_functions),
- .groups = ab9540_groups,
- .ngroups = ARRAY_SIZE(ab9540_groups),
- .alternate_functions = ab9540alternate_functions,
- .gpio_irq_cluster = ab9540_gpio_irq_cluster,
- .ngpio_irq_cluster = ARRAY_SIZE(ab9540_gpio_irq_cluster),
- .irq_gpio_rising_offset = AB8500_INT_GPIO6R,
- .irq_gpio_falling_offset = AB8500_INT_GPIO6F,
- .irq_gpio_factor = 1,
-};
-
-void
-abx500_pinctrl_ab9540_init(struct abx500_pinctrl_soc_data **soc)
-{
- *soc = &ab9540_soc;
-}
diff --git a/drivers/pinctrl/nomadik/pinctrl-abx500.c b/drivers/pinctrl/nomadik/pinctrl-abx500.c
index d56a49e53f56..aa592ef23a29 100644
--- a/drivers/pinctrl/nomadik/pinctrl-abx500.c
+++ b/drivers/pinctrl/nomadik/pinctrl-abx500.c
@@ -38,15 +38,6 @@
#include "../pinctrl-utils.h"
/*
- * The AB9540 and AB8540 GPIO support are extended versions
- * of the AB8500 GPIO support.
- * The AB9540 supports an additional (7th) register so that
- * more GPIO may be configured and used.
- * The AB8540 supports 4 new gpios (GPIOx_VBAT) that have
- * internal pull-up and pull-down capabilities.
- */
-
-/*
* GPIO registers offset
* Bank: 0x10
*/
@@ -56,7 +47,6 @@
#define AB8500_GPIO_SEL4_REG 0x03
#define AB8500_GPIO_SEL5_REG 0x04
#define AB8500_GPIO_SEL6_REG 0x05
-#define AB9540_GPIO_SEL7_REG 0x06
#define AB8500_GPIO_DIR1_REG 0x10
#define AB8500_GPIO_DIR2_REG 0x11
@@ -64,7 +54,6 @@
#define AB8500_GPIO_DIR4_REG 0x13
#define AB8500_GPIO_DIR5_REG 0x14
#define AB8500_GPIO_DIR6_REG 0x15
-#define AB9540_GPIO_DIR7_REG 0x16
#define AB8500_GPIO_OUT1_REG 0x20
#define AB8500_GPIO_OUT2_REG 0x21
@@ -72,7 +61,6 @@
#define AB8500_GPIO_OUT4_REG 0x23
#define AB8500_GPIO_OUT5_REG 0x24
#define AB8500_GPIO_OUT6_REG 0x25
-#define AB9540_GPIO_OUT7_REG 0x26
#define AB8500_GPIO_PUD1_REG 0x30
#define AB8500_GPIO_PUD2_REG 0x31
@@ -80,7 +68,6 @@
#define AB8500_GPIO_PUD4_REG 0x33
#define AB8500_GPIO_PUD5_REG 0x34
#define AB8500_GPIO_PUD6_REG 0x35
-#define AB9540_GPIO_PUD7_REG 0x36
#define AB8500_GPIO_IN1_REG 0x40
#define AB8500_GPIO_IN2_REG 0x41
@@ -88,14 +75,7 @@
#define AB8500_GPIO_IN4_REG 0x43
#define AB8500_GPIO_IN5_REG 0x44
#define AB8500_GPIO_IN6_REG 0x45
-#define AB9540_GPIO_IN7_REG 0x46
-#define AB8540_GPIO_VINSEL_REG 0x47
-#define AB8540_GPIO_PULL_UPDOWN_REG 0x48
#define AB8500_GPIO_ALTFUN_REG 0x50
-#define AB8540_GPIO_PULL_UPDOWN_MASK 0x03
-#define AB8540_GPIO_VINSEL_MASK 0x03
-#define AB8540_GPIOX_VBAT_START 51
-#define AB8540_GPIOX_VBAT_END 54
#define ABX500_GPIO_INPUT 0
#define ABX500_GPIO_OUTPUT 1
@@ -192,94 +172,11 @@ static void abx500_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
dev_err(pct->dev, "%s write failed (%d)\n", __func__, ret);
}
-#ifdef CONFIG_DEBUG_FS
-static int abx500_get_pull_updown(struct abx500_pinctrl *pct, int offset,
- enum abx500_gpio_pull_updown *pull_updown)
-{
- u8 pos;
- u8 val;
- int ret;
- struct pullud *pullud;
-
- if (!pct->soc->pullud) {
- dev_err(pct->dev, "%s AB chip doesn't support pull up/down feature",
- __func__);
- ret = -EPERM;
- goto out;
- }
-
- pullud = pct->soc->pullud;
-
- if ((offset < pullud->first_pin)
- || (offset > pullud->last_pin)) {
- ret = -EINVAL;
- goto out;
- }
-
- ret = abx500_get_register_interruptible(pct->dev,
- AB8500_MISC, AB8540_GPIO_PULL_UPDOWN_REG, &val);
-
- pos = (offset - pullud->first_pin) << 1;
- *pull_updown = (val >> pos) & AB8540_GPIO_PULL_UPDOWN_MASK;
-
-out:
- if (ret < 0)
- dev_err(pct->dev, "%s failed (%d)\n", __func__, ret);
-
- return ret;
-}
-#endif
-
-static int abx500_set_pull_updown(struct abx500_pinctrl *pct,
- int offset, enum abx500_gpio_pull_updown val)
-{
- u8 pos;
- int ret;
- struct pullud *pullud;
-
- if (!pct->soc->pullud) {
- dev_err(pct->dev, "%s AB chip doesn't support pull up/down feature",
- __func__);
- ret = -EPERM;
- goto out;
- }
-
- pullud = pct->soc->pullud;
-
- if ((offset < pullud->first_pin)
- || (offset > pullud->last_pin)) {
- ret = -EINVAL;
- goto out;
- }
- pos = (offset - pullud->first_pin) << 1;
-
- ret = abx500_mask_and_set_register_interruptible(pct->dev,
- AB8500_MISC, AB8540_GPIO_PULL_UPDOWN_REG,
- AB8540_GPIO_PULL_UPDOWN_MASK << pos, val << pos);
-
-out:
- if (ret < 0)
- dev_err(pct->dev, "%s failed (%d)\n", __func__, ret);
-
- return ret;
-}
-
-static bool abx500_pullud_supported(struct gpio_chip *chip, unsigned gpio)
-{
- struct abx500_pinctrl *pct = gpiochip_get_data(chip);
- struct pullud *pullud = pct->soc->pullud;
-
- return (pullud &&
- gpio >= pullud->first_pin &&
- gpio <= pullud->last_pin);
-}
-
static int abx500_gpio_direction_output(struct gpio_chip *chip,
unsigned offset,
int val)
{
struct abx500_pinctrl *pct = gpiochip_get_data(chip);
- unsigned gpio;
int ret;
/* set direction as output */
@@ -295,16 +192,7 @@ static int abx500_gpio_direction_output(struct gpio_chip *chip,
AB8500_GPIO_PUD1_REG,
offset,
ABX500_GPIO_PULL_NONE);
- if (ret < 0)
- goto out;
- /* if supported, disable both pull down and pull up */
- gpio = offset + 1;
- if (abx500_pullud_supported(chip, gpio)) {
- ret = abx500_set_pull_updown(pct,
- gpio,
- ABX500_GPIO_PULL_NONE);
- }
out:
if (ret < 0) {
dev_err(pct->dev, "%s failed (%d)\n", __func__, ret);
@@ -570,7 +458,6 @@ static void abx500_gpio_dbg_show_one(struct seq_file *s,
int mode = -1;
bool is_out;
bool pd;
- enum abx500_gpio_pull_updown pud = 0;
int ret;
const char *modes[] = {
@@ -597,20 +484,12 @@ static void abx500_gpio_dbg_show_one(struct seq_file *s,
is_out ? "out" : "in ");
if (!is_out) {
- if (abx500_pullud_supported(chip, offset)) {
- ret = abx500_get_pull_updown(pct, offset, &pud);
- if (ret < 0)
- goto out;
-
- seq_printf(s, " %-9s", pull_up_down[pud]);
- } else {
- ret = abx500_gpio_get_bit(chip, AB8500_GPIO_PUD1_REG,
- gpio_offset, &pd);
- if (ret < 0)
- goto out;
+ ret = abx500_gpio_get_bit(chip, AB8500_GPIO_PUD1_REG,
+ gpio_offset, &pd);
+ if (ret < 0)
+ goto out;
- seq_printf(s, " %-9s", pull_up_down[pd]);
- }
+ seq_printf(s, " %-9s", pull_up_down[pd]);
} else
seq_printf(s, " %-9s", chip->get(chip, offset) ? "hi" : "lo");
@@ -994,23 +873,11 @@ static int abx500_pin_config_set(struct pinctrl_dev *pctldev,
ret = abx500_gpio_direction_input(chip, offset);
if (ret < 0)
goto out;
- /*
- * Some chips only support pull down, while some
- * actually support both pull up and pull down. Such
- * chips have a "pullud" range specified for the pins
- * that support both features. If the pin is not
- * within that range, we fall back to the old bit set
- * that only support pull down.
- */
- if (abx500_pullud_supported(chip, pin))
- ret = abx500_set_pull_updown(pct,
- pin,
- ABX500_GPIO_PULL_NONE);
- else
- /* Chip only supports pull down */
- ret = abx500_gpio_set_bits(chip,
- AB8500_GPIO_PUD1_REG, offset,
- ABX500_GPIO_PULL_NONE);
+
+ /* Chip only supports pull down */
+ ret = abx500_gpio_set_bits(chip,
+ AB8500_GPIO_PUD1_REG, offset,
+ ABX500_GPIO_PULL_NONE);
break;
case PIN_CONFIG_BIAS_PULL_DOWN:
@@ -1020,25 +887,13 @@ static int abx500_pin_config_set(struct pinctrl_dev *pctldev,
/*
* if argument = 1 set the pull down
* else clear the pull down
- * Some chips only support pull down, while some
- * actually support both pull up and pull down. Such
- * chips have a "pullud" range specified for the pins
- * that support both features. If the pin is not
- * within that range, we fall back to the old bit set
- * that only support pull down.
+ * Chip only supports pull down
*/
- if (abx500_pullud_supported(chip, pin))
- ret = abx500_set_pull_updown(pct,
- pin,
- argument ? ABX500_GPIO_PULL_DOWN :
- ABX500_GPIO_PULL_NONE);
- else
- /* Chip only supports pull down */
- ret = abx500_gpio_set_bits(chip,
- AB8500_GPIO_PUD1_REG,
- offset,
- argument ? ABX500_GPIO_PULL_DOWN :
- ABX500_GPIO_PULL_NONE);
+ ret = abx500_gpio_set_bits(chip,
+ AB8500_GPIO_PUD1_REG,
+ offset,
+ argument ? ABX500_GPIO_PULL_DOWN :
+ ABX500_GPIO_PULL_NONE);
break;
case PIN_CONFIG_BIAS_PULL_UP:
@@ -1050,18 +905,6 @@ static int abx500_pin_config_set(struct pinctrl_dev *pctldev,
* else clear the pull up
*/
ret = abx500_gpio_direction_input(chip, offset);
- /*
- * Some chips only support pull down, while some
- * actually support both pull up and pull down. Such
- * chips have a "pullud" range specified for the pins
- * that support both features. If the pin is not
- * within that range, do nothing
- */
- if (abx500_pullud_supported(chip, pin))
- ret = abx500_set_pull_updown(pct,
- pin,
- argument ? ABX500_GPIO_PULL_UP :
- ABX500_GPIO_PULL_NONE);
break;
case PIN_CONFIG_OUTPUT:
@@ -1136,8 +979,6 @@ static int abx500_get_gpio_num(struct abx500_pinctrl_soc_data *soc)
static const struct of_device_id abx500_gpio_match[] = {
{ .compatible = "stericsson,ab8500-gpio", .data = (void *)PINCTRL_AB8500, },
{ .compatible = "stericsson,ab8505-gpio", .data = (void *)PINCTRL_AB8505, },
- { .compatible = "stericsson,ab8540-gpio", .data = (void *)PINCTRL_AB8540, },
- { .compatible = "stericsson,ab9540-gpio", .data = (void *)PINCTRL_AB9540, },
{ }
};
@@ -1177,12 +1018,6 @@ static int abx500_gpio_probe(struct platform_device *pdev)
case PINCTRL_AB8500:
abx500_pinctrl_ab8500_init(&pct->soc);
break;
- case PINCTRL_AB8540:
- abx500_pinctrl_ab8540_init(&pct->soc);
- break;
- case PINCTRL_AB9540:
- abx500_pinctrl_ab9540_init(&pct->soc);
- break;
case PINCTRL_AB8505:
abx500_pinctrl_ab8505_init(&pct->soc);
break;
diff --git a/drivers/pinctrl/nomadik/pinctrl-abx500.h b/drivers/pinctrl/nomadik/pinctrl-abx500.h
index 43f9b718a8ef..90bb12fe8073 100644
--- a/drivers/pinctrl/nomadik/pinctrl-abx500.h
+++ b/drivers/pinctrl/nomadik/pinctrl-abx500.h
@@ -4,9 +4,7 @@
/* Package definitions */
#define PINCTRL_AB8500 0
-#define PINCTRL_AB8540 1
-#define PINCTRL_AB9540 2
-#define PINCTRL_AB8505 3
+#define PINCTRL_AB8505 1
/* pins alternate function */
enum abx500_pin_func {
@@ -96,17 +94,6 @@ struct alternate_functions {
u8 altc_val;
};
-/**
- * struct pullud - specific pull up/down feature
- * @first_pin: The pin number of the first pins which support
- * specific pull up/down
- * @last_pin: The pin number of the last pins
- */
-struct pullud {
- unsigned first_pin;
- unsigned last_pin;
-};
-
#define GPIO_IRQ_CLUSTER(a, b, c) \
{ \
.start = a, \
@@ -162,8 +149,6 @@ struct abx500_pinrange {
* @ngroups: The number of entries in @groups.
* @alternate_functions: array describing pins which supports alternate and
* how to set it.
- * @pullud: array describing pins which supports pull up/down
- * specific registers.
* @gpio_irq_cluster: An array of GPIO interrupt capable for this SoC
* @ngpio_irq_cluster: The number of GPIO inetrrupt capable for this SoC
* @irq_gpio_rising_offset: Interrupt offset used as base to compute specific
@@ -184,7 +169,6 @@ struct abx500_pinctrl_soc_data {
const struct abx500_pingroup *groups;
unsigned ngroups;
struct alternate_functions *alternate_functions;
- struct pullud *pullud;
struct abx500_gpio_irq_cluster *gpio_irq_cluster;
unsigned ngpio_irq_cluster;
int irq_gpio_rising_offset;
@@ -205,32 +189,6 @@ abx500_pinctrl_ab8500_init(struct abx500_pinctrl_soc_data **soc)
#endif
-#ifdef CONFIG_PINCTRL_AB8540
-
-void abx500_pinctrl_ab8540_init(struct abx500_pinctrl_soc_data **soc);
-
-#else
-
-static inline void
-abx500_pinctrl_ab8540_init(struct abx500_pinctrl_soc_data **soc)
-{
-}
-
-#endif
-
-#ifdef CONFIG_PINCTRL_AB9540
-
-void abx500_pinctrl_ab9540_init(struct abx500_pinctrl_soc_data **soc);
-
-#else
-
-static inline void
-abx500_pinctrl_ab9540_init(struct abx500_pinctrl_soc_data **soc)
-{
-}
-
-#endif
-
#ifdef CONFIG_PINCTRL_AB8505
void abx500_pinctrl_ab8505_init(struct abx500_pinctrl_soc_data **soc);
diff --git a/drivers/pinctrl/nomadik/pinctrl-nomadik-db8540.c b/drivers/pinctrl/nomadik/pinctrl-nomadik-db8540.c
deleted file mode 100644
index ae3ac7b799a6..000000000000
--- a/drivers/pinctrl/nomadik/pinctrl-nomadik-db8540.c
+++ /dev/null
@@ -1,1243 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <linux/kernel.h>
-#include <linux/pinctrl/pinctrl.h>
-#include "pinctrl-nomadik.h"
-
-/* All the pins that can be used for GPIO and some other functions */
-#define _GPIO(offset) (offset)
-
-#define DB8540_PIN_AH6 _GPIO(0)
-#define DB8540_PIN_AG7 _GPIO(1)
-#define DB8540_PIN_AF2 _GPIO(2)
-#define DB8540_PIN_AD3 _GPIO(3)
-#define DB8540_PIN_AF6 _GPIO(4)
-#define DB8540_PIN_AG6 _GPIO(5)
-#define DB8540_PIN_AD5 _GPIO(6)
-#define DB8540_PIN_AF7 _GPIO(7)
-#define DB8540_PIN_AG5 _GPIO(8)
-#define DB8540_PIN_AH5 _GPIO(9)
-#define DB8540_PIN_AE4 _GPIO(10)
-#define DB8540_PIN_AD1 _GPIO(11)
-#define DB8540_PIN_AD2 _GPIO(12)
-#define DB8540_PIN_AC2 _GPIO(13)
-#define DB8540_PIN_AC4 _GPIO(14)
-#define DB8540_PIN_AC3 _GPIO(15)
-#define DB8540_PIN_AH7 _GPIO(16)
-#define DB8540_PIN_AE7 _GPIO(17)
-/* Hole */
-#define DB8540_PIN_AF8 _GPIO(22)
-#define DB8540_PIN_AH11 _GPIO(23)
-#define DB8540_PIN_AG11 _GPIO(24)
-#define DB8540_PIN_AF11 _GPIO(25)
-#define DB8540_PIN_AH10 _GPIO(26)
-#define DB8540_PIN_AG10 _GPIO(27)
-#define DB8540_PIN_AF10 _GPIO(28)
-/* Hole */
-#define DB8540_PIN_AD4 _GPIO(33)
-#define DB8540_PIN_AF3 _GPIO(34)
-#define DB8540_PIN_AF5 _GPIO(35)
-#define DB8540_PIN_AG4 _GPIO(36)
-#define DB8540_PIN_AF9 _GPIO(37)
-#define DB8540_PIN_AE8 _GPIO(38)
-/* Hole */
-#define DB8540_PIN_M26 _GPIO(64)
-#define DB8540_PIN_M25 _GPIO(65)
-#define DB8540_PIN_M27 _GPIO(66)
-#define DB8540_PIN_N25 _GPIO(67)
-/* Hole */
-#define DB8540_PIN_M28 _GPIO(70)
-#define DB8540_PIN_N26 _GPIO(71)
-#define DB8540_PIN_M22 _GPIO(72)
-#define DB8540_PIN_N22 _GPIO(73)
-#define DB8540_PIN_N27 _GPIO(74)
-#define DB8540_PIN_N28 _GPIO(75)
-#define DB8540_PIN_P22 _GPIO(76)
-#define DB8540_PIN_P28 _GPIO(77)
-#define DB8540_PIN_P26 _GPIO(78)
-#define DB8540_PIN_T22 _GPIO(79)
-#define DB8540_PIN_R27 _GPIO(80)
-#define DB8540_PIN_P27 _GPIO(81)
-#define DB8540_PIN_R26 _GPIO(82)
-#define DB8540_PIN_R25 _GPIO(83)
-#define DB8540_PIN_U22 _GPIO(84)
-#define DB8540_PIN_T27 _GPIO(85)
-#define DB8540_PIN_T25 _GPIO(86)
-#define DB8540_PIN_T26 _GPIO(87)
-/* Hole */
-#define DB8540_PIN_AF20 _GPIO(116)
-#define DB8540_PIN_AG21 _GPIO(117)
-#define DB8540_PIN_AH19 _GPIO(118)
-#define DB8540_PIN_AE19 _GPIO(119)
-#define DB8540_PIN_AG18 _GPIO(120)
-#define DB8540_PIN_AH17 _GPIO(121)
-#define DB8540_PIN_AF19 _GPIO(122)
-#define DB8540_PIN_AF18 _GPIO(123)
-#define DB8540_PIN_AE18 _GPIO(124)
-#define DB8540_PIN_AG17 _GPIO(125)
-#define DB8540_PIN_AF17 _GPIO(126)
-#define DB8540_PIN_AE17 _GPIO(127)
-#define DB8540_PIN_AC27 _GPIO(128)
-#define DB8540_PIN_AD27 _GPIO(129)
-#define DB8540_PIN_AE28 _GPIO(130)
-#define DB8540_PIN_AG26 _GPIO(131)
-#define DB8540_PIN_AF25 _GPIO(132)
-#define DB8540_PIN_AE27 _GPIO(133)
-#define DB8540_PIN_AF27 _GPIO(134)
-#define DB8540_PIN_AG28 _GPIO(135)
-#define DB8540_PIN_AF28 _GPIO(136)
-#define DB8540_PIN_AG25 _GPIO(137)
-#define DB8540_PIN_AG24 _GPIO(138)
-#define DB8540_PIN_AD25 _GPIO(139)
-#define DB8540_PIN_AH25 _GPIO(140)
-#define DB8540_PIN_AF26 _GPIO(141)
-#define DB8540_PIN_AF23 _GPIO(142)
-#define DB8540_PIN_AG23 _GPIO(143)
-#define DB8540_PIN_AE25 _GPIO(144)
-#define DB8540_PIN_AH24 _GPIO(145)
-#define DB8540_PIN_AJ25 _GPIO(146)
-#define DB8540_PIN_AG27 _GPIO(147)
-#define DB8540_PIN_AH23 _GPIO(148)
-#define DB8540_PIN_AE26 _GPIO(149)
-#define DB8540_PIN_AE24 _GPIO(150)
-#define DB8540_PIN_AJ24 _GPIO(151)
-#define DB8540_PIN_AE21 _GPIO(152)
-#define DB8540_PIN_AG22 _GPIO(153)
-#define DB8540_PIN_AF21 _GPIO(154)
-#define DB8540_PIN_AF24 _GPIO(155)
-#define DB8540_PIN_AH22 _GPIO(156)
-#define DB8540_PIN_AJ23 _GPIO(157)
-#define DB8540_PIN_AH21 _GPIO(158)
-#define DB8540_PIN_AG20 _GPIO(159)
-#define DB8540_PIN_AE23 _GPIO(160)
-#define DB8540_PIN_AH20 _GPIO(161)
-#define DB8540_PIN_AG19 _GPIO(162)
-#define DB8540_PIN_AF22 _GPIO(163)
-#define DB8540_PIN_AJ21 _GPIO(164)
-#define DB8540_PIN_AD26 _GPIO(165)
-#define DB8540_PIN_AD28 _GPIO(166)
-#define DB8540_PIN_AC28 _GPIO(167)
-#define DB8540_PIN_AC26 _GPIO(168)
-/* Hole */
-#define DB8540_PIN_J3 _GPIO(192)
-#define DB8540_PIN_H1 _GPIO(193)
-#define DB8540_PIN_J2 _GPIO(194)
-#define DB8540_PIN_H2 _GPIO(195)
-#define DB8540_PIN_H3 _GPIO(196)
-#define DB8540_PIN_H4 _GPIO(197)
-#define DB8540_PIN_G2 _GPIO(198)
-#define DB8540_PIN_G3 _GPIO(199)
-#define DB8540_PIN_G4 _GPIO(200)
-#define DB8540_PIN_F2 _GPIO(201)
-#define DB8540_PIN_C6 _GPIO(202)
-#define DB8540_PIN_B6 _GPIO(203)
-#define DB8540_PIN_B7 _GPIO(204)
-#define DB8540_PIN_A7 _GPIO(205)
-#define DB8540_PIN_D7 _GPIO(206)
-#define DB8540_PIN_D8 _GPIO(207)
-#define DB8540_PIN_F3 _GPIO(208)
-#define DB8540_PIN_E2 _GPIO(209)
-#define DB8540_PIN_C7 _GPIO(210)
-#define DB8540_PIN_B8 _GPIO(211)
-#define DB8540_PIN_C10 _GPIO(212)
-#define DB8540_PIN_C8 _GPIO(213)
-#define DB8540_PIN_C9 _GPIO(214)
-/* Hole */
-#define DB8540_PIN_B9 _GPIO(219)
-#define DB8540_PIN_A10 _GPIO(220)
-#define DB8540_PIN_D9 _GPIO(221)
-#define DB8540_PIN_B11 _GPIO(222)
-#define DB8540_PIN_B10 _GPIO(223)
-#define DB8540_PIN_E10 _GPIO(224)
-#define DB8540_PIN_B12 _GPIO(225)
-#define DB8540_PIN_D10 _GPIO(226)
-#define DB8540_PIN_D11 _GPIO(227)
-#define DB8540_PIN_AJ6 _GPIO(228)
-#define DB8540_PIN_B13 _GPIO(229)
-#define DB8540_PIN_C12 _GPIO(230)
-#define DB8540_PIN_B14 _GPIO(231)
-#define DB8540_PIN_E11 _GPIO(232)
-/* Hole */
-#define DB8540_PIN_D12 _GPIO(256)
-#define DB8540_PIN_D15 _GPIO(257)
-#define DB8540_PIN_C13 _GPIO(258)
-#define DB8540_PIN_C14 _GPIO(259)
-#define DB8540_PIN_C18 _GPIO(260)
-#define DB8540_PIN_C16 _GPIO(261)
-#define DB8540_PIN_B16 _GPIO(262)
-#define DB8540_PIN_D18 _GPIO(263)
-#define DB8540_PIN_C15 _GPIO(264)
-#define DB8540_PIN_C17 _GPIO(265)
-#define DB8540_PIN_B17 _GPIO(266)
-#define DB8540_PIN_D17 _GPIO(267)
-
-/*
- * The names of the pins are denoted by GPIO number and ball name, even
- * though they can be used for other things than GPIO, this is the first
- * column in the table of the data sheet and often used on schematics and
- * such.
- */
-static const struct pinctrl_pin_desc nmk_db8540_pins[] = {
- PINCTRL_PIN(DB8540_PIN_AH6, "GPIO0_AH6"),
- PINCTRL_PIN(DB8540_PIN_AG7, "GPIO1_AG7"),
- PINCTRL_PIN(DB8540_PIN_AF2, "GPIO2_AF2"),
- PINCTRL_PIN(DB8540_PIN_AD3, "GPIO3_AD3"),
- PINCTRL_PIN(DB8540_PIN_AF6, "GPIO4_AF6"),
- PINCTRL_PIN(DB8540_PIN_AG6, "GPIO5_AG6"),
- PINCTRL_PIN(DB8540_PIN_AD5, "GPIO6_AD5"),
- PINCTRL_PIN(DB8540_PIN_AF7, "GPIO7_AF7"),
- PINCTRL_PIN(DB8540_PIN_AG5, "GPIO8_AG5"),
- PINCTRL_PIN(DB8540_PIN_AH5, "GPIO9_AH5"),
- PINCTRL_PIN(DB8540_PIN_AE4, "GPIO10_AE4"),
- PINCTRL_PIN(DB8540_PIN_AD1, "GPIO11_AD1"),
- PINCTRL_PIN(DB8540_PIN_AD2, "GPIO12_AD2"),
- PINCTRL_PIN(DB8540_PIN_AC2, "GPIO13_AC2"),
- PINCTRL_PIN(DB8540_PIN_AC4, "GPIO14_AC4"),
- PINCTRL_PIN(DB8540_PIN_AC3, "GPIO15_AC3"),
- PINCTRL_PIN(DB8540_PIN_AH7, "GPIO16_AH7"),
- PINCTRL_PIN(DB8540_PIN_AE7, "GPIO17_AE7"),
- /* Hole */
- PINCTRL_PIN(DB8540_PIN_AF8, "GPIO22_AF8"),
- PINCTRL_PIN(DB8540_PIN_AH11, "GPIO23_AH11"),
- PINCTRL_PIN(DB8540_PIN_AG11, "GPIO24_AG11"),
- PINCTRL_PIN(DB8540_PIN_AF11, "GPIO25_AF11"),
- PINCTRL_PIN(DB8540_PIN_AH10, "GPIO26_AH10"),
- PINCTRL_PIN(DB8540_PIN_AG10, "GPIO27_AG10"),
- PINCTRL_PIN(DB8540_PIN_AF10, "GPIO28_AF10"),
- /* Hole */
- PINCTRL_PIN(DB8540_PIN_AD4, "GPIO33_AD4"),
- PINCTRL_PIN(DB8540_PIN_AF3, "GPIO34_AF3"),
- PINCTRL_PIN(DB8540_PIN_AF5, "GPIO35_AF5"),
- PINCTRL_PIN(DB8540_PIN_AG4, "GPIO36_AG4"),
- PINCTRL_PIN(DB8540_PIN_AF9, "GPIO37_AF9"),
- PINCTRL_PIN(DB8540_PIN_AE8, "GPIO38_AE8"),
- /* Hole */
- PINCTRL_PIN(DB8540_PIN_M26, "GPIO64_M26"),
- PINCTRL_PIN(DB8540_PIN_M25, "GPIO65_M25"),
- PINCTRL_PIN(DB8540_PIN_M27, "GPIO66_M27"),
- PINCTRL_PIN(DB8540_PIN_N25, "GPIO67_N25"),
- /* Hole */
- PINCTRL_PIN(DB8540_PIN_M28, "GPIO70_M28"),
- PINCTRL_PIN(DB8540_PIN_N26, "GPIO71_N26"),
- PINCTRL_PIN(DB8540_PIN_M22, "GPIO72_M22"),
- PINCTRL_PIN(DB8540_PIN_N22, "GPIO73_N22"),
- PINCTRL_PIN(DB8540_PIN_N27, "GPIO74_N27"),
- PINCTRL_PIN(DB8540_PIN_N28, "GPIO75_N28"),
- PINCTRL_PIN(DB8540_PIN_P22, "GPIO76_P22"),
- PINCTRL_PIN(DB8540_PIN_P28, "GPIO77_P28"),
- PINCTRL_PIN(DB8540_PIN_P26, "GPIO78_P26"),
- PINCTRL_PIN(DB8540_PIN_T22, "GPIO79_T22"),
- PINCTRL_PIN(DB8540_PIN_R27, "GPIO80_R27"),
- PINCTRL_PIN(DB8540_PIN_P27, "GPIO81_P27"),
- PINCTRL_PIN(DB8540_PIN_R26, "GPIO82_R26"),
- PINCTRL_PIN(DB8540_PIN_R25, "GPIO83_R25"),
- PINCTRL_PIN(DB8540_PIN_U22, "GPIO84_U22"),
- PINCTRL_PIN(DB8540_PIN_T27, "GPIO85_T27"),
- PINCTRL_PIN(DB8540_PIN_T25, "GPIO86_T25"),
- PINCTRL_PIN(DB8540_PIN_T26, "GPIO87_T26"),
- /* Hole */
- PINCTRL_PIN(DB8540_PIN_AF20, "GPIO116_AF20"),
- PINCTRL_PIN(DB8540_PIN_AG21, "GPIO117_AG21"),
- PINCTRL_PIN(DB8540_PIN_AH19, "GPIO118_AH19"),
- PINCTRL_PIN(DB8540_PIN_AE19, "GPIO119_AE19"),
- PINCTRL_PIN(DB8540_PIN_AG18, "GPIO120_AG18"),
- PINCTRL_PIN(DB8540_PIN_AH17, "GPIO121_AH17"),
- PINCTRL_PIN(DB8540_PIN_AF19, "GPIO122_AF19"),
- PINCTRL_PIN(DB8540_PIN_AF18, "GPIO123_AF18"),
- PINCTRL_PIN(DB8540_PIN_AE18, "GPIO124_AE18"),
- PINCTRL_PIN(DB8540_PIN_AG17, "GPIO125_AG17"),
- PINCTRL_PIN(DB8540_PIN_AF17, "GPIO126_AF17"),
- PINCTRL_PIN(DB8540_PIN_AE17, "GPIO127_AE17"),
- PINCTRL_PIN(DB8540_PIN_AC27, "GPIO128_AC27"),
- PINCTRL_PIN(DB8540_PIN_AD27, "GPIO129_AD27"),
- PINCTRL_PIN(DB8540_PIN_AE28, "GPIO130_AE28"),
- PINCTRL_PIN(DB8540_PIN_AG26, "GPIO131_AG26"),
- PINCTRL_PIN(DB8540_PIN_AF25, "GPIO132_AF25"),
- PINCTRL_PIN(DB8540_PIN_AE27, "GPIO133_AE27"),
- PINCTRL_PIN(DB8540_PIN_AF27, "GPIO134_AF27"),
- PINCTRL_PIN(DB8540_PIN_AG28, "GPIO135_AG28"),
- PINCTRL_PIN(DB8540_PIN_AF28, "GPIO136_AF28"),
- PINCTRL_PIN(DB8540_PIN_AG25, "GPIO137_AG25"),
- PINCTRL_PIN(DB8540_PIN_AG24, "GPIO138_AG24"),
- PINCTRL_PIN(DB8540_PIN_AD25, "GPIO139_AD25"),
- PINCTRL_PIN(DB8540_PIN_AH25, "GPIO140_AH25"),
- PINCTRL_PIN(DB8540_PIN_AF26, "GPIO141_AF26"),
- PINCTRL_PIN(DB8540_PIN_AF23, "GPIO142_AF23"),
- PINCTRL_PIN(DB8540_PIN_AG23, "GPIO143_AG23"),
- PINCTRL_PIN(DB8540_PIN_AE25, "GPIO144_AE25"),
- PINCTRL_PIN(DB8540_PIN_AH24, "GPIO145_AH24"),
- PINCTRL_PIN(DB8540_PIN_AJ25, "GPIO146_AJ25"),
- PINCTRL_PIN(DB8540_PIN_AG27, "GPIO147_AG27"),
- PINCTRL_PIN(DB8540_PIN_AH23, "GPIO148_AH23"),
- PINCTRL_PIN(DB8540_PIN_AE26, "GPIO149_AE26"),
- PINCTRL_PIN(DB8540_PIN_AE24, "GPIO150_AE24"),
- PINCTRL_PIN(DB8540_PIN_AJ24, "GPIO151_AJ24"),
- PINCTRL_PIN(DB8540_PIN_AE21, "GPIO152_AE21"),
- PINCTRL_PIN(DB8540_PIN_AG22, "GPIO153_AG22"),
- PINCTRL_PIN(DB8540_PIN_AF21, "GPIO154_AF21"),
- PINCTRL_PIN(DB8540_PIN_AF24, "GPIO155_AF24"),
- PINCTRL_PIN(DB8540_PIN_AH22, "GPIO156_AH22"),
- PINCTRL_PIN(DB8540_PIN_AJ23, "GPIO157_AJ23"),
- PINCTRL_PIN(DB8540_PIN_AH21, "GPIO158_AH21"),
- PINCTRL_PIN(DB8540_PIN_AG20, "GPIO159_AG20"),
- PINCTRL_PIN(DB8540_PIN_AE23, "GPIO160_AE23"),
- PINCTRL_PIN(DB8540_PIN_AH20, "GPIO161_AH20"),
- PINCTRL_PIN(DB8540_PIN_AG19, "GPIO162_AG19"),
- PINCTRL_PIN(DB8540_PIN_AF22, "GPIO163_AF22"),
- PINCTRL_PIN(DB8540_PIN_AJ21, "GPIO164_AJ21"),
- PINCTRL_PIN(DB8540_PIN_AD26, "GPIO165_AD26"),
- PINCTRL_PIN(DB8540_PIN_AD28, "GPIO166_AD28"),
- PINCTRL_PIN(DB8540_PIN_AC28, "GPIO167_AC28"),
- PINCTRL_PIN(DB8540_PIN_AC26, "GPIO168_AC26"),
- /* Hole */
- PINCTRL_PIN(DB8540_PIN_J3, "GPIO192_J3"),
- PINCTRL_PIN(DB8540_PIN_H1, "GPIO193_H1"),
- PINCTRL_PIN(DB8540_PIN_J2, "GPIO194_J2"),
- PINCTRL_PIN(DB8540_PIN_H2, "GPIO195_H2"),
- PINCTRL_PIN(DB8540_PIN_H3, "GPIO196_H3"),
- PINCTRL_PIN(DB8540_PIN_H4, "GPIO197_H4"),
- PINCTRL_PIN(DB8540_PIN_G2, "GPIO198_G2"),
- PINCTRL_PIN(DB8540_PIN_G3, "GPIO199_G3"),
- PINCTRL_PIN(DB8540_PIN_G4, "GPIO200_G4"),
- PINCTRL_PIN(DB8540_PIN_F2, "GPIO201_F2"),
- PINCTRL_PIN(DB8540_PIN_C6, "GPIO202_C6"),
- PINCTRL_PIN(DB8540_PIN_B6, "GPIO203_B6"),
- PINCTRL_PIN(DB8540_PIN_B7, "GPIO204_B7"),
- PINCTRL_PIN(DB8540_PIN_A7, "GPIO205_A7"),
- PINCTRL_PIN(DB8540_PIN_D7, "GPIO206_D7"),
- PINCTRL_PIN(DB8540_PIN_D8, "GPIO207_D8"),
- PINCTRL_PIN(DB8540_PIN_F3, "GPIO208_F3"),
- PINCTRL_PIN(DB8540_PIN_E2, "GPIO209_E2"),
- PINCTRL_PIN(DB8540_PIN_C7, "GPIO210_C7"),
- PINCTRL_PIN(DB8540_PIN_B8, "GPIO211_B8"),
- PINCTRL_PIN(DB8540_PIN_C10, "GPIO212_C10"),
- PINCTRL_PIN(DB8540_PIN_C8, "GPIO213_C8"),
- PINCTRL_PIN(DB8540_PIN_C9, "GPIO214_C9"),
- /* Hole */
- PINCTRL_PIN(DB8540_PIN_B9, "GPIO219_B9"),
- PINCTRL_PIN(DB8540_PIN_A10, "GPIO220_A10"),
- PINCTRL_PIN(DB8540_PIN_D9, "GPIO221_D9"),
- PINCTRL_PIN(DB8540_PIN_B11, "GPIO222_B11"),
- PINCTRL_PIN(DB8540_PIN_B10, "GPIO223_B10"),
- PINCTRL_PIN(DB8540_PIN_E10, "GPIO224_E10"),
- PINCTRL_PIN(DB8540_PIN_B12, "GPIO225_B12"),
- PINCTRL_PIN(DB8540_PIN_D10, "GPIO226_D10"),
- PINCTRL_PIN(DB8540_PIN_D11, "GPIO227_D11"),
- PINCTRL_PIN(DB8540_PIN_AJ6, "GPIO228_AJ6"),
- PINCTRL_PIN(DB8540_PIN_B13, "GPIO229_B13"),
- PINCTRL_PIN(DB8540_PIN_C12, "GPIO230_C12"),
- PINCTRL_PIN(DB8540_PIN_B14, "GPIO231_B14"),
- PINCTRL_PIN(DB8540_PIN_E11, "GPIO232_E11"),
- /* Hole */
- PINCTRL_PIN(DB8540_PIN_D12, "GPIO256_D12"),
- PINCTRL_PIN(DB8540_PIN_D15, "GPIO257_D15"),
- PINCTRL_PIN(DB8540_PIN_C13, "GPIO258_C13"),
- PINCTRL_PIN(DB8540_PIN_C14, "GPIO259_C14"),
- PINCTRL_PIN(DB8540_PIN_C18, "GPIO260_C18"),
- PINCTRL_PIN(DB8540_PIN_C16, "GPIO261_C16"),
- PINCTRL_PIN(DB8540_PIN_B16, "GPIO262_B16"),
- PINCTRL_PIN(DB8540_PIN_D18, "GPIO263_D18"),
- PINCTRL_PIN(DB8540_PIN_C15, "GPIO264_C15"),
- PINCTRL_PIN(DB8540_PIN_C17, "GPIO265_C17"),
- PINCTRL_PIN(DB8540_PIN_B17, "GPIO266_B17"),
- PINCTRL_PIN(DB8540_PIN_D17, "GPIO267_D17"),
-};
-
-/*
- * Read the pin group names like this:
- * u0_a_1 = first groups of pins for uart0 on alt function a
- * i2c2_b_2 = second group of pins for i2c2 on alt function b
- *
- * The groups are arranged as sets per altfunction column, so we can
- * mux in one group at a time by selecting the same altfunction for them
- * all. When functions require pins on different altfunctions, you need
- * to combine several groups.
- */
-
-/* Altfunction A column */
-static const unsigned u0_a_1_pins[] = { DB8540_PIN_AH6, DB8540_PIN_AG7,
- DB8540_PIN_AF2, DB8540_PIN_AD3 };
-static const unsigned u1rxtx_a_1_pins[] = { DB8540_PIN_AF6, DB8540_PIN_AG6 };
-static const unsigned u1ctsrts_a_1_pins[] = { DB8540_PIN_AD5, DB8540_PIN_AF7 };
-/* Image processor I2C line, this is driven by image processor firmware */
-static const unsigned ipi2c_a_1_pins[] = { DB8540_PIN_AG5, DB8540_PIN_AH5 };
-static const unsigned ipi2c_a_2_pins[] = { DB8540_PIN_AE4, DB8540_PIN_AD1 };
-/* MSP0 can only be on these pins, but TXD and RXD can be flipped */
-static const unsigned msp0txrx_a_1_pins[] = { DB8540_PIN_AD2, DB8540_PIN_AC3 };
-static const unsigned msp0tfstck_a_1_pins[] = { DB8540_PIN_AC2,
- DB8540_PIN_AC4 };
-static const unsigned msp0rfsrck_a_1_pins[] = { DB8540_PIN_AH7,
- DB8540_PIN_AE7 };
-/* Basic pins of the MMC/SD card 0 interface */
-static const unsigned mc0_a_1_pins[] = { DB8540_PIN_AH11, DB8540_PIN_AG11,
- DB8540_PIN_AF11, DB8540_PIN_AH10, DB8540_PIN_AG10, DB8540_PIN_AF10};
-/* MSP1 can only be on these pins, but TXD and RXD can be flipped */
-static const unsigned msp1txrx_a_1_pins[] = { DB8540_PIN_AD4, DB8540_PIN_AG4 };
-static const unsigned msp1_a_1_pins[] = { DB8540_PIN_AF3, DB8540_PIN_AF5 };
-
-static const unsigned modobsclk_a_1_pins[] = { DB8540_PIN_AF9 };
-static const unsigned clkoutreq_a_1_pins[] = { DB8540_PIN_AE8 };
-/* LCD interface */
-static const unsigned lcdb_a_1_pins[] = { DB8540_PIN_M26, DB8540_PIN_M25,
- DB8540_PIN_M27, DB8540_PIN_N25 };
-static const unsigned lcdvsi0_a_1_pins[] = { DB8540_PIN_AJ24 };
-static const unsigned lcdvsi1_a_1_pins[] = { DB8540_PIN_AE21 };
-static const unsigned lcd_d0_d7_a_1_pins[] = { DB8540_PIN_M28, DB8540_PIN_N26,
- DB8540_PIN_M22, DB8540_PIN_N22, DB8540_PIN_N27, DB8540_PIN_N28,
- DB8540_PIN_P22, DB8540_PIN_P28 };
-/* D8 thru D11 often used as TVOUT lines */
-static const unsigned lcd_d8_d11_a_1_pins[] = { DB8540_PIN_P26, DB8540_PIN_T22,
- DB8540_PIN_R27, DB8540_PIN_P27 };
-static const unsigned lcd_d12_d23_a_1_pins[] = { DB8540_PIN_R26, DB8540_PIN_R25,
- DB8540_PIN_U22, DB8540_PIN_T27, DB8540_PIN_AG22, DB8540_PIN_AF21,
- DB8540_PIN_AF24, DB8540_PIN_AH22, DB8540_PIN_AJ23, DB8540_PIN_AH21,
- DB8540_PIN_AG20, DB8540_PIN_AE23 };
-static const unsigned kp_a_1_pins[] = { DB8540_PIN_AH20, DB8540_PIN_AG19,
- DB8540_PIN_AF22, DB8540_PIN_AJ21, DB8540_PIN_T25, DB8540_PIN_T26 };
-/* MC2 has 8 data lines and no direction control, so only for (e)MMC */
-static const unsigned mc2_a_1_pins[] = { DB8540_PIN_AC27, DB8540_PIN_AD27,
- DB8540_PIN_AE28, DB8540_PIN_AG26, DB8540_PIN_AF25, DB8540_PIN_AE27,
- DB8540_PIN_AF27, DB8540_PIN_AG28, DB8540_PIN_AF28, DB8540_PIN_AG25,
- DB8540_PIN_AG24 };
-static const unsigned ssp1_a_1_pins[] = { DB8540_PIN_AD25, DB8540_PIN_AH25,
- DB8540_PIN_AF26, DB8540_PIN_AF23 };
-static const unsigned ssp0_a_1_pins[] = { DB8540_PIN_AG23, DB8540_PIN_AE25,
- DB8540_PIN_AH24, DB8540_PIN_AJ25 };
-static const unsigned i2c0_a_1_pins[] = { DB8540_PIN_AG27, DB8540_PIN_AH23 };
-/*
- * Image processor GPIO pins are named "ipgpio" and have their own
- * numberspace
- */
-static const unsigned ipgpio0_a_1_pins[] = { DB8540_PIN_AE26 };
-static const unsigned ipgpio1_a_1_pins[] = { DB8540_PIN_AE24 };
-/* modem i2s interface */
-static const unsigned modi2s_a_1_pins[] = { DB8540_PIN_AD26, DB8540_PIN_AD28,
- DB8540_PIN_AC28, DB8540_PIN_AC26 };
-static const unsigned spi2_a_1_pins[] = { DB8540_PIN_AF20, DB8540_PIN_AG21,
- DB8540_PIN_AH19, DB8540_PIN_AE19 };
-static const unsigned u2txrx_a_1_pins[] = { DB8540_PIN_AG18, DB8540_PIN_AH17 };
-static const unsigned u2ctsrts_a_1_pins[] = { DB8540_PIN_AF19,
- DB8540_PIN_AF18 };
-static const unsigned modsmb_a_1_pins[] = { DB8540_PIN_AF17, DB8540_PIN_AE17 };
-static const unsigned msp2sck_a_1_pins[] = { DB8540_PIN_J3 };
-static const unsigned msp2txdtcktfs_a_1_pins[] = { DB8540_PIN_H1, DB8540_PIN_J2,
- DB8540_PIN_H2 };
-static const unsigned msp2rxd_a_1_pins[] = { DB8540_PIN_H3 };
-static const unsigned mc4_a_1_pins[] = { DB8540_PIN_H4, DB8540_PIN_G2,
- DB8540_PIN_G3, DB8540_PIN_G4, DB8540_PIN_F2, DB8540_PIN_C6,
- DB8540_PIN_B6, DB8540_PIN_B7, DB8540_PIN_A7, DB8540_PIN_D7,
- DB8540_PIN_D8 };
-static const unsigned mc1_a_1_pins[] = { DB8540_PIN_F3, DB8540_PIN_E2,
- DB8540_PIN_C7, DB8540_PIN_B8, DB8540_PIN_C10, DB8540_PIN_C8,
- DB8540_PIN_C9 };
-/* mc1_a_2_pins exclude MC1_FBCLK */
-static const unsigned mc1_a_2_pins[] = { DB8540_PIN_F3, DB8540_PIN_C7,
- DB8540_PIN_B8, DB8540_PIN_C10, DB8540_PIN_C8,
- DB8540_PIN_C9 };
-static const unsigned hsir_a_1_pins[] = { DB8540_PIN_B9, DB8540_PIN_A10,
- DB8540_PIN_D9 };
-static const unsigned hsit_a_1_pins[] = { DB8540_PIN_B11, DB8540_PIN_B10,
- DB8540_PIN_E10, DB8540_PIN_B12, DB8540_PIN_D10 };
-static const unsigned hsit_a_2_pins[] = { DB8540_PIN_B11, DB8540_PIN_B10,
- DB8540_PIN_E10, DB8540_PIN_B12 };
-static const unsigned clkout1_a_1_pins[] = { DB8540_PIN_D11 };
-static const unsigned clkout1_a_2_pins[] = { DB8540_PIN_B13 };
-static const unsigned clkout2_a_1_pins[] = { DB8540_PIN_AJ6 };
-static const unsigned clkout2_a_2_pins[] = { DB8540_PIN_C12 };
-static const unsigned msp4_a_1_pins[] = { DB8540_PIN_B14, DB8540_PIN_E11 };
-static const unsigned usb_a_1_pins[] = { DB8540_PIN_D12, DB8540_PIN_D15,
- DB8540_PIN_C13, DB8540_PIN_C14, DB8540_PIN_C18, DB8540_PIN_C16,
- DB8540_PIN_B16, DB8540_PIN_D18, DB8540_PIN_C15, DB8540_PIN_C17,
- DB8540_PIN_B17, DB8540_PIN_D17 };
-/* Altfunction B colum */
-static const unsigned apetrig_b_1_pins[] = { DB8540_PIN_AH6, DB8540_PIN_AG7 };
-static const unsigned modtrig_b_1_pins[] = { DB8540_PIN_AF2, DB8540_PIN_AD3 };
-static const unsigned i2c4_b_1_pins[] = { DB8540_PIN_AF6, DB8540_PIN_AG6 };
-static const unsigned i2c1_b_1_pins[] = { DB8540_PIN_AD5, DB8540_PIN_AF7 };
-static const unsigned i2c2_b_1_pins[] = { DB8540_PIN_AG5, DB8540_PIN_AH5 };
-static const unsigned i2c2_b_2_pins[] = { DB8540_PIN_AE4, DB8540_PIN_AD1 };
-static const unsigned msp0txrx_b_1_pins[] = { DB8540_PIN_AD2, DB8540_PIN_AC3 };
-static const unsigned i2c1_b_2_pins[] = { DB8540_PIN_AH7, DB8540_PIN_AE7 };
-static const unsigned stmmod_b_1_pins[] = { DB8540_PIN_AH11, DB8540_PIN_AF11,
- DB8540_PIN_AH10, DB8540_PIN_AG10, DB8540_PIN_AF10 };
-static const unsigned moduartstmmux_b_1_pins[] = { DB8540_PIN_AG11 };
-static const unsigned msp1txrx_b_1_pins[] = { DB8540_PIN_AD4, DB8540_PIN_AG4 };
-static const unsigned kp_b_1_pins[] = { DB8540_PIN_AJ24, DB8540_PIN_AE21,
- DB8540_PIN_M26, DB8540_PIN_M25, DB8540_PIN_M27, DB8540_PIN_N25,
- DB8540_PIN_M28, DB8540_PIN_N26, DB8540_PIN_M22, DB8540_PIN_N22,
- DB8540_PIN_N27, DB8540_PIN_N28, DB8540_PIN_P22, DB8540_PIN_P28,
- DB8540_PIN_P26, DB8540_PIN_T22, DB8540_PIN_R27, DB8540_PIN_P27,
- DB8540_PIN_R26, DB8540_PIN_R25 };
-static const unsigned u2txrx_b_1_pins[] = { DB8540_PIN_U22, DB8540_PIN_T27 };
-static const unsigned sm_b_1_pins[] = { DB8540_PIN_AG22, DB8540_PIN_AF21,
- DB8540_PIN_AF24, DB8540_PIN_AH22, DB8540_PIN_AJ23, DB8540_PIN_AH21,
- DB8540_PIN_AG20, DB8540_PIN_AE23, DB8540_PIN_AH20, DB8540_PIN_AF22,
- DB8540_PIN_AJ21, DB8540_PIN_AC27, DB8540_PIN_AD27, DB8540_PIN_AE28,
- DB8540_PIN_AG26, DB8540_PIN_AF25, DB8540_PIN_AE27, DB8540_PIN_AF27,
- DB8540_PIN_AG28, DB8540_PIN_AF28, DB8540_PIN_AG25, DB8540_PIN_AG24,
- DB8540_PIN_AD25 };
-static const unsigned smcs0_b_1_pins[] = { DB8540_PIN_AG19 };
-static const unsigned smcs1_b_1_pins[] = { DB8540_PIN_AE26 };
-static const unsigned ipgpio7_b_1_pins[] = { DB8540_PIN_AH25 };
-static const unsigned ipgpio2_b_1_pins[] = { DB8540_PIN_AF26 };
-static const unsigned ipgpio3_b_1_pins[] = { DB8540_PIN_AF23 };
-static const unsigned i2c6_b_1_pins[] = { DB8540_PIN_AG23, DB8540_PIN_AE25 };
-static const unsigned i2c5_b_1_pins[] = { DB8540_PIN_AH24, DB8540_PIN_AJ25 };
-static const unsigned u3txrx_b_1_pins[] = { DB8540_PIN_AF20, DB8540_PIN_AG21 };
-static const unsigned u3ctsrts_b_1_pins[] = { DB8540_PIN_AH19,
- DB8540_PIN_AE19 };
-static const unsigned i2c5_b_2_pins[] = { DB8540_PIN_AG18, DB8540_PIN_AH17 };
-static const unsigned i2c4_b_2_pins[] = { DB8540_PIN_AF19, DB8540_PIN_AF18 };
-static const unsigned u4txrx_b_1_pins[] = { DB8540_PIN_AE18, DB8540_PIN_AG17 };
-static const unsigned u4ctsrts_b_1_pins[] = { DB8540_PIN_AF17,
- DB8540_PIN_AE17 };
-static const unsigned ddrtrig_b_1_pins[] = { DB8540_PIN_J3 };
-static const unsigned msp4_b_1_pins[] = { DB8540_PIN_H3 };
-static const unsigned pwl_b_1_pins[] = { DB8540_PIN_C6 };
-static const unsigned spi1_b_1_pins[] = { DB8540_PIN_E2, DB8540_PIN_C10,
- DB8540_PIN_C8, DB8540_PIN_C9 };
-static const unsigned mc3_b_1_pins[] = { DB8540_PIN_B9, DB8540_PIN_A10,
- DB8540_PIN_D9, DB8540_PIN_B11, DB8540_PIN_B10, DB8540_PIN_E10,
- DB8540_PIN_B12 };
-static const unsigned pwl_b_2_pins[] = { DB8540_PIN_D10 };
-static const unsigned pwl_b_3_pins[] = { DB8540_PIN_B13 };
-static const unsigned pwl_b_4_pins[] = { DB8540_PIN_C12 };
-static const unsigned u2txrx_b_2_pins[] = { DB8540_PIN_B17, DB8540_PIN_D17 };
-
-/* Altfunction C column */
-static const unsigned ipgpio6_c_1_pins[] = { DB8540_PIN_AG6 };
-static const unsigned ipgpio0_c_1_pins[] = { DB8540_PIN_AD5 };
-static const unsigned ipgpio1_c_1_pins[] = { DB8540_PIN_AF7 };
-static const unsigned ipgpio3_c_1_pins[] = { DB8540_PIN_AE4 };
-static const unsigned ipgpio2_c_1_pins[] = { DB8540_PIN_AD1 };
-static const unsigned u0_c_1_pins[] = { DB8540_PIN_AD4, DB8540_PIN_AF3,
- DB8540_PIN_AF5, DB8540_PIN_AG4 };
-static const unsigned smcleale_c_1_pins[] = { DB8540_PIN_AJ24,
- DB8540_PIN_AE21 };
-static const unsigned ipgpio4_c_1_pins[] = { DB8540_PIN_M26 };
-static const unsigned ipgpio5_c_1_pins[] = { DB8540_PIN_M25 };
-static const unsigned ipgpio6_c_2_pins[] = { DB8540_PIN_M27 };
-static const unsigned ipgpio7_c_1_pins[] = { DB8540_PIN_N25 };
-static const unsigned stmape_c_1_pins[] = { DB8540_PIN_M28, DB8540_PIN_N26,
- DB8540_PIN_M22, DB8540_PIN_N22, DB8540_PIN_N27 };
-static const unsigned u2rxtx_c_1_pins[] = { DB8540_PIN_N28, DB8540_PIN_P22 };
-static const unsigned modobsresout_c_1_pins[] = { DB8540_PIN_P28 };
-static const unsigned ipgpio2_c_2_pins[] = { DB8540_PIN_P26 };
-static const unsigned ipgpio3_c_2_pins[] = { DB8540_PIN_T22 };
-static const unsigned ipgpio4_c_2_pins[] = { DB8540_PIN_R27 };
-static const unsigned ipgpio5_c_2_pins[] = { DB8540_PIN_P27 };
-static const unsigned modaccgpo_c_1_pins[] = { DB8540_PIN_R26, DB8540_PIN_R25,
- DB8540_PIN_U22 };
-static const unsigned modobspwrrst_c_1_pins[] = { DB8540_PIN_T27 };
-static const unsigned mc5_c_1_pins[] = { DB8540_PIN_AG22, DB8540_PIN_AF21,
- DB8540_PIN_AF24, DB8540_PIN_AH22, DB8540_PIN_AJ23, DB8540_PIN_AH21,
- DB8540_PIN_AG20, DB8540_PIN_AE23, DB8540_PIN_AH20, DB8540_PIN_AF22,
- DB8540_PIN_AJ21};
-static const unsigned smps0_c_1_pins[] = { DB8540_PIN_AG19 };
-static const unsigned moduart1_c_1_pins[] = { DB8540_PIN_T25, DB8540_PIN_T26 };
-static const unsigned mc2rstn_c_1_pins[] = { DB8540_PIN_AE28 };
-static const unsigned i2c5_c_1_pins[] = { DB8540_PIN_AG28, DB8540_PIN_AF28 };
-static const unsigned ipgpio0_c_2_pins[] = { DB8540_PIN_AG25 };
-static const unsigned ipgpio1_c_2_pins[] = { DB8540_PIN_AG24 };
-static const unsigned kp_c_1_pins[] = { DB8540_PIN_AD25, DB8540_PIN_AH25,
- DB8540_PIN_AF26, DB8540_PIN_AF23 };
-static const unsigned modrf_c_1_pins[] = { DB8540_PIN_AG23, DB8540_PIN_AE25,
- DB8540_PIN_AH24 };
-static const unsigned smps1_c_1_pins[] = { DB8540_PIN_AE26 };
-static const unsigned i2c5_c_2_pins[] = { DB8540_PIN_AH19, DB8540_PIN_AE19 };
-static const unsigned u4ctsrts_c_1_pins[] = { DB8540_PIN_AG18,
- DB8540_PIN_AH17 };
-static const unsigned u3rxtx_c_1_pins[] = { DB8540_PIN_AF19, DB8540_PIN_AF18 };
-static const unsigned msp4_c_1_pins[] = { DB8540_PIN_J3 };
-static const unsigned mc4rstn_c_1_pins[] = { DB8540_PIN_C6 };
-static const unsigned spi0_c_1_pins[] = { DB8540_PIN_A10, DB8540_PIN_B10,
- DB8540_PIN_E10, DB8540_PIN_B12 };
-static const unsigned i2c3_c_1_pins[] = { DB8540_PIN_B13, DB8540_PIN_C12 };
-
-/* Other alt C1 column */
-static const unsigned spi3_oc1_1_pins[] = { DB8540_PIN_AG5, DB8540_PIN_AH5,
- DB8540_PIN_AE4, DB8540_PIN_AD1 };
-static const unsigned stmape_oc1_1_pins[] = { DB8540_PIN_AH11, DB8540_PIN_AF11,
- DB8540_PIN_AH10, DB8540_PIN_AG10, DB8540_PIN_AF10 };
-static const unsigned u2_oc1_1_pins[] = { DB8540_PIN_AG11 };
-static const unsigned remap0_oc1_1_pins[] = { DB8540_PIN_AJ24 };
-static const unsigned remap1_oc1_1_pins[] = { DB8540_PIN_AE21 };
-static const unsigned modobsrefclk_oc1_1_pins[] = { DB8540_PIN_M26 };
-static const unsigned modobspwrctrl_oc1_1_pins[] = { DB8540_PIN_M25 };
-static const unsigned modobsclkout_oc1_1_pins[] = { DB8540_PIN_M27 };
-static const unsigned moduart1_oc1_1_pins[] = { DB8540_PIN_N25 };
-static const unsigned modprcmudbg_oc1_1_pins[] = { DB8540_PIN_M28,
- DB8540_PIN_N26, DB8540_PIN_M22, DB8540_PIN_N22, DB8540_PIN_N27,
- DB8540_PIN_P22, DB8540_PIN_P28, DB8540_PIN_P26, DB8540_PIN_T22,
- DB8540_PIN_R26, DB8540_PIN_R25, DB8540_PIN_U22, DB8540_PIN_T27,
- DB8540_PIN_AH20, DB8540_PIN_AG19, DB8540_PIN_AF22, DB8540_PIN_AJ21,
- DB8540_PIN_T25};
-static const unsigned modobsresout_oc1_1_pins[] = { DB8540_PIN_N28 };
-static const unsigned modaccgpo_oc1_1_pins[] = { DB8540_PIN_R27, DB8540_PIN_P27,
- DB8540_PIN_T26 };
-static const unsigned kp_oc1_1_pins[] = { DB8540_PIN_AG22, DB8540_PIN_AF21,
- DB8540_PIN_AF24, DB8540_PIN_AH22, DB8540_PIN_AJ23, DB8540_PIN_AH21,
- DB8540_PIN_AG20, DB8540_PIN_AE23 };
-static const unsigned modxmip_oc1_1_pins[] = { DB8540_PIN_AD25, DB8540_PIN_AH25,
- DB8540_PIN_AG23, DB8540_PIN_AE25 };
-static const unsigned i2c6_oc1_1_pins[] = { DB8540_PIN_AE26, DB8540_PIN_AE24 };
-static const unsigned u2txrx_oc1_1_pins[] = { DB8540_PIN_B7, DB8540_PIN_A7 };
-static const unsigned u2ctsrts_oc1_1_pins[] = { DB8540_PIN_D7, DB8540_PIN_D8 };
-
-/* Other alt C2 column */
-static const unsigned sbag_oc2_1_pins[] = { DB8540_PIN_AH11, DB8540_PIN_AG11,
- DB8540_PIN_AF11, DB8540_PIN_AH10, DB8540_PIN_AG10, DB8540_PIN_AF10 };
-static const unsigned hxclk_oc2_1_pins[] = { DB8540_PIN_M25 };
-static const unsigned modaccuart_oc2_1_pins[] = { DB8540_PIN_N25 };
-static const unsigned stmmod_oc2_1_pins[] = { DB8540_PIN_M28, DB8540_PIN_N26,
- DB8540_PIN_M22, DB8540_PIN_N22, DB8540_PIN_N27 };
-static const unsigned moduartstmmux_oc2_1_pins[] = { DB8540_PIN_N28 };
-static const unsigned hxgpio_oc2_1_pins[] = { DB8540_PIN_P22, DB8540_PIN_P28,
- DB8540_PIN_P26, DB8540_PIN_T22, DB8540_PIN_R27, DB8540_PIN_P27,
- DB8540_PIN_R26, DB8540_PIN_R25 };
-static const unsigned sbag_oc2_2_pins[] = { DB8540_PIN_U22, DB8540_PIN_T27,
- DB8540_PIN_AG22, DB8540_PIN_AF21, DB8540_PIN_AF24, DB8540_PIN_AH22 };
-static const unsigned modobsservice_oc2_1_pins[] = { DB8540_PIN_AJ23 };
-static const unsigned moduart0_oc2_1_pins[] = { DB8540_PIN_AG20,
- DB8540_PIN_AE23 };
-static const unsigned stmape_oc2_1_pins[] = { DB8540_PIN_AH20, DB8540_PIN_AG19,
- DB8540_PIN_AF22, DB8540_PIN_AJ21, DB8540_PIN_T25 };
-static const unsigned u2_oc2_1_pins[] = { DB8540_PIN_T26, DB8540_PIN_AH21 };
-static const unsigned modxmip_oc2_1_pins[] = { DB8540_PIN_AE26,
- DB8540_PIN_AE24 };
-
-/* Other alt C3 column */
-static const unsigned modaccgpo_oc3_1_pins[] = { DB8540_PIN_AG11 };
-static const unsigned tpui_oc3_1_pins[] = { DB8540_PIN_M26, DB8540_PIN_M25,
- DB8540_PIN_M27, DB8540_PIN_N25, DB8540_PIN_M28, DB8540_PIN_N26,
- DB8540_PIN_M22, DB8540_PIN_N22, DB8540_PIN_N27, DB8540_PIN_N28,
- DB8540_PIN_P22, DB8540_PIN_P28, DB8540_PIN_P26, DB8540_PIN_T22,
- DB8540_PIN_R27, DB8540_PIN_P27, DB8540_PIN_R26, DB8540_PIN_R25,
- DB8540_PIN_U22, DB8540_PIN_T27, DB8540_PIN_AG22, DB8540_PIN_AF21,
- DB8540_PIN_AF24, DB8540_PIN_AH22, DB8540_PIN_AJ23, DB8540_PIN_AH21,
- DB8540_PIN_AG20, DB8540_PIN_AE23, DB8540_PIN_AH20, DB8540_PIN_AG19,
- DB8540_PIN_AF22, DB8540_PIN_AJ21, DB8540_PIN_T25, DB8540_PIN_T26 };
-
-/* Other alt C4 column */
-static const unsigned hwobs_oc4_1_pins[] = { DB8540_PIN_M26, DB8540_PIN_M25,
- DB8540_PIN_M27, DB8540_PIN_N25, DB8540_PIN_M28, DB8540_PIN_N26,
- DB8540_PIN_M22, DB8540_PIN_N22, DB8540_PIN_N27, DB8540_PIN_N28,
- DB8540_PIN_P22, DB8540_PIN_P28, DB8540_PIN_P26, DB8540_PIN_T22,
- DB8540_PIN_R27, DB8540_PIN_P27, DB8540_PIN_R26, DB8540_PIN_R25 };
-static const unsigned moduart1txrx_oc4_1_pins[] = { DB8540_PIN_U22,
- DB8540_PIN_T27 };
-static const unsigned moduart1rtscts_oc4_1_pins[] = { DB8540_PIN_AG22,
- DB8540_PIN_AF21 };
-static const unsigned modaccuarttxrx_oc4_1_pins[] = { DB8540_PIN_AF24,
- DB8540_PIN_AH22 };
-static const unsigned modaccuartrtscts_oc4_1_pins[] = { DB8540_PIN_AJ23,
- DB8540_PIN_AH21 };
-static const unsigned stmmod_oc4_1_pins[] = { DB8540_PIN_AH20, DB8540_PIN_AG19,
- DB8540_PIN_AF22, DB8540_PIN_AJ21, DB8540_PIN_T25 };
-static const unsigned moduartstmmux_oc4_1_pins[] = { DB8540_PIN_T26 };
-
-#define DB8540_PIN_GROUP(a, b) { .name = #a, .pins = a##_pins, \
- .npins = ARRAY_SIZE(a##_pins), .altsetting = b }
-
-static const struct nmk_pingroup nmk_db8540_groups[] = {
- /* Altfunction A column */
- DB8540_PIN_GROUP(u0_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(u1rxtx_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(u1ctsrts_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(ipi2c_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(ipi2c_a_2, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(msp0txrx_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(msp0tfstck_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(msp0rfsrck_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(mc0_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(msp1txrx_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(msp1_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(modobsclk_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(clkoutreq_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(lcdb_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(lcdvsi0_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(lcdvsi1_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(lcd_d0_d7_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(lcd_d8_d11_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(lcd_d12_d23_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(kp_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(mc2_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(ssp1_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(ssp0_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(i2c0_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(ipgpio0_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(ipgpio1_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(modi2s_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(spi2_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(u2txrx_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(u2ctsrts_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(modsmb_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(msp2sck_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(msp2txdtcktfs_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(msp2rxd_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(mc4_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(mc1_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(hsir_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(hsit_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(hsit_a_2, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(clkout1_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(clkout1_a_2, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(clkout2_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(clkout2_a_2, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(msp4_a_1, NMK_GPIO_ALT_A),
- DB8540_PIN_GROUP(usb_a_1, NMK_GPIO_ALT_A),
- /* Altfunction B column */
- DB8540_PIN_GROUP(apetrig_b_1, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(modtrig_b_1, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(i2c4_b_1, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(i2c1_b_1, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(i2c2_b_1, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(i2c2_b_2, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(msp0txrx_b_1, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(i2c1_b_2, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(stmmod_b_1, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(moduartstmmux_b_1, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(msp1txrx_b_1, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(kp_b_1, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(u2txrx_b_1, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(sm_b_1, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(smcs0_b_1, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(smcs1_b_1, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(ipgpio7_b_1, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(ipgpio2_b_1, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(ipgpio3_b_1, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(i2c6_b_1, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(i2c5_b_1, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(u3txrx_b_1, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(u3ctsrts_b_1, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(i2c5_b_2, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(i2c4_b_2, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(u4txrx_b_1, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(u4ctsrts_b_1, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(ddrtrig_b_1, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(msp4_b_1, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(pwl_b_1, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(spi1_b_1, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(mc3_b_1, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(pwl_b_2, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(pwl_b_3, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(pwl_b_4, NMK_GPIO_ALT_B),
- DB8540_PIN_GROUP(u2txrx_b_2, NMK_GPIO_ALT_B),
- /* Altfunction C column */
- DB8540_PIN_GROUP(ipgpio6_c_1, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(ipgpio0_c_1, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(ipgpio1_c_1, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(ipgpio3_c_1, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(ipgpio2_c_1, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(u0_c_1, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(smcleale_c_1, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(ipgpio4_c_1, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(ipgpio5_c_1, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(ipgpio6_c_2, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(ipgpio7_c_1, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(stmape_c_1, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(u2rxtx_c_1, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(modobsresout_c_1, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(ipgpio2_c_2, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(ipgpio3_c_2, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(ipgpio4_c_2, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(ipgpio5_c_2, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(modaccgpo_c_1, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(modobspwrrst_c_1, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(mc5_c_1, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(smps0_c_1, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(moduart1_c_1, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(mc2rstn_c_1, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(i2c5_c_1, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(ipgpio0_c_2, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(ipgpio1_c_2, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(kp_c_1, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(modrf_c_1, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(smps1_c_1, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(i2c5_c_2, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(u4ctsrts_c_1, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(u3rxtx_c_1, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(msp4_c_1, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(mc4rstn_c_1, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(spi0_c_1, NMK_GPIO_ALT_C),
- DB8540_PIN_GROUP(i2c3_c_1, NMK_GPIO_ALT_C),
-
- /* Other alt C1 column */
- DB8540_PIN_GROUP(spi3_oc1_1, NMK_GPIO_ALT_C1),
- DB8540_PIN_GROUP(stmape_oc1_1, NMK_GPIO_ALT_C1),
- DB8540_PIN_GROUP(u2_oc1_1, NMK_GPIO_ALT_C1),
- DB8540_PIN_GROUP(remap0_oc1_1, NMK_GPIO_ALT_C1),
- DB8540_PIN_GROUP(remap1_oc1_1, NMK_GPIO_ALT_C1),
- DB8540_PIN_GROUP(modobsrefclk_oc1_1, NMK_GPIO_ALT_C1),
- DB8540_PIN_GROUP(modobspwrctrl_oc1_1, NMK_GPIO_ALT_C1),
- DB8540_PIN_GROUP(modobsclkout_oc1_1, NMK_GPIO_ALT_C1),
- DB8540_PIN_GROUP(moduart1_oc1_1, NMK_GPIO_ALT_C1),
- DB8540_PIN_GROUP(modprcmudbg_oc1_1, NMK_GPIO_ALT_C1),
- DB8540_PIN_GROUP(modobsresout_oc1_1, NMK_GPIO_ALT_C1),
- DB8540_PIN_GROUP(modaccgpo_oc1_1, NMK_GPIO_ALT_C1),
- DB8540_PIN_GROUP(kp_oc1_1, NMK_GPIO_ALT_C1),
- DB8540_PIN_GROUP(modxmip_oc1_1, NMK_GPIO_ALT_C1),
- DB8540_PIN_GROUP(i2c6_oc1_1, NMK_GPIO_ALT_C1),
- DB8540_PIN_GROUP(u2txrx_oc1_1, NMK_GPIO_ALT_C1),
- DB8540_PIN_GROUP(u2ctsrts_oc1_1, NMK_GPIO_ALT_C1),
-
- /* Other alt C2 column */
- DB8540_PIN_GROUP(sbag_oc2_1, NMK_GPIO_ALT_C2),
- DB8540_PIN_GROUP(hxclk_oc2_1, NMK_GPIO_ALT_C2),
- DB8540_PIN_GROUP(modaccuart_oc2_1, NMK_GPIO_ALT_C2),
- DB8540_PIN_GROUP(stmmod_oc2_1, NMK_GPIO_ALT_C2),
- DB8540_PIN_GROUP(moduartstmmux_oc2_1, NMK_GPIO_ALT_C2),
- DB8540_PIN_GROUP(hxgpio_oc2_1, NMK_GPIO_ALT_C2),
- DB8540_PIN_GROUP(sbag_oc2_2, NMK_GPIO_ALT_C2),
- DB8540_PIN_GROUP(modobsservice_oc2_1, NMK_GPIO_ALT_C2),
- DB8540_PIN_GROUP(moduart0_oc2_1, NMK_GPIO_ALT_C2),
- DB8540_PIN_GROUP(stmape_oc2_1, NMK_GPIO_ALT_C2),
- DB8540_PIN_GROUP(u2_oc2_1, NMK_GPIO_ALT_C2),
- DB8540_PIN_GROUP(modxmip_oc2_1, NMK_GPIO_ALT_C2),
-
- /* Other alt C3 column */
- DB8540_PIN_GROUP(modaccgpo_oc3_1, NMK_GPIO_ALT_C3),
- DB8540_PIN_GROUP(tpui_oc3_1, NMK_GPIO_ALT_C3),
-
- /* Other alt C4 column */
- DB8540_PIN_GROUP(hwobs_oc4_1, NMK_GPIO_ALT_C4),
- DB8540_PIN_GROUP(moduart1txrx_oc4_1, NMK_GPIO_ALT_C4),
- DB8540_PIN_GROUP(moduart1rtscts_oc4_1, NMK_GPIO_ALT_C4),
- DB8540_PIN_GROUP(modaccuarttxrx_oc4_1, NMK_GPIO_ALT_C4),
- DB8540_PIN_GROUP(modaccuartrtscts_oc4_1, NMK_GPIO_ALT_C4),
- DB8540_PIN_GROUP(stmmod_oc4_1, NMK_GPIO_ALT_C4),
- DB8540_PIN_GROUP(moduartstmmux_oc4_1, NMK_GPIO_ALT_C4),
-
-};
-
-/* We use this macro to define the groups applicable to a function */
-#define DB8540_FUNC_GROUPS(a, b...) \
-static const char * const a##_groups[] = { b };
-
-DB8540_FUNC_GROUPS(apetrig, "apetrig_b_1");
-DB8540_FUNC_GROUPS(clkout, "clkoutreq_a_1", "clkout1_a_1", "clkout1_a_2",
- "clkout2_a_1", "clkout2_a_2");
-DB8540_FUNC_GROUPS(ddrtrig, "ddrtrig_b_1");
-DB8540_FUNC_GROUPS(hsi, "hsir_a_1", "hsit_a_1", "hsit_a_2");
-DB8540_FUNC_GROUPS(hwobs, "hwobs_oc4_1");
-DB8540_FUNC_GROUPS(hx, "hxclk_oc2_1", "hxgpio_oc2_1");
-DB8540_FUNC_GROUPS(i2c0, "i2c0_a_1");
-DB8540_FUNC_GROUPS(i2c1, "i2c1_b_1", "i2c1_b_2");
-DB8540_FUNC_GROUPS(i2c2, "i2c2_b_1", "i2c2_b_2");
-DB8540_FUNC_GROUPS(i2c3, "i2c3_c_1", "i2c4_b_1");
-DB8540_FUNC_GROUPS(i2c4, "i2c4_b_2");
-DB8540_FUNC_GROUPS(i2c5, "i2c5_b_1", "i2c5_b_2", "i2c5_c_1", "i2c5_c_2");
-DB8540_FUNC_GROUPS(i2c6, "i2c6_b_1", "i2c6_oc1_1");
-/* The image processor has 8 GPIO pins that can be muxed out */
-DB8540_FUNC_GROUPS(ipgpio, "ipgpio0_a_1", "ipgpio0_c_1", "ipgpio0_c_2",
- "ipgpio1_a_1", "ipgpio1_c_1", "ipgpio1_c_2",
- "ipgpio2_b_1", "ipgpio2_c_1", "ipgpio2_c_2",
- "ipgpio3_b_1", "ipgpio3_c_1", "ipgpio3_c_2",
- "ipgpio4_c_1", "ipgpio4_c_2",
- "ipgpio5_c_1", "ipgpio5_c_2",
- "ipgpio6_c_1", "ipgpio6_c_2",
- "ipgpio7_b_1", "ipgpio7_c_1");
-DB8540_FUNC_GROUPS(ipi2c, "ipi2c_a_1", "ipi2c_a_2");
-DB8540_FUNC_GROUPS(kp, "kp_a_1", "kp_b_1", "kp_c_1", "kp_oc1_1");
-DB8540_FUNC_GROUPS(lcd, "lcd_d0_d7_a_1", "lcd_d12_d23_a_1", "lcd_d8_d11_a_1",
- "lcdvsi0_a_1", "lcdvsi1_a_1");
-DB8540_FUNC_GROUPS(lcdb, "lcdb_a_1");
-DB8540_FUNC_GROUPS(mc0, "mc0_a_1");
-DB8540_FUNC_GROUPS(mc1, "mc1_a_1", "mc1_a_2");
-DB8540_FUNC_GROUPS(mc2, "mc2_a_1", "mc2rstn_c_1");
-DB8540_FUNC_GROUPS(mc3, "mc3_b_1");
-DB8540_FUNC_GROUPS(mc4, "mc4_a_1", "mc4rstn_c_1");
-DB8540_FUNC_GROUPS(mc5, "mc5_c_1");
-DB8540_FUNC_GROUPS(modaccgpo, "modaccgpo_c_1", "modaccgpo_oc1_1",
- "modaccgpo_oc3_1");
-DB8540_FUNC_GROUPS(modaccuart, "modaccuart_oc2_1", "modaccuarttxrx_oc4_1",
- "modaccuartrtccts_oc4_1");
-DB8540_FUNC_GROUPS(modi2s, "modi2s_a_1");
-DB8540_FUNC_GROUPS(modobs, "modobsclk_a_1", "modobsclkout_oc1_1",
- "modobspwrctrl_oc1_1", "modobspwrrst_c_1",
- "modobsrefclk_oc1_1", "modobsresout_c_1",
- "modobsresout_oc1_1", "modobsservice_oc2_1");
-DB8540_FUNC_GROUPS(modprcmudbg, "modprcmudbg_oc1_1");
-DB8540_FUNC_GROUPS(modrf, "modrf_c_1");
-DB8540_FUNC_GROUPS(modsmb, "modsmb_a_1");
-DB8540_FUNC_GROUPS(modtrig, "modtrig_b_1");
-DB8540_FUNC_GROUPS(moduart, "moduart1_c_1", "moduart1_oc1_1",
- "moduart1txrx_oc4_1", "moduart1rtscts_oc4_1", "moduart0_oc2_1");
-DB8540_FUNC_GROUPS(moduartstmmux, "moduartstmmux_b_1", "moduartstmmux_oc2_1",
- "moduartstmmux_oc4_1");
-DB8540_FUNC_GROUPS(modxmip, "modxmip_oc1_1", "modxmip_oc2_1");
-/*
- * MSP0 can only be on a certain set of pins, but the TX/RX pins can be
- * switched around by selecting the altfunction A or B.
- */
-DB8540_FUNC_GROUPS(msp0, "msp0rfsrck_a_1", "msp0tfstck_a_1", "msp0txrx_a_1",
- "msp0txrx_b_1");
-DB8540_FUNC_GROUPS(msp1, "msp1_a_1", "msp1txrx_a_1", "msp1txrx_b_1");
-DB8540_FUNC_GROUPS(msp2, "msp2sck_a_1", "msp2txdtcktfs_a_1", "msp2rxd_a_1");
-DB8540_FUNC_GROUPS(msp4, "msp4_a_1", "msp4_b_1", "msp4_c_1");
-DB8540_FUNC_GROUPS(pwl, "pwl_b_1", "pwl_b_2", "pwl_b_3", "pwl_b_4");
-DB8540_FUNC_GROUPS(remap, "remap0_oc1_1", "remap1_oc1_1");
-DB8540_FUNC_GROUPS(sbag, "sbag_oc2_1", "sbag_oc2_2");
-/* Select between CS0 on alt B or PS1 on alt C */
-DB8540_FUNC_GROUPS(sm, "sm_b_1", "smcleale_c_1", "smcs0_b_1", "smcs1_b_1",
- "smps0_c_1", "smps1_c_1");
-DB8540_FUNC_GROUPS(spi0, "spi0_c_1");
-DB8540_FUNC_GROUPS(spi1, "spi1_b_1");
-DB8540_FUNC_GROUPS(spi2, "spi2_a_1");
-DB8540_FUNC_GROUPS(spi3, "spi3_oc1_1");
-DB8540_FUNC_GROUPS(ssp0, "ssp0_a_1");
-DB8540_FUNC_GROUPS(ssp1, "ssp1_a_1");
-DB8540_FUNC_GROUPS(stmape, "stmape_c_1", "stmape_oc1_1", "stmape_oc2_1");
-DB8540_FUNC_GROUPS(stmmod, "stmmod_b_1", "stmmod_oc2_1", "stmmod_oc4_1");
-DB8540_FUNC_GROUPS(tpui, "tpui_oc3_1");
-DB8540_FUNC_GROUPS(u0, "u0_a_1", "u0_c_1");
-DB8540_FUNC_GROUPS(u1, "u1ctsrts_a_1", "u1rxtx_a_1");
-DB8540_FUNC_GROUPS(u2, "u2_oc1_1", "u2_oc2_1", "u2ctsrts_a_1", "u2ctsrts_oc1_1",
- "u2rxtx_c_1", "u2txrx_a_1", "u2txrx_b_1", "u2txrx_b_2",
- "u2txrx_oc1_1");
-DB8540_FUNC_GROUPS(u3, "u3ctsrts_b_1", "u3rxtx_c_1", "u3txrxa_b_1");
-DB8540_FUNC_GROUPS(u4, "u4ctsrts_b_1", "u4ctsrts_c_1", "u4txrx_b_1");
-DB8540_FUNC_GROUPS(usb, "usb_a_1");
-
-
-#define FUNCTION(fname) \
- { \
- .name = #fname, \
- .groups = fname##_groups, \
- .ngroups = ARRAY_SIZE(fname##_groups), \
- }
-
-static const struct nmk_function nmk_db8540_functions[] = {
- FUNCTION(apetrig),
- FUNCTION(clkout),
- FUNCTION(ddrtrig),
- FUNCTION(hsi),
- FUNCTION(hwobs),
- FUNCTION(hx),
- FUNCTION(i2c0),
- FUNCTION(i2c1),
- FUNCTION(i2c2),
- FUNCTION(i2c3),
- FUNCTION(i2c4),
- FUNCTION(i2c5),
- FUNCTION(i2c6),
- FUNCTION(ipgpio),
- FUNCTION(ipi2c),
- FUNCTION(kp),
- FUNCTION(lcd),
- FUNCTION(lcdb),
- FUNCTION(mc0),
- FUNCTION(mc1),
- FUNCTION(mc2),
- FUNCTION(mc3),
- FUNCTION(mc4),
- FUNCTION(mc5),
- FUNCTION(modaccgpo),
- FUNCTION(modaccuart),
- FUNCTION(modi2s),
- FUNCTION(modobs),
- FUNCTION(modprcmudbg),
- FUNCTION(modrf),
- FUNCTION(modsmb),
- FUNCTION(modtrig),
- FUNCTION(moduart),
- FUNCTION(modxmip),
- FUNCTION(msp0),
- FUNCTION(msp1),
- FUNCTION(msp2),
- FUNCTION(msp4),
- FUNCTION(pwl),
- FUNCTION(remap),
- FUNCTION(sbag),
- FUNCTION(sm),
- FUNCTION(spi0),
- FUNCTION(spi1),
- FUNCTION(spi2),
- FUNCTION(spi3),
- FUNCTION(ssp0),
- FUNCTION(ssp1),
- FUNCTION(stmape),
- FUNCTION(stmmod),
- FUNCTION(tpui),
- FUNCTION(u0),
- FUNCTION(u1),
- FUNCTION(u2),
- FUNCTION(u3),
- FUNCTION(u4),
- FUNCTION(usb)
-};
-
-static const struct prcm_gpiocr_altcx_pin_desc db8540_altcx_pins[] = {
- PRCM_GPIOCR_ALTCX(8, true, PRCM_IDX_GPIOCR1, 20, /* SPI3_CLK */
- false, 0, 0,
- false, 0, 0,
- false, 0, 0
- ),
- PRCM_GPIOCR_ALTCX(9, true, PRCM_IDX_GPIOCR1, 20, /* SPI3_RXD */
- false, 0, 0,
- false, 0, 0,
- false, 0, 0
- ),
- PRCM_GPIOCR_ALTCX(10, true, PRCM_IDX_GPIOCR1, 20, /* SPI3_FRM */
- false, 0, 0,
- false, 0, 0,
- false, 0, 0
- ),
- PRCM_GPIOCR_ALTCX(11, true, PRCM_IDX_GPIOCR1, 20, /* SPI3_TXD */
- false, 0, 0,
- false, 0, 0,
- false, 0, 0
- ),
- PRCM_GPIOCR_ALTCX(23, true, PRCM_IDX_GPIOCR1, 9, /* STMAPE_CLK_a */
- true, PRCM_IDX_GPIOCR2, 10, /* SBAG_CLK_a */
- false, 0, 0,
- false, 0, 0
- ),
- PRCM_GPIOCR_ALTCX(24, true, PRCM_IDX_GPIOCR3, 30, /* U2_RXD_g */
- true, PRCM_IDX_GPIOCR2, 10, /* SBAG_VAL_a */
- false, 0, 0,
- false, 0, 0
- ),
- PRCM_GPIOCR_ALTCX(25, true, PRCM_IDX_GPIOCR1, 9, /* STMAPE_DAT_a[0] */
- true, PRCM_IDX_GPIOCR2, 10, /* SBAG_D_a[0] */
- false, 0, 0,
- false, 0, 0
- ),
- PRCM_GPIOCR_ALTCX(26, true, PRCM_IDX_GPIOCR1, 9, /* STMAPE_DAT_a[1] */
- true, PRCM_IDX_GPIOCR2, 10, /* SBAG_D_a[1] */
- false, 0, 0,
- false, 0, 0
- ),
- PRCM_GPIOCR_ALTCX(27, true, PRCM_IDX_GPIOCR1, 9, /* STMAPE_DAT_a[2] */
- true, PRCM_IDX_GPIOCR2, 10, /* SBAG_D_a[2] */
- false, 0, 0,
- false, 0, 0
- ),
- PRCM_GPIOCR_ALTCX(28, true, PRCM_IDX_GPIOCR1, 9, /* STMAPE_DAT_a[3] */
- true, PRCM_IDX_GPIOCR2, 10, /* SBAG_D_a[3] */
- false, 0, 0,
- false, 0, 0
- ),
- PRCM_GPIOCR_ALTCX(64, true, PRCM_IDX_GPIOCR1, 15, /* MODOBS_REFCLK_REQ */
- false, 0, 0,
- true, PRCM_IDX_GPIOCR1, 2, /* TPIU_CTL */
- true, PRCM_IDX_GPIOCR2, 23 /* HW_OBS_APE_PRCMU[17] */
- ),
- PRCM_GPIOCR_ALTCX(65, true, PRCM_IDX_GPIOCR1, 19, /* MODOBS_PWRCTRL0 */
- true, PRCM_IDX_GPIOCR1, 24, /* Hx_CLK */
- true, PRCM_IDX_GPIOCR1, 2, /* TPIU_CLK */
- true, PRCM_IDX_GPIOCR2, 24 /* HW_OBS_APE_PRCMU[16] */
- ),
- PRCM_GPIOCR_ALTCX(66, true, PRCM_IDX_GPIOCR1, 15, /* MODOBS_CLKOUT1 */
- false, 0, 0,
- true, PRCM_IDX_GPIOCR1, 2, /* TPIU_D[15] */
- true, PRCM_IDX_GPIOCR2, 25 /* HW_OBS_APE_PRCMU[15] */
- ),
- PRCM_GPIOCR_ALTCX(67, true, PRCM_IDX_GPIOCR1, 1, /* MODUART1_TXD_a */
- true, PRCM_IDX_GPIOCR1, 6, /* MODACCUART_TXD_a */
- true, PRCM_IDX_GPIOCR1, 2, /* TPIU_D[14] */
- true, PRCM_IDX_GPIOCR2, 26 /* HW_OBS_APE_PRCMU[14] */
- ),
- PRCM_GPIOCR_ALTCX(70, true, PRCM_IDX_GPIOCR3, 6, /* MOD_PRCMU_DEBUG[17] */
- true, PRCM_IDX_GPIOCR1, 10, /* STMMOD_CLK_b */
- true, PRCM_IDX_GPIOCR1, 2, /* TPIU_D[13] */
- true, PRCM_IDX_GPIOCR2, 27 /* HW_OBS_APE_PRCMU[13] */
- ),
- PRCM_GPIOCR_ALTCX(71, true, PRCM_IDX_GPIOCR3, 6, /* MOD_PRCMU_DEBUG[16] */
- true, PRCM_IDX_GPIOCR1, 10, /* STMMOD_DAT_b[3] */
- true, PRCM_IDX_GPIOCR1, 2, /* TPIU_D[12] */
- true, PRCM_IDX_GPIOCR2, 27 /* HW_OBS_APE_PRCMU[12] */
- ),
- PRCM_GPIOCR_ALTCX(72, true, PRCM_IDX_GPIOCR3, 6, /* MOD_PRCMU_DEBUG[15] */
- true, PRCM_IDX_GPIOCR1, 10, /* STMMOD_DAT_b[2] */
- true, PRCM_IDX_GPIOCR1, 2, /* TPIU_D[11] */
- true, PRCM_IDX_GPIOCR2, 27 /* HW_OBS_APE_PRCMU[11] */
- ),
- PRCM_GPIOCR_ALTCX(73, true, PRCM_IDX_GPIOCR3, 6, /* MOD_PRCMU_DEBUG[14] */
- true, PRCM_IDX_GPIOCR1, 10, /* STMMOD_DAT_b[1] */
- true, PRCM_IDX_GPIOCR1, 2, /* TPIU_D[10] */
- true, PRCM_IDX_GPIOCR2, 27 /* HW_OBS_APE_PRCMU[10] */
- ),
- PRCM_GPIOCR_ALTCX(74, true, PRCM_IDX_GPIOCR3, 6, /* MOD_PRCMU_DEBUG[13] */
- true, PRCM_IDX_GPIOCR1, 10, /* STMMOD_DAT_b[0] */
- true, PRCM_IDX_GPIOCR1, 2, /* TPIU_D[9] */
- true, PRCM_IDX_GPIOCR2, 27 /* HW_OBS_APE_PRCMU[9] */
- ),
- PRCM_GPIOCR_ALTCX(75, true, PRCM_IDX_GPIOCR1, 12, /* MODOBS_RESOUT0_N */
- true, PRCM_IDX_GPIOCR2, 1, /* MODUART_STMMUX_RXD_b */
- true, PRCM_IDX_GPIOCR1, 2, /* TPIU_D[8] */
- true, PRCM_IDX_GPIOCR2, 28 /* HW_OBS_APE_PRCMU[8] */
- ),
- PRCM_GPIOCR_ALTCX(76, true, PRCM_IDX_GPIOCR3, 7, /* MOD_PRCMU_DEBUG[12] */
- true, PRCM_IDX_GPIOCR1, 25, /* Hx_GPIO[7] */
- true, PRCM_IDX_GPIOCR1, 2, /* TPIU_D[7] */
- true, PRCM_IDX_GPIOCR2, 29 /* HW_OBS_APE_PRCMU[7] */
- ),
- PRCM_GPIOCR_ALTCX(77, true, PRCM_IDX_GPIOCR3, 7, /* MOD_PRCMU_DEBUG[11] */
- true, PRCM_IDX_GPIOCR1, 25, /* Hx_GPIO[6] */
- true, PRCM_IDX_GPIOCR1, 2, /* TPIU_D[6] */
- true, PRCM_IDX_GPIOCR2, 29 /* HW_OBS_APE_PRCMU[6] */
- ),
- PRCM_GPIOCR_ALTCX(78, true, PRCM_IDX_GPIOCR3, 7, /* MOD_PRCMU_DEBUG[10] */
- true, PRCM_IDX_GPIOCR1, 25, /* Hx_GPIO[5] */
- true, PRCM_IDX_GPIOCR1, 2, /* TPIU_D[5] */
- true, PRCM_IDX_GPIOCR2, 29 /* HW_OBS_APE_PRCMU[5] */
- ),
- PRCM_GPIOCR_ALTCX(79, true, PRCM_IDX_GPIOCR3, 7, /* MOD_PRCMU_DEBUG[9] */
- true, PRCM_IDX_GPIOCR1, 25, /* Hx_GPIO[4] */
- true, PRCM_IDX_GPIOCR1, 2, /* TPIU_D[4] */
- true, PRCM_IDX_GPIOCR2, 29 /* HW_OBS_APE_PRCMU[4] */
- ),
- PRCM_GPIOCR_ALTCX(80, true, PRCM_IDX_GPIOCR1, 26, /* MODACC_GPO[0] */
- true, PRCM_IDX_GPIOCR1, 25, /* Hx_GPIO[3] */
- true, PRCM_IDX_GPIOCR1, 2, /* TPIU_D[3] */
- true, PRCM_IDX_GPIOCR2, 30 /* HW_OBS_APE_PRCMU[3] */
- ),
- PRCM_GPIOCR_ALTCX(81, true, PRCM_IDX_GPIOCR2, 17, /* MODACC_GPO[1] */
- true, PRCM_IDX_GPIOCR1, 25, /* Hx_GPIO[2] */
- true, PRCM_IDX_GPIOCR1, 2, /* TPIU_D[2] */
- true, PRCM_IDX_GPIOCR2, 30 /* HW_OBS_APE_PRCMU[2] */
- ),
- PRCM_GPIOCR_ALTCX(82, true, PRCM_IDX_GPIOCR3, 8, /* MOD_PRCMU_DEBUG[8] */
- true, PRCM_IDX_GPIOCR1, 25, /* Hx_GPIO[1] */
- true, PRCM_IDX_GPIOCR1, 2, /* TPIU_D[1] */
- true, PRCM_IDX_GPIOCR2, 31 /* HW_OBS_APE_PRCMU[1] */
- ),
- PRCM_GPIOCR_ALTCX(83, true, PRCM_IDX_GPIOCR3, 8, /* MOD_PRCMU_DEBUG[7] */
- true, PRCM_IDX_GPIOCR1, 25, /* Hx_GPIO[0] */
- true, PRCM_IDX_GPIOCR1, 2, /* TPIU_D[0] */
- true, PRCM_IDX_GPIOCR2, 31 /* HW_OBS_APE_PRCMU[0] */
- ),
- PRCM_GPIOCR_ALTCX(84, true, PRCM_IDX_GPIOCR3, 9, /* MOD_PRCMU_DEBUG[6] */
- true, PRCM_IDX_GPIOCR1, 8, /* SBAG_CLK_b */
- true, PRCM_IDX_GPIOCR1, 3, /* TPIU_D[23] */
- true, PRCM_IDX_GPIOCR1, 16 /* MODUART1_RXD_b */
- ),
- PRCM_GPIOCR_ALTCX(85, true, PRCM_IDX_GPIOCR3, 9, /* MOD_PRCMU_DEBUG[5] */
- true, PRCM_IDX_GPIOCR1, 8, /* SBAG_D_b[3] */
- true, PRCM_IDX_GPIOCR1, 3, /* TPIU_D[22] */
- true, PRCM_IDX_GPIOCR1, 16 /* MODUART1_TXD_b */
- ),
- PRCM_GPIOCR_ALTCX(86, true, PRCM_IDX_GPIOCR3, 9, /* MOD_PRCMU_DEBUG[0] */
- true, PRCM_IDX_GPIOCR2, 18, /* STMAPE_DAT_b[0] */
- true, PRCM_IDX_GPIOCR1, 14, /* TPIU_D[25] */
- true, PRCM_IDX_GPIOCR1, 11 /* STMMOD_DAT_c[0] */
- ),
- PRCM_GPIOCR_ALTCX(87, true, PRCM_IDX_GPIOCR3, 0, /* MODACC_GPO_a[5] */
- true, PRCM_IDX_GPIOCR2, 3, /* U2_RXD_c */
- true, PRCM_IDX_GPIOCR1, 4, /* TPIU_D[24] */
- true, PRCM_IDX_GPIOCR1, 21 /* MODUART_STMMUX_RXD_c */
- ),
- PRCM_GPIOCR_ALTCX(151, true, PRCM_IDX_GPIOCR1, 18, /* REMAP0 */
- false, 0, 0,
- false, 0, 0,
- false, 0, 0
- ),
- PRCM_GPIOCR_ALTCX(152, true, PRCM_IDX_GPIOCR1, 18, /* REMAP1 */
- false, 0, 0,
- false, 0, 0,
- false, 0, 0
- ),
- PRCM_GPIOCR_ALTCX(153, true, PRCM_IDX_GPIOCR3, 2, /* KP_O_b[6] */
- true, PRCM_IDX_GPIOCR1, 8, /* SBAG_D_b[2] */
- true, PRCM_IDX_GPIOCR1, 3, /* TPIU_D[21] */
- true, PRCM_IDX_GPIOCR1, 0 /* MODUART1_RTS */
- ),
- PRCM_GPIOCR_ALTCX(154, true, PRCM_IDX_GPIOCR3, 2, /* KP_I_b[6] */
- true, PRCM_IDX_GPIOCR1, 8, /* SBAG_D_b[1] */
- true, PRCM_IDX_GPIOCR1, 3, /* TPIU_D[20] */
- true, PRCM_IDX_GPIOCR1, 0 /* MODUART1_CTS */
- ),
- PRCM_GPIOCR_ALTCX(155, true, PRCM_IDX_GPIOCR3, 3, /* KP_O_b[5] */
- true, PRCM_IDX_GPIOCR1, 8, /* SBAG_D_b[0] */
- true, PRCM_IDX_GPIOCR1, 3, /* TPIU_D[19] */
- true, PRCM_IDX_GPIOCR1, 5 /* MODACCUART_RXD_c */
- ),
- PRCM_GPIOCR_ALTCX(156, true, PRCM_IDX_GPIOCR3, 3, /* KP_O_b[4] */
- true, PRCM_IDX_GPIOCR1, 8, /* SBAG_VAL_b */
- true, PRCM_IDX_GPIOCR1, 3, /* TPIU_D[18] */
- true, PRCM_IDX_GPIOCR1, 5 /* MODACCUART_TXD_b */
- ),
- PRCM_GPIOCR_ALTCX(157, true, PRCM_IDX_GPIOCR3, 4, /* KP_I_b[5] */
- true, PRCM_IDX_GPIOCR1, 23, /* MODOBS_SERVICE_N */
- true, PRCM_IDX_GPIOCR1, 3, /* TPIU_D[17] */
- true, PRCM_IDX_GPIOCR1, 14 /* MODACCUART_RTS */
- ),
- PRCM_GPIOCR_ALTCX(158, true, PRCM_IDX_GPIOCR3, 4, /* KP_I_b[4] */
- true, PRCM_IDX_GPIOCR2, 0, /* U2_TXD_c */
- true, PRCM_IDX_GPIOCR1, 3, /* TPIU_D[16] */
- true, PRCM_IDX_GPIOCR1, 14 /* MODACCUART_CTS */
- ),
- PRCM_GPIOCR_ALTCX(159, true, PRCM_IDX_GPIOCR3, 5, /* KP_O_b[3] */
- true, PRCM_IDX_GPIOCR3, 10, /* MODUART0_RXD */
- true, PRCM_IDX_GPIOCR1, 4, /* TPIU_D[31] */
- false, 0, 0
- ),
- PRCM_GPIOCR_ALTCX(160, true, PRCM_IDX_GPIOCR3, 5, /* KP_I_b[3] */
- true, PRCM_IDX_GPIOCR3, 10, /* MODUART0_TXD */
- true, PRCM_IDX_GPIOCR1, 4, /* TPIU_D[30] */
- false, 0, 0
- ),
- PRCM_GPIOCR_ALTCX(161, true, PRCM_IDX_GPIOCR3, 9, /* MOD_PRCMU_DEBUG[4] */
- true, PRCM_IDX_GPIOCR2, 18, /* STMAPE_CLK_b */
- true, PRCM_IDX_GPIOCR1, 4, /* TPIU_D[29] */
- true, PRCM_IDX_GPIOCR1, 11 /* STMMOD_CLK_c */
- ),
- PRCM_GPIOCR_ALTCX(162, true, PRCM_IDX_GPIOCR3, 9, /* MOD_PRCMU_DEBUG[3] */
- true, PRCM_IDX_GPIOCR2, 18, /* STMAPE_DAT_b[3] */
- true, PRCM_IDX_GPIOCR1, 4, /* TPIU_D[28] */
- true, PRCM_IDX_GPIOCR1, 11 /* STMMOD_DAT_c[3] */
- ),
- PRCM_GPIOCR_ALTCX(163, true, PRCM_IDX_GPIOCR3, 9, /* MOD_PRCMU_DEBUG[2] */
- true, PRCM_IDX_GPIOCR2, 18, /* STMAPE_DAT_b[2] */
- true, PRCM_IDX_GPIOCR1, 4, /* TPIU_D[27] */
- true, PRCM_IDX_GPIOCR1, 11 /* STMMOD_DAT_c[2] */
- ),
- PRCM_GPIOCR_ALTCX(164, true, PRCM_IDX_GPIOCR3, 9, /* MOD_PRCMU_DEBUG[1] */
- true, PRCM_IDX_GPIOCR2, 18, /* STMAPE_DAT_b[1] */
- true, PRCM_IDX_GPIOCR1, 4, /* TPIU_D[26] */
- true, PRCM_IDX_GPIOCR1, 11 /* STMMOD_DAT_c[1] */
- ),
- PRCM_GPIOCR_ALTCX(204, true, PRCM_IDX_GPIOCR2, 2, /* U2_RXD_f */
- false, 0, 0,
- false, 0, 0,
- false, 0, 0
- ),
- PRCM_GPIOCR_ALTCX(205, true, PRCM_IDX_GPIOCR2, 2, /* U2_TXD_f */
- false, 0, 0,
- false, 0, 0,
- false, 0, 0
- ),
- PRCM_GPIOCR_ALTCX(206, true, PRCM_IDX_GPIOCR2, 2, /* U2_CTSn_b */
- false, 0, 0,
- false, 0, 0,
- false, 0, 0
- ),
- PRCM_GPIOCR_ALTCX(207, true, PRCM_IDX_GPIOCR2, 2, /* U2_RTSn_b */
- false, 0, 0,
- false, 0, 0,
- false, 0, 0
- ),
-};
-
-static const u16 db8540_prcm_gpiocr_regs[] = {
- [PRCM_IDX_GPIOCR1] = 0x138,
- [PRCM_IDX_GPIOCR2] = 0x574,
- [PRCM_IDX_GPIOCR3] = 0x2bc,
-};
-
-static const struct nmk_pinctrl_soc_data nmk_db8540_soc = {
- .pins = nmk_db8540_pins,
- .npins = ARRAY_SIZE(nmk_db8540_pins),
- .functions = nmk_db8540_functions,
- .nfunctions = ARRAY_SIZE(nmk_db8540_functions),
- .groups = nmk_db8540_groups,
- .ngroups = ARRAY_SIZE(nmk_db8540_groups),
- .altcx_pins = db8540_altcx_pins,
- .npins_altcx = ARRAY_SIZE(db8540_altcx_pins),
- .prcm_gpiocr_registers = db8540_prcm_gpiocr_regs,
-};
-
-void nmk_pinctrl_db8540_init(const struct nmk_pinctrl_soc_data **soc)
-{
- *soc = &nmk_db8540_soc;
-}
diff --git a/drivers/pinctrl/nomadik/pinctrl-nomadik-stn8815.c b/drivers/pinctrl/nomadik/pinctrl-nomadik-stn8815.c
index 7e814764da7d..8d944bb3a036 100644
--- a/drivers/pinctrl/nomadik/pinctrl-nomadik-stn8815.c
+++ b/drivers/pinctrl/nomadik/pinctrl-nomadik-stn8815.c
@@ -291,7 +291,17 @@ static const unsigned i2cusb_b_1_pins[] = { STN8815_PIN_C21, STN8815_PIN_C20 };
static const unsigned clcd_16_23_b_1_pins[] = { STN8815_PIN_AB6,
STN8815_PIN_AA6, STN8815_PIN_Y6, STN8815_PIN_Y5, STN8815_PIN_AA5,
STN8815_PIN_AB5, STN8815_PIN_AB4, STN8815_PIN_Y4 };
-
+/* Full-speed and high-speed USB pins */
+static const unsigned usbfs_b_1_pins[] = { STN8815_PIN_E21, STN8815_PIN_E20,
+ STN8815_PIN_C22, STN8815_PIN_D21,
+ STN8815_PIN_D20, STN8815_PIN_C21,
+ STN8815_PIN_C20 };
+static const unsigned usbhs_c_1_pins[] = { STN8815_PIN_E21, STN8815_PIN_E20,
+ STN8815_PIN_C20, STN8815_PIN_C19,
+ STN8815_PIN_C22, STN8815_PIN_D21,
+ STN8815_PIN_D20, STN8815_PIN_C21,
+ STN8815_PIN_C16, STN8815_PIN_A15,
+ STN8815_PIN_D17, STN8815_PIN_C17 };
#define STN8815_PIN_GROUP(a, b) { .name = #a, .pins = a##_pins, \
.npins = ARRAY_SIZE(a##_pins), .altsetting = b }
@@ -308,6 +318,8 @@ static const struct nmk_pingroup nmk_stn8815_groups[] = {
STN8815_PIN_GROUP(u1_b_1, NMK_GPIO_ALT_B),
STN8815_PIN_GROUP(i2cusb_b_1, NMK_GPIO_ALT_B),
STN8815_PIN_GROUP(clcd_16_23_b_1, NMK_GPIO_ALT_B),
+ STN8815_PIN_GROUP(usbfs_b_1, NMK_GPIO_ALT_B),
+ STN8815_PIN_GROUP(usbhs_c_1, NMK_GPIO_ALT_C),
};
/* We use this macro to define the groups applicable to a function */
@@ -321,6 +333,7 @@ STN8815_FUNC_GROUPS(i2c1, "i2c1_a_1");
STN8815_FUNC_GROUPS(i2c0, "i2c0_a_1");
STN8815_FUNC_GROUPS(i2cusb, "i2cusb_b_1");
STN8815_FUNC_GROUPS(clcd, "clcd_16_23_b_1");
+STN8815_FUNC_GROUPS(usb, "usbfs_b_1", "usbhs_c_1");
#define FUNCTION(fname) \
{ \
@@ -337,6 +350,7 @@ static const struct nmk_function nmk_stn8815_functions[] = {
FUNCTION(i2c0),
FUNCTION(i2cusb),
FUNCTION(clcd),
+ FUNCTION(usb),
};
static const struct nmk_pinctrl_soc_data nmk_stn8815_soc = {
diff --git a/drivers/pinctrl/pinctrl-adi2-bf54x.c b/drivers/pinctrl/pinctrl-adi2-bf54x.c
deleted file mode 100644
index 008a29e92e56..000000000000
--- a/drivers/pinctrl/pinctrl-adi2-bf54x.c
+++ /dev/null
@@ -1,588 +0,0 @@
-/*
- * Pinctrl Driver for ADI GPIO2 controller
- *
- * Copyright 2007-2013 Analog Devices Inc.
- *
- * Licensed under the GPLv2 or later
- */
-
-#include <asm/portmux.h>
-#include "pinctrl-adi2.h"
-
-static const struct pinctrl_pin_desc adi_pads[] = {
- PINCTRL_PIN(0, "PA0"),
- PINCTRL_PIN(1, "PA1"),
- PINCTRL_PIN(2, "PA2"),
- PINCTRL_PIN(3, "PG3"),
- PINCTRL_PIN(4, "PA4"),
- PINCTRL_PIN(5, "PA5"),
- PINCTRL_PIN(6, "PA6"),
- PINCTRL_PIN(7, "PA7"),
- PINCTRL_PIN(8, "PA8"),
- PINCTRL_PIN(9, "PA9"),
- PINCTRL_PIN(10, "PA10"),
- PINCTRL_PIN(11, "PA11"),
- PINCTRL_PIN(12, "PA12"),
- PINCTRL_PIN(13, "PA13"),
- PINCTRL_PIN(14, "PA14"),
- PINCTRL_PIN(15, "PA15"),
- PINCTRL_PIN(16, "PB0"),
- PINCTRL_PIN(17, "PB1"),
- PINCTRL_PIN(18, "PB2"),
- PINCTRL_PIN(19, "PB3"),
- PINCTRL_PIN(20, "PB4"),
- PINCTRL_PIN(21, "PB5"),
- PINCTRL_PIN(22, "PB6"),
- PINCTRL_PIN(23, "PB7"),
- PINCTRL_PIN(24, "PB8"),
- PINCTRL_PIN(25, "PB9"),
- PINCTRL_PIN(26, "PB10"),
- PINCTRL_PIN(27, "PB11"),
- PINCTRL_PIN(28, "PB12"),
- PINCTRL_PIN(29, "PB13"),
- PINCTRL_PIN(30, "PB14"),
- PINCTRL_PIN(32, "PC0"),
- PINCTRL_PIN(33, "PC1"),
- PINCTRL_PIN(34, "PC2"),
- PINCTRL_PIN(35, "PC3"),
- PINCTRL_PIN(36, "PC4"),
- PINCTRL_PIN(37, "PC5"),
- PINCTRL_PIN(38, "PC6"),
- PINCTRL_PIN(39, "PC7"),
- PINCTRL_PIN(40, "PC8"),
- PINCTRL_PIN(41, "PC9"),
- PINCTRL_PIN(42, "PC10"),
- PINCTRL_PIN(43, "PC11"),
- PINCTRL_PIN(44, "PC12"),
- PINCTRL_PIN(45, "PC13"),
- PINCTRL_PIN(48, "PD0"),
- PINCTRL_PIN(49, "PD1"),
- PINCTRL_PIN(50, "PD2"),
- PINCTRL_PIN(51, "PD3"),
- PINCTRL_PIN(52, "PD4"),
- PINCTRL_PIN(53, "PD5"),
- PINCTRL_PIN(54, "PD6"),
- PINCTRL_PIN(55, "PD7"),
- PINCTRL_PIN(56, "PD8"),
- PINCTRL_PIN(57, "PD9"),
- PINCTRL_PIN(58, "PD10"),
- PINCTRL_PIN(59, "PD11"),
- PINCTRL_PIN(60, "PD12"),
- PINCTRL_PIN(61, "PD13"),
- PINCTRL_PIN(62, "PD14"),
- PINCTRL_PIN(63, "PD15"),
- PINCTRL_PIN(64, "PE0"),
- PINCTRL_PIN(65, "PE1"),
- PINCTRL_PIN(66, "PE2"),
- PINCTRL_PIN(67, "PE3"),
- PINCTRL_PIN(68, "PE4"),
- PINCTRL_PIN(69, "PE5"),
- PINCTRL_PIN(70, "PE6"),
- PINCTRL_PIN(71, "PE7"),
- PINCTRL_PIN(72, "PE8"),
- PINCTRL_PIN(73, "PE9"),
- PINCTRL_PIN(74, "PE10"),
- PINCTRL_PIN(75, "PE11"),
- PINCTRL_PIN(76, "PE12"),
- PINCTRL_PIN(77, "PE13"),
- PINCTRL_PIN(78, "PE14"),
- PINCTRL_PIN(79, "PE15"),
- PINCTRL_PIN(80, "PF0"),
- PINCTRL_PIN(81, "PF1"),
- PINCTRL_PIN(82, "PF2"),
- PINCTRL_PIN(83, "PF3"),
- PINCTRL_PIN(84, "PF4"),
- PINCTRL_PIN(85, "PF5"),
- PINCTRL_PIN(86, "PF6"),
- PINCTRL_PIN(87, "PF7"),
- PINCTRL_PIN(88, "PF8"),
- PINCTRL_PIN(89, "PF9"),
- PINCTRL_PIN(90, "PF10"),
- PINCTRL_PIN(91, "PF11"),
- PINCTRL_PIN(92, "PF12"),
- PINCTRL_PIN(93, "PF13"),
- PINCTRL_PIN(94, "PF14"),
- PINCTRL_PIN(95, "PF15"),
- PINCTRL_PIN(96, "PG0"),
- PINCTRL_PIN(97, "PG1"),
- PINCTRL_PIN(98, "PG2"),
- PINCTRL_PIN(99, "PG3"),
- PINCTRL_PIN(100, "PG4"),
- PINCTRL_PIN(101, "PG5"),
- PINCTRL_PIN(102, "PG6"),
- PINCTRL_PIN(103, "PG7"),
- PINCTRL_PIN(104, "PG8"),
- PINCTRL_PIN(105, "PG9"),
- PINCTRL_PIN(106, "PG10"),
- PINCTRL_PIN(107, "PG11"),
- PINCTRL_PIN(108, "PG12"),
- PINCTRL_PIN(109, "PG13"),
- PINCTRL_PIN(110, "PG14"),
- PINCTRL_PIN(111, "PG15"),
- PINCTRL_PIN(112, "PH0"),
- PINCTRL_PIN(113, "PH1"),
- PINCTRL_PIN(114, "PH2"),
- PINCTRL_PIN(115, "PH3"),
- PINCTRL_PIN(116, "PH4"),
- PINCTRL_PIN(117, "PH5"),
- PINCTRL_PIN(118, "PH6"),
- PINCTRL_PIN(119, "PH7"),
- PINCTRL_PIN(120, "PH8"),
- PINCTRL_PIN(121, "PH9"),
- PINCTRL_PIN(122, "PH10"),
- PINCTRL_PIN(123, "PH11"),
- PINCTRL_PIN(124, "PH12"),
- PINCTRL_PIN(125, "PH13"),
- PINCTRL_PIN(128, "PI0"),
- PINCTRL_PIN(129, "PI1"),
- PINCTRL_PIN(130, "PI2"),
- PINCTRL_PIN(131, "PI3"),
- PINCTRL_PIN(132, "PI4"),
- PINCTRL_PIN(133, "PI5"),
- PINCTRL_PIN(134, "PI6"),
- PINCTRL_PIN(135, "PI7"),
- PINCTRL_PIN(136, "PI8"),
- PINCTRL_PIN(137, "PI9"),
- PINCTRL_PIN(138, "PI10"),
- PINCTRL_PIN(139, "PI11"),
- PINCTRL_PIN(140, "PI12"),
- PINCTRL_PIN(141, "PI13"),
- PINCTRL_PIN(142, "PI14"),
- PINCTRL_PIN(143, "PI15"),
- PINCTRL_PIN(144, "PJ0"),
- PINCTRL_PIN(145, "PJ1"),
- PINCTRL_PIN(146, "PJ2"),
- PINCTRL_PIN(147, "PJ3"),
- PINCTRL_PIN(148, "PJ4"),
- PINCTRL_PIN(149, "PJ5"),
- PINCTRL_PIN(150, "PJ6"),
- PINCTRL_PIN(151, "PJ7"),
- PINCTRL_PIN(152, "PJ8"),
- PINCTRL_PIN(153, "PJ9"),
- PINCTRL_PIN(154, "PJ10"),
- PINCTRL_PIN(155, "PJ11"),
- PINCTRL_PIN(156, "PJ12"),
- PINCTRL_PIN(157, "PJ13"),
-};
-
-static const unsigned uart0_pins[] = {
- GPIO_PE7, GPIO_PE8,
-};
-
-static const unsigned uart1_pins[] = {
- GPIO_PH0, GPIO_PH1,
-};
-
-static const unsigned uart1_ctsrts_pins[] = {
- GPIO_PE9, GPIO_PE10,
-};
-
-static const unsigned uart2_pins[] = {
- GPIO_PB4, GPIO_PB5,
-};
-
-static const unsigned uart3_pins[] = {
- GPIO_PB6, GPIO_PB7,
-};
-
-static const unsigned uart3_ctsrts_pins[] = {
- GPIO_PB2, GPIO_PB3,
-};
-
-static const unsigned rsi0_pins[] = {
- GPIO_PC8, GPIO_PC9, GPIO_PC10, GPIO_PC11, GPIO_PC12, GPIO_PC13,
-};
-
-static const unsigned spi0_pins[] = {
- GPIO_PE0, GPIO_PE1, GPIO_PE2,
-};
-
-static const unsigned spi1_pins[] = {
- GPIO_PG8, GPIO_PG9, GPIO_PG10,
-};
-
-static const unsigned twi0_pins[] = {
- GPIO_PE14, GPIO_PE15,
-};
-
-static const unsigned twi1_pins[] = {
- GPIO_PB0, GPIO_PB1,
-};
-
-static const unsigned rotary_pins[] = {
- GPIO_PH4, GPIO_PH3, GPIO_PH5,
-};
-
-static const unsigned can0_pins[] = {
- GPIO_PG13, GPIO_PG12,
-};
-
-static const unsigned can1_pins[] = {
- GPIO_PG14, GPIO_PG15,
-};
-
-static const unsigned smc0_pins[] = {
- GPIO_PH8, GPIO_PH9, GPIO_PH10, GPIO_PH11, GPIO_PH12, GPIO_PH13,
- GPIO_PI0, GPIO_PI1, GPIO_PI2, GPIO_PI3, GPIO_PI4, GPIO_PI5, GPIO_PI6,
- GPIO_PI7, GPIO_PI8, GPIO_PI9, GPIO_PI10, GPIO_PI11,
- GPIO_PI12, GPIO_PI13, GPIO_PI14, GPIO_PI15,
-};
-
-static const unsigned sport0_pins[] = {
- GPIO_PC0, GPIO_PC2, GPIO_PC3, GPIO_PC4, GPIO_PC6, GPIO_PC7,
-};
-
-static const unsigned sport1_pins[] = {
- GPIO_PD0, GPIO_PD2, GPIO_PD3, GPIO_PD4, GPIO_PD6, GPIO_PD7,
-};
-
-static const unsigned sport2_pins[] = {
- GPIO_PA0, GPIO_PA2, GPIO_PA3, GPIO_PA4, GPIO_PA6, GPIO_PA7,
-};
-
-static const unsigned sport3_pins[] = {
- GPIO_PA8, GPIO_PA10, GPIO_PA11, GPIO_PA12, GPIO_PA14, GPIO_PA15,
-};
-
-static const unsigned ppi0_8b_pins[] = {
- GPIO_PF0, GPIO_PF1, GPIO_PF2, GPIO_PF3, GPIO_PF4, GPIO_PF5, GPIO_PF6,
- GPIO_PF7, GPIO_PF13, GPIO_PG0, GPIO_PG1, GPIO_PG2,
-};
-
-static const unsigned ppi0_16b_pins[] = {
- GPIO_PF0, GPIO_PF1, GPIO_PF2, GPIO_PF3, GPIO_PF4, GPIO_PF5, GPIO_PF6,
- GPIO_PF7, GPIO_PF9, GPIO_PF10, GPIO_PF11, GPIO_PF12,
- GPIO_PF13, GPIO_PF14, GPIO_PF15,
- GPIO_PG0, GPIO_PG1, GPIO_PG2,
-};
-
-static const unsigned ppi0_24b_pins[] = {
- GPIO_PF0, GPIO_PF1, GPIO_PF2, GPIO_PF3, GPIO_PF4, GPIO_PF5, GPIO_PF6,
- GPIO_PF7, GPIO_PF8, GPIO_PF9, GPIO_PF10, GPIO_PF11, GPIO_PF12,
- GPIO_PF13, GPIO_PF14, GPIO_PF15, GPIO_PD0, GPIO_PD1, GPIO_PD2,
- GPIO_PD3, GPIO_PD4, GPIO_PD5, GPIO_PG3, GPIO_PG4,
- GPIO_PG0, GPIO_PG1, GPIO_PG2,
-};
-
-static const unsigned ppi1_8b_pins[] = {
- GPIO_PD0, GPIO_PD1, GPIO_PD2, GPIO_PD3, GPIO_PD4, GPIO_PD5, GPIO_PD6,
- GPIO_PD7, GPIO_PE11, GPIO_PE12, GPIO_PE13,
-};
-
-static const unsigned ppi1_16b_pins[] = {
- GPIO_PD0, GPIO_PD1, GPIO_PD2, GPIO_PD3, GPIO_PD4, GPIO_PD5, GPIO_PD6,
- GPIO_PD7, GPIO_PD8, GPIO_PD9, GPIO_PD10, GPIO_PD11, GPIO_PD12,
- GPIO_PD13, GPIO_PD14, GPIO_PD15,
- GPIO_PE11, GPIO_PE12, GPIO_PE13,
-};
-
-static const unsigned ppi2_8b_pins[] = {
- GPIO_PD8, GPIO_PD9, GPIO_PD10, GPIO_PD11, GPIO_PD12,
- GPIO_PD13, GPIO_PD14, GPIO_PD15,
- GPIO_PA7, GPIO_PB0, GPIO_PB1, GPIO_PB2, GPIO_PB3,
-};
-
-static const unsigned atapi_pins[] = {
- GPIO_PH2, GPIO_PJ3, GPIO_PJ4, GPIO_PJ5, GPIO_PJ6,
- GPIO_PJ7, GPIO_PJ8, GPIO_PJ9, GPIO_PJ10,
-};
-
-static const unsigned atapi_alter_pins[] = {
- GPIO_PF0, GPIO_PF1, GPIO_PF2, GPIO_PF3, GPIO_PF4, GPIO_PF5, GPIO_PF6,
- GPIO_PF7, GPIO_PF8, GPIO_PF9, GPIO_PF10, GPIO_PF11, GPIO_PF12,
- GPIO_PF13, GPIO_PF14, GPIO_PF15, GPIO_PG2, GPIO_PG3, GPIO_PG4,
-};
-
-static const unsigned nfc0_pins[] = {
- GPIO_PJ1, GPIO_PJ2,
-};
-
-static const unsigned keys_4x4_pins[] = {
- GPIO_PD8, GPIO_PD9, GPIO_PD10, GPIO_PD11,
- GPIO_PD12, GPIO_PD13, GPIO_PD14, GPIO_PD15,
-};
-
-static const unsigned keys_8x8_pins[] = {
- GPIO_PD8, GPIO_PD9, GPIO_PD10, GPIO_PD11,
- GPIO_PD12, GPIO_PD13, GPIO_PD14, GPIO_PD15,
- GPIO_PE0, GPIO_PE1, GPIO_PE2, GPIO_PE3,
- GPIO_PE4, GPIO_PE5, GPIO_PE6, GPIO_PE7,
-};
-
-static const unsigned short uart0_mux[] = {
- P_UART0_TX, P_UART0_RX,
- 0
-};
-
-static const unsigned short uart1_mux[] = {
- P_UART1_TX, P_UART1_RX,
- 0
-};
-
-static const unsigned short uart1_ctsrts_mux[] = {
- P_UART1_RTS, P_UART1_CTS,
- 0
-};
-
-static const unsigned short uart2_mux[] = {
- P_UART2_TX, P_UART2_RX,
- 0
-};
-
-static const unsigned short uart3_mux[] = {
- P_UART3_TX, P_UART3_RX,
- 0
-};
-
-static const unsigned short uart3_ctsrts_mux[] = {
- P_UART3_RTS, P_UART3_CTS,
- 0
-};
-
-static const unsigned short rsi0_mux[] = {
- P_SD_D0, P_SD_D1, P_SD_D2, P_SD_D3, P_SD_CLK, P_SD_CMD,
- 0
-};
-
-static const unsigned short spi0_mux[] = {
- P_SPI0_SCK, P_SPI0_MISO, P_SPI0_MOSI, 0
-};
-
-static const unsigned short spi1_mux[] = {
- P_SPI1_SCK, P_SPI1_MISO, P_SPI1_MOSI, 0
-};
-
-static const unsigned short twi0_mux[] = {
- P_TWI0_SCL, P_TWI0_SDA, 0
-};
-
-static const unsigned short twi1_mux[] = {
- P_TWI1_SCL, P_TWI1_SDA, 0
-};
-
-static const unsigned short rotary_mux[] = {
- P_CNT_CUD, P_CNT_CDG, P_CNT_CZM, 0
-};
-
-static const unsigned short sport0_mux[] = {
- P_SPORT0_TFS, P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS,
- P_SPORT0_DRPRI, P_SPORT0_RSCLK, 0
-};
-
-static const unsigned short sport1_mux[] = {
- P_SPORT1_TFS, P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS,
- P_SPORT1_DRPRI, P_SPORT1_RSCLK, 0
-};
-
-static const unsigned short sport2_mux[] = {
- P_SPORT2_TFS, P_SPORT2_DTPRI, P_SPORT2_TSCLK, P_SPORT2_RFS,
- P_SPORT2_DRPRI, P_SPORT2_RSCLK, 0
-};
-
-static const unsigned short sport3_mux[] = {
- P_SPORT3_TFS, P_SPORT3_DTPRI, P_SPORT3_TSCLK, P_SPORT3_RFS,
- P_SPORT3_DRPRI, P_SPORT3_RSCLK, 0
-};
-
-static const unsigned short can0_mux[] = {
- P_CAN0_RX, P_CAN0_TX, 0
-};
-
-static const unsigned short can1_mux[] = {
- P_CAN1_RX, P_CAN1_TX, 0
-};
-
-static const unsigned short smc0_mux[] = {
- P_A4, P_A5, P_A6, P_A7, P_A8, P_A9, P_A10, P_A11, P_A12,
- P_A13, P_A14, P_A15, P_A16, P_A17, P_A18, P_A19, P_A20, P_A21,
- P_A22, P_A23, P_A24, P_A25, P_NOR_CLK, 0,
-};
-
-static const unsigned short ppi0_8b_mux[] = {
- P_PPI0_D0, P_PPI0_D1, P_PPI0_D2, P_PPI0_D3,
- P_PPI0_D4, P_PPI0_D5, P_PPI0_D6, P_PPI0_D7,
- P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2,
- 0,
-};
-
-static const unsigned short ppi0_16b_mux[] = {
- P_PPI0_D0, P_PPI0_D1, P_PPI0_D2, P_PPI0_D3,
- P_PPI0_D4, P_PPI0_D5, P_PPI0_D6, P_PPI0_D7,
- P_PPI0_D8, P_PPI0_D9, P_PPI0_D10, P_PPI0_D11,
- P_PPI0_D12, P_PPI0_D13, P_PPI0_D14, P_PPI0_D15,
- P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2,
- 0,
-};
-
-static const unsigned short ppi0_24b_mux[] = {
- P_PPI0_D0, P_PPI0_D1, P_PPI0_D2, P_PPI0_D3,
- P_PPI0_D4, P_PPI0_D5, P_PPI0_D6, P_PPI0_D7,
- P_PPI0_D8, P_PPI0_D9, P_PPI0_D10, P_PPI0_D11,
- P_PPI0_D12, P_PPI0_D13, P_PPI0_D14, P_PPI0_D15,
- P_PPI0_D16, P_PPI0_D17, P_PPI0_D18, P_PPI0_D19,
- P_PPI0_D20, P_PPI0_D21, P_PPI0_D22, P_PPI0_D23,
- P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2,
- 0,
-};
-
-static const unsigned short ppi1_8b_mux[] = {
- P_PPI1_D0, P_PPI1_D1, P_PPI1_D2, P_PPI1_D3,
- P_PPI1_D4, P_PPI1_D5, P_PPI1_D6, P_PPI1_D7,
- P_PPI1_CLK, P_PPI1_FS1, P_PPI1_FS2,
- 0,
-};
-
-static const unsigned short ppi1_16b_mux[] = {
- P_PPI1_D0, P_PPI1_D1, P_PPI1_D2, P_PPI1_D3,
- P_PPI1_D4, P_PPI1_D5, P_PPI1_D6, P_PPI1_D7,
- P_PPI1_D8, P_PPI1_D9, P_PPI1_D10, P_PPI1_D11,
- P_PPI1_D12, P_PPI1_D13, P_PPI1_D14, P_PPI1_D15,
- P_PPI1_CLK, P_PPI1_FS1, P_PPI1_FS2,
- 0,
-};
-
-static const unsigned short ppi2_8b_mux[] = {
- P_PPI2_D0, P_PPI2_D1, P_PPI2_D2, P_PPI2_D3,
- P_PPI2_D4, P_PPI2_D5, P_PPI2_D6, P_PPI2_D7,
- P_PPI2_CLK, P_PPI2_FS1, P_PPI2_FS2,
- 0,
-};
-
-static const unsigned short atapi_mux[] = {
- P_ATAPI_RESET, P_ATAPI_DIOR, P_ATAPI_DIOW, P_ATAPI_CS0, P_ATAPI_CS1,
- P_ATAPI_DMACK, P_ATAPI_DMARQ, P_ATAPI_INTRQ, P_ATAPI_IORDY,
-};
-
-static const unsigned short atapi_alter_mux[] = {
- P_ATAPI_D0A, P_ATAPI_D1A, P_ATAPI_D2A, P_ATAPI_D3A, P_ATAPI_D4A,
- P_ATAPI_D5A, P_ATAPI_D6A, P_ATAPI_D7A, P_ATAPI_D8A, P_ATAPI_D9A,
- P_ATAPI_D10A, P_ATAPI_D11A, P_ATAPI_D12A, P_ATAPI_D13A, P_ATAPI_D14A,
- P_ATAPI_D15A, P_ATAPI_A0A, P_ATAPI_A1A, P_ATAPI_A2A,
- 0
-};
-
-static const unsigned short nfc0_mux[] = {
- P_NAND_CE, P_NAND_RB,
- 0
-};
-
-static const unsigned short keys_4x4_mux[] = {
- P_KEY_ROW3, P_KEY_ROW2, P_KEY_ROW1, P_KEY_ROW0,
- P_KEY_COL3, P_KEY_COL2, P_KEY_COL1, P_KEY_COL0,
- 0
-};
-
-static const unsigned short keys_8x8_mux[] = {
- P_KEY_ROW7, P_KEY_ROW6, P_KEY_ROW5, P_KEY_ROW4,
- P_KEY_ROW3, P_KEY_ROW2, P_KEY_ROW1, P_KEY_ROW0,
- P_KEY_COL7, P_KEY_COL6, P_KEY_COL5, P_KEY_COL4,
- P_KEY_COL3, P_KEY_COL2, P_KEY_COL1, P_KEY_COL0,
- 0
-};
-
-static const struct adi_pin_group adi_pin_groups[] = {
- ADI_PIN_GROUP("uart0grp", uart0_pins, uart0_mux),
- ADI_PIN_GROUP("uart1grp", uart1_pins, uart1_mux),
- ADI_PIN_GROUP("uart1ctsrtsgrp", uart1_ctsrts_pins, uart1_ctsrts_mux),
- ADI_PIN_GROUP("uart2grp", uart2_pins, uart2_mux),
- ADI_PIN_GROUP("uart3grp", uart3_pins, uart3_mux),
- ADI_PIN_GROUP("uart3ctsrtsgrp", uart3_ctsrts_pins, uart3_ctsrts_mux),
- ADI_PIN_GROUP("rsi0grp", rsi0_pins, rsi0_mux),
- ADI_PIN_GROUP("spi0grp", spi0_pins, spi0_mux),
- ADI_PIN_GROUP("spi1grp", spi1_pins, spi1_mux),
- ADI_PIN_GROUP("twi0grp", twi0_pins, twi0_mux),
- ADI_PIN_GROUP("twi1grp", twi1_pins, twi1_mux),
- ADI_PIN_GROUP("rotarygrp", rotary_pins, rotary_mux),
- ADI_PIN_GROUP("can0grp", can0_pins, can0_mux),
- ADI_PIN_GROUP("can1grp", can1_pins, can1_mux),
- ADI_PIN_GROUP("smc0grp", smc0_pins, smc0_mux),
- ADI_PIN_GROUP("sport0grp", sport0_pins, sport0_mux),
- ADI_PIN_GROUP("sport1grp", sport1_pins, sport1_mux),
- ADI_PIN_GROUP("sport2grp", sport2_pins, sport2_mux),
- ADI_PIN_GROUP("sport3grp", sport3_pins, sport3_mux),
- ADI_PIN_GROUP("ppi0_8bgrp", ppi0_8b_pins, ppi0_8b_mux),
- ADI_PIN_GROUP("ppi0_16bgrp", ppi0_16b_pins, ppi0_16b_mux),
- ADI_PIN_GROUP("ppi0_24bgrp", ppi0_24b_pins, ppi0_24b_mux),
- ADI_PIN_GROUP("ppi1_8bgrp", ppi1_8b_pins, ppi1_8b_mux),
- ADI_PIN_GROUP("ppi1_16bgrp", ppi1_16b_pins, ppi1_16b_mux),
- ADI_PIN_GROUP("ppi2_8bgrp", ppi2_8b_pins, ppi2_8b_mux),
- ADI_PIN_GROUP("atapigrp", atapi_pins, atapi_mux),
- ADI_PIN_GROUP("atapialtergrp", atapi_alter_pins, atapi_alter_mux),
- ADI_PIN_GROUP("nfc0grp", nfc0_pins, nfc0_mux),
- ADI_PIN_GROUP("keys_4x4grp", keys_4x4_pins, keys_4x4_mux),
- ADI_PIN_GROUP("keys_8x8grp", keys_8x8_pins, keys_8x8_mux),
-};
-
-static const char * const uart0grp[] = { "uart0grp" };
-static const char * const uart1grp[] = { "uart1grp" };
-static const char * const uart1ctsrtsgrp[] = { "uart1ctsrtsgrp" };
-static const char * const uart2grp[] = { "uart2grp" };
-static const char * const uart3grp[] = { "uart3grp" };
-static const char * const uart3ctsrtsgrp[] = { "uart3ctsrtsgrp" };
-static const char * const rsi0grp[] = { "rsi0grp" };
-static const char * const spi0grp[] = { "spi0grp" };
-static const char * const spi1grp[] = { "spi1grp" };
-static const char * const twi0grp[] = { "twi0grp" };
-static const char * const twi1grp[] = { "twi1grp" };
-static const char * const rotarygrp[] = { "rotarygrp" };
-static const char * const can0grp[] = { "can0grp" };
-static const char * const can1grp[] = { "can1grp" };
-static const char * const smc0grp[] = { "smc0grp" };
-static const char * const sport0grp[] = { "sport0grp" };
-static const char * const sport1grp[] = { "sport1grp" };
-static const char * const sport2grp[] = { "sport2grp" };
-static const char * const sport3grp[] = { "sport3grp" };
-static const char * const ppi0grp[] = { "ppi0_8bgrp",
- "ppi0_16bgrp",
- "ppi0_24bgrp" };
-static const char * const ppi1grp[] = { "ppi1_8bgrp",
- "ppi1_16bgrp" };
-static const char * const ppi2grp[] = { "ppi2_8bgrp" };
-static const char * const atapigrp[] = { "atapigrp" };
-static const char * const atapialtergrp[] = { "atapialtergrp" };
-static const char * const nfc0grp[] = { "nfc0grp" };
-static const char * const keysgrp[] = { "keys_4x4grp",
- "keys_8x8grp" };
-
-static const struct adi_pmx_func adi_pmx_functions[] = {
- ADI_PMX_FUNCTION("uart0", uart0grp),
- ADI_PMX_FUNCTION("uart1", uart1grp),
- ADI_PMX_FUNCTION("uart1_ctsrts", uart1ctsrtsgrp),
- ADI_PMX_FUNCTION("uart2", uart2grp),
- ADI_PMX_FUNCTION("uart3", uart3grp),
- ADI_PMX_FUNCTION("uart3_ctsrts", uart3ctsrtsgrp),
- ADI_PMX_FUNCTION("rsi0", rsi0grp),
- ADI_PMX_FUNCTION("spi0", spi0grp),
- ADI_PMX_FUNCTION("spi1", spi1grp),
- ADI_PMX_FUNCTION("twi0", twi0grp),
- ADI_PMX_FUNCTION("twi1", twi1grp),
- ADI_PMX_FUNCTION("rotary", rotarygrp),
- ADI_PMX_FUNCTION("can0", can0grp),
- ADI_PMX_FUNCTION("can1", can1grp),
- ADI_PMX_FUNCTION("smc0", smc0grp),
- ADI_PMX_FUNCTION("sport0", sport0grp),
- ADI_PMX_FUNCTION("sport1", sport1grp),
- ADI_PMX_FUNCTION("sport2", sport2grp),
- ADI_PMX_FUNCTION("sport3", sport3grp),
- ADI_PMX_FUNCTION("ppi0", ppi0grp),
- ADI_PMX_FUNCTION("ppi1", ppi1grp),
- ADI_PMX_FUNCTION("ppi2", ppi2grp),
- ADI_PMX_FUNCTION("atapi", atapigrp),
- ADI_PMX_FUNCTION("atapi_alter", atapialtergrp),
- ADI_PMX_FUNCTION("nfc0", nfc0grp),
- ADI_PMX_FUNCTION("keys", keysgrp),
-};
-
-static const struct adi_pinctrl_soc_data adi_bf54x_soc = {
- .functions = adi_pmx_functions,
- .nfunctions = ARRAY_SIZE(adi_pmx_functions),
- .groups = adi_pin_groups,
- .ngroups = ARRAY_SIZE(adi_pin_groups),
- .pins = adi_pads,
- .npins = ARRAY_SIZE(adi_pads),
-};
-
-void adi_pinctrl_soc_init(const struct adi_pinctrl_soc_data **soc)
-{
- *soc = &adi_bf54x_soc;
-}
diff --git a/drivers/pinctrl/pinctrl-adi2-bf60x.c b/drivers/pinctrl/pinctrl-adi2-bf60x.c
deleted file mode 100644
index fcfa00821f12..000000000000
--- a/drivers/pinctrl/pinctrl-adi2-bf60x.c
+++ /dev/null
@@ -1,517 +0,0 @@
-/*
- * Pinctrl Driver for ADI GPIO2 controller
- *
- * Copyright 2007-2013 Analog Devices Inc.
- *
- * Licensed under the GPLv2 or later
- */
-
-#include <asm/portmux.h>
-#include "pinctrl-adi2.h"
-
-static const struct pinctrl_pin_desc adi_pads[] = {
- PINCTRL_PIN(0, "PA0"),
- PINCTRL_PIN(1, "PA1"),
- PINCTRL_PIN(2, "PA2"),
- PINCTRL_PIN(3, "PG3"),
- PINCTRL_PIN(4, "PA4"),
- PINCTRL_PIN(5, "PA5"),
- PINCTRL_PIN(6, "PA6"),
- PINCTRL_PIN(7, "PA7"),
- PINCTRL_PIN(8, "PA8"),
- PINCTRL_PIN(9, "PA9"),
- PINCTRL_PIN(10, "PA10"),
- PINCTRL_PIN(11, "PA11"),
- PINCTRL_PIN(12, "PA12"),
- PINCTRL_PIN(13, "PA13"),
- PINCTRL_PIN(14, "PA14"),
- PINCTRL_PIN(15, "PA15"),
- PINCTRL_PIN(16, "PB0"),
- PINCTRL_PIN(17, "PB1"),
- PINCTRL_PIN(18, "PB2"),
- PINCTRL_PIN(19, "PB3"),
- PINCTRL_PIN(20, "PB4"),
- PINCTRL_PIN(21, "PB5"),
- PINCTRL_PIN(22, "PB6"),
- PINCTRL_PIN(23, "PB7"),
- PINCTRL_PIN(24, "PB8"),
- PINCTRL_PIN(25, "PB9"),
- PINCTRL_PIN(26, "PB10"),
- PINCTRL_PIN(27, "PB11"),
- PINCTRL_PIN(28, "PB12"),
- PINCTRL_PIN(29, "PB13"),
- PINCTRL_PIN(30, "PB14"),
- PINCTRL_PIN(31, "PB15"),
- PINCTRL_PIN(32, "PC0"),
- PINCTRL_PIN(33, "PC1"),
- PINCTRL_PIN(34, "PC2"),
- PINCTRL_PIN(35, "PC3"),
- PINCTRL_PIN(36, "PC4"),
- PINCTRL_PIN(37, "PC5"),
- PINCTRL_PIN(38, "PC6"),
- PINCTRL_PIN(39, "PC7"),
- PINCTRL_PIN(40, "PC8"),
- PINCTRL_PIN(41, "PC9"),
- PINCTRL_PIN(42, "PC10"),
- PINCTRL_PIN(43, "PC11"),
- PINCTRL_PIN(44, "PC12"),
- PINCTRL_PIN(45, "PC13"),
- PINCTRL_PIN(46, "PC14"),
- PINCTRL_PIN(47, "PC15"),
- PINCTRL_PIN(48, "PD0"),
- PINCTRL_PIN(49, "PD1"),
- PINCTRL_PIN(50, "PD2"),
- PINCTRL_PIN(51, "PD3"),
- PINCTRL_PIN(52, "PD4"),
- PINCTRL_PIN(53, "PD5"),
- PINCTRL_PIN(54, "PD6"),
- PINCTRL_PIN(55, "PD7"),
- PINCTRL_PIN(56, "PD8"),
- PINCTRL_PIN(57, "PD9"),
- PINCTRL_PIN(58, "PD10"),
- PINCTRL_PIN(59, "PD11"),
- PINCTRL_PIN(60, "PD12"),
- PINCTRL_PIN(61, "PD13"),
- PINCTRL_PIN(62, "PD14"),
- PINCTRL_PIN(63, "PD15"),
- PINCTRL_PIN(64, "PE0"),
- PINCTRL_PIN(65, "PE1"),
- PINCTRL_PIN(66, "PE2"),
- PINCTRL_PIN(67, "PE3"),
- PINCTRL_PIN(68, "PE4"),
- PINCTRL_PIN(69, "PE5"),
- PINCTRL_PIN(70, "PE6"),
- PINCTRL_PIN(71, "PE7"),
- PINCTRL_PIN(72, "PE8"),
- PINCTRL_PIN(73, "PE9"),
- PINCTRL_PIN(74, "PE10"),
- PINCTRL_PIN(75, "PE11"),
- PINCTRL_PIN(76, "PE12"),
- PINCTRL_PIN(77, "PE13"),
- PINCTRL_PIN(78, "PE14"),
- PINCTRL_PIN(79, "PE15"),
- PINCTRL_PIN(80, "PF0"),
- PINCTRL_PIN(81, "PF1"),
- PINCTRL_PIN(82, "PF2"),
- PINCTRL_PIN(83, "PF3"),
- PINCTRL_PIN(84, "PF4"),
- PINCTRL_PIN(85, "PF5"),
- PINCTRL_PIN(86, "PF6"),
- PINCTRL_PIN(87, "PF7"),
- PINCTRL_PIN(88, "PF8"),
- PINCTRL_PIN(89, "PF9"),
- PINCTRL_PIN(90, "PF10"),
- PINCTRL_PIN(91, "PF11"),
- PINCTRL_PIN(92, "PF12"),
- PINCTRL_PIN(93, "PF13"),
- PINCTRL_PIN(94, "PF14"),
- PINCTRL_PIN(95, "PF15"),
- PINCTRL_PIN(96, "PG0"),
- PINCTRL_PIN(97, "PG1"),
- PINCTRL_PIN(98, "PG2"),
- PINCTRL_PIN(99, "PG3"),
- PINCTRL_PIN(100, "PG4"),
- PINCTRL_PIN(101, "PG5"),
- PINCTRL_PIN(102, "PG6"),
- PINCTRL_PIN(103, "PG7"),
- PINCTRL_PIN(104, "PG8"),
- PINCTRL_PIN(105, "PG9"),
- PINCTRL_PIN(106, "PG10"),
- PINCTRL_PIN(107, "PG11"),
- PINCTRL_PIN(108, "PG12"),
- PINCTRL_PIN(109, "PG13"),
- PINCTRL_PIN(110, "PG14"),
- PINCTRL_PIN(111, "PG15"),
-};
-
-static const unsigned uart0_pins[] = {
- GPIO_PD7, GPIO_PD8,
-};
-
-static const unsigned uart0_ctsrts_pins[] = {
- GPIO_PD9, GPIO_PD10,
-};
-
-static const unsigned uart1_pins[] = {
- GPIO_PG15, GPIO_PG14,
-};
-
-static const unsigned uart1_ctsrts_pins[] = {
- GPIO_PG10, GPIO_PG13,
-};
-
-static const unsigned rsi0_pins[] = {
- GPIO_PG3, GPIO_PG2, GPIO_PG0, GPIO_PE15, GPIO_PG5, GPIO_PG6,
-};
-
-static const unsigned eth0_pins[] = {
- GPIO_PC6, GPIO_PC7, GPIO_PC2, GPIO_PC0, GPIO_PC3, GPIO_PC1,
- GPIO_PB13, GPIO_PD6, GPIO_PC5, GPIO_PC4, GPIO_PB14, GPIO_PB15,
-};
-
-static const unsigned eth1_pins[] = {
- GPIO_PE10, GPIO_PE11, GPIO_PG3, GPIO_PG0, GPIO_PG2, GPIO_PE15,
- GPIO_PG5, GPIO_PE12, GPIO_PE13, GPIO_PE14, GPIO_PG6, GPIO_PC9,
-};
-
-static const unsigned spi0_pins[] = {
- GPIO_PD4, GPIO_PD2, GPIO_PD3,
-};
-
-static const unsigned spi1_pins[] = {
- GPIO_PD5, GPIO_PD14, GPIO_PD13,
-};
-
-static const unsigned twi0_pins[] = {
-};
-
-static const unsigned twi1_pins[] = {
-};
-
-static const unsigned rotary_pins[] = {
- GPIO_PG7, GPIO_PG11, GPIO_PG12,
-};
-
-static const unsigned can0_pins[] = {
- GPIO_PG1, GPIO_PG4,
-};
-
-static const unsigned smc0_pins[] = {
- GPIO_PA0, GPIO_PA1, GPIO_PA2, GPIO_PA3, GPIO_PA4, GPIO_PA5, GPIO_PA6,
- GPIO_PA7, GPIO_PA8, GPIO_PA9, GPIO_PB2, GPIO_PA10, GPIO_PA11,
- GPIO_PB3, GPIO_PA12, GPIO_PA13, GPIO_PA14, GPIO_PA15, GPIO_PB6,
- GPIO_PB7, GPIO_PB8, GPIO_PB10, GPIO_PB11, GPIO_PB0,
-};
-
-static const unsigned sport0_pins[] = {
- GPIO_PB5, GPIO_PB4, GPIO_PB9, GPIO_PB8, GPIO_PB7, GPIO_PB11,
-};
-
-static const unsigned sport1_pins[] = {
- GPIO_PE2, GPIO_PE5, GPIO_PD15, GPIO_PE4, GPIO_PE3, GPIO_PE1,
-};
-
-static const unsigned sport2_pins[] = {
- GPIO_PG4, GPIO_PG1, GPIO_PG9, GPIO_PG10, GPIO_PG7, GPIO_PB12,
-};
-
-static const unsigned ppi0_8b_pins[] = {
- GPIO_PF0, GPIO_PF1, GPIO_PF2, GPIO_PF3, GPIO_PF4, GPIO_PF5, GPIO_PF6,
- GPIO_PF7, GPIO_PF13, GPIO_PF14, GPIO_PF15,
- GPIO_PE6, GPIO_PE7, GPIO_PE8, GPIO_PE9,
-};
-
-static const unsigned ppi0_16b_pins[] = {
- GPIO_PF0, GPIO_PF1, GPIO_PF2, GPIO_PF3, GPIO_PF4, GPIO_PF5, GPIO_PF6,
- GPIO_PF7, GPIO_PF9, GPIO_PF10, GPIO_PF11, GPIO_PF12,
- GPIO_PF13, GPIO_PF14, GPIO_PF15,
- GPIO_PE6, GPIO_PE7, GPIO_PE8, GPIO_PE9,
-};
-
-static const unsigned ppi0_24b_pins[] = {
- GPIO_PF0, GPIO_PF1, GPIO_PF2, GPIO_PF3, GPIO_PF4, GPIO_PF5, GPIO_PF6,
- GPIO_PF7, GPIO_PF8, GPIO_PF9, GPIO_PF10, GPIO_PF11, GPIO_PF12,
- GPIO_PF13, GPIO_PF14, GPIO_PF15, GPIO_PE0, GPIO_PE1, GPIO_PE2,
- GPIO_PE3, GPIO_PE4, GPIO_PE5, GPIO_PE6, GPIO_PE7, GPIO_PE8,
- GPIO_PE9, GPIO_PD12, GPIO_PD15,
-};
-
-static const unsigned ppi1_8b_pins[] = {
- GPIO_PC0, GPIO_PC1, GPIO_PC2, GPIO_PC3, GPIO_PC4, GPIO_PC5, GPIO_PC6,
- GPIO_PC7, GPIO_PC8, GPIO_PB13, GPIO_PB14, GPIO_PB15, GPIO_PD6,
-};
-
-static const unsigned ppi1_16b_pins[] = {
- GPIO_PC0, GPIO_PC1, GPIO_PC2, GPIO_PC3, GPIO_PC4, GPIO_PC5, GPIO_PC6,
- GPIO_PC7, GPIO_PC9, GPIO_PC10, GPIO_PC11, GPIO_PC12,
- GPIO_PC13, GPIO_PC14, GPIO_PC15,
- GPIO_PB13, GPIO_PB14, GPIO_PB15, GPIO_PD6,
-};
-
-static const unsigned ppi2_8b_pins[] = {
- GPIO_PA0, GPIO_PA1, GPIO_PA2, GPIO_PA3, GPIO_PA4, GPIO_PA5, GPIO_PA6,
- GPIO_PA7, GPIO_PB0, GPIO_PB1, GPIO_PB2, GPIO_PB3,
-};
-
-static const unsigned ppi2_16b_pins[] = {
- GPIO_PA0, GPIO_PA1, GPIO_PA2, GPIO_PA3, GPIO_PA4, GPIO_PA5, GPIO_PA6,
- GPIO_PA7, GPIO_PA8, GPIO_PA9, GPIO_PA10, GPIO_PA11, GPIO_PA12,
- GPIO_PA13, GPIO_PA14, GPIO_PA15, GPIO_PB0, GPIO_PB1, GPIO_PB2,
-};
-
-static const unsigned lp0_pins[] = {
- GPIO_PB0, GPIO_PB1, GPIO_PA0, GPIO_PA1, GPIO_PA2, GPIO_PA3,
- GPIO_PA4, GPIO_PA5, GPIO_PA6, GPIO_PA7,
-};
-
-static const unsigned lp1_pins[] = {
- GPIO_PB3, GPIO_PB2, GPIO_PA8, GPIO_PA9, GPIO_PA10, GPIO_PA11,
- GPIO_PA12, GPIO_PA13, GPIO_PA14, GPIO_PA15,
-};
-
-static const unsigned lp2_pins[] = {
- GPIO_PE6, GPIO_PE7, GPIO_PF0, GPIO_PF1, GPIO_PF2, GPIO_PF3,
- GPIO_PF4, GPIO_PF5, GPIO_PF6, GPIO_PF7,
-};
-
-static const unsigned lp3_pins[] = {
- GPIO_PE9, GPIO_PE8, GPIO_PF8, GPIO_PF9, GPIO_PF10, GPIO_PF11,
- GPIO_PF12, GPIO_PF13, GPIO_PF14, GPIO_PF15,
-};
-
-static const unsigned short uart0_mux[] = {
- P_UART0_TX, P_UART0_RX,
- 0
-};
-
-static const unsigned short uart0_ctsrts_mux[] = {
- P_UART0_RTS, P_UART0_CTS,
- 0
-};
-
-static const unsigned short uart1_mux[] = {
- P_UART1_TX, P_UART1_RX,
- 0
-};
-
-static const unsigned short uart1_ctsrts_mux[] = {
- P_UART1_RTS, P_UART1_CTS,
- 0
-};
-
-static const unsigned short rsi0_mux[] = {
- P_RSI_DATA0, P_RSI_DATA1, P_RSI_DATA2, P_RSI_DATA3,
- P_RSI_CMD, P_RSI_CLK, 0
-};
-
-static const unsigned short eth0_mux[] = P_RMII0;
-static const unsigned short eth1_mux[] = P_RMII1;
-
-static const unsigned short spi0_mux[] = {
- P_SPI0_SCK, P_SPI0_MISO, P_SPI0_MOSI, 0
-};
-
-static const unsigned short spi1_mux[] = {
- P_SPI1_SCK, P_SPI1_MISO, P_SPI1_MOSI, 0
-};
-
-static const unsigned short twi0_mux[] = {
- P_TWI0_SCL, P_TWI0_SDA, 0
-};
-
-static const unsigned short twi1_mux[] = {
- P_TWI1_SCL, P_TWI1_SDA, 0
-};
-
-static const unsigned short rotary_mux[] = {
- P_CNT_CUD, P_CNT_CDG, P_CNT_CZM, 0
-};
-
-static const unsigned short sport0_mux[] = {
- P_SPORT0_ACLK, P_SPORT0_AFS, P_SPORT0_AD0, P_SPORT0_BCLK,
- P_SPORT0_BFS, P_SPORT0_BD0, 0,
-};
-
-static const unsigned short sport1_mux[] = {
- P_SPORT1_ACLK, P_SPORT1_AFS, P_SPORT1_AD0, P_SPORT1_BCLK,
- P_SPORT1_BFS, P_SPORT1_BD0, 0,
-};
-
-static const unsigned short sport2_mux[] = {
- P_SPORT2_ACLK, P_SPORT2_AFS, P_SPORT2_AD0, P_SPORT2_BCLK,
- P_SPORT2_BFS, P_SPORT2_BD0, 0,
-};
-
-static const unsigned short can0_mux[] = {
- P_CAN0_RX, P_CAN0_TX, 0
-};
-
-static const unsigned short smc0_mux[] = {
- P_A3, P_A4, P_A5, P_A6, P_A7, P_A8, P_A9, P_A10, P_A11, P_A12,
- P_A13, P_A14, P_A15, P_A16, P_A17, P_A18, P_A19, P_A20, P_A21,
- P_A22, P_A23, P_A24, P_A25, P_NORCK, 0,
-};
-
-static const unsigned short ppi0_8b_mux[] = {
- P_PPI0_D0, P_PPI0_D1, P_PPI0_D2, P_PPI0_D3,
- P_PPI0_D4, P_PPI0_D5, P_PPI0_D6, P_PPI0_D7,
- P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2,
- 0,
-};
-
-static const unsigned short ppi0_16b_mux[] = {
- P_PPI0_D0, P_PPI0_D1, P_PPI0_D2, P_PPI0_D3,
- P_PPI0_D4, P_PPI0_D5, P_PPI0_D6, P_PPI0_D7,
- P_PPI0_D8, P_PPI0_D9, P_PPI0_D10, P_PPI0_D11,
- P_PPI0_D12, P_PPI0_D13, P_PPI0_D14, P_PPI0_D15,
- P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2,
- 0,
-};
-
-static const unsigned short ppi0_24b_mux[] = {
- P_PPI0_D0, P_PPI0_D1, P_PPI0_D2, P_PPI0_D3,
- P_PPI0_D4, P_PPI0_D5, P_PPI0_D6, P_PPI0_D7,
- P_PPI0_D8, P_PPI0_D9, P_PPI0_D10, P_PPI0_D11,
- P_PPI0_D12, P_PPI0_D13, P_PPI0_D14, P_PPI0_D15,
- P_PPI0_D16, P_PPI0_D17, P_PPI0_D18, P_PPI0_D19,
- P_PPI0_D20, P_PPI0_D21, P_PPI0_D22, P_PPI0_D23,
- P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2,
- 0,
-};
-
-static const unsigned short ppi1_8b_mux[] = {
- P_PPI1_D0, P_PPI1_D1, P_PPI1_D2, P_PPI1_D3,
- P_PPI1_D4, P_PPI1_D5, P_PPI1_D6, P_PPI1_D7,
- P_PPI1_CLK, P_PPI1_FS1, P_PPI1_FS2,
- 0,
-};
-
-static const unsigned short ppi1_16b_mux[] = {
- P_PPI1_D0, P_PPI1_D1, P_PPI1_D2, P_PPI1_D3,
- P_PPI1_D4, P_PPI1_D5, P_PPI1_D6, P_PPI1_D7,
- P_PPI1_D8, P_PPI1_D9, P_PPI1_D10, P_PPI1_D11,
- P_PPI1_D12, P_PPI1_D13, P_PPI1_D14, P_PPI1_D15,
- P_PPI1_CLK, P_PPI1_FS1, P_PPI1_FS2,
- 0,
-};
-
-static const unsigned short ppi2_8b_mux[] = {
- P_PPI2_D0, P_PPI2_D1, P_PPI2_D2, P_PPI2_D3,
- P_PPI2_D4, P_PPI2_D5, P_PPI2_D6, P_PPI2_D7,
- P_PPI2_CLK, P_PPI2_FS1, P_PPI2_FS2,
- 0,
-};
-
-static const unsigned short ppi2_16b_mux[] = {
- P_PPI2_D0, P_PPI2_D1, P_PPI2_D2, P_PPI2_D3,
- P_PPI2_D4, P_PPI2_D5, P_PPI2_D6, P_PPI2_D7,
- P_PPI2_D8, P_PPI2_D9, P_PPI2_D10, P_PPI2_D11,
- P_PPI2_D12, P_PPI2_D13, P_PPI2_D14, P_PPI2_D15,
- P_PPI2_CLK, P_PPI2_FS1, P_PPI2_FS2,
- 0,
-};
-
-static const unsigned short lp0_mux[] = {
- P_LP0_CLK, P_LP0_ACK, P_LP0_D0, P_LP0_D1, P_LP0_D2,
- P_LP0_D3, P_LP0_D4, P_LP0_D5, P_LP0_D6, P_LP0_D7,
- 0
-};
-
-static const unsigned short lp1_mux[] = {
- P_LP1_CLK, P_LP1_ACK, P_LP1_D0, P_LP1_D1, P_LP1_D2,
- P_LP1_D3, P_LP1_D4, P_LP1_D5, P_LP1_D6, P_LP1_D7,
- 0
-};
-
-static const unsigned short lp2_mux[] = {
- P_LP2_CLK, P_LP2_ACK, P_LP2_D0, P_LP2_D1, P_LP2_D2,
- P_LP2_D3, P_LP2_D4, P_LP2_D5, P_LP2_D6, P_LP2_D7,
- 0
-};
-
-static const unsigned short lp3_mux[] = {
- P_LP3_CLK, P_LP3_ACK, P_LP3_D0, P_LP3_D1, P_LP3_D2,
- P_LP3_D3, P_LP3_D4, P_LP3_D5, P_LP3_D6, P_LP3_D7,
- 0
-};
-
-static const struct adi_pin_group adi_pin_groups[] = {
- ADI_PIN_GROUP("uart0grp", uart0_pins, uart0_mux),
- ADI_PIN_GROUP("uart0ctsrtsgrp", uart0_ctsrts_pins, uart0_ctsrts_mux),
- ADI_PIN_GROUP("uart1grp", uart1_pins, uart1_mux),
- ADI_PIN_GROUP("uart1ctsrtsgrp", uart1_ctsrts_pins, uart1_ctsrts_mux),
- ADI_PIN_GROUP("rsi0grp", rsi0_pins, rsi0_mux),
- ADI_PIN_GROUP("eth0grp", eth0_pins, eth0_mux),
- ADI_PIN_GROUP("eth1grp", eth1_pins, eth1_mux),
- ADI_PIN_GROUP("spi0grp", spi0_pins, spi0_mux),
- ADI_PIN_GROUP("spi1grp", spi1_pins, spi1_mux),
- ADI_PIN_GROUP("twi0grp", twi0_pins, twi0_mux),
- ADI_PIN_GROUP("twi1grp", twi1_pins, twi1_mux),
- ADI_PIN_GROUP("rotarygrp", rotary_pins, rotary_mux),
- ADI_PIN_GROUP("can0grp", can0_pins, can0_mux),
- ADI_PIN_GROUP("smc0grp", smc0_pins, smc0_mux),
- ADI_PIN_GROUP("sport0grp", sport0_pins, sport0_mux),
- ADI_PIN_GROUP("sport1grp", sport1_pins, sport1_mux),
- ADI_PIN_GROUP("sport2grp", sport2_pins, sport2_mux),
- ADI_PIN_GROUP("ppi0_8bgrp", ppi0_8b_pins, ppi0_8b_mux),
- ADI_PIN_GROUP("ppi0_16bgrp", ppi0_16b_pins, ppi0_16b_mux),
- ADI_PIN_GROUP("ppi0_24bgrp", ppi0_24b_pins, ppi0_24b_mux),
- ADI_PIN_GROUP("ppi1_8bgrp", ppi1_8b_pins, ppi1_8b_mux),
- ADI_PIN_GROUP("ppi1_16bgrp", ppi1_16b_pins, ppi1_16b_mux),
- ADI_PIN_GROUP("ppi2_8bgrp", ppi2_8b_pins, ppi2_8b_mux),
- ADI_PIN_GROUP("ppi2_16bgrp", ppi2_16b_pins, ppi2_16b_mux),
- ADI_PIN_GROUP("lp0grp", lp0_pins, lp0_mux),
- ADI_PIN_GROUP("lp1grp", lp1_pins, lp1_mux),
- ADI_PIN_GROUP("lp2grp", lp2_pins, lp2_mux),
- ADI_PIN_GROUP("lp3grp", lp3_pins, lp3_mux),
-};
-
-static const char * const uart0grp[] = { "uart0grp" };
-static const char * const uart0ctsrtsgrp[] = { "uart0ctsrtsgrp" };
-static const char * const uart1grp[] = { "uart1grp" };
-static const char * const uart1ctsrtsgrp[] = { "uart1ctsrtsgrp" };
-static const char * const rsi0grp[] = { "rsi0grp" };
-static const char * const eth0grp[] = { "eth0grp" };
-static const char * const eth1grp[] = { "eth1grp" };
-static const char * const spi0grp[] = { "spi0grp" };
-static const char * const spi1grp[] = { "spi1grp" };
-static const char * const twi0grp[] = { "twi0grp" };
-static const char * const twi1grp[] = { "twi1grp" };
-static const char * const rotarygrp[] = { "rotarygrp" };
-static const char * const can0grp[] = { "can0grp" };
-static const char * const smc0grp[] = { "smc0grp" };
-static const char * const sport0grp[] = { "sport0grp" };
-static const char * const sport1grp[] = { "sport1grp" };
-static const char * const sport2grp[] = { "sport2grp" };
-static const char * const ppi0grp[] = { "ppi0_8bgrp",
- "ppi0_16bgrp",
- "ppi0_24bgrp" };
-static const char * const ppi1grp[] = { "ppi1_8bgrp",
- "ppi1_16bgrp" };
-static const char * const ppi2grp[] = { "ppi2_8bgrp",
- "ppi2_16bgrp" };
-static const char * const lp0grp[] = { "lp0grp" };
-static const char * const lp1grp[] = { "lp1grp" };
-static const char * const lp2grp[] = { "lp2grp" };
-static const char * const lp3grp[] = { "lp3grp" };
-
-static const struct adi_pmx_func adi_pmx_functions[] = {
- ADI_PMX_FUNCTION("uart0", uart0grp),
- ADI_PMX_FUNCTION("uart0_ctsrts", uart0ctsrtsgrp),
- ADI_PMX_FUNCTION("uart1", uart1grp),
- ADI_PMX_FUNCTION("uart1_ctsrts", uart1ctsrtsgrp),
- ADI_PMX_FUNCTION("rsi0", rsi0grp),
- ADI_PMX_FUNCTION("eth0", eth0grp),
- ADI_PMX_FUNCTION("eth1", eth1grp),
- ADI_PMX_FUNCTION("spi0", spi0grp),
- ADI_PMX_FUNCTION("spi1", spi1grp),
- ADI_PMX_FUNCTION("twi0", twi0grp),
- ADI_PMX_FUNCTION("twi1", twi1grp),
- ADI_PMX_FUNCTION("rotary", rotarygrp),
- ADI_PMX_FUNCTION("can0", can0grp),
- ADI_PMX_FUNCTION("smc0", smc0grp),
- ADI_PMX_FUNCTION("sport0", sport0grp),
- ADI_PMX_FUNCTION("sport1", sport1grp),
- ADI_PMX_FUNCTION("sport2", sport2grp),
- ADI_PMX_FUNCTION("ppi0", ppi0grp),
- ADI_PMX_FUNCTION("ppi1", ppi1grp),
- ADI_PMX_FUNCTION("ppi2", ppi2grp),
- ADI_PMX_FUNCTION("lp0", lp0grp),
- ADI_PMX_FUNCTION("lp1", lp1grp),
- ADI_PMX_FUNCTION("lp2", lp2grp),
- ADI_PMX_FUNCTION("lp3", lp3grp),
-};
-
-static const struct adi_pinctrl_soc_data adi_bf60x_soc = {
- .functions = adi_pmx_functions,
- .nfunctions = ARRAY_SIZE(adi_pmx_functions),
- .groups = adi_pin_groups,
- .ngroups = ARRAY_SIZE(adi_pin_groups),
- .pins = adi_pads,
- .npins = ARRAY_SIZE(adi_pads),
-};
-
-void adi_pinctrl_soc_init(const struct adi_pinctrl_soc_data **soc)
-{
- *soc = &adi_bf60x_soc;
-}
diff --git a/drivers/pinctrl/pinctrl-adi2.c b/drivers/pinctrl/pinctrl-adi2.c
deleted file mode 100644
index 094a451db2a2..000000000000
--- a/drivers/pinctrl/pinctrl-adi2.c
+++ /dev/null
@@ -1,1114 +0,0 @@
-/*
- * Pinctrl Driver for ADI GPIO2 controller
- *
- * Copyright 2007-2013 Analog Devices Inc.
- *
- * Licensed under the GPLv2 or later
- */
-
-#include <linux/bitops.h>
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/err.h>
-#include <linux/debugfs.h>
-#include <linux/seq_file.h>
-#include <linux/irq.h>
-#include <linux/platform_data/pinctrl-adi2.h>
-#include <linux/irqdomain.h>
-#include <linux/irqchip/chained_irq.h>
-#include <linux/pinctrl/pinctrl.h>
-#include <linux/pinctrl/pinmux.h>
-#include <linux/pinctrl/consumer.h>
-#include <linux/pinctrl/machine.h>
-#include <linux/syscore_ops.h>
-#include <linux/gpio.h>
-#include <asm/portmux.h>
-#include "pinctrl-adi2.h"
-#include "core.h"
-
-/*
-According to the BF54x HRM, pint means "pin interrupt".
-http://www.analog.com/static/imported-files/processor_manuals/ADSP-BF54x_hwr_rev1.2.pdf
-
-ADSP-BF54x processor Blackfin processors have four SIC interrupt chan-
-nels dedicated to pin interrupt purposes. These channels are managed by
-four hardware blocks, called PINT0, PINT1, PINT2, and PINT3. Every PINTx
-block can sense to up to 32 pins. While PINT0 and PINT1 can sense the
-pins of port A and port B, PINT2 and PINT3 manage all the pins from port
-C to port J as shown in Figure 9-2.
-
-n BF54x HRM:
-The ten GPIO ports are subdivided into 8-bit half ports, resulting in lower and
-upper half 8-bit units. The PINTx_ASSIGN registers control the 8-bit multi-
-plexers shown in Figure 9-3. Lower half units of eight pins can be
-forwarded to either byte 0 or byte 2 of either associated PINTx block.
-Upper half units can be forwarded to either byte 1 or byte 3 of the pin
-interrupt blocks, without further restrictions.
-
-All MMR registers in the pin interrupt module are 32 bits wide. To simply the
-mapping logic, this driver only maps a 16-bit gpio port to the upper or lower
-16 bits of a PINTx block. You can find the Figure 9-3 on page 583.
-
-Each IRQ domain is binding to a GPIO bank device. 2 GPIO bank devices can map
-to one PINT device. Two in "struct gpio_pint" are used to ease the PINT
-interrupt handler.
-
-The GPIO bank mapping to the lower 16 bits of the PINT device set its IRQ
-domain pointer in domain[0]. The IRQ domain pointer of the other bank is set
-to domain[1]. PINT interrupt handler adi_gpio_handle_pint_irq() finds out
-the current domain pointer according to whether the interrupt request mask
-is in lower 16 bits (domain[0]) or upper 16bits (domain[1]).
-
-A PINT device is not part of a GPIO port device in Blackfin. Multiple GPIO
-port devices can be mapped to the same PINT device.
-
-*/
-
-static LIST_HEAD(adi_pint_list);
-static LIST_HEAD(adi_gpio_port_list);
-
-#define DRIVER_NAME "pinctrl-adi2"
-
-#define PINT_HI_OFFSET 16
-
-/**
- * struct gpio_port_saved - GPIO port registers that should be saved between
- * power suspend and resume operations.
- *
- * @fer: PORTx_FER register
- * @data: PORTx_DATA register
- * @dir: PORTx_DIR register
- * @inen: PORTx_INEN register
- * @mux: PORTx_MUX register
- */
-struct gpio_port_saved {
- u16 fer;
- u16 data;
- u16 dir;
- u16 inen;
- u32 mux;
-};
-
-/*
- * struct gpio_pint_saved - PINT registers saved in PM operations
- *
- * @assign: ASSIGN register
- * @edge_set: EDGE_SET register
- * @invert_set: INVERT_SET register
- */
-struct gpio_pint_saved {
- u32 assign;
- u32 edge_set;
- u32 invert_set;
-};
-
-/**
- * struct gpio_pint - Pin interrupt controller device. Multiple ADI GPIO
- * banks can be mapped into one Pin interrupt controller.
- *
- * @node: All gpio_pint instances are added to a global list.
- * @base: PINT device register base address
- * @irq: IRQ of the PINT device, it is the parent IRQ of all
- * GPIO IRQs mapping to this device.
- * @domain: [0] irq domain of the gpio port, whose hardware interrupts are
- * mapping to the low 16-bit of the pint registers.
- * [1] irq domain of the gpio port, whose hardware interrupts are
- * mapping to the high 16-bit of the pint registers.
- * @regs: address pointer to the PINT device
- * @map_count: No more than 2 GPIO banks can be mapped to this PINT device.
- * @lock: This lock make sure the irq_chip operations to one PINT device
- * for different GPIO interrrupts are atomic.
- * @pint_map_port: Set up the mapping between one PINT device and
- * multiple GPIO banks.
- */
-struct gpio_pint {
- struct list_head node;
- void __iomem *base;
- int irq;
- struct irq_domain *domain[2];
- struct gpio_pint_regs *regs;
- struct gpio_pint_saved saved_data;
- int map_count;
- spinlock_t lock;
-
- int (*pint_map_port)(struct gpio_pint *pint, bool assign,
- u8 map, struct irq_domain *domain);
-};
-
-/**
- * ADI pin controller
- *
- * @dev: a pointer back to containing device
- * @pctl: the pinctrl device
- * @soc: SoC data for this specific chip
- */
-struct adi_pinctrl {
- struct device *dev;
- struct pinctrl_dev *pctl;
- const struct adi_pinctrl_soc_data *soc;
-};
-
-/**
- * struct gpio_port - GPIO bank device. Multiple ADI GPIO banks can be mapped
- * into one pin interrupt controller.
- *
- * @node: All gpio_port instances are added to a list.
- * @base: GPIO bank device register base address
- * @irq_base: base IRQ of the GPIO bank device
- * @width: PIN number of the GPIO bank device
- * @regs: address pointer to the GPIO bank device
- * @saved_data: registers that should be saved between PM operations.
- * @dev: device structure of this GPIO bank
- * @pint: GPIO PINT device that this GPIO bank mapped to
- * @pint_map: GIOP bank mapping code in PINT device
- * @pint_assign: The 32-bit PINT registers can be divided into 2 parts. A
- * GPIO bank can be mapped into either low 16 bits[0] or high 16
- * bits[1] of each PINT register.
- * @lock: This lock make sure the irq_chip operations to one PINT device
- * for different GPIO interrrupts are atomic.
- * @chip: abstract a GPIO controller
- * @domain: The irq domain owned by the GPIO port.
- * @rsvmap: Reservation map array for each pin in the GPIO bank
- */
-struct gpio_port {
- struct list_head node;
- void __iomem *base;
- int irq_base;
- unsigned int width;
- struct gpio_port_t *regs;
- struct gpio_port_saved saved_data;
- struct device *dev;
-
- struct gpio_pint *pint;
- u8 pint_map;
- bool pint_assign;
-
- spinlock_t lock;
- struct gpio_chip chip;
- struct irq_domain *domain;
-};
-
-static inline u8 pin_to_offset(struct pinctrl_gpio_range *range, unsigned pin)
-{
- return pin - range->pin_base;
-}
-
-static inline u32 hwirq_to_pintbit(struct gpio_port *port, int hwirq)
-{
- return port->pint_assign ? BIT(hwirq) << PINT_HI_OFFSET : BIT(hwirq);
-}
-
-static struct gpio_pint *find_gpio_pint(unsigned id)
-{
- struct gpio_pint *pint;
- int i = 0;
-
- list_for_each_entry(pint, &adi_pint_list, node) {
- if (id == i)
- return pint;
- i++;
- }
-
- return NULL;
-}
-
-static inline void port_setup(struct gpio_port *port, unsigned offset,
- bool use_for_gpio)
-{
- struct gpio_port_t *regs = port->regs;
-
- if (use_for_gpio)
- writew(readw(&regs->port_fer) & ~BIT(offset),
- &regs->port_fer);
- else
- writew(readw(&regs->port_fer) | BIT(offset), &regs->port_fer);
-}
-
-static inline void portmux_setup(struct gpio_port *port, unsigned offset,
- unsigned short function)
-{
- struct gpio_port_t *regs = port->regs;
- u32 pmux;
-
- pmux = readl(&regs->port_mux);
-
- /* The function field of each pin has 2 consecutive bits in
- * the mux register.
- */
- pmux &= ~(0x3 << (2 * offset));
- pmux |= (function & 0x3) << (2 * offset);
-
- writel(pmux, &regs->port_mux);
-}
-
-static inline u16 get_portmux(struct gpio_port *port, unsigned offset)
-{
- struct gpio_port_t *regs = port->regs;
- u32 pmux = readl(&regs->port_mux);
-
- /* The function field of each pin has 2 consecutive bits in
- * the mux register.
- */
- return pmux >> (2 * offset) & 0x3;
-}
-
-static void adi_gpio_ack_irq(struct irq_data *d)
-{
- unsigned long flags;
- struct gpio_port *port = irq_data_get_irq_chip_data(d);
- struct gpio_pint_regs *regs = port->pint->regs;
- unsigned pintbit = hwirq_to_pintbit(port, d->hwirq);
-
- spin_lock_irqsave(&port->lock, flags);
- spin_lock(&port->pint->lock);
-
- if (irqd_get_trigger_type(d) == IRQ_TYPE_EDGE_BOTH) {
- if (readl(&regs->invert_set) & pintbit)
- writel(pintbit, &regs->invert_clear);
- else
- writel(pintbit, &regs->invert_set);
- }
-
- writel(pintbit, &regs->request);
-
- spin_unlock(&port->pint->lock);
- spin_unlock_irqrestore(&port->lock, flags);
-}
-
-static void adi_gpio_mask_ack_irq(struct irq_data *d)
-{
- unsigned long flags;
- struct gpio_port *port = irq_data_get_irq_chip_data(d);
- struct gpio_pint_regs *regs = port->pint->regs;
- unsigned pintbit = hwirq_to_pintbit(port, d->hwirq);
-
- spin_lock_irqsave(&port->lock, flags);
- spin_lock(&port->pint->lock);
-
- if (irqd_get_trigger_type(d) == IRQ_TYPE_EDGE_BOTH) {
- if (readl(&regs->invert_set) & pintbit)
- writel(pintbit, &regs->invert_clear);
- else
- writel(pintbit, &regs->invert_set);
- }
-
- writel(pintbit, &regs->request);
- writel(pintbit, &regs->mask_clear);
-
- spin_unlock(&port->pint->lock);
- spin_unlock_irqrestore(&port->lock, flags);
-}
-
-static void adi_gpio_mask_irq(struct irq_data *d)
-{
- unsigned long flags;
- struct gpio_port *port = irq_data_get_irq_chip_data(d);
- struct gpio_pint_regs *regs = port->pint->regs;
-
- spin_lock_irqsave(&port->lock, flags);
- spin_lock(&port->pint->lock);
-
- writel(hwirq_to_pintbit(port, d->hwirq), &regs->mask_clear);
-
- spin_unlock(&port->pint->lock);
- spin_unlock_irqrestore(&port->lock, flags);
-}
-
-static void adi_gpio_unmask_irq(struct irq_data *d)
-{
- unsigned long flags;
- struct gpio_port *port = irq_data_get_irq_chip_data(d);
- struct gpio_pint_regs *regs = port->pint->regs;
-
- spin_lock_irqsave(&port->lock, flags);
- spin_lock(&port->pint->lock);
-
- writel(hwirq_to_pintbit(port, d->hwirq), &regs->mask_set);
-
- spin_unlock(&port->pint->lock);
- spin_unlock_irqrestore(&port->lock, flags);
-}
-
-static unsigned int adi_gpio_irq_startup(struct irq_data *d)
-{
- unsigned long flags;
- struct gpio_port *port = irq_data_get_irq_chip_data(d);
- struct gpio_pint_regs *regs;
-
- if (!port) {
- pr_err("GPIO IRQ %d :Not exist\n", d->irq);
- /* FIXME: negative return code will be ignored */
- return -ENODEV;
- }
-
- regs = port->pint->regs;
-
- spin_lock_irqsave(&port->lock, flags);
- spin_lock(&port->pint->lock);
-
- port_setup(port, d->hwirq, true);
- writew(BIT(d->hwirq), &port->regs->dir_clear);
- writew(readw(&port->regs->inen) | BIT(d->hwirq), &port->regs->inen);
-
- writel(hwirq_to_pintbit(port, d->hwirq), &regs->mask_set);
-
- spin_unlock(&port->pint->lock);
- spin_unlock_irqrestore(&port->lock, flags);
-
- return 0;
-}
-
-static void adi_gpio_irq_shutdown(struct irq_data *d)
-{
- unsigned long flags;
- struct gpio_port *port = irq_data_get_irq_chip_data(d);
- struct gpio_pint_regs *regs = port->pint->regs;
-
- spin_lock_irqsave(&port->lock, flags);
- spin_lock(&port->pint->lock);
-
- writel(hwirq_to_pintbit(port, d->hwirq), &regs->mask_clear);
-
- spin_unlock(&port->pint->lock);
- spin_unlock_irqrestore(&port->lock, flags);
-}
-
-static int adi_gpio_irq_type(struct irq_data *d, unsigned int type)
-{
- unsigned long flags;
- struct gpio_port *port = irq_data_get_irq_chip_data(d);
- struct gpio_pint_regs *pint_regs;
- unsigned pintmask;
- unsigned int irq = d->irq;
- int ret = 0;
- char buf[16];
-
- if (!port) {
- pr_err("GPIO IRQ %d :Not exist\n", d->irq);
- return -ENODEV;
- }
-
- pint_regs = port->pint->regs;
-
- pintmask = hwirq_to_pintbit(port, d->hwirq);
-
- spin_lock_irqsave(&port->lock, flags);
- spin_lock(&port->pint->lock);
-
- /* In case of interrupt autodetect, set irq type to edge sensitive. */
- if (type == IRQ_TYPE_PROBE)
- type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
-
- if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING |
- IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {
- snprintf(buf, 16, "gpio-irq%u", irq);
- port_setup(port, d->hwirq, true);
- } else
- goto out;
-
- /* The GPIO interrupt is triggered only when its input value
- * transfer from 0 to 1. So, invert the input value if the
- * irq type is low or falling
- */
- if ((type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_LEVEL_LOW)))
- writel(pintmask, &pint_regs->invert_set);
- else
- writel(pintmask, &pint_regs->invert_clear);
-
- /* In edge sensitive case, if the input value of the requested irq
- * is already 1, invert it.
- */
- if ((type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) {
- if (gpio_get_value(port->chip.base + d->hwirq))
- writel(pintmask, &pint_regs->invert_set);
- else
- writel(pintmask, &pint_regs->invert_clear);
- }
-
- if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) {
- writel(pintmask, &pint_regs->edge_set);
- irq_set_handler_locked(d, handle_edge_irq);
- } else {
- writel(pintmask, &pint_regs->edge_clear);
- irq_set_handler_locked(d, handle_level_irq);
- }
-
-out:
- spin_unlock(&port->pint->lock);
- spin_unlock_irqrestore(&port->lock, flags);
-
- return ret;
-}
-
-#ifdef CONFIG_PM
-static int adi_gpio_set_wake(struct irq_data *d, unsigned int state)
-{
- struct gpio_port *port = irq_data_get_irq_chip_data(d);
-
- if (!port || !port->pint || port->pint->irq != d->irq)
- return -EINVAL;
-
-#ifndef SEC_GCTL
- adi_internal_set_wake(port->pint->irq, state);
-#endif
-
- return 0;
-}
-
-static int adi_pint_suspend(void)
-{
- struct gpio_pint *pint;
-
- list_for_each_entry(pint, &adi_pint_list, node) {
- writel(0xffffffff, &pint->regs->mask_clear);
- pint->saved_data.assign = readl(&pint->regs->assign);
- pint->saved_data.edge_set = readl(&pint->regs->edge_set);
- pint->saved_data.invert_set = readl(&pint->regs->invert_set);
- }
-
- return 0;
-}
-
-static void adi_pint_resume(void)
-{
- struct gpio_pint *pint;
-
- list_for_each_entry(pint, &adi_pint_list, node) {
- writel(pint->saved_data.assign, &pint->regs->assign);
- writel(pint->saved_data.edge_set, &pint->regs->edge_set);
- writel(pint->saved_data.invert_set, &pint->regs->invert_set);
- }
-}
-
-static int adi_gpio_suspend(void)
-{
- struct gpio_port *port;
-
- list_for_each_entry(port, &adi_gpio_port_list, node) {
- port->saved_data.fer = readw(&port->regs->port_fer);
- port->saved_data.mux = readl(&port->regs->port_mux);
- port->saved_data.data = readw(&port->regs->data);
- port->saved_data.inen = readw(&port->regs->inen);
- port->saved_data.dir = readw(&port->regs->dir_set);
- }
-
- return adi_pint_suspend();
-}
-
-static void adi_gpio_resume(void)
-{
- struct gpio_port *port;
-
- adi_pint_resume();
-
- list_for_each_entry(port, &adi_gpio_port_list, node) {
- writel(port->saved_data.mux, &port->regs->port_mux);
- writew(port->saved_data.fer, &port->regs->port_fer);
- writew(port->saved_data.inen, &port->regs->inen);
- writew(port->saved_data.data & port->saved_data.dir,
- &port->regs->data_set);
- writew(port->saved_data.dir, &port->regs->dir_set);
- }
-
-}
-
-static struct syscore_ops gpio_pm_syscore_ops = {
- .suspend = adi_gpio_suspend,
- .resume = adi_gpio_resume,
-};
-#else /* CONFIG_PM */
-#define adi_gpio_set_wake NULL
-#endif /* CONFIG_PM */
-
-#ifdef CONFIG_IRQ_PREFLOW_FASTEOI
-static inline void preflow_handler(struct irq_desc *desc)
-{
- if (desc->preflow_handler)
- desc->preflow_handler(&desc->irq_data);
-}
-#else
-static inline void preflow_handler(struct irq_desc *desc) { }
-#endif
-
-static void adi_gpio_handle_pint_irq(struct irq_desc *desc)
-{
- u32 request;
- u32 level_mask, hwirq;
- bool umask = false;
- struct gpio_pint *pint = irq_desc_get_handler_data(desc);
- struct irq_chip *chip = irq_desc_get_chip(desc);
- struct gpio_pint_regs *regs = pint->regs;
- struct irq_domain *domain;
-
- preflow_handler(desc);
- chained_irq_enter(chip, desc);
-
- request = readl(&regs->request);
- level_mask = readl(&regs->edge_set) & request;
-
- hwirq = 0;
- domain = pint->domain[0];
- while (request) {
- /* domain pointer need to be changed only once at IRQ 16 when
- * we go through IRQ requests from bit 0 to bit 31.
- */
- if (hwirq == PINT_HI_OFFSET)
- domain = pint->domain[1];
-
- if (request & 1) {
- if (level_mask & BIT(hwirq)) {
- umask = true;
- chained_irq_exit(chip, desc);
- }
- generic_handle_irq(irq_find_mapping(domain,
- hwirq % PINT_HI_OFFSET));
- }
-
- hwirq++;
- request >>= 1;
- }
-
- if (!umask)
- chained_irq_exit(chip, desc);
-}
-
-static struct irq_chip adi_gpio_irqchip = {
- .name = "GPIO",
- .irq_ack = adi_gpio_ack_irq,
- .irq_mask = adi_gpio_mask_irq,
- .irq_mask_ack = adi_gpio_mask_ack_irq,
- .irq_unmask = adi_gpio_unmask_irq,
- .irq_disable = adi_gpio_mask_irq,
- .irq_enable = adi_gpio_unmask_irq,
- .irq_set_type = adi_gpio_irq_type,
- .irq_startup = adi_gpio_irq_startup,
- .irq_shutdown = adi_gpio_irq_shutdown,
- .irq_set_wake = adi_gpio_set_wake,
-};
-
-static int adi_get_groups_count(struct pinctrl_dev *pctldev)
-{
- struct adi_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctldev);
-
- return pinctrl->soc->ngroups;
-}
-
-static const char *adi_get_group_name(struct pinctrl_dev *pctldev,
- unsigned selector)
-{
- struct adi_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctldev);
-
- return pinctrl->soc->groups[selector].name;
-}
-
-static int adi_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector,
- const unsigned **pins,
- unsigned *num_pins)
-{
- struct adi_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctldev);
-
- *pins = pinctrl->soc->groups[selector].pins;
- *num_pins = pinctrl->soc->groups[selector].num;
- return 0;
-}
-
-static const struct pinctrl_ops adi_pctrl_ops = {
- .get_groups_count = adi_get_groups_count,
- .get_group_name = adi_get_group_name,
- .get_group_pins = adi_get_group_pins,
-};
-
-static int adi_pinmux_set(struct pinctrl_dev *pctldev, unsigned func_id,
- unsigned group_id)
-{
- struct adi_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctldev);
- struct gpio_port *port;
- struct pinctrl_gpio_range *range;
- unsigned long flags;
- unsigned short *mux, pin;
-
- mux = (unsigned short *)pinctrl->soc->groups[group_id].mux;
-
- while (*mux) {
- pin = P_IDENT(*mux);
-
- range = pinctrl_find_gpio_range_from_pin(pctldev, pin);
- if (range == NULL) /* should not happen */
- return -ENODEV;
-
- port = gpiochip_get_data(range->gc);
-
- spin_lock_irqsave(&port->lock, flags);
-
- portmux_setup(port, pin_to_offset(range, pin),
- P_FUNCT2MUX(*mux));
- port_setup(port, pin_to_offset(range, pin), false);
- mux++;
-
- spin_unlock_irqrestore(&port->lock, flags);
- }
-
- return 0;
-}
-
-static int adi_pinmux_get_funcs_count(struct pinctrl_dev *pctldev)
-{
- struct adi_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctldev);
-
- return pinctrl->soc->nfunctions;
-}
-
-static const char *adi_pinmux_get_func_name(struct pinctrl_dev *pctldev,
- unsigned selector)
-{
- struct adi_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctldev);
-
- return pinctrl->soc->functions[selector].name;
-}
-
-static int adi_pinmux_get_groups(struct pinctrl_dev *pctldev, unsigned selector,
- const char * const **groups,
- unsigned * const num_groups)
-{
- struct adi_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctldev);
-
- *groups = pinctrl->soc->functions[selector].groups;
- *num_groups = pinctrl->soc->functions[selector].num_groups;
- return 0;
-}
-
-static int adi_pinmux_request_gpio(struct pinctrl_dev *pctldev,
- struct pinctrl_gpio_range *range, unsigned pin)
-{
- struct gpio_port *port;
- unsigned long flags;
- u8 offset;
-
- port = gpiochip_get_data(range->gc);
- offset = pin_to_offset(range, pin);
-
- spin_lock_irqsave(&port->lock, flags);
-
- port_setup(port, offset, true);
-
- spin_unlock_irqrestore(&port->lock, flags);
-
- return 0;
-}
-
-static const struct pinmux_ops adi_pinmux_ops = {
- .set_mux = adi_pinmux_set,
- .get_functions_count = adi_pinmux_get_funcs_count,
- .get_function_name = adi_pinmux_get_func_name,
- .get_function_groups = adi_pinmux_get_groups,
- .gpio_request_enable = adi_pinmux_request_gpio,
- .strict = true,
-};
-
-
-static struct pinctrl_desc adi_pinmux_desc = {
- .name = DRIVER_NAME,
- .pctlops = &adi_pctrl_ops,
- .pmxops = &adi_pinmux_ops,
- .owner = THIS_MODULE,
-};
-
-static int adi_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
-{
- struct gpio_port *port;
- unsigned long flags;
-
- port = gpiochip_get_data(chip);
-
- spin_lock_irqsave(&port->lock, flags);
-
- writew(BIT(offset), &port->regs->dir_clear);
- writew(readw(&port->regs->inen) | BIT(offset), &port->regs->inen);
-
- spin_unlock_irqrestore(&port->lock, flags);
-
- return 0;
-}
-
-static void adi_gpio_set_value(struct gpio_chip *chip, unsigned offset,
- int value)
-{
- struct gpio_port *port = gpiochip_get_data(chip);
- struct gpio_port_t *regs = port->regs;
- unsigned long flags;
-
- spin_lock_irqsave(&port->lock, flags);
-
- if (value)
- writew(BIT(offset), &regs->data_set);
- else
- writew(BIT(offset), &regs->data_clear);
-
- spin_unlock_irqrestore(&port->lock, flags);
-}
-
-static int adi_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
- int value)
-{
- struct gpio_port *port = gpiochip_get_data(chip);
- struct gpio_port_t *regs = port->regs;
- unsigned long flags;
-
- spin_lock_irqsave(&port->lock, flags);
-
- writew(readw(&regs->inen) & ~BIT(offset), &regs->inen);
- if (value)
- writew(BIT(offset), &regs->data_set);
- else
- writew(BIT(offset), &regs->data_clear);
- writew(BIT(offset), &regs->dir_set);
-
- spin_unlock_irqrestore(&port->lock, flags);
-
- return 0;
-}
-
-static int adi_gpio_get_value(struct gpio_chip *chip, unsigned offset)
-{
- struct gpio_port *port = gpiochip_get_data(chip);
- struct gpio_port_t *regs = port->regs;
- unsigned long flags;
- int ret;
-
- spin_lock_irqsave(&port->lock, flags);
-
- ret = !!(readw(&regs->data) & BIT(offset));
-
- spin_unlock_irqrestore(&port->lock, flags);
-
- return ret;
-}
-
-static int adi_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
-{
- struct gpio_port *port = gpiochip_get_data(chip);
-
- if (port->irq_base >= 0)
- return irq_find_mapping(port->domain, offset);
- else
- return irq_create_mapping(port->domain, offset);
-}
-
-static int adi_pint_map_port(struct gpio_pint *pint, bool assign, u8 map,
- struct irq_domain *domain)
-{
- struct gpio_pint_regs *regs = pint->regs;
- u32 map_mask;
-
- if (pint->map_count > 1)
- return -EINVAL;
-
- pint->map_count++;
-
- /* The map_mask of each gpio port is a 16-bit duplicate
- * of the 8-bit map. It can be set to either high 16 bits or low
- * 16 bits of the pint assignment register.
- */
- map_mask = (map << 8) | map;
- if (assign) {
- map_mask <<= PINT_HI_OFFSET;
- writel((readl(&regs->assign) & 0xFFFF) | map_mask,
- &regs->assign);
- } else
- writel((readl(&regs->assign) & 0xFFFF0000) | map_mask,
- &regs->assign);
-
- pint->domain[assign] = domain;
-
- return 0;
-}
-
-static int adi_gpio_pint_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct resource *res;
- struct gpio_pint *pint = devm_kzalloc(dev, sizeof(*pint), GFP_KERNEL);
-
- if (!pint)
- return -ENOMEM;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- pint->base = devm_ioremap_resource(dev, res);
- if (IS_ERR(pint->base))
- return PTR_ERR(pint->base);
-
- pint->regs = (struct gpio_pint_regs *)pint->base;
-
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res) {
- dev_err(dev, "Invalid IRQ resource\n");
- return -ENODEV;
- }
-
- spin_lock_init(&pint->lock);
-
- pint->irq = res->start;
- pint->pint_map_port = adi_pint_map_port;
- platform_set_drvdata(pdev, pint);
-
- irq_set_chained_handler_and_data(pint->irq, adi_gpio_handle_pint_irq,
- pint);
-
- list_add_tail(&pint->node, &adi_pint_list);
-
- return 0;
-}
-
-static int adi_gpio_pint_remove(struct platform_device *pdev)
-{
- struct gpio_pint *pint = platform_get_drvdata(pdev);
-
- list_del(&pint->node);
- irq_set_handler(pint->irq, handle_simple_irq);
-
- return 0;
-}
-
-static int adi_gpio_irq_map(struct irq_domain *d, unsigned int irq,
- irq_hw_number_t hwirq)
-{
- struct gpio_port *port = d->host_data;
-
- if (!port)
- return -EINVAL;
-
- irq_set_chip_data(irq, port);
- irq_set_chip_and_handler(irq, &adi_gpio_irqchip,
- handle_level_irq);
-
- return 0;
-}
-
-static const struct irq_domain_ops adi_gpio_irq_domain_ops = {
- .map = adi_gpio_irq_map,
- .xlate = irq_domain_xlate_onecell,
-};
-
-static int adi_gpio_init_int(struct gpio_port *port)
-{
- struct device_node *node = port->dev->of_node;
- struct gpio_pint *pint = port->pint;
- int ret;
-
- port->domain = irq_domain_add_linear(node, port->width,
- &adi_gpio_irq_domain_ops, port);
- if (!port->domain) {
- dev_err(port->dev, "Failed to create irqdomain\n");
- return -ENOSYS;
- }
-
- /* According to BF54x and BF60x HRM, pin interrupt devices are not
- * part of the GPIO port device. in GPIO interrupt mode, the GPIO
- * pins of multiple port devices can be routed into one pin interrupt
- * device. The mapping can be configured by setting pint assignment
- * register with the mapping value of different GPIO port. This is
- * done via function pint_map_port().
- */
- ret = pint->pint_map_port(port->pint, port->pint_assign,
- port->pint_map, port->domain);
- if (ret)
- return ret;
-
- if (port->irq_base >= 0) {
- ret = irq_create_strict_mappings(port->domain, port->irq_base,
- 0, port->width);
- if (ret) {
- dev_err(port->dev, "Couldn't associate to domain\n");
- return ret;
- }
- }
-
- return 0;
-}
-
-#define DEVNAME_SIZE 16
-
-static int adi_gpio_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- const struct adi_pinctrl_gpio_platform_data *pdata;
- struct resource *res;
- struct gpio_port *port;
- char pinctrl_devname[DEVNAME_SIZE];
- static int gpio;
- int ret = 0;
-
- pdata = dev->platform_data;
- if (!pdata)
- return -EINVAL;
-
- port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
- if (!port)
- return -ENOMEM;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- port->base = devm_ioremap_resource(dev, res);
- if (IS_ERR(port->base))
- return PTR_ERR(port->base);
-
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res)
- port->irq_base = -1;
- else
- port->irq_base = res->start;
-
- port->width = pdata->port_width;
- port->dev = dev;
- port->regs = (struct gpio_port_t *)port->base;
- port->pint_assign = pdata->pint_assign;
- port->pint_map = pdata->pint_map;
-
- port->pint = find_gpio_pint(pdata->pint_id);
- if (port->pint) {
- ret = adi_gpio_init_int(port);
- if (ret)
- return ret;
- }
-
- spin_lock_init(&port->lock);
-
- platform_set_drvdata(pdev, port);
-
- port->chip.label = "adi-gpio";
- port->chip.direction_input = adi_gpio_direction_input;
- port->chip.get = adi_gpio_get_value;
- port->chip.direction_output = adi_gpio_direction_output;
- port->chip.set = adi_gpio_set_value;
- port->chip.request = gpiochip_generic_request,
- port->chip.free = gpiochip_generic_free,
- port->chip.to_irq = adi_gpio_to_irq;
- if (pdata->port_gpio_base > 0)
- port->chip.base = pdata->port_gpio_base;
- else
- port->chip.base = gpio;
- port->chip.ngpio = port->width;
- gpio = port->chip.base + port->width;
-
- ret = gpiochip_add_data(&port->chip, port);
- if (ret) {
- dev_err(&pdev->dev, "Fail to add GPIO chip.\n");
- goto out_remove_domain;
- }
-
- /* Add gpio pin range */
- snprintf(pinctrl_devname, DEVNAME_SIZE, "pinctrl-adi2.%d",
- pdata->pinctrl_id);
- pinctrl_devname[DEVNAME_SIZE - 1] = 0;
- ret = gpiochip_add_pin_range(&port->chip, pinctrl_devname,
- 0, pdata->port_pin_base, port->width);
- if (ret) {
- dev_err(&pdev->dev, "Fail to add pin range to %s.\n",
- pinctrl_devname);
- goto out_remove_gpiochip;
- }
-
- list_add_tail(&port->node, &adi_gpio_port_list);
-
- return 0;
-
-out_remove_gpiochip:
- gpiochip_remove(&port->chip);
-out_remove_domain:
- if (port->pint)
- irq_domain_remove(port->domain);
-
- return ret;
-}
-
-static int adi_gpio_remove(struct platform_device *pdev)
-{
- struct gpio_port *port = platform_get_drvdata(pdev);
- u8 offset;
-
- list_del(&port->node);
- gpiochip_remove(&port->chip);
- if (port->pint) {
- for (offset = 0; offset < port->width; offset++)
- irq_dispose_mapping(irq_find_mapping(port->domain,
- offset));
- irq_domain_remove(port->domain);
- }
-
- return 0;
-}
-
-static int adi_pinctrl_probe(struct platform_device *pdev)
-{
- struct adi_pinctrl *pinctrl;
-
- pinctrl = devm_kzalloc(&pdev->dev, sizeof(*pinctrl), GFP_KERNEL);
- if (!pinctrl)
- return -ENOMEM;
-
- pinctrl->dev = &pdev->dev;
-
- adi_pinctrl_soc_init(&pinctrl->soc);
-
- adi_pinmux_desc.pins = pinctrl->soc->pins;
- adi_pinmux_desc.npins = pinctrl->soc->npins;
-
- /* Now register the pin controller and all pins it handles */
- pinctrl->pctl = devm_pinctrl_register(&pdev->dev, &adi_pinmux_desc,
- pinctrl);
- if (IS_ERR(pinctrl->pctl)) {
- dev_err(&pdev->dev, "could not register pinctrl ADI2 driver\n");
- return PTR_ERR(pinctrl->pctl);
- }
-
- platform_set_drvdata(pdev, pinctrl);
-
- return 0;
-}
-
-static struct platform_driver adi_pinctrl_driver = {
- .probe = adi_pinctrl_probe,
- .driver = {
- .name = DRIVER_NAME,
- },
-};
-
-static struct platform_driver adi_gpio_pint_driver = {
- .probe = adi_gpio_pint_probe,
- .remove = adi_gpio_pint_remove,
- .driver = {
- .name = "adi-gpio-pint",
- },
-};
-
-static struct platform_driver adi_gpio_driver = {
- .probe = adi_gpio_probe,
- .remove = adi_gpio_remove,
- .driver = {
- .name = "adi-gpio",
- },
-};
-
-static struct platform_driver * const drivers[] = {
- &adi_pinctrl_driver,
- &adi_gpio_pint_driver,
- &adi_gpio_driver,
-};
-
-static int __init adi_pinctrl_setup(void)
-{
- int ret;
-
- ret = platform_register_drivers(drivers, ARRAY_SIZE(drivers));
- if (ret)
- return ret;
-
-#ifdef CONFIG_PM
- register_syscore_ops(&gpio_pm_syscore_ops);
-#endif
- return 0;
-}
-arch_initcall(adi_pinctrl_setup);
-
-MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
-MODULE_DESCRIPTION("ADI gpio2 pin control driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/pinctrl/pinctrl-adi2.h b/drivers/pinctrl/pinctrl-adi2.h
deleted file mode 100644
index 3ca29738213f..000000000000
--- a/drivers/pinctrl/pinctrl-adi2.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Pinctrl Driver for ADI GPIO2 controller
- *
- * Copyright 2007-2013 Analog Devices Inc.
- *
- * Licensed under the GPLv2 or later
- */
-
-#ifndef PINCTRL_PINCTRL_ADI2_H
-#define PINCTRL_PINCTRL_ADI2_H
-
-#include <linux/pinctrl/pinctrl.h>
-
- /**
- * struct adi_pin_group - describes a pin group
- * @name: the name of this pin group
- * @pins: an array of pins
- * @num: the number of pins in this array
- */
-struct adi_pin_group {
- const char *name;
- const unsigned *pins;
- const unsigned num;
- const unsigned short *mux;
-};
-
-#define ADI_PIN_GROUP(n, p, m) \
- { \
- .name = n, \
- .pins = p, \
- .num = ARRAY_SIZE(p), \
- .mux = m, \
- }
-
- /**
- * struct adi_pmx_func - describes function mux setting of pin groups
- * @name: the name of this function mux setting
- * @groups: an array of pin groups
- * @num_groups: the number of pin groups in this array
- * @mux: the function mux setting array, end by zero
- */
-struct adi_pmx_func {
- const char *name;
- const char * const *groups;
- const unsigned num_groups;
-};
-
-#define ADI_PMX_FUNCTION(n, g) \
- { \
- .name = n, \
- .groups = g, \
- .num_groups = ARRAY_SIZE(g), \
- }
-
-/**
- * struct adi_pinctrl_soc_data - ADI pin controller per-SoC configuration
- * @functions: The functions supported on this SoC.
- * @nfunction: The number of entries in @functions.
- * @groups: An array describing all pin groups the pin SoC supports.
- * @ngroups: The number of entries in @groups.
- * @pins: An array describing all pins the pin controller affects.
- * @npins: The number of entries in @pins.
- */
-struct adi_pinctrl_soc_data {
- const struct adi_pmx_func *functions;
- int nfunctions;
- const struct adi_pin_group *groups;
- int ngroups;
- const struct pinctrl_pin_desc *pins;
- int npins;
-};
-
-void adi_pinctrl_soc_init(const struct adi_pinctrl_soc_data **soc);
-
-#endif /* PINCTRL_PINCTRL_ADI2_H */
diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c
index 61d830c2bc17..04ae139671c8 100644
--- a/drivers/pinctrl/pinctrl-amd.c
+++ b/drivers/pinctrl/pinctrl-amd.c
@@ -40,6 +40,19 @@
#include "pinctrl-utils.h"
#include "pinctrl-amd.h"
+static int amd_gpio_get_direction(struct gpio_chip *gc, unsigned offset)
+{
+ unsigned long flags;
+ u32 pin_reg;
+ struct amd_gpio *gpio_dev = gpiochip_get_data(gc);
+
+ raw_spin_lock_irqsave(&gpio_dev->lock, flags);
+ pin_reg = readl(gpio_dev->base + offset * 4);
+ raw_spin_unlock_irqrestore(&gpio_dev->lock, flags);
+
+ return !(pin_reg & BIT(OUTPUT_ENABLE_OFF));
+}
+
static int amd_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
{
unsigned long flags;
@@ -335,12 +348,21 @@ static void amd_gpio_irq_enable(struct irq_data *d)
unsigned long flags;
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct amd_gpio *gpio_dev = gpiochip_get_data(gc);
+ u32 mask = BIT(INTERRUPT_ENABLE_OFF) | BIT(INTERRUPT_MASK_OFF);
raw_spin_lock_irqsave(&gpio_dev->lock, flags);
pin_reg = readl(gpio_dev->base + (d->hwirq)*4);
pin_reg |= BIT(INTERRUPT_ENABLE_OFF);
pin_reg |= BIT(INTERRUPT_MASK_OFF);
writel(pin_reg, gpio_dev->base + (d->hwirq)*4);
+ /*
+ * When debounce logic is enabled it takes ~900 us before interrupts
+ * can be enabled. During this "debounce warm up" period the
+ * "INTERRUPT_ENABLE" bit will read as 0. Poll the bit here until it
+ * reads back as 1, signaling that interrupts are now enabled.
+ */
+ while ((readl(gpio_dev->base + (d->hwirq)*4) & mask) != mask)
+ continue;
raw_spin_unlock_irqrestore(&gpio_dev->lock, flags);
}
@@ -845,6 +867,7 @@ static int amd_gpio_probe(struct platform_device *pdev)
#endif
gpio_dev->pdev = pdev;
+ gpio_dev->gc.get_direction = amd_gpio_get_direction;
gpio_dev->gc.direction_input = amd_gpio_direction_input;
gpio_dev->gc.direction_output = amd_gpio_direction_output;
gpio_dev->gc.get = amd_gpio_get_value;
diff --git a/drivers/pinctrl/pinctrl-artpec6.c b/drivers/pinctrl/pinctrl-artpec6.c
index e33781cd0a05..d89dc43c5757 100644
--- a/drivers/pinctrl/pinctrl-artpec6.c
+++ b/drivers/pinctrl/pinctrl-artpec6.c
@@ -277,37 +277,61 @@ static const struct artpec6_pin_group artpec6_pin_groups[] = {
.config = ARTPEC6_CONFIG_3,
},
{
- .name = "uart0grp0",
+ .name = "uart0grp0", /* All pins. */
.pins = uart0_pins0,
.num_pins = ARRAY_SIZE(uart0_pins0),
.config = ARTPEC6_CONFIG_1,
},
{
- .name = "uart0grp1",
+ .name = "uart0grp1", /* RX/TX and RTS/CTS */
.pins = uart0_pins1,
.num_pins = ARRAY_SIZE(uart0_pins1),
.config = ARTPEC6_CONFIG_1,
},
{
- .name = "uart1grp0",
+ .name = "uart0grp2", /* Only RX/TX pins. */
+ .pins = uart0_pins1,
+ .num_pins = ARRAY_SIZE(uart0_pins1) - 2,
+ .config = ARTPEC6_CONFIG_1,
+ },
+ {
+ .name = "uart1grp0", /* RX/TX and RTS/CTS */
.pins = uart1_pins0,
.num_pins = ARRAY_SIZE(uart1_pins0),
.config = ARTPEC6_CONFIG_2,
},
{
- .name = "uart2grp0",
+ .name = "uart1grp1", /* Only RX/TX pins. */
+ .pins = uart1_pins0,
+ .num_pins = 2,
+ .config = ARTPEC6_CONFIG_2,
+ },
+ {
+ .name = "uart2grp0", /* Full pinout */
.pins = uart2_pins0,
.num_pins = ARRAY_SIZE(uart2_pins0),
.config = ARTPEC6_CONFIG_1,
},
{
- .name = "uart2grp1",
+ .name = "uart2grp1", /* RX/TX and RTS/CTS */
.pins = uart2_pins1,
.num_pins = ARRAY_SIZE(uart2_pins1),
.config = ARTPEC6_CONFIG_1,
},
{
- .name = "uart3grp0",
+ .name = "uart2grp2", /* Only RX/TX */
+ .pins = uart2_pins1,
+ .num_pins = 2,
+ .config = ARTPEC6_CONFIG_1,
+ },
+ {
+ .name = "uart3grp0", /* RX/TX and CTS/RTS */
+ .pins = uart3_pins0,
+ .num_pins = ARRAY_SIZE(uart3_pins0),
+ .config = ARTPEC6_CONFIG_0,
+ },
+ {
+ .name = "uart3grp1", /* Only RX/TX */
.pins = uart3_pins0,
.num_pins = ARRAY_SIZE(uart3_pins0),
.config = ARTPEC6_CONFIG_0,
@@ -319,13 +343,19 @@ static const struct artpec6_pin_group artpec6_pin_groups[] = {
.config = ARTPEC6_CONFIG_2,
},
{
- .name = "uart5grp0",
+ .name = "uart5grp0", /* TX/RX and RTS/CTS */
.pins = uart5_pins0,
.num_pins = ARRAY_SIZE(uart5_pins0),
.config = ARTPEC6_CONFIG_2,
},
{
- .name = "uart5nocts",
+ .name = "uart5grp1", /* Only TX/RX */
+ .pins = uart5_pins0,
+ .num_pins = 2,
+ .config = ARTPEC6_CONFIG_2,
+ },
+ {
+ .name = "uart5nocts", /* TX/RX/RTS */
.pins = uart5_pins0,
.num_pins = ARRAY_SIZE(uart5_pins0) - 1,
.config = ARTPEC6_CONFIG_2,
@@ -457,8 +487,9 @@ static const char * const gpiogrps[] = {
"cpuclkoutgrp0", "udlclkoutgrp0", "i2c1grp0", "i2c2grp0",
"i2c3grp0", "i2s0grp0", "i2s1grp0", "i2srefclkgrp0",
"spi0grp0", "spi1grp0", "pciedebuggrp0", "uart0grp0",
- "uart0grp1", "uart1grp0", "uart2grp0", "uart2grp1",
- "uart4grp0", "uart5grp0",
+ "uart0grp1", "uart0grp2", "uart1grp0", "uart1grp1",
+ "uart2grp0", "uart2grp1", "uart2grp2", "uart4grp0", "uart5grp0",
+ "uart5grp1", "uart5nocts",
};
static const char * const cpuclkoutgrps[] = { "cpuclkoutgrp0" };
static const char * const udlclkoutgrps[] = { "udlclkoutgrp0" };
@@ -471,12 +502,15 @@ static const char * const i2srefclkgrps[] = { "i2srefclkgrp0" };
static const char * const spi0grps[] = { "spi0grp0" };
static const char * const spi1grps[] = { "spi1grp0" };
static const char * const pciedebuggrps[] = { "pciedebuggrp0" };
-static const char * const uart0grps[] = { "uart0grp0", "uart0grp1" };
-static const char * const uart1grps[] = { "uart1grp0" };
-static const char * const uart2grps[] = { "uart2grp0", "uart2grp1" };
+static const char * const uart0grps[] = { "uart0grp0", "uart0grp1",
+ "uart0grp2" };
+static const char * const uart1grps[] = { "uart1grp0", "uart1grp1" };
+static const char * const uart2grps[] = { "uart2grp0", "uart2grp1",
+ "uart2grp2" };
static const char * const uart3grps[] = { "uart3grp0" };
-static const char * const uart4grps[] = { "uart4grp0" };
-static const char * const uart5grps[] = { "uart5grp0", "uart5nocts" };
+static const char * const uart4grps[] = { "uart4grp0", "uart4grp1" };
+static const char * const uart5grps[] = { "uart5grp0", "uart5grp1",
+ "uart5nocts" };
static const char * const nandgrps[] = { "nandgrp0" };
static const char * const sdio0grps[] = { "sdio0grp0" };
static const char * const sdio1grps[] = { "sdio1grp0" };
@@ -601,7 +635,7 @@ static int artpec6_pmx_get_functions_count(struct pinctrl_dev *pctldev)
}
static const char *artpec6_pmx_get_fname(struct pinctrl_dev *pctldev,
- unsigned int function)
+ unsigned int function)
{
return artpec6_pmx_functions[function].name;
}
diff --git a/drivers/pinctrl/pinctrl-mcp23s08.c b/drivers/pinctrl/pinctrl-mcp23s08.c
index 644c5beb05cb..022307dd4b54 100644
--- a/drivers/pinctrl/pinctrl-mcp23s08.c
+++ b/drivers/pinctrl/pinctrl-mcp23s08.c
@@ -771,6 +771,9 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
{
int status, ret;
bool mirror = false;
+ bool open_drain = false;
+ struct regmap_config *one_regmap_config = NULL;
+ int raw_chip_address = (addr & ~0x40) >> 1;
mutex_init(&mcp->lock);
@@ -791,19 +794,33 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
switch (type) {
#ifdef CONFIG_SPI_MASTER
case MCP_TYPE_S08:
- mcp->regmap = devm_regmap_init(dev, &mcp23sxx_spi_regmap, mcp,
- &mcp23x08_regmap);
- mcp->reg_shift = 0;
- mcp->chip.ngpio = 8;
- mcp->chip.label = "mcp23s08";
- break;
-
case MCP_TYPE_S17:
+ switch (type) {
+ case MCP_TYPE_S08:
+ one_regmap_config =
+ devm_kmemdup(dev, &mcp23x08_regmap,
+ sizeof(struct regmap_config), GFP_KERNEL);
+ mcp->reg_shift = 0;
+ mcp->chip.ngpio = 8;
+ mcp->chip.label = devm_kasprintf(dev, GFP_KERNEL,
+ "mcp23s08.%d", raw_chip_address);
+ break;
+ case MCP_TYPE_S17:
+ one_regmap_config =
+ devm_kmemdup(dev, &mcp23x17_regmap,
+ sizeof(struct regmap_config), GFP_KERNEL);
+ mcp->reg_shift = 1;
+ mcp->chip.ngpio = 16;
+ mcp->chip.label = devm_kasprintf(dev, GFP_KERNEL,
+ "mcp23s17.%d", raw_chip_address);
+ break;
+ }
+ if (!one_regmap_config)
+ return -ENOMEM;
+
+ one_regmap_config->name = devm_kasprintf(dev, GFP_KERNEL, "%d", raw_chip_address);
mcp->regmap = devm_regmap_init(dev, &mcp23sxx_spi_regmap, mcp,
- &mcp23x17_regmap);
- mcp->reg_shift = 1;
- mcp->chip.ngpio = 16;
- mcp->chip.label = "mcp23s17";
+ one_regmap_config);
break;
case MCP_TYPE_S18:
@@ -867,10 +884,11 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
"microchip,irq-active-high");
mirror = device_property_read_bool(dev, "microchip,irq-mirror");
+ open_drain = device_property_read_bool(dev, "drive-open-drain");
}
if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN) || mirror ||
- mcp->irq_active_high) {
+ mcp->irq_active_high || open_drain) {
/* mcp23s17 has IOCON twice, make sure they are in sync */
status &= ~(IOCON_SEQOP | (IOCON_SEQOP << 8));
status |= IOCON_HAEN | (IOCON_HAEN << 8);
@@ -882,6 +900,9 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
if (mirror)
status |= IOCON_MIRROR | (IOCON_MIRROR << 8);
+ if (open_drain)
+ status |= IOCON_ODR | (IOCON_ODR << 8);
+
if (type == MCP_TYPE_S18 || type == MCP_TYPE_018)
status |= IOCON_INTCC | (IOCON_INTCC << 8);
@@ -900,7 +921,14 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
if (ret < 0)
goto fail;
- mcp->pinctrl_desc.name = "mcp23xxx-pinctrl";
+ if (one_regmap_config) {
+ mcp->pinctrl_desc.name = devm_kasprintf(dev, GFP_KERNEL,
+ "mcp23xxx-pinctrl.%d", raw_chip_address);
+ if (!mcp->pinctrl_desc.name)
+ return -ENOMEM;
+ } else {
+ mcp->pinctrl_desc.name = "mcp23xxx-pinctrl";
+ }
mcp->pinctrl_desc.pctlops = &mcp_pinctrl_ops;
mcp->pinctrl_desc.confops = &mcp_pinconf_ops;
mcp->pinctrl_desc.npins = mcp->chip.ngpio;
diff --git a/drivers/pinctrl/pinctrl-ocelot.c b/drivers/pinctrl/pinctrl-ocelot.c
index 01a50d969111..b5b3547fdcb2 100644
--- a/drivers/pinctrl/pinctrl-ocelot.c
+++ b/drivers/pinctrl/pinctrl-ocelot.c
@@ -252,7 +252,7 @@ static int ocelot_gpio_set_direction(struct pinctrl_dev *pctldev,
struct ocelot_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
regmap_update_bits(info->map, OCELOT_GPIO_OE, BIT(pin),
- input ? BIT(pin) : 0);
+ input ? 0 : BIT(pin));
return 0;
}
@@ -462,7 +462,7 @@ static const struct of_device_id ocelot_pinctrl_of_match[] = {
{},
};
-int ocelot_pinctrl_probe(struct platform_device *pdev)
+static int ocelot_pinctrl_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct ocelot_pinctrl *info;
diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c
index cec75379f936..a7c5eb39b1eb 100644
--- a/drivers/pinctrl/pinctrl-single.c
+++ b/drivers/pinctrl/pinctrl-single.c
@@ -391,9 +391,25 @@ static int pcs_request_gpio(struct pinctrl_dev *pctldev,
|| pin < frange->offset)
continue;
mux_bytes = pcs->width / BITS_PER_BYTE;
- data = pcs->read(pcs->base + pin * mux_bytes) & ~pcs->fmask;
- data |= frange->gpiofunc;
- pcs->write(data, pcs->base + pin * mux_bytes);
+
+ if (pcs->bits_per_mux) {
+ int byte_num, offset, pin_shift;
+
+ byte_num = (pcs->bits_per_pin * pin) / BITS_PER_BYTE;
+ offset = (byte_num / mux_bytes) * mux_bytes;
+ pin_shift = pin % (pcs->width / pcs->bits_per_pin) *
+ pcs->bits_per_pin;
+
+ data = pcs->read(pcs->base + offset);
+ data &= ~(pcs->fmask << pin_shift);
+ data |= frange->gpiofunc << pin_shift;
+ pcs->write(data, pcs->base + offset);
+ } else {
+ data = pcs->read(pcs->base + pin * mux_bytes);
+ data &= ~pcs->fmask;
+ data |= frange->gpiofunc;
+ pcs->write(data, pcs->base + pin * mux_bytes);
+ }
break;
}
return 0;
diff --git a/drivers/pinctrl/pinctrl-tz1090-pdc.c b/drivers/pinctrl/pinctrl-tz1090-pdc.c
deleted file mode 100644
index b16d1c96b7eb..000000000000
--- a/drivers/pinctrl/pinctrl-tz1090-pdc.c
+++ /dev/null
@@ -1,989 +0,0 @@
-/*
- * Pinctrl driver for the Toumaz Xenif TZ1090 PowerDown Controller pins
- *
- * Copyright (c) 2013, Imagination Technologies Ltd.
- *
- * Derived from Tegra code:
- * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved.
- *
- * Derived from code:
- * Copyright (C) 2010 Google, Inc.
- * Copyright (C) 2010 NVIDIA Corporation
- * Copyright (C) 2009-2011 ST-Ericsson AB
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#include <linux/bitops.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
-#include <linux/pinctrl/machine.h>
-#include <linux/pinctrl/pinconf-generic.h>
-#include <linux/pinctrl/pinctrl.h>
-#include <linux/pinctrl/pinmux.h>
-#include <linux/slab.h>
-
-/*
- * The registers may be shared with other threads/cores, so we need to use the
- * metag global lock2 for atomicity.
- */
-#include <asm/global_lock.h>
-
-#include "core.h"
-#include "pinconf.h"
-
-/* Register offsets from bank base address */
-#define REG_GPIO_CONTROL0 0x00
-#define REG_GPIO_CONTROL2 0x08
-
-/* Register field information */
-#define REG_GPIO_CONTROL2_PU_PD_S 16
-#define REG_GPIO_CONTROL2_PDC_POS_S 4
-#define REG_GPIO_CONTROL2_PDC_DR_S 2
-#define REG_GPIO_CONTROL2_PDC_SR_S 1
-#define REG_GPIO_CONTROL2_PDC_SCHMITT_S 0
-
-/* PU_PD field values */
-#define REG_PU_PD_TRISTATE 0
-#define REG_PU_PD_UP 1
-#define REG_PU_PD_DOWN 2
-#define REG_PU_PD_REPEATER 3
-
-/* DR field values */
-#define REG_DR_2mA 0
-#define REG_DR_4mA 1
-#define REG_DR_8mA 2
-#define REG_DR_12mA 3
-
-/**
- * struct tz1090_pdc_function - TZ1090 PDC pinctrl mux function
- * @name: The name of the function, exported to pinctrl core.
- * @groups: An array of pin groups that may select this function.
- * @ngroups: The number of entries in @groups.
- */
-struct tz1090_pdc_function {
- const char *name;
- const char * const *groups;
- unsigned int ngroups;
-};
-
-/**
- * struct tz1090_pdc_pingroup - TZ1090 PDC pin group
- * @name: Name of pin group.
- * @pins: Array of pin numbers in this pin group.
- * @npins: Number of pins in this pin group.
- * @func: Function enabled by the mux.
- * @reg: Mux register offset.
- * @bit: Mux register bit.
- * @drv: Drive control supported, otherwise it's a mux.
- * This means Schmitt, Slew, and Drive strength.
- *
- * A representation of a group of pins (possibly just one pin) in the TZ1090
- * PDC pin controller. Each group allows some parameter or parameters to be
- * configured. The most common is mux function selection.
- */
-struct tz1090_pdc_pingroup {
- const char *name;
- const unsigned int *pins;
- unsigned int npins;
- int func;
- u16 reg;
- u8 bit;
- bool drv;
-};
-
-/*
- * All PDC pins can be GPIOs. Define these first to match how the GPIO driver
- * names/numbers its pins.
- */
-
-enum tz1090_pdc_pin {
- TZ1090_PDC_PIN_GPIO0,
- TZ1090_PDC_PIN_GPIO1,
- TZ1090_PDC_PIN_SYS_WAKE0,
- TZ1090_PDC_PIN_SYS_WAKE1,
- TZ1090_PDC_PIN_SYS_WAKE2,
- TZ1090_PDC_PIN_IR_DATA,
- TZ1090_PDC_PIN_EXT_POWER,
-};
-
-/* Pin names */
-
-static const struct pinctrl_pin_desc tz1090_pdc_pins[] = {
- /* PDC GPIOs */
- PINCTRL_PIN(TZ1090_PDC_PIN_GPIO0, "gpio0"),
- PINCTRL_PIN(TZ1090_PDC_PIN_GPIO1, "gpio1"),
- PINCTRL_PIN(TZ1090_PDC_PIN_SYS_WAKE0, "sys_wake0"),
- PINCTRL_PIN(TZ1090_PDC_PIN_SYS_WAKE1, "sys_wake1"),
- PINCTRL_PIN(TZ1090_PDC_PIN_SYS_WAKE2, "sys_wake2"),
- PINCTRL_PIN(TZ1090_PDC_PIN_IR_DATA, "ir_data"),
- PINCTRL_PIN(TZ1090_PDC_PIN_EXT_POWER, "ext_power"),
-};
-
-/* Pin group pins */
-
-static const unsigned int gpio0_pins[] = {
- TZ1090_PDC_PIN_GPIO0,
-};
-
-static const unsigned int gpio1_pins[] = {
- TZ1090_PDC_PIN_GPIO1,
-};
-
-static const unsigned int pdc_pins[] = {
- TZ1090_PDC_PIN_GPIO0,
- TZ1090_PDC_PIN_GPIO1,
- TZ1090_PDC_PIN_SYS_WAKE0,
- TZ1090_PDC_PIN_SYS_WAKE1,
- TZ1090_PDC_PIN_SYS_WAKE2,
- TZ1090_PDC_PIN_IR_DATA,
- TZ1090_PDC_PIN_EXT_POWER,
-};
-
-/* Mux functions */
-
-enum tz1090_pdc_mux {
- /* PDC_GPIO0 mux */
- TZ1090_PDC_MUX_IR_MOD_STABLE_OUT,
- /* PDC_GPIO1 mux */
- TZ1090_PDC_MUX_IR_MOD_POWER_OUT,
-};
-
-/* Pin groups a function can be muxed to */
-
-static const char * const gpio0_groups[] = {
- "gpio0",
-};
-
-static const char * const gpio1_groups[] = {
- "gpio1",
-};
-
-#define FUNCTION(mux, fname, group) \
- [(TZ1090_PDC_MUX_ ## mux)] = { \
- .name = #fname, \
- .groups = group##_groups, \
- .ngroups = ARRAY_SIZE(group##_groups), \
- }
-
-/* Must correlate with enum tz1090_pdc_mux */
-static const struct tz1090_pdc_function tz1090_pdc_functions[] = {
- /* MUX fn pingroups */
- FUNCTION(IR_MOD_STABLE_OUT, ir_mod_stable_out, gpio0),
- FUNCTION(IR_MOD_POWER_OUT, ir_mod_power_out, gpio1),
-};
-
-/**
- * MUX_PG() - Initialise a pin group with mux control
- * @pg_name: Pin group name (stringified, _pins appended to get pins array)
- * @f0: Function 0 (TZ1090_PDC_MUX_ is prepended)
- * @mux_r: Mux register (REG_PINCTRL_ is prepended)
- * @mux_b: Bit number in register of mux field
- */
-#define MUX_PG(pg_name, f0, mux_r, mux_b) \
- { \
- .name = #pg_name, \
- .pins = pg_name##_pins, \
- .npins = ARRAY_SIZE(pg_name##_pins), \
- .func = TZ1090_PDC_MUX_ ## f0, \
- .reg = (REG_ ## mux_r), \
- .bit = (mux_b), \
- }
-
-/**
- * DRV_PG() - Initialise a pin group with drive control
- * @pg_name: Pin group name (stringified, _pins appended to get pins array)
- */
-#define DRV_PG(pg_name) \
- { \
- .name = #pg_name, \
- .pins = pg_name##_pins, \
- .npins = ARRAY_SIZE(pg_name##_pins), \
- .drv = true, \
- }
-
-static const struct tz1090_pdc_pingroup tz1090_pdc_groups[] = {
- /* Muxing pin groups */
- /* pg_name, f0, mux register, mux bit */
- MUX_PG(gpio0, IR_MOD_STABLE_OUT, GPIO_CONTROL0, 7),
- MUX_PG(gpio1, IR_MOD_POWER_OUT, GPIO_CONTROL0, 6),
-
- /* Drive pin groups */
- /* pg_name */
- DRV_PG(pdc),
-};
-
-/**
- * struct tz1090_pdc_pmx - Private pinctrl data
- * @dev: Platform device
- * @pctl: Pin control device
- * @regs: Register region
- * @lock: Lock protecting coherency of mux_en and gpio_en
- * @mux_en: Muxes that have been enabled
- * @gpio_en: Muxable GPIOs that have been enabled
- */
-struct tz1090_pdc_pmx {
- struct device *dev;
- struct pinctrl_dev *pctl;
- void __iomem *regs;
- spinlock_t lock;
- u32 mux_en;
- u32 gpio_en;
-};
-
-static inline u32 pmx_read(struct tz1090_pdc_pmx *pmx, u32 reg)
-{
- return ioread32(pmx->regs + reg);
-}
-
-static inline void pmx_write(struct tz1090_pdc_pmx *pmx, u32 val, u32 reg)
-{
- iowrite32(val, pmx->regs + reg);
-}
-
-/*
- * Pin control operations
- */
-
-static int tz1090_pdc_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
-{
- return ARRAY_SIZE(tz1090_pdc_groups);
-}
-
-static const char *tz1090_pdc_pinctrl_get_group_name(struct pinctrl_dev *pctl,
- unsigned int group)
-{
- return tz1090_pdc_groups[group].name;
-}
-
-static int tz1090_pdc_pinctrl_get_group_pins(struct pinctrl_dev *pctldev,
- unsigned int group,
- const unsigned int **pins,
- unsigned int *num_pins)
-{
- *pins = tz1090_pdc_groups[group].pins;
- *num_pins = tz1090_pdc_groups[group].npins;
-
- return 0;
-}
-
-#ifdef CONFIG_DEBUG_FS
-static void tz1090_pdc_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev,
- struct seq_file *s,
- unsigned int offset)
-{
- seq_printf(s, " %s", dev_name(pctldev->dev));
-}
-#endif
-
-static int reserve_map(struct device *dev, struct pinctrl_map **map,
- unsigned int *reserved_maps, unsigned int *num_maps,
- unsigned int reserve)
-{
- unsigned int old_num = *reserved_maps;
- unsigned int new_num = *num_maps + reserve;
- struct pinctrl_map *new_map;
-
- if (old_num >= new_num)
- return 0;
-
- new_map = krealloc(*map, sizeof(*new_map) * new_num, GFP_KERNEL);
- if (!new_map) {
- dev_err(dev, "krealloc(map) failed\n");
- return -ENOMEM;
- }
-
- memset(new_map + old_num, 0, (new_num - old_num) * sizeof(*new_map));
-
- *map = new_map;
- *reserved_maps = new_num;
-
- return 0;
-}
-
-static int add_map_mux(struct pinctrl_map **map, unsigned int *reserved_maps,
- unsigned int *num_maps, const char *group,
- const char *function)
-{
- if (WARN_ON(*num_maps == *reserved_maps))
- return -ENOSPC;
-
- (*map)[*num_maps].type = PIN_MAP_TYPE_MUX_GROUP;
- (*map)[*num_maps].data.mux.group = group;
- (*map)[*num_maps].data.mux.function = function;
- (*num_maps)++;
-
- return 0;
-}
-
-/**
- * get_group_selector() - returns the group selector for a group
- * @pin_group: the pin group to look up
- *
- * This is the same as pinctrl_get_group_selector except it doesn't produce an
- * error message if the group isn't found or debug messages.
- */
-static int get_group_selector(const char *pin_group)
-{
- unsigned int group;
-
- for (group = 0; group < ARRAY_SIZE(tz1090_pdc_groups); ++group)
- if (!strcmp(tz1090_pdc_groups[group].name, pin_group))
- return group;
-
- return -EINVAL;
-}
-
-static int add_map_configs(struct device *dev,
- struct pinctrl_map **map,
- unsigned int *reserved_maps, unsigned int *num_maps,
- const char *group, unsigned long *configs,
- unsigned int num_configs)
-{
- unsigned long *dup_configs;
- enum pinctrl_map_type type;
-
- if (WARN_ON(*num_maps == *reserved_maps))
- return -ENOSPC;
-
- dup_configs = kmemdup(configs, num_configs * sizeof(*dup_configs),
- GFP_KERNEL);
- if (!dup_configs)
- return -ENOMEM;
-
- /*
- * We support both pins and pin groups, but we need to figure out which
- * one we have.
- */
- if (get_group_selector(group) >= 0)
- type = PIN_MAP_TYPE_CONFIGS_GROUP;
- else
- type = PIN_MAP_TYPE_CONFIGS_PIN;
- (*map)[*num_maps].type = type;
- (*map)[*num_maps].data.configs.group_or_pin = group;
- (*map)[*num_maps].data.configs.configs = dup_configs;
- (*map)[*num_maps].data.configs.num_configs = num_configs;
- (*num_maps)++;
-
- return 0;
-}
-
-static void tz1090_pdc_pinctrl_dt_free_map(struct pinctrl_dev *pctldev,
- struct pinctrl_map *map,
- unsigned int num_maps)
-{
- int i;
-
- for (i = 0; i < num_maps; i++)
- if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP)
- kfree(map[i].data.configs.configs);
-
- kfree(map);
-}
-
-static int tz1090_pdc_pinctrl_dt_subnode_to_map(struct device *dev,
- struct device_node *np,
- struct pinctrl_map **map,
- unsigned int *reserved_maps,
- unsigned int *num_maps)
-{
- int ret;
- const char *function;
- unsigned long *configs = NULL;
- unsigned int num_configs = 0;
- unsigned int reserve;
- struct property *prop;
- const char *group;
-
- ret = of_property_read_string(np, "tz1090,function", &function);
- if (ret < 0) {
- /* EINVAL=missing, which is fine since it's optional */
- if (ret != -EINVAL)
- dev_err(dev,
- "could not parse property function\n");
- function = NULL;
- }
-
- ret = pinconf_generic_parse_dt_config(np, NULL, &configs, &num_configs);
- if (ret)
- return ret;
-
- reserve = 0;
- if (function != NULL)
- reserve++;
- if (num_configs)
- reserve++;
- ret = of_property_count_strings(np, "tz1090,pins");
- if (ret < 0) {
- dev_err(dev, "could not parse property pins\n");
- goto exit;
- }
- reserve *= ret;
-
- ret = reserve_map(dev, map, reserved_maps, num_maps, reserve);
- if (ret < 0)
- goto exit;
-
- of_property_for_each_string(np, "tz1090,pins", prop, group) {
- if (function) {
- ret = add_map_mux(map, reserved_maps, num_maps,
- group, function);
- if (ret < 0)
- goto exit;
- }
-
- if (num_configs) {
- ret = add_map_configs(dev, map, reserved_maps,
- num_maps, group, configs,
- num_configs);
- if (ret < 0)
- goto exit;
- }
- }
-
- ret = 0;
-
-exit:
- kfree(configs);
- return ret;
-}
-
-static int tz1090_pdc_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev,
- struct device_node *np_config,
- struct pinctrl_map **map,
- unsigned int *num_maps)
-{
- unsigned int reserved_maps;
- struct device_node *np;
- int ret;
-
- reserved_maps = 0;
- *map = NULL;
- *num_maps = 0;
-
- for_each_child_of_node(np_config, np) {
- ret = tz1090_pdc_pinctrl_dt_subnode_to_map(pctldev->dev, np,
- map, &reserved_maps,
- num_maps);
- if (ret < 0) {
- tz1090_pdc_pinctrl_dt_free_map(pctldev, *map,
- *num_maps);
- return ret;
- }
- }
-
- return 0;
-}
-
-static const struct pinctrl_ops tz1090_pdc_pinctrl_ops = {
- .get_groups_count = tz1090_pdc_pinctrl_get_groups_count,
- .get_group_name = tz1090_pdc_pinctrl_get_group_name,
- .get_group_pins = tz1090_pdc_pinctrl_get_group_pins,
-#ifdef CONFIG_DEBUG_FS
- .pin_dbg_show = tz1090_pdc_pinctrl_pin_dbg_show,
-#endif
- .dt_node_to_map = tz1090_pdc_pinctrl_dt_node_to_map,
- .dt_free_map = tz1090_pdc_pinctrl_dt_free_map,
-};
-
-/*
- * Pin mux operations
- */
-
-static int tz1090_pdc_pinctrl_get_funcs_count(struct pinctrl_dev *pctldev)
-{
- return ARRAY_SIZE(tz1090_pdc_functions);
-}
-
-static const char *tz1090_pdc_pinctrl_get_func_name(struct pinctrl_dev *pctldev,
- unsigned int function)
-{
- return tz1090_pdc_functions[function].name;
-}
-
-static int tz1090_pdc_pinctrl_get_func_groups(struct pinctrl_dev *pctldev,
- unsigned int function,
- const char * const **groups,
- unsigned int * const num_groups)
-{
- *groups = tz1090_pdc_functions[function].groups;
- *num_groups = tz1090_pdc_functions[function].ngroups;
-
- return 0;
-}
-
-/**
- * tz1090_pdc_pinctrl_mux() - update mux bit
- * @pmx: Pinmux data
- * @grp: Pin mux group
- */
-static void tz1090_pdc_pinctrl_mux(struct tz1090_pdc_pmx *pmx,
- const struct tz1090_pdc_pingroup *grp)
-{
- u32 reg, select;
- unsigned int pin_shift = grp->pins[0];
- unsigned long flags;
-
- /* select = mux && !gpio */
- select = ((pmx->mux_en & ~pmx->gpio_en) >> pin_shift) & 1;
-
- /* set up the mux */
- __global_lock2(flags);
- reg = pmx_read(pmx, grp->reg);
- reg &= ~BIT(grp->bit);
- reg |= select << grp->bit;
- pmx_write(pmx, reg, grp->reg);
- __global_unlock2(flags);
-}
-
-static int tz1090_pdc_pinctrl_set_mux(struct pinctrl_dev *pctldev,
- unsigned int function,
- unsigned int group)
-{
- struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- const struct tz1090_pdc_pingroup *grp = &tz1090_pdc_groups[group];
-
- dev_dbg(pctldev->dev, "%s(func=%u (%s), group=%u (%s))\n",
- __func__,
- function, tz1090_pdc_functions[function].name,
- group, tz1090_pdc_groups[group].name);
-
- /* is it even a mux? */
- if (grp->drv)
- return -EINVAL;
-
- /* does this group even control the function? */
- if (function != grp->func)
- return -EINVAL;
-
- /* record the pin being muxed and update mux bit */
- spin_lock(&pmx->lock);
- pmx->mux_en |= BIT(grp->pins[0]);
- tz1090_pdc_pinctrl_mux(pmx, grp);
- spin_unlock(&pmx->lock);
- return 0;
-}
-
-static const struct tz1090_pdc_pingroup *find_mux_group(
- struct tz1090_pdc_pmx *pmx,
- unsigned int pin)
-{
- const struct tz1090_pdc_pingroup *grp;
- unsigned int group;
-
- grp = tz1090_pdc_groups;
- for (group = 0; group < ARRAY_SIZE(tz1090_pdc_groups); ++group, ++grp) {
- /* only match muxes */
- if (grp->drv)
- continue;
-
- /* with a matching pin */
- if (grp->pins[0] == pin)
- return grp;
- }
-
- return NULL;
-}
-
-static int tz1090_pdc_pinctrl_gpio_request_enable(
- struct pinctrl_dev *pctldev,
- struct pinctrl_gpio_range *range,
- unsigned int pin)
-{
- struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- const struct tz1090_pdc_pingroup *grp = find_mux_group(pmx, pin);
-
- if (grp) {
- /* record the pin in GPIO use and update mux bit */
- spin_lock(&pmx->lock);
- pmx->gpio_en |= BIT(pin);
- tz1090_pdc_pinctrl_mux(pmx, grp);
- spin_unlock(&pmx->lock);
- }
- return 0;
-}
-
-static void tz1090_pdc_pinctrl_gpio_disable_free(
- struct pinctrl_dev *pctldev,
- struct pinctrl_gpio_range *range,
- unsigned int pin)
-{
- struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- const struct tz1090_pdc_pingroup *grp = find_mux_group(pmx, pin);
-
- if (grp) {
- /* record the pin not in GPIO use and update mux bit */
- spin_lock(&pmx->lock);
- pmx->gpio_en &= ~BIT(pin);
- tz1090_pdc_pinctrl_mux(pmx, grp);
- spin_unlock(&pmx->lock);
- }
-}
-
-static const struct pinmux_ops tz1090_pdc_pinmux_ops = {
- .get_functions_count = tz1090_pdc_pinctrl_get_funcs_count,
- .get_function_name = tz1090_pdc_pinctrl_get_func_name,
- .get_function_groups = tz1090_pdc_pinctrl_get_func_groups,
- .set_mux = tz1090_pdc_pinctrl_set_mux,
- .gpio_request_enable = tz1090_pdc_pinctrl_gpio_request_enable,
- .gpio_disable_free = tz1090_pdc_pinctrl_gpio_disable_free,
-};
-
-/*
- * Pin config operations
- */
-
-static int tz1090_pdc_pinconf_reg(struct pinctrl_dev *pctldev,
- unsigned int pin,
- enum pin_config_param param,
- bool report_err,
- u32 *reg, u32 *width, u32 *mask, u32 *shift,
- u32 *val)
-{
- /* Find information about parameter's register */
- switch (param) {
- case PIN_CONFIG_BIAS_DISABLE:
- case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
- *val = REG_PU_PD_TRISTATE;
- break;
- case PIN_CONFIG_BIAS_PULL_UP:
- *val = REG_PU_PD_UP;
- break;
- case PIN_CONFIG_BIAS_PULL_DOWN:
- *val = REG_PU_PD_DOWN;
- break;
- case PIN_CONFIG_BIAS_BUS_HOLD:
- *val = REG_PU_PD_REPEATER;
- break;
- default:
- return -ENOTSUPP;
- }
-
- /* Only input bias parameters supported */
- *reg = REG_GPIO_CONTROL2;
- *shift = REG_GPIO_CONTROL2_PU_PD_S + pin*2;
- *width = 2;
-
- /* Calculate field information */
- *mask = (BIT(*width) - 1) << *shift;
-
- return 0;
-}
-
-static int tz1090_pdc_pinconf_get(struct pinctrl_dev *pctldev,
- unsigned int pin, unsigned long *config)
-{
- struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- enum pin_config_param param = pinconf_to_config_param(*config);
- int ret;
- u32 reg, width, mask, shift, val, tmp, arg;
-
- /* Get register information */
- ret = tz1090_pdc_pinconf_reg(pctldev, pin, param, true,
- &reg, &width, &mask, &shift, &val);
- if (ret < 0)
- return ret;
-
- /* Extract field from register */
- tmp = pmx_read(pmx, reg);
- arg = ((tmp & mask) >> shift) == val;
-
- /* Config not active */
- if (!arg)
- return -EINVAL;
-
- /* And pack config */
- *config = pinconf_to_config_packed(param, arg);
-
- return 0;
-}
-
-static int tz1090_pdc_pinconf_set(struct pinctrl_dev *pctldev,
- unsigned int pin, unsigned long *configs,
- unsigned num_configs)
-{
- struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- enum pin_config_param param;
- unsigned int arg;
- int ret;
- u32 reg, width, mask, shift, val, tmp;
- unsigned long flags;
- int i;
-
- for (i = 0; i < num_configs; i++) {
- param = pinconf_to_config_param(configs[i]);
- arg = pinconf_to_config_argument(configs[i]);
-
- dev_dbg(pctldev->dev, "%s(pin=%s, config=%#lx)\n",
- __func__, tz1090_pdc_pins[pin].name, configs[i]);
-
- /* Get register information */
- ret = tz1090_pdc_pinconf_reg(pctldev, pin, param, true,
- &reg, &width, &mask, &shift, &val);
- if (ret < 0)
- return ret;
-
- /* Unpack argument and range check it */
- if (arg > 1) {
- dev_dbg(pctldev->dev, "%s: arg %u out of range\n",
- __func__, arg);
- return -EINVAL;
- }
-
- /* Write register field */
- __global_lock2(flags);
- tmp = pmx_read(pmx, reg);
- tmp &= ~mask;
- if (arg)
- tmp |= val << shift;
- pmx_write(pmx, tmp, reg);
- __global_unlock2(flags);
- } /* for each config */
-
- return 0;
-}
-
-static const int tz1090_pdc_boolean_map[] = {
- [0] = -EINVAL,
- [1] = 1,
-};
-
-static const int tz1090_pdc_dr_map[] = {
- [REG_DR_2mA] = 2,
- [REG_DR_4mA] = 4,
- [REG_DR_8mA] = 8,
- [REG_DR_12mA] = 12,
-};
-
-static int tz1090_pdc_pinconf_group_reg(struct pinctrl_dev *pctldev,
- const struct tz1090_pdc_pingroup *g,
- enum pin_config_param param,
- bool report_err, u32 *reg, u32 *width,
- u32 *mask, u32 *shift, const int **map)
-{
- /* Drive configuration applies in groups, but not to all groups. */
- if (!g->drv) {
- if (report_err)
- dev_dbg(pctldev->dev,
- "%s: group %s has no drive control\n",
- __func__, g->name);
- return -ENOTSUPP;
- }
-
- /* Find information about drive parameter's register */
- *reg = REG_GPIO_CONTROL2;
- switch (param) {
- case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
- *shift = REG_GPIO_CONTROL2_PDC_SCHMITT_S;
- *width = 1;
- *map = tz1090_pdc_boolean_map;
- break;
- case PIN_CONFIG_DRIVE_STRENGTH:
- *shift = REG_GPIO_CONTROL2_PDC_DR_S;
- *width = 2;
- *map = tz1090_pdc_dr_map;
- break;
- case PIN_CONFIG_LOW_POWER_MODE:
- *shift = REG_GPIO_CONTROL2_PDC_POS_S;
- *width = 1;
- *map = tz1090_pdc_boolean_map;
- break;
- default:
- return -ENOTSUPP;
- }
-
- /* Calculate field information */
- *mask = (BIT(*width) - 1) << *shift;
-
- return 0;
-}
-
-static int tz1090_pdc_pinconf_group_get(struct pinctrl_dev *pctldev,
- unsigned int group,
- unsigned long *config)
-{
- struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- const struct tz1090_pdc_pingroup *g = &tz1090_pdc_groups[group];
- enum pin_config_param param = pinconf_to_config_param(*config);
- int ret, arg;
- u32 reg, width, mask, shift, val;
- const int *map;
-
- /* Get register information */
- ret = tz1090_pdc_pinconf_group_reg(pctldev, g, param, true,
- &reg, &width, &mask, &shift, &map);
- if (ret < 0)
- return ret;
-
- /* Extract field from register */
- val = pmx_read(pmx, reg);
- arg = map[(val & mask) >> shift];
- if (arg < 0)
- return arg;
-
- /* And pack config */
- *config = pinconf_to_config_packed(param, arg);
-
- return 0;
-}
-
-static int tz1090_pdc_pinconf_group_set(struct pinctrl_dev *pctldev,
- unsigned int group,
- unsigned long *configs,
- unsigned num_configs)
-{
- struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- const struct tz1090_pdc_pingroup *g = &tz1090_pdc_groups[group];
- enum pin_config_param param;
- const unsigned int *pit;
- unsigned int i;
- int ret, arg;
- u32 reg, width, mask, shift, val;
- unsigned long flags;
- const int *map;
- int j;
-
- for (j = 0; j < num_configs; j++) {
- param = pinconf_to_config_param(configs[j]);
-
- dev_dbg(pctldev->dev, "%s(group=%s, config=%#lx)\n",
- __func__, g->name, configs[j]);
-
- /* Get register information */
- ret = tz1090_pdc_pinconf_group_reg(pctldev, g, param, true,
- &reg, &width, &mask, &shift,
- &map);
- if (ret < 0) {
- /*
- * Maybe we're trying to set a per-pin configuration
- * of a group, so do the pins one by one. This is
- * mainly as a convenience.
- */
- for (i = 0, pit = g->pins; i < g->npins; ++i, ++pit) {
- ret = tz1090_pdc_pinconf_set(pctldev, *pit,
- configs, num_configs);
- if (ret)
- return ret;
- }
- return 0;
- }
-
- /* Unpack argument and map it to register value */
- arg = pinconf_to_config_argument(configs[j]);
- for (i = 0; i < BIT(width); ++i) {
- if (map[i] == arg || (map[i] == -EINVAL && !arg)) {
- /* Write register field */
- __global_lock2(flags);
- val = pmx_read(pmx, reg);
- val &= ~mask;
- val |= i << shift;
- pmx_write(pmx, val, reg);
- __global_unlock2(flags);
- goto next_config;
- }
- }
-
- dev_dbg(pctldev->dev, "%s: arg %u not supported\n",
- __func__, arg);
- return 0;
-
-next_config:
- ;
- } /* for each config */
-
- return 0;
-}
-
-static const struct pinconf_ops tz1090_pdc_pinconf_ops = {
- .is_generic = true,
- .pin_config_get = tz1090_pdc_pinconf_get,
- .pin_config_set = tz1090_pdc_pinconf_set,
- .pin_config_group_get = tz1090_pdc_pinconf_group_get,
- .pin_config_group_set = tz1090_pdc_pinconf_group_set,
- .pin_config_config_dbg_show = pinconf_generic_dump_config,
-};
-
-/*
- * Pin control driver setup
- */
-
-static struct pinctrl_desc tz1090_pdc_pinctrl_desc = {
- .pctlops = &tz1090_pdc_pinctrl_ops,
- .pmxops = &tz1090_pdc_pinmux_ops,
- .confops = &tz1090_pdc_pinconf_ops,
- .owner = THIS_MODULE,
-};
-
-static int tz1090_pdc_pinctrl_probe(struct platform_device *pdev)
-{
- struct tz1090_pdc_pmx *pmx;
- struct resource *res;
-
- pmx = devm_kzalloc(&pdev->dev, sizeof(*pmx), GFP_KERNEL);
- if (!pmx)
- return -ENOMEM;
-
- pmx->dev = &pdev->dev;
- spin_lock_init(&pmx->lock);
-
- tz1090_pdc_pinctrl_desc.name = dev_name(&pdev->dev);
- tz1090_pdc_pinctrl_desc.pins = tz1090_pdc_pins;
- tz1090_pdc_pinctrl_desc.npins = ARRAY_SIZE(tz1090_pdc_pins);
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- pmx->regs = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(pmx->regs))
- return PTR_ERR(pmx->regs);
-
- pmx->pctl = devm_pinctrl_register(&pdev->dev, &tz1090_pdc_pinctrl_desc,
- pmx);
- if (IS_ERR(pmx->pctl)) {
- dev_err(&pdev->dev, "Couldn't register pinctrl driver\n");
- return PTR_ERR(pmx->pctl);
- }
-
- platform_set_drvdata(pdev, pmx);
-
- dev_info(&pdev->dev, "TZ1090 PDC pinctrl driver initialised\n");
-
- return 0;
-}
-
-static const struct of_device_id tz1090_pdc_pinctrl_of_match[] = {
- { .compatible = "img,tz1090-pdc-pinctrl", },
- { },
-};
-
-static struct platform_driver tz1090_pdc_pinctrl_driver = {
- .driver = {
- .name = "tz1090-pdc-pinctrl",
- .of_match_table = tz1090_pdc_pinctrl_of_match,
- },
- .probe = tz1090_pdc_pinctrl_probe,
-};
-
-static int __init tz1090_pdc_pinctrl_init(void)
-{
- return platform_driver_register(&tz1090_pdc_pinctrl_driver);
-}
-arch_initcall(tz1090_pdc_pinctrl_init);
-
-static void __exit tz1090_pdc_pinctrl_exit(void)
-{
- platform_driver_unregister(&tz1090_pdc_pinctrl_driver);
-}
-module_exit(tz1090_pdc_pinctrl_exit);
-
-MODULE_AUTHOR("Imagination Technologies Ltd.");
-MODULE_DESCRIPTION("Toumaz Xenif TZ1090 PDC pinctrl driver");
-MODULE_LICENSE("GPL v2");
-MODULE_DEVICE_TABLE(of, tz1090_pdc_pinctrl_of_match);
diff --git a/drivers/pinctrl/pinctrl-tz1090.c b/drivers/pinctrl/pinctrl-tz1090.c
deleted file mode 100644
index 2379ce2be365..000000000000
--- a/drivers/pinctrl/pinctrl-tz1090.c
+++ /dev/null
@@ -1,2005 +0,0 @@
-/*
- * Pinctrl driver for the Toumaz Xenif TZ1090 SoC
- *
- * Copyright (c) 2013, Imagination Technologies Ltd.
- *
- * Derived from Tegra code:
- * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved.
- *
- * Derived from code:
- * Copyright (C) 2010 Google, Inc.
- * Copyright (C) 2010 NVIDIA Corporation
- * Copyright (C) 2009-2011 ST-Ericsson AB
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#include <linux/bitops.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
-#include <linux/pinctrl/machine.h>
-#include <linux/pinctrl/pinconf-generic.h>
-#include <linux/pinctrl/pinctrl.h>
-#include <linux/pinctrl/pinmux.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-
-/*
- * The registers may be shared with other threads/cores, so we need to use the
- * metag global lock2 for atomicity.
- */
-#include <asm/global_lock.h>
-
-#include "core.h"
-#include "pinconf.h"
-
-/* Register offsets from bank base address */
-#define REG_PINCTRL_SELECT 0x10
-#define REG_PINCTRL_SCHMITT 0x90
-#define REG_PINCTRL_PU_PD 0xa0
-#define REG_PINCTRL_SR 0xc0
-#define REG_PINCTRL_DR 0xd0
-#define REG_PINCTRL_IF_CTL 0xe0
-
-/* REG_PINCTRL_PU_PD field values */
-#define REG_PU_PD_TRISTATE 0
-#define REG_PU_PD_UP 1
-#define REG_PU_PD_DOWN 2
-#define REG_PU_PD_REPEATER 3
-
-/* REG_PINCTRL_DR field values */
-#define REG_DR_2mA 0
-#define REG_DR_4mA 1
-#define REG_DR_8mA 2
-#define REG_DR_12mA 3
-
-/**
- * struct tz1090_function - TZ1090 pinctrl mux function
- * @name: The name of the function, exported to pinctrl core.
- * @groups: An array of pin groups that may select this function.
- * @ngroups: The number of entries in @groups.
- */
-struct tz1090_function {
- const char *name;
- const char * const *groups;
- unsigned int ngroups;
-};
-
-/**
- * struct tz1090_muxdesc - TZ1090 individual mux description
- * @funcs: Function for each mux value.
- * @reg: Mux register offset. 0 if unsupported.
- * @bit: Mux register bit. 0 if unsupported.
- * @width: Mux field width. 0 if unsupported.
- *
- * A representation of a group of signals (possibly just one signal) in the
- * TZ1090 which can be muxed to a set of functions or sub muxes.
- */
-struct tz1090_muxdesc {
- int funcs[5];
- u16 reg;
- u8 bit;
- u8 width;
-};
-
-/**
- * struct tz1090_pingroup - TZ1090 pin group
- * @name: Name of pin group.
- * @pins: Array of pin numbers in this pin group.
- * @npins: Number of pins in this pin group.
- * @mux: Top level mux.
- * @drv: Drive control supported, 0 if unsupported.
- * This means Schmitt, Slew, and Drive strength.
- * @slw_bit: Slew register bit. 0 if unsupported.
- * The same bit is used for Schmitt, and Drive (*2).
- * @func: Currently muxed function.
- * @func_count: Number of pins using current mux function.
- *
- * A representation of a group of pins (possibly just one pin) in the TZ1090
- * pin controller. Each group allows some parameter or parameters to be
- * configured. The most common is mux function selection.
- */
-struct tz1090_pingroup {
- const char *name;
- const unsigned int *pins;
- unsigned int npins;
- struct tz1090_muxdesc mux;
-
- bool drv;
- u8 slw_bit;
-
- int func;
- unsigned int func_count;
-};
-
-/*
- * Most pins affected by the pinmux can also be GPIOs. Define these first.
- * These must match how the GPIO driver names/numbers its pins.
- */
-
-enum tz1090_pin {
- /* GPIO pins */
- TZ1090_PIN_SDIO_CLK,
- TZ1090_PIN_SDIO_CMD,
- TZ1090_PIN_SDIO_D0,
- TZ1090_PIN_SDIO_D1,
- TZ1090_PIN_SDIO_D2,
- TZ1090_PIN_SDIO_D3,
- TZ1090_PIN_SDH_CD,
- TZ1090_PIN_SDH_WP,
- TZ1090_PIN_SPI0_MCLK,
- TZ1090_PIN_SPI0_CS0,
- TZ1090_PIN_SPI0_CS1,
- TZ1090_PIN_SPI0_CS2,
- TZ1090_PIN_SPI0_DOUT,
- TZ1090_PIN_SPI0_DIN,
- TZ1090_PIN_SPI1_MCLK,
- TZ1090_PIN_SPI1_CS0,
- TZ1090_PIN_SPI1_CS1,
- TZ1090_PIN_SPI1_CS2,
- TZ1090_PIN_SPI1_DOUT,
- TZ1090_PIN_SPI1_DIN,
- TZ1090_PIN_UART0_RXD,
- TZ1090_PIN_UART0_TXD,
- TZ1090_PIN_UART0_CTS,
- TZ1090_PIN_UART0_RTS,
- TZ1090_PIN_UART1_RXD,
- TZ1090_PIN_UART1_TXD,
- TZ1090_PIN_SCB0_SDAT,
- TZ1090_PIN_SCB0_SCLK,
- TZ1090_PIN_SCB1_SDAT,
- TZ1090_PIN_SCB1_SCLK,
- TZ1090_PIN_SCB2_SDAT,
- TZ1090_PIN_SCB2_SCLK,
- TZ1090_PIN_I2S_MCLK,
- TZ1090_PIN_I2S_BCLK_OUT,
- TZ1090_PIN_I2S_LRCLK_OUT,
- TZ1090_PIN_I2S_DOUT0,
- TZ1090_PIN_I2S_DOUT1,
- TZ1090_PIN_I2S_DOUT2,
- TZ1090_PIN_I2S_DIN,
- TZ1090_PIN_PDM_A,
- TZ1090_PIN_PDM_B,
- TZ1090_PIN_PDM_C,
- TZ1090_PIN_PDM_D,
- TZ1090_PIN_TFT_RED0,
- TZ1090_PIN_TFT_RED1,
- TZ1090_PIN_TFT_RED2,
- TZ1090_PIN_TFT_RED3,
- TZ1090_PIN_TFT_RED4,
- TZ1090_PIN_TFT_RED5,
- TZ1090_PIN_TFT_RED6,
- TZ1090_PIN_TFT_RED7,
- TZ1090_PIN_TFT_GREEN0,
- TZ1090_PIN_TFT_GREEN1,
- TZ1090_PIN_TFT_GREEN2,
- TZ1090_PIN_TFT_GREEN3,
- TZ1090_PIN_TFT_GREEN4,
- TZ1090_PIN_TFT_GREEN5,
- TZ1090_PIN_TFT_GREEN6,
- TZ1090_PIN_TFT_GREEN7,
- TZ1090_PIN_TFT_BLUE0,
- TZ1090_PIN_TFT_BLUE1,
- TZ1090_PIN_TFT_BLUE2,
- TZ1090_PIN_TFT_BLUE3,
- TZ1090_PIN_TFT_BLUE4,
- TZ1090_PIN_TFT_BLUE5,
- TZ1090_PIN_TFT_BLUE6,
- TZ1090_PIN_TFT_BLUE7,
- TZ1090_PIN_TFT_VDDEN_GD,
- TZ1090_PIN_TFT_PANELCLK,
- TZ1090_PIN_TFT_BLANK_LS,
- TZ1090_PIN_TFT_VSYNC_NS,
- TZ1090_PIN_TFT_HSYNC_NR,
- TZ1090_PIN_TFT_VD12ACB,
- TZ1090_PIN_TFT_PWRSAVE,
- TZ1090_PIN_TX_ON,
- TZ1090_PIN_RX_ON,
- TZ1090_PIN_PLL_ON,
- TZ1090_PIN_PA_ON,
- TZ1090_PIN_RX_HP,
- TZ1090_PIN_GAIN0,
- TZ1090_PIN_GAIN1,
- TZ1090_PIN_GAIN2,
- TZ1090_PIN_GAIN3,
- TZ1090_PIN_GAIN4,
- TZ1090_PIN_GAIN5,
- TZ1090_PIN_GAIN6,
- TZ1090_PIN_GAIN7,
- TZ1090_PIN_ANT_SEL0,
- TZ1090_PIN_ANT_SEL1,
- TZ1090_PIN_SDH_CLK_IN,
-
- /* Non-GPIO pins */
- TZ1090_PIN_TCK,
- TZ1090_PIN_TRST,
- TZ1090_PIN_TDI,
- TZ1090_PIN_TDO,
- TZ1090_PIN_TMS,
- TZ1090_PIN_CLK_OUT0,
- TZ1090_PIN_CLK_OUT1,
-
- NUM_GPIOS = TZ1090_PIN_TCK,
-};
-
-/* Pin names */
-
-static const struct pinctrl_pin_desc tz1090_pins[] = {
- /* GPIO pins */
- PINCTRL_PIN(TZ1090_PIN_SDIO_CLK, "sdio_clk"),
- PINCTRL_PIN(TZ1090_PIN_SDIO_CMD, "sdio_cmd"),
- PINCTRL_PIN(TZ1090_PIN_SDIO_D0, "sdio_d0"),
- PINCTRL_PIN(TZ1090_PIN_SDIO_D1, "sdio_d1"),
- PINCTRL_PIN(TZ1090_PIN_SDIO_D2, "sdio_d2"),
- PINCTRL_PIN(TZ1090_PIN_SDIO_D3, "sdio_d3"),
- PINCTRL_PIN(TZ1090_PIN_SDH_CD, "sdh_cd"),
- PINCTRL_PIN(TZ1090_PIN_SDH_WP, "sdh_wp"),
- PINCTRL_PIN(TZ1090_PIN_SPI0_MCLK, "spi0_mclk"),
- PINCTRL_PIN(TZ1090_PIN_SPI0_CS0, "spi0_cs0"),
- PINCTRL_PIN(TZ1090_PIN_SPI0_CS1, "spi0_cs1"),
- PINCTRL_PIN(TZ1090_PIN_SPI0_CS2, "spi0_cs2"),
- PINCTRL_PIN(TZ1090_PIN_SPI0_DOUT, "spi0_dout"),
- PINCTRL_PIN(TZ1090_PIN_SPI0_DIN, "spi0_din"),
- PINCTRL_PIN(TZ1090_PIN_SPI1_MCLK, "spi1_mclk"),
- PINCTRL_PIN(TZ1090_PIN_SPI1_CS0, "spi1_cs0"),
- PINCTRL_PIN(TZ1090_PIN_SPI1_CS1, "spi1_cs1"),
- PINCTRL_PIN(TZ1090_PIN_SPI1_CS2, "spi1_cs2"),
- PINCTRL_PIN(TZ1090_PIN_SPI1_DOUT, "spi1_dout"),
- PINCTRL_PIN(TZ1090_PIN_SPI1_DIN, "spi1_din"),
- PINCTRL_PIN(TZ1090_PIN_UART0_RXD, "uart0_rxd"),
- PINCTRL_PIN(TZ1090_PIN_UART0_TXD, "uart0_txd"),
- PINCTRL_PIN(TZ1090_PIN_UART0_CTS, "uart0_cts"),
- PINCTRL_PIN(TZ1090_PIN_UART0_RTS, "uart0_rts"),
- PINCTRL_PIN(TZ1090_PIN_UART1_RXD, "uart1_rxd"),
- PINCTRL_PIN(TZ1090_PIN_UART1_TXD, "uart1_txd"),
- PINCTRL_PIN(TZ1090_PIN_SCB0_SDAT, "scb0_sdat"),
- PINCTRL_PIN(TZ1090_PIN_SCB0_SCLK, "scb0_sclk"),
- PINCTRL_PIN(TZ1090_PIN_SCB1_SDAT, "scb1_sdat"),
- PINCTRL_PIN(TZ1090_PIN_SCB1_SCLK, "scb1_sclk"),
- PINCTRL_PIN(TZ1090_PIN_SCB2_SDAT, "scb2_sdat"),
- PINCTRL_PIN(TZ1090_PIN_SCB2_SCLK, "scb2_sclk"),
- PINCTRL_PIN(TZ1090_PIN_I2S_MCLK, "i2s_mclk"),
- PINCTRL_PIN(TZ1090_PIN_I2S_BCLK_OUT, "i2s_bclk_out"),
- PINCTRL_PIN(TZ1090_PIN_I2S_LRCLK_OUT, "i2s_lrclk_out"),
- PINCTRL_PIN(TZ1090_PIN_I2S_DOUT0, "i2s_dout0"),
- PINCTRL_PIN(TZ1090_PIN_I2S_DOUT1, "i2s_dout1"),
- PINCTRL_PIN(TZ1090_PIN_I2S_DOUT2, "i2s_dout2"),
- PINCTRL_PIN(TZ1090_PIN_I2S_DIN, "i2s_din"),
- PINCTRL_PIN(TZ1090_PIN_PDM_A, "pdm_a"),
- PINCTRL_PIN(TZ1090_PIN_PDM_B, "pdm_b"),
- PINCTRL_PIN(TZ1090_PIN_PDM_C, "pdm_c"),
- PINCTRL_PIN(TZ1090_PIN_PDM_D, "pdm_d"),
- PINCTRL_PIN(TZ1090_PIN_TFT_RED0, "tft_red0"),
- PINCTRL_PIN(TZ1090_PIN_TFT_RED1, "tft_red1"),
- PINCTRL_PIN(TZ1090_PIN_TFT_RED2, "tft_red2"),
- PINCTRL_PIN(TZ1090_PIN_TFT_RED3, "tft_red3"),
- PINCTRL_PIN(TZ1090_PIN_TFT_RED4, "tft_red4"),
- PINCTRL_PIN(TZ1090_PIN_TFT_RED5, "tft_red5"),
- PINCTRL_PIN(TZ1090_PIN_TFT_RED6, "tft_red6"),
- PINCTRL_PIN(TZ1090_PIN_TFT_RED7, "tft_red7"),
- PINCTRL_PIN(TZ1090_PIN_TFT_GREEN0, "tft_green0"),
- PINCTRL_PIN(TZ1090_PIN_TFT_GREEN1, "tft_green1"),
- PINCTRL_PIN(TZ1090_PIN_TFT_GREEN2, "tft_green2"),
- PINCTRL_PIN(TZ1090_PIN_TFT_GREEN3, "tft_green3"),
- PINCTRL_PIN(TZ1090_PIN_TFT_GREEN4, "tft_green4"),
- PINCTRL_PIN(TZ1090_PIN_TFT_GREEN5, "tft_green5"),
- PINCTRL_PIN(TZ1090_PIN_TFT_GREEN6, "tft_green6"),
- PINCTRL_PIN(TZ1090_PIN_TFT_GREEN7, "tft_green7"),
- PINCTRL_PIN(TZ1090_PIN_TFT_BLUE0, "tft_blue0"),
- PINCTRL_PIN(TZ1090_PIN_TFT_BLUE1, "tft_blue1"),
- PINCTRL_PIN(TZ1090_PIN_TFT_BLUE2, "tft_blue2"),
- PINCTRL_PIN(TZ1090_PIN_TFT_BLUE3, "tft_blue3"),
- PINCTRL_PIN(TZ1090_PIN_TFT_BLUE4, "tft_blue4"),
- PINCTRL_PIN(TZ1090_PIN_TFT_BLUE5, "tft_blue5"),
- PINCTRL_PIN(TZ1090_PIN_TFT_BLUE6, "tft_blue6"),
- PINCTRL_PIN(TZ1090_PIN_TFT_BLUE7, "tft_blue7"),
- PINCTRL_PIN(TZ1090_PIN_TFT_VDDEN_GD, "tft_vdden_gd"),
- PINCTRL_PIN(TZ1090_PIN_TFT_PANELCLK, "tft_panelclk"),
- PINCTRL_PIN(TZ1090_PIN_TFT_BLANK_LS, "tft_blank_ls"),
- PINCTRL_PIN(TZ1090_PIN_TFT_VSYNC_NS, "tft_vsync_ns"),
- PINCTRL_PIN(TZ1090_PIN_TFT_HSYNC_NR, "tft_hsync_nr"),
- PINCTRL_PIN(TZ1090_PIN_TFT_VD12ACB, "tft_vd12acb"),
- PINCTRL_PIN(TZ1090_PIN_TFT_PWRSAVE, "tft_pwrsave"),
- PINCTRL_PIN(TZ1090_PIN_TX_ON, "tx_on"),
- PINCTRL_PIN(TZ1090_PIN_RX_ON, "rx_on"),
- PINCTRL_PIN(TZ1090_PIN_PLL_ON, "pll_on"),
- PINCTRL_PIN(TZ1090_PIN_PA_ON, "pa_on"),
- PINCTRL_PIN(TZ1090_PIN_RX_HP, "rx_hp"),
- PINCTRL_PIN(TZ1090_PIN_GAIN0, "gain0"),
- PINCTRL_PIN(TZ1090_PIN_GAIN1, "gain1"),
- PINCTRL_PIN(TZ1090_PIN_GAIN2, "gain2"),
- PINCTRL_PIN(TZ1090_PIN_GAIN3, "gain3"),
- PINCTRL_PIN(TZ1090_PIN_GAIN4, "gain4"),
- PINCTRL_PIN(TZ1090_PIN_GAIN5, "gain5"),
- PINCTRL_PIN(TZ1090_PIN_GAIN6, "gain6"),
- PINCTRL_PIN(TZ1090_PIN_GAIN7, "gain7"),
- PINCTRL_PIN(TZ1090_PIN_ANT_SEL0, "ant_sel0"),
- PINCTRL_PIN(TZ1090_PIN_ANT_SEL1, "ant_sel1"),
- PINCTRL_PIN(TZ1090_PIN_SDH_CLK_IN, "sdh_clk_in"),
-
- /* Non-GPIO pins */
- PINCTRL_PIN(TZ1090_PIN_TCK, "tck"),
- PINCTRL_PIN(TZ1090_PIN_TRST, "trst"),
- PINCTRL_PIN(TZ1090_PIN_TDI, "tdi"),
- PINCTRL_PIN(TZ1090_PIN_TDO, "tdo"),
- PINCTRL_PIN(TZ1090_PIN_TMS, "tms"),
- PINCTRL_PIN(TZ1090_PIN_CLK_OUT0, "clk_out0"),
- PINCTRL_PIN(TZ1090_PIN_CLK_OUT1, "clk_out1"),
-};
-
-/* Pins in each pin group */
-
-static const unsigned int spi1_cs2_pins[] = {
- TZ1090_PIN_SPI1_CS2,
-};
-
-static const unsigned int pdm_d_pins[] = {
- TZ1090_PIN_PDM_D,
-};
-
-static const unsigned int tft_pins[] = {
- TZ1090_PIN_TFT_RED0,
- TZ1090_PIN_TFT_RED1,
- TZ1090_PIN_TFT_RED2,
- TZ1090_PIN_TFT_RED3,
- TZ1090_PIN_TFT_RED4,
- TZ1090_PIN_TFT_RED5,
- TZ1090_PIN_TFT_RED6,
- TZ1090_PIN_TFT_RED7,
- TZ1090_PIN_TFT_GREEN0,
- TZ1090_PIN_TFT_GREEN1,
- TZ1090_PIN_TFT_GREEN2,
- TZ1090_PIN_TFT_GREEN3,
- TZ1090_PIN_TFT_GREEN4,
- TZ1090_PIN_TFT_GREEN5,
- TZ1090_PIN_TFT_GREEN6,
- TZ1090_PIN_TFT_GREEN7,
- TZ1090_PIN_TFT_BLUE0,
- TZ1090_PIN_TFT_BLUE1,
- TZ1090_PIN_TFT_BLUE2,
- TZ1090_PIN_TFT_BLUE3,
- TZ1090_PIN_TFT_BLUE4,
- TZ1090_PIN_TFT_BLUE5,
- TZ1090_PIN_TFT_BLUE6,
- TZ1090_PIN_TFT_BLUE7,
- TZ1090_PIN_TFT_VDDEN_GD,
- TZ1090_PIN_TFT_PANELCLK,
- TZ1090_PIN_TFT_BLANK_LS,
- TZ1090_PIN_TFT_VSYNC_NS,
- TZ1090_PIN_TFT_HSYNC_NR,
- TZ1090_PIN_TFT_VD12ACB,
- TZ1090_PIN_TFT_PWRSAVE,
-};
-
-static const unsigned int afe_pins[] = {
- TZ1090_PIN_TX_ON,
- TZ1090_PIN_RX_ON,
- TZ1090_PIN_PLL_ON,
- TZ1090_PIN_PA_ON,
- TZ1090_PIN_RX_HP,
- TZ1090_PIN_ANT_SEL0,
- TZ1090_PIN_ANT_SEL1,
- TZ1090_PIN_GAIN0,
- TZ1090_PIN_GAIN1,
- TZ1090_PIN_GAIN2,
- TZ1090_PIN_GAIN3,
- TZ1090_PIN_GAIN4,
- TZ1090_PIN_GAIN5,
- TZ1090_PIN_GAIN6,
- TZ1090_PIN_GAIN7,
-};
-
-static const unsigned int sdio_pins[] = {
- TZ1090_PIN_SDIO_CLK,
- TZ1090_PIN_SDIO_CMD,
- TZ1090_PIN_SDIO_D0,
- TZ1090_PIN_SDIO_D1,
- TZ1090_PIN_SDIO_D2,
- TZ1090_PIN_SDIO_D3,
-};
-
-static const unsigned int sdh_pins[] = {
- TZ1090_PIN_SDH_CD,
- TZ1090_PIN_SDH_WP,
- TZ1090_PIN_SDH_CLK_IN,
-};
-
-static const unsigned int spi0_pins[] = {
- TZ1090_PIN_SPI0_MCLK,
- TZ1090_PIN_SPI0_CS0,
- TZ1090_PIN_SPI0_CS1,
- TZ1090_PIN_SPI0_CS2,
- TZ1090_PIN_SPI0_DOUT,
- TZ1090_PIN_SPI0_DIN,
-};
-
-static const unsigned int spi1_pins[] = {
- TZ1090_PIN_SPI1_MCLK,
- TZ1090_PIN_SPI1_CS0,
- TZ1090_PIN_SPI1_CS1,
- TZ1090_PIN_SPI1_CS2,
- TZ1090_PIN_SPI1_DOUT,
- TZ1090_PIN_SPI1_DIN,
-};
-
-static const unsigned int uart0_pins[] = {
- TZ1090_PIN_UART0_RTS,
- TZ1090_PIN_UART0_CTS,
- TZ1090_PIN_UART0_TXD,
- TZ1090_PIN_UART0_RXD,
-};
-
-static const unsigned int uart1_pins[] = {
- TZ1090_PIN_UART1_TXD,
- TZ1090_PIN_UART1_RXD,
-};
-
-static const unsigned int uart_pins[] = {
- TZ1090_PIN_UART1_TXD,
- TZ1090_PIN_UART1_RXD,
- TZ1090_PIN_UART0_RTS,
- TZ1090_PIN_UART0_CTS,
- TZ1090_PIN_UART0_TXD,
- TZ1090_PIN_UART0_RXD,
-};
-
-static const unsigned int scb0_pins[] = {
- TZ1090_PIN_SCB0_SDAT,
- TZ1090_PIN_SCB0_SCLK,
-};
-
-static const unsigned int scb1_pins[] = {
- TZ1090_PIN_SCB1_SDAT,
- TZ1090_PIN_SCB1_SCLK,
-};
-
-static const unsigned int scb2_pins[] = {
- TZ1090_PIN_SCB2_SDAT,
- TZ1090_PIN_SCB2_SCLK,
-};
-
-static const unsigned int i2s_pins[] = {
- TZ1090_PIN_I2S_MCLK,
- TZ1090_PIN_I2S_BCLK_OUT,
- TZ1090_PIN_I2S_LRCLK_OUT,
- TZ1090_PIN_I2S_DOUT0,
- TZ1090_PIN_I2S_DOUT1,
- TZ1090_PIN_I2S_DOUT2,
- TZ1090_PIN_I2S_DIN,
-};
-
-static const unsigned int jtag_pins[] = {
- TZ1090_PIN_TCK,
- TZ1090_PIN_TRST,
- TZ1090_PIN_TDI,
- TZ1090_PIN_TDO,
- TZ1090_PIN_TMS,
-};
-
-/* Pins in each drive pin group */
-
-static const unsigned int drive_sdio_pins[] = {
- TZ1090_PIN_SDIO_CLK,
- TZ1090_PIN_SDIO_CMD,
- TZ1090_PIN_SDIO_D0,
- TZ1090_PIN_SDIO_D1,
- TZ1090_PIN_SDIO_D2,
- TZ1090_PIN_SDIO_D3,
- TZ1090_PIN_SDH_WP,
- TZ1090_PIN_SDH_CD,
- TZ1090_PIN_SDH_CLK_IN,
-};
-
-static const unsigned int drive_i2s_pins[] = {
- TZ1090_PIN_CLK_OUT1,
- TZ1090_PIN_I2S_DIN,
- TZ1090_PIN_I2S_DOUT0,
- TZ1090_PIN_I2S_DOUT1,
- TZ1090_PIN_I2S_DOUT2,
- TZ1090_PIN_I2S_LRCLK_OUT,
- TZ1090_PIN_I2S_BCLK_OUT,
- TZ1090_PIN_I2S_MCLK,
-};
-
-static const unsigned int drive_scb0_pins[] = {
- TZ1090_PIN_SCB0_SCLK,
- TZ1090_PIN_SCB0_SDAT,
- TZ1090_PIN_PDM_D,
- TZ1090_PIN_PDM_C,
-};
-
-static const unsigned int drive_pdm_pins[] = {
- TZ1090_PIN_CLK_OUT0,
- TZ1090_PIN_PDM_B,
- TZ1090_PIN_PDM_A,
-};
-
-/* Pin groups each function can be muxed to */
-
-/*
- * The magic "perip" function allows otherwise non-muxing pins to be enabled in
- * peripheral mode.
- */
-static const char * const perip_groups[] = {
- /* non-muxing convenient gpio pingroups */
- "uart",
- "uart0",
- "uart1",
- "spi0",
- "spi1",
- "scb0",
- "scb1",
- "scb2",
- "i2s",
- /* individual pins not part of a pin mux group */
- "spi0_mclk",
- "spi0_cs0",
- "spi0_cs1",
- "spi0_cs2",
- "spi0_dout",
- "spi0_din",
- "spi1_mclk",
- "spi1_cs0",
- "spi1_cs1",
- "spi1_dout",
- "spi1_din",
- "uart0_rxd",
- "uart0_txd",
- "uart0_cts",
- "uart0_rts",
- "uart1_rxd",
- "uart1_txd",
- "scb0_sdat",
- "scb0_sclk",
- "scb1_sdat",
- "scb1_sclk",
- "scb2_sdat",
- "scb2_sclk",
- "i2s_mclk",
- "i2s_bclk_out",
- "i2s_lrclk_out",
- "i2s_dout0",
- "i2s_dout1",
- "i2s_dout2",
- "i2s_din",
- "pdm_a",
- "pdm_b",
- "pdm_c",
-};
-
-static const char * const sdh_sdio_groups[] = {
- "sdh",
- "sdio",
- /* sdh pins */
- "sdh_cd",
- "sdh_wp",
- "sdh_clk_in",
- /* sdio pins */
- "sdio_clk",
- "sdio_cmd",
- "sdio_d0",
- "sdio_d1",
- "sdio_d2",
- "sdio_d3",
-};
-
-static const char * const spi1_cs2_groups[] = {
- "spi1_cs2",
-};
-
-static const char * const pdm_dac_groups[] = {
- "pdm_d",
-};
-
-static const char * const usb_vbus_groups[] = {
- "spi1_cs2",
- "pdm_d",
-};
-
-static const char * const afe_groups[] = {
- "afe",
- /* afe pins */
- "tx_on",
- "rx_on",
- "pll_on",
- "pa_on",
- "rx_hp",
- "ant_sel0",
- "ant_sel1",
- "gain0",
- "gain1",
- "gain2",
- "gain3",
- "gain4",
- "gain5",
- "gain6",
- "gain7",
-};
-
-static const char * const tft_groups[] = {
- "tft",
- /* tft pins */
- "tft_red0",
- "tft_red1",
- "tft_red2",
- "tft_red3",
- "tft_red4",
- "tft_red5",
- "tft_red6",
- "tft_red7",
- "tft_green0",
- "tft_green1",
- "tft_green2",
- "tft_green3",
- "tft_green4",
- "tft_green5",
- "tft_green6",
- "tft_green7",
- "tft_blue0",
- "tft_blue1",
- "tft_blue2",
- "tft_blue3",
- "tft_blue4",
- "tft_blue5",
- "tft_blue6",
- "tft_blue7",
- "tft_vdden_gd",
- "tft_panelclk",
- "tft_blank_ls",
- "tft_vsync_ns",
- "tft_hsync_nr",
- "tft_vd12acb",
- "tft_pwrsave",
-};
-
-/* Mux functions that can be used by a mux */
-
-enum tz1090_mux {
- /* internal placeholder */
- TZ1090_MUX_NA = -1,
- /* magic per-non-muxing-GPIO-pin peripheral mode mux */
- TZ1090_MUX_PERIP,
- /* SDH/SDIO mux */
- TZ1090_MUX_SDH,
- TZ1090_MUX_SDIO,
- /* USB_VBUS muxes */
- TZ1090_MUX_SPI1_CS2,
- TZ1090_MUX_PDM_DAC,
- TZ1090_MUX_USB_VBUS,
- /* AFE mux */
- TZ1090_MUX_AFE,
- TZ1090_MUX_TS_OUT_0,
- /* EXT_DAC mux */
- TZ1090_MUX_DAC,
- TZ1090_MUX_NOT_IQADC_STB,
- TZ1090_MUX_IQDAC_STB,
- /* TFT mux */
- TZ1090_MUX_TFT,
- TZ1090_MUX_EXT_DAC,
- TZ1090_MUX_TS_OUT_1,
- TZ1090_MUX_LCD_TRACE,
- TZ1090_MUX_PHY_RINGOSC,
-};
-
-#define FUNCTION(mux, fname, group) \
- [(TZ1090_MUX_ ## mux)] = { \
- .name = #fname, \
- .groups = group##_groups, \
- .ngroups = ARRAY_SIZE(group##_groups), \
- }
-/* For intermediate functions with submuxes */
-#define NULL_FUNCTION(mux, fname) \
- [(TZ1090_MUX_ ## mux)] = { \
- .name = #fname, \
- }
-
-/* Must correlate with enum tz1090_mux */
-static const struct tz1090_function tz1090_functions[] = {
- /* FUNCTION function name pingroups */
- FUNCTION(PERIP, perip, perip),
- FUNCTION(SDH, sdh, sdh_sdio),
- FUNCTION(SDIO, sdio, sdh_sdio),
- FUNCTION(SPI1_CS2, spi1_cs2, spi1_cs2),
- FUNCTION(PDM_DAC, pdm_dac, pdm_dac),
- FUNCTION(USB_VBUS, usb_vbus, usb_vbus),
- FUNCTION(AFE, afe, afe),
- FUNCTION(TS_OUT_0, ts_out_0, afe),
- FUNCTION(DAC, ext_dac, tft),
- FUNCTION(NOT_IQADC_STB, not_iqadc_stb, tft),
- FUNCTION(IQDAC_STB, iqdac_stb, tft),
- FUNCTION(TFT, tft, tft),
- NULL_FUNCTION(EXT_DAC, _ext_dac),
- FUNCTION(TS_OUT_1, ts_out_1, tft),
- FUNCTION(LCD_TRACE, lcd_trace, tft),
- FUNCTION(PHY_RINGOSC, phy_ringosc, tft),
-};
-
-/* Sub muxes */
-
-/**
- * MUX() - Initialise a mux description.
- * @f0: Function 0 (TZ1090_MUX_ is prepended, NA for none)
- * @f1: Function 1 (TZ1090_MUX_ is prepended, NA for none)
- * @f2: Function 2 (TZ1090_MUX_ is prepended, NA for none)
- * @f3: Function 3 (TZ1090_MUX_ is prepended, NA for none)
- * @f4: Function 4 (TZ1090_MUX_ is prepended, NA for none)
- * @mux_r: Mux register (REG_PINCTRL_ is prepended)
- * @mux_b: Bit number in register that the mux field begins
- * @mux_w: Width of mux field in register
- */
-#define MUX(f0, f1, f2, f3, f4, mux_r, mux_b, mux_w) \
- { \
- .funcs = { \
- TZ1090_MUX_ ## f0, \
- TZ1090_MUX_ ## f1, \
- TZ1090_MUX_ ## f2, \
- TZ1090_MUX_ ## f3, \
- TZ1090_MUX_ ## f4, \
- }, \
- .reg = (REG_PINCTRL_ ## mux_r), \
- .bit = (mux_b), \
- .width = (mux_w), \
- }
-
-/**
- * DEFINE_SUBMUX() - Defines a submux description separate from a pin group.
- * @mux: Mux name (_submux is appended)
- * @f0: Function 0 (TZ1090_MUX_ is prepended, NA for none)
- * @f1: Function 1 (TZ1090_MUX_ is prepended, NA for none)
- * @f2: Function 2 (TZ1090_MUX_ is prepended, NA for none)
- * @f3: Function 3 (TZ1090_MUX_ is prepended, NA for none)
- * @f4: Function 4 (TZ1090_MUX_ is prepended, NA for none)
- * @mux_r: Mux register (REG_PINCTRL_ is prepended)
- * @mux_b: Bit number in register that the mux field begins
- * @mux_w: Width of mux field in register
- *
- * A sub mux is a nested mux that can be bound to a magic function number used
- * by another mux description. For example value 4 of the top level mux might
- * correspond to a function which has a submux pointed to in tz1090_submux[].
- * The outer mux can then take on any function in the top level mux or the
- * submux, and if a submux function is chosen both muxes are updated to route
- * the signal from the submux.
- *
- * The submux can be defined with DEFINE_SUBMUX and pointed to from
- * tz1090_submux[] using SUBMUX.
- */
-#define DEFINE_SUBMUX(mux, f0, f1, f2, f3, f4, mux_r, mux_b, mux_w) \
- static struct tz1090_muxdesc mux ## _submux = \
- MUX(f0, f1, f2, f3, f4, mux_r, mux_b, mux_w)
-
-/**
- * SUBMUX() - Link a submux to a function number.
- * @f: Function name (TZ1090_MUX_ is prepended)
- * @submux: Submux name (_submux is appended)
- *
- * For use in tz1090_submux[] initialisation to link an intermediate function
- * number to a particular submux description. It indicates that when the
- * function is chosen the signal is connected to the submux.
- */
-#define SUBMUX(f, submux) [(TZ1090_MUX_ ## f)] = &(submux ## _submux)
-
-/**
- * MUX_PG() - Initialise a pin group with mux control
- * @pg_name: Pin group name (stringified, _pins appended to get pins array)
- * @f0: Function 0 (TZ1090_MUX_ is prepended, NA for none)
- * @f1: Function 1 (TZ1090_MUX_ is prepended, NA for none)
- * @f2: Function 2 (TZ1090_MUX_ is prepended, NA for none)
- * @f3: Function 3 (TZ1090_MUX_ is prepended, NA for none)
- * @f4: Function 4 (TZ1090_MUX_ is prepended, NA for none)
- * @mux_r: Mux register (REG_PINCTRL_ is prepended)
- * @mux_b: Bit number in register that the mux field begins
- * @mux_w: Width of mux field in register
- */
-#define MUX_PG(pg_name, f0, f1, f2, f3, f4, \
- mux_r, mux_b, mux_w) \
- { \
- .name = #pg_name, \
- .pins = pg_name##_pins, \
- .npins = ARRAY_SIZE(pg_name##_pins), \
- .mux = MUX(f0, f1, f2, f3, f4, \
- mux_r, mux_b, mux_w), \
- }
-
-/**
- * SIMPLE_PG() - Initialise a simple convenience pin group
- * @pg_name: Pin group name (stringified, _pins appended to get pins array)
- *
- * A simple pin group is simply used for binding pins together so they can be
- * referred to by a single name instead of having to list every pin
- * individually.
- */
-#define SIMPLE_PG(pg_name) \
- { \
- .name = #pg_name, \
- .pins = pg_name##_pins, \
- .npins = ARRAY_SIZE(pg_name##_pins), \
- }
-
-/**
- * DRV_PG() - Initialise a pin group with drive control
- * @pg_name: Pin group name (stringified, _pins appended to get pins array)
- * @slw_b: Slew register bit.
- * The same bit is used for Schmitt, and Drive (*2).
- */
-#define DRV_PG(pg_name, slw_b) \
- { \
- .name = #pg_name, \
- .pins = pg_name##_pins, \
- .npins = ARRAY_SIZE(pg_name##_pins), \
- .drv = true, \
- .slw_bit = (slw_b), \
- }
-
-/*
- * Define main muxing pin groups
- */
-
-/* submuxes */
-
-/* name f0, f1, f2, f3, f4, mux r/b/w */
-DEFINE_SUBMUX(ext_dac, DAC, NOT_IQADC_STB, IQDAC_STB, NA, NA, IF_CTL, 6, 2);
-
-/* bind submuxes to internal functions */
-static struct tz1090_muxdesc *tz1090_submux[] = {
- SUBMUX(EXT_DAC, ext_dac),
-};
-
-/*
- * These are the pin mux groups. Pin muxing can be enabled and disabled for each
- * pin individually so these groups are internal. The mapping of pins to pin mux
- * group is below (tz1090_mux_pins).
- */
-static struct tz1090_pingroup tz1090_mux_groups[] = {
- /* Muxing pin groups */
- /* pg_name, f0, f1, f2, f3, f4, mux r/b/w */
- MUX_PG(sdh, SDH, SDIO, NA, NA, NA, IF_CTL, 20, 2),
- MUX_PG(sdio, SDIO, SDH, NA, NA, NA, IF_CTL, 16, 2),
- MUX_PG(spi1_cs2, SPI1_CS2, USB_VBUS, NA, NA, NA, IF_CTL, 10, 2),
- MUX_PG(pdm_d, PDM_DAC, USB_VBUS, NA, NA, NA, IF_CTL, 8, 2),
- MUX_PG(afe, AFE, TS_OUT_0, NA, NA, NA, IF_CTL, 4, 2),
- MUX_PG(tft, TFT, EXT_DAC, TS_OUT_1, LCD_TRACE, PHY_RINGOSC, IF_CTL, 0, 3),
-};
-
-/*
- * This is the mapping from GPIO pins to pin mux groups in tz1090_mux_groups[].
- * Pins which aren't muxable to multiple peripherals are set to
- * TZ1090_MUX_GROUP_MAX to enable the "perip" function to enable/disable
- * peripheral control of the pin.
- *
- * This array is initialised in tz1090_init_mux_pins().
- */
-static u8 tz1090_mux_pins[NUM_GPIOS];
-
-/* TZ1090_MUX_GROUP_MAX is used in tz1090_mux_pins[] for non-muxing pins */
-#define TZ1090_MUX_GROUP_MAX ARRAY_SIZE(tz1090_mux_groups)
-
-/**
- * tz1090_init_mux_pins() - Initialise GPIO pin to mux group mapping.
- *
- * Initialises the tz1090_mux_pins[] array to be the inverse of the pin lists in
- * each pin mux group in tz1090_mux_groups[].
- *
- * It is assumed that no pin mux groups overlap (share pins).
- */
-static void __init tz1090_init_mux_pins(void)
-{
- unsigned int g, p;
- const struct tz1090_pingroup *grp;
- const unsigned int *pin;
-
- for (p = 0; p < NUM_GPIOS; ++p)
- tz1090_mux_pins[p] = TZ1090_MUX_GROUP_MAX;
-
- grp = tz1090_mux_groups;
- for (g = 0, grp = tz1090_mux_groups;
- g < ARRAY_SIZE(tz1090_mux_groups); ++g, ++grp)
- for (pin = grp->pins, p = 0; p < grp->npins; ++p, ++pin)
- tz1090_mux_pins[*pin] = g;
-}
-
-/*
- * These are the externally visible pin groups. Some of them allow group control
- * of drive configuration. Some are just simple convenience pingroups. All the
- * internal pin mux groups in tz1090_mux_groups[] are mirrored here with the
- * same pins.
- * Pseudo pin groups follow in the group numbers after this array for each GPIO
- * pin. Any group used for muxing must have all pins belonging to the same pin
- * mux group.
- */
-static struct tz1090_pingroup tz1090_groups[] = {
- /* Pin groups with drive control (with no out of place pins) */
- /* pg_name, slw/schmitt/drv b */
- DRV_PG(jtag, 11 /* 11, 22 */),
- DRV_PG(tft, 10 /* 10, 20 */),
- DRV_PG(scb2, 9 /* 9, 18 */),
- DRV_PG(spi0, 7 /* 7, 14 */),
- DRV_PG(uart, 5 /* 5, 10 */),
- DRV_PG(scb1, 4 /* 4, 8 */),
- DRV_PG(spi1, 3 /* 3, 6 */),
- DRV_PG(afe, 0 /* 0, 0 */),
-
- /*
- * Drive specific pin groups (with odd combinations of pins which makes
- * the pin group naming somewhat arbitrary)
- */
- /* pg_name, slw/schmitt/drv b */
- DRV_PG(drive_sdio, 8 /* 8, 16 */), /* sdio_* + sdh_* */
- DRV_PG(drive_i2s, 6 /* 6, 12 */), /* i2s_* + clk_out1 */
- DRV_PG(drive_scb0, 2 /* 2, 4 */), /* scb0_* + pdm_{c,d} */
- DRV_PG(drive_pdm, 1 /* 1, 2 */), /* pdm_{a,b} + clk_out0 */
-
- /* Convenience pin groups */
- /* pg_name */
- SIMPLE_PG(uart0),
- SIMPLE_PG(uart1),
- SIMPLE_PG(scb0),
- SIMPLE_PG(i2s),
- SIMPLE_PG(sdh),
- SIMPLE_PG(sdio),
-
- /* pseudo-pingroups for each GPIO pin follow */
-};
-
-/**
- * struct tz1090_pmx - Private pinctrl data
- * @dev: Platform device
- * @pctl: Pin control device
- * @regs: Register region
- * @lock: Lock protecting coherency of pin_en, gpio_en, and SELECT regs
- * @pin_en: Pins that have been enabled (32 pins packed into each element)
- * @gpio_en: GPIOs that have been enabled (32 pins packed into each element)
- */
-struct tz1090_pmx {
- struct device *dev;
- struct pinctrl_dev *pctl;
- void __iomem *regs;
- spinlock_t lock;
- u32 pin_en[3];
- u32 gpio_en[3];
-};
-
-static inline u32 pmx_read(struct tz1090_pmx *pmx, u32 reg)
-{
- return ioread32(pmx->regs + reg);
-}
-
-static inline void pmx_write(struct tz1090_pmx *pmx, u32 val, u32 reg)
-{
- iowrite32(val, pmx->regs + reg);
-}
-
-/*
- * Pin control operations
- */
-
-/* each GPIO pin has it's own pseudo pingroup containing only itself */
-
-static int tz1090_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
-{
- return ARRAY_SIZE(tz1090_groups) + NUM_GPIOS;
-}
-
-static const char *tz1090_pinctrl_get_group_name(struct pinctrl_dev *pctldev,
- unsigned int group)
-{
- if (group < ARRAY_SIZE(tz1090_groups)) {
- /* normal pingroup */
- return tz1090_groups[group].name;
- } else {
- /* individual gpio pin pseudo-pingroup */
- unsigned int pin = group - ARRAY_SIZE(tz1090_groups);
- return tz1090_pins[pin].name;
- }
-}
-
-static int tz1090_pinctrl_get_group_pins(struct pinctrl_dev *pctldev,
- unsigned int group,
- const unsigned int **pins,
- unsigned int *num_pins)
-{
- if (group < ARRAY_SIZE(tz1090_groups)) {
- /* normal pingroup */
- *pins = tz1090_groups[group].pins;
- *num_pins = tz1090_groups[group].npins;
- } else {
- /* individual gpio pin pseudo-pingroup */
- unsigned int pin = group - ARRAY_SIZE(tz1090_groups);
- *pins = &tz1090_pins[pin].number;
- *num_pins = 1;
- }
-
- return 0;
-}
-
-#ifdef CONFIG_DEBUG_FS
-static void tz1090_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev,
- struct seq_file *s,
- unsigned int offset)
-{
- seq_printf(s, " %s", dev_name(pctldev->dev));
-}
-#endif
-
-static int reserve_map(struct device *dev, struct pinctrl_map **map,
- unsigned int *reserved_maps, unsigned int *num_maps,
- unsigned int reserve)
-{
- unsigned int old_num = *reserved_maps;
- unsigned int new_num = *num_maps + reserve;
- struct pinctrl_map *new_map;
-
- if (old_num >= new_num)
- return 0;
-
- new_map = krealloc(*map, sizeof(*new_map) * new_num, GFP_KERNEL);
- if (!new_map) {
- dev_err(dev, "krealloc(map) failed\n");
- return -ENOMEM;
- }
-
- memset(new_map + old_num, 0, (new_num - old_num) * sizeof(*new_map));
-
- *map = new_map;
- *reserved_maps = new_num;
-
- return 0;
-}
-
-static int add_map_mux(struct pinctrl_map **map, unsigned int *reserved_maps,
- unsigned int *num_maps, const char *group,
- const char *function)
-{
- if (WARN_ON(*num_maps == *reserved_maps))
- return -ENOSPC;
-
- (*map)[*num_maps].type = PIN_MAP_TYPE_MUX_GROUP;
- (*map)[*num_maps].data.mux.group = group;
- (*map)[*num_maps].data.mux.function = function;
- (*num_maps)++;
-
- return 0;
-}
-
-static int add_map_configs(struct device *dev,
- struct pinctrl_map **map,
- unsigned int *reserved_maps, unsigned int *num_maps,
- const char *group, unsigned long *configs,
- unsigned int num_configs)
-{
- unsigned long *dup_configs;
-
- if (WARN_ON(*num_maps == *reserved_maps))
- return -ENOSPC;
-
- dup_configs = kmemdup(configs, num_configs * sizeof(*dup_configs),
- GFP_KERNEL);
- if (!dup_configs)
- return -ENOMEM;
-
- (*map)[*num_maps].type = PIN_MAP_TYPE_CONFIGS_GROUP;
- (*map)[*num_maps].data.configs.group_or_pin = group;
- (*map)[*num_maps].data.configs.configs = dup_configs;
- (*map)[*num_maps].data.configs.num_configs = num_configs;
- (*num_maps)++;
-
- return 0;
-}
-
-static void tz1090_pinctrl_dt_free_map(struct pinctrl_dev *pctldev,
- struct pinctrl_map *map,
- unsigned int num_maps)
-{
- int i;
-
- for (i = 0; i < num_maps; i++)
- if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP)
- kfree(map[i].data.configs.configs);
-
- kfree(map);
-}
-
-static int tz1090_pinctrl_dt_subnode_to_map(struct device *dev,
- struct device_node *np,
- struct pinctrl_map **map,
- unsigned int *reserved_maps,
- unsigned int *num_maps)
-{
- int ret;
- const char *function;
- unsigned long *configs = NULL;
- unsigned int num_configs = 0;
- unsigned int reserve;
- struct property *prop;
- const char *group;
-
- ret = of_property_read_string(np, "tz1090,function", &function);
- if (ret < 0) {
- /* EINVAL=missing, which is fine since it's optional */
- if (ret != -EINVAL)
- dev_err(dev, "could not parse property function\n");
- function = NULL;
- }
-
- ret = pinconf_generic_parse_dt_config(np, NULL, &configs, &num_configs);
- if (ret)
- return ret;
-
- reserve = 0;
- if (function != NULL)
- reserve++;
- if (num_configs)
- reserve++;
- ret = of_property_count_strings(np, "tz1090,pins");
- if (ret < 0) {
- dev_err(dev, "could not parse property pins\n");
- goto exit;
- }
- reserve *= ret;
-
- ret = reserve_map(dev, map, reserved_maps, num_maps, reserve);
- if (ret < 0)
- goto exit;
-
- of_property_for_each_string(np, "tz1090,pins", prop, group) {
- if (function) {
- ret = add_map_mux(map, reserved_maps, num_maps,
- group, function);
- if (ret < 0)
- goto exit;
- }
-
- if (num_configs) {
- ret = add_map_configs(dev, map, reserved_maps,
- num_maps, group, configs,
- num_configs);
- if (ret < 0)
- goto exit;
- }
- }
-
- ret = 0;
-
-exit:
- kfree(configs);
- return ret;
-}
-
-static int tz1090_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev,
- struct device_node *np_config,
- struct pinctrl_map **map,
- unsigned int *num_maps)
-{
- unsigned int reserved_maps;
- struct device_node *np;
- int ret;
-
- reserved_maps = 0;
- *map = NULL;
- *num_maps = 0;
-
- for_each_child_of_node(np_config, np) {
- ret = tz1090_pinctrl_dt_subnode_to_map(pctldev->dev, np, map,
- &reserved_maps,
- num_maps);
- if (ret < 0) {
- tz1090_pinctrl_dt_free_map(pctldev, *map, *num_maps);
- return ret;
- }
- }
-
- return 0;
-}
-
-static const struct pinctrl_ops tz1090_pinctrl_ops = {
- .get_groups_count = tz1090_pinctrl_get_groups_count,
- .get_group_name = tz1090_pinctrl_get_group_name,
- .get_group_pins = tz1090_pinctrl_get_group_pins,
-#ifdef CONFIG_DEBUG_FS
- .pin_dbg_show = tz1090_pinctrl_pin_dbg_show,
-#endif
- .dt_node_to_map = tz1090_pinctrl_dt_node_to_map,
- .dt_free_map = tz1090_pinctrl_dt_free_map,
-};
-
-/*
- * Pin mux operations
- */
-
-static int tz1090_pinctrl_get_funcs_count(struct pinctrl_dev *pctldev)
-{
- return ARRAY_SIZE(tz1090_functions);
-}
-
-static const char *tz1090_pinctrl_get_func_name(struct pinctrl_dev *pctldev,
- unsigned int function)
-{
- return tz1090_functions[function].name;
-}
-
-static int tz1090_pinctrl_get_func_groups(struct pinctrl_dev *pctldev,
- unsigned int function,
- const char * const **groups,
- unsigned int * const num_groups)
-{
- /* pingroup functions */
- *groups = tz1090_functions[function].groups;
- *num_groups = tz1090_functions[function].ngroups;
- return 0;
-}
-
-/**
- * tz1090_pinctrl_select() - update bit in SELECT register
- * @pmx: Pinmux data
- * @pin: Pin number (must be within GPIO range)
- */
-static void tz1090_pinctrl_select(struct tz1090_pmx *pmx,
- unsigned int pin)
-{
- u32 reg, reg_shift, select, val;
- unsigned int pmx_index, pmx_shift;
- unsigned long flags;
-
- /* uses base 32 instead of base 30 */
- pmx_index = pin >> 5;
- pmx_shift = pin & 0x1f;
-
- /* select = !perip || gpio */
- select = ((~pmx->pin_en[pmx_index] |
- pmx->gpio_en[pmx_index]) >> pmx_shift) & 1;
-
- /* find register and bit offset (base 30) */
- reg = REG_PINCTRL_SELECT + 4*(pin / 30);
- reg_shift = pin % 30;
-
- /* modify gpio select bit */
- __global_lock2(flags);
- val = pmx_read(pmx, reg);
- val &= ~BIT(reg_shift);
- val |= select << reg_shift;
- pmx_write(pmx, val, reg);
- __global_unlock2(flags);
-}
-
-/**
- * tz1090_pinctrl_gpio_select() - enable/disable GPIO usage for a pin
- * @pmx: Pinmux data
- * @pin: Pin number
- * @gpio_select: true to enable pin as GPIO,
- * false to leave control to whatever function is enabled
- *
- * Records that GPIO usage is enabled/disabled so that enabling a function
- * doesn't override the SELECT register bit.
- */
-static void tz1090_pinctrl_gpio_select(struct tz1090_pmx *pmx,
- unsigned int pin,
- bool gpio_select)
-{
- unsigned int index, shift;
- u32 gpio_en;
-
- if (pin >= NUM_GPIOS)
- return;
-
- /* uses base 32 instead of base 30 */
- index = pin >> 5;
- shift = pin & 0x1f;
-
- spin_lock(&pmx->lock);
-
- /* keep a record whether gpio is selected */
- gpio_en = pmx->gpio_en[index];
- gpio_en &= ~BIT(shift);
- if (gpio_select)
- gpio_en |= BIT(shift);
- pmx->gpio_en[index] = gpio_en;
-
- /* update the select bit */
- tz1090_pinctrl_select(pmx, pin);
-
- spin_unlock(&pmx->lock);
-}
-
-/**
- * tz1090_pinctrl_perip_select() - enable/disable peripheral interface for a pin
- * @pmx: Pinmux data
- * @pin: Pin number
- * @perip_select: true to enable peripheral interface when not GPIO,
- * false to leave pin in GPIO mode
- *
- * Records that peripheral usage is enabled/disabled so that SELECT register can
- * be set appropriately when GPIO is disabled.
- */
-static void tz1090_pinctrl_perip_select(struct tz1090_pmx *pmx,
- unsigned int pin,
- bool perip_select)
-{
- unsigned int index, shift;
- u32 pin_en;
-
- if (pin >= NUM_GPIOS)
- return;
-
- /* uses base 32 instead of base 30 */
- index = pin >> 5;
- shift = pin & 0x1f;
-
- spin_lock(&pmx->lock);
-
- /* keep a record whether peripheral is selected */
- pin_en = pmx->pin_en[index];
- pin_en &= ~BIT(shift);
- if (perip_select)
- pin_en |= BIT(shift);
- pmx->pin_en[index] = pin_en;
-
- /* update the select bit */
- tz1090_pinctrl_select(pmx, pin);
-
- spin_unlock(&pmx->lock);
-}
-
-/**
- * tz1090_pinctrl_enable_mux() - Switch a pin mux group to a function.
- * @pmx: Pinmux data
- * @desc: Pinmux description
- * @function: Function to switch to
- *
- * Enable a particular function on a pin mux group. Since pin mux descriptions
- * are nested this function is recursive.
- */
-static int tz1090_pinctrl_enable_mux(struct tz1090_pmx *pmx,
- const struct tz1090_muxdesc *desc,
- unsigned int function)
-{
- const int *fit;
- unsigned long flags;
- int mux;
- unsigned int func, ret;
- u32 reg, mask;
-
- /* find the mux value for this function, searching recursively */
- for (mux = 0, fit = desc->funcs;
- mux < ARRAY_SIZE(desc->funcs); ++mux, ++fit) {
- func = *fit;
- if (func == function)
- goto found_mux;
-
- /* maybe it's a sub-mux */
- if (func < ARRAY_SIZE(tz1090_submux) && tz1090_submux[func]) {
- ret = tz1090_pinctrl_enable_mux(pmx,
- tz1090_submux[func],
- function);
- if (!ret)
- goto found_mux;
- }
- }
-
- return -EINVAL;
-found_mux:
-
- /* Set up the mux */
- if (desc->width) {
- mask = (BIT(desc->width) - 1) << desc->bit;
- __global_lock2(flags);
- reg = pmx_read(pmx, desc->reg);
- reg &= ~mask;
- reg |= (mux << desc->bit) & mask;
- pmx_write(pmx, reg, desc->reg);
- __global_unlock2(flags);
- }
-
- return 0;
-}
-
-/**
- * tz1090_pinctrl_enable() - Enable a function on a pin group.
- * @pctldev: Pin control data
- * @function: Function index to enable
- * @group: Group index to enable
- *
- * Enable a particular function on a group of pins. The per GPIO pin pseudo pin
- * groups can be used (in which case the pin will be enabled in peripheral mode
- * and if it belongs to a pin mux group the mux will be switched if it isn't
- * already in use. Some convenience pin groups can also be used in which case
- * the effect is the same as enabling the function on each individual pin in the
- * group.
- */
-static int tz1090_pinctrl_set_mux(struct pinctrl_dev *pctldev,
- unsigned int function, unsigned int group)
-{
- struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- struct tz1090_pingroup *grp;
- int ret;
- unsigned int pin_num, mux_group, i, npins;
- const unsigned int *pins;
-
- /* group of pins? */
- if (group < ARRAY_SIZE(tz1090_groups)) {
- grp = &tz1090_groups[group];
- npins = grp->npins;
- pins = grp->pins;
- /*
- * All pins in the group must belong to the same mux group,
- * which allows us to just use the mux group of the first pin.
- * By explicitly listing permitted pingroups for each function
- * the pinmux core should ensure this is always the case.
- */
- } else {
- pin_num = group - ARRAY_SIZE(tz1090_groups);
- npins = 1;
- pins = &pin_num;
- }
- mux_group = tz1090_mux_pins[*pins];
-
- /* no mux group, but can still be individually muxed to peripheral */
- if (mux_group >= TZ1090_MUX_GROUP_MAX) {
- if (function == TZ1090_MUX_PERIP)
- goto mux_pins;
- return -EINVAL;
- }
-
- /* mux group already set to a different function? */
- grp = &tz1090_mux_groups[mux_group];
- if (grp->func_count && grp->func != function) {
- dev_err(pctldev->dev,
- "%s: can't mux pin(s) to '%s', group already muxed to '%s'\n",
- __func__, tz1090_functions[function].name,
- tz1090_functions[grp->func].name);
- return -EBUSY;
- }
-
- dev_dbg(pctldev->dev, "%s: muxing %u pin(s) in '%s' to '%s'\n",
- __func__, npins, grp->name, tz1090_functions[function].name);
-
- /* if first pin in mux group to be enabled, enable the group mux */
- if (!grp->func_count) {
- grp->func = function;
- ret = tz1090_pinctrl_enable_mux(pmx, &grp->mux, function);
- if (ret)
- return ret;
- }
- /* add pins to ref count and mux individually to peripheral */
- grp->func_count += npins;
-mux_pins:
- for (i = 0; i < npins; ++i)
- tz1090_pinctrl_perip_select(pmx, pins[i], true);
-
- return 0;
-}
-
-/**
- * tz1090_pinctrl_gpio_request_enable() - Put pin in GPIO mode.
- * @pctldev: Pin control data
- * @range: GPIO range
- * @pin: Pin number
- *
- * Puts a particular pin into GPIO mode, disabling peripheral control until it's
- * disabled again.
- */
-static int tz1090_pinctrl_gpio_request_enable(struct pinctrl_dev *pctldev,
- struct pinctrl_gpio_range *range,
- unsigned int pin)
-{
- struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- tz1090_pinctrl_gpio_select(pmx, pin, true);
- return 0;
-}
-
-/**
- * tz1090_pinctrl_gpio_disable_free() - Take pin out of GPIO mode.
- * @pctldev: Pin control data
- * @range: GPIO range
- * @pin: Pin number
- *
- * Take a particular pin out of GPIO mode. If the pin is enabled for a
- * peripheral it will return to peripheral mode.
- */
-static void tz1090_pinctrl_gpio_disable_free(struct pinctrl_dev *pctldev,
- struct pinctrl_gpio_range *range,
- unsigned int pin)
-{
- struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- tz1090_pinctrl_gpio_select(pmx, pin, false);
-}
-
-static const struct pinmux_ops tz1090_pinmux_ops = {
- .get_functions_count = tz1090_pinctrl_get_funcs_count,
- .get_function_name = tz1090_pinctrl_get_func_name,
- .get_function_groups = tz1090_pinctrl_get_func_groups,
- .set_mux = tz1090_pinctrl_set_mux,
- .gpio_request_enable = tz1090_pinctrl_gpio_request_enable,
- .gpio_disable_free = tz1090_pinctrl_gpio_disable_free,
-};
-
-/*
- * Pin config operations
- */
-
-struct tz1090_pinconf_pullup {
- unsigned char index;
- unsigned char shift;
-};
-
-/* The mapping of pin to pull up/down register index and shift */
-static struct tz1090_pinconf_pullup tz1090_pinconf_pullup[] = {
- {5, 22}, /* 0 - TZ1090_PIN_SDIO_CLK */
- {0, 14}, /* 1 - TZ1090_PIN_SDIO_CMD */
- {0, 6}, /* 2 - TZ1090_PIN_SDIO_D0 */
- {0, 8}, /* 3 - TZ1090_PIN_SDIO_D1 */
- {0, 10}, /* 4 - TZ1090_PIN_SDIO_D2 */
- {0, 12}, /* 5 - TZ1090_PIN_SDIO_D3 */
- {0, 2}, /* 6 - TZ1090_PIN_SDH_CD */
- {0, 4}, /* 7 - TZ1090_PIN_SDH_WP */
- {0, 16}, /* 8 - TZ1090_PIN_SPI0_MCLK */
- {0, 18}, /* 9 - TZ1090_PIN_SPI0_CS0 */
- {0, 20}, /* 10 - TZ1090_PIN_SPI0_CS1 */
- {0, 22}, /* 11 - TZ1090_PIN_SPI0_CS2 */
- {0, 24}, /* 12 - TZ1090_PIN_SPI0_DOUT */
- {0, 26}, /* 13 - TZ1090_PIN_SPI0_DIN */
- {0, 28}, /* 14 - TZ1090_PIN_SPI1_MCLK */
- {0, 30}, /* 15 - TZ1090_PIN_SPI1_CS0 */
- {1, 0}, /* 16 - TZ1090_PIN_SPI1_CS1 */
- {1, 2}, /* 17 - TZ1090_PIN_SPI1_CS2 */
- {1, 4}, /* 18 - TZ1090_PIN_SPI1_DOUT */
- {1, 6}, /* 19 - TZ1090_PIN_SPI1_DIN */
- {1, 8}, /* 20 - TZ1090_PIN_UART0_RXD */
- {1, 10}, /* 21 - TZ1090_PIN_UART0_TXD */
- {1, 12}, /* 22 - TZ1090_PIN_UART0_CTS */
- {1, 14}, /* 23 - TZ1090_PIN_UART0_RTS */
- {1, 16}, /* 24 - TZ1090_PIN_UART1_RXD */
- {1, 18}, /* 25 - TZ1090_PIN_UART1_TXD */
- {1, 20}, /* 26 - TZ1090_PIN_SCB0_SDAT */
- {1, 22}, /* 27 - TZ1090_PIN_SCB0_SCLK */
- {1, 24}, /* 28 - TZ1090_PIN_SCB1_SDAT */
- {1, 26}, /* 29 - TZ1090_PIN_SCB1_SCLK */
-
- {1, 28}, /* 30 - TZ1090_PIN_SCB2_SDAT */
- {1, 30}, /* 31 - TZ1090_PIN_SCB2_SCLK */
- {2, 0}, /* 32 - TZ1090_PIN_I2S_MCLK */
- {2, 2}, /* 33 - TZ1090_PIN_I2S_BCLK_OUT */
- {2, 4}, /* 34 - TZ1090_PIN_I2S_LRCLK_OUT */
- {2, 6}, /* 35 - TZ1090_PIN_I2S_DOUT0 */
- {2, 8}, /* 36 - TZ1090_PIN_I2S_DOUT1 */
- {2, 10}, /* 37 - TZ1090_PIN_I2S_DOUT2 */
- {2, 12}, /* 38 - TZ1090_PIN_I2S_DIN */
- {4, 12}, /* 39 - TZ1090_PIN_PDM_A */
- {4, 14}, /* 40 - TZ1090_PIN_PDM_B */
- {4, 18}, /* 41 - TZ1090_PIN_PDM_C */
- {4, 20}, /* 42 - TZ1090_PIN_PDM_D */
- {2, 14}, /* 43 - TZ1090_PIN_TFT_RED0 */
- {2, 16}, /* 44 - TZ1090_PIN_TFT_RED1 */
- {2, 18}, /* 45 - TZ1090_PIN_TFT_RED2 */
- {2, 20}, /* 46 - TZ1090_PIN_TFT_RED3 */
- {2, 22}, /* 47 - TZ1090_PIN_TFT_RED4 */
- {2, 24}, /* 48 - TZ1090_PIN_TFT_RED5 */
- {2, 26}, /* 49 - TZ1090_PIN_TFT_RED6 */
- {2, 28}, /* 50 - TZ1090_PIN_TFT_RED7 */
- {2, 30}, /* 51 - TZ1090_PIN_TFT_GREEN0 */
- {3, 0}, /* 52 - TZ1090_PIN_TFT_GREEN1 */
- {3, 2}, /* 53 - TZ1090_PIN_TFT_GREEN2 */
- {3, 4}, /* 54 - TZ1090_PIN_TFT_GREEN3 */
- {3, 6}, /* 55 - TZ1090_PIN_TFT_GREEN4 */
- {3, 8}, /* 56 - TZ1090_PIN_TFT_GREEN5 */
- {3, 10}, /* 57 - TZ1090_PIN_TFT_GREEN6 */
- {3, 12}, /* 58 - TZ1090_PIN_TFT_GREEN7 */
- {3, 14}, /* 59 - TZ1090_PIN_TFT_BLUE0 */
-
- {3, 16}, /* 60 - TZ1090_PIN_TFT_BLUE1 */
- {3, 18}, /* 61 - TZ1090_PIN_TFT_BLUE2 */
- {3, 20}, /* 62 - TZ1090_PIN_TFT_BLUE3 */
- {3, 22}, /* 63 - TZ1090_PIN_TFT_BLUE4 */
- {3, 24}, /* 64 - TZ1090_PIN_TFT_BLUE5 */
- {3, 26}, /* 65 - TZ1090_PIN_TFT_BLUE6 */
- {3, 28}, /* 66 - TZ1090_PIN_TFT_BLUE7 */
- {3, 30}, /* 67 - TZ1090_PIN_TFT_VDDEN_GD */
- {4, 0}, /* 68 - TZ1090_PIN_TFT_PANELCLK */
- {4, 2}, /* 69 - TZ1090_PIN_TFT_BLANK_LS */
- {4, 4}, /* 70 - TZ1090_PIN_TFT_VSYNC_NS */
- {4, 6}, /* 71 - TZ1090_PIN_TFT_HSYNC_NR */
- {4, 8}, /* 72 - TZ1090_PIN_TFT_VD12ACB */
- {4, 10}, /* 73 - TZ1090_PIN_TFT_PWRSAVE */
- {4, 24}, /* 74 - TZ1090_PIN_TX_ON */
- {4, 26}, /* 75 - TZ1090_PIN_RX_ON */
- {4, 28}, /* 76 - TZ1090_PIN_PLL_ON */
- {4, 30}, /* 77 - TZ1090_PIN_PA_ON */
- {5, 0}, /* 78 - TZ1090_PIN_RX_HP */
- {5, 6}, /* 79 - TZ1090_PIN_GAIN0 */
- {5, 8}, /* 80 - TZ1090_PIN_GAIN1 */
- {5, 10}, /* 81 - TZ1090_PIN_GAIN2 */
- {5, 12}, /* 82 - TZ1090_PIN_GAIN3 */
- {5, 14}, /* 83 - TZ1090_PIN_GAIN4 */
- {5, 16}, /* 84 - TZ1090_PIN_GAIN5 */
- {5, 18}, /* 85 - TZ1090_PIN_GAIN6 */
- {5, 20}, /* 86 - TZ1090_PIN_GAIN7 */
- {5, 2}, /* 87 - TZ1090_PIN_ANT_SEL0 */
- {5, 4}, /* 88 - TZ1090_PIN_ANT_SEL1 */
- {0, 0}, /* 89 - TZ1090_PIN_SDH_CLK_IN */
-
- {5, 24}, /* 90 - TZ1090_PIN_TCK */
- {5, 26}, /* 91 - TZ1090_PIN_TRST */
- {5, 28}, /* 92 - TZ1090_PIN_TDI */
- {5, 30}, /* 93 - TZ1090_PIN_TDO */
- {6, 0}, /* 94 - TZ1090_PIN_TMS */
- {4, 16}, /* 95 - TZ1090_PIN_CLK_OUT0 */
- {4, 22}, /* 96 - TZ1090_PIN_CLK_OUT1 */
-};
-
-static int tz1090_pinconf_reg(struct pinctrl_dev *pctldev,
- unsigned int pin,
- enum pin_config_param param,
- bool report_err,
- u32 *reg, u32 *width, u32 *mask, u32 *shift,
- u32 *val)
-{
- struct tz1090_pinconf_pullup *pu;
-
- /* All supported pins have controllable input bias */
- switch (param) {
- case PIN_CONFIG_BIAS_DISABLE:
- case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
- *val = REG_PU_PD_TRISTATE;
- break;
- case PIN_CONFIG_BIAS_PULL_UP:
- *val = REG_PU_PD_UP;
- break;
- case PIN_CONFIG_BIAS_PULL_DOWN:
- *val = REG_PU_PD_DOWN;
- break;
- case PIN_CONFIG_BIAS_BUS_HOLD:
- *val = REG_PU_PD_REPEATER;
- break;
- default:
- return -ENOTSUPP;
- }
-
- /* Only input bias parameters supported */
- pu = &tz1090_pinconf_pullup[pin];
- *reg = REG_PINCTRL_PU_PD + 4*pu->index;
- *shift = pu->shift;
- *width = 2;
-
- /* Calculate field information */
- *mask = (BIT(*width) - 1) << *shift;
-
- return 0;
-}
-
-static int tz1090_pinconf_get(struct pinctrl_dev *pctldev,
- unsigned int pin, unsigned long *config)
-{
- struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- enum pin_config_param param = pinconf_to_config_param(*config);
- int ret;
- u32 reg, width, mask, shift, val, tmp, arg;
-
- /* Get register information */
- ret = tz1090_pinconf_reg(pctldev, pin, param, true,
- &reg, &width, &mask, &shift, &val);
- if (ret < 0)
- return ret;
-
- /* Extract field from register */
- tmp = pmx_read(pmx, reg);
- arg = ((tmp & mask) >> shift) == val;
-
- /* Config not active */
- if (!arg)
- return -EINVAL;
-
- /* And pack config */
- *config = pinconf_to_config_packed(param, arg);
-
- return 0;
-}
-
-static int tz1090_pinconf_set(struct pinctrl_dev *pctldev,
- unsigned int pin, unsigned long *configs,
- unsigned num_configs)
-{
- struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- enum pin_config_param param;
- unsigned int arg;
- int ret;
- u32 reg, width, mask, shift, val, tmp;
- unsigned long flags;
- int i;
-
- for (i = 0; i < num_configs; i++) {
- param = pinconf_to_config_param(configs[i]);
- arg = pinconf_to_config_argument(configs[i]);
-
- dev_dbg(pctldev->dev, "%s(pin=%s, config=%#lx)\n",
- __func__, tz1090_pins[pin].name, configs[i]);
-
- /* Get register information */
- ret = tz1090_pinconf_reg(pctldev, pin, param, true,
- &reg, &width, &mask, &shift, &val);
- if (ret < 0)
- return ret;
-
- /* Unpack argument and range check it */
- if (arg > 1) {
- dev_dbg(pctldev->dev, "%s: arg %u out of range\n",
- __func__, arg);
- return -EINVAL;
- }
-
- /* Write register field */
- __global_lock2(flags);
- tmp = pmx_read(pmx, reg);
- tmp &= ~mask;
- if (arg)
- tmp |= val << shift;
- pmx_write(pmx, tmp, reg);
- __global_unlock2(flags);
- } /* for each config */
-
- return 0;
-}
-
-static const int tz1090_boolean_map[] = {
- [0] = -EINVAL,
- [1] = 1,
-};
-
-static const int tz1090_dr_map[] = {
- [REG_DR_2mA] = 2,
- [REG_DR_4mA] = 4,
- [REG_DR_8mA] = 8,
- [REG_DR_12mA] = 12,
-};
-
-static int tz1090_pinconf_group_reg(struct pinctrl_dev *pctldev,
- const struct tz1090_pingroup *g,
- enum pin_config_param param,
- bool report_err,
- u32 *reg, u32 *width, u32 *mask, u32 *shift,
- const int **map)
-{
- /* Drive configuration applies in groups, but not to all groups. */
- if (!g->drv) {
- if (report_err)
- dev_dbg(pctldev->dev,
- "%s: group %s has no drive control\n",
- __func__, g->name);
- return -ENOTSUPP;
- }
-
- /* Find information about drive parameter's register */
- switch (param) {
- case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
- *reg = REG_PINCTRL_SCHMITT;
- *width = 1;
- *map = tz1090_boolean_map;
- break;
- case PIN_CONFIG_DRIVE_STRENGTH:
- *reg = REG_PINCTRL_DR;
- *width = 2;
- *map = tz1090_dr_map;
- break;
- default:
- return -ENOTSUPP;
- }
-
- /* Calculate field information */
- *shift = g->slw_bit * *width;
- *mask = (BIT(*width) - 1) << *shift;
-
- return 0;
-}
-
-static int tz1090_pinconf_group_get(struct pinctrl_dev *pctldev,
- unsigned int group,
- unsigned long *config)
-{
- struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- const struct tz1090_pingroup *g;
- enum pin_config_param param = pinconf_to_config_param(*config);
- int ret, arg;
- unsigned int pin;
- u32 reg, width, mask, shift, val;
- const int *map;
-
- if (group >= ARRAY_SIZE(tz1090_groups)) {
- pin = group - ARRAY_SIZE(tz1090_groups);
- return tz1090_pinconf_get(pctldev, pin, config);
- }
-
- g = &tz1090_groups[group];
- if (g->npins == 1) {
- pin = g->pins[0];
- ret = tz1090_pinconf_get(pctldev, pin, config);
- if (ret != -ENOTSUPP)
- return ret;
- }
-
- /* Get register information */
- ret = tz1090_pinconf_group_reg(pctldev, g, param, true,
- &reg, &width, &mask, &shift, &map);
- if (ret < 0)
- return ret;
-
- /* Extract field from register */
- val = pmx_read(pmx, reg);
- arg = map[(val & mask) >> shift];
- if (arg < 0)
- return arg;
-
- /* And pack config */
- *config = pinconf_to_config_packed(param, arg);
-
- return 0;
-}
-
-static int tz1090_pinconf_group_set(struct pinctrl_dev *pctldev,
- unsigned int group, unsigned long *configs,
- unsigned num_configs)
-{
- struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- const struct tz1090_pingroup *g;
- enum pin_config_param param;
- unsigned int arg, pin, i;
- const unsigned int *pit;
- int ret;
- u32 reg, width, mask, shift, val;
- unsigned long flags;
- const int *map;
- int j;
-
- if (group >= ARRAY_SIZE(tz1090_groups)) {
- pin = group - ARRAY_SIZE(tz1090_groups);
- return tz1090_pinconf_set(pctldev, pin, configs, num_configs);
- }
-
- g = &tz1090_groups[group];
- if (g->npins == 1) {
- pin = g->pins[0];
- ret = tz1090_pinconf_set(pctldev, pin, configs, num_configs);
- if (ret != -ENOTSUPP)
- return ret;
- }
-
- for (j = 0; j < num_configs; j++) {
- param = pinconf_to_config_param(configs[j]);
-
- dev_dbg(pctldev->dev, "%s(group=%s, config=%#lx)\n",
- __func__, g->name, configs[j]);
-
- /* Get register information */
- ret = tz1090_pinconf_group_reg(pctldev, g, param, true, &reg,
- &width, &mask, &shift, &map);
- if (ret < 0) {
- /*
- * Maybe we're trying to set a per-pin configuration
- * of a group, so do the pins one by one. This is
- * mainly as a convenience.
- */
- for (i = 0, pit = g->pins; i < g->npins; ++i, ++pit) {
- ret = tz1090_pinconf_set(pctldev, *pit, configs,
- num_configs);
- if (ret)
- return ret;
- }
- return 0;
- }
-
- /* Unpack argument and map it to register value */
- arg = pinconf_to_config_argument(configs[j]);
- for (i = 0; i < BIT(width); ++i) {
- if (map[i] == arg || (map[i] == -EINVAL && !arg)) {
- /* Write register field */
- __global_lock2(flags);
- val = pmx_read(pmx, reg);
- val &= ~mask;
- val |= i << shift;
- pmx_write(pmx, val, reg);
- __global_unlock2(flags);
- goto next_config;
- }
- }
-
- dev_dbg(pctldev->dev, "%s: arg %u not supported\n",
- __func__, arg);
- return -EINVAL;
-
-next_config:
- ;
- } /* for each config */
-
- return 0;
-}
-
-static const struct pinconf_ops tz1090_pinconf_ops = {
- .is_generic = true,
- .pin_config_get = tz1090_pinconf_get,
- .pin_config_set = tz1090_pinconf_set,
- .pin_config_group_get = tz1090_pinconf_group_get,
- .pin_config_group_set = tz1090_pinconf_group_set,
- .pin_config_config_dbg_show = pinconf_generic_dump_config,
-};
-
-/*
- * Pin control driver setup
- */
-
-static struct pinctrl_desc tz1090_pinctrl_desc = {
- .pctlops = &tz1090_pinctrl_ops,
- .pmxops = &tz1090_pinmux_ops,
- .confops = &tz1090_pinconf_ops,
- .owner = THIS_MODULE,
-};
-
-static int tz1090_pinctrl_probe(struct platform_device *pdev)
-{
- struct tz1090_pmx *pmx;
- struct resource *res;
-
- pmx = devm_kzalloc(&pdev->dev, sizeof(*pmx), GFP_KERNEL);
- if (!pmx)
- return -ENOMEM;
-
- pmx->dev = &pdev->dev;
- spin_lock_init(&pmx->lock);
-
- tz1090_pinctrl_desc.name = dev_name(&pdev->dev);
- tz1090_pinctrl_desc.pins = tz1090_pins;
- tz1090_pinctrl_desc.npins = ARRAY_SIZE(tz1090_pins);
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- pmx->regs = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(pmx->regs))
- return PTR_ERR(pmx->regs);
-
- pmx->pctl = devm_pinctrl_register(&pdev->dev, &tz1090_pinctrl_desc,
- pmx);
- if (IS_ERR(pmx->pctl)) {
- dev_err(&pdev->dev, "Couldn't register pinctrl driver\n");
- return PTR_ERR(pmx->pctl);
- }
-
- platform_set_drvdata(pdev, pmx);
-
- dev_info(&pdev->dev, "TZ1090 pinctrl driver initialised\n");
-
- return 0;
-}
-
-static const struct of_device_id tz1090_pinctrl_of_match[] = {
- { .compatible = "img,tz1090-pinctrl", },
- { },
-};
-
-static struct platform_driver tz1090_pinctrl_driver = {
- .driver = {
- .name = "tz1090-pinctrl",
- .of_match_table = tz1090_pinctrl_of_match,
- },
- .probe = tz1090_pinctrl_probe,
-};
-
-static int __init tz1090_pinctrl_init(void)
-{
- tz1090_init_mux_pins();
- return platform_driver_register(&tz1090_pinctrl_driver);
-}
-arch_initcall(tz1090_pinctrl_init);
-
-static void __exit tz1090_pinctrl_exit(void)
-{
- platform_driver_unregister(&tz1090_pinctrl_driver);
-}
-module_exit(tz1090_pinctrl_exit);
-
-MODULE_AUTHOR("Imagination Technologies Ltd.");
-MODULE_DESCRIPTION("Toumaz Xenif TZ1090 pinctrl driver");
-MODULE_LICENSE("GPL v2");
-MODULE_DEVICE_TABLE(of, tz1090_pinctrl_of_match);
diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig
index fcaaa92d76df..195492033075 100644
--- a/drivers/pinctrl/qcom/Kconfig
+++ b/drivers/pinctrl/qcom/Kconfig
@@ -147,4 +147,13 @@ config PINCTRL_QCOM_SSBI_PMIC
which are using SSBI for communication with SoC. Example PMIC's
devices are pm8058 and pm8921.
+config PINCTRL_SDM845
+ tristate "Qualcomm Technologies Inc SDM845 pin controller driver"
+ depends on GPIOLIB && OF
+ select PINCTRL_MSM
+ help
+ This is the pinctrl, pinmux, pinconf and gpiolib driver for the
+ Qualcomm Technologies Inc TLMM block found on the Qualcomm
+ Technologies Inc SDM845 platform.
+
endif
diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile
index 8cb45bbd2e7f..0c6f3ddc296d 100644
--- a/drivers/pinctrl/qcom/Makefile
+++ b/drivers/pinctrl/qcom/Makefile
@@ -19,3 +19,4 @@ obj-$(CONFIG_PINCTRL_QCOM_SPMI_PMIC) += pinctrl-spmi-gpio.o
obj-$(CONFIG_PINCTRL_QCOM_SPMI_PMIC) += pinctrl-spmi-mpp.o
obj-$(CONFIG_PINCTRL_QCOM_SSBI_PMIC) += pinctrl-ssbi-gpio.o
obj-$(CONFIG_PINCTRL_QCOM_SSBI_PMIC) += pinctrl-ssbi-mpp.o
+obj-$(CONFIG_PINCTRL_SDM845) += pinctrl-sdm845.o
diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c
index 495432f3341b..ad80a17c9990 100644
--- a/drivers/pinctrl/qcom/pinctrl-msm.c
+++ b/drivers/pinctrl/qcom/pinctrl-msm.c
@@ -105,6 +105,14 @@ static const struct pinctrl_ops msm_pinctrl_ops = {
.dt_free_map = pinctrl_utils_free_map,
};
+static int msm_pinmux_request(struct pinctrl_dev *pctldev, unsigned offset)
+{
+ struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+ struct gpio_chip *chip = &pctrl->chip;
+
+ return gpiochip_line_is_valid(chip, offset) ? 0 : -EINVAL;
+}
+
static int msm_get_functions_count(struct pinctrl_dev *pctldev)
{
struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
@@ -166,6 +174,7 @@ static int msm_pinmux_set_mux(struct pinctrl_dev *pctldev,
}
static const struct pinmux_ops msm_pinmux_ops = {
+ .request = msm_pinmux_request,
.get_functions_count = msm_get_functions_count,
.get_function_name = msm_get_function_name,
.get_function_groups = msm_get_function_groups,
@@ -506,6 +515,9 @@ static void msm_gpio_dbg_show_one(struct seq_file *s,
"pull up"
};
+ if (!gpiochip_line_is_valid(chip, offset))
+ return;
+
g = &pctrl->soc->groups[offset];
ctl_reg = readl(pctrl->regs + g->ctl_reg);
@@ -517,6 +529,7 @@ static void msm_gpio_dbg_show_one(struct seq_file *s,
seq_printf(s, " %-8s: %-3s %d", g->name, is_out ? "out" : "in", func);
seq_printf(s, " %dmA", msm_regval_to_drive(drive));
seq_printf(s, " %s", pulls[pull]);
+ seq_puts(s, "\n");
}
static void msm_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
@@ -524,10 +537,8 @@ static void msm_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
unsigned gpio = chip->base;
unsigned i;
- for (i = 0; i < chip->ngpio; i++, gpio++) {
+ for (i = 0; i < chip->ngpio; i++, gpio++)
msm_gpio_dbg_show_one(s, NULL, chip, i, gpio);
- seq_puts(s, "\n");
- }
}
#else
@@ -808,6 +819,46 @@ static void msm_gpio_irq_handler(struct irq_desc *desc)
chained_irq_exit(chip, desc);
}
+static int msm_gpio_init_valid_mask(struct gpio_chip *chip,
+ struct msm_pinctrl *pctrl)
+{
+ int ret;
+ unsigned int len, i;
+ unsigned int max_gpios = pctrl->soc->ngpios;
+ u16 *tmp;
+
+ /* The number of GPIOs in the ACPI tables */
+ len = ret = device_property_read_u16_array(pctrl->dev, "gpios", NULL, 0);
+ if (ret < 0)
+ return 0;
+
+ if (ret > max_gpios)
+ return -EINVAL;
+
+ tmp = kmalloc_array(len, sizeof(*tmp), GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+
+ ret = device_property_read_u16_array(pctrl->dev, "gpios", tmp, len);
+ if (ret < 0) {
+ dev_err(pctrl->dev, "could not read list of GPIOs\n");
+ goto out;
+ }
+
+ bitmap_zero(chip->valid_mask, max_gpios);
+ for (i = 0; i < len; i++)
+ set_bit(tmp[i], chip->valid_mask);
+
+out:
+ kfree(tmp);
+ return ret;
+}
+
+static bool msm_gpio_needs_valid_mask(struct msm_pinctrl *pctrl)
+{
+ return device_property_read_u16_array(pctrl->dev, "gpios", NULL, 0) > 0;
+}
+
static int msm_gpio_init(struct msm_pinctrl *pctrl)
{
struct gpio_chip *chip;
@@ -818,12 +869,13 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl)
return -EINVAL;
chip = &pctrl->chip;
- chip->base = 0;
+ chip->base = -1;
chip->ngpio = ngpio;
chip->label = dev_name(pctrl->dev);
chip->parent = pctrl->dev;
chip->owner = THIS_MODULE;
chip->of_node = pctrl->dev->of_node;
+ chip->need_valid_mask = msm_gpio_needs_valid_mask(pctrl);
ret = gpiochip_add_data(&pctrl->chip, pctrl);
if (ret) {
@@ -831,6 +883,13 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl)
return ret;
}
+ ret = msm_gpio_init_valid_mask(chip, pctrl);
+ if (ret) {
+ dev_err(pctrl->dev, "Failed to setup irq valid bits\n");
+ gpiochip_remove(&pctrl->chip);
+ return ret;
+ }
+
ret = gpiochip_add_pin_range(&pctrl->chip, dev_name(pctrl->dev), 0, 0, chip->ngpio);
if (ret) {
dev_err(pctrl->dev, "Failed to add pin range\n");
diff --git a/drivers/pinctrl/qcom/pinctrl-msm8998.c b/drivers/pinctrl/qcom/pinctrl-msm8998.c
index c33953183013..00d7b94bc3f1 100644
--- a/drivers/pinctrl/qcom/pinctrl-msm8998.c
+++ b/drivers/pinctrl/qcom/pinctrl-msm8998.c
@@ -1566,7 +1566,6 @@ static const struct of_device_id msm8998_pinctrl_of_match[] = {
static struct platform_driver msm8998_pinctrl_driver = {
.driver = {
.name = "msm8998-pinctrl",
- .owner = THIS_MODULE,
.of_match_table = msm8998_pinctrl_of_match,
},
.probe = msm8998_pinctrl_probe,
diff --git a/drivers/pinctrl/qcom/pinctrl-sdm845.c b/drivers/pinctrl/qcom/pinctrl-sdm845.c
new file mode 100644
index 000000000000..2ab7a8885757
--- /dev/null
+++ b/drivers/pinctrl/qcom/pinctrl-sdm845.c
@@ -0,0 +1,1323 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/pinctrl.h>
+
+#include "pinctrl-msm.h"
+
+#define FUNCTION(fname) \
+ [msm_mux_##fname] = { \
+ .name = #fname, \
+ .groups = fname##_groups, \
+ .ngroups = ARRAY_SIZE(fname##_groups), \
+ }
+
+#define NORTH 0x00500000
+#define SOUTH 0x00900000
+#define EAST 0x00100000
+#define REG_SIZE 0x1000
+#define PINGROUP(id, base, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10) \
+ { \
+ .name = "gpio" #id, \
+ .pins = gpio##id##_pins, \
+ .npins = ARRAY_SIZE(gpio##id##_pins), \
+ .funcs = (int[]){ \
+ msm_mux_gpio, /* gpio mode */ \
+ msm_mux_##f1, \
+ msm_mux_##f2, \
+ msm_mux_##f3, \
+ msm_mux_##f4, \
+ msm_mux_##f5, \
+ msm_mux_##f6, \
+ msm_mux_##f7, \
+ msm_mux_##f8, \
+ msm_mux_##f9, \
+ msm_mux_##f10 \
+ }, \
+ .nfuncs = 11, \
+ .ctl_reg = base + REG_SIZE * id, \
+ .io_reg = base + 0x4 + REG_SIZE * id, \
+ .intr_cfg_reg = base + 0x8 + REG_SIZE * id, \
+ .intr_status_reg = base + 0xc + REG_SIZE * id, \
+ .intr_target_reg = base + 0x8 + REG_SIZE * id, \
+ .mux_bit = 2, \
+ .pull_bit = 0, \
+ .drv_bit = 6, \
+ .oe_bit = 9, \
+ .in_bit = 0, \
+ .out_bit = 1, \
+ .intr_enable_bit = 0, \
+ .intr_status_bit = 0, \
+ .intr_target_bit = 5, \
+ .intr_target_kpss_val = 3, \
+ .intr_raw_status_bit = 4, \
+ .intr_polarity_bit = 1, \
+ .intr_detection_bit = 2, \
+ .intr_detection_width = 2, \
+ }
+
+#define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv) \
+ { \
+ .name = #pg_name, \
+ .pins = pg_name##_pins, \
+ .npins = ARRAY_SIZE(pg_name##_pins), \
+ .ctl_reg = ctl, \
+ .io_reg = 0, \
+ .intr_cfg_reg = 0, \
+ .intr_status_reg = 0, \
+ .intr_target_reg = 0, \
+ .mux_bit = -1, \
+ .pull_bit = pull, \
+ .drv_bit = drv, \
+ .oe_bit = -1, \
+ .in_bit = -1, \
+ .out_bit = -1, \
+ .intr_enable_bit = -1, \
+ .intr_status_bit = -1, \
+ .intr_target_bit = -1, \
+ .intr_raw_status_bit = -1, \
+ .intr_polarity_bit = -1, \
+ .intr_detection_bit = -1, \
+ .intr_detection_width = -1, \
+ }
+
+#define UFS_RESET(pg_name, offset) \
+ { \
+ .name = #pg_name, \
+ .pins = pg_name##_pins, \
+ .npins = ARRAY_SIZE(pg_name##_pins), \
+ .ctl_reg = offset, \
+ .io_reg = offset + 0x4, \
+ .intr_cfg_reg = 0, \
+ .intr_status_reg = 0, \
+ .intr_target_reg = 0, \
+ .mux_bit = -1, \
+ .pull_bit = 3, \
+ .drv_bit = 0, \
+ .oe_bit = -1, \
+ .in_bit = -1, \
+ .out_bit = 0, \
+ .intr_enable_bit = -1, \
+ .intr_status_bit = -1, \
+ .intr_target_bit = -1, \
+ .intr_raw_status_bit = -1, \
+ .intr_polarity_bit = -1, \
+ .intr_detection_bit = -1, \
+ .intr_detection_width = -1, \
+ }
+static const struct pinctrl_pin_desc sdm845_pins[] = {
+ PINCTRL_PIN(0, "GPIO_0"),
+ PINCTRL_PIN(1, "GPIO_1"),
+ PINCTRL_PIN(2, "GPIO_2"),
+ PINCTRL_PIN(3, "GPIO_3"),
+ PINCTRL_PIN(4, "GPIO_4"),
+ PINCTRL_PIN(5, "GPIO_5"),
+ PINCTRL_PIN(6, "GPIO_6"),
+ PINCTRL_PIN(7, "GPIO_7"),
+ PINCTRL_PIN(8, "GPIO_8"),
+ PINCTRL_PIN(9, "GPIO_9"),
+ PINCTRL_PIN(10, "GPIO_10"),
+ PINCTRL_PIN(11, "GPIO_11"),
+ PINCTRL_PIN(12, "GPIO_12"),
+ PINCTRL_PIN(13, "GPIO_13"),
+ PINCTRL_PIN(14, "GPIO_14"),
+ PINCTRL_PIN(15, "GPIO_15"),
+ PINCTRL_PIN(16, "GPIO_16"),
+ PINCTRL_PIN(17, "GPIO_17"),
+ PINCTRL_PIN(18, "GPIO_18"),
+ PINCTRL_PIN(19, "GPIO_19"),
+ PINCTRL_PIN(20, "GPIO_20"),
+ PINCTRL_PIN(21, "GPIO_21"),
+ PINCTRL_PIN(22, "GPIO_22"),
+ PINCTRL_PIN(23, "GPIO_23"),
+ PINCTRL_PIN(24, "GPIO_24"),
+ PINCTRL_PIN(25, "GPIO_25"),
+ PINCTRL_PIN(26, "GPIO_26"),
+ PINCTRL_PIN(27, "GPIO_27"),
+ PINCTRL_PIN(28, "GPIO_28"),
+ PINCTRL_PIN(29, "GPIO_29"),
+ PINCTRL_PIN(30, "GPIO_30"),
+ PINCTRL_PIN(31, "GPIO_31"),
+ PINCTRL_PIN(32, "GPIO_32"),
+ PINCTRL_PIN(33, "GPIO_33"),
+ PINCTRL_PIN(34, "GPIO_34"),
+ PINCTRL_PIN(35, "GPIO_35"),
+ PINCTRL_PIN(36, "GPIO_36"),
+ PINCTRL_PIN(37, "GPIO_37"),
+ PINCTRL_PIN(38, "GPIO_38"),
+ PINCTRL_PIN(39, "GPIO_39"),
+ PINCTRL_PIN(40, "GPIO_40"),
+ PINCTRL_PIN(41, "GPIO_41"),
+ PINCTRL_PIN(42, "GPIO_42"),
+ PINCTRL_PIN(43, "GPIO_43"),
+ PINCTRL_PIN(44, "GPIO_44"),
+ PINCTRL_PIN(45, "GPIO_45"),
+ PINCTRL_PIN(46, "GPIO_46"),
+ PINCTRL_PIN(47, "GPIO_47"),
+ PINCTRL_PIN(48, "GPIO_48"),
+ PINCTRL_PIN(49, "GPIO_49"),
+ PINCTRL_PIN(50, "GPIO_50"),
+ PINCTRL_PIN(51, "GPIO_51"),
+ PINCTRL_PIN(52, "GPIO_52"),
+ PINCTRL_PIN(53, "GPIO_53"),
+ PINCTRL_PIN(54, "GPIO_54"),
+ PINCTRL_PIN(55, "GPIO_55"),
+ PINCTRL_PIN(56, "GPIO_56"),
+ PINCTRL_PIN(57, "GPIO_57"),
+ PINCTRL_PIN(58, "GPIO_58"),
+ PINCTRL_PIN(59, "GPIO_59"),
+ PINCTRL_PIN(60, "GPIO_60"),
+ PINCTRL_PIN(61, "GPIO_61"),
+ PINCTRL_PIN(62, "GPIO_62"),
+ PINCTRL_PIN(63, "GPIO_63"),
+ PINCTRL_PIN(64, "GPIO_64"),
+ PINCTRL_PIN(65, "GPIO_65"),
+ PINCTRL_PIN(66, "GPIO_66"),
+ PINCTRL_PIN(67, "GPIO_67"),
+ PINCTRL_PIN(68, "GPIO_68"),
+ PINCTRL_PIN(69, "GPIO_69"),
+ PINCTRL_PIN(70, "GPIO_70"),
+ PINCTRL_PIN(71, "GPIO_71"),
+ PINCTRL_PIN(72, "GPIO_72"),
+ PINCTRL_PIN(73, "GPIO_73"),
+ PINCTRL_PIN(74, "GPIO_74"),
+ PINCTRL_PIN(75, "GPIO_75"),
+ PINCTRL_PIN(76, "GPIO_76"),
+ PINCTRL_PIN(77, "GPIO_77"),
+ PINCTRL_PIN(78, "GPIO_78"),
+ PINCTRL_PIN(79, "GPIO_79"),
+ PINCTRL_PIN(80, "GPIO_80"),
+ PINCTRL_PIN(81, "GPIO_81"),
+ PINCTRL_PIN(82, "GPIO_82"),
+ PINCTRL_PIN(83, "GPIO_83"),
+ PINCTRL_PIN(84, "GPIO_84"),
+ PINCTRL_PIN(85, "GPIO_85"),
+ PINCTRL_PIN(86, "GPIO_86"),
+ PINCTRL_PIN(87, "GPIO_87"),
+ PINCTRL_PIN(88, "GPIO_88"),
+ PINCTRL_PIN(89, "GPIO_89"),
+ PINCTRL_PIN(90, "GPIO_90"),
+ PINCTRL_PIN(91, "GPIO_91"),
+ PINCTRL_PIN(92, "GPIO_92"),
+ PINCTRL_PIN(93, "GPIO_93"),
+ PINCTRL_PIN(94, "GPIO_94"),
+ PINCTRL_PIN(95, "GPIO_95"),
+ PINCTRL_PIN(96, "GPIO_96"),
+ PINCTRL_PIN(97, "GPIO_97"),
+ PINCTRL_PIN(98, "GPIO_98"),
+ PINCTRL_PIN(99, "GPIO_99"),
+ PINCTRL_PIN(100, "GPIO_100"),
+ PINCTRL_PIN(101, "GPIO_101"),
+ PINCTRL_PIN(102, "GPIO_102"),
+ PINCTRL_PIN(103, "GPIO_103"),
+ PINCTRL_PIN(104, "GPIO_104"),
+ PINCTRL_PIN(105, "GPIO_105"),
+ PINCTRL_PIN(106, "GPIO_106"),
+ PINCTRL_PIN(107, "GPIO_107"),
+ PINCTRL_PIN(108, "GPIO_108"),
+ PINCTRL_PIN(109, "GPIO_109"),
+ PINCTRL_PIN(110, "GPIO_110"),
+ PINCTRL_PIN(111, "GPIO_111"),
+ PINCTRL_PIN(112, "GPIO_112"),
+ PINCTRL_PIN(113, "GPIO_113"),
+ PINCTRL_PIN(114, "GPIO_114"),
+ PINCTRL_PIN(115, "GPIO_115"),
+ PINCTRL_PIN(116, "GPIO_116"),
+ PINCTRL_PIN(117, "GPIO_117"),
+ PINCTRL_PIN(118, "GPIO_118"),
+ PINCTRL_PIN(119, "GPIO_119"),
+ PINCTRL_PIN(120, "GPIO_120"),
+ PINCTRL_PIN(121, "GPIO_121"),
+ PINCTRL_PIN(122, "GPIO_122"),
+ PINCTRL_PIN(123, "GPIO_123"),
+ PINCTRL_PIN(124, "GPIO_124"),
+ PINCTRL_PIN(125, "GPIO_125"),
+ PINCTRL_PIN(126, "GPIO_126"),
+ PINCTRL_PIN(127, "GPIO_127"),
+ PINCTRL_PIN(128, "GPIO_128"),
+ PINCTRL_PIN(129, "GPIO_129"),
+ PINCTRL_PIN(130, "GPIO_130"),
+ PINCTRL_PIN(131, "GPIO_131"),
+ PINCTRL_PIN(132, "GPIO_132"),
+ PINCTRL_PIN(133, "GPIO_133"),
+ PINCTRL_PIN(134, "GPIO_134"),
+ PINCTRL_PIN(135, "GPIO_135"),
+ PINCTRL_PIN(136, "GPIO_136"),
+ PINCTRL_PIN(137, "GPIO_137"),
+ PINCTRL_PIN(138, "GPIO_138"),
+ PINCTRL_PIN(139, "GPIO_139"),
+ PINCTRL_PIN(140, "GPIO_140"),
+ PINCTRL_PIN(141, "GPIO_141"),
+ PINCTRL_PIN(142, "GPIO_142"),
+ PINCTRL_PIN(143, "GPIO_143"),
+ PINCTRL_PIN(144, "GPIO_144"),
+ PINCTRL_PIN(145, "GPIO_145"),
+ PINCTRL_PIN(146, "GPIO_146"),
+ PINCTRL_PIN(147, "GPIO_147"),
+ PINCTRL_PIN(148, "GPIO_148"),
+ PINCTRL_PIN(149, "GPIO_149"),
+ PINCTRL_PIN(150, "SDC2_CLK"),
+ PINCTRL_PIN(151, "SDC2_CMD"),
+ PINCTRL_PIN(152, "SDC2_DATA"),
+ PINCTRL_PIN(153, "UFS_RESET"),
+};
+
+#define DECLARE_MSM_GPIO_PINS(pin) \
+ static const unsigned int gpio##pin##_pins[] = { pin }
+DECLARE_MSM_GPIO_PINS(0);
+DECLARE_MSM_GPIO_PINS(1);
+DECLARE_MSM_GPIO_PINS(2);
+DECLARE_MSM_GPIO_PINS(3);
+DECLARE_MSM_GPIO_PINS(4);
+DECLARE_MSM_GPIO_PINS(5);
+DECLARE_MSM_GPIO_PINS(6);
+DECLARE_MSM_GPIO_PINS(7);
+DECLARE_MSM_GPIO_PINS(8);
+DECLARE_MSM_GPIO_PINS(9);
+DECLARE_MSM_GPIO_PINS(10);
+DECLARE_MSM_GPIO_PINS(11);
+DECLARE_MSM_GPIO_PINS(12);
+DECLARE_MSM_GPIO_PINS(13);
+DECLARE_MSM_GPIO_PINS(14);
+DECLARE_MSM_GPIO_PINS(15);
+DECLARE_MSM_GPIO_PINS(16);
+DECLARE_MSM_GPIO_PINS(17);
+DECLARE_MSM_GPIO_PINS(18);
+DECLARE_MSM_GPIO_PINS(19);
+DECLARE_MSM_GPIO_PINS(20);
+DECLARE_MSM_GPIO_PINS(21);
+DECLARE_MSM_GPIO_PINS(22);
+DECLARE_MSM_GPIO_PINS(23);
+DECLARE_MSM_GPIO_PINS(24);
+DECLARE_MSM_GPIO_PINS(25);
+DECLARE_MSM_GPIO_PINS(26);
+DECLARE_MSM_GPIO_PINS(27);
+DECLARE_MSM_GPIO_PINS(28);
+DECLARE_MSM_GPIO_PINS(29);
+DECLARE_MSM_GPIO_PINS(30);
+DECLARE_MSM_GPIO_PINS(31);
+DECLARE_MSM_GPIO_PINS(32);
+DECLARE_MSM_GPIO_PINS(33);
+DECLARE_MSM_GPIO_PINS(34);
+DECLARE_MSM_GPIO_PINS(35);
+DECLARE_MSM_GPIO_PINS(36);
+DECLARE_MSM_GPIO_PINS(37);
+DECLARE_MSM_GPIO_PINS(38);
+DECLARE_MSM_GPIO_PINS(39);
+DECLARE_MSM_GPIO_PINS(40);
+DECLARE_MSM_GPIO_PINS(41);
+DECLARE_MSM_GPIO_PINS(42);
+DECLARE_MSM_GPIO_PINS(43);
+DECLARE_MSM_GPIO_PINS(44);
+DECLARE_MSM_GPIO_PINS(45);
+DECLARE_MSM_GPIO_PINS(46);
+DECLARE_MSM_GPIO_PINS(47);
+DECLARE_MSM_GPIO_PINS(48);
+DECLARE_MSM_GPIO_PINS(49);
+DECLARE_MSM_GPIO_PINS(50);
+DECLARE_MSM_GPIO_PINS(51);
+DECLARE_MSM_GPIO_PINS(52);
+DECLARE_MSM_GPIO_PINS(53);
+DECLARE_MSM_GPIO_PINS(54);
+DECLARE_MSM_GPIO_PINS(55);
+DECLARE_MSM_GPIO_PINS(56);
+DECLARE_MSM_GPIO_PINS(57);
+DECLARE_MSM_GPIO_PINS(58);
+DECLARE_MSM_GPIO_PINS(59);
+DECLARE_MSM_GPIO_PINS(60);
+DECLARE_MSM_GPIO_PINS(61);
+DECLARE_MSM_GPIO_PINS(62);
+DECLARE_MSM_GPIO_PINS(63);
+DECLARE_MSM_GPIO_PINS(64);
+DECLARE_MSM_GPIO_PINS(65);
+DECLARE_MSM_GPIO_PINS(66);
+DECLARE_MSM_GPIO_PINS(67);
+DECLARE_MSM_GPIO_PINS(68);
+DECLARE_MSM_GPIO_PINS(69);
+DECLARE_MSM_GPIO_PINS(70);
+DECLARE_MSM_GPIO_PINS(71);
+DECLARE_MSM_GPIO_PINS(72);
+DECLARE_MSM_GPIO_PINS(73);
+DECLARE_MSM_GPIO_PINS(74);
+DECLARE_MSM_GPIO_PINS(75);
+DECLARE_MSM_GPIO_PINS(76);
+DECLARE_MSM_GPIO_PINS(77);
+DECLARE_MSM_GPIO_PINS(78);
+DECLARE_MSM_GPIO_PINS(79);
+DECLARE_MSM_GPIO_PINS(80);
+DECLARE_MSM_GPIO_PINS(81);
+DECLARE_MSM_GPIO_PINS(82);
+DECLARE_MSM_GPIO_PINS(83);
+DECLARE_MSM_GPIO_PINS(84);
+DECLARE_MSM_GPIO_PINS(85);
+DECLARE_MSM_GPIO_PINS(86);
+DECLARE_MSM_GPIO_PINS(87);
+DECLARE_MSM_GPIO_PINS(88);
+DECLARE_MSM_GPIO_PINS(89);
+DECLARE_MSM_GPIO_PINS(90);
+DECLARE_MSM_GPIO_PINS(91);
+DECLARE_MSM_GPIO_PINS(92);
+DECLARE_MSM_GPIO_PINS(93);
+DECLARE_MSM_GPIO_PINS(94);
+DECLARE_MSM_GPIO_PINS(95);
+DECLARE_MSM_GPIO_PINS(96);
+DECLARE_MSM_GPIO_PINS(97);
+DECLARE_MSM_GPIO_PINS(98);
+DECLARE_MSM_GPIO_PINS(99);
+DECLARE_MSM_GPIO_PINS(100);
+DECLARE_MSM_GPIO_PINS(101);
+DECLARE_MSM_GPIO_PINS(102);
+DECLARE_MSM_GPIO_PINS(103);
+DECLARE_MSM_GPIO_PINS(104);
+DECLARE_MSM_GPIO_PINS(105);
+DECLARE_MSM_GPIO_PINS(106);
+DECLARE_MSM_GPIO_PINS(107);
+DECLARE_MSM_GPIO_PINS(108);
+DECLARE_MSM_GPIO_PINS(109);
+DECLARE_MSM_GPIO_PINS(110);
+DECLARE_MSM_GPIO_PINS(111);
+DECLARE_MSM_GPIO_PINS(112);
+DECLARE_MSM_GPIO_PINS(113);
+DECLARE_MSM_GPIO_PINS(114);
+DECLARE_MSM_GPIO_PINS(115);
+DECLARE_MSM_GPIO_PINS(116);
+DECLARE_MSM_GPIO_PINS(117);
+DECLARE_MSM_GPIO_PINS(118);
+DECLARE_MSM_GPIO_PINS(119);
+DECLARE_MSM_GPIO_PINS(120);
+DECLARE_MSM_GPIO_PINS(121);
+DECLARE_MSM_GPIO_PINS(122);
+DECLARE_MSM_GPIO_PINS(123);
+DECLARE_MSM_GPIO_PINS(124);
+DECLARE_MSM_GPIO_PINS(125);
+DECLARE_MSM_GPIO_PINS(126);
+DECLARE_MSM_GPIO_PINS(127);
+DECLARE_MSM_GPIO_PINS(128);
+DECLARE_MSM_GPIO_PINS(129);
+DECLARE_MSM_GPIO_PINS(130);
+DECLARE_MSM_GPIO_PINS(131);
+DECLARE_MSM_GPIO_PINS(132);
+DECLARE_MSM_GPIO_PINS(133);
+DECLARE_MSM_GPIO_PINS(134);
+DECLARE_MSM_GPIO_PINS(135);
+DECLARE_MSM_GPIO_PINS(136);
+DECLARE_MSM_GPIO_PINS(137);
+DECLARE_MSM_GPIO_PINS(138);
+DECLARE_MSM_GPIO_PINS(139);
+DECLARE_MSM_GPIO_PINS(140);
+DECLARE_MSM_GPIO_PINS(141);
+DECLARE_MSM_GPIO_PINS(142);
+DECLARE_MSM_GPIO_PINS(143);
+DECLARE_MSM_GPIO_PINS(144);
+DECLARE_MSM_GPIO_PINS(145);
+DECLARE_MSM_GPIO_PINS(146);
+DECLARE_MSM_GPIO_PINS(147);
+DECLARE_MSM_GPIO_PINS(148);
+DECLARE_MSM_GPIO_PINS(149);
+
+static const unsigned int sdc2_clk_pins[] = { 150 };
+static const unsigned int sdc2_cmd_pins[] = { 151 };
+static const unsigned int sdc2_data_pins[] = { 152 };
+static const unsigned int ufs_reset_pins[] = { 153 };
+
+enum sdm845_functions {
+ msm_mux_gpio,
+ msm_mux_adsp_ext,
+ msm_mux_agera_pll,
+ msm_mux_atest_char,
+ msm_mux_atest_tsens,
+ msm_mux_atest_tsens2,
+ msm_mux_atest_usb1,
+ msm_mux_atest_usb10,
+ msm_mux_atest_usb11,
+ msm_mux_atest_usb12,
+ msm_mux_atest_usb13,
+ msm_mux_atest_usb2,
+ msm_mux_atest_usb20,
+ msm_mux_atest_usb21,
+ msm_mux_atest_usb22,
+ msm_mux_atest_usb23,
+ msm_mux_audio_ref,
+ msm_mux_btfm_slimbus,
+ msm_mux_cam_mclk,
+ msm_mux_cci_async,
+ msm_mux_cci_i2c,
+ msm_mux_cci_timer0,
+ msm_mux_cci_timer1,
+ msm_mux_cci_timer2,
+ msm_mux_cci_timer3,
+ msm_mux_cci_timer4,
+ msm_mux_cri_trng,
+ msm_mux_cri_trng0,
+ msm_mux_cri_trng1,
+ msm_mux_dbg_out,
+ msm_mux_ddr_bist,
+ msm_mux_ddr_pxi0,
+ msm_mux_ddr_pxi1,
+ msm_mux_ddr_pxi2,
+ msm_mux_ddr_pxi3,
+ msm_mux_edp_hot,
+ msm_mux_edp_lcd,
+ msm_mux_gcc_gp1,
+ msm_mux_gcc_gp2,
+ msm_mux_gcc_gp3,
+ msm_mux_jitter_bist,
+ msm_mux_ldo_en,
+ msm_mux_ldo_update,
+ msm_mux_lpass_slimbus,
+ msm_mux_m_voc,
+ msm_mux_mdp_vsync,
+ msm_mux_mdp_vsync0,
+ msm_mux_mdp_vsync1,
+ msm_mux_mdp_vsync2,
+ msm_mux_mdp_vsync3,
+ msm_mux_mss_lte,
+ msm_mux_nav_pps,
+ msm_mux_pa_indicator,
+ msm_mux_pci_e0,
+ msm_mux_pci_e1,
+ msm_mux_phase_flag,
+ msm_mux_pll_bist,
+ msm_mux_pll_bypassnl,
+ msm_mux_pll_reset,
+ msm_mux_pri_mi2s,
+ msm_mux_pri_mi2s_ws,
+ msm_mux_prng_rosc,
+ msm_mux_qdss_cti,
+ msm_mux_qdss,
+ msm_mux_qlink_enable,
+ msm_mux_qlink_request,
+ msm_mux_qspi_clk,
+ msm_mux_qspi_cs,
+ msm_mux_qspi_data,
+ msm_mux_qua_mi2s,
+ msm_mux_qup0,
+ msm_mux_qup1,
+ msm_mux_qup10,
+ msm_mux_qup11,
+ msm_mux_qup12,
+ msm_mux_qup13,
+ msm_mux_qup14,
+ msm_mux_qup15,
+ msm_mux_qup2,
+ msm_mux_qup3,
+ msm_mux_qup4,
+ msm_mux_qup5,
+ msm_mux_qup6,
+ msm_mux_qup7,
+ msm_mux_qup8,
+ msm_mux_qup9,
+ msm_mux_qup_l4,
+ msm_mux_qup_l5,
+ msm_mux_qup_l6,
+ msm_mux_sd_write,
+ msm_mux_sdc4_clk,
+ msm_mux_sdc4_cmd,
+ msm_mux_sdc4_data,
+ msm_mux_sec_mi2s,
+ msm_mux_sp_cmu,
+ msm_mux_spkr_i2s,
+ msm_mux_ter_mi2s,
+ msm_mux_tgu_ch0,
+ msm_mux_tgu_ch1,
+ msm_mux_tgu_ch2,
+ msm_mux_tgu_ch3,
+ msm_mux_tsense_pwm1,
+ msm_mux_tsense_pwm2,
+ msm_mux_tsif1_clk,
+ msm_mux_tsif1_data,
+ msm_mux_tsif1_en,
+ msm_mux_tsif1_error,
+ msm_mux_tsif1_sync,
+ msm_mux_tsif2_clk,
+ msm_mux_tsif2_data,
+ msm_mux_tsif2_en,
+ msm_mux_tsif2_error,
+ msm_mux_tsif2_sync,
+ msm_mux_uim1_clk,
+ msm_mux_uim1_data,
+ msm_mux_uim1_present,
+ msm_mux_uim1_reset,
+ msm_mux_uim2_clk,
+ msm_mux_uim2_data,
+ msm_mux_uim2_present,
+ msm_mux_uim2_reset,
+ msm_mux_uim_batt,
+ msm_mux_usb_phy,
+ msm_mux_vfr_1,
+ msm_mux_vsense_trigger,
+ msm_mux_wlan1_adc0,
+ msm_mux_wlan1_adc1,
+ msm_mux_wlan2_adc0,
+ msm_mux_wlan2_adc1,
+ msm_mux__,
+};
+
+static const char * const ddr_pxi3_groups[] = {
+ "gpio12", "gpio13",
+};
+static const char * const cam_mclk_groups[] = {
+ "gpio13", "gpio14", "gpio15", "gpio16",
+};
+static const char * const pll_bypassnl_groups[] = {
+ "gpio13",
+};
+static const char * const qdss_groups[] = {
+ "gpio13", "gpio14", "gpio15", "gpio16", "gpio17", "gpio18", "gpio19",
+ "gpio20", "gpio21", "gpio22", "gpio23", "gpio24", "gpio25", "gpio26",
+ "gpio27", "gpio28", "gpio29", "gpio30", "gpio41", "gpio42", "gpio43",
+ "gpio44", "gpio75", "gpio76", "gpio77", "gpio79", "gpio80", "gpio93",
+ "gpio117", "gpio118", "gpio119", "gpio120", "gpio121", "gpio122",
+ "gpio123", "gpio124",
+};
+static const char * const pll_reset_groups[] = {
+ "gpio14",
+};
+static const char * const cci_i2c_groups[] = {
+ "gpio17", "gpio18", "gpio19", "gpio20",
+};
+static const char * const qup1_groups[] = {
+ "gpio17", "gpio18", "gpio19", "gpio20",
+};
+static const char * const cci_timer0_groups[] = {
+ "gpio21",
+};
+static const char * const gcc_gp2_groups[] = {
+ "gpio21", "gpio58",
+};
+static const char * const cci_timer1_groups[] = {
+ "gpio22",
+};
+static const char * const gcc_gp3_groups[] = {
+ "gpio22", "gpio59",
+};
+static const char * const cci_timer2_groups[] = {
+ "gpio23",
+};
+static const char * const cci_timer3_groups[] = {
+ "gpio24",
+};
+static const char * const cci_async_groups[] = {
+ "gpio24", "gpio25", "gpio26",
+};
+static const char * const cci_timer4_groups[] = {
+ "gpio25",
+};
+static const char * const qup2_groups[] = {
+ "gpio27", "gpio28", "gpio29", "gpio30",
+};
+static const char * const phase_flag_groups[] = {
+ "gpio29", "gpio30", "gpio52", "gpio53", "gpio54", "gpio55", "gpio56",
+ "gpio57", "gpio58", "gpio59", "gpio60", "gpio61", "gpio62", "gpio63",
+ "gpio64", "gpio74", "gpio75", "gpio76", "gpio77", "gpio89", "gpio90",
+ "gpio96", "gpio99", "gpio100", "gpio103", "gpio137", "gpio138",
+ "gpio139", "gpio140", "gpio141", "gpio142", "gpio143",
+};
+static const char * const qup11_groups[] = {
+ "gpio31", "gpio32", "gpio33", "gpio34",
+};
+static const char * const qup14_groups[] = {
+ "gpio31", "gpio32", "gpio33", "gpio34",
+};
+static const char * const pci_e0_groups[] = {
+ "gpio35", "gpio36",
+};
+static const char * const jitter_bist_groups[] = {
+ "gpio35",
+};
+static const char * const pll_bist_groups[] = {
+ "gpio36",
+};
+static const char * const atest_tsens_groups[] = {
+ "gpio36",
+};
+static const char * const agera_pll_groups[] = {
+ "gpio37",
+};
+static const char * const usb_phy_groups[] = {
+ "gpio38",
+};
+static const char * const lpass_slimbus_groups[] = {
+ "gpio39", "gpio70", "gpio71", "gpio72",
+};
+static const char * const sd_write_groups[] = {
+ "gpio40",
+};
+static const char * const tsif1_error_groups[] = {
+ "gpio40",
+};
+static const char * const qup3_groups[] = {
+ "gpio41", "gpio42", "gpio43", "gpio44",
+};
+static const char * const qup6_groups[] = {
+ "gpio45", "gpio46", "gpio47", "gpio48",
+};
+static const char * const qup12_groups[] = {
+ "gpio49", "gpio50", "gpio51", "gpio52",
+};
+static const char * const qup10_groups[] = {
+ "gpio53", "gpio54", "gpio55", "gpio56",
+};
+static const char * const qua_mi2s_groups[] = {
+ "gpio57", "gpio58", "gpio59", "gpio60", "gpio61", "gpio62", "gpio63",
+};
+static const char * const gcc_gp1_groups[] = {
+ "gpio57", "gpio78",
+};
+static const char * const cri_trng0_groups[] = {
+ "gpio60",
+};
+static const char * const cri_trng1_groups[] = {
+ "gpio61",
+};
+static const char * const cri_trng_groups[] = {
+ "gpio62",
+};
+static const char * const pri_mi2s_groups[] = {
+ "gpio64", "gpio65", "gpio67", "gpio68",
+};
+static const char * const sp_cmu_groups[] = {
+ "gpio64",
+};
+static const char * const qup8_groups[] = {
+ "gpio65", "gpio66", "gpio67", "gpio68",
+};
+static const char * const pri_mi2s_ws_groups[] = {
+ "gpio66",
+};
+static const char * const spkr_i2s_groups[] = {
+ "gpio69", "gpio70", "gpio71", "gpio72",
+};
+static const char * const audio_ref_groups[] = {
+ "gpio69",
+};
+static const char * const tsense_pwm1_groups[] = {
+ "gpio71",
+};
+static const char * const tsense_pwm2_groups[] = {
+ "gpio71",
+};
+static const char * const btfm_slimbus_groups[] = {
+ "gpio73", "gpio74",
+};
+static const char * const atest_usb2_groups[] = {
+ "gpio73",
+};
+static const char * const ter_mi2s_groups[] = {
+ "gpio74", "gpio75", "gpio76", "gpio77", "gpio78",
+};
+static const char * const atest_usb23_groups[] = {
+ "gpio74",
+};
+static const char * const atest_usb22_groups[] = {
+ "gpio75",
+};
+static const char * const atest_usb21_groups[] = {
+ "gpio76",
+};
+static const char * const atest_usb20_groups[] = {
+ "gpio77",
+};
+static const char * const sec_mi2s_groups[] = {
+ "gpio79", "gpio80", "gpio81", "gpio82", "gpio83",
+};
+static const char * const qup15_groups[] = {
+ "gpio81", "gpio82", "gpio83", "gpio84",
+};
+static const char * const qup5_groups[] = {
+ "gpio85", "gpio86", "gpio87", "gpio88",
+};
+static const char * const tsif1_clk_groups[] = {
+ "gpio89",
+};
+static const char * const qup4_groups[] = {
+ "gpio89", "gpio90", "gpio91", "gpio92",
+};
+static const char * const qspi_cs_groups[] = {
+ "gpio89", "gpio90",
+};
+static const char * const tgu_ch3_groups[] = {
+ "gpio89",
+};
+static const char * const tsif1_en_groups[] = {
+ "gpio90",
+};
+static const char * const mdp_vsync0_groups[] = {
+ "gpio90",
+};
+static const char * const mdp_vsync1_groups[] = {
+ "gpio90",
+};
+static const char * const mdp_vsync2_groups[] = {
+ "gpio90",
+};
+static const char * const mdp_vsync3_groups[] = {
+ "gpio90",
+};
+static const char * const tgu_ch0_groups[] = {
+ "gpio90",
+};
+static const char * const tsif1_data_groups[] = {
+ "gpio91",
+};
+static const char * const sdc4_cmd_groups[] = {
+ "gpio91",
+};
+static const char * const qspi_data_groups[] = {
+ "gpio91", "gpio92", "gpio93", "gpio94",
+};
+static const char * const tgu_ch1_groups[] = {
+ "gpio91",
+};
+static const char * const tsif2_error_groups[] = {
+ "gpio92",
+};
+static const char * const sdc4_data_groups[] = {
+ "gpio92",
+ "gpio94",
+ "gpio95",
+ "gpio96",
+};
+static const char * const vfr_1_groups[] = {
+ "gpio92",
+};
+static const char * const tgu_ch2_groups[] = {
+ "gpio92",
+};
+static const char * const tsif2_clk_groups[] = {
+ "gpio93",
+};
+static const char * const sdc4_clk_groups[] = {
+ "gpio93",
+};
+static const char * const qup7_groups[] = {
+ "gpio93", "gpio94", "gpio95", "gpio96",
+};
+static const char * const tsif2_en_groups[] = {
+ "gpio94",
+};
+static const char * const tsif2_data_groups[] = {
+ "gpio95",
+};
+static const char * const qspi_clk_groups[] = {
+ "gpio95",
+};
+static const char * const tsif2_sync_groups[] = {
+ "gpio96",
+};
+static const char * const ldo_en_groups[] = {
+ "gpio97",
+};
+static const char * const ldo_update_groups[] = {
+ "gpio98",
+};
+static const char * const pci_e1_groups[] = {
+ "gpio102", "gpio103",
+};
+static const char * const prng_rosc_groups[] = {
+ "gpio102",
+};
+static const char * const uim2_data_groups[] = {
+ "gpio105",
+};
+static const char * const qup13_groups[] = {
+ "gpio105", "gpio106", "gpio107", "gpio108",
+};
+static const char * const uim2_clk_groups[] = {
+ "gpio106",
+};
+static const char * const uim2_reset_groups[] = {
+ "gpio107",
+};
+static const char * const uim2_present_groups[] = {
+ "gpio108",
+};
+static const char * const uim1_data_groups[] = {
+ "gpio109",
+};
+static const char * const uim1_clk_groups[] = {
+ "gpio110",
+};
+static const char * const uim1_reset_groups[] = {
+ "gpio111",
+};
+static const char * const uim1_present_groups[] = {
+ "gpio112",
+};
+static const char * const uim_batt_groups[] = {
+ "gpio113",
+};
+static const char * const edp_hot_groups[] = {
+ "gpio113",
+};
+static const char * const nav_pps_groups[] = {
+ "gpio114", "gpio114", "gpio115", "gpio115", "gpio128", "gpio128",
+ "gpio129", "gpio129", "gpio143", "gpio143",
+};
+static const char * const atest_char_groups[] = {
+ "gpio117", "gpio118", "gpio119", "gpio120", "gpio121",
+};
+static const char * const adsp_ext_groups[] = {
+ "gpio118",
+};
+static const char * const qlink_request_groups[] = {
+ "gpio130",
+};
+static const char * const qlink_enable_groups[] = {
+ "gpio131",
+};
+static const char * const pa_indicator_groups[] = {
+ "gpio135",
+};
+static const char * const mss_lte_groups[] = {
+ "gpio144", "gpio145",
+};
+static const char * const qup0_groups[] = {
+ "gpio0", "gpio1", "gpio2", "gpio3",
+};
+static const char * const gpio_groups[] = {
+ "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7",
+ "gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14",
+ "gpio15", "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21",
+ "gpio22", "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28",
+ "gpio29", "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35",
+ "gpio36", "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42",
+ "gpio43", "gpio44", "gpio45", "gpio46", "gpio47", "gpio48", "gpio49",
+ "gpio50", "gpio51", "gpio52", "gpio53", "gpio54", "gpio55", "gpio56",
+ "gpio57", "gpio58", "gpio59", "gpio60", "gpio61", "gpio62", "gpio63",
+ "gpio64", "gpio65", "gpio66", "gpio67", "gpio68", "gpio69", "gpio70",
+ "gpio71", "gpio72", "gpio73", "gpio74", "gpio75", "gpio76", "gpio77",
+ "gpio78", "gpio79", "gpio80", "gpio81", "gpio82", "gpio83", "gpio84",
+ "gpio85", "gpio86", "gpio87", "gpio88", "gpio89", "gpio90", "gpio91",
+ "gpio92", "gpio93", "gpio94", "gpio95", "gpio96", "gpio97", "gpio98",
+ "gpio99", "gpio100", "gpio101", "gpio102", "gpio103", "gpio104",
+ "gpio105", "gpio106", "gpio107", "gpio108", "gpio109", "gpio110",
+ "gpio111", "gpio112", "gpio113", "gpio114", "gpio115", "gpio116",
+ "gpio117", "gpio118", "gpio119", "gpio120", "gpio121", "gpio122",
+ "gpio123", "gpio124", "gpio125", "gpio126", "gpio127", "gpio128",
+ "gpio129", "gpio130", "gpio131", "gpio132", "gpio133", "gpio134",
+ "gpio135", "gpio136", "gpio137", "gpio138", "gpio139", "gpio140",
+ "gpio141", "gpio142", "gpio143", "gpio144", "gpio145", "gpio146",
+ "gpio147", "gpio148", "gpio149",
+};
+static const char * const qup9_groups[] = {
+ "gpio4", "gpio5", "gpio6", "gpio7",
+};
+static const char * const qdss_cti_groups[] = {
+ "gpio4", "gpio5", "gpio51", "gpio52", "gpio62", "gpio63", "gpio90",
+ "gpio91",
+};
+static const char * const ddr_pxi0_groups[] = {
+ "gpio6", "gpio7",
+};
+static const char * const ddr_bist_groups[] = {
+ "gpio7", "gpio8", "gpio9", "gpio10",
+};
+static const char * const atest_tsens2_groups[] = {
+ "gpio7",
+};
+static const char * const vsense_trigger_groups[] = {
+ "gpio7",
+};
+static const char * const atest_usb1_groups[] = {
+ "gpio7",
+};
+static const char * const qup_l4_groups[] = {
+ "gpio8", "gpio35", "gpio105", "gpio123",
+};
+static const char * const wlan1_adc1_groups[] = {
+ "gpio8",
+};
+static const char * const atest_usb13_groups[] = {
+ "gpio8",
+};
+static const char * const ddr_pxi1_groups[] = {
+ "gpio8", "gpio9",
+};
+static const char * const qup_l5_groups[] = {
+ "gpio9", "gpio36", "gpio106", "gpio124",
+};
+static const char * const wlan1_adc0_groups[] = {
+ "gpio9",
+};
+static const char * const atest_usb12_groups[] = {
+ "gpio9",
+};
+static const char * const mdp_vsync_groups[] = {
+ "gpio10", "gpio11", "gpio12", "gpio97", "gpio98",
+};
+static const char * const qup_l6_groups[] = {
+ "gpio10", "gpio37", "gpio107", "gpio125",
+};
+static const char * const wlan2_adc1_groups[] = {
+ "gpio10",
+};
+static const char * const atest_usb11_groups[] = {
+ "gpio10",
+};
+static const char * const ddr_pxi2_groups[] = {
+ "gpio10", "gpio11",
+};
+static const char * const edp_lcd_groups[] = {
+ "gpio11",
+};
+static const char * const dbg_out_groups[] = {
+ "gpio11",
+};
+static const char * const wlan2_adc0_groups[] = {
+ "gpio11",
+};
+static const char * const atest_usb10_groups[] = {
+ "gpio11",
+};
+static const char * const m_voc_groups[] = {
+ "gpio12",
+};
+static const char * const tsif1_sync_groups[] = {
+ "gpio12",
+};
+
+static const struct msm_function sdm845_functions[] = {
+ FUNCTION(gpio),
+ FUNCTION(adsp_ext),
+ FUNCTION(agera_pll),
+ FUNCTION(atest_char),
+ FUNCTION(atest_tsens),
+ FUNCTION(atest_tsens2),
+ FUNCTION(atest_usb1),
+ FUNCTION(atest_usb10),
+ FUNCTION(atest_usb11),
+ FUNCTION(atest_usb12),
+ FUNCTION(atest_usb13),
+ FUNCTION(atest_usb2),
+ FUNCTION(atest_usb20),
+ FUNCTION(atest_usb21),
+ FUNCTION(atest_usb22),
+ FUNCTION(atest_usb23),
+ FUNCTION(audio_ref),
+ FUNCTION(btfm_slimbus),
+ FUNCTION(cam_mclk),
+ FUNCTION(cci_async),
+ FUNCTION(cci_i2c),
+ FUNCTION(cci_timer0),
+ FUNCTION(cci_timer1),
+ FUNCTION(cci_timer2),
+ FUNCTION(cci_timer3),
+ FUNCTION(cci_timer4),
+ FUNCTION(cri_trng),
+ FUNCTION(cri_trng0),
+ FUNCTION(cri_trng1),
+ FUNCTION(dbg_out),
+ FUNCTION(ddr_bist),
+ FUNCTION(ddr_pxi0),
+ FUNCTION(ddr_pxi1),
+ FUNCTION(ddr_pxi2),
+ FUNCTION(ddr_pxi3),
+ FUNCTION(edp_hot),
+ FUNCTION(edp_lcd),
+ FUNCTION(gcc_gp1),
+ FUNCTION(gcc_gp2),
+ FUNCTION(gcc_gp3),
+ FUNCTION(jitter_bist),
+ FUNCTION(ldo_en),
+ FUNCTION(ldo_update),
+ FUNCTION(lpass_slimbus),
+ FUNCTION(m_voc),
+ FUNCTION(mdp_vsync),
+ FUNCTION(mdp_vsync0),
+ FUNCTION(mdp_vsync1),
+ FUNCTION(mdp_vsync2),
+ FUNCTION(mdp_vsync3),
+ FUNCTION(mss_lte),
+ FUNCTION(nav_pps),
+ FUNCTION(pa_indicator),
+ FUNCTION(pci_e0),
+ FUNCTION(pci_e1),
+ FUNCTION(phase_flag),
+ FUNCTION(pll_bist),
+ FUNCTION(pll_bypassnl),
+ FUNCTION(pll_reset),
+ FUNCTION(pri_mi2s),
+ FUNCTION(pri_mi2s_ws),
+ FUNCTION(prng_rosc),
+ FUNCTION(qdss_cti),
+ FUNCTION(qdss),
+ FUNCTION(qlink_enable),
+ FUNCTION(qlink_request),
+ FUNCTION(qspi_clk),
+ FUNCTION(qspi_cs),
+ FUNCTION(qspi_data),
+ FUNCTION(qua_mi2s),
+ FUNCTION(qup0),
+ FUNCTION(qup1),
+ FUNCTION(qup10),
+ FUNCTION(qup11),
+ FUNCTION(qup12),
+ FUNCTION(qup13),
+ FUNCTION(qup14),
+ FUNCTION(qup15),
+ FUNCTION(qup2),
+ FUNCTION(qup3),
+ FUNCTION(qup4),
+ FUNCTION(qup5),
+ FUNCTION(qup6),
+ FUNCTION(qup7),
+ FUNCTION(qup8),
+ FUNCTION(qup9),
+ FUNCTION(qup_l4),
+ FUNCTION(qup_l5),
+ FUNCTION(qup_l6),
+ FUNCTION(sd_write),
+ FUNCTION(sdc4_clk),
+ FUNCTION(sdc4_cmd),
+ FUNCTION(sdc4_data),
+ FUNCTION(sec_mi2s),
+ FUNCTION(sp_cmu),
+ FUNCTION(spkr_i2s),
+ FUNCTION(ter_mi2s),
+ FUNCTION(tgu_ch0),
+ FUNCTION(tgu_ch1),
+ FUNCTION(tgu_ch2),
+ FUNCTION(tgu_ch3),
+ FUNCTION(tsense_pwm1),
+ FUNCTION(tsense_pwm2),
+ FUNCTION(tsif1_clk),
+ FUNCTION(tsif1_data),
+ FUNCTION(tsif1_en),
+ FUNCTION(tsif1_error),
+ FUNCTION(tsif1_sync),
+ FUNCTION(tsif2_clk),
+ FUNCTION(tsif2_data),
+ FUNCTION(tsif2_en),
+ FUNCTION(tsif2_error),
+ FUNCTION(tsif2_sync),
+ FUNCTION(uim1_clk),
+ FUNCTION(uim1_data),
+ FUNCTION(uim1_present),
+ FUNCTION(uim1_reset),
+ FUNCTION(uim2_clk),
+ FUNCTION(uim2_data),
+ FUNCTION(uim2_present),
+ FUNCTION(uim2_reset),
+ FUNCTION(uim_batt),
+ FUNCTION(usb_phy),
+ FUNCTION(vfr_1),
+ FUNCTION(vsense_trigger),
+ FUNCTION(wlan1_adc0),
+ FUNCTION(wlan1_adc1),
+ FUNCTION(wlan2_adc0),
+ FUNCTION(wlan2_adc1),
+};
+
+/* Every pin is maintained as a single group, and missing or non-existing pin
+ * would be maintained as dummy group to synchronize pin group index with
+ * pin descriptor registered with pinctrl core.
+ * Clients would not be able to request these dummy pin groups.
+ */
+static const struct msm_pingroup sdm845_groups[] = {
+ PINGROUP(0, EAST, qup0, _, _, _, _, _, _, _, _, _),
+ PINGROUP(1, EAST, qup0, _, _, _, _, _, _, _, _, _),
+ PINGROUP(2, EAST, qup0, _, _, _, _, _, _, _, _, _),
+ PINGROUP(3, EAST, qup0, _, _, _, _, _, _, _, _, _),
+ PINGROUP(4, NORTH, qup9, qdss_cti, _, _, _, _, _, _, _, _),
+ PINGROUP(5, NORTH, qup9, qdss_cti, _, _, _, _, _, _, _, _),
+ PINGROUP(6, NORTH, qup9, _, ddr_pxi0, _, _, _, _, _, _, _),
+ PINGROUP(7, NORTH, qup9, ddr_bist, _, atest_tsens2, vsense_trigger, atest_usb1, ddr_pxi0, _, _, _),
+ PINGROUP(8, EAST, qup_l4, _, ddr_bist, _, _, wlan1_adc1, atest_usb13, ddr_pxi1, _, _),
+ PINGROUP(9, EAST, qup_l5, ddr_bist, _, wlan1_adc0, atest_usb12, ddr_pxi1, _, _, _, _),
+ PINGROUP(10, EAST, mdp_vsync, qup_l6, ddr_bist, wlan2_adc1, atest_usb11, ddr_pxi2, _, _, _, _),
+ PINGROUP(11, EAST, mdp_vsync, edp_lcd, dbg_out, wlan2_adc0, atest_usb10, ddr_pxi2, _, _, _, _),
+ PINGROUP(12, SOUTH, mdp_vsync, m_voc, tsif1_sync, ddr_pxi3, _, _, _, _, _, _),
+ PINGROUP(13, SOUTH, cam_mclk, pll_bypassnl, qdss, ddr_pxi3, _, _, _, _, _, _),
+ PINGROUP(14, SOUTH, cam_mclk, pll_reset, qdss, _, _, _, _, _, _, _),
+ PINGROUP(15, SOUTH, cam_mclk, qdss, _, _, _, _, _, _, _, _),
+ PINGROUP(16, SOUTH, cam_mclk, qdss, _, _, _, _, _, _, _, _),
+ PINGROUP(17, SOUTH, cci_i2c, qup1, qdss, _, _, _, _, _, _, _),
+ PINGROUP(18, SOUTH, cci_i2c, qup1, _, qdss, _, _, _, _, _, _),
+ PINGROUP(19, SOUTH, cci_i2c, qup1, _, qdss, _, _, _, _, _, _),
+ PINGROUP(20, SOUTH, cci_i2c, qup1, _, qdss, _, _, _, _, _, _),
+ PINGROUP(21, SOUTH, cci_timer0, gcc_gp2, qdss, _, _, _, _, _, _, _),
+ PINGROUP(22, SOUTH, cci_timer1, gcc_gp3, qdss, _, _, _, _, _, _, _),
+ PINGROUP(23, SOUTH, cci_timer2, qdss, _, _, _, _, _, _, _, _),
+ PINGROUP(24, SOUTH, cci_timer3, cci_async, qdss, _, _, _, _, _, _, _),
+ PINGROUP(25, SOUTH, cci_timer4, cci_async, qdss, _, _, _, _, _, _, _),
+ PINGROUP(26, SOUTH, cci_async, qdss, _, _, _, _, _, _, _, _),
+ PINGROUP(27, EAST, qup2, qdss, _, _, _, _, _, _, _, _),
+ PINGROUP(28, EAST, qup2, qdss, _, _, _, _, _, _, _, _),
+ PINGROUP(29, EAST, qup2, _, phase_flag, qdss, _, _, _, _, _, _),
+ PINGROUP(30, EAST, qup2, phase_flag, qdss, _, _, _, _, _, _, _),
+ PINGROUP(31, NORTH, qup11, qup14, _, _, _, _, _, _, _, _),
+ PINGROUP(32, NORTH, qup11, qup14, _, _, _, _, _, _, _, _),
+ PINGROUP(33, NORTH, qup11, qup14, _, _, _, _, _, _, _, _),
+ PINGROUP(34, NORTH, qup11, qup14, _, _, _, _, _, _, _, _),
+ PINGROUP(35, SOUTH, pci_e0, qup_l4, jitter_bist, _, _, _, _, _, _, _),
+ PINGROUP(36, SOUTH, pci_e0, qup_l5, pll_bist, _, atest_tsens, _, _, _, _, _),
+ PINGROUP(37, SOUTH, qup_l6, agera_pll, _, _, _, _, _, _, _, _),
+ PINGROUP(38, NORTH, usb_phy, _, _, _, _, _, _, _, _, _),
+ PINGROUP(39, EAST, lpass_slimbus, _, _, _, _, _, _, _, _, _),
+ PINGROUP(40, SOUTH, sd_write, tsif1_error, _, _, _, _, _, _, _, _),
+ PINGROUP(41, EAST, qup3, _, qdss, _, _, _, _, _, _, _),
+ PINGROUP(42, EAST, qup3, _, qdss, _, _, _, _, _, _, _),
+ PINGROUP(43, EAST, qup3, _, qdss, _, _, _, _, _, _, _),
+ PINGROUP(44, EAST, qup3, _, qdss, _, _, _, _, _, _, _),
+ PINGROUP(45, EAST, qup6, _, _, _, _, _, _, _, _, _),
+ PINGROUP(46, EAST, qup6, _, _, _, _, _, _, _, _, _),
+ PINGROUP(47, EAST, qup6, _, _, _, _, _, _, _, _, _),
+ PINGROUP(48, EAST, qup6, _, _, _, _, _, _, _, _, _),
+ PINGROUP(49, NORTH, qup12, _, _, _, _, _, _, _, _, _),
+ PINGROUP(50, NORTH, qup12, _, _, _, _, _, _, _, _, _),
+ PINGROUP(51, NORTH, qup12, qdss_cti, _, _, _, _, _, _, _, _),
+ PINGROUP(52, NORTH, qup12, phase_flag, qdss_cti, _, _, _, _, _, _, _),
+ PINGROUP(53, NORTH, qup10, phase_flag, _, _, _, _, _, _, _, _),
+ PINGROUP(54, NORTH, qup10, _, phase_flag, _, _, _, _, _, _, _),
+ PINGROUP(55, NORTH, qup10, phase_flag, _, _, _, _, _, _, _, _),
+ PINGROUP(56, NORTH, qup10, phase_flag, _, _, _, _, _, _, _, _),
+ PINGROUP(57, NORTH, qua_mi2s, gcc_gp1, phase_flag, _, _, _, _, _, _, _),
+ PINGROUP(58, NORTH, qua_mi2s, gcc_gp2, phase_flag, _, _, _, _, _, _, _),
+ PINGROUP(59, NORTH, qua_mi2s, gcc_gp3, phase_flag, _, _, _, _, _, _, _),
+ PINGROUP(60, NORTH, qua_mi2s, cri_trng0, phase_flag, _, _, _, _, _, _, _),
+ PINGROUP(61, NORTH, qua_mi2s, cri_trng1, phase_flag, _, _, _, _, _, _, _),
+ PINGROUP(62, NORTH, qua_mi2s, cri_trng, phase_flag, qdss_cti, _, _, _, _, _, _),
+ PINGROUP(63, NORTH, qua_mi2s, _, phase_flag, qdss_cti, _, _, _, _, _, _),
+ PINGROUP(64, NORTH, pri_mi2s, sp_cmu, phase_flag, _, _, _, _, _, _, _),
+ PINGROUP(65, NORTH, pri_mi2s, qup8, _, _, _, _, _, _, _, _),
+ PINGROUP(66, NORTH, pri_mi2s_ws, qup8, _, _, _, _, _, _, _, _),
+ PINGROUP(67, NORTH, pri_mi2s, qup8, _, _, _, _, _, _, _, _),
+ PINGROUP(68, NORTH, pri_mi2s, qup8, _, _, _, _, _, _, _, _),
+ PINGROUP(69, EAST, spkr_i2s, audio_ref, _, _, _, _, _, _, _, _),
+ PINGROUP(70, EAST, lpass_slimbus, spkr_i2s, _, _, _, _, _, _, _, _),
+ PINGROUP(71, EAST, lpass_slimbus, spkr_i2s, tsense_pwm1, tsense_pwm2, _, _, _, _, _, _),
+ PINGROUP(72, EAST, lpass_slimbus, spkr_i2s, _, _, _, _, _, _, _, _),
+ PINGROUP(73, EAST, btfm_slimbus, atest_usb2, _, _, _, _, _, _, _, _),
+ PINGROUP(74, EAST, btfm_slimbus, ter_mi2s, phase_flag, atest_usb23, _, _, _, _, _, _),
+ PINGROUP(75, EAST, ter_mi2s, phase_flag, qdss, atest_usb22, _, _, _, _, _, _),
+ PINGROUP(76, EAST, ter_mi2s, phase_flag, qdss, atest_usb21, _, _, _, _, _, _),
+ PINGROUP(77, EAST, ter_mi2s, phase_flag, qdss, atest_usb20, _, _, _, _, _, _),
+ PINGROUP(78, EAST, ter_mi2s, gcc_gp1, _, _, _, _, _, _, _, _),
+ PINGROUP(79, NORTH, sec_mi2s, _, _, qdss, _, _, _, _, _, _),
+ PINGROUP(80, NORTH, sec_mi2s, _, qdss, _, _, _, _, _, _, _),
+ PINGROUP(81, NORTH, sec_mi2s, qup15, _, _, _, _, _, _, _, _),
+ PINGROUP(82, NORTH, sec_mi2s, qup15, _, _, _, _, _, _, _, _),
+ PINGROUP(83, NORTH, sec_mi2s, qup15, _, _, _, _, _, _, _, _),
+ PINGROUP(84, NORTH, qup15, _, _, _, _, _, _, _, _, _),
+ PINGROUP(85, EAST, qup5, _, _, _, _, _, _, _, _, _),
+ PINGROUP(86, EAST, qup5, _, _, _, _, _, _, _, _, _),
+ PINGROUP(87, EAST, qup5, _, _, _, _, _, _, _, _, _),
+ PINGROUP(88, EAST, qup5, _, _, _, _, _, _, _, _, _),
+ PINGROUP(89, SOUTH, tsif1_clk, qup4, qspi_cs, tgu_ch3, phase_flag, _, _, _, _, _),
+ PINGROUP(90, SOUTH, tsif1_en, mdp_vsync0, qup4, qspi_cs, mdp_vsync1,
+ mdp_vsync2, mdp_vsync3, tgu_ch0, phase_flag, qdss_cti),
+ PINGROUP(91, SOUTH, tsif1_data, sdc4_cmd, qup4, qspi_data, tgu_ch1, _, qdss_cti, _, _, _),
+ PINGROUP(92, SOUTH, tsif2_error, sdc4_data, qup4, qspi_data, vfr_1, tgu_ch2, _, _, _, _),
+ PINGROUP(93, SOUTH, tsif2_clk, sdc4_clk, qup7, qspi_data, _, qdss, _, _, _, _),
+ PINGROUP(94, SOUTH, tsif2_en, sdc4_data, qup7, qspi_data, _, _, _, _, _, _),
+ PINGROUP(95, SOUTH, tsif2_data, sdc4_data, qup7, qspi_clk, _, _, _, _, _, _),
+ PINGROUP(96, SOUTH, tsif2_sync, sdc4_data, qup7, phase_flag, _, _, _, _, _, _),
+ PINGROUP(97, NORTH, _, _, mdp_vsync, ldo_en, _, _, _, _, _, _),
+ PINGROUP(98, NORTH, _, mdp_vsync, ldo_update, _, _, _, _, _, _, _),
+ PINGROUP(99, NORTH, phase_flag, _, _, _, _, _, _, _, _, _),
+ PINGROUP(100, NORTH, phase_flag, _, _, _, _, _, _, _, _, _),
+ PINGROUP(101, NORTH, _, _, _, _, _, _, _, _, _, _),
+ PINGROUP(102, NORTH, pci_e1, prng_rosc, _, _, _, _, _, _, _, _),
+ PINGROUP(103, NORTH, pci_e1, phase_flag, _, _, _, _, _, _, _, _),
+ PINGROUP(104, NORTH, _, _, _, _, _, _, _, _, _, _),
+ PINGROUP(105, NORTH, uim2_data, qup13, qup_l4, _, _, _, _, _, _, _),
+ PINGROUP(106, NORTH, uim2_clk, qup13, qup_l5, _, _, _, _, _, _, _),
+ PINGROUP(107, NORTH, uim2_reset, qup13, qup_l6, _, _, _, _, _, _, _),
+ PINGROUP(108, NORTH, uim2_present, qup13, _, _, _, _, _, _, _, _),
+ PINGROUP(109, NORTH, uim1_data, _, _, _, _, _, _, _, _, _),
+ PINGROUP(110, NORTH, uim1_clk, _, _, _, _, _, _, _, _, _),
+ PINGROUP(111, NORTH, uim1_reset, _, _, _, _, _, _, _, _, _),
+ PINGROUP(112, NORTH, uim1_present, _, _, _, _, _, _, _, _, _),
+ PINGROUP(113, NORTH, uim_batt, edp_hot, _, _, _, _, _, _, _, _),
+ PINGROUP(114, NORTH, _, nav_pps, nav_pps, _, _, _, _, _, _, _),
+ PINGROUP(115, NORTH, _, nav_pps, nav_pps, _, _, _, _, _, _, _),
+ PINGROUP(116, NORTH, _, _, _, _, _, _, _, _, _, _),
+ PINGROUP(117, NORTH, _, qdss, atest_char, _, _, _, _, _, _, _),
+ PINGROUP(118, NORTH, adsp_ext, _, qdss, atest_char, _, _, _, _, _, _),
+ PINGROUP(119, NORTH, _, qdss, atest_char, _, _, _, _, _, _, _),
+ PINGROUP(120, NORTH, _, qdss, atest_char, _, _, _, _, _, _, _),
+ PINGROUP(121, NORTH, _, qdss, atest_char, _, _, _, _, _, _, _),
+ PINGROUP(122, EAST, _, qdss, _, _, _, _, _, _, _, _),
+ PINGROUP(123, EAST, qup_l4, _, qdss, _, _, _, _, _, _, _),
+ PINGROUP(124, EAST, qup_l5, _, qdss, _, _, _, _, _, _, _),
+ PINGROUP(125, EAST, qup_l6, _, _, _, _, _, _, _, _, _),
+ PINGROUP(126, EAST, _, _, _, _, _, _, _, _, _, _),
+ PINGROUP(127, NORTH, _, _, _, _, _, _, _, _, _, _),
+ PINGROUP(128, NORTH, nav_pps, nav_pps, _, _, _, _, _, _, _, _),
+ PINGROUP(129, NORTH, nav_pps, nav_pps, _, _, _, _, _, _, _, _),
+ PINGROUP(130, NORTH, qlink_request, _, _, _, _, _, _, _, _, _),
+ PINGROUP(131, NORTH, qlink_enable, _, _, _, _, _, _, _, _, _),
+ PINGROUP(132, NORTH, _, _, _, _, _, _, _, _, _, _),
+ PINGROUP(133, NORTH, _, _, _, _, _, _, _, _, _, _),
+ PINGROUP(134, NORTH, _, _, _, _, _, _, _, _, _, _),
+ PINGROUP(135, NORTH, _, pa_indicator, _, _, _, _, _, _, _, _),
+ PINGROUP(136, NORTH, _, _, _, _, _, _, _, _, _, _),
+ PINGROUP(137, NORTH, _, _, phase_flag, _, _, _, _, _, _, _),
+ PINGROUP(138, NORTH, _, _, phase_flag, _, _, _, _, _, _, _),
+ PINGROUP(139, NORTH, _, phase_flag, _, _, _, _, _, _, _, _),
+ PINGROUP(140, NORTH, _, _, phase_flag, _, _, _, _, _, _, _),
+ PINGROUP(141, NORTH, _, phase_flag, _, _, _, _, _, _, _, _),
+ PINGROUP(142, NORTH, _, phase_flag, _, _, _, _, _, _, _, _),
+ PINGROUP(143, NORTH, _, nav_pps, nav_pps, _, phase_flag, _, _, _, _, _),
+ PINGROUP(144, NORTH, mss_lte, _, _, _, _, _, _, _, _, _),
+ PINGROUP(145, NORTH, mss_lte, _, _, _, _, _, _, _, _, _),
+ PINGROUP(146, NORTH, _, _, _, _, _, _, _, _, _, _),
+ PINGROUP(147, NORTH, _, _, _, _, _, _, _, _, _, _),
+ PINGROUP(148, NORTH, _, _, _, _, _, _, _, _, _, _),
+ PINGROUP(149, NORTH, _, _, _, _, _, _, _, _, _, _),
+ SDC_QDSD_PINGROUP(sdc2_clk, 0x99a000, 14, 6),
+ SDC_QDSD_PINGROUP(sdc2_cmd, 0x99a000, 11, 3),
+ SDC_QDSD_PINGROUP(sdc2_data, 0x99a000, 9, 0),
+ UFS_RESET(ufs_reset, 0x99f000),
+};
+
+static const struct msm_pinctrl_soc_data sdm845_pinctrl = {
+ .pins = sdm845_pins,
+ .npins = ARRAY_SIZE(sdm845_pins),
+ .functions = sdm845_functions,
+ .nfunctions = ARRAY_SIZE(sdm845_functions),
+ .groups = sdm845_groups,
+ .ngroups = ARRAY_SIZE(sdm845_groups),
+ .ngpios = 150,
+};
+
+static int sdm845_pinctrl_probe(struct platform_device *pdev)
+{
+ return msm_pinctrl_probe(pdev, &sdm845_pinctrl);
+}
+
+static const struct of_device_id sdm845_pinctrl_of_match[] = {
+ { .compatible = "qcom,sdm845-pinctrl", },
+ { },
+};
+
+static struct platform_driver sdm845_pinctrl_driver = {
+ .driver = {
+ .name = "sdm845-pinctrl",
+ .of_match_table = sdm845_pinctrl_of_match,
+ },
+ .probe = sdm845_pinctrl_probe,
+ .remove = msm_pinctrl_remove,
+};
+
+static int __init sdm845_pinctrl_init(void)
+{
+ return platform_driver_register(&sdm845_pinctrl_driver);
+}
+arch_initcall(sdm845_pinctrl_init);
+
+static void __exit sdm845_pinctrl_exit(void)
+{
+ platform_driver_unregister(&sdm845_pinctrl_driver);
+}
+module_exit(sdm845_pinctrl_exit);
+
+MODULE_DESCRIPTION("QTI sdm845 pinctrl driver");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, sdm845_pinctrl_of_match);
diff --git a/drivers/pinctrl/sh-pfc/Kconfig b/drivers/pinctrl/sh-pfc/Kconfig
index 4ed3761418f9..c11b789ec583 100644
--- a/drivers/pinctrl/sh-pfc/Kconfig
+++ b/drivers/pinctrl/sh-pfc/Kconfig
@@ -89,11 +89,21 @@ config PINCTRL_PFC_R8A7796
depends on ARCH_R8A7796
select PINCTRL_SH_PFC
+config PINCTRL_PFC_R8A77965
+ def_bool y
+ depends on ARCH_R8A77965
+ select PINCTRL_SH_PFC
+
config PINCTRL_PFC_R8A77970
def_bool y
depends on ARCH_R8A77970
select PINCTRL_SH_PFC
+config PINCTRL_PFC_R8A77980
+ def_bool y
+ depends on ARCH_R8A77980
+ select PINCTRL_SH_PFC
+
config PINCTRL_PFC_R8A77995
def_bool y
depends on ARCH_R8A77995
diff --git a/drivers/pinctrl/sh-pfc/Makefile b/drivers/pinctrl/sh-pfc/Makefile
index 22e758ce1fc2..463775f28cf1 100644
--- a/drivers/pinctrl/sh-pfc/Makefile
+++ b/drivers/pinctrl/sh-pfc/Makefile
@@ -16,7 +16,9 @@ obj-$(CONFIG_PINCTRL_PFC_R8A7794) += pfc-r8a7794.o
obj-$(CONFIG_PINCTRL_PFC_R8A7795) += pfc-r8a7795.o
obj-$(CONFIG_PINCTRL_PFC_R8A7795) += pfc-r8a7795-es1.o
obj-$(CONFIG_PINCTRL_PFC_R8A7796) += pfc-r8a7796.o
+obj-$(CONFIG_PINCTRL_PFC_R8A77965) += pfc-r8a77965.o
obj-$(CONFIG_PINCTRL_PFC_R8A77970) += pfc-r8a77970.o
+obj-$(CONFIG_PINCTRL_PFC_R8A77980) += pfc-r8a77980.o
obj-$(CONFIG_PINCTRL_PFC_R8A77995) += pfc-r8a77995.o
obj-$(CONFIG_PINCTRL_PFC_SH7203) += pfc-sh7203.o
obj-$(CONFIG_PINCTRL_PFC_SH7264) += pfc-sh7264.o
diff --git a/drivers/pinctrl/sh-pfc/core.c b/drivers/pinctrl/sh-pfc/core.c
index e9eb7a7c6fac..74861b7b5b0d 100644
--- a/drivers/pinctrl/sh-pfc/core.c
+++ b/drivers/pinctrl/sh-pfc/core.c
@@ -557,12 +557,24 @@ static const struct of_device_id sh_pfc_of_table[] = {
.data = &r8a7796_pinmux_info,
},
#endif
+#ifdef CONFIG_PINCTRL_PFC_R8A77965
+ {
+ .compatible = "renesas,pfc-r8a77965",
+ .data = &r8a77965_pinmux_info,
+ },
+#endif
#ifdef CONFIG_PINCTRL_PFC_R8A77970
{
.compatible = "renesas,pfc-r8a77970",
.data = &r8a77970_pinmux_info,
},
#endif
+#ifdef CONFIG_PINCTRL_PFC_R8A77980
+ {
+ .compatible = "renesas,pfc-r8a77980",
+ .data = &r8a77980_pinmux_info,
+ },
+#endif
#ifdef CONFIG_PINCTRL_PFC_R8A77995
{
.compatible = "renesas,pfc-r8a77995",
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7790.c b/drivers/pinctrl/sh-pfc/pfc-r8a7790.c
index b769c05480da..f6332f247368 100644
--- a/drivers/pinctrl/sh-pfc/pfc-r8a7790.c
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a7790.c
@@ -1835,8 +1835,8 @@ static const unsigned int avb_mii_pins[] = {
RCAR_GP_PIN(2, 2),
RCAR_GP_PIN(2, 7), RCAR_GP_PIN(2, 8), RCAR_GP_PIN(2, 9),
- RCAR_GP_PIN(2, 10), RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 10),
- RCAR_GP_PIN(3, 12),
+ RCAR_GP_PIN(2, 10), RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9),
+ RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 12),
};
static const unsigned int avb_mii_mux[] = {
AVB_TXD0_MARK, AVB_TXD1_MARK, AVB_TXD2_MARK,
@@ -1846,8 +1846,8 @@ static const unsigned int avb_mii_mux[] = {
AVB_RXD3_MARK,
AVB_RX_ER_MARK, AVB_RX_CLK_MARK, AVB_RX_DV_MARK,
- AVB_CRS_MARK, AVB_TX_EN_MARK, AVB_TX_CLK_MARK,
- AVB_COL_MARK,
+ AVB_CRS_MARK, AVB_TX_EN_MARK, AVB_TX_ER_MARK,
+ AVB_TX_CLK_MARK, AVB_COL_MARK,
};
static const unsigned int avb_gmii_pins[] = {
RCAR_GP_PIN(0, 8), RCAR_GP_PIN(0, 9), RCAR_GP_PIN(0, 10),
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7795-es1.c b/drivers/pinctrl/sh-pfc/pfc-r8a7795-es1.c
index 292e35d4d2f4..82a1c411c952 100644
--- a/drivers/pinctrl/sh-pfc/pfc-r8a7795-es1.c
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a7795-es1.c
@@ -1,7 +1,7 @@
/*
* R8A7795 ES1.x processor support - PFC hardware block.
*
- * Copyright (C) 2015 Renesas Electronics Corporation
+ * Copyright (C) 2015-2017 Renesas Electronics Corporation
*
* 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
@@ -1652,11 +1652,11 @@ static const unsigned int avb_phy_int_pins[] = {
static const unsigned int avb_phy_int_mux[] = {
AVB_PHY_INT_MARK,
};
-static const unsigned int avb_mdc_pins[] = {
+static const unsigned int avb_mdio_pins[] = {
/* AVB_MDC, AVB_MDIO */
RCAR_GP_PIN(2, 9), PIN_NUMBER('A', 9),
};
-static const unsigned int avb_mdc_mux[] = {
+static const unsigned int avb_mdio_mux[] = {
AVB_MDC_MARK, AVB_MDIO_MARK,
};
static const unsigned int avb_mii_pins[] = {
@@ -2067,6 +2067,22 @@ static const unsigned int du_disp_pins[] = {
static const unsigned int du_disp_mux[] = {
DU_DISP_MARK,
};
+/* - HDMI ------------------------------------------------------------------- */
+static const unsigned int hdmi0_cec_pins[] = {
+ /* HDMI0_CEC */
+ RCAR_GP_PIN(7, 2),
+};
+static const unsigned int hdmi0_cec_mux[] = {
+ HDMI0_CEC_MARK,
+};
+static const unsigned int hdmi1_cec_pins[] = {
+ /* HDMI1_CEC */
+ RCAR_GP_PIN(7, 3),
+};
+static const unsigned int hdmi1_cec_mux[] = {
+ HDMI1_CEC_MARK,
+};
+
/* - HSCIF0 ----------------------------------------------------------------- */
static const unsigned int hscif0_data_pins[] = {
/* RX, TX */
@@ -3750,6 +3766,36 @@ static const unsigned int ssi9_ctrl_b_mux[] = {
SSI_SCK9_B_MARK, SSI_WS9_B_MARK,
};
+/* - TMU -------------------------------------------------------------------- */
+static const unsigned int tmu_tclk1_a_pins[] = {
+ /* TCLK */
+ RCAR_GP_PIN(6, 23),
+};
+static const unsigned int tmu_tclk1_a_mux[] = {
+ TCLK1_A_MARK,
+};
+static const unsigned int tmu_tclk1_b_pins[] = {
+ /* TCLK */
+ RCAR_GP_PIN(5, 19),
+};
+static const unsigned int tmu_tclk1_b_mux[] = {
+ TCLK1_B_MARK,
+};
+static const unsigned int tmu_tclk2_a_pins[] = {
+ /* TCLK */
+ RCAR_GP_PIN(6, 19),
+};
+static const unsigned int tmu_tclk2_a_mux[] = {
+ TCLK2_A_MARK,
+};
+static const unsigned int tmu_tclk2_b_pins[] = {
+ /* TCLK */
+ RCAR_GP_PIN(6, 28),
+};
+static const unsigned int tmu_tclk2_b_mux[] = {
+ TCLK2_B_MARK,
+};
+
/* - USB0 ------------------------------------------------------------------- */
static const unsigned int usb0_pins[] = {
/* PWEN, OVC */
@@ -3813,7 +3859,8 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(avb_link),
SH_PFC_PIN_GROUP(avb_magic),
SH_PFC_PIN_GROUP(avb_phy_int),
- SH_PFC_PIN_GROUP(avb_mdc),
+ SH_PFC_PIN_GROUP_ALIAS(avb_mdc, avb_mdio), /* Deprecated */
+ SH_PFC_PIN_GROUP(avb_mdio),
SH_PFC_PIN_GROUP(avb_mii),
SH_PFC_PIN_GROUP(avb_avtp_pps),
SH_PFC_PIN_GROUP(avb_avtp_match_a),
@@ -3865,6 +3912,8 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(du_oddf),
SH_PFC_PIN_GROUP(du_cde),
SH_PFC_PIN_GROUP(du_disp),
+ SH_PFC_PIN_GROUP(hdmi0_cec),
+ SH_PFC_PIN_GROUP(hdmi1_cec),
SH_PFC_PIN_GROUP(hscif0_data),
SH_PFC_PIN_GROUP(hscif0_clk),
SH_PFC_PIN_GROUP(hscif0_ctrl),
@@ -4095,6 +4144,10 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(ssi9_data_b),
SH_PFC_PIN_GROUP(ssi9_ctrl_a),
SH_PFC_PIN_GROUP(ssi9_ctrl_b),
+ SH_PFC_PIN_GROUP(tmu_tclk1_a),
+ SH_PFC_PIN_GROUP(tmu_tclk1_b),
+ SH_PFC_PIN_GROUP(tmu_tclk2_a),
+ SH_PFC_PIN_GROUP(tmu_tclk2_b),
SH_PFC_PIN_GROUP(usb0),
SH_PFC_PIN_GROUP(usb1),
SH_PFC_PIN_GROUP(usb2),
@@ -4126,7 +4179,8 @@ static const char * const avb_groups[] = {
"avb_link",
"avb_magic",
"avb_phy_int",
- "avb_mdc",
+ "avb_mdc", /* Deprecated, please use "avb_mdio" instead */
+ "avb_mdio",
"avb_mii",
"avb_avtp_pps",
"avb_avtp_match_a",
@@ -4210,6 +4264,14 @@ static const char * const du_groups[] = {
"du_disp",
};
+static const char * const hdmi0_groups[] = {
+ "hdmi0_cec",
+};
+
+static const char * const hdmi1_groups[] = {
+ "hdmi1_cec",
+};
+
static const char * const hscif0_groups[] = {
"hscif0_data",
"hscif0_clk",
@@ -4545,6 +4607,13 @@ static const char * const ssi_groups[] = {
"ssi9_ctrl_b",
};
+static const char * const tmu_groups[] = {
+ "tmu_tclk1_a",
+ "tmu_tclk1_b",
+ "tmu_tclk2_a",
+ "tmu_tclk2_b",
+};
+
static const char * const usb0_groups[] = {
"usb0",
};
@@ -4578,6 +4647,8 @@ static const struct sh_pfc_function pinmux_functions[] = {
SH_PFC_FUNCTION(drif2),
SH_PFC_FUNCTION(drif3),
SH_PFC_FUNCTION(du),
+ SH_PFC_FUNCTION(hdmi0),
+ SH_PFC_FUNCTION(hdmi1),
SH_PFC_FUNCTION(hscif0),
SH_PFC_FUNCTION(hscif1),
SH_PFC_FUNCTION(hscif2),
@@ -4613,6 +4684,7 @@ static const struct sh_pfc_function pinmux_functions[] = {
SH_PFC_FUNCTION(sdhi2),
SH_PFC_FUNCTION(sdhi3),
SH_PFC_FUNCTION(ssi),
+ SH_PFC_FUNCTION(tmu),
SH_PFC_FUNCTION(usb0),
SH_PFC_FUNCTION(usb1),
SH_PFC_FUNCTION(usb2),
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7795.c b/drivers/pinctrl/sh-pfc/pfc-r8a7795.c
index 35951e7b89d2..7100a2dd65f8 100644
--- a/drivers/pinctrl/sh-pfc/pfc-r8a7795.c
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a7795.c
@@ -1,7 +1,7 @@
/*
* R8A7795 ES2.0+ processor support - PFC hardware block.
*
- * Copyright (C) 2015-2016 Renesas Electronics Corporation
+ * Copyright (C) 2015-2017 Renesas Electronics Corporation
*
* 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
@@ -472,7 +472,7 @@ FM(IP16_31_28) IP16_31_28 FM(IP17_31_28) IP17_31_28
#define MOD_SEL1_26 FM(SEL_TIMER_TMU1_0) FM(SEL_TIMER_TMU1_1)
#define MOD_SEL1_25_24 FM(SEL_SSP1_1_0) FM(SEL_SSP1_1_1) FM(SEL_SSP1_1_2) FM(SEL_SSP1_1_3)
#define MOD_SEL1_23_22_21 FM(SEL_SSP1_0_0) FM(SEL_SSP1_0_1) FM(SEL_SSP1_0_2) FM(SEL_SSP1_0_3) FM(SEL_SSP1_0_4) F_(0, 0) F_(0, 0) F_(0, 0)
-#define MOD_SEL1_20 FM(SEL_SSI_0) FM(SEL_SSI_1)
+#define MOD_SEL1_20 FM(SEL_SSI1_0) FM(SEL_SSI1_1)
#define MOD_SEL1_19 FM(SEL_SPEED_PULSE_0) FM(SEL_SPEED_PULSE_1)
#define MOD_SEL1_18_17 FM(SEL_SIMCARD_0) FM(SEL_SIMCARD_1) FM(SEL_SIMCARD_2) FM(SEL_SIMCARD_3)
#define MOD_SEL1_16 FM(SEL_SDHI2_0) FM(SEL_SDHI2_1)
@@ -1218,7 +1218,7 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_GPSR(IP13_11_8, HSCK0),
PINMUX_IPSR_MSEL(IP13_11_8, MSIOF1_SCK_D, SEL_MSIOF1_3),
PINMUX_IPSR_MSEL(IP13_11_8, AUDIO_CLKB_A, SEL_ADG_B_0),
- PINMUX_IPSR_MSEL(IP13_11_8, SSI_SDATA1_B, SEL_SSI_1),
+ PINMUX_IPSR_MSEL(IP13_11_8, SSI_SDATA1_B, SEL_SSI1_1),
PINMUX_IPSR_MSEL(IP13_11_8, TS_SCK0_D, SEL_TSIF0_3),
PINMUX_IPSR_MSEL(IP13_11_8, STP_ISCLK_0_D, SEL_SSP1_0_3),
PINMUX_IPSR_MSEL(IP13_11_8, RIF0_CLK_C, SEL_DRIF0_2),
@@ -1226,14 +1226,14 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_GPSR(IP13_15_12, HRX0),
PINMUX_IPSR_MSEL(IP13_15_12, MSIOF1_RXD_D, SEL_MSIOF1_3),
- PINMUX_IPSR_MSEL(IP13_15_12, SSI_SDATA2_B, SEL_SSI_1),
+ PINMUX_IPSR_MSEL(IP13_15_12, SSI_SDATA2_B, SEL_SSI2_1),
PINMUX_IPSR_MSEL(IP13_15_12, TS_SDEN0_D, SEL_TSIF0_3),
PINMUX_IPSR_MSEL(IP13_15_12, STP_ISEN_0_D, SEL_SSP1_0_3),
PINMUX_IPSR_MSEL(IP13_15_12, RIF0_D0_C, SEL_DRIF0_2),
PINMUX_IPSR_GPSR(IP13_19_16, HTX0),
PINMUX_IPSR_MSEL(IP13_19_16, MSIOF1_TXD_D, SEL_MSIOF1_3),
- PINMUX_IPSR_MSEL(IP13_19_16, SSI_SDATA9_B, SEL_SSI_1),
+ PINMUX_IPSR_MSEL(IP13_19_16, SSI_SDATA9_B, SEL_SSI9_1),
PINMUX_IPSR_MSEL(IP13_19_16, TS_SDAT0_D, SEL_TSIF0_3),
PINMUX_IPSR_MSEL(IP13_19_16, STP_ISD_0_D, SEL_SSP1_0_3),
PINMUX_IPSR_MSEL(IP13_19_16, RIF0_D1_C, SEL_DRIF0_2),
@@ -1241,7 +1241,7 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_GPSR(IP13_23_20, HCTS0_N),
PINMUX_IPSR_MSEL(IP13_23_20, RX2_B, SEL_SCIF2_1),
PINMUX_IPSR_MSEL(IP13_23_20, MSIOF1_SYNC_D, SEL_MSIOF1_3),
- PINMUX_IPSR_MSEL(IP13_23_20, SSI_SCK9_A, SEL_SSI_0),
+ PINMUX_IPSR_MSEL(IP13_23_20, SSI_SCK9_A, SEL_SSI9_0),
PINMUX_IPSR_MSEL(IP13_23_20, TS_SPSYNC0_D, SEL_TSIF0_3),
PINMUX_IPSR_MSEL(IP13_23_20, STP_ISSYNC_0_D, SEL_SSP1_0_3),
PINMUX_IPSR_MSEL(IP13_23_20, RIF0_SYNC_C, SEL_DRIF0_2),
@@ -1250,7 +1250,7 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_GPSR(IP13_27_24, HRTS0_N),
PINMUX_IPSR_MSEL(IP13_27_24, TX2_B, SEL_SCIF2_1),
PINMUX_IPSR_MSEL(IP13_27_24, MSIOF1_SS1_D, SEL_MSIOF1_3),
- PINMUX_IPSR_MSEL(IP13_27_24, SSI_WS9_A, SEL_SSI_0),
+ PINMUX_IPSR_MSEL(IP13_27_24, SSI_WS9_A, SEL_SSI9_0),
PINMUX_IPSR_MSEL(IP13_27_24, STP_IVCXO27_0_D, SEL_SSP1_0_3),
PINMUX_IPSR_MSEL(IP13_27_24, BPFCLK_A, SEL_FM_0),
PINMUX_IPSR_GPSR(IP13_27_24, AUDIO_CLKOUT2_A),
@@ -1265,7 +1265,7 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_MSEL(IP14_3_0, RX5_A, SEL_SCIF5_0),
PINMUX_IPSR_GPSR(IP14_3_0, NFWP_N_A),
PINMUX_IPSR_MSEL(IP14_3_0, AUDIO_CLKA_C, SEL_ADG_A_2),
- PINMUX_IPSR_MSEL(IP14_3_0, SSI_SCK2_A, SEL_SSI_0),
+ PINMUX_IPSR_MSEL(IP14_3_0, SSI_SCK2_A, SEL_SSI2_0),
PINMUX_IPSR_MSEL(IP14_3_0, STP_IVCXO27_0_C, SEL_SSP1_0_2),
PINMUX_IPSR_GPSR(IP14_3_0, AUDIO_CLKOUT3_A),
PINMUX_IPSR_MSEL(IP14_3_0, TCLK1_B, SEL_TIMER_TMU1_1),
@@ -1274,7 +1274,7 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_MSEL(IP14_7_4, TX5_A, SEL_SCIF5_0),
PINMUX_IPSR_MSEL(IP14_7_4, MSIOF1_SS2_D, SEL_MSIOF1_3),
PINMUX_IPSR_MSEL(IP14_7_4, AUDIO_CLKC_A, SEL_ADG_C_0),
- PINMUX_IPSR_MSEL(IP14_7_4, SSI_WS2_A, SEL_SSI_0),
+ PINMUX_IPSR_MSEL(IP14_7_4, SSI_WS2_A, SEL_SSI2_0),
PINMUX_IPSR_MSEL(IP14_7_4, STP_OPWM_0_D, SEL_SSP1_0_3),
PINMUX_IPSR_GPSR(IP14_7_4, AUDIO_CLKOUT_D),
PINMUX_IPSR_MSEL(IP14_7_4, SPEEDIN_B, SEL_SPEED_PULSE_1),
@@ -1302,10 +1302,10 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_MSEL(IP14_31_28, MSIOF1_SS2_F, SEL_MSIOF1_5),
/* IPSR15 */
- PINMUX_IPSR_MSEL(IP15_3_0, SSI_SDATA1_A, SEL_SSI_0),
+ PINMUX_IPSR_MSEL(IP15_3_0, SSI_SDATA1_A, SEL_SSI1_0),
- PINMUX_IPSR_MSEL(IP15_7_4, SSI_SDATA2_A, SEL_SSI_0),
- PINMUX_IPSR_MSEL(IP15_7_4, SSI_SCK1_B, SEL_SSI_1),
+ PINMUX_IPSR_MSEL(IP15_7_4, SSI_SDATA2_A, SEL_SSI2_0),
+ PINMUX_IPSR_MSEL(IP15_7_4, SSI_SCK1_B, SEL_SSI1_1),
PINMUX_IPSR_GPSR(IP15_11_8, SSI_SCK349),
PINMUX_IPSR_MSEL(IP15_11_8, MSIOF1_SS1_A, SEL_MSIOF1_0),
@@ -1394,11 +1394,11 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_MSEL(IP16_27_24, RIF1_D1_A, SEL_DRIF1_0),
PINMUX_IPSR_MSEL(IP16_27_24, RIF3_D1_A, SEL_DRIF3_0),
- PINMUX_IPSR_MSEL(IP16_31_28, SSI_SDATA9_A, SEL_SSI_0),
+ PINMUX_IPSR_MSEL(IP16_31_28, SSI_SDATA9_A, SEL_SSI9_0),
PINMUX_IPSR_MSEL(IP16_31_28, HSCK2_B, SEL_HSCIF2_1),
PINMUX_IPSR_MSEL(IP16_31_28, MSIOF1_SS1_C, SEL_MSIOF1_2),
PINMUX_IPSR_MSEL(IP16_31_28, HSCK1_A, SEL_HSCIF1_0),
- PINMUX_IPSR_MSEL(IP16_31_28, SSI_WS1_B, SEL_SSI_1),
+ PINMUX_IPSR_MSEL(IP16_31_28, SSI_WS1_B, SEL_SSI1_1),
PINMUX_IPSR_GPSR(IP16_31_28, SCK1),
PINMUX_IPSR_MSEL(IP16_31_28, STP_IVCXO27_1_A, SEL_SSP1_1_0),
PINMUX_IPSR_MSEL(IP16_31_28, SCK5_A, SEL_SCIF5_0),
@@ -1430,7 +1430,7 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_GPSR(IP17_19_16, USB1_PWEN),
PINMUX_IPSR_MSEL(IP17_19_16, SIM0_CLK_C, SEL_SIMCARD_2),
- PINMUX_IPSR_MSEL(IP17_19_16, SSI_SCK1_A, SEL_SSI_0),
+ PINMUX_IPSR_MSEL(IP17_19_16, SSI_SCK1_A, SEL_SSI1_0),
PINMUX_IPSR_MSEL(IP17_19_16, TS_SCK0_E, SEL_TSIF0_4),
PINMUX_IPSR_MSEL(IP17_19_16, STP_ISCLK_0_E, SEL_SSP1_0_4),
PINMUX_IPSR_MSEL(IP17_19_16, FMCLK_B, SEL_FM_1),
@@ -1440,7 +1440,7 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_GPSR(IP17_23_20, USB1_OVC),
PINMUX_IPSR_MSEL(IP17_23_20, MSIOF1_SS2_C, SEL_MSIOF1_2),
- PINMUX_IPSR_MSEL(IP17_23_20, SSI_WS1_A, SEL_SSI_0),
+ PINMUX_IPSR_MSEL(IP17_23_20, SSI_WS1_A, SEL_SSI1_0),
PINMUX_IPSR_MSEL(IP17_23_20, TS_SDAT0_E, SEL_TSIF0_4),
PINMUX_IPSR_MSEL(IP17_23_20, STP_ISD_0_E, SEL_SSP1_0_4),
PINMUX_IPSR_MSEL(IP17_23_20, FMIN_B, SEL_FM_1),
@@ -1450,7 +1450,7 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_GPSR(IP17_27_24, USB30_PWEN),
PINMUX_IPSR_GPSR(IP17_27_24, AUDIO_CLKOUT_B),
- PINMUX_IPSR_MSEL(IP17_27_24, SSI_SCK2_B, SEL_SSI_1),
+ PINMUX_IPSR_MSEL(IP17_27_24, SSI_SCK2_B, SEL_SSI2_1),
PINMUX_IPSR_MSEL(IP17_27_24, TS_SDEN1_D, SEL_TSIF1_3),
PINMUX_IPSR_MSEL(IP17_27_24, STP_ISEN_1_D, SEL_SSP1_1_3),
PINMUX_IPSR_MSEL(IP17_27_24, STP_OPWM_0_E, SEL_SSP1_0_4),
@@ -1462,7 +1462,7 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_GPSR(IP17_31_28, USB30_OVC),
PINMUX_IPSR_GPSR(IP17_31_28, AUDIO_CLKOUT1_B),
- PINMUX_IPSR_MSEL(IP17_31_28, SSI_WS2_B, SEL_SSI_1),
+ PINMUX_IPSR_MSEL(IP17_31_28, SSI_WS2_B, SEL_SSI2_1),
PINMUX_IPSR_MSEL(IP17_31_28, TS_SPSYNC1_D, SEL_TSIF1_3),
PINMUX_IPSR_MSEL(IP17_31_28, STP_ISSYNC_1_D, SEL_SSP1_1_3),
PINMUX_IPSR_MSEL(IP17_31_28, STP_IVCXO27_0_E, SEL_SSP1_0_4),
@@ -1473,7 +1473,7 @@ static const u16 pinmux_data[] = {
/* IPSR18 */
PINMUX_IPSR_GPSR(IP18_3_0, USB2_CH3_PWEN),
PINMUX_IPSR_GPSR(IP18_3_0, AUDIO_CLKOUT2_B),
- PINMUX_IPSR_MSEL(IP18_3_0, SSI_SCK9_B, SEL_SSI_1),
+ PINMUX_IPSR_MSEL(IP18_3_0, SSI_SCK9_B, SEL_SSI9_1),
PINMUX_IPSR_MSEL(IP18_3_0, TS_SDEN0_E, SEL_TSIF0_4),
PINMUX_IPSR_MSEL(IP18_3_0, STP_ISEN_0_E, SEL_SSP1_0_4),
PINMUX_IPSR_MSEL(IP18_3_0, RIF2_D0_B, SEL_DRIF2_1),
@@ -1483,7 +1483,7 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_GPSR(IP18_7_4, USB2_CH3_OVC),
PINMUX_IPSR_GPSR(IP18_7_4, AUDIO_CLKOUT3_B),
- PINMUX_IPSR_MSEL(IP18_7_4, SSI_WS9_B, SEL_SSI_1),
+ PINMUX_IPSR_MSEL(IP18_7_4, SSI_WS9_B, SEL_SSI9_1),
PINMUX_IPSR_MSEL(IP18_7_4, TS_SPSYNC0_E, SEL_TSIF0_4),
PINMUX_IPSR_MSEL(IP18_7_4, STP_ISSYNC_0_E, SEL_SSP1_0_4),
PINMUX_IPSR_MSEL(IP18_7_4, RIF2_D1_B, SEL_DRIF2_1),
@@ -1711,11 +1711,11 @@ static const unsigned int avb_phy_int_pins[] = {
static const unsigned int avb_phy_int_mux[] = {
AVB_PHY_INT_MARK,
};
-static const unsigned int avb_mdc_pins[] = {
+static const unsigned int avb_mdio_pins[] = {
/* AVB_MDC, AVB_MDIO */
RCAR_GP_PIN(2, 9), PIN_NUMBER('A', 9),
};
-static const unsigned int avb_mdc_mux[] = {
+static const unsigned int avb_mdio_mux[] = {
AVB_MDC_MARK, AVB_MDIO_MARK,
};
static const unsigned int avb_mii_pins[] = {
@@ -2127,6 +2127,22 @@ static const unsigned int du_disp_mux[] = {
DU_DISP_MARK,
};
+/* - HDMI ------------------------------------------------------------------- */
+static const unsigned int hdmi0_cec_pins[] = {
+ /* HDMI0_CEC */
+ RCAR_GP_PIN(7, 2),
+};
+static const unsigned int hdmi0_cec_mux[] = {
+ HDMI0_CEC_MARK,
+};
+static const unsigned int hdmi1_cec_pins[] = {
+ /* HDMI1_CEC */
+ RCAR_GP_PIN(7, 3),
+};
+static const unsigned int hdmi1_cec_mux[] = {
+ HDMI1_CEC_MARK,
+};
+
/* - HSCIF0 ----------------------------------------------------------------- */
static const unsigned int hscif0_data_pins[] = {
/* RX, TX */
@@ -3839,6 +3855,36 @@ static const unsigned int ssi9_ctrl_b_mux[] = {
SSI_SCK9_B_MARK, SSI_WS9_B_MARK,
};
+/* - TMU -------------------------------------------------------------------- */
+static const unsigned int tmu_tclk1_a_pins[] = {
+ /* TCLK */
+ RCAR_GP_PIN(6, 23),
+};
+static const unsigned int tmu_tclk1_a_mux[] = {
+ TCLK1_A_MARK,
+};
+static const unsigned int tmu_tclk1_b_pins[] = {
+ /* TCLK */
+ RCAR_GP_PIN(5, 19),
+};
+static const unsigned int tmu_tclk1_b_mux[] = {
+ TCLK1_B_MARK,
+};
+static const unsigned int tmu_tclk2_a_pins[] = {
+ /* TCLK */
+ RCAR_GP_PIN(6, 19),
+};
+static const unsigned int tmu_tclk2_a_mux[] = {
+ TCLK2_A_MARK,
+};
+static const unsigned int tmu_tclk2_b_pins[] = {
+ /* TCLK */
+ RCAR_GP_PIN(6, 28),
+};
+static const unsigned int tmu_tclk2_b_mux[] = {
+ TCLK2_B_MARK,
+};
+
/* - USB0 ------------------------------------------------------------------- */
static const unsigned int usb0_pins[] = {
/* PWEN, OVC */
@@ -3881,6 +3927,236 @@ static const unsigned int usb30_mux[] = {
USB30_PWEN_MARK, USB30_OVC_MARK,
};
+/* - VIN4 ------------------------------------------------------------------- */
+static const unsigned int vin4_data18_a_pins[] = {
+ RCAR_GP_PIN(0, 10), RCAR_GP_PIN(0, 11),
+ RCAR_GP_PIN(0, 12), RCAR_GP_PIN(0, 13),
+ RCAR_GP_PIN(0, 14), RCAR_GP_PIN(0, 15),
+ RCAR_GP_PIN(1, 2), RCAR_GP_PIN(1, 3),
+ RCAR_GP_PIN(1, 4), RCAR_GP_PIN(1, 5),
+ RCAR_GP_PIN(1, 6), RCAR_GP_PIN(1, 7),
+ RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3),
+ RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5),
+ RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7),
+};
+static const unsigned int vin4_data18_a_mux[] = {
+ VI4_DATA2_A_MARK, VI4_DATA3_A_MARK,
+ VI4_DATA4_A_MARK, VI4_DATA5_A_MARK,
+ VI4_DATA6_A_MARK, VI4_DATA7_A_MARK,
+ VI4_DATA10_MARK, VI4_DATA11_MARK,
+ VI4_DATA12_MARK, VI4_DATA13_MARK,
+ VI4_DATA14_MARK, VI4_DATA15_MARK,
+ VI4_DATA18_MARK, VI4_DATA19_MARK,
+ VI4_DATA20_MARK, VI4_DATA21_MARK,
+ VI4_DATA22_MARK, VI4_DATA23_MARK,
+};
+static const unsigned int vin4_data18_b_pins[] = {
+ RCAR_GP_PIN(2, 2), RCAR_GP_PIN(2, 3),
+ RCAR_GP_PIN(2, 4), RCAR_GP_PIN(2, 5),
+ RCAR_GP_PIN(2, 6), RCAR_GP_PIN(2, 7),
+ RCAR_GP_PIN(1, 2), RCAR_GP_PIN(1, 3),
+ RCAR_GP_PIN(1, 4), RCAR_GP_PIN(1, 5),
+ RCAR_GP_PIN(1, 6), RCAR_GP_PIN(1, 7),
+ RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3),
+ RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5),
+ RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7),
+};
+static const unsigned int vin4_data18_b_mux[] = {
+ VI4_DATA2_B_MARK, VI4_DATA3_B_MARK,
+ VI4_DATA4_B_MARK, VI4_DATA5_B_MARK,
+ VI4_DATA6_B_MARK, VI4_DATA7_B_MARK,
+ VI4_DATA10_MARK, VI4_DATA11_MARK,
+ VI4_DATA12_MARK, VI4_DATA13_MARK,
+ VI4_DATA14_MARK, VI4_DATA15_MARK,
+ VI4_DATA18_MARK, VI4_DATA19_MARK,
+ VI4_DATA20_MARK, VI4_DATA21_MARK,
+ VI4_DATA22_MARK, VI4_DATA23_MARK,
+};
+static const union vin_data vin4_data_a_pins = {
+ .data24 = {
+ RCAR_GP_PIN(0, 8), RCAR_GP_PIN(0, 9),
+ RCAR_GP_PIN(0, 10), RCAR_GP_PIN(0, 11),
+ RCAR_GP_PIN(0, 12), RCAR_GP_PIN(0, 13),
+ RCAR_GP_PIN(0, 14), RCAR_GP_PIN(0, 15),
+ RCAR_GP_PIN(1, 0), RCAR_GP_PIN(1, 1),
+ RCAR_GP_PIN(1, 2), RCAR_GP_PIN(1, 3),
+ RCAR_GP_PIN(1, 4), RCAR_GP_PIN(1, 5),
+ RCAR_GP_PIN(1, 6), RCAR_GP_PIN(1, 7),
+ RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1),
+ RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3),
+ RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5),
+ RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7),
+ },
+};
+static const union vin_data vin4_data_a_mux = {
+ .data24 = {
+ VI4_DATA0_A_MARK, VI4_DATA1_A_MARK,
+ VI4_DATA2_A_MARK, VI4_DATA3_A_MARK,
+ VI4_DATA4_A_MARK, VI4_DATA5_A_MARK,
+ VI4_DATA6_A_MARK, VI4_DATA7_A_MARK,
+ VI4_DATA8_MARK, VI4_DATA9_MARK,
+ VI4_DATA10_MARK, VI4_DATA11_MARK,
+ VI4_DATA12_MARK, VI4_DATA13_MARK,
+ VI4_DATA14_MARK, VI4_DATA15_MARK,
+ VI4_DATA16_MARK, VI4_DATA17_MARK,
+ VI4_DATA18_MARK, VI4_DATA19_MARK,
+ VI4_DATA20_MARK, VI4_DATA21_MARK,
+ VI4_DATA22_MARK, VI4_DATA23_MARK,
+ },
+};
+static const union vin_data vin4_data_b_pins = {
+ .data24 = {
+ RCAR_GP_PIN(2, 0), RCAR_GP_PIN(2, 1),
+ RCAR_GP_PIN(2, 2), RCAR_GP_PIN(2, 3),
+ RCAR_GP_PIN(2, 4), RCAR_GP_PIN(2, 5),
+ RCAR_GP_PIN(2, 6), RCAR_GP_PIN(2, 7),
+ RCAR_GP_PIN(1, 0), RCAR_GP_PIN(1, 1),
+ RCAR_GP_PIN(1, 2), RCAR_GP_PIN(1, 3),
+ RCAR_GP_PIN(1, 4), RCAR_GP_PIN(1, 5),
+ RCAR_GP_PIN(1, 6), RCAR_GP_PIN(1, 7),
+ RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1),
+ RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3),
+ RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5),
+ RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7),
+ },
+};
+static const union vin_data vin4_data_b_mux = {
+ .data24 = {
+ VI4_DATA0_B_MARK, VI4_DATA1_B_MARK,
+ VI4_DATA2_B_MARK, VI4_DATA3_B_MARK,
+ VI4_DATA4_B_MARK, VI4_DATA5_B_MARK,
+ VI4_DATA6_B_MARK, VI4_DATA7_B_MARK,
+ VI4_DATA8_MARK, VI4_DATA9_MARK,
+ VI4_DATA10_MARK, VI4_DATA11_MARK,
+ VI4_DATA12_MARK, VI4_DATA13_MARK,
+ VI4_DATA14_MARK, VI4_DATA15_MARK,
+ VI4_DATA16_MARK, VI4_DATA17_MARK,
+ VI4_DATA18_MARK, VI4_DATA19_MARK,
+ VI4_DATA20_MARK, VI4_DATA21_MARK,
+ VI4_DATA22_MARK, VI4_DATA23_MARK,
+ },
+};
+static const unsigned int vin4_sync_pins[] = {
+ /* HSYNC#, VSYNC# */
+ RCAR_GP_PIN(1, 18), RCAR_GP_PIN(1, 17),
+};
+static const unsigned int vin4_sync_mux[] = {
+ VI4_HSYNC_N_MARK, VI4_VSYNC_N_MARK,
+};
+static const unsigned int vin4_field_pins[] = {
+ /* FIELD */
+ RCAR_GP_PIN(1, 16),
+};
+static const unsigned int vin4_field_mux[] = {
+ VI4_FIELD_MARK,
+};
+static const unsigned int vin4_clkenb_pins[] = {
+ /* CLKENB */
+ RCAR_GP_PIN(1, 19),
+};
+static const unsigned int vin4_clkenb_mux[] = {
+ VI4_CLKENB_MARK,
+};
+static const unsigned int vin4_clk_pins[] = {
+ /* CLK */
+ RCAR_GP_PIN(1, 27),
+};
+static const unsigned int vin4_clk_mux[] = {
+ VI4_CLK_MARK,
+};
+
+/* - VIN5 ------------------------------------------------------------------- */
+static const unsigned int vin5_data8_pins[] = {
+ RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1),
+ RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3),
+ RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5),
+ RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7),
+};
+static const unsigned int vin5_data8_mux[] = {
+ VI5_DATA0_MARK, VI5_DATA1_MARK,
+ VI5_DATA2_MARK, VI5_DATA3_MARK,
+ VI5_DATA4_MARK, VI5_DATA5_MARK,
+ VI5_DATA6_MARK, VI5_DATA7_MARK,
+};
+static const unsigned int vin5_data10_pins[] = {
+ RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1),
+ RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3),
+ RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5),
+ RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7),
+ RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13),
+};
+static const unsigned int vin5_data10_mux[] = {
+ VI5_DATA0_MARK, VI5_DATA1_MARK,
+ VI5_DATA2_MARK, VI5_DATA3_MARK,
+ VI5_DATA4_MARK, VI5_DATA5_MARK,
+ VI5_DATA6_MARK, VI5_DATA7_MARK,
+ VI5_DATA8_MARK, VI5_DATA9_MARK,
+};
+static const unsigned int vin5_data12_pins[] = {
+ RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1),
+ RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3),
+ RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5),
+ RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7),
+ RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13),
+ RCAR_GP_PIN(1, 14), RCAR_GP_PIN(1, 15),
+};
+static const unsigned int vin5_data12_mux[] = {
+ VI5_DATA0_MARK, VI5_DATA1_MARK,
+ VI5_DATA2_MARK, VI5_DATA3_MARK,
+ VI5_DATA4_MARK, VI5_DATA5_MARK,
+ VI5_DATA6_MARK, VI5_DATA7_MARK,
+ VI5_DATA8_MARK, VI5_DATA9_MARK,
+ VI5_DATA10_MARK, VI5_DATA11_MARK,
+};
+static const unsigned int vin5_data16_pins[] = {
+ RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1),
+ RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3),
+ RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5),
+ RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7),
+ RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13),
+ RCAR_GP_PIN(1, 14), RCAR_GP_PIN(1, 15),
+ RCAR_GP_PIN(1, 4), RCAR_GP_PIN(1, 5),
+ RCAR_GP_PIN(1, 6), RCAR_GP_PIN(1, 7),
+};
+static const unsigned int vin5_data16_mux[] = {
+ VI5_DATA0_MARK, VI5_DATA1_MARK,
+ VI5_DATA2_MARK, VI5_DATA3_MARK,
+ VI5_DATA4_MARK, VI5_DATA5_MARK,
+ VI5_DATA6_MARK, VI5_DATA7_MARK,
+ VI5_DATA8_MARK, VI5_DATA9_MARK,
+ VI5_DATA10_MARK, VI5_DATA11_MARK,
+ VI5_DATA12_MARK, VI5_DATA13_MARK,
+ VI5_DATA14_MARK, VI5_DATA15_MARK,
+};
+static const unsigned int vin5_sync_pins[] = {
+ /* HSYNC#, VSYNC# */
+ RCAR_GP_PIN(1, 10), RCAR_GP_PIN(1, 9),
+};
+static const unsigned int vin5_sync_mux[] = {
+ VI5_HSYNC_N_MARK, VI5_VSYNC_N_MARK,
+};
+static const unsigned int vin5_field_pins[] = {
+ RCAR_GP_PIN(1, 11),
+};
+static const unsigned int vin5_field_mux[] = {
+ /* FIELD */
+ VI5_FIELD_MARK,
+};
+static const unsigned int vin5_clkenb_pins[] = {
+ RCAR_GP_PIN(1, 20),
+};
+static const unsigned int vin5_clkenb_mux[] = {
+ /* CLKENB */
+ VI5_CLKENB_MARK,
+};
+static const unsigned int vin5_clk_pins[] = {
+ RCAR_GP_PIN(1, 21),
+};
+static const unsigned int vin5_clk_mux[] = {
+ /* CLK */
+ VI5_CLK_MARK,
+};
+
static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(audio_clk_a_a),
SH_PFC_PIN_GROUP(audio_clk_a_b),
@@ -3902,7 +4178,8 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(avb_link),
SH_PFC_PIN_GROUP(avb_magic),
SH_PFC_PIN_GROUP(avb_phy_int),
- SH_PFC_PIN_GROUP(avb_mdc),
+ SH_PFC_PIN_GROUP_ALIAS(avb_mdc, avb_mdio), /* Deprecated */
+ SH_PFC_PIN_GROUP(avb_mdio),
SH_PFC_PIN_GROUP(avb_mii),
SH_PFC_PIN_GROUP(avb_avtp_pps),
SH_PFC_PIN_GROUP(avb_avtp_match_a),
@@ -3954,6 +4231,8 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(du_oddf),
SH_PFC_PIN_GROUP(du_cde),
SH_PFC_PIN_GROUP(du_disp),
+ SH_PFC_PIN_GROUP(hdmi0_cec),
+ SH_PFC_PIN_GROUP(hdmi1_cec),
SH_PFC_PIN_GROUP(hscif0_data),
SH_PFC_PIN_GROUP(hscif0_clk),
SH_PFC_PIN_GROUP(hscif0_ctrl),
@@ -4189,11 +4468,41 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(ssi9_data_b),
SH_PFC_PIN_GROUP(ssi9_ctrl_a),
SH_PFC_PIN_GROUP(ssi9_ctrl_b),
+ SH_PFC_PIN_GROUP(tmu_tclk1_a),
+ SH_PFC_PIN_GROUP(tmu_tclk1_b),
+ SH_PFC_PIN_GROUP(tmu_tclk2_a),
+ SH_PFC_PIN_GROUP(tmu_tclk2_b),
SH_PFC_PIN_GROUP(usb0),
SH_PFC_PIN_GROUP(usb1),
SH_PFC_PIN_GROUP(usb2),
SH_PFC_PIN_GROUP(usb2_ch3),
SH_PFC_PIN_GROUP(usb30),
+ VIN_DATA_PIN_GROUP(vin4_data_a, 8),
+ VIN_DATA_PIN_GROUP(vin4_data_a, 10),
+ VIN_DATA_PIN_GROUP(vin4_data_a, 12),
+ VIN_DATA_PIN_GROUP(vin4_data_a, 16),
+ SH_PFC_PIN_GROUP(vin4_data18_a),
+ VIN_DATA_PIN_GROUP(vin4_data_a, 20),
+ VIN_DATA_PIN_GROUP(vin4_data_a, 24),
+ VIN_DATA_PIN_GROUP(vin4_data_b, 8),
+ VIN_DATA_PIN_GROUP(vin4_data_b, 10),
+ VIN_DATA_PIN_GROUP(vin4_data_b, 12),
+ VIN_DATA_PIN_GROUP(vin4_data_b, 16),
+ SH_PFC_PIN_GROUP(vin4_data18_b),
+ VIN_DATA_PIN_GROUP(vin4_data_b, 20),
+ VIN_DATA_PIN_GROUP(vin4_data_b, 24),
+ SH_PFC_PIN_GROUP(vin4_sync),
+ SH_PFC_PIN_GROUP(vin4_field),
+ SH_PFC_PIN_GROUP(vin4_clkenb),
+ SH_PFC_PIN_GROUP(vin4_clk),
+ SH_PFC_PIN_GROUP(vin5_data8),
+ SH_PFC_PIN_GROUP(vin5_data10),
+ SH_PFC_PIN_GROUP(vin5_data12),
+ SH_PFC_PIN_GROUP(vin5_data16),
+ SH_PFC_PIN_GROUP(vin5_sync),
+ SH_PFC_PIN_GROUP(vin5_field),
+ SH_PFC_PIN_GROUP(vin5_clkenb),
+ SH_PFC_PIN_GROUP(vin5_clk),
};
static const char * const audio_clk_groups[] = {
@@ -4220,7 +4529,8 @@ static const char * const avb_groups[] = {
"avb_link",
"avb_magic",
"avb_phy_int",
- "avb_mdc",
+ "avb_mdc", /* Deprecated, please use "avb_mdio" instead */
+ "avb_mdio",
"avb_mii",
"avb_avtp_pps",
"avb_avtp_match_a",
@@ -4304,6 +4614,14 @@ static const char * const du_groups[] = {
"du_disp",
};
+static const char * const hdmi0_groups[] = {
+ "hdmi0_cec",
+};
+
+static const char * const hdmi1_groups[] = {
+ "hdmi1_cec",
+};
+
static const char * const hscif0_groups[] = {
"hscif0_data",
"hscif0_clk",
@@ -4638,6 +4956,13 @@ static const char * const ssi_groups[] = {
"ssi9_ctrl_b",
};
+static const char * const tmu_groups[] = {
+ "tmu_tclk1_a",
+ "tmu_tclk1_b",
+ "tmu_tclk2_a",
+ "tmu_tclk2_b",
+};
+
static const char * const usb0_groups[] = {
"usb0",
};
@@ -4658,6 +4983,38 @@ static const char * const usb30_groups[] = {
"usb30",
};
+static const char * const vin4_groups[] = {
+ "vin4_data8_a",
+ "vin4_data10_a",
+ "vin4_data12_a",
+ "vin4_data16_a",
+ "vin4_data18_a",
+ "vin4_data20_a",
+ "vin4_data24_a",
+ "vin4_data8_b",
+ "vin4_data10_b",
+ "vin4_data12_b",
+ "vin4_data16_b",
+ "vin4_data18_b",
+ "vin4_data20_b",
+ "vin4_data24_b",
+ "vin4_sync",
+ "vin4_field",
+ "vin4_clkenb",
+ "vin4_clk",
+};
+
+static const char * const vin5_groups[] = {
+ "vin5_data8",
+ "vin5_data10",
+ "vin5_data12",
+ "vin5_data16",
+ "vin5_sync",
+ "vin5_field",
+ "vin5_clkenb",
+ "vin5_clk",
+};
+
static const struct sh_pfc_function pinmux_functions[] = {
SH_PFC_FUNCTION(audio_clk),
SH_PFC_FUNCTION(avb),
@@ -4671,6 +5028,8 @@ static const struct sh_pfc_function pinmux_functions[] = {
SH_PFC_FUNCTION(drif2),
SH_PFC_FUNCTION(drif3),
SH_PFC_FUNCTION(du),
+ SH_PFC_FUNCTION(hdmi0),
+ SH_PFC_FUNCTION(hdmi1),
SH_PFC_FUNCTION(hscif0),
SH_PFC_FUNCTION(hscif1),
SH_PFC_FUNCTION(hscif2),
@@ -4704,11 +5063,14 @@ static const struct sh_pfc_function pinmux_functions[] = {
SH_PFC_FUNCTION(sdhi2),
SH_PFC_FUNCTION(sdhi3),
SH_PFC_FUNCTION(ssi),
+ SH_PFC_FUNCTION(tmu),
SH_PFC_FUNCTION(usb0),
SH_PFC_FUNCTION(usb1),
SH_PFC_FUNCTION(usb2),
SH_PFC_FUNCTION(usb2_ch3),
SH_PFC_FUNCTION(usb30),
+ SH_PFC_FUNCTION(vin4),
+ SH_PFC_FUNCTION(vin5),
};
static const struct pinmux_cfg_reg pinmux_config_regs[] = {
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7796.c b/drivers/pinctrl/sh-pfc/pfc-r8a7796.c
index e5807d1ce0dc..4bc5b1f820c1 100644
--- a/drivers/pinctrl/sh-pfc/pfc-r8a7796.c
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a7796.c
@@ -1,7 +1,7 @@
/*
* R8A7796 processor support - PFC hardware block.
*
- * Copyright (C) 2016 Renesas Electronics Corp.
+ * Copyright (C) 2016-2017 Renesas Electronics Corp.
*
* This file is based on the drivers/pinctrl/sh-pfc/pfc-r8a7795.c
*
@@ -477,7 +477,7 @@ FM(IP16_31_28) IP16_31_28 FM(IP17_31_28) IP17_31_28
#define MOD_SEL1_26 FM(SEL_TIMER_TMU_0) FM(SEL_TIMER_TMU_1)
#define MOD_SEL1_25_24 FM(SEL_SSP1_1_0) FM(SEL_SSP1_1_1) FM(SEL_SSP1_1_2) FM(SEL_SSP1_1_3)
#define MOD_SEL1_23_22_21 FM(SEL_SSP1_0_0) FM(SEL_SSP1_0_1) FM(SEL_SSP1_0_2) FM(SEL_SSP1_0_3) FM(SEL_SSP1_0_4) F_(0, 0) F_(0, 0) F_(0, 0)
-#define MOD_SEL1_20 FM(SEL_SSI_0) FM(SEL_SSI_1)
+#define MOD_SEL1_20 FM(SEL_SSI1_0) FM(SEL_SSI1_1)
#define MOD_SEL1_19 FM(SEL_SPEED_PULSE_0) FM(SEL_SPEED_PULSE_1)
#define MOD_SEL1_18_17 FM(SEL_SIMCARD_0) FM(SEL_SIMCARD_1) FM(SEL_SIMCARD_2) FM(SEL_SIMCARD_3)
#define MOD_SEL1_16 FM(SEL_SDHI2_0) FM(SEL_SDHI2_1)
@@ -502,7 +502,7 @@ FM(IP16_31_28) IP16_31_28 FM(IP17_31_28) IP17_31_28
#define MOD_SEL2_28_27 FM(SEL_FM_0) FM(SEL_FM_1) FM(SEL_FM_2) FM(SEL_FM_3)
#define MOD_SEL2_26 FM(SEL_SCIF5_0) FM(SEL_SCIF5_1)
#define MOD_SEL2_25_24_23 FM(SEL_I2C6_0) FM(SEL_I2C6_1) FM(SEL_I2C6_2) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
-#define MOD_SEL2_22 FM(SEL_NDF_0) FM(SEL_NDF_1)
+#define MOD_SEL2_22 FM(SEL_NDFC_0) FM(SEL_NDFC_1)
#define MOD_SEL2_21 FM(SEL_SSI2_0) FM(SEL_SSI2_1)
#define MOD_SEL2_20 FM(SEL_SSI9_0) FM(SEL_SSI9_1)
#define MOD_SEL2_19 FM(SEL_TIMER_TMU2_0) FM(SEL_TIMER_TMU2_1)
@@ -1016,35 +1016,35 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_GPSR(IP8_15_12, SD1_CMD),
PINMUX_IPSR_MSEL(IP8_15_12, MSIOF1_SYNC_G, SEL_MSIOF1_6),
- PINMUX_IPSR_MSEL(IP8_15_12, NFCE_N_B, SEL_NDF_1),
+ PINMUX_IPSR_MSEL(IP8_15_12, NFCE_N_B, SEL_NDFC_1),
PINMUX_IPSR_MSEL(IP8_15_12, SIM0_D_A, SEL_SIMCARD_0),
PINMUX_IPSR_MSEL(IP8_15_12, STP_IVCXO27_1_B, SEL_SSP1_1_1),
PINMUX_IPSR_GPSR(IP8_19_16, SD1_DAT0),
PINMUX_IPSR_GPSR(IP8_19_16, SD2_DAT4),
PINMUX_IPSR_MSEL(IP8_19_16, MSIOF1_RXD_G, SEL_MSIOF1_6),
- PINMUX_IPSR_MSEL(IP8_19_16, NFWP_N_B, SEL_NDF_1),
+ PINMUX_IPSR_MSEL(IP8_19_16, NFWP_N_B, SEL_NDFC_1),
PINMUX_IPSR_MSEL(IP8_19_16, TS_SCK1_B, SEL_TSIF1_1),
PINMUX_IPSR_MSEL(IP8_19_16, STP_ISCLK_1_B, SEL_SSP1_1_1),
PINMUX_IPSR_GPSR(IP8_23_20, SD1_DAT1),
PINMUX_IPSR_GPSR(IP8_23_20, SD2_DAT5),
PINMUX_IPSR_MSEL(IP8_23_20, MSIOF1_TXD_G, SEL_MSIOF1_6),
- PINMUX_IPSR_MSEL(IP8_23_20, NFDATA14_B, SEL_NDF_1),
+ PINMUX_IPSR_MSEL(IP8_23_20, NFDATA14_B, SEL_NDFC_1),
PINMUX_IPSR_MSEL(IP8_23_20, TS_SPSYNC1_B, SEL_TSIF1_1),
PINMUX_IPSR_MSEL(IP8_23_20, STP_ISSYNC_1_B, SEL_SSP1_1_1),
PINMUX_IPSR_GPSR(IP8_27_24, SD1_DAT2),
PINMUX_IPSR_GPSR(IP8_27_24, SD2_DAT6),
PINMUX_IPSR_MSEL(IP8_27_24, MSIOF1_SS1_G, SEL_MSIOF1_6),
- PINMUX_IPSR_MSEL(IP8_27_24, NFDATA15_B, SEL_NDF_1),
+ PINMUX_IPSR_MSEL(IP8_27_24, NFDATA15_B, SEL_NDFC_1),
PINMUX_IPSR_MSEL(IP8_27_24, TS_SDAT1_B, SEL_TSIF1_1),
PINMUX_IPSR_MSEL(IP8_27_24, STP_ISD_1_B, SEL_SSP1_1_1),
PINMUX_IPSR_GPSR(IP8_31_28, SD1_DAT3),
PINMUX_IPSR_GPSR(IP8_31_28, SD2_DAT7),
PINMUX_IPSR_MSEL(IP8_31_28, MSIOF1_SS2_G, SEL_MSIOF1_6),
- PINMUX_IPSR_MSEL(IP8_31_28, NFRB_N_B, SEL_NDF_1),
+ PINMUX_IPSR_MSEL(IP8_31_28, NFRB_N_B, SEL_NDFC_1),
PINMUX_IPSR_MSEL(IP8_31_28, TS_SDEN1_B, SEL_TSIF1_1),
PINMUX_IPSR_MSEL(IP8_31_28, STP_ISEN_1_B, SEL_SSP1_1_1),
@@ -1110,16 +1110,20 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_GPSR(IP11_7_4, NFCLE),
PINMUX_IPSR_GPSR(IP11_11_8, SD0_CD),
+ PINMUX_IPSR_MSEL(IP11_11_8, NFDATA14_A, SEL_NDFC_0),
PINMUX_IPSR_MSEL(IP11_11_8, SCL2_B, SEL_I2C2_1),
PINMUX_IPSR_MSEL(IP11_11_8, SIM0_RST_A, SEL_SIMCARD_0),
PINMUX_IPSR_GPSR(IP11_15_12, SD0_WP),
+ PINMUX_IPSR_MSEL(IP11_15_12, NFDATA15_A, SEL_NDFC_0),
PINMUX_IPSR_MSEL(IP11_15_12, SDA2_B, SEL_I2C2_1),
PINMUX_IPSR_GPSR(IP11_19_16, SD1_CD),
+ PINMUX_IPSR_MSEL(IP11_19_16, NFRB_N_A, SEL_NDFC_0),
PINMUX_IPSR_MSEL(IP11_19_16, SIM0_CLK_B, SEL_SIMCARD_1),
PINMUX_IPSR_GPSR(IP11_23_20, SD1_WP),
+ PINMUX_IPSR_MSEL(IP11_23_20, NFCE_N_A, SEL_NDFC_0),
PINMUX_IPSR_MSEL(IP11_23_20, SIM0_D_B, SEL_SIMCARD_1),
PINMUX_IPSR_GPSR(IP11_27_24, SCK0),
@@ -1218,7 +1222,7 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_GPSR(IP13_11_8, HSCK0),
PINMUX_IPSR_MSEL(IP13_11_8, MSIOF1_SCK_D, SEL_MSIOF1_3),
PINMUX_IPSR_MSEL(IP13_11_8, AUDIO_CLKB_A, SEL_ADG_B_0),
- PINMUX_IPSR_MSEL(IP13_11_8, SSI_SDATA1_B, SEL_SSI_1),
+ PINMUX_IPSR_MSEL(IP13_11_8, SSI_SDATA1_B, SEL_SSI1_1),
PINMUX_IPSR_MSEL(IP13_11_8, TS_SCK0_D, SEL_TSIF0_3),
PINMUX_IPSR_MSEL(IP13_11_8, STP_ISCLK_0_D, SEL_SSP1_0_3),
PINMUX_IPSR_MSEL(IP13_11_8, RIF0_CLK_C, SEL_DRIF0_2),
@@ -1226,14 +1230,14 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_GPSR(IP13_15_12, HRX0),
PINMUX_IPSR_MSEL(IP13_15_12, MSIOF1_RXD_D, SEL_MSIOF1_3),
- PINMUX_IPSR_MSEL(IP13_15_12, SSI_SDATA2_B, SEL_SSI_1),
+ PINMUX_IPSR_MSEL(IP13_15_12, SSI_SDATA2_B, SEL_SSI2_1),
PINMUX_IPSR_MSEL(IP13_15_12, TS_SDEN0_D, SEL_TSIF0_3),
PINMUX_IPSR_MSEL(IP13_15_12, STP_ISEN_0_D, SEL_SSP1_0_3),
PINMUX_IPSR_MSEL(IP13_15_12, RIF0_D0_C, SEL_DRIF0_2),
PINMUX_IPSR_GPSR(IP13_19_16, HTX0),
PINMUX_IPSR_MSEL(IP13_19_16, MSIOF1_TXD_D, SEL_MSIOF1_3),
- PINMUX_IPSR_MSEL(IP13_19_16, SSI_SDATA9_B, SEL_SSI_1),
+ PINMUX_IPSR_MSEL(IP13_19_16, SSI_SDATA9_B, SEL_SSI9_1),
PINMUX_IPSR_MSEL(IP13_19_16, TS_SDAT0_D, SEL_TSIF0_3),
PINMUX_IPSR_MSEL(IP13_19_16, STP_ISD_0_D, SEL_SSP1_0_3),
PINMUX_IPSR_MSEL(IP13_19_16, RIF0_D1_C, SEL_DRIF0_2),
@@ -1241,7 +1245,7 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_GPSR(IP13_23_20, HCTS0_N),
PINMUX_IPSR_MSEL(IP13_23_20, RX2_B, SEL_SCIF2_1),
PINMUX_IPSR_MSEL(IP13_23_20, MSIOF1_SYNC_D, SEL_MSIOF1_3),
- PINMUX_IPSR_MSEL(IP13_23_20, SSI_SCK9_A, SEL_SSI_0),
+ PINMUX_IPSR_MSEL(IP13_23_20, SSI_SCK9_A, SEL_SSI9_0),
PINMUX_IPSR_MSEL(IP13_23_20, TS_SPSYNC0_D, SEL_TSIF0_3),
PINMUX_IPSR_MSEL(IP13_23_20, STP_ISSYNC_0_D, SEL_SSP1_0_3),
PINMUX_IPSR_MSEL(IP13_23_20, RIF0_SYNC_C, SEL_DRIF0_2),
@@ -1250,7 +1254,7 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_GPSR(IP13_27_24, HRTS0_N),
PINMUX_IPSR_MSEL(IP13_27_24, TX2_B, SEL_SCIF2_1),
PINMUX_IPSR_MSEL(IP13_27_24, MSIOF1_SS1_D, SEL_MSIOF1_3),
- PINMUX_IPSR_MSEL(IP13_27_24, SSI_WS9_A, SEL_SSI_0),
+ PINMUX_IPSR_MSEL(IP13_27_24, SSI_WS9_A, SEL_SSI9_0),
PINMUX_IPSR_MSEL(IP13_27_24, STP_IVCXO27_0_D, SEL_SSP1_0_3),
PINMUX_IPSR_MSEL(IP13_27_24, BPFCLK_A, SEL_FM_0),
PINMUX_IPSR_GPSR(IP13_27_24, AUDIO_CLKOUT2_A),
@@ -1263,9 +1267,9 @@ static const u16 pinmux_data[] = {
/* IPSR14 */
PINMUX_IPSR_GPSR(IP14_3_0, MSIOF0_SS1),
PINMUX_IPSR_MSEL(IP14_3_0, RX5_A, SEL_SCIF5_0),
- PINMUX_IPSR_MSEL(IP14_3_0, NFWP_N_A, SEL_NDF_0),
+ PINMUX_IPSR_MSEL(IP14_3_0, NFWP_N_A, SEL_NDFC_0),
PINMUX_IPSR_MSEL(IP14_3_0, AUDIO_CLKA_C, SEL_ADG_A_2),
- PINMUX_IPSR_MSEL(IP14_3_0, SSI_SCK2_A, SEL_SSI_0),
+ PINMUX_IPSR_MSEL(IP14_3_0, SSI_SCK2_A, SEL_SSI2_0),
PINMUX_IPSR_MSEL(IP14_3_0, STP_IVCXO27_0_C, SEL_SSP1_0_2),
PINMUX_IPSR_GPSR(IP14_3_0, AUDIO_CLKOUT3_A),
PINMUX_IPSR_MSEL(IP14_3_0, TCLK1_B, SEL_TIMER_TMU_1),
@@ -1274,7 +1278,7 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_MSEL(IP14_7_4, TX5_A, SEL_SCIF5_0),
PINMUX_IPSR_MSEL(IP14_7_4, MSIOF1_SS2_D, SEL_MSIOF1_3),
PINMUX_IPSR_MSEL(IP14_7_4, AUDIO_CLKC_A, SEL_ADG_C_0),
- PINMUX_IPSR_MSEL(IP14_7_4, SSI_WS2_A, SEL_SSI_0),
+ PINMUX_IPSR_MSEL(IP14_7_4, SSI_WS2_A, SEL_SSI2_0),
PINMUX_IPSR_MSEL(IP14_7_4, STP_OPWM_0_D, SEL_SSP1_0_3),
PINMUX_IPSR_GPSR(IP14_7_4, AUDIO_CLKOUT_D),
PINMUX_IPSR_MSEL(IP14_7_4, SPEEDIN_B, SEL_SPEED_PULSE_1),
@@ -1302,10 +1306,10 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_MSEL(IP14_31_28, MSIOF1_SS2_F, SEL_MSIOF1_5),
/* IPSR15 */
- PINMUX_IPSR_MSEL(IP15_3_0, SSI_SDATA1_A, SEL_SSI_0),
+ PINMUX_IPSR_MSEL(IP15_3_0, SSI_SDATA1_A, SEL_SSI1_0),
- PINMUX_IPSR_MSEL(IP15_7_4, SSI_SDATA2_A, SEL_SSI_0),
- PINMUX_IPSR_MSEL(IP15_7_4, SSI_SCK1_B, SEL_SSI_1),
+ PINMUX_IPSR_MSEL(IP15_7_4, SSI_SDATA2_A, SEL_SSI2_0),
+ PINMUX_IPSR_MSEL(IP15_7_4, SSI_SCK1_B, SEL_SSI1_1),
PINMUX_IPSR_GPSR(IP15_11_8, SSI_SCK349),
PINMUX_IPSR_MSEL(IP15_11_8, MSIOF1_SS1_A, SEL_MSIOF1_0),
@@ -1391,11 +1395,11 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_MSEL(IP16_27_24, RIF1_D1_A, SEL_DRIF1_0),
PINMUX_IPSR_MSEL(IP16_27_24, RIF3_D1_A, SEL_DRIF3_0),
- PINMUX_IPSR_MSEL(IP16_31_28, SSI_SDATA9_A, SEL_SSI_0),
+ PINMUX_IPSR_MSEL(IP16_31_28, SSI_SDATA9_A, SEL_SSI9_0),
PINMUX_IPSR_MSEL(IP16_31_28, HSCK2_B, SEL_HSCIF2_1),
PINMUX_IPSR_MSEL(IP16_31_28, MSIOF1_SS1_C, SEL_MSIOF1_2),
PINMUX_IPSR_MSEL(IP16_31_28, HSCK1_A, SEL_HSCIF1_0),
- PINMUX_IPSR_MSEL(IP16_31_28, SSI_WS1_B, SEL_SSI_1),
+ PINMUX_IPSR_MSEL(IP16_31_28, SSI_WS1_B, SEL_SSI1_1),
PINMUX_IPSR_GPSR(IP16_31_28, SCK1),
PINMUX_IPSR_MSEL(IP16_31_28, STP_IVCXO27_1_A, SEL_SSP1_1_0),
PINMUX_IPSR_MSEL(IP16_31_28, SCK5_A, SEL_SCIF5_0),
@@ -1427,7 +1431,7 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_GPSR(IP17_19_16, USB1_PWEN),
PINMUX_IPSR_MSEL(IP17_19_16, SIM0_CLK_C, SEL_SIMCARD_2),
- PINMUX_IPSR_MSEL(IP17_19_16, SSI_SCK1_A, SEL_SSI_0),
+ PINMUX_IPSR_MSEL(IP17_19_16, SSI_SCK1_A, SEL_SSI1_0),
PINMUX_IPSR_MSEL(IP17_19_16, TS_SCK0_E, SEL_TSIF0_4),
PINMUX_IPSR_MSEL(IP17_19_16, STP_ISCLK_0_E, SEL_SSP1_0_4),
PINMUX_IPSR_MSEL(IP17_19_16, FMCLK_B, SEL_FM_1),
@@ -1437,7 +1441,7 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_GPSR(IP17_23_20, USB1_OVC),
PINMUX_IPSR_MSEL(IP17_23_20, MSIOF1_SS2_C, SEL_MSIOF1_2),
- PINMUX_IPSR_MSEL(IP17_23_20, SSI_WS1_A, SEL_SSI_0),
+ PINMUX_IPSR_MSEL(IP17_23_20, SSI_WS1_A, SEL_SSI1_0),
PINMUX_IPSR_MSEL(IP17_23_20, TS_SDAT0_E, SEL_TSIF0_4),
PINMUX_IPSR_MSEL(IP17_23_20, STP_ISD_0_E, SEL_SSP1_0_4),
PINMUX_IPSR_MSEL(IP17_23_20, FMIN_B, SEL_FM_1),
@@ -1447,7 +1451,7 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_GPSR(IP17_27_24, USB30_PWEN),
PINMUX_IPSR_GPSR(IP17_27_24, AUDIO_CLKOUT_B),
- PINMUX_IPSR_MSEL(IP17_27_24, SSI_SCK2_B, SEL_SSI_1),
+ PINMUX_IPSR_MSEL(IP17_27_24, SSI_SCK2_B, SEL_SSI2_1),
PINMUX_IPSR_MSEL(IP17_27_24, TS_SDEN1_D, SEL_TSIF1_3),
PINMUX_IPSR_MSEL(IP17_27_24, STP_ISEN_1_D, SEL_SSP1_1_3),
PINMUX_IPSR_MSEL(IP17_27_24, STP_OPWM_0_E, SEL_SSP1_0_4),
@@ -1459,7 +1463,7 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_GPSR(IP17_31_28, USB30_OVC),
PINMUX_IPSR_GPSR(IP17_31_28, AUDIO_CLKOUT1_B),
- PINMUX_IPSR_MSEL(IP17_31_28, SSI_WS2_B, SEL_SSI_1),
+ PINMUX_IPSR_MSEL(IP17_31_28, SSI_WS2_B, SEL_SSI2_1),
PINMUX_IPSR_MSEL(IP17_31_28, TS_SPSYNC1_D, SEL_TSIF1_3),
PINMUX_IPSR_MSEL(IP17_31_28, STP_ISSYNC_1_D, SEL_SSP1_1_3),
PINMUX_IPSR_MSEL(IP17_31_28, STP_IVCXO27_0_E, SEL_SSP1_0_4),
@@ -1470,7 +1474,7 @@ static const u16 pinmux_data[] = {
/* IPSR18 */
PINMUX_IPSR_GPSR(IP18_3_0, GP6_30),
PINMUX_IPSR_GPSR(IP18_3_0, AUDIO_CLKOUT2_B),
- PINMUX_IPSR_MSEL(IP18_3_0, SSI_SCK9_B, SEL_SSI_1),
+ PINMUX_IPSR_MSEL(IP18_3_0, SSI_SCK9_B, SEL_SSI9_1),
PINMUX_IPSR_MSEL(IP18_3_0, TS_SDEN0_E, SEL_TSIF0_4),
PINMUX_IPSR_MSEL(IP18_3_0, STP_ISEN_0_E, SEL_SSP1_0_4),
PINMUX_IPSR_MSEL(IP18_3_0, RIF2_D0_B, SEL_DRIF2_1),
@@ -1480,7 +1484,7 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_GPSR(IP18_7_4, GP6_31),
PINMUX_IPSR_GPSR(IP18_7_4, AUDIO_CLKOUT3_B),
- PINMUX_IPSR_MSEL(IP18_7_4, SSI_WS9_B, SEL_SSI_1),
+ PINMUX_IPSR_MSEL(IP18_7_4, SSI_WS9_B, SEL_SSI9_1),
PINMUX_IPSR_MSEL(IP18_7_4, TS_SPSYNC0_E, SEL_TSIF0_4),
PINMUX_IPSR_MSEL(IP18_7_4, STP_ISSYNC_0_E, SEL_SSP1_0_4),
PINMUX_IPSR_MSEL(IP18_7_4, RIF2_D1_B, SEL_DRIF2_1),
@@ -1713,11 +1717,11 @@ static const unsigned int avb_phy_int_pins[] = {
static const unsigned int avb_phy_int_mux[] = {
AVB_PHY_INT_MARK,
};
-static const unsigned int avb_mdc_pins[] = {
+static const unsigned int avb_mdio_pins[] = {
/* AVB_MDC, AVB_MDIO */
RCAR_GP_PIN(2, 9), PIN_NUMBER('A', 9),
};
-static const unsigned int avb_mdc_mux[] = {
+static const unsigned int avb_mdio_mux[] = {
AVB_MDC_MARK, AVB_MDIO_MARK,
};
static const unsigned int avb_mii_pins[] = {
@@ -2129,6 +2133,15 @@ static const unsigned int du_disp_mux[] = {
DU_DISP_MARK,
};
+/* - HDMI ------------------------------------------------------------------- */
+static const unsigned int hdmi0_cec_pins[] = {
+ /* HDMI0_CEC */
+ RCAR_GP_PIN(7, 2),
+};
+static const unsigned int hdmi0_cec_mux[] = {
+ HDMI0_CEC_MARK,
+};
+
/* - HSCIF0 ----------------------------------------------------------------- */
static const unsigned int hscif0_data_pins[] = {
/* RX, TX */
@@ -3827,6 +3840,36 @@ static const unsigned int ssi9_ctrl_b_mux[] = {
SSI_SCK9_B_MARK, SSI_WS9_B_MARK,
};
+/* - TMU -------------------------------------------------------------------- */
+static const unsigned int tmu_tclk1_a_pins[] = {
+ /* TCLK */
+ RCAR_GP_PIN(6, 23),
+};
+static const unsigned int tmu_tclk1_a_mux[] = {
+ TCLK1_A_MARK,
+};
+static const unsigned int tmu_tclk1_b_pins[] = {
+ /* TCLK */
+ RCAR_GP_PIN(5, 19),
+};
+static const unsigned int tmu_tclk1_b_mux[] = {
+ TCLK1_B_MARK,
+};
+static const unsigned int tmu_tclk2_a_pins[] = {
+ /* TCLK */
+ RCAR_GP_PIN(6, 19),
+};
+static const unsigned int tmu_tclk2_a_mux[] = {
+ TCLK2_A_MARK,
+};
+static const unsigned int tmu_tclk2_b_pins[] = {
+ /* TCLK */
+ RCAR_GP_PIN(6, 28),
+};
+static const unsigned int tmu_tclk2_b_mux[] = {
+ TCLK2_B_MARK,
+};
+
/* - USB0 ------------------------------------------------------------------- */
static const unsigned int usb0_pins[] = {
/* PWEN, OVC */
@@ -3853,6 +3896,236 @@ static const unsigned int usb30_mux[] = {
USB30_PWEN_MARK, USB30_OVC_MARK,
};
+/* - VIN4 ------------------------------------------------------------------- */
+static const unsigned int vin4_data18_a_pins[] = {
+ RCAR_GP_PIN(0, 10), RCAR_GP_PIN(0, 11),
+ RCAR_GP_PIN(0, 12), RCAR_GP_PIN(0, 13),
+ RCAR_GP_PIN(0, 14), RCAR_GP_PIN(0, 15),
+ RCAR_GP_PIN(1, 2), RCAR_GP_PIN(1, 3),
+ RCAR_GP_PIN(1, 4), RCAR_GP_PIN(1, 5),
+ RCAR_GP_PIN(1, 6), RCAR_GP_PIN(1, 7),
+ RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3),
+ RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5),
+ RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7),
+};
+static const unsigned int vin4_data18_a_mux[] = {
+ VI4_DATA2_A_MARK, VI4_DATA3_A_MARK,
+ VI4_DATA4_A_MARK, VI4_DATA5_A_MARK,
+ VI4_DATA6_A_MARK, VI4_DATA7_A_MARK,
+ VI4_DATA10_MARK, VI4_DATA11_MARK,
+ VI4_DATA12_MARK, VI4_DATA13_MARK,
+ VI4_DATA14_MARK, VI4_DATA15_MARK,
+ VI4_DATA18_MARK, VI4_DATA19_MARK,
+ VI4_DATA20_MARK, VI4_DATA21_MARK,
+ VI4_DATA22_MARK, VI4_DATA23_MARK,
+};
+static const unsigned int vin4_data18_b_pins[] = {
+ RCAR_GP_PIN(2, 2), RCAR_GP_PIN(2, 3),
+ RCAR_GP_PIN(2, 4), RCAR_GP_PIN(2, 5),
+ RCAR_GP_PIN(2, 6), RCAR_GP_PIN(2, 7),
+ RCAR_GP_PIN(1, 2), RCAR_GP_PIN(1, 3),
+ RCAR_GP_PIN(1, 4), RCAR_GP_PIN(1, 5),
+ RCAR_GP_PIN(1, 6), RCAR_GP_PIN(1, 7),
+ RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3),
+ RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5),
+ RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7),
+};
+static const unsigned int vin4_data18_b_mux[] = {
+ VI4_DATA2_B_MARK, VI4_DATA3_B_MARK,
+ VI4_DATA4_B_MARK, VI4_DATA5_B_MARK,
+ VI4_DATA6_B_MARK, VI4_DATA7_B_MARK,
+ VI4_DATA10_MARK, VI4_DATA11_MARK,
+ VI4_DATA12_MARK, VI4_DATA13_MARK,
+ VI4_DATA14_MARK, VI4_DATA15_MARK,
+ VI4_DATA18_MARK, VI4_DATA19_MARK,
+ VI4_DATA20_MARK, VI4_DATA21_MARK,
+ VI4_DATA22_MARK, VI4_DATA23_MARK,
+};
+static const union vin_data vin4_data_a_pins = {
+ .data24 = {
+ RCAR_GP_PIN(0, 8), RCAR_GP_PIN(0, 9),
+ RCAR_GP_PIN(0, 10), RCAR_GP_PIN(0, 11),
+ RCAR_GP_PIN(0, 12), RCAR_GP_PIN(0, 13),
+ RCAR_GP_PIN(0, 14), RCAR_GP_PIN(0, 15),
+ RCAR_GP_PIN(1, 0), RCAR_GP_PIN(1, 1),
+ RCAR_GP_PIN(1, 2), RCAR_GP_PIN(1, 3),
+ RCAR_GP_PIN(1, 4), RCAR_GP_PIN(1, 5),
+ RCAR_GP_PIN(1, 6), RCAR_GP_PIN(1, 7),
+ RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1),
+ RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3),
+ RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5),
+ RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7),
+ },
+};
+static const union vin_data vin4_data_a_mux = {
+ .data24 = {
+ VI4_DATA0_A_MARK, VI4_DATA1_A_MARK,
+ VI4_DATA2_A_MARK, VI4_DATA3_A_MARK,
+ VI4_DATA4_A_MARK, VI4_DATA5_A_MARK,
+ VI4_DATA6_A_MARK, VI4_DATA7_A_MARK,
+ VI4_DATA8_MARK, VI4_DATA9_MARK,
+ VI4_DATA10_MARK, VI4_DATA11_MARK,
+ VI4_DATA12_MARK, VI4_DATA13_MARK,
+ VI4_DATA14_MARK, VI4_DATA15_MARK,
+ VI4_DATA16_MARK, VI4_DATA17_MARK,
+ VI4_DATA18_MARK, VI4_DATA19_MARK,
+ VI4_DATA20_MARK, VI4_DATA21_MARK,
+ VI4_DATA22_MARK, VI4_DATA23_MARK,
+ },
+};
+static const union vin_data vin4_data_b_pins = {
+ .data24 = {
+ RCAR_GP_PIN(2, 0), RCAR_GP_PIN(2, 1),
+ RCAR_GP_PIN(2, 2), RCAR_GP_PIN(2, 3),
+ RCAR_GP_PIN(2, 4), RCAR_GP_PIN(2, 5),
+ RCAR_GP_PIN(2, 6), RCAR_GP_PIN(2, 7),
+ RCAR_GP_PIN(1, 0), RCAR_GP_PIN(1, 1),
+ RCAR_GP_PIN(1, 2), RCAR_GP_PIN(1, 3),
+ RCAR_GP_PIN(1, 4), RCAR_GP_PIN(1, 5),
+ RCAR_GP_PIN(1, 6), RCAR_GP_PIN(1, 7),
+ RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1),
+ RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3),
+ RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5),
+ RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7),
+ },
+};
+static const union vin_data vin4_data_b_mux = {
+ .data24 = {
+ VI4_DATA0_B_MARK, VI4_DATA1_B_MARK,
+ VI4_DATA2_B_MARK, VI4_DATA3_B_MARK,
+ VI4_DATA4_B_MARK, VI4_DATA5_B_MARK,
+ VI4_DATA6_B_MARK, VI4_DATA7_B_MARK,
+ VI4_DATA8_MARK, VI4_DATA9_MARK,
+ VI4_DATA10_MARK, VI4_DATA11_MARK,
+ VI4_DATA12_MARK, VI4_DATA13_MARK,
+ VI4_DATA14_MARK, VI4_DATA15_MARK,
+ VI4_DATA16_MARK, VI4_DATA17_MARK,
+ VI4_DATA18_MARK, VI4_DATA19_MARK,
+ VI4_DATA20_MARK, VI4_DATA21_MARK,
+ VI4_DATA22_MARK, VI4_DATA23_MARK,
+ },
+};
+static const unsigned int vin4_sync_pins[] = {
+ /* HSYNC#, VSYNC# */
+ RCAR_GP_PIN(1, 18), RCAR_GP_PIN(1, 17),
+};
+static const unsigned int vin4_sync_mux[] = {
+ VI4_HSYNC_N_MARK, VI4_VSYNC_N_MARK,
+};
+static const unsigned int vin4_field_pins[] = {
+ /* FIELD */
+ RCAR_GP_PIN(1, 16),
+};
+static const unsigned int vin4_field_mux[] = {
+ VI4_FIELD_MARK,
+};
+static const unsigned int vin4_clkenb_pins[] = {
+ /* CLKENB */
+ RCAR_GP_PIN(1, 19),
+};
+static const unsigned int vin4_clkenb_mux[] = {
+ VI4_CLKENB_MARK,
+};
+static const unsigned int vin4_clk_pins[] = {
+ /* CLK */
+ RCAR_GP_PIN(1, 27),
+};
+static const unsigned int vin4_clk_mux[] = {
+ VI4_CLK_MARK,
+};
+
+/* - VIN5 ------------------------------------------------------------------- */
+static const unsigned int vin5_data8_pins[] = {
+ RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1),
+ RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3),
+ RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5),
+ RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7),
+};
+static const unsigned int vin5_data8_mux[] = {
+ VI5_DATA0_MARK, VI5_DATA1_MARK,
+ VI5_DATA2_MARK, VI5_DATA3_MARK,
+ VI5_DATA4_MARK, VI5_DATA5_MARK,
+ VI5_DATA6_MARK, VI5_DATA7_MARK,
+};
+static const unsigned int vin5_data10_pins[] = {
+ RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1),
+ RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3),
+ RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5),
+ RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7),
+ RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13),
+};
+static const unsigned int vin5_data10_mux[] = {
+ VI5_DATA0_MARK, VI5_DATA1_MARK,
+ VI5_DATA2_MARK, VI5_DATA3_MARK,
+ VI5_DATA4_MARK, VI5_DATA5_MARK,
+ VI5_DATA6_MARK, VI5_DATA7_MARK,
+ VI5_DATA8_MARK, VI5_DATA9_MARK,
+};
+static const unsigned int vin5_data12_pins[] = {
+ RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1),
+ RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3),
+ RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5),
+ RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7),
+ RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13),
+ RCAR_GP_PIN(1, 14), RCAR_GP_PIN(1, 15),
+};
+static const unsigned int vin5_data12_mux[] = {
+ VI5_DATA0_MARK, VI5_DATA1_MARK,
+ VI5_DATA2_MARK, VI5_DATA3_MARK,
+ VI5_DATA4_MARK, VI5_DATA5_MARK,
+ VI5_DATA6_MARK, VI5_DATA7_MARK,
+ VI5_DATA8_MARK, VI5_DATA9_MARK,
+ VI5_DATA10_MARK, VI5_DATA11_MARK,
+};
+static const unsigned int vin5_data16_pins[] = {
+ RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1),
+ RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3),
+ RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5),
+ RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7),
+ RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13),
+ RCAR_GP_PIN(1, 14), RCAR_GP_PIN(1, 15),
+ RCAR_GP_PIN(1, 4), RCAR_GP_PIN(1, 5),
+ RCAR_GP_PIN(1, 6), RCAR_GP_PIN(1, 7),
+};
+static const unsigned int vin5_data16_mux[] = {
+ VI5_DATA0_MARK, VI5_DATA1_MARK,
+ VI5_DATA2_MARK, VI5_DATA3_MARK,
+ VI5_DATA4_MARK, VI5_DATA5_MARK,
+ VI5_DATA6_MARK, VI5_DATA7_MARK,
+ VI5_DATA8_MARK, VI5_DATA9_MARK,
+ VI5_DATA10_MARK, VI5_DATA11_MARK,
+ VI5_DATA12_MARK, VI5_DATA13_MARK,
+ VI5_DATA14_MARK, VI5_DATA15_MARK,
+};
+static const unsigned int vin5_sync_pins[] = {
+ /* HSYNC#, VSYNC# */
+ RCAR_GP_PIN(1, 10), RCAR_GP_PIN(1, 9),
+};
+static const unsigned int vin5_sync_mux[] = {
+ VI5_HSYNC_N_MARK, VI5_VSYNC_N_MARK,
+};
+static const unsigned int vin5_field_pins[] = {
+ RCAR_GP_PIN(1, 11),
+};
+static const unsigned int vin5_field_mux[] = {
+ /* FIELD */
+ VI5_FIELD_MARK,
+};
+static const unsigned int vin5_clkenb_pins[] = {
+ RCAR_GP_PIN(1, 20),
+};
+static const unsigned int vin5_clkenb_mux[] = {
+ /* CLKENB */
+ VI5_CLKENB_MARK,
+};
+static const unsigned int vin5_clk_pins[] = {
+ RCAR_GP_PIN(1, 21),
+};
+static const unsigned int vin5_clk_mux[] = {
+ /* CLK */
+ VI5_CLK_MARK,
+};
+
static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(audio_clk_a_a),
SH_PFC_PIN_GROUP(audio_clk_a_b),
@@ -3874,7 +4147,8 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(avb_link),
SH_PFC_PIN_GROUP(avb_magic),
SH_PFC_PIN_GROUP(avb_phy_int),
- SH_PFC_PIN_GROUP(avb_mdc),
+ SH_PFC_PIN_GROUP_ALIAS(avb_mdc, avb_mdio), /* Deprecated */
+ SH_PFC_PIN_GROUP(avb_mdio),
SH_PFC_PIN_GROUP(avb_mii),
SH_PFC_PIN_GROUP(avb_avtp_pps),
SH_PFC_PIN_GROUP(avb_avtp_match_a),
@@ -3926,6 +4200,7 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(du_oddf),
SH_PFC_PIN_GROUP(du_cde),
SH_PFC_PIN_GROUP(du_disp),
+ SH_PFC_PIN_GROUP(hdmi0_cec),
SH_PFC_PIN_GROUP(hscif0_data),
SH_PFC_PIN_GROUP(hscif0_clk),
SH_PFC_PIN_GROUP(hscif0_ctrl),
@@ -4159,9 +4434,39 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(ssi9_data_b),
SH_PFC_PIN_GROUP(ssi9_ctrl_a),
SH_PFC_PIN_GROUP(ssi9_ctrl_b),
+ SH_PFC_PIN_GROUP(tmu_tclk1_a),
+ SH_PFC_PIN_GROUP(tmu_tclk1_b),
+ SH_PFC_PIN_GROUP(tmu_tclk2_a),
+ SH_PFC_PIN_GROUP(tmu_tclk2_b),
SH_PFC_PIN_GROUP(usb0),
SH_PFC_PIN_GROUP(usb1),
SH_PFC_PIN_GROUP(usb30),
+ VIN_DATA_PIN_GROUP(vin4_data_a, 8),
+ VIN_DATA_PIN_GROUP(vin4_data_a, 10),
+ VIN_DATA_PIN_GROUP(vin4_data_a, 12),
+ VIN_DATA_PIN_GROUP(vin4_data_a, 16),
+ SH_PFC_PIN_GROUP(vin4_data18_a),
+ VIN_DATA_PIN_GROUP(vin4_data_a, 20),
+ VIN_DATA_PIN_GROUP(vin4_data_a, 24),
+ VIN_DATA_PIN_GROUP(vin4_data_b, 8),
+ VIN_DATA_PIN_GROUP(vin4_data_b, 10),
+ VIN_DATA_PIN_GROUP(vin4_data_b, 12),
+ VIN_DATA_PIN_GROUP(vin4_data_b, 16),
+ SH_PFC_PIN_GROUP(vin4_data18_b),
+ VIN_DATA_PIN_GROUP(vin4_data_b, 20),
+ VIN_DATA_PIN_GROUP(vin4_data_b, 24),
+ SH_PFC_PIN_GROUP(vin4_sync),
+ SH_PFC_PIN_GROUP(vin4_field),
+ SH_PFC_PIN_GROUP(vin4_clkenb),
+ SH_PFC_PIN_GROUP(vin4_clk),
+ SH_PFC_PIN_GROUP(vin5_data8),
+ SH_PFC_PIN_GROUP(vin5_data10),
+ SH_PFC_PIN_GROUP(vin5_data12),
+ SH_PFC_PIN_GROUP(vin5_data16),
+ SH_PFC_PIN_GROUP(vin5_sync),
+ SH_PFC_PIN_GROUP(vin5_field),
+ SH_PFC_PIN_GROUP(vin5_clkenb),
+ SH_PFC_PIN_GROUP(vin5_clk),
};
static const char * const audio_clk_groups[] = {
@@ -4188,7 +4493,8 @@ static const char * const avb_groups[] = {
"avb_link",
"avb_magic",
"avb_phy_int",
- "avb_mdc",
+ "avb_mdc", /* Deprecated, please use "avb_mdio" instead */
+ "avb_mdio",
"avb_mii",
"avb_avtp_pps",
"avb_avtp_match_a",
@@ -4272,6 +4578,10 @@ static const char * const du_groups[] = {
"du_disp",
};
+static const char * const hdmi0_groups[] = {
+ "hdmi0_cec",
+};
+
static const char * const hscif0_groups[] = {
"hscif0_data",
"hscif0_clk",
@@ -4601,6 +4911,13 @@ static const char * const ssi_groups[] = {
"ssi9_ctrl_b",
};
+static const char * const tmu_groups[] = {
+ "tmu_tclk1_a",
+ "tmu_tclk1_b",
+ "tmu_tclk2_a",
+ "tmu_tclk2_b",
+};
+
static const char * const usb0_groups[] = {
"usb0",
};
@@ -4613,6 +4930,38 @@ static const char * const usb30_groups[] = {
"usb30",
};
+static const char * const vin4_groups[] = {
+ "vin4_data8_a",
+ "vin4_data10_a",
+ "vin4_data12_a",
+ "vin4_data16_a",
+ "vin4_data18_a",
+ "vin4_data20_a",
+ "vin4_data24_a",
+ "vin4_data8_b",
+ "vin4_data10_b",
+ "vin4_data12_b",
+ "vin4_data16_b",
+ "vin4_data18_b",
+ "vin4_data20_b",
+ "vin4_data24_b",
+ "vin4_sync",
+ "vin4_field",
+ "vin4_clkenb",
+ "vin4_clk",
+};
+
+static const char * const vin5_groups[] = {
+ "vin5_data8",
+ "vin5_data10",
+ "vin5_data12",
+ "vin5_data16",
+ "vin5_sync",
+ "vin5_field",
+ "vin5_clkenb",
+ "vin5_clk",
+};
+
static const struct sh_pfc_function pinmux_functions[] = {
SH_PFC_FUNCTION(audio_clk),
SH_PFC_FUNCTION(avb),
@@ -4626,6 +4975,7 @@ static const struct sh_pfc_function pinmux_functions[] = {
SH_PFC_FUNCTION(drif2),
SH_PFC_FUNCTION(drif3),
SH_PFC_FUNCTION(du),
+ SH_PFC_FUNCTION(hdmi0),
SH_PFC_FUNCTION(hscif0),
SH_PFC_FUNCTION(hscif1),
SH_PFC_FUNCTION(hscif2),
@@ -4658,9 +5008,12 @@ static const struct sh_pfc_function pinmux_functions[] = {
SH_PFC_FUNCTION(sdhi2),
SH_PFC_FUNCTION(sdhi3),
SH_PFC_FUNCTION(ssi),
+ SH_PFC_FUNCTION(tmu),
SH_PFC_FUNCTION(usb0),
SH_PFC_FUNCTION(usb1),
SH_PFC_FUNCTION(usb30),
+ SH_PFC_FUNCTION(vin4),
+ SH_PFC_FUNCTION(vin5),
};
static const struct pinmux_cfg_reg pinmux_config_regs[] = {
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77965.c b/drivers/pinctrl/sh-pfc/pfc-r8a77965.c
new file mode 100644
index 000000000000..cea9d0599c12
--- /dev/null
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a77965.c
@@ -0,0 +1,3243 @@
+// SPDX-License-Identifier: GPL-2.
+/*
+ * R8A77965 processor support - PFC hardware block.
+ *
+ * Copyright (C) 2018 Jacopo Mondi <jacopo+renesas@jmondi.org>
+ * Copyright (C) 2016 Renesas Electronics Corp.
+ *
+ * This file is based on the drivers/pinctrl/sh-pfc/pfc-r8a7796.c
+ *
+ * R-Car Gen3 processor support - PFC hardware block.
+ *
+ * Copyright (C) 2015 Renesas Electronics Corporation
+ */
+
+#include <linux/kernel.h>
+
+#include "core.h"
+#include "sh_pfc.h"
+
+#define CFG_FLAGS (SH_PFC_PIN_CFG_DRIVE_STRENGTH | \
+ SH_PFC_PIN_CFG_PULL_UP | \
+ SH_PFC_PIN_CFG_PULL_DOWN)
+
+#define CPU_ALL_PORT(fn, sfx) \
+ PORT_GP_CFG_16(0, fn, sfx, CFG_FLAGS), \
+ PORT_GP_CFG_29(1, fn, sfx, CFG_FLAGS), \
+ PORT_GP_CFG_15(2, fn, sfx, CFG_FLAGS), \
+ PORT_GP_CFG_12(3, fn, sfx, CFG_FLAGS | SH_PFC_PIN_CFG_IO_VOLTAGE), \
+ PORT_GP_CFG_1(3, 12, fn, sfx, CFG_FLAGS), \
+ PORT_GP_CFG_1(3, 13, fn, sfx, CFG_FLAGS), \
+ PORT_GP_CFG_1(3, 14, fn, sfx, CFG_FLAGS), \
+ PORT_GP_CFG_1(3, 15, fn, sfx, CFG_FLAGS), \
+ PORT_GP_CFG_18(4, fn, sfx, CFG_FLAGS | SH_PFC_PIN_CFG_IO_VOLTAGE), \
+ PORT_GP_CFG_26(5, fn, sfx, CFG_FLAGS), \
+ PORT_GP_CFG_32(6, fn, sfx, CFG_FLAGS), \
+ PORT_GP_CFG_4(7, fn, sfx, CFG_FLAGS)
+/*
+ * F_() : just information
+ * FM() : macro for FN_xxx / xxx_MARK
+ */
+
+/* GPSR0 */
+#define GPSR0_15 F_(D15, IP7_11_8)
+#define GPSR0_14 F_(D14, IP7_7_4)
+#define GPSR0_13 F_(D13, IP7_3_0)
+#define GPSR0_12 F_(D12, IP6_31_28)
+#define GPSR0_11 F_(D11, IP6_27_24)
+#define GPSR0_10 F_(D10, IP6_23_20)
+#define GPSR0_9 F_(D9, IP6_19_16)
+#define GPSR0_8 F_(D8, IP6_15_12)
+#define GPSR0_7 F_(D7, IP6_11_8)
+#define GPSR0_6 F_(D6, IP6_7_4)
+#define GPSR0_5 F_(D5, IP6_3_0)
+#define GPSR0_4 F_(D4, IP5_31_28)
+#define GPSR0_3 F_(D3, IP5_27_24)
+#define GPSR0_2 F_(D2, IP5_23_20)
+#define GPSR0_1 F_(D1, IP5_19_16)
+#define GPSR0_0 F_(D0, IP5_15_12)
+
+/* GPSR1 */
+#define GPSR1_28 FM(CLKOUT)
+#define GPSR1_27 F_(EX_WAIT0_A, IP5_11_8)
+#define GPSR1_26 F_(WE1_N, IP5_7_4)
+#define GPSR1_25 F_(WE0_N, IP5_3_0)
+#define GPSR1_24 F_(RD_WR_N, IP4_31_28)
+#define GPSR1_23 F_(RD_N, IP4_27_24)
+#define GPSR1_22 F_(BS_N, IP4_23_20)
+#define GPSR1_21 F_(CS1_N, IP4_19_16)
+#define GPSR1_20 F_(CS0_N, IP4_15_12)
+#define GPSR1_19 F_(A19, IP4_11_8)
+#define GPSR1_18 F_(A18, IP4_7_4)
+#define GPSR1_17 F_(A17, IP4_3_0)
+#define GPSR1_16 F_(A16, IP3_31_28)
+#define GPSR1_15 F_(A15, IP3_27_24)
+#define GPSR1_14 F_(A14, IP3_23_20)
+#define GPSR1_13 F_(A13, IP3_19_16)
+#define GPSR1_12 F_(A12, IP3_15_12)
+#define GPSR1_11 F_(A11, IP3_11_8)
+#define GPSR1_10 F_(A10, IP3_7_4)
+#define GPSR1_9 F_(A9, IP3_3_0)
+#define GPSR1_8 F_(A8, IP2_31_28)
+#define GPSR1_7 F_(A7, IP2_27_24)
+#define GPSR1_6 F_(A6, IP2_23_20)
+#define GPSR1_5 F_(A5, IP2_19_16)
+#define GPSR1_4 F_(A4, IP2_15_12)
+#define GPSR1_3 F_(A3, IP2_11_8)
+#define GPSR1_2 F_(A2, IP2_7_4)
+#define GPSR1_1 F_(A1, IP2_3_0)
+#define GPSR1_0 F_(A0, IP1_31_28)
+
+/* GPSR2 */
+#define GPSR2_14 F_(AVB_AVTP_CAPTURE_A, IP0_23_20)
+#define GPSR2_13 F_(AVB_AVTP_MATCH_A, IP0_19_16)
+#define GPSR2_12 F_(AVB_LINK, IP0_15_12)
+#define GPSR2_11 F_(AVB_PHY_INT, IP0_11_8)
+#define GPSR2_10 F_(AVB_MAGIC, IP0_7_4)
+#define GPSR2_9 F_(AVB_MDC, IP0_3_0)
+#define GPSR2_8 F_(PWM2_A, IP1_27_24)
+#define GPSR2_7 F_(PWM1_A, IP1_23_20)
+#define GPSR2_6 F_(PWM0, IP1_19_16)
+#define GPSR2_5 F_(IRQ5, IP1_15_12)
+#define GPSR2_4 F_(IRQ4, IP1_11_8)
+#define GPSR2_3 F_(IRQ3, IP1_7_4)
+#define GPSR2_2 F_(IRQ2, IP1_3_0)
+#define GPSR2_1 F_(IRQ1, IP0_31_28)
+#define GPSR2_0 F_(IRQ0, IP0_27_24)
+
+/* GPSR3 */
+#define GPSR3_15 F_(SD1_WP, IP11_23_20)
+#define GPSR3_14 F_(SD1_CD, IP11_19_16)
+#define GPSR3_13 F_(SD0_WP, IP11_15_12)
+#define GPSR3_12 F_(SD0_CD, IP11_11_8)
+#define GPSR3_11 F_(SD1_DAT3, IP8_31_28)
+#define GPSR3_10 F_(SD1_DAT2, IP8_27_24)
+#define GPSR3_9 F_(SD1_DAT1, IP8_23_20)
+#define GPSR3_8 F_(SD1_DAT0, IP8_19_16)
+#define GPSR3_7 F_(SD1_CMD, IP8_15_12)
+#define GPSR3_6 F_(SD1_CLK, IP8_11_8)
+#define GPSR3_5 F_(SD0_DAT3, IP8_7_4)
+#define GPSR3_4 F_(SD0_DAT2, IP8_3_0)
+#define GPSR3_3 F_(SD0_DAT1, IP7_31_28)
+#define GPSR3_2 F_(SD0_DAT0, IP7_27_24)
+#define GPSR3_1 F_(SD0_CMD, IP7_23_20)
+#define GPSR3_0 F_(SD0_CLK, IP7_19_16)
+
+/* GPSR4 */
+#define GPSR4_17 F_(SD3_DS, IP11_7_4)
+#define GPSR4_16 F_(SD3_DAT7, IP11_3_0)
+#define GPSR4_15 F_(SD3_DAT6, IP10_31_28)
+#define GPSR4_14 F_(SD3_DAT5, IP10_27_24)
+#define GPSR4_13 F_(SD3_DAT4, IP10_23_20)
+#define GPSR4_12 F_(SD3_DAT3, IP10_19_16)
+#define GPSR4_11 F_(SD3_DAT2, IP10_15_12)
+#define GPSR4_10 F_(SD3_DAT1, IP10_11_8)
+#define GPSR4_9 F_(SD3_DAT0, IP10_7_4)
+#define GPSR4_8 F_(SD3_CMD, IP10_3_0)
+#define GPSR4_7 F_(SD3_CLK, IP9_31_28)
+#define GPSR4_6 F_(SD2_DS, IP9_27_24)
+#define GPSR4_5 F_(SD2_DAT3, IP9_23_20)
+#define GPSR4_4 F_(SD2_DAT2, IP9_19_16)
+#define GPSR4_3 F_(SD2_DAT1, IP9_15_12)
+#define GPSR4_2 F_(SD2_DAT0, IP9_11_8)
+#define GPSR4_1 F_(SD2_CMD, IP9_7_4)
+#define GPSR4_0 F_(SD2_CLK, IP9_3_0)
+
+/* GPSR5 */
+#define GPSR5_25 F_(MLB_DAT, IP14_19_16)
+#define GPSR5_24 F_(MLB_SIG, IP14_15_12)
+#define GPSR5_23 F_(MLB_CLK, IP14_11_8)
+#define GPSR5_22 FM(MSIOF0_RXD)
+#define GPSR5_21 F_(MSIOF0_SS2, IP14_7_4)
+#define GPSR5_20 FM(MSIOF0_TXD)
+#define GPSR5_19 F_(MSIOF0_SS1, IP14_3_0)
+#define GPSR5_18 F_(MSIOF0_SYNC, IP13_31_28)
+#define GPSR5_17 FM(MSIOF0_SCK)
+#define GPSR5_16 F_(HRTS0_N, IP13_27_24)
+#define GPSR5_15 F_(HCTS0_N, IP13_23_20)
+#define GPSR5_14 F_(HTX0, IP13_19_16)
+#define GPSR5_13 F_(HRX0, IP13_15_12)
+#define GPSR5_12 F_(HSCK0, IP13_11_8)
+#define GPSR5_11 F_(RX2_A, IP13_7_4)
+#define GPSR5_10 F_(TX2_A, IP13_3_0)
+#define GPSR5_9 F_(SCK2, IP12_31_28)
+#define GPSR5_8 F_(RTS1_N, IP12_27_24)
+#define GPSR5_7 F_(CTS1_N, IP12_23_20)
+#define GPSR5_6 F_(TX1_A, IP12_19_16)
+#define GPSR5_5 F_(RX1_A, IP12_15_12)
+#define GPSR5_4 F_(RTS0_N, IP12_11_8)
+#define GPSR5_3 F_(CTS0_N, IP12_7_4)
+#define GPSR5_2 F_(TX0, IP12_3_0)
+#define GPSR5_1 F_(RX0, IP11_31_28)
+#define GPSR5_0 F_(SCK0, IP11_27_24)
+
+/* GPSR6 */
+#define GPSR6_31 F_(GP6_31, IP18_7_4)
+#define GPSR6_30 F_(GP6_30, IP18_3_0)
+#define GPSR6_29 F_(USB30_OVC, IP17_31_28)
+#define GPSR6_28 F_(USB30_PWEN, IP17_27_24)
+#define GPSR6_27 F_(USB1_OVC, IP17_23_20)
+#define GPSR6_26 F_(USB1_PWEN, IP17_19_16)
+#define GPSR6_25 F_(USB0_OVC, IP17_15_12)
+#define GPSR6_24 F_(USB0_PWEN, IP17_11_8)
+#define GPSR6_23 F_(AUDIO_CLKB_B, IP17_7_4)
+#define GPSR6_22 F_(AUDIO_CLKA_A, IP17_3_0)
+#define GPSR6_21 F_(SSI_SDATA9_A, IP16_31_28)
+#define GPSR6_20 F_(SSI_SDATA8, IP16_27_24)
+#define GPSR6_19 F_(SSI_SDATA7, IP16_23_20)
+#define GPSR6_18 F_(SSI_WS78, IP16_19_16)
+#define GPSR6_17 F_(SSI_SCK78, IP16_15_12)
+#define GPSR6_16 F_(SSI_SDATA6, IP16_11_8)
+#define GPSR6_15 F_(SSI_WS6, IP16_7_4)
+#define GPSR6_14 F_(SSI_SCK6, IP16_3_0)
+#define GPSR6_13 FM(SSI_SDATA5)
+#define GPSR6_12 FM(SSI_WS5)
+#define GPSR6_11 FM(SSI_SCK5)
+#define GPSR6_10 F_(SSI_SDATA4, IP15_31_28)
+#define GPSR6_9 F_(SSI_WS4, IP15_27_24)
+#define GPSR6_8 F_(SSI_SCK4, IP15_23_20)
+#define GPSR6_7 F_(SSI_SDATA3, IP15_19_16)
+#define GPSR6_6 F_(SSI_WS349, IP15_15_12)
+#define GPSR6_5 F_(SSI_SCK349, IP15_11_8)
+#define GPSR6_4 F_(SSI_SDATA2_A, IP15_7_4)
+#define GPSR6_3 F_(SSI_SDATA1_A, IP15_3_0)
+#define GPSR6_2 F_(SSI_SDATA0, IP14_31_28)
+#define GPSR6_1 F_(SSI_WS01239, IP14_27_24)
+#define GPSR6_0 F_(SSI_SCK01239, IP14_23_20)
+
+/* GPSR7 */
+#define GPSR7_3 FM(GP7_03)
+#define GPSR7_2 FM(HDMI0_CEC)
+#define GPSR7_1 FM(AVS2)
+#define GPSR7_0 FM(AVS1)
+
+
+/* IPSRx */ /* 0 */ /* 1 */ /* 2 */ /* 3 */ /* 4 */ /* 5 */ /* 6 */ /* 7 */ /* 8 */ /* 9 */ /* A */ /* B */ /* C - F */
+#define IP0_3_0 FM(AVB_MDC) F_(0, 0) FM(MSIOF2_SS2_C) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP0_7_4 FM(AVB_MAGIC) F_(0, 0) FM(MSIOF2_SS1_C) FM(SCK4_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP0_11_8 FM(AVB_PHY_INT) F_(0, 0) FM(MSIOF2_SYNC_C) FM(RX4_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP0_15_12 FM(AVB_LINK) F_(0, 0) FM(MSIOF2_SCK_C) FM(TX4_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP0_19_16 FM(AVB_AVTP_MATCH_A) F_(0, 0) FM(MSIOF2_RXD_C) FM(CTS4_N_A) F_(0, 0) FM(FSCLKST2_N_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP0_23_20 FM(AVB_AVTP_CAPTURE_A) F_(0, 0) FM(MSIOF2_TXD_C) FM(RTS4_N_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP0_27_24 FM(IRQ0) FM(QPOLB) F_(0, 0) FM(DU_CDE) FM(VI4_DATA0_B) FM(CAN0_TX_B) FM(CANFD0_TX_B) FM(MSIOF3_SS2_E) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP0_31_28 FM(IRQ1) FM(QPOLA) F_(0, 0) FM(DU_DISP) FM(VI4_DATA1_B) FM(CAN0_RX_B) FM(CANFD0_RX_B) FM(MSIOF3_SS1_E) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP1_3_0 FM(IRQ2) FM(QCPV_QDE) F_(0, 0) FM(DU_EXODDF_DU_ODDF_DISP_CDE) FM(VI4_DATA2_B) F_(0, 0) F_(0, 0) FM(MSIOF3_SYNC_E) F_(0, 0) FM(PWM3_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP1_7_4 FM(IRQ3) FM(QSTVB_QVE) F_(0, 0) FM(DU_DOTCLKOUT1) FM(VI4_DATA3_B) F_(0, 0) F_(0, 0) FM(MSIOF3_SCK_E) F_(0, 0) FM(PWM4_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP1_11_8 FM(IRQ4) FM(QSTH_QHS) F_(0, 0) FM(DU_EXHSYNC_DU_HSYNC) FM(VI4_DATA4_B) F_(0, 0) F_(0, 0) FM(MSIOF3_RXD_E) F_(0, 0) FM(PWM5_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP1_15_12 FM(IRQ5) FM(QSTB_QHE) F_(0, 0) FM(DU_EXVSYNC_DU_VSYNC) FM(VI4_DATA5_B) FM(FSCLKST2_N_B) F_(0, 0) FM(MSIOF3_TXD_E) F_(0, 0) FM(PWM6_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP1_19_16 FM(PWM0) FM(AVB_AVTP_PPS)F_(0, 0) F_(0, 0) FM(VI4_DATA6_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) FM(IECLK_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP1_23_20 FM(PWM1_A) F_(0, 0) F_(0, 0) FM(HRX3_D) FM(VI4_DATA7_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) FM(IERX_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP1_27_24 FM(PWM2_A) F_(0, 0) F_(0, 0) FM(HTX3_D) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) FM(IETX_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP1_31_28 FM(A0) FM(LCDOUT16) FM(MSIOF3_SYNC_B) F_(0, 0) FM(VI4_DATA8) F_(0, 0) FM(DU_DB0) F_(0, 0) F_(0, 0) FM(PWM3_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP2_3_0 FM(A1) FM(LCDOUT17) FM(MSIOF3_TXD_B) F_(0, 0) FM(VI4_DATA9) F_(0, 0) FM(DU_DB1) F_(0, 0) F_(0, 0) FM(PWM4_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP2_7_4 FM(A2) FM(LCDOUT18) FM(MSIOF3_SCK_B) F_(0, 0) FM(VI4_DATA10) F_(0, 0) FM(DU_DB2) F_(0, 0) F_(0, 0) FM(PWM5_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP2_11_8 FM(A3) FM(LCDOUT19) FM(MSIOF3_RXD_B) F_(0, 0) FM(VI4_DATA11) F_(0, 0) FM(DU_DB3) F_(0, 0) F_(0, 0) FM(PWM6_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP2_15_12 FM(A4) FM(LCDOUT20) FM(MSIOF3_SS1_B) F_(0, 0) FM(VI4_DATA12) FM(VI5_DATA12) FM(DU_DB4) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP2_19_16 FM(A5) FM(LCDOUT21) FM(MSIOF3_SS2_B) FM(SCK4_B) FM(VI4_DATA13) FM(VI5_DATA13) FM(DU_DB5) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP2_23_20 FM(A6) FM(LCDOUT22) FM(MSIOF2_SS1_A) FM(RX4_B) FM(VI4_DATA14) FM(VI5_DATA14) FM(DU_DB6) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP2_27_24 FM(A7) FM(LCDOUT23) FM(MSIOF2_SS2_A) FM(TX4_B) FM(VI4_DATA15) FM(VI5_DATA15) FM(DU_DB7) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP2_31_28 FM(A8) FM(RX3_B) FM(MSIOF2_SYNC_A) FM(HRX4_B) F_(0, 0) F_(0, 0) F_(0, 0) FM(SDA6_A) FM(AVB_AVTP_MATCH_B) FM(PWM1_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP3_3_0 FM(A9) F_(0, 0) FM(MSIOF2_SCK_A) FM(CTS4_N_B) F_(0, 0) FM(VI5_VSYNC_N) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP3_7_4 FM(A10) F_(0, 0) FM(MSIOF2_RXD_A) FM(RTS4_N_B) F_(0, 0) FM(VI5_HSYNC_N) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP3_11_8 FM(A11) FM(TX3_B) FM(MSIOF2_TXD_A) FM(HTX4_B) FM(HSCK4) FM(VI5_FIELD) F_(0, 0) FM(SCL6_A) FM(AVB_AVTP_CAPTURE_B) FM(PWM2_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+
+/* IPSRx */ /* 0 */ /* 1 */ /* 2 */ /* 3 */ /* 4 */ /* 5 */ /* 6 */ /* 7 */ /* 8 */ /* 9 */ /* A */ /* B */ /* C - F */
+#define IP3_15_12 FM(A12) FM(LCDOUT12) FM(MSIOF3_SCK_C) F_(0, 0) FM(HRX4_A) FM(VI5_DATA8) FM(DU_DG4) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP3_19_16 FM(A13) FM(LCDOUT13) FM(MSIOF3_SYNC_C) F_(0, 0) FM(HTX4_A) FM(VI5_DATA9) FM(DU_DG5) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP3_23_20 FM(A14) FM(LCDOUT14) FM(MSIOF3_RXD_C) F_(0, 0) FM(HCTS4_N) FM(VI5_DATA10) FM(DU_DG6) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP3_27_24 FM(A15) FM(LCDOUT15) FM(MSIOF3_TXD_C) F_(0, 0) FM(HRTS4_N) FM(VI5_DATA11) FM(DU_DG7) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP3_31_28 FM(A16) FM(LCDOUT8) F_(0, 0) F_(0, 0) FM(VI4_FIELD) F_(0, 0) FM(DU_DG0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP4_3_0 FM(A17) FM(LCDOUT9) F_(0, 0) F_(0, 0) FM(VI4_VSYNC_N) F_(0, 0) FM(DU_DG1) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP4_7_4 FM(A18) FM(LCDOUT10) F_(0, 0) F_(0, 0) FM(VI4_HSYNC_N) F_(0, 0) FM(DU_DG2) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP4_11_8 FM(A19) FM(LCDOUT11) F_(0, 0) F_(0, 0) FM(VI4_CLKENB) F_(0, 0) FM(DU_DG3) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP4_15_12 FM(CS0_N) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) FM(VI5_CLKENB) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP4_19_16 FM(CS1_N) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) FM(VI5_CLK) F_(0, 0) FM(EX_WAIT0_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP4_23_20 FM(BS_N) FM(QSTVA_QVS) FM(MSIOF3_SCK_D) FM(SCK3) FM(HSCK3) F_(0, 0) F_(0, 0) F_(0, 0) FM(CAN1_TX) FM(CANFD1_TX) FM(IETX_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP4_27_24 FM(RD_N) F_(0, 0) FM(MSIOF3_SYNC_D) FM(RX3_A) FM(HRX3_A) F_(0, 0) F_(0, 0) F_(0, 0) FM(CAN0_TX_A) FM(CANFD0_TX_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP4_31_28 FM(RD_WR_N) F_(0, 0) FM(MSIOF3_RXD_D) FM(TX3_A) FM(HTX3_A) F_(0, 0) F_(0, 0) F_(0, 0) FM(CAN0_RX_A) FM(CANFD0_RX_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP5_3_0 FM(WE0_N) F_(0, 0) FM(MSIOF3_TXD_D) FM(CTS3_N) FM(HCTS3_N) F_(0, 0) F_(0, 0) FM(SCL6_B) FM(CAN_CLK) F_(0, 0) FM(IECLK_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP5_7_4 FM(WE1_N) F_(0, 0) FM(MSIOF3_SS1_D) FM(RTS3_N) FM(HRTS3_N) F_(0, 0) F_(0, 0) FM(SDA6_B) FM(CAN1_RX) FM(CANFD1_RX) FM(IERX_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP5_11_8 FM(EX_WAIT0_A) FM(QCLK) F_(0, 0) F_(0, 0) FM(VI4_CLK) F_(0, 0) FM(DU_DOTCLKOUT0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP5_15_12 FM(D0) FM(MSIOF2_SS1_B)FM(MSIOF3_SCK_A) F_(0, 0) FM(VI4_DATA16) FM(VI5_DATA0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP5_19_16 FM(D1) FM(MSIOF2_SS2_B)FM(MSIOF3_SYNC_A) F_(0, 0) FM(VI4_DATA17) FM(VI5_DATA1) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP5_23_20 FM(D2) F_(0, 0) FM(MSIOF3_RXD_A) F_(0, 0) FM(VI4_DATA18) FM(VI5_DATA2) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP5_27_24 FM(D3) F_(0, 0) FM(MSIOF3_TXD_A) F_(0, 0) FM(VI4_DATA19) FM(VI5_DATA3) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP5_31_28 FM(D4) FM(MSIOF2_SCK_B)F_(0, 0) F_(0, 0) FM(VI4_DATA20) FM(VI5_DATA4) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP6_3_0 FM(D5) FM(MSIOF2_SYNC_B)F_(0, 0) F_(0, 0) FM(VI4_DATA21) FM(VI5_DATA5) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP6_7_4 FM(D6) FM(MSIOF2_RXD_B)F_(0, 0) F_(0, 0) FM(VI4_DATA22) FM(VI5_DATA6) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP6_11_8 FM(D7) FM(MSIOF2_TXD_B)F_(0, 0) F_(0, 0) FM(VI4_DATA23) FM(VI5_DATA7) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP6_15_12 FM(D8) FM(LCDOUT0) FM(MSIOF2_SCK_D) FM(SCK4_C) FM(VI4_DATA0_A) F_(0, 0) FM(DU_DR0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP6_19_16 FM(D9) FM(LCDOUT1) FM(MSIOF2_SYNC_D) F_(0, 0) FM(VI4_DATA1_A) F_(0, 0) FM(DU_DR1) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP6_23_20 FM(D10) FM(LCDOUT2) FM(MSIOF2_RXD_D) FM(HRX3_B) FM(VI4_DATA2_A) FM(CTS4_N_C) FM(DU_DR2) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP6_27_24 FM(D11) FM(LCDOUT3) FM(MSIOF2_TXD_D) FM(HTX3_B) FM(VI4_DATA3_A) FM(RTS4_N_C) FM(DU_DR3) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP6_31_28 FM(D12) FM(LCDOUT4) FM(MSIOF2_SS1_D) FM(RX4_C) FM(VI4_DATA4_A) F_(0, 0) FM(DU_DR4) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+
+/* IPSRx */ /* 0 */ /* 1 */ /* 2 */ /* 3 */ /* 4 */ /* 5 */ /* 6 */ /* 7 */ /* 8 */ /* 9 */ /* A */ /* B */ /* C - F */
+#define IP7_3_0 FM(D13) FM(LCDOUT5) FM(MSIOF2_SS2_D) FM(TX4_C) FM(VI4_DATA5_A) F_(0, 0) FM(DU_DR5) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP7_7_4 FM(D14) FM(LCDOUT6) FM(MSIOF3_SS1_A) FM(HRX3_C) FM(VI4_DATA6_A) F_(0, 0) FM(DU_DR6) FM(SCL6_C) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP7_11_8 FM(D15) FM(LCDOUT7) FM(MSIOF3_SS2_A) FM(HTX3_C) FM(VI4_DATA7_A) F_(0, 0) FM(DU_DR7) FM(SDA6_C) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP7_19_16 FM(SD0_CLK) F_(0, 0) FM(MSIOF1_SCK_E) F_(0, 0) F_(0, 0) F_(0, 0) FM(STP_OPWM_0_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP7_23_20 FM(SD0_CMD) F_(0, 0) FM(MSIOF1_SYNC_E) F_(0, 0) F_(0, 0) F_(0, 0) FM(STP_IVCXO27_0_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP7_27_24 FM(SD0_DAT0) F_(0, 0) FM(MSIOF1_RXD_E) F_(0, 0) F_(0, 0) FM(TS_SCK0_B) FM(STP_ISCLK_0_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP7_31_28 FM(SD0_DAT1) F_(0, 0) FM(MSIOF1_TXD_E) F_(0, 0) F_(0, 0) FM(TS_SPSYNC0_B)FM(STP_ISSYNC_0_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP8_3_0 FM(SD0_DAT2) F_(0, 0) FM(MSIOF1_SS1_E) F_(0, 0) F_(0, 0) FM(TS_SDAT0_B) FM(STP_ISD_0_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP8_7_4 FM(SD0_DAT3) F_(0, 0) FM(MSIOF1_SS2_E) F_(0, 0) F_(0, 0) FM(TS_SDEN0_B) FM(STP_ISEN_0_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP8_11_8 FM(SD1_CLK) F_(0, 0) FM(MSIOF1_SCK_G) F_(0, 0) F_(0, 0) FM(SIM0_CLK_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP8_15_12 FM(SD1_CMD) F_(0, 0) FM(MSIOF1_SYNC_G) FM(NFCE_N_B) F_(0, 0) FM(SIM0_D_A) FM(STP_IVCXO27_1_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP8_19_16 FM(SD1_DAT0) FM(SD2_DAT4) FM(MSIOF1_RXD_G) FM(NFWP_N_B) F_(0, 0) FM(TS_SCK1_B) FM(STP_ISCLK_1_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP8_23_20 FM(SD1_DAT1) FM(SD2_DAT5) FM(MSIOF1_TXD_G) FM(NFDATA14_B) F_(0, 0) FM(TS_SPSYNC1_B)FM(STP_ISSYNC_1_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP8_27_24 FM(SD1_DAT2) FM(SD2_DAT6) FM(MSIOF1_SS1_G) FM(NFDATA15_B) F_(0, 0) FM(TS_SDAT1_B) FM(STP_ISD_1_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP8_31_28 FM(SD1_DAT3) FM(SD2_DAT7) FM(MSIOF1_SS2_G) FM(NFRB_N_B) F_(0, 0) FM(TS_SDEN1_B) FM(STP_ISEN_1_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP9_3_0 FM(SD2_CLK) F_(0, 0) FM(NFDATA8) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP9_7_4 FM(SD2_CMD) F_(0, 0) FM(NFDATA9) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP9_11_8 FM(SD2_DAT0) F_(0, 0) FM(NFDATA10) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP9_15_12 FM(SD2_DAT1) F_(0, 0) FM(NFDATA11) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP9_19_16 FM(SD2_DAT2) F_(0, 0) FM(NFDATA12) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP9_23_20 FM(SD2_DAT3) F_(0, 0) FM(NFDATA13) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP9_27_24 FM(SD2_DS) F_(0, 0) FM(NFALE) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) FM(SATA_DEVSLP_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP9_31_28 FM(SD3_CLK) F_(0, 0) FM(NFWE_N) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP10_3_0 FM(SD3_CMD) F_(0, 0) FM(NFRE_N) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP10_7_4 FM(SD3_DAT0) F_(0, 0) FM(NFDATA0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP10_11_8 FM(SD3_DAT1) F_(0, 0) FM(NFDATA1) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP10_15_12 FM(SD3_DAT2) F_(0, 0) FM(NFDATA2) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP10_19_16 FM(SD3_DAT3) F_(0, 0) FM(NFDATA3) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP10_23_20 FM(SD3_DAT4) FM(SD2_CD_A) FM(NFDATA4) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP10_27_24 FM(SD3_DAT5) FM(SD2_WP_A) FM(NFDATA5) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP10_31_28 FM(SD3_DAT6) FM(SD3_CD) FM(NFDATA6) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP11_3_0 FM(SD3_DAT7) FM(SD3_WP) FM(NFDATA7) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP11_7_4 FM(SD3_DS) F_(0, 0) FM(NFCLE) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP11_11_8 FM(SD0_CD) F_(0, 0) FM(NFDATA14_A) F_(0, 0) FM(SCL2_B) FM(SIM0_RST_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+
+/* IPSRx */ /* 0 */ /* 1 */ /* 2 */ /* 3 */ /* 4 */ /* 5 */ /* 6 */ /* 7 */ /* 8 */ /* 9 */ /* A */ /* B */ /* C - F */
+#define IP11_15_12 FM(SD0_WP) F_(0, 0) FM(NFDATA15_A) F_(0, 0) FM(SDA2_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP11_19_16 FM(SD1_CD) F_(0, 0) FM(NFRB_N_A) F_(0, 0) F_(0, 0) FM(SIM0_CLK_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP11_23_20 FM(SD1_WP) F_(0, 0) FM(NFCE_N_A) F_(0, 0) F_(0, 0) FM(SIM0_D_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP11_27_24 FM(SCK0) FM(HSCK1_B) FM(MSIOF1_SS2_B) FM(AUDIO_CLKC_B) FM(SDA2_A) FM(SIM0_RST_B) FM(STP_OPWM_0_C) FM(RIF0_CLK_B) F_(0, 0) FM(ADICHS2) FM(SCK5_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP11_31_28 FM(RX0) FM(HRX1_B) F_(0, 0) F_(0, 0) F_(0, 0) FM(TS_SCK0_C) FM(STP_ISCLK_0_C) FM(RIF0_D0_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP12_3_0 FM(TX0) FM(HTX1_B) F_(0, 0) F_(0, 0) F_(0, 0) FM(TS_SPSYNC0_C)FM(STP_ISSYNC_0_C) FM(RIF0_D1_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP12_7_4 FM(CTS0_N) FM(HCTS1_N_B) FM(MSIOF1_SYNC_B) F_(0, 0) F_(0, 0) FM(TS_SPSYNC1_C)FM(STP_ISSYNC_1_C) FM(RIF1_SYNC_B) FM(AUDIO_CLKOUT_C) FM(ADICS_SAMP) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP12_11_8 FM(RTS0_N) FM(HRTS1_N_B) FM(MSIOF1_SS1_B) FM(AUDIO_CLKA_B) FM(SCL2_A) F_(0, 0) FM(STP_IVCXO27_1_C) FM(RIF0_SYNC_B) F_(0, 0) FM(ADICHS1) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP12_15_12 FM(RX1_A) FM(HRX1_A) F_(0, 0) F_(0, 0) F_(0, 0) FM(TS_SDAT0_C) FM(STP_ISD_0_C) FM(RIF1_CLK_C) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP12_19_16 FM(TX1_A) FM(HTX1_A) F_(0, 0) F_(0, 0) F_(0, 0) FM(TS_SDEN0_C) FM(STP_ISEN_0_C) FM(RIF1_D0_C) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP12_23_20 FM(CTS1_N) FM(HCTS1_N_A) FM(MSIOF1_RXD_B) F_(0, 0) F_(0, 0) FM(TS_SDEN1_C) FM(STP_ISEN_1_C) FM(RIF1_D0_B) F_(0, 0) FM(ADIDATA) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP12_27_24 FM(RTS1_N) FM(HRTS1_N_A) FM(MSIOF1_TXD_B) F_(0, 0) F_(0, 0) FM(TS_SDAT1_C) FM(STP_ISD_1_C) FM(RIF1_D1_B) F_(0, 0) FM(ADICHS0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP12_31_28 FM(SCK2) FM(SCIF_CLK_B) FM(MSIOF1_SCK_B) F_(0, 0) F_(0, 0) FM(TS_SCK1_C) FM(STP_ISCLK_1_C) FM(RIF1_CLK_B) F_(0, 0) FM(ADICLK) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP13_3_0 FM(TX2_A) F_(0, 0) F_(0, 0) FM(SD2_CD_B) FM(SCL1_A) F_(0, 0) FM(FMCLK_A) FM(RIF1_D1_C) F_(0, 0) FM(FSO_CFE_0_N) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP13_7_4 FM(RX2_A) F_(0, 0) F_(0, 0) FM(SD2_WP_B) FM(SDA1_A) F_(0, 0) FM(FMIN_A) FM(RIF1_SYNC_C) F_(0, 0) FM(FSO_CFE_1_N) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP13_11_8 FM(HSCK0) F_(0, 0) FM(MSIOF1_SCK_D) FM(AUDIO_CLKB_A) FM(SSI_SDATA1_B)FM(TS_SCK0_D) FM(STP_ISCLK_0_D) FM(RIF0_CLK_C) F_(0, 0) F_(0, 0) FM(RX5_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP13_15_12 FM(HRX0) F_(0, 0) FM(MSIOF1_RXD_D) F_(0, 0) FM(SSI_SDATA2_B)FM(TS_SDEN0_D) FM(STP_ISEN_0_D) FM(RIF0_D0_C) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP13_19_16 FM(HTX0) F_(0, 0) FM(MSIOF1_TXD_D) F_(0, 0) FM(SSI_SDATA9_B)FM(TS_SDAT0_D) FM(STP_ISD_0_D) FM(RIF0_D1_C) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP13_23_20 FM(HCTS0_N) FM(RX2_B) FM(MSIOF1_SYNC_D) F_(0, 0) FM(SSI_SCK9_A) FM(TS_SPSYNC0_D)FM(STP_ISSYNC_0_D) FM(RIF0_SYNC_C) FM(AUDIO_CLKOUT1_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP13_27_24 FM(HRTS0_N) FM(TX2_B) FM(MSIOF1_SS1_D) F_(0, 0) FM(SSI_WS9_A) F_(0, 0) FM(STP_IVCXO27_0_D) FM(BPFCLK_A) FM(AUDIO_CLKOUT2_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP13_31_28 FM(MSIOF0_SYNC) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) FM(AUDIO_CLKOUT_A) F_(0, 0) FM(TX5_B) F_(0, 0) F_(0, 0) FM(BPFCLK_D) F_(0, 0) F_(0, 0)
+#define IP14_3_0 FM(MSIOF0_SS1) FM(RX5_A) FM(NFWP_N_A) FM(AUDIO_CLKA_C) FM(SSI_SCK2_A) F_(0, 0) FM(STP_IVCXO27_0_C) F_(0, 0) FM(AUDIO_CLKOUT3_A) F_(0, 0) FM(TCLK1_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP14_7_4 FM(MSIOF0_SS2) FM(TX5_A) FM(MSIOF1_SS2_D) FM(AUDIO_CLKC_A) FM(SSI_WS2_A) F_(0, 0) FM(STP_OPWM_0_D) F_(0, 0) FM(AUDIO_CLKOUT_D) F_(0, 0) FM(SPEEDIN_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP14_11_8 FM(MLB_CLK) F_(0, 0) FM(MSIOF1_SCK_F) F_(0, 0) FM(SCL1_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP14_15_12 FM(MLB_SIG) FM(RX1_B) FM(MSIOF1_SYNC_F) F_(0, 0) FM(SDA1_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP14_19_16 FM(MLB_DAT) FM(TX1_B) FM(MSIOF1_RXD_F) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP14_23_20 FM(SSI_SCK01239) F_(0, 0) FM(MSIOF1_TXD_F) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP14_27_24 FM(SSI_WS01239) F_(0, 0) FM(MSIOF1_SS1_F) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+
+/* IPSRx */ /* 0 */ /* 1 */ /* 2 */ /* 3 */ /* 4 */ /* 5 */ /* 6 */ /* 7 */ /* 8 */ /* 9 */ /* A */ /* B */ /* C - F */
+#define IP14_31_28 FM(SSI_SDATA0) F_(0, 0) FM(MSIOF1_SS2_F) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP15_3_0 FM(SSI_SDATA1_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP15_7_4 FM(SSI_SDATA2_A) F_(0, 0) F_(0, 0) F_(0, 0) FM(SSI_SCK1_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP15_11_8 FM(SSI_SCK349) F_(0, 0) FM(MSIOF1_SS1_A) F_(0, 0) F_(0, 0) F_(0, 0) FM(STP_OPWM_0_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP15_15_12 FM(SSI_WS349) FM(HCTS2_N_A) FM(MSIOF1_SS2_A) F_(0, 0) F_(0, 0) F_(0, 0) FM(STP_IVCXO27_0_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP15_19_16 FM(SSI_SDATA3) FM(HRTS2_N_A) FM(MSIOF1_TXD_A) F_(0, 0) F_(0, 0) FM(TS_SCK0_A) FM(STP_ISCLK_0_A) FM(RIF0_D1_A) FM(RIF2_D0_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP15_23_20 FM(SSI_SCK4) FM(HRX2_A) FM(MSIOF1_SCK_A) F_(0, 0) F_(0, 0) FM(TS_SDAT0_A) FM(STP_ISD_0_A) FM(RIF0_CLK_A) FM(RIF2_CLK_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP15_27_24 FM(SSI_WS4) FM(HTX2_A) FM(MSIOF1_SYNC_A) F_(0, 0) F_(0, 0) FM(TS_SDEN0_A) FM(STP_ISEN_0_A) FM(RIF0_SYNC_A) FM(RIF2_SYNC_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP15_31_28 FM(SSI_SDATA4) FM(HSCK2_A) FM(MSIOF1_RXD_A) F_(0, 0) F_(0, 0) FM(TS_SPSYNC0_A)FM(STP_ISSYNC_0_A) FM(RIF0_D0_A) FM(RIF2_D1_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP16_3_0 FM(SSI_SCK6) F_(0, 0) F_(0, 0) FM(SIM0_RST_D) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP16_7_4 FM(SSI_WS6) F_(0, 0) F_(0, 0) FM(SIM0_D_D) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP16_11_8 FM(SSI_SDATA6) F_(0, 0) F_(0, 0) FM(SIM0_CLK_D) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) FM(SATA_DEVSLP_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP16_15_12 FM(SSI_SCK78) FM(HRX2_B) FM(MSIOF1_SCK_C) F_(0, 0) F_(0, 0) FM(TS_SCK1_A) FM(STP_ISCLK_1_A) FM(RIF1_CLK_A) FM(RIF3_CLK_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP16_19_16 FM(SSI_WS78) FM(HTX2_B) FM(MSIOF1_SYNC_C) F_(0, 0) F_(0, 0) FM(TS_SDAT1_A) FM(STP_ISD_1_A) FM(RIF1_SYNC_A) FM(RIF3_SYNC_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP16_23_20 FM(SSI_SDATA7) FM(HCTS2_N_B) FM(MSIOF1_RXD_C) F_(0, 0) F_(0, 0) FM(TS_SDEN1_A) FM(STP_ISEN_1_A) FM(RIF1_D0_A) FM(RIF3_D0_A) F_(0, 0) FM(TCLK2_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP16_27_24 FM(SSI_SDATA8) FM(HRTS2_N_B) FM(MSIOF1_TXD_C) F_(0, 0) F_(0, 0) FM(TS_SPSYNC1_A)FM(STP_ISSYNC_1_A) FM(RIF1_D1_A) FM(RIF3_D1_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP16_31_28 FM(SSI_SDATA9_A) FM(HSCK2_B) FM(MSIOF1_SS1_C) FM(HSCK1_A) FM(SSI_WS1_B) FM(SCK1) FM(STP_IVCXO27_1_A) FM(SCK5_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP17_3_0 FM(AUDIO_CLKA_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) FM(CC5_OSCOUT) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP17_7_4 FM(AUDIO_CLKB_B) FM(SCIF_CLK_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) FM(STP_IVCXO27_1_D) FM(REMOCON_A) F_(0, 0) F_(0, 0) FM(TCLK1_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP17_11_8 FM(USB0_PWEN) F_(0, 0) F_(0, 0) FM(SIM0_RST_C) F_(0, 0) FM(TS_SCK1_D) FM(STP_ISCLK_1_D) FM(BPFCLK_B) FM(RIF3_CLK_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) FM(HSCK2_C) F_(0, 0) F_(0, 0)
+#define IP17_15_12 FM(USB0_OVC) F_(0, 0) F_(0, 0) FM(SIM0_D_C) F_(0, 0) FM(TS_SDAT1_D) FM(STP_ISD_1_D) F_(0, 0) FM(RIF3_SYNC_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) FM(HRX2_C) F_(0, 0) F_(0, 0)
+#define IP17_19_16 FM(USB1_PWEN) F_(0, 0) F_(0, 0) FM(SIM0_CLK_C) FM(SSI_SCK1_A) FM(TS_SCK0_E) FM(STP_ISCLK_0_E) FM(FMCLK_B) FM(RIF2_CLK_B) F_(0, 0) FM(SPEEDIN_A) F_(0, 0) F_(0, 0) FM(HTX2_C) F_(0, 0) F_(0, 0)
+#define IP17_23_20 FM(USB1_OVC) F_(0, 0) FM(MSIOF1_SS2_C) F_(0, 0) FM(SSI_WS1_A) FM(TS_SDAT0_E) FM(STP_ISD_0_E) FM(FMIN_B) FM(RIF2_SYNC_B) F_(0, 0) FM(REMOCON_B) F_(0, 0) F_(0, 0) FM(HCTS2_N_C) F_(0, 0) F_(0, 0)
+#define IP17_27_24 FM(USB30_PWEN) F_(0, 0) F_(0, 0) FM(AUDIO_CLKOUT_B) FM(SSI_SCK2_B) FM(TS_SDEN1_D) FM(STP_ISEN_1_D) FM(STP_OPWM_0_E)FM(RIF3_D0_B) F_(0, 0) FM(TCLK2_B) FM(TPU0TO0) FM(BPFCLK_C) FM(HRTS2_N_C) F_(0, 0) F_(0, 0)
+#define IP17_31_28 FM(USB30_OVC) F_(0, 0) F_(0, 0) FM(AUDIO_CLKOUT1_B) FM(SSI_WS2_B) FM(TS_SPSYNC1_D)FM(STP_ISSYNC_1_D) FM(STP_IVCXO27_0_E)FM(RIF3_D1_B) F_(0, 0) FM(FSO_TOE_N) FM(TPU0TO1) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP18_3_0 FM(GP6_30) F_(0, 0) F_(0, 0) FM(AUDIO_CLKOUT2_B) FM(SSI_SCK9_B) FM(TS_SDEN0_E) FM(STP_ISEN_0_E) F_(0, 0) FM(RIF2_D0_B) F_(0, 0) F_(0, 0) FM(TPU0TO2) FM(FMCLK_C) FM(FMCLK_D) F_(0, 0) F_(0, 0)
+#define IP18_7_4 FM(GP6_31) F_(0, 0) F_(0, 0) FM(AUDIO_CLKOUT3_B) FM(SSI_WS9_B) FM(TS_SPSYNC0_E)FM(STP_ISSYNC_0_E) F_(0, 0) FM(RIF2_D1_B) F_(0, 0) F_(0, 0) FM(TPU0TO3) FM(FMIN_C) FM(FMIN_D) F_(0, 0) F_(0, 0)
+
+#define PINMUX_GPSR \
+\
+ GPSR6_31 \
+ GPSR6_30 \
+ GPSR6_29 \
+ GPSR1_28 GPSR6_28 \
+ GPSR1_27 GPSR6_27 \
+ GPSR1_26 GPSR6_26 \
+ GPSR1_25 GPSR5_25 GPSR6_25 \
+ GPSR1_24 GPSR5_24 GPSR6_24 \
+ GPSR1_23 GPSR5_23 GPSR6_23 \
+ GPSR1_22 GPSR5_22 GPSR6_22 \
+ GPSR1_21 GPSR5_21 GPSR6_21 \
+ GPSR1_20 GPSR5_20 GPSR6_20 \
+ GPSR1_19 GPSR5_19 GPSR6_19 \
+ GPSR1_18 GPSR5_18 GPSR6_18 \
+ GPSR1_17 GPSR4_17 GPSR5_17 GPSR6_17 \
+ GPSR1_16 GPSR4_16 GPSR5_16 GPSR6_16 \
+GPSR0_15 GPSR1_15 GPSR3_15 GPSR4_15 GPSR5_15 GPSR6_15 \
+GPSR0_14 GPSR1_14 GPSR2_14 GPSR3_14 GPSR4_14 GPSR5_14 GPSR6_14 \
+GPSR0_13 GPSR1_13 GPSR2_13 GPSR3_13 GPSR4_13 GPSR5_13 GPSR6_13 \
+GPSR0_12 GPSR1_12 GPSR2_12 GPSR3_12 GPSR4_12 GPSR5_12 GPSR6_12 \
+GPSR0_11 GPSR1_11 GPSR2_11 GPSR3_11 GPSR4_11 GPSR5_11 GPSR6_11 \
+GPSR0_10 GPSR1_10 GPSR2_10 GPSR3_10 GPSR4_10 GPSR5_10 GPSR6_10 \
+GPSR0_9 GPSR1_9 GPSR2_9 GPSR3_9 GPSR4_9 GPSR5_9 GPSR6_9 \
+GPSR0_8 GPSR1_8 GPSR2_8 GPSR3_8 GPSR4_8 GPSR5_8 GPSR6_8 \
+GPSR0_7 GPSR1_7 GPSR2_7 GPSR3_7 GPSR4_7 GPSR5_7 GPSR6_7 \
+GPSR0_6 GPSR1_6 GPSR2_6 GPSR3_6 GPSR4_6 GPSR5_6 GPSR6_6 \
+GPSR0_5 GPSR1_5 GPSR2_5 GPSR3_5 GPSR4_5 GPSR5_5 GPSR6_5 \
+GPSR0_4 GPSR1_4 GPSR2_4 GPSR3_4 GPSR4_4 GPSR5_4 GPSR6_4 \
+GPSR0_3 GPSR1_3 GPSR2_3 GPSR3_3 GPSR4_3 GPSR5_3 GPSR6_3 GPSR7_3 \
+GPSR0_2 GPSR1_2 GPSR2_2 GPSR3_2 GPSR4_2 GPSR5_2 GPSR6_2 GPSR7_2 \
+GPSR0_1 GPSR1_1 GPSR2_1 GPSR3_1 GPSR4_1 GPSR5_1 GPSR6_1 GPSR7_1 \
+GPSR0_0 GPSR1_0 GPSR2_0 GPSR3_0 GPSR4_0 GPSR5_0 GPSR6_0 GPSR7_0
+
+#define PINMUX_IPSR \
+\
+FM(IP0_3_0) IP0_3_0 FM(IP1_3_0) IP1_3_0 FM(IP2_3_0) IP2_3_0 FM(IP3_3_0) IP3_3_0 \
+FM(IP0_7_4) IP0_7_4 FM(IP1_7_4) IP1_7_4 FM(IP2_7_4) IP2_7_4 FM(IP3_7_4) IP3_7_4 \
+FM(IP0_11_8) IP0_11_8 FM(IP1_11_8) IP1_11_8 FM(IP2_11_8) IP2_11_8 FM(IP3_11_8) IP3_11_8 \
+FM(IP0_15_12) IP0_15_12 FM(IP1_15_12) IP1_15_12 FM(IP2_15_12) IP2_15_12 FM(IP3_15_12) IP3_15_12 \
+FM(IP0_19_16) IP0_19_16 FM(IP1_19_16) IP1_19_16 FM(IP2_19_16) IP2_19_16 FM(IP3_19_16) IP3_19_16 \
+FM(IP0_23_20) IP0_23_20 FM(IP1_23_20) IP1_23_20 FM(IP2_23_20) IP2_23_20 FM(IP3_23_20) IP3_23_20 \
+FM(IP0_27_24) IP0_27_24 FM(IP1_27_24) IP1_27_24 FM(IP2_27_24) IP2_27_24 FM(IP3_27_24) IP3_27_24 \
+FM(IP0_31_28) IP0_31_28 FM(IP1_31_28) IP1_31_28 FM(IP2_31_28) IP2_31_28 FM(IP3_31_28) IP3_31_28 \
+\
+FM(IP4_3_0) IP4_3_0 FM(IP5_3_0) IP5_3_0 FM(IP6_3_0) IP6_3_0 FM(IP7_3_0) IP7_3_0 \
+FM(IP4_7_4) IP4_7_4 FM(IP5_7_4) IP5_7_4 FM(IP6_7_4) IP6_7_4 FM(IP7_7_4) IP7_7_4 \
+FM(IP4_11_8) IP4_11_8 FM(IP5_11_8) IP5_11_8 FM(IP6_11_8) IP6_11_8 FM(IP7_11_8) IP7_11_8 \
+FM(IP4_15_12) IP4_15_12 FM(IP5_15_12) IP5_15_12 FM(IP6_15_12) IP6_15_12 \
+FM(IP4_19_16) IP4_19_16 FM(IP5_19_16) IP5_19_16 FM(IP6_19_16) IP6_19_16 FM(IP7_19_16) IP7_19_16 \
+FM(IP4_23_20) IP4_23_20 FM(IP5_23_20) IP5_23_20 FM(IP6_23_20) IP6_23_20 FM(IP7_23_20) IP7_23_20 \
+FM(IP4_27_24) IP4_27_24 FM(IP5_27_24) IP5_27_24 FM(IP6_27_24) IP6_27_24 FM(IP7_27_24) IP7_27_24 \
+FM(IP4_31_28) IP4_31_28 FM(IP5_31_28) IP5_31_28 FM(IP6_31_28) IP6_31_28 FM(IP7_31_28) IP7_31_28 \
+\
+FM(IP8_3_0) IP8_3_0 FM(IP9_3_0) IP9_3_0 FM(IP10_3_0) IP10_3_0 FM(IP11_3_0) IP11_3_0 \
+FM(IP8_7_4) IP8_7_4 FM(IP9_7_4) IP9_7_4 FM(IP10_7_4) IP10_7_4 FM(IP11_7_4) IP11_7_4 \
+FM(IP8_11_8) IP8_11_8 FM(IP9_11_8) IP9_11_8 FM(IP10_11_8) IP10_11_8 FM(IP11_11_8) IP11_11_8 \
+FM(IP8_15_12) IP8_15_12 FM(IP9_15_12) IP9_15_12 FM(IP10_15_12) IP10_15_12 FM(IP11_15_12) IP11_15_12 \
+FM(IP8_19_16) IP8_19_16 FM(IP9_19_16) IP9_19_16 FM(IP10_19_16) IP10_19_16 FM(IP11_19_16) IP11_19_16 \
+FM(IP8_23_20) IP8_23_20 FM(IP9_23_20) IP9_23_20 FM(IP10_23_20) IP10_23_20 FM(IP11_23_20) IP11_23_20 \
+FM(IP8_27_24) IP8_27_24 FM(IP9_27_24) IP9_27_24 FM(IP10_27_24) IP10_27_24 FM(IP11_27_24) IP11_27_24 \
+FM(IP8_31_28) IP8_31_28 FM(IP9_31_28) IP9_31_28 FM(IP10_31_28) IP10_31_28 FM(IP11_31_28) IP11_31_28 \
+\
+FM(IP12_3_0) IP12_3_0 FM(IP13_3_0) IP13_3_0 FM(IP14_3_0) IP14_3_0 FM(IP15_3_0) IP15_3_0 \
+FM(IP12_7_4) IP12_7_4 FM(IP13_7_4) IP13_7_4 FM(IP14_7_4) IP14_7_4 FM(IP15_7_4) IP15_7_4 \
+FM(IP12_11_8) IP12_11_8 FM(IP13_11_8) IP13_11_8 FM(IP14_11_8) IP14_11_8 FM(IP15_11_8) IP15_11_8 \
+FM(IP12_15_12) IP12_15_12 FM(IP13_15_12) IP13_15_12 FM(IP14_15_12) IP14_15_12 FM(IP15_15_12) IP15_15_12 \
+FM(IP12_19_16) IP12_19_16 FM(IP13_19_16) IP13_19_16 FM(IP14_19_16) IP14_19_16 FM(IP15_19_16) IP15_19_16 \
+FM(IP12_23_20) IP12_23_20 FM(IP13_23_20) IP13_23_20 FM(IP14_23_20) IP14_23_20 FM(IP15_23_20) IP15_23_20 \
+FM(IP12_27_24) IP12_27_24 FM(IP13_27_24) IP13_27_24 FM(IP14_27_24) IP14_27_24 FM(IP15_27_24) IP15_27_24 \
+FM(IP12_31_28) IP12_31_28 FM(IP13_31_28) IP13_31_28 FM(IP14_31_28) IP14_31_28 FM(IP15_31_28) IP15_31_28 \
+\
+FM(IP16_3_0) IP16_3_0 FM(IP17_3_0) IP17_3_0 FM(IP18_3_0) IP18_3_0 \
+FM(IP16_7_4) IP16_7_4 FM(IP17_7_4) IP17_7_4 FM(IP18_7_4) IP18_7_4 \
+FM(IP16_11_8) IP16_11_8 FM(IP17_11_8) IP17_11_8 \
+FM(IP16_15_12) IP16_15_12 FM(IP17_15_12) IP17_15_12 \
+FM(IP16_19_16) IP16_19_16 FM(IP17_19_16) IP17_19_16 \
+FM(IP16_23_20) IP16_23_20 FM(IP17_23_20) IP17_23_20 \
+FM(IP16_27_24) IP16_27_24 FM(IP17_27_24) IP17_27_24 \
+FM(IP16_31_28) IP16_31_28 FM(IP17_31_28) IP17_31_28
+
+/* MOD_SEL0 */ /* 0 */ /* 1 */ /* 2 */ /* 3 */ /* 4 */ /* 5 */ /* 6 */ /* 7 */
+#define MOD_SEL0_31_30_29 FM(SEL_MSIOF3_0) FM(SEL_MSIOF3_1) FM(SEL_MSIOF3_2) FM(SEL_MSIOF3_3) FM(SEL_MSIOF3_4) F_(0, 0) F_(0, 0) F_(0, 0)
+#define MOD_SEL0_28_27 FM(SEL_MSIOF2_0) FM(SEL_MSIOF2_1) FM(SEL_MSIOF2_2) FM(SEL_MSIOF2_3)
+#define MOD_SEL0_26_25_24 FM(SEL_MSIOF1_0) FM(SEL_MSIOF1_1) FM(SEL_MSIOF1_2) FM(SEL_MSIOF1_3) FM(SEL_MSIOF1_4) FM(SEL_MSIOF1_5) FM(SEL_MSIOF1_6) F_(0, 0)
+#define MOD_SEL0_23 FM(SEL_LBSC_0) FM(SEL_LBSC_1)
+#define MOD_SEL0_22 FM(SEL_IEBUS_0) FM(SEL_IEBUS_1)
+#define MOD_SEL0_21 FM(SEL_I2C2_0) FM(SEL_I2C2_1)
+#define MOD_SEL0_20 FM(SEL_I2C1_0) FM(SEL_I2C1_1)
+#define MOD_SEL0_19 FM(SEL_HSCIF4_0) FM(SEL_HSCIF4_1)
+#define MOD_SEL0_18_17 FM(SEL_HSCIF3_0) FM(SEL_HSCIF3_1) FM(SEL_HSCIF3_2) FM(SEL_HSCIF3_3)
+#define MOD_SEL0_16 FM(SEL_HSCIF1_0) FM(SEL_HSCIF1_1)
+#define MOD_SEL0_14_13 FM(SEL_HSCIF2_0) FM(SEL_HSCIF2_1) FM(SEL_HSCIF2_2) F_(0, 0)
+#define MOD_SEL0_12 FM(SEL_ETHERAVB_0) FM(SEL_ETHERAVB_1)
+#define MOD_SEL0_11 FM(SEL_DRIF3_0) FM(SEL_DRIF3_1)
+#define MOD_SEL0_10 FM(SEL_DRIF2_0) FM(SEL_DRIF2_1)
+#define MOD_SEL0_9_8 FM(SEL_DRIF1_0) FM(SEL_DRIF1_1) FM(SEL_DRIF1_2) F_(0, 0)
+#define MOD_SEL0_7_6 FM(SEL_DRIF0_0) FM(SEL_DRIF0_1) FM(SEL_DRIF0_2) F_(0, 0)
+#define MOD_SEL0_5 FM(SEL_CANFD0_0) FM(SEL_CANFD0_1)
+#define MOD_SEL0_4_3 FM(SEL_ADG_A_0) FM(SEL_ADG_A_1) FM(SEL_ADG_A_2) FM(SEL_ADG_A_3)
+
+/* MOD_SEL1 */ /* 0 */ /* 1 */ /* 2 */ /* 3 */ /* 4 */ /* 5 */ /* 6 */ /* 7 */
+#define MOD_SEL1_31_30 FM(SEL_TSIF1_0) FM(SEL_TSIF1_1) FM(SEL_TSIF1_2) FM(SEL_TSIF1_3)
+#define MOD_SEL1_29_28_27 FM(SEL_TSIF0_0) FM(SEL_TSIF0_1) FM(SEL_TSIF0_2) FM(SEL_TSIF0_3) FM(SEL_TSIF0_4) F_(0, 0) F_(0, 0) F_(0, 0)
+#define MOD_SEL1_26 FM(SEL_TIMER_TMU_0) FM(SEL_TIMER_TMU_1)
+#define MOD_SEL1_25_24 FM(SEL_SSP1_1_0) FM(SEL_SSP1_1_1) FM(SEL_SSP1_1_2) FM(SEL_SSP1_1_3)
+#define MOD_SEL1_23_22_21 FM(SEL_SSP1_0_0) FM(SEL_SSP1_0_1) FM(SEL_SSP1_0_2) FM(SEL_SSP1_0_3) FM(SEL_SSP1_0_4) F_(0, 0) F_(0, 0) F_(0, 0)
+#define MOD_SEL1_20 FM(SEL_SSI1_0) FM(SEL_SSI1_1)
+#define MOD_SEL1_19 FM(SEL_SPEED_PULSE_0) FM(SEL_SPEED_PULSE_1)
+#define MOD_SEL1_18_17 FM(SEL_SIMCARD_0) FM(SEL_SIMCARD_1) FM(SEL_SIMCARD_2) FM(SEL_SIMCARD_3)
+#define MOD_SEL1_16 FM(SEL_SDHI2_0) FM(SEL_SDHI2_1)
+#define MOD_SEL1_15_14 FM(SEL_SCIF4_0) FM(SEL_SCIF4_1) FM(SEL_SCIF4_2) F_(0, 0)
+#define MOD_SEL1_13 FM(SEL_SCIF3_0) FM(SEL_SCIF3_1)
+#define MOD_SEL1_12 FM(SEL_SCIF2_0) FM(SEL_SCIF2_1)
+#define MOD_SEL1_11 FM(SEL_SCIF1_0) FM(SEL_SCIF1_1)
+#define MOD_SEL1_10 FM(SEL_SCIF_0) FM(SEL_SCIF_1)
+#define MOD_SEL1_9 FM(SEL_REMOCON_0) FM(SEL_REMOCON_1)
+#define MOD_SEL1_6 FM(SEL_RCAN0_0) FM(SEL_RCAN0_1)
+#define MOD_SEL1_5 FM(SEL_PWM6_0) FM(SEL_PWM6_1)
+#define MOD_SEL1_4 FM(SEL_PWM5_0) FM(SEL_PWM5_1)
+#define MOD_SEL1_3 FM(SEL_PWM4_0) FM(SEL_PWM4_1)
+#define MOD_SEL1_2 FM(SEL_PWM3_0) FM(SEL_PWM3_1)
+#define MOD_SEL1_1 FM(SEL_PWM2_0) FM(SEL_PWM2_1)
+#define MOD_SEL1_0 FM(SEL_PWM1_0) FM(SEL_PWM1_1)
+
+/* MOD_SEL2 */ /* 0 */ /* 1 */ /* 2 */ /* 3 */ /* 4 */ /* 5 */ /* 6 */ /* 7 */
+#define MOD_SEL2_31 FM(I2C_SEL_5_0) FM(I2C_SEL_5_1)
+#define MOD_SEL2_30 FM(I2C_SEL_3_0) FM(I2C_SEL_3_1)
+#define MOD_SEL2_29 FM(I2C_SEL_0_0) FM(I2C_SEL_0_1)
+#define MOD_SEL2_28_27 FM(SEL_FM_0) FM(SEL_FM_1) FM(SEL_FM_2) FM(SEL_FM_3)
+#define MOD_SEL2_26 FM(SEL_SCIF5_0) FM(SEL_SCIF5_1)
+#define MOD_SEL2_25_24_23 FM(SEL_I2C6_0) FM(SEL_I2C6_1) FM(SEL_I2C6_2) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define MOD_SEL2_22 FM(SEL_NDFC_0) FM(SEL_NDFC_1)
+#define MOD_SEL2_21 FM(SEL_SSI2_0) FM(SEL_SSI2_1)
+#define MOD_SEL2_20 FM(SEL_SSI9_0) FM(SEL_SSI9_1)
+#define MOD_SEL2_19 FM(SEL_TIMER_TMU2_0) FM(SEL_TIMER_TMU2_1)
+#define MOD_SEL2_18 FM(SEL_ADG_B_0) FM(SEL_ADG_B_1)
+#define MOD_SEL2_17 FM(SEL_ADG_C_0) FM(SEL_ADG_C_1)
+#define MOD_SEL2_0 FM(SEL_VIN4_0) FM(SEL_VIN4_1)
+
+#define PINMUX_MOD_SELS \
+\
+MOD_SEL0_31_30_29 MOD_SEL1_31_30 MOD_SEL2_31 \
+ MOD_SEL2_30 \
+ MOD_SEL1_29_28_27 MOD_SEL2_29 \
+MOD_SEL0_28_27 MOD_SEL2_28_27 \
+MOD_SEL0_26_25_24 MOD_SEL1_26 MOD_SEL2_26 \
+ MOD_SEL1_25_24 MOD_SEL2_25_24_23 \
+MOD_SEL0_23 MOD_SEL1_23_22_21 \
+MOD_SEL0_22 MOD_SEL2_22 \
+MOD_SEL0_21 MOD_SEL2_21 \
+MOD_SEL0_20 MOD_SEL1_20 MOD_SEL2_20 \
+MOD_SEL0_19 MOD_SEL1_19 MOD_SEL2_19 \
+MOD_SEL0_18_17 MOD_SEL1_18_17 MOD_SEL2_18 \
+ MOD_SEL2_17 \
+MOD_SEL0_16 MOD_SEL1_16 \
+ MOD_SEL1_15_14 \
+MOD_SEL0_14_13 \
+ MOD_SEL1_13 \
+MOD_SEL0_12 MOD_SEL1_12 \
+MOD_SEL0_11 MOD_SEL1_11 \
+MOD_SEL0_10 MOD_SEL1_10 \
+MOD_SEL0_9_8 MOD_SEL1_9 \
+MOD_SEL0_7_6 \
+ MOD_SEL1_6 \
+MOD_SEL0_5 MOD_SEL1_5 \
+MOD_SEL0_4_3 MOD_SEL1_4 \
+ MOD_SEL1_3 \
+ MOD_SEL1_2 \
+ MOD_SEL1_1 \
+ MOD_SEL1_0 MOD_SEL2_0
+
+/*
+ * These pins are not able to be muxed but have other properties
+ * that can be set, such as drive-strength or pull-up/pull-down enable.
+ */
+#define PINMUX_STATIC \
+ FM(QSPI0_SPCLK) FM(QSPI0_SSL) FM(QSPI0_MOSI_IO0) FM(QSPI0_MISO_IO1) \
+ FM(QSPI0_IO2) FM(QSPI0_IO3) \
+ FM(QSPI1_SPCLK) FM(QSPI1_SSL) FM(QSPI1_MOSI_IO0) FM(QSPI1_MISO_IO1) \
+ FM(QSPI1_IO2) FM(QSPI1_IO3) \
+ FM(RPC_INT) FM(RPC_WP) FM(RPC_RESET) \
+ FM(AVB_TX_CTL) FM(AVB_TXC) FM(AVB_TD0) FM(AVB_TD1) FM(AVB_TD2) FM(AVB_TD3) \
+ FM(AVB_RX_CTL) FM(AVB_RXC) FM(AVB_RD0) FM(AVB_RD1) FM(AVB_RD2) FM(AVB_RD3) \
+ FM(AVB_TXCREFCLK) FM(AVB_MDIO) \
+ FM(PRESETOUT) \
+ FM(DU_DOTCLKIN0) FM(DU_DOTCLKIN1) FM(DU_DOTCLKIN2) \
+ FM(TMS) FM(TDO) FM(ASEBRK) FM(MLB_REF) FM(TDI) FM(TCK) FM(TRST) FM(EXTALR)
+
+enum {
+ PINMUX_RESERVED = 0,
+
+ PINMUX_DATA_BEGIN,
+ GP_ALL(DATA),
+ PINMUX_DATA_END,
+
+#define F_(x, y)
+#define FM(x) FN_##x,
+ PINMUX_FUNCTION_BEGIN,
+ GP_ALL(FN),
+ PINMUX_GPSR
+ PINMUX_IPSR
+ PINMUX_MOD_SELS
+ PINMUX_FUNCTION_END,
+#undef F_
+#undef FM
+
+#define F_(x, y)
+#define FM(x) x##_MARK,
+ PINMUX_MARK_BEGIN,
+ PINMUX_GPSR
+ PINMUX_IPSR
+ PINMUX_MOD_SELS
+ PINMUX_STATIC
+ PINMUX_MARK_END,
+#undef F_
+#undef FM
+};
+
+static const u16 pinmux_data[] = {
+ PINMUX_DATA_GP_ALL(),
+
+ PINMUX_SINGLE(AVS1),
+ PINMUX_SINGLE(AVS2),
+ PINMUX_SINGLE(CLKOUT),
+ PINMUX_SINGLE(GP7_03),
+ PINMUX_SINGLE(HDMI0_CEC),
+ PINMUX_SINGLE(MSIOF0_RXD),
+ PINMUX_SINGLE(MSIOF0_SCK),
+ PINMUX_SINGLE(MSIOF0_TXD),
+ PINMUX_SINGLE(SSI_SCK5),
+ PINMUX_SINGLE(SSI_SDATA5),
+ PINMUX_SINGLE(SSI_WS5),
+
+ /* IPSR0 */
+ PINMUX_IPSR_GPSR(IP0_3_0, AVB_MDC),
+ PINMUX_IPSR_MSEL(IP0_3_0, MSIOF2_SS2_C, SEL_MSIOF2_2),
+
+ PINMUX_IPSR_GPSR(IP0_7_4, AVB_MAGIC),
+ PINMUX_IPSR_MSEL(IP0_7_4, MSIOF2_SS1_C, SEL_MSIOF2_2),
+ PINMUX_IPSR_MSEL(IP0_7_4, SCK4_A, SEL_SCIF4_0),
+
+ PINMUX_IPSR_GPSR(IP0_11_8, AVB_PHY_INT),
+ PINMUX_IPSR_MSEL(IP0_11_8, MSIOF2_SYNC_C, SEL_MSIOF2_2),
+ PINMUX_IPSR_MSEL(IP0_11_8, RX4_A, SEL_SCIF4_0),
+
+ PINMUX_IPSR_GPSR(IP0_15_12, AVB_LINK),
+ PINMUX_IPSR_MSEL(IP0_15_12, MSIOF2_SCK_C, SEL_MSIOF2_2),
+ PINMUX_IPSR_MSEL(IP0_15_12, TX4_A, SEL_SCIF4_0),
+ PINMUX_IPSR_GPSR(IP0_19_16, FSCLKST2_N_A),
+
+ PINMUX_IPSR_MSEL(IP0_19_16, AVB_AVTP_MATCH_A, SEL_ETHERAVB_0),
+ PINMUX_IPSR_MSEL(IP0_19_16, MSIOF2_RXD_C, SEL_MSIOF2_2),
+ PINMUX_IPSR_MSEL(IP0_19_16, CTS4_N_A, SEL_SCIF4_0),
+
+ PINMUX_IPSR_MSEL(IP0_23_20, AVB_AVTP_CAPTURE_A, SEL_ETHERAVB_0),
+ PINMUX_IPSR_MSEL(IP0_23_20, MSIOF2_TXD_C, SEL_MSIOF2_2),
+ PINMUX_IPSR_MSEL(IP0_23_20, RTS4_N_A, SEL_SCIF4_0),
+
+ PINMUX_IPSR_GPSR(IP0_27_24, IRQ0),
+ PINMUX_IPSR_GPSR(IP0_27_24, QPOLB),
+ PINMUX_IPSR_GPSR(IP0_27_24, DU_CDE),
+ PINMUX_IPSR_MSEL(IP0_27_24, VI4_DATA0_B, SEL_VIN4_1),
+ PINMUX_IPSR_MSEL(IP0_27_24, CAN0_TX_B, SEL_RCAN0_1),
+ PINMUX_IPSR_MSEL(IP0_27_24, CANFD0_TX_B, SEL_CANFD0_1),
+ PINMUX_IPSR_MSEL(IP0_27_24, MSIOF3_SS2_E, SEL_MSIOF3_4),
+
+ PINMUX_IPSR_GPSR(IP0_31_28, IRQ1),
+ PINMUX_IPSR_GPSR(IP0_31_28, QPOLA),
+ PINMUX_IPSR_GPSR(IP0_31_28, DU_DISP),
+ PINMUX_IPSR_MSEL(IP0_31_28, VI4_DATA1_B, SEL_VIN4_1),
+ PINMUX_IPSR_MSEL(IP0_31_28, CAN0_RX_B, SEL_RCAN0_1),
+ PINMUX_IPSR_MSEL(IP0_31_28, CANFD0_RX_B, SEL_CANFD0_1),
+ PINMUX_IPSR_MSEL(IP0_31_28, MSIOF3_SS1_E, SEL_MSIOF3_4),
+
+ /* IPSR1 */
+ PINMUX_IPSR_GPSR(IP1_3_0, IRQ2),
+ PINMUX_IPSR_GPSR(IP1_3_0, QCPV_QDE),
+ PINMUX_IPSR_GPSR(IP1_3_0, DU_EXODDF_DU_ODDF_DISP_CDE),
+ PINMUX_IPSR_MSEL(IP1_3_0, VI4_DATA2_B, SEL_VIN4_1),
+ PINMUX_IPSR_MSEL(IP1_3_0, PWM3_B, SEL_PWM3_1),
+ PINMUX_IPSR_MSEL(IP1_3_0, MSIOF3_SYNC_E, SEL_MSIOF3_4),
+
+ PINMUX_IPSR_GPSR(IP1_7_4, IRQ3),
+ PINMUX_IPSR_GPSR(IP1_7_4, QSTVB_QVE),
+ PINMUX_IPSR_GPSR(IP1_7_4, DU_DOTCLKOUT1),
+ PINMUX_IPSR_MSEL(IP1_7_4, VI4_DATA3_B, SEL_VIN4_1),
+ PINMUX_IPSR_MSEL(IP1_7_4, PWM4_B, SEL_PWM4_1),
+ PINMUX_IPSR_MSEL(IP1_7_4, MSIOF3_SCK_E, SEL_MSIOF3_4),
+
+ PINMUX_IPSR_GPSR(IP1_11_8, IRQ4),
+ PINMUX_IPSR_GPSR(IP1_11_8, QSTH_QHS),
+ PINMUX_IPSR_GPSR(IP1_11_8, DU_EXHSYNC_DU_HSYNC),
+ PINMUX_IPSR_MSEL(IP1_11_8, VI4_DATA4_B, SEL_VIN4_1),
+ PINMUX_IPSR_MSEL(IP1_11_8, PWM5_B, SEL_PWM5_1),
+ PINMUX_IPSR_MSEL(IP1_11_8, MSIOF3_RXD_E, SEL_MSIOF3_4),
+
+ PINMUX_IPSR_GPSR(IP1_15_12, IRQ5),
+ PINMUX_IPSR_GPSR(IP1_15_12, QSTB_QHE),
+ PINMUX_IPSR_GPSR(IP1_15_12, DU_EXVSYNC_DU_VSYNC),
+ PINMUX_IPSR_MSEL(IP1_15_12, VI4_DATA5_B, SEL_VIN4_1),
+ PINMUX_IPSR_MSEL(IP1_15_12, PWM6_B, SEL_PWM6_1),
+ PINMUX_IPSR_GPSR(IP1_15_12, FSCLKST2_N_B),
+ PINMUX_IPSR_MSEL(IP1_15_12, MSIOF3_TXD_E, SEL_MSIOF3_4),
+
+ PINMUX_IPSR_GPSR(IP1_19_16, PWM0),
+ PINMUX_IPSR_GPSR(IP1_19_16, AVB_AVTP_PPS),
+ PINMUX_IPSR_MSEL(IP1_19_16, VI4_DATA6_B, SEL_VIN4_1),
+ PINMUX_IPSR_MSEL(IP1_19_16, IECLK_B, SEL_IEBUS_1),
+
+ PINMUX_IPSR_MSEL(IP1_23_20, PWM1_A, SEL_PWM1_0),
+ PINMUX_IPSR_MSEL(IP1_23_20, HRX3_D, SEL_HSCIF3_3),
+ PINMUX_IPSR_MSEL(IP1_23_20, VI4_DATA7_B, SEL_VIN4_1),
+ PINMUX_IPSR_MSEL(IP1_23_20, IERX_B, SEL_IEBUS_1),
+
+ PINMUX_IPSR_MSEL(IP1_27_24, PWM2_A, SEL_PWM2_0),
+ PINMUX_IPSR_MSEL(IP1_27_24, HTX3_D, SEL_HSCIF3_3),
+ PINMUX_IPSR_MSEL(IP1_27_24, IETX_B, SEL_IEBUS_1),
+
+ PINMUX_IPSR_GPSR(IP1_31_28, A0),
+ PINMUX_IPSR_GPSR(IP1_31_28, LCDOUT16),
+ PINMUX_IPSR_MSEL(IP1_31_28, MSIOF3_SYNC_B, SEL_MSIOF3_1),
+ PINMUX_IPSR_GPSR(IP1_31_28, VI4_DATA8),
+ PINMUX_IPSR_GPSR(IP1_31_28, DU_DB0),
+ PINMUX_IPSR_MSEL(IP1_31_28, PWM3_A, SEL_PWM3_0),
+
+ /* IPSR2 */
+ PINMUX_IPSR_GPSR(IP2_3_0, A1),
+ PINMUX_IPSR_GPSR(IP2_3_0, LCDOUT17),
+ PINMUX_IPSR_MSEL(IP2_3_0, MSIOF3_TXD_B, SEL_MSIOF3_1),
+ PINMUX_IPSR_GPSR(IP2_3_0, VI4_DATA9),
+ PINMUX_IPSR_GPSR(IP2_3_0, DU_DB1),
+ PINMUX_IPSR_MSEL(IP2_3_0, PWM4_A, SEL_PWM4_0),
+
+ PINMUX_IPSR_GPSR(IP2_7_4, A2),
+ PINMUX_IPSR_GPSR(IP2_7_4, LCDOUT18),
+ PINMUX_IPSR_MSEL(IP2_7_4, MSIOF3_SCK_B, SEL_MSIOF3_1),
+ PINMUX_IPSR_GPSR(IP2_7_4, VI4_DATA10),
+ PINMUX_IPSR_GPSR(IP2_7_4, DU_DB2),
+ PINMUX_IPSR_MSEL(IP2_7_4, PWM5_A, SEL_PWM5_0),
+
+ PINMUX_IPSR_GPSR(IP2_11_8, A3),
+ PINMUX_IPSR_GPSR(IP2_11_8, LCDOUT19),
+ PINMUX_IPSR_MSEL(IP2_11_8, MSIOF3_RXD_B, SEL_MSIOF3_1),
+ PINMUX_IPSR_GPSR(IP2_11_8, VI4_DATA11),
+ PINMUX_IPSR_GPSR(IP2_11_8, DU_DB3),
+ PINMUX_IPSR_MSEL(IP2_11_8, PWM6_A, SEL_PWM6_0),
+
+ PINMUX_IPSR_GPSR(IP2_15_12, A4),
+ PINMUX_IPSR_GPSR(IP2_15_12, LCDOUT20),
+ PINMUX_IPSR_MSEL(IP2_15_12, MSIOF3_SS1_B, SEL_MSIOF3_1),
+ PINMUX_IPSR_GPSR(IP2_15_12, VI4_DATA12),
+ PINMUX_IPSR_GPSR(IP2_15_12, VI5_DATA12),
+ PINMUX_IPSR_GPSR(IP2_15_12, DU_DB4),
+
+ PINMUX_IPSR_GPSR(IP2_19_16, A5),
+ PINMUX_IPSR_GPSR(IP2_19_16, LCDOUT21),
+ PINMUX_IPSR_MSEL(IP2_19_16, MSIOF3_SS2_B, SEL_MSIOF3_1),
+ PINMUX_IPSR_MSEL(IP2_19_16, SCK4_B, SEL_SCIF4_1),
+ PINMUX_IPSR_GPSR(IP2_19_16, VI4_DATA13),
+ PINMUX_IPSR_GPSR(IP2_19_16, VI5_DATA13),
+ PINMUX_IPSR_GPSR(IP2_19_16, DU_DB5),
+
+ PINMUX_IPSR_GPSR(IP2_23_20, A6),
+ PINMUX_IPSR_GPSR(IP2_23_20, LCDOUT22),
+ PINMUX_IPSR_MSEL(IP2_23_20, MSIOF2_SS1_A, SEL_MSIOF2_0),
+ PINMUX_IPSR_MSEL(IP2_23_20, RX4_B, SEL_SCIF4_1),
+ PINMUX_IPSR_GPSR(IP2_23_20, VI4_DATA14),
+ PINMUX_IPSR_GPSR(IP2_23_20, VI5_DATA14),
+ PINMUX_IPSR_GPSR(IP2_23_20, DU_DB6),
+
+ PINMUX_IPSR_GPSR(IP2_27_24, A7),
+ PINMUX_IPSR_GPSR(IP2_27_24, LCDOUT23),
+ PINMUX_IPSR_MSEL(IP2_27_24, MSIOF2_SS2_A, SEL_MSIOF2_0),
+ PINMUX_IPSR_MSEL(IP2_27_24, TX4_B, SEL_SCIF4_1),
+ PINMUX_IPSR_GPSR(IP2_27_24, VI4_DATA15),
+ PINMUX_IPSR_GPSR(IP2_27_24, VI5_DATA15),
+ PINMUX_IPSR_GPSR(IP2_27_24, DU_DB7),
+
+ PINMUX_IPSR_GPSR(IP2_31_28, A8),
+ PINMUX_IPSR_MSEL(IP2_31_28, RX3_B, SEL_SCIF3_1),
+ PINMUX_IPSR_MSEL(IP2_31_28, MSIOF2_SYNC_A, SEL_MSIOF2_0),
+ PINMUX_IPSR_MSEL(IP2_31_28, HRX4_B, SEL_HSCIF4_1),
+ PINMUX_IPSR_MSEL(IP2_31_28, SDA6_A, SEL_I2C6_0),
+ PINMUX_IPSR_MSEL(IP2_31_28, AVB_AVTP_MATCH_B, SEL_ETHERAVB_1),
+ PINMUX_IPSR_MSEL(IP2_31_28, PWM1_B, SEL_PWM1_1),
+
+ /* IPSR3 */
+ PINMUX_IPSR_GPSR(IP3_3_0, A9),
+ PINMUX_IPSR_MSEL(IP3_3_0, MSIOF2_SCK_A, SEL_MSIOF2_0),
+ PINMUX_IPSR_MSEL(IP3_3_0, CTS4_N_B, SEL_SCIF4_1),
+ PINMUX_IPSR_GPSR(IP3_3_0, VI5_VSYNC_N),
+
+ PINMUX_IPSR_GPSR(IP3_7_4, A10),
+ PINMUX_IPSR_MSEL(IP3_7_4, MSIOF2_RXD_A, SEL_MSIOF2_0),
+ PINMUX_IPSR_MSEL(IP3_7_4, RTS4_N_B, SEL_SCIF4_1),
+ PINMUX_IPSR_GPSR(IP3_7_4, VI5_HSYNC_N),
+
+ PINMUX_IPSR_GPSR(IP3_11_8, A11),
+ PINMUX_IPSR_MSEL(IP3_11_8, TX3_B, SEL_SCIF3_1),
+ PINMUX_IPSR_MSEL(IP3_11_8, MSIOF2_TXD_A, SEL_MSIOF2_0),
+ PINMUX_IPSR_MSEL(IP3_11_8, HTX4_B, SEL_HSCIF4_1),
+ PINMUX_IPSR_GPSR(IP3_11_8, HSCK4),
+ PINMUX_IPSR_GPSR(IP3_11_8, VI5_FIELD),
+ PINMUX_IPSR_MSEL(IP3_11_8, SCL6_A, SEL_I2C6_0),
+ PINMUX_IPSR_MSEL(IP3_11_8, AVB_AVTP_CAPTURE_B, SEL_ETHERAVB_1),
+ PINMUX_IPSR_MSEL(IP3_11_8, PWM2_B, SEL_PWM2_1),
+
+ PINMUX_IPSR_GPSR(IP3_15_12, A12),
+ PINMUX_IPSR_GPSR(IP3_15_12, LCDOUT12),
+ PINMUX_IPSR_MSEL(IP3_15_12, MSIOF3_SCK_C, SEL_MSIOF3_2),
+ PINMUX_IPSR_MSEL(IP3_15_12, HRX4_A, SEL_HSCIF4_0),
+ PINMUX_IPSR_GPSR(IP3_15_12, VI5_DATA8),
+ PINMUX_IPSR_GPSR(IP3_15_12, DU_DG4),
+
+ PINMUX_IPSR_GPSR(IP3_19_16, A13),
+ PINMUX_IPSR_GPSR(IP3_19_16, LCDOUT13),
+ PINMUX_IPSR_MSEL(IP3_19_16, MSIOF3_SYNC_C, SEL_MSIOF3_2),
+ PINMUX_IPSR_MSEL(IP3_19_16, HTX4_A, SEL_HSCIF4_0),
+ PINMUX_IPSR_GPSR(IP3_19_16, VI5_DATA9),
+ PINMUX_IPSR_GPSR(IP3_19_16, DU_DG5),
+
+ PINMUX_IPSR_GPSR(IP3_23_20, A14),
+ PINMUX_IPSR_GPSR(IP3_23_20, LCDOUT14),
+ PINMUX_IPSR_MSEL(IP3_23_20, MSIOF3_RXD_C, SEL_MSIOF3_2),
+ PINMUX_IPSR_GPSR(IP3_23_20, HCTS4_N),
+ PINMUX_IPSR_GPSR(IP3_23_20, VI5_DATA10),
+ PINMUX_IPSR_GPSR(IP3_23_20, DU_DG6),
+
+ PINMUX_IPSR_GPSR(IP3_27_24, A15),
+ PINMUX_IPSR_GPSR(IP3_27_24, LCDOUT15),
+ PINMUX_IPSR_MSEL(IP3_27_24, MSIOF3_TXD_C, SEL_MSIOF3_2),
+ PINMUX_IPSR_GPSR(IP3_27_24, HRTS4_N),
+ PINMUX_IPSR_GPSR(IP3_27_24, VI5_DATA11),
+ PINMUX_IPSR_GPSR(IP3_27_24, DU_DG7),
+
+ PINMUX_IPSR_GPSR(IP3_31_28, A16),
+ PINMUX_IPSR_GPSR(IP3_31_28, LCDOUT8),
+ PINMUX_IPSR_GPSR(IP3_31_28, VI4_FIELD),
+ PINMUX_IPSR_GPSR(IP3_31_28, DU_DG0),
+
+ /* IPSR4 */
+ PINMUX_IPSR_GPSR(IP4_3_0, A17),
+ PINMUX_IPSR_GPSR(IP4_3_0, LCDOUT9),
+ PINMUX_IPSR_GPSR(IP4_3_0, VI4_VSYNC_N),
+ PINMUX_IPSR_GPSR(IP4_3_0, DU_DG1),
+
+ PINMUX_IPSR_GPSR(IP4_7_4, A18),
+ PINMUX_IPSR_GPSR(IP4_7_4, LCDOUT10),
+ PINMUX_IPSR_GPSR(IP4_7_4, VI4_HSYNC_N),
+ PINMUX_IPSR_GPSR(IP4_7_4, DU_DG2),
+
+ PINMUX_IPSR_GPSR(IP4_11_8, A19),
+ PINMUX_IPSR_GPSR(IP4_11_8, LCDOUT11),
+ PINMUX_IPSR_GPSR(IP4_11_8, VI4_CLKENB),
+ PINMUX_IPSR_GPSR(IP4_11_8, DU_DG3),
+
+ PINMUX_IPSR_GPSR(IP4_15_12, CS0_N),
+ PINMUX_IPSR_GPSR(IP4_15_12, VI5_CLKENB),
+
+ PINMUX_IPSR_GPSR(IP4_19_16, CS1_N),
+ PINMUX_IPSR_GPSR(IP4_19_16, VI5_CLK),
+ PINMUX_IPSR_MSEL(IP4_19_16, EX_WAIT0_B, SEL_LBSC_1),
+
+ PINMUX_IPSR_GPSR(IP4_23_20, BS_N),
+ PINMUX_IPSR_GPSR(IP4_23_20, QSTVA_QVS),
+ PINMUX_IPSR_MSEL(IP4_23_20, MSIOF3_SCK_D, SEL_MSIOF3_3),
+ PINMUX_IPSR_GPSR(IP4_23_20, SCK3),
+ PINMUX_IPSR_GPSR(IP4_23_20, HSCK3),
+ PINMUX_IPSR_GPSR(IP4_23_20, CAN1_TX),
+ PINMUX_IPSR_GPSR(IP4_23_20, CANFD1_TX),
+ PINMUX_IPSR_MSEL(IP4_23_20, IETX_A, SEL_IEBUS_0),
+
+ PINMUX_IPSR_GPSR(IP4_27_24, RD_N),
+ PINMUX_IPSR_MSEL(IP4_27_24, MSIOF3_SYNC_D, SEL_MSIOF3_3),
+ PINMUX_IPSR_MSEL(IP4_27_24, RX3_A, SEL_SCIF3_0),
+ PINMUX_IPSR_MSEL(IP4_27_24, HRX3_A, SEL_HSCIF3_0),
+ PINMUX_IPSR_MSEL(IP4_27_24, CAN0_TX_A, SEL_RCAN0_0),
+ PINMUX_IPSR_MSEL(IP4_27_24, CANFD0_TX_A, SEL_CANFD0_0),
+
+ PINMUX_IPSR_GPSR(IP4_31_28, RD_WR_N),
+ PINMUX_IPSR_MSEL(IP4_31_28, MSIOF3_RXD_D, SEL_MSIOF3_3),
+ PINMUX_IPSR_MSEL(IP4_31_28, TX3_A, SEL_SCIF3_0),
+ PINMUX_IPSR_MSEL(IP4_31_28, HTX3_A, SEL_HSCIF3_0),
+ PINMUX_IPSR_MSEL(IP4_31_28, CAN0_RX_A, SEL_RCAN0_0),
+ PINMUX_IPSR_MSEL(IP4_31_28, CANFD0_RX_A, SEL_CANFD0_0),
+
+ /* IPSR5 */
+ PINMUX_IPSR_GPSR(IP5_3_0, WE0_N),
+ PINMUX_IPSR_MSEL(IP5_3_0, MSIOF3_TXD_D, SEL_MSIOF3_3),
+ PINMUX_IPSR_GPSR(IP5_3_0, CTS3_N),
+ PINMUX_IPSR_GPSR(IP5_3_0, HCTS3_N),
+ PINMUX_IPSR_MSEL(IP5_3_0, SCL6_B, SEL_I2C6_1),
+ PINMUX_IPSR_GPSR(IP5_3_0, CAN_CLK),
+ PINMUX_IPSR_MSEL(IP5_3_0, IECLK_A, SEL_IEBUS_0),
+
+ PINMUX_IPSR_GPSR(IP5_7_4, WE1_N),
+ PINMUX_IPSR_MSEL(IP5_7_4, MSIOF3_SS1_D, SEL_MSIOF3_3),
+ PINMUX_IPSR_GPSR(IP5_7_4, RTS3_N),
+ PINMUX_IPSR_GPSR(IP5_7_4, HRTS3_N),
+ PINMUX_IPSR_MSEL(IP5_7_4, SDA6_B, SEL_I2C6_1),
+ PINMUX_IPSR_GPSR(IP5_7_4, CAN1_RX),
+ PINMUX_IPSR_GPSR(IP5_7_4, CANFD1_RX),
+ PINMUX_IPSR_MSEL(IP5_7_4, IERX_A, SEL_IEBUS_0),
+
+ PINMUX_IPSR_MSEL(IP5_11_8, EX_WAIT0_A, SEL_LBSC_0),
+ PINMUX_IPSR_GPSR(IP5_11_8, QCLK),
+ PINMUX_IPSR_GPSR(IP5_11_8, VI4_CLK),
+ PINMUX_IPSR_GPSR(IP5_11_8, DU_DOTCLKOUT0),
+
+ PINMUX_IPSR_GPSR(IP5_15_12, D0),
+ PINMUX_IPSR_MSEL(IP5_15_12, MSIOF2_SS1_B, SEL_MSIOF2_1),
+ PINMUX_IPSR_MSEL(IP5_15_12, MSIOF3_SCK_A, SEL_MSIOF3_0),
+ PINMUX_IPSR_GPSR(IP5_15_12, VI4_DATA16),
+ PINMUX_IPSR_GPSR(IP5_15_12, VI5_DATA0),
+
+ PINMUX_IPSR_GPSR(IP5_19_16, D1),
+ PINMUX_IPSR_MSEL(IP5_19_16, MSIOF2_SS2_B, SEL_MSIOF2_1),
+ PINMUX_IPSR_MSEL(IP5_19_16, MSIOF3_SYNC_A, SEL_MSIOF3_0),
+ PINMUX_IPSR_GPSR(IP5_19_16, VI4_DATA17),
+ PINMUX_IPSR_GPSR(IP5_19_16, VI5_DATA1),
+
+ PINMUX_IPSR_GPSR(IP5_23_20, D2),
+ PINMUX_IPSR_MSEL(IP5_23_20, MSIOF3_RXD_A, SEL_MSIOF3_0),
+ PINMUX_IPSR_GPSR(IP5_23_20, VI4_DATA18),
+ PINMUX_IPSR_GPSR(IP5_23_20, VI5_DATA2),
+
+ PINMUX_IPSR_GPSR(IP5_27_24, D3),
+ PINMUX_IPSR_MSEL(IP5_27_24, MSIOF3_TXD_A, SEL_MSIOF3_0),
+ PINMUX_IPSR_GPSR(IP5_27_24, VI4_DATA19),
+ PINMUX_IPSR_GPSR(IP5_27_24, VI5_DATA3),
+
+ PINMUX_IPSR_GPSR(IP5_31_28, D4),
+ PINMUX_IPSR_MSEL(IP5_31_28, MSIOF2_SCK_B, SEL_MSIOF2_1),
+ PINMUX_IPSR_GPSR(IP5_31_28, VI4_DATA20),
+ PINMUX_IPSR_GPSR(IP5_31_28, VI5_DATA4),
+
+ /* IPSR6 */
+ PINMUX_IPSR_GPSR(IP6_3_0, D5),
+ PINMUX_IPSR_MSEL(IP6_3_0, MSIOF2_SYNC_B, SEL_MSIOF2_1),
+ PINMUX_IPSR_GPSR(IP6_3_0, VI4_DATA21),
+ PINMUX_IPSR_GPSR(IP6_3_0, VI5_DATA5),
+
+ PINMUX_IPSR_GPSR(IP6_7_4, D6),
+ PINMUX_IPSR_MSEL(IP6_7_4, MSIOF2_RXD_B, SEL_MSIOF2_1),
+ PINMUX_IPSR_GPSR(IP6_7_4, VI4_DATA22),
+ PINMUX_IPSR_GPSR(IP6_7_4, VI5_DATA6),
+
+ PINMUX_IPSR_GPSR(IP6_11_8, D7),
+ PINMUX_IPSR_MSEL(IP6_11_8, MSIOF2_TXD_B, SEL_MSIOF2_1),
+ PINMUX_IPSR_GPSR(IP6_11_8, VI4_DATA23),
+ PINMUX_IPSR_GPSR(IP6_11_8, VI5_DATA7),
+
+ PINMUX_IPSR_GPSR(IP6_15_12, D8),
+ PINMUX_IPSR_GPSR(IP6_15_12, LCDOUT0),
+ PINMUX_IPSR_MSEL(IP6_15_12, MSIOF2_SCK_D, SEL_MSIOF2_3),
+ PINMUX_IPSR_MSEL(IP6_15_12, SCK4_C, SEL_SCIF4_2),
+ PINMUX_IPSR_MSEL(IP6_15_12, VI4_DATA0_A, SEL_VIN4_0),
+ PINMUX_IPSR_GPSR(IP6_15_12, DU_DR0),
+
+ PINMUX_IPSR_GPSR(IP6_19_16, D9),
+ PINMUX_IPSR_GPSR(IP6_19_16, LCDOUT1),
+ PINMUX_IPSR_MSEL(IP6_19_16, MSIOF2_SYNC_D, SEL_MSIOF2_3),
+ PINMUX_IPSR_MSEL(IP6_19_16, VI4_DATA1_A, SEL_VIN4_0),
+ PINMUX_IPSR_GPSR(IP6_19_16, DU_DR1),
+
+ PINMUX_IPSR_GPSR(IP6_23_20, D10),
+ PINMUX_IPSR_GPSR(IP6_23_20, LCDOUT2),
+ PINMUX_IPSR_MSEL(IP6_23_20, MSIOF2_RXD_D, SEL_MSIOF2_3),
+ PINMUX_IPSR_MSEL(IP6_23_20, HRX3_B, SEL_HSCIF3_1),
+ PINMUX_IPSR_MSEL(IP6_23_20, VI4_DATA2_A, SEL_VIN4_0),
+ PINMUX_IPSR_MSEL(IP6_23_20, CTS4_N_C, SEL_SCIF4_2),
+ PINMUX_IPSR_GPSR(IP6_23_20, DU_DR2),
+
+ PINMUX_IPSR_GPSR(IP6_27_24, D11),
+ PINMUX_IPSR_GPSR(IP6_27_24, LCDOUT3),
+ PINMUX_IPSR_MSEL(IP6_27_24, MSIOF2_TXD_D, SEL_MSIOF2_3),
+ PINMUX_IPSR_MSEL(IP6_27_24, HTX3_B, SEL_HSCIF3_1),
+ PINMUX_IPSR_MSEL(IP6_27_24, VI4_DATA3_A, SEL_VIN4_0),
+ PINMUX_IPSR_MSEL(IP6_27_24, RTS4_N_C, SEL_SCIF4_2),
+ PINMUX_IPSR_GPSR(IP6_27_24, DU_DR3),
+
+ PINMUX_IPSR_GPSR(IP6_31_28, D12),
+ PINMUX_IPSR_GPSR(IP6_31_28, LCDOUT4),
+ PINMUX_IPSR_MSEL(IP6_31_28, MSIOF2_SS1_D, SEL_MSIOF2_3),
+ PINMUX_IPSR_MSEL(IP6_31_28, RX4_C, SEL_SCIF4_2),
+ PINMUX_IPSR_MSEL(IP6_31_28, VI4_DATA4_A, SEL_VIN4_0),
+ PINMUX_IPSR_GPSR(IP6_31_28, DU_DR4),
+
+ /* IPSR7 */
+ PINMUX_IPSR_GPSR(IP7_3_0, D13),
+ PINMUX_IPSR_GPSR(IP7_3_0, LCDOUT5),
+ PINMUX_IPSR_MSEL(IP7_3_0, MSIOF2_SS2_D, SEL_MSIOF2_3),
+ PINMUX_IPSR_MSEL(IP7_3_0, TX4_C, SEL_SCIF4_2),
+ PINMUX_IPSR_MSEL(IP7_3_0, VI4_DATA5_A, SEL_VIN4_0),
+ PINMUX_IPSR_GPSR(IP7_3_0, DU_DR5),
+
+ PINMUX_IPSR_GPSR(IP7_7_4, D14),
+ PINMUX_IPSR_GPSR(IP7_7_4, LCDOUT6),
+ PINMUX_IPSR_MSEL(IP7_7_4, MSIOF3_SS1_A, SEL_MSIOF3_0),
+ PINMUX_IPSR_MSEL(IP7_7_4, HRX3_C, SEL_HSCIF3_2),
+ PINMUX_IPSR_MSEL(IP7_7_4, VI4_DATA6_A, SEL_VIN4_0),
+ PINMUX_IPSR_GPSR(IP7_7_4, DU_DR6),
+ PINMUX_IPSR_MSEL(IP7_7_4, SCL6_C, SEL_I2C6_2),
+
+ PINMUX_IPSR_GPSR(IP7_11_8, D15),
+ PINMUX_IPSR_GPSR(IP7_11_8, LCDOUT7),
+ PINMUX_IPSR_MSEL(IP7_11_8, MSIOF3_SS2_A, SEL_MSIOF3_0),
+ PINMUX_IPSR_MSEL(IP7_11_8, HTX3_C, SEL_HSCIF3_2),
+ PINMUX_IPSR_MSEL(IP7_11_8, VI4_DATA7_A, SEL_VIN4_0),
+ PINMUX_IPSR_GPSR(IP7_11_8, DU_DR7),
+ PINMUX_IPSR_MSEL(IP7_11_8, SDA6_C, SEL_I2C6_2),
+
+ PINMUX_IPSR_GPSR(IP7_19_16, SD0_CLK),
+ PINMUX_IPSR_MSEL(IP7_19_16, MSIOF1_SCK_E, SEL_MSIOF1_4),
+ PINMUX_IPSR_MSEL(IP7_19_16, STP_OPWM_0_B, SEL_SSP1_0_1),
+
+ PINMUX_IPSR_GPSR(IP7_23_20, SD0_CMD),
+ PINMUX_IPSR_MSEL(IP7_23_20, MSIOF1_SYNC_E, SEL_MSIOF1_4),
+ PINMUX_IPSR_MSEL(IP7_23_20, STP_IVCXO27_0_B, SEL_SSP1_0_1),
+
+ PINMUX_IPSR_GPSR(IP7_27_24, SD0_DAT0),
+ PINMUX_IPSR_MSEL(IP7_27_24, MSIOF1_RXD_E, SEL_MSIOF1_4),
+ PINMUX_IPSR_MSEL(IP7_27_24, TS_SCK0_B, SEL_TSIF0_1),
+ PINMUX_IPSR_MSEL(IP7_27_24, STP_ISCLK_0_B, SEL_SSP1_0_1),
+
+ PINMUX_IPSR_GPSR(IP7_31_28, SD0_DAT1),
+ PINMUX_IPSR_MSEL(IP7_31_28, MSIOF1_TXD_E, SEL_MSIOF1_4),
+ PINMUX_IPSR_MSEL(IP7_31_28, TS_SPSYNC0_B, SEL_TSIF0_1),
+ PINMUX_IPSR_MSEL(IP7_31_28, STP_ISSYNC_0_B, SEL_SSP1_0_1),
+
+ /* IPSR8 */
+ PINMUX_IPSR_GPSR(IP8_3_0, SD0_DAT2),
+ PINMUX_IPSR_MSEL(IP8_3_0, MSIOF1_SS1_E, SEL_MSIOF1_4),
+ PINMUX_IPSR_MSEL(IP8_3_0, TS_SDAT0_B, SEL_TSIF0_1),
+ PINMUX_IPSR_MSEL(IP8_3_0, STP_ISD_0_B, SEL_SSP1_0_1),
+
+ PINMUX_IPSR_GPSR(IP8_7_4, SD0_DAT3),
+ PINMUX_IPSR_MSEL(IP8_7_4, MSIOF1_SS2_E, SEL_MSIOF1_4),
+ PINMUX_IPSR_MSEL(IP8_7_4, TS_SDEN0_B, SEL_TSIF0_1),
+ PINMUX_IPSR_MSEL(IP8_7_4, STP_ISEN_0_B, SEL_SSP1_0_1),
+
+ PINMUX_IPSR_GPSR(IP8_11_8, SD1_CLK),
+ PINMUX_IPSR_MSEL(IP8_11_8, MSIOF1_SCK_G, SEL_MSIOF1_6),
+ PINMUX_IPSR_MSEL(IP8_11_8, SIM0_CLK_A, SEL_SIMCARD_0),
+
+ PINMUX_IPSR_GPSR(IP8_15_12, SD1_CMD),
+ PINMUX_IPSR_MSEL(IP8_15_12, MSIOF1_SYNC_G, SEL_MSIOF1_6),
+ PINMUX_IPSR_MSEL(IP8_15_12, NFCE_N_B, SEL_NDFC_1),
+ PINMUX_IPSR_MSEL(IP8_15_12, SIM0_D_A, SEL_SIMCARD_0),
+ PINMUX_IPSR_MSEL(IP8_15_12, STP_IVCXO27_1_B, SEL_SSP1_1_1),
+
+ PINMUX_IPSR_GPSR(IP8_19_16, SD1_DAT0),
+ PINMUX_IPSR_GPSR(IP8_19_16, SD2_DAT4),
+ PINMUX_IPSR_MSEL(IP8_19_16, MSIOF1_RXD_G, SEL_MSIOF1_6),
+ PINMUX_IPSR_MSEL(IP8_19_16, NFWP_N_B, SEL_NDFC_1),
+ PINMUX_IPSR_MSEL(IP8_19_16, TS_SCK1_B, SEL_TSIF1_1),
+ PINMUX_IPSR_MSEL(IP8_19_16, STP_ISCLK_1_B, SEL_SSP1_1_1),
+
+ PINMUX_IPSR_GPSR(IP8_23_20, SD1_DAT1),
+ PINMUX_IPSR_GPSR(IP8_23_20, SD2_DAT5),
+ PINMUX_IPSR_MSEL(IP8_23_20, MSIOF1_TXD_G, SEL_MSIOF1_6),
+ PINMUX_IPSR_MSEL(IP8_23_20, NFDATA14_B, SEL_NDFC_1),
+ PINMUX_IPSR_MSEL(IP8_23_20, TS_SPSYNC1_B, SEL_TSIF1_1),
+ PINMUX_IPSR_MSEL(IP8_23_20, STP_ISSYNC_1_B, SEL_SSP1_1_1),
+
+ PINMUX_IPSR_GPSR(IP8_27_24, SD1_DAT2),
+ PINMUX_IPSR_GPSR(IP8_27_24, SD2_DAT6),
+ PINMUX_IPSR_MSEL(IP8_27_24, MSIOF1_SS1_G, SEL_MSIOF1_6),
+ PINMUX_IPSR_MSEL(IP8_27_24, NFDATA15_B, SEL_NDFC_1),
+ PINMUX_IPSR_MSEL(IP8_27_24, TS_SDAT1_B, SEL_TSIF1_1),
+ PINMUX_IPSR_MSEL(IP8_27_24, STP_ISD_1_B, SEL_SSP1_1_1),
+
+ PINMUX_IPSR_GPSR(IP8_31_28, SD1_DAT3),
+ PINMUX_IPSR_GPSR(IP8_31_28, SD2_DAT7),
+ PINMUX_IPSR_MSEL(IP8_31_28, MSIOF1_SS2_G, SEL_MSIOF1_6),
+ PINMUX_IPSR_MSEL(IP8_31_28, NFRB_N_B, SEL_NDFC_1),
+ PINMUX_IPSR_MSEL(IP8_31_28, TS_SDEN1_B, SEL_TSIF1_1),
+ PINMUX_IPSR_MSEL(IP8_31_28, STP_ISEN_1_B, SEL_SSP1_1_1),
+
+ /* IPSR9 */
+ PINMUX_IPSR_GPSR(IP9_3_0, SD2_CLK),
+ PINMUX_IPSR_GPSR(IP9_3_0, NFDATA8),
+
+ PINMUX_IPSR_GPSR(IP9_7_4, SD2_CMD),
+ PINMUX_IPSR_GPSR(IP9_7_4, NFDATA9),
+
+ PINMUX_IPSR_GPSR(IP9_11_8, SD2_DAT0),
+ PINMUX_IPSR_GPSR(IP9_11_8, NFDATA10),
+
+ PINMUX_IPSR_GPSR(IP9_15_12, SD2_DAT1),
+ PINMUX_IPSR_GPSR(IP9_15_12, NFDATA11),
+
+ PINMUX_IPSR_GPSR(IP9_19_16, SD2_DAT2),
+ PINMUX_IPSR_GPSR(IP9_19_16, NFDATA12),
+
+ PINMUX_IPSR_GPSR(IP9_23_20, SD2_DAT3),
+ PINMUX_IPSR_GPSR(IP9_23_20, NFDATA13),
+
+ PINMUX_IPSR_GPSR(IP9_27_24, SD2_DS),
+ PINMUX_IPSR_GPSR(IP9_27_24, NFALE),
+ PINMUX_IPSR_GPSR(IP9_27_24, SATA_DEVSLP_B),
+
+ PINMUX_IPSR_GPSR(IP9_31_28, SD3_CLK),
+ PINMUX_IPSR_GPSR(IP9_31_28, NFWE_N),
+
+ /* IPSR10 */
+ PINMUX_IPSR_GPSR(IP10_3_0, SD3_CMD),
+ PINMUX_IPSR_GPSR(IP10_3_0, NFRE_N),
+
+ PINMUX_IPSR_GPSR(IP10_7_4, SD3_DAT0),
+ PINMUX_IPSR_GPSR(IP10_7_4, NFDATA0),
+
+ PINMUX_IPSR_GPSR(IP10_11_8, SD3_DAT1),
+ PINMUX_IPSR_GPSR(IP10_11_8, NFDATA1),
+
+ PINMUX_IPSR_GPSR(IP10_15_12, SD3_DAT2),
+ PINMUX_IPSR_GPSR(IP10_15_12, NFDATA2),
+
+ PINMUX_IPSR_GPSR(IP10_19_16, SD3_DAT3),
+ PINMUX_IPSR_GPSR(IP10_19_16, NFDATA3),
+
+ PINMUX_IPSR_GPSR(IP10_23_20, SD3_DAT4),
+ PINMUX_IPSR_MSEL(IP10_23_20, SD2_CD_A, SEL_SDHI2_0),
+ PINMUX_IPSR_GPSR(IP10_23_20, NFDATA4),
+
+ PINMUX_IPSR_GPSR(IP10_27_24, SD3_DAT5),
+ PINMUX_IPSR_MSEL(IP10_27_24, SD2_WP_A, SEL_SDHI2_0),
+ PINMUX_IPSR_GPSR(IP10_27_24, NFDATA5),
+
+ PINMUX_IPSR_GPSR(IP10_31_28, SD3_DAT6),
+ PINMUX_IPSR_GPSR(IP10_31_28, SD3_CD),
+ PINMUX_IPSR_GPSR(IP10_31_28, NFDATA6),
+
+ /* IPSR11 */
+ PINMUX_IPSR_GPSR(IP11_3_0, SD3_DAT7),
+ PINMUX_IPSR_GPSR(IP11_3_0, SD3_WP),
+ PINMUX_IPSR_GPSR(IP11_3_0, NFDATA7),
+
+ PINMUX_IPSR_GPSR(IP11_7_4, SD3_DS),
+ PINMUX_IPSR_GPSR(IP11_7_4, NFCLE),
+
+ PINMUX_IPSR_GPSR(IP11_11_8, SD0_CD),
+ PINMUX_IPSR_MSEL(IP11_11_8, NFDATA14_A, SEL_NDFC_0),
+ PINMUX_IPSR_MSEL(IP11_11_8, SCL2_B, SEL_I2C2_1),
+ PINMUX_IPSR_MSEL(IP11_11_8, SIM0_RST_A, SEL_SIMCARD_0),
+
+ PINMUX_IPSR_GPSR(IP11_15_12, SD0_WP),
+ PINMUX_IPSR_MSEL(IP11_15_12, NFDATA15_A, SEL_NDFC_0),
+ PINMUX_IPSR_MSEL(IP11_15_12, SDA2_B, SEL_I2C2_1),
+
+ PINMUX_IPSR_GPSR(IP11_19_16, SD1_CD),
+ PINMUX_IPSR_MSEL(IP11_19_16, NFRB_N_A, SEL_NDFC_0),
+ PINMUX_IPSR_MSEL(IP11_19_16, SIM0_CLK_B, SEL_SIMCARD_1),
+
+ PINMUX_IPSR_GPSR(IP11_23_20, SD1_WP),
+ PINMUX_IPSR_MSEL(IP11_23_20, NFCE_N_A, SEL_NDFC_0),
+ PINMUX_IPSR_MSEL(IP11_23_20, SIM0_D_B, SEL_SIMCARD_1),
+
+ PINMUX_IPSR_GPSR(IP11_27_24, SCK0),
+ PINMUX_IPSR_MSEL(IP11_27_24, HSCK1_B, SEL_HSCIF1_1),
+ PINMUX_IPSR_MSEL(IP11_27_24, MSIOF1_SS2_B, SEL_MSIOF1_1),
+ PINMUX_IPSR_MSEL(IP11_27_24, AUDIO_CLKC_B, SEL_ADG_C_1),
+ PINMUX_IPSR_MSEL(IP11_27_24, SDA2_A, SEL_I2C2_0),
+ PINMUX_IPSR_MSEL(IP11_27_24, SIM0_RST_B, SEL_SIMCARD_1),
+ PINMUX_IPSR_MSEL(IP11_27_24, STP_OPWM_0_C, SEL_SSP1_0_2),
+ PINMUX_IPSR_MSEL(IP11_27_24, RIF0_CLK_B, SEL_DRIF0_1),
+ PINMUX_IPSR_GPSR(IP11_27_24, ADICHS2),
+ PINMUX_IPSR_MSEL(IP11_27_24, SCK5_B, SEL_SCIF5_1),
+
+ PINMUX_IPSR_GPSR(IP11_31_28, RX0),
+ PINMUX_IPSR_MSEL(IP11_31_28, HRX1_B, SEL_HSCIF1_1),
+ PINMUX_IPSR_MSEL(IP11_31_28, TS_SCK0_C, SEL_TSIF0_2),
+ PINMUX_IPSR_MSEL(IP11_31_28, STP_ISCLK_0_C, SEL_SSP1_0_2),
+ PINMUX_IPSR_MSEL(IP11_31_28, RIF0_D0_B, SEL_DRIF0_1),
+
+ /* IPSR12 */
+ PINMUX_IPSR_GPSR(IP12_3_0, TX0),
+ PINMUX_IPSR_MSEL(IP12_3_0, HTX1_B, SEL_HSCIF1_1),
+ PINMUX_IPSR_MSEL(IP12_3_0, TS_SPSYNC0_C, SEL_TSIF0_2),
+ PINMUX_IPSR_MSEL(IP12_3_0, STP_ISSYNC_0_C, SEL_SSP1_0_2),
+ PINMUX_IPSR_MSEL(IP12_3_0, RIF0_D1_B, SEL_DRIF0_1),
+
+ PINMUX_IPSR_GPSR(IP12_7_4, CTS0_N),
+ PINMUX_IPSR_MSEL(IP12_7_4, HCTS1_N_B, SEL_HSCIF1_1),
+ PINMUX_IPSR_MSEL(IP12_7_4, MSIOF1_SYNC_B, SEL_MSIOF1_1),
+ PINMUX_IPSR_MSEL(IP12_7_4, TS_SPSYNC1_C, SEL_TSIF1_2),
+ PINMUX_IPSR_MSEL(IP12_7_4, STP_ISSYNC_1_C, SEL_SSP1_1_2),
+ PINMUX_IPSR_MSEL(IP12_7_4, RIF1_SYNC_B, SEL_DRIF1_1),
+ PINMUX_IPSR_GPSR(IP12_7_4, AUDIO_CLKOUT_C),
+ PINMUX_IPSR_GPSR(IP12_7_4, ADICS_SAMP),
+
+ PINMUX_IPSR_GPSR(IP12_11_8, RTS0_N),
+ PINMUX_IPSR_MSEL(IP12_11_8, HRTS1_N_B, SEL_HSCIF1_1),
+ PINMUX_IPSR_MSEL(IP12_11_8, MSIOF1_SS1_B, SEL_MSIOF1_1),
+ PINMUX_IPSR_MSEL(IP12_11_8, AUDIO_CLKA_B, SEL_ADG_A_1),
+ PINMUX_IPSR_MSEL(IP12_11_8, SCL2_A, SEL_I2C2_0),
+ PINMUX_IPSR_MSEL(IP12_11_8, STP_IVCXO27_1_C, SEL_SSP1_1_2),
+ PINMUX_IPSR_MSEL(IP12_11_8, RIF0_SYNC_B, SEL_DRIF0_1),
+ PINMUX_IPSR_GPSR(IP12_11_8, ADICHS1),
+
+ PINMUX_IPSR_MSEL(IP12_15_12, RX1_A, SEL_SCIF1_0),
+ PINMUX_IPSR_MSEL(IP12_15_12, HRX1_A, SEL_HSCIF1_0),
+ PINMUX_IPSR_MSEL(IP12_15_12, TS_SDAT0_C, SEL_TSIF0_2),
+ PINMUX_IPSR_MSEL(IP12_15_12, STP_ISD_0_C, SEL_SSP1_0_2),
+ PINMUX_IPSR_MSEL(IP12_15_12, RIF1_CLK_C, SEL_DRIF1_2),
+
+ PINMUX_IPSR_MSEL(IP12_19_16, TX1_A, SEL_SCIF1_0),
+ PINMUX_IPSR_MSEL(IP12_19_16, HTX1_A, SEL_HSCIF1_0),
+ PINMUX_IPSR_MSEL(IP12_19_16, TS_SDEN0_C, SEL_TSIF0_2),
+ PINMUX_IPSR_MSEL(IP12_19_16, STP_ISEN_0_C, SEL_SSP1_0_2),
+ PINMUX_IPSR_MSEL(IP12_19_16, RIF1_D0_C, SEL_DRIF1_2),
+
+ PINMUX_IPSR_GPSR(IP12_23_20, CTS1_N),
+ PINMUX_IPSR_MSEL(IP12_23_20, HCTS1_N_A, SEL_HSCIF1_0),
+ PINMUX_IPSR_MSEL(IP12_23_20, MSIOF1_RXD_B, SEL_MSIOF1_1),
+ PINMUX_IPSR_MSEL(IP12_23_20, TS_SDEN1_C, SEL_TSIF1_2),
+ PINMUX_IPSR_MSEL(IP12_23_20, STP_ISEN_1_C, SEL_SSP1_1_2),
+ PINMUX_IPSR_MSEL(IP12_23_20, RIF1_D0_B, SEL_DRIF1_1),
+ PINMUX_IPSR_GPSR(IP12_23_20, ADIDATA),
+
+ PINMUX_IPSR_GPSR(IP12_27_24, RTS1_N),
+ PINMUX_IPSR_MSEL(IP12_27_24, HRTS1_N_A, SEL_HSCIF1_0),
+ PINMUX_IPSR_MSEL(IP12_27_24, MSIOF1_TXD_B, SEL_MSIOF1_1),
+ PINMUX_IPSR_MSEL(IP12_27_24, TS_SDAT1_C, SEL_TSIF1_2),
+ PINMUX_IPSR_MSEL(IP12_27_24, STP_ISD_1_C, SEL_SSP1_1_2),
+ PINMUX_IPSR_MSEL(IP12_27_24, RIF1_D1_B, SEL_DRIF1_1),
+ PINMUX_IPSR_GPSR(IP12_27_24, ADICHS0),
+
+ PINMUX_IPSR_GPSR(IP12_31_28, SCK2),
+ PINMUX_IPSR_MSEL(IP12_31_28, SCIF_CLK_B, SEL_SCIF_1),
+ PINMUX_IPSR_MSEL(IP12_31_28, MSIOF1_SCK_B, SEL_MSIOF1_1),
+ PINMUX_IPSR_MSEL(IP12_31_28, TS_SCK1_C, SEL_TSIF1_2),
+ PINMUX_IPSR_MSEL(IP12_31_28, STP_ISCLK_1_C, SEL_SSP1_1_2),
+ PINMUX_IPSR_MSEL(IP12_31_28, RIF1_CLK_B, SEL_DRIF1_1),
+ PINMUX_IPSR_GPSR(IP12_31_28, ADICLK),
+
+ /* IPSR13 */
+ PINMUX_IPSR_MSEL(IP13_3_0, TX2_A, SEL_SCIF2_0),
+ PINMUX_IPSR_MSEL(IP13_3_0, SD2_CD_B, SEL_SDHI2_1),
+ PINMUX_IPSR_MSEL(IP13_3_0, SCL1_A, SEL_I2C1_0),
+ PINMUX_IPSR_MSEL(IP13_3_0, FMCLK_A, SEL_FM_0),
+ PINMUX_IPSR_MSEL(IP13_3_0, RIF1_D1_C, SEL_DRIF1_2),
+ PINMUX_IPSR_GPSR(IP13_3_0, FSO_CFE_0_N),
+
+ PINMUX_IPSR_MSEL(IP13_7_4, RX2_A, SEL_SCIF2_0),
+ PINMUX_IPSR_MSEL(IP13_7_4, SD2_WP_B, SEL_SDHI2_1),
+ PINMUX_IPSR_MSEL(IP13_7_4, SDA1_A, SEL_I2C1_0),
+ PINMUX_IPSR_MSEL(IP13_7_4, FMIN_A, SEL_FM_0),
+ PINMUX_IPSR_MSEL(IP13_7_4, RIF1_SYNC_C, SEL_DRIF1_2),
+ PINMUX_IPSR_GPSR(IP13_7_4, FSO_CFE_1_N),
+
+ PINMUX_IPSR_GPSR(IP13_11_8, HSCK0),
+ PINMUX_IPSR_MSEL(IP13_11_8, MSIOF1_SCK_D, SEL_MSIOF1_3),
+ PINMUX_IPSR_MSEL(IP13_11_8, AUDIO_CLKB_A, SEL_ADG_B_0),
+ PINMUX_IPSR_MSEL(IP13_11_8, SSI_SDATA1_B, SEL_SSI1_1),
+ PINMUX_IPSR_MSEL(IP13_11_8, TS_SCK0_D, SEL_TSIF0_3),
+ PINMUX_IPSR_MSEL(IP13_11_8, STP_ISCLK_0_D, SEL_SSP1_0_3),
+ PINMUX_IPSR_MSEL(IP13_11_8, RIF0_CLK_C, SEL_DRIF0_2),
+ PINMUX_IPSR_MSEL(IP13_11_8, RX5_B, SEL_SCIF5_1),
+
+ PINMUX_IPSR_GPSR(IP13_15_12, HRX0),
+ PINMUX_IPSR_MSEL(IP13_15_12, MSIOF1_RXD_D, SEL_MSIOF1_3),
+ PINMUX_IPSR_MSEL(IP13_15_12, SSI_SDATA2_B, SEL_SSI2_1),
+ PINMUX_IPSR_MSEL(IP13_15_12, TS_SDEN0_D, SEL_TSIF0_3),
+ PINMUX_IPSR_MSEL(IP13_15_12, STP_ISEN_0_D, SEL_SSP1_0_3),
+ PINMUX_IPSR_MSEL(IP13_15_12, RIF0_D0_C, SEL_DRIF0_2),
+
+ PINMUX_IPSR_GPSR(IP13_19_16, HTX0),
+ PINMUX_IPSR_MSEL(IP13_19_16, MSIOF1_TXD_D, SEL_MSIOF1_3),
+ PINMUX_IPSR_MSEL(IP13_19_16, SSI_SDATA9_B, SEL_SSI9_1),
+ PINMUX_IPSR_MSEL(IP13_19_16, TS_SDAT0_D, SEL_TSIF0_3),
+ PINMUX_IPSR_MSEL(IP13_19_16, STP_ISD_0_D, SEL_SSP1_0_3),
+ PINMUX_IPSR_MSEL(IP13_19_16, RIF0_D1_C, SEL_DRIF0_2),
+
+ PINMUX_IPSR_GPSR(IP13_23_20, HCTS0_N),
+ PINMUX_IPSR_MSEL(IP13_23_20, RX2_B, SEL_SCIF2_1),
+ PINMUX_IPSR_MSEL(IP13_23_20, MSIOF1_SYNC_D, SEL_MSIOF1_3),
+ PINMUX_IPSR_MSEL(IP13_23_20, SSI_SCK9_A, SEL_SSI9_0),
+ PINMUX_IPSR_MSEL(IP13_23_20, TS_SPSYNC0_D, SEL_TSIF0_3),
+ PINMUX_IPSR_MSEL(IP13_23_20, STP_ISSYNC_0_D, SEL_SSP1_0_3),
+ PINMUX_IPSR_MSEL(IP13_23_20, RIF0_SYNC_C, SEL_DRIF0_2),
+ PINMUX_IPSR_GPSR(IP13_23_20, AUDIO_CLKOUT1_A),
+
+ PINMUX_IPSR_GPSR(IP13_27_24, HRTS0_N),
+ PINMUX_IPSR_MSEL(IP13_27_24, TX2_B, SEL_SCIF2_1),
+ PINMUX_IPSR_MSEL(IP13_27_24, MSIOF1_SS1_D, SEL_MSIOF1_3),
+ PINMUX_IPSR_MSEL(IP13_27_24, SSI_WS9_A, SEL_SSI9_0),
+ PINMUX_IPSR_MSEL(IP13_27_24, STP_IVCXO27_0_D, SEL_SSP1_0_3),
+ PINMUX_IPSR_MSEL(IP13_27_24, BPFCLK_A, SEL_FM_0),
+ PINMUX_IPSR_GPSR(IP13_27_24, AUDIO_CLKOUT2_A),
+
+ PINMUX_IPSR_GPSR(IP13_31_28, MSIOF0_SYNC),
+ PINMUX_IPSR_GPSR(IP13_31_28, AUDIO_CLKOUT_A),
+ PINMUX_IPSR_MSEL(IP13_31_28, TX5_B, SEL_SCIF5_1),
+ PINMUX_IPSR_MSEL(IP13_31_28, BPFCLK_D, SEL_FM_3),
+
+ /* IPSR14 */
+ PINMUX_IPSR_GPSR(IP14_3_0, MSIOF0_SS1),
+ PINMUX_IPSR_MSEL(IP14_3_0, RX5_A, SEL_SCIF5_0),
+ PINMUX_IPSR_MSEL(IP14_3_0, NFWP_N_A, SEL_NDFC_0),
+ PINMUX_IPSR_MSEL(IP14_3_0, AUDIO_CLKA_C, SEL_ADG_A_2),
+ PINMUX_IPSR_MSEL(IP14_3_0, SSI_SCK2_A, SEL_SSI2_0),
+ PINMUX_IPSR_MSEL(IP14_3_0, STP_IVCXO27_0_C, SEL_SSP1_0_2),
+ PINMUX_IPSR_GPSR(IP14_3_0, AUDIO_CLKOUT3_A),
+ PINMUX_IPSR_MSEL(IP14_3_0, TCLK1_B, SEL_TIMER_TMU_1),
+
+ PINMUX_IPSR_GPSR(IP14_7_4, MSIOF0_SS2),
+ PINMUX_IPSR_MSEL(IP14_7_4, TX5_A, SEL_SCIF5_0),
+ PINMUX_IPSR_MSEL(IP14_7_4, MSIOF1_SS2_D, SEL_MSIOF1_3),
+ PINMUX_IPSR_MSEL(IP14_7_4, AUDIO_CLKC_A, SEL_ADG_C_0),
+ PINMUX_IPSR_MSEL(IP14_7_4, SSI_WS2_A, SEL_SSI2_0),
+ PINMUX_IPSR_MSEL(IP14_7_4, STP_OPWM_0_D, SEL_SSP1_0_3),
+ PINMUX_IPSR_GPSR(IP14_7_4, AUDIO_CLKOUT_D),
+ PINMUX_IPSR_MSEL(IP14_7_4, SPEEDIN_B, SEL_SPEED_PULSE_1),
+
+ PINMUX_IPSR_GPSR(IP14_11_8, MLB_CLK),
+ PINMUX_IPSR_MSEL(IP14_11_8, MSIOF1_SCK_F, SEL_MSIOF1_5),
+ PINMUX_IPSR_MSEL(IP14_11_8, SCL1_B, SEL_I2C1_1),
+
+ PINMUX_IPSR_GPSR(IP14_15_12, MLB_SIG),
+ PINMUX_IPSR_MSEL(IP14_15_12, RX1_B, SEL_SCIF1_1),
+ PINMUX_IPSR_MSEL(IP14_15_12, MSIOF1_SYNC_F, SEL_MSIOF1_5),
+ PINMUX_IPSR_MSEL(IP14_15_12, SDA1_B, SEL_I2C1_1),
+
+ PINMUX_IPSR_GPSR(IP14_19_16, MLB_DAT),
+ PINMUX_IPSR_MSEL(IP14_19_16, TX1_B, SEL_SCIF1_1),
+ PINMUX_IPSR_MSEL(IP14_19_16, MSIOF1_RXD_F, SEL_MSIOF1_5),
+
+ PINMUX_IPSR_GPSR(IP14_23_20, SSI_SCK01239),
+ PINMUX_IPSR_MSEL(IP14_23_20, MSIOF1_TXD_F, SEL_MSIOF1_5),
+
+ PINMUX_IPSR_GPSR(IP14_27_24, SSI_WS01239),
+ PINMUX_IPSR_MSEL(IP14_27_24, MSIOF1_SS1_F, SEL_MSIOF1_5),
+
+ PINMUX_IPSR_GPSR(IP14_31_28, SSI_SDATA0),
+ PINMUX_IPSR_MSEL(IP14_31_28, MSIOF1_SS2_F, SEL_MSIOF1_5),
+
+ /* IPSR15 */
+ PINMUX_IPSR_MSEL(IP15_3_0, SSI_SDATA1_A, SEL_SSI1_0),
+
+ PINMUX_IPSR_MSEL(IP15_7_4, SSI_SDATA2_A, SEL_SSI2_0),
+ PINMUX_IPSR_MSEL(IP15_7_4, SSI_SCK1_B, SEL_SSI1_1),
+
+ PINMUX_IPSR_GPSR(IP15_11_8, SSI_SCK349),
+ PINMUX_IPSR_MSEL(IP15_11_8, MSIOF1_SS1_A, SEL_MSIOF1_0),
+ PINMUX_IPSR_MSEL(IP15_11_8, STP_OPWM_0_A, SEL_SSP1_0_0),
+
+ PINMUX_IPSR_GPSR(IP15_15_12, SSI_WS349),
+ PINMUX_IPSR_MSEL(IP15_15_12, HCTS2_N_A, SEL_HSCIF2_0),
+ PINMUX_IPSR_MSEL(IP15_15_12, MSIOF1_SS2_A, SEL_MSIOF1_0),
+ PINMUX_IPSR_MSEL(IP15_15_12, STP_IVCXO27_0_A, SEL_SSP1_0_0),
+
+ PINMUX_IPSR_GPSR(IP15_19_16, SSI_SDATA3),
+ PINMUX_IPSR_MSEL(IP15_19_16, HRTS2_N_A, SEL_HSCIF2_0),
+ PINMUX_IPSR_MSEL(IP15_19_16, MSIOF1_TXD_A, SEL_MSIOF1_0),
+ PINMUX_IPSR_MSEL(IP15_19_16, TS_SCK0_A, SEL_TSIF0_0),
+ PINMUX_IPSR_MSEL(IP15_19_16, STP_ISCLK_0_A, SEL_SSP1_0_0),
+ PINMUX_IPSR_MSEL(IP15_19_16, RIF0_D1_A, SEL_DRIF0_0),
+ PINMUX_IPSR_MSEL(IP15_19_16, RIF2_D0_A, SEL_DRIF2_0),
+
+ PINMUX_IPSR_GPSR(IP15_23_20, SSI_SCK4),
+ PINMUX_IPSR_MSEL(IP15_23_20, HRX2_A, SEL_HSCIF2_0),
+ PINMUX_IPSR_MSEL(IP15_23_20, MSIOF1_SCK_A, SEL_MSIOF1_0),
+ PINMUX_IPSR_MSEL(IP15_23_20, TS_SDAT0_A, SEL_TSIF0_0),
+ PINMUX_IPSR_MSEL(IP15_23_20, STP_ISD_0_A, SEL_SSP1_0_0),
+ PINMUX_IPSR_MSEL(IP15_23_20, RIF0_CLK_A, SEL_DRIF0_0),
+ PINMUX_IPSR_MSEL(IP15_23_20, RIF2_CLK_A, SEL_DRIF2_0),
+
+ PINMUX_IPSR_GPSR(IP15_27_24, SSI_WS4),
+ PINMUX_IPSR_MSEL(IP15_27_24, HTX2_A, SEL_HSCIF2_0),
+ PINMUX_IPSR_MSEL(IP15_27_24, MSIOF1_SYNC_A, SEL_MSIOF1_0),
+ PINMUX_IPSR_MSEL(IP15_27_24, TS_SDEN0_A, SEL_TSIF0_0),
+ PINMUX_IPSR_MSEL(IP15_27_24, STP_ISEN_0_A, SEL_SSP1_0_0),
+ PINMUX_IPSR_MSEL(IP15_27_24, RIF0_SYNC_A, SEL_DRIF0_0),
+ PINMUX_IPSR_MSEL(IP15_27_24, RIF2_SYNC_A, SEL_DRIF2_0),
+
+ PINMUX_IPSR_GPSR(IP15_31_28, SSI_SDATA4),
+ PINMUX_IPSR_MSEL(IP15_31_28, HSCK2_A, SEL_HSCIF2_0),
+ PINMUX_IPSR_MSEL(IP15_31_28, MSIOF1_RXD_A, SEL_MSIOF1_0),
+ PINMUX_IPSR_MSEL(IP15_31_28, TS_SPSYNC0_A, SEL_TSIF0_0),
+ PINMUX_IPSR_MSEL(IP15_31_28, STP_ISSYNC_0_A, SEL_SSP1_0_0),
+ PINMUX_IPSR_MSEL(IP15_31_28, RIF0_D0_A, SEL_DRIF0_0),
+ PINMUX_IPSR_MSEL(IP15_31_28, RIF2_D1_A, SEL_DRIF2_0),
+
+ /* IPSR16 */
+ PINMUX_IPSR_GPSR(IP16_3_0, SSI_SCK6),
+ PINMUX_IPSR_MSEL(IP16_3_0, SIM0_RST_D, SEL_SIMCARD_3),
+
+ PINMUX_IPSR_GPSR(IP16_7_4, SSI_WS6),
+ PINMUX_IPSR_MSEL(IP16_7_4, SIM0_D_D, SEL_SIMCARD_3),
+
+ PINMUX_IPSR_GPSR(IP16_11_8, SSI_SDATA6),
+ PINMUX_IPSR_MSEL(IP16_11_8, SIM0_CLK_D, SEL_SIMCARD_3),
+ PINMUX_IPSR_GPSR(IP16_11_8, SATA_DEVSLP_A),
+
+ PINMUX_IPSR_GPSR(IP16_15_12, SSI_SCK78),
+ PINMUX_IPSR_MSEL(IP16_15_12, HRX2_B, SEL_HSCIF2_1),
+ PINMUX_IPSR_MSEL(IP16_15_12, MSIOF1_SCK_C, SEL_MSIOF1_2),
+ PINMUX_IPSR_MSEL(IP16_15_12, TS_SCK1_A, SEL_TSIF1_0),
+ PINMUX_IPSR_MSEL(IP16_15_12, STP_ISCLK_1_A, SEL_SSP1_1_0),
+ PINMUX_IPSR_MSEL(IP16_15_12, RIF1_CLK_A, SEL_DRIF1_0),
+ PINMUX_IPSR_MSEL(IP16_15_12, RIF3_CLK_A, SEL_DRIF3_0),
+
+ PINMUX_IPSR_GPSR(IP16_19_16, SSI_WS78),
+ PINMUX_IPSR_MSEL(IP16_19_16, HTX2_B, SEL_HSCIF2_1),
+ PINMUX_IPSR_MSEL(IP16_19_16, MSIOF1_SYNC_C, SEL_MSIOF1_2),
+ PINMUX_IPSR_MSEL(IP16_19_16, TS_SDAT1_A, SEL_TSIF1_0),
+ PINMUX_IPSR_MSEL(IP16_19_16, STP_ISD_1_A, SEL_SSP1_1_0),
+ PINMUX_IPSR_MSEL(IP16_19_16, RIF1_SYNC_A, SEL_DRIF1_0),
+ PINMUX_IPSR_MSEL(IP16_19_16, RIF3_SYNC_A, SEL_DRIF3_0),
+
+ PINMUX_IPSR_GPSR(IP16_23_20, SSI_SDATA7),
+ PINMUX_IPSR_MSEL(IP16_23_20, HCTS2_N_B, SEL_HSCIF2_1),
+ PINMUX_IPSR_MSEL(IP16_23_20, MSIOF1_RXD_C, SEL_MSIOF1_2),
+ PINMUX_IPSR_MSEL(IP16_23_20, TS_SDEN1_A, SEL_TSIF1_0),
+ PINMUX_IPSR_MSEL(IP16_23_20, STP_ISEN_1_A, SEL_SSP1_1_0),
+ PINMUX_IPSR_MSEL(IP16_23_20, RIF1_D0_A, SEL_DRIF1_0),
+ PINMUX_IPSR_MSEL(IP16_23_20, RIF3_D0_A, SEL_DRIF3_0),
+ PINMUX_IPSR_MSEL(IP16_23_20, TCLK2_A, SEL_TIMER_TMU2_0),
+
+ PINMUX_IPSR_GPSR(IP16_27_24, SSI_SDATA8),
+ PINMUX_IPSR_MSEL(IP16_27_24, HRTS2_N_B, SEL_HSCIF2_1),
+ PINMUX_IPSR_MSEL(IP16_27_24, MSIOF1_TXD_C, SEL_MSIOF1_2),
+ PINMUX_IPSR_MSEL(IP16_27_24, TS_SPSYNC1_A, SEL_TSIF1_0),
+ PINMUX_IPSR_MSEL(IP16_27_24, STP_ISSYNC_1_A, SEL_SSP1_1_0),
+ PINMUX_IPSR_MSEL(IP16_27_24, RIF1_D1_A, SEL_DRIF1_0),
+ PINMUX_IPSR_MSEL(IP16_27_24, RIF3_D1_A, SEL_DRIF3_0),
+
+ PINMUX_IPSR_MSEL(IP16_31_28, SSI_SDATA9_A, SEL_SSI9_0),
+ PINMUX_IPSR_MSEL(IP16_31_28, HSCK2_B, SEL_HSCIF2_1),
+ PINMUX_IPSR_MSEL(IP16_31_28, MSIOF1_SS1_C, SEL_MSIOF1_2),
+ PINMUX_IPSR_MSEL(IP16_31_28, HSCK1_A, SEL_HSCIF1_0),
+ PINMUX_IPSR_MSEL(IP16_31_28, SSI_WS1_B, SEL_SSI1_1),
+ PINMUX_IPSR_GPSR(IP16_31_28, SCK1),
+ PINMUX_IPSR_MSEL(IP16_31_28, STP_IVCXO27_1_A, SEL_SSP1_1_0),
+ PINMUX_IPSR_MSEL(IP16_31_28, SCK5_A, SEL_SCIF5_0),
+
+ /* IPSR17 */
+ PINMUX_IPSR_MSEL(IP17_3_0, AUDIO_CLKA_A, SEL_ADG_A_0),
+ PINMUX_IPSR_GPSR(IP17_3_0, CC5_OSCOUT),
+
+ PINMUX_IPSR_MSEL(IP17_7_4, AUDIO_CLKB_B, SEL_ADG_B_1),
+ PINMUX_IPSR_MSEL(IP17_7_4, SCIF_CLK_A, SEL_SCIF_0),
+ PINMUX_IPSR_MSEL(IP17_7_4, STP_IVCXO27_1_D, SEL_SSP1_1_3),
+ PINMUX_IPSR_MSEL(IP17_7_4, REMOCON_A, SEL_REMOCON_0),
+ PINMUX_IPSR_MSEL(IP17_7_4, TCLK1_A, SEL_TIMER_TMU_0),
+
+ PINMUX_IPSR_GPSR(IP17_11_8, USB0_PWEN),
+ PINMUX_IPSR_MSEL(IP17_11_8, SIM0_RST_C, SEL_SIMCARD_2),
+ PINMUX_IPSR_MSEL(IP17_11_8, TS_SCK1_D, SEL_TSIF1_3),
+ PINMUX_IPSR_MSEL(IP17_11_8, STP_ISCLK_1_D, SEL_SSP1_1_3),
+ PINMUX_IPSR_MSEL(IP17_11_8, BPFCLK_B, SEL_FM_1),
+ PINMUX_IPSR_MSEL(IP17_11_8, RIF3_CLK_B, SEL_DRIF3_1),
+ PINMUX_IPSR_MSEL(IP17_11_8, HSCK2_C, SEL_HSCIF2_2),
+
+ PINMUX_IPSR_GPSR(IP17_15_12, USB0_OVC),
+ PINMUX_IPSR_MSEL(IP17_15_12, SIM0_D_C, SEL_SIMCARD_2),
+ PINMUX_IPSR_MSEL(IP17_15_12, TS_SDAT1_D, SEL_TSIF1_3),
+ PINMUX_IPSR_MSEL(IP17_15_12, STP_ISD_1_D, SEL_SSP1_1_3),
+ PINMUX_IPSR_MSEL(IP17_15_12, RIF3_SYNC_B, SEL_DRIF3_1),
+ PINMUX_IPSR_MSEL(IP17_15_12, HRX2_C, SEL_HSCIF2_2),
+
+ PINMUX_IPSR_GPSR(IP17_19_16, USB1_PWEN),
+ PINMUX_IPSR_MSEL(IP17_19_16, SIM0_CLK_C, SEL_SIMCARD_2),
+ PINMUX_IPSR_MSEL(IP17_19_16, SSI_SCK1_A, SEL_SSI1_0),
+ PINMUX_IPSR_MSEL(IP17_19_16, TS_SCK0_E, SEL_TSIF0_4),
+ PINMUX_IPSR_MSEL(IP17_19_16, STP_ISCLK_0_E, SEL_SSP1_0_4),
+ PINMUX_IPSR_MSEL(IP17_19_16, FMCLK_B, SEL_FM_1),
+ PINMUX_IPSR_MSEL(IP17_19_16, RIF2_CLK_B, SEL_DRIF2_1),
+ PINMUX_IPSR_MSEL(IP17_19_16, SPEEDIN_A, SEL_SPEED_PULSE_0),
+ PINMUX_IPSR_MSEL(IP17_19_16, HTX2_C, SEL_HSCIF2_2),
+
+ PINMUX_IPSR_GPSR(IP17_23_20, USB1_OVC),
+ PINMUX_IPSR_MSEL(IP17_23_20, MSIOF1_SS2_C, SEL_MSIOF1_2),
+ PINMUX_IPSR_MSEL(IP17_23_20, SSI_WS1_A, SEL_SSI1_0),
+ PINMUX_IPSR_MSEL(IP17_23_20, TS_SDAT0_E, SEL_TSIF0_4),
+ PINMUX_IPSR_MSEL(IP17_23_20, STP_ISD_0_E, SEL_SSP1_0_4),
+ PINMUX_IPSR_MSEL(IP17_23_20, FMIN_B, SEL_FM_1),
+ PINMUX_IPSR_MSEL(IP17_23_20, RIF2_SYNC_B, SEL_DRIF2_1),
+ PINMUX_IPSR_MSEL(IP17_23_20, REMOCON_B, SEL_REMOCON_1),
+ PINMUX_IPSR_MSEL(IP17_23_20, HCTS2_N_C, SEL_HSCIF2_2),
+
+ PINMUX_IPSR_GPSR(IP17_27_24, USB30_PWEN),
+ PINMUX_IPSR_GPSR(IP17_27_24, AUDIO_CLKOUT_B),
+ PINMUX_IPSR_MSEL(IP17_27_24, SSI_SCK2_B, SEL_SSI2_1),
+ PINMUX_IPSR_MSEL(IP17_27_24, TS_SDEN1_D, SEL_TSIF1_3),
+ PINMUX_IPSR_MSEL(IP17_27_24, STP_ISEN_1_D, SEL_SSP1_1_3),
+ PINMUX_IPSR_MSEL(IP17_27_24, STP_OPWM_0_E, SEL_SSP1_0_4),
+ PINMUX_IPSR_MSEL(IP17_27_24, RIF3_D0_B, SEL_DRIF3_1),
+ PINMUX_IPSR_MSEL(IP17_27_24, TCLK2_B, SEL_TIMER_TMU2_1),
+ PINMUX_IPSR_GPSR(IP17_27_24, TPU0TO0),
+ PINMUX_IPSR_MSEL(IP17_27_24, BPFCLK_C, SEL_FM_2),
+ PINMUX_IPSR_MSEL(IP17_27_24, HRTS2_N_C, SEL_HSCIF2_2),
+
+ PINMUX_IPSR_GPSR(IP17_31_28, USB30_OVC),
+ PINMUX_IPSR_GPSR(IP17_31_28, AUDIO_CLKOUT1_B),
+ PINMUX_IPSR_MSEL(IP17_31_28, SSI_WS2_B, SEL_SSI2_1),
+ PINMUX_IPSR_MSEL(IP17_31_28, TS_SPSYNC1_D, SEL_TSIF1_3),
+ PINMUX_IPSR_MSEL(IP17_31_28, STP_ISSYNC_1_D, SEL_SSP1_1_3),
+ PINMUX_IPSR_MSEL(IP17_31_28, STP_IVCXO27_0_E, SEL_SSP1_0_4),
+ PINMUX_IPSR_MSEL(IP17_31_28, RIF3_D1_B, SEL_DRIF3_1),
+ PINMUX_IPSR_GPSR(IP17_31_28, FSO_TOE_N),
+ PINMUX_IPSR_GPSR(IP17_31_28, TPU0TO1),
+
+ /* IPSR18 */
+ PINMUX_IPSR_GPSR(IP18_3_0, GP6_30),
+ PINMUX_IPSR_GPSR(IP18_3_0, AUDIO_CLKOUT2_B),
+ PINMUX_IPSR_MSEL(IP18_3_0, SSI_SCK9_B, SEL_SSI9_1),
+ PINMUX_IPSR_MSEL(IP18_3_0, TS_SDEN0_E, SEL_TSIF0_4),
+ PINMUX_IPSR_MSEL(IP18_3_0, STP_ISEN_0_E, SEL_SSP1_0_4),
+ PINMUX_IPSR_MSEL(IP18_3_0, RIF2_D0_B, SEL_DRIF2_1),
+ PINMUX_IPSR_GPSR(IP18_3_0, TPU0TO2),
+ PINMUX_IPSR_MSEL(IP18_3_0, FMCLK_C, SEL_FM_2),
+ PINMUX_IPSR_MSEL(IP18_3_0, FMCLK_D, SEL_FM_3),
+
+ PINMUX_IPSR_GPSR(IP18_7_4, GP6_31),
+ PINMUX_IPSR_GPSR(IP18_7_4, AUDIO_CLKOUT3_B),
+ PINMUX_IPSR_MSEL(IP18_7_4, SSI_WS9_B, SEL_SSI9_1),
+ PINMUX_IPSR_MSEL(IP18_7_4, TS_SPSYNC0_E, SEL_TSIF0_4),
+ PINMUX_IPSR_MSEL(IP18_7_4, STP_ISSYNC_0_E, SEL_SSP1_0_4),
+ PINMUX_IPSR_MSEL(IP18_7_4, RIF2_D1_B, SEL_DRIF2_1),
+ PINMUX_IPSR_GPSR(IP18_7_4, TPU0TO3),
+ PINMUX_IPSR_MSEL(IP18_7_4, FMIN_C, SEL_FM_2),
+ PINMUX_IPSR_MSEL(IP18_7_4, FMIN_D, SEL_FM_3),
+
+ /* I2C */
+ PINMUX_IPSR_NOGP(0, I2C_SEL_0_1),
+ PINMUX_IPSR_NOGP(0, I2C_SEL_3_1),
+ PINMUX_IPSR_NOGP(0, I2C_SEL_5_1),
+
+/*
+ * Static pins can not be muxed between different functions but
+ * still needs a mark entry in the pinmux list. Add each static
+ * pin to the list without an associated function. The sh-pfc
+ * core will do the right thing and skip trying to mux then pin
+ * while still applying configuration to it
+ */
+#define FM(x) PINMUX_DATA(x##_MARK, 0),
+ PINMUX_STATIC
+#undef FM
+};
+
+/*
+ * R8A77965 has 8 banks with 32 GPIOs in each => 256 GPIOs.
+ * Physical layout rows: A - AW, cols: 1 - 39.
+ */
+#define ROW_GROUP_A(r) ('Z' - 'A' + 1 + (r))
+#define PIN_NUMBER(r, c) (((r) - 'A') * 39 + (c) + 300)
+#define PIN_A_NUMBER(r, c) PIN_NUMBER(ROW_GROUP_A(r), c)
+#define PIN_NONE U16_MAX
+
+static const struct sh_pfc_pin pinmux_pins[] = {
+ PINMUX_GPIO_GP_ALL(),
+
+ /*
+ * Pins not associated with a GPIO port.
+ *
+ * The pin positions are different between different r8a77965
+ * packages, all that is needed for the pfc driver is a unique
+ * number for each pin. To this end use the pin layout from
+ * R-Car M3SiP to calculate a unique number for each pin.
+ */
+ SH_PFC_PIN_NAMED_CFG('A', 8, AVB_TX_CTL, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG('A', 9, AVB_MDIO, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG('A', 12, AVB_TXCREFCLK, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG('A', 13, AVB_RD0, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG('A', 14, AVB_RD2, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG('A', 16, AVB_RX_CTL, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG('A', 17, AVB_TD2, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG('A', 18, AVB_TD0, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG('A', 19, AVB_TXC, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG('B', 13, AVB_RD1, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG('B', 14, AVB_RD3, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG('B', 17, AVB_TD3, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG('B', 18, AVB_TD1, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG('B', 19, AVB_RXC, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG('C', 1, PRESETOUT#, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG('H', 37, MLB_REF, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG('V', 3, QSPI1_SPCLK, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG('V', 5, QSPI1_SSL, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG('V', 6, RPC_WP#, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG('V', 7, RPC_RESET#, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG('W', 3, QSPI0_SPCLK, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG('Y', 3, QSPI0_SSL, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG('Y', 6, QSPI0_IO2, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG('Y', 7, RPC_INT#, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('B'), 4, QSPI0_MISO_IO1, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('B'), 6, QSPI0_IO3, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('C'), 3, QSPI1_IO3, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('C'), 5, QSPI0_MOSI_IO0, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('C'), 7, QSPI1_MOSI_IO0, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('D'), 38, FSCLKST, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('D'), 39, EXTALR, SH_PFC_PIN_CFG_PULL_UP | SH_PFC_PIN_CFG_PULL_DOWN),
+ SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('E'), 4, QSPI1_IO2, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('E'), 5, QSPI1_MISO_IO1, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('P'), 7, DU_DOTCLKIN0, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('P'), 8, DU_DOTCLKIN1, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('R'), 8, DU_DOTCLKIN2, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('R'), 26, TRST#, SH_PFC_PIN_CFG_PULL_UP | SH_PFC_PIN_CFG_PULL_DOWN),
+ SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('R'), 29, TDI, SH_PFC_PIN_CFG_PULL_UP | SH_PFC_PIN_CFG_PULL_DOWN),
+ SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('R'), 30, TMS, CFG_FLAGS),
+ SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('T'), 27, TCK, SH_PFC_PIN_CFG_PULL_UP | SH_PFC_PIN_CFG_PULL_DOWN),
+ SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('T'), 28, TDO, SH_PFC_PIN_CFG_DRIVE_STRENGTH),
+ SH_PFC_PIN_NAMED_CFG(ROW_GROUP_A('T'), 30, ASEBRK, CFG_FLAGS),
+};
+
+/* - EtherAVB --------------------------------------------------------------- */
+static const unsigned int avb_link_pins[] = {
+ /* AVB_LINK */
+ RCAR_GP_PIN(2, 12),
+};
+static const unsigned int avb_link_mux[] = {
+ AVB_LINK_MARK,
+};
+static const unsigned int avb_magic_pins[] = {
+ /* AVB_MAGIC_ */
+ RCAR_GP_PIN(2, 10),
+};
+static const unsigned int avb_magic_mux[] = {
+ AVB_MAGIC_MARK,
+};
+static const unsigned int avb_phy_int_pins[] = {
+ /* AVB_PHY_INT */
+ RCAR_GP_PIN(2, 11),
+};
+static const unsigned int avb_phy_int_mux[] = {
+ AVB_PHY_INT_MARK,
+};
+static const unsigned int avb_mdio_pins[] = {
+ /* AVB_MDC, AVB_MDIO */
+ RCAR_GP_PIN(2, 9), PIN_NUMBER('A', 9),
+};
+static const unsigned int avb_mdio_mux[] = {
+ AVB_MDC_MARK, AVB_MDIO_MARK,
+};
+static const unsigned int avb_mii_pins[] = {
+ /*
+ * AVB_TX_CTL, AVB_TXC, AVB_TD0,
+ * AVB_TD1, AVB_TD2, AVB_TD3,
+ * AVB_RX_CTL, AVB_RXC, AVB_RD0,
+ * AVB_RD1, AVB_RD2, AVB_RD3,
+ * AVB_TXCREFCLK
+ */
+ PIN_NUMBER('A', 8), PIN_NUMBER('A', 19), PIN_NUMBER('A', 18),
+ PIN_NUMBER('B', 18), PIN_NUMBER('A', 17), PIN_NUMBER('B', 17),
+ PIN_NUMBER('A', 16), PIN_NUMBER('B', 19), PIN_NUMBER('A', 13),
+ PIN_NUMBER('B', 13), PIN_NUMBER('A', 14), PIN_NUMBER('B', 14),
+ PIN_NUMBER('A', 12),
+
+};
+static const unsigned int avb_mii_mux[] = {
+ AVB_TX_CTL_MARK, AVB_TXC_MARK, AVB_TD0_MARK,
+ AVB_TD1_MARK, AVB_TD2_MARK, AVB_TD3_MARK,
+ AVB_RX_CTL_MARK, AVB_RXC_MARK, AVB_RD0_MARK,
+ AVB_RD1_MARK, AVB_RD2_MARK, AVB_RD3_MARK,
+ AVB_TXCREFCLK_MARK,
+};
+static const unsigned int avb_avtp_pps_pins[] = {
+ /* AVB_AVTP_PPS */
+ RCAR_GP_PIN(2, 6),
+};
+static const unsigned int avb_avtp_pps_mux[] = {
+ AVB_AVTP_PPS_MARK,
+};
+static const unsigned int avb_avtp_match_a_pins[] = {
+ /* AVB_AVTP_MATCH_A */
+ RCAR_GP_PIN(2, 13),
+};
+static const unsigned int avb_avtp_match_a_mux[] = {
+ AVB_AVTP_MATCH_A_MARK,
+};
+static const unsigned int avb_avtp_capture_a_pins[] = {
+ /* AVB_AVTP_CAPTURE_A */
+ RCAR_GP_PIN(2, 14),
+};
+static const unsigned int avb_avtp_capture_a_mux[] = {
+ AVB_AVTP_CAPTURE_A_MARK,
+};
+static const unsigned int avb_avtp_match_b_pins[] = {
+ /* AVB_AVTP_MATCH_B */
+ RCAR_GP_PIN(1, 8),
+};
+static const unsigned int avb_avtp_match_b_mux[] = {
+ AVB_AVTP_MATCH_B_MARK,
+};
+static const unsigned int avb_avtp_capture_b_pins[] = {
+ /* AVB_AVTP_CAPTURE_B */
+ RCAR_GP_PIN(1, 11),
+};
+static const unsigned int avb_avtp_capture_b_mux[] = {
+ AVB_AVTP_CAPTURE_B_MARK,
+};
+
+/* - INTC-EX ---------------------------------------------------------------- */
+static const unsigned int intc_ex_irq0_pins[] = {
+ /* IRQ0 */
+ RCAR_GP_PIN(2, 0),
+};
+static const unsigned int intc_ex_irq0_mux[] = {
+ IRQ0_MARK,
+};
+static const unsigned int intc_ex_irq1_pins[] = {
+ /* IRQ1 */
+ RCAR_GP_PIN(2, 1),
+};
+static const unsigned int intc_ex_irq1_mux[] = {
+ IRQ1_MARK,
+};
+static const unsigned int intc_ex_irq2_pins[] = {
+ /* IRQ2 */
+ RCAR_GP_PIN(2, 2),
+};
+static const unsigned int intc_ex_irq2_mux[] = {
+ IRQ2_MARK,
+};
+static const unsigned int intc_ex_irq3_pins[] = {
+ /* IRQ3 */
+ RCAR_GP_PIN(2, 3),
+};
+static const unsigned int intc_ex_irq3_mux[] = {
+ IRQ3_MARK,
+};
+static const unsigned int intc_ex_irq4_pins[] = {
+ /* IRQ4 */
+ RCAR_GP_PIN(2, 4),
+};
+static const unsigned int intc_ex_irq4_mux[] = {
+ IRQ4_MARK,
+};
+static const unsigned int intc_ex_irq5_pins[] = {
+ /* IRQ5 */
+ RCAR_GP_PIN(2, 5),
+};
+static const unsigned int intc_ex_irq5_mux[] = {
+ IRQ5_MARK,
+};
+
+/* - SCIF0 ------------------------------------------------------------------ */
+static const unsigned int scif0_data_pins[] = {
+ /* RX, TX */
+ RCAR_GP_PIN(5, 1), RCAR_GP_PIN(5, 2),
+};
+static const unsigned int scif0_data_mux[] = {
+ RX0_MARK, TX0_MARK,
+};
+static const unsigned int scif0_clk_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(5, 0),
+};
+static const unsigned int scif0_clk_mux[] = {
+ SCK0_MARK,
+};
+static const unsigned int scif0_ctrl_pins[] = {
+ /* RTS, CTS */
+ RCAR_GP_PIN(5, 4), RCAR_GP_PIN(5, 3),
+};
+static const unsigned int scif0_ctrl_mux[] = {
+ RTS0_N_MARK, CTS0_N_MARK,
+};
+/* - SCIF1 ------------------------------------------------------------------ */
+static const unsigned int scif1_data_a_pins[] = {
+ /* RX, TX */
+ RCAR_GP_PIN(5, 5), RCAR_GP_PIN(5, 6),
+};
+static const unsigned int scif1_data_a_mux[] = {
+ RX1_A_MARK, TX1_A_MARK,
+};
+static const unsigned int scif1_clk_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(6, 21),
+};
+static const unsigned int scif1_clk_mux[] = {
+ SCK1_MARK,
+};
+static const unsigned int scif1_ctrl_pins[] = {
+ /* RTS, CTS */
+ RCAR_GP_PIN(5, 8), RCAR_GP_PIN(5, 7),
+};
+static const unsigned int scif1_ctrl_mux[] = {
+ RTS1_N_MARK, CTS1_N_MARK,
+};
+static const unsigned int scif1_data_b_pins[] = {
+ /* RX, TX */
+ RCAR_GP_PIN(5, 24), RCAR_GP_PIN(5, 25),
+};
+static const unsigned int scif1_data_b_mux[] = {
+ RX1_B_MARK, TX1_B_MARK,
+};
+/* - SCIF2 ------------------------------------------------------------------ */
+static const unsigned int scif2_data_a_pins[] = {
+ /* RX, TX */
+ RCAR_GP_PIN(5, 11), RCAR_GP_PIN(5, 10),
+};
+static const unsigned int scif2_data_a_mux[] = {
+ RX2_A_MARK, TX2_A_MARK,
+};
+static const unsigned int scif2_clk_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(5, 9),
+};
+static const unsigned int scif2_clk_mux[] = {
+ SCK2_MARK,
+};
+static const unsigned int scif2_data_b_pins[] = {
+ /* RX, TX */
+ RCAR_GP_PIN(5, 15), RCAR_GP_PIN(5, 16),
+};
+static const unsigned int scif2_data_b_mux[] = {
+ RX2_B_MARK, TX2_B_MARK,
+};
+/* - SCIF3 ------------------------------------------------------------------ */
+static const unsigned int scif3_data_a_pins[] = {
+ /* RX, TX */
+ RCAR_GP_PIN(1, 23), RCAR_GP_PIN(1, 24),
+};
+static const unsigned int scif3_data_a_mux[] = {
+ RX3_A_MARK, TX3_A_MARK,
+};
+static const unsigned int scif3_clk_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(1, 22),
+};
+static const unsigned int scif3_clk_mux[] = {
+ SCK3_MARK,
+};
+static const unsigned int scif3_ctrl_pins[] = {
+ /* RTS, CTS */
+ RCAR_GP_PIN(1, 26), RCAR_GP_PIN(1, 25),
+};
+static const unsigned int scif3_ctrl_mux[] = {
+ RTS3_N_MARK, CTS3_N_MARK,
+};
+static const unsigned int scif3_data_b_pins[] = {
+ /* RX, TX */
+ RCAR_GP_PIN(1, 8), RCAR_GP_PIN(1, 11),
+};
+static const unsigned int scif3_data_b_mux[] = {
+ RX3_B_MARK, TX3_B_MARK,
+};
+/* - SCIF4 ------------------------------------------------------------------ */
+static const unsigned int scif4_data_a_pins[] = {
+ /* RX, TX */
+ RCAR_GP_PIN(2, 11), RCAR_GP_PIN(2, 12),
+};
+static const unsigned int scif4_data_a_mux[] = {
+ RX4_A_MARK, TX4_A_MARK,
+};
+static const unsigned int scif4_clk_a_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(2, 10),
+};
+static const unsigned int scif4_clk_a_mux[] = {
+ SCK4_A_MARK,
+};
+static const unsigned int scif4_ctrl_a_pins[] = {
+ /* RTS, CTS */
+ RCAR_GP_PIN(2, 14), RCAR_GP_PIN(2, 13),
+};
+static const unsigned int scif4_ctrl_a_mux[] = {
+ RTS4_N_A_MARK, CTS4_N_A_MARK,
+};
+static const unsigned int scif4_data_b_pins[] = {
+ /* RX, TX */
+ RCAR_GP_PIN(1, 6), RCAR_GP_PIN(1, 7),
+};
+static const unsigned int scif4_data_b_mux[] = {
+ RX4_B_MARK, TX4_B_MARK,
+};
+static const unsigned int scif4_clk_b_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(1, 5),
+};
+static const unsigned int scif4_clk_b_mux[] = {
+ SCK4_B_MARK,
+};
+static const unsigned int scif4_ctrl_b_pins[] = {
+ /* RTS, CTS */
+ RCAR_GP_PIN(1, 10), RCAR_GP_PIN(1, 9),
+};
+static const unsigned int scif4_ctrl_b_mux[] = {
+ RTS4_N_B_MARK, CTS4_N_B_MARK,
+};
+static const unsigned int scif4_data_c_pins[] = {
+ /* RX, TX */
+ RCAR_GP_PIN(0, 12), RCAR_GP_PIN(0, 13),
+};
+static const unsigned int scif4_data_c_mux[] = {
+ RX4_C_MARK, TX4_C_MARK,
+};
+static const unsigned int scif4_clk_c_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(0, 8),
+};
+static const unsigned int scif4_clk_c_mux[] = {
+ SCK4_C_MARK,
+};
+static const unsigned int scif4_ctrl_c_pins[] = {
+ /* RTS, CTS */
+ RCAR_GP_PIN(0, 11), RCAR_GP_PIN(0, 10),
+};
+static const unsigned int scif4_ctrl_c_mux[] = {
+ RTS4_N_C_MARK, CTS4_N_C_MARK,
+};
+/* - SCIF5 ------------------------------------------------------------------ */
+static const unsigned int scif5_data_a_pins[] = {
+ /* RX, TX */
+ RCAR_GP_PIN(5, 19), RCAR_GP_PIN(5, 21),
+};
+static const unsigned int scif5_data_a_mux[] = {
+ RX5_A_MARK, TX5_A_MARK,
+};
+static const unsigned int scif5_clk_a_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(6, 21),
+};
+static const unsigned int scif5_clk_a_mux[] = {
+ SCK5_A_MARK,
+};
+static const unsigned int scif5_data_b_pins[] = {
+ /* RX, TX */
+ RCAR_GP_PIN(5, 12), RCAR_GP_PIN(5, 18),
+};
+static const unsigned int scif5_data_b_mux[] = {
+ RX5_B_MARK, TX5_B_MARK,
+};
+static const unsigned int scif5_clk_b_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(5, 0),
+};
+static const unsigned int scif5_clk_b_mux[] = {
+ SCK5_B_MARK,
+};
+/* - SCIF Clock ------------------------------------------------------------- */
+static const unsigned int scif_clk_a_pins[] = {
+ /* SCIF_CLK */
+ RCAR_GP_PIN(6, 23),
+};
+static const unsigned int scif_clk_a_mux[] = {
+ SCIF_CLK_A_MARK,
+};
+static const unsigned int scif_clk_b_pins[] = {
+ /* SCIF_CLK */
+ RCAR_GP_PIN(5, 9),
+};
+static const unsigned int scif_clk_b_mux[] = {
+ SCIF_CLK_B_MARK,
+};
+
+/* - USB0 ------------------------------------------------------------------- */
+static const unsigned int usb0_pins[] = {
+ /* PWEN, OVC */
+ RCAR_GP_PIN(6, 24), RCAR_GP_PIN(6, 25),
+};
+
+static const unsigned int usb0_mux[] = {
+ USB0_PWEN_MARK, USB0_OVC_MARK,
+};
+
+/* - USB1 ------------------------------------------------------------------- */
+static const unsigned int usb1_pins[] = {
+ /* PWEN, OVC */
+ RCAR_GP_PIN(6, 26), RCAR_GP_PIN(6, 27),
+};
+
+static const unsigned int usb1_mux[] = {
+ USB1_PWEN_MARK, USB1_OVC_MARK,
+};
+
+/* - USB30 ------------------------------------------------------------------ */
+static const unsigned int usb30_pins[] = {
+ /* PWEN, OVC */
+ RCAR_GP_PIN(6, 28), RCAR_GP_PIN(6, 29),
+};
+
+static const unsigned int usb30_mux[] = {
+ USB30_PWEN_MARK, USB30_OVC_MARK,
+};
+
+static const struct sh_pfc_pin_group pinmux_groups[] = {
+ SH_PFC_PIN_GROUP(avb_link),
+ SH_PFC_PIN_GROUP(avb_magic),
+ SH_PFC_PIN_GROUP(avb_phy_int),
+ SH_PFC_PIN_GROUP_ALIAS(avb_mdc, avb_mdio), /* Deprecated */
+ SH_PFC_PIN_GROUP(avb_mdio),
+ SH_PFC_PIN_GROUP(avb_mii),
+ SH_PFC_PIN_GROUP(avb_avtp_pps),
+ SH_PFC_PIN_GROUP(avb_avtp_match_a),
+ SH_PFC_PIN_GROUP(avb_avtp_capture_a),
+ SH_PFC_PIN_GROUP(avb_avtp_match_b),
+ SH_PFC_PIN_GROUP(avb_avtp_capture_b),
+ SH_PFC_PIN_GROUP(intc_ex_irq0),
+ SH_PFC_PIN_GROUP(intc_ex_irq1),
+ SH_PFC_PIN_GROUP(intc_ex_irq2),
+ SH_PFC_PIN_GROUP(intc_ex_irq3),
+ SH_PFC_PIN_GROUP(intc_ex_irq4),
+ SH_PFC_PIN_GROUP(intc_ex_irq5),
+ SH_PFC_PIN_GROUP(scif0_data),
+ SH_PFC_PIN_GROUP(scif0_clk),
+ SH_PFC_PIN_GROUP(scif0_ctrl),
+ SH_PFC_PIN_GROUP(scif1_data_a),
+ SH_PFC_PIN_GROUP(scif1_clk),
+ SH_PFC_PIN_GROUP(scif1_ctrl),
+ SH_PFC_PIN_GROUP(scif1_data_b),
+ SH_PFC_PIN_GROUP(scif2_data_a),
+ SH_PFC_PIN_GROUP(scif2_clk),
+ SH_PFC_PIN_GROUP(scif2_data_b),
+ SH_PFC_PIN_GROUP(scif3_data_a),
+ SH_PFC_PIN_GROUP(scif3_clk),
+ SH_PFC_PIN_GROUP(scif3_ctrl),
+ SH_PFC_PIN_GROUP(scif3_data_b),
+ SH_PFC_PIN_GROUP(scif4_data_a),
+ SH_PFC_PIN_GROUP(scif4_clk_a),
+ SH_PFC_PIN_GROUP(scif4_ctrl_a),
+ SH_PFC_PIN_GROUP(scif4_data_b),
+ SH_PFC_PIN_GROUP(scif4_clk_b),
+ SH_PFC_PIN_GROUP(scif4_ctrl_b),
+ SH_PFC_PIN_GROUP(scif4_data_c),
+ SH_PFC_PIN_GROUP(scif4_clk_c),
+ SH_PFC_PIN_GROUP(scif4_ctrl_c),
+ SH_PFC_PIN_GROUP(scif5_data_a),
+ SH_PFC_PIN_GROUP(scif5_clk_a),
+ SH_PFC_PIN_GROUP(scif5_data_b),
+ SH_PFC_PIN_GROUP(scif5_clk_b),
+ SH_PFC_PIN_GROUP(scif_clk_a),
+ SH_PFC_PIN_GROUP(scif_clk_b),
+ SH_PFC_PIN_GROUP(usb0),
+ SH_PFC_PIN_GROUP(usb1),
+ SH_PFC_PIN_GROUP(usb30),
+};
+
+static const char * const avb_groups[] = {
+ "avb_link",
+ "avb_magic",
+ "avb_phy_int",
+ "avb_mdc", /* Deprecated, please use "avb_mdio" instead */
+ "avb_mdio",
+ "avb_mii",
+ "avb_avtp_pps",
+ "avb_avtp_match_a",
+ "avb_avtp_capture_a",
+ "avb_avtp_match_b",
+ "avb_avtp_capture_b",
+};
+
+static const char * const intc_ex_groups[] = {
+ "intc_ex_irq0",
+ "intc_ex_irq1",
+ "intc_ex_irq2",
+ "intc_ex_irq3",
+ "intc_ex_irq4",
+ "intc_ex_irq5",
+};
+
+static const char * const scif0_groups[] = {
+ "scif0_data",
+ "scif0_clk",
+ "scif0_ctrl",
+};
+
+static const char * const scif1_groups[] = {
+ "scif1_data_a",
+ "scif1_clk",
+ "scif1_ctrl",
+ "scif1_data_b",
+};
+static const char * const scif2_groups[] = {
+ "scif2_data_a",
+ "scif2_clk",
+ "scif2_data_b",
+};
+
+static const char * const scif3_groups[] = {
+ "scif3_data_a",
+ "scif3_clk",
+ "scif3_ctrl",
+ "scif3_data_b",
+};
+
+static const char * const scif4_groups[] = {
+ "scif4_data_a",
+ "scif4_clk_a",
+ "scif4_ctrl_a",
+ "scif4_data_b",
+ "scif4_clk_b",
+ "scif4_ctrl_b",
+ "scif4_data_c",
+ "scif4_clk_c",
+ "scif4_ctrl_c",
+};
+
+static const char * const scif5_groups[] = {
+ "scif5_data_a",
+ "scif5_clk_a",
+ "scif5_data_b",
+ "scif5_clk_b",
+};
+
+static const char * const scif_clk_groups[] = {
+ "scif_clk_a",
+ "scif_clk_b",
+};
+
+static const char * const usb0_groups[] = {
+ "usb0",
+};
+
+static const char * const usb1_groups[] = {
+ "usb1",
+};
+
+static const char * const usb30_groups[] = {
+ "usb30",
+};
+
+static const struct sh_pfc_function pinmux_functions[] = {
+ SH_PFC_FUNCTION(avb),
+ SH_PFC_FUNCTION(intc_ex),
+ SH_PFC_FUNCTION(scif0),
+ SH_PFC_FUNCTION(scif1),
+ SH_PFC_FUNCTION(scif2),
+ SH_PFC_FUNCTION(scif3),
+ SH_PFC_FUNCTION(scif4),
+ SH_PFC_FUNCTION(scif5),
+ SH_PFC_FUNCTION(scif_clk),
+ SH_PFC_FUNCTION(usb0),
+ SH_PFC_FUNCTION(usb1),
+ SH_PFC_FUNCTION(usb30),
+};
+
+static const struct pinmux_cfg_reg pinmux_config_regs[] = {
+#define F_(x, y) FN_##y
+#define FM(x) FN_##x
+ { PINMUX_CFG_REG("GPSR0", 0xe6060100, 32, 1) {
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ GP_0_15_FN, GPSR0_15,
+ GP_0_14_FN, GPSR0_14,
+ GP_0_13_FN, GPSR0_13,
+ GP_0_12_FN, GPSR0_12,
+ GP_0_11_FN, GPSR0_11,
+ GP_0_10_FN, GPSR0_10,
+ GP_0_9_FN, GPSR0_9,
+ GP_0_8_FN, GPSR0_8,
+ GP_0_7_FN, GPSR0_7,
+ GP_0_6_FN, GPSR0_6,
+ GP_0_5_FN, GPSR0_5,
+ GP_0_4_FN, GPSR0_4,
+ GP_0_3_FN, GPSR0_3,
+ GP_0_2_FN, GPSR0_2,
+ GP_0_1_FN, GPSR0_1,
+ GP_0_0_FN, GPSR0_0, }
+ },
+ { PINMUX_CFG_REG("GPSR1", 0xe6060104, 32, 1) {
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ GP_1_28_FN, GPSR1_28,
+ GP_1_27_FN, GPSR1_27,
+ GP_1_26_FN, GPSR1_26,
+ GP_1_25_FN, GPSR1_25,
+ GP_1_24_FN, GPSR1_24,
+ GP_1_23_FN, GPSR1_23,
+ GP_1_22_FN, GPSR1_22,
+ GP_1_21_FN, GPSR1_21,
+ GP_1_20_FN, GPSR1_20,
+ GP_1_19_FN, GPSR1_19,
+ GP_1_18_FN, GPSR1_18,
+ GP_1_17_FN, GPSR1_17,
+ GP_1_16_FN, GPSR1_16,
+ GP_1_15_FN, GPSR1_15,
+ GP_1_14_FN, GPSR1_14,
+ GP_1_13_FN, GPSR1_13,
+ GP_1_12_FN, GPSR1_12,
+ GP_1_11_FN, GPSR1_11,
+ GP_1_10_FN, GPSR1_10,
+ GP_1_9_FN, GPSR1_9,
+ GP_1_8_FN, GPSR1_8,
+ GP_1_7_FN, GPSR1_7,
+ GP_1_6_FN, GPSR1_6,
+ GP_1_5_FN, GPSR1_5,
+ GP_1_4_FN, GPSR1_4,
+ GP_1_3_FN, GPSR1_3,
+ GP_1_2_FN, GPSR1_2,
+ GP_1_1_FN, GPSR1_1,
+ GP_1_0_FN, GPSR1_0, }
+ },
+ { PINMUX_CFG_REG("GPSR2", 0xe6060108, 32, 1) {
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ GP_2_14_FN, GPSR2_14,
+ GP_2_13_FN, GPSR2_13,
+ GP_2_12_FN, GPSR2_12,
+ GP_2_11_FN, GPSR2_11,
+ GP_2_10_FN, GPSR2_10,
+ GP_2_9_FN, GPSR2_9,
+ GP_2_8_FN, GPSR2_8,
+ GP_2_7_FN, GPSR2_7,
+ GP_2_6_FN, GPSR2_6,
+ GP_2_5_FN, GPSR2_5,
+ GP_2_4_FN, GPSR2_4,
+ GP_2_3_FN, GPSR2_3,
+ GP_2_2_FN, GPSR2_2,
+ GP_2_1_FN, GPSR2_1,
+ GP_2_0_FN, GPSR2_0, }
+ },
+ { PINMUX_CFG_REG("GPSR3", 0xe606010c, 32, 1) {
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ GP_3_15_FN, GPSR3_15,
+ GP_3_14_FN, GPSR3_14,
+ GP_3_13_FN, GPSR3_13,
+ GP_3_12_FN, GPSR3_12,
+ GP_3_11_FN, GPSR3_11,
+ GP_3_10_FN, GPSR3_10,
+ GP_3_9_FN, GPSR3_9,
+ GP_3_8_FN, GPSR3_8,
+ GP_3_7_FN, GPSR3_7,
+ GP_3_6_FN, GPSR3_6,
+ GP_3_5_FN, GPSR3_5,
+ GP_3_4_FN, GPSR3_4,
+ GP_3_3_FN, GPSR3_3,
+ GP_3_2_FN, GPSR3_2,
+ GP_3_1_FN, GPSR3_1,
+ GP_3_0_FN, GPSR3_0, }
+ },
+ { PINMUX_CFG_REG("GPSR4", 0xe6060110, 32, 1) {
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ GP_4_17_FN, GPSR4_17,
+ GP_4_16_FN, GPSR4_16,
+ GP_4_15_FN, GPSR4_15,
+ GP_4_14_FN, GPSR4_14,
+ GP_4_13_FN, GPSR4_13,
+ GP_4_12_FN, GPSR4_12,
+ GP_4_11_FN, GPSR4_11,
+ GP_4_10_FN, GPSR4_10,
+ GP_4_9_FN, GPSR4_9,
+ GP_4_8_FN, GPSR4_8,
+ GP_4_7_FN, GPSR4_7,
+ GP_4_6_FN, GPSR4_6,
+ GP_4_5_FN, GPSR4_5,
+ GP_4_4_FN, GPSR4_4,
+ GP_4_3_FN, GPSR4_3,
+ GP_4_2_FN, GPSR4_2,
+ GP_4_1_FN, GPSR4_1,
+ GP_4_0_FN, GPSR4_0, }
+ },
+ { PINMUX_CFG_REG("GPSR5", 0xe6060114, 32, 1) {
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ GP_5_25_FN, GPSR5_25,
+ GP_5_24_FN, GPSR5_24,
+ GP_5_23_FN, GPSR5_23,
+ GP_5_22_FN, GPSR5_22,
+ GP_5_21_FN, GPSR5_21,
+ GP_5_20_FN, GPSR5_20,
+ GP_5_19_FN, GPSR5_19,
+ GP_5_18_FN, GPSR5_18,
+ GP_5_17_FN, GPSR5_17,
+ GP_5_16_FN, GPSR5_16,
+ GP_5_15_FN, GPSR5_15,
+ GP_5_14_FN, GPSR5_14,
+ GP_5_13_FN, GPSR5_13,
+ GP_5_12_FN, GPSR5_12,
+ GP_5_11_FN, GPSR5_11,
+ GP_5_10_FN, GPSR5_10,
+ GP_5_9_FN, GPSR5_9,
+ GP_5_8_FN, GPSR5_8,
+ GP_5_7_FN, GPSR5_7,
+ GP_5_6_FN, GPSR5_6,
+ GP_5_5_FN, GPSR5_5,
+ GP_5_4_FN, GPSR5_4,
+ GP_5_3_FN, GPSR5_3,
+ GP_5_2_FN, GPSR5_2,
+ GP_5_1_FN, GPSR5_1,
+ GP_5_0_FN, GPSR5_0, }
+ },
+ { PINMUX_CFG_REG("GPSR6", 0xe6060118, 32, 1) {
+ GP_6_31_FN, GPSR6_31,
+ GP_6_30_FN, GPSR6_30,
+ GP_6_29_FN, GPSR6_29,
+ GP_6_28_FN, GPSR6_28,
+ GP_6_27_FN, GPSR6_27,
+ GP_6_26_FN, GPSR6_26,
+ GP_6_25_FN, GPSR6_25,
+ GP_6_24_FN, GPSR6_24,
+ GP_6_23_FN, GPSR6_23,
+ GP_6_22_FN, GPSR6_22,
+ GP_6_21_FN, GPSR6_21,
+ GP_6_20_FN, GPSR6_20,
+ GP_6_19_FN, GPSR6_19,
+ GP_6_18_FN, GPSR6_18,
+ GP_6_17_FN, GPSR6_17,
+ GP_6_16_FN, GPSR6_16,
+ GP_6_15_FN, GPSR6_15,
+ GP_6_14_FN, GPSR6_14,
+ GP_6_13_FN, GPSR6_13,
+ GP_6_12_FN, GPSR6_12,
+ GP_6_11_FN, GPSR6_11,
+ GP_6_10_FN, GPSR6_10,
+ GP_6_9_FN, GPSR6_9,
+ GP_6_8_FN, GPSR6_8,
+ GP_6_7_FN, GPSR6_7,
+ GP_6_6_FN, GPSR6_6,
+ GP_6_5_FN, GPSR6_5,
+ GP_6_4_FN, GPSR6_4,
+ GP_6_3_FN, GPSR6_3,
+ GP_6_2_FN, GPSR6_2,
+ GP_6_1_FN, GPSR6_1,
+ GP_6_0_FN, GPSR6_0, }
+ },
+ { PINMUX_CFG_REG("GPSR7", 0xe606011c, 32, 1) {
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ GP_7_3_FN, GPSR7_3,
+ GP_7_2_FN, GPSR7_2,
+ GP_7_1_FN, GPSR7_1,
+ GP_7_0_FN, GPSR7_0, }
+ },
+#undef F_
+#undef FM
+
+#define F_(x, y) x,
+#define FM(x) FN_##x,
+ { PINMUX_CFG_REG("IPSR0", 0xe6060200, 32, 4) {
+ IP0_31_28
+ IP0_27_24
+ IP0_23_20
+ IP0_19_16
+ IP0_15_12
+ IP0_11_8
+ IP0_7_4
+ IP0_3_0 }
+ },
+ { PINMUX_CFG_REG("IPSR1", 0xe6060204, 32, 4) {
+ IP1_31_28
+ IP1_27_24
+ IP1_23_20
+ IP1_19_16
+ IP1_15_12
+ IP1_11_8
+ IP1_7_4
+ IP1_3_0 }
+ },
+ { PINMUX_CFG_REG("IPSR2", 0xe6060208, 32, 4) {
+ IP2_31_28
+ IP2_27_24
+ IP2_23_20
+ IP2_19_16
+ IP2_15_12
+ IP2_11_8
+ IP2_7_4
+ IP2_3_0 }
+ },
+ { PINMUX_CFG_REG("IPSR3", 0xe606020c, 32, 4) {
+ IP3_31_28
+ IP3_27_24
+ IP3_23_20
+ IP3_19_16
+ IP3_15_12
+ IP3_11_8
+ IP3_7_4
+ IP3_3_0 }
+ },
+ { PINMUX_CFG_REG("IPSR4", 0xe6060210, 32, 4) {
+ IP4_31_28
+ IP4_27_24
+ IP4_23_20
+ IP4_19_16
+ IP4_15_12
+ IP4_11_8
+ IP4_7_4
+ IP4_3_0 }
+ },
+ { PINMUX_CFG_REG("IPSR5", 0xe6060214, 32, 4) {
+ IP5_31_28
+ IP5_27_24
+ IP5_23_20
+ IP5_19_16
+ IP5_15_12
+ IP5_11_8
+ IP5_7_4
+ IP5_3_0 }
+ },
+ { PINMUX_CFG_REG("IPSR6", 0xe6060218, 32, 4) {
+ IP6_31_28
+ IP6_27_24
+ IP6_23_20
+ IP6_19_16
+ IP6_15_12
+ IP6_11_8
+ IP6_7_4
+ IP6_3_0 }
+ },
+ { PINMUX_CFG_REG("IPSR7", 0xe606021c, 32, 4) {
+ IP7_31_28
+ IP7_27_24
+ IP7_23_20
+ IP7_19_16
+ /* IP7_15_12 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ IP7_11_8
+ IP7_7_4
+ IP7_3_0 }
+ },
+ { PINMUX_CFG_REG("IPSR8", 0xe6060220, 32, 4) {
+ IP8_31_28
+ IP8_27_24
+ IP8_23_20
+ IP8_19_16
+ IP8_15_12
+ IP8_11_8
+ IP8_7_4
+ IP8_3_0 }
+ },
+ { PINMUX_CFG_REG("IPSR9", 0xe6060224, 32, 4) {
+ IP9_31_28
+ IP9_27_24
+ IP9_23_20
+ IP9_19_16
+ IP9_15_12
+ IP9_11_8
+ IP9_7_4
+ IP9_3_0 }
+ },
+ { PINMUX_CFG_REG("IPSR10", 0xe6060228, 32, 4) {
+ IP10_31_28
+ IP10_27_24
+ IP10_23_20
+ IP10_19_16
+ IP10_15_12
+ IP10_11_8
+ IP10_7_4
+ IP10_3_0 }
+ },
+ { PINMUX_CFG_REG("IPSR11", 0xe606022c, 32, 4) {
+ IP11_31_28
+ IP11_27_24
+ IP11_23_20
+ IP11_19_16
+ IP11_15_12
+ IP11_11_8
+ IP11_7_4
+ IP11_3_0 }
+ },
+ { PINMUX_CFG_REG("IPSR12", 0xe6060230, 32, 4) {
+ IP12_31_28
+ IP12_27_24
+ IP12_23_20
+ IP12_19_16
+ IP12_15_12
+ IP12_11_8
+ IP12_7_4
+ IP12_3_0 }
+ },
+ { PINMUX_CFG_REG("IPSR13", 0xe6060234, 32, 4) {
+ IP13_31_28
+ IP13_27_24
+ IP13_23_20
+ IP13_19_16
+ IP13_15_12
+ IP13_11_8
+ IP13_7_4
+ IP13_3_0 }
+ },
+ { PINMUX_CFG_REG("IPSR14", 0xe6060238, 32, 4) {
+ IP14_31_28
+ IP14_27_24
+ IP14_23_20
+ IP14_19_16
+ IP14_15_12
+ IP14_11_8
+ IP14_7_4
+ IP14_3_0 }
+ },
+ { PINMUX_CFG_REG("IPSR15", 0xe606023c, 32, 4) {
+ IP15_31_28
+ IP15_27_24
+ IP15_23_20
+ IP15_19_16
+ IP15_15_12
+ IP15_11_8
+ IP15_7_4
+ IP15_3_0 }
+ },
+ { PINMUX_CFG_REG("IPSR16", 0xe6060240, 32, 4) {
+ IP16_31_28
+ IP16_27_24
+ IP16_23_20
+ IP16_19_16
+ IP16_15_12
+ IP16_11_8
+ IP16_7_4
+ IP16_3_0 }
+ },
+ { PINMUX_CFG_REG("IPSR17", 0xe6060244, 32, 4) {
+ IP17_31_28
+ IP17_27_24
+ IP17_23_20
+ IP17_19_16
+ IP17_15_12
+ IP17_11_8
+ IP17_7_4
+ IP17_3_0 }
+ },
+ { PINMUX_CFG_REG("IPSR18", 0xe6060248, 32, 4) {
+ /* IP18_31_28 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* IP18_27_24 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* IP18_23_20 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* IP18_19_16 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* IP18_15_12 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* IP18_11_8 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ IP18_7_4
+ IP18_3_0 }
+ },
+#undef F_
+#undef FM
+
+#define F_(x, y) x,
+#define FM(x) FN_##x,
+ { PINMUX_CFG_REG_VAR("MOD_SEL0", 0xe6060500, 32,
+ 3, 2, 3, 1, 1, 1, 1, 1, 2, 1,
+ 1, 2, 1, 1, 1, 2, 2, 1, 2, 3) {
+ MOD_SEL0_31_30_29
+ MOD_SEL0_28_27
+ MOD_SEL0_26_25_24
+ MOD_SEL0_23
+ MOD_SEL0_22
+ MOD_SEL0_21
+ MOD_SEL0_20
+ MOD_SEL0_19
+ MOD_SEL0_18_17
+ MOD_SEL0_16
+ 0, 0, /* RESERVED 15 */
+ MOD_SEL0_14_13
+ MOD_SEL0_12
+ MOD_SEL0_11
+ MOD_SEL0_10
+ MOD_SEL0_9_8
+ MOD_SEL0_7_6
+ MOD_SEL0_5
+ MOD_SEL0_4_3
+ /* RESERVED 2, 1, 0 */
+ 0, 0, 0, 0, 0, 0, 0, 0 }
+ },
+ { PINMUX_CFG_REG_VAR("MOD_SEL1", 0xe6060504, 32,
+ 2, 3, 1, 2, 3, 1, 1, 2, 1,
+ 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1) {
+ MOD_SEL1_31_30
+ MOD_SEL1_29_28_27
+ MOD_SEL1_26
+ MOD_SEL1_25_24
+ MOD_SEL1_23_22_21
+ MOD_SEL1_20
+ MOD_SEL1_19
+ MOD_SEL1_18_17
+ MOD_SEL1_16
+ MOD_SEL1_15_14
+ MOD_SEL1_13
+ MOD_SEL1_12
+ MOD_SEL1_11
+ MOD_SEL1_10
+ MOD_SEL1_9
+ 0, 0, 0, 0, /* RESERVED 8, 7 */
+ MOD_SEL1_6
+ MOD_SEL1_5
+ MOD_SEL1_4
+ MOD_SEL1_3
+ MOD_SEL1_2
+ MOD_SEL1_1
+ MOD_SEL1_0 }
+ },
+ { PINMUX_CFG_REG_VAR("MOD_SEL2", 0xe6060508, 32,
+ 1, 1, 1, 2, 1, 3, 1, 1, 1, 1, 1, 1, 1,
+ 4, 4, 4, 3, 1) {
+ MOD_SEL2_31
+ MOD_SEL2_30
+ MOD_SEL2_29
+ MOD_SEL2_28_27
+ MOD_SEL2_26
+ MOD_SEL2_25_24_23
+ MOD_SEL2_22
+ MOD_SEL2_21
+ MOD_SEL2_20
+ MOD_SEL2_19
+ MOD_SEL2_18
+ MOD_SEL2_17
+ /* RESERVED 16 */
+ 0, 0,
+ /* RESERVED 15, 14, 13, 12 */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* RESERVED 11, 10, 9, 8 */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* RESERVED 7, 6, 5, 4 */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* RESERVED 3, 2, 1 */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ MOD_SEL2_0 }
+ },
+ { },
+};
+
+static const struct pinmux_drive_reg pinmux_drive_regs[] = {
+ { PINMUX_DRIVE_REG("DRVCTRL0", 0xe6060300) {
+ { PIN_NUMBER('W', 3), 28, 2 }, /* QSPI0_SPCLK */
+ { PIN_A_NUMBER('C', 5), 24, 2 }, /* QSPI0_MOSI_IO0 */
+ { PIN_A_NUMBER('B', 4), 20, 2 }, /* QSPI0_MISO_IO1 */
+ { PIN_NUMBER('Y', 6), 16, 2 }, /* QSPI0_IO2 */
+ { PIN_A_NUMBER('B', 6), 12, 2 }, /* QSPI0_IO3 */
+ { PIN_NUMBER('Y', 3), 8, 2 }, /* QSPI0_SSL */
+ { PIN_NUMBER('V', 3), 4, 2 }, /* QSPI1_SPCLK */
+ { PIN_A_NUMBER('C', 7), 0, 2 }, /* QSPI1_MOSI_IO0 */
+ } },
+ { PINMUX_DRIVE_REG("DRVCTRL1", 0xe6060304) {
+ { PIN_A_NUMBER('E', 5), 28, 2 }, /* QSPI1_MISO_IO1 */
+ { PIN_A_NUMBER('E', 4), 24, 2 }, /* QSPI1_IO2 */
+ { PIN_A_NUMBER('C', 3), 20, 2 }, /* QSPI1_IO3 */
+ { PIN_NUMBER('V', 5), 16, 2 }, /* QSPI1_SSL */
+ { PIN_NUMBER('Y', 7), 12, 2 }, /* RPC_INT# */
+ { PIN_NUMBER('V', 6), 8, 2 }, /* RPC_WP# */
+ { PIN_NUMBER('V', 7), 4, 2 }, /* RPC_RESET# */
+ { PIN_NUMBER('A', 16), 0, 3 }, /* AVB_RX_CTL */
+ } },
+ { PINMUX_DRIVE_REG("DRVCTRL2", 0xe6060308) {
+ { PIN_NUMBER('B', 19), 28, 3 }, /* AVB_RXC */
+ { PIN_NUMBER('A', 13), 24, 3 }, /* AVB_RD0 */
+ { PIN_NUMBER('B', 13), 20, 3 }, /* AVB_RD1 */
+ { PIN_NUMBER('A', 14), 16, 3 }, /* AVB_RD2 */
+ { PIN_NUMBER('B', 14), 12, 3 }, /* AVB_RD3 */
+ { PIN_NUMBER('A', 8), 8, 3 }, /* AVB_TX_CTL */
+ { PIN_NUMBER('A', 19), 4, 3 }, /* AVB_TXC */
+ { PIN_NUMBER('A', 18), 0, 3 }, /* AVB_TD0 */
+ } },
+ { PINMUX_DRIVE_REG("DRVCTRL3", 0xe606030c) {
+ { PIN_NUMBER('B', 18), 28, 3 }, /* AVB_TD1 */
+ { PIN_NUMBER('A', 17), 24, 3 }, /* AVB_TD2 */
+ { PIN_NUMBER('B', 17), 20, 3 }, /* AVB_TD3 */
+ { PIN_NUMBER('A', 12), 16, 3 }, /* AVB_TXCREFCLK */
+ { PIN_NUMBER('A', 9), 12, 3 }, /* AVB_MDIO */
+ { RCAR_GP_PIN(2, 9), 8, 3 }, /* AVB_MDC */
+ { RCAR_GP_PIN(2, 10), 4, 3 }, /* AVB_MAGIC */
+ { RCAR_GP_PIN(2, 11), 0, 3 }, /* AVB_PHY_INT */
+ } },
+ { PINMUX_DRIVE_REG("DRVCTRL4", 0xe6060310) {
+ { RCAR_GP_PIN(2, 12), 28, 3 }, /* AVB_LINK */
+ { RCAR_GP_PIN(2, 13), 24, 3 }, /* AVB_AVTP_MATCH */
+ { RCAR_GP_PIN(2, 14), 20, 3 }, /* AVB_AVTP_CAPTURE */
+ { RCAR_GP_PIN(2, 0), 16, 3 }, /* IRQ0 */
+ { RCAR_GP_PIN(2, 1), 12, 3 }, /* IRQ1 */
+ { RCAR_GP_PIN(2, 2), 8, 3 }, /* IRQ2 */
+ { RCAR_GP_PIN(2, 3), 4, 3 }, /* IRQ3 */
+ { RCAR_GP_PIN(2, 4), 0, 3 }, /* IRQ4 */
+ } },
+ { PINMUX_DRIVE_REG("DRVCTRL5", 0xe6060314) {
+ { RCAR_GP_PIN(2, 5), 28, 3 }, /* IRQ5 */
+ { RCAR_GP_PIN(2, 6), 24, 3 }, /* PWM0 */
+ { RCAR_GP_PIN(2, 7), 20, 3 }, /* PWM1 */
+ { RCAR_GP_PIN(2, 8), 16, 3 }, /* PWM2 */
+ { RCAR_GP_PIN(1, 0), 12, 3 }, /* A0 */
+ { RCAR_GP_PIN(1, 1), 8, 3 }, /* A1 */
+ { RCAR_GP_PIN(1, 2), 4, 3 }, /* A2 */
+ { RCAR_GP_PIN(1, 3), 0, 3 }, /* A3 */
+ } },
+ { PINMUX_DRIVE_REG("DRVCTRL6", 0xe6060318) {
+ { RCAR_GP_PIN(1, 4), 28, 3 }, /* A4 */
+ { RCAR_GP_PIN(1, 5), 24, 3 }, /* A5 */
+ { RCAR_GP_PIN(1, 6), 20, 3 }, /* A6 */
+ { RCAR_GP_PIN(1, 7), 16, 3 }, /* A7 */
+ { RCAR_GP_PIN(1, 8), 12, 3 }, /* A8 */
+ { RCAR_GP_PIN(1, 9), 8, 3 }, /* A9 */
+ { RCAR_GP_PIN(1, 10), 4, 3 }, /* A10 */
+ { RCAR_GP_PIN(1, 11), 0, 3 }, /* A11 */
+ } },
+ { PINMUX_DRIVE_REG("DRVCTRL7", 0xe606031c) {
+ { RCAR_GP_PIN(1, 12), 28, 3 }, /* A12 */
+ { RCAR_GP_PIN(1, 13), 24, 3 }, /* A13 */
+ { RCAR_GP_PIN(1, 14), 20, 3 }, /* A14 */
+ { RCAR_GP_PIN(1, 15), 16, 3 }, /* A15 */
+ { RCAR_GP_PIN(1, 16), 12, 3 }, /* A16 */
+ { RCAR_GP_PIN(1, 17), 8, 3 }, /* A17 */
+ { RCAR_GP_PIN(1, 18), 4, 3 }, /* A18 */
+ { RCAR_GP_PIN(1, 19), 0, 3 }, /* A19 */
+ } },
+ { PINMUX_DRIVE_REG("DRVCTRL8", 0xe6060320) {
+ { RCAR_GP_PIN(1, 28), 28, 3 }, /* CLKOUT */
+ { RCAR_GP_PIN(1, 20), 24, 3 }, /* CS0 */
+ { RCAR_GP_PIN(1, 21), 20, 3 }, /* CS1_A26 */
+ { RCAR_GP_PIN(1, 22), 16, 3 }, /* BS */
+ { RCAR_GP_PIN(1, 23), 12, 3 }, /* RD */
+ { RCAR_GP_PIN(1, 24), 8, 3 }, /* RD_WR */
+ { RCAR_GP_PIN(1, 25), 4, 3 }, /* WE0 */
+ { RCAR_GP_PIN(1, 26), 0, 3 }, /* WE1 */
+ } },
+ { PINMUX_DRIVE_REG("DRVCTRL9", 0xe6060324) {
+ { RCAR_GP_PIN(1, 27), 28, 3 }, /* EX_WAIT0 */
+ { PIN_NUMBER('C', 1), 24, 3 }, /* PRESETOUT# */
+ { RCAR_GP_PIN(0, 0), 20, 3 }, /* D0 */
+ { RCAR_GP_PIN(0, 1), 16, 3 }, /* D1 */
+ { RCAR_GP_PIN(0, 2), 12, 3 }, /* D2 */
+ { RCAR_GP_PIN(0, 3), 8, 3 }, /* D3 */
+ { RCAR_GP_PIN(0, 4), 4, 3 }, /* D4 */
+ { RCAR_GP_PIN(0, 5), 0, 3 }, /* D5 */
+ } },
+ { PINMUX_DRIVE_REG("DRVCTRL10", 0xe6060328) {
+ { RCAR_GP_PIN(0, 6), 28, 3 }, /* D6 */
+ { RCAR_GP_PIN(0, 7), 24, 3 }, /* D7 */
+ { RCAR_GP_PIN(0, 8), 20, 3 }, /* D8 */
+ { RCAR_GP_PIN(0, 9), 16, 3 }, /* D9 */
+ { RCAR_GP_PIN(0, 10), 12, 3 }, /* D10 */
+ { RCAR_GP_PIN(0, 11), 8, 3 }, /* D11 */
+ { RCAR_GP_PIN(0, 12), 4, 3 }, /* D12 */
+ { RCAR_GP_PIN(0, 13), 0, 3 }, /* D13 */
+ } },
+ { PINMUX_DRIVE_REG("DRVCTRL11", 0xe606032c) {
+ { RCAR_GP_PIN(0, 14), 28, 3 }, /* D14 */
+ { RCAR_GP_PIN(0, 15), 24, 3 }, /* D15 */
+ { RCAR_GP_PIN(7, 0), 20, 3 }, /* AVS1 */
+ { RCAR_GP_PIN(7, 1), 16, 3 }, /* AVS2 */
+ { RCAR_GP_PIN(7, 2), 12, 3 }, /* HDMI0_CEC */
+ { RCAR_GP_PIN(7, 3), 8, 3 }, /* GP7_03 */
+ { PIN_A_NUMBER('P', 7), 4, 2 }, /* DU_DOTCLKIN0 */
+ { PIN_A_NUMBER('P', 8), 0, 2 }, /* DU_DOTCLKIN1 */
+ } },
+ { PINMUX_DRIVE_REG("DRVCTRL12", 0xe6060330) {
+ { PIN_A_NUMBER('R', 8), 28, 2 }, /* DU_DOTCLKIN3 */
+ { PIN_A_NUMBER('D', 38), 20, 2 }, /* FSCLKST */
+ { PIN_A_NUMBER('R', 30), 4, 2 }, /* TMS */
+ } },
+ { PINMUX_DRIVE_REG("DRVCTRL13", 0xe6060334) {
+ { PIN_A_NUMBER('T', 28), 28, 2 }, /* TDO */
+ { PIN_A_NUMBER('T', 30), 24, 2 }, /* ASEBRK */
+ { RCAR_GP_PIN(3, 0), 20, 3 }, /* SD0_CLK */
+ { RCAR_GP_PIN(3, 1), 16, 3 }, /* SD0_CMD */
+ { RCAR_GP_PIN(3, 2), 12, 3 }, /* SD0_DAT0 */
+ { RCAR_GP_PIN(3, 3), 8, 3 }, /* SD0_DAT1 */
+ { RCAR_GP_PIN(3, 4), 4, 3 }, /* SD0_DAT2 */
+ { RCAR_GP_PIN(3, 5), 0, 3 }, /* SD0_DAT3 */
+ } },
+ { PINMUX_DRIVE_REG("DRVCTRL14", 0xe6060338) {
+ { RCAR_GP_PIN(3, 6), 28, 3 }, /* SD1_CLK */
+ { RCAR_GP_PIN(3, 7), 24, 3 }, /* SD1_CMD */
+ { RCAR_GP_PIN(3, 8), 20, 3 }, /* SD1_DAT0 */
+ { RCAR_GP_PIN(3, 9), 16, 3 }, /* SD1_DAT1 */
+ { RCAR_GP_PIN(3, 10), 12, 3 }, /* SD1_DAT2 */
+ { RCAR_GP_PIN(3, 11), 8, 3 }, /* SD1_DAT3 */
+ { RCAR_GP_PIN(4, 0), 4, 3 }, /* SD2_CLK */
+ { RCAR_GP_PIN(4, 1), 0, 3 }, /* SD2_CMD */
+ } },
+ { PINMUX_DRIVE_REG("DRVCTRL15", 0xe606033c) {
+ { RCAR_GP_PIN(4, 2), 28, 3 }, /* SD2_DAT0 */
+ { RCAR_GP_PIN(4, 3), 24, 3 }, /* SD2_DAT1 */
+ { RCAR_GP_PIN(4, 4), 20, 3 }, /* SD2_DAT2 */
+ { RCAR_GP_PIN(4, 5), 16, 3 }, /* SD2_DAT3 */
+ { RCAR_GP_PIN(4, 6), 12, 3 }, /* SD2_DS */
+ { RCAR_GP_PIN(4, 7), 8, 3 }, /* SD3_CLK */
+ { RCAR_GP_PIN(4, 8), 4, 3 }, /* SD3_CMD */
+ { RCAR_GP_PIN(4, 9), 0, 3 }, /* SD3_DAT0 */
+ } },
+ { PINMUX_DRIVE_REG("DRVCTRL16", 0xe6060340) {
+ { RCAR_GP_PIN(4, 10), 28, 3 }, /* SD3_DAT1 */
+ { RCAR_GP_PIN(4, 11), 24, 3 }, /* SD3_DAT2 */
+ { RCAR_GP_PIN(4, 12), 20, 3 }, /* SD3_DAT3 */
+ { RCAR_GP_PIN(4, 13), 16, 3 }, /* SD3_DAT4 */
+ { RCAR_GP_PIN(4, 14), 12, 3 }, /* SD3_DAT5 */
+ { RCAR_GP_PIN(4, 15), 8, 3 }, /* SD3_DAT6 */
+ { RCAR_GP_PIN(4, 16), 4, 3 }, /* SD3_DAT7 */
+ { RCAR_GP_PIN(4, 17), 0, 3 }, /* SD3_DS */
+ } },
+ { PINMUX_DRIVE_REG("DRVCTRL17", 0xe6060344) {
+ { RCAR_GP_PIN(3, 12), 28, 3 }, /* SD0_CD */
+ { RCAR_GP_PIN(3, 13), 24, 3 }, /* SD0_WP */
+ { RCAR_GP_PIN(3, 14), 20, 3 }, /* SD1_CD */
+ { RCAR_GP_PIN(3, 15), 16, 3 }, /* SD1_WP */
+ { RCAR_GP_PIN(5, 0), 12, 3 }, /* SCK0 */
+ { RCAR_GP_PIN(5, 1), 8, 3 }, /* RX0 */
+ { RCAR_GP_PIN(5, 2), 4, 3 }, /* TX0 */
+ { RCAR_GP_PIN(5, 3), 0, 3 }, /* CTS0 */
+ } },
+ { PINMUX_DRIVE_REG("DRVCTRL18", 0xe6060348) {
+ { RCAR_GP_PIN(5, 4), 28, 3 }, /* RTS0 */
+ { RCAR_GP_PIN(5, 5), 24, 3 }, /* RX1 */
+ { RCAR_GP_PIN(5, 6), 20, 3 }, /* TX1 */
+ { RCAR_GP_PIN(5, 7), 16, 3 }, /* CTS1 */
+ { RCAR_GP_PIN(5, 8), 12, 3 }, /* RTS1 */
+ { RCAR_GP_PIN(5, 9), 8, 3 }, /* SCK2 */
+ { RCAR_GP_PIN(5, 10), 4, 3 }, /* TX2 */
+ { RCAR_GP_PIN(5, 11), 0, 3 }, /* RX2 */
+ } },
+ { PINMUX_DRIVE_REG("DRVCTRL19", 0xe606034c) {
+ { RCAR_GP_PIN(5, 12), 28, 3 }, /* HSCK0 */
+ { RCAR_GP_PIN(5, 13), 24, 3 }, /* HRX0 */
+ { RCAR_GP_PIN(5, 14), 20, 3 }, /* HTX0 */
+ { RCAR_GP_PIN(5, 15), 16, 3 }, /* HCTS0 */
+ { RCAR_GP_PIN(5, 16), 12, 3 }, /* HRTS0 */
+ { RCAR_GP_PIN(5, 17), 8, 3 }, /* MSIOF0_SCK */
+ { RCAR_GP_PIN(5, 18), 4, 3 }, /* MSIOF0_SYNC */
+ { RCAR_GP_PIN(5, 19), 0, 3 }, /* MSIOF0_SS1 */
+ } },
+ { PINMUX_DRIVE_REG("DRVCTRL20", 0xe6060350) {
+ { RCAR_GP_PIN(5, 20), 28, 3 }, /* MSIOF0_TXD */
+ { RCAR_GP_PIN(5, 21), 24, 3 }, /* MSIOF0_SS2 */
+ { RCAR_GP_PIN(5, 22), 20, 3 }, /* MSIOF0_RXD */
+ { RCAR_GP_PIN(5, 23), 16, 3 }, /* MLB_CLK */
+ { RCAR_GP_PIN(5, 24), 12, 3 }, /* MLB_SIG */
+ { RCAR_GP_PIN(5, 25), 8, 3 }, /* MLB_DAT */
+ { PIN_NUMBER('H', 37), 4, 3 }, /* MLB_REF */
+ { RCAR_GP_PIN(6, 0), 0, 3 }, /* SSI_SCK01239 */
+ } },
+ { PINMUX_DRIVE_REG("DRVCTRL21", 0xe6060354) {
+ { RCAR_GP_PIN(6, 1), 28, 3 }, /* SSI_WS01239 */
+ { RCAR_GP_PIN(6, 2), 24, 3 }, /* SSI_SDATA0 */
+ { RCAR_GP_PIN(6, 3), 20, 3 }, /* SSI_SDATA1 */
+ { RCAR_GP_PIN(6, 4), 16, 3 }, /* SSI_SDATA2 */
+ { RCAR_GP_PIN(6, 5), 12, 3 }, /* SSI_SCK349 */
+ { RCAR_GP_PIN(6, 6), 8, 3 }, /* SSI_WS349 */
+ { RCAR_GP_PIN(6, 7), 4, 3 }, /* SSI_SDATA3 */
+ { RCAR_GP_PIN(6, 8), 0, 3 }, /* SSI_SCK4 */
+ } },
+ { PINMUX_DRIVE_REG("DRVCTRL22", 0xe6060358) {
+ { RCAR_GP_PIN(6, 9), 28, 3 }, /* SSI_WS4 */
+ { RCAR_GP_PIN(6, 10), 24, 3 }, /* SSI_SDATA4 */
+ { RCAR_GP_PIN(6, 11), 20, 3 }, /* SSI_SCK5 */
+ { RCAR_GP_PIN(6, 12), 16, 3 }, /* SSI_WS5 */
+ { RCAR_GP_PIN(6, 13), 12, 3 }, /* SSI_SDATA5 */
+ { RCAR_GP_PIN(6, 14), 8, 3 }, /* SSI_SCK6 */
+ { RCAR_GP_PIN(6, 15), 4, 3 }, /* SSI_WS6 */
+ { RCAR_GP_PIN(6, 16), 0, 3 }, /* SSI_SDATA6 */
+ } },
+ { PINMUX_DRIVE_REG("DRVCTRL23", 0xe606035c) {
+ { RCAR_GP_PIN(6, 17), 28, 3 }, /* SSI_SCK78 */
+ { RCAR_GP_PIN(6, 18), 24, 3 }, /* SSI_WS78 */
+ { RCAR_GP_PIN(6, 19), 20, 3 }, /* SSI_SDATA7 */
+ { RCAR_GP_PIN(6, 20), 16, 3 }, /* SSI_SDATA8 */
+ { RCAR_GP_PIN(6, 21), 12, 3 }, /* SSI_SDATA9 */
+ { RCAR_GP_PIN(6, 22), 8, 3 }, /* AUDIO_CLKA */
+ { RCAR_GP_PIN(6, 23), 4, 3 }, /* AUDIO_CLKB */
+ { RCAR_GP_PIN(6, 24), 0, 3 }, /* USB0_PWEN */
+ } },
+ { PINMUX_DRIVE_REG("DRVCTRL24", 0xe6060360) {
+ { RCAR_GP_PIN(6, 25), 28, 3 }, /* USB0_OVC */
+ { RCAR_GP_PIN(6, 26), 24, 3 }, /* USB1_PWEN */
+ { RCAR_GP_PIN(6, 27), 20, 3 }, /* USB1_OVC */
+ { RCAR_GP_PIN(6, 28), 16, 3 }, /* USB30_PWEN */
+ { RCAR_GP_PIN(6, 29), 12, 3 }, /* USB30_OVC */
+ { RCAR_GP_PIN(6, 30), 8, 3 }, /* GP6_30 */
+ { RCAR_GP_PIN(6, 31), 4, 3 }, /* GP6_31 */
+ } },
+ { },
+};
+
+enum ioctrl_regs {
+ POCCTRL,
+};
+
+static const struct pinmux_ioctrl_reg pinmux_ioctrl_regs[] = {
+ [POCCTRL] = { 0xe6060380, },
+ { /* sentinel */ },
+};
+
+static int r8a77965_pin_to_pocctrl(struct sh_pfc *pfc, unsigned int pin, u32 *pocctrl)
+{
+ int bit = -EINVAL;
+
+ *pocctrl = pinmux_ioctrl_regs[POCCTRL].reg;
+
+ if (pin >= RCAR_GP_PIN(3, 0) && pin <= RCAR_GP_PIN(3, 11))
+ bit = pin & 0x1f;
+
+ if (pin >= RCAR_GP_PIN(4, 0) && pin <= RCAR_GP_PIN(4, 17))
+ bit = (pin & 0x1f) + 12;
+
+ return bit;
+}
+
+static const struct pinmux_bias_reg pinmux_bias_regs[] = {
+ { PINMUX_BIAS_REG("PUEN0", 0xe6060400, "PUD0", 0xe6060440) {
+ [ 0] = PIN_NUMBER('W', 3), /* QSPI0_SPCLK */
+ [ 1] = PIN_A_NUMBER('C', 5), /* QSPI0_MOSI_IO0 */
+ [ 2] = PIN_A_NUMBER('B', 4), /* QSPI0_MISO_IO1 */
+ [ 3] = PIN_NUMBER('Y', 6), /* QSPI0_IO2 */
+ [ 4] = PIN_A_NUMBER('B', 6), /* QSPI0_IO3 */
+ [ 5] = PIN_NUMBER('Y', 3), /* QSPI0_SSL */
+ [ 6] = PIN_NUMBER('V', 3), /* QSPI1_SPCLK */
+ [ 7] = PIN_A_NUMBER('C', 7), /* QSPI1_MOSI_IO0 */
+ [ 8] = PIN_A_NUMBER('E', 5), /* QSPI1_MISO_IO1 */
+ [ 9] = PIN_A_NUMBER('E', 4), /* QSPI1_IO2 */
+ [10] = PIN_A_NUMBER('C', 3), /* QSPI1_IO3 */
+ [11] = PIN_NUMBER('V', 5), /* QSPI1_SSL */
+ [12] = PIN_NUMBER('Y', 7), /* RPC_INT# */
+ [13] = PIN_NUMBER('V', 6), /* RPC_WP# */
+ [14] = PIN_NUMBER('V', 7), /* RPC_RESET# */
+ [15] = PIN_NUMBER('A', 16), /* AVB_RX_CTL */
+ [16] = PIN_NUMBER('B', 19), /* AVB_RXC */
+ [17] = PIN_NUMBER('A', 13), /* AVB_RD0 */
+ [18] = PIN_NUMBER('B', 13), /* AVB_RD1 */
+ [19] = PIN_NUMBER('A', 14), /* AVB_RD2 */
+ [20] = PIN_NUMBER('B', 14), /* AVB_RD3 */
+ [21] = PIN_NUMBER('A', 8), /* AVB_TX_CTL */
+ [22] = PIN_NUMBER('A', 19), /* AVB_TXC */
+ [23] = PIN_NUMBER('A', 18), /* AVB_TD0 */
+ [24] = PIN_NUMBER('B', 18), /* AVB_TD1 */
+ [25] = PIN_NUMBER('A', 17), /* AVB_TD2 */
+ [26] = PIN_NUMBER('B', 17), /* AVB_TD3 */
+ [27] = PIN_NUMBER('A', 12), /* AVB_TXCREFCLK */
+ [28] = PIN_NUMBER('A', 9), /* AVB_MDIO */
+ [29] = RCAR_GP_PIN(2, 9), /* AVB_MDC */
+ [30] = RCAR_GP_PIN(2, 10), /* AVB_MAGIC */
+ [31] = RCAR_GP_PIN(2, 11), /* AVB_PHY_INT */
+ } },
+ { PINMUX_BIAS_REG("PUEN1", 0xe6060404, "PUD1", 0xe6060444) {
+ [ 0] = RCAR_GP_PIN(2, 12), /* AVB_LINK */
+ [ 1] = RCAR_GP_PIN(2, 13), /* AVB_AVTP_MATCH_A */
+ [ 2] = RCAR_GP_PIN(2, 14), /* AVB_AVTP_CAPTURE_A */
+ [ 3] = RCAR_GP_PIN(2, 0), /* IRQ0 */
+ [ 4] = RCAR_GP_PIN(2, 1), /* IRQ1 */
+ [ 5] = RCAR_GP_PIN(2, 2), /* IRQ2 */
+ [ 6] = RCAR_GP_PIN(2, 3), /* IRQ3 */
+ [ 7] = RCAR_GP_PIN(2, 4), /* IRQ4 */
+ [ 8] = RCAR_GP_PIN(2, 5), /* IRQ5 */
+ [ 9] = RCAR_GP_PIN(2, 6), /* PWM0 */
+ [10] = RCAR_GP_PIN(2, 7), /* PWM1_A */
+ [11] = RCAR_GP_PIN(2, 8), /* PWM2_A */
+ [12] = RCAR_GP_PIN(1, 0), /* A0 */
+ [13] = RCAR_GP_PIN(1, 1), /* A1 */
+ [14] = RCAR_GP_PIN(1, 2), /* A2 */
+ [15] = RCAR_GP_PIN(1, 3), /* A3 */
+ [16] = RCAR_GP_PIN(1, 4), /* A4 */
+ [17] = RCAR_GP_PIN(1, 5), /* A5 */
+ [18] = RCAR_GP_PIN(1, 6), /* A6 */
+ [19] = RCAR_GP_PIN(1, 7), /* A7 */
+ [20] = RCAR_GP_PIN(1, 8), /* A8 */
+ [21] = RCAR_GP_PIN(1, 9), /* A9 */
+ [22] = RCAR_GP_PIN(1, 10), /* A10 */
+ [23] = RCAR_GP_PIN(1, 11), /* A11 */
+ [24] = RCAR_GP_PIN(1, 12), /* A12 */
+ [25] = RCAR_GP_PIN(1, 13), /* A13 */
+ [26] = RCAR_GP_PIN(1, 14), /* A14 */
+ [27] = RCAR_GP_PIN(1, 15), /* A15 */
+ [28] = RCAR_GP_PIN(1, 16), /* A16 */
+ [29] = RCAR_GP_PIN(1, 17), /* A17 */
+ [30] = RCAR_GP_PIN(1, 18), /* A18 */
+ [31] = RCAR_GP_PIN(1, 19), /* A19 */
+ } },
+ { PINMUX_BIAS_REG("PUEN2", 0xe6060408, "PUD2", 0xe6060448) {
+ [ 0] = RCAR_GP_PIN(1, 28), /* CLKOUT */
+ [ 1] = RCAR_GP_PIN(1, 20), /* CS0_N */
+ [ 2] = RCAR_GP_PIN(1, 21), /* CS1_N */
+ [ 3] = RCAR_GP_PIN(1, 22), /* BS_N */
+ [ 4] = RCAR_GP_PIN(1, 23), /* RD_N */
+ [ 5] = RCAR_GP_PIN(1, 24), /* RD_WR_N */
+ [ 6] = RCAR_GP_PIN(1, 25), /* WE0_N */
+ [ 7] = RCAR_GP_PIN(1, 26), /* WE1_N */
+ [ 8] = RCAR_GP_PIN(1, 27), /* EX_WAIT0_A */
+ [ 9] = PIN_NUMBER('C', 1), /* PRESETOUT# */
+ [10] = RCAR_GP_PIN(0, 0), /* D0 */
+ [11] = RCAR_GP_PIN(0, 1), /* D1 */
+ [12] = RCAR_GP_PIN(0, 2), /* D2 */
+ [13] = RCAR_GP_PIN(0, 3), /* D3 */
+ [14] = RCAR_GP_PIN(0, 4), /* D4 */
+ [15] = RCAR_GP_PIN(0, 5), /* D5 */
+ [16] = RCAR_GP_PIN(0, 6), /* D6 */
+ [17] = RCAR_GP_PIN(0, 7), /* D7 */
+ [18] = RCAR_GP_PIN(0, 8), /* D8 */
+ [19] = RCAR_GP_PIN(0, 9), /* D9 */
+ [20] = RCAR_GP_PIN(0, 10), /* D10 */
+ [21] = RCAR_GP_PIN(0, 11), /* D11 */
+ [22] = RCAR_GP_PIN(0, 12), /* D12 */
+ [23] = RCAR_GP_PIN(0, 13), /* D13 */
+ [24] = RCAR_GP_PIN(0, 14), /* D14 */
+ [25] = RCAR_GP_PIN(0, 15), /* D15 */
+ [26] = RCAR_GP_PIN(7, 0), /* AVS1 */
+ [27] = RCAR_GP_PIN(7, 1), /* AVS2 */
+ [28] = RCAR_GP_PIN(7, 2), /* HDMI0_CEC */
+ [29] = RCAR_GP_PIN(7, 3), /* GP7_03 */
+ [30] = PIN_A_NUMBER('P', 7), /* DU_DOTCLKIN0 */
+ [31] = PIN_A_NUMBER('P', 8), /* DU_DOTCLKIN1 */
+ } },
+ { PINMUX_BIAS_REG("PUEN3", 0xe606040c, "PUD3", 0xe606044c) {
+ [ 0] = PIN_A_NUMBER('R', 8), /* DU_DOTCLKIN2 */
+ [ 1] = PIN_NONE,
+ [ 2] = PIN_A_NUMBER('D', 38), /* FSCLKST */
+ [ 3] = PIN_A_NUMBER('D', 39), /* EXTALR*/
+ [ 4] = PIN_A_NUMBER('R', 26), /* TRST# */
+ [ 5] = PIN_A_NUMBER('T', 27), /* TCK */
+ [ 6] = PIN_A_NUMBER('R', 30), /* TMS */
+ [ 7] = PIN_A_NUMBER('R', 29), /* TDI */
+ [ 8] = PIN_NONE,
+ [ 9] = PIN_A_NUMBER('T', 30), /* ASEBRK */
+ [10] = RCAR_GP_PIN(3, 0), /* SD0_CLK */
+ [11] = RCAR_GP_PIN(3, 1), /* SD0_CMD */
+ [12] = RCAR_GP_PIN(3, 2), /* SD0_DAT0 */
+ [13] = RCAR_GP_PIN(3, 3), /* SD0_DAT1 */
+ [14] = RCAR_GP_PIN(3, 4), /* SD0_DAT2 */
+ [15] = RCAR_GP_PIN(3, 5), /* SD0_DAT3 */
+ [16] = RCAR_GP_PIN(3, 6), /* SD1_CLK */
+ [17] = RCAR_GP_PIN(3, 7), /* SD1_CMD */
+ [18] = RCAR_GP_PIN(3, 8), /* SD1_DAT0 */
+ [19] = RCAR_GP_PIN(3, 9), /* SD1_DAT1 */
+ [20] = RCAR_GP_PIN(3, 10), /* SD1_DAT2 */
+ [21] = RCAR_GP_PIN(3, 11), /* SD1_DAT3 */
+ [22] = RCAR_GP_PIN(4, 0), /* SD2_CLK */
+ [23] = RCAR_GP_PIN(4, 1), /* SD2_CMD */
+ [24] = RCAR_GP_PIN(4, 2), /* SD2_DAT0 */
+ [25] = RCAR_GP_PIN(4, 3), /* SD2_DAT1 */
+ [26] = RCAR_GP_PIN(4, 4), /* SD2_DAT2 */
+ [27] = RCAR_GP_PIN(4, 5), /* SD2_DAT3 */
+ [28] = RCAR_GP_PIN(4, 6), /* SD2_DS */
+ [29] = RCAR_GP_PIN(4, 7), /* SD3_CLK */
+ [30] = RCAR_GP_PIN(4, 8), /* SD3_CMD */
+ [31] = RCAR_GP_PIN(4, 9), /* SD3_DAT0 */
+ } },
+ { PINMUX_BIAS_REG("PUEN4", 0xe6060410, "PUD4", 0xe6060450) {
+ [ 0] = RCAR_GP_PIN(4, 10), /* SD3_DAT1 */
+ [ 1] = RCAR_GP_PIN(4, 11), /* SD3_DAT2 */
+ [ 2] = RCAR_GP_PIN(4, 12), /* SD3_DAT3 */
+ [ 3] = RCAR_GP_PIN(4, 13), /* SD3_DAT4 */
+ [ 4] = RCAR_GP_PIN(4, 14), /* SD3_DAT5 */
+ [ 5] = RCAR_GP_PIN(4, 15), /* SD3_DAT6 */
+ [ 6] = RCAR_GP_PIN(4, 16), /* SD3_DAT7 */
+ [ 7] = RCAR_GP_PIN(4, 17), /* SD3_DS */
+ [ 8] = RCAR_GP_PIN(3, 12), /* SD0_CD */
+ [ 9] = RCAR_GP_PIN(3, 13), /* SD0_WP */
+ [10] = RCAR_GP_PIN(3, 14), /* SD1_CD */
+ [11] = RCAR_GP_PIN(3, 15), /* SD1_WP */
+ [12] = RCAR_GP_PIN(5, 0), /* SCK0 */
+ [13] = RCAR_GP_PIN(5, 1), /* RX0 */
+ [14] = RCAR_GP_PIN(5, 2), /* TX0 */
+ [15] = RCAR_GP_PIN(5, 3), /* CTS0_N */
+ [16] = RCAR_GP_PIN(5, 4), /* RTS0_N */
+ [17] = RCAR_GP_PIN(5, 5), /* RX1_A */
+ [18] = RCAR_GP_PIN(5, 6), /* TX1_A */
+ [19] = RCAR_GP_PIN(5, 7), /* CTS1_N */
+ [20] = RCAR_GP_PIN(5, 8), /* RTS1_N */
+ [21] = RCAR_GP_PIN(5, 9), /* SCK2 */
+ [22] = RCAR_GP_PIN(5, 10), /* TX2_A */
+ [23] = RCAR_GP_PIN(5, 11), /* RX2_A */
+ [24] = RCAR_GP_PIN(5, 12), /* HSCK0 */
+ [25] = RCAR_GP_PIN(5, 13), /* HRX0 */
+ [26] = RCAR_GP_PIN(5, 14), /* HTX0 */
+ [27] = RCAR_GP_PIN(5, 15), /* HCTS0_N */
+ [28] = RCAR_GP_PIN(5, 16), /* HRTS0_N */
+ [29] = RCAR_GP_PIN(5, 17), /* MSIOF0_SCK */
+ [30] = RCAR_GP_PIN(5, 18), /* MSIOF0_SYNC */
+ [31] = RCAR_GP_PIN(5, 19), /* MSIOF0_SS1 */
+ } },
+ { PINMUX_BIAS_REG("PUEN5", 0xe6060414, "PUD5", 0xe6060454) {
+ [ 0] = RCAR_GP_PIN(5, 20), /* MSIOF0_TXD */
+ [ 1] = RCAR_GP_PIN(5, 21), /* MSIOF0_SS2 */
+ [ 2] = RCAR_GP_PIN(5, 22), /* MSIOF0_RXD */
+ [ 3] = RCAR_GP_PIN(5, 23), /* MLB_CLK */
+ [ 4] = RCAR_GP_PIN(5, 24), /* MLB_SIG */
+ [ 5] = RCAR_GP_PIN(5, 25), /* MLB_DAT */
+ [ 6] = PIN_NUMBER('H', 37), /* MLB_REF */
+ [ 7] = RCAR_GP_PIN(6, 0), /* SSI_SCK01239 */
+ [ 8] = RCAR_GP_PIN(6, 1), /* SSI_WS01239 */
+ [ 9] = RCAR_GP_PIN(6, 2), /* SSI_SDATA0 */
+ [10] = RCAR_GP_PIN(6, 3), /* SSI_SDATA1_A */
+ [11] = RCAR_GP_PIN(6, 4), /* SSI_SDATA2_A */
+ [12] = RCAR_GP_PIN(6, 5), /* SSI_SCK349 */
+ [13] = RCAR_GP_PIN(6, 6), /* SSI_WS349 */
+ [14] = RCAR_GP_PIN(6, 7), /* SSI_SDATA3 */
+ [15] = RCAR_GP_PIN(6, 8), /* SSI_SCK4 */
+ [16] = RCAR_GP_PIN(6, 9), /* SSI_WS4 */
+ [17] = RCAR_GP_PIN(6, 10), /* SSI_SDATA4 */
+ [18] = RCAR_GP_PIN(6, 11), /* SSI_SCK5 */
+ [19] = RCAR_GP_PIN(6, 12), /* SSI_WS5 */
+ [20] = RCAR_GP_PIN(6, 13), /* SSI_SDATA5 */
+ [21] = RCAR_GP_PIN(6, 14), /* SSI_SCK6 */
+ [22] = RCAR_GP_PIN(6, 15), /* SSI_WS6 */
+ [23] = RCAR_GP_PIN(6, 16), /* SSI_SDATA6 */
+ [24] = RCAR_GP_PIN(6, 17), /* SSI_SCK78 */
+ [25] = RCAR_GP_PIN(6, 18), /* SSI_WS78 */
+ [26] = RCAR_GP_PIN(6, 19), /* SSI_SDATA7 */
+ [27] = RCAR_GP_PIN(6, 20), /* SSI_SDATA8 */
+ [28] = RCAR_GP_PIN(6, 21), /* SSI_SDATA9_A */
+ [29] = RCAR_GP_PIN(6, 22), /* AUDIO_CLKA_A */
+ [30] = RCAR_GP_PIN(6, 23), /* AUDIO_CLKB_B */
+ [31] = RCAR_GP_PIN(6, 24), /* USB0_PWEN */
+ } },
+ { PINMUX_BIAS_REG("PUEN6", 0xe6060418, "PUD6", 0xe6060458) {
+ [ 0] = RCAR_GP_PIN(6, 25), /* USB0_OVC */
+ [ 1] = RCAR_GP_PIN(6, 26), /* USB1_PWEN */
+ [ 2] = RCAR_GP_PIN(6, 27), /* USB1_OVC */
+ [ 3] = RCAR_GP_PIN(6, 28), /* USB30_PWEN */
+ [ 4] = RCAR_GP_PIN(6, 29), /* USB30_OVC */
+ [ 5] = RCAR_GP_PIN(6, 30), /* GP6_30 */
+ [ 6] = RCAR_GP_PIN(6, 31), /* GP6_31 */
+ [ 7] = PIN_NONE,
+ [ 8] = PIN_NONE,
+ [ 9] = PIN_NONE,
+ [10] = PIN_NONE,
+ [11] = PIN_NONE,
+ [12] = PIN_NONE,
+ [13] = PIN_NONE,
+ [14] = PIN_NONE,
+ [15] = PIN_NONE,
+ [16] = PIN_NONE,
+ [17] = PIN_NONE,
+ [18] = PIN_NONE,
+ [19] = PIN_NONE,
+ [20] = PIN_NONE,
+ [21] = PIN_NONE,
+ [22] = PIN_NONE,
+ [23] = PIN_NONE,
+ [24] = PIN_NONE,
+ [25] = PIN_NONE,
+ [26] = PIN_NONE,
+ [27] = PIN_NONE,
+ [28] = PIN_NONE,
+ [29] = PIN_NONE,
+ [30] = PIN_NONE,
+ [31] = PIN_NONE,
+ } },
+ { /* sentinel */ },
+};
+
+static unsigned int r8a77965_pinmux_get_bias(struct sh_pfc *pfc,
+ unsigned int pin)
+{
+ const struct pinmux_bias_reg *reg;
+ unsigned int bit;
+
+ reg = sh_pfc_pin_to_bias_reg(pfc, pin, &bit);
+ if (!reg)
+ return PIN_CONFIG_BIAS_DISABLE;
+
+ if (!(sh_pfc_read(pfc, reg->puen) & BIT(bit)))
+ return PIN_CONFIG_BIAS_DISABLE;
+ else if (sh_pfc_read(pfc, reg->pud) & BIT(bit))
+ return PIN_CONFIG_BIAS_PULL_UP;
+ else
+ return PIN_CONFIG_BIAS_PULL_DOWN;
+}
+
+static void r8a77965_pinmux_set_bias(struct sh_pfc *pfc, unsigned int pin,
+ unsigned int bias)
+{
+ const struct pinmux_bias_reg *reg;
+ u32 enable, updown;
+ unsigned int bit;
+
+ reg = sh_pfc_pin_to_bias_reg(pfc, pin, &bit);
+ if (!reg)
+ return;
+
+ enable = sh_pfc_read(pfc, reg->puen) & ~BIT(bit);
+ if (bias != PIN_CONFIG_BIAS_DISABLE)
+ enable |= BIT(bit);
+
+ updown = sh_pfc_read(pfc, reg->pud) & ~BIT(bit);
+ if (bias == PIN_CONFIG_BIAS_PULL_UP)
+ updown |= BIT(bit);
+
+ sh_pfc_write(pfc, reg->pud, updown);
+ sh_pfc_write(pfc, reg->puen, enable);
+}
+
+static const struct sh_pfc_soc_operations r8a77965_pinmux_ops = {
+ .pin_to_pocctrl = r8a77965_pin_to_pocctrl,
+ .get_bias = r8a77965_pinmux_get_bias,
+ .set_bias = r8a77965_pinmux_set_bias,
+};
+
+const struct sh_pfc_soc_info r8a77965_pinmux_info = {
+ .name = "r8a77965_pfc",
+ .ops = &r8a77965_pinmux_ops,
+ .unlock_reg = 0xe6060000, /* PMMR */
+
+ .function = { PINMUX_FUNCTION_BEGIN, PINMUX_FUNCTION_END },
+
+ .pins = pinmux_pins,
+ .nr_pins = ARRAY_SIZE(pinmux_pins),
+ .groups = pinmux_groups,
+ .nr_groups = ARRAY_SIZE(pinmux_groups),
+ .functions = pinmux_functions,
+ .nr_functions = ARRAY_SIZE(pinmux_functions),
+
+ .cfg_regs = pinmux_config_regs,
+ .drive_regs = pinmux_drive_regs,
+ .bias_regs = pinmux_bias_regs,
+ .ioctrl_regs = pinmux_ioctrl_regs,
+
+ .pinmux_data = pinmux_data,
+ .pinmux_data_size = ARRAY_SIZE(pinmux_data),
+};
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77970.c b/drivers/pinctrl/sh-pfc/pfc-r8a77970.c
index 794f12d74449..b1bb7263532b 100644
--- a/drivers/pinctrl/sh-pfc/pfc-r8a77970.c
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a77970.c
@@ -728,6 +728,82 @@ static const struct sh_pfc_pin pinmux_pins[] = {
PINMUX_GPIO_GP_ALL(),
};
+/* - AVB0 ------------------------------------------------------------------- */
+static const unsigned int avb0_link_pins[] = {
+ /* AVB0_LINK */
+ RCAR_GP_PIN(1, 18),
+};
+static const unsigned int avb0_link_mux[] = {
+ AVB0_LINK_MARK,
+};
+static const unsigned int avb0_magic_pins[] = {
+ /* AVB0_MAGIC */
+ RCAR_GP_PIN(1, 16),
+};
+static const unsigned int avb0_magic_mux[] = {
+ AVB0_MAGIC_MARK,
+};
+static const unsigned int avb0_phy_int_pins[] = {
+ /* AVB0_PHY_INT */
+ RCAR_GP_PIN(1, 17),
+};
+static const unsigned int avb0_phy_int_mux[] = {
+ AVB0_PHY_INT_MARK,
+};
+static const unsigned int avb0_mdio_pins[] = {
+ /* AVB0_MDC, AVB0_MDIO */
+ RCAR_GP_PIN(1, 15), RCAR_GP_PIN(1, 14),
+};
+static const unsigned int avb0_mdio_mux[] = {
+ AVB0_MDC_MARK, AVB0_MDIO_MARK,
+};
+static const unsigned int avb0_rgmii_pins[] = {
+ /*
+ * AVB0_TX_CTL, AVB0_TXC, AVB0_TD0, AVB0_TD1, AVB0_TD2, AVB0_TD3,
+ * AVB0_RX_CTL, AVB0_RXC, AVB0_RD0, AVB0_RD1, AVB0_RD2, AVB0_RD3
+ */
+ RCAR_GP_PIN(1, 7), RCAR_GP_PIN(1, 8),
+ RCAR_GP_PIN(1, 9), RCAR_GP_PIN(1, 10),
+ RCAR_GP_PIN(1, 11), RCAR_GP_PIN(1, 12),
+ RCAR_GP_PIN(1, 1), RCAR_GP_PIN(1, 2),
+ RCAR_GP_PIN(1, 3), RCAR_GP_PIN(1, 4),
+ RCAR_GP_PIN(1, 5), RCAR_GP_PIN(1, 6),
+};
+static const unsigned int avb0_rgmii_mux[] = {
+ AVB0_TX_CTL_MARK, AVB0_TXC_MARK,
+ AVB0_TD0_MARK, AVB0_TD1_MARK, AVB0_TD2_MARK, AVB0_TD3_MARK,
+ AVB0_RX_CTL_MARK, AVB0_RXC_MARK,
+ AVB0_RD0_MARK, AVB0_RD1_MARK, AVB0_RD2_MARK, AVB0_RD3_MARK,
+};
+static const unsigned int avb0_txcrefclk_pins[] = {
+ /* AVB0_TXCREFCLK */
+ RCAR_GP_PIN(1, 13),
+};
+static const unsigned int avb0_txcrefclk_mux[] = {
+ AVB0_TXCREFCLK_MARK,
+};
+static const unsigned int avb0_avtp_pps_pins[] = {
+ /* AVB0_AVTP_PPS */
+ RCAR_GP_PIN(2, 6),
+};
+static const unsigned int avb0_avtp_pps_mux[] = {
+ AVB0_AVTP_PPS_MARK,
+};
+static const unsigned int avb0_avtp_capture_pins[] = {
+ /* AVB0_AVTP_CAPTURE */
+ RCAR_GP_PIN(1, 20),
+};
+static const unsigned int avb0_avtp_capture_mux[] = {
+ AVB0_AVTP_CAPTURE_MARK,
+};
+static const unsigned int avb0_avtp_match_pins[] = {
+ /* AVB0_AVTP_MATCH */
+ RCAR_GP_PIN(1, 19),
+};
+static const unsigned int avb0_avtp_match_mux[] = {
+ AVB0_AVTP_MATCH_MARK,
+};
+
/* - CANFD Clock ------------------------------------------------------------ */
static const unsigned int canfd_clk_a_pins[] = {
/* CANFD_CLK */
@@ -1599,6 +1675,15 @@ static const unsigned int vin1_clk_mux[] = {
};
static const struct sh_pfc_pin_group pinmux_groups[] = {
+ SH_PFC_PIN_GROUP(avb0_link),
+ SH_PFC_PIN_GROUP(avb0_magic),
+ SH_PFC_PIN_GROUP(avb0_phy_int),
+ SH_PFC_PIN_GROUP(avb0_mdio),
+ SH_PFC_PIN_GROUP(avb0_rgmii),
+ SH_PFC_PIN_GROUP(avb0_txcrefclk),
+ SH_PFC_PIN_GROUP(avb0_avtp_pps),
+ SH_PFC_PIN_GROUP(avb0_avtp_capture),
+ SH_PFC_PIN_GROUP(avb0_avtp_match),
SH_PFC_PIN_GROUP(canfd_clk_a),
SH_PFC_PIN_GROUP(canfd_clk_b),
SH_PFC_PIN_GROUP(canfd0_data_a),
@@ -1709,6 +1794,18 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(vin1_clk),
};
+static const char * const avb0_groups[] = {
+ "avb0_link",
+ "avb0_magic",
+ "avb0_phy_int",
+ "avb0_mdio",
+ "avb0_rgmii",
+ "avb0_txcrefclk",
+ "avb0_avtp_pps",
+ "avb0_avtp_capture",
+ "avb0_avtp_match",
+};
+
static const char * const canfd_clk_groups[] = {
"canfd_clk_a",
"canfd_clk_b",
@@ -1914,6 +2011,7 @@ static const char * const vin1_groups[] = {
};
static const struct sh_pfc_function pinmux_functions[] = {
+ SH_PFC_FUNCTION(avb0),
SH_PFC_FUNCTION(canfd_clk),
SH_PFC_FUNCTION(canfd0),
SH_PFC_FUNCTION(canfd1),
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77980.c b/drivers/pinctrl/sh-pfc/pfc-r8a77980.c
new file mode 100644
index 000000000000..84c8f1c2f1d1
--- /dev/null
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a77980.c
@@ -0,0 +1,2799 @@
+// SPDX-Lincense-Identifier: GPL 2.0
+/*
+ * R8A77980 processor support - PFC hardware block.
+ *
+ * Copyright (C) 2018 Renesas Electronics Corp.
+ * Copyright (C) 2018 Cogent Embedded, Inc.
+ *
+ * This file is based on the drivers/pinctrl/sh-pfc/pfc-r8a7795.c
+ *
+ * R-Car Gen3 processor support - PFC hardware block.
+ *
+ * Copyright (C) 2015 Renesas Electronics Corporation
+ */
+
+#include <linux/io.h>
+#include <linux/kernel.h>
+
+#include "core.h"
+#include "sh_pfc.h"
+
+#define CPU_ALL_PORT(fn, sfx) \
+ PORT_GP_22(0, fn, sfx), \
+ PORT_GP_28(1, fn, sfx), \
+ PORT_GP_30(2, fn, sfx), \
+ PORT_GP_17(3, fn, sfx), \
+ PORT_GP_25(4, fn, sfx), \
+ PORT_GP_15(5, fn, sfx)
+
+/*
+ * F_() : just information
+ * FM() : macro for FN_xxx / xxx_MARK
+ */
+
+/* GPSR0 */
+#define GPSR0_21 F_(DU_EXODDF_DU_ODDF_DISP_CDE, IP2_23_20)
+#define GPSR0_20 F_(DU_EXVSYNC_DU_VSYNC, IP2_19_16)
+#define GPSR0_19 F_(DU_EXHSYNC_DU_HSYNC, IP2_15_12)
+#define GPSR0_18 F_(DU_DOTCLKOUT, IP2_11_8)
+#define GPSR0_17 F_(DU_DB7, IP2_7_4)
+#define GPSR0_16 F_(DU_DB6, IP2_3_0)
+#define GPSR0_15 F_(DU_DB5, IP1_31_28)
+#define GPSR0_14 F_(DU_DB4, IP1_27_24)
+#define GPSR0_13 F_(DU_DB3, IP1_23_20)
+#define GPSR0_12 F_(DU_DB2, IP1_19_16)
+#define GPSR0_11 F_(DU_DG7, IP1_15_12)
+#define GPSR0_10 F_(DU_DG6, IP1_11_8)
+#define GPSR0_9 F_(DU_DG5, IP1_7_4)
+#define GPSR0_8 F_(DU_DG4, IP1_3_0)
+#define GPSR0_7 F_(DU_DG3, IP0_31_28)
+#define GPSR0_6 F_(DU_DG2, IP0_27_24)
+#define GPSR0_5 F_(DU_DR7, IP0_23_20)
+#define GPSR0_4 F_(DU_DR6, IP0_19_16)
+#define GPSR0_3 F_(DU_DR5, IP0_15_12)
+#define GPSR0_2 F_(DU_DR4, IP0_11_8)
+#define GPSR0_1 F_(DU_DR3, IP0_7_4)
+#define GPSR0_0 F_(DU_DR2, IP0_3_0)
+
+/* GPSR1 */
+#define GPSR1_27 F_(DIGRF_CLKOUT, IP8_31_28)
+#define GPSR1_26 F_(DIGRF_CLKIN, IP8_27_24)
+#define GPSR1_25 F_(CANFD_CLK_A, IP8_23_20)
+#define GPSR1_24 F_(CANFD1_RX, IP8_19_16)
+#define GPSR1_23 F_(CANFD1_TX, IP8_15_12)
+#define GPSR1_22 F_(CANFD0_RX_A, IP8_11_8)
+#define GPSR1_21 F_(CANFD0_TX_A, IP8_7_4)
+#define GPSR1_20 F_(AVB_AVTP_CAPTURE, IP8_3_0)
+#define GPSR1_19 F_(AVB_AVTP_MATCH, IP7_31_28)
+#define GPSR1_18 FM(AVB_LINK)
+#define GPSR1_17 FM(AVB_PHY_INT)
+#define GPSR1_16 FM(AVB_MAGIC)
+#define GPSR1_15 FM(AVB_MDC)
+#define GPSR1_14 FM(AVB_MDIO)
+#define GPSR1_13 FM(AVB_TXCREFCLK)
+#define GPSR1_12 FM(AVB_TD3)
+#define GPSR1_11 FM(AVB_TD2)
+#define GPSR1_10 FM(AVB_TD1)
+#define GPSR1_9 FM(AVB_TD0)
+#define GPSR1_8 FM(AVB_TXC)
+#define GPSR1_7 FM(AVB_TX_CTL)
+#define GPSR1_6 FM(AVB_RD3)
+#define GPSR1_5 FM(AVB_RD2)
+#define GPSR1_4 FM(AVB_RD1)
+#define GPSR1_3 FM(AVB_RD0)
+#define GPSR1_2 FM(AVB_RXC)
+#define GPSR1_1 FM(AVB_RX_CTL)
+#define GPSR1_0 F_(IRQ0, IP2_27_24)
+
+/* GPSR2 */
+#define GPSR2_29 F_(FSO_TOE_N, IP10_19_16)
+#define GPSR2_28 F_(FSO_CFE_1_N, IP10_15_12)
+#define GPSR2_27 F_(FSO_CFE_0_N, IP10_11_8)
+#define GPSR2_26 F_(SDA3, IP10_7_4)
+#define GPSR2_25 F_(SCL3, IP10_3_0)
+#define GPSR2_24 F_(MSIOF0_SS2, IP9_31_28)
+#define GPSR2_23 F_(MSIOF0_SS1, IP9_27_24)
+#define GPSR2_22 F_(MSIOF0_SYNC, IP9_23_20)
+#define GPSR2_21 F_(MSIOF0_SCK, IP9_19_16)
+#define GPSR2_20 F_(MSIOF0_TXD, IP9_15_12)
+#define GPSR2_19 F_(MSIOF0_RXD, IP9_11_8)
+#define GPSR2_18 F_(IRQ5, IP9_7_4)
+#define GPSR2_17 F_(IRQ4, IP9_3_0)
+#define GPSR2_16 F_(VI0_FIELD, IP4_31_28)
+#define GPSR2_15 F_(VI0_DATA11, IP4_27_24)
+#define GPSR2_14 F_(VI0_DATA10, IP4_23_20)
+#define GPSR2_13 F_(VI0_DATA9, IP4_19_16)
+#define GPSR2_12 F_(VI0_DATA8, IP4_15_12)
+#define GPSR2_11 F_(VI0_DATA7, IP4_11_8)
+#define GPSR2_10 F_(VI0_DATA6, IP4_7_4)
+#define GPSR2_9 F_(VI0_DATA5, IP4_3_0)
+#define GPSR2_8 F_(VI0_DATA4, IP3_31_28)
+#define GPSR2_7 F_(VI0_DATA3, IP3_27_24)
+#define GPSR2_6 F_(VI0_DATA2, IP3_23_20)
+#define GPSR2_5 F_(VI0_DATA1, IP3_19_16)
+#define GPSR2_4 F_(VI0_DATA0, IP3_15_12)
+#define GPSR2_3 F_(VI0_VSYNC_N, IP3_11_8)
+#define GPSR2_2 F_(VI0_HSYNC_N, IP3_7_4)
+#define GPSR2_1 F_(VI0_CLKENB, IP3_3_0)
+#define GPSR2_0 F_(VI0_CLK, IP2_31_28)
+
+/* GPSR3 */
+#define GPSR3_16 F_(VI1_FIELD, IP7_3_0)
+#define GPSR3_15 F_(VI1_DATA11, IP6_31_28)
+#define GPSR3_14 F_(VI1_DATA10, IP6_27_24)
+#define GPSR3_13 F_(VI1_DATA9, IP6_23_20)
+#define GPSR3_12 F_(VI1_DATA8, IP6_19_16)
+#define GPSR3_11 F_(VI1_DATA7, IP6_15_12)
+#define GPSR3_10 F_(VI1_DATA6, IP6_11_8)
+#define GPSR3_9 F_(VI1_DATA5, IP6_7_4)
+#define GPSR3_8 F_(VI1_DATA4, IP6_3_0)
+#define GPSR3_7 F_(VI1_DATA3, IP5_31_28)
+#define GPSR3_6 F_(VI1_DATA2, IP5_27_24)
+#define GPSR3_5 F_(VI1_DATA1, IP5_23_20)
+#define GPSR3_4 F_(VI1_DATA0, IP5_19_16)
+#define GPSR3_3 F_(VI1_VSYNC_N, IP5_15_12)
+#define GPSR3_2 F_(VI1_HSYNC_N, IP5_11_8)
+#define GPSR3_1 F_(VI1_CLKENB, IP5_7_4)
+#define GPSR3_0 F_(VI1_CLK, IP5_3_0)
+
+/* GPSR4 */
+#define GPSR4_24 FM(GETHER_LINK_A)
+#define GPSR4_23 FM(GETHER_PHY_INT_A)
+#define GPSR4_22 FM(GETHER_MAGIC)
+#define GPSR4_21 FM(GETHER_MDC_A)
+#define GPSR4_20 FM(GETHER_MDIO_A)
+#define GPSR4_19 FM(GETHER_TXCREFCLK_MEGA)
+#define GPSR4_18 FM(GETHER_TXCREFCLK)
+#define GPSR4_17 FM(GETHER_TD3)
+#define GPSR4_16 FM(GETHER_TD2)
+#define GPSR4_15 FM(GETHER_TD1)
+#define GPSR4_14 FM(GETHER_TD0)
+#define GPSR4_13 FM(GETHER_TXC)
+#define GPSR4_12 FM(GETHER_TX_CTL)
+#define GPSR4_11 FM(GETHER_RD3)
+#define GPSR4_10 FM(GETHER_RD2)
+#define GPSR4_9 FM(GETHER_RD1)
+#define GPSR4_8 FM(GETHER_RD0)
+#define GPSR4_7 FM(GETHER_RXC)
+#define GPSR4_6 FM(GETHER_RX_CTL)
+#define GPSR4_5 F_(SDA2, IP7_27_24)
+#define GPSR4_4 F_(SCL2, IP7_23_20)
+#define GPSR4_3 F_(SDA1, IP7_19_16)
+#define GPSR4_2 F_(SCL1, IP7_15_12)
+#define GPSR4_1 F_(SDA0, IP7_11_8)
+#define GPSR4_0 F_(SCL0, IP7_7_4)
+
+/* GPSR5 */
+#define GPSR5_14 FM(RPC_INT_N)
+#define GPSR5_13 FM(RPC_WP_N)
+#define GPSR5_12 FM(RPC_RESET_N)
+#define GPSR5_11 FM(QSPI1_SSL)
+#define GPSR5_10 FM(QSPI1_IO3)
+#define GPSR5_9 FM(QSPI1_IO2)
+#define GPSR5_8 FM(QSPI1_MISO_IO1)
+#define GPSR5_7 FM(QSPI1_MOSI_IO0)
+#define GPSR5_6 FM(QSPI1_SPCLK)
+#define GPSR5_5 FM(QSPI0_SSL)
+#define GPSR5_4 FM(QSPI0_IO3)
+#define GPSR5_3 FM(QSPI0_IO2)
+#define GPSR5_2 FM(QSPI0_MISO_IO1)
+#define GPSR5_1 FM(QSPI0_MOSI_IO0)
+#define GPSR5_0 FM(QSPI0_SPCLK)
+
+
+/* IPSRx */ /* 0 */ /* 1 */ /* 2 */ /* 3 */ /* 4 */ /* 5 */ /* 6 - F */
+#define IP0_3_0 FM(DU_DR2) FM(SCK4) FM(GETHER_RMII_CRS_DV) FM(A0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP0_7_4 FM(DU_DR3) FM(RX4) FM(GETHER_RMII_RX_ER) FM(A1) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP0_11_8 FM(DU_DR4) FM(TX4) FM(GETHER_RMII_RXD0) FM(A2) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP0_15_12 FM(DU_DR5) FM(CTS4_N) FM(GETHER_RMII_RXD1) FM(A3) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP0_19_16 FM(DU_DR6) FM(RTS4_N_TANS) FM(GETHER_RMII_TXD_EN) FM(A4) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP0_23_20 FM(DU_DR7) F_(0, 0) FM(GETHER_RMII_TXD0) FM(A5) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP0_27_24 FM(DU_DG2) F_(0, 0) FM(GETHER_RMII_TXD1) FM(A6) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP0_31_28 FM(DU_DG3) FM(CPG_CPCKOUT) FM(GETHER_RMII_REFCLK) FM(A7) FM(PWMFSW0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP1_3_0 FM(DU_DG4) FM(SCL5) F_(0, 0) FM(A8) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP1_7_4 FM(DU_DG5) FM(SDA5) FM(GETHER_MDC_B) FM(A9) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP1_11_8 FM(DU_DG6) FM(SCIF_CLK_A) FM(GETHER_MDIO_B) FM(A10) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP1_15_12 FM(DU_DG7) FM(HRX0_A) F_(0, 0) FM(A11) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP1_19_16 FM(DU_DB2) FM(HSCK0_A) F_(0, 0) FM(A12) FM(IRQ1) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP1_23_20 FM(DU_DB3) FM(HRTS0_N_A) F_(0, 0) FM(A13) FM(IRQ2) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP1_27_24 FM(DU_DB4) FM(HCTS0_N_A) F_(0, 0) FM(A14) FM(IRQ3) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP1_31_28 FM(DU_DB5) FM(HTX0_A) FM(PWM0_A) FM(A15) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP2_3_0 FM(DU_DB6) FM(MSIOF3_RXD) F_(0, 0) FM(A16) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP2_7_4 FM(DU_DB7) FM(MSIOF3_TXD) F_(0, 0) FM(A17) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP2_11_8 FM(DU_DOTCLKOUT) FM(MSIOF3_SS1) FM(GETHER_LINK_B) FM(A18) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP2_15_12 FM(DU_EXHSYNC_DU_HSYNC) FM(MSIOF3_SS2) FM(GETHER_PHY_INT_B) FM(A19) FM(FXR_TXENA_N) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP2_19_16 FM(DU_EXVSYNC_DU_VSYNC) FM(MSIOF3_SCK) F_(0, 0) F_(0, 0) FM(FXR_TXENB_N) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP2_23_20 FM(DU_EXODDF_DU_ODDF_DISP_CDE) FM(MSIOF3_SYNC) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP2_27_24 FM(IRQ0) FM(CC5_OSCOUT) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP2_31_28 FM(VI0_CLK) FM(MSIOF2_SCK) FM(SCK3) F_(0, 0) FM(HSCK3) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP3_3_0 FM(VI0_CLKENB) FM(MSIOF2_RXD) FM(RX3) FM(RD_WR_N) FM(HCTS3_N) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP3_7_4 FM(VI0_HSYNC_N) FM(MSIOF2_TXD) FM(TX3) F_(0, 0) FM(HRTS3_N) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP3_11_8 FM(VI0_VSYNC_N) FM(MSIOF2_SYNC) FM(CTS3_N) F_(0, 0) FM(HTX3) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP3_15_12 FM(VI0_DATA0) FM(MSIOF2_SS1) FM(RTS3_N_TANS) F_(0, 0) FM(HRX3) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP3_19_16 FM(VI0_DATA1) FM(MSIOF2_SS2) FM(SCK1) F_(0, 0) FM(SPEEDIN_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP3_23_20 FM(VI0_DATA2) FM(AVB_AVTP_PPS) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP3_27_24 FM(VI0_DATA3) FM(HSCK1) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP3_31_28 FM(VI0_DATA4) FM(HRTS1_N) FM(RX1_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP4_3_0 FM(VI0_DATA5) FM(HCTS1_N) FM(TX1_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP4_7_4 FM(VI0_DATA6) FM(HTX1) FM(CTS1_N) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP4_11_8 FM(VI0_DATA7) FM(HRX1) FM(RTS1_N_TANS) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP4_15_12 FM(VI0_DATA8) FM(HSCK2) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP4_19_16 FM(VI0_DATA9) FM(HCTS2_N) FM(PWM1_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP4_23_20 FM(VI0_DATA10) FM(HRTS2_N) FM(PWM2_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP4_27_24 FM(VI0_DATA11) FM(HTX2) FM(PWM3_A) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP4_31_28 FM(VI0_FIELD) FM(HRX2) FM(PWM4_A) FM(CS1_N) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP5_3_0 FM(VI1_CLK) FM(MSIOF1_RXD) F_(0, 0) FM(CS0_N) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP5_7_4 FM(VI1_CLKENB) FM(MSIOF1_TXD) F_(0, 0) FM(D0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP5_11_8 FM(VI1_HSYNC_N) FM(MSIOF1_SCK) F_(0, 0) FM(D1) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP5_15_12 FM(VI1_VSYNC_N) FM(MSIOF1_SYNC) F_(0, 0) FM(D2) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP5_19_16 FM(VI1_DATA0) FM(MSIOF1_SS1) F_(0, 0) FM(D3) FM(MMC_WP) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP5_23_20 FM(VI1_DATA1) FM(MSIOF1_SS2) F_(0, 0) FM(D4) FM(MMC_CD) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP5_27_24 FM(VI1_DATA2) FM(CANFD0_TX_B) F_(0, 0) FM(D5) FM(MMC_DS) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP5_31_28 FM(VI1_DATA3) FM(CANFD0_RX_B) F_(0, 0) FM(D6) FM(MMC_CMD) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP6_3_0 FM(VI1_DATA4) FM(CANFD_CLK_B) F_(0, 0) FM(D7) FM(MMC_D0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP6_7_4 FM(VI1_DATA5) F_(0, 0) F_(0, 0) FM(D8) FM(MMC_D1) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP6_11_8 FM(VI1_DATA6) F_(0, 0) F_(0, 0) FM(D9) FM(MMC_D2) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP6_15_12 FM(VI1_DATA7) F_(0, 0) F_(0, 0) FM(D10) FM(MMC_D3) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP6_19_16 FM(VI1_DATA8) F_(0, 0) F_(0, 0) FM(D11) FM(MMC_CLK) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP6_23_20 FM(VI1_DATA9) FM(TCLK1_A) F_(0, 0) FM(D12) FM(MMC_D4) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP6_27_24 FM(VI1_DATA10) FM(TCLK2_A) F_(0, 0) FM(D13) FM(MMC_D5) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP6_31_28 FM(VI1_DATA11) FM(SCL4) F_(0, 0) FM(D14) FM(MMC_D6) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP7_3_0 FM(VI1_FIELD) FM(SDA4) F_(0, 0) FM(D15) FM(MMC_D7) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP7_7_4 FM(SCL0) F_(0, 0) F_(0, 0) FM(CLKOUT) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP7_11_8 FM(SDA0) F_(0, 0) F_(0, 0) FM(BS_N) FM(SCK0) FM(HSCK0_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP7_15_12 FM(SCL1) F_(0, 0) FM(TPU0TO2) FM(RD_N) FM(CTS0_N) FM(HCTS0_N_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP7_19_16 FM(SDA1) F_(0, 0) FM(TPU0TO3) FM(WE0_N) FM(RTS0_N_TANS) FM(HRTS0_N_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP7_23_20 FM(SCL2) F_(0, 0) F_(0, 0) FM(WE1_N) FM(RX0) FM(HRX0_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP7_27_24 FM(SDA2) F_(0, 0) F_(0, 0) FM(EX_WAIT0) FM(TX0) FM(HTX0_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP7_31_28 FM(AVB_AVTP_MATCH) FM(TPU0TO0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP8_3_0 FM(AVB_AVTP_CAPTURE) FM(TPU0TO1) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP8_7_4 FM(CANFD0_TX_A) FM(FXR_TXDA) FM(PWM0_B) FM(DU_DISP) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP8_11_8 FM(CANFD0_RX_A) FM(RXDA_EXTFXR) FM(PWM1_B) FM(DU_CDE) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP8_15_12 FM(CANFD1_TX) FM(FXR_TXDB) FM(PWM2_B) FM(TCLK1_B) FM(TX1_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP8_19_16 FM(CANFD1_RX) FM(RXDB_EXTFXR) FM(PWM3_B) FM(TCLK2_B) FM(RX1_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP8_23_20 FM(CANFD_CLK_A) FM(CLK_EXTFXR) FM(PWM4_B) FM(SPEEDIN_B) FM(SCIF_CLK_B) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP8_27_24 FM(DIGRF_CLKIN) FM(DIGRF_CLKEN_IN) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP8_31_28 FM(DIGRF_CLKOUT) FM(DIGRF_CLKEN_OUT) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP9_3_0 FM(IRQ4) F_(0, 0) F_(0, 0) FM(VI0_DATA12) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP9_7_4 FM(IRQ5) F_(0, 0) F_(0, 0) FM(VI0_DATA13) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP9_11_8 FM(MSIOF0_RXD) FM(DU_DR0) F_(0, 0) FM(VI0_DATA14) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP9_15_12 FM(MSIOF0_TXD) FM(DU_DR1) F_(0, 0) FM(VI0_DATA15) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP9_19_16 FM(MSIOF0_SCK) FM(DU_DG0) F_(0, 0) FM(VI0_DATA16) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP9_23_20 FM(MSIOF0_SYNC) FM(DU_DG1) F_(0, 0) FM(VI0_DATA17) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP9_27_24 FM(MSIOF0_SS1) FM(DU_DB0) FM(TCLK3) FM(VI0_DATA18) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP9_31_28 FM(MSIOF0_SS2) FM(DU_DB1) FM(TCLK4) FM(VI0_DATA19) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP10_3_0 FM(SCL3) F_(0, 0) F_(0, 0) FM(VI0_DATA20) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP10_7_4 FM(SDA3) F_(0, 0) F_(0, 0) FM(VI0_DATA21) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP10_11_8 FM(FSO_CFE_0_N) F_(0, 0) F_(0, 0) FM(VI0_DATA22) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP10_15_12 FM(FSO_CFE_1_N) F_(0, 0) F_(0, 0) FM(VI0_DATA23) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP10_19_16 FM(FSO_TOE_N) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP10_23_20 F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP10_27_24 F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+#define IP10_31_28 F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0) F_(0, 0)
+
+#define PINMUX_GPSR \
+\
+ GPSR2_29 \
+ GPSR2_28 \
+ GPSR1_27 GPSR2_27 \
+ GPSR1_26 GPSR2_26 \
+ GPSR1_25 GPSR2_25 \
+ GPSR1_24 GPSR2_24 GPSR4_24 \
+ GPSR1_23 GPSR2_23 GPSR4_23 \
+ GPSR1_22 GPSR2_22 GPSR4_22 \
+GPSR0_21 GPSR1_21 GPSR2_21 GPSR4_21 \
+GPSR0_20 GPSR1_20 GPSR2_20 GPSR4_20 \
+GPSR0_19 GPSR1_19 GPSR2_19 GPSR4_19 \
+GPSR0_18 GPSR1_18 GPSR2_18 GPSR4_18 \
+GPSR0_17 GPSR1_17 GPSR2_17 GPSR4_17 \
+GPSR0_16 GPSR1_16 GPSR2_16 GPSR3_16 GPSR4_16 \
+GPSR0_15 GPSR1_15 GPSR2_15 GPSR3_15 GPSR4_15 \
+GPSR0_14 GPSR1_14 GPSR2_14 GPSR3_14 GPSR4_14 GPSR5_14 \
+GPSR0_13 GPSR1_13 GPSR2_13 GPSR3_13 GPSR4_13 GPSR5_13 \
+GPSR0_12 GPSR1_12 GPSR2_12 GPSR3_12 GPSR4_12 GPSR5_12 \
+GPSR0_11 GPSR1_11 GPSR2_11 GPSR3_11 GPSR4_11 GPSR5_11 \
+GPSR0_10 GPSR1_10 GPSR2_10 GPSR3_10 GPSR4_10 GPSR5_10 \
+GPSR0_9 GPSR1_9 GPSR2_9 GPSR3_9 GPSR4_9 GPSR5_9 \
+GPSR0_8 GPSR1_8 GPSR2_8 GPSR3_8 GPSR4_8 GPSR5_8 \
+GPSR0_7 GPSR1_7 GPSR2_7 GPSR3_7 GPSR4_7 GPSR5_7 \
+GPSR0_6 GPSR1_6 GPSR2_6 GPSR3_6 GPSR4_6 GPSR5_6 \
+GPSR0_5 GPSR1_5 GPSR2_5 GPSR3_5 GPSR4_5 GPSR5_5 \
+GPSR0_4 GPSR1_4 GPSR2_4 GPSR3_4 GPSR4_4 GPSR5_4 \
+GPSR0_3 GPSR1_3 GPSR2_3 GPSR3_3 GPSR4_3 GPSR5_3 \
+GPSR0_2 GPSR1_2 GPSR2_2 GPSR3_2 GPSR4_2 GPSR5_2 \
+GPSR0_1 GPSR1_1 GPSR2_1 GPSR3_1 GPSR4_1 GPSR5_1 \
+GPSR0_0 GPSR1_0 GPSR2_0 GPSR3_0 GPSR4_0 GPSR5_0
+
+#define PINMUX_IPSR \
+\
+FM(IP0_3_0) IP0_3_0 FM(IP1_3_0) IP1_3_0 FM(IP2_3_0) IP2_3_0 FM(IP3_3_0) IP3_3_0 \
+FM(IP0_7_4) IP0_7_4 FM(IP1_7_4) IP1_7_4 FM(IP2_7_4) IP2_7_4 FM(IP3_7_4) IP3_7_4 \
+FM(IP0_11_8) IP0_11_8 FM(IP1_11_8) IP1_11_8 FM(IP2_11_8) IP2_11_8 FM(IP3_11_8) IP3_11_8 \
+FM(IP0_15_12) IP0_15_12 FM(IP1_15_12) IP1_15_12 FM(IP2_15_12) IP2_15_12 FM(IP3_15_12) IP3_15_12 \
+FM(IP0_19_16) IP0_19_16 FM(IP1_19_16) IP1_19_16 FM(IP2_19_16) IP2_19_16 FM(IP3_19_16) IP3_19_16 \
+FM(IP0_23_20) IP0_23_20 FM(IP1_23_20) IP1_23_20 FM(IP2_23_20) IP2_23_20 FM(IP3_23_20) IP3_23_20 \
+FM(IP0_27_24) IP0_27_24 FM(IP1_27_24) IP1_27_24 FM(IP2_27_24) IP2_27_24 FM(IP3_27_24) IP3_27_24 \
+FM(IP0_31_28) IP0_31_28 FM(IP1_31_28) IP1_31_28 FM(IP2_31_28) IP2_31_28 FM(IP3_31_28) IP3_31_28 \
+\
+FM(IP4_3_0) IP4_3_0 FM(IP5_3_0) IP5_3_0 FM(IP6_3_0) IP6_3_0 FM(IP7_3_0) IP7_3_0 \
+FM(IP4_7_4) IP4_7_4 FM(IP5_7_4) IP5_7_4 FM(IP6_7_4) IP6_7_4 FM(IP7_7_4) IP7_7_4 \
+FM(IP4_11_8) IP4_11_8 FM(IP5_11_8) IP5_11_8 FM(IP6_11_8) IP6_11_8 FM(IP7_11_8) IP7_11_8 \
+FM(IP4_15_12) IP4_15_12 FM(IP5_15_12) IP5_15_12 FM(IP6_15_12) IP6_15_12 FM(IP7_15_12) IP7_15_12 \
+FM(IP4_19_16) IP4_19_16 FM(IP5_19_16) IP5_19_16 FM(IP6_19_16) IP6_19_16 FM(IP7_19_16) IP7_19_16 \
+FM(IP4_23_20) IP4_23_20 FM(IP5_23_20) IP5_23_20 FM(IP6_23_20) IP6_23_20 FM(IP7_23_20) IP7_23_20 \
+FM(IP4_27_24) IP4_27_24 FM(IP5_27_24) IP5_27_24 FM(IP6_27_24) IP6_27_24 FM(IP7_27_24) IP7_27_24 \
+FM(IP4_31_28) IP4_31_28 FM(IP5_31_28) IP5_31_28 FM(IP6_31_28) IP6_31_28 FM(IP7_31_28) IP7_31_28 \
+\
+FM(IP8_3_0) IP8_3_0 FM(IP9_3_0) IP9_3_0 FM(IP10_3_0) IP10_3_0 \
+FM(IP8_7_4) IP8_7_4 FM(IP9_7_4) IP9_7_4 FM(IP10_7_4) IP10_7_4 \
+FM(IP8_11_8) IP8_11_8 FM(IP9_11_8) IP9_11_8 FM(IP10_11_8) IP10_11_8 \
+FM(IP8_15_12) IP8_15_12 FM(IP9_15_12) IP9_15_12 FM(IP10_15_12) IP10_15_12 \
+FM(IP8_19_16) IP8_19_16 FM(IP9_19_16) IP9_19_16 FM(IP10_19_16) IP10_19_16 \
+FM(IP8_23_20) IP8_23_20 FM(IP9_23_20) IP9_23_20 FM(IP10_23_20) IP10_23_20 \
+FM(IP8_27_24) IP8_27_24 FM(IP9_27_24) IP9_27_24 FM(IP10_27_24) IP10_27_24 \
+FM(IP8_31_28) IP8_31_28 FM(IP9_31_28) IP9_31_28 FM(IP10_31_28) IP10_31_28
+
+/* MOD_SEL0 */ /* 0 */ /* 1 */
+#define MOD_SEL0_11 FM(SEL_CANFD0_0) FM(SEL_CANFD0_1)
+#define MOD_SEL0_10 FM(SEL_GETHER_0) FM(SEL_GETHER_1)
+#define MOD_SEL0_9 FM(SEL_HSCIF0_0) FM(SEL_HSCIF0_1)
+#define MOD_SEL0_8 FM(SEL_PWM0_0) FM(SEL_PWM0_1)
+#define MOD_SEL0_7 FM(SEL_PWM1_0) FM(SEL_PWM1_1)
+#define MOD_SEL0_6 FM(SEL_PWM2_0) FM(SEL_PWM2_1)
+#define MOD_SEL0_5 FM(SEL_PWM3_0) FM(SEL_PWM3_1)
+#define MOD_SEL0_4 FM(SEL_PWM4_0) FM(SEL_PWM4_1)
+#define MOD_SEL0_2 FM(SEL_RSP_0) FM(SEL_RSP_1)
+#define MOD_SEL0_1 FM(SEL_SCIF1_0) FM(SEL_SCIF1_1)
+#define MOD_SEL0_0 FM(SEL_TMU_0) FM(SEL_TMU_1)
+
+#define PINMUX_MOD_SELS \
+\
+MOD_SEL0_11 \
+MOD_SEL0_10 \
+MOD_SEL0_9 \
+MOD_SEL0_8 \
+MOD_SEL0_7 \
+MOD_SEL0_6 \
+MOD_SEL0_5 \
+MOD_SEL0_4 \
+MOD_SEL0_2 \
+MOD_SEL0_1 \
+MOD_SEL0_0
+
+enum {
+ PINMUX_RESERVED = 0,
+
+ PINMUX_DATA_BEGIN,
+ GP_ALL(DATA),
+ PINMUX_DATA_END,
+
+#define F_(x, y)
+#define FM(x) FN_##x,
+ PINMUX_FUNCTION_BEGIN,
+ GP_ALL(FN),
+ PINMUX_GPSR
+ PINMUX_IPSR
+ PINMUX_MOD_SELS
+ PINMUX_FUNCTION_END,
+#undef F_
+#undef FM
+
+#define F_(x, y)
+#define FM(x) x##_MARK,
+ PINMUX_MARK_BEGIN,
+ PINMUX_GPSR
+ PINMUX_IPSR
+ PINMUX_MOD_SELS
+ PINMUX_MARK_END,
+#undef F_
+#undef FM
+};
+
+static const u16 pinmux_data[] = {
+ PINMUX_DATA_GP_ALL(),
+
+ PINMUX_SINGLE(AVB_RX_CTL),
+ PINMUX_SINGLE(AVB_RXC),
+ PINMUX_SINGLE(AVB_RD0),
+ PINMUX_SINGLE(AVB_RD1),
+ PINMUX_SINGLE(AVB_RD2),
+ PINMUX_SINGLE(AVB_RD3),
+ PINMUX_SINGLE(AVB_TX_CTL),
+ PINMUX_SINGLE(AVB_TXC),
+ PINMUX_SINGLE(AVB_TD0),
+ PINMUX_SINGLE(AVB_TD1),
+ PINMUX_SINGLE(AVB_TD2),
+ PINMUX_SINGLE(AVB_TD3),
+ PINMUX_SINGLE(AVB_TXCREFCLK),
+ PINMUX_SINGLE(AVB_MDIO),
+ PINMUX_SINGLE(AVB_MDC),
+ PINMUX_SINGLE(AVB_MAGIC),
+ PINMUX_SINGLE(AVB_PHY_INT),
+ PINMUX_SINGLE(AVB_LINK),
+
+ PINMUX_SINGLE(GETHER_RX_CTL),
+ PINMUX_SINGLE(GETHER_RXC),
+ PINMUX_SINGLE(GETHER_RD0),
+ PINMUX_SINGLE(GETHER_RD1),
+ PINMUX_SINGLE(GETHER_RD2),
+ PINMUX_SINGLE(GETHER_RD3),
+ PINMUX_SINGLE(GETHER_TX_CTL),
+ PINMUX_SINGLE(GETHER_TXC),
+ PINMUX_SINGLE(GETHER_TD0),
+ PINMUX_SINGLE(GETHER_TD1),
+ PINMUX_SINGLE(GETHER_TD2),
+ PINMUX_SINGLE(GETHER_TD3),
+ PINMUX_SINGLE(GETHER_TXCREFCLK),
+ PINMUX_SINGLE(GETHER_TXCREFCLK_MEGA),
+ PINMUX_SINGLE(GETHER_MDIO_A),
+ PINMUX_SINGLE(GETHER_MDC_A),
+ PINMUX_SINGLE(GETHER_MAGIC),
+ PINMUX_SINGLE(GETHER_PHY_INT_A),
+ PINMUX_SINGLE(GETHER_LINK_A),
+
+ PINMUX_SINGLE(QSPI0_SPCLK),
+ PINMUX_SINGLE(QSPI0_MOSI_IO0),
+ PINMUX_SINGLE(QSPI0_MISO_IO1),
+ PINMUX_SINGLE(QSPI0_IO2),
+ PINMUX_SINGLE(QSPI0_IO3),
+ PINMUX_SINGLE(QSPI0_SSL),
+ PINMUX_SINGLE(QSPI1_SPCLK),
+ PINMUX_SINGLE(QSPI1_MOSI_IO0),
+ PINMUX_SINGLE(QSPI1_MISO_IO1),
+ PINMUX_SINGLE(QSPI1_IO2),
+ PINMUX_SINGLE(QSPI1_IO3),
+ PINMUX_SINGLE(QSPI1_SSL),
+ PINMUX_SINGLE(RPC_RESET_N),
+ PINMUX_SINGLE(RPC_WP_N),
+ PINMUX_SINGLE(RPC_INT_N),
+
+ /* IPSR0 */
+ PINMUX_IPSR_GPSR(IP0_3_0, DU_DR2),
+ PINMUX_IPSR_GPSR(IP0_3_0, SCK4),
+ PINMUX_IPSR_GPSR(IP0_3_0, GETHER_RMII_CRS_DV),
+ PINMUX_IPSR_GPSR(IP0_3_0, A0),
+
+ PINMUX_IPSR_GPSR(IP0_7_4, DU_DR3),
+ PINMUX_IPSR_GPSR(IP0_7_4, RX4),
+ PINMUX_IPSR_GPSR(IP0_7_4, GETHER_RMII_RX_ER),
+ PINMUX_IPSR_GPSR(IP0_7_4, A1),
+
+ PINMUX_IPSR_GPSR(IP0_11_8, DU_DR4),
+ PINMUX_IPSR_GPSR(IP0_11_8, TX4),
+ PINMUX_IPSR_GPSR(IP0_11_8, GETHER_RMII_RXD0),
+ PINMUX_IPSR_GPSR(IP0_11_8, A2),
+
+ PINMUX_IPSR_GPSR(IP0_15_12, DU_DR5),
+ PINMUX_IPSR_GPSR(IP0_15_12, CTS4_N),
+ PINMUX_IPSR_GPSR(IP0_15_12, GETHER_RMII_RXD1),
+ PINMUX_IPSR_GPSR(IP0_15_12, A3),
+
+ PINMUX_IPSR_GPSR(IP0_19_16, DU_DR6),
+ PINMUX_IPSR_GPSR(IP0_19_16, RTS4_N_TANS),
+ PINMUX_IPSR_GPSR(IP0_19_16, GETHER_RMII_TXD_EN),
+ PINMUX_IPSR_GPSR(IP0_19_16, A4),
+
+ PINMUX_IPSR_GPSR(IP0_23_20, DU_DR7),
+ PINMUX_IPSR_GPSR(IP0_23_20, GETHER_RMII_TXD0),
+ PINMUX_IPSR_GPSR(IP0_23_20, A5),
+
+ PINMUX_IPSR_GPSR(IP0_27_24, DU_DG2),
+ PINMUX_IPSR_GPSR(IP0_27_24, GETHER_RMII_TXD1),
+ PINMUX_IPSR_GPSR(IP0_27_24, A6),
+
+ PINMUX_IPSR_GPSR(IP0_31_28, DU_DG3),
+ PINMUX_IPSR_GPSR(IP0_31_28, CPG_CPCKOUT),
+ PINMUX_IPSR_GPSR(IP0_31_28, GETHER_RMII_REFCLK),
+ PINMUX_IPSR_GPSR(IP0_31_28, A7),
+ PINMUX_IPSR_GPSR(IP0_31_28, PWMFSW0),
+
+ /* IPSR1 */
+ PINMUX_IPSR_GPSR(IP1_3_0, DU_DG4),
+ PINMUX_IPSR_GPSR(IP1_3_0, SCL5),
+ PINMUX_IPSR_GPSR(IP1_3_0, A8),
+
+ PINMUX_IPSR_GPSR(IP1_7_4, DU_DG5),
+ PINMUX_IPSR_GPSR(IP1_7_4, SDA5),
+ PINMUX_IPSR_MSEL(IP1_7_4, GETHER_MDC_B, SEL_GETHER_1),
+ PINMUX_IPSR_GPSR(IP1_7_4, A9),
+
+ PINMUX_IPSR_GPSR(IP1_11_8, DU_DG6),
+ PINMUX_IPSR_MSEL(IP1_11_8, SCIF_CLK_A, SEL_HSCIF0_0),
+ PINMUX_IPSR_MSEL(IP1_11_8, GETHER_MDIO_B, SEL_GETHER_1),
+ PINMUX_IPSR_GPSR(IP1_11_8, A10),
+
+ PINMUX_IPSR_GPSR(IP1_15_12, DU_DG7),
+ PINMUX_IPSR_MSEL(IP1_15_12, HRX0_A, SEL_HSCIF0_0),
+ PINMUX_IPSR_GPSR(IP1_15_12, A11),
+
+ PINMUX_IPSR_GPSR(IP1_19_16, DU_DB2),
+ PINMUX_IPSR_MSEL(IP1_19_16, HSCK0_A, SEL_HSCIF0_0),
+ PINMUX_IPSR_GPSR(IP1_19_16, A12),
+ PINMUX_IPSR_GPSR(IP1_19_16, IRQ1),
+
+ PINMUX_IPSR_GPSR(IP1_23_20, DU_DB3),
+ PINMUX_IPSR_MSEL(IP1_23_20, HRTS0_N_A, SEL_HSCIF0_0),
+ PINMUX_IPSR_GPSR(IP1_23_20, A13),
+ PINMUX_IPSR_GPSR(IP1_23_20, IRQ2),
+
+ PINMUX_IPSR_GPSR(IP1_27_24, DU_DB4),
+ PINMUX_IPSR_MSEL(IP1_27_24, HCTS0_N_A, SEL_HSCIF0_0),
+ PINMUX_IPSR_GPSR(IP1_27_24, A14),
+ PINMUX_IPSR_GPSR(IP1_27_24, IRQ3),
+
+ PINMUX_IPSR_GPSR(IP1_31_28, DU_DB5),
+ PINMUX_IPSR_MSEL(IP1_31_28, HTX0_A, SEL_HSCIF0_0),
+ PINMUX_IPSR_MSEL(IP1_31_28, PWM0_A, SEL_PWM0_0),
+ PINMUX_IPSR_GPSR(IP1_31_28, A15),
+
+ /* IPSR2 */
+ PINMUX_IPSR_GPSR(IP2_3_0, DU_DB6),
+ PINMUX_IPSR_GPSR(IP2_3_0, MSIOF3_RXD),
+ PINMUX_IPSR_GPSR(IP2_3_0, A16),
+
+ PINMUX_IPSR_GPSR(IP2_7_4, DU_DB7),
+ PINMUX_IPSR_GPSR(IP2_7_4, MSIOF3_TXD),
+ PINMUX_IPSR_GPSR(IP2_7_4, A17),
+
+ PINMUX_IPSR_GPSR(IP2_11_8, DU_DOTCLKOUT),
+ PINMUX_IPSR_GPSR(IP2_11_8, MSIOF3_SS1),
+ PINMUX_IPSR_MSEL(IP2_11_8, GETHER_LINK_B, SEL_GETHER_1),
+ PINMUX_IPSR_GPSR(IP2_11_8, A18),
+
+ PINMUX_IPSR_GPSR(IP2_15_12, DU_EXHSYNC_DU_HSYNC),
+ PINMUX_IPSR_GPSR(IP2_15_12, MSIOF3_SS2),
+ PINMUX_IPSR_MSEL(IP2_15_12, GETHER_PHY_INT_B, SEL_GETHER_1),
+ PINMUX_IPSR_GPSR(IP2_15_12, A19),
+ PINMUX_IPSR_GPSR(IP2_15_12, FXR_TXENA_N),
+
+ PINMUX_IPSR_GPSR(IP2_19_16, DU_EXVSYNC_DU_VSYNC),
+ PINMUX_IPSR_GPSR(IP2_19_16, MSIOF3_SCK),
+ PINMUX_IPSR_GPSR(IP2_19_16, FXR_TXENB_N),
+
+ PINMUX_IPSR_GPSR(IP2_23_20, DU_EXODDF_DU_ODDF_DISP_CDE),
+ PINMUX_IPSR_GPSR(IP2_23_20, MSIOF3_SYNC),
+
+ PINMUX_IPSR_GPSR(IP2_27_24, IRQ0),
+ PINMUX_IPSR_GPSR(IP2_27_24, CC5_OSCOUT),
+
+ PINMUX_IPSR_GPSR(IP2_31_28, VI0_CLK),
+ PINMUX_IPSR_GPSR(IP2_31_28, MSIOF2_SCK),
+ PINMUX_IPSR_GPSR(IP2_31_28, SCK3),
+ PINMUX_IPSR_GPSR(IP2_31_28, HSCK3),
+
+ /* IPSR3 */
+ PINMUX_IPSR_GPSR(IP3_3_0, VI0_CLKENB),
+ PINMUX_IPSR_GPSR(IP3_3_0, MSIOF2_RXD),
+ PINMUX_IPSR_GPSR(IP3_3_0, RX3),
+ PINMUX_IPSR_GPSR(IP3_3_0, RD_WR_N),
+ PINMUX_IPSR_GPSR(IP3_3_0, HCTS3_N),
+
+ PINMUX_IPSR_GPSR(IP3_7_4, VI0_HSYNC_N),
+ PINMUX_IPSR_GPSR(IP3_7_4, MSIOF2_TXD),
+ PINMUX_IPSR_GPSR(IP3_7_4, TX3),
+ PINMUX_IPSR_GPSR(IP3_7_4, HRTS3_N),
+
+ PINMUX_IPSR_GPSR(IP3_11_8, VI0_VSYNC_N),
+ PINMUX_IPSR_GPSR(IP3_11_8, MSIOF2_SYNC),
+ PINMUX_IPSR_GPSR(IP3_11_8, CTS3_N),
+ PINMUX_IPSR_GPSR(IP3_11_8, HTX3),
+
+ PINMUX_IPSR_GPSR(IP3_15_12, VI0_DATA0),
+ PINMUX_IPSR_GPSR(IP3_15_12, MSIOF2_SS1),
+ PINMUX_IPSR_GPSR(IP3_15_12, RTS3_N_TANS),
+ PINMUX_IPSR_GPSR(IP3_15_12, HRX3),
+
+ PINMUX_IPSR_GPSR(IP3_19_16, VI0_DATA1),
+ PINMUX_IPSR_GPSR(IP3_19_16, MSIOF2_SS2),
+ PINMUX_IPSR_GPSR(IP3_19_16, SCK1),
+ PINMUX_IPSR_MSEL(IP3_19_16, SPEEDIN_A, SEL_RSP_0),
+
+ PINMUX_IPSR_GPSR(IP3_23_20, VI0_DATA2),
+ PINMUX_IPSR_GPSR(IP3_23_20, AVB_AVTP_PPS),
+
+ PINMUX_IPSR_GPSR(IP3_27_24, VI0_DATA3),
+ PINMUX_IPSR_GPSR(IP3_27_24, HSCK1),
+
+ PINMUX_IPSR_GPSR(IP3_31_28, VI0_DATA4),
+ PINMUX_IPSR_GPSR(IP3_31_28, HRTS1_N),
+ PINMUX_IPSR_MSEL(IP3_31_28, RX1_A, SEL_SCIF1_0),
+
+ /* IPSR4 */
+ PINMUX_IPSR_GPSR(IP4_3_0, VI0_DATA5),
+ PINMUX_IPSR_GPSR(IP4_3_0, HCTS1_N),
+ PINMUX_IPSR_MSEL(IP4_3_0, TX1_A, SEL_SCIF1_0),
+
+ PINMUX_IPSR_GPSR(IP4_7_4, VI0_DATA6),
+ PINMUX_IPSR_GPSR(IP4_7_4, HTX1),
+ PINMUX_IPSR_GPSR(IP4_7_4, CTS1_N),
+
+ PINMUX_IPSR_GPSR(IP4_11_8, VI0_DATA7),
+ PINMUX_IPSR_GPSR(IP4_11_8, HRX1),
+ PINMUX_IPSR_GPSR(IP4_11_8, RTS1_N_TANS),
+
+ PINMUX_IPSR_GPSR(IP4_15_12, VI0_DATA8),
+ PINMUX_IPSR_GPSR(IP4_15_12, HSCK2),
+
+ PINMUX_IPSR_GPSR(IP4_19_16, VI0_DATA9),
+ PINMUX_IPSR_GPSR(IP4_19_16, HCTS2_N),
+ PINMUX_IPSR_MSEL(IP4_19_16, PWM1_A, SEL_PWM1_0),
+
+ PINMUX_IPSR_GPSR(IP4_23_20, VI0_DATA10),
+ PINMUX_IPSR_GPSR(IP4_23_20, HRTS2_N),
+ PINMUX_IPSR_MSEL(IP4_23_20, PWM2_A, SEL_PWM2_0),
+
+ PINMUX_IPSR_GPSR(IP4_27_24, VI0_DATA11),
+ PINMUX_IPSR_GPSR(IP4_27_24, HTX2),
+ PINMUX_IPSR_MSEL(IP4_27_24, PWM3_A, SEL_PWM3_0),
+
+ PINMUX_IPSR_GPSR(IP4_31_28, VI0_FIELD),
+ PINMUX_IPSR_GPSR(IP4_31_28, HRX2),
+ PINMUX_IPSR_MSEL(IP4_31_28, PWM4_A, SEL_PWM4_0),
+ PINMUX_IPSR_GPSR(IP4_31_28, CS1_N),
+
+ /* IPSR5 */
+ PINMUX_IPSR_GPSR(IP5_3_0, VI1_CLK),
+ PINMUX_IPSR_GPSR(IP5_3_0, MSIOF1_RXD),
+ PINMUX_IPSR_GPSR(IP5_3_0, CS0_N),
+
+ PINMUX_IPSR_GPSR(IP5_7_4, VI1_CLKENB),
+ PINMUX_IPSR_GPSR(IP5_7_4, MSIOF1_TXD),
+ PINMUX_IPSR_GPSR(IP5_7_4, D0),
+
+ PINMUX_IPSR_GPSR(IP5_11_8, VI1_HSYNC_N),
+ PINMUX_IPSR_GPSR(IP5_11_8, MSIOF1_SCK),
+ PINMUX_IPSR_GPSR(IP5_11_8, D1),
+
+ PINMUX_IPSR_GPSR(IP5_15_12, VI1_VSYNC_N),
+ PINMUX_IPSR_GPSR(IP5_15_12, MSIOF1_SYNC),
+ PINMUX_IPSR_GPSR(IP5_15_12, D2),
+
+ PINMUX_IPSR_GPSR(IP5_19_16, VI1_DATA0),
+ PINMUX_IPSR_GPSR(IP5_19_16, MSIOF1_SS1),
+ PINMUX_IPSR_GPSR(IP5_19_16, D3),
+ PINMUX_IPSR_GPSR(IP5_19_16, MMC_WP),
+
+ PINMUX_IPSR_GPSR(IP5_23_20, VI1_DATA1),
+ PINMUX_IPSR_GPSR(IP5_23_20, MSIOF1_SS2),
+ PINMUX_IPSR_GPSR(IP5_23_20, D4),
+ PINMUX_IPSR_GPSR(IP5_23_20, MMC_CD),
+
+ PINMUX_IPSR_GPSR(IP5_27_24, VI1_DATA2),
+ PINMUX_IPSR_MSEL(IP5_27_24, CANFD0_TX_B, SEL_CANFD0_1),
+ PINMUX_IPSR_GPSR(IP5_27_24, D5),
+ PINMUX_IPSR_GPSR(IP5_27_24, MMC_DS),
+
+ PINMUX_IPSR_GPSR(IP5_31_28, VI1_DATA3),
+ PINMUX_IPSR_MSEL(IP5_31_28, CANFD0_RX_B, SEL_CANFD0_1),
+ PINMUX_IPSR_GPSR(IP5_31_28, D6),
+ PINMUX_IPSR_GPSR(IP5_31_28, MMC_CMD),
+
+ /* IPSR6 */
+ PINMUX_IPSR_GPSR(IP6_3_0, VI1_DATA4),
+ PINMUX_IPSR_MSEL(IP6_3_0, CANFD_CLK_B, SEL_CANFD0_1),
+ PINMUX_IPSR_GPSR(IP6_3_0, D7),
+ PINMUX_IPSR_GPSR(IP6_3_0, MMC_D0),
+
+ PINMUX_IPSR_GPSR(IP6_7_4, VI1_DATA5),
+ PINMUX_IPSR_GPSR(IP6_7_4, D8),
+ PINMUX_IPSR_GPSR(IP6_7_4, MMC_D1),
+
+ PINMUX_IPSR_GPSR(IP6_11_8, VI1_DATA6),
+ PINMUX_IPSR_GPSR(IP6_11_8, D9),
+ PINMUX_IPSR_GPSR(IP6_11_8, MMC_D2),
+
+ PINMUX_IPSR_GPSR(IP6_15_12, VI1_DATA7),
+ PINMUX_IPSR_GPSR(IP6_15_12, D10),
+ PINMUX_IPSR_GPSR(IP6_15_12, MMC_D3),
+
+ PINMUX_IPSR_GPSR(IP6_19_16, VI1_DATA8),
+ PINMUX_IPSR_GPSR(IP6_19_16, D11),
+ PINMUX_IPSR_GPSR(IP6_19_16, MMC_CLK),
+
+ PINMUX_IPSR_GPSR(IP6_23_20, VI1_DATA9),
+ PINMUX_IPSR_MSEL(IP6_23_20, TCLK1_A, SEL_TMU_0),
+ PINMUX_IPSR_GPSR(IP6_23_20, D12),
+ PINMUX_IPSR_GPSR(IP6_23_20, MMC_D4),
+
+ PINMUX_IPSR_GPSR(IP6_27_24, VI1_DATA10),
+ PINMUX_IPSR_MSEL(IP6_27_24, TCLK2_A, SEL_TMU_0),
+ PINMUX_IPSR_GPSR(IP6_27_24, D13),
+ PINMUX_IPSR_GPSR(IP6_27_24, MMC_D5),
+
+ PINMUX_IPSR_GPSR(IP6_31_28, VI1_DATA11),
+ PINMUX_IPSR_GPSR(IP6_31_28, SCL4),
+ PINMUX_IPSR_GPSR(IP6_31_28, D14),
+ PINMUX_IPSR_GPSR(IP6_31_28, MMC_D6),
+
+ /* IPSR7 */
+ PINMUX_IPSR_GPSR(IP7_3_0, VI1_FIELD),
+ PINMUX_IPSR_GPSR(IP7_3_0, SDA4),
+ PINMUX_IPSR_GPSR(IP7_3_0, D15),
+ PINMUX_IPSR_GPSR(IP7_3_0, MMC_D7),
+
+ PINMUX_IPSR_GPSR(IP7_7_4, SCL0),
+ PINMUX_IPSR_GPSR(IP7_7_4, CLKOUT),
+
+ PINMUX_IPSR_GPSR(IP7_11_8, SDA0),
+ PINMUX_IPSR_GPSR(IP7_11_8, BS_N),
+ PINMUX_IPSR_GPSR(IP7_11_8, SCK0),
+ PINMUX_IPSR_MSEL(IP7_11_8, HSCK0_B, SEL_HSCIF0_1),
+
+ PINMUX_IPSR_GPSR(IP7_15_12, SCL1),
+ PINMUX_IPSR_GPSR(IP7_15_12, TPU0TO2),
+ PINMUX_IPSR_GPSR(IP7_15_12, RD_N),
+ PINMUX_IPSR_GPSR(IP7_15_12, CTS0_N),
+ PINMUX_IPSR_GPSR(IP7_15_12, HCTS0_N_B),
+
+ PINMUX_IPSR_GPSR(IP7_19_16, SDA1),
+ PINMUX_IPSR_GPSR(IP7_19_16, TPU0TO3),
+ PINMUX_IPSR_GPSR(IP7_19_16, WE0_N),
+ PINMUX_IPSR_GPSR(IP7_19_16, RTS0_N_TANS),
+ PINMUX_IPSR_MSEL(IP1_23_20, HRTS0_N_B, SEL_HSCIF0_1),
+
+ PINMUX_IPSR_GPSR(IP7_23_20, SCL2),
+ PINMUX_IPSR_GPSR(IP7_23_20, WE1_N),
+ PINMUX_IPSR_GPSR(IP7_23_20, RX0),
+ PINMUX_IPSR_MSEL(IP7_23_20, HRX0_B, SEL_HSCIF0_1),
+
+ PINMUX_IPSR_GPSR(IP7_27_24, SDA2),
+ PINMUX_IPSR_GPSR(IP7_27_24, EX_WAIT0),
+ PINMUX_IPSR_GPSR(IP7_27_24, TX0),
+ PINMUX_IPSR_MSEL(IP7_27_24, HTX0_B, SEL_HSCIF0_1),
+
+ PINMUX_IPSR_GPSR(IP7_31_28, AVB_AVTP_MATCH),
+ PINMUX_IPSR_GPSR(IP7_31_28, TPU0TO0),
+
+ /* IPSR8 */
+ PINMUX_IPSR_GPSR(IP8_3_0, AVB_AVTP_CAPTURE),
+ PINMUX_IPSR_GPSR(IP8_3_0, TPU0TO1),
+
+ PINMUX_IPSR_MSEL(IP8_7_4, CANFD0_TX_A, SEL_CANFD0_0),
+ PINMUX_IPSR_GPSR(IP8_7_4, FXR_TXDA),
+ PINMUX_IPSR_MSEL(IP8_7_4, PWM0_B, SEL_PWM0_1),
+ PINMUX_IPSR_GPSR(IP8_7_4, DU_DISP),
+
+ PINMUX_IPSR_MSEL(IP8_11_8, CANFD0_RX_A, SEL_CANFD0_0),
+ PINMUX_IPSR_GPSR(IP8_11_8, RXDA_EXTFXR),
+ PINMUX_IPSR_MSEL(IP8_11_8, PWM1_B, SEL_PWM1_1),
+ PINMUX_IPSR_GPSR(IP8_11_8, DU_CDE),
+
+ PINMUX_IPSR_GPSR(IP8_15_12, CANFD1_TX),
+ PINMUX_IPSR_GPSR(IP8_15_12, FXR_TXDB),
+ PINMUX_IPSR_MSEL(IP8_15_12, PWM2_B, SEL_PWM2_1),
+ PINMUX_IPSR_MSEL(IP8_15_12, TCLK1_B, SEL_TMU_1),
+ PINMUX_IPSR_MSEL(IP8_15_12, TX1_B, SEL_SCIF1_1),
+
+ PINMUX_IPSR_GPSR(IP8_19_16, CANFD1_RX),
+ PINMUX_IPSR_GPSR(IP8_19_16, RXDB_EXTFXR),
+ PINMUX_IPSR_MSEL(IP8_19_16, PWM3_B, SEL_PWM3_1),
+ PINMUX_IPSR_MSEL(IP8_19_16, TCLK2_B, SEL_TMU_1),
+ PINMUX_IPSR_MSEL(IP8_19_16, RX1_B, SEL_SCIF1_1),
+
+ PINMUX_IPSR_MSEL(IP8_23_20, CANFD_CLK_A, SEL_CANFD0_0),
+ PINMUX_IPSR_GPSR(IP8_23_20, CLK_EXTFXR),
+ PINMUX_IPSR_MSEL(IP8_23_20, PWM4_B, SEL_PWM4_1),
+ PINMUX_IPSR_MSEL(IP8_23_20, SPEEDIN_B, SEL_RSP_1),
+ PINMUX_IPSR_MSEL(IP8_23_20, SCIF_CLK_B, SEL_HSCIF0_1),
+
+ PINMUX_IPSR_GPSR(IP8_27_24, DIGRF_CLKIN),
+ PINMUX_IPSR_GPSR(IP8_27_24, DIGRF_CLKEN_IN),
+
+ PINMUX_IPSR_GPSR(IP8_31_28, DIGRF_CLKOUT),
+ PINMUX_IPSR_GPSR(IP8_31_28, DIGRF_CLKEN_OUT),
+
+ /* IPSR9 */
+ PINMUX_IPSR_GPSR(IP9_3_0, IRQ4),
+ PINMUX_IPSR_GPSR(IP9_3_0, VI0_DATA12),
+
+ PINMUX_IPSR_GPSR(IP9_7_4, IRQ5),
+ PINMUX_IPSR_GPSR(IP9_7_4, VI0_DATA13),
+
+ PINMUX_IPSR_GPSR(IP9_11_8, MSIOF0_RXD),
+ PINMUX_IPSR_GPSR(IP9_11_8, DU_DR0),
+ PINMUX_IPSR_GPSR(IP9_11_8, VI0_DATA14),
+
+ PINMUX_IPSR_GPSR(IP9_15_12, MSIOF0_TXD),
+ PINMUX_IPSR_GPSR(IP9_15_12, DU_DR1),
+ PINMUX_IPSR_GPSR(IP9_15_12, VI0_DATA15),
+
+ PINMUX_IPSR_GPSR(IP9_19_16, MSIOF0_SCK),
+ PINMUX_IPSR_GPSR(IP9_19_16, DU_DG0),
+ PINMUX_IPSR_GPSR(IP9_19_16, VI0_DATA16),
+
+ PINMUX_IPSR_GPSR(IP9_23_20, MSIOF0_SYNC),
+ PINMUX_IPSR_GPSR(IP9_23_20, DU_DG1),
+ PINMUX_IPSR_GPSR(IP9_23_20, VI0_DATA17),
+
+ PINMUX_IPSR_GPSR(IP9_27_24, MSIOF0_SS1),
+ PINMUX_IPSR_GPSR(IP9_27_24, DU_DB0),
+ PINMUX_IPSR_GPSR(IP9_27_24, TCLK3),
+ PINMUX_IPSR_GPSR(IP9_27_24, VI0_DATA18),
+
+ PINMUX_IPSR_GPSR(IP9_31_28, MSIOF0_SS2),
+ PINMUX_IPSR_GPSR(IP9_31_28, DU_DB1),
+ PINMUX_IPSR_GPSR(IP9_31_28, TCLK4),
+ PINMUX_IPSR_GPSR(IP9_31_28, VI0_DATA19),
+
+ /* IPSR10 */
+ PINMUX_IPSR_GPSR(IP10_3_0, SCL3),
+ PINMUX_IPSR_GPSR(IP10_3_0, VI0_DATA20),
+
+ PINMUX_IPSR_GPSR(IP10_7_4, SDA3),
+ PINMUX_IPSR_GPSR(IP10_7_4, VI0_DATA21),
+
+ PINMUX_IPSR_GPSR(IP10_11_8, FSO_CFE_0_N),
+ PINMUX_IPSR_GPSR(IP10_11_8, VI0_DATA22),
+
+ PINMUX_IPSR_GPSR(IP10_15_12, FSO_CFE_1_N),
+ PINMUX_IPSR_GPSR(IP10_15_12, VI0_DATA23),
+
+ PINMUX_IPSR_GPSR(IP10_19_16, FSO_TOE_N),
+};
+
+static const struct sh_pfc_pin pinmux_pins[] = {
+ PINMUX_GPIO_GP_ALL(),
+};
+
+/* - AVB -------------------------------------------------------------------- */
+static const unsigned int avb_link_pins[] = {
+ /* AVB_LINK */
+ RCAR_GP_PIN(1, 18),
+};
+static const unsigned int avb_link_mux[] = {
+ AVB_LINK_MARK,
+};
+static const unsigned int avb_magic_pins[] = {
+ /* AVB_MAGIC */
+ RCAR_GP_PIN(1, 16),
+};
+static const unsigned int avb_magic_mux[] = {
+ AVB_MAGIC_MARK,
+};
+static const unsigned int avb_phy_int_pins[] = {
+ /* AVB_PHY_INT */
+ RCAR_GP_PIN(1, 17),
+};
+static const unsigned int avb_phy_int_mux[] = {
+ AVB_PHY_INT_MARK,
+};
+static const unsigned int avb_mdio_pins[] = {
+ /* AVB_MDC, AVB_MDIO */
+ RCAR_GP_PIN(1, 15), RCAR_GP_PIN(1, 14),
+};
+static const unsigned int avb_mdio_mux[] = {
+ AVB_MDC_MARK, AVB_MDIO_MARK,
+};
+static const unsigned int avb_rgmii_pins[] = {
+ /*
+ * AVB_TX_CTL, AVB_TXC, AVB_TD0, AVB_TD1, AVB_TD2, AVB_TD3,
+ * AVB_RX_CTL, AVB_RXC, AVB_RD0, AVB_RD1, AVB_RD2, AVB_RD3,
+ */
+ RCAR_GP_PIN(1, 7), RCAR_GP_PIN(1, 8),
+ RCAR_GP_PIN(1, 9), RCAR_GP_PIN(1, 10),
+ RCAR_GP_PIN(1, 11), RCAR_GP_PIN(1, 12),
+ RCAR_GP_PIN(1, 1), RCAR_GP_PIN(1, 2),
+ RCAR_GP_PIN(1, 3), RCAR_GP_PIN(1, 4),
+ RCAR_GP_PIN(1, 5), RCAR_GP_PIN(1, 6),
+};
+static const unsigned int avb_rgmii_mux[] = {
+ AVB_TX_CTL_MARK, AVB_TXC_MARK,
+ AVB_TD0_MARK, AVB_TD1_MARK, AVB_TD2_MARK, AVB_TD3_MARK,
+ AVB_RX_CTL_MARK, AVB_RXC_MARK,
+ AVB_RD0_MARK, AVB_RD1_MARK, AVB_RD2_MARK, AVB_RD3_MARK,
+};
+static const unsigned int avb_txcrefclk_pins[] = {
+ /* AVB_TXCREFCLK */
+ RCAR_GP_PIN(1, 13),
+};
+static const unsigned int avb_txcrefclk_mux[] = {
+ AVB_TXCREFCLK_MARK,
+};
+static const unsigned int avb_avtp_pps_pins[] = {
+ /* AVB_AVTP_PPS */
+ RCAR_GP_PIN(2, 6),
+};
+static const unsigned int avb_avtp_pps_mux[] = {
+ AVB_AVTP_PPS_MARK,
+};
+static const unsigned int avb_avtp_capture_pins[] = {
+ /* AVB_AVTP_CAPTURE */
+ RCAR_GP_PIN(1, 20),
+};
+static const unsigned int avb_avtp_capture_mux[] = {
+ AVB_AVTP_CAPTURE_MARK,
+};
+static const unsigned int avb_avtp_match_pins[] = {
+ /* AVB_AVTP_MATCH */
+ RCAR_GP_PIN(1, 19),
+};
+static const unsigned int avb_avtp_match_mux[] = {
+ AVB_AVTP_MATCH_MARK,
+};
+
+/* - CANFD0 ----------------------------------------------------------------- */
+static const unsigned int canfd0_data_a_pins[] = {
+ /* CANFD0_TX, CANFD0_RX */
+ RCAR_GP_PIN(1, 21), RCAR_GP_PIN(1, 22),
+};
+static const unsigned int canfd0_data_a_mux[] = {
+ CANFD0_TX_A_MARK, CANFD0_RX_A_MARK,
+};
+static const unsigned int canfd0_data_b_pins[] = {
+ /* CANFD0_TX, CANFD0_RX */
+ RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7),
+};
+static const unsigned int canfd0_data_b_mux[] = {
+ CANFD0_TX_B_MARK, CANFD0_RX_B_MARK,
+};
+
+/* - CANFD1 ----------------------------------------------------------------- */
+static const unsigned int canfd1_data_pins[] = {
+ /* CANFD1_TX, CANFD1_RX */
+ RCAR_GP_PIN(1, 23), RCAR_GP_PIN(1, 24),
+};
+static const unsigned int canfd1_data_mux[] = {
+ CANFD1_TX_MARK, CANFD1_RX_MARK,
+};
+
+/* - CANFD Clock ------------------------------------------------------------ */
+static const unsigned int canfd_clk_a_pins[] = {
+ /* CANFD_CLK */
+ RCAR_GP_PIN(1, 25),
+};
+static const unsigned int canfd_clk_a_mux[] = {
+ CANFD_CLK_A_MARK,
+};
+static const unsigned int canfd_clk_b_pins[] = {
+ /* CANFD_CLK */
+ RCAR_GP_PIN(3, 8),
+};
+static const unsigned int canfd_clk_b_mux[] = {
+ CANFD_CLK_B_MARK,
+};
+
+/* - DU --------------------------------------------------------------------- */
+static const unsigned int du_rgb666_pins[] = {
+ /* DU_DR[7:2], DU_DG[7:2], DU_DB[7:2] */
+ RCAR_GP_PIN(0, 5), RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 3),
+ RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 1), RCAR_GP_PIN(0, 0),
+ RCAR_GP_PIN(0, 11), RCAR_GP_PIN(0, 10), RCAR_GP_PIN(0, 9),
+ RCAR_GP_PIN(0, 8), RCAR_GP_PIN(0, 7), RCAR_GP_PIN(0, 6),
+ RCAR_GP_PIN(0, 17), RCAR_GP_PIN(0, 16), RCAR_GP_PIN(0, 15),
+ RCAR_GP_PIN(0, 14), RCAR_GP_PIN(0, 13), RCAR_GP_PIN(0, 12),
+};
+static const unsigned int du_rgb666_mux[] = {
+ DU_DR7_MARK, DU_DR6_MARK, DU_DR5_MARK,
+ DU_DR4_MARK, DU_DR3_MARK, DU_DR2_MARK,
+ DU_DG7_MARK, DU_DG6_MARK, DU_DG5_MARK,
+ DU_DG4_MARK, DU_DG3_MARK, DU_DG2_MARK,
+ DU_DB7_MARK, DU_DB6_MARK, DU_DB5_MARK,
+ DU_DB4_MARK, DU_DB3_MARK, DU_DB2_MARK,
+};
+static const unsigned int du_rgb888_pins[] = {
+ /* DU_DR[7:0], DU_DG[7:0], DU_DB[7:0] */
+ RCAR_GP_PIN(0, 5), RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 3),
+ RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 1), RCAR_GP_PIN(0, 0),
+ RCAR_GP_PIN(2, 20), RCAR_GP_PIN(2, 19),
+ RCAR_GP_PIN(0, 11), RCAR_GP_PIN(0, 10), RCAR_GP_PIN(0, 9),
+ RCAR_GP_PIN(0, 8), RCAR_GP_PIN(0, 7), RCAR_GP_PIN(0, 6),
+ RCAR_GP_PIN(2, 22), RCAR_GP_PIN(2, 21),
+ RCAR_GP_PIN(0, 17), RCAR_GP_PIN(0, 16), RCAR_GP_PIN(0, 15),
+ RCAR_GP_PIN(0, 14), RCAR_GP_PIN(0, 13), RCAR_GP_PIN(0, 12),
+ RCAR_GP_PIN(2, 24), RCAR_GP_PIN(2, 23),
+};
+static const unsigned int du_rgb888_mux[] = {
+ DU_DR7_MARK, DU_DR6_MARK, DU_DR5_MARK,
+ DU_DR4_MARK, DU_DR3_MARK, DU_DR2_MARK,
+ DU_DR1_MARK, DU_DR0_MARK,
+ DU_DG7_MARK, DU_DG6_MARK, DU_DG5_MARK,
+ DU_DG4_MARK, DU_DG3_MARK, DU_DG2_MARK,
+ DU_DG1_MARK, DU_DG0_MARK,
+ DU_DB7_MARK, DU_DB6_MARK, DU_DB5_MARK,
+ DU_DB4_MARK, DU_DB3_MARK, DU_DB2_MARK,
+ DU_DB1_MARK, DU_DB0_MARK,
+};
+static const unsigned int du_clk_out_pins[] = {
+ /* DU_DOTCLKOUT */
+ RCAR_GP_PIN(0, 18),
+};
+static const unsigned int du_clk_out_mux[] = {
+ DU_DOTCLKOUT_MARK,
+};
+static const unsigned int du_sync_pins[] = {
+ /* DU_EXVSYNC/DU_VSYNC, DU_EXHSYNC/DU_HSYNC */
+ RCAR_GP_PIN(0, 20), RCAR_GP_PIN(0, 19),
+};
+static const unsigned int du_sync_mux[] = {
+ DU_EXVSYNC_DU_VSYNC_MARK, DU_EXHSYNC_DU_HSYNC_MARK,
+};
+static const unsigned int du_oddf_pins[] = {
+ /* DU_EXODDF/DU_ODDF/DISP/CDE */
+ RCAR_GP_PIN(0, 21),
+};
+static const unsigned int du_oddf_mux[] = {
+ DU_EXODDF_DU_ODDF_DISP_CDE_MARK,
+};
+static const unsigned int du_cde_pins[] = {
+ /* DU_CDE */
+ RCAR_GP_PIN(1, 22),
+};
+static const unsigned int du_cde_mux[] = {
+ DU_CDE_MARK,
+};
+static const unsigned int du_disp_pins[] = {
+ /* DU_DISP */
+ RCAR_GP_PIN(1, 21),
+};
+static const unsigned int du_disp_mux[] = {
+ DU_DISP_MARK,
+};
+
+/* - GETHER ----------------------------------------------------------------- */
+static const unsigned int gether_link_a_pins[] = {
+ /* GETHER_LINK */
+ RCAR_GP_PIN(4, 24),
+};
+static const unsigned int gether_link_a_mux[] = {
+ GETHER_LINK_A_MARK,
+};
+static const unsigned int gether_phy_int_a_pins[] = {
+ /* GETHER_PHY_INT */
+ RCAR_GP_PIN(4, 23),
+};
+static const unsigned int gether_phy_int_a_mux[] = {
+ GETHER_PHY_INT_A_MARK,
+};
+static const unsigned int gether_mdio_a_pins[] = {
+ /* GETHER_MDC, GETHER_MDIO */
+ RCAR_GP_PIN(4, 21), RCAR_GP_PIN(4, 20),
+};
+static const unsigned int gether_mdio_a_mux[] = {
+ GETHER_MDC_A_MARK, GETHER_MDIO_A_MARK,
+};
+static const unsigned int gether_link_b_pins[] = {
+ /* GETHER_LINK */
+ RCAR_GP_PIN(0, 18),
+};
+static const unsigned int gether_link_b_mux[] = {
+ GETHER_LINK_B_MARK,
+};
+static const unsigned int gether_phy_int_b_pins[] = {
+ /* GETHER_PHY_INT */
+ RCAR_GP_PIN(0, 19),
+};
+static const unsigned int gether_phy_int_b_mux[] = {
+ GETHER_PHY_INT_B_MARK,
+};
+static const unsigned int gether_mdio_b_mux[] = {
+ GETHER_MDC_B_MARK, GETHER_MDIO_B_MARK,
+};
+static const unsigned int gether_mdio_b_pins[] = {
+ /* GETHER_MDC, GETHER_MDIO */
+ RCAR_GP_PIN(0, 9), RCAR_GP_PIN(0, 10),
+};
+static const unsigned int gether_magic_pins[] = {
+ /* GETHER_MAGIC */
+ RCAR_GP_PIN(4, 22),
+};
+static const unsigned int gether_magic_mux[] = {
+ GETHER_MAGIC_MARK,
+};
+static const unsigned int gether_rgmii_pins[] = {
+ /*
+ * GETHER_TX_CTL, GETHER_TXC,
+ * GETHER_TD0, GETHER_TD1, GETHER_TD2, GETHER_TD3,
+ * GETHER_RX_CTL, GETHER_RXC,
+ * GETHER_RD0, GETHER_RD1, GETHER_RD2, GETHER_RD3,
+ */
+ RCAR_GP_PIN(4, 12), RCAR_GP_PIN(4, 13),
+ RCAR_GP_PIN(4, 14), RCAR_GP_PIN(4, 15),
+ RCAR_GP_PIN(4, 16), RCAR_GP_PIN(4, 17),
+ RCAR_GP_PIN(4, 6), RCAR_GP_PIN(4, 7),
+ RCAR_GP_PIN(4, 8), RCAR_GP_PIN(4, 9),
+ RCAR_GP_PIN(4, 10), RCAR_GP_PIN(4, 11),
+};
+static const unsigned int gether_rgmii_mux[] = {
+ GETHER_TX_CTL_MARK, GETHER_TXC_MARK,
+ GETHER_TD0_MARK, GETHER_TD1_MARK,
+ GETHER_TD2_MARK, GETHER_TD3_MARK,
+ GETHER_RX_CTL_MARK, GETHER_RXC_MARK,
+ GETHER_RD0_MARK, AVB_RD1_MARK,
+ GETHER_RD2_MARK, AVB_RD3_MARK,
+};
+static const unsigned int gether_txcrefclk_pins[] = {
+ /* GETHER_TXCREFCLK */
+ RCAR_GP_PIN(4, 18),
+};
+static const unsigned int gether_txcrefclk_mux[] = {
+ GETHER_TXCREFCLK_MARK,
+};
+static const unsigned int gether_txcrefclk_mega_pins[] = {
+ /* GETHER_TXCREFCLK_MEGA */
+ RCAR_GP_PIN(4, 19),
+};
+static const unsigned int gether_txcrefclk_mega_mux[] = {
+ GETHER_TXCREFCLK_MEGA_MARK,
+};
+static const unsigned int gether_rmii_pins[] = {
+ /*
+ * GETHER_RMII_CRS_DV, GETHER_RMII_RX_ER,
+ * GETHER_RMII_RXD0, GETHER_RMII_RXD1,
+ * GETHER_RMII_TXD_EN, GETHER_RMII_TXD0,
+ * GETHER_RMII_TXD1, GETHER_RMII_REFCLK
+ */
+ RCAR_GP_PIN(0, 0), RCAR_GP_PIN(0, 1),
+ RCAR_GP_PIN(0, 2), RCAR_GP_PIN(0, 3),
+ RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 5),
+ RCAR_GP_PIN(0, 6), RCAR_GP_PIN(0, 7),
+};
+static const unsigned int gether_rmii_mux[] = {
+ GETHER_RMII_CRS_DV_MARK, GETHER_RMII_RX_ER_MARK,
+ GETHER_RMII_RXD0_MARK, GETHER_RMII_RXD1_MARK,
+ GETHER_RMII_TXD_EN_MARK, GETHER_RMII_TXD0_MARK,
+ GETHER_RMII_TXD1_MARK, GETHER_RMII_REFCLK_MARK,
+};
+
+/* - HSCIF0 ----------------------------------------------------------------- */
+static const unsigned int hscif0_data_a_pins[] = {
+ /* HRX0, HTX0 */
+ RCAR_GP_PIN(0, 11), RCAR_GP_PIN(0, 15),
+};
+static const unsigned int hscif0_data_a_mux[] = {
+ HRX0_A_MARK, HTX0_A_MARK,
+};
+static const unsigned int hscif0_clk_a_pins[] = {
+ /* HSCK0 */
+ RCAR_GP_PIN(0, 12),
+};
+static const unsigned int hscif0_clk_a_mux[] = {
+ HSCK0_A_MARK,
+};
+static const unsigned int hscif0_ctrl_a_pins[] = {
+ /* HRTS0#, HCTS0# */
+ RCAR_GP_PIN(0, 13), RCAR_GP_PIN(0, 14),
+};
+static const unsigned int hscif0_ctrl_a_mux[] = {
+ HRTS0_N_A_MARK, HCTS0_N_A_MARK,
+};
+static const unsigned int hscif0_data_b_pins[] = {
+ /* HRX0, HTX0 */
+ RCAR_GP_PIN(4, 4), RCAR_GP_PIN(4, 5),
+};
+static const unsigned int hscif0_data_b_mux[] = {
+ HRX0_B_MARK, HTX0_B_MARK,
+};
+static const unsigned int hscif0_clk_b_pins[] = {
+ /* HSCK0 */
+ RCAR_GP_PIN(4, 1),
+};
+static const unsigned int hscif0_clk_b_mux[] = {
+ HSCK0_B_MARK,
+};
+static const unsigned int hscif0_ctrl_b_pins[] = {
+ /* HRTS0#, HCTS0# */
+ RCAR_GP_PIN(4, 3), RCAR_GP_PIN(4, 2),
+};
+static const unsigned int hscif0_ctrl_b_mux[] = {
+ HRTS0_N_B_MARK, HCTS0_N_B_MARK,
+};
+
+/* - HSCIF1 ----------------------------------------------------------------- */
+static const unsigned int hscif1_data_pins[] = {
+ /* HRX1, HTX1 */
+ RCAR_GP_PIN(2, 11), RCAR_GP_PIN(2, 10),
+};
+static const unsigned int hscif1_data_mux[] = {
+ HRX1_MARK, HTX1_MARK,
+};
+static const unsigned int hscif1_clk_pins[] = {
+ /* HSCK1 */
+ RCAR_GP_PIN(2, 7),
+};
+static const unsigned int hscif1_clk_mux[] = {
+ HSCK1_MARK,
+};
+static const unsigned int hscif1_ctrl_pins[] = {
+ /* HRTS1#, HCTS1# */
+ RCAR_GP_PIN(2, 8), RCAR_GP_PIN(2, 9),
+};
+static const unsigned int hscif1_ctrl_mux[] = {
+ HRTS1_N_MARK, HCTS1_N_MARK,
+};
+
+/* - HSCIF2 ----------------------------------------------------------------- */
+static const unsigned int hscif2_data_pins[] = {
+ /* HRX2, HTX2 */
+ RCAR_GP_PIN(2, 16), RCAR_GP_PIN(2, 15),
+};
+static const unsigned int hscif2_data_mux[] = {
+ HRX2_MARK, HTX2_MARK,
+};
+static const unsigned int hscif2_clk_pins[] = {
+ /* HSCK2 */
+ RCAR_GP_PIN(2, 12),
+};
+static const unsigned int hscif2_clk_mux[] = {
+ HSCK2_MARK,
+};
+static const unsigned int hscif2_ctrl_pins[] = {
+ /* HRTS2#, HCTS2# */
+ RCAR_GP_PIN(2, 14), RCAR_GP_PIN(2, 13),
+};
+static const unsigned int hscif2_ctrl_mux[] = {
+ HRTS2_N_MARK, HCTS2_N_MARK,
+};
+
+/* - HSCIF3 ----------------------------------------------------------------- */
+static const unsigned int hscif3_data_pins[] = {
+ /* HRX3, HTX3 */
+ RCAR_GP_PIN(2, 4), RCAR_GP_PIN(2, 3),
+};
+static const unsigned int hscif3_data_mux[] = {
+ HRX3_MARK, HTX3_MARK,
+};
+static const unsigned int hscif3_clk_pins[] = {
+ /* HSCK3 */
+ RCAR_GP_PIN(2, 0),
+};
+static const unsigned int hscif3_clk_mux[] = {
+ HSCK3_MARK,
+};
+static const unsigned int hscif3_ctrl_pins[] = {
+ /* HRTS3#, HCTS3# */
+ RCAR_GP_PIN(2, 2), RCAR_GP_PIN(2, 1),
+};
+static const unsigned int hscif3_ctrl_mux[] = {
+ HRTS3_N_MARK, HCTS3_N_MARK,
+};
+
+/* - I2C0 ------------------------------------------------------------------- */
+static const unsigned int i2c0_pins[] = {
+ /* SDA0, SCL0 */
+ RCAR_GP_PIN(4, 1), RCAR_GP_PIN(4, 0),
+};
+static const unsigned int i2c0_mux[] = {
+ SDA0_MARK, SCL0_MARK,
+};
+
+/* - I2C1 ------------------------------------------------------------------- */
+static const unsigned int i2c1_pins[] = {
+ /* SDA1, SCL1 */
+ RCAR_GP_PIN(4, 3), RCAR_GP_PIN(4, 2),
+};
+static const unsigned int i2c1_mux[] = {
+ SDA1_MARK, SCL1_MARK,
+};
+
+/* - I2C2 ------------------------------------------------------------------- */
+static const unsigned int i2c2_pins[] = {
+ /* SDA2, SCL2 */
+ RCAR_GP_PIN(4, 5), RCAR_GP_PIN(4, 4),
+};
+static const unsigned int i2c2_mux[] = {
+ SDA2_MARK, SCL2_MARK,
+};
+
+/* - I2C3 ------------------------------------------------------------------- */
+static const unsigned int i2c3_pins[] = {
+ /* SDA3, SCL3 */
+ RCAR_GP_PIN(2, 26), RCAR_GP_PIN(2, 25),
+};
+static const unsigned int i2c3_mux[] = {
+ SDA3_MARK, SCL3_MARK,
+};
+
+/* - I2C4 ------------------------------------------------------------------- */
+static const unsigned int i2c4_pins[] = {
+ /* SDA4, SCL4 */
+ RCAR_GP_PIN(3, 16), RCAR_GP_PIN(3, 15),
+};
+static const unsigned int i2c4_mux[] = {
+ SDA4_MARK, SCL4_MARK,
+};
+
+/* - I2C5 ------------------------------------------------------------------- */
+static const unsigned int i2c5_pins[] = {
+ /* SDA5, SCL5 */
+ RCAR_GP_PIN(0, 9), RCAR_GP_PIN(0, 8),
+};
+static const unsigned int i2c5_mux[] = {
+ SDA5_MARK, SCL5_MARK,
+};
+
+/* - INTC-EX ---------------------------------------------------------------- */
+static const unsigned int intc_ex_irq0_pins[] = {
+ /* IRQ0 */
+ RCAR_GP_PIN(1, 0),
+};
+static const unsigned int intc_ex_irq0_mux[] = {
+ IRQ0_MARK,
+};
+static const unsigned int intc_ex_irq1_pins[] = {
+ /* IRQ1 */
+ RCAR_GP_PIN(0, 12),
+};
+static const unsigned int intc_ex_irq1_mux[] = {
+ IRQ1_MARK,
+};
+static const unsigned int intc_ex_irq2_pins[] = {
+ /* IRQ2 */
+ RCAR_GP_PIN(0, 13),
+};
+static const unsigned int intc_ex_irq2_mux[] = {
+ IRQ2_MARK,
+};
+static const unsigned int intc_ex_irq3_pins[] = {
+ /* IRQ3 */
+ RCAR_GP_PIN(0, 14),
+};
+static const unsigned int intc_ex_irq3_mux[] = {
+ IRQ3_MARK,
+};
+static const unsigned int intc_ex_irq4_pins[] = {
+ /* IRQ4 */
+ RCAR_GP_PIN(2, 17),
+};
+static const unsigned int intc_ex_irq4_mux[] = {
+ IRQ4_MARK,
+};
+static const unsigned int intc_ex_irq5_pins[] = {
+ /* IRQ5 */
+ RCAR_GP_PIN(2, 18),
+};
+static const unsigned int intc_ex_irq5_mux[] = {
+ IRQ5_MARK,
+};
+
+/* - MMC -------------------------------------------------------------------- */
+static const unsigned int mmc_data1_pins[] = {
+ /* MMC_D0 */
+ RCAR_GP_PIN(3, 8),
+};
+static const unsigned int mmc_data1_mux[] = {
+ MMC_D0_MARK,
+};
+static const unsigned int mmc_data4_pins[] = {
+ /* MMC_D[0:3] */
+ RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9),
+ RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11),
+};
+static const unsigned int mmc_data4_mux[] = {
+ MMC_D0_MARK, MMC_D1_MARK,
+ MMC_D2_MARK, MMC_D3_MARK,
+};
+static const unsigned int mmc_data8_pins[] = {
+ /* MMC_D[0:7] */
+ RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9),
+ RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11),
+ RCAR_GP_PIN(3, 13), RCAR_GP_PIN(3, 14),
+ RCAR_GP_PIN(3, 15), RCAR_GP_PIN(3, 16),
+};
+static const unsigned int mmc_data8_mux[] = {
+ MMC_D0_MARK, MMC_D1_MARK,
+ MMC_D2_MARK, MMC_D3_MARK,
+ MMC_D4_MARK, MMC_D5_MARK,
+ MMC_D6_MARK, MMC_D7_MARK,
+};
+static const unsigned int mmc_ctrl_pins[] = {
+ /* MMC_CLK, MMC_CMD */
+ RCAR_GP_PIN(3, 12), RCAR_GP_PIN(3, 7),
+};
+static const unsigned int mmc_ctrl_mux[] = {
+ MMC_CLK_MARK, MMC_CMD_MARK,
+};
+static const unsigned int mmc_cd_pins[] = {
+ /* MMC_CD */
+ RCAR_GP_PIN(3, 5),
+};
+static const unsigned int mmc_cd_mux[] = {
+ MMC_CD_MARK,
+};
+static const unsigned int mmc_wp_pins[] = {
+ /* MMC_WP */
+ RCAR_GP_PIN(3, 4),
+};
+static const unsigned int mmc_wp_mux[] = {
+ MMC_WP_MARK,
+};
+static const unsigned int mmc_ds_pins[] = {
+ /* MMC_DS */
+ RCAR_GP_PIN(3, 6),
+};
+static const unsigned int mmc_ds_mux[] = {
+ MMC_DS_MARK,
+};
+
+/* - MSIOF0 ----------------------------------------------------------------- */
+static const unsigned int msiof0_clk_pins[] = {
+ /* MSIOF0_SCK */
+ RCAR_GP_PIN(2, 21),
+};
+static const unsigned int msiof0_clk_mux[] = {
+ MSIOF0_SCK_MARK,
+};
+static const unsigned int msiof0_sync_pins[] = {
+ /* MSIOF0_SYNC */
+ RCAR_GP_PIN(2, 22),
+};
+static const unsigned int msiof0_sync_mux[] = {
+ MSIOF0_SYNC_MARK,
+};
+static const unsigned int msiof0_ss1_pins[] = {
+ /* MSIOF0_SS1 */
+ RCAR_GP_PIN(2, 23),
+};
+static const unsigned int msiof0_ss1_mux[] = {
+ MSIOF0_SS1_MARK,
+};
+static const unsigned int msiof0_ss2_pins[] = {
+ /* MSIOF0_SS2 */
+ RCAR_GP_PIN(2, 24),
+};
+static const unsigned int msiof0_ss2_mux[] = {
+ MSIOF0_SS2_MARK,
+};
+static const unsigned int msiof0_txd_pins[] = {
+ /* MSIOF0_TXD */
+ RCAR_GP_PIN(2, 20),
+};
+static const unsigned int msiof0_txd_mux[] = {
+ MSIOF0_TXD_MARK,
+};
+static const unsigned int msiof0_rxd_pins[] = {
+ /* MSIOF0_RXD */
+ RCAR_GP_PIN(2, 19),
+};
+static const unsigned int msiof0_rxd_mux[] = {
+ MSIOF0_RXD_MARK,
+};
+
+/* - MSIOF1 ----------------------------------------------------------------- */
+static const unsigned int msiof1_clk_pins[] = {
+ /* MSIOF1_SCK */
+ RCAR_GP_PIN(3, 2),
+};
+static const unsigned int msiof1_clk_mux[] = {
+ MSIOF1_SCK_MARK,
+};
+static const unsigned int msiof1_sync_pins[] = {
+ /* MSIOF1_SYNC */
+ RCAR_GP_PIN(3, 3),
+};
+static const unsigned int msiof1_sync_mux[] = {
+ MSIOF1_SYNC_MARK,
+};
+static const unsigned int msiof1_ss1_pins[] = {
+ /* MSIOF1_SS1 */
+ RCAR_GP_PIN(3, 4),
+};
+static const unsigned int msiof1_ss1_mux[] = {
+ MSIOF1_SS1_MARK,
+};
+static const unsigned int msiof1_ss2_pins[] = {
+ /* MSIOF1_SS2 */
+ RCAR_GP_PIN(3, 5),
+};
+static const unsigned int msiof1_ss2_mux[] = {
+ MSIOF1_SS2_MARK,
+};
+static const unsigned int msiof1_txd_pins[] = {
+ /* MSIOF1_TXD */
+ RCAR_GP_PIN(3, 1),
+};
+static const unsigned int msiof1_txd_mux[] = {
+ MSIOF1_TXD_MARK,
+};
+static const unsigned int msiof1_rxd_pins[] = {
+ /* MSIOF1_RXD */
+ RCAR_GP_PIN(3, 0),
+};
+static const unsigned int msiof1_rxd_mux[] = {
+ MSIOF1_RXD_MARK,
+};
+
+/* - MSIOF2 ----------------------------------------------------------------- */
+static const unsigned int msiof2_clk_pins[] = {
+ /* MSIOF2_SCK */
+ RCAR_GP_PIN(2, 0),
+};
+static const unsigned int msiof2_clk_mux[] = {
+ MSIOF2_SCK_MARK,
+};
+static const unsigned int msiof2_sync_pins[] = {
+ /* MSIOF2_SYNC */
+ RCAR_GP_PIN(2, 3),
+};
+static const unsigned int msiof2_sync_mux[] = {
+ MSIOF2_SYNC_MARK,
+};
+static const unsigned int msiof2_ss1_pins[] = {
+ /* MSIOF2_SS1 */
+ RCAR_GP_PIN(2, 4),
+};
+static const unsigned int msiof2_ss1_mux[] = {
+ MSIOF2_SS1_MARK,
+};
+static const unsigned int msiof2_ss2_pins[] = {
+ /* MSIOF2_SS2 */
+ RCAR_GP_PIN(2, 5),
+};
+static const unsigned int msiof2_ss2_mux[] = {
+ MSIOF2_SS2_MARK,
+};
+static const unsigned int msiof2_txd_pins[] = {
+ /* MSIOF2_TXD */
+ RCAR_GP_PIN(2, 2),
+};
+static const unsigned int msiof2_txd_mux[] = {
+ MSIOF2_TXD_MARK,
+};
+static const unsigned int msiof2_rxd_pins[] = {
+ /* MSIOF2_RXD */
+ RCAR_GP_PIN(2, 1),
+};
+static const unsigned int msiof2_rxd_mux[] = {
+ MSIOF2_RXD_MARK,
+};
+
+/* - MSIOF3 ----------------------------------------------------------------- */
+static const unsigned int msiof3_clk_pins[] = {
+ /* MSIOF3_SCK */
+ RCAR_GP_PIN(0, 20),
+};
+static const unsigned int msiof3_clk_mux[] = {
+ MSIOF3_SCK_MARK,
+};
+static const unsigned int msiof3_sync_pins[] = {
+ /* MSIOF3_SYNC */
+ RCAR_GP_PIN(0, 21),
+};
+static const unsigned int msiof3_sync_mux[] = {
+ MSIOF3_SYNC_MARK,
+};
+static const unsigned int msiof3_ss1_pins[] = {
+ /* MSIOF3_SS1 */
+ RCAR_GP_PIN(0, 18),
+};
+static const unsigned int msiof3_ss1_mux[] = {
+ MSIOF3_SS1_MARK,
+};
+static const unsigned int msiof3_ss2_pins[] = {
+ /* MSIOF3_SS2 */
+ RCAR_GP_PIN(0, 19),
+};
+static const unsigned int msiof3_ss2_mux[] = {
+ MSIOF3_SS2_MARK,
+};
+static const unsigned int msiof3_txd_pins[] = {
+ /* MSIOF3_TXD */
+ RCAR_GP_PIN(0, 17),
+};
+static const unsigned int msiof3_txd_mux[] = {
+ MSIOF3_TXD_MARK,
+};
+static const unsigned int msiof3_rxd_pins[] = {
+ /* MSIOF3_RXD */
+ RCAR_GP_PIN(0, 16),
+};
+static const unsigned int msiof3_rxd_mux[] = {
+ MSIOF3_RXD_MARK,
+};
+
+/* - PWM0 ------------------------------------------------------------------- */
+static const unsigned int pwm0_a_pins[] = {
+ /* PWM0 */
+ RCAR_GP_PIN(0, 15),
+};
+static const unsigned int pwm0_a_mux[] = {
+ PWM0_A_MARK,
+};
+static const unsigned int pwm0_b_pins[] = {
+ /* PWM0 */
+ RCAR_GP_PIN(1, 21),
+};
+static const unsigned int pwm0_b_mux[] = {
+ PWM0_B_MARK,
+};
+
+/* - PWM1 ------------------------------------------------------------------- */
+static const unsigned int pwm1_a_pins[] = {
+ /* PWM1 */
+ RCAR_GP_PIN(2, 13),
+};
+static const unsigned int pwm1_a_mux[] = {
+ PWM1_A_MARK,
+};
+static const unsigned int pwm1_b_pins[] = {
+ /* PWM1 */
+ RCAR_GP_PIN(1, 22),
+};
+static const unsigned int pwm1_b_mux[] = {
+ PWM1_B_MARK,
+};
+
+/* - PWM2 ------------------------------------------------------------------- */
+static const unsigned int pwm2_a_pins[] = {
+ /* PWM2 */
+ RCAR_GP_PIN(2, 14),
+};
+static const unsigned int pwm2_a_mux[] = {
+ PWM2_A_MARK,
+};
+static const unsigned int pwm2_b_pins[] = {
+ /* PWM2 */
+ RCAR_GP_PIN(1, 23),
+};
+static const unsigned int pwm2_b_mux[] = {
+ PWM2_B_MARK,
+};
+
+/* - PWM3 ------------------------------------------------------------------- */
+static const unsigned int pwm3_a_pins[] = {
+ /* PWM3 */
+ RCAR_GP_PIN(2, 15),
+};
+static const unsigned int pwm3_a_mux[] = {
+ PWM3_A_MARK,
+};
+static const unsigned int pwm3_b_pins[] = {
+ /* PWM3 */
+ RCAR_GP_PIN(1, 24),
+};
+static const unsigned int pwm3_b_mux[] = {
+ PWM3_B_MARK,
+};
+
+/* - PWM4 ------------------------------------------------------------------- */
+static const unsigned int pwm4_a_pins[] = {
+ /* PWM4 */
+ RCAR_GP_PIN(2, 16),
+};
+static const unsigned int pwm4_a_mux[] = {
+ PWM4_A_MARK,
+};
+static const unsigned int pwm4_b_pins[] = {
+ /* PWM4 */
+ RCAR_GP_PIN(1, 25),
+};
+static const unsigned int pwm4_b_mux[] = {
+ PWM4_B_MARK,
+};
+
+/* - SCIF0 ------------------------------------------------------------------ */
+static const unsigned int scif0_data_pins[] = {
+ /* RX0, TX0 */
+ RCAR_GP_PIN(4, 4), RCAR_GP_PIN(4, 5),
+};
+static const unsigned int scif0_data_mux[] = {
+ RX0_MARK, TX0_MARK,
+};
+static const unsigned int scif0_clk_pins[] = {
+ /* SCK0 */
+ RCAR_GP_PIN(4, 1),
+};
+static const unsigned int scif0_clk_mux[] = {
+ SCK0_MARK,
+};
+static const unsigned int scif0_ctrl_pins[] = {
+ /* RTS0#/TANS, CTS0# */
+ RCAR_GP_PIN(4, 3), RCAR_GP_PIN(4, 2),
+};
+static const unsigned int scif0_ctrl_mux[] = {
+ RTS0_N_TANS_MARK, CTS0_N_MARK,
+};
+
+/* - SCIF1 ------------------------------------------------------------------ */
+static const unsigned int scif1_data_a_pins[] = {
+ /* RX1, TX1 */
+ RCAR_GP_PIN(2, 8), RCAR_GP_PIN(2, 9),
+};
+static const unsigned int scif1_data_a_mux[] = {
+ RX1_A_MARK, TX1_A_MARK,
+};
+static const unsigned int scif1_clk_pins[] = {
+ /* SCK1 */
+ RCAR_GP_PIN(2, 5),
+};
+static const unsigned int scif1_clk_mux[] = {
+ SCK1_MARK,
+};
+static const unsigned int scif1_ctrl_pins[] = {
+ /* RTS1#/TANS, CTS1# */
+ RCAR_GP_PIN(2, 11), RCAR_GP_PIN(2, 10),
+};
+static const unsigned int scif1_ctrl_mux[] = {
+ RTS1_N_TANS_MARK, CTS1_N_MARK,
+};
+static const unsigned int scif1_data_b_pins[] = {
+ /* RX1, TX1 */
+ RCAR_GP_PIN(1, 24), RCAR_GP_PIN(1, 23),
+};
+static const unsigned int scif1_data_b_mux[] = {
+ RX1_B_MARK, TX1_B_MARK,
+};
+
+/* - SCIF3 ------------------------------------------------------------------ */
+static const unsigned int scif3_data_pins[] = {
+ /* RX3, TX3 */
+ RCAR_GP_PIN(2, 1), RCAR_GP_PIN(2, 2),
+};
+static const unsigned int scif3_data_mux[] = {
+ RX3_MARK, TX3_MARK,
+};
+static const unsigned int scif3_clk_pins[] = {
+ /* SCK3 */
+ RCAR_GP_PIN(2, 0),
+};
+static const unsigned int scif3_clk_mux[] = {
+ SCK3_MARK,
+};
+static const unsigned int scif3_ctrl_pins[] = {
+ /* RTS3#/TANS, CTS3# */
+ RCAR_GP_PIN(2, 4), RCAR_GP_PIN(2, 3),
+};
+static const unsigned int scif3_ctrl_mux[] = {
+ RTS3_N_TANS_MARK, CTS3_N_MARK,
+};
+
+/* - SCIF4 ------------------------------------------------------------------ */
+static const unsigned int scif4_data_pins[] = {
+ /* RX4, TX4 */
+ RCAR_GP_PIN(0, 1), RCAR_GP_PIN(0, 2),
+};
+static const unsigned int scif4_data_mux[] = {
+ RX4_MARK, TX4_MARK,
+};
+static const unsigned int scif4_clk_pins[] = {
+ /* SCK4 */
+ RCAR_GP_PIN(0, 0),
+};
+static const unsigned int scif4_clk_mux[] = {
+ SCK4_MARK,
+};
+static const unsigned int scif4_ctrl_pins[] = {
+ /* RTS4#/TANS, CTS4# */
+ RCAR_GP_PIN(0, 4), RCAR_GP_PIN(0, 3),
+};
+static const unsigned int scif4_ctrl_mux[] = {
+ RTS4_N_TANS_MARK, CTS4_N_MARK,
+};
+
+/* - SCIF Clock ------------------------------------------------------------- */
+static const unsigned int scif_clk_a_pins[] = {
+ /* SCIF_CLK */
+ RCAR_GP_PIN(0, 10),
+};
+static const unsigned int scif_clk_a_mux[] = {
+ SCIF_CLK_A_MARK,
+};
+static const unsigned int scif_clk_b_pins[] = {
+ /* SCIF_CLK */
+ RCAR_GP_PIN(1, 25),
+};
+static const unsigned int scif_clk_b_mux[] = {
+ SCIF_CLK_B_MARK,
+};
+
+/* - TMU -------------------------------------------------------------------- */
+static const unsigned int tmu_tclk1_a_pins[] = {
+ /* TCLK1 */
+ RCAR_GP_PIN(3, 13),
+};
+static const unsigned int tmu_tclk1_a_mux[] = {
+ TCLK1_A_MARK,
+};
+static const unsigned int tmu_tclk1_b_pins[] = {
+ /* TCLK1 */
+ RCAR_GP_PIN(1, 23),
+};
+static const unsigned int tmu_tclk1_b_mux[] = {
+ TCLK1_B_MARK,
+};
+static const unsigned int tmu_tclk2_a_pins[] = {
+ /* TCLK2 */
+ RCAR_GP_PIN(3, 14),
+};
+static const unsigned int tmu_tclk2_a_mux[] = {
+ TCLK2_A_MARK,
+};
+static const unsigned int tmu_tclk2_b_pins[] = {
+ /* TCLK2 */
+ RCAR_GP_PIN(1, 24),
+};
+static const unsigned int tmu_tclk2_b_mux[] = {
+ TCLK2_B_MARK,
+};
+
+/* - TPU ------------------------------------------------------------------- */
+static const unsigned int tpu_to0_pins[] = {
+ /* TPU0TO0 */
+ RCAR_GP_PIN(1, 19),
+};
+static const unsigned int tpu_to0_mux[] = {
+ TPU0TO0_MARK,
+};
+static const unsigned int tpu_to1_pins[] = {
+ /* TPU0TO1 */
+ RCAR_GP_PIN(1, 20),
+};
+static const unsigned int tpu_to1_mux[] = {
+ TPU0TO1_MARK,
+};
+static const unsigned int tpu_to2_pins[] = {
+ /* TPU0TO2 */
+ RCAR_GP_PIN(4, 2),
+};
+static const unsigned int tpu_to2_mux[] = {
+ TPU0TO2_MARK,
+};
+static const unsigned int tpu_to3_pins[] = {
+ /* TPU0TO3 */
+ RCAR_GP_PIN(4, 3),
+};
+static const unsigned int tpu_to3_mux[] = {
+ TPU0TO3_MARK,
+};
+
+/* - VIN0 ------------------------------------------------------------------- */
+static const union vin_data vin0_data_pins = {
+ .data24 = {
+ RCAR_GP_PIN(2, 4), RCAR_GP_PIN(2, 5),
+ RCAR_GP_PIN(2, 6), RCAR_GP_PIN(2, 7),
+ RCAR_GP_PIN(2, 8), RCAR_GP_PIN(2, 9),
+ RCAR_GP_PIN(2, 10), RCAR_GP_PIN(2, 11),
+ RCAR_GP_PIN(2, 12), RCAR_GP_PIN(2, 13),
+ RCAR_GP_PIN(2, 14), RCAR_GP_PIN(2, 15),
+ RCAR_GP_PIN(2, 17), RCAR_GP_PIN(2, 18),
+ RCAR_GP_PIN(2, 19), RCAR_GP_PIN(2, 20),
+ RCAR_GP_PIN(2, 21), RCAR_GP_PIN(2, 22),
+ RCAR_GP_PIN(2, 23), RCAR_GP_PIN(2, 24),
+ RCAR_GP_PIN(2, 25), RCAR_GP_PIN(2, 26),
+ RCAR_GP_PIN(2, 27), RCAR_GP_PIN(2, 28),
+ },
+};
+static const union vin_data vin0_data_mux = {
+ .data24 = {
+ VI0_DATA0_MARK, VI0_DATA1_MARK,
+ VI0_DATA2_MARK, VI0_DATA3_MARK,
+ VI0_DATA4_MARK, VI0_DATA5_MARK,
+ VI0_DATA6_MARK, VI0_DATA7_MARK,
+ VI0_DATA8_MARK, VI0_DATA9_MARK,
+ VI0_DATA10_MARK, VI0_DATA11_MARK,
+ VI0_DATA12_MARK, VI0_DATA13_MARK,
+ VI0_DATA14_MARK, VI0_DATA15_MARK,
+ VI0_DATA16_MARK, VI0_DATA17_MARK,
+ VI0_DATA18_MARK, VI0_DATA19_MARK,
+ VI0_DATA20_MARK, VI0_DATA21_MARK,
+ VI0_DATA22_MARK, VI0_DATA23_MARK,
+ },
+};
+static const unsigned int vin0_data18_pins[] = {
+ RCAR_GP_PIN(2, 6), RCAR_GP_PIN(2, 7),
+ RCAR_GP_PIN(2, 8), RCAR_GP_PIN(2, 9),
+ RCAR_GP_PIN(2, 10), RCAR_GP_PIN(2, 11),
+ RCAR_GP_PIN(2, 14), RCAR_GP_PIN(2, 15),
+ RCAR_GP_PIN(2, 17), RCAR_GP_PIN(2, 18),
+ RCAR_GP_PIN(2, 19), RCAR_GP_PIN(2, 20),
+ RCAR_GP_PIN(2, 23), RCAR_GP_PIN(2, 24),
+ RCAR_GP_PIN(2, 25), RCAR_GP_PIN(2, 26),
+ RCAR_GP_PIN(2, 27), RCAR_GP_PIN(2, 28),
+};
+static const unsigned int vin0_data18_mux[] = {
+ VI0_DATA2_MARK, VI0_DATA3_MARK,
+ VI0_DATA4_MARK, VI0_DATA5_MARK,
+ VI0_DATA6_MARK, VI0_DATA7_MARK,
+ VI0_DATA10_MARK, VI0_DATA11_MARK,
+ VI0_DATA12_MARK, VI0_DATA13_MARK,
+ VI0_DATA14_MARK, VI0_DATA15_MARK,
+ VI0_DATA18_MARK, VI0_DATA19_MARK,
+ VI0_DATA20_MARK, VI0_DATA21_MARK,
+ VI0_DATA22_MARK, VI0_DATA23_MARK,
+};
+static const unsigned int vin0_sync_pins[] = {
+ /* VI0_VSYNC#, VI0_HSYNC# */
+ RCAR_GP_PIN(2, 3), RCAR_GP_PIN(2, 2),
+};
+static const unsigned int vin0_sync_mux[] = {
+ VI0_VSYNC_N_MARK, VI0_HSYNC_N_MARK,
+};
+static const unsigned int vin0_field_pins[] = {
+ /* VI0_FIELD */
+ RCAR_GP_PIN(2, 16),
+};
+static const unsigned int vin0_field_mux[] = {
+ VI0_FIELD_MARK,
+};
+static const unsigned int vin0_clkenb_pins[] = {
+ /* VI0_CLKENB */
+ RCAR_GP_PIN(2, 1),
+};
+static const unsigned int vin0_clkenb_mux[] = {
+ VI0_CLKENB_MARK,
+};
+static const unsigned int vin0_clk_pins[] = {
+ /* VI0_CLK */
+ RCAR_GP_PIN(2, 0),
+};
+static const unsigned int vin0_clk_mux[] = {
+ VI0_CLK_MARK,
+};
+
+/* - VIN1 ------------------------------------------------------------------- */
+static const unsigned int vin1_data8_pins[] = {
+ RCAR_GP_PIN(3, 4), RCAR_GP_PIN(3, 5),
+ RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7),
+ RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9),
+ RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11),
+};
+static const unsigned int vin1_data8_mux[] = {
+ VI1_DATA0_MARK, VI1_DATA1_MARK,
+ VI1_DATA2_MARK, VI1_DATA3_MARK,
+ VI1_DATA4_MARK, VI1_DATA5_MARK,
+ VI1_DATA6_MARK, VI1_DATA7_MARK,
+};
+static const unsigned int vin1_data10_pins[] = {
+ RCAR_GP_PIN(3, 4), RCAR_GP_PIN(3, 5),
+ RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7),
+ RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9),
+ RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11),
+ RCAR_GP_PIN(3, 12), RCAR_GP_PIN(3, 13),
+};
+static const unsigned int vin1_data10_mux[] = {
+ VI1_DATA0_MARK, VI1_DATA1_MARK,
+ VI1_DATA2_MARK, VI1_DATA3_MARK,
+ VI1_DATA4_MARK, VI1_DATA5_MARK,
+ VI1_DATA6_MARK, VI1_DATA7_MARK,
+ VI1_DATA8_MARK, VI1_DATA9_MARK,
+};
+static const unsigned int vin1_data12_pins[] = {
+ RCAR_GP_PIN(3, 4), RCAR_GP_PIN(3, 5),
+ RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7),
+ RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9),
+ RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11),
+ RCAR_GP_PIN(3, 12), RCAR_GP_PIN(3, 13),
+ RCAR_GP_PIN(3, 14), RCAR_GP_PIN(3, 15),
+};
+static const unsigned int vin1_data12_mux[] = {
+ VI1_DATA0_MARK, VI1_DATA1_MARK,
+ VI1_DATA2_MARK, VI1_DATA3_MARK,
+ VI1_DATA4_MARK, VI1_DATA5_MARK,
+ VI1_DATA6_MARK, VI1_DATA7_MARK,
+ VI1_DATA8_MARK, VI1_DATA9_MARK,
+ VI1_DATA10_MARK, VI1_DATA11_MARK,
+};
+static const unsigned int vin1_sync_pins[] = {
+ /* VI1_VSYNC#, VI1_HSYNC# */
+ RCAR_GP_PIN(3, 3), RCAR_GP_PIN(3, 2),
+};
+static const unsigned int vin1_sync_mux[] = {
+ VI1_VSYNC_N_MARK, VI1_HSYNC_N_MARK,
+};
+static const unsigned int vin1_field_pins[] = {
+ /* VI1_FIELD */
+ RCAR_GP_PIN(3, 16),
+};
+static const unsigned int vin1_field_mux[] = {
+ VI1_FIELD_MARK,
+};
+static const unsigned int vin1_clkenb_pins[] = {
+ /* VI1_CLKENB */
+ RCAR_GP_PIN(3, 1),
+};
+static const unsigned int vin1_clkenb_mux[] = {
+ VI1_CLKENB_MARK,
+};
+static const unsigned int vin1_clk_pins[] = {
+ /* VI1_CLK */
+ RCAR_GP_PIN(3, 0),
+};
+static const unsigned int vin1_clk_mux[] = {
+ VI1_CLK_MARK,
+};
+
+static const struct sh_pfc_pin_group pinmux_groups[] = {
+ SH_PFC_PIN_GROUP(avb_link),
+ SH_PFC_PIN_GROUP(avb_magic),
+ SH_PFC_PIN_GROUP(avb_phy_int),
+ SH_PFC_PIN_GROUP(avb_mdio),
+ SH_PFC_PIN_GROUP(avb_rgmii),
+ SH_PFC_PIN_GROUP(avb_txcrefclk),
+ SH_PFC_PIN_GROUP(avb_avtp_pps),
+ SH_PFC_PIN_GROUP(avb_avtp_capture),
+ SH_PFC_PIN_GROUP(avb_avtp_match),
+ SH_PFC_PIN_GROUP(canfd0_data_a),
+ SH_PFC_PIN_GROUP(canfd0_data_b),
+ SH_PFC_PIN_GROUP(canfd1_data),
+ SH_PFC_PIN_GROUP(canfd_clk_a),
+ SH_PFC_PIN_GROUP(canfd_clk_b),
+ SH_PFC_PIN_GROUP(du_rgb666),
+ SH_PFC_PIN_GROUP(du_rgb888),
+ SH_PFC_PIN_GROUP(du_clk_out),
+ SH_PFC_PIN_GROUP(du_sync),
+ SH_PFC_PIN_GROUP(du_oddf),
+ SH_PFC_PIN_GROUP(du_cde),
+ SH_PFC_PIN_GROUP(du_disp),
+ SH_PFC_PIN_GROUP(gether_link_a),
+ SH_PFC_PIN_GROUP(gether_phy_int_a),
+ SH_PFC_PIN_GROUP(gether_mdio_a),
+ SH_PFC_PIN_GROUP(gether_link_b),
+ SH_PFC_PIN_GROUP(gether_phy_int_b),
+ SH_PFC_PIN_GROUP(gether_mdio_b),
+ SH_PFC_PIN_GROUP(gether_magic),
+ SH_PFC_PIN_GROUP(gether_rgmii),
+ SH_PFC_PIN_GROUP(gether_txcrefclk),
+ SH_PFC_PIN_GROUP(gether_txcrefclk_mega),
+ SH_PFC_PIN_GROUP(gether_rmii),
+ SH_PFC_PIN_GROUP(hscif0_data_a),
+ SH_PFC_PIN_GROUP(hscif0_clk_a),
+ SH_PFC_PIN_GROUP(hscif0_ctrl_a),
+ SH_PFC_PIN_GROUP(hscif0_data_b),
+ SH_PFC_PIN_GROUP(hscif0_clk_b),
+ SH_PFC_PIN_GROUP(hscif0_ctrl_b),
+ SH_PFC_PIN_GROUP(hscif1_data),
+ SH_PFC_PIN_GROUP(hscif1_clk),
+ SH_PFC_PIN_GROUP(hscif1_ctrl),
+ SH_PFC_PIN_GROUP(hscif2_data),
+ SH_PFC_PIN_GROUP(hscif2_clk),
+ SH_PFC_PIN_GROUP(hscif2_ctrl),
+ SH_PFC_PIN_GROUP(hscif3_data),
+ SH_PFC_PIN_GROUP(hscif3_clk),
+ SH_PFC_PIN_GROUP(hscif3_ctrl),
+ SH_PFC_PIN_GROUP(i2c0),
+ SH_PFC_PIN_GROUP(i2c1),
+ SH_PFC_PIN_GROUP(i2c2),
+ SH_PFC_PIN_GROUP(i2c3),
+ SH_PFC_PIN_GROUP(i2c4),
+ SH_PFC_PIN_GROUP(i2c5),
+ SH_PFC_PIN_GROUP(intc_ex_irq0),
+ SH_PFC_PIN_GROUP(intc_ex_irq1),
+ SH_PFC_PIN_GROUP(intc_ex_irq2),
+ SH_PFC_PIN_GROUP(intc_ex_irq3),
+ SH_PFC_PIN_GROUP(intc_ex_irq4),
+ SH_PFC_PIN_GROUP(intc_ex_irq5),
+ SH_PFC_PIN_GROUP(mmc_data1),
+ SH_PFC_PIN_GROUP(mmc_data4),
+ SH_PFC_PIN_GROUP(mmc_data8),
+ SH_PFC_PIN_GROUP(mmc_ctrl),
+ SH_PFC_PIN_GROUP(mmc_cd),
+ SH_PFC_PIN_GROUP(mmc_wp),
+ SH_PFC_PIN_GROUP(mmc_ds),
+ SH_PFC_PIN_GROUP(msiof0_clk),
+ SH_PFC_PIN_GROUP(msiof0_sync),
+ SH_PFC_PIN_GROUP(msiof0_ss1),
+ SH_PFC_PIN_GROUP(msiof0_ss2),
+ SH_PFC_PIN_GROUP(msiof0_txd),
+ SH_PFC_PIN_GROUP(msiof0_rxd),
+ SH_PFC_PIN_GROUP(msiof1_clk),
+ SH_PFC_PIN_GROUP(msiof1_sync),
+ SH_PFC_PIN_GROUP(msiof1_ss1),
+ SH_PFC_PIN_GROUP(msiof1_ss2),
+ SH_PFC_PIN_GROUP(msiof1_txd),
+ SH_PFC_PIN_GROUP(msiof1_rxd),
+ SH_PFC_PIN_GROUP(msiof2_clk),
+ SH_PFC_PIN_GROUP(msiof2_sync),
+ SH_PFC_PIN_GROUP(msiof2_ss1),
+ SH_PFC_PIN_GROUP(msiof2_ss2),
+ SH_PFC_PIN_GROUP(msiof2_txd),
+ SH_PFC_PIN_GROUP(msiof2_rxd),
+ SH_PFC_PIN_GROUP(msiof3_clk),
+ SH_PFC_PIN_GROUP(msiof3_sync),
+ SH_PFC_PIN_GROUP(msiof3_ss1),
+ SH_PFC_PIN_GROUP(msiof3_ss2),
+ SH_PFC_PIN_GROUP(msiof3_txd),
+ SH_PFC_PIN_GROUP(msiof3_rxd),
+ SH_PFC_PIN_GROUP(pwm0_a),
+ SH_PFC_PIN_GROUP(pwm0_b),
+ SH_PFC_PIN_GROUP(pwm1_a),
+ SH_PFC_PIN_GROUP(pwm1_b),
+ SH_PFC_PIN_GROUP(pwm2_a),
+ SH_PFC_PIN_GROUP(pwm2_b),
+ SH_PFC_PIN_GROUP(pwm3_a),
+ SH_PFC_PIN_GROUP(pwm3_b),
+ SH_PFC_PIN_GROUP(pwm4_a),
+ SH_PFC_PIN_GROUP(pwm4_b),
+ SH_PFC_PIN_GROUP(scif0_data),
+ SH_PFC_PIN_GROUP(scif0_clk),
+ SH_PFC_PIN_GROUP(scif0_ctrl),
+ SH_PFC_PIN_GROUP(scif1_data_a),
+ SH_PFC_PIN_GROUP(scif1_clk),
+ SH_PFC_PIN_GROUP(scif1_ctrl),
+ SH_PFC_PIN_GROUP(scif1_data_b),
+ SH_PFC_PIN_GROUP(scif3_data),
+ SH_PFC_PIN_GROUP(scif3_clk),
+ SH_PFC_PIN_GROUP(scif3_ctrl),
+ SH_PFC_PIN_GROUP(scif4_data),
+ SH_PFC_PIN_GROUP(scif4_clk),
+ SH_PFC_PIN_GROUP(scif4_ctrl),
+ SH_PFC_PIN_GROUP(scif_clk_a),
+ SH_PFC_PIN_GROUP(scif_clk_b),
+ SH_PFC_PIN_GROUP(tmu_tclk1_a),
+ SH_PFC_PIN_GROUP(tmu_tclk1_b),
+ SH_PFC_PIN_GROUP(tmu_tclk2_a),
+ SH_PFC_PIN_GROUP(tmu_tclk2_b),
+ SH_PFC_PIN_GROUP(tpu_to0),
+ SH_PFC_PIN_GROUP(tpu_to1),
+ SH_PFC_PIN_GROUP(tpu_to2),
+ SH_PFC_PIN_GROUP(tpu_to3),
+ VIN_DATA_PIN_GROUP(vin0_data, 8),
+ VIN_DATA_PIN_GROUP(vin0_data, 10),
+ VIN_DATA_PIN_GROUP(vin0_data, 12),
+ VIN_DATA_PIN_GROUP(vin0_data, 16),
+ SH_PFC_PIN_GROUP(vin0_data18),
+ VIN_DATA_PIN_GROUP(vin0_data, 20),
+ VIN_DATA_PIN_GROUP(vin0_data, 24),
+ SH_PFC_PIN_GROUP(vin0_sync),
+ SH_PFC_PIN_GROUP(vin0_field),
+ SH_PFC_PIN_GROUP(vin0_clkenb),
+ SH_PFC_PIN_GROUP(vin0_clk),
+ SH_PFC_PIN_GROUP(vin1_data8),
+ SH_PFC_PIN_GROUP(vin1_data10),
+ SH_PFC_PIN_GROUP(vin1_data12),
+ SH_PFC_PIN_GROUP(vin1_sync),
+ SH_PFC_PIN_GROUP(vin1_field),
+ SH_PFC_PIN_GROUP(vin1_clkenb),
+ SH_PFC_PIN_GROUP(vin1_clk),
+};
+
+static const char * const avb_groups[] = {
+ "avb_link",
+ "avb_magic",
+ "avb_phy_int",
+ "avb_mdio",
+ "avb_rgmii",
+ "avb_txcrefclk",
+ "avb_avtp_pps",
+ "avb_avtp_capture",
+ "avb_avtp_match",
+};
+
+static const char * const canfd0_groups[] = {
+ "canfd0_data_a",
+ "canfd0_data_b",
+};
+
+static const char * const canfd1_groups[] = {
+ "canfd1_data",
+};
+
+static const char * const canfd_clk_groups[] = {
+ "canfd_clk_a",
+ "canfd_clk_b",
+};
+
+static const char * const du_groups[] = {
+ "du_rgb666",
+ "du_rgb888",
+ "du_clk_out",
+ "du_sync",
+ "du_oddf",
+ "du_cde",
+ "du_disp",
+};
+
+static const char * const gether_groups[] = {
+ "gether_link_a",
+ "gether_phy_int_a",
+ "gether_mdio_a",
+ "gether_link_b",
+ "gether_phy_int_b",
+ "gether_mdio_b",
+ "gether_magic",
+ "gether_rgmii",
+ "gether_txcrefclk",
+ "gether_txcrefclk_mega",
+ "gether_rmii",
+};
+
+static const char * const hscif0_groups[] = {
+ "hscif0_data_a",
+ "hscif0_clk_a",
+ "hscif0_ctrl_a",
+ "hscif0_data_b",
+ "hscif0_clk_b",
+ "hscif0_ctrl_b",
+};
+
+static const char * const hscif1_groups[] = {
+ "hscif1_data",
+ "hscif1_clk",
+ "hscif1_ctrl",
+};
+
+static const char * const hscif2_groups[] = {
+ "hscif2_data",
+ "hscif2_clk",
+ "hscif2_ctrl",
+};
+
+static const char * const hscif3_groups[] = {
+ "hscif3_data",
+ "hscif3_clk",
+ "hscif3_ctrl",
+};
+
+static const char * const i2c0_groups[] = {
+ "i2c0",
+};
+
+static const char * const i2c1_groups[] = {
+ "i2c1",
+};
+
+static const char * const i2c2_groups[] = {
+ "i2c2",
+};
+
+static const char * const i2c3_groups[] = {
+ "i2c3",
+};
+
+static const char * const i2c4_groups[] = {
+ "i2c4",
+};
+
+static const char * const i2c5_groups[] = {
+ "i2c5",
+};
+
+static const char * const intc_ex_groups[] = {
+ "intc_ex_irq0",
+ "intc_ex_irq1",
+ "intc_ex_irq2",
+ "intc_ex_irq3",
+ "intc_ex_irq4",
+ "intc_ex_irq5",
+};
+
+static const char * const mmc_groups[] = {
+ "mmc_data1",
+ "mmc_data4",
+ "mmc_data8",
+ "mmc_ctrl",
+ "mmc_cd",
+ "mmc_wp",
+ "mmc_ds",
+};
+
+static const char * const msiof0_groups[] = {
+ "msiof0_clk",
+ "msiof0_sync",
+ "msiof0_ss1",
+ "msiof0_ss2",
+ "msiof0_txd",
+ "msiof0_rxd",
+};
+
+static const char * const msiof1_groups[] = {
+ "msiof1_clk",
+ "msiof1_sync",
+ "msiof1_ss1",
+ "msiof1_ss2",
+ "msiof1_txd",
+ "msiof1_rxd",
+};
+
+static const char * const msiof2_groups[] = {
+ "msiof2_clk",
+ "msiof2_sync",
+ "msiof2_ss1",
+ "msiof2_ss2",
+ "msiof2_txd",
+ "msiof2_rxd",
+};
+
+static const char * const msiof3_groups[] = {
+ "msiof3_clk",
+ "msiof3_sync",
+ "msiof3_ss1",
+ "msiof3_ss2",
+ "msiof3_txd",
+ "msiof3_rxd",
+};
+
+static const char * const pwm0_groups[] = {
+ "pwm0_a",
+ "pwm0_b",
+};
+
+static const char * const pwm1_groups[] = {
+ "pwm1_a",
+ "pwm1_b",
+};
+
+static const char * const pwm2_groups[] = {
+ "pwm2_a",
+ "pwm2_b",
+};
+
+static const char * const pwm3_groups[] = {
+ "pwm3_a",
+ "pwm3_b",
+};
+
+static const char * const pwm4_groups[] = {
+ "pwm4_a",
+ "pwm4_b",
+};
+
+static const char * const scif0_groups[] = {
+ "scif0_data",
+ "scif0_clk",
+ "scif0_ctrl",
+};
+
+static const char * const scif1_groups[] = {
+ "scif1_data_a",
+ "scif1_clk",
+ "scif1_ctrl",
+ "scif1_data_b",
+};
+
+static const char * const scif3_groups[] = {
+ "scif3_data",
+ "scif3_clk",
+ "scif3_ctrl",
+};
+
+static const char * const scif4_groups[] = {
+ "scif4_data",
+ "scif4_clk",
+ "scif4_ctrl",
+};
+
+static const char * const scif_clk_groups[] = {
+ "scif_clk_a",
+ "scif_clk_b",
+};
+
+static const char * const tmu_groups[] = {
+ "tmu_tclk1_a",
+ "tmu_tclk1_b",
+ "tmu_tclk2_a",
+ "tmu_tclk2_b",
+};
+
+static const char * const tpu_groups[] = {
+ "tpu_to0",
+ "tpu_to1",
+ "tpu_to2",
+ "tpu_to3",
+};
+
+static const char * const vin0_groups[] = {
+ "vin0_data8",
+ "vin0_data10",
+ "vin0_data12",
+ "vin0_data16",
+ "vin0_data18",
+ "vin0_data20",
+ "vin0_data24",
+ "vin0_sync",
+ "vin0_field",
+ "vin0_clkenb",
+ "vin0_clk",
+};
+
+static const char * const vin1_groups[] = {
+ "vin1_data8",
+ "vin1_data10",
+ "vin1_data12",
+ "vin1_sync",
+ "vin1_field",
+ "vin1_clkenb",
+ "vin1_clk",
+};
+
+static const struct sh_pfc_function pinmux_functions[] = {
+ SH_PFC_FUNCTION(avb),
+ SH_PFC_FUNCTION(canfd0),
+ SH_PFC_FUNCTION(canfd1),
+ SH_PFC_FUNCTION(canfd_clk),
+ SH_PFC_FUNCTION(du),
+ SH_PFC_FUNCTION(gether),
+ SH_PFC_FUNCTION(hscif0),
+ SH_PFC_FUNCTION(hscif1),
+ SH_PFC_FUNCTION(hscif2),
+ SH_PFC_FUNCTION(hscif3),
+ SH_PFC_FUNCTION(i2c0),
+ SH_PFC_FUNCTION(i2c1),
+ SH_PFC_FUNCTION(i2c2),
+ SH_PFC_FUNCTION(i2c3),
+ SH_PFC_FUNCTION(i2c4),
+ SH_PFC_FUNCTION(i2c5),
+ SH_PFC_FUNCTION(intc_ex),
+ SH_PFC_FUNCTION(mmc),
+ SH_PFC_FUNCTION(msiof0),
+ SH_PFC_FUNCTION(msiof1),
+ SH_PFC_FUNCTION(msiof2),
+ SH_PFC_FUNCTION(msiof3),
+ SH_PFC_FUNCTION(pwm0),
+ SH_PFC_FUNCTION(pwm1),
+ SH_PFC_FUNCTION(pwm2),
+ SH_PFC_FUNCTION(pwm3),
+ SH_PFC_FUNCTION(pwm4),
+ SH_PFC_FUNCTION(scif0),
+ SH_PFC_FUNCTION(scif1),
+ SH_PFC_FUNCTION(scif3),
+ SH_PFC_FUNCTION(scif4),
+ SH_PFC_FUNCTION(scif_clk),
+ SH_PFC_FUNCTION(tmu),
+ SH_PFC_FUNCTION(tpu),
+ SH_PFC_FUNCTION(vin0),
+ SH_PFC_FUNCTION(vin1),
+};
+
+static const struct pinmux_cfg_reg pinmux_config_regs[] = {
+#define F_(x, y) FN_##y
+#define FM(x) FN_##x
+ { PINMUX_CFG_REG("GPSR0", 0xe6060100, 32, 1) {
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ GP_0_21_FN, GPSR0_21,
+ GP_0_20_FN, GPSR0_20,
+ GP_0_19_FN, GPSR0_19,
+ GP_0_18_FN, GPSR0_18,
+ GP_0_17_FN, GPSR0_17,
+ GP_0_16_FN, GPSR0_16,
+ GP_0_15_FN, GPSR0_15,
+ GP_0_14_FN, GPSR0_14,
+ GP_0_13_FN, GPSR0_13,
+ GP_0_12_FN, GPSR0_12,
+ GP_0_11_FN, GPSR0_11,
+ GP_0_10_FN, GPSR0_10,
+ GP_0_9_FN, GPSR0_9,
+ GP_0_8_FN, GPSR0_8,
+ GP_0_7_FN, GPSR0_7,
+ GP_0_6_FN, GPSR0_6,
+ GP_0_5_FN, GPSR0_5,
+ GP_0_4_FN, GPSR0_4,
+ GP_0_3_FN, GPSR0_3,
+ GP_0_2_FN, GPSR0_2,
+ GP_0_1_FN, GPSR0_1,
+ GP_0_0_FN, GPSR0_0, }
+ },
+ { PINMUX_CFG_REG("GPSR1", 0xe6060104, 32, 1) {
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ GP_1_27_FN, GPSR1_27,
+ GP_1_26_FN, GPSR1_26,
+ GP_1_25_FN, GPSR1_25,
+ GP_1_24_FN, GPSR1_24,
+ GP_1_23_FN, GPSR1_23,
+ GP_1_22_FN, GPSR1_22,
+ GP_1_21_FN, GPSR1_21,
+ GP_1_20_FN, GPSR1_20,
+ GP_1_19_FN, GPSR1_19,
+ GP_1_18_FN, GPSR1_18,
+ GP_1_17_FN, GPSR1_17,
+ GP_1_16_FN, GPSR1_16,
+ GP_1_15_FN, GPSR1_15,
+ GP_1_14_FN, GPSR1_14,
+ GP_1_13_FN, GPSR1_13,
+ GP_1_12_FN, GPSR1_12,
+ GP_1_11_FN, GPSR1_11,
+ GP_1_10_FN, GPSR1_10,
+ GP_1_9_FN, GPSR1_9,
+ GP_1_8_FN, GPSR1_8,
+ GP_1_7_FN, GPSR1_7,
+ GP_1_6_FN, GPSR1_6,
+ GP_1_5_FN, GPSR1_5,
+ GP_1_4_FN, GPSR1_4,
+ GP_1_3_FN, GPSR1_3,
+ GP_1_2_FN, GPSR1_2,
+ GP_1_1_FN, GPSR1_1,
+ GP_1_0_FN, GPSR1_0, }
+ },
+ { PINMUX_CFG_REG("GPSR2", 0xe6060108, 32, 1) {
+ 0, 0,
+ 0, 0,
+ GP_2_29_FN, GPSR2_29,
+ GP_2_28_FN, GPSR2_28,
+ GP_2_27_FN, GPSR2_27,
+ GP_2_26_FN, GPSR2_26,
+ GP_2_25_FN, GPSR2_25,
+ GP_2_24_FN, GPSR2_24,
+ GP_2_23_FN, GPSR2_23,
+ GP_2_22_FN, GPSR2_22,
+ GP_2_21_FN, GPSR2_21,
+ GP_2_20_FN, GPSR2_20,
+ GP_2_19_FN, GPSR2_19,
+ GP_2_18_FN, GPSR2_18,
+ GP_2_17_FN, GPSR2_17,
+ GP_2_16_FN, GPSR2_16,
+ GP_2_15_FN, GPSR2_15,
+ GP_2_14_FN, GPSR2_14,
+ GP_2_13_FN, GPSR2_13,
+ GP_2_12_FN, GPSR2_12,
+ GP_2_11_FN, GPSR2_11,
+ GP_2_10_FN, GPSR2_10,
+ GP_2_9_FN, GPSR2_9,
+ GP_2_8_FN, GPSR2_8,
+ GP_2_7_FN, GPSR2_7,
+ GP_2_6_FN, GPSR2_6,
+ GP_2_5_FN, GPSR2_5,
+ GP_2_4_FN, GPSR2_4,
+ GP_2_3_FN, GPSR2_3,
+ GP_2_2_FN, GPSR2_2,
+ GP_2_1_FN, GPSR2_1,
+ GP_2_0_FN, GPSR2_0, }
+ },
+ { PINMUX_CFG_REG("GPSR3", 0xe606010c, 32, 1) {
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ GP_3_16_FN, GPSR3_16,
+ GP_3_15_FN, GPSR3_15,
+ GP_3_14_FN, GPSR3_14,
+ GP_3_13_FN, GPSR3_13,
+ GP_3_12_FN, GPSR3_12,
+ GP_3_11_FN, GPSR3_11,
+ GP_3_10_FN, GPSR3_10,
+ GP_3_9_FN, GPSR3_9,
+ GP_3_8_FN, GPSR3_8,
+ GP_3_7_FN, GPSR3_7,
+ GP_3_6_FN, GPSR3_6,
+ GP_3_5_FN, GPSR3_5,
+ GP_3_4_FN, GPSR3_4,
+ GP_3_3_FN, GPSR3_3,
+ GP_3_2_FN, GPSR3_2,
+ GP_3_1_FN, GPSR3_1,
+ GP_3_0_FN, GPSR3_0, }
+ },
+ { PINMUX_CFG_REG("GPSR4", 0xe6060110, 32, 1) {
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ GP_4_24_FN, GPSR4_24,
+ GP_4_23_FN, GPSR4_23,
+ GP_4_22_FN, GPSR4_22,
+ GP_4_21_FN, GPSR4_21,
+ GP_4_20_FN, GPSR4_20,
+ GP_4_19_FN, GPSR4_19,
+ GP_4_18_FN, GPSR4_18,
+ GP_4_17_FN, GPSR4_17,
+ GP_4_16_FN, GPSR4_16,
+ GP_4_15_FN, GPSR4_15,
+ GP_4_14_FN, GPSR4_14,
+ GP_4_13_FN, GPSR4_13,
+ GP_4_12_FN, GPSR4_12,
+ GP_4_11_FN, GPSR4_11,
+ GP_4_10_FN, GPSR4_10,
+ GP_4_9_FN, GPSR4_9,
+ GP_4_8_FN, GPSR4_8,
+ GP_4_7_FN, GPSR4_7,
+ GP_4_6_FN, GPSR4_6,
+ GP_4_5_FN, GPSR4_5,
+ GP_4_4_FN, GPSR4_4,
+ GP_4_3_FN, GPSR4_3,
+ GP_4_2_FN, GPSR4_2,
+ GP_4_1_FN, GPSR4_1,
+ GP_4_0_FN, GPSR4_0, }
+ },
+ { PINMUX_CFG_REG("GPSR5", 0xe6060114, 32, 1) {
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ GP_5_14_FN, GPSR5_14,
+ GP_5_13_FN, GPSR5_13,
+ GP_5_12_FN, GPSR5_12,
+ GP_5_11_FN, GPSR5_11,
+ GP_5_10_FN, GPSR5_10,
+ GP_5_9_FN, GPSR5_9,
+ GP_5_8_FN, GPSR5_8,
+ GP_5_7_FN, GPSR5_7,
+ GP_5_6_FN, GPSR5_6,
+ GP_5_5_FN, GPSR5_5,
+ GP_5_4_FN, GPSR5_4,
+ GP_5_3_FN, GPSR5_3,
+ GP_5_2_FN, GPSR5_2,
+ GP_5_1_FN, GPSR5_1,
+ GP_5_0_FN, GPSR5_0, }
+ },
+#undef F_
+#undef FM
+
+#define F_(x, y) x,
+#define FM(x) FN_##x,
+ { PINMUX_CFG_REG("IPSR0", 0xe6060200, 32, 4) {
+ IP0_31_28
+ IP0_27_24
+ IP0_23_20
+ IP0_19_16
+ IP0_15_12
+ IP0_11_8
+ IP0_7_4
+ IP0_3_0 }
+ },
+ { PINMUX_CFG_REG("IPSR1", 0xe6060204, 32, 4) {
+ IP1_31_28
+ IP1_27_24
+ IP1_23_20
+ IP1_19_16
+ IP1_15_12
+ IP1_11_8
+ IP1_7_4
+ IP1_3_0 }
+ },
+ { PINMUX_CFG_REG("IPSR2", 0xe6060208, 32, 4) {
+ IP2_31_28
+ IP2_27_24
+ IP2_23_20
+ IP2_19_16
+ IP2_15_12
+ IP2_11_8
+ IP2_7_4
+ IP2_3_0 }
+ },
+ { PINMUX_CFG_REG("IPSR3", 0xe606020c, 32, 4) {
+ IP3_31_28
+ IP3_27_24
+ IP3_23_20
+ IP3_19_16
+ IP3_15_12
+ IP3_11_8
+ IP3_7_4
+ IP3_3_0 }
+ },
+ { PINMUX_CFG_REG("IPSR4", 0xe6060210, 32, 4) {
+ IP4_31_28
+ IP4_27_24
+ IP4_23_20
+ IP4_19_16
+ IP4_15_12
+ IP4_11_8
+ IP4_7_4
+ IP4_3_0 }
+ },
+ { PINMUX_CFG_REG("IPSR5", 0xe6060214, 32, 4) {
+ IP5_31_28
+ IP5_27_24
+ IP5_23_20
+ IP5_19_16
+ IP5_15_12
+ IP5_11_8
+ IP5_7_4
+ IP5_3_0 }
+ },
+ { PINMUX_CFG_REG("IPSR6", 0xe6060218, 32, 4) {
+ IP6_31_28
+ IP6_27_24
+ IP6_23_20
+ IP6_19_16
+ IP6_15_12
+ IP6_11_8
+ IP6_7_4
+ IP6_3_0 }
+ },
+ { PINMUX_CFG_REG("IPSR7", 0xe606021c, 32, 4) {
+ IP7_31_28
+ IP7_27_24
+ IP7_23_20
+ IP7_19_16
+ IP7_15_12
+ IP7_11_8
+ IP7_7_4
+ IP7_3_0 }
+ },
+ { PINMUX_CFG_REG("IPSR8", 0xe6060220, 32, 4) {
+ IP8_31_28
+ IP8_27_24
+ IP8_23_20
+ IP8_19_16
+ IP8_15_12
+ IP8_11_8
+ IP8_7_4
+ IP8_3_0 }
+ },
+ { PINMUX_CFG_REG("IPSR9", 0xe6060224, 32, 4) {
+ IP9_31_28
+ IP9_27_24
+ IP9_23_20
+ IP9_19_16
+ IP9_15_12
+ IP9_11_8
+ IP9_7_4
+ IP9_3_0 }
+ },
+ { PINMUX_CFG_REG("IPSR10", 0xe6060228, 32, 4) {
+ IP10_31_28
+ IP10_27_24
+ IP10_23_20
+ IP10_19_16
+ IP10_15_12
+ IP10_11_8
+ IP10_7_4
+ IP10_3_0 }
+ },
+#undef F_
+#undef FM
+
+#define F_(x, y) x,
+#define FM(x) FN_##x,
+ { PINMUX_CFG_REG_VAR("MOD_SEL0", 0xe6060500, 32,
+ 4, 4, 4, 4,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) {
+ /* RESERVED 31, 30, 29, 28 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* RESERVED 27, 26, 25, 24 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* RESERVED 23, 22, 21, 20 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* RESERVED 19, 18, 17, 16 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* RESERVED 15, 14, 13, 12 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ MOD_SEL0_11
+ MOD_SEL0_10
+ MOD_SEL0_9
+ MOD_SEL0_8
+ MOD_SEL0_7
+ MOD_SEL0_6
+ MOD_SEL0_5
+ MOD_SEL0_4
+ 0, 0,
+ MOD_SEL0_2
+ MOD_SEL0_1
+ MOD_SEL0_0 }
+ },
+ { },
+};
+
+const struct sh_pfc_soc_info r8a77980_pinmux_info = {
+ .name = "r8a77980_pfc",
+ .unlock_reg = 0xe6060000, /* PMMR */
+
+ .function = { PINMUX_FUNCTION_BEGIN, PINMUX_FUNCTION_END },
+
+ .pins = pinmux_pins,
+ .nr_pins = ARRAY_SIZE(pinmux_pins),
+ .groups = pinmux_groups,
+ .nr_groups = ARRAY_SIZE(pinmux_groups),
+ .functions = pinmux_functions,
+ .nr_functions = ARRAY_SIZE(pinmux_functions),
+
+ .cfg_regs = pinmux_config_regs,
+
+ .pinmux_data = pinmux_data,
+ .pinmux_data_size = ARRAY_SIZE(pinmux_data),
+};
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77995.c b/drivers/pinctrl/sh-pfc/pfc-r8a77995.c
index a4927b78a17b..adade5b7ffbc 100644
--- a/drivers/pinctrl/sh-pfc/pfc-r8a77995.c
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a77995.c
@@ -988,11 +988,11 @@ static const unsigned int avb0_phy_int_pins[] = {
static const unsigned int avb0_phy_int_mux[] = {
AVB0_PHY_INT_MARK,
};
-static const unsigned int avb0_mdc_pins[] = {
+static const unsigned int avb0_mdio_pins[] = {
/* AVB0_MDC, AVB0_MDIO */
RCAR_GP_PIN(5, 17), RCAR_GP_PIN(5, 16),
};
-static const unsigned int avb0_mdc_mux[] = {
+static const unsigned int avb0_mdio_mux[] = {
AVB0_MDC_MARK, AVB0_MDIO_MARK,
};
static const unsigned int avb0_mii_pins[] = {
@@ -1114,6 +1114,87 @@ static const unsigned int canfd1_data_mux[] = {
CANFD1_TX_MARK, CANFD1_RX_MARK,
};
+/* - DU --------------------------------------------------------------------- */
+static const unsigned int du_rgb666_pins[] = {
+ /* R[7:2], G[7:2], B[7:2] */
+ RCAR_GP_PIN(1, 23), RCAR_GP_PIN(1, 22), RCAR_GP_PIN(1, 21),
+ RCAR_GP_PIN(1, 20), RCAR_GP_PIN(1, 19), RCAR_GP_PIN(1, 18),
+ RCAR_GP_PIN(1, 15), RCAR_GP_PIN(1, 14), RCAR_GP_PIN(1, 13),
+ RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 11), RCAR_GP_PIN(1, 10),
+ RCAR_GP_PIN(1, 7), RCAR_GP_PIN(1, 6), RCAR_GP_PIN(1, 5),
+ RCAR_GP_PIN(1, 4), RCAR_GP_PIN(1, 3), RCAR_GP_PIN(1, 2),
+};
+static const unsigned int du_rgb666_mux[] = {
+ DU_DR7_MARK, DU_DR6_MARK, DU_DR5_MARK, DU_DR4_MARK,
+ DU_DR3_MARK, DU_DR2_MARK,
+ DU_DG7_MARK, DU_DG6_MARK, DU_DG5_MARK, DU_DG4_MARK,
+ DU_DG3_MARK, DU_DG2_MARK,
+ DU_DB7_MARK, DU_DB6_MARK, DU_DB5_MARK, DU_DB4_MARK,
+ DU_DB3_MARK, DU_DB2_MARK,
+};
+static const unsigned int du_rgb888_pins[] = {
+ /* R[7:0], G[7:0], B[7:0] */
+ RCAR_GP_PIN(1, 23), RCAR_GP_PIN(1, 22), RCAR_GP_PIN(1, 21),
+ RCAR_GP_PIN(1, 20), RCAR_GP_PIN(1, 19), RCAR_GP_PIN(1, 18),
+ RCAR_GP_PIN(1, 17), RCAR_GP_PIN(1, 16),
+ RCAR_GP_PIN(1, 15), RCAR_GP_PIN(1, 14), RCAR_GP_PIN(1, 13),
+ RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 11), RCAR_GP_PIN(1, 10),
+ RCAR_GP_PIN(1, 9), RCAR_GP_PIN(1, 8),
+ RCAR_GP_PIN(1, 7), RCAR_GP_PIN(1, 6), RCAR_GP_PIN(1, 5),
+ RCAR_GP_PIN(1, 4), RCAR_GP_PIN(1, 3), RCAR_GP_PIN(1, 2),
+ RCAR_GP_PIN(1, 1), RCAR_GP_PIN(1, 0),
+};
+static const unsigned int du_rgb888_mux[] = {
+ DU_DR7_MARK, DU_DR6_MARK, DU_DR5_MARK, DU_DR4_MARK,
+ DU_DR3_MARK, DU_DR2_MARK, DU_DR1_MARK, DU_DR0_MARK,
+ DU_DG7_MARK, DU_DG6_MARK, DU_DG5_MARK, DU_DG4_MARK,
+ DU_DG3_MARK, DU_DG2_MARK, DU_DG1_MARK, DU_DG0_MARK,
+ DU_DB7_MARK, DU_DB6_MARK, DU_DB5_MARK, DU_DB4_MARK,
+ DU_DB3_MARK, DU_DB2_MARK, DU_DB1_MARK, DU_DB0_MARK,
+};
+static const unsigned int du_clk_in_1_pins[] = {
+ /* CLKIN */
+ RCAR_GP_PIN(1, 28),
+};
+static const unsigned int du_clk_in_1_mux[] = {
+ DU_DOTCLKIN1_MARK
+};
+static const unsigned int du_clk_out_0_pins[] = {
+ /* CLKOUT */
+ RCAR_GP_PIN(1, 24),
+};
+static const unsigned int du_clk_out_0_mux[] = {
+ DU_DOTCLKOUT0_MARK
+};
+static const unsigned int du_sync_pins[] = {
+ /* VSYNC, HSYNC */
+ RCAR_GP_PIN(1, 26), RCAR_GP_PIN(1, 25),
+};
+static const unsigned int du_sync_mux[] = {
+ DU_VSYNC_MARK, DU_HSYNC_MARK
+};
+static const unsigned int du_disp_cde_pins[] = {
+ /* DISP_CDE */
+ RCAR_GP_PIN(1, 28),
+};
+static const unsigned int du_disp_cde_mux[] = {
+ DU_DISP_CDE_MARK,
+};
+static const unsigned int du_cde_pins[] = {
+ /* CDE */
+ RCAR_GP_PIN(1, 29),
+};
+static const unsigned int du_cde_mux[] = {
+ DU_CDE_MARK,
+};
+static const unsigned int du_disp_pins[] = {
+ /* DISP */
+ RCAR_GP_PIN(1, 27),
+};
+static const unsigned int du_disp_mux[] = {
+ DU_DISP_MARK,
+};
+
/* - I2C -------------------------------------------------------------------- */
static const unsigned int i2c0_pins[] = {
/* SCL, SDA */
@@ -1545,6 +1626,90 @@ static const unsigned int usb0_mux[] = {
USB0_PWEN_MARK, USB0_OVC_MARK,
};
+/* - VIN4 ------------------------------------------------------------------- */
+static const unsigned int vin4_data18_pins[] = {
+ RCAR_GP_PIN(2, 3), RCAR_GP_PIN(2, 4),
+ RCAR_GP_PIN(2, 5), RCAR_GP_PIN(2, 6),
+ RCAR_GP_PIN(2, 7), RCAR_GP_PIN(2, 8),
+ RCAR_GP_PIN(2, 11), RCAR_GP_PIN(2, 12),
+ RCAR_GP_PIN(2, 13), RCAR_GP_PIN(2, 14),
+ RCAR_GP_PIN(2, 15), RCAR_GP_PIN(2, 16),
+ RCAR_GP_PIN(2, 19), RCAR_GP_PIN(2, 20),
+ RCAR_GP_PIN(2, 21), RCAR_GP_PIN(2, 22),
+ RCAR_GP_PIN(2, 23), RCAR_GP_PIN(2, 24),
+};
+static const unsigned int vin4_data18_mux[] = {
+ VI4_DATA2_MARK, VI4_DATA3_MARK,
+ VI4_DATA4_MARK, VI4_DATA5_MARK,
+ VI4_DATA6_MARK, VI4_DATA7_MARK,
+ VI4_DATA10_MARK, VI4_DATA11_MARK,
+ VI4_DATA12_MARK, VI4_DATA13_MARK,
+ VI4_DATA14_MARK, VI4_DATA15_MARK,
+ VI4_DATA18_MARK, VI4_DATA19_MARK,
+ VI4_DATA20_MARK, VI4_DATA21_MARK,
+ VI4_DATA22_MARK, VI4_DATA23_MARK,
+};
+static const union vin_data vin4_data_pins = {
+ .data24 = {
+ RCAR_GP_PIN(2, 1), RCAR_GP_PIN(2, 2),
+ RCAR_GP_PIN(2, 3), RCAR_GP_PIN(2, 4),
+ RCAR_GP_PIN(2, 5), RCAR_GP_PIN(2, 6),
+ RCAR_GP_PIN(2, 7), RCAR_GP_PIN(2, 8),
+ RCAR_GP_PIN(2, 9), RCAR_GP_PIN(2, 10),
+ RCAR_GP_PIN(2, 11), RCAR_GP_PIN(2, 12),
+ RCAR_GP_PIN(2, 13), RCAR_GP_PIN(2, 14),
+ RCAR_GP_PIN(2, 15), RCAR_GP_PIN(2, 16),
+ RCAR_GP_PIN(2, 17), RCAR_GP_PIN(2, 18),
+ RCAR_GP_PIN(2, 19), RCAR_GP_PIN(2, 20),
+ RCAR_GP_PIN(2, 21), RCAR_GP_PIN(2, 22),
+ RCAR_GP_PIN(2, 23), RCAR_GP_PIN(2, 24),
+ },
+};
+static const union vin_data vin4_data_mux = {
+ .data24 = {
+ VI4_DATA0_MARK, VI4_DATA1_MARK,
+ VI4_DATA2_MARK, VI4_DATA3_MARK,
+ VI4_DATA4_MARK, VI4_DATA5_MARK,
+ VI4_DATA6_MARK, VI4_DATA7_MARK,
+ VI4_DATA8_MARK, VI4_DATA9_MARK,
+ VI4_DATA10_MARK, VI4_DATA11_MARK,
+ VI4_DATA12_MARK, VI4_DATA13_MARK,
+ VI4_DATA14_MARK, VI4_DATA15_MARK,
+ VI4_DATA16_MARK, VI4_DATA17_MARK,
+ VI4_DATA18_MARK, VI4_DATA19_MARK,
+ VI4_DATA20_MARK, VI4_DATA21_MARK,
+ VI4_DATA22_MARK, VI4_DATA23_MARK,
+ },
+};
+static const unsigned int vin4_sync_pins[] = {
+ /* HSYNC#, VSYNC# */
+ RCAR_GP_PIN(2, 26), RCAR_GP_PIN(2, 25),
+};
+static const unsigned int vin4_sync_mux[] = {
+ VI4_HSYNC_N_MARK, VI4_VSYNC_N_MARK,
+};
+static const unsigned int vin4_field_pins[] = {
+ /* FIELD */
+ RCAR_GP_PIN(2, 27),
+};
+static const unsigned int vin4_field_mux[] = {
+ VI4_FIELD_MARK,
+};
+static const unsigned int vin4_clkenb_pins[] = {
+ /* CLKENB */
+ RCAR_GP_PIN(2, 28),
+};
+static const unsigned int vin4_clkenb_mux[] = {
+ VI4_CLKENB_MARK,
+};
+static const unsigned int vin4_clk_pins[] = {
+ /* CLK */
+ RCAR_GP_PIN(2, 0),
+};
+static const unsigned int vin4_clk_mux[] = {
+ VI4_CLK_MARK,
+};
+
static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(audio_clk_a),
SH_PFC_PIN_GROUP(audio_clk_b),
@@ -1553,7 +1718,8 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(avb0_link),
SH_PFC_PIN_GROUP(avb0_magic),
SH_PFC_PIN_GROUP(avb0_phy_int),
- SH_PFC_PIN_GROUP(avb0_mdc),
+ SH_PFC_PIN_GROUP_ALIAS(avb0_mdc, avb0_mdio), /* Deprecated */
+ SH_PFC_PIN_GROUP(avb0_mdio),
SH_PFC_PIN_GROUP(avb0_mii),
SH_PFC_PIN_GROUP(avb0_avtp_pps_a),
SH_PFC_PIN_GROUP(avb0_avtp_match_a),
@@ -1568,6 +1734,14 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(can_clk),
SH_PFC_PIN_GROUP(canfd0_data),
SH_PFC_PIN_GROUP(canfd1_data),
+ SH_PFC_PIN_GROUP(du_rgb666),
+ SH_PFC_PIN_GROUP(du_rgb888),
+ SH_PFC_PIN_GROUP(du_clk_in_1),
+ SH_PFC_PIN_GROUP(du_clk_out_0),
+ SH_PFC_PIN_GROUP(du_sync),
+ SH_PFC_PIN_GROUP(du_disp_cde),
+ SH_PFC_PIN_GROUP(du_cde),
+ SH_PFC_PIN_GROUP(du_disp),
SH_PFC_PIN_GROUP(i2c0),
SH_PFC_PIN_GROUP(i2c1),
SH_PFC_PIN_GROUP(i2c2_a),
@@ -1622,6 +1796,17 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(ssi4_ctrl_b),
SH_PFC_PIN_GROUP(ssi4_data_b),
SH_PFC_PIN_GROUP(usb0),
+ VIN_DATA_PIN_GROUP(vin4_data, 8),
+ VIN_DATA_PIN_GROUP(vin4_data, 10),
+ VIN_DATA_PIN_GROUP(vin4_data, 12),
+ VIN_DATA_PIN_GROUP(vin4_data, 16),
+ SH_PFC_PIN_GROUP(vin4_data18),
+ VIN_DATA_PIN_GROUP(vin4_data, 20),
+ VIN_DATA_PIN_GROUP(vin4_data, 24),
+ SH_PFC_PIN_GROUP(vin4_sync),
+ SH_PFC_PIN_GROUP(vin4_field),
+ SH_PFC_PIN_GROUP(vin4_clkenb),
+ SH_PFC_PIN_GROUP(vin4_clk),
};
static const char * const audio_clk_groups[] = {
@@ -1635,7 +1820,8 @@ static const char * const avb0_groups[] = {
"avb0_link",
"avb0_magic",
"avb0_phy_int",
- "avb0_mdc",
+ "avb0_mdc", /* Deprecated, please use "avb0_mdio" instead */
+ "avb0_mdio",
"avb0_mii",
"avb0_avtp_pps_a",
"avb0_avtp_match_a",
@@ -1664,6 +1850,17 @@ static const char * const canfd1_groups[] = {
"canfd1_data",
};
+static const char * const du_groups[] = {
+ "du_rgb666",
+ "du_rgb888",
+ "du_clk_in_1",
+ "du_clk_out_0",
+ "du_sync",
+ "du_disp_cde",
+ "du_cde",
+ "du_disp",
+};
+
static const char * const i2c0_groups[] = {
"i2c0",
};
@@ -1771,6 +1968,20 @@ static const char * const usb0_groups[] = {
"usb0",
};
+static const char * const vin4_groups[] = {
+ "vin4_data8",
+ "vin4_data10",
+ "vin4_data12",
+ "vin4_data16",
+ "vin4_data18",
+ "vin4_data20",
+ "vin4_data24",
+ "vin4_sync",
+ "vin4_field",
+ "vin4_clkenb",
+ "vin4_clk",
+};
+
static const struct sh_pfc_function pinmux_functions[] = {
SH_PFC_FUNCTION(audio_clk),
SH_PFC_FUNCTION(avb0),
@@ -1779,6 +1990,7 @@ static const struct sh_pfc_function pinmux_functions[] = {
SH_PFC_FUNCTION(can_clk),
SH_PFC_FUNCTION(canfd0),
SH_PFC_FUNCTION(canfd1),
+ SH_PFC_FUNCTION(du),
SH_PFC_FUNCTION(i2c0),
SH_PFC_FUNCTION(i2c1),
SH_PFC_FUNCTION(i2c2),
@@ -1797,6 +2009,7 @@ static const struct sh_pfc_function pinmux_functions[] = {
SH_PFC_FUNCTION(scif_clk),
SH_PFC_FUNCTION(ssi),
SH_PFC_FUNCTION(usb0),
+ SH_PFC_FUNCTION(vin4),
};
static const struct pinmux_cfg_reg pinmux_config_regs[] = {
diff --git a/drivers/pinctrl/sh-pfc/pinctrl.c b/drivers/pinctrl/sh-pfc/pinctrl.c
index 736634aee500..70db21638901 100644
--- a/drivers/pinctrl/sh-pfc/pinctrl.c
+++ b/drivers/pinctrl/sh-pfc/pinctrl.c
@@ -75,7 +75,7 @@ static int sh_pfc_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector,
static void sh_pfc_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s,
unsigned offset)
{
- seq_printf(s, "%s", DRV_NAME);
+ seq_puts(s, DRV_NAME);
}
#ifdef CONFIG_OF
diff --git a/drivers/pinctrl/sh-pfc/sh_pfc.h b/drivers/pinctrl/sh-pfc/sh_pfc.h
index 5747ab0472df..7fad897cd9f5 100644
--- a/drivers/pinctrl/sh-pfc/sh_pfc.h
+++ b/drivers/pinctrl/sh-pfc/sh_pfc.h
@@ -39,13 +39,14 @@ struct sh_pfc_pin {
unsigned int configs;
};
-#define SH_PFC_PIN_GROUP(n) \
+#define SH_PFC_PIN_GROUP_ALIAS(alias, n) \
{ \
- .name = #n, \
+ .name = #alias, \
.pins = n##_pins, \
.mux = n##_mux, \
.nr_pins = ARRAY_SIZE(n##_pins), \
}
+#define SH_PFC_PIN_GROUP(n) SH_PFC_PIN_GROUP_ALIAS(n, n)
struct sh_pfc_pin_group {
const char *name;
@@ -283,7 +284,9 @@ extern const struct sh_pfc_soc_info r8a7794_pinmux_info;
extern const struct sh_pfc_soc_info r8a7795_pinmux_info;
extern const struct sh_pfc_soc_info r8a7795es1_pinmux_info;
extern const struct sh_pfc_soc_info r8a7796_pinmux_info;
+extern const struct sh_pfc_soc_info r8a77965_pinmux_info;
extern const struct sh_pfc_soc_info r8a77970_pinmux_info;
+extern const struct sh_pfc_soc_info r8a77980_pinmux_info;
extern const struct sh_pfc_soc_info r8a77995_pinmux_info;
extern const struct sh_pfc_soc_info sh7203_pinmux_info;
extern const struct sh_pfc_soc_info sh7264_pinmux_info;
@@ -470,9 +473,13 @@ extern const struct sh_pfc_soc_info shx3_pinmux_info;
PORT_GP_CFG_1(bank, 23, fn, sfx, cfg)
#define PORT_GP_24(bank, fn, sfx) PORT_GP_CFG_24(bank, fn, sfx, 0)
-#define PORT_GP_CFG_26(bank, fn, sfx, cfg) \
+#define PORT_GP_CFG_25(bank, fn, sfx, cfg) \
PORT_GP_CFG_24(bank, fn, sfx, cfg), \
- PORT_GP_CFG_1(bank, 24, fn, sfx, cfg), \
+ PORT_GP_CFG_1(bank, 24, fn, sfx, cfg)
+#define PORT_GP_25(bank, fn, sfx) PORT_GP_CFG_25(bank, fn, sfx, 0)
+
+#define PORT_GP_CFG_26(bank, fn, sfx, cfg) \
+ PORT_GP_CFG_25(bank, fn, sfx, cfg), \
PORT_GP_CFG_1(bank, 25, fn, sfx, cfg)
#define PORT_GP_26(bank, fn, sfx) PORT_GP_CFG_26(bank, fn, sfx, 0)
diff --git a/drivers/pinctrl/stm32/Kconfig b/drivers/pinctrl/stm32/Kconfig
index 1c4e00b2eb65..cd3936e3afaa 100644
--- a/drivers/pinctrl/stm32/Kconfig
+++ b/drivers/pinctrl/stm32/Kconfig
@@ -11,37 +11,37 @@ config PINCTRL_STM32
config PINCTRL_STM32F429
bool "STMicroelectronics STM32F429 pin control" if COMPILE_TEST && !MACH_STM32F429
- depends on OF
+ depends on OF && HAS_IOMEM
default MACH_STM32F429
select PINCTRL_STM32
config PINCTRL_STM32F469
bool "STMicroelectronics STM32F469 pin control" if COMPILE_TEST && !MACH_STM32F469
- depends on OF
+ depends on OF && HAS_IOMEM
default MACH_STM32F469
select PINCTRL_STM32
config PINCTRL_STM32F746
bool "STMicroelectronics STM32F746 pin control" if COMPILE_TEST && !MACH_STM32F746
- depends on OF
+ depends on OF && HAS_IOMEM
default MACH_STM32F746
select PINCTRL_STM32
config PINCTRL_STM32F769
bool "STMicroelectronics STM32F769 pin control" if COMPILE_TEST && !MACH_STM32F769
- depends on OF
+ depends on OF && HAS_IOMEM
default MACH_STM32F769
select PINCTRL_STM32
config PINCTRL_STM32H743
bool "STMicroelectronics STM32H743 pin control" if COMPILE_TEST && !MACH_STM32H743
- depends on OF
+ depends on OF && HAS_IOMEM
default MACH_STM32H743
select PINCTRL_STM32
config PINCTRL_STM32MP157
bool "STMicroelectronics STM32MP157 pin control" if COMPILE_TEST && !MACH_STM32MP157
- depends on OF
+ depends on OF && HAS_IOMEM
default MACH_STM32MP157
select PINCTRL_STM32
endif
diff --git a/drivers/pinctrl/stm32/pinctrl-stm32.c b/drivers/pinctrl/stm32/pinctrl-stm32.c
index 617df163067f..6cbcff42ba47 100644
--- a/drivers/pinctrl/stm32/pinctrl-stm32.c
+++ b/drivers/pinctrl/stm32/pinctrl-stm32.c
@@ -268,10 +268,11 @@ static void stm32_gpio_irq_release_resources(struct irq_data *irq_data)
static struct irq_chip stm32_gpio_irq_chip = {
.name = "stm32gpio",
- .irq_eoi = irq_chip_eoi_parent,
+ .irq_ack = irq_chip_ack_parent,
.irq_mask = irq_chip_mask_parent,
.irq_unmask = irq_chip_unmask_parent,
.irq_set_type = irq_chip_set_type_parent,
+ .irq_set_wake = irq_chip_set_wake_parent,
.irq_request_resources = stm32_gpio_irq_request_resources,
.irq_release_resources = stm32_gpio_irq_release_resources,
};
diff --git a/drivers/pinctrl/sunxi/Kconfig b/drivers/pinctrl/sunxi/Kconfig
index bfce99d86dfc..5de1f63b07bb 100644
--- a/drivers/pinctrl/sunxi/Kconfig
+++ b/drivers/pinctrl/sunxi/Kconfig
@@ -77,4 +77,8 @@ config PINCTRL_SUN50I_H5
def_bool ARM64 && ARCH_SUNXI
select PINCTRL_SUNXI
+config PINCTRL_SUN50I_H6
+ def_bool ARM64 && ARCH_SUNXI
+ select PINCTRL_SUNXI
+
endif
diff --git a/drivers/pinctrl/sunxi/Makefile b/drivers/pinctrl/sunxi/Makefile
index 12a752e836ef..3c4aec6611e9 100644
--- a/drivers/pinctrl/sunxi/Makefile
+++ b/drivers/pinctrl/sunxi/Makefile
@@ -18,5 +18,6 @@ obj-$(CONFIG_PINCTRL_SUN8I_H3) += pinctrl-sun8i-h3.o
obj-$(CONFIG_PINCTRL_SUN8I_H3_R) += pinctrl-sun8i-h3-r.o
obj-$(CONFIG_PINCTRL_SUN8I_V3S) += pinctrl-sun8i-v3s.o
obj-$(CONFIG_PINCTRL_SUN50I_H5) += pinctrl-sun50i-h5.o
+obj-$(CONFIG_PINCTRL_SUN50I_H6) += pinctrl-sun50i-h6.o
obj-$(CONFIG_PINCTRL_SUN9I_A80) += pinctrl-sun9i-a80.o
obj-$(CONFIG_PINCTRL_SUN9I_A80_R) += pinctrl-sun9i-a80-r.o
diff --git a/drivers/pinctrl/sunxi/pinctrl-sun50i-h6.c b/drivers/pinctrl/sunxi/pinctrl-sun50i-h6.c
new file mode 100644
index 000000000000..aa8b58125568
--- /dev/null
+++ b/drivers/pinctrl/sunxi/pinctrl-sun50i-h6.c
@@ -0,0 +1,614 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Allwinner H6 SoC pinctrl driver.
+ *
+ * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.io>
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/pinctrl.h>
+
+#include "pinctrl-sunxi.h"
+
+static const struct sunxi_desc_pin h6_pins[] = {
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 0),
+ SUNXI_FUNCTION(0x2, "emac")), /* ERXD1 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 1),
+ SUNXI_FUNCTION(0x2, "emac")), /* ERXD0 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 2),
+ SUNXI_FUNCTION(0x2, "emac")), /* ECRS_DV */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 3),
+ SUNXI_FUNCTION(0x2, "emac")), /* ERXERR */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 4),
+ SUNXI_FUNCTION(0x2, "emac")), /* ETXD1 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 5),
+ SUNXI_FUNCTION(0x2, "emac")), /* ETXD0 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 6),
+ SUNXI_FUNCTION(0x2, "emac")), /* ETXCK */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 7),
+ SUNXI_FUNCTION(0x2, "emac")), /* ETXEN */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 8),
+ SUNXI_FUNCTION(0x2, "emac")), /* EMDC */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 9),
+ SUNXI_FUNCTION(0x2, "emac")), /* EMDIO */
+ /* Hole */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 0),
+ SUNXI_FUNCTION(0x2, "ccir"), /* CLK */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 0)),
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 1),
+ SUNXI_FUNCTION(0x2, "ccir"), /* DE */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 1)),
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 2),
+ SUNXI_FUNCTION(0x2, "ccir"), /* HSYNC */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 2)),
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 3),
+ SUNXI_FUNCTION(0x2, "ccir"), /* VSYNC */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 3)),
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 4),
+ SUNXI_FUNCTION(0x2, "ccir"), /* DO0 */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 4)),
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 5),
+ SUNXI_FUNCTION(0x2, "ccir"), /* DO1 */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 5)),
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 6),
+ SUNXI_FUNCTION(0x2, "ccir"), /* DO2 */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 6)),
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 7),
+ SUNXI_FUNCTION(0x2, "ccir"), /* DO3 */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 7)),
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 8),
+ SUNXI_FUNCTION(0x2, "ccir"), /* DO4 */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 8)),
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 9),
+ SUNXI_FUNCTION(0x2, "ccir"), /* DO5 */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 9)),
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 10),
+ SUNXI_FUNCTION(0x2, "ccir"), /* DO6 */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 10)),
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 11),
+ SUNXI_FUNCTION(0x2, "ccir"), /* DO7 */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 11)),
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 12),
+ SUNXI_FUNCTION(0x2, "i2s3"), /* SYNC */
+ SUNXI_FUNCTION(0x4, "h_i2s3"), /* SYNC */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 12)),
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 13),
+ SUNXI_FUNCTION(0x2, "i2s3"), /* CLK */
+ SUNXI_FUNCTION(0x4, "h_i2s3"), /* CLK */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 13)),
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 14),
+ SUNXI_FUNCTION(0x2, "i2s3"), /* DOUT */
+ SUNXI_FUNCTION(0x4, "h_i2s3"), /* DOUT */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 14)),
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 15),
+ SUNXI_FUNCTION(0x2, "i2s3"), /* DIN */
+ SUNXI_FUNCTION(0x4, "h_i2s3"), /* DIN */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 15)),
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 16),
+ SUNXI_FUNCTION(0x2, "i2s3"), /* MCLK */
+ SUNXI_FUNCTION(0x4, "h_i2s3"), /* MCLK */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 16)),
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 17),
+ SUNXI_FUNCTION(0x2, "i2c3"), /* SCK */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 17)),
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 18),
+ SUNXI_FUNCTION(0x2, "i2c3"), /* SDA */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 18)),
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 19),
+ SUNXI_FUNCTION(0x2, "pwm1"),
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 19)),
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 20),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 20)),
+ /* Hole */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 0),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0"), /* WE */
+ SUNXI_FUNCTION(0x4, "spi0")), /* CLK */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 1),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0"), /* ALE */
+ SUNXI_FUNCTION(0x3, "mmc2")), /* DS */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 2),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0"), /* CLE */
+ SUNXI_FUNCTION(0x4, "spi0")), /* MOSI */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 3),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0"), /* CE0 */
+ SUNXI_FUNCTION(0x4, "spi0")), /* MISO */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 4),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0"), /* RE */
+ SUNXI_FUNCTION(0x3, "mmc2")), /* CLK */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 5),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0"), /* RB0 */
+ SUNXI_FUNCTION(0x3, "mmc2"), /* CMD */
+ SUNXI_FUNCTION(0x4, "spi0")), /* CS */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 6),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0"), /* DQ0 */
+ SUNXI_FUNCTION(0x3, "mmc2"), /* D0 */
+ SUNXI_FUNCTION(0x4, "spi0")), /* HOLD */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 7),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0"), /* DQ1 */
+ SUNXI_FUNCTION(0x3, "mmc2"), /* D1 */
+ SUNXI_FUNCTION(0x4, "spi0")), /* WP */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 8),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0"), /* DQ2 */
+ SUNXI_FUNCTION(0x3, "mmc2")), /* D2 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 9),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0"), /* DQ3 */
+ SUNXI_FUNCTION(0x3, "mmc2")), /* D3 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 10),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0"), /* DQ4 */
+ SUNXI_FUNCTION(0x3, "mmc2")), /* D4 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 11),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0"), /* DQ5 */
+ SUNXI_FUNCTION(0x3, "mmc2")), /* D5 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 12),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0"), /* DQ6 */
+ SUNXI_FUNCTION(0x3, "mmc2")), /* D6 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 13),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0"), /* DQ7 */
+ SUNXI_FUNCTION(0x3, "mmc2")), /* D7 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 14),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0"), /* DQS */
+ SUNXI_FUNCTION(0x3, "mmc2")), /* RST */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 15),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0")), /* CE1 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 16),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0")), /* RB1 */
+ /* Hole */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 0),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd0"), /* D2 */
+ SUNXI_FUNCTION(0x3, "ts0"), /* CLK */
+ SUNXI_FUNCTION(0x4, "csi"), /* PCLK */
+ SUNXI_FUNCTION(0x5, "emac")), /* ERXD3 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 1),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd0"), /* D3 */
+ SUNXI_FUNCTION(0x3, "ts0"), /* ERR */
+ SUNXI_FUNCTION(0x4, "csi"), /* MCLK */
+ SUNXI_FUNCTION(0x5, "emac")), /* ERXD2 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 2),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd0"), /* D4 */
+ SUNXI_FUNCTION(0x3, "ts0"), /* SYNC */
+ SUNXI_FUNCTION(0x4, "csi"), /* HSYNC */
+ SUNXI_FUNCTION(0x5, "emac")), /* ERXD1 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 3),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd0"), /* D5 */
+ SUNXI_FUNCTION(0x3, "ts0"), /* DVLD */
+ SUNXI_FUNCTION(0x4, "csi"), /* VSYNC */
+ SUNXI_FUNCTION(0x5, "emac")), /* ERXD0 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 4),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd0"), /* D6 */
+ SUNXI_FUNCTION(0x3, "ts0"), /* D0 */
+ SUNXI_FUNCTION(0x4, "csi"), /* D0 */
+ SUNXI_FUNCTION(0x5, "emac")), /* ERXCK */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 5),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd0"), /* D7 */
+ SUNXI_FUNCTION(0x3, "ts0"), /* D1 */
+ SUNXI_FUNCTION(0x4, "csi"), /* D1 */
+ SUNXI_FUNCTION(0x5, "emac")), /* ERXCTL */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 6),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd0"), /* D10 */
+ SUNXI_FUNCTION(0x3, "ts0"), /* D2 */
+ SUNXI_FUNCTION(0x4, "csi"), /* D2 */
+ SUNXI_FUNCTION(0x5, "emac")), /* ENULL */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 7),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd0"), /* D11 */
+ SUNXI_FUNCTION(0x3, "ts0"), /* D3 */
+ SUNXI_FUNCTION(0x4, "csi"), /* D3 */
+ SUNXI_FUNCTION(0x5, "emac")), /* ETXD3 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 8),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd0"), /* D12 */
+ SUNXI_FUNCTION(0x3, "ts0"), /* D4 */
+ SUNXI_FUNCTION(0x4, "csi"), /* D4 */
+ SUNXI_FUNCTION(0x5, "emac")), /* ETXD2 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 9),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd0"), /* D13 */
+ SUNXI_FUNCTION(0x3, "ts0"), /* D5 */
+ SUNXI_FUNCTION(0x4, "csi"), /* D5 */
+ SUNXI_FUNCTION(0x5, "emac")), /* ETXD1 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 10),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd0"), /* D14 */
+ SUNXI_FUNCTION(0x3, "ts0"), /* D6 */
+ SUNXI_FUNCTION(0x4, "csi"), /* D6 */
+ SUNXI_FUNCTION(0x5, "emac")), /* ETXD0 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 11),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd0"), /* D15 */
+ SUNXI_FUNCTION(0x3, "ts0"), /* D7 */
+ SUNXI_FUNCTION(0x4, "csi"), /* D7 */
+ SUNXI_FUNCTION(0x5, "emac")), /* ETXCK */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 12),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd0"), /* D18 */
+ SUNXI_FUNCTION(0x3, "ts1"), /* CLK */
+ SUNXI_FUNCTION(0x4, "csi"), /* SCK */
+ SUNXI_FUNCTION(0x5, "emac")), /* ETXCTL */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 13),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd0"), /* D19 */
+ SUNXI_FUNCTION(0x3, "ts1"), /* ERR */
+ SUNXI_FUNCTION(0x4, "csi"), /* SDA */
+ SUNXI_FUNCTION(0x5, "emac")), /* ECLKIN */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 14),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd0"), /* D20 */
+ SUNXI_FUNCTION(0x3, "ts1"), /* SYNC */
+ SUNXI_FUNCTION(0x4, "dmic"), /* CLK */
+ SUNXI_FUNCTION(0x5, "csi")), /* D8 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 15),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd0"), /* D21 */
+ SUNXI_FUNCTION(0x3, "ts1"), /* DVLD */
+ SUNXI_FUNCTION(0x4, "dmic"), /* DATA0 */
+ SUNXI_FUNCTION(0x5, "csi")), /* D9 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 16),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd0"), /* D22 */
+ SUNXI_FUNCTION(0x3, "ts1"), /* D0 */
+ SUNXI_FUNCTION(0x4, "dmic")), /* DATA1 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 17),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd0"), /* D23 */
+ SUNXI_FUNCTION(0x3, "ts2"), /* CLK */
+ SUNXI_FUNCTION(0x4, "dmic")), /* DATA2 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 18),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd0"), /* CLK */
+ SUNXI_FUNCTION(0x3, "ts2"), /* ERR */
+ SUNXI_FUNCTION(0x4, "dmic")), /* DATA3 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 19),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd0"), /* DE */
+ SUNXI_FUNCTION(0x3, "ts2"), /* SYNC */
+ SUNXI_FUNCTION(0x4, "uart2"), /* TX */
+ SUNXI_FUNCTION(0x5, "emac")), /* EMDC */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 20),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd0"), /* HSYNC */
+ SUNXI_FUNCTION(0x3, "ts2"), /* DVLD */
+ SUNXI_FUNCTION(0x4, "uart2"), /* RX */
+ SUNXI_FUNCTION(0x5, "emac")), /* EMDIO */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 21),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "lcd0"), /* VSYNC */
+ SUNXI_FUNCTION(0x3, "ts2"), /* D0 */
+ SUNXI_FUNCTION(0x4, "uart2")), /* RTS */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 22),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "pwm"), /* PWM0 */
+ SUNXI_FUNCTION(0x3, "ts3"), /* CLK */
+ SUNXI_FUNCTION(0x4, "uart2")), /* CTS */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 23),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "i2c2"), /* SCK */
+ SUNXI_FUNCTION(0x3, "ts3"), /* ERR */
+ SUNXI_FUNCTION(0x4, "uart3"), /* TX */
+ SUNXI_FUNCTION(0x5, "jtag")), /* MS */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 24),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "i2c2"), /* SDA */
+ SUNXI_FUNCTION(0x3, "ts3"), /* SYNC */
+ SUNXI_FUNCTION(0x4, "uart3"), /* RX */
+ SUNXI_FUNCTION(0x5, "jtag")), /* CK */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 25),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "i2c0"), /* SCK */
+ SUNXI_FUNCTION(0x3, "ts3"), /* DVLD */
+ SUNXI_FUNCTION(0x4, "uart3"), /* RTS */
+ SUNXI_FUNCTION(0x5, "jtag")), /* DO */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 26),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "i2c0"), /* SDA */
+ SUNXI_FUNCTION(0x3, "ts3"), /* D0 */
+ SUNXI_FUNCTION(0x4, "uart3"), /* CTS */
+ SUNXI_FUNCTION(0x5, "jtag")), /* DI */
+ /* Hole */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 0),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc0"), /* D1 */
+ SUNXI_FUNCTION(0x3, "jtag"), /* MS */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 0)), /* PF_EINT0 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 1),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc0"), /* D0 */
+ SUNXI_FUNCTION(0x3, "jtag"), /* DI */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 1)), /* PF_EINT1 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 2),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc0"), /* CLK */
+ SUNXI_FUNCTION(0x3, "uart0"), /* TX */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 2)), /* PF_EINT2 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 3),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc0"), /* CMD */
+ SUNXI_FUNCTION(0x3, "jtag"), /* DO */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 3)), /* PF_EINT3 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 4),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc0"), /* D3 */
+ SUNXI_FUNCTION(0x3, "uart0"), /* RX */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 4)), /* PF_EINT4 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 5),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc0"), /* D2 */
+ SUNXI_FUNCTION(0x3, "jtag"), /* CK */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 5)), /* PF_EINT5 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 6),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 6)), /* PF_EINT6 */
+ /* Hole */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 0),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc1"), /* CLK */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 0)), /* PG_EINT0 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 1),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc1"), /* CMD */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 1)), /* PG_EINT1 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 2),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc1"), /* D0 */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 2)), /* PG_EINT2 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 3),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc1"), /* D1 */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 3)), /* PG_EINT3 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 4),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc1"), /* D2 */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 4)), /* PG_EINT4 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 5),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc1"), /* D3 */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 5)), /* PG_EINT5 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 6),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "uart1"), /* TX */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 6)), /* PG_EINT6 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 7),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "uart1"), /* RX */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 7)), /* PG_EINT7 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 8),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "uart1"), /* RTS */
+ SUNXI_FUNCTION(0x4, "sim0"), /* VPPEN */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 8)), /* PG_EINT8 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 9),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "uart1"), /* CTS */
+ SUNXI_FUNCTION(0x4, "sim0"), /* VPPPP */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 9)), /* PG_EINT9 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 10),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "i2s2"), /* SYNC */
+ SUNXI_FUNCTION(0x3, "h_i2s2"), /* SYNC */
+ SUNXI_FUNCTION(0x4, "sim0"), /* PWREN */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 10)), /* PG_EINT10 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 11),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "i2s2"), /* CLK */
+ SUNXI_FUNCTION(0x3, "h_i2s2"), /* CLK */
+ SUNXI_FUNCTION(0x4, "sim0"), /* CLK */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 11)), /* PG_EINT11 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 12),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "i2s2"), /* DOUT */
+ SUNXI_FUNCTION(0x3, "h_i2s2"), /* DOUT */
+ SUNXI_FUNCTION(0x4, "sim0"), /* DATA */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 12)), /* PG_EINT12 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 13),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "i2s2"), /* DIN */
+ SUNXI_FUNCTION(0x3, "h_i2s2"), /* DIN */
+ SUNXI_FUNCTION(0x4, "sim0"), /* RST */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 13)), /* PG_EINT13 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 14),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "i2s2"), /* MCLK */
+ SUNXI_FUNCTION(0x3, "h_i2s2"), /* MCLK */
+ SUNXI_FUNCTION(0x4, "sim0"), /* DET */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 14)), /* PG_EINT14 */
+ /* Hole */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 0),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "uart0"), /* TX */
+ SUNXI_FUNCTION(0x3, "i2s0"), /* SYNC */
+ SUNXI_FUNCTION(0x4, "h_i2s0"), /* SYNC */
+ SUNXI_FUNCTION(0x5, "sim1"), /* VPPEN */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 3, 0)), /* PH_EINT0 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 1),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "uart0"), /* RX */
+ SUNXI_FUNCTION(0x3, "i2s0"), /* CLK */
+ SUNXI_FUNCTION(0x4, "h_i2s0"), /* CLK */
+ SUNXI_FUNCTION(0x5, "sim1"), /* VPPPP */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 3, 1)), /* PH_EINT1 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 2),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "ir_tx"),
+ SUNXI_FUNCTION(0x3, "i2s0"), /* DOUT */
+ SUNXI_FUNCTION(0x4, "h_i2s0"), /* DOUT */
+ SUNXI_FUNCTION(0x5, "sim1"), /* PWREN */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 3, 2)), /* PH_EINT2 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 3),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "spi1"), /* CS */
+ SUNXI_FUNCTION(0x3, "i2s0"), /* DIN */
+ SUNXI_FUNCTION(0x4, "h_i2s0"), /* DIN */
+ SUNXI_FUNCTION(0x5, "sim1"), /* CLK */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 3, 3)), /* PH_EINT3 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 4),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "spi1"), /* CLK */
+ SUNXI_FUNCTION(0x3, "i2s0"), /* MCLK */
+ SUNXI_FUNCTION(0x4, "h_i2s0"), /* MCLK */
+ SUNXI_FUNCTION(0x5, "sim1"), /* DATA */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 3, 4)), /* PH_EINT4 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 5),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "spi1"), /* MOSI */
+ SUNXI_FUNCTION(0x3, "spdif"), /* MCLK */
+ SUNXI_FUNCTION(0x4, "i2c1"), /* SCK */
+ SUNXI_FUNCTION(0x5, "sim1"), /* RST */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 3, 5)), /* PH_EINT5 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 6),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "spi1"), /* MISO */
+ SUNXI_FUNCTION(0x3, "spdif"), /* IN */
+ SUNXI_FUNCTION(0x4, "i2c1"), /* SDA */
+ SUNXI_FUNCTION(0x5, "sim1"), /* DET */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 3, 6)), /* PH_EINT6 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 7),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x3, "spdif"), /* OUT */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 3, 7)), /* PH_EINT7 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 8),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "hdmi"), /* HSCL */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 3, 8)), /* PH_EINT8 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 9),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "hdmi"), /* HSDA */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 3, 9)), /* PH_EINT9 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 10),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "hdmi"), /* HCEC */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 3, 10)), /* PH_EINT10 */
+};
+
+static const unsigned int h6_irq_bank_map[] = { 1, 5, 6, 7 };
+
+static const struct sunxi_pinctrl_desc h6_pinctrl_data = {
+ .pins = h6_pins,
+ .npins = ARRAY_SIZE(h6_pins),
+ .irq_banks = 3,
+ .irq_bank_map = h6_irq_bank_map,
+ .irq_read_needs_mux = true,
+};
+
+static int h6_pinctrl_probe(struct platform_device *pdev)
+{
+ return sunxi_pinctrl_init(pdev,
+ &h6_pinctrl_data);
+}
+
+static const struct of_device_id h6_pinctrl_match[] = {
+ { .compatible = "allwinner,sun50i-h6-pinctrl", },
+ {}
+};
+
+static struct platform_driver h6_pinctrl_driver = {
+ .probe = h6_pinctrl_probe,
+ .driver = {
+ .name = "sun50i-h6-pinctrl",
+ .of_match_table = h6_pinctrl_match,
+ },
+};
+builtin_platform_driver(h6_pinctrl_driver);
diff --git a/drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c b/drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c
index da387211a75e..f043afa1aac5 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c
+++ b/drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c
@@ -481,11 +481,13 @@ static const struct sunxi_desc_pin sun8i_a33_pins[] = {
SUNXI_FUNCTION(0x3, "uart3")), /* CTS */
};
+static const unsigned int sun8i_a33_pinctrl_irq_bank_map[] = { 1, 2 };
+
static const struct sunxi_pinctrl_desc sun8i_a33_pinctrl_data = {
.pins = sun8i_a33_pins,
.npins = ARRAY_SIZE(sun8i_a33_pins),
.irq_banks = 2,
- .irq_bank_base = 1,
+ .irq_bank_map = sun8i_a33_pinctrl_irq_bank_map,
.disable_strict_mode = true,
};
diff --git a/drivers/pinctrl/sunxi/pinctrl-sun8i-v3s.c b/drivers/pinctrl/sunxi/pinctrl-sun8i-v3s.c
index 496ba34e1f5f..6704ce8e5e3d 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sun8i-v3s.c
+++ b/drivers/pinctrl/sunxi/pinctrl-sun8i-v3s.c
@@ -293,11 +293,13 @@ static const struct sunxi_desc_pin sun8i_v3s_pins[] = {
SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 5)), /* PG_EINT5 */
};
+static const unsigned int sun8i_v3s_pinctrl_irq_bank_map[] = { 1, 2 };
+
static const struct sunxi_pinctrl_desc sun8i_v3s_pinctrl_data = {
.pins = sun8i_v3s_pins,
.npins = ARRAY_SIZE(sun8i_v3s_pins),
.irq_banks = 2,
- .irq_bank_base = 1,
+ .irq_bank_map = sun8i_v3s_pinctrl_irq_bank_map,
.irq_read_needs_mux = true
};
diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
index 341312d66512..020d6d84639c 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
@@ -835,7 +835,7 @@ static void sunxi_pinctrl_irq_release_resources(struct irq_data *d)
static int sunxi_pinctrl_irq_set_type(struct irq_data *d, unsigned int type)
{
struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d);
- u32 reg = sunxi_irq_cfg_reg(d->hwirq, pctl->desc->irq_bank_base);
+ u32 reg = sunxi_irq_cfg_reg(pctl->desc, d->hwirq);
u8 index = sunxi_irq_cfg_offset(d->hwirq);
unsigned long flags;
u32 regval;
@@ -882,8 +882,7 @@ static int sunxi_pinctrl_irq_set_type(struct irq_data *d, unsigned int type)
static void sunxi_pinctrl_irq_ack(struct irq_data *d)
{
struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d);
- u32 status_reg = sunxi_irq_status_reg(d->hwirq,
- pctl->desc->irq_bank_base);
+ u32 status_reg = sunxi_irq_status_reg(pctl->desc, d->hwirq);
u8 status_idx = sunxi_irq_status_offset(d->hwirq);
/* Clear the IRQ */
@@ -893,7 +892,7 @@ static void sunxi_pinctrl_irq_ack(struct irq_data *d)
static void sunxi_pinctrl_irq_mask(struct irq_data *d)
{
struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d);
- u32 reg = sunxi_irq_ctrl_reg(d->hwirq, pctl->desc->irq_bank_base);
+ u32 reg = sunxi_irq_ctrl_reg(pctl->desc, d->hwirq);
u8 idx = sunxi_irq_ctrl_offset(d->hwirq);
unsigned long flags;
u32 val;
@@ -910,7 +909,7 @@ static void sunxi_pinctrl_irq_mask(struct irq_data *d)
static void sunxi_pinctrl_irq_unmask(struct irq_data *d)
{
struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d);
- u32 reg = sunxi_irq_ctrl_reg(d->hwirq, pctl->desc->irq_bank_base);
+ u32 reg = sunxi_irq_ctrl_reg(pctl->desc, d->hwirq);
u8 idx = sunxi_irq_ctrl_offset(d->hwirq);
unsigned long flags;
u32 val;
@@ -1002,7 +1001,7 @@ static void sunxi_pinctrl_irq_handler(struct irq_desc *desc)
if (bank == pctl->desc->irq_banks)
return;
- reg = sunxi_irq_status_reg_from_bank(bank, pctl->desc->irq_bank_base);
+ reg = sunxi_irq_status_reg_from_bank(pctl->desc, bank);
val = readl(pctl->membase + reg);
if (val) {
@@ -1234,8 +1233,7 @@ static int sunxi_pinctrl_setup_debounce(struct sunxi_pinctrl *pctl,
writel(src | div << 4,
pctl->membase +
- sunxi_irq_debounce_reg_from_bank(i,
- pctl->desc->irq_bank_base));
+ sunxi_irq_debounce_reg_from_bank(pctl->desc, i));
}
return 0;
@@ -1363,7 +1361,8 @@ int sunxi_pinctrl_init_with_variant(struct platform_device *pdev,
goto gpiochip_error;
}
- clk = devm_clk_get(&pdev->dev, NULL);
+ ret = of_count_phandle_with_args(node, "clocks", "#clock-cells");
+ clk = devm_clk_get(&pdev->dev, ret == 1 ? NULL : "apb");
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
goto gpiochip_error;
@@ -1410,11 +1409,11 @@ int sunxi_pinctrl_init_with_variant(struct platform_device *pdev,
for (i = 0; i < pctl->desc->irq_banks; i++) {
/* Mask and clear all IRQs before registering a handler */
- writel(0, pctl->membase + sunxi_irq_ctrl_reg_from_bank(i,
- pctl->desc->irq_bank_base));
+ writel(0, pctl->membase +
+ sunxi_irq_ctrl_reg_from_bank(pctl->desc, i));
writel(0xffffffff,
- pctl->membase + sunxi_irq_status_reg_from_bank(i,
- pctl->desc->irq_bank_base));
+ pctl->membase +
+ sunxi_irq_status_reg_from_bank(pctl->desc, i));
irq_set_chained_handler_and_data(pctl->irq[i],
sunxi_pinctrl_irq_handler,
diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.h b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
index 11b128f54ed2..4a892e7dde66 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.h
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
@@ -110,7 +110,7 @@ struct sunxi_pinctrl_desc {
int npins;
unsigned pin_base;
unsigned irq_banks;
- unsigned irq_bank_base;
+ const unsigned int *irq_bank_map;
bool irq_read_needs_mux;
bool disable_strict_mode;
};
@@ -263,12 +263,22 @@ static inline u32 sunxi_pull_offset(u16 pin)
return pin_num * PULL_PINS_BITS;
}
-static inline u32 sunxi_irq_cfg_reg(u16 irq, unsigned bank_base)
+static inline u32 sunxi_irq_hw_bank_num(const struct sunxi_pinctrl_desc *desc, u8 bank)
+{
+ if (!desc->irq_bank_map)
+ return bank;
+ else
+ return desc->irq_bank_map[bank];
+}
+
+static inline u32 sunxi_irq_cfg_reg(const struct sunxi_pinctrl_desc *desc,
+ u16 irq)
{
u8 bank = irq / IRQ_PER_BANK;
u8 reg = (irq % IRQ_PER_BANK) / IRQ_CFG_IRQ_PER_REG * 0x04;
- return IRQ_CFG_REG + (bank_base + bank) * IRQ_MEM_SIZE + reg;
+ return IRQ_CFG_REG +
+ sunxi_irq_hw_bank_num(desc, bank) * IRQ_MEM_SIZE + reg;
}
static inline u32 sunxi_irq_cfg_offset(u16 irq)
@@ -277,16 +287,17 @@ static inline u32 sunxi_irq_cfg_offset(u16 irq)
return irq_num * IRQ_CFG_IRQ_BITS;
}
-static inline u32 sunxi_irq_ctrl_reg_from_bank(u8 bank, unsigned bank_base)
+static inline u32 sunxi_irq_ctrl_reg_from_bank(const struct sunxi_pinctrl_desc *desc, u8 bank)
{
- return IRQ_CTRL_REG + (bank_base + bank) * IRQ_MEM_SIZE;
+ return IRQ_CTRL_REG + sunxi_irq_hw_bank_num(desc, bank) * IRQ_MEM_SIZE;
}
-static inline u32 sunxi_irq_ctrl_reg(u16 irq, unsigned bank_base)
+static inline u32 sunxi_irq_ctrl_reg(const struct sunxi_pinctrl_desc *desc,
+ u16 irq)
{
u8 bank = irq / IRQ_PER_BANK;
- return sunxi_irq_ctrl_reg_from_bank(bank, bank_base);
+ return sunxi_irq_ctrl_reg_from_bank(desc, bank);
}
static inline u32 sunxi_irq_ctrl_offset(u16 irq)
@@ -295,21 +306,24 @@ static inline u32 sunxi_irq_ctrl_offset(u16 irq)
return irq_num * IRQ_CTRL_IRQ_BITS;
}
-static inline u32 sunxi_irq_debounce_reg_from_bank(u8 bank, unsigned bank_base)
+static inline u32 sunxi_irq_debounce_reg_from_bank(const struct sunxi_pinctrl_desc *desc, u8 bank)
{
- return IRQ_DEBOUNCE_REG + (bank_base + bank) * IRQ_MEM_SIZE;
+ return IRQ_DEBOUNCE_REG +
+ sunxi_irq_hw_bank_num(desc, bank) * IRQ_MEM_SIZE;
}
-static inline u32 sunxi_irq_status_reg_from_bank(u8 bank, unsigned bank_base)
+static inline u32 sunxi_irq_status_reg_from_bank(const struct sunxi_pinctrl_desc *desc, u8 bank)
{
- return IRQ_STATUS_REG + (bank_base + bank) * IRQ_MEM_SIZE;
+ return IRQ_STATUS_REG +
+ sunxi_irq_hw_bank_num(desc, bank) * IRQ_MEM_SIZE;
}
-static inline u32 sunxi_irq_status_reg(u16 irq, unsigned bank_base)
+static inline u32 sunxi_irq_status_reg(const struct sunxi_pinctrl_desc *desc,
+ u16 irq)
{
u8 bank = irq / IRQ_PER_BANK;
- return sunxi_irq_status_reg_from_bank(bank, bank_base);
+ return sunxi_irq_status_reg_from_bank(desc, bank);
}
static inline u32 sunxi_irq_status_offset(u16 irq)
diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-ld11.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-ld11.c
index 8a5ecd6277d8..0976fbfecd50 100644
--- a/drivers/pinctrl/uniphier/pinctrl-uniphier-ld11.c
+++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-ld11.c
@@ -470,8 +470,10 @@ static const struct pinctrl_pin_desc uniphier_ld11_pins[] = {
166, UNIPHIER_PIN_PULL_DOWN),
};
-static const unsigned aout_pins[] = {135, 136, 137, 138, 139, 140, 141, 142};
-static const int aout_muxvals[] = {0, 0, 0, 0, 0, 0, 0, 0};
+static const unsigned aout1_pins[] = {137, 138, 139, 140, 141, 142};
+static const int aout1_muxvals[] = {0, 0, 0, 0, 0, 0};
+static const unsigned aoutiec1_pins[] = {135, 136};
+static const int aoutiec1_muxvals[] = {0, 0};
static const unsigned int emmc_pins[] = {19, 20, 21, 22, 23, 24, 25};
static const int emmc_muxvals[] = {0, 0, 0, 0, 0, 0, 0};
static const unsigned emmc_dat8_pins[] = {26, 27, 28, 29};
@@ -504,6 +506,8 @@ static const unsigned uart2_pins[] = {90, 91};
static const int uart2_muxvals[] = {1, 1};
static const unsigned uart3_pins[] = {94, 95};
static const int uart3_muxvals[] = {1, 1};
+static const unsigned uart3_ctsrts_pins[] = {96, 97, 98, 99, 100, 101};
+static const int uart3_ctsrts_muxvals[] = {1, 1, 1, 1, 1, 1};
static const unsigned usb0_pins[] = {46, 47};
static const int usb0_muxvals[] = {0, 0};
static const unsigned usb1_pins[] = {48, 49};
@@ -547,7 +551,8 @@ static const unsigned int gpio_range5_pins[] = {
};
static const struct uniphier_pinctrl_group uniphier_ld11_groups[] = {
- UNIPHIER_PINCTRL_GROUP(aout),
+ UNIPHIER_PINCTRL_GROUP(aout1),
+ UNIPHIER_PINCTRL_GROUP(aoutiec1),
UNIPHIER_PINCTRL_GROUP(emmc),
UNIPHIER_PINCTRL_GROUP(emmc_dat8),
UNIPHIER_PINCTRL_GROUP(ether_rmii),
@@ -562,6 +567,7 @@ static const struct uniphier_pinctrl_group uniphier_ld11_groups[] = {
UNIPHIER_PINCTRL_GROUP(uart1),
UNIPHIER_PINCTRL_GROUP(uart2),
UNIPHIER_PINCTRL_GROUP(uart3),
+ UNIPHIER_PINCTRL_GROUP(uart3_ctsrts),
UNIPHIER_PINCTRL_GROUP(usb0),
UNIPHIER_PINCTRL_GROUP(usb1),
UNIPHIER_PINCTRL_GROUP(usb2),
@@ -573,7 +579,8 @@ static const struct uniphier_pinctrl_group uniphier_ld11_groups[] = {
UNIPHIER_PINCTRL_GROUP_GPIO(gpio_range5),
};
-static const char * const aout_groups[] = {"aout"};
+static const char * const aout1_groups[] = {"aout1"};
+static const char * const aoutiec1_groups[] = {"aoutiec1"};
static const char * const emmc_groups[] = {"emmc", "emmc_dat8"};
static const char * const ether_rmii_groups[] = {"ether_rmii"};
static const char * const i2c0_groups[] = {"i2c0"};
@@ -586,13 +593,14 @@ static const char * const system_bus_groups[] = {"system_bus",
static const char * const uart0_groups[] = {"uart0"};
static const char * const uart1_groups[] = {"uart1"};
static const char * const uart2_groups[] = {"uart2"};
-static const char * const uart3_groups[] = {"uart3"};
+static const char * const uart3_groups[] = {"uart3", "uart3_ctsrts"};
static const char * const usb0_groups[] = {"usb0"};
static const char * const usb1_groups[] = {"usb1"};
static const char * const usb2_groups[] = {"usb2"};
static const struct uniphier_pinmux_function uniphier_ld11_functions[] = {
- UNIPHIER_PINMUX_FUNCTION(aout),
+ UNIPHIER_PINMUX_FUNCTION(aout1),
+ UNIPHIER_PINMUX_FUNCTION(aoutiec1),
UNIPHIER_PINMUX_FUNCTION(emmc),
UNIPHIER_PINMUX_FUNCTION(ether_rmii),
UNIPHIER_PINMUX_FUNCTION(i2c0),
diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-ld20.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-ld20.c
index 3be7967edae0..bf8f0c3bea5e 100644
--- a/drivers/pinctrl/uniphier/pinctrl-uniphier-ld20.c
+++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-ld20.c
@@ -551,8 +551,10 @@ static const struct pinctrl_pin_desc uniphier_ld20_pins[] = {
175, UNIPHIER_PIN_PULL_DOWN),
};
-static const unsigned aout_pins[] = {135, 136, 137, 138, 139, 140, 141, 142};
-static const int aout_muxvals[] = {0, 0, 0, 0, 0, 0, 0, 0};
+static const unsigned aout1_pins[] = {137, 138, 139, 140, 141, 142};
+static const int aout1_muxvals[] = {0, 0, 0, 0, 0, 0};
+static const unsigned aoutiec1_pins[] = {135, 136};
+static const int aoutiec1_muxvals[] = {0, 0};
static const unsigned int emmc_pins[] = {19, 20, 21, 22, 23, 24, 25};
static const int emmc_muxvals[] = {0, 0, 0, 0, 0, 0, 0};
static const unsigned emmc_dat8_pins[] = {26, 27, 28, 29};
@@ -591,6 +593,8 @@ static const unsigned uart2_pins[] = {90, 91};
static const int uart2_muxvals[] = {1, 1};
static const unsigned uart3_pins[] = {94, 95};
static const int uart3_muxvals[] = {1, 1};
+static const unsigned uart3_ctsrts_pins[] = {96, 97, 98, 99, 100, 101};
+static const int uart3_ctsrts_muxvals[] = {1, 1, 1, 1, 1, 1};
static const unsigned usb0_pins[] = {46, 47};
static const int usb0_muxvals[] = {0, 0};
static const unsigned usb1_pins[] = {48, 49};
@@ -631,7 +635,8 @@ static const unsigned int gpio_range2_pins[] = {
};
static const struct uniphier_pinctrl_group uniphier_ld20_groups[] = {
- UNIPHIER_PINCTRL_GROUP(aout),
+ UNIPHIER_PINCTRL_GROUP(aout1),
+ UNIPHIER_PINCTRL_GROUP(aoutiec1),
UNIPHIER_PINCTRL_GROUP(emmc),
UNIPHIER_PINCTRL_GROUP(emmc_dat8),
UNIPHIER_PINCTRL_GROUP(ether_rgmii),
@@ -648,6 +653,7 @@ static const struct uniphier_pinctrl_group uniphier_ld20_groups[] = {
UNIPHIER_PINCTRL_GROUP(uart1),
UNIPHIER_PINCTRL_GROUP(uart2),
UNIPHIER_PINCTRL_GROUP(uart3),
+ UNIPHIER_PINCTRL_GROUP(uart3_ctsrts),
UNIPHIER_PINCTRL_GROUP(usb0),
UNIPHIER_PINCTRL_GROUP(usb1),
UNIPHIER_PINCTRL_GROUP(usb2),
@@ -657,7 +663,8 @@ static const struct uniphier_pinctrl_group uniphier_ld20_groups[] = {
UNIPHIER_PINCTRL_GROUP_GPIO(gpio_range2),
};
-static const char * const aout_groups[] = {"aout"};
+static const char * const aout1_groups[] = {"aout1"};
+static const char * const aoutiec1_groups[] = {"aoutiec1"};
static const char * const emmc_groups[] = {"emmc", "emmc_dat8"};
static const char * const ether_rgmii_groups[] = {"ether_rgmii"};
static const char * const ether_rmii_groups[] = {"ether_rmii"};
@@ -672,14 +679,15 @@ static const char * const system_bus_groups[] = {"system_bus",
static const char * const uart0_groups[] = {"uart0"};
static const char * const uart1_groups[] = {"uart1"};
static const char * const uart2_groups[] = {"uart2"};
-static const char * const uart3_groups[] = {"uart3"};
+static const char * const uart3_groups[] = {"uart3", "uart3_ctsrts"};
static const char * const usb0_groups[] = {"usb0"};
static const char * const usb1_groups[] = {"usb1"};
static const char * const usb2_groups[] = {"usb2"};
static const char * const usb3_groups[] = {"usb3"};
static const struct uniphier_pinmux_function uniphier_ld20_functions[] = {
- UNIPHIER_PINMUX_FUNCTION(aout),
+ UNIPHIER_PINMUX_FUNCTION(aout1),
+ UNIPHIER_PINMUX_FUNCTION(aoutiec1),
UNIPHIER_PINMUX_FUNCTION(emmc),
UNIPHIER_PINMUX_FUNCTION(ether_rgmii),
UNIPHIER_PINMUX_FUNCTION(ether_rmii),
diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-ld4.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-ld4.c
index 840382847212..0b10ebc07eb8 100644
--- a/drivers/pinctrl/uniphier/pinctrl-uniphier-ld4.c
+++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-ld4.c
@@ -590,6 +590,8 @@ static const unsigned system_bus_cs3_pins[] = {156};
static const int system_bus_cs3_muxvals[] = {1};
static const unsigned uart0_pins[] = {85, 88};
static const int uart0_muxvals[] = {1, 1};
+static const unsigned uart0_ctsrts_pins[] = {86, 87, 89};
+static const int uart0_ctsrts_muxvals[] = {1, 1, 1};
static const unsigned uart1_pins[] = {155, 156};
static const int uart1_muxvals[] = {13, 13};
static const unsigned uart1b_pins[] = {69, 70};
@@ -644,6 +646,7 @@ static const struct uniphier_pinctrl_group uniphier_ld4_groups[] = {
UNIPHIER_PINCTRL_GROUP(system_bus_cs2),
UNIPHIER_PINCTRL_GROUP(system_bus_cs3),
UNIPHIER_PINCTRL_GROUP(uart0),
+ UNIPHIER_PINCTRL_GROUP(uart0_ctsrts),
UNIPHIER_PINCTRL_GROUP(uart1),
UNIPHIER_PINCTRL_GROUP(uart1b),
UNIPHIER_PINCTRL_GROUP(uart2),
@@ -669,7 +672,7 @@ static const char * const system_bus_groups[] = {"system_bus",
"system_bus_cs1",
"system_bus_cs2",
"system_bus_cs3"};
-static const char * const uart0_groups[] = {"uart0"};
+static const char * const uart0_groups[] = {"uart0", "uart0_ctsrts"};
static const char * const uart1_groups[] = {"uart1", "uart1b"};
static const char * const uart2_groups[] = {"uart2"};
static const char * const uart3_groups[] = {"uart3"};
diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-ld6b.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-ld6b.c
index 493a90c6d733..8e4d45fea885 100644
--- a/drivers/pinctrl/uniphier/pinctrl-uniphier-ld6b.c
+++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-ld6b.c
@@ -785,6 +785,8 @@ static const unsigned system_bus_cs5_pins[] = {55};
static const int system_bus_cs5_muxvals[] = {6};
static const unsigned uart0_pins[] = {135, 136};
static const int uart0_muxvals[] = {3, 3};
+static const unsigned uart0_ctsrts_pins[] = {137, 138, 139, 140, 141, 124};
+static const int uart0_ctsrts_muxvals[] = {3, 3, 3, 3, 3, 3};
static const unsigned uart0b_pins[] = {11, 12};
static const int uart0b_muxvals[] = {2, 2};
static const unsigned uart1_pins[] = {115, 116};
@@ -856,6 +858,7 @@ static const struct uniphier_pinctrl_group uniphier_ld6b_groups[] = {
UNIPHIER_PINCTRL_GROUP(system_bus_cs4),
UNIPHIER_PINCTRL_GROUP(system_bus_cs5),
UNIPHIER_PINCTRL_GROUP(uart0),
+ UNIPHIER_PINCTRL_GROUP(uart0_ctsrts),
UNIPHIER_PINCTRL_GROUP(uart0b),
UNIPHIER_PINCTRL_GROUP(uart1),
UNIPHIER_PINCTRL_GROUP(uart1b),
@@ -885,7 +888,7 @@ static const char * const system_bus_groups[] = {"system_bus",
"system_bus_cs3",
"system_bus_cs4",
"system_bus_cs5"};
-static const char * const uart0_groups[] = {"uart0", "uart0b"};
+static const char * const uart0_groups[] = {"uart0", "uart0_ctsrts", "uart0b"};
static const char * const uart1_groups[] = {"uart1", "uart1b"};
static const char * const uart2_groups[] = {"uart2", "uart2b"};
static const char * const usb0_groups[] = {"usb0"};
diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-pro4.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-pro4.c
index 603204a00213..24788a74c254 100644
--- a/drivers/pinctrl/uniphier/pinctrl-uniphier-pro4.c
+++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-pro4.c
@@ -1078,6 +1078,8 @@ static const unsigned uart2_pins[] = {131, 132};
static const int uart2_muxvals[] = {0, 0};
static const unsigned uart3_pins[] = {88, 89};
static const int uart3_muxvals[] = {2, 2};
+static const unsigned uart3_ctsrts_pins[] = {80, 81, 82, 83, 90, 91};
+static const int uart3_ctsrts_muxvals[] = {2, 2, 2, 2, 2, 2};
static const unsigned usb0_pins[] = {180, 181};
static const int usb0_muxvals[] = {0, 0};
static const unsigned usb1_pins[] = {182, 183};
@@ -1149,6 +1151,7 @@ static const struct uniphier_pinctrl_group uniphier_pro4_groups[] = {
UNIPHIER_PINCTRL_GROUP(uart1),
UNIPHIER_PINCTRL_GROUP(uart2),
UNIPHIER_PINCTRL_GROUP(uart3),
+ UNIPHIER_PINCTRL_GROUP(uart3_ctsrts),
UNIPHIER_PINCTRL_GROUP(usb0),
UNIPHIER_PINCTRL_GROUP(usb1),
UNIPHIER_PINCTRL_GROUP(usb2),
@@ -1180,7 +1183,7 @@ static const char * const system_bus_groups[] = {"system_bus",
static const char * const uart0_groups[] = {"uart0"};
static const char * const uart1_groups[] = {"uart1"};
static const char * const uart2_groups[] = {"uart2"};
-static const char * const uart3_groups[] = {"uart3"};
+static const char * const uart3_groups[] = {"uart3", "uart3_ctsrts"};
static const char * const usb0_groups[] = {"usb0"};
static const char * const usb1_groups[] = {"usb1"};
static const char * const usb2_groups[] = {"usb2"};
diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-pro5.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-pro5.c
index 9381a4ff4389..d5d5e579cb08 100644
--- a/drivers/pinctrl/uniphier/pinctrl-uniphier-pro5.c
+++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-pro5.c
@@ -842,6 +842,8 @@ static const unsigned uart0_pins[] = {47, 48};
static const int uart0_muxvals[] = {0, 0};
static const unsigned uart0b_pins[] = {227, 228};
static const int uart0b_muxvals[] = {3, 3};
+static const unsigned uart0b_ctsrts_pins[] = {229, 230, 231, 232, 233, 234};
+static const int uart0b_ctsrts_muxvals[] = {3, 3, 3, 3, 3, 3};
static const unsigned uart1_pins[] = {49, 50};
static const int uart1_muxvals[] = {0, 0};
static const unsigned uart2_pins[] = {51, 52};
@@ -913,6 +915,7 @@ static const struct uniphier_pinctrl_group uniphier_pro5_groups[] = {
UNIPHIER_PINCTRL_GROUP(system_bus_cs7),
UNIPHIER_PINCTRL_GROUP(uart0),
UNIPHIER_PINCTRL_GROUP(uart0b),
+ UNIPHIER_PINCTRL_GROUP(uart0b_ctsrts),
UNIPHIER_PINCTRL_GROUP(uart1),
UNIPHIER_PINCTRL_GROUP(uart2),
UNIPHIER_PINCTRL_GROUP(uart3),
@@ -940,7 +943,7 @@ static const char * const system_bus_groups[] = {"system_bus",
"system_bus_cs5",
"system_bus_cs6",
"system_bus_cs7"};
-static const char * const uart0_groups[] = {"uart0", "uart0b"};
+static const char * const uart0_groups[] = {"uart0", "uart0b", "uart0b_ctsrts"};
static const char * const uart1_groups[] = {"uart1"};
static const char * const uart2_groups[] = {"uart2"};
static const char * const uart3_groups[] = {"uart3"};
diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-pxs2.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-pxs2.c
index c0ef40ae99a7..032619ad0e73 100644
--- a/drivers/pinctrl/uniphier/pinctrl-uniphier-pxs2.c
+++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-pxs2.c
@@ -728,6 +728,20 @@ static const struct pinctrl_pin_desc uniphier_pxs2_pins[] = {
234, UNIPHIER_PIN_PULL_DOWN),
};
+static const unsigned ain1_pins[] = {161, 162, 173, 174};
+static const int ain1_muxvals[] = {8, 8, 8, 8};
+static const unsigned ain2_pins[] = {98, 99, 100, 101, 102, 103, 104};
+static const int ain2_muxvals[] = {8, 8, 8, 8, 8, 8, 8};
+static const unsigned ainiec1_pins[] = {91};
+static const int ainiec1_muxvals[] = {11};
+static const unsigned aout2_pins[] = {175, 176, 177, 178, 183, 184, 185};
+static const int aout2_muxvals[] = {8, 8, 8, 8, 9, 9, 9};
+static const unsigned aout3_pins[] = {105, 106, 107, 108};
+static const int aout3_muxvals[] = {8, 8, 8, 8};
+static const unsigned aoutiec1_pins[] = {95};
+static const int aoutiec1_muxvals[] = {11};
+static const unsigned aoutiec2_pins[] = {97};
+static const int aoutiec2_muxvals[] = {8};
static const unsigned emmc_pins[] = {36, 37, 38, 39, 40, 41, 42};
static const int emmc_muxvals[] = {9, 9, 9, 9, 9, 9, 9};
static const unsigned emmc_dat8_pins[] = {43, 44, 45, 46};
@@ -774,6 +788,8 @@ static const unsigned uart0_pins[] = {217, 218};
static const int uart0_muxvals[] = {8, 8};
static const unsigned uart0b_pins[] = {179, 180};
static const int uart0b_muxvals[] = {10, 10};
+static const unsigned uart0b_ctsrts_pins[] = {176, 177, 178, 183, 184, 185};
+static const int uart0b_ctsrts_muxvals[] = {10, 10, 10, 10, 10, 10};
static const unsigned uart1_pins[] = {115, 116};
static const int uart1_muxvals[] = {8, 8};
static const unsigned uart2_pins[] = {113, 114};
@@ -824,6 +840,13 @@ static const unsigned int gpio_range1_pins[] = {
};
static const struct uniphier_pinctrl_group uniphier_pxs2_groups[] = {
+ UNIPHIER_PINCTRL_GROUP(ain1),
+ UNIPHIER_PINCTRL_GROUP(ain2),
+ UNIPHIER_PINCTRL_GROUP(ainiec1),
+ UNIPHIER_PINCTRL_GROUP(aout2),
+ UNIPHIER_PINCTRL_GROUP(aout3),
+ UNIPHIER_PINCTRL_GROUP(aoutiec1),
+ UNIPHIER_PINCTRL_GROUP(aoutiec2),
UNIPHIER_PINCTRL_GROUP(emmc),
UNIPHIER_PINCTRL_GROUP(emmc_dat8),
UNIPHIER_PINCTRL_GROUP(ether_mii),
@@ -842,6 +865,7 @@ static const struct uniphier_pinctrl_group uniphier_pxs2_groups[] = {
UNIPHIER_PINCTRL_GROUP(system_bus_cs1),
UNIPHIER_PINCTRL_GROUP(uart0),
UNIPHIER_PINCTRL_GROUP(uart0b),
+ UNIPHIER_PINCTRL_GROUP(uart0b_ctsrts),
UNIPHIER_PINCTRL_GROUP(uart1),
UNIPHIER_PINCTRL_GROUP(uart2),
UNIPHIER_PINCTRL_GROUP(uart3),
@@ -854,6 +878,13 @@ static const struct uniphier_pinctrl_group uniphier_pxs2_groups[] = {
UNIPHIER_PINCTRL_GROUP_GPIO(gpio_range1),
};
+static const char * const ain1_groups[] = {"ain1"};
+static const char * const ain2_groups[] = {"ain2"};
+static const char * const ainiec1_groups[] = {"ainiec1"};
+static const char * const aout2_groups[] = {"aout2"};
+static const char * const aout3_groups[] = {"aout3"};
+static const char * const aoutiec1_groups[] = {"aoutiec1"};
+static const char * const aoutiec2_groups[] = {"aoutiec2"};
static const char * const emmc_groups[] = {"emmc", "emmc_dat8"};
static const char * const ether_mii_groups[] = {"ether_mii"};
static const char * const ether_rgmii_groups[] = {"ether_rgmii"};
@@ -868,7 +899,7 @@ static const char * const nand_groups[] = {"nand", "nand_cs1"};
static const char * const sd_groups[] = {"sd"};
static const char * const system_bus_groups[] = {"system_bus",
"system_bus_cs1"};
-static const char * const uart0_groups[] = {"uart0", "uart0b"};
+static const char * const uart0_groups[] = {"uart0", "uart0b", "uart0b_ctsrts"};
static const char * const uart1_groups[] = {"uart1"};
static const char * const uart2_groups[] = {"uart2"};
static const char * const uart3_groups[] = {"uart3", "uart3b"};
@@ -878,6 +909,13 @@ static const char * const usb2_groups[] = {"usb2"};
static const char * const usb3_groups[] = {"usb3"};
static const struct uniphier_pinmux_function uniphier_pxs2_functions[] = {
+ UNIPHIER_PINMUX_FUNCTION(ain1),
+ UNIPHIER_PINMUX_FUNCTION(ain2),
+ UNIPHIER_PINMUX_FUNCTION(ainiec1),
+ UNIPHIER_PINMUX_FUNCTION(aout2),
+ UNIPHIER_PINMUX_FUNCTION(aout3),
+ UNIPHIER_PINMUX_FUNCTION(aoutiec1),
+ UNIPHIER_PINMUX_FUNCTION(aoutiec2),
UNIPHIER_PINMUX_FUNCTION(emmc),
UNIPHIER_PINMUX_FUNCTION(ether_mii),
UNIPHIER_PINMUX_FUNCTION(ether_rgmii),
diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-pxs3.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-pxs3.c
index dbe94a9a0353..535bb2e935e4 100644
--- a/drivers/pinctrl/uniphier/pinctrl-uniphier-pxs3.c
+++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-pxs3.c
@@ -816,6 +816,8 @@ static const unsigned int system_bus_cs1_pins[] = {15};
static const int system_bus_cs1_muxvals[] = {0};
static const unsigned int uart0_pins[] = {92, 93};
static const int uart0_muxvals[] = {0, 0};
+static const unsigned int uart0_ctsrts_pins[] = {243, 244, 245, 246, 247, 248};
+static const int uart0_ctsrts_muxvals[] = {3, 3, 3, 3, 3, 3};
static const unsigned int uart1_pins[] = {94, 95};
static const int uart1_muxvals[] = {0, 0};
static const unsigned int uart2_pins[] = {96, 97};
@@ -887,6 +889,7 @@ static const struct uniphier_pinctrl_group uniphier_pxs3_groups[] = {
UNIPHIER_PINCTRL_GROUP(system_bus),
UNIPHIER_PINCTRL_GROUP(system_bus_cs1),
UNIPHIER_PINCTRL_GROUP(uart0),
+ UNIPHIER_PINCTRL_GROUP(uart0_ctsrts),
UNIPHIER_PINCTRL_GROUP(uart1),
UNIPHIER_PINCTRL_GROUP(uart2),
UNIPHIER_PINCTRL_GROUP(uart3),
@@ -912,7 +915,7 @@ static const char * const nand_groups[] = {"nand"};
static const char * const sd_groups[] = {"sd"};
static const char * const system_bus_groups[] = {"system_bus",
"system_bus_cs1"};
-static const char * const uart0_groups[] = {"uart0"};
+static const char * const uart0_groups[] = {"uart0", "uart0_ctsrts"};
static const char * const uart1_groups[] = {"uart1"};
static const char * const uart2_groups[] = {"uart2"};
static const char * const uart3_groups[] = {"uart3"};
diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-sld8.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-sld8.c
index 1af430d701be..0f921a653164 100644
--- a/drivers/pinctrl/uniphier/pinctrl-uniphier-sld8.c
+++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-sld8.c
@@ -520,6 +520,8 @@ static const unsigned system_bus_cs5_pins[] = {13};
static const int system_bus_cs5_muxvals[] = {1};
static const unsigned uart0_pins[] = {70, 71};
static const int uart0_muxvals[] = {3, 3};
+static const unsigned uart0_ctsrts_pins[] = {72, 73, 74};
+static const int uart0_ctsrts_muxvals[] = {3, 3, 3};
static const unsigned uart1_pins[] = {114, 115};
static const int uart1_muxvals[] = {0, 0};
static const unsigned uart2_pins[] = {112, 113};
@@ -575,6 +577,7 @@ static const struct uniphier_pinctrl_group uniphier_sld8_groups[] = {
UNIPHIER_PINCTRL_GROUP(system_bus_cs4),
UNIPHIER_PINCTRL_GROUP(system_bus_cs5),
UNIPHIER_PINCTRL_GROUP(uart0),
+ UNIPHIER_PINCTRL_GROUP(uart0_ctsrts),
UNIPHIER_PINCTRL_GROUP(uart1),
UNIPHIER_PINCTRL_GROUP(uart2),
UNIPHIER_PINCTRL_GROUP(uart3),
@@ -601,7 +604,7 @@ static const char * const system_bus_groups[] = {"system_bus",
"system_bus_cs3",
"system_bus_cs4",
"system_bus_cs5"};
-static const char * const uart0_groups[] = {"uart0"};
+static const char * const uart0_groups[] = {"uart0", "uart0_ctsrts"};
static const char * const uart1_groups[] = {"uart1"};
static const char * const uart2_groups[] = {"uart2"};
static const char * const uart3_groups[] = {"uart3"};
diff --git a/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c
index d8599736a41a..5c47f451e43b 100644
--- a/drivers/platform/chrome/chromeos_laptop.c
+++ b/drivers/platform/chrome/chromeos_laptop.c
@@ -1,33 +1,20 @@
-/*
- * chromeos_laptop.c - Driver to instantiate Chromebook i2c/smbus devices.
- *
- * Author : Benson Leung <bleung@chromium.org>
- *
- * Copyright (C) 2012 Google, Inc.
- *
- * 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
- *
- */
+// SPDX-License-Identifier: GPL-2.0+
+// Driver to instantiate Chromebook i2c/smbus devices.
+//
+// Copyright (C) 2012 Google, Inc.
+// Author: Benson Leung <bleung@chromium.org>
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/dmi.h>
#include <linux/i2c.h>
-#include <linux/platform_data/atmel_mxt_ts.h>
#include <linux/input.h>
#include <linux/interrupt.h>
+#include <linux/ioport.h>
#include <linux/module.h>
+#include <linux/pci.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#define ATMEL_TP_I2C_ADDR 0x4b
#define ATMEL_TP_I2C_BL_ADDR 0x25
@@ -38,18 +25,11 @@
#define ISL_ALS_I2C_ADDR 0x44
#define TAOS_ALS_I2C_ADDR 0x29
-#define MAX_I2C_DEVICE_DEFERRALS 5
-
-static struct i2c_client *als;
-static struct i2c_client *tp;
-static struct i2c_client *ts;
-
static const char *i2c_adapter_names[] = {
"SMBus I801 adapter",
"i915 gmbus vga",
"i915 gmbus panel",
"Synopsys DesignWare I2C adapter",
- "Synopsys DesignWare I2C adapter",
};
/* Keep this enum consistent with i2c_adapter_names */
@@ -57,126 +37,41 @@ enum i2c_adapter_type {
I2C_ADAPTER_SMBUS = 0,
I2C_ADAPTER_VGADDC,
I2C_ADAPTER_PANEL,
- I2C_ADAPTER_DESIGNWARE_0,
- I2C_ADAPTER_DESIGNWARE_1,
-};
-
-enum i2c_peripheral_state {
- UNPROBED = 0,
- PROBED,
- TIMEDOUT,
+ I2C_ADAPTER_DESIGNWARE,
};
struct i2c_peripheral {
- int (*add)(enum i2c_adapter_type type);
- enum i2c_adapter_type type;
- enum i2c_peripheral_state state;
- int tries;
-};
-
-#define MAX_I2C_PERIPHERALS 4
+ struct i2c_board_info board_info;
+ unsigned short alt_addr;
-struct chromeos_laptop {
- struct i2c_peripheral i2c_peripherals[MAX_I2C_PERIPHERALS];
-};
-
-static struct chromeos_laptop *cros_laptop;
-
-static struct i2c_board_info cyapa_device = {
- I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
- .flags = I2C_CLIENT_WAKE,
-};
+ const char *dmi_name;
+ unsigned long irqflags;
+ struct resource irq_resource;
-static struct i2c_board_info elantech_device = {
- I2C_BOARD_INFO("elan_i2c", ELAN_TP_I2C_ADDR),
- .flags = I2C_CLIENT_WAKE,
-};
-
-static struct i2c_board_info isl_als_device = {
- I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
-};
-
-static struct i2c_board_info tsl2583_als_device = {
- I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR),
-};
-
-static struct i2c_board_info tsl2563_als_device = {
- I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR),
-};
-
-static int mxt_t19_keys[] = {
- KEY_RESERVED,
- KEY_RESERVED,
- KEY_RESERVED,
- KEY_RESERVED,
- KEY_RESERVED,
- BTN_LEFT
-};
-
-static struct mxt_platform_data atmel_224s_tp_platform_data = {
- .irqflags = IRQF_TRIGGER_FALLING,
- .t19_num_keys = ARRAY_SIZE(mxt_t19_keys),
- .t19_keymap = mxt_t19_keys,
- .suspend_mode = MXT_SUSPEND_T9_CTRL,
-};
+ enum i2c_adapter_type type;
+ u32 pci_devid;
-static struct i2c_board_info atmel_224s_tp_device = {
- I2C_BOARD_INFO("atmel_mxt_tp", ATMEL_TP_I2C_ADDR),
- .platform_data = &atmel_224s_tp_platform_data,
- .flags = I2C_CLIENT_WAKE,
+ struct i2c_client *client;
};
-static struct mxt_platform_data atmel_1664s_platform_data = {
- .irqflags = IRQF_TRIGGER_FALLING,
- .suspend_mode = MXT_SUSPEND_T9_CTRL,
+struct chromeos_laptop {
+ /*
+ * Note that we can't mark this pointer as const because
+ * i2c_new_probed_device() changes passed in I2C board info, so.
+ */
+ struct i2c_peripheral *i2c_peripherals;
+ unsigned int num_i2c_peripherals;
};
-static struct i2c_board_info atmel_1664s_device = {
- I2C_BOARD_INFO("atmel_mxt_ts", ATMEL_TS_I2C_ADDR),
- .platform_data = &atmel_1664s_platform_data,
- .flags = I2C_CLIENT_WAKE,
-};
+static const struct chromeos_laptop *cros_laptop;
-static struct i2c_client *__add_probed_i2c_device(
- const char *name,
- int bus,
- struct i2c_board_info *info,
- const unsigned short *alt_addr_list)
+static struct i2c_client *
+chromes_laptop_instantiate_i2c_device(struct i2c_adapter *adapter,
+ struct i2c_board_info *info,
+ unsigned short alt_addr)
{
- const struct dmi_device *dmi_dev;
- const struct dmi_dev_onboard *dev_data;
- struct i2c_adapter *adapter;
- struct i2c_client *client = NULL;
const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END };
-
- if (bus < 0)
- return NULL;
- /*
- * If a name is specified, look for irq platform information stashed
- * in DMI_DEV_TYPE_DEV_ONBOARD by the Chrome OS custom system firmware.
- */
- if (name) {
- dmi_dev = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, name, NULL);
- if (!dmi_dev) {
- pr_err("%s failed to dmi find device %s.\n",
- __func__,
- name);
- return NULL;
- }
- dev_data = (struct dmi_dev_onboard *)dmi_dev->device_data;
- if (!dev_data) {
- pr_err("%s failed to get data from dmi for %s.\n",
- __func__, name);
- return NULL;
- }
- info->irq = dev_data->instance;
- }
-
- adapter = i2c_get_adapter(bus);
- if (!adapter) {
- pr_err("%s failed to get i2c adapter %d.\n", __func__, bus);
- return NULL;
- }
+ struct i2c_client *client;
/*
* Add the i2c device. If we can't detect it at the primary
@@ -184,339 +79,345 @@ static struct i2c_client *__add_probed_i2c_device(
* structure gets assigned primary address.
*/
client = i2c_new_probed_device(adapter, info, addr_list, NULL);
- if (!client && alt_addr_list) {
+ if (!client && alt_addr) {
struct i2c_board_info dummy_info = {
I2C_BOARD_INFO("dummy", info->addr),
};
+ const unsigned short alt_addr_list[] = {
+ alt_addr, I2C_CLIENT_END
+ };
struct i2c_client *dummy;
dummy = i2c_new_probed_device(adapter, &dummy_info,
alt_addr_list, NULL);
if (dummy) {
- pr_debug("%s %d-%02x is probed at %02x\n",
- __func__, bus, info->addr, dummy->addr);
+ pr_debug("%d-%02x is probed at %02x\n",
+ adapter->nr, info->addr, dummy->addr);
i2c_unregister_device(dummy);
client = i2c_new_device(adapter, info);
}
}
if (!client)
- pr_notice("%s failed to register device %d-%02x\n",
- __func__, bus, info->addr);
+ pr_debug("failed to register device %d-%02x\n",
+ adapter->nr, info->addr);
else
- pr_debug("%s added i2c device %d-%02x\n",
- __func__, bus, info->addr);
+ pr_debug("added i2c device %d-%02x\n",
+ adapter->nr, info->addr);
- i2c_put_adapter(adapter);
return client;
}
-struct i2c_lookup {
- const char *name;
- int instance;
- int n;
-};
-
-static int __find_i2c_adap(struct device *dev, void *data)
-{
- struct i2c_lookup *lookup = data;
- static const char *prefix = "i2c-";
- struct i2c_adapter *adapter;
-
- if (strncmp(dev_name(dev), prefix, strlen(prefix)) != 0)
- return 0;
- adapter = to_i2c_adapter(dev);
- if (strncmp(adapter->name, lookup->name, strlen(lookup->name)) == 0 &&
- lookup->n++ == lookup->instance)
- return 1;
- return 0;
-}
-
-static int find_i2c_adapter_num(enum i2c_adapter_type type)
-{
- struct device *dev = NULL;
- struct i2c_adapter *adapter;
- struct i2c_lookup lookup;
-
- memset(&lookup, 0, sizeof(lookup));
- lookup.name = i2c_adapter_names[type];
- lookup.instance = (type == I2C_ADAPTER_DESIGNWARE_1) ? 1 : 0;
-
- /* find the adapter by name */
- dev = bus_find_device(&i2c_bus_type, NULL, &lookup, __find_i2c_adap);
- if (!dev) {
- /* Adapters may appear later. Deferred probing will retry */
- pr_notice("%s: i2c adapter %s not found on system.\n", __func__,
- lookup.name);
- return -ENODEV;
- }
- adapter = to_i2c_adapter(dev);
- return adapter->nr;
-}
-
-/*
- * Takes a list of addresses in addrs as such :
- * { addr1, ... , addrn, I2C_CLIENT_END };
- * add_probed_i2c_device will use i2c_new_probed_device
- * and probe for devices at all of the addresses listed.
- * Returns NULL if no devices found.
- * See Documentation/i2c/instantiating-devices for more information.
- */
-static struct i2c_client *add_probed_i2c_device(
- const char *name,
- enum i2c_adapter_type type,
- struct i2c_board_info *info,
- const unsigned short *addrs)
+static bool chromeos_laptop_match_adapter_devid(struct device *dev, u32 devid)
{
- return __add_probed_i2c_device(name,
- find_i2c_adapter_num(type),
- info,
- addrs);
-}
+ struct pci_dev *pdev;
-/*
- * Probes for a device at a single address, the one provided by
- * info->addr.
- * Returns NULL if no device found.
- */
-static struct i2c_client *add_i2c_device(const char *name,
- enum i2c_adapter_type type,
- struct i2c_board_info *info)
-{
- return __add_probed_i2c_device(name,
- find_i2c_adapter_num(type),
- info,
- NULL);
-}
-
-static int setup_cyapa_tp(enum i2c_adapter_type type)
-{
- if (tp)
- return 0;
+ if (!dev_is_pci(dev))
+ return false;
- /* add cyapa touchpad */
- tp = add_i2c_device("trackpad", type, &cyapa_device);
- return (!tp) ? -EAGAIN : 0;
+ pdev = to_pci_dev(dev);
+ return devid == PCI_DEVID(pdev->bus->number, pdev->devfn);
}
-static int setup_atmel_224s_tp(enum i2c_adapter_type type)
+static void chromeos_laptop_check_adapter(struct i2c_adapter *adapter)
{
- const unsigned short addr_list[] = { ATMEL_TP_I2C_BL_ADDR,
- I2C_CLIENT_END };
- if (tp)
- return 0;
-
- /* add atmel mxt touchpad */
- tp = add_probed_i2c_device("trackpad", type,
- &atmel_224s_tp_device, addr_list);
- return (!tp) ? -EAGAIN : 0;
-}
+ struct i2c_peripheral *i2c_dev;
+ int i;
-static int setup_elantech_tp(enum i2c_adapter_type type)
-{
- if (tp)
- return 0;
+ for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) {
+ i2c_dev = &cros_laptop->i2c_peripherals[i];
- /* add elantech touchpad */
- tp = add_i2c_device("trackpad", type, &elantech_device);
- return (!tp) ? -EAGAIN : 0;
-}
+ /* Skip devices already created */
+ if (i2c_dev->client)
+ continue;
-static int setup_atmel_1664s_ts(enum i2c_adapter_type type)
-{
- const unsigned short addr_list[] = { ATMEL_TS_I2C_BL_ADDR,
- I2C_CLIENT_END };
- if (ts)
- return 0;
-
- /* add atmel mxt touch device */
- ts = add_probed_i2c_device("touchscreen", type,
- &atmel_1664s_device, addr_list);
- return (!ts) ? -EAGAIN : 0;
-}
+ if (strncmp(adapter->name, i2c_adapter_names[i2c_dev->type],
+ strlen(i2c_adapter_names[i2c_dev->type])))
+ continue;
-static int setup_isl29018_als(enum i2c_adapter_type type)
-{
- if (als)
- return 0;
+ if (i2c_dev->pci_devid &&
+ !chromeos_laptop_match_adapter_devid(adapter->dev.parent,
+ i2c_dev->pci_devid)) {
+ continue;
+ }
- /* add isl29018 light sensor */
- als = add_i2c_device("lightsensor", type, &isl_als_device);
- return (!als) ? -EAGAIN : 0;
+ i2c_dev->client =
+ chromes_laptop_instantiate_i2c_device(adapter,
+ &i2c_dev->board_info,
+ i2c_dev->alt_addr);
+ }
}
-static int setup_tsl2583_als(enum i2c_adapter_type type)
+static void chromeos_laptop_detach_i2c_client(struct i2c_client *client)
{
- if (als)
- return 0;
-
- /* add tsl2583 light sensor */
- als = add_i2c_device(NULL, type, &tsl2583_als_device);
- return (!als) ? -EAGAIN : 0;
-}
+ struct i2c_peripheral *i2c_dev;
+ int i;
-static int setup_tsl2563_als(enum i2c_adapter_type type)
-{
- if (als)
- return 0;
+ for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) {
+ i2c_dev = &cros_laptop->i2c_peripherals[i];
- /* add tsl2563 light sensor */
- als = add_i2c_device(NULL, type, &tsl2563_als_device);
- return (!als) ? -EAGAIN : 0;
+ if (i2c_dev->client == client)
+ i2c_dev->client = NULL;
+ }
}
-static int __init chromeos_laptop_dmi_matched(const struct dmi_system_id *id)
+static int chromeos_laptop_i2c_notifier_call(struct notifier_block *nb,
+ unsigned long action, void *data)
{
- cros_laptop = (void *)id->driver_data;
- pr_debug("DMI Matched %s.\n", id->ident);
+ struct device *dev = data;
+
+ switch (action) {
+ case BUS_NOTIFY_ADD_DEVICE:
+ if (dev->type == &i2c_adapter_type)
+ chromeos_laptop_check_adapter(to_i2c_adapter(dev));
+ break;
+
+ case BUS_NOTIFY_REMOVED_DEVICE:
+ if (dev->type == &i2c_client_type)
+ chromeos_laptop_detach_i2c_client(to_i2c_client(dev));
+ break;
+ }
- /* Indicate to dmi_scan that processing is done. */
- return 1;
+ return 0;
}
-static int chromeos_laptop_probe(struct platform_device *pdev)
-{
- int i;
- int ret = 0;
-
- for (i = 0; i < MAX_I2C_PERIPHERALS; i++) {
- struct i2c_peripheral *i2c_dev;
-
- i2c_dev = &cros_laptop->i2c_peripherals[i];
-
- /* No more peripherals. */
- if (i2c_dev->add == NULL)
- break;
-
- if (i2c_dev->state == TIMEDOUT || i2c_dev->state == PROBED)
- continue;
-
- /*
- * Check that the i2c adapter is present.
- * -EPROBE_DEFER if missing as the adapter may appear much
- * later.
- */
- if (find_i2c_adapter_num(i2c_dev->type) == -ENODEV) {
- ret = -EPROBE_DEFER;
- continue;
- }
-
- /* Add the device. */
- if (i2c_dev->add(i2c_dev->type) == -EAGAIN) {
- /*
- * Set -EPROBE_DEFER a limited num of times
- * if device is not successfully added.
- */
- if (++i2c_dev->tries < MAX_I2C_DEVICE_DEFERRALS) {
- ret = -EPROBE_DEFER;
- } else {
- /* Ran out of tries. */
- pr_notice("%s: Ran out of tries for device.\n",
- __func__);
- i2c_dev->state = TIMEDOUT;
- }
- } else {
- i2c_dev->state = PROBED;
- }
- }
+static struct notifier_block chromeos_laptop_i2c_notifier = {
+ .notifier_call = chromeos_laptop_i2c_notifier_call,
+};
- return ret;
+#define DECLARE_CROS_LAPTOP(_name) \
+static const struct chromeos_laptop _name __initconst = { \
+ .i2c_peripherals = _name##_peripherals, \
+ .num_i2c_peripherals = ARRAY_SIZE(_name##_peripherals), \
}
-static struct chromeos_laptop samsung_series_5_550 = {
- .i2c_peripherals = {
- /* Touchpad. */
- { .add = setup_cyapa_tp, I2C_ADAPTER_SMBUS },
- /* Light Sensor. */
- { .add = setup_isl29018_als, I2C_ADAPTER_SMBUS },
+static struct i2c_peripheral samsung_series_5_550_peripherals[] __initdata = {
+ /* Touchpad. */
+ {
+ .board_info = {
+ I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
+ .flags = I2C_CLIENT_WAKE,
+ },
+ .dmi_name = "trackpad",
+ .type = I2C_ADAPTER_SMBUS,
+ },
+ /* Light Sensor. */
+ {
+ .board_info = {
+ I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
+ },
+ .dmi_name = "lightsensor",
+ .type = I2C_ADAPTER_SMBUS,
},
};
+DECLARE_CROS_LAPTOP(samsung_series_5_550);
-static struct chromeos_laptop samsung_series_5 = {
- .i2c_peripherals = {
- /* Light Sensor. */
- { .add = setup_tsl2583_als, I2C_ADAPTER_SMBUS },
+static struct i2c_peripheral samsung_series_5_peripherals[] __initdata = {
+ /* Light Sensor. */
+ {
+ .board_info = {
+ I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR),
+ },
+ .type = I2C_ADAPTER_SMBUS,
},
};
+DECLARE_CROS_LAPTOP(samsung_series_5);
-static struct chromeos_laptop chromebook_pixel = {
- .i2c_peripherals = {
- /* Touch Screen. */
- { .add = setup_atmel_1664s_ts, I2C_ADAPTER_PANEL },
- /* Touchpad. */
- { .add = setup_atmel_224s_tp, I2C_ADAPTER_VGADDC },
- /* Light Sensor. */
- { .add = setup_isl29018_als, I2C_ADAPTER_PANEL },
- },
+static const int chromebook_pixel_tp_keys[] __initconst = {
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ BTN_LEFT
};
-static struct chromeos_laptop hp_chromebook_14 = {
- .i2c_peripherals = {
- /* Touchpad. */
- { .add = setup_cyapa_tp, I2C_ADAPTER_DESIGNWARE_0 },
+static const struct property_entry
+chromebook_pixel_trackpad_props[] __initconst = {
+ PROPERTY_ENTRY_U32_ARRAY("linux,gpio-keymap", chromebook_pixel_tp_keys),
+ { }
+};
+
+static struct i2c_peripheral chromebook_pixel_peripherals[] __initdata = {
+ /* Touch Screen. */
+ {
+ .board_info = {
+ I2C_BOARD_INFO("atmel_mxt_ts",
+ ATMEL_TS_I2C_ADDR),
+ .flags = I2C_CLIENT_WAKE,
+ },
+ .dmi_name = "touchscreen",
+ .irqflags = IRQF_TRIGGER_FALLING,
+ .type = I2C_ADAPTER_PANEL,
+ .alt_addr = ATMEL_TS_I2C_BL_ADDR,
+ },
+ /* Touchpad. */
+ {
+ .board_info = {
+ I2C_BOARD_INFO("atmel_mxt_tp",
+ ATMEL_TP_I2C_ADDR),
+ .properties =
+ chromebook_pixel_trackpad_props,
+ .flags = I2C_CLIENT_WAKE,
+ },
+ .dmi_name = "trackpad",
+ .irqflags = IRQF_TRIGGER_FALLING,
+ .type = I2C_ADAPTER_VGADDC,
+ .alt_addr = ATMEL_TP_I2C_BL_ADDR,
+ },
+ /* Light Sensor. */
+ {
+ .board_info = {
+ I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
+ },
+ .dmi_name = "lightsensor",
+ .type = I2C_ADAPTER_PANEL,
},
};
+DECLARE_CROS_LAPTOP(chromebook_pixel);
-static struct chromeos_laptop dell_chromebook_11 = {
- .i2c_peripherals = {
- /* Touchpad. */
- { .add = setup_cyapa_tp, I2C_ADAPTER_DESIGNWARE_0 },
- /* Elan Touchpad option. */
- { .add = setup_elantech_tp, I2C_ADAPTER_DESIGNWARE_0 },
+static struct i2c_peripheral hp_chromebook_14_peripherals[] __initdata = {
+ /* Touchpad. */
+ {
+ .board_info = {
+ I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
+ .flags = I2C_CLIENT_WAKE,
+ },
+ .dmi_name = "trackpad",
+ .type = I2C_ADAPTER_DESIGNWARE,
},
};
+DECLARE_CROS_LAPTOP(hp_chromebook_14);
-static struct chromeos_laptop toshiba_cb35 = {
- .i2c_peripherals = {
- /* Touchpad. */
- { .add = setup_cyapa_tp, I2C_ADAPTER_DESIGNWARE_0 },
+static struct i2c_peripheral dell_chromebook_11_peripherals[] __initdata = {
+ /* Touchpad. */
+ {
+ .board_info = {
+ I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
+ .flags = I2C_CLIENT_WAKE,
+ },
+ .dmi_name = "trackpad",
+ .type = I2C_ADAPTER_DESIGNWARE,
+ },
+ /* Elan Touchpad option. */
+ {
+ .board_info = {
+ I2C_BOARD_INFO("elan_i2c", ELAN_TP_I2C_ADDR),
+ .flags = I2C_CLIENT_WAKE,
+ },
+ .dmi_name = "trackpad",
+ .type = I2C_ADAPTER_DESIGNWARE,
},
};
+DECLARE_CROS_LAPTOP(dell_chromebook_11);
-static struct chromeos_laptop acer_c7_chromebook = {
- .i2c_peripherals = {
- /* Touchpad. */
- { .add = setup_cyapa_tp, I2C_ADAPTER_SMBUS },
+static struct i2c_peripheral toshiba_cb35_peripherals[] __initdata = {
+ /* Touchpad. */
+ {
+ .board_info = {
+ I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
+ .flags = I2C_CLIENT_WAKE,
+ },
+ .dmi_name = "trackpad",
+ .type = I2C_ADAPTER_DESIGNWARE,
},
};
+DECLARE_CROS_LAPTOP(toshiba_cb35);
-static struct chromeos_laptop acer_ac700 = {
- .i2c_peripherals = {
- /* Light Sensor. */
- { .add = setup_tsl2563_als, I2C_ADAPTER_SMBUS },
+static struct i2c_peripheral acer_c7_chromebook_peripherals[] __initdata = {
+ /* Touchpad. */
+ {
+ .board_info = {
+ I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
+ .flags = I2C_CLIENT_WAKE,
+ },
+ .dmi_name = "trackpad",
+ .type = I2C_ADAPTER_SMBUS,
},
};
+DECLARE_CROS_LAPTOP(acer_c7_chromebook);
-static struct chromeos_laptop acer_c720 = {
- .i2c_peripherals = {
- /* Touchscreen. */
- { .add = setup_atmel_1664s_ts, I2C_ADAPTER_DESIGNWARE_1 },
- /* Touchpad. */
- { .add = setup_cyapa_tp, I2C_ADAPTER_DESIGNWARE_0 },
- /* Elan Touchpad option. */
- { .add = setup_elantech_tp, I2C_ADAPTER_DESIGNWARE_0 },
- /* Light Sensor. */
- { .add = setup_isl29018_als, I2C_ADAPTER_DESIGNWARE_1 },
+static struct i2c_peripheral acer_ac700_peripherals[] __initdata = {
+ /* Light Sensor. */
+ {
+ .board_info = {
+ I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR),
+ },
+ .type = I2C_ADAPTER_SMBUS,
},
};
+DECLARE_CROS_LAPTOP(acer_ac700);
-static struct chromeos_laptop hp_pavilion_14_chromebook = {
- .i2c_peripherals = {
- /* Touchpad. */
- { .add = setup_cyapa_tp, I2C_ADAPTER_SMBUS },
+static struct i2c_peripheral acer_c720_peripherals[] __initdata = {
+ /* Touchscreen. */
+ {
+ .board_info = {
+ I2C_BOARD_INFO("atmel_mxt_ts",
+ ATMEL_TS_I2C_ADDR),
+ .flags = I2C_CLIENT_WAKE,
+ },
+ .dmi_name = "touchscreen",
+ .irqflags = IRQF_TRIGGER_FALLING,
+ .type = I2C_ADAPTER_DESIGNWARE,
+ .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x2)),
+ .alt_addr = ATMEL_TS_I2C_BL_ADDR,
+ },
+ /* Touchpad. */
+ {
+ .board_info = {
+ I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
+ .flags = I2C_CLIENT_WAKE,
+ },
+ .dmi_name = "trackpad",
+ .type = I2C_ADAPTER_DESIGNWARE,
+ .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x1)),
+ },
+ /* Elan Touchpad option. */
+ {
+ .board_info = {
+ I2C_BOARD_INFO("elan_i2c", ELAN_TP_I2C_ADDR),
+ .flags = I2C_CLIENT_WAKE,
+ },
+ .dmi_name = "trackpad",
+ .type = I2C_ADAPTER_DESIGNWARE,
+ .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x1)),
+ },
+ /* Light Sensor. */
+ {
+ .board_info = {
+ I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
+ },
+ .dmi_name = "lightsensor",
+ .type = I2C_ADAPTER_DESIGNWARE,
+ .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x2)),
},
};
+DECLARE_CROS_LAPTOP(acer_c720);
-static struct chromeos_laptop cr48 = {
- .i2c_peripherals = {
- /* Light Sensor. */
- { .add = setup_tsl2563_als, I2C_ADAPTER_SMBUS },
+static struct i2c_peripheral
+hp_pavilion_14_chromebook_peripherals[] __initdata = {
+ /* Touchpad. */
+ {
+ .board_info = {
+ I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
+ .flags = I2C_CLIENT_WAKE,
+ },
+ .dmi_name = "trackpad",
+ .type = I2C_ADAPTER_SMBUS,
},
};
+DECLARE_CROS_LAPTOP(hp_pavilion_14_chromebook);
-#define _CBDD(board_) \
- .callback = chromeos_laptop_dmi_matched, \
- .driver_data = (void *)&board_
+static struct i2c_peripheral cr48_peripherals[] __initdata = {
+ /* Light Sensor. */
+ {
+ .board_info = {
+ I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR),
+ },
+ .type = I2C_ADAPTER_SMBUS,
+ },
+};
+DECLARE_CROS_LAPTOP(cr48);
static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = {
{
@@ -525,14 +426,14 @@ static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = {
DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"),
DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"),
},
- _CBDD(samsung_series_5_550),
+ .driver_data = (void *)&samsung_series_5_550,
},
{
.ident = "Samsung Series 5",
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "Alex"),
},
- _CBDD(samsung_series_5),
+ .driver_data = (void *)&samsung_series_5,
},
{
.ident = "Chromebook Pixel",
@@ -540,7 +441,7 @@ static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = {
DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
},
- _CBDD(chromebook_pixel),
+ .driver_data = (void *)&chromebook_pixel,
},
{
.ident = "Wolf",
@@ -548,7 +449,7 @@ static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = {
DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
DMI_MATCH(DMI_PRODUCT_NAME, "Wolf"),
},
- _CBDD(dell_chromebook_11),
+ .driver_data = (void *)&dell_chromebook_11,
},
{
.ident = "HP Chromebook 14",
@@ -556,7 +457,7 @@ static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = {
DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
DMI_MATCH(DMI_PRODUCT_NAME, "Falco"),
},
- _CBDD(hp_chromebook_14),
+ .driver_data = (void *)&hp_chromebook_14,
},
{
.ident = "Toshiba CB35",
@@ -564,99 +465,214 @@ static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = {
DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
DMI_MATCH(DMI_PRODUCT_NAME, "Leon"),
},
- _CBDD(toshiba_cb35),
+ .driver_data = (void *)&toshiba_cb35,
},
{
.ident = "Acer C7 Chromebook",
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "Parrot"),
},
- _CBDD(acer_c7_chromebook),
+ .driver_data = (void *)&acer_c7_chromebook,
},
{
.ident = "Acer AC700",
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"),
},
- _CBDD(acer_ac700),
+ .driver_data = (void *)&acer_ac700,
},
{
.ident = "Acer C720",
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "Peppy"),
},
- _CBDD(acer_c720),
+ .driver_data = (void *)&acer_c720,
},
{
.ident = "HP Pavilion 14 Chromebook",
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "Butterfly"),
},
- _CBDD(hp_pavilion_14_chromebook),
+ .driver_data = (void *)&hp_pavilion_14_chromebook,
},
{
.ident = "Cr-48",
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "Mario"),
},
- _CBDD(cr48),
+ .driver_data = (void *)&cr48,
},
{ }
};
MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table);
-static struct platform_device *cros_platform_device;
+static int __init chromeos_laptop_scan_adapter(struct device *dev, void *data)
+{
+ struct i2c_adapter *adapter;
-static struct platform_driver cros_platform_driver = {
- .driver = {
- .name = "chromeos_laptop",
- },
- .probe = chromeos_laptop_probe,
-};
+ adapter = i2c_verify_adapter(dev);
+ if (adapter)
+ chromeos_laptop_check_adapter(adapter);
+
+ return 0;
+}
+
+static int __init chromeos_laptop_get_irq_from_dmi(const char *dmi_name)
+{
+ const struct dmi_device *dmi_dev;
+ const struct dmi_dev_onboard *dev_data;
+
+ dmi_dev = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, dmi_name, NULL);
+ if (!dmi_dev) {
+ pr_err("failed to find DMI device '%s'\n", dmi_name);
+ return -ENOENT;
+ }
+
+ dev_data = dmi_dev->device_data;
+ if (!dev_data) {
+ pr_err("failed to get data from DMI for '%s'\n", dmi_name);
+ return -EINVAL;
+ }
+
+ return dev_data->instance;
+}
+
+static int __init chromeos_laptop_setup_irq(struct i2c_peripheral *i2c_dev)
+{
+ int irq;
+
+ if (i2c_dev->dmi_name) {
+ irq = chromeos_laptop_get_irq_from_dmi(i2c_dev->dmi_name);
+ if (irq < 0)
+ return irq;
+
+ i2c_dev->irq_resource = (struct resource)
+ DEFINE_RES_NAMED(irq, 1, NULL,
+ IORESOURCE_IRQ | i2c_dev->irqflags);
+ i2c_dev->board_info.resources = &i2c_dev->irq_resource;
+ i2c_dev->board_info.num_resources = 1;
+ }
+
+ return 0;
+}
+
+static struct chromeos_laptop * __init
+chromeos_laptop_prepare(const struct chromeos_laptop *src)
+{
+ struct chromeos_laptop *cros_laptop;
+ struct i2c_peripheral *i2c_dev;
+ struct i2c_board_info *info;
+ int error;
+ int i;
+
+ cros_laptop = kzalloc(sizeof(*cros_laptop), GFP_KERNEL);
+ if (!cros_laptop)
+ return ERR_PTR(-ENOMEM);
+
+ cros_laptop->i2c_peripherals = kmemdup(src->i2c_peripherals,
+ src->num_i2c_peripherals *
+ sizeof(*src->i2c_peripherals),
+ GFP_KERNEL);
+ if (!cros_laptop->i2c_peripherals) {
+ error = -ENOMEM;
+ goto err_free_cros_laptop;
+ }
+
+ cros_laptop->num_i2c_peripherals = src->num_i2c_peripherals;
+
+ for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) {
+ i2c_dev = &cros_laptop->i2c_peripherals[i];
+ info = &i2c_dev->board_info;
+
+ error = chromeos_laptop_setup_irq(i2c_dev);
+ if (error)
+ goto err_destroy_cros_peripherals;
+
+ /* We need to deep-copy properties */
+ if (info->properties) {
+ info->properties =
+ property_entries_dup(info->properties);
+ if (IS_ERR(info->properties)) {
+ error = PTR_ERR(info->properties);
+ goto err_destroy_cros_peripherals;
+ }
+ }
+ }
+
+ return cros_laptop;
+
+err_destroy_cros_peripherals:
+ while (--i >= 0) {
+ i2c_dev = &cros_laptop->i2c_peripherals[i];
+ info = &i2c_dev->board_info;
+ if (info->properties)
+ property_entries_free(info->properties);
+ }
+ kfree(cros_laptop->i2c_peripherals);
+err_free_cros_laptop:
+ kfree(cros_laptop);
+ return ERR_PTR(error);
+}
+
+static void chromeos_laptop_destroy(const struct chromeos_laptop *cros_laptop)
+{
+ struct i2c_peripheral *i2c_dev;
+ struct i2c_board_info *info;
+ int i;
+
+ for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) {
+ i2c_dev = &cros_laptop->i2c_peripherals[i];
+ info = &i2c_dev->board_info;
+
+ if (i2c_dev->client)
+ i2c_unregister_device(i2c_dev->client);
+
+ if (info->properties)
+ property_entries_free(info->properties);
+ }
+
+ kfree(cros_laptop->i2c_peripherals);
+ kfree(cros_laptop);
+}
static int __init chromeos_laptop_init(void)
{
- int ret;
+ const struct dmi_system_id *dmi_id;
+ int error;
- if (!dmi_check_system(chromeos_laptop_dmi_table)) {
- pr_debug("%s unsupported system.\n", __func__);
+ dmi_id = dmi_first_match(chromeos_laptop_dmi_table);
+ if (!dmi_id) {
+ pr_debug("unsupported system\n");
return -ENODEV;
}
- ret = platform_driver_register(&cros_platform_driver);
- if (ret)
- return ret;
+ pr_debug("DMI Matched %s\n", dmi_id->ident);
+
+ cros_laptop = chromeos_laptop_prepare((void *)dmi_id->driver_data);
+ if (IS_ERR(cros_laptop))
+ return PTR_ERR(cros_laptop);
- cros_platform_device = platform_device_alloc("chromeos_laptop", -1);
- if (!cros_platform_device) {
- ret = -ENOMEM;
- goto fail_platform_device1;
+ error = bus_register_notifier(&i2c_bus_type,
+ &chromeos_laptop_i2c_notifier);
+ if (error) {
+ pr_err("failed to register i2c bus notifier: %d\n", error);
+ chromeos_laptop_destroy(cros_laptop);
+ return error;
}
- ret = platform_device_add(cros_platform_device);
- if (ret)
- goto fail_platform_device2;
+ /*
+ * Scan adapters that have been registered before we installed
+ * the notifier to make sure we do not miss any devices.
+ */
+ i2c_for_each_dev(NULL, chromeos_laptop_scan_adapter);
return 0;
-
-fail_platform_device2:
- platform_device_put(cros_platform_device);
-fail_platform_device1:
- platform_driver_unregister(&cros_platform_driver);
- return ret;
}
static void __exit chromeos_laptop_exit(void)
{
- if (als)
- i2c_unregister_device(als);
- if (tp)
- i2c_unregister_device(tp);
- if (ts)
- i2c_unregister_device(ts);
-
- platform_device_unregister(cros_platform_device);
- platform_driver_unregister(&cros_platform_driver);
+ bus_unregister_notifier(&i2c_bus_type, &chromeos_laptop_i2c_notifier);
+ chromeos_laptop_destroy(cros_laptop);
}
module_init(chromeos_laptop_init);
diff --git a/drivers/platform/chrome/cros_ec_debugfs.c b/drivers/platform/chrome/cros_ec_debugfs.c
index 0e88e18362c1..cc265ed8deb7 100644
--- a/drivers/platform/chrome/cros_ec_debugfs.c
+++ b/drivers/platform/chrome/cros_ec_debugfs.c
@@ -211,6 +211,58 @@ static int cros_ec_console_log_release(struct inode *inode, struct file *file)
return 0;
}
+static ssize_t cros_ec_pdinfo_read(struct file *file,
+ char __user *user_buf,
+ size_t count,
+ loff_t *ppos)
+{
+ char read_buf[EC_USB_PD_MAX_PORTS * 40], *p = read_buf;
+ struct cros_ec_debugfs *debug_info = file->private_data;
+ struct cros_ec_device *ec_dev = debug_info->ec->ec_dev;
+ struct {
+ struct cros_ec_command msg;
+ union {
+ struct ec_response_usb_pd_control_v1 resp;
+ struct ec_params_usb_pd_control params;
+ };
+ } __packed ec_buf;
+ struct cros_ec_command *msg;
+ struct ec_response_usb_pd_control_v1 *resp;
+ struct ec_params_usb_pd_control *params;
+ int i;
+
+ msg = &ec_buf.msg;
+ params = (struct ec_params_usb_pd_control *)msg->data;
+ resp = (struct ec_response_usb_pd_control_v1 *)msg->data;
+
+ msg->command = EC_CMD_USB_PD_CONTROL;
+ msg->version = 1;
+ msg->insize = sizeof(*resp);
+ msg->outsize = sizeof(*params);
+
+ /*
+ * Read status from all PD ports until failure, typically caused
+ * by attempting to read status on a port that doesn't exist.
+ */
+ for (i = 0; i < EC_USB_PD_MAX_PORTS; ++i) {
+ params->port = i;
+ params->role = 0;
+ params->mux = 0;
+ params->swap = 0;
+
+ if (cros_ec_cmd_xfer_status(ec_dev, msg) < 0)
+ break;
+
+ p += scnprintf(p, sizeof(read_buf) + read_buf - p,
+ "p%d: %s en:%.2x role:%.2x pol:%.2x\n", i,
+ resp->state, resp->enabled, resp->role,
+ resp->polarity);
+ }
+
+ return simple_read_from_buffer(user_buf, count, ppos,
+ read_buf, p - read_buf);
+}
+
const struct file_operations cros_ec_console_log_fops = {
.owner = THIS_MODULE,
.open = cros_ec_console_log_open,
@@ -220,6 +272,13 @@ const struct file_operations cros_ec_console_log_fops = {
.release = cros_ec_console_log_release,
};
+const struct file_operations cros_ec_pdinfo_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = cros_ec_pdinfo_read,
+ .llseek = default_llseek,
+};
+
static int ec_read_version_supported(struct cros_ec_dev *ec)
{
struct ec_params_get_cmd_versions_v1 *params;
@@ -288,7 +347,7 @@ static int cros_ec_create_console_log(struct cros_ec_debugfs *debug_info)
init_waitqueue_head(&debug_info->log_wq);
if (!debugfs_create_file("console_log",
- S_IFREG | S_IRUGO,
+ S_IFREG | 0444,
debug_info->dir,
debug_info,
&cros_ec_console_log_fops))
@@ -341,7 +400,7 @@ static int cros_ec_create_panicinfo(struct cros_ec_debugfs *debug_info)
debug_info->panicinfo_blob.size = ret;
if (!debugfs_create_blob("panicinfo",
- S_IFREG | S_IRUGO,
+ S_IFREG | 0444,
debug_info->dir,
&debug_info->panicinfo_blob)) {
ret = -ENOMEM;
@@ -355,6 +414,15 @@ free:
return ret;
}
+static int cros_ec_create_pdinfo(struct cros_ec_debugfs *debug_info)
+{
+ if (!debugfs_create_file("pdinfo", 0444, debug_info->dir, debug_info,
+ &cros_ec_pdinfo_fops))
+ return -ENOMEM;
+
+ return 0;
+}
+
int cros_ec_debugfs_init(struct cros_ec_dev *ec)
{
struct cros_ec_platform *ec_platform = dev_get_platdata(ec->dev);
@@ -379,6 +447,10 @@ int cros_ec_debugfs_init(struct cros_ec_dev *ec)
if (ret)
goto remove_debugfs;
+ ret = cros_ec_create_pdinfo(debug_info);
+ if (ret)
+ goto remove_debugfs;
+
ec->debug_info = debug_info;
return 0;
diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c
index af89e82eecd2..3682e1539251 100644
--- a/drivers/platform/chrome/cros_ec_lpc.c
+++ b/drivers/platform/chrome/cros_ec_lpc.c
@@ -31,6 +31,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/printk.h>
+#include <linux/suspend.h>
#define DRV_NAME "cros_ec_lpcs"
#define ACPI_DRV_NAME "GOOG0004"
@@ -235,6 +236,9 @@ static void cros_ec_lpc_acpi_notify(acpi_handle device, u32 value, void *data)
cros_ec_get_next_event(ec_dev, NULL) > 0)
blocking_notifier_call_chain(&ec_dev->event_notifier, 0,
ec_dev);
+
+ if (value == ACPI_NOTIFY_DEVICE_WAKE)
+ pm_system_wakeup();
}
static int cros_ec_lpc_probe(struct platform_device *pdev)
@@ -342,6 +346,18 @@ static const struct dmi_system_id cros_ec_lpc_dmi_table[] __initconst = {
},
},
{
+ /*
+ * If the box is running custom coreboot firmware then the
+ * DMI BIOS version string will not be matched by "Google_",
+ * but the system vendor string will still be matched by
+ * "GOOGLE".
+ */
+ .matches = {
+ DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
+ DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
+ },
+ },
+ {
/* x86-link, the Chromebook Pixel. */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
diff --git a/drivers/platform/chrome/cros_ec_sysfs.c b/drivers/platform/chrome/cros_ec_sysfs.c
index da0a719d32f7..5a6db3fe213a 100644
--- a/drivers/platform/chrome/cros_ec_sysfs.c
+++ b/drivers/platform/chrome/cros_ec_sysfs.c
@@ -34,10 +34,12 @@
#include <linux/types.h>
#include <linux/uaccess.h>
+#define to_cros_ec_dev(dev) container_of(dev, struct cros_ec_dev, class_dev)
+
/* Accessor functions */
-static ssize_t show_ec_reboot(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t reboot_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
int count = 0;
@@ -48,9 +50,9 @@ static ssize_t show_ec_reboot(struct device *dev,
return count;
}
-static ssize_t store_ec_reboot(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t reboot_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
static const struct {
const char * const str;
@@ -70,8 +72,7 @@ static ssize_t store_ec_reboot(struct device *dev,
int got_cmd = 0, offset = 0;
int i;
int ret;
- struct cros_ec_dev *ec = container_of(dev,
- struct cros_ec_dev, class_dev);
+ struct cros_ec_dev *ec = to_cros_ec_dev(dev);
msg = kmalloc(sizeof(*msg) + sizeof(*param), GFP_KERNEL);
if (!msg)
@@ -114,22 +115,16 @@ static ssize_t store_ec_reboot(struct device *dev,
msg->command = EC_CMD_REBOOT_EC + ec->cmd_offset;
msg->outsize = sizeof(*param);
msg->insize = 0;
- ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
- if (ret < 0) {
+ ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
+ if (ret < 0)
count = ret;
- goto exit;
- }
- if (msg->result != EC_RES_SUCCESS) {
- dev_dbg(ec->dev, "EC result %d\n", msg->result);
- count = -EINVAL;
- }
exit:
kfree(msg);
return count;
}
-static ssize_t show_ec_version(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t version_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
static const char * const image_names[] = {"unknown", "RO", "RW"};
struct ec_response_get_version *r_ver;
@@ -138,8 +133,7 @@ static ssize_t show_ec_version(struct device *dev,
struct cros_ec_command *msg;
int ret;
int count = 0;
- struct cros_ec_dev *ec = container_of(dev,
- struct cros_ec_dev, class_dev);
+ struct cros_ec_dev *ec = to_cros_ec_dev(dev);
msg = kmalloc(sizeof(*msg) + EC_HOST_PARAM_SIZE, GFP_KERNEL);
if (!msg)
@@ -150,17 +144,11 @@ static ssize_t show_ec_version(struct device *dev,
msg->command = EC_CMD_GET_VERSION + ec->cmd_offset;
msg->insize = sizeof(*r_ver);
msg->outsize = 0;
- ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
+ ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
if (ret < 0) {
count = ret;
goto exit;
}
- if (msg->result != EC_RES_SUCCESS) {
- count = scnprintf(buf, PAGE_SIZE,
- "ERROR: EC returned %d\n", msg->result);
- goto exit;
- }
-
r_ver = (struct ec_response_get_version *)msg->data;
/* Strings should be null-terminated, but let's be sure. */
r_ver->version_string_ro[sizeof(r_ver->version_string_ro) - 1] = '\0';
@@ -237,14 +225,13 @@ exit:
return count;
}
-static ssize_t show_ec_flashinfo(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t flashinfo_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct ec_response_flash_info *resp;
struct cros_ec_command *msg;
int ret;
- struct cros_ec_dev *ec = container_of(dev,
- struct cros_ec_dev, class_dev);
+ struct cros_ec_dev *ec = to_cros_ec_dev(dev);
msg = kmalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL);
if (!msg)
@@ -255,14 +242,9 @@ static ssize_t show_ec_flashinfo(struct device *dev,
msg->command = EC_CMD_FLASH_INFO + ec->cmd_offset;
msg->insize = sizeof(*resp);
msg->outsize = 0;
- ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
+ ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
if (ret < 0)
goto exit;
- if (msg->result != EC_RES_SUCCESS) {
- ret = scnprintf(buf, PAGE_SIZE,
- "ERROR: EC returned %d\n", msg->result);
- goto exit;
- }
resp = (struct ec_response_flash_info *)msg->data;
@@ -276,21 +258,102 @@ exit:
return ret;
}
+/* Keyboard wake angle control */
+static ssize_t kb_wake_angle_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cros_ec_dev *ec = to_cros_ec_dev(dev);
+ struct ec_response_motion_sense *resp;
+ struct ec_params_motion_sense *param;
+ struct cros_ec_command *msg;
+ int ret;
+
+ msg = kmalloc(sizeof(*msg) + EC_HOST_PARAM_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ param = (struct ec_params_motion_sense *)msg->data;
+ msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
+ msg->version = 2;
+ param->cmd = MOTIONSENSE_CMD_KB_WAKE_ANGLE;
+ param->kb_wake_angle.data = EC_MOTION_SENSE_NO_VALUE;
+ msg->outsize = sizeof(*param);
+ msg->insize = sizeof(*resp);
+
+ ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
+ if (ret < 0)
+ goto exit;
+
+ resp = (struct ec_response_motion_sense *)msg->data;
+ ret = scnprintf(buf, PAGE_SIZE, "%d\n", resp->kb_wake_angle.ret);
+exit:
+ kfree(msg);
+ return ret;
+}
+
+static ssize_t kb_wake_angle_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cros_ec_dev *ec = to_cros_ec_dev(dev);
+ struct ec_params_motion_sense *param;
+ struct cros_ec_command *msg;
+ u16 angle;
+ int ret;
+
+ ret = kstrtou16(buf, 0, &angle);
+ if (ret)
+ return ret;
+
+ msg = kmalloc(sizeof(*msg) + EC_HOST_PARAM_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ param = (struct ec_params_motion_sense *)msg->data;
+ msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
+ msg->version = 2;
+ param->cmd = MOTIONSENSE_CMD_KB_WAKE_ANGLE;
+ param->kb_wake_angle.data = angle;
+ msg->outsize = sizeof(*param);
+ msg->insize = sizeof(struct ec_response_motion_sense);
+
+ ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
+ kfree(msg);
+ if (ret < 0)
+ return ret;
+ return count;
+}
+
/* Module initialization */
-static DEVICE_ATTR(reboot, S_IWUSR | S_IRUGO, show_ec_reboot, store_ec_reboot);
-static DEVICE_ATTR(version, S_IRUGO, show_ec_version, NULL);
-static DEVICE_ATTR(flashinfo, S_IRUGO, show_ec_flashinfo, NULL);
+static DEVICE_ATTR_RW(reboot);
+static DEVICE_ATTR_RO(version);
+static DEVICE_ATTR_RO(flashinfo);
+static DEVICE_ATTR_RW(kb_wake_angle);
static struct attribute *__ec_attrs[] = {
+ &dev_attr_kb_wake_angle.attr,
&dev_attr_reboot.attr,
&dev_attr_version.attr,
&dev_attr_flashinfo.attr,
NULL,
};
+static umode_t cros_ec_ctrl_visible(struct kobject *kobj,
+ struct attribute *a, int n)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct cros_ec_dev *ec = to_cros_ec_dev(dev);
+
+ if (a == &dev_attr_kb_wake_angle.attr && !ec->has_kb_wake_angle)
+ return 0;
+
+ return a->mode;
+}
+
struct attribute_group cros_ec_attr_group = {
.attrs = __ec_attrs,
+ .is_visible = cros_ec_ctrl_visible,
};
EXPORT_SYMBOL(cros_ec_attr_group);
diff --git a/drivers/platform/mellanox/mlxreg-hotplug.c b/drivers/platform/mellanox/mlxreg-hotplug.c
index 313cf8ad77bf..ea9e7f4479ca 100644
--- a/drivers/platform/mellanox/mlxreg-hotplug.c
+++ b/drivers/platform/mellanox/mlxreg-hotplug.c
@@ -93,9 +93,11 @@ struct mlxreg_hotplug_priv_data {
bool after_probe;
};
-static int mlxreg_hotplug_device_create(struct device *dev,
+static int mlxreg_hotplug_device_create(struct mlxreg_hotplug_priv_data *priv,
struct mlxreg_core_data *data)
{
+ struct mlxreg_core_hotplug_platform_data *pdata;
+
/*
* Return if adapter number is negative. It could be in case hotplug
* event is not associated with hotplug device.
@@ -103,19 +105,21 @@ static int mlxreg_hotplug_device_create(struct device *dev,
if (data->hpdev.nr < 0)
return 0;
- data->hpdev.adapter = i2c_get_adapter(data->hpdev.nr);
+ pdata = dev_get_platdata(&priv->pdev->dev);
+ data->hpdev.adapter = i2c_get_adapter(data->hpdev.nr +
+ pdata->shift_nr);
if (!data->hpdev.adapter) {
- dev_err(dev, "Failed to get adapter for bus %d\n",
- data->hpdev.nr);
+ dev_err(priv->dev, "Failed to get adapter for bus %d\n",
+ data->hpdev.nr + pdata->shift_nr);
return -EFAULT;
}
data->hpdev.client = i2c_new_device(data->hpdev.adapter,
data->hpdev.brdinfo);
if (!data->hpdev.client) {
- dev_err(dev, "Failed to create client %s at bus %d at addr 0x%02x\n",
- data->hpdev.brdinfo->type, data->hpdev.nr,
- data->hpdev.brdinfo->addr);
+ dev_err(priv->dev, "Failed to create client %s at bus %d at addr 0x%02x\n",
+ data->hpdev.brdinfo->type, data->hpdev.nr +
+ pdata->shift_nr, data->hpdev.brdinfo->addr);
i2c_put_adapter(data->hpdev.adapter);
data->hpdev.adapter = NULL;
@@ -270,10 +274,10 @@ mlxreg_hotplug_work_helper(struct mlxreg_hotplug_priv_data *priv,
if (item->inversed)
mlxreg_hotplug_device_destroy(data);
else
- mlxreg_hotplug_device_create(priv->dev, data);
+ mlxreg_hotplug_device_create(priv, data);
} else {
if (item->inversed)
- mlxreg_hotplug_device_create(priv->dev, data);
+ mlxreg_hotplug_device_create(priv, data);
else
mlxreg_hotplug_device_destroy(data);
}
@@ -319,7 +323,7 @@ mlxreg_hotplug_health_work_helper(struct mlxreg_hotplug_priv_data *priv,
if (regval == MLXREG_HOTPLUG_HEALTH_MASK) {
if ((data->health_cntr++ == MLXREG_HOTPLUG_RST_CNTR) ||
!priv->after_probe) {
- mlxreg_hotplug_device_create(priv->dev, data);
+ mlxreg_hotplug_device_create(priv, data);
data->attached = true;
}
} else {
@@ -550,6 +554,7 @@ static int mlxreg_hotplug_probe(struct platform_device *pdev)
{
struct mlxreg_core_hotplug_platform_data *pdata;
struct mlxreg_hotplug_priv_data *priv;
+ struct i2c_adapter *deferred_adap;
int err;
pdata = dev_get_platdata(&pdev->dev);
@@ -558,6 +563,12 @@ static int mlxreg_hotplug_probe(struct platform_device *pdev)
return -EINVAL;
}
+ /* Defer probing if the necessary adapter is not configured yet. */
+ deferred_adap = i2c_get_adapter(pdata->deferred_nr);
+ if (!deferred_adap)
+ return -EPROBE_DEFER;
+ i2c_put_adapter(deferred_adap);
+
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 51ebc5a6053f..39d06dd1f63a 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -439,6 +439,7 @@ config SURFACE3_WMI
config THINKPAD_ACPI
tristate "ThinkPad ACPI Laptop Extras"
depends on ACPI
+ depends on ACPI_BATTERY
depends on INPUT
depends on RFKILL || RFKILL = n
depends on ACPI_VIDEO || ACPI_VIDEO = n
@@ -756,6 +757,8 @@ config TOPSTAR_LAPTOP
depends on ACPI
depends on INPUT
select INPUT_SPARSEKMAP
+ select LEDS_CLASS
+ select NEW_LEDS
---help---
This driver adds support for hotkeys found on Topstar laptops.
@@ -1173,6 +1176,7 @@ config INTEL_TELEMETRY
config MLX_PLATFORM
tristate "Mellanox Technologies platform support"
+ depends on I2C && REGMAP
---help---
This option enables system support for the Mellanox Technologies
platform. The Mellanox systems provide data center networking
diff --git a/drivers/platform/x86/dell-smbios-base.c b/drivers/platform/x86/dell-smbios-base.c
index 2485c80a9fdd..33fb2a20458a 100644
--- a/drivers/platform/x86/dell-smbios-base.c
+++ b/drivers/platform/x86/dell-smbios-base.c
@@ -514,7 +514,7 @@ static int build_tokens_sysfs(struct platform_device *dev)
continue;
loop_fail_create_value:
- kfree(value_name);
+ kfree(location_name);
goto out_unwind_strings;
}
smbios_attribute_group.attrs = token_attrs;
@@ -525,7 +525,7 @@ loop_fail_create_value:
return 0;
out_unwind_strings:
- for (i = i-1; i > 0; i--) {
+ while (i--) {
kfree(token_location_attrs[i].attr.name);
kfree(token_value_attrs[i].attr.name);
}
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c
index 5a681962899c..4c38904a8a32 100644
--- a/drivers/platform/x86/eeepc-laptop.c
+++ b/drivers/platform/x86/eeepc-laptop.c
@@ -492,7 +492,7 @@ static void eeepc_platform_exit(struct eeepc_laptop *eeepc)
* potentially bad time, such as a timer interrupt.
*/
static void tpd_led_update(struct work_struct *work)
- {
+{
struct eeepc_laptop *eeepc;
eeepc = container_of(work, struct eeepc_laptop, tpd_led_work);
diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c
index 2cfbd3fa5136..cd95b6f3a064 100644
--- a/drivers/platform/x86/fujitsu-laptop.c
+++ b/drivers/platform/x86/fujitsu-laptop.c
@@ -53,6 +53,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/acpi.h>
+#include <linux/bitops.h>
#include <linux/dmi.h>
#include <linux/backlight.h>
#include <linux/fb.h>
@@ -61,12 +62,11 @@
#include <linux/kfifo.h>
#include <linux/leds.h>
#include <linux/platform_device.h>
-#include <linux/slab.h>
#include <acpi/video.h>
-#define FUJITSU_DRIVER_VERSION "0.6.0"
+#define FUJITSU_DRIVER_VERSION "0.6.0"
-#define FUJITSU_LCD_N_LEVELS 8
+#define FUJITSU_LCD_N_LEVELS 8
#define ACPI_FUJITSU_CLASS "fujitsu"
#define ACPI_FUJITSU_BL_HID "FUJ02B1"
@@ -76,41 +76,51 @@
#define ACPI_FUJITSU_LAPTOP_DRIVER_NAME "Fujitsu laptop FUJ02E3 ACPI hotkeys driver"
#define ACPI_FUJITSU_LAPTOP_DEVICE_NAME "Fujitsu FUJ02E3"
-#define ACPI_FUJITSU_NOTIFY_CODE1 0x80
+#define ACPI_FUJITSU_NOTIFY_CODE 0x80
/* FUNC interface - command values */
-#define FUNC_FLAGS 0x1000
-#define FUNC_LEDS 0x1001
-#define FUNC_BUTTONS 0x1002
-#define FUNC_BACKLIGHT 0x1004
+#define FUNC_FLAGS BIT(12)
+#define FUNC_LEDS (BIT(12) | BIT(0))
+#define FUNC_BUTTONS (BIT(12) | BIT(1))
+#define FUNC_BACKLIGHT (BIT(12) | BIT(2))
/* FUNC interface - responses */
-#define UNSUPPORTED_CMD 0x80000000
+#define UNSUPPORTED_CMD 0x80000000
/* FUNC interface - status flags */
-#define FLAG_RFKILL 0x020
-#define FLAG_LID 0x100
-#define FLAG_DOCK 0x200
+#define FLAG_RFKILL BIT(5)
+#define FLAG_LID BIT(8)
+#define FLAG_DOCK BIT(9)
/* FUNC interface - LED control */
-#define FUNC_LED_OFF 0x1
-#define FUNC_LED_ON 0x30001
-#define KEYBOARD_LAMPS 0x100
-#define LOGOLAMP_POWERON 0x2000
-#define LOGOLAMP_ALWAYS 0x4000
-#define RADIO_LED_ON 0x20
-#define ECO_LED 0x10000
-#define ECO_LED_ON 0x80000
-
-/* Hotkey details */
-#define KEY1_CODE 0x410 /* codes for the keys in the GIRB register */
-#define KEY2_CODE 0x411
-#define KEY3_CODE 0x412
-#define KEY4_CODE 0x413
-#define KEY5_CODE 0x420
-
-#define MAX_HOTKEY_RINGBUFFER_SIZE 100
-#define RINGBUFFERSIZE 40
+#define FUNC_LED_OFF BIT(0)
+#define FUNC_LED_ON (BIT(0) | BIT(16) | BIT(17))
+#define LOGOLAMP_POWERON BIT(13)
+#define LOGOLAMP_ALWAYS BIT(14)
+#define KEYBOARD_LAMPS BIT(8)
+#define RADIO_LED_ON BIT(5)
+#define ECO_LED BIT(16)
+#define ECO_LED_ON BIT(19)
+
+/* FUNC interface - backlight power control */
+#define BACKLIGHT_PARAM_POWER BIT(2)
+#define BACKLIGHT_OFF (BIT(0) | BIT(1))
+#define BACKLIGHT_ON 0
+
+/* Scancodes read from the GIRB register */
+#define KEY1_CODE 0x410
+#define KEY2_CODE 0x411
+#define KEY3_CODE 0x412
+#define KEY4_CODE 0x413
+#define KEY5_CODE 0x420
+
+/* Hotkey ringbuffer limits */
+#define MAX_HOTKEY_RINGBUFFER_SIZE 100
+#define RINGBUFFERSIZE 40
+
+/* Module parameters */
+static int use_alt_lcd_levels = -1;
+static bool disable_brightness_adjust;
/* Device controlling the backlight and associated keys */
struct fujitsu_bl {
@@ -122,8 +132,6 @@ struct fujitsu_bl {
};
static struct fujitsu_bl *fujitsu_bl;
-static int use_alt_lcd_levels = -1;
-static bool disable_brightness_adjust;
/* Device used to access hotkeys and other features on the laptop */
struct fujitsu_laptop {
@@ -256,9 +264,11 @@ static int bl_update_status(struct backlight_device *b)
if (fext) {
if (b->props.power == FB_BLANK_POWERDOWN)
- call_fext_func(fext, FUNC_BACKLIGHT, 0x1, 0x4, 0x3);
+ call_fext_func(fext, FUNC_BACKLIGHT, 0x1,
+ BACKLIGHT_PARAM_POWER, BACKLIGHT_OFF);
else
- call_fext_func(fext, FUNC_BACKLIGHT, 0x1, 0x4, 0x0);
+ call_fext_func(fext, FUNC_BACKLIGHT, 0x1,
+ BACKLIGHT_PARAM_POWER, BACKLIGHT_ON);
}
return set_lcd_level(device, b->props.brightness);
@@ -385,7 +395,7 @@ static int fujitsu_backlight_register(struct acpi_device *device)
static int acpi_fujitsu_bl_add(struct acpi_device *device)
{
struct fujitsu_bl *priv;
- int error;
+ int ret;
if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
return -ENODEV;
@@ -399,10 +409,6 @@ static int acpi_fujitsu_bl_add(struct acpi_device *device)
strcpy(acpi_device_class(device), ACPI_FUJITSU_CLASS);
device->driver_data = priv;
- error = acpi_fujitsu_bl_input_setup(device);
- if (error)
- return error;
-
pr_info("ACPI: %s [%s]\n",
acpi_device_name(device), acpi_device_bid(device));
@@ -410,11 +416,11 @@ static int acpi_fujitsu_bl_add(struct acpi_device *device)
priv->max_brightness = FUJITSU_LCD_N_LEVELS;
get_lcd_level(device);
- error = fujitsu_backlight_register(device);
- if (error)
- return error;
+ ret = acpi_fujitsu_bl_input_setup(device);
+ if (ret)
+ return ret;
- return 0;
+ return fujitsu_backlight_register(device);
}
/* Brightness notify */
@@ -424,7 +430,7 @@ static void acpi_fujitsu_bl_notify(struct acpi_device *device, u32 event)
struct fujitsu_bl *priv = acpi_driver_data(device);
int oldb, newb;
- if (event != ACPI_FUJITSU_NOTIFY_CODE1) {
+ if (event != ACPI_FUJITSU_NOTIFY_CODE) {
acpi_handle_info(device->handle, "unsupported event [0x%x]\n",
event);
sparse_keymap_report_event(priv->input, -1, 1, true);
@@ -455,7 +461,9 @@ static const struct key_entry keymap_default[] = {
{ KE_KEY, KEY3_CODE, { KEY_PROG3 } },
{ KE_KEY, KEY4_CODE, { KEY_PROG4 } },
{ KE_KEY, KEY5_CODE, { KEY_RFKILL } },
+ { KE_KEY, BIT(5), { KEY_RFKILL } },
{ KE_KEY, BIT(26), { KEY_TOUCHPAD_TOGGLE } },
+ { KE_KEY, BIT(29), { KEY_MICMUTE } },
{ KE_END, 0 }
};
@@ -693,7 +701,7 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
{
struct fujitsu_laptop *priv = acpi_driver_data(device);
struct led_classdev *led;
- int result;
+ int ret;
if (call_fext_func(device,
FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) {
@@ -704,9 +712,9 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
led->name = "fujitsu::logolamp";
led->brightness_set_blocking = logolamp_set;
led->brightness_get = logolamp_get;
- result = devm_led_classdev_register(&device->dev, led);
- if (result)
- return result;
+ ret = devm_led_classdev_register(&device->dev, led);
+ if (ret)
+ return ret;
}
if ((call_fext_func(device,
@@ -719,9 +727,9 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
led->name = "fujitsu::kblamps";
led->brightness_set_blocking = kblamps_set;
led->brightness_get = kblamps_get;
- result = devm_led_classdev_register(&device->dev, led);
- if (result)
- return result;
+ ret = devm_led_classdev_register(&device->dev, led);
+ if (ret)
+ return ret;
}
/*
@@ -742,9 +750,9 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
led->brightness_set_blocking = radio_led_set;
led->brightness_get = radio_led_get;
led->default_trigger = "rfkill-any";
- result = devm_led_classdev_register(&device->dev, led);
- if (result)
- return result;
+ ret = devm_led_classdev_register(&device->dev, led);
+ if (ret)
+ return ret;
}
/* Support for eco led is not always signaled in bit corresponding
@@ -762,9 +770,9 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
led->name = "fujitsu::eco_led";
led->brightness_set_blocking = eco_led_set;
led->brightness_get = eco_led_get;
- result = devm_led_classdev_register(&device->dev, led);
- if (result)
- return result;
+ ret = devm_led_classdev_register(&device->dev, led);
+ if (ret)
+ return ret;
}
return 0;
@@ -773,8 +781,7 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
static int acpi_fujitsu_laptop_add(struct acpi_device *device)
{
struct fujitsu_laptop *priv;
- int error;
- int i;
+ int ret, i = 0;
priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -789,23 +796,16 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device)
/* kfifo */
spin_lock_init(&priv->fifo_lock);
- error = kfifo_alloc(&priv->fifo, RINGBUFFERSIZE * sizeof(int),
- GFP_KERNEL);
- if (error) {
- pr_err("kfifo_alloc failed\n");
- goto err_stop;
- }
-
- error = acpi_fujitsu_laptop_input_setup(device);
- if (error)
- goto err_free_fifo;
+ ret = kfifo_alloc(&priv->fifo, RINGBUFFERSIZE * sizeof(int),
+ GFP_KERNEL);
+ if (ret)
+ return ret;
pr_info("ACPI: %s [%s]\n",
acpi_device_name(device), acpi_device_bid(device));
- i = 0;
- while (call_fext_func(device, FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0
- && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE)
+ while (call_fext_func(device, FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0 &&
+ i++ < MAX_HOTKEY_RINGBUFFER_SIZE)
; /* No action, result is discarded */
acpi_handle_debug(device->handle, "Discarded %i ringbuffer entries\n",
i);
@@ -829,26 +829,31 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device)
/* Sync backlight power status */
if (fujitsu_bl && fujitsu_bl->bl_device &&
acpi_video_get_backlight_type() == acpi_backlight_vendor) {
- if (call_fext_func(fext, FUNC_BACKLIGHT, 0x2, 0x4, 0x0) == 3)
+ if (call_fext_func(fext, FUNC_BACKLIGHT, 0x2,
+ BACKLIGHT_PARAM_POWER, 0x0) == BACKLIGHT_OFF)
fujitsu_bl->bl_device->props.power = FB_BLANK_POWERDOWN;
else
fujitsu_bl->bl_device->props.power = FB_BLANK_UNBLANK;
}
- error = acpi_fujitsu_laptop_leds_register(device);
- if (error)
+ ret = acpi_fujitsu_laptop_input_setup(device);
+ if (ret)
+ goto err_free_fifo;
+
+ ret = acpi_fujitsu_laptop_leds_register(device);
+ if (ret)
goto err_free_fifo;
- error = fujitsu_laptop_platform_add(device);
- if (error)
+ ret = fujitsu_laptop_platform_add(device);
+ if (ret)
goto err_free_fifo;
return 0;
err_free_fifo:
kfifo_free(&priv->fifo);
-err_stop:
- return error;
+
+ return ret;
}
static int acpi_fujitsu_laptop_remove(struct acpi_device *device)
@@ -865,11 +870,11 @@ static int acpi_fujitsu_laptop_remove(struct acpi_device *device)
static void acpi_fujitsu_laptop_press(struct acpi_device *device, int scancode)
{
struct fujitsu_laptop *priv = acpi_driver_data(device);
- int status;
+ int ret;
- status = kfifo_in_locked(&priv->fifo, (unsigned char *)&scancode,
- sizeof(scancode), &priv->fifo_lock);
- if (status != sizeof(scancode)) {
+ ret = kfifo_in_locked(&priv->fifo, (unsigned char *)&scancode,
+ sizeof(scancode), &priv->fifo_lock);
+ if (ret != sizeof(scancode)) {
dev_info(&priv->input->dev, "Could not push scancode [0x%x]\n",
scancode);
return;
@@ -882,13 +887,12 @@ static void acpi_fujitsu_laptop_press(struct acpi_device *device, int scancode)
static void acpi_fujitsu_laptop_release(struct acpi_device *device)
{
struct fujitsu_laptop *priv = acpi_driver_data(device);
- int scancode, status;
+ int scancode, ret;
while (true) {
- status = kfifo_out_locked(&priv->fifo,
- (unsigned char *)&scancode,
- sizeof(scancode), &priv->fifo_lock);
- if (status != sizeof(scancode))
+ ret = kfifo_out_locked(&priv->fifo, (unsigned char *)&scancode,
+ sizeof(scancode), &priv->fifo_lock);
+ if (ret != sizeof(scancode))
return;
sparse_keymap_report_event(priv->input, scancode, 0, false);
dev_dbg(&priv->input->dev,
@@ -899,10 +903,10 @@ static void acpi_fujitsu_laptop_release(struct acpi_device *device)
static void acpi_fujitsu_laptop_notify(struct acpi_device *device, u32 event)
{
struct fujitsu_laptop *priv = acpi_driver_data(device);
- int scancode, i = 0;
+ int scancode, i = 0, ret;
unsigned int irb;
- if (event != ACPI_FUJITSU_NOTIFY_CODE1) {
+ if (event != ACPI_FUJITSU_NOTIFY_CODE) {
acpi_handle_info(device->handle, "Unsupported event [0x%x]\n",
event);
sparse_keymap_report_event(priv->input, -1, 1, true);
@@ -930,9 +934,18 @@ static void acpi_fujitsu_laptop_notify(struct acpi_device *device, u32 event)
* E736/E746/E756), the touchpad toggle hotkey (Fn+F4) is
* handled in software; its state is queried using FUNC_FLAGS
*/
- if ((priv->flags_supported & BIT(26)) &&
- (call_fext_func(device, FUNC_FLAGS, 0x1, 0x0, 0x0) & BIT(26)))
- sparse_keymap_report_event(priv->input, BIT(26), 1, true);
+ if (priv->flags_supported & (BIT(5) | BIT(26) | BIT(29))) {
+ ret = call_fext_func(device, FUNC_FLAGS, 0x1, 0x0, 0x0);
+ if (ret & BIT(5))
+ sparse_keymap_report_event(priv->input,
+ BIT(5), 1, true);
+ if (ret & BIT(26))
+ sparse_keymap_report_event(priv->input,
+ BIT(26), 1, true);
+ if (ret & BIT(29))
+ sparse_keymap_report_event(priv->input,
+ BIT(29), 1, true);
+ }
}
/* Initialization */
diff --git a/drivers/platform/x86/gpd-pocket-fan.c b/drivers/platform/x86/gpd-pocket-fan.c
index 2d645c505f81..be85ed966bf3 100644
--- a/drivers/platform/x86/gpd-pocket-fan.c
+++ b/drivers/platform/x86/gpd-pocket-fan.c
@@ -19,12 +19,12 @@
static int temp_limits[3] = { 55000, 60000, 65000 };
module_param_array(temp_limits, int, NULL, 0444);
MODULE_PARM_DESC(temp_limits,
- "Milli-celcius values above which the fan speed increases");
+ "Millicelsius values above which the fan speed increases");
static int hysteresis = 3000;
module_param(hysteresis, int, 0444);
MODULE_PARM_DESC(hysteresis,
- "Hysteresis in milli-celcius before lowering the fan speed");
+ "Hysteresis in millicelsius before lowering the fan speed");
static int speed_on_ac = 2;
module_param(speed_on_ac, int, 0444);
diff --git a/drivers/platform/x86/intel-hid.c b/drivers/platform/x86/intel-hid.c
index 5e3df194723e..b5adba227783 100644
--- a/drivers/platform/x86/intel-hid.c
+++ b/drivers/platform/x86/intel-hid.c
@@ -16,16 +16,14 @@
*
*/
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/input.h>
#include <linux/platform_device.h>
-#include <linux/input/sparse-keymap.h>
-#include <linux/acpi.h>
#include <linux/suspend.h>
-#include <acpi/acpi_bus.h>
-#include <linux/dmi.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alex Hung");
@@ -67,8 +65,8 @@ static const struct key_entry intel_array_keymap[] = {
{ KE_IGNORE, 0xC5, { KEY_VOLUMEUP } }, /* Release */
{ KE_KEY, 0xC6, { KEY_VOLUMEDOWN } }, /* Press */
{ KE_IGNORE, 0xC7, { KEY_VOLUMEDOWN } }, /* Release */
- { KE_SW, 0xC8, { .sw = { SW_ROTATE_LOCK, 1 } } }, /* Press */
- { KE_SW, 0xC9, { .sw = { SW_ROTATE_LOCK, 0 } } }, /* Release */
+ { KE_KEY, 0xC8, { KEY_ROTATE_LOCK_TOGGLE } }, /* Press */
+ { KE_IGNORE, 0xC9, { KEY_ROTATE_LOCK_TOGGLE } }, /* Release */
{ KE_KEY, 0xCE, { KEY_POWER } }, /* Press */
{ KE_IGNORE, 0xCF, { KEY_POWER } }, /* Release */
{ KE_END },
diff --git a/drivers/platform/x86/intel_cht_int33fe.c b/drivers/platform/x86/intel_cht_int33fe.c
index 380ef7ec094f..39d4100c60a2 100644
--- a/drivers/platform/x86/intel_cht_int33fe.c
+++ b/drivers/platform/x86/intel_cht_int33fe.c
@@ -33,6 +33,8 @@ struct cht_int33fe_data {
struct i2c_client *max17047;
struct i2c_client *fusb302;
struct i2c_client *pi3usb30532;
+ /* Contain a list-head must be per device */
+ struct device_connection connections[3];
};
/*
@@ -172,6 +174,20 @@ static int cht_int33fe_probe(struct i2c_client *client)
return -EPROBE_DEFER; /* Wait for i2c-adapter to load */
}
+ data->connections[0].endpoint[0] = "i2c-fusb302";
+ data->connections[0].endpoint[1] = "i2c-pi3usb30532";
+ data->connections[0].id = "typec-switch";
+ data->connections[1].endpoint[0] = "i2c-fusb302";
+ data->connections[1].endpoint[1] = "i2c-pi3usb30532";
+ data->connections[1].id = "typec-mux";
+ data->connections[2].endpoint[0] = "i2c-fusb302";
+ data->connections[2].endpoint[1] = "intel_xhci_usb_sw-role-switch";
+ data->connections[2].id = "usb-role-switch";
+
+ device_connection_add(&data->connections[0]);
+ device_connection_add(&data->connections[1]);
+ device_connection_add(&data->connections[2]);
+
memset(&board_info, 0, sizeof(board_info));
strlcpy(board_info.type, "typec_fusb302", I2C_NAME_SIZE);
board_info.dev_name = "fusb302";
@@ -201,6 +217,10 @@ out_unregister_max17047:
if (data->max17047)
i2c_unregister_device(data->max17047);
+ device_connection_remove(&data->connections[2]);
+ device_connection_remove(&data->connections[1]);
+ device_connection_remove(&data->connections[0]);
+
return -EPROBE_DEFER; /* Wait for the i2c-adapter to load */
}
@@ -213,6 +233,10 @@ static int cht_int33fe_remove(struct i2c_client *i2c)
if (data->max17047)
i2c_unregister_device(data->max17047);
+ device_connection_remove(&data->connections[2]);
+ device_connection_remove(&data->connections[1]);
+ device_connection_remove(&data->connections[0]);
+
return 0;
}
diff --git a/drivers/platform/x86/intel_turbo_max_3.c b/drivers/platform/x86/intel_turbo_max_3.c
index d4ea01805879..a6d5aa0c3c47 100644
--- a/drivers/platform/x86/intel_turbo_max_3.c
+++ b/drivers/platform/x86/intel_turbo_max_3.c
@@ -138,9 +138,6 @@ static int __init itmt_legacy_init(void)
if (!id)
return -ENODEV;
- if (boot_cpu_has(X86_FEATURE_HWP))
- return -ENODEV;
-
ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
"platform/x86/turbo_max_3:online",
itmt_legacy_cpu_online, NULL);
diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c
index 454e14f02285..7a0bd24c1ae2 100644
--- a/drivers/platform/x86/mlx-platform.c
+++ b/drivers/platform/x86/mlx-platform.c
@@ -85,6 +85,15 @@
#define MLXPLAT_CPLD_FAN_MASK GENMASK(3, 0)
#define MLXPLAT_CPLD_FAN_NG_MASK GENMASK(5, 0)
+/* Default I2C parent bus number */
+#define MLXPLAT_CPLD_PHYS_ADAPTER_DEF_NR 1
+
+/* Maximum number of possible physical buses equipped on system */
+#define MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM 16
+
+/* Number of channels in group */
+#define MLXPLAT_CPLD_GRP_CHNL_NUM 8
+
/* Start channel numbers */
#define MLXPLAT_CPLD_CH1 2
#define MLXPLAT_CPLD_CH2 10
@@ -124,7 +133,7 @@ static const struct resource mlxplat_lpc_resources[] = {
};
/* Platform default channels */
-static const int mlxplat_default_channels[][8] = {
+static const int mlxplat_default_channels[][MLXPLAT_CPLD_GRP_CHNL_NUM] = {
{
MLXPLAT_CPLD_CH1, MLXPLAT_CPLD_CH1 + 1, MLXPLAT_CPLD_CH1 + 2,
MLXPLAT_CPLD_CH1 + 3, MLXPLAT_CPLD_CH1 + 4, MLXPLAT_CPLD_CH1 +
@@ -694,6 +703,8 @@ static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi)
ARRAY_SIZE(mlxplat_default_channels[i]);
}
mlxplat_hotplug = &mlxplat_mlxcpld_default_data;
+ mlxplat_hotplug->deferred_nr =
+ mlxplat_default_channels[i - 1][MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
return 1;
};
@@ -708,6 +719,8 @@ static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi)
ARRAY_SIZE(mlxplat_msn21xx_channels);
}
mlxplat_hotplug = &mlxplat_mlxcpld_msn21xx_data;
+ mlxplat_hotplug->deferred_nr =
+ mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
return 1;
};
@@ -722,6 +735,8 @@ static int __init mlxplat_dmi_msn274x_matched(const struct dmi_system_id *dmi)
ARRAY_SIZE(mlxplat_msn21xx_channels);
}
mlxplat_hotplug = &mlxplat_mlxcpld_msn274x_data;
+ mlxplat_hotplug->deferred_nr =
+ mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
return 1;
};
@@ -736,6 +751,8 @@ static int __init mlxplat_dmi_msn201x_matched(const struct dmi_system_id *dmi)
ARRAY_SIZE(mlxplat_msn21xx_channels);
}
mlxplat_hotplug = &mlxplat_mlxcpld_msn201x_data;
+ mlxplat_hotplug->deferred_nr =
+ mlxplat_default_channels[i - 1][MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
return 1;
};
@@ -750,6 +767,8 @@ static int __init mlxplat_dmi_qmb7xx_matched(const struct dmi_system_id *dmi)
ARRAY_SIZE(mlxplat_msn21xx_channels);
}
mlxplat_hotplug = &mlxplat_mlxcpld_default_ng_data;
+ mlxplat_hotplug->deferred_nr =
+ mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
return 1;
};
@@ -830,10 +849,48 @@ static const struct dmi_system_id mlxplat_dmi_table[] __initconst = {
MODULE_DEVICE_TABLE(dmi, mlxplat_dmi_table);
+static int mlxplat_mlxcpld_verify_bus_topology(int *nr)
+{
+ struct i2c_adapter *search_adap;
+ int shift, i;
+
+ /* Scan adapters from expected id to verify it is free. */
+ *nr = MLXPLAT_CPLD_PHYS_ADAPTER_DEF_NR;
+ for (i = MLXPLAT_CPLD_PHYS_ADAPTER_DEF_NR; i <
+ MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM; i++) {
+ search_adap = i2c_get_adapter(i);
+ if (search_adap) {
+ i2c_put_adapter(search_adap);
+ continue;
+ }
+
+ /* Return if expected parent adapter is free. */
+ if (i == MLXPLAT_CPLD_PHYS_ADAPTER_DEF_NR)
+ return 0;
+ break;
+ }
+
+ /* Return with error if free id for adapter is not found. */
+ if (i == MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM)
+ return -ENODEV;
+
+ /* Shift adapter ids, since expected parent adapter is not free. */
+ *nr = i;
+ for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
+ shift = *nr - mlxplat_mux_data[i].parent;
+ mlxplat_mux_data[i].parent = *nr;
+ mlxplat_mux_data[i].base_nr += shift;
+ if (shift > 0)
+ mlxplat_hotplug->shift_nr = shift;
+ }
+
+ return 0;
+}
+
static int __init mlxplat_init(void)
{
struct mlxplat_priv *priv;
- int i, err;
+ int i, nr, err;
if (!dmi_check_system(mlxplat_dmi_table))
return -ENODEV;
@@ -853,7 +910,12 @@ static int __init mlxplat_init(void)
}
platform_set_drvdata(mlxplat_dev, priv);
- priv->pdev_i2c = platform_device_register_simple("i2c_mlxcpld", -1,
+ err = mlxplat_mlxcpld_verify_bus_topology(&nr);
+ if (nr < 0)
+ goto fail_alloc;
+
+ nr = (nr == MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM) ? -1 : nr;
+ priv->pdev_i2c = platform_device_register_simple("i2c_mlxcpld", nr,
NULL, 0);
if (IS_ERR(priv->pdev_i2c)) {
err = PTR_ERR(priv->pdev_i2c);
diff --git a/drivers/platform/x86/silead_dmi.c b/drivers/platform/x86/silead_dmi.c
index 3a624090191d..452aacabaa8e 100644
--- a/drivers/platform/x86/silead_dmi.c
+++ b/drivers/platform/x86/silead_dmi.c
@@ -446,6 +446,23 @@ static const struct dmi_system_id silead_ts_dmi_table[] = {
DMI_MATCH(DMI_BOARD_NAME, "X3 Plus"),
},
},
+ {
+ /* I.T.Works TW701 */
+ .driver_data = (void *)&surftab_wintron70_st70416_6_data,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "i71c"),
+ DMI_MATCH(DMI_BIOS_VERSION, "itWORKS.G.WI71C.JGBMRB"),
+ },
+ },
+ {
+ /* Yours Y8W81, same case and touchscreen as Chuwi Vi8 */
+ .driver_data = (void *)&chuwi_vi8_data,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "YOURS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Y8W81"),
+ },
+ },
{ },
};
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index d5eaf3b1edba..da1ca4856ea1 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -23,7 +23,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#define TPACPI_VERSION "0.25"
+#define TPACPI_VERSION "0.26"
#define TPACPI_SYSFS_VERSION 0x030000
/*
@@ -66,6 +66,7 @@
#include <linux/seq_file.h>
#include <linux/sysfs.h>
#include <linux/backlight.h>
+#include <linux/bitops.h>
#include <linux/fb.h>
#include <linux/platform_device.h>
#include <linux/hwmon.h>
@@ -78,11 +79,13 @@
#include <linux/workqueue.h>
#include <linux/acpi.h>
#include <linux/pci_ids.h>
+#include <linux/power_supply.h>
#include <linux/thinkpad_acpi.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/initval.h>
#include <linux/uaccess.h>
+#include <acpi/battery.h>
#include <acpi/video.h>
/* ThinkPad CMOS commands */
@@ -335,6 +338,7 @@ static struct {
u32 sensors_pdev_attrs_registered:1;
u32 hotkey_poll_active:1;
u32 has_adaptive_kbd:1;
+ u32 battery:1;
} tp_features;
static struct {
@@ -8699,16 +8703,24 @@ static const struct attribute_group fan_attr_group = {
.ec = TPID(__id1, __id2), \
.quirks = __quirks }
+#define TPACPI_FAN_QB(__id1, __id2, __quirks) \
+ { .vendor = PCI_VENDOR_ID_LENOVO, \
+ .bios = TPID(__id1, __id2), \
+ .ec = TPACPI_MATCH_ANY, \
+ .quirks = __quirks }
+
static const struct tpacpi_quirk fan_quirk_table[] __initconst = {
TPACPI_FAN_QI('1', 'Y', TPACPI_FAN_Q1),
TPACPI_FAN_QI('7', '8', TPACPI_FAN_Q1),
TPACPI_FAN_QI('7', '6', TPACPI_FAN_Q1),
TPACPI_FAN_QI('7', '0', TPACPI_FAN_Q1),
TPACPI_FAN_QL('7', 'M', TPACPI_FAN_2FAN),
+ TPACPI_FAN_QB('N', '1', TPACPI_FAN_2FAN),
};
#undef TPACPI_FAN_QL
#undef TPACPI_FAN_QI
+#undef TPACPI_FAN_QB
static int __init fan_init(struct ibm_init_struct *iibm)
{
@@ -9209,6 +9221,385 @@ static struct ibm_struct mute_led_driver_data = {
.resume = mute_led_resume,
};
+/*
+ * Battery Wear Control Driver
+ * Contact: Ognjen Galic <smclt30p@gmail.com>
+ */
+
+/* Metadata */
+
+#define GET_START "BCTG"
+#define SET_START "BCCS"
+#define GET_STOP "BCSG"
+#define SET_STOP "BCSS"
+
+#define START_ATTR "charge_start_threshold"
+#define STOP_ATTR "charge_stop_threshold"
+
+enum {
+ BAT_ANY = 0,
+ BAT_PRIMARY = 1,
+ BAT_SECONDARY = 2
+};
+
+enum {
+ /* Error condition bit */
+ METHOD_ERR = BIT(31),
+};
+
+enum {
+ /* This is used in the get/set helpers */
+ THRESHOLD_START,
+ THRESHOLD_STOP,
+};
+
+struct tpacpi_battery_data {
+ int charge_start;
+ int start_support;
+ int charge_stop;
+ int stop_support;
+};
+
+struct tpacpi_battery_driver_data {
+ struct tpacpi_battery_data batteries[3];
+ int individual_addressing;
+};
+
+static struct tpacpi_battery_driver_data battery_info;
+
+/* ACPI helpers/functions/probes */
+
+/**
+ * This evaluates a ACPI method call specific to the battery
+ * ACPI extension. The specifics are that an error is marked
+ * in the 32rd bit of the response, so we just check that here.
+ */
+static acpi_status tpacpi_battery_acpi_eval(char *method, int *ret, int param)
+{
+ int response;
+
+ if (!acpi_evalf(hkey_handle, &response, method, "dd", param)) {
+ acpi_handle_err(hkey_handle, "%s: evaluate failed", method);
+ return AE_ERROR;
+ }
+ if (response & METHOD_ERR) {
+ acpi_handle_err(hkey_handle,
+ "%s evaluated but flagged as error", method);
+ return AE_ERROR;
+ }
+ *ret = response;
+ return AE_OK;
+}
+
+static int tpacpi_battery_get(int what, int battery, int *ret)
+{
+ switch (what) {
+ case THRESHOLD_START:
+ if ACPI_FAILURE(tpacpi_battery_acpi_eval(GET_START, ret, battery))
+ return -ENODEV;
+
+ /* The value is in the low 8 bits of the response */
+ *ret = *ret & 0xFF;
+ return 0;
+ case THRESHOLD_STOP:
+ if ACPI_FAILURE(tpacpi_battery_acpi_eval(GET_STOP, ret, battery))
+ return -ENODEV;
+ /* Value is in lower 8 bits */
+ *ret = *ret & 0xFF;
+ /*
+ * On the stop value, if we return 0 that
+ * does not make any sense. 0 means Default, which
+ * means that charging stops at 100%, so we return
+ * that.
+ */
+ if (*ret == 0)
+ *ret = 100;
+ return 0;
+ default:
+ pr_crit("wrong parameter: %d", what);
+ return -EINVAL;
+ }
+}
+
+static int tpacpi_battery_set(int what, int battery, int value)
+{
+ int param, ret;
+ /* The first 8 bits are the value of the threshold */
+ param = value;
+ /* The battery ID is in bits 8-9, 2 bits */
+ param |= battery << 8;
+
+ switch (what) {
+ case THRESHOLD_START:
+ if ACPI_FAILURE(tpacpi_battery_acpi_eval(SET_START, &ret, param)) {
+ pr_err("failed to set charge threshold on battery %d",
+ battery);
+ return -ENODEV;
+ }
+ return 0;
+ case THRESHOLD_STOP:
+ if ACPI_FAILURE(tpacpi_battery_acpi_eval(SET_STOP, &ret, param)) {
+ pr_err("failed to set stop threshold: %d", battery);
+ return -ENODEV;
+ }
+ return 0;
+ default:
+ pr_crit("wrong parameter: %d", what);
+ return -EINVAL;
+ }
+}
+
+static int tpacpi_battery_probe(int battery)
+{
+ int ret = 0;
+
+ memset(&battery_info, 0, sizeof(struct tpacpi_battery_driver_data));
+ /*
+ * 1) Get the current start threshold
+ * 2) Check for support
+ * 3) Get the current stop threshold
+ * 4) Check for support
+ */
+ if (acpi_has_method(hkey_handle, GET_START)) {
+ if ACPI_FAILURE(tpacpi_battery_acpi_eval(GET_START, &ret, battery)) {
+ pr_err("Error probing battery %d\n", battery);
+ return -ENODEV;
+ }
+ /* Individual addressing is in bit 9 */
+ if (ret & BIT(9))
+ battery_info.individual_addressing = true;
+ /* Support is marked in bit 8 */
+ if (ret & BIT(8))
+ battery_info.batteries[battery].start_support = 1;
+ else
+ return -ENODEV;
+ if (tpacpi_battery_get(THRESHOLD_START, battery,
+ &battery_info.batteries[battery].charge_start)) {
+ pr_err("Error probing battery %d\n", battery);
+ return -ENODEV;
+ }
+ }
+ if (acpi_has_method(hkey_handle, GET_STOP)) {
+ if ACPI_FAILURE(tpacpi_battery_acpi_eval(GET_STOP, &ret, battery)) {
+ pr_err("Error probing battery stop; %d\n", battery);
+ return -ENODEV;
+ }
+ /* Support is marked in bit 8 */
+ if (ret & BIT(8))
+ battery_info.batteries[battery].stop_support = 1;
+ else
+ return -ENODEV;
+ if (tpacpi_battery_get(THRESHOLD_STOP, battery,
+ &battery_info.batteries[battery].charge_stop)) {
+ pr_err("Error probing battery stop: %d\n", battery);
+ return -ENODEV;
+ }
+ }
+ pr_info("battery %d registered (start %d, stop %d)",
+ battery,
+ battery_info.batteries[battery].charge_start,
+ battery_info.batteries[battery].charge_stop);
+
+ return 0;
+}
+
+/* General helper functions */
+
+static int tpacpi_battery_get_id(const char *battery_name)
+{
+
+ if (strcmp(battery_name, "BAT0") == 0)
+ return BAT_PRIMARY;
+ if (strcmp(battery_name, "BAT1") == 0)
+ return BAT_SECONDARY;
+ /*
+ * If for some reason the battery is not BAT0 nor is it
+ * BAT1, we will assume it's the default, first battery,
+ * AKA primary.
+ */
+ pr_warn("unknown battery %s, assuming primary", battery_name);
+ return BAT_PRIMARY;
+}
+
+/* sysfs interface */
+
+static ssize_t tpacpi_battery_store(int what,
+ struct device *dev,
+ const char *buf, size_t count)
+{
+ struct power_supply *supply = to_power_supply(dev);
+ unsigned long value;
+ int battery, rval;
+ /*
+ * Some systems have support for more than
+ * one battery. If that is the case,
+ * tpacpi_battery_probe marked that addressing
+ * them individually is supported, so we do that
+ * based on the device struct.
+ *
+ * On systems that are not supported, we assume
+ * the primary as most of the ACPI calls fail
+ * with "Any Battery" as the parameter.
+ */
+ if (battery_info.individual_addressing)
+ /* BAT_PRIMARY or BAT_SECONDARY */
+ battery = tpacpi_battery_get_id(supply->desc->name);
+ else
+ battery = BAT_PRIMARY;
+
+ rval = kstrtoul(buf, 10, &value);
+ if (rval)
+ return rval;
+
+ switch (what) {
+ case THRESHOLD_START:
+ if (!battery_info.batteries[battery].start_support)
+ return -ENODEV;
+ /* valid values are [0, 99] */
+ if (value < 0 || value > 99)
+ return -EINVAL;
+ if (value > battery_info.batteries[battery].charge_stop)
+ return -EINVAL;
+ if (tpacpi_battery_set(THRESHOLD_START, battery, value))
+ return -ENODEV;
+ battery_info.batteries[battery].charge_start = value;
+ return count;
+
+ case THRESHOLD_STOP:
+ if (!battery_info.batteries[battery].stop_support)
+ return -ENODEV;
+ /* valid values are [1, 100] */
+ if (value < 1 || value > 100)
+ return -EINVAL;
+ if (value < battery_info.batteries[battery].charge_start)
+ return -EINVAL;
+ battery_info.batteries[battery].charge_stop = value;
+ /*
+ * When 100 is passed to stop, we need to flip
+ * it to 0 as that the EC understands that as
+ * "Default", which will charge to 100%
+ */
+ if (value == 100)
+ value = 0;
+ if (tpacpi_battery_set(THRESHOLD_STOP, battery, value))
+ return -EINVAL;
+ return count;
+ default:
+ pr_crit("Wrong parameter: %d", what);
+ return -EINVAL;
+ }
+ return count;
+}
+
+static ssize_t tpacpi_battery_show(int what,
+ struct device *dev,
+ char *buf)
+{
+ struct power_supply *supply = to_power_supply(dev);
+ int ret, battery;
+ /*
+ * Some systems have support for more than
+ * one battery. If that is the case,
+ * tpacpi_battery_probe marked that addressing
+ * them individually is supported, so we;
+ * based on the device struct.
+ *
+ * On systems that are not supported, we assume
+ * the primary as most of the ACPI calls fail
+ * with "Any Battery" as the parameter.
+ */
+ if (battery_info.individual_addressing)
+ /* BAT_PRIMARY or BAT_SECONDARY */
+ battery = tpacpi_battery_get_id(supply->desc->name);
+ else
+ battery = BAT_PRIMARY;
+ if (tpacpi_battery_get(what, battery, &ret))
+ return -ENODEV;
+ return sprintf(buf, "%d\n", ret);
+}
+
+static ssize_t charge_start_threshold_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return tpacpi_battery_show(THRESHOLD_START, device, buf);
+}
+
+static ssize_t charge_stop_threshold_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return tpacpi_battery_show(THRESHOLD_STOP, device, buf);
+}
+
+static ssize_t charge_start_threshold_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return tpacpi_battery_store(THRESHOLD_START, dev, buf, count);
+}
+
+static ssize_t charge_stop_threshold_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return tpacpi_battery_store(THRESHOLD_STOP, dev, buf, count);
+}
+
+static DEVICE_ATTR_RW(charge_start_threshold);
+static DEVICE_ATTR_RW(charge_stop_threshold);
+
+static struct attribute *tpacpi_battery_attrs[] = {
+ &dev_attr_charge_start_threshold.attr,
+ &dev_attr_charge_stop_threshold.attr,
+ NULL,
+};
+
+ATTRIBUTE_GROUPS(tpacpi_battery);
+
+/* ACPI battery hooking */
+
+static int tpacpi_battery_add(struct power_supply *battery)
+{
+ int batteryid = tpacpi_battery_get_id(battery->desc->name);
+
+ if (tpacpi_battery_probe(batteryid))
+ return -ENODEV;
+ if (device_add_groups(&battery->dev, tpacpi_battery_groups))
+ return -ENODEV;
+ return 0;
+}
+
+static int tpacpi_battery_remove(struct power_supply *battery)
+{
+ device_remove_groups(&battery->dev, tpacpi_battery_groups);
+ return 0;
+}
+
+static struct acpi_battery_hook battery_hook = {
+ .add_battery = tpacpi_battery_add,
+ .remove_battery = tpacpi_battery_remove,
+ .name = "ThinkPad Battery Extension",
+};
+
+/* Subdriver init/exit */
+
+static int __init tpacpi_battery_init(struct ibm_init_struct *ibm)
+{
+ battery_hook_register(&battery_hook);
+ return 0;
+}
+
+static void tpacpi_battery_exit(void)
+{
+ battery_hook_unregister(&battery_hook);
+}
+
+static struct ibm_struct battery_driver_data = {
+ .name = "battery",
+ .exit = tpacpi_battery_exit,
+};
+
/****************************************************************************
****************************************************************************
*
@@ -9655,6 +10046,10 @@ static struct ibm_init_struct ibms_init[] __initdata = {
.init = mute_led_init,
.data = &mute_led_driver_data,
},
+ {
+ .init = tpacpi_battery_init,
+ .data = &battery_driver_data,
+ },
};
static int __init set_ibm_param(const char *val, const struct kernel_param *kp)
diff --git a/drivers/platform/x86/topstar-laptop.c b/drivers/platform/x86/topstar-laptop.c
index 1032c00b907b..f7761d98c0fd 100644
--- a/drivers/platform/x86/topstar-laptop.c
+++ b/drivers/platform/x86/topstar-laptop.c
@@ -1,14 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
/*
- * ACPI driver for Topstar notebooks (hotkeys support only)
+ * Topstar Laptop ACPI Extras driver
*
* Copyright (c) 2009 Herton Ronaldo Krzesinski <herton@mandriva.com.br>
+ * Copyright (c) 2018 Guillaume Douézan-Grard
*
* Implementation inspired by existing x86 platform drivers, in special
- * asus/eepc/fujitsu-laptop, thanks to their authors
- *
- * 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.
+ * asus/eepc/fujitsu-laptop, thanks to their authors.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -18,15 +16,93 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/acpi.h>
+#include <linux/dmi.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
+#include <linux/leds.h>
+#include <linux/platform_device.h>
-#define ACPI_TOPSTAR_CLASS "topstar"
+#define TOPSTAR_LAPTOP_CLASS "topstar"
-struct topstar_hkey {
- struct input_dev *inputdev;
+struct topstar_laptop {
+ struct acpi_device *device;
+ struct platform_device *platform;
+ struct input_dev *input;
+ struct led_classdev led;
};
+/*
+ * LED
+ */
+
+static enum led_brightness topstar_led_get(struct led_classdev *led)
+{
+ return led->brightness;
+}
+
+static int topstar_led_set(struct led_classdev *led,
+ enum led_brightness state)
+{
+ struct topstar_laptop *topstar = container_of(led,
+ struct topstar_laptop, led);
+
+ struct acpi_object_list params;
+ union acpi_object in_obj;
+ unsigned long long int ret;
+ acpi_status status;
+
+ params.count = 1;
+ params.pointer = &in_obj;
+ in_obj.type = ACPI_TYPE_INTEGER;
+ in_obj.integer.value = 0x83;
+
+ /*
+ * Topstar ACPI returns 0x30001 when the LED is ON and 0x30000 when it
+ * is OFF.
+ */
+ status = acpi_evaluate_integer(topstar->device->handle,
+ "GETX", &params, &ret);
+ if (ACPI_FAILURE(status))
+ return -1;
+
+ /*
+ * FNCX(0x83) toggles the LED (more precisely, it is supposed to
+ * act as an hardware switch and disconnect the WLAN adapter but
+ * it seems to be faulty on some models like the Topstar U931
+ * Notebook).
+ */
+ if ((ret == 0x30001 && state == LED_OFF)
+ || (ret == 0x30000 && state != LED_OFF)) {
+ status = acpi_execute_simple_method(topstar->device->handle,
+ "FNCX", 0x83);
+ if (ACPI_FAILURE(status))
+ return -1;
+ }
+
+ return 0;
+}
+
+static int topstar_led_init(struct topstar_laptop *topstar)
+{
+ topstar->led = (struct led_classdev) {
+ .default_trigger = "rfkill0",
+ .brightness_get = topstar_led_get,
+ .brightness_set_blocking = topstar_led_set,
+ .name = TOPSTAR_LAPTOP_CLASS "::wlan",
+ };
+
+ return led_classdev_register(&topstar->platform->dev, &topstar->led);
+}
+
+static void topstar_led_exit(struct topstar_laptop *topstar)
+{
+ led_classdev_unregister(&topstar->led);
+}
+
+/*
+ * Input
+ */
+
static const struct key_entry topstar_keymap[] = {
{ KE_KEY, 0x80, { KEY_BRIGHTNESSUP } },
{ KE_KEY, 0x81, { KEY_BRIGHTNESSDOWN } },
@@ -57,107 +133,217 @@ static const struct key_entry topstar_keymap[] = {
{ KE_END, 0 }
};
-static void acpi_topstar_notify(struct acpi_device *device, u32 event)
+static void topstar_input_notify(struct topstar_laptop *topstar, int event)
{
- static bool dup_evnt[2];
- bool *dup;
- struct topstar_hkey *hkey = acpi_driver_data(device);
-
- /* 0x83 and 0x84 key events comes duplicated... */
- if (event == 0x83 || event == 0x84) {
- dup = &dup_evnt[event - 0x83];
- if (*dup) {
- *dup = false;
- return;
- }
- *dup = true;
- }
-
- if (!sparse_keymap_report_event(hkey->inputdev, event, 1, true))
+ if (!sparse_keymap_report_event(topstar->input, event, 1, true))
pr_info("unknown event = 0x%02x\n", event);
}
-static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state)
-{
- acpi_status status;
-
- status = acpi_execute_simple_method(device->handle, "FNCX",
- state ? 0x86 : 0x87);
- if (ACPI_FAILURE(status)) {
- pr_err("Unable to switch FNCX notifications\n");
- return -ENODEV;
- }
-
- return 0;
-}
-
-static int acpi_topstar_init_hkey(struct topstar_hkey *hkey)
+static int topstar_input_init(struct topstar_laptop *topstar)
{
struct input_dev *input;
- int error;
+ int err;
input = input_allocate_device();
if (!input)
return -ENOMEM;
input->name = "Topstar Laptop extra buttons";
- input->phys = "topstar/input0";
+ input->phys = TOPSTAR_LAPTOP_CLASS "/input0";
input->id.bustype = BUS_HOST;
+ input->dev.parent = &topstar->platform->dev;
- error = sparse_keymap_setup(input, topstar_keymap, NULL);
- if (error) {
+ err = sparse_keymap_setup(input, topstar_keymap, NULL);
+ if (err) {
pr_err("Unable to setup input device keymap\n");
goto err_free_dev;
}
- error = input_register_device(input);
- if (error) {
+ err = input_register_device(input);
+ if (err) {
pr_err("Unable to register input device\n");
goto err_free_dev;
}
- hkey->inputdev = input;
+ topstar->input = input;
return 0;
- err_free_dev:
+err_free_dev:
input_free_device(input);
- return error;
+ return err;
}
-static int acpi_topstar_add(struct acpi_device *device)
+static void topstar_input_exit(struct topstar_laptop *topstar)
{
- struct topstar_hkey *tps_hkey;
+ input_unregister_device(topstar->input);
+}
- tps_hkey = kzalloc(sizeof(struct topstar_hkey), GFP_KERNEL);
- if (!tps_hkey)
+/*
+ * Platform
+ */
+
+static struct platform_driver topstar_platform_driver = {
+ .driver = {
+ .name = TOPSTAR_LAPTOP_CLASS,
+ },
+};
+
+static int topstar_platform_init(struct topstar_laptop *topstar)
+{
+ int err;
+
+ topstar->platform = platform_device_alloc(TOPSTAR_LAPTOP_CLASS, -1);
+ if (!topstar->platform)
return -ENOMEM;
- strcpy(acpi_device_name(device), "Topstar TPSACPI");
- strcpy(acpi_device_class(device), ACPI_TOPSTAR_CLASS);
+ platform_set_drvdata(topstar->platform, topstar);
+
+ err = platform_device_add(topstar->platform);
+ if (err)
+ goto err_device_put;
+
+ return 0;
- if (acpi_topstar_fncx_switch(device, true))
- goto add_err;
+err_device_put:
+ platform_device_put(topstar->platform);
+ return err;
+}
+
+static void topstar_platform_exit(struct topstar_laptop *topstar)
+{
+ platform_device_unregister(topstar->platform);
+}
+
+/*
+ * ACPI
+ */
+
+static int topstar_acpi_fncx_switch(struct acpi_device *device, bool state)
+{
+ acpi_status status;
+ u64 arg = state ? 0x86 : 0x87;
- if (acpi_topstar_init_hkey(tps_hkey))
- goto add_err;
+ status = acpi_execute_simple_method(device->handle, "FNCX", arg);
+ if (ACPI_FAILURE(status)) {
+ pr_err("Unable to switch FNCX notifications\n");
+ return -ENODEV;
+ }
- device->driver_data = tps_hkey;
return 0;
+}
-add_err:
- kfree(tps_hkey);
- return -ENODEV;
+static void topstar_acpi_notify(struct acpi_device *device, u32 event)
+{
+ struct topstar_laptop *topstar = acpi_driver_data(device);
+ static bool dup_evnt[2];
+ bool *dup;
+
+ /* 0x83 and 0x84 key events comes duplicated... */
+ if (event == 0x83 || event == 0x84) {
+ dup = &dup_evnt[event - 0x83];
+ if (*dup) {
+ *dup = false;
+ return;
+ }
+ *dup = true;
+ }
+
+ topstar_input_notify(topstar, event);
}
-static int acpi_topstar_remove(struct acpi_device *device)
+static int topstar_acpi_init(struct topstar_laptop *topstar)
{
- struct topstar_hkey *tps_hkey = acpi_driver_data(device);
+ return topstar_acpi_fncx_switch(topstar->device, true);
+}
- acpi_topstar_fncx_switch(device, false);
+static void topstar_acpi_exit(struct topstar_laptop *topstar)
+{
+ topstar_acpi_fncx_switch(topstar->device, false);
+}
- input_unregister_device(tps_hkey->inputdev);
- kfree(tps_hkey);
+/*
+ * Enable software-based WLAN LED control on systems with defective
+ * hardware switch.
+ */
+static bool led_workaround;
+static int dmi_led_workaround(const struct dmi_system_id *id)
+{
+ led_workaround = true;
+ return 0;
+}
+
+static const struct dmi_system_id topstar_dmi_ids[] = {
+ {
+ .callback = dmi_led_workaround,
+ .ident = "Topstar U931/RVP7",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "U931"),
+ DMI_MATCH(DMI_BOARD_VERSION, "RVP7"),
+ },
+ },
+ {}
+};
+
+static int topstar_acpi_add(struct acpi_device *device)
+{
+ struct topstar_laptop *topstar;
+ int err;
+
+ dmi_check_system(topstar_dmi_ids);
+
+ topstar = kzalloc(sizeof(struct topstar_laptop), GFP_KERNEL);
+ if (!topstar)
+ return -ENOMEM;
+
+ strcpy(acpi_device_name(device), "Topstar TPSACPI");
+ strcpy(acpi_device_class(device), TOPSTAR_LAPTOP_CLASS);
+ device->driver_data = topstar;
+ topstar->device = device;
+
+ err = topstar_acpi_init(topstar);
+ if (err)
+ goto err_free;
+
+ err = topstar_platform_init(topstar);
+ if (err)
+ goto err_acpi_exit;
+
+ err = topstar_input_init(topstar);
+ if (err)
+ goto err_platform_exit;
+
+ if (led_workaround) {
+ err = topstar_led_init(topstar);
+ if (err)
+ goto err_input_exit;
+ }
+
+ return 0;
+
+err_input_exit:
+ topstar_input_exit(topstar);
+err_platform_exit:
+ topstar_platform_exit(topstar);
+err_acpi_exit:
+ topstar_acpi_exit(topstar);
+err_free:
+ kfree(topstar);
+ return err;
+}
+
+static int topstar_acpi_remove(struct acpi_device *device)
+{
+ struct topstar_laptop *topstar = acpi_driver_data(device);
+
+ if (led_workaround)
+ topstar_led_exit(topstar);
+
+ topstar_input_exit(topstar);
+ topstar_platform_exit(topstar);
+ topstar_acpi_exit(topstar);
+
+ kfree(topstar);
return 0;
}
@@ -168,18 +354,47 @@ static const struct acpi_device_id topstar_device_ids[] = {
};
MODULE_DEVICE_TABLE(acpi, topstar_device_ids);
-static struct acpi_driver acpi_topstar_driver = {
+static struct acpi_driver topstar_acpi_driver = {
.name = "Topstar laptop ACPI driver",
- .class = ACPI_TOPSTAR_CLASS,
+ .class = TOPSTAR_LAPTOP_CLASS,
.ids = topstar_device_ids,
.ops = {
- .add = acpi_topstar_add,
- .remove = acpi_topstar_remove,
- .notify = acpi_topstar_notify,
+ .add = topstar_acpi_add,
+ .remove = topstar_acpi_remove,
+ .notify = topstar_acpi_notify,
},
};
-module_acpi_driver(acpi_topstar_driver);
+
+static int __init topstar_laptop_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&topstar_platform_driver);
+ if (ret < 0)
+ return ret;
+
+ ret = acpi_bus_register_driver(&topstar_acpi_driver);
+ if (ret < 0)
+ goto err_driver_unreg;
+
+ pr_info("ACPI extras driver loaded\n");
+ return 0;
+
+err_driver_unreg:
+ platform_driver_unregister(&topstar_platform_driver);
+ return ret;
+}
+
+static void __exit topstar_laptop_exit(void)
+{
+ acpi_bus_unregister_driver(&topstar_acpi_driver);
+ platform_driver_unregister(&topstar_platform_driver);
+}
+
+module_init(topstar_laptop_init);
+module_exit(topstar_laptop_exit);
MODULE_AUTHOR("Herton Ronaldo Krzesinski");
+MODULE_AUTHOR("Guillaume Douézan-Grard");
MODULE_DESCRIPTION("Topstar Laptop ACPI Extras driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
index 8796211ef24a..8e3d0146ff8c 100644
--- a/drivers/platform/x86/wmi.c
+++ b/drivers/platform/x86/wmi.c
@@ -130,13 +130,11 @@ static bool find_guid(const char *guid_string, struct wmi_block **out)
uuid_le guid_input;
struct wmi_block *wblock;
struct guid_block *block;
- struct list_head *p;
if (uuid_le_to_bin(guid_string, &guid_input))
return false;
- list_for_each(p, &wmi_block_list) {
- wblock = list_entry(p, struct wmi_block, list);
+ list_for_each_entry(wblock, &wmi_block_list, list) {
block = &wblock->gblock;
if (memcmp(block->guid, &guid_input, 16) == 0) {
@@ -519,7 +517,6 @@ wmi_notify_handler handler, void *data)
struct wmi_block *block;
acpi_status status = AE_NOT_EXIST;
uuid_le guid_input;
- struct list_head *p;
if (!guid || !handler)
return AE_BAD_PARAMETER;
@@ -527,9 +524,8 @@ wmi_notify_handler handler, void *data)
if (uuid_le_to_bin(guid, &guid_input))
return AE_BAD_PARAMETER;
- list_for_each(p, &wmi_block_list) {
+ list_for_each_entry(block, &wmi_block_list, list) {
acpi_status wmi_status;
- block = list_entry(p, struct wmi_block, list);
if (memcmp(block->gblock.guid, &guid_input, 16) == 0) {
if (block->handler &&
@@ -560,7 +556,6 @@ acpi_status wmi_remove_notify_handler(const char *guid)
struct wmi_block *block;
acpi_status status = AE_NOT_EXIST;
uuid_le guid_input;
- struct list_head *p;
if (!guid)
return AE_BAD_PARAMETER;
@@ -568,9 +563,8 @@ acpi_status wmi_remove_notify_handler(const char *guid)
if (uuid_le_to_bin(guid, &guid_input))
return AE_BAD_PARAMETER;
- list_for_each(p, &wmi_block_list) {
+ list_for_each_entry(block, &wmi_block_list, list) {
acpi_status wmi_status;
- block = list_entry(p, struct wmi_block, list);
if (memcmp(block->gblock.guid, &guid_input, 16) == 0) {
if (!block->handler ||
@@ -610,15 +604,13 @@ acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
union acpi_object params[1];
struct guid_block *gblock;
struct wmi_block *wblock;
- struct list_head *p;
input.count = 1;
input.pointer = params;
params[0].type = ACPI_TYPE_INTEGER;
params[0].integer.value = event;
- list_for_each(p, &wmi_block_list) {
- wblock = list_entry(p, struct wmi_block, list);
+ list_for_each_entry(wblock, &wmi_block_list, list) {
gblock = &wblock->gblock;
if ((gblock->flags & ACPI_WMI_EVENT) &&
@@ -933,12 +925,11 @@ static int wmi_dev_probe(struct device *dev)
goto probe_failure;
}
- buf = kmalloc(strlen(wdriver->driver.name) + 5, GFP_KERNEL);
+ buf = kasprintf(GFP_KERNEL, "wmi/%s", wdriver->driver.name);
if (!buf) {
ret = -ENOMEM;
goto probe_string_failure;
}
- sprintf(buf, "wmi/%s", wdriver->driver.name);
wblock->char_dev.minor = MISC_DYNAMIC_MINOR;
wblock->char_dev.name = buf;
wblock->char_dev.fops = &wmi_fops;
@@ -1261,11 +1252,9 @@ static void acpi_wmi_notify_handler(acpi_handle handle, u32 event,
{
struct guid_block *block;
struct wmi_block *wblock;
- struct list_head *p;
bool found_it = false;
- list_for_each(p, &wmi_block_list) {
- wblock = list_entry(p, struct wmi_block, list);
+ list_for_each_entry(wblock, &wmi_block_list, list) {
block = &wblock->gblock;
if (wblock->acpi_device->handle == handle &&
diff --git a/drivers/power/avs/smartreflex.c b/drivers/power/avs/smartreflex.c
index 89bf4d6cb486..cb0237143dbe 100644
--- a/drivers/power/avs/smartreflex.c
+++ b/drivers/power/avs/smartreflex.c
@@ -132,12 +132,16 @@ static void sr_set_clk_length(struct omap_sr *sr)
struct clk *fck;
u32 fclk_speed;
- fck = clk_get(&sr->pdev->dev, "fck");
-
+ /* Try interconnect target module fck first if it already exists */
+ fck = clk_get(sr->pdev->dev.parent, "fck");
if (IS_ERR(fck)) {
- dev_err(&sr->pdev->dev, "%s: unable to get fck for device %s\n",
- __func__, dev_name(&sr->pdev->dev));
- return;
+ fck = clk_get(&sr->pdev->dev, "fck");
+ if (IS_ERR(fck)) {
+ dev_err(&sr->pdev->dev,
+ "%s: unable to get fck for device %s\n",
+ __func__, dev_name(&sr->pdev->dev));
+ return;
+ }
}
fclk_speed = clk_get_rate(fck);
@@ -838,7 +842,7 @@ static int omap_sr_autocomp_store(void *data, u64 val)
DEFINE_SIMPLE_ATTRIBUTE(pm_sr_fops, omap_sr_autocomp_show,
omap_sr_autocomp_store, "%llu\n");
-static int __init omap_sr_probe(struct platform_device *pdev)
+static int omap_sr_probe(struct platform_device *pdev)
{
struct omap_sr *sr_info;
struct omap_sr_data *pdata = pdev->dev.platform_data;
@@ -898,6 +902,12 @@ static int __init omap_sr_probe(struct platform_device *pdev)
list_add(&sr_info->node, &sr_list);
+ ret = pm_runtime_get_sync(&pdev->dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(&pdev->dev);
+ goto err_list_del;
+ }
+
/*
* Call into late init to do initializations that require
* both sr driver and sr class driver to be initiallized.
@@ -966,12 +976,17 @@ static int __init omap_sr_probe(struct platform_device *pdev)
}
+ pm_runtime_put_sync(&pdev->dev);
+
return ret;
err_debugfs:
debugfs_remove_recursive(sr_info->dbg_dir);
err_list_del:
list_del(&sr_info->node);
+
+ pm_runtime_put_sync(&pdev->dev);
+
return ret;
}
@@ -1025,11 +1040,23 @@ static void omap_sr_shutdown(struct platform_device *pdev)
return;
}
+static const struct of_device_id omap_sr_match[] = {
+ { .compatible = "ti,omap3-smartreflex-core", },
+ { .compatible = "ti,omap3-smartreflex-mpu-iva", },
+ { .compatible = "ti,omap4-smartreflex-core", },
+ { .compatible = "ti,omap4-smartreflex-mpu", },
+ { .compatible = "ti,omap4-smartreflex-iva", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, omap_sr_match);
+
static struct platform_driver smartreflex_driver = {
+ .probe = omap_sr_probe,
.remove = omap_sr_remove,
.shutdown = omap_sr_shutdown,
.driver = {
.name = DRIVER_NAME,
+ .of_match_table = omap_sr_match,
},
};
@@ -1048,7 +1075,7 @@ static int __init sr_init(void)
else
pr_warn("%s: No PMIC hook to init smartreflex\n", __func__);
- ret = platform_driver_probe(&smartreflex_driver, omap_sr_probe);
+ ret = platform_driver_register(&smartreflex_driver);
if (ret) {
pr_err("%s: platform driver register failed for SR\n",
__func__);
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
index a102e74ab24e..df58fc878b3e 100644
--- a/drivers/power/reset/Kconfig
+++ b/drivers/power/reset/Kconfig
@@ -104,6 +104,13 @@ config POWER_RESET_MSM
help
Power off and restart support for Qualcomm boards.
+config POWER_RESET_OCELOT_RESET
+ bool "Microsemi Ocelot reset driver"
+ depends on MSCC_OCELOT || COMPILE_TEST
+ select MFD_SYSCON
+ help
+ This driver supports restart for Microsemi Ocelot SoC.
+
config POWER_RESET_PIIX4_POWEROFF
tristate "Intel PIIX4 power-off driver"
depends on PCI
@@ -218,5 +225,14 @@ config SYSCON_REBOOT_MODE
register, then the bootloader can read it to take different
action according to the mode.
+config POWER_RESET_SC27XX
+ bool "Spreadtrum SC27xx PMIC power-off driver"
+ depends on MFD_SC27XX_PMIC || COMPILE_TEST
+ help
+ This driver supports powering off a system through
+ Spreadtrum SC27xx series PMICs. The SC27xx series
+ PMICs includes the SC2720, SC2721, SC2723, SC2730
+ and SC2731 chips.
+
endif
diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile
index dcc92f5f7a37..7778c7485cf1 100644
--- a/drivers/power/reset/Makefile
+++ b/drivers/power/reset/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o
obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o
obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o
+obj-$(CONFIG_POWER_RESET_OCELOT_RESET) += ocelot-reset.o
obj-$(CONFIG_POWER_RESET_PIIX4_POWEROFF) += piix4-poweroff.o
obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o
obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o
@@ -26,3 +27,4 @@ obj-$(CONFIG_POWER_RESET_RMOBILE) += rmobile-reset.o
obj-$(CONFIG_POWER_RESET_ZX) += zx-reboot.o
obj-$(CONFIG_REBOOT_MODE) += reboot-mode.o
obj-$(CONFIG_SYSCON_REBOOT_MODE) += syscon-reboot-mode.o
+obj-$(CONFIG_POWER_RESET_SC27XX) += sc27xx-poweroff.o
diff --git a/drivers/power/reset/at91-poweroff.c b/drivers/power/reset/at91-poweroff.c
index c30c40193aaa..fb2fc8f741a1 100644
--- a/drivers/power/reset/at91-poweroff.c
+++ b/drivers/power/reset/at91-poweroff.c
@@ -55,10 +55,10 @@ static void __iomem *at91_shdwc_base;
static struct clk *sclk;
static void __iomem *mpddrc_base;
-static void __init at91_wakeup_status(void)
+static void __init at91_wakeup_status(struct platform_device *pdev)
{
+ const char *reason;
u32 reg = readl(at91_shdwc_base + AT91_SHDW_SR);
- char *reason = "unknown";
/* Simple power-on, just bail out */
if (!reg)
@@ -68,8 +68,10 @@ static void __init at91_wakeup_status(void)
reason = "RTT";
else if (reg & AT91_SHDW_RTCWK)
reason = "RTC";
+ else
+ reason = "unknown";
- pr_info("AT91: Wake-Up source: %s\n", reason);
+ dev_info(&pdev->dev, "Wake-Up source: %s\n", reason);
}
static void at91_poweroff(void)
@@ -157,10 +159,8 @@ static int __init at91_poweroff_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
at91_shdwc_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(at91_shdwc_base)) {
- dev_err(&pdev->dev, "Could not map reset controller address\n");
+ if (IS_ERR(at91_shdwc_base))
return PTR_ERR(at91_shdwc_base);
- }
sclk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(sclk))
@@ -172,7 +172,7 @@ static int __init at91_poweroff_probe(struct platform_device *pdev)
return ret;
}
- at91_wakeup_status();
+ at91_wakeup_status(pdev);
if (pdev->dev.of_node)
at91_poweroff_dt_set_wakeup_mode(pdev);
diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c
index b99769f8ab15..f44a9ffcc2ab 100644
--- a/drivers/power/reset/at91-reset.c
+++ b/drivers/power/reset/at91-reset.c
@@ -145,8 +145,8 @@ static int samx7_restart(struct notifier_block *this, unsigned long mode,
static void __init at91_reset_status(struct platform_device *pdev)
{
+ const char *reason;
u32 reg = readl(at91_rstc_base + AT91_RSTC_SR);
- char *reason;
switch ((reg & AT91_RSTC_RSTTYP) >> 8) {
case RESET_TYPE_GENERAL:
@@ -169,7 +169,7 @@ static void __init at91_reset_status(struct platform_device *pdev)
break;
}
- pr_info("AT91: Starting after %s\n", reason);
+ dev_info(&pdev->dev, "Starting after %s\n", reason);
}
static const struct of_device_id at91_ramc_of_match[] = {
diff --git a/drivers/power/reset/gemini-poweroff.c b/drivers/power/reset/gemini-poweroff.c
index ff75af5abbc5..2ac291af1265 100644
--- a/drivers/power/reset/gemini-poweroff.c
+++ b/drivers/power/reset/gemini-poweroff.c
@@ -47,8 +47,12 @@ static irqreturn_t gemini_powerbutton_interrupt(int irq, void *data)
val &= 0x70U;
switch (val) {
case GEMINI_STAT_CIR:
- dev_info(gpw->dev, "infrared poweroff\n");
- orderly_poweroff(true);
+ /*
+ * We do not yet have a driver for the infrared
+ * controller so it can cause spurious poweroff
+ * events. Ignore those for now.
+ */
+ dev_info(gpw->dev, "infrared poweroff - ignored\n");
break;
case GEMINI_STAT_RTC:
dev_info(gpw->dev, "RTC poweroff\n");
@@ -116,7 +120,17 @@ static int gemini_poweroff_probe(struct platform_device *pdev)
return -ENODEV;
}
- /* Clear the power management IRQ */
+ /*
+ * Enable the power controller. This is crucial on Gemini
+ * systems: if this is not done, pressing the power button
+ * will result in unconditional poweroff without any warning.
+ * This makes the kernel handle the poweroff.
+ */
+ val = readl(gpw->base + GEMINI_PWC_CTRLREG);
+ val |= GEMINI_CTRL_ENABLE;
+ writel(val, gpw->base + GEMINI_PWC_CTRLREG);
+
+ /* Now that the state machine is active, clear the IRQ */
val = readl(gpw->base + GEMINI_PWC_CTRLREG);
val |= GEMINI_CTRL_IRQ_CLR;
writel(val, gpw->base + GEMINI_PWC_CTRLREG);
@@ -129,16 +143,6 @@ static int gemini_poweroff_probe(struct platform_device *pdev)
pm_power_off = gemini_poweroff;
gpw_poweroff = gpw;
- /*
- * Enable the power controller. This is crucial on Gemini
- * systems: if this is not done, pressing the power button
- * will result in unconditional poweroff without any warning.
- * This makes the kernel handle the poweroff.
- */
- val = readl(gpw->base + GEMINI_PWC_CTRLREG);
- val |= GEMINI_CTRL_ENABLE;
- writel(val, gpw->base + GEMINI_PWC_CTRLREG);
-
dev_info(dev, "Gemini poweroff driver registered\n");
return 0;
diff --git a/drivers/power/reset/gpio-poweroff.c b/drivers/power/reset/gpio-poweroff.c
index be3d81ff51cc..6273ad3b411d 100644
--- a/drivers/power/reset/gpio-poweroff.c
+++ b/drivers/power/reset/gpio-poweroff.c
@@ -19,11 +19,13 @@
#include <linux/of_platform.h>
#include <linux/module.h>
+#define DEFAULT_TIMEOUT_MS 3000
/*
* Hold configuration here, cannot be more than one instance of the driver
* since pm_power_off itself is global.
*/
static struct gpio_desc *reset_gpio;
+static u32 timeout = DEFAULT_TIMEOUT_MS;
static void gpio_poweroff_do_poweroff(void)
{
@@ -40,7 +42,7 @@ static void gpio_poweroff_do_poweroff(void)
gpiod_set_value(reset_gpio, 1);
/* give it some time */
- mdelay(3000);
+ mdelay(timeout);
WARN_ON(1);
}
@@ -58,12 +60,14 @@ static int gpio_poweroff_probe(struct platform_device *pdev)
return -EBUSY;
}
- input = of_property_read_bool(pdev->dev.of_node, "input");
+ input = device_property_read_bool(&pdev->dev, "input");
if (input)
flags = GPIOD_IN;
else
flags = GPIOD_OUT_LOW;
+ device_property_read_u32(&pdev->dev, "timeout-ms", &timeout);
+
reset_gpio = devm_gpiod_get(&pdev->dev, NULL, flags);
if (IS_ERR(reset_gpio))
return PTR_ERR(reset_gpio);
diff --git a/drivers/power/reset/ocelot-reset.c b/drivers/power/reset/ocelot-reset.c
new file mode 100644
index 000000000000..5a13a5cc8188
--- /dev/null
+++ b/drivers/power/reset/ocelot-reset.c
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Microsemi MIPS SoC reset driver
+ *
+ * License: Dual MIT/GPL
+ * Copyright (c) 2017 Microsemi Corporation
+ */
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/notifier.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/regmap.h>
+
+struct ocelot_reset_context {
+ void __iomem *base;
+ struct regmap *cpu_ctrl;
+ struct notifier_block restart_handler;
+};
+
+#define ICPU_CFG_CPU_SYSTEM_CTRL_RESET 0x20
+#define CORE_RST_PROTECT BIT(2)
+
+#define SOFT_CHIP_RST BIT(0)
+
+static int ocelot_restart_handle(struct notifier_block *this,
+ unsigned long mode, void *cmd)
+{
+ struct ocelot_reset_context *ctx = container_of(this, struct
+ ocelot_reset_context,
+ restart_handler);
+
+ /* Make sure the core is not protected from reset */
+ regmap_update_bits(ctx->cpu_ctrl, ICPU_CFG_CPU_SYSTEM_CTRL_RESET,
+ CORE_RST_PROTECT, 0);
+
+ writel(SOFT_CHIP_RST, ctx->base);
+
+ pr_emerg("Unable to restart system\n");
+ return NOTIFY_DONE;
+}
+
+static int ocelot_reset_probe(struct platform_device *pdev)
+{
+ struct ocelot_reset_context *ctx;
+ struct resource *res;
+
+ struct device *dev = &pdev->dev;
+ int err;
+
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ctx->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ctx->base))
+ return PTR_ERR(ctx->base);
+
+ ctx->cpu_ctrl = syscon_regmap_lookup_by_compatible("mscc,ocelot-cpu-syscon");
+ if (IS_ERR(ctx->cpu_ctrl))
+ return PTR_ERR(ctx->cpu_ctrl);
+
+ ctx->restart_handler.notifier_call = ocelot_restart_handle;
+ ctx->restart_handler.priority = 192;
+ err = register_restart_handler(&ctx->restart_handler);
+ if (err)
+ dev_err(dev, "can't register restart notifier (err=%d)\n", err);
+
+ return err;
+}
+
+static const struct of_device_id ocelot_reset_of_match[] = {
+ { .compatible = "mscc,ocelot-chip-reset" },
+ {}
+};
+
+static struct platform_driver ocelot_reset_driver = {
+ .probe = ocelot_reset_probe,
+ .driver = {
+ .name = "ocelot-chip-reset",
+ .of_match_table = ocelot_reset_of_match,
+ },
+};
+builtin_platform_driver(ocelot_reset_driver);
diff --git a/drivers/power/reset/sc27xx-poweroff.c b/drivers/power/reset/sc27xx-poweroff.c
new file mode 100644
index 000000000000..29fb08b8faa0
--- /dev/null
+++ b/drivers/power/reset/sc27xx-poweroff.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Spreadtrum Communications Inc.
+ * Copyright (C) 2018 Linaro Ltd.
+ */
+
+#include <linux/cpu.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/syscore_ops.h>
+
+#define SC27XX_PWR_PD_HW 0xc2c
+#define SC27XX_PWR_OFF_EN BIT(0)
+
+static struct regmap *regmap;
+
+/*
+ * On Spreadtrum platform, we need power off system through external SC27xx
+ * series PMICs, and it is one similar SPI bus mapped by regmap to access PMIC,
+ * which is not fast io access.
+ *
+ * So before stopping other cores, we need release other cores' resource by
+ * taking cpus down to avoid racing regmap or spi mutex lock when poweroff
+ * system through PMIC.
+ */
+static void sc27xx_poweroff_shutdown(void)
+{
+#ifdef CONFIG_PM_SLEEP_SMP
+ int cpu = smp_processor_id();
+
+ freeze_secondary_cpus(cpu);
+#endif
+}
+
+static struct syscore_ops poweroff_syscore_ops = {
+ .shutdown = sc27xx_poweroff_shutdown,
+};
+
+static void sc27xx_poweroff_do_poweroff(void)
+{
+ regmap_write(regmap, SC27XX_PWR_PD_HW, SC27XX_PWR_OFF_EN);
+}
+
+static int sc27xx_poweroff_probe(struct platform_device *pdev)
+{
+ if (regmap)
+ return -EINVAL;
+
+ regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!regmap)
+ return -ENODEV;
+
+ pm_power_off = sc27xx_poweroff_do_poweroff;
+ register_syscore_ops(&poweroff_syscore_ops);
+ return 0;
+}
+
+static struct platform_driver sc27xx_poweroff_driver = {
+ .probe = sc27xx_poweroff_probe,
+ .driver = {
+ .name = "sc27xx-poweroff",
+ },
+};
+builtin_platform_driver(sc27xx_poweroff_driver);
diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c
index 7494f0f0eadb..e84b6e4da14a 100644
--- a/drivers/power/supply/axp20x_battery.c
+++ b/drivers/power/supply/axp20x_battery.c
@@ -49,10 +49,22 @@
#define AXP22X_CHRG_CTRL1_TGT_4_22V (1 << 5)
#define AXP22X_CHRG_CTRL1_TGT_4_24V (3 << 5)
+#define AXP813_CHRG_CTRL1_TGT_4_35V (3 << 5)
+
#define AXP20X_CHRG_CTRL1_TGT_CURR GENMASK(3, 0)
#define AXP20X_V_OFF_MASK GENMASK(2, 0)
+struct axp20x_batt_ps;
+
+struct axp_data {
+ int ccc_scale;
+ int ccc_offset;
+ bool has_fg_valid;
+ int (*get_max_voltage)(struct axp20x_batt_ps *batt, int *val);
+ int (*set_max_voltage)(struct axp20x_batt_ps *batt, int val);
+};
+
struct axp20x_batt_ps {
struct regmap *regmap;
struct power_supply *batt;
@@ -62,7 +74,7 @@ struct axp20x_batt_ps {
struct iio_channel *batt_v;
/* Maximum constant charge current */
unsigned int max_ccc;
- u8 axp_id;
+ const struct axp_data *data;
};
static int axp20x_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt,
@@ -123,20 +135,33 @@ static int axp22x_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt,
return 0;
}
-static void raw_to_constant_charge_current(struct axp20x_batt_ps *axp, int *val)
+static int axp813_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt,
+ int *val)
{
- if (axp->axp_id == AXP209_ID)
- *val = *val * 100000 + 300000;
- else
- *val = *val * 150000 + 300000;
-}
+ int ret, reg;
-static void constant_charge_current_to_raw(struct axp20x_batt_ps *axp, int *val)
-{
- if (axp->axp_id == AXP209_ID)
- *val = (*val - 300000) / 100000;
- else
- *val = (*val - 300000) / 150000;
+ ret = regmap_read(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, &reg);
+ if (ret)
+ return ret;
+
+ switch (reg & AXP20X_CHRG_CTRL1_TGT_VOLT) {
+ case AXP20X_CHRG_CTRL1_TGT_4_1V:
+ *val = 4100000;
+ break;
+ case AXP20X_CHRG_CTRL1_TGT_4_15V:
+ *val = 4150000;
+ break;
+ case AXP20X_CHRG_CTRL1_TGT_4_2V:
+ *val = 4200000;
+ break;
+ case AXP813_CHRG_CTRL1_TGT_4_35V:
+ *val = 4350000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
}
static int axp20x_get_constant_charge_current(struct axp20x_batt_ps *axp,
@@ -150,7 +175,7 @@ static int axp20x_get_constant_charge_current(struct axp20x_batt_ps *axp,
*val &= AXP20X_CHRG_CTRL1_TGT_CURR;
- raw_to_constant_charge_current(axp, val);
+ *val = *val * axp->data->ccc_scale + axp->data->ccc_offset;
return 0;
}
@@ -269,8 +294,7 @@ static int axp20x_battery_get_prop(struct power_supply *psy,
if (ret)
return ret;
- if (axp20x_batt->axp_id == AXP221_ID &&
- !(reg & AXP22X_FG_VALID))
+ if (axp20x_batt->data->has_fg_valid && !(reg & AXP22X_FG_VALID))
return -EINVAL;
/*
@@ -281,11 +305,8 @@ static int axp20x_battery_get_prop(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
- if (axp20x_batt->axp_id == AXP209_ID)
- return axp20x_battery_get_max_voltage(axp20x_batt,
- &val->intval);
- return axp22x_battery_get_max_voltage(axp20x_batt,
- &val->intval);
+ return axp20x_batt->data->get_max_voltage(axp20x_batt,
+ &val->intval);
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
ret = regmap_read(axp20x_batt->regmap, AXP20X_V_OFF, &reg);
@@ -312,6 +333,32 @@ static int axp20x_battery_get_prop(struct power_supply *psy,
return 0;
}
+static int axp22x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt,
+ int val)
+{
+ switch (val) {
+ case 4100000:
+ val = AXP20X_CHRG_CTRL1_TGT_4_1V;
+ break;
+
+ case 4200000:
+ val = AXP20X_CHRG_CTRL1_TGT_4_2V;
+ break;
+
+ default:
+ /*
+ * AXP20x max voltage can be set to 4.36V and AXP22X max voltage
+ * can be set to 4.22V and 4.24V, but these voltages are too
+ * high for Lithium based batteries (AXP PMICs are supposed to
+ * be used with these kinds of battery).
+ */
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(axp20x_batt->regmap, AXP20X_CHRG_CTRL1,
+ AXP20X_CHRG_CTRL1_TGT_VOLT, val);
+}
+
static int axp20x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt,
int val)
{
@@ -321,9 +368,6 @@ static int axp20x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt,
break;
case 4150000:
- if (axp20x_batt->axp_id == AXP221_ID)
- return -EINVAL;
-
val = AXP20X_CHRG_CTRL1_TGT_4_15V;
break;
@@ -351,7 +395,8 @@ static int axp20x_set_constant_charge_current(struct axp20x_batt_ps *axp_batt,
if (charge_current > axp_batt->max_ccc)
return -EINVAL;
- constant_charge_current_to_raw(axp_batt, &charge_current);
+ charge_current = (charge_current - axp_batt->data->ccc_offset) /
+ axp_batt->data->ccc_scale;
if (charge_current > AXP20X_CHRG_CTRL1_TGT_CURR || charge_current < 0)
return -EINVAL;
@@ -365,12 +410,14 @@ static int axp20x_set_max_constant_charge_current(struct axp20x_batt_ps *axp,
{
bool lower_max = false;
- constant_charge_current_to_raw(axp, &charge_current);
+ charge_current = (charge_current - axp->data->ccc_offset) /
+ axp->data->ccc_scale;
if (charge_current > AXP20X_CHRG_CTRL1_TGT_CURR || charge_current < 0)
return -EINVAL;
- raw_to_constant_charge_current(axp, &charge_current);
+ charge_current = charge_current * axp->data->ccc_scale +
+ axp->data->ccc_offset;
if (charge_current > axp->max_ccc)
dev_warn(axp->dev,
@@ -413,7 +460,7 @@ static int axp20x_battery_set_prop(struct power_supply *psy,
return axp20x_set_voltage_min_design(axp20x_batt, val->intval);
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
- return axp20x_battery_set_max_voltage(axp20x_batt, val->intval);
+ return axp20x_batt->data->set_max_voltage(axp20x_batt, val->intval);
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
return axp20x_set_constant_charge_current(axp20x_batt,
@@ -460,13 +507,39 @@ static const struct power_supply_desc axp20x_batt_ps_desc = {
.set_property = axp20x_battery_set_prop,
};
+static const struct axp_data axp209_data = {
+ .ccc_scale = 100000,
+ .ccc_offset = 300000,
+ .get_max_voltage = axp20x_battery_get_max_voltage,
+ .set_max_voltage = axp20x_battery_set_max_voltage,
+};
+
+static const struct axp_data axp221_data = {
+ .ccc_scale = 150000,
+ .ccc_offset = 300000,
+ .has_fg_valid = true,
+ .get_max_voltage = axp22x_battery_get_max_voltage,
+ .set_max_voltage = axp22x_battery_set_max_voltage,
+};
+
+static const struct axp_data axp813_data = {
+ .ccc_scale = 200000,
+ .ccc_offset = 200000,
+ .has_fg_valid = true,
+ .get_max_voltage = axp813_battery_get_max_voltage,
+ .set_max_voltage = axp20x_battery_set_max_voltage,
+};
+
static const struct of_device_id axp20x_battery_ps_id[] = {
{
.compatible = "x-powers,axp209-battery-power-supply",
- .data = (void *)AXP209_ID,
+ .data = (void *)&axp209_data,
}, {
.compatible = "x-powers,axp221-battery-power-supply",
- .data = (void *)AXP221_ID,
+ .data = (void *)&axp221_data,
+ }, {
+ .compatible = "x-powers,axp813-battery-power-supply",
+ .data = (void *)&axp813_data,
}, { /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, axp20x_battery_ps_id);
@@ -476,6 +549,7 @@ static int axp20x_power_probe(struct platform_device *pdev)
struct axp20x_batt_ps *axp20x_batt;
struct power_supply_config psy_cfg = {};
struct power_supply_battery_info info;
+ struct device *dev = &pdev->dev;
if (!of_device_is_available(pdev->dev.of_node))
return -ENODEV;
@@ -516,7 +590,7 @@ static int axp20x_power_probe(struct platform_device *pdev)
psy_cfg.drv_data = axp20x_batt;
psy_cfg.of_node = pdev->dev.of_node;
- axp20x_batt->axp_id = (uintptr_t)of_device_get_match_data(&pdev->dev);
+ axp20x_batt->data = (struct axp_data *)of_device_get_match_data(dev);
axp20x_batt->batt = devm_power_supply_register(&pdev->dev,
&axp20x_batt_ps_desc,
diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c
index 4cc6e038dfdd..fd8f0b2210bc 100644
--- a/drivers/power/supply/axp288_fuel_gauge.c
+++ b/drivers/power/supply/axp288_fuel_gauge.c
@@ -343,7 +343,7 @@ static inline void fuel_gauge_remove_debugfs(struct axp288_fg_info *info)
static void fuel_gauge_get_status(struct axp288_fg_info *info)
{
- int pwr_stat, fg_res;
+ int pwr_stat, fg_res, curr, ret;
pwr_stat = fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS);
if (pwr_stat < 0) {
@@ -353,19 +353,42 @@ static void fuel_gauge_get_status(struct axp288_fg_info *info)
}
/* Report full if Vbus is valid and the reported capacity is 100% */
- if (pwr_stat & PS_STAT_VBUS_VALID) {
- fg_res = fuel_gauge_reg_readb(info, AXP20X_FG_RES);
- if (fg_res < 0) {
- dev_err(&info->pdev->dev,
- "FG RES read failed: %d\n", fg_res);
- return;
- }
- if (fg_res == (FG_REP_CAP_VALID | 100)) {
- info->status = POWER_SUPPLY_STATUS_FULL;
- return;
- }
+ if (!(pwr_stat & PS_STAT_VBUS_VALID))
+ goto not_full;
+
+ fg_res = fuel_gauge_reg_readb(info, AXP20X_FG_RES);
+ if (fg_res < 0) {
+ dev_err(&info->pdev->dev, "FG RES read failed: %d\n", fg_res);
+ return;
}
+ if (!(fg_res & FG_REP_CAP_VALID))
+ goto not_full;
+ fg_res &= ~FG_REP_CAP_VALID;
+ if (fg_res == 100) {
+ info->status = POWER_SUPPLY_STATUS_FULL;
+ return;
+ }
+
+ /*
+ * Sometimes the charger turns itself off before fg-res reaches 100%.
+ * When this happens the AXP288 reports a not-charging status and
+ * 0 mA discharge current.
+ */
+ if (fg_res < 90 || (pwr_stat & PS_STAT_BAT_CHRG_DIR))
+ goto not_full;
+
+ ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &curr);
+ if (ret < 0) {
+ dev_err(&info->pdev->dev, "FG get current failed: %d\n", ret);
+ return;
+ }
+ if (curr == 0) {
+ info->status = POWER_SUPPLY_STATUS_FULL;
+ return;
+ }
+
+not_full:
if (pwr_stat & PS_STAT_BAT_CHRG_DIR)
info->status = POWER_SUPPLY_STATUS_CHARGING;
else
@@ -708,6 +731,12 @@ static const struct dmi_system_id axp288_fuel_gauge_blacklist[] = {
DMI_MATCH(DMI_BOARD_VERSION, "V1.1"),
},
},
+ {
+ /* ECS EF20EA */
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "EF20EA"),
+ },
+ },
{}
};
diff --git a/drivers/power/supply/bq2415x_charger.c b/drivers/power/supply/bq2415x_charger.c
index c4770a94cc8e..cbec70f3e73e 100644
--- a/drivers/power/supply/bq2415x_charger.c
+++ b/drivers/power/supply/bq2415x_charger.c
@@ -1037,7 +1037,10 @@ static int bq2415x_power_supply_init(struct bq2415x_device *bq)
int ret;
int chip;
char revstr[8];
- struct power_supply_config psy_cfg = { .drv_data = bq, };
+ struct power_supply_config psy_cfg = {
+ .drv_data = bq,
+ .of_node = bq->dev->of_node,
+ };
bq->charger_desc.name = bq->name;
bq->charger_desc.type = POWER_SUPPLY_TYPE_USB;
diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
index d99981542a46..7ce60519b1bc 100644
--- a/drivers/power/supply/bq27xxx_battery.c
+++ b/drivers/power/supply/bq27xxx_battery.c
@@ -1670,7 +1670,7 @@ static int bq27xxx_battery_status(struct bq27xxx_device_info *di,
status = POWER_SUPPLY_STATUS_FULL;
else if (di->cache.flags & BQ27000_FLAG_CHGS)
status = POWER_SUPPLY_STATUS_CHARGING;
- else if (power_supply_am_i_supplied(di->bat))
+ else if (power_supply_am_i_supplied(di->bat) > 0)
status = POWER_SUPPLY_STATUS_NOT_CHARGING;
else
status = POWER_SUPPLY_STATUS_DISCHARGING;
diff --git a/drivers/power/supply/da9150-fg.c b/drivers/power/supply/da9150-fg.c
index 8b8ce978656a..1e2e5b0520c9 100644
--- a/drivers/power/supply/da9150-fg.c
+++ b/drivers/power/supply/da9150-fg.c
@@ -92,7 +92,7 @@ struct da9150_fg {
static u32 da9150_fg_read_attr(struct da9150_fg *fg, u8 code, u8 size)
{
- u8 buf[size];
+ u8 buf[DA9150_QIF_LONG_SIZE];
u8 read_addr;
u32 res = 0;
int i;
@@ -111,7 +111,7 @@ static void da9150_fg_write_attr(struct da9150_fg *fg, u8 code, u8 size,
u32 val)
{
- u8 buf[size];
+ u8 buf[DA9150_QIF_LONG_SIZE];
u8 write_addr;
int i;
diff --git a/drivers/power/supply/ds2780_battery.c b/drivers/power/supply/ds2780_battery.c
index e5d81b493c45..370e9109342b 100644
--- a/drivers/power/supply/ds2780_battery.c
+++ b/drivers/power/supply/ds2780_battery.c
@@ -56,11 +56,6 @@ to_ds2780_device_info(struct power_supply *psy)
return power_supply_get_drvdata(psy);
}
-static inline struct power_supply *to_power_supply(struct device *dev)
-{
- return dev_get_drvdata(dev);
-}
-
static inline int ds2780_battery_io(struct ds2780_device_info *dev_info,
char *buf, int addr, size_t count, int io)
{
diff --git a/drivers/power/supply/ds2781_battery.c b/drivers/power/supply/ds2781_battery.c
index efe83ef8670c..d1b5a19aae7c 100644
--- a/drivers/power/supply/ds2781_battery.c
+++ b/drivers/power/supply/ds2781_battery.c
@@ -54,11 +54,6 @@ to_ds2781_device_info(struct power_supply *psy)
return power_supply_get_drvdata(psy);
}
-static inline struct power_supply *to_power_supply(struct device *dev)
-{
- return dev_get_drvdata(dev);
-}
-
static inline int ds2781_battery_io(struct ds2781_device_info *dev_info,
char *buf, int addr, size_t count, int io)
{
diff --git a/drivers/power/supply/gpio-charger.c b/drivers/power/supply/gpio-charger.c
index 001731e88718..bd2468ca6b63 100644
--- a/drivers/power/supply/gpio-charger.c
+++ b/drivers/power/supply/gpio-charger.c
@@ -28,14 +28,12 @@
#include <linux/power/gpio-charger.h>
struct gpio_charger {
- const struct gpio_charger_platform_data *pdata;
unsigned int irq;
bool wakeup_enabled;
struct power_supply *charger;
struct power_supply_desc charger_desc;
struct gpio_desc *gpiod;
- bool legacy_gpio_requested;
};
static irqreturn_t gpio_charger_irq(int irq, void *devid)
@@ -56,13 +54,10 @@ static int gpio_charger_get_property(struct power_supply *psy,
enum power_supply_property psp, union power_supply_propval *val)
{
struct gpio_charger *gpio_charger = psy_to_gpio_charger(psy);
- const struct gpio_charger_platform_data *pdata = gpio_charger->pdata;
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
val->intval = gpiod_get_value_cansleep(gpio_charger->gpiod);
- /* This xor is only ever used with legacy pdata GPIO */
- val->intval ^= pdata->gpio_active_low;
break;
default:
return -EINVAL;
@@ -71,175 +66,134 @@ static int gpio_charger_get_property(struct power_supply *psy,
return 0;
}
-static enum power_supply_property gpio_charger_properties[] = {
- POWER_SUPPLY_PROP_ONLINE,
-};
-
-static
-struct gpio_charger_platform_data *gpio_charger_parse_dt(struct device *dev)
+static enum power_supply_type gpio_charger_get_type(struct device *dev)
{
- struct device_node *np = dev->of_node;
- struct gpio_charger_platform_data *pdata;
const char *chargetype;
- int ret;
-
- if (!np)
- return ERR_PTR(-ENOENT);
-
- pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return ERR_PTR(-ENOMEM);
-
- pdata->name = np->name;
- pdata->type = POWER_SUPPLY_TYPE_UNKNOWN;
- ret = of_property_read_string(np, "charger-type", &chargetype);
- if (ret >= 0) {
- if (!strncmp("unknown", chargetype, 7))
- pdata->type = POWER_SUPPLY_TYPE_UNKNOWN;
- else if (!strncmp("battery", chargetype, 7))
- pdata->type = POWER_SUPPLY_TYPE_BATTERY;
- else if (!strncmp("ups", chargetype, 3))
- pdata->type = POWER_SUPPLY_TYPE_UPS;
- else if (!strncmp("mains", chargetype, 5))
- pdata->type = POWER_SUPPLY_TYPE_MAINS;
- else if (!strncmp("usb-sdp", chargetype, 7))
- pdata->type = POWER_SUPPLY_TYPE_USB;
- else if (!strncmp("usb-dcp", chargetype, 7))
- pdata->type = POWER_SUPPLY_TYPE_USB_DCP;
- else if (!strncmp("usb-cdp", chargetype, 7))
- pdata->type = POWER_SUPPLY_TYPE_USB_CDP;
- else if (!strncmp("usb-aca", chargetype, 7))
- pdata->type = POWER_SUPPLY_TYPE_USB_ACA;
- else
- dev_warn(dev, "unknown charger type %s\n", chargetype);
+
+ if (!device_property_read_string(dev, "charger-type", &chargetype)) {
+ if (!strcmp("unknown", chargetype))
+ return POWER_SUPPLY_TYPE_UNKNOWN;
+ if (!strcmp("battery", chargetype))
+ return POWER_SUPPLY_TYPE_BATTERY;
+ if (!strcmp("ups", chargetype))
+ return POWER_SUPPLY_TYPE_UPS;
+ if (!strcmp("mains", chargetype))
+ return POWER_SUPPLY_TYPE_MAINS;
+ if (!strcmp("usb-sdp", chargetype))
+ return POWER_SUPPLY_TYPE_USB;
+ if (!strcmp("usb-dcp", chargetype))
+ return POWER_SUPPLY_TYPE_USB_DCP;
+ if (!strcmp("usb-cdp", chargetype))
+ return POWER_SUPPLY_TYPE_USB_CDP;
+ if (!strcmp("usb-aca", chargetype))
+ return POWER_SUPPLY_TYPE_USB_ACA;
}
+ dev_warn(dev, "unknown charger type %s\n", chargetype);
- return pdata;
+ return POWER_SUPPLY_TYPE_UNKNOWN;
}
+static enum power_supply_property gpio_charger_properties[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
static int gpio_charger_probe(struct platform_device *pdev)
{
- const struct gpio_charger_platform_data *pdata = pdev->dev.platform_data;
+ struct device *dev = &pdev->dev;
+ const struct gpio_charger_platform_data *pdata = dev->platform_data;
struct power_supply_config psy_cfg = {};
struct gpio_charger *gpio_charger;
struct power_supply_desc *charger_desc;
- int ret;
- int irq;
-
- if (!pdata) {
- pdata = gpio_charger_parse_dt(&pdev->dev);
- if (IS_ERR(pdata)) {
- ret = PTR_ERR(pdata);
- if (ret != -EPROBE_DEFER)
- dev_err(&pdev->dev, "No platform data\n");
- return ret;
- }
+ unsigned long flags;
+ int irq, ret;
+
+ if (!pdata && !dev->of_node) {
+ dev_err(dev, "No platform data\n");
+ return -ENOENT;
}
- gpio_charger = devm_kzalloc(&pdev->dev, sizeof(*gpio_charger),
- GFP_KERNEL);
- if (!gpio_charger) {
- dev_err(&pdev->dev, "Failed to alloc driver structure\n");
+ gpio_charger = devm_kzalloc(dev, sizeof(*gpio_charger), GFP_KERNEL);
+ if (!gpio_charger)
return -ENOMEM;
- }
/*
* This will fetch a GPIO descriptor from device tree, ACPI or
* boardfile descriptor tables. It's good to try this first.
*/
- gpio_charger->gpiod = devm_gpiod_get(&pdev->dev, NULL, GPIOD_IN);
+ gpio_charger->gpiod = devm_gpiod_get(dev, NULL, GPIOD_IN);
/*
* If this fails and we're not using device tree, try the
* legacy platform data method.
*/
- if (IS_ERR(gpio_charger->gpiod) && !pdev->dev.of_node) {
+ if (IS_ERR(gpio_charger->gpiod) && !dev->of_node) {
/* Non-DT: use legacy GPIO numbers */
if (!gpio_is_valid(pdata->gpio)) {
- dev_err(&pdev->dev, "Invalid gpio pin in pdata\n");
+ dev_err(dev, "Invalid gpio pin in pdata\n");
return -EINVAL;
}
- ret = gpio_request(pdata->gpio, dev_name(&pdev->dev));
+ flags = GPIOF_IN;
+ if (pdata->gpio_active_low)
+ flags |= GPIOF_ACTIVE_LOW;
+ ret = devm_gpio_request_one(dev, pdata->gpio, flags,
+ dev_name(dev));
if (ret) {
- dev_err(&pdev->dev, "Failed to request gpio pin: %d\n",
- ret);
+ dev_err(dev, "Failed to request gpio pin: %d\n", ret);
return ret;
}
- gpio_charger->legacy_gpio_requested = true;
- ret = gpio_direction_input(pdata->gpio);
- if (ret) {
- dev_err(&pdev->dev, "Failed to set gpio to input: %d\n",
- ret);
- goto err_gpio_free;
- }
/* Then convert this to gpiod for now */
gpio_charger->gpiod = gpio_to_desc(pdata->gpio);
} else if (IS_ERR(gpio_charger->gpiod)) {
/* Just try again if this happens */
if (PTR_ERR(gpio_charger->gpiod) == -EPROBE_DEFER)
return -EPROBE_DEFER;
- dev_err(&pdev->dev, "error getting GPIO descriptor\n");
+ dev_err(dev, "error getting GPIO descriptor\n");
return PTR_ERR(gpio_charger->gpiod);
}
charger_desc = &gpio_charger->charger_desc;
-
- charger_desc->name = pdata->name ? pdata->name : "gpio-charger";
- charger_desc->type = pdata->type;
charger_desc->properties = gpio_charger_properties;
charger_desc->num_properties = ARRAY_SIZE(gpio_charger_properties);
charger_desc->get_property = gpio_charger_get_property;
- psy_cfg.supplied_to = pdata->supplied_to;
- psy_cfg.num_supplicants = pdata->num_supplicants;
- psy_cfg.of_node = pdev->dev.of_node;
+ psy_cfg.of_node = dev->of_node;
psy_cfg.drv_data = gpio_charger;
- gpio_charger->pdata = pdata;
+ if (pdata) {
+ charger_desc->name = pdata->name;
+ charger_desc->type = pdata->type;
+ psy_cfg.supplied_to = pdata->supplied_to;
+ psy_cfg.num_supplicants = pdata->num_supplicants;
+ } else {
+ charger_desc->name = dev->of_node->name;
+ charger_desc->type = gpio_charger_get_type(dev);
+ }
+
+ if (!charger_desc->name)
+ charger_desc->name = pdev->name;
- gpio_charger->charger = power_supply_register(&pdev->dev,
- charger_desc, &psy_cfg);
+ gpio_charger->charger = devm_power_supply_register(dev, charger_desc,
+ &psy_cfg);
if (IS_ERR(gpio_charger->charger)) {
ret = PTR_ERR(gpio_charger->charger);
- dev_err(&pdev->dev, "Failed to register power supply: %d\n",
- ret);
- goto err_gpio_free;
+ dev_err(dev, "Failed to register power supply: %d\n", ret);
+ return ret;
}
irq = gpiod_to_irq(gpio_charger->gpiod);
if (irq > 0) {
- ret = request_any_context_irq(irq, gpio_charger_irq,
+ ret = devm_request_any_context_irq(dev, irq, gpio_charger_irq,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
- dev_name(&pdev->dev), gpio_charger->charger);
+ dev_name(dev), gpio_charger->charger);
if (ret < 0)
- dev_warn(&pdev->dev, "Failed to request irq: %d\n", ret);
+ dev_warn(dev, "Failed to request irq: %d\n", ret);
else
gpio_charger->irq = irq;
}
platform_set_drvdata(pdev, gpio_charger);
- device_init_wakeup(&pdev->dev, 1);
-
- return 0;
-
-err_gpio_free:
- if (gpio_charger->legacy_gpio_requested)
- gpio_free(pdata->gpio);
- return ret;
-}
-
-static int gpio_charger_remove(struct platform_device *pdev)
-{
- struct gpio_charger *gpio_charger = platform_get_drvdata(pdev);
-
- if (gpio_charger->irq)
- free_irq(gpio_charger->irq, gpio_charger->charger);
-
- power_supply_unregister(gpio_charger->charger);
-
- if (gpio_charger->legacy_gpio_requested)
- gpio_free(gpio_charger->pdata->gpio);
+ device_init_wakeup(dev, 1);
return 0;
}
@@ -280,7 +234,6 @@ MODULE_DEVICE_TABLE(of, gpio_charger_match);
static struct platform_driver gpio_charger_driver = {
.probe = gpio_charger_probe,
- .remove = gpio_charger_remove,
.driver = {
.name = "gpio-charger",
.pm = &gpio_charger_pm_ops,
diff --git a/drivers/power/supply/ltc2941-battery-gauge.c b/drivers/power/supply/ltc2941-battery-gauge.c
index 4cfa3f0cd689..4f129bb4c972 100644
--- a/drivers/power/supply/ltc2941-battery-gauge.c
+++ b/drivers/power/supply/ltc2941-battery-gauge.c
@@ -34,6 +34,10 @@ enum ltc294x_reg {
LTC294X_REG_CONTROL = 0x01,
LTC294X_REG_ACC_CHARGE_MSB = 0x02,
LTC294X_REG_ACC_CHARGE_LSB = 0x03,
+ LTC294X_REG_CHARGE_THR_HIGH_MSB = 0x04,
+ LTC294X_REG_CHARGE_THR_HIGH_LSB = 0x05,
+ LTC294X_REG_CHARGE_THR_LOW_MSB = 0x06,
+ LTC294X_REG_CHARGE_THR_LOW_LSB = 0x07,
LTC294X_REG_VOLTAGE_MSB = 0x08,
LTC294X_REG_VOLTAGE_LSB = 0x09,
LTC2942_REG_TEMPERATURE_MSB = 0x0C,
@@ -179,21 +183,22 @@ error_exit:
return ret;
}
-static int ltc294x_read_charge_register(const struct ltc294x_info *info)
-{
+static int ltc294x_read_charge_register(const struct ltc294x_info *info,
+ enum ltc294x_reg reg)
+ {
int ret;
u8 datar[2];
- ret = ltc294x_read_regs(info->client,
- LTC294X_REG_ACC_CHARGE_MSB, &datar[0], 2);
+ ret = ltc294x_read_regs(info->client, reg, &datar[0], 2);
if (ret < 0)
return ret;
return (datar[0] << 8) + datar[1];
}
-static int ltc294x_get_charge_now(const struct ltc294x_info *info, int *val)
+static int ltc294x_get_charge(const struct ltc294x_info *info,
+ enum ltc294x_reg reg, int *val)
{
- int value = ltc294x_read_charge_register(info);
+ int value = ltc294x_read_charge_register(info, reg);
if (value < 0)
return value;
@@ -245,10 +250,29 @@ error_exit:
return ret < 0 ? ret : 0;
}
+static int ltc294x_set_charge_thr(const struct ltc294x_info *info,
+ enum ltc294x_reg reg, int val)
+{
+ u8 dataw[2];
+ s32 value;
+
+ value = convert_uAh_to_bin(info, val);
+ /* Direction depends on how sense+/- were connected */
+ if (info->Qlsb < 0)
+ value += 0xFFFF;
+ if ((value < 0) || (value > 0xFFFF)) /* input validation */
+ return -EINVAL;
+
+ /* Set new charge value */
+ dataw[0] = I16_MSB(value);
+ dataw[1] = I16_LSB(value);
+ return ltc294x_write_regs(info->client, reg, &dataw[0], 2);
+}
+
static int ltc294x_get_charge_counter(
const struct ltc294x_info *info, int *val)
{
- int value = ltc294x_read_charge_register(info);
+ int value = ltc294x_read_charge_register(info, LTC294X_REG_ACC_CHARGE_MSB);
if (value < 0)
return value;
@@ -317,15 +341,15 @@ static int ltc294x_get_temperature(const struct ltc294x_info *info, int *val)
if (info->id == LTC2942_ID) {
reg = LTC2942_REG_TEMPERATURE_MSB;
- value = 60000; /* Full-scale is 600 Kelvin */
+ value = 6000; /* Full-scale is 600 Kelvin */
} else {
reg = LTC2943_REG_TEMPERATURE_MSB;
- value = 51000; /* Full-scale is 510 Kelvin */
+ value = 5100; /* Full-scale is 510 Kelvin */
}
ret = ltc294x_read_regs(info->client, reg, &datar[0], 2);
value *= (datar[0] << 8) | datar[1];
- /* Convert to centidegrees */
- *val = value / 0xFFFF - 27215;
+ /* Convert to tenths of degree Celsius */
+ *val = value / 0xFFFF - 2722;
return ret;
}
@@ -336,8 +360,15 @@ static int ltc294x_get_property(struct power_supply *psy,
struct ltc294x_info *info = power_supply_get_drvdata(psy);
switch (prop) {
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ return ltc294x_get_charge(info, LTC294X_REG_CHARGE_THR_HIGH_MSB,
+ &val->intval);
+ case POWER_SUPPLY_PROP_CHARGE_EMPTY:
+ return ltc294x_get_charge(info, LTC294X_REG_CHARGE_THR_LOW_MSB,
+ &val->intval);
case POWER_SUPPLY_PROP_CHARGE_NOW:
- return ltc294x_get_charge_now(info, &val->intval);
+ return ltc294x_get_charge(info, LTC294X_REG_ACC_CHARGE_MSB,
+ &val->intval);
case POWER_SUPPLY_PROP_CHARGE_COUNTER:
return ltc294x_get_charge_counter(info, &val->intval);
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
@@ -358,6 +389,12 @@ static int ltc294x_set_property(struct power_supply *psy,
struct ltc294x_info *info = power_supply_get_drvdata(psy);
switch (psp) {
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ return ltc294x_set_charge_thr(info,
+ LTC294X_REG_CHARGE_THR_HIGH_MSB, val->intval);
+ case POWER_SUPPLY_PROP_CHARGE_EMPTY:
+ return ltc294x_set_charge_thr(info,
+ LTC294X_REG_CHARGE_THR_LOW_MSB, val->intval);
case POWER_SUPPLY_PROP_CHARGE_NOW:
return ltc294x_set_charge_now(info, val->intval);
default:
@@ -369,6 +406,8 @@ static int ltc294x_property_is_writeable(
struct power_supply *psy, enum power_supply_property psp)
{
switch (psp) {
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ case POWER_SUPPLY_PROP_CHARGE_EMPTY:
case POWER_SUPPLY_PROP_CHARGE_NOW:
return 1;
default:
@@ -378,7 +417,7 @@ static int ltc294x_property_is_writeable(
static void ltc294x_update(struct ltc294x_info *info)
{
- int charge = ltc294x_read_charge_register(info);
+ int charge = ltc294x_read_charge_register(info, LTC294X_REG_ACC_CHARGE_MSB);
if (charge != info->charge) {
info->charge = charge;
@@ -397,6 +436,8 @@ static void ltc294x_work(struct work_struct *work)
static enum power_supply_property ltc294x_properties[] = {
POWER_SUPPLY_PROP_CHARGE_COUNTER,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_EMPTY,
POWER_SUPPLY_PROP_CHARGE_NOW,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_TEMP,
diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c
index 35dde81b1c9b..1a568df383db 100644
--- a/drivers/power/supply/max17042_battery.c
+++ b/drivers/power/supply/max17042_battery.c
@@ -1053,6 +1053,7 @@ static int max17042_probe(struct i2c_client *client,
i2c_set_clientdata(client, chip);
psy_cfg.drv_data = chip;
+ psy_cfg.of_node = dev->of_node;
/* When current is not measured,
* CURRENT_NOW and CURRENT_AVG properties should be invisible. */
diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c
index 82f998ab5a52..feac7b066e6c 100644
--- a/drivers/power/supply/power_supply_core.c
+++ b/drivers/power/supply/power_supply_core.c
@@ -668,7 +668,7 @@ EXPORT_SYMBOL_GPL(power_supply_powers);
static void power_supply_dev_release(struct device *dev)
{
- struct power_supply *psy = container_of(dev, struct power_supply, dev);
+ struct power_supply *psy = to_power_supply(dev);
dev_dbg(dev, "%s\n", __func__);
kfree(psy);
}
diff --git a/drivers/powercap/intel_rapl.c b/drivers/powercap/intel_rapl.c
index 35636e1d8a3d..295d8dcba48c 100644
--- a/drivers/powercap/intel_rapl.c
+++ b/drivers/powercap/intel_rapl.c
@@ -1162,6 +1162,7 @@ static const struct x86_cpu_id rapl_ids[] __initconst = {
RAPL_CPU(INTEL_FAM6_SKYLAKE_X, rapl_defaults_hsw_server),
RAPL_CPU(INTEL_FAM6_KABYLAKE_MOBILE, rapl_defaults_core),
RAPL_CPU(INTEL_FAM6_KABYLAKE_DESKTOP, rapl_defaults_core),
+ RAPL_CPU(INTEL_FAM6_CANNONLAKE_MOBILE, rapl_defaults_core),
RAPL_CPU(INTEL_FAM6_ATOM_SILVERMONT1, rapl_defaults_byt),
RAPL_CPU(INTEL_FAM6_ATOM_AIRMONT, rapl_defaults_cht),
diff --git a/drivers/pps/clients/pps_parport.c b/drivers/pps/clients/pps_parport.c
index 83797d89c30f..4db824f88d00 100644
--- a/drivers/pps/clients/pps_parport.c
+++ b/drivers/pps/clients/pps_parport.c
@@ -49,6 +49,7 @@ MODULE_PARM_DESC(clear_wait,
" zero turns clear edge capture off entirely");
module_param(clear_wait, uint, 0);
+static DEFINE_IDA(pps_client_index);
/* internal per port structure */
struct pps_client_pp {
@@ -56,6 +57,7 @@ struct pps_client_pp {
struct pps_device *pps; /* PPS device */
unsigned int cw; /* port clear timeout */
unsigned int cw_err; /* number of timeouts */
+ int index; /* device number */
};
static inline int signal_is_set(struct parport *port)
@@ -136,6 +138,8 @@ out_both:
static void parport_attach(struct parport *port)
{
+ struct pardev_cb pps_client_cb;
+ int index;
struct pps_client_pp *device;
struct pps_source_info info = {
.name = KBUILD_MODNAME,
@@ -154,8 +158,15 @@ static void parport_attach(struct parport *port)
return;
}
- device->pardev = parport_register_device(port, KBUILD_MODNAME,
- NULL, NULL, parport_irq, PARPORT_FLAG_EXCL, device);
+ index = ida_simple_get(&pps_client_index, 0, 0, GFP_KERNEL);
+ memset(&pps_client_cb, 0, sizeof(pps_client_cb));
+ pps_client_cb.private = device;
+ pps_client_cb.irq_func = parport_irq;
+ pps_client_cb.flags = PARPORT_FLAG_EXCL;
+ device->pardev = parport_register_dev_model(port,
+ KBUILD_MODNAME,
+ &pps_client_cb,
+ index);
if (!device->pardev) {
pr_err("couldn't register with %s\n", port->name);
goto err_free;
@@ -176,6 +187,7 @@ static void parport_attach(struct parport *port)
device->cw = clear_wait;
port->ops->enable_irq(port);
+ device->index = index;
pr_info("attached to %s\n", port->name);
@@ -186,6 +198,7 @@ err_release_dev:
err_unregister_dev:
parport_unregister_device(device->pardev);
err_free:
+ ida_simple_remove(&pps_client_index, index);
kfree(device);
}
@@ -205,13 +218,15 @@ static void parport_detach(struct parport *port)
pps_unregister_source(device->pps);
parport_release(pardev);
parport_unregister_device(pardev);
+ ida_simple_remove(&pps_client_index, device->index);
kfree(device);
}
static struct parport_driver pps_parport_driver = {
.name = KBUILD_MODNAME,
- .attach = parport_attach,
+ .match_port = parport_attach,
.detach = parport_detach,
+ .devmodel = true,
};
/* module staff */
diff --git a/drivers/pps/generators/pps_gen_parport.c b/drivers/pps/generators/pps_gen_parport.c
index 51cfde6afffd..7fd36cac063b 100644
--- a/drivers/pps/generators/pps_gen_parport.c
+++ b/drivers/pps/generators/pps_gen_parport.c
@@ -192,13 +192,18 @@ static inline ktime_t next_intr_time(struct pps_generator_pp *dev)
static void parport_attach(struct parport *port)
{
+ struct pardev_cb pps_cb;
+
if (attached) {
/* we already have a port */
return;
}
- device.pardev = parport_register_device(port, KBUILD_MODNAME,
- NULL, NULL, NULL, PARPORT_FLAG_EXCL, &device);
+ memset(&pps_cb, 0, sizeof(pps_cb));
+ pps_cb.private = &device;
+ pps_cb.flags = PARPORT_FLAG_EXCL;
+ device.pardev = parport_register_dev_model(port, KBUILD_MODNAME,
+ &pps_cb, 0);
if (!device.pardev) {
pr_err("couldn't register with %s\n", port->name);
return;
@@ -236,8 +241,9 @@ static void parport_detach(struct parport *port)
static struct parport_driver pps_gen_parport_driver = {
.name = KBUILD_MODNAME,
- .attach = parport_attach,
+ .match_port = parport_attach,
.detach = parport_detach,
+ .devmodel = true,
};
/* module staff */
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 763ee50ea57d..4635cb35008c 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -43,7 +43,7 @@ config PWM_AB8500
config PWM_ATMEL
tristate "Atmel PWM support"
- depends on ARCH_AT91 || AVR32
+ depends on ARCH_AT91
help
Generic PWM framework driver for Atmel SoC.
@@ -113,15 +113,6 @@ config PWM_BERLIN
To compile this driver as a module, choose M here: the module
will be called pwm-berlin.
-config PWM_BFIN
- tristate "Blackfin PWM support"
- depends on BFIN_GPTIMERS
- help
- Generic PWM framework driver for Blackfin.
-
- To compile this driver as a module, choose M here: the module
- will be called pwm-bfin.
-
config PWM_BRCMSTB
tristate "Broadcom STB PWM support"
depends on ARCH_BRCMSTB || BMIPS_GENERIC
@@ -209,10 +200,10 @@ config PWM_IMX
will be called pwm-imx.
config PWM_JZ4740
- tristate "Ingenic JZ4740 PWM support"
- depends on MACH_JZ4740
+ tristate "Ingenic JZ47xx PWM support"
+ depends on MACH_INGENIC
help
- Generic PWM framework driver for Ingenic JZ4740 based
+ Generic PWM framework driver for Ingenic JZ47xx based
machines.
To compile this driver as a module, choose M here: the module
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 0258a745f30c..9c676a0dadf5 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -9,7 +9,6 @@ obj-$(CONFIG_PWM_BCM_IPROC) += pwm-bcm-iproc.o
obj-$(CONFIG_PWM_BCM_KONA) += pwm-bcm-kona.o
obj-$(CONFIG_PWM_BCM2835) += pwm-bcm2835.o
obj-$(CONFIG_PWM_BERLIN) += pwm-berlin.o
-obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o
obj-$(CONFIG_PWM_BRCMSTB) += pwm-brcmstb.o
obj-$(CONFIG_PWM_CLPS711X) += pwm-clps711x.o
obj-$(CONFIG_PWM_CRC) += pwm-crc.o
diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c
index acd3ce8ecf3f..4fb1be246c44 100644
--- a/drivers/pwm/pwm-atmel-tcb.c
+++ b/drivers/pwm/pwm-atmel-tcb.c
@@ -401,7 +401,6 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev)
tcbpwm = devm_kzalloc(&pdev->dev, sizeof(*tcbpwm), GFP_KERNEL);
if (tcbpwm == NULL) {
err = -ENOMEM;
- dev_err(&pdev->dev, "failed to allocate memory\n");
goto err_free_tc;
}
diff --git a/drivers/pwm/pwm-bfin.c b/drivers/pwm/pwm-bfin.c
deleted file mode 100644
index a9a88137f2cb..000000000000
--- a/drivers/pwm/pwm-bfin.c
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Blackfin Pulse Width Modulation (PWM) core
- *
- * Copyright (c) 2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/pwm.h>
-#include <linux/slab.h>
-
-#include <asm/gptimers.h>
-#include <asm/portmux.h>
-
-struct bfin_pwm_chip {
- struct pwm_chip chip;
-};
-
-struct bfin_pwm {
- unsigned short pin;
-};
-
-static const unsigned short pwm_to_gptimer_per[] = {
- P_TMR0, P_TMR1, P_TMR2, P_TMR3, P_TMR4, P_TMR5,
- P_TMR6, P_TMR7, P_TMR8, P_TMR9, P_TMR10, P_TMR11,
-};
-
-static int bfin_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
-{
- struct bfin_pwm *priv;
- int ret;
-
- if (pwm->hwpwm >= ARRAY_SIZE(pwm_to_gptimer_per))
- return -EINVAL;
-
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- priv->pin = pwm_to_gptimer_per[pwm->hwpwm];
-
- ret = peripheral_request(priv->pin, NULL);
- if (ret) {
- kfree(priv);
- return ret;
- }
-
- pwm_set_chip_data(pwm, priv);
-
- return 0;
-}
-
-static void bfin_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
-{
- struct bfin_pwm *priv = pwm_get_chip_data(pwm);
-
- if (priv) {
- peripheral_free(priv->pin);
- kfree(priv);
- }
-}
-
-static int bfin_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
- int duty_ns, int period_ns)
-{
- struct bfin_pwm *priv = pwm_get_chip_data(pwm);
- unsigned long period, duty;
- unsigned long long val;
-
- val = (unsigned long long)get_sclk() * period_ns;
- do_div(val, NSEC_PER_SEC);
- period = val;
-
- val = (unsigned long long)period * duty_ns;
- do_div(val, period_ns);
- duty = period - val;
-
- if (duty >= period)
- duty = period - 1;
-
- set_gptimer_config(priv->pin, TIMER_MODE_PWM | TIMER_PERIOD_CNT);
- set_gptimer_pwidth(priv->pin, duty);
- set_gptimer_period(priv->pin, period);
-
- return 0;
-}
-
-static int bfin_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
-{
- struct bfin_pwm *priv = pwm_get_chip_data(pwm);
-
- enable_gptimer(priv->pin);
-
- return 0;
-}
-
-static void bfin_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
-{
- struct bfin_pwm *priv = pwm_get_chip_data(pwm);
-
- disable_gptimer(priv->pin);
-}
-
-static const struct pwm_ops bfin_pwm_ops = {
- .request = bfin_pwm_request,
- .free = bfin_pwm_free,
- .config = bfin_pwm_config,
- .enable = bfin_pwm_enable,
- .disable = bfin_pwm_disable,
- .owner = THIS_MODULE,
-};
-
-static int bfin_pwm_probe(struct platform_device *pdev)
-{
- struct bfin_pwm_chip *pwm;
- int ret;
-
- pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
- if (!pwm)
- return -ENOMEM;
-
- platform_set_drvdata(pdev, pwm);
-
- pwm->chip.dev = &pdev->dev;
- pwm->chip.ops = &bfin_pwm_ops;
- pwm->chip.base = -1;
- pwm->chip.npwm = 12;
-
- ret = pwmchip_add(&pwm->chip);
- if (ret < 0) {
- dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
- return ret;
- }
-
- return 0;
-}
-
-static int bfin_pwm_remove(struct platform_device *pdev)
-{
- struct bfin_pwm_chip *pwm = platform_get_drvdata(pdev);
-
- return pwmchip_remove(&pwm->chip);
-}
-
-static struct platform_driver bfin_pwm_driver = {
- .driver = {
- .name = "bfin-pwm",
- },
- .probe = bfin_pwm_probe,
- .remove = bfin_pwm_remove,
-};
-
-module_platform_driver(bfin_pwm_driver);
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c
index 2ba5c3a398ff..08cbe8120588 100644
--- a/drivers/pwm/pwm-imx.c
+++ b/drivers/pwm/pwm-imx.c
@@ -35,6 +35,7 @@
#define MX3_PWMSAR 0x0C /* PWM Sample Register */
#define MX3_PWMPR 0x10 /* PWM Period Register */
#define MX3_PWMCR_PRESCALER(x) ((((x) - 1) & 0xFFF) << 4)
+#define MX3_PWMCR_STOPEN (1 << 25)
#define MX3_PWMCR_DOZEEN (1 << 24)
#define MX3_PWMCR_WAITEN (1 << 23)
#define MX3_PWMCR_DBGEN (1 << 22)
@@ -210,7 +211,7 @@ static int imx_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm,
writel(period_cycles, imx->mmio_base + MX3_PWMPR);
cr = MX3_PWMCR_PRESCALER(prescale) |
- MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN |
+ MX3_PWMCR_STOPEN | MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN |
MX3_PWMCR_DBGEN | MX3_PWMCR_CLKSRC_IPG_HIGH |
MX3_PWMCR_EN;
diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c
index a75ff3622450..a7b134af5e04 100644
--- a/drivers/pwm/pwm-jz4740.c
+++ b/drivers/pwm/pwm-jz4740.c
@@ -18,6 +18,7 @@
#include <linux/gpio.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
@@ -71,9 +72,15 @@ static void jz4740_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
{
uint32_t ctrl = jz4740_timer_get_ctrl(pwm->hwpwm);
+ /* Disable PWM output.
+ * In TCU2 mode (channel 1/2 on JZ4750+), this must be done before the
+ * counter is stopped, while in TCU1 mode the order does not matter.
+ */
ctrl &= ~JZ_TIMER_CTRL_PWM_ENABLE;
- jz4740_timer_disable(pwm->hwpwm);
jz4740_timer_set_ctrl(pwm->hwpwm, ctrl);
+
+ /* Stop counter */
+ jz4740_timer_disable(pwm->hwpwm);
}
static int jz4740_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
@@ -124,10 +131,29 @@ static int jz4740_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
return 0;
}
+static int jz4740_pwm_set_polarity(struct pwm_chip *chip,
+ struct pwm_device *pwm, enum pwm_polarity polarity)
+{
+ uint32_t ctrl = jz4740_timer_get_ctrl(pwm->pwm);
+
+ switch (polarity) {
+ case PWM_POLARITY_NORMAL:
+ ctrl &= ~JZ_TIMER_CTRL_PWM_ACTIVE_LOW;
+ break;
+ case PWM_POLARITY_INVERSED:
+ ctrl |= JZ_TIMER_CTRL_PWM_ACTIVE_LOW;
+ break;
+ }
+
+ jz4740_timer_set_ctrl(pwm->hwpwm, ctrl);
+ return 0;
+}
+
static const struct pwm_ops jz4740_pwm_ops = {
.request = jz4740_pwm_request,
.free = jz4740_pwm_free,
.config = jz4740_pwm_config,
+ .set_polarity = jz4740_pwm_set_polarity,
.enable = jz4740_pwm_enable,
.disable = jz4740_pwm_disable,
.owner = THIS_MODULE,
@@ -149,6 +175,8 @@ static int jz4740_pwm_probe(struct platform_device *pdev)
jz4740->chip.ops = &jz4740_pwm_ops;
jz4740->chip.npwm = NUM_PWM;
jz4740->chip.base = -1;
+ jz4740->chip.of_xlate = of_pwm_xlate_with_flags;
+ jz4740->chip.of_pwm_n_cells = 3;
platform_set_drvdata(pdev, jz4740);
@@ -162,9 +190,20 @@ static int jz4740_pwm_remove(struct platform_device *pdev)
return pwmchip_remove(&jz4740->chip);
}
+#ifdef CONFIG_OF
+static const struct of_device_id jz4740_pwm_dt_ids[] = {
+ { .compatible = "ingenic,jz4740-pwm", },
+ { .compatible = "ingenic,jz4770-pwm", },
+ { .compatible = "ingenic,jz4780-pwm", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, jz4740_pwm_dt_ids);
+#endif
+
static struct platform_driver jz4740_pwm_driver = {
.driver = {
.name = "jz4740-pwm",
+ .of_match_table = of_match_ptr(jz4740_pwm_dt_ids),
},
.probe = jz4740_pwm_probe,
.remove = jz4740_pwm_remove,
diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c
index f5d97e0ad52b..328c124773b2 100644
--- a/drivers/pwm/pwm-mediatek.c
+++ b/drivers/pwm/pwm-mediatek.c
@@ -29,7 +29,9 @@
#define PWMGDUR 0x0c
#define PWMWAVENUM 0x28
#define PWMDWIDTH 0x2c
+#define PWM45DWIDTH_FIXUP 0x30
#define PWMTHRES 0x30
+#define PWM45THRES_FIXUP 0x34
#define PWM_CLK_DIV_MAX 7
@@ -54,6 +56,7 @@ static const char * const mtk_pwm_clk_name[MTK_CLK_MAX] = {
struct mtk_pwm_platform_data {
unsigned int num_pwms;
+ bool pwm45_fixup;
};
/**
@@ -66,6 +69,7 @@ struct mtk_pwm_chip {
struct pwm_chip chip;
void __iomem *regs;
struct clk *clks[MTK_CLK_MAX];
+ const struct mtk_pwm_platform_data *soc;
};
static const unsigned int mtk_pwm_reg_offset[] = {
@@ -131,18 +135,25 @@ static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
{
struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip);
struct clk *clk = pc->clks[MTK_CLK_PWM1 + pwm->hwpwm];
- u32 resolution, clkdiv = 0;
+ u32 clkdiv = 0, cnt_period, cnt_duty, reg_width = PWMDWIDTH,
+ reg_thres = PWMTHRES;
+ u64 resolution;
int ret;
ret = mtk_pwm_clk_enable(chip, pwm);
if (ret < 0)
return ret;
- resolution = NSEC_PER_SEC / clk_get_rate(clk);
+ /* Using resolution in picosecond gets accuracy higher */
+ resolution = (u64)NSEC_PER_SEC * 1000;
+ do_div(resolution, clk_get_rate(clk));
- while (period_ns / resolution > 8191) {
+ cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000, resolution);
+ while (cnt_period > 8191) {
resolution *= 2;
clkdiv++;
+ cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000,
+ resolution);
}
if (clkdiv > PWM_CLK_DIV_MAX) {
@@ -151,9 +162,19 @@ static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
return -EINVAL;
}
+ if (pc->soc->pwm45_fixup && pwm->hwpwm > 2) {
+ /*
+ * PWM[4,5] has distinct offset for PWMDWIDTH and PWMTHRES
+ * from the other PWMs on MT7623.
+ */
+ reg_width = PWM45DWIDTH_FIXUP;
+ reg_thres = PWM45THRES_FIXUP;
+ }
+
+ cnt_duty = DIV_ROUND_CLOSEST_ULL((u64)duty_ns * 1000, resolution);
mtk_pwm_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | clkdiv);
- mtk_pwm_writel(pc, pwm->hwpwm, PWMDWIDTH, period_ns / resolution);
- mtk_pwm_writel(pc, pwm->hwpwm, PWMTHRES, duty_ns / resolution);
+ mtk_pwm_writel(pc, pwm->hwpwm, reg_width, cnt_period);
+ mtk_pwm_writel(pc, pwm->hwpwm, reg_thres, cnt_duty);
mtk_pwm_clk_disable(chip, pwm);
@@ -211,6 +232,7 @@ static int mtk_pwm_probe(struct platform_device *pdev)
data = of_device_get_match_data(&pdev->dev);
if (data == NULL)
return -EINVAL;
+ pc->soc = data;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
pc->regs = devm_ioremap_resource(&pdev->dev, res);
@@ -251,14 +273,17 @@ static int mtk_pwm_remove(struct platform_device *pdev)
static const struct mtk_pwm_platform_data mt2712_pwm_data = {
.num_pwms = 8,
+ .pwm45_fixup = false,
};
static const struct mtk_pwm_platform_data mt7622_pwm_data = {
.num_pwms = 6,
+ .pwm45_fixup = false,
};
static const struct mtk_pwm_platform_data mt7623_pwm_data = {
.num_pwms = 5,
+ .pwm45_fixup = true,
};
static const struct of_device_id mtk_pwm_of_match[] = {
@@ -280,5 +305,4 @@ static struct platform_driver mtk_pwm_driver = {
module_platform_driver(mtk_pwm_driver);
MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
-MODULE_ALIAS("platform:mtk-pwm");
MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-omap-dmtimer.c b/drivers/pwm/pwm-omap-dmtimer.c
index 5ad42f33e70c..665da3c8fbce 100644
--- a/drivers/pwm/pwm-omap-dmtimer.c
+++ b/drivers/pwm/pwm-omap-dmtimer.c
@@ -23,6 +23,7 @@
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_platform.h>
+#include <linux/platform_data/dmtimer-omap.h>
#include <linux/platform_data/pwm_omap_dmtimer.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@@ -37,7 +38,7 @@ struct pwm_omap_dmtimer_chip {
struct pwm_chip chip;
struct mutex mutex;
pwm_omap_dmtimer *dm_timer;
- struct pwm_omap_dmtimer_pdata *pdata;
+ const struct omap_dm_timer_ops *pdata;
struct platform_device *dm_timer_pdev;
};
@@ -242,19 +243,35 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device_node *timer;
+ struct platform_device *timer_pdev;
struct pwm_omap_dmtimer_chip *omap;
- struct pwm_omap_dmtimer_pdata *pdata;
+ struct dmtimer_platform_data *timer_pdata;
+ const struct omap_dm_timer_ops *pdata;
pwm_omap_dmtimer *dm_timer;
u32 v;
- int status;
+ int ret = 0;
- pdata = dev_get_platdata(&pdev->dev);
- if (!pdata) {
- dev_err(&pdev->dev, "Missing dmtimer platform data\n");
- return -EINVAL;
+ timer = of_parse_phandle(np, "ti,timers", 0);
+ if (!timer)
+ return -ENODEV;
+
+ timer_pdev = of_find_device_by_node(timer);
+ if (!timer_pdev) {
+ dev_err(&pdev->dev, "Unable to find Timer pdev\n");
+ ret = -ENODEV;
+ goto put;
}
- if (!pdata->request_by_node ||
+ timer_pdata = dev_get_platdata(&timer_pdev->dev);
+ if (!timer_pdata) {
+ dev_err(&pdev->dev, "dmtimer pdata structure NULL\n");
+ ret = -EINVAL;
+ goto put;
+ }
+
+ pdata = timer_pdata->timer_ops;
+
+ if (!pdata || !pdata->request_by_node ||
!pdata->free ||
!pdata->enable ||
!pdata->disable ||
@@ -267,21 +284,26 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev)
!pdata->set_prescaler ||
!pdata->write_counter) {
dev_err(&pdev->dev, "Incomplete dmtimer pdata structure\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto put;
}
- timer = of_parse_phandle(np, "ti,timers", 0);
- if (!timer)
- return -ENODEV;
-
if (!of_get_property(timer, "ti,timer-pwm", NULL)) {
dev_err(&pdev->dev, "Missing ti,timer-pwm capability\n");
- return -ENODEV;
+ ret = -ENODEV;
+ goto put;
}
dm_timer = pdata->request_by_node(timer);
- if (!dm_timer)
- return -EPROBE_DEFER;
+ if (!dm_timer) {
+ ret = -EPROBE_DEFER;
+ goto put;
+ }
+
+put:
+ of_node_put(timer);
+ if (ret < 0)
+ return ret;
omap = devm_kzalloc(&pdev->dev, sizeof(*omap), GFP_KERNEL);
if (!omap) {
@@ -291,13 +313,7 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev)
omap->pdata = pdata;
omap->dm_timer = dm_timer;
-
- omap->dm_timer_pdev = of_find_device_by_node(timer);
- if (!omap->dm_timer_pdev) {
- dev_err(&pdev->dev, "Unable to find timer pdev\n");
- omap->pdata->free(dm_timer);
- return -EINVAL;
- }
+ omap->dm_timer_pdev = timer_pdev;
/*
* Ensure that the timer is stopped before we allow PWM core to call
@@ -322,11 +338,11 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev)
mutex_init(&omap->mutex);
- status = pwmchip_add(&omap->chip);
- if (status < 0) {
+ ret = pwmchip_add(&omap->chip);
+ if (ret < 0) {
dev_err(&pdev->dev, "failed to register PWM\n");
omap->pdata->free(omap->dm_timer);
- return status;
+ return ret;
}
platform_set_drvdata(pdev, omap);
diff --git a/drivers/pwm/pwm-puv3.c b/drivers/pwm/pwm-puv3.c
index ed6007b27585..754fd9a98f6b 100644
--- a/drivers/pwm/pwm-puv3.c
+++ b/drivers/pwm/pwm-puv3.c
@@ -107,10 +107,8 @@ static int pwm_probe(struct platform_device *pdev)
int ret;
puv3 = devm_kzalloc(&pdev->dev, sizeof(*puv3), GFP_KERNEL);
- if (puv3 == NULL) {
- dev_err(&pdev->dev, "failed to allocate memory\n");
+ if (!puv3)
return -ENOMEM;
- }
puv3->clk = devm_clk_get(&pdev->dev, "OST_CLK");
if (IS_ERR(puv3->clk))
diff --git a/drivers/pwm/pwm-rcar.c b/drivers/pwm/pwm-rcar.c
index 1c85ecc9e7ac..91d11f2e2fef 100644
--- a/drivers/pwm/pwm-rcar.c
+++ b/drivers/pwm/pwm-rcar.c
@@ -134,16 +134,12 @@ static int rcar_pwm_set_counter(struct rcar_pwm_chip *rp, int div, int duty_ns,
static int rcar_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
{
- struct rcar_pwm_chip *rp = to_rcar_pwm_chip(chip);
-
- return clk_prepare_enable(rp->clk);
+ return pm_runtime_get_sync(chip->dev);
}
static void rcar_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
{
- struct rcar_pwm_chip *rp = to_rcar_pwm_chip(chip);
-
- clk_disable_unprepare(rp->clk);
+ pm_runtime_put(chip->dev);
}
static int rcar_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
@@ -156,8 +152,12 @@ static int rcar_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
if (div < 0)
return div;
- /* Let the core driver set pwm->period if disabled and duty_ns == 0 */
- if (!pwm_is_enabled(pwm) && !duty_ns)
+ /*
+ * Let the core driver set pwm->period if disabled and duty_ns == 0.
+ * But, this driver should prevent to set the new duty_ns if current
+ * duty_cycle is not set
+ */
+ if (!pwm_is_enabled(pwm) && !duty_ns && !pwm->state.duty_cycle)
return 0;
rcar_pwm_update(rp, RCAR_PWMCR_SYNC, RCAR_PWMCR_SYNC, RCAR_PWMCR);
@@ -258,11 +258,53 @@ static const struct of_device_id rcar_pwm_of_table[] = {
};
MODULE_DEVICE_TABLE(of, rcar_pwm_of_table);
+#ifdef CONFIG_PM_SLEEP
+static struct pwm_device *rcar_pwm_dev_to_pwm_dev(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rcar_pwm_chip *rcar_pwm = platform_get_drvdata(pdev);
+ struct pwm_chip *chip = &rcar_pwm->chip;
+
+ return &chip->pwms[0];
+}
+
+static int rcar_pwm_suspend(struct device *dev)
+{
+ struct pwm_device *pwm = rcar_pwm_dev_to_pwm_dev(dev);
+
+ if (!test_bit(PWMF_REQUESTED, &pwm->flags))
+ return 0;
+
+ pm_runtime_put(dev);
+
+ return 0;
+}
+
+static int rcar_pwm_resume(struct device *dev)
+{
+ struct pwm_device *pwm = rcar_pwm_dev_to_pwm_dev(dev);
+
+ if (!test_bit(PWMF_REQUESTED, &pwm->flags))
+ return 0;
+
+ pm_runtime_get_sync(dev);
+
+ rcar_pwm_config(pwm->chip, pwm, pwm->state.duty_cycle,
+ pwm->state.period);
+ if (pwm_is_enabled(pwm))
+ rcar_pwm_enable(pwm->chip, pwm);
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+static SIMPLE_DEV_PM_OPS(rcar_pwm_pm_ops, rcar_pwm_suspend, rcar_pwm_resume);
+
static struct platform_driver rcar_pwm_driver = {
.probe = rcar_pwm_probe,
.remove = rcar_pwm_remove,
.driver = {
.name = "pwm-rcar",
+ .pm = &rcar_pwm_pm_ops,
.of_match_table = of_match_ptr(rcar_pwm_of_table),
}
};
diff --git a/drivers/pwm/pwm-stm32-lp.c b/drivers/pwm/pwm-stm32-lp.c
index 1ac9e4384142..7c13e2505080 100644
--- a/drivers/pwm/pwm-stm32-lp.c
+++ b/drivers/pwm/pwm-stm32-lp.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* STM32 Low-Power Timer PWM driver
*
@@ -5,8 +6,6 @@
*
* Author: Gerald Baeza <gerald.baeza@st.com>
*
- * License terms: GNU General Public License (GPL), version 2
- *
* Inspired by Gerald Baeza's pwm-stm32 driver
*/
@@ -203,6 +202,8 @@ static int stm32_pwm_lp_probe(struct platform_device *pdev)
priv->chip.dev = &pdev->dev;
priv->chip.ops = &stm32_pwm_lp_ops;
priv->chip.npwm = 1;
+ priv->chip.of_xlate = of_pwm_xlate_with_flags;
+ priv->chip.of_pwm_n_cells = 3;
ret = pwmchip_add(&priv->chip);
if (ret < 0)
diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c
index 6139512aab7b..2708212933f7 100644
--- a/drivers/pwm/pwm-stm32.c
+++ b/drivers/pwm/pwm-stm32.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) STMicroelectronics 2016
*
* Author: Gerald Baeza <gerald.baeza@st.com>
*
- * License terms: GNU General Public License (GPL), version 2
- *
* Inspired by timer-stm32.c from Maxime Coquelin
* pwm-atmel.c from Bo Shen
*/
@@ -21,7 +20,7 @@
struct stm32_pwm {
struct pwm_chip chip;
- struct device *dev;
+ struct mutex lock; /* protect pwm config/enable */
struct clk *clk;
struct regmap *regmap;
u32 max_arr;
@@ -214,9 +213,23 @@ static int stm32_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
return ret;
}
+static int stm32_pwm_apply_locked(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct stm32_pwm *priv = to_stm32_pwm_dev(chip);
+ int ret;
+
+ /* protect common prescaler for all active channels */
+ mutex_lock(&priv->lock);
+ ret = stm32_pwm_apply(chip, pwm, state);
+ mutex_unlock(&priv->lock);
+
+ return ret;
+}
+
static const struct pwm_ops stm32pwm_ops = {
.owner = THIS_MODULE,
- .apply = stm32_pwm_apply,
+ .apply = stm32_pwm_apply_locked,
};
static int stm32_pwm_set_breakinput(struct stm32_pwm *priv,
@@ -336,6 +349,7 @@ static int stm32_pwm_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
+ mutex_init(&priv->lock);
priv->regmap = ddata->regmap;
priv->clk = ddata->clk;
priv->max_arr = ddata->max_arr;
diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c
index 334199c58f1d..470d4f71e7eb 100644
--- a/drivers/pwm/pwm-sun4i.c
+++ b/drivers/pwm/pwm-sun4i.c
@@ -73,7 +73,6 @@ static const u32 prescaler_table[] = {
struct sun4i_pwm_data {
bool has_prescaler_bypass;
- bool has_rdy;
unsigned int npwm;
};
@@ -117,7 +116,8 @@ static void sun4i_pwm_get_state(struct pwm_chip *chip,
val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
- if ((val == PWM_PRESCAL_MASK) && sun4i_pwm->data->has_prescaler_bypass)
+ if ((PWM_REG_PRESCAL(val, pwm->hwpwm) == PWM_PRESCAL_MASK) &&
+ sun4i_pwm->data->has_prescaler_bypass)
prescaler = 1;
else
prescaler = prescaler_table[PWM_REG_PRESCAL(val, pwm->hwpwm)];
@@ -130,7 +130,8 @@ static void sun4i_pwm_get_state(struct pwm_chip *chip,
else
state->polarity = PWM_POLARITY_INVERSED;
- if (val & BIT_CH(PWM_CLK_GATING | PWM_EN, pwm->hwpwm))
+ if ((val & BIT_CH(PWM_CLK_GATING | PWM_EN, pwm->hwpwm)) ==
+ BIT_CH(PWM_CLK_GATING | PWM_EN, pwm->hwpwm))
state->enabled = true;
else
state->enabled = false;
@@ -311,52 +312,37 @@ static const struct pwm_ops sun4i_pwm_ops = {
.owner = THIS_MODULE,
};
-static const struct sun4i_pwm_data sun4i_pwm_data_a10 = {
+static const struct sun4i_pwm_data sun4i_pwm_dual_nobypass = {
.has_prescaler_bypass = false,
- .has_rdy = false,
.npwm = 2,
};
-static const struct sun4i_pwm_data sun4i_pwm_data_a10s = {
+static const struct sun4i_pwm_data sun4i_pwm_dual_bypass = {
.has_prescaler_bypass = true,
- .has_rdy = true,
.npwm = 2,
};
-static const struct sun4i_pwm_data sun4i_pwm_data_a13 = {
+static const struct sun4i_pwm_data sun4i_pwm_single_bypass = {
.has_prescaler_bypass = true,
- .has_rdy = true,
- .npwm = 1,
-};
-
-static const struct sun4i_pwm_data sun4i_pwm_data_a20 = {
- .has_prescaler_bypass = true,
- .has_rdy = true,
- .npwm = 2,
-};
-
-static const struct sun4i_pwm_data sun4i_pwm_data_h3 = {
- .has_prescaler_bypass = true,
- .has_rdy = true,
.npwm = 1,
};
static const struct of_device_id sun4i_pwm_dt_ids[] = {
{
.compatible = "allwinner,sun4i-a10-pwm",
- .data = &sun4i_pwm_data_a10,
+ .data = &sun4i_pwm_dual_nobypass,
}, {
.compatible = "allwinner,sun5i-a10s-pwm",
- .data = &sun4i_pwm_data_a10s,
+ .data = &sun4i_pwm_dual_bypass,
}, {
.compatible = "allwinner,sun5i-a13-pwm",
- .data = &sun4i_pwm_data_a13,
+ .data = &sun4i_pwm_single_bypass,
}, {
.compatible = "allwinner,sun7i-a20-pwm",
- .data = &sun4i_pwm_data_a20,
+ .data = &sun4i_pwm_dual_bypass,
}, {
.compatible = "allwinner,sun8i-h3-pwm",
- .data = &sun4i_pwm_data_h3,
+ .data = &sun4i_pwm_single_bypass,
}, {
/* sentinel */
},
diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c
index 83f2b0b15712..7c71cdb8a9d8 100644
--- a/drivers/pwm/sysfs.c
+++ b/drivers/pwm/sysfs.c
@@ -273,7 +273,8 @@ static int pwm_export_child(struct device *parent, struct pwm_device *pwm)
ret = device_register(&export->child);
if (ret) {
clear_bit(PWMF_EXPORTED, &pwm->flags);
- kfree(export);
+ put_device(&export->child);
+ export = NULL;
return ret;
}
diff --git a/drivers/rapidio/devices/rio_mport_cdev.c b/drivers/rapidio/devices/rio_mport_cdev.c
index cfb54e01d758..0434ab7b6497 100644
--- a/drivers/rapidio/devices/rio_mport_cdev.c
+++ b/drivers/rapidio/devices/rio_mport_cdev.c
@@ -212,7 +212,6 @@ struct mport_cdev_priv {
#ifdef CONFIG_RAPIDIO_DMA_ENGINE
struct dma_chan *dmach;
struct list_head async_list;
- struct list_head pend_list;
spinlock_t req_lock;
struct mutex dma_lock;
struct kref dma_ref;
@@ -258,8 +257,6 @@ static DECLARE_WAIT_QUEUE_HEAD(mport_cdev_wait);
static struct class *dev_class;
static dev_t dev_number;
-static struct workqueue_struct *dma_wq;
-
static void mport_release_mapping(struct kref *ref);
static int rio_mport_maint_rd(struct mport_cdev_priv *priv, void __user *arg,
@@ -539,6 +536,7 @@ static int maint_comptag_set(struct mport_cdev_priv *priv, void __user *arg)
#ifdef CONFIG_RAPIDIO_DMA_ENGINE
struct mport_dma_req {
+ struct kref refcount;
struct list_head node;
struct file *filp;
struct mport_cdev_priv *priv;
@@ -554,11 +552,6 @@ struct mport_dma_req {
struct completion req_comp;
};
-struct mport_faf_work {
- struct work_struct work;
- struct mport_dma_req *req;
-};
-
static void mport_release_def_dma(struct kref *dma_ref)
{
struct mport_dev *md =
@@ -578,8 +571,10 @@ static void mport_release_dma(struct kref *dma_ref)
complete(&priv->comp);
}
-static void dma_req_free(struct mport_dma_req *req)
+static void dma_req_free(struct kref *ref)
{
+ struct mport_dma_req *req = container_of(ref, struct mport_dma_req,
+ refcount);
struct mport_cdev_priv *priv = req->priv;
unsigned int i;
@@ -611,30 +606,7 @@ static void dma_xfer_callback(void *param)
req->status = dma_async_is_tx_complete(priv->dmach, req->cookie,
NULL, NULL);
complete(&req->req_comp);
-}
-
-static void dma_faf_cleanup(struct work_struct *_work)
-{
- struct mport_faf_work *work = container_of(_work,
- struct mport_faf_work, work);
- struct mport_dma_req *req = work->req;
-
- dma_req_free(req);
- kfree(work);
-}
-
-static void dma_faf_callback(void *param)
-{
- struct mport_dma_req *req = (struct mport_dma_req *)param;
- struct mport_faf_work *work;
-
- work = kmalloc(sizeof(*work), GFP_ATOMIC);
- if (!work)
- return;
-
- INIT_WORK(&work->work, dma_faf_cleanup);
- work->req = req;
- queue_work(dma_wq, &work->work);
+ kref_put(&req->refcount, dma_req_free);
}
/*
@@ -765,16 +737,11 @@ static int do_dma_request(struct mport_dma_req *req,
goto err_out;
}
- if (sync == RIO_TRANSFER_FAF)
- tx->callback = dma_faf_callback;
- else
- tx->callback = dma_xfer_callback;
+ tx->callback = dma_xfer_callback;
tx->callback_param = req;
- req->dmach = chan;
- req->sync = sync;
req->status = DMA_IN_PROGRESS;
- init_completion(&req->req_comp);
+ kref_get(&req->refcount);
cookie = dmaengine_submit(tx);
req->cookie = cookie;
@@ -785,6 +752,7 @@ static int do_dma_request(struct mport_dma_req *req,
if (dma_submit_error(cookie)) {
rmcd_error("submit err=%d (addr:0x%llx len:0x%llx)",
cookie, xfer->rio_addr, xfer->length);
+ kref_put(&req->refcount, dma_req_free);
ret = -EIO;
goto err_out;
}
@@ -865,6 +833,15 @@ rio_dma_transfer(struct file *filp, u32 transfer_mode,
kfree(req);
return ret;
}
+ chan = priv->dmach;
+
+ kref_init(&req->refcount);
+ init_completion(&req->req_comp);
+ req->dir = dir;
+ req->filp = filp;
+ req->priv = priv;
+ req->dmach = chan;
+ req->sync = sync;
/*
* If parameter loc_addr != NULL, we are transferring data from/to
@@ -952,11 +929,6 @@ rio_dma_transfer(struct file *filp, u32 transfer_mode,
xfer->offset, xfer->length);
}
- req->dir = dir;
- req->filp = filp;
- req->priv = priv;
- chan = priv->dmach;
-
nents = dma_map_sg(chan->device->dev,
req->sgt.sgl, req->sgt.nents, dir);
if (nents == 0) {
@@ -968,42 +940,20 @@ rio_dma_transfer(struct file *filp, u32 transfer_mode,
ret = do_dma_request(req, xfer, sync, nents);
if (ret >= 0) {
- if (sync == RIO_TRANSFER_SYNC)
- goto sync_out;
- return ret; /* return ASYNC cookie */
- }
-
- if (ret == -ETIMEDOUT || ret == -EINTR) {
- /*
- * This can happen only in case of SYNC transfer.
- * Do not free unfinished request structure immediately.
- * Place it into pending list and deal with it later
- */
- spin_lock(&priv->req_lock);
- list_add_tail(&req->node, &priv->pend_list);
- spin_unlock(&priv->req_lock);
- return ret;
+ if (sync == RIO_TRANSFER_ASYNC)
+ return ret; /* return ASYNC cookie */
+ } else {
+ rmcd_debug(DMA, "do_dma_request failed with err=%d", ret);
}
-
- rmcd_debug(DMA, "do_dma_request failed with err=%d", ret);
-sync_out:
- dma_unmap_sg(chan->device->dev, req->sgt.sgl, req->sgt.nents, dir);
- sg_free_table(&req->sgt);
err_pg:
- if (page_list) {
+ if (!req->page_list) {
for (i = 0; i < nr_pages; i++)
put_page(page_list[i]);
kfree(page_list);
}
err_req:
- if (req->map) {
- mutex_lock(&md->buf_mutex);
- kref_put(&req->map->ref, mport_release_mapping);
- mutex_unlock(&md->buf_mutex);
- }
- put_dma_channel(priv);
- kfree(req);
+ kref_put(&req->refcount, dma_req_free);
return ret;
}
@@ -1121,7 +1071,7 @@ static int rio_mport_wait_for_async_dma(struct file *filp, void __user *arg)
ret = 0;
if (req->status != DMA_IN_PROGRESS && req->status != DMA_PAUSED)
- dma_req_free(req);
+ kref_put(&req->refcount, dma_req_free);
return ret;
@@ -1966,7 +1916,6 @@ static int mport_cdev_open(struct inode *inode, struct file *filp)
#ifdef CONFIG_RAPIDIO_DMA_ENGINE
INIT_LIST_HEAD(&priv->async_list);
- INIT_LIST_HEAD(&priv->pend_list);
spin_lock_init(&priv->req_lock);
mutex_init(&priv->dma_lock);
#endif
@@ -2006,8 +1955,6 @@ static void mport_cdev_release_dma(struct file *filp)
md = priv->md;
- flush_workqueue(dma_wq);
-
spin_lock(&priv->req_lock);
if (!list_empty(&priv->async_list)) {
rmcd_debug(EXIT, "async list not empty filp=%p %s(%d)",
@@ -2023,20 +1970,7 @@ static void mport_cdev_release_dma(struct file *filp)
req->filp, req->cookie,
completion_done(&req->req_comp)?"yes":"no");
list_del(&req->node);
- dma_req_free(req);
- }
- }
-
- if (!list_empty(&priv->pend_list)) {
- rmcd_debug(EXIT, "Free pending DMA requests for filp=%p %s(%d)",
- filp, current->comm, task_pid_nr(current));
- list_for_each_entry_safe(req,
- req_next, &priv->pend_list, node) {
- rmcd_debug(EXIT, "free req->filp=%p cookie=%d compl=%s",
- req->filp, req->cookie,
- completion_done(&req->req_comp)?"yes":"no");
- list_del(&req->node);
- dma_req_free(req);
+ kref_put(&req->refcount, dma_req_free);
}
}
@@ -2048,15 +1982,6 @@ static void mport_cdev_release_dma(struct file *filp)
current->comm, task_pid_nr(current), wret);
}
- spin_lock(&priv->req_lock);
-
- if (!list_empty(&priv->pend_list)) {
- rmcd_debug(EXIT, "ATTN: pending DMA requests, filp=%p %s(%d)",
- filp, current->comm, task_pid_nr(current));
- }
-
- spin_unlock(&priv->req_lock);
-
if (priv->dmach != priv->md->dma_chan) {
rmcd_debug(EXIT, "Release DMA channel for filp=%p %s(%d)",
filp, current->comm, task_pid_nr(current));
@@ -2573,8 +2498,6 @@ static void mport_cdev_remove(struct mport_dev *md)
cdev_device_del(&md->cdev, &md->dev);
mport_cdev_kill_fasync(md);
- flush_workqueue(dma_wq);
-
/* TODO: do we need to give clients some time to close file
* descriptors? Simple wait for XX, or kref?
*/
@@ -2691,17 +2614,8 @@ static int __init mport_init(void)
goto err_cli;
}
- dma_wq = create_singlethread_workqueue("dma_wq");
- if (!dma_wq) {
- rmcd_error("failed to create DMA work queue");
- ret = -ENOMEM;
- goto err_wq;
- }
-
return 0;
-err_wq:
- class_interface_unregister(&rio_mport_interface);
err_cli:
unregister_chrdev_region(dev_number, RIO_MAX_MPORTS);
err_chr:
@@ -2717,7 +2631,6 @@ static void __exit mport_exit(void)
class_interface_unregister(&rio_mport_interface);
class_destroy(dev_class);
unregister_chrdev_region(dev_number, RIO_MAX_MPORTS);
- destroy_workqueue(dma_wq);
}
module_init(mport_init);
diff --git a/drivers/rapidio/devices/tsi721.c b/drivers/rapidio/devices/tsi721.c
index 9a68914100ad..bb655854713d 100644
--- a/drivers/rapidio/devices/tsi721.c
+++ b/drivers/rapidio/devices/tsi721.c
@@ -2880,8 +2880,9 @@ static int tsi721_probe(struct pci_dev *pdev,
"Invalid MRRS override value %d", pcie_mrrs);
}
- /* Adjust PCIe completion timeout. */
- pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL2, 0xf, 0x2);
+ /* Set PCIe completion timeout to 1-10ms */
+ pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL2,
+ PCI_EXP_DEVCTL2_COMP_TIMEOUT, 0x2);
/*
* FIXUP: correct offsets of MSI-X tables in the MSI-X Capability Block
diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c
index 23429bdaca84..161b927d9de1 100644
--- a/drivers/rapidio/rio-scan.c
+++ b/drivers/rapidio/rio-scan.c
@@ -76,7 +76,7 @@ static u16 rio_destid_alloc(struct rio_net *net)
}
/**
- * rio_destid_reserve - Reserve the specivied destID
+ * rio_destid_reserve - Reserve the specified destID
* @net: RIO network
* @destid: destID to reserve
*
@@ -885,7 +885,7 @@ static struct rio_net *rio_scan_alloc_net(struct rio_mport *mport,
*
* For each enumerated device, ensure that each switch in a system
* has correct routing entries. Add routes for devices that where
- * unknown dirung the first enumeration pass through the switch.
+ * unknown during the first enumeration pass through the switch.
*/
static void rio_update_route_tables(struct rio_net *net)
{
@@ -983,7 +983,7 @@ static int rio_enum_mport(struct rio_mport *mport, u32 flags)
/* reserve mport destID in new net */
rio_destid_reserve(net, mport->host_deviceid);
- /* Enable Input Output Port (transmitter reviever) */
+ /* Enable Input Output Port (transmitter receiver) */
rio_enable_rx_tx_port(mport, 1, 0, 0, 0);
/* Set component tag for host */
diff --git a/drivers/regulator/88pg86x.c b/drivers/regulator/88pg86x.c
new file mode 100644
index 000000000000..d5ef55c81185
--- /dev/null
+++ b/drivers/regulator/88pg86x.c
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/of.h>
+#include <linux/regulator/driver.h>
+#include <linux/regmap.h>
+
+static const struct regulator_ops pg86x_ops = {
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear_range,
+};
+
+static const struct regulator_linear_range pg86x_buck1_ranges[] = {
+ REGULATOR_LINEAR_RANGE( 0, 0, 10, 0),
+ REGULATOR_LINEAR_RANGE(1000000, 11, 34, 25000),
+ REGULATOR_LINEAR_RANGE(1600000, 35, 47, 50000),
+};
+
+static const struct regulator_linear_range pg86x_buck2_ranges[] = {
+ REGULATOR_LINEAR_RANGE( 0, 0, 15, 0),
+ REGULATOR_LINEAR_RANGE(1000000, 16, 39, 25000),
+ REGULATOR_LINEAR_RANGE(1600000, 40, 52, 50000),
+};
+
+static const struct regulator_desc pg86x_regulators[] = {
+ {
+ .id = 0,
+ .type = REGULATOR_VOLTAGE,
+ .name = "buck1",
+ .of_match = of_match_ptr("buck1"),
+ .n_voltages = 11 + 24 + 13,
+ .linear_ranges = pg86x_buck1_ranges,
+ .n_linear_ranges = 3,
+ .vsel_reg = 0x24,
+ .vsel_mask = 0xff,
+ .ops = &pg86x_ops,
+ .owner = THIS_MODULE
+ },
+ {
+ .id = 1,
+ .type = REGULATOR_VOLTAGE,
+ .name = "buck2",
+ .of_match = of_match_ptr("buck2"),
+ .n_voltages = 16 + 24 + 13,
+ .linear_ranges = pg86x_buck2_ranges,
+ .n_linear_ranges = 3,
+ .vsel_reg = 0x13,
+ .vsel_mask = 0xff,
+ .ops = &pg86x_ops,
+ .owner = THIS_MODULE
+ },
+};
+
+static const struct regmap_config pg86x_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int pg86x_i2c_probe(struct i2c_client *i2c)
+{
+ int id, ret;
+ struct regulator_config config = {.dev = &i2c->dev};
+ struct regmap *regmap = devm_regmap_init_i2c(i2c, &pg86x_regmap);
+
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ dev_err(&i2c->dev, "regmap init failed: %d\n", ret);
+ return ret;
+ }
+
+ for (id = 0; id < ARRAY_SIZE(pg86x_regulators); id++) {
+ struct regulator_dev *rdev;
+ rdev = devm_regulator_register(&i2c->dev,
+ &pg86x_regulators[id],
+ &config);
+ if (IS_ERR(rdev)) {
+ ret = PTR_ERR(rdev);
+ dev_err(&i2c->dev, "failed to register %s: %d\n",
+ pg86x_regulators[id].name, ret);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static const struct of_device_id pg86x_dt_ids [] = {
+ { .compatible = "marvell,88pg867" },
+ { .compatible = "marvell,88pg868" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pg86x_dt_ids);
+
+static const struct i2c_device_id pg86x_i2c_id[] = {
+ { "88pg867", },
+ { "88pg868", },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pg86x_i2c_id);
+
+static struct i2c_driver pg86x_regulator_driver = {
+ .driver = {
+ .name = "88pg86x",
+ .of_match_table = of_match_ptr(pg86x_dt_ids),
+ },
+ .probe_new = pg86x_i2c_probe,
+ .id_table = pg86x_i2c_id,
+};
+
+module_i2c_driver(pg86x_regulator_driver);
+
+MODULE_DESCRIPTION("Marvell 88PG86X voltage regulator");
+MODULE_AUTHOR("Alexander Monakov <amonakov@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index b27417ca188a..097f61784a7d 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -54,6 +54,15 @@ config REGULATOR_USERSPACE_CONSUMER
If unsure, say no.
+config REGULATOR_88PG86X
+ tristate "Marvell 88PG86X voltage regulators"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ This driver supports Marvell 88PG867 and 88PG868 voltage regulators.
+ They provide two I2C-controlled DC/DC step-down converters with
+ sleep mode and separate enable pins.
+
config REGULATOR_88PM800
tristate "Marvell 88PM800 Power regulators"
depends on MFD_88PM800
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 19fea09ba10a..590674fbecd7 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o
obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o
obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o
+obj-$(CONFIG_REGULATOR_88PG86X) += 88pg86x.o
obj-$(CONFIG_REGULATOR_88PM800) += 88pm800.o
obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o
obj-$(CONFIG_REGULATOR_CPCAP) += cpcap-regulator.o
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 1fc0c0811da4..d4803460a557 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -1937,7 +1937,10 @@ static int regulator_ena_gpio_request(struct regulator_dev *rdev,
struct gpio_desc *gpiod;
int ret;
- gpiod = gpio_to_desc(config->ena_gpio);
+ if (config->ena_gpiod)
+ gpiod = config->ena_gpiod;
+ else
+ gpiod = gpio_to_desc(config->ena_gpio);
list_for_each_entry(pin, &regulator_ena_gpio_list, list) {
if (pin->gpiod == gpiod) {
@@ -1947,15 +1950,18 @@ static int regulator_ena_gpio_request(struct regulator_dev *rdev,
}
}
- ret = gpio_request_one(config->ena_gpio,
- GPIOF_DIR_OUT | config->ena_gpio_flags,
- rdev_get_name(rdev));
- if (ret)
- return ret;
+ if (!config->ena_gpiod) {
+ ret = gpio_request_one(config->ena_gpio,
+ GPIOF_DIR_OUT | config->ena_gpio_flags,
+ rdev_get_name(rdev));
+ if (ret)
+ return ret;
+ }
pin = kzalloc(sizeof(struct regulator_enable_gpio), GFP_KERNEL);
if (pin == NULL) {
- gpio_free(config->ena_gpio);
+ if (!config->ena_gpiod)
+ gpio_free(config->ena_gpio);
return -ENOMEM;
}
@@ -4154,8 +4160,9 @@ regulator_register(const struct regulator_desc *regulator_desc,
goto clean;
}
- if ((config->ena_gpio || config->ena_gpio_initialized) &&
- gpio_is_valid(config->ena_gpio)) {
+ if (config->ena_gpiod ||
+ ((config->ena_gpio || config->ena_gpio_initialized) &&
+ gpio_is_valid(config->ena_gpio))) {
mutex_lock(&regulator_list_mutex);
ret = regulator_ena_gpio_request(rdev, config);
mutex_unlock(&regulator_list_mutex);
@@ -4301,6 +4308,7 @@ static int regulator_suspend_late(struct device *dev)
return class_for_each_device(&regulator_class, NULL, &state,
_regulator_suspend_late);
}
+
static int _regulator_resume_early(struct device *dev, void *data)
{
int ret = 0;
diff --git a/drivers/regulator/da9055-regulator.c b/drivers/regulator/da9055-regulator.c
index d029c941a1e1..f40c3b8644ae 100644
--- a/drivers/regulator/da9055-regulator.c
+++ b/drivers/regulator/da9055-regulator.c
@@ -16,6 +16,7 @@
#include <linux/init.h>
#include <linux/err.h>
#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
@@ -455,8 +456,7 @@ static int da9055_gpio_init(struct da9055_regulator *regulator,
char name[18];
int gpio_mux = pdata->gpio_ren[id];
- config->ena_gpio = pdata->ena_gpio[id];
- config->ena_gpio_flags = GPIOF_OUT_INIT_HIGH;
+ config->ena_gpiod = pdata->ena_gpiods[id];
config->ena_gpio_invert = 1;
/*
diff --git a/drivers/regulator/da9211-regulator.c b/drivers/regulator/da9211-regulator.c
index 9b8f47617724..6c122b3df5d0 100644
--- a/drivers/regulator/da9211-regulator.c
+++ b/drivers/regulator/da9211-regulator.c
@@ -15,7 +15,6 @@
*/
#include <linux/err.h>
-#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/init.h>
@@ -25,7 +24,7 @@
#include <linux/regmap.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
-#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/da9211.h>
#include "da9211-regulator.h"
@@ -294,9 +293,12 @@ static struct da9211_pdata *da9211_parse_regulators_dt(
pdata->init_data[n] = da9211_matches[i].init_data;
pdata->reg_node[n] = da9211_matches[i].of_node;
- pdata->gpio_ren[n] =
- of_get_named_gpio(da9211_matches[i].of_node,
- "enable-gpios", 0);
+ pdata->gpiod_ren[n] = devm_gpiod_get_from_of_node(dev,
+ da9211_matches[i].of_node,
+ "enable",
+ 0,
+ GPIOD_OUT_HIGH,
+ "da9211-enable");
n++;
}
@@ -382,13 +384,10 @@ static int da9211_regulator_init(struct da9211 *chip)
config.regmap = chip->regmap;
config.of_node = chip->pdata->reg_node[i];
- if (gpio_is_valid(chip->pdata->gpio_ren[i])) {
- config.ena_gpio = chip->pdata->gpio_ren[i];
- config.ena_gpio_initialized = true;
- } else {
- config.ena_gpio = -EINVAL;
- config.ena_gpio_initialized = false;
- }
+ if (chip->pdata->gpiod_ren[i])
+ config.ena_gpiod = chip->pdata->gpiod_ren[i];
+ else
+ config.ena_gpiod = NULL;
chip->rdev[i] = devm_regulator_register(chip->dev,
&da9211_regulators[i], &config);
diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c
index 0fce06acfaec..a86b8997bb54 100644
--- a/drivers/regulator/gpio-regulator.c
+++ b/drivers/regulator/gpio-regulator.c
@@ -196,6 +196,7 @@ of_get_gpio_regulator_config(struct device *dev, struct device_node *np,
break;
}
config->gpios[i].gpio = gpio;
+ config->gpios[i].label = config->supply_name;
if (proplen > 0) {
of_property_read_u32_index(np, "gpios-states",
i, &ret);
@@ -271,8 +272,7 @@ static int gpio_regulator_probe(struct platform_device *pdev)
drvdata->desc.name = kstrdup(config->supply_name, GFP_KERNEL);
if (drvdata->desc.name == NULL) {
dev_err(&pdev->dev, "Failed to allocate supply name\n");
- ret = -ENOMEM;
- goto err;
+ return -ENOMEM;
}
if (config->nr_gpios != 0) {
@@ -292,7 +292,7 @@ static int gpio_regulator_probe(struct platform_device *pdev)
dev_err(&pdev->dev,
"Could not obtain regulator setting GPIOs: %d\n",
ret);
- goto err_memstate;
+ goto err_memgpio;
}
}
@@ -303,7 +303,7 @@ static int gpio_regulator_probe(struct platform_device *pdev)
if (drvdata->states == NULL) {
dev_err(&pdev->dev, "Failed to allocate state data\n");
ret = -ENOMEM;
- goto err_memgpio;
+ goto err_stategpio;
}
drvdata->nr_states = config->nr_states;
@@ -324,7 +324,7 @@ static int gpio_regulator_probe(struct platform_device *pdev)
default:
dev_err(&pdev->dev, "No regulator type set\n");
ret = -EINVAL;
- goto err_memgpio;
+ goto err_memstate;
}
/* build initial state from gpio init data. */
@@ -361,22 +361,21 @@ static int gpio_regulator_probe(struct platform_device *pdev)
if (IS_ERR(drvdata->dev)) {
ret = PTR_ERR(drvdata->dev);
dev_err(&pdev->dev, "Failed to register regulator: %d\n", ret);
- goto err_stategpio;
+ goto err_memstate;
}
platform_set_drvdata(pdev, drvdata);
return 0;
-err_stategpio:
- gpio_free_array(drvdata->gpios, drvdata->nr_gpios);
err_memstate:
kfree(drvdata->states);
+err_stategpio:
+ gpio_free_array(drvdata->gpios, drvdata->nr_gpios);
err_memgpio:
kfree(drvdata->gpios);
err_name:
kfree(drvdata->desc.name);
-err:
return ret;
}
diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c
index 092ed6efb3ec..f47264fa1940 100644
--- a/drivers/regulator/of_regulator.c
+++ b/drivers/regulator/of_regulator.c
@@ -321,6 +321,7 @@ int of_regulator_match(struct device *dev, struct device_node *node,
dev_err(dev,
"failed to parse DT for regulator %s\n",
child->name);
+ of_node_put(child);
return -EINVAL;
}
match->of_node = of_node_get(child);
diff --git a/drivers/regulator/qcom_smd-regulator.c b/drivers/regulator/qcom_smd-regulator.c
index 940fe1b78411..fe2fb36803e0 100644
--- a/drivers/regulator/qcom_smd-regulator.c
+++ b/drivers/regulator/qcom_smd-regulator.c
@@ -17,8 +17,6 @@
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
-#include <linux/regulator/machine.h>
-#include <linux/regulator/of_regulator.h>
#include <linux/soc/qcom/smd-rpm.h>
struct qcom_rpm_reg {
@@ -165,6 +163,15 @@ static const struct regulator_ops rpm_switch_ops = {
.is_enabled = rpm_reg_is_enabled,
};
+static const struct regulator_ops rpm_bob_ops = {
+ .enable = rpm_reg_enable,
+ .disable = rpm_reg_disable,
+ .is_enabled = rpm_reg_is_enabled,
+
+ .get_voltage = rpm_reg_get_voltage,
+ .set_voltage = rpm_reg_set_voltage,
+};
+
static const struct regulator_desc pma8084_hfsmps = {
.linear_ranges = (struct regulator_linear_range[]) {
REGULATOR_LINEAR_RANGE(375000, 0, 95, 12500),
@@ -355,6 +362,64 @@ static const struct regulator_desc pm8994_lnldo = {
.ops = &rpm_smps_ldo_ops_fixed,
};
+static const struct regulator_desc pm8998_ftsmps = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(320000, 0, 258, 4000),
+ },
+ .n_linear_ranges = 1,
+ .n_voltages = 259,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pm8998_hfsmps = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(320000, 0, 215, 8000),
+ },
+ .n_linear_ranges = 1,
+ .n_voltages = 216,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pm8998_nldo = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(312000, 0, 127, 8000),
+ },
+ .n_linear_ranges = 1,
+ .n_voltages = 128,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pm8998_pldo = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(1664000, 0, 255, 8000),
+ },
+ .n_linear_ranges = 1,
+ .n_voltages = 256,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pm8998_pldo_lv = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(1256000, 0, 127, 8000),
+ },
+ .n_linear_ranges = 1,
+ .n_voltages = 128,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pm8998_switch = {
+ .ops = &rpm_switch_ops,
+};
+
+static const struct regulator_desc pmi8998_bob = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(1824000, 0, 83, 32000),
+ },
+ .n_linear_ranges = 1,
+ .n_voltages = 84,
+ .ops = &rpm_bob_ops,
+};
+
struct rpm_regulator_data {
const char *name;
u32 type;
@@ -544,12 +609,66 @@ static const struct rpm_regulator_data rpm_pm8994_regulators[] = {
{}
};
+static const struct rpm_regulator_data rpm_pm8998_regulators[] = {
+ { "s1", QCOM_SMD_RPM_SMPA, 1, &pm8998_ftsmps, "vdd_s1" },
+ { "s2", QCOM_SMD_RPM_SMPA, 2, &pm8998_ftsmps, "vdd_s2" },
+ { "s3", QCOM_SMD_RPM_SMPA, 3, &pm8998_hfsmps, "vdd_s3" },
+ { "s4", QCOM_SMD_RPM_SMPA, 4, &pm8998_hfsmps, "vdd_s4" },
+ { "s5", QCOM_SMD_RPM_SMPA, 5, &pm8998_hfsmps, "vdd_s5" },
+ { "s6", QCOM_SMD_RPM_SMPA, 6, &pm8998_ftsmps, "vdd_s6" },
+ { "s7", QCOM_SMD_RPM_SMPA, 7, &pm8998_ftsmps, "vdd_s7" },
+ { "s8", QCOM_SMD_RPM_SMPA, 8, &pm8998_ftsmps, "vdd_s8" },
+ { "s9", QCOM_SMD_RPM_SMPA, 9, &pm8998_ftsmps, "vdd_s9" },
+ { "s10", QCOM_SMD_RPM_SMPA, 10, &pm8998_ftsmps, "vdd_s10" },
+ { "s11", QCOM_SMD_RPM_SMPA, 11, &pm8998_ftsmps, "vdd_s11" },
+ { "s12", QCOM_SMD_RPM_SMPA, 12, &pm8998_ftsmps, "vdd_s12" },
+ { "s13", QCOM_SMD_RPM_SMPA, 13, &pm8998_ftsmps, "vdd_s13" },
+ { "l1", QCOM_SMD_RPM_LDOA, 1, &pm8998_nldo, "vdd_l1_l27" },
+ { "l2", QCOM_SMD_RPM_LDOA, 2, &pm8998_nldo, "vdd_l2_l8_l17" },
+ { "l3", QCOM_SMD_RPM_LDOA, 3, &pm8998_nldo, "vdd_l3_l11" },
+ { "l4", QCOM_SMD_RPM_LDOA, 4, &pm8998_nldo, "vdd_l4_l5" },
+ { "l5", QCOM_SMD_RPM_LDOA, 5, &pm8998_nldo, "vdd_l4_l5" },
+ { "l6", QCOM_SMD_RPM_LDOA, 6, &pm8998_pldo, "vdd_l6" },
+ { "l7", QCOM_SMD_RPM_LDOA, 7, &pm8998_pldo_lv, "vdd_l7_l12_l14_l15" },
+ { "l8", QCOM_SMD_RPM_LDOA, 8, &pm8998_nldo, "vdd_l2_l8_l17" },
+ { "l9", QCOM_SMD_RPM_LDOA, 9, &pm8998_pldo, "vdd_l9" },
+ { "l10", QCOM_SMD_RPM_LDOA, 10, &pm8998_pldo, "vdd_l10_l23_l25" },
+ { "l11", QCOM_SMD_RPM_LDOA, 11, &pm8998_nldo, "vdd_l3_l11" },
+ { "l12", QCOM_SMD_RPM_LDOA, 12, &pm8998_pldo_lv, "vdd_l7_l12_l14_l15" },
+ { "l13", QCOM_SMD_RPM_LDOA, 13, &pm8998_pldo, "vdd_l13_l19_l21" },
+ { "l14", QCOM_SMD_RPM_LDOA, 14, &pm8998_pldo_lv, "vdd_l7_l12_l14_l15" },
+ { "l15", QCOM_SMD_RPM_LDOA, 15, &pm8998_pldo_lv, "vdd_l7_l12_l14_l15" },
+ { "l16", QCOM_SMD_RPM_LDOA, 16, &pm8998_pldo, "vdd_l16_l28" },
+ { "l17", QCOM_SMD_RPM_LDOA, 17, &pm8998_nldo, "vdd_l2_l8_l17" },
+ { "l18", QCOM_SMD_RPM_LDOA, 18, &pm8998_pldo, "vdd_l18_l22" },
+ { "l19", QCOM_SMD_RPM_LDOA, 19, &pm8998_pldo, "vdd_l13_l19_l21" },
+ { "l20", QCOM_SMD_RPM_LDOA, 20, &pm8998_pldo, "vdd_l20_l24" },
+ { "l21", QCOM_SMD_RPM_LDOA, 21, &pm8998_pldo, "vdd_l13_l19_l21" },
+ { "l22", QCOM_SMD_RPM_LDOA, 22, &pm8998_pldo, "vdd_l18_l22" },
+ { "l23", QCOM_SMD_RPM_LDOA, 23, &pm8998_pldo, "vdd_l10_l23_l25" },
+ { "l24", QCOM_SMD_RPM_LDOA, 24, &pm8998_pldo, "vdd_l20_l24" },
+ { "l25", QCOM_SMD_RPM_LDOA, 25, &pm8998_pldo, "vdd_l10_l23_l25" },
+ { "l26", QCOM_SMD_RPM_LDOA, 26, &pm8998_nldo, "vdd_l26" },
+ { "l27", QCOM_SMD_RPM_LDOA, 27, &pm8998_nldo, "vdd_l1_l27" },
+ { "l28", QCOM_SMD_RPM_LDOA, 28, &pm8998_pldo, "vdd_l16_l28" },
+ { "lvs1", QCOM_SMD_RPM_VSA, 1, &pm8998_switch, "vdd_lvs1_lvs2" },
+ { "lvs2", QCOM_SMD_RPM_VSA, 2, &pm8998_switch, "vdd_lvs1_lvs2" },
+ {}
+};
+
+static const struct rpm_regulator_data rpm_pmi8998_regulators[] = {
+ { "bob", QCOM_SMD_RPM_BOBB, 1, &pmi8998_bob, "vdd_bob" },
+ {}
+};
+
static const struct of_device_id rpm_of_match[] = {
{ .compatible = "qcom,rpm-pm8841-regulators", .data = &rpm_pm8841_regulators },
{ .compatible = "qcom,rpm-pm8916-regulators", .data = &rpm_pm8916_regulators },
{ .compatible = "qcom,rpm-pm8941-regulators", .data = &rpm_pm8941_regulators },
{ .compatible = "qcom,rpm-pm8994-regulators", .data = &rpm_pm8994_regulators },
+ { .compatible = "qcom,rpm-pm8998-regulators", .data = &rpm_pm8998_regulators },
{ .compatible = "qcom,rpm-pma8084-regulators", .data = &rpm_pma8084_regulators },
+ { .compatible = "qcom,rpm-pmi8998-regulators", .data = &rpm_pmi8998_regulators },
{}
};
MODULE_DEVICE_TABLE(of, rpm_of_match);
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index b609e1d3654b..027274008b08 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -6,6 +6,7 @@ config REMOTEPROC
select CRC32
select FW_LOADER
select VIRTIO
+ select WANT_DEV_COREDUMP
help
Support for remote processors (such as DSP coprocessors). These
are mainly used on embedded systems.
@@ -90,6 +91,7 @@ config QCOM_ADSP_PIL
depends on QCOM_SMEM
depends on RPMSG_QCOM_SMD || (COMPILE_TEST && RPMSG_QCOM_SMD=n)
depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n
+ depends on QCOM_SYSMON || QCOM_SYSMON=n
select MFD_SYSCON
select QCOM_MDT_LOADER
select QCOM_RPROC_COMMON
@@ -107,6 +109,7 @@ config QCOM_Q6V5_PIL
depends on QCOM_SMEM
depends on RPMSG_QCOM_SMD || (COMPILE_TEST && RPMSG_QCOM_SMD=n)
depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n
+ depends on QCOM_SYSMON || QCOM_SYSMON=n
select MFD_SYSCON
select QCOM_RPROC_COMMON
select QCOM_SCM
@@ -114,12 +117,28 @@ config QCOM_Q6V5_PIL
Say y here to support the Qualcomm Peripherial Image Loader for the
Hexagon V5 based remote processors.
+config QCOM_SYSMON
+ tristate "Qualcomm sysmon driver"
+ depends on RPMSG
+ depends on ARCH_QCOM
+ depends on NET
+ select QCOM_QMI_HELPERS
+ help
+ The sysmon driver implements a sysmon QMI client and a handler for
+ the sys_mon SMD and GLINK channel, which are used for graceful
+ shutdown, retrieving failure information and propagating information
+ about other subsystems being shut down.
+
+ Say y here if your system runs firmware on any other subsystems, e.g.
+ modem or DSP.
+
config QCOM_WCNSS_PIL
tristate "Qualcomm WCNSS Peripheral Image Loader"
depends on OF && ARCH_QCOM
depends on RPMSG_QCOM_SMD || (COMPILE_TEST && RPMSG_QCOM_SMD=n)
depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n
depends on QCOM_SMEM
+ depends on QCOM_SYSMON || QCOM_SYSMON=n
select QCOM_MDT_LOADER
select QCOM_RPROC_COMMON
select QCOM_SCM
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index 6e16450ce11f..02627ede8d4a 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_KEYSTONE_REMOTEPROC) += keystone_remoteproc.o
obj-$(CONFIG_QCOM_ADSP_PIL) += qcom_adsp_pil.o
obj-$(CONFIG_QCOM_RPROC_COMMON) += qcom_common.o
obj-$(CONFIG_QCOM_Q6V5_PIL) += qcom_q6v5_pil.o
+obj-$(CONFIG_QCOM_SYSMON) += qcom_sysmon.o
obj-$(CONFIG_QCOM_WCNSS_PIL) += qcom_wcnss_pil.o
qcom_wcnss_pil-y += qcom_wcnss.o
qcom_wcnss_pil-y += qcom_wcnss_iris.o
diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c
index 633268e9d550..54c07fd3f204 100644
--- a/drivers/remoteproc/imx_rproc.c
+++ b/drivers/remoteproc/imx_rproc.c
@@ -333,14 +333,14 @@ static int imx_rproc_probe(struct platform_device *pdev)
/* set some other name then imx */
rproc = rproc_alloc(dev, "imx-rproc", &imx_rproc_ops,
NULL, sizeof(*priv));
- if (!rproc) {
- ret = -ENOMEM;
- goto err;
- }
+ if (!rproc)
+ return -ENOMEM;
dcfg = of_device_get_match_data(dev);
- if (!dcfg)
- return -EINVAL;
+ if (!dcfg) {
+ ret = -EINVAL;
+ goto err_put_rproc;
+ }
priv = rproc->priv;
priv->rproc = rproc;
@@ -359,8 +359,8 @@ static int imx_rproc_probe(struct platform_device *pdev)
priv->clk = devm_clk_get(dev, NULL);
if (IS_ERR(priv->clk)) {
dev_err(dev, "Failed to get clock\n");
- rproc_free(rproc);
- return PTR_ERR(priv->clk);
+ ret = PTR_ERR(priv->clk);
+ goto err_put_rproc;
}
/*
@@ -370,8 +370,7 @@ static int imx_rproc_probe(struct platform_device *pdev)
ret = clk_prepare_enable(priv->clk);
if (ret) {
dev_err(&rproc->dev, "Failed to enable clock\n");
- rproc_free(rproc);
- return ret;
+ goto err_put_rproc;
}
ret = rproc_add(rproc);
@@ -380,13 +379,13 @@ static int imx_rproc_probe(struct platform_device *pdev)
goto err_put_clk;
}
- return ret;
+ return 0;
err_put_clk:
clk_disable_unprepare(priv->clk);
err_put_rproc:
rproc_free(rproc);
-err:
+
return ret;
}
diff --git a/drivers/remoteproc/qcom_adsp_pil.c b/drivers/remoteproc/qcom_adsp_pil.c
index 373c167892d7..89a86ce07f99 100644
--- a/drivers/remoteproc/qcom_adsp_pil.c
+++ b/drivers/remoteproc/qcom_adsp_pil.c
@@ -38,7 +38,10 @@ struct adsp_data {
const char *firmware_name;
int pas_id;
bool has_aggre2_clk;
+
const char *ssr_name;
+ const char *sysmon_name;
+ int ssctl_id;
};
struct qcom_adsp {
@@ -75,6 +78,7 @@ struct qcom_adsp {
struct qcom_rproc_glink glink_subdev;
struct qcom_rproc_subdev smd_subdev;
struct qcom_rproc_ssr ssr_subdev;
+ struct qcom_sysmon *sysmon;
};
static int adsp_load(struct rproc *rproc, const struct firmware *fw)
@@ -82,7 +86,9 @@ static int adsp_load(struct rproc *rproc, const struct firmware *fw)
struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
return qcom_mdt_load(adsp->dev, fw, rproc->firmware, adsp->pas_id,
- adsp->mem_region, adsp->mem_phys, adsp->mem_size);
+ adsp->mem_region, adsp->mem_phys, adsp->mem_size,
+ &adsp->mem_reloc);
+
}
static int adsp_start(struct rproc *rproc)
@@ -177,6 +183,7 @@ static const struct rproc_ops adsp_ops = {
.start = adsp_start,
.stop = adsp_stop,
.da_to_va = adsp_da_to_va,
+ .parse_fw = qcom_register_dump_segments,
.load = adsp_load,
};
@@ -201,9 +208,6 @@ static irqreturn_t adsp_fatal_interrupt(int irq, void *dev)
rproc_report_crash(adsp->rproc, RPROC_FATAL_ERROR);
- if (!IS_ERR(msg))
- msg[0] = '\0';
-
return IRQ_HANDLED;
}
@@ -398,6 +402,9 @@ static int adsp_probe(struct platform_device *pdev)
qcom_add_glink_subdev(rproc, &adsp->glink_subdev);
qcom_add_smd_subdev(rproc, &adsp->smd_subdev);
qcom_add_ssr_subdev(rproc, &adsp->ssr_subdev, desc->ssr_name);
+ adsp->sysmon = qcom_add_sysmon_subdev(rproc,
+ desc->sysmon_name,
+ desc->ssctl_id);
ret = rproc_add(rproc);
if (ret)
@@ -419,6 +426,7 @@ static int adsp_remove(struct platform_device *pdev)
rproc_del(adsp->rproc);
qcom_remove_glink_subdev(adsp->rproc, &adsp->glink_subdev);
+ qcom_remove_sysmon_subdev(adsp->sysmon);
qcom_remove_smd_subdev(adsp->rproc, &adsp->smd_subdev);
qcom_remove_ssr_subdev(adsp->rproc, &adsp->ssr_subdev);
rproc_free(adsp->rproc);
@@ -432,6 +440,8 @@ static const struct adsp_data adsp_resource_init = {
.pas_id = 1,
.has_aggre2_clk = false,
.ssr_name = "lpass",
+ .sysmon_name = "adsp",
+ .ssctl_id = 0x14,
};
static const struct adsp_data slpi_resource_init = {
@@ -440,6 +450,8 @@ static const struct adsp_data slpi_resource_init = {
.pas_id = 12,
.has_aggre2_clk = true,
.ssr_name = "dsps",
+ .sysmon_name = "slpi",
+ .ssctl_id = 0x16,
};
static const struct of_device_id adsp_of_match[] = {
diff --git a/drivers/remoteproc/qcom_common.c b/drivers/remoteproc/qcom_common.c
index 00602499713f..acfc99f82fb8 100644
--- a/drivers/remoteproc/qcom_common.c
+++ b/drivers/remoteproc/qcom_common.c
@@ -22,6 +22,7 @@
#include <linux/remoteproc.h>
#include <linux/rpmsg/qcom_glink.h>
#include <linux/rpmsg/qcom_smd.h>
+#include <linux/soc/qcom/mdt_loader.h>
#include "remoteproc_internal.h"
#include "qcom_common.h"
@@ -41,7 +42,7 @@ static int glink_subdev_probe(struct rproc_subdev *subdev)
return PTR_ERR_OR_ZERO(glink->edge);
}
-static void glink_subdev_remove(struct rproc_subdev *subdev)
+static void glink_subdev_remove(struct rproc_subdev *subdev, bool crashed)
{
struct qcom_rproc_glink *glink = to_glink_subdev(subdev);
@@ -74,11 +75,57 @@ EXPORT_SYMBOL_GPL(qcom_add_glink_subdev);
*/
void qcom_remove_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink)
{
+ if (!glink->node)
+ return;
+
rproc_remove_subdev(rproc, &glink->subdev);
of_node_put(glink->node);
}
EXPORT_SYMBOL_GPL(qcom_remove_glink_subdev);
+/**
+ * qcom_register_dump_segments() - register segments for coredump
+ * @rproc: remoteproc handle
+ * @fw: firmware header
+ *
+ * Register all segments of the ELF in the remoteproc coredump segment list
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int qcom_register_dump_segments(struct rproc *rproc,
+ const struct firmware *fw)
+{
+ const struct elf32_phdr *phdrs;
+ const struct elf32_phdr *phdr;
+ const struct elf32_hdr *ehdr;
+ int ret;
+ int i;
+
+ ehdr = (struct elf32_hdr *)fw->data;
+ phdrs = (struct elf32_phdr *)(ehdr + 1);
+
+ for (i = 0; i < ehdr->e_phnum; i++) {
+ phdr = &phdrs[i];
+
+ if (phdr->p_type != PT_LOAD)
+ continue;
+
+ if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH)
+ continue;
+
+ if (!phdr->p_memsz)
+ continue;
+
+ ret = rproc_coredump_add_segment(rproc, phdr->p_paddr,
+ phdr->p_memsz);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qcom_register_dump_segments);
+
static int smd_subdev_probe(struct rproc_subdev *subdev)
{
struct qcom_rproc_subdev *smd = to_smd_subdev(subdev);
@@ -88,7 +135,7 @@ static int smd_subdev_probe(struct rproc_subdev *subdev)
return PTR_ERR_OR_ZERO(smd->edge);
}
-static void smd_subdev_remove(struct rproc_subdev *subdev)
+static void smd_subdev_remove(struct rproc_subdev *subdev, bool crashed)
{
struct qcom_rproc_subdev *smd = to_smd_subdev(subdev);
@@ -121,6 +168,9 @@ EXPORT_SYMBOL_GPL(qcom_add_smd_subdev);
*/
void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd)
{
+ if (!smd->node)
+ return;
+
rproc_remove_subdev(rproc, &smd->subdev);
of_node_put(smd->node);
}
@@ -157,7 +207,7 @@ static int ssr_notify_start(struct rproc_subdev *subdev)
return 0;
}
-static void ssr_notify_stop(struct rproc_subdev *subdev)
+static void ssr_notify_stop(struct rproc_subdev *subdev, bool crashed)
{
struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev);
diff --git a/drivers/remoteproc/qcom_common.h b/drivers/remoteproc/qcom_common.h
index 728be9834d8b..58de71e4781c 100644
--- a/drivers/remoteproc/qcom_common.h
+++ b/drivers/remoteproc/qcom_common.h
@@ -4,6 +4,9 @@
#include <linux/remoteproc.h>
#include "remoteproc_internal.h"
+#include <linux/soc/qcom/qmi.h>
+
+struct qcom_sysmon;
struct qcom_rproc_glink {
struct rproc_subdev subdev;
@@ -30,6 +33,8 @@ struct qcom_rproc_ssr {
void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink);
void qcom_remove_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink);
+int qcom_register_dump_segments(struct rproc *rproc, const struct firmware *fw);
+
void qcom_add_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd);
void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd);
@@ -37,4 +42,22 @@ void qcom_add_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr,
const char *ssr_name);
void qcom_remove_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr);
+#if IS_ENABLED(CONFIG_QCOM_SYSMON)
+struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc,
+ const char *name,
+ int ssctl_instance);
+void qcom_remove_sysmon_subdev(struct qcom_sysmon *sysmon);
+#else
+static inline struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc,
+ const char *name,
+ int ssctl_instance)
+{
+ return NULL;
+}
+
+static inline void qcom_remove_sysmon_subdev(struct qcom_sysmon *sysmon)
+{
+}
+#endif
+
#endif
diff --git a/drivers/remoteproc/qcom_q6v5_pil.c b/drivers/remoteproc/qcom_q6v5_pil.c
index b4e5e725848d..8e70a627e0bb 100644
--- a/drivers/remoteproc/qcom_q6v5_pil.c
+++ b/drivers/remoteproc/qcom_q6v5_pil.c
@@ -168,6 +168,7 @@ struct q6v5 {
struct qcom_rproc_subdev smd_subdev;
struct qcom_rproc_ssr ssr_subdev;
+ struct qcom_sysmon *sysmon;
bool need_mem_protection;
int mpss_perm;
int mba_perm;
@@ -939,9 +940,6 @@ static irqreturn_t q6v5_wdog_interrupt(int irq, void *dev)
rproc_report_crash(qproc->rproc, RPROC_WATCHDOG);
- if (!IS_ERR(msg))
- msg[0] = '\0';
-
return IRQ_HANDLED;
}
@@ -959,9 +957,6 @@ static irqreturn_t q6v5_fatal_interrupt(int irq, void *dev)
rproc_report_crash(qproc->rproc, RPROC_FATAL_ERROR);
- if (!IS_ERR(msg))
- msg[0] = '\0';
-
return IRQ_HANDLED;
}
@@ -1215,6 +1210,7 @@ static int q6v5_probe(struct platform_device *pdev)
qproc->mba_perm = BIT(QCOM_SCM_VMID_HLOS);
qcom_add_smd_subdev(rproc, &qproc->smd_subdev);
qcom_add_ssr_subdev(rproc, &qproc->ssr_subdev, "mpss");
+ qproc->sysmon = qcom_add_sysmon_subdev(rproc, "modem", 0x12);
ret = rproc_add(rproc);
if (ret)
@@ -1234,6 +1230,7 @@ static int q6v5_remove(struct platform_device *pdev)
rproc_del(qproc->rproc);
+ qcom_remove_sysmon_subdev(qproc->sysmon);
qcom_remove_smd_subdev(qproc->rproc, &qproc->smd_subdev);
qcom_remove_ssr_subdev(qproc->rproc, &qproc->ssr_subdev);
rproc_free(qproc->rproc);
diff --git a/drivers/remoteproc/qcom_sysmon.c b/drivers/remoteproc/qcom_sysmon.c
new file mode 100644
index 000000000000..f085545d7da5
--- /dev/null
+++ b/drivers/remoteproc/qcom_sysmon.c
@@ -0,0 +1,579 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2017, Linaro Ltd.
+ */
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/notifier.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/remoteproc/qcom_rproc.h>
+#include <linux/rpmsg.h>
+
+#include "qcom_common.h"
+
+static BLOCKING_NOTIFIER_HEAD(sysmon_notifiers);
+
+struct qcom_sysmon {
+ struct rproc_subdev subdev;
+ struct rproc *rproc;
+
+ struct list_head node;
+
+ const char *name;
+
+ int ssctl_version;
+ int ssctl_instance;
+
+ struct notifier_block nb;
+
+ struct device *dev;
+
+ struct rpmsg_endpoint *ept;
+ struct completion comp;
+ struct mutex lock;
+
+ bool ssr_ack;
+
+ struct qmi_handle qmi;
+ struct sockaddr_qrtr ssctl;
+};
+
+static DEFINE_MUTEX(sysmon_lock);
+static LIST_HEAD(sysmon_list);
+
+/**
+ * sysmon_send_event() - send notification of other remote's SSR event
+ * @sysmon: sysmon context
+ * @name: other remote's name
+ */
+static void sysmon_send_event(struct qcom_sysmon *sysmon, const char *name)
+{
+ char req[50];
+ int len;
+ int ret;
+
+ len = snprintf(req, sizeof(req), "ssr:%s:before_shutdown", name);
+ if (len >= sizeof(req))
+ return;
+
+ mutex_lock(&sysmon->lock);
+ reinit_completion(&sysmon->comp);
+ sysmon->ssr_ack = false;
+
+ ret = rpmsg_send(sysmon->ept, req, len);
+ if (ret < 0) {
+ dev_err(sysmon->dev, "failed to send sysmon event\n");
+ goto out_unlock;
+ }
+
+ ret = wait_for_completion_timeout(&sysmon->comp,
+ msecs_to_jiffies(5000));
+ if (!ret) {
+ dev_err(sysmon->dev, "timeout waiting for sysmon ack\n");
+ goto out_unlock;
+ }
+
+ if (!sysmon->ssr_ack)
+ dev_err(sysmon->dev, "unexpected response to sysmon event\n");
+
+out_unlock:
+ mutex_unlock(&sysmon->lock);
+}
+
+/**
+ * sysmon_request_shutdown() - request graceful shutdown of remote
+ * @sysmon: sysmon context
+ */
+static void sysmon_request_shutdown(struct qcom_sysmon *sysmon)
+{
+ char *req = "ssr:shutdown";
+ int ret;
+
+ mutex_lock(&sysmon->lock);
+ reinit_completion(&sysmon->comp);
+ sysmon->ssr_ack = false;
+
+ ret = rpmsg_send(sysmon->ept, req, strlen(req) + 1);
+ if (ret < 0) {
+ dev_err(sysmon->dev, "send sysmon shutdown request failed\n");
+ goto out_unlock;
+ }
+
+ ret = wait_for_completion_timeout(&sysmon->comp,
+ msecs_to_jiffies(5000));
+ if (!ret) {
+ dev_err(sysmon->dev, "timeout waiting for sysmon ack\n");
+ goto out_unlock;
+ }
+
+ if (!sysmon->ssr_ack)
+ dev_err(sysmon->dev,
+ "unexpected response to sysmon shutdown request\n");
+
+out_unlock:
+ mutex_unlock(&sysmon->lock);
+}
+
+static int sysmon_callback(struct rpmsg_device *rpdev, void *data, int count,
+ void *priv, u32 addr)
+{
+ struct qcom_sysmon *sysmon = priv;
+ const char *ssr_ack = "ssr:ack";
+ const int ssr_ack_len = strlen(ssr_ack) + 1;
+
+ if (!sysmon)
+ return -EINVAL;
+
+ if (count >= ssr_ack_len && !memcmp(data, ssr_ack, ssr_ack_len))
+ sysmon->ssr_ack = true;
+
+ complete(&sysmon->comp);
+
+ return 0;
+}
+
+#define SSCTL_SHUTDOWN_REQ 0x21
+#define SSCTL_SUBSYS_EVENT_REQ 0x23
+
+#define SSCTL_MAX_MSG_LEN 7
+
+#define SSCTL_SUBSYS_NAME_LENGTH 15
+
+enum {
+ SSCTL_SSR_EVENT_BEFORE_POWERUP,
+ SSCTL_SSR_EVENT_AFTER_POWERUP,
+ SSCTL_SSR_EVENT_BEFORE_SHUTDOWN,
+ SSCTL_SSR_EVENT_AFTER_SHUTDOWN,
+};
+
+enum {
+ SSCTL_SSR_EVENT_FORCED,
+ SSCTL_SSR_EVENT_GRACEFUL,
+};
+
+struct ssctl_shutdown_resp {
+ struct qmi_response_type_v01 resp;
+};
+
+static struct qmi_elem_info ssctl_shutdown_resp_ei[] = {
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct qmi_response_type_v01),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(struct ssctl_shutdown_resp, resp),
+ .ei_array = qmi_response_type_v01_ei,
+ },
+ {}
+};
+
+struct ssctl_subsys_event_req {
+ u8 subsys_name_len;
+ char subsys_name[SSCTL_SUBSYS_NAME_LENGTH];
+ u32 event;
+ u8 evt_driven_valid;
+ u32 evt_driven;
+};
+
+static struct qmi_elem_info ssctl_subsys_event_req_ei[] = {
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(struct ssctl_subsys_event_req,
+ subsys_name_len),
+ .ei_array = NULL,
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = SSCTL_SUBSYS_NAME_LENGTH,
+ .elem_size = sizeof(char),
+ .array_type = VAR_LEN_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(struct ssctl_subsys_event_req,
+ subsys_name),
+ .ei_array = NULL,
+ },
+ {
+ .data_type = QMI_SIGNED_4_BYTE_ENUM,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(struct ssctl_subsys_event_req,
+ event),
+ .ei_array = NULL,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct ssctl_subsys_event_req,
+ evt_driven_valid),
+ .ei_array = NULL,
+ },
+ {
+ .data_type = QMI_SIGNED_4_BYTE_ENUM,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct ssctl_subsys_event_req,
+ evt_driven),
+ .ei_array = NULL,
+ },
+ {}
+};
+
+struct ssctl_subsys_event_resp {
+ struct qmi_response_type_v01 resp;
+};
+
+static struct qmi_elem_info ssctl_subsys_event_resp_ei[] = {
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct qmi_response_type_v01),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(struct ssctl_subsys_event_resp,
+ resp),
+ .ei_array = qmi_response_type_v01_ei,
+ },
+ {}
+};
+
+/**
+ * ssctl_request_shutdown() - request shutdown via SSCTL QMI service
+ * @sysmon: sysmon context
+ */
+static void ssctl_request_shutdown(struct qcom_sysmon *sysmon)
+{
+ struct ssctl_shutdown_resp resp;
+ struct qmi_txn txn;
+ int ret;
+
+ ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_shutdown_resp_ei, &resp);
+ if (ret < 0) {
+ dev_err(sysmon->dev, "failed to allocate QMI txn\n");
+ return;
+ }
+
+ ret = qmi_send_request(&sysmon->qmi, &sysmon->ssctl, &txn,
+ SSCTL_SHUTDOWN_REQ, 0, NULL, NULL);
+ if (ret < 0) {
+ dev_err(sysmon->dev, "failed to send shutdown request\n");
+ qmi_txn_cancel(&txn);
+ return;
+ }
+
+ ret = qmi_txn_wait(&txn, 5 * HZ);
+ if (ret < 0)
+ dev_err(sysmon->dev, "failed receiving QMI response\n");
+ else if (resp.resp.result)
+ dev_err(sysmon->dev, "shutdown request failed\n");
+ else
+ dev_dbg(sysmon->dev, "shutdown request completed\n");
+}
+
+/**
+ * ssctl_send_event() - send notification of other remote's SSR event
+ * @sysmon: sysmon context
+ * @name: other remote's name
+ */
+static void ssctl_send_event(struct qcom_sysmon *sysmon, const char *name)
+{
+ struct ssctl_subsys_event_resp resp;
+ struct ssctl_subsys_event_req req;
+ struct qmi_txn txn;
+ int ret;
+
+ memset(&resp, 0, sizeof(resp));
+ ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_subsys_event_resp_ei, &resp);
+ if (ret < 0) {
+ dev_err(sysmon->dev, "failed to allocate QMI txn\n");
+ return;
+ }
+
+ memset(&req, 0, sizeof(req));
+ strlcpy(req.subsys_name, name, sizeof(req.subsys_name));
+ req.subsys_name_len = strlen(req.subsys_name);
+ req.event = SSCTL_SSR_EVENT_BEFORE_SHUTDOWN;
+ req.evt_driven_valid = true;
+ req.evt_driven = SSCTL_SSR_EVENT_FORCED;
+
+ ret = qmi_send_request(&sysmon->qmi, &sysmon->ssctl, &txn,
+ SSCTL_SUBSYS_EVENT_REQ, 40,
+ ssctl_subsys_event_req_ei, &req);
+ if (ret < 0) {
+ dev_err(sysmon->dev, "failed to send shutdown request\n");
+ qmi_txn_cancel(&txn);
+ return;
+ }
+
+ ret = qmi_txn_wait(&txn, 5 * HZ);
+ if (ret < 0)
+ dev_err(sysmon->dev, "failed receiving QMI response\n");
+ else if (resp.resp.result)
+ dev_err(sysmon->dev, "ssr event send failed\n");
+ else
+ dev_dbg(sysmon->dev, "ssr event send completed\n");
+}
+
+/**
+ * ssctl_new_server() - QMI callback indicating a new service
+ * @qmi: QMI handle
+ * @svc: service information
+ *
+ * Return: 0 if we're interested in this service, -EINVAL otherwise.
+ */
+static int ssctl_new_server(struct qmi_handle *qmi, struct qmi_service *svc)
+{
+ struct qcom_sysmon *sysmon = container_of(qmi, struct qcom_sysmon, qmi);
+
+ switch (svc->version) {
+ case 1:
+ if (svc->instance != 0)
+ return -EINVAL;
+ if (strcmp(sysmon->name, "modem"))
+ return -EINVAL;
+ break;
+ case 2:
+ if (svc->instance != sysmon->ssctl_instance)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ sysmon->ssctl_version = svc->version;
+
+ sysmon->ssctl.sq_family = AF_QIPCRTR;
+ sysmon->ssctl.sq_node = svc->node;
+ sysmon->ssctl.sq_port = svc->port;
+
+ svc->priv = sysmon;
+
+ return 0;
+}
+
+/**
+ * ssctl_del_server() - QMI callback indicating that @svc is removed
+ * @qmi: QMI handle
+ * @svc: service information
+ */
+static void ssctl_del_server(struct qmi_handle *qmi, struct qmi_service *svc)
+{
+ struct qcom_sysmon *sysmon = svc->priv;
+
+ sysmon->ssctl_version = 0;
+}
+
+static const struct qmi_ops ssctl_ops = {
+ .new_server = ssctl_new_server,
+ .del_server = ssctl_del_server,
+};
+
+static int sysmon_start(struct rproc_subdev *subdev)
+{
+ return 0;
+}
+
+static void sysmon_stop(struct rproc_subdev *subdev, bool crashed)
+{
+ struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon, subdev);
+
+ blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)sysmon->name);
+
+ /* Don't request graceful shutdown if we've crashed */
+ if (crashed)
+ return;
+
+ if (sysmon->ssctl_version)
+ ssctl_request_shutdown(sysmon);
+ else if (sysmon->ept)
+ sysmon_request_shutdown(sysmon);
+}
+
+/**
+ * sysmon_notify() - notify sysmon target of another's SSR
+ * @nb: notifier_block associated with sysmon instance
+ * @event: unused
+ * @data: SSR identifier of the remote that is going down
+ */
+static int sysmon_notify(struct notifier_block *nb, unsigned long event,
+ void *data)
+{
+ struct qcom_sysmon *sysmon = container_of(nb, struct qcom_sysmon, nb);
+ struct rproc *rproc = sysmon->rproc;
+ const char *ssr_name = data;
+
+ /* Skip non-running rprocs and the originating instance */
+ if (rproc->state != RPROC_RUNNING || !strcmp(data, sysmon->name)) {
+ dev_dbg(sysmon->dev, "not notifying %s\n", sysmon->name);
+ return NOTIFY_DONE;
+ }
+
+ /* Only SSCTL version 2 supports SSR events */
+ if (sysmon->ssctl_version == 2)
+ ssctl_send_event(sysmon, ssr_name);
+ else if (sysmon->ept)
+ sysmon_send_event(sysmon, ssr_name);
+
+ return NOTIFY_DONE;
+}
+
+/**
+ * qcom_add_sysmon_subdev() - create a sysmon subdev for the given remoteproc
+ * @rproc: rproc context to associate the subdev with
+ * @name: name of this subdev, to use in SSR
+ * @ssctl_instance: instance id of the ssctl QMI service
+ *
+ * Return: A new qcom_sysmon object, or NULL on failure
+ */
+struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc,
+ const char *name,
+ int ssctl_instance)
+{
+ struct qcom_sysmon *sysmon;
+ int ret;
+
+ sysmon = kzalloc(sizeof(*sysmon), GFP_KERNEL);
+ if (!sysmon)
+ return NULL;
+
+ sysmon->dev = rproc->dev.parent;
+ sysmon->rproc = rproc;
+
+ sysmon->name = name;
+ sysmon->ssctl_instance = ssctl_instance;
+
+ init_completion(&sysmon->comp);
+ mutex_init(&sysmon->lock);
+
+ ret = qmi_handle_init(&sysmon->qmi, SSCTL_MAX_MSG_LEN, &ssctl_ops, NULL);
+ if (ret < 0) {
+ dev_err(sysmon->dev, "failed to initialize qmi handle\n");
+ kfree(sysmon);
+ return NULL;
+ }
+
+ qmi_add_lookup(&sysmon->qmi, 43, 0, 0);
+
+ rproc_add_subdev(rproc, &sysmon->subdev, sysmon_start, sysmon_stop);
+
+ sysmon->nb.notifier_call = sysmon_notify;
+ blocking_notifier_chain_register(&sysmon_notifiers, &sysmon->nb);
+
+ mutex_lock(&sysmon_lock);
+ list_add(&sysmon->node, &sysmon_list);
+ mutex_unlock(&sysmon_lock);
+
+ return sysmon;
+}
+EXPORT_SYMBOL_GPL(qcom_add_sysmon_subdev);
+
+/**
+ * qcom_remove_sysmon_subdev() - release a qcom_sysmon
+ * @sysmon: sysmon context, as retrieved by qcom_add_sysmon_subdev()
+ */
+void qcom_remove_sysmon_subdev(struct qcom_sysmon *sysmon)
+{
+ if (!sysmon)
+ return;
+
+ mutex_lock(&sysmon_lock);
+ list_del(&sysmon->node);
+ mutex_unlock(&sysmon_lock);
+
+ blocking_notifier_chain_unregister(&sysmon_notifiers, &sysmon->nb);
+
+ rproc_remove_subdev(sysmon->rproc, &sysmon->subdev);
+
+ qmi_handle_release(&sysmon->qmi);
+
+ kfree(sysmon);
+}
+EXPORT_SYMBOL_GPL(qcom_remove_sysmon_subdev);
+
+/**
+ * sysmon_probe() - probe sys_mon channel
+ * @rpdev: rpmsg device handle
+ *
+ * Find the sysmon context associated with the ancestor remoteproc and assign
+ * this rpmsg device with said sysmon context.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+static int sysmon_probe(struct rpmsg_device *rpdev)
+{
+ struct qcom_sysmon *sysmon;
+ struct rproc *rproc;
+
+ rproc = rproc_get_by_child(&rpdev->dev);
+ if (!rproc) {
+ dev_err(&rpdev->dev, "sysmon device not child of rproc\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&sysmon_lock);
+ list_for_each_entry(sysmon, &sysmon_list, node) {
+ if (sysmon->rproc == rproc)
+ goto found;
+ }
+ mutex_unlock(&sysmon_lock);
+
+ dev_err(&rpdev->dev, "no sysmon associated with parent rproc\n");
+
+ return -EINVAL;
+
+found:
+ mutex_unlock(&sysmon_lock);
+
+ rpdev->ept->priv = sysmon;
+ sysmon->ept = rpdev->ept;
+
+ return 0;
+}
+
+/**
+ * sysmon_remove() - sys_mon channel remove handler
+ * @rpdev: rpmsg device handle
+ *
+ * Disassociate the rpmsg device with the sysmon instance.
+ */
+static void sysmon_remove(struct rpmsg_device *rpdev)
+{
+ struct qcom_sysmon *sysmon = rpdev->ept->priv;
+
+ sysmon->ept = NULL;
+}
+
+static const struct rpmsg_device_id sysmon_match[] = {
+ { "sys_mon" },
+ {}
+};
+
+static struct rpmsg_driver sysmon_driver = {
+ .probe = sysmon_probe,
+ .remove = sysmon_remove,
+ .callback = sysmon_callback,
+ .id_table = sysmon_match,
+ .drv = {
+ .name = "qcom_sysmon",
+ },
+};
+
+module_rpmsg_driver(sysmon_driver);
+
+MODULE_DESCRIPTION("Qualcomm sysmon driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c
index 3f0609236a76..b0e07e9f42d5 100644
--- a/drivers/remoteproc/qcom_wcnss.c
+++ b/drivers/remoteproc/qcom_wcnss.c
@@ -40,6 +40,7 @@
#define WCNSS_CRASH_REASON_SMEM 422
#define WCNSS_FIRMWARE_NAME "wcnss.mdt"
#define WCNSS_PAS_ID 6
+#define WCNSS_SSCTL_ID 0x13
#define WCNSS_SPARE_NVBIN_DLND BIT(25)
@@ -98,6 +99,7 @@ struct qcom_wcnss {
size_t mem_size;
struct qcom_rproc_subdev smd_subdev;
+ struct qcom_sysmon *sysmon;
};
static const struct wcnss_data riva_data = {
@@ -153,7 +155,8 @@ static int wcnss_load(struct rproc *rproc, const struct firmware *fw)
struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv;
return qcom_mdt_load(wcnss->dev, fw, rproc->firmware, WCNSS_PAS_ID,
- wcnss->mem_region, wcnss->mem_phys, wcnss->mem_size);
+ wcnss->mem_region, wcnss->mem_phys,
+ wcnss->mem_size, &wcnss->mem_reloc);
}
static void wcnss_indicate_nv_download(struct qcom_wcnss *wcnss)
@@ -308,6 +311,7 @@ static const struct rproc_ops wcnss_ops = {
.start = wcnss_start,
.stop = wcnss_stop,
.da_to_va = wcnss_da_to_va,
+ .parse_fw = qcom_register_dump_segments,
.load = wcnss_load,
};
@@ -332,9 +336,6 @@ static irqreturn_t wcnss_fatal_interrupt(int irq, void *dev)
rproc_report_crash(wcnss->rproc, RPROC_FATAL_ERROR);
- if (!IS_ERR(msg))
- msg[0] = '\0';
-
return IRQ_HANDLED;
}
@@ -551,6 +552,7 @@ static int wcnss_probe(struct platform_device *pdev)
}
qcom_add_smd_subdev(rproc, &wcnss->smd_subdev);
+ wcnss->sysmon = qcom_add_sysmon_subdev(rproc, "wcnss", WCNSS_SSCTL_ID);
ret = rproc_add(rproc);
if (ret)
@@ -573,6 +575,7 @@ static int wcnss_remove(struct platform_device *pdev)
qcom_smem_state_put(wcnss->state);
rproc_del(wcnss->rproc);
+ qcom_remove_sysmon_subdev(wcnss->sysmon);
qcom_remove_smd_subdev(wcnss->rproc, &wcnss->smd_subdev);
rproc_free(wcnss->rproc);
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
index 4170dfbd93bd..6d9c5832ce47 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -33,6 +33,7 @@
#include <linux/firmware.h>
#include <linux/string.h>
#include <linux/debugfs.h>
+#include <linux/devcoredump.h>
#include <linux/remoteproc.h>
#include <linux/iommu.h>
#include <linux/idr.h>
@@ -307,7 +308,7 @@ static int rproc_vdev_do_probe(struct rproc_subdev *subdev)
return rproc_add_virtio_dev(rvdev, rvdev->id);
}
-static void rproc_vdev_do_remove(struct rproc_subdev *subdev)
+static void rproc_vdev_do_remove(struct rproc_subdev *subdev, bool crashed)
{
struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev);
@@ -788,17 +789,31 @@ static int rproc_probe_subdevices(struct rproc *rproc)
unroll_registration:
list_for_each_entry_continue_reverse(subdev, &rproc->subdevs, node)
- subdev->remove(subdev);
+ subdev->remove(subdev, true);
return ret;
}
-static void rproc_remove_subdevices(struct rproc *rproc)
+static void rproc_remove_subdevices(struct rproc *rproc, bool crashed)
{
struct rproc_subdev *subdev;
list_for_each_entry_reverse(subdev, &rproc->subdevs, node)
- subdev->remove(subdev);
+ subdev->remove(subdev, crashed);
+}
+
+/**
+ * rproc_coredump_cleanup() - clean up dump_segments list
+ * @rproc: the remote processor handle
+ */
+static void rproc_coredump_cleanup(struct rproc *rproc)
+{
+ struct rproc_dump_segment *entry, *tmp;
+
+ list_for_each_entry_safe(entry, tmp, &rproc->dump_segments, node) {
+ list_del(&entry->node);
+ kfree(entry);
+ }
}
/**
@@ -848,6 +863,8 @@ static void rproc_resource_cleanup(struct rproc *rproc)
/* clean up remote vdev entries */
list_for_each_entry_safe(rvdev, rvtmp, &rproc->rvdevs, node)
kref_put(&rvdev->refcount, rproc_vdev_release);
+
+ rproc_coredump_cleanup(rproc);
}
static int rproc_start(struct rproc *rproc, const struct firmware *fw)
@@ -927,8 +944,8 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
rproc->bootaddr = rproc_get_boot_addr(rproc, fw);
- /* load resource table */
- ret = rproc_load_rsc_table(rproc, fw);
+ /* Load resource table, core dump segment list etc from the firmware */
+ ret = rproc_parse_fw(rproc, fw);
if (ret)
goto disable_iommu;
@@ -992,13 +1009,13 @@ static int rproc_trigger_auto_boot(struct rproc *rproc)
return ret;
}
-static int rproc_stop(struct rproc *rproc)
+static int rproc_stop(struct rproc *rproc, bool crashed)
{
struct device *dev = &rproc->dev;
int ret;
/* remove any subdevices for the remote processor */
- rproc_remove_subdevices(rproc);
+ rproc_remove_subdevices(rproc, crashed);
/* the installed resource table is no longer accessible */
rproc->table_ptr = rproc->cached_table;
@@ -1018,6 +1035,113 @@ static int rproc_stop(struct rproc *rproc)
}
/**
+ * rproc_coredump_add_segment() - add segment of device memory to coredump
+ * @rproc: handle of a remote processor
+ * @da: device address
+ * @size: size of segment
+ *
+ * Add device memory to the list of segments to be included in a coredump for
+ * the remoteproc.
+ *
+ * Return: 0 on success, negative errno on error.
+ */
+int rproc_coredump_add_segment(struct rproc *rproc, dma_addr_t da, size_t size)
+{
+ struct rproc_dump_segment *segment;
+
+ segment = kzalloc(sizeof(*segment), GFP_KERNEL);
+ if (!segment)
+ return -ENOMEM;
+
+ segment->da = da;
+ segment->size = size;
+
+ list_add_tail(&segment->node, &rproc->dump_segments);
+
+ return 0;
+}
+EXPORT_SYMBOL(rproc_coredump_add_segment);
+
+/**
+ * rproc_coredump() - perform coredump
+ * @rproc: rproc handle
+ *
+ * This function will generate an ELF header for the registered segments
+ * and create a devcoredump device associated with rproc.
+ */
+static void rproc_coredump(struct rproc *rproc)
+{
+ struct rproc_dump_segment *segment;
+ struct elf32_phdr *phdr;
+ struct elf32_hdr *ehdr;
+ size_t data_size;
+ size_t offset;
+ void *data;
+ void *ptr;
+ int phnum = 0;
+
+ if (list_empty(&rproc->dump_segments))
+ return;
+
+ data_size = sizeof(*ehdr);
+ list_for_each_entry(segment, &rproc->dump_segments, node) {
+ data_size += sizeof(*phdr) + segment->size;
+
+ phnum++;
+ }
+
+ data = vmalloc(data_size);
+ if (!data)
+ return;
+
+ ehdr = data;
+
+ memset(ehdr, 0, sizeof(*ehdr));
+ memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
+ ehdr->e_ident[EI_CLASS] = ELFCLASS32;
+ ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
+ ehdr->e_ident[EI_VERSION] = EV_CURRENT;
+ ehdr->e_ident[EI_OSABI] = ELFOSABI_NONE;
+ ehdr->e_type = ET_CORE;
+ ehdr->e_machine = EM_NONE;
+ ehdr->e_version = EV_CURRENT;
+ ehdr->e_entry = rproc->bootaddr;
+ ehdr->e_phoff = sizeof(*ehdr);
+ ehdr->e_ehsize = sizeof(*ehdr);
+ ehdr->e_phentsize = sizeof(*phdr);
+ ehdr->e_phnum = phnum;
+
+ phdr = data + ehdr->e_phoff;
+ offset = ehdr->e_phoff + sizeof(*phdr) * ehdr->e_phnum;
+ list_for_each_entry(segment, &rproc->dump_segments, node) {
+ memset(phdr, 0, sizeof(*phdr));
+ phdr->p_type = PT_LOAD;
+ phdr->p_offset = offset;
+ phdr->p_vaddr = segment->da;
+ phdr->p_paddr = segment->da;
+ phdr->p_filesz = segment->size;
+ phdr->p_memsz = segment->size;
+ phdr->p_flags = PF_R | PF_W | PF_X;
+ phdr->p_align = 0;
+
+ ptr = rproc_da_to_va(rproc, segment->da, segment->size);
+ if (!ptr) {
+ dev_err(&rproc->dev,
+ "invalid coredump segment (%pad, %zu)\n",
+ &segment->da, segment->size);
+ memset(data + offset, 0xff, segment->size);
+ } else {
+ memcpy(data + offset, ptr, segment->size);
+ }
+
+ offset += phdr->p_filesz;
+ phdr++;
+ }
+
+ dev_coredumpv(&rproc->dev, data, data_size, GFP_KERNEL);
+}
+
+/**
* rproc_trigger_recovery() - recover a remoteproc
* @rproc: the remote processor
*
@@ -1039,10 +1163,13 @@ int rproc_trigger_recovery(struct rproc *rproc)
if (ret)
return ret;
- ret = rproc_stop(rproc);
+ ret = rproc_stop(rproc, false);
if (ret)
goto unlock_mutex;
+ /* generate coredump */
+ rproc_coredump(rproc);
+
/* load firmware */
ret = request_firmware(&firmware_p, rproc->firmware, dev);
if (ret < 0) {
@@ -1189,7 +1316,7 @@ void rproc_shutdown(struct rproc *rproc)
if (!atomic_dec_and_test(&rproc->power))
goto out;
- ret = rproc_stop(rproc);
+ ret = rproc_stop(rproc, true);
if (ret) {
atomic_inc(&rproc->power);
goto out;
@@ -1428,7 +1555,7 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
/* Default to ELF loader if no load function is specified */
if (!rproc->ops->load) {
rproc->ops->load = rproc_elf_load_segments;
- rproc->ops->load_rsc_table = rproc_elf_load_rsc_table;
+ rproc->ops->parse_fw = rproc_elf_load_rsc_table;
rproc->ops->find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table;
rproc->ops->sanity_check = rproc_elf_sanity_check;
rproc->ops->get_boot_addr = rproc_elf_get_boot_addr;
@@ -1443,6 +1570,7 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
INIT_LIST_HEAD(&rproc->traces);
INIT_LIST_HEAD(&rproc->rvdevs);
INIT_LIST_HEAD(&rproc->subdevs);
+ INIT_LIST_HEAD(&rproc->dump_segments);
INIT_WORK(&rproc->crash_handler, rproc_crash_handler_work);
@@ -1535,7 +1663,7 @@ EXPORT_SYMBOL(rproc_del);
void rproc_add_subdev(struct rproc *rproc,
struct rproc_subdev *subdev,
int (*probe)(struct rproc_subdev *subdev),
- void (*remove)(struct rproc_subdev *subdev))
+ void (*remove)(struct rproc_subdev *subdev, bool crashed))
{
subdev->probe = probe;
subdev->remove = remove;
diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
index 55a2950c5cb7..7570beb035b5 100644
--- a/drivers/remoteproc/remoteproc_internal.h
+++ b/drivers/remoteproc/remoteproc_internal.h
@@ -88,11 +88,10 @@ int rproc_load_segments(struct rproc *rproc, const struct firmware *fw)
return -EINVAL;
}
-static inline int rproc_load_rsc_table(struct rproc *rproc,
- const struct firmware *fw)
+static inline int rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
{
- if (rproc->ops->load_rsc_table)
- return rproc->ops->load_rsc_table(rproc, fw);
+ if (rproc->ops->parse_fw)
+ return rproc->ops->parse_fw(rproc, fw);
return 0;
}
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index 7fc77696bb1e..c0b292be1b72 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -49,6 +49,7 @@ config RESET_HSDK
config RESET_IMX7
bool "i.MX7 Reset Driver" if COMPILE_TEST
+ depends on HAS_IOMEM
default SOC_IMX7D
select MFD_SYSCON
help
@@ -83,14 +84,24 @@ config RESET_PISTACHIO
config RESET_SIMPLE
bool "Simple Reset Controller Driver" if COMPILE_TEST
- default ARCH_SOCFPGA || ARCH_STM32 || ARCH_STRATIX10 || ARCH_SUNXI || ARCH_ZX
+ default ARCH_SOCFPGA || ARCH_STM32 || ARCH_STRATIX10 || ARCH_SUNXI || ARCH_ZX || ARCH_ASPEED
help
This enables a simple reset controller driver for reset lines that
that can be asserted and deasserted by toggling bits in a contiguous,
exclusive register space.
- Currently this driver supports Altera SoCFPGAs, the RCC reset
- controller in STM32 MCUs, Allwinner SoCs, and ZTE's zx2967 family.
+ Currently this driver supports:
+ - Altera SoCFPGAs
+ - ASPEED BMC SoCs
+ - RCC reset controller in STM32 MCUs
+ - Allwinner SoCs
+ - ZTE's zx2967 family
+
+config RESET_STM32MP157
+ bool "STM32MP157 Reset Driver" if COMPILE_TEST
+ default MACH_STM32MP157
+ help
+ This enables the RCC reset controller driver for STM32 MPUs.
config RESET_SUNXI
bool "Allwinner SoCs Reset Driver" if COMPILE_TEST && !ARCH_SUNXI
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 132c24f5ddb5..c1261dcfe9ad 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_RESET_MESON) += reset-meson.o
obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o
obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o
obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
+obj-$(CONFIG_RESET_STM32MP157) += reset-stm32mp1.o
obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o
obj-$(CONFIG_RESET_TI_SCI) += reset-ti-sci.o
obj-$(CONFIG_RESET_TI_SYSCON) += reset-ti-syscon.o
diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index da4292e9de97..6488292e129c 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -23,6 +23,9 @@
static DEFINE_MUTEX(reset_list_mutex);
static LIST_HEAD(reset_controller_list);
+static DEFINE_MUTEX(reset_lookup_mutex);
+static LIST_HEAD(reset_lookup_list);
+
/**
* struct reset_control - a reset control
* @rcdev: a pointer to the reset controller device
@@ -148,6 +151,33 @@ int devm_reset_controller_register(struct device *dev,
}
EXPORT_SYMBOL_GPL(devm_reset_controller_register);
+/**
+ * reset_controller_add_lookup - register a set of lookup entries
+ * @lookup: array of reset lookup entries
+ * @num_entries: number of entries in the lookup array
+ */
+void reset_controller_add_lookup(struct reset_control_lookup *lookup,
+ unsigned int num_entries)
+{
+ struct reset_control_lookup *entry;
+ unsigned int i;
+
+ mutex_lock(&reset_lookup_mutex);
+ for (i = 0; i < num_entries; i++) {
+ entry = &lookup[i];
+
+ if (!entry->dev_id || !entry->provider) {
+ pr_warn("%s(): reset lookup entry badly specified, skipping\n",
+ __func__);
+ continue;
+ }
+
+ list_add_tail(&entry->list, &reset_lookup_list);
+ }
+ mutex_unlock(&reset_lookup_mutex);
+}
+EXPORT_SYMBOL_GPL(reset_controller_add_lookup);
+
static inline struct reset_control_array *
rstc_to_array(struct reset_control *rstc) {
return container_of(rstc, struct reset_control_array, base);
@@ -493,6 +523,70 @@ struct reset_control *__of_reset_control_get(struct device_node *node,
}
EXPORT_SYMBOL_GPL(__of_reset_control_get);
+static struct reset_controller_dev *
+__reset_controller_by_name(const char *name)
+{
+ struct reset_controller_dev *rcdev;
+
+ lockdep_assert_held(&reset_list_mutex);
+
+ list_for_each_entry(rcdev, &reset_controller_list, list) {
+ if (!rcdev->dev)
+ continue;
+
+ if (!strcmp(name, dev_name(rcdev->dev)))
+ return rcdev;
+ }
+
+ return NULL;
+}
+
+static struct reset_control *
+__reset_control_get_from_lookup(struct device *dev, const char *con_id,
+ bool shared, bool optional)
+{
+ const struct reset_control_lookup *lookup;
+ struct reset_controller_dev *rcdev;
+ const char *dev_id = dev_name(dev);
+ struct reset_control *rstc = NULL;
+
+ if (!dev)
+ return ERR_PTR(-EINVAL);
+
+ mutex_lock(&reset_lookup_mutex);
+
+ list_for_each_entry(lookup, &reset_lookup_list, list) {
+ if (strcmp(lookup->dev_id, dev_id))
+ continue;
+
+ if ((!con_id && !lookup->con_id) ||
+ ((con_id && lookup->con_id) &&
+ !strcmp(con_id, lookup->con_id))) {
+ mutex_lock(&reset_list_mutex);
+ rcdev = __reset_controller_by_name(lookup->provider);
+ if (!rcdev) {
+ mutex_unlock(&reset_list_mutex);
+ mutex_unlock(&reset_lookup_mutex);
+ /* Reset provider may not be ready yet. */
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+
+ rstc = __reset_control_get_internal(rcdev,
+ lookup->index,
+ shared);
+ mutex_unlock(&reset_list_mutex);
+ break;
+ }
+ }
+
+ mutex_unlock(&reset_lookup_mutex);
+
+ if (!rstc)
+ return optional ? NULL : ERR_PTR(-ENOENT);
+
+ return rstc;
+}
+
struct reset_control *__reset_control_get(struct device *dev, const char *id,
int index, bool shared, bool optional)
{
@@ -500,7 +594,7 @@ struct reset_control *__reset_control_get(struct device *dev, const char *id,
return __of_reset_control_get(dev->of_node, id, index, shared,
optional);
- return optional ? NULL : ERR_PTR(-EINVAL);
+ return __reset_control_get_from_lookup(dev, id, shared, optional);
}
EXPORT_SYMBOL_GPL(__reset_control_get);
diff --git a/drivers/reset/reset-meson.c b/drivers/reset/reset-meson.c
index 93cbee1ae8ef..5242e0679df7 100644
--- a/drivers/reset/reset-meson.c
+++ b/drivers/reset/reset-meson.c
@@ -124,29 +124,21 @@ static int meson_reset_deassert(struct reset_controller_dev *rcdev,
return meson_reset_level(rcdev, id, false);
}
-static const struct reset_control_ops meson_reset_meson8_ops = {
- .reset = meson_reset_reset,
-};
-
-static const struct reset_control_ops meson_reset_gx_ops = {
+static const struct reset_control_ops meson_reset_ops = {
.reset = meson_reset_reset,
.assert = meson_reset_assert,
.deassert = meson_reset_deassert,
};
static const struct of_device_id meson_reset_dt_ids[] = {
- { .compatible = "amlogic,meson8b-reset",
- .data = &meson_reset_meson8_ops, },
- { .compatible = "amlogic,meson-gxbb-reset",
- .data = &meson_reset_gx_ops, },
- { .compatible = "amlogic,meson-axg-reset",
- .data = &meson_reset_gx_ops, },
+ { .compatible = "amlogic,meson8b-reset" },
+ { .compatible = "amlogic,meson-gxbb-reset" },
+ { .compatible = "amlogic,meson-axg-reset" },
{ /* sentinel */ },
};
static int meson_reset_probe(struct platform_device *pdev)
{
- const struct reset_control_ops *ops;
struct meson_reset *data;
struct resource *res;
@@ -154,10 +146,6 @@ static int meson_reset_probe(struct platform_device *pdev)
if (!data)
return -ENOMEM;
- ops = of_device_get_match_data(&pdev->dev);
- if (!ops)
- return -EINVAL;
-
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
data->reg_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(data->reg_base))
@@ -169,7 +157,7 @@ static int meson_reset_probe(struct platform_device *pdev)
data->rcdev.owner = THIS_MODULE;
data->rcdev.nr_resets = REG_COUNT * BITS_PER_REG;
- data->rcdev.ops = ops;
+ data->rcdev.ops = &meson_reset_ops;
data->rcdev.of_node = pdev->dev.of_node;
return devm_reset_controller_register(&pdev->dev, &data->rcdev);
diff --git a/drivers/reset/reset-simple.c b/drivers/reset/reset-simple.c
index 2d4f362ef025..f7ce8910a392 100644
--- a/drivers/reset/reset-simple.c
+++ b/drivers/reset/reset-simple.c
@@ -125,6 +125,8 @@ static const struct of_device_id reset_simple_dt_ids[] = {
.data = &reset_simple_active_low },
{ .compatible = "zte,zx296718-reset",
.data = &reset_simple_active_low },
+ { .compatible = "aspeed,ast2400-lpc-reset" },
+ { .compatible = "aspeed,ast2500-lpc-reset" },
{ /* sentinel */ },
};
diff --git a/drivers/reset/reset-stm32mp1.c b/drivers/reset/reset-stm32mp1.c
new file mode 100644
index 000000000000..b221a28041fa
--- /dev/null
+++ b/drivers/reset/reset-stm32mp1.c
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
+ * Author: Gabriel Fernandez <gabriel.fernandez@st.com> for STMicroelectronics.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+
+#define CLR_OFFSET 0x4
+
+struct stm32_reset_data {
+ struct reset_controller_dev rcdev;
+ void __iomem *membase;
+};
+
+static inline struct stm32_reset_data *
+to_stm32_reset_data(struct reset_controller_dev *rcdev)
+{
+ return container_of(rcdev, struct stm32_reset_data, rcdev);
+}
+
+static int stm32_reset_update(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct stm32_reset_data *data = to_stm32_reset_data(rcdev);
+ int reg_width = sizeof(u32);
+ int bank = id / (reg_width * BITS_PER_BYTE);
+ int offset = id % (reg_width * BITS_PER_BYTE);
+ void __iomem *addr;
+
+ addr = data->membase + (bank * reg_width);
+ if (!assert)
+ addr += CLR_OFFSET;
+
+ writel(BIT(offset), addr);
+
+ return 0;
+}
+
+static int stm32_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return stm32_reset_update(rcdev, id, true);
+}
+
+static int stm32_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return stm32_reset_update(rcdev, id, false);
+}
+
+static int stm32_reset_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct stm32_reset_data *data = to_stm32_reset_data(rcdev);
+ int reg_width = sizeof(u32);
+ int bank = id / (reg_width * BITS_PER_BYTE);
+ int offset = id % (reg_width * BITS_PER_BYTE);
+ u32 reg;
+
+ reg = readl(data->membase + (bank * reg_width));
+
+ return !!(reg & BIT(offset));
+}
+
+static const struct reset_control_ops stm32_reset_ops = {
+ .assert = stm32_reset_assert,
+ .deassert = stm32_reset_deassert,
+ .status = stm32_reset_status,
+};
+
+static const struct of_device_id stm32_reset_dt_ids[] = {
+ { .compatible = "st,stm32mp1-rcc"},
+ { /* sentinel */ },
+};
+
+static int stm32_reset_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct stm32_reset_data *data;
+ void __iomem *membase;
+ struct resource *res;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ membase = devm_ioremap_resource(dev, res);
+ if (IS_ERR(membase))
+ return PTR_ERR(membase);
+
+ data->membase = membase;
+ data->rcdev.owner = THIS_MODULE;
+ data->rcdev.nr_resets = resource_size(res) * BITS_PER_BYTE;
+ data->rcdev.ops = &stm32_reset_ops;
+ data->rcdev.of_node = dev->of_node;
+
+ return devm_reset_controller_register(dev, &data->rcdev);
+}
+
+static struct platform_driver stm32_reset_driver = {
+ .probe = stm32_reset_probe,
+ .driver = {
+ .name = "stm32mp1-reset",
+ .of_match_table = stm32_reset_dt_ids,
+ },
+};
+
+builtin_platform_driver(stm32_reset_driver);
diff --git a/drivers/reset/reset-uniphier.c b/drivers/reset/reset-uniphier.c
index e8bb023ff15e..360e06b20c53 100644
--- a/drivers/reset/reset-uniphier.c
+++ b/drivers/reset/reset-uniphier.c
@@ -63,6 +63,7 @@ static const struct uniphier_reset_data uniphier_pro4_sys_reset_data[] = {
UNIPHIER_RESETX(12, 0x2000, 6), /* GIO (Ether, SATA, USB3) */
UNIPHIER_RESETX(14, 0x2000, 17), /* USB30 */
UNIPHIER_RESETX(15, 0x2004, 17), /* USB31 */
+ UNIPHIER_RESETX(40, 0x2000, 13), /* AIO */
UNIPHIER_RESET_END,
};
@@ -72,6 +73,7 @@ static const struct uniphier_reset_data uniphier_pro5_sys_reset_data[] = {
UNIPHIER_RESETX(12, 0x2000, 6), /* GIO (PCIe, USB3) */
UNIPHIER_RESETX(14, 0x2000, 17), /* USB30 */
UNIPHIER_RESETX(15, 0x2004, 17), /* USB31 */
+ UNIPHIER_RESETX(40, 0x2000, 13), /* AIO */
UNIPHIER_RESET_END,
};
@@ -88,6 +90,7 @@ static const struct uniphier_reset_data uniphier_pxs2_sys_reset_data[] = {
UNIPHIER_RESETX(21, 0x2014, 1), /* USB31-PHY1 */
UNIPHIER_RESETX(28, 0x2014, 12), /* SATA */
UNIPHIER_RESET(29, 0x2014, 8), /* SATA-PHY (active high) */
+ UNIPHIER_RESETX(40, 0x2000, 13), /* AIO */
UNIPHIER_RESET_END,
};
@@ -121,6 +124,8 @@ static const struct uniphier_reset_data uniphier_ld20_sys_reset_data[] = {
static const struct uniphier_reset_data uniphier_pxs3_sys_reset_data[] = {
UNIPHIER_RESETX(2, 0x200c, 0), /* NAND */
UNIPHIER_RESETX(4, 0x200c, 2), /* eMMC */
+ UNIPHIER_RESETX(6, 0x200c, 9), /* Ether0 */
+ UNIPHIER_RESETX(7, 0x200c, 10), /* Ether1 */
UNIPHIER_RESETX(8, 0x200c, 12), /* STDMAC */
UNIPHIER_RESETX(12, 0x200c, 4), /* USB30 link (GIO0) */
UNIPHIER_RESETX(13, 0x200c, 5), /* USB31 link (GIO1) */
diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c
index e0f31ed096a5..768ef542a841 100644
--- a/drivers/rpmsg/qcom_glink_native.c
+++ b/drivers/rpmsg/qcom_glink_native.c
@@ -113,7 +113,7 @@ struct qcom_glink {
spinlock_t rx_lock;
struct list_head rx_queue;
- struct mutex tx_lock;
+ spinlock_t tx_lock;
spinlock_t idr_lock;
struct idr lcids;
@@ -288,15 +288,14 @@ static int qcom_glink_tx(struct qcom_glink *glink,
const void *data, size_t dlen, bool wait)
{
unsigned int tlen = hlen + dlen;
- int ret;
+ unsigned long flags;
+ int ret = 0;
/* Reject packets that are too big */
if (tlen >= glink->tx_pipe->length)
return -EINVAL;
- ret = mutex_lock_interruptible(&glink->tx_lock);
- if (ret)
- return ret;
+ spin_lock_irqsave(&glink->tx_lock, flags);
while (qcom_glink_tx_avail(glink) < tlen) {
if (!wait) {
@@ -304,7 +303,12 @@ static int qcom_glink_tx(struct qcom_glink *glink,
goto out;
}
+ /* Wait without holding the tx_lock */
+ spin_unlock_irqrestore(&glink->tx_lock, flags);
+
usleep_range(10000, 15000);
+
+ spin_lock_irqsave(&glink->tx_lock, flags);
}
qcom_glink_tx_write(glink, hdr, hlen, data, dlen);
@@ -313,7 +317,7 @@ static int qcom_glink_tx(struct qcom_glink *glink,
mbox_client_txdone(glink->mbox_chan, 0);
out:
- mutex_unlock(&glink->tx_lock);
+ spin_unlock_irqrestore(&glink->tx_lock, flags);
return ret;
}
@@ -1567,7 +1571,7 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev,
glink->features = features;
glink->intentless = intentless;
- mutex_init(&glink->tx_lock);
+ spin_lock_init(&glink->tx_lock);
spin_lock_init(&glink->rx_lock);
INIT_LIST_HEAD(&glink->rx_queue);
INIT_WORK(&glink->rx_work, qcom_glink_work);
diff --git a/drivers/rpmsg/qcom_glink_smem.c b/drivers/rpmsg/qcom_glink_smem.c
index 892f2b92a4d8..3fa9d43e2c87 100644
--- a/drivers/rpmsg/qcom_glink_smem.c
+++ b/drivers/rpmsg/qcom_glink_smem.c
@@ -217,6 +217,7 @@ struct qcom_glink *qcom_glink_smem_register(struct device *parent,
ret = device_register(dev);
if (ret) {
pr_err("failed to register glink edge\n");
+ put_device(dev);
return ERR_PTR(ret);
}
@@ -299,7 +300,7 @@ struct qcom_glink *qcom_glink_smem_register(struct device *parent,
return glink;
err_put_dev:
- put_device(dev);
+ device_unregister(dev);
return ERR_PTR(ret);
}
diff --git a/drivers/rpmsg/qcom_smd.c b/drivers/rpmsg/qcom_smd.c
index 92d0c6a7a837..5ce9bf7b897d 100644
--- a/drivers/rpmsg/qcom_smd.c
+++ b/drivers/rpmsg/qcom_smd.c
@@ -167,9 +167,9 @@ struct qcom_smd_endpoint {
struct qcom_smd_channel *qsch;
};
-#define to_smd_device(_rpdev) container_of(_rpdev, struct qcom_smd_device, rpdev)
+#define to_smd_device(r) container_of(r, struct qcom_smd_device, rpdev)
#define to_smd_edge(d) container_of(d, struct qcom_smd_edge, dev)
-#define to_smd_endpoint(ept) container_of(ept, struct qcom_smd_endpoint, ept)
+#define to_smd_endpoint(e) container_of(e, struct qcom_smd_endpoint, ept)
/**
* struct qcom_smd_channel - smd channel struct
@@ -205,7 +205,7 @@ struct qcom_smd_channel {
struct smd_channel_info_pair *info;
struct smd_channel_info_word_pair *info_word;
- struct mutex tx_lock;
+ spinlock_t tx_lock;
wait_queue_head_t fblockread_event;
void *tx_fifo;
@@ -729,6 +729,7 @@ static int __qcom_smd_send(struct qcom_smd_channel *channel, const void *data,
{
__le32 hdr[5] = { cpu_to_le32(len), };
int tlen = sizeof(hdr) + len;
+ unsigned long flags;
int ret;
/* Word aligned channels only accept word size aligned data */
@@ -739,9 +740,11 @@ static int __qcom_smd_send(struct qcom_smd_channel *channel, const void *data,
if (tlen >= channel->fifo_size)
return -EINVAL;
- ret = mutex_lock_interruptible(&channel->tx_lock);
- if (ret)
- return ret;
+ /* Highlight the fact that if we enter the loop below we might sleep */
+ if (wait)
+ might_sleep();
+
+ spin_lock_irqsave(&channel->tx_lock, flags);
while (qcom_smd_get_tx_avail(channel) < tlen &&
channel->state == SMD_CHANNEL_OPENED) {
@@ -753,7 +756,7 @@ static int __qcom_smd_send(struct qcom_smd_channel *channel, const void *data,
SET_TX_CHANNEL_FLAG(channel, fBLOCKREADINTR, 0);
/* Wait without holding the tx_lock */
- mutex_unlock(&channel->tx_lock);
+ spin_unlock_irqrestore(&channel->tx_lock, flags);
ret = wait_event_interruptible(channel->fblockread_event,
qcom_smd_get_tx_avail(channel) >= tlen ||
@@ -761,9 +764,7 @@ static int __qcom_smd_send(struct qcom_smd_channel *channel, const void *data,
if (ret)
return ret;
- ret = mutex_lock_interruptible(&channel->tx_lock);
- if (ret)
- return ret;
+ spin_lock_irqsave(&channel->tx_lock, flags);
SET_TX_CHANNEL_FLAG(channel, fBLOCKREADINTR, 1);
}
@@ -787,7 +788,7 @@ static int __qcom_smd_send(struct qcom_smd_channel *channel, const void *data,
qcom_smd_signal_channel(channel);
out_unlock:
- mutex_unlock(&channel->tx_lock);
+ spin_unlock_irqrestore(&channel->tx_lock, flags);
return ret;
}
@@ -996,8 +997,26 @@ static struct device_node *qcom_smd_match_channel(struct device_node *edge_node,
return NULL;
}
+static int qcom_smd_announce_create(struct rpmsg_device *rpdev)
+{
+ struct qcom_smd_endpoint *qept = to_smd_endpoint(rpdev->ept);
+ struct qcom_smd_channel *channel = qept->qsch;
+ unsigned long flags;
+ bool kick_state;
+
+ spin_lock_irqsave(&channel->recv_lock, flags);
+ kick_state = qcom_smd_channel_intr(channel);
+ spin_unlock_irqrestore(&channel->recv_lock, flags);
+
+ if (kick_state)
+ schedule_work(&channel->edge->state_work);
+
+ return 0;
+}
+
static const struct rpmsg_device_ops qcom_smd_device_ops = {
.create_ept = qcom_smd_create_ept,
+ .announce_create = qcom_smd_announce_create,
};
static const struct rpmsg_endpoint_ops qcom_smd_endpoint_ops = {
@@ -1090,7 +1109,7 @@ static struct qcom_smd_channel *qcom_smd_create_channel(struct qcom_smd_edge *ed
if (!channel->name)
return ERR_PTR(-ENOMEM);
- mutex_init(&channel->tx_lock);
+ spin_lock_init(&channel->tx_lock);
spin_lock_init(&channel->recv_lock);
init_waitqueue_head(&channel->fblockread_event);
init_waitqueue_head(&channel->state_change_event);
@@ -1234,6 +1253,11 @@ static void qcom_channel_state_worker(struct work_struct *work)
if (channel->state != SMD_CHANNEL_CLOSED)
continue;
+ remote_state = GET_RX_CHANNEL_INFO(channel, state);
+ if (remote_state != SMD_CHANNEL_OPENING &&
+ remote_state != SMD_CHANNEL_OPENED)
+ continue;
+
if (channel->registered)
continue;
@@ -1408,6 +1432,7 @@ struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent,
ret = device_register(&edge->dev);
if (ret) {
pr_err("failed to register smd edge\n");
+ put_device(&edge->dev);
return ERR_PTR(ret);
}
@@ -1428,7 +1453,7 @@ struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent,
return edge;
unregister_dev:
- put_device(&edge->dev);
+ device_unregister(&edge->dev);
return ERR_PTR(ret);
}
EXPORT_SYMBOL(qcom_smd_register_edge);
diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c
index 5a081762afcc..920a02f0462c 100644
--- a/drivers/rpmsg/rpmsg_core.c
+++ b/drivers/rpmsg/rpmsg_core.c
@@ -442,7 +442,7 @@ static int rpmsg_dev_probe(struct device *dev)
goto out;
}
- if (rpdev->ops->announce_create)
+ if (ept && rpdev->ops->announce_create)
err = rpdev->ops->announce_create(rpdev);
out:
return err;
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 8ab5f0a5d323..59e6dede3db3 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -407,6 +407,16 @@ config RTC_DRV_ISL12022
This driver can also be built as a module. If so, the module
will be called rtc-isl12022.
+config RTC_DRV_ISL12026
+ tristate "Intersil ISL12026"
+ depends on OF || COMPILE_TEST
+ help
+ If you say yes here you get support for the
+ Intersil ISL12026 RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-isl12026.
+
config RTC_DRV_X1205
tristate "Xicor/Intersil X1205"
help
@@ -868,7 +878,7 @@ comment "Platform RTC drivers"
config RTC_DRV_CMOS
tristate "PC-style 'CMOS'"
- depends on X86 || ARM || M32R || PPC || MIPS || SPARC64 || MN10300
+ depends on X86 || ARM || PPC || MIPS || SPARC64
default y if X86
select RTC_MC146818_LIB
help
@@ -1413,6 +1423,7 @@ config RTC_DRV_AT91RM9200
config RTC_DRV_AT91SAM9
tristate "AT91SAM9 RTT as RTC"
depends on ARCH_AT91 || COMPILE_TEST
+ depends on HAS_IOMEM
select MFD_SYSCON
help
Some AT91SAM9 SoCs provide an RTT (Real Time Timer) block which
@@ -1434,16 +1445,6 @@ config RTC_DRV_AU1XXX
This driver can also be built as a module. If so, the module
will be called rtc-au1xxx.
-config RTC_DRV_BFIN
- tristate "Blackfin On-Chip RTC"
- depends on BLACKFIN && !BF561
- help
- If you say yes here you will get support for the
- Blackfin On-Chip Real Time Clock.
-
- This driver can also be built as a module. If so, the module
- will be called rtc-bfin.
-
config RTC_DRV_RS5C313
tristate "Ricoh RS5C313"
depends on SH_LANDISK
@@ -1512,7 +1513,7 @@ config RTC_DRV_STARFIRE
config RTC_DRV_TX4939
tristate "TX4939 SoC"
- depends on SOC_TX4939
+ depends on SOC_TX4939 || COMPILE_TEST
help
Driver for the internal RTC (Realtime Clock) module found on
Toshiba TX4939 SoC.
@@ -1656,13 +1657,6 @@ config RTC_DRV_TEGRA
This drive can also be built as a module. If so, the module
will be called rtc-tegra.
-config RTC_DRV_TILE
- tristate "Tilera hypervisor RTC support"
- depends on TILE
- help
- Enable support for the Linux driver side of the Tilera
- hypervisor's real-time clock interface.
-
config RTC_DRV_PUV3
tristate "PKUnity v3 RTC support"
depends on ARCH_PUV3
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 4fbf87e45a7c..5ff2fc0c361a 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -36,7 +36,6 @@ obj-$(CONFIG_RTC_DRV_ASM9260) += rtc-asm9260.o
obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o
obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o
obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o
-obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o
obj-$(CONFIG_RTC_DRV_BRCMSTB) += rtc-brcmstb-waketimer.o
obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o
obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o
@@ -76,6 +75,7 @@ obj-$(CONFIG_RTC_DRV_HID_SENSOR_TIME) += rtc-hid-sensor-time.o
obj-$(CONFIG_RTC_DRV_HYM8563) += rtc-hym8563.o
obj-$(CONFIG_RTC_DRV_IMXDI) += rtc-imxdi.o
obj-$(CONFIG_RTC_DRV_ISL12022) += rtc-isl12022.o
+obj-$(CONFIG_RTC_DRV_ISL12026) += rtc-isl12026.o
obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o
obj-$(CONFIG_RTC_DRV_LP8788) += rtc-lp8788.o
@@ -162,7 +162,6 @@ obj-$(CONFIG_RTC_DRV_SUN6I) += rtc-sun6i.o
obj-$(CONFIG_RTC_DRV_SUNXI) += rtc-sunxi.o
obj-$(CONFIG_RTC_DRV_TEGRA) += rtc-tegra.o
obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o
-obj-$(CONFIG_RTC_DRV_TILE) += rtc-tile.o
obj-$(CONFIG_RTC_DRV_TPS6586X) += rtc-tps6586x.o
obj-$(CONFIG_RTC_DRV_TPS65910) += rtc-tps65910.o
obj-$(CONFIG_RTC_DRV_TPS80031) += rtc-tps80031.o
diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c
index 722d683e0b0f..d37588f08055 100644
--- a/drivers/rtc/class.c
+++ b/drivers/rtc/class.c
@@ -211,6 +211,73 @@ static int rtc_device_get_id(struct device *dev)
return id;
}
+static void rtc_device_get_offset(struct rtc_device *rtc)
+{
+ time64_t range_secs;
+ u32 start_year;
+ int ret;
+
+ /*
+ * If RTC driver did not implement the range of RTC hardware device,
+ * then we can not expand the RTC range by adding or subtracting one
+ * offset.
+ */
+ if (rtc->range_min == rtc->range_max)
+ return;
+
+ ret = device_property_read_u32(rtc->dev.parent, "start-year",
+ &start_year);
+ if (!ret) {
+ rtc->start_secs = mktime64(start_year, 1, 1, 0, 0, 0);
+ rtc->set_start_time = true;
+ }
+
+ /*
+ * If user did not implement the start time for RTC driver, then no
+ * need to expand the RTC range.
+ */
+ if (!rtc->set_start_time)
+ return;
+
+ range_secs = rtc->range_max - rtc->range_min + 1;
+
+ /*
+ * If the start_secs is larger than the maximum seconds (rtc->range_max)
+ * supported by RTC hardware or the maximum seconds of new expanded
+ * range (start_secs + rtc->range_max - rtc->range_min) is less than
+ * rtc->range_min, which means the minimum seconds (rtc->range_min) of
+ * RTC hardware will be mapped to start_secs by adding one offset, so
+ * the offset seconds calculation formula should be:
+ * rtc->offset_secs = rtc->start_secs - rtc->range_min;
+ *
+ * If the start_secs is larger than the minimum seconds (rtc->range_min)
+ * supported by RTC hardware, then there is one region is overlapped
+ * between the original RTC hardware range and the new expanded range,
+ * and this overlapped region do not need to be mapped into the new
+ * expanded range due to it is valid for RTC device. So the minimum
+ * seconds of RTC hardware (rtc->range_min) should be mapped to
+ * rtc->range_max + 1, then the offset seconds formula should be:
+ * rtc->offset_secs = rtc->range_max - rtc->range_min + 1;
+ *
+ * If the start_secs is less than the minimum seconds (rtc->range_min),
+ * which is similar to case 2. So the start_secs should be mapped to
+ * start_secs + rtc->range_max - rtc->range_min + 1, then the
+ * offset seconds formula should be:
+ * rtc->offset_secs = -(rtc->range_max - rtc->range_min + 1);
+ *
+ * Otherwise the offset seconds should be 0.
+ */
+ if (rtc->start_secs > rtc->range_max ||
+ rtc->start_secs + range_secs - 1 < rtc->range_min)
+ rtc->offset_secs = rtc->start_secs - rtc->range_min;
+ else if (rtc->start_secs > rtc->range_min)
+ rtc->offset_secs = range_secs;
+ else if (rtc->start_secs < rtc->range_min)
+ rtc->offset_secs = -range_secs;
+ else
+ rtc->offset_secs = 0;
+}
+
/**
* rtc_device_register - register w/ RTC class
* @dev: the device to register
@@ -247,6 +314,8 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev,
dev_set_name(&rtc->dev, "rtc%d", id);
+ rtc_device_get_offset(rtc);
+
/* Check to see if there is an ALARM already set in hw */
err = __rtc_read_alarm(rtc, &alrm);
@@ -293,8 +362,6 @@ EXPORT_SYMBOL_GPL(rtc_device_register);
*/
void rtc_device_unregister(struct rtc_device *rtc)
{
- rtc_nvmem_unregister(rtc);
-
mutex_lock(&rtc->ops_lock);
/*
* Remove innards of this RTC, then disable it, before
@@ -312,6 +379,7 @@ static void devm_rtc_device_release(struct device *dev, void *res)
{
struct rtc_device *rtc = *(struct rtc_device **)res;
+ rtc_nvmem_unregister(rtc);
rtc_device_unregister(rtc);
}
@@ -382,6 +450,8 @@ static void devm_rtc_release_device(struct device *dev, void *res)
{
struct rtc_device *rtc = *(struct rtc_device **)res;
+ rtc_nvmem_unregister(rtc);
+
if (rtc->registered)
rtc_device_unregister(rtc);
else
@@ -435,6 +505,7 @@ int __rtc_register_device(struct module *owner, struct rtc_device *rtc)
return -EINVAL;
rtc->owner = owner;
+ rtc_device_get_offset(rtc);
/* Check to see if there is an ALARM already set in hw */
err = __rtc_read_alarm(rtc, &alrm);
@@ -453,8 +524,6 @@ int __rtc_register_device(struct module *owner, struct rtc_device *rtc)
rtc_proc_add_device(rtc);
- rtc_nvmem_register(rtc);
-
rtc->registered = true;
dev_info(rtc->dev.parent, "registered as %s\n",
dev_name(&rtc->dev));
diff --git a/drivers/rtc/hctosys.c b/drivers/rtc/hctosys.c
index e1cfa06810ef..e79f2a181ad2 100644
--- a/drivers/rtc/hctosys.c
+++ b/drivers/rtc/hctosys.c
@@ -49,6 +49,11 @@ static int __init rtc_hctosys(void)
tv64.tv_sec = rtc_tm_to_time64(&tm);
+#if BITS_PER_LONG == 32
+ if (tv64.tv_sec > INT_MAX)
+ goto err_read;
+#endif
+
err = do_settimeofday64(&tv64);
dev_info(rtc->dev.parent,
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index 672b192f8153..7cbdc9228dd5 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -17,9 +17,73 @@
#include <linux/log2.h>
#include <linux/workqueue.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/rtc.h>
+
static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer);
static void rtc_timer_remove(struct rtc_device *rtc, struct rtc_timer *timer);
+static void rtc_add_offset(struct rtc_device *rtc, struct rtc_time *tm)
+{
+ time64_t secs;
+
+ if (!rtc->offset_secs)
+ return;
+
+ secs = rtc_tm_to_time64(tm);
+
+ /*
+ * Since the reading time values from RTC device are always in the RTC
+ * original valid range, but we need to skip the overlapped region
+ * between expanded range and original range, which is no need to add
+ * the offset.
+ */
+ if ((rtc->start_secs > rtc->range_min && secs >= rtc->start_secs) ||
+ (rtc->start_secs < rtc->range_min &&
+ secs <= (rtc->start_secs + rtc->range_max - rtc->range_min)))
+ return;
+
+ rtc_time64_to_tm(secs + rtc->offset_secs, tm);
+}
+
+static void rtc_subtract_offset(struct rtc_device *rtc, struct rtc_time *tm)
+{
+ time64_t secs;
+
+ if (!rtc->offset_secs)
+ return;
+
+ secs = rtc_tm_to_time64(tm);
+
+ /*
+ * If the setting time values are in the valid range of RTC hardware
+ * device, then no need to subtract the offset when setting time to RTC
+ * device. Otherwise we need to subtract the offset to make the time
+ * values are valid for RTC hardware device.
+ */
+ if (secs >= rtc->range_min && secs <= rtc->range_max)
+ return;
+
+ rtc_time64_to_tm(secs - rtc->offset_secs, tm);
+}
+
+static int rtc_valid_range(struct rtc_device *rtc, struct rtc_time *tm)
+{
+ if (rtc->range_min != rtc->range_max) {
+ time64_t time = rtc_tm_to_time64(tm);
+ time64_t range_min = rtc->set_start_time ? rtc->start_secs :
+ rtc->range_min;
+ time64_t range_max = rtc->set_start_time ?
+ (rtc->start_secs + rtc->range_max - rtc->range_min) :
+ rtc->range_max;
+
+ if (time < range_min || time > range_max)
+ return -ERANGE;
+ }
+
+ return 0;
+}
+
static int __rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
{
int err;
@@ -36,6 +100,8 @@ static int __rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
return err;
}
+ rtc_add_offset(rtc, tm);
+
err = rtc_valid_tm(tm);
if (err < 0)
dev_dbg(&rtc->dev, "read_time: rtc_time isn't valid\n");
@@ -53,6 +119,8 @@ int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
err = __rtc_read_time(rtc, tm);
mutex_unlock(&rtc->ops_lock);
+
+ trace_rtc_read_time(rtc_tm_to_time64(tm), err);
return err;
}
EXPORT_SYMBOL_GPL(rtc_read_time);
@@ -65,6 +133,12 @@ int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)
if (err != 0)
return err;
+ err = rtc_valid_range(rtc, tm);
+ if (err)
+ return err;
+
+ rtc_subtract_offset(rtc, tm);
+
err = mutex_lock_interruptible(&rtc->ops_lock);
if (err)
return err;
@@ -87,6 +161,8 @@ int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)
mutex_unlock(&rtc->ops_lock);
/* A timer might have just expired */
schedule_work(&rtc->irqwork);
+
+ trace_rtc_set_time(rtc_tm_to_time64(tm), err);
return err;
}
EXPORT_SYMBOL_GPL(rtc_set_time);
@@ -119,6 +195,8 @@ static int rtc_read_alarm_internal(struct rtc_device *rtc, struct rtc_wkalrm *al
}
mutex_unlock(&rtc->ops_lock);
+
+ trace_rtc_read_alarm(rtc_tm_to_time64(&alarm->time), err);
return err;
}
@@ -316,6 +394,7 @@ int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
}
mutex_unlock(&rtc->ops_lock);
+ trace_rtc_read_alarm(rtc_tm_to_time64(&alarm->time), err);
return err;
}
EXPORT_SYMBOL_GPL(rtc_read_alarm);
@@ -329,6 +408,8 @@ static int __rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
err = rtc_valid_tm(&alarm->time);
if (err)
return err;
+
+ rtc_subtract_offset(rtc, &alarm->time);
scheduled = rtc_tm_to_time64(&alarm->time);
/* Make sure we're not setting alarms in the past */
@@ -352,6 +433,7 @@ static int __rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
else
err = rtc->ops->set_alarm(rtc->dev.parent, alarm);
+ trace_rtc_set_alarm(rtc_tm_to_time64(&alarm->time), err);
return err;
}
@@ -363,6 +445,10 @@ int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
if (err != 0)
return err;
+ err = rtc_valid_range(rtc, &alarm->time);
+ if (err)
+ return err;
+
err = mutex_lock_interruptible(&rtc->ops_lock);
if (err)
return err;
@@ -375,6 +461,8 @@ int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
err = rtc_timer_enqueue(rtc, &rtc->aie_timer);
mutex_unlock(&rtc->ops_lock);
+
+ rtc_add_offset(rtc, &alarm->time);
return err;
}
EXPORT_SYMBOL_GPL(rtc_set_alarm);
@@ -406,6 +494,7 @@ int rtc_initialize_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
rtc->aie_timer.enabled = 1;
timerqueue_add(&rtc->timerqueue, &rtc->aie_timer.node);
+ trace_rtc_timer_enqueue(&rtc->aie_timer);
}
mutex_unlock(&rtc->ops_lock);
return err;
@@ -435,6 +524,8 @@ int rtc_alarm_irq_enable(struct rtc_device *rtc, unsigned int enabled)
err = rtc->ops->alarm_irq_enable(rtc->dev.parent, enabled);
mutex_unlock(&rtc->ops_lock);
+
+ trace_rtc_alarm_irq_enable(enabled, err);
return err;
}
EXPORT_SYMBOL_GPL(rtc_alarm_irq_enable);
@@ -709,6 +800,8 @@ retry:
rtc->pie_enabled = enabled;
}
spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
+
+ trace_rtc_irq_set_state(enabled, err);
return err;
}
EXPORT_SYMBOL_GPL(rtc_irq_set_state);
@@ -745,6 +838,8 @@ retry:
}
}
spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
+
+ trace_rtc_irq_set_freq(freq, err);
return err;
}
EXPORT_SYMBOL_GPL(rtc_irq_set_freq);
@@ -779,6 +874,7 @@ static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer)
}
timerqueue_add(&rtc->timerqueue, &timer->node);
+ trace_rtc_timer_enqueue(timer);
if (!next || ktime_before(timer->node.expires, next->expires)) {
struct rtc_wkalrm alarm;
int err;
@@ -790,6 +886,7 @@ static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer)
schedule_work(&rtc->irqwork);
} else if (err) {
timerqueue_del(&rtc->timerqueue, &timer->node);
+ trace_rtc_timer_dequeue(timer);
timer->enabled = 0;
return err;
}
@@ -803,6 +900,7 @@ static void rtc_alarm_disable(struct rtc_device *rtc)
return;
rtc->ops->alarm_irq_enable(rtc->dev.parent, false);
+ trace_rtc_alarm_irq_enable(0, 0);
}
/**
@@ -821,6 +919,7 @@ static void rtc_timer_remove(struct rtc_device *rtc, struct rtc_timer *timer)
{
struct timerqueue_node *next = timerqueue_getnext(&rtc->timerqueue);
timerqueue_del(&rtc->timerqueue, &timer->node);
+ trace_rtc_timer_dequeue(timer);
timer->enabled = 0;
if (next == &timer->node) {
struct rtc_wkalrm alarm;
@@ -871,16 +970,19 @@ again:
/* expire timer */
timer = container_of(next, struct rtc_timer, node);
timerqueue_del(&rtc->timerqueue, &timer->node);
+ trace_rtc_timer_dequeue(timer);
timer->enabled = 0;
if (timer->task.func)
timer->task.func(timer->task.private_data);
+ trace_rtc_timer_fired(timer);
/* Re-add/fwd periodic timers */
if (ktime_to_ns(timer->period)) {
timer->node.expires = ktime_add(timer->node.expires,
timer->period);
timer->enabled = 1;
timerqueue_add(&rtc->timerqueue, &timer->node);
+ trace_rtc_timer_enqueue(timer);
}
}
@@ -902,6 +1004,7 @@ reprogram:
timer = container_of(next, struct rtc_timer, node);
timerqueue_del(&rtc->timerqueue, &timer->node);
+ trace_rtc_timer_dequeue(timer);
timer->enabled = 0;
dev_err(&rtc->dev, "__rtc_set_alarm: err=%d\n", err);
goto again;
@@ -992,6 +1095,8 @@ int rtc_read_offset(struct rtc_device *rtc, long *offset)
mutex_lock(&rtc->ops_lock);
ret = rtc->ops->read_offset(rtc->dev.parent, offset);
mutex_unlock(&rtc->ops_lock);
+
+ trace_rtc_read_offset(*offset, ret);
return ret;
}
@@ -1025,5 +1130,7 @@ int rtc_set_offset(struct rtc_device *rtc, long offset)
mutex_lock(&rtc->ops_lock);
ret = rtc->ops->set_offset(rtc->dev.parent, offset);
mutex_unlock(&rtc->ops_lock);
+
+ trace_rtc_set_offset(offset, ret);
return ret;
}
diff --git a/drivers/rtc/nvmem.c b/drivers/rtc/nvmem.c
index 8567b4ed9ac6..17ec4c8d0fad 100644
--- a/drivers/rtc/nvmem.c
+++ b/drivers/rtc/nvmem.c
@@ -14,8 +14,6 @@
#include <linux/rtc.h>
#include <linux/sysfs.h>
-#include "rtc-core.h"
-
/*
* Deprecated ABI compatibility, this should be removed at some point
*/
@@ -46,7 +44,7 @@ rtc_nvram_write(struct file *filp, struct kobject *kobj,
return nvmem_device_write(rtc->nvmem, off, count, buf);
}
-static int rtc_nvram_register(struct rtc_device *rtc)
+static int rtc_nvram_register(struct rtc_device *rtc, size_t size)
{
int err;
@@ -64,7 +62,7 @@ static int rtc_nvram_register(struct rtc_device *rtc)
rtc->nvram->read = rtc_nvram_read;
rtc->nvram->write = rtc_nvram_write;
- rtc->nvram->size = rtc->nvmem_config->size;
+ rtc->nvram->size = size;
err = sysfs_create_bin_file(&rtc->dev.parent->kobj,
rtc->nvram);
@@ -84,21 +82,28 @@ static void rtc_nvram_unregister(struct rtc_device *rtc)
/*
* New ABI, uses nvmem
*/
-void rtc_nvmem_register(struct rtc_device *rtc)
+int rtc_nvmem_register(struct rtc_device *rtc,
+ struct nvmem_config *nvmem_config)
{
- if (!rtc->nvmem_config)
- return;
+ if (!IS_ERR_OR_NULL(rtc->nvmem))
+ return -EBUSY;
+
+ if (!nvmem_config)
+ return -ENODEV;
- rtc->nvmem_config->dev = &rtc->dev;
- rtc->nvmem_config->owner = rtc->owner;
- rtc->nvmem = nvmem_register(rtc->nvmem_config);
+ nvmem_config->dev = rtc->dev.parent;
+ nvmem_config->owner = rtc->owner;
+ rtc->nvmem = nvmem_register(nvmem_config);
if (IS_ERR_OR_NULL(rtc->nvmem))
- return;
+ return PTR_ERR(rtc->nvmem);
/* Register the old ABI */
if (rtc->nvram_old_abi)
- rtc_nvram_register(rtc);
+ rtc_nvram_register(rtc, nvmem_config->size);
+
+ return 0;
}
+EXPORT_SYMBOL_GPL(rtc_nvmem_register);
void rtc_nvmem_unregister(struct rtc_device *rtc)
{
diff --git a/drivers/rtc/rtc-88pm80x.c b/drivers/rtc/rtc-88pm80x.c
index 466bf7f9a285..6cbafefa80a2 100644
--- a/drivers/rtc/rtc-88pm80x.c
+++ b/drivers/rtc/rtc-88pm80x.c
@@ -134,9 +134,9 @@ static int pm80x_rtc_set_time(struct device *dev, struct rtc_time *tm)
struct pm80x_rtc_info *info = dev_get_drvdata(dev);
unsigned char buf[4];
unsigned long ticks, base, data;
- if ((tm->tm_year < 70) || (tm->tm_year > 138)) {
+ if (tm->tm_year > 206) {
dev_dbg(info->dev,
- "Set time %d out of range. Please set time between 1970 to 2038.\n",
+ "Set time %d out of range. Please set time between 1970 to 2106.\n",
1900 + tm->tm_year);
return -EINVAL;
}
diff --git a/drivers/rtc/rtc-88pm860x.c b/drivers/rtc/rtc-88pm860x.c
index 19e53b3b8e00..01ffc0ef8033 100644
--- a/drivers/rtc/rtc-88pm860x.c
+++ b/drivers/rtc/rtc-88pm860x.c
@@ -135,9 +135,9 @@ static int pm860x_rtc_set_time(struct device *dev, struct rtc_time *tm)
unsigned char buf[4];
unsigned long ticks, base, data;
- if ((tm->tm_year < 70) || (tm->tm_year > 138)) {
+ if (tm->tm_year > 206) {
dev_dbg(info->dev, "Set time %d out of range. "
- "Please set time between 1970 to 2038.\n",
+ "Please set time between 1970 to 2106.\n",
1900 + tm->tm_year);
return -EINVAL;
}
diff --git a/drivers/rtc/rtc-ab-b5ze-s3.c b/drivers/rtc/rtc-ab-b5ze-s3.c
index a319bf1e49de..8dc451932446 100644
--- a/drivers/rtc/rtc-ab-b5ze-s3.c
+++ b/drivers/rtc/rtc-ab-b5ze-s3.c
@@ -217,7 +217,7 @@ static int _abb5zes3_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct abb5zes3_rtc_data *data = dev_get_drvdata(dev);
u8 regs[ABB5ZES3_REG_RTC_SC + ABB5ZES3_RTC_SEC_LEN];
- int ret;
+ int ret = 0;
/*
* As we need to read CTRL1 register anyway to access 24/12h
@@ -255,8 +255,6 @@ static int _abb5zes3_rtc_read_time(struct device *dev, struct rtc_time *tm)
tm->tm_mon = bcd2bin(regs[ABB5ZES3_REG_RTC_MO]) - 1; /* starts at 1 */
tm->tm_year = bcd2bin(regs[ABB5ZES3_REG_RTC_YR]) + 100;
- ret = rtc_valid_tm(tm);
-
err:
return ret;
}
@@ -648,7 +646,7 @@ static int abb5zes3_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
ret);
return ret;
- }
+}
/* Enable or disable battery low irq generation */
static inline int _abb5zes3_rtc_battery_low_irq_enable(struct regmap *regmap,
diff --git a/drivers/rtc/rtc-ab3100.c b/drivers/rtc/rtc-ab3100.c
index 9b725c553058..821ff52a2222 100644
--- a/drivers/rtc/rtc-ab3100.c
+++ b/drivers/rtc/rtc-ab3100.c
@@ -106,7 +106,7 @@ static int ab3100_rtc_read_time(struct device *dev, struct rtc_time *tm)
rtc_time64_to_tm(time, tm);
- return rtc_valid_tm(tm);
+ return 0;
}
static int ab3100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
diff --git a/drivers/rtc/rtc-ab8500.c b/drivers/rtc/rtc-ab8500.c
index 24a0af650a1b..e28f4401fd35 100644
--- a/drivers/rtc/rtc-ab8500.c
+++ b/drivers/rtc/rtc-ab8500.c
@@ -36,10 +36,6 @@
#define AB8500_RTC_FORCE_BKUP_REG 0x0D
#define AB8500_RTC_CALIB_REG 0x0E
#define AB8500_RTC_SWITCH_STAT_REG 0x0F
-#define AB8540_RTC_ALRM_SEC 0x22
-#define AB8540_RTC_ALRM_MIN_LOW_REG 0x23
-#define AB8540_RTC_ALRM_MIN_MID_REG 0x24
-#define AB8540_RTC_ALRM_MIN_HI_REG 0x25
/* RtcReadRequest bits */
#define RTC_READ_REQUEST 0x01
@@ -63,11 +59,6 @@ static const u8 ab8500_rtc_alarm_regs[] = {
AB8500_RTC_ALRM_MIN_LOW_REG
};
-static const u8 ab8540_rtc_alarm_regs[] = {
- AB8540_RTC_ALRM_MIN_HI_REG, AB8540_RTC_ALRM_MIN_MID_REG,
- AB8540_RTC_ALRM_MIN_LOW_REG, AB8540_RTC_ALRM_SEC
-};
-
/* Calculate the seconds from 1970 to 01-01-2000 00:00:00 */
static unsigned long get_elapsed_seconds(int year)
{
@@ -131,7 +122,7 @@ static int ab8500_rtc_read_time(struct device *dev, struct rtc_time *tm)
secs += get_elapsed_seconds(AB8500_RTC_EPOCH);
rtc_time_to_tm(secs, tm);
- return rtc_valid_tm(tm);
+ return 0;
}
static int ab8500_rtc_set_time(struct device *dev, struct rtc_time *tm)
@@ -277,43 +268,6 @@ static int ab8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
return ab8500_rtc_irq_enable(dev, alarm->enabled);
}
-static int ab8540_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
-{
- int retval, i;
- unsigned char buf[ARRAY_SIZE(ab8540_rtc_alarm_regs)];
- unsigned long mins, secs = 0;
-
- if (alarm->time.tm_year < (AB8500_RTC_EPOCH - 1900)) {
- dev_dbg(dev, "year should be equal to or greater than %d\n",
- AB8500_RTC_EPOCH);
- return -EINVAL;
- }
-
- /* Get the number of seconds since 1970 */
- rtc_tm_to_time(&alarm->time, &secs);
-
- /*
- * Convert it to the number of seconds since 01-01-2000 00:00:00
- */
- secs -= get_elapsed_seconds(AB8500_RTC_EPOCH);
- mins = secs / 60;
-
- buf[3] = secs % 60;
- buf[2] = mins & 0xFF;
- buf[1] = (mins >> 8) & 0xFF;
- buf[0] = (mins >> 16) & 0xFF;
-
- /* Set the alarm time */
- for (i = 0; i < ARRAY_SIZE(ab8540_rtc_alarm_regs); i++) {
- retval = abx500_set_register_interruptible(dev, AB8500_RTC,
- ab8540_rtc_alarm_regs[i], buf[i]);
- if (retval < 0)
- return retval;
- }
-
- return ab8500_rtc_irq_enable(dev, alarm->enabled);
-}
-
static int ab8500_rtc_set_calibration(struct device *dev, int calibration)
{
int retval;
@@ -435,17 +389,8 @@ static const struct rtc_class_ops ab8500_rtc_ops = {
.alarm_irq_enable = ab8500_rtc_irq_enable,
};
-static const struct rtc_class_ops ab8540_rtc_ops = {
- .read_time = ab8500_rtc_read_time,
- .set_time = ab8500_rtc_set_time,
- .read_alarm = ab8500_rtc_read_alarm,
- .set_alarm = ab8540_rtc_set_alarm,
- .alarm_irq_enable = ab8500_rtc_irq_enable,
-};
-
static const struct platform_device_id ab85xx_rtc_ids[] = {
{ "ab8500-rtc", (kernel_ulong_t)&ab8500_rtc_ops, },
- { "ab8540-rtc", (kernel_ulong_t)&ab8540_rtc_ops, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, ab85xx_rtc_ids);
diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c
index b033bc556f5d..2cefa67a1132 100644
--- a/drivers/rtc/rtc-abx80x.c
+++ b/drivers/rtc/rtc-abx80x.c
@@ -172,11 +172,7 @@ static int abx80x_rtc_read_time(struct device *dev, struct rtc_time *tm)
tm->tm_mon = bcd2bin(buf[ABX8XX_REG_MO] & 0x1F) - 1;
tm->tm_year = bcd2bin(buf[ABX8XX_REG_YR]) + 100;
- err = rtc_valid_tm(tm);
- if (err < 0)
- dev_err(&client->dev, "retrieved date/time is not valid.\n");
-
- return err;
+ return 0;
}
static int abx80x_rtc_set_time(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-ac100.c b/drivers/rtc/rtc-ac100.c
index 8ff9dc3fe5bf..3fe576fdd45e 100644
--- a/drivers/rtc/rtc-ac100.c
+++ b/drivers/rtc/rtc-ac100.c
@@ -183,7 +183,29 @@ static int ac100_clkout_determine_rate(struct clk_hw *hw,
for (i = 0; i < num_parents; i++) {
struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i);
- unsigned long tmp, prate = clk_hw_get_rate(parent);
+ unsigned long tmp, prate;
+
+ /*
+ * The clock has two parents, one is a fixed clock which is
+ * internally registered by the ac100 driver. The other parent
+ * is a clock from the codec side of the chip, which we
+ * properly declare and reference in the devicetree and is
+ * not implemented in any driver right now.
+ * If the clock core looks for the parent of that second
+ * missing clock, it can't find one that is registered and
+ * returns NULL.
+ * So we end up in a situation where clk_hw_get_num_parents
+ * returns the amount of clocks we can be parented to, but
+ * clk_hw_get_parent_by_index will not return the orphan
+ * clocks.
+ * Thus we need to check if the parent exists before
+ * we get the parent rate, so we could use the RTC
+ * without waiting for the codec to be supported.
+ */
+ if (!parent)
+ continue;
+
+ prate = clk_hw_get_rate(parent);
tmp = ac100_clkout_round_rate(hw, req->rate, prate);
@@ -387,7 +409,7 @@ static int ac100_rtc_get_time(struct device *dev, struct rtc_time *rtc_tm)
rtc_tm->tm_year = bcd2bin(reg[6] & AC100_RTC_YEA_MASK) +
AC100_YEAR_OFF;
- return rtc_valid_tm(rtc_tm);
+ return 0;
}
static int ac100_rtc_set_time(struct device *dev, struct rtc_time *rtc_tm)
diff --git a/drivers/rtc/rtc-at91sam9.c b/drivers/rtc/rtc-at91sam9.c
index 7418a763ce52..ee71e647fd43 100644
--- a/drivers/rtc/rtc-at91sam9.c
+++ b/drivers/rtc/rtc-at91sam9.c
@@ -349,6 +349,7 @@ static const struct rtc_class_ops at91_rtc_ops = {
};
static const struct regmap_config gpbr_regmap_config = {
+ .name = "gpbr",
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
diff --git a/drivers/rtc/rtc-au1xxx.c b/drivers/rtc/rtc-au1xxx.c
index 2ba44ccb9c3a..7c5530c71285 100644
--- a/drivers/rtc/rtc-au1xxx.c
+++ b/drivers/rtc/rtc-au1xxx.c
@@ -36,7 +36,7 @@ static int au1xtoy_rtc_read_time(struct device *dev, struct rtc_time *tm)
rtc_time_to_tm(t, tm);
- return rtc_valid_tm(tm);
+ return 0;
}
static int au1xtoy_rtc_set_time(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-bfin.c b/drivers/rtc/rtc-bfin.c
deleted file mode 100644
index 15344b7c07c5..000000000000
--- a/drivers/rtc/rtc-bfin.c
+++ /dev/null
@@ -1,448 +0,0 @@
-/*
- * Blackfin On-Chip Real Time Clock Driver
- * Supports BF51x/BF52x/BF53[123]/BF53[467]/BF54x
- *
- * Copyright 2004-2010 Analog Devices Inc.
- *
- * Enter bugs at http://blackfin.uclinux.org/
- *
- * Licensed under the GPL-2 or later.
- */
-
-/* The biggest issue we deal with in this driver is that register writes are
- * synced to the RTC frequency of 1Hz. So if you write to a register and
- * attempt to write again before the first write has completed, the new write
- * is simply discarded. This can easily be troublesome if userspace disables
- * one event (say periodic) and then right after enables an event (say alarm).
- * Since all events are maintained in the same interrupt mask register, if
- * we wrote to it to disable the first event and then wrote to it again to
- * enable the second event, that second event would not be enabled as the
- * write would be discarded and things quickly fall apart.
- *
- * To keep this delay from significantly degrading performance (we, in theory,
- * would have to sleep for up to 1 second every time we wanted to write a
- * register), we only check the write pending status before we start to issue
- * a new write. We bank on the idea that it doesn't matter when the sync
- * happens so long as we don't attempt another write before it does. The only
- * time userspace would take this penalty is when they try and do multiple
- * operations right after another ... but in this case, they need to take the
- * sync penalty, so we should be OK.
- *
- * Also note that the RTC_ISTAT register does not suffer this penalty; its
- * writes to clear status registers complete immediately.
- */
-
-/* It may seem odd that there is no SWCNT code in here (which would be exposed
- * via the periodic interrupt event, or PIE). Since the Blackfin RTC peripheral
- * runs in units of seconds (N/HZ) but the Linux framework runs in units of HZ
- * (2^N HZ), there is no point in keeping code that only provides 1 HZ PIEs.
- * The same exact behavior can be accomplished by using the update interrupt
- * event (UIE). Maybe down the line the RTC peripheral will suck less in which
- * case we can re-introduce PIE support.
- */
-
-#include <linux/bcd.h>
-#include <linux/completion.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/rtc.h>
-#include <linux/seq_file.h>
-#include <linux/slab.h>
-
-#include <asm/blackfin.h>
-
-#define dev_dbg_stamp(dev) dev_dbg(dev, "%s:%i: here i am\n", __func__, __LINE__)
-
-struct bfin_rtc {
- struct rtc_device *rtc_dev;
- struct rtc_time rtc_alarm;
- u16 rtc_wrote_regs;
-};
-
-/* Bit values for the ISTAT / ICTL registers */
-#define RTC_ISTAT_WRITE_COMPLETE 0x8000
-#define RTC_ISTAT_WRITE_PENDING 0x4000
-#define RTC_ISTAT_ALARM_DAY 0x0040
-#define RTC_ISTAT_24HR 0x0020
-#define RTC_ISTAT_HOUR 0x0010
-#define RTC_ISTAT_MIN 0x0008
-#define RTC_ISTAT_SEC 0x0004
-#define RTC_ISTAT_ALARM 0x0002
-#define RTC_ISTAT_STOPWATCH 0x0001
-
-/* Shift values for RTC_STAT register */
-#define DAY_BITS_OFF 17
-#define HOUR_BITS_OFF 12
-#define MIN_BITS_OFF 6
-#define SEC_BITS_OFF 0
-
-/* Some helper functions to convert between the common RTC notion of time
- * and the internal Blackfin notion that is encoded in 32bits.
- */
-static inline u32 rtc_time_to_bfin(unsigned long now)
-{
- u32 sec = (now % 60);
- u32 min = (now % (60 * 60)) / 60;
- u32 hour = (now % (60 * 60 * 24)) / (60 * 60);
- u32 days = (now / (60 * 60 * 24));
- return (sec << SEC_BITS_OFF) +
- (min << MIN_BITS_OFF) +
- (hour << HOUR_BITS_OFF) +
- (days << DAY_BITS_OFF);
-}
-static inline unsigned long rtc_bfin_to_time(u32 rtc_bfin)
-{
- return (((rtc_bfin >> SEC_BITS_OFF) & 0x003F)) +
- (((rtc_bfin >> MIN_BITS_OFF) & 0x003F) * 60) +
- (((rtc_bfin >> HOUR_BITS_OFF) & 0x001F) * 60 * 60) +
- (((rtc_bfin >> DAY_BITS_OFF) & 0x7FFF) * 60 * 60 * 24);
-}
-static inline void rtc_bfin_to_tm(u32 rtc_bfin, struct rtc_time *tm)
-{
- rtc_time_to_tm(rtc_bfin_to_time(rtc_bfin), tm);
-}
-
-/**
- * bfin_rtc_sync_pending - make sure pending writes have complete
- *
- * Wait for the previous write to a RTC register to complete.
- * Unfortunately, we can't sleep here as that introduces a race condition when
- * turning on interrupt events. Consider this:
- * - process sets alarm
- * - process enables alarm
- * - process sleeps while waiting for rtc write to sync
- * - interrupt fires while process is sleeping
- * - interrupt acks the event by writing to ISTAT
- * - interrupt sets the WRITE PENDING bit
- * - interrupt handler finishes
- * - process wakes up, sees WRITE PENDING bit set, goes to sleep
- * - interrupt fires while process is sleeping
- * If anyone can point out the obvious solution here, i'm listening :). This
- * shouldn't be an issue on an SMP or preempt system as this function should
- * only be called with the rtc lock held.
- *
- * Other options:
- * - disable PREN so the sync happens at 32.768kHZ ... but this changes the
- * inc rate for all RTC registers from 1HZ to 32.768kHZ ...
- * - use the write complete IRQ
- */
-/*
-static void bfin_rtc_sync_pending_polled(void)
-{
- while (!(bfin_read_RTC_ISTAT() & RTC_ISTAT_WRITE_COMPLETE))
- if (!(bfin_read_RTC_ISTAT() & RTC_ISTAT_WRITE_PENDING))
- break;
- bfin_write_RTC_ISTAT(RTC_ISTAT_WRITE_COMPLETE);
-}
-*/
-static DECLARE_COMPLETION(bfin_write_complete);
-static void bfin_rtc_sync_pending(struct device *dev)
-{
- dev_dbg_stamp(dev);
- while (bfin_read_RTC_ISTAT() & RTC_ISTAT_WRITE_PENDING)
- wait_for_completion_timeout(&bfin_write_complete, HZ * 5);
- dev_dbg_stamp(dev);
-}
-
-/**
- * bfin_rtc_reset - set RTC to sane/known state
- *
- * Initialize the RTC. Enable pre-scaler to scale RTC clock
- * to 1Hz and clear interrupt/status registers.
- */
-static void bfin_rtc_reset(struct device *dev, u16 rtc_ictl)
-{
- struct bfin_rtc *rtc = dev_get_drvdata(dev);
- dev_dbg_stamp(dev);
- bfin_rtc_sync_pending(dev);
- bfin_write_RTC_PREN(0x1);
- bfin_write_RTC_ICTL(rtc_ictl);
- bfin_write_RTC_ALARM(0);
- bfin_write_RTC_ISTAT(0xFFFF);
- rtc->rtc_wrote_regs = 0;
-}
-
-/**
- * bfin_rtc_interrupt - handle interrupt from RTC
- *
- * Since we handle all RTC events here, we have to make sure the requested
- * interrupt is enabled (in RTC_ICTL) as the event status register (RTC_ISTAT)
- * always gets updated regardless of the interrupt being enabled. So when one
- * even we care about (e.g. stopwatch) goes off, we don't want to turn around
- * and say that other events have happened as well (e.g. second). We do not
- * have to worry about pending writes to the RTC_ICTL register as interrupts
- * only fire if they are enabled in the RTC_ICTL register.
- */
-static irqreturn_t bfin_rtc_interrupt(int irq, void *dev_id)
-{
- struct device *dev = dev_id;
- struct bfin_rtc *rtc = dev_get_drvdata(dev);
- unsigned long events = 0;
- bool write_complete = false;
- u16 rtc_istat, rtc_istat_clear, rtc_ictl, bits;
-
- dev_dbg_stamp(dev);
-
- rtc_istat = bfin_read_RTC_ISTAT();
- rtc_ictl = bfin_read_RTC_ICTL();
- rtc_istat_clear = 0;
-
- bits = RTC_ISTAT_WRITE_COMPLETE;
- if (rtc_istat & bits) {
- rtc_istat_clear |= bits;
- write_complete = true;
- complete(&bfin_write_complete);
- }
-
- bits = (RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY);
- if (rtc_ictl & bits) {
- if (rtc_istat & bits) {
- rtc_istat_clear |= bits;
- events |= RTC_AF | RTC_IRQF;
- }
- }
-
- bits = RTC_ISTAT_SEC;
- if (rtc_ictl & bits) {
- if (rtc_istat & bits) {
- rtc_istat_clear |= bits;
- events |= RTC_UF | RTC_IRQF;
- }
- }
-
- if (events)
- rtc_update_irq(rtc->rtc_dev, 1, events);
-
- if (write_complete || events) {
- bfin_write_RTC_ISTAT(rtc_istat_clear);
- return IRQ_HANDLED;
- } else
- return IRQ_NONE;
-}
-
-static void bfin_rtc_int_set(u16 rtc_int)
-{
- bfin_write_RTC_ISTAT(rtc_int);
- bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() | rtc_int);
-}
-static void bfin_rtc_int_clear(u16 rtc_int)
-{
- bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() & rtc_int);
-}
-static void bfin_rtc_int_set_alarm(struct bfin_rtc *rtc)
-{
- /* Blackfin has different bits for whether the alarm is
- * more than 24 hours away.
- */
- bfin_rtc_int_set(rtc->rtc_alarm.tm_yday == -1 ? RTC_ISTAT_ALARM : RTC_ISTAT_ALARM_DAY);
-}
-
-static int bfin_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
-{
- struct bfin_rtc *rtc = dev_get_drvdata(dev);
-
- dev_dbg_stamp(dev);
- if (enabled)
- bfin_rtc_int_set_alarm(rtc);
- else
- bfin_rtc_int_clear(~(RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY));
-
- return 0;
-}
-
-static int bfin_rtc_read_time(struct device *dev, struct rtc_time *tm)
-{
- struct bfin_rtc *rtc = dev_get_drvdata(dev);
-
- dev_dbg_stamp(dev);
-
- if (rtc->rtc_wrote_regs & 0x1)
- bfin_rtc_sync_pending(dev);
-
- rtc_bfin_to_tm(bfin_read_RTC_STAT(), tm);
-
- return 0;
-}
-
-static int bfin_rtc_set_time(struct device *dev, struct rtc_time *tm)
-{
- struct bfin_rtc *rtc = dev_get_drvdata(dev);
- int ret;
- unsigned long now;
-
- dev_dbg_stamp(dev);
-
- ret = rtc_tm_to_time(tm, &now);
- if (ret == 0) {
- if (rtc->rtc_wrote_regs & 0x1)
- bfin_rtc_sync_pending(dev);
- bfin_write_RTC_STAT(rtc_time_to_bfin(now));
- rtc->rtc_wrote_regs = 0x1;
- }
-
- return ret;
-}
-
-static int bfin_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
-{
- struct bfin_rtc *rtc = dev_get_drvdata(dev);
- dev_dbg_stamp(dev);
- alrm->time = rtc->rtc_alarm;
- bfin_rtc_sync_pending(dev);
- alrm->enabled = !!(bfin_read_RTC_ICTL() & (RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY));
- return 0;
-}
-
-static int bfin_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
-{
- struct bfin_rtc *rtc = dev_get_drvdata(dev);
- unsigned long rtc_alarm;
-
- dev_dbg_stamp(dev);
-
- if (rtc_tm_to_time(&alrm->time, &rtc_alarm))
- return -EINVAL;
-
- rtc->rtc_alarm = alrm->time;
-
- bfin_rtc_sync_pending(dev);
- bfin_write_RTC_ALARM(rtc_time_to_bfin(rtc_alarm));
- if (alrm->enabled)
- bfin_rtc_int_set_alarm(rtc);
-
- return 0;
-}
-
-static int bfin_rtc_proc(struct device *dev, struct seq_file *seq)
-{
-#define yesno(x) ((x) ? "yes" : "no")
- u16 ictl = bfin_read_RTC_ICTL();
- dev_dbg_stamp(dev);
- seq_printf(seq,
- "alarm_IRQ\t: %s\n"
- "wkalarm_IRQ\t: %s\n"
- "seconds_IRQ\t: %s\n",
- yesno(ictl & RTC_ISTAT_ALARM),
- yesno(ictl & RTC_ISTAT_ALARM_DAY),
- yesno(ictl & RTC_ISTAT_SEC));
- return 0;
-#undef yesno
-}
-
-static const struct rtc_class_ops bfin_rtc_ops = {
- .read_time = bfin_rtc_read_time,
- .set_time = bfin_rtc_set_time,
- .read_alarm = bfin_rtc_read_alarm,
- .set_alarm = bfin_rtc_set_alarm,
- .proc = bfin_rtc_proc,
- .alarm_irq_enable = bfin_rtc_alarm_irq_enable,
-};
-
-static int bfin_rtc_probe(struct platform_device *pdev)
-{
- struct bfin_rtc *rtc;
- struct device *dev = &pdev->dev;
- int ret;
- unsigned long timeout = jiffies + HZ;
-
- dev_dbg_stamp(dev);
-
- /* Allocate memory for our RTC struct */
- rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL);
- if (unlikely(!rtc))
- return -ENOMEM;
- platform_set_drvdata(pdev, rtc);
- device_init_wakeup(dev, 1);
-
- /* Register our RTC with the RTC framework */
- rtc->rtc_dev = devm_rtc_device_register(dev, pdev->name, &bfin_rtc_ops,
- THIS_MODULE);
- if (IS_ERR(rtc->rtc_dev))
- return PTR_ERR(rtc->rtc_dev);
-
- /* Grab the IRQ and init the hardware */
- ret = devm_request_irq(dev, IRQ_RTC, bfin_rtc_interrupt, 0,
- pdev->name, dev);
- if (unlikely(ret))
- dev_err(&pdev->dev,
- "unable to request IRQ; alarm won't work, "
- "and writes will be delayed\n");
-
- /* sometimes the bootloader touched things, but the write complete was not
- * enabled, so let's just do a quick timeout here since the IRQ will not fire ...
- */
- while (bfin_read_RTC_ISTAT() & RTC_ISTAT_WRITE_PENDING)
- if (time_after(jiffies, timeout))
- break;
- bfin_rtc_reset(dev, RTC_ISTAT_WRITE_COMPLETE);
- bfin_write_RTC_SWCNT(0);
-
- return 0;
-}
-
-static int bfin_rtc_remove(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
-
- bfin_rtc_reset(dev, 0);
-
- return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int bfin_rtc_suspend(struct device *dev)
-{
- dev_dbg_stamp(dev);
-
- if (device_may_wakeup(dev)) {
- enable_irq_wake(IRQ_RTC);
- bfin_rtc_sync_pending(dev);
- } else
- bfin_rtc_int_clear(0);
-
- return 0;
-}
-
-static int bfin_rtc_resume(struct device *dev)
-{
- dev_dbg_stamp(dev);
-
- if (device_may_wakeup(dev))
- disable_irq_wake(IRQ_RTC);
-
- /*
- * Since only some of the RTC bits are maintained externally in the
- * Vbat domain, we need to wait for the RTC MMRs to be synced into
- * the core after waking up. This happens every RTC 1HZ. Once that
- * has happened, we can go ahead and re-enable the important write
- * complete interrupt event.
- */
- while (!(bfin_read_RTC_ISTAT() & RTC_ISTAT_SEC))
- continue;
- bfin_rtc_int_set(RTC_ISTAT_WRITE_COMPLETE);
-
- return 0;
-}
-#endif
-
-static SIMPLE_DEV_PM_OPS(bfin_rtc_pm_ops, bfin_rtc_suspend, bfin_rtc_resume);
-
-static struct platform_driver bfin_rtc_driver = {
- .driver = {
- .name = "rtc-bfin",
- .pm = &bfin_rtc_pm_ops,
- },
- .probe = bfin_rtc_probe,
- .remove = bfin_rtc_remove,
-};
-
-module_platform_driver(bfin_rtc_driver);
-
-MODULE_DESCRIPTION("Blackfin On-Chip Real Time Clock Driver");
-MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:rtc-bfin");
diff --git a/drivers/rtc/rtc-bq32k.c b/drivers/rtc/rtc-bq32k.c
index 98ac8d5c7901..ef52741000a8 100644
--- a/drivers/rtc/rtc-bq32k.c
+++ b/drivers/rtc/rtc-bq32k.c
@@ -36,6 +36,10 @@
#define BQ32K_CFG2 0x09 /* Trickle charger control */
#define BQ32K_TCFE BIT(6) /* Trickle charge FET bypass */
+#define MAX_LEN 10 /* Maximum number of consecutive
+ * register for this particular RTC.
+ */
+
struct bq32k_regs {
uint8_t seconds;
uint8_t minutes;
@@ -74,7 +78,7 @@ static int bq32k_read(struct device *dev, void *data, uint8_t off, uint8_t len)
static int bq32k_write(struct device *dev, void *data, uint8_t off, uint8_t len)
{
struct i2c_client *client = to_i2c_client(dev);
- uint8_t buffer[len + 1];
+ uint8_t buffer[MAX_LEN + 1];
buffer[0] = off;
memcpy(&buffer[1], data, len);
@@ -110,7 +114,7 @@ static int bq32k_rtc_read_time(struct device *dev, struct rtc_time *tm)
tm->tm_year = bcd2bin(regs.years) +
((regs.cent_hours & BQ32K_CENT) ? 100 : 0);
- return rtc_valid_tm(tm);
+ return 0;
}
static int bq32k_rtc_set_time(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-brcmstb-waketimer.c b/drivers/rtc/rtc-brcmstb-waketimer.c
index 6cee61201c30..bdd6674a1054 100644
--- a/drivers/rtc/rtc-brcmstb-waketimer.c
+++ b/drivers/rtc/rtc-brcmstb-waketimer.c
@@ -60,6 +60,9 @@ static void brcmstb_waketmr_set_alarm(struct brcmstb_waketmr *timer,
{
brcmstb_waketmr_clear_alarm(timer);
+ /* Make sure we are actually counting in seconds */
+ writel_relaxed(timer->rate, timer->base + BRCMSTB_WKTMR_PRESCALER);
+
writel_relaxed(secs + 1, timer->base + BRCMSTB_WKTMR_ALARM);
}
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index 9dca53df3584..1b3738a11702 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -541,11 +541,10 @@ static const struct rtc_class_ops cmos_rtc_ops = {
#define NVRAM_OFFSET (RTC_REG_D + 1)
-static ssize_t
-cmos_nvram_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *attr,
- char *buf, loff_t off, size_t count)
+static int cmos_nvram_read(void *priv, unsigned int off, void *val,
+ size_t count)
{
+ unsigned char *buf = val;
int retval;
off += NVRAM_OFFSET;
@@ -563,16 +562,13 @@ cmos_nvram_read(struct file *filp, struct kobject *kobj,
return retval;
}
-static ssize_t
-cmos_nvram_write(struct file *filp, struct kobject *kobj,
- struct bin_attribute *attr,
- char *buf, loff_t off, size_t count)
+static int cmos_nvram_write(void *priv, unsigned int off, void *val,
+ size_t count)
{
- struct cmos_rtc *cmos;
+ struct cmos_rtc *cmos = priv;
+ unsigned char *buf = val;
int retval;
- cmos = dev_get_drvdata(container_of(kobj, struct device, kobj));
-
/* NOTE: on at least PCs and Ataris, the boot firmware uses a
* checksum on part of the NVRAM data. That's currently ignored
* here. If userspace is smart enough to know what fields of
@@ -598,17 +594,6 @@ cmos_nvram_write(struct file *filp, struct kobject *kobj,
return retval;
}
-static struct bin_attribute nvram = {
- .attr = {
- .name = "nvram",
- .mode = S_IRUGO | S_IWUSR,
- },
-
- .read = cmos_nvram_read,
- .write = cmos_nvram_write,
- /* size gets set up later */
-};
-
/*----------------------------------------------------------------*/
static struct cmos_rtc cmos_rtc;
@@ -675,6 +660,14 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
unsigned char rtc_control;
unsigned address_space;
u32 flags = 0;
+ struct nvmem_config nvmem_cfg = {
+ .name = "cmos_nvram",
+ .word_size = 1,
+ .stride = 1,
+ .reg_read = cmos_nvram_read,
+ .reg_write = cmos_nvram_write,
+ .priv = &cmos_rtc,
+ };
/* there can be only one ... */
if (cmos_rtc.dev)
@@ -711,7 +704,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
address_space = 64;
#elif defined(__i386__) || defined(__x86_64__) || defined(__arm__) \
|| defined(__sparc__) || defined(__mips__) \
- || defined(__powerpc__) || defined(CONFIG_MN10300)
+ || defined(__powerpc__)
address_space = 128;
#else
#warning Assuming 128 bytes of RTC+NVRAM address space, not 64 bytes.
@@ -751,8 +744,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
cmos_rtc.dev = dev;
dev_set_drvdata(dev, &cmos_rtc);
- cmos_rtc.rtc = rtc_device_register(driver_name, dev,
- &cmos_rtc_ops, THIS_MODULE);
+ cmos_rtc.rtc = devm_rtc_allocate_device(dev);
if (IS_ERR(cmos_rtc.rtc)) {
retval = PTR_ERR(cmos_rtc.rtc);
goto cleanup0;
@@ -814,22 +806,25 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
}
}
- /* export at least the first block of NVRAM */
- nvram.size = address_space - NVRAM_OFFSET;
- retval = sysfs_create_bin_file(&dev->kobj, &nvram);
- if (retval < 0) {
- dev_dbg(dev, "can't create nvram file? %d\n", retval);
+ cmos_rtc.rtc->ops = &cmos_rtc_ops;
+ cmos_rtc.rtc->nvram_old_abi = true;
+ retval = rtc_register_device(cmos_rtc.rtc);
+ if (retval)
goto cleanup2;
- }
- dev_info(dev, "%s%s, %zd bytes nvram%s\n",
- !is_valid_irq(rtc_irq) ? "no alarms" :
- cmos_rtc.mon_alrm ? "alarms up to one year" :
- cmos_rtc.day_alrm ? "alarms up to one month" :
- "alarms up to one day",
- cmos_rtc.century ? ", y3k" : "",
- nvram.size,
- is_hpet_enabled() ? ", hpet irqs" : "");
+ /* export at least the first block of NVRAM */
+ nvmem_cfg.size = address_space - NVRAM_OFFSET;
+ if (rtc_nvmem_register(cmos_rtc.rtc, &nvmem_cfg))
+ dev_err(dev, "nvmem registration failed\n");
+
+ dev_info(dev, "%s%s, %d bytes nvram%s\n",
+ !is_valid_irq(rtc_irq) ? "no alarms" :
+ cmos_rtc.mon_alrm ? "alarms up to one year" :
+ cmos_rtc.day_alrm ? "alarms up to one month" :
+ "alarms up to one day",
+ cmos_rtc.century ? ", y3k" : "",
+ nvmem_cfg.size,
+ is_hpet_enabled() ? ", hpet irqs" : "");
return 0;
@@ -838,7 +833,6 @@ cleanup2:
free_irq(rtc_irq, cmos_rtc.rtc);
cleanup1:
cmos_rtc.dev = NULL;
- rtc_device_unregister(cmos_rtc.rtc);
cleanup0:
if (RTC_IOMAPPED)
release_region(ports->start, resource_size(ports));
@@ -862,14 +856,11 @@ static void cmos_do_remove(struct device *dev)
cmos_do_shutdown(cmos->irq);
- sysfs_remove_bin_file(&dev->kobj, &nvram);
-
if (is_valid_irq(cmos->irq)) {
free_irq(cmos->irq, cmos->rtc);
hpet_unregister_irq_handler(cmos_interrupt);
}
- rtc_device_unregister(cmos->rtc);
cmos->rtc = NULL;
ports = cmos->iomem;
@@ -1271,8 +1262,6 @@ MODULE_DEVICE_TABLE(of, of_cmos_match);
static __init void cmos_of_init(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
- struct rtc_time time;
- int ret;
const __be32 *val;
if (!node)
@@ -1285,16 +1274,6 @@ static __init void cmos_of_init(struct platform_device *pdev)
val = of_get_property(node, "freq-reg", NULL);
if (val)
CMOS_WRITE(be32_to_cpup(val), RTC_FREQ_SELECT);
-
- cmos_read_time(&pdev->dev, &time);
- ret = rtc_valid_tm(&time);
- if (ret) {
- struct rtc_time def_time = {
- .tm_year = 1,
- .tm_mday = 1,
- };
- cmos_set_time(&pdev->dev, &def_time);
- }
}
#else
static inline void cmos_of_init(struct platform_device *pdev) {}
diff --git a/drivers/rtc/rtc-coh901331.c b/drivers/rtc/rtc-coh901331.c
index cfc4141d99cd..2fc517498a5d 100644
--- a/drivers/rtc/rtc-coh901331.c
+++ b/drivers/rtc/rtc-coh901331.c
@@ -82,7 +82,7 @@ static int coh901331_read_time(struct device *dev, struct rtc_time *tm)
if (readl(rtap->virtbase + COH901331_VALID)) {
rtc_time_to_tm(readl(rtap->virtbase + COH901331_CUR_TIME), tm);
clk_disable(rtap->clk);
- return rtc_valid_tm(tm);
+ return 0;
}
clk_disable(rtap->clk);
return -EINVAL;
diff --git a/drivers/rtc/rtc-core.h b/drivers/rtc/rtc-core.h
index 513b9bedd2c8..0abf98983e13 100644
--- a/drivers/rtc/rtc-core.h
+++ b/drivers/rtc/rtc-core.h
@@ -46,11 +46,3 @@ static inline const struct attribute_group **rtc_get_dev_attribute_groups(void)
return NULL;
}
#endif
-
-#ifdef CONFIG_RTC_NVMEM
-void rtc_nvmem_register(struct rtc_device *rtc);
-void rtc_nvmem_unregister(struct rtc_device *rtc);
-#else
-static inline void rtc_nvmem_register(struct rtc_device *rtc) {}
-static inline void rtc_nvmem_unregister(struct rtc_device *rtc) {}
-#endif
diff --git a/drivers/rtc/rtc-cpcap.c b/drivers/rtc/rtc-cpcap.c
index 3a0333e1f21a..a8856f2b9bc2 100644
--- a/drivers/rtc/rtc-cpcap.c
+++ b/drivers/rtc/rtc-cpcap.c
@@ -119,7 +119,7 @@ static int cpcap_rtc_read_time(struct device *dev, struct rtc_time *tm)
cpcap2rtc_time(tm, &cpcap_tm);
- return rtc_valid_tm(tm);
+ return 0;
}
static int cpcap_rtc_set_time(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-cros-ec.c b/drivers/rtc/rtc-cros-ec.c
index f0ea6899c731..bf7ced095c94 100644
--- a/drivers/rtc/rtc-cros-ec.c
+++ b/drivers/rtc/rtc-cros-ec.c
@@ -197,10 +197,10 @@ static int cros_ec_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
cros_ec_rtc->saved_alarm = (u32)alarm_time;
} else {
/* Don't set an alarm in the past. */
- if ((u32)alarm_time < current_time)
- alarm_offset = EC_RTC_ALARM_CLEAR;
- else
- alarm_offset = (u32)alarm_time - current_time;
+ if ((u32)alarm_time <= current_time)
+ return -ETIME;
+
+ alarm_offset = (u32)alarm_time - current_time;
}
ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM, alarm_offset);
diff --git a/drivers/rtc/rtc-da9052.c b/drivers/rtc/rtc-da9052.c
index 4273377562ec..03044e1bc497 100644
--- a/drivers/rtc/rtc-da9052.c
+++ b/drivers/rtc/rtc-da9052.c
@@ -187,8 +187,7 @@ static int da9052_rtc_read_time(struct device *dev, struct rtc_time *rtc_tm)
rtc_tm->tm_min = v[0][1] & DA9052_RTC_MIN;
rtc_tm->tm_sec = v[0][0] & DA9052_RTC_SEC;
- ret = rtc_valid_tm(rtc_tm);
- return ret;
+ return 0;
}
idx = (1-idx);
diff --git a/drivers/rtc/rtc-da9055.c b/drivers/rtc/rtc-da9055.c
index 678af8648c45..e08cd8130c23 100644
--- a/drivers/rtc/rtc-da9055.c
+++ b/drivers/rtc/rtc-da9055.c
@@ -158,7 +158,7 @@ static int da9055_rtc_read_time(struct device *dev, struct rtc_time *rtc_tm)
rtc_tm->tm_min = v[1] & DA9055_RTC_MIN;
rtc_tm->tm_sec = v[0] & DA9055_RTC_SEC;
- return rtc_valid_tm(rtc_tm);
+ return 0;
}
static int da9055_rtc_set_time(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-da9063.c b/drivers/rtc/rtc-da9063.c
index f85cae240f12..b4e054c64bad 100644
--- a/drivers/rtc/rtc-da9063.c
+++ b/drivers/rtc/rtc-da9063.c
@@ -256,7 +256,7 @@ static int da9063_rtc_read_time(struct device *dev, struct rtc_time *tm)
else
rtc->rtc_sync = false;
- return rtc_valid_tm(tm);
+ return 0;
}
static int da9063_rtc_set_time(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-ds1216.c b/drivers/rtc/rtc-ds1216.c
index 9c82b1da2d45..5f158715fb4c 100644
--- a/drivers/rtc/rtc-ds1216.c
+++ b/drivers/rtc/rtc-ds1216.c
@@ -99,7 +99,7 @@ static int ds1216_rtc_read_time(struct device *dev, struct rtc_time *tm)
if (tm->tm_year < 70)
tm->tm_year += 100;
- return rtc_valid_tm(tm);
+ return 0;
}
static int ds1216_rtc_set_time(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-ds1286.c b/drivers/rtc/rtc-ds1286.c
index ef75c349dff9..0744916b79c5 100644
--- a/drivers/rtc/rtc-ds1286.c
+++ b/drivers/rtc/rtc-ds1286.c
@@ -211,7 +211,7 @@ static int ds1286_read_time(struct device *dev, struct rtc_time *tm)
tm->tm_mon--;
- return rtc_valid_tm(tm);
+ return 0;
}
static int ds1286_set_time(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-ds1302.c b/drivers/rtc/rtc-ds1302.c
index 0ec4be62322b..2a881150d51c 100644
--- a/drivers/rtc/rtc-ds1302.c
+++ b/drivers/rtc/rtc-ds1302.c
@@ -43,7 +43,7 @@ static int ds1302_rtc_set_time(struct device *dev, struct rtc_time *time)
{
struct spi_device *spi = dev_get_drvdata(dev);
u8 buf[1 + RTC_CLCK_LEN];
- u8 *bp = buf;
+ u8 *bp;
int status;
/* Enable writing */
@@ -98,8 +98,7 @@ static int ds1302_rtc_get_time(struct device *dev, struct rtc_time *time)
time->tm_mon = bcd2bin(buf[RTC_ADDR_MON]) - 1;
time->tm_year = bcd2bin(buf[RTC_ADDR_YEAR]) + 100;
- /* Time may not be set */
- return rtc_valid_tm(time);
+ return 0;
}
static const struct rtc_class_ops ds1302_rtc_ops = {
@@ -112,7 +111,7 @@ static int ds1302_probe(struct spi_device *spi)
struct rtc_device *rtc;
u8 addr;
u8 buf[4];
- u8 *bp = buf;
+ u8 *bp;
int status;
/* Sanity check board setup data. This may be hooked up
diff --git a/drivers/rtc/rtc-ds1305.c b/drivers/rtc/rtc-ds1305.c
index d8df2e9e14ad..2d502fc85698 100644
--- a/drivers/rtc/rtc-ds1305.c
+++ b/drivers/rtc/rtc-ds1305.c
@@ -203,8 +203,7 @@ static int ds1305_get_time(struct device *dev, struct rtc_time *time)
time->tm_hour, time->tm_mday,
time->tm_mon, time->tm_year, time->tm_wday);
- /* Time may not be set */
- return rtc_valid_tm(time);
+ return 0;
}
static int ds1305_set_time(struct device *dev, struct rtc_time *time)
@@ -544,15 +543,6 @@ static int ds1305_nvram_write(void *priv, unsigned int off, void *buf,
return spi_sync(spi, &m);
}
-static struct nvmem_config ds1305_nvmem_cfg = {
- .name = "ds1305_nvram",
- .word_size = 1,
- .stride = 1,
- .size = DS1305_NVRAM_LEN,
- .reg_read = ds1305_nvram_read,
- .reg_write = ds1305_nvram_write,
-};
-
/*----------------------------------------------------------------------*/
/*
@@ -566,6 +556,14 @@ static int ds1305_probe(struct spi_device *spi)
u8 addr, value;
struct ds1305_platform_data *pdata = dev_get_platdata(&spi->dev);
bool write_ctrl = false;
+ struct nvmem_config ds1305_nvmem_cfg = {
+ .name = "ds1305_nvram",
+ .word_size = 1,
+ .stride = 1,
+ .size = DS1305_NVRAM_LEN,
+ .reg_read = ds1305_nvram_read,
+ .reg_write = ds1305_nvram_write,
+ };
/* Sanity check board setup data. This may be hooked up
* in 3wire mode, but we don't care. Note that unless
@@ -703,15 +701,15 @@ static int ds1305_probe(struct spi_device *spi)
ds1305->rtc->ops = &ds1305_ops;
ds1305_nvmem_cfg.priv = ds1305;
- ds1305->rtc->nvmem_config = &ds1305_nvmem_cfg;
ds1305->rtc->nvram_old_abi = true;
-
status = rtc_register_device(ds1305->rtc);
if (status) {
dev_dbg(&spi->dev, "register rtc --> %d\n", status);
return status;
}
+ rtc_nvmem_register(ds1305->rtc, &ds1305_nvmem_cfg);
+
/* Maybe set up alarm IRQ; be ready to handle it triggering right
* away. NOTE that we don't share this. The signal is active low,
* and we can't ack it before a SPI message delay. We temporarily
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
index 923dde912f60..a13e59edff53 100644
--- a/drivers/rtc/rtc-ds1307.c
+++ b/drivers/rtc/rtc-ds1307.c
@@ -114,7 +114,6 @@ enum ds_type {
# define RX8025_BIT_XST 0x20
struct ds1307 {
- struct nvmem_config nvmem_cfg;
enum ds_type type;
unsigned long flags;
#define HAS_NVRAM 0 /* bit 0 == sysfs file active */
@@ -438,8 +437,7 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t)
t->tm_hour, t->tm_mday,
t->tm_mon, t->tm_year, t->tm_wday);
- /* initial clock setting can be undefined */
- return rtc_valid_tm(t);
+ return 0;
}
static int ds1307_set_time(struct device *dev, struct rtc_time *t)
@@ -1696,24 +1694,26 @@ read_rtc:
}
}
- if (chip->nvram_size) {
- ds1307->nvmem_cfg.name = "ds1307_nvram";
- ds1307->nvmem_cfg.word_size = 1;
- ds1307->nvmem_cfg.stride = 1;
- ds1307->nvmem_cfg.size = chip->nvram_size;
- ds1307->nvmem_cfg.reg_read = ds1307_nvram_read;
- ds1307->nvmem_cfg.reg_write = ds1307_nvram_write;
- ds1307->nvmem_cfg.priv = ds1307;
-
- ds1307->rtc->nvmem_config = &ds1307->nvmem_cfg;
- ds1307->rtc->nvram_old_abi = true;
- }
-
ds1307->rtc->ops = chip->rtc_ops ?: &ds13xx_rtc_ops;
err = rtc_register_device(ds1307->rtc);
if (err)
return err;
+ if (chip->nvram_size) {
+ struct nvmem_config nvmem_cfg = {
+ .name = "ds1307_nvram",
+ .word_size = 1,
+ .stride = 1,
+ .size = chip->nvram_size,
+ .reg_read = ds1307_nvram_read,
+ .reg_write = ds1307_nvram_write,
+ .priv = ds1307,
+ };
+
+ ds1307->rtc->nvram_old_abi = true;
+ rtc_nvmem_register(ds1307->rtc, &nvmem_cfg);
+ }
+
ds1307_hwmon_register(ds1307);
ds1307_clks_register(ds1307);
diff --git a/drivers/rtc/rtc-ds1343.c b/drivers/rtc/rtc-ds1343.c
index 895fbeeb47fe..5208da4cf94a 100644
--- a/drivers/rtc/rtc-ds1343.c
+++ b/drivers/rtc/rtc-ds1343.c
@@ -153,120 +153,22 @@ static ssize_t ds1343_store_glitchfilter(struct device *dev,
static DEVICE_ATTR(glitch_filter, S_IRUGO | S_IWUSR, ds1343_show_glitchfilter,
ds1343_store_glitchfilter);
-static ssize_t ds1343_nvram_write(struct file *filp, struct kobject *kobj,
- struct bin_attribute *attr,
- char *buf, loff_t off, size_t count)
+static int ds1343_nvram_write(void *priv, unsigned int off, void *val,
+ size_t bytes)
{
- int ret;
- unsigned char address;
- struct device *dev = kobj_to_dev(kobj);
- struct ds1343_priv *priv = dev_get_drvdata(dev);
-
- address = DS1343_NVRAM + off;
-
- ret = regmap_bulk_write(priv->map, address, buf, count);
- if (ret < 0)
- dev_err(&priv->spi->dev, "Error in nvram write %d", ret);
+ struct ds1343_priv *ds1343 = priv;
- return (ret < 0) ? ret : count;
+ return regmap_bulk_write(ds1343->map, DS1343_NVRAM + off, val, bytes);
}
-
-static ssize_t ds1343_nvram_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *attr,
- char *buf, loff_t off, size_t count)
+static int ds1343_nvram_read(void *priv, unsigned int off, void *val,
+ size_t bytes)
{
- int ret;
- unsigned char address;
- struct device *dev = kobj_to_dev(kobj);
- struct ds1343_priv *priv = dev_get_drvdata(dev);
+ struct ds1343_priv *ds1343 = priv;
- address = DS1343_NVRAM + off;
-
- ret = regmap_bulk_read(priv->map, address, buf, count);
- if (ret < 0)
- dev_err(&priv->spi->dev, "Error in nvram read %d\n", ret);
-
- return (ret < 0) ? ret : count;
+ return regmap_bulk_read(ds1343->map, DS1343_NVRAM + off, val, bytes);
}
-
-static struct bin_attribute nvram_attr = {
- .attr.name = "nvram",
- .attr.mode = S_IRUGO | S_IWUSR,
- .read = ds1343_nvram_read,
- .write = ds1343_nvram_write,
- .size = DS1343_NVRAM_LEN,
-};
-
-static ssize_t ds1343_show_alarmstatus(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct ds1343_priv *priv = dev_get_drvdata(dev);
- int alarmstatus, data;
-
- regmap_read(priv->map, DS1343_CONTROL_REG, &data);
-
- alarmstatus = !!(data & DS1343_A0IE);
-
- if (alarmstatus)
- return sprintf(buf, "enabled\n");
- else
- return sprintf(buf, "disabled\n");
-}
-
-static DEVICE_ATTR(alarm_status, S_IRUGO, ds1343_show_alarmstatus, NULL);
-
-static ssize_t ds1343_show_alarmmode(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct ds1343_priv *priv = dev_get_drvdata(dev);
- int alarm_mode, data;
- char *alarm_str;
-
- regmap_read(priv->map, DS1343_ALM0_SEC_REG, &data);
- alarm_mode = (data & 0x80) >> 4;
-
- regmap_read(priv->map, DS1343_ALM0_MIN_REG, &data);
- alarm_mode |= (data & 0x80) >> 5;
-
- regmap_read(priv->map, DS1343_ALM0_HOUR_REG, &data);
- alarm_mode |= (data & 0x80) >> 6;
-
- regmap_read(priv->map, DS1343_ALM0_DAY_REG, &data);
- alarm_mode |= (data & 0x80) >> 7;
-
- switch (alarm_mode) {
- case 15:
- alarm_str = "each second";
- break;
-
- case 7:
- alarm_str = "seconds match";
- break;
-
- case 3:
- alarm_str = "minutes and seconds match";
- break;
-
- case 1:
- alarm_str = "hours, minutes and seconds match";
- break;
-
- case 0:
- alarm_str = "day, hours, minutes and seconds match";
- break;
-
- default:
- alarm_str = "invalid";
- break;
- }
-
- return sprintf(buf, "%s\n", alarm_str);
-}
-
-static DEVICE_ATTR(alarm_mode, S_IRUGO, ds1343_show_alarmmode, NULL);
-
static ssize_t ds1343_show_tricklecharger(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -313,7 +215,6 @@ static DEVICE_ATTR(trickle_charger, S_IRUGO, ds1343_show_tricklecharger, NULL);
static int ds1343_sysfs_register(struct device *dev)
{
- struct ds1343_priv *priv = dev_get_drvdata(dev);
int err;
err = device_create_file(dev, &dev_attr_glitch_filter);
@@ -321,33 +222,9 @@ static int ds1343_sysfs_register(struct device *dev)
return err;
err = device_create_file(dev, &dev_attr_trickle_charger);
- if (err)
- goto error1;
-
- err = device_create_bin_file(dev, &nvram_attr);
- if (err)
- goto error2;
-
- if (priv->irq <= 0)
- return err;
-
- err = device_create_file(dev, &dev_attr_alarm_mode);
- if (err)
- goto error3;
-
- err = device_create_file(dev, &dev_attr_alarm_status);
if (!err)
- return err;
+ return 0;
- device_remove_file(dev, &dev_attr_alarm_mode);
-
-error3:
- device_remove_bin_file(dev, &nvram_attr);
-
-error2:
- device_remove_file(dev, &dev_attr_trickle_charger);
-
-error1:
device_remove_file(dev, &dev_attr_glitch_filter);
return err;
@@ -355,17 +232,8 @@ error1:
static void ds1343_sysfs_unregister(struct device *dev)
{
- struct ds1343_priv *priv = dev_get_drvdata(dev);
-
device_remove_file(dev, &dev_attr_glitch_filter);
device_remove_file(dev, &dev_attr_trickle_charger);
- device_remove_bin_file(dev, &nvram_attr);
-
- if (priv->irq <= 0)
- return;
-
- device_remove_file(dev, &dev_attr_alarm_status);
- device_remove_file(dev, &dev_attr_alarm_mode);
}
static int ds1343_read_time(struct device *dev, struct rtc_time *dt)
@@ -386,7 +254,7 @@ static int ds1343_read_time(struct device *dev, struct rtc_time *dt)
dt->tm_mon = bcd2bin(buf[5] & 0x1F) - 1;
dt->tm_year = bcd2bin(buf[6]) + 100; /* year offset from 1900 */
- return rtc_valid_tm(dt);
+ return 0;
}
static int ds1343_set_time(struct device *dev, struct rtc_time *dt)
@@ -599,14 +467,18 @@ static const struct rtc_class_ops ds1343_rtc_ops = {
static int ds1343_probe(struct spi_device *spi)
{
struct ds1343_priv *priv;
- struct regmap_config config;
+ struct regmap_config config = { .reg_bits = 8, .val_bits = 8,
+ .write_flag_mask = 0x80, };
unsigned int data;
int res;
-
- memset(&config, 0, sizeof(config));
- config.reg_bits = 8;
- config.val_bits = 8;
- config.write_flag_mask = 0x80;
+ struct nvmem_config nvmem_cfg = {
+ .name = "ds1343-",
+ .word_size = 1,
+ .stride = 1,
+ .size = DS1343_NVRAM_LEN,
+ .reg_read = ds1343_nvram_read,
+ .reg_write = ds1343_nvram_write,
+ };
priv = devm_kzalloc(&spi->dev, sizeof(struct ds1343_priv), GFP_KERNEL);
if (!priv)
@@ -646,12 +518,19 @@ static int ds1343_probe(struct spi_device *spi)
data &= ~(DS1343_OSF | DS1343_IRQF1 | DS1343_IRQF0);
regmap_write(priv->map, DS1343_STATUS_REG, data);
- priv->rtc = devm_rtc_device_register(&spi->dev, "ds1343",
- &ds1343_rtc_ops, THIS_MODULE);
- if (IS_ERR(priv->rtc)) {
- dev_err(&spi->dev, "unable to register rtc ds1343\n");
+ priv->rtc = devm_rtc_allocate_device(&spi->dev);
+ if (IS_ERR(priv->rtc))
return PTR_ERR(priv->rtc);
- }
+
+ priv->rtc->nvram_old_abi = true;
+ priv->rtc->ops = &ds1343_rtc_ops;
+
+ res = rtc_register_device(priv->rtc);
+ if (res)
+ return res;
+
+ nvmem_cfg.priv = priv;
+ rtc_nvmem_register(priv->rtc, &nvmem_cfg);
priv->irq = spi->irq;
diff --git a/drivers/rtc/rtc-ds1347.c b/drivers/rtc/rtc-ds1347.c
index ccfc9d43eb1e..938512c676ee 100644
--- a/drivers/rtc/rtc-ds1347.c
+++ b/drivers/rtc/rtc-ds1347.c
@@ -66,7 +66,7 @@ static int ds1347_read_time(struct device *dev, struct rtc_time *dt)
dt->tm_wday = bcd2bin(buf[5]) - 1;
dt->tm_year = bcd2bin(buf[6]) + 100;
- return rtc_valid_tm(dt);
+ return 0;
}
static int ds1347_set_time(struct device *dev, struct rtc_time *dt)
diff --git a/drivers/rtc/rtc-ds1390.c b/drivers/rtc/rtc-ds1390.c
index 4d5b007d7fc6..3b095401f848 100644
--- a/drivers/rtc/rtc-ds1390.c
+++ b/drivers/rtc/rtc-ds1390.c
@@ -153,7 +153,7 @@ static int ds1390_read_time(struct device *dev, struct rtc_time *dt)
/* adjust for century bit */
dt->tm_year = bcd2bin(chip->txrx_buf[6]) + ((chip->txrx_buf[5] & 0x80) ? 100 : 0);
- return rtc_valid_tm(dt);
+ return 0;
}
static int ds1390_set_time(struct device *dev, struct rtc_time *dt)
diff --git a/drivers/rtc/rtc-ds1511.c b/drivers/rtc/rtc-ds1511.c
index 1e95312a6f2e..a7d5ca428d68 100644
--- a/drivers/rtc/rtc-ds1511.c
+++ b/drivers/rtc/rtc-ds1511.c
@@ -277,10 +277,6 @@ static int ds1511_rtc_read_time(struct device *dev, struct rtc_time *rtc_tm)
rtc_tm->tm_mon--;
- if (rtc_valid_tm(rtc_tm) < 0) {
- dev_err(dev, "retrieved date/time is not valid.\n");
- rtc_time_to_tm(0, rtc_tm);
- }
return 0;
}
@@ -422,20 +418,20 @@ static int ds1511_nvram_write(void *priv, unsigned int pos, void *buf,
return 0;
}
-static struct nvmem_config ds1511_nvmem_cfg = {
- .name = "ds1511_nvram",
- .word_size = 1,
- .stride = 1,
- .size = DS1511_RAM_MAX,
- .reg_read = ds1511_nvram_read,
- .reg_write = ds1511_nvram_write,
-};
-
static int ds1511_rtc_probe(struct platform_device *pdev)
{
struct resource *res;
struct rtc_plat_data *pdata;
int ret = 0;
+ struct nvmem_config ds1511_nvmem_cfg = {
+ .name = "ds1511_nvram",
+ .word_size = 1,
+ .stride = 1,
+ .size = DS1511_RAM_MAX,
+ .reg_read = ds1511_nvram_read,
+ .reg_write = ds1511_nvram_write,
+ .priv = &pdev->dev,
+ };
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
@@ -478,14 +474,14 @@ static int ds1511_rtc_probe(struct platform_device *pdev)
pdata->rtc->ops = &ds1511_rtc_ops;
- ds1511_nvmem_cfg.priv = &pdev->dev;
- pdata->rtc->nvmem_config = &ds1511_nvmem_cfg;
pdata->rtc->nvram_old_abi = true;
ret = rtc_register_device(pdata->rtc);
if (ret)
return ret;
+ rtc_nvmem_register(pdata->rtc, &ds1511_nvmem_cfg);
+
/*
* if the platform has an interrupt in mind for this device,
* then by all means, set it
diff --git a/drivers/rtc/rtc-ds1553.c b/drivers/rtc/rtc-ds1553.c
index 9961ec646fd2..2441b9a2b366 100644
--- a/drivers/rtc/rtc-ds1553.c
+++ b/drivers/rtc/rtc-ds1553.c
@@ -127,10 +127,6 @@ static int ds1553_rtc_read_time(struct device *dev, struct rtc_time *tm)
/* year is 1900 + tm->tm_year */
tm->tm_year = bcd2bin(year) + bcd2bin(century) * 100 - 1900;
- if (rtc_valid_tm(tm) < 0) {
- dev_err(dev, "retrieved date/time is not valid.\n");
- rtc_time_to_tm(0, tm);
- }
return 0;
}
@@ -233,46 +229,32 @@ static const struct rtc_class_ops ds1553_rtc_ops = {
.alarm_irq_enable = ds1553_rtc_alarm_irq_enable,
};
-static ssize_t ds1553_nvram_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
- char *buf, loff_t pos, size_t size)
+static int ds1553_nvram_read(void *priv, unsigned int pos, void *val,
+ size_t bytes)
{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct platform_device *pdev = to_platform_device(dev);
+ struct platform_device *pdev = priv;
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
void __iomem *ioaddr = pdata->ioaddr;
- ssize_t count;
+ u8 *buf = val;
- for (count = 0; count < size; count++)
+ for (; bytes; bytes--)
*buf++ = readb(ioaddr + pos++);
- return count;
+ return 0;
}
-static ssize_t ds1553_nvram_write(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
- char *buf, loff_t pos, size_t size)
+static int ds1553_nvram_write(void *priv, unsigned int pos, void *val,
+ size_t bytes)
{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct platform_device *pdev = to_platform_device(dev);
+ struct platform_device *pdev = priv;
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
void __iomem *ioaddr = pdata->ioaddr;
- ssize_t count;
+ u8 *buf = val;
- for (count = 0; count < size; count++)
+ for (; bytes; bytes--)
writeb(*buf++, ioaddr + pos++);
- return count;
+ return 0;
}
-static struct bin_attribute ds1553_nvram_attr = {
- .attr = {
- .name = "nvram",
- .mode = S_IRUGO | S_IWUSR,
- },
- .size = RTC_OFFSET,
- .read = ds1553_nvram_read,
- .write = ds1553_nvram_write,
-};
-
static int ds1553_rtc_probe(struct platform_device *pdev)
{
struct resource *res;
@@ -280,6 +262,15 @@ static int ds1553_rtc_probe(struct platform_device *pdev)
struct rtc_plat_data *pdata;
void __iomem *ioaddr;
int ret = 0;
+ struct nvmem_config nvmem_cfg = {
+ .name = "ds1553_nvram",
+ .word_size = 1,
+ .stride = 1,
+ .size = RTC_OFFSET,
+ .reg_read = ds1553_nvram_read,
+ .reg_write = ds1553_nvram_write,
+ .priv = pdev,
+ };
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
@@ -308,11 +299,17 @@ static int ds1553_rtc_probe(struct platform_device *pdev)
pdata->last_jiffies = jiffies;
platform_set_drvdata(pdev, pdata);
- pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
- &ds1553_rtc_ops, THIS_MODULE);
+ pdata->rtc = devm_rtc_allocate_device(&pdev->dev);
if (IS_ERR(pdata->rtc))
return PTR_ERR(pdata->rtc);
+ pdata->rtc->ops = &ds1553_rtc_ops;
+ pdata->rtc->nvram_old_abi = true;
+
+ ret = rtc_register_device(pdata->rtc);
+ if (ret)
+ return ret;
+
if (pdata->irq > 0) {
writeb(0, ioaddr + RTC_INTERRUPTS);
if (devm_request_irq(&pdev->dev, pdata->irq,
@@ -323,21 +320,9 @@ static int ds1553_rtc_probe(struct platform_device *pdev)
}
}
- ret = sysfs_create_bin_file(&pdev->dev.kobj, &ds1553_nvram_attr);
- if (ret)
- dev_err(&pdev->dev, "unable to create sysfs file: %s\n",
- ds1553_nvram_attr.attr.name);
-
- return 0;
-}
-
-static int ds1553_rtc_remove(struct platform_device *pdev)
-{
- struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ if (rtc_nvmem_register(pdata->rtc, &nvmem_cfg))
+ dev_err(&pdev->dev, "unable to register nvmem\n");
- sysfs_remove_bin_file(&pdev->dev.kobj, &ds1553_nvram_attr);
- if (pdata->irq > 0)
- writeb(0, pdata->ioaddr + RTC_INTERRUPTS);
return 0;
}
@@ -346,7 +331,6 @@ MODULE_ALIAS("platform:rtc-ds1553");
static struct platform_driver ds1553_rtc_driver = {
.probe = ds1553_rtc_probe,
- .remove = ds1553_rtc_remove,
.driver = {
.name = "rtc-ds1553",
},
diff --git a/drivers/rtc/rtc-ds1685.c b/drivers/rtc/rtc-ds1685.c
index ed43b4311660..1a39829d2b40 100644
--- a/drivers/rtc/rtc-ds1685.c
+++ b/drivers/rtc/rtc-ds1685.c
@@ -306,7 +306,7 @@ ds1685_rtc_read_time(struct device *dev, struct rtc_time *tm)
tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
tm->tm_isdst = 0; /* RTC has hardcoded timezone, so don't use. */
- return rtc_valid_tm(tm);
+ return 0;
}
/**
diff --git a/drivers/rtc/rtc-ds1742.c b/drivers/rtc/rtc-ds1742.c
index 3abf1cbfb8ce..2d781180e968 100644
--- a/drivers/rtc/rtc-ds1742.c
+++ b/drivers/rtc/rtc-ds1742.c
@@ -53,9 +53,7 @@
struct rtc_plat_data {
void __iomem *ioaddr_nvram;
void __iomem *ioaddr_rtc;
- size_t size_nvram;
unsigned long last_jiffies;
- struct bin_attribute nvram_attr;
};
static int ds1742_rtc_set_time(struct device *dev, struct rtc_time *tm)
@@ -114,7 +112,7 @@ static int ds1742_rtc_read_time(struct device *dev, struct rtc_time *tm)
/* year is 1900 + tm->tm_year */
tm->tm_year = bcd2bin(year) + bcd2bin(century) * 100 - 1900;
- return rtc_valid_tm(tm);
+ return 0;
}
static const struct rtc_class_ops ds1742_rtc_ops = {
@@ -122,34 +120,28 @@ static const struct rtc_class_ops ds1742_rtc_ops = {
.set_time = ds1742_rtc_set_time,
};
-static ssize_t ds1742_nvram_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
- char *buf, loff_t pos, size_t size)
+static int ds1742_nvram_read(void *priv, unsigned int pos, void *val,
+ size_t bytes)
{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct platform_device *pdev = to_platform_device(dev);
- struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ struct rtc_plat_data *pdata = priv;
void __iomem *ioaddr = pdata->ioaddr_nvram;
- ssize_t count;
+ u8 *buf = val;
- for (count = 0; count < size; count++)
+ for (; bytes; bytes--)
*buf++ = readb(ioaddr + pos++);
- return count;
+ return 0;
}
-static ssize_t ds1742_nvram_write(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
- char *buf, loff_t pos, size_t size)
+static int ds1742_nvram_write(void *priv, unsigned int pos, void *val,
+ size_t bytes)
{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct platform_device *pdev = to_platform_device(dev);
- struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ struct rtc_plat_data *pdata = priv;
void __iomem *ioaddr = pdata->ioaddr_nvram;
- ssize_t count;
+ u8 *buf = val;
- for (count = 0; count < size; count++)
+ for (; bytes; bytes--)
writeb(*buf++, ioaddr + pos++);
- return count;
+ return 0;
}
static int ds1742_rtc_probe(struct platform_device *pdev)
@@ -160,6 +152,14 @@ static int ds1742_rtc_probe(struct platform_device *pdev)
struct rtc_plat_data *pdata;
void __iomem *ioaddr;
int ret = 0;
+ struct nvmem_config nvmem_cfg = {
+ .name = "ds1742_nvram",
+ .word_size = 1,
+ .stride = 1,
+ .reg_read = ds1742_nvram_read,
+ .reg_write = ds1742_nvram_write,
+ };
+
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
@@ -171,15 +171,10 @@ static int ds1742_rtc_probe(struct platform_device *pdev)
return PTR_ERR(ioaddr);
pdata->ioaddr_nvram = ioaddr;
- pdata->size_nvram = resource_size(res) - RTC_SIZE;
- pdata->ioaddr_rtc = ioaddr + pdata->size_nvram;
+ pdata->ioaddr_rtc = ioaddr + resource_size(res) - RTC_SIZE;
- sysfs_bin_attr_init(&pdata->nvram_attr);
- pdata->nvram_attr.attr.name = "nvram";
- pdata->nvram_attr.attr.mode = S_IRUGO | S_IWUSR;
- pdata->nvram_attr.read = ds1742_nvram_read;
- pdata->nvram_attr.write = ds1742_nvram_write;
- pdata->nvram_attr.size = pdata->size_nvram;
+ nvmem_cfg.size = resource_size(res) - RTC_SIZE;
+ nvmem_cfg.priv = pdata;
/* turn RTC on if it was not on */
ioaddr = pdata->ioaddr_rtc;
@@ -196,24 +191,21 @@ static int ds1742_rtc_probe(struct platform_device *pdev)
pdata->last_jiffies = jiffies;
platform_set_drvdata(pdev, pdata);
- rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
- &ds1742_rtc_ops, THIS_MODULE);
+
+ rtc = devm_rtc_allocate_device(&pdev->dev);
if (IS_ERR(rtc))
return PTR_ERR(rtc);
- ret = sysfs_create_bin_file(&pdev->dev.kobj, &pdata->nvram_attr);
- if (ret)
- dev_err(&pdev->dev, "Unable to create sysfs entry: %s\n",
- pdata->nvram_attr.attr.name);
+ rtc->ops = &ds1742_rtc_ops;
+ rtc->nvram_old_abi = true;
- return 0;
-}
+ ret = rtc_register_device(rtc);
+ if (ret)
+ return ret;
-static int ds1742_rtc_remove(struct platform_device *pdev)
-{
- struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ if (rtc_nvmem_register(rtc, &nvmem_cfg))
+ dev_err(&pdev->dev, "Unable to register nvmem\n");
- sysfs_remove_bin_file(&pdev->dev.kobj, &pdata->nvram_attr);
return 0;
}
@@ -225,7 +217,6 @@ MODULE_DEVICE_TABLE(of, ds1742_rtc_of_match);
static struct platform_driver ds1742_rtc_driver = {
.probe = ds1742_rtc_probe,
- .remove = ds1742_rtc_remove,
.driver = {
.name = "rtc-ds1742",
.of_match_table = of_match_ptr(ds1742_rtc_of_match),
diff --git a/drivers/rtc/rtc-ds2404.c b/drivers/rtc/rtc-ds2404.c
index 9a1582ed7070..b886b6a5c178 100644
--- a/drivers/rtc/rtc-ds2404.c
+++ b/drivers/rtc/rtc-ds2404.c
@@ -207,7 +207,7 @@ static int ds2404_read_time(struct device *dev, struct rtc_time *dt)
time = le32_to_cpu(time);
rtc_time_to_tm(time, dt);
- return rtc_valid_tm(dt);
+ return 0;
}
static int ds2404_set_mmss(struct device *dev, unsigned long secs)
diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c
index 0550f7ba464f..7184e5145f12 100644
--- a/drivers/rtc/rtc-ds3232.c
+++ b/drivers/rtc/rtc-ds3232.c
@@ -145,7 +145,7 @@ static int ds3232_read_time(struct device *dev, struct rtc_time *time)
time->tm_year = bcd2bin(year) + add_century;
- return rtc_valid_tm(time);
+ return 0;
}
static int ds3232_set_time(struct device *dev, struct rtc_time *time)
diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
index 0130afd7fe88..3454e7814524 100644
--- a/drivers/rtc/rtc-efi.c
+++ b/drivers/rtc/rtc-efi.c
@@ -176,7 +176,7 @@ static int efi_read_time(struct device *dev, struct rtc_time *tm)
if (!convert_from_efi_time(&eft, tm))
return -EIO;
- return rtc_valid_tm(tm);
+ return 0;
}
static int efi_set_time(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-fm3130.c b/drivers/rtc/rtc-fm3130.c
index 576eadbba296..e1137670d4d2 100644
--- a/drivers/rtc/rtc-fm3130.c
+++ b/drivers/rtc/rtc-fm3130.c
@@ -136,8 +136,7 @@ static int fm3130_get_time(struct device *dev, struct rtc_time *t)
t->tm_hour, t->tm_mday,
t->tm_mon, t->tm_year, t->tm_wday);
- /* initial clock setting can be undefined */
- return rtc_valid_tm(t);
+ return 0;
}
diff --git a/drivers/rtc/rtc-goldfish.c b/drivers/rtc/rtc-goldfish.c
index d67769265185..a1c44d0c8557 100644
--- a/drivers/rtc/rtc-goldfish.c
+++ b/drivers/rtc/rtc-goldfish.c
@@ -235,3 +235,5 @@ static struct platform_driver goldfish_rtc = {
};
module_platform_driver(goldfish_rtc);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rtc/rtc-isl12022.c b/drivers/rtc/rtc-isl12022.c
index 38586a024ee8..890ccfc9e5aa 100644
--- a/drivers/rtc/rtc-isl12022.c
+++ b/drivers/rtc/rtc-isl12022.c
@@ -104,8 +104,9 @@ static int isl12022_write_reg(struct i2c_client *client,
* In the routines that deal directly with the isl12022 hardware, we use
* rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
*/
-static int isl12022_get_datetime(struct i2c_client *client, struct rtc_time *tm)
+static int isl12022_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
+ struct i2c_client *client = to_i2c_client(dev);
uint8_t buf[ISL12022_REG_INT + 1];
int ret;
@@ -149,11 +150,12 @@ static int isl12022_get_datetime(struct i2c_client *client, struct rtc_time *tm)
tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
- return rtc_valid_tm(tm);
+ return 0;
}
-static int isl12022_set_datetime(struct i2c_client *client, struct rtc_time *tm)
+static int isl12022_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct isl12022 *isl12022 = i2c_get_clientdata(client);
size_t i;
int ret;
@@ -199,7 +201,7 @@ static int isl12022_set_datetime(struct i2c_client *client, struct rtc_time *tm)
return ret;
}
- isl12022->write_enabled = 1;
+ isl12022->write_enabled = true;
}
/* hours, minutes and seconds */
@@ -228,16 +230,6 @@ static int isl12022_set_datetime(struct i2c_client *client, struct rtc_time *tm)
return 0;
}
-static int isl12022_rtc_read_time(struct device *dev, struct rtc_time *tm)
-{
- return isl12022_get_datetime(to_i2c_client(dev), tm);
-}
-
-static int isl12022_rtc_set_time(struct device *dev, struct rtc_time *tm)
-{
- return isl12022_set_datetime(to_i2c_client(dev), tm);
-}
-
static const struct rtc_class_ops isl12022_rtc_ops = {
.read_time = isl12022_rtc_read_time,
.set_time = isl12022_rtc_set_time,
diff --git a/drivers/rtc/rtc-isl12026.c b/drivers/rtc/rtc-isl12026.c
new file mode 100644
index 000000000000..97f594f9667c
--- /dev/null
+++ b/drivers/rtc/rtc-isl12026.c
@@ -0,0 +1,501 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * An I2C driver for the Intersil ISL 12026
+ *
+ * Copyright (c) 2018 Cavium, Inc.
+ */
+#include <linux/bcd.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/nvmem-provider.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+
+/* register offsets */
+#define ISL12026_REG_PWR 0x14
+# define ISL12026_REG_PWR_BSW BIT(6)
+# define ISL12026_REG_PWR_SBIB BIT(7)
+#define ISL12026_REG_SC 0x30
+#define ISL12026_REG_HR 0x32
+# define ISL12026_REG_HR_MIL BIT(7) /* military or 24 hour time */
+#define ISL12026_REG_SR 0x3f
+# define ISL12026_REG_SR_RTCF BIT(0)
+# define ISL12026_REG_SR_WEL BIT(1)
+# define ISL12026_REG_SR_RWEL BIT(2)
+# define ISL12026_REG_SR_MBZ BIT(3)
+# define ISL12026_REG_SR_OSCF BIT(4)
+
+/* The EEPROM array responds at i2c address 0x57 */
+#define ISL12026_EEPROM_ADDR 0x57
+
+#define ISL12026_PAGESIZE 16
+#define ISL12026_NVMEM_WRITE_TIME 20
+
+struct isl12026 {
+ struct rtc_device *rtc;
+ struct i2c_client *nvm_client;
+};
+
+static int isl12026_read_reg(struct i2c_client *client, int reg)
+{
+ u8 addr[] = {0, reg};
+ u8 val;
+ int ret;
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = sizeof(addr),
+ .buf = addr
+ }, {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = &val
+ }
+ };
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs)) {
+ dev_err(&client->dev, "read reg error, ret=%d\n", ret);
+ ret = ret < 0 ? ret : -EIO;
+ } else {
+ ret = val;
+ }
+
+ return ret;
+}
+
+static int isl12026_arm_write(struct i2c_client *client)
+{
+ int ret;
+ u8 op[3];
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = op
+ };
+
+ /* Set SR.WEL */
+ op[0] = 0;
+ op[1] = ISL12026_REG_SR;
+ op[2] = ISL12026_REG_SR_WEL;
+ msg.len = 3;
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret != 1) {
+ dev_err(&client->dev, "write error SR.WEL, ret=%d\n", ret);
+ ret = ret < 0 ? ret : -EIO;
+ goto out;
+ }
+
+ /* Set SR.WEL and SR.RWEL */
+ op[2] = ISL12026_REG_SR_WEL | ISL12026_REG_SR_RWEL;
+ msg.len = 3;
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret != 1) {
+ dev_err(&client->dev,
+ "write error SR.WEL|SR.RWEL, ret=%d\n", ret);
+ ret = ret < 0 ? ret : -EIO;
+ goto out;
+ } else {
+ ret = 0;
+ }
+out:
+ return ret;
+}
+
+static int isl12026_disarm_write(struct i2c_client *client)
+{
+ int ret;
+ u8 op[3] = {0, ISL12026_REG_SR, 0};
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .flags = 0,
+ .len = sizeof(op),
+ .buf = op
+ };
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret != 1) {
+ dev_err(&client->dev,
+ "write error SR, ret=%d\n", ret);
+ ret = ret < 0 ? ret : -EIO;
+ } else {
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int isl12026_write_reg(struct i2c_client *client, int reg, u8 val)
+{
+ int ret;
+ u8 op[3] = {0, reg, val};
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .flags = 0,
+ .len = sizeof(op),
+ .buf = op
+ };
+
+ ret = isl12026_arm_write(client);
+ if (ret)
+ return ret;
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret != 1) {
+ dev_err(&client->dev, "write error CCR, ret=%d\n", ret);
+ ret = ret < 0 ? ret : -EIO;
+ goto out;
+ }
+
+ msleep(ISL12026_NVMEM_WRITE_TIME);
+
+ ret = isl12026_disarm_write(client);
+out:
+ return ret;
+}
+
+static int isl12026_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ int ret;
+ u8 op[10];
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .flags = 0,
+ .len = sizeof(op),
+ .buf = op
+ };
+
+ ret = isl12026_arm_write(client);
+ if (ret)
+ return ret;
+
+ /* Set the CCR registers */
+ op[0] = 0;
+ op[1] = ISL12026_REG_SC;
+ op[2] = bin2bcd(tm->tm_sec); /* SC */
+ op[3] = bin2bcd(tm->tm_min); /* MN */
+ op[4] = bin2bcd(tm->tm_hour) | ISL12026_REG_HR_MIL; /* HR */
+ op[5] = bin2bcd(tm->tm_mday); /* DT */
+ op[6] = bin2bcd(tm->tm_mon + 1); /* MO */
+ op[7] = bin2bcd(tm->tm_year % 100); /* YR */
+ op[8] = bin2bcd(tm->tm_wday & 7); /* DW */
+ op[9] = bin2bcd(tm->tm_year >= 100 ? 20 : 19); /* Y2K */
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret != 1) {
+ dev_err(&client->dev, "write error CCR, ret=%d\n", ret);
+ ret = ret < 0 ? ret : -EIO;
+ goto out;
+ }
+
+ ret = isl12026_disarm_write(client);
+out:
+ return ret;
+}
+
+static int isl12026_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ u8 ccr[8];
+ u8 addr[2];
+ u8 sr;
+ int ret;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = sizeof(addr),
+ .buf = addr
+ }, {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ }
+ };
+
+ /* First, read SR */
+ addr[0] = 0;
+ addr[1] = ISL12026_REG_SR;
+ msgs[1].len = 1;
+ msgs[1].buf = &sr;
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs)) {
+ dev_err(&client->dev, "read error, ret=%d\n", ret);
+ ret = ret < 0 ? ret : -EIO;
+ goto out;
+ }
+
+ if (sr & ISL12026_REG_SR_RTCF)
+ dev_warn(&client->dev, "Real-Time Clock Failure on read\n");
+ if (sr & ISL12026_REG_SR_OSCF)
+ dev_warn(&client->dev, "Oscillator Failure on read\n");
+
+ /* Second, CCR regs */
+ addr[0] = 0;
+ addr[1] = ISL12026_REG_SC;
+ msgs[1].len = sizeof(ccr);
+ msgs[1].buf = ccr;
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs)) {
+ dev_err(&client->dev, "read error, ret=%d\n", ret);
+ ret = ret < 0 ? ret : -EIO;
+ goto out;
+ }
+
+ tm->tm_sec = bcd2bin(ccr[0] & 0x7F);
+ tm->tm_min = bcd2bin(ccr[1] & 0x7F);
+ if (ccr[2] & ISL12026_REG_HR_MIL)
+ tm->tm_hour = bcd2bin(ccr[2] & 0x3F);
+ else
+ tm->tm_hour = bcd2bin(ccr[2] & 0x1F) +
+ ((ccr[2] & 0x20) ? 12 : 0);
+ tm->tm_mday = bcd2bin(ccr[3] & 0x3F);
+ tm->tm_mon = bcd2bin(ccr[4] & 0x1F) - 1;
+ tm->tm_year = bcd2bin(ccr[5]);
+ if (bcd2bin(ccr[7]) == 20)
+ tm->tm_year += 100;
+ tm->tm_wday = ccr[6] & 0x07;
+
+ ret = 0;
+out:
+ return ret;
+}
+
+static const struct rtc_class_ops isl12026_rtc_ops = {
+ .read_time = isl12026_rtc_read_time,
+ .set_time = isl12026_rtc_set_time,
+};
+
+static int isl12026_nvm_read(void *p, unsigned int offset,
+ void *val, size_t bytes)
+{
+ struct isl12026 *priv = p;
+ int ret;
+ u8 addr[2];
+ struct i2c_msg msgs[] = {
+ {
+ .addr = priv->nvm_client->addr,
+ .flags = 0,
+ .len = sizeof(addr),
+ .buf = addr
+ }, {
+ .addr = priv->nvm_client->addr,
+ .flags = I2C_M_RD,
+ .buf = val
+ }
+ };
+
+ /*
+ * offset and bytes checked and limited by nvmem core, so
+ * proceed without further checks.
+ */
+ ret = mutex_lock_interruptible(&priv->rtc->ops_lock);
+ if (ret)
+ return ret;
+
+ /* 2 bytes of address, most significant first */
+ addr[0] = offset >> 8;
+ addr[1] = offset;
+ msgs[1].len = bytes;
+ ret = i2c_transfer(priv->nvm_client->adapter, msgs, ARRAY_SIZE(msgs));
+
+ mutex_unlock(&priv->rtc->ops_lock);
+
+ if (ret != ARRAY_SIZE(msgs)) {
+ dev_err(&priv->nvm_client->dev,
+ "nvmem read error, ret=%d\n", ret);
+ return ret < 0 ? ret : -EIO;
+ }
+
+ return 0;
+}
+
+static int isl12026_nvm_write(void *p, unsigned int offset,
+ void *val, size_t bytes)
+{
+ struct isl12026 *priv = p;
+ int ret;
+ u8 *v = val;
+ size_t chunk_size, num_written;
+ u8 payload[ISL12026_PAGESIZE + 2]; /* page + 2 address bytes */
+ struct i2c_msg msgs[] = {
+ {
+ .addr = priv->nvm_client->addr,
+ .flags = 0,
+ .buf = payload
+ }
+ };
+
+ /*
+ * offset and bytes checked and limited by nvmem core, so
+ * proceed without further checks.
+ */
+ ret = mutex_lock_interruptible(&priv->rtc->ops_lock);
+ if (ret)
+ return ret;
+
+ num_written = 0;
+ while (bytes) {
+ chunk_size = round_down(offset, ISL12026_PAGESIZE) +
+ ISL12026_PAGESIZE - offset;
+ chunk_size = min(bytes, chunk_size);
+ /*
+ * 2 bytes of address, most significant first, followed
+ * by page data bytes
+ */
+ memcpy(payload + 2, v + num_written, chunk_size);
+ payload[0] = offset >> 8;
+ payload[1] = offset;
+ msgs[0].len = chunk_size + 2;
+ ret = i2c_transfer(priv->nvm_client->adapter,
+ msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs)) {
+ dev_err(&priv->nvm_client->dev,
+ "nvmem write error, ret=%d\n", ret);
+ ret = ret < 0 ? ret : -EIO;
+ break;
+ }
+ ret = 0;
+ bytes -= chunk_size;
+ offset += chunk_size;
+ num_written += chunk_size;
+ msleep(ISL12026_NVMEM_WRITE_TIME);
+ }
+
+ mutex_unlock(&priv->rtc->ops_lock);
+
+ return ret;
+}
+
+static void isl12026_force_power_modes(struct i2c_client *client)
+{
+ int ret;
+ int pwr, requested_pwr;
+ u32 bsw_val, sbib_val;
+ bool set_bsw, set_sbib;
+
+ /*
+ * If we can read the of_property, set the specified value.
+ * If there is an error reading the of_property (likely
+ * because it does not exist), keep the current value.
+ */
+ ret = of_property_read_u32(client->dev.of_node,
+ "isil,pwr-bsw", &bsw_val);
+ set_bsw = (ret == 0);
+
+ ret = of_property_read_u32(client->dev.of_node,
+ "isil,pwr-sbib", &sbib_val);
+ set_sbib = (ret == 0);
+
+ /* Check if PWR.BSW and/or PWR.SBIB need specified values */
+ if (!set_bsw && !set_sbib)
+ return;
+
+ pwr = isl12026_read_reg(client, ISL12026_REG_PWR);
+ if (pwr < 0) {
+ dev_warn(&client->dev, "Error: Failed to read PWR %d\n", pwr);
+ return;
+ }
+
+ requested_pwr = pwr;
+
+ if (set_bsw) {
+ if (bsw_val)
+ requested_pwr |= ISL12026_REG_PWR_BSW;
+ else
+ requested_pwr &= ~ISL12026_REG_PWR_BSW;
+ } /* else keep current BSW */
+
+ if (set_sbib) {
+ if (sbib_val)
+ requested_pwr |= ISL12026_REG_PWR_SBIB;
+ else
+ requested_pwr &= ~ISL12026_REG_PWR_SBIB;
+ } /* else keep current SBIB */
+
+ if (pwr >= 0 && pwr != requested_pwr) {
+ dev_dbg(&client->dev, "PWR: %02x\n", pwr);
+ dev_dbg(&client->dev, "Updating PWR to: %02x\n", requested_pwr);
+ isl12026_write_reg(client, ISL12026_REG_PWR, requested_pwr);
+ }
+}
+
+static int isl12026_probe_new(struct i2c_client *client)
+{
+ struct isl12026 *priv;
+ int ret;
+ struct nvmem_config nvm_cfg = {
+ .name = "isl12026-",
+ .base_dev = &client->dev,
+ .stride = 1,
+ .word_size = 1,
+ .size = 512,
+ .reg_read = isl12026_nvm_read,
+ .reg_write = isl12026_nvm_write,
+ };
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, priv);
+
+ isl12026_force_power_modes(client);
+
+ priv->nvm_client = i2c_new_dummy(client->adapter, ISL12026_EEPROM_ADDR);
+ if (!priv->nvm_client)
+ return -ENOMEM;
+
+ priv->rtc = devm_rtc_allocate_device(&client->dev);
+ ret = PTR_ERR_OR_ZERO(priv->rtc);
+ if (ret)
+ return ret;
+
+ priv->rtc->ops = &isl12026_rtc_ops;
+ nvm_cfg.priv = priv;
+ ret = rtc_nvmem_register(priv->rtc, &nvm_cfg);
+ if (ret)
+ return ret;
+
+ return rtc_register_device(priv->rtc);
+}
+
+static int isl12026_remove(struct i2c_client *client)
+{
+ struct isl12026 *priv = i2c_get_clientdata(client);
+
+ i2c_unregister_device(priv->nvm_client);
+ return 0;
+}
+
+static const struct of_device_id isl12026_dt_match[] = {
+ { .compatible = "isil,isl12026" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, isl12026_dt_match);
+
+static struct i2c_driver isl12026_driver = {
+ .driver = {
+ .name = "rtc-isl12026",
+ .of_match_table = isl12026_dt_match,
+ },
+ .probe_new = isl12026_probe_new,
+ .remove = isl12026_remove,
+};
+
+module_i2c_driver(isl12026_driver);
+
+MODULE_DESCRIPTION("ISL 12026 RTC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c
index 8dd299c6a1f3..1a2c38cc0178 100644
--- a/drivers/rtc/rtc-isl1208.c
+++ b/drivers/rtc/rtc-isl1208.c
@@ -459,6 +459,11 @@ isl1208_i2c_set_time(struct i2c_client *client, struct rtc_time const *tm)
}
/* clear WRTC again */
+ sr = isl1208_i2c_get_sr(client);
+ if (sr < 0) {
+ dev_err(&client->dev, "%s: reading SR failed\n", __func__);
+ return sr;
+ }
sr = i2c_smbus_write_byte_data(client, ISL1208_REG_SR,
sr & ~ISL1208_REG_SR_WRTC);
if (sr < 0) {
@@ -630,29 +635,12 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (isl1208_i2c_validate_client(client) < 0)
return -ENODEV;
- if (client->irq > 0) {
- rc = devm_request_threaded_irq(&client->dev, client->irq, NULL,
- isl1208_rtc_interrupt,
- IRQF_SHARED | IRQF_ONESHOT,
- isl1208_driver.driver.name,
- client);
- if (!rc) {
- device_init_wakeup(&client->dev, 1);
- enable_irq_wake(client->irq);
- } else {
- dev_err(&client->dev,
- "Unable to request irq %d, no alarm support\n",
- client->irq);
- client->irq = 0;
- }
- }
-
- rtc = devm_rtc_device_register(&client->dev, isl1208_driver.driver.name,
- &isl1208_rtc_ops,
- THIS_MODULE);
+ rtc = devm_rtc_allocate_device(&client->dev);
if (IS_ERR(rtc))
return PTR_ERR(rtc);
+ rtc->ops = &isl1208_rtc_ops;
+
i2c_set_clientdata(client, rtc);
rc = isl1208_i2c_get_sr(client);
@@ -669,7 +657,24 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (rc)
return rc;
- return 0;
+ if (client->irq > 0) {
+ rc = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+ isl1208_rtc_interrupt,
+ IRQF_SHARED | IRQF_ONESHOT,
+ isl1208_driver.driver.name,
+ client);
+ if (!rc) {
+ device_init_wakeup(&client->dev, 1);
+ enable_irq_wake(client->irq);
+ } else {
+ dev_err(&client->dev,
+ "Unable to request irq %d, no alarm support\n",
+ client->irq);
+ client->irq = 0;
+ }
+ }
+
+ return rtc_register_device(rtc);
}
static int
diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c
index ff65a7d2b9c9..d0a891777f44 100644
--- a/drivers/rtc/rtc-jz4740.c
+++ b/drivers/rtc/rtc-jz4740.c
@@ -173,7 +173,7 @@ static int jz4740_rtc_read_time(struct device *dev, struct rtc_time *time)
rtc_time_to_tm(secs, time);
- return rtc_valid_tm(time);
+ return 0;
}
static int jz4740_rtc_set_mmss(struct device *dev, unsigned long secs)
diff --git a/drivers/rtc/rtc-lib.c b/drivers/rtc/rtc-lib.c
index 1ae7da5cfc60..4a3c0f3aab14 100644
--- a/drivers/rtc/rtc-lib.c
+++ b/drivers/rtc/rtc-lib.c
@@ -52,13 +52,11 @@ EXPORT_SYMBOL(rtc_year_days);
*/
void rtc_time64_to_tm(time64_t time, struct rtc_time *tm)
{
- unsigned int month, year;
- unsigned long secs;
+ unsigned int month, year, secs;
int days;
/* time must be positive */
- days = div_s64(time, 86400);
- secs = time - (unsigned int) days * 86400;
+ days = div_s64_rem(time, 86400, &secs);
/* day of the week, 1970-01-01 was a Thursday */
tm->tm_wday = (days + 4) % 7;
@@ -67,7 +65,7 @@ void rtc_time64_to_tm(time64_t time, struct rtc_time *tm)
days -= (year - 1970) * 365
+ LEAPS_THRU_END_OF(year - 1)
- LEAPS_THRU_END_OF(1970 - 1);
- if (days < 0) {
+ while (days < 0) {
year -= 1;
days += 365 + is_leap_year(year);
}
diff --git a/drivers/rtc/rtc-lpc24xx.c b/drivers/rtc/rtc-lpc24xx.c
index 59d99596fdeb..14dc7b04fae0 100644
--- a/drivers/rtc/rtc-lpc24xx.c
+++ b/drivers/rtc/rtc-lpc24xx.c
@@ -110,7 +110,7 @@ static int lpc24xx_rtc_read_time(struct device *dev, struct rtc_time *tm)
tm->tm_year = CT1_YEAR(ct1);
tm->tm_yday = CT2_DOY(ct2);
- return rtc_valid_tm(tm);
+ return 0;
}
static int lpc24xx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
diff --git a/drivers/rtc/rtc-lpc32xx.c b/drivers/rtc/rtc-lpc32xx.c
index 887871c3d526..3ba87239aacc 100644
--- a/drivers/rtc/rtc-lpc32xx.c
+++ b/drivers/rtc/rtc-lpc32xx.c
@@ -70,7 +70,7 @@ static int lpc32xx_rtc_read_time(struct device *dev, struct rtc_time *time)
elapsed_sec = rtc_readl(rtc, LPC32XX_RTC_UCOUNT);
rtc_time_to_tm(elapsed_sec, time);
- return rtc_valid_tm(time);
+ return 0;
}
static int lpc32xx_rtc_set_mmss(struct device *dev, unsigned long secs)
diff --git a/drivers/rtc/rtc-ls1x.c b/drivers/rtc/rtc-ls1x.c
index e04ca54f21e2..045af1135e48 100644
--- a/drivers/rtc/rtc-ls1x.c
+++ b/drivers/rtc/rtc-ls1x.c
@@ -98,7 +98,7 @@ static int ls1x_rtc_read_time(struct device *dev, struct rtc_time *rtm)
ls1x_get_min(v), ls1x_get_sec(v));
rtc_time_to_tm(t, rtm);
- return rtc_valid_tm(rtm);
+ return 0;
}
static int ls1x_rtc_set_time(struct device *dev, struct rtc_time *rtm)
diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c
index c90fba3ed861..ad03e2f12f5d 100644
--- a/drivers/rtc/rtc-m41t80.c
+++ b/drivers/rtc/rtc-m41t80.c
@@ -73,7 +73,6 @@
#define M41T80_FEATURE_WD BIT(3) /* Extra watchdog resolution */
#define M41T80_FEATURE_SQ_ALT BIT(4) /* RSx bits are in reg 4 */
-static DEFINE_MUTEX(m41t80_rtc_mutex);
static const struct i2c_device_id m41t80_id[] = {
{ "m41t62", M41T80_FEATURE_SQ | M41T80_FEATURE_SQ_ALT },
{ "m41t65", M41T80_FEATURE_HT | M41T80_FEATURE_WD },
@@ -199,9 +198,9 @@ static irqreturn_t m41t80_handle_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int m41t80_get_datetime(struct i2c_client *client,
- struct rtc_time *tm)
+static int m41t80_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
+ struct i2c_client *client = to_i2c_client(dev);
unsigned char buf[8];
int err, flags;
@@ -230,12 +229,12 @@ static int m41t80_get_datetime(struct i2c_client *client,
/* assume 20YY not 19YY, and ignore the Century Bit */
tm->tm_year = bcd2bin(buf[M41T80_REG_YEAR]) + 100;
- return rtc_valid_tm(tm);
+ return 0;
}
-/* Sets the given date and time to the real time clock. */
-static int m41t80_set_datetime(struct i2c_client *client, struct rtc_time *tm)
+static int m41t80_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct m41t80_data *clientdata = i2c_get_clientdata(client);
unsigned char buf[8];
int err, flags;
@@ -298,16 +297,6 @@ static int m41t80_rtc_proc(struct device *dev, struct seq_file *seq)
return 0;
}
-static int m41t80_rtc_read_time(struct device *dev, struct rtc_time *tm)
-{
- return m41t80_get_datetime(to_i2c_client(dev), tm);
-}
-
-static int m41t80_rtc_set_time(struct device *dev, struct rtc_time *tm)
-{
- return m41t80_set_datetime(to_i2c_client(dev), tm);
-}
-
static int m41t80_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
struct i2c_client *client = to_i2c_client(dev);
@@ -598,6 +587,7 @@ static struct clk *m41t80_sqw_register_clk(struct m41t80_data *m41t80)
*
*****************************************************************************
*/
+static DEFINE_MUTEX(m41t80_rtc_mutex);
static struct i2c_client *save_client;
/* Default margin */
@@ -885,7 +875,6 @@ static int m41t80_probe(struct i2c_client *client,
{
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
int rc = 0;
- struct rtc_device *rtc = NULL;
struct rtc_time tm;
struct m41t80_data *m41t80_data = NULL;
bool wakeup_source = false;
@@ -909,6 +898,10 @@ static int m41t80_probe(struct i2c_client *client,
m41t80_data->features = id->driver_data;
i2c_set_clientdata(client, m41t80_data);
+ m41t80_data->rtc = devm_rtc_allocate_device(&client->dev);
+ if (IS_ERR(m41t80_data->rtc))
+ return PTR_ERR(m41t80_data->rtc);
+
#ifdef CONFIG_OF
wakeup_source = of_property_read_bool(client->dev.of_node,
"wakeup-source");
@@ -932,15 +925,11 @@ static int m41t80_probe(struct i2c_client *client,
device_init_wakeup(&client->dev, true);
}
- rtc = devm_rtc_device_register(&client->dev, client->name,
- &m41t80_rtc_ops, THIS_MODULE);
- if (IS_ERR(rtc))
- return PTR_ERR(rtc);
+ m41t80_data->rtc->ops = &m41t80_rtc_ops;
- m41t80_data->rtc = rtc;
if (client->irq <= 0) {
/* We cannot support UIE mode if we do not have an IRQ line */
- rtc->uie_unsupported = 1;
+ m41t80_data->rtc->uie_unsupported = 1;
}
/* Make sure HT (Halt Update) bit is cleared */
@@ -948,7 +937,7 @@ static int m41t80_probe(struct i2c_client *client,
if (rc >= 0 && rc & M41T80_ALHOUR_HT) {
if (m41t80_data->features & M41T80_FEATURE_HT) {
- m41t80_get_datetime(client, &tm);
+ m41t80_rtc_read_time(&client->dev, &tm);
dev_info(&client->dev, "HT bit was set!\n");
dev_info(&client->dev,
"Power Down at %04i-%02i-%02i %02i:%02i:%02i\n",
@@ -993,6 +982,11 @@ static int m41t80_probe(struct i2c_client *client,
if (m41t80_data->features & M41T80_FEATURE_SQ)
m41t80_sqw_register_clk(m41t80_data);
#endif
+
+ rc = rtc_register_device(m41t80_data->rtc);
+ if (rc)
+ return rc;
+
return 0;
}
diff --git a/drivers/rtc/rtc-m41t93.c b/drivers/rtc/rtc-m41t93.c
index 5ac45fc1a787..4a08a9dabc82 100644
--- a/drivers/rtc/rtc-m41t93.c
+++ b/drivers/rtc/rtc-m41t93.c
@@ -159,7 +159,7 @@ static int m41t93_get_time(struct device *dev, struct rtc_time *tm)
tm->tm_hour, tm->tm_mday,
tm->tm_mon, tm->tm_year, tm->tm_wday);
- return ret < 0 ? ret : rtc_valid_tm(tm);
+ return ret;
}
diff --git a/drivers/rtc/rtc-m41t94.c b/drivers/rtc/rtc-m41t94.c
index 1f0eb79e69f9..bab82b4be356 100644
--- a/drivers/rtc/rtc-m41t94.c
+++ b/drivers/rtc/rtc-m41t94.c
@@ -99,8 +99,7 @@ static int m41t94_read_time(struct device *dev, struct rtc_time *tm)
tm->tm_hour, tm->tm_mday,
tm->tm_mon, tm->tm_year, tm->tm_wday);
- /* initial clock setting can be undefined */
- return rtc_valid_tm(tm);
+ return 0;
}
static const struct rtc_class_ops m41t94_rtc_ops = {
diff --git a/drivers/rtc/rtc-m48t35.c b/drivers/rtc/rtc-m48t35.c
index 810f4ea481e4..0cf6507de3c7 100644
--- a/drivers/rtc/rtc-m48t35.c
+++ b/drivers/rtc/rtc-m48t35.c
@@ -84,7 +84,7 @@ static int m48t35_read_time(struct device *dev, struct rtc_time *tm)
tm->tm_year += 100;
tm->tm_mon--;
- return rtc_valid_tm(tm);
+ return 0;
}
static int m48t35_set_time(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-m48t59.c b/drivers/rtc/rtc-m48t59.c
index d99a705bec07..216fac62c888 100644
--- a/drivers/rtc/rtc-m48t59.c
+++ b/drivers/rtc/rtc-m48t59.c
@@ -105,7 +105,7 @@ static int m48t59_rtc_read_time(struct device *dev, struct rtc_time *tm)
dev_dbg(dev, "RTC read time %04d-%02d-%02d %02d/%02d/%02d\n",
tm->tm_year + 1900, tm->tm_mon, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
- return rtc_valid_tm(tm);
+ return 0;
}
static int m48t59_rtc_set_time(struct device *dev, struct rtc_time *tm)
@@ -334,16 +334,16 @@ static const struct rtc_class_ops m48t02_rtc_ops = {
.set_time = m48t59_rtc_set_time,
};
-static ssize_t m48t59_nvram_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
- char *buf, loff_t pos, size_t size)
+static int m48t59_nvram_read(void *priv, unsigned int offset, void *val,
+ size_t size)
{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct platform_device *pdev = to_platform_device(dev);
+ struct platform_device *pdev = priv;
+ struct device *dev = &pdev->dev;
struct m48t59_plat_data *pdata = dev_get_platdata(&pdev->dev);
struct m48t59_private *m48t59 = platform_get_drvdata(pdev);
ssize_t cnt = 0;
unsigned long flags;
+ u8 *buf = val;
spin_lock_irqsave(&m48t59->lock, flags);
@@ -352,19 +352,19 @@ static ssize_t m48t59_nvram_read(struct file *filp, struct kobject *kobj,
spin_unlock_irqrestore(&m48t59->lock, flags);
- return cnt;
+ return 0;
}
-static ssize_t m48t59_nvram_write(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
- char *buf, loff_t pos, size_t size)
+static int m48t59_nvram_write(void *priv, unsigned int offset, void *val,
+ size_t size)
{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct platform_device *pdev = to_platform_device(dev);
+ struct platform_device *pdev = priv;
+ struct device *dev = &pdev->dev;
struct m48t59_plat_data *pdata = dev_get_platdata(&pdev->dev);
struct m48t59_private *m48t59 = platform_get_drvdata(pdev);
ssize_t cnt = 0;
unsigned long flags;
+ u8 *buf = val;
spin_lock_irqsave(&m48t59->lock, flags);
@@ -373,18 +373,9 @@ static ssize_t m48t59_nvram_write(struct file *filp, struct kobject *kobj,
spin_unlock_irqrestore(&m48t59->lock, flags);
- return cnt;
+ return 0;
}
-static struct bin_attribute m48t59_nvram_attr = {
- .attr = {
- .name = "nvram",
- .mode = S_IRUGO | S_IWUSR,
- },
- .read = m48t59_nvram_read,
- .write = m48t59_nvram_write,
-};
-
static int m48t59_rtc_probe(struct platform_device *pdev)
{
struct m48t59_plat_data *pdata = dev_get_platdata(&pdev->dev);
@@ -393,6 +384,14 @@ static int m48t59_rtc_probe(struct platform_device *pdev)
int ret = -ENOMEM;
char *name;
const struct rtc_class_ops *ops;
+ struct nvmem_config nvmem_cfg = {
+ .name = "m48t59-",
+ .word_size = 1,
+ .stride = 1,
+ .reg_read = m48t59_nvram_read,
+ .reg_write = m48t59_nvram_write,
+ .priv = pdev,
+ };
/* This chip could be memory-mapped or I/O-mapped */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -480,23 +479,22 @@ static int m48t59_rtc_probe(struct platform_device *pdev)
spin_lock_init(&m48t59->lock);
platform_set_drvdata(pdev, m48t59);
- m48t59->rtc = devm_rtc_device_register(&pdev->dev, name, ops,
- THIS_MODULE);
+ m48t59->rtc = devm_rtc_allocate_device(&pdev->dev);
if (IS_ERR(m48t59->rtc))
return PTR_ERR(m48t59->rtc);
- m48t59_nvram_attr.size = pdata->offset;
+ m48t59->rtc->nvram_old_abi = true;
+ m48t59->rtc->ops = ops;
- ret = sysfs_create_bin_file(&pdev->dev.kobj, &m48t59_nvram_attr);
+ nvmem_cfg.size = pdata->offset;
+ ret = rtc_nvmem_register(m48t59->rtc, &nvmem_cfg);
if (ret)
return ret;
- return 0;
-}
+ ret = rtc_register_device(m48t59->rtc);
+ if (ret)
+ return ret;
-static int m48t59_rtc_remove(struct platform_device *pdev)
-{
- sysfs_remove_bin_file(&pdev->dev.kobj, &m48t59_nvram_attr);
return 0;
}
@@ -508,7 +506,6 @@ static struct platform_driver m48t59_rtc_driver = {
.name = "rtc-m48t59",
},
.probe = m48t59_rtc_probe,
- .remove = m48t59_rtc_remove,
};
module_platform_driver(m48t59_rtc_driver);
diff --git a/drivers/rtc/rtc-m48t86.c b/drivers/rtc/rtc-m48t86.c
index d9aea9b6d9cd..a9533535c3b7 100644
--- a/drivers/rtc/rtc-m48t86.c
+++ b/drivers/rtc/rtc-m48t86.c
@@ -100,7 +100,7 @@ static int m48t86_rtc_read_time(struct device *dev, struct rtc_time *tm)
if (m48t86_readb(dev, M48T86_HOUR) & 0x80)
tm->tm_hour += 12;
- return rtc_valid_tm(tm);
+ return 0;
}
static int m48t86_rtc_set_time(struct device *dev, struct rtc_time *tm)
@@ -218,21 +218,21 @@ static bool m48t86_verify_chip(struct platform_device *pdev)
return false;
}
-static struct nvmem_config m48t86_nvmem_cfg = {
- .name = "m48t86_nvram",
- .word_size = 1,
- .stride = 1,
- .size = M48T86_NVRAM_LEN,
- .reg_read = m48t86_nvram_read,
- .reg_write = m48t86_nvram_write,
-};
-
static int m48t86_rtc_probe(struct platform_device *pdev)
{
struct m48t86_rtc_info *info;
struct resource *res;
unsigned char reg;
int err;
+ struct nvmem_config m48t86_nvmem_cfg = {
+ .name = "m48t86_nvram",
+ .word_size = 1,
+ .stride = 1,
+ .size = M48T86_NVRAM_LEN,
+ .reg_read = m48t86_nvram_read,
+ .reg_write = m48t86_nvram_write,
+ .priv = &pdev->dev,
+ };
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info)
@@ -264,15 +264,14 @@ static int m48t86_rtc_probe(struct platform_device *pdev)
return PTR_ERR(info->rtc);
info->rtc->ops = &m48t86_rtc_ops;
-
- m48t86_nvmem_cfg.priv = &pdev->dev;
- info->rtc->nvmem_config = &m48t86_nvmem_cfg;
info->rtc->nvram_old_abi = true;
err = rtc_register_device(info->rtc);
if (err)
return err;
+ rtc_nvmem_register(info->rtc, &m48t86_nvmem_cfg);
+
/* read battery status */
reg = m48t86_readb(&pdev->dev, M48T86_D);
dev_info(&pdev->dev, "battery %s\n",
diff --git a/drivers/rtc/rtc-max6900.c b/drivers/rtc/rtc-max6900.c
index cbdc86a560ba..ab60f13fa3ef 100644
--- a/drivers/rtc/rtc-max6900.c
+++ b/drivers/rtc/rtc-max6900.c
@@ -139,8 +139,9 @@ static int max6900_i2c_write_regs(struct i2c_client *client, u8 const *buf)
return -EIO;
}
-static int max6900_i2c_read_time(struct i2c_client *client, struct rtc_time *tm)
+static int max6900_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
+ struct i2c_client *client = to_i2c_client(dev);
int rc;
u8 regs[MAX6900_REG_LEN];
@@ -157,7 +158,7 @@ static int max6900_i2c_read_time(struct i2c_client *client, struct rtc_time *tm)
bcd2bin(regs[MAX6900_REG_CENTURY]) * 100 - 1900;
tm->tm_wday = bcd2bin(regs[MAX6900_REG_DW]);
- return rtc_valid_tm(tm);
+ return 0;
}
static int max6900_i2c_clear_write_protect(struct i2c_client *client)
@@ -165,9 +166,9 @@ static int max6900_i2c_clear_write_protect(struct i2c_client *client)
return i2c_smbus_write_byte_data(client, MAX6900_REG_CONTROL_WRITE, 0);
}
-static int
-max6900_i2c_set_time(struct i2c_client *client, struct rtc_time const *tm)
+static int max6900_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
+ struct i2c_client *client = to_i2c_client(dev);
u8 regs[MAX6900_REG_LEN];
int rc;
@@ -193,16 +194,6 @@ max6900_i2c_set_time(struct i2c_client *client, struct rtc_time const *tm)
return 0;
}
-static int max6900_rtc_read_time(struct device *dev, struct rtc_time *tm)
-{
- return max6900_i2c_read_time(to_i2c_client(dev), tm);
-}
-
-static int max6900_rtc_set_time(struct device *dev, struct rtc_time *tm)
-{
- return max6900_i2c_set_time(to_i2c_client(dev), tm);
-}
-
static const struct rtc_class_ops max6900_rtc_ops = {
.read_time = max6900_rtc_read_time,
.set_time = max6900_rtc_set_time,
diff --git a/drivers/rtc/rtc-max6902.c b/drivers/rtc/rtc-max6902.c
index 315d09e0f2c1..745827463367 100644
--- a/drivers/rtc/rtc-max6902.c
+++ b/drivers/rtc/rtc-max6902.c
@@ -85,7 +85,7 @@ static int max6902_read_time(struct device *dev, struct rtc_time *dt)
dt->tm_year += century;
dt->tm_year -= 1900;
- return rtc_valid_tm(dt);
+ return 0;
}
static int max6902_set_time(struct device *dev, struct rtc_time *dt)
diff --git a/drivers/rtc/rtc-max6916.c b/drivers/rtc/rtc-max6916.c
index 623ab27b2757..7e908a490cf6 100644
--- a/drivers/rtc/rtc-max6916.c
+++ b/drivers/rtc/rtc-max6916.c
@@ -75,7 +75,7 @@ static int max6916_read_time(struct device *dev, struct rtc_time *dt)
dt->tm_wday = bcd2bin(buf[5]) - 1;
dt->tm_year = bcd2bin(buf[6]) + 100;
- return rtc_valid_tm(dt);
+ return 0;
}
static int max6916_set_time(struct device *dev, struct rtc_time *dt)
diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c
index 182fdd00e290..cefde273fae6 100644
--- a/drivers/rtc/rtc-max77686.c
+++ b/drivers/rtc/rtc-max77686.c
@@ -364,11 +364,9 @@ static int max77686_rtc_read_time(struct device *dev, struct rtc_time *tm)
max77686_rtc_data_to_tm(data, tm, info);
- ret = rtc_valid_tm(tm);
-
out:
mutex_unlock(&info->lock);
- return ret;
+ return 0;
}
static int max77686_rtc_set_time(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-max8997.c b/drivers/rtc/rtc-max8997.c
index db984d4bf952..e8cee123e8aa 100644
--- a/drivers/rtc/rtc-max8997.c
+++ b/drivers/rtc/rtc-max8997.c
@@ -153,7 +153,7 @@ static int max8997_rtc_read_time(struct device *dev, struct rtc_time *tm)
max8997_rtc_data_to_tm(data, tm, info->rtc_24hr_mode);
- return rtc_valid_tm(tm);
+ return 0;
}
static int max8997_rtc_set_time(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-max8998.c b/drivers/rtc/rtc-max8998.c
index 30804b00985e..d8c0f9b3f87d 100644
--- a/drivers/rtc/rtc-max8998.c
+++ b/drivers/rtc/rtc-max8998.c
@@ -120,7 +120,7 @@ static int max8998_rtc_read_time(struct device *dev, struct rtc_time *tm)
max8998_data_to_tm(data, tm);
- return rtc_valid_tm(tm);
+ return 0;
}
static int max8998_rtc_set_time(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-mc13xxx.c b/drivers/rtc/rtc-mc13xxx.c
index 30b8ef6a3676..1f892b238ddb 100644
--- a/drivers/rtc/rtc-mc13xxx.c
+++ b/drivers/rtc/rtc-mc13xxx.c
@@ -85,7 +85,7 @@ static int mc13xxx_rtc_read_time(struct device *dev, struct rtc_time *tm)
rtc_time64_to_tm((time64_t)days1 * SEC_PER_DAY + seconds, tm);
- return rtc_valid_tm(tm);
+ return 0;
}
static int mc13xxx_rtc_set_mmss(struct device *dev, time64_t secs)
diff --git a/drivers/rtc/rtc-mcp795.c b/drivers/rtc/rtc-mcp795.c
index 77f21331ae21..00e11c1b2186 100644
--- a/drivers/rtc/rtc-mcp795.c
+++ b/drivers/rtc/rtc-mcp795.c
@@ -82,7 +82,7 @@ static int mcp795_rtcc_write(struct device *dev, u8 addr, u8 *data, u8 count)
{
struct spi_device *spi = to_spi_device(dev);
int ret;
- u8 tx[2 + count];
+ u8 tx[257];
tx[0] = MCP795_WRITE;
tx[1] = addr;
@@ -262,7 +262,7 @@ static int mcp795_read_time(struct device *dev, struct rtc_time *tim)
tim->tm_year + 1900, tim->tm_mon, tim->tm_mday,
tim->tm_wday, tim->tm_hour, tim->tm_min, tim->tm_sec);
- return rtc_valid_tm(tim);
+ return 0;
}
static int mcp795_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
diff --git a/drivers/rtc/rtc-mpc5121.c b/drivers/rtc/rtc-mpc5121.c
index 4ca4daa0b8f3..dd0364293bc0 100644
--- a/drivers/rtc/rtc-mpc5121.c
+++ b/drivers/rtc/rtc-mpc5121.c
@@ -122,7 +122,7 @@ static int mpc5121_rtc_read_time(struct device *dev, struct rtc_time *tm)
*/
mpc5121_rtc_update_smh(regs, tm);
- return rtc_valid_tm(tm);
+ return 0;
}
static int mpc5121_rtc_set_time(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-mrst.c b/drivers/rtc/rtc-mrst.c
index 7334c44fa7c3..fcb9de5218b2 100644
--- a/drivers/rtc/rtc-mrst.c
+++ b/drivers/rtc/rtc-mrst.c
@@ -105,7 +105,7 @@ static int mrst_read_time(struct device *dev, struct rtc_time *time)
/* Adjust for the 1972/1900 */
time->tm_year += 72;
time->tm_mon--;
- return rtc_valid_tm(time);
+ return 0;
}
static int mrst_set_time(struct device *dev, struct rtc_time *time)
@@ -122,7 +122,7 @@ static int mrst_set_time(struct device *dev, struct rtc_time *time)
min = time->tm_min;
sec = time->tm_sec;
- if (yrs < 72 || yrs > 138)
+ if (yrs < 72 || yrs > 172)
return -EINVAL;
yrs -= 72;
diff --git a/drivers/rtc/rtc-msm6242.c b/drivers/rtc/rtc-msm6242.c
index c1c5c4e3b3b4..0c72a2e8ec67 100644
--- a/drivers/rtc/rtc-msm6242.c
+++ b/drivers/rtc/rtc-msm6242.c
@@ -155,7 +155,7 @@ static int msm6242_read_time(struct device *dev, struct rtc_time *tm)
msm6242_unlock(priv);
- return rtc_valid_tm(tm);
+ return 0;
}
static int msm6242_set_time(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-mt7622.c b/drivers/rtc/rtc-mt7622.c
index d79b9ae4d237..fd0cea722286 100644
--- a/drivers/rtc/rtc-mt7622.c
+++ b/drivers/rtc/rtc-mt7622.c
@@ -232,7 +232,7 @@ static int mtk_rtc_gettime(struct device *dev, struct rtc_time *tm)
mtk_rtc_get_alarm_or_time(hw, tm, MTK_TC);
- return rtc_valid_tm(tm);
+ return 0;
}
static int mtk_rtc_settime(struct device *dev, struct rtc_time *tm)
@@ -307,6 +307,7 @@ static const struct of_device_id mtk_rtc_match[] = {
{ .compatible = "mediatek,soc-rtc" },
{},
};
+MODULE_DEVICE_TABLE(of, mtk_rtc_match);
static int mtk_rtc_probe(struct platform_device *pdev)
{
diff --git a/drivers/rtc/rtc-mv.c b/drivers/rtc/rtc-mv.c
index 79bb28617d45..bc52dbb0c0e2 100644
--- a/drivers/rtc/rtc-mv.c
+++ b/drivers/rtc/rtc-mv.c
@@ -94,7 +94,7 @@ static int mv_rtc_read_time(struct device *dev, struct rtc_time *tm)
/* hw counts from year 2000, but tm_year is relative to 1900 */
tm->tm_year = bcd2bin(year) + 100;
- return rtc_valid_tm(tm);
+ return 0;
}
static int mv_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
@@ -223,7 +223,6 @@ static int __init mv_rtc_probe(struct platform_device *pdev)
struct resource *res;
struct rtc_plat_data *pdata;
u32 rtc_time;
- u32 rtc_date;
int ret = 0;
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
@@ -259,17 +258,6 @@ static int __init mv_rtc_probe(struct platform_device *pdev)
}
}
- /*
- * A date after January 19th, 2038 does not fit on 32 bits and
- * will confuse the kernel and userspace. Reset to a sane date
- * (January 1st, 2013) if we're after 2038.
- */
- rtc_date = readl(pdata->ioaddr + RTC_DATE_REG_OFFS);
- if (bcd2bin((rtc_date >> RTC_YEAR_OFFS) & 0xff) >= 38) {
- dev_info(&pdev->dev, "invalid RTC date, resetting to January 1st, 2013\n");
- writel(0x130101, pdata->ioaddr + RTC_DATE_REG_OFFS);
- }
-
pdata->irq = platform_get_irq(pdev, 0);
platform_set_drvdata(pdev, pdata);
diff --git a/drivers/rtc/rtc-mxc_v2.c b/drivers/rtc/rtc-mxc_v2.c
index 784221dfc9c7..9e14efb990b2 100644
--- a/drivers/rtc/rtc-mxc_v2.c
+++ b/drivers/rtc/rtc-mxc_v2.c
@@ -273,7 +273,7 @@ static const struct rtc_class_ops mxc_rtc_ops = {
.alarm_irq_enable = mxc_rtc_alarm_irq_enable,
};
-static int mxc_rtc_wait_for_flag(void *__iomem ioaddr, int flag)
+static int mxc_rtc_wait_for_flag(void __iomem *ioaddr, int flag)
{
unsigned int timeout = REG_READ_TIMEOUT;
diff --git a/drivers/rtc/rtc-nuc900.c b/drivers/rtc/rtc-nuc900.c
index 4ed81117cf5f..7da664a77181 100644
--- a/drivers/rtc/rtc-nuc900.c
+++ b/drivers/rtc/rtc-nuc900.c
@@ -102,8 +102,8 @@ static int *check_rtc_access_enable(struct nuc900_rtc *nuc900_rtc)
return NULL;
}
-static int nuc900_rtc_bcd2bin(unsigned int timereg,
- unsigned int calreg, struct rtc_time *tm)
+static void nuc900_rtc_bcd2bin(unsigned int timereg,
+ unsigned int calreg, struct rtc_time *tm)
{
tm->tm_mday = bcd2bin(calreg >> 0);
tm->tm_mon = bcd2bin(calreg >> 8);
@@ -112,8 +112,6 @@ static int nuc900_rtc_bcd2bin(unsigned int timereg,
tm->tm_sec = bcd2bin(timereg >> 0);
tm->tm_min = bcd2bin(timereg >> 8);
tm->tm_hour = bcd2bin(timereg >> 16);
-
- return rtc_valid_tm(tm);
}
static void nuc900_rtc_bin2bcd(struct device *dev, struct rtc_time *settm,
@@ -156,7 +154,9 @@ static int nuc900_rtc_read_time(struct device *dev, struct rtc_time *tm)
timeval = __raw_readl(rtc->rtc_reg + REG_RTC_TLR);
clrval = __raw_readl(rtc->rtc_reg + REG_RTC_CLR);
- return nuc900_rtc_bcd2bin(timeval, clrval, tm);
+ nuc900_rtc_bcd2bin(timeval, clrval, tm);
+
+ return 0;
}
static int nuc900_rtc_set_time(struct device *dev, struct rtc_time *tm)
@@ -189,7 +189,9 @@ static int nuc900_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
timeval = __raw_readl(rtc->rtc_reg + REG_RTC_TAR);
carval = __raw_readl(rtc->rtc_reg + REG_RTC_CAR);
- return nuc900_rtc_bcd2bin(timeval, carval, &alrm->time);
+ nuc900_rtc_bcd2bin(timeval, carval, &alrm->time);
+
+ return rtc_valid_tm(&alrm->time);
}
static int nuc900_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c
index 09ef802d6e54..39086398833e 100644
--- a/drivers/rtc/rtc-omap.c
+++ b/drivers/rtc/rtc-omap.c
@@ -273,9 +273,6 @@ static int omap_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
/* this hardware doesn't support "don't care" alarm fields */
static int tm2bcd(struct rtc_time *tm)
{
- if (rtc_valid_tm(tm) != 0)
- return -EINVAL;
-
tm->tm_sec = bin2bcd(tm->tm_sec);
tm->tm_min = bin2bcd(tm->tm_min);
tm->tm_hour = bin2bcd(tm->tm_hour);
@@ -850,7 +847,6 @@ static int omap_rtc_probe(struct platform_device *pdev)
rtc->rtc->ops = &omap_rtc_ops;
omap_rtc_nvmem_config.priv = rtc;
- rtc->rtc->nvmem_config = &omap_rtc_nvmem_config;
/* handle periodic and alarm irqs */
ret = devm_request_irq(&pdev->dev, rtc->irq_timer, rtc_irq, 0,
@@ -886,6 +882,8 @@ static int omap_rtc_probe(struct platform_device *pdev)
if (ret)
goto err;
+ rtc_nvmem_register(rtc->rtc, &omap_rtc_nvmem_config);
+
return 0;
err:
diff --git a/drivers/rtc/rtc-opal.c b/drivers/rtc/rtc-opal.c
index 304e891e35fc..60f2250fd96b 100644
--- a/drivers/rtc/rtc-opal.c
+++ b/drivers/rtc/rtc-opal.c
@@ -57,7 +57,7 @@ static void tm_to_opal(struct rtc_time *tm, u32 *y_m_d, u64 *h_m_s_ms)
static int opal_get_rtc_time(struct device *dev, struct rtc_time *tm)
{
- long rc = OPAL_BUSY;
+ s64 rc = OPAL_BUSY;
int retries = 10;
u32 y_m_d;
u64 h_m_s_ms;
@@ -66,13 +66,17 @@ static int opal_get_rtc_time(struct device *dev, struct rtc_time *tm)
while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
rc = opal_rtc_read(&__y_m_d, &__h_m_s_ms);
- if (rc == OPAL_BUSY_EVENT)
+ if (rc == OPAL_BUSY_EVENT) {
+ msleep(OPAL_BUSY_DELAY_MS);
opal_poll_events(NULL);
- else if (retries-- && (rc == OPAL_HARDWARE
- || rc == OPAL_INTERNAL_ERROR))
- msleep(10);
- else if (rc != OPAL_BUSY && rc != OPAL_BUSY_EVENT)
- break;
+ } else if (rc == OPAL_BUSY) {
+ msleep(OPAL_BUSY_DELAY_MS);
+ } else if (rc == OPAL_HARDWARE || rc == OPAL_INTERNAL_ERROR) {
+ if (retries--) {
+ msleep(10); /* Wait 10ms before retry */
+ rc = OPAL_BUSY; /* go around again */
+ }
+ }
}
if (rc != OPAL_SUCCESS)
@@ -87,21 +91,26 @@ static int opal_get_rtc_time(struct device *dev, struct rtc_time *tm)
static int opal_set_rtc_time(struct device *dev, struct rtc_time *tm)
{
- long rc = OPAL_BUSY;
+ s64 rc = OPAL_BUSY;
int retries = 10;
u32 y_m_d = 0;
u64 h_m_s_ms = 0;
tm_to_opal(tm, &y_m_d, &h_m_s_ms);
+
while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
rc = opal_rtc_write(y_m_d, h_m_s_ms);
- if (rc == OPAL_BUSY_EVENT)
+ if (rc == OPAL_BUSY_EVENT) {
+ msleep(OPAL_BUSY_DELAY_MS);
opal_poll_events(NULL);
- else if (retries-- && (rc == OPAL_HARDWARE
- || rc == OPAL_INTERNAL_ERROR))
- msleep(10);
- else if (rc != OPAL_BUSY && rc != OPAL_BUSY_EVENT)
- break;
+ } else if (rc == OPAL_BUSY) {
+ msleep(OPAL_BUSY_DELAY_MS);
+ } else if (rc == OPAL_HARDWARE || rc == OPAL_INTERNAL_ERROR) {
+ if (retries--) {
+ msleep(10); /* Wait 10ms before retry */
+ rc = OPAL_BUSY; /* go around again */
+ }
+ }
}
return rc == OPAL_SUCCESS ? 0 : -EIO;
diff --git a/drivers/rtc/rtc-pcap.c b/drivers/rtc/rtc-pcap.c
index c4433240d8a9..c05f524ba9af 100644
--- a/drivers/rtc/rtc-pcap.c
+++ b/drivers/rtc/rtc-pcap.c
@@ -95,7 +95,7 @@ static int pcap_rtc_read_time(struct device *dev, struct rtc_time *tm)
rtc_time_to_tm(secs, tm);
- return rtc_valid_tm(tm);
+ return 0;
}
static int pcap_rtc_set_mmss(struct device *dev, unsigned long secs)
diff --git a/drivers/rtc/rtc-pcf2123.c b/drivers/rtc/rtc-pcf2123.c
index 8895f77726e8..e5222c5d8223 100644
--- a/drivers/rtc/rtc-pcf2123.c
+++ b/drivers/rtc/rtc-pcf2123.c
@@ -289,7 +289,7 @@ static int pcf2123_rtc_read_time(struct device *dev, struct rtc_time *tm)
tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
- return rtc_valid_tm(tm);
+ return 0;
}
static int pcf2123_rtc_set_time(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
index f33447c5db85..e83be1852c2f 100644
--- a/drivers/rtc/rtc-pcf2127.c
+++ b/drivers/rtc/rtc-pcf2127.c
@@ -111,7 +111,7 @@ static int pcf2127_rtc_read_time(struct device *dev, struct rtc_time *tm)
tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
- return rtc_valid_tm(tm);
+ return 0;
}
static int pcf2127_rtc_set_time(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-pcf50633.c b/drivers/rtc/rtc-pcf50633.c
index 00c31c91b245..ef72b0c389d7 100644
--- a/drivers/rtc/rtc-pcf50633.c
+++ b/drivers/rtc/rtc-pcf50633.c
@@ -135,7 +135,7 @@ static int pcf50633_rtc_read_time(struct device *dev, struct rtc_time *tm)
tm->tm_mday, tm->tm_mon, tm->tm_year,
tm->tm_hour, tm->tm_min, tm->tm_sec);
- return rtc_valid_tm(tm);
+ return 0;
}
static int pcf50633_rtc_set_time(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-pcf85063.c b/drivers/rtc/rtc-pcf85063.c
index a06dff994c83..49bcbb3d4a69 100644
--- a/drivers/rtc/rtc-pcf85063.c
+++ b/drivers/rtc/rtc-pcf85063.c
@@ -70,7 +70,7 @@ static int pcf85063_start_clock(struct i2c_client *client, u8 ctrl1)
s32 ret;
/* start the clock */
- ctrl1 &= PCF85063_REG_CTRL1_STOP;
+ ctrl1 &= ~PCF85063_REG_CTRL1_STOP;
ret = i2c_smbus_write_byte_data(client, PCF85063_REG_CTRL1, ctrl1);
if (ret < 0) {
@@ -81,8 +81,9 @@ static int pcf85063_start_clock(struct i2c_client *client, u8 ctrl1)
return 0;
}
-static int pcf85063_get_datetime(struct i2c_client *client, struct rtc_time *tm)
+static int pcf85063_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
+ struct i2c_client *client = to_i2c_client(dev);
int rc;
u8 regs[7];
@@ -114,11 +115,12 @@ static int pcf85063_get_datetime(struct i2c_client *client, struct rtc_time *tm)
tm->tm_year = bcd2bin(regs[6]);
tm->tm_year += 100;
- return rtc_valid_tm(tm);
+ return 0;
}
-static int pcf85063_set_datetime(struct i2c_client *client, struct rtc_time *tm)
+static int pcf85063_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
+ struct i2c_client *client = to_i2c_client(dev);
int rc;
u8 regs[7];
u8 ctrl1;
@@ -172,16 +174,6 @@ static int pcf85063_set_datetime(struct i2c_client *client, struct rtc_time *tm)
return 0;
}
-static int pcf85063_rtc_read_time(struct device *dev, struct rtc_time *tm)
-{
- return pcf85063_get_datetime(to_i2c_client(dev), tm);
-}
-
-static int pcf85063_rtc_set_time(struct device *dev, struct rtc_time *tm)
-{
- return pcf85063_set_datetime(to_i2c_client(dev), tm);
-}
-
static const struct rtc_class_ops pcf85063_rtc_ops = {
.read_time = pcf85063_rtc_read_time,
.set_time = pcf85063_rtc_set_time
diff --git a/drivers/rtc/rtc-pcf8523.c b/drivers/rtc/rtc-pcf8523.c
index c312af0db729..453615f8ac9a 100644
--- a/drivers/rtc/rtc-pcf8523.c
+++ b/drivers/rtc/rtc-pcf8523.c
@@ -192,7 +192,7 @@ static int pcf8523_rtc_read_time(struct device *dev, struct rtc_time *tm)
tm->tm_mon = bcd2bin(regs[5] & 0x1f) - 1;
tm->tm_year = bcd2bin(regs[6]) + 100;
- return rtc_valid_tm(tm);
+ return 0;
}
static int pcf8523_rtc_set_time(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-pcf85363.c b/drivers/rtc/rtc-pcf85363.c
index ea04e9f0930b..c04a1edcd571 100644
--- a/drivers/rtc/rtc-pcf85363.c
+++ b/drivers/rtc/rtc-pcf85363.c
@@ -73,6 +73,43 @@
#define CTRL_RESETS 0x2f
#define CTRL_RAM 0x40
+#define ALRM_SEC_A1E BIT(0)
+#define ALRM_MIN_A1E BIT(1)
+#define ALRM_HR_A1E BIT(2)
+#define ALRM_DAY_A1E BIT(3)
+#define ALRM_MON_A1E BIT(4)
+#define ALRM_MIN_A2E BIT(5)
+#define ALRM_HR_A2E BIT(6)
+#define ALRM_DAY_A2E BIT(7)
+
+#define INT_WDIE BIT(0)
+#define INT_BSIE BIT(1)
+#define INT_TSRIE BIT(2)
+#define INT_A2IE BIT(3)
+#define INT_A1IE BIT(4)
+#define INT_OIE BIT(5)
+#define INT_PIE BIT(6)
+#define INT_ILP BIT(7)
+
+#define FLAGS_TSR1F BIT(0)
+#define FLAGS_TSR2F BIT(1)
+#define FLAGS_TSR3F BIT(2)
+#define FLAGS_BSF BIT(3)
+#define FLAGS_WDF BIT(4)
+#define FLAGS_A1F BIT(5)
+#define FLAGS_A2F BIT(6)
+#define FLAGS_PIF BIT(7)
+
+#define PIN_IO_INTAPM GENMASK(1, 0)
+#define PIN_IO_INTA_CLK 0
+#define PIN_IO_INTA_BAT 1
+#define PIN_IO_INTA_OUT 2
+#define PIN_IO_INTA_HIZ 3
+
+#define STOP_EN_STOP BIT(0)
+
+#define RESET_CPR 0xa4
+
#define NVRAM_SIZE 0x40
static struct i2c_driver pcf85363_driver;
@@ -80,7 +117,6 @@ static struct i2c_driver pcf85363_driver;
struct pcf85363 {
struct device *dev;
struct rtc_device *rtc;
- struct nvmem_config nvmem_cfg;
struct regmap *regmap;
};
@@ -116,8 +152,12 @@ static int pcf85363_rtc_read_time(struct device *dev, struct rtc_time *tm)
static int pcf85363_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct pcf85363 *pcf85363 = dev_get_drvdata(dev);
- unsigned char buf[DT_YEARS + 1];
- int len = sizeof(buf);
+ unsigned char tmp[11];
+ unsigned char *buf = &tmp[2];
+ int ret;
+
+ tmp[0] = STOP_EN_STOP;
+ tmp[1] = RESET_CPR;
buf[DT_100THS] = 0;
buf[DT_SECS] = bin2bcd(tm->tm_sec);
@@ -128,8 +168,116 @@ static int pcf85363_rtc_set_time(struct device *dev, struct rtc_time *tm)
buf[DT_MONTHS] = bin2bcd(tm->tm_mon + 1);
buf[DT_YEARS] = bin2bcd(tm->tm_year % 100);
- return regmap_bulk_write(pcf85363->regmap, DT_100THS,
- buf, len);
+ ret = regmap_bulk_write(pcf85363->regmap, CTRL_STOP_EN,
+ tmp, sizeof(tmp));
+ if (ret)
+ return ret;
+
+ return regmap_write(pcf85363->regmap, CTRL_STOP_EN, 0);
+}
+
+static int pcf85363_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct pcf85363 *pcf85363 = dev_get_drvdata(dev);
+ unsigned char buf[DT_MONTH_ALM1 - DT_SECOND_ALM1 + 1];
+ unsigned int val;
+ int ret;
+
+ ret = regmap_bulk_read(pcf85363->regmap, DT_SECOND_ALM1, buf,
+ sizeof(buf));
+ if (ret)
+ return ret;
+
+ alrm->time.tm_sec = bcd2bin(buf[0]);
+ alrm->time.tm_min = bcd2bin(buf[1]);
+ alrm->time.tm_hour = bcd2bin(buf[2]);
+ alrm->time.tm_mday = bcd2bin(buf[3]);
+ alrm->time.tm_mon = bcd2bin(buf[4]) - 1;
+
+ ret = regmap_read(pcf85363->regmap, CTRL_INTA_EN, &val);
+ if (ret)
+ return ret;
+
+ alrm->enabled = !!(val & INT_A1IE);
+
+ return 0;
+}
+
+static int _pcf85363_rtc_alarm_irq_enable(struct pcf85363 *pcf85363, unsigned
+ int enabled)
+{
+ unsigned int alarm_flags = ALRM_SEC_A1E | ALRM_MIN_A1E | ALRM_HR_A1E |
+ ALRM_DAY_A1E | ALRM_MON_A1E;
+ int ret;
+
+ ret = regmap_update_bits(pcf85363->regmap, DT_ALARM_EN, alarm_flags,
+ enabled ? alarm_flags : 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(pcf85363->regmap, CTRL_INTA_EN,
+ INT_A1IE, enabled ? INT_A1IE : 0);
+
+ if (ret || enabled)
+ return ret;
+
+ /* clear current flags */
+ return regmap_update_bits(pcf85363->regmap, CTRL_FLAGS, FLAGS_A1F, 0);
+}
+
+static int pcf85363_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct pcf85363 *pcf85363 = dev_get_drvdata(dev);
+
+ return _pcf85363_rtc_alarm_irq_enable(pcf85363, enabled);
+}
+
+static int pcf85363_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct pcf85363 *pcf85363 = dev_get_drvdata(dev);
+ unsigned char buf[DT_MONTH_ALM1 - DT_SECOND_ALM1 + 1];
+ int ret;
+
+ buf[0] = bin2bcd(alrm->time.tm_sec);
+ buf[1] = bin2bcd(alrm->time.tm_min);
+ buf[2] = bin2bcd(alrm->time.tm_hour);
+ buf[3] = bin2bcd(alrm->time.tm_mday);
+ buf[4] = bin2bcd(alrm->time.tm_mon + 1);
+
+ /*
+ * Disable the alarm interrupt before changing the value to avoid
+ * spurious interrupts
+ */
+ ret = _pcf85363_rtc_alarm_irq_enable(pcf85363, 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_bulk_write(pcf85363->regmap, DT_SECOND_ALM1, buf,
+ sizeof(buf));
+ if (ret)
+ return ret;
+
+ return _pcf85363_rtc_alarm_irq_enable(pcf85363, alrm->enabled);
+}
+
+static irqreturn_t pcf85363_rtc_handle_irq(int irq, void *dev_id)
+{
+ struct pcf85363 *pcf85363 = i2c_get_clientdata(dev_id);
+ unsigned int flags;
+ int err;
+
+ err = regmap_read(pcf85363->regmap, CTRL_FLAGS, &flags);
+ if (err)
+ return IRQ_NONE;
+
+ if (flags & FLAGS_A1F) {
+ rtc_update_irq(pcf85363->rtc, 1, RTC_IRQF | RTC_AF);
+ regmap_update_bits(pcf85363->regmap, CTRL_FLAGS, FLAGS_A1F, 0);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
}
static const struct rtc_class_ops rtc_ops = {
@@ -137,6 +285,14 @@ static const struct rtc_class_ops rtc_ops = {
.set_time = pcf85363_rtc_set_time,
};
+static const struct rtc_class_ops rtc_ops_alarm = {
+ .read_time = pcf85363_rtc_read_time,
+ .set_time = pcf85363_rtc_set_time,
+ .read_alarm = pcf85363_rtc_read_alarm,
+ .set_alarm = pcf85363_rtc_set_alarm,
+ .alarm_irq_enable = pcf85363_rtc_alarm_irq_enable,
+};
+
static int pcf85363_nvram_read(void *priv, unsigned int offset, void *val,
size_t bytes)
{
@@ -158,12 +314,22 @@ static int pcf85363_nvram_write(void *priv, unsigned int offset, void *val,
static const struct regmap_config regmap_config = {
.reg_bits = 8,
.val_bits = 8,
+ .max_register = 0x7f,
};
static int pcf85363_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct pcf85363 *pcf85363;
+ struct nvmem_config nvmem_cfg = {
+ .name = "pcf85363-",
+ .word_size = 1,
+ .stride = 1,
+ .size = NVRAM_SIZE,
+ .reg_read = pcf85363_nvram_read,
+ .reg_write = pcf85363_nvram_write,
+ };
+ int ret;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
return -ENODEV;
@@ -186,17 +352,28 @@ static int pcf85363_probe(struct i2c_client *client,
if (IS_ERR(pcf85363->rtc))
return PTR_ERR(pcf85363->rtc);
- pcf85363->nvmem_cfg.name = "pcf85363-";
- pcf85363->nvmem_cfg.word_size = 1;
- pcf85363->nvmem_cfg.stride = 1;
- pcf85363->nvmem_cfg.size = NVRAM_SIZE;
- pcf85363->nvmem_cfg.reg_read = pcf85363_nvram_read;
- pcf85363->nvmem_cfg.reg_write = pcf85363_nvram_write;
- pcf85363->nvmem_cfg.priv = pcf85363;
- pcf85363->rtc->nvmem_config = &pcf85363->nvmem_cfg;
pcf85363->rtc->ops = &rtc_ops;
- return rtc_register_device(pcf85363->rtc);
+ if (client->irq > 0) {
+ regmap_write(pcf85363->regmap, CTRL_FLAGS, 0);
+ regmap_update_bits(pcf85363->regmap, CTRL_PIN_IO,
+ PIN_IO_INTA_OUT, PIN_IO_INTAPM);
+ ret = devm_request_threaded_irq(pcf85363->dev, client->irq,
+ NULL, pcf85363_rtc_handle_irq,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "pcf85363", client);
+ if (ret)
+ dev_warn(&client->dev, "unable to request IRQ, alarms disabled\n");
+ else
+ pcf85363->rtc->ops = &rtc_ops_alarm;
+ }
+
+ ret = rtc_register_device(pcf85363->rtc);
+
+ nvmem_cfg.priv = pcf85363;
+ rtc_nvmem_register(pcf85363->rtc, &nvmem_cfg);
+
+ return ret;
}
static const struct of_device_id dev_ids[] = {
diff --git a/drivers/rtc/rtc-pic32.c b/drivers/rtc/rtc-pic32.c
index 5cfb6df5c430..3c08eab4f1a8 100644
--- a/drivers/rtc/rtc-pic32.c
+++ b/drivers/rtc/rtc-pic32.c
@@ -175,7 +175,7 @@ static int pic32_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);
clk_disable(pdata->clk);
- return rtc_valid_tm(rtc_tm);
+ return 0;
}
static int pic32_rtc_settime(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-pm8xxx.c b/drivers/rtc/rtc-pm8xxx.c
index fac835530671..29358a045925 100644
--- a/drivers/rtc/rtc-pm8xxx.c
+++ b/drivers/rtc/rtc-pm8xxx.c
@@ -74,16 +74,18 @@ struct pm8xxx_rtc {
/*
* Steps to write the RTC registers.
* 1. Disable alarm if enabled.
- * 2. Write 0x00 to LSB.
- * 3. Write Byte[1], Byte[2], Byte[3] then Byte[0].
- * 4. Enable alarm if disabled in step 1.
+ * 2. Disable rtc if enabled.
+ * 3. Write 0x00 to LSB.
+ * 4. Write Byte[1], Byte[2], Byte[3] then Byte[0].
+ * 5. Enable rtc if disabled in step 2.
+ * 6. Enable alarm if disabled in step 1.
*/
static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
int rc, i;
unsigned long secs, irq_flags;
- u8 value[NUM_8_BIT_RTC_REGS], alarm_enabled = 0;
- unsigned int ctrl_reg;
+ u8 value[NUM_8_BIT_RTC_REGS], alarm_enabled = 0, rtc_disabled = 0;
+ unsigned int ctrl_reg, rtc_ctrl_reg;
struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
@@ -92,23 +94,38 @@ static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm)
rtc_tm_to_time(tm, &secs);
+ dev_dbg(dev, "Seconds value to be written to RTC = %lu\n", secs);
+
for (i = 0; i < NUM_8_BIT_RTC_REGS; i++) {
value[i] = secs & 0xFF;
secs >>= 8;
}
- dev_dbg(dev, "Seconds value to be written to RTC = %lu\n", secs);
-
spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags);
- rc = regmap_read(rtc_dd->regmap, regs->ctrl, &ctrl_reg);
+ rc = regmap_read(rtc_dd->regmap, regs->alarm_ctrl, &ctrl_reg);
if (rc)
goto rtc_rw_fail;
if (ctrl_reg & regs->alarm_en) {
alarm_enabled = 1;
ctrl_reg &= ~regs->alarm_en;
- rc = regmap_write(rtc_dd->regmap, regs->ctrl, ctrl_reg);
+ rc = regmap_write(rtc_dd->regmap, regs->alarm_ctrl, ctrl_reg);
+ if (rc) {
+ dev_err(dev, "Write to RTC Alarm control register failed\n");
+ goto rtc_rw_fail;
+ }
+ }
+
+ /* Disable RTC H/w before writing on RTC register */
+ rc = regmap_read(rtc_dd->regmap, regs->ctrl, &rtc_ctrl_reg);
+ if (rc)
+ goto rtc_rw_fail;
+
+ if (rtc_ctrl_reg & PM8xxx_RTC_ENABLE) {
+ rtc_disabled = 1;
+ rtc_ctrl_reg &= ~PM8xxx_RTC_ENABLE;
+ rc = regmap_write(rtc_dd->regmap, regs->ctrl, rtc_ctrl_reg);
if (rc) {
dev_err(dev, "Write to RTC control register failed\n");
goto rtc_rw_fail;
@@ -137,11 +154,21 @@ static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm)
goto rtc_rw_fail;
}
+ /* Enable RTC H/w after writing on RTC register */
+ if (rtc_disabled) {
+ rtc_ctrl_reg |= PM8xxx_RTC_ENABLE;
+ rc = regmap_write(rtc_dd->regmap, regs->ctrl, rtc_ctrl_reg);
+ if (rc) {
+ dev_err(dev, "Write to RTC control register failed\n");
+ goto rtc_rw_fail;
+ }
+ }
+
if (alarm_enabled) {
ctrl_reg |= regs->alarm_en;
- rc = regmap_write(rtc_dd->regmap, regs->ctrl, ctrl_reg);
+ rc = regmap_write(rtc_dd->regmap, regs->alarm_ctrl, ctrl_reg);
if (rc) {
- dev_err(dev, "Write to RTC control register failed\n");
+ dev_err(dev, "Write to RTC Alarm control register failed\n");
goto rtc_rw_fail;
}
}
@@ -190,12 +217,6 @@ static int pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm)
rtc_time_to_tm(secs, tm);
- rc = rtc_valid_tm(tm);
- if (rc < 0) {
- dev_err(dev, "Invalid time read from RTC\n");
- return rc;
- }
-
dev_dbg(dev, "secs = %lu, h:m:s == %d:%d:%d, d/m/y = %d/%d/%d\n",
secs, tm->tm_hour, tm->tm_min, tm->tm_sec,
tm->tm_mday, tm->tm_mon, tm->tm_year);
diff --git a/drivers/rtc/rtc-ps3.c b/drivers/rtc/rtc-ps3.c
index 6a8f5d758eac..347288bff438 100644
--- a/drivers/rtc/rtc-ps3.c
+++ b/drivers/rtc/rtc-ps3.c
@@ -41,7 +41,7 @@ static u64 read_rtc(void)
static int ps3_get_time(struct device *dev, struct rtc_time *tm)
{
rtc_time_to_tm(read_rtc() + ps3_os_area_get_rtc_diff(), tm);
- return rtc_valid_tm(tm);
+ return 0;
}
static int ps3_set_time(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-r7301.c b/drivers/rtc/rtc-r7301.c
index 500e8c8a2605..169704b2ce13 100644
--- a/drivers/rtc/rtc-r7301.c
+++ b/drivers/rtc/rtc-r7301.c
@@ -224,7 +224,7 @@ static int rtc7301_read_time(struct device *dev, struct rtc_time *tm)
spin_unlock_irqrestore(&priv->lock, flags);
- return err ? err : rtc_valid_tm(tm);
+ return err;
}
static int rtc7301_set_time(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-r9701.c b/drivers/rtc/rtc-r9701.c
index b6c5eb97051c..a39ccd1cf6e8 100644
--- a/drivers/rtc/rtc-r9701.c
+++ b/drivers/rtc/rtc-r9701.c
@@ -92,7 +92,7 @@ static int r9701_get_datetime(struct device *dev, struct rtc_time *dt)
* according to the data sheet. make sure they are valid.
*/
- return rtc_valid_tm(dt);
+ return 0;
}
static int r9701_set_datetime(struct device *dev, struct rtc_time *dt)
diff --git a/drivers/rtc/rtc-rk808.c b/drivers/rtc/rtc-rk808.c
index 35c9aada07c8..739c0d42e835 100644
--- a/drivers/rtc/rtc-rk808.c
+++ b/drivers/rtc/rtc-rk808.c
@@ -375,7 +375,6 @@ static int rk808_rtc_probe(struct platform_device *pdev)
{
struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent);
struct rk808_rtc *rk808_rtc;
- struct rtc_time tm;
int ret;
rk808_rtc = devm_kzalloc(&pdev->dev, sizeof(*rk808_rtc), GFP_KERNEL);
@@ -404,24 +403,13 @@ static int rk808_rtc_probe(struct platform_device *pdev)
return ret;
}
- /* set init time */
- ret = rk808_rtc_readtime(&pdev->dev, &tm);
- if (ret) {
- dev_err(&pdev->dev, "Failed to read RTC time\n");
- return ret;
- }
- ret = rtc_valid_tm(&tm);
- if (ret)
- dev_warn(&pdev->dev, "invalid date/time\n");
-
device_init_wakeup(&pdev->dev, 1);
- rk808_rtc->rtc = devm_rtc_device_register(&pdev->dev, "rk808-rtc",
- &rk808_rtc_ops, THIS_MODULE);
- if (IS_ERR(rk808_rtc->rtc)) {
- ret = PTR_ERR(rk808_rtc->rtc);
- return ret;
- }
+ rk808_rtc->rtc = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(rk808_rtc->rtc))
+ return PTR_ERR(rk808_rtc->rtc);
+
+ rk808_rtc->rtc->ops = &rk808_rtc_ops;
rk808_rtc->irq = platform_get_irq(pdev, 0);
if (rk808_rtc->irq < 0) {
@@ -438,9 +426,10 @@ static int rk808_rtc_probe(struct platform_device *pdev)
if (ret) {
dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n",
rk808_rtc->irq, ret);
+ return ret;
}
- return ret;
+ return rtc_register_device(rk808_rtc->rtc);
}
static struct platform_driver rk808_rtc_driver = {
diff --git a/drivers/rtc/rtc-rp5c01.c b/drivers/rtc/rtc-rp5c01.c
index 026035373ae6..f1c160fe7d37 100644
--- a/drivers/rtc/rtc-rp5c01.c
+++ b/drivers/rtc/rtc-rp5c01.c
@@ -64,7 +64,6 @@ struct rp5c01_priv {
u32 __iomem *regs;
struct rtc_device *rtc;
spinlock_t lock; /* against concurrent RTC/NVRAM access */
- struct bin_attribute nvram_attr;
};
static inline unsigned int rp5c01_read(struct rp5c01_priv *priv,
@@ -116,7 +115,7 @@ static int rp5c01_read_time(struct device *dev, struct rtc_time *tm)
rp5c01_unlock(priv);
spin_unlock_irq(&priv->lock);
- return rtc_valid_tm(tm);
+ return 0;
}
static int rp5c01_set_time(struct device *dev, struct rtc_time *tm)
@@ -160,17 +159,15 @@ static const struct rtc_class_ops rp5c01_rtc_ops = {
* byte is stored in BLOCK10, the low nibble in BLOCK11.
*/
-static ssize_t rp5c01_nvram_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
- char *buf, loff_t pos, size_t size)
+static int rp5c01_nvram_read(void *_priv, unsigned int pos, void *val,
+ size_t bytes)
{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct rp5c01_priv *priv = dev_get_drvdata(dev);
- ssize_t count;
+ struct rp5c01_priv *priv = _priv;
+ u8 *buf = val;
spin_lock_irq(&priv->lock);
- for (count = 0; count < size; count++) {
+ for (; bytes; bytes--) {
u8 data;
rp5c01_write(priv,
@@ -187,20 +184,18 @@ static ssize_t rp5c01_nvram_read(struct file *filp, struct kobject *kobj,
}
spin_unlock_irq(&priv->lock);
- return count;
+ return 0;
}
-static ssize_t rp5c01_nvram_write(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
- char *buf, loff_t pos, size_t size)
+static int rp5c01_nvram_write(void *_priv, unsigned int pos, void *val,
+ size_t bytes)
{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct rp5c01_priv *priv = dev_get_drvdata(dev);
- ssize_t count;
+ struct rp5c01_priv *priv = _priv;
+ u8 *buf = val;
spin_lock_irq(&priv->lock);
- for (count = 0; count < size; count++) {
+ for (; bytes; bytes--) {
u8 data = *buf++;
rp5c01_write(priv,
@@ -216,7 +211,7 @@ static ssize_t rp5c01_nvram_write(struct file *filp, struct kobject *kobj,
}
spin_unlock_irq(&priv->lock);
- return count;
+ return 0;
}
static int __init rp5c01_rtc_probe(struct platform_device *dev)
@@ -225,6 +220,14 @@ static int __init rp5c01_rtc_probe(struct platform_device *dev)
struct rp5c01_priv *priv;
struct rtc_device *rtc;
int error;
+ struct nvmem_config nvmem_cfg = {
+ .name = "rp5c01_nvram",
+ .word_size = 1,
+ .stride = 1,
+ .size = RP5C01_MODE,
+ .reg_read = rp5c01_nvram_read,
+ .reg_write = rp5c01_nvram_write,
+ };
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (!res)
@@ -238,43 +241,31 @@ static int __init rp5c01_rtc_probe(struct platform_device *dev)
if (!priv->regs)
return -ENOMEM;
- sysfs_bin_attr_init(&priv->nvram_attr);
- priv->nvram_attr.attr.name = "nvram";
- priv->nvram_attr.attr.mode = S_IRUGO | S_IWUSR;
- priv->nvram_attr.read = rp5c01_nvram_read;
- priv->nvram_attr.write = rp5c01_nvram_write;
- priv->nvram_attr.size = RP5C01_MODE;
-
spin_lock_init(&priv->lock);
platform_set_drvdata(dev, priv);
- rtc = devm_rtc_device_register(&dev->dev, "rtc-rp5c01", &rp5c01_rtc_ops,
- THIS_MODULE);
+ rtc = devm_rtc_allocate_device(&dev->dev);
if (IS_ERR(rtc))
return PTR_ERR(rtc);
+
+ rtc->ops = &rp5c01_rtc_ops;
+ rtc->nvram_old_abi = true;
+
priv->rtc = rtc;
- error = sysfs_create_bin_file(&dev->dev.kobj, &priv->nvram_attr);
+ nvmem_cfg.priv = priv;
+ error = rtc_nvmem_register(rtc, &nvmem_cfg);
if (error)
return error;
- return 0;
-}
-
-static int __exit rp5c01_rtc_remove(struct platform_device *dev)
-{
- struct rp5c01_priv *priv = platform_get_drvdata(dev);
-
- sysfs_remove_bin_file(&dev->dev.kobj, &priv->nvram_attr);
- return 0;
+ return rtc_register_device(rtc);
}
static struct platform_driver rp5c01_rtc_driver = {
.driver = {
.name = "rtc-rp5c01",
},
- .remove = __exit_p(rp5c01_rtc_remove),
};
module_platform_driver_probe(rp5c01_rtc_driver, rp5c01_rtc_probe);
diff --git a/drivers/rtc/rtc-rs5c348.c b/drivers/rtc/rtc-rs5c348.c
index 9a306983aaba..f2de8b17e7e3 100644
--- a/drivers/rtc/rtc-rs5c348.c
+++ b/drivers/rtc/rtc-rs5c348.c
@@ -135,11 +135,6 @@ rs5c348_rtc_read_time(struct device *dev, struct rtc_time *tm)
tm->tm_year = bcd2bin(rxbuf[RS5C348_REG_YEAR]) +
((rxbuf[RS5C348_REG_MONTH] & RS5C348_BIT_Y2K) ? 100 : 0);
- if (rtc_valid_tm(tm) < 0) {
- dev_err(&spi->dev, "retrieved date/time is not valid.\n");
- rtc_time_to_tm(0, tm);
- }
-
return 0;
}
diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c
index d4eff8d7131f..c5038329058c 100644
--- a/drivers/rtc/rtc-rs5c372.c
+++ b/drivers/rtc/rtc-rs5c372.c
@@ -207,8 +207,9 @@ static unsigned rs5c_hr2reg(struct rs5c372 *rs5c, unsigned hour)
return bin2bcd(hour);
}
-static int rs5c372_get_datetime(struct i2c_client *client, struct rtc_time *tm)
+static int rs5c372_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct rs5c372 *rs5c = i2c_get_clientdata(client);
int status = rs5c_get_regs(rs5c);
@@ -234,12 +235,12 @@ static int rs5c372_get_datetime(struct i2c_client *client, struct rtc_time *tm)
tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
- /* rtc might need initialization */
- return rtc_valid_tm(tm);
+ return 0;
}
-static int rs5c372_set_datetime(struct i2c_client *client, struct rtc_time *tm)
+static int rs5c372_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct rs5c372 *rs5c = i2c_get_clientdata(client);
unsigned char buf[7];
int addr;
@@ -305,17 +306,6 @@ static int rs5c372_get_trim(struct i2c_client *client, int *osc, int *trim)
}
#endif
-static int rs5c372_rtc_read_time(struct device *dev, struct rtc_time *tm)
-{
- return rs5c372_get_datetime(to_i2c_client(dev), tm);
-}
-
-static int rs5c372_rtc_set_time(struct device *dev, struct rtc_time *tm)
-{
- return rs5c372_set_datetime(to_i2c_client(dev), tm);
-}
-
-
static int rs5c_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
struct i2c_client *client = to_i2c_client(dev);
@@ -581,7 +571,6 @@ static int rs5c372_probe(struct i2c_client *client,
int err = 0;
int smbus_mode = 0;
struct rs5c372 *rs5c372;
- struct rtc_time tm;
dev_dbg(&client->dev, "%s\n", __func__);
@@ -662,9 +651,6 @@ static int rs5c372_probe(struct i2c_client *client,
goto exit;
}
- if (rs5c372_get_datetime(client, &tm) < 0)
- dev_warn(&client->dev, "clock needs to be set\n");
-
dev_info(&client->dev, "%s found, %s\n",
({ char *s; switch (rs5c372->type) {
case rtc_r2025sd: s = "r2025sd"; break;
diff --git a/drivers/rtc/rtc-rv8803.c b/drivers/rtc/rtc-rv8803.c
index aae2576741a6..29fc3d210392 100644
--- a/drivers/rtc/rtc-rv8803.c
+++ b/drivers/rtc/rtc-rv8803.c
@@ -68,7 +68,6 @@ struct rv8803_data {
struct mutex flags_lock;
u8 ctrl;
enum rv8803_type type;
- struct nvmem_config nvmem_cfg;
};
static int rv8803_read_reg(const struct i2c_client *client, u8 reg)
@@ -528,6 +527,15 @@ static int rv8803_probe(struct i2c_client *client,
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct rv8803_data *rv8803;
int err, flags;
+ struct nvmem_config nvmem_cfg = {
+ .name = "rv8803_nvram",
+ .word_size = 1,
+ .stride = 1,
+ .size = 1,
+ .reg_read = rv8803_nvram_read,
+ .reg_write = rv8803_nvram_write,
+ .priv = client,
+ };
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_I2C_BLOCK)) {
@@ -582,21 +590,6 @@ static int rv8803_probe(struct i2c_client *client,
}
}
- rv8803->nvmem_cfg.name = "rv8803_nvram",
- rv8803->nvmem_cfg.word_size = 1,
- rv8803->nvmem_cfg.stride = 1,
- rv8803->nvmem_cfg.size = 1,
- rv8803->nvmem_cfg.reg_read = rv8803_nvram_read,
- rv8803->nvmem_cfg.reg_write = rv8803_nvram_write,
- rv8803->nvmem_cfg.priv = client;
-
- rv8803->rtc->ops = &rv8803_rtc_ops;
- rv8803->rtc->nvmem_config = &rv8803->nvmem_cfg;
- rv8803->rtc->nvram_old_abi = true;
- err = rtc_register_device(rv8803->rtc);
- if (err)
- return err;
-
err = rv8803_write_reg(rv8803->client, RV8803_EXT, RV8803_EXT_WADA);
if (err)
return err;
@@ -607,6 +600,14 @@ static int rv8803_probe(struct i2c_client *client,
return err;
}
+ rv8803->rtc->ops = &rv8803_rtc_ops;
+ rv8803->rtc->nvram_old_abi = true;
+ err = rtc_register_device(rv8803->rtc);
+ if (err)
+ return err;
+
+ rtc_nvmem_register(rv8803->rtc, &nvmem_cfg);
+
rv8803->rtc->max_user_freq = 1;
return 0;
diff --git a/drivers/rtc/rtc-rx4581.c b/drivers/rtc/rtc-rx4581.c
index de3fe4f8d133..c59a218bdd87 100644
--- a/drivers/rtc/rtc-rx4581.c
+++ b/drivers/rtc/rtc-rx4581.c
@@ -172,11 +172,7 @@ static int rx4581_get_datetime(struct device *dev, struct rtc_time *tm)
tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
- err = rtc_valid_tm(tm);
- if (err < 0)
- dev_err(dev, "retrieved date/time is not valid.\n");
-
- return err;
+ return 0;
}
static int rx4581_set_datetime(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-rx6110.c b/drivers/rtc/rtc-rx6110.c
index 7c9c08eab5e5..8e322d884cc2 100644
--- a/drivers/rtc/rtc-rx6110.c
+++ b/drivers/rtc/rtc-rx6110.c
@@ -252,7 +252,7 @@ static int rx6110_get_time(struct device *dev, struct rtc_time *tm)
tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon, tm->tm_year);
- return rtc_valid_tm(tm);
+ return 0;
}
static const struct reg_sequence rx6110_default_regs[] = {
diff --git a/drivers/rtc/rtc-rx8010.c b/drivers/rtc/rtc-rx8010.c
index 5c5938ab3d86..7ddc22eb5b0f 100644
--- a/drivers/rtc/rtc-rx8010.c
+++ b/drivers/rtc/rtc-rx8010.c
@@ -138,7 +138,7 @@ static int rx8010_get_time(struct device *dev, struct rtc_time *dt)
dt->tm_year = bcd2bin(date[RX8010_YEAR - RX8010_SEC]) + 100;
dt->tm_wday = ffs(date[RX8010_WDAY - RX8010_SEC] & 0x7f);
- return rtc_valid_tm(dt);
+ return 0;
}
static int rx8010_set_time(struct device *dev, struct rtc_time *dt)
diff --git a/drivers/rtc/rtc-rx8025.c b/drivers/rtc/rtc-rx8025.c
index 91857d8d2df8..41127adf5765 100644
--- a/drivers/rtc/rtc-rx8025.c
+++ b/drivers/rtc/rtc-rx8025.c
@@ -214,7 +214,7 @@ static int rx8025_get_time(struct device *dev, struct rtc_time *dt)
dt->tm_sec, dt->tm_min, dt->tm_hour,
dt->tm_mday, dt->tm_mon, dt->tm_year);
- return rtc_valid_tm(dt);
+ return 0;
}
static int rx8025_set_time(struct device *dev, struct rtc_time *dt)
diff --git a/drivers/rtc/rtc-rx8581.c b/drivers/rtc/rtc-rx8581.c
index 9998d7937688..32caadf912ca 100644
--- a/drivers/rtc/rtc-rx8581.c
+++ b/drivers/rtc/rtc-rx8581.c
@@ -164,11 +164,7 @@ static int rx8581_get_datetime(struct i2c_client *client, struct rtc_time *tm)
tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
- err = rtc_valid_tm(tm);
- if (err < 0)
- dev_err(&client->dev, "retrieved date/time is not valid.\n");
-
- return err;
+ return 0;
}
static int rx8581_set_datetime(struct i2c_client *client, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-s35390a.c b/drivers/rtc/rtc-s35390a.c
index 7067bca5c20d..77feb603cd4c 100644
--- a/drivers/rtc/rtc-s35390a.c
+++ b/drivers/rtc/rtc-s35390a.c
@@ -210,8 +210,9 @@ static int s35390a_reg2hr(struct s35390a *s35390a, char reg)
return hour;
}
-static int s35390a_set_datetime(struct i2c_client *client, struct rtc_time *tm)
+static int s35390a_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct s35390a *s35390a = i2c_get_clientdata(client);
int i, err;
char buf[7], status;
@@ -241,8 +242,9 @@ static int s35390a_set_datetime(struct i2c_client *client, struct rtc_time *tm)
return err;
}
-static int s35390a_get_datetime(struct i2c_client *client, struct rtc_time *tm)
+static int s35390a_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct s35390a *s35390a = i2c_get_clientdata(client);
char buf[7], status;
int i, err;
@@ -271,11 +273,12 @@ static int s35390a_get_datetime(struct i2c_client *client, struct rtc_time *tm)
tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year,
tm->tm_wday);
- return rtc_valid_tm(tm);
+ return 0;
}
-static int s35390a_set_alarm(struct i2c_client *client, struct rtc_wkalrm *alm)
+static int s35390a_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct s35390a *s35390a = i2c_get_clientdata(client);
char buf[3], sts = 0;
int err, i;
@@ -329,8 +332,9 @@ static int s35390a_set_alarm(struct i2c_client *client, struct rtc_wkalrm *alm)
return err;
}
-static int s35390a_read_alarm(struct i2c_client *client, struct rtc_wkalrm *alm)
+static int s35390a_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct s35390a *s35390a = i2c_get_clientdata(client);
char buf[3], sts;
int i, err;
@@ -384,26 +388,6 @@ static int s35390a_read_alarm(struct i2c_client *client, struct rtc_wkalrm *alm)
return 0;
}
-static int s35390a_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
-{
- return s35390a_read_alarm(to_i2c_client(dev), alm);
-}
-
-static int s35390a_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
-{
- return s35390a_set_alarm(to_i2c_client(dev), alm);
-}
-
-static int s35390a_rtc_read_time(struct device *dev, struct rtc_time *tm)
-{
- return s35390a_get_datetime(to_i2c_client(dev), tm);
-}
-
-static int s35390a_rtc_set_time(struct device *dev, struct rtc_time *tm)
-{
- return s35390a_set_datetime(to_i2c_client(dev), tm);
-}
-
static int s35390a_rtc_ioctl(struct device *dev, unsigned int cmd,
unsigned long arg)
{
@@ -450,7 +434,6 @@ static int s35390a_probe(struct i2c_client *client,
int err, err_read;
unsigned int i;
struct s35390a *s35390a;
- struct rtc_time tm;
char buf, status1;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
@@ -508,9 +491,6 @@ static int s35390a_probe(struct i2c_client *client,
}
}
- if (err_read > 0 || s35390a_get_datetime(client, &tm) < 0)
- dev_warn(&client->dev, "clock needs to be set\n");
-
device_set_wakeup_capable(&client->dev, 1);
s35390a->rtc = devm_rtc_device_register(&client->dev,
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c
index a8992c227f61..75c8c5033e08 100644
--- a/drivers/rtc/rtc-s3c.c
+++ b/drivers/rtc/rtc-s3c.c
@@ -232,7 +232,7 @@ retry_get_time:
rtc_tm->tm_mon -= 1;
- return rtc_valid_tm(rtc_tm);
+ return 0;
}
static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-s5m.c b/drivers/rtc/rtc-s5m.c
index 0477678d968f..8428455432ca 100644
--- a/drivers/rtc/rtc-s5m.c
+++ b/drivers/rtc/rtc-s5m.c
@@ -38,6 +38,19 @@
*/
#define UDR_READ_RETRY_CNT 5
+enum {
+ RTC_SEC = 0,
+ RTC_MIN,
+ RTC_HOUR,
+ RTC_WEEKDAY,
+ RTC_DATE,
+ RTC_MONTH,
+ RTC_YEAR1,
+ RTC_YEAR2,
+ /* Make sure this is always the last enum name. */
+ RTC_MAX_NUM_TIME_REGS
+};
+
/*
* Registers used by the driver which are different between chipsets.
*
@@ -367,7 +380,7 @@ static void s5m8763_tm_to_data(struct rtc_time *tm, u8 *data)
static int s5m_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct s5m_rtc_info *info = dev_get_drvdata(dev);
- u8 data[info->regs->regs_count];
+ u8 data[RTC_MAX_NUM_TIME_REGS];
int ret;
if (info->regs->read_time_udr_mask) {
@@ -407,13 +420,13 @@ static int s5m_rtc_read_time(struct device *dev, struct rtc_time *tm)
1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_wday);
- return rtc_valid_tm(tm);
+ return 0;
}
static int s5m_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct s5m_rtc_info *info = dev_get_drvdata(dev);
- u8 data[info->regs->regs_count];
+ u8 data[RTC_MAX_NUM_TIME_REGS];
int ret = 0;
switch (info->device_type) {
@@ -450,7 +463,7 @@ static int s5m_rtc_set_time(struct device *dev, struct rtc_time *tm)
static int s5m_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct s5m_rtc_info *info = dev_get_drvdata(dev);
- u8 data[info->regs->regs_count];
+ u8 data[RTC_MAX_NUM_TIME_REGS];
unsigned int val;
int ret, i;
@@ -500,7 +513,7 @@ static int s5m_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
static int s5m_rtc_stop_alarm(struct s5m_rtc_info *info)
{
- u8 data[info->regs->regs_count];
+ u8 data[RTC_MAX_NUM_TIME_REGS];
int ret, i;
struct rtc_time tm;
@@ -545,7 +558,7 @@ static int s5m_rtc_stop_alarm(struct s5m_rtc_info *info)
static int s5m_rtc_start_alarm(struct s5m_rtc_info *info)
{
int ret;
- u8 data[info->regs->regs_count];
+ u8 data[RTC_MAX_NUM_TIME_REGS];
u8 alarm0_conf;
struct rtc_time tm;
@@ -598,7 +611,7 @@ static int s5m_rtc_start_alarm(struct s5m_rtc_info *info)
static int s5m_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct s5m_rtc_info *info = dev_get_drvdata(dev);
- u8 data[info->regs->regs_count];
+ u8 data[RTC_MAX_NUM_TIME_REGS];
int ret;
switch (info->device_type) {
diff --git a/drivers/rtc/rtc-sc27xx.c b/drivers/rtc/rtc-sc27xx.c
index d544d5268757..00d87d138984 100644
--- a/drivers/rtc/rtc-sc27xx.c
+++ b/drivers/rtc/rtc-sc27xx.c
@@ -376,7 +376,7 @@ static int sprd_rtc_read_time(struct device *dev, struct rtc_time *tm)
return ret;
rtc_time64_to_tm(secs, tm);
- return rtc_valid_tm(tm);
+ return 0;
}
static int sprd_rtc_set_time(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c
index 6c2d3989f967..4e8ab370ce63 100644
--- a/drivers/rtc/rtc-sh.c
+++ b/drivers/rtc/rtc-sh.c
@@ -414,7 +414,7 @@ static int sh_rtc_read_time(struct device *dev, struct rtc_time *tm)
tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon + 1, tm->tm_year, tm->tm_wday);
- return rtc_valid_tm(tm);
+ return 0;
}
static int sh_rtc_set_time(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-sirfsoc.c b/drivers/rtc/rtc-sirfsoc.c
index 7367f617145c..2a9e151cae99 100644
--- a/drivers/rtc/rtc-sirfsoc.c
+++ b/drivers/rtc/rtc-sirfsoc.c
@@ -204,23 +204,6 @@ static int sirfsoc_rtc_set_time(struct device *dev,
return 0;
}
-static int sirfsoc_rtc_ioctl(struct device *dev, unsigned int cmd,
- unsigned long arg)
-{
- switch (cmd) {
- case RTC_PIE_ON:
- case RTC_PIE_OFF:
- case RTC_UIE_ON:
- case RTC_UIE_OFF:
- case RTC_AIE_ON:
- case RTC_AIE_OFF:
- return 0;
-
- default:
- return -ENOIOCTLCMD;
- }
-}
-
static int sirfsoc_rtc_alarm_irq_enable(struct device *dev,
unsigned int enabled)
{
@@ -250,7 +233,6 @@ static const struct rtc_class_ops sirfsoc_rtc_ops = {
.set_time = sirfsoc_rtc_set_time,
.read_alarm = sirfsoc_rtc_read_alarm,
.set_alarm = sirfsoc_rtc_set_alarm,
- .ioctl = sirfsoc_rtc_ioctl,
.alarm_irq_enable = sirfsoc_rtc_alarm_irq_enable
};
diff --git a/drivers/rtc/rtc-snvs.c b/drivers/rtc/rtc-snvs.c
index d8ef9e052c4f..9af591d5223c 100644
--- a/drivers/rtc/rtc-snvs.c
+++ b/drivers/rtc/rtc-snvs.c
@@ -132,20 +132,23 @@ static int snvs_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct snvs_rtc_data *data = dev_get_drvdata(dev);
unsigned long time;
+ int ret;
rtc_tm_to_time(tm, &time);
/* Disable RTC first */
- snvs_rtc_enable(data, false);
+ ret = snvs_rtc_enable(data, false);
+ if (ret)
+ return ret;
/* Write 32-bit time to 47-bit timer, leaving 15 LSBs blank */
regmap_write(data->regmap, data->offset + SNVS_LPSRTCLR, time << CNTR_TO_SECS_SH);
regmap_write(data->regmap, data->offset + SNVS_LPSRTCMR, time >> (32 - CNTR_TO_SECS_SH));
/* Enable RTC again */
- snvs_rtc_enable(data, true);
+ ret = snvs_rtc_enable(data, true);
- return 0;
+ return ret;
}
static int snvs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
@@ -288,7 +291,11 @@ static int snvs_rtc_probe(struct platform_device *pdev)
regmap_write(data->regmap, data->offset + SNVS_LPSR, 0xffffffff);
/* Enable RTC */
- snvs_rtc_enable(data, true);
+ ret = snvs_rtc_enable(data, true);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable rtc %d\n", ret);
+ goto error_rtc_device_register;
+ }
device_init_wakeup(&pdev->dev, true);
diff --git a/drivers/rtc/rtc-spear.c b/drivers/rtc/rtc-spear.c
index e377f42abae7..0567944fd4f8 100644
--- a/drivers/rtc/rtc-spear.c
+++ b/drivers/rtc/rtc-spear.c
@@ -170,18 +170,14 @@ static irqreturn_t spear_rtc_irq(int irq, void *dev_id)
}
-static int tm2bcd(struct rtc_time *tm)
+static void tm2bcd(struct rtc_time *tm)
{
- if (rtc_valid_tm(tm) != 0)
- return -EINVAL;
tm->tm_sec = bin2bcd(tm->tm_sec);
tm->tm_min = bin2bcd(tm->tm_min);
tm->tm_hour = bin2bcd(tm->tm_hour);
tm->tm_mday = bin2bcd(tm->tm_mday);
tm->tm_mon = bin2bcd(tm->tm_mon + 1);
tm->tm_year = bin2bcd(tm->tm_year);
-
- return 0;
}
static void bcd2tm(struct rtc_time *tm)
@@ -237,8 +233,7 @@ static int spear_rtc_set_time(struct device *dev, struct rtc_time *tm)
struct spear_rtc_config *config = dev_get_drvdata(dev);
unsigned int time, date;
- if (tm2bcd(tm) < 0)
- return -EINVAL;
+ tm2bcd(tm);
rtc_wait_not_busy(config);
time = (tm->tm_sec << SECOND_SHIFT) | (tm->tm_min << MINUTE_SHIFT) |
@@ -295,8 +290,7 @@ static int spear_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
unsigned int time, date;
int err;
- if (tm2bcd(&alm->time) < 0)
- return -EINVAL;
+ tm2bcd(&alm->time);
rtc_wait_not_busy(config);
diff --git a/drivers/rtc/rtc-st-lpc.c b/drivers/rtc/rtc-st-lpc.c
index 82b0af159a28..d5222667f892 100644
--- a/drivers/rtc/rtc-st-lpc.c
+++ b/drivers/rtc/rtc-st-lpc.c
@@ -195,7 +195,6 @@ static int st_rtc_probe(struct platform_device *pdev)
struct device_node *np = pdev->dev.of_node;
struct st_rtc *rtc;
struct resource *res;
- struct rtc_time tm_check;
uint32_t mode;
int ret = 0;
@@ -254,21 +253,6 @@ static int st_rtc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, rtc);
- /*
- * The RTC-LPC is able to manage date.year > 2038
- * but currently the kernel can not manage this date!
- * If the RTC-LPC has a date.year > 2038 then
- * it's set to the epoch "Jan 1st 2000"
- */
- st_rtc_read_time(&pdev->dev, &tm_check);
-
- if (tm_check.tm_year >= (2038 - 1900)) {
- memset(&tm_check, 0, sizeof(tm_check));
- tm_check.tm_year = 100;
- tm_check.tm_mday = 1;
- st_rtc_set_time(&pdev->dev, &tm_check);
- }
-
rtc->rtc_dev = rtc_device_register("st-lpc-rtc", &pdev->dev,
&st_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc->rtc_dev)) {
diff --git a/drivers/rtc/rtc-starfire.c b/drivers/rtc/rtc-starfire.c
index 7fc36973fa33..a7d49329d626 100644
--- a/drivers/rtc/rtc-starfire.c
+++ b/drivers/rtc/rtc-starfire.c
@@ -28,7 +28,7 @@ static u32 starfire_get_time(void)
static int starfire_read_time(struct device *dev, struct rtc_time *tm)
{
rtc_time_to_tm(starfire_get_time(), tm);
- return rtc_valid_tm(tm);
+ return 0;
}
static const struct rtc_class_ops starfire_rtc_ops = {
diff --git a/drivers/rtc/rtc-stk17ta8.c b/drivers/rtc/rtc-stk17ta8.c
index a456cb6177ea..e70b78d17a98 100644
--- a/drivers/rtc/rtc-stk17ta8.c
+++ b/drivers/rtc/rtc-stk17ta8.c
@@ -129,10 +129,6 @@ static int stk17ta8_rtc_read_time(struct device *dev, struct rtc_time *tm)
/* year is 1900 + tm->tm_year */
tm->tm_year = bcd2bin(year) + bcd2bin(century) * 100 - 1900;
- if (rtc_valid_tm(tm) < 0) {
- dev_err(dev, "retrieved date/time is not valid.\n");
- rtc_time_to_tm(0, tm);
- }
return 0;
}
@@ -242,46 +238,30 @@ static const struct rtc_class_ops stk17ta8_rtc_ops = {
.alarm_irq_enable = stk17ta8_rtc_alarm_irq_enable,
};
-static ssize_t stk17ta8_nvram_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *attr, char *buf,
- loff_t pos, size_t size)
+static int stk17ta8_nvram_read(void *priv, unsigned int pos, void *val,
+ size_t bytes)
{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct platform_device *pdev = to_platform_device(dev);
- struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ struct rtc_plat_data *pdata = priv;
void __iomem *ioaddr = pdata->ioaddr;
- ssize_t count;
+ u8 *buf = val;
- for (count = 0; count < size; count++)
+ for (; bytes; bytes--)
*buf++ = readb(ioaddr + pos++);
- return count;
+ return 0;
}
-static ssize_t stk17ta8_nvram_write(struct file *filp, struct kobject *kobj,
- struct bin_attribute *attr, char *buf,
- loff_t pos, size_t size)
+static int stk17ta8_nvram_write(void *priv, unsigned int pos, void *val,
+ size_t bytes)
{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct platform_device *pdev = to_platform_device(dev);
- struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ struct rtc_plat_data *pdata = priv;
void __iomem *ioaddr = pdata->ioaddr;
- ssize_t count;
+ u8 *buf = val;
- for (count = 0; count < size; count++)
+ for (; bytes; bytes--)
writeb(*buf++, ioaddr + pos++);
- return count;
+ return 0;
}
-static struct bin_attribute stk17ta8_nvram_attr = {
- .attr = {
- .name = "nvram",
- .mode = S_IRUGO | S_IWUSR,
- },
- .size = RTC_OFFSET,
- .read = stk17ta8_nvram_read,
- .write = stk17ta8_nvram_write,
-};
-
static int stk17ta8_rtc_probe(struct platform_device *pdev)
{
struct resource *res;
@@ -290,6 +270,14 @@ static int stk17ta8_rtc_probe(struct platform_device *pdev)
struct rtc_plat_data *pdata;
void __iomem *ioaddr;
int ret = 0;
+ struct nvmem_config nvmem_cfg = {
+ .name = "stk17ta8_nvram",
+ .word_size = 1,
+ .stride = 1,
+ .size = RTC_OFFSET,
+ .reg_read = stk17ta8_nvram_read,
+ .reg_write = stk17ta8_nvram_write,
+ };
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
@@ -328,24 +316,19 @@ static int stk17ta8_rtc_probe(struct platform_device *pdev)
}
}
- pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
- &stk17ta8_rtc_ops, THIS_MODULE);
+ pdata->rtc = devm_rtc_allocate_device(&pdev->dev);
if (IS_ERR(pdata->rtc))
return PTR_ERR(pdata->rtc);
- ret = sysfs_create_bin_file(&pdev->dev.kobj, &stk17ta8_nvram_attr);
+ pdata->rtc->ops = &stk17ta8_rtc_ops;
+ pdata->rtc->nvram_old_abi = true;
- return ret;
-}
+ nvmem_cfg.priv = pdata;
+ ret = rtc_nvmem_register(pdata->rtc, &nvmem_cfg);
+ if (ret)
+ return ret;
-static int stk17ta8_rtc_remove(struct platform_device *pdev)
-{
- struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
-
- sysfs_remove_bin_file(&pdev->dev.kobj, &stk17ta8_nvram_attr);
- if (pdata->irq > 0)
- writeb(0, pdata->ioaddr + RTC_INTERRUPTS);
- return 0;
+ return rtc_register_device(pdata->rtc);
}
/* work with hotplug and coldplug */
@@ -353,7 +336,6 @@ MODULE_ALIAS("platform:stk17ta8");
static struct platform_driver stk17ta8_rtc_driver = {
.probe = stk17ta8_rtc_probe,
- .remove = stk17ta8_rtc_remove,
.driver = {
.name = "stk17ta8",
},
diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c
index 5bc28eed1adf..2e6fb275acc8 100644
--- a/drivers/rtc/rtc-sun6i.c
+++ b/drivers/rtc/rtc-sun6i.c
@@ -349,7 +349,7 @@ static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
*/
rtc_tm->tm_year += SUN6I_YEAR_OFF;
- return rtc_valid_tm(rtc_tm);
+ return 0;
}
static int sun6i_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
diff --git a/drivers/rtc/rtc-sunxi.c b/drivers/rtc/rtc-sunxi.c
index abada609ddc7..dadbf8b324ad 100644
--- a/drivers/rtc/rtc-sunxi.c
+++ b/drivers/rtc/rtc-sunxi.c
@@ -261,7 +261,7 @@ static int sunxi_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
*/
rtc_tm->tm_year += SUNXI_YEAR_OFF(chip->data_year);
- return rtc_valid_tm(rtc_tm);
+ return 0;
}
static int sunxi_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c
index 92ff2edb86a6..454da38c6012 100644
--- a/drivers/rtc/rtc-sysfs.c
+++ b/drivers/rtc/rtc-sysfs.c
@@ -248,6 +248,14 @@ offset_store(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR_RW(offset);
+static ssize_t
+range_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "[%lld,%llu]\n", to_rtc_device(dev)->range_min,
+ to_rtc_device(dev)->range_max);
+}
+static DEVICE_ATTR_RO(range);
+
static struct attribute *rtc_attrs[] = {
&dev_attr_name.attr,
&dev_attr_date.attr,
@@ -257,6 +265,7 @@ static struct attribute *rtc_attrs[] = {
&dev_attr_hctosys.attr,
&dev_attr_wakealarm.attr,
&dev_attr_offset.attr,
+ &dev_attr_range.attr,
NULL,
};
@@ -286,6 +295,9 @@ static umode_t rtc_attr_is_visible(struct kobject *kobj,
} else if (attr == &dev_attr_offset.attr) {
if (!rtc->ops->set_offset)
mode = 0;
+ } else if (attr == &dev_attr_range.attr) {
+ if (!(rtc->range_max - rtc->range_min))
+ mode = 0;
}
return mode;
diff --git a/drivers/rtc/rtc-tegra.c b/drivers/rtc/rtc-tegra.c
index d30d57b048d3..66efff60c4d5 100644
--- a/drivers/rtc/rtc-tegra.c
+++ b/drivers/rtc/rtc-tegra.c
@@ -144,10 +144,6 @@ static int tegra_rtc_set_time(struct device *dev, struct rtc_time *tm)
int ret;
/* convert tm to seconds. */
- ret = rtc_valid_tm(tm);
- if (ret)
- return ret;
-
rtc_tm_to_time(tm, &sec);
dev_vdbg(dev, "time set to %lu. %d/%d/%d %d:%02u:%02u\n",
diff --git a/drivers/rtc/rtc-tile.c b/drivers/rtc/rtc-tile.c
deleted file mode 100644
index 0b60867d8390..000000000000
--- a/drivers/rtc/rtc-tile.c
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright 2011 Tilera Corporation. All Rights Reserved.
- *
- * 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 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, GOOD TITLE or
- * NON INFRINGEMENT. See the GNU General Public License for
- * more details.
- *
- * Tilera-specific RTC driver.
- */
-
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/rtc.h>
-#include <linux/platform_device.h>
-
-/* Platform device pointer. */
-static struct platform_device *tile_rtc_platform_device;
-
-/*
- * RTC read routine. Gets time info from RTC chip via hypervisor syscall.
- */
-static int read_rtc_time(struct device *dev, struct rtc_time *tm)
-{
- HV_RTCTime hvtm = hv_get_rtc();
-
- tm->tm_sec = hvtm.tm_sec;
- tm->tm_min = hvtm.tm_min;
- tm->tm_hour = hvtm.tm_hour;
- tm->tm_mday = hvtm.tm_mday;
- tm->tm_mon = hvtm.tm_mon;
- tm->tm_year = hvtm.tm_year;
- tm->tm_wday = 0;
- tm->tm_yday = 0;
- tm->tm_isdst = 0;
-
- if (rtc_valid_tm(tm) < 0)
- dev_warn(dev, "Read invalid date/time from RTC\n");
-
- return 0;
-}
-
-/*
- * RTC write routine. Sends time info to hypervisor via syscall, to be
- * written to RTC chip.
- */
-static int set_rtc_time(struct device *dev, struct rtc_time *tm)
-{
- HV_RTCTime hvtm;
-
- hvtm.tm_sec = tm->tm_sec;
- hvtm.tm_min = tm->tm_min;
- hvtm.tm_hour = tm->tm_hour;
- hvtm.tm_mday = tm->tm_mday;
- hvtm.tm_mon = tm->tm_mon;
- hvtm.tm_year = tm->tm_year;
-
- hv_set_rtc(hvtm);
-
- return 0;
-}
-
-/*
- * RTC read/write ops.
- */
-static const struct rtc_class_ops tile_rtc_ops = {
- .read_time = read_rtc_time,
- .set_time = set_rtc_time,
-};
-
-/*
- * Device probe routine.
- */
-static int tile_rtc_probe(struct platform_device *dev)
-{
- struct rtc_device *rtc;
-
- rtc = devm_rtc_device_register(&dev->dev, "tile",
- &tile_rtc_ops, THIS_MODULE);
-
- if (IS_ERR(rtc))
- return PTR_ERR(rtc);
-
- platform_set_drvdata(dev, rtc);
-
- return 0;
-}
-
-static struct platform_driver tile_rtc_platform_driver = {
- .driver = {
- .name = "rtc-tile",
- },
- .probe = tile_rtc_probe,
-};
-
-/*
- * Driver init routine.
- */
-static int __init tile_rtc_driver_init(void)
-{
- int err;
-
- err = platform_driver_register(&tile_rtc_platform_driver);
- if (err)
- return err;
-
- tile_rtc_platform_device = platform_device_alloc("rtc-tile", 0);
- if (tile_rtc_platform_device == NULL) {
- err = -ENOMEM;
- goto exit_driver_unregister;
- }
-
- err = platform_device_add(tile_rtc_platform_device);
- if (err)
- goto exit_device_put;
-
- return 0;
-
-exit_device_put:
- platform_device_put(tile_rtc_platform_device);
-
-exit_driver_unregister:
- platform_driver_unregister(&tile_rtc_platform_driver);
- return err;
-}
-
-/*
- * Driver cleanup routine.
- */
-static void __exit tile_rtc_driver_exit(void)
-{
- platform_device_unregister(tile_rtc_platform_device);
- platform_driver_unregister(&tile_rtc_platform_driver);
-}
-
-module_init(tile_rtc_driver_init);
-module_exit(tile_rtc_driver_exit);
-
-MODULE_DESCRIPTION("Tilera-specific Real Time Clock Driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:rtc-tile");
diff --git a/drivers/rtc/rtc-tps6586x.c b/drivers/rtc/rtc-tps6586x.c
index a3418a8a3796..d7785ae0a2b4 100644
--- a/drivers/rtc/rtc-tps6586x.c
+++ b/drivers/rtc/rtc-tps6586x.c
@@ -90,7 +90,7 @@ static int tps6586x_rtc_read_time(struct device *dev, struct rtc_time *tm)
seconds = ticks >> 10;
seconds += rtc->epoch_start;
rtc_time_to_tm(seconds, tm);
- return rtc_valid_tm(tm);
+ return 0;
}
static int tps6586x_rtc_set_time(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-tx4939.c b/drivers/rtc/rtc-tx4939.c
index 560d9a5e0225..08dbefc79520 100644
--- a/drivers/rtc/rtc-tx4939.c
+++ b/drivers/rtc/rtc-tx4939.c
@@ -14,7 +14,30 @@
#include <linux/module.h>
#include <linux/io.h>
#include <linux/gfp.h>
-#include <asm/txx9/tx4939.h>
+
+#define TX4939_RTCCTL_ALME 0x00000080
+#define TX4939_RTCCTL_ALMD 0x00000040
+#define TX4939_RTCCTL_BUSY 0x00000020
+
+#define TX4939_RTCCTL_COMMAND 0x00000007
+#define TX4939_RTCCTL_COMMAND_NOP 0x00000000
+#define TX4939_RTCCTL_COMMAND_GETTIME 0x00000001
+#define TX4939_RTCCTL_COMMAND_SETTIME 0x00000002
+#define TX4939_RTCCTL_COMMAND_GETALARM 0x00000003
+#define TX4939_RTCCTL_COMMAND_SETALARM 0x00000004
+
+#define TX4939_RTCTBC_PM 0x00000080
+#define TX4939_RTCTBC_COMP 0x0000007f
+
+#define TX4939_RTC_REG_RAMSIZE 0x00000100
+#define TX4939_RTC_REG_RWBSIZE 0x00000006
+
+struct tx4939_rtc_reg {
+ __u32 ctl;
+ __u32 adr;
+ __u32 dat;
+ __u32 tbc;
+};
struct tx4939rtc_plat_data {
struct rtc_device *rtc;
@@ -86,9 +109,10 @@ static int tx4939_rtc_read_time(struct device *dev, struct rtc_time *tm)
for (i = 2; i < 6; i++)
buf[i] = __raw_readl(&rtcreg->dat);
spin_unlock_irq(&pdata->lock);
- sec = (buf[5] << 24) | (buf[4] << 16) | (buf[3] << 8) | buf[2];
+ sec = ((unsigned long)buf[5] << 24) | (buf[4] << 16) |
+ (buf[3] << 8) | buf[2];
rtc_time_to_tm(sec, tm);
- return rtc_valid_tm(tm);
+ return 0;
}
static int tx4939_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
@@ -147,7 +171,8 @@ static int tx4939_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
alrm->enabled = (ctl & TX4939_RTCCTL_ALME) ? 1 : 0;
alrm->pending = (ctl & TX4939_RTCCTL_ALMD) ? 1 : 0;
spin_unlock_irq(&pdata->lock);
- sec = (buf[5] << 24) | (buf[4] << 16) | (buf[3] << 8) | buf[2];
+ sec = ((unsigned long)buf[5] << 24) | (buf[4] << 16) |
+ (buf[3] << 8) | buf[2];
rtc_time_to_tm(sec, &alrm->time);
return rtc_valid_tm(&alrm->time);
}
@@ -189,58 +214,52 @@ static const struct rtc_class_ops tx4939_rtc_ops = {
.alarm_irq_enable = tx4939_rtc_alarm_irq_enable,
};
-static ssize_t tx4939_rtc_nvram_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
- char *buf, loff_t pos, size_t size)
+static int tx4939_nvram_read(void *priv, unsigned int pos, void *val,
+ size_t bytes)
{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct tx4939rtc_plat_data *pdata = get_tx4939rtc_plat_data(dev);
+ struct tx4939rtc_plat_data *pdata = priv;
struct tx4939_rtc_reg __iomem *rtcreg = pdata->rtcreg;
- ssize_t count;
+ u8 *buf = val;
spin_lock_irq(&pdata->lock);
- for (count = 0; count < size; count++) {
+ for (; bytes; bytes--) {
__raw_writel(pos++, &rtcreg->adr);
*buf++ = __raw_readl(&rtcreg->dat);
}
spin_unlock_irq(&pdata->lock);
- return count;
+ return 0;
}
-static ssize_t tx4939_rtc_nvram_write(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
- char *buf, loff_t pos, size_t size)
+static int tx4939_nvram_write(void *priv, unsigned int pos, void *val,
+ size_t bytes)
{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct tx4939rtc_plat_data *pdata = get_tx4939rtc_plat_data(dev);
+ struct tx4939rtc_plat_data *pdata = priv;
struct tx4939_rtc_reg __iomem *rtcreg = pdata->rtcreg;
- ssize_t count;
+ u8 *buf = val;
spin_lock_irq(&pdata->lock);
- for (count = 0; count < size; count++) {
+ for (; bytes; bytes--) {
__raw_writel(pos++, &rtcreg->adr);
__raw_writel(*buf++, &rtcreg->dat);
}
spin_unlock_irq(&pdata->lock);
- return count;
+ return 0;
}
-static struct bin_attribute tx4939_rtc_nvram_attr = {
- .attr = {
- .name = "nvram",
- .mode = S_IRUGO | S_IWUSR,
- },
- .size = TX4939_RTC_REG_RAMSIZE,
- .read = tx4939_rtc_nvram_read,
- .write = tx4939_rtc_nvram_write,
-};
-
static int __init tx4939_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;
struct tx4939rtc_plat_data *pdata;
struct resource *res;
int irq, ret;
+ struct nvmem_config nvmem_cfg = {
+ .name = "rv8803_nvram",
+ .word_size = 4,
+ .stride = 4,
+ .size = TX4939_RTC_REG_RAMSIZE,
+ .reg_read = tx4939_nvram_read,
+ .reg_write = tx4939_nvram_write,
+ };
irq = platform_get_irq(pdev, 0);
if (irq < 0)
@@ -260,21 +279,27 @@ static int __init tx4939_rtc_probe(struct platform_device *pdev)
if (devm_request_irq(&pdev->dev, irq, tx4939_rtc_interrupt,
0, pdev->name, &pdev->dev) < 0)
return -EBUSY;
- rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
- &tx4939_rtc_ops, THIS_MODULE);
+ rtc = devm_rtc_allocate_device(&pdev->dev);
if (IS_ERR(rtc))
return PTR_ERR(rtc);
+
+ rtc->ops = &tx4939_rtc_ops;
+ rtc->nvram_old_abi = true;
+
pdata->rtc = rtc;
- ret = sysfs_create_bin_file(&pdev->dev.kobj, &tx4939_rtc_nvram_attr);
- return ret;
+ nvmem_cfg.priv = pdata;
+ ret = rtc_nvmem_register(rtc, &nvmem_cfg);
+ if (ret)
+ return ret;
+
+ return rtc_register_device(rtc);
}
static int __exit tx4939_rtc_remove(struct platform_device *pdev)
{
struct tx4939rtc_plat_data *pdata = platform_get_drvdata(pdev);
- sysfs_remove_bin_file(&pdev->dev.kobj, &tx4939_rtc_nvram_attr);
spin_lock_irq(&pdata->lock);
tx4939_rtc_cmd(pdata->rtcreg, TX4939_RTCCTL_COMMAND_NOP);
spin_unlock_irq(&pdata->lock);
diff --git a/drivers/rtc/rtc-wm831x.c b/drivers/rtc/rtc-wm831x.c
index 75aea4c4d334..7b824dabf104 100644
--- a/drivers/rtc/rtc-wm831x.c
+++ b/drivers/rtc/rtc-wm831x.c
@@ -156,7 +156,7 @@ static int wm831x_rtc_readtime(struct device *dev, struct rtc_time *tm)
u32 time = (time1[0] << 16) | time1[1];
rtc_time_to_tm(time, tm);
- return rtc_valid_tm(tm);
+ return 0;
}
} while (++count < WM831X_GET_TIME_RETRIES);
diff --git a/drivers/rtc/rtc-xgene.c b/drivers/rtc/rtc-xgene.c
index 0c34d3b81279..153820876a82 100644
--- a/drivers/rtc/rtc-xgene.c
+++ b/drivers/rtc/rtc-xgene.c
@@ -60,7 +60,7 @@ static int xgene_rtc_read_time(struct device *dev, struct rtc_time *tm)
struct xgene_rtc_dev *pdata = dev_get_drvdata(dev);
rtc_time_to_tm(readl(pdata->csr_base + RTC_CCVR), tm);
- return rtc_valid_tm(tm);
+ return 0;
}
static int xgene_rtc_set_mmss(struct device *dev, unsigned long secs)
diff --git a/drivers/rtc/rtc-zynqmp.c b/drivers/rtc/rtc-zynqmp.c
index da18a8ae3c1d..fba994dc31eb 100644
--- a/drivers/rtc/rtc-zynqmp.c
+++ b/drivers/rtc/rtc-zynqmp.c
@@ -122,7 +122,7 @@ static int xlnx_rtc_read_time(struct device *dev, struct rtc_time *tm)
rtc_time64_to_tm(read_time, tm);
}
- return rtc_valid_tm(tm);
+ return 0;
}
static int xlnx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
diff --git a/drivers/rtc/systohc.c b/drivers/rtc/systohc.c
index 0c177647ea6c..718293d72426 100644
--- a/drivers/rtc/systohc.c
+++ b/drivers/rtc/systohc.c
@@ -20,7 +20,7 @@
* cases.
*
* -EPROTO is returned if now.tv_nsec is not close enough to *target_nsec.
- (
+ *
* If temporary failure is indicated the caller should try again 'soon'
*/
int rtc_set_ntp_time(struct timespec64 now, unsigned long *target_nsec)
diff --git a/drivers/s390/Makefile b/drivers/s390/Makefile
index 2fdab400c1fe..a863b0462b43 100644
--- a/drivers/s390/Makefile
+++ b/drivers/s390/Makefile
@@ -5,5 +5,5 @@
obj-y += cio/ block/ char/ crypto/ net/ scsi/ virtio/
-drivers-y += drivers/s390/built-in.o
+drivers-y += drivers/s390/built-in.a
diff --git a/drivers/s390/block/Kconfig b/drivers/s390/block/Kconfig
index 1444333210c7..9ac7574e3cfb 100644
--- a/drivers/s390/block/Kconfig
+++ b/drivers/s390/block/Kconfig
@@ -15,8 +15,8 @@ config BLK_DEV_XPRAM
config DCSSBLK
def_tristate m
- select DAX
select FS_DAX_LIMITED
+ select DAX_DRIVER
prompt "DCSSBLK support"
depends on S390 && BLOCK
help
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index ecef8e73d40b..04143c08bd6e 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -3208,7 +3208,7 @@ static void dasd_setup_queue(struct dasd_block *block)
} else {
max = block->base->discipline->max_blocks << block->s2b_shift;
}
- queue_flag_set_unlocked(QUEUE_FLAG_NONROT, q);
+ blk_queue_flag_set(QUEUE_FLAG_NONROT, q);
q->limits.max_dev_sectors = max;
blk_queue_logical_block_size(q, logical_block_size);
blk_queue_max_hw_sectors(q, max);
@@ -3231,7 +3231,7 @@ static void dasd_setup_queue(struct dasd_block *block)
blk_queue_max_discard_sectors(q, max_discard_sectors);
blk_queue_max_write_zeroes_sectors(q, max_discard_sectors);
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q);
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, q);
}
}
@@ -3918,8 +3918,13 @@ static int dasd_generic_requeue_all_requests(struct dasd_device *device)
cqr = refers;
}
- if (cqr->block)
- list_del_init(&cqr->blocklist);
+ /*
+ * _dasd_requeue_request already checked for a valid
+ * blockdevice, no need to check again
+ * all erp requests (cqr->refers) have a cqr->block
+ * pointer copy from the original cqr
+ */
+ list_del_init(&cqr->blocklist);
cqr->block->base->discipline->free_cp(
cqr, (struct request *) cqr->callback_data);
}
diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c
index ee14d8e45c97..ee73b0607e47 100644
--- a/drivers/s390/block/dasd_3990_erp.c
+++ b/drivers/s390/block/dasd_3990_erp.c
@@ -2214,15 +2214,28 @@ static void dasd_3990_erp_disable_path(struct dasd_device *device, __u8 lpum)
{
int pos = pathmask_to_pos(lpum);
+ if (!(device->features & DASD_FEATURE_PATH_AUTODISABLE)) {
+ dev_err(&device->cdev->dev,
+ "Path %x.%02x (pathmask %02x) is operational despite excessive IFCCs\n",
+ device->path[pos].cssid, device->path[pos].chpid, lpum);
+ goto out;
+ }
+
/* no remaining path, cannot disable */
- if (!(dasd_path_get_opm(device) & ~lpum))
- return;
+ if (!(dasd_path_get_opm(device) & ~lpum)) {
+ dev_err(&device->cdev->dev,
+ "Last path %x.%02x (pathmask %02x) is operational despite excessive IFCCs\n",
+ device->path[pos].cssid, device->path[pos].chpid, lpum);
+ goto out;
+ }
dev_err(&device->cdev->dev,
"Path %x.%02x (pathmask %02x) is disabled - IFCC threshold exceeded\n",
device->path[pos].cssid, device->path[pos].chpid, lpum);
dasd_path_remove_opm(device, lpum);
dasd_path_add_ifccpm(device, lpum);
+
+out:
device->path[pos].errorclk = 0;
atomic_set(&device->path[pos].error_count, 0);
}
diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c
index 62f5f04d8f61..5e963fe0e38d 100644
--- a/drivers/s390/block/dasd_alias.c
+++ b/drivers/s390/block/dasd_alias.c
@@ -592,13 +592,22 @@ static int _schedule_lcu_update(struct alias_lcu *lcu,
int dasd_alias_add_device(struct dasd_device *device)
{
struct dasd_eckd_private *private = device->private;
- struct alias_lcu *lcu;
+ __u8 uaddr = private->uid.real_unit_addr;
+ struct alias_lcu *lcu = private->lcu;
unsigned long flags;
int rc;
- lcu = private->lcu;
rc = 0;
spin_lock_irqsave(&lcu->lock, flags);
+ /*
+ * Check if device and lcu type differ. If so, the uac data may be
+ * outdated and needs to be updated.
+ */
+ if (private->uid.type != lcu->uac->unit[uaddr].ua_type) {
+ lcu->flags |= UPDATE_PENDING;
+ DBF_DEV_EVENT(DBF_WARNING, device, "%s",
+ "uid type mismatch - trigger rescan");
+ }
if (!(lcu->flags & UPDATE_PENDING)) {
rc = _add_device_to_lcu(lcu, device, device);
if (rc)
diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c
index e7cd28ff1984..b9ebb565ee2c 100644
--- a/drivers/s390/block/dasd_devmap.c
+++ b/drivers/s390/block/dasd_devmap.c
@@ -1550,9 +1550,49 @@ dasd_path_threshold_store(struct device *dev, struct device_attribute *attr,
dasd_put_device(device);
return count;
}
-
static DEVICE_ATTR(path_threshold, 0644, dasd_path_threshold_show,
dasd_path_threshold_store);
+
+/*
+ * configure if path is disabled after IFCC/CCC error threshold is
+ * exceeded
+ */
+static ssize_t
+dasd_path_autodisable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dasd_devmap *devmap;
+ int flag;
+
+ devmap = dasd_find_busid(dev_name(dev));
+ if (!IS_ERR(devmap))
+ flag = (devmap->features & DASD_FEATURE_PATH_AUTODISABLE) != 0;
+ else
+ flag = (DASD_FEATURE_DEFAULT &
+ DASD_FEATURE_PATH_AUTODISABLE) != 0;
+ return snprintf(buf, PAGE_SIZE, flag ? "1\n" : "0\n");
+}
+
+static ssize_t
+dasd_path_autodisable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned int val;
+ int rc;
+
+ if (kstrtouint(buf, 0, &val) || val > 1)
+ return -EINVAL;
+
+ rc = dasd_set_feature(to_ccwdev(dev),
+ DASD_FEATURE_PATH_AUTODISABLE, val);
+
+ return rc ? : count;
+}
+
+static DEVICE_ATTR(path_autodisable, 0644,
+ dasd_path_autodisable_show,
+ dasd_path_autodisable_store);
/*
* interval for IFCC/CCC checks
* meaning time with no IFCC/CCC error before the error counter
@@ -1623,6 +1663,7 @@ static struct attribute * dasd_attrs[] = {
&dev_attr_host_access_count.attr,
&dev_attr_path_masks.attr,
&dev_attr_path_threshold.attr,
+ &dev_attr_path_autodisable.attr,
&dev_attr_path_interval.attr,
&dev_attr_path_reset.attr,
&dev_attr_hpf.attr,
diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c
index f035c2f25d35..131f1989f6f3 100644
--- a/drivers/s390/block/dasd_diag.c
+++ b/drivers/s390/block/dasd_diag.c
@@ -27,7 +27,6 @@
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/vtoc.h>
-#include <asm/diag.h>
#include "dasd_int.h"
#include "dasd_diag.h"
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 29397a9dba68..be208e7adcb4 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -214,24 +214,25 @@ static void set_ch_t(struct ch_t *geo, __u32 cyl, __u8 head)
geo->head |= head;
}
-static int check_XRC(struct ccw1 *ccw, struct DE_eckd_data *data,
+static int set_timestamp(struct ccw1 *ccw, struct DE_eckd_data *data,
struct dasd_device *device)
{
struct dasd_eckd_private *private = device->private;
int rc;
- if (!private->rdc_data.facilities.XRC_supported)
+ rc = get_phys_clock(&data->ep_sys_time);
+ /*
+ * Ignore return code if XRC is not supported or
+ * sync clock is switched off
+ */
+ if ((rc && !private->rdc_data.facilities.XRC_supported) ||
+ rc == -EOPNOTSUPP || rc == -EACCES)
return 0;
/* switch on System Time Stamp - needed for XRC Support */
data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid' */
data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */
- rc = get_phys_clock(&data->ep_sys_time);
- /* Ignore return code if sync clock is switched off. */
- if (rc == -EOPNOTSUPP || rc == -EACCES)
- rc = 0;
-
if (ccw) {
ccw->count = sizeof(struct DE_eckd_data);
ccw->flags |= CCW_FLAG_SLI;
@@ -286,12 +287,12 @@ define_extent(struct ccw1 *ccw, struct DE_eckd_data *data, unsigned int trk,
case DASD_ECKD_CCW_WRITE_KD_MT:
data->mask.perm = 0x02;
data->attributes.operation = private->attrib.operation;
- rc = check_XRC(ccw, data, device);
+ rc = set_timestamp(ccw, data, device);
break;
case DASD_ECKD_CCW_WRITE_CKD:
case DASD_ECKD_CCW_WRITE_CKD_MT:
data->attributes.operation = DASD_BYPASS_CACHE;
- rc = check_XRC(ccw, data, device);
+ rc = set_timestamp(ccw, data, device);
break;
case DASD_ECKD_CCW_ERASE:
case DASD_ECKD_CCW_WRITE_HOME_ADDRESS:
@@ -299,7 +300,7 @@ define_extent(struct ccw1 *ccw, struct DE_eckd_data *data, unsigned int trk,
data->mask.perm = 0x3;
data->mask.auth = 0x1;
data->attributes.operation = DASD_BYPASS_CACHE;
- rc = check_XRC(ccw, data, device);
+ rc = set_timestamp(ccw, data, device);
break;
case DASD_ECKD_CCW_WRITE_FULL_TRACK:
data->mask.perm = 0x03;
@@ -310,7 +311,7 @@ define_extent(struct ccw1 *ccw, struct DE_eckd_data *data, unsigned int trk,
data->mask.perm = 0x02;
data->attributes.operation = private->attrib.operation;
data->blk_size = blksize;
- rc = check_XRC(ccw, data, device);
+ rc = set_timestamp(ccw, data, device);
break;
default:
dev_err(&device->cdev->dev,
@@ -993,7 +994,7 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
struct dasd_eckd_private *private, path_private;
struct dasd_uid *uid;
char print_path_uid[60], print_device_uid[60];
- struct channel_path_desc *chp_desc;
+ struct channel_path_desc_fmt0 *chp_desc;
struct subchannel_id sch_id;
private = device->private;
@@ -3440,7 +3441,7 @@ static int prepare_itcw(struct itcw *itcw,
dedata->mask.perm = 0x02;
dedata->attributes.operation = basepriv->attrib.operation;
dedata->blk_size = blksize;
- rc = check_XRC(NULL, dedata, basedev);
+ rc = set_timestamp(NULL, dedata, basedev);
dedata->ga_extended |= 0x42;
lredata->operation.orientation = 0x0;
lredata->operation.operation = 0x3F;
diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c
index 9cae08b36b80..0a312e450207 100644
--- a/drivers/s390/block/dcssblk.c
+++ b/drivers/s390/block/dcssblk.c
@@ -633,7 +633,7 @@ dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char
dev_info->gd->private_data = dev_info;
blk_queue_make_request(dev_info->dcssblk_queue, dcssblk_make_request);
blk_queue_logical_block_size(dev_info->dcssblk_queue, 4096);
- queue_flag_set_unlocked(QUEUE_FLAG_DAX, dev_info->dcssblk_queue);
+ blk_queue_flag_set(QUEUE_FLAG_DAX, dev_info->dcssblk_queue);
seg_byte_size = (dev_info->end - dev_info->start + 1);
set_capacity(dev_info->gd, seg_byte_size >> 9); // size in sectors
diff --git a/drivers/s390/block/scm_blk.c b/drivers/s390/block/scm_blk.c
index b4130c7880d8..b1fcb76dd272 100644
--- a/drivers/s390/block/scm_blk.c
+++ b/drivers/s390/block/scm_blk.c
@@ -472,8 +472,8 @@ int scm_blk_dev_setup(struct scm_blk_dev *bdev, struct scm_device *scmdev)
blk_queue_logical_block_size(rq, 1 << 12);
blk_queue_max_hw_sectors(rq, nr_max_blk << 3); /* 8 * 512 = blk_size */
blk_queue_max_segments(rq, nr_max_blk);
- queue_flag_set_unlocked(QUEUE_FLAG_NONROT, rq);
- queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, rq);
+ blk_queue_flag_set(QUEUE_FLAG_NONROT, rq);
+ blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, rq);
bdev->gendisk = alloc_disk(SCM_NR_PARTS);
if (!bdev->gendisk) {
diff --git a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c
index 2a6334ca750e..3df5d68d09f0 100644
--- a/drivers/s390/block/xpram.c
+++ b/drivers/s390/block/xpram.c
@@ -348,8 +348,8 @@ static int __init xpram_setup_blkdev(void)
put_disk(xpram_disks[i]);
goto out;
}
- queue_flag_set_unlocked(QUEUE_FLAG_NONROT, xpram_queues[i]);
- queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, xpram_queues[i]);
+ blk_queue_flag_set(QUEUE_FLAG_NONROT, xpram_queues[i]);
+ blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, xpram_queues[i]);
blk_queue_make_request(xpram_queues[i], xpram_make_request);
blk_queue_logical_block_size(xpram_queues[i], 4096);
}
diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile
index a2b33a22c82a..d049e2d74484 100644
--- a/drivers/s390/char/Makefile
+++ b/drivers/s390/char/Makefile
@@ -23,7 +23,7 @@ CFLAGS_REMOVE_sclp_early_core.o += $(CC_FLAGS_EXPOLINE)
obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \
sclp_cmd.o sclp_config.o sclp_cpi_sys.o sclp_ocf.o sclp_ctl.o \
- sclp_early.o sclp_early_core.o
+ sclp_early.o sclp_early_core.o sclp_sd.o
obj-$(CONFIG_TN3270) += raw3270.o
obj-$(CONFIG_TN3270_CONSOLE) += con3270.o
diff --git a/drivers/s390/char/defkeymap.c b/drivers/s390/char/defkeymap.c
index 98a5c459a1bf..60845d467a1b 100644
--- a/drivers/s390/char/defkeymap.c
+++ b/drivers/s390/char/defkeymap.c
@@ -9,7 +9,9 @@
#include <linux/kbd_kern.h>
#include <linux/kbd_diacr.h>
-u_short plain_map[NR_KEYS] = {
+#include "keyboard.h"
+
+u_short ebc_plain_map[NR_KEYS] = {
0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000,
0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000,
0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000,
@@ -85,12 +87,12 @@ static u_short shift_ctrl_map[NR_KEYS] = {
0xf20a, 0xf108, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
};
-ushort *key_maps[MAX_NR_KEYMAPS] = {
- plain_map, shift_map, NULL, NULL,
+ushort *ebc_key_maps[MAX_NR_KEYMAPS] = {
+ ebc_plain_map, shift_map, NULL, NULL,
ctrl_map, shift_ctrl_map, NULL,
};
-unsigned int keymap_count = 4;
+unsigned int ebc_keymap_count = 4;
/*
@@ -99,7 +101,7 @@ unsigned int keymap_count = 4;
* the default and allocate dynamically in chunks of 512 bytes.
*/
-char func_buf[] = {
+char ebc_func_buf[] = {
'\033', '[', '[', 'A', 0,
'\033', '[', '[', 'B', 0,
'\033', '[', '[', 'C', 0,
@@ -123,37 +125,37 @@ char func_buf[] = {
};
-char *funcbufptr = func_buf;
-int funcbufsize = sizeof(func_buf);
-int funcbufleft = 0; /* space left */
-
-char *func_table[MAX_NR_FUNC] = {
- func_buf + 0,
- func_buf + 5,
- func_buf + 10,
- func_buf + 15,
- func_buf + 20,
- func_buf + 25,
- func_buf + 31,
- func_buf + 37,
- func_buf + 43,
- func_buf + 49,
- func_buf + 55,
- func_buf + 61,
- func_buf + 67,
- func_buf + 73,
- func_buf + 79,
- func_buf + 85,
- func_buf + 91,
- func_buf + 97,
- func_buf + 103,
- func_buf + 109,
+char *ebc_funcbufptr = ebc_func_buf;
+int ebc_funcbufsize = sizeof(ebc_func_buf);
+int ebc_funcbufleft; /* space left */
+
+char *ebc_func_table[MAX_NR_FUNC] = {
+ ebc_func_buf + 0,
+ ebc_func_buf + 5,
+ ebc_func_buf + 10,
+ ebc_func_buf + 15,
+ ebc_func_buf + 20,
+ ebc_func_buf + 25,
+ ebc_func_buf + 31,
+ ebc_func_buf + 37,
+ ebc_func_buf + 43,
+ ebc_func_buf + 49,
+ ebc_func_buf + 55,
+ ebc_func_buf + 61,
+ ebc_func_buf + 67,
+ ebc_func_buf + 73,
+ ebc_func_buf + 79,
+ ebc_func_buf + 85,
+ ebc_func_buf + 91,
+ ebc_func_buf + 97,
+ ebc_func_buf + 103,
+ ebc_func_buf + 109,
NULL,
};
-struct kbdiacruc accent_table[MAX_DIACR] = {
+struct kbdiacruc ebc_accent_table[MAX_DIACR] = {
{'^', 'c', 0003}, {'^', 'd', 0004},
{'^', 'z', 0032}, {'^', 0012, 0000},
};
-unsigned int accent_table_size = 4;
+unsigned int ebc_accent_table_size = 4;
diff --git a/drivers/s390/char/keyboard.c b/drivers/s390/char/keyboard.c
index 5b505fdaedec..db1fbf9b00b5 100644
--- a/drivers/s390/char/keyboard.c
+++ b/drivers/s390/char/keyboard.c
@@ -54,24 +54,24 @@ kbd_alloc(void) {
kbd = kzalloc(sizeof(struct kbd_data), GFP_KERNEL);
if (!kbd)
goto out;
- kbd->key_maps = kzalloc(sizeof(key_maps), GFP_KERNEL);
+ kbd->key_maps = kzalloc(sizeof(ebc_key_maps), GFP_KERNEL);
if (!kbd->key_maps)
goto out_kbd;
- for (i = 0; i < ARRAY_SIZE(key_maps); i++) {
- if (key_maps[i]) {
- kbd->key_maps[i] = kmemdup(key_maps[i],
+ for (i = 0; i < ARRAY_SIZE(ebc_key_maps); i++) {
+ if (ebc_key_maps[i]) {
+ kbd->key_maps[i] = kmemdup(ebc_key_maps[i],
sizeof(u_short) * NR_KEYS,
GFP_KERNEL);
if (!kbd->key_maps[i])
goto out_maps;
}
}
- kbd->func_table = kzalloc(sizeof(func_table), GFP_KERNEL);
+ kbd->func_table = kzalloc(sizeof(ebc_func_table), GFP_KERNEL);
if (!kbd->func_table)
goto out_maps;
- for (i = 0; i < ARRAY_SIZE(func_table); i++) {
- if (func_table[i]) {
- kbd->func_table[i] = kstrdup(func_table[i],
+ for (i = 0; i < ARRAY_SIZE(ebc_func_table); i++) {
+ if (ebc_func_table[i]) {
+ kbd->func_table[i] = kstrdup(ebc_func_table[i],
GFP_KERNEL);
if (!kbd->func_table[i])
goto out_func;
@@ -81,22 +81,22 @@ kbd_alloc(void) {
kzalloc(sizeof(fn_handler_fn *) * NR_FN_HANDLER, GFP_KERNEL);
if (!kbd->fn_handler)
goto out_func;
- kbd->accent_table = kmemdup(accent_table,
+ kbd->accent_table = kmemdup(ebc_accent_table,
sizeof(struct kbdiacruc) * MAX_DIACR,
GFP_KERNEL);
if (!kbd->accent_table)
goto out_fn_handler;
- kbd->accent_table_size = accent_table_size;
+ kbd->accent_table_size = ebc_accent_table_size;
return kbd;
out_fn_handler:
kfree(kbd->fn_handler);
out_func:
- for (i = 0; i < ARRAY_SIZE(func_table); i++)
+ for (i = 0; i < ARRAY_SIZE(ebc_func_table); i++)
kfree(kbd->func_table[i]);
kfree(kbd->func_table);
out_maps:
- for (i = 0; i < ARRAY_SIZE(key_maps); i++)
+ for (i = 0; i < ARRAY_SIZE(ebc_key_maps); i++)
kfree(kbd->key_maps[i]);
kfree(kbd->key_maps);
out_kbd:
@@ -112,10 +112,10 @@ kbd_free(struct kbd_data *kbd)
kfree(kbd->accent_table);
kfree(kbd->fn_handler);
- for (i = 0; i < ARRAY_SIZE(func_table); i++)
+ for (i = 0; i < ARRAY_SIZE(ebc_func_table); i++)
kfree(kbd->func_table[i]);
kfree(kbd->func_table);
- for (i = 0; i < ARRAY_SIZE(key_maps); i++)
+ for (i = 0; i < ARRAY_SIZE(ebc_key_maps); i++)
kfree(kbd->key_maps[i]);
kfree(kbd->key_maps);
kfree(kbd);
@@ -131,7 +131,7 @@ kbd_ascebc(struct kbd_data *kbd, unsigned char *ascebc)
int i, j, k;
memset(ascebc, 0x40, 256);
- for (i = 0; i < ARRAY_SIZE(key_maps); i++) {
+ for (i = 0; i < ARRAY_SIZE(ebc_key_maps); i++) {
keymap = kbd->key_maps[i];
if (!keymap)
continue;
@@ -158,7 +158,7 @@ kbd_ebcasc(struct kbd_data *kbd, unsigned char *ebcasc)
int i, j, k;
memset(ebcasc, ' ', 256);
- for (i = 0; i < ARRAY_SIZE(key_maps); i++) {
+ for (i = 0; i < ARRAY_SIZE(ebc_key_maps); i++) {
keymap = kbd->key_maps[i];
if (!keymap)
continue;
diff --git a/drivers/s390/char/keyboard.h b/drivers/s390/char/keyboard.h
index a074d9711628..c467589c7f45 100644
--- a/drivers/s390/char/keyboard.h
+++ b/drivers/s390/char/keyboard.h
@@ -14,6 +14,17 @@
struct kbd_data;
+extern int ebc_funcbufsize, ebc_funcbufleft;
+extern char *ebc_func_table[MAX_NR_FUNC];
+extern char ebc_func_buf[];
+extern char *ebc_funcbufptr;
+extern unsigned int ebc_keymap_count;
+
+extern struct kbdiacruc ebc_accent_table[];
+extern unsigned int ebc_accent_table_size;
+extern unsigned short *ebc_key_maps[MAX_NR_KEYMAPS];
+extern unsigned short ebc_plain_map[NR_KEYS];
+
typedef void (fn_handler_fn)(struct kbd_data *);
/*
diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c
index e4e2df7a478e..e9aa71cdfc44 100644
--- a/drivers/s390/char/sclp.c
+++ b/drivers/s390/char/sclp.c
@@ -417,7 +417,7 @@ sclp_dispatch_evbufs(struct sccb_header *sccb)
reg = NULL;
list_for_each(l, &sclp_reg_list) {
reg = list_entry(l, struct sclp_register, list);
- if (reg->receive_mask & (1 << (32 - evbuf->type)))
+ if (reg->receive_mask & SCLP_EVTYP_MASK(evbuf->type))
break;
else
reg = NULL;
@@ -618,9 +618,12 @@ struct sclp_statechangebuf {
u16 _zeros : 12;
u16 mask_length;
u64 sclp_active_facility_mask;
- sccb_mask_t sclp_receive_mask;
- sccb_mask_t sclp_send_mask;
- u32 read_data_function_mask;
+ u8 masks[2 * 1021 + 4]; /* variable length */
+ /*
+ * u8 sclp_receive_mask[mask_length];
+ * u8 sclp_send_mask[mask_length];
+ * u32 read_data_function_mask;
+ */
} __attribute__((packed));
@@ -631,14 +634,14 @@ sclp_state_change_cb(struct evbuf_header *evbuf)
unsigned long flags;
struct sclp_statechangebuf *scbuf;
+ BUILD_BUG_ON(sizeof(struct sclp_statechangebuf) > PAGE_SIZE);
+
scbuf = (struct sclp_statechangebuf *) evbuf;
- if (scbuf->mask_length != sizeof(sccb_mask_t))
- return;
spin_lock_irqsave(&sclp_lock, flags);
if (scbuf->validity_sclp_receive_mask)
- sclp_receive_mask = scbuf->sclp_receive_mask;
+ sclp_receive_mask = sccb_get_recv_mask(scbuf);
if (scbuf->validity_sclp_send_mask)
- sclp_send_mask = scbuf->sclp_send_mask;
+ sclp_send_mask = sccb_get_send_mask(scbuf);
spin_unlock_irqrestore(&sclp_lock, flags);
if (scbuf->validity_sclp_active_facility_mask)
sclp.facilities = scbuf->sclp_active_facility_mask;
@@ -748,7 +751,7 @@ EXPORT_SYMBOL(sclp_remove_processed);
/* Prepare init mask request. Called while sclp_lock is locked. */
static inline void
-__sclp_make_init_req(u32 receive_mask, u32 send_mask)
+__sclp_make_init_req(sccb_mask_t receive_mask, sccb_mask_t send_mask)
{
struct init_sccb *sccb;
@@ -761,12 +764,15 @@ __sclp_make_init_req(u32 receive_mask, u32 send_mask)
sclp_init_req.callback = NULL;
sclp_init_req.callback_data = NULL;
sclp_init_req.sccb = sccb;
- sccb->header.length = sizeof(struct init_sccb);
- sccb->mask_length = sizeof(sccb_mask_t);
- sccb->receive_mask = receive_mask;
- sccb->send_mask = send_mask;
- sccb->sclp_receive_mask = 0;
- sccb->sclp_send_mask = 0;
+ sccb->header.length = sizeof(*sccb);
+ if (sclp_mask_compat_mode)
+ sccb->mask_length = SCLP_MASK_SIZE_COMPAT;
+ else
+ sccb->mask_length = sizeof(sccb_mask_t);
+ sccb_set_recv_mask(sccb, receive_mask);
+ sccb_set_send_mask(sccb, send_mask);
+ sccb_set_sclp_recv_mask(sccb, 0);
+ sccb_set_sclp_send_mask(sccb, 0);
}
/* Start init mask request. If calculate is non-zero, calculate the mask as
@@ -822,8 +828,8 @@ sclp_init_mask(int calculate)
sccb->header.response_code == 0x20) {
/* Successful request */
if (calculate) {
- sclp_receive_mask = sccb->sclp_receive_mask;
- sclp_send_mask = sccb->sclp_send_mask;
+ sclp_receive_mask = sccb_get_sclp_recv_mask(sccb);
+ sclp_send_mask = sccb_get_sclp_send_mask(sccb);
} else {
sclp_receive_mask = 0;
sclp_send_mask = 0;
@@ -974,12 +980,18 @@ sclp_check_interface(void)
irq_subclass_unregister(IRQ_SUBCLASS_SERVICE_SIGNAL);
spin_lock_irqsave(&sclp_lock, flags);
del_timer(&sclp_request_timer);
- if (sclp_init_req.status == SCLP_REQ_DONE &&
- sccb->header.response_code == 0x20) {
- rc = 0;
- break;
- } else
- rc = -EBUSY;
+ rc = -EBUSY;
+ if (sclp_init_req.status == SCLP_REQ_DONE) {
+ if (sccb->header.response_code == 0x20) {
+ rc = 0;
+ break;
+ } else if (sccb->header.response_code == 0x74f0) {
+ if (!sclp_mask_compat_mode) {
+ sclp_mask_compat_mode = true;
+ retry = 0;
+ }
+ }
+ }
}
unregister_external_irq(EXT_IRQ_SERVICE_SIG, sclp_check_handler);
spin_unlock_irqrestore(&sclp_lock, flags);
diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h
index f41f6e2ca063..1fe4918088e7 100644
--- a/drivers/s390/char/sclp.h
+++ b/drivers/s390/char/sclp.h
@@ -18,7 +18,7 @@
#define MAX_KMEM_PAGES (sizeof(unsigned long) << 3)
#define SCLP_CONSOLE_PAGES 6
-#define SCLP_EVTYP_MASK(T) (1U << (32 - (T)))
+#define SCLP_EVTYP_MASK(T) (1UL << (sizeof(sccb_mask_t) * BITS_PER_BYTE - (T)))
#define EVTYP_OPCMD 0x01
#define EVTYP_MSG 0x02
@@ -28,6 +28,7 @@
#define EVTYP_PMSGCMD 0x09
#define EVTYP_ASYNC 0x0A
#define EVTYP_CTLPROGIDENT 0x0B
+#define EVTYP_STORE_DATA 0x0C
#define EVTYP_ERRNOTIFY 0x18
#define EVTYP_VT220MSG 0x1A
#define EVTYP_SDIAS 0x1C
@@ -42,6 +43,7 @@
#define EVTYP_PMSGCMD_MASK SCLP_EVTYP_MASK(EVTYP_PMSGCMD)
#define EVTYP_ASYNC_MASK SCLP_EVTYP_MASK(EVTYP_ASYNC)
#define EVTYP_CTLPROGIDENT_MASK SCLP_EVTYP_MASK(EVTYP_CTLPROGIDENT)
+#define EVTYP_STORE_DATA_MASK SCLP_EVTYP_MASK(EVTYP_STORE_DATA)
#define EVTYP_ERRNOTIFY_MASK SCLP_EVTYP_MASK(EVTYP_ERRNOTIFY)
#define EVTYP_VT220MSG_MASK SCLP_EVTYP_MASK(EVTYP_VT220MSG)
#define EVTYP_SDIAS_MASK SCLP_EVTYP_MASK(EVTYP_SDIAS)
@@ -85,7 +87,7 @@ enum sclp_pm_event {
#define SCLP_PANIC_PRIO 1
#define SCLP_PANIC_PRIO_CLIENT 0
-typedef u32 sccb_mask_t; /* ATTENTION: assumes 32bit mask !!! */
+typedef u64 sccb_mask_t;
struct sccb_header {
u16 length;
@@ -98,12 +100,53 @@ struct init_sccb {
struct sccb_header header;
u16 _reserved;
u16 mask_length;
- sccb_mask_t receive_mask;
- sccb_mask_t send_mask;
- sccb_mask_t sclp_receive_mask;
- sccb_mask_t sclp_send_mask;
+ u8 masks[4 * 1021]; /* variable length */
+ /*
+ * u8 receive_mask[mask_length];
+ * u8 send_mask[mask_length];
+ * u8 sclp_receive_mask[mask_length];
+ * u8 sclp_send_mask[mask_length];
+ */
} __attribute__((packed));
+#define SCLP_MASK_SIZE_COMPAT 4
+
+static inline sccb_mask_t sccb_get_mask(u8 *masks, size_t len, int i)
+{
+ sccb_mask_t res = 0;
+
+ memcpy(&res, masks + i * len, min(sizeof(res), len));
+ return res;
+}
+
+static inline void sccb_set_mask(u8 *masks, size_t len, int i, sccb_mask_t val)
+{
+ memset(masks + i * len, 0, len);
+ memcpy(masks + i * len, &val, min(sizeof(val), len));
+}
+
+#define sccb_get_generic_mask(sccb, i) \
+({ \
+ __typeof__(sccb) __sccb = sccb; \
+ \
+ sccb_get_mask(__sccb->masks, __sccb->mask_length, i); \
+})
+#define sccb_get_recv_mask(sccb) sccb_get_generic_mask(sccb, 0)
+#define sccb_get_send_mask(sccb) sccb_get_generic_mask(sccb, 1)
+#define sccb_get_sclp_recv_mask(sccb) sccb_get_generic_mask(sccb, 2)
+#define sccb_get_sclp_send_mask(sccb) sccb_get_generic_mask(sccb, 3)
+
+#define sccb_set_generic_mask(sccb, i, val) \
+({ \
+ __typeof__(sccb) __sccb = sccb; \
+ \
+ sccb_set_mask(__sccb->masks, __sccb->mask_length, i, val); \
+})
+#define sccb_set_recv_mask(sccb, val) sccb_set_generic_mask(sccb, 0, val)
+#define sccb_set_send_mask(sccb, val) sccb_set_generic_mask(sccb, 1, val)
+#define sccb_set_sclp_recv_mask(sccb, val) sccb_set_generic_mask(sccb, 2, val)
+#define sccb_set_sclp_send_mask(sccb, val) sccb_set_generic_mask(sccb, 3, val)
+
struct read_cpu_info_sccb {
struct sccb_header header;
u16 nr_configured;
@@ -221,15 +264,17 @@ extern int sclp_init_state;
extern int sclp_console_pages;
extern int sclp_console_drop;
extern unsigned long sclp_console_full;
+extern bool sclp_mask_compat_mode;
extern char sclp_early_sccb[PAGE_SIZE];
void sclp_early_wait_irq(void);
int sclp_early_cmd(sclp_cmdw_t cmd, void *sccb);
unsigned int sclp_early_con_check_linemode(struct init_sccb *sccb);
+unsigned int sclp_early_con_check_vt220(struct init_sccb *sccb);
int sclp_early_set_event_mask(struct init_sccb *sccb,
- unsigned long receive_mask,
- unsigned long send_mask);
+ sccb_mask_t receive_mask,
+ sccb_mask_t send_mask);
/* useful inlines */
diff --git a/drivers/s390/char/sclp_early.c b/drivers/s390/char/sclp_early.c
index 6b1891539c84..9a74abb9224d 100644
--- a/drivers/s390/char/sclp_early.c
+++ b/drivers/s390/char/sclp_early.c
@@ -249,7 +249,7 @@ static void __init sclp_early_console_detect(struct init_sccb *sccb)
if (sccb->header.response_code != 0x20)
return;
- if (sccb->sclp_send_mask & EVTYP_VT220MSG_MASK)
+ if (sclp_early_con_check_vt220(sccb))
sclp.has_vt220 = 1;
if (sclp_early_con_check_linemode(sccb))
diff --git a/drivers/s390/char/sclp_early_core.c b/drivers/s390/char/sclp_early_core.c
index 17b0c67f3e8d..eceba3858cef 100644
--- a/drivers/s390/char/sclp_early_core.c
+++ b/drivers/s390/char/sclp_early_core.c
@@ -14,6 +14,11 @@
char sclp_early_sccb[PAGE_SIZE] __aligned(PAGE_SIZE) __section(.data);
int sclp_init_state __section(.data) = sclp_init_state_uninitialized;
+/*
+ * Used to keep track of the size of the event masks. Qemu until version 2.11
+ * only supports 4 and needs a workaround.
+ */
+bool sclp_mask_compat_mode __section(.data);
void sclp_early_wait_irq(void)
{
@@ -142,16 +147,24 @@ static void sclp_early_print_vt220(const char *str, unsigned int len)
}
int sclp_early_set_event_mask(struct init_sccb *sccb,
- unsigned long receive_mask,
- unsigned long send_mask)
+ sccb_mask_t receive_mask,
+ sccb_mask_t send_mask)
{
+retry:
memset(sccb, 0, sizeof(*sccb));
sccb->header.length = sizeof(*sccb);
- sccb->mask_length = sizeof(sccb_mask_t);
- sccb->receive_mask = receive_mask;
- sccb->send_mask = send_mask;
+ if (sclp_mask_compat_mode)
+ sccb->mask_length = SCLP_MASK_SIZE_COMPAT;
+ else
+ sccb->mask_length = sizeof(sccb_mask_t);
+ sccb_set_recv_mask(sccb, receive_mask);
+ sccb_set_send_mask(sccb, send_mask);
if (sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_MASK, sccb))
return -EIO;
+ if ((sccb->header.response_code == 0x74f0) && !sclp_mask_compat_mode) {
+ sclp_mask_compat_mode = true;
+ goto retry;
+ }
if (sccb->header.response_code != 0x20)
return -EIO;
return 0;
@@ -159,19 +172,28 @@ int sclp_early_set_event_mask(struct init_sccb *sccb,
unsigned int sclp_early_con_check_linemode(struct init_sccb *sccb)
{
- if (!(sccb->sclp_send_mask & EVTYP_OPCMD_MASK))
+ if (!(sccb_get_sclp_send_mask(sccb) & EVTYP_OPCMD_MASK))
return 0;
- if (!(sccb->sclp_receive_mask & (EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK)))
+ if (!(sccb_get_sclp_recv_mask(sccb) & (EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK)))
return 0;
return 1;
}
+unsigned int sclp_early_con_check_vt220(struct init_sccb *sccb)
+{
+ if (sccb_get_sclp_send_mask(sccb) & EVTYP_VT220MSG_MASK)
+ return 1;
+ return 0;
+}
+
static int sclp_early_setup(int disable, int *have_linemode, int *have_vt220)
{
unsigned long receive_mask, send_mask;
struct init_sccb *sccb;
int rc;
+ BUILD_BUG_ON(sizeof(struct init_sccb) > PAGE_SIZE);
+
*have_linemode = *have_vt220 = 0;
sccb = (struct init_sccb *) &sclp_early_sccb;
receive_mask = disable ? 0 : EVTYP_OPCMD_MASK;
@@ -180,7 +202,7 @@ static int sclp_early_setup(int disable, int *have_linemode, int *have_vt220)
if (rc)
return rc;
*have_linemode = sclp_early_con_check_linemode(sccb);
- *have_vt220 = sccb->send_mask & EVTYP_VT220MSG_MASK;
+ *have_vt220 = !!(sccb_get_send_mask(sccb) & EVTYP_VT220MSG_MASK);
return rc;
}
diff --git a/drivers/s390/char/sclp_sd.c b/drivers/s390/char/sclp_sd.c
new file mode 100644
index 000000000000..99f41db5123b
--- /dev/null
+++ b/drivers/s390/char/sclp_sd.c
@@ -0,0 +1,569 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * SCLP Store Data support and sysfs interface
+ *
+ * Copyright IBM Corp. 2017
+ */
+
+#define KMSG_COMPONENT "sclp_sd"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/completion.h>
+#include <linux/kobject.h>
+#include <linux/list.h>
+#include <linux/printk.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/async.h>
+#include <linux/export.h>
+#include <linux/mutex.h>
+
+#include <asm/pgalloc.h>
+
+#include "sclp.h"
+
+#define SD_EQ_STORE_DATA 0
+#define SD_EQ_HALT 1
+#define SD_EQ_SIZE 2
+
+#define SD_DI_CONFIG 3
+
+struct sclp_sd_evbuf {
+ struct evbuf_header hdr;
+ u8 eq;
+ u8 di;
+ u8 rflags;
+ u64 :56;
+ u32 id;
+ u16 :16;
+ u8 fmt;
+ u8 status;
+ u64 sat;
+ u64 sa;
+ u32 esize;
+ u32 dsize;
+} __packed;
+
+struct sclp_sd_sccb {
+ struct sccb_header hdr;
+ struct sclp_sd_evbuf evbuf;
+} __packed __aligned(PAGE_SIZE);
+
+/**
+ * struct sclp_sd_data - Result of a Store Data request
+ * @esize_bytes: Resulting esize in bytes
+ * @dsize_bytes: Resulting dsize in bytes
+ * @data: Pointer to data - must be released using vfree()
+ */
+struct sclp_sd_data {
+ size_t esize_bytes;
+ size_t dsize_bytes;
+ void *data;
+};
+
+/**
+ * struct sclp_sd_listener - Listener for asynchronous Store Data response
+ * @list: For enqueueing this struct
+ * @id: Event ID of response to listen for
+ * @completion: Can be used to wait for response
+ * @evbuf: Contains the resulting Store Data response after completion
+ */
+struct sclp_sd_listener {
+ struct list_head list;
+ u32 id;
+ struct completion completion;
+ struct sclp_sd_evbuf evbuf;
+};
+
+/**
+ * struct sclp_sd_file - Sysfs representation of a Store Data entity
+ * @kobj: Kobject
+ * @data_attr: Attribute for accessing data contents
+ * @data_mutex: Mutex to serialize access and updates to @data
+ * @data: Data associated with this entity
+ * @di: DI value associated with this entity
+ */
+struct sclp_sd_file {
+ struct kobject kobj;
+ struct bin_attribute data_attr;
+ struct mutex data_mutex;
+ struct sclp_sd_data data;
+ u8 di;
+};
+#define to_sd_file(x) container_of(x, struct sclp_sd_file, kobj)
+
+static struct kset *sclp_sd_kset;
+static struct sclp_sd_file *config_file;
+
+static LIST_HEAD(sclp_sd_queue);
+static DEFINE_SPINLOCK(sclp_sd_queue_lock);
+
+/**
+ * sclp_sd_listener_add() - Add listener for Store Data responses
+ * @listener: Listener to add
+ */
+static void sclp_sd_listener_add(struct sclp_sd_listener *listener)
+{
+ spin_lock_irq(&sclp_sd_queue_lock);
+ list_add_tail(&listener->list, &sclp_sd_queue);
+ spin_unlock_irq(&sclp_sd_queue_lock);
+}
+
+/**
+ * sclp_sd_listener_remove() - Remove listener for Store Data responses
+ * @listener: Listener to remove
+ */
+static void sclp_sd_listener_remove(struct sclp_sd_listener *listener)
+{
+ spin_lock_irq(&sclp_sd_queue_lock);
+ list_del(&listener->list);
+ spin_unlock_irq(&sclp_sd_queue_lock);
+}
+
+/**
+ * sclp_sd_listener_init() - Initialize a Store Data response listener
+ * @id: Event ID to listen for
+ *
+ * Initialize a listener for asynchronous Store Data responses. This listener
+ * can afterwards be used to wait for a specific response and to retrieve
+ * the associated response data.
+ */
+static void sclp_sd_listener_init(struct sclp_sd_listener *listener, u32 id)
+{
+ memset(listener, 0, sizeof(*listener));
+ listener->id = id;
+ init_completion(&listener->completion);
+}
+
+/**
+ * sclp_sd_receiver() - Receiver for Store Data events
+ * @evbuf_hdr: Header of received events
+ *
+ * Process Store Data events and complete listeners with matching event IDs.
+ */
+static void sclp_sd_receiver(struct evbuf_header *evbuf_hdr)
+{
+ struct sclp_sd_evbuf *evbuf = (struct sclp_sd_evbuf *) evbuf_hdr;
+ struct sclp_sd_listener *listener;
+ int found = 0;
+
+ pr_debug("received event (id=0x%08x)\n", evbuf->id);
+ spin_lock(&sclp_sd_queue_lock);
+ list_for_each_entry(listener, &sclp_sd_queue, list) {
+ if (listener->id != evbuf->id)
+ continue;
+
+ listener->evbuf = *evbuf;
+ complete(&listener->completion);
+ found = 1;
+ break;
+ }
+ spin_unlock(&sclp_sd_queue_lock);
+
+ if (!found)
+ pr_debug("unsolicited event (id=0x%08x)\n", evbuf->id);
+}
+
+static struct sclp_register sclp_sd_register = {
+ .send_mask = EVTYP_STORE_DATA_MASK,
+ .receive_mask = EVTYP_STORE_DATA_MASK,
+ .receiver_fn = sclp_sd_receiver,
+};
+
+/**
+ * sclp_sd_sync() - Perform Store Data request synchronously
+ * @page: Address of work page - must be below 2GB
+ * @eq: Input EQ value
+ * @di: Input DI value
+ * @sat: Input SAT value
+ * @sa: Input SA value used to specify the address of the target buffer
+ * @dsize_ptr: Optional pointer to input and output DSIZE value
+ * @esize_ptr: Optional pointer to output ESIZE value
+ *
+ * Perform Store Data request with specified parameters and wait for completion.
+ *
+ * Return %0 on success and store resulting DSIZE and ESIZE values in
+ * @dsize_ptr and @esize_ptr (if provided). Return non-zero on error.
+ */
+static int sclp_sd_sync(unsigned long page, u8 eq, u8 di, u64 sat, u64 sa,
+ u32 *dsize_ptr, u32 *esize_ptr)
+{
+ struct sclp_sd_sccb *sccb = (void *) page;
+ struct sclp_sd_listener listener;
+ struct sclp_sd_evbuf *evbuf;
+ int rc;
+
+ sclp_sd_listener_init(&listener, (u32) (addr_t) sccb);
+ sclp_sd_listener_add(&listener);
+
+ /* Prepare SCCB */
+ memset(sccb, 0, PAGE_SIZE);
+ sccb->hdr.length = sizeof(sccb->hdr) + sizeof(sccb->evbuf);
+ evbuf = &sccb->evbuf;
+ evbuf->hdr.length = sizeof(*evbuf);
+ evbuf->hdr.type = EVTYP_STORE_DATA;
+ evbuf->eq = eq;
+ evbuf->di = di;
+ evbuf->id = listener.id;
+ evbuf->fmt = 1;
+ evbuf->sat = sat;
+ evbuf->sa = sa;
+ if (dsize_ptr)
+ evbuf->dsize = *dsize_ptr;
+
+ /* Perform command */
+ pr_debug("request (eq=%d, di=%d, id=0x%08x)\n", eq, di, listener.id);
+ rc = sclp_sync_request(SCLP_CMDW_WRITE_EVENT_DATA, sccb);
+ pr_debug("request done (rc=%d)\n", rc);
+ if (rc)
+ goto out;
+
+ /* Evaluate response */
+ if (sccb->hdr.response_code == 0x73f0) {
+ pr_debug("event not supported\n");
+ rc = -EIO;
+ goto out_remove;
+ }
+ if (sccb->hdr.response_code != 0x0020 || !(evbuf->hdr.flags & 0x80)) {
+ rc = -EIO;
+ goto out;
+ }
+ if (!(evbuf->rflags & 0x80)) {
+ rc = wait_for_completion_interruptible(&listener.completion);
+ if (rc)
+ goto out;
+ evbuf = &listener.evbuf;
+ }
+ switch (evbuf->status) {
+ case 0:
+ if (dsize_ptr)
+ *dsize_ptr = evbuf->dsize;
+ if (esize_ptr)
+ *esize_ptr = evbuf->esize;
+ pr_debug("success (dsize=%u, esize=%u)\n", evbuf->dsize,
+ evbuf->esize);
+ break;
+ case 3:
+ rc = -ENOENT;
+ break;
+ default:
+ rc = -EIO;
+ break;
+
+ }
+
+out:
+ if (rc && rc != -ENOENT) {
+ /* Provide some information about what went wrong */
+ pr_warn("Store Data request failed (eq=%d, di=%d, "
+ "response=0x%04x, flags=0x%02x, status=%d, rc=%d)\n",
+ eq, di, sccb->hdr.response_code, evbuf->hdr.flags,
+ evbuf->status, rc);
+ }
+
+out_remove:
+ sclp_sd_listener_remove(&listener);
+
+ return rc;
+}
+
+/**
+ * sclp_sd_store_data() - Obtain data for specified Store Data entity
+ * @result: Resulting data
+ * @di: DI value associated with this entity
+ *
+ * Perform a series of Store Data requests to obtain the size and contents of
+ * the specified Store Data entity.
+ *
+ * Return:
+ * %0: Success - result is stored in @result. @result->data must be
+ * released using vfree() after use.
+ * %-ENOENT: No data available for this entity
+ * %<0: Other error
+ */
+static int sclp_sd_store_data(struct sclp_sd_data *result, u8 di)
+{
+ u32 dsize = 0, esize = 0;
+ unsigned long page, asce = 0;
+ void *data = NULL;
+ int rc;
+
+ page = __get_free_page(GFP_KERNEL | GFP_DMA);
+ if (!page)
+ return -ENOMEM;
+
+ /* Get size */
+ rc = sclp_sd_sync(page, SD_EQ_SIZE, di, 0, 0, &dsize, &esize);
+ if (rc)
+ goto out;
+ if (dsize == 0)
+ goto out_result;
+
+ /* Allocate memory */
+ data = vzalloc((size_t) dsize * PAGE_SIZE);
+ if (!data) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ /* Get translation table for buffer */
+ asce = base_asce_alloc((unsigned long) data, dsize);
+ if (!asce) {
+ vfree(data);
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ /* Get data */
+ rc = sclp_sd_sync(page, SD_EQ_STORE_DATA, di, asce, (u64) data, &dsize,
+ &esize);
+ if (rc) {
+ /* Cancel running request if interrupted */
+ if (rc == -ERESTARTSYS)
+ sclp_sd_sync(page, SD_EQ_HALT, di, 0, 0, NULL, NULL);
+ vfree(data);
+ goto out;
+ }
+
+out_result:
+ result->esize_bytes = (size_t) esize * PAGE_SIZE;
+ result->dsize_bytes = (size_t) dsize * PAGE_SIZE;
+ result->data = data;
+
+out:
+ base_asce_free(asce);
+ free_page(page);
+
+ return rc;
+}
+
+/**
+ * sclp_sd_data_reset() - Reset Store Data result buffer
+ * @data: Data buffer to reset
+ *
+ * Reset @data to initial state and release associated memory.
+ */
+static void sclp_sd_data_reset(struct sclp_sd_data *data)
+{
+ vfree(data->data);
+ data->data = NULL;
+ data->dsize_bytes = 0;
+ data->esize_bytes = 0;
+}
+
+/**
+ * sclp_sd_file_release() - Release function for sclp_sd_file object
+ * @kobj: Kobject embedded in sclp_sd_file object
+ */
+static void sclp_sd_file_release(struct kobject *kobj)
+{
+ struct sclp_sd_file *sd_file = to_sd_file(kobj);
+
+ sclp_sd_data_reset(&sd_file->data);
+ kfree(sd_file);
+}
+
+/**
+ * sclp_sd_file_update() - Update contents of sclp_sd_file object
+ * @sd_file: Object to update
+ *
+ * Obtain the current version of data associated with the Store Data entity
+ * @sd_file.
+ *
+ * On success, return %0 and generate a KOBJ_CHANGE event to indicate that the
+ * data may have changed. Return non-zero otherwise.
+ */
+static int sclp_sd_file_update(struct sclp_sd_file *sd_file)
+{
+ const char *name = kobject_name(&sd_file->kobj);
+ struct sclp_sd_data data;
+ int rc;
+
+ rc = sclp_sd_store_data(&data, sd_file->di);
+ if (rc) {
+ if (rc == -ENOENT) {
+ pr_info("No data is available for the %s data entity\n",
+ name);
+ }
+ return rc;
+ }
+
+ mutex_lock(&sd_file->data_mutex);
+ sclp_sd_data_reset(&sd_file->data);
+ sd_file->data = data;
+ mutex_unlock(&sd_file->data_mutex);
+
+ pr_info("A %zu-byte %s data entity was retrieved\n", data.dsize_bytes,
+ name);
+ kobject_uevent(&sd_file->kobj, KOBJ_CHANGE);
+
+ return 0;
+}
+
+/**
+ * sclp_sd_file_update_async() - Wrapper for asynchronous update call
+ * @data: Object to update
+ */
+static void sclp_sd_file_update_async(void *data, async_cookie_t cookie)
+{
+ struct sclp_sd_file *sd_file = data;
+
+ sclp_sd_file_update(sd_file);
+}
+
+/**
+ * reload_store() - Store function for "reload" sysfs attribute
+ * @kobj: Kobject of sclp_sd_file object
+ *
+ * Initiate a reload of the data associated with an sclp_sd_file object.
+ */
+static ssize_t reload_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sclp_sd_file *sd_file = to_sd_file(kobj);
+
+ sclp_sd_file_update(sd_file);
+
+ return count;
+}
+
+static struct kobj_attribute reload_attr = __ATTR_WO(reload);
+
+static struct attribute *sclp_sd_file_default_attrs[] = {
+ &reload_attr.attr,
+ NULL,
+};
+
+static struct kobj_type sclp_sd_file_ktype = {
+ .sysfs_ops = &kobj_sysfs_ops,
+ .release = sclp_sd_file_release,
+ .default_attrs = sclp_sd_file_default_attrs,
+};
+
+/**
+ * data_read() - Read function for "read" sysfs attribute
+ * @kobj: Kobject of sclp_sd_file object
+ * @buffer: Target buffer
+ * @off: Requested file offset
+ * @size: Requested number of bytes
+ *
+ * Store the requested portion of the Store Data entity contents into the
+ * specified buffer. Return the number of bytes stored on success, or %0
+ * on EOF.
+ */
+static ssize_t data_read(struct file *file, struct kobject *kobj,
+ struct bin_attribute *attr, char *buffer,
+ loff_t off, size_t size)
+{
+ struct sclp_sd_file *sd_file = to_sd_file(kobj);
+ size_t data_size;
+ char *data;
+
+ mutex_lock(&sd_file->data_mutex);
+
+ data = sd_file->data.data;
+ data_size = sd_file->data.dsize_bytes;
+ if (!data || off >= data_size) {
+ size = 0;
+ } else {
+ if (off + size > data_size)
+ size = data_size - off;
+ memcpy(buffer, data + off, size);
+ }
+
+ mutex_unlock(&sd_file->data_mutex);
+
+ return size;
+}
+
+/**
+ * sclp_sd_file_create() - Add a sysfs file representing a Store Data entity
+ * @name: Name of file
+ * @di: DI value associated with this entity
+ *
+ * Create a sysfs directory with the given @name located under
+ *
+ * /sys/firmware/sclp_sd/
+ *
+ * The files in this directory can be used to access the contents of the Store
+ * Data entity associated with @DI.
+ *
+ * Return pointer to resulting sclp_sd_file object on success, %NULL otherwise.
+ * The object must be freed by calling kobject_put() on the embedded kobject
+ * pointer after use.
+ */
+static __init struct sclp_sd_file *sclp_sd_file_create(const char *name, u8 di)
+{
+ struct sclp_sd_file *sd_file;
+ int rc;
+
+ sd_file = kzalloc(sizeof(*sd_file), GFP_KERNEL);
+ if (!sd_file)
+ return NULL;
+ sd_file->di = di;
+ mutex_init(&sd_file->data_mutex);
+
+ /* Create kobject located under /sys/firmware/sclp_sd/ */
+ sd_file->kobj.kset = sclp_sd_kset;
+ rc = kobject_init_and_add(&sd_file->kobj, &sclp_sd_file_ktype, NULL,
+ "%s", name);
+ if (rc) {
+ kobject_put(&sd_file->kobj);
+ return NULL;
+ }
+
+ sysfs_bin_attr_init(&sd_file->data_attr);
+ sd_file->data_attr.attr.name = "data";
+ sd_file->data_attr.attr.mode = 0444;
+ sd_file->data_attr.read = data_read;
+
+ rc = sysfs_create_bin_file(&sd_file->kobj, &sd_file->data_attr);
+ if (rc) {
+ kobject_put(&sd_file->kobj);
+ return NULL;
+ }
+
+ /*
+ * For completeness only - users interested in entity data should listen
+ * for KOBJ_CHANGE instead.
+ */
+ kobject_uevent(&sd_file->kobj, KOBJ_ADD);
+
+ /* Don't let a slow Store Data request delay further initialization */
+ async_schedule(sclp_sd_file_update_async, sd_file);
+
+ return sd_file;
+}
+
+/**
+ * sclp_sd_init() - Initialize sclp_sd support and register sysfs files
+ */
+static __init int sclp_sd_init(void)
+{
+ int rc;
+
+ rc = sclp_register(&sclp_sd_register);
+ if (rc)
+ return rc;
+
+ /* Create kset named "sclp_sd" located under /sys/firmware/ */
+ rc = -ENOMEM;
+ sclp_sd_kset = kset_create_and_add("sclp_sd", NULL, firmware_kobj);
+ if (!sclp_sd_kset)
+ goto err_kset;
+
+ rc = -EINVAL;
+ config_file = sclp_sd_file_create("config", SD_DI_CONFIG);
+ if (!config_file)
+ goto err_config;
+
+ return 0;
+
+err_config:
+ kset_unregister(sclp_sd_kset);
+err_kset:
+ sclp_unregister(&sclp_sd_register);
+
+ return rc;
+}
+device_initcall(sclp_sd_init);
diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c
index 9f7b87d6d434..5aff8b684eb2 100644
--- a/drivers/s390/char/sclp_tty.c
+++ b/drivers/s390/char/sclp_tty.c
@@ -502,7 +502,10 @@ sclp_tty_init(void)
int i;
int rc;
- if (!CONSOLE_IS_SCLP)
+ /* z/VM multiplexes the line mode output on the 32xx screen */
+ if (MACHINE_IS_VM && !CONSOLE_IS_SCLP)
+ return 0;
+ if (!sclp.has_linemode)
return 0;
driver = alloc_tty_driver(1);
if (!driver)
diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c
index bfec1485ca23..5535312602af 100644
--- a/drivers/s390/cio/ccwgroup.c
+++ b/drivers/s390/cio/ccwgroup.c
@@ -323,6 +323,9 @@ int ccwgroup_create_dev(struct device *parent, struct ccwgroup_driver *gdrv,
struct ccw_dev_id dev_id;
int rc, i;
+ if (num_devices < 1)
+ return -EINVAL;
+
gdev = kzalloc(sizeof(*gdev) + num_devices * sizeof(gdev->cdev[0]),
GFP_KERNEL);
if (!gdev)
@@ -375,7 +378,7 @@ int ccwgroup_create_dev(struct device *parent, struct ccwgroup_driver *gdrv,
goto error;
}
/* Check if the devices are bound to the required ccw driver. */
- if (gdev->count && gdrv && gdrv->ccw_driver &&
+ if (gdrv && gdrv->ccw_driver &&
gdev->cdev[0]->drv != gdrv->ccw_driver) {
rc = -EINVAL;
goto error;
diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c
index f95b452b8bbc..afbdee74147d 100644
--- a/drivers/s390/cio/chp.c
+++ b/drivers/s390/cio/chp.c
@@ -384,6 +384,28 @@ static ssize_t chp_chid_external_show(struct device *dev,
}
static DEVICE_ATTR(chid_external, 0444, chp_chid_external_show, NULL);
+static ssize_t util_string_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct channel_path *chp = to_channelpath(kobj_to_dev(kobj));
+ ssize_t rc;
+
+ mutex_lock(&chp->lock);
+ rc = memory_read_from_buffer(buf, count, &off, chp->desc_fmt3.util_str,
+ sizeof(chp->desc_fmt3.util_str));
+ mutex_unlock(&chp->lock);
+
+ return rc;
+}
+static BIN_ATTR_RO(util_string,
+ sizeof(((struct channel_path_desc_fmt3 *)0)->util_str));
+
+static struct bin_attribute *chp_bin_attrs[] = {
+ &bin_attr_util_string,
+ NULL,
+};
+
static struct attribute *chp_attrs[] = {
&dev_attr_status.attr,
&dev_attr_configure.attr,
@@ -396,6 +418,7 @@ static struct attribute *chp_attrs[] = {
};
static struct attribute_group chp_attr_group = {
.attrs = chp_attrs,
+ .bin_attrs = chp_bin_attrs,
};
static const struct attribute_group *chp_attr_groups[] = {
&chp_attr_group,
@@ -422,7 +445,7 @@ int chp_update_desc(struct channel_path *chp)
{
int rc;
- rc = chsc_determine_base_channel_path_desc(chp->chpid, &chp->desc);
+ rc = chsc_determine_fmt0_channel_path_desc(chp->chpid, &chp->desc);
if (rc)
return rc;
@@ -431,6 +454,7 @@ int chp_update_desc(struct channel_path *chp)
* hypervisors implement the required chsc commands.
*/
chsc_determine_fmt1_channel_path_desc(chp->chpid, &chp->desc_fmt1);
+ chsc_determine_fmt3_channel_path_desc(chp->chpid, &chp->desc_fmt3);
chsc_get_channel_measurement_chars(chp);
return 0;
@@ -506,20 +530,20 @@ out:
* On success return a newly allocated copy of the channel-path description
* data associated with the given channel-path ID. Return %NULL on error.
*/
-struct channel_path_desc *chp_get_chp_desc(struct chp_id chpid)
+struct channel_path_desc_fmt0 *chp_get_chp_desc(struct chp_id chpid)
{
struct channel_path *chp;
- struct channel_path_desc *desc;
+ struct channel_path_desc_fmt0 *desc;
chp = chpid_to_chp(chpid);
if (!chp)
return NULL;
- desc = kmalloc(sizeof(struct channel_path_desc), GFP_KERNEL);
+ desc = kmalloc(sizeof(*desc), GFP_KERNEL);
if (!desc)
return NULL;
mutex_lock(&chp->lock);
- memcpy(desc, &chp->desc, sizeof(struct channel_path_desc));
+ memcpy(desc, &chp->desc, sizeof(*desc));
mutex_unlock(&chp->lock);
return desc;
}
diff --git a/drivers/s390/cio/chp.h b/drivers/s390/cio/chp.h
index 7e80323cd261..20259f3fbf45 100644
--- a/drivers/s390/cio/chp.h
+++ b/drivers/s390/cio/chp.h
@@ -44,8 +44,9 @@ struct channel_path {
struct chp_id chpid;
struct mutex lock; /* Serialize access to below members. */
int state;
- struct channel_path_desc desc;
+ struct channel_path_desc_fmt0 desc;
struct channel_path_desc_fmt1 desc_fmt1;
+ struct channel_path_desc_fmt3 desc_fmt3;
/* Channel-measurement related stuff: */
int cmg;
int shared;
@@ -61,7 +62,7 @@ static inline struct channel_path *chpid_to_chp(struct chp_id chpid)
int chp_get_status(struct chp_id chpid);
u8 chp_get_sch_opm(struct subchannel *sch);
int chp_is_registered(struct chp_id chpid);
-struct channel_path_desc *chp_get_chp_desc(struct chp_id chpid);
+struct channel_path_desc_fmt0 *chp_get_chp_desc(struct chp_id chpid);
void chp_remove_cmg_attr(struct channel_path *chp);
int chp_add_cmg_attr(struct channel_path *chp);
int chp_update_desc(struct channel_path *chp);
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index c08fc5a8df0c..9029804dcd22 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -452,6 +452,7 @@ static void chsc_process_sei_link_incident(struct chsc_sei_nt0_area *sei_area)
static void chsc_process_sei_res_acc(struct chsc_sei_nt0_area *sei_area)
{
+ struct channel_path *chp;
struct chp_link link;
struct chp_id chpid;
int status;
@@ -464,10 +465,17 @@ static void chsc_process_sei_res_acc(struct chsc_sei_nt0_area *sei_area)
chpid.id = sei_area->rsid;
/* allocate a new channel path structure, if needed */
status = chp_get_status(chpid);
- if (status < 0)
- chp_new(chpid);
- else if (!status)
+ if (!status)
return;
+
+ if (status < 0) {
+ chp_new(chpid);
+ } else {
+ chp = chpid_to_chp(chpid);
+ mutex_lock(&chp->lock);
+ chp_update_desc(chp);
+ mutex_unlock(&chp->lock);
+ }
memset(&link, 0, sizeof(struct chp_link));
link.chpid = chpid;
if ((sei_area->vf & 0xc0) != 0) {
@@ -915,6 +923,8 @@ int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt,
return -EINVAL;
if ((rfmt == 2) && !css_general_characteristics.cib)
return -EINVAL;
+ if ((rfmt == 3) && !css_general_characteristics.util_str)
+ return -EINVAL;
memset(page, 0, PAGE_SIZE);
scpd_area = page;
@@ -940,43 +950,30 @@ int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt,
}
EXPORT_SYMBOL_GPL(chsc_determine_channel_path_desc);
-int chsc_determine_base_channel_path_desc(struct chp_id chpid,
- struct channel_path_desc *desc)
-{
- struct chsc_scpd *scpd_area;
- unsigned long flags;
- int ret;
-
- spin_lock_irqsave(&chsc_page_lock, flags);
- scpd_area = chsc_page;
- ret = chsc_determine_channel_path_desc(chpid, 0, 0, 0, 0, scpd_area);
- if (ret)
- goto out;
-
- memcpy(desc, scpd_area->data, sizeof(*desc));
-out:
- spin_unlock_irqrestore(&chsc_page_lock, flags);
- return ret;
+#define chsc_det_chp_desc(FMT, c) \
+int chsc_determine_fmt##FMT##_channel_path_desc( \
+ struct chp_id chpid, struct channel_path_desc_fmt##FMT *desc) \
+{ \
+ struct chsc_scpd *scpd_area; \
+ unsigned long flags; \
+ int ret; \
+ \
+ spin_lock_irqsave(&chsc_page_lock, flags); \
+ scpd_area = chsc_page; \
+ ret = chsc_determine_channel_path_desc(chpid, 0, FMT, c, 0, \
+ scpd_area); \
+ if (ret) \
+ goto out; \
+ \
+ memcpy(desc, scpd_area->data, sizeof(*desc)); \
+out: \
+ spin_unlock_irqrestore(&chsc_page_lock, flags); \
+ return ret; \
}
-int chsc_determine_fmt1_channel_path_desc(struct chp_id chpid,
- struct channel_path_desc_fmt1 *desc)
-{
- struct chsc_scpd *scpd_area;
- unsigned long flags;
- int ret;
-
- spin_lock_irqsave(&chsc_page_lock, flags);
- scpd_area = chsc_page;
- ret = chsc_determine_channel_path_desc(chpid, 0, 1, 1, 0, scpd_area);
- if (ret)
- goto out;
-
- memcpy(desc, scpd_area->data, sizeof(*desc));
-out:
- spin_unlock_irqrestore(&chsc_page_lock, flags);
- return ret;
-}
+chsc_det_chp_desc(0, 0)
+chsc_det_chp_desc(1, 1)
+chsc_det_chp_desc(3, 0)
static void
chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv,
diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h
index dda5953534b7..5c9f0dd33f4e 100644
--- a/drivers/s390/cio/chsc.h
+++ b/drivers/s390/cio/chsc.h
@@ -40,6 +40,11 @@ struct channel_path_desc_fmt1 {
u32 zeros[2];
} __attribute__ ((packed));
+struct channel_path_desc_fmt3 {
+ struct channel_path_desc_fmt1 fmt1_desc;
+ u8 util_str[64];
+};
+
struct channel_path;
struct css_chsc_char {
@@ -147,10 +152,12 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable);
int chsc_chp_vary(struct chp_id chpid, int on);
int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt,
int c, int m, void *page);
-int chsc_determine_base_channel_path_desc(struct chp_id chpid,
- struct channel_path_desc *desc);
+int chsc_determine_fmt0_channel_path_desc(struct chp_id chpid,
+ struct channel_path_desc_fmt0 *desc);
int chsc_determine_fmt1_channel_path_desc(struct chp_id chpid,
struct channel_path_desc_fmt1 *desc);
+int chsc_determine_fmt3_channel_path_desc(struct chp_id chpid,
+ struct channel_path_desc_fmt3 *desc);
void chsc_chp_online(struct chp_id chpid);
void chsc_chp_offline(struct chp_id chpid);
int chsc_get_channel_measurement_chars(struct channel_path *chp);
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index 6886b3d34cf8..5130d7c67239 100644
--- a/drivers/s390/cio/cio.c
+++ b/drivers/s390/cio/cio.c
@@ -25,7 +25,6 @@
#include <asm/irq.h>
#include <asm/irq_regs.h>
#include <asm/setup.h>
-#include <asm/reset.h>
#include <asm/ipl.h>
#include <asm/chpid.h>
#include <asm/airq.h>
@@ -767,262 +766,6 @@ void cio_register_early_subchannels(void)
}
#endif /* CONFIG_CCW_CONSOLE */
-static int
-__disable_subchannel_easy(struct subchannel_id schid, struct schib *schib)
-{
- int retry, cc;
-
- cc = 0;
- for (retry=0;retry<3;retry++) {
- schib->pmcw.ena = 0;
- cc = msch(schid, schib);
- if (cc)
- return (cc==3?-ENODEV:-EBUSY);
- if (stsch(schid, schib) || !css_sch_is_valid(schib))
- return -ENODEV;
- if (!schib->pmcw.ena)
- return 0;
- }
- return -EBUSY; /* uhm... */
-}
-
-static int
-__clear_io_subchannel_easy(struct subchannel_id schid)
-{
- int retry;
-
- if (csch(schid))
- return -ENODEV;
- for (retry=0;retry<20;retry++) {
- struct tpi_info ti;
-
- if (tpi(&ti)) {
- tsch(ti.schid, this_cpu_ptr(&cio_irb));
- if (schid_equal(&ti.schid, &schid))
- return 0;
- }
- udelay_simple(100);
- }
- return -EBUSY;
-}
-
-static void __clear_chsc_subchannel_easy(void)
-{
- /* It seems we can only wait for a bit here :/ */
- udelay_simple(100);
-}
-
-static int pgm_check_occured;
-
-static void cio_reset_pgm_check_handler(void)
-{
- pgm_check_occured = 1;
-}
-
-static int stsch_reset(struct subchannel_id schid, struct schib *addr)
-{
- int rc;
-
- pgm_check_occured = 0;
- s390_base_pgm_handler_fn = cio_reset_pgm_check_handler;
- rc = stsch(schid, addr);
- s390_base_pgm_handler_fn = NULL;
-
- /* The program check handler could have changed pgm_check_occured. */
- barrier();
-
- if (pgm_check_occured)
- return -EIO;
- else
- return rc;
-}
-
-static int __shutdown_subchannel_easy(struct subchannel_id schid, void *data)
-{
- struct schib schib;
-
- if (stsch_reset(schid, &schib))
- return -ENXIO;
- if (!schib.pmcw.ena)
- return 0;
- switch(__disable_subchannel_easy(schid, &schib)) {
- case 0:
- case -ENODEV:
- break;
- default: /* -EBUSY */
- switch (schib.pmcw.st) {
- case SUBCHANNEL_TYPE_IO:
- if (__clear_io_subchannel_easy(schid))
- goto out; /* give up... */
- break;
- case SUBCHANNEL_TYPE_CHSC:
- __clear_chsc_subchannel_easy();
- break;
- default:
- /* No default clear strategy */
- break;
- }
- stsch(schid, &schib);
- __disable_subchannel_easy(schid, &schib);
- }
-out:
- return 0;
-}
-
-static atomic_t chpid_reset_count;
-
-static void s390_reset_chpids_mcck_handler(void)
-{
- struct crw crw;
- union mci mci;
-
- /* Check for pending channel report word. */
- mci.val = S390_lowcore.mcck_interruption_code;
- if (!mci.cp)
- return;
- /* Process channel report words. */
- while (stcrw(&crw) == 0) {
- /* Check for responses to RCHP. */
- if (crw.slct && crw.rsc == CRW_RSC_CPATH)
- atomic_dec(&chpid_reset_count);
- }
-}
-
-#define RCHP_TIMEOUT (30 * USEC_PER_SEC)
-static void css_reset(void)
-{
- int i, ret;
- unsigned long long timeout;
- struct chp_id chpid;
-
- /* Reset subchannels. */
- for_each_subchannel(__shutdown_subchannel_easy, NULL);
- /* Reset channel paths. */
- s390_base_mcck_handler_fn = s390_reset_chpids_mcck_handler;
- /* Enable channel report machine checks. */
- __ctl_set_bit(14, 28);
- /* Temporarily reenable machine checks. */
- local_mcck_enable();
- chp_id_init(&chpid);
- for (i = 0; i <= __MAX_CHPID; i++) {
- chpid.id = i;
- ret = rchp(chpid);
- if ((ret == 0) || (ret == 2))
- /*
- * rchp either succeeded, or another rchp is already
- * in progress. In either case, we'll get a crw.
- */
- atomic_inc(&chpid_reset_count);
- }
- /* Wait for machine check for all channel paths. */
- timeout = get_tod_clock_fast() + (RCHP_TIMEOUT << 12);
- while (atomic_read(&chpid_reset_count) != 0) {
- if (get_tod_clock_fast() > timeout)
- break;
- cpu_relax();
- }
- /* Disable machine checks again. */
- local_mcck_disable();
- /* Disable channel report machine checks. */
- __ctl_clear_bit(14, 28);
- s390_base_mcck_handler_fn = NULL;
-}
-
-static struct reset_call css_reset_call = {
- .fn = css_reset,
-};
-
-static int __init init_css_reset_call(void)
-{
- atomic_set(&chpid_reset_count, 0);
- register_reset_call(&css_reset_call);
- return 0;
-}
-
-arch_initcall(init_css_reset_call);
-
-struct sch_match_id {
- struct subchannel_id schid;
- struct ccw_dev_id devid;
- int rc;
-};
-
-static int __reipl_subchannel_match(struct subchannel_id schid, void *data)
-{
- struct schib schib;
- struct sch_match_id *match_id = data;
-
- if (stsch_reset(schid, &schib))
- return -ENXIO;
- if ((schib.pmcw.st == SUBCHANNEL_TYPE_IO) && schib.pmcw.dnv &&
- (schib.pmcw.dev == match_id->devid.devno) &&
- (schid.ssid == match_id->devid.ssid)) {
- match_id->schid = schid;
- match_id->rc = 0;
- return 1;
- }
- return 0;
-}
-
-static int reipl_find_schid(struct ccw_dev_id *devid,
- struct subchannel_id *schid)
-{
- struct sch_match_id match_id;
-
- match_id.devid = *devid;
- match_id.rc = -ENODEV;
- for_each_subchannel(__reipl_subchannel_match, &match_id);
- if (match_id.rc == 0)
- *schid = match_id.schid;
- return match_id.rc;
-}
-
-extern void do_reipl_asm(__u32 schid);
-
-/* Make sure all subchannels are quiet before we re-ipl an lpar. */
-void reipl_ccw_dev(struct ccw_dev_id *devid)
-{
- struct subchannel_id uninitialized_var(schid);
-
- s390_reset_system();
- if (reipl_find_schid(devid, &schid) != 0)
- panic("IPL Device not found\n");
- do_reipl_asm(*((__u32*)&schid));
-}
-
-int __init cio_get_iplinfo(struct cio_iplinfo *iplinfo)
-{
- static struct chsc_sda_area sda_area __initdata;
- struct subchannel_id schid;
- struct schib schib;
-
- schid = *(struct subchannel_id *)&S390_lowcore.subchannel_id;
- if (!schid.one)
- return -ENODEV;
-
- if (schid.ssid) {
- /*
- * Firmware should have already enabled MSS but whoever started
- * the kernel might have initiated a channel subsystem reset.
- * Ensure that MSS is enabled.
- */
- memset(&sda_area, 0, sizeof(sda_area));
- if (__chsc_enable_facility(&sda_area, CHSC_SDA_OC_MSS))
- return -ENODEV;
- }
- if (stsch(schid, &schib))
- return -ENODEV;
- if (schib.pmcw.st != SUBCHANNEL_TYPE_IO)
- return -ENODEV;
- if (!schib.pmcw.dnv)
- return -ENODEV;
-
- iplinfo->ssid = schid.ssid;
- iplinfo->devno = schib.pmcw.dev;
- iplinfo->is_qdio = schib.pmcw.qf;
- return 0;
-}
-
/**
* cio_tm_start_key - perform start function
* @sch: subchannel on which to perform the start function
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index f50ea035aa9b..1540229a37bb 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -1073,8 +1073,7 @@ out_schedule:
return 0;
}
-static int
-io_subchannel_remove (struct subchannel *sch)
+static int io_subchannel_remove(struct subchannel *sch)
{
struct io_subchannel_private *io_priv = to_io_private(sch);
struct ccw_device *cdev;
@@ -1082,14 +1081,12 @@ io_subchannel_remove (struct subchannel *sch)
cdev = sch_get_cdev(sch);
if (!cdev)
goto out_free;
- io_subchannel_quiesce(sch);
- /* Set ccw device to not operational and drop reference. */
- spin_lock_irq(cdev->ccwlock);
+
+ ccw_device_unregister(cdev);
+ spin_lock_irq(sch->lock);
sch_set_cdev(sch, NULL);
set_io_private(sch, NULL);
- cdev->private->state = DEV_STATE_NOT_OPER;
- spin_unlock_irq(cdev->ccwlock);
- ccw_device_unregister(cdev);
+ spin_unlock_irq(sch->lock);
out_free:
kfree(io_priv);
sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group);
@@ -1721,6 +1718,7 @@ static int ccw_device_remove(struct device *dev)
{
struct ccw_device *cdev = to_ccwdev(dev);
struct ccw_driver *cdrv = cdev->drv;
+ struct subchannel *sch;
int ret;
if (cdrv->remove)
@@ -1746,7 +1744,9 @@ static int ccw_device_remove(struct device *dev)
ccw_device_set_timeout(cdev, 0);
cdev->drv = NULL;
cdev->private->int_class = IRQIO_CIO;
+ sch = to_subchannel(cdev->dev.parent);
spin_unlock_irq(cdev->ccwlock);
+ io_subchannel_quiesce(sch);
__disable_cmf(cdev);
return 0;
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c
index 75ce12a24dc2..aecfebb74157 100644
--- a/drivers/s390/cio/device_ops.c
+++ b/drivers/s390/cio/device_ops.c
@@ -460,8 +460,8 @@ __u8 ccw_device_get_path_mask(struct ccw_device *cdev)
* On success return a newly allocated copy of the channel-path description
* data associated with the given channel path. Return %NULL on error.
*/
-struct channel_path_desc *ccw_device_get_chp_desc(struct ccw_device *cdev,
- int chp_idx)
+struct channel_path_desc_fmt0 *ccw_device_get_chp_desc(struct ccw_device *cdev,
+ int chp_idx)
{
struct subchannel *sch;
struct chp_id chpid;
diff --git a/drivers/s390/cio/ioasm.c b/drivers/s390/cio/ioasm.c
index 4fa9ee1d09fa..14d328338ce2 100644
--- a/drivers/s390/cio/ioasm.c
+++ b/drivers/s390/cio/ioasm.c
@@ -183,30 +183,6 @@ int chsc(void *chsc_area)
}
EXPORT_SYMBOL(chsc);
-static inline int __rchp(struct chp_id chpid)
-{
- register struct chp_id reg1 asm ("1") = chpid;
- int ccode;
-
- asm volatile(
- " lr 1,%1\n"
- " rchp\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode) : "d" (reg1) : "cc");
- return ccode;
-}
-
-int rchp(struct chp_id chpid)
-{
- int ccode;
-
- ccode = __rchp(chpid);
- trace_s390_cio_rchp(chpid, ccode);
-
- return ccode;
-}
-
static inline int __rsch(struct subchannel_id schid)
{
register struct subchannel_id reg1 asm("1") = schid;
diff --git a/drivers/s390/cio/ioasm.h b/drivers/s390/cio/ioasm.h
index 35ad4ddd61e0..4be539cb9adc 100644
--- a/drivers/s390/cio/ioasm.h
+++ b/drivers/s390/cio/ioasm.h
@@ -20,7 +20,6 @@ int ssch(struct subchannel_id schid, union orb *addr);
int csch(struct subchannel_id schid);
int tpi(struct tpi_info *addr);
int chsc(void *chsc_area);
-int rchp(struct chp_id chpid);
int rsch(struct subchannel_id schid);
int hsch(struct subchannel_id schid);
int xsch(struct subchannel_id schid);
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
index d5b02de02a3a..f4ca72dd862f 100644
--- a/drivers/s390/cio/qdio_main.c
+++ b/drivers/s390/cio/qdio_main.c
@@ -98,22 +98,6 @@ static inline int do_siga_output(unsigned long schid, unsigned long mask,
return cc;
}
-static inline int qdio_check_ccq(struct qdio_q *q, unsigned int ccq)
-{
- /* all done or next buffer state different */
- if (ccq == 0 || ccq == 32)
- return 0;
- /* no buffer processed */
- if (ccq == 97)
- return 1;
- /* not all buffers processed */
- if (ccq == 96)
- return 2;
- /* notify devices immediately */
- DBF_ERROR("%4x ccq:%3d", SCH_NO(q), ccq);
- return -EIO;
-}
-
/**
* qdio_do_eqbs - extract buffer states for QEBSM
* @q: queue to manipulate
@@ -128,7 +112,7 @@ static inline int qdio_check_ccq(struct qdio_q *q, unsigned int ccq)
static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state,
int start, int count, int auto_ack)
{
- int rc, tmp_count = count, tmp_start = start, nr = q->nr, retried = 0;
+ int tmp_count = count, tmp_start = start, nr = q->nr;
unsigned int ccq = 0;
qperf_inc(q, eqbs);
@@ -138,34 +122,30 @@ static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state,
again:
ccq = do_eqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count,
auto_ack);
- rc = qdio_check_ccq(q, ccq);
- if (!rc)
- return count - tmp_count;
- if (rc == 1) {
- DBF_DEV_EVENT(DBF_WARN, q->irq_ptr, "EQBS again:%2d", ccq);
- goto again;
- }
-
- if (rc == 2) {
+ switch (ccq) {
+ case 0:
+ case 32:
+ /* all done, or next buffer state different */
+ return count - tmp_count;
+ case 96:
+ /* not all buffers processed */
qperf_inc(q, eqbs_partial);
DBF_DEV_EVENT(DBF_WARN, q->irq_ptr, "EQBS part:%02x",
tmp_count);
- /*
- * Retry once, if that fails bail out and process the
- * extracted buffers before trying again.
- */
- if (!retried++)
- goto again;
- else
- return count - tmp_count;
+ return count - tmp_count;
+ case 97:
+ /* no buffer processed */
+ DBF_DEV_EVENT(DBF_WARN, q->irq_ptr, "EQBS again:%2d", ccq);
+ goto again;
+ default:
+ DBF_ERROR("%4x ccq:%3d", SCH_NO(q), ccq);
+ DBF_ERROR("%4x EQBS ERROR", SCH_NO(q));
+ DBF_ERROR("%3d%3d%2d", count, tmp_count, nr);
+ q->handler(q->irq_ptr->cdev, QDIO_ERROR_GET_BUF_STATE, q->nr,
+ q->first_to_kick, count, q->irq_ptr->int_parm);
+ return 0;
}
-
- DBF_ERROR("%4x EQBS ERROR", SCH_NO(q));
- DBF_ERROR("%3d%3d%2d", count, tmp_count, nr);
- q->handler(q->irq_ptr->cdev, QDIO_ERROR_GET_BUF_STATE,
- q->nr, q->first_to_kick, count, q->irq_ptr->int_parm);
- return 0;
}
/**
@@ -185,7 +165,6 @@ static int qdio_do_sqbs(struct qdio_q *q, unsigned char state, int start,
unsigned int ccq = 0;
int tmp_count = count, tmp_start = start;
int nr = q->nr;
- int rc;
if (!count)
return 0;
@@ -195,26 +174,32 @@ static int qdio_do_sqbs(struct qdio_q *q, unsigned char state, int start,
nr += q->irq_ptr->nr_input_qs;
again:
ccq = do_sqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count);
- rc = qdio_check_ccq(q, ccq);
- if (!rc) {
+
+ switch (ccq) {
+ case 0:
+ case 32:
+ /* all done, or active buffer adapter-owned */
WARN_ON_ONCE(tmp_count);
return count - tmp_count;
- }
-
- if (rc == 1 || rc == 2) {
+ case 96:
+ /* not all buffers processed */
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "SQBS again:%2d", ccq);
qperf_inc(q, sqbs_partial);
goto again;
+ default:
+ DBF_ERROR("%4x ccq:%3d", SCH_NO(q), ccq);
+ DBF_ERROR("%4x SQBS ERROR", SCH_NO(q));
+ DBF_ERROR("%3d%3d%2d", count, tmp_count, nr);
+ q->handler(q->irq_ptr->cdev, QDIO_ERROR_SET_BUF_STATE, q->nr,
+ q->first_to_kick, count, q->irq_ptr->int_parm);
+ return 0;
}
-
- DBF_ERROR("%4x SQBS ERROR", SCH_NO(q));
- DBF_ERROR("%3d%3d%2d", count, tmp_count, nr);
- q->handler(q->irq_ptr->cdev, QDIO_ERROR_SET_BUF_STATE,
- q->nr, q->first_to_kick, count, q->irq_ptr->int_parm);
- return 0;
}
-/* returns number of examined buffers and their common state in *state */
+/*
+ * Returns number of examined buffers and their common state in *state.
+ * Requested number of buffers-to-examine must be > 0.
+ */
static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr,
unsigned char *state, unsigned int count,
int auto_ack, int merge_pending)
@@ -225,17 +210,23 @@ static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr,
if (is_qebsm(q))
return qdio_do_eqbs(q, state, bufnr, count, auto_ack);
- for (i = 0; i < count; i++) {
- if (!__state) {
- __state = q->slsb.val[bufnr];
- if (merge_pending && __state == SLSB_P_OUTPUT_PENDING)
- __state = SLSB_P_OUTPUT_EMPTY;
- } else if (merge_pending) {
- if ((q->slsb.val[bufnr] & __state) != __state)
- break;
- } else if (q->slsb.val[bufnr] != __state)
- break;
+ /* get initial state: */
+ __state = q->slsb.val[bufnr];
+ if (merge_pending && __state == SLSB_P_OUTPUT_PENDING)
+ __state = SLSB_P_OUTPUT_EMPTY;
+
+ for (i = 1; i < count; i++) {
bufnr = next_buf(bufnr);
+
+ /* merge PENDING into EMPTY: */
+ if (merge_pending &&
+ q->slsb.val[bufnr] == SLSB_P_OUTPUT_PENDING &&
+ __state == SLSB_P_OUTPUT_EMPTY)
+ continue;
+
+ /* stop if next state differs from initial state: */
+ if (q->slsb.val[bufnr] != __state)
+ break;
}
*state = __state;
return i;
@@ -502,8 +493,8 @@ static inline void inbound_primed(struct qdio_q *q, int count)
static int get_inbound_buffer_frontier(struct qdio_q *q)
{
- int count, stop;
unsigned char state = 0;
+ int count;
q->timestamp = get_tod_clock_fast();
@@ -512,9 +503,7 @@ static int get_inbound_buffer_frontier(struct qdio_q *q)
* would return 0.
*/
count = min(atomic_read(&q->nr_buf_used), QDIO_MAX_BUFFERS_MASK);
- stop = add_buf(q->first_to_check, count);
-
- if (q->first_to_check == stop)
+ if (!count)
goto out;
/*
@@ -734,8 +723,8 @@ void qdio_inbound_processing(unsigned long data)
static int get_outbound_buffer_frontier(struct qdio_q *q)
{
- int count, stop;
unsigned char state = 0;
+ int count;
q->timestamp = get_tod_clock_fast();
@@ -751,11 +740,11 @@ static int get_outbound_buffer_frontier(struct qdio_q *q)
* would return 0.
*/
count = min(atomic_read(&q->nr_buf_used), QDIO_MAX_BUFFERS_MASK);
- stop = add_buf(q->first_to_check, count);
- if (q->first_to_check == stop)
+ if (!count)
goto out;
- count = get_buf_states(q, q->first_to_check, &state, count, 0, 1);
+ count = get_buf_states(q, q->first_to_check, &state, count, 0,
+ q->u.out.use_cq);
if (!count)
goto out;
@@ -1218,8 +1207,10 @@ no_cleanup:
qdio_shutdown_thinint(irq_ptr);
/* restore interrupt handler */
- if ((void *)cdev->handler == (void *)qdio_int_handler)
+ if ((void *)cdev->handler == (void *)qdio_int_handler) {
cdev->handler = irq_ptr->orig_handler;
+ cdev->private->intparm = 0;
+ }
spin_unlock_irq(get_ccwdev_lock(cdev));
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE);
diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c
index 98f3cfdc0d02..439991d71b14 100644
--- a/drivers/s390/cio/qdio_setup.c
+++ b/drivers/s390/cio/qdio_setup.c
@@ -507,8 +507,10 @@ int qdio_setup_irq(struct qdio_initialize *init_data)
irq_ptr->aqueue = *ciw;
/* set new interrupt handler */
+ spin_lock_irq(get_ccwdev_lock(irq_ptr->cdev));
irq_ptr->orig_handler = init_data->cdev->handler;
init_data->cdev->handler = qdio_int_handler;
+ spin_unlock_irq(get_ccwdev_lock(irq_ptr->cdev));
return 0;
out_err:
qdio_release_memory(irq_ptr);
diff --git a/drivers/s390/cio/vfio_ccw_fsm.c b/drivers/s390/cio/vfio_ccw_fsm.c
index c30420c517b1..3c800642134e 100644
--- a/drivers/s390/cio/vfio_ccw_fsm.c
+++ b/drivers/s390/cio/vfio_ccw_fsm.c
@@ -20,12 +20,12 @@ static int fsm_io_helper(struct vfio_ccw_private *private)
int ccode;
__u8 lpm;
unsigned long flags;
+ int ret;
sch = private->sch;
spin_lock_irqsave(sch->lock, flags);
private->state = VFIO_CCW_STATE_BUSY;
- spin_unlock_irqrestore(sch->lock, flags);
orb = cp_get_orb(&private->cp, (u32)(addr_t)sch, sch->lpm);
@@ -38,10 +38,12 @@ static int fsm_io_helper(struct vfio_ccw_private *private)
* Initialize device status information
*/
sch->schib.scsw.cmd.actl |= SCSW_ACTL_START_PEND;
- return 0;
+ ret = 0;
+ break;
case 1: /* Status pending */
case 2: /* Busy */
- return -EBUSY;
+ ret = -EBUSY;
+ break;
case 3: /* Device/path not operational */
{
lpm = orb->cmd.lpm;
@@ -51,13 +53,16 @@ static int fsm_io_helper(struct vfio_ccw_private *private)
sch->lpm = 0;
if (cio_update_schib(sch))
- return -ENODEV;
-
- return sch->lpm ? -EACCES : -ENODEV;
+ ret = -ENODEV;
+ else
+ ret = sch->lpm ? -EACCES : -ENODEV;
+ break;
}
default:
- return ccode;
+ ret = ccode;
}
+ spin_unlock_irqrestore(sch->lock, flags);
+ return ret;
}
static void fsm_notoper(struct vfio_ccw_private *private,
@@ -124,6 +129,11 @@ static void fsm_io_request(struct vfio_ccw_private *private,
if (scsw->cmd.fctl & SCSW_FCTL_START_FUNC) {
orb = (union orb *)io_region->orb_area;
+ /* Don't try to build a cp if transport mode is specified. */
+ if (orb->tm.b) {
+ io_region->ret_code = -EOPNOTSUPP;
+ goto err_out;
+ }
io_region->ret_code = cp_init(&private->cp, mdev_dev(mdev),
orb);
if (io_region->ret_code)
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index 48d55dc9e986..35a0c2b52f82 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -25,7 +25,6 @@
#include <linux/kthread.h>
#include <linux/mutex.h>
#include <linux/suspend.h>
-#include <asm/reset.h>
#include <asm/airq.h>
#include <linux/atomic.h>
#include <asm/isc.h>
@@ -1197,26 +1196,7 @@ static void ap_config_timeout(struct timer_list *unused)
queue_work(system_long_wq, &ap_scan_work);
}
-static void ap_reset_all(void)
-{
- int i, j;
-
- for (i = 0; i < AP_DOMAINS; i++) {
- if (!ap_test_config_domain(i))
- continue;
- for (j = 0; j < AP_DEVICES; j++) {
- if (!ap_test_config_card_id(j))
- continue;
- ap_rapq(AP_MKQID(j, i));
- }
- }
-}
-
-static struct reset_call ap_reset_call = {
- .fn = ap_reset_all,
-};
-
-int __init ap_debug_init(void)
+static int __init ap_debug_init(void)
{
ap_dbf_info = debug_register("ap", 1, 1,
DBF_MAX_SPRINTF_ARGS * sizeof(long));
@@ -1226,17 +1206,12 @@ int __init ap_debug_init(void)
return 0;
}
-void ap_debug_exit(void)
-{
- debug_unregister(ap_dbf_info);
-}
-
/**
* ap_module_init(): The module initialization code.
*
* Initializes the module.
*/
-int __init ap_module_init(void)
+static int __init ap_module_init(void)
{
int max_domain_id;
int rc, i;
@@ -1274,8 +1249,6 @@ int __init ap_module_init(void)
ap_airq_flag = (rc == 0);
}
- register_reset_call(&ap_reset_call);
-
/* Create /sys/bus/ap. */
rc = bus_register(&ap_bus_type);
if (rc)
@@ -1331,7 +1304,6 @@ out_bus:
bus_remove_file(&ap_bus_type, ap_bus_attrs[i]);
bus_unregister(&ap_bus_type);
out:
- unregister_reset_call(&ap_reset_call);
if (ap_using_interrupts())
unregister_adapter_interrupt(&ap_airq);
kfree(ap_configuration);
diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h
index e0827eaa42f1..02184cf35834 100644
--- a/drivers/s390/crypto/ap_bus.h
+++ b/drivers/s390/crypto/ap_bus.h
@@ -17,7 +17,7 @@
#include <linux/types.h>
#include <asm/ap.h>
-#define AP_DEVICES 64 /* Number of AP devices. */
+#define AP_DEVICES 256 /* Number of AP devices. */
#define AP_DOMAINS 256 /* Number of AP domains. */
#define AP_RESET_TIMEOUT (HZ*0.7) /* Time in ticks for reset timeouts. */
#define AP_CONFIG_TIME 30 /* Time in seconds between AP bus rescans. */
@@ -240,7 +240,4 @@ void ap_queue_resume(struct ap_device *ap_dev);
struct ap_card *ap_card_create(int id, int queue_depth, int raw_device_type,
int comp_device_type, unsigned int functions);
-int ap_module_init(void);
-void ap_module_exit(void);
-
#endif /* _AP_BUS_H_ */
diff --git a/drivers/s390/crypto/ap_debug.h b/drivers/s390/crypto/ap_debug.h
index 6a9d77c75ec3..dc675eb5aef6 100644
--- a/drivers/s390/crypto/ap_debug.h
+++ b/drivers/s390/crypto/ap_debug.h
@@ -23,7 +23,4 @@
extern debug_info_t *ap_dbf_info;
-int ap_debug_init(void);
-void ap_debug_exit(void);
-
#endif /* AP_DEBUG_H */
diff --git a/drivers/s390/crypto/pkey_api.c b/drivers/s390/crypto/pkey_api.c
index e7c2e4f9529a..ed80d00cdb6f 100644
--- a/drivers/s390/crypto/pkey_api.c
+++ b/drivers/s390/crypto/pkey_api.c
@@ -889,7 +889,7 @@ int pkey_findcard(const struct pkey_seckey *seckey,
u16 *pcardnr, u16 *pdomain, int verify)
{
struct secaeskeytoken *t = (struct secaeskeytoken *) seckey;
- struct zcrypt_device_matrix *device_matrix;
+ struct zcrypt_device_status_ext *device_status;
u16 card, dom;
u64 mkvp[2];
int i, rc, oi = -1;
@@ -899,18 +899,19 @@ int pkey_findcard(const struct pkey_seckey *seckey,
return -EINVAL;
/* fetch status of all crypto cards */
- device_matrix = kmalloc(sizeof(struct zcrypt_device_matrix),
+ device_status = kmalloc(MAX_ZDEV_ENTRIES_EXT
+ * sizeof(struct zcrypt_device_status_ext),
GFP_KERNEL);
- if (!device_matrix)
+ if (!device_status)
return -ENOMEM;
- zcrypt_device_status_mask(device_matrix);
+ zcrypt_device_status_mask_ext(device_status);
/* walk through all crypto cards */
- for (i = 0; i < MAX_ZDEV_ENTRIES; i++) {
- card = AP_QID_CARD(device_matrix->device[i].qid);
- dom = AP_QID_QUEUE(device_matrix->device[i].qid);
- if (device_matrix->device[i].online &&
- device_matrix->device[i].functions & 0x04) {
+ for (i = 0; i < MAX_ZDEV_ENTRIES_EXT; i++) {
+ card = AP_QID_CARD(device_status[i].qid);
+ dom = AP_QID_QUEUE(device_status[i].qid);
+ if (device_status[i].online &&
+ device_status[i].functions & 0x04) {
/* an enabled CCA Coprocessor card */
/* try cached mkvp */
if (mkvp_cache_fetch(card, dom, mkvp) == 0 &&
@@ -930,14 +931,14 @@ int pkey_findcard(const struct pkey_seckey *seckey,
mkvp_cache_scrub(card, dom);
}
}
- if (i >= MAX_ZDEV_ENTRIES) {
+ if (i >= MAX_ZDEV_ENTRIES_EXT) {
/* nothing found, so this time without cache */
- for (i = 0; i < MAX_ZDEV_ENTRIES; i++) {
- if (!(device_matrix->device[i].online &&
- device_matrix->device[i].functions & 0x04))
+ for (i = 0; i < MAX_ZDEV_ENTRIES_EXT; i++) {
+ if (!(device_status[i].online &&
+ device_status[i].functions & 0x04))
continue;
- card = AP_QID_CARD(device_matrix->device[i].qid);
- dom = AP_QID_QUEUE(device_matrix->device[i].qid);
+ card = AP_QID_CARD(device_status[i].qid);
+ dom = AP_QID_QUEUE(device_status[i].qid);
/* fresh fetch mkvp from adapter */
if (fetch_mkvp(card, dom, mkvp) == 0) {
mkvp_cache_update(card, dom, mkvp);
@@ -947,13 +948,13 @@ int pkey_findcard(const struct pkey_seckey *seckey,
oi = i;
}
}
- if (i >= MAX_ZDEV_ENTRIES && oi >= 0) {
+ if (i >= MAX_ZDEV_ENTRIES_EXT && oi >= 0) {
/* old mkvp matched, use this card then */
- card = AP_QID_CARD(device_matrix->device[oi].qid);
- dom = AP_QID_QUEUE(device_matrix->device[oi].qid);
+ card = AP_QID_CARD(device_status[oi].qid);
+ dom = AP_QID_QUEUE(device_status[oi].qid);
}
}
- if (i < MAX_ZDEV_ENTRIES || oi >= 0) {
+ if (i < MAX_ZDEV_ENTRIES_EXT || oi >= 0) {
if (pcardnr)
*pcardnr = card;
if (pdomain)
@@ -962,7 +963,7 @@ int pkey_findcard(const struct pkey_seckey *seckey,
} else
rc = -ENODEV;
- kfree(device_matrix);
+ kfree(device_status);
return rc;
}
EXPORT_SYMBOL(pkey_findcard);
diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c
index ce15f101ee28..5efd84862ccb 100644
--- a/drivers/s390/crypto/zcrypt_api.c
+++ b/drivers/s390/crypto/zcrypt_api.c
@@ -18,8 +18,6 @@
#include <linux/interrupt.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
#include <linux/compat.h>
#include <linux/slab.h>
#include <linux/atomic.h>
@@ -607,19 +605,24 @@ out:
return rc;
}
-void zcrypt_device_status_mask(struct zcrypt_device_matrix *matrix)
+static void zcrypt_device_status_mask(struct zcrypt_device_status *devstatus)
{
struct zcrypt_card *zc;
struct zcrypt_queue *zq;
struct zcrypt_device_status *stat;
+ int card, queue;
+
+ memset(devstatus, 0, MAX_ZDEV_ENTRIES
+ * sizeof(struct zcrypt_device_status));
- memset(matrix, 0, sizeof(*matrix));
spin_lock(&zcrypt_list_lock);
for_each_zcrypt_card(zc) {
for_each_zcrypt_queue(zq, zc) {
- stat = matrix->device;
- stat += AP_QID_CARD(zq->queue->qid) * MAX_ZDEV_DOMAINS;
- stat += AP_QID_QUEUE(zq->queue->qid);
+ card = AP_QID_CARD(zq->queue->qid);
+ if (card >= MAX_ZDEV_CARDIDS)
+ continue;
+ queue = AP_QID_QUEUE(zq->queue->qid);
+ stat = &devstatus[card * AP_DOMAINS + queue];
stat->hwtype = zc->card->ap_dev.device_type;
stat->functions = zc->card->functions >> 26;
stat->qid = zq->queue->qid;
@@ -628,40 +631,70 @@ void zcrypt_device_status_mask(struct zcrypt_device_matrix *matrix)
}
spin_unlock(&zcrypt_list_lock);
}
-EXPORT_SYMBOL(zcrypt_device_status_mask);
-static void zcrypt_status_mask(char status[AP_DEVICES])
+void zcrypt_device_status_mask_ext(struct zcrypt_device_status_ext *devstatus)
{
struct zcrypt_card *zc;
struct zcrypt_queue *zq;
+ struct zcrypt_device_status_ext *stat;
+ int card, queue;
+
+ memset(devstatus, 0, MAX_ZDEV_ENTRIES_EXT
+ * sizeof(struct zcrypt_device_status_ext));
- memset(status, 0, sizeof(char) * AP_DEVICES);
spin_lock(&zcrypt_list_lock);
for_each_zcrypt_card(zc) {
for_each_zcrypt_queue(zq, zc) {
- if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index)
+ card = AP_QID_CARD(zq->queue->qid);
+ queue = AP_QID_QUEUE(zq->queue->qid);
+ stat = &devstatus[card * AP_DOMAINS + queue];
+ stat->hwtype = zc->card->ap_dev.device_type;
+ stat->functions = zc->card->functions >> 26;
+ stat->qid = zq->queue->qid;
+ stat->online = zq->online ? 0x01 : 0x00;
+ }
+ }
+ spin_unlock(&zcrypt_list_lock);
+}
+EXPORT_SYMBOL(zcrypt_device_status_mask_ext);
+
+static void zcrypt_status_mask(char status[], size_t max_adapters)
+{
+ struct zcrypt_card *zc;
+ struct zcrypt_queue *zq;
+ int card;
+
+ memset(status, 0, max_adapters);
+ spin_lock(&zcrypt_list_lock);
+ for_each_zcrypt_card(zc) {
+ for_each_zcrypt_queue(zq, zc) {
+ card = AP_QID_CARD(zq->queue->qid);
+ if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index
+ || card >= max_adapters)
continue;
- status[AP_QID_CARD(zq->queue->qid)] =
- zc->online ? zc->user_space_type : 0x0d;
+ status[card] = zc->online ? zc->user_space_type : 0x0d;
}
}
spin_unlock(&zcrypt_list_lock);
}
-static void zcrypt_qdepth_mask(char qdepth[AP_DEVICES])
+static void zcrypt_qdepth_mask(char qdepth[], size_t max_adapters)
{
struct zcrypt_card *zc;
struct zcrypt_queue *zq;
+ int card;
- memset(qdepth, 0, sizeof(char) * AP_DEVICES);
+ memset(qdepth, 0, max_adapters);
spin_lock(&zcrypt_list_lock);
local_bh_disable();
for_each_zcrypt_card(zc) {
for_each_zcrypt_queue(zq, zc) {
- if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index)
+ card = AP_QID_CARD(zq->queue->qid);
+ if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index
+ || card >= max_adapters)
continue;
spin_lock(&zq->queue->lock);
- qdepth[AP_QID_CARD(zq->queue->qid)] =
+ qdepth[card] =
zq->queue->pendingq_count +
zq->queue->requestq_count;
spin_unlock(&zq->queue->lock);
@@ -671,21 +704,23 @@ static void zcrypt_qdepth_mask(char qdepth[AP_DEVICES])
spin_unlock(&zcrypt_list_lock);
}
-static void zcrypt_perdev_reqcnt(int reqcnt[AP_DEVICES])
+static void zcrypt_perdev_reqcnt(int reqcnt[], size_t max_adapters)
{
struct zcrypt_card *zc;
struct zcrypt_queue *zq;
+ int card;
- memset(reqcnt, 0, sizeof(int) * AP_DEVICES);
+ memset(reqcnt, 0, sizeof(int) * max_adapters);
spin_lock(&zcrypt_list_lock);
local_bh_disable();
for_each_zcrypt_card(zc) {
for_each_zcrypt_queue(zq, zc) {
- if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index)
+ card = AP_QID_CARD(zq->queue->qid);
+ if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index
+ || card >= max_adapters)
continue;
spin_lock(&zq->queue->lock);
- reqcnt[AP_QID_CARD(zq->queue->qid)] =
- zq->queue->total_request_count;
+ reqcnt[card] = zq->queue->total_request_count;
spin_unlock(&zq->queue->lock);
}
}
@@ -739,60 +774,10 @@ static int zcrypt_requestq_count(void)
return requestq_count;
}
-static int zcrypt_count_type(int type)
-{
- struct zcrypt_card *zc;
- struct zcrypt_queue *zq;
- int device_count;
-
- device_count = 0;
- spin_lock(&zcrypt_list_lock);
- for_each_zcrypt_card(zc) {
- if (zc->card->id != type)
- continue;
- for_each_zcrypt_queue(zq, zc) {
- if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index)
- continue;
- device_count++;
- }
- }
- spin_unlock(&zcrypt_list_lock);
- return device_count;
-}
-
-/**
- * zcrypt_ica_status(): Old, depracted combi status call.
- *
- * Old, deprecated combi status call.
- */
-static long zcrypt_ica_status(struct file *filp, unsigned long arg)
-{
- struct ica_z90_status *pstat;
- int ret;
-
- pstat = kzalloc(sizeof(*pstat), GFP_KERNEL);
- if (!pstat)
- return -ENOMEM;
- pstat->totalcount = zcrypt_device_count;
- pstat->leedslitecount = zcrypt_count_type(ZCRYPT_PCICA);
- pstat->leeds2count = zcrypt_count_type(ZCRYPT_PCICC);
- pstat->requestqWaitCount = zcrypt_requestq_count();
- pstat->pendingqWaitCount = zcrypt_pendingq_count();
- pstat->totalOpenCount = atomic_read(&zcrypt_open_count);
- pstat->cryptoDomain = ap_domain_index;
- zcrypt_status_mask(pstat->status);
- zcrypt_qdepth_mask(pstat->qdepth);
- ret = 0;
- if (copy_to_user((void __user *) arg, pstat, sizeof(*pstat)))
- ret = -EFAULT;
- kfree(pstat);
- return ret;
-}
-
static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
- int rc;
+ int rc = 0;
switch (cmd) {
case ICARSAMODEXPO: {
@@ -871,48 +856,48 @@ static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd,
return -EFAULT;
return rc;
}
- case ZDEVICESTATUS: {
- struct zcrypt_device_matrix *device_status;
+ case ZCRYPT_DEVICE_STATUS: {
+ struct zcrypt_device_status_ext *device_status;
+ size_t total_size = MAX_ZDEV_ENTRIES_EXT
+ * sizeof(struct zcrypt_device_status_ext);
- device_status = kzalloc(sizeof(struct zcrypt_device_matrix),
- GFP_KERNEL);
+ device_status = kzalloc(total_size, GFP_KERNEL);
if (!device_status)
return -ENOMEM;
-
- zcrypt_device_status_mask(device_status);
-
+ zcrypt_device_status_mask_ext(device_status);
if (copy_to_user((char __user *) arg, device_status,
- sizeof(struct zcrypt_device_matrix))) {
- kfree(device_status);
- return -EFAULT;
- }
-
+ total_size))
+ rc = -EFAULT;
kfree(device_status);
- return 0;
+ return rc;
}
- case Z90STAT_STATUS_MASK: {
+ case ZCRYPT_STATUS_MASK: {
char status[AP_DEVICES];
- zcrypt_status_mask(status);
- if (copy_to_user((char __user *) arg, status,
- sizeof(char) * AP_DEVICES))
+
+ zcrypt_status_mask(status, AP_DEVICES);
+ if (copy_to_user((char __user *) arg, status, sizeof(status)))
return -EFAULT;
return 0;
}
- case Z90STAT_QDEPTH_MASK: {
+ case ZCRYPT_QDEPTH_MASK: {
char qdepth[AP_DEVICES];
- zcrypt_qdepth_mask(qdepth);
- if (copy_to_user((char __user *) arg, qdepth,
- sizeof(char) * AP_DEVICES))
+
+ zcrypt_qdepth_mask(qdepth, AP_DEVICES);
+ if (copy_to_user((char __user *) arg, qdepth, sizeof(qdepth)))
return -EFAULT;
return 0;
}
- case Z90STAT_PERDEV_REQCNT: {
- int reqcnt[AP_DEVICES];
- zcrypt_perdev_reqcnt(reqcnt);
- if (copy_to_user((int __user *) arg, reqcnt,
- sizeof(int) * AP_DEVICES))
- return -EFAULT;
- return 0;
+ case ZCRYPT_PERDEV_REQCNT: {
+ int *reqcnt;
+
+ reqcnt = kcalloc(AP_DEVICES, sizeof(int), GFP_KERNEL);
+ if (!reqcnt)
+ return -ENOMEM;
+ zcrypt_perdev_reqcnt(reqcnt, AP_DEVICES);
+ if (copy_to_user((int __user *) arg, reqcnt, sizeof(reqcnt)))
+ rc = -EFAULT;
+ kfree(reqcnt);
+ return rc;
}
case Z90STAT_REQUESTQ_COUNT:
return put_user(zcrypt_requestq_count(), (int __user *) arg);
@@ -924,38 +909,54 @@ static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd,
case Z90STAT_DOMAIN_INDEX:
return put_user(ap_domain_index, (int __user *) arg);
/*
- * Deprecated ioctls. Don't add another device count ioctl,
- * you can count them yourself in the user space with the
- * output of the Z90STAT_STATUS_MASK ioctl.
+ * Deprecated ioctls
*/
- case ICAZ90STATUS:
- return zcrypt_ica_status(filp, arg);
- case Z90STAT_TOTALCOUNT:
- return put_user(zcrypt_device_count, (int __user *) arg);
- case Z90STAT_PCICACOUNT:
- return put_user(zcrypt_count_type(ZCRYPT_PCICA),
- (int __user *) arg);
- case Z90STAT_PCICCCOUNT:
- return put_user(zcrypt_count_type(ZCRYPT_PCICC),
- (int __user *) arg);
- case Z90STAT_PCIXCCMCL2COUNT:
- return put_user(zcrypt_count_type(ZCRYPT_PCIXCC_MCL2),
- (int __user *) arg);
- case Z90STAT_PCIXCCMCL3COUNT:
- return put_user(zcrypt_count_type(ZCRYPT_PCIXCC_MCL3),
- (int __user *) arg);
- case Z90STAT_PCIXCCCOUNT:
- return put_user(zcrypt_count_type(ZCRYPT_PCIXCC_MCL2) +
- zcrypt_count_type(ZCRYPT_PCIXCC_MCL3),
- (int __user *) arg);
- case Z90STAT_CEX2CCOUNT:
- return put_user(zcrypt_count_type(ZCRYPT_CEX2C),
- (int __user *) arg);
- case Z90STAT_CEX2ACOUNT:
- return put_user(zcrypt_count_type(ZCRYPT_CEX2A),
- (int __user *) arg);
+ case ZDEVICESTATUS: {
+ /* the old ioctl supports only 64 adapters */
+ struct zcrypt_device_status *device_status;
+ size_t total_size = MAX_ZDEV_ENTRIES
+ * sizeof(struct zcrypt_device_status);
+
+ device_status = kzalloc(total_size, GFP_KERNEL);
+ if (!device_status)
+ return -ENOMEM;
+ zcrypt_device_status_mask(device_status);
+ if (copy_to_user((char __user *) arg, device_status,
+ total_size))
+ rc = -EFAULT;
+ kfree(device_status);
+ return rc;
+ }
+ case Z90STAT_STATUS_MASK: {
+ /* the old ioctl supports only 64 adapters */
+ char status[MAX_ZDEV_CARDIDS];
+
+ zcrypt_status_mask(status, MAX_ZDEV_CARDIDS);
+ if (copy_to_user((char __user *) arg, status, sizeof(status)))
+ return -EFAULT;
+ return 0;
+ }
+ case Z90STAT_QDEPTH_MASK: {
+ /* the old ioctl supports only 64 adapters */
+ char qdepth[MAX_ZDEV_CARDIDS];
+
+ zcrypt_qdepth_mask(qdepth, MAX_ZDEV_CARDIDS);
+ if (copy_to_user((char __user *) arg, qdepth, sizeof(qdepth)))
+ return -EFAULT;
+ return 0;
+ }
+ case Z90STAT_PERDEV_REQCNT: {
+ /* the old ioctl supports only 64 adapters */
+ int reqcnt[MAX_ZDEV_CARDIDS];
+
+ zcrypt_perdev_reqcnt(reqcnt, MAX_ZDEV_CARDIDS);
+ if (copy_to_user((int __user *) arg, reqcnt, sizeof(reqcnt)))
+ return -EFAULT;
+ return 0;
+ }
+ /* unknown ioctl number */
default:
- /* unknown ioctl number */
+ ZCRYPT_DBF(DBF_DEBUG, "unknown ioctl 0x%08x\n", cmd);
return -ENOIOCTLCMD;
}
}
@@ -1152,201 +1153,6 @@ static struct miscdevice zcrypt_misc_device = {
.fops = &zcrypt_fops,
};
-/*
- * Deprecated /proc entry support.
- */
-static struct proc_dir_entry *zcrypt_entry;
-
-static void sprintcl(struct seq_file *m, unsigned char *addr, unsigned int len)
-{
- int i;
-
- for (i = 0; i < len; i++)
- seq_printf(m, "%01x", (unsigned int) addr[i]);
- seq_putc(m, ' ');
-}
-
-static void sprintrw(struct seq_file *m, unsigned char *addr, unsigned int len)
-{
- int inl, c, cx;
-
- seq_printf(m, " ");
- inl = 0;
- for (c = 0; c < (len / 16); c++) {
- sprintcl(m, addr+inl, 16);
- inl += 16;
- }
- cx = len%16;
- if (cx) {
- sprintcl(m, addr+inl, cx);
- inl += cx;
- }
- seq_putc(m, '\n');
-}
-
-static void sprinthx(unsigned char *title, struct seq_file *m,
- unsigned char *addr, unsigned int len)
-{
- int inl, r, rx;
-
- seq_printf(m, "\n%s\n", title);
- inl = 0;
- for (r = 0; r < (len / 64); r++) {
- sprintrw(m, addr+inl, 64);
- inl += 64;
- }
- rx = len % 64;
- if (rx) {
- sprintrw(m, addr+inl, rx);
- inl += rx;
- }
- seq_putc(m, '\n');
-}
-
-static void sprinthx4(unsigned char *title, struct seq_file *m,
- unsigned int *array, unsigned int len)
-{
- seq_printf(m, "\n%s\n", title);
- seq_hex_dump(m, " ", DUMP_PREFIX_NONE, 32, 4, array, len, false);
- seq_putc(m, '\n');
-}
-
-static int zcrypt_proc_show(struct seq_file *m, void *v)
-{
- char workarea[sizeof(int) * AP_DEVICES];
-
- seq_printf(m, "\nzcrypt version: %d.%d.%d\n",
- ZCRYPT_VERSION, ZCRYPT_RELEASE, ZCRYPT_VARIANT);
- seq_printf(m, "Cryptographic domain: %d\n", ap_domain_index);
- seq_printf(m, "Total device count: %d\n", zcrypt_device_count);
- seq_printf(m, "PCICA count: %d\n", zcrypt_count_type(ZCRYPT_PCICA));
- seq_printf(m, "PCICC count: %d\n", zcrypt_count_type(ZCRYPT_PCICC));
- seq_printf(m, "PCIXCC MCL2 count: %d\n",
- zcrypt_count_type(ZCRYPT_PCIXCC_MCL2));
- seq_printf(m, "PCIXCC MCL3 count: %d\n",
- zcrypt_count_type(ZCRYPT_PCIXCC_MCL3));
- seq_printf(m, "CEX2C count: %d\n", zcrypt_count_type(ZCRYPT_CEX2C));
- seq_printf(m, "CEX2A count: %d\n", zcrypt_count_type(ZCRYPT_CEX2A));
- seq_printf(m, "CEX3C count: %d\n", zcrypt_count_type(ZCRYPT_CEX3C));
- seq_printf(m, "CEX3A count: %d\n", zcrypt_count_type(ZCRYPT_CEX3A));
- seq_printf(m, "requestq count: %d\n", zcrypt_requestq_count());
- seq_printf(m, "pendingq count: %d\n", zcrypt_pendingq_count());
- seq_printf(m, "Total open handles: %d\n\n",
- atomic_read(&zcrypt_open_count));
- zcrypt_status_mask(workarea);
- sprinthx("Online devices: 1=PCICA 2=PCICC 3=PCIXCC(MCL2) "
- "4=PCIXCC(MCL3) 5=CEX2C 6=CEX2A 7=CEX3C 8=CEX3A",
- m, workarea, AP_DEVICES);
- zcrypt_qdepth_mask(workarea);
- sprinthx("Waiting work element counts", m, workarea, AP_DEVICES);
- zcrypt_perdev_reqcnt((int *) workarea);
- sprinthx4("Per-device successfully completed request counts",
- m, (unsigned int *) workarea, AP_DEVICES);
- return 0;
-}
-
-static int zcrypt_proc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, zcrypt_proc_show, NULL);
-}
-
-static void zcrypt_disable_card(int index)
-{
- struct zcrypt_card *zc;
- struct zcrypt_queue *zq;
-
- spin_lock(&zcrypt_list_lock);
- for_each_zcrypt_card(zc) {
- for_each_zcrypt_queue(zq, zc) {
- if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index)
- continue;
- zq->online = 0;
- ap_flush_queue(zq->queue);
- }
- }
- spin_unlock(&zcrypt_list_lock);
-}
-
-static void zcrypt_enable_card(int index)
-{
- struct zcrypt_card *zc;
- struct zcrypt_queue *zq;
-
- spin_lock(&zcrypt_list_lock);
- for_each_zcrypt_card(zc) {
- for_each_zcrypt_queue(zq, zc) {
- if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index)
- continue;
- zq->online = 1;
- ap_flush_queue(zq->queue);
- }
- }
- spin_unlock(&zcrypt_list_lock);
-}
-
-static ssize_t zcrypt_proc_write(struct file *file, const char __user *buffer,
- size_t count, loff_t *pos)
-{
- unsigned char *lbuf, *ptr;
- size_t local_count;
- int j;
-
- if (count <= 0)
- return 0;
-
-#define LBUFSIZE 1200UL
- lbuf = kmalloc(LBUFSIZE, GFP_KERNEL);
- if (!lbuf)
- return 0;
-
- local_count = min(LBUFSIZE - 1, count);
- if (copy_from_user(lbuf, buffer, local_count) != 0) {
- kfree(lbuf);
- return -EFAULT;
- }
- lbuf[local_count] = '\0';
-
- ptr = strstr(lbuf, "Online devices");
- if (!ptr)
- goto out;
- ptr = strstr(ptr, "\n");
- if (!ptr)
- goto out;
- ptr++;
-
- if (strstr(ptr, "Waiting work element counts") == NULL)
- goto out;
-
- for (j = 0; j < 64 && *ptr; ptr++) {
- /*
- * '0' for no device, '1' for PCICA, '2' for PCICC,
- * '3' for PCIXCC_MCL2, '4' for PCIXCC_MCL3,
- * '5' for CEX2C and '6' for CEX2A'
- * '7' for CEX3C and '8' for CEX3A
- */
- if (*ptr >= '0' && *ptr <= '8')
- j++;
- else if (*ptr == 'd' || *ptr == 'D')
- zcrypt_disable_card(j++);
- else if (*ptr == 'e' || *ptr == 'E')
- zcrypt_enable_card(j++);
- else if (*ptr != ' ' && *ptr != '\t')
- break;
- }
-out:
- kfree(lbuf);
- return count;
-}
-
-static const struct file_operations zcrypt_proc_fops = {
- .owner = THIS_MODULE,
- .open = zcrypt_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .write = zcrypt_proc_write,
-};
-
static int zcrypt_rng_device_count;
static u32 *zcrypt_rng_buffer;
static int zcrypt_rng_buffer_index;
@@ -1448,27 +1254,15 @@ int __init zcrypt_api_init(void)
if (rc)
goto out;
- atomic_set(&zcrypt_rescan_req, 0);
-
/* Register the request sprayer. */
rc = misc_register(&zcrypt_misc_device);
if (rc < 0)
goto out;
- /* Set up the proc file system */
- zcrypt_entry = proc_create("driver/z90crypt", 0644, NULL,
- &zcrypt_proc_fops);
- if (!zcrypt_entry) {
- rc = -ENOMEM;
- goto out_misc;
- }
-
zcrypt_msgtype6_init();
zcrypt_msgtype50_init();
return 0;
-out_misc:
- misc_deregister(&zcrypt_misc_device);
out:
return rc;
}
@@ -1480,7 +1274,6 @@ out:
*/
void __exit zcrypt_api_exit(void)
{
- remove_proc_entry("driver/z90crypt", NULL);
misc_deregister(&zcrypt_misc_device);
zcrypt_msgtype6_exit();
zcrypt_msgtype50_exit();
diff --git a/drivers/s390/crypto/zcrypt_api.h b/drivers/s390/crypto/zcrypt_api.h
index 9fff8912f6e3..f149a8fee60d 100644
--- a/drivers/s390/crypto/zcrypt_api.h
+++ b/drivers/s390/crypto/zcrypt_api.h
@@ -21,30 +21,6 @@
#include <asm/zcrypt.h>
#include "ap_bus.h"
-/* deprecated status calls */
-#define ICAZ90STATUS _IOR(ZCRYPT_IOCTL_MAGIC, 0x10, struct ica_z90_status)
-#define Z90STAT_PCIXCCCOUNT _IOR(ZCRYPT_IOCTL_MAGIC, 0x43, int)
-
-/**
- * This structure is deprecated and the corresponding ioctl() has been
- * replaced with individual ioctl()s for each piece of data!
- */
-struct ica_z90_status {
- int totalcount;
- int leedslitecount; // PCICA
- int leeds2count; // PCICC
- // int PCIXCCCount; is not in struct for backward compatibility
- int requestqWaitCount;
- int pendingqWaitCount;
- int totalOpenCount;
- int cryptoDomain;
- // status: 0=not there, 1=PCICA, 2=PCICC, 3=PCIXCC_MCL2, 4=PCIXCC_MCL3,
- // 5=CEX2C
- unsigned char status[64];
- // qdepth: # work elements waiting for each device
- unsigned char qdepth[64];
-};
-
/**
* device type for an actual device is either PCICA, PCICC, PCIXCC_MCL2,
* PCIXCC_MCL3, CEX2C, or CEX2A
@@ -179,6 +155,6 @@ struct zcrypt_ops *zcrypt_msgtype(unsigned char *, int);
int zcrypt_api_init(void);
void zcrypt_api_exit(void);
long zcrypt_send_cprb(struct ica_xcRB *xcRB);
-void zcrypt_device_status_mask(struct zcrypt_device_matrix *devstatus);
+void zcrypt_device_status_mask_ext(struct zcrypt_device_status_ext *devstatus);
#endif /* _ZCRYPT_API_H_ */
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h
index 959c65cf75d9..78b98b3e7efa 100644
--- a/drivers/s390/net/qeth_core.h
+++ b/drivers/s390/net/qeth_core.h
@@ -233,8 +233,6 @@ static inline int qeth_is_ipa_enabled(struct qeth_ipa_info *ipa,
#define QETH_IDX_FUNC_LEVEL_OSD 0x0101
#define QETH_IDX_FUNC_LEVEL_IQD 0x4108
-#define QETH_REAL_CARD 1
-#define QETH_VLAN_CARD 2
#define QETH_BUFSIZE 4096
/**
@@ -556,16 +554,9 @@ enum qeth_prot_versions {
QETH_PROT_IPV6 = 0x0006,
};
-enum qeth_ip_types {
- QETH_IP_TYPE_NORMAL,
- QETH_IP_TYPE_VIPA,
- QETH_IP_TYPE_RXIP,
-};
-
enum qeth_cmd_buffer_state {
BUF_STATE_FREE,
BUF_STATE_LOCKED,
- BUF_STATE_PROCESSED,
};
enum qeth_cq {
@@ -609,7 +600,6 @@ struct qeth_channel {
struct qeth_cmd_buffer iob[QETH_CMD_BUFFER_NO];
atomic_t irq_pending;
int io_buf_no;
- int buf_no;
};
/**
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index 3653bea38470..dffd820731f2 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -706,7 +706,6 @@ void qeth_clear_ipacmd_list(struct qeth_card *card)
qeth_put_reply(reply);
}
spin_unlock_irqrestore(&card->lock, flags);
- atomic_set(&card->write.irq_pending, 0);
}
EXPORT_SYMBOL_GPL(qeth_clear_ipacmd_list);
@@ -718,11 +717,8 @@ static int qeth_check_idx_response(struct qeth_card *card,
QETH_DBF_HEX(CTRL, 2, buffer, QETH_DBF_CTRL_LEN);
if ((buffer[2] & 0xc0) == 0xc0) {
- QETH_DBF_MESSAGE(2, "received an IDX TERMINATE "
- "with cause code 0x%02x%s\n",
- buffer[4],
- ((buffer[4] == 0x22) ?
- " -- try another portname" : ""));
+ QETH_DBF_MESSAGE(2, "received an IDX TERMINATE with cause code %#02x\n",
+ buffer[4]);
QETH_CARD_TEXT(card, 2, "ckidxres");
QETH_CARD_TEXT(card, 2, " idxterm");
QETH_CARD_TEXT_(card, 2, " rc%d", -EIO);
@@ -821,7 +817,6 @@ void qeth_clear_cmd_buffers(struct qeth_channel *channel)
for (cnt = 0; cnt < QETH_CMD_BUFFER_NO; cnt++)
qeth_release_buffer(channel, &channel->iob[cnt]);
- channel->buf_no = 0;
channel->io_buf_no = 0;
}
EXPORT_SYMBOL_GPL(qeth_clear_cmd_buffers);
@@ -927,7 +922,6 @@ static int qeth_setup_channel(struct qeth_channel *channel)
kfree(channel->iob[cnt].data);
return -ENOMEM;
}
- channel->buf_no = 0;
channel->io_buf_no = 0;
atomic_set(&channel->irq_pending, 0);
spin_lock_init(&channel->iob_lock);
@@ -1103,16 +1097,9 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm,
{
int rc;
int cstat, dstat;
- struct qeth_cmd_buffer *buffer;
+ struct qeth_cmd_buffer *iob = NULL;
struct qeth_channel *channel;
struct qeth_card *card;
- struct qeth_cmd_buffer *iob;
- __u8 index;
-
- if (__qeth_check_irb_error(cdev, intparm, irb))
- return;
- cstat = irb->scsw.cmd.cstat;
- dstat = irb->scsw.cmd.dstat;
card = CARD_FROM_CDEV(cdev);
if (!card)
@@ -1130,6 +1117,19 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm,
channel = &card->data;
QETH_CARD_TEXT(card, 5, "data");
}
+
+ if (qeth_intparm_is_iob(intparm))
+ iob = (struct qeth_cmd_buffer *) __va((addr_t)intparm);
+
+ if (__qeth_check_irb_error(cdev, intparm, irb)) {
+ /* IO was terminated, free its resources. */
+ if (iob)
+ qeth_release_buffer(iob->channel, iob);
+ atomic_set(&channel->irq_pending, 0);
+ wake_up(&card->wait_q);
+ return;
+ }
+
atomic_set(&channel->irq_pending, 0);
if (irb->scsw.cmd.fctl & (SCSW_FCTL_CLEAR_FUNC))
@@ -1153,6 +1153,10 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm,
/* we don't have to handle this further */
intparm = 0;
}
+
+ cstat = irb->scsw.cmd.cstat;
+ dstat = irb->scsw.cmd.dstat;
+
if ((dstat & DEV_STAT_UNIT_EXCEP) ||
(dstat & DEV_STAT_UNIT_CHECK) ||
(cstat)) {
@@ -1185,25 +1189,15 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm,
channel->state = CH_STATE_RCD_DONE;
goto out;
}
- if (intparm) {
- buffer = (struct qeth_cmd_buffer *) __va((addr_t)intparm);
- buffer->state = BUF_STATE_PROCESSED;
- }
if (channel == &card->data)
return;
if (channel == &card->read &&
channel->state == CH_STATE_UP)
__qeth_issue_next_read(card);
- iob = channel->iob;
- index = channel->buf_no;
- while (iob[index].state == BUF_STATE_PROCESSED) {
- if (iob[index].callback != NULL)
- iob[index].callback(channel, iob + index);
+ if (iob && iob->callback)
+ iob->callback(iob->channel, iob);
- index = (index + 1) % QETH_CMD_BUFFER_NO;
- }
- channel->buf_no = index;
out:
wake_up(&card->wait_q);
return;
@@ -1372,7 +1366,7 @@ static void qeth_set_multiple_write_queues(struct qeth_card *card)
static void qeth_update_from_chp_desc(struct qeth_card *card)
{
struct ccw_device *ccwdev;
- struct channel_path_desc *chp_dsc;
+ struct channel_path_desc_fmt0 *chp_dsc;
QETH_DBF_TEXT(SETUP, 2, "chp_desc");
@@ -1873,8 +1867,8 @@ static int qeth_idx_activate_get_answer(struct qeth_channel *channel,
atomic_cmpxchg(&channel->irq_pending, 0, 1) == 0);
QETH_DBF_TEXT(SETUP, 6, "noirqpnd");
spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
- rc = ccw_device_start(channel->ccwdev,
- &channel->ccw, (addr_t) iob, 0, 0);
+ rc = ccw_device_start_timeout(channel->ccwdev, &channel->ccw,
+ (addr_t) iob, 0, 0, QETH_TIMEOUT);
spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
if (rc) {
@@ -1891,7 +1885,6 @@ static int qeth_idx_activate_get_answer(struct qeth_channel *channel,
if (channel->state != CH_STATE_UP) {
rc = -ETIME;
QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc);
- qeth_clear_cmd_buffers(channel);
} else
rc = 0;
return rc;
@@ -1945,8 +1938,8 @@ static int qeth_idx_activate_channel(struct qeth_channel *channel,
atomic_cmpxchg(&channel->irq_pending, 0, 1) == 0);
QETH_DBF_TEXT(SETUP, 6, "noirqpnd");
spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
- rc = ccw_device_start(channel->ccwdev,
- &channel->ccw, (addr_t) iob, 0, 0);
+ rc = ccw_device_start_timeout(channel->ccwdev, &channel->ccw,
+ (addr_t) iob, 0, 0, QETH_TIMEOUT);
spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
if (rc) {
@@ -1967,7 +1960,6 @@ static int qeth_idx_activate_channel(struct qeth_channel *channel,
QETH_DBF_MESSAGE(2, "%s IDX activate timed out\n",
dev_name(&channel->ccwdev->dev));
QETH_DBF_TEXT_(SETUP, 2, "2err%d", -ETIME);
- qeth_clear_cmd_buffers(channel);
return -ETIME;
}
return qeth_idx_activate_get_answer(channel, idx_reply_cb);
@@ -2169,8 +2161,8 @@ int qeth_send_control_data(struct qeth_card *card, int len,
QETH_CARD_TEXT(card, 6, "noirqpnd");
spin_lock_irqsave(get_ccwdev_lock(card->write.ccwdev), flags);
- rc = ccw_device_start(card->write.ccwdev, &card->write.ccw,
- (addr_t) iob, 0, 0);
+ rc = ccw_device_start_timeout(CARD_WDEV(card), &card->write.ccw,
+ (addr_t) iob, 0, 0, event_timeout);
spin_unlock_irqrestore(get_ccwdev_lock(card->write.ccwdev), flags);
if (rc) {
QETH_DBF_MESSAGE(2, "%s qeth_send_control_data: "
@@ -2202,8 +2194,6 @@ int qeth_send_control_data(struct qeth_card *card, int len,
}
}
- if (reply->rc == -EIO)
- goto error;
rc = reply->rc;
qeth_put_reply(reply);
return rc;
@@ -2214,10 +2204,6 @@ time_err:
list_del_init(&reply->list);
spin_unlock_irqrestore(&reply->card->lock, flags);
atomic_inc(&reply->received);
-error:
- atomic_set(&card->write.irq_pending, 0);
- qeth_release_buffer(iob->channel, iob);
- card->write.buf_no = (card->write.buf_no + 1) % QETH_CMD_BUFFER_NO;
rc = reply->rc;
qeth_put_reply(reply);
return rc;
@@ -2849,7 +2835,8 @@ static int qeth_init_input_buffer(struct qeth_card *card,
int i;
if ((card->options.cq == QETH_CQ_ENABLED) && (!buf->rx_skb)) {
- buf->rx_skb = dev_alloc_skb(QETH_RX_PULL_LEN + ETH_HLEN);
+ buf->rx_skb = netdev_alloc_skb(card->dev,
+ QETH_RX_PULL_LEN + ETH_HLEN);
if (!buf->rx_skb)
return 1;
}
@@ -2886,8 +2873,8 @@ int qeth_init_qdio_queues(struct qeth_card *card)
QETH_DBF_TEXT(SETUP, 2, "initqdqs");
/* inbound queue */
- qdio_reset_buffers(card->qdio.in_q->qdio_bufs,
- QDIO_MAX_BUFFERS_PER_Q);
+ qdio_reset_buffers(card->qdio.in_q->qdio_bufs, QDIO_MAX_BUFFERS_PER_Q);
+ memset(&card->rx, 0, sizeof(struct qeth_rx));
qeth_initialize_working_pool_list(card);
/*give only as many buffers to hardware as we have buffer pool entries*/
for (i = 0; i < card->qdio.in_buf_pool.buf_count - 1; ++i)
@@ -2962,12 +2949,10 @@ struct qeth_cmd_buffer *qeth_get_ipacmd_buffer(struct qeth_card *card,
enum qeth_ipa_cmds ipacmd, enum qeth_prot_versions prot)
{
struct qeth_cmd_buffer *iob;
- struct qeth_ipa_cmd *cmd;
iob = qeth_get_buffer(&card->write);
if (iob) {
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
- qeth_fill_ipacmd_header(card, cmd, ipacmd, prot);
+ qeth_fill_ipacmd_header(card, __ipa_cmd(iob), ipacmd, prot);
} else {
dev_warn(&card->gdev->dev,
"The qeth driver ran out of channel command buffers\n");
@@ -3037,28 +3022,23 @@ static int qeth_send_startlan(struct qeth_card *card)
return rc;
}
-static int qeth_default_setadapterparms_cb(struct qeth_card *card,
- struct qeth_reply *reply, unsigned long data)
+static int qeth_setadpparms_inspect_rc(struct qeth_ipa_cmd *cmd)
{
- struct qeth_ipa_cmd *cmd;
-
- QETH_CARD_TEXT(card, 4, "defadpcb");
-
- cmd = (struct qeth_ipa_cmd *) data;
- if (cmd->hdr.return_code == 0)
+ if (!cmd->hdr.return_code)
cmd->hdr.return_code =
cmd->data.setadapterparms.hdr.return_code;
- return 0;
+ return cmd->hdr.return_code;
}
static int qeth_query_setadapterparms_cb(struct qeth_card *card,
struct qeth_reply *reply, unsigned long data)
{
- struct qeth_ipa_cmd *cmd;
+ struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
QETH_CARD_TEXT(card, 3, "quyadpcb");
+ if (qeth_setadpparms_inspect_rc(cmd))
+ return 0;
- cmd = (struct qeth_ipa_cmd *) data;
if (cmd->data.setadapterparms.data.query_cmds_supp.lan_type & 0x7f) {
card->info.link_type =
cmd->data.setadapterparms.data.query_cmds_supp.lan_type;
@@ -3066,7 +3046,7 @@ static int qeth_query_setadapterparms_cb(struct qeth_card *card,
}
card->options.adp.supported_funcs =
cmd->data.setadapterparms.data.query_cmds_supp.supported_cmds;
- return qeth_default_setadapterparms_cb(card, reply, (unsigned long)cmd);
+ return 0;
}
static struct qeth_cmd_buffer *qeth_get_adapter_cmd(struct qeth_card *card,
@@ -3078,7 +3058,7 @@ static struct qeth_cmd_buffer *qeth_get_adapter_cmd(struct qeth_card *card,
iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETADAPTERPARMS,
QETH_PROT_IPV4);
if (iob) {
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd = __ipa_cmd(iob);
cmd->data.setadapterparms.hdr.cmdlength = cmdlen;
cmd->data.setadapterparms.hdr.command_code = command;
cmd->data.setadapterparms.hdr.used_total = 1;
@@ -3158,22 +3138,20 @@ EXPORT_SYMBOL_GPL(qeth_query_ipassists);
static int qeth_query_switch_attributes_cb(struct qeth_card *card,
struct qeth_reply *reply, unsigned long data)
{
- struct qeth_ipa_cmd *cmd;
- struct qeth_switch_info *sw_info;
+ struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
struct qeth_query_switch_attributes *attrs;
+ struct qeth_switch_info *sw_info;
QETH_CARD_TEXT(card, 2, "qswiatcb");
- cmd = (struct qeth_ipa_cmd *) data;
- sw_info = (struct qeth_switch_info *)reply->param;
- if (cmd->data.setadapterparms.hdr.return_code == 0) {
- attrs = &cmd->data.setadapterparms.data.query_switch_attributes;
- sw_info->capabilities = attrs->capabilities;
- sw_info->settings = attrs->settings;
- QETH_CARD_TEXT_(card, 2, "%04x%04x", sw_info->capabilities,
- sw_info->settings);
- }
- qeth_default_setadapterparms_cb(card, reply, (unsigned long) cmd);
+ if (qeth_setadpparms_inspect_rc(cmd))
+ return 0;
+ sw_info = (struct qeth_switch_info *)reply->param;
+ attrs = &cmd->data.setadapterparms.data.query_switch_attributes;
+ sw_info->capabilities = attrs->capabilities;
+ sw_info->settings = attrs->settings;
+ QETH_CARD_TEXT_(card, 2, "%04x%04x", sw_info->capabilities,
+ sw_info->settings);
return 0;
}
@@ -3220,7 +3198,7 @@ static int qeth_query_setdiagass(struct qeth_card *card)
iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SET_DIAG_ASS, 0);
if (!iob)
return -ENOMEM;
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd = __ipa_cmd(iob);
cmd->data.diagass.subcmd_len = 16;
cmd->data.diagass.subcmd = QETH_DIAGS_CMD_QUERY;
return qeth_send_ipa_cmd(card, iob, qeth_query_setdiagass_cb, NULL);
@@ -3273,7 +3251,7 @@ int qeth_hw_trap(struct qeth_card *card, enum qeth_diags_trap_action action)
iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SET_DIAG_ASS, 0);
if (!iob)
return -ENOMEM;
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd = __ipa_cmd(iob);
cmd->data.diagass.subcmd_len = 80;
cmd->data.diagass.subcmd = QETH_DIAGS_CMD_TRAP;
cmd->data.diagass.type = 1;
@@ -4211,16 +4189,13 @@ EXPORT_SYMBOL_GPL(qeth_do_send_packet);
static int qeth_setadp_promisc_mode_cb(struct qeth_card *card,
struct qeth_reply *reply, unsigned long data)
{
- struct qeth_ipa_cmd *cmd;
+ struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
struct qeth_ipacmd_setadpparms *setparms;
QETH_CARD_TEXT(card, 4, "prmadpcb");
- cmd = (struct qeth_ipa_cmd *) data;
setparms = &(cmd->data.setadapterparms);
-
- qeth_default_setadapterparms_cb(card, reply, (unsigned long)cmd);
- if (cmd->hdr.return_code) {
+ if (qeth_setadpparms_inspect_rc(cmd)) {
QETH_CARD_TEXT_(card, 4, "prmrc%x", cmd->hdr.return_code);
setparms->data.mode = SET_PROMISC_MODE_OFF;
}
@@ -4251,7 +4226,7 @@ void qeth_setadp_promisc_mode(struct qeth_card *card)
sizeof(struct qeth_ipacmd_setadpparms_hdr) + 8);
if (!iob)
return;
- cmd = (struct qeth_ipa_cmd *)(iob->data + IPA_PDU_HEADER_SIZE);
+ cmd = __ipa_cmd(iob);
cmd->data.setadapterparms.data.mode = mode;
qeth_send_ipa_cmd(card, iob, qeth_setadp_promisc_mode_cb, NULL);
}
@@ -4290,18 +4265,18 @@ EXPORT_SYMBOL_GPL(qeth_get_stats);
static int qeth_setadpparms_change_macaddr_cb(struct qeth_card *card,
struct qeth_reply *reply, unsigned long data)
{
- struct qeth_ipa_cmd *cmd;
+ struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
QETH_CARD_TEXT(card, 4, "chgmaccb");
+ if (qeth_setadpparms_inspect_rc(cmd))
+ return 0;
- cmd = (struct qeth_ipa_cmd *) data;
if (!card->options.layer2 ||
!(card->info.mac_bits & QETH_LAYER2_MAC_READ)) {
ether_addr_copy(card->dev->dev_addr,
cmd->data.setadapterparms.data.change_addr.addr);
card->info.mac_bits |= QETH_LAYER2_MAC_READ;
}
- qeth_default_setadapterparms_cb(card, reply, (unsigned long) cmd);
return 0;
}
@@ -4318,7 +4293,7 @@ int qeth_setadpparms_change_macaddr(struct qeth_card *card)
sizeof(struct qeth_change_addr));
if (!iob)
return -ENOMEM;
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd = __ipa_cmd(iob);
cmd->data.setadapterparms.data.change_addr.cmd = CHANGE_ADDR_READ_MAC;
cmd->data.setadapterparms.data.change_addr.addr_size = ETH_ALEN;
ether_addr_copy(cmd->data.setadapterparms.data.change_addr.addr,
@@ -4332,13 +4307,15 @@ EXPORT_SYMBOL_GPL(qeth_setadpparms_change_macaddr);
static int qeth_setadpparms_set_access_ctrl_cb(struct qeth_card *card,
struct qeth_reply *reply, unsigned long data)
{
- struct qeth_ipa_cmd *cmd;
+ struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
struct qeth_set_access_ctrl *access_ctrl_req;
int fallback = *(int *)reply->param;
QETH_CARD_TEXT(card, 4, "setaccb");
+ if (cmd->hdr.return_code)
+ return 0;
+ qeth_setadpparms_inspect_rc(cmd);
- cmd = (struct qeth_ipa_cmd *) data;
access_ctrl_req = &cmd->data.setadapterparms.data.set_access_ctrl;
QETH_DBF_TEXT_(SETUP, 2, "setaccb");
QETH_DBF_TEXT_(SETUP, 2, "%s", card->gdev->dev.kobj.name);
@@ -4411,7 +4388,6 @@ static int qeth_setadpparms_set_access_ctrl_cb(struct qeth_card *card,
card->options.isolation = card->options.prev_isolation;
break;
}
- qeth_default_setadapterparms_cb(card, reply, (unsigned long) cmd);
return 0;
}
@@ -4433,7 +4409,7 @@ static int qeth_setadpparms_set_access_ctrl(struct qeth_card *card,
sizeof(struct qeth_set_access_ctrl));
if (!iob)
return -ENOMEM;
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd = __ipa_cmd(iob);
access_ctrl_req = &cmd->data.setadapterparms.data.set_access_ctrl;
access_ctrl_req->subcmd_code = isolation;
@@ -4679,7 +4655,7 @@ static int qeth_snmp_command(struct qeth_card *card, char __user *udata)
rc = -ENOMEM;
goto out;
}
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd = __ipa_cmd(iob);
memcpy(&cmd->data.setadapterparms.data.snmp, &ureq->cmd, req_len);
rc = qeth_send_ipa_snmp_cmd(card, iob, QETH_SETADP_BASE_LEN + req_len,
qeth_snmp_command_cb, (void *)&qinfo);
@@ -4699,14 +4675,15 @@ out:
static int qeth_setadpparms_query_oat_cb(struct qeth_card *card,
struct qeth_reply *reply, unsigned long data)
{
- struct qeth_ipa_cmd *cmd;
+ struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *)data;
struct qeth_qoat_priv *priv;
char *resdata;
int resdatalen;
QETH_CARD_TEXT(card, 3, "qoatcb");
+ if (qeth_setadpparms_inspect_rc(cmd))
+ return 0;
- cmd = (struct qeth_ipa_cmd *)data;
priv = (struct qeth_qoat_priv *)reply->param;
resdatalen = cmd->data.setadapterparms.hdr.cmdlength;
resdata = (char *)data + 28;
@@ -4764,7 +4741,7 @@ static int qeth_query_oat_command(struct qeth_card *card, char __user *udata)
rc = -ENOMEM;
goto out_free;
}
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd = __ipa_cmd(iob);
oat_req = &cmd->data.setadapterparms.data.query_oat;
oat_req->subcmd_code = oat_data.command;
@@ -4800,21 +4777,18 @@ out:
static int qeth_query_card_info_cb(struct qeth_card *card,
struct qeth_reply *reply, unsigned long data)
{
- struct qeth_ipa_cmd *cmd;
+ struct carrier_info *carrier_info = (struct carrier_info *)reply->param;
+ struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *)data;
struct qeth_query_card_info *card_info;
- struct carrier_info *carrier_info;
QETH_CARD_TEXT(card, 2, "qcrdincb");
- carrier_info = (struct carrier_info *)reply->param;
- cmd = (struct qeth_ipa_cmd *)data;
- card_info = &cmd->data.setadapterparms.data.card_info;
- if (cmd->data.setadapterparms.hdr.return_code == 0) {
- carrier_info->card_type = card_info->card_type;
- carrier_info->port_mode = card_info->port_mode;
- carrier_info->port_speed = card_info->port_speed;
- }
+ if (qeth_setadpparms_inspect_rc(cmd))
+ return 0;
- qeth_default_setadapterparms_cb(card, reply, (unsigned long) cmd);
+ card_info = &cmd->data.setadapterparms.data.card_info;
+ carrier_info->card_type = card_info->card_type;
+ carrier_info->port_mode = card_info->port_mode;
+ carrier_info->port_speed = card_info->port_speed;
return 0;
}
@@ -4861,7 +4835,7 @@ int qeth_vm_request_mac(struct qeth_card *card)
goto out;
}
- ccw_device_get_id(CARD_DDEV(card), &id);
+ ccw_device_get_id(CARD_RDEV(card), &id);
request->resp_buf_len = sizeof(*response);
request->resp_version = DIAG26C_VERSION2;
request->op_code = DIAG26C_GET_MAC;
@@ -5339,7 +5313,7 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card,
} else {
unsigned int linear = (use_rx_sg) ? QETH_RX_PULL_LEN : skb_len;
- skb = dev_alloc_skb(linear + headroom);
+ skb = napi_alloc_skb(&card->napi, linear + headroom);
}
if (!skb)
goto no_mem;
@@ -5503,7 +5477,7 @@ struct qeth_cmd_buffer *qeth_get_setassparms_cmd(struct qeth_card *card,
iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETASSPARMS, prot);
if (iob) {
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd = __ipa_cmd(iob);
cmd->data.setassparms.hdr.assist_no = ipa_func;
cmd->data.setassparms.hdr.length = 8 + len;
cmd->data.setassparms.hdr.command_code = cmd_code;
@@ -5526,7 +5500,7 @@ int qeth_send_setassparms(struct qeth_card *card,
QETH_CARD_TEXT(card, 4, "sendassp");
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd = __ipa_cmd(iob);
if (len <= sizeof(__u32))
cmd->data.setassparms.data.flags_32bit = (__u32) data;
else /* (len > sizeof(__u32)) */
@@ -6567,10 +6541,14 @@ static int __init qeth_core_init(void)
mutex_init(&qeth_mod_mutex);
qeth_wq = create_singlethread_workqueue("qeth_wq");
+ if (!qeth_wq) {
+ rc = -ENOMEM;
+ goto out_err;
+ }
rc = qeth_register_dbf_views();
if (rc)
- goto out_err;
+ goto dbf_err;
qeth_core_root_dev = root_device_register("qeth");
rc = PTR_ERR_OR_ZERO(qeth_core_root_dev);
if (rc)
@@ -6607,6 +6585,8 @@ slab_err:
root_device_unregister(qeth_core_root_dev);
register_err:
qeth_unregister_dbf_views();
+dbf_err:
+ destroy_workqueue(qeth_wq);
out_err:
pr_err("Initializing the qeth device driver failed\n");
return rc;
diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h
index 619f897b4bb0..f4d1ec0b8f5a 100644
--- a/drivers/s390/net/qeth_core_mpc.h
+++ b/drivers/s390/net/qeth_core_mpc.h
@@ -35,6 +35,18 @@ extern unsigned char IPA_PDU_HEADER[];
#define QETH_HALT_CHANNEL_PARM -11
#define QETH_RCD_PARM -12
+static inline bool qeth_intparm_is_iob(unsigned long intparm)
+{
+ switch (intparm) {
+ case QETH_CLEAR_CHANNEL_PARM:
+ case QETH_HALT_CHANNEL_PARM:
+ case QETH_RCD_PARM:
+ case 0:
+ return false;
+ }
+ return true;
+}
+
/*****************************************************************************/
/* IP Assist related definitions */
/*****************************************************************************/
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index 5ef4c978ad19..b8079f2a65b3 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -21,7 +21,6 @@
#include <linux/list.h>
#include <linux/hash.h>
#include <linux/hashtable.h>
-#include <linux/string.h>
#include <asm/setup.h>
#include "qeth_core.h"
#include "qeth_l2.h"
@@ -108,7 +107,7 @@ static int qeth_l2_send_setdelmac(struct qeth_card *card, __u8 *mac,
iob = qeth_get_ipacmd_buffer(card, ipacmd, QETH_PROT_IPV4);
if (!iob)
return -ENOMEM;
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd = __ipa_cmd(iob);
cmd->data.setdelmac.mac_length = ETH_ALEN;
ether_addr_copy(cmd->data.setdelmac.mac, mac);
return qeth_setdelmac_makerc(card, qeth_send_ipa_cmd(card, iob,
@@ -122,13 +121,10 @@ static int qeth_l2_send_setmac(struct qeth_card *card, __u8 *mac)
QETH_CARD_TEXT(card, 2, "L2Setmac");
rc = qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETVMAC);
if (rc == 0) {
- card->info.mac_bits |= QETH_LAYER2_MAC_REGISTERED;
- ether_addr_copy(card->dev->dev_addr, mac);
dev_info(&card->gdev->dev,
- "MAC address %pM successfully registered on device %s\n",
- card->dev->dev_addr, card->dev->name);
+ "MAC address %pM successfully registered on device %s\n",
+ mac, card->dev->name);
} else {
- card->info.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED;
switch (rc) {
case -EEXIST:
dev_warn(&card->gdev->dev,
@@ -143,19 +139,6 @@ static int qeth_l2_send_setmac(struct qeth_card *card, __u8 *mac)
return rc;
}
-static int qeth_l2_send_delmac(struct qeth_card *card, __u8 *mac)
-{
- int rc;
-
- QETH_CARD_TEXT(card, 2, "L2Delmac");
- if (!(card->info.mac_bits & QETH_LAYER2_MAC_REGISTERED))
- return 0;
- rc = qeth_l2_send_setdelmac(card, mac, IPA_CMD_DELVMAC);
- if (rc == 0)
- card->info.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED;
- return rc;
-}
-
static int qeth_l2_write_mac(struct qeth_card *card, u8 *mac)
{
enum qeth_ipa_cmds cmd = is_multicast_ether_addr_64bits(mac) ?
@@ -305,7 +288,7 @@ static int qeth_l2_send_setdelvlan(struct qeth_card *card, __u16 i,
iob = qeth_get_ipacmd_buffer(card, ipacmd, QETH_PROT_IPV4);
if (!iob)
return -ENOMEM;
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd = __ipa_cmd(iob);
cmd->data.setdelvlan.vlan_id = i;
return qeth_setdelvlan_makerc(card, qeth_send_ipa_cmd(card, iob,
qeth_l2_send_setdelvlan_cb, NULL));
@@ -437,10 +420,8 @@ static int qeth_l2_process_inbound_buffer(struct qeth_card *card,
*done = 1;
break;
}
- skb->dev = card->dev;
switch (hdr->hdr.l2.id) {
case QETH_HEADER_TYPE_LAYER2:
- skb->pkt_type = PACKET_HOST;
skb->protocol = eth_type_trans(skb, skb->dev);
if ((card->dev->features & NETIF_F_RXCSUM)
&& ((hdr->hdr.l2.flags[1] &
@@ -522,6 +503,7 @@ static int qeth_l2_set_mac_address(struct net_device *dev, void *p)
{
struct sockaddr *addr = p;
struct qeth_card *card = dev->ml_priv;
+ u8 old_addr[ETH_ALEN];
int rc = 0;
QETH_CARD_TEXT(card, 3, "setmac");
@@ -533,14 +515,35 @@ static int qeth_l2_set_mac_address(struct net_device *dev, void *p)
return -EOPNOTSUPP;
}
QETH_CARD_HEX(card, 3, addr->sa_data, ETH_ALEN);
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+
if (qeth_wait_for_threads(card, QETH_RECOVER_THREAD)) {
QETH_CARD_TEXT(card, 3, "setmcREC");
return -ERESTARTSYS;
}
- rc = qeth_l2_send_delmac(card, &card->dev->dev_addr[0]);
- if (!rc || (rc == -ENOENT))
- rc = qeth_l2_send_setmac(card, addr->sa_data);
- return rc ? -EINVAL : 0;
+
+ if (!qeth_card_hw_is_reachable(card)) {
+ ether_addr_copy(dev->dev_addr, addr->sa_data);
+ return 0;
+ }
+
+ /* don't register the same address twice */
+ if (ether_addr_equal_64bits(dev->dev_addr, addr->sa_data) &&
+ (card->info.mac_bits & QETH_LAYER2_MAC_REGISTERED))
+ return 0;
+
+ /* add the new address, switch over, drop the old */
+ rc = qeth_l2_send_setmac(card, addr->sa_data);
+ if (rc)
+ return rc;
+ ether_addr_copy(old_addr, dev->dev_addr);
+ ether_addr_copy(dev->dev_addr, addr->sa_data);
+
+ if (card->info.mac_bits & QETH_LAYER2_MAC_REGISTERED)
+ qeth_l2_remove_mac(card, old_addr);
+ card->info.mac_bits |= QETH_LAYER2_MAC_REGISTERED;
+ return 0;
}
static void qeth_promisc_to_bridge(struct qeth_card *card)
@@ -975,6 +978,7 @@ static int qeth_l2_setup_netdev(struct qeth_card *card)
return -ENODEV;
card->dev->ml_priv = card;
+ card->dev->priv_flags |= IFF_UNICAST_FLT;
card->dev->watchdog_timeo = QETH_TX_TIMEOUT;
card->dev->mtu = card->info.initial_mtu;
card->dev->min_mtu = 64;
@@ -991,9 +995,16 @@ static int qeth_l2_setup_netdev(struct qeth_card *card)
card->dev->features |= NETIF_F_VLAN_CHALLENGED;
else
card->dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
+
+ if (card->info.type != QETH_CARD_TYPE_OSN &&
+ card->info.type != QETH_CARD_TYPE_IQD) {
+ card->dev->priv_flags &= ~IFF_TX_SKB_SHARING;
+ card->dev->needed_headroom = sizeof(struct qeth_hdr);
+ card->dev->hw_features |= NETIF_F_SG;
+ card->dev->vlan_features |= NETIF_F_SG;
+ }
+
if (card->info.type == QETH_CARD_TYPE_OSD && !card->info.guestlan) {
- card->dev->hw_features = NETIF_F_SG;
- card->dev->vlan_features = NETIF_F_SG;
card->dev->features |= NETIF_F_SG;
/* OSA 3S and earlier has no RX/TX support */
if (qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM)) {
@@ -1005,11 +1016,6 @@ static int qeth_l2_setup_netdev(struct qeth_card *card)
card->dev->vlan_features |= NETIF_F_RXCSUM;
}
}
- if (card->info.type != QETH_CARD_TYPE_OSN &&
- card->info.type != QETH_CARD_TYPE_IQD) {
- card->dev->priv_flags &= ~IFF_TX_SKB_SHARING;
- card->dev->needed_headroom = sizeof(struct qeth_hdr);
- }
card->info.broadcast_capable = 1;
qeth_l2_request_initial_mac(card);
@@ -1067,8 +1073,9 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
goto out_remove;
}
- if (card->info.type != QETH_CARD_TYPE_OSN)
- qeth_l2_send_setmac(card, &card->dev->dev_addr[0]);
+ if (card->info.type != QETH_CARD_TYPE_OSN &&
+ !qeth_l2_send_setmac(card, card->dev->dev_addr))
+ card->info.mac_bits |= QETH_LAYER2_MAC_REGISTERED;
if (qeth_is_diagass_supported(card, QETH_DIAGS_CMD_TRAP)) {
if (card->info.hwtrap &&
@@ -1086,7 +1093,6 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
qeth_l2_setup_bridgeport_attrs(card);
card->state = CARD_STATE_HARDSETUP;
- memset(&card->rx, 0, sizeof(struct qeth_rx));
qeth_print_status_message(card);
/* softsetup */
@@ -1339,8 +1345,8 @@ static int qeth_osn_send_control_data(struct qeth_card *card, int len,
qeth_prepare_control_data(card, len, iob);
QETH_CARD_TEXT(card, 6, "osnoirqp");
spin_lock_irqsave(get_ccwdev_lock(card->write.ccwdev), flags);
- rc = ccw_device_start(card->write.ccwdev, &card->write.ccw,
- (addr_t) iob, 0, 0);
+ rc = ccw_device_start_timeout(CARD_WDEV(card), &card->write.ccw,
+ (addr_t) iob, 0, 0, QETH_IPA_TIMEOUT);
spin_unlock_irqrestore(get_ccwdev_lock(card->write.ccwdev), flags);
if (rc) {
QETH_DBF_MESSAGE(2, "qeth_osn_send_control_data: "
@@ -1374,7 +1380,6 @@ int qeth_osn_assist(struct net_device *dev, void *data, int data_len)
{
struct qeth_cmd_buffer *iob;
struct qeth_card *card;
- int rc;
if (!dev)
return -ENODEV;
@@ -1385,9 +1390,8 @@ int qeth_osn_assist(struct net_device *dev, void *data, int data_len)
if (!qeth_card_hw_is_reachable(card))
return -ENODEV;
iob = qeth_wait_for_buffer(&card->write);
- memcpy(iob->data+IPA_PDU_HEADER_SIZE, data, data_len);
- rc = qeth_osn_send_ipa_cmd(card, iob, data_len);
- return rc;
+ memcpy(__ipa_cmd(iob), data, data_len);
+ return qeth_osn_send_ipa_cmd(card, iob, data_len);
}
EXPORT_SYMBOL(qeth_osn_assist);
@@ -1764,7 +1768,7 @@ static struct qeth_cmd_buffer *qeth_sbp_build_cmd(struct qeth_card *card,
iob = qeth_get_ipacmd_buffer(card, ipa_cmd, 0);
if (!iob)
return iob;
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd = __ipa_cmd(iob);
cmd->data.sbp.hdr.cmdlength = sizeof(struct qeth_ipacmd_sbp_hdr) +
cmd_length;
cmd->data.sbp.hdr.command_code = sbp_cmd;
@@ -2129,7 +2133,7 @@ static int qeth_l2_vnicc_request(struct qeth_card *card,
return -ENOMEM;
/* create header for request */
- cmd = (struct qeth_ipa_cmd *)(iob->data + IPA_PDU_HEADER_SIZE);
+ cmd = __ipa_cmd(iob);
req = &cmd->data.vnicc;
/* create sub command header for request */
diff --git a/drivers/s390/net/qeth_l3.h b/drivers/s390/net/qeth_l3.h
index 498fe9af2cdb..87659cfc9066 100644
--- a/drivers/s390/net/qeth_l3.h
+++ b/drivers/s390/net/qeth_l3.h
@@ -15,21 +15,26 @@
#define QETH_SNIFF_AVAIL 0x0008
+enum qeth_ip_types {
+ QETH_IP_TYPE_NORMAL,
+ QETH_IP_TYPE_VIPA,
+ QETH_IP_TYPE_RXIP,
+};
+
struct qeth_ipaddr {
struct hlist_node hnode;
enum qeth_ip_types type;
- enum qeth_ipa_setdelip_flags set_flags;
- enum qeth_ipa_setdelip_flags del_flags;
+ unsigned char mac[ETH_ALEN];
u8 is_multicast:1;
u8 in_progress:1;
u8 disp_flag:2;
+ u8 ipato:1; /* ucast only */
/* is changed only for normal ip addresses
* for non-normal addresses it always is 1
*/
int ref_counter;
enum qeth_prot_versions proto;
- unsigned char mac[ETH_ALEN];
union {
struct {
unsigned int addr;
@@ -42,6 +47,16 @@ struct qeth_ipaddr {
} u;
};
+static inline void qeth_l3_init_ipaddr(struct qeth_ipaddr *addr,
+ enum qeth_ip_types type,
+ enum qeth_prot_versions proto)
+{
+ memset(addr, 0, sizeof(*addr));
+ addr->type = type;
+ addr->proto = proto;
+ addr->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
+}
+
static inline bool qeth_l3_addr_match_ip(struct qeth_ipaddr *a1,
struct qeth_ipaddr *a2)
{
@@ -109,15 +124,10 @@ int qeth_l3_add_ipato_entry(struct qeth_card *, struct qeth_ipato_entry *);
int qeth_l3_del_ipato_entry(struct qeth_card *card,
enum qeth_prot_versions proto, u8 *addr,
int mask_bits);
-int qeth_l3_add_vipa(struct qeth_card *, enum qeth_prot_versions, const u8 *);
-int qeth_l3_del_vipa(struct qeth_card *card, enum qeth_prot_versions proto,
- const u8 *addr);
-int qeth_l3_add_rxip(struct qeth_card *, enum qeth_prot_versions, const u8 *);
-int qeth_l3_del_rxip(struct qeth_card *card, enum qeth_prot_versions proto,
- const u8 *addr);
void qeth_l3_update_ipato(struct qeth_card *card);
-struct qeth_ipaddr *qeth_l3_get_addr_buffer(enum qeth_prot_versions);
-int qeth_l3_add_ip(struct qeth_card *, struct qeth_ipaddr *);
-int qeth_l3_delete_ip(struct qeth_card *, struct qeth_ipaddr *);
+int qeth_l3_modify_hsuid(struct qeth_card *card, bool add);
+int qeth_l3_modify_rxip_vipa(struct qeth_card *card, bool add, const u8 *ip,
+ enum qeth_ip_types type,
+ enum qeth_prot_versions proto);
#endif /* __QETH_L3_H__ */
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index b6b12220da71..c1a16a74aa83 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -67,6 +67,15 @@ void qeth_l3_ipaddr_to_string(enum qeth_prot_versions proto, const __u8 *addr,
qeth_l3_ipaddr6_to_string(addr, buf);
}
+static struct qeth_ipaddr *qeth_l3_get_addr_buffer(enum qeth_prot_versions prot)
+{
+ struct qeth_ipaddr *addr = kmalloc(sizeof(*addr), GFP_ATOMIC);
+
+ if (addr)
+ qeth_l3_init_ipaddr(addr, QETH_IP_TYPE_NORMAL, prot);
+ return addr;
+}
+
static struct qeth_ipaddr *qeth_l3_find_addr_by_ip(struct qeth_card *card,
struct qeth_ipaddr *query)
{
@@ -138,12 +147,18 @@ static bool qeth_l3_is_addr_covered_by_ipato(struct qeth_card *card,
return rc;
}
-int qeth_l3_delete_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
+static int qeth_l3_delete_ip(struct qeth_card *card,
+ struct qeth_ipaddr *tmp_addr)
{
int rc = 0;
struct qeth_ipaddr *addr;
- QETH_CARD_TEXT(card, 4, "delip");
+ if (tmp_addr->type == QETH_IP_TYPE_RXIP)
+ QETH_CARD_TEXT(card, 2, "delrxip");
+ else if (tmp_addr->type == QETH_IP_TYPE_VIPA)
+ QETH_CARD_TEXT(card, 2, "delvipa");
+ else
+ QETH_CARD_TEXT(card, 2, "delip");
if (tmp_addr->proto == QETH_PROT_IPV4)
QETH_CARD_HEX(card, 4, &tmp_addr->u.a4.addr, 4);
@@ -171,13 +186,18 @@ int qeth_l3_delete_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
return rc;
}
-int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
+static int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
{
int rc = 0;
struct qeth_ipaddr *addr;
char buf[40];
- QETH_CARD_TEXT(card, 4, "addip");
+ if (tmp_addr->type == QETH_IP_TYPE_RXIP)
+ QETH_CARD_TEXT(card, 2, "addrxip");
+ else if (tmp_addr->type == QETH_IP_TYPE_VIPA)
+ QETH_CARD_TEXT(card, 2, "addvipa");
+ else
+ QETH_CARD_TEXT(card, 2, "addip");
if (tmp_addr->proto == QETH_PROT_IPV4)
QETH_CARD_HEX(card, 4, &tmp_addr->u.a4.addr, 4);
@@ -209,7 +229,7 @@ int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
if (qeth_l3_is_addr_covered_by_ipato(card, addr)) {
QETH_CARD_TEXT(card, 2, "tkovaddr");
- addr->set_flags |= QETH_IPA_SETIP_TAKEOVER_FLAG;
+ addr->ipato = 1;
}
hash_add(card->ip_htable, &addr->hnode,
qeth_l3_ipaddr_hash(addr));
@@ -251,23 +271,6 @@ int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
return rc;
}
-
-struct qeth_ipaddr *qeth_l3_get_addr_buffer(
- enum qeth_prot_versions prot)
-{
- struct qeth_ipaddr *addr;
-
- addr = kzalloc(sizeof(struct qeth_ipaddr), GFP_ATOMIC);
- if (!addr)
- return NULL;
-
- addr->type = QETH_IP_TYPE_NORMAL;
- addr->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
- addr->proto = prot;
-
- return addr;
-}
-
static void qeth_l3_clear_ip_htable(struct qeth_card *card, int recover)
{
struct qeth_ipaddr *addr;
@@ -352,7 +355,7 @@ static int qeth_l3_send_setdelmc(struct qeth_card *card,
iob = qeth_get_ipacmd_buffer(card, ipacmd, addr->proto);
if (!iob)
return -ENOMEM;
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd = __ipa_cmd(iob);
ether_addr_copy(cmd->data.setdelipm.mac, addr->mac);
if (addr->proto == QETH_PROT_IPV6)
memcpy(cmd->data.setdelipm.ip6, &addr->u.a6.addr,
@@ -379,21 +382,38 @@ static void qeth_l3_fill_netmask(u8 *netmask, unsigned int len)
}
}
+static u32 qeth_l3_get_setdelip_flags(struct qeth_ipaddr *addr, bool set)
+{
+ switch (addr->type) {
+ case QETH_IP_TYPE_RXIP:
+ return (set) ? QETH_IPA_SETIP_TAKEOVER_FLAG : 0;
+ case QETH_IP_TYPE_VIPA:
+ return (set) ? QETH_IPA_SETIP_VIPA_FLAG :
+ QETH_IPA_DELIP_VIPA_FLAG;
+ default:
+ return (set && addr->ipato) ? QETH_IPA_SETIP_TAKEOVER_FLAG : 0;
+ }
+}
+
static int qeth_l3_send_setdelip(struct qeth_card *card,
- struct qeth_ipaddr *addr, int ipacmd, unsigned int flags)
+ struct qeth_ipaddr *addr,
+ enum qeth_ipa_cmds ipacmd)
{
- int rc;
struct qeth_cmd_buffer *iob;
struct qeth_ipa_cmd *cmd;
__u8 netmask[16];
+ u32 flags;
QETH_CARD_TEXT(card, 4, "setdelip");
- QETH_CARD_TEXT_(card, 4, "flags%02X", flags);
iob = qeth_get_ipacmd_buffer(card, ipacmd, addr->proto);
if (!iob)
return -ENOMEM;
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd = __ipa_cmd(iob);
+
+ flags = qeth_l3_get_setdelip_flags(addr, ipacmd == IPA_CMD_SETIP);
+ QETH_CARD_TEXT_(card, 4, "flags%02X", flags);
+
if (addr->proto == QETH_PROT_IPV6) {
memcpy(cmd->data.setdelip6.ip_addr, &addr->u.a6.addr,
sizeof(struct in6_addr));
@@ -407,9 +427,7 @@ static int qeth_l3_send_setdelip(struct qeth_card *card,
cmd->data.setdelip4.flags = flags;
}
- rc = qeth_send_ipa_cmd(card, iob, NULL, NULL);
-
- return rc;
+ return qeth_send_ipa_cmd(card, iob, NULL, NULL);
}
static int qeth_l3_send_setrouting(struct qeth_card *card,
@@ -423,7 +441,7 @@ static int qeth_l3_send_setrouting(struct qeth_card *card,
iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETRTG, prot);
if (!iob)
return -ENOMEM;
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd = __ipa_cmd(iob);
cmd->data.setrtg.type = (type);
rc = qeth_send_ipa_cmd(card, iob, NULL, NULL);
@@ -525,10 +543,7 @@ void qeth_l3_update_ipato(struct qeth_card *card)
hash_for_each(card->ip_htable, i, addr, hnode) {
if (addr->type != QETH_IP_TYPE_NORMAL)
continue;
- if (qeth_l3_is_addr_covered_by_ipato(card, addr))
- addr->set_flags |= QETH_IPA_SETIP_TAKEOVER_FLAG;
- else
- addr->set_flags &= ~QETH_IPA_SETIP_TAKEOVER_FLAG;
+ addr->ipato = qeth_l3_is_addr_covered_by_ipato(card, addr);
}
}
@@ -606,132 +621,39 @@ int qeth_l3_del_ipato_entry(struct qeth_card *card,
return rc;
}
-/*
- * VIPA related functions
- */
-int qeth_l3_add_vipa(struct qeth_card *card, enum qeth_prot_versions proto,
- const u8 *addr)
-{
- struct qeth_ipaddr *ipaddr;
- int rc;
-
- ipaddr = qeth_l3_get_addr_buffer(proto);
- if (ipaddr) {
- if (proto == QETH_PROT_IPV4) {
- QETH_CARD_TEXT(card, 2, "addvipa4");
- memcpy(&ipaddr->u.a4.addr, addr, 4);
- ipaddr->u.a4.mask = 0;
- } else if (proto == QETH_PROT_IPV6) {
- QETH_CARD_TEXT(card, 2, "addvipa6");
- memcpy(&ipaddr->u.a6.addr, addr, 16);
- ipaddr->u.a6.pfxlen = 0;
- }
- ipaddr->type = QETH_IP_TYPE_VIPA;
- ipaddr->set_flags = QETH_IPA_SETIP_VIPA_FLAG;
- ipaddr->del_flags = QETH_IPA_DELIP_VIPA_FLAG;
- } else
- return -ENOMEM;
-
- spin_lock_bh(&card->ip_lock);
- rc = qeth_l3_add_ip(card, ipaddr);
- spin_unlock_bh(&card->ip_lock);
-
- kfree(ipaddr);
-
- return rc;
-}
-
-int qeth_l3_del_vipa(struct qeth_card *card, enum qeth_prot_versions proto,
- const u8 *addr)
-{
- struct qeth_ipaddr *ipaddr;
- int rc;
-
- ipaddr = qeth_l3_get_addr_buffer(proto);
- if (ipaddr) {
- if (proto == QETH_PROT_IPV4) {
- QETH_CARD_TEXT(card, 2, "delvipa4");
- memcpy(&ipaddr->u.a4.addr, addr, 4);
- ipaddr->u.a4.mask = 0;
- } else if (proto == QETH_PROT_IPV6) {
- QETH_CARD_TEXT(card, 2, "delvipa6");
- memcpy(&ipaddr->u.a6.addr, addr, 16);
- ipaddr->u.a6.pfxlen = 0;
- }
- ipaddr->type = QETH_IP_TYPE_VIPA;
- } else
- return -ENOMEM;
-
- spin_lock_bh(&card->ip_lock);
- rc = qeth_l3_delete_ip(card, ipaddr);
- spin_unlock_bh(&card->ip_lock);
-
- kfree(ipaddr);
- return rc;
-}
-
-/*
- * proxy ARP related functions
- */
-int qeth_l3_add_rxip(struct qeth_card *card, enum qeth_prot_versions proto,
- const u8 *addr)
+int qeth_l3_modify_rxip_vipa(struct qeth_card *card, bool add, const u8 *ip,
+ enum qeth_ip_types type,
+ enum qeth_prot_versions proto)
{
- struct qeth_ipaddr *ipaddr;
+ struct qeth_ipaddr addr;
int rc;
- ipaddr = qeth_l3_get_addr_buffer(proto);
- if (ipaddr) {
- if (proto == QETH_PROT_IPV4) {
- QETH_CARD_TEXT(card, 2, "addrxip4");
- memcpy(&ipaddr->u.a4.addr, addr, 4);
- ipaddr->u.a4.mask = 0;
- } else if (proto == QETH_PROT_IPV6) {
- QETH_CARD_TEXT(card, 2, "addrxip6");
- memcpy(&ipaddr->u.a6.addr, addr, 16);
- ipaddr->u.a6.pfxlen = 0;
- }
-
- ipaddr->type = QETH_IP_TYPE_RXIP;
- ipaddr->set_flags = QETH_IPA_SETIP_TAKEOVER_FLAG;
- ipaddr->del_flags = 0;
- } else
- return -ENOMEM;
+ qeth_l3_init_ipaddr(&addr, type, proto);
+ if (proto == QETH_PROT_IPV4)
+ memcpy(&addr.u.a4.addr, ip, 4);
+ else
+ memcpy(&addr.u.a6.addr, ip, 16);
spin_lock_bh(&card->ip_lock);
- rc = qeth_l3_add_ip(card, ipaddr);
+ rc = add ? qeth_l3_add_ip(card, &addr) : qeth_l3_delete_ip(card, &addr);
spin_unlock_bh(&card->ip_lock);
-
- kfree(ipaddr);
-
return rc;
}
-int qeth_l3_del_rxip(struct qeth_card *card, enum qeth_prot_versions proto,
- const u8 *addr)
+int qeth_l3_modify_hsuid(struct qeth_card *card, bool add)
{
- struct qeth_ipaddr *ipaddr;
- int rc;
+ struct qeth_ipaddr addr;
+ int rc, i;
- ipaddr = qeth_l3_get_addr_buffer(proto);
- if (ipaddr) {
- if (proto == QETH_PROT_IPV4) {
- QETH_CARD_TEXT(card, 2, "delrxip4");
- memcpy(&ipaddr->u.a4.addr, addr, 4);
- ipaddr->u.a4.mask = 0;
- } else if (proto == QETH_PROT_IPV6) {
- QETH_CARD_TEXT(card, 2, "delrxip6");
- memcpy(&ipaddr->u.a6.addr, addr, 16);
- ipaddr->u.a6.pfxlen = 0;
- }
- ipaddr->type = QETH_IP_TYPE_RXIP;
- } else
- return -ENOMEM;
+ qeth_l3_init_ipaddr(&addr, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV6);
+ addr.u.a6.addr.s6_addr[0] = 0xfe;
+ addr.u.a6.addr.s6_addr[1] = 0x80;
+ for (i = 0; i < 8; i++)
+ addr.u.a6.addr.s6_addr[8+i] = card->options.hsuid[i];
spin_lock_bh(&card->ip_lock);
- rc = qeth_l3_delete_ip(card, ipaddr);
+ rc = add ? qeth_l3_add_ip(card, &addr) : qeth_l3_delete_ip(card, &addr);
spin_unlock_bh(&card->ip_lock);
-
- kfree(ipaddr);
return rc;
}
@@ -758,8 +680,7 @@ static int qeth_l3_register_addr_entry(struct qeth_card *card,
if (addr->is_multicast)
rc = qeth_l3_send_setdelmc(card, addr, IPA_CMD_SETIPM);
else
- rc = qeth_l3_send_setdelip(card, addr, IPA_CMD_SETIP,
- addr->set_flags);
+ rc = qeth_l3_send_setdelip(card, addr, IPA_CMD_SETIP);
if (rc)
QETH_CARD_TEXT(card, 2, "failed");
} while ((--cnt > 0) && rc);
@@ -791,8 +712,7 @@ static int qeth_l3_deregister_addr_entry(struct qeth_card *card,
if (addr->is_multicast)
rc = qeth_l3_send_setdelmc(card, addr, IPA_CMD_DELIPM);
else
- rc = qeth_l3_send_setdelip(card, addr, IPA_CMD_DELIP,
- addr->del_flags);
+ rc = qeth_l3_send_setdelip(card, addr, IPA_CMD_DELIP);
if (rc)
QETH_CARD_TEXT(card, 2, "failed");
@@ -1072,7 +992,7 @@ static int qeth_l3_iqd_read_initial_mac(struct qeth_card *card)
QETH_PROT_IPV6);
if (!iob)
return -ENOMEM;
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd = __ipa_cmd(iob);
*((__u16 *) &cmd->data.create_destroy_addr.unique_id[6]) =
card->info.unique_id;
@@ -1117,7 +1037,7 @@ static int qeth_l3_get_unique_id(struct qeth_card *card)
QETH_PROT_IPV6);
if (!iob)
return -ENOMEM;
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd = __ipa_cmd(iob);
*((__u16 *) &cmd->data.create_destroy_addr.unique_id[6]) =
card->info.unique_id;
@@ -1193,7 +1113,7 @@ qeth_diags_trace(struct qeth_card *card, enum qeth_diags_trace_cmds diags_cmd)
iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SET_DIAG_ASS, 0);
if (!iob)
return -ENOMEM;
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd = __ipa_cmd(iob);
cmd->data.diagass.subcmd_len = 16;
cmd->data.diagass.subcmd = QETH_DIAGS_CMD_TRACE;
cmd->data.diagass.type = QETH_DIAGS_TYPE_HIPERSOCKET;
@@ -1502,30 +1422,24 @@ static void qeth_l3_rebuild_skb(struct qeth_card *card, struct sk_buff *skb,
ipv6_eth_mc_map(&ipv6_hdr(skb)->daddr, tg_addr);
card->stats.multicast++;
- skb->pkt_type = PACKET_MULTICAST;
break;
case QETH_CAST_BROADCAST:
ether_addr_copy(tg_addr, card->dev->broadcast);
card->stats.multicast++;
- skb->pkt_type = PACKET_BROADCAST;
break;
- case QETH_CAST_UNICAST:
- case QETH_CAST_ANYCAST:
- case QETH_CAST_NOCAST:
default:
if (card->options.sniffer)
skb->pkt_type = PACKET_OTHERHOST;
- else
- skb->pkt_type = PACKET_HOST;
ether_addr_copy(tg_addr, card->dev->dev_addr);
}
+
if (hdr->hdr.l3.ext_flags & QETH_HDR_EXT_SRC_MAC_ADDR)
card->dev->header_ops->create(skb, card->dev, prot,
tg_addr, &hdr->hdr.l3.next_hop.rx.src_mac,
- card->dev->addr_len);
+ skb->len);
else
card->dev->header_ops->create(skb, card->dev, prot,
- tg_addr, "FAKELL", card->dev->addr_len);
+ tg_addr, "FAKELL", skb->len);
}
skb->protocol = eth_type_trans(skb, card->dev);
@@ -1572,20 +1486,16 @@ static int qeth_l3_process_inbound_buffer(struct qeth_card *card,
*done = 1;
break;
}
- skb->dev = card->dev;
switch (hdr->hdr.l3.id) {
case QETH_HEADER_TYPE_LAYER3:
magic = *(__u16 *)skb->data;
if ((card->info.type == QETH_CARD_TYPE_IQD) &&
(magic == ETH_P_AF_IUCV)) {
skb->protocol = cpu_to_be16(ETH_P_AF_IUCV);
- skb->pkt_type = PACKET_HOST;
- skb->mac_header = NET_SKB_PAD;
- skb->dev = card->dev;
len = skb->len;
card->dev->header_ops->create(skb, card->dev, 0,
- card->dev->dev_addr, "FAKELL",
- card->dev->addr_len);
+ card->dev->dev_addr, "FAKELL", len);
+ skb_reset_mac_header(skb);
netif_receive_skb(skb);
} else {
qeth_l3_rebuild_skb(card, skb, hdr);
@@ -1594,7 +1504,6 @@ static int qeth_l3_process_inbound_buffer(struct qeth_card *card,
}
break;
case QETH_HEADER_TYPE_LAYER2: /* for HiperSockets sniffer */
- skb->pkt_type = PACKET_HOST;
skb->protocol = eth_type_trans(skb, skb->dev);
len = skb->len;
netif_receive_skb(skb);
@@ -1613,69 +1522,6 @@ static int qeth_l3_process_inbound_buffer(struct qeth_card *card,
return work_done;
}
-static int qeth_l3_verify_vlan_dev(struct net_device *dev,
- struct qeth_card *card)
-{
- int rc = 0;
- u16 vid;
-
- for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) {
- struct net_device *netdev;
-
- rcu_read_lock();
- netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q),
- vid);
- rcu_read_unlock();
- if (netdev == dev) {
- rc = QETH_VLAN_CARD;
- break;
- }
- }
-
- if (rc && !(vlan_dev_real_dev(dev)->ml_priv == (void *)card))
- return 0;
-
- return rc;
-}
-
-static int qeth_l3_verify_dev(struct net_device *dev)
-{
- struct qeth_card *card;
- int rc = 0;
- unsigned long flags;
-
- read_lock_irqsave(&qeth_core_card_list.rwlock, flags);
- list_for_each_entry(card, &qeth_core_card_list.list, list) {
- if (card->dev == dev) {
- rc = QETH_REAL_CARD;
- break;
- }
- rc = qeth_l3_verify_vlan_dev(dev, card);
- if (rc)
- break;
- }
- read_unlock_irqrestore(&qeth_core_card_list.rwlock, flags);
-
- return rc;
-}
-
-static struct qeth_card *qeth_l3_get_card_from_dev(struct net_device *dev)
-{
- struct qeth_card *card = NULL;
- int rc;
-
- rc = qeth_l3_verify_dev(dev);
- if (rc == QETH_REAL_CARD)
- card = dev->ml_priv;
- else if (rc == QETH_VLAN_CARD)
- card = vlan_dev_real_dev(dev)->ml_priv;
- if (card && card->options.layer2)
- card = NULL;
- if (card)
- QETH_CARD_TEXT_(card, 4, "%d", rc);
- return card ;
-}
-
static void qeth_l3_stop_card(struct qeth_card *card, int recovery_mode)
{
QETH_DBF_TEXT(SETUP, 2, "stopcard");
@@ -2004,7 +1850,7 @@ static int qeth_l3_query_arp_cache_info(struct qeth_card *card,
prot);
if (!iob)
return -ENOMEM;
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd = __ipa_cmd(iob);
cmd->data.setassparms.data.query_arp.request_bits = 0x000F;
cmd->data.setassparms.data.query_arp.reply_bits = 0;
cmd->data.setassparms.data.query_arp.no_entries = 0;
@@ -2785,14 +2631,16 @@ static int qeth_l3_setup_netdev(struct qeth_card *card)
if (!(card->info.unique_id & UNIQUE_ID_NOT_BY_CARD))
card->dev->dev_id = card->info.unique_id &
0xffff;
+
+ card->dev->hw_features |= NETIF_F_SG;
+ card->dev->vlan_features |= NETIF_F_SG;
+
if (!card->info.guestlan) {
- card->dev->hw_features = NETIF_F_SG |
- NETIF_F_RXCSUM | NETIF_F_IP_CSUM |
- NETIF_F_TSO;
- card->dev->vlan_features = NETIF_F_SG |
- NETIF_F_RXCSUM | NETIF_F_IP_CSUM |
- NETIF_F_TSO;
card->dev->features |= NETIF_F_SG;
+ card->dev->hw_features |= NETIF_F_TSO |
+ NETIF_F_RXCSUM | NETIF_F_IP_CSUM;
+ card->dev->vlan_features |= NETIF_F_TSO |
+ NETIF_F_RXCSUM | NETIF_F_IP_CSUM;
}
}
} else if (card->info.type == QETH_CARD_TYPE_IQD) {
@@ -2907,7 +2755,6 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode)
card->info.hwtrap = 0;
card->state = CARD_STATE_HARDSETUP;
- memset(&card->rx, 0, sizeof(struct qeth_rx));
qeth_print_status_message(card);
/* softsetup */
@@ -3130,13 +2977,43 @@ struct qeth_discipline qeth_l3_discipline = {
};
EXPORT_SYMBOL_GPL(qeth_l3_discipline);
+static int qeth_l3_handle_ip_event(struct qeth_card *card,
+ struct qeth_ipaddr *addr,
+ unsigned long event)
+{
+ switch (event) {
+ case NETDEV_UP:
+ spin_lock_bh(&card->ip_lock);
+ qeth_l3_add_ip(card, addr);
+ spin_unlock_bh(&card->ip_lock);
+ return NOTIFY_OK;
+ case NETDEV_DOWN:
+ spin_lock_bh(&card->ip_lock);
+ qeth_l3_delete_ip(card, addr);
+ spin_unlock_bh(&card->ip_lock);
+ return NOTIFY_OK;
+ default:
+ return NOTIFY_DONE;
+ }
+}
+
+static struct qeth_card *qeth_l3_get_card_from_dev(struct net_device *dev)
+{
+ if (is_vlan_dev(dev))
+ dev = vlan_dev_real_dev(dev);
+ if (dev->netdev_ops == &qeth_l3_osa_netdev_ops ||
+ dev->netdev_ops == &qeth_l3_netdev_ops)
+ return (struct qeth_card *) dev->ml_priv;
+ return NULL;
+}
+
static int qeth_l3_ip_event(struct notifier_block *this,
unsigned long event, void *ptr)
{
struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
- struct net_device *dev = (struct net_device *)ifa->ifa_dev->dev;
- struct qeth_ipaddr *addr;
+ struct net_device *dev = ifa->ifa_dev->dev;
+ struct qeth_ipaddr addr;
struct qeth_card *card;
if (dev_net(dev) != &init_net)
@@ -3147,29 +3024,11 @@ static int qeth_l3_ip_event(struct notifier_block *this,
return NOTIFY_DONE;
QETH_CARD_TEXT(card, 3, "ipevent");
- addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV4);
- if (addr) {
- addr->u.a4.addr = be32_to_cpu(ifa->ifa_address);
- addr->u.a4.mask = be32_to_cpu(ifa->ifa_mask);
- addr->type = QETH_IP_TYPE_NORMAL;
- } else
- return NOTIFY_DONE;
-
- switch (event) {
- case NETDEV_UP:
- spin_lock_bh(&card->ip_lock);
- qeth_l3_add_ip(card, addr);
- spin_unlock_bh(&card->ip_lock);
- break;
- case NETDEV_DOWN:
- spin_lock_bh(&card->ip_lock);
- qeth_l3_delete_ip(card, addr);
- spin_unlock_bh(&card->ip_lock);
- break;
- }
+ qeth_l3_init_ipaddr(&addr, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV4);
+ addr.u.a4.addr = be32_to_cpu(ifa->ifa_address);
+ addr.u.a4.mask = be32_to_cpu(ifa->ifa_mask);
- kfree(addr);
- return NOTIFY_DONE;
+ return qeth_l3_handle_ip_event(card, &addr, event);
}
static struct notifier_block qeth_l3_ip_notifier = {
@@ -3181,8 +3040,8 @@ static int qeth_l3_ip6_event(struct notifier_block *this,
unsigned long event, void *ptr)
{
struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr;
- struct net_device *dev = (struct net_device *)ifa->idev->dev;
- struct qeth_ipaddr *addr;
+ struct net_device *dev = ifa->idev->dev;
+ struct qeth_ipaddr addr;
struct qeth_card *card;
card = qeth_l3_get_card_from_dev(dev);
@@ -3192,29 +3051,11 @@ static int qeth_l3_ip6_event(struct notifier_block *this,
if (!qeth_is_supported(card, IPA_IPV6))
return NOTIFY_DONE;
- addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV6);
- if (addr) {
- memcpy(&addr->u.a6.addr, &ifa->addr, sizeof(struct in6_addr));
- addr->u.a6.pfxlen = ifa->prefix_len;
- addr->type = QETH_IP_TYPE_NORMAL;
- } else
- return NOTIFY_DONE;
-
- switch (event) {
- case NETDEV_UP:
- spin_lock_bh(&card->ip_lock);
- qeth_l3_add_ip(card, addr);
- spin_unlock_bh(&card->ip_lock);
- break;
- case NETDEV_DOWN:
- spin_lock_bh(&card->ip_lock);
- qeth_l3_delete_ip(card, addr);
- spin_unlock_bh(&card->ip_lock);
- break;
- }
+ qeth_l3_init_ipaddr(&addr, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV6);
+ addr.u.a6.addr = ifa->addr;
+ addr.u.a6.pfxlen = ifa->prefix_len;
- kfree(addr);
- return NOTIFY_DONE;
+ return qeth_l3_handle_ip_event(card, &addr, event);
}
static struct notifier_block qeth_l3_ip6_notifier = {
diff --git a/drivers/s390/net/qeth_l3_sys.c b/drivers/s390/net/qeth_l3_sys.c
index a645cfe66ddf..f61192a048f4 100644
--- a/drivers/s390/net/qeth_l3_sys.c
+++ b/drivers/s390/net/qeth_l3_sys.c
@@ -272,9 +272,8 @@ static ssize_t qeth_l3_dev_hsuid_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
- struct qeth_ipaddr *addr;
char *tmp;
- int rc, i;
+ int rc;
if (!card)
return -EINVAL;
@@ -293,25 +292,9 @@ static ssize_t qeth_l3_dev_hsuid_store(struct device *dev,
if (strlen(tmp) > 8)
return -EINVAL;
- if (card->options.hsuid[0]) {
+ if (card->options.hsuid[0])
/* delete old ip address */
- addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV6);
- if (!addr)
- return -ENOMEM;
-
- addr->u.a6.addr.s6_addr32[0] = cpu_to_be32(0xfe800000);
- addr->u.a6.addr.s6_addr32[1] = 0x00000000;
- for (i = 8; i < 16; i++)
- addr->u.a6.addr.s6_addr[i] =
- card->options.hsuid[i - 8];
- addr->u.a6.pfxlen = 0;
- addr->type = QETH_IP_TYPE_NORMAL;
-
- spin_lock_bh(&card->ip_lock);
- qeth_l3_delete_ip(card, addr);
- spin_unlock_bh(&card->ip_lock);
- kfree(addr);
- }
+ qeth_l3_modify_hsuid(card, false);
if (strlen(tmp) == 0) {
/* delete ip address only */
@@ -331,21 +314,7 @@ static ssize_t qeth_l3_dev_hsuid_store(struct device *dev,
if (card->dev)
memcpy(card->dev->perm_addr, card->options.hsuid, 9);
- addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV6);
- if (addr != NULL) {
- addr->u.a6.addr.s6_addr32[0] = cpu_to_be32(0xfe800000);
- addr->u.a6.addr.s6_addr32[1] = 0x00000000;
- for (i = 8; i < 16; i++)
- addr->u.a6.addr.s6_addr[i] = card->options.hsuid[i - 8];
- addr->u.a6.pfxlen = 0;
- addr->type = QETH_IP_TYPE_NORMAL;
- } else
- return -ENOMEM;
-
- spin_lock_bh(&card->ip_lock);
- rc = qeth_l3_add_ip(card, addr);
- spin_unlock_bh(&card->ip_lock);
- kfree(addr);
+ rc = qeth_l3_modify_hsuid(card, true);
return rc ? rc : count;
}
@@ -767,7 +736,8 @@ static ssize_t qeth_l3_dev_vipa_add_store(const char *buf, size_t count,
mutex_lock(&card->conf_mutex);
rc = qeth_l3_parse_vipae(buf, proto, addr);
if (!rc)
- rc = qeth_l3_add_vipa(card, proto, addr);
+ rc = qeth_l3_modify_rxip_vipa(card, true, addr,
+ QETH_IP_TYPE_VIPA, proto);
mutex_unlock(&card->conf_mutex);
return rc ? rc : count;
}
@@ -796,7 +766,8 @@ static ssize_t qeth_l3_dev_vipa_del_store(const char *buf, size_t count,
mutex_lock(&card->conf_mutex);
rc = qeth_l3_parse_vipae(buf, proto, addr);
if (!rc)
- rc = qeth_l3_del_vipa(card, proto, addr);
+ rc = qeth_l3_modify_rxip_vipa(card, false, addr,
+ QETH_IP_TYPE_VIPA, proto);
mutex_unlock(&card->conf_mutex);
return rc ? rc : count;
}
@@ -908,7 +879,8 @@ static ssize_t qeth_l3_dev_rxip_add_store(const char *buf, size_t count,
mutex_lock(&card->conf_mutex);
rc = qeth_l3_parse_rxipe(buf, proto, addr);
if (!rc)
- rc = qeth_l3_add_rxip(card, proto, addr);
+ rc = qeth_l3_modify_rxip_vipa(card, true, addr,
+ QETH_IP_TYPE_RXIP, proto);
mutex_unlock(&card->conf_mutex);
return rc ? rc : count;
}
@@ -937,7 +909,8 @@ static ssize_t qeth_l3_dev_rxip_del_store(const char *buf, size_t count,
mutex_lock(&card->conf_mutex);
rc = qeth_l3_parse_rxipe(buf, proto, addr);
if (!rc)
- rc = qeth_l3_del_rxip(card, proto, addr);
+ rc = qeth_l3_modify_rxip_vipa(card, false, addr,
+ QETH_IP_TYPE_RXIP, proto);
mutex_unlock(&card->conf_mutex);
return rc ? rc : count;
}
diff --git a/drivers/s390/net/smsgiucv.c b/drivers/s390/net/smsgiucv.c
index 3b0c8b8a7634..066b5c3aaae6 100644
--- a/drivers/s390/net/smsgiucv.c
+++ b/drivers/s390/net/smsgiucv.c
@@ -176,7 +176,7 @@ static struct device_driver smsg_driver = {
static void __exit smsg_exit(void)
{
- cpcmd("SET SMSG IUCV", NULL, 0, NULL);
+ cpcmd("SET SMSG OFF", NULL, 0, NULL);
device_unregister(smsg_dev);
iucv_unregister(&smsg_handler, 1);
driver_unregister(&smsg_driver);
diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c
index ca218c82321f..6162cf57a20a 100644
--- a/drivers/s390/scsi/zfcp_fc.c
+++ b/drivers/s390/scsi/zfcp_fc.c
@@ -961,7 +961,7 @@ static int zfcp_fc_exec_els_job(struct bsg_job *job,
d_id = ntoh24(bsg_request->rqst_data.h_els.port_id);
els->handler = zfcp_fc_ct_els_job_handler;
- return zfcp_fsf_send_els(adapter, d_id, els, job->req->timeout / HZ);
+ return zfcp_fsf_send_els(adapter, d_id, els, job->timeout / HZ);
}
static int zfcp_fc_exec_ct_job(struct bsg_job *job,
@@ -980,7 +980,7 @@ static int zfcp_fc_exec_ct_job(struct bsg_job *job,
return ret;
ct->handler = zfcp_fc_ct_job_handler;
- ret = zfcp_fsf_send_ct(wka_port, ct, NULL, job->req->timeout / HZ);
+ ret = zfcp_fsf_send_ct(wka_port, ct, NULL, job->timeout / HZ);
if (ret)
zfcp_fc_wka_port_put(wka_port);
diff --git a/drivers/sbus/char/Kconfig b/drivers/sbus/char/Kconfig
index a785aa7660c3..bf3c5f735614 100644
--- a/drivers/sbus/char/Kconfig
+++ b/drivers/sbus/char/Kconfig
@@ -72,7 +72,8 @@ config DISPLAY7SEG
config ORACLE_DAX
tristate "Oracle Data Analytics Accelerator"
- default m if SPARC64
+ depends on SPARC64
+ default m
help
Driver for Oracle Data Analytics Accelerator, which is
a coprocessor that performs database operations in hardware.
diff --git a/drivers/sbus/char/oradax.c b/drivers/sbus/char/oradax.c
index 03dc04739225..c44d7c7ffc92 100644
--- a/drivers/sbus/char/oradax.c
+++ b/drivers/sbus/char/oradax.c
@@ -880,7 +880,7 @@ static int dax_ccb_exec(struct dax_ctx *ctx, const char __user *buf,
dax_dbg("args: ccb_buf_len=%ld, idx=%d", count, idx);
/* for given index and length, verify ca_buf range exists */
- if (idx + nccbs >= DAX_CA_ELEMS) {
+ if (idx < 0 || idx > (DAX_CA_ELEMS - nccbs)) {
ctx->result.exec.status = DAX_SUBMIT_ERR_NO_CA_AVAIL;
return 0;
}
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 8a739b74cfb7..11e89e56b865 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -640,88 +640,6 @@ config SCSI_DMX3191D
To compile this driver as a module, choose M here: the
module will be called dmx3191d.
-config SCSI_EATA
- tristate "EATA ISA/EISA/PCI (DPT and generic EATA/DMA-compliant boards) support"
- depends on (ISA || EISA || PCI) && SCSI && ISA_DMA_API
- ---help---
- This driver supports all EATA/DMA-compliant SCSI host adapters. DPT
- ISA and all EISA I/O addresses are probed looking for the "EATA"
- signature. The addresses of all the PCI SCSI controllers reported
- by the PCI subsystem are probed as well.
-
- You want to read the start of <file:drivers/scsi/eata.c> and the
- SCSI-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here: the
- module will be called eata.
-
-config SCSI_EATA_TAGGED_QUEUE
- bool "enable tagged command queueing"
- depends on SCSI_EATA
- help
- This is a feature of SCSI-2 which improves performance: the host
- adapter can send several SCSI commands to a device's queue even if
- previous commands haven't finished yet.
- This is equivalent to the "eata=tc:y" boot option.
-
-config SCSI_EATA_LINKED_COMMANDS
- bool "enable elevator sorting"
- depends on SCSI_EATA
- help
- This option enables elevator sorting for all probed SCSI disks and
- CD-ROMs. It definitely reduces the average seek distance when doing
- random seeks, but this does not necessarily result in a noticeable
- performance improvement: your mileage may vary...
- This is equivalent to the "eata=lc:y" boot option.
-
-config SCSI_EATA_MAX_TAGS
- int "maximum number of queued commands"
- depends on SCSI_EATA
- default "16"
- help
- This specifies how many SCSI commands can be maximally queued for
- each probed SCSI device. You should reduce the default value of 16
- only if you have disks with buggy or limited tagged command support.
- Minimum is 2 and maximum is 62. This value is also the window size
- used by the elevator sorting option above. The effective value used
- by the driver for each probed SCSI device is reported at boot time.
- This is equivalent to the "eata=mq:8" boot option.
-
-config SCSI_EATA_PIO
- tristate "EATA-PIO (old DPT PM2001, PM2012A) support"
- depends on (ISA || EISA || PCI) && SCSI && BROKEN
- ---help---
- This driver supports all EATA-PIO protocol compliant SCSI Host
- Adapters like the DPT PM2001 and the PM2012A. EATA-DMA compliant
- host adapters could also use this driver but are discouraged from
- doing so, since this driver only supports hard disks and lacks
- numerous features. You might want to have a look at the SCSI-HOWTO,
- available from <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here: the
- module will be called eata_pio.
-
-config SCSI_FUTURE_DOMAIN
- tristate "Future Domain 16xx SCSI/AHA-2920A support"
- depends on (ISA || PCI) && SCSI
- select CHECK_SIGNATURE
- ---help---
- This is support for Future Domain's 16-bit SCSI host adapters
- (TMC-1660/1680, TMC-1650/1670, TMC-3260, TMC-1610M/MER/MEX) and
- other adapters based on the Future Domain chipsets (Quantum
- ISA-200S, ISA-250MG; Adaptec AHA-2920A; and at least one IBM board).
- It is explained in section 3.7 of the SCSI-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- NOTE: Newer Adaptec AHA-2920C boards use the Adaptec AIC-7850 chip
- and should use the aic7xxx driver ("Adaptec AIC7xxx chipset SCSI
- controller support"). This Future Domain driver works with the older
- Adaptec AHA-2920A boards with a Future Domain chip on them.
-
- To compile this driver as a module, choose M here: the
- module will be called fdomain.
-
config SCSI_GDTH
tristate "Intel/ICP (former GDT SCSI Disk Array) RAID Controller support"
depends on (ISA || EISA || PCI) && SCSI && ISA_DMA_API
@@ -923,18 +841,6 @@ config SCSI_IZIP_SLOW_CTR
Generally, saying N is fine.
-config SCSI_NCR53C406A
- tristate "NCR53c406a SCSI support"
- depends on ISA && SCSI
- help
- This is support for the NCR53c406a SCSI host adapter. For user
- configurable parameters, check out <file:drivers/scsi/NCR53c406a.c>
- in the kernel source. Also read the SCSI-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as a module, choose M here: the
- module will be called NCR53c406.
-
config SCSI_NCR_D700
tristate "NCR Dual 700 MCA SCSI support"
depends on MCA && SCSI
@@ -1059,6 +965,7 @@ config SCSI_IPR
depends on PCI && SCSI && ATA
select FW_LOADER
select IRQ_POLL
+ select SGL_ALLOC
---help---
This driver supports the IBM Power Linux family RAID adapters.
This includes IBM pSeries 5712, 5703, 5709, and 570A, as well
@@ -1265,24 +1172,6 @@ config SCSI_SIM710
It currently supports Compaq EISA cards and NCR MCA cards
-config SCSI_SYM53C416
- tristate "Symbios 53c416 SCSI support"
- depends on ISA && SCSI
- ---help---
- This is support for the sym53c416 SCSI host adapter, the SCSI
- adapter that comes with some HP scanners. This driver requires that
- the sym53c416 is configured first using some sort of PnP
- configuration program (e.g. isapnp) or by a PnP aware BIOS. If you
- are using isapnp then you need to compile this driver as a module
- and then load it using insmod after isapnp has run. The parameters
- of the configured card(s) should be passed to the driver. The format
- is:
-
- insmod sym53c416 sym53c416=<base>,<irq> [sym53c416_1=<base>,<irq>]
-
- To compile this driver as a module, choose M here: the
- module will be called sym53c416.
-
config SCSI_DC395x
tristate "Tekram DC395(U/UW/F) and DC315(U) SCSI support"
depends on PCI && SCSI
@@ -1576,6 +1465,7 @@ config ZFCP
config SCSI_PMCRAID
tristate "PMC SIERRA Linux MaxRAID adapter support"
depends on PCI && SCSI && NET
+ select SGL_ALLOC
---help---
This driver supports the PMC SIERRA MaxRAID adapters.
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index de1b3fce936d..e29f9b8fd66d 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -74,12 +74,9 @@ obj-$(CONFIG_SCSI_AIC94XX) += aic94xx/
obj-$(CONFIG_SCSI_PM8001) += pm8001/
obj-$(CONFIG_SCSI_ISCI) += isci/
obj-$(CONFIG_SCSI_IPS) += ips.o
-obj-$(CONFIG_SCSI_FUTURE_DOMAIN)+= fdomain.o
obj-$(CONFIG_SCSI_GENERIC_NCR5380) += g_NCR5380.o
-obj-$(CONFIG_SCSI_NCR53C406A) += NCR53c406a.o
obj-$(CONFIG_SCSI_NCR_D700) += 53c700.o NCR_D700.o
obj-$(CONFIG_SCSI_NCR_Q720) += NCR_Q720_mod.o
-obj-$(CONFIG_SCSI_SYM53C416) += sym53c416.o
obj-$(CONFIG_SCSI_QLOGIC_FAS) += qlogicfas408.o qlogicfas.o
obj-$(CONFIG_PCMCIA_QLOGIC) += qlogicfas408.o
obj-$(CONFIG_SCSI_QLOGIC_1280) += qla1280.o
@@ -93,8 +90,6 @@ obj-$(CONFIG_SCSI_HPSA) += hpsa.o
obj-$(CONFIG_SCSI_SMARTPQI) += smartpqi/
obj-$(CONFIG_SCSI_SYM53C8XX_2) += sym53c8xx_2/
obj-$(CONFIG_SCSI_ZALON) += zalon7xx.o
-obj-$(CONFIG_SCSI_EATA_PIO) += eata_pio.o
-obj-$(CONFIG_SCSI_EATA) += eata.o
obj-$(CONFIG_SCSI_DC395x) += dc395x.o
obj-$(CONFIG_SCSI_AM53C974) += esp_scsi.o am53c974.o
obj-$(CONFIG_CXLFLASH) += cxlflash/
diff --git a/drivers/scsi/NCR53c406a.c b/drivers/scsi/NCR53c406a.c
deleted file mode 100644
index 6e110c630d2c..000000000000
--- a/drivers/scsi/NCR53c406a.c
+++ /dev/null
@@ -1,1090 +0,0 @@
-/*
- * NCR53c406.c
- * Low-level SCSI driver for NCR53c406a chip.
- * Copyright (C) 1994, 1995, 1996 Normunds Saumanis (normunds@fi.ibm.com)
- *
- * LILO command line usage: ncr53c406a=<PORTBASE>[,<IRQ>[,<FASTPIO>]]
- * Specify IRQ = 0 for non-interrupt driven mode.
- * FASTPIO = 1 for fast pio mode, 0 for slow mode.
- *
- * 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.
- *
- */
-
-#define NCR53C406A_DEBUG 0
-#define VERBOSE_NCR53C406A_DEBUG 0
-
-/* Set this to 1 for PIO mode (recommended) or to 0 for DMA mode */
-#define USE_PIO 1
-
-#define USE_BIOS 0
- /* #define BIOS_ADDR 0xD8000 *//* define this if autoprobe fails */
- /* #define PORT_BASE 0x330 *//* define this if autoprobe fails */
- /* #define IRQ_LEV 0 *//* define this if autoprobe fails */
-#define DMA_CHAN 5 /* this is ignored if DMA is disabled */
-
-/* Set this to 0 if you encounter kernel lockups while transferring
- * data in PIO mode */
-#define USE_FAST_PIO 1
-
-/* ============= End of user configurable parameters ============= */
-
-#include <linux/module.h>
-
-#include <linux/errno.h>
-#include <linux/ioport.h>
-#include <linux/interrupt.h>
-#include <linux/proc_fs.h>
-#include <linux/stat.h>
-#include <linux/init.h>
-#include <linux/bitops.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <asm/irq.h>
-
-#include <linux/blkdev.h>
-#include <linux/spinlock.h>
-#include "scsi.h"
-#include <scsi/scsi_host.h>
-
-/* ============================================================= */
-
-#define WATCHDOG 5000000
-
-#define SYNC_MODE 0 /* Synchronous transfer mode */
-
-#ifdef DEBUG
-#undef NCR53C406A_DEBUG
-#define NCR53C406A_DEBUG 1
-#endif
-
-#if USE_PIO
-#define USE_DMA 0
-#else
-#define USE_DMA 1
-#endif
-
-/* Default configuration */
-#define C1_IMG 0x07 /* ID=7 */
-#define C2_IMG 0x48 /* FE SCSI2 */
-#if USE_DMA
-#define C3_IMG 0x21 /* CDB TE */
-#else
-#define C3_IMG 0x20 /* CDB */
-#endif
-#define C4_IMG 0x04 /* ANE */
-#define C5_IMG 0xb6 /* AA PI SIE POL */
-
-#define REG0 (outb(C4_IMG, CONFIG4))
-#define REG1 (outb(C5_IMG, CONFIG5))
-
-#if NCR53C406A_DEBUG
-#define DEB(x) x
-#else
-#define DEB(x)
-#endif
-
-#if VERBOSE_NCR53C406A_DEBUG
-#define VDEB(x) x
-#else
-#define VDEB(x)
-#endif
-
-#define LOAD_DMA_COUNT(count) \
- outb(count & 0xff, TC_LSB); \
- outb((count >> 8) & 0xff, TC_MSB); \
- outb((count >> 16) & 0xff, TC_HIGH);
-
-/* Chip commands */
-#define DMA_OP 0x80
-
-#define SCSI_NOP 0x00
-#define FLUSH_FIFO 0x01
-#define CHIP_RESET 0x02
-#define SCSI_RESET 0x03
-#define RESELECT 0x40
-#define SELECT_NO_ATN 0x41
-#define SELECT_ATN 0x42
-#define SELECT_ATN_STOP 0x43
-#define ENABLE_SEL 0x44
-#define DISABLE_SEL 0x45
-#define SELECT_ATN3 0x46
-#define RESELECT3 0x47
-#define TRANSFER_INFO 0x10
-#define INIT_CMD_COMPLETE 0x11
-#define MSG_ACCEPT 0x12
-#define TRANSFER_PAD 0x18
-#define SET_ATN 0x1a
-#define RESET_ATN 0x1b
-#define SEND_MSG 0x20
-#define SEND_STATUS 0x21
-#define SEND_DATA 0x22
-#define DISCONN_SEQ 0x23
-#define TERMINATE_SEQ 0x24
-#define TARG_CMD_COMPLETE 0x25
-#define DISCONN 0x27
-#define RECV_MSG 0x28
-#define RECV_CMD 0x29
-#define RECV_DATA 0x2a
-#define RECV_CMD_SEQ 0x2b
-#define TARGET_ABORT_DMA 0x04
-
-/*----------------------------------------------------------------*/
-/* the following will set the monitor border color (useful to find
- where something crashed or gets stuck at */
-/* 1 = blue
- 2 = green
- 3 = cyan
- 4 = red
- 5 = magenta
- 6 = yellow
- 7 = white
-*/
-
-#if NCR53C406A_DEBUG
-#define rtrc(i) {inb(0x3da);outb(0x31,0x3c0);outb((i),0x3c0);}
-#else
-#define rtrc(i) {}
-#endif
-/*----------------------------------------------------------------*/
-
-enum Phase {
- idle,
- data_out,
- data_in,
- command_ph,
- status_ph,
- message_out,
- message_in
-};
-
-/* Static function prototypes */
-static void NCR53c406a_intr(void *);
-static irqreturn_t do_NCR53c406a_intr(int, void *);
-static void chip_init(void);
-static void calc_port_addr(void);
-#ifndef IRQ_LEV
-static int irq_probe(void);
-#endif
-
-/* ================================================================= */
-
-#if USE_BIOS
-static void *bios_base;
-#endif
-
-#ifdef PORT_BASE
-static int port_base = PORT_BASE;
-#else
-static int port_base;
-#endif
-
-#ifdef IRQ_LEV
-static int irq_level = IRQ_LEV;
-#else
-static int irq_level = -1; /* 0 is 'no irq', so use -1 for 'uninitialized' */
-#endif
-
-#if USE_DMA
-static int dma_chan;
-#endif
-
-#if USE_PIO
-static int fast_pio = USE_FAST_PIO;
-#endif
-
-static Scsi_Cmnd *current_SC;
-static char info_msg[256];
-
-/* ================================================================= */
-
-/* possible BIOS locations */
-#if USE_BIOS
-static void *addresses[] = {
- (void *) 0xd8000,
- (void *) 0xc8000
-};
-#define ADDRESS_COUNT ARRAY_SIZE(addresses)
-#endif /* USE_BIOS */
-
-/* possible i/o port addresses */
-static unsigned short ports[] = { 0x230, 0x330, 0x280, 0x290, 0x330, 0x340, 0x300, 0x310, 0x348, 0x350 };
-#define PORT_COUNT ARRAY_SIZE(ports)
-
-#ifndef MODULE
-/* possible interrupt channels */
-static unsigned short intrs[] = { 10, 11, 12, 15 };
-#define INTR_COUNT ARRAY_SIZE(intrs)
-#endif /* !MODULE */
-
-/* signatures for NCR 53c406a based controllers */
-#if USE_BIOS
-struct signature {
- char *signature;
- int sig_offset;
- int sig_length;
-} signatures[] __initdata = {
- /* 1 2 3 4 5 6 */
- /* 123456789012345678901234567890123456789012345678901234567890 */
- {
-"Copyright (C) Acculogic, Inc.\r\n2.8M Diskette Extension Bios ver 4.04.03 03/01/1993", 61, 82},};
-
-#define SIGNATURE_COUNT ARRAY_SIZE(signatures)
-#endif /* USE_BIOS */
-
-/* ============================================================ */
-
-/* Control Register Set 0 */
-static int TC_LSB; /* transfer counter lsb */
-static int TC_MSB; /* transfer counter msb */
-static int SCSI_FIFO; /* scsi fifo register */
-static int CMD_REG; /* command register */
-static int STAT_REG; /* status register */
-static int DEST_ID; /* selection/reselection bus id */
-static int INT_REG; /* interrupt status register */
-static int SRTIMOUT; /* select/reselect timeout reg */
-static int SEQ_REG; /* sequence step register */
-static int SYNCPRD; /* synchronous transfer period */
-static int FIFO_FLAGS; /* indicates # of bytes in fifo */
-static int SYNCOFF; /* synchronous offset register */
-static int CONFIG1; /* configuration register */
-static int CLKCONV; /* clock conversion reg */
- /*static int TESTREG;*//* test mode register */
-static int CONFIG2; /* Configuration 2 Register */
-static int CONFIG3; /* Configuration 3 Register */
-static int CONFIG4; /* Configuration 4 Register */
-static int TC_HIGH; /* Transfer Counter High */
- /*static int FIFO_BOTTOM;*//* Reserve FIFO byte register */
-
-/* Control Register Set 1 */
- /*static int JUMPER_SENSE;*//* Jumper sense port reg (r/w) */
- /*static int SRAM_PTR;*//* SRAM address pointer reg (r/w) */
- /*static int SRAM_DATA;*//* SRAM data register (r/w) */
-static int PIO_FIFO; /* PIO FIFO registers (r/w) */
- /*static int PIO_FIFO1;*//* */
- /*static int PIO_FIFO2;*//* */
- /*static int PIO_FIFO3;*//* */
-static int PIO_STATUS; /* PIO status (r/w) */
- /*static int ATA_CMD;*//* ATA command/status reg (r/w) */
- /*static int ATA_ERR;*//* ATA features/error register (r/w) */
-static int PIO_FLAG; /* PIO flag interrupt enable (r/w) */
-static int CONFIG5; /* Configuration 5 register (r/w) */
- /*static int SIGNATURE;*//* Signature Register (r) */
- /*static int CONFIG6;*//* Configuration 6 register (r) */
-
-/* ============================================================== */
-
-#if USE_DMA
-static __inline__ int NCR53c406a_dma_setup(unsigned char *ptr, unsigned int count, unsigned char mode)
-{
- unsigned limit;
- unsigned long flags = 0;
-
- VDEB(printk("dma: before count=%d ", count));
- if (dma_chan <= 3) {
- if (count > 65536)
- count = 65536;
- limit = 65536 - (((unsigned) ptr) & 0xFFFF);
- } else {
- if (count > (65536 << 1))
- count = (65536 << 1);
- limit = (65536 << 1) - (((unsigned) ptr) & 0x1FFFF);
- }
-
- if (count > limit)
- count = limit;
-
- VDEB(printk("after count=%d\n", count));
- if ((count & 1) || (((unsigned) ptr) & 1))
- panic("NCR53c406a: attempted unaligned DMA transfer\n");
-
- flags = claim_dma_lock();
- disable_dma(dma_chan);
- clear_dma_ff(dma_chan);
- set_dma_addr(dma_chan, (long) ptr);
- set_dma_count(dma_chan, count);
- set_dma_mode(dma_chan, mode);
- enable_dma(dma_chan);
- release_dma_lock(flags);
-
- return count;
-}
-
-static __inline__ int NCR53c406a_dma_write(unsigned char *src, unsigned int count)
-{
- return NCR53c406a_dma_setup(src, count, DMA_MODE_WRITE);
-}
-
-static __inline__ int NCR53c406a_dma_read(unsigned char *src, unsigned int count)
-{
- return NCR53c406a_dma_setup(src, count, DMA_MODE_READ);
-}
-
-static __inline__ int NCR53c406a_dma_residual(void)
-{
- register int tmp;
- unsigned long flags;
-
- flags = claim_dma_lock();
- clear_dma_ff(dma_chan);
- tmp = get_dma_residue(dma_chan);
- release_dma_lock(flags);
-
- return tmp;
-}
-#endif /* USE_DMA */
-
-#if USE_PIO
-static __inline__ int NCR53c406a_pio_read(unsigned char *request, unsigned int reqlen)
-{
- int i;
- int len; /* current scsi fifo size */
-
- REG1;
- while (reqlen) {
- i = inb(PIO_STATUS);
- /* VDEB(printk("pio_status=%x\n", i)); */
- if (i & 0x80)
- return 0;
-
- switch (i & 0x1e) {
- default:
- case 0x10:
- len = 0;
- break;
- case 0x0:
- len = 1;
- break;
- case 0x8:
- len = 42;
- break;
- case 0xc:
- len = 84;
- break;
- case 0xe:
- len = 128;
- break;
- }
-
- if ((i & 0x40) && len == 0) { /* fifo empty and interrupt occurred */
- return 0;
- }
-
- if (len) {
- if (len > reqlen)
- len = reqlen;
-
- if (fast_pio && len > 3) {
- insl(PIO_FIFO, request, len >> 2);
- request += len & 0xfc;
- reqlen -= len & 0xfc;
- } else {
- while (len--) {
- *request++ = inb(PIO_FIFO);
- reqlen--;
- }
- }
- }
- }
- return 0;
-}
-
-static __inline__ int NCR53c406a_pio_write(unsigned char *request, unsigned int reqlen)
-{
- int i = 0;
- int len; /* current scsi fifo size */
-
- REG1;
- while (reqlen && !(i & 0x40)) {
- i = inb(PIO_STATUS);
- /* VDEB(printk("pio_status=%x\n", i)); */
- if (i & 0x80) /* error */
- return 0;
-
- switch (i & 0x1e) {
- case 0x10:
- len = 128;
- break;
- case 0x0:
- len = 84;
- break;
- case 0x8:
- len = 42;
- break;
- case 0xc:
- len = 1;
- break;
- default:
- case 0xe:
- len = 0;
- break;
- }
-
- if (len) {
- if (len > reqlen)
- len = reqlen;
-
- if (fast_pio && len > 3) {
- outsl(PIO_FIFO, request, len >> 2);
- request += len & 0xfc;
- reqlen -= len & 0xfc;
- } else {
- while (len--) {
- outb(*request++, PIO_FIFO);
- reqlen--;
- }
- }
- }
- }
- return 0;
-}
-#endif /* USE_PIO */
-
-static int __init NCR53c406a_detect(struct scsi_host_template * tpnt)
-{
- int present = 0;
- struct Scsi_Host *shpnt = NULL;
-#ifndef PORT_BASE
- int i;
-#endif
-
-#if USE_BIOS
- int ii, jj;
- bios_base = 0;
- /* look for a valid signature */
- for (ii = 0; ii < ADDRESS_COUNT && !bios_base; ii++)
- for (jj = 0; (jj < SIGNATURE_COUNT) && !bios_base; jj++)
- if (!memcmp((void *) addresses[ii] + signatures[jj].sig_offset, (void *) signatures[jj].signature, (int) signatures[jj].sig_length))
- bios_base = addresses[ii];
-
- if (!bios_base) {
- printk("NCR53c406a: BIOS signature not found\n");
- return 0;
- }
-
- DEB(printk("NCR53c406a BIOS found at 0x%x\n", (unsigned int) bios_base);
- );
-#endif /* USE_BIOS */
-
-#ifdef PORT_BASE
- if (!request_region(port_base, 0x10, "NCR53c406a")) /* ports already snatched */
- port_base = 0;
-
-#else /* autodetect */
- if (port_base) { /* LILO override */
- if (!request_region(port_base, 0x10, "NCR53c406a"))
- port_base = 0;
- } else {
- for (i = 0; i < PORT_COUNT && !port_base; i++) {
- if (!request_region(ports[i], 0x10, "NCR53c406a")) {
- DEB(printk("NCR53c406a: port 0x%x in use\n", ports[i]));
- } else {
- VDEB(printk("NCR53c406a: port 0x%x available\n", ports[i]));
- outb(C5_IMG, ports[i] + 0x0d); /* reg set 1 */
- if ((inb(ports[i] + 0x0e) ^ inb(ports[i] + 0x0e)) == 7 && (inb(ports[i] + 0x0e) ^ inb(ports[i] + 0x0e)) == 7 && (inb(ports[i] + 0x0e) & 0xf8) == 0x58) {
- port_base = ports[i];
- VDEB(printk("NCR53c406a: Sig register valid\n"));
- VDEB(printk("port_base=0x%x\n", port_base));
- break;
- }
- release_region(ports[i], 0x10);
- }
- }
- }
-#endif /* PORT_BASE */
-
- if (!port_base) { /* no ports found */
- printk("NCR53c406a: no available ports found\n");
- return 0;
- }
-
- DEB(printk("NCR53c406a detected\n"));
-
- calc_port_addr();
- chip_init();
-
-#ifndef IRQ_LEV
- if (irq_level < 0) { /* LILO override if >= 0 */
- irq_level = irq_probe();
- if (irq_level < 0) { /* Trouble */
- printk("NCR53c406a: IRQ problem, irq_level=%d, giving up\n", irq_level);
- goto err_release;
- }
- }
-#endif
-
- DEB(printk("NCR53c406a: using port_base 0x%x\n", port_base));
-
- present = 1;
- tpnt->proc_name = "NCR53c406a";
-
- shpnt = scsi_register(tpnt, 0);
- if (!shpnt) {
- printk("NCR53c406a: Unable to register host, giving up.\n");
- goto err_release;
- }
-
- if (irq_level > 0) {
- if (request_irq(irq_level, do_NCR53c406a_intr, 0, "NCR53c406a", shpnt)) {
- printk("NCR53c406a: unable to allocate IRQ %d\n", irq_level);
- goto err_free_scsi;
- }
- tpnt->can_queue = 1;
- DEB(printk("NCR53c406a: allocated IRQ %d\n", irq_level));
- } else if (irq_level == 0) {
- tpnt->can_queue = 0;
- DEB(printk("NCR53c406a: No interrupts detected\n"));
- printk("NCR53c406a driver no longer supports polling interface\n");
- printk("Please email linux-scsi@vger.kernel.org\n");
-
-#if USE_DMA
- printk("NCR53c406a: No interrupts found and DMA mode defined. Giving up.\n");
-#endif /* USE_DMA */
- goto err_free_scsi;
- } else {
- DEB(printk("NCR53c406a: Shouldn't get here!\n"));
- goto err_free_scsi;
- }
-
-#if USE_DMA
- dma_chan = DMA_CHAN;
- if (request_dma(dma_chan, "NCR53c406a") != 0) {
- printk("NCR53c406a: unable to allocate DMA channel %d\n", dma_chan);
- goto err_free_irq;
- }
-
- DEB(printk("Allocated DMA channel %d\n", dma_chan));
-#endif /* USE_DMA */
-
- shpnt->irq = irq_level;
- shpnt->io_port = port_base;
- shpnt->n_io_port = 0x10;
-#if USE_DMA
- shpnt->dma = dma_chan;
-#endif
-
-#if USE_DMA
- sprintf(info_msg, "NCR53c406a at 0x%x, IRQ %d, DMA channel %d.", port_base, irq_level, dma_chan);
-#else
- sprintf(info_msg, "NCR53c406a at 0x%x, IRQ %d, %s PIO mode.", port_base, irq_level, fast_pio ? "fast" : "slow");
-#endif
-
- return (present);
-
-#if USE_DMA
- err_free_irq:
- if (irq_level)
- free_irq(irq_level, shpnt);
-#endif
- err_free_scsi:
- scsi_unregister(shpnt);
- err_release:
- release_region(port_base, 0x10);
- return 0;
-}
-
-static int NCR53c406a_release(struct Scsi_Host *shost)
-{
- if (shost->irq)
- free_irq(shost->irq, NULL);
-#if USE_DMA
- if (shost->dma_channel != 0xff)
- free_dma(shost->dma_channel);
-#endif
- if (shost->io_port && shost->n_io_port)
- release_region(shost->io_port, shost->n_io_port);
-
- scsi_unregister(shost);
- return 0;
-}
-
-#ifndef MODULE
-/* called from init/main.c */
-static int __init NCR53c406a_setup(char *str)
-{
- static size_t setup_idx = 0;
- size_t i;
- int ints[4];
-
- DEB(printk("NCR53c406a: Setup called\n");
- );
-
- if (setup_idx >= PORT_COUNT - 1) {
- printk("NCR53c406a: Setup called too many times. Bad LILO params?\n");
- return 0;
- }
- get_options(str, 4, ints);
- if (ints[0] < 1 || ints[0] > 3) {
- printk("NCR53c406a: Malformed command line\n");
- printk("NCR53c406a: Usage: ncr53c406a=<PORTBASE>[,<IRQ>[,<FASTPIO>]]\n");
- return 0;
- }
- for (i = 0; i < PORT_COUNT && !port_base; i++)
- if (ports[i] == ints[1]) {
- port_base = ints[1];
- DEB(printk("NCR53c406a: Specified port_base 0x%x\n", port_base);
- )
- }
- if (!port_base) {
- printk("NCR53c406a: Invalid PORTBASE 0x%x specified\n", ints[1]);
- return 0;
- }
-
- if (ints[0] > 1) {
- if (ints[2] == 0) {
- irq_level = 0;
- DEB(printk("NCR53c406a: Specified irq %d\n", irq_level);
- )
- } else
- for (i = 0; i < INTR_COUNT && irq_level < 0; i++)
- if (intrs[i] == ints[2]) {
- irq_level = ints[2];
- DEB(printk("NCR53c406a: Specified irq %d\n", port_base);
- )
- }
- if (irq_level < 0)
- printk("NCR53c406a: Invalid IRQ %d specified\n", ints[2]);
- }
-
- if (ints[0] > 2)
- fast_pio = ints[3];
-
- DEB(printk("NCR53c406a: port_base=0x%x, irq=%d, fast_pio=%d\n", port_base, irq_level, fast_pio);)
- return 1;
-}
-
-__setup("ncr53c406a=", NCR53c406a_setup);
-
-#endif /* !MODULE */
-
-static const char *NCR53c406a_info(struct Scsi_Host *SChost)
-{
- DEB(printk("NCR53c406a_info called\n"));
- return (info_msg);
-}
-
-#if 0
-static void wait_intr(void)
-{
- unsigned long i = jiffies + WATCHDOG;
-
- while (time_after(i, jiffies) && !(inb(STAT_REG) & 0xe0)) { /* wait for a pseudo-interrupt */
- cpu_relax();
- barrier();
- }
-
- if (time_before_eq(i, jiffies)) { /* Timed out */
- rtrc(0);
- current_SC->result = DID_TIME_OUT << 16;
- current_SC->SCp.phase = idle;
- current_SC->scsi_done(current_SC);
- return;
- }
-
- NCR53c406a_intr(NULL);
-}
-#endif
-
-static int NCR53c406a_queue_lck(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
-{
- int i;
-
- VDEB(printk("NCR53c406a_queue called\n"));
- DEB(printk("cmd=%02x, cmd_len=%02x, target=%02x, lun=%02x, bufflen=%d\n", SCpnt->cmnd[0], SCpnt->cmd_len, SCpnt->device->target, (u8)SCpnt->device->lun, scsi_bufflen(SCpnt)));
-
-#if 0
- VDEB(for (i = 0; i < SCpnt->cmd_len; i++)
- printk("cmd[%d]=%02x ", i, SCpnt->cmnd[i]));
- VDEB(printk("\n"));
-#endif
-
- current_SC = SCpnt;
- current_SC->scsi_done = done;
- current_SC->SCp.phase = command_ph;
- current_SC->SCp.Status = 0;
- current_SC->SCp.Message = 0;
-
- /* We are locked here already by the mid layer */
- REG0;
- outb(scmd_id(SCpnt), DEST_ID); /* set destination */
- outb(FLUSH_FIFO, CMD_REG); /* reset the fifos */
-
- for (i = 0; i < SCpnt->cmd_len; i++) {
- outb(SCpnt->cmnd[i], SCSI_FIFO);
- }
- outb(SELECT_NO_ATN, CMD_REG);
-
- rtrc(1);
- return 0;
-}
-
-static DEF_SCSI_QCMD(NCR53c406a_queue)
-
-static int NCR53c406a_host_reset(Scsi_Cmnd * SCpnt)
-{
- DEB(printk("NCR53c406a_reset called\n"));
-
- spin_lock_irq(SCpnt->device->host->host_lock);
-
- outb(C4_IMG, CONFIG4); /* Select reg set 0 */
- outb(CHIP_RESET, CMD_REG);
- outb(SCSI_NOP, CMD_REG); /* required after reset */
- outb(SCSI_RESET, CMD_REG);
- chip_init();
-
- rtrc(2);
-
- spin_unlock_irq(SCpnt->device->host->host_lock);
-
- return SUCCESS;
-}
-
-static int NCR53c406a_biosparm(struct scsi_device *disk,
- struct block_device *dev,
- sector_t capacity, int *info_array)
-{
- int size;
-
- DEB(printk("NCR53c406a_biosparm called\n"));
-
- size = capacity;
- info_array[0] = 64; /* heads */
- info_array[1] = 32; /* sectors */
- info_array[2] = size >> 11; /* cylinders */
- if (info_array[2] > 1024) { /* big disk */
- info_array[0] = 255;
- info_array[1] = 63;
- info_array[2] = size / (255 * 63);
- }
- return 0;
-}
-
-static irqreturn_t do_NCR53c406a_intr(int unused, void *dev_id)
-{
- unsigned long flags;
- struct Scsi_Host *dev = dev_id;
-
- spin_lock_irqsave(dev->host_lock, flags);
- NCR53c406a_intr(dev_id);
- spin_unlock_irqrestore(dev->host_lock, flags);
- return IRQ_HANDLED;
-}
-
-static void NCR53c406a_intr(void *dev_id)
-{
- DEB(unsigned char fifo_size;
- )
- DEB(unsigned char seq_reg;
- )
- unsigned char status, int_reg;
-#if USE_PIO
- unsigned char pio_status;
- struct scatterlist *sg;
- int i;
-#endif
-
- VDEB(printk("NCR53c406a_intr called\n"));
-
-#if USE_PIO
- REG1;
- pio_status = inb(PIO_STATUS);
-#endif
- REG0;
- status = inb(STAT_REG);
- DEB(seq_reg = inb(SEQ_REG));
- int_reg = inb(INT_REG);
- DEB(fifo_size = inb(FIFO_FLAGS) & 0x1f);
-
-#if NCR53C406A_DEBUG
- printk("status=%02x, seq_reg=%02x, int_reg=%02x, fifo_size=%02x", status, seq_reg, int_reg, fifo_size);
-#if (USE_DMA)
- printk("\n");
-#else
- printk(", pio=%02x\n", pio_status);
-#endif /* USE_DMA */
-#endif /* NCR53C406A_DEBUG */
-
- if (int_reg & 0x80) { /* SCSI reset intr */
- rtrc(3);
- DEB(printk("NCR53c406a: reset intr received\n"));
- current_SC->SCp.phase = idle;
- current_SC->result = DID_RESET << 16;
- current_SC->scsi_done(current_SC);
- return;
- }
-#if USE_PIO
- if (pio_status & 0x80) {
- printk("NCR53C406A: Warning: PIO error!\n");
- current_SC->SCp.phase = idle;
- current_SC->result = DID_ERROR << 16;
- current_SC->scsi_done(current_SC);
- return;
- }
-#endif /* USE_PIO */
-
- if (status & 0x20) { /* Parity error */
- printk("NCR53c406a: Warning: parity error!\n");
- current_SC->SCp.phase = idle;
- current_SC->result = DID_PARITY << 16;
- current_SC->scsi_done(current_SC);
- return;
- }
-
- if (status & 0x40) { /* Gross error */
- printk("NCR53c406a: Warning: gross error!\n");
- current_SC->SCp.phase = idle;
- current_SC->result = DID_ERROR << 16;
- current_SC->scsi_done(current_SC);
- return;
- }
-
- if (int_reg & 0x20) { /* Disconnect */
- DEB(printk("NCR53c406a: disconnect intr received\n"));
- if (current_SC->SCp.phase != message_in) { /* Unexpected disconnect */
- current_SC->result = DID_NO_CONNECT << 16;
- } else { /* Command complete, return status and message */
- current_SC->result = (current_SC->SCp.Status & 0xff)
- | ((current_SC->SCp.Message & 0xff) << 8) | (DID_OK << 16);
- }
-
- rtrc(0);
- current_SC->SCp.phase = idle;
- current_SC->scsi_done(current_SC);
- return;
- }
-
- switch (status & 0x07) { /* scsi phase */
- case 0x00: /* DATA-OUT */
- if (int_reg & 0x10) { /* Target requesting info transfer */
- rtrc(5);
- current_SC->SCp.phase = data_out;
- VDEB(printk("NCR53c406a: Data-Out phase\n"));
- outb(FLUSH_FIFO, CMD_REG);
- LOAD_DMA_COUNT(scsi_bufflen(current_SC)); /* Max transfer size */
-#if USE_DMA /* No s/g support for DMA */
- NCR53c406a_dma_write(scsi_sglist(current_SC),
- scsdi_bufflen(current_SC));
-
-#endif /* USE_DMA */
- outb(TRANSFER_INFO | DMA_OP, CMD_REG);
-#if USE_PIO
- scsi_for_each_sg(current_SC, sg, scsi_sg_count(current_SC), i) {
- NCR53c406a_pio_write(sg_virt(sg), sg->length);
- }
- REG0;
-#endif /* USE_PIO */
- }
- break;
-
- case 0x01: /* DATA-IN */
- if (int_reg & 0x10) { /* Target requesting info transfer */
- rtrc(6);
- current_SC->SCp.phase = data_in;
- VDEB(printk("NCR53c406a: Data-In phase\n"));
- outb(FLUSH_FIFO, CMD_REG);
- LOAD_DMA_COUNT(scsi_bufflen(current_SC)); /* Max transfer size */
-#if USE_DMA /* No s/g support for DMA */
- NCR53c406a_dma_read(scsi_sglist(current_SC),
- scsdi_bufflen(current_SC));
-#endif /* USE_DMA */
- outb(TRANSFER_INFO | DMA_OP, CMD_REG);
-#if USE_PIO
- scsi_for_each_sg(current_SC, sg, scsi_sg_count(current_SC), i) {
- NCR53c406a_pio_read(sg_virt(sg), sg->length);
- }
- REG0;
-#endif /* USE_PIO */
- }
- break;
-
- case 0x02: /* COMMAND */
- current_SC->SCp.phase = command_ph;
- printk("NCR53c406a: Warning: Unknown interrupt occurred in command phase!\n");
- break;
-
- case 0x03: /* STATUS */
- rtrc(7);
- current_SC->SCp.phase = status_ph;
- VDEB(printk("NCR53c406a: Status phase\n"));
- outb(FLUSH_FIFO, CMD_REG);
- outb(INIT_CMD_COMPLETE, CMD_REG);
- break;
-
- case 0x04: /* Reserved */
- case 0x05: /* Reserved */
- printk("NCR53c406a: WARNING: Reserved phase!!!\n");
- break;
-
- case 0x06: /* MESSAGE-OUT */
- DEB(printk("NCR53c406a: Message-Out phase\n"));
- current_SC->SCp.phase = message_out;
- outb(SET_ATN, CMD_REG); /* Reject the message */
- outb(MSG_ACCEPT, CMD_REG);
- break;
-
- case 0x07: /* MESSAGE-IN */
- rtrc(4);
- VDEB(printk("NCR53c406a: Message-In phase\n"));
- current_SC->SCp.phase = message_in;
-
- current_SC->SCp.Status = inb(SCSI_FIFO);
- current_SC->SCp.Message = inb(SCSI_FIFO);
-
- VDEB(printk("SCSI FIFO size=%d\n", inb(FIFO_FLAGS) & 0x1f));
- DEB(printk("Status = %02x Message = %02x\n", current_SC->SCp.Status, current_SC->SCp.Message));
-
- if (current_SC->SCp.Message == SAVE_POINTERS || current_SC->SCp.Message == DISCONNECT) {
- outb(SET_ATN, CMD_REG); /* Reject message */
- DEB(printk("Discarding SAVE_POINTERS message\n"));
- }
- outb(MSG_ACCEPT, CMD_REG);
- break;
- }
-}
-
-#ifndef IRQ_LEV
-static int irq_probe(void)
-{
- int irqs, irq;
- unsigned long i;
-
- inb(INT_REG); /* clear the interrupt register */
- irqs = probe_irq_on();
-
- /* Invalid command will cause an interrupt */
- REG0;
- outb(0xff, CMD_REG);
-
- /* Wait for the interrupt to occur */
- i = jiffies + WATCHDOG;
- while (time_after(i, jiffies) && !(inb(STAT_REG) & 0x80))
- barrier();
- if (time_before_eq(i, jiffies)) { /* Timed out, must be hardware trouble */
- probe_irq_off(irqs);
- return -1;
- }
-
- irq = probe_irq_off(irqs);
-
- /* Kick the chip */
- outb(CHIP_RESET, CMD_REG);
- outb(SCSI_NOP, CMD_REG);
- chip_init();
-
- return irq;
-}
-#endif /* IRQ_LEV */
-
-static void chip_init(void)
-{
- REG1;
-#if USE_DMA
- outb(0x00, PIO_STATUS);
-#else /* USE_PIO */
- outb(0x01, PIO_STATUS);
-#endif
- outb(0x00, PIO_FLAG);
-
- outb(C4_IMG, CONFIG4); /* REG0; */
- outb(C3_IMG, CONFIG3);
- outb(C2_IMG, CONFIG2);
- outb(C1_IMG, CONFIG1);
-
- outb(0x05, CLKCONV); /* clock conversion factor */
- outb(0x9C, SRTIMOUT); /* Selection timeout */
- outb(0x05, SYNCPRD); /* Synchronous transfer period */
- outb(SYNC_MODE, SYNCOFF); /* synchronous mode */
-}
-
-static void __init calc_port_addr(void)
-{
- /* Control Register Set 0 */
- TC_LSB = (port_base + 0x00);
- TC_MSB = (port_base + 0x01);
- SCSI_FIFO = (port_base + 0x02);
- CMD_REG = (port_base + 0x03);
- STAT_REG = (port_base + 0x04);
- DEST_ID = (port_base + 0x04);
- INT_REG = (port_base + 0x05);
- SRTIMOUT = (port_base + 0x05);
- SEQ_REG = (port_base + 0x06);
- SYNCPRD = (port_base + 0x06);
- FIFO_FLAGS = (port_base + 0x07);
- SYNCOFF = (port_base + 0x07);
- CONFIG1 = (port_base + 0x08);
- CLKCONV = (port_base + 0x09);
- /* TESTREG = (port_base+0x0A); */
- CONFIG2 = (port_base + 0x0B);
- CONFIG3 = (port_base + 0x0C);
- CONFIG4 = (port_base + 0x0D);
- TC_HIGH = (port_base + 0x0E);
- /* FIFO_BOTTOM = (port_base+0x0F); */
-
- /* Control Register Set 1 */
- /* JUMPER_SENSE = (port_base+0x00); */
- /* SRAM_PTR = (port_base+0x01); */
- /* SRAM_DATA = (port_base+0x02); */
- PIO_FIFO = (port_base + 0x04);
- /* PIO_FIFO1 = (port_base+0x05); */
- /* PIO_FIFO2 = (port_base+0x06); */
- /* PIO_FIFO3 = (port_base+0x07); */
- PIO_STATUS = (port_base + 0x08);
- /* ATA_CMD = (port_base+0x09); */
- /* ATA_ERR = (port_base+0x0A); */
- PIO_FLAG = (port_base + 0x0B);
- CONFIG5 = (port_base + 0x0D);
- /* SIGNATURE = (port_base+0x0E); */
- /* CONFIG6 = (port_base+0x0F); */
-}
-
-MODULE_LICENSE("GPL");
-
-/* NOTE: scatter-gather support only works in PIO mode.
- * Use SG_NONE if DMA mode is enabled!
- */
-
-static struct scsi_host_template driver_template =
-{
- .proc_name = "NCR53c406a" /* proc_name */,
- .name = "NCR53c406a" /* name */,
- .detect = NCR53c406a_detect /* detect */,
- .release = NCR53c406a_release,
- .info = NCR53c406a_info /* info */,
- .queuecommand = NCR53c406a_queue /* queuecommand */,
- .eh_host_reset_handler = NCR53c406a_host_reset /* reset */,
- .bios_param = NCR53c406a_biosparm /* biosparm */,
- .can_queue = 1 /* can_queue */,
- .this_id = 7 /* SCSI ID of the chip */,
- .sg_tablesize = 32 /*SG_ALL*/ /*SG_NONE*/,
- .unchecked_isa_dma = 1 /* unchecked_isa_dma */,
- .use_clustering = ENABLE_CLUSTERING,
-};
-
-#include "scsi_module.c"
-
-/*
- * Overrides for Emacs so that we get a uniform tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 4
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -4
- * c-argdecl-indent: 4
- * c-label-offset: -4
- * c-continued-statement-offset: 4
- * c-continued-brace-offset: 0
- * indent-tabs-mode: nil
- * tab-width: 8
- * End:
- */
diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h
index 0095fcbd1c88..29bf1e60f542 100644
--- a/drivers/scsi/aacraid/aacraid.h
+++ b/drivers/scsi/aacraid/aacraid.h
@@ -1231,6 +1231,7 @@ struct src_registers {
#define SRC_ODR_SHIFT 12
#define SRC_IDR_SHIFT 9
+#define SRC_MSI_READ_MASK 0x1000
typedef void (*fib_callback)(void *ctxt, struct fib *fibctx);
@@ -1528,6 +1529,7 @@ struct aac_bus_info_response {
#define AAC_COMM_MESSAGE_TYPE3 5
#define AAC_EXTOPT_SA_FIRMWARE cpu_to_le32(1<<1)
+#define AAC_EXTOPT_SOFT_RESET cpu_to_le32(1<<16)
/* MSIX context */
struct aac_msix_ctx {
@@ -1662,6 +1664,7 @@ struct aac_dev
u8 raw_io_64;
u8 printf_enabled;
u8 in_reset;
+ u8 in_soft_reset;
u8 msi;
u8 sa_firmware;
int management_fib_count;
@@ -2504,6 +2507,7 @@ struct aac_hba_info {
#define RCV_TEMP_READINGS 0x00000025
#define GET_COMM_PREFERRED_SETTINGS 0x00000026
#define IOP_RESET_FW_FIB_DUMP 0x00000034
+#define DROP_IO 0x00000035
#define IOP_RESET 0x00001000
#define IOP_RESET_ALWAYS 0x00001001
#define RE_INIT_ADAPTER 0x000000ee
@@ -2539,6 +2543,7 @@ struct aac_hba_info {
#define FLASH_UPD_PENDING 0x00002000
#define FLASH_UPD_SUCCESS 0x00004000
#define FLASH_UPD_FAILED 0x00008000
+#define INVALID_OMR 0xffffffff
#define FWUPD_TIMEOUT (5 * 60)
/*
diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c
index 84858d5c8257..0156c9623c35 100644
--- a/drivers/scsi/aacraid/commsup.c
+++ b/drivers/scsi/aacraid/commsup.c
@@ -1502,9 +1502,10 @@ static int _aac_reset_adapter(struct aac_dev *aac, int forced, u8 reset_type)
host = aac->scsi_host_ptr;
scsi_block_requests(host);
aac_adapter_disable_int(aac);
- if (aac->thread->pid != current->pid) {
+ if (aac->thread && aac->thread->pid != current->pid) {
spin_unlock_irq(host->host_lock);
kthread_stop(aac->thread);
+ aac->thread = NULL;
jafo = 1;
}
@@ -1591,6 +1592,7 @@ static int _aac_reset_adapter(struct aac_dev *aac, int forced, u8 reset_type)
aac->name);
if (IS_ERR(aac->thread)) {
retval = PTR_ERR(aac->thread);
+ aac->thread = NULL;
goto out;
}
}
diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c
index 2664ea0df35f..f24fb942065d 100644
--- a/drivers/scsi/aacraid/linit.c
+++ b/drivers/scsi/aacraid/linit.c
@@ -1562,6 +1562,7 @@ static void __aac_shutdown(struct aac_dev * aac)
up(&fib->event_wait);
}
kthread_stop(aac->thread);
+ aac->thread = NULL;
}
aac_send_shutdown(aac);
diff --git a/drivers/scsi/aacraid/src.c b/drivers/scsi/aacraid/src.c
index fde6b6aa86e3..4ebb35a29caa 100644
--- a/drivers/scsi/aacraid/src.c
+++ b/drivers/scsi/aacraid/src.c
@@ -255,7 +255,8 @@ static int src_sync_cmd(struct aac_dev *dev, u32 command,
*/
src_writel(dev, MUnit.IDR, INBOUNDDOORBELL_0 << SRC_IDR_SHIFT);
- if (!dev->sync_mode || command != SEND_SYNCHRONOUS_FIB) {
+ if ((!dev->sync_mode || command != SEND_SYNCHRONOUS_FIB) &&
+ !dev->in_soft_reset) {
ok = 0;
start = jiffies;
@@ -679,6 +680,25 @@ void aac_set_intx_mode(struct aac_dev *dev)
}
}
+static void aac_clear_omr(struct aac_dev *dev)
+{
+ u32 omr_value = 0;
+
+ omr_value = src_readl(dev, MUnit.OMR);
+
+ /*
+ * Check for PCI Errors or Kernel Panic
+ */
+ if ((omr_value == INVALID_OMR) || (omr_value & KERNEL_PANIC))
+ omr_value = 0;
+
+ /*
+ * Preserve MSIX Value if any
+ */
+ src_writel(dev, MUnit.OMR, omr_value & AAC_INT_MODE_MSIX);
+ src_readl(dev, MUnit.OMR);
+}
+
static void aac_dump_fw_fib_iop_reset(struct aac_dev *dev)
{
__le32 supported_options3;
@@ -739,6 +759,8 @@ static void aac_send_iop_reset(struct aac_dev *dev)
aac_set_intx_mode(dev);
+ aac_clear_omr(dev);
+
src_writel(dev, MUnit.IDR, IOP_SRC_RESET_MASK);
msleep(5000);
@@ -748,6 +770,7 @@ static void aac_send_hardware_soft_reset(struct aac_dev *dev)
{
u_int32_t val;
+ aac_clear_omr(dev);
val = readl(((char *)(dev->base) + IBW_SWR_OFFSET));
val |= 0x01;
writel(val, ((char *)(dev->base) + IBW_SWR_OFFSET));
@@ -786,7 +809,7 @@ static int aac_src_restart_adapter(struct aac_dev *dev, int bled, u8 reset_type)
if (!is_ctrl_up)
dev_err(&dev->pdev->dev, "IOP reset failed\n");
else {
- dev_info(&dev->pdev->dev, "IOP reset succeded\n");
+ dev_info(&dev->pdev->dev, "IOP reset succeeded\n");
goto set_startup;
}
}
@@ -808,7 +831,7 @@ static int aac_src_restart_adapter(struct aac_dev *dev, int bled, u8 reset_type)
ret = -ENODEV;
goto out;
} else
- dev_info(&dev->pdev->dev, "SOFT reset succeded\n");
+ dev_info(&dev->pdev->dev, "SOFT reset succeeded\n");
}
set_startup:
@@ -992,6 +1015,148 @@ error_iounmap:
return -1;
}
+static int aac_src_wait_sync(struct aac_dev *dev, int *status)
+{
+ unsigned long start = jiffies;
+ unsigned long usecs = 0;
+ int delay = 5 * HZ;
+ int rc = 1;
+
+ while (time_before(jiffies, start+delay)) {
+ /*
+ * Delay 5 microseconds to let Mon960 get info.
+ */
+ udelay(5);
+
+ /*
+ * Mon960 will set doorbell0 bit when it has completed the
+ * command.
+ */
+ if (aac_src_get_sync_status(dev) & OUTBOUNDDOORBELL_0) {
+ /*
+ * Clear: the doorbell.
+ */
+ if (dev->msi_enabled)
+ aac_src_access_devreg(dev, AAC_CLEAR_SYNC_BIT);
+ else
+ src_writel(dev, MUnit.ODR_C,
+ OUTBOUNDDOORBELL_0 << SRC_ODR_SHIFT);
+ rc = 0;
+
+ break;
+ }
+
+ /*
+ * Yield the processor in case we are slow
+ */
+ usecs = 1 * USEC_PER_MSEC;
+ usleep_range(usecs, usecs + 50);
+ }
+ /*
+ * Pull the synch status from Mailbox 0.
+ */
+ if (status && !rc) {
+ status[0] = readl(&dev->IndexRegs->Mailbox[0]);
+ status[1] = readl(&dev->IndexRegs->Mailbox[1]);
+ status[2] = readl(&dev->IndexRegs->Mailbox[2]);
+ status[3] = readl(&dev->IndexRegs->Mailbox[3]);
+ status[4] = readl(&dev->IndexRegs->Mailbox[4]);
+ }
+
+ return rc;
+}
+
+/**
+ * aac_src_soft_reset - perform soft reset to speed up
+ * access
+ *
+ * Assumptions: That the controller is in a state where we can
+ * bring it back to life with an init struct. We can only use
+ * fast sync commands, as the timeout is 5 seconds.
+ *
+ * @dev: device to configure
+ *
+ */
+
+static int aac_src_soft_reset(struct aac_dev *dev)
+{
+ u32 status_omr = src_readl(dev, MUnit.OMR);
+ u32 status[5];
+ int rc = 1;
+ int state = 0;
+ char *state_str[7] = {
+ "GET_ADAPTER_PROPERTIES Failed",
+ "GET_ADAPTER_PROPERTIES timeout",
+ "SOFT_RESET not supported",
+ "DROP_IO Failed",
+ "DROP_IO timeout",
+ "Check Health failed"
+ };
+
+ if (status_omr == INVALID_OMR)
+ return 1; // pcie hosed
+
+ if (!(status_omr & KERNEL_UP_AND_RUNNING))
+ return 1; // not up and running
+
+ /*
+ * We go into soft reset mode to allow us to handle response
+ */
+ dev->in_soft_reset = 1;
+ dev->msi_enabled = status_omr & AAC_INT_MODE_MSIX;
+
+ /* Get adapter properties */
+ rc = aac_adapter_sync_cmd(dev, GET_ADAPTER_PROPERTIES, 0, 0, 0,
+ 0, 0, 0, status+0, status+1, status+2, status+3, status+4);
+ if (rc)
+ goto out;
+
+ state++;
+ if (aac_src_wait_sync(dev, status)) {
+ rc = 1;
+ goto out;
+ }
+
+ state++;
+ if (!(status[1] & le32_to_cpu(AAC_OPT_EXTENDED) &&
+ (status[4] & le32_to_cpu(AAC_EXTOPT_SOFT_RESET)))) {
+ rc = 2;
+ goto out;
+ }
+
+ if ((status[1] & le32_to_cpu(AAC_OPT_EXTENDED)) &&
+ (status[4] & le32_to_cpu(AAC_EXTOPT_SA_FIRMWARE)))
+ dev->sa_firmware = 1;
+
+ state++;
+ rc = aac_adapter_sync_cmd(dev, DROP_IO, 0, 0, 0, 0, 0, 0,
+ status+0, status+1, status+2, status+3, status+4);
+
+ if (rc)
+ goto out;
+
+ state++;
+ if (aac_src_wait_sync(dev, status)) {
+ rc = 3;
+ goto out;
+ }
+
+ if (status[1])
+ dev_err(&dev->pdev->dev, "%s: %d outstanding I/O pending\n",
+ __func__, status[1]);
+
+ state++;
+ rc = aac_src_check_health(dev);
+
+out:
+ dev->in_soft_reset = 0;
+ dev->msi_enabled = 0;
+ if (rc)
+ dev_err(&dev->pdev->dev, "%s: %s status = %d", __func__,
+ state_str[state], rc);
+
+return rc;
+}
/**
* aac_srcv_init - initialize an SRCv card
* @dev: device to configure
@@ -1021,8 +1186,10 @@ int aac_srcv_init(struct aac_dev *dev)
if (dev->init_reset) {
dev->init_reset = false;
- if (!aac_src_restart_adapter(dev, 0, IOP_HWSOFT_RESET))
+ if (aac_src_soft_reset(dev)) {
+ aac_src_restart_adapter(dev, 0, IOP_HWSOFT_RESET);
++restart;
+ }
}
/*
@@ -1072,13 +1239,16 @@ int aac_srcv_init(struct aac_dev *dev)
printk(KERN_ERR "%s%d: adapter monitor panic.\n", dev->name, instance);
goto error_iounmap;
}
+
start = jiffies;
/*
* Wait for the adapter to be up and running. Wait up to 3 minutes
*/
- while (!((status = src_readl(dev, MUnit.OMR)) &
- KERNEL_UP_AND_RUNNING) ||
- status == 0xffffffff) {
+ do {
+ status = src_readl(dev, MUnit.OMR);
+ if (status == INVALID_OMR)
+ status = 0;
+
if ((restart &&
(status & (KERNEL_PANIC|SELF_TEST_FAILED|MONITOR_PANIC))) ||
time_after(jiffies, start+HZ*startup_timeout)) {
@@ -1098,7 +1268,8 @@ int aac_srcv_init(struct aac_dev *dev)
++restart;
}
msleep(1);
- }
+ } while (!(status & KERNEL_UP_AND_RUNNING));
+
if (restart && aac_commit)
aac_commit = 1;
/*
@@ -1234,13 +1405,23 @@ void aac_src_access_devreg(struct aac_dev *dev, int mode)
static int aac_src_get_sync_status(struct aac_dev *dev)
{
+ int msix_val = 0;
+ int legacy_val = 0;
- int val;
+ msix_val = src_readl(dev, MUnit.ODR_MSI) & SRC_MSI_READ_MASK ? 1 : 0;
- if (dev->msi_enabled)
- val = src_readl(dev, MUnit.ODR_MSI) & 0x1000 ? 1 : 0;
- else
- val = src_readl(dev, MUnit.ODR_R) >> SRC_ODR_SHIFT;
+ if (!dev->msi_enabled) {
+ /*
+ * if Legacy int status indicates cmd is not complete
+ * sample MSIx register to see if it indiactes cmd complete,
+ * if yes set the controller in MSIx mode and consider cmd
+ * completed
+ */
+ legacy_val = src_readl(dev, MUnit.ODR_R) >> SRC_ODR_SHIFT;
+ if (!(legacy_val & 1) && msix_val)
+ dev->msi_enabled = 1;
+ return legacy_val;
+ }
- return val;
+ return msix_val;
}
diff --git a/drivers/scsi/aha1740.c b/drivers/scsi/aha1740.c
index bad35ffc015d..b48d5436f094 100644
--- a/drivers/scsi/aha1740.c
+++ b/drivers/scsi/aha1740.c
@@ -592,7 +592,7 @@ static int aha1740_probe (struct device *dev)
DMA_BIDIRECTIONAL);
if (!host->ecb_dma_addr) {
printk (KERN_ERR "aha1740_probe: Couldn't map ECB, giving up\n");
- scsi_unregister (shpnt);
+ scsi_host_put (shpnt);
goto err_host_put;
}
diff --git a/drivers/scsi/aic7xxx/aic79xx_core.c b/drivers/scsi/aic7xxx/aic79xx_core.c
index b560f396ee99..034f4eebb160 100644
--- a/drivers/scsi/aic7xxx/aic79xx_core.c
+++ b/drivers/scsi/aic7xxx/aic79xx_core.c
@@ -9338,9 +9338,9 @@ ahd_dumpseq(struct ahd_softc* ahd)
static void
ahd_loadseq(struct ahd_softc *ahd)
{
- struct cs cs_table[num_critical_sections];
- u_int begin_set[num_critical_sections];
- u_int end_set[num_critical_sections];
+ struct cs cs_table[NUM_CRITICAL_SECTIONS];
+ u_int begin_set[NUM_CRITICAL_SECTIONS];
+ u_int end_set[NUM_CRITICAL_SECTIONS];
const struct patch *cur_patch;
u_int cs_count;
u_int cur_cs;
@@ -9456,7 +9456,7 @@ ahd_loadseq(struct ahd_softc *ahd)
* Move through the CS table until we find a CS
* that might apply to this instruction.
*/
- for (; cur_cs < num_critical_sections; cur_cs++) {
+ for (; cur_cs < NUM_CRITICAL_SECTIONS; cur_cs++) {
if (critical_sections[cur_cs].end <= i) {
if (begin_set[cs_count] == TRUE
&& end_set[cs_count] == FALSE) {
diff --git a/drivers/scsi/aic7xxx/aic79xx_seq.h_shipped b/drivers/scsi/aic7xxx/aic79xx_seq.h_shipped
index 4b51e232392f..fd64a950ee44 100644
--- a/drivers/scsi/aic7xxx/aic79xx_seq.h_shipped
+++ b/drivers/scsi/aic7xxx/aic79xx_seq.h_shipped
@@ -1186,5 +1186,4 @@ static const struct cs {
{ 759, 763 }
};
-static const int num_critical_sections = sizeof(critical_sections)
- / sizeof(*critical_sections);
+#define NUM_CRITICAL_SECTIONS ARRAY_SIZE(critical_sections)
diff --git a/drivers/scsi/aic7xxx/aic7xxx_core.c b/drivers/scsi/aic7xxx/aic7xxx_core.c
index 6612ff3b2e83..e97eceacf522 100644
--- a/drivers/scsi/aic7xxx/aic7xxx_core.c
+++ b/drivers/scsi/aic7xxx/aic7xxx_core.c
@@ -6848,9 +6848,9 @@ ahc_dumpseq(struct ahc_softc* ahc)
static int
ahc_loadseq(struct ahc_softc *ahc)
{
- struct cs cs_table[num_critical_sections];
- u_int begin_set[num_critical_sections];
- u_int end_set[num_critical_sections];
+ struct cs cs_table[NUM_CRITICAL_SECTIONS];
+ u_int begin_set[NUM_CRITICAL_SECTIONS];
+ u_int end_set[NUM_CRITICAL_SECTIONS];
const struct patch *cur_patch;
u_int cs_count;
u_int cur_cs;
@@ -6915,7 +6915,7 @@ ahc_loadseq(struct ahc_softc *ahc)
* Move through the CS table until we find a CS
* that might apply to this instruction.
*/
- for (; cur_cs < num_critical_sections; cur_cs++) {
+ for (; cur_cs < NUM_CRITICAL_SECTIONS; cur_cs++) {
if (critical_sections[cur_cs].end <= i) {
if (begin_set[cs_count] == TRUE
&& end_set[cs_count] == FALSE) {
diff --git a/drivers/scsi/aic7xxx/aic7xxx_seq.h_shipped b/drivers/scsi/aic7xxx/aic7xxx_seq.h_shipped
index 07e93fbae706..f37362bc8ece 100644
--- a/drivers/scsi/aic7xxx/aic7xxx_seq.h_shipped
+++ b/drivers/scsi/aic7xxx/aic7xxx_seq.h_shipped
@@ -1304,5 +1304,4 @@ static const struct cs {
{ 875, 877 }
};
-static const int num_critical_sections = sizeof(critical_sections)
- / sizeof(*critical_sections);
+#define NUM_CRITICAL_SECTIONS ARRAY_SIZE(critical_sections)
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm.c b/drivers/scsi/aic7xxx/aicasm/aicasm.c
index 21ac265280bf..5f474e490f3e 100644
--- a/drivers/scsi/aic7xxx/aicasm/aicasm.c
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm.c
@@ -451,8 +451,7 @@ output_code()
fprintf(ofile, "\n};\n\n");
fprintf(ofile,
-"static const int num_critical_sections = sizeof(critical_sections)\n"
-" / sizeof(*critical_sections);\n");
+ "#define NUM_CRITICAL_SECTIONS ARRAY_SIZE(critical_sections)\n");
fprintf(stderr, "%s: %d instructions used\n", appname, instrcount);
}
diff --git a/drivers/scsi/arcmsr/arcmsr.h b/drivers/scsi/arcmsr/arcmsr.h
index f375f3557c18..2e51ccc510e8 100644
--- a/drivers/scsi/arcmsr/arcmsr.h
+++ b/drivers/scsi/arcmsr/arcmsr.h
@@ -49,7 +49,7 @@ struct device_attribute;
#define ARCMSR_MAX_OUTSTANDING_CMD 1024
#define ARCMSR_DEFAULT_OUTSTANDING_CMD 128
#define ARCMSR_MIN_OUTSTANDING_CMD 32
-#define ARCMSR_DRIVER_VERSION "v1.40.00.04-20171130"
+#define ARCMSR_DRIVER_VERSION "v1.40.00.05-20180309"
#define ARCMSR_SCSI_INITIATOR_ID 255
#define ARCMSR_MAX_XFER_SECTORS 512
#define ARCMSR_MAX_XFER_SECTORS_B 4096
@@ -779,12 +779,12 @@ struct AdapterControlBlock
/* message clear rqbuffer */
#define ACB_F_MESSAGE_WQBUFFER_READED 0x0040
#define ACB_F_BUS_RESET 0x0080
-#define ACB_F_BUS_HANG_ON 0x0800/* need hardware reset bus */
#define ACB_F_IOP_INITED 0x0100
/* iop init */
#define ACB_F_ABORT 0x0200
#define ACB_F_FIRMWARE_TRAP 0x0400
+#define ACB_F_ADAPTER_REMOVED 0x0800
#define ACB_F_MSG_GET_CONFIG 0x1000
struct CommandControlBlock * pccb_pool[ARCMSR_MAX_FREECCB_NUM];
/* used for memory free */
diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c
index 75e828bd30e3..732b5d9242f1 100644
--- a/drivers/scsi/arcmsr/arcmsr_hba.c
+++ b/drivers/scsi/arcmsr/arcmsr_hba.c
@@ -1446,12 +1446,80 @@ static void arcmsr_done4abort_postqueue(struct AdapterControlBlock *acb)
}
}
+static void arcmsr_remove_scsi_devices(struct AdapterControlBlock *acb)
+{
+ char *acb_dev_map = (char *)acb->device_map;
+ int target, lun, i;
+ struct scsi_device *psdev;
+ struct CommandControlBlock *ccb;
+ char temp;
+
+ for (i = 0; i < acb->maxFreeCCB; i++) {
+ ccb = acb->pccb_pool[i];
+ if (ccb->startdone == ARCMSR_CCB_START) {
+ ccb->pcmd->result = DID_NO_CONNECT << 16;
+ arcmsr_pci_unmap_dma(ccb);
+ ccb->pcmd->scsi_done(ccb->pcmd);
+ }
+ }
+ for (target = 0; target < ARCMSR_MAX_TARGETID; target++) {
+ temp = *acb_dev_map;
+ if (temp) {
+ for (lun = 0; lun < ARCMSR_MAX_TARGETLUN; lun++) {
+ if (temp & 1) {
+ psdev = scsi_device_lookup(acb->host,
+ 0, target, lun);
+ if (psdev != NULL) {
+ scsi_remove_device(psdev);
+ scsi_device_put(psdev);
+ }
+ }
+ temp >>= 1;
+ }
+ *acb_dev_map = 0;
+ }
+ acb_dev_map++;
+ }
+}
+
+static void arcmsr_free_pcidev(struct AdapterControlBlock *acb)
+{
+ struct pci_dev *pdev;
+ struct Scsi_Host *host;
+
+ host = acb->host;
+ arcmsr_free_sysfs_attr(acb);
+ scsi_remove_host(host);
+ flush_work(&acb->arcmsr_do_message_isr_bh);
+ del_timer_sync(&acb->eternal_timer);
+ if (set_date_time)
+ del_timer_sync(&acb->refresh_timer);
+ pdev = acb->pdev;
+ arcmsr_free_irq(pdev, acb);
+ arcmsr_free_ccb_pool(acb);
+ arcmsr_free_mu(acb);
+ arcmsr_unmap_pciregion(acb);
+ pci_release_regions(pdev);
+ scsi_host_put(host);
+ pci_disable_device(pdev);
+}
+
static void arcmsr_remove(struct pci_dev *pdev)
{
struct Scsi_Host *host = pci_get_drvdata(pdev);
struct AdapterControlBlock *acb =
(struct AdapterControlBlock *) host->hostdata;
int poll_count = 0;
+ uint16_t dev_id;
+
+ pci_read_config_word(pdev, PCI_DEVICE_ID, &dev_id);
+ if (dev_id == 0xffff) {
+ acb->acb_flags &= ~ACB_F_IOP_INITED;
+ acb->acb_flags |= ACB_F_ADAPTER_REMOVED;
+ arcmsr_remove_scsi_devices(acb);
+ arcmsr_free_pcidev(acb);
+ return;
+ }
arcmsr_free_sysfs_attr(acb);
scsi_remove_host(host);
flush_work(&acb->arcmsr_do_message_isr_bh);
@@ -1499,6 +1567,8 @@ static void arcmsr_shutdown(struct pci_dev *pdev)
struct Scsi_Host *host = pci_get_drvdata(pdev);
struct AdapterControlBlock *acb =
(struct AdapterControlBlock *)host->hostdata;
+ if (acb->acb_flags & ACB_F_ADAPTER_REMOVED)
+ return;
del_timer_sync(&acb->eternal_timer);
if (set_date_time)
del_timer_sync(&acb->refresh_timer);
@@ -2931,6 +3001,12 @@ static int arcmsr_queue_command_lck(struct scsi_cmnd *cmd,
struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata;
struct CommandControlBlock *ccb;
int target = cmd->device->id;
+
+ if (acb->acb_flags & ACB_F_ADAPTER_REMOVED) {
+ cmd->result = (DID_NO_CONNECT << 16);
+ cmd->scsi_done(cmd);
+ return 0;
+ }
cmd->scsi_done = done;
cmd->host_scribble = NULL;
cmd->result = 0;
@@ -3731,6 +3807,8 @@ static void arcmsr_wait_firmware_ready(struct AdapterControlBlock *acb)
case ACB_ADAPTER_TYPE_A: {
struct MessageUnit_A __iomem *reg = acb->pmuA;
do {
+ if (!(acb->acb_flags & ACB_F_IOP_INITED))
+ msleep(20);
firmware_state = readl(&reg->outbound_msgaddr1);
} while ((firmware_state & ARCMSR_OUTBOUND_MESG1_FIRMWARE_OK) == 0);
}
@@ -3739,6 +3817,8 @@ static void arcmsr_wait_firmware_ready(struct AdapterControlBlock *acb)
case ACB_ADAPTER_TYPE_B: {
struct MessageUnit_B *reg = acb->pmuB;
do {
+ if (!(acb->acb_flags & ACB_F_IOP_INITED))
+ msleep(20);
firmware_state = readl(reg->iop2drv_doorbell);
} while ((firmware_state & ARCMSR_MESSAGE_FIRMWARE_OK) == 0);
writel(ARCMSR_DRV2IOP_END_OF_INTERRUPT, reg->drv2iop_doorbell);
@@ -3747,6 +3827,8 @@ static void arcmsr_wait_firmware_ready(struct AdapterControlBlock *acb)
case ACB_ADAPTER_TYPE_C: {
struct MessageUnit_C __iomem *reg = acb->pmuC;
do {
+ if (!(acb->acb_flags & ACB_F_IOP_INITED))
+ msleep(20);
firmware_state = readl(&reg->outbound_msgaddr1);
} while ((firmware_state & ARCMSR_HBCMU_MESSAGE_FIRMWARE_OK) == 0);
}
@@ -3754,6 +3836,8 @@ static void arcmsr_wait_firmware_ready(struct AdapterControlBlock *acb)
case ACB_ADAPTER_TYPE_D: {
struct MessageUnit_D *reg = acb->pmuD;
do {
+ if (!(acb->acb_flags & ACB_F_IOP_INITED))
+ msleep(20);
firmware_state = readl(reg->outbound_msgaddr1);
} while ((firmware_state &
ARCMSR_ARC1214_MESSAGE_FIRMWARE_OK) == 0);
@@ -3762,6 +3846,8 @@ static void arcmsr_wait_firmware_ready(struct AdapterControlBlock *acb)
case ACB_ADAPTER_TYPE_E: {
struct MessageUnit_E __iomem *reg = acb->pmuE;
do {
+ if (!(acb->acb_flags & ACB_F_IOP_INITED))
+ msleep(20);
firmware_state = readl(&reg->outbound_msgaddr1);
} while ((firmware_state & ARCMSR_HBEMU_MESSAGE_FIRMWARE_OK) == 0);
}
@@ -4177,6 +4263,8 @@ static int arcmsr_bus_reset(struct scsi_cmnd *cmd)
int retry_count = 0;
int rtn = FAILED;
acb = (struct AdapterControlBlock *) cmd->device->host->hostdata;
+ if (acb->acb_flags & ACB_F_ADAPTER_REMOVED)
+ return SUCCESS;
pr_notice("arcmsr: executing bus reset eh.....num_resets = %d,"
" num_aborts = %d \n", acb->num_resets, acb->num_aborts);
acb->num_resets++;
@@ -4243,6 +4331,8 @@ static int arcmsr_abort(struct scsi_cmnd *cmd)
int rtn = FAILED;
uint32_t intmask_org;
+ if (acb->acb_flags & ACB_F_ADAPTER_REMOVED)
+ return SUCCESS;
printk(KERN_NOTICE
"arcmsr%d: abort device command of scsi id = %d lun = %d\n",
acb->host->host_no, cmd->device->id, (u32)cmd->device->lun);
diff --git a/drivers/scsi/atp870u.c b/drivers/scsi/atp870u.c
index 8b52a9dbb9cf..b46997cf77e2 100644
--- a/drivers/scsi/atp870u.c
+++ b/drivers/scsi/atp870u.c
@@ -1413,11 +1413,11 @@ static void atp885_init(struct Scsi_Host *shpnt)
atpdev->global_map[m] = 0;
for (k = 0; k < 4; k++) {
atp_writew_base(atpdev, 0x3c, n++);
- ((unsigned long *)&setupdata[m][0])[k] = atp_readl_base(atpdev, 0x38);
+ ((u32 *)&setupdata[m][0])[k] = atp_readl_base(atpdev, 0x38);
}
for (k = 0; k < 4; k++) {
atp_writew_base(atpdev, 0x3c, n++);
- ((unsigned long *)&atpdev->sp[m][0])[k] = atp_readl_base(atpdev, 0x38);
+ ((u32 *)&atpdev->sp[m][0])[k] = atp_readl_base(atpdev, 0x38);
}
n += 8;
}
diff --git a/drivers/scsi/bfa/bfad_bsg.c b/drivers/scsi/bfa/bfad_bsg.c
index 3976e787ba64..7c884f881180 100644
--- a/drivers/scsi/bfa/bfad_bsg.c
+++ b/drivers/scsi/bfa/bfad_bsg.c
@@ -891,7 +891,7 @@ bfad_iocmd_fabric_get_lports(struct bfad_s *bfad, void *cmd,
if (bfad_chk_iocmd_sz(payload_len,
sizeof(struct bfa_bsg_fabric_get_lports_s),
- sizeof(wwn_t[iocmd->nports])) != BFA_STATUS_OK) {
+ sizeof(wwn_t) * iocmd->nports) != BFA_STATUS_OK) {
iocmd->status = BFA_STATUS_VERSION_FAIL;
goto out;
}
diff --git a/drivers/scsi/csiostor/csio_attr.c b/drivers/scsi/csiostor/csio_attr.c
index 2d1c4ebd40f9..8a004036e3d7 100644
--- a/drivers/scsi/csiostor/csio_attr.c
+++ b/drivers/scsi/csiostor/csio_attr.c
@@ -274,12 +274,24 @@ csio_get_host_speed(struct Scsi_Host *shost)
spin_lock_irq(&hw->lock);
switch (hw->pport[ln->portid].link_speed) {
- case FW_PORT_CAP_SPEED_1G:
+ case FW_PORT_CAP32_SPEED_1G:
fc_host_speed(shost) = FC_PORTSPEED_1GBIT;
break;
- case FW_PORT_CAP_SPEED_10G:
+ case FW_PORT_CAP32_SPEED_10G:
fc_host_speed(shost) = FC_PORTSPEED_10GBIT;
break;
+ case FW_PORT_CAP32_SPEED_25G:
+ fc_host_speed(shost) = FC_PORTSPEED_25GBIT;
+ break;
+ case FW_PORT_CAP32_SPEED_40G:
+ fc_host_speed(shost) = FC_PORTSPEED_40GBIT;
+ break;
+ case FW_PORT_CAP32_SPEED_50G:
+ fc_host_speed(shost) = FC_PORTSPEED_50GBIT;
+ break;
+ case FW_PORT_CAP32_SPEED_100G:
+ fc_host_speed(shost) = FC_PORTSPEED_100GBIT;
+ break;
default:
fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN;
break;
diff --git a/drivers/scsi/csiostor/csio_hw.c b/drivers/scsi/csiostor/csio_hw.c
index 0bd1131b6cc9..96bbb82c826d 100644
--- a/drivers/scsi/csiostor/csio_hw.c
+++ b/drivers/scsi/csiostor/csio_hw.c
@@ -1409,6 +1409,235 @@ out:
return rv;
}
+static inline enum cc_fec fwcap_to_cc_fec(fw_port_cap32_t fw_fec)
+{
+ enum cc_fec cc_fec = 0;
+
+ if (fw_fec & FW_PORT_CAP32_FEC_RS)
+ cc_fec |= FEC_RS;
+ if (fw_fec & FW_PORT_CAP32_FEC_BASER_RS)
+ cc_fec |= FEC_BASER_RS;
+
+ return cc_fec;
+}
+
+static inline fw_port_cap32_t cc_to_fwcap_pause(enum cc_pause cc_pause)
+{
+ fw_port_cap32_t fw_pause = 0;
+
+ if (cc_pause & PAUSE_RX)
+ fw_pause |= FW_PORT_CAP32_FC_RX;
+ if (cc_pause & PAUSE_TX)
+ fw_pause |= FW_PORT_CAP32_FC_TX;
+
+ return fw_pause;
+}
+
+static inline fw_port_cap32_t cc_to_fwcap_fec(enum cc_fec cc_fec)
+{
+ fw_port_cap32_t fw_fec = 0;
+
+ if (cc_fec & FEC_RS)
+ fw_fec |= FW_PORT_CAP32_FEC_RS;
+ if (cc_fec & FEC_BASER_RS)
+ fw_fec |= FW_PORT_CAP32_FEC_BASER_RS;
+
+ return fw_fec;
+}
+
+/**
+ * fwcap_to_fwspeed - return highest speed in Port Capabilities
+ * @acaps: advertised Port Capabilities
+ *
+ * Get the highest speed for the port from the advertised Port
+ * Capabilities.
+ */
+fw_port_cap32_t fwcap_to_fwspeed(fw_port_cap32_t acaps)
+{
+ #define TEST_SPEED_RETURN(__caps_speed) \
+ do { \
+ if (acaps & FW_PORT_CAP32_SPEED_##__caps_speed) \
+ return FW_PORT_CAP32_SPEED_##__caps_speed; \
+ } while (0)
+
+ TEST_SPEED_RETURN(400G);
+ TEST_SPEED_RETURN(200G);
+ TEST_SPEED_RETURN(100G);
+ TEST_SPEED_RETURN(50G);
+ TEST_SPEED_RETURN(40G);
+ TEST_SPEED_RETURN(25G);
+ TEST_SPEED_RETURN(10G);
+ TEST_SPEED_RETURN(1G);
+ TEST_SPEED_RETURN(100M);
+
+ #undef TEST_SPEED_RETURN
+
+ return 0;
+}
+
+/**
+ * fwcaps16_to_caps32 - convert 16-bit Port Capabilities to 32-bits
+ * @caps16: a 16-bit Port Capabilities value
+ *
+ * Returns the equivalent 32-bit Port Capabilities value.
+ */
+fw_port_cap32_t fwcaps16_to_caps32(fw_port_cap16_t caps16)
+{
+ fw_port_cap32_t caps32 = 0;
+
+ #define CAP16_TO_CAP32(__cap) \
+ do { \
+ if (caps16 & FW_PORT_CAP_##__cap) \
+ caps32 |= FW_PORT_CAP32_##__cap; \
+ } while (0)
+
+ CAP16_TO_CAP32(SPEED_100M);
+ CAP16_TO_CAP32(SPEED_1G);
+ CAP16_TO_CAP32(SPEED_25G);
+ CAP16_TO_CAP32(SPEED_10G);
+ CAP16_TO_CAP32(SPEED_40G);
+ CAP16_TO_CAP32(SPEED_100G);
+ CAP16_TO_CAP32(FC_RX);
+ CAP16_TO_CAP32(FC_TX);
+ CAP16_TO_CAP32(ANEG);
+ CAP16_TO_CAP32(MDIX);
+ CAP16_TO_CAP32(MDIAUTO);
+ CAP16_TO_CAP32(FEC_RS);
+ CAP16_TO_CAP32(FEC_BASER_RS);
+ CAP16_TO_CAP32(802_3_PAUSE);
+ CAP16_TO_CAP32(802_3_ASM_DIR);
+
+ #undef CAP16_TO_CAP32
+
+ return caps32;
+}
+
+/**
+ * lstatus_to_fwcap - translate old lstatus to 32-bit Port Capabilities
+ * @lstatus: old FW_PORT_ACTION_GET_PORT_INFO lstatus value
+ *
+ * Translates old FW_PORT_ACTION_GET_PORT_INFO lstatus field into new
+ * 32-bit Port Capabilities value.
+ */
+fw_port_cap32_t lstatus_to_fwcap(u32 lstatus)
+{
+ fw_port_cap32_t linkattr = 0;
+
+ /* The format of the Link Status in the old
+ * 16-bit Port Information message isn't the same as the
+ * 16-bit Port Capabilities bitfield used everywhere else.
+ */
+ if (lstatus & FW_PORT_CMD_RXPAUSE_F)
+ linkattr |= FW_PORT_CAP32_FC_RX;
+ if (lstatus & FW_PORT_CMD_TXPAUSE_F)
+ linkattr |= FW_PORT_CAP32_FC_TX;
+ if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100M))
+ linkattr |= FW_PORT_CAP32_SPEED_100M;
+ if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_1G))
+ linkattr |= FW_PORT_CAP32_SPEED_1G;
+ if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_10G))
+ linkattr |= FW_PORT_CAP32_SPEED_10G;
+ if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_25G))
+ linkattr |= FW_PORT_CAP32_SPEED_25G;
+ if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_40G))
+ linkattr |= FW_PORT_CAP32_SPEED_40G;
+ if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100G))
+ linkattr |= FW_PORT_CAP32_SPEED_100G;
+
+ return linkattr;
+}
+
+/**
+ * csio_init_link_config - initialize a link's SW state
+ * @lc: pointer to structure holding the link state
+ * @pcaps: link Port Capabilities
+ * @acaps: link current Advertised Port Capabilities
+ *
+ * Initializes the SW state maintained for each link, including the link's
+ * capabilities and default speed/flow-control/autonegotiation settings.
+ */
+static void csio_init_link_config(struct link_config *lc, fw_port_cap32_t pcaps,
+ fw_port_cap32_t acaps)
+{
+ lc->pcaps = pcaps;
+ lc->def_acaps = acaps;
+ lc->lpacaps = 0;
+ lc->speed_caps = 0;
+ lc->speed = 0;
+ lc->requested_fc = PAUSE_RX | PAUSE_TX;
+ lc->fc = lc->requested_fc;
+
+ /*
+ * For Forward Error Control, we default to whatever the Firmware
+ * tells us the Link is currently advertising.
+ */
+ lc->requested_fec = FEC_AUTO;
+ lc->fec = fwcap_to_cc_fec(lc->def_acaps);
+
+ /* If the Port is capable of Auto-Negtotiation, initialize it as
+ * "enabled" and copy over all of the Physical Port Capabilities
+ * to the Advertised Port Capabilities. Otherwise mark it as
+ * Auto-Negotiate disabled and select the highest supported speed
+ * for the link. Note parallel structure in t4_link_l1cfg_core()
+ * and t4_handle_get_port_info().
+ */
+ if (lc->pcaps & FW_PORT_CAP32_ANEG) {
+ lc->acaps = lc->pcaps & ADVERT_MASK;
+ lc->autoneg = AUTONEG_ENABLE;
+ lc->requested_fc |= PAUSE_AUTONEG;
+ } else {
+ lc->acaps = 0;
+ lc->autoneg = AUTONEG_DISABLE;
+ }
+}
+
+static void csio_link_l1cfg(struct link_config *lc, uint16_t fw_caps,
+ uint32_t *rcaps)
+{
+ unsigned int fw_mdi = FW_PORT_CAP32_MDI_V(FW_PORT_CAP32_MDI_AUTO);
+ fw_port_cap32_t fw_fc, cc_fec, fw_fec, lrcap;
+
+ lc->link_ok = 0;
+
+ /*
+ * Convert driver coding of Pause Frame Flow Control settings into the
+ * Firmware's API.
+ */
+ fw_fc = cc_to_fwcap_pause(lc->requested_fc);
+
+ /*
+ * Convert Common Code Forward Error Control settings into the
+ * Firmware's API. If the current Requested FEC has "Automatic"
+ * (IEEE 802.3) specified, then we use whatever the Firmware
+ * sent us as part of it's IEEE 802.3-based interpratation of
+ * the Transceiver Module EPROM FEC parameters. Otherwise we
+ * use whatever is in the current Requested FEC settings.
+ */
+ if (lc->requested_fec & FEC_AUTO)
+ cc_fec = fwcap_to_cc_fec(lc->def_acaps);
+ else
+ cc_fec = lc->requested_fec;
+ fw_fec = cc_to_fwcap_fec(cc_fec);
+
+ /* Figure out what our Requested Port Capabilities are going to be.
+ * Note parallel structure in t4_handle_get_port_info() and
+ * init_link_config().
+ */
+ if (!(lc->pcaps & FW_PORT_CAP32_ANEG)) {
+ lrcap = (lc->pcaps & ADVERT_MASK) | fw_fc | fw_fec;
+ lc->fc = lc->requested_fc & ~PAUSE_AUTONEG;
+ lc->fec = cc_fec;
+ } else if (lc->autoneg == AUTONEG_DISABLE) {
+ lrcap = lc->speed_caps | fw_fc | fw_fec | fw_mdi;
+ lc->fc = lc->requested_fc & ~PAUSE_AUTONEG;
+ lc->fec = cc_fec;
+ } else {
+ lrcap = lc->acaps | fw_fc | fw_fec | fw_mdi;
+ }
+
+ *rcaps = lrcap;
+}
+
/*
* csio_enable_ports - Bring up all available ports.
* @hw: HW module.
@@ -1418,8 +1647,10 @@ static int
csio_enable_ports(struct csio_hw *hw)
{
struct csio_mb *mbp;
+ u16 fw_caps = FW_CAPS_UNKNOWN;
enum fw_retval retval;
uint8_t portid;
+ fw_port_cap32_t pcaps, acaps, rcaps;
int i;
mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
@@ -1431,9 +1662,39 @@ csio_enable_ports(struct csio_hw *hw)
for (i = 0; i < hw->num_pports; i++) {
portid = hw->pport[i].portid;
+ if (fw_caps == FW_CAPS_UNKNOWN) {
+ u32 param, val;
+
+ param = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_PFVF) |
+ FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_PFVF_PORT_CAPS32));
+ val = 1;
+
+ csio_mb_params(hw, mbp, CSIO_MB_DEFAULT_TMO,
+ hw->pfn, 0, 1, &param, &val, false,
+ NULL);
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "failed to issue FW_PARAMS_CMD(r) port:%d\n",
+ portid);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ csio_mb_process_read_params_rsp(hw, mbp, &retval, 1,
+ &val);
+ if (retval != FW_SUCCESS) {
+ csio_err(hw, "FW_PARAMS_CMD(r) port:%d failed: 0x%x\n",
+ portid, retval);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ fw_caps = val;
+ }
+
/* Read PORT information */
csio_mb_port(hw, mbp, CSIO_MB_DEFAULT_TMO, portid,
- false, 0, 0, NULL);
+ false, 0, fw_caps, NULL);
if (csio_mb_issue(hw, mbp)) {
csio_err(hw, "failed to issue FW_PORT_CMD(r) port:%d\n",
@@ -1442,8 +1703,8 @@ csio_enable_ports(struct csio_hw *hw)
return -EINVAL;
}
- csio_mb_process_read_port_rsp(hw, mbp, &retval,
- &hw->pport[i].pcap);
+ csio_mb_process_read_port_rsp(hw, mbp, &retval, fw_caps,
+ &pcaps, &acaps);
if (retval != FW_SUCCESS) {
csio_err(hw, "FW_PORT_CMD(r) port:%d failed: 0x%x\n",
portid, retval);
@@ -1451,9 +1712,13 @@ csio_enable_ports(struct csio_hw *hw)
return -EINVAL;
}
+ csio_init_link_config(&hw->pport[i].link_cfg, pcaps, acaps);
+
+ csio_link_l1cfg(&hw->pport[i].link_cfg, fw_caps, &rcaps);
+
/* Write back PORT information */
- csio_mb_port(hw, mbp, CSIO_MB_DEFAULT_TMO, portid, true,
- (PAUSE_RX | PAUSE_TX), hw->pport[i].pcap, NULL);
+ csio_mb_port(hw, mbp, CSIO_MB_DEFAULT_TMO, portid,
+ true, rcaps, fw_caps, NULL);
if (csio_mb_issue(hw, mbp)) {
csio_err(hw, "failed to issue FW_PORT_CMD(w) port:%d\n",
diff --git a/drivers/scsi/csiostor/csio_hw.h b/drivers/scsi/csiostor/csio_hw.h
index 30f5f523c8cc..9e73ef771eb7 100644
--- a/drivers/scsi/csiostor/csio_hw.h
+++ b/drivers/scsi/csiostor/csio_hw.h
@@ -268,8 +268,62 @@ struct csio_vpd {
uint8_t id[ID_LEN + 1];
};
+/* Firmware Port Capabilities types. */
+
+typedef u16 fw_port_cap16_t; /* 16-bit Port Capabilities integral value */
+typedef u32 fw_port_cap32_t; /* 32-bit Port Capabilities integral value */
+
+enum fw_caps {
+ FW_CAPS_UNKNOWN = 0, /* 0'ed out initial state */
+ FW_CAPS16 = 1, /* old Firmware: 16-bit Port Capabilities */
+ FW_CAPS32 = 2, /* new Firmware: 32-bit Port Capabilities */
+};
+
+enum cc_pause {
+ PAUSE_RX = 1 << 0,
+ PAUSE_TX = 1 << 1,
+ PAUSE_AUTONEG = 1 << 2
+};
+
+enum cc_fec {
+ FEC_AUTO = 1 << 0, /* IEEE 802.3 "automatic" */
+ FEC_RS = 1 << 1, /* Reed-Solomon */
+ FEC_BASER_RS = 1 << 2 /* BaseR/Reed-Solomon */
+};
+
+struct link_config {
+ fw_port_cap32_t pcaps; /* link capabilities */
+ fw_port_cap32_t def_acaps; /* default advertised capabilities */
+ fw_port_cap32_t acaps; /* advertised capabilities */
+ fw_port_cap32_t lpacaps; /* peer advertised capabilities */
+
+ fw_port_cap32_t speed_caps; /* speed(s) user has requested */
+ unsigned int speed; /* actual link speed (Mb/s) */
+
+ enum cc_pause requested_fc; /* flow control user has requested */
+ enum cc_pause fc; /* actual link flow control */
+
+ enum cc_fec requested_fec; /* Forward Error Correction: */
+ enum cc_fec fec; /* requested and actual in use */
+
+ unsigned char autoneg; /* autonegotiating? */
+
+ unsigned char link_ok; /* link up? */
+ unsigned char link_down_rc; /* link down reason */
+};
+
+#define FW_LEN16(fw_struct) FW_CMD_LEN16_V(sizeof(fw_struct) / 16)
+
+#define ADVERT_MASK (FW_PORT_CAP32_SPEED_V(FW_PORT_CAP32_SPEED_M) | \
+ FW_PORT_CAP32_ANEG)
+
+/* Enable or disable autonegotiation. */
+#define AUTONEG_DISABLE 0x00
+#define AUTONEG_ENABLE 0x01
+
struct csio_pport {
uint16_t pcap;
+ uint16_t acap;
uint8_t portid;
uint8_t link_status;
uint16_t link_speed;
@@ -278,6 +332,7 @@ struct csio_pport {
uint8_t rsvd1;
uint8_t rsvd2;
uint8_t rsvd3;
+ struct link_config link_cfg;
};
/* fcoe resource information */
@@ -582,6 +637,10 @@ int csio_hw_slow_intr_handler(struct csio_hw *);
int csio_handle_intr_status(struct csio_hw *, unsigned int,
const struct intr_info *);
+fw_port_cap32_t fwcap_to_fwspeed(fw_port_cap32_t acaps);
+fw_port_cap32_t fwcaps16_to_caps32(fw_port_cap16_t caps16);
+fw_port_cap32_t lstatus_to_fwcap(u32 lstatus);
+
int csio_hw_start(struct csio_hw *);
int csio_hw_stop(struct csio_hw *);
int csio_hw_reset(struct csio_hw *);
diff --git a/drivers/scsi/csiostor/csio_lnode.c b/drivers/scsi/csiostor/csio_lnode.c
index 7dbbbb81a1e7..cc5611efc7a9 100644
--- a/drivers/scsi/csiostor/csio_lnode.c
+++ b/drivers/scsi/csiostor/csio_lnode.c
@@ -352,6 +352,14 @@ csio_ln_fdmi_rhba_cbfn(struct csio_hw *hw, struct csio_ioreq *fdmi_req)
val = htonl(FC_PORTSPEED_1GBIT);
else if (hw->pport[ln->portid].link_speed == FW_PORT_CAP_SPEED_10G)
val = htonl(FC_PORTSPEED_10GBIT);
+ else if (hw->pport[ln->portid].link_speed == FW_PORT_CAP32_SPEED_25G)
+ val = htonl(FC_PORTSPEED_25GBIT);
+ else if (hw->pport[ln->portid].link_speed == FW_PORT_CAP32_SPEED_40G)
+ val = htonl(FC_PORTSPEED_40GBIT);
+ else if (hw->pport[ln->portid].link_speed == FW_PORT_CAP32_SPEED_50G)
+ val = htonl(FC_PORTSPEED_50GBIT);
+ else if (hw->pport[ln->portid].link_speed == FW_PORT_CAP32_SPEED_100G)
+ val = htonl(FC_PORTSPEED_100GBIT);
else
val = htonl(CSIO_HBA_PORTSPEED_UNKNOWN);
csio_append_attrib(&pld, FC_FDMI_PORT_ATTR_CURRENTPORTSPEED,
diff --git a/drivers/scsi/csiostor/csio_mb.c b/drivers/scsi/csiostor/csio_mb.c
index 5f4e0a787bd1..c026417269c3 100644
--- a/drivers/scsi/csiostor/csio_mb.c
+++ b/drivers/scsi/csiostor/csio_mb.c
@@ -326,10 +326,6 @@ csio_mb_caps_config(struct csio_hw *hw, struct csio_mb *mbp, uint32_t tmo,
cmdp->fcoecaps |= htons(FW_CAPS_CONFIG_FCOE_TARGET);
}
-#define CSIO_ADVERT_MASK (FW_PORT_CAP_SPEED_100M | FW_PORT_CAP_SPEED_1G |\
- FW_PORT_CAP_SPEED_10G | FW_PORT_CAP_SPEED_40G |\
- FW_PORT_CAP_ANEG)
-
/*
* csio_mb_port- FW PORT command helper
* @hw: The HW structure
@@ -344,11 +340,10 @@ csio_mb_caps_config(struct csio_hw *hw, struct csio_mb *mbp, uint32_t tmo,
*/
void
csio_mb_port(struct csio_hw *hw, struct csio_mb *mbp, uint32_t tmo,
- uint8_t portid, bool wr, uint32_t fc, uint16_t caps,
+ u8 portid, bool wr, uint32_t fc, uint16_t fw_caps,
void (*cbfn) (struct csio_hw *, struct csio_mb *))
{
struct fw_port_cmd *cmdp = (struct fw_port_cmd *)(mbp->mb);
- unsigned int lfc = 0, mdi = FW_PORT_CAP_MDI_V(FW_PORT_CAP_MDI_AUTO);
CSIO_INIT_MBP(mbp, cmdp, tmo, hw, cbfn, 1);
@@ -358,26 +353,24 @@ csio_mb_port(struct csio_hw *hw, struct csio_mb *mbp, uint32_t tmo,
FW_PORT_CMD_PORTID_V(portid));
if (!wr) {
cmdp->action_to_len16 = htonl(
- FW_PORT_CMD_ACTION_V(FW_PORT_ACTION_GET_PORT_INFO) |
+ FW_PORT_CMD_ACTION_V(fw_caps == FW_CAPS16
+ ? FW_PORT_ACTION_GET_PORT_INFO
+ : FW_PORT_ACTION_GET_PORT_INFO32) |
FW_CMD_LEN16_V(sizeof(*cmdp) / 16));
return;
}
/* Set port */
cmdp->action_to_len16 = htonl(
- FW_PORT_CMD_ACTION_V(FW_PORT_ACTION_L1_CFG) |
+ FW_PORT_CMD_ACTION_V(fw_caps == FW_CAPS16
+ ? FW_PORT_ACTION_L1_CFG
+ : FW_PORT_ACTION_L1_CFG32) |
FW_CMD_LEN16_V(sizeof(*cmdp) / 16));
- if (fc & PAUSE_RX)
- lfc |= FW_PORT_CAP_FC_RX;
- if (fc & PAUSE_TX)
- lfc |= FW_PORT_CAP_FC_TX;
-
- if (!(caps & FW_PORT_CAP_ANEG))
- cmdp->u.l1cfg.rcap = htonl((caps & CSIO_ADVERT_MASK) | lfc);
+ if (fw_caps == FW_CAPS16)
+ cmdp->u.l1cfg.rcap = cpu_to_be32(fc);
else
- cmdp->u.l1cfg.rcap = htonl((caps & CSIO_ADVERT_MASK) |
- lfc | mdi);
+ cmdp->u.l1cfg32.rcap32 = cpu_to_be32(fc);
}
/*
@@ -390,14 +383,22 @@ csio_mb_port(struct csio_hw *hw, struct csio_mb *mbp, uint32_t tmo,
*/
void
csio_mb_process_read_port_rsp(struct csio_hw *hw, struct csio_mb *mbp,
- enum fw_retval *retval, uint16_t *caps)
+ enum fw_retval *retval, uint16_t fw_caps,
+ u32 *pcaps, u32 *acaps)
{
struct fw_port_cmd *rsp = (struct fw_port_cmd *)(mbp->mb);
*retval = FW_CMD_RETVAL_G(ntohl(rsp->action_to_len16));
- if (*retval == FW_SUCCESS)
- *caps = ntohs(rsp->u.info.pcap);
+ if (*retval == FW_SUCCESS) {
+ if (fw_caps == FW_CAPS16) {
+ *pcaps = fwcaps16_to_caps32(ntohs(rsp->u.info.pcap));
+ *acaps = fwcaps16_to_caps32(ntohs(rsp->u.info.acap));
+ } else {
+ *pcaps = ntohs(rsp->u.info32.pcaps32);
+ *acaps = ntohs(rsp->u.info32.acaps32);
+ }
+ }
}
/*
@@ -1409,6 +1410,7 @@ csio_mb_fwevt_handler(struct csio_hw *hw, __be64 *cmd)
uint32_t link_status;
uint16_t action;
uint8_t mod_type;
+ fw_port_cap32_t linkattr;
if (opcode == FW_PORT_CMD) {
pcmd = (struct fw_port_cmd *)cmd;
@@ -1416,22 +1418,34 @@ csio_mb_fwevt_handler(struct csio_hw *hw, __be64 *cmd)
ntohl(pcmd->op_to_portid));
action = FW_PORT_CMD_ACTION_G(
ntohl(pcmd->action_to_len16));
- if (action != FW_PORT_ACTION_GET_PORT_INFO) {
+ if (action != FW_PORT_ACTION_GET_PORT_INFO &&
+ action != FW_PORT_ACTION_GET_PORT_INFO32) {
csio_err(hw, "Unhandled FW_PORT_CMD action: %u\n",
action);
return -EINVAL;
}
- link_status = ntohl(pcmd->u.info.lstatus_to_modtype);
- mod_type = FW_PORT_CMD_MODTYPE_G(link_status);
+ if (action == FW_PORT_ACTION_GET_PORT_INFO) {
+ link_status = ntohl(pcmd->u.info.lstatus_to_modtype);
+ mod_type = FW_PORT_CMD_MODTYPE_G(link_status);
+ linkattr = lstatus_to_fwcap(link_status);
+
+ hw->pport[port_id].link_status =
+ FW_PORT_CMD_LSTATUS_G(link_status);
+ } else {
+ link_status =
+ ntohl(pcmd->u.info32.lstatus32_to_cbllen32);
+ mod_type = FW_PORT_CMD_MODTYPE32_G(link_status);
+ linkattr = ntohl(pcmd->u.info32.linkattr32);
+
+ hw->pport[port_id].link_status =
+ FW_PORT_CMD_LSTATUS32_G(link_status);
+ }
- hw->pport[port_id].link_status =
- FW_PORT_CMD_LSTATUS_G(link_status);
- hw->pport[port_id].link_speed =
- FW_PORT_CMD_LSPEED_G(link_status);
+ hw->pport[port_id].link_speed = fwcap_to_fwspeed(linkattr);
csio_info(hw, "Port:%x - LINK %s\n", port_id,
- FW_PORT_CMD_LSTATUS_G(link_status) ? "UP" : "DOWN");
+ hw->pport[port_id].link_status ? "UP" : "DOWN");
if (mod_type != hw->pport[port_id].mod_type) {
hw->pport[port_id].mod_type = mod_type;
diff --git a/drivers/scsi/csiostor/csio_mb.h b/drivers/scsi/csiostor/csio_mb.h
index a6823df73015..b07e891c5936 100644
--- a/drivers/scsi/csiostor/csio_mb.h
+++ b/drivers/scsi/csiostor/csio_mb.h
@@ -88,12 +88,6 @@ enum csio_dev_state {
FW_PARAMS_PARAM_Y_V(0) | \
FW_PARAMS_PARAM_Z_V(0))
-enum {
- PAUSE_RX = 1 << 0,
- PAUSE_TX = 1 << 1,
- PAUSE_AUTONEG = 1 << 2
-};
-
#define CSIO_INIT_MBP(__mbp, __cp, __tmo, __priv, __fn, __clear) \
do { \
if (__clear) \
@@ -189,7 +183,8 @@ void csio_mb_port(struct csio_hw *, struct csio_mb *, uint32_t,
void (*) (struct csio_hw *, struct csio_mb *));
void csio_mb_process_read_port_rsp(struct csio_hw *, struct csio_mb *,
- enum fw_retval *, uint16_t *);
+ enum fw_retval *, uint16_t,
+ uint32_t *, uint32_t *);
void csio_mb_initialize(struct csio_hw *, struct csio_mb *, uint32_t,
void (*)(struct csio_hw *, struct csio_mb *));
diff --git a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
index 406e94312d4e..211da1d5a869 100644
--- a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
+++ b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
@@ -2108,12 +2108,12 @@ static int t4_uld_rx_handler(void *handle, const __be64 *rsp,
log_debug(1 << CXGBI_DBG_TOE,
"cdev %p, opcode 0x%x(0x%x,0x%x), skb %p.\n",
cdev, opc, rpl->ot.opcode_tid, ntohl(rpl->ot.opcode_tid), skb);
- if (cxgb4i_cplhandlers[opc])
- cxgb4i_cplhandlers[opc](cdev, skb);
- else {
+ if (opc >= ARRAY_SIZE(cxgb4i_cplhandlers) || !cxgb4i_cplhandlers[opc]) {
pr_err("No handler for opcode 0x%x.\n", opc);
__kfree_skb(skb);
- }
+ } else
+ cxgb4i_cplhandlers[opc](cdev, skb);
+
return 0;
nomem:
log_debug(1 << CXGBI_DBG_TOE, "OOM bailing out.\n");
diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c
index 4b44325d1a82..12dc7100bb4c 100644
--- a/drivers/scsi/device_handler/scsi_dh_alua.c
+++ b/drivers/scsi/device_handler/scsi_dh_alua.c
@@ -138,12 +138,12 @@ static void release_port_group(struct kref *kref)
static int submit_rtpg(struct scsi_device *sdev, unsigned char *buff,
int bufflen, struct scsi_sense_hdr *sshdr, int flags)
{
- u8 cdb[COMMAND_SIZE(MAINTENANCE_IN)];
+ u8 cdb[MAX_COMMAND_SIZE];
int req_flags = REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT |
REQ_FAILFAST_DRIVER;
/* Prepare the command. */
- memset(cdb, 0x0, COMMAND_SIZE(MAINTENANCE_IN));
+ memset(cdb, 0x0, MAX_COMMAND_SIZE);
cdb[0] = MAINTENANCE_IN;
if (!(flags & ALUA_RTPG_EXT_HDR_UNSUPP))
cdb[1] = MI_REPORT_TARGET_PGS | MI_EXT_HDR_PARAM_FMT;
@@ -166,7 +166,7 @@ static int submit_rtpg(struct scsi_device *sdev, unsigned char *buff,
static int submit_stpg(struct scsi_device *sdev, int group_id,
struct scsi_sense_hdr *sshdr)
{
- u8 cdb[COMMAND_SIZE(MAINTENANCE_OUT)];
+ u8 cdb[MAX_COMMAND_SIZE];
unsigned char stpg_data[8];
int stpg_len = 8;
int req_flags = REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT |
@@ -178,7 +178,7 @@ static int submit_stpg(struct scsi_device *sdev, int group_id,
put_unaligned_be16(group_id, &stpg_data[6]);
/* Prepare the command. */
- memset(cdb, 0x0, COMMAND_SIZE(MAINTENANCE_OUT));
+ memset(cdb, 0x0, MAX_COMMAND_SIZE);
cdb[0] = MAINTENANCE_OUT;
cdb[1] = MO_SET_TARGET_PGS;
put_unaligned_be32(stpg_len, &cdb[6]);
@@ -214,8 +214,8 @@ static struct alua_port_group *alua_find_get_pg(char *id_str, size_t id_size,
/*
* alua_alloc_pg - Allocate a new port_group structure
* @sdev: scsi device
- * @h: alua device_handler data
* @group_id: port group id
+ * @tpgs: target port group settings
*
* Allocate a new port_group structure for a given
* device.
diff --git a/drivers/scsi/device_handler/scsi_dh_emc.c b/drivers/scsi/device_handler/scsi_dh_emc.c
index 6a2792f3a37e..95c47909a58f 100644
--- a/drivers/scsi/device_handler/scsi_dh_emc.c
+++ b/drivers/scsi/device_handler/scsi_dh_emc.c
@@ -249,7 +249,7 @@ static int send_trespass_cmd(struct scsi_device *sdev,
struct clariion_dh_data *csdev)
{
unsigned char *page22;
- unsigned char cdb[COMMAND_SIZE(MODE_SELECT)];
+ unsigned char cdb[MAX_COMMAND_SIZE];
int err, res = SCSI_DH_OK, len;
struct scsi_sense_hdr sshdr;
u64 req_flags = REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT |
diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c
index 7af31a1247ee..d27fabae8ddd 100644
--- a/drivers/scsi/device_handler/scsi_dh_rdac.c
+++ b/drivers/scsi/device_handler/scsi_dh_rdac.c
@@ -533,7 +533,7 @@ static void send_mode_select(struct work_struct *work)
int err = SCSI_DH_OK, retry_cnt = RDAC_RETRY_COUNT;
struct rdac_queue_data *tmp, *qdata;
LIST_HEAD(list);
- unsigned char cdb[COMMAND_SIZE(MODE_SELECT_10)];
+ unsigned char cdb[MAX_COMMAND_SIZE];
struct scsi_sense_hdr sshdr;
unsigned int data_size;
u64 req_flags = REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT |
diff --git a/drivers/scsi/dpt_i2o.c b/drivers/scsi/dpt_i2o.c
index fd172b0890d3..5ceea8da7bb6 100644
--- a/drivers/scsi/dpt_i2o.c
+++ b/drivers/scsi/dpt_i2o.c
@@ -302,16 +302,14 @@ rebuild_sys_tab:
}
-/*
- * scsi_unregister will be called AFTER we return.
- */
-static int adpt_release(struct Scsi_Host *host)
+static void adpt_release(adpt_hba *pHba)
{
- adpt_hba* pHba = (adpt_hba*) host->hostdata[0];
+ struct Scsi_Host *shost = pHba->host;
+
+ scsi_remove_host(shost);
// adpt_i2o_quiesce_hba(pHba);
adpt_i2o_delete_hba(pHba);
- scsi_unregister(host);
- return 0;
+ scsi_host_put(shost);
}
@@ -801,14 +799,17 @@ static int __adpt_reset(struct scsi_cmnd* cmd)
{
adpt_hba* pHba;
int rcode;
+ char name[32];
+
pHba = (adpt_hba*)cmd->device->host->hostdata[0];
- printk(KERN_WARNING"%s: Hba Reset: scsi id %d: tid: %d\n",pHba->name,cmd->device->channel,pHba->channel[cmd->device->channel].tid );
+ strncpy(name, pHba->name, sizeof(name));
+ printk(KERN_WARNING"%s: Hba Reset: scsi id %d: tid: %d\n", name, cmd->device->channel, pHba->channel[cmd->device->channel].tid);
rcode = adpt_hba_reset(pHba);
if(rcode == 0){
- printk(KERN_WARNING"%s: HBA reset complete\n",pHba->name);
+ printk(KERN_WARNING"%s: HBA reset complete\n", name);
return SUCCESS;
} else {
- printk(KERN_WARNING"%s: HBA reset failed (%x)\n",pHba->name, rcode);
+ printk(KERN_WARNING"%s: HBA reset failed (%x)\n", name, rcode);
return FAILED;
}
}
@@ -1087,8 +1088,6 @@ static void adpt_i2o_delete_hba(adpt_hba* pHba)
mutex_lock(&adpt_configuration_lock);
- // scsi_unregister calls our adpt_release which
- // does a quiese
if(pHba->host){
free_irq(pHba->host->irq, pHba);
}
@@ -2052,13 +2051,16 @@ static int adpt_ioctl(struct inode *inode, struct file *file, uint cmd, ulong ar
}
break;
}
- case I2ORESETCMD:
- if(pHba->host)
- spin_lock_irqsave(pHba->host->host_lock, flags);
+ case I2ORESETCMD: {
+ struct Scsi_Host *shost = pHba->host;
+
+ if (shost)
+ spin_lock_irqsave(shost->host_lock, flags);
adpt_hba_reset(pHba);
- if(pHba->host)
- spin_unlock_irqrestore(pHba->host->host_lock, flags);
+ if (shost)
+ spin_unlock_irqrestore(shost->host_lock, flags);
break;
+ }
case I2ORESCANCMD:
adpt_rescan(pHba);
break;
@@ -3524,7 +3526,7 @@ static int adpt_i2o_systab_send(adpt_hba* pHba)
#endif
return ret;
- }
+}
/*============================================================================
@@ -3595,11 +3597,9 @@ static void __exit adpt_exit(void)
{
adpt_hba *pHba, *next;
- for (pHba = hba_chain; pHba; pHba = pHba->next)
- scsi_remove_host(pHba->host);
for (pHba = hba_chain; pHba; pHba = next) {
next = pHba->next;
- adpt_release(pHba->host);
+ adpt_release(pHba);
}
}
diff --git a/drivers/scsi/dpti.h b/drivers/scsi/dpti.h
index 1fa345ab8ecb..dfc8d2eaa09e 100644
--- a/drivers/scsi/dpti.h
+++ b/drivers/scsi/dpti.h
@@ -32,7 +32,6 @@ static int adpt_detect(struct scsi_host_template * sht);
static int adpt_queue(struct Scsi_Host *h, struct scsi_cmnd * cmd);
static int adpt_abort(struct scsi_cmnd * cmd);
static int adpt_reset(struct scsi_cmnd* cmd);
-static int adpt_release(struct Scsi_Host *host);
static int adpt_slave_configure(struct scsi_device *);
static const char *adpt_info(struct Scsi_Host *pSHost);
diff --git a/drivers/scsi/eata.c b/drivers/scsi/eata.c
deleted file mode 100644
index 6501c330d8c8..000000000000
--- a/drivers/scsi/eata.c
+++ /dev/null
@@ -1,2571 +0,0 @@
-/*
- * eata.c - Low-level driver for EATA/DMA SCSI host adapters.
- *
- * 03 Jun 2003 Rev. 8.10 for linux-2.5.70
- * + Update for new IRQ API.
- * + Use "goto" when appropriate.
- * + Drop eata.h.
- * + Update for new module_param API.
- * + Module parameters can now be specified only in the
- * same format as the kernel boot options.
- *
- * boot option old module param
- * ----------- ------------------
- * addr,... io_port=addr,...
- * lc:[y|n] linked_comm=[1|0]
- * mq:xx max_queue_depth=xx
- * tm:[0|1|2] tag_mode=[0|1|2]
- * et:[y|n] ext_tran=[1|0]
- * rs:[y|n] rev_scan=[1|0]
- * ip:[y|n] isa_probe=[1|0]
- * ep:[y|n] eisa_probe=[1|0]
- * pp:[y|n] pci_probe=[1|0]
- *
- * A valid example using the new parameter format is:
- * modprobe eata "eata=0x7410,0x230,lc:y,tm:0,mq:4,ep:n"
- *
- * which is equivalent to the old format:
- * modprobe eata io_port=0x7410,0x230 linked_comm=1 tag_mode=0 \
- * max_queue_depth=4 eisa_probe=0
- *
- * 12 Feb 2003 Rev. 8.04 for linux 2.5.60
- * + Release irq before calling scsi_register.
- *
- * 12 Nov 2002 Rev. 8.02 for linux 2.5.47
- * + Release driver_lock before calling scsi_register.
- *
- * 11 Nov 2002 Rev. 8.01 for linux 2.5.47
- * + Fixed bios_param and scsicam_bios_param calling parameters.
- *
- * 28 Oct 2002 Rev. 8.00 for linux 2.5.44-ac4
- * + Use new tcq and adjust_queue_depth api.
- * + New command line option (tm:[0-2]) to choose the type of tags:
- * 0 -> disable tagging ; 1 -> simple tags ; 2 -> ordered tags.
- * Default is tm:0 (tagged commands disabled).
- * For compatibility the "tc:" option is an alias of the "tm:"
- * option; tc:n is equivalent to tm:0 and tc:y is equivalent to
- * tm:1.
- * + The tagged_comm module parameter has been removed, use tag_mode
- * instead, equivalent to the "tm:" boot option.
- *
- * 10 Oct 2002 Rev. 7.70 for linux 2.5.42
- * + Foreport from revision 6.70.
- *
- * 25 Jun 2002 Rev. 6.70 for linux 2.4.19
- * + This release is the first one tested on a Big Endian platform:
- * fixed endian-ness problem due to bitfields;
- * fixed endian-ness problem in read_pio.
- * + Added new options for selectively probing ISA, EISA and PCI bus:
- *
- * Boot option Parameter name Default according to
- *
- * ip:[y|n] isa_probe=[1|0] CONFIG_ISA defined
- * ep:[y|n] eisa_probe=[1|0] CONFIG_EISA defined
- * pp:[y|n] pci_probe=[1|0] CONFIG_PCI defined
- *
- * The default action is to perform probing if the corresponding
- * bus is configured and to skip probing otherwise.
- *
- * + If pci_probe is in effect and a list of I/O ports is specified
- * as parameter or boot option, pci_enable_device() is performed
- * on all pci devices matching PCI_CLASS_STORAGE_SCSI.
- *
- * 21 Feb 2002 Rev. 6.52 for linux 2.4.18
- * + Backport from rev. 7.22 (use io_request_lock).
- *
- * 20 Feb 2002 Rev. 7.22 for linux 2.5.5
- * + Remove any reference to virt_to_bus().
- * + Fix pio hang while detecting multiple HBAs.
- * + Fixed a board detection bug: in a system with
- * multiple ISA/EISA boards, all but the first one
- * were erroneously detected as PCI.
- *
- * 01 Jan 2002 Rev. 7.20 for linux 2.5.1
- * + Use the dynamic DMA mapping API.
- *
- * 19 Dec 2001 Rev. 7.02 for linux 2.5.1
- * + Use SCpnt->sc_data_direction if set.
- * + Use sglist.page instead of sglist.address.
- *
- * 11 Dec 2001 Rev. 7.00 for linux 2.5.1
- * + Use host->host_lock instead of io_request_lock.
- *
- * 1 May 2001 Rev. 6.05 for linux 2.4.4
- * + Clean up all pci related routines.
- * + Fix data transfer direction for opcode SEND_CUE_SHEET (0x5d)
- *
- * 30 Jan 2001 Rev. 6.04 for linux 2.4.1
- * + Call pci_resource_start after pci_enable_device.
- *
- * 25 Jan 2001 Rev. 6.03 for linux 2.4.0
- * + "check_region" call replaced by "request_region".
- *
- * 22 Nov 2000 Rev. 6.02 for linux 2.4.0-test11
- * + Return code checked when calling pci_enable_device.
- * + Removed old scsi error handling support.
- * + The obsolete boot option flag eh:n is silently ignored.
- * + Removed error messages while a disk drive is powered up at
- * boot time.
- * + Improved boot messages: all tagged capable device are
- * indicated as "tagged" or "soft-tagged" :
- * - "soft-tagged" means that the driver is trying to do its
- * own tagging (i.e. the tc:y option is in effect);
- * - "tagged" means that the device supports tagged commands,
- * but the driver lets the HBA be responsible for tagging
- * support.
- *
- * 16 Sep 1999 Rev. 5.11 for linux 2.2.12 and 2.3.18
- * + Updated to the new __setup interface for boot command line options.
- * + When loaded as a module, accepts the new parameter boot_options
- * which value is a string with the same format of the kernel boot
- * command line options. A valid example is:
- * modprobe eata 'boot_options="0x7410,0x230,lc:y,tc:n,mq:4"'
- *
- * 9 Sep 1999 Rev. 5.10 for linux 2.2.12 and 2.3.17
- * + 64bit cleanup for Linux/Alpha platform support
- * (contribution from H.J. Lu).
- *
- * 22 Jul 1999 Rev. 5.00 for linux 2.2.10 and 2.3.11
- * + Removed pre-2.2 source code compatibility.
- * + Added call to pci_set_master.
- *
- * 26 Jul 1998 Rev. 4.33 for linux 2.0.35 and 2.1.111
- * + Added command line option (rs:[y|n]) to reverse the scan order
- * of PCI boards. The default is rs:y, which reverses the BIOS order
- * while registering PCI boards. The default value rs:y generates
- * the same order of all previous revisions of this driver.
- * Pls. note that "BIOS order" might have been reversed itself
- * after the 2.1.9x PCI modifications in the linux kernel.
- * The rs value is ignored when the explicit list of addresses
- * is used by the "eata=port0,port1,..." command line option.
- * + Added command line option (et:[y|n]) to force use of extended
- * translation (255 heads, 63 sectors) as disk geometry.
- * The default is et:n, which uses the disk geometry returned
- * by scsicam_bios_param. The default value et:n is compatible with
- * all previous revisions of this driver.
- *
- * 28 May 1998 Rev. 4.32 for linux 2.0.33 and 2.1.104
- * Increased busy timeout from 10 msec. to 200 msec. while
- * processing interrupts.
- *
- * 16 May 1998 Rev. 4.31 for linux 2.0.33 and 2.1.102
- * Improved abort handling during the eh recovery process.
- *
- * 13 May 1998 Rev. 4.30 for linux 2.0.33 and 2.1.101
- * The driver is now fully SMP safe, including the
- * abort and reset routines.
- * Added command line options (eh:[y|n]) to choose between
- * new_eh_code and the old scsi code.
- * If linux version >= 2.1.101 the default is eh:y, while the eh
- * option is ignored for previous releases and the old scsi code
- * is used.
- *
- * 18 Apr 1998 Rev. 4.20 for linux 2.0.33 and 2.1.97
- * Reworked interrupt handler.
- *
- * 11 Apr 1998 rev. 4.05 for linux 2.0.33 and 2.1.95
- * Major reliability improvement: when a batch with overlapping
- * requests is detected, requests are queued one at a time
- * eliminating any possible board or drive reordering.
- *
- * 10 Apr 1998 rev. 4.04 for linux 2.0.33 and 2.1.95
- * Improved SMP support (if linux version >= 2.1.95).
- *
- * 9 Apr 1998 rev. 4.03 for linux 2.0.33 and 2.1.94
- * Added support for new PCI code and IO-APIC remapping of irqs.
- * Performance improvement: when sequential i/o is detected,
- * always use direct sort instead of reverse sort.
- *
- * 4 Apr 1998 rev. 4.02 for linux 2.0.33 and 2.1.92
- * io_port is now unsigned long.
- *
- * 17 Mar 1998 rev. 4.01 for linux 2.0.33 and 2.1.88
- * Use new scsi error handling code (if linux version >= 2.1.88).
- * Use new interrupt code.
- *
- * 12 Sep 1997 rev. 3.11 for linux 2.0.30 and 2.1.55
- * Use of udelay inside the wait loops to avoid timeout
- * problems with fast cpus.
- * Removed check about useless calls to the interrupt service
- * routine (reported on SMP systems only).
- * At initialization time "sorted/unsorted" is displayed instead
- * of "linked/unlinked" to reinforce the fact that "linking" is
- * nothing but "elevator sorting" in the actual implementation.
- *
- * 17 May 1997 rev. 3.10 for linux 2.0.30 and 2.1.38
- * Use of serial_number_at_timeout in abort and reset processing.
- * Use of the __initfunc and __initdata macro in setup code.
- * Minor cleanups in the list_statistics code.
- * Increased controller busy timeout in order to better support
- * slow SCSI devices.
- *
- * 24 Feb 1997 rev. 3.00 for linux 2.0.29 and 2.1.26
- * When loading as a module, parameter passing is now supported
- * both in 2.0 and in 2.1 style.
- * Fixed data transfer direction for some SCSI opcodes.
- * Immediate acknowledge to request sense commands.
- * Linked commands to each disk device are now reordered by elevator
- * sorting. Rare cases in which reordering of write requests could
- * cause wrong results are managed.
- * Fixed spurious timeouts caused by long simple queue tag sequences.
- * New command line option (tm:[0-3]) to choose the type of tags:
- * 0 -> mixed (default); 1 -> simple; 2 -> head; 3 -> ordered.
- *
- * 18 Jan 1997 rev. 2.60 for linux 2.1.21 and 2.0.28
- * Added command line options to enable/disable linked commands
- * (lc:[y|n]), tagged commands (tc:[y|n]) and to set the max queue
- * depth (mq:xx). Default is "eata=lc:n,tc:n,mq:16".
- * Improved command linking.
- * Documented how to setup RAID-0 with DPT SmartRAID boards.
- *
- * 8 Jan 1997 rev. 2.50 for linux 2.1.20 and 2.0.27
- * Added linked command support.
- * Improved detection of PCI boards using ISA base addresses.
- *
- * 3 Dec 1996 rev. 2.40 for linux 2.1.14 and 2.0.27
- * Added support for tagged commands and queue depth adjustment.
- *
- * 22 Nov 1996 rev. 2.30 for linux 2.1.12 and 2.0.26
- * When CONFIG_PCI is defined, BIOS32 is used to include in the
- * list of i/o ports to be probed all the PCI SCSI controllers.
- * The list of i/o ports to be probed can be overwritten by the
- * "eata=port0,port1,...." boot command line option.
- * Scatter/gather lists are now allocated by a number of kmalloc
- * calls, in order to avoid the previous size limit of 64Kb.
- *
- * 16 Nov 1996 rev. 2.20 for linux 2.1.10 and 2.0.25
- * Added support for EATA 2.0C, PCI, multichannel and wide SCSI.
- *
- * 27 Sep 1996 rev. 2.12 for linux 2.1.0
- * Portability cleanups (virtual/bus addressing, little/big endian
- * support).
- *
- * 09 Jul 1996 rev. 2.11 for linux 2.0.4
- * Number of internal retries is now limited.
- *
- * 16 Apr 1996 rev. 2.10 for linux 1.3.90
- * New argument "reset_flags" to the reset routine.
- *
- * 6 Jul 1995 rev. 2.01 for linux 1.3.7
- * Update required by the new /proc/scsi support.
- *
- * 11 Mar 1995 rev. 2.00 for linux 1.2.0
- * Fixed a bug which prevented media change detection for removable
- * disk drives.
- *
- * 23 Feb 1995 rev. 1.18 for linux 1.1.94
- * Added a check for scsi_register returning NULL.
- *
- * 11 Feb 1995 rev. 1.17 for linux 1.1.91
- * Now DEBUG_RESET is disabled by default.
- * Register a board even if it does not assert DMA protocol support
- * (DPT SK2011B does not report correctly the dmasup bit).
- *
- * 9 Feb 1995 rev. 1.16 for linux 1.1.90
- * Use host->wish_block instead of host->block.
- * New list of Data Out SCSI commands.
- *
- * 8 Feb 1995 rev. 1.15 for linux 1.1.89
- * Cleared target_time_out counter while performing a reset.
- * All external symbols renamed to avoid possible name conflicts.
- *
- * 28 Jan 1995 rev. 1.14 for linux 1.1.86
- * Added module support.
- * Log and do a retry when a disk drive returns a target status
- * different from zero on a recovered error.
- *
- * 24 Jan 1995 rev. 1.13 for linux 1.1.85
- * Use optimized board configuration, with a measured performance
- * increase in the range 10%-20% on i/o throughput.
- *
- * 16 Jan 1995 rev. 1.12 for linux 1.1.81
- * Fix mscp structure comments (no functional change).
- * Display a message if check_region detects a port address
- * already in use.
- *
- * 17 Dec 1994 rev. 1.11 for linux 1.1.74
- * Use the scsicam_bios_param routine. This allows an easy
- * migration path from disk partition tables created using
- * different SCSI drivers and non optimal disk geometry.
- *
- * 15 Dec 1994 rev. 1.10 for linux 1.1.74
- * Added support for ISA EATA boards (DPT PM2011, DPT PM2021).
- * The host->block flag is set for all the detected ISA boards.
- * The detect routine no longer enforces LEVEL triggering
- * for EISA boards, it just prints a warning message.
- *
- * 30 Nov 1994 rev. 1.09 for linux 1.1.68
- * Redo i/o on target status CHECK_CONDITION for TYPE_DISK only.
- * Added optional support for using a single board at a time.
- *
- * 18 Nov 1994 rev. 1.08 for linux 1.1.64
- * Forces sg_tablesize = 64 and can_queue = 64 if these
- * values are not correctly detected (DPT PM2012).
- *
- * 14 Nov 1994 rev. 1.07 for linux 1.1.63 Final BETA release.
- * 04 Aug 1994 rev. 1.00 for linux 1.1.39 First BETA release.
- *
- *
- * This driver is based on the CAM (Common Access Method Committee)
- * EATA (Enhanced AT Bus Attachment) rev. 2.0A, using DMA protocol.
- *
- * Copyright (C) 1994-2003 Dario Ballabio (ballabio_dario@emc.com)
- *
- * Alternate email: dario.ballabio@inwind.it, dario.ballabio@tiscalinet.it
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that redistributions of source
- * code retain the above copyright notice and this comment without
- * modification.
- *
- */
-
-/*
- *
- * Here is a brief description of the DPT SCSI host adapters.
- * All these boards provide an EATA/DMA compatible programming interface
- * and are fully supported by this driver in any configuration, including
- * multiple SCSI channels:
- *
- * PM2011B/9X - Entry Level ISA
- * PM2021A/9X - High Performance ISA
- * PM2012A Old EISA
- * PM2012B Old EISA
- * PM2022A/9X - Entry Level EISA
- * PM2122A/9X - High Performance EISA
- * PM2322A/9X - Extra High Performance EISA
- * PM3021 - SmartRAID Adapter for ISA
- * PM3222 - SmartRAID Adapter for EISA (PM3222W is 16-bit wide SCSI)
- * PM3224 - SmartRAID Adapter for PCI (PM3224W is 16-bit wide SCSI)
- * PM33340UW - SmartRAID Adapter for PCI ultra wide multichannel
- *
- * The above list is just an indication: as a matter of fact all DPT
- * boards using the EATA/DMA protocol are supported by this driver,
- * since they use exactely the same programming interface.
- *
- * The DPT PM2001 provides only the EATA/PIO interface and hence is not
- * supported by this driver.
- *
- * This code has been tested with up to 3 Distributed Processing Technology
- * PM2122A/9X (DPT SCSI BIOS v002.D1, firmware v05E.0) EISA controllers,
- * in any combination of private and shared IRQ.
- * PCI support has been tested using up to 2 DPT PM3224W (DPT SCSI BIOS
- * v003.D0, firmware v07G.0).
- *
- * DPT SmartRAID boards support "Hardware Array" - a group of disk drives
- * which are all members of the same RAID-0, RAID-1 or RAID-5 array implemented
- * in host adapter hardware. Hardware Arrays are fully compatible with this
- * driver, since they look to it as a single disk drive.
- *
- * WARNING: to create a RAID-0 "Hardware Array" you must select "Other Unix"
- * as the current OS in the DPTMGR "Initial System Installation" menu.
- * Otherwise RAID-0 is generated as an "Array Group" (i.e. software RAID-0),
- * which is not supported by the actual SCSI subsystem.
- * To get the "Array Group" functionality, the Linux MD driver must be used
- * instead of the DPT "Array Group" feature.
- *
- * Multiple ISA, EISA and PCI boards can be configured in the same system.
- * It is suggested to put all the EISA boards on the same IRQ level, all
- * the PCI boards on another IRQ level, while ISA boards cannot share
- * interrupts.
- *
- * If you configure multiple boards on the same IRQ, the interrupt must
- * be _level_ triggered (not _edge_ triggered).
- *
- * This driver detects EATA boards by probes at fixed port addresses,
- * so no BIOS32 or PCI BIOS support is required.
- * The suggested way to detect a generic EATA PCI board is to force on it
- * any unused EISA address, even if there are other controllers on the EISA
- * bus, or even if you system has no EISA bus at all.
- * Do not force any ISA address on EATA PCI boards.
- *
- * If PCI bios support is configured into the kernel, BIOS32 is used to
- * include in the list of i/o ports to be probed all the PCI SCSI controllers.
- *
- * Due to a DPT BIOS "feature", it might not be possible to force an EISA
- * address on more than a single DPT PCI board, so in this case you have to
- * let the PCI BIOS assign the addresses.
- *
- * The sequence of detection probes is:
- *
- * - ISA 0x1F0;
- * - PCI SCSI controllers (only if BIOS32 is available);
- * - EISA/PCI 0x1C88 through 0xFC88 (corresponding to EISA slots 1 to 15);
- * - ISA 0x170, 0x230, 0x330.
- *
- * The above list of detection probes can be totally replaced by the
- * boot command line option: "eata=port0,port1,port2,...", where the
- * port0, port1... arguments are ISA/EISA/PCI addresses to be probed.
- * For example using "eata=0x7410,0x7450,0x230", the driver probes
- * only the two PCI addresses 0x7410 and 0x7450 and the ISA address 0x230,
- * in this order; "eata=0" totally disables this driver.
- *
- * After the optional list of detection probes, other possible command line
- * options are:
- *
- * et:y force use of extended translation (255 heads, 63 sectors);
- * et:n use disk geometry detected by scsicam_bios_param;
- * rs:y reverse scan order while detecting PCI boards;
- * rs:n use BIOS order while detecting PCI boards;
- * lc:y enables linked commands;
- * lc:n disables linked commands;
- * tm:0 disables tagged commands (same as tc:n);
- * tm:1 use simple queue tags (same as tc:y);
- * tm:2 use ordered queue tags (same as tc:2);
- * mq:xx set the max queue depth to the value xx (2 <= xx <= 32).
- *
- * The default value is: "eata=lc:n,mq:16,tm:0,et:n,rs:n".
- * An example using the list of detection probes could be:
- * "eata=0x7410,0x230,lc:y,tm:2,mq:4,et:n".
- *
- * When loading as a module, parameters can be specified as well.
- * The above example would be (use 1 in place of y and 0 in place of n):
- *
- * modprobe eata io_port=0x7410,0x230 linked_comm=1 \
- * max_queue_depth=4 ext_tran=0 tag_mode=2 \
- * rev_scan=1
- *
- * ----------------------------------------------------------------------------
- * In this implementation, linked commands are designed to work with any DISK
- * or CD-ROM, since this linking has only the intent of clustering (time-wise)
- * and reordering by elevator sorting commands directed to each device,
- * without any relation with the actual SCSI protocol between the controller
- * and the device.
- * If Q is the queue depth reported at boot time for each device (also named
- * cmds/lun) and Q > 2, whenever there is already an active command to the
- * device all other commands to the same device (up to Q-1) are kept waiting
- * in the elevator sorting queue. When the active command completes, the
- * commands in this queue are sorted by sector address. The sort is chosen
- * between increasing or decreasing by minimizing the seek distance between
- * the sector of the commands just completed and the sector of the first
- * command in the list to be sorted.
- * Trivial math assures that the unsorted average seek distance when doing
- * random seeks over S sectors is S/3.
- * When (Q-1) requests are uniformly distributed over S sectors, the average
- * distance between two adjacent requests is S/((Q-1) + 1), so the sorted
- * average seek distance for (Q-1) random requests over S sectors is S/Q.
- * The elevator sorting hence divides the seek distance by a factor Q/3.
- * The above pure geometric remarks are valid in all cases and the
- * driver effectively reduces the seek distance by the predicted factor
- * when there are Q concurrent read i/o operations on the device, but this
- * does not necessarily results in a noticeable performance improvement:
- * your mileage may vary....
- *
- * Note: command reordering inside a batch of queued commands could cause
- * wrong results only if there is at least one write request and the
- * intersection (sector-wise) of all requests is not empty.
- * When the driver detects a batch including overlapping requests
- * (a really rare event) strict serial (pid) order is enforced.
- * ----------------------------------------------------------------------------
- * The extended translation option (et:y) is useful when using large physical
- * disks/arrays. It could also be useful when switching between Adaptec boards
- * and DPT boards without reformatting the disk.
- * When a boot disk is partitioned with extended translation, in order to
- * be able to boot it with a DPT board is could be necessary to add to
- * lilo.conf additional commands as in the following example:
- *
- * fix-table
- * disk=/dev/sda bios=0x80 sectors=63 heads=128 cylindres=546
- *
- * where the above geometry should be replaced with the one reported at
- * power up by the DPT controller.
- * ----------------------------------------------------------------------------
- *
- * The boards are named EATA0, EATA1,... according to the detection order.
- *
- * In order to support multiple ISA boards in a reliable way,
- * the driver sets host->wish_block = 1 for all ISA boards.
- */
-
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/proc_fs.h>
-#include <linux/blkdev.h>
-#include <linux/interrupt.h>
-#include <linux/stat.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/ctype.h>
-#include <linux/spinlock.h>
-#include <linux/dma-mapping.h>
-#include <linux/slab.h>
-#include <asm/byteorder.h>
-#include <asm/dma.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
-#include <scsi/scsi_device.h>
-#include <scsi/scsi_host.h>
-#include <scsi/scsi_tcq.h>
-#include <scsi/scsicam.h>
-
-static int eata2x_detect(struct scsi_host_template *);
-static int eata2x_release(struct Scsi_Host *);
-static int eata2x_queuecommand(struct Scsi_Host *, struct scsi_cmnd *);
-static int eata2x_eh_abort(struct scsi_cmnd *);
-static int eata2x_eh_host_reset(struct scsi_cmnd *);
-static int eata2x_bios_param(struct scsi_device *, struct block_device *,
- sector_t, int *);
-static int eata2x_slave_configure(struct scsi_device *);
-
-static struct scsi_host_template driver_template = {
- .name = "EATA/DMA 2.0x rev. 8.10.00 ",
- .detect = eata2x_detect,
- .release = eata2x_release,
- .queuecommand = eata2x_queuecommand,
- .eh_abort_handler = eata2x_eh_abort,
- .eh_host_reset_handler = eata2x_eh_host_reset,
- .bios_param = eata2x_bios_param,
- .slave_configure = eata2x_slave_configure,
- .this_id = 7,
- .unchecked_isa_dma = 1,
- .use_clustering = ENABLE_CLUSTERING,
-};
-
-#if !defined(__BIG_ENDIAN_BITFIELD) && !defined(__LITTLE_ENDIAN_BITFIELD)
-#error "Adjust your <asm/byteorder.h> defines"
-#endif
-
-/* Subversion values */
-#define ISA 0
-#define ESA 1
-
-#undef FORCE_CONFIG
-
-#undef DEBUG_LINKED_COMMANDS
-#undef DEBUG_DETECT
-#undef DEBUG_PCI_DETECT
-#undef DEBUG_INTERRUPT
-#undef DEBUG_RESET
-#undef DEBUG_GENERATE_ERRORS
-#undef DEBUG_GENERATE_ABORTS
-#undef DEBUG_GEOMETRY
-
-#define MAX_ISA 4
-#define MAX_VESA 0
-#define MAX_EISA 15
-#define MAX_PCI 16
-#define MAX_BOARDS (MAX_ISA + MAX_VESA + MAX_EISA + MAX_PCI)
-#define MAX_CHANNEL 4
-#define MAX_LUN 32
-#define MAX_TARGET 32
-#define MAX_MAILBOXES 64
-#define MAX_SGLIST 64
-#define MAX_LARGE_SGLIST 122
-#define MAX_INTERNAL_RETRIES 64
-#define MAX_CMD_PER_LUN 2
-#define MAX_TAGGED_CMD_PER_LUN (MAX_MAILBOXES - MAX_CMD_PER_LUN)
-
-#define SKIP ULONG_MAX
-#define FREE 0
-#define IN_USE 1
-#define LOCKED 2
-#define IN_RESET 3
-#define IGNORE 4
-#define READY 5
-#define ABORTING 6
-#define NO_DMA 0xff
-#define MAXLOOP 10000
-#define TAG_DISABLED 0
-#define TAG_SIMPLE 1
-#define TAG_ORDERED 2
-
-#define REG_CMD 7
-#define REG_STATUS 7
-#define REG_AUX_STATUS 8
-#define REG_DATA 0
-#define REG_DATA2 1
-#define REG_SEE 6
-#define REG_LOW 2
-#define REG_LM 3
-#define REG_MID 4
-#define REG_MSB 5
-#define REGION_SIZE 9UL
-#define MAX_ISA_ADDR 0x03ff
-#define MIN_EISA_ADDR 0x1c88
-#define MAX_EISA_ADDR 0xfc88
-#define BSY_ASSERTED 0x80
-#define DRQ_ASSERTED 0x08
-#define ABSY_ASSERTED 0x01
-#define IRQ_ASSERTED 0x02
-#define READ_CONFIG_PIO 0xf0
-#define SET_CONFIG_PIO 0xf1
-#define SEND_CP_PIO 0xf2
-#define RECEIVE_SP_PIO 0xf3
-#define TRUNCATE_XFR_PIO 0xf4
-#define RESET_PIO 0xf9
-#define READ_CONFIG_DMA 0xfd
-#define SET_CONFIG_DMA 0xfe
-#define SEND_CP_DMA 0xff
-#define ASOK 0x00
-#define ASST 0x01
-
-#define YESNO(a) ((a) ? 'y' : 'n')
-#define TLDEV(type) ((type) == TYPE_DISK || (type) == TYPE_ROM)
-
-/* "EATA", in Big Endian format */
-#define EATA_SIG_BE 0x45415441
-
-/* Number of valid bytes in the board config structure for EATA 2.0x */
-#define EATA_2_0A_SIZE 28
-#define EATA_2_0B_SIZE 30
-#define EATA_2_0C_SIZE 34
-
-/* Board info structure */
-struct eata_info {
- u_int32_t data_len; /* Number of valid bytes after this field */
- u_int32_t sign; /* ASCII "EATA" signature */
-
-#if defined(__BIG_ENDIAN_BITFIELD)
- unchar version : 4,
- : 4;
- unchar haaval : 1,
- ata : 1,
- drqvld : 1,
- dmasup : 1,
- morsup : 1,
- trnxfr : 1,
- tarsup : 1,
- ocsena : 1;
-#else
- unchar : 4, /* unused low nibble */
- version : 4; /* EATA version, should be 0x1 */
- unchar ocsena : 1, /* Overlap Command Support Enabled */
- tarsup : 1, /* Target Mode Supported */
- trnxfr : 1, /* Truncate Transfer Cmd NOT Necessary */
- morsup : 1, /* More Supported */
- dmasup : 1, /* DMA Supported */
- drqvld : 1, /* DRQ Index (DRQX) is valid */
- ata : 1, /* This is an ATA device */
- haaval : 1; /* Host Adapter Address Valid */
-#endif
-
- ushort cp_pad_len; /* Number of pad bytes after cp_len */
- unchar host_addr[4]; /* Host Adapter SCSI ID for channels 3, 2, 1, 0 */
- u_int32_t cp_len; /* Number of valid bytes in cp */
- u_int32_t sp_len; /* Number of valid bytes in sp */
- ushort queue_size; /* Max number of cp that can be queued */
- ushort unused;
- ushort scatt_size; /* Max number of entries in scatter/gather table */
-
-#if defined(__BIG_ENDIAN_BITFIELD)
- unchar drqx : 2,
- second : 1,
- irq_tr : 1,
- irq : 4;
- unchar sync;
- unchar : 4,
- res1 : 1,
- large_sg : 1,
- forcaddr : 1,
- isaena : 1;
- unchar max_chan : 3,
- max_id : 5;
- unchar max_lun;
- unchar eisa : 1,
- pci : 1,
- idquest : 1,
- m1 : 1,
- : 4;
-#else
- unchar irq : 4, /* Interrupt Request assigned to this controller */
- irq_tr : 1, /* 0 for edge triggered, 1 for level triggered */
- second : 1, /* 1 if this is a secondary (not primary) controller */
- drqx : 2; /* DRQ Index (0=DMA0, 1=DMA7, 2=DMA6, 3=DMA5) */
- unchar sync; /* 1 if scsi target id 7...0 is running sync scsi */
-
- /* Structure extension defined in EATA 2.0B */
- unchar isaena : 1, /* ISA i/o addressing is disabled/enabled */
- forcaddr : 1, /* Port address has been forced */
- large_sg : 1, /* 1 if large SG lists are supported */
- res1 : 1,
- : 4;
- unchar max_id : 5, /* Max SCSI target ID number */
- max_chan : 3; /* Max SCSI channel number on this board */
-
- /* Structure extension defined in EATA 2.0C */
- unchar max_lun; /* Max SCSI LUN number */
- unchar
- : 4,
- m1 : 1, /* This is a PCI with an M1 chip installed */
- idquest : 1, /* RAIDNUM returned is questionable */
- pci : 1, /* This board is PCI */
- eisa : 1; /* This board is EISA */
-#endif
-
- unchar raidnum; /* Uniquely identifies this HBA in a system */
- unchar notused;
-
- ushort ipad[247];
-};
-
-/* Board config structure */
-struct eata_config {
- ushort len; /* Number of bytes following this field */
-
-#if defined(__BIG_ENDIAN_BITFIELD)
- unchar : 4,
- tarena : 1,
- mdpena : 1,
- ocena : 1,
- edis : 1;
-#else
- unchar edis : 1, /* Disable EATA interface after config command */
- ocena : 1, /* Overlapped Commands Enabled */
- mdpena : 1, /* Transfer all Modified Data Pointer Messages */
- tarena : 1, /* Target Mode Enabled for this controller */
- : 4;
-#endif
- unchar cpad[511];
-};
-
-/* Returned status packet structure */
-struct mssp {
-#if defined(__BIG_ENDIAN_BITFIELD)
- unchar eoc : 1,
- adapter_status : 7;
-#else
- unchar adapter_status : 7, /* State related to current command */
- eoc : 1; /* End Of Command (1 = command completed) */
-#endif
- unchar target_status; /* SCSI status received after data transfer */
- unchar unused[2];
- u_int32_t inv_res_len; /* Number of bytes not transferred */
- u_int32_t cpp_index; /* Index of address set in cp */
- char mess[12];
-};
-
-struct sg_list {
- unsigned int address; /* Segment Address */
- unsigned int num_bytes; /* Segment Length */
-};
-
-/* MailBox SCSI Command Packet */
-struct mscp {
-#if defined(__BIG_ENDIAN_BITFIELD)
- unchar din : 1,
- dout : 1,
- interp : 1,
- : 1,
- sg : 1,
- reqsen :1,
- init : 1,
- sreset : 1;
- unchar sense_len;
- unchar unused[3];
- unchar : 7,
- fwnest : 1;
- unchar : 5,
- hbaci : 1,
- iat : 1,
- phsunit : 1;
- unchar channel : 3,
- target : 5;
- unchar one : 1,
- dispri : 1,
- luntar : 1,
- lun : 5;
-#else
- unchar sreset :1, /* SCSI Bus Reset Signal should be asserted */
- init :1, /* Re-initialize controller and self test */
- reqsen :1, /* Transfer Request Sense Data to addr using DMA */
- sg :1, /* Use Scatter/Gather */
- :1,
- interp :1, /* The controller interprets cp, not the target */
- dout :1, /* Direction of Transfer is Out (Host to Target) */
- din :1; /* Direction of Transfer is In (Target to Host) */
- unchar sense_len; /* Request Sense Length */
- unchar unused[3];
- unchar fwnest : 1, /* Send command to a component of an Array Group */
- : 7;
- unchar phsunit : 1, /* Send to Target Physical Unit (bypass RAID) */
- iat : 1, /* Inhibit Address Translation */
- hbaci : 1, /* Inhibit HBA Caching for this command */
- : 5;
- unchar target : 5, /* SCSI target ID */
- channel : 3; /* SCSI channel number */
- unchar lun : 5, /* SCSI logical unit number */
- luntar : 1, /* This cp is for Target (not LUN) */
- dispri : 1, /* Disconnect Privilege granted */
- one : 1; /* 1 */
-#endif
-
- unchar mess[3]; /* Massage to/from Target */
- unchar cdb[12]; /* Command Descriptor Block */
- u_int32_t data_len; /* If sg=0 Data Length, if sg=1 sglist length */
- u_int32_t cpp_index; /* Index of address to be returned in sp */
- u_int32_t data_address; /* If sg=0 Data Address, if sg=1 sglist address */
- u_int32_t sp_dma_addr; /* Address where sp is DMA'ed when cp completes */
- u_int32_t sense_addr; /* Address where Sense Data is DMA'ed on error */
-
- /* Additional fields begin here. */
- struct scsi_cmnd *SCpnt;
-
- /* All the cp structure is zero filled by queuecommand except the
- following CP_TAIL_SIZE bytes, initialized by detect */
- dma_addr_t cp_dma_addr; /* dma handle for this cp structure */
- struct sg_list *sglist; /* pointer to the allocated SG list */
-};
-
-#define CP_TAIL_SIZE (sizeof(struct sglist *) + sizeof(dma_addr_t))
-
-struct hostdata {
- struct mscp cp[MAX_MAILBOXES]; /* Mailboxes for this board */
- unsigned int cp_stat[MAX_MAILBOXES]; /* FREE, IN_USE, LOCKED, IN_RESET */
- unsigned int last_cp_used; /* Index of last mailbox used */
- unsigned int iocount; /* Total i/o done for this board */
- int board_number; /* Number of this board */
- char board_name[16]; /* Name of this board */
- int in_reset; /* True if board is doing a reset */
- int target_to[MAX_TARGET][MAX_CHANNEL]; /* N. of timeout errors on target */
- int target_redo[MAX_TARGET][MAX_CHANNEL]; /* If 1 redo i/o on target */
- unsigned int retries; /* Number of internal retries */
- unsigned long last_retried_pid; /* Pid of last retried command */
- unsigned char subversion; /* Bus type, either ISA or EISA/PCI */
- unsigned char protocol_rev; /* EATA 2.0 rev., 'A' or 'B' or 'C' */
- unsigned char is_pci; /* 1 is bus type is PCI */
- struct pci_dev *pdev; /* pdev for PCI bus, NULL otherwise */
- struct mssp *sp_cpu_addr; /* cpu addr for DMA buffer sp */
- dma_addr_t sp_dma_addr; /* dma handle for DMA buffer sp */
- struct mssp sp; /* Local copy of sp buffer */
-};
-
-static struct Scsi_Host *sh[MAX_BOARDS];
-static const char *driver_name = "EATA";
-static char sha[MAX_BOARDS];
-
-/* Initialize num_boards so that ihdlr can work while detect is in progress */
-static unsigned int num_boards = MAX_BOARDS;
-
-static unsigned long io_port[] = {
-
- /* Space for MAX_INT_PARAM ports usable while loading as a module */
- SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP,
- SKIP, SKIP,
-
- /* First ISA */
- 0x1f0,
-
- /* Space for MAX_PCI ports possibly reported by PCI_BIOS */
- SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP,
- SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP,
-
- /* MAX_EISA ports */
- 0x1c88, 0x2c88, 0x3c88, 0x4c88, 0x5c88, 0x6c88, 0x7c88, 0x8c88,
- 0x9c88, 0xac88, 0xbc88, 0xcc88, 0xdc88, 0xec88, 0xfc88,
-
- /* Other (MAX_ISA - 1) ports */
- 0x170, 0x230, 0x330,
-
- /* End of list */
- 0x0
-};
-
-/* Device is Big Endian */
-#define H2DEV(x) cpu_to_be32(x)
-#define DEV2H(x) be32_to_cpu(x)
-#define H2DEV16(x) cpu_to_be16(x)
-#define DEV2H16(x) be16_to_cpu(x)
-
-/* But transfer orientation from the 16 bit data register is Little Endian */
-#define REG2H(x) le16_to_cpu(x)
-
-static irqreturn_t do_interrupt_handler(int, void *);
-static void flush_dev(struct scsi_device *, unsigned long, struct hostdata *,
- unsigned int);
-static int do_trace = 0;
-static int setup_done = 0;
-static int link_statistics;
-static int ext_tran = 0;
-static int rev_scan = 1;
-
-#if defined(CONFIG_SCSI_EATA_TAGGED_QUEUE)
-static int tag_mode = TAG_SIMPLE;
-#else
-static int tag_mode = TAG_DISABLED;
-#endif
-
-#if defined(CONFIG_SCSI_EATA_LINKED_COMMANDS)
-static int linked_comm = 1;
-#else
-static int linked_comm = 0;
-#endif
-
-#if defined(CONFIG_SCSI_EATA_MAX_TAGS)
-static int max_queue_depth = CONFIG_SCSI_EATA_MAX_TAGS;
-#else
-static int max_queue_depth = MAX_CMD_PER_LUN;
-#endif
-
-#if defined(CONFIG_ISA)
-static int isa_probe = 1;
-#else
-static int isa_probe = 0;
-#endif
-
-#if defined(CONFIG_EISA)
-static int eisa_probe = 1;
-#else
-static int eisa_probe = 0;
-#endif
-
-#if defined(CONFIG_PCI)
-static int pci_probe = 1;
-#else
-static int pci_probe = 0;
-#endif
-
-#define MAX_INT_PARAM 10
-#define MAX_BOOT_OPTIONS_SIZE 256
-static char boot_options[MAX_BOOT_OPTIONS_SIZE];
-
-#if defined(MODULE)
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-
-module_param_string(eata, boot_options, MAX_BOOT_OPTIONS_SIZE, 0);
-MODULE_PARM_DESC(eata, " equivalent to the \"eata=...\" kernel boot option."
- " Example: modprobe eata \"eata=0x7410,0x230,lc:y,tm:0,mq:4,ep:n\"");
-MODULE_AUTHOR("Dario Ballabio");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("EATA/DMA SCSI Driver");
-
-#endif
-
-static int eata2x_slave_configure(struct scsi_device *dev)
-{
- int tqd, utqd;
- char *tag_suffix, *link_suffix;
-
- utqd = MAX_CMD_PER_LUN;
- tqd = max_queue_depth;
-
- if (TLDEV(dev->type) && dev->tagged_supported) {
- if (tag_mode == TAG_SIMPLE) {
- tag_suffix = ", simple tags";
- } else if (tag_mode == TAG_ORDERED) {
- tag_suffix = ", ordered tags";
- } else {
- tag_suffix = ", no tags";
- }
- scsi_change_queue_depth(dev, tqd);
- } else if (TLDEV(dev->type) && linked_comm) {
- scsi_change_queue_depth(dev, tqd);
- tag_suffix = ", untagged";
- } else {
- scsi_change_queue_depth(dev, utqd);
- tag_suffix = "";
- }
-
- if (TLDEV(dev->type) && linked_comm && dev->queue_depth > 2)
- link_suffix = ", sorted";
- else if (TLDEV(dev->type))
- link_suffix = ", unsorted";
- else
- link_suffix = "";
-
- sdev_printk(KERN_INFO, dev,
- "cmds/lun %d%s%s.\n",
- dev->queue_depth, link_suffix, tag_suffix);
-
- return 0;
-}
-
-static int wait_on_busy(unsigned long iobase, unsigned int loop)
-{
- while (inb(iobase + REG_AUX_STATUS) & ABSY_ASSERTED) {
- udelay(1L);
- if (--loop == 0)
- return 1;
- }
- return 0;
-}
-
-static int do_dma(unsigned long iobase, unsigned long addr, unchar cmd)
-{
- unsigned char *byaddr;
- unsigned long devaddr;
-
- if (wait_on_busy(iobase, (addr ? MAXLOOP * 100 : MAXLOOP)))
- return 1;
-
- if (addr) {
- devaddr = H2DEV(addr);
- byaddr = (unsigned char *)&devaddr;
- outb(byaddr[3], iobase + REG_LOW);
- outb(byaddr[2], iobase + REG_LM);
- outb(byaddr[1], iobase + REG_MID);
- outb(byaddr[0], iobase + REG_MSB);
- }
-
- outb(cmd, iobase + REG_CMD);
- return 0;
-}
-
-static int read_pio(unsigned long iobase, ushort * start, ushort * end)
-{
- unsigned int loop = MAXLOOP;
- ushort *p;
-
- for (p = start; p <= end; p++) {
- while (!(inb(iobase + REG_STATUS) & DRQ_ASSERTED)) {
- udelay(1L);
- if (--loop == 0)
- return 1;
- }
- loop = MAXLOOP;
- *p = REG2H(inw(iobase));
- }
-
- return 0;
-}
-
-static struct pci_dev *get_pci_dev(unsigned long port_base)
-{
-#if defined(CONFIG_PCI)
- unsigned int addr;
- struct pci_dev *dev = NULL;
-
- while ((dev = pci_get_class(PCI_CLASS_STORAGE_SCSI << 8, dev))) {
- addr = pci_resource_start(dev, 0);
-
-#if defined(DEBUG_PCI_DETECT)
- printk("%s: get_pci_dev, bus %d, devfn 0x%x, addr 0x%x.\n",
- driver_name, dev->bus->number, dev->devfn, addr);
-#endif
-
- /* we are in so much trouble for a pci hotplug system with this driver
- * anyway, so doing this at least lets people unload the driver and not
- * cause memory problems, but in general this is a bad thing to do (this
- * driver needs to be converted to the proper PCI api someday... */
- pci_dev_put(dev);
- if (addr + PCI_BASE_ADDRESS_0 == port_base)
- return dev;
- }
-#endif /* end CONFIG_PCI */
- return NULL;
-}
-
-static void enable_pci_ports(void)
-{
-#if defined(CONFIG_PCI)
- struct pci_dev *dev = NULL;
-
- while ((dev = pci_get_class(PCI_CLASS_STORAGE_SCSI << 8, dev))) {
-#if defined(DEBUG_PCI_DETECT)
- printk("%s: enable_pci_ports, bus %d, devfn 0x%x.\n",
- driver_name, dev->bus->number, dev->devfn);
-#endif
-
- if (pci_enable_device(dev))
- printk
- ("%s: warning, pci_enable_device failed, bus %d devfn 0x%x.\n",
- driver_name, dev->bus->number, dev->devfn);
- }
-
-#endif /* end CONFIG_PCI */
-}
-
-static int port_detect(unsigned long port_base, unsigned int j,
- struct scsi_host_template *tpnt)
-{
- unsigned char irq, dma_channel, subversion, i, is_pci = 0;
- unsigned char protocol_rev;
- struct eata_info info;
- char *bus_type, dma_name[16];
- struct pci_dev *pdev;
- /* Allowed DMA channels for ISA (0 indicates reserved) */
- unsigned char dma_channel_table[4] = { 5, 6, 7, 0 };
- struct Scsi_Host *shost;
- struct hostdata *ha;
- char name[16];
-
- sprintf(name, "%s%d", driver_name, j);
-
- if (!request_region(port_base, REGION_SIZE, driver_name)) {
-#if defined(DEBUG_DETECT)
- printk("%s: address 0x%03lx in use, skipping probe.\n", name,
- port_base);
-#endif
- goto fail;
- }
-
- if (do_dma(port_base, 0, READ_CONFIG_PIO)) {
-#if defined(DEBUG_DETECT)
- printk("%s: detect, do_dma failed at 0x%03lx.\n", name,
- port_base);
-#endif
- goto freelock;
- }
-
- /* Read the info structure */
- if (read_pio(port_base, (ushort *) & info, (ushort *) & info.ipad[0])) {
-#if defined(DEBUG_DETECT)
- printk("%s: detect, read_pio failed at 0x%03lx.\n", name,
- port_base);
-#endif
- goto freelock;
- }
-
- info.data_len = DEV2H(info.data_len);
- info.sign = DEV2H(info.sign);
- info.cp_pad_len = DEV2H16(info.cp_pad_len);
- info.cp_len = DEV2H(info.cp_len);
- info.sp_len = DEV2H(info.sp_len);
- info.scatt_size = DEV2H16(info.scatt_size);
- info.queue_size = DEV2H16(info.queue_size);
-
- /* Check the controller "EATA" signature */
- if (info.sign != EATA_SIG_BE) {
-#if defined(DEBUG_DETECT)
- printk("%s: signature 0x%04x discarded.\n", name, info.sign);
-#endif
- goto freelock;
- }
-
- if (info.data_len < EATA_2_0A_SIZE) {
- printk
- ("%s: config structure size (%d bytes) too short, detaching.\n",
- name, info.data_len);
- goto freelock;
- } else if (info.data_len == EATA_2_0A_SIZE)
- protocol_rev = 'A';
- else if (info.data_len == EATA_2_0B_SIZE)
- protocol_rev = 'B';
- else
- protocol_rev = 'C';
-
- if (protocol_rev != 'A' && info.forcaddr) {
- printk("%s: warning, port address has been forced.\n", name);
- bus_type = "PCI";
- is_pci = 1;
- subversion = ESA;
- } else if (port_base > MAX_EISA_ADDR
- || (protocol_rev == 'C' && info.pci)) {
- bus_type = "PCI";
- is_pci = 1;
- subversion = ESA;
- } else if (port_base >= MIN_EISA_ADDR
- || (protocol_rev == 'C' && info.eisa)) {
- bus_type = "EISA";
- subversion = ESA;
- } else if (protocol_rev == 'C' && !info.eisa && !info.pci) {
- bus_type = "ISA";
- subversion = ISA;
- } else if (port_base > MAX_ISA_ADDR) {
- bus_type = "PCI";
- is_pci = 1;
- subversion = ESA;
- } else {
- bus_type = "ISA";
- subversion = ISA;
- }
-
- if (!info.haaval || info.ata) {
- printk
- ("%s: address 0x%03lx, unusable %s board (%d%d), detaching.\n",
- name, port_base, bus_type, info.haaval, info.ata);
- goto freelock;
- }
-
- if (info.drqvld) {
- if (subversion == ESA)
- printk("%s: warning, weird %s board using DMA.\n", name,
- bus_type);
-
- subversion = ISA;
- dma_channel = dma_channel_table[3 - info.drqx];
- } else {
- if (subversion == ISA)
- printk("%s: warning, weird %s board not using DMA.\n",
- name, bus_type);
-
- subversion = ESA;
- dma_channel = NO_DMA;
- }
-
- if (!info.dmasup)
- printk("%s: warning, DMA protocol support not asserted.\n",
- name);
-
- irq = info.irq;
-
- if (subversion == ESA && !info.irq_tr)
- printk
- ("%s: warning, LEVEL triggering is suggested for IRQ %u.\n",
- name, irq);
-
- if (is_pci) {
- pdev = get_pci_dev(port_base);
- if (!pdev)
- printk
- ("%s: warning, failed to get pci_dev structure.\n",
- name);
- } else
- pdev = NULL;
-
- if (pdev && (irq != pdev->irq)) {
- printk("%s: IRQ %u mapped to IO-APIC IRQ %u.\n", name, irq,
- pdev->irq);
- irq = pdev->irq;
- }
-
- /* Board detected, allocate its IRQ */
- if (request_irq(irq, do_interrupt_handler,
- (subversion == ESA) ? IRQF_SHARED : 0,
- driver_name, (void *)&sha[j])) {
- printk("%s: unable to allocate IRQ %u, detaching.\n", name,
- irq);
- goto freelock;
- }
-
- if (subversion == ISA && request_dma(dma_channel, driver_name)) {
- printk("%s: unable to allocate DMA channel %u, detaching.\n",
- name, dma_channel);
- goto freeirq;
- }
-#if defined(FORCE_CONFIG)
- {
- struct eata_config *cf;
- dma_addr_t cf_dma_addr;
-
- cf = pci_zalloc_consistent(pdev, sizeof(struct eata_config),
- &cf_dma_addr);
-
- if (!cf) {
- printk
- ("%s: config, pci_alloc_consistent failed, detaching.\n",
- name);
- goto freedma;
- }
-
- /* Set board configuration */
- cf->len = (ushort) H2DEV16((ushort) 510);
- cf->ocena = 1;
-
- if (do_dma(port_base, cf_dma_addr, SET_CONFIG_DMA)) {
- printk
- ("%s: busy timeout sending configuration, detaching.\n",
- name);
- pci_free_consistent(pdev, sizeof(struct eata_config),
- cf, cf_dma_addr);
- goto freedma;
- }
-
- }
-#endif
-
- sh[j] = shost = scsi_register(tpnt, sizeof(struct hostdata));
- if (shost == NULL) {
- printk("%s: unable to register host, detaching.\n", name);
- goto freedma;
- }
-
- shost->io_port = port_base;
- shost->unique_id = port_base;
- shost->n_io_port = REGION_SIZE;
- shost->dma_channel = dma_channel;
- shost->irq = irq;
- shost->sg_tablesize = (ushort) info.scatt_size;
- shost->this_id = (ushort) info.host_addr[3];
- shost->can_queue = (ushort) info.queue_size;
- shost->cmd_per_lun = MAX_CMD_PER_LUN;
-
- ha = (struct hostdata *)shost->hostdata;
-
- memset(ha, 0, sizeof(struct hostdata));
- ha->subversion = subversion;
- ha->protocol_rev = protocol_rev;
- ha->is_pci = is_pci;
- ha->pdev = pdev;
- ha->board_number = j;
-
- if (ha->subversion == ESA)
- shost->unchecked_isa_dma = 0;
- else {
- unsigned long flags;
- shost->unchecked_isa_dma = 1;
-
- flags = claim_dma_lock();
- disable_dma(dma_channel);
- clear_dma_ff(dma_channel);
- set_dma_mode(dma_channel, DMA_MODE_CASCADE);
- enable_dma(dma_channel);
- release_dma_lock(flags);
-
- }
-
- strcpy(ha->board_name, name);
-
- /* DPT PM2012 does not allow to detect sg_tablesize correctly */
- if (shost->sg_tablesize > MAX_SGLIST || shost->sg_tablesize < 2) {
- printk("%s: detect, wrong n. of SG lists %d, fixed.\n",
- ha->board_name, shost->sg_tablesize);
- shost->sg_tablesize = MAX_SGLIST;
- }
-
- /* DPT PM2012 does not allow to detect can_queue correctly */
- if (shost->can_queue > MAX_MAILBOXES || shost->can_queue < 2) {
- printk("%s: detect, wrong n. of mbox %d, fixed.\n",
- ha->board_name, shost->can_queue);
- shost->can_queue = MAX_MAILBOXES;
- }
-
- if (protocol_rev != 'A') {
- if (info.max_chan > 0 && info.max_chan < MAX_CHANNEL)
- shost->max_channel = info.max_chan;
-
- if (info.max_id > 7 && info.max_id < MAX_TARGET)
- shost->max_id = info.max_id + 1;
-
- if (info.large_sg && shost->sg_tablesize == MAX_SGLIST)
- shost->sg_tablesize = MAX_LARGE_SGLIST;
- }
-
- if (protocol_rev == 'C') {
- if (info.max_lun > 7 && info.max_lun < MAX_LUN)
- shost->max_lun = info.max_lun + 1;
- }
-
- if (dma_channel == NO_DMA)
- sprintf(dma_name, "%s", "BMST");
- else
- sprintf(dma_name, "DMA %u", dma_channel);
-
- for (i = 0; i < shost->can_queue; i++)
- ha->cp[i].cp_dma_addr = pci_map_single(ha->pdev,
- &ha->cp[i],
- sizeof(struct mscp),
- PCI_DMA_BIDIRECTIONAL);
-
- for (i = 0; i < shost->can_queue; i++) {
- size_t sz = shost->sg_tablesize *sizeof(struct sg_list);
- gfp_t gfp_mask = (shost->unchecked_isa_dma ? GFP_DMA : 0) | GFP_ATOMIC;
- ha->cp[i].sglist = kmalloc(sz, gfp_mask);
- if (!ha->cp[i].sglist) {
- printk
- ("%s: kmalloc SGlist failed, mbox %d, detaching.\n",
- ha->board_name, i);
- goto release;
- }
- }
-
- if (!(ha->sp_cpu_addr = pci_alloc_consistent(ha->pdev,
- sizeof(struct mssp),
- &ha->sp_dma_addr))) {
- printk("%s: pci_alloc_consistent failed, detaching.\n", ha->board_name);
- goto release;
- }
-
- if (max_queue_depth > MAX_TAGGED_CMD_PER_LUN)
- max_queue_depth = MAX_TAGGED_CMD_PER_LUN;
-
- if (max_queue_depth < MAX_CMD_PER_LUN)
- max_queue_depth = MAX_CMD_PER_LUN;
-
- if (tag_mode != TAG_DISABLED && tag_mode != TAG_SIMPLE)
- tag_mode = TAG_ORDERED;
-
- if (j == 0) {
- printk
- ("EATA/DMA 2.0x: Copyright (C) 1994-2003 Dario Ballabio.\n");
- printk
- ("%s config options -> tm:%d, lc:%c, mq:%d, rs:%c, et:%c, "
- "ip:%c, ep:%c, pp:%c.\n", driver_name, tag_mode,
- YESNO(linked_comm), max_queue_depth, YESNO(rev_scan),
- YESNO(ext_tran), YESNO(isa_probe), YESNO(eisa_probe),
- YESNO(pci_probe));
- }
-
- printk("%s: 2.0%c, %s 0x%03lx, IRQ %u, %s, SG %d, MB %d.\n",
- ha->board_name, ha->protocol_rev, bus_type,
- (unsigned long)shost->io_port, shost->irq, dma_name,
- shost->sg_tablesize, shost->can_queue);
-
- if (shost->max_id > 8 || shost->max_lun > 8)
- printk
- ("%s: wide SCSI support enabled, max_id %u, max_lun %llu.\n",
- ha->board_name, shost->max_id, shost->max_lun);
-
- for (i = 0; i <= shost->max_channel; i++)
- printk("%s: SCSI channel %u enabled, host target ID %d.\n",
- ha->board_name, i, info.host_addr[3 - i]);
-
-#if defined(DEBUG_DETECT)
- printk("%s: Vers. 0x%x, ocs %u, tar %u, trnxfr %u, more %u, SYNC 0x%x, "
- "sec. %u, infol %d, cpl %d spl %d.\n", name, info.version,
- info.ocsena, info.tarsup, info.trnxfr, info.morsup, info.sync,
- info.second, info.data_len, info.cp_len, info.sp_len);
-
- if (protocol_rev == 'B' || protocol_rev == 'C')
- printk("%s: isaena %u, forcaddr %u, max_id %u, max_chan %u, "
- "large_sg %u, res1 %u.\n", name, info.isaena,
- info.forcaddr, info.max_id, info.max_chan, info.large_sg,
- info.res1);
-
- if (protocol_rev == 'C')
- printk("%s: max_lun %u, m1 %u, idquest %u, pci %u, eisa %u, "
- "raidnum %u.\n", name, info.max_lun, info.m1,
- info.idquest, info.pci, info.eisa, info.raidnum);
-#endif
-
- if (ha->pdev) {
- pci_set_master(ha->pdev);
- if (pci_set_dma_mask(ha->pdev, DMA_BIT_MASK(32)))
- printk("%s: warning, pci_set_dma_mask failed.\n",
- ha->board_name);
- }
-
- return 1;
-
- freedma:
- if (subversion == ISA)
- free_dma(dma_channel);
- freeirq:
- free_irq(irq, &sha[j]);
- freelock:
- release_region(port_base, REGION_SIZE);
- fail:
- return 0;
-
- release:
- eata2x_release(shost);
- return 0;
-}
-
-static void internal_setup(char *str, int *ints)
-{
- int i, argc = ints[0];
- char *cur = str, *pc;
-
- if (argc > 0) {
- if (argc > MAX_INT_PARAM)
- argc = MAX_INT_PARAM;
-
- for (i = 0; i < argc; i++)
- io_port[i] = ints[i + 1];
-
- io_port[i] = 0;
- setup_done = 1;
- }
-
- while (cur && (pc = strchr(cur, ':'))) {
- int val = 0, c = *++pc;
-
- if (c == 'n' || c == 'N')
- val = 0;
- else if (c == 'y' || c == 'Y')
- val = 1;
- else
- val = (int)simple_strtoul(pc, NULL, 0);
-
- if (!strncmp(cur, "lc:", 3))
- linked_comm = val;
- else if (!strncmp(cur, "tm:", 3))
- tag_mode = val;
- else if (!strncmp(cur, "tc:", 3))
- tag_mode = val;
- else if (!strncmp(cur, "mq:", 3))
- max_queue_depth = val;
- else if (!strncmp(cur, "ls:", 3))
- link_statistics = val;
- else if (!strncmp(cur, "et:", 3))
- ext_tran = val;
- else if (!strncmp(cur, "rs:", 3))
- rev_scan = val;
- else if (!strncmp(cur, "ip:", 3))
- isa_probe = val;
- else if (!strncmp(cur, "ep:", 3))
- eisa_probe = val;
- else if (!strncmp(cur, "pp:", 3))
- pci_probe = val;
-
- if ((cur = strchr(cur, ',')))
- ++cur;
- }
-
- return;
-}
-
-static int option_setup(char *str)
-{
- int ints[MAX_INT_PARAM];
- char *cur = str;
- int i = 1;
-
- while (cur && isdigit(*cur) && i < MAX_INT_PARAM) {
- ints[i++] = simple_strtoul(cur, NULL, 0);
-
- if ((cur = strchr(cur, ',')) != NULL)
- cur++;
- }
-
- ints[0] = i - 1;
- internal_setup(cur, ints);
- return 1;
-}
-
-static void add_pci_ports(void)
-{
-#if defined(CONFIG_PCI)
- unsigned int addr, k;
- struct pci_dev *dev = NULL;
-
- for (k = 0; k < MAX_PCI; k++) {
-
- if (!(dev = pci_get_class(PCI_CLASS_STORAGE_SCSI << 8, dev)))
- break;
-
- if (pci_enable_device(dev)) {
-#if defined(DEBUG_PCI_DETECT)
- printk
- ("%s: detect, bus %d, devfn 0x%x, pci_enable_device failed.\n",
- driver_name, dev->bus->number, dev->devfn);
-#endif
-
- continue;
- }
-
- addr = pci_resource_start(dev, 0);
-
-#if defined(DEBUG_PCI_DETECT)
- printk("%s: detect, seq. %d, bus %d, devfn 0x%x, addr 0x%x.\n",
- driver_name, k, dev->bus->number, dev->devfn, addr);
-#endif
-
- /* Order addresses according to rev_scan value */
- io_port[MAX_INT_PARAM + (rev_scan ? (MAX_PCI - k) : (1 + k))] =
- addr + PCI_BASE_ADDRESS_0;
- }
-
- pci_dev_put(dev);
-#endif /* end CONFIG_PCI */
-}
-
-static int eata2x_detect(struct scsi_host_template *tpnt)
-{
- unsigned int j = 0, k;
-
- tpnt->proc_name = "eata2x";
-
- if (strlen(boot_options))
- option_setup(boot_options);
-
-#if defined(MODULE)
- /* io_port could have been modified when loading as a module */
- if (io_port[0] != SKIP) {
- setup_done = 1;
- io_port[MAX_INT_PARAM] = 0;
- }
-#endif
-
- for (k = MAX_INT_PARAM; io_port[k]; k++)
- if (io_port[k] == SKIP)
- continue;
- else if (io_port[k] <= MAX_ISA_ADDR) {
- if (!isa_probe)
- io_port[k] = SKIP;
- } else if (io_port[k] >= MIN_EISA_ADDR
- && io_port[k] <= MAX_EISA_ADDR) {
- if (!eisa_probe)
- io_port[k] = SKIP;
- }
-
- if (pci_probe) {
- if (!setup_done)
- add_pci_ports();
- else
- enable_pci_ports();
- }
-
- for (k = 0; io_port[k]; k++) {
-
- if (io_port[k] == SKIP)
- continue;
-
- if (j < MAX_BOARDS && port_detect(io_port[k], j, tpnt))
- j++;
- }
-
- num_boards = j;
- return j;
-}
-
-static void map_dma(unsigned int i, struct hostdata *ha)
-{
- unsigned int k, pci_dir;
- int count;
- struct scatterlist *sg;
- struct mscp *cpp;
- struct scsi_cmnd *SCpnt;
-
- cpp = &ha->cp[i];
- SCpnt = cpp->SCpnt;
- pci_dir = SCpnt->sc_data_direction;
-
- if (SCpnt->sense_buffer)
- cpp->sense_addr =
- H2DEV(pci_map_single(ha->pdev, SCpnt->sense_buffer,
- SCSI_SENSE_BUFFERSIZE, PCI_DMA_FROMDEVICE));
-
- cpp->sense_len = SCSI_SENSE_BUFFERSIZE;
-
- if (!scsi_sg_count(SCpnt)) {
- cpp->data_len = 0;
- return;
- }
-
- count = pci_map_sg(ha->pdev, scsi_sglist(SCpnt), scsi_sg_count(SCpnt),
- pci_dir);
- BUG_ON(!count);
-
- scsi_for_each_sg(SCpnt, sg, count, k) {
- cpp->sglist[k].address = H2DEV(sg_dma_address(sg));
- cpp->sglist[k].num_bytes = H2DEV(sg_dma_len(sg));
- }
-
- cpp->sg = 1;
- cpp->data_address = H2DEV(pci_map_single(ha->pdev, cpp->sglist,
- scsi_sg_count(SCpnt) *
- sizeof(struct sg_list),
- pci_dir));
- cpp->data_len = H2DEV((scsi_sg_count(SCpnt) * sizeof(struct sg_list)));
-}
-
-static void unmap_dma(unsigned int i, struct hostdata *ha)
-{
- unsigned int pci_dir;
- struct mscp *cpp;
- struct scsi_cmnd *SCpnt;
-
- cpp = &ha->cp[i];
- SCpnt = cpp->SCpnt;
- pci_dir = SCpnt->sc_data_direction;
-
- if (DEV2H(cpp->sense_addr))
- pci_unmap_single(ha->pdev, DEV2H(cpp->sense_addr),
- DEV2H(cpp->sense_len), PCI_DMA_FROMDEVICE);
-
- if (scsi_sg_count(SCpnt))
- pci_unmap_sg(ha->pdev, scsi_sglist(SCpnt), scsi_sg_count(SCpnt),
- pci_dir);
-
- if (!DEV2H(cpp->data_len))
- pci_dir = PCI_DMA_BIDIRECTIONAL;
-
- if (DEV2H(cpp->data_address))
- pci_unmap_single(ha->pdev, DEV2H(cpp->data_address),
- DEV2H(cpp->data_len), pci_dir);
-}
-
-static void sync_dma(unsigned int i, struct hostdata *ha)
-{
- unsigned int pci_dir;
- struct mscp *cpp;
- struct scsi_cmnd *SCpnt;
-
- cpp = &ha->cp[i];
- SCpnt = cpp->SCpnt;
- pci_dir = SCpnt->sc_data_direction;
-
- if (DEV2H(cpp->sense_addr))
- pci_dma_sync_single_for_cpu(ha->pdev, DEV2H(cpp->sense_addr),
- DEV2H(cpp->sense_len),
- PCI_DMA_FROMDEVICE);
-
- if (scsi_sg_count(SCpnt))
- pci_dma_sync_sg_for_cpu(ha->pdev, scsi_sglist(SCpnt),
- scsi_sg_count(SCpnt), pci_dir);
-
- if (!DEV2H(cpp->data_len))
- pci_dir = PCI_DMA_BIDIRECTIONAL;
-
- if (DEV2H(cpp->data_address))
- pci_dma_sync_single_for_cpu(ha->pdev,
- DEV2H(cpp->data_address),
- DEV2H(cpp->data_len), pci_dir);
-}
-
-static void scsi_to_dev_dir(unsigned int i, struct hostdata *ha)
-{
- unsigned int k;
-
- static const unsigned char data_out_cmds[] = {
- 0x0a, 0x2a, 0x15, 0x55, 0x04, 0x07, 0x18, 0x1d, 0x24, 0x2e,
- 0x30, 0x31, 0x32, 0x38, 0x39, 0x3a, 0x3b, 0x3d, 0x3f, 0x40,
- 0x41, 0x4c, 0xaa, 0xae, 0xb0, 0xb1, 0xb2, 0xb6, 0xea, 0x1b, 0x5d
- };
-
- static const unsigned char data_none_cmds[] = {
- 0x01, 0x0b, 0x10, 0x11, 0x13, 0x16, 0x17, 0x19, 0x2b, 0x1e,
- 0x2c, 0xac, 0x2f, 0xaf, 0x33, 0xb3, 0x35, 0x36, 0x45, 0x47,
- 0x48, 0x49, 0xa9, 0x4b, 0xa5, 0xa6, 0xb5, 0x00
- };
-
- struct mscp *cpp;
- struct scsi_cmnd *SCpnt;
-
- cpp = &ha->cp[i];
- SCpnt = cpp->SCpnt;
-
- if (SCpnt->sc_data_direction == DMA_FROM_DEVICE) {
- cpp->din = 1;
- cpp->dout = 0;
- return;
- } else if (SCpnt->sc_data_direction == DMA_TO_DEVICE) {
- cpp->din = 0;
- cpp->dout = 1;
- return;
- } else if (SCpnt->sc_data_direction == DMA_NONE) {
- cpp->din = 0;
- cpp->dout = 0;
- return;
- }
-
- if (SCpnt->sc_data_direction != DMA_BIDIRECTIONAL)
- panic("%s: qcomm, invalid SCpnt->sc_data_direction.\n",
- ha->board_name);
-
- for (k = 0; k < ARRAY_SIZE(data_out_cmds); k++)
- if (SCpnt->cmnd[0] == data_out_cmds[k]) {
- cpp->dout = 1;
- break;
- }
-
- if ((cpp->din = !cpp->dout))
- for (k = 0; k < ARRAY_SIZE(data_none_cmds); k++)
- if (SCpnt->cmnd[0] == data_none_cmds[k]) {
- cpp->din = 0;
- break;
- }
-
-}
-
-static int eata2x_queuecommand_lck(struct scsi_cmnd *SCpnt,
- void (*done) (struct scsi_cmnd *))
-{
- struct Scsi_Host *shost = SCpnt->device->host;
- struct hostdata *ha = (struct hostdata *)shost->hostdata;
- unsigned int i, k;
- struct mscp *cpp;
-
- if (SCpnt->host_scribble)
- panic("%s: qcomm, SCpnt %p already active.\n",
- ha->board_name, SCpnt);
-
- /* i is the mailbox number, look for the first free mailbox
- starting from last_cp_used */
- i = ha->last_cp_used + 1;
-
- for (k = 0; k < shost->can_queue; k++, i++) {
- if (i >= shost->can_queue)
- i = 0;
- if (ha->cp_stat[i] == FREE) {
- ha->last_cp_used = i;
- break;
- }
- }
-
- if (k == shost->can_queue) {
- printk("%s: qcomm, no free mailbox.\n", ha->board_name);
- return 1;
- }
-
- /* Set pointer to control packet structure */
- cpp = &ha->cp[i];
-
- memset(cpp, 0, sizeof(struct mscp) - CP_TAIL_SIZE);
-
- /* Set pointer to status packet structure, Big Endian format */
- cpp->sp_dma_addr = H2DEV(ha->sp_dma_addr);
-
- SCpnt->scsi_done = done;
- cpp->cpp_index = i;
- SCpnt->host_scribble = (unsigned char *)&cpp->cpp_index;
-
- if (do_trace)
- scmd_printk(KERN_INFO, SCpnt,
- "qcomm, mbox %d.\n", i);
-
- cpp->reqsen = 1;
- cpp->dispri = 1;
-#if 0
- if (SCpnt->device->type == TYPE_TAPE)
- cpp->hbaci = 1;
-#endif
- cpp->one = 1;
- cpp->channel = SCpnt->device->channel;
- cpp->target = SCpnt->device->id;
- cpp->lun = SCpnt->device->lun;
- cpp->SCpnt = SCpnt;
- memcpy(cpp->cdb, SCpnt->cmnd, SCpnt->cmd_len);
-
- /* Use data transfer direction SCpnt->sc_data_direction */
- scsi_to_dev_dir(i, ha);
-
- /* Map DMA buffers and SG list */
- map_dma(i, ha);
-
- if (linked_comm && SCpnt->device->queue_depth > 2
- && TLDEV(SCpnt->device->type)) {
- ha->cp_stat[i] = READY;
- flush_dev(SCpnt->device, blk_rq_pos(SCpnt->request), ha, 0);
- return 0;
- }
-
- /* Send control packet to the board */
- if (do_dma(shost->io_port, cpp->cp_dma_addr, SEND_CP_DMA)) {
- unmap_dma(i, ha);
- SCpnt->host_scribble = NULL;
- scmd_printk(KERN_INFO, SCpnt, "qcomm, adapter busy.\n");
- return 1;
- }
-
- ha->cp_stat[i] = IN_USE;
- return 0;
-}
-
-static DEF_SCSI_QCMD(eata2x_queuecommand)
-
-static int eata2x_eh_abort(struct scsi_cmnd *SCarg)
-{
- struct Scsi_Host *shost = SCarg->device->host;
- struct hostdata *ha = (struct hostdata *)shost->hostdata;
- unsigned int i;
-
- if (SCarg->host_scribble == NULL) {
- scmd_printk(KERN_INFO, SCarg, "abort, cmd inactive.\n");
- return SUCCESS;
- }
-
- i = *(unsigned int *)SCarg->host_scribble;
- scmd_printk(KERN_WARNING, SCarg, "abort, mbox %d.\n", i);
-
- if (i >= shost->can_queue)
- panic("%s: abort, invalid SCarg->host_scribble.\n", ha->board_name);
-
- if (wait_on_busy(shost->io_port, MAXLOOP)) {
- printk("%s: abort, timeout error.\n", ha->board_name);
- return FAILED;
- }
-
- if (ha->cp_stat[i] == FREE) {
- printk("%s: abort, mbox %d is free.\n", ha->board_name, i);
- return SUCCESS;
- }
-
- if (ha->cp_stat[i] == IN_USE) {
- printk("%s: abort, mbox %d is in use.\n", ha->board_name, i);
-
- if (SCarg != ha->cp[i].SCpnt)
- panic("%s: abort, mbox %d, SCarg %p, cp SCpnt %p.\n",
- ha->board_name, i, SCarg, ha->cp[i].SCpnt);
-
- if (inb(shost->io_port + REG_AUX_STATUS) & IRQ_ASSERTED)
- printk("%s: abort, mbox %d, interrupt pending.\n",
- ha->board_name, i);
-
- return FAILED;
- }
-
- if (ha->cp_stat[i] == IN_RESET) {
- printk("%s: abort, mbox %d is in reset.\n", ha->board_name, i);
- return FAILED;
- }
-
- if (ha->cp_stat[i] == LOCKED) {
- printk("%s: abort, mbox %d is locked.\n", ha->board_name, i);
- return SUCCESS;
- }
-
- if (ha->cp_stat[i] == READY || ha->cp_stat[i] == ABORTING) {
- unmap_dma(i, ha);
- SCarg->result = DID_ABORT << 16;
- SCarg->host_scribble = NULL;
- ha->cp_stat[i] = FREE;
- printk("%s, abort, mbox %d ready, DID_ABORT, done.\n",
- ha->board_name, i);
- SCarg->scsi_done(SCarg);
- return SUCCESS;
- }
-
- panic("%s: abort, mbox %d, invalid cp_stat.\n", ha->board_name, i);
-}
-
-static int eata2x_eh_host_reset(struct scsi_cmnd *SCarg)
-{
- unsigned int i, time, k, c, limit = 0;
- struct scsi_cmnd *SCpnt;
- struct Scsi_Host *shost = SCarg->device->host;
- struct hostdata *ha = (struct hostdata *)shost->hostdata;
-
- scmd_printk(KERN_INFO, SCarg, "reset, enter.\n");
-
- spin_lock_irq(shost->host_lock);
-
- if (SCarg->host_scribble == NULL)
- printk("%s: reset, inactive.\n", ha->board_name);
-
- if (ha->in_reset) {
- printk("%s: reset, exit, already in reset.\n", ha->board_name);
- spin_unlock_irq(shost->host_lock);
- return FAILED;
- }
-
- if (wait_on_busy(shost->io_port, MAXLOOP)) {
- printk("%s: reset, exit, timeout error.\n", ha->board_name);
- spin_unlock_irq(shost->host_lock);
- return FAILED;
- }
-
- ha->retries = 0;
-
- for (c = 0; c <= shost->max_channel; c++)
- for (k = 0; k < shost->max_id; k++) {
- ha->target_redo[k][c] = 1;
- ha->target_to[k][c] = 0;
- }
-
- for (i = 0; i < shost->can_queue; i++) {
-
- if (ha->cp_stat[i] == FREE)
- continue;
-
- if (ha->cp_stat[i] == LOCKED) {
- ha->cp_stat[i] = FREE;
- printk("%s: reset, locked mbox %d forced free.\n",
- ha->board_name, i);
- continue;
- }
-
- if (!(SCpnt = ha->cp[i].SCpnt))
- panic("%s: reset, mbox %d, SCpnt == NULL.\n", ha->board_name, i);
-
- if (ha->cp_stat[i] == READY || ha->cp_stat[i] == ABORTING) {
- ha->cp_stat[i] = ABORTING;
- printk("%s: reset, mbox %d aborting.\n",
- ha->board_name, i);
- }
-
- else {
- ha->cp_stat[i] = IN_RESET;
- printk("%s: reset, mbox %d in reset.\n",
- ha->board_name, i);
- }
-
- if (SCpnt->host_scribble == NULL)
- panic("%s: reset, mbox %d, garbled SCpnt.\n", ha->board_name, i);
-
- if (*(unsigned int *)SCpnt->host_scribble != i)
- panic("%s: reset, mbox %d, index mismatch.\n", ha->board_name, i);
-
- if (SCpnt->scsi_done == NULL)
- panic("%s: reset, mbox %d, SCpnt->scsi_done == NULL.\n",
- ha->board_name, i);
- }
-
- if (do_dma(shost->io_port, 0, RESET_PIO)) {
- printk("%s: reset, cannot reset, timeout error.\n", ha->board_name);
- spin_unlock_irq(shost->host_lock);
- return FAILED;
- }
-
- printk("%s: reset, board reset done, enabling interrupts.\n", ha->board_name);
-
-#if defined(DEBUG_RESET)
- do_trace = 1;
-#endif
-
- ha->in_reset = 1;
-
- spin_unlock_irq(shost->host_lock);
-
- /* FIXME: use a sleep instead */
- time = jiffies;
- while ((jiffies - time) < (10 * HZ) && limit++ < 200000)
- udelay(100L);
-
- spin_lock_irq(shost->host_lock);
-
- printk("%s: reset, interrupts disabled, loops %d.\n", ha->board_name, limit);
-
- for (i = 0; i < shost->can_queue; i++) {
-
- if (ha->cp_stat[i] == IN_RESET) {
- SCpnt = ha->cp[i].SCpnt;
- unmap_dma(i, ha);
- SCpnt->result = DID_RESET << 16;
- SCpnt->host_scribble = NULL;
-
- /* This mailbox is still waiting for its interrupt */
- ha->cp_stat[i] = LOCKED;
-
- printk
- ("%s, reset, mbox %d locked, DID_RESET, done.\n",
- ha->board_name, i);
- }
-
- else if (ha->cp_stat[i] == ABORTING) {
- SCpnt = ha->cp[i].SCpnt;
- unmap_dma(i, ha);
- SCpnt->result = DID_RESET << 16;
- SCpnt->host_scribble = NULL;
-
- /* This mailbox was never queued to the adapter */
- ha->cp_stat[i] = FREE;
-
- printk
- ("%s, reset, mbox %d aborting, DID_RESET, done.\n",
- ha->board_name, i);
- }
-
- else
- /* Any other mailbox has already been set free by interrupt */
- continue;
-
- SCpnt->scsi_done(SCpnt);
- }
-
- ha->in_reset = 0;
- do_trace = 0;
-
- printk("%s: reset, exit.\n", ha->board_name);
-
- spin_unlock_irq(shost->host_lock);
- return SUCCESS;
-}
-
-int eata2x_bios_param(struct scsi_device *sdev, struct block_device *bdev,
- sector_t capacity, int *dkinfo)
-{
- unsigned int size = capacity;
-
- if (ext_tran || (scsicam_bios_param(bdev, capacity, dkinfo) < 0)) {
- dkinfo[0] = 255;
- dkinfo[1] = 63;
- dkinfo[2] = size / (dkinfo[0] * dkinfo[1]);
- }
-#if defined (DEBUG_GEOMETRY)
- printk("%s: bios_param, head=%d, sec=%d, cyl=%d.\n", driver_name,
- dkinfo[0], dkinfo[1], dkinfo[2]);
-#endif
-
- return 0;
-}
-
-static void sort(unsigned long sk[], unsigned int da[], unsigned int n,
- unsigned int rev)
-{
- unsigned int i, j, k, y;
- unsigned long x;
-
- for (i = 0; i < n - 1; i++) {
- k = i;
-
- for (j = k + 1; j < n; j++)
- if (rev) {
- if (sk[j] > sk[k])
- k = j;
- } else {
- if (sk[j] < sk[k])
- k = j;
- }
-
- if (k != i) {
- x = sk[k];
- sk[k] = sk[i];
- sk[i] = x;
- y = da[k];
- da[k] = da[i];
- da[i] = y;
- }
- }
-
- return;
-}
-
-static int reorder(struct hostdata *ha, unsigned long cursec,
- unsigned int ihdlr, unsigned int il[], unsigned int n_ready)
-{
- struct scsi_cmnd *SCpnt;
- struct mscp *cpp;
- unsigned int k, n;
- unsigned int rev = 0, s = 1, r = 1;
- unsigned int input_only = 1, overlap = 0;
- unsigned long sl[n_ready], pl[n_ready], ll[n_ready];
- unsigned long maxsec = 0, minsec = ULONG_MAX, seek = 0, iseek = 0;
- unsigned long ioseek = 0;
-
- static unsigned int flushcount = 0, batchcount = 0, sortcount = 0;
- static unsigned int readycount = 0, ovlcount = 0, inputcount = 0;
- static unsigned int readysorted = 0, revcount = 0;
- static unsigned long seeksorted = 0, seeknosort = 0;
-
- if (link_statistics && !(++flushcount % link_statistics))
- printk("fc %d bc %d ic %d oc %d rc %d rs %d sc %d re %d"
- " av %ldK as %ldK.\n", flushcount, batchcount,
- inputcount, ovlcount, readycount, readysorted, sortcount,
- revcount, seeknosort / (readycount + 1),
- seeksorted / (readycount + 1));
-
- if (n_ready <= 1)
- return 0;
-
- for (n = 0; n < n_ready; n++) {
- k = il[n];
- cpp = &ha->cp[k];
- SCpnt = cpp->SCpnt;
-
- if (!cpp->din)
- input_only = 0;
-
- if (blk_rq_pos(SCpnt->request) < minsec)
- minsec = blk_rq_pos(SCpnt->request);
- if (blk_rq_pos(SCpnt->request) > maxsec)
- maxsec = blk_rq_pos(SCpnt->request);
-
- sl[n] = blk_rq_pos(SCpnt->request);
- ioseek += blk_rq_sectors(SCpnt->request);
-
- if (!n)
- continue;
-
- if (sl[n] < sl[n - 1])
- s = 0;
- if (sl[n] > sl[n - 1])
- r = 0;
-
- if (link_statistics) {
- if (sl[n] > sl[n - 1])
- seek += sl[n] - sl[n - 1];
- else
- seek += sl[n - 1] - sl[n];
- }
-
- }
-
- if (link_statistics) {
- if (cursec > sl[0])
- seek += cursec - sl[0];
- else
- seek += sl[0] - cursec;
- }
-
- if (cursec > ((maxsec + minsec) / 2))
- rev = 1;
-
- if (ioseek > ((maxsec - minsec) / 2))
- rev = 0;
-
- if (!((rev && r) || (!rev && s)))
- sort(sl, il, n_ready, rev);
-
- if (!input_only)
- for (n = 0; n < n_ready; n++) {
- k = il[n];
- cpp = &ha->cp[k];
- SCpnt = cpp->SCpnt;
- ll[n] = blk_rq_sectors(SCpnt->request);
- pl[n] = SCpnt->serial_number;
-
- if (!n)
- continue;
-
- if ((sl[n] == sl[n - 1])
- || (!rev && ((sl[n - 1] + ll[n - 1]) > sl[n]))
- || (rev && ((sl[n] + ll[n]) > sl[n - 1])))
- overlap = 1;
- }
-
- if (overlap)
- sort(pl, il, n_ready, 0);
-
- if (link_statistics) {
- if (cursec > sl[0])
- iseek = cursec - sl[0];
- else
- iseek = sl[0] - cursec;
- batchcount++;
- readycount += n_ready;
- seeknosort += seek / 1024;
- if (input_only)
- inputcount++;
- if (overlap) {
- ovlcount++;
- seeksorted += iseek / 1024;
- } else
- seeksorted += (iseek + maxsec - minsec) / 1024;
- if (rev && !r) {
- revcount++;
- readysorted += n_ready;
- }
- if (!rev && !s) {
- sortcount++;
- readysorted += n_ready;
- }
- }
-#if defined(DEBUG_LINKED_COMMANDS)
- if (link_statistics && (overlap || !(flushcount % link_statistics)))
- for (n = 0; n < n_ready; n++) {
- k = il[n];
- cpp = &ha->cp[k];
- SCpnt = cpp->SCpnt;
- scmd_printk(KERN_INFO, SCpnt,
- "%s mb %d fc %d nr %d sec %ld ns %u"
- " cur %ld s:%c r:%c rev:%c in:%c ov:%c xd %d.\n",
- (ihdlr ? "ihdlr" : "qcomm"),
- k, flushcount,
- n_ready, blk_rq_pos(SCpnt->request),
- blk_rq_sectors(SCpnt->request), cursec, YESNO(s),
- YESNO(r), YESNO(rev), YESNO(input_only),
- YESNO(overlap), cpp->din);
- }
-#endif
- return overlap;
-}
-
-static void flush_dev(struct scsi_device *dev, unsigned long cursec,
- struct hostdata *ha, unsigned int ihdlr)
-{
- struct scsi_cmnd *SCpnt;
- struct mscp *cpp;
- unsigned int k, n, n_ready = 0, il[MAX_MAILBOXES];
-
- for (k = 0; k < dev->host->can_queue; k++) {
-
- if (ha->cp_stat[k] != READY && ha->cp_stat[k] != IN_USE)
- continue;
-
- cpp = &ha->cp[k];
- SCpnt = cpp->SCpnt;
-
- if (SCpnt->device != dev)
- continue;
-
- if (ha->cp_stat[k] == IN_USE)
- return;
-
- il[n_ready++] = k;
- }
-
- if (reorder(ha, cursec, ihdlr, il, n_ready))
- n_ready = 1;
-
- for (n = 0; n < n_ready; n++) {
- k = il[n];
- cpp = &ha->cp[k];
- SCpnt = cpp->SCpnt;
-
- if (do_dma(dev->host->io_port, cpp->cp_dma_addr, SEND_CP_DMA)) {
- scmd_printk(KERN_INFO, SCpnt,
- "%s, mbox %d, adapter"
- " busy, will abort.\n",
- (ihdlr ? "ihdlr" : "qcomm"),
- k);
- ha->cp_stat[k] = ABORTING;
- continue;
- }
-
- ha->cp_stat[k] = IN_USE;
- }
-}
-
-static irqreturn_t ihdlr(struct Scsi_Host *shost)
-{
- struct scsi_cmnd *SCpnt;
- unsigned int i, k, c, status, tstatus, reg;
- struct mssp *spp;
- struct mscp *cpp;
- struct hostdata *ha = (struct hostdata *)shost->hostdata;
- int irq = shost->irq;
-
- /* Check if this board need to be serviced */
- if (!(inb(shost->io_port + REG_AUX_STATUS) & IRQ_ASSERTED))
- goto none;
-
- ha->iocount++;
-
- if (do_trace)
- printk("%s: ihdlr, enter, irq %d, count %d.\n", ha->board_name, irq,
- ha->iocount);
-
- /* Check if this board is still busy */
- if (wait_on_busy(shost->io_port, 20 * MAXLOOP)) {
- reg = inb(shost->io_port + REG_STATUS);
- printk
- ("%s: ihdlr, busy timeout error, irq %d, reg 0x%x, count %d.\n",
- ha->board_name, irq, reg, ha->iocount);
- goto none;
- }
-
- spp = &ha->sp;
-
- /* Make a local copy just before clearing the interrupt indication */
- memcpy(spp, ha->sp_cpu_addr, sizeof(struct mssp));
-
- /* Clear the completion flag and cp pointer on the dynamic copy of sp */
- memset(ha->sp_cpu_addr, 0, sizeof(struct mssp));
-
- /* Read the status register to clear the interrupt indication */
- reg = inb(shost->io_port + REG_STATUS);
-
-#if defined (DEBUG_INTERRUPT)
- {
- unsigned char *bytesp;
- int cnt;
- bytesp = (unsigned char *)spp;
- if (ha->iocount < 200) {
- printk("sp[] =");
- for (cnt = 0; cnt < 15; cnt++)
- printk(" 0x%x", bytesp[cnt]);
- printk("\n");
- }
- }
-#endif
-
- /* Reject any sp with supspect data */
- if (spp->eoc == 0 && ha->iocount > 1)
- printk
- ("%s: ihdlr, spp->eoc == 0, irq %d, reg 0x%x, count %d.\n",
- ha->board_name, irq, reg, ha->iocount);
- if (spp->cpp_index < 0 || spp->cpp_index >= shost->can_queue)
- printk
- ("%s: ihdlr, bad spp->cpp_index %d, irq %d, reg 0x%x, count %d.\n",
- ha->board_name, spp->cpp_index, irq, reg, ha->iocount);
- if (spp->eoc == 0 || spp->cpp_index < 0
- || spp->cpp_index >= shost->can_queue)
- goto handled;
-
- /* Find the mailbox to be serviced on this board */
- i = spp->cpp_index;
-
- cpp = &(ha->cp[i]);
-
-#if defined(DEBUG_GENERATE_ABORTS)
- if ((ha->iocount > 500) && ((ha->iocount % 500) < 3))
- goto handled;
-#endif
-
- if (ha->cp_stat[i] == IGNORE) {
- ha->cp_stat[i] = FREE;
- goto handled;
- } else if (ha->cp_stat[i] == LOCKED) {
- ha->cp_stat[i] = FREE;
- printk("%s: ihdlr, mbox %d unlocked, count %d.\n", ha->board_name, i,
- ha->iocount);
- goto handled;
- } else if (ha->cp_stat[i] == FREE) {
- printk("%s: ihdlr, mbox %d is free, count %d.\n", ha->board_name, i,
- ha->iocount);
- goto handled;
- } else if (ha->cp_stat[i] == IN_RESET)
- printk("%s: ihdlr, mbox %d is in reset.\n", ha->board_name, i);
- else if (ha->cp_stat[i] != IN_USE)
- panic("%s: ihdlr, mbox %d, invalid cp_stat: %d.\n",
- ha->board_name, i, ha->cp_stat[i]);
-
- ha->cp_stat[i] = FREE;
- SCpnt = cpp->SCpnt;
-
- if (SCpnt == NULL)
- panic("%s: ihdlr, mbox %d, SCpnt == NULL.\n", ha->board_name, i);
-
- if (SCpnt->host_scribble == NULL)
- panic("%s: ihdlr, mbox %d, SCpnt %p garbled.\n", ha->board_name,
- i, SCpnt);
-
- if (*(unsigned int *)SCpnt->host_scribble != i)
- panic("%s: ihdlr, mbox %d, index mismatch %d.\n",
- ha->board_name, i,
- *(unsigned int *)SCpnt->host_scribble);
-
- sync_dma(i, ha);
-
- if (linked_comm && SCpnt->device->queue_depth > 2
- && TLDEV(SCpnt->device->type))
- flush_dev(SCpnt->device, blk_rq_pos(SCpnt->request), ha, 1);
-
- tstatus = status_byte(spp->target_status);
-
-#if defined(DEBUG_GENERATE_ERRORS)
- if ((ha->iocount > 500) && ((ha->iocount % 200) < 2))
- spp->adapter_status = 0x01;
-#endif
-
- switch (spp->adapter_status) {
- case ASOK: /* status OK */
-
- /* Forces a reset if a disk drive keeps returning BUSY */
- if (tstatus == BUSY && SCpnt->device->type != TYPE_TAPE)
- status = DID_ERROR << 16;
-
- /* If there was a bus reset, redo operation on each target */
- else if (tstatus != GOOD && SCpnt->device->type == TYPE_DISK
- && ha->target_redo[SCpnt->device->id][SCpnt->
- device->
- channel])
- status = DID_BUS_BUSY << 16;
-
- /* Works around a flaw in scsi.c */
- else if (tstatus == CHECK_CONDITION
- && SCpnt->device->type == TYPE_DISK
- && (SCpnt->sense_buffer[2] & 0xf) == RECOVERED_ERROR)
- status = DID_BUS_BUSY << 16;
-
- else
- status = DID_OK << 16;
-
- if (tstatus == GOOD)
- ha->target_redo[SCpnt->device->id][SCpnt->device->
- channel] = 0;
-
- if (spp->target_status && SCpnt->device->type == TYPE_DISK &&
- (!(tstatus == CHECK_CONDITION && ha->iocount <= 1000 &&
- (SCpnt->sense_buffer[2] & 0xf) == NOT_READY)))
- printk("%s: ihdlr, target %d.%d:%d, "
- "target_status 0x%x, sense key 0x%x.\n",
- ha->board_name,
- SCpnt->device->channel, SCpnt->device->id,
- (u8)SCpnt->device->lun,
- spp->target_status, SCpnt->sense_buffer[2]);
-
- ha->target_to[SCpnt->device->id][SCpnt->device->channel] = 0;
-
- if (ha->last_retried_pid == SCpnt->serial_number)
- ha->retries = 0;
-
- break;
- case ASST: /* Selection Time Out */
- case 0x02: /* Command Time Out */
-
- if (ha->target_to[SCpnt->device->id][SCpnt->device->channel] > 1)
- status = DID_ERROR << 16;
- else {
- status = DID_TIME_OUT << 16;
- ha->target_to[SCpnt->device->id][SCpnt->device->
- channel]++;
- }
-
- break;
-
- /* Perform a limited number of internal retries */
- case 0x03: /* SCSI Bus Reset Received */
- case 0x04: /* Initial Controller Power-up */
-
- for (c = 0; c <= shost->max_channel; c++)
- for (k = 0; k < shost->max_id; k++)
- ha->target_redo[k][c] = 1;
-
- if (SCpnt->device->type != TYPE_TAPE
- && ha->retries < MAX_INTERNAL_RETRIES) {
-
-#if defined(DID_SOFT_ERROR)
- status = DID_SOFT_ERROR << 16;
-#else
- status = DID_BUS_BUSY << 16;
-#endif
-
- ha->retries++;
- ha->last_retried_pid = SCpnt->serial_number;
- } else
- status = DID_ERROR << 16;
-
- break;
- case 0x05: /* Unexpected Bus Phase */
- case 0x06: /* Unexpected Bus Free */
- case 0x07: /* Bus Parity Error */
- case 0x08: /* SCSI Hung */
- case 0x09: /* Unexpected Message Reject */
- case 0x0a: /* SCSI Bus Reset Stuck */
- case 0x0b: /* Auto Request-Sense Failed */
- case 0x0c: /* Controller Ram Parity Error */
- default:
- status = DID_ERROR << 16;
- break;
- }
-
- SCpnt->result = status | spp->target_status;
-
-#if defined(DEBUG_INTERRUPT)
- if (SCpnt->result || do_trace)
-#else
- if ((spp->adapter_status != ASOK && ha->iocount > 1000) ||
- (spp->adapter_status != ASOK &&
- spp->adapter_status != ASST && ha->iocount <= 1000) ||
- do_trace || msg_byte(spp->target_status))
-#endif
- scmd_printk(KERN_INFO, SCpnt, "ihdlr, mbox %2d, err 0x%x:%x,"
- " reg 0x%x, count %d.\n",
- i, spp->adapter_status, spp->target_status,
- reg, ha->iocount);
-
- unmap_dma(i, ha);
-
- /* Set the command state to inactive */
- SCpnt->host_scribble = NULL;
-
- SCpnt->scsi_done(SCpnt);
-
- if (do_trace)
- printk("%s: ihdlr, exit, irq %d, count %d.\n", ha->board_name,
- irq, ha->iocount);
-
- handled:
- return IRQ_HANDLED;
- none:
- return IRQ_NONE;
-}
-
-static irqreturn_t do_interrupt_handler(int dummy, void *shap)
-{
- struct Scsi_Host *shost;
- unsigned int j;
- unsigned long spin_flags;
- irqreturn_t ret;
-
- /* Check if the interrupt must be processed by this handler */
- if ((j = (unsigned int)((char *)shap - sha)) >= num_boards)
- return IRQ_NONE;
- shost = sh[j];
-
- spin_lock_irqsave(shost->host_lock, spin_flags);
- ret = ihdlr(shost);
- spin_unlock_irqrestore(shost->host_lock, spin_flags);
- return ret;
-}
-
-static int eata2x_release(struct Scsi_Host *shost)
-{
- struct hostdata *ha = (struct hostdata *)shost->hostdata;
- unsigned int i;
-
- for (i = 0; i < shost->can_queue; i++)
- kfree((&ha->cp[i])->sglist);
-
- for (i = 0; i < shost->can_queue; i++)
- pci_unmap_single(ha->pdev, ha->cp[i].cp_dma_addr,
- sizeof(struct mscp), PCI_DMA_BIDIRECTIONAL);
-
- if (ha->sp_cpu_addr)
- pci_free_consistent(ha->pdev, sizeof(struct mssp),
- ha->sp_cpu_addr, ha->sp_dma_addr);
-
- free_irq(shost->irq, &sha[ha->board_number]);
-
- if (shost->dma_channel != NO_DMA)
- free_dma(shost->dma_channel);
-
- release_region(shost->io_port, shost->n_io_port);
- scsi_unregister(shost);
- return 0;
-}
-
-#include "scsi_module.c"
-
-#ifndef MODULE
-__setup("eata=", option_setup);
-#endif /* end MODULE */
diff --git a/drivers/scsi/eata_generic.h b/drivers/scsi/eata_generic.h
deleted file mode 100644
index 1a396c5e7f73..000000000000
--- a/drivers/scsi/eata_generic.h
+++ /dev/null
@@ -1,401 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/********************************************************
-* Header file for eata_dma.c and eata_pio.c *
-* Linux EATA SCSI drivers *
-* (c) 1993-96 Michael Neuffer *
-* mike@i-Connect.Net *
-* neuffer@mail.uni-mainz.de *
-*********************************************************
-* last change: 96/08/14 *
-********************************************************/
-
-
-#ifndef _EATA_GENERIC_H
-#define _EATA_GENERIC_H
-
-
-
-/*********************************************
- * Misc. definitions *
- *********************************************/
-
-#define R_LIMIT 0x20000
-
-#define MAXISA 4
-#define MAXEISA 16
-#define MAXPCI 16
-#define MAXIRQ 16
-#define MAXTARGET 16
-#define MAXCHANNEL 3
-
-#define IS_ISA 'I'
-#define IS_EISA 'E'
-#define IS_PCI 'P'
-
-#define BROKEN_INQUIRY 1
-
-#define BUSMASTER 0xff
-#define PIO 0xfe
-
-#define EATA_SIGNATURE 0x45415441 /* BIG ENDIAN coded "EATA" sig. */
-
-#define DPT_ID1 0x12
-#define DPT_ID2 0x14
-
-#define ATT_ID1 0x06
-#define ATT_ID2 0x94
-#define ATT_ID3 0x0
-
-#define NEC_ID1 0x38
-#define NEC_ID2 0xa3
-#define NEC_ID3 0x82
-
-
-#define EATA_CP_SIZE 44
-
-#define MAX_PCI_DEVICES 32 /* Maximum # Of Devices Per Bus */
-#define MAX_METHOD_2 16 /* Max Devices For Method 2 */
-#define MAX_PCI_BUS 16 /* Maximum # Of Busses Allowed */
-
-#define SG_SIZE 64
-#define SG_SIZE_BIG 252 /* max. 8096 elements, 64k */
-
-#define UPPER_DEVICE_QUEUE_LIMIT 64 /* The limit we have to set for the
- * device queue to keep the broken
- * midlevel SCSI code from producing
- * bogus timeouts
- */
-
-#define TYPE_DISK_QUEUE 16
-#define TYPE_TAPE_QUEUE 4
-#define TYPE_ROM_QUEUE 4
-#define TYPE_OTHER_QUEUE 2
-
-#define FREE 0
-#define OK 0
-#define NO_TIMEOUT 0
-#define USED 1
-#define TIMEOUT 2
-#define RESET 4
-#define LOCKED 8
-#define ABORTED 16
-
-#define READ 0
-#define WRITE 1
-#define OTHER 2
-
-#define HD(cmd) ((hostdata *)&(cmd->device->host->hostdata))
-#define CD(cmd) ((struct eata_ccb *)(cmd->host_scribble))
-#define SD(host) ((hostdata *)&(host->hostdata))
-
-/***********************************************
- * EATA Command & Register definitions *
- ***********************************************/
-#define PCI_REG_DPTconfig 0x40
-#define PCI_REG_PumpModeAddress 0x44
-#define PCI_REG_PumpModeData 0x48
-#define PCI_REG_ConfigParam1 0x50
-#define PCI_REG_ConfigParam2 0x54
-
-
-#define EATA_CMD_PIO_SETUPTEST 0xc6
-#define EATA_CMD_PIO_READ_CONFIG 0xf0
-#define EATA_CMD_PIO_SET_CONFIG 0xf1
-#define EATA_CMD_PIO_SEND_CP 0xf2
-#define EATA_CMD_PIO_RECEIVE_SP 0xf3
-#define EATA_CMD_PIO_TRUNC 0xf4
-
-#define EATA_CMD_RESET 0xf9
-#define EATA_CMD_IMMEDIATE 0xfa
-
-#define EATA_CMD_DMA_READ_CONFIG 0xfd
-#define EATA_CMD_DMA_SET_CONFIG 0xfe
-#define EATA_CMD_DMA_SEND_CP 0xff
-
-#define ECS_EMULATE_SENSE 0xd4
-
-#define EATA_GENERIC_ABORT 0x00
-#define EATA_SPECIFIC_RESET 0x01
-#define EATA_BUS_RESET 0x02
-#define EATA_SPECIFIC_ABORT 0x03
-#define EATA_QUIET_INTR 0x04
-#define EATA_COLD_BOOT_HBA 0x06 /* Only as a last resort */
-#define EATA_FORCE_IO 0x07
-
-#define HA_CTRLREG 0x206 /* control register for HBA */
-#define HA_CTRL_DISINT 0x02 /* CTRLREG: disable interrupts */
-#define HA_CTRL_RESCPU 0x04 /* CTRLREG: reset processor */
-#define HA_CTRL_8HEADS 0x08 /* CTRLREG: set for drives with*
- * >=8 heads (WD1003 rudimentary :-) */
-
-#define HA_WCOMMAND 0x07 /* command register offset */
-#define HA_WIFC 0x06 /* immediate command offset */
-#define HA_WCODE 0x05
-#define HA_WCODE2 0x04
-#define HA_WDMAADDR 0x02 /* DMA address LSB offset */
-#define HA_RAUXSTAT 0x08 /* aux status register offset*/
-#define HA_RSTATUS 0x07 /* status register offset */
-#define HA_RDATA 0x00 /* data register (16bit) */
-#define HA_WDATA 0x00 /* data register (16bit) */
-
-#define HA_ABUSY 0x01 /* aux busy bit */
-#define HA_AIRQ 0x02 /* aux IRQ pending bit */
-#define HA_SERROR 0x01 /* pr. command ended in error*/
-#define HA_SMORE 0x02 /* more data soon to come */
-#define HA_SCORR 0x04 /* data corrected */
-#define HA_SDRQ 0x08 /* data request active */
-#define HA_SSC 0x10 /* seek complete */
-#define HA_SFAULT 0x20 /* write fault */
-#define HA_SREADY 0x40 /* drive ready */
-#define HA_SBUSY 0x80 /* drive busy */
-#define HA_SDRDY HA_SSC+HA_SREADY+HA_SDRQ
-
-/**********************************************
- * Message definitions *
- **********************************************/
-
-#define HA_NO_ERROR 0x00 /* No Error */
-#define HA_ERR_SEL_TO 0x01 /* Selection Timeout */
-#define HA_ERR_CMD_TO 0x02 /* Command Timeout */
-#define HA_BUS_RESET 0x03 /* SCSI Bus Reset Received */
-#define HA_INIT_POWERUP 0x04 /* Initial Controller Power-up */
-#define HA_UNX_BUSPHASE 0x05 /* Unexpected Bus Phase */
-#define HA_UNX_BUS_FREE 0x06 /* Unexpected Bus Free */
-#define HA_BUS_PARITY 0x07 /* Bus Parity Error */
-#define HA_SCSI_HUNG 0x08 /* SCSI Hung */
-#define HA_UNX_MSGRJCT 0x09 /* Unexpected Message Rejected */
-#define HA_RESET_STUCK 0x0a /* SCSI Bus Reset Stuck */
-#define HA_RSENSE_FAIL 0x0b /* Auto Request-Sense Failed */
-#define HA_PARITY_ERR 0x0c /* Controller Ram Parity Error */
-#define HA_CP_ABORT_NA 0x0d /* Abort Message sent to non-active cmd */
-#define HA_CP_ABORTED 0x0e /* Abort Message sent to active cmd */
-#define HA_CP_RESET_NA 0x0f /* Reset Message sent to non-active cmd */
-#define HA_CP_RESET 0x10 /* Reset Message sent to active cmd */
-#define HA_ECC_ERR 0x11 /* Controller Ram ECC Error */
-#define HA_PCI_PARITY 0x12 /* PCI Parity Error */
-#define HA_PCI_MABORT 0x13 /* PCI Master Abort */
-#define HA_PCI_TABORT 0x14 /* PCI Target Abort */
-#define HA_PCI_STABORT 0x15 /* PCI Signaled Target Abort */
-
-/**********************************************
- * Other definitions *
- **********************************************/
-
-struct reg_bit { /* reading this one will clear the interrupt */
- __u8 error:1; /* previous command ended in an error */
- __u8 more:1; /* more DATA coming soon, poll BSY & DRQ (PIO) */
- __u8 corr:1; /* data read was successfully corrected with ECC*/
- __u8 drq:1; /* data request active */
- __u8 sc:1; /* seek complete */
- __u8 fault:1; /* write fault */
- __u8 ready:1; /* drive ready */
- __u8 busy:1; /* controller busy */
-};
-
-struct reg_abit { /* reading this won't clear the interrupt */
- __u8 abusy:1; /* auxiliary busy */
- __u8 irq:1; /* set when drive interrupt is asserted */
- __u8 dummy:6;
-};
-
-struct eata_register { /* EATA register set */
- __u8 data_reg[2]; /* R, couldn't figure this one out */
- __u8 cp_addr[4]; /* W, CP address register */
- union {
- __u8 command; /* W, command code: [read|set] conf, send CP*/
- struct reg_bit status; /* R, see register_bit1 */
- __u8 statusbyte;
- } ovr;
- struct reg_abit aux_stat; /* R, see register_bit2 */
-};
-
-struct get_conf { /* Read Configuration Array */
- __u32 len; /* Should return 0x22, 0x24, etc */
- __u32 signature; /* Signature MUST be "EATA" */
- __u8 version2:4,
- version:4; /* EATA Version level */
- __u8 OCS_enabled:1, /* Overlap Command Support enabled */
- TAR_support:1, /* SCSI Target Mode supported */
- TRNXFR:1, /* Truncate Transfer Cmd not necessary *
- * Only used in PIO Mode */
- MORE_support:1, /* MORE supported (only PIO Mode) */
- DMA_support:1, /* DMA supported Driver uses only *
- * this mode */
- DMA_valid:1, /* DRQ value in Byte 30 is valid */
- ATA:1, /* ATA device connected (not supported) */
- HAA_valid:1; /* Hostadapter Address is valid */
-
- __u16 cppadlen; /* Number of pad bytes send after CD data *
- * set to zero for DMA commands */
- __u8 scsi_id[4]; /* SCSI ID of controller 2-0 Byte 0 res. *
- * if not, zero is returned */
- __u32 cplen; /* CP length: number of valid cp bytes */
- __u32 splen; /* Number of bytes returned after *
- * Receive SP command */
- __u16 queuesiz; /* max number of queueable CPs */
- __u16 dummy;
- __u16 SGsiz; /* max number of SG table entries */
- __u8 IRQ:4, /* IRQ used this HA */
- IRQ_TR:1, /* IRQ Trigger: 0=edge, 1=level */
- SECOND:1, /* This is a secondary controller */
- DMA_channel:2; /* DRQ index, DRQ is 2comp of DRQX */
- __u8 sync; /* device at ID 7 tru 0 is running in *
- * synchronous mode, this will disappear */
- __u8 DSBLE:1, /* ISA i/o addressing is disabled */
- FORCADR:1, /* i/o address has been forced */
- SG_64K:1,
- SG_UAE:1,
- :4;
- __u8 MAX_ID:5, /* Max number of SCSI target IDs */
- MAX_CHAN:3; /* Number of SCSI busses on HBA */
- __u8 MAX_LUN; /* Max number of LUNs */
- __u8 :3,
- AUTOTRM:1,
- M1_inst:1,
- ID_qest:1, /* Raidnum ID is questionable */
- is_PCI:1, /* HBA is PCI */
- is_EISA:1; /* HBA is EISA */
- __u8 RAIDNUM; /* unique HBA identifier */
- __u8 unused[474];
-};
-
-struct eata_sg_list
-{
- __u32 data;
- __u32 len;
-};
-
-struct eata_ccb { /* Send Command Packet structure */
-
- __u8 SCSI_Reset:1, /* Cause a SCSI Bus reset on the cmd */
- HBA_Init:1, /* Cause Controller to reinitialize */
- Auto_Req_Sen:1, /* Do Auto Request Sense on errors */
- scatter:1, /* Data Ptr points to a SG Packet */
- Resrvd:1, /* RFU */
- Interpret:1, /* Interpret the SCSI cdb of own use */
- DataOut:1, /* Data Out phase with command */
- DataIn:1; /* Data In phase with command */
- __u8 reqlen; /* Request Sense Length *
- * Valid if Auto_Req_Sen=1 */
- __u8 unused[3];
- __u8 FWNEST:1, /* send cmd to phys RAID component */
- unused2:7;
- __u8 Phsunit:1, /* physical unit on mirrored pair */
- I_AT:1, /* inhibit address translation */
- I_HBA_C:1, /* HBA inhibit caching */
- unused3:5;
-
- __u8 cp_id:5, /* SCSI Device ID of target */
- cp_channel:3; /* SCSI Channel # of HBA */
- __u8 cp_lun:3,
- :2,
- cp_luntar:1, /* CP is for target ROUTINE */
- cp_dispri:1, /* Grant disconnect privilege */
- cp_identify:1; /* Always TRUE */
- __u8 cp_msg1; /* Message bytes 0-3 */
- __u8 cp_msg2;
- __u8 cp_msg3;
- __u8 cp_cdb[12]; /* Command Descriptor Block */
- __u32 cp_datalen; /* Data Transfer Length *
- * If scatter=1 len of sg package */
- void *cp_viraddr; /* address of this ccb */
- __u32 cp_dataDMA; /* Data Address, if scatter=1 *
- * address of scatter packet */
- __u32 cp_statDMA; /* address for Status Packet */
- __u32 cp_reqDMA; /* Request Sense Address, used if *
- * CP command ends with error */
- /* Additional CP info begins here */
- __u32 timestamp; /* Needed to measure command latency */
- __u32 timeout;
- __u8 sizeindex;
- __u8 rw_latency;
- __u8 retries;
- __u8 status; /* status of this queueslot */
- struct scsi_cmnd *cmd; /* address of cmd */
- struct eata_sg_list *sg_list;
-};
-
-
-struct eata_sp {
- __u8 hba_stat:7, /* HBA status */
- EOC:1; /* True if command finished */
- __u8 scsi_stat; /* Target SCSI status */
- __u8 reserved[2];
- __u32 residue_len; /* Number of bytes not transferred */
- struct eata_ccb *ccb; /* Address set in COMMAND PACKET */
- __u8 msg[12];
-};
-
-typedef struct hstd {
- __u8 vendor[9];
- __u8 name[18];
- __u8 revision[6];
- __u8 EATA_revision;
- __u32 firmware_revision;
- __u8 HBA_number;
- __u8 bustype; /* bustype of HBA */
- __u8 channel; /* # of avail. scsi channels */
- __u8 state; /* state of HBA */
- __u8 primary; /* true if primary */
- __u8 more_support:1, /* HBA supports MORE flag */
- immediate_support:1, /* HBA supports IMMEDIATE CMDs*/
- broken_INQUIRY:1; /* This is an EISA HBA with *
- * broken INQUIRY */
- __u8 do_latency; /* Latency measurement flag */
- __u32 reads[13];
- __u32 writes[13];
- __u32 reads_lat[12][4];
- __u32 writes_lat[12][4];
- __u32 all_lat[4];
- __u8 resetlevel[MAXCHANNEL];
- __u32 last_ccb; /* Last used ccb */
- __u32 cplen; /* size of CP in words */
- __u16 cppadlen; /* pad length of cp in words */
- __u16 queuesize;
- __u16 sgsize; /* # of entries in the SG list*/
- __u16 devflags; /* bits set for detected devices */
- __u8 hostid; /* SCSI ID of HBA */
- __u8 moresupport; /* HBA supports MORE flag */
- struct Scsi_Host *next;
- struct Scsi_Host *prev;
- struct pci_dev *pdev; /* PCI device or NULL for non PCI */
- struct eata_sp sp; /* status packet */
- struct eata_ccb ccb[0]; /* ccb array begins here */
-}hostdata;
-
-/* structure for max. 2 emulated drives */
-struct drive_geom_emul {
- __u8 trans; /* translation flag 1=transl */
- __u8 channel; /* SCSI channel number */
- __u8 HBA; /* HBA number (prim/sec) */
- __u8 id; /* drive id */
- __u8 lun; /* drive lun */
- __u32 heads; /* number of heads */
- __u32 sectors; /* number of sectors */
- __u32 cylinder; /* number of cylinders */
-};
-
-struct geom_emul {
- __u8 bios_drives; /* number of emulated drives */
- struct drive_geom_emul drv[2]; /* drive structures */
-};
-
-#endif /* _EATA_GENERIC_H */
-
-/*
- * Overrides for Emacs so that we almost follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 4
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -4
- * c-argdecl-indent: 4
- * c-label-offset: -4
- * c-continued-statement-offset: 4
- * c-continued-brace-offset: 0
- * tab-width: 8
- * End:
- */
diff --git a/drivers/scsi/eata_pio.c b/drivers/scsi/eata_pio.c
deleted file mode 100644
index 4299fa485622..000000000000
--- a/drivers/scsi/eata_pio.c
+++ /dev/null
@@ -1,966 +0,0 @@
-/************************************************************
- * *
- * Linux EATA SCSI PIO driver *
- * *
- * based on the CAM document CAM/89-004 rev. 2.0c, *
- * DPT's driver kit, some internal documents and source, *
- * and several other Linux scsi drivers and kernel docs. *
- * *
- * The driver currently: *
- * -supports all EATA-PIO boards *
- * -only supports DASD devices *
- * *
- * (c)1993-96 Michael Neuffer, Alfred Arnold *
- * neuffer@goofy.zdv.uni-mainz.de *
- * a.arnold@kfa-juelich.de *
- * *
- * Updated 2002 by Alan Cox <alan@lxorguk.ukuu.org.uk> for *
- * Linux 2.5.x and the newer locking and error handling *
- * *
- * 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 kernel; if not, write to *
- * the Free Software Foundation, Inc., 675 Mass Ave, *
- * Cambridge, MA 02139, USA. *
- * *
- * For the avoidance of doubt the "preferred form" of this *
- * code is one which is in an open non patent encumbered *
- * format. Where cryptographic key signing forms part of *
- * the process of creating an executable the information *
- * including keys needed to generate an equivalently *
- * functional executable are deemed to be part of the *
- * source code are deemed to be part of the source code. *
- * *
- ************************************************************
- * last change: 2002/11/02 OS: Linux 2.5.45 *
- ************************************************************/
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/pci.h>
-#include <linux/proc_fs.h>
-#include <linux/interrupt.h>
-#include <linux/blkdev.h>
-#include <linux/spinlock.h>
-#include <linux/delay.h>
-
-#include <asm/io.h>
-
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
-#include <scsi/scsi_device.h>
-#include <scsi/scsi_host.h>
-
-#include "eata_generic.h"
-#include "eata_pio.h"
-
-
-static unsigned int ISAbases[MAXISA] = {
- 0x1F0, 0x170, 0x330, 0x230
-};
-
-static unsigned int ISAirqs[MAXISA] = {
- 14, 12, 15, 11
-};
-
-static unsigned char EISAbases[] = {
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1
-};
-
-static unsigned int registered_HBAs;
-static struct Scsi_Host *last_HBA;
-static struct Scsi_Host *first_HBA;
-static unsigned char reg_IRQ[16];
-static unsigned char reg_IRQL[16];
-static unsigned long int_counter;
-static unsigned long queue_counter;
-
-static struct scsi_host_template driver_template;
-
-static int eata_pio_show_info(struct seq_file *m, struct Scsi_Host *shost)
-{
- seq_printf(m, "EATA (Extended Attachment) PIO driver version: "
- "%d.%d%s\n",VER_MAJOR, VER_MINOR, VER_SUB);
- seq_printf(m, "queued commands: %10ld\n"
- "processed interrupts:%10ld\n", queue_counter, int_counter);
- seq_printf(m, "\nscsi%-2d: HBA %.10s\n",
- shost->host_no, SD(shost)->name);
- seq_printf(m, "Firmware revision: v%s\n",
- SD(shost)->revision);
- seq_puts(m, "IO: PIO\n");
- seq_printf(m, "Base IO : %#.4x\n", (u32) shost->base);
- seq_printf(m, "Host Bus: %s\n",
- (SD(shost)->bustype == 'P')?"PCI ":
- (SD(shost)->bustype == 'E')?"EISA":"ISA ");
- return 0;
-}
-
-static int eata_pio_release(struct Scsi_Host *sh)
-{
- hostdata *hd = SD(sh);
- if (sh->irq && reg_IRQ[sh->irq] == 1)
- free_irq(sh->irq, NULL);
- else
- reg_IRQ[sh->irq]--;
- if (SD(sh)->channel == 0) {
- if (sh->io_port && sh->n_io_port)
- release_region(sh->io_port, sh->n_io_port);
- }
- /* At this point the PCI reference can go */
- if (hd->pdev)
- pci_dev_put(hd->pdev);
- return 1;
-}
-
-static void IncStat(struct scsi_pointer *SCp, unsigned int Increment)
-{
- SCp->ptr += Increment;
- if ((SCp->this_residual -= Increment) == 0) {
- if ((--SCp->buffers_residual) == 0)
- SCp->Status = 0;
- else {
- SCp->buffer++;
- SCp->ptr = sg_virt(SCp->buffer);
- SCp->this_residual = SCp->buffer->length;
- }
- }
-}
-
-static irqreturn_t eata_pio_int_handler(int irq, void *dev_id);
-
-static irqreturn_t do_eata_pio_int_handler(int irq, void *dev_id)
-{
- unsigned long flags;
- struct Scsi_Host *dev = dev_id;
- irqreturn_t ret;
-
- spin_lock_irqsave(dev->host_lock, flags);
- ret = eata_pio_int_handler(irq, dev_id);
- spin_unlock_irqrestore(dev->host_lock, flags);
- return ret;
-}
-
-static irqreturn_t eata_pio_int_handler(int irq, void *dev_id)
-{
- unsigned int eata_stat = 0xfffff;
- struct scsi_cmnd *cmd;
- hostdata *hd;
- struct eata_ccb *cp;
- unsigned long base;
- unsigned int x, z;
- struct Scsi_Host *sh;
- unsigned short zwickel = 0;
- unsigned char stat, odd;
- irqreturn_t ret = IRQ_NONE;
-
- for (x = 1, sh = first_HBA; x <= registered_HBAs; x++, sh = SD(sh)->prev)
- {
- if (sh->irq != irq)
- continue;
- if (inb(sh->base + HA_RSTATUS) & HA_SBUSY)
- continue;
-
- int_counter++;
- ret = IRQ_HANDLED;
-
- hd = SD(sh);
-
- cp = &hd->ccb[0];
- cmd = cp->cmd;
- base = cmd->device->host->base;
-
- do {
- stat = inb(base + HA_RSTATUS);
- if (stat & HA_SDRQ) {
- if (cp->DataIn) {
- z = 256;
- odd = 0;
- while ((cmd->SCp.Status) && ((z > 0) || (odd))) {
- if (odd) {
- *(cmd->SCp.ptr) = zwickel >> 8;
- IncStat(&cmd->SCp, 1);
- odd = 0;
- }
- x = min_t(unsigned int, z, cmd->SCp.this_residual / 2);
- insw(base + HA_RDATA, cmd->SCp.ptr, x);
- z -= x;
- IncStat(&cmd->SCp, 2 * x);
- if ((z > 0) && (cmd->SCp.this_residual == 1)) {
- zwickel = inw(base + HA_RDATA);
- *(cmd->SCp.ptr) = zwickel & 0xff;
- IncStat(&cmd->SCp, 1);
- z--;
- odd = 1;
- }
- }
- while (z > 0) {
- zwickel = inw(base + HA_RDATA);
- z--;
- }
- } else { /* cp->DataOut */
-
- odd = 0;
- z = 256;
- while ((cmd->SCp.Status) && ((z > 0) || (odd))) {
- if (odd) {
- zwickel += *(cmd->SCp.ptr) << 8;
- IncStat(&cmd->SCp, 1);
- outw(zwickel, base + HA_RDATA);
- z--;
- odd = 0;
- }
- x = min_t(unsigned int, z, cmd->SCp.this_residual / 2);
- outsw(base + HA_RDATA, cmd->SCp.ptr, x);
- z -= x;
- IncStat(&cmd->SCp, 2 * x);
- if ((z > 0) && (cmd->SCp.this_residual == 1)) {
- zwickel = *(cmd->SCp.ptr);
- zwickel &= 0xff;
- IncStat(&cmd->SCp, 1);
- odd = 1;
- }
- }
- while (z > 0 || odd) {
- outw(zwickel, base + HA_RDATA);
- z--;
- odd = 0;
- }
- }
- }
- }
- while ((stat & HA_SDRQ) || ((stat & HA_SMORE) && hd->moresupport));
-
- /* terminate handler if HBA goes busy again, i.e. transfers
- * more data */
-
- if (stat & HA_SBUSY)
- break;
-
- /* OK, this is quite stupid, but I haven't found any correct
- * way to get HBA&SCSI status so far */
-
- if (!(inb(base + HA_RSTATUS) & HA_SERROR)) {
- cmd->result = (DID_OK << 16);
- hd->devflags |= (1 << cp->cp_id);
- } else if (hd->devflags & (1 << cp->cp_id))
- cmd->result = (DID_OK << 16) + 0x02;
- else
- cmd->result = (DID_NO_CONNECT << 16);
-
- if (cp->status == LOCKED) {
- cp->status = FREE;
- eata_stat = inb(base + HA_RSTATUS);
- printk(KERN_CRIT "eata_pio: int_handler, freeing locked " "queueslot\n");
- return ret;
- }
-#if DBG_INTR2
- if (stat != 0x50)
- printk(KERN_DEBUG "stat: %#.2x, result: %#.8x\n", stat, cmd->result);
-#endif
-
- cp->status = FREE; /* now we can release the slot */
-
- cmd->scsi_done(cmd);
- }
-
- return ret;
-}
-
-static inline unsigned int eata_pio_send_command(unsigned long base, unsigned char command)
-{
- unsigned int loop = 50;
-
- while (inb(base + HA_RSTATUS) & HA_SBUSY)
- if (--loop == 0)
- return 1;
-
- /* Enable interrupts for HBA. It is not the best way to do it at this
- * place, but I hope that it doesn't interfere with the IDE driver
- * initialization this way */
-
- outb(HA_CTRL_8HEADS, base + HA_CTRLREG);
-
- outb(command, base + HA_WCOMMAND);
- return 0;
-}
-
-static int eata_pio_queue_lck(struct scsi_cmnd *cmd,
- void (*done)(struct scsi_cmnd *))
-{
- unsigned int x, y;
- unsigned long base;
-
- hostdata *hd;
- struct Scsi_Host *sh;
- struct eata_ccb *cp;
-
- queue_counter++;
-
- hd = HD(cmd);
- sh = cmd->device->host;
- base = sh->base;
-
- /* use only slot 0, as 2001 can handle only one cmd at a time */
-
- y = x = 0;
-
- if (hd->ccb[y].status != FREE) {
-
- DBG(DBG_QUEUE, printk(KERN_EMERG "can_queue %d, x %d, y %d\n", sh->can_queue, x, y));
-#if DEBUG_EATA
- panic(KERN_EMERG "eata_pio: run out of queue slots cmdno:%ld " "intrno: %ld\n", queue_counter, int_counter);
-#else
- panic(KERN_EMERG "eata_pio: run out of queue slots....\n");
-#endif
- }
-
- cp = &hd->ccb[y];
-
- memset(cp, 0, sizeof(struct eata_ccb));
-
- cp->status = USED; /* claim free slot */
-
- DBG(DBG_QUEUE, scmd_printk(KERN_DEBUG, cmd,
- "eata_pio_queue 0x%p, y %d\n", cmd, y));
-
- cmd->scsi_done = (void *) done;
-
- if (cmd->sc_data_direction == DMA_TO_DEVICE)
- cp->DataOut = 1; /* Output mode */
- else
- cp->DataIn = 0; /* Input mode */
-
- cp->Interpret = (cmd->device->id == hd->hostid);
- cp->cp_datalen = cpu_to_be32(scsi_bufflen(cmd));
- cp->Auto_Req_Sen = 0;
- cp->cp_reqDMA = 0;
- cp->reqlen = 0;
-
- cp->cp_id = cmd->device->id;
- cp->cp_lun = cmd->device->lun;
- cp->cp_dispri = 0;
- cp->cp_identify = 1;
- memcpy(cp->cp_cdb, cmd->cmnd, COMMAND_SIZE(*cmd->cmnd));
-
- cp->cp_statDMA = 0;
-
- cp->cp_viraddr = cp;
- cp->cmd = cmd;
- cmd->host_scribble = (char *) &hd->ccb[y];
-
- if (!scsi_bufflen(cmd)) {
- cmd->SCp.buffers_residual = 1;
- cmd->SCp.ptr = NULL;
- cmd->SCp.this_residual = 0;
- cmd->SCp.buffer = NULL;
- } else {
- cmd->SCp.buffer = scsi_sglist(cmd);
- cmd->SCp.buffers_residual = scsi_sg_count(cmd);
- cmd->SCp.ptr = sg_virt(cmd->SCp.buffer);
- cmd->SCp.this_residual = cmd->SCp.buffer->length;
- }
- cmd->SCp.Status = (cmd->SCp.this_residual != 0); /* TRUE as long as bytes
- * are to transfer */
-
- if (eata_pio_send_command(base, EATA_CMD_PIO_SEND_CP)) {
- cmd->result = DID_BUS_BUSY << 16;
- scmd_printk(KERN_NOTICE, cmd,
- "eata_pio_queue pid 0x%p, HBA busy, "
- "returning DID_BUS_BUSY, done.\n", cmd);
- done(cmd);
- cp->status = FREE;
- return 0;
- }
- /* FIXME: timeout */
- while (!(inb(base + HA_RSTATUS) & HA_SDRQ))
- cpu_relax();
- outsw(base + HA_RDATA, cp, hd->cplen);
- outb(EATA_CMD_PIO_TRUNC, base + HA_WCOMMAND);
- for (x = 0; x < hd->cppadlen; x++)
- outw(0, base + HA_RDATA);
-
- DBG(DBG_QUEUE, scmd_printk(KERN_DEBUG, cmd,
- "Queued base %#.4lx cmd: 0x%p "
- "slot %d irq %d\n", sh->base, cmd, y, sh->irq));
-
- return 0;
-}
-
-static DEF_SCSI_QCMD(eata_pio_queue)
-
-static int eata_pio_abort(struct scsi_cmnd *cmd)
-{
- unsigned int loop = 100;
-
- DBG(DBG_ABNORM, scmd_printk(KERN_WARNING, cmd,
- "eata_pio_abort called pid: 0x%p\n", cmd));
-
- while (inb(cmd->device->host->base + HA_RAUXSTAT) & HA_ABUSY)
- if (--loop == 0) {
- printk(KERN_WARNING "eata_pio: abort, timeout error.\n");
- return FAILED;
- }
- if (CD(cmd)->status == FREE) {
- DBG(DBG_ABNORM, printk(KERN_WARNING "Returning: SCSI_ABORT_NOT_RUNNING\n"));
- return FAILED;
- }
- if (CD(cmd)->status == USED) {
- DBG(DBG_ABNORM, printk(KERN_WARNING "Returning: SCSI_ABORT_BUSY\n"));
- /* We want to sleep a bit more here */
- return FAILED; /* SNOOZE */
- }
- if (CD(cmd)->status == RESET) {
- printk(KERN_WARNING "eata_pio: abort, command reset error.\n");
- return FAILED;
- }
- if (CD(cmd)->status == LOCKED) {
- DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio: abort, queue slot " "locked.\n"));
- return FAILED;
- }
- panic("eata_pio: abort: invalid slot status\n");
-}
-
-static int eata_pio_host_reset(struct scsi_cmnd *cmd)
-{
- unsigned int x, limit = 0;
- unsigned char success = 0;
- struct scsi_cmnd *sp;
- struct Scsi_Host *host = cmd->device->host;
-
- DBG(DBG_ABNORM, scmd_printk(KERN_WARNING, cmd,
- "eata_pio_reset called\n"));
-
- spin_lock_irq(host->host_lock);
-
- if (HD(cmd)->state == RESET) {
- printk(KERN_WARNING "eata_pio_reset: exit, already in reset.\n");
- spin_unlock_irq(host->host_lock);
- return FAILED;
- }
-
- /* force all slots to be free */
-
- for (x = 0; x < cmd->device->host->can_queue; x++) {
-
- if (HD(cmd)->ccb[x].status == FREE)
- continue;
-
- sp = HD(cmd)->ccb[x].cmd;
- HD(cmd)->ccb[x].status = RESET;
- printk(KERN_WARNING "eata_pio_reset: slot %d in reset.\n", x);
-
- if (sp == NULL)
- panic("eata_pio_reset: slot %d, sp==NULL.\n", x);
- }
-
- /* hard reset the HBA */
- outb(EATA_CMD_RESET, cmd->device->host->base + HA_WCOMMAND);
-
- DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: board reset done.\n"));
- HD(cmd)->state = RESET;
-
- spin_unlock_irq(host->host_lock);
- msleep(3000);
- spin_lock_irq(host->host_lock);
-
- DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: interrupts disabled, " "loops %d.\n", limit));
-
- for (x = 0; x < cmd->device->host->can_queue; x++) {
-
- /* Skip slots already set free by interrupt */
- if (HD(cmd)->ccb[x].status != RESET)
- continue;
-
- sp = HD(cmd)->ccb[x].cmd;
- sp->result = DID_RESET << 16;
-
- /* This mailbox is terminated */
- printk(KERN_WARNING "eata_pio_reset: reset ccb %d.\n", x);
- HD(cmd)->ccb[x].status = FREE;
-
- sp->scsi_done(sp);
- }
-
- HD(cmd)->state = 0;
-
- spin_unlock_irq(host->host_lock);
-
- if (success) { /* hmmm... */
- DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: exit, success.\n"));
- return SUCCESS;
- } else {
- DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: exit, wakeup.\n"));
- return FAILED;
- }
-}
-
-static char *get_pio_board_data(unsigned long base, unsigned int irq, unsigned int id, unsigned long cplen, unsigned short cppadlen)
-{
- struct eata_ccb cp;
- static char buff[256];
- int z;
-
- memset(&cp, 0, sizeof(struct eata_ccb));
- memset(buff, 0, sizeof(buff));
-
- cp.DataIn = 1;
- cp.Interpret = 1; /* Interpret command */
-
- cp.cp_datalen = cpu_to_be32(254);
- cp.cp_dataDMA = cpu_to_be32(0);
-
- cp.cp_id = id;
- cp.cp_lun = 0;
-
- cp.cp_cdb[0] = INQUIRY;
- cp.cp_cdb[1] = 0;
- cp.cp_cdb[2] = 0;
- cp.cp_cdb[3] = 0;
- cp.cp_cdb[4] = 254;
- cp.cp_cdb[5] = 0;
-
- if (eata_pio_send_command(base, EATA_CMD_PIO_SEND_CP))
- return NULL;
-
- while (!(inb(base + HA_RSTATUS) & HA_SDRQ))
- cpu_relax();
-
- outsw(base + HA_RDATA, &cp, cplen);
- outb(EATA_CMD_PIO_TRUNC, base + HA_WCOMMAND);
- for (z = 0; z < cppadlen; z++)
- outw(0, base + HA_RDATA);
-
- while (inb(base + HA_RSTATUS) & HA_SBUSY)
- cpu_relax();
-
- if (inb(base + HA_RSTATUS) & HA_SERROR)
- return NULL;
- else if (!(inb(base + HA_RSTATUS) & HA_SDRQ))
- return NULL;
- else {
- insw(base + HA_RDATA, &buff, 127);
- while (inb(base + HA_RSTATUS) & HA_SDRQ)
- inw(base + HA_RDATA);
- return buff;
- }
-}
-
-static int get_pio_conf_PIO(unsigned long base, struct get_conf *buf)
-{
- unsigned long loop = HZ / 2;
- int z;
- unsigned short *p;
-
- if (!request_region(base, 9, "eata_pio"))
- return 0;
-
- memset(buf, 0, sizeof(struct get_conf));
-
- while (inb(base + HA_RSTATUS) & HA_SBUSY)
- if (--loop == 0)
- goto fail;
-
- DBG(DBG_PIO && DBG_PROBE, printk(KERN_DEBUG "Issuing PIO READ CONFIG to HBA at %#lx\n", base));
- eata_pio_send_command(base, EATA_CMD_PIO_READ_CONFIG);
-
- loop = 50;
- for (p = (unsigned short *) buf; (long) p <= ((long) buf + (sizeof(struct get_conf) / 2)); p++) {
- while (!(inb(base + HA_RSTATUS) & HA_SDRQ))
- if (--loop == 0)
- goto fail;
-
- loop = 50;
- *p = inw(base + HA_RDATA);
- }
- if (inb(base + HA_RSTATUS) & HA_SERROR) {
- DBG(DBG_PROBE, printk("eata_dma: get_conf_PIO, error during "
- "transfer for HBA at %lx\n", base));
- goto fail;
- }
-
- if (cpu_to_be32(EATA_SIGNATURE) != buf->signature)
- goto fail;
-
- DBG(DBG_PIO && DBG_PROBE, printk(KERN_NOTICE "EATA Controller found "
- "at %#4lx EATA Level: %x\n",
- base, (unsigned int) (buf->version)));
-
- while (inb(base + HA_RSTATUS) & HA_SDRQ)
- inw(base + HA_RDATA);
-
- if (!ALLOW_DMA_BOARDS) {
- for (z = 0; z < MAXISA; z++)
- if (base == ISAbases[z]) {
- buf->IRQ = ISAirqs[z];
- break;
- }
- }
-
- return 1;
-
- fail:
- release_region(base, 9);
- return 0;
-}
-
-static void print_pio_config(struct get_conf *gc)
-{
- printk("Please check values: (read config data)\n");
- printk("LEN: %d ver:%d OCS:%d TAR:%d TRNXFR:%d MORES:%d\n", be32_to_cpu(gc->len), gc->version, gc->OCS_enabled, gc->TAR_support, gc->TRNXFR, gc->MORE_support);
- printk("HAAV:%d SCSIID0:%d ID1:%d ID2:%d QUEUE:%d SG:%d SEC:%d\n", gc->HAA_valid, gc->scsi_id[3], gc->scsi_id[2], gc->scsi_id[1], be16_to_cpu(gc->queuesiz), be16_to_cpu(gc->SGsiz), gc->SECOND);
- printk("IRQ:%d IRQT:%d FORCADR:%d MCH:%d RIDQ:%d\n", gc->IRQ, gc->IRQ_TR, gc->FORCADR, gc->MAX_CHAN, gc->ID_qest);
-}
-
-static unsigned int print_selftest(unsigned int base)
-{
- unsigned char buffer[512];
-#ifdef VERBOSE_SETUP
- int z;
-#endif
-
- printk("eata_pio: executing controller self test & setup...\n");
- while (inb(base + HA_RSTATUS) & HA_SBUSY);
- outb(EATA_CMD_PIO_SETUPTEST, base + HA_WCOMMAND);
- do {
- while (inb(base + HA_RSTATUS) & HA_SBUSY)
- /* nothing */ ;
- if (inb(base + HA_RSTATUS) & HA_SDRQ) {
- insw(base + HA_RDATA, &buffer, 256);
-#ifdef VERBOSE_SETUP
- /* no beeps please... */
- for (z = 0; z < 511 && buffer[z]; z++)
- if (buffer[z] != 7)
- printk("%c", buffer[z]);
-#endif
- }
- } while (inb(base + HA_RSTATUS) & (HA_SBUSY | HA_SDRQ));
-
- return (!(inb(base + HA_RSTATUS) & HA_SERROR));
-}
-
-static int register_pio_HBA(long base, struct get_conf *gc, struct pci_dev *pdev)
-{
- unsigned long size = 0;
- char *buff;
- unsigned long cplen;
- unsigned short cppadlen;
- struct Scsi_Host *sh;
- hostdata *hd;
-
- DBG(DBG_REGISTER, print_pio_config(gc));
-
- if (gc->DMA_support) {
- printk("HBA at %#.4lx supports DMA. Please use EATA-DMA driver.\n", base);
- if (!ALLOW_DMA_BOARDS)
- return 0;
- }
-
- if ((buff = get_pio_board_data(base, gc->IRQ, gc->scsi_id[3], cplen = (cpu_to_be32(gc->cplen) + 1) / 2, cppadlen = (cpu_to_be16(gc->cppadlen) + 1) / 2)) == NULL) {
- printk("HBA at %#lx didn't react on INQUIRY. Sorry.\n", base);
- return 0;
- }
-
- if (!print_selftest(base) && !ALLOW_DMA_BOARDS) {
- printk("HBA at %#lx failed while performing self test & setup.\n", base);
- return 0;
- }
-
- size = sizeof(hostdata) + (sizeof(struct eata_ccb) * be16_to_cpu(gc->queuesiz));
-
- sh = scsi_register(&driver_template, size);
- if (sh == NULL)
- return 0;
-
- if (!reg_IRQ[gc->IRQ]) { /* Interrupt already registered ? */
- if (!request_irq(gc->IRQ, do_eata_pio_int_handler, 0, "EATA-PIO", sh)) {
- reg_IRQ[gc->IRQ]++;
- if (!gc->IRQ_TR)
- reg_IRQL[gc->IRQ] = 1; /* IRQ is edge triggered */
- } else {
- printk("Couldn't allocate IRQ %d, Sorry.\n", gc->IRQ);
- return 0;
- }
- } else { /* More than one HBA on this IRQ */
- if (reg_IRQL[gc->IRQ]) {
- printk("Can't support more than one HBA on this IRQ,\n" " if the IRQ is edge triggered. Sorry.\n");
- return 0;
- } else
- reg_IRQ[gc->IRQ]++;
- }
-
- hd = SD(sh);
-
- memset(hd->ccb, 0, (sizeof(struct eata_ccb) * be16_to_cpu(gc->queuesiz)));
- memset(hd->reads, 0, sizeof(hd->reads));
-
- strlcpy(SD(sh)->vendor, &buff[8], sizeof(SD(sh)->vendor));
- strlcpy(SD(sh)->name, &buff[16], sizeof(SD(sh)->name));
- SD(sh)->revision[0] = buff[32];
- SD(sh)->revision[1] = buff[33];
- SD(sh)->revision[2] = buff[34];
- SD(sh)->revision[3] = '.';
- SD(sh)->revision[4] = buff[35];
- SD(sh)->revision[5] = 0;
-
- switch (be32_to_cpu(gc->len)) {
- case 0x1c:
- SD(sh)->EATA_revision = 'a';
- break;
- case 0x1e:
- SD(sh)->EATA_revision = 'b';
- break;
- case 0x22:
- SD(sh)->EATA_revision = 'c';
- break;
- case 0x24:
- SD(sh)->EATA_revision = 'z';
- break;
- default:
- SD(sh)->EATA_revision = '?';
- }
-
- if (be32_to_cpu(gc->len) >= 0x22) {
- if (gc->is_PCI)
- hd->bustype = IS_PCI;
- else if (gc->is_EISA)
- hd->bustype = IS_EISA;
- else
- hd->bustype = IS_ISA;
- } else {
- if (buff[21] == '4')
- hd->bustype = IS_PCI;
- else if (buff[21] == '2')
- hd->bustype = IS_EISA;
- else
- hd->bustype = IS_ISA;
- }
-
- SD(sh)->cplen = cplen;
- SD(sh)->cppadlen = cppadlen;
- SD(sh)->hostid = gc->scsi_id[3];
- SD(sh)->devflags = 1 << gc->scsi_id[3];
- SD(sh)->moresupport = gc->MORE_support;
- sh->unique_id = base;
- sh->base = base;
- sh->io_port = base;
- sh->n_io_port = 9;
- sh->irq = gc->IRQ;
- sh->dma_channel = PIO;
- sh->this_id = gc->scsi_id[3];
- sh->can_queue = 1;
- sh->cmd_per_lun = 1;
- sh->sg_tablesize = SG_ALL;
-
- hd->channel = 0;
-
- hd->pdev = pci_dev_get(pdev); /* Keep a PCI reference */
-
- sh->max_id = 8;
- sh->max_lun = 8;
-
- if (gc->SECOND)
- hd->primary = 0;
- else
- hd->primary = 1;
-
- hd->next = NULL; /* build a linked list of all HBAs */
- hd->prev = last_HBA;
- if (hd->prev != NULL)
- SD(hd->prev)->next = sh;
- last_HBA = sh;
- if (first_HBA == NULL)
- first_HBA = sh;
- registered_HBAs++;
- return (1);
-}
-
-static void find_pio_ISA(struct get_conf *buf)
-{
- int i;
-
- for (i = 0; i < MAXISA; i++) {
- if (!ISAbases[i])
- continue;
- if (!get_pio_conf_PIO(ISAbases[i], buf))
- continue;
- if (!register_pio_HBA(ISAbases[i], buf, NULL))
- release_region(ISAbases[i], 9);
- else
- ISAbases[i] = 0;
- }
- return;
-}
-
-static void find_pio_EISA(struct get_conf *buf)
-{
- u32 base;
- int i;
-
-#ifdef CHECKPAL
- u8 pal1, pal2, pal3;
-#endif
-
- for (i = 0; i < MAXEISA; i++) {
- if (EISAbases[i]) { /* Still a possibility ? */
-
- base = 0x1c88 + (i * 0x1000);
-#ifdef CHECKPAL
- pal1 = inb((u16) base - 8);
- pal2 = inb((u16) base - 7);
- pal3 = inb((u16) base - 6);
-
- if (((pal1 == 0x12) && (pal2 == 0x14)) || ((pal1 == 0x38) && (pal2 == 0xa3) && (pal3 == 0x82)) || ((pal1 == 0x06) && (pal2 == 0x94) && (pal3 == 0x24))) {
- DBG(DBG_PROBE, printk(KERN_NOTICE "EISA EATA id tags found: " "%x %x %x \n", (int) pal1, (int) pal2, (int) pal3));
-#endif
- if (get_pio_conf_PIO(base, buf)) {
- DBG(DBG_PROBE && DBG_EISA, print_pio_config(buf));
- if (buf->IRQ) {
- if (!register_pio_HBA(base, buf, NULL))
- release_region(base, 9);
- } else {
- printk(KERN_NOTICE "eata_dma: No valid IRQ. HBA " "removed from list\n");
- release_region(base, 9);
- }
- }
- /* Nothing found here so we take it from the list */
- EISAbases[i] = 0;
-#ifdef CHECKPAL
- }
-#endif
- }
- }
- return;
-}
-
-static void find_pio_PCI(struct get_conf *buf)
-{
-#ifndef CONFIG_PCI
- printk("eata_dma: kernel PCI support not enabled. Skipping scan for PCI HBAs.\n");
-#else
- struct pci_dev *dev = NULL;
- unsigned long base, x;
-
- while ((dev = pci_get_device(PCI_VENDOR_ID_DPT, PCI_DEVICE_ID_DPT, dev)) != NULL) {
- DBG(DBG_PROBE && DBG_PCI, printk("eata_pio: find_PCI, HBA at %s\n", pci_name(dev)));
- if (pci_enable_device(dev))
- continue;
- pci_set_master(dev);
- base = pci_resource_flags(dev, 0);
- if (base & IORESOURCE_MEM) {
- printk("eata_pio: invalid base address of device %s\n", pci_name(dev));
- continue;
- }
- base = pci_resource_start(dev, 0);
- /* EISA tag there ? */
- if ((inb(base) == 0x12) && (inb(base + 1) == 0x14))
- continue; /* Jep, it's forced, so move on */
- base += 0x10; /* Now, THIS is the real address */
- if (base != 0x1f8) {
- /* We didn't find it in the primary search */
- if (get_pio_conf_PIO(base, buf)) {
- if (buf->FORCADR) { /* If the address is forced */
- release_region(base, 9);
- continue; /* we'll find it later */
- }
-
- /* OK. We made it till here, so we can go now
- * and register it. We only have to check and
- * eventually remove it from the EISA and ISA list
- */
-
- if (!register_pio_HBA(base, buf, dev)) {
- release_region(base, 9);
- continue;
- }
-
- if (base < 0x1000) {
- for (x = 0; x < MAXISA; ++x) {
- if (ISAbases[x] == base) {
- ISAbases[x] = 0;
- break;
- }
- }
- } else if ((base & 0x0fff) == 0x0c88) {
- x = (base >> 12) & 0x0f;
- EISAbases[x] = 0;
- }
- }
-#ifdef CHECK_BLINK
- else if (check_blink_state(base)) {
- printk("eata_pio: HBA is in BLINK state.\n" "Consult your HBAs manual to correct this.\n");
- }
-#endif
- }
- }
-#endif /* #ifndef CONFIG_PCI */
-}
-
-static int eata_pio_detect(struct scsi_host_template *tpnt)
-{
- struct Scsi_Host *HBA_ptr;
- struct get_conf gc;
- int i;
-
- find_pio_PCI(&gc);
- find_pio_EISA(&gc);
- find_pio_ISA(&gc);
-
- for (i = 0; i < MAXIRQ; i++)
- if (reg_IRQ[i])
- request_irq(i, do_eata_pio_int_handler, 0, "EATA-PIO", NULL);
-
- HBA_ptr = first_HBA;
-
- if (registered_HBAs != 0) {
- printk("EATA (Extended Attachment) PIO driver version: %d.%d%s\n"
- "(c) 1993-95 Michael Neuffer, neuffer@goofy.zdv.uni-mainz.de\n" " Alfred Arnold, a.arnold@kfa-juelich.de\n" "This release only supports DASD devices (harddisks)\n", VER_MAJOR, VER_MINOR, VER_SUB);
-
- printk("Registered HBAs:\n");
- printk("HBA no. Boardtype: Revis: EATA: Bus: BaseIO: IRQ: Ch: ID: Pr:" " QS: SG: CPL:\n");
- for (i = 1; i <= registered_HBAs; i++) {
- printk("scsi%-2d: %.10s v%s 2.0%c %s %#.4lx %2d %d %d %c"
- " %2d %2d %2d\n",
- HBA_ptr->host_no, SD(HBA_ptr)->name, SD(HBA_ptr)->revision,
- SD(HBA_ptr)->EATA_revision, (SD(HBA_ptr)->bustype == 'P') ?
- "PCI " : (SD(HBA_ptr)->bustype == 'E') ? "EISA" : "ISA ",
- HBA_ptr->base, HBA_ptr->irq, SD(HBA_ptr)->channel, HBA_ptr->this_id,
- SD(HBA_ptr)->primary ? 'Y' : 'N', HBA_ptr->can_queue,
- HBA_ptr->sg_tablesize, HBA_ptr->cmd_per_lun);
- HBA_ptr = SD(HBA_ptr)->next;
- }
- }
- return (registered_HBAs);
-}
-
-static struct scsi_host_template driver_template = {
- .proc_name = "eata_pio",
- .name = "EATA (Extended Attachment) PIO driver",
- .show_info = eata_pio_show_info,
- .detect = eata_pio_detect,
- .release = eata_pio_release,
- .queuecommand = eata_pio_queue,
- .eh_abort_handler = eata_pio_abort,
- .eh_host_reset_handler = eata_pio_host_reset,
- .use_clustering = ENABLE_CLUSTERING,
-};
-
-MODULE_AUTHOR("Michael Neuffer, Alfred Arnold");
-MODULE_DESCRIPTION("EATA SCSI PIO driver");
-MODULE_LICENSE("GPL");
-
-#include "scsi_module.c"
diff --git a/drivers/scsi/eata_pio.h b/drivers/scsi/eata_pio.h
deleted file mode 100644
index 5b5e3d13670b..000000000000
--- a/drivers/scsi/eata_pio.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/********************************************************
-* Header file for eata_pio.c Linux EATA-PIO SCSI driver *
-* (c) 1993-96 Michael Neuffer *
-*********************************************************
-* last change: 2002/11/02 *
-********************************************************/
-
-
-#ifndef _EATA_PIO_H
-#define _EATA_PIO_H
-
-#define VER_MAJOR 0
-#define VER_MINOR 0
-#define VER_SUB "1b"
-
-/************************************************************************
- * Here you can switch parts of the code on and of *
- ************************************************************************/
-
-#define VERBOSE_SETUP /* show startup screen of 2001 */
-#define ALLOW_DMA_BOARDS 1
-
-/************************************************************************
- * Debug options. *
- * Enable DEBUG and whichever options you require. *
- ************************************************************************/
-#define DEBUG_EATA 1 /* Enable debug code. */
-#define DPT_DEBUG 0 /* Bobs special */
-#define DBG_DELAY 0 /* Build in delays so debug messages can be
- * be read before they vanish of the top of
- * the screen!
- */
-#define DBG_PROBE 0 /* Debug probe routines. */
-#define DBG_ISA 0 /* Trace ISA routines */
-#define DBG_EISA 0 /* Trace EISA routines */
-#define DBG_PCI 0 /* Trace PCI routines */
-#define DBG_PIO 0 /* Trace get_config_PIO */
-#define DBG_COM 0 /* Trace command call */
-#define DBG_QUEUE 0 /* Trace command queueing. */
-#define DBG_INTR 0 /* Trace interrupt service routine. */
-#define DBG_INTR2 0 /* Trace interrupt service routine. */
-#define DBG_PROC 0 /* Debug proc-fs related statistics */
-#define DBG_PROC_WRITE 0
-#define DBG_REGISTER 0 /* */
-#define DBG_ABNORM 1 /* Debug abnormal actions (reset, abort) */
-
-#if DEBUG_EATA
-#define DBG(x, y) if ((x)) {y;}
-#else
-#define DBG(x, y)
-#endif
-
-#endif /* _EATA_PIO_H */
diff --git a/drivers/scsi/esas2r/esas2r.h b/drivers/scsi/esas2r/esas2r.h
index 1da6407ee142..858c3b33db78 100644
--- a/drivers/scsi/esas2r/esas2r.h
+++ b/drivers/scsi/esas2r/esas2r.h
@@ -962,7 +962,6 @@ struct esas2r_adapter {
* Function Declarations
* SCSI functions
*/
-int esas2r_release(struct Scsi_Host *);
const char *esas2r_info(struct Scsi_Host *);
int esas2r_write_params(struct esas2r_adapter *a, struct esas2r_request *rq,
struct esas2r_sas_nvram *data);
@@ -984,7 +983,6 @@ int esas2r_target_reset(struct scsi_cmnd *cmd);
/* Internal functions */
int esas2r_init_adapter(struct Scsi_Host *host, struct pci_dev *pcid,
int index);
-int esas2r_cleanup(struct Scsi_Host *host);
int esas2r_read_fw(struct esas2r_adapter *a, char *buf, long off, int count);
int esas2r_write_fw(struct esas2r_adapter *a, const char *buf, long off,
int count);
diff --git a/drivers/scsi/esas2r/esas2r_init.c b/drivers/scsi/esas2r/esas2r_init.c
index 5b14dd29b764..9dffcb28c9b7 100644
--- a/drivers/scsi/esas2r/esas2r_init.c
+++ b/drivers/scsi/esas2r/esas2r_init.c
@@ -661,27 +661,6 @@ void esas2r_kill_adapter(int i)
}
}
-int esas2r_cleanup(struct Scsi_Host *host)
-{
- struct esas2r_adapter *a;
- int index;
-
- if (host == NULL) {
- int i;
-
- esas2r_debug("esas2r_cleanup everything");
- for (i = 0; i < MAX_ADAPTERS; i++)
- esas2r_kill_adapter(i);
- return -1;
- }
-
- esas2r_debug("esas2r_cleanup called for host %p", host);
- a = (struct esas2r_adapter *)host->hostdata;
- index = a->index;
- esas2r_kill_adapter(index);
- return index;
-}
-
int esas2r_suspend(struct pci_dev *pdev, pm_message_t state)
{
struct Scsi_Host *host = pci_get_drvdata(pdev);
diff --git a/drivers/scsi/esas2r/esas2r_main.c b/drivers/scsi/esas2r/esas2r_main.c
index 4eb14301a497..e07eac5be087 100644
--- a/drivers/scsi/esas2r/esas2r_main.c
+++ b/drivers/scsi/esas2r/esas2r_main.c
@@ -235,7 +235,6 @@ static struct scsi_host_template driver_template = {
.module = THIS_MODULE,
.show_info = esas2r_show_info,
.name = ESAS2R_LONGNAME,
- .release = esas2r_release,
.info = esas2r_info,
.ioctl = esas2r_ioctl,
.queuecommand = esas2r_queuecommand,
@@ -520,44 +519,16 @@ static int esas2r_probe(struct pci_dev *pcid,
static void esas2r_remove(struct pci_dev *pdev)
{
- struct Scsi_Host *host;
- int index;
-
- if (pdev == NULL) {
- esas2r_log(ESAS2R_LOG_WARN, "esas2r_remove pdev==NULL");
- return;
- }
-
- host = pci_get_drvdata(pdev);
-
- if (host == NULL) {
- /*
- * this can happen if pci_set_drvdata was already called
- * to clear the host pointer. if this is the case, we
- * are okay; this channel has already been cleaned up.
- */
-
- return;
- }
+ struct Scsi_Host *host = pci_get_drvdata(pdev);
+ struct esas2r_adapter *a = (struct esas2r_adapter *)host->hostdata;
esas2r_log_dev(ESAS2R_LOG_INFO, &(pdev->dev),
"esas2r_remove(%p) called; "
"host:%p", pdev,
host);
- index = esas2r_cleanup(host);
-
- if (index < 0)
- esas2r_log_dev(ESAS2R_LOG_WARN, &(pdev->dev),
- "unknown host in %s",
- __func__);
-
+ esas2r_kill_adapter(a->index);
found_adapters--;
-
- /* if this was the last adapter, clean up the rest of the driver */
-
- if (found_adapters == 0)
- esas2r_cleanup(NULL);
}
static int __init esas2r_init(void)
@@ -638,30 +609,7 @@ static int __init esas2r_init(void)
for (i = 0; i < MAX_ADAPTERS; i++)
esas2r_adapters[i] = NULL;
- /* initialize */
-
- driver_template.module = THIS_MODULE;
-
- if (pci_register_driver(&esas2r_pci_driver) != 0)
- esas2r_log(ESAS2R_LOG_CRIT, "pci_register_driver FAILED");
- else
- esas2r_log(ESAS2R_LOG_INFO, "pci_register_driver() OK");
-
- if (!found_adapters) {
- pci_unregister_driver(&esas2r_pci_driver);
- esas2r_cleanup(NULL);
-
- esas2r_log(ESAS2R_LOG_CRIT,
- "driver will not be loaded because no ATTO "
- "%s devices were found",
- ESAS2R_DRVR_NAME);
- return -1;
- } else {
- esas2r_log(ESAS2R_LOG_INFO, "found %d adapters",
- found_adapters);
- }
-
- return 0;
+ return pci_register_driver(&esas2r_pci_driver);
}
/* Handle ioctl calls to "/proc/scsi/esas2r/ATTOnode" */
@@ -753,18 +701,6 @@ int esas2r_show_info(struct seq_file *m, struct Scsi_Host *sh)
}
-int esas2r_release(struct Scsi_Host *sh)
-{
- esas2r_log_dev(ESAS2R_LOG_INFO, &(sh->shost_gendev),
- "esas2r_release() called");
-
- esas2r_cleanup(sh);
- if (sh->irq)
- free_irq(sh->irq, NULL);
- scsi_unregister(sh);
- return 0;
-}
-
const char *esas2r_info(struct Scsi_Host *sh)
{
struct esas2r_adapter *a = (struct esas2r_adapter *)sh->hostdata;
diff --git a/drivers/scsi/fdomain.c b/drivers/scsi/fdomain.c
deleted file mode 100644
index ebbe5a3e665d..000000000000
--- a/drivers/scsi/fdomain.c
+++ /dev/null
@@ -1,1783 +0,0 @@
-/* fdomain.c -- Future Domain TMC-16x0 SCSI driver
- * Created: Sun May 3 18:53:19 1992 by faith@cs.unc.edu
- * Revised: Mon Dec 28 21:59:02 1998 by faith@acm.org
- * Author: Rickard E. Faith, faith@cs.unc.edu
- * Copyright 1992-1996, 1998 Rickard E. Faith (faith@acm.org)
- * Shared IRQ supported added 7/7/2001 Alan Cox <alan@lxorguk.ukuu.org.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.
-
- **************************************************************************
-
- SUMMARY:
-
- Future Domain BIOS versions supported for autodetect:
- 2.0, 3.0, 3.2, 3.4 (1.0), 3.5 (2.0), 3.6, 3.61
- Chips are supported:
- TMC-1800, TMC-18C50, TMC-18C30, TMC-36C70
- Boards supported:
- Future Domain TMC-1650, TMC-1660, TMC-1670, TMC-1680, TMC-1610M/MER/MEX
- Future Domain TMC-3260 (PCI)
- Quantum ISA-200S, ISA-250MG
- Adaptec AHA-2920A (PCI) [BUT *NOT* AHA-2920C -- use aic7xxx instead]
- IBM ?
- LILO/INSMOD command-line options:
- fdomain=<PORT_BASE>,<IRQ>[,<ADAPTER_ID>]
-
-
-
- NOTE:
-
- The Adaptec AHA-2920C has an Adaptec AIC-7850 chip on it.
- Use the aic7xxx driver for this board.
-
- The Adaptec AHA-2920A has a Future Domain chip on it, so this is the right
- driver for that card. Unfortunately, the boxes will probably just say
- "2920", so you'll have to look on the card for a Future Domain logo, or a
- letter after the 2920.
-
-
-
- THANKS:
-
- Thanks to Adaptec for providing PCI boards for testing. This finally
- enabled me to test the PCI detection and correct it for PCI boards that do
- not have a BIOS at a standard ISA location. For PCI boards, LILO/INSMOD
- command-line options should no longer be needed. --RF 18Nov98
-
-
-
- DESCRIPTION:
-
- This is the Linux low-level SCSI driver for Future Domain TMC-1660/1680
- TMC-1650/1670, and TMC-3260 SCSI host adapters. The 1650 and 1670 have a
- 25-pin external connector, whereas the 1660 and 1680 have a SCSI-2 50-pin
- high-density external connector. The 1670 and 1680 have floppy disk
- controllers built in. The TMC-3260 is a PCI bus card.
-
- Future Domain's older boards are based on the TMC-1800 chip, and this
- driver was originally written for a TMC-1680 board with the TMC-1800 chip.
- More recently, boards are being produced with the TMC-18C50 and TMC-18C30
- chips. The latest and greatest board may not work with this driver. If
- you have to patch this driver so that it will recognize your board's BIOS
- signature, then the driver may fail to function after the board is
- detected.
-
- Please note that the drive ordering that Future Domain implemented in BIOS
- versions 3.4 and 3.5 is the opposite of the order (currently) used by the
- rest of the SCSI industry. If you have BIOS version 3.4 or 3.5, and have
- more than one drive, then the drive ordering will be the reverse of that
- which you see under DOS. For example, under DOS SCSI ID 0 will be D: and
- SCSI ID 1 will be C: (the boot device). Under Linux, SCSI ID 0 will be
- /dev/sda and SCSI ID 1 will be /dev/sdb. The Linux ordering is consistent
- with that provided by all the other SCSI drivers for Linux. If you want
- this changed, you will probably have to patch the higher level SCSI code.
- If you do so, please send me patches that are protected by #ifdefs.
-
- If you have a TMC-8xx or TMC-9xx board, then this is not the driver for
- your board. Please refer to the Seagate driver for more information and
- possible support.
-
-
-
- HISTORY:
-
- Linux Driver Driver
- Version Version Date Support/Notes
-
- 0.0 3 May 1992 V2.0 BIOS; 1800 chip
- 0.97 1.9 28 Jul 1992
- 0.98.6 3.1 27 Nov 1992
- 0.99 3.2 9 Dec 1992
-
- 0.99.3 3.3 10 Jan 1993 V3.0 BIOS
- 0.99.5 3.5 18 Feb 1993
- 0.99.10 3.6 15 May 1993 V3.2 BIOS; 18C50 chip
- 0.99.11 3.17 3 Jul 1993 (now under RCS)
- 0.99.12 3.18 13 Aug 1993
- 0.99.14 5.6 31 Oct 1993 (reselection code removed)
-
- 0.99.15 5.9 23 Jan 1994 V3.4 BIOS (preliminary)
- 1.0.8/1.1.1 5.15 1 Apr 1994 V3.4 BIOS; 18C30 chip (preliminary)
- 1.0.9/1.1.3 5.16 7 Apr 1994 V3.4 BIOS; 18C30 chip
- 1.1.38 5.18 30 Jul 1994 36C70 chip (PCI version of 18C30)
- 1.1.62 5.20 2 Nov 1994 V3.5 BIOS
- 1.1.73 5.22 7 Dec 1994 Quantum ISA-200S board; V2.0 BIOS
-
- 1.1.82 5.26 14 Jan 1995 V3.5 BIOS; TMC-1610M/MER/MEX board
- 1.2.10 5.28 5 Jun 1995 Quantum ISA-250MG board; V2.0, V2.01 BIOS
- 1.3.4 5.31 23 Jun 1995 PCI BIOS-32 detection (preliminary)
- 1.3.7 5.33 4 Jul 1995 PCI BIOS-32 detection
- 1.3.28 5.36 17 Sep 1995 V3.61 BIOS; LILO command-line support
- 1.3.34 5.39 12 Oct 1995 V3.60 BIOS; /proc
- 1.3.72 5.39 8 Feb 1996 Adaptec AHA-2920 board
- 1.3.85 5.41 4 Apr 1996
- 2.0.12 5.44 8 Aug 1996 Use ID 7 for all PCI cards
- 2.1.1 5.45 2 Oct 1996 Update ROM accesses for 2.1.x
- 2.1.97 5.46 23 Apr 1998 Rewritten PCI detection routines [mj]
- 2.1.11x 5.47 9 Aug 1998 Touched for 8 SCSI disk majors support
- 5.48 18 Nov 1998 BIOS no longer needed for PCI detection
- 2.2.0 5.50 28 Dec 1998 Support insmod parameters
-
-
- REFERENCES USED:
-
- "TMC-1800 SCSI Chip Specification (FDC-1800T)", Future Domain Corporation,
- 1990.
-
- "Technical Reference Manual: 18C50 SCSI Host Adapter Chip", Future Domain
- Corporation, January 1992.
-
- "LXT SCSI Products: Specifications and OEM Technical Manual (Revision
- B/September 1991)", Maxtor Corporation, 1991.
-
- "7213S product Manual (Revision P3)", Maxtor Corporation, 1992.
-
- "Draft Proposed American National Standard: Small Computer System
- Interface - 2 (SCSI-2)", Global Engineering Documents. (X3T9.2/86-109,
- revision 10h, October 17, 1991)
-
- Private communications, Drew Eckhardt (drew@cs.colorado.edu) and Eric
- Youngdale (ericy@cais.com), 1992.
-
- Private communication, Tuong Le (Future Domain Engineering department),
- 1994. (Disk geometry computations for Future Domain BIOS version 3.4, and
- TMC-18C30 detection.)
-
- Hogan, Thom. The Programmer's PC Sourcebook. Microsoft Press, 1988. Page
- 60 (2.39: Disk Partition Table Layout).
-
- "18C30 Technical Reference Manual", Future Domain Corporation, 1993, page
- 6-1.
-
-
-
- NOTES ON REFERENCES:
-
- The Maxtor manuals were free. Maxtor telephone technical support is
- great!
-
- The Future Domain manuals were $25 and $35. They document the chip, not
- the TMC-16x0 boards, so some information I had to guess at. In 1992,
- Future Domain sold DOS BIOS source for $250 and the UN*X driver source was
- $750, but these required a non-disclosure agreement, so even if I could
- have afforded them, they would *not* have been useful for writing this
- publicly distributable driver. Future Domain technical support has
- provided some information on the phone and have sent a few useful FAXs.
- They have been much more helpful since they started to recognize that the
- word "Linux" refers to an operating system :-).
-
-
-
- ALPHA TESTERS:
-
- There are many other alpha testers that come and go as the driver
- develops. The people listed here were most helpful in times of greatest
- need (mostly early on -- I've probably left out a few worthy people in
- more recent times):
-
- Todd Carrico (todd@wutc.wustl.edu), Dan Poirier (poirier@cs.unc.edu ), Ken
- Corey (kenc@sol.acs.unt.edu), C. de Bruin (bruin@bruin@sterbbs.nl), Sakari
- Aaltonen (sakaria@vipunen.hit.fi), John Rice (rice@xanth.cs.odu.edu), Brad
- Yearwood (brad@optilink.com), and Ray Toy (toy@soho.crd.ge.com).
-
- Special thanks to Tien-Wan Yang (twyang@cs.uh.edu), who graciously lent me
- his 18C50-based card for debugging. He is the sole reason that this
- driver works with the 18C50 chip.
-
- Thanks to Dave Newman (dnewman@crl.com) for providing initial patches for
- the version 3.4 BIOS.
-
- Thanks to James T. McKinley (mckinley@msupa.pa.msu.edu) for providing
- patches that support the TMC-3260, a PCI bus card with the 36C70 chip.
- The 36C70 chip appears to be "completely compatible" with the 18C30 chip.
-
- Thanks to Eric Kasten (tigger@petroglyph.cl.msu.edu) for providing the
- patch for the version 3.5 BIOS.
-
- Thanks for Stephen Henson (shenson@nyx10.cs.du.edu) for providing the
- patch for the Quantum ISA-200S SCSI adapter.
-
- Thanks to Adam Bowen for the signature to the 1610M/MER/MEX scsi cards, to
- Martin Andrews (andrewm@ccfadm.eeg.ccf.org) for the signature to some
- random TMC-1680 repackaged by IBM; and to Mintak Ng (mintak@panix.com) for
- the version 3.61 BIOS signature.
-
- Thanks for Mark Singer (elf@netcom.com) and Richard Simpson
- (rsimpson@ewrcsdra.demon.co.uk) for more Quantum signatures and detective
- work on the Quantum RAM layout.
-
- Special thanks to James T. McKinley (mckinley@msupa.pa.msu.edu) for
- providing patches for proper PCI BIOS32-mediated detection of the TMC-3260
- card (a PCI bus card with the 36C70 chip). Please send James PCI-related
- bug reports.
-
- Thanks to Tom Cavin (tec@usa1.com) for preliminary command-line option
- patches.
-
- New PCI detection code written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>
-
- Insmod parameter code based on patches from Daniel Graham
- <graham@balance.uoregon.edu>.
-
- All of the alpha testers deserve much thanks.
-
-
-
- NOTES ON USER DEFINABLE OPTIONS:
-
- DEBUG: This turns on the printing of various debug information.
-
- ENABLE_PARITY: This turns on SCSI parity checking. With the current
- driver, all attached devices must support SCSI parity. If none of your
- devices support parity, then you can probably get the driver to work by
- turning this option off. I have no way of testing this, however, and it
- would appear that no one ever uses this option.
-
- FIFO_COUNT: The host adapter has an 8K cache (host adapters based on the
- 18C30 chip have a 2k cache). When this many 512 byte blocks are filled by
- the SCSI device, an interrupt will be raised. Therefore, this could be as
- low as 0, or as high as 16. Note, however, that values which are too high
- or too low seem to prevent any interrupts from occurring, and thereby lock
- up the machine. I have found that 2 is a good number, but throughput may
- be increased by changing this value to values which are close to 2.
- Please let me know if you try any different values.
-
- RESELECTION: This is no longer an option, since I gave up trying to
- implement it in version 4.x of this driver. It did not improve
- performance at all and made the driver unstable (because I never found one
- of the two race conditions which were introduced by the multiple
- outstanding command code). The instability seems a very high price to pay
- just so that you don't have to wait for the tape to rewind. If you want
- this feature implemented, send me patches. I'll be happy to send a copy
- of my (broken) driver to anyone who would like to see a copy.
-
- **************************************************************************/
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/blkdev.h>
-#include <linux/spinlock.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/ioport.h>
-#include <linux/proc_fs.h>
-#include <linux/pci.h>
-#include <linux/stat.h>
-#include <linux/delay.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-#include <scsi/scsicam.h>
-
-
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
-#include <scsi/scsi_device.h>
-#include <scsi/scsi_host.h>
-#include <scsi/scsi_ioctl.h>
-#include "fdomain.h"
-
-#ifndef PCMCIA
-MODULE_AUTHOR("Rickard E. Faith");
-MODULE_DESCRIPTION("Future domain SCSI driver");
-MODULE_LICENSE("GPL");
-#endif
-
-
-#define VERSION "$Revision: 5.51 $"
-
-/* START OF USER DEFINABLE OPTIONS */
-
-#define DEBUG 0 /* Enable debugging output */
-#define ENABLE_PARITY 1 /* Enable SCSI Parity */
-#define FIFO_COUNT 2 /* Number of 512 byte blocks before INTR */
-
-/* END OF USER DEFINABLE OPTIONS */
-
-#if DEBUG
-#define EVERY_ACCESS 0 /* Write a line on every scsi access */
-#define ERRORS_ONLY 1 /* Only write a line if there is an error */
-#define DEBUG_DETECT 0 /* Debug fdomain_16x0_detect() */
-#define DEBUG_MESSAGES 1 /* Debug MESSAGE IN phase */
-#define DEBUG_ABORT 1 /* Debug abort() routine */
-#define DEBUG_RESET 1 /* Debug reset() routine */
-#define DEBUG_RACE 1 /* Debug interrupt-driven race condition */
-#else
-#define EVERY_ACCESS 0 /* LEAVE THESE ALONE--CHANGE THE ONES ABOVE */
-#define ERRORS_ONLY 0
-#define DEBUG_DETECT 0
-#define DEBUG_MESSAGES 0
-#define DEBUG_ABORT 0
-#define DEBUG_RESET 0
-#define DEBUG_RACE 0
-#endif
-
-/* Errors are reported on the line, so we don't need to report them again */
-#if EVERY_ACCESS
-#undef ERRORS_ONLY
-#define ERRORS_ONLY 0
-#endif
-
-#if ENABLE_PARITY
-#define PARITY_MASK 0x08
-#else
-#define PARITY_MASK 0x00
-#endif
-
-enum chip_type {
- unknown = 0x00,
- tmc1800 = 0x01,
- tmc18c50 = 0x02,
- tmc18c30 = 0x03,
-};
-
-enum {
- in_arbitration = 0x02,
- in_selection = 0x04,
- in_other = 0x08,
- disconnect = 0x10,
- aborted = 0x20,
- sent_ident = 0x40,
-};
-
-enum in_port_type {
- Read_SCSI_Data = 0,
- SCSI_Status = 1,
- TMC_Status = 2,
- FIFO_Status = 3, /* tmc18c50/tmc18c30 only */
- Interrupt_Cond = 4, /* tmc18c50/tmc18c30 only */
- LSB_ID_Code = 5,
- MSB_ID_Code = 6,
- Read_Loopback = 7,
- SCSI_Data_NoACK = 8,
- Interrupt_Status = 9,
- Configuration1 = 10,
- Configuration2 = 11, /* tmc18c50/tmc18c30 only */
- Read_FIFO = 12,
- FIFO_Data_Count = 14
-};
-
-enum out_port_type {
- Write_SCSI_Data = 0,
- SCSI_Cntl = 1,
- Interrupt_Cntl = 2,
- SCSI_Mode_Cntl = 3,
- TMC_Cntl = 4,
- Memory_Cntl = 5, /* tmc18c50/tmc18c30 only */
- Write_Loopback = 7,
- IO_Control = 11, /* tmc18c30 only */
- Write_FIFO = 12
-};
-
-/* .bss will zero all the static variables below */
-static int port_base;
-static unsigned long bios_base;
-static void __iomem * bios_mem;
-static int bios_major;
-static int bios_minor;
-static int PCI_bus;
-#ifdef CONFIG_PCI
-static struct pci_dev *PCI_dev;
-#endif
-static int Quantum; /* Quantum board variant */
-static int interrupt_level;
-static volatile int in_command;
-static struct scsi_cmnd *current_SC;
-static enum chip_type chip = unknown;
-static int adapter_mask;
-static int this_id;
-static int setup_called;
-
-#if DEBUG_RACE
-static volatile int in_interrupt_flag;
-#endif
-
-static int FIFO_Size = 0x2000; /* 8k FIFO for
- pre-tmc18c30 chips */
-
-static irqreturn_t do_fdomain_16x0_intr( int irq, void *dev_id );
-/* Allow insmod parameters to be like LILO parameters. For example:
- insmod fdomain fdomain=0x140,11 */
-static char * fdomain = NULL;
-module_param(fdomain, charp, 0);
-
-#ifndef PCMCIA
-
-static unsigned long addresses[] = {
- 0xc8000,
- 0xca000,
- 0xce000,
- 0xde000,
- 0xcc000, /* Extra addresses for PCI boards */
- 0xd0000,
- 0xe0000,
-};
-#define ADDRESS_COUNT ARRAY_SIZE(addresses)
-
-static unsigned short ports[] = { 0x140, 0x150, 0x160, 0x170 };
-#define PORT_COUNT ARRAY_SIZE(ports)
-
-static unsigned short ints[] = { 3, 5, 10, 11, 12, 14, 15, 0 };
-
-#endif /* !PCMCIA */
-
-/*
-
- READ THIS BEFORE YOU ADD A SIGNATURE!
-
- READING THIS SHORT NOTE CAN SAVE YOU LOTS OF TIME!
-
- READ EVERY WORD, ESPECIALLY THE WORD *NOT*
-
- This driver works *ONLY* for Future Domain cards using the TMC-1800,
- TMC-18C50, or TMC-18C30 chip. This includes models TMC-1650, 1660, 1670,
- and 1680. These are all 16-bit cards.
-
- The following BIOS signature signatures are for boards which do *NOT*
- work with this driver (these TMC-8xx and TMC-9xx boards may work with the
- Seagate driver):
-
- FUTURE DOMAIN CORP. (C) 1986-1988 V4.0I 03/16/88
- FUTURE DOMAIN CORP. (C) 1986-1989 V5.0C2/14/89
- FUTURE DOMAIN CORP. (C) 1986-1989 V6.0A7/28/89
- FUTURE DOMAIN CORP. (C) 1986-1990 V6.0105/31/90
- FUTURE DOMAIN CORP. (C) 1986-1990 V6.0209/18/90
- FUTURE DOMAIN CORP. (C) 1986-1990 V7.009/18/90
- FUTURE DOMAIN CORP. (C) 1992 V8.00.004/02/92
-
- (The cards which do *NOT* work are all 8-bit cards -- although some of
- them have a 16-bit form-factor, the upper 8-bits are used only for IRQs
- and are *NOT* used for data. You can tell the difference by following
- the tracings on the circuit board -- if only the IRQ lines are involved,
- you have a "8-bit" card, and should *NOT* use this driver.)
-
-*/
-
-#ifndef PCMCIA
-
-static struct signature {
- const char *signature;
- int sig_offset;
- int sig_length;
- int major_bios_version;
- int minor_bios_version;
- int flag; /* 1 == PCI_bus, 2 == ISA_200S, 3 == ISA_250MG, 4 == ISA_200S */
-} signatures[] = {
- /* 1 2 3 4 5 6 */
- /* 123456789012345678901234567890123456789012345678901234567890 */
- { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.07/28/89", 5, 50, 2, 0, 0 },
- { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V1.07/28/89", 5, 50, 2, 0, 0 },
- { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.07/28/89", 72, 50, 2, 0, 2 },
- { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.0", 73, 43, 2, 0, 3 },
- { "FUTURE DOMAIN CORP. (C) 1991 1800-V2.0.", 72, 39, 2, 0, 4 },
- { "FUTURE DOMAIN CORP. (C) 1992 V3.00.004/02/92", 5, 44, 3, 0, 0 },
- { "FUTURE DOMAIN TMC-18XX (C) 1993 V3.203/12/93", 5, 44, 3, 2, 0 },
- { "IBM F1 P2 BIOS v1.0104/29/93", 5, 28, 3, -1, 0 },
- { "Future Domain Corp. V1.0008/18/93", 5, 33, 3, 4, 0 },
- { "Future Domain Corp. V1.0008/18/93", 26, 33, 3, 4, 1 },
- { "Adaptec AHA-2920 PCI-SCSI Card", 42, 31, 3, -1, 1 },
- { "IBM F1 P264/32", 5, 14, 3, -1, 1 },
- /* This next signature may not be a 3.5 bios */
- { "Future Domain Corp. V2.0108/18/93", 5, 33, 3, 5, 0 },
- { "FUTURE DOMAIN CORP. V3.5008/18/93", 5, 34, 3, 5, 0 },
- { "FUTURE DOMAIN 18c30/18c50/1800 (C) 1994 V3.5", 5, 44, 3, 5, 0 },
- { "FUTURE DOMAIN CORP. V3.6008/18/93", 5, 34, 3, 6, 0 },
- { "FUTURE DOMAIN CORP. V3.6108/18/93", 5, 34, 3, 6, 0 },
- { "FUTURE DOMAIN TMC-18XX", 5, 22, -1, -1, 0 },
-
- /* READ NOTICE ABOVE *BEFORE* YOU WASTE YOUR TIME ADDING A SIGNATURE
- Also, fix the disk geometry code for your signature and send your
- changes for faith@cs.unc.edu. Above all, do *NOT* change any old
- signatures!
-
- Note that the last line will match a "generic" 18XX bios. Because
- Future Domain has changed the host SCSI ID and/or the location of the
- geometry information in the on-board RAM area for each of the first
- three BIOS's, it is still important to enter a fully qualified
- signature in the table for any new BIOS's (after the host SCSI ID and
- geometry location are verified). */
-};
-
-#define SIGNATURE_COUNT ARRAY_SIZE(signatures)
-
-#endif /* !PCMCIA */
-
-static void print_banner( struct Scsi_Host *shpnt )
-{
- if (!shpnt) return; /* This won't ever happen */
-
- if (bios_major < 0 && bios_minor < 0) {
- printk(KERN_INFO "scsi%d: <fdomain> No BIOS; using scsi id %d\n",
- shpnt->host_no, shpnt->this_id);
- } else {
- printk(KERN_INFO "scsi%d: <fdomain> BIOS version ", shpnt->host_no);
-
- if (bios_major >= 0) printk("%d.", bios_major);
- else printk("?.");
-
- if (bios_minor >= 0) printk("%d", bios_minor);
- else printk("?.");
-
- printk( " at 0x%lx using scsi id %d\n",
- bios_base, shpnt->this_id );
- }
-
- /* If this driver works for later FD PCI
- boards, we will have to modify banner
- for additional PCI cards, but for now if
- it's PCI it's a TMC-3260 - JTM */
- printk(KERN_INFO "scsi%d: <fdomain> %s chip at 0x%x irq ",
- shpnt->host_no,
- chip == tmc1800 ? "TMC-1800" : (chip == tmc18c50 ? "TMC-18C50" : (chip == tmc18c30 ? (PCI_bus ? "TMC-36C70 (PCI bus)" : "TMC-18C30") : "Unknown")),
- port_base);
-
- if (interrupt_level)
- printk("%d", interrupt_level);
- else
- printk("<none>");
-
- printk( "\n" );
-}
-
-int fdomain_setup(char *str)
-{
- int ints[4];
-
- (void)get_options(str, ARRAY_SIZE(ints), ints);
-
- if (setup_called++ || ints[0] < 2 || ints[0] > 3) {
- printk(KERN_INFO "scsi: <fdomain> Usage: fdomain=<PORT_BASE>,<IRQ>[,<ADAPTER_ID>]\n");
- printk(KERN_ERR "scsi: <fdomain> Bad LILO/INSMOD parameters?\n");
- return 0;
- }
-
- port_base = ints[0] >= 1 ? ints[1] : 0;
- interrupt_level = ints[0] >= 2 ? ints[2] : 0;
- this_id = ints[0] >= 3 ? ints[3] : 0;
-
- bios_major = bios_minor = -1; /* Use geometry for BIOS version >= 3.4 */
- ++setup_called;
- return 1;
-}
-
-__setup("fdomain=", fdomain_setup);
-
-
-static void do_pause(unsigned amount) /* Pause for amount*10 milliseconds */
-{
- mdelay(10*amount);
-}
-
-static inline void fdomain_make_bus_idle( void )
-{
- outb(0, port_base + SCSI_Cntl);
- outb(0, port_base + SCSI_Mode_Cntl);
- if (chip == tmc18c50 || chip == tmc18c30)
- outb(0x21 | PARITY_MASK, port_base + TMC_Cntl); /* Clear forced intr. */
- else
- outb(0x01 | PARITY_MASK, port_base + TMC_Cntl);
-}
-
-static int fdomain_is_valid_port( int port )
-{
-#if DEBUG_DETECT
- printk( " (%x%x),",
- inb( port + MSB_ID_Code ), inb( port + LSB_ID_Code ) );
-#endif
-
- /* The MCA ID is a unique id for each MCA compatible board. We
- are using ISA boards, but Future Domain provides the MCA ID
- anyway. We can use this ID to ensure that this is a Future
- Domain TMC-1660/TMC-1680.
- */
-
- if (inb( port + LSB_ID_Code ) != 0xe9) { /* test for 0x6127 id */
- if (inb( port + LSB_ID_Code ) != 0x27) return 0;
- if (inb( port + MSB_ID_Code ) != 0x61) return 0;
- chip = tmc1800;
- } else { /* test for 0xe960 id */
- if (inb( port + MSB_ID_Code ) != 0x60) return 0;
- chip = tmc18c50;
-
- /* Try to toggle 32-bit mode. This only
- works on an 18c30 chip. (User reports
- say this works, so we should switch to
- it in the near future.) */
-
- outb( 0x80, port + IO_Control );
- if ((inb( port + Configuration2 ) & 0x80) == 0x80) {
- outb( 0x00, port + IO_Control );
- if ((inb( port + Configuration2 ) & 0x80) == 0x00) {
- chip = tmc18c30;
- FIFO_Size = 0x800; /* 2k FIFO */
- }
- }
- /* If that failed, we are an 18c50. */
- }
-
- return 1;
-}
-
-static int fdomain_test_loopback( void )
-{
- int i;
- int result;
-
- for (i = 0; i < 255; i++) {
- outb( i, port_base + Write_Loopback );
- result = inb( port_base + Read_Loopback );
- if (i != result)
- return 1;
- }
- return 0;
-}
-
-#ifndef PCMCIA
-
-/* fdomain_get_irq assumes that we have a valid MCA ID for a
- TMC-1660/TMC-1680 Future Domain board. Now, check to be sure the
- bios_base matches these ports. If someone was unlucky enough to have
- purchased more than one Future Domain board, then they will have to
- modify this code, as we only detect one board here. [The one with the
- lowest bios_base.]
-
- Note that this routine is only used for systems without a PCI BIOS32
- (e.g., ISA bus). For PCI bus systems, this routine will likely fail
- unless one of the IRQs listed in the ints array is used by the board.
- Sometimes it is possible to use the computer's BIOS setup screen to
- configure a PCI system so that one of these IRQs will be used by the
- Future Domain card. */
-
-static int fdomain_get_irq( int base )
-{
- int options = inb(base + Configuration1);
-
-#if DEBUG_DETECT
- printk("scsi: <fdomain> Options = %x\n", options);
-#endif
-
- /* Check for board with lowest bios_base --
- this isn't valid for the 18c30 or for
- boards on the PCI bus, so just assume we
- have the right board. */
-
- if (chip != tmc18c30 && !PCI_bus && addresses[(options & 0xc0) >> 6 ] != bios_base)
- return 0;
- return ints[(options & 0x0e) >> 1];
-}
-
-static int fdomain_isa_detect( int *irq, int *iobase )
-{
- int i, j;
- int base = 0xdeadbeef;
- int flag = 0;
-
-#if DEBUG_DETECT
- printk( "scsi: <fdomain> fdomain_isa_detect:" );
-#endif
-
- for (i = 0; i < ADDRESS_COUNT; i++) {
- void __iomem *p = ioremap(addresses[i], 0x2000);
- if (!p)
- continue;
-#if DEBUG_DETECT
- printk( " %lx(%lx),", addresses[i], bios_base );
-#endif
- for (j = 0; j < SIGNATURE_COUNT; j++) {
- if (check_signature(p + signatures[j].sig_offset,
- signatures[j].signature,
- signatures[j].sig_length )) {
- bios_major = signatures[j].major_bios_version;
- bios_minor = signatures[j].minor_bios_version;
- PCI_bus = (signatures[j].flag == 1);
- Quantum = (signatures[j].flag > 1) ? signatures[j].flag : 0;
- bios_base = addresses[i];
- bios_mem = p;
- goto found;
- }
- }
- iounmap(p);
- }
-
-found:
- if (bios_major == 2) {
- /* The TMC-1660/TMC-1680 has a RAM area just after the BIOS ROM.
- Assuming the ROM is enabled (otherwise we wouldn't have been
- able to read the ROM signature :-), then the ROM sets up the
- RAM area with some magic numbers, such as a list of port
- base addresses and a list of the disk "geometry" reported to
- DOS (this geometry has nothing to do with physical geometry).
- */
-
- switch (Quantum) {
- case 2: /* ISA_200S */
- case 3: /* ISA_250MG */
- base = readb(bios_mem + 0x1fa2) + (readb(bios_mem + 0x1fa3) << 8);
- break;
- case 4: /* ISA_200S (another one) */
- base = readb(bios_mem + 0x1fa3) + (readb(bios_mem + 0x1fa4) << 8);
- break;
- default:
- base = readb(bios_mem + 0x1fcc) + (readb(bios_mem + 0x1fcd) << 8);
- break;
- }
-
-#if DEBUG_DETECT
- printk( " %x,", base );
-#endif
-
- for (i = 0; i < PORT_COUNT; i++) {
- if (base == ports[i]) {
- if (!request_region(base, 0x10, "fdomain"))
- break;
- if (!fdomain_is_valid_port(base)) {
- release_region(base, 0x10);
- break;
- }
- *irq = fdomain_get_irq( base );
- *iobase = base;
- return 1;
- }
- }
-
- /* This is a bad sign. It usually means that someone patched the
- BIOS signature list (the signatures variable) to contain a BIOS
- signature for a board *OTHER THAN* the TMC-1660/TMC-1680. */
-
-#if DEBUG_DETECT
- printk( " RAM FAILED, " );
-#endif
- }
-
- /* Anyway, the alternative to finding the address in the RAM is to just
- search through every possible port address for one that is attached
- to the Future Domain card. Don't panic, though, about reading all
- these random port addresses -- there are rumors that the Future
- Domain BIOS does something very similar.
-
- Do not, however, check ports which the kernel knows are being used by
- another driver. */
-
- for (i = 0; i < PORT_COUNT; i++) {
- base = ports[i];
- if (!request_region(base, 0x10, "fdomain")) {
-#if DEBUG_DETECT
- printk( " (%x inuse),", base );
-#endif
- continue;
- }
-#if DEBUG_DETECT
- printk( " %x,", base );
-#endif
- flag = fdomain_is_valid_port(base);
- if (flag)
- break;
- release_region(base, 0x10);
- }
-
-#if DEBUG_DETECT
- if (flag) printk( " SUCCESS\n" );
- else printk( " FAILURE\n" );
-#endif
-
- if (!flag) return 0; /* iobase not found */
-
- *irq = fdomain_get_irq( base );
- *iobase = base;
-
- return 1; /* success */
-}
-
-#else /* PCMCIA */
-
-static int fdomain_isa_detect( int *irq, int *iobase )
-{
- if (irq)
- *irq = 0;
- if (iobase)
- *iobase = 0;
- return 0;
-}
-
-#endif /* !PCMCIA */
-
-
-/* PCI detection function: int fdomain_pci_bios_detect(int* irq, int*
- iobase) This function gets the Interrupt Level and I/O base address from
- the PCI configuration registers. */
-
-#ifdef CONFIG_PCI
-static int fdomain_pci_bios_detect( int *irq, int *iobase, struct pci_dev **ret_pdev )
-{
- unsigned int pci_irq; /* PCI interrupt line */
- unsigned long pci_base; /* PCI I/O base address */
- struct pci_dev *pdev = NULL;
-
-#if DEBUG_DETECT
- /* Tell how to print a list of the known PCI devices from bios32 and
- list vendor and device IDs being used if in debug mode. */
-
- printk( "scsi: <fdomain> INFO: use lspci -v to see list of PCI devices\n" );
- printk( "scsi: <fdomain> TMC-3260 detect:"
- " Using Vendor ID: 0x%x and Device ID: 0x%x\n",
- PCI_VENDOR_ID_FD,
- PCI_DEVICE_ID_FD_36C70 );
-#endif
-
- if ((pdev = pci_get_device(PCI_VENDOR_ID_FD, PCI_DEVICE_ID_FD_36C70, pdev)) == NULL)
- return 0;
- if (pci_enable_device(pdev))
- goto fail;
-
-#if DEBUG_DETECT
- printk( "scsi: <fdomain> TMC-3260 detect:"
- " PCI bus %u, device %u, function %u\n",
- pdev->bus->number,
- PCI_SLOT(pdev->devfn),
- PCI_FUNC(pdev->devfn));
-#endif
-
- /* We now have the appropriate device function for the FD board so we
- just read the PCI config info from the registers. */
-
- pci_base = pci_resource_start(pdev, 0);
- pci_irq = pdev->irq;
-
- if (!request_region( pci_base, 0x10, "fdomain" ))
- goto fail;
-
- /* Now we have the I/O base address and interrupt from the PCI
- configuration registers. */
-
- *irq = pci_irq;
- *iobase = pci_base;
- *ret_pdev = pdev;
-
-#if DEBUG_DETECT
- printk( "scsi: <fdomain> TMC-3260 detect:"
- " IRQ = %d, I/O base = 0x%x [0x%lx]\n", *irq, *iobase, pci_base );
-#endif
-
- if (!fdomain_is_valid_port(pci_base)) {
- printk(KERN_ERR "scsi: <fdomain> PCI card detected, but driver not loaded (invalid port)\n" );
- release_region(pci_base, 0x10);
- goto fail;
- }
-
- /* Fill in a few global variables. Ugh. */
- bios_major = bios_minor = -1;
- PCI_bus = 1;
- PCI_dev = pdev;
- Quantum = 0;
- bios_base = 0;
-
- return 1;
-fail:
- pci_dev_put(pdev);
- return 0;
-}
-
-#endif
-
-struct Scsi_Host *__fdomain_16x0_detect(struct scsi_host_template *tpnt )
-{
- int retcode;
- struct Scsi_Host *shpnt;
- struct pci_dev *pdev = NULL;
-
- if (setup_called) {
-#if DEBUG_DETECT
- printk( "scsi: <fdomain> No BIOS, using port_base = 0x%x, irq = %d\n",
- port_base, interrupt_level );
-#endif
- if (!request_region(port_base, 0x10, "fdomain")) {
- printk( "scsi: <fdomain> port 0x%x is busy\n", port_base );
- printk( "scsi: <fdomain> Bad LILO/INSMOD parameters?\n" );
- return NULL;
- }
- if (!fdomain_is_valid_port( port_base )) {
- printk( "scsi: <fdomain> Cannot locate chip at port base 0x%x\n",
- port_base );
- printk( "scsi: <fdomain> Bad LILO/INSMOD parameters?\n" );
- release_region(port_base, 0x10);
- return NULL;
- }
- } else {
- int flag = 0;
-
-#ifdef CONFIG_PCI
- /* Try PCI detection first */
- flag = fdomain_pci_bios_detect( &interrupt_level, &port_base, &pdev );
-#endif
- if (!flag) {
- /* Then try ISA bus detection */
- flag = fdomain_isa_detect( &interrupt_level, &port_base );
-
- if (!flag) {
- printk( "scsi: <fdomain> Detection failed (no card)\n" );
- return NULL;
- }
- }
- }
-
- fdomain_16x0_host_reset(NULL);
-
- if (fdomain_test_loopback()) {
- printk(KERN_ERR "scsi: <fdomain> Detection failed (loopback test failed at port base 0x%x)\n", port_base);
- if (setup_called) {
- printk(KERN_ERR "scsi: <fdomain> Bad LILO/INSMOD parameters?\n");
- }
- goto fail;
- }
-
- if (this_id) {
- tpnt->this_id = (this_id & 0x07);
- adapter_mask = (1 << tpnt->this_id);
- } else {
- if (PCI_bus || (bios_major == 3 && bios_minor >= 2) || bios_major < 0) {
- tpnt->this_id = 7;
- adapter_mask = 0x80;
- } else {
- tpnt->this_id = 6;
- adapter_mask = 0x40;
- }
- }
-
-/* Print out a banner here in case we can't
- get resources. */
-
- shpnt = scsi_register( tpnt, 0 );
- if(shpnt == NULL) {
- release_region(port_base, 0x10);
- return NULL;
- }
- shpnt->irq = interrupt_level;
- shpnt->io_port = port_base;
- shpnt->n_io_port = 0x10;
- print_banner( shpnt );
-
- /* Log IRQ with kernel */
- if (!interrupt_level) {
- printk(KERN_ERR "scsi: <fdomain> Card Detected, but driver not loaded (no IRQ)\n" );
- goto fail;
- } else {
- /* Register the IRQ with the kernel */
-
- retcode = request_irq( interrupt_level,
- do_fdomain_16x0_intr, pdev?IRQF_SHARED:0, "fdomain", shpnt);
-
- if (retcode < 0) {
- if (retcode == -EINVAL) {
- printk(KERN_ERR "scsi: <fdomain> IRQ %d is bad!\n", interrupt_level );
- printk(KERN_ERR " This shouldn't happen!\n" );
- printk(KERN_ERR " Send mail to faith@acm.org\n" );
- } else if (retcode == -EBUSY) {
- printk(KERN_ERR "scsi: <fdomain> IRQ %d is already in use!\n", interrupt_level );
- printk(KERN_ERR " Please use another IRQ!\n" );
- } else {
- printk(KERN_ERR "scsi: <fdomain> Error getting IRQ %d\n", interrupt_level );
- printk(KERN_ERR " This shouldn't happen!\n" );
- printk(KERN_ERR " Send mail to faith@acm.org\n" );
- }
- printk(KERN_ERR "scsi: <fdomain> Detected, but driver not loaded (IRQ)\n" );
- goto fail;
- }
- }
- return shpnt;
-fail:
- pci_dev_put(pdev);
- release_region(port_base, 0x10);
- return NULL;
-}
-
-static int fdomain_16x0_detect(struct scsi_host_template *tpnt)
-{
- if (fdomain)
- fdomain_setup(fdomain);
- return (__fdomain_16x0_detect(tpnt) != NULL);
-}
-
-static const char *fdomain_16x0_info( struct Scsi_Host *ignore )
-{
- static char buffer[128];
- char *pt;
-
- strcpy( buffer, "Future Domain 16-bit SCSI Driver Version" );
- if (strchr( VERSION, ':')) { /* Assume VERSION is an RCS Revision string */
- strcat( buffer, strchr( VERSION, ':' ) + 1 );
- pt = strrchr( buffer, '$') - 1;
- if (!pt) /* Stripped RCS Revision string? */
- pt = buffer + strlen( buffer ) - 1;
- if (*pt != ' ')
- ++pt;
- *pt = '\0';
- } else { /* Assume VERSION is a number */
- strcat( buffer, " " VERSION );
- }
-
- return buffer;
-}
-
-#if 0
-static int fdomain_arbitrate( void )
-{
- int status = 0;
- unsigned long timeout;
-
-#if EVERY_ACCESS
- printk( "fdomain_arbitrate()\n" );
-#endif
-
- outb(0x00, port_base + SCSI_Cntl); /* Disable data drivers */
- outb(adapter_mask, port_base + SCSI_Data_NoACK); /* Set our id bit */
- outb(0x04 | PARITY_MASK, port_base + TMC_Cntl); /* Start arbitration */
-
- timeout = 500;
- do {
- status = inb(port_base + TMC_Status); /* Read adapter status */
- if (status & 0x02) /* Arbitration complete */
- return 0;
- mdelay(1); /* Wait one millisecond */
- } while (--timeout);
-
- /* Make bus idle */
- fdomain_make_bus_idle();
-
-#if EVERY_ACCESS
- printk( "Arbitration failed, status = %x\n", status );
-#endif
-#if ERRORS_ONLY
- printk( "scsi: <fdomain> Arbitration failed, status = %x\n", status );
-#endif
- return 1;
-}
-#endif
-
-static int fdomain_select( int target )
-{
- int status;
- unsigned long timeout;
-#if ERRORS_ONLY
- static int flag = 0;
-#endif
-
- outb(0x82, port_base + SCSI_Cntl); /* Bus Enable + Select */
- outb(adapter_mask | (1 << target), port_base + SCSI_Data_NoACK);
-
- /* Stop arbitration and enable parity */
- outb(PARITY_MASK, port_base + TMC_Cntl);
-
- timeout = 350; /* 350 msec */
-
- do {
- status = inb(port_base + SCSI_Status); /* Read adapter status */
- if (status & 1) { /* Busy asserted */
- /* Enable SCSI Bus (on error, should make bus idle with 0) */
- outb(0x80, port_base + SCSI_Cntl);
- return 0;
- }
- mdelay(1); /* wait one msec */
- } while (--timeout);
- /* Make bus idle */
- fdomain_make_bus_idle();
-#if EVERY_ACCESS
- if (!target) printk( "Selection failed\n" );
-#endif
-#if ERRORS_ONLY
- if (!target) {
- if (!flag) /* Skip first failure for all chips. */
- ++flag;
- else
- printk( "scsi: <fdomain> Selection failed\n" );
- }
-#endif
- return 1;
-}
-
-static void my_done(int error)
-{
- if (in_command) {
- in_command = 0;
- outb(0x00, port_base + Interrupt_Cntl);
- fdomain_make_bus_idle();
- current_SC->result = error;
- if (current_SC->scsi_done)
- current_SC->scsi_done( current_SC );
- else panic( "scsi: <fdomain> current_SC->scsi_done() == NULL" );
- } else {
- panic( "scsi: <fdomain> my_done() called outside of command\n" );
- }
-#if DEBUG_RACE
- in_interrupt_flag = 0;
-#endif
-}
-
-static irqreturn_t do_fdomain_16x0_intr(int irq, void *dev_id)
-{
- unsigned long flags;
- int status;
- int done = 0;
- unsigned data_count;
-
- /* The fdomain_16x0_intr is only called via
- the interrupt handler. The goal of the
- sti() here is to allow other
- interruptions while this routine is
- running. */
-
- /* Check for other IRQ sources */
- if ((inb(port_base + TMC_Status) & 0x01) == 0)
- return IRQ_NONE;
-
- /* It is our IRQ */
- outb(0x00, port_base + Interrupt_Cntl);
-
- /* We usually have one spurious interrupt after each command. Ignore it. */
- if (!in_command || !current_SC) { /* Spurious interrupt */
-#if EVERY_ACCESS
- printk( "Spurious interrupt, in_command = %d, current_SC = %x\n",
- in_command, current_SC );
-#endif
- return IRQ_NONE;
- }
-
- /* Abort calls my_done, so we do nothing here. */
- if (current_SC->SCp.phase & aborted) {
-#if DEBUG_ABORT
- printk( "scsi: <fdomain> Interrupt after abort, ignoring\n" );
-#endif
- /*
- return IRQ_HANDLED; */
- }
-
-#if DEBUG_RACE
- ++in_interrupt_flag;
-#endif
-
- if (current_SC->SCp.phase & in_arbitration) {
- status = inb(port_base + TMC_Status); /* Read adapter status */
- if (!(status & 0x02)) {
-#if EVERY_ACCESS
- printk( " AFAIL " );
-#endif
- spin_lock_irqsave(current_SC->device->host->host_lock, flags);
- my_done( DID_BUS_BUSY << 16 );
- spin_unlock_irqrestore(current_SC->device->host->host_lock, flags);
- return IRQ_HANDLED;
- }
- current_SC->SCp.phase = in_selection;
-
- outb(0x40 | FIFO_COUNT, port_base + Interrupt_Cntl);
-
- outb(0x82, port_base + SCSI_Cntl); /* Bus Enable + Select */
- outb(adapter_mask | (1 << scmd_id(current_SC)), port_base + SCSI_Data_NoACK);
-
- /* Stop arbitration and enable parity */
- outb(0x10 | PARITY_MASK, port_base + TMC_Cntl);
-#if DEBUG_RACE
- in_interrupt_flag = 0;
-#endif
- return IRQ_HANDLED;
- } else if (current_SC->SCp.phase & in_selection) {
- status = inb(port_base + SCSI_Status);
- if (!(status & 0x01)) {
- /* Try again, for slow devices */
- if (fdomain_select( scmd_id(current_SC) )) {
-#if EVERY_ACCESS
- printk( " SFAIL " );
-#endif
- spin_lock_irqsave(current_SC->device->host->host_lock, flags);
- my_done( DID_NO_CONNECT << 16 );
- spin_unlock_irqrestore(current_SC->device->host->host_lock, flags);
- return IRQ_HANDLED;
- } else {
-#if EVERY_ACCESS
- printk( " AltSel " );
-#endif
- /* Stop arbitration and enable parity */
- outb(0x10 | PARITY_MASK, port_base + TMC_Cntl);
- }
- }
- current_SC->SCp.phase = in_other;
- outb(0x90 | FIFO_COUNT, port_base + Interrupt_Cntl);
- outb(0x80, port_base + SCSI_Cntl);
-#if DEBUG_RACE
- in_interrupt_flag = 0;
-#endif
- return IRQ_HANDLED;
- }
-
- /* current_SC->SCp.phase == in_other: this is the body of the routine */
-
- status = inb(port_base + SCSI_Status);
-
- if (status & 0x10) { /* REQ */
-
- switch (status & 0x0e) {
-
- case 0x08: /* COMMAND OUT */
- outb(current_SC->cmnd[current_SC->SCp.sent_command++],
- port_base + Write_SCSI_Data);
-#if EVERY_ACCESS
- printk( "CMD = %x,",
- current_SC->cmnd[ current_SC->SCp.sent_command - 1] );
-#endif
- break;
- case 0x00: /* DATA OUT -- tmc18c50/tmc18c30 only */
- if (chip != tmc1800 && !current_SC->SCp.have_data_in) {
- current_SC->SCp.have_data_in = -1;
- outb(0xd0 | PARITY_MASK, port_base + TMC_Cntl);
- }
- break;
- case 0x04: /* DATA IN -- tmc18c50/tmc18c30 only */
- if (chip != tmc1800 && !current_SC->SCp.have_data_in) {
- current_SC->SCp.have_data_in = 1;
- outb(0x90 | PARITY_MASK, port_base + TMC_Cntl);
- }
- break;
- case 0x0c: /* STATUS IN */
- current_SC->SCp.Status = inb(port_base + Read_SCSI_Data);
-#if EVERY_ACCESS
- printk( "Status = %x, ", current_SC->SCp.Status );
-#endif
-#if ERRORS_ONLY
- if (current_SC->SCp.Status
- && current_SC->SCp.Status != 2
- && current_SC->SCp.Status != 8) {
- printk( "scsi: <fdomain> target = %d, command = %x, status = %x\n",
- current_SC->device->id,
- current_SC->cmnd[0],
- current_SC->SCp.Status );
- }
-#endif
- break;
- case 0x0a: /* MESSAGE OUT */
- outb(MESSAGE_REJECT, port_base + Write_SCSI_Data); /* Reject */
- break;
- case 0x0e: /* MESSAGE IN */
- current_SC->SCp.Message = inb(port_base + Read_SCSI_Data);
-#if EVERY_ACCESS
- printk( "Message = %x, ", current_SC->SCp.Message );
-#endif
- if (!current_SC->SCp.Message) ++done;
-#if DEBUG_MESSAGES || EVERY_ACCESS
- if (current_SC->SCp.Message) {
- printk( "scsi: <fdomain> message = %x\n",
- current_SC->SCp.Message );
- }
-#endif
- break;
- }
- }
-
- if (chip == tmc1800 && !current_SC->SCp.have_data_in
- && (current_SC->SCp.sent_command >= current_SC->cmd_len)) {
-
- if(current_SC->sc_data_direction == DMA_TO_DEVICE)
- {
- current_SC->SCp.have_data_in = -1;
- outb(0xd0 | PARITY_MASK, port_base + TMC_Cntl);
- }
- else
- {
- current_SC->SCp.have_data_in = 1;
- outb(0x90 | PARITY_MASK, port_base + TMC_Cntl);
- }
- }
-
- if (current_SC->SCp.have_data_in == -1) { /* DATA OUT */
- while ((data_count = FIFO_Size - inw(port_base + FIFO_Data_Count)) > 512) {
-#if EVERY_ACCESS
- printk( "DC=%d, ", data_count ) ;
-#endif
- if (data_count > current_SC->SCp.this_residual)
- data_count = current_SC->SCp.this_residual;
- if (data_count > 0) {
-#if EVERY_ACCESS
- printk( "%d OUT, ", data_count );
-#endif
- if (data_count == 1) {
- outb(*current_SC->SCp.ptr++, port_base + Write_FIFO);
- --current_SC->SCp.this_residual;
- } else {
- data_count >>= 1;
- outsw(port_base + Write_FIFO, current_SC->SCp.ptr, data_count);
- current_SC->SCp.ptr += 2 * data_count;
- current_SC->SCp.this_residual -= 2 * data_count;
- }
- }
- if (!current_SC->SCp.this_residual) {
- if (current_SC->SCp.buffers_residual) {
- --current_SC->SCp.buffers_residual;
- ++current_SC->SCp.buffer;
- current_SC->SCp.ptr = sg_virt(current_SC->SCp.buffer);
- current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
- } else
- break;
- }
- }
- }
-
- if (current_SC->SCp.have_data_in == 1) { /* DATA IN */
- while ((data_count = inw(port_base + FIFO_Data_Count)) > 0) {
-#if EVERY_ACCESS
- printk( "DC=%d, ", data_count );
-#endif
- if (data_count > current_SC->SCp.this_residual)
- data_count = current_SC->SCp.this_residual;
- if (data_count) {
-#if EVERY_ACCESS
- printk( "%d IN, ", data_count );
-#endif
- if (data_count == 1) {
- *current_SC->SCp.ptr++ = inb(port_base + Read_FIFO);
- --current_SC->SCp.this_residual;
- } else {
- data_count >>= 1; /* Number of words */
- insw(port_base + Read_FIFO, current_SC->SCp.ptr, data_count);
- current_SC->SCp.ptr += 2 * data_count;
- current_SC->SCp.this_residual -= 2 * data_count;
- }
- }
- if (!current_SC->SCp.this_residual
- && current_SC->SCp.buffers_residual) {
- --current_SC->SCp.buffers_residual;
- ++current_SC->SCp.buffer;
- current_SC->SCp.ptr = sg_virt(current_SC->SCp.buffer);
- current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
- }
- }
- }
-
- if (done) {
-#if EVERY_ACCESS
- printk( " ** IN DONE %d ** ", current_SC->SCp.have_data_in );
-#endif
-
-#if ERRORS_ONLY
- if (current_SC->cmnd[0] == REQUEST_SENSE && !current_SC->SCp.Status) {
- char *buf = scsi_sglist(current_SC);
- if ((unsigned char)(*(buf + 2)) & 0x0f) {
- unsigned char key;
- unsigned char code;
- unsigned char qualifier;
-
- key = (unsigned char)(*(buf + 2)) & 0x0f;
- code = (unsigned char)(*(buf + 12));
- qualifier = (unsigned char)(*(buf + 13));
-
- if (key != UNIT_ATTENTION
- && !(key == NOT_READY
- && code == 0x04
- && (!qualifier || qualifier == 0x02 || qualifier == 0x01))
- && !(key == ILLEGAL_REQUEST && (code == 0x25
- || code == 0x24
- || !code)))
-
- printk( "scsi: <fdomain> REQUEST SENSE"
- " Key = %x, Code = %x, Qualifier = %x\n",
- key, code, qualifier );
- }
- }
-#endif
-#if EVERY_ACCESS
- printk( "BEFORE MY_DONE. . ." );
-#endif
- spin_lock_irqsave(current_SC->device->host->host_lock, flags);
- my_done( (current_SC->SCp.Status & 0xff)
- | ((current_SC->SCp.Message & 0xff) << 8) | (DID_OK << 16) );
- spin_unlock_irqrestore(current_SC->device->host->host_lock, flags);
-#if EVERY_ACCESS
- printk( "RETURNING.\n" );
-#endif
-
- } else {
- if (current_SC->SCp.phase & disconnect) {
- outb(0xd0 | FIFO_COUNT, port_base + Interrupt_Cntl);
- outb(0x00, port_base + SCSI_Cntl);
- } else {
- outb(0x90 | FIFO_COUNT, port_base + Interrupt_Cntl);
- }
- }
-#if DEBUG_RACE
- in_interrupt_flag = 0;
-#endif
- return IRQ_HANDLED;
-}
-
-static int fdomain_16x0_queue_lck(struct scsi_cmnd *SCpnt,
- void (*done)(struct scsi_cmnd *))
-{
- if (in_command) {
- panic( "scsi: <fdomain> fdomain_16x0_queue() NOT REENTRANT!\n" );
- }
-#if EVERY_ACCESS
- printk( "queue: target = %d cmnd = 0x%02x pieces = %d size = %u\n",
- SCpnt->target,
- *(unsigned char *)SCpnt->cmnd,
- scsi_sg_count(SCpnt),
- scsi_bufflen(SCpnt));
-#endif
-
- fdomain_make_bus_idle();
-
- current_SC = SCpnt; /* Save this for the done function */
- current_SC->scsi_done = done;
-
- /* Initialize static data */
-
- if (scsi_sg_count(current_SC)) {
- current_SC->SCp.buffer = scsi_sglist(current_SC);
- current_SC->SCp.ptr = sg_virt(current_SC->SCp.buffer);
- current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
- current_SC->SCp.buffers_residual = scsi_sg_count(current_SC) - 1;
- } else {
- current_SC->SCp.ptr = NULL;
- current_SC->SCp.this_residual = 0;
- current_SC->SCp.buffer = NULL;
- current_SC->SCp.buffers_residual = 0;
- }
-
- current_SC->SCp.Status = 0;
- current_SC->SCp.Message = 0;
- current_SC->SCp.have_data_in = 0;
- current_SC->SCp.sent_command = 0;
- current_SC->SCp.phase = in_arbitration;
-
- /* Start arbitration */
- outb(0x00, port_base + Interrupt_Cntl);
- outb(0x00, port_base + SCSI_Cntl); /* Disable data drivers */
- outb(adapter_mask, port_base + SCSI_Data_NoACK); /* Set our id bit */
- ++in_command;
- outb(0x20, port_base + Interrupt_Cntl);
- outb(0x14 | PARITY_MASK, port_base + TMC_Cntl); /* Start arbitration */
-
- return 0;
-}
-
-static DEF_SCSI_QCMD(fdomain_16x0_queue)
-
-#if DEBUG_ABORT
-static void print_info(struct scsi_cmnd *SCpnt)
-{
- unsigned int imr;
- unsigned int irr;
- unsigned int isr;
-
- if (!SCpnt || !SCpnt->device || !SCpnt->device->host) {
- printk(KERN_WARNING "scsi: <fdomain> Cannot provide detailed information\n");
- return;
- }
-
- printk(KERN_INFO "%s\n", fdomain_16x0_info( SCpnt->device->host ) );
- print_banner(SCpnt->device->host);
- switch (SCpnt->SCp.phase) {
- case in_arbitration: printk("arbitration"); break;
- case in_selection: printk("selection"); break;
- case in_other: printk("other"); break;
- default: printk("unknown"); break;
- }
-
- printk( " (%d), target = %d cmnd = 0x%02x pieces = %d size = %u\n",
- SCpnt->SCp.phase,
- SCpnt->device->id,
- *(unsigned char *)SCpnt->cmnd,
- scsi_sg_count(SCpnt),
- scsi_bufflen(SCpnt));
- printk( "sent_command = %d, have_data_in = %d, timeout = %d\n",
- SCpnt->SCp.sent_command,
- SCpnt->SCp.have_data_in,
- SCpnt->timeout );
-#if DEBUG_RACE
- printk( "in_interrupt_flag = %d\n", in_interrupt_flag );
-#endif
-
- imr = (inb( 0x0a1 ) << 8) + inb( 0x21 );
- outb( 0x0a, 0xa0 );
- irr = inb( 0xa0 ) << 8;
- outb( 0x0a, 0x20 );
- irr += inb( 0x20 );
- outb( 0x0b, 0xa0 );
- isr = inb( 0xa0 ) << 8;
- outb( 0x0b, 0x20 );
- isr += inb( 0x20 );
-
- /* Print out interesting information */
- printk( "IMR = 0x%04x", imr );
- if (imr & (1 << interrupt_level))
- printk( " (masked)" );
- printk( ", IRR = 0x%04x, ISR = 0x%04x\n", irr, isr );
-
- printk( "SCSI Status = 0x%02x\n", inb(port_base + SCSI_Status));
- printk( "TMC Status = 0x%02x", inb(port_base + TMC_Status));
- if (inb((port_base + TMC_Status) & 1))
- printk( " (interrupt)" );
- printk( "\n" );
- printk("Interrupt Status = 0x%02x", inb(port_base + Interrupt_Status));
- if (inb(port_base + Interrupt_Status) & 0x08)
- printk( " (enabled)" );
- printk( "\n" );
- if (chip == tmc18c50 || chip == tmc18c30) {
- printk("FIFO Status = 0x%02x\n", inb(port_base + FIFO_Status));
- printk( "Int. Condition = 0x%02x\n",
- inb( port_base + Interrupt_Cond ) );
- }
- printk( "Configuration 1 = 0x%02x\n", inb( port_base + Configuration1 ) );
- if (chip == tmc18c50 || chip == tmc18c30)
- printk( "Configuration 2 = 0x%02x\n",
- inb( port_base + Configuration2 ) );
-}
-#endif
-
-static int fdomain_16x0_abort(struct scsi_cmnd *SCpnt)
-{
-#if EVERY_ACCESS || ERRORS_ONLY || DEBUG_ABORT
- printk( "scsi: <fdomain> abort " );
-#endif
-
- if (!in_command) {
-#if EVERY_ACCESS || ERRORS_ONLY
- printk( " (not in command)\n" );
-#endif
- return FAILED;
- } else printk( "\n" );
-
-#if DEBUG_ABORT
- print_info( SCpnt );
-#endif
-
- fdomain_make_bus_idle();
- current_SC->SCp.phase |= aborted;
- current_SC->result = DID_ABORT << 16;
-
- /* Aborts are not done well. . . */
- my_done(DID_ABORT << 16);
- return SUCCESS;
-}
-
-int fdomain_16x0_host_reset(struct scsi_cmnd *SCpnt)
-{
- unsigned long flags;
-
- local_irq_save(flags);
-
- outb(1, port_base + SCSI_Cntl);
- do_pause( 2 );
- outb(0, port_base + SCSI_Cntl);
- do_pause( 115 );
- outb(0, port_base + SCSI_Mode_Cntl);
- outb(PARITY_MASK, port_base + TMC_Cntl);
-
- local_irq_restore(flags);
- return SUCCESS;
-}
-
-static int fdomain_16x0_biosparam(struct scsi_device *sdev,
- struct block_device *bdev,
- sector_t capacity, int *info_array)
-{
- int drive;
- int size = capacity;
- unsigned long offset;
- struct drive_info {
- unsigned short cylinders;
- unsigned char heads;
- unsigned char sectors;
- } i;
-
- /* NOTES:
- The RAM area starts at 0x1f00 from the bios_base address.
-
- For BIOS Version 2.0:
-
- The drive parameter table seems to start at 0x1f30.
- The first byte's purpose is not known.
- Next is the cylinder, head, and sector information.
- The last 4 bytes appear to be the drive's size in sectors.
- The other bytes in the drive parameter table are unknown.
- If anyone figures them out, please send me mail, and I will
- update these notes.
-
- Tape drives do not get placed in this table.
-
- There is another table at 0x1fea:
- If the byte is 0x01, then the SCSI ID is not in use.
- If the byte is 0x18 or 0x48, then the SCSI ID is in use,
- although tapes don't seem to be in this table. I haven't
- seen any other numbers (in a limited sample).
-
- 0x1f2d is a drive count (i.e., not including tapes)
-
- The table at 0x1fcc are I/O ports addresses for the various
- operations. I calculate these by hand in this driver code.
-
-
-
- For the ISA-200S version of BIOS Version 2.0:
-
- The drive parameter table starts at 0x1f33.
-
- WARNING: Assume that the table entry is 25 bytes long. Someone needs
- to check this for the Quantum ISA-200S card.
-
-
-
- For BIOS Version 3.2:
-
- The drive parameter table starts at 0x1f70. Each entry is
- 0x0a bytes long. Heads are one less than we need to report.
- */
-
- if (MAJOR(bdev->bd_dev) != SCSI_DISK0_MAJOR) {
- printk("scsi: <fdomain> fdomain_16x0_biosparam: too many disks");
- return 0;
- }
- drive = MINOR(bdev->bd_dev) >> 4;
-
- if (bios_major == 2) {
- switch (Quantum) {
- case 2: /* ISA_200S */
- /* The value of 25 has never been verified.
- It should probably be 15. */
- offset = 0x1f33 + drive * 25;
- break;
- case 3: /* ISA_250MG */
- offset = 0x1f36 + drive * 15;
- break;
- case 4: /* ISA_200S (another one) */
- offset = 0x1f34 + drive * 15;
- break;
- default:
- offset = 0x1f31 + drive * 25;
- break;
- }
- memcpy_fromio( &i, bios_mem + offset, sizeof( struct drive_info ) );
- info_array[0] = i.heads;
- info_array[1] = i.sectors;
- info_array[2] = i.cylinders;
- } else if (bios_major == 3
- && bios_minor >= 0
- && bios_minor < 4) { /* 3.0 and 3.2 BIOS */
- memcpy_fromio( &i, bios_mem + 0x1f71 + drive * 10,
- sizeof( struct drive_info ) );
- info_array[0] = i.heads + 1;
- info_array[1] = i.sectors;
- info_array[2] = i.cylinders;
- } else { /* 3.4 BIOS (and up?) */
- /* This algorithm was provided by Future Domain (much thanks!). */
- unsigned char *p = scsi_bios_ptable(bdev);
-
- if (p && p[65] == 0xaa && p[64] == 0x55 /* Partition table valid */
- && p[4]) { /* Partition type */
-
- /* The partition table layout is as follows:
-
- Start: 0x1b3h
- Offset: 0 = partition status
- 1 = starting head
- 2 = starting sector and cylinder (word, encoded)
- 4 = partition type
- 5 = ending head
- 6 = ending sector and cylinder (word, encoded)
- 8 = starting absolute sector (double word)
- c = number of sectors (double word)
- Signature: 0x1fe = 0x55aa
-
- So, this algorithm assumes:
- 1) the first partition table is in use,
- 2) the data in the first entry is correct, and
- 3) partitions never divide cylinders
-
- Note that (1) may be FALSE for NetBSD (and other BSD flavors),
- as well as for Linux. Note also, that Linux doesn't pay any
- attention to the fields that are used by this algorithm -- it
- only uses the absolute sector data. Recent versions of Linux's
- fdisk(1) will fill this data in correctly, and forthcoming
- versions will check for consistency.
-
- Checking for a non-zero partition type is not part of the
- Future Domain algorithm, but it seemed to be a reasonable thing
- to do, especially in the Linux and BSD worlds. */
-
- info_array[0] = p[5] + 1; /* heads */
- info_array[1] = p[6] & 0x3f; /* sectors */
- } else {
-
- /* Note that this new method guarantees that there will always be
- less than 1024 cylinders on a platter. This is good for drives
- up to approximately 7.85GB (where 1GB = 1024 * 1024 kB). */
-
- if ((unsigned int)size >= 0x7e0000U) {
- info_array[0] = 0xff; /* heads = 255 */
- info_array[1] = 0x3f; /* sectors = 63 */
- } else if ((unsigned int)size >= 0x200000U) {
- info_array[0] = 0x80; /* heads = 128 */
- info_array[1] = 0x3f; /* sectors = 63 */
- } else {
- info_array[0] = 0x40; /* heads = 64 */
- info_array[1] = 0x20; /* sectors = 32 */
- }
- }
- /* For both methods, compute the cylinders */
- info_array[2] = (unsigned int)size / (info_array[0] * info_array[1] );
- kfree(p);
- }
-
- return 0;
-}
-
-static int fdomain_16x0_release(struct Scsi_Host *shpnt)
-{
- if (shpnt->irq)
- free_irq(shpnt->irq, shpnt);
- if (shpnt->io_port && shpnt->n_io_port)
- release_region(shpnt->io_port, shpnt->n_io_port);
- if (PCI_bus)
- pci_dev_put(PCI_dev);
- return 0;
-}
-
-struct scsi_host_template fdomain_driver_template = {
- .module = THIS_MODULE,
- .name = "fdomain",
- .proc_name = "fdomain",
- .detect = fdomain_16x0_detect,
- .info = fdomain_16x0_info,
- .queuecommand = fdomain_16x0_queue,
- .eh_abort_handler = fdomain_16x0_abort,
- .eh_host_reset_handler = fdomain_16x0_host_reset,
- .bios_param = fdomain_16x0_biosparam,
- .release = fdomain_16x0_release,
- .can_queue = 1,
- .this_id = 6,
- .sg_tablesize = 64,
- .use_clustering = DISABLE_CLUSTERING,
-};
-
-#ifndef PCMCIA
-#if defined(CONFIG_PCI) && defined(MODULE)
-
-static struct pci_device_id fdomain_pci_tbl[] = {
- { PCI_VENDOR_ID_FD, PCI_DEVICE_ID_FD_36C70,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
- { }
-};
-MODULE_DEVICE_TABLE(pci, fdomain_pci_tbl);
-#endif
-#define driver_template fdomain_driver_template
-#include "scsi_module.c"
-
-#endif
diff --git a/drivers/scsi/fdomain.h b/drivers/scsi/fdomain.h
deleted file mode 100644
index 5cbe86b573ae..000000000000
--- a/drivers/scsi/fdomain.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * fdomain.c -- Future Domain TMC-16x0 SCSI driver
- * Author: Rickard E. Faith, faith@cs.unc.edu
- * Copyright 1992-1996, 1998 Rickard E. Faith (faith@acm.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, 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.
- */
-
-extern struct scsi_host_template fdomain_driver_template;
-extern int fdomain_setup(char *str);
-extern struct Scsi_Host *__fdomain_16x0_detect(struct scsi_host_template *tpnt );
-extern int fdomain_16x0_host_reset(struct scsi_cmnd *SCpnt);
diff --git a/drivers/scsi/fnic/fnic_trace.c b/drivers/scsi/fnic/fnic_trace.c
index abddde11982b..98597b59c12a 100644
--- a/drivers/scsi/fnic/fnic_trace.c
+++ b/drivers/scsi/fnic/fnic_trace.c
@@ -296,7 +296,7 @@ int fnic_get_stats_data(struct stats_debug_info *debug,
"Number of Abort FW Timeouts: %lld\n"
"Number of Abort IO NOT Found: %lld\n"
- "Abord issued times: \n"
+ "Abort issued times: \n"
" < 6 sec : %lld\n"
" 6 sec - 20 sec : %lld\n"
" 20 sec - 30 sec : %lld\n"
diff --git a/drivers/scsi/gdth.h b/drivers/scsi/gdth.h
index 95fc720c1b30..e6e5ccb1e0f3 100644
--- a/drivers/scsi/gdth.h
+++ b/drivers/scsi/gdth.h
@@ -178,9 +178,6 @@
#define MSG_SIZE 34 /* size of message structure */
#define MSG_REQUEST 0 /* async. event: message */
-/* cacheservice defines */
-#define SECTOR_SIZE 0x200 /* always 512 bytes per sec. */
-
/* DPMEM constants */
#define DPMEM_MAGIC 0xC0FFEE11
#define IC_HEADER_BYTES 48
diff --git a/drivers/scsi/hisi_sas/Kconfig b/drivers/scsi/hisi_sas/Kconfig
index d42f29a5eb65..57183fce70fb 100644
--- a/drivers/scsi/hisi_sas/Kconfig
+++ b/drivers/scsi/hisi_sas/Kconfig
@@ -1,6 +1,6 @@
config SCSI_HISI_SAS
tristate "HiSilicon SAS"
- depends on HAS_DMA && HAS_IOMEM
+ depends on HAS_IOMEM
depends on ARM64 || COMPILE_TEST
select SCSI_SAS_LIBSAS
select BLK_DEV_INTEGRITY
diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h
index e7fd2877c19c..d1153e8e846b 100644
--- a/drivers/scsi/hisi_sas/hisi_sas.h
+++ b/drivers/scsi/hisi_sas/hisi_sas.h
@@ -175,7 +175,6 @@ struct hisi_sas_device {
struct hisi_sas_dq *dq;
struct list_head list;
u64 attached_phy;
- atomic64_t running_req;
enum sas_device_type dev_type;
int device_id;
int sata_idx;
diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c
index 2d4dbed03ee3..49c1fa643803 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_main.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_main.c
@@ -33,7 +33,7 @@ u8 hisi_sas_get_ata_protocol(struct host_to_dev_fis *fis, int direction)
case ATA_CMD_FPDMA_RECV:
case ATA_CMD_FPDMA_SEND:
case ATA_CMD_NCQ_NON_DATA:
- return HISI_SAS_SATA_PROTOCOL_FPDMA;
+ return HISI_SAS_SATA_PROTOCOL_FPDMA;
case ATA_CMD_DOWNLOAD_MICRO:
case ATA_CMD_ID_ATA:
@@ -45,7 +45,7 @@ u8 hisi_sas_get_ata_protocol(struct host_to_dev_fis *fis, int direction)
case ATA_CMD_WRITE_LOG_EXT:
case ATA_CMD_PIO_WRITE:
case ATA_CMD_PIO_WRITE_EXT:
- return HISI_SAS_SATA_PROTOCOL_PIO;
+ return HISI_SAS_SATA_PROTOCOL_PIO;
case ATA_CMD_DSM:
case ATA_CMD_DOWNLOAD_MICRO_DMA:
@@ -64,7 +64,7 @@ u8 hisi_sas_get_ata_protocol(struct host_to_dev_fis *fis, int direction)
case ATA_CMD_WRITE_LOG_DMA_EXT:
case ATA_CMD_WRITE_STREAM_DMA_EXT:
case ATA_CMD_ZAC_MGMT_IN:
- return HISI_SAS_SATA_PROTOCOL_DMA;
+ return HISI_SAS_SATA_PROTOCOL_DMA;
case ATA_CMD_CHK_POWER:
case ATA_CMD_DEV_RESET:
@@ -77,21 +77,21 @@ u8 hisi_sas_get_ata_protocol(struct host_to_dev_fis *fis, int direction)
case ATA_CMD_STANDBY:
case ATA_CMD_STANDBYNOW1:
case ATA_CMD_ZAC_MGMT_OUT:
- return HISI_SAS_SATA_PROTOCOL_NONDATA;
+ return HISI_SAS_SATA_PROTOCOL_NONDATA;
default:
{
if (fis->command == ATA_CMD_SET_MAX) {
switch (fis->features) {
case ATA_SET_MAX_PASSWD:
case ATA_SET_MAX_LOCK:
- return HISI_SAS_SATA_PROTOCOL_PIO;
+ return HISI_SAS_SATA_PROTOCOL_PIO;
case ATA_SET_MAX_PASSWD_DMA:
case ATA_SET_MAX_UNLOCK_DMA:
- return HISI_SAS_SATA_PROTOCOL_DMA;
+ return HISI_SAS_SATA_PROTOCOL_DMA;
default:
- return HISI_SAS_SATA_PROTOCOL_NONDATA;
+ return HISI_SAS_SATA_PROTOCOL_NONDATA;
}
}
if (direction == DMA_NONE)
@@ -200,8 +200,6 @@ void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba, struct sas_task *task,
if (task) {
struct device *dev = hisi_hba->dev;
- struct domain_device *device = task->dev;
- struct hisi_sas_device *sas_dev = device->lldd_dev;
if (!task->lldd_task)
return;
@@ -213,9 +211,6 @@ void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba, struct sas_task *task,
dma_unmap_sg(dev, task->scatter,
task->num_scatter,
task->data_dir);
-
- if (sas_dev)
- atomic64_dec(&sas_dev->running_req);
}
if (slot->buf)
@@ -321,7 +316,7 @@ static int hisi_sas_task_prep(struct sas_task *task, struct hisi_sas_dq
*/
if (device->dev_type != SAS_SATA_DEV)
task->task_done(task);
- return SAS_PHY_DOWN;
+ return -ECOMM;
}
if (DEV_IS_GONE(sas_dev)) {
@@ -332,7 +327,7 @@ static int hisi_sas_task_prep(struct sas_task *task, struct hisi_sas_dq
dev_info(dev, "task prep: device %016llx not ready\n",
SAS_ADDR(device->sas_addr));
- return SAS_PHY_DOWN;
+ return -ECOMM;
}
port = to_hisi_sas_port(sas_port);
@@ -342,7 +337,7 @@ static int hisi_sas_task_prep(struct sas_task *task, struct hisi_sas_dq
"SATA/STP" : "SAS",
device->port->id);
- return SAS_PHY_DOWN;
+ return -ECOMM;
}
if (!sas_protocol_ata(task->task_proto)) {
@@ -431,8 +426,6 @@ static int hisi_sas_task_prep(struct sas_task *task, struct hisi_sas_dq
spin_unlock_irqrestore(&task->task_state_lock, flags);
dq->slot_prep = slot;
-
- atomic64_inc(&sas_dev->running_req);
++(*pass);
return 0;
@@ -683,6 +676,8 @@ static void hisi_sas_phy_init(struct hisi_hba *hisi_hba, int phy_no)
phy->hisi_hba = hisi_hba;
phy->port = NULL;
+ phy->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS;
+ phy->maximum_linkrate = hisi_hba->hw->phy_get_max_linkrate();
sas_phy->enabled = (phy_no < hisi_hba->n_phy) ? 1 : 0;
sas_phy->class = SAS;
sas_phy->iproto = SAS_PROTOCOL_ALL;
@@ -869,6 +864,7 @@ static void hisi_sas_tmf_timedout(struct timer_list *t)
#define TASK_TIMEOUT 20
#define TASK_RETRY 3
+#define INTERNAL_ABORT_TIMEOUT 6
static int hisi_sas_exec_internal_tmf_task(struct domain_device *device,
void *parameter, u32 para_len,
struct hisi_sas_tmf_task *tmf)
@@ -1514,8 +1510,6 @@ hisi_sas_internal_abort_task_exec(struct hisi_hba *hisi_hba, int device_id,
dq->slot_prep = slot;
- atomic64_inc(&sas_dev->running_req);
-
/* send abort command to the chip */
hisi_hba->hw->start_delivery(dq);
spin_unlock_irqrestore(&dq->lock, flags_dq);
@@ -1572,7 +1566,7 @@ hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba,
task->task_proto = device->tproto;
task->task_done = hisi_sas_task_done;
task->slow_task->timer.function = hisi_sas_tmf_timedout;
- task->slow_task->timer.expires = jiffies + msecs_to_jiffies(110);
+ task->slow_task->timer.expires = jiffies + INTERNAL_ABORT_TIMEOUT*HZ;
add_timer(&task->slow_task->timer);
res = hisi_sas_internal_abort_task_exec(hisi_hba, sas_dev->device_id,
diff --git a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c
index 679e76f58a0a..84a0ccc4daf5 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c
@@ -651,8 +651,10 @@ static int reset_hw_v1_hw(struct hisi_hba *hisi_hba)
dev_err(dev, "De-reset failed\n");
return -EIO;
}
- } else
+ } else {
dev_warn(dev, "no reset method\n");
+ return -EINVAL;
+ }
return 0;
}
@@ -873,7 +875,6 @@ static void phy_set_linkrate_v1_hw(struct hisi_hba *hisi_hba, int phy_no,
sas_phy->phy->maximum_linkrate = max;
sas_phy->phy->minimum_linkrate = min;
- min -= SAS_LINK_RATE_1_5_GBPS;
max -= SAS_LINK_RATE_1_5_GBPS;
for (i = 0; i <= max; i++)
@@ -882,10 +883,11 @@ static void phy_set_linkrate_v1_hw(struct hisi_hba *hisi_hba, int phy_no,
prog_phy_link_rate &= ~0xff;
prog_phy_link_rate |= rate_mask;
+ disable_phy_v1_hw(hisi_hba, phy_no);
+ msleep(100);
hisi_sas_phy_write32(hisi_hba, phy_no, PROG_PHY_LINK_RATE,
prog_phy_link_rate);
-
- phy_hard_reset_v1_hw(hisi_hba, phy_no);
+ start_phy_v1_hw(hisi_hba, phy_no);
}
static int get_wideport_bitmap_v1_hw(struct hisi_hba *hisi_hba, int port_id)
@@ -1407,9 +1409,6 @@ static int slot_complete_v1_hw(struct hisi_hba *hisi_hba,
}
out:
- if (sas_dev)
- atomic64_dec(&sas_dev->running_req);
-
hisi_sas_slot_task_free(hisi_hba, task, slot);
sts = ts->stat;
diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
index 4ccb61e2ae5c..f89fb9a49ea9 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
@@ -406,6 +406,17 @@ struct hisi_sas_err_record_v2 {
__le32 dma_rx_err_type;
};
+struct signal_attenuation_s {
+ u32 de_emphasis;
+ u32 preshoot;
+ u32 boost;
+};
+
+struct sig_atten_lu_s {
+ const struct signal_attenuation_s *att;
+ u32 sas_phy_ctrl;
+};
+
static const struct hisi_sas_hw_error one_bit_ecc_errors[] = {
{
.irq_msk = BIT(SAS_ECC_INTR_DQE_ECC_1B_OFF),
@@ -1084,8 +1095,10 @@ static int reset_hw_v2_hw(struct hisi_hba *hisi_hba)
dev_err(dev, "SAS de-reset fail.\n");
return -EIO;
}
- } else
- dev_warn(dev, "no reset method\n");
+ } else {
+ dev_err(dev, "no reset method\n");
+ return -EINVAL;
+ }
return 0;
}
@@ -1130,9 +1143,16 @@ static void phys_try_accept_stp_links_v2_hw(struct hisi_hba *hisi_hba)
}
}
+static const struct signal_attenuation_s x6000 = {9200, 0, 10476};
+static const struct sig_atten_lu_s sig_atten_lu[] = {
+ { &x6000, 0x3016a68 },
+};
+
static void init_reg_v2_hw(struct hisi_hba *hisi_hba)
{
struct device *dev = hisi_hba->dev;
+ u32 sas_phy_ctrl = 0x30b9908;
+ u32 signal[3];
int i;
/* Global registers init */
@@ -1176,9 +1196,28 @@ static void init_reg_v2_hw(struct hisi_hba *hisi_hba)
hisi_sas_write32(hisi_hba, AXI_AHB_CLK_CFG, 1);
hisi_sas_write32(hisi_hba, HYPER_STREAM_ID_EN_CFG, 1);
+ /* Get sas_phy_ctrl value to deal with TX FFE issue. */
+ if (!device_property_read_u32_array(dev, "hisilicon,signal-attenuation",
+ signal, ARRAY_SIZE(signal))) {
+ for (i = 0; i < ARRAY_SIZE(sig_atten_lu); i++) {
+ const struct sig_atten_lu_s *lookup = &sig_atten_lu[i];
+ const struct signal_attenuation_s *att = lookup->att;
+
+ if ((signal[0] == att->de_emphasis) &&
+ (signal[1] == att->preshoot) &&
+ (signal[2] == att->boost)) {
+ sas_phy_ctrl = lookup->sas_phy_ctrl;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(sig_atten_lu))
+ dev_warn(dev, "unknown signal attenuation values, using default PHY ctrl config\n");
+ }
+
for (i = 0; i < hisi_hba->n_phy; i++) {
hisi_sas_phy_write32(hisi_hba, i, PROG_PHY_LINK_RATE, 0x855);
- hisi_sas_phy_write32(hisi_hba, i, SAS_PHY_CTRL, 0x30b9908);
+ hisi_sas_phy_write32(hisi_hba, i, SAS_PHY_CTRL, sas_phy_ctrl);
hisi_sas_phy_write32(hisi_hba, i, SL_TOUT_CFG, 0x7d7d7d7d);
hisi_sas_phy_write32(hisi_hba, i, SL_CONTROL, 0x0);
hisi_sas_phy_write32(hisi_hba, i, TXID_AUTO, 0x2);
@@ -1566,7 +1605,6 @@ static void phy_set_linkrate_v2_hw(struct hisi_hba *hisi_hba, int phy_no,
sas_phy->phy->maximum_linkrate = max;
sas_phy->phy->minimum_linkrate = min;
- min -= SAS_LINK_RATE_1_5_GBPS;
max -= SAS_LINK_RATE_1_5_GBPS;
for (i = 0; i <= max; i++)
@@ -1575,10 +1613,11 @@ static void phy_set_linkrate_v2_hw(struct hisi_hba *hisi_hba, int phy_no,
prog_phy_link_rate &= ~0xff;
prog_phy_link_rate |= rate_mask;
+ disable_phy_v2_hw(hisi_hba, phy_no);
+ msleep(100);
hisi_sas_phy_write32(hisi_hba, phy_no, PROG_PHY_LINK_RATE,
prog_phy_link_rate);
-
- phy_hard_reset_v2_hw(hisi_hba, phy_no);
+ start_phy_v2_hw(hisi_hba, phy_no);
}
static int get_wideport_bitmap_v2_hw(struct hisi_hba *hisi_hba, int port_id)
@@ -2371,7 +2410,7 @@ slot_complete_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot)
spin_lock_irqsave(&hisi_hba->lock, flags);
hisi_sas_slot_task_free(hisi_hba, task, slot);
spin_unlock_irqrestore(&hisi_hba->lock, flags);
- return -1;
+ return ts->stat;
}
if (unlikely(!sas_dev)) {
@@ -2630,7 +2669,7 @@ static int prep_abort_v2_hw(struct hisi_hba *hisi_hba,
/* dw0 */
hdr->dw0 = cpu_to_le32((5 << CMD_HDR_CMD_OFF) | /*abort*/
(port->id << CMD_HDR_PORT_OFF) |
- ((dev_is_sata(dev) ? 1:0) <<
+ (dev_is_sata(dev) <<
CMD_HDR_ABORT_DEVICE_TYPE_OFF) |
(abort_flag << CMD_HDR_ABORT_FLAG_OFF));
@@ -2647,7 +2686,7 @@ static int prep_abort_v2_hw(struct hisi_hba *hisi_hba,
static int phy_up_v2_hw(int phy_no, struct hisi_hba *hisi_hba)
{
int i, res = IRQ_HANDLED;
- u32 port_id, link_rate, hard_phy_linkrate;
+ u32 port_id, link_rate;
struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
struct asd_sas_phy *sas_phy = &phy->sas_phy;
struct device *dev = hisi_hba->dev;
@@ -2686,11 +2725,6 @@ static int phy_up_v2_hw(int phy_no, struct hisi_hba *hisi_hba)
}
sas_phy->linkrate = link_rate;
- hard_phy_linkrate = hisi_sas_phy_read32(hisi_hba, phy_no,
- HARD_PHY_LINKRATE);
- phy->maximum_linkrate = hard_phy_linkrate & 0xf;
- phy->minimum_linkrate = (hard_phy_linkrate >> 4) & 0xf;
-
sas_phy->oob_mode = SAS_OOB_MODE;
memcpy(sas_phy->attached_sas_addr, &id->sas_addr, SAS_ADDR_SIZE);
dev_info(dev, "phyup: phy%d link_rate=%d\n", phy_no, link_rate);
diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
index a1f18689729a..6f3e5ba6b472 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
@@ -172,6 +172,7 @@
#define CHL_INT1_MSK (PORT_BASE + 0x1c4)
#define CHL_INT2_MSK (PORT_BASE + 0x1c8)
#define CHL_INT_COAL_EN (PORT_BASE + 0x1d0)
+#define SAS_RX_TRAIN_TIMER (PORT_BASE + 0x2a4)
#define PHY_CTRL_RDY_MSK (PORT_BASE + 0x2b0)
#define PHYCTRL_NOT_RDY_MSK (PORT_BASE + 0x2b4)
#define PHYCTRL_DWS_RESET_MSK (PORT_BASE + 0x2b8)
@@ -184,6 +185,8 @@
#define DMA_RX_STATUS (PORT_BASE + 0x2e8)
#define DMA_RX_STATUS_BUSY_OFF 0
#define DMA_RX_STATUS_BUSY_MSK (0x1 << DMA_RX_STATUS_BUSY_OFF)
+
+#define COARSETUNE_TIME (PORT_BASE + 0x304)
#define ERR_CNT_DWS_LOST (PORT_BASE + 0x380)
#define ERR_CNT_RESET_PROB (PORT_BASE + 0x384)
#define ERR_CNT_INVLD_DW (PORT_BASE + 0x390)
@@ -340,12 +343,6 @@ struct hisi_sas_err_record_v3 {
#define HISI_SAS_COMMAND_ENTRIES_V3_HW 4096
#define HISI_SAS_MSI_COUNT_V3_HW 32
-enum {
- HISI_SAS_PHY_PHY_UPDOWN,
- HISI_SAS_PHY_CHNL_INT,
- HISI_SAS_PHY_INT_NR
-};
-
#define DIR_NO_DATA 0
#define DIR_TO_INI 1
#define DIR_TO_DEVICE 2
@@ -423,10 +420,10 @@ static void init_reg_v3_hw(struct hisi_hba *hisi_hba)
hisi_sas_write32(hisi_hba, OQ0_INT_SRC_MSK+0x4*i, 0);
hisi_sas_write32(hisi_hba, HYPER_STREAM_ID_EN_CFG, 1);
- hisi_sas_write32(hisi_hba, AXI_MASTER_CFG_BASE, 0x30000);
for (i = 0; i < hisi_hba->n_phy; i++) {
- hisi_sas_phy_write32(hisi_hba, i, PROG_PHY_LINK_RATE, 0x801);
+ hisi_sas_phy_write32(hisi_hba, i, PROG_PHY_LINK_RATE, 0x855);
+ hisi_sas_phy_write32(hisi_hba, i, SAS_RX_TRAIN_TIMER, 0x13e80);
hisi_sas_phy_write32(hisi_hba, i, CHL_INT0, 0xffffffff);
hisi_sas_phy_write32(hisi_hba, i, CHL_INT1, 0xffffffff);
hisi_sas_phy_write32(hisi_hba, i, CHL_INT2, 0xffffffff);
@@ -438,17 +435,13 @@ static void init_reg_v3_hw(struct hisi_hba *hisi_hba)
hisi_sas_phy_write32(hisi_hba, i, PHYCTRL_DWS_RESET_MSK, 0x0);
hisi_sas_phy_write32(hisi_hba, i, PHYCTRL_PHY_ENA_MSK, 0x0);
hisi_sas_phy_write32(hisi_hba, i, SL_RX_BCAST_CHK_MSK, 0x0);
- hisi_sas_phy_write32(hisi_hba, i, PHYCTRL_OOB_RESTART_MSK, 0x0);
- hisi_sas_phy_write32(hisi_hba, i, PHY_CTRL, 0x199b4fa);
- hisi_sas_phy_write32(hisi_hba, i, SAS_SSP_CON_TIMER_CFG,
- 0xa03e8);
- hisi_sas_phy_write32(hisi_hba, i, SAS_STP_CON_TIMER_CFG,
- 0xa03e8);
- hisi_sas_phy_write32(hisi_hba, i, STP_LINK_TIMER,
- 0x7f7a120);
- hisi_sas_phy_write32(hisi_hba, i, CON_CFG_DRIVER,
- 0x2a0a80);
+ hisi_sas_phy_write32(hisi_hba, i, PHYCTRL_OOB_RESTART_MSK, 0x1);
+ hisi_sas_phy_write32(hisi_hba, i, STP_LINK_TIMER, 0x7f7a120);
+
+ /* used for 12G negotiate */
+ hisi_sas_phy_write32(hisi_hba, i, COARSETUNE_TIME, 0x1e);
}
+
for (i = 0; i < hisi_hba->queue_count; i++) {
/* Delivery queue */
hisi_sas_write32(hisi_hba,
@@ -676,8 +669,10 @@ static int reset_hw_v3_hw(struct hisi_hba *hisi_hba)
dev_err(dev, "Reset failed\n");
return -EIO;
}
- } else
+ } else {
dev_err(dev, "no reset method!\n");
+ return -EINVAL;
+ }
return 0;
}
@@ -737,7 +732,7 @@ static void phy_hard_reset_v3_hw(struct hisi_hba *hisi_hba, int phy_no)
start_phy_v3_hw(hisi_hba, phy_no);
}
-enum sas_linkrate phy_get_max_linkrate_v3_hw(void)
+static enum sas_linkrate phy_get_max_linkrate_v3_hw(void)
{
return SAS_LINK_RATE_12_0_GBPS;
}
@@ -1102,7 +1097,7 @@ static int prep_abort_v3_hw(struct hisi_hba *hisi_hba,
/* dw0 */
hdr->dw0 = cpu_to_le32((5 << CMD_HDR_CMD_OFF) | /*abort*/
(port->id << CMD_HDR_PORT_OFF) |
- ((dev_is_sata(dev) ? 1:0)
+ (dev_is_sata(dev)
<< CMD_HDR_ABORT_DEVICE_TYPE_OFF) |
(abort_flag
<< CMD_HDR_ABORT_FLAG_OFF));
@@ -1118,10 +1113,10 @@ static int prep_abort_v3_hw(struct hisi_hba *hisi_hba,
return 0;
}
-static int phy_up_v3_hw(int phy_no, struct hisi_hba *hisi_hba)
+static irqreturn_t phy_up_v3_hw(int phy_no, struct hisi_hba *hisi_hba)
{
- int i, res = 0;
- u32 context, port_id, link_rate, hard_phy_linkrate;
+ int i, res;
+ u32 context, port_id, link_rate;
struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
struct asd_sas_phy *sas_phy = &phy->sas_phy;
struct device *dev = hisi_hba->dev;
@@ -1139,10 +1134,6 @@ static int phy_up_v3_hw(int phy_no, struct hisi_hba *hisi_hba)
goto end;
}
sas_phy->linkrate = link_rate;
- hard_phy_linkrate = hisi_sas_phy_read32(hisi_hba, phy_no,
- HARD_PHY_LINKRATE);
- phy->maximum_linkrate = hard_phy_linkrate & 0xf;
- phy->minimum_linkrate = (hard_phy_linkrate >> 4) & 0xf;
phy->phy_type &= ~(PORT_TYPE_SAS | PORT_TYPE_SATA);
/* Check for SATA dev */
@@ -1196,7 +1187,7 @@ static int phy_up_v3_hw(int phy_no, struct hisi_hba *hisi_hba)
phy->port_id = port_id;
phy->phy_attached = 1;
hisi_sas_notify_phy_event(phy, HISI_PHYE_PHY_UP);
-
+ res = IRQ_HANDLED;
end:
hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0,
CHL_INT0_SL_PHY_ENABLE_MSK);
@@ -1205,7 +1196,7 @@ end:
return res;
}
-static int phy_down_v3_hw(int phy_no, struct hisi_hba *hisi_hba)
+static irqreturn_t phy_down_v3_hw(int phy_no, struct hisi_hba *hisi_hba)
{
u32 phy_state, sl_ctrl, txid_auto;
struct device *dev = hisi_hba->dev;
@@ -1227,10 +1218,10 @@ static int phy_down_v3_hw(int phy_no, struct hisi_hba *hisi_hba)
hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0, CHL_INT0_NOT_RDY_MSK);
hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_NOT_RDY_MSK, 0);
- return 0;
+ return IRQ_HANDLED;
}
-static void phy_bcast_v3_hw(int phy_no, struct hisi_hba *hisi_hba)
+static irqreturn_t phy_bcast_v3_hw(int phy_no, struct hisi_hba *hisi_hba)
{
struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
struct asd_sas_phy *sas_phy = &phy->sas_phy;
@@ -1241,6 +1232,8 @@ static void phy_bcast_v3_hw(int phy_no, struct hisi_hba *hisi_hba)
hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0,
CHL_INT0_SL_RX_BCST_ACK_MSK);
hisi_sas_phy_write32(hisi_hba, phy_no, SL_RX_BCAST_CHK_MSK, 0);
+
+ return IRQ_HANDLED;
}
static irqreturn_t int_phy_up_down_bcast_v3_hw(int irq_no, void *p)
@@ -1267,7 +1260,9 @@ static irqreturn_t int_phy_up_down_bcast_v3_hw(int irq_no, void *p)
res = IRQ_HANDLED;
if (irq_value & CHL_INT0_SL_RX_BCST_ACK_MSK)
/* phy bcast */
- phy_bcast_v3_hw(phy_no, hisi_hba);
+ if (phy_bcast_v3_hw(phy_no, hisi_hba)
+ == IRQ_HANDLED)
+ res = IRQ_HANDLED;
} else {
if (irq_value & CHL_INT0_NOT_RDY_MSK)
/* phy down */
@@ -1583,7 +1578,7 @@ slot_complete_v3_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot)
spin_lock_irqsave(&hisi_hba->lock, flags);
hisi_sas_slot_task_free(hisi_hba, task, slot);
spin_unlock_irqrestore(&hisi_hba->lock, flags);
- return -1;
+ return ts->stat;
}
if (unlikely(!sas_dev)) {
@@ -1864,7 +1859,6 @@ static void phy_set_linkrate_v3_hw(struct hisi_hba *hisi_hba, int phy_no,
sas_phy->phy->maximum_linkrate = max;
sas_phy->phy->minimum_linkrate = min;
- min -= SAS_LINK_RATE_1_5_GBPS;
max -= SAS_LINK_RATE_1_5_GBPS;
for (i = 0; i <= max; i++)
@@ -1873,10 +1867,11 @@ static void phy_set_linkrate_v3_hw(struct hisi_hba *hisi_hba, int phy_no,
prog_phy_link_rate &= ~0xff;
prog_phy_link_rate |= rate_mask;
+ disable_phy_v3_hw(hisi_hba, phy_no);
+ msleep(100);
hisi_sas_phy_write32(hisi_hba, phy_no, PROG_PHY_LINK_RATE,
prog_phy_link_rate);
-
- phy_hard_reset_v3_hw(hisi_hba, phy_no);
+ start_phy_v3_hw(hisi_hba, phy_no);
}
static void interrupt_disable_v3_hw(struct hisi_hba *hisi_hba)
@@ -2399,6 +2394,7 @@ static const struct pci_device_id sas_v3_pci_table[] = {
{ PCI_VDEVICE(HUAWEI, 0xa230), hip08 },
{}
};
+MODULE_DEVICE_TABLE(pci, sas_v3_pci_table);
static const struct pci_error_handlers hisi_sas_err_handler = {
.error_detected = hisi_sas_error_detected_v3_hw,
@@ -2421,4 +2417,4 @@ module_pci_driver(sas_v3_pci_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("John Garry <john.garry@huawei.com>");
MODULE_DESCRIPTION("HISILICON SAS controller v3 hw driver based on pci device");
-MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_ALIAS("pci:" DRV_NAME);
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
index dd9464920456..3771e59a9fae 100644
--- a/drivers/scsi/hosts.c
+++ b/drivers/scsi/hosts.c
@@ -42,6 +42,12 @@
#include "scsi_logging.h"
+static int shost_eh_deadline = -1;
+
+module_param_named(eh_deadline, shost_eh_deadline, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(eh_deadline,
+ "SCSI EH timeout in seconds (should be between 0 and 2^31-1)");
+
static DEFINE_IDA(host_index_ida);
@@ -148,7 +154,6 @@ int scsi_host_set_state(struct Scsi_Host *shost, enum scsi_host_state state)
scsi_host_state_name(state)));
return -EINVAL;
}
-EXPORT_SYMBOL(scsi_host_set_state);
/**
* scsi_remove_host - remove a scsi host
@@ -356,12 +361,6 @@ static void scsi_host_dev_release(struct device *dev)
kfree(shost);
}
-static int shost_eh_deadline = -1;
-
-module_param_named(eh_deadline, shost_eh_deadline, int, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(eh_deadline,
- "SCSI EH timeout in seconds (should be between 0 and 2^31-1)");
-
static struct device_type scsi_host_type = {
.name = "scsi_host",
.release = scsi_host_dev_release,
@@ -473,7 +472,7 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
else
shost->dma_boundary = 0xffffffff;
- shost->use_blk_mq = scsi_use_blk_mq;
+ shost->use_blk_mq = scsi_use_blk_mq || shost->hostt->force_blk_mq;
device_initialize(&shost->shost_gendev);
dev_set_name(&shost->shost_gendev, "host%d", shost->host_no);
@@ -516,29 +515,6 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
}
EXPORT_SYMBOL(scsi_host_alloc);
-struct Scsi_Host *scsi_register(struct scsi_host_template *sht, int privsize)
-{
- struct Scsi_Host *shost = scsi_host_alloc(sht, privsize);
-
- if (!sht->detect) {
- printk(KERN_WARNING "scsi_register() called on new-style "
- "template for driver %s\n", sht->name);
- dump_stack();
- }
-
- if (shost)
- list_add_tail(&shost->sht_legacy_list, &sht->legacy_hosts);
- return shost;
-}
-EXPORT_SYMBOL(scsi_register);
-
-void scsi_unregister(struct Scsi_Host *shost)
-{
- list_del(&shost->sht_legacy_list);
- scsi_host_put(shost);
-}
-EXPORT_SYMBOL(scsi_unregister);
-
static int __scsi_host_match(struct device *dev, const void *data)
{
struct Scsi_Host *p;
diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
index 5293e6827ce5..3a9eca163db8 100644
--- a/drivers/scsi/hpsa.c
+++ b/drivers/scsi/hpsa.c
@@ -1045,11 +1045,7 @@ static void set_performant_mode(struct ctlr_info *h, struct CommandList *c,
c->busaddr |= 1 | (h->blockFetchTable[c->Header.SGList] << 1);
if (unlikely(!h->msix_vectors))
return;
- if (likely(reply_queue == DEFAULT_REPLY_QUEUE))
- c->Header.ReplyQueue =
- raw_smp_processor_id() % h->nreply_queues;
- else
- c->Header.ReplyQueue = reply_queue % h->nreply_queues;
+ c->Header.ReplyQueue = reply_queue;
}
}
@@ -1063,10 +1059,7 @@ static void set_ioaccel1_performant_mode(struct ctlr_info *h,
* Tell the controller to post the reply to the queue for this
* processor. This seems to give the best I/O throughput.
*/
- if (likely(reply_queue == DEFAULT_REPLY_QUEUE))
- cp->ReplyQueue = smp_processor_id() % h->nreply_queues;
- else
- cp->ReplyQueue = reply_queue % h->nreply_queues;
+ cp->ReplyQueue = reply_queue;
/*
* Set the bits in the address sent down to include:
* - performant mode bit (bit 0)
@@ -1087,10 +1080,7 @@ static void set_ioaccel2_tmf_performant_mode(struct ctlr_info *h,
/* Tell the controller to post the reply to the queue for this
* processor. This seems to give the best I/O throughput.
*/
- if (likely(reply_queue == DEFAULT_REPLY_QUEUE))
- cp->reply_queue = smp_processor_id() % h->nreply_queues;
- else
- cp->reply_queue = reply_queue % h->nreply_queues;
+ cp->reply_queue = reply_queue;
/* Set the bits in the address sent down to include:
* - performant mode bit not used in ioaccel mode 2
* - pull count (bits 0-3)
@@ -1109,10 +1099,7 @@ static void set_ioaccel2_performant_mode(struct ctlr_info *h,
* Tell the controller to post the reply to the queue for this
* processor. This seems to give the best I/O throughput.
*/
- if (likely(reply_queue == DEFAULT_REPLY_QUEUE))
- cp->reply_queue = smp_processor_id() % h->nreply_queues;
- else
- cp->reply_queue = reply_queue % h->nreply_queues;
+ cp->reply_queue = reply_queue;
/*
* Set the bits in the address sent down to include:
* - performant mode bit not used in ioaccel mode 2
@@ -1157,6 +1144,8 @@ static void __enqueue_cmd_and_start_io(struct ctlr_info *h,
{
dial_down_lockup_detection_during_fw_flash(h, c);
atomic_inc(&h->commands_outstanding);
+
+ reply_queue = h->reply_map[raw_smp_processor_id()];
switch (c->cmd_type) {
case CMD_IOACCEL1:
set_ioaccel1_performant_mode(h, c, reply_queue);
@@ -7376,6 +7365,26 @@ static void hpsa_disable_interrupt_mode(struct ctlr_info *h)
h->msix_vectors = 0;
}
+static void hpsa_setup_reply_map(struct ctlr_info *h)
+{
+ const struct cpumask *mask;
+ unsigned int queue, cpu;
+
+ for (queue = 0; queue < h->msix_vectors; queue++) {
+ mask = pci_irq_get_affinity(h->pdev, queue);
+ if (!mask)
+ goto fallback;
+
+ for_each_cpu(cpu, mask)
+ h->reply_map[cpu] = queue;
+ }
+ return;
+
+fallback:
+ for_each_possible_cpu(cpu)
+ h->reply_map[cpu] = 0;
+}
+
/* If MSI/MSI-X is supported by the kernel we will try to enable it on
* controllers that are capable. If not, we use legacy INTx mode.
*/
@@ -7771,6 +7780,10 @@ static int hpsa_pci_init(struct ctlr_info *h)
err = hpsa_interrupt_mode(h);
if (err)
goto clean1;
+
+ /* setup mapping between CPU and reply queue */
+ hpsa_setup_reply_map(h);
+
err = hpsa_pci_find_memory_BAR(h->pdev, &h->paddr);
if (err)
goto clean2; /* intmode+region, pci */
@@ -8480,6 +8493,28 @@ static struct workqueue_struct *hpsa_create_controller_wq(struct ctlr_info *h,
return wq;
}
+static void hpda_free_ctlr_info(struct ctlr_info *h)
+{
+ kfree(h->reply_map);
+ kfree(h);
+}
+
+static struct ctlr_info *hpda_alloc_ctlr_info(void)
+{
+ struct ctlr_info *h;
+
+ h = kzalloc(sizeof(*h), GFP_KERNEL);
+ if (!h)
+ return NULL;
+
+ h->reply_map = kzalloc(sizeof(*h->reply_map) * nr_cpu_ids, GFP_KERNEL);
+ if (!h->reply_map) {
+ kfree(h);
+ return NULL;
+ }
+ return h;
+}
+
static int hpsa_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
int dac, rc;
@@ -8517,7 +8552,7 @@ reinit_after_soft_reset:
* the driver. See comments in hpsa.h for more info.
*/
BUILD_BUG_ON(sizeof(struct CommandList) % COMMANDLIST_ALIGNMENT);
- h = kzalloc(sizeof(*h), GFP_KERNEL);
+ h = hpda_alloc_ctlr_info();
if (!h) {
dev_err(&pdev->dev, "Failed to allocate controller head\n");
return -ENOMEM;
@@ -8916,7 +8951,7 @@ static void hpsa_remove_one(struct pci_dev *pdev)
h->lockup_detected = NULL; /* init_one 2 */
/* (void) pci_disable_pcie_error_reporting(pdev); */ /* init_one 1 */
- kfree(h); /* init_one 1 */
+ hpda_free_ctlr_info(h); /* init_one 1 */
}
static int hpsa_suspend(__attribute__((unused)) struct pci_dev *pdev,
diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h
index 018f980a701c..fb9f5e7f8209 100644
--- a/drivers/scsi/hpsa.h
+++ b/drivers/scsi/hpsa.h
@@ -158,6 +158,7 @@ struct bmic_controller_parameters {
#pragma pack()
struct ctlr_info {
+ unsigned int *reply_map;
int ctlr;
char devname[8];
char *product_name;
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index b1b1d3a3b173..daefe8172b04 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -3579,11 +3579,9 @@ static void ibmvfc_tgt_implicit_logout(struct ibmvfc_target *tgt)
static int ibmvfc_adisc_needs_plogi(struct ibmvfc_passthru_mad *mad,
struct ibmvfc_target *tgt)
{
- if (memcmp(&mad->fc_iu.response[2], &tgt->ids.port_name,
- sizeof(tgt->ids.port_name)))
+ if (wwn_to_u64((u8 *)&mad->fc_iu.response[2]) != tgt->ids.port_name)
return 1;
- if (memcmp(&mad->fc_iu.response[4], &tgt->ids.node_name,
- sizeof(tgt->ids.node_name)))
+ if (wwn_to_u64((u8 *)&mad->fc_iu.response[4]) != tgt->ids.node_name)
return 1;
if (be32_to_cpu(mad->fc_iu.response[6]) != tgt->scsi_id)
return 1;
diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c
index e07dd990e585..dda1a64ab89c 100644
--- a/drivers/scsi/ipr.c
+++ b/drivers/scsi/ipr.c
@@ -3816,10 +3816,8 @@ static struct device_attribute ipr_iopoll_weight_attr = {
**/
static struct ipr_sglist *ipr_alloc_ucode_buffer(int buf_len)
{
- int sg_size, order, bsize_elem, num_elem, i, j;
+ int sg_size, order;
struct ipr_sglist *sglist;
- struct scatterlist *scatterlist;
- struct page *page;
/* Get the minimum size per scatter/gather element */
sg_size = buf_len / (IPR_MAX_SGLIST - 1);
@@ -3827,45 +3825,18 @@ static struct ipr_sglist *ipr_alloc_ucode_buffer(int buf_len)
/* Get the actual size per element */
order = get_order(sg_size);
- /* Determine the actual number of bytes per element */
- bsize_elem = PAGE_SIZE * (1 << order);
-
- /* Determine the actual number of sg entries needed */
- if (buf_len % bsize_elem)
- num_elem = (buf_len / bsize_elem) + 1;
- else
- num_elem = buf_len / bsize_elem;
-
/* Allocate a scatter/gather list for the DMA */
- sglist = kzalloc(sizeof(struct ipr_sglist) +
- (sizeof(struct scatterlist) * (num_elem - 1)),
- GFP_KERNEL);
-
+ sglist = kzalloc(sizeof(struct ipr_sglist), GFP_KERNEL);
if (sglist == NULL) {
ipr_trace;
return NULL;
}
-
- scatterlist = sglist->scatterlist;
- sg_init_table(scatterlist, num_elem);
-
sglist->order = order;
- sglist->num_sg = num_elem;
-
- /* Allocate a bunch of sg elements */
- for (i = 0; i < num_elem; i++) {
- page = alloc_pages(GFP_KERNEL, order);
- if (!page) {
- ipr_trace;
-
- /* Free up what we already allocated */
- for (j = i - 1; j >= 0; j--)
- __free_pages(sg_page(&scatterlist[j]), order);
- kfree(sglist);
- return NULL;
- }
-
- sg_set_page(&scatterlist[i], page, 0, 0);
+ sglist->scatterlist = sgl_alloc_order(buf_len, order, false, GFP_KERNEL,
+ &sglist->num_sg);
+ if (!sglist->scatterlist) {
+ kfree(sglist);
+ return NULL;
}
return sglist;
@@ -3883,11 +3854,7 @@ static struct ipr_sglist *ipr_alloc_ucode_buffer(int buf_len)
**/
static void ipr_free_ucode_buffer(struct ipr_sglist *sglist)
{
- int i;
-
- for (i = 0; i < sglist->num_sg; i++)
- __free_pages(sg_page(&sglist->scatterlist[i]), sglist->order);
-
+ sgl_free_order(sglist->scatterlist, sglist->order);
kfree(sglist);
}
@@ -9684,14 +9651,14 @@ static int ipr_alloc_cmd_blks(struct ipr_ioa_cfg *ioa_cfg)
}
for (i = 0; i < IPR_NUM_CMD_BLKS; i++) {
- ipr_cmd = dma_pool_alloc(ioa_cfg->ipr_cmd_pool, GFP_KERNEL, &dma_addr);
+ ipr_cmd = dma_pool_zalloc(ioa_cfg->ipr_cmd_pool,
+ GFP_KERNEL, &dma_addr);
if (!ipr_cmd) {
ipr_free_cmd_blks(ioa_cfg);
return -ENOMEM;
}
- memset(ipr_cmd, 0, sizeof(*ipr_cmd));
ioa_cfg->ipr_cmnd_list[i] = ipr_cmd;
ioa_cfg->ipr_cmnd_list_dma[i] = dma_addr;
diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h
index c7f0e9e3cd7d..93570734cbfb 100644
--- a/drivers/scsi/ipr.h
+++ b/drivers/scsi/ipr.h
@@ -1454,7 +1454,7 @@ struct ipr_sglist {
u32 num_sg;
u32 num_dma_sg;
u32 buffer_len;
- struct scatterlist scatterlist[1];
+ struct scatterlist *scatterlist;
};
enum ipr_sdt_state {
diff --git a/drivers/scsi/ips.c b/drivers/scsi/ips.c
index 67621308eb9c..e3c8857741a1 100644
--- a/drivers/scsi/ips.c
+++ b/drivers/scsi/ips.c
@@ -224,8 +224,6 @@ module_param(ips, charp, 0);
/*
* Function prototypes
*/
-static int ips_detect(struct scsi_host_template *);
-static int ips_release(struct Scsi_Host *);
static int ips_eh_abort(struct scsi_cmnd *);
static int ips_eh_reset(struct scsi_cmnd *);
static int ips_queue(struct Scsi_Host *, struct scsi_cmnd *);
@@ -355,8 +353,6 @@ static dma_addr_t ips_flashbusaddr;
static long ips_FlashDataInUse; /* CD Boot - Flash Data In Use Flag */
static uint32_t MaxLiteCmds = 32; /* Max Active Cmds for a Lite Adapter */
static struct scsi_host_template ips_driver_template = {
- .detect = ips_detect,
- .release = ips_release,
.info = ips_info,
.queuecommand = ips_queue,
.eh_abort_handler = ips_eh_abort,
diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c
index 13b37cdffa8e..1ee3868ade07 100644
--- a/drivers/scsi/isci/host.c
+++ b/drivers/scsi/isci/host.c
@@ -2766,7 +2766,7 @@ static int sci_write_gpio_tx_gp(struct isci_host *ihost, u8 reg_index, u8 reg_co
int i;
for (i = 0; i < 3; i++) {
- int bit = (i << 2) + 2;
+ int bit;
bit = try_test_sas_gpio_gp_bit(to_sas_gpio_od(d, i),
write_data, reg_index,
diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
index 6198559abbd8..2ba4b68fdb73 100644
--- a/drivers/scsi/iscsi_tcp.c
+++ b/drivers/scsi/iscsi_tcp.c
@@ -37,6 +37,7 @@
#include <linux/kfifo.h>
#include <linux/scatterlist.h>
#include <linux/module.h>
+#include <linux/backing-dev.h>
#include <net/tcp.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
@@ -732,7 +733,7 @@ static int iscsi_sw_tcp_conn_get_param(struct iscsi_cls_conn *cls_conn,
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
struct sockaddr_in6 addr;
- int rc, len;
+ int rc;
switch(param) {
case ISCSI_PARAM_CONN_PORT:
@@ -745,12 +746,12 @@ static int iscsi_sw_tcp_conn_get_param(struct iscsi_cls_conn *cls_conn,
}
if (param == ISCSI_PARAM_LOCAL_PORT)
rc = kernel_getsockname(tcp_sw_conn->sock,
- (struct sockaddr *)&addr, &len);
+ (struct sockaddr *)&addr);
else
rc = kernel_getpeername(tcp_sw_conn->sock,
- (struct sockaddr *)&addr, &len);
+ (struct sockaddr *)&addr);
spin_unlock_bh(&conn->session->frwd_lock);
- if (rc)
+ if (rc < 0)
return rc;
return iscsi_conn_get_addr_param((struct sockaddr_storage *)
@@ -771,7 +772,7 @@ static int iscsi_sw_tcp_host_get_param(struct Scsi_Host *shost,
struct iscsi_tcp_conn *tcp_conn;
struct iscsi_sw_tcp_conn *tcp_sw_conn;
struct sockaddr_in6 addr;
- int rc, len;
+ int rc;
switch (param) {
case ISCSI_HOST_PARAM_IPADDRESS:
@@ -793,9 +794,9 @@ static int iscsi_sw_tcp_host_get_param(struct Scsi_Host *shost,
}
rc = kernel_getsockname(tcp_sw_conn->sock,
- (struct sockaddr *)&addr, &len);
+ (struct sockaddr *)&addr);
spin_unlock_bh(&session->frwd_lock);
- if (rc)
+ if (rc < 0)
return rc;
return iscsi_conn_get_addr_param((struct sockaddr_storage *)
@@ -948,12 +949,19 @@ static umode_t iscsi_sw_tcp_attr_is_visible(int param_type, int param)
static int iscsi_sw_tcp_slave_alloc(struct scsi_device *sdev)
{
- set_bit(QUEUE_FLAG_BIDI, &sdev->request_queue->queue_flags);
+ blk_queue_flag_set(QUEUE_FLAG_BIDI, sdev->request_queue);
return 0;
}
static int iscsi_sw_tcp_slave_configure(struct scsi_device *sdev)
{
+ struct iscsi_sw_tcp_host *tcp_sw_host = iscsi_host_priv(sdev->host);
+ struct iscsi_session *session = tcp_sw_host->session;
+ struct iscsi_conn *conn = session->leadconn;
+
+ if (conn->datadgst_en)
+ sdev->request_queue->backing_dev_info->capabilities
+ |= BDI_CAP_STABLE_WRITES;
blk_queue_bounce_limit(sdev->request_queue, BLK_BOUNCE_ANY);
blk_queue_dma_alignment(sdev->request_queue, 0);
return 0;
diff --git a/drivers/scsi/jazz_esp.c b/drivers/scsi/jazz_esp.c
index 9aaa74e349cc..6eb5ff3e2e61 100644
--- a/drivers/scsi/jazz_esp.c
+++ b/drivers/scsi/jazz_esp.c
@@ -147,7 +147,7 @@ static int esp_jazz_probe(struct platform_device *dev)
esp = shost_priv(host);
esp->host = host;
- esp->dev = dev;
+ esp->dev = &dev->dev;
esp->ops = &jazz_esp_ops;
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c
index 8660f923ace0..3f3569ec5ce3 100644
--- a/drivers/scsi/libfc/fc_disc.c
+++ b/drivers/scsi/libfc/fc_disc.c
@@ -731,7 +731,7 @@ static void fc_disc_stop_final(struct fc_lport *lport)
*/
void fc_disc_config(struct fc_lport *lport, void *priv)
{
- struct fc_disc *disc = &lport->disc;
+ struct fc_disc *disc;
if (!lport->tt.disc_start)
lport->tt.disc_start = fc_disc_start;
diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c
index 2b3637b40dde..0cc1567eacc1 100644
--- a/drivers/scsi/libsas/sas_ata.c
+++ b/drivers/scsi/libsas/sas_ata.c
@@ -709,7 +709,7 @@ void sas_resume_sata(struct asd_sas_port *port)
}
/**
- * sas_discover_sata -- discover an STP/SATA domain device
+ * sas_discover_sata - discover an STP/SATA domain device
* @dev: pointer to struct domain_device of interest
*
* Devices directly attached to a HA port, have no parents. All other
diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c
index e4fd078e4175..a0fa7ef3a071 100644
--- a/drivers/scsi/libsas/sas_discover.c
+++ b/drivers/scsi/libsas/sas_discover.c
@@ -55,7 +55,7 @@ void sas_init_dev(struct domain_device *dev)
/* ---------- Domain device discovery ---------- */
/**
- * sas_get_port_device -- Discover devices which caused port creation
+ * sas_get_port_device - Discover devices which caused port creation
* @port: pointer to struct sas_port of interest
*
* Devices directly attached to a HA port, have no parent. This is
@@ -278,8 +278,8 @@ static void sas_resume_devices(struct work_struct *work)
}
/**
- * sas_discover_end_dev -- discover an end device (SSP, etc)
- * @end: pointer to domain device of interest
+ * sas_discover_end_dev - discover an end device (SSP, etc)
+ * @dev: pointer to domain device of interest
*
* See comment in sas_discover_sata().
*/
@@ -428,8 +428,8 @@ void sas_device_set_phy(struct domain_device *dev, struct sas_port *port)
/* ---------- Discovery and Revalidation ---------- */
/**
- * sas_discover_domain -- discover the domain
- * @port: port to the domain of interest
+ * sas_discover_domain - discover the domain
+ * @work: work structure embedded in port domain device.
*
* NOTE: this process _must_ quit (return) as soon as any connection
* errors are encountered. Connection recovery is done elsewhere.
@@ -572,7 +572,8 @@ int sas_discover_event(struct asd_sas_port *port, enum discover_event ev)
}
/**
- * sas_init_disc -- initialize the discovery struct in the port
+ * sas_init_disc - initialize the discovery struct in the port
+ * @disc: port discovery structure
* @port: pointer to struct port
*
* Called when the ports are being initialized.
diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c
index 6a4f8198b78e..8b7114348def 100644
--- a/drivers/scsi/libsas/sas_expander.c
+++ b/drivers/scsi/libsas/sas_expander.c
@@ -1170,9 +1170,9 @@ static int sas_check_level_subtractive_boundary(struct domain_device *dev)
return 0;
}
/**
- * sas_ex_discover_devices -- discover devices attached to this expander
- * dev: pointer to the expander domain device
- * single: if you want to do a single phy, else set to -1;
+ * sas_ex_discover_devices - discover devices attached to this expander
+ * @dev: pointer to the expander domain device
+ * @single: if you want to do a single phy, else set to -1;
*
* Configure this expander for use with its devices and register the
* devices of this expander.
@@ -1528,10 +1528,11 @@ static int sas_configure_phy(struct domain_device *dev, int phy_id,
}
/**
- * sas_configure_parent -- configure routing table of parent
- * parent: parent expander
- * child: child expander
- * sas_addr: SAS port identifier of device directly attached to child
+ * sas_configure_parent - configure routing table of parent
+ * @parent: parent expander
+ * @child: child expander
+ * @sas_addr: SAS port identifier of device directly attached to child
+ * @include: whether or not to include @child in the expander routing table
*/
static int sas_configure_parent(struct domain_device *parent,
struct domain_device *child,
@@ -1570,9 +1571,9 @@ static int sas_configure_parent(struct domain_device *parent,
}
/**
- * sas_configure_routing -- configure routing
- * dev: expander device
- * sas_addr: port identifier of device directly attached to the expander device
+ * sas_configure_routing - configure routing
+ * @dev: expander device
+ * @sas_addr: port identifier of device directly attached to the expander device
*/
static int sas_configure_routing(struct domain_device *dev, u8 *sas_addr)
{
@@ -1589,8 +1590,8 @@ static int sas_disable_routing(struct domain_device *dev, u8 *sas_addr)
}
/**
- * sas_discover_expander -- expander discovery
- * @ex: pointer to expander domain device
+ * sas_discover_expander - expander discovery
+ * @dev: pointer to expander domain device
*
* See comment in sas_discover_sata().
*/
@@ -2111,8 +2112,8 @@ static int sas_rediscover(struct domain_device *dev, const int phy_id)
}
/**
- * sas_revalidate_domain -- revalidate the domain
- * @port: port to the domain of interest
+ * sas_ex_revalidate_domain - revalidate the domain
+ * @port_dev: port domain device.
*
* NOTE: this process _must_ quit (return) as soon as any connection
* errors are encountered. Connection recovery is done elsewhere.
diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c
index c81a63b5dc71..ede0af78144f 100644
--- a/drivers/scsi/libsas/sas_init.c
+++ b/drivers/scsi/libsas/sas_init.c
@@ -234,7 +234,7 @@ int sas_try_ata_reset(struct asd_sas_phy *asd_phy)
return -ENODEV;
}
-/**
+/*
* transport_sas_phy_reset - reset a phy and permit libata to manage the link
*
* phy reset request via sysfs in host workqueue context so we know we
diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c
index f07e55d3aa73..fad23dd39114 100644
--- a/drivers/scsi/libsas/sas_port.c
+++ b/drivers/scsi/libsas/sas_port.c
@@ -84,7 +84,7 @@ static void sas_resume_port(struct asd_sas_phy *phy)
}
/**
- * sas_form_port -- add this phy to a port
+ * sas_form_port - add this phy to a port
* @phy: the phy of interest
*
* This function adds this phy to an existing port, thus creating a wide
@@ -197,8 +197,9 @@ static void sas_form_port(struct asd_sas_phy *phy)
}
/**
- * sas_deform_port -- remove this phy from the port it belongs to
+ * sas_deform_port - remove this phy from the port it belongs to
* @phy: the phy of interest
+ * @gone: whether or not the PHY is gone
*
* This is called when the physical link to the other phy has been
* lost (on this phy), in Event thread context. We cannot delay here.
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
index 61fb46da05d4..6c0d351c0d0d 100644
--- a/drivers/scsi/lpfc/lpfc.h
+++ b/drivers/scsi/lpfc/lpfc.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Limited and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -544,16 +544,10 @@ struct unsol_rcv_ct_ctx {
#define LPFC_USER_LINK_SPEED_10G 10 /* 10 Gigabaud */
#define LPFC_USER_LINK_SPEED_16G 16 /* 16 Gigabaud */
#define LPFC_USER_LINK_SPEED_32G 32 /* 32 Gigabaud */
-#define LPFC_USER_LINK_SPEED_MAX LPFC_USER_LINK_SPEED_32G
-#define LPFC_USER_LINK_SPEED_BITMAP ((1ULL << LPFC_USER_LINK_SPEED_32G) | \
- (1 << LPFC_USER_LINK_SPEED_16G) | \
- (1 << LPFC_USER_LINK_SPEED_10G) | \
- (1 << LPFC_USER_LINK_SPEED_8G) | \
- (1 << LPFC_USER_LINK_SPEED_4G) | \
- (1 << LPFC_USER_LINK_SPEED_2G) | \
- (1 << LPFC_USER_LINK_SPEED_1G) | \
- (1 << LPFC_USER_LINK_SPEED_AUTO))
-#define LPFC_LINK_SPEED_STRING "0, 1, 2, 4, 8, 10, 16, 32"
+#define LPFC_USER_LINK_SPEED_64G 64 /* 64 Gigabaud */
+#define LPFC_USER_LINK_SPEED_MAX LPFC_USER_LINK_SPEED_64G
+
+#define LPFC_LINK_SPEED_STRING "0, 1, 2, 4, 8, 10, 16, 32, 64"
enum nemb_type {
nemb_mse = 1,
@@ -760,6 +754,7 @@ struct lpfc_hba {
uint8_t mds_diags_support;
uint32_t initial_imax;
uint8_t bbcredit_support;
+ uint8_t enab_exp_wqcq_pages;
/* HBA Config Parameters */
uint32_t cfg_ack0;
@@ -787,6 +782,7 @@ struct lpfc_hba {
uint32_t cfg_fcp_io_channel;
uint32_t cfg_suppress_rsp;
uint32_t cfg_nvme_oas;
+ uint32_t cfg_nvme_embed_cmd;
uint32_t cfg_nvme_io_channel;
uint32_t cfg_nvmet_mrq;
uint32_t cfg_enable_nvmet;
@@ -839,11 +835,14 @@ struct lpfc_hba {
uint32_t cfg_enable_SmartSAN;
uint32_t cfg_enable_mds_diags;
uint32_t cfg_enable_fc4_type;
- uint32_t cfg_enable_bbcr; /*Enable BB Credit Recovery*/
+ uint32_t cfg_enable_bbcr; /* Enable BB Credit Recovery */
+ uint32_t cfg_enable_dpp; /* Enable Direct Packet Push */
uint32_t cfg_xri_split;
#define LPFC_ENABLE_FCP 1
#define LPFC_ENABLE_NVME 2
#define LPFC_ENABLE_BOTH 3
+ uint32_t nvme_embed_pbde;
+ uint32_t fcp_embed_pbde;
uint32_t io_channel_irqs; /* number of irqs for io channels */
struct nvmet_fc_target_port *targetport;
lpfc_vpd_t vpd; /* vital product data */
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
index ac77081e6e9e..2ac1d21c553f 100644
--- a/drivers/scsi/lpfc/lpfc_attr.c
+++ b/drivers/scsi/lpfc/lpfc_attr.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Limited and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -259,6 +259,12 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr,
atomic_read(&tgtp->xmt_abort_rsp),
atomic_read(&tgtp->xmt_abort_rsp_error));
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "DELAY: ctx %08x fod %08x wqfull %08x\n",
+ atomic_read(&tgtp->defer_ctx),
+ atomic_read(&tgtp->defer_fod),
+ atomic_read(&tgtp->defer_wqfull));
+
/* Calculate outstanding IOs */
tot = atomic_read(&tgtp->rcv_fcp_cmd_drop);
tot += atomic_read(&tgtp->xmt_fcp_release);
@@ -905,7 +911,12 @@ lpfc_issue_lip(struct Scsi_Host *shost)
LPFC_MBOXQ_t *pmboxq;
int mbxstatus = MBXERR_ERROR;
+ /*
+ * If the link is offline, disabled or BLOCK_MGMT_IO
+ * it doesn't make any sense to allow issue_lip
+ */
if ((vport->fc_flag & FC_OFFLINE_MODE) ||
+ (phba->hba_flag & LINK_DISABLED) ||
(phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO))
return -EPERM;
@@ -3458,8 +3469,8 @@ LPFC_VPORT_ATTR_R(lun_queue_depth, 30, 1, 512,
# tgt_queue_depth: This parameter is used to limit the number of outstanding
# commands per target port. Value range is [10,65535]. Default value is 65535.
*/
-LPFC_VPORT_ATTR_R(tgt_queue_depth, 65535, 10, 65535,
- "Max number of FCP commands we can queue to a specific target port");
+LPFC_VPORT_ATTR_RW(tgt_queue_depth, 65535, 10, 65535,
+ "Max number of FCP commands we can queue to a specific target port");
/*
# hba_queue_depth: This parameter is used to limit the number of outstanding
@@ -4104,23 +4115,32 @@ lpfc_link_speed_store(struct device *dev, struct device_attribute *attr,
((val == LPFC_USER_LINK_SPEED_8G) && !(phba->lmt & LMT_8Gb)) ||
((val == LPFC_USER_LINK_SPEED_10G) && !(phba->lmt & LMT_10Gb)) ||
((val == LPFC_USER_LINK_SPEED_16G) && !(phba->lmt & LMT_16Gb)) ||
- ((val == LPFC_USER_LINK_SPEED_32G) && !(phba->lmt & LMT_32Gb))) {
+ ((val == LPFC_USER_LINK_SPEED_32G) && !(phba->lmt & LMT_32Gb)) ||
+ ((val == LPFC_USER_LINK_SPEED_64G) && !(phba->lmt & LMT_64Gb))) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"2879 lpfc_link_speed attribute cannot be set "
"to %d. Speed is not supported by this port.\n",
val);
return -EINVAL;
}
- if (val == LPFC_USER_LINK_SPEED_16G &&
- phba->fc_topology == LPFC_TOPOLOGY_LOOP) {
+ if (val >= LPFC_USER_LINK_SPEED_16G &&
+ phba->fc_topology == LPFC_TOPOLOGY_LOOP) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"3112 lpfc_link_speed attribute cannot be set "
"to %d. Speed is not supported in loop mode.\n",
val);
return -EINVAL;
}
- if ((val >= 0) && (val <= LPFC_USER_LINK_SPEED_MAX) &&
- (LPFC_USER_LINK_SPEED_BITMAP & (1 << val))) {
+
+ switch (val) {
+ case LPFC_USER_LINK_SPEED_AUTO:
+ case LPFC_USER_LINK_SPEED_1G:
+ case LPFC_USER_LINK_SPEED_2G:
+ case LPFC_USER_LINK_SPEED_4G:
+ case LPFC_USER_LINK_SPEED_8G:
+ case LPFC_USER_LINK_SPEED_16G:
+ case LPFC_USER_LINK_SPEED_32G:
+ case LPFC_USER_LINK_SPEED_64G:
prev_val = phba->cfg_link_speed;
phba->cfg_link_speed = val;
if (nolip)
@@ -4130,13 +4150,18 @@ lpfc_link_speed_store(struct device *dev, struct device_attribute *attr,
if (err) {
phba->cfg_link_speed = prev_val;
return -EINVAL;
- } else
- return strlen(buf);
+ }
+ return strlen(buf);
+ default:
+ break;
}
+
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0469 lpfc_link_speed attribute cannot be set to %d, "
- "allowed values are ["LPFC_LINK_SPEED_STRING"]\n", val);
+ "0469 lpfc_link_speed attribute cannot be set to %d, "
+ "allowed values are [%s]\n",
+ val, LPFC_LINK_SPEED_STRING);
return -EINVAL;
+
}
static int lpfc_link_speed = 0;
@@ -4163,24 +4188,33 @@ lpfc_param_show(link_speed)
static int
lpfc_link_speed_init(struct lpfc_hba *phba, int val)
{
- if (val == LPFC_USER_LINK_SPEED_16G && phba->cfg_topology == 4) {
+ if (val >= LPFC_USER_LINK_SPEED_16G && phba->cfg_topology == 4) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"3111 lpfc_link_speed of %d cannot "
"support loop mode, setting topology to default.\n",
val);
phba->cfg_topology = 0;
}
- if ((val >= 0) && (val <= LPFC_USER_LINK_SPEED_MAX) &&
- (LPFC_USER_LINK_SPEED_BITMAP & (1 << val))) {
+
+ switch (val) {
+ case LPFC_USER_LINK_SPEED_AUTO:
+ case LPFC_USER_LINK_SPEED_1G:
+ case LPFC_USER_LINK_SPEED_2G:
+ case LPFC_USER_LINK_SPEED_4G:
+ case LPFC_USER_LINK_SPEED_8G:
+ case LPFC_USER_LINK_SPEED_16G:
+ case LPFC_USER_LINK_SPEED_32G:
+ case LPFC_USER_LINK_SPEED_64G:
phba->cfg_link_speed = val;
return 0;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0405 lpfc_link_speed attribute cannot "
+ "be set to %d, allowed values are "
+ "["LPFC_LINK_SPEED_STRING"]\n", val);
+ phba->cfg_link_speed = LPFC_USER_LINK_SPEED_AUTO;
+ return -EINVAL;
}
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0405 lpfc_link_speed attribute cannot "
- "be set to %d, allowed values are "
- "["LPFC_LINK_SPEED_STRING"]\n", val);
- phba->cfg_link_speed = LPFC_USER_LINK_SPEED_AUTO;
- return -EINVAL;
}
static DEVICE_ATTR_RW(lpfc_link_speed);
@@ -5008,6 +5042,18 @@ LPFC_ATTR_RW(nvme_oas, 0, 0, 1,
"Use OAS bit on NVME IOs");
/*
+ * lpfc_nvme_embed_cmd: Use the oas bit when sending NVME/NVMET IOs
+ *
+ * 0 = Put NVME Command in SGL
+ * 1 = Embed NVME Command in WQE (unless G7)
+ * 2 = Embed NVME Command in WQE (force)
+ *
+ * Value range is [0,2]. Default value is 1.
+ */
+LPFC_ATTR_RW(nvme_embed_cmd, 1, 0, 2,
+ "Embed NVME Command in WQE");
+
+/*
* lpfc_fcp_io_channel: Set the number of FCP IO channels the driver
* will advertise it supports to the SCSI layer. This also will map to
* the number of WQs the driver will create.
@@ -5175,6 +5221,14 @@ LPFC_ATTR_R(enable_mds_diags, 0, 0, 1, "Enable MDS Diagnostics");
*/
LPFC_BBCR_ATTR_RW(enable_bbcr, 1, 0, 1, "Enable BBC Recovery");
+/*
+ * lpfc_enable_dpp: Enable DPP on G7
+ * 0 = DPP on G7 disabled
+ * 1 = DPP on G7 enabled (default)
+ * Value range is [0,1]. Default value is 1.
+ */
+LPFC_ATTR_RW(enable_dpp, 1, 0, 1, "Enable Direct Packet Push");
+
struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_nvme_info,
&dev_attr_bg_info,
@@ -5240,6 +5294,7 @@ struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_lpfc_task_mgmt_tmo,
&dev_attr_lpfc_use_msi,
&dev_attr_lpfc_nvme_oas,
+ &dev_attr_lpfc_nvme_embed_cmd,
&dev_attr_lpfc_auto_imax,
&dev_attr_lpfc_fcp_imax,
&dev_attr_lpfc_fcp_cpu_map,
@@ -5283,6 +5338,7 @@ struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_lpfc_xlane_supported,
&dev_attr_lpfc_enable_mds_diags,
&dev_attr_lpfc_enable_bbcr,
+ &dev_attr_lpfc_enable_dpp,
NULL,
};
@@ -5696,6 +5752,9 @@ lpfc_get_host_speed(struct Scsi_Host *shost)
case LPFC_LINK_SPEED_32GHZ:
fc_host_speed(shost) = FC_PORTSPEED_32GBIT;
break;
+ case LPFC_LINK_SPEED_64GHZ:
+ fc_host_speed(shost) = FC_PORTSPEED_64GBIT;
+ break;
default:
fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN;
break;
@@ -6260,6 +6319,7 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
lpfc_enable_SmartSAN_init(phba, lpfc_enable_SmartSAN);
lpfc_use_msi_init(phba, lpfc_use_msi);
lpfc_nvme_oas_init(phba, lpfc_nvme_oas);
+ lpfc_nvme_embed_cmd_init(phba, lpfc_nvme_embed_cmd);
lpfc_auto_imax_init(phba, lpfc_auto_imax);
lpfc_fcp_imax_init(phba, lpfc_fcp_imax);
lpfc_fcp_cpu_map_init(phba, lpfc_fcp_cpu_map);
@@ -6284,6 +6344,10 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
phba->cfg_poll = 0;
else
phba->cfg_poll = lpfc_poll;
+
+ if (phba->cfg_enable_bg)
+ phba->sli3_options |= LPFC_SLI3_BG_ENABLED;
+
lpfc_suppress_rsp_init(phba, lpfc_suppress_rsp);
lpfc_enable_fc4_type_init(phba, lpfc_enable_fc4_type);
@@ -6295,6 +6359,7 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
lpfc_fcp_io_channel_init(phba, lpfc_fcp_io_channel);
lpfc_nvme_io_channel_init(phba, lpfc_nvme_io_channel);
lpfc_enable_bbcr_init(phba, lpfc_enable_bbcr);
+ lpfc_enable_dpp_init(phba, lpfc_enable_dpp);
if (phba->sli_rev != LPFC_SLI_REV4) {
/* NVME only supported on SLI4 */
diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c
index d89816222b23..0f174ca80f67 100644
--- a/drivers/scsi/lpfc/lpfc_bsg.c
+++ b/drivers/scsi/lpfc/lpfc_bsg.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Limited and/or its subsidiaries. *
* Copyright (C) 2009-2015 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -3867,7 +3867,7 @@ lpfc_bsg_sli_cfg_read_cmd_ext(struct lpfc_hba *phba, struct bsg_job *job,
"ext_buf_cnt:%d\n", ext_buf_cnt);
} else {
/* sanity check on interface type for support */
- if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) !=
+ if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) <
LPFC_SLI_INTF_IF_TYPE_2) {
rc = -ENODEV;
goto job_error;
@@ -4053,7 +4053,7 @@ lpfc_bsg_sli_cfg_write_cmd_ext(struct lpfc_hba *phba, struct bsg_job *job,
"ext_buf_cnt:%d\n", ext_buf_cnt);
} else {
/* sanity check on interface type for support */
- if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) !=
+ if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) <
LPFC_SLI_INTF_IF_TYPE_2)
return -ENODEV;
/* nemb_tp == nemb_hbd */
diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h
index 559f9aa0ed08..4ae9ba425e78 100644
--- a/drivers/scsi/lpfc/lpfc_crtn.h
+++ b/drivers/scsi/lpfc/lpfc_crtn.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Limited and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -254,6 +254,7 @@ void lpfc_nvmet_ctxbuf_post(struct lpfc_hba *phba,
struct lpfc_nvmet_ctxbuf *ctxp);
int lpfc_nvmet_rcv_unsol_abort(struct lpfc_vport *vport,
struct fc_frame_header *fc_hdr);
+void lpfc_nvmet_wqfull_process(struct lpfc_hba *phba, struct lpfc_queue *wq);
void lpfc_sli_flush_nvme_rings(struct lpfc_hba *phba);
void lpfc_nvme_wait_for_io_drain(struct lpfc_hba *phba);
void lpfc_sli4_build_dflt_fcf_record(struct lpfc_hba *, struct fcf_record *,
@@ -564,6 +565,8 @@ void lpfc_nvme_mod_param_dep(struct lpfc_hba *phba);
void lpfc_nvme_abort_fcreq_cmpl(struct lpfc_hba *phba,
struct lpfc_iocbq *cmdiocb,
struct lpfc_wcqe_complete *abts_cmpl);
+void lpfc_nvme_cmd_template(void);
+void lpfc_nvmet_cmd_template(void);
extern int lpfc_enable_nvmet_cnt;
extern unsigned long long lpfc_enable_nvmet[];
extern int lpfc_no_hba_reset_cnt;
diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c
index 9d20d2c208c7..0617c8ea88c6 100644
--- a/drivers/scsi/lpfc/lpfc_ct.c
+++ b/drivers/scsi/lpfc/lpfc_ct.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Limited and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -471,7 +471,6 @@ lpfc_prep_node_fc4type(struct lpfc_vport *vport, uint32_t Did, uint8_t fc4_type)
"Parse GID_FTrsp: did:x%x flg:x%x x%x",
Did, ndlp->nlp_flag, vport->fc_flag);
- ndlp->nlp_fc4_type &= ~(NLP_FC4_FCP | NLP_FC4_NVME);
/* By default, the driver expects to support FCP FC4 */
if (fc4_type == FC_TYPE_FCP)
ndlp->nlp_fc4_type |= NLP_FC4_FCP;
@@ -2130,6 +2129,8 @@ lpfc_fdmi_port_attr_support_speed(struct lpfc_vport *vport,
ae->un.AttrInt = 0;
if (!(phba->hba_flag & HBA_FCOE_MODE)) {
+ if (phba->lmt & LMT_64Gb)
+ ae->un.AttrInt |= HBA_PORTSPEED_64GFC;
if (phba->lmt & LMT_32Gb)
ae->un.AttrInt |= HBA_PORTSPEED_32GFC;
if (phba->lmt & LMT_16Gb)
@@ -2201,6 +2202,9 @@ lpfc_fdmi_port_attr_speed(struct lpfc_vport *vport,
case LPFC_LINK_SPEED_32GHZ:
ae->un.AttrInt = HBA_PORTSPEED_32GFC;
break;
+ case LPFC_LINK_SPEED_64GHZ:
+ ae->un.AttrInt = HBA_PORTSPEED_64GFC;
+ break;
default:
ae->un.AttrInt = HBA_PORTSPEED_UNKNOWN;
break;
diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c
index 17ea3bb04266..fb0dc2aeed91 100644
--- a/drivers/scsi/lpfc/lpfc_debugfs.c
+++ b/drivers/scsi/lpfc/lpfc_debugfs.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Limited and/or its subsidiaries. *
* Copyright (C) 2007-2015 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -3944,10 +3944,15 @@ lpfc_idiag_drbacc_read_reg(struct lpfc_hba *phba, char *pbuffer,
return 0;
switch (drbregid) {
- case LPFC_DRB_EQCQ:
- len += snprintf(pbuffer+len, LPFC_DRB_ACC_BUF_SIZE-len,
- "EQCQ-DRB-REG: 0x%08x\n",
- readl(phba->sli4_hba.EQCQDBregaddr));
+ case LPFC_DRB_EQ:
+ len += snprintf(pbuffer + len, LPFC_DRB_ACC_BUF_SIZE-len,
+ "EQ-DRB-REG: 0x%08x\n",
+ readl(phba->sli4_hba.EQDBregaddr));
+ break;
+ case LPFC_DRB_CQ:
+ len += snprintf(pbuffer + len, LPFC_DRB_ACC_BUF_SIZE - len,
+ "CQ-DRB-REG: 0x%08x\n",
+ readl(phba->sli4_hba.CQDBregaddr));
break;
case LPFC_DRB_MQ:
len += snprintf(pbuffer+len, LPFC_DRB_ACC_BUF_SIZE-len,
@@ -4086,8 +4091,11 @@ lpfc_idiag_drbacc_write(struct file *file, const char __user *buf,
idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_ST ||
idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_CL) {
switch (drb_reg_id) {
- case LPFC_DRB_EQCQ:
- drb_reg = phba->sli4_hba.EQCQDBregaddr;
+ case LPFC_DRB_EQ:
+ drb_reg = phba->sli4_hba.EQDBregaddr;
+ break;
+ case LPFC_DRB_CQ:
+ drb_reg = phba->sli4_hba.CQDBregaddr;
break;
case LPFC_DRB_MQ:
drb_reg = phba->sli4_hba.MQDBregaddr;
diff --git a/drivers/scsi/lpfc/lpfc_debugfs.h b/drivers/scsi/lpfc/lpfc_debugfs.h
index c4edd87bfc65..f32eaeb2225a 100644
--- a/drivers/scsi/lpfc/lpfc_debugfs.h
+++ b/drivers/scsi/lpfc/lpfc_debugfs.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Limited and/or its subsidiaries. *
* Copyright (C) 2007-2011 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -126,12 +126,13 @@
#define LPFC_DRB_ACC_WR_CMD_ARG 2
#define LPFC_DRB_ACC_BUF_SIZE 256
-#define LPFC_DRB_EQCQ 1
-#define LPFC_DRB_MQ 2
-#define LPFC_DRB_WQ 3
-#define LPFC_DRB_RQ 4
+#define LPFC_DRB_EQ 1
+#define LPFC_DRB_CQ 2
+#define LPFC_DRB_MQ 3
+#define LPFC_DRB_WQ 4
+#define LPFC_DRB_RQ 5
-#define LPFC_DRB_MAX 4
+#define LPFC_DRB_MAX 5
#define IDIAG_DRBACC_REGID_INDX 0
#define IDIAG_DRBACC_VALUE_INDX 1
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index 234c7c015982..74895e62aaea 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Limited and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -1661,6 +1661,7 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
if (ndlp->nrport) {
ndlp->nrport = NULL;
lpfc_nlp_put(ndlp);
+ new_ndlp->nlp_fc4_type = ndlp->nlp_fc4_type;
}
/* We shall actually free the ndlp with both nlp_DID and
@@ -2293,10 +2294,11 @@ lpfc_issue_els_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
if (phba->nvmet_support) {
bf_set(prli_tgt, npr_nvme, 1);
bf_set(prli_disc, npr_nvme, 1);
-
} else {
bf_set(prli_init, npr_nvme, 1);
+ bf_set(prli_conf, npr_nvme, 1);
}
+
npr_nvme->word1 = cpu_to_be32(npr_nvme->word1);
npr_nvme->word4 = cpu_to_be32(npr_nvme->word4);
elsiocb->iocb_flag |= LPFC_PRLI_NVME_REQ;
@@ -5269,6 +5271,9 @@ lpfc_rdp_res_speed(struct fc_rdp_port_speed_desc *desc, struct lpfc_hba *phba)
case LPFC_LINK_SPEED_32GHZ:
rdp_speed = RDP_PS_32GB;
break;
+ case LPFC_LINK_SPEED_64GHZ:
+ rdp_speed = RDP_PS_64GB;
+ break;
default:
rdp_speed = RDP_PS_UNKNOWN;
break;
@@ -5276,6 +5281,8 @@ lpfc_rdp_res_speed(struct fc_rdp_port_speed_desc *desc, struct lpfc_hba *phba)
desc->info.port_speed.speed = cpu_to_be16(rdp_speed);
+ if (phba->lmt & LMT_64Gb)
+ rdp_cap |= RDP_PS_64GB;
if (phba->lmt & LMT_32Gb)
rdp_cap |= RDP_PS_32GB;
if (phba->lmt & LMT_16Gb)
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
index b159a5c4e388..3e7712cd6c9a 100644
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Limited and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -696,8 +696,9 @@ lpfc_work_done(struct lpfc_hba *phba)
phba->hba_flag & HBA_SP_QUEUE_EVT)) {
if (pring->flag & LPFC_STOP_IOCB_EVENT) {
pring->flag |= LPFC_DEFERRED_RING_EVENT;
- /* Set the lpfc data pending flag */
- set_bit(LPFC_DATA_READY, &phba->data_flags);
+ /* Preserve legacy behavior. */
+ if (!(phba->hba_flag & HBA_SP_QUEUE_EVT))
+ set_bit(LPFC_DATA_READY, &phba->data_flags);
} else {
if (phba->link_state >= LPFC_LINK_UP ||
phba->link_flag & LS_MDS_LOOPBACK) {
@@ -958,6 +959,7 @@ lpfc_linkup_cleanup_nodes(struct lpfc_vport *vport)
struct lpfc_nodelist *ndlp;
list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) {
+ ndlp->nlp_fc4_type &= ~(NLP_FC4_FCP | NLP_FC4_NVME);
if (!NLP_CHK_NODE_ACT(ndlp))
continue;
if (ndlp->nlp_state == NLP_STE_UNUSED_NODE)
@@ -3083,6 +3085,7 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, struct lpfc_mbx_read_top *la)
case LPFC_LINK_SPEED_10GHZ:
case LPFC_LINK_SPEED_16GHZ:
case LPFC_LINK_SPEED_32GHZ:
+ case LPFC_LINK_SPEED_64GHZ:
break;
default:
phba->fc_linkspeed = LPFC_LINK_SPEED_UNKNOWN;
@@ -3873,6 +3876,10 @@ int
lpfc_issue_gidft(struct lpfc_vport *vport)
{
struct lpfc_hba *phba = vport->phba;
+ struct lpfc_nodelist *ndlp;
+
+ list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp)
+ ndlp->nlp_fc4_type &= ~(NLP_FC4_FCP | NLP_FC4_NVME);
/* Good status, issue CT Request to NameServer */
if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h
index bdc1f184f67a..08a3f1520159 100644
--- a/drivers/scsi/lpfc/lpfc_hw.h
+++ b/drivers/scsi/lpfc/lpfc_hw.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Limited and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -1177,6 +1177,9 @@ struct fc_rdp_link_error_status_desc {
#define RDP_PS_8GB 0x0800
#define RDP_PS_16GB 0x0400
#define RDP_PS_32GB 0x0200
+#define RDP_PS_64GB 0x0100
+#define RDP_PS_128GB 0x0080
+#define RDP_PS_256GB 0x0040
#define RDP_CAP_USER_CONFIGURED 0x0002
#define RDP_CAP_UNKNOWN 0x0001
@@ -1580,6 +1583,7 @@ struct lpfc_fdmi_reg_portattr {
#define PCI_DEVICE_ID_LANCER_FCOE 0xe260
#define PCI_DEVICE_ID_LANCER_FCOE_VF 0xe268
#define PCI_DEVICE_ID_LANCER_G6_FC 0xe300
+#define PCI_DEVICE_ID_LANCER_G7_FC 0xf400
#define PCI_DEVICE_ID_SAT_SMB 0xf011
#define PCI_DEVICE_ID_SAT_MID 0xf015
#define PCI_DEVICE_ID_RFLY 0xf095
@@ -2257,6 +2261,9 @@ typedef struct {
#define LINK_SPEED_10G 0x10 /* 10 Gigabaud */
#define LINK_SPEED_16G 0x11 /* 16 Gigabaud */
#define LINK_SPEED_32G 0x14 /* 32 Gigabaud */
+#define LINK_SPEED_64G 0x17 /* 64 Gigabaud */
+#define LINK_SPEED_128G 0x1A /* 128 Gigabaud */
+#define LINK_SPEED_256G 0x1D /* 256 Gigabaud */
} INIT_LINK_VAR;
@@ -2441,6 +2448,9 @@ typedef struct {
#define LMT_10Gb 0x100
#define LMT_16Gb 0x200
#define LMT_32Gb 0x400
+#define LMT_64Gb 0x800
+#define LMT_128Gb 0x1000
+#define LMT_256Gb 0x2000
uint32_t rsvd2;
uint32_t rsvd3;
uint32_t max_xri;
@@ -2965,6 +2975,9 @@ struct lpfc_mbx_read_top {
#define LPFC_LINK_SPEED_10GHZ 0x40
#define LPFC_LINK_SPEED_16GHZ 0x80
#define LPFC_LINK_SPEED_32GHZ 0x90
+#define LPFC_LINK_SPEED_64GHZ 0xA0
+#define LPFC_LINK_SPEED_128GHZ 0xB0
+#define LPFC_LINK_SPEED_256GHZ 0xC0
};
/* Structure for MB Command CLEAR_LA (22) */
diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h
index 73c2f6971d2b..98b80559c215 100644
--- a/drivers/scsi/lpfc/lpfc_hw4.h
+++ b/drivers/scsi/lpfc/lpfc_hw4.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Limited and/or its subsidiaries. *
* Copyright (C) 2009-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -84,6 +84,7 @@ struct lpfc_sli_intf {
#define LPFC_SLI_INTF_IF_TYPE_0 0
#define LPFC_SLI_INTF_IF_TYPE_1 1
#define LPFC_SLI_INTF_IF_TYPE_2 2
+#define LPFC_SLI_INTF_IF_TYPE_6 6
#define lpfc_sli_intf_sli_family_SHIFT 8
#define lpfc_sli_intf_sli_family_MASK 0x0000000F
#define lpfc_sli_intf_sli_family_WORD word0
@@ -731,11 +732,13 @@ struct lpfc_register {
* register sets depending on the UCNA Port's reported if_type
* value. For UCNA ports running SLI4 and if_type 0, they reside in
* BAR4. For UCNA ports running SLI4 and if_type 2, they reside in
- * BAR0. The offsets are the same so the driver must account for
- * any base address difference.
+ * BAR0. For FC ports running SLI4 and if_type 6, they reside in
+ * BAR2. The offsets and base address are different, so the driver
+ * has to compute the register addresses accordingly
*/
#define LPFC_ULP0_RQ_DOORBELL 0x00A0
#define LPFC_ULP1_RQ_DOORBELL 0x00C0
+#define LPFC_IF6_RQ_DOORBELL 0x0080
#define lpfc_rq_db_list_fm_num_posted_SHIFT 24
#define lpfc_rq_db_list_fm_num_posted_MASK 0x00FF
#define lpfc_rq_db_list_fm_num_posted_WORD word0
@@ -770,6 +773,20 @@ struct lpfc_register {
#define lpfc_wq_db_ring_fm_id_MASK 0xFFFF
#define lpfc_wq_db_ring_fm_id_WORD word0
+#define LPFC_IF6_WQ_DOORBELL 0x0040
+#define lpfc_if6_wq_db_list_fm_num_posted_SHIFT 24
+#define lpfc_if6_wq_db_list_fm_num_posted_MASK 0x00FF
+#define lpfc_if6_wq_db_list_fm_num_posted_WORD word0
+#define lpfc_if6_wq_db_list_fm_dpp_SHIFT 23
+#define lpfc_if6_wq_db_list_fm_dpp_MASK 0x0001
+#define lpfc_if6_wq_db_list_fm_dpp_WORD word0
+#define lpfc_if6_wq_db_list_fm_dpp_id_SHIFT 16
+#define lpfc_if6_wq_db_list_fm_dpp_id_MASK 0x001F
+#define lpfc_if6_wq_db_list_fm_dpp_id_WORD word0
+#define lpfc_if6_wq_db_list_fm_id_SHIFT 0
+#define lpfc_if6_wq_db_list_fm_id_MASK 0xFFFF
+#define lpfc_if6_wq_db_list_fm_id_WORD word0
+
#define LPFC_EQCQ_DOORBELL 0x0120
#define lpfc_eqcq_doorbell_se_SHIFT 31
#define lpfc_eqcq_doorbell_se_MASK 0x0001
@@ -805,6 +822,38 @@ struct lpfc_register {
#define LPFC_CQID_HI_FIELD_SHIFT 10
#define LPFC_EQID_HI_FIELD_SHIFT 9
+#define LPFC_IF6_CQ_DOORBELL 0x00C0
+#define lpfc_if6_cq_doorbell_se_SHIFT 31
+#define lpfc_if6_cq_doorbell_se_MASK 0x0001
+#define lpfc_if6_cq_doorbell_se_WORD word0
+#define LPFC_IF6_CQ_SOLICIT_ENABLE_OFF 0
+#define LPFC_IF6_CQ_SOLICIT_ENABLE_ON 1
+#define lpfc_if6_cq_doorbell_arm_SHIFT 29
+#define lpfc_if6_cq_doorbell_arm_MASK 0x0001
+#define lpfc_if6_cq_doorbell_arm_WORD word0
+#define lpfc_if6_cq_doorbell_num_released_SHIFT 16
+#define lpfc_if6_cq_doorbell_num_released_MASK 0x1FFF
+#define lpfc_if6_cq_doorbell_num_released_WORD word0
+#define lpfc_if6_cq_doorbell_cqid_SHIFT 0
+#define lpfc_if6_cq_doorbell_cqid_MASK 0xFFFF
+#define lpfc_if6_cq_doorbell_cqid_WORD word0
+
+#define LPFC_IF6_EQ_DOORBELL 0x0120
+#define lpfc_if6_eq_doorbell_io_SHIFT 31
+#define lpfc_if6_eq_doorbell_io_MASK 0x0001
+#define lpfc_if6_eq_doorbell_io_WORD word0
+#define LPFC_IF6_EQ_INTR_OVERRIDE_OFF 0
+#define LPFC_IF6_EQ_INTR_OVERRIDE_ON 1
+#define lpfc_if6_eq_doorbell_arm_SHIFT 29
+#define lpfc_if6_eq_doorbell_arm_MASK 0x0001
+#define lpfc_if6_eq_doorbell_arm_WORD word0
+#define lpfc_if6_eq_doorbell_num_released_SHIFT 16
+#define lpfc_if6_eq_doorbell_num_released_MASK 0x1FFF
+#define lpfc_if6_eq_doorbell_num_released_WORD word0
+#define lpfc_if6_eq_doorbell_eqid_SHIFT 0
+#define lpfc_if6_eq_doorbell_eqid_MASK 0x0FFF
+#define lpfc_if6_eq_doorbell_eqid_WORD word0
+
#define LPFC_BMBX 0x0160
#define lpfc_bmbx_addr_SHIFT 2
#define lpfc_bmbx_addr_MASK 0x3FFFFFFF
@@ -817,6 +866,7 @@ struct lpfc_register {
#define lpfc_bmbx_rdy_WORD word0
#define LPFC_MQ_DOORBELL 0x0140
+#define LPFC_IF6_MQ_DOORBELL 0x0160
#define lpfc_mq_doorbell_num_posted_SHIFT 16
#define lpfc_mq_doorbell_num_posted_MASK 0x3FFF
#define lpfc_mq_doorbell_num_posted_WORD word0
@@ -990,6 +1040,9 @@ struct eq_context {
#define lpfc_eq_context_valid_SHIFT 29
#define lpfc_eq_context_valid_MASK 0x00000001
#define lpfc_eq_context_valid_WORD word0
+#define lpfc_eq_context_autovalid_SHIFT 28
+#define lpfc_eq_context_autovalid_MASK 0x00000001
+#define lpfc_eq_context_autovalid_WORD word0
uint32_t word1;
#define lpfc_eq_context_count_SHIFT 26
#define lpfc_eq_context_count_MASK 0x00000003
@@ -1123,6 +1176,9 @@ struct cq_context {
#define LPFC_CQ_CNT_512 0x1
#define LPFC_CQ_CNT_1024 0x2
#define LPFC_CQ_CNT_WORD7 0x3
+#define lpfc_cq_context_autovalid_SHIFT 15
+#define lpfc_cq_context_autovalid_MASK 0x00000001
+#define lpfc_cq_context_autovalid_WORD word0
uint32_t word1;
#define lpfc_cq_eq_id_SHIFT 22 /* Version 0 Only */
#define lpfc_cq_eq_id_MASK 0x000000FF
@@ -1181,9 +1237,9 @@ struct lpfc_mbx_cq_create_set {
#define lpfc_mbx_cq_create_set_cqe_size_SHIFT 25
#define lpfc_mbx_cq_create_set_cqe_size_MASK 0x00000003
#define lpfc_mbx_cq_create_set_cqe_size_WORD word1
-#define lpfc_mbx_cq_create_set_auto_SHIFT 15
-#define lpfc_mbx_cq_create_set_auto_MASK 0x0000001
-#define lpfc_mbx_cq_create_set_auto_WORD word1
+#define lpfc_mbx_cq_create_set_autovalid_SHIFT 15
+#define lpfc_mbx_cq_create_set_autovalid_MASK 0x0000001
+#define lpfc_mbx_cq_create_set_autovalid_WORD word1
#define lpfc_mbx_cq_create_set_nodelay_SHIFT 14
#define lpfc_mbx_cq_create_set_nodelay_MASK 0x00000001
#define lpfc_mbx_cq_create_set_nodelay_WORD word1
@@ -1322,6 +1378,15 @@ struct lpfc_mbx_wq_create {
#define lpfc_mbx_wq_create_page_size_MASK 0x000000FF
#define lpfc_mbx_wq_create_page_size_WORD word1
#define LPFC_WQ_PAGE_SIZE_4096 0x1
+#define lpfc_mbx_wq_create_dpp_req_SHIFT 15
+#define lpfc_mbx_wq_create_dpp_req_MASK 0x00000001
+#define lpfc_mbx_wq_create_dpp_req_WORD word1
+#define lpfc_mbx_wq_create_doe_SHIFT 14
+#define lpfc_mbx_wq_create_doe_MASK 0x00000001
+#define lpfc_mbx_wq_create_doe_WORD word1
+#define lpfc_mbx_wq_create_toe_SHIFT 13
+#define lpfc_mbx_wq_create_toe_MASK 0x00000001
+#define lpfc_mbx_wq_create_toe_WORD word1
#define lpfc_mbx_wq_create_wqe_size_SHIFT 8
#define lpfc_mbx_wq_create_wqe_size_MASK 0x0000000F
#define lpfc_mbx_wq_create_wqe_size_WORD word1
@@ -1350,6 +1415,28 @@ struct lpfc_mbx_wq_create {
#define lpfc_mbx_wq_create_db_format_MASK 0x0000FFFF
#define lpfc_mbx_wq_create_db_format_WORD word2
} response;
+ struct {
+ uint32_t word0;
+#define lpfc_mbx_wq_create_dpp_rsp_SHIFT 31
+#define lpfc_mbx_wq_create_dpp_rsp_MASK 0x00000001
+#define lpfc_mbx_wq_create_dpp_rsp_WORD word0
+#define lpfc_mbx_wq_create_v1_q_id_SHIFT 0
+#define lpfc_mbx_wq_create_v1_q_id_MASK 0x0000FFFF
+#define lpfc_mbx_wq_create_v1_q_id_WORD word0
+ uint32_t word1;
+#define lpfc_mbx_wq_create_v1_bar_set_SHIFT 0
+#define lpfc_mbx_wq_create_v1_bar_set_MASK 0x0000000F
+#define lpfc_mbx_wq_create_v1_bar_set_WORD word1
+ uint32_t doorbell_offset;
+ uint32_t word3;
+#define lpfc_mbx_wq_create_dpp_id_SHIFT 16
+#define lpfc_mbx_wq_create_dpp_id_MASK 0x0000001F
+#define lpfc_mbx_wq_create_dpp_id_WORD word3
+#define lpfc_mbx_wq_create_dpp_bar_SHIFT 0
+#define lpfc_mbx_wq_create_dpp_bar_MASK 0x0000000F
+#define lpfc_mbx_wq_create_dpp_bar_WORD word3
+ uint32_t dpp_offset;
+ } response_1;
} u;
};
@@ -2154,6 +2241,7 @@ struct lpfc_mbx_redisc_fcf_tbl {
* command.
*/
#define ADD_STATUS_OPERATION_ALREADY_ACTIVE 0x67
+#define ADD_STATUS_FW_NOT_SUPPORTED 0xEB
struct lpfc_mbx_sli4_config {
struct mbox_header header;
@@ -2590,6 +2678,7 @@ struct lpfc_mbx_read_rev {
#define lpfc_mbx_rd_rev_vpd_MASK 0x00000001
#define lpfc_mbx_rd_rev_vpd_WORD word1
uint32_t first_hw_rev;
+#define LPFC_G7_ASIC_1 0xd
uint32_t second_hw_rev;
uint32_t word4_rsvd;
uint32_t third_hw_rev;
@@ -3207,11 +3296,20 @@ struct lpfc_sli4_parameters {
#define cfg_sli_hint_2_MASK 0x0000001f
#define cfg_sli_hint_2_WORD word1
uint32_t word2;
+#define cfg_eqav_SHIFT 31
+#define cfg_eqav_MASK 0x00000001
+#define cfg_eqav_WORD word2
uint32_t word3;
uint32_t word4;
#define cfg_cqv_SHIFT 14
#define cfg_cqv_MASK 0x00000003
#define cfg_cqv_WORD word4
+#define cfg_cqpsize_SHIFT 16
+#define cfg_cqpsize_MASK 0x000000ff
+#define cfg_cqpsize_WORD word4
+#define cfg_cqav_SHIFT 31
+#define cfg_cqav_MASK 0x00000001
+#define cfg_cqav_WORD word4
uint32_t word5;
uint32_t word6;
#define cfg_mqv_SHIFT 14
@@ -3290,6 +3388,9 @@ struct lpfc_sli4_parameters {
#define cfg_eqdr_SHIFT 8
#define cfg_eqdr_MASK 0x00000001
#define cfg_eqdr_WORD word19
+#define cfg_nosr_SHIFT 9
+#define cfg_nosr_MASK 0x00000001
+#define cfg_nosr_WORD word19
#define LPFC_NODELAY_MAX_IO 32
};
@@ -3874,6 +3975,9 @@ struct lpfc_acqe_fc_la {
#define LPFC_FC_LA_SPEED_10G 0xA
#define LPFC_FC_LA_SPEED_16G 0x10
#define LPFC_FC_LA_SPEED_32G 0x20
+#define LPFC_FC_LA_SPEED_64G 0x21
+#define LPFC_FC_LA_SPEED_128G 0x22
+#define LPFC_FC_LA_SPEED_256G 0x23
#define lpfc_acqe_fc_la_topology_SHIFT 16
#define lpfc_acqe_fc_la_topology_MASK 0x000000FF
#define lpfc_acqe_fc_la_topology_WORD word0
@@ -4079,6 +4183,7 @@ struct wqe_common {
#define wqe_iod_SHIFT 13
#define wqe_iod_MASK 0x00000001
#define wqe_iod_WORD word10
+#define LPFC_WQE_IOD_NONE 0
#define LPFC_WQE_IOD_WRITE 0
#define LPFC_WQE_IOD_READ 1
#define wqe_dbde_SHIFT 14
@@ -4123,6 +4228,9 @@ struct wqe_common {
#define wqe_irsp_SHIFT 4
#define wqe_irsp_MASK 0x00000001
#define wqe_irsp_WORD word11
+#define wqe_pbde_SHIFT 5
+#define wqe_pbde_MASK 0x00000001
+#define wqe_pbde_WORD word11
#define wqe_sup_SHIFT 6
#define wqe_sup_MASK 0x00000001
#define wqe_sup_WORD word11
@@ -4343,9 +4451,9 @@ struct lpfc_nvme_prli {
#define prli_init_SHIFT 5
#define prli_init_MASK 0x00000001
#define prli_init_WORD word4
-#define prli_recov_SHIFT 8
-#define prli_recov_MASK 0x00000001
-#define prli_recov_WORD word4
+#define prli_conf_SHIFT 7
+#define prli_conf_MASK 0x00000001
+#define prli_conf_WORD word4
uint32_t word5;
#define prli_fb_sz_SHIFT 0
#define prli_fb_sz_MASK 0x0000ffff
@@ -4494,17 +4602,20 @@ union lpfc_wqe128 {
struct fcp_icmnd64_wqe fcp_icmd;
struct fcp_iread64_wqe fcp_iread;
struct fcp_iwrite64_wqe fcp_iwrite;
+ struct abort_cmd_wqe abort_cmd;
+ struct create_xri_wqe create_xri;
+ struct xmit_bcast64_wqe xmit_bcast64;
+ struct xmit_seq64_wqe xmit_sequence;
+ struct xmit_bls_rsp64_wqe xmit_bls_rsp;
+ struct xmit_els_rsp64_wqe xmit_els_rsp;
+ struct els_request64_wqe els_req;
+ struct gen_req64_wqe gen_req;
struct fcp_trsp64_wqe fcp_trsp;
struct fcp_tsend64_wqe fcp_tsend;
struct fcp_treceive64_wqe fcp_treceive;
- struct xmit_seq64_wqe xmit_sequence;
- struct gen_req64_wqe gen_req;
+ struct send_frame_wqe send_frame;
};
-#define LPFC_GROUP_OJECT_MAGIC_G5 0xfeaa0001
-#define LPFC_GROUP_OJECT_MAGIC_G6 0xfeaa0003
-#define LPFC_FILE_TYPE_GROUP 0xf7
-#define LPFC_FILE_ID_GROUP 0xa2
struct lpfc_grp_hdr {
uint32_t size;
uint32_t magic_number;
diff --git a/drivers/scsi/lpfc/lpfc_ids.h b/drivers/scsi/lpfc/lpfc_ids.h
index 0ba3733eb36d..07ee34017d88 100644
--- a/drivers/scsi/lpfc/lpfc_ids.h
+++ b/drivers/scsi/lpfc/lpfc_ids.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Limited and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -116,6 +116,8 @@ const struct pci_device_id lpfc_id_table[] = {
PCI_ANY_ID, PCI_ANY_ID, },
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LANCER_G6_FC,
PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LANCER_G7_FC,
+ PCI_ANY_ID, PCI_ANY_ID, },
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SKYHAWK,
PCI_ANY_ID, PCI_ANY_ID, },
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SKYHAWK_VF,
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index f539c554588c..7887468c71b4 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Limited and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -731,7 +731,9 @@ lpfc_hba_init_link_fc_topology(struct lpfc_hba *phba, uint32_t fc_topology,
((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_16G) &&
!(phba->lmt & LMT_16Gb)) ||
((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_32G) &&
- !(phba->lmt & LMT_32Gb))) {
+ !(phba->lmt & LMT_32Gb)) ||
+ ((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_64G) &&
+ !(phba->lmt & LMT_64Gb))) {
/* Reset link speed to auto */
lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT,
"1302 Invalid speed for this board:%d "
@@ -958,6 +960,7 @@ lpfc_hba_clean_txcmplq(struct lpfc_hba *phba)
struct lpfc_sli_ring *pring;
LIST_HEAD(completions);
int i;
+ struct lpfc_iocbq *piocb, *next_iocb;
if (phba->sli_rev != LPFC_SLI_REV4) {
for (i = 0; i < psli->num_rings; i++) {
@@ -983,6 +986,9 @@ lpfc_hba_clean_txcmplq(struct lpfc_hba *phba)
if (!pring)
continue;
spin_lock_irq(&pring->ring_lock);
+ list_for_each_entry_safe(piocb, next_iocb,
+ &pring->txcmplq, list)
+ piocb->iocb_flag &= ~LPFC_IO_ON_TXCMPLQ;
list_splice_init(&pring->txcmplq, &completions);
pring->txcmplq_cnt = 0;
spin_unlock_irq(&pring->ring_lock);
@@ -1757,7 +1763,7 @@ lpfc_sli4_port_sta_fn_reset(struct lpfc_hba *phba, int mbx_action,
int rc;
uint32_t intr_mode;
- if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) ==
+ if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) >=
LPFC_SLI_INTF_IF_TYPE_2) {
/*
* On error status condition, driver need to wait for port
@@ -1888,6 +1894,7 @@ lpfc_handle_eratt_s4(struct lpfc_hba *phba)
break;
case LPFC_SLI_INTF_IF_TYPE_2:
+ case LPFC_SLI_INTF_IF_TYPE_6:
pci_rd_rc1 = lpfc_readl(
phba->sli4_hba.u.if_type2.STATUSregaddr,
&portstat_reg.word0);
@@ -2269,7 +2276,9 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
&& descp && descp[0] != '\0')
return;
- if (phba->lmt & LMT_32Gb)
+ if (phba->lmt & LMT_64Gb)
+ max_speed = 64;
+ else if (phba->lmt & LMT_32Gb)
max_speed = 32;
else if (phba->lmt & LMT_16Gb)
max_speed = 16;
@@ -2468,6 +2477,9 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
case PCI_DEVICE_ID_LANCER_G6_FC:
m = (typeof(m)){"LPe32000", "PCIe", "Fibre Channel Adapter"};
break;
+ case PCI_DEVICE_ID_LANCER_G7_FC:
+ m = (typeof(m)){"LPe36000", "PCIe", "Fibre Channel Adapter"};
+ break;
case PCI_DEVICE_ID_SKYHAWK:
case PCI_DEVICE_ID_SKYHAWK_VF:
oneConnect = 1;
@@ -4104,6 +4116,8 @@ void lpfc_host_attrib_init(struct Scsi_Host *shost)
sizeof fc_host_symbolic_name(shost));
fc_host_supported_speeds(shost) = 0;
+ if (phba->lmt & LMT_64Gb)
+ fc_host_supported_speeds(shost) |= FC_PORTSPEED_64GBIT;
if (phba->lmt & LMT_32Gb)
fc_host_supported_speeds(shost) |= FC_PORTSPEED_32GBIT;
if (phba->lmt & LMT_16Gb)
@@ -4440,6 +4454,9 @@ lpfc_sli4_port_speed_parse(struct lpfc_hba *phba, uint32_t evt_code,
case LPFC_FC_LA_SPEED_32G:
port_speed = 32000;
break;
+ case LPFC_FC_LA_SPEED_64G:
+ port_speed = 64000;
+ break;
default:
port_speed = 0;
}
@@ -5895,7 +5912,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
* Since lpfc_sg_seg_cnt is module param, the sg_dma_buf_size
* used to create the sg_dma_buf_pool must be calculated.
*/
- if (phba->cfg_enable_bg) {
+ if (phba->sli3_options & LPFC_SLI3_BG_ENABLED) {
/*
* The scsi_buf for a T10-DIF I/O holds the FCP cmnd,
* the FCP rsp, and a SGE. Sice we have no control
@@ -6014,7 +6031,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
return -ENOMEM;
/* IF Type 2 ports get initialized now. */
- if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) ==
+ if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) >=
LPFC_SLI_INTF_IF_TYPE_2) {
rc = lpfc_pci_function_reset(phba);
if (unlikely(rc)) {
@@ -7344,6 +7361,7 @@ lpfc_sli4_post_status_check(struct lpfc_hba *phba)
}
break;
case LPFC_SLI_INTF_IF_TYPE_2:
+ case LPFC_SLI_INTF_IF_TYPE_6:
/* Final checks. The port status should be clean. */
if (lpfc_readl(phba->sli4_hba.u.if_type2.STATUSregaddr,
&reg_data.word0) ||
@@ -7426,13 +7444,36 @@ lpfc_sli4_bar0_register_memmap(struct lpfc_hba *phba, uint32_t if_type)
phba->sli4_hba.WQDBregaddr =
phba->sli4_hba.conf_regs_memmap_p +
LPFC_ULP0_WQ_DOORBELL;
- phba->sli4_hba.EQCQDBregaddr =
+ phba->sli4_hba.CQDBregaddr =
phba->sli4_hba.conf_regs_memmap_p + LPFC_EQCQ_DOORBELL;
+ phba->sli4_hba.EQDBregaddr = phba->sli4_hba.CQDBregaddr;
phba->sli4_hba.MQDBregaddr =
phba->sli4_hba.conf_regs_memmap_p + LPFC_MQ_DOORBELL;
phba->sli4_hba.BMBXregaddr =
phba->sli4_hba.conf_regs_memmap_p + LPFC_BMBX;
break;
+ case LPFC_SLI_INTF_IF_TYPE_6:
+ phba->sli4_hba.u.if_type2.EQDregaddr =
+ phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PORT_EQ_DELAY_OFFSET;
+ phba->sli4_hba.u.if_type2.ERR1regaddr =
+ phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PORT_ER1_OFFSET;
+ phba->sli4_hba.u.if_type2.ERR2regaddr =
+ phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PORT_ER2_OFFSET;
+ phba->sli4_hba.u.if_type2.CTRLregaddr =
+ phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PORT_CTL_OFFSET;
+ phba->sli4_hba.u.if_type2.STATUSregaddr =
+ phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PORT_STA_OFFSET;
+ phba->sli4_hba.PSMPHRregaddr =
+ phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_CTL_PORT_SEM_OFFSET;
+ phba->sli4_hba.BMBXregaddr =
+ phba->sli4_hba.conf_regs_memmap_p + LPFC_BMBX;
+ break;
case LPFC_SLI_INTF_IF_TYPE_1:
default:
dev_printk(KERN_ERR, &phba->pcidev->dev,
@@ -7446,20 +7487,43 @@ lpfc_sli4_bar0_register_memmap(struct lpfc_hba *phba, uint32_t if_type)
* lpfc_sli4_bar1_register_memmap - Set up SLI4 BAR1 register memory map.
* @phba: pointer to lpfc hba data structure.
*
- * This routine is invoked to set up SLI4 BAR1 control status register (CSR)
- * memory map.
+ * This routine is invoked to set up SLI4 BAR1 register memory map.
**/
static void
-lpfc_sli4_bar1_register_memmap(struct lpfc_hba *phba)
+lpfc_sli4_bar1_register_memmap(struct lpfc_hba *phba, uint32_t if_type)
{
- phba->sli4_hba.PSMPHRregaddr = phba->sli4_hba.ctrl_regs_memmap_p +
- LPFC_SLIPORT_IF0_SMPHR;
- phba->sli4_hba.ISRregaddr = phba->sli4_hba.ctrl_regs_memmap_p +
- LPFC_HST_ISR0;
- phba->sli4_hba.IMRregaddr = phba->sli4_hba.ctrl_regs_memmap_p +
- LPFC_HST_IMR0;
- phba->sli4_hba.ISCRregaddr = phba->sli4_hba.ctrl_regs_memmap_p +
- LPFC_HST_ISCR0;
+ switch (if_type) {
+ case LPFC_SLI_INTF_IF_TYPE_0:
+ phba->sli4_hba.PSMPHRregaddr =
+ phba->sli4_hba.ctrl_regs_memmap_p +
+ LPFC_SLIPORT_IF0_SMPHR;
+ phba->sli4_hba.ISRregaddr = phba->sli4_hba.ctrl_regs_memmap_p +
+ LPFC_HST_ISR0;
+ phba->sli4_hba.IMRregaddr = phba->sli4_hba.ctrl_regs_memmap_p +
+ LPFC_HST_IMR0;
+ phba->sli4_hba.ISCRregaddr = phba->sli4_hba.ctrl_regs_memmap_p +
+ LPFC_HST_ISCR0;
+ break;
+ case LPFC_SLI_INTF_IF_TYPE_6:
+ phba->sli4_hba.RQDBregaddr = phba->sli4_hba.drbl_regs_memmap_p +
+ LPFC_IF6_RQ_DOORBELL;
+ phba->sli4_hba.WQDBregaddr = phba->sli4_hba.drbl_regs_memmap_p +
+ LPFC_IF6_WQ_DOORBELL;
+ phba->sli4_hba.CQDBregaddr = phba->sli4_hba.drbl_regs_memmap_p +
+ LPFC_IF6_CQ_DOORBELL;
+ phba->sli4_hba.EQDBregaddr = phba->sli4_hba.drbl_regs_memmap_p +
+ LPFC_IF6_EQ_DOORBELL;
+ phba->sli4_hba.MQDBregaddr = phba->sli4_hba.drbl_regs_memmap_p +
+ LPFC_IF6_MQ_DOORBELL;
+ break;
+ case LPFC_SLI_INTF_IF_TYPE_2:
+ case LPFC_SLI_INTF_IF_TYPE_1:
+ default:
+ dev_err(&phba->pcidev->dev,
+ "FATAL - unsupported SLI4 interface type - %d\n",
+ if_type);
+ break;
+ }
}
/**
@@ -7484,8 +7548,10 @@ lpfc_sli4_bar2_register_memmap(struct lpfc_hba *phba, uint32_t vf)
phba->sli4_hba.WQDBregaddr = (phba->sli4_hba.drbl_regs_memmap_p +
vf * LPFC_VFR_PAGE_SIZE +
LPFC_ULP0_WQ_DOORBELL);
- phba->sli4_hba.EQCQDBregaddr = (phba->sli4_hba.drbl_regs_memmap_p +
- vf * LPFC_VFR_PAGE_SIZE + LPFC_EQCQ_DOORBELL);
+ phba->sli4_hba.CQDBregaddr = (phba->sli4_hba.drbl_regs_memmap_p +
+ vf * LPFC_VFR_PAGE_SIZE +
+ LPFC_EQCQ_DOORBELL);
+ phba->sli4_hba.EQDBregaddr = phba->sli4_hba.CQDBregaddr;
phba->sli4_hba.MQDBregaddr = (phba->sli4_hba.drbl_regs_memmap_p +
vf * LPFC_VFR_PAGE_SIZE + LPFC_MQ_DOORBELL);
phba->sli4_hba.BMBXregaddr = (phba->sli4_hba.drbl_regs_memmap_p +
@@ -7722,7 +7788,7 @@ lpfc_sli4_read_config(struct lpfc_hba *phba)
/* Update link speed if forced link speed is supported */
if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf);
- if (if_type == LPFC_SLI_INTF_IF_TYPE_2) {
+ if (if_type >= LPFC_SLI_INTF_IF_TYPE_2) {
forced_link_speed =
bf_get(lpfc_mbx_rd_conf_link_speed, rd_config);
if (forced_link_speed) {
@@ -7757,6 +7823,10 @@ lpfc_sli4_read_config(struct lpfc_hba *phba)
phba->cfg_link_speed =
LPFC_USER_LINK_SPEED_32G;
break;
+ case LINK_SPEED_64G:
+ phba->cfg_link_speed =
+ LPFC_USER_LINK_SPEED_64G;
+ break;
case 0xffff:
phba->cfg_link_speed =
LPFC_USER_LINK_SPEED_AUTO;
@@ -7782,7 +7852,7 @@ lpfc_sli4_read_config(struct lpfc_hba *phba)
phba->cfg_hba_queue_depth = length;
}
- if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) !=
+ if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) <
LPFC_SLI_INTF_IF_TYPE_2)
goto read_cfg_out;
@@ -7896,6 +7966,7 @@ lpfc_setup_endian_order(struct lpfc_hba *phba)
}
mempool_free(mboxq, phba->mbox_mem_pool);
break;
+ case LPFC_SLI_INTF_IF_TYPE_6:
case LPFC_SLI_INTF_IF_TYPE_2:
case LPFC_SLI_INTF_IF_TYPE_1:
default:
@@ -7992,6 +8063,7 @@ lpfc_alloc_nvme_wq_cq(struct lpfc_hba *phba, int wqidx)
wqidx);
return 1;
}
+ qdesc->qe_valid = 1;
phba->sli4_hba.nvme_cq[wqidx] = qdesc;
qdesc = lpfc_sli4_queue_alloc(phba, LPFC_EXPANDED_PAGE_SIZE,
@@ -8011,9 +8083,10 @@ static int
lpfc_alloc_fcp_wq_cq(struct lpfc_hba *phba, int wqidx)
{
struct lpfc_queue *qdesc;
+ uint32_t wqesize;
/* Create Fast Path FCP CQs */
- if (phba->fcp_embed_io)
+ if (phba->enab_exp_wqcq_pages)
/* Increase the CQ size when WQEs contain an embedded cdb */
qdesc = lpfc_sli4_queue_alloc(phba, LPFC_EXPANDED_PAGE_SIZE,
phba->sli4_hba.cq_esize,
@@ -8028,18 +8101,22 @@ lpfc_alloc_fcp_wq_cq(struct lpfc_hba *phba, int wqidx)
"0499 Failed allocate fast-path FCP CQ (%d)\n", wqidx);
return 1;
}
+ qdesc->qe_valid = 1;
phba->sli4_hba.fcp_cq[wqidx] = qdesc;
/* Create Fast Path FCP WQs */
- if (phba->fcp_embed_io)
+ if (phba->enab_exp_wqcq_pages) {
/* Increase the WQ size when WQEs contain an embedded cdb */
+ wqesize = (phba->fcp_embed_io) ?
+ LPFC_WQE128_SIZE : phba->sli4_hba.wq_esize;
qdesc = lpfc_sli4_queue_alloc(phba, LPFC_EXPANDED_PAGE_SIZE,
- LPFC_WQE128_SIZE,
+ wqesize,
LPFC_WQE_EXP_COUNT);
- else
+ } else
qdesc = lpfc_sli4_queue_alloc(phba, LPFC_DEFAULT_PAGE_SIZE,
phba->sli4_hba.wq_esize,
phba->sli4_hba.wq_ecount);
+
if (!qdesc) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"0503 Failed allocate fast-path FCP WQ (%d)\n",
@@ -8218,6 +8295,7 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba)
"0497 Failed allocate EQ (%d)\n", idx);
goto out_error;
}
+ qdesc->qe_valid = 1;
phba->sli4_hba.hba_eq[idx] = qdesc;
}
@@ -8243,6 +8321,7 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba)
"CQ Set (%d)\n", idx);
goto out_error;
}
+ qdesc->qe_valid = 1;
phba->sli4_hba.nvmet_cqset[idx] = qdesc;
}
}
@@ -8260,6 +8339,7 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba)
"0500 Failed allocate slow-path mailbox CQ\n");
goto out_error;
}
+ qdesc->qe_valid = 1;
phba->sli4_hba.mbx_cq = qdesc;
/* Create slow-path ELS Complete Queue */
@@ -8271,6 +8351,7 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba)
"0501 Failed allocate slow-path ELS CQ\n");
goto out_error;
}
+ qdesc->qe_valid = 1;
phba->sli4_hba.els_cq = qdesc;
@@ -8316,6 +8397,7 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba)
"6079 Failed allocate NVME LS CQ\n");
goto out_error;
}
+ qdesc->qe_valid = 1;
phba->sli4_hba.nvmels_cq = qdesc;
/* Create NVME LS Work Queue */
@@ -9303,6 +9385,7 @@ lpfc_pci_function_reset(struct lpfc_hba *phba)
}
break;
case LPFC_SLI_INTF_IF_TYPE_2:
+ case LPFC_SLI_INTF_IF_TYPE_6:
wait:
/*
* Poll the Port Status Register and wait for RDY for
@@ -9458,7 +9541,7 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba)
} else {
phba->pci_bar0_map = pci_resource_start(pdev, 1);
bar0map_len = pci_resource_len(pdev, 1);
- if (if_type == LPFC_SLI_INTF_IF_TYPE_2) {
+ if (if_type >= LPFC_SLI_INTF_IF_TYPE_2) {
dev_printk(KERN_ERR, &pdev->dev,
"FATAL - No BAR0 mapping for SLI4, if_type 2\n");
goto out;
@@ -9495,13 +9578,32 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba)
}
phba->pci_bar2_memmap_p =
phba->sli4_hba.ctrl_regs_memmap_p;
- lpfc_sli4_bar1_register_memmap(phba);
+ lpfc_sli4_bar1_register_memmap(phba, if_type);
} else {
error = -ENOMEM;
goto out_iounmap_conf;
}
}
+ if ((if_type == LPFC_SLI_INTF_IF_TYPE_6) &&
+ (pci_resource_start(pdev, PCI_64BIT_BAR2))) {
+ /*
+ * Map SLI4 if type 6 HBA Doorbell Register base to a kernel
+ * virtual address and setup the registers.
+ */
+ phba->pci_bar1_map = pci_resource_start(pdev, PCI_64BIT_BAR2);
+ bar1map_len = pci_resource_len(pdev, PCI_64BIT_BAR2);
+ phba->sli4_hba.drbl_regs_memmap_p =
+ ioremap(phba->pci_bar1_map, bar1map_len);
+ if (!phba->sli4_hba.drbl_regs_memmap_p) {
+ dev_err(&pdev->dev,
+ "ioremap failed for SLI4 HBA doorbell registers.\n");
+ goto out_iounmap_conf;
+ }
+ phba->pci_bar2_memmap_p = phba->sli4_hba.drbl_regs_memmap_p;
+ lpfc_sli4_bar1_register_memmap(phba, if_type);
+ }
+
if (if_type == LPFC_SLI_INTF_IF_TYPE_0) {
if (pci_resource_start(pdev, PCI_64BIT_BAR4)) {
/*
@@ -9532,6 +9634,41 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba)
}
}
+ if (if_type == LPFC_SLI_INTF_IF_TYPE_6 &&
+ pci_resource_start(pdev, PCI_64BIT_BAR4)) {
+ /*
+ * Map SLI4 if type 6 HBA DPP Register base to a kernel
+ * virtual address and setup the registers.
+ */
+ phba->pci_bar2_map = pci_resource_start(pdev, PCI_64BIT_BAR4);
+ bar2map_len = pci_resource_len(pdev, PCI_64BIT_BAR4);
+ phba->sli4_hba.dpp_regs_memmap_p =
+ ioremap(phba->pci_bar2_map, bar2map_len);
+ if (!phba->sli4_hba.dpp_regs_memmap_p) {
+ dev_err(&pdev->dev,
+ "ioremap failed for SLI4 HBA dpp registers.\n");
+ goto out_iounmap_ctrl;
+ }
+ phba->pci_bar4_memmap_p = phba->sli4_hba.dpp_regs_memmap_p;
+ }
+
+ /* Set up the EQ/CQ register handeling functions now */
+ switch (if_type) {
+ case LPFC_SLI_INTF_IF_TYPE_0:
+ case LPFC_SLI_INTF_IF_TYPE_2:
+ phba->sli4_hba.sli4_eq_clr_intr = lpfc_sli4_eq_clr_intr;
+ phba->sli4_hba.sli4_eq_release = lpfc_sli4_eq_release;
+ phba->sli4_hba.sli4_cq_release = lpfc_sli4_cq_release;
+ break;
+ case LPFC_SLI_INTF_IF_TYPE_6:
+ phba->sli4_hba.sli4_eq_clr_intr = lpfc_sli4_if6_eq_clr_intr;
+ phba->sli4_hba.sli4_eq_release = lpfc_sli4_if6_eq_release;
+ phba->sli4_hba.sli4_cq_release = lpfc_sli4_if6_cq_release;
+ break;
+ default:
+ break;
+ }
+
return 0;
out_iounmap_all:
@@ -9566,6 +9703,10 @@ lpfc_sli4_pci_mem_unset(struct lpfc_hba *phba)
case LPFC_SLI_INTF_IF_TYPE_2:
iounmap(phba->sli4_hba.conf_regs_memmap_p);
break;
+ case LPFC_SLI_INTF_IF_TYPE_6:
+ iounmap(phba->sli4_hba.drbl_regs_memmap_p);
+ iounmap(phba->sli4_hba.conf_regs_memmap_p);
+ break;
case LPFC_SLI_INTF_IF_TYPE_1:
default:
dev_printk(KERN_ERR, &phba->pcidev->dev,
@@ -10435,6 +10576,8 @@ lpfc_get_sli4_parameters(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
sli4_params->mqv = bf_get(cfg_mqv, mbx_sli4_parameters);
sli4_params->wqv = bf_get(cfg_wqv, mbx_sli4_parameters);
sli4_params->rqv = bf_get(cfg_rqv, mbx_sli4_parameters);
+ sli4_params->eqav = bf_get(cfg_eqav, mbx_sli4_parameters);
+ sli4_params->cqav = bf_get(cfg_cqav, mbx_sli4_parameters);
sli4_params->wqsize = bf_get(cfg_wqsize, mbx_sli4_parameters);
sli4_params->sgl_pages_max = bf_get(cfg_sgl_page_cnt,
mbx_sli4_parameters);
@@ -10465,8 +10608,32 @@ lpfc_get_sli4_parameters(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
phba->cfg_enable_fc4_type = LPFC_ENABLE_FCP;
}
- if (bf_get(cfg_xib, mbx_sli4_parameters) && phba->cfg_suppress_rsp)
+ /* Only embed PBDE for if_type 6 */
+ if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) ==
+ LPFC_SLI_INTF_IF_TYPE_6) {
+ phba->fcp_embed_pbde = 1;
+ phba->nvme_embed_pbde = 1;
+ }
+
+ /* PBDE support requires xib be set */
+ if (!bf_get(cfg_xib, mbx_sli4_parameters)) {
+ phba->fcp_embed_pbde = 0;
+ phba->nvme_embed_pbde = 0;
+ }
+
+ /*
+ * To support Suppress Response feature we must satisfy 3 conditions.
+ * lpfc_suppress_rsp module parameter must be set (default).
+ * In SLI4-Parameters Descriptor:
+ * Extended Inline Buffers (XIB) must be supported.
+ * Suppress Response IU Not Supported (SRIUNS) must NOT be supported
+ * (double negative).
+ */
+ if (phba->cfg_suppress_rsp && bf_get(cfg_xib, mbx_sli4_parameters) &&
+ !(bf_get(cfg_nosr, mbx_sli4_parameters)))
phba->sli.sli_flag |= LPFC_SLI_SUPPRESS_RSP;
+ else
+ phba->cfg_suppress_rsp = 0;
if (bf_get(cfg_eqdr, mbx_sli4_parameters))
phba->sli.sli_flag |= LPFC_SLI_USE_EQDR;
@@ -10476,15 +10643,28 @@ lpfc_get_sli4_parameters(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
sli4_params->sge_supp_len = LPFC_MAX_SGE_SIZE;
/*
- * Issue IOs with CDB embedded in WQE to minimized the number
- * of DMAs the firmware has to do. Setting this to 1 also forces
- * the driver to use 128 bytes WQEs for FCP IOs.
+ * Check whether the adapter supports an embedded copy of the
+ * FCP CMD IU within the WQE for FCP_Ixxx commands. In order
+ * to use this option, 128-byte WQEs must be used.
*/
if (bf_get(cfg_ext_embed_cb, mbx_sli4_parameters))
phba->fcp_embed_io = 1;
else
phba->fcp_embed_io = 0;
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT | LOG_NVME,
+ "6422 XIB %d: FCP %d %d NVME %d %d %d %d\n",
+ bf_get(cfg_xib, mbx_sli4_parameters),
+ phba->fcp_embed_pbde, phba->fcp_embed_io,
+ phba->nvme_support, phba->nvme_embed_pbde,
+ phba->cfg_nvme_embed_cmd, phba->cfg_suppress_rsp);
+
+ if ((bf_get(cfg_cqpsize, mbx_sli4_parameters) & LPFC_CQ_16K_PAGE_SZ) &&
+ (bf_get(cfg_wqpsize, mbx_sli4_parameters) & LPFC_WQ_16K_PAGE_SZ) &&
+ (sli4_params->wqsize & LPFC_WQ_SZ128_SUPPORT))
+ phba->enab_exp_wqcq_pages = 1;
+ else
+ phba->enab_exp_wqcq_pages = 0;
/*
* Check if the SLI port supports MDS Diagnostics
*/
@@ -11137,6 +11317,27 @@ lpfc_sli4_get_iocb_cnt(struct lpfc_hba *phba)
}
+static void
+lpfc_log_write_firmware_error(struct lpfc_hba *phba, uint32_t offset,
+ uint32_t magic_number, uint32_t ftype, uint32_t fid, uint32_t fsize,
+ const struct firmware *fw)
+{
+ if (offset == ADD_STATUS_FW_NOT_SUPPORTED)
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "3030 This firmware version is not supported on "
+ "this HBA model. Device:%x Magic:%x Type:%x "
+ "ID:%x Size %d %zd\n",
+ phba->pcidev->device, magic_number, ftype, fid,
+ fsize, fw->size);
+ else
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "3022 FW Download failed. Device:%x Magic:%x Type:%x "
+ "ID:%x Size %d %zd\n",
+ phba->pcidev->device, magic_number, ftype, fid,
+ fsize, fw->size);
+}
+
+
/**
* lpfc_write_firmware - attempt to write a firmware image to the port
* @fw: pointer to firmware image returned from request_firmware.
@@ -11164,20 +11365,10 @@ lpfc_write_firmware(const struct firmware *fw, void *context)
magic_number = be32_to_cpu(image->magic_number);
ftype = bf_get_be32(lpfc_grp_hdr_file_type, image);
- fid = bf_get_be32(lpfc_grp_hdr_id, image),
+ fid = bf_get_be32(lpfc_grp_hdr_id, image);
fsize = be32_to_cpu(image->size);
INIT_LIST_HEAD(&dma_buffer_list);
- if ((magic_number != LPFC_GROUP_OJECT_MAGIC_G5 &&
- magic_number != LPFC_GROUP_OJECT_MAGIC_G6) ||
- ftype != LPFC_FILE_TYPE_GROUP || fsize != fw->size) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "3022 Invalid FW image found. "
- "Magic:%x Type:%x ID:%x Size %d %zd\n",
- magic_number, ftype, fid, fsize, fw->size);
- rc = -EINVAL;
- goto release_out;
- }
lpfc_decode_firmware_rev(phba, fwrev, 1);
if (strncmp(fwrev, image->revision, strnlen(image->revision, 16))) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
@@ -11218,11 +11409,18 @@ lpfc_write_firmware(const struct firmware *fw, void *context)
}
rc = lpfc_wr_object(phba, &dma_buffer_list,
(fw->size - offset), &offset);
- if (rc)
+ if (rc) {
+ lpfc_log_write_firmware_error(phba, offset,
+ magic_number, ftype, fid, fsize, fw);
goto release_out;
+ }
}
rc = offset;
- }
+ } else
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "3029 Skipped Firmware update, Current "
+ "Version:%s New Version:%s\n",
+ fwrev, image->revision);
release_out:
list_for_each_entry_safe(dmabuf, next, &dma_buffer_list, list) {
@@ -11253,7 +11451,7 @@ lpfc_sli4_request_firmware_update(struct lpfc_hba *phba, uint8_t fw_upgrade)
const struct firmware *fw;
/* Only supported on SLI4 interface type 2 for now */
- if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) !=
+ if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) <
LPFC_SLI_INTF_IF_TYPE_2)
return -EPERM;
@@ -11493,13 +11691,6 @@ lpfc_pci_remove_one_s4(struct pci_dev *pdev)
/* Remove FC host and then SCSI host with the physical port */
fc_remove_host(shost);
scsi_remove_host(shost);
- /*
- * Bring down the SLI Layer. This step disables all interrupts,
- * clears the rings, discards all mailbox commands, and resets
- * the HBA FCoE function.
- */
- lpfc_debugfs_terminate(vport);
- lpfc_sli4_hba_unset(phba);
/* Perform ndlp cleanup on the physical port. The nvme and nvmet
* localports are destroyed after to cleanup all transport memory.
@@ -11508,6 +11699,13 @@ lpfc_pci_remove_one_s4(struct pci_dev *pdev)
lpfc_nvmet_destroy_targetport(phba);
lpfc_nvme_destroy_localport(vport);
+ /*
+ * Bring down the SLI Layer. This step disables all interrupts,
+ * clears the rings, discards all mailbox commands, and resets
+ * the HBA FCoE function.
+ */
+ lpfc_debugfs_terminate(vport);
+ lpfc_sli4_hba_unset(phba);
lpfc_stop_hba_timers(phba);
spin_lock_irq(&phba->hbalock);
@@ -12227,6 +12425,7 @@ int
lpfc_fof_queue_create(struct lpfc_hba *phba)
{
struct lpfc_queue *qdesc;
+ uint32_t wqesize;
/* Create FOF EQ */
qdesc = lpfc_sli4_queue_alloc(phba, LPFC_DEFAULT_PAGE_SIZE,
@@ -12235,12 +12434,13 @@ lpfc_fof_queue_create(struct lpfc_hba *phba)
if (!qdesc)
goto out_error;
+ qdesc->qe_valid = 1;
phba->sli4_hba.fof_eq = qdesc;
if (phba->cfg_fof) {
/* Create OAS CQ */
- if (phba->fcp_embed_io)
+ if (phba->enab_exp_wqcq_pages)
qdesc = lpfc_sli4_queue_alloc(phba,
LPFC_EXPANDED_PAGE_SIZE,
phba->sli4_hba.cq_esize,
@@ -12253,19 +12453,23 @@ lpfc_fof_queue_create(struct lpfc_hba *phba)
if (!qdesc)
goto out_error;
+ qdesc->qe_valid = 1;
phba->sli4_hba.oas_cq = qdesc;
/* Create OAS WQ */
- if (phba->fcp_embed_io)
+ if (phba->enab_exp_wqcq_pages) {
+ wqesize = (phba->fcp_embed_io) ?
+ LPFC_WQE128_SIZE : phba->sli4_hba.wq_esize;
qdesc = lpfc_sli4_queue_alloc(phba,
LPFC_EXPANDED_PAGE_SIZE,
- LPFC_WQE128_SIZE,
+ wqesize,
LPFC_WQE_EXP_COUNT);
- else
+ } else
qdesc = lpfc_sli4_queue_alloc(phba,
LPFC_DEFAULT_PAGE_SIZE,
phba->sli4_hba.wq_esize,
phba->sli4_hba.wq_ecount);
+
if (!qdesc)
goto out_error;
@@ -12379,6 +12583,8 @@ lpfc_init(void)
fc_release_transport(lpfc_transport_template);
return -ENOMEM;
}
+ lpfc_nvme_cmd_template();
+ lpfc_nvmet_cmd_template();
/* Initialize in case vector mapping is needed */
lpfc_used_cpu = NULL;
diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c
index 81fb92967b11..47c02da11f01 100644
--- a/drivers/scsi/lpfc/lpfc_mbox.c
+++ b/drivers/scsi/lpfc/lpfc_mbox.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Limited and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -557,6 +557,10 @@ lpfc_init_link(struct lpfc_hba * phba,
mb->un.varInitLnk.link_flags |= FLAGS_LINK_SPEED;
mb->un.varInitLnk.link_speed = LINK_SPEED_32G;
break;
+ case LPFC_USER_LINK_SPEED_64G:
+ mb->un.varInitLnk.link_flags |= FLAGS_LINK_SPEED;
+ mb->un.varInitLnk.link_speed = LINK_SPEED_64G;
+ break;
case LPFC_USER_LINK_SPEED_AUTO:
default:
mb->un.varInitLnk.link_speed = LINK_SPEED_AUTO;
@@ -2170,10 +2174,8 @@ lpfc_reg_vfi(struct lpfcMboxq *mbox, struct lpfc_vport *vport, dma_addr_t phys)
/* Only FC supports upd bit */
if ((phba->sli4_hba.lnk_info.lnk_tp == LPFC_LNK_TYPE_FC) &&
(vport->fc_flag & FC_VFI_REGISTERED) &&
- (!phba->fc_topology_changed)) {
- bf_set(lpfc_reg_vfi_vp, reg_vfi, 0);
+ (!phba->fc_topology_changed))
bf_set(lpfc_reg_vfi_upd, reg_vfi, 1);
- }
bf_set(lpfc_reg_vfi_bbcr, reg_vfi, 0);
bf_set(lpfc_reg_vfi_bbscn, reg_vfi, 0);
diff --git a/drivers/scsi/lpfc/lpfc_mem.c b/drivers/scsi/lpfc/lpfc_mem.c
index 87c08ff37ddd..41361662ff08 100644
--- a/drivers/scsi/lpfc/lpfc_mem.c
+++ b/drivers/scsi/lpfc/lpfc_mem.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Limited and/or its subsidiaries. *
* Copyright (C) 2004-2014 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -753,12 +753,16 @@ lpfc_rq_buf_free(struct lpfc_hba *phba, struct lpfc_dmabuf *mp)
drqe.address_hi = putPaddrHigh(rqb_entry->dbuf.phys);
rc = lpfc_sli4_rq_put(rqb_entry->hrq, rqb_entry->drq, &hrqe, &drqe);
if (rc < 0) {
+ (rqbp->rqb_free_buffer)(phba, rqb_entry);
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "6409 Cannot post to RQ %d: %x %x\n",
+ "6409 Cannot post to HRQ %d: %x %x %x "
+ "DRQ %x %x\n",
rqb_entry->hrq->queue_id,
rqb_entry->hrq->host_index,
- rqb_entry->hrq->hba_index);
- (rqbp->rqb_free_buffer)(phba, rqb_entry);
+ rqb_entry->hrq->hba_index,
+ rqb_entry->hrq->entry_count,
+ rqb_entry->drq->host_index,
+ rqb_entry->drq->hba_index);
} else {
list_add_tail(&rqb_entry->hbuf.list, &rqbp->rqb_buffer_list);
rqbp->buffer_count++;
diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c
index d841aa42f607..022060636ae1 100644
--- a/drivers/scsi/lpfc/lpfc_nportdisc.c
+++ b/drivers/scsi/lpfc/lpfc_nportdisc.c
@@ -1,7 +1,7 @@
- /*******************************************************************
+/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Limited and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -1998,8 +1998,14 @@ lpfc_cmpl_prli_prli_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
ndlp->nlp_type |= NLP_NVME_TARGET;
if (bf_get_be32(prli_disc, nvpr))
ndlp->nlp_type |= NLP_NVME_DISCOVERY;
+
+ /*
+ * If prli_fba is set, the Target supports FirstBurst.
+ * If prli_fb_sz is 0, the FirstBurst size is unlimited,
+ * otherwise it defines the actual size supported by
+ * the NVME Target.
+ */
if ((bf_get_be32(prli_fba, nvpr) == 1) &&
- (bf_get_be32(prli_fb_sz, nvpr) > 0) &&
(phba->cfg_nvme_enable_fb) &&
(!phba->nvmet_support)) {
/* Both sides support FB. The target's first
@@ -2008,12 +2014,16 @@ lpfc_cmpl_prli_prli_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
ndlp->nlp_flag |= NLP_FIRSTBURST;
ndlp->nvme_fb_size = bf_get_be32(prli_fb_sz,
nvpr);
+
+ /* Expressed in units of 512 bytes */
+ if (ndlp->nvme_fb_size)
+ ndlp->nvme_fb_size <<=
+ LPFC_NVME_FB_SHIFT;
+ else
+ ndlp->nvme_fb_size = LPFC_NVME_MAX_FB;
}
}
- if (bf_get_be32(prli_recov, nvpr))
- ndlp->nlp_fcp_info |= NLP_FCP_2_DEVICE;
-
lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC,
"6029 NVME PRLI Cmpl w1 x%08x "
"w4 x%08x w5 x%08x flag x%x, "
diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c
index 81e3a4f10c3c..378dca40ca20 100644
--- a/drivers/scsi/lpfc/lpfc_nvme.c
+++ b/drivers/scsi/lpfc/lpfc_nvme.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Limited and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -65,6 +65,136 @@ lpfc_release_nvme_buf(struct lpfc_hba *, struct lpfc_nvme_buf *);
static struct nvme_fc_port_template lpfc_nvme_template;
+static union lpfc_wqe128 lpfc_iread_cmd_template;
+static union lpfc_wqe128 lpfc_iwrite_cmd_template;
+static union lpfc_wqe128 lpfc_icmnd_cmd_template;
+
+/* Setup WQE templates for NVME IOs */
+void
+lpfc_nvme_cmd_template(void)
+{
+ union lpfc_wqe128 *wqe;
+
+ /* IREAD template */
+ wqe = &lpfc_iread_cmd_template;
+ memset(wqe, 0, sizeof(union lpfc_wqe128));
+
+ /* Word 0, 1, 2 - BDE is variable */
+
+ /* Word 3 - cmd_buff_len, payload_offset_len is zero */
+
+ /* Word 4 - total_xfer_len is variable */
+
+ /* Word 5 - is zero */
+
+ /* Word 6 - ctxt_tag, xri_tag is variable */
+
+ /* Word 7 */
+ bf_set(wqe_cmnd, &wqe->fcp_iread.wqe_com, CMD_FCP_IREAD64_WQE);
+ bf_set(wqe_pu, &wqe->fcp_iread.wqe_com, PARM_READ_CHECK);
+ bf_set(wqe_class, &wqe->fcp_iread.wqe_com, CLASS3);
+ bf_set(wqe_ct, &wqe->fcp_iread.wqe_com, SLI4_CT_RPI);
+
+ /* Word 8 - abort_tag is variable */
+
+ /* Word 9 - reqtag is variable */
+
+ /* Word 10 - dbde, wqes is variable */
+ bf_set(wqe_qosd, &wqe->fcp_iread.wqe_com, 0);
+ bf_set(wqe_nvme, &wqe->fcp_iread.wqe_com, 1);
+ bf_set(wqe_iod, &wqe->fcp_iread.wqe_com, LPFC_WQE_IOD_READ);
+ bf_set(wqe_lenloc, &wqe->fcp_iread.wqe_com, LPFC_WQE_LENLOC_WORD4);
+ bf_set(wqe_dbde, &wqe->fcp_iread.wqe_com, 0);
+ bf_set(wqe_wqes, &wqe->fcp_iread.wqe_com, 1);
+
+ /* Word 11 - pbde is variable */
+ bf_set(wqe_cmd_type, &wqe->fcp_iread.wqe_com, NVME_READ_CMD);
+ bf_set(wqe_cqid, &wqe->fcp_iread.wqe_com, LPFC_WQE_CQ_ID_DEFAULT);
+ bf_set(wqe_pbde, &wqe->fcp_iread.wqe_com, 1);
+
+ /* Word 12 - is zero */
+
+ /* Word 13, 14, 15 - PBDE is variable */
+
+ /* IWRITE template */
+ wqe = &lpfc_iwrite_cmd_template;
+ memset(wqe, 0, sizeof(union lpfc_wqe128));
+
+ /* Word 0, 1, 2 - BDE is variable */
+
+ /* Word 3 - cmd_buff_len, payload_offset_len is zero */
+
+ /* Word 4 - total_xfer_len is variable */
+
+ /* Word 5 - initial_xfer_len is variable */
+
+ /* Word 6 - ctxt_tag, xri_tag is variable */
+
+ /* Word 7 */
+ bf_set(wqe_cmnd, &wqe->fcp_iwrite.wqe_com, CMD_FCP_IWRITE64_WQE);
+ bf_set(wqe_pu, &wqe->fcp_iwrite.wqe_com, PARM_READ_CHECK);
+ bf_set(wqe_class, &wqe->fcp_iwrite.wqe_com, CLASS3);
+ bf_set(wqe_ct, &wqe->fcp_iwrite.wqe_com, SLI4_CT_RPI);
+
+ /* Word 8 - abort_tag is variable */
+
+ /* Word 9 - reqtag is variable */
+
+ /* Word 10 - dbde, wqes is variable */
+ bf_set(wqe_qosd, &wqe->fcp_iwrite.wqe_com, 0);
+ bf_set(wqe_nvme, &wqe->fcp_iwrite.wqe_com, 1);
+ bf_set(wqe_iod, &wqe->fcp_iwrite.wqe_com, LPFC_WQE_IOD_WRITE);
+ bf_set(wqe_lenloc, &wqe->fcp_iwrite.wqe_com, LPFC_WQE_LENLOC_WORD4);
+ bf_set(wqe_dbde, &wqe->fcp_iwrite.wqe_com, 0);
+ bf_set(wqe_wqes, &wqe->fcp_iwrite.wqe_com, 1);
+
+ /* Word 11 - pbde is variable */
+ bf_set(wqe_cmd_type, &wqe->fcp_iwrite.wqe_com, NVME_WRITE_CMD);
+ bf_set(wqe_cqid, &wqe->fcp_iwrite.wqe_com, LPFC_WQE_CQ_ID_DEFAULT);
+ bf_set(wqe_pbde, &wqe->fcp_iwrite.wqe_com, 1);
+
+ /* Word 12 - is zero */
+
+ /* Word 13, 14, 15 - PBDE is variable */
+
+ /* ICMND template */
+ wqe = &lpfc_icmnd_cmd_template;
+ memset(wqe, 0, sizeof(union lpfc_wqe128));
+
+ /* Word 0, 1, 2 - BDE is variable */
+
+ /* Word 3 - payload_offset_len is variable */
+
+ /* Word 4, 5 - is zero */
+
+ /* Word 6 - ctxt_tag, xri_tag is variable */
+
+ /* Word 7 */
+ bf_set(wqe_cmnd, &wqe->fcp_icmd.wqe_com, CMD_FCP_ICMND64_WQE);
+ bf_set(wqe_pu, &wqe->fcp_icmd.wqe_com, 0);
+ bf_set(wqe_class, &wqe->fcp_icmd.wqe_com, CLASS3);
+ bf_set(wqe_ct, &wqe->fcp_icmd.wqe_com, SLI4_CT_RPI);
+
+ /* Word 8 - abort_tag is variable */
+
+ /* Word 9 - reqtag is variable */
+
+ /* Word 10 - dbde, wqes is variable */
+ bf_set(wqe_qosd, &wqe->fcp_icmd.wqe_com, 1);
+ bf_set(wqe_nvme, &wqe->fcp_icmd.wqe_com, 1);
+ bf_set(wqe_iod, &wqe->fcp_icmd.wqe_com, LPFC_WQE_IOD_NONE);
+ bf_set(wqe_lenloc, &wqe->fcp_icmd.wqe_com, LPFC_WQE_LENLOC_NONE);
+ bf_set(wqe_dbde, &wqe->fcp_icmd.wqe_com, 0);
+ bf_set(wqe_wqes, &wqe->fcp_icmd.wqe_com, 1);
+
+ /* Word 11 */
+ bf_set(wqe_cmd_type, &wqe->fcp_icmd.wqe_com, FCP_COMMAND);
+ bf_set(wqe_cqid, &wqe->fcp_icmd.wqe_com, LPFC_WQE_CQ_ID_DEFAULT);
+ bf_set(wqe_pbde, &wqe->fcp_icmd.wqe_com, 0);
+
+ /* Word 12, 13, 14, 15 - is zero */
+}
+
/**
* lpfc_nvme_create_queue -
* @lpfc_pnvme: Pointer to the driver's nvme instance data
@@ -241,10 +371,11 @@ lpfc_nvme_cmpl_gen_req(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe,
ndlp = (struct lpfc_nodelist *)cmdwqe->context1;
lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC,
"6047 nvme cmpl Enter "
- "Data %p DID %x Xri: %x status %x cmd:%p lsreg:%p "
- "bmp:%p ndlp:%p\n",
+ "Data %p DID %x Xri: %x status %x reason x%x cmd:%p "
+ "lsreg:%p bmp:%p ndlp:%p\n",
pnvme_lsreq, ndlp ? ndlp->nlp_DID : 0,
cmdwqe->sli4_xritag, status,
+ (wcqe->parameter & 0xffff),
cmdwqe, pnvme_lsreq, cmdwqe->context3, ndlp);
lpfc_nvmeio_data(phba, "NVME LS CMPL: xri x%x stat x%x parm x%x\n",
@@ -274,14 +405,14 @@ lpfc_nvme_cmpl_gen_req(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe,
static int
lpfc_nvme_gen_req(struct lpfc_vport *vport, struct lpfc_dmabuf *bmp,
struct lpfc_dmabuf *inp,
- struct nvmefc_ls_req *pnvme_lsreq,
- void (*cmpl)(struct lpfc_hba *, struct lpfc_iocbq *,
- struct lpfc_wcqe_complete *),
- struct lpfc_nodelist *ndlp, uint32_t num_entry,
- uint32_t tmo, uint8_t retry)
+ struct nvmefc_ls_req *pnvme_lsreq,
+ void (*cmpl)(struct lpfc_hba *, struct lpfc_iocbq *,
+ struct lpfc_wcqe_complete *),
+ struct lpfc_nodelist *ndlp, uint32_t num_entry,
+ uint32_t tmo, uint8_t retry)
{
- struct lpfc_hba *phba = vport->phba;
- union lpfc_wqe *wqe;
+ struct lpfc_hba *phba = vport->phba;
+ union lpfc_wqe128 *wqe;
struct lpfc_iocbq *genwqe;
struct ulp_bde64 *bpl;
struct ulp_bde64 bde;
@@ -419,6 +550,7 @@ lpfc_nvme_ls_req(struct nvme_fc_local_port *pnvme_lport,
{
int ret = 0;
struct lpfc_nvme_lport *lport;
+ struct lpfc_nvme_rport *rport;
struct lpfc_vport *vport;
struct lpfc_nodelist *ndlp;
struct ulp_bde64 *bpl;
@@ -437,19 +569,18 @@ lpfc_nvme_ls_req(struct nvme_fc_local_port *pnvme_lport,
*/
lport = (struct lpfc_nvme_lport *)pnvme_lport->private;
+ rport = (struct lpfc_nvme_rport *)pnvme_rport->private;
vport = lport->vport;
if (vport->load_flag & FC_UNLOADING)
return -ENODEV;
- if (vport->load_flag & FC_UNLOADING)
- return -ENODEV;
-
- ndlp = lpfc_findnode_did(vport, pnvme_rport->port_id);
+ /* Need the ndlp. It is stored in the driver's rport. */
+ ndlp = rport->ndlp;
if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE | LOG_NVME_IOERR,
- "6051 DID x%06x not an active rport.\n",
- pnvme_rport->port_id);
+ "6051 Remoteport %p, rport has invalid ndlp. "
+ "Failing LS Req\n", pnvme_rport);
return -ENODEV;
}
@@ -500,8 +631,9 @@ lpfc_nvme_ls_req(struct nvme_fc_local_port *pnvme_lport,
/* Expand print to include key fields. */
lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC,
- "6149 ENTER. lport %p, rport %p lsreq%p rqstlen:%d "
- "rsplen:%d %pad %pad\n",
+ "6149 Issue LS Req to DID 0x%06x lport %p, rport %p "
+ "lsreq%p rqstlen:%d rsplen:%d %pad %pad\n",
+ ndlp->nlp_DID,
pnvme_lport, pnvme_rport,
pnvme_lsreq, pnvme_lsreq->rqstlen,
pnvme_lsreq->rsplen, &pnvme_lsreq->rqstdma,
@@ -517,7 +649,7 @@ lpfc_nvme_ls_req(struct nvme_fc_local_port *pnvme_lport,
ndlp, 2, 30, 0);
if (ret != WQE_SUCCESS) {
atomic_inc(&lport->xmt_ls_err);
- lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC,
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_DISC,
"6052 EXIT. issue ls wqe failed lport %p, "
"rport %p lsreq%p Status %x DID %x\n",
pnvme_lport, pnvme_rport, pnvme_lsreq,
@@ -610,16 +742,25 @@ lpfc_nvme_ls_abort(struct nvme_fc_local_port *pnvme_lport,
}
/* Fix up the existing sgls for NVME IO. */
-static void
+static inline void
lpfc_nvme_adj_fcp_sgls(struct lpfc_vport *vport,
struct lpfc_nvme_buf *lpfc_ncmd,
struct nvmefc_fcp_req *nCmd)
{
+ struct lpfc_hba *phba = vport->phba;
struct sli4_sge *sgl;
union lpfc_wqe128 *wqe;
uint32_t *wptr, *dptr;
/*
+ * Get a local pointer to the built-in wqe and correct
+ * the cmd size to match NVME's 96 bytes and fix
+ * the dma address.
+ */
+
+ wqe = &lpfc_ncmd->cur_iocbq.wqe;
+
+ /*
* Adjust the FCP_CMD and FCP_RSP DMA data and sge_len to
* match NVME. NVME sends 96 bytes. Also, use the
* nvme commands command and response dma addresses
@@ -628,6 +769,60 @@ lpfc_nvme_adj_fcp_sgls(struct lpfc_vport *vport,
*/
sgl = lpfc_ncmd->nvme_sgl;
sgl->sge_len = cpu_to_le32(nCmd->cmdlen);
+ if (phba->cfg_nvme_embed_cmd) {
+ sgl->addr_hi = 0;
+ sgl->addr_lo = 0;
+
+ /* Word 0-2 - NVME CMND IU (embedded payload) */
+ wqe->generic.bde.tus.f.bdeFlags = BUFF_TYPE_BDE_IMMED;
+ wqe->generic.bde.tus.f.bdeSize = 56;
+ wqe->generic.bde.addrHigh = 0;
+ wqe->generic.bde.addrLow = 64; /* Word 16 */
+
+ /* Word 10 - dbde is 0, wqes is 1 in template */
+
+ /*
+ * Embed the payload in the last half of the WQE
+ * WQE words 16-30 get the NVME CMD IU payload
+ *
+ * WQE words 16-19 get payload Words 1-4
+ * WQE words 20-21 get payload Words 6-7
+ * WQE words 22-29 get payload Words 16-23
+ */
+ wptr = &wqe->words[16]; /* WQE ptr */
+ dptr = (uint32_t *)nCmd->cmdaddr; /* payload ptr */
+ dptr++; /* Skip Word 0 in payload */
+
+ *wptr++ = *dptr++; /* Word 1 */
+ *wptr++ = *dptr++; /* Word 2 */
+ *wptr++ = *dptr++; /* Word 3 */
+ *wptr++ = *dptr++; /* Word 4 */
+ dptr++; /* Skip Word 5 in payload */
+ *wptr++ = *dptr++; /* Word 6 */
+ *wptr++ = *dptr++; /* Word 7 */
+ dptr += 8; /* Skip Words 8-15 in payload */
+ *wptr++ = *dptr++; /* Word 16 */
+ *wptr++ = *dptr++; /* Word 17 */
+ *wptr++ = *dptr++; /* Word 18 */
+ *wptr++ = *dptr++; /* Word 19 */
+ *wptr++ = *dptr++; /* Word 20 */
+ *wptr++ = *dptr++; /* Word 21 */
+ *wptr++ = *dptr++; /* Word 22 */
+ *wptr = *dptr; /* Word 23 */
+ } else {
+ sgl->addr_hi = cpu_to_le32(putPaddrHigh(nCmd->cmddma));
+ sgl->addr_lo = cpu_to_le32(putPaddrLow(nCmd->cmddma));
+
+ /* Word 0-2 - NVME CMND IU Inline BDE */
+ wqe->generic.bde.tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+ wqe->generic.bde.tus.f.bdeSize = nCmd->cmdlen;
+ wqe->generic.bde.addrHigh = sgl->addr_hi;
+ wqe->generic.bde.addrLow = sgl->addr_lo;
+
+ /* Word 10 */
+ bf_set(wqe_dbde, &wqe->generic.wqe_com, 1);
+ bf_set(wqe_wqes, &wqe->generic.wqe_com, 0);
+ }
sgl++;
@@ -641,58 +836,6 @@ lpfc_nvme_adj_fcp_sgls(struct lpfc_vport *vport,
bf_set(lpfc_sli4_sge_last, sgl, 1);
sgl->word2 = cpu_to_le32(sgl->word2);
sgl->sge_len = cpu_to_le32(nCmd->rsplen);
-
- /*
- * Get a local pointer to the built-in wqe and correct
- * the cmd size to match NVME's 96 bytes and fix
- * the dma address.
- */
-
- /* 128 byte wqe support here */
- wqe = (union lpfc_wqe128 *)&lpfc_ncmd->cur_iocbq.wqe;
-
- /* Word 0-2 - NVME CMND IU (embedded payload) */
- wqe->generic.bde.tus.f.bdeFlags = BUFF_TYPE_BDE_IMMED;
- wqe->generic.bde.tus.f.bdeSize = 60;
- wqe->generic.bde.addrHigh = 0;
- wqe->generic.bde.addrLow = 64; /* Word 16 */
-
- /* Word 3 */
- bf_set(payload_offset_len, &wqe->fcp_icmd,
- (nCmd->rsplen + nCmd->cmdlen));
-
- /* Word 10 */
- bf_set(wqe_nvme, &wqe->fcp_icmd.wqe_com, 1);
- bf_set(wqe_wqes, &wqe->fcp_icmd.wqe_com, 1);
-
- /*
- * Embed the payload in the last half of the WQE
- * WQE words 16-30 get the NVME CMD IU payload
- *
- * WQE words 16-19 get payload Words 1-4
- * WQE words 20-21 get payload Words 6-7
- * WQE words 22-29 get payload Words 16-23
- */
- wptr = &wqe->words[16]; /* WQE ptr */
- dptr = (uint32_t *)nCmd->cmdaddr; /* payload ptr */
- dptr++; /* Skip Word 0 in payload */
-
- *wptr++ = *dptr++; /* Word 1 */
- *wptr++ = *dptr++; /* Word 2 */
- *wptr++ = *dptr++; /* Word 3 */
- *wptr++ = *dptr++; /* Word 4 */
- dptr++; /* Skip Word 5 in payload */
- *wptr++ = *dptr++; /* Word 6 */
- *wptr++ = *dptr++; /* Word 7 */
- dptr += 8; /* Skip Words 8-15 in payload */
- *wptr++ = *dptr++; /* Word 16 */
- *wptr++ = *dptr++; /* Word 17 */
- *wptr++ = *dptr++; /* Word 18 */
- *wptr++ = *dptr++; /* Word 19 */
- *wptr++ = *dptr++; /* Word 20 */
- *wptr++ = *dptr++; /* Word 21 */
- *wptr++ = *dptr++; /* Word 22 */
- *wptr = *dptr; /* Word 23 */
}
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
@@ -980,14 +1123,14 @@ out_err:
phba->cpucheck_cmpl_io[lpfc_ncmd->cpu]++;
}
#endif
- freqpriv = nCmd->private;
- freqpriv->nvme_buf = NULL;
/* NVME targets need completion held off until the abort exchange
* completes unless the NVME Rport is getting unregistered.
*/
if (!(lpfc_ncmd->flags & LPFC_SBUF_XBUSY)) {
+ freqpriv = nCmd->private;
+ freqpriv->nvme_buf = NULL;
nCmd->done(nCmd);
lpfc_ncmd->nvmeCmd = NULL;
}
@@ -1025,7 +1168,7 @@ lpfc_nvme_prep_io_cmd(struct lpfc_vport *vport,
struct lpfc_hba *phba = vport->phba;
struct nvmefc_fcp_req *nCmd = lpfc_ncmd->nvmeCmd;
struct lpfc_iocbq *pwqeq = &(lpfc_ncmd->cur_iocbq);
- union lpfc_wqe128 *wqe = (union lpfc_wqe128 *)&pwqeq->wqe;
+ union lpfc_wqe128 *wqe = &pwqeq->wqe;
uint32_t req_len;
if (!pnode || !NLP_CHK_NODE_ACT(pnode))
@@ -1035,9 +1178,16 @@ lpfc_nvme_prep_io_cmd(struct lpfc_vport *vport,
* There are three possibilities here - use scatter-gather segment, use
* the single mapping, or neither.
*/
- wqe->fcp_iwrite.initial_xfer_len = 0;
if (nCmd->sg_cnt) {
if (nCmd->io_dir == NVMEFC_FCP_WRITE) {
+ /* From the iwrite template, initialize words 7 - 11 */
+ memcpy(&wqe->words[7],
+ &lpfc_iwrite_cmd_template.words[7],
+ sizeof(uint32_t) * 5);
+
+ /* Word 4 */
+ wqe->fcp_iwrite.total_xfer_len = nCmd->payload_length;
+
/* Word 5 */
if ((phba->cfg_nvme_enable_fb) &&
(pnode->nlp_flag & NLP_FIRSTBURST)) {
@@ -1048,69 +1198,28 @@ lpfc_nvme_prep_io_cmd(struct lpfc_vport *vport,
else
wqe->fcp_iwrite.initial_xfer_len =
pnode->nvme_fb_size;
+ } else {
+ wqe->fcp_iwrite.initial_xfer_len = 0;
}
-
- /* Word 7 */
- bf_set(wqe_cmnd, &wqe->generic.wqe_com,
- CMD_FCP_IWRITE64_WQE);
- bf_set(wqe_pu, &wqe->generic.wqe_com,
- PARM_READ_CHECK);
-
- /* Word 10 */
- bf_set(wqe_qosd, &wqe->fcp_iwrite.wqe_com, 0);
- bf_set(wqe_iod, &wqe->fcp_iwrite.wqe_com,
- LPFC_WQE_IOD_WRITE);
- bf_set(wqe_lenloc, &wqe->fcp_iwrite.wqe_com,
- LPFC_WQE_LENLOC_WORD4);
- if (phba->cfg_nvme_oas)
- bf_set(wqe_oas, &wqe->fcp_iwrite.wqe_com, 1);
-
- /* Word 11 */
- bf_set(wqe_cmd_type, &wqe->generic.wqe_com,
- NVME_WRITE_CMD);
-
atomic_inc(&phba->fc4NvmeOutputRequests);
} else {
- /* Word 7 */
- bf_set(wqe_cmnd, &wqe->generic.wqe_com,
- CMD_FCP_IREAD64_WQE);
- bf_set(wqe_pu, &wqe->generic.wqe_com,
- PARM_READ_CHECK);
-
- /* Word 10 */
- bf_set(wqe_qosd, &wqe->fcp_iread.wqe_com, 0);
- bf_set(wqe_iod, &wqe->fcp_iread.wqe_com,
- LPFC_WQE_IOD_READ);
- bf_set(wqe_lenloc, &wqe->fcp_iread.wqe_com,
- LPFC_WQE_LENLOC_WORD4);
- if (phba->cfg_nvme_oas)
- bf_set(wqe_oas, &wqe->fcp_iread.wqe_com, 1);
-
- /* Word 11 */
- bf_set(wqe_cmd_type, &wqe->generic.wqe_com,
- NVME_READ_CMD);
+ /* From the iread template, initialize words 7 - 11 */
+ memcpy(&wqe->words[7],
+ &lpfc_iread_cmd_template.words[7],
+ sizeof(uint32_t) * 5);
+
+ /* Word 4 */
+ wqe->fcp_iread.total_xfer_len = nCmd->payload_length;
+
+ /* Word 5 */
+ wqe->fcp_iread.rsrvd5 = 0;
atomic_inc(&phba->fc4NvmeInputRequests);
}
} else {
- /* Word 4 */
- wqe->fcp_icmd.rsrvd4 = 0;
-
- /* Word 7 */
- bf_set(wqe_cmnd, &wqe->generic.wqe_com, CMD_FCP_ICMND64_WQE);
- bf_set(wqe_pu, &wqe->generic.wqe_com, 0);
-
- /* Word 10 */
- bf_set(wqe_qosd, &wqe->fcp_icmd.wqe_com, 1);
- bf_set(wqe_iod, &wqe->fcp_icmd.wqe_com, LPFC_WQE_IOD_WRITE);
- bf_set(wqe_lenloc, &wqe->fcp_icmd.wqe_com,
- LPFC_WQE_LENLOC_NONE);
- if (phba->cfg_nvme_oas)
- bf_set(wqe_oas, &wqe->fcp_icmd.wqe_com, 1);
-
- /* Word 11 */
- bf_set(wqe_cmd_type, &wqe->generic.wqe_com, NVME_READ_CMD);
-
+ /* From the icmnd template, initialize words 4 - 11 */
+ memcpy(&wqe->words[4], &lpfc_icmnd_cmd_template.words[4],
+ sizeof(uint32_t) * 8);
atomic_inc(&phba->fc4NvmeControlRequests);
}
/*
@@ -1118,25 +1227,21 @@ lpfc_nvme_prep_io_cmd(struct lpfc_vport *vport,
* of the nvme_cmnd request_buffer
*/
+ /* Word 3 */
+ bf_set(payload_offset_len, &wqe->fcp_icmd,
+ (nCmd->rsplen + nCmd->cmdlen));
+
/* Word 6 */
bf_set(wqe_ctxt_tag, &wqe->generic.wqe_com,
phba->sli4_hba.rpi_ids[pnode->nlp_rpi]);
bf_set(wqe_xri_tag, &wqe->generic.wqe_com, pwqeq->sli4_xritag);
- /* Word 7 */
- /* Preserve Class data in the ndlp. */
- bf_set(wqe_class, &wqe->generic.wqe_com,
- (pnode->nlp_fcp_info & 0x0f));
-
/* Word 8 */
wqe->generic.wqe_com.abort_tag = pwqeq->iotag;
/* Word 9 */
bf_set(wqe_reqtag, &wqe->generic.wqe_com, pwqeq->iotag);
- /* Word 11 */
- bf_set(wqe_cqid, &wqe->generic.wqe_com, LPFC_WQE_CQ_ID_DEFAULT);
-
pwqeq->vport = vport;
return 0;
}
@@ -1164,10 +1269,11 @@ lpfc_nvme_prep_io_dma(struct lpfc_vport *vport,
{
struct lpfc_hba *phba = vport->phba;
struct nvmefc_fcp_req *nCmd = lpfc_ncmd->nvmeCmd;
- union lpfc_wqe128 *wqe = (union lpfc_wqe128 *)&lpfc_ncmd->cur_iocbq.wqe;
+ union lpfc_wqe128 *wqe = &lpfc_ncmd->cur_iocbq.wqe;
struct sli4_sge *sgl = lpfc_ncmd->nvme_sgl;
struct scatterlist *data_sg;
struct sli4_sge *first_data_sgl;
+ struct ulp_bde64 *bde;
dma_addr_t physaddr;
uint32_t num_bde = 0;
uint32_t dma_len;
@@ -1235,7 +1341,26 @@ lpfc_nvme_prep_io_dma(struct lpfc_vport *vport,
data_sg = sg_next(data_sg);
sgl++;
}
+ if (phba->nvme_embed_pbde) {
+ /* Use PBDE support for first SGL only, offset == 0 */
+ /* Words 13-15 */
+ bde = (struct ulp_bde64 *)
+ &wqe->words[13];
+ bde->addrLow = first_data_sgl->addr_lo;
+ bde->addrHigh = first_data_sgl->addr_hi;
+ bde->tus.f.bdeSize =
+ le32_to_cpu(first_data_sgl->sge_len);
+ bde->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+ bde->tus.w = cpu_to_le32(bde->tus.w);
+ /* wqe_pbde is 1 in template */
+ } else {
+ memset(&wqe->words[13], 0, (sizeof(uint32_t) * 3));
+ bf_set(wqe_pbde, &wqe->generic.wqe_com, 0);
+ }
} else {
+ bf_set(wqe_pbde, &wqe->generic.wqe_com, 0);
+ memset(&wqe->words[13], 0, (sizeof(uint32_t) * 3));
+
/* For this clause to be valid, the payload_length
* and sg_cnt must zero.
*/
@@ -1247,12 +1372,6 @@ lpfc_nvme_prep_io_dma(struct lpfc_vport *vport,
return 1;
}
}
-
- /*
- * Due to difference in data length between DIF/non-DIF paths,
- * we need to set word 4 of WQE here
- */
- wqe->fcp_iread.total_xfer_len = nCmd->payload_length;
return 0;
}
@@ -1554,7 +1673,7 @@ lpfc_nvme_fcp_abort(struct nvme_fc_local_port *pnvme_lport,
struct lpfc_iocbq *abts_buf;
struct lpfc_iocbq *nvmereq_wqe;
struct lpfc_nvme_fcpreq_priv *freqpriv;
- union lpfc_wqe *abts_wqe;
+ union lpfc_wqe128 *abts_wqe;
unsigned long flags;
int ret_val;
@@ -2098,7 +2217,7 @@ lpfc_new_nvme_buf(struct lpfc_vport *vport, int num_to_alloc)
break;
}
pwqeq = &(lpfc_ncmd->cur_iocbq);
- wqe = (union lpfc_wqe128 *)&pwqeq->wqe;
+ wqe = &pwqeq->wqe;
/* Allocate iotag for lpfc_ncmd->cur_iocbq. */
iotag = lpfc_sli_next_iotag(phba, pwqeq);
@@ -2135,14 +2254,8 @@ lpfc_new_nvme_buf(struct lpfc_vport *vport, int num_to_alloc)
lpfc_ncmd->cur_iocbq.context1 = lpfc_ncmd;
- /* Word 7 */
- bf_set(wqe_erp, &wqe->generic.wqe_com, 0);
- /* NVME upper layers will time things out, if needed */
- bf_set(wqe_tmo, &wqe->generic.wqe_com, 0);
-
- /* Word 10 */
- bf_set(wqe_ebde_cnt, &wqe->generic.wqe_com, 0);
- bf_set(wqe_dbde, &wqe->generic.wqe_com, 1);
+ /* Initialize WQE */
+ memset(wqe, 0, sizeof(union lpfc_wqe));
/* add the nvme buffer to a post list */
list_add_tail(&lpfc_ncmd->list, &post_nblist);
diff --git a/drivers/scsi/lpfc/lpfc_nvme.h b/drivers/scsi/lpfc/lpfc_nvme.h
index e79f8f75758c..9216653e0441 100644
--- a/drivers/scsi/lpfc/lpfc_nvme.h
+++ b/drivers/scsi/lpfc/lpfc_nvme.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Limited and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -27,6 +27,8 @@
#define LPFC_NVME_WAIT_TMO 10
#define LPFC_NVME_EXPEDITE_XRICNT 8
+#define LPFC_NVME_FB_SHIFT 9
+#define LPFC_NVME_MAX_FB (1 << 20) /* 1M */
struct lpfc_nvme_qhandle {
uint32_t index; /* WQ index to use */
diff --git a/drivers/scsi/lpfc/lpfc_nvmet.c b/drivers/scsi/lpfc/lpfc_nvmet.c
index 8dbf5c9d51aa..7271c9d885dd 100644
--- a/drivers/scsi/lpfc/lpfc_nvmet.c
+++ b/drivers/scsi/lpfc/lpfc_nvmet.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channsel Host Bus Adapters. *
- * Copyright (C) 2017 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Limited and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -36,7 +36,7 @@
#include <scsi/scsi_transport_fc.h>
#include <scsi/fc/fc_fs.h>
-#include <../drivers/nvme/host/nvme.h>
+#include <linux/nvme.h>
#include <linux/nvme-fc-driver.h>
#include <linux/nvme-fc.h>
@@ -71,6 +71,151 @@ static int lpfc_nvmet_unsol_fcp_issue_abort(struct lpfc_hba *,
static int lpfc_nvmet_unsol_ls_issue_abort(struct lpfc_hba *,
struct lpfc_nvmet_rcv_ctx *,
uint32_t, uint16_t);
+static void lpfc_nvmet_wqfull_flush(struct lpfc_hba *, struct lpfc_queue *,
+ struct lpfc_nvmet_rcv_ctx *);
+
+static union lpfc_wqe128 lpfc_tsend_cmd_template;
+static union lpfc_wqe128 lpfc_treceive_cmd_template;
+static union lpfc_wqe128 lpfc_trsp_cmd_template;
+
+/* Setup WQE templates for NVME IOs */
+void
+lpfc_nvmet_cmd_template(void)
+{
+ union lpfc_wqe128 *wqe;
+
+ /* TSEND template */
+ wqe = &lpfc_tsend_cmd_template;
+ memset(wqe, 0, sizeof(union lpfc_wqe128));
+
+ /* Word 0, 1, 2 - BDE is variable */
+
+ /* Word 3 - payload_offset_len is zero */
+
+ /* Word 4 - relative_offset is variable */
+
+ /* Word 5 - is zero */
+
+ /* Word 6 - ctxt_tag, xri_tag is variable */
+
+ /* Word 7 - wqe_ar is variable */
+ bf_set(wqe_cmnd, &wqe->fcp_tsend.wqe_com, CMD_FCP_TSEND64_WQE);
+ bf_set(wqe_pu, &wqe->fcp_tsend.wqe_com, PARM_REL_OFF);
+ bf_set(wqe_class, &wqe->fcp_tsend.wqe_com, CLASS3);
+ bf_set(wqe_ct, &wqe->fcp_tsend.wqe_com, SLI4_CT_RPI);
+ bf_set(wqe_ar, &wqe->fcp_tsend.wqe_com, 1);
+
+ /* Word 8 - abort_tag is variable */
+
+ /* Word 9 - reqtag, rcvoxid is variable */
+
+ /* Word 10 - wqes, xc is variable */
+ bf_set(wqe_nvme, &wqe->fcp_tsend.wqe_com, 1);
+ bf_set(wqe_dbde, &wqe->fcp_tsend.wqe_com, 1);
+ bf_set(wqe_wqes, &wqe->fcp_tsend.wqe_com, 0);
+ bf_set(wqe_xc, &wqe->fcp_tsend.wqe_com, 1);
+ bf_set(wqe_iod, &wqe->fcp_tsend.wqe_com, LPFC_WQE_IOD_WRITE);
+ bf_set(wqe_lenloc, &wqe->fcp_tsend.wqe_com, LPFC_WQE_LENLOC_WORD12);
+
+ /* Word 11 - sup, irsp, irsplen is variable */
+ bf_set(wqe_cmd_type, &wqe->fcp_tsend.wqe_com, FCP_COMMAND_TSEND);
+ bf_set(wqe_cqid, &wqe->fcp_tsend.wqe_com, LPFC_WQE_CQ_ID_DEFAULT);
+ bf_set(wqe_sup, &wqe->fcp_tsend.wqe_com, 0);
+ bf_set(wqe_irsp, &wqe->fcp_tsend.wqe_com, 0);
+ bf_set(wqe_irsplen, &wqe->fcp_tsend.wqe_com, 0);
+ bf_set(wqe_pbde, &wqe->fcp_tsend.wqe_com, 0);
+
+ /* Word 12 - fcp_data_len is variable */
+
+ /* Word 13, 14, 15 - PBDE is zero */
+
+ /* TRECEIVE template */
+ wqe = &lpfc_treceive_cmd_template;
+ memset(wqe, 0, sizeof(union lpfc_wqe128));
+
+ /* Word 0, 1, 2 - BDE is variable */
+
+ /* Word 3 */
+ wqe->fcp_treceive.payload_offset_len = TXRDY_PAYLOAD_LEN;
+
+ /* Word 4 - relative_offset is variable */
+
+ /* Word 5 - is zero */
+
+ /* Word 6 - ctxt_tag, xri_tag is variable */
+
+ /* Word 7 */
+ bf_set(wqe_cmnd, &wqe->fcp_treceive.wqe_com, CMD_FCP_TRECEIVE64_WQE);
+ bf_set(wqe_pu, &wqe->fcp_treceive.wqe_com, PARM_REL_OFF);
+ bf_set(wqe_class, &wqe->fcp_treceive.wqe_com, CLASS3);
+ bf_set(wqe_ct, &wqe->fcp_treceive.wqe_com, SLI4_CT_RPI);
+ bf_set(wqe_ar, &wqe->fcp_treceive.wqe_com, 0);
+
+ /* Word 8 - abort_tag is variable */
+
+ /* Word 9 - reqtag, rcvoxid is variable */
+
+ /* Word 10 - xc is variable */
+ bf_set(wqe_dbde, &wqe->fcp_treceive.wqe_com, 1);
+ bf_set(wqe_wqes, &wqe->fcp_treceive.wqe_com, 0);
+ bf_set(wqe_nvme, &wqe->fcp_treceive.wqe_com, 1);
+ bf_set(wqe_iod, &wqe->fcp_treceive.wqe_com, LPFC_WQE_IOD_READ);
+ bf_set(wqe_lenloc, &wqe->fcp_treceive.wqe_com, LPFC_WQE_LENLOC_WORD12);
+ bf_set(wqe_xc, &wqe->fcp_tsend.wqe_com, 1);
+
+ /* Word 11 - pbde is variable */
+ bf_set(wqe_cmd_type, &wqe->fcp_treceive.wqe_com, FCP_COMMAND_TRECEIVE);
+ bf_set(wqe_cqid, &wqe->fcp_treceive.wqe_com, LPFC_WQE_CQ_ID_DEFAULT);
+ bf_set(wqe_sup, &wqe->fcp_treceive.wqe_com, 0);
+ bf_set(wqe_irsp, &wqe->fcp_treceive.wqe_com, 0);
+ bf_set(wqe_irsplen, &wqe->fcp_treceive.wqe_com, 0);
+ bf_set(wqe_pbde, &wqe->fcp_treceive.wqe_com, 1);
+
+ /* Word 12 - fcp_data_len is variable */
+
+ /* Word 13, 14, 15 - PBDE is variable */
+
+ /* TRSP template */
+ wqe = &lpfc_trsp_cmd_template;
+ memset(wqe, 0, sizeof(union lpfc_wqe128));
+
+ /* Word 0, 1, 2 - BDE is variable */
+
+ /* Word 3 - response_len is variable */
+
+ /* Word 4, 5 - is zero */
+
+ /* Word 6 - ctxt_tag, xri_tag is variable */
+
+ /* Word 7 */
+ bf_set(wqe_cmnd, &wqe->fcp_trsp.wqe_com, CMD_FCP_TRSP64_WQE);
+ bf_set(wqe_pu, &wqe->fcp_trsp.wqe_com, PARM_UNUSED);
+ bf_set(wqe_class, &wqe->fcp_trsp.wqe_com, CLASS3);
+ bf_set(wqe_ct, &wqe->fcp_trsp.wqe_com, SLI4_CT_RPI);
+ bf_set(wqe_ag, &wqe->fcp_trsp.wqe_com, 1); /* wqe_ar */
+
+ /* Word 8 - abort_tag is variable */
+
+ /* Word 9 - reqtag is variable */
+
+ /* Word 10 wqes, xc is variable */
+ bf_set(wqe_dbde, &wqe->fcp_trsp.wqe_com, 1);
+ bf_set(wqe_nvme, &wqe->fcp_trsp.wqe_com, 1);
+ bf_set(wqe_wqes, &wqe->fcp_trsp.wqe_com, 0);
+ bf_set(wqe_xc, &wqe->fcp_trsp.wqe_com, 0);
+ bf_set(wqe_iod, &wqe->fcp_trsp.wqe_com, LPFC_WQE_IOD_NONE);
+ bf_set(wqe_lenloc, &wqe->fcp_trsp.wqe_com, LPFC_WQE_LENLOC_WORD3);
+
+ /* Word 11 irsp, irsplen is variable */
+ bf_set(wqe_cmd_type, &wqe->fcp_trsp.wqe_com, FCP_COMMAND_TRSP);
+ bf_set(wqe_cqid, &wqe->fcp_trsp.wqe_com, LPFC_WQE_CQ_ID_DEFAULT);
+ bf_set(wqe_sup, &wqe->fcp_trsp.wqe_com, 0);
+ bf_set(wqe_irsp, &wqe->fcp_trsp.wqe_com, 0);
+ bf_set(wqe_irsplen, &wqe->fcp_trsp.wqe_com, 0);
+ bf_set(wqe_pbde, &wqe->fcp_trsp.wqe_com, 0);
+
+ /* Word 12, 13, 14, 15 - is zero */
+}
void
lpfc_nvmet_defer_release(struct lpfc_hba *phba, struct lpfc_nvmet_rcv_ctx *ctxp)
@@ -130,7 +275,7 @@ lpfc_nvmet_xmt_ls_rsp_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe,
if (tgtp) {
if (status) {
atomic_inc(&tgtp->xmt_ls_rsp_error);
- if (status == IOERR_ABORT_REQUESTED)
+ if (result == IOERR_ABORT_REQUESTED)
atomic_inc(&tgtp->xmt_ls_rsp_aborted);
if (bf_get(lpfc_wcqe_c_xb, wcqe))
atomic_inc(&tgtp->xmt_ls_rsp_xb_set);
@@ -268,8 +413,6 @@ lpfc_nvmet_ctxbuf_post(struct lpfc_hba *phba, struct lpfc_nvmet_ctxbuf *ctx_buf)
"NVMET RCV BUSY: xri x%x sz %d "
"from %06x\n",
oxid, size, sid);
- /* defer repost rcv buffer till .defer_rcv callback */
- ctxp->flag &= ~LPFC_NVMET_DEFER_RCV_REPOST;
atomic_inc(&tgtp->rcv_fcp_cmd_out);
return;
}
@@ -541,7 +684,7 @@ lpfc_nvmet_xmt_fcp_op_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe,
rsp->transferred_length = 0;
if (tgtp) {
atomic_inc(&tgtp->xmt_fcp_rsp_error);
- if (status == IOERR_ABORT_REQUESTED)
+ if (result == IOERR_ABORT_REQUESTED)
atomic_inc(&tgtp->xmt_fcp_rsp_aborted);
}
@@ -741,7 +884,10 @@ lpfc_nvmet_xmt_fcp_op(struct nvmet_fc_target_port *tgtport,
struct lpfc_nvmet_rcv_ctx *ctxp =
container_of(rsp, struct lpfc_nvmet_rcv_ctx, ctx.fcp_req);
struct lpfc_hba *phba = ctxp->phba;
+ struct lpfc_queue *wq;
struct lpfc_iocbq *nvmewqeq;
+ struct lpfc_sli_ring *pring;
+ unsigned long iflags;
int rc;
if (phba->pport->load_flag & FC_UNLOADING) {
@@ -820,6 +966,22 @@ lpfc_nvmet_xmt_fcp_op(struct nvmet_fc_target_port *tgtport,
return 0;
}
+ if (rc == -EBUSY) {
+ /*
+ * WQ was full, so queue nvmewqeq to be sent after
+ * WQE release CQE
+ */
+ ctxp->flag |= LPFC_NVMET_DEFER_WQFULL;
+ wq = phba->sli4_hba.nvme_wq[rsp->hwqid];
+ pring = wq->pring;
+ spin_lock_irqsave(&pring->ring_lock, iflags);
+ list_add_tail(&nvmewqeq->list, &wq->wqfull_list);
+ wq->q_flag |= HBA_NVMET_WQFULL;
+ spin_unlock_irqrestore(&pring->ring_lock, iflags);
+ atomic_inc(&lpfc_nvmep->defer_wqfull);
+ return 0;
+ }
+
/* Give back resources */
atomic_inc(&lpfc_nvmep->xmt_fcp_drop);
lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR,
@@ -851,6 +1013,7 @@ lpfc_nvmet_xmt_fcp_abort(struct nvmet_fc_target_port *tgtport,
struct lpfc_nvmet_rcv_ctx *ctxp =
container_of(req, struct lpfc_nvmet_rcv_ctx, ctx.fcp_req);
struct lpfc_hba *phba = ctxp->phba;
+ struct lpfc_queue *wq;
unsigned long flags;
if (phba->pport->load_flag & FC_UNLOADING)
@@ -880,6 +1043,15 @@ lpfc_nvmet_xmt_fcp_abort(struct nvmet_fc_target_port *tgtport,
}
ctxp->flag |= LPFC_NVMET_ABORT_OP;
+ if (ctxp->flag & LPFC_NVMET_DEFER_WQFULL) {
+ lpfc_nvmet_unsol_fcp_issue_abort(phba, ctxp, ctxp->sid,
+ ctxp->oxid);
+ wq = phba->sli4_hba.nvme_wq[ctxp->wqeq->hba_wqidx];
+ spin_unlock_irqrestore(&ctxp->ctxlock, flags);
+ lpfc_nvmet_wqfull_flush(phba, wq, ctxp);
+ return;
+ }
+
/* An state of LPFC_NVMET_STE_RCV means we have just received
* the NVME command and have not started processing it.
* (by issuing any IO WQEs on this exchange yet)
@@ -946,11 +1118,9 @@ lpfc_nvmet_defer_rcv(struct nvmet_fc_target_port *tgtport,
tgtp = phba->targetport->private;
atomic_inc(&tgtp->rcv_fcp_cmd_defer);
- if (ctxp->flag & LPFC_NVMET_DEFER_RCV_REPOST)
- lpfc_rq_buf_free(phba, &nvmebuf->hbuf); /* repost */
- else
- nvmebuf->hrq->rqbp->rqb_free_buffer(phba, nvmebuf);
- ctxp->flag &= ~LPFC_NVMET_DEFER_RCV_REPOST;
+
+ /* Free the nvmebuf since a new buffer already replaced it */
+ nvmebuf->hrq->rqbp->rqb_free_buffer(phba, nvmebuf);
}
static struct nvmet_fc_target_template lpfc_tgttemplate = {
@@ -1124,16 +1294,10 @@ lpfc_nvmet_setup_io_context(struct lpfc_hba *phba)
}
ctx_buf->iocbq->iocb_flag = LPFC_IO_NVMET;
nvmewqe = ctx_buf->iocbq;
- wqe = (union lpfc_wqe128 *)&nvmewqe->wqe;
+ wqe = &nvmewqe->wqe;
+
/* Initialize WQE */
memset(wqe, 0, sizeof(union lpfc_wqe));
- /* Word 7 */
- bf_set(wqe_ct, &wqe->generic.wqe_com, SLI4_CT_RPI);
- bf_set(wqe_class, &wqe->generic.wqe_com, CLASS3);
- /* Word 10 */
- bf_set(wqe_nvme, &wqe->fcp_tsend.wqe_com, 1);
- bf_set(wqe_ebde_cnt, &wqe->generic.wqe_com, 0);
- bf_set(wqe_qosd, &wqe->generic.wqe_com, 0);
ctx_buf->iocbq->context1 = NULL;
spin_lock(&phba->sli4_hba.sgl_list_lock);
@@ -1280,6 +1444,9 @@ lpfc_nvmet_create_targetport(struct lpfc_hba *phba)
atomic_set(&tgtp->xmt_abort_sol, 0);
atomic_set(&tgtp->xmt_abort_rsp, 0);
atomic_set(&tgtp->xmt_abort_rsp_error, 0);
+ atomic_set(&tgtp->defer_ctx, 0);
+ atomic_set(&tgtp->defer_fod, 0);
+ atomic_set(&tgtp->defer_wqfull, 0);
}
return error;
}
@@ -1435,16 +1602,103 @@ lpfc_nvmet_rcv_unsol_abort(struct lpfc_vport *vport,
return 0;
}
+static void
+lpfc_nvmet_wqfull_flush(struct lpfc_hba *phba, struct lpfc_queue *wq,
+ struct lpfc_nvmet_rcv_ctx *ctxp)
+{
+ struct lpfc_sli_ring *pring;
+ struct lpfc_iocbq *nvmewqeq;
+ struct lpfc_iocbq *next_nvmewqeq;
+ unsigned long iflags;
+ struct lpfc_wcqe_complete wcqe;
+ struct lpfc_wcqe_complete *wcqep;
+
+ pring = wq->pring;
+ wcqep = &wcqe;
+
+ /* Fake an ABORT error code back to cmpl routine */
+ memset(wcqep, 0, sizeof(struct lpfc_wcqe_complete));
+ bf_set(lpfc_wcqe_c_status, wcqep, IOSTAT_LOCAL_REJECT);
+ wcqep->parameter = IOERR_ABORT_REQUESTED;
+
+ spin_lock_irqsave(&pring->ring_lock, iflags);
+ list_for_each_entry_safe(nvmewqeq, next_nvmewqeq,
+ &wq->wqfull_list, list) {
+ if (ctxp) {
+ /* Checking for a specific IO to flush */
+ if (nvmewqeq->context2 == ctxp) {
+ list_del(&nvmewqeq->list);
+ spin_unlock_irqrestore(&pring->ring_lock,
+ iflags);
+ lpfc_nvmet_xmt_fcp_op_cmp(phba, nvmewqeq,
+ wcqep);
+ return;
+ }
+ continue;
+ } else {
+ /* Flush all IOs */
+ list_del(&nvmewqeq->list);
+ spin_unlock_irqrestore(&pring->ring_lock, iflags);
+ lpfc_nvmet_xmt_fcp_op_cmp(phba, nvmewqeq, wcqep);
+ spin_lock_irqsave(&pring->ring_lock, iflags);
+ }
+ }
+ if (!ctxp)
+ wq->q_flag &= ~HBA_NVMET_WQFULL;
+ spin_unlock_irqrestore(&pring->ring_lock, iflags);
+}
+
+void
+lpfc_nvmet_wqfull_process(struct lpfc_hba *phba,
+ struct lpfc_queue *wq)
+{
+#if (IS_ENABLED(CONFIG_NVME_TARGET_FC))
+ struct lpfc_sli_ring *pring;
+ struct lpfc_iocbq *nvmewqeq;
+ unsigned long iflags;
+ int rc;
+
+ /*
+ * Some WQE slots are available, so try to re-issue anything
+ * on the WQ wqfull_list.
+ */
+ pring = wq->pring;
+ spin_lock_irqsave(&pring->ring_lock, iflags);
+ while (!list_empty(&wq->wqfull_list)) {
+ list_remove_head(&wq->wqfull_list, nvmewqeq, struct lpfc_iocbq,
+ list);
+ spin_unlock_irqrestore(&pring->ring_lock, iflags);
+ rc = lpfc_sli4_issue_wqe(phba, LPFC_FCP_RING, nvmewqeq);
+ spin_lock_irqsave(&pring->ring_lock, iflags);
+ if (rc == -EBUSY) {
+ /* WQ was full again, so put it back on the list */
+ list_add(&nvmewqeq->list, &wq->wqfull_list);
+ spin_unlock_irqrestore(&pring->ring_lock, iflags);
+ return;
+ }
+ }
+ wq->q_flag &= ~HBA_NVMET_WQFULL;
+ spin_unlock_irqrestore(&pring->ring_lock, iflags);
+
+#endif
+}
+
void
lpfc_nvmet_destroy_targetport(struct lpfc_hba *phba)
{
#if (IS_ENABLED(CONFIG_NVME_TARGET_FC))
struct lpfc_nvmet_tgtport *tgtp;
+ struct lpfc_queue *wq;
+ uint32_t qidx;
if (phba->nvmet_support == 0)
return;
if (phba->targetport) {
tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
+ for (qidx = 0; qidx < phba->cfg_nvme_io_channel; qidx++) {
+ wq = phba->sli4_hba.nvme_wq[qidx];
+ lpfc_nvmet_wqfull_flush(phba, wq, NULL);
+ }
init_completion(&tgtp->tport_unreg_done);
nvmet_fc_unregister_targetport(phba->targetport);
wait_for_completion_timeout(&tgtp->tport_unreg_done, 5);
@@ -1694,6 +1948,8 @@ lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba,
lpfc_nvmeio_data(phba, "NVMET FCP RCV: xri x%x sz %d CPU %02x\n",
oxid, size, smp_processor_id());
+ tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
+
if (!ctx_buf) {
/* Queue this NVME IO to process later */
spin_lock_irqsave(&phba->sli4_hba.nvmet_io_wait_lock, iflag);
@@ -1709,10 +1965,11 @@ lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba,
lpfc_post_rq_buffer(
phba, phba->sli4_hba.nvmet_mrq_hdr[qno],
phba->sli4_hba.nvmet_mrq_data[qno], 1, qno);
+
+ atomic_inc(&tgtp->defer_ctx);
return;
}
- tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private;
payload = (uint32_t *)(nvmebuf->dbuf.virt);
sid = sli4_sid_from_fc_hdr(fc_hdr);
@@ -1776,12 +2033,20 @@ lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba,
/* Processing of FCP command is deferred */
if (rc == -EOVERFLOW) {
+ /*
+ * Post a brand new DMA buffer to RQ and defer
+ * freeing rcv buffer till .defer_rcv callback
+ */
+ qno = nvmebuf->idx;
+ lpfc_post_rq_buffer(
+ phba, phba->sli4_hba.nvmet_mrq_hdr[qno],
+ phba->sli4_hba.nvmet_mrq_data[qno], 1, qno);
+
lpfc_nvmeio_data(phba,
"NVMET RCV BUSY: xri x%x sz %d from %06x\n",
oxid, size, sid);
- /* defer reposting rcv buffer till .defer_rcv callback */
- ctxp->flag |= LPFC_NVMET_DEFER_RCV_REPOST;
atomic_inc(&tgtp->rcv_fcp_cmd_out);
+ atomic_inc(&tgtp->defer_fod);
return;
}
ctxp->rqb_buffer = nvmebuf;
@@ -1897,7 +2162,7 @@ lpfc_nvmet_prep_ls_wqe(struct lpfc_hba *phba,
{
struct lpfc_nodelist *ndlp;
struct lpfc_iocbq *nvmewqe;
- union lpfc_wqe *wqe;
+ union lpfc_wqe128 *wqe;
if (!lpfc_is_link_up(phba)) {
lpfc_printf_log(phba, KERN_ERR, LOG_NVME_DISC,
@@ -2023,9 +2288,11 @@ lpfc_nvmet_prep_fcp_wqe(struct lpfc_hba *phba,
struct lpfc_iocbq *nvmewqe;
struct scatterlist *sgel;
union lpfc_wqe128 *wqe;
+ struct ulp_bde64 *bde;
uint32_t *txrdy;
dma_addr_t physaddr;
int i, cnt;
+ int do_pbde;
int xc = 1;
if (!lpfc_is_link_up(phba)) {
@@ -2078,7 +2345,7 @@ lpfc_nvmet_prep_fcp_wqe(struct lpfc_hba *phba,
if (((ctxp->state == LPFC_NVMET_STE_RCV) &&
(ctxp->entry_cnt == 1)) ||
(ctxp->state == LPFC_NVMET_STE_DATA)) {
- wqe = (union lpfc_wqe128 *)&nvmewqe->wqe;
+ wqe = &nvmewqe->wqe;
} else {
lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR,
"6111 Wrong state NVMET FCP: %d cnt %d\n",
@@ -2090,6 +2357,11 @@ lpfc_nvmet_prep_fcp_wqe(struct lpfc_hba *phba,
switch (rsp->op) {
case NVMET_FCOP_READDATA:
case NVMET_FCOP_READDATA_RSP:
+ /* From the tsend template, initialize words 7 - 11 */
+ memcpy(&wqe->words[7],
+ &lpfc_tsend_cmd_template.words[7],
+ sizeof(uint32_t) * 5);
+
/* Words 0 - 2 : The first sg segment */
sgel = &rsp->sg[0];
physaddr = sg_dma_address(sgel);
@@ -2106,6 +2378,7 @@ lpfc_nvmet_prep_fcp_wqe(struct lpfc_hba *phba,
wqe->fcp_tsend.relative_offset = ctxp->offset;
/* Word 5 */
+ wqe->fcp_tsend.reserved = 0;
/* Word 6 */
bf_set(wqe_ctxt_tag, &wqe->fcp_tsend.wqe_com,
@@ -2113,9 +2386,7 @@ lpfc_nvmet_prep_fcp_wqe(struct lpfc_hba *phba,
bf_set(wqe_xri_tag, &wqe->fcp_tsend.wqe_com,
nvmewqe->sli4_xritag);
- /* Word 7 */
- bf_set(wqe_pu, &wqe->fcp_tsend.wqe_com, 1);
- bf_set(wqe_cmnd, &wqe->fcp_tsend.wqe_com, CMD_FCP_TSEND64_WQE);
+ /* Word 7 - set ar later */
/* Word 8 */
wqe->fcp_tsend.wqe_com.abort_tag = nvmewqe->iotag;
@@ -2124,23 +2395,12 @@ lpfc_nvmet_prep_fcp_wqe(struct lpfc_hba *phba,
bf_set(wqe_reqtag, &wqe->fcp_tsend.wqe_com, nvmewqe->iotag);
bf_set(wqe_rcvoxid, &wqe->fcp_tsend.wqe_com, ctxp->oxid);
- /* Word 10 */
- bf_set(wqe_nvme, &wqe->fcp_tsend.wqe_com, 1);
- bf_set(wqe_dbde, &wqe->fcp_tsend.wqe_com, 1);
- bf_set(wqe_iod, &wqe->fcp_tsend.wqe_com, LPFC_WQE_IOD_WRITE);
- bf_set(wqe_lenloc, &wqe->fcp_tsend.wqe_com,
- LPFC_WQE_LENLOC_WORD12);
- bf_set(wqe_ebde_cnt, &wqe->fcp_tsend.wqe_com, 0);
- bf_set(wqe_xc, &wqe->fcp_tsend.wqe_com, xc);
- bf_set(wqe_nvme, &wqe->fcp_tsend.wqe_com, 1);
- if (phba->cfg_nvme_oas)
- bf_set(wqe_oas, &wqe->fcp_tsend.wqe_com, 1);
+ /* Word 10 - set wqes later, in template xc=1 */
+ if (!xc)
+ bf_set(wqe_xc, &wqe->fcp_tsend.wqe_com, 0);
- /* Word 11 */
- bf_set(wqe_cqid, &wqe->fcp_tsend.wqe_com,
- LPFC_WQE_CQ_ID_DEFAULT);
- bf_set(wqe_cmd_type, &wqe->fcp_tsend.wqe_com,
- FCP_COMMAND_TSEND);
+ /* Word 11 - set sup, irsp, irsplen later */
+ do_pbde = 0;
/* Word 12 */
wqe->fcp_tsend.fcp_data_len = rsp->transfer_length;
@@ -2162,15 +2422,14 @@ lpfc_nvmet_prep_fcp_wqe(struct lpfc_hba *phba,
sgl++;
if (rsp->op == NVMET_FCOP_READDATA_RSP) {
atomic_inc(&tgtp->xmt_fcp_read_rsp);
- bf_set(wqe_ar, &wqe->fcp_tsend.wqe_com, 1);
- if ((ndlp->nlp_flag & NLP_SUPPRESS_RSP) &&
- (rsp->rsplen == 12)) {
- bf_set(wqe_sup, &wqe->fcp_tsend.wqe_com, 1);
- bf_set(wqe_wqes, &wqe->fcp_tsend.wqe_com, 0);
- bf_set(wqe_irsp, &wqe->fcp_tsend.wqe_com, 0);
- bf_set(wqe_irsplen, &wqe->fcp_tsend.wqe_com, 0);
+
+ /* In template ar=1 wqes=0 sup=0 irsp=0 irsplen=0 */
+
+ if (rsp->rsplen == LPFC_NVMET_SUCCESS_LEN) {
+ if (ndlp->nlp_flag & NLP_SUPPRESS_RSP)
+ bf_set(wqe_sup,
+ &wqe->fcp_tsend.wqe_com, 1);
} else {
- bf_set(wqe_sup, &wqe->fcp_tsend.wqe_com, 0);
bf_set(wqe_wqes, &wqe->fcp_tsend.wqe_com, 1);
bf_set(wqe_irsp, &wqe->fcp_tsend.wqe_com, 1);
bf_set(wqe_irsplen, &wqe->fcp_tsend.wqe_com,
@@ -2181,15 +2440,17 @@ lpfc_nvmet_prep_fcp_wqe(struct lpfc_hba *phba,
} else {
atomic_inc(&tgtp->xmt_fcp_read);
- bf_set(wqe_sup, &wqe->fcp_tsend.wqe_com, 0);
- bf_set(wqe_wqes, &wqe->fcp_tsend.wqe_com, 0);
- bf_set(wqe_irsp, &wqe->fcp_tsend.wqe_com, 0);
+ /* In template ar=1 wqes=0 sup=0 irsp=0 irsplen=0 */
bf_set(wqe_ar, &wqe->fcp_tsend.wqe_com, 0);
- bf_set(wqe_irsplen, &wqe->fcp_tsend.wqe_com, 0);
}
break;
case NVMET_FCOP_WRITEDATA:
+ /* From the treceive template, initialize words 3 - 11 */
+ memcpy(&wqe->words[3],
+ &lpfc_treceive_cmd_template.words[3],
+ sizeof(uint32_t) * 9);
+
/* Words 0 - 2 : The first sg segment */
txrdy = dma_pool_alloc(phba->txrdy_payload_pool,
GFP_KERNEL, &physaddr);
@@ -2208,14 +2469,9 @@ lpfc_nvmet_prep_fcp_wqe(struct lpfc_hba *phba,
wqe->fcp_treceive.bde.addrHigh =
cpu_to_le32(putPaddrHigh(physaddr));
- /* Word 3 */
- wqe->fcp_treceive.payload_offset_len = TXRDY_PAYLOAD_LEN;
-
/* Word 4 */
wqe->fcp_treceive.relative_offset = ctxp->offset;
- /* Word 5 */
-
/* Word 6 */
bf_set(wqe_ctxt_tag, &wqe->fcp_treceive.wqe_com,
phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]);
@@ -2223,10 +2479,6 @@ lpfc_nvmet_prep_fcp_wqe(struct lpfc_hba *phba,
nvmewqe->sli4_xritag);
/* Word 7 */
- bf_set(wqe_pu, &wqe->fcp_treceive.wqe_com, 1);
- bf_set(wqe_ar, &wqe->fcp_treceive.wqe_com, 0);
- bf_set(wqe_cmnd, &wqe->fcp_treceive.wqe_com,
- CMD_FCP_TRECEIVE64_WQE);
/* Word 8 */
wqe->fcp_treceive.wqe_com.abort_tag = nvmewqe->iotag;
@@ -2235,26 +2487,17 @@ lpfc_nvmet_prep_fcp_wqe(struct lpfc_hba *phba,
bf_set(wqe_reqtag, &wqe->fcp_treceive.wqe_com, nvmewqe->iotag);
bf_set(wqe_rcvoxid, &wqe->fcp_treceive.wqe_com, ctxp->oxid);
- /* Word 10 */
- bf_set(wqe_nvme, &wqe->fcp_treceive.wqe_com, 1);
- bf_set(wqe_dbde, &wqe->fcp_treceive.wqe_com, 1);
- bf_set(wqe_iod, &wqe->fcp_treceive.wqe_com, LPFC_WQE_IOD_READ);
- bf_set(wqe_lenloc, &wqe->fcp_treceive.wqe_com,
- LPFC_WQE_LENLOC_WORD12);
- bf_set(wqe_xc, &wqe->fcp_treceive.wqe_com, xc);
- bf_set(wqe_wqes, &wqe->fcp_treceive.wqe_com, 0);
- bf_set(wqe_irsp, &wqe->fcp_treceive.wqe_com, 0);
- bf_set(wqe_irsplen, &wqe->fcp_treceive.wqe_com, 0);
- bf_set(wqe_nvme, &wqe->fcp_treceive.wqe_com, 1);
- if (phba->cfg_nvme_oas)
- bf_set(wqe_oas, &wqe->fcp_treceive.wqe_com, 1);
+ /* Word 10 - in template xc=1 */
+ if (!xc)
+ bf_set(wqe_xc, &wqe->fcp_treceive.wqe_com, 0);
- /* Word 11 */
- bf_set(wqe_cqid, &wqe->fcp_treceive.wqe_com,
- LPFC_WQE_CQ_ID_DEFAULT);
- bf_set(wqe_cmd_type, &wqe->fcp_treceive.wqe_com,
- FCP_COMMAND_TRECEIVE);
- bf_set(wqe_sup, &wqe->fcp_tsend.wqe_com, 0);
+ /* Word 11 - set pbde later */
+ if (phba->nvme_embed_pbde) {
+ do_pbde = 1;
+ } else {
+ bf_set(wqe_pbde, &wqe->fcp_treceive.wqe_com, 0);
+ do_pbde = 0;
+ }
/* Word 12 */
wqe->fcp_tsend.fcp_data_len = rsp->transfer_length;
@@ -2282,6 +2525,11 @@ lpfc_nvmet_prep_fcp_wqe(struct lpfc_hba *phba,
break;
case NVMET_FCOP_RSP:
+ /* From the treceive template, initialize words 4 - 11 */
+ memcpy(&wqe->words[4],
+ &lpfc_trsp_cmd_template.words[4],
+ sizeof(uint32_t) * 8);
+
/* Words 0 - 2 */
physaddr = rsp->rspdma;
wqe->fcp_trsp.bde.tus.f.bdeFlags = BUFF_TYPE_BDE_64;
@@ -2294,12 +2542,6 @@ lpfc_nvmet_prep_fcp_wqe(struct lpfc_hba *phba,
/* Word 3 */
wqe->fcp_trsp.response_len = rsp->rsplen;
- /* Word 4 */
- wqe->fcp_trsp.rsvd_4_5[0] = 0;
-
-
- /* Word 5 */
-
/* Word 6 */
bf_set(wqe_ctxt_tag, &wqe->fcp_trsp.wqe_com,
phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]);
@@ -2307,9 +2549,6 @@ lpfc_nvmet_prep_fcp_wqe(struct lpfc_hba *phba,
nvmewqe->sli4_xritag);
/* Word 7 */
- bf_set(wqe_pu, &wqe->fcp_trsp.wqe_com, 0);
- bf_set(wqe_ag, &wqe->fcp_trsp.wqe_com, 1);
- bf_set(wqe_cmnd, &wqe->fcp_trsp.wqe_com, CMD_FCP_TRSP64_WQE);
/* Word 8 */
wqe->fcp_trsp.wqe_com.abort_tag = nvmewqe->iotag;
@@ -2319,35 +2558,23 @@ lpfc_nvmet_prep_fcp_wqe(struct lpfc_hba *phba,
bf_set(wqe_rcvoxid, &wqe->fcp_trsp.wqe_com, ctxp->oxid);
/* Word 10 */
- bf_set(wqe_nvme, &wqe->fcp_trsp.wqe_com, 1);
- bf_set(wqe_dbde, &wqe->fcp_trsp.wqe_com, 0);
- bf_set(wqe_iod, &wqe->fcp_trsp.wqe_com, LPFC_WQE_IOD_WRITE);
- bf_set(wqe_lenloc, &wqe->fcp_trsp.wqe_com,
- LPFC_WQE_LENLOC_WORD3);
- bf_set(wqe_xc, &wqe->fcp_trsp.wqe_com, xc);
- bf_set(wqe_nvme, &wqe->fcp_trsp.wqe_com, 1);
- if (phba->cfg_nvme_oas)
- bf_set(wqe_oas, &wqe->fcp_trsp.wqe_com, 1);
+ if (xc)
+ bf_set(wqe_xc, &wqe->fcp_trsp.wqe_com, 1);
/* Word 11 */
- bf_set(wqe_cqid, &wqe->fcp_trsp.wqe_com,
- LPFC_WQE_CQ_ID_DEFAULT);
- bf_set(wqe_cmd_type, &wqe->fcp_trsp.wqe_com,
- FCP_COMMAND_TRSP);
- bf_set(wqe_sup, &wqe->fcp_tsend.wqe_com, 0);
-
- if (rsp->rsplen == LPFC_NVMET_SUCCESS_LEN) {
- /* Good response - all zero's on wire */
- bf_set(wqe_wqes, &wqe->fcp_trsp.wqe_com, 0);
- bf_set(wqe_irsp, &wqe->fcp_trsp.wqe_com, 0);
- bf_set(wqe_irsplen, &wqe->fcp_trsp.wqe_com, 0);
- } else {
+ /* In template wqes=0 irsp=0 irsplen=0 - good response */
+ if (rsp->rsplen != LPFC_NVMET_SUCCESS_LEN) {
+ /* Bad response - embed it */
bf_set(wqe_wqes, &wqe->fcp_trsp.wqe_com, 1);
bf_set(wqe_irsp, &wqe->fcp_trsp.wqe_com, 1);
bf_set(wqe_irsplen, &wqe->fcp_trsp.wqe_com,
((rsp->rsplen >> 2) - 1));
memcpy(&wqe->words[16], rsp->rspaddr, rsp->rsplen);
}
+ do_pbde = 0;
+
+ /* Word 12 */
+ wqe->fcp_trsp.rsvd_12_15[0] = 0;
/* Use rspbuf, NOT sg list */
rsp->sg_cnt = 0;
@@ -2380,6 +2607,17 @@ lpfc_nvmet_prep_fcp_wqe(struct lpfc_hba *phba,
bf_set(lpfc_sli4_sge_last, sgl, 1);
sgl->word2 = cpu_to_le32(sgl->word2);
sgl->sge_len = cpu_to_le32(cnt);
+ if (do_pbde && i == 0) {
+ bde = (struct ulp_bde64 *)&wqe->words[13];
+ memset(bde, 0, sizeof(struct ulp_bde64));
+ /* Words 13-15 (PBDE)*/
+ bde->addrLow = sgl->addr_lo;
+ bde->addrHigh = sgl->addr_hi;
+ bde->tus.f.bdeSize =
+ le32_to_cpu(sgl->sge_len);
+ bde->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+ bde->tus.w = cpu_to_le32(bde->tus.w);
+ }
sgl++;
ctxp->offset += cnt;
}
@@ -2597,7 +2835,7 @@ lpfc_nvmet_unsol_issue_abort(struct lpfc_hba *phba,
{
struct lpfc_nvmet_tgtport *tgtp;
struct lpfc_iocbq *abts_wqeq;
- union lpfc_wqe *wqe_abts;
+ union lpfc_wqe128 *wqe_abts;
struct lpfc_nodelist *ndlp;
lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS,
@@ -2692,7 +2930,7 @@ lpfc_nvmet_sol_fcp_issue_abort(struct lpfc_hba *phba,
{
struct lpfc_nvmet_tgtport *tgtp;
struct lpfc_iocbq *abts_wqeq;
- union lpfc_wqe *abts_wqe;
+ union lpfc_wqe128 *abts_wqe;
struct lpfc_nodelist *ndlp;
unsigned long flags;
int rc;
@@ -2882,7 +3120,7 @@ lpfc_nvmet_unsol_ls_issue_abort(struct lpfc_hba *phba,
{
struct lpfc_nvmet_tgtport *tgtp;
struct lpfc_iocbq *abts_wqeq;
- union lpfc_wqe *wqe_abts;
+ union lpfc_wqe128 *wqe_abts;
unsigned long flags;
int rc;
diff --git a/drivers/scsi/lpfc/lpfc_nvmet.h b/drivers/scsi/lpfc/lpfc_nvmet.h
index 5b32c9e4d4ef..c1bcef3f103c 100644
--- a/drivers/scsi/lpfc/lpfc_nvmet.h
+++ b/drivers/scsi/lpfc/lpfc_nvmet.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Limited and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -72,7 +72,6 @@ struct lpfc_nvmet_tgtport {
atomic_t xmt_fcp_rsp_aborted;
atomic_t xmt_fcp_rsp_drop;
-
/* Stats counters - lpfc_nvmet_xmt_fcp_abort */
atomic_t xmt_fcp_xri_abort_cqe;
atomic_t xmt_fcp_abort;
@@ -81,6 +80,11 @@ struct lpfc_nvmet_tgtport {
atomic_t xmt_abort_unsol;
atomic_t xmt_abort_rsp;
atomic_t xmt_abort_rsp_error;
+
+ /* Stats counters - defer IO */
+ atomic_t defer_ctx;
+ atomic_t defer_fod;
+ atomic_t defer_wqfull;
};
struct lpfc_nvmet_ctx_info {
@@ -131,7 +135,7 @@ struct lpfc_nvmet_rcv_ctx {
#define LPFC_NVMET_XBUSY 0x4 /* XB bit set on IO cmpl */
#define LPFC_NVMET_CTX_RLS 0x8 /* ctx free requested */
#define LPFC_NVMET_ABTS_RCV 0x10 /* ABTS received on exchange */
-#define LPFC_NVMET_DEFER_RCV_REPOST 0x20 /* repost to RQ on defer rcv */
+#define LPFC_NVMET_DEFER_WQFULL 0x40 /* Waiting on a free WQE */
struct rqb_dmabuf *rqb_buffer;
struct lpfc_nvmet_ctxbuf *ctxbuf;
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c
index c0cdaef4db24..050f04418f5f 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.c
+++ b/drivers/scsi/lpfc/lpfc_scsi.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Limited and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -837,8 +837,13 @@ lpfc_new_scsi_buf_s4(struct lpfc_vport *vport, int num_to_alloc)
* 4K Page alignment is CRITICAL to BlockGuard, double check
* to be sure.
*/
- if (phba->cfg_enable_bg && (((unsigned long)(psb->data) &
+ if ((phba->sli3_options & LPFC_SLI3_BG_ENABLED) &&
+ (((unsigned long)(psb->data) &
(unsigned long)(SLI4_PAGE_SIZE - 1)) != 0)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
+ "3369 Memory alignment error "
+ "addr=%lx\n",
+ (unsigned long)psb->data);
dma_pool_free(phba->lpfc_sg_dma_buf_pool,
psb->data, psb->dma_handle);
kfree(psb);
@@ -3304,8 +3309,12 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
dma_offset += dma_len;
sgl++;
}
- /* setup the performance hint (first data BDE) if enabled */
- if (phba->sli3_options & LPFC_SLI4_PERFH_ENABLED) {
+ /*
+ * Setup the first Payload BDE. For FCoE we just key off
+ * Performance Hints, for FC we utilize fcp_embed_pbde.
+ */
+ if ((phba->sli3_options & LPFC_SLI4_PERFH_ENABLED) ||
+ phba->fcp_embed_pbde) {
bde = (struct ulp_bde64 *)
&(iocb_cmd->unsli3.sli3Words[5]);
bde->addrLow = first_data_sgl->addr_lo;
@@ -3772,20 +3781,18 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
scsi_set_resid(cmnd, be32_to_cpu(fcprsp->rspResId));
lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP_UNDER,
- "9025 FCP Read Underrun, expected %d, "
+ "9025 FCP Underrun, expected %d, "
"residual %d Data: x%x x%x x%x\n",
fcpDl,
scsi_get_resid(cmnd), fcpi_parm, cmnd->cmnd[0],
cmnd->underflow);
/*
- * If there is an under run check if under run reported by
+ * If there is an under run, check if under run reported by
* storage array is same as the under run reported by HBA.
* If this is not same, there is a dropped frame.
*/
- if ((cmnd->sc_data_direction == DMA_FROM_DEVICE) &&
- fcpi_parm &&
- (scsi_get_resid(cmnd) != fcpi_parm)) {
+ if (fcpi_parm && (scsi_get_resid(cmnd) != fcpi_parm)) {
lpfc_printf_vlog(vport, KERN_WARNING,
LOG_FCP | LOG_FCP_ERROR,
"9026 FCP Read Check Error "
@@ -3926,7 +3933,6 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
struct lpfc_rport_data *rdata = lpfc_cmd->rdata;
struct lpfc_nodelist *pnode = rdata->pnode;
struct scsi_cmnd *cmd;
- int depth;
unsigned long flags;
struct lpfc_fast_path_event *fast_path_evt;
struct Scsi_Host *shost;
@@ -4132,16 +4138,11 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
}
spin_unlock_irqrestore(shost->host_lock, flags);
} else if (pnode && NLP_CHK_NODE_ACT(pnode)) {
- if ((pnode->cmd_qdepth < vport->cfg_tgt_queue_depth) &&
- time_after(jiffies, pnode->last_change_time +
+ if ((pnode->cmd_qdepth != vport->cfg_tgt_queue_depth) &&
+ time_after(jiffies, pnode->last_change_time +
msecs_to_jiffies(LPFC_TGTQ_INTERVAL))) {
spin_lock_irqsave(shost->host_lock, flags);
- depth = pnode->cmd_qdepth * LPFC_TGTQ_RAMPUP_PCENT
- / 100;
- depth = depth ? depth : 1;
- pnode->cmd_qdepth += depth;
- if (pnode->cmd_qdepth > vport->cfg_tgt_queue_depth)
- pnode->cmd_qdepth = vport->cfg_tgt_queue_depth;
+ pnode->cmd_qdepth = vport->cfg_tgt_queue_depth;
pnode->last_change_time = jiffies;
spin_unlock_irqrestore(shost->host_lock, flags);
}
@@ -4564,9 +4565,32 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd)
*/
if (!ndlp || !NLP_CHK_NODE_ACT(ndlp))
goto out_tgt_busy;
- if (atomic_read(&ndlp->cmd_pending) >= ndlp->cmd_qdepth)
+ if (atomic_read(&ndlp->cmd_pending) >= ndlp->cmd_qdepth) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP_ERROR,
+ "3377 Target Queue Full, scsi Id:%d Qdepth:%d"
+ " Pending command:%d"
+ " WWNN:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x, "
+ " WWPN:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+ ndlp->nlp_sid, ndlp->cmd_qdepth,
+ atomic_read(&ndlp->cmd_pending),
+ ndlp->nlp_nodename.u.wwn[0],
+ ndlp->nlp_nodename.u.wwn[1],
+ ndlp->nlp_nodename.u.wwn[2],
+ ndlp->nlp_nodename.u.wwn[3],
+ ndlp->nlp_nodename.u.wwn[4],
+ ndlp->nlp_nodename.u.wwn[5],
+ ndlp->nlp_nodename.u.wwn[6],
+ ndlp->nlp_nodename.u.wwn[7],
+ ndlp->nlp_portname.u.wwn[0],
+ ndlp->nlp_portname.u.wwn[1],
+ ndlp->nlp_portname.u.wwn[2],
+ ndlp->nlp_portname.u.wwn[3],
+ ndlp->nlp_portname.u.wwn[4],
+ ndlp->nlp_portname.u.wwn[5],
+ ndlp->nlp_portname.u.wwn[6],
+ ndlp->nlp_portname.u.wwn[7]);
goto out_tgt_busy;
-
+ }
lpfc_cmd = lpfc_get_scsi_buf(phba, ndlp);
if (lpfc_cmd == NULL) {
lpfc_rampdown_queue_depth(phba);
diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h
index 5da7e15400cb..8e38e0204c47 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.h
+++ b/drivers/scsi/lpfc/lpfc_scsi.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Limited and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index 5f5528a12308..cb17e2b2be81 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -1,8 +1,7 @@
-
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Limited and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -36,6 +35,9 @@
#include <scsi/scsi_transport_fc.h>
#include <scsi/fc/fc_fs.h>
#include <linux/aer.h>
+#ifdef CONFIG_X86
+#include <asm/set_memory.h>
+#endif
#include <linux/nvme-fc-driver.h>
@@ -107,12 +109,14 @@ lpfc_get_iocb_from_iocbq(struct lpfc_iocbq *iocbq)
* The caller is expected to hold the hbalock when calling this routine.
**/
static int
-lpfc_sli4_wq_put(struct lpfc_queue *q, union lpfc_wqe *wqe)
+lpfc_sli4_wq_put(struct lpfc_queue *q, union lpfc_wqe128 *wqe)
{
union lpfc_wqe *temp_wqe;
struct lpfc_register doorbell;
uint32_t host_index;
uint32_t idx;
+ uint32_t i = 0;
+ uint8_t *tmp;
/* sanity check on queue memory */
if (unlikely(!q))
@@ -129,10 +133,25 @@ lpfc_sli4_wq_put(struct lpfc_queue *q, union lpfc_wqe *wqe)
/* set consumption flag every once in a while */
if (!((q->host_index + 1) % q->entry_repost))
bf_set(wqe_wqec, &wqe->generic.wqe_com, 1);
+ else
+ bf_set(wqe_wqec, &wqe->generic.wqe_com, 0);
if (q->phba->sli3_options & LPFC_SLI4_PHWQ_ENABLED)
bf_set(wqe_wqid, &wqe->generic.wqe_com, q->queue_id);
lpfc_sli_pcimem_bcopy(wqe, temp_wqe, q->entry_size);
- /* ensure WQE bcopy flushed before doorbell write */
+ if (q->dpp_enable && q->phba->cfg_enable_dpp) {
+ /* write to DPP aperture taking advatage of Combined Writes */
+ tmp = (uint8_t *)temp_wqe;
+#ifdef __raw_writeq
+ for (i = 0; i < q->entry_size; i += sizeof(uint64_t))
+ __raw_writeq(*((uint64_t *)(tmp + i)),
+ q->dpp_regaddr + i);
+#else
+ for (i = 0; i < q->entry_size; i += sizeof(uint32_t))
+ __raw_writel(*((uint32_t *)(tmp + i)),
+ q->dpp_regaddr + i);
+#endif
+ }
+ /* ensure WQE bcopy and DPP flushed before doorbell write */
wmb();
/* Update the host index before invoking device */
@@ -143,9 +162,18 @@ lpfc_sli4_wq_put(struct lpfc_queue *q, union lpfc_wqe *wqe)
/* Ring Doorbell */
doorbell.word0 = 0;
if (q->db_format == LPFC_DB_LIST_FORMAT) {
- bf_set(lpfc_wq_db_list_fm_num_posted, &doorbell, 1);
- bf_set(lpfc_wq_db_list_fm_index, &doorbell, host_index);
- bf_set(lpfc_wq_db_list_fm_id, &doorbell, q->queue_id);
+ if (q->dpp_enable && q->phba->cfg_enable_dpp) {
+ bf_set(lpfc_if6_wq_db_list_fm_num_posted, &doorbell, 1);
+ bf_set(lpfc_if6_wq_db_list_fm_dpp, &doorbell, 1);
+ bf_set(lpfc_if6_wq_db_list_fm_dpp_id, &doorbell,
+ q->dpp_id);
+ bf_set(lpfc_if6_wq_db_list_fm_id, &doorbell,
+ q->queue_id);
+ } else {
+ bf_set(lpfc_wq_db_list_fm_num_posted, &doorbell, 1);
+ bf_set(lpfc_wq_db_list_fm_index, &doorbell, host_index);
+ bf_set(lpfc_wq_db_list_fm_id, &doorbell, q->queue_id);
+ }
} else if (q->db_format == LPFC_DB_RING_FORMAT) {
bf_set(lpfc_wq_db_ring_fm_num_posted, &doorbell, 1);
bf_set(lpfc_wq_db_ring_fm_id, &doorbell, q->queue_id);
@@ -262,16 +290,18 @@ lpfc_sli4_mq_release(struct lpfc_queue *q)
static struct lpfc_eqe *
lpfc_sli4_eq_get(struct lpfc_queue *q)
{
+ struct lpfc_hba *phba;
struct lpfc_eqe *eqe;
uint32_t idx;
/* sanity check on queue memory */
if (unlikely(!q))
return NULL;
+ phba = q->phba;
eqe = q->qe[q->hba_index].eqe;
/* If the next EQE is not valid then we are done */
- if (!bf_get_le32(lpfc_eqe_valid, eqe))
+ if (bf_get_le32(lpfc_eqe_valid, eqe) != q->qe_valid)
return NULL;
/* If the host has not yet processed the next entry then we are done */
idx = ((q->hba_index + 1) % q->entry_count);
@@ -279,6 +309,10 @@ lpfc_sli4_eq_get(struct lpfc_queue *q)
return NULL;
q->hba_index = idx;
+ /* if the index wrapped around, toggle the valid bit */
+ if (phba->sli4_hba.pc_sli4_params.eqav && !q->hba_index)
+ q->qe_valid = (q->qe_valid) ? 0 : 1;
+
/*
* insert barrier for instruction interlock : data from the hardware
@@ -298,7 +332,7 @@ lpfc_sli4_eq_get(struct lpfc_queue *q)
* @q: The Event Queue to disable interrupts
*
**/
-static inline void
+inline void
lpfc_sli4_eq_clr_intr(struct lpfc_queue *q)
{
struct lpfc_register doorbell;
@@ -309,7 +343,26 @@ lpfc_sli4_eq_clr_intr(struct lpfc_queue *q)
bf_set(lpfc_eqcq_doorbell_eqid_hi, &doorbell,
(q->queue_id >> LPFC_EQID_HI_FIELD_SHIFT));
bf_set(lpfc_eqcq_doorbell_eqid_lo, &doorbell, q->queue_id);
- writel(doorbell.word0, q->phba->sli4_hba.EQCQDBregaddr);
+ writel(doorbell.word0, q->phba->sli4_hba.EQDBregaddr);
+}
+
+/**
+ * lpfc_sli4_if6_eq_clr_intr - Turn off interrupts from this EQ
+ * @q: The Event Queue to disable interrupts
+ *
+ **/
+inline void
+lpfc_sli4_if6_eq_clr_intr(struct lpfc_queue *q)
+{
+ struct lpfc_register doorbell;
+
+ doorbell.word0 = 0;
+ bf_set(lpfc_eqcq_doorbell_eqci, &doorbell, 1);
+ bf_set(lpfc_eqcq_doorbell_qt, &doorbell, LPFC_QUEUE_TYPE_EVENT);
+ bf_set(lpfc_eqcq_doorbell_eqid_hi, &doorbell,
+ (q->queue_id >> LPFC_EQID_HI_FIELD_SHIFT));
+ bf_set(lpfc_eqcq_doorbell_eqid_lo, &doorbell, q->queue_id);
+ writel(doorbell.word0, q->phba->sli4_hba.EQDBregaddr);
}
/**
@@ -331,17 +384,21 @@ uint32_t
lpfc_sli4_eq_release(struct lpfc_queue *q, bool arm)
{
uint32_t released = 0;
+ struct lpfc_hba *phba;
struct lpfc_eqe *temp_eqe;
struct lpfc_register doorbell;
/* sanity check on queue memory */
if (unlikely(!q))
return 0;
+ phba = q->phba;
/* while there are valid entries */
while (q->hba_index != q->host_index) {
- temp_eqe = q->qe[q->host_index].eqe;
- bf_set_le32(lpfc_eqe_valid, temp_eqe, 0);
+ if (!phba->sli4_hba.pc_sli4_params.eqav) {
+ temp_eqe = q->qe[q->host_index].eqe;
+ bf_set_le32(lpfc_eqe_valid, temp_eqe, 0);
+ }
released++;
q->host_index = ((q->host_index + 1) % q->entry_count);
}
@@ -359,10 +416,63 @@ lpfc_sli4_eq_release(struct lpfc_queue *q, bool arm)
bf_set(lpfc_eqcq_doorbell_eqid_hi, &doorbell,
(q->queue_id >> LPFC_EQID_HI_FIELD_SHIFT));
bf_set(lpfc_eqcq_doorbell_eqid_lo, &doorbell, q->queue_id);
- writel(doorbell.word0, q->phba->sli4_hba.EQCQDBregaddr);
+ writel(doorbell.word0, q->phba->sli4_hba.EQDBregaddr);
/* PCI read to flush PCI pipeline on re-arming for INTx mode */
if ((q->phba->intr_type == INTx) && (arm == LPFC_QUEUE_REARM))
- readl(q->phba->sli4_hba.EQCQDBregaddr);
+ readl(q->phba->sli4_hba.EQDBregaddr);
+ return released;
+}
+
+/**
+ * lpfc_sli4_if6_eq_release - Indicates the host has finished processing an EQ
+ * @q: The Event Queue that the host has completed processing for.
+ * @arm: Indicates whether the host wants to arms this CQ.
+ *
+ * This routine will mark all Event Queue Entries on @q, from the last
+ * known completed entry to the last entry that was processed, as completed
+ * by clearing the valid bit for each completion queue entry. Then it will
+ * notify the HBA, by ringing the doorbell, that the EQEs have been processed.
+ * The internal host index in the @q will be updated by this routine to indicate
+ * that the host has finished processing the entries. The @arm parameter
+ * indicates that the queue should be rearmed when ringing the doorbell.
+ *
+ * This function will return the number of EQEs that were popped.
+ **/
+uint32_t
+lpfc_sli4_if6_eq_release(struct lpfc_queue *q, bool arm)
+{
+ uint32_t released = 0;
+ struct lpfc_hba *phba;
+ struct lpfc_eqe *temp_eqe;
+ struct lpfc_register doorbell;
+
+ /* sanity check on queue memory */
+ if (unlikely(!q))
+ return 0;
+ phba = q->phba;
+
+ /* while there are valid entries */
+ while (q->hba_index != q->host_index) {
+ if (!phba->sli4_hba.pc_sli4_params.eqav) {
+ temp_eqe = q->qe[q->host_index].eqe;
+ bf_set_le32(lpfc_eqe_valid, temp_eqe, 0);
+ }
+ released++;
+ q->host_index = ((q->host_index + 1) % q->entry_count);
+ }
+ if (unlikely(released == 0 && !arm))
+ return 0;
+
+ /* ring doorbell for number popped */
+ doorbell.word0 = 0;
+ if (arm)
+ bf_set(lpfc_if6_eq_doorbell_arm, &doorbell, 1);
+ bf_set(lpfc_if6_eq_doorbell_num_released, &doorbell, released);
+ bf_set(lpfc_if6_eq_doorbell_eqid, &doorbell, q->queue_id);
+ writel(doorbell.word0, q->phba->sli4_hba.EQDBregaddr);
+ /* PCI read to flush PCI pipeline on re-arming for INTx mode */
+ if ((q->phba->intr_type == INTx) && (arm == LPFC_QUEUE_REARM))
+ readl(q->phba->sli4_hba.EQDBregaddr);
return released;
}
@@ -378,23 +488,28 @@ lpfc_sli4_eq_release(struct lpfc_queue *q, bool arm)
static struct lpfc_cqe *
lpfc_sli4_cq_get(struct lpfc_queue *q)
{
+ struct lpfc_hba *phba;
struct lpfc_cqe *cqe;
uint32_t idx;
/* sanity check on queue memory */
if (unlikely(!q))
return NULL;
+ phba = q->phba;
+ cqe = q->qe[q->hba_index].cqe;
/* If the next CQE is not valid then we are done */
- if (!bf_get_le32(lpfc_cqe_valid, q->qe[q->hba_index].cqe))
+ if (bf_get_le32(lpfc_cqe_valid, cqe) != q->qe_valid)
return NULL;
/* If the host has not yet processed the next entry then we are done */
idx = ((q->hba_index + 1) % q->entry_count);
if (idx == q->host_index)
return NULL;
- cqe = q->qe[q->hba_index].cqe;
q->hba_index = idx;
+ /* if the index wrapped around, toggle the valid bit */
+ if (phba->sli4_hba.pc_sli4_params.cqav && !q->hba_index)
+ q->qe_valid = (q->qe_valid) ? 0 : 1;
/*
* insert barrier for instruction interlock : data from the hardware
@@ -427,16 +542,21 @@ uint32_t
lpfc_sli4_cq_release(struct lpfc_queue *q, bool arm)
{
uint32_t released = 0;
+ struct lpfc_hba *phba;
struct lpfc_cqe *temp_qe;
struct lpfc_register doorbell;
/* sanity check on queue memory */
if (unlikely(!q))
return 0;
+ phba = q->phba;
+
/* while there are valid entries */
while (q->hba_index != q->host_index) {
- temp_qe = q->qe[q->host_index].cqe;
- bf_set_le32(lpfc_cqe_valid, temp_qe, 0);
+ if (!phba->sli4_hba.pc_sli4_params.cqav) {
+ temp_qe = q->qe[q->host_index].cqe;
+ bf_set_le32(lpfc_cqe_valid, temp_qe, 0);
+ }
released++;
q->host_index = ((q->host_index + 1) % q->entry_count);
}
@@ -452,7 +572,57 @@ lpfc_sli4_cq_release(struct lpfc_queue *q, bool arm)
bf_set(lpfc_eqcq_doorbell_cqid_hi, &doorbell,
(q->queue_id >> LPFC_CQID_HI_FIELD_SHIFT));
bf_set(lpfc_eqcq_doorbell_cqid_lo, &doorbell, q->queue_id);
- writel(doorbell.word0, q->phba->sli4_hba.EQCQDBregaddr);
+ writel(doorbell.word0, q->phba->sli4_hba.CQDBregaddr);
+ return released;
+}
+
+/**
+ * lpfc_sli4_if6_cq_release - Indicates the host has finished processing a CQ
+ * @q: The Completion Queue that the host has completed processing for.
+ * @arm: Indicates whether the host wants to arms this CQ.
+ *
+ * This routine will mark all Completion queue entries on @q, from the last
+ * known completed entry to the last entry that was processed, as completed
+ * by clearing the valid bit for each completion queue entry. Then it will
+ * notify the HBA, by ringing the doorbell, that the CQEs have been processed.
+ * The internal host index in the @q will be updated by this routine to indicate
+ * that the host has finished processing the entries. The @arm parameter
+ * indicates that the queue should be rearmed when ringing the doorbell.
+ *
+ * This function will return the number of CQEs that were released.
+ **/
+uint32_t
+lpfc_sli4_if6_cq_release(struct lpfc_queue *q, bool arm)
+{
+ uint32_t released = 0;
+ struct lpfc_hba *phba;
+ struct lpfc_cqe *temp_qe;
+ struct lpfc_register doorbell;
+
+ /* sanity check on queue memory */
+ if (unlikely(!q))
+ return 0;
+ phba = q->phba;
+
+ /* while there are valid entries */
+ while (q->hba_index != q->host_index) {
+ if (!phba->sli4_hba.pc_sli4_params.cqav) {
+ temp_qe = q->qe[q->host_index].cqe;
+ bf_set_le32(lpfc_cqe_valid, temp_qe, 0);
+ }
+ released++;
+ q->host_index = ((q->host_index + 1) % q->entry_count);
+ }
+ if (unlikely(released == 0 && !arm))
+ return 0;
+
+ /* ring doorbell for number popped */
+ doorbell.word0 = 0;
+ if (arm)
+ bf_set(lpfc_if6_cq_doorbell_arm, &doorbell, 1);
+ bf_set(lpfc_if6_cq_doorbell_num_released, &doorbell, released);
+ bf_set(lpfc_if6_cq_doorbell_cqid, &doorbell, q->queue_id);
+ writel(doorbell.word0, q->phba->sli4_hba.CQDBregaddr);
return released;
}
@@ -2218,18 +2388,18 @@ lpfc_sli_chk_mbx_command(uint8_t mbxCommand)
void
lpfc_sli_wake_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
{
- wait_queue_head_t *pdone_q;
unsigned long drvr_flag;
+ struct completion *pmbox_done;
/*
- * If pdone_q is empty, the driver thread gave up waiting and
+ * If pmbox_done is empty, the driver thread gave up waiting and
* continued running.
*/
pmboxq->mbox_flag |= LPFC_MBX_WAKE;
spin_lock_irqsave(&phba->hbalock, drvr_flag);
- pdone_q = (wait_queue_head_t *) pmboxq->context1;
- if (pdone_q)
- wake_up_interruptible(pdone_q);
+ pmbox_done = (struct completion *)pmboxq->context3;
+ if (pmbox_done)
+ complete(pmbox_done);
spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
return;
}
@@ -2330,7 +2500,7 @@ lpfc_sli4_unreg_rpi_cmpl_clr(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
if (pmb->u.mb.mbxCommand == MBX_UNREG_LOGIN) {
if (phba->sli_rev == LPFC_SLI_REV4 &&
(bf_get(lpfc_sli_intf_if_type,
- &phba->sli4_hba.sli_intf) ==
+ &phba->sli4_hba.sli_intf) >=
LPFC_SLI_INTF_IF_TYPE_2)) {
if (ndlp) {
lpfc_printf_vlog(vport, KERN_INFO, LOG_SLI,
@@ -3776,6 +3946,7 @@ lpfc_sli_flush_fcp_rings(struct lpfc_hba *phba)
struct lpfc_sli *psli = &phba->sli;
struct lpfc_sli_ring *pring;
uint32_t i;
+ struct lpfc_iocbq *piocb, *next_iocb;
spin_lock_irq(&phba->hbalock);
/* Indicate the I/O queues are flushed */
@@ -3790,6 +3961,9 @@ lpfc_sli_flush_fcp_rings(struct lpfc_hba *phba)
spin_lock_irq(&pring->ring_lock);
/* Retrieve everything on txq */
list_splice_init(&pring->txq, &txq);
+ list_for_each_entry_safe(piocb, next_iocb,
+ &pring->txcmplq, list)
+ piocb->iocb_flag &= ~LPFC_IO_ON_TXCMPLQ;
/* Retrieve everything on the txcmplq */
list_splice_init(&pring->txcmplq, &txcmplq);
pring->txq_cnt = 0;
@@ -3811,6 +3985,9 @@ lpfc_sli_flush_fcp_rings(struct lpfc_hba *phba)
spin_lock_irq(&phba->hbalock);
/* Retrieve everything on txq */
list_splice_init(&pring->txq, &txq);
+ list_for_each_entry_safe(piocb, next_iocb,
+ &pring->txcmplq, list)
+ piocb->iocb_flag &= ~LPFC_IO_ON_TXCMPLQ;
/* Retrieve everything on the txcmplq */
list_splice_init(&pring->txcmplq, &txcmplq);
pring->txq_cnt = 0;
@@ -3842,6 +4019,7 @@ lpfc_sli_flush_nvme_rings(struct lpfc_hba *phba)
LIST_HEAD(txcmplq);
struct lpfc_sli_ring *pring;
uint32_t i;
+ struct lpfc_iocbq *piocb, *next_iocb;
if (phba->sli_rev < LPFC_SLI_REV4)
return;
@@ -3858,8 +4036,11 @@ lpfc_sli_flush_nvme_rings(struct lpfc_hba *phba)
for (i = 0; i < phba->cfg_nvme_io_channel; i++) {
pring = phba->sli4_hba.nvme_wq[i]->pring;
- /* Retrieve everything on the txcmplq */
spin_lock_irq(&pring->ring_lock);
+ list_for_each_entry_safe(piocb, next_iocb,
+ &pring->txcmplq, list)
+ piocb->iocb_flag &= ~LPFC_IO_ON_TXCMPLQ;
+ /* Retrieve everything on the txcmplq */
list_splice_init(&pring->txcmplq, &txcmplq);
pring->txcmplq_cnt = 0;
spin_unlock_irq(&pring->ring_lock);
@@ -4812,13 +4993,14 @@ lpfc_sli_config_port(struct lpfc_hba *phba, int sli_mode)
phba->hbq_get = phba->mbox->us.s3_pgp.hbq_get;
phba->port_gp = phba->mbox->us.s3_pgp.port;
- if (phba->cfg_enable_bg) {
- if (pmb->u.mb.un.varCfgPort.gbg)
- phba->sli3_options |= LPFC_SLI3_BG_ENABLED;
- else
+ if (phba->sli3_options & LPFC_SLI3_BG_ENABLED) {
+ if (pmb->u.mb.un.varCfgPort.gbg == 0) {
+ phba->cfg_enable_bg = 0;
+ phba->sli3_options &= ~LPFC_SLI3_BG_ENABLED;
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"0443 Adapter did not grant "
"BlockGuard\n");
+ }
}
} else {
phba->hbq_get = NULL;
@@ -5290,41 +5472,42 @@ static void
lpfc_sli4_arm_cqeq_intr(struct lpfc_hba *phba)
{
int qidx;
+ struct lpfc_sli4_hba *sli4_hba = &phba->sli4_hba;
- lpfc_sli4_cq_release(phba->sli4_hba.mbx_cq, LPFC_QUEUE_REARM);
- lpfc_sli4_cq_release(phba->sli4_hba.els_cq, LPFC_QUEUE_REARM);
- if (phba->sli4_hba.nvmels_cq)
- lpfc_sli4_cq_release(phba->sli4_hba.nvmels_cq,
+ sli4_hba->sli4_cq_release(sli4_hba->mbx_cq, LPFC_QUEUE_REARM);
+ sli4_hba->sli4_cq_release(sli4_hba->els_cq, LPFC_QUEUE_REARM);
+ if (sli4_hba->nvmels_cq)
+ sli4_hba->sli4_cq_release(sli4_hba->nvmels_cq,
LPFC_QUEUE_REARM);
- if (phba->sli4_hba.fcp_cq)
+ if (sli4_hba->fcp_cq)
for (qidx = 0; qidx < phba->cfg_fcp_io_channel; qidx++)
- lpfc_sli4_cq_release(phba->sli4_hba.fcp_cq[qidx],
+ sli4_hba->sli4_cq_release(sli4_hba->fcp_cq[qidx],
LPFC_QUEUE_REARM);
- if (phba->sli4_hba.nvme_cq)
+ if (sli4_hba->nvme_cq)
for (qidx = 0; qidx < phba->cfg_nvme_io_channel; qidx++)
- lpfc_sli4_cq_release(phba->sli4_hba.nvme_cq[qidx],
+ sli4_hba->sli4_cq_release(sli4_hba->nvme_cq[qidx],
LPFC_QUEUE_REARM);
if (phba->cfg_fof)
- lpfc_sli4_cq_release(phba->sli4_hba.oas_cq, LPFC_QUEUE_REARM);
+ sli4_hba->sli4_cq_release(sli4_hba->oas_cq, LPFC_QUEUE_REARM);
- if (phba->sli4_hba.hba_eq)
+ if (sli4_hba->hba_eq)
for (qidx = 0; qidx < phba->io_channel_irqs; qidx++)
- lpfc_sli4_eq_release(phba->sli4_hba.hba_eq[qidx],
- LPFC_QUEUE_REARM);
+ sli4_hba->sli4_eq_release(sli4_hba->hba_eq[qidx],
+ LPFC_QUEUE_REARM);
if (phba->nvmet_support) {
for (qidx = 0; qidx < phba->cfg_nvmet_mrq; qidx++) {
- lpfc_sli4_cq_release(
- phba->sli4_hba.nvmet_cqset[qidx],
+ sli4_hba->sli4_cq_release(
+ sli4_hba->nvmet_cqset[qidx],
LPFC_QUEUE_REARM);
}
}
if (phba->cfg_fof)
- lpfc_sli4_eq_release(phba->sli4_hba.fof_eq, LPFC_QUEUE_REARM);
+ sli4_hba->sli4_eq_release(sli4_hba->fof_eq, LPFC_QUEUE_REARM);
}
/**
@@ -6533,9 +6716,11 @@ lpfc_post_rq_buffer(struct lpfc_hba *phba, struct lpfc_queue *hrq,
struct lpfc_rqe hrqe;
struct lpfc_rqe drqe;
struct lpfc_rqb *rqbp;
+ unsigned long flags;
struct rqb_dmabuf *rqb_buffer;
LIST_HEAD(rqb_buf_list);
+ spin_lock_irqsave(&phba->hbalock, flags);
rqbp = hrq->rqbp;
for (i = 0; i < count; i++) {
/* IF RQ is already full, don't bother */
@@ -6559,6 +6744,15 @@ lpfc_post_rq_buffer(struct lpfc_hba *phba, struct lpfc_queue *hrq,
drqe.address_hi = putPaddrHigh(rqb_buffer->dbuf.phys);
rc = lpfc_sli4_rq_put(hrq, drq, &hrqe, &drqe);
if (rc < 0) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "6421 Cannot post to HRQ %d: %x %x %x "
+ "DRQ %x %x\n",
+ hrq->queue_id,
+ hrq->host_index,
+ hrq->hba_index,
+ hrq->entry_count,
+ drq->host_index,
+ drq->hba_index);
rqbp->rqb_free_buffer(phba, rqb_buffer);
} else {
list_add_tail(&rqb_buffer->hbuf.list,
@@ -6566,6 +6760,7 @@ lpfc_post_rq_buffer(struct lpfc_hba *phba, struct lpfc_queue *hrq,
rqbp->buffer_count++;
}
}
+ spin_unlock_irqrestore(&phba->hbalock, flags);
return 1;
}
@@ -6693,6 +6888,18 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
/* Save information as VPD data */
phba->vpd.rev.biuRev = mqe->un.read_rev.first_hw_rev;
phba->vpd.rev.smRev = mqe->un.read_rev.second_hw_rev;
+
+ /*
+ * This is because first G7 ASIC doesn't support the standard
+ * 0x5a NVME cmd descriptor type/subtype
+ */
+ if ((bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) ==
+ LPFC_SLI_INTF_IF_TYPE_6) &&
+ (phba->vpd.rev.biuRev == LPFC_G7_ASIC_1) &&
+ (phba->vpd.rev.smRev == 0) &&
+ (phba->cfg_nvme_embed_cmd == 1))
+ phba->cfg_nvme_embed_cmd = 0;
+
phba->vpd.rev.endecRev = mqe->un.read_rev.third_hw_rev;
phba->vpd.rev.fcphHigh = bf_get(lpfc_mbx_rd_rev_fcph_high,
&mqe->un.read_rev);
@@ -6771,21 +6978,26 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
"0378 No support for fcpi mode.\n");
ftr_rsp++;
}
- if (bf_get(lpfc_mbx_rq_ftr_rsp_perfh, &mqe->un.req_ftrs))
- phba->sli3_options |= LPFC_SLI4_PERFH_ENABLED;
- else
- phba->sli3_options &= ~LPFC_SLI4_PERFH_ENABLED;
+
+ /* Performance Hints are ONLY for FCoE */
+ if (phba->hba_flag & HBA_FCOE_MODE) {
+ if (bf_get(lpfc_mbx_rq_ftr_rsp_perfh, &mqe->un.req_ftrs))
+ phba->sli3_options |= LPFC_SLI4_PERFH_ENABLED;
+ else
+ phba->sli3_options &= ~LPFC_SLI4_PERFH_ENABLED;
+ }
+
/*
* If the port cannot support the host's requested features
* then turn off the global config parameters to disable the
* feature in the driver. This is not a fatal error.
*/
- phba->sli3_options &= ~LPFC_SLI3_BG_ENABLED;
- if (phba->cfg_enable_bg) {
- if (bf_get(lpfc_mbx_rq_ftr_rsp_dif, &mqe->un.req_ftrs))
- phba->sli3_options |= LPFC_SLI3_BG_ENABLED;
- else
+ if (phba->sli3_options & LPFC_SLI3_BG_ENABLED) {
+ if (!(bf_get(lpfc_mbx_rq_ftr_rsp_dif, &mqe->un.req_ftrs))) {
+ phba->cfg_enable_bg = 0;
+ phba->sli3_options &= ~LPFC_SLI3_BG_ENABLED;
ftr_rsp++;
+ }
}
if (phba->max_vpi && phba->cfg_enable_npiv &&
@@ -7209,6 +7421,7 @@ lpfc_sli4_mbox_completions_pending(struct lpfc_hba *phba)
struct lpfc_queue *mcq;
struct lpfc_mcqe *mcqe;
bool pending_completions = false;
+ uint8_t qe_valid;
if (unlikely(!phba) || (phba->sli_rev != LPFC_SLI_REV4))
return false;
@@ -7217,7 +7430,8 @@ lpfc_sli4_mbox_completions_pending(struct lpfc_hba *phba)
mcq = phba->sli4_hba.mbx_cq;
idx = mcq->hba_index;
- while (bf_get_le32(lpfc_cqe_valid, mcq->qe[idx].cqe)) {
+ qe_valid = mcq->qe_valid;
+ while (bf_get_le32(lpfc_cqe_valid, mcq->qe[idx].cqe) == qe_valid) {
mcqe = (struct lpfc_mcqe *)mcq->qe[idx].cqe;
if (bf_get_le32(lpfc_trailer_completed, mcqe) &&
(!bf_get_le32(lpfc_trailer_async, mcqe))) {
@@ -7227,6 +7441,10 @@ lpfc_sli4_mbox_completions_pending(struct lpfc_hba *phba)
idx = (idx + 1) % mcq->entry_count;
if (mcq->hba_index == idx)
break;
+
+ /* if the index wrapped around, toggle the valid bit */
+ if (phba->sli4_hba.pc_sli4_params.cqav && !idx)
+ qe_valid = (qe_valid) ? 0 : 1;
}
return pending_completions;
@@ -7246,7 +7464,7 @@ lpfc_sli4_mbox_completions_pending(struct lpfc_hba *phba)
bool
lpfc_sli4_process_missed_mbox_completions(struct lpfc_hba *phba)
{
-
+ struct lpfc_sli4_hba *sli4_hba = &phba->sli4_hba;
uint32_t eqidx;
struct lpfc_queue *fpeq = NULL;
struct lpfc_eqe *eqe;
@@ -7257,11 +7475,11 @@ lpfc_sli4_process_missed_mbox_completions(struct lpfc_hba *phba)
/* Find the eq associated with the mcq */
- if (phba->sli4_hba.hba_eq)
+ if (sli4_hba->hba_eq)
for (eqidx = 0; eqidx < phba->io_channel_irqs; eqidx++)
- if (phba->sli4_hba.hba_eq[eqidx]->queue_id ==
- phba->sli4_hba.mbx_cq->assoc_qid) {
- fpeq = phba->sli4_hba.hba_eq[eqidx];
+ if (sli4_hba->hba_eq[eqidx]->queue_id ==
+ sli4_hba->mbx_cq->assoc_qid) {
+ fpeq = sli4_hba->hba_eq[eqidx];
break;
}
if (!fpeq)
@@ -7269,7 +7487,7 @@ lpfc_sli4_process_missed_mbox_completions(struct lpfc_hba *phba)
/* Turn off interrupts from this EQ */
- lpfc_sli4_eq_clr_intr(fpeq);
+ sli4_hba->sli4_eq_clr_intr(fpeq);
/* Check to see if a mbox completion is pending */
@@ -7290,7 +7508,7 @@ lpfc_sli4_process_missed_mbox_completions(struct lpfc_hba *phba)
/* Always clear and re-arm the EQ */
- lpfc_sli4_eq_release(fpeq, LPFC_QUEUE_REARM);
+ sli4_hba->sli4_eq_release(fpeq, LPFC_QUEUE_REARM);
return mbox_pending;
@@ -8100,7 +8318,7 @@ lpfc_sli_issue_mbox_s4(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq,
} else if (flag == MBX_POLL) {
lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX | LOG_SLI,
"(%d):2542 Try to issue mailbox command "
- "x%x (x%x/x%x) synchronously ahead of async"
+ "x%x (x%x/x%x) synchronously ahead of async "
"mailbox command queue: x%x x%x\n",
mboxq->vport ? mboxq->vport->vpi : 0,
mboxq->u.mb.mbxCommand,
@@ -8664,7 +8882,7 @@ lpfc_sli4_bpl2sgl(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq,
**/
static int
lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
- union lpfc_wqe *wqe)
+ union lpfc_wqe128 *wqe)
{
uint32_t xmit_len = 0, total_len = 0;
uint8_t ct = 0;
@@ -8767,7 +8985,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
iocbq->context2)->virt);
if_type = bf_get(lpfc_sli_intf_if_type,
&phba->sli4_hba.sli_intf);
- if (if_type == LPFC_SLI_INTF_IF_TYPE_2) {
+ if (if_type >= LPFC_SLI_INTF_IF_TYPE_2) {
if (pcmd && (*pcmd == ELS_CMD_FLOGI ||
*pcmd == ELS_CMD_SCR ||
*pcmd == ELS_CMD_FDISC ||
@@ -8870,31 +9088,36 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
}
/* Note, word 10 is already initialized to 0 */
+ /* Don't set PBDE for Perf hints, just fcp_embed_pbde */
+ if (phba->fcp_embed_pbde)
+ bf_set(wqe_pbde, &wqe->fcp_iwrite.wqe_com, 1);
+ else
+ bf_set(wqe_pbde, &wqe->fcp_iwrite.wqe_com, 0);
+
if (phba->fcp_embed_io) {
struct lpfc_scsi_buf *lpfc_cmd;
struct sli4_sge *sgl;
- union lpfc_wqe128 *wqe128;
struct fcp_cmnd *fcp_cmnd;
uint32_t *ptr;
/* 128 byte wqe support here */
- wqe128 = (union lpfc_wqe128 *)wqe;
lpfc_cmd = iocbq->context1;
sgl = (struct sli4_sge *)lpfc_cmd->fcp_bpl;
fcp_cmnd = lpfc_cmd->fcp_cmnd;
/* Word 0-2 - FCP_CMND */
- wqe128->generic.bde.tus.f.bdeFlags =
+ wqe->generic.bde.tus.f.bdeFlags =
BUFF_TYPE_BDE_IMMED;
- wqe128->generic.bde.tus.f.bdeSize = sgl->sge_len;
- wqe128->generic.bde.addrHigh = 0;
- wqe128->generic.bde.addrLow = 88; /* Word 22 */
+ wqe->generic.bde.tus.f.bdeSize = sgl->sge_len;
+ wqe->generic.bde.addrHigh = 0;
+ wqe->generic.bde.addrLow = 88; /* Word 22 */
- bf_set(wqe_wqes, &wqe128->fcp_iwrite.wqe_com, 1);
+ bf_set(wqe_wqes, &wqe->fcp_iwrite.wqe_com, 1);
+ bf_set(wqe_dbde, &wqe->fcp_iwrite.wqe_com, 0);
/* Word 22-29 FCP CMND Payload */
- ptr = &wqe128->words[22];
+ ptr = &wqe->words[22];
memcpy(ptr, fcp_cmnd, sizeof(struct fcp_cmnd));
}
break;
@@ -8929,31 +9152,36 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
}
/* Note, word 10 is already initialized to 0 */
+ /* Don't set PBDE for Perf hints, just fcp_embed_pbde */
+ if (phba->fcp_embed_pbde)
+ bf_set(wqe_pbde, &wqe->fcp_iread.wqe_com, 1);
+ else
+ bf_set(wqe_pbde, &wqe->fcp_iread.wqe_com, 0);
+
if (phba->fcp_embed_io) {
struct lpfc_scsi_buf *lpfc_cmd;
struct sli4_sge *sgl;
- union lpfc_wqe128 *wqe128;
struct fcp_cmnd *fcp_cmnd;
uint32_t *ptr;
/* 128 byte wqe support here */
- wqe128 = (union lpfc_wqe128 *)wqe;
lpfc_cmd = iocbq->context1;
sgl = (struct sli4_sge *)lpfc_cmd->fcp_bpl;
fcp_cmnd = lpfc_cmd->fcp_cmnd;
/* Word 0-2 - FCP_CMND */
- wqe128->generic.bde.tus.f.bdeFlags =
+ wqe->generic.bde.tus.f.bdeFlags =
BUFF_TYPE_BDE_IMMED;
- wqe128->generic.bde.tus.f.bdeSize = sgl->sge_len;
- wqe128->generic.bde.addrHigh = 0;
- wqe128->generic.bde.addrLow = 88; /* Word 22 */
+ wqe->generic.bde.tus.f.bdeSize = sgl->sge_len;
+ wqe->generic.bde.addrHigh = 0;
+ wqe->generic.bde.addrLow = 88; /* Word 22 */
- bf_set(wqe_wqes, &wqe128->fcp_iread.wqe_com, 1);
+ bf_set(wqe_wqes, &wqe->fcp_iread.wqe_com, 1);
+ bf_set(wqe_dbde, &wqe->fcp_iread.wqe_com, 0);
/* Word 22-29 FCP CMND Payload */
- ptr = &wqe128->words[22];
+ ptr = &wqe->words[22];
memcpy(ptr, fcp_cmnd, sizeof(struct fcp_cmnd));
}
break;
@@ -8990,28 +9218,27 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
if (phba->fcp_embed_io) {
struct lpfc_scsi_buf *lpfc_cmd;
struct sli4_sge *sgl;
- union lpfc_wqe128 *wqe128;
struct fcp_cmnd *fcp_cmnd;
uint32_t *ptr;
/* 128 byte wqe support here */
- wqe128 = (union lpfc_wqe128 *)wqe;
lpfc_cmd = iocbq->context1;
sgl = (struct sli4_sge *)lpfc_cmd->fcp_bpl;
fcp_cmnd = lpfc_cmd->fcp_cmnd;
/* Word 0-2 - FCP_CMND */
- wqe128->generic.bde.tus.f.bdeFlags =
+ wqe->generic.bde.tus.f.bdeFlags =
BUFF_TYPE_BDE_IMMED;
- wqe128->generic.bde.tus.f.bdeSize = sgl->sge_len;
- wqe128->generic.bde.addrHigh = 0;
- wqe128->generic.bde.addrLow = 88; /* Word 22 */
+ wqe->generic.bde.tus.f.bdeSize = sgl->sge_len;
+ wqe->generic.bde.addrHigh = 0;
+ wqe->generic.bde.addrLow = 88; /* Word 22 */
- bf_set(wqe_wqes, &wqe128->fcp_icmd.wqe_com, 1);
+ bf_set(wqe_wqes, &wqe->fcp_icmd.wqe_com, 1);
+ bf_set(wqe_dbde, &wqe->fcp_icmd.wqe_com, 0);
/* Word 22-29 FCP CMND Payload */
- ptr = &wqe128->words[22];
+ ptr = &wqe->words[22];
memcpy(ptr, fcp_cmnd, sizeof(struct fcp_cmnd));
}
break;
@@ -9064,7 +9291,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
if_type = bf_get(lpfc_sli_intf_if_type,
&phba->sli4_hba.sli_intf);
- if (if_type == LPFC_SLI_INTF_IF_TYPE_2) {
+ if (if_type >= LPFC_SLI_INTF_IF_TYPE_2) {
if (iocbq->vport->fc_flag & FC_PT2PT) {
bf_set(els_rsp64_sp, &wqe->xmit_els_rsp, 1);
bf_set(els_rsp64_sid, &wqe->xmit_els_rsp,
@@ -9249,8 +9476,7 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number,
struct lpfc_iocbq *piocb, uint32_t flag)
{
struct lpfc_sglq *sglq;
- union lpfc_wqe *wqe;
- union lpfc_wqe128 wqe128;
+ union lpfc_wqe128 wqe;
struct lpfc_queue *wq;
struct lpfc_sli_ring *pring;
@@ -9270,9 +9496,7 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number,
/*
* The WQE can be either 64 or 128 bytes,
- * so allocate space on the stack assuming the largest.
*/
- wqe = (union lpfc_wqe *)&wqe128;
lockdep_assert_held(&phba->hbalock);
@@ -9322,10 +9546,10 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number,
return IOCB_ERROR;
}
- if (lpfc_sli4_iocb2wqe(phba, piocb, wqe))
+ if (lpfc_sli4_iocb2wqe(phba, piocb, &wqe))
return IOCB_ERROR;
- if (lpfc_sli4_wq_put(wq, wqe))
+ if (lpfc_sli4_wq_put(wq, &wqe))
return IOCB_ERROR;
lpfc_sli_ringtxcmpl_put(phba, pring, piocb);
@@ -9470,7 +9694,7 @@ lpfc_sli_issue_iocb(struct lpfc_hba *phba, uint32_t ring_number,
fpeq = phba->sli4_hba.hba_eq[idx];
/* Turn off interrupts from this EQ */
- lpfc_sli4_eq_clr_intr(fpeq);
+ phba->sli4_hba.sli4_eq_clr_intr(fpeq);
/*
* Process all the events on FCP EQ
@@ -9482,7 +9706,7 @@ lpfc_sli_issue_iocb(struct lpfc_hba *phba, uint32_t ring_number,
}
/* Always clear and re-arm the EQ */
- lpfc_sli4_eq_release(fpeq,
+ phba->sli4_hba.sli4_eq_release(fpeq,
LPFC_QUEUE_REARM);
}
atomic_inc(&hba_eq_hdl->hba_eq_in_use);
@@ -10695,7 +10919,7 @@ lpfc_sli4_abort_nvme_io(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
{
struct lpfc_vport *vport = cmdiocb->vport;
struct lpfc_iocbq *abtsiocbp;
- union lpfc_wqe *abts_wqe;
+ union lpfc_wqe128 *abts_wqe;
int retval;
/*
@@ -11442,31 +11666,25 @@ int
lpfc_sli_issue_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq,
uint32_t timeout)
{
- DECLARE_WAIT_QUEUE_HEAD_ONSTACK(done_q);
- MAILBOX_t *mb = NULL;
+ struct completion mbox_done;
int retval;
unsigned long flag;
- /* The caller might set context1 for extended buffer */
- if (pmboxq->context1)
- mb = (MAILBOX_t *)pmboxq->context1;
-
pmboxq->mbox_flag &= ~LPFC_MBX_WAKE;
/* setup wake call as IOCB callback */
pmboxq->mbox_cmpl = lpfc_sli_wake_mbox_wait;
- /* setup context field to pass wait_queue pointer to wake function */
- pmboxq->context1 = &done_q;
+ /* setup context3 field to pass wait_queue pointer to wake function */
+ init_completion(&mbox_done);
+ pmboxq->context3 = &mbox_done;
/* now issue the command */
retval = lpfc_sli_issue_mbox(phba, pmboxq, MBX_NOWAIT);
if (retval == MBX_BUSY || retval == MBX_SUCCESS) {
- wait_event_interruptible_timeout(done_q,
- pmboxq->mbox_flag & LPFC_MBX_WAKE,
- msecs_to_jiffies(timeout * 1000));
+ wait_for_completion_timeout(&mbox_done,
+ msecs_to_jiffies(timeout * 1000));
spin_lock_irqsave(&phba->hbalock, flag);
- /* restore the possible extended buffer for free resource */
- pmboxq->context1 = (uint8_t *)mb;
+ pmboxq->context3 = NULL;
/*
* if LPFC_MBX_WAKE flag is set the mailbox is completed
* else do not free the resources.
@@ -11478,11 +11696,7 @@ lpfc_sli_issue_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq,
pmboxq->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
}
spin_unlock_irqrestore(&phba->hbalock, flag);
- } else {
- /* restore the possible extended buffer for free resource */
- pmboxq->context1 = (uint8_t *)mb;
}
-
return retval;
}
@@ -11648,6 +11862,7 @@ lpfc_sli4_eratt_read(struct lpfc_hba *phba)
}
break;
case LPFC_SLI_INTF_IF_TYPE_2:
+ case LPFC_SLI_INTF_IF_TYPE_6:
if (lpfc_readl(phba->sli4_hba.u.if_type2.STATUSregaddr,
&portstat_reg.word0) ||
lpfc_readl(phba->sli4_hba.PSMPHRregaddr,
@@ -13112,7 +13327,7 @@ lpfc_sli4_sp_process_cq(struct work_struct *work)
"(x%x), type (%d)\n", cq->queue_id, cq->type);
/* In any case, flash and re-arm the RCQ */
- lpfc_sli4_cq_release(cq, LPFC_QUEUE_REARM);
+ phba->sli4_hba.sli4_cq_release(cq, LPFC_QUEUE_REARM);
/* wake up worker thread if there are works to be done */
if (workposted)
@@ -13230,6 +13445,8 @@ lpfc_sli4_fp_handle_rel_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
if (childwq->queue_id == hba_wqid) {
lpfc_sli4_wq_release(childwq,
bf_get(lpfc_wcqe_r_wqe_index, wcqe));
+ if (childwq->q_flag & HBA_NVMET_WQFULL)
+ lpfc_nvmet_wqfull_process(phba, childwq);
wqid_matched = true;
break;
}
@@ -13542,7 +13759,7 @@ lpfc_sli4_hba_process_cq(struct work_struct *work)
"queue fcpcqid=%d\n", cq->queue_id);
/* In any case, flash and re-arm the CQ */
- lpfc_sli4_cq_release(cq, LPFC_QUEUE_REARM);
+ phba->sli4_hba.sli4_cq_release(cq, LPFC_QUEUE_REARM);
/* wake up worker thread if there are works to be done */
if (workposted)
@@ -13559,7 +13776,7 @@ lpfc_sli4_eq_flush(struct lpfc_hba *phba, struct lpfc_queue *eq)
;
/* Clear and re-arm the EQ */
- lpfc_sli4_eq_release(eq, LPFC_QUEUE_REARM);
+ phba->sli4_hba.sli4_eq_release(eq, LPFC_QUEUE_REARM);
}
@@ -13707,7 +13924,7 @@ lpfc_sli4_fof_intr_handler(int irq, void *dev_id)
}
}
/* Always clear and re-arm the fast-path EQ */
- lpfc_sli4_eq_release(eq, LPFC_QUEUE_REARM);
+ phba->sli4_hba.sli4_eq_release(eq, LPFC_QUEUE_REARM);
return IRQ_HANDLED;
}
@@ -13765,7 +13982,7 @@ lpfc_sli4_hba_intr_handler(int irq, void *dev_id)
if (lpfc_fcp_look_ahead) {
if (atomic_dec_and_test(&hba_eq_hdl->hba_eq_in_use))
- lpfc_sli4_eq_clr_intr(fpeq);
+ phba->sli4_hba.sli4_eq_clr_intr(fpeq);
else {
atomic_inc(&hba_eq_hdl->hba_eq_in_use);
return IRQ_NONE;
@@ -13800,7 +14017,7 @@ lpfc_sli4_hba_intr_handler(int irq, void *dev_id)
fpeq->EQ_max_eqe = ecount;
/* Always clear and re-arm the fast-path EQ */
- lpfc_sli4_eq_release(fpeq, LPFC_QUEUE_REARM);
+ phba->sli4_hba.sli4_eq_release(fpeq, LPFC_QUEUE_REARM);
if (unlikely(ecount == 0)) {
fpeq->EQ_no_entry++;
@@ -13948,6 +14165,7 @@ lpfc_sli4_queue_alloc(struct lpfc_hba *phba, uint32_t page_size,
INIT_LIST_HEAD(&queue->list);
INIT_LIST_HEAD(&queue->wq_list);
+ INIT_LIST_HEAD(&queue->wqfull_list);
INIT_LIST_HEAD(&queue->page_list);
INIT_LIST_HEAD(&queue->child_list);
@@ -14173,11 +14391,21 @@ lpfc_eq_create(struct lpfc_hba *phba, struct lpfc_queue *eq, uint32_t imax)
LPFC_MBOX_OPCODE_EQ_CREATE,
length, LPFC_SLI4_MBX_EMBED);
eq_create = &mbox->u.mqe.un.eq_create;
+ shdr = (union lpfc_sli4_cfg_shdr *) &eq_create->header.cfg_shdr;
bf_set(lpfc_mbx_eq_create_num_pages, &eq_create->u.request,
eq->page_count);
bf_set(lpfc_eq_context_size, &eq_create->u.request.context,
LPFC_EQE_SIZE);
bf_set(lpfc_eq_context_valid, &eq_create->u.request.context, 1);
+
+ /* Use version 2 of CREATE_EQ if eqav is set */
+ if (phba->sli4_hba.pc_sli4_params.eqav) {
+ bf_set(lpfc_mbox_hdr_version, &shdr->request,
+ LPFC_Q_CREATE_VERSION_2);
+ bf_set(lpfc_eq_context_autovalid, &eq_create->u.request.context,
+ phba->sli4_hba.pc_sli4_params.eqav);
+ }
+
/* don't setup delay multiplier using EQ_CREATE */
dmult = 0;
bf_set(lpfc_eq_context_delay_multi, &eq_create->u.request.context,
@@ -14222,7 +14450,6 @@ lpfc_eq_create(struct lpfc_hba *phba, struct lpfc_queue *eq, uint32_t imax)
mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
mbox->context1 = NULL;
rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
- shdr = (union lpfc_sli4_cfg_shdr *) &eq_create->header.cfg_shdr;
shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
if (shdr_status || shdr_add_status || rc) {
@@ -14305,6 +14532,8 @@ lpfc_cq_create(struct lpfc_hba *phba, struct lpfc_queue *cq,
(cq->page_size / SLI4_PAGE_SIZE));
bf_set(lpfc_cq_eq_id_2, &cq_create->u.request.context,
eq->queue_id);
+ bf_set(lpfc_cq_context_autovalid, &cq_create->u.request.context,
+ phba->sli4_hba.pc_sli4_params.cqav);
} else {
bf_set(lpfc_cq_eq_id, &cq_create->u.request.context,
eq->queue_id);
@@ -14476,6 +14705,9 @@ lpfc_cq_create_set(struct lpfc_hba *phba, struct lpfc_queue **cqp,
&cq_set->u.request, 0);
bf_set(lpfc_mbx_cq_create_set_num_cq,
&cq_set->u.request, numcq);
+ bf_set(lpfc_mbx_cq_create_set_autovalid,
+ &cq_set->u.request,
+ phba->sli4_hba.pc_sli4_params.cqav);
switch (cq->entry_count) {
case 2048:
case 4096:
@@ -14881,6 +15113,9 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq,
void __iomem *bar_memmap_p;
uint32_t db_offset;
uint16_t pci_barset;
+ uint8_t dpp_barset;
+ uint32_t dpp_offset;
+ unsigned long pg_addr;
uint8_t wq_create_version;
/* sanity check on queue memory */
@@ -14908,43 +15143,19 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq,
bf_set(lpfc_mbox_hdr_version, &shdr->request,
phba->sli4_hba.pc_sli4_params.wqv);
+ if ((phba->sli4_hba.pc_sli4_params.wqsize & LPFC_WQ_SZ128_SUPPORT) ||
+ (wq->page_size > SLI4_PAGE_SIZE))
+ wq_create_version = LPFC_Q_CREATE_VERSION_1;
+ else
+ wq_create_version = LPFC_Q_CREATE_VERSION_0;
+
+
if (phba->sli4_hba.pc_sli4_params.wqsize & LPFC_WQ_SZ128_SUPPORT)
wq_create_version = LPFC_Q_CREATE_VERSION_1;
else
wq_create_version = LPFC_Q_CREATE_VERSION_0;
switch (wq_create_version) {
- case LPFC_Q_CREATE_VERSION_0:
- switch (wq->entry_size) {
- default:
- case 64:
- /* Nothing to do, version 0 ONLY supports 64 byte */
- page = wq_create->u.request.page;
- break;
- case 128:
- if (!(phba->sli4_hba.pc_sli4_params.wqsize &
- LPFC_WQ_SZ128_SUPPORT)) {
- status = -ERANGE;
- goto out;
- }
- /* If we get here the HBA MUST also support V1 and
- * we MUST use it
- */
- bf_set(lpfc_mbox_hdr_version, &shdr->request,
- LPFC_Q_CREATE_VERSION_1);
-
- bf_set(lpfc_mbx_wq_create_wqe_count,
- &wq_create->u.request_1, wq->entry_count);
- bf_set(lpfc_mbx_wq_create_wqe_size,
- &wq_create->u.request_1,
- LPFC_WQ_WQE_SIZE_128);
- bf_set(lpfc_mbx_wq_create_page_size,
- &wq_create->u.request_1,
- LPFC_WQ_PAGE_SIZE_4096);
- page = wq_create->u.request_1.page;
- break;
- }
- break;
case LPFC_Q_CREATE_VERSION_1:
bf_set(lpfc_mbx_wq_create_wqe_count, &wq_create->u.request_1,
wq->entry_count);
@@ -14959,24 +15170,21 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq,
LPFC_WQ_WQE_SIZE_64);
break;
case 128:
- if (!(phba->sli4_hba.pc_sli4_params.wqsize &
- LPFC_WQ_SZ128_SUPPORT)) {
- status = -ERANGE;
- goto out;
- }
bf_set(lpfc_mbx_wq_create_wqe_size,
&wq_create->u.request_1,
LPFC_WQ_WQE_SIZE_128);
break;
}
+ /* Request DPP by default */
+ bf_set(lpfc_mbx_wq_create_dpp_req, &wq_create->u.request_1, 1);
bf_set(lpfc_mbx_wq_create_page_size,
&wq_create->u.request_1,
(wq->page_size / SLI4_PAGE_SIZE));
page = wq_create->u.request_1.page;
break;
default:
- status = -ERANGE;
- goto out;
+ page = wq_create->u.request.page;
+ break;
}
list_for_each_entry(dmabuf, &wq->page_list, list) {
@@ -15000,52 +15208,120 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq,
status = -ENXIO;
goto out;
}
- wq->queue_id = bf_get(lpfc_mbx_wq_create_q_id, &wq_create->u.response);
+
+ if (wq_create_version == LPFC_Q_CREATE_VERSION_0)
+ wq->queue_id = bf_get(lpfc_mbx_wq_create_q_id,
+ &wq_create->u.response);
+ else
+ wq->queue_id = bf_get(lpfc_mbx_wq_create_v1_q_id,
+ &wq_create->u.response_1);
+
if (wq->queue_id == 0xFFFF) {
status = -ENXIO;
goto out;
}
- if (phba->sli4_hba.fw_func_mode & LPFC_DUA_MODE) {
- wq->db_format = bf_get(lpfc_mbx_wq_create_db_format,
- &wq_create->u.response);
- if ((wq->db_format != LPFC_DB_LIST_FORMAT) &&
- (wq->db_format != LPFC_DB_RING_FORMAT)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "3265 WQ[%d] doorbell format not "
- "supported: x%x\n", wq->queue_id,
- wq->db_format);
- status = -EINVAL;
- goto out;
- }
- pci_barset = bf_get(lpfc_mbx_wq_create_bar_set,
- &wq_create->u.response);
- bar_memmap_p = lpfc_dual_chute_pci_bar_map(phba, pci_barset);
- if (!bar_memmap_p) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "3263 WQ[%d] failed to memmap pci "
- "barset:x%x\n", wq->queue_id,
- pci_barset);
- status = -ENOMEM;
- goto out;
- }
- db_offset = wq_create->u.response.doorbell_offset;
- if ((db_offset != LPFC_ULP0_WQ_DOORBELL) &&
- (db_offset != LPFC_ULP1_WQ_DOORBELL)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "3252 WQ[%d] doorbell offset not "
- "supported: x%x\n", wq->queue_id,
- db_offset);
- status = -EINVAL;
- goto out;
- }
- wq->db_regaddr = bar_memmap_p + db_offset;
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "3264 WQ[%d]: barset:x%x, offset:x%x, "
- "format:x%x\n", wq->queue_id, pci_barset,
- db_offset, wq->db_format);
+
+ wq->db_format = LPFC_DB_LIST_FORMAT;
+ if (wq_create_version == LPFC_Q_CREATE_VERSION_0) {
+ if (phba->sli4_hba.fw_func_mode & LPFC_DUA_MODE) {
+ wq->db_format = bf_get(lpfc_mbx_wq_create_db_format,
+ &wq_create->u.response);
+ if ((wq->db_format != LPFC_DB_LIST_FORMAT) &&
+ (wq->db_format != LPFC_DB_RING_FORMAT)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "3265 WQ[%d] doorbell format "
+ "not supported: x%x\n",
+ wq->queue_id, wq->db_format);
+ status = -EINVAL;
+ goto out;
+ }
+ pci_barset = bf_get(lpfc_mbx_wq_create_bar_set,
+ &wq_create->u.response);
+ bar_memmap_p = lpfc_dual_chute_pci_bar_map(phba,
+ pci_barset);
+ if (!bar_memmap_p) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "3263 WQ[%d] failed to memmap "
+ "pci barset:x%x\n",
+ wq->queue_id, pci_barset);
+ status = -ENOMEM;
+ goto out;
+ }
+ db_offset = wq_create->u.response.doorbell_offset;
+ if ((db_offset != LPFC_ULP0_WQ_DOORBELL) &&
+ (db_offset != LPFC_ULP1_WQ_DOORBELL)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "3252 WQ[%d] doorbell offset "
+ "not supported: x%x\n",
+ wq->queue_id, db_offset);
+ status = -EINVAL;
+ goto out;
+ }
+ wq->db_regaddr = bar_memmap_p + db_offset;
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "3264 WQ[%d]: barset:x%x, offset:x%x, "
+ "format:x%x\n", wq->queue_id,
+ pci_barset, db_offset, wq->db_format);
+ } else
+ wq->db_regaddr = phba->sli4_hba.WQDBregaddr;
} else {
- wq->db_format = LPFC_DB_LIST_FORMAT;
- wq->db_regaddr = phba->sli4_hba.WQDBregaddr;
+ /* Check if DPP was honored by the firmware */
+ wq->dpp_enable = bf_get(lpfc_mbx_wq_create_dpp_rsp,
+ &wq_create->u.response_1);
+ if (wq->dpp_enable) {
+ pci_barset = bf_get(lpfc_mbx_wq_create_v1_bar_set,
+ &wq_create->u.response_1);
+ bar_memmap_p = lpfc_dual_chute_pci_bar_map(phba,
+ pci_barset);
+ if (!bar_memmap_p) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "3267 WQ[%d] failed to memmap "
+ "pci barset:x%x\n",
+ wq->queue_id, pci_barset);
+ status = -ENOMEM;
+ goto out;
+ }
+ db_offset = wq_create->u.response_1.doorbell_offset;
+ wq->db_regaddr = bar_memmap_p + db_offset;
+ wq->dpp_id = bf_get(lpfc_mbx_wq_create_dpp_id,
+ &wq_create->u.response_1);
+ dpp_barset = bf_get(lpfc_mbx_wq_create_dpp_bar,
+ &wq_create->u.response_1);
+ bar_memmap_p = lpfc_dual_chute_pci_bar_map(phba,
+ dpp_barset);
+ if (!bar_memmap_p) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "3268 WQ[%d] failed to memmap "
+ "pci barset:x%x\n",
+ wq->queue_id, dpp_barset);
+ status = -ENOMEM;
+ goto out;
+ }
+ dpp_offset = wq_create->u.response_1.dpp_offset;
+ wq->dpp_regaddr = bar_memmap_p + dpp_offset;
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "3271 WQ[%d]: barset:x%x, offset:x%x, "
+ "dpp_id:x%x dpp_barset:x%x "
+ "dpp_offset:x%x\n",
+ wq->queue_id, pci_barset, db_offset,
+ wq->dpp_id, dpp_barset, dpp_offset);
+
+ /* Enable combined writes for DPP aperture */
+ pg_addr = (unsigned long)(wq->dpp_regaddr) & PAGE_MASK;
+#ifdef CONFIG_X86
+ rc = set_memory_wc(pg_addr, 1);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "3272 Cannot setup Combined "
+ "Write on WQ[%d] - disable DPP\n",
+ wq->queue_id);
+ phba->cfg_enable_dpp = 0;
+ }
+#else
+ phba->cfg_enable_dpp = 0;
+#endif
+ } else
+ wq->db_regaddr = phba->sli4_hba.WQDBregaddr;
}
wq->pring = kzalloc(sizeof(struct lpfc_sli_ring), GFP_KERNEL);
if (wq->pring == NULL) {
@@ -18616,6 +18892,7 @@ lpfc_wr_object(struct lpfc_hba *phba, struct list_head *dmabuf_list,
"status x%x add_status x%x, mbx status x%x\n",
shdr_status, shdr_add_status, rc);
rc = -ENXIO;
+ *offset = shdr_add_status;
} else
*offset += wr_object->u.response.actual_write_length;
return rc;
@@ -18753,8 +19030,7 @@ lpfc_drain_txq(struct lpfc_hba *phba)
unsigned long iflags = 0;
char *fail_msg = NULL;
struct lpfc_sglq *sglq;
- union lpfc_wqe128 wqe128;
- union lpfc_wqe *wqe = (union lpfc_wqe *) &wqe128;
+ union lpfc_wqe128 wqe;
uint32_t txq_cnt = 0;
pring = lpfc_phba_elsring(phba);
@@ -18797,9 +19073,9 @@ lpfc_drain_txq(struct lpfc_hba *phba)
piocbq->sli4_xritag = sglq->sli4_xritag;
if (NO_XRI == lpfc_sli4_bpl2sgl(phba, piocbq, sglq))
fail_msg = "to convert bpl to sgl";
- else if (lpfc_sli4_iocb2wqe(phba, piocbq, wqe))
+ else if (lpfc_sli4_iocb2wqe(phba, piocbq, &wqe))
fail_msg = "to convert iocb to wqe";
- else if (lpfc_sli4_wq_put(phba->sli4_hba.els_wq, wqe))
+ else if (lpfc_sli4_wq_put(phba->sli4_hba.els_wq, &wqe))
fail_msg = " - Wq is full";
else
lpfc_sli_ringtxcmpl_put(phba, pring, piocbq);
@@ -18849,7 +19125,7 @@ lpfc_wqe_bpl2sgl(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeq,
struct ulp_bde64 bde;
struct sli4_sge *sgl = NULL;
struct lpfc_dmabuf *dmabuf;
- union lpfc_wqe *wqe;
+ union lpfc_wqe128 *wqe;
int numBdes = 0;
int i = 0;
uint32_t offset = 0; /* accumulated offset in the sg request list */
@@ -18958,7 +19234,7 @@ int
lpfc_sli4_issue_wqe(struct lpfc_hba *phba, uint32_t ring_number,
struct lpfc_iocbq *pwqe)
{
- union lpfc_wqe *wqe = &pwqe->wqe;
+ union lpfc_wqe128 *wqe = &pwqe->wqe;
struct lpfc_nvmet_rcv_ctx *ctxp;
struct lpfc_queue *wq;
struct lpfc_sglq *sglq;
diff --git a/drivers/scsi/lpfc/lpfc_sli.h b/drivers/scsi/lpfc/lpfc_sli.h
index a3b1b5145d2b..431754195505 100644
--- a/drivers/scsi/lpfc/lpfc_sli.h
+++ b/drivers/scsi/lpfc/lpfc_sli.h
@@ -61,9 +61,8 @@ struct lpfc_iocbq {
struct lpfc_wcqe_complete wcqe_cmpl; /* WQE cmpl */
uint64_t isr_timestamp;
- /* Be careful here */
- union lpfc_wqe wqe; /* WQE cmd */
- IOCB_t iocb; /* For IOCB cmd or if we want 128 byte WQE */
+ union lpfc_wqe128 wqe; /* SLI-4 */
+ IOCB_t iocb; /* SLI-3 */
uint8_t rsvd2;
uint8_t priority; /* OAS priority */
@@ -148,6 +147,7 @@ typedef struct lpfcMboxq {
struct lpfc_vport *vport;/* virtual port pointer */
void *context1; /* caller context information */
void *context2; /* caller context information */
+ void *context3;
void (*mbox_cmpl) (struct lpfc_hba *, struct lpfcMboxq *);
uint8_t mbox_flag;
diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h
index 81fb58e59e60..cf64aca82bd0 100644
--- a/drivers/scsi/lpfc/lpfc_sli4.h
+++ b/drivers/scsi/lpfc/lpfc_sli4.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Limited and/or its subsidiaries. *
* Copyright (C) 2009-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -145,6 +145,7 @@ struct lpfc_rqb {
struct lpfc_queue {
struct list_head list;
struct list_head wq_list;
+ struct list_head wqfull_list;
enum lpfc_sli4_queue_type type;
enum lpfc_sli4_queue_subtype subtype;
struct lpfc_hba *phba;
@@ -173,10 +174,16 @@ struct lpfc_queue {
#define LPFC_EXPANDED_PAGE_SIZE 16384
#define LPFC_DEFAULT_PAGE_SIZE 4096
uint16_t chann; /* IO channel this queue is associated with */
- uint16_t db_format;
+ uint8_t db_format;
#define LPFC_DB_RING_FORMAT 0x01
#define LPFC_DB_LIST_FORMAT 0x02
+ uint8_t q_flag;
+#define HBA_NVMET_WQFULL 0x1 /* We hit WQ Full condition for NVMET */
void __iomem *db_regaddr;
+ uint16_t dpp_enable;
+ uint16_t dpp_id;
+ void __iomem *dpp_regaddr;
+
/* For q stats */
uint32_t q_cnt_1;
uint32_t q_cnt_2;
@@ -209,6 +216,7 @@ struct lpfc_queue {
struct work_struct spwork;
uint64_t isr_timestamp;
+ uint8_t qe_valid;
struct lpfc_queue *assoc_qp;
union sli4_qe qe[1]; /* array to index entries (must be last) */
};
@@ -479,12 +487,19 @@ struct lpfc_pc_sli4_params {
uint8_t mqv;
uint8_t wqv;
uint8_t rqv;
+ uint8_t eqav;
+ uint8_t cqav;
uint8_t wqsize;
#define LPFC_WQ_SZ64_SUPPORT 1
#define LPFC_WQ_SZ128_SUPPORT 2
uint8_t wqpcnt;
};
+#define LPFC_CQ_4K_PAGE_SZ 0x1
+#define LPFC_CQ_16K_PAGE_SZ 0x4
+#define LPFC_WQ_4K_PAGE_SZ 0x1
+#define LPFC_WQ_16K_PAGE_SZ 0x4
+
struct lpfc_iov {
uint32_t pf_number;
uint32_t vf_number;
@@ -516,11 +531,17 @@ struct lpfc_vector_map_info {
/* SLI4 HBA data structure entries */
struct lpfc_sli4_hba {
void __iomem *conf_regs_memmap_p; /* Kernel memory mapped address for
- PCI BAR0, config space registers */
+ * config space registers
+ */
void __iomem *ctrl_regs_memmap_p; /* Kernel memory mapped address for
- PCI BAR1, control registers */
+ * control registers
+ */
void __iomem *drbl_regs_memmap_p; /* Kernel memory mapped address for
- PCI BAR2, doorbell registers */
+ * doorbell registers
+ */
+ void __iomem *dpp_regs_memmap_p; /* Kernel memory mapped address for
+ * dpp registers
+ */
union {
struct {
/* IF Type 0, BAR 0 PCI cfg space reg mem map */
@@ -561,7 +582,8 @@ struct lpfc_sli4_hba {
/* IF type 0, BAR 0 and if type 2, BAR 0 doorbell register memory map */
void __iomem *RQDBregaddr; /* RQ_DOORBELL register */
void __iomem *WQDBregaddr; /* WQ_DOORBELL register */
- void __iomem *EQCQDBregaddr; /* EQCQ_DOORBELL register */
+ void __iomem *CQDBregaddr; /* CQ_DOORBELL register */
+ void __iomem *EQDBregaddr; /* EQ_DOORBELL register */
void __iomem *MQDBregaddr; /* MQ_DOORBELL register */
void __iomem *BMBXregaddr; /* BootStrap MBX register */
@@ -574,6 +596,10 @@ struct lpfc_sli4_hba {
struct lpfc_bbscn_params bbscn_params;
struct lpfc_hba_eq_hdl *hba_eq_hdl; /* HBA per-WQ handle */
+ void (*sli4_eq_clr_intr)(struct lpfc_queue *q);
+ uint32_t (*sli4_eq_release)(struct lpfc_queue *q, bool arm);
+ uint32_t (*sli4_cq_release)(struct lpfc_queue *q, bool arm);
+
/* Pointers to the constructed SLI4 queues */
struct lpfc_queue **hba_eq; /* Event queues for HBA */
struct lpfc_queue **fcp_cq; /* Fast-path FCP compl queue */
@@ -840,8 +866,12 @@ void lpfc_sli_remove_dflt_fcf(struct lpfc_hba *);
int lpfc_sli4_get_els_iocb_cnt(struct lpfc_hba *);
int lpfc_sli4_get_iocb_cnt(struct lpfc_hba *phba);
int lpfc_sli4_init_vpi(struct lpfc_vport *);
+inline void lpfc_sli4_eq_clr_intr(struct lpfc_queue *);
uint32_t lpfc_sli4_cq_release(struct lpfc_queue *, bool);
uint32_t lpfc_sli4_eq_release(struct lpfc_queue *, bool);
+inline void lpfc_sli4_if6_eq_clr_intr(struct lpfc_queue *q);
+uint32_t lpfc_sli4_if6_cq_release(struct lpfc_queue *q, bool arm);
+uint32_t lpfc_sli4_if6_eq_release(struct lpfc_queue *q, bool arm);
void lpfc_sli4_fcfi_unreg(struct lpfc_hba *, uint16_t);
int lpfc_sli4_fcf_scan_read_fcf_rec(struct lpfc_hba *, uint16_t);
int lpfc_sli4_fcf_rr_read_fcf_rec(struct lpfc_hba *, uint16_t);
diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h
index c232bf0e8998..e8b089abbfb3 100644
--- a/drivers/scsi/lpfc/lpfc_version.h
+++ b/drivers/scsi/lpfc/lpfc_version.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2017 Broadcom. All Rights Reserved. The term *
+ * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term *
* “Broadcom” refers to Broadcom Limited and/or its subsidiaries. *
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
@@ -20,7 +20,7 @@
* included with this package. *
*******************************************************************/
-#define LPFC_DRIVER_VERSION "11.4.0.6"
+#define LPFC_DRIVER_VERSION "12.0.0.1"
#define LPFC_DRIVER_NAME "lpfc"
/* Used for SLI 2/3 */
@@ -32,6 +32,6 @@
#define LPFC_MODULE_DESC "Emulex LightPulse Fibre Channel SCSI driver " \
LPFC_DRIVER_VERSION
-#define LPFC_COPYRIGHT "Copyright (C) 2017 Broadcom. All Rights Reserved. " \
- "The term \"Broadcom\" refers to Broadcom Limited " \
+#define LPFC_COPYRIGHT "Copyright (C) 2017-2018 Broadcom. All Rights " \
+ "Reserved. The term \"Broadcom\" refers to Broadcom Limited " \
"and/or its subsidiaries."
diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h
index ba6503f37756..27fab8235ea5 100644
--- a/drivers/scsi/megaraid/megaraid_sas.h
+++ b/drivers/scsi/megaraid/megaraid_sas.h
@@ -2128,6 +2128,7 @@ enum MR_PD_TYPE {
struct megasas_instance {
+ unsigned int *reply_map;
__le32 *producer;
dma_addr_t producer_h;
__le32 *consumer;
diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c
index a71ee67df084..b89c6e6c0589 100644
--- a/drivers/scsi/megaraid/megaraid_sas_base.c
+++ b/drivers/scsi/megaraid/megaraid_sas_base.c
@@ -1864,7 +1864,7 @@ megasas_set_nvme_device_properties(struct scsi_device *sdev, u32 max_io_size)
blk_queue_max_hw_sectors(sdev->request_queue, (max_io_size / 512));
- queue_flag_set_unlocked(QUEUE_FLAG_NOMERGES, sdev->request_queue);
+ blk_queue_flag_set(QUEUE_FLAG_NOMERGES, sdev->request_queue);
blk_queue_virt_boundary(sdev->request_queue, mr_nvme_pg_size - 1);
}
@@ -4022,7 +4022,7 @@ static int megasas_create_frame_pool(struct megasas_instance *instance)
cmd = instance->cmd_list[i];
- cmd->frame = dma_pool_alloc(instance->frame_dma_pool,
+ cmd->frame = dma_pool_zalloc(instance->frame_dma_pool,
GFP_KERNEL, &cmd->frame_phys_addr);
cmd->sense = dma_pool_alloc(instance->sense_dma_pool,
@@ -4038,7 +4038,6 @@ static int megasas_create_frame_pool(struct megasas_instance *instance)
return -ENOMEM;
}
- memset(cmd->frame, 0, instance->mfi_frame_size);
cmd->frame->io.context = cpu_to_le32(cmd->index);
cmd->frame->io.pad_0 = 0;
if ((instance->adapter_type == MFI_SERIES) && reset_devices)
@@ -5165,6 +5164,26 @@ skip_alloc:
instance->use_seqnum_jbod_fp = false;
}
+static void megasas_setup_reply_map(struct megasas_instance *instance)
+{
+ const struct cpumask *mask;
+ unsigned int queue, cpu;
+
+ for (queue = 0; queue < instance->msix_vectors; queue++) {
+ mask = pci_irq_get_affinity(instance->pdev, queue);
+ if (!mask)
+ goto fallback;
+
+ for_each_cpu(cpu, mask)
+ instance->reply_map[cpu] = queue;
+ }
+ return;
+
+fallback:
+ for_each_possible_cpu(cpu)
+ instance->reply_map[cpu] = cpu % instance->msix_vectors;
+}
+
/**
* megasas_init_fw - Initializes the FW
* @instance: Adapter soft state
@@ -5343,6 +5362,8 @@ static int megasas_init_fw(struct megasas_instance *instance)
goto fail_setup_irqs;
}
+ megasas_setup_reply_map(instance);
+
dev_info(&instance->pdev->dev,
"firmware supports msix\t: (%d)", fw_msix_count);
dev_info(&instance->pdev->dev,
@@ -6123,20 +6144,29 @@ static inline int megasas_alloc_mfi_ctrl_mem(struct megasas_instance *instance)
*/
static int megasas_alloc_ctrl_mem(struct megasas_instance *instance)
{
+ instance->reply_map = kzalloc(sizeof(unsigned int) * nr_cpu_ids,
+ GFP_KERNEL);
+ if (!instance->reply_map)
+ return -ENOMEM;
+
switch (instance->adapter_type) {
case MFI_SERIES:
if (megasas_alloc_mfi_ctrl_mem(instance))
- return -ENOMEM;
+ goto fail;
break;
case VENTURA_SERIES:
case THUNDERBOLT_SERIES:
case INVADER_SERIES:
if (megasas_alloc_fusion_context(instance))
- return -ENOMEM;
+ goto fail;
break;
}
return 0;
+ fail:
+ kfree(instance->reply_map);
+ instance->reply_map = NULL;
+ return -ENOMEM;
}
/*
@@ -6148,6 +6178,7 @@ static int megasas_alloc_ctrl_mem(struct megasas_instance *instance)
*/
static inline void megasas_free_ctrl_mem(struct megasas_instance *instance)
{
+ kfree(instance->reply_map);
if (instance->adapter_type == MFI_SERIES) {
if (instance->producer)
pci_free_consistent(instance->pdev, sizeof(u32),
@@ -6540,7 +6571,6 @@ fail_io_attach:
pci_free_irq_vectors(instance->pdev);
fail_init_mfi:
scsi_host_put(host);
-
fail_alloc_instance:
pci_disable_device(pdev);
@@ -6746,6 +6776,8 @@ megasas_resume(struct pci_dev *pdev)
if (rval < 0)
goto fail_reenable_msix;
+ megasas_setup_reply_map(instance);
+
if (instance->adapter_type != MFI_SERIES) {
megasas_reset_reply_desc(instance);
if (megasas_ioc_init_fusion(instance)) {
diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c
index dc8e850fbfd2..f4d988dd1e9d 100644
--- a/drivers/scsi/megaraid/megaraid_sas_fusion.c
+++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c
@@ -1124,12 +1124,12 @@ megasas_ioc_init_fusion(struct megasas_instance *instance)
goto fail_fw_init;
}
- ret = 0;
+ return 0;
fail_fw_init:
dev_err(&instance->pdev->dev,
- "Init cmd return status %s for SCSI host %d\n",
- ret ? "FAILED" : "SUCCESS", instance->host->host_no);
+ "Init cmd return status FAILED for SCSI host %d\n",
+ instance->host->host_no);
return ret;
}
@@ -1894,7 +1894,7 @@ megasas_is_prp_possible(struct megasas_instance *instance,
* then sending IOs with holes.
*
* Though driver can request block layer to disable IO merging by calling-
- * queue_flag_set_unlocked(QUEUE_FLAG_NOMERGES, sdev->request_queue) but
+ * blk_queue_flag_set(QUEUE_FLAG_NOMERGES, sdev->request_queue) but
* user may tune sysfs parameter- nomerges again to 0 or 1.
*
* If in future IO scheduling is enabled with SCSI BLK MQ,
@@ -2641,11 +2641,8 @@ megasas_build_ldio_fusion(struct megasas_instance *instance,
fp_possible = (io_info.fpOkForIo > 0) ? true : false;
}
- /* Use raw_smp_processor_id() for now until cmd->request->cpu is CPU
- id by default, not CPU group id, otherwise all MSI-X queues won't
- be utilized */
- cmd->request_desc->SCSIIO.MSIxIndex = instance->msix_vectors ?
- raw_smp_processor_id() % instance->msix_vectors : 0;
+ cmd->request_desc->SCSIIO.MSIxIndex =
+ instance->reply_map[raw_smp_processor_id()];
praid_context = &io_request->RaidContext;
@@ -2971,10 +2968,9 @@ megasas_build_syspd_fusion(struct megasas_instance *instance,
}
cmd->request_desc->SCSIIO.DevHandle = io_request->DevHandle;
- cmd->request_desc->SCSIIO.MSIxIndex =
- instance->msix_vectors ?
- (raw_smp_processor_id() % instance->msix_vectors) : 0;
+ cmd->request_desc->SCSIIO.MSIxIndex =
+ instance->reply_map[raw_smp_processor_id()];
if (!fp_possible) {
/* system pd firmware path */
diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h b/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h
index ee117106d0f7..0ad88deb3176 100644
--- a/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h
+++ b/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h
@@ -524,6 +524,7 @@ typedef struct _MPI2_CONFIG_REPLY {
#define MPI2_MFGPAGE_DEVID_SAS2308_1 (0x0086)
#define MPI2_MFGPAGE_DEVID_SAS2308_2 (0x0087)
#define MPI2_MFGPAGE_DEVID_SAS2308_3 (0x006E)
+#define MPI2_MFGPAGE_DEVID_SAS2308_MPI_EP (0x02B0)
/*MPI v2.5 SAS products */
#define MPI25_MFGPAGE_DEVID_SAS3004 (0x0096)
diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c
index 0aafbfd1b746..61f93a134956 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_base.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_base.c
@@ -126,6 +126,362 @@ module_param_call(mpt3sas_fwfault_debug, _scsih_set_fwfault_debug,
param_get_int, &mpt3sas_fwfault_debug, 0644);
/**
+ * _base_clone_reply_to_sys_mem - copies reply to reply free iomem
+ * in BAR0 space.
+ *
+ * @ioc: per adapter object
+ * @reply: reply message frame(lower 32bit addr)
+ * @index: System request message index.
+ *
+ * @Returns - Nothing
+ */
+static void
+_base_clone_reply_to_sys_mem(struct MPT3SAS_ADAPTER *ioc, u32 reply,
+ u32 index)
+{
+ /*
+ * 256 is offset within sys register.
+ * 256 offset MPI frame starts. Max MPI frame supported is 32.
+ * 32 * 128 = 4K. From here, Clone of reply free for mcpu starts
+ */
+ u16 cmd_credit = ioc->facts.RequestCredit + 1;
+ void __iomem *reply_free_iomem = (void __iomem *)ioc->chip +
+ MPI_FRAME_START_OFFSET +
+ (cmd_credit * ioc->request_sz) + (index * sizeof(u32));
+
+ writel(reply, reply_free_iomem);
+}
+
+/**
+ * _base_clone_mpi_to_sys_mem - Writes/copies MPI frames
+ * to system/BAR0 region.
+ *
+ * @dst_iomem: Pointer to the destinaltion location in BAR0 space.
+ * @src: Pointer to the Source data.
+ * @size: Size of data to be copied.
+ */
+static void
+_base_clone_mpi_to_sys_mem(void *dst_iomem, void *src, u32 size)
+{
+ int i;
+ u32 *src_virt_mem = (u32 *)src;
+
+ for (i = 0; i < size/4; i++)
+ writel((u32)src_virt_mem[i],
+ (void __iomem *)dst_iomem + (i * 4));
+}
+
+/**
+ * _base_clone_to_sys_mem - Writes/copies data to system/BAR0 region
+ *
+ * @dst_iomem: Pointer to the destination location in BAR0 space.
+ * @src: Pointer to the Source data.
+ * @size: Size of data to be copied.
+ */
+static void
+_base_clone_to_sys_mem(void __iomem *dst_iomem, void *src, u32 size)
+{
+ int i;
+ u32 *src_virt_mem = (u32 *)(src);
+
+ for (i = 0; i < size/4; i++)
+ writel((u32)src_virt_mem[i],
+ (void __iomem *)dst_iomem + (i * 4));
+}
+
+/**
+ * _base_get_chain - Calculates and Returns virtual chain address
+ * for the provided smid in BAR0 space.
+ *
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @sge_chain_count: Scatter gather chain count.
+ *
+ * @Return: chain address.
+ */
+static inline void __iomem*
+_base_get_chain(struct MPT3SAS_ADAPTER *ioc, u16 smid,
+ u8 sge_chain_count)
+{
+ void __iomem *base_chain, *chain_virt;
+ u16 cmd_credit = ioc->facts.RequestCredit + 1;
+
+ base_chain = (void __iomem *)ioc->chip + MPI_FRAME_START_OFFSET +
+ (cmd_credit * ioc->request_sz) +
+ REPLY_FREE_POOL_SIZE;
+ chain_virt = base_chain + (smid * ioc->facts.MaxChainDepth *
+ ioc->request_sz) + (sge_chain_count * ioc->request_sz);
+ return chain_virt;
+}
+
+/**
+ * _base_get_chain_phys - Calculates and Returns physical address
+ * in BAR0 for scatter gather chains, for
+ * the provided smid.
+ *
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @sge_chain_count: Scatter gather chain count.
+ *
+ * @Return - Physical chain address.
+ */
+static inline phys_addr_t
+_base_get_chain_phys(struct MPT3SAS_ADAPTER *ioc, u16 smid,
+ u8 sge_chain_count)
+{
+ phys_addr_t base_chain_phys, chain_phys;
+ u16 cmd_credit = ioc->facts.RequestCredit + 1;
+
+ base_chain_phys = ioc->chip_phys + MPI_FRAME_START_OFFSET +
+ (cmd_credit * ioc->request_sz) +
+ REPLY_FREE_POOL_SIZE;
+ chain_phys = base_chain_phys + (smid * ioc->facts.MaxChainDepth *
+ ioc->request_sz) + (sge_chain_count * ioc->request_sz);
+ return chain_phys;
+}
+
+/**
+ * _base_get_buffer_bar0 - Calculates and Returns BAR0 mapped Host
+ * buffer address for the provided smid.
+ * (Each smid can have 64K starts from 17024)
+ *
+ * @ioc: per adapter object
+ * @smid: system request message index
+ *
+ * @Returns - Pointer to buffer location in BAR0.
+ */
+
+static void __iomem *
+_base_get_buffer_bar0(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+{
+ u16 cmd_credit = ioc->facts.RequestCredit + 1;
+ // Added extra 1 to reach end of chain.
+ void __iomem *chain_end = _base_get_chain(ioc,
+ cmd_credit + 1,
+ ioc->facts.MaxChainDepth);
+ return chain_end + (smid * 64 * 1024);
+}
+
+/**
+ * _base_get_buffer_phys_bar0 - Calculates and Returns BAR0 mapped
+ * Host buffer Physical address for the provided smid.
+ * (Each smid can have 64K starts from 17024)
+ *
+ * @ioc: per adapter object
+ * @smid: system request message index
+ *
+ * @Returns - Pointer to buffer location in BAR0.
+ */
+static phys_addr_t
+_base_get_buffer_phys_bar0(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+{
+ u16 cmd_credit = ioc->facts.RequestCredit + 1;
+ phys_addr_t chain_end_phys = _base_get_chain_phys(ioc,
+ cmd_credit + 1,
+ ioc->facts.MaxChainDepth);
+ return chain_end_phys + (smid * 64 * 1024);
+}
+
+/**
+ * _base_get_chain_buffer_dma_to_chain_buffer - Iterates chain
+ * lookup list and Provides chain_buffer
+ * address for the matching dma address.
+ * (Each smid can have 64K starts from 17024)
+ *
+ * @ioc: per adapter object
+ * @chain_buffer_dma: Chain buffer dma address.
+ *
+ * @Returns - Pointer to chain buffer. Or Null on Failure.
+ */
+static void *
+_base_get_chain_buffer_dma_to_chain_buffer(struct MPT3SAS_ADAPTER *ioc,
+ dma_addr_t chain_buffer_dma)
+{
+ u16 index;
+
+ for (index = 0; index < ioc->chain_depth; index++) {
+ if (ioc->chain_lookup[index].chain_buffer_dma ==
+ chain_buffer_dma)
+ return ioc->chain_lookup[index].chain_buffer;
+ }
+ pr_info(MPT3SAS_FMT
+ "Provided chain_buffer_dma address is not in the lookup list\n",
+ ioc->name);
+ return NULL;
+}
+
+/**
+ * _clone_sg_entries - MPI EP's scsiio and config requests
+ * are handled here. Base function for
+ * double buffering, before submitting
+ * the requests.
+ *
+ * @ioc: per adapter object.
+ * @mpi_request: mf request pointer.
+ * @smid: system request message index.
+ *
+ * @Returns: Nothing.
+ */
+static void _clone_sg_entries(struct MPT3SAS_ADAPTER *ioc,
+ void *mpi_request, u16 smid)
+{
+ Mpi2SGESimple32_t *sgel, *sgel_next;
+ u32 sgl_flags, sge_chain_count = 0;
+ bool is_write = 0;
+ u16 i = 0;
+ void __iomem *buffer_iomem;
+ phys_addr_t buffer_iomem_phys;
+ void __iomem *buff_ptr;
+ phys_addr_t buff_ptr_phys;
+ void __iomem *dst_chain_addr[MCPU_MAX_CHAINS_PER_IO];
+ void *src_chain_addr[MCPU_MAX_CHAINS_PER_IO];
+ phys_addr_t dst_addr_phys;
+ MPI2RequestHeader_t *request_hdr;
+ struct scsi_cmnd *scmd;
+ struct scatterlist *sg_scmd = NULL;
+ int is_scsiio_req = 0;
+
+ request_hdr = (MPI2RequestHeader_t *) mpi_request;
+
+ if (request_hdr->Function == MPI2_FUNCTION_SCSI_IO_REQUEST) {
+ Mpi25SCSIIORequest_t *scsiio_request =
+ (Mpi25SCSIIORequest_t *)mpi_request;
+ sgel = (Mpi2SGESimple32_t *) &scsiio_request->SGL;
+ is_scsiio_req = 1;
+ } else if (request_hdr->Function == MPI2_FUNCTION_CONFIG) {
+ Mpi2ConfigRequest_t *config_req =
+ (Mpi2ConfigRequest_t *)mpi_request;
+ sgel = (Mpi2SGESimple32_t *) &config_req->PageBufferSGE;
+ } else
+ return;
+
+ /* From smid we can get scsi_cmd, once we have sg_scmd,
+ * we just need to get sg_virt and sg_next to get virual
+ * address associated with sgel->Address.
+ */
+
+ if (is_scsiio_req) {
+ /* Get scsi_cmd using smid */
+ scmd = mpt3sas_scsih_scsi_lookup_get(ioc, smid);
+ if (scmd == NULL) {
+ pr_err(MPT3SAS_FMT "scmd is NULL\n", ioc->name);
+ return;
+ }
+
+ /* Get sg_scmd from scmd provided */
+ sg_scmd = scsi_sglist(scmd);
+ }
+
+ /*
+ * 0 - 255 System register
+ * 256 - 4352 MPI Frame. (This is based on maxCredit 32)
+ * 4352 - 4864 Reply_free pool (512 byte is reserved
+ * considering maxCredit 32. Reply need extra
+ * room, for mCPU case kept four times of
+ * maxCredit).
+ * 4864 - 17152 SGE chain element. (32cmd * 3 chain of
+ * 128 byte size = 12288)
+ * 17152 - x Host buffer mapped with smid.
+ * (Each smid can have 64K Max IO.)
+ * BAR0+Last 1K MSIX Addr and Data
+ * Total size in use 2113664 bytes of 4MB BAR0
+ */
+
+ buffer_iomem = _base_get_buffer_bar0(ioc, smid);
+ buffer_iomem_phys = _base_get_buffer_phys_bar0(ioc, smid);
+
+ buff_ptr = buffer_iomem;
+ buff_ptr_phys = buffer_iomem_phys;
+ WARN_ON(buff_ptr_phys > U32_MAX);
+
+ if (sgel->FlagsLength &
+ (MPI2_SGE_FLAGS_HOST_TO_IOC << MPI2_SGE_FLAGS_SHIFT))
+ is_write = 1;
+
+ for (i = 0; i < MPT_MIN_PHYS_SEGMENTS + ioc->facts.MaxChainDepth; i++) {
+
+ sgl_flags = (sgel->FlagsLength >> MPI2_SGE_FLAGS_SHIFT);
+
+ switch (sgl_flags & MPI2_SGE_FLAGS_ELEMENT_MASK) {
+ case MPI2_SGE_FLAGS_CHAIN_ELEMENT:
+ /*
+ * Helper function which on passing
+ * chain_buffer_dma returns chain_buffer. Get
+ * the virtual address for sgel->Address
+ */
+ sgel_next =
+ _base_get_chain_buffer_dma_to_chain_buffer(ioc,
+ sgel->Address);
+ if (sgel_next == NULL)
+ return;
+ /*
+ * This is coping 128 byte chain
+ * frame (not a host buffer)
+ */
+ dst_chain_addr[sge_chain_count] =
+ _base_get_chain(ioc,
+ smid, sge_chain_count);
+ src_chain_addr[sge_chain_count] =
+ (void *) sgel_next;
+ dst_addr_phys = _base_get_chain_phys(ioc,
+ smid, sge_chain_count);
+ WARN_ON(dst_addr_phys > U32_MAX);
+ sgel->Address = (u32)dst_addr_phys;
+ sgel = sgel_next;
+ sge_chain_count++;
+ break;
+ case MPI2_SGE_FLAGS_SIMPLE_ELEMENT:
+ if (is_write) {
+ if (is_scsiio_req) {
+ _base_clone_to_sys_mem(buff_ptr,
+ sg_virt(sg_scmd),
+ (sgel->FlagsLength & 0x00ffffff));
+ /*
+ * FIXME: this relies on a a zero
+ * PCI mem_offset.
+ */
+ sgel->Address = (u32)buff_ptr_phys;
+ } else {
+ _base_clone_to_sys_mem(buff_ptr,
+ ioc->config_vaddr,
+ (sgel->FlagsLength & 0x00ffffff));
+ sgel->Address = (u32)buff_ptr_phys;
+ }
+ }
+ buff_ptr += (sgel->FlagsLength & 0x00ffffff);
+ buff_ptr_phys += (sgel->FlagsLength & 0x00ffffff);
+ if ((sgel->FlagsLength &
+ (MPI2_SGE_FLAGS_END_OF_BUFFER
+ << MPI2_SGE_FLAGS_SHIFT)))
+ goto eob_clone_chain;
+ else {
+ /*
+ * Every single element in MPT will have
+ * associated sg_next. Better to sanity that
+ * sg_next is not NULL, but it will be a bug
+ * if it is null.
+ */
+ if (is_scsiio_req) {
+ sg_scmd = sg_next(sg_scmd);
+ if (sg_scmd)
+ sgel++;
+ else
+ goto eob_clone_chain;
+ }
+ }
+ break;
+ }
+ }
+
+eob_clone_chain:
+ for (i = 0; i < sge_chain_count; i++) {
+ if (is_scsiio_req)
+ _base_clone_to_sys_mem(dst_chain_addr[i],
+ src_chain_addr[i], ioc->request_sz);
+ }
+}
+
+/**
* mpt3sas_remove_dead_ioc_func - kthread context to remove dead ioc
* @arg: input argument, used to derive ioc
*
@@ -875,7 +1231,7 @@ _base_async_event(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, u32 reply)
ack_request->EventContext = mpi_reply->EventContext;
ack_request->VF_ID = 0; /* TODO */
ack_request->VP_ID = 0;
- ioc->put_smid_default(ioc, smid);
+ mpt3sas_base_put_smid_default(ioc, smid);
out:
@@ -1075,6 +1431,10 @@ _base_interrupt(int irq, void *bus_id)
0 : ioc->reply_free_host_index + 1;
ioc->reply_free[ioc->reply_free_host_index] =
cpu_to_le32(reply);
+ if (ioc->is_mcpu_endpoint)
+ _base_clone_reply_to_sys_mem(ioc,
+ cpu_to_le32(reply),
+ ioc->reply_free_host_index);
writel(ioc->reply_free_host_index,
&ioc->chip->ReplyFreeHostIndex);
}
@@ -2214,6 +2574,9 @@ _base_config_dma_addressing(struct MPT3SAS_ADAPTER *ioc, struct pci_dev *pdev)
struct sysinfo s;
u64 consistent_dma_mask;
+ if (ioc->is_mcpu_endpoint)
+ goto try_32bit;
+
if (ioc->dma_mask)
consistent_dma_mask = DMA_BIT_MASK(64);
else
@@ -2232,6 +2595,7 @@ _base_config_dma_addressing(struct MPT3SAS_ADAPTER *ioc, struct pci_dev *pdev)
}
}
+ try_32bit:
if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))
&& !pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) {
ioc->base_add_sg_single = &_base_add_sg_single_32;
@@ -2581,7 +2945,7 @@ mpt3sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc)
u32 pio_sz;
int i, r = 0;
u64 pio_chip = 0;
- u64 chip_phys = 0;
+ phys_addr_t chip_phys = 0;
struct adapter_reply_queue *reply_q;
dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n",
@@ -2629,7 +2993,7 @@ mpt3sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc)
if (memap_sz)
continue;
ioc->chip_phys = pci_resource_start(pdev, i);
- chip_phys = (u64)ioc->chip_phys;
+ chip_phys = ioc->chip_phys;
memap_sz = pci_resource_len(pdev, i);
ioc->chip = ioremap(ioc->chip_phys, memap_sz);
}
@@ -2704,8 +3068,8 @@ mpt3sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc)
"IO-APIC enabled"),
pci_irq_vector(ioc->pdev, reply_q->msix_index));
- pr_info(MPT3SAS_FMT "iomem(0x%016llx), mapped(0x%p), size(%d)\n",
- ioc->name, (unsigned long long)chip_phys, ioc->chip, memap_sz);
+ pr_info(MPT3SAS_FMT "iomem(%pap), mapped(0x%p), size(%d)\n",
+ ioc->name, &chip_phys, ioc->chip, memap_sz);
pr_info(MPT3SAS_FMT "ioport(0x%016llx), size(%d)\n",
ioc->name, (unsigned long long)pio_chip, pio_sz);
@@ -2961,6 +3325,29 @@ mpt3sas_base_free_smid(struct MPT3SAS_ADAPTER *ioc, u16 smid)
}
/**
+ * _base_mpi_ep_writeq - 32 bit write to MMIO
+ * @b: data payload
+ * @addr: address in MMIO space
+ * @writeq_lock: spin lock
+ *
+ * This special handling for MPI EP to take care of 32 bit
+ * environment where its not quarenteed to send the entire word
+ * in one transfer.
+ */
+static inline void
+_base_mpi_ep_writeq(__u64 b, volatile void __iomem *addr,
+ spinlock_t *writeq_lock)
+{
+ unsigned long flags;
+ __u64 data_out = cpu_to_le64(b);
+
+ spin_lock_irqsave(writeq_lock, flags);
+ writel((u32)(data_out), addr);
+ writel((u32)(data_out >> 32), (addr + 4));
+ spin_unlock_irqrestore(writeq_lock, flags);
+}
+
+/**
* _base_writeq - 64 bit write to MMIO
* @ioc: per adapter object
* @b: data payload
@@ -2981,17 +3368,41 @@ _base_writeq(__u64 b, volatile void __iomem *addr, spinlock_t *writeq_lock)
static inline void
_base_writeq(__u64 b, volatile void __iomem *addr, spinlock_t *writeq_lock)
{
- unsigned long flags;
- __u64 data_out = cpu_to_le64(b);
-
- spin_lock_irqsave(writeq_lock, flags);
- writel((u32)(data_out), addr);
- writel((u32)(data_out >> 32), (addr + 4));
- spin_unlock_irqrestore(writeq_lock, flags);
+ _base_mpi_ep_writeq(b, addr, writeq_lock);
}
#endif
/**
+ * _base_put_smid_mpi_ep_scsi_io - send SCSI_IO request to firmware
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @handle: device handle
+ *
+ * Return nothing.
+ */
+static void
+_base_put_smid_mpi_ep_scsi_io(struct MPT3SAS_ADAPTER *ioc, u16 smid, u16 handle)
+{
+ Mpi2RequestDescriptorUnion_t descriptor;
+ u64 *request = (u64 *)&descriptor;
+ void *mpi_req_iomem;
+ __le32 *mfp = (__le32 *)mpt3sas_base_get_msg_frame(ioc, smid);
+
+ _clone_sg_entries(ioc, (void *) mfp, smid);
+ mpi_req_iomem = (void *)ioc->chip +
+ MPI_FRAME_START_OFFSET + (smid * ioc->request_sz);
+ _base_clone_mpi_to_sys_mem(mpi_req_iomem, (void *)mfp,
+ ioc->request_sz);
+ descriptor.SCSIIO.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO;
+ descriptor.SCSIIO.MSIxIndex = _base_get_msix_index(ioc);
+ descriptor.SCSIIO.SMID = cpu_to_le16(smid);
+ descriptor.SCSIIO.DevHandle = cpu_to_le16(handle);
+ descriptor.SCSIIO.LMID = 0;
+ _base_mpi_ep_writeq(*request, &ioc->chip->RequestDescriptorPostLow,
+ &ioc->scsi_lookup_lock);
+}
+
+/**
* _base_put_smid_scsi_io - send SCSI_IO request to firmware
* @ioc: per adapter object
* @smid: system request message index
@@ -3016,15 +3427,15 @@ _base_put_smid_scsi_io(struct MPT3SAS_ADAPTER *ioc, u16 smid, u16 handle)
}
/**
- * _base_put_smid_fast_path - send fast path request to firmware
+ * mpt3sas_base_put_smid_fast_path - send fast path request to firmware
* @ioc: per adapter object
* @smid: system request message index
* @handle: device handle
*
* Return nothing.
*/
-static void
-_base_put_smid_fast_path(struct MPT3SAS_ADAPTER *ioc, u16 smid,
+void
+mpt3sas_base_put_smid_fast_path(struct MPT3SAS_ADAPTER *ioc, u16 smid,
u16 handle)
{
Mpi2RequestDescriptorUnion_t descriptor;
@@ -3041,18 +3452,34 @@ _base_put_smid_fast_path(struct MPT3SAS_ADAPTER *ioc, u16 smid,
}
/**
- * _base_put_smid_hi_priority - send Task Management request to firmware
+ * mpt3sas_base_put_smid_hi_priority - send Task Management request to firmware
* @ioc: per adapter object
* @smid: system request message index
* @msix_task: msix_task will be same as msix of IO incase of task abort else 0.
* Return nothing.
*/
-static void
-_base_put_smid_hi_priority(struct MPT3SAS_ADAPTER *ioc, u16 smid,
+void
+mpt3sas_base_put_smid_hi_priority(struct MPT3SAS_ADAPTER *ioc, u16 smid,
u16 msix_task)
{
Mpi2RequestDescriptorUnion_t descriptor;
- u64 *request = (u64 *)&descriptor;
+ void *mpi_req_iomem;
+ u64 *request;
+
+ if (ioc->is_mcpu_endpoint) {
+ MPI2RequestHeader_t *request_hdr;
+
+ __le32 *mfp = (__le32 *)mpt3sas_base_get_msg_frame(ioc, smid);
+
+ request_hdr = (MPI2RequestHeader_t *)mfp;
+ /* TBD 256 is offset within sys register. */
+ mpi_req_iomem = (void *)ioc->chip + MPI_FRAME_START_OFFSET
+ + (smid * ioc->request_sz);
+ _base_clone_mpi_to_sys_mem(mpi_req_iomem, (void *)mfp,
+ ioc->request_sz);
+ }
+
+ request = (u64 *)&descriptor;
descriptor.HighPriority.RequestFlags =
MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY;
@@ -3060,20 +3487,25 @@ _base_put_smid_hi_priority(struct MPT3SAS_ADAPTER *ioc, u16 smid,
descriptor.HighPriority.SMID = cpu_to_le16(smid);
descriptor.HighPriority.LMID = 0;
descriptor.HighPriority.Reserved1 = 0;
- _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow,
- &ioc->scsi_lookup_lock);
+ if (ioc->is_mcpu_endpoint)
+ _base_mpi_ep_writeq(*request,
+ &ioc->chip->RequestDescriptorPostLow,
+ &ioc->scsi_lookup_lock);
+ else
+ _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow,
+ &ioc->scsi_lookup_lock);
}
/**
- * _base_put_smid_nvme_encap - send NVMe encapsulated request to
+ * mpt3sas_base_put_smid_nvme_encap - send NVMe encapsulated request to
* firmware
* @ioc: per adapter object
* @smid: system request message index
*
* Return nothing.
*/
-static void
-_base_put_smid_nvme_encap(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+void
+mpt3sas_base_put_smid_nvme_encap(struct MPT3SAS_ADAPTER *ioc, u16 smid)
{
Mpi2RequestDescriptorUnion_t descriptor;
u64 *request = (u64 *)&descriptor;
@@ -3089,135 +3521,45 @@ _base_put_smid_nvme_encap(struct MPT3SAS_ADAPTER *ioc, u16 smid)
}
/**
- * _base_put_smid_default - Default, primarily used for config pages
+ * mpt3sas_base_put_smid_default - Default, primarily used for config pages
* @ioc: per adapter object
* @smid: system request message index
*
* Return nothing.
*/
-static void
-_base_put_smid_default(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+void
+mpt3sas_base_put_smid_default(struct MPT3SAS_ADAPTER *ioc, u16 smid)
{
Mpi2RequestDescriptorUnion_t descriptor;
- u64 *request = (u64 *)&descriptor;
+ void *mpi_req_iomem;
+ u64 *request;
+ MPI2RequestHeader_t *request_hdr;
+
+ if (ioc->is_mcpu_endpoint) {
+ __le32 *mfp = (__le32 *)mpt3sas_base_get_msg_frame(ioc, smid);
+ request_hdr = (MPI2RequestHeader_t *)mfp;
+
+ _clone_sg_entries(ioc, (void *) mfp, smid);
+ /* TBD 256 is offset within sys register */
+ mpi_req_iomem = (void *)ioc->chip +
+ MPI_FRAME_START_OFFSET + (smid * ioc->request_sz);
+ _base_clone_mpi_to_sys_mem(mpi_req_iomem, (void *)mfp,
+ ioc->request_sz);
+ }
+ request = (u64 *)&descriptor;
descriptor.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
descriptor.Default.MSIxIndex = _base_get_msix_index(ioc);
descriptor.Default.SMID = cpu_to_le16(smid);
descriptor.Default.LMID = 0;
descriptor.Default.DescriptorTypeDependent = 0;
- _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow,
- &ioc->scsi_lookup_lock);
-}
-
-/**
-* _base_put_smid_scsi_io_atomic - send SCSI_IO request to firmware using
-* Atomic Request Descriptor
-* @ioc: per adapter object
-* @smid: system request message index
-* @handle: device handle, unused in this function, for function type match
-*
-* Return nothing.
-*/
-static void
-_base_put_smid_scsi_io_atomic(struct MPT3SAS_ADAPTER *ioc, u16 smid,
- u16 handle)
-{
- Mpi26AtomicRequestDescriptor_t descriptor;
- u32 *request = (u32 *)&descriptor;
-
- descriptor.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO;
- descriptor.MSIxIndex = _base_get_msix_index(ioc);
- descriptor.SMID = cpu_to_le16(smid);
-
- writel(cpu_to_le32(*request), &ioc->chip->AtomicRequestDescriptorPost);
-}
-
-/**
- * _base_put_smid_fast_path_atomic - send fast path request to firmware
- * using Atomic Request Descriptor
- * @ioc: per adapter object
- * @smid: system request message index
- * @handle: device handle, unused in this function, for function type match
- * Return nothing
- */
-static void
-_base_put_smid_fast_path_atomic(struct MPT3SAS_ADAPTER *ioc, u16 smid,
- u16 handle)
-{
- Mpi26AtomicRequestDescriptor_t descriptor;
- u32 *request = (u32 *)&descriptor;
-
- descriptor.RequestFlags = MPI25_REQ_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO;
- descriptor.MSIxIndex = _base_get_msix_index(ioc);
- descriptor.SMID = cpu_to_le16(smid);
-
- writel(cpu_to_le32(*request), &ioc->chip->AtomicRequestDescriptorPost);
-}
-
-/**
- * _base_put_smid_hi_priority_atomic - send Task Management request to
- * firmware using Atomic Request Descriptor
- * @ioc: per adapter object
- * @smid: system request message index
- * @msix_task: msix_task will be same as msix of IO incase of task abort else 0
- *
- * Return nothing.
- */
-static void
-_base_put_smid_hi_priority_atomic(struct MPT3SAS_ADAPTER *ioc, u16 smid,
- u16 msix_task)
-{
- Mpi26AtomicRequestDescriptor_t descriptor;
- u32 *request = (u32 *)&descriptor;
-
- descriptor.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY;
- descriptor.MSIxIndex = msix_task;
- descriptor.SMID = cpu_to_le16(smid);
-
- writel(cpu_to_le32(*request), &ioc->chip->AtomicRequestDescriptorPost);
-}
-
-/**
- * _base_put_smid_nvme_encap_atomic - send NVMe encapsulated request to
- * firmware using Atomic Request Descriptor
- * @ioc: per adapter object
- * @smid: system request message index
- *
- * Return nothing.
- */
-static void
-_base_put_smid_nvme_encap_atomic(struct MPT3SAS_ADAPTER *ioc, u16 smid)
-{
- Mpi26AtomicRequestDescriptor_t descriptor;
- u32 *request = (u32 *)&descriptor;
-
- descriptor.RequestFlags = MPI26_REQ_DESCRIPT_FLAGS_PCIE_ENCAPSULATED;
- descriptor.MSIxIndex = _base_get_msix_index(ioc);
- descriptor.SMID = cpu_to_le16(smid);
-
- writel(cpu_to_le32(*request), &ioc->chip->AtomicRequestDescriptorPost);
-}
-
-/**
- * _base_put_smid_default - Default, primarily used for config pages
- * use Atomic Request Descriptor
- * @ioc: per adapter object
- * @smid: system request message index
- *
- * Return nothing.
- */
-static void
-_base_put_smid_default_atomic(struct MPT3SAS_ADAPTER *ioc, u16 smid)
-{
- Mpi26AtomicRequestDescriptor_t descriptor;
- u32 *request = (u32 *)&descriptor;
-
- descriptor.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
- descriptor.MSIxIndex = _base_get_msix_index(ioc);
- descriptor.SMID = cpu_to_le16(smid);
-
- writel(cpu_to_le32(*request), &ioc->chip->AtomicRequestDescriptorPost);
+ if (ioc->is_mcpu_endpoint)
+ _base_mpi_ep_writeq(*request,
+ &ioc->chip->RequestDescriptorPostLow,
+ &ioc->scsi_lookup_lock);
+ else
+ _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow,
+ &ioc->scsi_lookup_lock);
}
/**
@@ -3890,17 +4232,21 @@ _base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc)
sg_tablesize = min_t(unsigned short, sg_tablesize,
MPT_KDUMP_MIN_PHYS_SEGMENTS);
- if (sg_tablesize < MPT_MIN_PHYS_SEGMENTS)
- sg_tablesize = MPT_MIN_PHYS_SEGMENTS;
- else if (sg_tablesize > MPT_MAX_PHYS_SEGMENTS) {
- sg_tablesize = min_t(unsigned short, sg_tablesize,
- SG_MAX_SEGMENTS);
- pr_warn(MPT3SAS_FMT
- "sg_tablesize(%u) is bigger than kernel"
- " defined SG_CHUNK_SIZE(%u)\n", ioc->name,
- sg_tablesize, MPT_MAX_PHYS_SEGMENTS);
+ if (ioc->is_mcpu_endpoint)
+ ioc->shost->sg_tablesize = MPT_MIN_PHYS_SEGMENTS;
+ else {
+ if (sg_tablesize < MPT_MIN_PHYS_SEGMENTS)
+ sg_tablesize = MPT_MIN_PHYS_SEGMENTS;
+ else if (sg_tablesize > MPT_MAX_PHYS_SEGMENTS) {
+ sg_tablesize = min_t(unsigned short, sg_tablesize,
+ SG_MAX_SEGMENTS);
+ pr_warn(MPT3SAS_FMT
+ "sg_tablesize(%u) is bigger than kernel "
+ "defined SG_CHUNK_SIZE(%u)\n", ioc->name,
+ sg_tablesize, MPT_MAX_PHYS_SEGMENTS);
+ }
+ ioc->shost->sg_tablesize = sg_tablesize;
}
- ioc->shost->sg_tablesize = sg_tablesize;
ioc->internal_depth = min_t(int, (facts->HighPriorityCredit + (5)),
(facts->RequestCredit / 4));
@@ -3985,13 +4331,18 @@ _base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc)
/* reply free queue sizing - taking into account for 64 FW events */
ioc->reply_free_queue_depth = ioc->hba_queue_depth + 64;
- /* calculate reply descriptor post queue depth */
- ioc->reply_post_queue_depth = ioc->hba_queue_depth +
- ioc->reply_free_queue_depth + 1 ;
- /* align the reply post queue on the next 16 count boundary */
- if (ioc->reply_post_queue_depth % 16)
- ioc->reply_post_queue_depth += 16 -
- (ioc->reply_post_queue_depth % 16);
+ /* mCPU manage single counters for simplicity */
+ if (ioc->is_mcpu_endpoint)
+ ioc->reply_post_queue_depth = ioc->reply_free_queue_depth;
+ else {
+ /* calculate reply descriptor post queue depth */
+ ioc->reply_post_queue_depth = ioc->hba_queue_depth +
+ ioc->reply_free_queue_depth + 1;
+ /* align the reply post queue on the next 16 count boundary */
+ if (ioc->reply_post_queue_depth % 16)
+ ioc->reply_post_queue_depth += 16 -
+ (ioc->reply_post_queue_depth % 16);
+ }
if (ioc->reply_post_queue_depth >
facts->MaxReplyDescriptorPostQueueDepth) {
@@ -4789,7 +5140,7 @@ mpt3sas_base_sas_iounit_control(struct MPT3SAS_ADAPTER *ioc,
mpi_request->Operation == MPI2_SAS_OP_PHY_LINK_RESET)
ioc->ioc_link_reset_in_progress = 1;
init_completion(&ioc->base_cmds.done);
- ioc->put_smid_default(ioc, smid);
+ mpt3sas_base_put_smid_default(ioc, smid);
wait_for_completion_timeout(&ioc->base_cmds.done,
msecs_to_jiffies(10000));
if ((mpi_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET ||
@@ -4889,7 +5240,7 @@ mpt3sas_base_scsi_enclosure_processor(struct MPT3SAS_ADAPTER *ioc,
ioc->base_cmds.smid = smid;
memcpy(request, mpi_request, sizeof(Mpi2SepReply_t));
init_completion(&ioc->base_cmds.done);
- ioc->put_smid_default(ioc, smid);
+ mpt3sas_base_put_smid_default(ioc, smid);
wait_for_completion_timeout(&ioc->base_cmds.done,
msecs_to_jiffies(10000));
if (!(ioc->base_cmds.status & MPT3_CMD_COMPLETE)) {
@@ -5074,8 +5425,6 @@ _base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc)
if ((facts->IOCCapabilities &
MPI2_IOCFACTS_CAPABILITY_RDPQ_ARRAY_CAPABLE) && (!reset_devices))
ioc->rdpq_array_capable = 1;
- if (facts->IOCCapabilities & MPI26_IOCFACTS_CAPABILITY_ATOMIC_REQ)
- ioc->atomic_desc_capable = 1;
facts->FWVersion.Word = le32_to_cpu(mpi_reply.FWVersion.Word);
facts->IOCRequestFrameSize =
le16_to_cpu(mpi_reply.IOCRequestFrameSize);
@@ -5317,7 +5666,7 @@ _base_send_port_enable(struct MPT3SAS_ADAPTER *ioc)
mpi_request->Function = MPI2_FUNCTION_PORT_ENABLE;
init_completion(&ioc->port_enable_cmds.done);
- ioc->put_smid_default(ioc, smid);
+ mpt3sas_base_put_smid_default(ioc, smid);
wait_for_completion_timeout(&ioc->port_enable_cmds.done, 300*HZ);
if (!(ioc->port_enable_cmds.status & MPT3_CMD_COMPLETE)) {
pr_err(MPT3SAS_FMT "%s: timeout\n",
@@ -5380,7 +5729,7 @@ mpt3sas_port_enable(struct MPT3SAS_ADAPTER *ioc)
memset(mpi_request, 0, sizeof(Mpi2PortEnableRequest_t));
mpi_request->Function = MPI2_FUNCTION_PORT_ENABLE;
- ioc->put_smid_default(ioc, smid);
+ mpt3sas_base_put_smid_default(ioc, smid);
return 0;
}
@@ -5499,7 +5848,7 @@ _base_event_notification(struct MPT3SAS_ADAPTER *ioc)
mpi_request->EventMasks[i] =
cpu_to_le32(ioc->event_masks[i]);
init_completion(&ioc->base_cmds.done);
- ioc->put_smid_default(ioc, smid);
+ mpt3sas_base_put_smid_default(ioc, smid);
wait_for_completion_timeout(&ioc->base_cmds.done, 30*HZ);
if (!(ioc->base_cmds.status & MPT3_CMD_COMPLETE)) {
pr_err(MPT3SAS_FMT "%s: timeout\n",
@@ -5819,8 +6168,12 @@ _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc)
/* initialize Reply Free Queue */
for (i = 0, reply_address = (u32)ioc->reply_dma ;
i < ioc->reply_free_queue_depth ; i++, reply_address +=
- ioc->reply_sz)
+ ioc->reply_sz) {
ioc->reply_free[i] = cpu_to_le32(reply_address);
+ if (ioc->is_mcpu_endpoint)
+ _base_clone_reply_to_sys_mem(ioc,
+ (__le32)reply_address, i);
+ }
/* initialize reply queues */
if (ioc->is_driver_loading)
@@ -6009,20 +6362,10 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc)
break;
}
- if (ioc->atomic_desc_capable) {
- ioc->put_smid_default = &_base_put_smid_default_atomic;
- ioc->put_smid_scsi_io = &_base_put_smid_scsi_io_atomic;
- ioc->put_smid_fast_path = &_base_put_smid_fast_path_atomic;
- ioc->put_smid_hi_priority = &_base_put_smid_hi_priority_atomic;
- ioc->put_smid_nvme_encap = &_base_put_smid_nvme_encap_atomic;
- } else {
- ioc->put_smid_default = &_base_put_smid_default;
+ if (ioc->is_mcpu_endpoint)
+ ioc->put_smid_scsi_io = &_base_put_smid_mpi_ep_scsi_io;
+ else
ioc->put_smid_scsi_io = &_base_put_smid_scsi_io;
- ioc->put_smid_fast_path = &_base_put_smid_fast_path;
- ioc->put_smid_hi_priority = &_base_put_smid_hi_priority;
- ioc->put_smid_nvme_encap = &_base_put_smid_nvme_encap;
- }
-
/*
* These function pointers for other requests that don't
diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.h b/drivers/scsi/mpt3sas/mpt3sas_base.h
index 99ccf83b8c51..ae36d8fb2f2b 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_base.h
+++ b/drivers/scsi/mpt3sas/mpt3sas_base.h
@@ -95,6 +95,8 @@
#define MPT_MIN_PHYS_SEGMENTS 16
#define MPT_KDUMP_MIN_PHYS_SEGMENTS 32
+#define MCPU_MAX_CHAINS_PER_IO 3
+
#ifdef CONFIG_SCSI_MPT3SAS_MAX_SGE
#define MPT3SAS_SG_DEPTH CONFIG_SCSI_MPT3SAS_MAX_SGE
#else
@@ -120,6 +122,8 @@
#define MPT3SAS_NVME_QUEUE_DEPTH 128
#define MPT_NAME_LENGTH 32 /* generic length of strings */
#define MPT_STRING_LENGTH 64
+#define MPI_FRAME_START_OFFSET 256
+#define REPLY_FREE_POOL_SIZE 512 /*(32 maxcredix *4)*(4 times)*/
#define MPT_MAX_CALLBACKS 32
@@ -1099,7 +1103,7 @@ struct MPT3SAS_ADAPTER {
char tmp_string[MPT_STRING_LENGTH];
struct pci_dev *pdev;
Mpi2SystemInterfaceRegs_t __iomem *chip;
- resource_size_t chip_phys;
+ phys_addr_t chip_phys;
int logging_level;
int fwfault_debug;
u8 ir_firmware;
@@ -1236,6 +1240,7 @@ struct MPT3SAS_ADAPTER {
u16 config_page_sz;
void *config_page;
dma_addr_t config_page_dma;
+ void *config_vaddr;
/* scsiio request */
u16 hba_queue_depth;
@@ -1336,6 +1341,7 @@ struct MPT3SAS_ADAPTER {
u32 ring_buffer_offset;
u32 ring_buffer_sz;
u8 is_warpdrive;
+ u8 is_mcpu_endpoint;
u8 hide_ir_msg;
u8 mfg_pg10_hide_flag;
u8 hide_drives;
@@ -1348,12 +1354,7 @@ struct MPT3SAS_ADAPTER {
void *device_remove_in_progress;
u16 device_remove_in_progress_sz;
u8 is_gen35_ioc;
- u8 atomic_desc_capable;
PUT_SMID_IO_FP_HIP put_smid_scsi_io;
- PUT_SMID_IO_FP_HIP put_smid_fast_path;
- PUT_SMID_IO_FP_HIP put_smid_hi_priority;
- PUT_SMID_DEFAULT put_smid_default;
- PUT_SMID_DEFAULT put_smid_nvme_encap;
};
@@ -1394,6 +1395,12 @@ void *mpt3sas_base_get_pcie_sgl(struct MPT3SAS_ADAPTER *ioc, u16 smid);
dma_addr_t mpt3sas_base_get_pcie_sgl_dma(struct MPT3SAS_ADAPTER *ioc, u16 smid);
void mpt3sas_base_sync_reply_irqs(struct MPT3SAS_ADAPTER *ioc);
+void mpt3sas_base_put_smid_fast_path(struct MPT3SAS_ADAPTER *ioc, u16 smid,
+ u16 handle);
+void mpt3sas_base_put_smid_hi_priority(struct MPT3SAS_ADAPTER *ioc, u16 smid,
+ u16 msix_task);
+void mpt3sas_base_put_smid_nvme_encap(struct MPT3SAS_ADAPTER *ioc, u16 smid);
+void mpt3sas_base_put_smid_default(struct MPT3SAS_ADAPTER *ioc, u16 smid);
/* hi-priority queue */
u16 mpt3sas_base_get_smid_hpr(struct MPT3SAS_ADAPTER *ioc, u8 cb_idx);
u16 mpt3sas_base_get_smid_scsiio(struct MPT3SAS_ADAPTER *ioc, u8 cb_idx,
diff --git a/drivers/scsi/mpt3sas/mpt3sas_config.c b/drivers/scsi/mpt3sas/mpt3sas_config.c
index 1c747cf419d5..e87c76a832f6 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_config.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_config.c
@@ -219,6 +219,7 @@ _config_alloc_config_dma_memory(struct MPT3SAS_ADAPTER *ioc,
mem->page = ioc->config_page;
mem->page_dma = ioc->config_page_dma;
}
+ ioc->config_vaddr = mem->page;
return r;
}
@@ -402,7 +403,7 @@ _config_request(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigRequest_t
memcpy(config_request, mpi_request, sizeof(Mpi2ConfigRequest_t));
_config_display_some_debug(ioc, smid, "config_request", NULL);
init_completion(&ioc->config_cmds.done);
- ioc->put_smid_default(ioc, smid);
+ mpt3sas_base_put_smid_default(ioc, smid);
wait_for_completion_timeout(&ioc->config_cmds.done, timeout*HZ);
if (!(ioc->config_cmds.status & MPT3_CMD_COMPLETE)) {
pr_err(MPT3SAS_FMT "%s: timeout\n",
diff --git a/drivers/scsi/mpt3sas/mpt3sas_ctl.c b/drivers/scsi/mpt3sas/mpt3sas_ctl.c
index 523971aeb4c1..d3cb387ba9f4 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_ctl.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_ctl.c
@@ -820,7 +820,7 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg,
ret = -EINVAL;
goto out;
}
- ioc->put_smid_nvme_encap(ioc, smid);
+ mpt3sas_base_put_smid_nvme_encap(ioc, smid);
break;
}
case MPI2_FUNCTION_SCSI_IO_REQUEST:
@@ -845,7 +845,7 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg,
if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST)
ioc->put_smid_scsi_io(ioc, smid, device_handle);
else
- ioc->put_smid_default(ioc, smid);
+ mpt3sas_base_put_smid_default(ioc, smid);
break;
}
case MPI2_FUNCTION_SCSI_TASK_MGMT:
@@ -882,7 +882,7 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg,
tm_request->DevHandle));
ioc->build_sg_mpi(ioc, psge, data_out_dma, data_out_sz,
data_in_dma, data_in_sz);
- ioc->put_smid_hi_priority(ioc, smid, 0);
+ mpt3sas_base_put_smid_hi_priority(ioc, smid, 0);
break;
}
case MPI2_FUNCTION_SMP_PASSTHROUGH:
@@ -913,7 +913,7 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg,
}
ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, data_in_dma,
data_in_sz);
- ioc->put_smid_default(ioc, smid);
+ mpt3sas_base_put_smid_default(ioc, smid);
break;
}
case MPI2_FUNCTION_SATA_PASSTHROUGH:
@@ -928,7 +928,7 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg,
}
ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, data_in_dma,
data_in_sz);
- ioc->put_smid_default(ioc, smid);
+ mpt3sas_base_put_smid_default(ioc, smid);
break;
}
case MPI2_FUNCTION_FW_DOWNLOAD:
@@ -936,7 +936,7 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg,
{
ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, data_in_dma,
data_in_sz);
- ioc->put_smid_default(ioc, smid);
+ mpt3sas_base_put_smid_default(ioc, smid);
break;
}
case MPI2_FUNCTION_TOOLBOX:
@@ -951,7 +951,7 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg,
ioc->build_sg_mpi(ioc, psge, data_out_dma, data_out_sz,
data_in_dma, data_in_sz);
}
- ioc->put_smid_default(ioc, smid);
+ mpt3sas_base_put_smid_default(ioc, smid);
break;
}
case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL:
@@ -970,7 +970,7 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg,
default:
ioc->build_sg_mpi(ioc, psge, data_out_dma, data_out_sz,
data_in_dma, data_in_sz);
- ioc->put_smid_default(ioc, smid);
+ mpt3sas_base_put_smid_default(ioc, smid);
break;
}
@@ -1601,7 +1601,7 @@ _ctl_diag_register_2(struct MPT3SAS_ADAPTER *ioc,
cpu_to_le32(ioc->product_specific[buffer_type][i]);
init_completion(&ioc->ctl_cmds.done);
- ioc->put_smid_default(ioc, smid);
+ mpt3sas_base_put_smid_default(ioc, smid);
wait_for_completion_timeout(&ioc->ctl_cmds.done,
MPT3_IOCTL_DEFAULT_TIMEOUT*HZ);
@@ -1948,7 +1948,7 @@ mpt3sas_send_diag_release(struct MPT3SAS_ADAPTER *ioc, u8 buffer_type,
mpi_request->VP_ID = 0;
init_completion(&ioc->ctl_cmds.done);
- ioc->put_smid_default(ioc, smid);
+ mpt3sas_base_put_smid_default(ioc, smid);
wait_for_completion_timeout(&ioc->ctl_cmds.done,
MPT3_IOCTL_DEFAULT_TIMEOUT*HZ);
@@ -2215,7 +2215,7 @@ _ctl_diag_read_buffer(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
mpi_request->VP_ID = 0;
init_completion(&ioc->ctl_cmds.done);
- ioc->put_smid_default(ioc, smid);
+ mpt3sas_base_put_smid_default(ioc, smid);
wait_for_completion_timeout(&ioc->ctl_cmds.done,
MPT3_IOCTL_DEFAULT_TIMEOUT*HZ);
diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
index a1cb0236c550..8cd3782fab49 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
@@ -2352,7 +2352,7 @@ scsih_slave_configure(struct scsi_device *sdev)
** merged and can eliminate holes created during merging
** operation.
**/
- queue_flag_set_unlocked(QUEUE_FLAG_NOMERGES,
+ blk_queue_flag_set(QUEUE_FLAG_NOMERGES,
sdev->request_queue);
blk_queue_virt_boundary(sdev->request_queue,
ioc->page_size - 1);
@@ -2679,7 +2679,7 @@ mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle,
int_to_scsilun(lun, (struct scsi_lun *)mpi_request->LUN);
mpt3sas_scsih_set_tm_flag(ioc, handle);
init_completion(&ioc->tm_cmds.done);
- ioc->put_smid_hi_priority(ioc, smid, msix_task);
+ mpt3sas_base_put_smid_hi_priority(ioc, smid, msix_task);
wait_for_completion_timeout(&ioc->tm_cmds.done, timeout*HZ);
if (!(ioc->tm_cmds.status & MPT3_CMD_COMPLETE)) {
pr_err(MPT3SAS_FMT "%s: timeout\n",
@@ -3641,7 +3641,7 @@ _scsih_tm_tr_send(struct MPT3SAS_ADAPTER *ioc, u16 handle)
mpi_request->DevHandle = cpu_to_le16(handle);
mpi_request->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET;
set_bit(handle, ioc->device_remove_in_progress);
- ioc->put_smid_hi_priority(ioc, smid, 0);
+ mpt3sas_base_put_smid_hi_priority(ioc, smid, 0);
mpt3sas_trigger_master(ioc, MASTER_TRIGGER_DEVICE_REMOVAL);
out:
@@ -3742,7 +3742,7 @@ _scsih_tm_tr_complete(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
mpi_request->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL;
mpi_request->Operation = MPI2_SAS_OP_REMOVE_DEVICE;
mpi_request->DevHandle = mpi_request_tm->DevHandle;
- ioc->put_smid_default(ioc, smid_sas_ctrl);
+ mpt3sas_base_put_smid_default(ioc, smid_sas_ctrl);
return _scsih_check_for_pending_tm(ioc, smid);
}
@@ -3837,7 +3837,7 @@ _scsih_tm_tr_volume_send(struct MPT3SAS_ADAPTER *ioc, u16 handle)
mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
mpi_request->DevHandle = cpu_to_le16(handle);
mpi_request->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET;
- ioc->put_smid_hi_priority(ioc, smid, 0);
+ mpt3sas_base_put_smid_hi_priority(ioc, smid, 0);
}
/**
@@ -3929,7 +3929,7 @@ _scsih_issue_delayed_event_ack(struct MPT3SAS_ADAPTER *ioc, u16 smid, u16 event,
ack_request->EventContext = event_context;
ack_request->VF_ID = 0; /* TODO */
ack_request->VP_ID = 0;
- ioc->put_smid_default(ioc, smid);
+ mpt3sas_base_put_smid_default(ioc, smid);
}
/**
@@ -3986,7 +3986,7 @@ _scsih_issue_delayed_sas_io_unit_ctrl(struct MPT3SAS_ADAPTER *ioc,
mpi_request->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL;
mpi_request->Operation = MPI2_SAS_OP_REMOVE_DEVICE;
mpi_request->DevHandle = handle;
- ioc->put_smid_default(ioc, smid);
+ mpt3sas_base_put_smid_default(ioc, smid);
}
/**
@@ -4715,12 +4715,12 @@ scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd)
if (sas_target_priv_data->flags & MPT_TARGET_FASTPATH_IO) {
mpi_request->IoFlags = cpu_to_le16(scmd->cmd_len |
MPI25_SCSIIO_IOFLAGS_FAST_PATH);
- ioc->put_smid_fast_path(ioc, smid, handle);
+ mpt3sas_base_put_smid_fast_path(ioc, smid, handle);
} else
ioc->put_smid_scsi_io(ioc, smid,
le16_to_cpu(mpi_request->DevHandle));
} else
- ioc->put_smid_default(ioc, smid);
+ mpt3sas_base_put_smid_default(ioc, smid);
return 0;
out:
@@ -7609,7 +7609,7 @@ _scsih_ir_fastpath(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phys_disk_num)
handle, phys_disk_num));
init_completion(&ioc->scsih_cmds.done);
- ioc->put_smid_default(ioc, smid);
+ mpt3sas_base_put_smid_default(ioc, smid);
wait_for_completion_timeout(&ioc->scsih_cmds.done, 10*HZ);
if (!(ioc->scsih_cmds.status & MPT3_CMD_COMPLETE)) {
@@ -9700,7 +9700,7 @@ _scsih_ir_shutdown(struct MPT3SAS_ADAPTER *ioc)
if (!ioc->hide_ir_msg)
pr_info(MPT3SAS_FMT "IR shutdown (sending)\n", ioc->name);
init_completion(&ioc->scsih_cmds.done);
- ioc->put_smid_default(ioc, smid);
+ mpt3sas_base_put_smid_default(ioc, smid);
wait_for_completion_timeout(&ioc->scsih_cmds.done, 10*HZ);
if (!(ioc->scsih_cmds.status & MPT3_CMD_COMPLETE)) {
@@ -10346,6 +10346,7 @@ _scsih_determine_hba_mpi_version(struct pci_dev *pdev)
case MPI2_MFGPAGE_DEVID_SAS2308_1:
case MPI2_MFGPAGE_DEVID_SAS2308_2:
case MPI2_MFGPAGE_DEVID_SAS2308_3:
+ case MPI2_MFGPAGE_DEVID_SAS2308_MPI_EP:
return MPI2_VERSION;
case MPI25_MFGPAGE_DEVID_SAS3004:
case MPI25_MFGPAGE_DEVID_SAS3008:
@@ -10423,11 +10424,18 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
ioc->hba_mpi_version_belonged = hba_mpi_version;
ioc->id = mpt2_ids++;
sprintf(ioc->driver_name, "%s", MPT2SAS_DRIVER_NAME);
- if (pdev->device == MPI2_MFGPAGE_DEVID_SSS6200) {
+ switch (pdev->device) {
+ case MPI2_MFGPAGE_DEVID_SSS6200:
ioc->is_warpdrive = 1;
ioc->hide_ir_msg = 1;
- } else
+ break;
+ case MPI2_MFGPAGE_DEVID_SAS2308_MPI_EP:
+ ioc->is_mcpu_endpoint = 1;
+ break;
+ default:
ioc->mfg_pg10_hide_flag = MFG_PAGE10_EXPOSE_ALL_DISKS;
+ break;
+ }
break;
case MPI25_VERSION:
case MPI26_VERSION:
@@ -10524,26 +10532,34 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
shost->transportt = mpt3sas_transport_template;
shost->unique_id = ioc->id;
- if (max_sectors != 0xFFFF) {
- if (max_sectors < 64) {
- shost->max_sectors = 64;
- pr_warn(MPT3SAS_FMT "Invalid value %d passed " \
- "for max_sectors, range is 64 to 32767. Assigning "
- "value of 64.\n", ioc->name, max_sectors);
- } else if (max_sectors > 32767) {
- shost->max_sectors = 32767;
- pr_warn(MPT3SAS_FMT "Invalid value %d passed " \
- "for max_sectors, range is 64 to 32767. Assigning "
- "default value of 32767.\n", ioc->name,
- max_sectors);
- } else {
- shost->max_sectors = max_sectors & 0xFFFE;
- pr_info(MPT3SAS_FMT
+ if (ioc->is_mcpu_endpoint) {
+ /* mCPU MPI support 64K max IO */
+ shost->max_sectors = 128;
+ pr_info(MPT3SAS_FMT
"The max_sectors value is set to %d\n",
ioc->name, shost->max_sectors);
+ } else {
+ if (max_sectors != 0xFFFF) {
+ if (max_sectors < 64) {
+ shost->max_sectors = 64;
+ pr_warn(MPT3SAS_FMT "Invalid value %d passed " \
+ "for max_sectors, range is 64 to 32767. " \
+ "Assigning value of 64.\n", \
+ ioc->name, max_sectors);
+ } else if (max_sectors > 32767) {
+ shost->max_sectors = 32767;
+ pr_warn(MPT3SAS_FMT "Invalid value %d passed " \
+ "for max_sectors, range is 64 to 32767." \
+ "Assigning default value of 32767.\n", \
+ ioc->name, max_sectors);
+ } else {
+ shost->max_sectors = max_sectors & 0xFFFE;
+ pr_info(MPT3SAS_FMT
+ "The max_sectors value is set to %d\n",
+ ioc->name, shost->max_sectors);
+ }
}
}
-
/* register EEDP capabilities with SCSI layer */
if (prot_mask > 0)
scsi_host_set_prot(shost, prot_mask);
@@ -10856,6 +10872,8 @@ static const struct pci_device_id mpt3sas_pci_table[] = {
PCI_ANY_ID, PCI_ANY_ID },
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_3,
PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_MPI_EP,
+ PCI_ANY_ID, PCI_ANY_ID },
/* SSS6200 */
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SSS6200,
PCI_ANY_ID, PCI_ANY_ID },
diff --git a/drivers/scsi/mpt3sas/mpt3sas_transport.c b/drivers/scsi/mpt3sas/mpt3sas_transport.c
index d3940c5d079d..3a143bb5ca72 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_transport.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_transport.c
@@ -392,7 +392,7 @@ _transport_expander_report_manufacture(struct MPT3SAS_ADAPTER *ioc,
"report_manufacture - send to sas_addr(0x%016llx)\n",
ioc->name, (unsigned long long)sas_address));
init_completion(&ioc->transport_cmds.done);
- ioc->put_smid_default(ioc, smid);
+ mpt3sas_base_put_smid_default(ioc, smid);
wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ);
if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) {
@@ -1198,7 +1198,7 @@ _transport_get_expander_phy_error_log(struct MPT3SAS_ADAPTER *ioc,
ioc->name, (unsigned long long)phy->identify.sas_address,
phy->number));
init_completion(&ioc->transport_cmds.done);
- ioc->put_smid_default(ioc, smid);
+ mpt3sas_base_put_smid_default(ioc, smid);
wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ);
if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) {
@@ -1514,7 +1514,7 @@ _transport_expander_phy_control(struct MPT3SAS_ADAPTER *ioc,
ioc->name, (unsigned long long)phy->identify.sas_address,
phy->number, phy_operation));
init_completion(&ioc->transport_cmds.done);
- ioc->put_smid_default(ioc, smid);
+ mpt3sas_base_put_smid_default(ioc, smid);
wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ);
if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) {
@@ -2014,7 +2014,7 @@ _transport_smp_handler(struct bsg_job *job, struct Scsi_Host *shost,
"%s - sending smp request\n", ioc->name, __func__));
init_completion(&ioc->transport_cmds.done);
- ioc->put_smid_default(ioc, smid);
+ mpt3sas_base_put_smid_default(ioc, smid);
wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ);
if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) {
diff --git a/drivers/scsi/mvme147.c b/drivers/scsi/mvme147.c
index e6b2b681fda3..7d1ab414b78f 100644
--- a/drivers/scsi/mvme147.c
+++ b/drivers/scsi/mvme147.c
@@ -3,6 +3,9 @@
#include <linux/mm.h>
#include <linux/blkdev.h>
#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
#include <asm/page.h>
#include <asm/pgtable.h>
@@ -14,9 +17,6 @@
#include "wd33c93.h"
#include "mvme147.h"
-#include <linux/stat.h>
-
-
static irqreturn_t mvme147_intr(int irq, void *data)
{
struct Scsi_Host *instance = data;
@@ -65,40 +65,57 @@ static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt,
m147_pcc->dma_cntrl = 0;
}
-int mvme147_detect(struct scsi_host_template *tpnt)
+static struct scsi_host_template mvme147_host_template = {
+ .module = THIS_MODULE,
+ .proc_name = "MVME147",
+ .name = "MVME147 built-in SCSI",
+ .queuecommand = wd33c93_queuecommand,
+ .eh_abort_handler = wd33c93_abort,
+ .eh_host_reset_handler = wd33c93_host_reset,
+ .show_info = wd33c93_show_info,
+ .write_info = wd33c93_write_info,
+ .can_queue = CAN_QUEUE,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = CMD_PER_LUN,
+ .use_clustering = ENABLE_CLUSTERING
+};
+
+static struct Scsi_Host *mvme147_shost;
+
+static int __init mvme147_init(void)
{
- static unsigned char called = 0;
- struct Scsi_Host *instance;
wd33c93_regs regs;
struct WD33C93_hostdata *hdata;
+ int error = -ENOMEM;
- if (!MACH_IS_MVME147 || called)
+ if (!MACH_IS_MVME147)
return 0;
- called++;
- tpnt->proc_name = "MVME147";
- tpnt->show_info = wd33c93_show_info,
- tpnt->write_info = wd33c93_write_info,
-
- instance = scsi_register(tpnt, sizeof(struct WD33C93_hostdata));
- if (!instance)
+ mvme147_shost = scsi_host_alloc(&mvme147_host_template,
+ sizeof(struct WD33C93_hostdata));
+ if (!mvme147_shost)
goto err_out;
+ mvme147_shost->base = 0xfffe4000;
+ mvme147_shost->irq = MVME147_IRQ_SCSI_PORT;
- instance->base = 0xfffe4000;
- instance->irq = MVME147_IRQ_SCSI_PORT;
regs.SASR = (volatile unsigned char *)0xfffe4000;
regs.SCMD = (volatile unsigned char *)0xfffe4001;
- hdata = shost_priv(instance);
+
+ hdata = shost_priv(mvme147_shost);
hdata->no_sync = 0xff;
hdata->fast = 0;
hdata->dma_mode = CTRL_DMA;
- wd33c93_init(instance, regs, dma_setup, dma_stop, WD33C93_FS_8_10);
- if (request_irq(MVME147_IRQ_SCSI_PORT, mvme147_intr, 0,
- "MVME147 SCSI PORT", instance))
+ wd33c93_init(mvme147_shost, regs, dma_setup, dma_stop, WD33C93_FS_8_10);
+
+ error = request_irq(MVME147_IRQ_SCSI_PORT, mvme147_intr, 0,
+ "MVME147 SCSI PORT", mvme147_shost);
+ if (error)
goto err_unregister;
- if (request_irq(MVME147_IRQ_SCSI_DMA, mvme147_intr, 0,
- "MVME147 SCSI DMA", instance))
+ error = request_irq(MVME147_IRQ_SCSI_DMA, mvme147_intr, 0,
+ "MVME147 SCSI DMA", mvme147_shost);
+ if (error)
goto err_free_irq;
#if 0 /* Disabled; causes problems booting */
m147_pcc->scsi_interrupt = 0x10; /* Assert SCSI bus reset */
@@ -112,40 +129,30 @@ int mvme147_detect(struct scsi_host_template *tpnt)
m147_pcc->dma_cntrl = 0x00; /* ensure DMA is stopped */
m147_pcc->dma_intr = 0x89; /* Ack and enable ints */
- return 1;
+ error = scsi_add_host(mvme147_shost, NULL);
+ if (error)
+ goto err_free_irq;
+ scsi_scan_host(mvme147_shost);
+ return 0;
err_free_irq:
- free_irq(MVME147_IRQ_SCSI_PORT, mvme147_intr);
+ free_irq(MVME147_IRQ_SCSI_PORT, mvme147_shost);
err_unregister:
- scsi_unregister(instance);
+ scsi_host_put(mvme147_shost);
err_out:
- return 0;
+ return error;
}
-static struct scsi_host_template driver_template = {
- .proc_name = "MVME147",
- .name = "MVME147 built-in SCSI",
- .detect = mvme147_detect,
- .release = mvme147_release,
- .queuecommand = wd33c93_queuecommand,
- .eh_abort_handler = wd33c93_abort,
- .eh_host_reset_handler = wd33c93_host_reset,
- .can_queue = CAN_QUEUE,
- .this_id = 7,
- .sg_tablesize = SG_ALL,
- .cmd_per_lun = CMD_PER_LUN,
- .use_clustering = ENABLE_CLUSTERING
-};
-
-
-#include "scsi_module.c"
-
-int mvme147_release(struct Scsi_Host *instance)
+static void __exit mvme147_exit(void)
{
-#ifdef MODULE
+ scsi_remove_host(mvme147_shost);
+
/* XXX Make sure DMA is stopped! */
- free_irq(MVME147_IRQ_SCSI_PORT, mvme147_intr);
- free_irq(MVME147_IRQ_SCSI_DMA, mvme147_intr);
-#endif
- return 1;
+ free_irq(MVME147_IRQ_SCSI_PORT, mvme147_shost);
+ free_irq(MVME147_IRQ_SCSI_DMA, mvme147_shost);
+
+ scsi_host_put(mvme147_shost);
}
+
+module_init(mvme147_init);
+module_exit(mvme147_exit);
diff --git a/drivers/scsi/mvsas/mv_94xx.c b/drivers/scsi/mvsas/mv_94xx.c
index 7de5d8d75480..eb5471bc7263 100644
--- a/drivers/scsi/mvsas/mv_94xx.c
+++ b/drivers/scsi/mvsas/mv_94xx.c
@@ -1080,16 +1080,16 @@ static int mvs_94xx_gpio_write(struct mvs_prv_info *mvs_prv,
void __iomem *regs = mvi->regs_ex - 0x10200;
int drive = (i/3) & (4-1); /* drive number on host */
- u32 block = mr32(MVS_SGPIO_DCTRL +
+ int driveshift = drive * 8; /* bit offset of drive */
+ u32 block = ioread32be(regs + MVS_SGPIO_DCTRL +
MVS_SGPIO_HOST_OFFSET * mvi->id);
-
/*
* if bit is set then create a mask with the first
* bit of the drive set in the mask ...
*/
- u32 bit = (write_data[i/8] & (1 << (i&(8-1)))) ?
- 1<<(24-drive*8) : 0;
+ u32 bit = get_unaligned_be32(write_data) & (1 << i) ?
+ 1 << driveshift : 0;
/*
* ... and then shift it to the right position based
@@ -1098,26 +1098,27 @@ static int mvs_94xx_gpio_write(struct mvs_prv_info *mvs_prv,
switch (i%3) {
case 0: /* activity */
block &= ~((0x7 << MVS_SGPIO_DCTRL_ACT_SHIFT)
- << (24-drive*8));
+ << driveshift);
/* hardwire activity bit to SOF */
block |= LED_BLINKA_SOF << (
MVS_SGPIO_DCTRL_ACT_SHIFT +
- (24-drive*8));
+ driveshift);
break;
case 1: /* id */
block &= ~((0x3 << MVS_SGPIO_DCTRL_LOC_SHIFT)
- << (24-drive*8));
+ << driveshift);
block |= bit << MVS_SGPIO_DCTRL_LOC_SHIFT;
break;
case 2: /* fail */
block &= ~((0x7 << MVS_SGPIO_DCTRL_ERR_SHIFT)
- << (24-drive*8));
+ << driveshift);
block |= bit << MVS_SGPIO_DCTRL_ERR_SHIFT;
break;
}
- mw32(MVS_SGPIO_DCTRL + MVS_SGPIO_HOST_OFFSET * mvi->id,
- block);
+ iowrite32be(block,
+ regs + MVS_SGPIO_DCTRL +
+ MVS_SGPIO_HOST_OFFSET * mvi->id);
}
@@ -1132,7 +1133,7 @@ static int mvs_94xx_gpio_write(struct mvs_prv_info *mvs_prv,
void __iomem *regs = mvi->regs_ex - 0x10200;
mw32(MVS_SGPIO_DCTRL + MVS_SGPIO_HOST_OFFSET * mvi->id,
- be32_to_cpu(((u32 *) write_data)[i]));
+ ((u32 *) write_data)[i]);
}
return reg_count;
}
diff --git a/drivers/scsi/pcmcia/Kconfig b/drivers/scsi/pcmcia/Kconfig
index ecc855c550aa..2d435f105b16 100644
--- a/drivers/scsi/pcmcia/Kconfig
+++ b/drivers/scsi/pcmcia/Kconfig
@@ -19,15 +19,6 @@ config PCMCIA_AHA152X
To compile this driver as a module, choose M here: the
module will be called aha152x_cs.
-config PCMCIA_FDOMAIN
- tristate "Future Domain PCMCIA support"
- help
- Say Y here if you intend to attach this type of PCMCIA SCSI host
- adapter to your computer.
-
- To compile this driver as a module, choose M here: the
- module will be called fdomain_cs.
-
config PCMCIA_NINJA_SCSI
tristate "NinjaSCSI-3 / NinjaSCSI-32Bi (16bit) PCMCIA support"
depends on !64BIT
diff --git a/drivers/scsi/pcmcia/Makefile b/drivers/scsi/pcmcia/Makefile
index 44eea2d43143..faa87a4b2d2b 100644
--- a/drivers/scsi/pcmcia/Makefile
+++ b/drivers/scsi/pcmcia/Makefile
@@ -4,11 +4,9 @@ ccflags-y := -Idrivers/scsi
# 16-bit client drivers
obj-$(CONFIG_PCMCIA_QLOGIC) += qlogic_cs.o
-obj-$(CONFIG_PCMCIA_FDOMAIN) += fdomain_cs.o
obj-$(CONFIG_PCMCIA_AHA152X) += aha152x_cs.o
obj-$(CONFIG_PCMCIA_NINJA_SCSI) += nsp_cs.o
obj-$(CONFIG_PCMCIA_SYM53C500) += sym53c500_cs.o
aha152x_cs-objs := aha152x_stub.o aha152x_core.o
-fdomain_cs-objs := fdomain_stub.o fdomain_core.o
qlogic_cs-objs := qlogic_stub.o
diff --git a/drivers/scsi/pcmcia/fdomain_core.c b/drivers/scsi/pcmcia/fdomain_core.c
deleted file mode 100644
index a48913791868..000000000000
--- a/drivers/scsi/pcmcia/fdomain_core.c
+++ /dev/null
@@ -1,2 +0,0 @@
-#define PCMCIA 1
-#include "fdomain.c"
diff --git a/drivers/scsi/pcmcia/fdomain_stub.c b/drivers/scsi/pcmcia/fdomain_stub.c
deleted file mode 100644
index 953a792150ae..000000000000
--- a/drivers/scsi/pcmcia/fdomain_stub.c
+++ /dev/null
@@ -1,209 +0,0 @@
-/*======================================================================
-
- A driver for Future Domain-compatible PCMCIA SCSI cards
-
- fdomain_cs.c 1.47 2001/10/13 00:08:52
-
- The contents of this file are subject to the Mozilla Public
- License Version 1.1 (the "License"); you may not use this file
- except in compliance with the License. You may obtain a copy of
- the License at http://www.mozilla.org/MPL/
-
- Software distributed under the License is distributed on an "AS
- IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- implied. See the License for the specific language governing
- rights and limitations under the License.
-
- The initial developer of the original code is David A. Hinds
- <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
- are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
-
- Alternatively, the contents of this file may be used under the
- terms of the GNU General Public License version 2 (the "GPL"), in
- which case the provisions of the GPL are applicable instead of the
- above. If you wish to allow the use of your version of this file
- only under the terms of the GPL and not to allow others to use
- your version of this file under the MPL, indicate your decision
- by deleting the provisions above and replace them with the notice
- and other provisions required by the GPL. If you do not delete
- the provisions above, a recipient may use your version of this
- file under either the MPL or the GPL.
-
-======================================================================*/
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/ioport.h>
-#include <scsi/scsi.h>
-#include <linux/major.h>
-#include <linux/blkdev.h>
-#include <scsi/scsi_ioctl.h>
-
-#include "scsi.h"
-#include <scsi/scsi_host.h>
-#include "fdomain.h"
-
-#include <pcmcia/cistpl.h>
-#include <pcmcia/ds.h>
-
-/*====================================================================*/
-
-/* Module parameters */
-
-MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
-MODULE_DESCRIPTION("Future Domain PCMCIA SCSI driver");
-MODULE_LICENSE("Dual MPL/GPL");
-
-/*====================================================================*/
-
-typedef struct scsi_info_t {
- struct pcmcia_device *p_dev;
- struct Scsi_Host *host;
-} scsi_info_t;
-
-
-static void fdomain_release(struct pcmcia_device *link);
-static void fdomain_detach(struct pcmcia_device *p_dev);
-static int fdomain_config(struct pcmcia_device *link);
-
-static int fdomain_probe(struct pcmcia_device *link)
-{
- scsi_info_t *info;
-
- dev_dbg(&link->dev, "fdomain_attach()\n");
-
- /* Create new SCSI device */
- info = kzalloc(sizeof(*info), GFP_KERNEL);
- if (!info)
- return -ENOMEM;
-
- info->p_dev = link;
- link->priv = info;
- link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
- link->config_regs = PRESENT_OPTION;
-
- return fdomain_config(link);
-} /* fdomain_attach */
-
-/*====================================================================*/
-
-static void fdomain_detach(struct pcmcia_device *link)
-{
- dev_dbg(&link->dev, "fdomain_detach\n");
-
- fdomain_release(link);
-
- kfree(link->priv);
-} /* fdomain_detach */
-
-/*====================================================================*/
-
-static int fdomain_config_check(struct pcmcia_device *p_dev, void *priv_data)
-{
- p_dev->io_lines = 10;
- p_dev->resource[0]->end = 0x10;
- p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
- p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
- return pcmcia_request_io(p_dev);
-}
-
-
-static int fdomain_config(struct pcmcia_device *link)
-{
- scsi_info_t *info = link->priv;
- int ret;
- char str[22];
- struct Scsi_Host *host;
-
- dev_dbg(&link->dev, "fdomain_config\n");
-
- ret = pcmcia_loop_config(link, fdomain_config_check, NULL);
- if (ret)
- goto failed;
-
- if (!link->irq)
- goto failed;
- ret = pcmcia_enable_device(link);
- if (ret)
- goto failed;
-
- /* A bad hack... */
- release_region(link->resource[0]->start, resource_size(link->resource[0]));
-
- /* Set configuration options for the fdomain driver */
- sprintf(str, "%d,%d", (unsigned int) link->resource[0]->start, link->irq);
- fdomain_setup(str);
-
- host = __fdomain_16x0_detect(&fdomain_driver_template);
- if (!host) {
- printk(KERN_INFO "fdomain_cs: no SCSI devices found\n");
- goto failed;
- }
-
- if (scsi_add_host(host, NULL))
- goto failed;
- scsi_scan_host(host);
-
- info->host = host;
-
- return 0;
-
-failed:
- fdomain_release(link);
- return -ENODEV;
-} /* fdomain_config */
-
-/*====================================================================*/
-
-static void fdomain_release(struct pcmcia_device *link)
-{
- scsi_info_t *info = link->priv;
-
- dev_dbg(&link->dev, "fdomain_release\n");
-
- scsi_remove_host(info->host);
- pcmcia_disable_device(link);
- scsi_unregister(info->host);
-}
-
-/*====================================================================*/
-
-static int fdomain_resume(struct pcmcia_device *link)
-{
- fdomain_16x0_host_reset(NULL);
-
- return 0;
-}
-
-static const struct pcmcia_device_id fdomain_ids[] = {
- PCMCIA_DEVICE_PROD_ID12("IBM Corp.", "SCSI PCMCIA Card", 0xe3736c88, 0x859cad20),
- PCMCIA_DEVICE_PROD_ID1("SCSI PCMCIA Adapter Card", 0x8dacb57e),
- PCMCIA_DEVICE_PROD_ID12(" SIMPLE TECHNOLOGY Corporation", "SCSI PCMCIA Credit Card Controller", 0x182bdafe, 0xc80d106f),
- PCMCIA_DEVICE_NULL,
-};
-MODULE_DEVICE_TABLE(pcmcia, fdomain_ids);
-
-static struct pcmcia_driver fdomain_cs_driver = {
- .owner = THIS_MODULE,
- .name = "fdomain_cs",
- .probe = fdomain_probe,
- .remove = fdomain_detach,
- .id_table = fdomain_ids,
- .resume = fdomain_resume,
-};
-
-static int __init init_fdomain_cs(void)
-{
- return pcmcia_register_driver(&fdomain_cs_driver);
-}
-
-static void __exit exit_fdomain_cs(void)
-{
- pcmcia_unregister_driver(&fdomain_cs_driver);
-}
-
-module_init(init_fdomain_cs);
-module_exit(exit_fdomain_cs);
diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c
index 201c8de1853d..95530393872d 100644
--- a/drivers/scsi/pmcraid.c
+++ b/drivers/scsi/pmcraid.c
@@ -1025,7 +1025,7 @@ static void pmcraid_get_fwversion_done(struct pmcraid_cmd *cmd)
static void pmcraid_get_fwversion(struct pmcraid_cmd *cmd)
{
struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
- struct pmcraid_ioadl_desc *ioadl = ioarcb->add_data.u.ioadl;
+ struct pmcraid_ioadl_desc *ioadl;
struct pmcraid_instance *pinstance = cmd->drv_inst;
u16 data_size = sizeof(struct pmcraid_inquiry_data);
@@ -3175,7 +3175,7 @@ static int pmcraid_build_ioadl(
struct scsi_cmnd *scsi_cmd = cmd->scsi_cmd;
struct pmcraid_ioarcb *ioarcb = &(cmd->ioa_cb->ioarcb);
- struct pmcraid_ioadl_desc *ioadl = ioarcb->add_data.u.ioadl;
+ struct pmcraid_ioadl_desc *ioadl;
u32 length = scsi_bufflen(scsi_cmd);
@@ -3225,12 +3225,7 @@ static int pmcraid_build_ioadl(
*/
static void pmcraid_free_sglist(struct pmcraid_sglist *sglist)
{
- int i;
-
- for (i = 0; i < sglist->num_sg; i++)
- __free_pages(sg_page(&(sglist->scatterlist[i])),
- sglist->order);
-
+ sgl_free_order(sglist->scatterlist, sglist->order);
kfree(sglist);
}
@@ -3247,50 +3242,20 @@ static void pmcraid_free_sglist(struct pmcraid_sglist *sglist)
static struct pmcraid_sglist *pmcraid_alloc_sglist(int buflen)
{
struct pmcraid_sglist *sglist;
- struct scatterlist *scatterlist;
- struct page *page;
- int num_elem, i, j;
int sg_size;
int order;
- int bsize_elem;
sg_size = buflen / (PMCRAID_MAX_IOADLS - 1);
order = (sg_size > 0) ? get_order(sg_size) : 0;
- bsize_elem = PAGE_SIZE * (1 << order);
-
- /* Determine the actual number of sg entries needed */
- if (buflen % bsize_elem)
- num_elem = (buflen / bsize_elem) + 1;
- else
- num_elem = buflen / bsize_elem;
/* Allocate a scatter/gather list for the DMA */
- sglist = kzalloc(sizeof(struct pmcraid_sglist) +
- (sizeof(struct scatterlist) * (num_elem - 1)),
- GFP_KERNEL);
-
+ sglist = kzalloc(sizeof(struct pmcraid_sglist), GFP_KERNEL);
if (sglist == NULL)
return NULL;
- scatterlist = sglist->scatterlist;
- sg_init_table(scatterlist, num_elem);
sglist->order = order;
- sglist->num_sg = num_elem;
- sg_size = buflen;
-
- for (i = 0; i < num_elem; i++) {
- page = alloc_pages(GFP_KERNEL|GFP_DMA|__GFP_ZERO, order);
- if (!page) {
- for (j = i - 1; j >= 0; j--)
- __free_pages(sg_page(&scatterlist[j]), order);
- kfree(sglist);
- return NULL;
- }
-
- sg_set_page(&scatterlist[i], page,
- sg_size < bsize_elem ? sg_size : bsize_elem, 0);
- sg_size -= bsize_elem;
- }
+ sgl_alloc_order(buflen, order, false,
+ GFP_KERNEL | GFP_DMA | __GFP_ZERO, &sglist->num_sg);
return sglist;
}
@@ -5492,7 +5457,7 @@ static void pmcraid_set_timestamp(struct pmcraid_cmd *cmd)
struct pmcraid_instance *pinstance = cmd->drv_inst;
struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
__be32 time_stamp_len = cpu_to_be32(PMCRAID_TIMESTAMP_LEN);
- struct pmcraid_ioadl_desc *ioadl = ioarcb->add_data.u.ioadl;
+ struct pmcraid_ioadl_desc *ioadl;
u64 timestamp;
timestamp = ktime_get_real_seconds() * 1000;
@@ -5665,7 +5630,7 @@ static void pmcraid_init_res_table(struct pmcraid_cmd *cmd)
static void pmcraid_querycfg(struct pmcraid_cmd *cmd)
{
struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
- struct pmcraid_ioadl_desc *ioadl = ioarcb->add_data.u.ioadl;
+ struct pmcraid_ioadl_desc *ioadl;
struct pmcraid_instance *pinstance = cmd->drv_inst;
__be32 cfg_table_size = cpu_to_be32(sizeof(struct pmcraid_config_table));
diff --git a/drivers/scsi/pmcraid.h b/drivers/scsi/pmcraid.h
index 8bfac72a242b..754ef30927e2 100644
--- a/drivers/scsi/pmcraid.h
+++ b/drivers/scsi/pmcraid.h
@@ -542,8 +542,7 @@ struct pmcraid_sglist {
u32 order;
u32 num_sg;
u32 num_dma_sg;
- u32 buffer_len;
- struct scatterlist scatterlist[1];
+ struct scatterlist *scatterlist;
};
/* page D0 inquiry data of focal point resource */
diff --git a/drivers/scsi/qedf/qedf_dbg.c b/drivers/scsi/qedf/qedf_dbg.c
index e023f5d0dc12..bd1cef25a900 100644
--- a/drivers/scsi/qedf/qedf_dbg.c
+++ b/drivers/scsi/qedf/qedf_dbg.c
@@ -160,7 +160,7 @@ qedf_uevent_emit(struct Scsi_Host *shost, u32 code, char *msg)
switch (code) {
case QEDF_UEVENT_CODE_GRCDUMP:
if (msg)
- strncpy(event_string, msg, strlen(msg));
+ strscpy(event_string, msg, sizeof(event_string));
else
sprintf(event_string, "GRCDUMP=%u", shost->host_no);
break;
diff --git a/drivers/scsi/qedf/qedf_dbg.h b/drivers/scsi/qedf/qedf_dbg.h
index 50083cae84c3..77c27e888969 100644
--- a/drivers/scsi/qedf/qedf_dbg.h
+++ b/drivers/scsi/qedf/qedf_dbg.h
@@ -116,6 +116,14 @@ extern int qedf_create_sysfs_attr(struct Scsi_Host *shost,
extern void qedf_remove_sysfs_attr(struct Scsi_Host *shost,
struct sysfs_bin_attrs *iter);
+struct qedf_debugfs_ops {
+ char *name;
+ struct qedf_list_of_funcs *qedf_funcs;
+};
+
+extern const struct qedf_debugfs_ops qedf_debugfs_ops[];
+extern const struct file_operations qedf_dbg_fops[];
+
#ifdef CONFIG_DEBUG_FS
/* DebugFS related code */
struct qedf_list_of_funcs {
@@ -123,11 +131,6 @@ struct qedf_list_of_funcs {
ssize_t (*oper_func)(struct qedf_dbg_ctx *qedf);
};
-struct qedf_debugfs_ops {
- char *name;
- struct qedf_list_of_funcs *qedf_funcs;
-};
-
#define qedf_dbg_fileops(drv, ops) \
{ \
.owner = THIS_MODULE, \
@@ -147,8 +150,8 @@ struct qedf_debugfs_ops {
}
extern void qedf_dbg_host_init(struct qedf_dbg_ctx *qedf,
- struct qedf_debugfs_ops *dops,
- struct file_operations *fops);
+ const struct qedf_debugfs_ops *dops,
+ const struct file_operations *fops);
extern void qedf_dbg_host_exit(struct qedf_dbg_ctx *qedf);
extern void qedf_dbg_init(char *drv_name);
extern void qedf_dbg_exit(void);
diff --git a/drivers/scsi/qedf/qedf_debugfs.c b/drivers/scsi/qedf/qedf_debugfs.c
index 2b1ef3075e93..c539a7ae3a7e 100644
--- a/drivers/scsi/qedf/qedf_debugfs.c
+++ b/drivers/scsi/qedf/qedf_debugfs.c
@@ -23,8 +23,8 @@ static struct dentry *qedf_dbg_root;
**/
void
qedf_dbg_host_init(struct qedf_dbg_ctx *qedf,
- struct qedf_debugfs_ops *dops,
- struct file_operations *fops)
+ const struct qedf_debugfs_ops *dops,
+ const struct file_operations *fops)
{
char host_dirname[32];
struct dentry *file_dentry = NULL;
@@ -99,7 +99,7 @@ qedf_dbg_exit(void)
qedf_dbg_root = NULL;
}
-struct qedf_debugfs_ops qedf_debugfs_ops[] = {
+const struct qedf_debugfs_ops qedf_debugfs_ops[] = {
{ "fp_int", NULL },
{ "io_trace", NULL },
{ "debug", NULL },
diff --git a/drivers/scsi/qedf/qedf_io.c b/drivers/scsi/qedf/qedf_io.c
index b15e69586a36..50a50c4249d0 100644
--- a/drivers/scsi/qedf/qedf_io.c
+++ b/drivers/scsi/qedf/qedf_io.c
@@ -917,7 +917,7 @@ qedf_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc_cmd)
struct qedf_ctx *qedf = lport_priv(lport);
struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));
struct fc_rport_libfc_priv *rp = rport->dd_data;
- struct qedf_rport *fcport = rport->dd_data;
+ struct qedf_rport *fcport;
struct qedf_ioreq *io_req;
int rc = 0;
int rval;
diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c
index ccd9a08ea030..284ccb566b19 100644
--- a/drivers/scsi/qedf/qedf_main.c
+++ b/drivers/scsi/qedf/qedf_main.c
@@ -23,6 +23,7 @@
#include <linux/if_vlan.h>
#include <linux/cpu.h>
#include "qedf.h"
+#include "qedf_dbg.h"
#include <uapi/linux/pci_regs.h>
const struct qed_fcoe_ops *qed_ops;
@@ -30,9 +31,6 @@ const struct qed_fcoe_ops *qed_ops;
static int qedf_probe(struct pci_dev *pdev, const struct pci_device_id *id);
static void qedf_remove(struct pci_dev *pdev);
-extern struct qedf_debugfs_ops qedf_debugfs_ops;
-extern struct file_operations qedf_dbg_fops;
-
/*
* Driver module parameters.
*/
@@ -3155,8 +3153,8 @@ static int __qedf_probe(struct pci_dev *pdev, int mode)
}
#ifdef CONFIG_DEBUG_FS
- qedf_dbg_host_init(&(qedf->dbg_ctx), &qedf_debugfs_ops,
- &qedf_dbg_fops);
+ qedf_dbg_host_init(&(qedf->dbg_ctx), qedf_debugfs_ops,
+ qedf_dbg_fops);
#endif
/* Start LL2 */
diff --git a/drivers/scsi/qedi/qedi_dbg.h b/drivers/scsi/qedi/qedi_dbg.h
index c55572badfb0..0bc9c31d5a4f 100644
--- a/drivers/scsi/qedi/qedi_dbg.h
+++ b/drivers/scsi/qedi/qedi_dbg.h
@@ -103,7 +103,6 @@ int qedi_create_sysfs_attr(struct Scsi_Host *shost,
void qedi_remove_sysfs_attr(struct Scsi_Host *shost,
struct sysfs_bin_attrs *iter);
-#ifdef CONFIG_DEBUG_FS
/* DebugFS related code */
struct qedi_list_of_funcs {
char *oper_str;
@@ -134,11 +133,10 @@ struct qedi_debugfs_ops {
}
void qedi_dbg_host_init(struct qedi_dbg_ctx *qedi,
- struct qedi_debugfs_ops *dops,
+ const struct qedi_debugfs_ops *dops,
const struct file_operations *fops);
void qedi_dbg_host_exit(struct qedi_dbg_ctx *qedi);
void qedi_dbg_init(char *drv_name);
void qedi_dbg_exit(void);
-#endif /* CONFIG_DEBUG_FS */
#endif /* _QEDI_DBG_H_ */
diff --git a/drivers/scsi/qedi/qedi_debugfs.c b/drivers/scsi/qedi/qedi_debugfs.c
index fd8a1eea3163..fd914ca4149a 100644
--- a/drivers/scsi/qedi/qedi_debugfs.c
+++ b/drivers/scsi/qedi/qedi_debugfs.c
@@ -19,7 +19,7 @@ static struct dentry *qedi_dbg_root;
void
qedi_dbg_host_init(struct qedi_dbg_ctx *qedi,
- struct qedi_debugfs_ops *dops,
+ const struct qedi_debugfs_ops *dops,
const struct file_operations *fops)
{
char host_dirname[32];
@@ -99,7 +99,7 @@ static struct qedi_list_of_funcs qedi_dbg_do_not_recover_ops[] = {
{ NULL, NULL }
};
-struct qedi_debugfs_ops qedi_debugfs_ops[] = {
+const struct qedi_debugfs_ops qedi_debugfs_ops[] = {
{ "gbl_ctx", NULL },
{ "do_not_recover", qedi_dbg_do_not_recover_ops},
{ "io_trace", NULL },
diff --git a/drivers/scsi/qedi/qedi_fw.c b/drivers/scsi/qedi/qedi_fw.c
index d09afe1b567d..25d763ae5d5a 100644
--- a/drivers/scsi/qedi/qedi_fw.c
+++ b/drivers/scsi/qedi/qedi_fw.c
@@ -1882,7 +1882,7 @@ static int qedi_map_scsi_sg(struct qedi_ctx *qedi, struct qedi_cmd *cmd)
bd[bd_count].sge_len = (u16)sg_len;
QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_IO,
- "single-cashed-sgl: bd_count:%d addr=%llx, len=%x",
+ "single-cached-sgl: bd_count:%d addr=%llx, len=%x",
sg_count, addr, sg_len);
return ++bd_count;
diff --git a/drivers/scsi/qedi/qedi_gbl.h b/drivers/scsi/qedi/qedi_gbl.h
index f5b5a31999aa..a2aa06ed1620 100644
--- a/drivers/scsi/qedi/qedi_gbl.h
+++ b/drivers/scsi/qedi/qedi_gbl.h
@@ -23,8 +23,8 @@ extern uint qedi_io_tracing;
extern struct scsi_host_template qedi_host_template;
extern struct iscsi_transport qedi_iscsi_transport;
extern const struct qed_iscsi_ops *qedi_ops;
-extern struct qedi_debugfs_ops qedi_debugfs_ops;
-extern const struct file_operations qedi_dbg_fops;
+extern const struct qedi_debugfs_ops qedi_debugfs_ops[];
+extern const struct file_operations qedi_dbg_fops[];
extern struct device_attribute *qedi_shost_attrs[];
int qedi_alloc_sq(struct qedi_ctx *qedi, struct qedi_endpoint *ep);
diff --git a/drivers/scsi/qedi/qedi_main.c b/drivers/scsi/qedi/qedi_main.c
index f57a94b4f0d9..4da3592aec0f 100644
--- a/drivers/scsi/qedi/qedi_main.c
+++ b/drivers/scsi/qedi/qedi_main.c
@@ -2300,8 +2300,8 @@ static int __qedi_probe(struct pci_dev *pdev, int mode)
}
#ifdef CONFIG_DEBUG_FS
- qedi_dbg_host_init(&qedi->dbg_ctx, &qedi_debugfs_ops,
- &qedi_dbg_fops);
+ qedi_dbg_host_init(&qedi->dbg_ctx, qedi_debugfs_ops,
+ qedi_dbg_fops);
#endif
QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO,
"QLogic FastLinQ iSCSI Module qedi %s, FW %d.%d.%d.%d\n",
diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
index e2d5d3ca0f57..c11a89be292c 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.c
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
@@ -1035,7 +1035,7 @@ qla84xx_updatefw(struct bsg_job *bsg_job)
sg_copy_to_buffer(bsg_job->request_payload.sg_list,
bsg_job->request_payload.sg_cnt, fw_buf, data_len);
- mn = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &mn_dma);
+ mn = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &mn_dma);
if (!mn) {
ql_log(ql_log_warn, vha, 0x7036,
"DMA alloc failed for fw buffer.\n");
@@ -1046,7 +1046,6 @@ qla84xx_updatefw(struct bsg_job *bsg_job)
flag = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
fw_ver = le32_to_cpu(*((uint32_t *)((uint32_t *)fw_buf + 2)));
- memset(mn, 0, sizeof(struct access_chip_84xx));
mn->entry_type = VERIFY_CHIP_IOCB_TYPE;
mn->entry_count = 1;
diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c
index 3e9dc54b89a3..5fd44c50bbac 100644
--- a/drivers/scsi/qla2xxx/qla_dbg.c
+++ b/drivers/scsi/qla2xxx/qla_dbg.c
@@ -14,7 +14,7 @@
* | Module Init and Probe | 0x0193 | 0x0146 |
* | | | 0x015b-0x0160 |
* | | | 0x016e |
- * | Mailbox commands | 0x1205 | 0x11a2-0x11ff |
+ * | Mailbox commands | 0x1206 | 0x11a2-0x11ff |
* | Device Discovery | 0x2134 | 0x210e-0x2116 |
* | | | 0x211a |
* | | | 0x211c-0x2128 |
@@ -60,7 +60,7 @@
* | | | 0xb13c-0xb140 |
* | | | 0xb149 |
* | MultiQ | 0xc010 | |
- * | Misc | 0xd302 | 0xd031-0xd0ff |
+ * | Misc | 0xd303 | 0xd031-0xd0ff |
* | | | 0xd101-0xd1fe |
* | | | 0xd214-0xd2fe |
* | Target Mode | 0xe081 | |
@@ -717,7 +717,7 @@ qla2xxx_dump_post_process(scsi_qla_host_t *vha, int rval)
/**
* qla2300_fw_dump() - Dumps binary data from the 2300 firmware.
- * @ha: HA context
+ * @vha: HA context
* @hardware_locked: Called with the hardware_lock
*/
void
@@ -887,7 +887,7 @@ qla2300_fw_dump_failed:
/**
* qla2100_fw_dump() - Dumps binary data from the 2100/2200 firmware.
- * @ha: HA context
+ * @vha: HA context
* @hardware_locked: Called with the hardware_lock
*/
void
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index c9689f97c307..eb2ec1fb07cb 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -2356,6 +2356,8 @@ typedef struct fc_port {
#define NVME_PRLI_SP_DISCOVERY BIT_3
uint8_t nvme_flag;
#define NVME_FLAG_REGISTERED 4
+#define NVME_FLAG_DELETING 2
+#define NVME_FLAG_RESETTING 1
struct fc_port *conflict;
unsigned char logout_completed;
@@ -2981,8 +2983,14 @@ enum scan_flags_t {
SF_QUEUED = BIT_1,
};
+enum fc4type_t {
+ FS_FC4TYPE_FCP = BIT_0,
+ FS_FC4TYPE_NVME = BIT_1,
+};
+
struct fab_scan_rp {
port_id_t id;
+ enum fc4type_t fc4type;
u8 port_name[8];
u8 node_name[8];
};
@@ -3274,6 +3282,7 @@ struct qla_work_evt {
} nack;
struct {
u8 fc4_type;
+ srb_t *sp;
} gpnft;
} u;
};
@@ -3463,7 +3472,6 @@ struct qla_qpair {
struct work_struct q_work;
struct list_head qp_list_elem; /* vha->qp_list */
struct list_head hints_list;
- struct list_head nvme_done_list;
uint16_t cpuid;
struct qla_tgt_counters tgt_counters;
};
@@ -4281,8 +4289,6 @@ typedef struct scsi_qla_host {
struct nvme_fc_local_port *nvme_local_port;
struct completion nvme_del_done;
struct list_head nvme_rport_list;
- atomic_t nvme_active_aen_cnt;
- uint16_t nvme_last_rptd_aen;
uint16_t fcoe_vlan_id;
uint16_t fcoe_fcf_idx;
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index e9295398050c..3c4c84ed0f0f 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -658,7 +658,7 @@ void qla24xx_handle_gpsc_event(scsi_qla_host_t *, struct event_arg *);
int qla2x00_mgmt_svr_login(scsi_qla_host_t *);
void qla24xx_handle_gffid_event(scsi_qla_host_t *vha, struct event_arg *ea);
int qla24xx_async_gffid(scsi_qla_host_t *vha, fc_port_t *fcport);
-int qla24xx_async_gpnft(scsi_qla_host_t *, u8);
+int qla24xx_async_gpnft(scsi_qla_host_t *, u8, srb_t *);
void qla24xx_async_gpnft_done(scsi_qla_host_t *, srb_t *);
void qla24xx_async_gnnft_done(scsi_qla_host_t *, srb_t *);
int qla24xx_async_gnnid(scsi_qla_host_t *, fc_port_t *);
@@ -896,6 +896,4 @@ void qlt_update_host_map(struct scsi_qla_host *, port_id_t);
void qlt_remove_target_resources(struct qla_hw_data *);
void qlt_clr_qp_table(struct scsi_qla_host *vha);
-void qla_nvme_cmpl_io(struct srb_iocb *);
-
#endif /* _QLA_GBL_H */
diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c
index 403fa096f8c8..9e914f9c3ffb 100644
--- a/drivers/scsi/qla2xxx/qla_gs.c
+++ b/drivers/scsi/qla2xxx/qla_gs.c
@@ -21,11 +21,10 @@ static int qla_async_rsnn_nn(scsi_qla_host_t *);
/**
* qla2x00_prep_ms_iocb() - Prepare common MS/CT IOCB fields for SNS CT query.
- * @ha: HA context
- * @req_size: request size in bytes
- * @rsp_size: response size in bytes
+ * @vha: HA context
+ * @arg: CT arguments
*
- * Returns a pointer to the @ha's ms_iocb.
+ * Returns a pointer to the @vha's ms_iocb.
*/
void *
qla2x00_prep_ms_iocb(scsi_qla_host_t *vha, struct ct_arg *arg)
@@ -61,9 +60,8 @@ qla2x00_prep_ms_iocb(scsi_qla_host_t *vha, struct ct_arg *arg)
/**
* qla24xx_prep_ms_iocb() - Prepare common CT IOCB fields for SNS CT query.
- * @ha: HA context
- * @req_size: request size in bytes
- * @rsp_size: response size in bytes
+ * @vha: HA context
+ * @arg: CT arguments
*
* Returns a pointer to the @ha's ms_iocb.
*/
@@ -101,7 +99,7 @@ qla24xx_prep_ms_iocb(scsi_qla_host_t *vha, struct ct_arg *arg)
/**
* qla2x00_prep_ct_req() - Prepare common CT request fields for SNS query.
- * @ct_req: CT request buffer
+ * @p: CT request buffer
* @cmd: GS command
* @rsp_size: response size in bytes
*
@@ -196,7 +194,7 @@ qla2x00_chk_ms_status(scsi_qla_host_t *vha, ms_iocb_entry_t *ms_pkt,
/**
* qla2x00_ga_nxt() - SNS scan for fabric devices via GA_NXT command.
- * @ha: HA context
+ * @vha: HA context
* @fcport: fcport entry to updated
*
* Returns 0 on success.
@@ -283,7 +281,7 @@ qla2x00_gid_pt_rsp_size(scsi_qla_host_t *vha)
/**
* qla2x00_gid_pt() - SNS scan for fabric devices via GID_PT command.
- * @ha: HA context
+ * @vha: HA context
* @list: switch info entries to populate
*
* NOTE: Non-Nx_Ports are not requested.
@@ -371,7 +369,7 @@ qla2x00_gid_pt(scsi_qla_host_t *vha, sw_info_t *list)
/**
* qla2x00_gpn_id() - SNS Get Port Name (GPN_ID) query.
- * @ha: HA context
+ * @vha: HA context
* @list: switch info entries to populate
*
* Returns 0 on success.
@@ -441,7 +439,7 @@ qla2x00_gpn_id(scsi_qla_host_t *vha, sw_info_t *list)
/**
* qla2x00_gnn_id() - SNS Get Node Name (GNN_ID) query.
- * @ha: HA context
+ * @vha: HA context
* @list: switch info entries to populate
*
* Returns 0 on success.
@@ -583,7 +581,7 @@ err2:
/**
* qla2x00_rft_id() - SNS Register FC-4 TYPEs (RFT_ID) supported by the HBA.
- * @ha: HA context
+ * @vha: HA context
*
* Returns 0 on success.
*/
@@ -675,7 +673,8 @@ done:
/**
* qla2x00_rff_id() - SNS Register FC-4 Features (RFF_ID) supported by the HBA.
- * @ha: HA context
+ * @vha: HA context
+ * @type: not used
*
* Returns 0 on success.
*/
@@ -769,7 +768,7 @@ done:
/**
* qla2x00_rnn_id() - SNS Register Node Name (RNN_ID) of the HBA.
- * @ha: HA context
+ * @vha: HA context
*
* Returns 0 on success.
*/
@@ -874,7 +873,7 @@ qla2x00_get_sym_node_name(scsi_qla_host_t *vha, uint8_t *snn, size_t size)
/**
* qla2x00_rsnn_nn() - SNS Register Symbolic Node Name (RSNN_NN) of the HBA.
- * @ha: HA context
+ * @vha: HA context
*
* Returns 0 on success.
*/
@@ -970,7 +969,7 @@ done:
/**
* qla2x00_prep_sns_cmd() - Prepare common SNS command request fields for query.
- * @ha: HA context
+ * @vha: HA context
* @cmd: GS command
* @scmd_len: Subcommand length
* @data_size: response size in bytes
@@ -1003,7 +1002,7 @@ qla2x00_prep_sns_cmd(scsi_qla_host_t *vha, uint16_t cmd, uint16_t scmd_len,
/**
* qla2x00_sns_ga_nxt() - SNS scan for fabric devices via GA_NXT command.
- * @ha: HA context
+ * @vha: HA context
* @fcport: fcport entry to updated
*
* This command uses the old Exectute SNS Command mailbox routine.
@@ -1067,7 +1066,7 @@ qla2x00_sns_ga_nxt(scsi_qla_host_t *vha, fc_port_t *fcport)
/**
* qla2x00_sns_gid_pt() - SNS scan for fabric devices via GID_PT command.
- * @ha: HA context
+ * @vha: HA context
* @list: switch info entries to populate
*
* This command uses the old Exectute SNS Command mailbox routine.
@@ -1140,7 +1139,7 @@ qla2x00_sns_gid_pt(scsi_qla_host_t *vha, sw_info_t *list)
/**
* qla2x00_sns_gpn_id() - SNS Get Port Name (GPN_ID) query.
- * @ha: HA context
+ * @vha: HA context
* @list: switch info entries to populate
*
* This command uses the old Exectute SNS Command mailbox routine.
@@ -1196,7 +1195,7 @@ qla2x00_sns_gpn_id(scsi_qla_host_t *vha, sw_info_t *list)
/**
* qla2x00_sns_gnn_id() - SNS Get Node Name (GNN_ID) query.
- * @ha: HA context
+ * @vha: HA context
* @list: switch info entries to populate
*
* This command uses the old Exectute SNS Command mailbox routine.
@@ -1259,7 +1258,7 @@ qla2x00_sns_gnn_id(scsi_qla_host_t *vha, sw_info_t *list)
/**
* qla2x00_snd_rft_id() - SNS Register FC-4 TYPEs (RFT_ID) supported by the HBA.
- * @ha: HA context
+ * @vha: HA context
*
* This command uses the old Exectute SNS Command mailbox routine.
*
@@ -1308,8 +1307,7 @@ qla2x00_sns_rft_id(scsi_qla_host_t *vha)
/**
* qla2x00_sns_rnn_id() - SNS Register Node Name (RNN_ID) of the HBA.
- * HBA.
- * @ha: HA context
+ * @vha: HA context
*
* This command uses the old Exectute SNS Command mailbox routine.
*
@@ -1365,7 +1363,7 @@ qla2x00_sns_rnn_id(scsi_qla_host_t *vha)
/**
* qla2x00_mgmt_svr_login() - Login to fabric Management Service.
- * @ha: HA context
+ * @vha: HA context
*
* Returns 0 on success.
*/
@@ -1401,7 +1399,7 @@ qla2x00_mgmt_svr_login(scsi_qla_host_t *vha)
/**
* qla2x00_prep_ms_fdmi_iocb() - Prepare common MS IOCB fields for FDMI query.
- * @ha: HA context
+ * @vha: HA context
* @req_size: request size in bytes
* @rsp_size: response size in bytes
*
@@ -1439,7 +1437,7 @@ qla2x00_prep_ms_fdmi_iocb(scsi_qla_host_t *vha, uint32_t req_size,
/**
* qla24xx_prep_ms_fdmi_iocb() - Prepare common MS IOCB fields for FDMI query.
- * @ha: HA context
+ * @vha: HA context
* @req_size: request size in bytes
* @rsp_size: response size in bytes
*
@@ -1496,7 +1494,7 @@ qla2x00_update_ms_fdmi_iocb(scsi_qla_host_t *vha, uint32_t req_size)
/**
* qla2x00_prep_ct_req() - Prepare common CT request fields for SNS query.
- * @ct_req: CT request buffer
+ * @p: CT request buffer
* @cmd: GS command
* @rsp_size: response size in bytes
*
@@ -1518,8 +1516,8 @@ qla2x00_prep_ct_fdmi_req(struct ct_sns_pkt *p, uint16_t cmd,
}
/**
- * qla2x00_fdmi_rhba() -
- * @ha: HA context
+ * qla2x00_fdmi_rhba() - perform RHBA FDMI registration
+ * @vha: HA context
*
* Returns 0 on success.
*/
@@ -1728,8 +1726,8 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *vha)
}
/**
- * qla2x00_fdmi_rpa() -
- * @ha: HA context
+ * qla2x00_fdmi_rpa() - perform RPA registration
+ * @vha: HA context
*
* Returns 0 on success.
*/
@@ -1940,8 +1938,8 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *vha)
}
/**
- * qla2x00_fdmiv2_rhba() -
- * @ha: HA context
+ * qla2x00_fdmiv2_rhba() - perform RHBA FDMI v2 registration
+ * @vha: HA context
*
* Returns 0 on success.
*/
@@ -2257,7 +2255,7 @@ qla2x00_fdmiv2_rhba(scsi_qla_host_t *vha)
/**
* qla2x00_fdmi_dhba() -
- * @ha: HA context
+ * @vha: HA context
*
* Returns 0 on success.
*/
@@ -2305,7 +2303,7 @@ qla2x00_fdmi_dhba(scsi_qla_host_t *vha)
/**
* qla2x00_fdmiv2_rpa() -
- * @ha: HA context
+ * @vha: HA context
*
* Returns 0 on success.
*/
@@ -2635,7 +2633,7 @@ qla2x00_fdmiv2_rpa(scsi_qla_host_t *vha)
/**
* qla2x00_fdmi_register() -
- * @ha: HA context
+ * @vha: HA context
*
* Returns 0 on success.
*/
@@ -2693,7 +2691,7 @@ out:
/**
* qla2x00_gfpn_id() - SNS Get Fabric Port Name (GFPN_ID) query.
- * @ha: HA context
+ * @vha: HA context
* @list: switch info entries to populate
*
* Returns 0 on success.
@@ -2778,7 +2776,7 @@ qla24xx_prep_ct_fm_req(struct ct_sns_pkt *p, uint16_t cmd,
/**
* qla2x00_gpsc() - FCS Get Port Speed Capabilities (GPSC) query.
- * @ha: HA context
+ * @vha: HA context
* @list: switch info entries to populate
*
* Returns 0 on success.
@@ -2892,7 +2890,7 @@ qla2x00_gpsc(scsi_qla_host_t *vha, sw_info_t *list)
/**
* qla2x00_gff_id() - SNS Get FC-4 Features (GFF_ID) query.
*
- * @ha: HA context
+ * @vha: HA context
* @list: switch info entries to populate
*
*/
@@ -3796,6 +3794,7 @@ int qla24xx_async_gffid(scsi_qla_host_t *vha, fc_port_t *fcport)
sp->gen1 = fcport->rscn_gen;
sp->gen2 = fcport->login_gen;
+ sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
/* CT_IU preamble */
@@ -3814,7 +3813,6 @@ int qla24xx_async_gffid(scsi_qla_host_t *vha, fc_port_t *fcport)
sp->u.iocb_cmd.u.ctarg.rsp_size = GFF_ID_RSP_SIZE;
sp->u.iocb_cmd.u.ctarg.nport_handle = NPH_SNS;
- sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
sp->done = qla24xx_async_gffid_sp_done;
rval = qla2x00_start_sp(sp);
@@ -3862,7 +3860,6 @@ void qla24xx_async_gnnft_done(scsi_qla_host_t *vha, srb_t *sp)
fc_port_t *fcport;
u32 i, rc;
bool found;
- u8 fc4type = sp->gen2;
struct fab_scan_rp *rp;
unsigned long flags;
@@ -3935,7 +3932,7 @@ void qla24xx_async_gnnft_done(scsi_qla_host_t *vha, srb_t *sp)
"%s %d %8phC post new sess\n",
__func__, __LINE__, rp->port_name);
qla24xx_post_newsess_work(vha, &rp->id, rp->port_name,
- rp->node_name, NULL, fc4type);
+ rp->node_name, NULL, rp->fc4type);
}
}
@@ -3973,24 +3970,114 @@ out:
spin_lock_irqsave(&vha->work_lock, flags);
vha->scan.scan_flags &= ~SF_SCANNING;
spin_unlock_irqrestore(&vha->work_lock, flags);
-
- if ((fc4type == FC4_TYPE_FCP_SCSI) && vha->flags.nvme_enabled)
- qla24xx_async_gpnft(vha, FC4_TYPE_NVME);
}
-static void qla2x00_async_gpnft_gnnft_sp_done(void *s, int res)
+static void qla2x00_find_free_fcp_nvme_slot(struct scsi_qla_host *vha,
+ struct srb *sp)
{
- struct srb *sp = s;
- struct scsi_qla_host *vha = sp->vha;
- struct qla_work_evt *e;
+ struct qla_hw_data *ha = vha->hw;
+ int num_fibre_dev = ha->max_fibre_devices;
struct ct_sns_req *ct_req =
(struct ct_sns_req *)sp->u.iocb_cmd.u.ctarg.req;
struct ct_sns_gpnft_rsp *ct_rsp =
(struct ct_sns_gpnft_rsp *)sp->u.iocb_cmd.u.ctarg.rsp;
struct ct_sns_gpn_ft_data *d;
struct fab_scan_rp *rp;
+ u16 cmd = be16_to_cpu(ct_req->command);
+ u8 fc4_type = sp->gen2;
int i, j, k;
+ port_id_t id;
+ u8 found;
+ u64 wwn;
+
+ j = 0;
+ for (i = 0; i < num_fibre_dev; i++) {
+ d = &ct_rsp->entries[i];
+
+ id.b.rsvd_1 = 0;
+ id.b.domain = d->port_id[0];
+ id.b.area = d->port_id[1];
+ id.b.al_pa = d->port_id[2];
+ wwn = wwn_to_u64(d->port_name);
+
+ if (id.b24 == 0 || wwn == 0)
+ continue;
+
+ if (fc4_type == FC4_TYPE_FCP_SCSI) {
+ if (cmd == GPN_FT_CMD) {
+ rp = &vha->scan.l[j];
+ rp->id = id;
+ memcpy(rp->port_name, d->port_name, 8);
+ j++;
+ rp->fc4type = FS_FC4TYPE_FCP;
+ } else {
+ for (k = 0; k < num_fibre_dev; k++) {
+ rp = &vha->scan.l[k];
+ if (id.b24 == rp->id.b24) {
+ memcpy(rp->node_name,
+ d->port_name, 8);
+ break;
+ }
+ }
+ }
+ } else {
+ /* Search if the fibre device supports FC4_TYPE_NVME */
+ if (cmd == GPN_FT_CMD) {
+ found = 0;
+
+ for (k = 0; k < num_fibre_dev; k++) {
+ rp = &vha->scan.l[k];
+ if (!memcmp(rp->port_name,
+ d->port_name, 8)) {
+ /*
+ * Supports FC-NVMe & FCP
+ */
+ rp->fc4type |= FS_FC4TYPE_NVME;
+ found = 1;
+ break;
+ }
+ }
+
+ /* We found new FC-NVMe only port */
+ if (!found) {
+ for (k = 0; k < num_fibre_dev; k++) {
+ rp = &vha->scan.l[k];
+ if (wwn_to_u64(rp->port_name)) {
+ continue;
+ } else {
+ rp->id = id;
+ memcpy(rp->port_name,
+ d->port_name, 8);
+ rp->fc4type =
+ FS_FC4TYPE_NVME;
+ break;
+ }
+ }
+ }
+ } else {
+ for (k = 0; k < num_fibre_dev; k++) {
+ rp = &vha->scan.l[k];
+ if (id.b24 == rp->id.b24) {
+ memcpy(rp->node_name,
+ d->port_name, 8);
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+static void qla2x00_async_gpnft_gnnft_sp_done(void *s, int res)
+{
+ struct srb *sp = s;
+ struct scsi_qla_host *vha = sp->vha;
+ struct qla_work_evt *e;
+ struct ct_sns_req *ct_req =
+ (struct ct_sns_req *)sp->u.iocb_cmd.u.ctarg.req;
u16 cmd = be16_to_cpu(ct_req->command);
+ u8 fc4_type = sp->gen2;
+ unsigned long flags;
/* gen2 field is holding the fc4type */
ql_dbg(ql_dbg_disc, vha, 0xffff,
@@ -4018,40 +4105,51 @@ static void qla2x00_async_gpnft_gnnft_sp_done(void *s, int res)
return;
}
- if (!res) {
- port_id_t id;
- u64 wwn;
+ if (!res)
+ qla2x00_find_free_fcp_nvme_slot(vha, sp);
- j = 0;
- for (i = 0; i < vha->hw->max_fibre_devices; i++) {
- d = &ct_rsp->entries[i];
-
- id.b.rsvd_1 = 0;
- id.b.domain = d->port_id[0];
- id.b.area = d->port_id[1];
- id.b.al_pa = d->port_id[2];
- wwn = wwn_to_u64(d->port_name);
-
- if (id.b24 == 0 || wwn == 0)
- continue;
+ if ((fc4_type == FC4_TYPE_FCP_SCSI) && vha->flags.nvme_enabled &&
+ cmd == GNN_FT_CMD) {
+ del_timer(&sp->u.iocb_cmd.timer);
+ spin_lock_irqsave(&vha->work_lock, flags);
+ vha->scan.scan_flags &= ~SF_SCANNING;
+ spin_unlock_irqrestore(&vha->work_lock, flags);
- if (cmd == GPN_FT_CMD) {
- rp = &vha->scan.l[j];
- rp->id = id;
- memcpy(rp->port_name, d->port_name, 8);
- j++;
- } else {/* GNN_FT_CMD */
- for (k = 0; k < vha->hw->max_fibre_devices;
- k++) {
- rp = &vha->scan.l[k];
- if (id.b24 == rp->id.b24) {
- memcpy(rp->node_name,
- d->port_name, 8);
- break;
- }
- }
+ e = qla2x00_alloc_work(vha, QLA_EVT_GPNFT);
+ if (!e) {
+ /*
+ * please ignore kernel warning. Otherwise,
+ * we have mem leak.
+ */
+ if (sp->u.iocb_cmd.u.ctarg.req) {
+ dma_free_coherent(&vha->hw->pdev->dev,
+ sizeof(struct ct_sns_pkt),
+ sp->u.iocb_cmd.u.ctarg.req,
+ sp->u.iocb_cmd.u.ctarg.req_dma);
+ sp->u.iocb_cmd.u.ctarg.req = NULL;
+ }
+ if (sp->u.iocb_cmd.u.ctarg.rsp) {
+ dma_free_coherent(&vha->hw->pdev->dev,
+ sizeof(struct ct_sns_pkt),
+ sp->u.iocb_cmd.u.ctarg.rsp,
+ sp->u.iocb_cmd.u.ctarg.rsp_dma);
+ sp->u.iocb_cmd.u.ctarg.rsp = NULL;
}
+
+ ql_dbg(ql_dbg_disc, vha, 0xffff,
+ "Async done-%s unable to alloc work element\n",
+ sp->name);
+ sp->free(sp);
+ set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
+ set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+ return;
}
+ e->u.gpnft.fc4_type = FC4_TYPE_NVME;
+ sp->rc = res;
+ e->u.gpnft.sp = sp;
+
+ qla2x00_post_work(vha, e);
+ return;
}
if (cmd == GPN_FT_CMD)
@@ -4102,9 +4200,12 @@ static int qla24xx_async_gnnft(scsi_qla_host_t *vha, struct srb *sp,
int rval = QLA_FUNCTION_FAILED;
struct ct_sns_req *ct_req;
struct ct_sns_pkt *ct_sns;
+ unsigned long flags;
if (!vha->flags.online) {
+ spin_lock_irqsave(&vha->work_lock, flags);
vha->scan.scan_flags &= ~SF_SCANNING;
+ spin_unlock_irqrestore(&vha->work_lock, flags);
goto done_free_sp;
}
@@ -4113,14 +4214,24 @@ static int qla24xx_async_gnnft(scsi_qla_host_t *vha, struct srb *sp,
"%s: req %p rsp %p are not setup\n",
__func__, sp->u.iocb_cmd.u.ctarg.req,
sp->u.iocb_cmd.u.ctarg.rsp);
+ spin_lock_irqsave(&vha->work_lock, flags);
vha->scan.scan_flags &= ~SF_SCANNING;
+ spin_unlock_irqrestore(&vha->work_lock, flags);
WARN_ON(1);
goto done_free_sp;
}
+
+ ql_dbg(ql_dbg_disc, vha, 0xfffff,
+ "%s: FC4Type %x, CT-PASSTRHU %s command ctarg rsp size %d, ctarg req size %d\n",
+ __func__, fc4_type, sp->name, sp->u.iocb_cmd.u.ctarg.rsp_size,
+ sp->u.iocb_cmd.u.ctarg.req_size);
+
sp->type = SRB_CT_PTHRU_CMD;
sp->name = "gnnft";
sp->gen1 = vha->hw->base_qpair->chip_reset;
sp->gen2 = fc4_type;
+
+ sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
memset(sp->u.iocb_cmd.u.ctarg.rsp, 0, sp->u.iocb_cmd.u.ctarg.rsp_size);
@@ -4137,7 +4248,6 @@ static int qla24xx_async_gnnft(scsi_qla_host_t *vha, struct srb *sp,
sp->u.iocb_cmd.u.ctarg.req_size = GNN_FT_REQ_SIZE;
sp->u.iocb_cmd.u.ctarg.nport_handle = NPH_SNS;
- sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
sp->done = qla2x00_async_gpnft_gnnft_sp_done;
rval = qla2x00_start_sp(sp);
@@ -4179,15 +4289,17 @@ void qla24xx_async_gpnft_done(scsi_qla_host_t *vha, srb_t *sp)
}
/* Get WWPN list for certain fc4_type */
-int qla24xx_async_gpnft(scsi_qla_host_t *vha, u8 fc4_type)
+int qla24xx_async_gpnft(scsi_qla_host_t *vha, u8 fc4_type, srb_t *sp)
{
int rval = QLA_FUNCTION_FAILED;
struct ct_sns_req *ct_req;
- srb_t *sp;
struct ct_sns_pkt *ct_sns;
u32 rspsz;
unsigned long flags;
+ ql_dbg(ql_dbg_disc, vha, 0xffff,
+ "%s enter\n", __func__);
+
if (!vha->flags.online)
return rval;
@@ -4200,9 +4312,58 @@ int qla24xx_async_gpnft(scsi_qla_host_t *vha, u8 fc4_type)
vha->scan.scan_flags |= SF_SCANNING;
spin_unlock_irqrestore(&vha->work_lock, flags);
- sp = qla2x00_get_sp(vha, NULL, GFP_KERNEL);
- if (!sp) {
- vha->scan.scan_flags &= ~SF_SCANNING;
+ if (fc4_type == FC4_TYPE_FCP_SCSI) {
+ ql_dbg(ql_dbg_disc, vha, 0xffff,
+ "%s: Performing FCP Scan\n", __func__);
+
+ if (sp)
+ sp->free(sp); /* should not happen */
+
+ sp = qla2x00_get_sp(vha, NULL, GFP_KERNEL);
+ if (!sp) {
+ spin_lock_irqsave(&vha->work_lock, flags);
+ vha->scan.scan_flags &= ~SF_SCANNING;
+ spin_unlock_irqrestore(&vha->work_lock, flags);
+ return rval;
+ }
+
+ sp->u.iocb_cmd.u.ctarg.req = dma_zalloc_coherent(
+ &vha->hw->pdev->dev, sizeof(struct ct_sns_pkt),
+ &sp->u.iocb_cmd.u.ctarg.req_dma, GFP_KERNEL);
+ if (!sp->u.iocb_cmd.u.ctarg.req) {
+ ql_log(ql_log_warn, vha, 0xffff,
+ "Failed to allocate ct_sns request.\n");
+ spin_lock_irqsave(&vha->work_lock, flags);
+ vha->scan.scan_flags &= ~SF_SCANNING;
+ spin_unlock_irqrestore(&vha->work_lock, flags);
+ goto done_free_sp;
+ }
+ sp->u.iocb_cmd.u.ctarg.req_size = GPN_FT_REQ_SIZE;
+
+ rspsz = sizeof(struct ct_sns_gpnft_rsp) +
+ ((vha->hw->max_fibre_devices - 1) *
+ sizeof(struct ct_sns_gpn_ft_data));
+
+ sp->u.iocb_cmd.u.ctarg.rsp = dma_zalloc_coherent(
+ &vha->hw->pdev->dev, rspsz,
+ &sp->u.iocb_cmd.u.ctarg.rsp_dma, GFP_KERNEL);
+ if (!sp->u.iocb_cmd.u.ctarg.rsp) {
+ ql_log(ql_log_warn, vha, 0xffff,
+ "Failed to allocate ct_sns request.\n");
+ spin_lock_irqsave(&vha->work_lock, flags);
+ vha->scan.scan_flags &= ~SF_SCANNING;
+ spin_unlock_irqrestore(&vha->work_lock, flags);
+ goto done_free_sp;
+ }
+ sp->u.iocb_cmd.u.ctarg.rsp_size = rspsz;
+
+ ql_dbg(ql_dbg_disc, vha, 0xffff,
+ "%s scan list size %d\n", __func__, vha->scan.size);
+
+ memset(vha->scan.l, 0, vha->scan.size);
+ } else if (!sp) {
+ ql_dbg(ql_dbg_disc, vha, 0xffff,
+ "NVME scan did not provide SP\n");
return rval;
}
@@ -4210,33 +4371,14 @@ int qla24xx_async_gpnft(scsi_qla_host_t *vha, u8 fc4_type)
sp->name = "gpnft";
sp->gen1 = vha->hw->base_qpair->chip_reset;
sp->gen2 = fc4_type;
- qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
- sp->u.iocb_cmd.u.ctarg.req = dma_zalloc_coherent(&vha->hw->pdev->dev,
- sizeof(struct ct_sns_pkt), &sp->u.iocb_cmd.u.ctarg.req_dma,
- GFP_KERNEL);
- if (!sp->u.iocb_cmd.u.ctarg.req) {
- ql_log(ql_log_warn, vha, 0xffff,
- "Failed to allocate ct_sns request.\n");
- vha->scan.scan_flags &= ~SF_SCANNING;
- goto done_free_sp;
- }
+ sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
+ qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
rspsz = sizeof(struct ct_sns_gpnft_rsp) +
((vha->hw->max_fibre_devices - 1) *
sizeof(struct ct_sns_gpn_ft_data));
- sp->u.iocb_cmd.u.ctarg.rsp = dma_zalloc_coherent(&vha->hw->pdev->dev,
- rspsz, &sp->u.iocb_cmd.u.ctarg.rsp_dma, GFP_KERNEL);
- if (!sp->u.iocb_cmd.u.ctarg.rsp) {
- ql_log(ql_log_warn, vha, 0xffff,
- "Failed to allocate ct_sns request.\n");
- vha->scan.scan_flags &= ~SF_SCANNING;
- goto done_free_sp;
- }
-
- memset(vha->scan.l, 0, vha->scan.size);
-
ct_sns = (struct ct_sns_pkt *)sp->u.iocb_cmd.u.ctarg.req;
/* CT_IU preamble */
ct_req = qla2x00_prep_ct_req(ct_sns, GPN_FT_CMD, rspsz);
@@ -4244,16 +4386,15 @@ int qla24xx_async_gpnft(scsi_qla_host_t *vha, u8 fc4_type)
/* GPN_FT req */
ct_req->req.gpn_ft.port_type = fc4_type;
- sp->u.iocb_cmd.u.ctarg.req_size = GPN_FT_REQ_SIZE;
- sp->u.iocb_cmd.u.ctarg.rsp_size = rspsz;
sp->u.iocb_cmd.u.ctarg.nport_handle = NPH_SNS;
- sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
sp->done = qla2x00_async_gpnft_gnnft_sp_done;
rval = qla2x00_start_sp(sp);
if (rval != QLA_SUCCESS) {
+ spin_lock_irqsave(&vha->work_lock, flags);
vha->scan.scan_flags &= ~SF_SCANNING;
+ spin_unlock_irqrestore(&vha->work_lock, flags);
goto done_free_sp;
}
@@ -4356,6 +4497,7 @@ int qla24xx_async_gnnid(scsi_qla_host_t *vha, fc_port_t *fcport)
sp->gen1 = fcport->rscn_gen;
sp->gen2 = fcport->login_gen;
+ sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
/* CT_IU preamble */
@@ -4377,7 +4519,6 @@ int qla24xx_async_gnnid(scsi_qla_host_t *vha, fc_port_t *fcport)
sp->u.iocb_cmd.u.ctarg.rsp_size = GNN_ID_RSP_SIZE;
sp->u.iocb_cmd.u.ctarg.nport_handle = NPH_SNS;
- sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
sp->done = qla2x00_async_gnnid_sp_done;
rval = qla2x00_start_sp(sp);
@@ -4493,6 +4634,7 @@ int qla24xx_async_gfpnid(scsi_qla_host_t *vha, fc_port_t *fcport)
sp->gen1 = fcport->rscn_gen;
sp->gen2 = fcport->login_gen;
+ sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
/* CT_IU preamble */
@@ -4514,7 +4656,6 @@ int qla24xx_async_gfpnid(scsi_qla_host_t *vha, fc_port_t *fcport)
sp->u.iocb_cmd.u.ctarg.rsp_size = GFPN_ID_RSP_SIZE;
sp->u.iocb_cmd.u.ctarg.nport_handle = NPH_SNS;
- sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
sp->done = qla2x00_async_gfpnid_sp_done;
rval = qla2x00_start_sp(sp);
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index 8d7fab3cd01d..8f55dd44adae 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -183,10 +183,11 @@ qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport,
sp->name = "login";
sp->gen1 = fcport->rscn_gen;
sp->gen2 = fcport->login_gen;
- qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
lio = &sp->u.iocb_cmd;
lio->timeout = qla2x00_async_iocb_timeout;
+ qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+
sp->done = qla2x00_async_login_sp_done;
lio->u.logio.flags |= SRB_LOGIN_COND_PLOGI;
@@ -245,10 +246,11 @@ qla2x00_async_logout(struct scsi_qla_host *vha, fc_port_t *fcport)
sp->type = SRB_LOGOUT_CMD;
sp->name = "logout";
- qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
lio = &sp->u.iocb_cmd;
lio->timeout = qla2x00_async_iocb_timeout;
+ qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+
sp->done = qla2x00_async_logout_sp_done;
rval = qla2x00_start_sp(sp);
if (rval != QLA_SUCCESS)
@@ -307,10 +309,11 @@ qla2x00_async_prlo(struct scsi_qla_host *vha, fc_port_t *fcport)
sp->type = SRB_PRLO_CMD;
sp->name = "prlo";
- qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
lio = &sp->u.iocb_cmd;
lio->timeout = qla2x00_async_iocb_timeout;
+ qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+
sp->done = qla2x00_async_prlo_sp_done;
rval = qla2x00_start_sp(sp);
if (rval != QLA_SUCCESS)
@@ -412,10 +415,11 @@ qla2x00_async_adisc(struct scsi_qla_host *vha, fc_port_t *fcport,
sp->type = SRB_ADISC_CMD;
sp->name = "adisc";
- qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
lio = &sp->u.iocb_cmd;
lio->timeout = qla2x00_async_iocb_timeout;
+ qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+
sp->done = qla2x00_async_adisc_sp_done;
if (data[1] & QLA_LOGIO_LOGIN_RETRIED)
lio->u.logio.flags |= SRB_LOGIN_RETRIED;
@@ -745,6 +749,8 @@ int qla24xx_async_gnl(struct scsi_qla_host *vha, fc_port_t *fcport)
sp->gen1 = fcport->rscn_gen;
sp->gen2 = fcport->login_gen;
+ mbx = &sp->u.iocb_cmd;
+ mbx->timeout = qla2x00_async_iocb_timeout;
qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha)+2);
mb = sp->u.iocb_cmd.u.mbx.out_mb;
@@ -757,9 +763,6 @@ int qla24xx_async_gnl(struct scsi_qla_host *vha, fc_port_t *fcport)
mb[8] = vha->gnl.size;
mb[9] = vha->vp_idx;
- mbx = &sp->u.iocb_cmd;
- mbx->timeout = qla2x00_async_iocb_timeout;
-
sp->done = qla24xx_async_gnl_sp_done;
rval = qla2x00_start_sp(sp);
@@ -875,7 +878,6 @@ qla24xx_async_prli(struct scsi_qla_host *vha, fc_port_t *fcport)
return rval;
if (fcport->fw_login_state == DSC_LS_PLOGI_PEND ||
- fcport->fw_login_state == DSC_LS_PLOGI_COMP ||
fcport->fw_login_state == DSC_LS_PRLI_PEND)
return rval;
@@ -888,10 +890,11 @@ qla24xx_async_prli(struct scsi_qla_host *vha, fc_port_t *fcport)
sp->type = SRB_PRLI_CMD;
sp->name = "prli";
- qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
lio = &sp->u.iocb_cmd;
lio->timeout = qla2x00_async_iocb_timeout;
+ qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+
sp->done = qla2x00_async_prli_sp_done;
lio->u.logio.flags = 0;
@@ -956,6 +959,9 @@ int qla24xx_async_gpdb(struct scsi_qla_host *vha, fc_port_t *fcport, u8 opt)
sp->name = "gpdb";
sp->gen1 = fcport->rscn_gen;
sp->gen2 = fcport->login_gen;
+
+ mbx = &sp->u.iocb_cmd;
+ mbx->timeout = qla2x00_async_iocb_timeout;
qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
pd = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &pd_dma);
@@ -975,8 +981,6 @@ int qla24xx_async_gpdb(struct scsi_qla_host *vha, fc_port_t *fcport, u8 opt)
mb[9] = vha->vp_idx;
mb[10] = opt;
- mbx = &sp->u.iocb_cmd;
- mbx->timeout = qla2x00_async_iocb_timeout;
mbx->u.mbx.in = (void *)pd;
mbx->u.mbx.in_dma = pd_dma;
@@ -1240,6 +1244,11 @@ int qla24xx_fcport_handle_login(struct scsi_qla_host *vha, fc_port_t *fcport)
qla2x00_post_async_adisc_work(vha, fcport, data);
break;
+ case DSC_LOGIN_PEND:
+ if (fcport->fw_login_state == DSC_LS_PLOGI_COMP)
+ qla24xx_post_prli_work(vha, fcport);
+ break;
+
default:
break;
}
@@ -1486,13 +1495,15 @@ qla2x00_async_tm_cmd(fc_port_t *fcport, uint32_t flags, uint32_t lun,
tm_iocb = &sp->u.iocb_cmd;
sp->type = SRB_TM_CMD;
sp->name = "tmf";
+
+ tm_iocb->timeout = qla2x00_tmf_iocb_timeout;
+ init_completion(&tm_iocb->u.tmf.comp);
qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha));
+
tm_iocb->u.tmf.flags = flags;
tm_iocb->u.tmf.lun = lun;
tm_iocb->u.tmf.data = tag;
sp->done = qla2x00_tmf_sp_done;
- tm_iocb->timeout = qla2x00_tmf_iocb_timeout;
- init_completion(&tm_iocb->u.tmf.comp);
rval = qla2x00_start_sp(sp);
if (rval != QLA_SUCCESS)
@@ -1546,8 +1557,8 @@ qla24xx_abort_sp_done(void *ptr, int res)
srb_t *sp = ptr;
struct srb_iocb *abt = &sp->u.iocb_cmd;
- del_timer(&sp->u.iocb_cmd.timer);
- complete(&abt->u.abt.comp);
+ if (del_timer(&sp->u.iocb_cmd.timer))
+ complete(&abt->u.abt.comp);
}
int
@@ -1566,7 +1577,11 @@ qla24xx_async_abort_cmd(srb_t *cmd_sp)
abt_iocb = &sp->u.iocb_cmd;
sp->type = SRB_ABT_CMD;
sp->name = "abort";
+
+ abt_iocb->timeout = qla24xx_abort_iocb_timeout;
+ init_completion(&abt_iocb->u.abt.comp);
qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha));
+
abt_iocb->u.abt.cmd_hndl = cmd_sp->handle;
if (vha->flags.qpairs_available && cmd_sp->qpair)
@@ -1576,8 +1591,6 @@ qla24xx_async_abort_cmd(srb_t *cmd_sp)
abt_iocb->u.abt.req_que_no = cpu_to_le16(vha->req->id);
sp->done = qla24xx_abort_sp_done;
- abt_iocb->timeout = qla24xx_abort_iocb_timeout;
- init_completion(&abt_iocb->u.abt.comp);
rval = qla2x00_start_sp(sp);
if (rval != QLA_SUCCESS)
@@ -1643,6 +1656,13 @@ qla24xx_handle_prli_done_event(struct scsi_qla_host *vha, struct event_arg *ea)
qla24xx_post_gpdb_work(vha, ea->fcport, 0);
break;
default:
+ if ((ea->iop[0] == LSC_SCODE_ELS_REJECT) &&
+ (ea->iop[1] == 0x50000)) { /* reson 5=busy expl:0x0 */
+ set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
+ ea->fcport->fw_login_state = DSC_LS_PLOGI_COMP;
+ break;
+ }
+
if (ea->fcport->n2n_flag) {
ql_dbg(ql_dbg_disc, vha, 0x2118,
"%s %d %8phC post fc4 prli\n",
@@ -2049,7 +2069,7 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha)
/**
* qla2100_pci_config() - Setup ISP21xx PCI configuration registers.
- * @ha: HA context
+ * @vha: HA context
*
* Returns 0 on success.
*/
@@ -2080,7 +2100,7 @@ qla2100_pci_config(scsi_qla_host_t *vha)
/**
* qla2300_pci_config() - Setup ISP23xx PCI configuration registers.
- * @ha: HA context
+ * @vha: HA context
*
* Returns 0 on success.
*/
@@ -2162,7 +2182,7 @@ qla2300_pci_config(scsi_qla_host_t *vha)
/**
* qla24xx_pci_config() - Setup ISP24xx PCI configuration registers.
- * @ha: HA context
+ * @vha: HA context
*
* Returns 0 on success.
*/
@@ -2206,7 +2226,7 @@ qla24xx_pci_config(scsi_qla_host_t *vha)
/**
* qla25xx_pci_config() - Setup ISP25xx PCI configuration registers.
- * @ha: HA context
+ * @vha: HA context
*
* Returns 0 on success.
*/
@@ -2237,7 +2257,7 @@ qla25xx_pci_config(scsi_qla_host_t *vha)
/**
* qla2x00_isp_firmware() - Choose firmware image.
- * @ha: HA context
+ * @vha: HA context
*
* Returns 0 on success.
*/
@@ -2273,7 +2293,7 @@ qla2x00_isp_firmware(scsi_qla_host_t *vha)
/**
* qla2x00_reset_chip() - Reset ISP chip.
- * @ha: HA context
+ * @vha: HA context
*
* Returns 0 on success.
*/
@@ -2417,6 +2437,7 @@ qla2x00_reset_chip(scsi_qla_host_t *vha)
/**
* qla81xx_reset_mpi() - Reset's MPI FW via Write MPI Register MBC.
+ * @vha: HA context
*
* Returns 0 on success.
*/
@@ -2433,7 +2454,7 @@ qla81xx_reset_mpi(scsi_qla_host_t *vha)
/**
* qla24xx_reset_risc() - Perform full reset of ISP24xx RISC.
- * @ha: HA context
+ * @vha: HA context
*
* Returns 0 on success.
*/
@@ -2648,7 +2669,7 @@ acquired:
/**
* qla24xx_reset_chip() - Reset ISP24xx chip.
- * @ha: HA context
+ * @vha: HA context
*
* Returns 0 on success.
*/
@@ -2672,7 +2693,7 @@ qla24xx_reset_chip(scsi_qla_host_t *vha)
/**
* qla2x00_chip_diag() - Test chip for proper operation.
- * @ha: HA context
+ * @vha: HA context
*
* Returns 0 on success.
*/
@@ -2691,8 +2712,8 @@ qla2x00_chip_diag(scsi_qla_host_t *vha)
/* Assume a failed state */
rval = QLA_FUNCTION_FAILED;
- ql_dbg(ql_dbg_init, vha, 0x007b,
- "Testing device at %lx.\n", (u_long)&reg->flash_address);
+ ql_dbg(ql_dbg_init, vha, 0x007b, "Testing device at %p.\n",
+ &reg->flash_address);
spin_lock_irqsave(&ha->hardware_lock, flags);
@@ -2796,7 +2817,7 @@ chip_diag_failed:
/**
* qla24xx_chip_diag() - Test ISP24xx for proper operation.
- * @ha: HA context
+ * @vha: HA context
*
* Returns 0 on success.
*/
@@ -3264,7 +3285,7 @@ out:
/**
* qla2x00_setup_chip() - Load and start RISC firmware.
- * @ha: HA context
+ * @vha: HA context
*
* Returns 0 on success.
*/
@@ -3419,7 +3440,7 @@ failed:
/**
* qla2x00_init_response_q_entries() - Initializes response queue entries.
- * @ha: HA context
+ * @rsp: response queue
*
* Beginning of request ring has initialization control block already built
* by nvram config routine.
@@ -3444,7 +3465,7 @@ qla2x00_init_response_q_entries(struct rsp_que *rsp)
/**
* qla2x00_update_fw_options() - Read and process firmware options.
- * @ha: HA context
+ * @vha: HA context
*
* Returns 0 on success.
*/
@@ -3707,7 +3728,7 @@ qla24xx_config_rings(struct scsi_qla_host *vha)
/**
* qla2x00_init_rings() - Initializes firmware.
- * @ha: HA context
+ * @vha: HA context
*
* Beginning of request ring has initialization control block already built
* by nvram config routine.
@@ -3815,7 +3836,7 @@ next_check:
/**
* qla2x00_fw_ready() - Waits for firmware ready.
- * @ha: HA context
+ * @vha: HA context
*
* Returns 0 on success.
*/
@@ -4483,7 +4504,7 @@ qla2x00_rport_del(void *data)
/**
* qla2x00_alloc_fcport() - Allocate a generic fcport.
- * @ha: HA context
+ * @vha: HA context
* @flags: allocation flags
*
* Returns a pointer to the allocated fcport, or NULL, if none available.
@@ -5027,9 +5048,9 @@ qla2x00_iidma_fcport(scsi_qla_host_t *vha, fc_port_t *fcport)
fcport->port_name, rval, fcport->fp_speed, mb[0], mb[1]);
} else {
ql_dbg(ql_dbg_disc, vha, 0x2005,
- "iIDMA adjusted to %s GB/s on %8phN.\n",
+ "iIDMA adjusted to %s GB/s (%X) on %8phN.\n",
qla2x00_get_link_speed_str(ha, fcport->fp_speed),
- fcport->port_name);
+ fcport->fp_speed, fcport->port_name);
}
}
@@ -5109,13 +5130,14 @@ qla2x00_update_fcport(scsi_qla_host_t *vha, fc_port_t *fcport)
fcport->deleted = 0;
fcport->logout_on_delete = 1;
+ qla2x00_set_fcport_state(fcport, FCS_ONLINE);
+ qla2x00_iidma_fcport(vha, fcport);
+
if (fcport->fc4f_nvme) {
qla_nvme_register_remote(vha, fcport);
return;
}
- qla2x00_set_fcport_state(fcport, FCS_ONLINE);
- qla2x00_iidma_fcport(vha, fcport);
qla24xx_update_fcport_fcp_prio(vha, fcport);
reg_port:
@@ -5254,8 +5276,8 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha)
qlt_do_generation_tick(vha, &discovery_gen);
if (USE_ASYNC_SCAN(ha)) {
- rval = QLA_SUCCESS;
- rval = qla24xx_async_gpnft(vha, FC4_TYPE_FCP_SCSI);
+ rval = qla24xx_async_gpnft(vha, FC4_TYPE_FCP_SCSI,
+ NULL);
if (rval)
set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
} else {
@@ -5518,6 +5540,14 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha)
break;
}
+ if (fcport->fc4f_nvme) {
+ if (fcport->disc_state == DSC_DELETE_PEND) {
+ fcport->disc_state = DSC_GNL;
+ vha->fcport_count--;
+ fcport->login_succ = 0;
+ }
+ }
+
if (found) {
spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
continue;
@@ -8398,7 +8428,6 @@ struct qla_qpair *qla2xxx_create_qpair(struct scsi_qla_host *vha, int qos,
qpair->vp_idx = vp_idx;
qpair->fw_started = ha->flags.fw_started;
INIT_LIST_HEAD(&qpair->hints_list);
- INIT_LIST_HEAD(&qpair->nvme_done_list);
qpair->chip_reset = ha->base_qpair->chip_reset;
qpair->enable_class_2 = ha->base_qpair->enable_class_2;
qpair->enable_explicit_conf =
diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h
index 4d32426393c7..37ae0f6d8ae5 100644
--- a/drivers/scsi/qla2xxx/qla_inline.h
+++ b/drivers/scsi/qla2xxx/qla_inline.h
@@ -10,6 +10,7 @@
* qla24xx_calc_iocbs() - Determine number of Command Type 3 and
* Continuation Type 1 IOCBs to allocate.
*
+ * @vha: HA context
* @dsds: number of data segment decriptors needed
*
* Returns the number of IOCB entries needed to store @dsds.
@@ -271,13 +272,13 @@ qla2x00_init_timer(srb_t *sp, unsigned long tmo)
{
timer_setup(&sp->u.iocb_cmd.timer, qla2x00_sp_timeout, 0);
sp->u.iocb_cmd.timer.expires = jiffies + tmo * HZ;
- add_timer(&sp->u.iocb_cmd.timer);
sp->free = qla2x00_sp_free;
init_completion(&sp->comp);
if (IS_QLAFX00(sp->vha->hw) && (sp->type == SRB_FXIOCB_DCMD))
init_completion(&sp->u.iocb_cmd.u.fxiocb.fxiocb_comp);
if (sp->type == SRB_ELS_DCMD)
init_completion(&sp->u.iocb_cmd.u.els_logo.comp);
+ add_timer(&sp->u.iocb_cmd.timer);
}
static inline int
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index 8d00d559bd26..a91cca52b5d5 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -14,7 +14,7 @@
/**
* qla2x00_get_cmd_direction() - Determine control_flag data direction.
- * @cmd: SCSI command
+ * @sp: SCSI command
*
* Returns the proper CF_* direction based on CDB.
*/
@@ -86,7 +86,7 @@ qla2x00_calc_iocbs_64(uint16_t dsds)
/**
* qla2x00_prep_cont_type0_iocb() - Initialize a Continuation Type 0 IOCB.
- * @ha: HA context
+ * @vha: HA context
*
* Returns a pointer to the Continuation Type 0 IOCB packet.
*/
@@ -114,7 +114,8 @@ qla2x00_prep_cont_type0_iocb(struct scsi_qla_host *vha)
/**
* qla2x00_prep_cont_type1_iocb() - Initialize a Continuation Type 1 IOCB.
- * @ha: HA context
+ * @vha: HA context
+ * @req: request queue
*
* Returns a pointer to the continuation type 1 IOCB packet.
*/
@@ -445,6 +446,8 @@ queuing_error:
/**
* qla2x00_start_iocbs() - Execute the IOCB command
+ * @vha: HA context
+ * @req: request queue
*/
void
qla2x00_start_iocbs(struct scsi_qla_host *vha, struct req_que *req)
@@ -486,7 +489,9 @@ qla2x00_start_iocbs(struct scsi_qla_host *vha, struct req_que *req)
/**
* qla2x00_marker() - Send a marker IOCB to the firmware.
- * @ha: HA context
+ * @vha: HA context
+ * @req: request queue
+ * @rsp: response queue
* @loop_id: loop ID
* @lun: LUN
* @type: marker modifier
@@ -1190,6 +1195,8 @@ qla24xx_walk_and_build_prot_sglist(struct qla_hw_data *ha, srb_t *sp,
* @sp: SRB command to process
* @cmd_pkt: Command type 3 IOCB
* @tot_dsds: Total number of segments to transfer
+ * @tot_prot_dsds:
+ * @fw_prot_opts:
*/
inline int
qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt,
@@ -1203,7 +1210,6 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt,
uint32_t dif_bytes;
uint8_t bundling = 1;
uint16_t blk_size;
- uint8_t *clr_ptr;
struct crc_context *crc_ctx_pkt = NULL;
struct qla_hw_data *ha;
uint8_t additional_fcpcdb_len;
@@ -1245,15 +1251,11 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt,
/* Allocate CRC context from global pool */
crc_ctx_pkt = sp->u.scmd.ctx =
- dma_pool_alloc(ha->dl_dma_pool, GFP_ATOMIC, &crc_ctx_dma);
+ dma_pool_zalloc(ha->dl_dma_pool, GFP_ATOMIC, &crc_ctx_dma);
if (!crc_ctx_pkt)
goto crc_queuing_error;
- /* Zero out CTX area. */
- clr_ptr = (uint8_t *)crc_ctx_pkt;
- memset(clr_ptr, 0, sizeof(*crc_ctx_pkt));
-
crc_ctx_pkt->crc_ctx_dma = crc_ctx_dma;
sp->flags |= SRB_CRC_CTX_DMA_VALID;
@@ -2458,8 +2460,8 @@ qla24xx_els_dcmd_iocb(scsi_qla_host_t *vha, int els_opcode,
sp->type = SRB_ELS_DCMD;
sp->name = "ELS_DCMD";
sp->fcport = fcport;
- qla2x00_init_timer(sp, ELS_DCMD_TIMEOUT);
elsio->timeout = qla2x00_els_dcmd_iocb_timeout;
+ qla2x00_init_timer(sp, ELS_DCMD_TIMEOUT);
sp->done = qla2x00_els_dcmd_sp_done;
sp->free = qla2x00_els_dcmd_sp_free;
@@ -2656,8 +2658,11 @@ qla24xx_els_dcmd2_iocb(scsi_qla_host_t *vha, int els_opcode,
sp->type = SRB_ELS_DCMD;
sp->name = "ELS_DCMD";
sp->fcport = fcport;
- qla2x00_init_timer(sp, ELS_DCMD_TIMEOUT);
+
elsio->timeout = qla2x00_els_dcmd2_iocb_timeout;
+ init_completion(&elsio->u.els_plogi.comp);
+ qla2x00_init_timer(sp, ELS_DCMD_TIMEOUT);
+
sp->done = qla2x00_els_dcmd2_sp_done;
sp->free = qla2x00_els_dcmd2_sp_free;
@@ -2694,7 +2699,6 @@ qla24xx_els_dcmd2_iocb(scsi_qla_host_t *vha, int els_opcode,
ql_dump_buffer(ql_dbg_io + ql_dbg_buffer, vha, 0x0109,
(uint8_t *)elsio->u.els_plogi.els_plogi_pyld, 0x70);
- init_completion(&elsio->u.els_plogi.comp);
rval = qla2x00_start_sp(sp);
if (rval != QLA_SUCCESS) {
rval = QLA_FUNCTION_FAILED;
@@ -3067,7 +3071,7 @@ sufficient_dsds:
}
memset(ctx, 0, sizeof(struct ct6_dsd));
- ctx->fcp_cmnd = dma_pool_alloc(ha->fcp_cmnd_dma_pool,
+ ctx->fcp_cmnd = dma_pool_zalloc(ha->fcp_cmnd_dma_pool,
GFP_ATOMIC, &ctx->fcp_cmnd_dma);
if (!ctx->fcp_cmnd) {
ql_log(ql_log_fatal, vha, 0x3011,
@@ -3120,7 +3124,6 @@ sufficient_dsds:
host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun));
/* build FCP_CMND IU */
- memset(ctx->fcp_cmnd, 0, sizeof(struct fcp_cmnd));
int_to_scsilun(cmd->device->lun, &ctx->fcp_cmnd->lun);
ctx->fcp_cmnd->additional_cdb_len = additional_cdb_len;
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index 89f93ebd819d..a3dc83f9444d 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -259,7 +259,7 @@ qla2300_intr_handler(int irq, void *dev_id)
/**
* qla2x00_mbx_completion() - Process mailbox command completions.
- * @ha: SCSI driver HA context
+ * @vha: SCSI driver HA context
* @mb0: Mailbox0 register
*/
static void
@@ -613,7 +613,8 @@ qla2x00_find_fcport_by_nportid(scsi_qla_host_t *vha, port_id_t *id,
/**
* qla2x00_async_event() - Process aynchronous events.
- * @ha: SCSI driver HA context
+ * @vha: SCSI driver HA context
+ * @rsp: response queue
* @mb: Mailbox registers (0 - 3)
*/
void
@@ -767,7 +768,6 @@ skip_rio:
case MBA_LIP_OCCURRED: /* Loop Initialization Procedure */
ha->flags.lip_ae = 1;
- ha->flags.n2n_ae = 0;
ql_dbg(ql_dbg_async, vha, 0x5009,
"LIP occurred (%x).\n", mb[1]);
@@ -811,7 +811,6 @@ skip_rio:
case MBA_LOOP_DOWN: /* Loop Down Event */
SAVE_TOPO(ha);
- ha->flags.n2n_ae = 0;
ha->flags.lip_ae = 0;
ha->current_topology = 0;
@@ -885,7 +884,6 @@ skip_rio:
/* case MBA_DCBX_COMPLETE: */
case MBA_POINT_TO_POINT: /* Point-to-Point */
ha->flags.lip_ae = 0;
- ha->flags.n2n_ae = 1;
if (IS_QLA2100(ha))
break;
@@ -1256,7 +1254,8 @@ global_port_update:
/**
* qla2x00_process_completed_request() - Process a Fast Post response.
- * @ha: SCSI driver HA context
+ * @vha: SCSI driver HA context
+ * @req: request queue
* @index: SRB index
*/
void
@@ -1839,31 +1838,23 @@ qla24xx_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, void *tsk)
sp->done(sp, 0);
}
-static void
-qla24xx_nvme_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, void *tsk)
+static void qla24xx_nvme_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
+ void *tsk, srb_t *sp)
{
- const char func[] = "NVME-IOCB";
fc_port_t *fcport;
- srb_t *sp;
struct srb_iocb *iocb;
struct sts_entry_24xx *sts = (struct sts_entry_24xx *)tsk;
uint16_t state_flags;
struct nvmefc_fcp_req *fd;
uint16_t ret = 0;
- struct srb_iocb *nvme;
-
- sp = qla2x00_get_sp_from_handle(vha, func, req, tsk);
- if (!sp)
- return;
iocb = &sp->u.iocb_cmd;
fcport = sp->fcport;
iocb->u.nvme.comp_status = le16_to_cpu(sts->comp_status);
state_flags = le16_to_cpu(sts->state_flags);
fd = iocb->u.nvme.desc;
- nvme = &sp->u.iocb_cmd;
- if (unlikely(nvme->u.nvme.aen_op))
+ if (unlikely(iocb->u.nvme.aen_op))
atomic_dec(&sp->vha->hw->nvme_active_aen_cnt);
/*
@@ -1897,42 +1888,30 @@ qla24xx_nvme_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, void *tsk)
fd->transferred_length = fd->payload_length -
le32_to_cpu(sts->residual_len);
- /*
- * If transport error then Failure (HBA rejects request)
- * otherwise transport will handle.
- */
- if (sts->entry_status) {
- ql_log(ql_log_warn, fcport->vha, 0x5038,
- "NVME-%s error - hdl=%x entry-status(%x).\n",
- sp->name, sp->handle, sts->entry_status);
+ switch (le16_to_cpu(sts->comp_status)) {
+ case CS_COMPLETE:
+ ret = QLA_SUCCESS;
+ break;
+ case CS_ABORTED:
+ case CS_RESET:
+ case CS_PORT_UNAVAILABLE:
+ case CS_PORT_LOGGED_OUT:
+ case CS_PORT_BUSY:
+ ql_log(ql_log_warn, fcport->vha, 0x5060,
+ "NVME-%s ERR Handling - hdl=%x completion status(%x) resid=%x ox_id=%x\n",
+ sp->name, sp->handle, sts->comp_status,
+ le32_to_cpu(sts->residual_len), sts->ox_id);
+ fd->transferred_length = 0;
+ iocb->u.nvme.rsp_pyld_len = 0;
+ ret = QLA_ABORTED;
+ break;
+ default:
+ ql_log(ql_log_warn, fcport->vha, 0x5060,
+ "NVME-%s error - hdl=%x completion status(%x) resid=%x ox_id=%x\n",
+ sp->name, sp->handle, sts->comp_status,
+ le32_to_cpu(sts->residual_len), sts->ox_id);
ret = QLA_FUNCTION_FAILED;
- } else {
- switch (le16_to_cpu(sts->comp_status)) {
- case CS_COMPLETE:
- ret = 0;
- break;
-
- case CS_ABORTED:
- case CS_RESET:
- case CS_PORT_UNAVAILABLE:
- case CS_PORT_LOGGED_OUT:
- case CS_PORT_BUSY:
- ql_log(ql_log_warn, fcport->vha, 0x5060,
- "NVME-%s ERR Handling - hdl=%x completion status(%x) resid=%x ox_id=%x\n",
- sp->name, sp->handle, sts->comp_status,
- le32_to_cpu(sts->residual_len), sts->ox_id);
- fd->transferred_length = fd->payload_length;
- ret = QLA_ABORTED;
- break;
-
- default:
- ql_log(ql_log_warn, fcport->vha, 0x5060,
- "NVME-%s error - hdl=%x completion status(%x) resid=%x ox_id=%x\n",
- sp->name, sp->handle, sts->comp_status,
- le32_to_cpu(sts->residual_len), sts->ox_id);
- ret = QLA_FUNCTION_FAILED;
- break;
- }
+ break;
}
sp->done(sp, ret);
}
@@ -1970,7 +1949,7 @@ static void qla_ctrlvp_completed(scsi_qla_host_t *vha, struct req_que *req,
/**
* qla2x00_process_response_queue() - Process response queue entries.
- * @ha: SCSI driver HA context
+ * @rsp: response queue
*/
void
qla2x00_process_response_queue(struct rsp_que *rsp)
@@ -2195,7 +2174,7 @@ qla2x00_handle_dif_error(srb_t *sp, struct sts_entry_24xx *sts24)
0x10, 0x1);
set_driver_byte(cmd, DRIVER_SENSE);
set_host_byte(cmd, DID_ABORT);
- cmd->result |= SAM_STAT_CHECK_CONDITION << 1;
+ cmd->result |= SAM_STAT_CHECK_CONDITION;
return 1;
}
@@ -2205,7 +2184,7 @@ qla2x00_handle_dif_error(srb_t *sp, struct sts_entry_24xx *sts24)
0x10, 0x3);
set_driver_byte(cmd, DRIVER_SENSE);
set_host_byte(cmd, DID_ABORT);
- cmd->result |= SAM_STAT_CHECK_CONDITION << 1;
+ cmd->result |= SAM_STAT_CHECK_CONDITION;
return 1;
}
@@ -2215,7 +2194,7 @@ qla2x00_handle_dif_error(srb_t *sp, struct sts_entry_24xx *sts24)
0x10, 0x2);
set_driver_byte(cmd, DRIVER_SENSE);
set_host_byte(cmd, DID_ABORT);
- cmd->result |= SAM_STAT_CHECK_CONDITION << 1;
+ cmd->result |= SAM_STAT_CHECK_CONDITION;
return 1;
}
@@ -2368,13 +2347,14 @@ done:
bsg_job->reply_len = sizeof(struct fc_bsg_reply);
/* Always return DID_OK, bsg will send the vendor specific response
* in this case only */
- sp->done(sp, DID_OK << 6);
+ sp->done(sp, DID_OK << 16);
}
/**
* qla2x00_status_entry() - Process a Status IOCB entry.
- * @ha: SCSI driver HA context
+ * @vha: SCSI driver HA context
+ * @rsp: response queue
* @pkt: Entry pointer
*/
static void
@@ -2459,7 +2439,8 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
/* NVME completion. */
if (sp->type == SRB_NVME_CMD) {
- qla24xx_nvme_iocb_entry(vha, req, pkt);
+ req->outstanding_cmds[handle] = NULL;
+ qla24xx_nvme_iocb_entry(vha, req, pkt, sp);
return;
}
@@ -2751,7 +2732,7 @@ out:
/**
* qla2x00_status_cont_entry() - Process a Status Continuations entry.
- * @ha: SCSI driver HA context
+ * @rsp: response queue
* @pkt: Entry pointer
*
* Extended sense data.
@@ -2809,7 +2790,8 @@ qla2x00_status_cont_entry(struct rsp_que *rsp, sts_cont_entry_t *pkt)
/**
* qla2x00_error_entry() - Process an error entry.
- * @ha: SCSI driver HA context
+ * @vha: SCSI driver HA context
+ * @rsp: response queue
* @pkt: Entry pointer
* return : 1=allow further error analysis. 0=no additional error analysis.
*/
@@ -2868,7 +2850,7 @@ fatal:
/**
* qla24xx_mbx_completion() - Process mailbox command completions.
- * @ha: SCSI driver HA context
+ * @vha: SCSI driver HA context
* @mb0: Mailbox0 register
*/
static void
@@ -2937,7 +2919,8 @@ void qla24xx_nvme_ls4_iocb(struct scsi_qla_host *vha,
/**
* qla24xx_process_response_queue() - Process response queue entries.
- * @ha: SCSI driver HA context
+ * @vha: SCSI driver HA context
+ * @rsp: response queue
*/
void qla24xx_process_response_queue(struct scsi_qla_host *vha,
struct rsp_que *rsp)
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 7397aeddd96c..d8a36c13aeda 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -503,11 +503,19 @@ mbx_done:
}
pr_warn(" cmd=%x ****\n", command);
}
- ql_dbg(ql_dbg_mbx, vha, 0x1198,
- "host_status=%#x intr_ctrl=%#x intr_status=%#x\n",
- RD_REG_DWORD(&reg->isp24.host_status),
- RD_REG_DWORD(&reg->isp24.ictrl),
- RD_REG_DWORD(&reg->isp24.istatus));
+ if (IS_FWI2_CAPABLE(ha) && !(IS_P3P_TYPE(ha))) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1198,
+ "host_status=%#x intr_ctrl=%#x intr_status=%#x\n",
+ RD_REG_DWORD(&reg->isp24.host_status),
+ RD_REG_DWORD(&reg->isp24.ictrl),
+ RD_REG_DWORD(&reg->isp24.istatus));
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x1206,
+ "ctrl_status=%#x ictrl=%#x istatus=%#x\n",
+ RD_REG_WORD(&reg->isp.ctrl_status),
+ RD_REG_WORD(&reg->isp.ictrl),
+ RD_REG_WORD(&reg->isp.istatus));
+ }
} else {
ql_dbg(ql_dbg_mbx, base_vha, 0x1021, "Done %s.\n", __func__);
}
@@ -1025,9 +1033,12 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha)
* FW supports nvme and driver load parameter requested nvme.
* BIT 26 of fw_attributes indicates NVMe support.
*/
- if ((ha->fw_attributes_h & 0x400) && ql2xnvmeenable)
+ if ((ha->fw_attributes_h & 0x400) && ql2xnvmeenable) {
vha->flags.nvme_enabled = 1;
-
+ ql_log(ql_log_info, vha, 0xd302,
+ "%s: FC-NVMe is Enabled (0x%x)\n",
+ __func__, ha->fw_attributes_h);
+ }
}
if (IS_QLA27XX(ha)) {
@@ -3385,7 +3396,10 @@ qla8044_read_serdes_word(scsi_qla_host_t *vha, uint32_t addr, uint32_t *data)
/**
* qla2x00_set_serdes_params() -
- * @ha: HA context
+ * @vha: HA context
+ * @sw_em_1g:
+ * @sw_em_2g:
+ * @sw_em_4g:
*
* Returns
*/
@@ -3744,6 +3758,7 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
id.b.area = rptid_entry->port_id[1];
id.b.al_pa = rptid_entry->port_id[0];
id.b.rsvd_1 = 0;
+ ha->flags.n2n_ae = 0;
if (rptid_entry->format == 0) {
/* loop */
@@ -3796,6 +3811,7 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
set_bit(N2N_LOGIN_NEEDED, &vha->dpc_flags);
set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags);
set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);
+ ha->flags.n2n_ae = 1;
return;
}
@@ -3872,6 +3888,7 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
vha->d_id.b.area = rptid_entry->port_id[1];
vha->d_id.b.al_pa = rptid_entry->port_id[0];
+ ha->flags.n2n_ae = 1;
spin_lock_irqsave(&ha->vport_slock, flags);
qlt_update_vp_map(vha, SET_AL_PA);
spin_unlock_irqrestore(&ha->vport_slock, flags);
@@ -6006,14 +6023,14 @@ int qla24xx_send_mb_cmd(struct scsi_qla_host *vha, mbx_cmd_t *mcp)
sp->type = SRB_MB_IOCB;
sp->name = mb_to_str(mcp->mb[0]);
- qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
-
- memcpy(sp->u.iocb_cmd.u.mbx.out_mb, mcp->mb, SIZEOF_IOCB_MB_REG);
-
c = &sp->u.iocb_cmd;
c->timeout = qla2x00_async_iocb_timeout;
init_completion(&c->u.mbx.comp);
+ qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+
+ memcpy(sp->u.iocb_cmd.u.mbx.out_mb, mcp->mb, SIZEOF_IOCB_MB_REG);
+
sp->done = qla2x00_async_mb_sp_done;
rval = qla2x00_start_sp(sp);
diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
index e965b16f21e3..f6f0a759a7c2 100644
--- a/drivers/scsi/qla2xxx/qla_mid.c
+++ b/drivers/scsi/qla2xxx/qla_mid.c
@@ -778,18 +778,12 @@ static void qla_do_work(struct work_struct *work)
struct qla_qpair *qpair = container_of(work, struct qla_qpair, q_work);
struct scsi_qla_host *vha;
struct qla_hw_data *ha = qpair->hw;
- struct srb_iocb *nvme, *nxt_nvme;
spin_lock_irqsave(&qpair->qp_lock, flags);
vha = pci_get_drvdata(ha->pdev);
qla24xx_process_response_queue(vha, qpair->rsp);
spin_unlock_irqrestore(&qpair->qp_lock, flags);
- list_for_each_entry_safe(nvme, nxt_nvme, &qpair->nvme_done_list,
- u.nvme.entry) {
- list_del_init(&nvme->u.nvme.entry);
- qla_nvme_cmpl_io(nvme);
- }
}
/* create response queue */
@@ -935,8 +929,8 @@ int qla24xx_control_vp(scsi_qla_host_t *vha, int cmd)
sp->type = SRB_CTRL_VP;
sp->name = "ctrl_vp";
sp->done = qla_ctrlvp_sp_done;
- qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
+ qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
sp->u.iocb_cmd.u.ctrlvp.cmd = cmd;
sp->u.iocb_cmd.u.ctrlvp.vp_index = vp_index;
diff --git a/drivers/scsi/qla2xxx/qla_mr.c b/drivers/scsi/qla2xxx/qla_mr.c
index d5da3981cefe..521a51370554 100644
--- a/drivers/scsi/qla2xxx/qla_mr.c
+++ b/drivers/scsi/qla2xxx/qla_mr.c
@@ -490,7 +490,7 @@ qlafx00_mbx_reg_test(scsi_qla_host_t *vha)
/**
* qlafx00_pci_config() - Setup ISPFx00 PCI configuration registers.
- * @ha: HA context
+ * @vha: HA context
*
* Returns 0 on success.
*/
@@ -519,9 +519,9 @@ qlafx00_pci_config(scsi_qla_host_t *vha)
/**
* qlafx00_warm_reset() - Perform warm reset of iSA(CPUs being reset on SOC).
- * @ha: HA context
+ * @vha: HA context
*
- */
+ */
static inline void
qlafx00_soc_cpu_reset(scsi_qla_host_t *vha)
{
@@ -625,7 +625,7 @@ qlafx00_soc_cpu_reset(scsi_qla_host_t *vha)
/**
* qlafx00_soft_reset() - Soft Reset ISPFx00.
- * @ha: HA context
+ * @vha: HA context
*
* Returns 0 on success.
*/
@@ -644,7 +644,7 @@ qlafx00_soft_reset(scsi_qla_host_t *vha)
/**
* qlafx00_chip_diag() - Test ISPFx00 for proper operation.
- * @ha: HA context
+ * @vha: HA context
*
* Returns 0 on success.
*/
@@ -1408,7 +1408,7 @@ qlafx00_abort_isp_cleanup(scsi_qla_host_t *vha, bool critemp)
/**
* qlafx00_init_response_q_entries() - Initializes response queue entries.
- * @ha: HA context
+ * @rsp: response queue
*
* Beginning of request ring has initialization control block already built
* by nvram config routine.
@@ -1821,9 +1821,11 @@ qlafx00_fx_disc(scsi_qla_host_t *vha, fc_port_t *fcport, uint16_t fx_type)
sp->type = SRB_FXIOCB_DCMD;
sp->name = "fxdisc";
- qla2x00_init_timer(sp, FXDISC_TIMEOUT);
fdisc = &sp->u.iocb_cmd;
+ fdisc->timeout = qla2x00_fxdisc_iocb_timeout;
+ qla2x00_init_timer(sp, FXDISC_TIMEOUT);
+
switch (fx_type) {
case FXDISC_GET_CONFIG_INFO:
fdisc->u.fxiocb.flags =
@@ -1924,7 +1926,6 @@ qlafx00_fx_disc(scsi_qla_host_t *vha, fc_port_t *fcport, uint16_t fx_type)
goto done_unmap_req;
}
- fdisc->timeout = qla2x00_fxdisc_iocb_timeout;
fdisc->u.fxiocb.req_func_type = cpu_to_le16(fx_type);
sp->done = qla2x00_fxdisc_sp_done;
@@ -2269,7 +2270,8 @@ qlafx00_ioctl_iosb_entry(scsi_qla_host_t *vha, struct req_que *req,
/**
* qlafx00_status_entry() - Process a Status IOCB entry.
- * @ha: SCSI driver HA context
+ * @vha: SCSI driver HA context
+ * @rsp: response queue
* @pkt: Entry pointer
*/
static void
@@ -2542,7 +2544,7 @@ check_scsi_status:
/**
* qlafx00_status_cont_entry() - Process a Status Continuations entry.
- * @ha: SCSI driver HA context
+ * @rsp: response queue
* @pkt: Entry pointer
*
* Extended sense data.
@@ -2620,7 +2622,9 @@ qlafx00_status_cont_entry(struct rsp_que *rsp, sts_cont_entry_t *pkt)
/**
* qlafx00_multistatus_entry() - Process Multi response queue entries.
- * @ha: SCSI driver HA context
+ * @vha: SCSI driver HA context
+ * @rsp: response queue
+ * @pkt:
*/
static void
qlafx00_multistatus_entry(struct scsi_qla_host *vha,
@@ -2674,8 +2678,11 @@ qlafx00_multistatus_entry(struct scsi_qla_host *vha,
/**
* qlafx00_error_entry() - Process an error entry.
- * @ha: SCSI driver HA context
+ * @vha: SCSI driver HA context
+ * @rsp: response queue
* @pkt: Entry pointer
+ * @estatus:
+ * @etype:
*/
static void
qlafx00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp,
@@ -2705,7 +2712,8 @@ qlafx00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp,
/**
* qlafx00_process_response_queue() - Process response queue entries.
- * @ha: SCSI driver HA context
+ * @vha: SCSI driver HA context
+ * @rsp: response queue
*/
static void
qlafx00_process_response_queue(struct scsi_qla_host *vha,
@@ -2781,7 +2789,7 @@ qlafx00_process_response_queue(struct scsi_qla_host *vha,
/**
* qlafx00_async_event() - Process aynchronous events.
- * @ha: SCSI driver HA context
+ * @vha: SCSI driver HA context
*/
static void
qlafx00_async_event(scsi_qla_host_t *vha)
@@ -2857,10 +2865,9 @@ qlafx00_async_event(scsi_qla_host_t *vha)
}
/**
- *
* qlafx00x_mbx_completion() - Process mailbox command completions.
- * @ha: SCSI driver HA context
- * @mb16: Mailbox16 register
+ * @vha: SCSI driver HA context
+ * @mb0:
*/
static void
qlafx00_mbx_completion(scsi_qla_host_t *vha, uint32_t mb0)
diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c
index 6b33a1f24f56..c5a963c2c86e 100644
--- a/drivers/scsi/qla2xxx/qla_nvme.c
+++ b/drivers/scsi/qla2xxx/qla_nvme.c
@@ -16,15 +16,13 @@ static void qla_nvme_unregister_remote_port(struct work_struct *);
int qla_nvme_register_remote(struct scsi_qla_host *vha, struct fc_port *fcport)
{
- struct nvme_rport *rport;
+ struct qla_nvme_rport *rport;
+ struct nvme_fc_port_info req;
int ret;
if (!IS_ENABLED(CONFIG_NVME_FC))
return 0;
- if (fcport->nvme_flag & NVME_FLAG_REGISTERED)
- return 0;
-
if (!vha->flags.nvme_enabled) {
ql_log(ql_log_info, vha, 0x2100,
"%s: Not registering target since Host NVME is not enabled\n",
@@ -33,38 +31,36 @@ int qla_nvme_register_remote(struct scsi_qla_host *vha, struct fc_port *fcport)
}
if (!(fcport->nvme_prli_service_param &
- (NVME_PRLI_SP_TARGET | NVME_PRLI_SP_DISCOVERY)))
+ (NVME_PRLI_SP_TARGET | NVME_PRLI_SP_DISCOVERY)) ||
+ (fcport->nvme_flag & NVME_FLAG_REGISTERED))
return 0;
INIT_WORK(&fcport->nvme_del_work, qla_nvme_unregister_remote_port);
- rport = kzalloc(sizeof(*rport), GFP_KERNEL);
- if (!rport) {
- ql_log(ql_log_warn, vha, 0x2101,
- "%s: unable to alloc memory\n", __func__);
- return -ENOMEM;
- }
+ fcport->nvme_flag &= ~NVME_FLAG_RESETTING;
- rport->req.port_name = wwn_to_u64(fcport->port_name);
- rport->req.node_name = wwn_to_u64(fcport->node_name);
- rport->req.port_role = 0;
+ memset(&req, 0, sizeof(struct nvme_fc_port_info));
+ req.port_name = wwn_to_u64(fcport->port_name);
+ req.node_name = wwn_to_u64(fcport->node_name);
+ req.port_role = 0;
+ req.dev_loss_tmo = NVME_FC_DEV_LOSS_TMO;
if (fcport->nvme_prli_service_param & NVME_PRLI_SP_INITIATOR)
- rport->req.port_role = FC_PORT_ROLE_NVME_INITIATOR;
+ req.port_role = FC_PORT_ROLE_NVME_INITIATOR;
if (fcport->nvme_prli_service_param & NVME_PRLI_SP_TARGET)
- rport->req.port_role |= FC_PORT_ROLE_NVME_TARGET;
+ req.port_role |= FC_PORT_ROLE_NVME_TARGET;
if (fcport->nvme_prli_service_param & NVME_PRLI_SP_DISCOVERY)
- rport->req.port_role |= FC_PORT_ROLE_NVME_DISCOVERY;
+ req.port_role |= FC_PORT_ROLE_NVME_DISCOVERY;
- rport->req.port_id = fcport->d_id.b24;
+ req.port_id = fcport->d_id.b24;
ql_log(ql_log_info, vha, 0x2102,
"%s: traddr=nn-0x%016llx:pn-0x%016llx PortID:%06x\n",
- __func__, rport->req.node_name, rport->req.port_name,
- rport->req.port_id);
+ __func__, req.node_name, req.port_name,
+ req.port_id);
- ret = nvme_fc_register_remoteport(vha->nvme_local_port, &rport->req,
+ ret = nvme_fc_register_remoteport(vha->nvme_local_port, &req,
&fcport->nvme_remote_port);
if (ret) {
ql_log(ql_log_warn, vha, 0x212e,
@@ -73,10 +69,11 @@ int qla_nvme_register_remote(struct scsi_qla_host *vha, struct fc_port *fcport)
return ret;
}
- fcport->nvme_remote_port->private = fcport;
- fcport->nvme_flag |= NVME_FLAG_REGISTERED;
+ rport = fcport->nvme_remote_port->private;
rport->fcport = fcport;
list_add_tail(&rport->list, &vha->nvme_rport_list);
+
+ fcport->nvme_flag |= NVME_FLAG_REGISTERED;
return 0;
}
@@ -113,8 +110,6 @@ static int qla_nvme_alloc_queue(struct nvme_fc_local_port *lport,
return 0;
}
- ql_log(ql_log_warn, vha, 0xffff,
- "allocating q for idx=%x w/o cpu mask\n", qidx);
qpair = qla2xxx_create_qpair(vha, 5, vha->vp_idx, true);
if (qpair == NULL) {
ql_log(ql_log_warn, vha, 0x2122,
@@ -154,16 +149,6 @@ static void qla_nvme_sp_ls_done(void *ptr, int res)
qla2x00_rel_sp(sp);
}
-void qla_nvme_cmpl_io(struct srb_iocb *nvme)
-{
- srb_t *sp;
- struct nvmefc_fcp_req *fd = nvme->u.nvme.desc;
-
- sp = container_of(nvme, srb_t, u.iocb_cmd);
- fd->done(fd);
- qla2xxx_rel_qpair_sp(sp->qpair, sp);
-}
-
static void qla_nvme_sp_done(void *ptr, int res)
{
srb_t *sp = ptr;
@@ -176,36 +161,42 @@ static void qla_nvme_sp_done(void *ptr, int res)
if (!atomic_dec_and_test(&sp->ref_count))
return;
- if (!(sp->fcport->nvme_flag & NVME_FLAG_REGISTERED))
- goto rel;
-
- if (unlikely(res == QLA_FUNCTION_FAILED))
- fd->status = NVME_SC_INTERNAL;
- else
+ if (res == QLA_SUCCESS)
fd->status = 0;
+ else
+ fd->status = NVME_SC_INTERNAL;
fd->rcv_rsplen = nvme->u.nvme.rsp_pyld_len;
- list_add_tail(&nvme->u.nvme.entry, &sp->qpair->nvme_done_list);
- return;
-rel:
+ fd->done(fd);
qla2xxx_rel_qpair_sp(sp->qpair, sp);
+
+ return;
}
-static void qla_nvme_ls_abort(struct nvme_fc_local_port *lport,
- struct nvme_fc_remote_port *rport, struct nvmefc_ls_req *fd)
+static void qla_nvme_abort_work(struct work_struct *work)
{
- struct nvme_private *priv = fd->private;
- fc_port_t *fcport = rport->private;
+ struct nvme_private *priv =
+ container_of(work, struct nvme_private, abort_work);
srb_t *sp = priv->sp;
- int rval;
+ fc_port_t *fcport = sp->fcport;
struct qla_hw_data *ha = fcport->vha->hw;
+ int rval;
rval = ha->isp_ops->abort_command(sp);
ql_dbg(ql_dbg_io, fcport->vha, 0x212b,
- "%s: %s LS command for sp=%p on fcport=%p rval=%x\n", __func__,
- (rval != QLA_SUCCESS) ? "Failed to abort" : "Aborted",
- sp, fcport, rval);
+ "%s: %s command for sp=%p, handle=%x on fcport=%p rval=%x\n",
+ __func__, (rval != QLA_SUCCESS) ? "Failed to abort" : "Aborted",
+ sp, sp->handle, fcport, rval);
+}
+
+static void qla_nvme_ls_abort(struct nvme_fc_local_port *lport,
+ struct nvme_fc_remote_port *rport, struct nvmefc_ls_req *fd)
+{
+ struct nvme_private *priv = fd->private;
+
+ INIT_WORK(&priv->abort_work, qla_nvme_abort_work);
+ schedule_work(&priv->abort_work);
}
static void qla_nvme_ls_complete(struct work_struct *work)
@@ -220,7 +211,8 @@ static void qla_nvme_ls_complete(struct work_struct *work)
static int qla_nvme_ls_req(struct nvme_fc_local_port *lport,
struct nvme_fc_remote_port *rport, struct nvmefc_ls_req *fd)
{
- fc_port_t *fcport = rport->private;
+ struct qla_nvme_rport *qla_rport = rport->private;
+ fc_port_t *fcport = qla_rport->fcport;
struct srb_iocb *nvme;
struct nvme_private *priv = fd->private;
struct scsi_qla_host *vha;
@@ -228,9 +220,6 @@ static int qla_nvme_ls_req(struct nvme_fc_local_port *lport,
struct qla_hw_data *ha;
srb_t *sp;
- if (!(fcport->nvme_flag & NVME_FLAG_REGISTERED))
- return rval;
-
vha = fcport->vha;
ha = vha->hw;
/* Alloc SRB structure */
@@ -275,32 +264,23 @@ static void qla_nvme_fcp_abort(struct nvme_fc_local_port *lport,
struct nvmefc_fcp_req *fd)
{
struct nvme_private *priv = fd->private;
- srb_t *sp = priv->sp;
- int rval;
- fc_port_t *fcport = rport->private;
- struct qla_hw_data *ha = fcport->vha->hw;
-
- rval = ha->isp_ops->abort_command(sp);
- ql_dbg(ql_dbg_io, fcport->vha, 0x2127,
- "%s: %s command for sp=%p on fcport=%p rval=%x\n", __func__,
- (rval != QLA_SUCCESS) ? "Failed to abort" : "Aborted",
- sp, fcport, rval);
+ INIT_WORK(&priv->abort_work, qla_nvme_abort_work);
+ schedule_work(&priv->abort_work);
}
static void qla_nvme_poll(struct nvme_fc_local_port *lport, void *hw_queue_handle)
{
- struct scsi_qla_host *vha = lport->private;
- unsigned long flags;
struct qla_qpair *qpair = hw_queue_handle;
+ unsigned long flags;
+ struct scsi_qla_host *vha = lport->private;
- /* Acquire ring specific lock */
spin_lock_irqsave(&qpair->qp_lock, flags);
qla24xx_process_response_queue(vha, qpair->rsp);
spin_unlock_irqrestore(&qpair->qp_lock, flags);
}
-static int qla2x00_start_nvme_mq(srb_t *sp)
+static inline int qla2x00_start_nvme_mq(srb_t *sp)
{
unsigned long flags;
uint32_t *clr_ptr;
@@ -313,7 +293,6 @@ static int qla2x00_start_nvme_mq(srb_t *sp)
uint16_t avail_dsds;
uint32_t *cur_dsd;
struct req_que *req = NULL;
- struct rsp_que *rsp = NULL;
struct scsi_qla_host *vha = sp->fcport->vha;
struct qla_hw_data *ha = vha->hw;
struct qla_qpair *qpair = sp->qpair;
@@ -322,15 +301,13 @@ static int qla2x00_start_nvme_mq(srb_t *sp)
struct nvmefc_fcp_req *fd = nvme->u.nvme.desc;
uint32_t rval = QLA_SUCCESS;
+ /* Setup qpair pointers */
+ req = qpair->req;
tot_dsds = fd->sg_cnt;
/* Acquire qpair specific lock */
spin_lock_irqsave(&qpair->qp_lock, flags);
- /* Setup qpair pointers */
- req = qpair->req;
- rsp = qpair->rsp;
-
/* Check for room in outstanding command list. */
handle = req->current_outstanding_cmd;
for (index = 1; index < req->num_outstanding_cmds; index++) {
@@ -342,7 +319,7 @@ static int qla2x00_start_nvme_mq(srb_t *sp)
}
if (index == req->num_outstanding_cmds) {
- rval = -1;
+ rval = -EBUSY;
goto queuing_error;
}
req_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
@@ -356,7 +333,7 @@ static int qla2x00_start_nvme_mq(srb_t *sp)
req->cnt = req->length - (req->ring_index - cnt);
if (req->cnt < (req_cnt + 2)){
- rval = -1;
+ rval = -EBUSY;
goto queuing_error;
}
}
@@ -365,7 +342,7 @@ static int qla2x00_start_nvme_mq(srb_t *sp)
struct nvme_fc_cmd_iu *cmd = fd->cmdaddr;
if (cmd->sqe.common.opcode == nvme_admin_async_event) {
nvme->u.nvme.aen_op = 1;
- atomic_inc(&vha->hw->nvme_active_aen_cnt);
+ atomic_inc(&ha->nvme_active_aen_cnt);
}
}
@@ -478,11 +455,6 @@ static int qla2x00_start_nvme_mq(srb_t *sp)
/* Set chip new ring index. */
WRT_REG_DWORD(req->req_q_in, req->ring_index);
- /* Manage unprocessed RIO/ZIO commands in response queue. */
- if (vha->flags.process_response_queue &&
- rsp->ring_ptr->signature != RESPONSE_PROCESSED)
- qla24xx_process_response_queue(vha, rsp);
-
queuing_error:
spin_unlock_irqrestore(&qpair->qp_lock, flags);
return rval;
@@ -496,31 +468,44 @@ static int qla_nvme_post_cmd(struct nvme_fc_local_port *lport,
fc_port_t *fcport;
struct srb_iocb *nvme;
struct scsi_qla_host *vha;
- int rval = QLA_FUNCTION_FAILED;
+ int rval = -ENODEV;
srb_t *sp;
struct qla_qpair *qpair = hw_queue_handle;
struct nvme_private *priv;
+ struct qla_nvme_rport *qla_rport = rport->private;
- if (!fd) {
- ql_log(ql_log_warn, NULL, 0x2134, "NO NVMe FCP request\n");
+ if (!fd || !qpair) {
+ ql_log(ql_log_warn, NULL, 0x2134,
+ "NO NVMe request or Queue Handle\n");
return rval;
}
priv = fd->private;
- fcport = rport->private;
+ fcport = qla_rport->fcport;
if (!fcport) {
ql_log(ql_log_warn, NULL, 0x210e, "No fcport ptr\n");
return rval;
}
vha = fcport->vha;
- if ((!qpair) || (!(fcport->nvme_flag & NVME_FLAG_REGISTERED)))
+
+ if (test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags))
+ return rval;
+
+ /*
+ * If we know the dev is going away while the transport is still sending
+ * IO's return busy back to stall the IO Q. This happens when the
+ * link goes away and fw hasn't notified us yet, but IO's are being
+ * returned. If the dev comes back quickly we won't exhaust the IO
+ * retry count at the core.
+ */
+ if (fcport->nvme_flag & NVME_FLAG_RESETTING)
return -EBUSY;
/* Alloc SRB structure */
sp = qla2xxx_get_qpair_sp(qpair, fcport, GFP_ATOMIC);
if (!sp)
- return -EIO;
+ return -EBUSY;
atomic_set(&sp->ref_count, 1);
init_waitqueue_head(&sp->nvme_ls_waitq);
@@ -538,7 +523,6 @@ static int qla_nvme_post_cmd(struct nvme_fc_local_port *lport,
"qla2x00_start_nvme_mq failed = %d\n", rval);
atomic_dec(&sp->ref_count);
wake_up(&sp->nvme_ls_waitq);
- return -EIO;
}
return rval;
@@ -557,22 +541,27 @@ static void qla_nvme_localport_delete(struct nvme_fc_local_port *lport)
static void qla_nvme_remoteport_delete(struct nvme_fc_remote_port *rport)
{
fc_port_t *fcport;
- struct nvme_rport *r_port, *trport;
+ struct qla_nvme_rport *qla_rport = rport->private, *trport;
- fcport = rport->private;
+ fcport = qla_rport->fcport;
fcport->nvme_remote_port = NULL;
fcport->nvme_flag &= ~NVME_FLAG_REGISTERED;
- list_for_each_entry_safe(r_port, trport,
+ list_for_each_entry_safe(qla_rport, trport,
&fcport->vha->nvme_rport_list, list) {
- if (r_port->fcport == fcport) {
- list_del(&r_port->list);
+ if (qla_rport->fcport == fcport) {
+ list_del(&qla_rport->list);
break;
}
}
- kfree(r_port);
complete(&fcport->nvme_del_done);
+ if (!test_bit(UNLOADING, &fcport->vha->dpc_flags)) {
+ INIT_WORK(&fcport->free_work, qlt_free_session_done);
+ schedule_work(&fcport->free_work);
+ }
+
+ fcport->nvme_flag &= ~(NVME_FLAG_REGISTERED | NVME_FLAG_DELETING);
ql_log(ql_log_info, fcport->vha, 0x2110,
"remoteport_delete of %p completed.\n", fcport);
}
@@ -592,7 +581,7 @@ static struct nvme_fc_port_template qla_nvme_fc_transport = {
.max_dif_sgl_segments = 64,
.dma_boundary = 0xFFFFFFFF,
.local_priv_sz = 8,
- .remote_priv_sz = 0,
+ .remote_priv_sz = sizeof(struct qla_nvme_rport),
.lsrqst_priv_sz = sizeof(struct nvme_private),
.fcprqst_priv_sz = sizeof(struct nvme_private),
};
@@ -611,37 +600,25 @@ static int qla_nvme_wait_on_command(srb_t *sp)
return ret;
}
-static int qla_nvme_wait_on_rport_del(fc_port_t *fcport)
-{
- int ret = QLA_SUCCESS;
- int timeout;
-
- timeout = wait_for_completion_timeout(&fcport->nvme_del_done,
- msecs_to_jiffies(2000));
- if (!timeout) {
- ret = QLA_FUNCTION_FAILED;
- ql_log(ql_log_info, fcport->vha, 0x2111,
- "timed out waiting for fcport=%p to delete\n", fcport);
- }
-
- return ret;
-}
-
-void qla_nvme_abort(struct qla_hw_data *ha, struct srb *sp)
+void qla_nvme_abort(struct qla_hw_data *ha, struct srb *sp, int res)
{
int rval;
- rval = ha->isp_ops->abort_command(sp);
- if (!rval && !qla_nvme_wait_on_command(sp))
- ql_log(ql_log_warn, NULL, 0x2112,
- "nvme_wait_on_comand timed out waiting on sp=%p\n", sp);
+ if (!test_bit(ABORT_ISP_ACTIVE, &sp->vha->dpc_flags)) {
+ rval = ha->isp_ops->abort_command(sp);
+ if (!rval && !qla_nvme_wait_on_command(sp))
+ ql_log(ql_log_warn, NULL, 0x2112,
+ "timed out waiting on sp=%p\n", sp);
+ } else {
+ sp->done(sp, res);
+ }
}
static void qla_nvme_unregister_remote_port(struct work_struct *work)
{
struct fc_port *fcport = container_of(work, struct fc_port,
nvme_del_work);
- struct nvme_rport *rport, *trport;
+ struct qla_nvme_rport *qla_rport, *trport;
if (!IS_ENABLED(CONFIG_NVME_FC))
return;
@@ -649,51 +626,53 @@ static void qla_nvme_unregister_remote_port(struct work_struct *work)
ql_log(ql_log_warn, NULL, 0x2112,
"%s: unregister remoteport on %p\n",__func__, fcport);
- list_for_each_entry_safe(rport, trport,
+ list_for_each_entry_safe(qla_rport, trport,
&fcport->vha->nvme_rport_list, list) {
- if (rport->fcport == fcport) {
+ if (qla_rport->fcport == fcport) {
ql_log(ql_log_info, fcport->vha, 0x2113,
"%s: fcport=%p\n", __func__, fcport);
init_completion(&fcport->nvme_del_done);
nvme_fc_unregister_remoteport(
fcport->nvme_remote_port);
- qla_nvme_wait_on_rport_del(fcport);
+ wait_for_completion(&fcport->nvme_del_done);
+ break;
}
}
}
void qla_nvme_delete(struct scsi_qla_host *vha)
{
- struct nvme_rport *rport, *trport;
+ struct qla_nvme_rport *qla_rport, *trport;
fc_port_t *fcport;
int nv_ret;
if (!IS_ENABLED(CONFIG_NVME_FC))
return;
- list_for_each_entry_safe(rport, trport, &vha->nvme_rport_list, list) {
- fcport = rport->fcport;
+ list_for_each_entry_safe(qla_rport, trport,
+ &vha->nvme_rport_list, list) {
+ fcport = qla_rport->fcport;
ql_log(ql_log_info, fcport->vha, 0x2114, "%s: fcport=%p\n",
__func__, fcport);
+ nvme_fc_set_remoteport_devloss(fcport->nvme_remote_port, 0);
init_completion(&fcport->nvme_del_done);
nvme_fc_unregister_remoteport(fcport->nvme_remote_port);
- qla_nvme_wait_on_rport_del(fcport);
+ wait_for_completion(&fcport->nvme_del_done);
}
if (vha->nvme_local_port) {
init_completion(&vha->nvme_del_done);
+ ql_log(ql_log_info, vha, 0x2116,
+ "unregister localport=%p\n",
+ vha->nvme_local_port);
nv_ret = nvme_fc_unregister_localport(vha->nvme_local_port);
- if (nv_ret == 0)
- ql_log(ql_log_info, vha, 0x2116,
- "unregistered localport=%p\n",
- vha->nvme_local_port);
- else
+ if (nv_ret)
ql_log(ql_log_info, vha, 0x2115,
"Unregister of localport failed\n");
- wait_for_completion_timeout(&vha->nvme_del_done,
- msecs_to_jiffies(5000));
+ else
+ wait_for_completion(&vha->nvme_del_done);
}
}
diff --git a/drivers/scsi/qla2xxx/qla_nvme.h b/drivers/scsi/qla2xxx/qla_nvme.h
index 7f05fa1c77db..816854ada654 100644
--- a/drivers/scsi/qla2xxx/qla_nvme.h
+++ b/drivers/scsi/qla2xxx/qla_nvme.h
@@ -14,6 +14,9 @@
#include "qla_def.h"
+/* default dev loss time (seconds) before transport tears down ctrl */
+#define NVME_FC_DEV_LOSS_TMO 30
+
#define NVME_ATIO_CMD_OFF 32
#define NVME_FIRST_PACKET_CMDLEN (64 - NVME_ATIO_CMD_OFF)
#define Q2T_NVME_NUM_TAGS 2048
@@ -28,11 +31,11 @@ struct nvme_private {
struct srb *sp;
struct nvmefc_ls_req *fd;
struct work_struct ls_work;
+ struct work_struct abort_work;
int comp_status;
};
-struct nvme_rport {
- struct nvme_fc_port_info req;
+struct qla_nvme_rport {
struct list_head list;
struct fc_port *fcport;
};
@@ -142,7 +145,7 @@ struct pt_ls4_rx_unsol {
void qla_nvme_register_hba(struct scsi_qla_host *);
int qla_nvme_register_remote(struct scsi_qla_host *, struct fc_port *);
void qla_nvme_delete(struct scsi_qla_host *);
-void qla_nvme_abort(struct qla_hw_data *, struct srb *sp);
+void qla_nvme_abort(struct qla_hw_data *, struct srb *sp, int res);
void qla24xx_nvme_ls4_iocb(struct scsi_qla_host *, struct pt_ls4_request *,
struct req_que *);
void qla24xx_async_gffid_sp_done(void *, int);
diff --git a/drivers/scsi/qla2xxx/qla_nx.c b/drivers/scsi/qla2xxx/qla_nx.c
index a77c33987703..872d66dd79cd 100644
--- a/drivers/scsi/qla2xxx/qla_nx.c
+++ b/drivers/scsi/qla2xxx/qla_nx.c
@@ -1732,7 +1732,7 @@ iospace_error_exit:
/**
* qla82xx_pci_config() - Setup ISP82xx PCI configuration registers.
- * @ha: HA context
+ * @vha: HA context
*
* Returns 0 on success.
*/
@@ -1753,7 +1753,7 @@ qla82xx_pci_config(scsi_qla_host_t *vha)
/**
* qla82xx_reset_chip() - Setup ISP82xx PCI configuration registers.
- * @ha: HA context
+ * @vha: HA context
*
* Returns 0 on success.
*/
@@ -2008,11 +2008,10 @@ qla82xx_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0)
"MBX pointer ERROR.\n");
}
-/*
+/**
* qla82xx_intr_handler() - Process interrupts for the ISP23xx and ISP63xx.
* @irq:
* @dev_id: SCSI driver HA context
- * @regs:
*
* Called by system whenever the host adapter generates an interrupt.
*
diff --git a/drivers/scsi/qla2xxx/qla_nx2.c b/drivers/scsi/qla2xxx/qla_nx2.c
index 525ac35a757b..3a2b0282df14 100644
--- a/drivers/scsi/qla2xxx/qla_nx2.c
+++ b/drivers/scsi/qla2xxx/qla_nx2.c
@@ -280,9 +280,8 @@ qla8044_clear_qsnt_ready(struct scsi_qla_host *vha)
}
/**
- *
* qla8044_lock_recovery - Recovers the idc_lock.
- * @ha : Pointer to adapter structure
+ * @vha : Pointer to adapter structure
*
* Lock Recovery Register
* 5-2 Lock recovery owner: Function ID of driver doing lock recovery,
@@ -1639,10 +1638,10 @@ qla8044_set_rst_ready(struct scsi_qla_host *vha)
/**
* qla8044_need_reset_handler - Code to start reset sequence
- * @ha: pointer to adapter structure
+ * @vha: pointer to adapter structure
*
* Note: IDC lock must be held upon entry
- **/
+ */
static void
qla8044_need_reset_handler(struct scsi_qla_host *vha)
{
@@ -1859,8 +1858,8 @@ exit_update_idc_reg:
/**
* qla8044_need_qsnt_handler - Code to start qsnt
- * @ha: pointer to adapter structure
- **/
+ * @vha: pointer to adapter structure
+ */
static void
qla8044_need_qsnt_handler(struct scsi_qla_host *vha)
{
@@ -2031,10 +2030,10 @@ exit_error:
/**
* qla4_8xxx_check_temp - Check the ISP82XX temperature.
- * @ha: adapter block pointer.
+ * @vha: adapter block pointer.
*
* Note: The caller should not hold the idc lock.
- **/
+ */
static int
qla8044_check_temp(struct scsi_qla_host *vha)
{
@@ -2071,10 +2070,10 @@ int qla8044_read_temperature(scsi_qla_host_t *vha)
/**
* qla8044_check_fw_alive - Check firmware health
- * @ha: Pointer to host adapter structure.
+ * @vha: Pointer to host adapter structure.
*
* Context: Interrupt
- **/
+ */
int
qla8044_check_fw_alive(struct scsi_qla_host *vha)
{
diff --git a/drivers/scsi/qla2xxx/qla_nx2.h b/drivers/scsi/qla2xxx/qla_nx2.h
index 83c1b7e17c80..8ba7c1db07c3 100644
--- a/drivers/scsi/qla2xxx/qla_nx2.h
+++ b/drivers/scsi/qla2xxx/qla_nx2.h
@@ -23,10 +23,6 @@
#define MD_MIU_TEST_AGT_WRDATA_HI 0x410000A4
#define MD_MIU_TEST_AGT_WRDATA_ULO 0x410000B0
#define MD_MIU_TEST_AGT_WRDATA_UHI 0x410000B4
-#define MD_MIU_TEST_AGT_RDDATA_LO 0x410000A8
-#define MD_MIU_TEST_AGT_RDDATA_HI 0x410000AC
-#define MD_MIU_TEST_AGT_RDDATA_ULO 0x410000B8
-#define MD_MIU_TEST_AGT_RDDATA_UHI 0x410000BC
/* MIU_TEST_AGT_CTRL flags. work for SIU as well */
#define MIU_TA_CTL_WRITE_ENABLE (MIU_TA_CTL_WRITE | MIU_TA_CTL_ENABLE)
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 5c5dcca4d1da..15eaa6dded04 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -397,7 +397,6 @@ static void qla_init_base_qpair(struct scsi_qla_host *vha, struct req_que *req,
ha->base_qpair->use_shadow_reg = IS_SHADOW_REG_CAPABLE(ha) ? 1 : 0;
ha->base_qpair->msix = &ha->msix_entries[QLA_MSIX_RSP_Q];
INIT_LIST_HEAD(&ha->base_qpair->hints_list);
- INIT_LIST_HEAD(&ha->base_qpair->nvme_done_list);
ha->base_qpair->enable_class_2 = ql2xenableclass2;
/* init qpair to this cpu. Will adjust at run time. */
qla_cpu_update(rsp->qpair, raw_smp_processor_id());
@@ -471,9 +470,6 @@ fail_req_map:
static void qla2x00_free_req_que(struct qla_hw_data *ha, struct req_que *req)
{
- if (!ha->req_q_map)
- return;
-
if (IS_QLAFX00(ha)) {
if (req && req->ring_fx00)
dma_free_coherent(&ha->pdev->dev,
@@ -484,19 +480,16 @@ static void qla2x00_free_req_que(struct qla_hw_data *ha, struct req_que *req)
(req->length + 1) * sizeof(request_t),
req->ring, req->dma);
- if (req) {
+ if (req)
kfree(req->outstanding_cmds);
- kfree(req);
- }
+
+ kfree(req);
}
static void qla2x00_free_rsp_que(struct qla_hw_data *ha, struct rsp_que *rsp)
{
- if (!ha->rsp_q_map)
- return;
-
if (IS_QLAFX00(ha)) {
- if (rsp && rsp->ring)
+ if (rsp && rsp->ring_fx00)
dma_free_coherent(&ha->pdev->dev,
(rsp->length_fx00 + 1) * sizeof(request_t),
rsp->ring_fx00, rsp->dma_fx00);
@@ -505,8 +498,7 @@ static void qla2x00_free_rsp_que(struct qla_hw_data *ha, struct rsp_que *rsp)
(rsp->length + 1) * sizeof(response_t),
rsp->ring, rsp->dma);
}
- if (rsp)
- kfree(rsp);
+ kfree(rsp);
}
static void qla2x00_free_queues(struct qla_hw_data *ha)
@@ -1744,7 +1736,7 @@ __qla2x00_abort_all_cmds(struct qla_qpair *qp, int res)
sp_get(sp);
spin_unlock_irqrestore(qp->qp_lock_ptr,
flags);
- qla_nvme_abort(ha, sp);
+ qla_nvme_abort(ha, sp, res);
spin_lock_irqsave(qp->qp_lock_ptr,
flags);
} else if (GET_CMD_SP(sp) &&
@@ -3107,7 +3099,8 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
goto probe_failed;
/* Alloc arrays of request and response ring ptrs */
- if (qla2x00_alloc_queues(ha, req, rsp)) {
+ ret = qla2x00_alloc_queues(ha, req, rsp);
+ if (ret) {
ql_log(ql_log_fatal, base_vha, 0x003d,
"Failed to allocate memory for queue pointers..."
"aborting.\n");
@@ -3408,8 +3401,15 @@ probe_failed:
}
qla2x00_free_device(base_vha);
-
scsi_host_put(base_vha->host);
+ /*
+ * Need to NULL out local req/rsp after
+ * qla2x00_free_device => qla2x00_free_queues frees
+ * what these are pointing to. Or else we'll
+ * fall over below in qla2x00_free_req/rsp_que.
+ */
+ req = NULL;
+ rsp = NULL;
probe_hw_failed:
qla2x00_mem_free(ha);
@@ -4115,6 +4115,7 @@ fail_npiv_info:
(*rsp)->dma = 0;
fail_rsp_ring:
kfree(*rsp);
+ *rsp = NULL;
fail_rsp:
dma_free_coherent(&ha->pdev->dev, ((*req)->length + 1) *
sizeof(request_t), (*req)->ring, (*req)->dma);
@@ -4122,6 +4123,7 @@ fail_rsp:
(*req)->dma = 0;
fail_req_ring:
kfree(*req);
+ *req = NULL;
fail_req:
dma_free_coherent(&ha->pdev->dev, sizeof(struct ct_sns_pkt),
ha->ct_sns, ha->ct_sns_dma);
@@ -4509,16 +4511,11 @@ qla2x00_mem_free(struct qla_hw_data *ha)
dma_free_coherent(&ha->pdev->dev, ha->init_cb_size,
ha->init_cb, ha->init_cb_dma);
- if (ha->optrom_buffer)
- vfree(ha->optrom_buffer);
- if (ha->nvram)
- kfree(ha->nvram);
- if (ha->npiv_info)
- kfree(ha->npiv_info);
- if (ha->swl)
- kfree(ha->swl);
- if (ha->loop_id_map)
- kfree(ha->loop_id_map);
+ vfree(ha->optrom_buffer);
+ kfree(ha->nvram);
+ kfree(ha->npiv_info);
+ kfree(ha->swl);
+ kfree(ha->loop_id_map);
ha->srb_mempool = NULL;
ha->ctx_mempool = NULL;
@@ -4822,12 +4819,14 @@ void qla24xx_create_new_sess(struct scsi_qla_host *vha, struct qla_work_evt *e)
fcport->d_id = e->u.new_sess.id;
fcport->flags |= FCF_FABRIC_DEVICE;
fcport->fw_login_state = DSC_LS_PLOGI_PEND;
- if (e->u.new_sess.fc4_type == FC4_TYPE_FCP_SCSI) {
+ if (e->u.new_sess.fc4_type & FS_FC4TYPE_FCP)
fcport->fc4_type = FC4_TYPE_FCP_SCSI;
- } else if (e->u.new_sess.fc4_type == FC4_TYPE_NVME) {
+
+ if (e->u.new_sess.fc4_type & FS_FC4TYPE_NVME) {
fcport->fc4_type = FC4_TYPE_OTHER;
fcport->fc4f_nvme = FC4_TYPE_NVME;
}
+
memcpy(fcport->port_name, e->u.new_sess.port_name,
WWN_SIZE);
} else {
@@ -5047,7 +5046,8 @@ qla2x00_do_work(struct scsi_qla_host *vha)
e->u.logio.data);
break;
case QLA_EVT_GPNFT:
- qla24xx_async_gpnft(vha, e->u.gpnft.fc4_type);
+ qla24xx_async_gpnft(vha, e->u.gpnft.fc4_type,
+ e->u.gpnft.sp);
break;
case QLA_EVT_GPNFT_DONE:
qla24xx_async_gpnft_done(vha, e->u.iosb.sp);
@@ -6830,7 +6830,7 @@ static int qla2xxx_map_queues(struct Scsi_Host *shost)
if (USER_CTRL_IRQ(vha->hw))
rc = blk_mq_map_queues(&shost->tag_set);
else
- rc = blk_mq_pci_map_queues(&shost->tag_set, vha->hw->pdev);
+ rc = blk_mq_pci_map_queues(&shost->tag_set, vha->hw->pdev, 0);
return rc;
}
diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c
index d2db86ea06b2..04458eb19d38 100644
--- a/drivers/scsi/qla2xxx/qla_sup.c
+++ b/drivers/scsi/qla2xxx/qla_sup.c
@@ -2226,6 +2226,7 @@ qla2x00_erase_flash_sector(struct qla_hw_data *ha, uint32_t addr,
/**
* qla2x00_get_flash_manufacturer() - Read manufacturer ID from flash chip.
+ * @ha:
* @man_id: Flash manufacturer ID
* @flash_id: Flash ID
*/
diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c
index b49ac85f3de2..025dc2d3f3de 100644
--- a/drivers/scsi/qla2xxx/qla_target.c
+++ b/drivers/scsi/qla2xxx/qla_target.c
@@ -664,10 +664,10 @@ int qla24xx_async_notify_ack(scsi_qla_host_t *vha, fc_port_t *fcport,
sp->type = type;
sp->name = "nack";
+ sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha)+2);
sp->u.iocb_cmd.u.nack.ntfy = ntfy;
- sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
sp->done = qla2x00_async_nack_sp_done;
rval = qla2x00_start_sp(sp);
@@ -961,7 +961,7 @@ qlt_send_first_logo(struct scsi_qla_host *vha, qlt_port_logo_t *logo)
logo->cmd_count, res);
}
-static void qlt_free_session_done(struct work_struct *work)
+void qlt_free_session_done(struct work_struct *work)
{
struct fc_port *sess = container_of(work, struct fc_port,
free_work);
@@ -1169,11 +1169,14 @@ void qlt_unreg_sess(struct fc_port *sess)
sess->last_rscn_gen = sess->rscn_gen;
sess->last_login_gen = sess->login_gen;
- if (sess->nvme_flag & NVME_FLAG_REGISTERED)
+ if (sess->nvme_flag & NVME_FLAG_REGISTERED &&
+ !(sess->nvme_flag & NVME_FLAG_DELETING)) {
+ sess->nvme_flag |= NVME_FLAG_DELETING;
schedule_work(&sess->nvme_del_work);
-
- INIT_WORK(&sess->free_work, qlt_free_session_done);
- schedule_work(&sess->free_work);
+ } else {
+ INIT_WORK(&sess->free_work, qlt_free_session_done);
+ schedule_work(&sess->free_work);
+ }
}
EXPORT_SYMBOL(qlt_unreg_sess);
@@ -2023,7 +2026,7 @@ static void qlt_24xx_handle_abts(struct scsi_qla_host *vha,
sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha, s_id);
if (!sess) {
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf012,
- "qla_target(%d): task abort for non-existant session\n",
+ "qla_target(%d): task abort for non-existent session\n",
vha->vp_idx);
spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
@@ -2866,7 +2869,6 @@ qlt_build_ctio_crc2_pkt(struct qla_qpair *qpair, struct qla_tgt_prm *prm)
uint32_t data_bytes;
uint32_t dif_bytes;
uint8_t bundling = 1;
- uint8_t *clr_ptr;
struct crc_context *crc_ctx_pkt = NULL;
struct qla_hw_data *ha;
struct ctio_crc2_to_fw *pkt;
@@ -2995,15 +2997,11 @@ qlt_build_ctio_crc2_pkt(struct qla_qpair *qpair, struct qla_tgt_prm *prm)
/* Allocate CRC context from global pool */
crc_ctx_pkt = cmd->ctx =
- dma_pool_alloc(ha->dl_dma_pool, GFP_ATOMIC, &crc_ctx_dma);
+ dma_pool_zalloc(ha->dl_dma_pool, GFP_ATOMIC, &crc_ctx_dma);
if (!crc_ctx_pkt)
goto crc_queuing_error;
- /* Zero out CTX area. */
- clr_ptr = (uint8_t *)crc_ctx_pkt;
- memset(clr_ptr, 0, sizeof(*crc_ctx_pkt));
-
crc_ctx_pkt->crc_ctx_dma = crc_ctx_dma;
INIT_LIST_HEAD(&crc_ctx_pkt->dsd_list);
@@ -6292,10 +6290,11 @@ static void qlt_lport_dump(struct scsi_qla_host *vha, u64 wwpn,
/**
* qla_tgt_lport_register - register lport with external module
*
- * @qla_tgt_ops: Pointer for tcm_qla2xxx qla_tgt_ops
- * @wwpn: Passwd FC target WWPN
- * @callback: lport initialization callback for tcm_qla2xxx code
* @target_lport_ptr: pointer for tcm_qla2xxx specific lport data
+ * @phys_wwpn:
+ * @npiv_wwpn:
+ * @npiv_wwnn:
+ * @callback: lport initialization callback for tcm_qla2xxx code
*/
int qlt_lport_register(void *target_lport_ptr, u64 phys_wwpn,
u64 npiv_wwpn, u64 npiv_wwnn,
diff --git a/drivers/scsi/qla2xxx/qla_target.h b/drivers/scsi/qla2xxx/qla_target.h
index bb67b5a284a8..728ce74358e7 100644
--- a/drivers/scsi/qla2xxx/qla_target.h
+++ b/drivers/scsi/qla2xxx/qla_target.h
@@ -1016,7 +1016,7 @@ extern void qlt_fc_port_deleted(struct scsi_qla_host *, fc_port_t *, int);
extern int __init qlt_init(void);
extern void qlt_exit(void);
extern void qlt_update_vp_map(struct scsi_qla_host *, int);
-
+extern void qlt_free_session_done(struct work_struct *);
/*
* This macro is used during early initializations when host->active_mode
* is not set. Right now, ha value is ignored.
diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h
index 549bef9afddd..0c55d7057280 100644
--- a/drivers/scsi/qla2xxx/qla_version.h
+++ b/drivers/scsi/qla2xxx/qla_version.h
@@ -7,7 +7,7 @@
/*
* Driver version
*/
-#define QLA2XXX_VERSION "10.00.00.05-k"
+#define QLA2XXX_VERSION "10.00.00.06-k"
#define QLA_DRIVER_MAJOR_VER 10
#define QLA_DRIVER_MINOR_VER 0
diff --git a/drivers/scsi/qla4xxx/ql4_mbx.c b/drivers/scsi/qla4xxx/ql4_mbx.c
index bda2e64ee5ca..5d56904687b9 100644
--- a/drivers/scsi/qla4xxx/ql4_mbx.c
+++ b/drivers/scsi/qla4xxx/ql4_mbx.c
@@ -1584,12 +1584,11 @@ int qla4xxx_get_chap(struct scsi_qla_host *ha, char *username, char *password,
struct ql4_chap_table *chap_table;
dma_addr_t chap_dma;
- chap_table = dma_pool_alloc(ha->chap_dma_pool, GFP_KERNEL, &chap_dma);
+ chap_table = dma_pool_zalloc(ha->chap_dma_pool, GFP_KERNEL, &chap_dma);
if (chap_table == NULL)
return -ENOMEM;
chap_size = sizeof(struct ql4_chap_table);
- memset(chap_table, 0, chap_size);
if (is_qla40XX(ha))
offset = FLASH_CHAP_OFFSET | (idx * chap_size);
@@ -1648,13 +1647,12 @@ int qla4xxx_set_chap(struct scsi_qla_host *ha, char *username, char *password,
uint32_t chap_size = 0;
dma_addr_t chap_dma;
- chap_table = dma_pool_alloc(ha->chap_dma_pool, GFP_KERNEL, &chap_dma);
+ chap_table = dma_pool_zalloc(ha->chap_dma_pool, GFP_KERNEL, &chap_dma);
if (chap_table == NULL) {
ret = -ENOMEM;
goto exit_set_chap;
}
- memset(chap_table, 0, sizeof(struct ql4_chap_table));
if (bidi)
chap_table->flags |= BIT_6; /* peer */
else
diff --git a/drivers/scsi/qla4xxx/ql4_nx.c b/drivers/scsi/qla4xxx/ql4_nx.c
index 968bd85610f8..43f73583ef5c 100644
--- a/drivers/scsi/qla4xxx/ql4_nx.c
+++ b/drivers/scsi/qla4xxx/ql4_nx.c
@@ -45,6 +45,8 @@ qla4_8xxx_pci_base_offsetfset(struct scsi_qla_host *ha, unsigned long off)
return NULL;
}
+static const int MD_MIU_TEST_AGT_RDDATA[] = { 0x410000A8,
+ 0x410000AC, 0x410000B8, 0x410000BC };
#define MAX_CRB_XFORM 60
static unsigned long crb_addr_xform[MAX_CRB_XFORM];
static int qla4_8xxx_crb_table_initialized;
diff --git a/drivers/scsi/qla4xxx/ql4_nx.h b/drivers/scsi/qla4xxx/ql4_nx.h
index 337d9fcf6417..98fe78613eb7 100644
--- a/drivers/scsi/qla4xxx/ql4_nx.h
+++ b/drivers/scsi/qla4xxx/ql4_nx.h
@@ -1022,11 +1022,4 @@ struct qla8xxx_minidump_entry_queue {
#define MD_MIU_TEST_AGT_WRDATA_ULO 0x410000B0
#define MD_MIU_TEST_AGT_WRDATA_UHI 0x410000B4
-#define MD_MIU_TEST_AGT_RDDATA_LO 0x410000A8
-#define MD_MIU_TEST_AGT_RDDATA_HI 0x410000AC
-#define MD_MIU_TEST_AGT_RDDATA_ULO 0x410000B8
-#define MD_MIU_TEST_AGT_RDDATA_UHI 0x410000BC
-
-static const int MD_MIU_TEST_AGT_RDDATA[] = { 0x410000A8,
- 0x410000AC, 0x410000B8, 0x410000BC };
#endif
diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c
index fc2c97d9a0d6..94c14ce94da2 100644
--- a/drivers/scsi/qla4xxx/ql4_os.c
+++ b/drivers/scsi/qla4xxx/ql4_os.c
@@ -843,12 +843,10 @@ static int qla4xxx_delete_chap(struct Scsi_Host *shost, uint16_t chap_tbl_idx)
uint32_t chap_size;
int ret = 0;
- chap_table = dma_pool_alloc(ha->chap_dma_pool, GFP_KERNEL, &chap_dma);
+ chap_table = dma_pool_zalloc(ha->chap_dma_pool, GFP_KERNEL, &chap_dma);
if (chap_table == NULL)
return -ENOMEM;
- memset(chap_table, 0, sizeof(struct ql4_chap_table));
-
if (is_qla80XX(ha))
max_chap_entries = (ha->hw.flt_chap_size / 2) /
sizeof(struct ql4_chap_table);
diff --git a/drivers/scsi/raid_class.c b/drivers/scsi/raid_class.c
index 2c146b44d95f..ea88906d2cc5 100644
--- a/drivers/scsi/raid_class.c
+++ b/drivers/scsi/raid_class.c
@@ -157,6 +157,7 @@ static struct {
{ RAID_LEVEL_5, "raid5" },
{ RAID_LEVEL_50, "raid50" },
{ RAID_LEVEL_6, "raid6" },
+ { RAID_LEVEL_JBOD, "jbod" },
};
static const char *raid_level_name(enum raid_level level)
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index a7e4fba724b7..4c60c260c5da 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -231,7 +231,7 @@ void scsi_finish_command(struct scsi_cmnd *cmd)
"(result %x)\n", cmd->result));
good_bytes = scsi_bufflen(cmd);
- if (!blk_rq_is_passthrough(cmd->request)) {
+ if (!blk_rq_is_passthrough(cmd->request)) {
int old_good_bytes = good_bytes;
drv = scsi_cmd_to_driver(cmd);
if (drv->done)
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index a5986dae9020..656c98e116a9 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -6,7 +6,7 @@
* anything out of the ordinary is seen.
* ^^^^^^^^^^^^^^^^^^^^^^^ Original ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
*
- * Copyright (C) 2001 - 2017 Douglas Gilbert
+ * Copyright (C) 2001 - 2018 Douglas Gilbert
*
* 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
@@ -61,8 +61,8 @@
#include "scsi_logging.h"
/* make sure inq_product_rev string corresponds to this version */
-#define SDEBUG_VERSION "0187" /* format to fit INQUIRY revision field */
-static const char *sdebug_version_date = "20171202";
+#define SDEBUG_VERSION "0188" /* format to fit INQUIRY revision field */
+static const char *sdebug_version_date = "20180128";
#define MY_NAME "scsi_debug"
@@ -234,10 +234,13 @@ static const char *sdebug_version_date = "20171202";
#define F_INV_OP 0x200
#define F_FAKE_RW 0x400
#define F_M_ACCESS 0x800 /* media access */
+#define F_SSU_DELAY 0x1000
+#define F_SYNC_DELAY 0x2000
#define FF_RESPOND (F_RL_WLUN_OK | F_SKIP_UA | F_DELAY_OVERR)
#define FF_MEDIA_IO (F_M_ACCESS | F_FAKE_RW)
#define FF_SA (F_SA_HIGH | F_SA_LOW)
+#define F_LONG_DELAY (F_SSU_DELAY | F_SYNC_DELAY)
#define SDEBUG_MAX_PARTS 4
@@ -349,7 +352,7 @@ enum sdeb_opcode_index {
SDEB_I_XDWRITEREAD = 25, /* 10 only */
SDEB_I_WRITE_BUFFER = 26,
SDEB_I_WRITE_SAME = 27, /* 10, 16 */
- SDEB_I_SYNC_CACHE = 28, /* 10 only */
+ SDEB_I_SYNC_CACHE = 28, /* 10, 16 */
SDEB_I_COMP_WRITE = 29,
SDEB_I_LAST_ELEMENT = 30, /* keep this last (previous + 1) */
};
@@ -382,7 +385,7 @@ static const unsigned char opcode_ind_arr[256] = {
/* 0x80; 0x80->0x9f: 16 byte cdbs */
0, 0, 0, 0, 0, SDEB_I_ATA_PT, 0, 0,
SDEB_I_READ, SDEB_I_COMP_WRITE, SDEB_I_WRITE, 0, 0, 0, 0, 0,
- 0, 0, 0, SDEB_I_WRITE_SAME, 0, 0, 0, 0,
+ 0, SDEB_I_SYNC_CACHE, 0, SDEB_I_WRITE_SAME, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, SDEB_I_SERV_ACT_IN_16, SDEB_I_SERV_ACT_OUT_16,
/* 0xa0; 0xa0->0xbf: 12 byte cdbs */
SDEB_I_REPORT_LUNS, SDEB_I_ATA_PT, 0, SDEB_I_MAINT_IN,
@@ -398,6 +401,14 @@ static const unsigned char opcode_ind_arr[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
+/*
+ * The following "response" functions return the SCSI mid-level's 4 byte
+ * tuple-in-an-int. To handle commands with an IMMED bit, for a faster
+ * command completion, they can mask their return value with
+ * SDEG_RES_IMMED_MASK .
+ */
+#define SDEG_RES_IMMED_MASK 0x40000000
+
static int resp_inquiry(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_report_luns(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_requests(struct scsi_cmnd *, struct sdebug_dev_info *);
@@ -420,6 +431,7 @@ static int resp_write_same_16(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_xdwriteread_10(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_comp_write(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_write_buffer(struct scsi_cmnd *, struct sdebug_dev_info *);
+static int resp_sync_cache(struct scsi_cmnd *, struct sdebug_dev_info *);
/*
* The following are overflow arrays for cdbs that "hit" the same index in
@@ -499,6 +511,12 @@ static const struct opcode_info_t release_iarr[] = {
{6, 0x1f, 0xff, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
};
+static const struct opcode_info_t sync_cache_iarr[] = {
+ {0, 0x91, 0, F_SYNC_DELAY | F_M_ACCESS, resp_sync_cache, NULL,
+ {16, 0x6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x3f, 0xc7} }, /* SYNC_CACHE (16) */
+};
+
/* This array is accessed via SDEB_I_* values. Make sure all are mapped,
* plus the terminating elements for logic that scans this table such as
@@ -536,8 +554,8 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEMENT + 1] = {
{ARRAY_SIZE(write_iarr), 0x8a, 0, F_D_OUT | FF_MEDIA_IO,
resp_write_dt0, write_iarr, /* WRITE(16) */
{16, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7} }, /* WRITE(16) */
- {0, 0x1b, 0, 0, resp_start_stop, NULL, /* START STOP UNIT */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7} },
+ {0, 0x1b, 0, F_SSU_DELAY, resp_start_stop, NULL,/* START STOP UNIT */
{6, 0x1, 0, 0xf, 0xf7, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
{ARRAY_SIZE(sa_in_16_iarr), 0x9e, 0x10, F_SA_LOW | F_D_IN,
resp_readcap16, sa_in_16_iarr, /* SA_IN(16), READ CAPACITY(16) */
@@ -590,9 +608,10 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEMENT + 1] = {
resp_write_same_10, write_same_iarr, /* WRITE SAME(10) */
{10, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0,
0, 0, 0, 0, 0} },
- {0, 0x35, 0, F_DELAY_OVERR | FF_MEDIA_IO, NULL, NULL, /* SYNC_CACHE */
+ {ARRAY_SIZE(sync_cache_iarr), 0x35, 0, F_SYNC_DELAY | F_M_ACCESS,
+ resp_sync_cache, sync_cache_iarr,
{10, 0x7, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0, 0,
- 0, 0, 0, 0} },
+ 0, 0, 0, 0} }, /* SYNC_CACHE (10) */
{0, 0x89, 0, F_D_OUT | FF_MEDIA_IO, resp_comp_write, NULL,
{16, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0,
0, 0xff, 0x3f, 0xc7} }, /* COMPARE AND WRITE */
@@ -616,6 +635,8 @@ static unsigned int sdebug_guard = DEF_GUARD;
static int sdebug_lowest_aligned = DEF_LOWEST_ALIGNED;
static int sdebug_max_luns = DEF_MAX_LUNS;
static int sdebug_max_queue = SDEBUG_CANQUEUE; /* per submit queue */
+static unsigned int sdebug_medium_error_start = OPT_MEDIUM_ERR_ADDR;
+static int sdebug_medium_error_count = OPT_MEDIUM_ERR_NUM;
static atomic_t retired_max_queue; /* if > 0 then was prior max_queue */
static int sdebug_ndelay = DEF_NDELAY; /* if > 0 then unit is nanoseconds */
static int sdebug_no_lun_0 = DEF_NO_LUN_0;
@@ -648,8 +669,8 @@ static bool sdebug_strict = DEF_STRICT;
static bool sdebug_any_injecting_opt;
static bool sdebug_verbose;
static bool have_dif_prot;
+static bool write_since_sync;
static bool sdebug_statistics = DEF_STATISTICS;
-static bool sdebug_mq_active;
static unsigned int sdebug_store_sectors;
static sector_t sdebug_capacity; /* in sectors */
@@ -1155,8 +1176,8 @@ static int inquiry_vpd_84(unsigned char *arr)
static int inquiry_vpd_85(unsigned char *arr)
{
int num = 0;
- const char * na1 = "https://www.kernel.org/config";
- const char * na2 = "http://www.kernel.org/log";
+ const char *na1 = "https://www.kernel.org/config";
+ const char *na2 = "http://www.kernel.org/log";
int plen, olen;
arr[num++] = 0x1; /* lu, storage config */
@@ -1372,7 +1393,7 @@ static int inquiry_vpd_b2(unsigned char *arr)
static int resp_inquiry(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
{
unsigned char pq_pdt;
- unsigned char * arr;
+ unsigned char *arr;
unsigned char *cmd = scp->cmnd;
int alloc_len, n, ret;
bool have_wlun, is_disk;
@@ -1523,10 +1544,10 @@ static int resp_inquiry(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
static unsigned char iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0,
0, 0, 0x0, 0x0};
-static int resp_requests(struct scsi_cmnd * scp,
- struct sdebug_dev_info * devip)
+static int resp_requests(struct scsi_cmnd *scp,
+ struct sdebug_dev_info *devip)
{
- unsigned char * sbuff;
+ unsigned char *sbuff;
unsigned char *cmd = scp->cmnd;
unsigned char arr[SCSI_SENSE_BUFFERSIZE];
bool dsense;
@@ -1584,11 +1605,12 @@ static int resp_requests(struct scsi_cmnd * scp,
return fill_from_dev_buffer(scp, arr, len);
}
-static int resp_start_stop(struct scsi_cmnd * scp,
- struct sdebug_dev_info * devip)
+static int resp_start_stop(struct scsi_cmnd *scp,
+ struct sdebug_dev_info *devip)
{
unsigned char *cmd = scp->cmnd;
int power_cond, stop;
+ bool changing;
power_cond = (cmd[4] & 0xf0) >> 4;
if (power_cond) {
@@ -1596,8 +1618,12 @@ static int resp_start_stop(struct scsi_cmnd * scp,
return check_condition_result;
}
stop = !(cmd[4] & 1);
+ changing = atomic_read(&devip->stopped) == !stop;
atomic_xchg(&devip->stopped, stop);
- return 0;
+ if (!changing || cmd[1] & 0x1) /* state unchanged or IMMED set */
+ return SDEG_RES_IMMED_MASK;
+ else
+ return 0;
}
static sector_t get_sdebug_capacity(void)
@@ -1612,8 +1638,8 @@ static sector_t get_sdebug_capacity(void)
}
#define SDEBUG_READCAP_ARR_SZ 8
-static int resp_readcap(struct scsi_cmnd * scp,
- struct sdebug_dev_info * devip)
+static int resp_readcap(struct scsi_cmnd *scp,
+ struct sdebug_dev_info *devip)
{
unsigned char arr[SDEBUG_READCAP_ARR_SZ];
unsigned int capac;
@@ -1631,8 +1657,8 @@ static int resp_readcap(struct scsi_cmnd * scp,
}
#define SDEBUG_READCAP16_ARR_SZ 32
-static int resp_readcap16(struct scsi_cmnd * scp,
- struct sdebug_dev_info * devip)
+static int resp_readcap16(struct scsi_cmnd *scp,
+ struct sdebug_dev_info *devip)
{
unsigned char *cmd = scp->cmnd;
unsigned char arr[SDEBUG_READCAP16_ARR_SZ];
@@ -1670,11 +1696,11 @@ static int resp_readcap16(struct scsi_cmnd * scp,
#define SDEBUG_MAX_TGTPGS_ARR_SZ 1412
-static int resp_report_tgtpgs(struct scsi_cmnd * scp,
- struct sdebug_dev_info * devip)
+static int resp_report_tgtpgs(struct scsi_cmnd *scp,
+ struct sdebug_dev_info *devip)
{
unsigned char *cmd = scp->cmnd;
- unsigned char * arr;
+ unsigned char *arr;
int host_no = devip->sdbg_host->shost->host_no;
int n, ret, alen, rlen;
int port_group_a, port_group_b, port_a, port_b;
@@ -1926,7 +1952,7 @@ static int resp_rsup_tmfs(struct scsi_cmnd *scp,
/* <<Following mode page info copied from ST318451LW>> */
-static int resp_err_recov_pg(unsigned char * p, int pcontrol, int target)
+static int resp_err_recov_pg(unsigned char *p, int pcontrol, int target)
{ /* Read-Write Error Recovery page for mode_sense */
unsigned char err_recov_pg[] = {0x1, 0xa, 0xc0, 11, 240, 0, 0, 0,
5, 0, 0xff, 0xff};
@@ -1937,7 +1963,7 @@ static int resp_err_recov_pg(unsigned char * p, int pcontrol, int target)
return sizeof(err_recov_pg);
}
-static int resp_disconnect_pg(unsigned char * p, int pcontrol, int target)
+static int resp_disconnect_pg(unsigned char *p, int pcontrol, int target)
{ /* Disconnect-Reconnect page for mode_sense */
unsigned char disconnect_pg[] = {0x2, 0xe, 128, 128, 0, 10, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0};
@@ -1948,7 +1974,7 @@ static int resp_disconnect_pg(unsigned char * p, int pcontrol, int target)
return sizeof(disconnect_pg);
}
-static int resp_format_pg(unsigned char * p, int pcontrol, int target)
+static int resp_format_pg(unsigned char *p, int pcontrol, int target)
{ /* Format device page for mode_sense */
unsigned char format_pg[] = {0x3, 0x16, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
@@ -1968,7 +1994,7 @@ static unsigned char caching_pg[] = {0x8, 18, 0x14, 0, 0xff, 0xff, 0, 0,
0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0,
0, 0, 0, 0};
-static int resp_caching_pg(unsigned char * p, int pcontrol, int target)
+static int resp_caching_pg(unsigned char *p, int pcontrol, int target)
{ /* Caching page for mode_sense */
unsigned char ch_caching_pg[] = {/* 0x8, 18, */ 0x4, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
@@ -1988,7 +2014,7 @@ static int resp_caching_pg(unsigned char * p, int pcontrol, int target)
static unsigned char ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0,
0, 0, 0x2, 0x4b};
-static int resp_ctrl_m_pg(unsigned char * p, int pcontrol, int target)
+static int resp_ctrl_m_pg(unsigned char *p, int pcontrol, int target)
{ /* Control mode page for mode_sense */
unsigned char ch_ctrl_m_pg[] = {/* 0xa, 10, */ 0x6, 0, 0, 0, 0, 0,
0, 0, 0, 0};
@@ -2012,7 +2038,7 @@ static int resp_ctrl_m_pg(unsigned char * p, int pcontrol, int target)
}
-static int resp_iec_m_pg(unsigned char * p, int pcontrol, int target)
+static int resp_iec_m_pg(unsigned char *p, int pcontrol, int target)
{ /* Informational Exceptions control mode page for mode_sense */
unsigned char ch_iec_m_pg[] = {/* 0x1c, 0xa, */ 0x4, 0xf, 0, 0, 0, 0,
0, 0, 0x0, 0x0};
@@ -2027,7 +2053,7 @@ static int resp_iec_m_pg(unsigned char * p, int pcontrol, int target)
return sizeof(iec_m_pg);
}
-static int resp_sas_sf_m_pg(unsigned char * p, int pcontrol, int target)
+static int resp_sas_sf_m_pg(unsigned char *p, int pcontrol, int target)
{ /* SAS SSP mode page - short format for mode_sense */
unsigned char sas_sf_m_pg[] = {0x19, 0x6,
0x6, 0x0, 0x7, 0xd0, 0x0, 0x0};
@@ -2039,7 +2065,7 @@ static int resp_sas_sf_m_pg(unsigned char * p, int pcontrol, int target)
}
-static int resp_sas_pcd_m_spg(unsigned char * p, int pcontrol, int target,
+static int resp_sas_pcd_m_spg(unsigned char *p, int pcontrol, int target,
int target_dev_id)
{ /* SAS phy control and discover mode page for mode_sense */
unsigned char sas_pcd_m_pg[] = {0x59, 0x1, 0, 0x64, 0, 0x6, 0, 2,
@@ -2072,7 +2098,7 @@ static int resp_sas_pcd_m_spg(unsigned char * p, int pcontrol, int target,
return sizeof(sas_pcd_m_pg);
}
-static int resp_sas_sha_m_spg(unsigned char * p, int pcontrol)
+static int resp_sas_sha_m_spg(unsigned char *p, int pcontrol)
{ /* SAS SSP shared protocol specific port mode subpage */
unsigned char sas_sha_m_pg[] = {0x59, 0x2, 0, 0xc, 0, 0x6, 0x10, 0,
0, 0, 0, 0, 0, 0, 0, 0,
@@ -2093,7 +2119,7 @@ static int resp_mode_sense(struct scsi_cmnd *scp,
unsigned char dev_spec;
int alloc_len, offset, len, target_dev_id;
int target = scp->device->id;
- unsigned char * ap;
+ unsigned char *ap;
unsigned char arr[SDEBUG_MAX_MSENSE_SZ];
unsigned char *cmd = scp->cmnd;
bool dbd, llbaa, msense_6, is_disk, bad_pcode;
@@ -2324,7 +2350,7 @@ set_mode_changed_ua:
return 0;
}
-static int resp_temp_l_pg(unsigned char * arr)
+static int resp_temp_l_pg(unsigned char *arr)
{
unsigned char temp_l_pg[] = {0x0, 0x0, 0x3, 0x2, 0x0, 38,
0x0, 0x1, 0x3, 0x2, 0x0, 65,
@@ -2334,7 +2360,7 @@ static int resp_temp_l_pg(unsigned char * arr)
return sizeof(temp_l_pg);
}
-static int resp_ie_l_pg(unsigned char * arr)
+static int resp_ie_l_pg(unsigned char *arr)
{
unsigned char ie_l_pg[] = {0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 38,
};
@@ -2455,6 +2481,7 @@ static int do_device_access(struct scsi_cmnd *scmd, u32 sg_skip, u64 lba,
if (do_write) {
sdb = scsi_out(scmd);
dir = DMA_TO_DEVICE;
+ write_since_sync = true;
} else {
sdb = scsi_in(scmd);
dir = DMA_FROM_DEVICE;
@@ -2712,8 +2739,8 @@ static int resp_read_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
}
if (unlikely((SDEBUG_OPT_MEDIUM_ERR & sdebug_opts) &&
- (lba <= (OPT_MEDIUM_ERR_ADDR + OPT_MEDIUM_ERR_NUM - 1)) &&
- ((lba + num) > OPT_MEDIUM_ERR_ADDR))) {
+ (lba <= (sdebug_medium_error_start + sdebug_medium_error_count - 1)) &&
+ ((lba + num) > sdebug_medium_error_start))) {
/* claim unrecoverable read error */
mk_sense_buffer(scp, MEDIUM_ERROR, UNRECOVERED_READ_ERR, 0);
/* set info field and valid bit for fixed descriptor */
@@ -3562,6 +3589,32 @@ static int resp_get_lba_status(struct scsi_cmnd *scp,
return fill_from_dev_buffer(scp, arr, SDEBUG_GET_LBA_STATUS_LEN);
}
+static int resp_sync_cache(struct scsi_cmnd *scp,
+ struct sdebug_dev_info *devip)
+{
+ int res = 0;
+ u64 lba;
+ u32 num_blocks;
+ u8 *cmd = scp->cmnd;
+
+ if (cmd[0] == SYNCHRONIZE_CACHE) { /* 10 byte cdb */
+ lba = get_unaligned_be32(cmd + 2);
+ num_blocks = get_unaligned_be16(cmd + 7);
+ } else { /* SYNCHRONIZE_CACHE(16) */
+ lba = get_unaligned_be64(cmd + 2);
+ num_blocks = get_unaligned_be32(cmd + 10);
+ }
+ if (lba + num_blocks > sdebug_capacity) {
+ mk_sense_buffer(scp, ILLEGAL_REQUEST, LBA_OUT_OF_RANGE, 0);
+ return check_condition_result;
+ }
+ if (!write_since_sync || cmd[1] & 0x2)
+ res = SDEG_RES_IMMED_MASK;
+ else /* delay if write_since_sync and IMMED clear */
+ write_since_sync = false;
+ return res;
+}
+
#define RL_BUCKET_ELEMS 8
/* Even though each pseudo target has a REPORT LUNS "well known logical unit"
@@ -3727,20 +3780,13 @@ static int resp_xdwriteread_10(struct scsi_cmnd *scp,
static struct sdebug_queue *get_queue(struct scsi_cmnd *cmnd)
{
- struct sdebug_queue *sqp = sdebug_q_arr;
-
- if (sdebug_mq_active) {
- u32 tag = blk_mq_unique_tag(cmnd->request);
- u16 hwq = blk_mq_unique_tag_to_hwq(tag);
+ u32 tag = blk_mq_unique_tag(cmnd->request);
+ u16 hwq = blk_mq_unique_tag_to_hwq(tag);
- if (unlikely(hwq >= submit_queues)) {
- pr_warn("Unexpected hwq=%d, apply modulo\n", hwq);
- hwq %= submit_queues;
- }
- pr_debug("tag=%u, hwq=%d\n", tag, hwq);
- return sqp + hwq;
- } else
- return sqp;
+ pr_debug("tag=%#x, hwq=%d\n", tag, hwq);
+ if (WARN_ON_ONCE(hwq >= submit_queues))
+ hwq = 0;
+ return sdebug_q_arr + hwq;
}
/* Queued (deferred) command completions converge here. */
@@ -3897,7 +3943,7 @@ static int scsi_debug_slave_alloc(struct scsi_device *sdp)
if (sdebug_verbose)
pr_info("slave_alloc <%u %u %u %llu>\n",
sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
- queue_flag_set_unlocked(QUEUE_FLAG_BIDI, sdp->request_queue);
+ blk_queue_flag_set(QUEUE_FLAG_BIDI, sdp->request_queue);
return 0;
}
@@ -4066,7 +4112,7 @@ static int scsi_debug_abort(struct scsi_cmnd *SCpnt)
return SUCCESS;
}
-static int scsi_debug_device_reset(struct scsi_cmnd * SCpnt)
+static int scsi_debug_device_reset(struct scsi_cmnd *SCpnt)
{
++num_dev_resets;
if (SCpnt && SCpnt->device) {
@@ -4118,7 +4164,7 @@ lie:
return SUCCESS;
}
-static int scsi_debug_bus_reset(struct scsi_cmnd * SCpnt)
+static int scsi_debug_bus_reset(struct scsi_cmnd *SCpnt)
{
struct sdebug_host_info *sdbg_host;
struct sdebug_dev_info *devip;
@@ -4151,9 +4197,9 @@ lie:
return SUCCESS;
}
-static int scsi_debug_host_reset(struct scsi_cmnd * SCpnt)
+static int scsi_debug_host_reset(struct scsi_cmnd *SCpnt)
{
- struct sdebug_host_info * sdbg_host;
+ struct sdebug_host_info *sdbg_host;
struct sdebug_dev_info *devip;
int k = 0;
@@ -4179,7 +4225,7 @@ static int scsi_debug_host_reset(struct scsi_cmnd * SCpnt)
static void __init sdebug_build_parts(unsigned char *ramp,
unsigned long store_size)
{
- struct partition * pp;
+ struct partition *pp;
int starts[SDEBUG_MAX_PARTS + 2];
int sectors_per_part, num_sectors, k;
int heads_by_sects, start_sec, end_sec;
@@ -4262,8 +4308,13 @@ static void clear_queue_stats(void)
static void setup_inject(struct sdebug_queue *sqp,
struct sdebug_queued_cmd *sqcp)
{
- if ((atomic_read(&sdebug_cmnd_count) % abs(sdebug_every_nth)) > 0)
+ if ((atomic_read(&sdebug_cmnd_count) % abs(sdebug_every_nth)) > 0) {
+ if (sdebug_every_nth > 0)
+ sqcp->inj_recovered = sqcp->inj_transport
+ = sqcp->inj_dif
+ = sqcp->inj_dix = sqcp->inj_short = 0;
return;
+ }
sqcp->inj_recovered = !!(SDEBUG_OPT_RECOVERED_ERR & sdebug_opts);
sqcp->inj_transport = !!(SDEBUG_OPT_TRANSPORT_ERR & sdebug_opts);
sqcp->inj_dif = !!(SDEBUG_OPT_DIF_ERR & sdebug_opts);
@@ -4278,7 +4329,10 @@ static void setup_inject(struct sdebug_queue *sqp,
* SCSI_MLQUEUE_HOST_BUSY if temporarily out of resources.
*/
static int schedule_resp(struct scsi_cmnd *cmnd, struct sdebug_dev_info *devip,
- int scsi_result, int delta_jiff, int ndelay)
+ int scsi_result,
+ int (*pfp)(struct scsi_cmnd *,
+ struct sdebug_dev_info *),
+ int delta_jiff, int ndelay)
{
unsigned long iflags;
int k, num_in_q, qdepth, inject;
@@ -4294,9 +4348,6 @@ static int schedule_resp(struct scsi_cmnd *cmnd, struct sdebug_dev_info *devip,
}
sdp = cmnd->device;
- if (unlikely(sdebug_verbose && scsi_result))
- sdev_printk(KERN_INFO, sdp, "%s: non-zero result=0x%x\n",
- __func__, scsi_result);
if (delta_jiff == 0)
goto respond_in_thread;
@@ -4351,7 +4402,6 @@ static int schedule_resp(struct scsi_cmnd *cmnd, struct sdebug_dev_info *devip,
sqcp = &sqp->qc_arr[k];
sqcp->a_cmnd = cmnd;
cmnd->host_scribble = (unsigned char *)sqcp;
- cmnd->result = scsi_result;
sd_dp = sqcp->sd_dp;
spin_unlock_irqrestore(&sqp->qc_lock, iflags);
if (unlikely(sdebug_every_nth && sdebug_any_injecting_opt))
@@ -4361,6 +4411,22 @@ static int schedule_resp(struct scsi_cmnd *cmnd, struct sdebug_dev_info *devip,
if (sd_dp == NULL)
return SCSI_MLQUEUE_HOST_BUSY;
}
+
+ cmnd->result = pfp != NULL ? pfp(cmnd, devip) : 0;
+ if (cmnd->result & SDEG_RES_IMMED_MASK) {
+ /*
+ * This is the F_DELAY_OVERR case. No delay.
+ */
+ cmnd->result &= ~SDEG_RES_IMMED_MASK;
+ delta_jiff = ndelay = 0;
+ }
+ if (cmnd->result == 0 && scsi_result != 0)
+ cmnd->result = scsi_result;
+
+ if (unlikely(sdebug_verbose && cmnd->result))
+ sdev_printk(KERN_INFO, sdp, "%s: non-zero result=0x%x\n",
+ __func__, cmnd->result);
+
if (delta_jiff > 0 || ndelay > 0) {
ktime_t kt;
@@ -4403,7 +4469,10 @@ static int schedule_resp(struct scsi_cmnd *cmnd, struct sdebug_dev_info *devip,
return 0;
respond_in_thread: /* call back to mid-layer using invocation thread */
- cmnd->result = scsi_result;
+ cmnd->result = pfp != NULL ? pfp(cmnd, devip) : 0;
+ cmnd->result &= ~SDEG_RES_IMMED_MASK;
+ if (cmnd->result == 0 && scsi_result != 0)
+ cmnd->result = scsi_result;
cmnd->scsi_done(cmnd);
return 0;
}
@@ -4440,6 +4509,8 @@ module_param_named(lbprz, sdebug_lbprz, int, S_IRUGO);
module_param_named(lowest_aligned, sdebug_lowest_aligned, int, S_IRUGO);
module_param_named(max_luns, sdebug_max_luns, int, S_IRUGO | S_IWUSR);
module_param_named(max_queue, sdebug_max_queue, int, S_IRUGO | S_IWUSR);
+module_param_named(medium_error_start, sdebug_medium_error_start, int, S_IRUGO | S_IWUSR);
+module_param_named(medium_error_count, sdebug_medium_error_count, int, S_IRUGO | S_IWUSR);
module_param_named(ndelay, sdebug_ndelay, int, S_IRUGO | S_IWUSR);
module_param_named(no_lun_0, sdebug_no_lun_0, int, S_IRUGO | S_IWUSR);
module_param_named(no_uld, sdebug_no_uld, int, S_IRUGO);
@@ -4497,6 +4568,8 @@ MODULE_PARM_DESC(lbprz,
MODULE_PARM_DESC(lowest_aligned, "lowest aligned lba (def=0)");
MODULE_PARM_DESC(max_luns, "number of LUNs per target to simulate(def=1)");
MODULE_PARM_DESC(max_queue, "max number of queued commands (1 to max(def))");
+MODULE_PARM_DESC(medium_error_start, "starting sector number to return MEDIUM error");
+MODULE_PARM_DESC(medium_error_count, "count of sectors to return follow on MEDIUM error");
MODULE_PARM_DESC(ndelay, "response delay in nanoseconds (def=0 -> ignore)");
MODULE_PARM_DESC(no_lun_0, "no LU number 0 (def=0 -> have lun 0)");
MODULE_PARM_DESC(no_uld, "stop ULD (e.g. sd driver) attaching (def=0))");
@@ -4526,7 +4599,7 @@ MODULE_PARM_DESC(write_same_length, "Maximum blocks per WRITE SAME cmd (def=0xff
#define SDEBUG_INFO_LEN 256
static char sdebug_info[SDEBUG_INFO_LEN];
-static const char * scsi_debug_info(struct Scsi_Host * shp)
+static const char *scsi_debug_info(struct Scsi_Host *shp)
{
int k;
@@ -4587,9 +4660,8 @@ static int scsi_debug_show_info(struct seq_file *m, struct Scsi_Host *host)
num_host_resets);
seq_printf(m, "dix_reads=%d, dix_writes=%d, dif_errors=%d\n",
dix_reads, dix_writes, dif_errors);
- seq_printf(m, "usec_in_jiffy=%lu, %s=%d, mq_active=%d\n",
- TICK_NSEC / 1000, "statistics", sdebug_statistics,
- sdebug_mq_active);
+ seq_printf(m, "usec_in_jiffy=%lu, statistics=%d\n", TICK_NSEC / 1000,
+ sdebug_statistics);
seq_printf(m, "cmnd_count=%d, completions=%d, %s=%d, a_tsf=%d\n",
atomic_read(&sdebug_cmnd_count),
atomic_read(&sdebug_completions),
@@ -5450,7 +5522,7 @@ static void __exit scsi_debug_exit(void)
device_initcall(scsi_debug_init);
module_exit(scsi_debug_exit);
-static void sdebug_release_adapter(struct device * dev)
+static void sdebug_release_adapter(struct device *dev)
{
struct sdebug_host_info *sdbg_host;
@@ -5588,6 +5660,7 @@ static int scsi_debug_queuecommand(struct Scsi_Host *shost,
struct sdebug_dev_info *devip;
u8 *cmd = scp->cmnd;
int (*r_pfp)(struct scsi_cmnd *, struct sdebug_dev_info *);
+ int (*pfp)(struct scsi_cmnd *, struct sdebug_dev_info *) = NULL;
int k, na;
int errsts = 0;
u32 flags;
@@ -5612,13 +5685,8 @@ static int scsi_debug_queuecommand(struct Scsi_Host *shost,
n += scnprintf(b + n, sb - n, "%02x ",
(u32)cmd[k]);
}
- if (sdebug_mq_active)
- sdev_printk(KERN_INFO, sdp, "%s: tag=%u, cmd %s\n",
- my_name, blk_mq_unique_tag(scp->request),
- b);
- else
- sdev_printk(KERN_INFO, sdp, "%s: cmd %s\n", my_name,
- b);
+ sdev_printk(KERN_INFO, sdp, "%s: tag=%#x, cmd %s\n", my_name,
+ blk_mq_unique_tag(scp->request), b);
}
if (fake_host_busy(scp))
return SCSI_MLQUEUE_HOST_BUSY;
@@ -5714,20 +5782,31 @@ static int scsi_debug_queuecommand(struct Scsi_Host *shost,
return 0; /* ignore command: make trouble */
}
if (likely(oip->pfp))
- errsts = oip->pfp(scp, devip); /* calls a resp_* function */
- else if (r_pfp) /* if leaf function ptr NULL, try the root's */
- errsts = r_pfp(scp, devip);
+ pfp = oip->pfp; /* calls a resp_* function */
+ else
+ pfp = r_pfp; /* if leaf function ptr NULL, try the root's */
fini:
if (F_DELAY_OVERR & flags)
- return schedule_resp(scp, devip, errsts, 0, 0);
- else
- return schedule_resp(scp, devip, errsts, sdebug_jdelay,
+ return schedule_resp(scp, devip, errsts, pfp, 0, 0);
+ else if ((sdebug_jdelay || sdebug_ndelay) && (flags & F_LONG_DELAY)) {
+ /*
+ * If any delay is active, for F_SSU_DELAY want at least 1
+ * second and if sdebug_jdelay>0 want a long delay of that
+ * many seconds; for F_SYNC_DELAY want 1/20 of that.
+ */
+ int jdelay = (sdebug_jdelay < 2) ? 1 : sdebug_jdelay;
+ int denom = (flags & F_SYNC_DELAY) ? 20 : 1;
+
+ jdelay = mult_frac(USER_HZ * jdelay, HZ, denom * USER_HZ);
+ return schedule_resp(scp, devip, errsts, pfp, jdelay, 0);
+ } else
+ return schedule_resp(scp, devip, errsts, pfp, sdebug_jdelay,
sdebug_ndelay);
check_cond:
- return schedule_resp(scp, devip, check_condition_result, 0, 0);
+ return schedule_resp(scp, devip, check_condition_result, NULL, 0, 0);
err_out:
- return schedule_resp(scp, NULL, DID_NO_CONNECT << 16, 0, 0);
+ return schedule_resp(scp, NULL, DID_NO_CONNECT << 16, NULL, 0, 0);
}
static struct scsi_host_template sdebug_driver_template = {
@@ -5757,7 +5836,7 @@ static struct scsi_host_template sdebug_driver_template = {
.track_queue_depth = 1,
};
-static int sdebug_driver_probe(struct device * dev)
+static int sdebug_driver_probe(struct device *dev)
{
int error = 0;
struct sdebug_host_info *sdbg_host;
@@ -5782,8 +5861,7 @@ static int sdebug_driver_probe(struct device * dev)
}
/* Decide whether to tell scsi subsystem that we want mq */
/* Following should give the same answer for each host */
- sdebug_mq_active = shost_use_blk_mq(hpnt) && (submit_queues > 1);
- if (sdebug_mq_active)
+ if (shost_use_blk_mq(hpnt))
hpnt->nr_hw_queues = submit_queues;
sdbg_host->shost = hpnt;
@@ -5855,7 +5933,7 @@ static int sdebug_driver_probe(struct device * dev)
return error;
}
-static int sdebug_driver_remove(struct device * dev)
+static int sdebug_driver_remove(struct device *dev)
{
struct sdebug_host_info *sdbg_host;
struct sdebug_dev_info *sdbg_devinfo, *tmp;
diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c
index f3b117246d47..dd107dc4db0e 100644
--- a/drivers/scsi/scsi_devinfo.c
+++ b/drivers/scsi/scsi_devinfo.c
@@ -175,11 +175,6 @@ static struct {
{"HITACHI", "DISK-SUBSYSTEM", "*", BLIST_REPORTLUN2},
{"HITACHI", "HUS1530", "*", BLIST_NO_DIF},
{"HITACHI", "OPEN-", "*", BLIST_REPORTLUN2 | BLIST_TRY_VPD_PAGES},
- {"HITACHI", "OP-C-", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
- {"HITACHI", "3380-", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
- {"HITACHI", "3390-", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
- {"HITACHI", "6586-", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
- {"HITACHI", "6588-", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
{"HP", "A6189A", NULL, BLIST_SPARSELUN | BLIST_LARGELUN}, /* HP VA7400 */
{"HP", "OPEN-", "*", BLIST_REPORTLUN2 | BLIST_TRY_VPD_PAGES}, /* HP XP Arrays */
{"HP", "NetRAID-4M", NULL, BLIST_FORCELUN},
@@ -187,13 +182,7 @@ static struct {
{"HP", "C1557A", NULL, BLIST_FORCELUN},
{"HP", "C3323-300", "4269", BLIST_NOTQ},
{"HP", "C5713A", NULL, BLIST_NOREPORTLUN},
- {"HP", "DF400", "*", BLIST_REPORTLUN2},
- {"HP", "DF500", "*", BLIST_REPORTLUN2},
- {"HP", "OP-C-", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
- {"HP", "3380-", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
- {"HP", "3390-", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
- {"HP", "6586-", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
- {"HP", "6588-", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
+ {"HP", "DISK-SUBSYSTEM", "*", BLIST_REPORTLUN2},
{"IBM", "AuSaV1S2", NULL, BLIST_FORCELUN},
{"IBM", "ProFibre 4000R", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
{"IBM", "2105", NULL, BLIST_RETRY_HWERROR},
@@ -213,7 +202,7 @@ static struct {
{"Medion", "Flash XL MMC/SD", "2.6D", BLIST_FORCELUN},
{"MegaRAID", "LD", NULL, BLIST_FORCELUN},
{"MICROP", "4110", NULL, BLIST_NOTQ},
- {"MSFT", "Virtual HD", NULL, BLIST_NO_RSOC},
+ {"MSFT", "Virtual HD", NULL, BLIST_MAX_1024 | BLIST_NO_RSOC},
{"MYLEX", "DACARMRB", "*", BLIST_REPORTLUN2},
{"nCipher", "Fastness Crypto", NULL, BLIST_FORCELUN},
{"NAKAMICH", "MJ-4.8S", NULL, BLIST_FORCELUN | BLIST_SINGLELUN},
diff --git a/drivers/scsi/scsi_dh.c b/drivers/scsi/scsi_dh.c
index b88b5dbbc444..188f30572aa1 100644
--- a/drivers/scsi/scsi_dh.c
+++ b/drivers/scsi/scsi_dh.c
@@ -112,6 +112,9 @@ static struct scsi_device_handler *scsi_dh_lookup(const char *name)
{
struct scsi_device_handler *dh;
+ if (!name || strlen(name) == 0)
+ return NULL;
+
dh = __scsi_dh_lookup(name);
if (!dh) {
request_module("scsi_dh_%s", name);
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index ca53a5f785ee..946039117bf4 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -117,6 +117,12 @@ static int scsi_host_eh_past_deadline(struct Scsi_Host *shost)
/**
* scmd_eh_abort_handler - Handle command aborts
* @work: command to be aborted.
+ *
+ * Note: this function must be called only for a command that has timed out.
+ * Because the block layer marks a request as complete before it calls
+ * scsi_times_out(), a .scsi_done() call from the LLD for a command that has
+ * timed out do not have any effect. Hence it is safe to call
+ * scsi_finish_command() from this function.
*/
void
scmd_eh_abort_handler(struct work_struct *work)
@@ -1889,7 +1895,7 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd)
}
return FAILED;
- maybe_retry:
+maybe_retry:
/* we requeue for retry because the error was retryable, and
* the request was not marked fast fail. Note that above,
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index c84f931388f2..e9b4f279d29c 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -191,7 +191,19 @@ static void __scsi_queue_insert(struct scsi_cmnd *cmd, int reason, bool unbusy)
*/
cmd->result = 0;
if (q->mq_ops) {
- scsi_mq_requeue_cmd(cmd);
+ /*
+ * Before a SCSI command is dispatched,
+ * get_device(&sdev->sdev_gendev) is called and the host,
+ * target and device busy counters are increased. Since
+ * requeuing a request causes these actions to be repeated and
+ * since scsi_device_unbusy() has already been called,
+ * put_device(&device->sdev_gendev) must still be called. Call
+ * put_device() after blk_mq_requeue_request() to avoid that
+ * removal of the SCSI device can start before requeueing has
+ * happened.
+ */
+ blk_mq_requeue_request(cmd->request, true);
+ put_device(&device->sdev_gendev);
return;
}
spin_lock_irqsave(q->queue_lock, flags);
@@ -711,18 +723,25 @@ static bool scsi_end_request(struct request *req, blk_status_t error,
}
/**
- * __scsi_error_from_host_byte - translate SCSI error code into errno
- * @cmd: SCSI command (unused)
+ * scsi_result_to_blk_status - translate a SCSI result code into blk_status_t
+ * @cmd: SCSI command
* @result: scsi error code
*
- * Translate SCSI error code into block errors.
+ * Translate a SCSI result code into a blk_status_t value. May reset the host
+ * byte of @cmd->result.
*/
-static blk_status_t __scsi_error_from_host_byte(struct scsi_cmnd *cmd,
- int result)
+static blk_status_t scsi_result_to_blk_status(struct scsi_cmnd *cmd, int result)
{
switch (host_byte(result)) {
case DID_OK:
- return BLK_STS_OK;
+ /*
+ * Also check the other bytes than the status byte in result
+ * to handle the case when a SCSI LLD sets result to
+ * DRIVER_SENSE << 24 without setting SAM_STAT_CHECK_CONDITION.
+ */
+ if (scsi_status_is_good(result) && (result & ~0xff) == 0)
+ return BLK_STS_OK;
+ return BLK_STS_IOERR;
case DID_TRANSPORT_FAILFAST:
return BLK_STS_TRANSPORT;
case DID_TARGET_FAILURE:
@@ -800,10 +819,10 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
SCSI_SENSE_BUFFERSIZE);
}
if (!sense_deferred)
- error = __scsi_error_from_host_byte(cmd, result);
+ error = scsi_result_to_blk_status(cmd, result);
}
/*
- * __scsi_error_from_host_byte may have reset the host_byte
+ * scsi_result_to_blk_status may have reset the host_byte
*/
scsi_req(req)->result = cmd->result;
scsi_req(req)->resid_len = scsi_get_resid(cmd);
@@ -825,7 +844,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
* good_bytes != blk_rq_bytes(req) as the signal for an error.
* This sets the error explicitly for the problem case.
*/
- error = __scsi_error_from_host_byte(cmd, result);
+ error = scsi_result_to_blk_status(cmd, result);
}
/* no bidi support for !blk_rq_is_passthrough yet */
@@ -858,6 +877,17 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
/* for passthrough error may be set */
error = BLK_STS_OK;
}
+ /*
+ * Another corner case: the SCSI status byte is non-zero but 'good'.
+ * Example: PRE-FETCH command returns SAM_STAT_CONDITION_MET when
+ * it is able to fit nominated LBs in its cache (and SAM_STAT_GOOD
+ * if it can't fit). Treat SAM_STAT_CONDITION_MET and the related
+ * intermediate statuses (both obsolete in SAM-4) as good.
+ */
+ if (status_byte(result) && scsi_status_is_good(result)) {
+ result = 0;
+ error = BLK_STS_OK;
+ }
/*
* special case: failed zero length commands always need to
@@ -884,7 +914,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
if (result == 0)
goto requeue;
- error = __scsi_error_from_host_byte(cmd, result);
+ error = scsi_result_to_blk_status(cmd, result);
if (host_byte(result) == DID_RESET) {
/* Third party bus reset or reset for error recovery
@@ -2144,8 +2174,6 @@ void __scsi_init_queue(struct Scsi_Host *shost, struct request_queue *q)
{
struct device *dev = shost->dma_dev;
- queue_flag_set_unlocked(QUEUE_FLAG_SCSI_PASSTHROUGH, q);
-
/*
* this limit is imposed by hardware restrictions
*/
@@ -2227,7 +2255,7 @@ struct request_queue *scsi_old_alloc_queue(struct scsi_device *sdev)
struct Scsi_Host *shost = sdev->host;
struct request_queue *q;
- q = blk_alloc_queue_node(GFP_KERNEL, NUMA_NO_NODE);
+ q = blk_alloc_queue_node(GFP_KERNEL, NUMA_NO_NODE, NULL);
if (!q)
return NULL;
q->cmd_size = sizeof(struct scsi_cmnd) + shost->hostt->cmd_size;
@@ -2243,6 +2271,7 @@ struct request_queue *scsi_old_alloc_queue(struct scsi_device *sdev)
}
__scsi_init_queue(shost, q);
+ blk_queue_flag_set(QUEUE_FLAG_SCSI_PASSTHROUGH, q);
blk_queue_prep_rq(q, scsi_prep_fn);
blk_queue_unprep_rq(q, scsi_unprep_fn);
blk_queue_softirq_done(q, scsi_softirq_done);
@@ -2274,6 +2303,7 @@ struct request_queue *scsi_mq_alloc_queue(struct scsi_device *sdev)
sdev->request_queue->queuedata = sdev;
__scsi_init_queue(sdev->host, sdev->request_queue);
+ blk_queue_flag_set(QUEUE_FLAG_SCSI_PASSTHROUGH, sdev->request_queue);
return sdev->request_queue;
}
@@ -2611,7 +2641,7 @@ scsi_test_unit_ready(struct scsi_device *sdev, int timeout, int retries,
/* try to eat the UNIT_ATTENTION if there are enough retries */
do {
result = scsi_execute_req(sdev, cmd, DMA_NONE, NULL, 0, sshdr,
- timeout, retries, NULL);
+ timeout, 1, NULL);
if (sdev->removable && scsi_sense_valid(sshdr) &&
sshdr->sense_key == UNIT_ATTENTION)
sdev->changed = 1;
diff --git a/drivers/scsi/scsi_module.c b/drivers/scsi/scsi_module.c
deleted file mode 100644
index 489175833709..000000000000
--- a/drivers/scsi/scsi_module.c
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2003 Christoph Hellwig.
- * Released under GPL v2.
- *
- * Support for old-style host templates.
- *
- * NOTE: Do not use this for new drivers ever.
- */
-
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-
-#include <scsi/scsi_host.h>
-
-
-static int __init init_this_scsi_driver(void)
-{
- struct scsi_host_template *sht = &driver_template;
- struct Scsi_Host *shost;
- struct list_head *l;
- int error;
-
- if (!sht->release) {
- printk(KERN_ERR
- "scsi HBA driver %s didn't set a release method.\n",
- sht->name);
- return -EINVAL;
- }
-
- sht->module = THIS_MODULE;
- INIT_LIST_HEAD(&sht->legacy_hosts);
-
- sht->detect(sht);
- if (list_empty(&sht->legacy_hosts))
- return -ENODEV;
-
- list_for_each_entry(shost, &sht->legacy_hosts, sht_legacy_list) {
- error = scsi_add_host(shost, NULL);
- if (error)
- goto fail;
- scsi_scan_host(shost);
- }
- return 0;
- fail:
- l = &shost->sht_legacy_list;
- while ((l = l->prev) != &sht->legacy_hosts)
- scsi_remove_host(list_entry(l, struct Scsi_Host, sht_legacy_list));
- return error;
-}
-
-static void __exit exit_this_scsi_driver(void)
-{
- struct scsi_host_template *sht = &driver_template;
- struct Scsi_Host *shost, *s;
-
- list_for_each_entry(shost, &sht->legacy_hosts, sht_legacy_list)
- scsi_remove_host(shost);
- list_for_each_entry_safe(shost, s, &sht->legacy_hosts, sht_legacy_list)
- sht->release(shost);
-
- if (list_empty(&sht->legacy_hosts))
- return;
-
- printk(KERN_WARNING "%s did not call scsi_unregister\n", sht->name);
- dump_stack();
-
- list_for_each_entry_safe(shost, s, &sht->legacy_hosts, sht_legacy_list)
- scsi_unregister(shost);
-}
-
-module_init(init_this_scsi_driver);
-module_exit(exit_this_scsi_driver);
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index 91b90f672d23..1e36c9a9ad17 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -1292,8 +1292,7 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
transport_add_device(&sdev->sdev_gendev);
sdev->is_visible = 1;
- error = bsg_register_queue(rq, &sdev->sdev_gendev, NULL, NULL);
-
+ error = bsg_scsi_register_queue(rq, &sdev->sdev_gendev);
if (error)
/* we're treating error on bsg register as non-fatal,
* so pretend nothing went wrong */
@@ -1310,6 +1309,13 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
}
}
+ if (sdev->host->hostt->sdev_groups) {
+ error = sysfs_create_groups(&sdev->sdev_gendev.kobj,
+ sdev->host->hostt->sdev_groups);
+ if (error)
+ return error;
+ }
+
scsi_autopm_put_device(sdev);
return error;
}
@@ -1349,6 +1355,10 @@ void __scsi_remove_device(struct scsi_device *sdev)
if (res != 0)
return;
+ if (sdev->host->hostt->sdev_groups)
+ sysfs_remove_groups(&sdev->sdev_gendev.kobj,
+ sdev->host->hostt->sdev_groups);
+
bsg_unregister_queue(sdev->request_queue);
device_unregister(&sdev->sdev_dev);
transport_remove_device(dev);
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
index f4b52b44b966..65f6c94f2e9b 100644
--- a/drivers/scsi/scsi_transport_iscsi.c
+++ b/drivers/scsi/scsi_transport_iscsi.c
@@ -2322,6 +2322,12 @@ iscsi_multicast_skb(struct sk_buff *skb, uint32_t group, gfp_t gfp)
return nlmsg_multicast(nls, skb, 0, group, gfp);
}
+static int
+iscsi_unicast_skb(struct sk_buff *skb, u32 portid)
+{
+ return nlmsg_unicast(nls, skb, portid);
+}
+
int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr,
char *data, uint32_t data_size)
{
@@ -2524,14 +2530,11 @@ void iscsi_ping_comp_event(uint32_t host_no, struct iscsi_transport *transport,
EXPORT_SYMBOL_GPL(iscsi_ping_comp_event);
static int
-iscsi_if_send_reply(uint32_t group, int seq, int type, int done, int multi,
- void *payload, int size)
+iscsi_if_send_reply(u32 portid, int type, void *payload, int size)
{
struct sk_buff *skb;
struct nlmsghdr *nlh;
int len = nlmsg_total_size(size);
- int flags = multi ? NLM_F_MULTI : 0;
- int t = done ? NLMSG_DONE : type;
skb = alloc_skb(len, GFP_ATOMIC);
if (!skb) {
@@ -2539,10 +2542,9 @@ iscsi_if_send_reply(uint32_t group, int seq, int type, int done, int multi,
return -ENOMEM;
}
- nlh = __nlmsg_put(skb, 0, 0, t, (len - sizeof(*nlh)), 0);
- nlh->nlmsg_flags = flags;
+ nlh = __nlmsg_put(skb, 0, 0, type, (len - sizeof(*nlh)), 0);
memcpy(nlmsg_data(nlh), payload, size);
- return iscsi_multicast_skb(skb, group, GFP_ATOMIC);
+ return iscsi_unicast_skb(skb, portid);
}
static int
@@ -3470,6 +3472,7 @@ static int
iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
{
int err = 0;
+ u32 portid;
struct iscsi_uevent *ev = nlmsg_data(nlh);
struct iscsi_transport *transport = NULL;
struct iscsi_internal *priv;
@@ -3490,10 +3493,12 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
if (!try_module_get(transport->owner))
return -EINVAL;
+ portid = NETLINK_CB(skb).portid;
+
switch (nlh->nlmsg_type) {
case ISCSI_UEVENT_CREATE_SESSION:
err = iscsi_if_create_session(priv, ep, ev,
- NETLINK_CB(skb).portid,
+ portid,
ev->u.c_session.initial_cmdsn,
ev->u.c_session.cmds_max,
ev->u.c_session.queue_depth);
@@ -3506,7 +3511,7 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
}
err = iscsi_if_create_session(priv, ep, ev,
- NETLINK_CB(skb).portid,
+ portid,
ev->u.c_bound_session.initial_cmdsn,
ev->u.c_bound_session.cmds_max,
ev->u.c_bound_session.queue_depth);
@@ -3664,6 +3669,8 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
static void
iscsi_if_rx(struct sk_buff *skb)
{
+ u32 portid = NETLINK_CB(skb).portid;
+
mutex_lock(&rx_queue_mutex);
while (skb->len >= NLMSG_HDRLEN) {
int err;
@@ -3699,8 +3706,8 @@ iscsi_if_rx(struct sk_buff *skb)
break;
if (ev->type == ISCSI_UEVENT_GET_CHAP && !err)
break;
- err = iscsi_if_send_reply(group, nlh->nlmsg_seq,
- nlh->nlmsg_type, 0, 0, ev, sizeof(*ev));
+ err = iscsi_if_send_reply(portid, nlh->nlmsg_type,
+ ev, sizeof(*ev));
} while (err < 0 && err != -ECONNREFUSED && err != -ESRCH);
skb_pull(skb, rlen);
}
diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c
index 736a1f4f9676..08acbabfae07 100644
--- a/drivers/scsi/scsi_transport_sas.c
+++ b/drivers/scsi/scsi_transport_sas.c
@@ -227,8 +227,7 @@ static int sas_bsg_initialize(struct Scsi_Host *shost, struct sas_rphy *rphy)
* by default assume old behaviour and bounce for any highmem page
*/
blk_queue_bounce_limit(q, BLK_BOUNCE_HIGH);
- queue_flag_set_unlocked(QUEUE_FLAG_BIDI, q);
- queue_flag_set_unlocked(QUEUE_FLAG_SCSI_PASSTHROUGH, q);
+ blk_queue_flag_set(QUEUE_FLAG_BIDI, q);
return 0;
}
diff --git a/drivers/scsi/scsi_transport_spi.c b/drivers/scsi/scsi_transport_spi.c
index 871ea582029e..2ca150b16764 100644
--- a/drivers/scsi/scsi_transport_spi.c
+++ b/drivers/scsi/scsi_transport_spi.c
@@ -822,11 +822,11 @@ spi_dv_device_get_echo_buffer(struct scsi_device *sdev, u8 *buffer)
* fails, the device won't let us write to the echo buffer
* so just return failure */
- const char spi_test_unit_ready[] = {
+ static const char spi_test_unit_ready[] = {
TEST_UNIT_READY, 0, 0, 0, 0, 0
};
- const char spi_read_buffer_descriptor[] = {
+ static const char spi_read_buffer_descriptor[] = {
READ_BUFFER, 0x0b, 0, 0, 0, 0, 0, 0, 4, 0
};
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 3541caf3fceb..9421d9877730 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -714,7 +714,7 @@ static void sd_config_discard(struct scsi_disk *sdkp, unsigned int mode)
case SD_LBP_FULL:
case SD_LBP_DISABLE:
blk_queue_max_discard_sectors(q, 0);
- queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, q);
+ blk_queue_flag_clear(QUEUE_FLAG_DISCARD, q);
return;
case SD_LBP_UNMAP:
@@ -747,7 +747,7 @@ static void sd_config_discard(struct scsi_disk *sdkp, unsigned int mode)
}
blk_queue_max_discard_sectors(q, max_blocks * (logical_block_size >> 9));
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q);
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, q);
}
static int sd_setup_unmap_cmnd(struct scsi_cmnd *cmd)
@@ -2121,6 +2121,8 @@ sd_spinup_disk(struct scsi_disk *sdkp)
break; /* standby */
if (sshdr.asc == 4 && sshdr.ascq == 0xc)
break; /* unavailable */
+ if (sshdr.asc == 4 && sshdr.ascq == 0x1b)
+ break; /* sanitize in progress */
/*
* Issue command to spin up drive when not ready
*/
@@ -2484,6 +2486,8 @@ sd_read_capacity(struct scsi_disk *sdkp, unsigned char *buffer)
sector_size = old_sector_size;
goto got_data;
}
+ /* Remember that READ CAPACITY(16) succeeded */
+ sdp->try_rc_10_first = 0;
}
}
@@ -2953,8 +2957,8 @@ static void sd_read_block_characteristics(struct scsi_disk *sdkp)
rot = get_unaligned_be16(&buffer[4]);
if (rot == 1) {
- queue_flag_set_unlocked(QUEUE_FLAG_NONROT, q);
- queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, q);
+ blk_queue_flag_set(QUEUE_FLAG_NONROT, q);
+ blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, q);
}
if (sdkp->device->type == TYPE_ZBC) {
diff --git a/drivers/scsi/sd_zbc.c b/drivers/scsi/sd_zbc.c
index 89cf4498f535..210407cd2341 100644
--- a/drivers/scsi/sd_zbc.c
+++ b/drivers/scsi/sd_zbc.c
@@ -400,8 +400,10 @@ static int sd_zbc_check_capacity(struct scsi_disk *sdkp, unsigned char *buf)
*
* Check that all zones of the device are equal. The last zone can however
* be smaller. The zone size must also be a power of two number of LBAs.
+ *
+ * Returns the zone size in bytes upon success or an error code upon failure.
*/
-static int sd_zbc_check_zone_size(struct scsi_disk *sdkp)
+static s64 sd_zbc_check_zone_size(struct scsi_disk *sdkp)
{
u64 zone_blocks = 0;
sector_t block = 0;
@@ -412,8 +414,6 @@ static int sd_zbc_check_zone_size(struct scsi_disk *sdkp)
int ret;
u8 same;
- sdkp->zone_blocks = 0;
-
/* Get a buffer */
buf = kmalloc(SD_ZBC_BUF_SIZE, GFP_KERNEL);
if (!buf)
@@ -445,16 +445,17 @@ static int sd_zbc_check_zone_size(struct scsi_disk *sdkp)
/* Parse zone descriptors */
while (rec < buf + buf_len) {
- zone_blocks = get_unaligned_be64(&rec[8]);
- if (sdkp->zone_blocks == 0) {
- sdkp->zone_blocks = zone_blocks;
- } else if (zone_blocks != sdkp->zone_blocks &&
- (block + zone_blocks < sdkp->capacity
- || zone_blocks > sdkp->zone_blocks)) {
- zone_blocks = 0;
+ u64 this_zone_blocks = get_unaligned_be64(&rec[8]);
+
+ if (zone_blocks == 0) {
+ zone_blocks = this_zone_blocks;
+ } else if (this_zone_blocks != zone_blocks &&
+ (block + this_zone_blocks < sdkp->capacity
+ || this_zone_blocks > zone_blocks)) {
+ this_zone_blocks = 0;
goto out;
}
- block += zone_blocks;
+ block += this_zone_blocks;
rec += 64;
}
@@ -467,8 +468,6 @@ static int sd_zbc_check_zone_size(struct scsi_disk *sdkp)
} while (block < sdkp->capacity);
- zone_blocks = sdkp->zone_blocks;
-
out:
if (!zone_blocks) {
if (sdkp->first_scan)
@@ -488,8 +487,7 @@ out:
"Zone size too large\n");
ret = -ENODEV;
} else {
- sdkp->zone_blocks = zone_blocks;
- sdkp->zone_shift = ilog2(zone_blocks);
+ ret = zone_blocks;
}
out_free:
@@ -500,22 +498,23 @@ out_free:
/**
* sd_zbc_alloc_zone_bitmap - Allocate a zone bitmap (one bit per zone).
- * @sdkp: The disk of the bitmap
+ * @nr_zones: Number of zones to allocate space for.
+ * @numa_node: NUMA node to allocate the memory from.
*/
-static inline unsigned long *sd_zbc_alloc_zone_bitmap(struct scsi_disk *sdkp)
+static inline unsigned long *
+sd_zbc_alloc_zone_bitmap(u32 nr_zones, int numa_node)
{
- struct request_queue *q = sdkp->disk->queue;
-
- return kzalloc_node(BITS_TO_LONGS(sdkp->nr_zones)
- * sizeof(unsigned long),
- GFP_KERNEL, q->node);
+ return kzalloc_node(BITS_TO_LONGS(nr_zones) * sizeof(unsigned long),
+ GFP_KERNEL, numa_node);
}
/**
* sd_zbc_get_seq_zones - Parse report zones reply to identify sequential zones
* @sdkp: disk used
* @buf: report reply buffer
- * @seq_zone_bitamp: bitmap of sequential zones to set
+ * @buflen: length of @buf
+ * @zone_shift: logarithm base 2 of the number of blocks in a zone
+ * @seq_zones_bitmap: bitmap of sequential zones to set
*
* Parse reported zone descriptors in @buf to identify sequential zones and
* set the reported zone bit in @seq_zones_bitmap accordingly.
@@ -524,7 +523,7 @@ static inline unsigned long *sd_zbc_alloc_zone_bitmap(struct scsi_disk *sdkp)
* Return the LBA after the last zone reported.
*/
static sector_t sd_zbc_get_seq_zones(struct scsi_disk *sdkp, unsigned char *buf,
- unsigned int buflen,
+ unsigned int buflen, u32 zone_shift,
unsigned long *seq_zones_bitmap)
{
sector_t lba, next_lba = sdkp->capacity;
@@ -543,7 +542,7 @@ static sector_t sd_zbc_get_seq_zones(struct scsi_disk *sdkp, unsigned char *buf,
if (type != ZBC_ZONE_TYPE_CONV &&
cond != ZBC_ZONE_COND_READONLY &&
cond != ZBC_ZONE_COND_OFFLINE)
- set_bit(lba >> sdkp->zone_shift, seq_zones_bitmap);
+ set_bit(lba >> zone_shift, seq_zones_bitmap);
next_lba = lba + get_unaligned_be64(&rec[8]);
rec += 64;
}
@@ -552,12 +551,16 @@ static sector_t sd_zbc_get_seq_zones(struct scsi_disk *sdkp, unsigned char *buf,
}
/**
- * sd_zbc_setup_seq_zones_bitmap - Initialize the disk seq zone bitmap.
+ * sd_zbc_setup_seq_zones_bitmap - Initialize a seq zone bitmap.
* @sdkp: target disk
+ * @zone_shift: logarithm base 2 of the number of blocks in a zone
+ * @nr_zones: number of zones to set up a seq zone bitmap for
*
* Allocate a zone bitmap and initialize it by identifying sequential zones.
*/
-static int sd_zbc_setup_seq_zones_bitmap(struct scsi_disk *sdkp)
+static unsigned long *
+sd_zbc_setup_seq_zones_bitmap(struct scsi_disk *sdkp, u32 zone_shift,
+ u32 nr_zones)
{
struct request_queue *q = sdkp->disk->queue;
unsigned long *seq_zones_bitmap;
@@ -565,9 +568,9 @@ static int sd_zbc_setup_seq_zones_bitmap(struct scsi_disk *sdkp)
unsigned char *buf;
int ret = -ENOMEM;
- seq_zones_bitmap = sd_zbc_alloc_zone_bitmap(sdkp);
+ seq_zones_bitmap = sd_zbc_alloc_zone_bitmap(nr_zones, q->node);
if (!seq_zones_bitmap)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
buf = kmalloc(SD_ZBC_BUF_SIZE, GFP_KERNEL);
if (!buf)
@@ -578,7 +581,7 @@ static int sd_zbc_setup_seq_zones_bitmap(struct scsi_disk *sdkp)
if (ret)
goto out;
lba = sd_zbc_get_seq_zones(sdkp, buf, SD_ZBC_BUF_SIZE,
- seq_zones_bitmap);
+ zone_shift, seq_zones_bitmap);
}
if (lba != sdkp->capacity) {
@@ -590,12 +593,9 @@ out:
kfree(buf);
if (ret) {
kfree(seq_zones_bitmap);
- return ret;
+ return ERR_PTR(ret);
}
-
- q->seq_zones_bitmap = seq_zones_bitmap;
-
- return 0;
+ return seq_zones_bitmap;
}
static void sd_zbc_cleanup(struct scsi_disk *sdkp)
@@ -611,44 +611,64 @@ static void sd_zbc_cleanup(struct scsi_disk *sdkp)
q->nr_zones = 0;
}
-static int sd_zbc_setup(struct scsi_disk *sdkp)
+static int sd_zbc_setup(struct scsi_disk *sdkp, u32 zone_blocks)
{
struct request_queue *q = sdkp->disk->queue;
+ u32 zone_shift = ilog2(zone_blocks);
+ u32 nr_zones;
int ret;
- /* READ16/WRITE16 is mandatory for ZBC disks */
- sdkp->device->use_16_for_rw = 1;
- sdkp->device->use_10_for_rw = 0;
-
/* chunk_sectors indicates the zone size */
- blk_queue_chunk_sectors(sdkp->disk->queue,
- logical_to_sectors(sdkp->device, sdkp->zone_blocks));
- sdkp->nr_zones =
- round_up(sdkp->capacity, sdkp->zone_blocks) >> sdkp->zone_shift;
+ blk_queue_chunk_sectors(q,
+ logical_to_sectors(sdkp->device, zone_blocks));
+ nr_zones = round_up(sdkp->capacity, zone_blocks) >> zone_shift;
/*
* Initialize the device request queue information if the number
* of zones changed.
*/
- if (sdkp->nr_zones != q->nr_zones) {
-
- sd_zbc_cleanup(sdkp);
-
- q->nr_zones = sdkp->nr_zones;
- if (sdkp->nr_zones) {
- q->seq_zones_wlock = sd_zbc_alloc_zone_bitmap(sdkp);
- if (!q->seq_zones_wlock) {
+ if (nr_zones != sdkp->nr_zones || nr_zones != q->nr_zones) {
+ unsigned long *seq_zones_wlock = NULL, *seq_zones_bitmap = NULL;
+ size_t zone_bitmap_size;
+
+ if (nr_zones) {
+ seq_zones_wlock = sd_zbc_alloc_zone_bitmap(nr_zones,
+ q->node);
+ if (!seq_zones_wlock) {
ret = -ENOMEM;
goto err;
}
- ret = sd_zbc_setup_seq_zones_bitmap(sdkp);
- if (ret) {
- sd_zbc_cleanup(sdkp);
+ seq_zones_bitmap = sd_zbc_setup_seq_zones_bitmap(sdkp,
+ zone_shift, nr_zones);
+ if (IS_ERR(seq_zones_bitmap)) {
+ ret = PTR_ERR(seq_zones_bitmap);
+ kfree(seq_zones_wlock);
goto err;
}
}
-
+ zone_bitmap_size = BITS_TO_LONGS(nr_zones) *
+ sizeof(unsigned long);
+ blk_mq_freeze_queue(q);
+ if (q->nr_zones != nr_zones) {
+ /* READ16/WRITE16 is mandatory for ZBC disks */
+ sdkp->device->use_16_for_rw = 1;
+ sdkp->device->use_10_for_rw = 0;
+
+ sdkp->zone_blocks = zone_blocks;
+ sdkp->zone_shift = zone_shift;
+ sdkp->nr_zones = nr_zones;
+ q->nr_zones = nr_zones;
+ swap(q->seq_zones_wlock, seq_zones_wlock);
+ swap(q->seq_zones_bitmap, seq_zones_bitmap);
+ } else if (memcmp(q->seq_zones_bitmap, seq_zones_bitmap,
+ zone_bitmap_size) != 0) {
+ memcpy(q->seq_zones_bitmap, seq_zones_bitmap,
+ zone_bitmap_size);
+ }
+ blk_mq_unfreeze_queue(q);
+ kfree(seq_zones_wlock);
+ kfree(seq_zones_bitmap);
}
return 0;
@@ -660,6 +680,7 @@ err:
int sd_zbc_read_zones(struct scsi_disk *sdkp, unsigned char *buf)
{
+ int64_t zone_blocks;
int ret;
if (!sd_is_zoned(sdkp))
@@ -696,12 +717,16 @@ int sd_zbc_read_zones(struct scsi_disk *sdkp, unsigned char *buf)
* Check zone size: only devices with a constant zone size (except
* an eventual last runt zone) that is a power of 2 are supported.
*/
- ret = sd_zbc_check_zone_size(sdkp);
- if (ret)
+ zone_blocks = sd_zbc_check_zone_size(sdkp);
+ ret = -EFBIG;
+ if (zone_blocks != (u32)zone_blocks)
+ goto err;
+ ret = zone_blocks;
+ if (ret < 0)
goto err;
/* The drive satisfies the kernel restrictions: set it up */
- ret = sd_zbc_setup(sdkp);
+ ret = sd_zbc_setup(sdkp, zone_blocks);
if (ret)
goto err;
diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c
index b2880c7709e6..592b6dbf8b35 100644
--- a/drivers/scsi/smartpqi/smartpqi_init.c
+++ b/drivers/scsi/smartpqi/smartpqi_init.c
@@ -40,11 +40,11 @@
#define BUILD_TIMESTAMP
#endif
-#define DRIVER_VERSION "1.1.2-126"
+#define DRIVER_VERSION "1.1.4-115"
#define DRIVER_MAJOR 1
#define DRIVER_MINOR 1
-#define DRIVER_RELEASE 2
-#define DRIVER_REVISION 126
+#define DRIVER_RELEASE 4
+#define DRIVER_REVISION 115
#define DRIVER_NAME "Microsemi PQI Driver (v" \
DRIVER_VERSION BUILD_TIMESTAMP ")"
@@ -3898,29 +3898,6 @@ static int pqi_validate_device_capability(struct pqi_ctrl_info *ctrl_info)
return 0;
}
-static int pqi_delete_operational_queue(struct pqi_ctrl_info *ctrl_info,
- bool inbound_queue, u16 queue_id)
-{
- struct pqi_general_admin_request request;
- struct pqi_general_admin_response response;
-
- memset(&request, 0, sizeof(request));
- request.header.iu_type = PQI_REQUEST_IU_GENERAL_ADMIN;
- put_unaligned_le16(PQI_GENERAL_ADMIN_IU_LENGTH,
- &request.header.iu_length);
- if (inbound_queue)
- request.function_code =
- PQI_GENERAL_ADMIN_FUNCTION_DELETE_IQ;
- else
- request.function_code =
- PQI_GENERAL_ADMIN_FUNCTION_DELETE_OQ;
- put_unaligned_le16(queue_id,
- &request.data.delete_operational_queue.queue_id);
-
- return pqi_submit_admin_request_synchronous(ctrl_info, &request,
- &response);
-}
-
static int pqi_create_event_queue(struct pqi_ctrl_info *ctrl_info)
{
int rc;
@@ -4038,7 +4015,7 @@ static int pqi_create_queue_group(struct pqi_ctrl_info *ctrl_info,
if (rc) {
dev_err(&ctrl_info->pci_dev->dev,
"error creating inbound AIO queue\n");
- goto delete_inbound_queue_raid;
+ return rc;
}
queue_group->iq_pi[AIO_PATH] = ctrl_info->iomem_base +
@@ -4066,7 +4043,7 @@ static int pqi_create_queue_group(struct pqi_ctrl_info *ctrl_info,
if (rc) {
dev_err(&ctrl_info->pci_dev->dev,
"error changing queue property\n");
- goto delete_inbound_queue_aio;
+ return rc;
}
/*
@@ -4096,7 +4073,7 @@ static int pqi_create_queue_group(struct pqi_ctrl_info *ctrl_info,
if (rc) {
dev_err(&ctrl_info->pci_dev->dev,
"error creating outbound queue\n");
- goto delete_inbound_queue_aio;
+ return rc;
}
queue_group->oq_ci = ctrl_info->iomem_base +
@@ -4105,16 +4082,6 @@ static int pqi_create_queue_group(struct pqi_ctrl_info *ctrl_info,
&response.data.create_operational_oq.oq_ci_offset);
return 0;
-
-delete_inbound_queue_aio:
- pqi_delete_operational_queue(ctrl_info, true,
- queue_group->iq_id[AIO_PATH]);
-
-delete_inbound_queue_raid:
- pqi_delete_operational_queue(ctrl_info, true,
- queue_group->iq_id[RAID_PATH]);
-
- return rc;
}
static int pqi_create_queues(struct pqi_ctrl_info *ctrl_info)
@@ -5348,7 +5315,7 @@ static int pqi_map_queues(struct Scsi_Host *shost)
{
struct pqi_ctrl_info *ctrl_info = shost_to_hba(shost);
- return blk_mq_pci_map_queues(&shost->tag_set, ctrl_info->pci_dev);
+ return blk_mq_pci_map_queues(&shost->tag_set, ctrl_info->pci_dev, 0);
}
static int pqi_getpciinfo_ioctl(struct pqi_ctrl_info *ctrl_info,
@@ -6797,6 +6764,14 @@ static __maybe_unused int pqi_resume(struct pci_dev *pci_dev)
static const struct pci_device_id pqi_pci_id_table[] = {
{
PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
+ 0x105b, 0x1211)
+ },
+ {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
+ 0x105b, 0x1321)
+ },
+ {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
0x152d, 0x8a22)
},
{
@@ -6817,6 +6792,38 @@ static const struct pci_device_id pqi_pci_id_table[] = {
},
{
PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
+ 0x193d, 0x8460)
+ },
+ {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
+ 0x193d, 0x8461)
+ },
+ {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
+ 0x193d, 0xf460)
+ },
+ {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
+ 0x193d, 0xf461)
+ },
+ {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
+ 0x1bd4, 0x0045)
+ },
+ {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
+ 0x1bd4, 0x0046)
+ },
+ {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
+ 0x1bd4, 0x0047)
+ },
+ {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
+ 0x1bd4, 0x0048)
+ },
+ {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
PCI_VENDOR_ID_ADAPTEC2, 0x0110)
},
{
@@ -6917,6 +6924,10 @@ static const struct pci_device_id pqi_pci_id_table[] = {
},
{
PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
+ PCI_VENDOR_ID_ADAPTEC2, 0x1282)
+ },
+ {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
PCI_VENDOR_ID_ADAPTEC2, 0x1300)
},
{
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index 9be34d37c356..3f3cb72e0c0c 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -525,6 +525,8 @@ static int sr_block_open(struct block_device *bdev, fmode_t mode)
struct scsi_cd *cd;
int ret = -ENXIO;
+ check_disk_change(bdev);
+
mutex_lock(&sr_mutex);
cd = scsi_cd_get(bdev->bd_disk);
if (cd) {
@@ -585,18 +587,28 @@ out:
static unsigned int sr_block_check_events(struct gendisk *disk,
unsigned int clearing)
{
- struct scsi_cd *cd = scsi_cd(disk);
+ unsigned int ret = 0;
+ struct scsi_cd *cd;
- if (atomic_read(&cd->device->disk_events_disable_depth))
+ cd = scsi_cd_get(disk);
+ if (!cd)
return 0;
- return cdrom_check_events(&cd->cdi, clearing);
+ if (!atomic_read(&cd->device->disk_events_disable_depth))
+ ret = cdrom_check_events(&cd->cdi, clearing);
+
+ scsi_cd_put(cd);
+ return ret;
}
static int sr_block_revalidate_disk(struct gendisk *disk)
{
- struct scsi_cd *cd = scsi_cd(disk);
struct scsi_sense_hdr sshdr;
+ struct scsi_cd *cd;
+
+ cd = scsi_cd_get(disk);
+ if (!cd)
+ return -ENXIO;
/* if the unit is not ready, nothing more to do */
if (scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES, &sshdr))
@@ -605,6 +617,7 @@ static int sr_block_revalidate_disk(struct gendisk *disk)
sr_cd_check(&cd->cdi);
get_sectorsize(cd);
out:
+ scsi_cd_put(cd);
return 0;
}
diff --git a/drivers/scsi/sun3x_esp.c b/drivers/scsi/sun3x_esp.c
index d50c5ed8f428..0b1421cdf8a0 100644
--- a/drivers/scsi/sun3x_esp.c
+++ b/drivers/scsi/sun3x_esp.c
@@ -210,7 +210,7 @@ static int esp_sun3x_probe(struct platform_device *dev)
esp = shost_priv(host);
esp->host = host;
- esp->dev = dev;
+ esp->dev = &dev->dev;
esp->ops = &sun3x_esp_ops;
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
diff --git a/drivers/scsi/sym53c416.c b/drivers/scsi/sym53c416.c
deleted file mode 100644
index 5bdcbe8fa958..000000000000
--- a/drivers/scsi/sym53c416.c
+++ /dev/null
@@ -1,844 +0,0 @@
-/*
- * sym53c416.c
- * Low-level SCSI driver for sym53c416 chip.
- * Copyright (C) 1998 Lieven Willems (lw_linux@hotmail.com)
- *
- * Changes :
- *
- * Marcelo Tosatti <marcelo@conectiva.com.br> : Added io_request_lock locking
- * Alan Cox <alan@lxorguk.ukuu.org.uk> : Cleaned up code formatting
- * Fixed an irq locking bug
- * Added ISAPnP support
- * Bjoern A. Zeeb <bzeeb@zabbadoz.net> : Initial irq locking updates
- * Added another card with ISAPnP support
- *
- * LILO command line usage: sym53c416=<PORTBASE>[,<IRQ>]
- *
- * 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.
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/init.h>
-#include <linux/string.h>
-#include <linux/ioport.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/proc_fs.h>
-#include <linux/spinlock.h>
-#include <asm/dma.h>
-#include <asm/io.h>
-#include <linux/blkdev.h>
-#include <linux/isapnp.h>
-#include "scsi.h"
-#include <scsi/scsi_host.h>
-#include "sym53c416.h"
-
-#define VERSION_STRING "Version 1.0.0-ac"
-
-#define TC_LOW 0x00 /* Transfer counter low */
-#define TC_MID 0x01 /* Transfer counter mid */
-#define SCSI_FIFO 0x02 /* SCSI FIFO register */
-#define COMMAND_REG 0x03 /* Command Register */
-#define STATUS_REG 0x04 /* Status Register (READ) */
-#define DEST_BUS_ID 0x04 /* Destination Bus ID (WRITE) */
-#define INT_REG 0x05 /* Interrupt Register (READ) */
-#define TOM 0x05 /* Time out multiplier (WRITE) */
-#define STP 0x06 /* Synchronous Transfer period */
-#define SYNC_OFFSET 0x07 /* Synchronous Offset */
-#define CONF_REG_1 0x08 /* Configuration register 1 */
-#define CONF_REG_2 0x0B /* Configuration register 2 */
-#define CONF_REG_3 0x0C /* Configuration register 3 */
-#define CONF_REG_4 0x0D /* Configuration register 4 */
-#define TC_HIGH 0x0E /* Transfer counter high */
-#define PIO_FIFO_1 0x10 /* PIO FIFO register 1 */
-#define PIO_FIFO_2 0x11 /* PIO FIFO register 2 */
-#define PIO_FIFO_3 0x12 /* PIO FIFO register 3 */
-#define PIO_FIFO_4 0x13 /* PIO FIFO register 4 */
-#define PIO_FIFO_CNT 0x14 /* PIO FIFO count */
-#define PIO_INT_REG 0x15 /* PIO interrupt register */
-#define CONF_REG_5 0x16 /* Configuration register 5 */
-#define FEATURE_EN 0x1D /* Feature Enable register */
-
-/* Configuration register 1 entries: */
-/* Bits 2-0: SCSI ID of host adapter */
-#define SCM 0x80 /* Slow Cable Mode */
-#define SRID 0x40 /* SCSI Reset Interrupt Disable */
-#define PTM 0x20 /* Parity Test Mode */
-#define EPC 0x10 /* Enable Parity Checking */
-#define CTME 0x08 /* Special Test Mode */
-
-/* Configuration register 2 entries: */
-#define FE 0x40 /* Features Enable */
-#define SCSI2 0x08 /* SCSI 2 Enable */
-#define TBPA 0x04 /* Target Bad Parity Abort */
-
-/* Configuration register 3 entries: */
-#define IDMRC 0x80 /* ID Message Reserved Check */
-#define QTE 0x40 /* Queue Tag Enable */
-#define CDB10 0x20 /* Command Descriptor Block 10 */
-#define FSCSI 0x10 /* FastSCSI */
-#define FCLK 0x08 /* FastClock */
-
-/* Configuration register 4 entries: */
-#define RBS 0x08 /* Register bank select */
-#define EAN 0x04 /* Enable Active Negotiation */
-
-/* Configuration register 5 entries: */
-#define LPSR 0x80 /* Lower Power SCSI Reset */
-#define IE 0x20 /* Interrupt Enable */
-#define LPM 0x02 /* Low Power Mode */
-#define WSE0 0x01 /* 0WS Enable */
-
-/* Interrupt register entries: */
-#define SRST 0x80 /* SCSI Reset */
-#define ILCMD 0x40 /* Illegal Command */
-#define DIS 0x20 /* Disconnect */
-#define BS 0x10 /* Bus Service */
-#define FC 0x08 /* Function Complete */
-#define RESEL 0x04 /* Reselected */
-#define SI 0x03 /* Selection Interrupt */
-
-/* Status Register Entries: */
-#define SCI 0x80 /* SCSI Core Int */
-#define GE 0x40 /* Gross Error */
-#define PE 0x20 /* Parity Error */
-#define TC 0x10 /* Terminal Count */
-#define VGC 0x08 /* Valid Group Code */
-#define PHBITS 0x07 /* Phase bits */
-
-/* PIO Interrupt Register Entries: */
-#define SCI 0x80 /* SCSI Core Int */
-#define PFI 0x40 /* PIO FIFO Interrupt */
-#define FULL 0x20 /* PIO FIFO Full */
-#define EMPTY 0x10 /* PIO FIFO Empty */
-#define CE 0x08 /* Collision Error */
-#define OUE 0x04 /* Overflow / Underflow error */
-#define FIE 0x02 /* Full Interrupt Enable */
-#define EIE 0x01 /* Empty Interrupt Enable */
-
-/* SYM53C416 SCSI phases (lower 3 bits of SYM53C416_STATUS_REG) */
-#define PHASE_DATA_OUT 0x00
-#define PHASE_DATA_IN 0x01
-#define PHASE_COMMAND 0x02
-#define PHASE_STATUS 0x03
-#define PHASE_RESERVED_1 0x04
-#define PHASE_RESERVED_2 0x05
-#define PHASE_MESSAGE_OUT 0x06
-#define PHASE_MESSAGE_IN 0x07
-
-/* SYM53C416 core commands */
-#define NOOP 0x00
-#define FLUSH_FIFO 0x01
-#define RESET_CHIP 0x02
-#define RESET_SCSI_BUS 0x03
-#define DISABLE_SEL_RESEL 0x45
-#define RESEL_SEQ 0x40
-#define SEL_WITHOUT_ATN_SEQ 0x41
-#define SEL_WITH_ATN_SEQ 0x42
-#define SEL_WITH_ATN_AND_STOP_SEQ 0x43
-#define ENABLE_SEL_RESEL 0x44
-#define SEL_WITH_ATN3_SEQ 0x46
-#define RESEL3_SEQ 0x47
-#define SND_MSG 0x20
-#define SND_STAT 0x21
-#define SND_DATA 0x22
-#define DISCONNECT_SEQ 0x23
-#define TERMINATE_SEQ 0x24
-#define TARGET_COMM_COMPLETE_SEQ 0x25
-#define DISCONN 0x27
-#define RECV_MSG_SEQ 0x28
-#define RECV_CMD 0x29
-#define RECV_DATA 0x2A
-#define RECV_CMD_SEQ 0x2B
-#define TARGET_ABORT_PIO 0x04
-#define TRANSFER_INFORMATION 0x10
-#define INIT_COMM_COMPLETE_SEQ 0x11
-#define MSG_ACCEPTED 0x12
-#define TRANSFER_PAD 0x18
-#define SET_ATN 0x1A
-#define RESET_ATN 0x1B
-#define ILLEGAL 0xFF
-
-#define PIO_MODE 0x80
-
-#define IO_RANGE 0x20 /* 0x00 - 0x1F */
-#define ID "sym53c416" /* Attention: copied to the sym53c416.h */
-#define PIO_SIZE 128 /* Size of PIO fifo is 128 bytes */
-
-#define READ_TIMEOUT 150
-#define WRITE_TIMEOUT 150
-
-#ifdef MODULE
-
-#define sym53c416_base sym53c416
-#define sym53c416_base_1 sym53c416_1
-#define sym53c416_base_2 sym53c416_2
-#define sym53c416_base_3 sym53c416_3
-
-static unsigned int sym53c416_base[2];
-static unsigned int sym53c416_base_1[2];
-static unsigned int sym53c416_base_2[2];
-static unsigned int sym53c416_base_3[2];
-
-#endif
-
-#define MAXHOSTS 4
-
-#define SG_ADDRESS(buffer) ((char *) sg_virt((buffer)))
-
-enum phases
-{
- idle,
- data_out,
- data_in,
- command_ph,
- status_ph,
- message_out,
- message_in
-};
-
-typedef struct
-{
- int base;
- int irq;
- int scsi_id;
-} host;
-
-static host hosts[MAXHOSTS] = {
- {0, 0, SYM53C416_SCSI_ID},
- {0, 0, SYM53C416_SCSI_ID},
- {0, 0, SYM53C416_SCSI_ID},
- {0, 0, SYM53C416_SCSI_ID}
- };
-
-static int host_index = 0;
-static char info[120];
-static Scsi_Cmnd *current_command = NULL;
-static int fastpio = 1;
-
-static int probeaddrs[] = {0x200, 0x220, 0x240, 0};
-
-static void sym53c416_set_transfer_counter(int base, unsigned int len)
-{
- /* Program Transfer Counter */
- outb(len & 0x0000FF, base + TC_LOW);
- outb((len & 0x00FF00) >> 8, base + TC_MID);
- outb((len & 0xFF0000) >> 16, base + TC_HIGH);
-}
-
-static DEFINE_SPINLOCK(sym53c416_lock);
-
-/* Returns the number of bytes read */
-static __inline__ unsigned int sym53c416_read(int base, unsigned char *buffer, unsigned int len)
-{
- unsigned int orig_len = len;
- unsigned long flags = 0;
- unsigned int bytes_left;
- unsigned long i;
- int timeout = READ_TIMEOUT;
-
- /* Do transfer */
- spin_lock_irqsave(&sym53c416_lock, flags);
- while(len && timeout)
- {
- bytes_left = inb(base + PIO_FIFO_CNT); /* Number of bytes in the PIO FIFO */
- if(fastpio && bytes_left > 3)
- {
- insl(base + PIO_FIFO_1, buffer, bytes_left >> 2);
- buffer += bytes_left & 0xFC;
- len -= bytes_left & 0xFC;
- }
- else if(bytes_left > 0)
- {
- len -= bytes_left;
- for(; bytes_left > 0; bytes_left--)
- *(buffer++) = inb(base + PIO_FIFO_1);
- }
- else
- {
- i = jiffies + timeout;
- spin_unlock_irqrestore(&sym53c416_lock, flags);
- while(time_before(jiffies, i) && (inb(base + PIO_INT_REG) & EMPTY) && timeout)
- if(inb(base + PIO_INT_REG) & SCI)
- timeout = 0;
- spin_lock_irqsave(&sym53c416_lock, flags);
- if(inb(base + PIO_INT_REG) & EMPTY)
- timeout = 0;
- }
- }
- spin_unlock_irqrestore(&sym53c416_lock, flags);
- return orig_len - len;
-}
-
-/* Returns the number of bytes written */
-static __inline__ unsigned int sym53c416_write(int base, unsigned char *buffer, unsigned int len)
-{
- unsigned int orig_len = len;
- unsigned long flags = 0;
- unsigned int bufferfree;
- unsigned long i;
- unsigned int timeout = WRITE_TIMEOUT;
-
- /* Do transfer */
- spin_lock_irqsave(&sym53c416_lock, flags);
- while(len && timeout)
- {
- bufferfree = PIO_SIZE - inb(base + PIO_FIFO_CNT);
- if(bufferfree > len)
- bufferfree = len;
- if(fastpio && bufferfree > 3)
- {
- outsl(base + PIO_FIFO_1, buffer, bufferfree >> 2);
- buffer += bufferfree & 0xFC;
- len -= bufferfree & 0xFC;
- }
- else if(bufferfree > 0)
- {
- len -= bufferfree;
- for(; bufferfree > 0; bufferfree--)
- outb(*(buffer++), base + PIO_FIFO_1);
- }
- else
- {
- i = jiffies + timeout;
- spin_unlock_irqrestore(&sym53c416_lock, flags);
- while(time_before(jiffies, i) && (inb(base + PIO_INT_REG) & FULL) && timeout)
- ;
- spin_lock_irqsave(&sym53c416_lock, flags);
- if(inb(base + PIO_INT_REG) & FULL)
- timeout = 0;
- }
- }
- spin_unlock_irqrestore(&sym53c416_lock, flags);
- return orig_len - len;
-}
-
-static irqreturn_t sym53c416_intr_handle(int irq, void *dev_id)
-{
- struct Scsi_Host *dev = dev_id;
- int base = dev->io_port;
- int i;
- unsigned long flags = 0;
- unsigned char status_reg, pio_int_reg, int_reg;
- struct scatterlist *sg;
- unsigned int tot_trans = 0;
-
- spin_lock_irqsave(dev->host_lock,flags);
- status_reg = inb(base + STATUS_REG);
- pio_int_reg = inb(base + PIO_INT_REG);
- int_reg = inb(base + INT_REG);
- spin_unlock_irqrestore(dev->host_lock, flags);
-
- /* First, we handle error conditions */
- if(int_reg & SCI) /* SCSI Reset */
- {
- printk(KERN_DEBUG "sym53c416: Reset received\n");
- current_command->SCp.phase = idle;
- current_command->result = DID_RESET << 16;
- spin_lock_irqsave(dev->host_lock, flags);
- current_command->scsi_done(current_command);
- spin_unlock_irqrestore(dev->host_lock, flags);
- goto out;
- }
- if(int_reg & ILCMD) /* Illegal Command */
- {
- printk(KERN_WARNING "sym53c416: Illegal Command: 0x%02x.\n", inb(base + COMMAND_REG));
- current_command->SCp.phase = idle;
- current_command->result = DID_ERROR << 16;
- spin_lock_irqsave(dev->host_lock, flags);
- current_command->scsi_done(current_command);
- spin_unlock_irqrestore(dev->host_lock, flags);
- goto out;
- }
- if(status_reg & GE) /* Gross Error */
- {
- printk(KERN_WARNING "sym53c416: Controller reports gross error.\n");
- current_command->SCp.phase = idle;
- current_command->result = DID_ERROR << 16;
- spin_lock_irqsave(dev->host_lock, flags);
- current_command->scsi_done(current_command);
- spin_unlock_irqrestore(dev->host_lock, flags);
- goto out;
- }
- if(status_reg & PE) /* Parity Error */
- {
- printk(KERN_WARNING "sym53c416:SCSI parity error.\n");
- current_command->SCp.phase = idle;
- current_command->result = DID_PARITY << 16;
- spin_lock_irqsave(dev->host_lock, flags);
- current_command->scsi_done(current_command);
- spin_unlock_irqrestore(dev->host_lock, flags);
- goto out;
- }
- if(pio_int_reg & (CE | OUE))
- {
- printk(KERN_WARNING "sym53c416: PIO interrupt error.\n");
- current_command->SCp.phase = idle;
- current_command->result = DID_ERROR << 16;
- spin_lock_irqsave(dev->host_lock, flags);
- current_command->scsi_done(current_command);
- spin_unlock_irqrestore(dev->host_lock, flags);
- goto out;
- }
- if(int_reg & DIS) /* Disconnect */
- {
- if(current_command->SCp.phase != message_in)
- current_command->result = DID_NO_CONNECT << 16;
- else
- current_command->result = (current_command->SCp.Status & 0xFF) | ((current_command->SCp.Message & 0xFF) << 8) | (DID_OK << 16);
- current_command->SCp.phase = idle;
- spin_lock_irqsave(dev->host_lock, flags);
- current_command->scsi_done(current_command);
- spin_unlock_irqrestore(dev->host_lock, flags);
- goto out;
- }
- /* Now we handle SCSI phases */
-
- switch(status_reg & PHBITS) /* Filter SCSI phase out of status reg */
- {
- case PHASE_DATA_OUT:
- {
- if(int_reg & BS)
- {
- current_command->SCp.phase = data_out;
- outb(FLUSH_FIFO, base + COMMAND_REG);
- sym53c416_set_transfer_counter(base,
- scsi_bufflen(current_command));
- outb(TRANSFER_INFORMATION | PIO_MODE, base + COMMAND_REG);
-
- scsi_for_each_sg(current_command,
- sg, scsi_sg_count(current_command), i) {
- tot_trans += sym53c416_write(base,
- SG_ADDRESS(sg),
- sg->length);
- }
- if(tot_trans < current_command->underflow)
- printk(KERN_WARNING "sym53c416: Underflow, wrote %d bytes, request for %d bytes.\n", tot_trans, current_command->underflow);
- }
- break;
- }
-
- case PHASE_DATA_IN:
- {
- if(int_reg & BS)
- {
- current_command->SCp.phase = data_in;
- outb(FLUSH_FIFO, base + COMMAND_REG);
- sym53c416_set_transfer_counter(base,
- scsi_bufflen(current_command));
-
- outb(TRANSFER_INFORMATION | PIO_MODE, base + COMMAND_REG);
-
- scsi_for_each_sg(current_command,
- sg, scsi_sg_count(current_command), i) {
- tot_trans += sym53c416_read(base,
- SG_ADDRESS(sg),
- sg->length);
- }
- if(tot_trans < current_command->underflow)
- printk(KERN_WARNING "sym53c416: Underflow, read %d bytes, request for %d bytes.\n", tot_trans, current_command->underflow);
- }
- break;
- }
-
- case PHASE_COMMAND:
- {
- current_command->SCp.phase = command_ph;
- printk(KERN_ERR "sym53c416: Unknown interrupt in command phase.\n");
- break;
- }
-
- case PHASE_STATUS:
- {
- current_command->SCp.phase = status_ph;
- outb(FLUSH_FIFO, base + COMMAND_REG);
- outb(INIT_COMM_COMPLETE_SEQ, base + COMMAND_REG);
- break;
- }
-
- case PHASE_RESERVED_1:
- case PHASE_RESERVED_2:
- {
- printk(KERN_ERR "sym53c416: Reserved phase occurred.\n");
- break;
- }
-
- case PHASE_MESSAGE_OUT:
- {
- current_command->SCp.phase = message_out;
- outb(SET_ATN, base + COMMAND_REG);
- outb(MSG_ACCEPTED, base + COMMAND_REG);
- break;
- }
-
- case PHASE_MESSAGE_IN:
- {
- current_command->SCp.phase = message_in;
- current_command->SCp.Status = inb(base + SCSI_FIFO);
- current_command->SCp.Message = inb(base + SCSI_FIFO);
- if(current_command->SCp.Message == SAVE_POINTERS || current_command->SCp.Message == DISCONNECT)
- outb(SET_ATN, base + COMMAND_REG);
- outb(MSG_ACCEPTED, base + COMMAND_REG);
- break;
- }
- }
-out:
- return IRQ_HANDLED;
-}
-
-static void sym53c416_init(int base, int scsi_id)
-{
- outb(RESET_CHIP, base + COMMAND_REG);
- outb(NOOP, base + COMMAND_REG);
- outb(0x99, base + TOM); /* Time out of 250 ms */
- outb(0x05, base + STP);
- outb(0x00, base + SYNC_OFFSET);
- outb(EPC | scsi_id, base + CONF_REG_1);
- outb(FE | SCSI2 | TBPA, base + CONF_REG_2);
- outb(IDMRC | QTE | CDB10 | FSCSI | FCLK, base + CONF_REG_3);
- outb(0x83 | EAN, base + CONF_REG_4);
- outb(IE | WSE0, base + CONF_REG_5);
- outb(0, base + FEATURE_EN);
-}
-
-static int sym53c416_probeirq(int base, int scsi_id)
-{
- int irq, irqs;
- unsigned long i;
-
- /* Clear interrupt register */
- inb(base + INT_REG);
- /* Start probing for irq's */
- irqs = probe_irq_on();
- /* Reinit chip */
- sym53c416_init(base, scsi_id);
- /* Cause interrupt */
- outb(NOOP, base + COMMAND_REG);
- outb(ILLEGAL, base + COMMAND_REG);
- outb(0x07, base + DEST_BUS_ID);
- outb(0x00, base + DEST_BUS_ID);
- /* Wait for interrupt to occur */
- i = jiffies + 20;
- while(time_before(jiffies, i) && !(inb(base + STATUS_REG) & SCI))
- barrier();
- if(time_before_eq(i, jiffies)) /* timed out */
- return 0;
- /* Get occurred irq */
- irq = probe_irq_off(irqs);
- sym53c416_init(base, scsi_id);
- return irq;
-}
-
-/* Setup: sym53c416=base,irq */
-void sym53c416_setup(char *str, int *ints)
-{
- int i;
-
- if(host_index >= MAXHOSTS)
- {
- printk(KERN_WARNING "sym53c416: Too many hosts defined\n");
- return;
- }
- if(ints[0] < 1 || ints[0] > 2)
- {
- printk(KERN_ERR "sym53c416: Wrong number of parameters:\n");
- printk(KERN_ERR "sym53c416: usage: sym53c416=<base>[,<irq>]\n");
- return;
- }
- for(i = 0; i < host_index && i >= 0; i++)
- if(hosts[i].base == ints[1])
- i = -2;
- if(i >= 0)
- {
- hosts[host_index].base = ints[1];
- hosts[host_index].irq = (ints[0] == 2)? ints[2] : 0;
- host_index++;
- }
-}
-
-static int sym53c416_test(int base)
-{
- outb(RESET_CHIP, base + COMMAND_REG);
- outb(NOOP, base + COMMAND_REG);
- if(inb(base + COMMAND_REG) != NOOP)
- return 0;
- if(!inb(base + TC_HIGH) || inb(base + TC_HIGH) == 0xFF)
- return 0;
- if((inb(base + PIO_INT_REG) & (FULL | EMPTY | CE | OUE | FIE | EIE)) != EMPTY)
- return 0;
- return 1;
-}
-
-
-static struct isapnp_device_id id_table[] = {
- { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
- ISAPNP_VENDOR('S','L','I'), ISAPNP_FUNCTION(0x4161), 0 },
- { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
- ISAPNP_VENDOR('S','L','I'), ISAPNP_FUNCTION(0x4163), 0 },
- { ISAPNP_DEVICE_SINGLE_END }
-};
-
-MODULE_DEVICE_TABLE(isapnp, id_table);
-
-static void sym53c416_probe(void)
-{
- int *base = probeaddrs;
- int ints[2];
-
- ints[0] = 1;
- for(; *base; base++) {
- if (request_region(*base, IO_RANGE, ID)) {
- if (sym53c416_test(*base)) {
- ints[1] = *base;
- sym53c416_setup(NULL, ints);
- }
- release_region(*base, IO_RANGE);
- }
- }
-}
-
-int __init sym53c416_detect(struct scsi_host_template *tpnt)
-{
- unsigned long flags;
- struct Scsi_Host * shpnt = NULL;
- int i;
- int count;
- struct pnp_dev *idev = NULL;
-
-#ifdef MODULE
- int ints[3];
-
- ints[0] = 2;
- if(sym53c416_base[0])
- {
- ints[1] = sym53c416_base[0];
- ints[2] = sym53c416_base[1];
- sym53c416_setup(NULL, ints);
- }
- if(sym53c416_base_1[0])
- {
- ints[1] = sym53c416_base_1[0];
- ints[2] = sym53c416_base_1[1];
- sym53c416_setup(NULL, ints);
- }
- if(sym53c416_base_2[0])
- {
- ints[1] = sym53c416_base_2[0];
- ints[2] = sym53c416_base_2[1];
- sym53c416_setup(NULL, ints);
- }
- if(sym53c416_base_3[0])
- {
- ints[1] = sym53c416_base_3[0];
- ints[2] = sym53c416_base_3[1];
- sym53c416_setup(NULL, ints);
- }
-#endif
- printk(KERN_INFO "sym53c416.c: %s\n", VERSION_STRING);
-
- for (i=0; id_table[i].vendor != 0; i++) {
- while((idev=pnp_find_dev(NULL, id_table[i].vendor,
- id_table[i].function, idev))!=NULL)
- {
- int i[3];
-
- if(pnp_device_attach(idev)<0)
- {
- printk(KERN_WARNING "sym53c416: unable to attach PnP device.\n");
- continue;
- }
- if(pnp_activate_dev(idev) < 0)
- {
- printk(KERN_WARNING "sym53c416: unable to activate PnP device.\n");
- pnp_device_detach(idev);
- continue;
-
- }
-
- i[0] = 2;
- i[1] = pnp_port_start(idev, 0);
- i[2] = pnp_irq(idev, 0);
-
- printk(KERN_INFO "sym53c416: ISAPnP card found and configured at 0x%X, IRQ %d.\n",
- i[1], i[2]);
- sym53c416_setup(NULL, i);
- }
- }
- sym53c416_probe();
-
- /* Now we register and set up each host adapter found... */
- for(count = 0, i = 0; i < host_index; i++) {
- if (!request_region(hosts[i].base, IO_RANGE, ID))
- continue;
- if (!sym53c416_test(hosts[i].base)) {
- printk(KERN_WARNING "No sym53c416 found at address 0x%03x\n", hosts[i].base);
- goto fail_release_region;
- }
-
- /* We don't have an irq yet, so we should probe for one */
- if (!hosts[i].irq)
- hosts[i].irq = sym53c416_probeirq(hosts[i].base, hosts[i].scsi_id);
- if (!hosts[i].irq)
- goto fail_release_region;
-
- shpnt = scsi_register(tpnt, 0);
- if (!shpnt)
- goto fail_release_region;
- /* Request for specified IRQ */
- if (request_irq(hosts[i].irq, sym53c416_intr_handle, 0, ID, shpnt))
- goto fail_free_host;
-
- spin_lock_irqsave(&sym53c416_lock, flags);
- shpnt->unique_id = hosts[i].base;
- shpnt->io_port = hosts[i].base;
- shpnt->n_io_port = IO_RANGE;
- shpnt->irq = hosts[i].irq;
- shpnt->this_id = hosts[i].scsi_id;
- sym53c416_init(hosts[i].base, hosts[i].scsi_id);
- count++;
- spin_unlock_irqrestore(&sym53c416_lock, flags);
- continue;
-
- fail_free_host:
- scsi_unregister(shpnt);
- fail_release_region:
- release_region(hosts[i].base, IO_RANGE);
- }
- return count;
-}
-
-const char *sym53c416_info(struct Scsi_Host *SChost)
-{
- int i;
- int base = SChost->io_port;
- int irq = SChost->irq;
- int scsi_id = 0;
- int rev = inb(base + TC_HIGH);
-
- for(i = 0; i < host_index; i++)
- if(hosts[i].base == base)
- scsi_id = hosts[i].scsi_id;
- sprintf(info, "Symbios Logic 53c416 (rev. %d) at 0x%03x, irq %d, SCSI-ID %d, %s pio", rev, base, irq, scsi_id, (fastpio)? "fast" : "slow");
- return info;
-}
-
-static int sym53c416_queuecommand_lck(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
-{
- int base;
- unsigned long flags = 0;
- int i;
-
- /* Store base register as we can have more than one controller in the system */
- base = SCpnt->device->host->io_port;
- current_command = SCpnt; /* set current command */
- current_command->scsi_done = done; /* set ptr to done function */
- current_command->SCp.phase = command_ph; /* currect phase is the command phase */
- current_command->SCp.Status = 0;
- current_command->SCp.Message = 0;
-
- spin_lock_irqsave(&sym53c416_lock, flags);
- outb(scmd_id(SCpnt), base + DEST_BUS_ID); /* Set scsi id target */
- outb(FLUSH_FIFO, base + COMMAND_REG); /* Flush SCSI and PIO FIFO's */
- /* Write SCSI command into the SCSI fifo */
- for(i = 0; i < SCpnt->cmd_len; i++)
- outb(SCpnt->cmnd[i], base + SCSI_FIFO);
- /* Start selection sequence */
- outb(SEL_WITHOUT_ATN_SEQ, base + COMMAND_REG);
- /* Now an interrupt will be generated which we will catch in out interrupt routine */
- spin_unlock_irqrestore(&sym53c416_lock, flags);
- return 0;
-}
-
-DEF_SCSI_QCMD(sym53c416_queuecommand)
-
-static int sym53c416_host_reset(Scsi_Cmnd *SCpnt)
-{
- int base;
- int scsi_id = -1;
- int i;
- unsigned long flags;
-
- spin_lock_irqsave(&sym53c416_lock, flags);
-
- /* printk("sym53c416_reset\n"); */
- base = SCpnt->device->host->io_port;
- /* search scsi_id - fixme, we shouldn't need to iterate for this! */
- for(i = 0; i < host_index && scsi_id == -1; i++)
- if(hosts[i].base == base)
- scsi_id = hosts[i].scsi_id;
- outb(RESET_CHIP, base + COMMAND_REG);
- outb(NOOP | PIO_MODE, base + COMMAND_REG);
- outb(RESET_SCSI_BUS, base + COMMAND_REG);
- sym53c416_init(base, scsi_id);
-
- spin_unlock_irqrestore(&sym53c416_lock, flags);
- return SUCCESS;
-}
-
-static int sym53c416_release(struct Scsi_Host *shost)
-{
- if (shost->irq)
- free_irq(shost->irq, shost);
- if (shost->io_port && shost->n_io_port)
- release_region(shost->io_port, shost->n_io_port);
- return 0;
-}
-
-static int sym53c416_bios_param(struct scsi_device *sdev,
- struct block_device *dev,
- sector_t capacity, int *ip)
-{
- int size;
-
- size = capacity;
- ip[0] = 64; /* heads */
- ip[1] = 32; /* sectors */
- if((ip[2] = size >> 11) > 1024) /* cylinders, test for big disk */
- {
- ip[0] = 255; /* heads */
- ip[1] = 63; /* sectors */
- ip[2] = size / (255 * 63); /* cylinders */
- }
- return 0;
-}
-
-/* Loadable module support */
-#ifdef MODULE
-
-MODULE_AUTHOR("Lieven Willems");
-MODULE_LICENSE("GPL");
-
-module_param_array(sym53c416, uint, NULL, 0);
-module_param_array(sym53c416_1, uint, NULL, 0);
-module_param_array(sym53c416_2, uint, NULL, 0);
-module_param_array(sym53c416_3, uint, NULL, 0);
-
-#endif
-
-static struct scsi_host_template driver_template = {
- .proc_name = "sym53c416",
- .name = "Symbios Logic 53c416",
- .detect = sym53c416_detect,
- .info = sym53c416_info,
- .queuecommand = sym53c416_queuecommand,
- .eh_host_reset_handler =sym53c416_host_reset,
- .release = sym53c416_release,
- .bios_param = sym53c416_bios_param,
- .can_queue = 1,
- .this_id = SYM53C416_SCSI_ID,
- .sg_tablesize = 32,
- .unchecked_isa_dma = 1,
- .use_clustering = ENABLE_CLUSTERING,
-};
-#include "scsi_module.c"
diff --git a/drivers/scsi/sym53c416.h b/drivers/scsi/sym53c416.h
deleted file mode 100644
index 387de5d80a70..000000000000
--- a/drivers/scsi/sym53c416.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * sym53c416.h
- *
- * Copyright (C) 1998 Lieven Willems (lw_linux@hotmail.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, 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.
- *
- */
-
-#ifndef _SYM53C416_H
-#define _SYM53C416_H
-
-#include <linux/types.h>
-
-#define SYM53C416_SCSI_ID 7
-
-static int sym53c416_detect(struct scsi_host_template *);
-static const char *sym53c416_info(struct Scsi_Host *);
-static int sym53c416_release(struct Scsi_Host *);
-static int sym53c416_queuecommand(struct Scsi_Host *, struct scsi_cmnd *);
-static int sym53c416_host_reset(Scsi_Cmnd *);
-static int sym53c416_bios_param(struct scsi_device *, struct block_device *,
- sector_t, int *);
-static void sym53c416_setup(char *str, int *ints);
-#endif
diff --git a/drivers/scsi/sym53c8xx_2/sym_glue.c b/drivers/scsi/sym53c8xx_2/sym_glue.c
index 791a2182de53..7320d5fe4cbc 100644
--- a/drivers/scsi/sym53c8xx_2/sym_glue.c
+++ b/drivers/scsi/sym53c8xx_2/sym_glue.c
@@ -1393,7 +1393,7 @@ static struct Scsi_Host *sym_attach(struct scsi_host_template *tpnt, int unit,
scsi_host_put(shost);
return NULL;
- }
+}
/*
diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile
index 9310c6c83041..918f5791202d 100644
--- a/drivers/scsi/ufs/Makefile
+++ b/drivers/scsi/ufs/Makefile
@@ -3,6 +3,7 @@
obj-$(CONFIG_SCSI_UFS_DWC_TC_PCI) += tc-dwc-g210-pci.o ufshcd-dwc.o tc-dwc-g210.o
obj-$(CONFIG_SCSI_UFS_DWC_TC_PLATFORM) += tc-dwc-g210-pltfrm.o ufshcd-dwc.o tc-dwc-g210.o
obj-$(CONFIG_SCSI_UFS_QCOM) += ufs-qcom.o
-obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o
+obj-$(CONFIG_SCSI_UFSHCD) += ufshcd-core.o
+ufshcd-core-objs := ufshcd.o ufs-sysfs.o
obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o
obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o
diff --git a/drivers/scsi/ufs/tc-dwc-g210-pci.c b/drivers/scsi/ufs/tc-dwc-g210-pci.c
index 325d5e14fc0d..2f41722a8c28 100644
--- a/drivers/scsi/ufs/tc-dwc-g210-pci.c
+++ b/drivers/scsi/ufs/tc-dwc-g210-pci.c
@@ -51,7 +51,7 @@ static int tc_dwc_g210_pci_runtime_idle(struct device *dev)
return ufshcd_runtime_idle(dev_get_drvdata(dev));
}
-/**
+/*
* struct ufs_hba_dwc_vops - UFS DWC specific variant operations
*/
static struct ufs_hba_variant_ops tc_dwc_g210_pci_hba_vops = {
@@ -71,7 +71,7 @@ static void tc_dwc_g210_pci_shutdown(struct pci_dev *pdev)
/**
* tc_dwc_g210_pci_remove - de-allocate PCI/SCSI host and host memory space
* data structure memory
- * @pdev - pointer to PCI handle
+ * @pdev: pointer to PCI handle
*/
static void tc_dwc_g210_pci_remove(struct pci_dev *pdev)
{
diff --git a/drivers/scsi/ufs/tc-dwc-g210-pltfrm.c b/drivers/scsi/ufs/tc-dwc-g210-pltfrm.c
index 2d3f5270f875..6dfe5a9206e9 100644
--- a/drivers/scsi/ufs/tc-dwc-g210-pltfrm.c
+++ b/drivers/scsi/ufs/tc-dwc-g210-pltfrm.c
@@ -20,7 +20,7 @@
#include "ufshcd-dwc.h"
#include "tc-dwc-g210.h"
-/**
+/*
* UFS DWC specific variant operations
*/
static struct ufs_hba_variant_ops tc_dwc_g210_20bit_pltfm_hba_vops = {
diff --git a/drivers/scsi/ufs/ufs-sysfs.c b/drivers/scsi/ufs/ufs-sysfs.c
new file mode 100644
index 000000000000..8d9332bb7d0c
--- /dev/null
+++ b/drivers/scsi/ufs/ufs-sysfs.c
@@ -0,0 +1,817 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Western Digital Corporation
+
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/bitfield.h>
+#include <asm/unaligned.h>
+
+#include "ufs.h"
+#include "ufs-sysfs.h"
+
+static const char *ufschd_uic_link_state_to_string(
+ enum uic_link_state state)
+{
+ switch (state) {
+ case UIC_LINK_OFF_STATE: return "OFF";
+ case UIC_LINK_ACTIVE_STATE: return "ACTIVE";
+ case UIC_LINK_HIBERN8_STATE: return "HIBERN8";
+ default: return "UNKNOWN";
+ }
+}
+
+static const char *ufschd_ufs_dev_pwr_mode_to_string(
+ enum ufs_dev_pwr_mode state)
+{
+ switch (state) {
+ case UFS_ACTIVE_PWR_MODE: return "ACTIVE";
+ case UFS_SLEEP_PWR_MODE: return "SLEEP";
+ case UFS_POWERDOWN_PWR_MODE: return "POWERDOWN";
+ default: return "UNKNOWN";
+ }
+}
+
+static inline ssize_t ufs_sysfs_pm_lvl_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count,
+ bool rpm)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+ unsigned long flags, value;
+
+ if (kstrtoul(buf, 0, &value))
+ return -EINVAL;
+
+ if (value >= UFS_PM_LVL_MAX)
+ return -EINVAL;
+
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ if (rpm)
+ hba->rpm_lvl = value;
+ else
+ hba->spm_lvl = value;
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ return count;
+}
+
+static ssize_t rpm_lvl_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", hba->rpm_lvl);
+}
+
+static ssize_t rpm_lvl_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ return ufs_sysfs_pm_lvl_store(dev, attr, buf, count, true);
+}
+
+static ssize_t rpm_target_dev_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", ufschd_ufs_dev_pwr_mode_to_string(
+ ufs_pm_lvl_states[hba->rpm_lvl].dev_state));
+}
+
+static ssize_t rpm_target_link_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", ufschd_uic_link_state_to_string(
+ ufs_pm_lvl_states[hba->rpm_lvl].link_state));
+}
+
+static ssize_t spm_lvl_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", hba->spm_lvl);
+}
+
+static ssize_t spm_lvl_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ return ufs_sysfs_pm_lvl_store(dev, attr, buf, count, false);
+}
+
+static ssize_t spm_target_dev_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", ufschd_ufs_dev_pwr_mode_to_string(
+ ufs_pm_lvl_states[hba->spm_lvl].dev_state));
+}
+
+static ssize_t spm_target_link_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", ufschd_uic_link_state_to_string(
+ ufs_pm_lvl_states[hba->spm_lvl].link_state));
+}
+
+static void ufshcd_auto_hibern8_update(struct ufs_hba *hba, u32 ahit)
+{
+ unsigned long flags;
+
+ if (!(hba->capabilities & MASK_AUTO_HIBERN8_SUPPORT))
+ return;
+
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ if (hba->ahit == ahit)
+ goto out_unlock;
+ hba->ahit = ahit;
+ if (!pm_runtime_suspended(hba->dev))
+ ufshcd_writel(hba, hba->ahit, REG_AUTO_HIBERNATE_IDLE_TIMER);
+out_unlock:
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+}
+
+/* Convert Auto-Hibernate Idle Timer register value to microseconds */
+static int ufshcd_ahit_to_us(u32 ahit)
+{
+ int timer = FIELD_GET(UFSHCI_AHIBERN8_TIMER_MASK, ahit);
+ int scale = FIELD_GET(UFSHCI_AHIBERN8_SCALE_MASK, ahit);
+
+ for (; scale > 0; --scale)
+ timer *= UFSHCI_AHIBERN8_SCALE_FACTOR;
+
+ return timer;
+}
+
+/* Convert microseconds to Auto-Hibernate Idle Timer register value */
+static u32 ufshcd_us_to_ahit(unsigned int timer)
+{
+ unsigned int scale;
+
+ for (scale = 0; timer > UFSHCI_AHIBERN8_TIMER_MASK; ++scale)
+ timer /= UFSHCI_AHIBERN8_SCALE_FACTOR;
+
+ return FIELD_PREP(UFSHCI_AHIBERN8_TIMER_MASK, timer) |
+ FIELD_PREP(UFSHCI_AHIBERN8_SCALE_MASK, scale);
+}
+
+static ssize_t auto_hibern8_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+
+ if (!(hba->capabilities & MASK_AUTO_HIBERN8_SUPPORT))
+ return -EOPNOTSUPP;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", ufshcd_ahit_to_us(hba->ahit));
+}
+
+static ssize_t auto_hibern8_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+ unsigned int timer;
+
+ if (!(hba->capabilities & MASK_AUTO_HIBERN8_SUPPORT))
+ return -EOPNOTSUPP;
+
+ if (kstrtouint(buf, 0, &timer))
+ return -EINVAL;
+
+ if (timer > UFSHCI_AHIBERN8_MAX)
+ return -EINVAL;
+
+ ufshcd_auto_hibern8_update(hba, ufshcd_us_to_ahit(timer));
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(rpm_lvl);
+static DEVICE_ATTR_RO(rpm_target_dev_state);
+static DEVICE_ATTR_RO(rpm_target_link_state);
+static DEVICE_ATTR_RW(spm_lvl);
+static DEVICE_ATTR_RO(spm_target_dev_state);
+static DEVICE_ATTR_RO(spm_target_link_state);
+static DEVICE_ATTR_RW(auto_hibern8);
+
+static struct attribute *ufs_sysfs_ufshcd_attrs[] = {
+ &dev_attr_rpm_lvl.attr,
+ &dev_attr_rpm_target_dev_state.attr,
+ &dev_attr_rpm_target_link_state.attr,
+ &dev_attr_spm_lvl.attr,
+ &dev_attr_spm_target_dev_state.attr,
+ &dev_attr_spm_target_link_state.attr,
+ &dev_attr_auto_hibern8.attr,
+ NULL
+};
+
+static const struct attribute_group ufs_sysfs_default_group = {
+ .attrs = ufs_sysfs_ufshcd_attrs,
+};
+
+static ssize_t ufs_sysfs_read_desc_param(struct ufs_hba *hba,
+ enum desc_idn desc_id,
+ u8 desc_index,
+ u8 param_offset,
+ u8 *sysfs_buf,
+ u8 param_size)
+{
+ u8 desc_buf[8] = {0};
+ int ret;
+
+ if (param_size > 8)
+ return -EINVAL;
+
+ ret = ufshcd_read_desc_param(hba, desc_id, desc_index,
+ param_offset, desc_buf, param_size);
+ if (ret)
+ return -EINVAL;
+ switch (param_size) {
+ case 1:
+ ret = sprintf(sysfs_buf, "0x%02X\n", *desc_buf);
+ break;
+ case 2:
+ ret = sprintf(sysfs_buf, "0x%04X\n",
+ get_unaligned_be16(desc_buf));
+ break;
+ case 4:
+ ret = sprintf(sysfs_buf, "0x%08X\n",
+ get_unaligned_be32(desc_buf));
+ break;
+ case 8:
+ ret = sprintf(sysfs_buf, "0x%016llX\n",
+ get_unaligned_be64(desc_buf));
+ break;
+ }
+
+ return ret;
+}
+
+#define UFS_DESC_PARAM(_name, _puname, _duname, _size) \
+static ssize_t _name##_show(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct ufs_hba *hba = dev_get_drvdata(dev); \
+ return ufs_sysfs_read_desc_param(hba, QUERY_DESC_IDN_##_duname, \
+ 0, _duname##_DESC_PARAM##_puname, buf, _size); \
+} \
+static DEVICE_ATTR_RO(_name)
+
+#define UFS_DEVICE_DESC_PARAM(_name, _uname, _size) \
+ UFS_DESC_PARAM(_name, _uname, DEVICE, _size)
+
+UFS_DEVICE_DESC_PARAM(device_type, _DEVICE_TYPE, 1);
+UFS_DEVICE_DESC_PARAM(device_class, _DEVICE_CLASS, 1);
+UFS_DEVICE_DESC_PARAM(device_sub_class, _DEVICE_SUB_CLASS, 1);
+UFS_DEVICE_DESC_PARAM(protocol, _PRTCL, 1);
+UFS_DEVICE_DESC_PARAM(number_of_luns, _NUM_LU, 1);
+UFS_DEVICE_DESC_PARAM(number_of_wluns, _NUM_WLU, 1);
+UFS_DEVICE_DESC_PARAM(boot_enable, _BOOT_ENBL, 1);
+UFS_DEVICE_DESC_PARAM(descriptor_access_enable, _DESC_ACCSS_ENBL, 1);
+UFS_DEVICE_DESC_PARAM(initial_power_mode, _INIT_PWR_MODE, 1);
+UFS_DEVICE_DESC_PARAM(high_priority_lun, _HIGH_PR_LUN, 1);
+UFS_DEVICE_DESC_PARAM(secure_removal_type, _SEC_RMV_TYPE, 1);
+UFS_DEVICE_DESC_PARAM(support_security_lun, _SEC_LU, 1);
+UFS_DEVICE_DESC_PARAM(bkops_termination_latency, _BKOP_TERM_LT, 1);
+UFS_DEVICE_DESC_PARAM(initial_active_icc_level, _ACTVE_ICC_LVL, 1);
+UFS_DEVICE_DESC_PARAM(specification_version, _SPEC_VER, 2);
+UFS_DEVICE_DESC_PARAM(manufacturing_date, _MANF_DATE, 2);
+UFS_DEVICE_DESC_PARAM(manufacturer_id, _MANF_ID, 2);
+UFS_DEVICE_DESC_PARAM(rtt_capability, _RTT_CAP, 1);
+UFS_DEVICE_DESC_PARAM(rtc_update, _FRQ_RTC, 2);
+UFS_DEVICE_DESC_PARAM(ufs_features, _UFS_FEAT, 1);
+UFS_DEVICE_DESC_PARAM(ffu_timeout, _FFU_TMT, 1);
+UFS_DEVICE_DESC_PARAM(queue_depth, _Q_DPTH, 1);
+UFS_DEVICE_DESC_PARAM(device_version, _DEV_VER, 2);
+UFS_DEVICE_DESC_PARAM(number_of_secure_wpa, _NUM_SEC_WPA, 1);
+UFS_DEVICE_DESC_PARAM(psa_max_data_size, _PSA_MAX_DATA, 4);
+UFS_DEVICE_DESC_PARAM(psa_state_timeout, _PSA_TMT, 1);
+
+static struct attribute *ufs_sysfs_device_descriptor[] = {
+ &dev_attr_device_type.attr,
+ &dev_attr_device_class.attr,
+ &dev_attr_device_sub_class.attr,
+ &dev_attr_protocol.attr,
+ &dev_attr_number_of_luns.attr,
+ &dev_attr_number_of_wluns.attr,
+ &dev_attr_boot_enable.attr,
+ &dev_attr_descriptor_access_enable.attr,
+ &dev_attr_initial_power_mode.attr,
+ &dev_attr_high_priority_lun.attr,
+ &dev_attr_secure_removal_type.attr,
+ &dev_attr_support_security_lun.attr,
+ &dev_attr_bkops_termination_latency.attr,
+ &dev_attr_initial_active_icc_level.attr,
+ &dev_attr_specification_version.attr,
+ &dev_attr_manufacturing_date.attr,
+ &dev_attr_manufacturer_id.attr,
+ &dev_attr_rtt_capability.attr,
+ &dev_attr_rtc_update.attr,
+ &dev_attr_ufs_features.attr,
+ &dev_attr_ffu_timeout.attr,
+ &dev_attr_queue_depth.attr,
+ &dev_attr_device_version.attr,
+ &dev_attr_number_of_secure_wpa.attr,
+ &dev_attr_psa_max_data_size.attr,
+ &dev_attr_psa_state_timeout.attr,
+ NULL,
+};
+
+static const struct attribute_group ufs_sysfs_device_descriptor_group = {
+ .name = "device_descriptor",
+ .attrs = ufs_sysfs_device_descriptor,
+};
+
+#define UFS_INTERCONNECT_DESC_PARAM(_name, _uname, _size) \
+ UFS_DESC_PARAM(_name, _uname, INTERCONNECT, _size)
+
+UFS_INTERCONNECT_DESC_PARAM(unipro_version, _UNIPRO_VER, 2);
+UFS_INTERCONNECT_DESC_PARAM(mphy_version, _MPHY_VER, 2);
+
+static struct attribute *ufs_sysfs_interconnect_descriptor[] = {
+ &dev_attr_unipro_version.attr,
+ &dev_attr_mphy_version.attr,
+ NULL,
+};
+
+static const struct attribute_group ufs_sysfs_interconnect_descriptor_group = {
+ .name = "interconnect_descriptor",
+ .attrs = ufs_sysfs_interconnect_descriptor,
+};
+
+#define UFS_GEOMETRY_DESC_PARAM(_name, _uname, _size) \
+ UFS_DESC_PARAM(_name, _uname, GEOMETRY, _size)
+
+UFS_GEOMETRY_DESC_PARAM(raw_device_capacity, _DEV_CAP, 8);
+UFS_GEOMETRY_DESC_PARAM(max_number_of_luns, _MAX_NUM_LUN, 1);
+UFS_GEOMETRY_DESC_PARAM(segment_size, _SEG_SIZE, 4);
+UFS_GEOMETRY_DESC_PARAM(allocation_unit_size, _ALLOC_UNIT_SIZE, 1);
+UFS_GEOMETRY_DESC_PARAM(min_addressable_block_size, _MIN_BLK_SIZE, 1);
+UFS_GEOMETRY_DESC_PARAM(optimal_read_block_size, _OPT_RD_BLK_SIZE, 1);
+UFS_GEOMETRY_DESC_PARAM(optimal_write_block_size, _OPT_WR_BLK_SIZE, 1);
+UFS_GEOMETRY_DESC_PARAM(max_in_buffer_size, _MAX_IN_BUF_SIZE, 1);
+UFS_GEOMETRY_DESC_PARAM(max_out_buffer_size, _MAX_OUT_BUF_SIZE, 1);
+UFS_GEOMETRY_DESC_PARAM(rpmb_rw_size, _RPMB_RW_SIZE, 1);
+UFS_GEOMETRY_DESC_PARAM(dyn_capacity_resource_policy, _DYN_CAP_RSRC_PLC, 1);
+UFS_GEOMETRY_DESC_PARAM(data_ordering, _DATA_ORDER, 1);
+UFS_GEOMETRY_DESC_PARAM(max_number_of_contexts, _MAX_NUM_CTX, 1);
+UFS_GEOMETRY_DESC_PARAM(sys_data_tag_unit_size, _TAG_UNIT_SIZE, 1);
+UFS_GEOMETRY_DESC_PARAM(sys_data_tag_resource_size, _TAG_RSRC_SIZE, 1);
+UFS_GEOMETRY_DESC_PARAM(secure_removal_types, _SEC_RM_TYPES, 1);
+UFS_GEOMETRY_DESC_PARAM(memory_types, _MEM_TYPES, 2);
+UFS_GEOMETRY_DESC_PARAM(sys_code_memory_max_alloc_units,
+ _SCM_MAX_NUM_UNITS, 4);
+UFS_GEOMETRY_DESC_PARAM(sys_code_memory_capacity_adjustment_factor,
+ _SCM_CAP_ADJ_FCTR, 2);
+UFS_GEOMETRY_DESC_PARAM(non_persist_memory_max_alloc_units,
+ _NPM_MAX_NUM_UNITS, 4);
+UFS_GEOMETRY_DESC_PARAM(non_persist_memory_capacity_adjustment_factor,
+ _NPM_CAP_ADJ_FCTR, 2);
+UFS_GEOMETRY_DESC_PARAM(enh1_memory_max_alloc_units,
+ _ENM1_MAX_NUM_UNITS, 4);
+UFS_GEOMETRY_DESC_PARAM(enh1_memory_capacity_adjustment_factor,
+ _ENM1_CAP_ADJ_FCTR, 2);
+UFS_GEOMETRY_DESC_PARAM(enh2_memory_max_alloc_units,
+ _ENM2_MAX_NUM_UNITS, 4);
+UFS_GEOMETRY_DESC_PARAM(enh2_memory_capacity_adjustment_factor,
+ _ENM2_CAP_ADJ_FCTR, 2);
+UFS_GEOMETRY_DESC_PARAM(enh3_memory_max_alloc_units,
+ _ENM3_MAX_NUM_UNITS, 4);
+UFS_GEOMETRY_DESC_PARAM(enh3_memory_capacity_adjustment_factor,
+ _ENM3_CAP_ADJ_FCTR, 2);
+UFS_GEOMETRY_DESC_PARAM(enh4_memory_max_alloc_units,
+ _ENM4_MAX_NUM_UNITS, 4);
+UFS_GEOMETRY_DESC_PARAM(enh4_memory_capacity_adjustment_factor,
+ _ENM4_CAP_ADJ_FCTR, 2);
+
+static struct attribute *ufs_sysfs_geometry_descriptor[] = {
+ &dev_attr_raw_device_capacity.attr,
+ &dev_attr_max_number_of_luns.attr,
+ &dev_attr_segment_size.attr,
+ &dev_attr_allocation_unit_size.attr,
+ &dev_attr_min_addressable_block_size.attr,
+ &dev_attr_optimal_read_block_size.attr,
+ &dev_attr_optimal_write_block_size.attr,
+ &dev_attr_max_in_buffer_size.attr,
+ &dev_attr_max_out_buffer_size.attr,
+ &dev_attr_rpmb_rw_size.attr,
+ &dev_attr_dyn_capacity_resource_policy.attr,
+ &dev_attr_data_ordering.attr,
+ &dev_attr_max_number_of_contexts.attr,
+ &dev_attr_sys_data_tag_unit_size.attr,
+ &dev_attr_sys_data_tag_resource_size.attr,
+ &dev_attr_secure_removal_types.attr,
+ &dev_attr_memory_types.attr,
+ &dev_attr_sys_code_memory_max_alloc_units.attr,
+ &dev_attr_sys_code_memory_capacity_adjustment_factor.attr,
+ &dev_attr_non_persist_memory_max_alloc_units.attr,
+ &dev_attr_non_persist_memory_capacity_adjustment_factor.attr,
+ &dev_attr_enh1_memory_max_alloc_units.attr,
+ &dev_attr_enh1_memory_capacity_adjustment_factor.attr,
+ &dev_attr_enh2_memory_max_alloc_units.attr,
+ &dev_attr_enh2_memory_capacity_adjustment_factor.attr,
+ &dev_attr_enh3_memory_max_alloc_units.attr,
+ &dev_attr_enh3_memory_capacity_adjustment_factor.attr,
+ &dev_attr_enh4_memory_max_alloc_units.attr,
+ &dev_attr_enh4_memory_capacity_adjustment_factor.attr,
+ NULL,
+};
+
+static const struct attribute_group ufs_sysfs_geometry_descriptor_group = {
+ .name = "geometry_descriptor",
+ .attrs = ufs_sysfs_geometry_descriptor,
+};
+
+#define UFS_HEALTH_DESC_PARAM(_name, _uname, _size) \
+ UFS_DESC_PARAM(_name, _uname, HEALTH, _size)
+
+UFS_HEALTH_DESC_PARAM(eol_info, _EOL_INFO, 1);
+UFS_HEALTH_DESC_PARAM(life_time_estimation_a, _LIFE_TIME_EST_A, 1);
+UFS_HEALTH_DESC_PARAM(life_time_estimation_b, _LIFE_TIME_EST_B, 1);
+
+static struct attribute *ufs_sysfs_health_descriptor[] = {
+ &dev_attr_eol_info.attr,
+ &dev_attr_life_time_estimation_a.attr,
+ &dev_attr_life_time_estimation_b.attr,
+ NULL,
+};
+
+static const struct attribute_group ufs_sysfs_health_descriptor_group = {
+ .name = "health_descriptor",
+ .attrs = ufs_sysfs_health_descriptor,
+};
+
+#define UFS_POWER_DESC_PARAM(_name, _uname, _index) \
+static ssize_t _name##_index##_show(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct ufs_hba *hba = dev_get_drvdata(dev); \
+ return ufs_sysfs_read_desc_param(hba, QUERY_DESC_IDN_POWER, 0, \
+ PWR_DESC##_uname##_0 + _index * 2, buf, 2); \
+} \
+static DEVICE_ATTR_RO(_name##_index)
+
+UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 0);
+UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 1);
+UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 2);
+UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 3);
+UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 4);
+UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 5);
+UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 6);
+UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 7);
+UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 8);
+UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 9);
+UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 10);
+UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 11);
+UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 12);
+UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 13);
+UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 14);
+UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 15);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 0);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 1);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 2);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 3);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 4);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 5);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 6);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 7);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 8);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 9);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 10);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 11);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 12);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 13);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 14);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 15);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 0);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 1);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 2);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 3);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 4);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 5);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 6);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 7);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 8);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 9);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 10);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 11);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 12);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 13);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 14);
+UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 15);
+
+static struct attribute *ufs_sysfs_power_descriptor[] = {
+ &dev_attr_active_icc_levels_vcc0.attr,
+ &dev_attr_active_icc_levels_vcc1.attr,
+ &dev_attr_active_icc_levels_vcc2.attr,
+ &dev_attr_active_icc_levels_vcc3.attr,
+ &dev_attr_active_icc_levels_vcc4.attr,
+ &dev_attr_active_icc_levels_vcc5.attr,
+ &dev_attr_active_icc_levels_vcc6.attr,
+ &dev_attr_active_icc_levels_vcc7.attr,
+ &dev_attr_active_icc_levels_vcc8.attr,
+ &dev_attr_active_icc_levels_vcc9.attr,
+ &dev_attr_active_icc_levels_vcc10.attr,
+ &dev_attr_active_icc_levels_vcc11.attr,
+ &dev_attr_active_icc_levels_vcc12.attr,
+ &dev_attr_active_icc_levels_vcc13.attr,
+ &dev_attr_active_icc_levels_vcc14.attr,
+ &dev_attr_active_icc_levels_vcc15.attr,
+ &dev_attr_active_icc_levels_vccq0.attr,
+ &dev_attr_active_icc_levels_vccq1.attr,
+ &dev_attr_active_icc_levels_vccq2.attr,
+ &dev_attr_active_icc_levels_vccq3.attr,
+ &dev_attr_active_icc_levels_vccq4.attr,
+ &dev_attr_active_icc_levels_vccq5.attr,
+ &dev_attr_active_icc_levels_vccq6.attr,
+ &dev_attr_active_icc_levels_vccq7.attr,
+ &dev_attr_active_icc_levels_vccq8.attr,
+ &dev_attr_active_icc_levels_vccq9.attr,
+ &dev_attr_active_icc_levels_vccq10.attr,
+ &dev_attr_active_icc_levels_vccq11.attr,
+ &dev_attr_active_icc_levels_vccq12.attr,
+ &dev_attr_active_icc_levels_vccq13.attr,
+ &dev_attr_active_icc_levels_vccq14.attr,
+ &dev_attr_active_icc_levels_vccq15.attr,
+ &dev_attr_active_icc_levels_vccq20.attr,
+ &dev_attr_active_icc_levels_vccq21.attr,
+ &dev_attr_active_icc_levels_vccq22.attr,
+ &dev_attr_active_icc_levels_vccq23.attr,
+ &dev_attr_active_icc_levels_vccq24.attr,
+ &dev_attr_active_icc_levels_vccq25.attr,
+ &dev_attr_active_icc_levels_vccq26.attr,
+ &dev_attr_active_icc_levels_vccq27.attr,
+ &dev_attr_active_icc_levels_vccq28.attr,
+ &dev_attr_active_icc_levels_vccq29.attr,
+ &dev_attr_active_icc_levels_vccq210.attr,
+ &dev_attr_active_icc_levels_vccq211.attr,
+ &dev_attr_active_icc_levels_vccq212.attr,
+ &dev_attr_active_icc_levels_vccq213.attr,
+ &dev_attr_active_icc_levels_vccq214.attr,
+ &dev_attr_active_icc_levels_vccq215.attr,
+ NULL,
+};
+
+static const struct attribute_group ufs_sysfs_power_descriptor_group = {
+ .name = "power_descriptor",
+ .attrs = ufs_sysfs_power_descriptor,
+};
+
+#define UFS_STRING_DESCRIPTOR(_name, _pname) \
+static ssize_t _name##_show(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ u8 index; \
+ struct ufs_hba *hba = dev_get_drvdata(dev); \
+ int ret; \
+ int desc_len = QUERY_DESC_MAX_SIZE; \
+ u8 *desc_buf; \
+ desc_buf = kzalloc(QUERY_DESC_MAX_SIZE, GFP_ATOMIC); \
+ if (!desc_buf) \
+ return -ENOMEM; \
+ ret = ufshcd_query_descriptor_retry(hba, \
+ UPIU_QUERY_OPCODE_READ_DESC, QUERY_DESC_IDN_DEVICE, \
+ 0, 0, desc_buf, &desc_len); \
+ if (ret) { \
+ ret = -EINVAL; \
+ goto out; \
+ } \
+ index = desc_buf[DEVICE_DESC_PARAM##_pname]; \
+ memset(desc_buf, 0, QUERY_DESC_MAX_SIZE); \
+ if (ufshcd_read_string_desc(hba, index, desc_buf, \
+ QUERY_DESC_MAX_SIZE, true)) { \
+ ret = -EINVAL; \
+ goto out; \
+ } \
+ ret = snprintf(buf, PAGE_SIZE, "%s\n", \
+ desc_buf + QUERY_DESC_HDR_SIZE); \
+out: \
+ kfree(desc_buf); \
+ return ret; \
+} \
+static DEVICE_ATTR_RO(_name)
+
+UFS_STRING_DESCRIPTOR(manufacturer_name, _MANF_NAME);
+UFS_STRING_DESCRIPTOR(product_name, _PRDCT_NAME);
+UFS_STRING_DESCRIPTOR(oem_id, _OEM_ID);
+UFS_STRING_DESCRIPTOR(serial_number, _SN);
+UFS_STRING_DESCRIPTOR(product_revision, _PRDCT_REV);
+
+static struct attribute *ufs_sysfs_string_descriptors[] = {
+ &dev_attr_manufacturer_name.attr,
+ &dev_attr_product_name.attr,
+ &dev_attr_oem_id.attr,
+ &dev_attr_serial_number.attr,
+ &dev_attr_product_revision.attr,
+ NULL,
+};
+
+static const struct attribute_group ufs_sysfs_string_descriptors_group = {
+ .name = "string_descriptors",
+ .attrs = ufs_sysfs_string_descriptors,
+};
+
+#define UFS_FLAG(_name, _uname) \
+static ssize_t _name##_show(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ bool flag; \
+ struct ufs_hba *hba = dev_get_drvdata(dev); \
+ if (ufshcd_query_flag(hba, UPIU_QUERY_OPCODE_READ_FLAG, \
+ QUERY_FLAG_IDN##_uname, &flag)) \
+ return -EINVAL; \
+ return sprintf(buf, "%s\n", flag ? "true" : "false"); \
+} \
+static DEVICE_ATTR_RO(_name)
+
+UFS_FLAG(device_init, _FDEVICEINIT);
+UFS_FLAG(permanent_wpe, _PERMANENT_WPE);
+UFS_FLAG(power_on_wpe, _PWR_ON_WPE);
+UFS_FLAG(bkops_enable, _BKOPS_EN);
+UFS_FLAG(life_span_mode_enable, _LIFE_SPAN_MODE_ENABLE);
+UFS_FLAG(phy_resource_removal, _FPHYRESOURCEREMOVAL);
+UFS_FLAG(busy_rtc, _BUSY_RTC);
+UFS_FLAG(disable_fw_update, _PERMANENTLY_DISABLE_FW_UPDATE);
+
+static struct attribute *ufs_sysfs_device_flags[] = {
+ &dev_attr_device_init.attr,
+ &dev_attr_permanent_wpe.attr,
+ &dev_attr_power_on_wpe.attr,
+ &dev_attr_bkops_enable.attr,
+ &dev_attr_life_span_mode_enable.attr,
+ &dev_attr_phy_resource_removal.attr,
+ &dev_attr_busy_rtc.attr,
+ &dev_attr_disable_fw_update.attr,
+ NULL,
+};
+
+static const struct attribute_group ufs_sysfs_flags_group = {
+ .name = "flags",
+ .attrs = ufs_sysfs_device_flags,
+};
+
+#define UFS_ATTRIBUTE(_name, _uname) \
+static ssize_t _name##_show(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct ufs_hba *hba = dev_get_drvdata(dev); \
+ u32 value; \
+ if (ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR, \
+ QUERY_ATTR_IDN##_uname, 0, 0, &value)) \
+ return -EINVAL; \
+ return sprintf(buf, "0x%08X\n", value); \
+} \
+static DEVICE_ATTR_RO(_name)
+
+UFS_ATTRIBUTE(boot_lun_enabled, _BOOT_LU_EN);
+UFS_ATTRIBUTE(current_power_mode, _POWER_MODE);
+UFS_ATTRIBUTE(active_icc_level, _ACTIVE_ICC_LVL);
+UFS_ATTRIBUTE(ooo_data_enabled, _OOO_DATA_EN);
+UFS_ATTRIBUTE(bkops_status, _BKOPS_STATUS);
+UFS_ATTRIBUTE(purge_status, _PURGE_STATUS);
+UFS_ATTRIBUTE(max_data_in_size, _MAX_DATA_IN);
+UFS_ATTRIBUTE(max_data_out_size, _MAX_DATA_OUT);
+UFS_ATTRIBUTE(reference_clock_frequency, _REF_CLK_FREQ);
+UFS_ATTRIBUTE(configuration_descriptor_lock, _CONF_DESC_LOCK);
+UFS_ATTRIBUTE(max_number_of_rtt, _MAX_NUM_OF_RTT);
+UFS_ATTRIBUTE(exception_event_control, _EE_CONTROL);
+UFS_ATTRIBUTE(exception_event_status, _EE_STATUS);
+UFS_ATTRIBUTE(ffu_status, _FFU_STATUS);
+UFS_ATTRIBUTE(psa_state, _PSA_STATE);
+UFS_ATTRIBUTE(psa_data_size, _PSA_DATA_SIZE);
+
+static struct attribute *ufs_sysfs_attributes[] = {
+ &dev_attr_boot_lun_enabled.attr,
+ &dev_attr_current_power_mode.attr,
+ &dev_attr_active_icc_level.attr,
+ &dev_attr_ooo_data_enabled.attr,
+ &dev_attr_bkops_status.attr,
+ &dev_attr_purge_status.attr,
+ &dev_attr_max_data_in_size.attr,
+ &dev_attr_max_data_out_size.attr,
+ &dev_attr_reference_clock_frequency.attr,
+ &dev_attr_configuration_descriptor_lock.attr,
+ &dev_attr_max_number_of_rtt.attr,
+ &dev_attr_exception_event_control.attr,
+ &dev_attr_exception_event_status.attr,
+ &dev_attr_ffu_status.attr,
+ &dev_attr_psa_state.attr,
+ &dev_attr_psa_data_size.attr,
+ NULL,
+};
+
+static const struct attribute_group ufs_sysfs_attributes_group = {
+ .name = "attributes",
+ .attrs = ufs_sysfs_attributes,
+};
+
+static const struct attribute_group *ufs_sysfs_groups[] = {
+ &ufs_sysfs_default_group,
+ &ufs_sysfs_device_descriptor_group,
+ &ufs_sysfs_interconnect_descriptor_group,
+ &ufs_sysfs_geometry_descriptor_group,
+ &ufs_sysfs_health_descriptor_group,
+ &ufs_sysfs_power_descriptor_group,
+ &ufs_sysfs_string_descriptors_group,
+ &ufs_sysfs_flags_group,
+ &ufs_sysfs_attributes_group,
+ NULL,
+};
+
+#define UFS_LUN_DESC_PARAM(_pname, _puname, _duname, _size) \
+static ssize_t _pname##_show(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct scsi_device *sdev = to_scsi_device(dev); \
+ struct ufs_hba *hba = shost_priv(sdev->host); \
+ u8 lun = ufshcd_scsi_to_upiu_lun(sdev->lun); \
+ if (!ufs_is_valid_unit_desc_lun(lun)) \
+ return -EINVAL; \
+ return ufs_sysfs_read_desc_param(hba, QUERY_DESC_IDN_##_duname, \
+ lun, _duname##_DESC_PARAM##_puname, buf, _size); \
+} \
+static DEVICE_ATTR_RO(_pname)
+
+#define UFS_UNIT_DESC_PARAM(_name, _uname, _size) \
+ UFS_LUN_DESC_PARAM(_name, _uname, UNIT, _size)
+
+UFS_UNIT_DESC_PARAM(boot_lun_id, _BOOT_LUN_ID, 1);
+UFS_UNIT_DESC_PARAM(lun_write_protect, _LU_WR_PROTECT, 1);
+UFS_UNIT_DESC_PARAM(lun_queue_depth, _LU_Q_DEPTH, 1);
+UFS_UNIT_DESC_PARAM(psa_sensitive, _PSA_SENSITIVE, 1);
+UFS_UNIT_DESC_PARAM(lun_memory_type, _MEM_TYPE, 1);
+UFS_UNIT_DESC_PARAM(data_reliability, _DATA_RELIABILITY, 1);
+UFS_UNIT_DESC_PARAM(logical_block_size, _LOGICAL_BLK_SIZE, 1);
+UFS_UNIT_DESC_PARAM(logical_block_count, _LOGICAL_BLK_COUNT, 8);
+UFS_UNIT_DESC_PARAM(erase_block_size, _ERASE_BLK_SIZE, 4);
+UFS_UNIT_DESC_PARAM(provisioning_type, _PROVISIONING_TYPE, 1);
+UFS_UNIT_DESC_PARAM(physical_memory_resourse_count, _PHY_MEM_RSRC_CNT, 8);
+UFS_UNIT_DESC_PARAM(context_capabilities, _CTX_CAPABILITIES, 2);
+UFS_UNIT_DESC_PARAM(large_unit_granularity, _LARGE_UNIT_SIZE_M1, 1);
+
+static struct attribute *ufs_sysfs_unit_descriptor[] = {
+ &dev_attr_boot_lun_id.attr,
+ &dev_attr_lun_write_protect.attr,
+ &dev_attr_lun_queue_depth.attr,
+ &dev_attr_psa_sensitive.attr,
+ &dev_attr_lun_memory_type.attr,
+ &dev_attr_data_reliability.attr,
+ &dev_attr_logical_block_size.attr,
+ &dev_attr_logical_block_count.attr,
+ &dev_attr_erase_block_size.attr,
+ &dev_attr_provisioning_type.attr,
+ &dev_attr_physical_memory_resourse_count.attr,
+ &dev_attr_context_capabilities.attr,
+ &dev_attr_large_unit_granularity.attr,
+ NULL,
+};
+
+const struct attribute_group ufs_sysfs_unit_descriptor_group = {
+ .name = "unit_descriptor",
+ .attrs = ufs_sysfs_unit_descriptor,
+};
+
+static ssize_t dyn_cap_needed_attribute_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ u32 value;
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct ufs_hba *hba = shost_priv(sdev->host);
+ u8 lun = ufshcd_scsi_to_upiu_lun(sdev->lun);
+
+ if (ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR,
+ QUERY_ATTR_IDN_DYN_CAP_NEEDED, lun, 0, &value))
+ return -EINVAL;
+ return sprintf(buf, "0x%08X\n", value);
+}
+static DEVICE_ATTR_RO(dyn_cap_needed_attribute);
+
+static struct attribute *ufs_sysfs_lun_attributes[] = {
+ &dev_attr_dyn_cap_needed_attribute.attr,
+ NULL,
+};
+
+const struct attribute_group ufs_sysfs_lun_attributes_group = {
+ .attrs = ufs_sysfs_lun_attributes,
+};
+
+void ufs_sysfs_add_nodes(struct device *dev)
+{
+ int ret;
+
+ ret = sysfs_create_groups(&dev->kobj, ufs_sysfs_groups);
+ if (ret)
+ dev_err(dev,
+ "%s: sysfs groups creation failed (err = %d)\n",
+ __func__, ret);
+}
+
+void ufs_sysfs_remove_nodes(struct device *dev)
+{
+ sysfs_remove_groups(&dev->kobj, ufs_sysfs_groups);
+}
diff --git a/drivers/scsi/ufs/ufs-sysfs.h b/drivers/scsi/ufs/ufs-sysfs.h
new file mode 100644
index 000000000000..e5621e59a432
--- /dev/null
+++ b/drivers/scsi/ufs/ufs-sysfs.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0
+ * Copyright (C) 2018 Western Digital Corporation
+ */
+
+#ifndef __UFS_SYSFS_H__
+#define __UFS_SYSFS_H__
+
+#include <linux/sysfs.h>
+
+#include "ufshcd.h"
+
+void ufs_sysfs_add_nodes(struct device *dev);
+void ufs_sysfs_remove_nodes(struct device *dev);
+
+extern const struct attribute_group ufs_sysfs_unit_descriptor_group;
+extern const struct attribute_group ufs_sysfs_lun_attributes_group;
+#endif
diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 54deeb754db5..14e5bf7af0bb 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -130,17 +130,44 @@ enum {
/* Flag idn for Query Requests*/
enum flag_idn {
- QUERY_FLAG_IDN_FDEVICEINIT = 0x01,
- QUERY_FLAG_IDN_PWR_ON_WPE = 0x03,
- QUERY_FLAG_IDN_BKOPS_EN = 0x04,
+ QUERY_FLAG_IDN_FDEVICEINIT = 0x01,
+ QUERY_FLAG_IDN_PERMANENT_WPE = 0x02,
+ QUERY_FLAG_IDN_PWR_ON_WPE = 0x03,
+ QUERY_FLAG_IDN_BKOPS_EN = 0x04,
+ QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE = 0x05,
+ QUERY_FLAG_IDN_PURGE_ENABLE = 0x06,
+ QUERY_FLAG_IDN_RESERVED2 = 0x07,
+ QUERY_FLAG_IDN_FPHYRESOURCEREMOVAL = 0x08,
+ QUERY_FLAG_IDN_BUSY_RTC = 0x09,
+ QUERY_FLAG_IDN_RESERVED3 = 0x0A,
+ QUERY_FLAG_IDN_PERMANENTLY_DISABLE_FW_UPDATE = 0x0B,
};
/* Attribute idn for Query requests */
enum attr_idn {
- QUERY_ATTR_IDN_ACTIVE_ICC_LVL = 0x03,
- QUERY_ATTR_IDN_BKOPS_STATUS = 0x05,
- QUERY_ATTR_IDN_EE_CONTROL = 0x0D,
- QUERY_ATTR_IDN_EE_STATUS = 0x0E,
+ QUERY_ATTR_IDN_BOOT_LU_EN = 0x00,
+ QUERY_ATTR_IDN_RESERVED = 0x01,
+ QUERY_ATTR_IDN_POWER_MODE = 0x02,
+ QUERY_ATTR_IDN_ACTIVE_ICC_LVL = 0x03,
+ QUERY_ATTR_IDN_OOO_DATA_EN = 0x04,
+ QUERY_ATTR_IDN_BKOPS_STATUS = 0x05,
+ QUERY_ATTR_IDN_PURGE_STATUS = 0x06,
+ QUERY_ATTR_IDN_MAX_DATA_IN = 0x07,
+ QUERY_ATTR_IDN_MAX_DATA_OUT = 0x08,
+ QUERY_ATTR_IDN_DYN_CAP_NEEDED = 0x09,
+ QUERY_ATTR_IDN_REF_CLK_FREQ = 0x0A,
+ QUERY_ATTR_IDN_CONF_DESC_LOCK = 0x0B,
+ QUERY_ATTR_IDN_MAX_NUM_OF_RTT = 0x0C,
+ QUERY_ATTR_IDN_EE_CONTROL = 0x0D,
+ QUERY_ATTR_IDN_EE_STATUS = 0x0E,
+ QUERY_ATTR_IDN_SECONDS_PASSED = 0x0F,
+ QUERY_ATTR_IDN_CNTX_CONF = 0x10,
+ QUERY_ATTR_IDN_CORR_PRG_BLK_NUM = 0x11,
+ QUERY_ATTR_IDN_RESERVED2 = 0x12,
+ QUERY_ATTR_IDN_RESERVED3 = 0x13,
+ QUERY_ATTR_IDN_FFU_STATUS = 0x14,
+ QUERY_ATTR_IDN_PSA_STATE = 0x15,
+ QUERY_ATTR_IDN_PSA_DATA_SIZE = 0x16,
};
/* Descriptor idn for Query requests */
@@ -154,6 +181,7 @@ enum desc_idn {
QUERY_DESC_IDN_RFU_1 = 0x6,
QUERY_DESC_IDN_GEOMETRY = 0x7,
QUERY_DESC_IDN_POWER = 0x8,
+ QUERY_DESC_IDN_HEALTH = 0x9,
QUERY_DESC_IDN_MAX,
};
@@ -169,6 +197,7 @@ enum ufs_desc_def_size {
QUERY_DESC_INTERCONNECT_DEF_SIZE = 0x06,
QUERY_DESC_GEOMETRY_DEF_SIZE = 0x44,
QUERY_DESC_POWER_DEF_SIZE = 0x62,
+ QUERY_DESC_HEALTH_DEF_SIZE = 0x25,
};
/* Unit descriptor parameters offsets in bytes*/
@@ -180,6 +209,7 @@ enum unit_desc_param {
UNIT_DESC_PARAM_BOOT_LUN_ID = 0x4,
UNIT_DESC_PARAM_LU_WR_PROTECT = 0x5,
UNIT_DESC_PARAM_LU_Q_DEPTH = 0x6,
+ UNIT_DESC_PARAM_PSA_SENSITIVE = 0x7,
UNIT_DESC_PARAM_MEM_TYPE = 0x8,
UNIT_DESC_PARAM_DATA_RELIABILITY = 0x9,
UNIT_DESC_PARAM_LOGICAL_BLK_SIZE = 0xA,
@@ -220,6 +250,67 @@ enum device_desc_param {
DEVICE_DESC_PARAM_UD_LEN = 0x1B,
DEVICE_DESC_PARAM_RTT_CAP = 0x1C,
DEVICE_DESC_PARAM_FRQ_RTC = 0x1D,
+ DEVICE_DESC_PARAM_UFS_FEAT = 0x1F,
+ DEVICE_DESC_PARAM_FFU_TMT = 0x20,
+ DEVICE_DESC_PARAM_Q_DPTH = 0x21,
+ DEVICE_DESC_PARAM_DEV_VER = 0x22,
+ DEVICE_DESC_PARAM_NUM_SEC_WPA = 0x24,
+ DEVICE_DESC_PARAM_PSA_MAX_DATA = 0x25,
+ DEVICE_DESC_PARAM_PSA_TMT = 0x29,
+ DEVICE_DESC_PARAM_PRDCT_REV = 0x2A,
+};
+
+/* Interconnect descriptor parameters offsets in bytes*/
+enum interconnect_desc_param {
+ INTERCONNECT_DESC_PARAM_LEN = 0x0,
+ INTERCONNECT_DESC_PARAM_TYPE = 0x1,
+ INTERCONNECT_DESC_PARAM_UNIPRO_VER = 0x2,
+ INTERCONNECT_DESC_PARAM_MPHY_VER = 0x4,
+};
+
+/* Geometry descriptor parameters offsets in bytes*/
+enum geometry_desc_param {
+ GEOMETRY_DESC_PARAM_LEN = 0x0,
+ GEOMETRY_DESC_PARAM_TYPE = 0x1,
+ GEOMETRY_DESC_PARAM_DEV_CAP = 0x4,
+ GEOMETRY_DESC_PARAM_MAX_NUM_LUN = 0xC,
+ GEOMETRY_DESC_PARAM_SEG_SIZE = 0xD,
+ GEOMETRY_DESC_PARAM_ALLOC_UNIT_SIZE = 0x11,
+ GEOMETRY_DESC_PARAM_MIN_BLK_SIZE = 0x12,
+ GEOMETRY_DESC_PARAM_OPT_RD_BLK_SIZE = 0x13,
+ GEOMETRY_DESC_PARAM_OPT_WR_BLK_SIZE = 0x14,
+ GEOMETRY_DESC_PARAM_MAX_IN_BUF_SIZE = 0x15,
+ GEOMETRY_DESC_PARAM_MAX_OUT_BUF_SIZE = 0x16,
+ GEOMETRY_DESC_PARAM_RPMB_RW_SIZE = 0x17,
+ GEOMETRY_DESC_PARAM_DYN_CAP_RSRC_PLC = 0x18,
+ GEOMETRY_DESC_PARAM_DATA_ORDER = 0x19,
+ GEOMETRY_DESC_PARAM_MAX_NUM_CTX = 0x1A,
+ GEOMETRY_DESC_PARAM_TAG_UNIT_SIZE = 0x1B,
+ GEOMETRY_DESC_PARAM_TAG_RSRC_SIZE = 0x1C,
+ GEOMETRY_DESC_PARAM_SEC_RM_TYPES = 0x1D,
+ GEOMETRY_DESC_PARAM_MEM_TYPES = 0x1E,
+ GEOMETRY_DESC_PARAM_SCM_MAX_NUM_UNITS = 0x20,
+ GEOMETRY_DESC_PARAM_SCM_CAP_ADJ_FCTR = 0x24,
+ GEOMETRY_DESC_PARAM_NPM_MAX_NUM_UNITS = 0x26,
+ GEOMETRY_DESC_PARAM_NPM_CAP_ADJ_FCTR = 0x2A,
+ GEOMETRY_DESC_PARAM_ENM1_MAX_NUM_UNITS = 0x2C,
+ GEOMETRY_DESC_PARAM_ENM1_CAP_ADJ_FCTR = 0x30,
+ GEOMETRY_DESC_PARAM_ENM2_MAX_NUM_UNITS = 0x32,
+ GEOMETRY_DESC_PARAM_ENM2_CAP_ADJ_FCTR = 0x36,
+ GEOMETRY_DESC_PARAM_ENM3_MAX_NUM_UNITS = 0x38,
+ GEOMETRY_DESC_PARAM_ENM3_CAP_ADJ_FCTR = 0x3C,
+ GEOMETRY_DESC_PARAM_ENM4_MAX_NUM_UNITS = 0x3E,
+ GEOMETRY_DESC_PARAM_ENM4_CAP_ADJ_FCTR = 0x42,
+ GEOMETRY_DESC_PARAM_OPT_LOG_BLK_SIZE = 0x44,
+};
+
+/* Health descriptor parameters offsets in bytes*/
+enum health_desc_param {
+ HEALTH_DESC_PARAM_LEN = 0x0,
+ HEALTH_DESC_PARAM_TYPE = 0x1,
+ HEALTH_DESC_PARAM_EOL_INFO = 0x2,
+ HEALTH_DESC_PARAM_LIFE_TIME_EST_A = 0x3,
+ HEALTH_DESC_PARAM_LIFE_TIME_EST_B = 0x4,
};
/*
@@ -529,4 +620,14 @@ struct ufs_dev_desc {
char model[MAX_MODEL_LEN + 1];
};
+/**
+ * ufs_is_valid_unit_desc_lun - checks if the given LUN has a unit descriptor
+ * @lun: LU number to check
+ * @return: true if the lun has a matching unit descriptor, false otherwise
+ */
+static inline bool ufs_is_valid_unit_desc_lun(u8 lun)
+{
+ return lun == UFS_UPIU_RPMB_WLUN || (lun < UFS_UPIU_MAX_GENERAL_LUN);
+}
+
#endif /* End of Header */
diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c
index 925b0ec7ec54..ffe6f82182ba 100644
--- a/drivers/scsi/ufs/ufshcd-pci.c
+++ b/drivers/scsi/ufs/ufshcd-pci.c
@@ -75,8 +75,7 @@ static struct ufs_hba_variant_ops ufs_intel_cnl_hba_vops = {
#ifdef CONFIG_PM_SLEEP
/**
* ufshcd_pci_suspend - suspend power management function
- * @pdev: pointer to PCI device handle
- * @state: power state
+ * @dev: pointer to PCI device handle
*
* Returns 0 if successful
* Returns non-zero otherwise
@@ -88,7 +87,7 @@ static int ufshcd_pci_suspend(struct device *dev)
/**
* ufshcd_pci_resume - resume power management function
- * @pdev: pointer to PCI device handle
+ * @dev: pointer to PCI device handle
*
* Returns 0 if successful
* Returns non-zero otherwise
@@ -126,7 +125,7 @@ static void ufshcd_pci_shutdown(struct pci_dev *pdev)
/**
* ufshcd_pci_remove - de-allocate PCI/SCSI host and host memory space
* data structure memory
- * @pdev - pointer to PCI handle
+ * @pdev: pointer to PCI handle
*/
static void ufshcd_pci_remove(struct pci_dev *pdev)
{
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index c7da2c185990..00e79057f870 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -41,9 +41,11 @@
#include <linux/devfreq.h>
#include <linux/nls.h>
#include <linux/of.h>
+#include <linux/bitfield.h>
#include "ufshcd.h"
#include "ufs_quirks.h"
#include "unipro.h"
+#include "ufs-sysfs.h"
#define CREATE_TRACE_POINTS
#include <trace/events/ufs.h>
@@ -150,7 +152,7 @@ enum {
#define ufshcd_is_ufs_dev_poweroff(h) \
((h)->curr_dev_pwr_mode == UFS_POWERDOWN_PWR_MODE)
-static struct ufs_pm_lvl_states ufs_pm_lvl_states[] = {
+struct ufs_pm_lvl_states ufs_pm_lvl_states[] = {
{UFS_ACTIVE_PWR_MODE, UIC_LINK_ACTIVE_STATE},
{UFS_ACTIVE_PWR_MODE, UIC_LINK_HIBERN8_STATE},
{UFS_SLEEP_PWR_MODE, UIC_LINK_ACTIVE_STATE},
@@ -274,6 +276,35 @@ static inline void ufshcd_remove_non_printable(char *val)
*val = ' ';
}
+static void ufshcd_add_cmd_upiu_trace(struct ufs_hba *hba, unsigned int tag,
+ const char *str)
+{
+ struct utp_upiu_req *rq = hba->lrb[tag].ucd_req_ptr;
+
+ trace_ufshcd_upiu(dev_name(hba->dev), str, &rq->header, &rq->sc.cdb);
+}
+
+static void ufshcd_add_query_upiu_trace(struct ufs_hba *hba, unsigned int tag,
+ const char *str)
+{
+ struct utp_upiu_req *rq = hba->lrb[tag].ucd_req_ptr;
+
+ trace_ufshcd_upiu(dev_name(hba->dev), str, &rq->header, &rq->qr);
+}
+
+static void ufshcd_add_tm_upiu_trace(struct ufs_hba *hba, unsigned int tag,
+ const char *str)
+{
+ struct utp_task_req_desc *descp;
+ struct utp_upiu_task_req *task_req;
+ int off = (int)tag - hba->nutrs;
+
+ descp = &hba->utmrdl_base_addr[off];
+ task_req = (struct utp_upiu_task_req *)descp->task_req_upiu;
+ trace_ufshcd_upiu(dev_name(hba->dev), str, &task_req->header,
+ &task_req->input_param1);
+}
+
static void ufshcd_add_command_trace(struct ufs_hba *hba,
unsigned int tag, const char *str)
{
@@ -283,6 +314,9 @@ static void ufshcd_add_command_trace(struct ufs_hba *hba,
struct ufshcd_lrb *lrbp;
int transfer_len = -1;
+ /* trace UPIU also */
+ ufshcd_add_cmd_upiu_trace(hba, tag, str);
+
if (!trace_ufshcd_command_enabled())
return;
@@ -523,7 +557,7 @@ int ufshcd_wait_for_register(struct ufs_hba *hba, u32 reg, u32 mask,
/**
* ufshcd_get_intr_mask - Get the interrupt bit mask
- * @hba - Pointer to adapter instance
+ * @hba: Pointer to adapter instance
*
* Returns interrupt bit mask per version
*/
@@ -550,7 +584,7 @@ static inline u32 ufshcd_get_intr_mask(struct ufs_hba *hba)
/**
* ufshcd_get_ufs_version - Get the UFS version supported by the HBA
- * @hba - Pointer to adapter instance
+ * @hba: Pointer to adapter instance
*
* Returns UFSHCI version supported by the controller
*/
@@ -577,7 +611,7 @@ static inline bool ufshcd_is_device_present(struct ufs_hba *hba)
/**
* ufshcd_get_tr_ocs - Get the UTRD Overall Command Status
- * @lrb: pointer to local command reference block
+ * @lrbp: pointer to local command reference block
*
* This function is used to get the OCS field from UTRD
* Returns the OCS field in the UTRD
@@ -813,28 +847,6 @@ static inline bool ufshcd_is_hba_active(struct ufs_hba *hba)
? false : true;
}
-static const char *ufschd_uic_link_state_to_string(
- enum uic_link_state state)
-{
- switch (state) {
- case UIC_LINK_OFF_STATE: return "OFF";
- case UIC_LINK_ACTIVE_STATE: return "ACTIVE";
- case UIC_LINK_HIBERN8_STATE: return "HIBERN8";
- default: return "UNKNOWN";
- }
-}
-
-static const char *ufschd_ufs_dev_pwr_mode_to_string(
- enum ufs_dev_pwr_mode state)
-{
- switch (state) {
- case UFS_ACTIVE_PWR_MODE: return "ACTIVE";
- case UFS_SLEEP_PWR_MODE: return "SLEEP";
- case UFS_POWERDOWN_PWR_MODE: return "POWERDOWN";
- default: return "UNKNOWN";
- }
-}
-
u32 ufshcd_get_local_unipro_ver(struct ufs_hba *hba)
{
/* HCI version 1.0 and 1.1 supports UniPro 1.41 */
@@ -1759,7 +1771,7 @@ void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
/**
* ufshcd_copy_sense_data - Copy sense data in case of check condition
- * @lrb - pointer to local reference block
+ * @lrbp: pointer to local reference block
*/
static inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp)
{
@@ -1781,7 +1793,7 @@ static inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp)
* ufshcd_copy_query_response() - Copy the Query Response and the data
* descriptor
* @hba: per adapter instance
- * @lrb - pointer to local reference block
+ * @lrbp: pointer to local reference block
*/
static
int ufshcd_copy_query_response(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
@@ -1882,7 +1894,7 @@ ufshcd_dispatch_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd)
/**
* ufshcd_wait_for_uic_cmd - Wait complectioin of UIC command
* @hba: per adapter instance
- * @uic_command: UIC command
+ * @uic_cmd: UIC command
*
* Must be called with mutex held.
* Returns 0 only if success.
@@ -1965,7 +1977,8 @@ ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd)
/**
* ufshcd_map_sg - Map scatter-gather list to prdt
- * @lrbp - pointer to local reference block
+ * @hba: per adapter instance
+ * @lrbp: pointer to local reference block
*
* Returns 0 in case of success, non-zero value in case of failure
*/
@@ -2101,8 +2114,8 @@ static void ufshcd_prepare_req_desc_hdr(struct ufshcd_lrb *lrbp,
/**
* ufshcd_prepare_utp_scsi_cmd_upiu() - fills the utp_transfer_req_desc,
* for scsi commands
- * @lrbp - local reference block pointer
- * @upiu_flags - flags
+ * @lrbp: local reference block pointer
+ * @upiu_flags: flags
*/
static
void ufshcd_prepare_utp_scsi_cmd_upiu(struct ufshcd_lrb *lrbp, u32 upiu_flags)
@@ -2190,8 +2203,8 @@ static inline void ufshcd_prepare_utp_nop_upiu(struct ufshcd_lrb *lrbp)
/**
* ufshcd_comp_devman_upiu - UFS Protocol Information Unit(UPIU)
* for Device Management Purposes
- * @hba - per adapter instance
- * @lrb - pointer to local reference block
+ * @hba: per adapter instance
+ * @lrbp: pointer to local reference block
*/
static int ufshcd_comp_devman_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
{
@@ -2218,8 +2231,8 @@ static int ufshcd_comp_devman_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
/**
* ufshcd_comp_scsi_upiu - UFS Protocol Information Unit(UPIU)
* for SCSI Purposes
- * @hba - per adapter instance
- * @lrb - pointer to local reference block
+ * @hba: per adapter instance
+ * @lrbp: pointer to local reference block
*/
static int ufshcd_comp_scsi_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
{
@@ -2243,24 +2256,9 @@ static int ufshcd_comp_scsi_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
return ret;
}
-/*
- * ufshcd_scsi_to_upiu_lun - maps scsi LUN to UPIU LUN
- * @scsi_lun: scsi LUN id
- *
- * Returns UPIU LUN id
- */
-static inline u8 ufshcd_scsi_to_upiu_lun(unsigned int scsi_lun)
-{
- if (scsi_is_wlun(scsi_lun))
- return (scsi_lun & UFS_UPIU_MAX_UNIT_NUM_ID)
- | UFS_UPIU_WLUN_ID;
- else
- return scsi_lun & UFS_UPIU_MAX_UNIT_NUM_ID;
-}
-
/**
* ufshcd_upiu_wlun_to_scsi_wlun - maps UPIU W-LUN id to SCSI W-LUN ID
- * @scsi_lun: UPIU W-LUN id
+ * @upiu_wlun_id: UPIU W-LUN id
*
* Returns SCSI W-LUN id
*/
@@ -2271,8 +2269,8 @@ static inline u16 ufshcd_upiu_wlun_to_scsi_wlun(u8 upiu_wlun_id)
/**
* ufshcd_queuecommand - main entry point for SCSI requests
+ * @host: SCSI host pointer
* @cmd: command from SCSI Midlayer
- * @done: call back function
*
* Returns 0 for success, non-zero in case of failure
*/
@@ -2513,7 +2511,7 @@ static int ufshcd_wait_for_dev_cmd(struct ufs_hba *hba,
/**
* ufshcd_get_dev_cmd_tag - Get device management command tag
* @hba: per-adapter instance
- * @tag: pointer to variable with available slot value
+ * @tag_out: pointer to variable with available slot value
*
* Get a free slot and lock it until device management command
* completes.
@@ -2550,9 +2548,9 @@ static inline void ufshcd_put_dev_cmd_tag(struct ufs_hba *hba, int tag)
/**
* ufshcd_exec_dev_cmd - API for sending device management requests
- * @hba - UFS hba
- * @cmd_type - specifies the type (NOP, Query...)
- * @timeout - time in seconds
+ * @hba: UFS hba
+ * @cmd_type: specifies the type (NOP, Query...)
+ * @timeout: time in seconds
*
* NOTE: Since there is only one available tag for device management commands,
* it is expected you hold the hba->dev_cmd.lock mutex.
@@ -2584,6 +2582,7 @@ static int ufshcd_exec_dev_cmd(struct ufs_hba *hba,
hba->dev_cmd.complete = &wait;
+ ufshcd_add_query_upiu_trace(hba, tag, "query_send");
/* Make sure descriptors are ready before ringing the doorbell */
wmb();
spin_lock_irqsave(hba->host->host_lock, flags);
@@ -2593,6 +2592,9 @@ static int ufshcd_exec_dev_cmd(struct ufs_hba *hba,
err = ufshcd_wait_for_dev_cmd(hba, lrbp, timeout);
+ ufshcd_add_query_upiu_trace(hba, tag,
+ err ? "query_complete_err" : "query_complete");
+
out_put_tag:
ufshcd_put_dev_cmd_tag(hba, tag);
wake_up(&hba->dev_cmd.tag_wq);
@@ -2649,10 +2651,10 @@ static int ufshcd_query_flag_retry(struct ufs_hba *hba,
/**
* ufshcd_query_flag() - API function for sending flag query requests
- * hba: per-adapter instance
- * query_opcode: flag query to perform
- * idn: flag idn to access
- * flag_res: the flag value after the query request completes
+ * @hba: per-adapter instance
+ * @opcode: flag query to perform
+ * @idn: flag idn to access
+ * @flag_res: the flag value after the query request completes
*
* Returns 0 for success, non-zero in case of failure
*/
@@ -2716,17 +2718,17 @@ out_unlock:
/**
* ufshcd_query_attr - API function for sending attribute requests
- * hba: per-adapter instance
- * opcode: attribute opcode
- * idn: attribute idn to access
- * index: index field
- * selector: selector field
- * attr_val: the attribute value after the query request completes
+ * @hba: per-adapter instance
+ * @opcode: attribute opcode
+ * @idn: attribute idn to access
+ * @index: index field
+ * @selector: selector field
+ * @attr_val: the attribute value after the query request completes
*
* Returns 0 for success, non-zero in case of failure
*/
-static int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode,
- enum attr_idn idn, u8 index, u8 selector, u32 *attr_val)
+int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode,
+ enum attr_idn idn, u8 index, u8 selector, u32 *attr_val)
{
struct ufs_query_req *request = NULL;
struct ufs_query_res *response = NULL;
@@ -2880,25 +2882,24 @@ out:
}
/**
- * ufshcd_query_descriptor_retry - API function for sending descriptor
- * requests
- * hba: per-adapter instance
- * opcode: attribute opcode
- * idn: attribute idn to access
- * index: index field
- * selector: selector field
- * desc_buf: the buffer that contains the descriptor
- * buf_len: length parameter passed to the device
+ * ufshcd_query_descriptor_retry - API function for sending descriptor requests
+ * @hba: per-adapter instance
+ * @opcode: attribute opcode
+ * @idn: attribute idn to access
+ * @index: index field
+ * @selector: selector field
+ * @desc_buf: the buffer that contains the descriptor
+ * @buf_len: length parameter passed to the device
*
* Returns 0 for success, non-zero in case of failure.
* The buf_len parameter will contain, on return, the length parameter
* received on the response.
*/
-static int ufshcd_query_descriptor_retry(struct ufs_hba *hba,
- enum query_opcode opcode,
- enum desc_idn idn, u8 index,
- u8 selector,
- u8 *desc_buf, int *buf_len)
+int ufshcd_query_descriptor_retry(struct ufs_hba *hba,
+ enum query_opcode opcode,
+ enum desc_idn idn, u8 index,
+ u8 selector,
+ u8 *desc_buf, int *buf_len)
{
int err;
int retries;
@@ -2987,6 +2988,9 @@ int ufshcd_map_desc_id_to_length(struct ufs_hba *hba,
case QUERY_DESC_IDN_STRING:
*desc_len = QUERY_DESC_MAX_SIZE;
break;
+ case QUERY_DESC_IDN_HEALTH:
+ *desc_len = hba->desc_size.hlth_desc;
+ break;
case QUERY_DESC_IDN_RFU_0:
case QUERY_DESC_IDN_RFU_1:
*desc_len = 0;
@@ -3010,12 +3014,12 @@ EXPORT_SYMBOL(ufshcd_map_desc_id_to_length);
*
* Return 0 in case of success, non-zero otherwise
*/
-static int ufshcd_read_desc_param(struct ufs_hba *hba,
- enum desc_idn desc_id,
- int desc_index,
- u8 param_offset,
- u8 *param_read_buf,
- u8 param_size)
+int ufshcd_read_desc_param(struct ufs_hba *hba,
+ enum desc_idn desc_id,
+ int desc_index,
+ u8 param_offset,
+ u8 *param_read_buf,
+ u8 param_size)
{
int ret;
u8 *desc_buf;
@@ -3110,9 +3114,8 @@ static int ufshcd_read_device_desc(struct ufs_hba *hba, u8 *buf, u32 size)
*
* Return 0 in case of success, non-zero otherwise
*/
-#define ASCII_STD true
-static int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index,
- u8 *buf, u32 size, bool ascii)
+int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index,
+ u8 *buf, u32 size, bool ascii)
{
int err = 0;
@@ -3189,7 +3192,7 @@ static inline int ufshcd_read_unit_desc_param(struct ufs_hba *hba,
* Unit descriptors are only available for general purpose LUs (LUN id
* from 0 to 7) and RPMB Well known LU.
*/
- if (lun != UFS_UPIU_RPMB_WLUN && (lun >= UFS_UPIU_MAX_GENERAL_LUN))
+ if (!ufs_is_valid_unit_desc_lun(lun))
return -EOPNOTSUPP;
return ufshcd_read_desc_param(hba, QUERY_DESC_IDN_UNIT, lun,
@@ -3742,6 +3745,18 @@ static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba)
return ret;
}
+static void ufshcd_auto_hibern8_enable(struct ufs_hba *hba)
+{
+ unsigned long flags;
+
+ if (!(hba->capabilities & MASK_AUTO_HIBERN8_SUPPORT) || !hba->ahit)
+ return;
+
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ ufshcd_writel(hba, hba->ahit, REG_AUTO_HIBERNATE_IDLE_TIMER);
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+}
+
/**
* ufshcd_init_pwr_info - setting the POR (power on reset)
* values in hba power info
@@ -3912,7 +3927,7 @@ static int ufshcd_config_pwr_mode(struct ufs_hba *hba,
/**
* ufshcd_complete_dev_init() - checks device readiness
- * hba: per-adapter instance
+ * @hba: per-adapter instance
*
* Set fDeviceInit flag and poll until device toggles it.
*/
@@ -4453,7 +4468,7 @@ static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index, u8 *resp)
/**
* ufshcd_scsi_cmd_status - Update SCSI command result based on SCSI status
- * @lrb: pointer to local reference block of completed command
+ * @lrbp: pointer to local reference block of completed command
* @scsi_status: SCSI command status
*
* Returns value base on SCSI command status
@@ -4488,7 +4503,7 @@ ufshcd_scsi_cmd_status(struct ufshcd_lrb *lrbp, int scsi_status)
/**
* ufshcd_transfer_rsp_status - Get overall status of the response
* @hba: per adapter instance
- * @lrb: pointer to local reference block of completed command
+ * @lrbp: pointer to local reference block of completed command
*
* Returns result of the command to notify SCSI midlayer
*/
@@ -5464,11 +5479,14 @@ static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id,
spin_unlock_irqrestore(host->host_lock, flags);
+ ufshcd_add_tm_upiu_trace(hba, task_tag, "tm_send");
+
/* wait until the task management command is completed */
err = wait_event_timeout(hba->tm_wq,
test_bit(free_slot, &hba->tm_condition),
msecs_to_jiffies(TM_CMD_TIMEOUT));
if (!err) {
+ ufshcd_add_tm_upiu_trace(hba, task_tag, "tm_complete_err");
dev_err(hba->dev, "%s: task management cmd 0x%.2x timed-out\n",
__func__, tm_function);
if (ufshcd_clear_tm_cmd(hba, free_slot))
@@ -5477,6 +5495,7 @@ static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id,
err = -ETIMEDOUT;
} else {
err = ufshcd_task_req_compl(hba, free_slot, tm_response);
+ ufshcd_add_tm_upiu_trace(hba, task_tag, "tm_complete");
}
clear_bit(free_slot, &hba->tm_condition);
@@ -5796,7 +5815,7 @@ static int ufshcd_reset_and_restore(struct ufs_hba *hba)
/**
* ufshcd_eh_host_reset_handler - host reset handler registered to scsi layer
- * @cmd - SCSI command pointer
+ * @cmd: SCSI command pointer
*
* Returns SUCCESS/FAILED
*/
@@ -5981,11 +6000,11 @@ static void ufshcd_init_icc_levels(struct ufs_hba *hba)
* will take effect only when its sent to "UFS device" well known logical unit
* hence we require the scsi_device instance to represent this logical unit in
* order for the UFS host driver to send the SSU command for power management.
-
+ *
* We also require the scsi_device instance for "RPMB" (Replay Protected Memory
* Block) LU so user space process can control this LU. User space may also
* want to have access to BOOT LU.
-
+ *
* This function adds scsi device instances for each of all well known LUs
* (except "REPORT LUNS" LU).
*
@@ -6054,7 +6073,7 @@ static int ufs_get_device_desc(struct ufs_hba *hba,
model_index = desc_buf[DEVICE_DESC_PARAM_PRDCT_NAME];
err = ufshcd_read_string_desc(hba, model_index, str_desc_buf,
- QUERY_DESC_MAX_SIZE, ASCII_STD);
+ QUERY_DESC_MAX_SIZE, true/*ASCII*/);
if (err) {
dev_err(hba->dev, "%s: Failed reading Product Name. err = %d\n",
__func__, err);
@@ -6300,6 +6319,10 @@ static void ufshcd_init_desc_sizes(struct ufs_hba *hba)
&hba->desc_size.geom_desc);
if (err)
hba->desc_size.geom_desc = QUERY_DESC_GEOMETRY_DEF_SIZE;
+ err = ufshcd_read_desc_length(hba, QUERY_DESC_IDN_HEALTH, 0,
+ &hba->desc_size.hlth_desc);
+ if (err)
+ hba->desc_size.hlth_desc = QUERY_DESC_HEALTH_DEF_SIZE;
}
static void ufshcd_def_desc_sizes(struct ufs_hba *hba)
@@ -6310,6 +6333,7 @@ static void ufshcd_def_desc_sizes(struct ufs_hba *hba)
hba->desc_size.conf_desc = QUERY_DESC_CONFIGURATION_DEF_SIZE;
hba->desc_size.unit_desc = QUERY_DESC_UNIT_DEF_SIZE;
hba->desc_size.geom_desc = QUERY_DESC_GEOMETRY_DEF_SIZE;
+ hba->desc_size.hlth_desc = QUERY_DESC_HEALTH_DEF_SIZE;
}
/**
@@ -6338,6 +6362,9 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
/* UniPro link is active now */
ufshcd_set_link_active(hba);
+ /* Enable Auto-Hibernate if configured */
+ ufshcd_auto_hibern8_enable(hba);
+
ret = ufshcd_verify_dev_init(hba);
if (ret)
goto out;
@@ -6496,6 +6523,12 @@ static enum blk_eh_timer_return ufshcd_eh_timed_out(struct scsi_cmnd *scmd)
return found ? BLK_EH_NOT_HANDLED : BLK_EH_RESET_TIMER;
}
+static const struct attribute_group *ufshcd_driver_groups[] = {
+ &ufs_sysfs_unit_descriptor_group,
+ &ufs_sysfs_lun_attributes_group,
+ NULL,
+};
+
static struct scsi_host_template ufshcd_driver_template = {
.module = THIS_MODULE,
.name = UFSHCD,
@@ -6515,6 +6548,7 @@ static struct scsi_host_template ufshcd_driver_template = {
.can_queue = UFSHCD_CAN_QUEUE,
.max_host_blocked = 1,
.track_queue_depth = 1,
+ .sdev_groups = ufshcd_driver_groups,
};
static int ufshcd_config_vreg_load(struct device *dev, struct ufs_vreg *vreg,
@@ -7415,6 +7449,10 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
/* Schedule clock gating in case of no access to UFS device yet */
ufshcd_release(hba);
+
+ /* Enable Auto-Hibernate if configured */
+ ufshcd_auto_hibern8_enable(hba);
+
goto out;
set_old_link_state:
@@ -7436,7 +7474,6 @@ out:
/**
* ufshcd_system_suspend - system suspend routine
* @hba: per adapter instance
- * @pm_op: runtime PM or system PM
*
* Check the description of ufshcd_suspend() function for more details.
*
@@ -7587,133 +7624,6 @@ int ufshcd_runtime_idle(struct ufs_hba *hba)
}
EXPORT_SYMBOL(ufshcd_runtime_idle);
-static inline ssize_t ufshcd_pm_lvl_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count,
- bool rpm)
-{
- struct ufs_hba *hba = dev_get_drvdata(dev);
- unsigned long flags, value;
-
- if (kstrtoul(buf, 0, &value))
- return -EINVAL;
-
- if (value >= UFS_PM_LVL_MAX)
- return -EINVAL;
-
- spin_lock_irqsave(hba->host->host_lock, flags);
- if (rpm)
- hba->rpm_lvl = value;
- else
- hba->spm_lvl = value;
- spin_unlock_irqrestore(hba->host->host_lock, flags);
- return count;
-}
-
-static ssize_t ufshcd_rpm_lvl_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct ufs_hba *hba = dev_get_drvdata(dev);
- int curr_len;
- u8 lvl;
-
- curr_len = snprintf(buf, PAGE_SIZE,
- "\nCurrent Runtime PM level [%d] => dev_state [%s] link_state [%s]\n",
- hba->rpm_lvl,
- ufschd_ufs_dev_pwr_mode_to_string(
- ufs_pm_lvl_states[hba->rpm_lvl].dev_state),
- ufschd_uic_link_state_to_string(
- ufs_pm_lvl_states[hba->rpm_lvl].link_state));
-
- curr_len += snprintf((buf + curr_len), (PAGE_SIZE - curr_len),
- "\nAll available Runtime PM levels info:\n");
- for (lvl = UFS_PM_LVL_0; lvl < UFS_PM_LVL_MAX; lvl++)
- curr_len += snprintf((buf + curr_len), (PAGE_SIZE - curr_len),
- "\tRuntime PM level [%d] => dev_state [%s] link_state [%s]\n",
- lvl,
- ufschd_ufs_dev_pwr_mode_to_string(
- ufs_pm_lvl_states[lvl].dev_state),
- ufschd_uic_link_state_to_string(
- ufs_pm_lvl_states[lvl].link_state));
-
- return curr_len;
-}
-
-static ssize_t ufshcd_rpm_lvl_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- return ufshcd_pm_lvl_store(dev, attr, buf, count, true);
-}
-
-static void ufshcd_add_rpm_lvl_sysfs_nodes(struct ufs_hba *hba)
-{
- hba->rpm_lvl_attr.show = ufshcd_rpm_lvl_show;
- hba->rpm_lvl_attr.store = ufshcd_rpm_lvl_store;
- sysfs_attr_init(&hba->rpm_lvl_attr.attr);
- hba->rpm_lvl_attr.attr.name = "rpm_lvl";
- hba->rpm_lvl_attr.attr.mode = 0644;
- if (device_create_file(hba->dev, &hba->rpm_lvl_attr))
- dev_err(hba->dev, "Failed to create sysfs for rpm_lvl\n");
-}
-
-static ssize_t ufshcd_spm_lvl_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct ufs_hba *hba = dev_get_drvdata(dev);
- int curr_len;
- u8 lvl;
-
- curr_len = snprintf(buf, PAGE_SIZE,
- "\nCurrent System PM level [%d] => dev_state [%s] link_state [%s]\n",
- hba->spm_lvl,
- ufschd_ufs_dev_pwr_mode_to_string(
- ufs_pm_lvl_states[hba->spm_lvl].dev_state),
- ufschd_uic_link_state_to_string(
- ufs_pm_lvl_states[hba->spm_lvl].link_state));
-
- curr_len += snprintf((buf + curr_len), (PAGE_SIZE - curr_len),
- "\nAll available System PM levels info:\n");
- for (lvl = UFS_PM_LVL_0; lvl < UFS_PM_LVL_MAX; lvl++)
- curr_len += snprintf((buf + curr_len), (PAGE_SIZE - curr_len),
- "\tSystem PM level [%d] => dev_state [%s] link_state [%s]\n",
- lvl,
- ufschd_ufs_dev_pwr_mode_to_string(
- ufs_pm_lvl_states[lvl].dev_state),
- ufschd_uic_link_state_to_string(
- ufs_pm_lvl_states[lvl].link_state));
-
- return curr_len;
-}
-
-static ssize_t ufshcd_spm_lvl_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- return ufshcd_pm_lvl_store(dev, attr, buf, count, false);
-}
-
-static void ufshcd_add_spm_lvl_sysfs_nodes(struct ufs_hba *hba)
-{
- hba->spm_lvl_attr.show = ufshcd_spm_lvl_show;
- hba->spm_lvl_attr.store = ufshcd_spm_lvl_store;
- sysfs_attr_init(&hba->spm_lvl_attr.attr);
- hba->spm_lvl_attr.attr.name = "spm_lvl";
- hba->spm_lvl_attr.attr.mode = 0644;
- if (device_create_file(hba->dev, &hba->spm_lvl_attr))
- dev_err(hba->dev, "Failed to create sysfs for spm_lvl\n");
-}
-
-static inline void ufshcd_add_sysfs_nodes(struct ufs_hba *hba)
-{
- ufshcd_add_rpm_lvl_sysfs_nodes(hba);
- ufshcd_add_spm_lvl_sysfs_nodes(hba);
-}
-
-static inline void ufshcd_remove_sysfs_nodes(struct ufs_hba *hba)
-{
- device_remove_file(hba->dev, &hba->rpm_lvl_attr);
- device_remove_file(hba->dev, &hba->spm_lvl_attr);
-}
-
/**
* ufshcd_shutdown - shutdown routine
* @hba: per adapter instance
@@ -7747,11 +7657,11 @@ EXPORT_SYMBOL(ufshcd_shutdown);
/**
* ufshcd_remove - de-allocate SCSI host and host memory space
* data structure memory
- * @hba - per adapter instance
+ * @hba: per adapter instance
*/
void ufshcd_remove(struct ufs_hba *hba)
{
- ufshcd_remove_sysfs_nodes(hba);
+ ufs_sysfs_remove_nodes(hba->dev);
scsi_remove_host(hba->host);
/* disable interrupts */
ufshcd_disable_intr(hba, hba->intr_mask);
@@ -7986,6 +7896,12 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
UFS_SLEEP_PWR_MODE,
UIC_LINK_HIBERN8_STATE);
+ /* Set the default auto-hiberate idle timer value to 150 ms */
+ if (hba->capabilities & MASK_AUTO_HIBERN8_SUPPORT) {
+ hba->ahit = FIELD_PREP(UFSHCI_AHIBERN8_TIMER_MASK, 150) |
+ FIELD_PREP(UFSHCI_AHIBERN8_SCALE_MASK, 3);
+ }
+
/* Hold auto suspend until async scan completes */
pm_runtime_get_sync(dev);
@@ -7998,7 +7914,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
ufshcd_set_ufs_dev_active(hba);
async_schedule(ufshcd_async_scan, hba);
- ufshcd_add_sysfs_nodes(hba);
+ ufs_sysfs_add_nodes(hba->dev);
return 0;
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 1332e544da92..8110dcd04d22 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -229,6 +229,7 @@ struct ufs_desc_size {
int interc_desc;
int unit_desc;
int conf_desc;
+ int hlth_desc;
};
/**
@@ -530,6 +531,9 @@ struct ufs_hba {
struct device_attribute spm_lvl_attr;
int pm_op_in_progress;
+ /* Auto-Hibernate Idle Timer register value */
+ u32 ahit;
+
struct ufshcd_lrb *lrb;
unsigned long lrb_in_use;
@@ -841,8 +845,24 @@ static inline bool ufshcd_is_hs_mode(struct ufs_pa_layer_attr *pwr_info)
}
/* Expose Query-Request API */
+int ufshcd_query_descriptor_retry(struct ufs_hba *hba,
+ enum query_opcode opcode,
+ enum desc_idn idn, u8 index,
+ u8 selector,
+ u8 *desc_buf, int *buf_len);
+int ufshcd_read_desc_param(struct ufs_hba *hba,
+ enum desc_idn desc_id,
+ int desc_index,
+ u8 param_offset,
+ u8 *param_read_buf,
+ u8 param_size);
+int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode,
+ enum attr_idn idn, u8 index, u8 selector, u32 *attr_val);
int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode,
enum flag_idn idn, bool *flag_res);
+int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index,
+ u8 *buf, u32 size, bool ascii);
+
int ufshcd_hold(struct ufs_hba *hba, bool async);
void ufshcd_release(struct ufs_hba *hba);
@@ -985,4 +1005,21 @@ static inline void ufshcd_vops_dbg_register_dump(struct ufs_hba *hba)
hba->vops->dbg_register_dump(hba);
}
+extern struct ufs_pm_lvl_states ufs_pm_lvl_states[];
+
+/*
+ * ufshcd_scsi_to_upiu_lun - maps scsi LUN to UPIU LUN
+ * @scsi_lun: scsi LUN id
+ *
+ * Returns UPIU LUN id
+ */
+static inline u8 ufshcd_scsi_to_upiu_lun(unsigned int scsi_lun)
+{
+ if (scsi_is_wlun(scsi_lun))
+ return (scsi_lun & UFS_UPIU_MAX_UNIT_NUM_ID)
+ | UFS_UPIU_WLUN_ID;
+ else
+ return scsi_lun & UFS_UPIU_MAX_UNIT_NUM_ID;
+}
+
#endif /* End of Header */
diff --git a/drivers/scsi/ufs/ufshci.h b/drivers/scsi/ufs/ufshci.h
index 1a1b5d9fe514..bb5d9c7f3353 100644
--- a/drivers/scsi/ufs/ufshci.h
+++ b/drivers/scsi/ufs/ufshci.h
@@ -86,6 +86,7 @@ enum {
enum {
MASK_TRANSFER_REQUESTS_SLOTS = 0x0000001F,
MASK_TASK_MANAGEMENT_REQUEST_SLOTS = 0x00070000,
+ MASK_AUTO_HIBERN8_SUPPORT = 0x00800000,
MASK_64_ADDRESSING_SUPPORT = 0x01000000,
MASK_OUT_OF_ORDER_DATA_DELIVERY_SUPPORT = 0x02000000,
MASK_UIC_DME_TEST_MODE_SUPPORT = 0x04000000,
@@ -119,6 +120,12 @@ enum {
#define MANUFACTURE_ID_MASK UFS_MASK(0xFFFF, 0)
#define PRODUCT_ID_MASK UFS_MASK(0xFFFF, 16)
+/* AHIT - Auto-Hibernate Idle Timer */
+#define UFSHCI_AHIBERN8_TIMER_MASK GENMASK(9, 0)
+#define UFSHCI_AHIBERN8_SCALE_MASK GENMASK(12, 10)
+#define UFSHCI_AHIBERN8_SCALE_FACTOR 10
+#define UFSHCI_AHIBERN8_MAX (1023 * 100000)
+
/*
* IS - Interrupt Status - 20h
*/
diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c
index 7c28e8d4955a..45d04631888a 100644
--- a/drivers/scsi/virtio_scsi.c
+++ b/drivers/scsi/virtio_scsi.c
@@ -91,9 +91,6 @@ struct virtio_scsi_vq {
struct virtio_scsi_target_state {
seqcount_t tgt_seq;
- /* Count of outstanding requests. */
- atomic_t reqs;
-
/* Currently active virtqueue for requests sent to this target. */
struct virtio_scsi_vq *req_vq;
};
@@ -152,8 +149,6 @@ static void virtscsi_complete_cmd(struct virtio_scsi *vscsi, void *buf)
struct virtio_scsi_cmd *cmd = buf;
struct scsi_cmnd *sc = cmd->sc;
struct virtio_scsi_cmd_resp *resp = &cmd->resp.cmd;
- struct virtio_scsi_target_state *tgt =
- scsi_target(sc->device)->hostdata;
dev_dbg(&sc->device->sdev_gendev,
"cmd %p response %u status %#02x sense_len %u\n",
@@ -210,8 +205,6 @@ static void virtscsi_complete_cmd(struct virtio_scsi *vscsi, void *buf)
}
sc->scsi_done(sc);
-
- atomic_dec(&tgt->reqs);
}
static void virtscsi_vq_done(struct virtio_scsi *vscsi,
@@ -529,11 +522,20 @@ static void virtio_scsi_init_hdr_pi(struct virtio_device *vdev,
}
#endif
-static int virtscsi_queuecommand(struct virtio_scsi *vscsi,
- struct virtio_scsi_vq *req_vq,
+static struct virtio_scsi_vq *virtscsi_pick_vq_mq(struct virtio_scsi *vscsi,
+ struct scsi_cmnd *sc)
+{
+ u32 tag = blk_mq_unique_tag(sc->request);
+ u16 hwq = blk_mq_unique_tag_to_hwq(tag);
+
+ return &vscsi->req_vqs[hwq];
+}
+
+static int virtscsi_queuecommand(struct Scsi_Host *shost,
struct scsi_cmnd *sc)
{
- struct Scsi_Host *shost = virtio_scsi_host(vscsi->vdev);
+ struct virtio_scsi *vscsi = shost_priv(shost);
+ struct virtio_scsi_vq *req_vq = virtscsi_pick_vq_mq(vscsi, sc);
struct virtio_scsi_cmd *cmd = scsi_cmd_priv(sc);
unsigned long flags;
int req_size;
@@ -576,79 +578,6 @@ static int virtscsi_queuecommand(struct virtio_scsi *vscsi,
return 0;
}
-static int virtscsi_queuecommand_single(struct Scsi_Host *sh,
- struct scsi_cmnd *sc)
-{
- struct virtio_scsi *vscsi = shost_priv(sh);
- struct virtio_scsi_target_state *tgt =
- scsi_target(sc->device)->hostdata;
-
- atomic_inc(&tgt->reqs);
- return virtscsi_queuecommand(vscsi, &vscsi->req_vqs[0], sc);
-}
-
-static struct virtio_scsi_vq *virtscsi_pick_vq_mq(struct virtio_scsi *vscsi,
- struct scsi_cmnd *sc)
-{
- u32 tag = blk_mq_unique_tag(sc->request);
- u16 hwq = blk_mq_unique_tag_to_hwq(tag);
-
- return &vscsi->req_vqs[hwq];
-}
-
-static struct virtio_scsi_vq *virtscsi_pick_vq(struct virtio_scsi *vscsi,
- struct virtio_scsi_target_state *tgt)
-{
- struct virtio_scsi_vq *vq;
- unsigned long flags;
- u32 queue_num;
-
- local_irq_save(flags);
- if (atomic_inc_return(&tgt->reqs) > 1) {
- unsigned long seq;
-
- do {
- seq = read_seqcount_begin(&tgt->tgt_seq);
- vq = tgt->req_vq;
- } while (read_seqcount_retry(&tgt->tgt_seq, seq));
- } else {
- /* no writes can be concurrent because of atomic_t */
- write_seqcount_begin(&tgt->tgt_seq);
-
- /* keep previous req_vq if a reader just arrived */
- if (unlikely(atomic_read(&tgt->reqs) > 1)) {
- vq = tgt->req_vq;
- goto unlock;
- }
-
- queue_num = smp_processor_id();
- while (unlikely(queue_num >= vscsi->num_queues))
- queue_num -= vscsi->num_queues;
- tgt->req_vq = vq = &vscsi->req_vqs[queue_num];
- unlock:
- write_seqcount_end(&tgt->tgt_seq);
- }
- local_irq_restore(flags);
-
- return vq;
-}
-
-static int virtscsi_queuecommand_multi(struct Scsi_Host *sh,
- struct scsi_cmnd *sc)
-{
- struct virtio_scsi *vscsi = shost_priv(sh);
- struct virtio_scsi_target_state *tgt =
- scsi_target(sc->device)->hostdata;
- struct virtio_scsi_vq *req_vq;
-
- if (shost_use_blk_mq(sh))
- req_vq = virtscsi_pick_vq_mq(vscsi, sc);
- else
- req_vq = virtscsi_pick_vq(vscsi, tgt);
-
- return virtscsi_queuecommand(vscsi, req_vq, sc);
-}
-
static int virtscsi_tmf(struct virtio_scsi *vscsi, struct virtio_scsi_cmd *cmd)
{
DECLARE_COMPLETION_ONSTACK(comp);
@@ -775,7 +704,6 @@ static int virtscsi_target_alloc(struct scsi_target *starget)
return -ENOMEM;
seqcount_init(&tgt->tgt_seq);
- atomic_set(&tgt->reqs, 0);
tgt->req_vq = &vscsi->req_vqs[0];
starget->hostdata = tgt;
@@ -805,33 +733,13 @@ static enum blk_eh_timer_return virtscsi_eh_timed_out(struct scsi_cmnd *scmnd)
return BLK_EH_RESET_TIMER;
}
-static struct scsi_host_template virtscsi_host_template_single = {
- .module = THIS_MODULE,
- .name = "Virtio SCSI HBA",
- .proc_name = "virtio_scsi",
- .this_id = -1,
- .cmd_size = sizeof(struct virtio_scsi_cmd),
- .queuecommand = virtscsi_queuecommand_single,
- .change_queue_depth = virtscsi_change_queue_depth,
- .eh_abort_handler = virtscsi_abort,
- .eh_device_reset_handler = virtscsi_device_reset,
- .eh_timed_out = virtscsi_eh_timed_out,
- .slave_alloc = virtscsi_device_alloc,
-
- .dma_boundary = UINT_MAX,
- .use_clustering = ENABLE_CLUSTERING,
- .target_alloc = virtscsi_target_alloc,
- .target_destroy = virtscsi_target_destroy,
- .track_queue_depth = 1,
-};
-
-static struct scsi_host_template virtscsi_host_template_multi = {
+static struct scsi_host_template virtscsi_host_template = {
.module = THIS_MODULE,
.name = "Virtio SCSI HBA",
.proc_name = "virtio_scsi",
.this_id = -1,
.cmd_size = sizeof(struct virtio_scsi_cmd),
- .queuecommand = virtscsi_queuecommand_multi,
+ .queuecommand = virtscsi_queuecommand,
.change_queue_depth = virtscsi_change_queue_depth,
.eh_abort_handler = virtscsi_abort,
.eh_device_reset_handler = virtscsi_device_reset,
@@ -844,6 +752,7 @@ static struct scsi_host_template virtscsi_host_template_multi = {
.target_destroy = virtscsi_target_destroy,
.map_queues = virtscsi_map_queues,
.track_queue_depth = 1,
+ .force_blk_mq = 1,
};
#define virtscsi_config_get(vdev, fld) \
@@ -936,7 +845,6 @@ static int virtscsi_probe(struct virtio_device *vdev)
u32 sg_elems, num_targets;
u32 cmd_per_lun;
u32 num_queues;
- struct scsi_host_template *hostt;
if (!vdev->config->get) {
dev_err(&vdev->dev, "%s failure: config access disabled\n",
@@ -949,12 +857,7 @@ static int virtscsi_probe(struct virtio_device *vdev)
num_targets = virtscsi_config_get(vdev, max_target) + 1;
- if (num_queues == 1)
- hostt = &virtscsi_host_template_single;
- else
- hostt = &virtscsi_host_template_multi;
-
- shost = scsi_host_alloc(hostt,
+ shost = scsi_host_alloc(&virtscsi_host_template,
sizeof(*vscsi) + sizeof(vscsi->req_vqs[0]) * num_queues);
if (!shost)
return -ENOMEM;
diff --git a/drivers/siox/siox-core.c b/drivers/siox/siox-core.c
index fdfcdea25867..16590dfaafa4 100644
--- a/drivers/siox/siox-core.c
+++ b/drivers/siox/siox-core.c
@@ -594,7 +594,7 @@ static ssize_t device_add_store(struct device *dev,
size_t inbytes = 0, outbytes = 0;
u8 statustype = 0;
- ret = sscanf(buf, "%20s %zu %zu %hhu", type, &inbytes,
+ ret = sscanf(buf, "%19s %zu %zu %hhu", type, &inbytes,
&outbytes, &statustype);
if (ret != 3 && ret != 4)
return -EINVAL;
diff --git a/drivers/slimbus/core.c b/drivers/slimbus/core.c
index 4988a8f4d905..7ddfc675b131 100644
--- a/drivers/slimbus/core.c
+++ b/drivers/slimbus/core.c
@@ -141,7 +141,7 @@ static struct slim_device *slim_alloc_device(struct slim_controller *ctrl,
sbdev->e_addr = *eaddr;
ret = slim_add_device(ctrl, sbdev, node);
if (ret) {
- kfree(sbdev);
+ put_device(&sbdev->dev);
return NULL;
}
diff --git a/drivers/slimbus/messaging.c b/drivers/slimbus/messaging.c
index 884419c37e84..457ea1f8db30 100644
--- a/drivers/slimbus/messaging.c
+++ b/drivers/slimbus/messaging.c
@@ -183,7 +183,7 @@ static u16 slim_slicesize(int code)
0, 1, 2, 3, 3, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7
};
- clamp(code, 1, (int)ARRAY_SIZE(sizetocode));
+ code = clamp(code, 1, (int)ARRAY_SIZE(sizetocode));
return sizetocode[code - 1];
}
diff --git a/drivers/soc/amlogic/meson-gx-pwrc-vpu.c b/drivers/soc/amlogic/meson-gx-pwrc-vpu.c
index 2bdeebc48901..6289965c42e9 100644
--- a/drivers/soc/amlogic/meson-gx-pwrc-vpu.c
+++ b/drivers/soc/amlogic/meson-gx-pwrc-vpu.c
@@ -184,7 +184,8 @@ static int meson_gx_pwrc_vpu_probe(struct platform_device *pdev)
rstc = devm_reset_control_array_get(&pdev->dev, false, false);
if (IS_ERR(rstc)) {
- dev_err(&pdev->dev, "failed to get reset lines\n");
+ if (PTR_ERR(rstc) != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "failed to get reset lines\n");
return PTR_ERR(rstc);
}
@@ -224,7 +225,11 @@ static int meson_gx_pwrc_vpu_probe(struct platform_device *pdev)
static void meson_gx_pwrc_vpu_shutdown(struct platform_device *pdev)
{
- meson_gx_pwrc_vpu_power_off(&vpu_hdmi_pd.genpd);
+ bool powered_off;
+
+ powered_off = meson_gx_pwrc_vpu_get_power(&vpu_hdmi_pd);
+ if (!powered_off)
+ meson_gx_pwrc_vpu_power_off(&vpu_hdmi_pd.genpd);
}
static const struct of_device_id meson_gx_pwrc_vpu_match_table[] = {
diff --git a/drivers/soc/amlogic/meson-gx-socinfo.c b/drivers/soc/amlogic/meson-gx-socinfo.c
index f2d8c3c53ea4..37ea0a1c24c8 100644
--- a/drivers/soc/amlogic/meson-gx-socinfo.c
+++ b/drivers/soc/amlogic/meson-gx-socinfo.c
@@ -33,6 +33,10 @@ static const struct meson_gx_soc_id {
{ "GXL", 0x21 },
{ "GXM", 0x22 },
{ "TXL", 0x23 },
+ { "TXLX", 0x24 },
+ { "AXG", 0x25 },
+ { "GXLX", 0x26 },
+ { "TXHD", 0x27 },
};
static const struct meson_gx_package_id {
@@ -41,12 +45,18 @@ static const struct meson_gx_package_id {
unsigned int pack_id;
} soc_packages[] = {
{ "S905", 0x1f, 0 },
+ { "S905H", 0x1f, 0x13 },
{ "S905M", 0x1f, 0x20 },
{ "S905D", 0x21, 0 },
{ "S905X", 0x21, 0x80 },
+ { "S905W", 0x21, 0xa0 },
{ "S905L", 0x21, 0xc0 },
{ "S905M2", 0x21, 0xe0 },
{ "S912", 0x22, 0 },
+ { "962X", 0x24, 0x10 },
+ { "962E", 0x24, 0x20 },
+ { "A113X", 0x25, 0x37 },
+ { "A113D", 0x25, 0x22 },
};
static inline unsigned int socinfo_to_major(u32 socinfo)
@@ -97,7 +107,7 @@ static const char *socinfo_to_soc_id(u32 socinfo)
return "Unknown";
}
-int __init meson_gx_socinfo_init(void)
+static int __init meson_gx_socinfo_init(void)
{
struct soc_device_attribute *soc_dev_attr;
struct soc_device *soc_dev;
diff --git a/drivers/soc/amlogic/meson-mx-socinfo.c b/drivers/soc/amlogic/meson-mx-socinfo.c
index 7bfff5ff22a2..78f0f1aeca57 100644
--- a/drivers/soc/amlogic/meson-mx-socinfo.c
+++ b/drivers/soc/amlogic/meson-mx-socinfo.c
@@ -104,7 +104,7 @@ static const struct of_device_id meson_mx_socinfo_analog_top_ids[] = {
{ /* sentinel */ }
};
-int __init meson_mx_socinfo_init(void)
+static int __init meson_mx_socinfo_init(void)
{
struct soc_device_attribute *soc_dev_attr;
struct soc_device *soc_dev;
diff --git a/drivers/soc/bcm/raspberrypi-power.c b/drivers/soc/bcm/raspberrypi-power.c
index fe96a8b956fb..f7ed1187518b 100644
--- a/drivers/soc/bcm/raspberrypi-power.c
+++ b/drivers/soc/bcm/raspberrypi-power.c
@@ -45,7 +45,7 @@ struct rpi_power_domains {
struct rpi_power_domain_packet {
u32 domain;
u32 on;
-} __packet;
+};
/*
* Asks the firmware to enable or disable power on a specific power
diff --git a/drivers/soc/imx/gpc.c b/drivers/soc/imx/gpc.c
index 750f93197411..c4d35f32af8d 100644
--- a/drivers/soc/imx/gpc.c
+++ b/drivers/soc/imx/gpc.c
@@ -254,6 +254,7 @@ static struct imx_pm_domain imx_gpc_domains[] = {
{
.base = {
.name = "ARM",
+ .flags = GENPD_FLAG_ALWAYS_ON,
},
}, {
.base = {
diff --git a/drivers/soc/mediatek/mtk-scpsys.c b/drivers/soc/mediatek/mtk-scpsys.c
index 435ce5ec648a..d762a46d434f 100644
--- a/drivers/soc/mediatek/mtk-scpsys.c
+++ b/drivers/soc/mediatek/mtk-scpsys.c
@@ -24,6 +24,7 @@
#include <dt-bindings/power/mt2712-power.h>
#include <dt-bindings/power/mt6797-power.h>
#include <dt-bindings/power/mt7622-power.h>
+#include <dt-bindings/power/mt7623a-power.h>
#include <dt-bindings/power/mt8173-power.h>
#define SPM_VDE_PWR_CON 0x0210
@@ -518,7 +519,8 @@ static const struct scp_domain_data scp_domain_data_mt2701[] = {
.name = "conn",
.sta_mask = PWR_STATUS_CONN,
.ctl_offs = SPM_CONN_PWR_CON,
- .bus_prot_mask = 0x0104,
+ .bus_prot_mask = MT2701_TOP_AXI_PROT_EN_CONN_M |
+ MT2701_TOP_AXI_PROT_EN_CONN_S,
.clk_id = {CLK_NONE},
.active_wakeup = true,
},
@@ -528,7 +530,7 @@ static const struct scp_domain_data scp_domain_data_mt2701[] = {
.ctl_offs = SPM_DIS_PWR_CON,
.sram_pdn_bits = GENMASK(11, 8),
.clk_id = {CLK_MM},
- .bus_prot_mask = 0x0002,
+ .bus_prot_mask = MT2701_TOP_AXI_PROT_EN_MM_M0,
.active_wakeup = true,
},
[MT2701_POWER_DOMAIN_MFG] = {
@@ -664,12 +666,48 @@ static const struct scp_domain_data scp_domain_data_mt2712[] = {
.name = "mfg",
.sta_mask = PWR_STATUS_MFG,
.ctl_offs = SPM_MFG_PWR_CON,
- .sram_pdn_bits = GENMASK(11, 8),
- .sram_pdn_ack_bits = GENMASK(19, 16),
+ .sram_pdn_bits = GENMASK(8, 8),
+ .sram_pdn_ack_bits = GENMASK(16, 16),
.clk_id = {CLK_MFG},
.bus_prot_mask = BIT(14) | BIT(21) | BIT(23),
.active_wakeup = true,
},
+ [MT2712_POWER_DOMAIN_MFG_SC1] = {
+ .name = "mfg_sc1",
+ .sta_mask = BIT(22),
+ .ctl_offs = 0x02c0,
+ .sram_pdn_bits = GENMASK(8, 8),
+ .sram_pdn_ack_bits = GENMASK(16, 16),
+ .clk_id = {CLK_NONE},
+ .active_wakeup = true,
+ },
+ [MT2712_POWER_DOMAIN_MFG_SC2] = {
+ .name = "mfg_sc2",
+ .sta_mask = BIT(23),
+ .ctl_offs = 0x02c4,
+ .sram_pdn_bits = GENMASK(8, 8),
+ .sram_pdn_ack_bits = GENMASK(16, 16),
+ .clk_id = {CLK_NONE},
+ .active_wakeup = true,
+ },
+ [MT2712_POWER_DOMAIN_MFG_SC3] = {
+ .name = "mfg_sc3",
+ .sta_mask = BIT(30),
+ .ctl_offs = 0x01f8,
+ .sram_pdn_bits = GENMASK(8, 8),
+ .sram_pdn_ack_bits = GENMASK(16, 16),
+ .clk_id = {CLK_NONE},
+ .active_wakeup = true,
+ },
+};
+
+static const struct scp_subdomain scp_subdomain_mt2712[] = {
+ {MT2712_POWER_DOMAIN_MM, MT2712_POWER_DOMAIN_VDEC},
+ {MT2712_POWER_DOMAIN_MM, MT2712_POWER_DOMAIN_VENC},
+ {MT2712_POWER_DOMAIN_MM, MT2712_POWER_DOMAIN_ISP},
+ {MT2712_POWER_DOMAIN_MFG, MT2712_POWER_DOMAIN_MFG_SC1},
+ {MT2712_POWER_DOMAIN_MFG_SC1, MT2712_POWER_DOMAIN_MFG_SC2},
+ {MT2712_POWER_DOMAIN_MFG_SC2, MT2712_POWER_DOMAIN_MFG_SC3},
};
/*
@@ -794,6 +832,47 @@ static const struct scp_domain_data scp_domain_data_mt7622[] = {
};
/*
+ * MT7623A power domain support
+ */
+
+static const struct scp_domain_data scp_domain_data_mt7623a[] = {
+ [MT7623A_POWER_DOMAIN_CONN] = {
+ .name = "conn",
+ .sta_mask = PWR_STATUS_CONN,
+ .ctl_offs = SPM_CONN_PWR_CON,
+ .bus_prot_mask = MT2701_TOP_AXI_PROT_EN_CONN_M |
+ MT2701_TOP_AXI_PROT_EN_CONN_S,
+ .clk_id = {CLK_NONE},
+ .active_wakeup = true,
+ },
+ [MT7623A_POWER_DOMAIN_ETH] = {
+ .name = "eth",
+ .sta_mask = PWR_STATUS_ETH,
+ .ctl_offs = SPM_ETH_PWR_CON,
+ .sram_pdn_bits = GENMASK(11, 8),
+ .sram_pdn_ack_bits = GENMASK(15, 12),
+ .clk_id = {CLK_ETHIF},
+ .active_wakeup = true,
+ },
+ [MT7623A_POWER_DOMAIN_HIF] = {
+ .name = "hif",
+ .sta_mask = PWR_STATUS_HIF,
+ .ctl_offs = SPM_HIF_PWR_CON,
+ .sram_pdn_bits = GENMASK(11, 8),
+ .sram_pdn_ack_bits = GENMASK(15, 12),
+ .clk_id = {CLK_ETHIF},
+ .active_wakeup = true,
+ },
+ [MT7623A_POWER_DOMAIN_IFR_MSC] = {
+ .name = "ifr_msc",
+ .sta_mask = PWR_STATUS_IFR_MSC,
+ .ctl_offs = SPM_IFR_MSC_PWR_CON,
+ .clk_id = {CLK_NONE},
+ .active_wakeup = true,
+ },
+};
+
+/*
* MT8173 power domain support
*/
@@ -905,6 +984,8 @@ static const struct scp_soc_data mt2701_data = {
static const struct scp_soc_data mt2712_data = {
.domains = scp_domain_data_mt2712,
.num_domains = ARRAY_SIZE(scp_domain_data_mt2712),
+ .subdomains = scp_subdomain_mt2712,
+ .num_subdomains = ARRAY_SIZE(scp_subdomain_mt2712),
.regs = {
.pwr_sta_offs = SPM_PWR_STATUS,
.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND
@@ -934,6 +1015,16 @@ static const struct scp_soc_data mt7622_data = {
.bus_prot_reg_update = true,
};
+static const struct scp_soc_data mt7623a_data = {
+ .domains = scp_domain_data_mt7623a,
+ .num_domains = ARRAY_SIZE(scp_domain_data_mt7623a),
+ .regs = {
+ .pwr_sta_offs = SPM_PWR_STATUS,
+ .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND
+ },
+ .bus_prot_reg_update = true,
+};
+
static const struct scp_soc_data mt8173_data = {
.domains = scp_domain_data_mt8173,
.num_domains = ARRAY_SIZE(scp_domain_data_mt8173),
@@ -964,6 +1055,9 @@ static const struct of_device_id of_scpsys_match_tbl[] = {
.compatible = "mediatek,mt7622-scpsys",
.data = &mt7622_data,
}, {
+ .compatible = "mediatek,mt7623a-scpsys",
+ .data = &mt7623a_data,
+ }, {
.compatible = "mediatek,mt8173-scpsys",
.data = &mt8173_data,
}, {
@@ -992,7 +1086,7 @@ static int scpsys_probe(struct platform_device *pdev)
pd_data = &scp->pd_data;
- for (i = 0, sd = soc->subdomains ; i < soc->num_subdomains ; i++) {
+ for (i = 0, sd = soc->subdomains; i < soc->num_subdomains; i++, sd++) {
ret = pm_genpd_add_subdomain(pd_data->domains[sd->origin],
pd_data->domains[sd->subdomain]);
if (ret && IS_ENABLED(CONFIG_PM))
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index e050eb83341d..5c4535b545cc 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -37,7 +37,7 @@ config QCOM_PM
config QCOM_QMI_HELPERS
tristate
- depends on ARCH_QCOM
+ depends on ARCH_QCOM && NET
help
Helper library for handling QMI encoded messages. QMI encoded
messages are used in communication between the majority of QRTR
@@ -47,6 +47,7 @@ config QCOM_QMI_HELPERS
config QCOM_RMTFS_MEM
tristate "Qualcomm Remote Filesystem memory driver"
depends on ARCH_QCOM
+ select QCOM_SCM
help
The Qualcomm remote filesystem memory driver is used for allocating
and exposing regions of shared memory with remote processors for the
diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c
index 08bd8549242a..17b314d9a148 100644
--- a/drivers/soc/qcom/mdt_loader.c
+++ b/drivers/soc/qcom/mdt_loader.c
@@ -83,12 +83,14 @@ EXPORT_SYMBOL_GPL(qcom_mdt_get_size);
* @mem_region: allocated memory region to load firmware into
* @mem_phys: physical address of allocated memory region
* @mem_size: size of the allocated memory region
+ * @reloc_base: adjusted physical address after relocation
*
* Returns 0 on success, negative errno otherwise.
*/
int qcom_mdt_load(struct device *dev, const struct firmware *fw,
const char *firmware, int pas_id, void *mem_region,
- phys_addr_t mem_phys, size_t mem_size)
+ phys_addr_t mem_phys, size_t mem_size,
+ phys_addr_t *reloc_base)
{
const struct elf32_phdr *phdrs;
const struct elf32_phdr *phdr;
@@ -192,6 +194,9 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw,
memset(ptr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz);
}
+ if (reloc_base)
+ *reloc_base = mem_reloc;
+
out:
kfree(fw_name);
diff --git a/drivers/soc/qcom/qmi_interface.c b/drivers/soc/qcom/qmi_interface.c
index 877611d5c42b..321982277697 100644
--- a/drivers/soc/qcom/qmi_interface.c
+++ b/drivers/soc/qcom/qmi_interface.c
@@ -586,7 +586,6 @@ static struct socket *qmi_sock_create(struct qmi_handle *qmi,
struct sockaddr_qrtr *sq)
{
struct socket *sock;
- int sl = sizeof(*sq);
int ret;
ret = sock_create_kern(&init_net, AF_QIPCRTR, SOCK_DGRAM,
@@ -594,7 +593,7 @@ static struct socket *qmi_sock_create(struct qmi_handle *qmi,
if (ret < 0)
return ERR_PTR(ret);
- ret = kernel_getsockname(sock, (struct sockaddr *)sq, &sl);
+ ret = kernel_getsockname(sock, (struct sockaddr *)sq);
if (ret < 0) {
sock_release(sock);
return ERR_PTR(ret);
diff --git a/drivers/soc/qcom/rmtfs_mem.c b/drivers/soc/qcom/rmtfs_mem.c
index 0a43b2e8906f..c8999e38b005 100644
--- a/drivers/soc/qcom/rmtfs_mem.c
+++ b/drivers/soc/qcom/rmtfs_mem.c
@@ -37,6 +37,8 @@ struct qcom_rmtfs_mem {
phys_addr_t size;
unsigned int client_id;
+
+ unsigned int perms;
};
static ssize_t qcom_rmtfs_mem_show(struct device *dev,
@@ -151,9 +153,11 @@ static void qcom_rmtfs_mem_release_device(struct device *dev)
static int qcom_rmtfs_mem_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
+ struct qcom_scm_vmperm perms[2];
struct reserved_mem *rmem;
struct qcom_rmtfs_mem *rmtfs_mem;
u32 client_id;
+ u32 vmid;
int ret;
rmem = of_reserved_mem_lookup(node);
@@ -204,10 +208,31 @@ static int qcom_rmtfs_mem_probe(struct platform_device *pdev)
rmtfs_mem->dev.release = qcom_rmtfs_mem_release_device;
+ ret = of_property_read_u32(node, "qcom,vmid", &vmid);
+ if (ret < 0 && ret != -EINVAL) {
+ dev_err(&pdev->dev, "failed to parse qcom,vmid\n");
+ goto remove_cdev;
+ } else if (!ret) {
+ perms[0].vmid = QCOM_SCM_VMID_HLOS;
+ perms[0].perm = QCOM_SCM_PERM_RW;
+ perms[1].vmid = vmid;
+ perms[1].perm = QCOM_SCM_PERM_RW;
+
+ rmtfs_mem->perms = BIT(QCOM_SCM_VMID_HLOS);
+ ret = qcom_scm_assign_mem(rmtfs_mem->addr, rmtfs_mem->size,
+ &rmtfs_mem->perms, perms, 2);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "assign memory failed\n");
+ goto remove_cdev;
+ }
+ }
+
dev_set_drvdata(&pdev->dev, rmtfs_mem);
return 0;
+remove_cdev:
+ cdev_device_del(&rmtfs_mem->cdev, &rmtfs_mem->dev);
put_device:
put_device(&rmtfs_mem->dev);
@@ -217,6 +242,15 @@ put_device:
static int qcom_rmtfs_mem_remove(struct platform_device *pdev)
{
struct qcom_rmtfs_mem *rmtfs_mem = dev_get_drvdata(&pdev->dev);
+ struct qcom_scm_vmperm perm;
+
+ if (rmtfs_mem->perms) {
+ perm.vmid = QCOM_SCM_VMID_HLOS;
+ perm.perm = QCOM_SCM_PERM_RW;
+
+ qcom_scm_assign_mem(rmtfs_mem->addr, rmtfs_mem->size,
+ &rmtfs_mem->perms, &perm, 1);
+ }
cdev_device_del(&rmtfs_mem->cdev, &rmtfs_mem->dev);
put_device(&rmtfs_mem->dev);
diff --git a/drivers/soc/qcom/wcnss_ctrl.c b/drivers/soc/qcom/wcnss_ctrl.c
index d008e5b82db4..df3ccb30bc2d 100644
--- a/drivers/soc/qcom/wcnss_ctrl.c
+++ b/drivers/soc/qcom/wcnss_ctrl.c
@@ -249,7 +249,7 @@ static int wcnss_download_nv(struct wcnss_ctrl *wcnss, bool *expect_cbc)
/* Increment for next fragment */
req->seq++;
- data += req->hdr.len;
+ data += NV_FRAGMENT_SIZE;
left -= NV_FRAGMENT_SIZE;
} while (left > 0);
diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig
index 09550b1da56d..3bbe6114a420 100644
--- a/drivers/soc/renesas/Kconfig
+++ b/drivers/soc/renesas/Kconfig
@@ -3,8 +3,8 @@ config SOC_RENESAS
default y if ARCH_RENESAS
select SOC_BUS
select RST_RCAR if ARCH_RCAR_GEN1 || ARCH_RCAR_GEN2 || \
- ARCH_R8A7795 || ARCH_R8A7796 || ARCH_R8A77970 || \
- ARCH_R8A77995
+ ARCH_R8A7795 || ARCH_R8A7796 || ARCH_R8A77965 || \
+ ARCH_R8A77970 || ARCH_R8A77980 || ARCH_R8A77995
select SYSC_R8A7743 if ARCH_R8A7743
select SYSC_R8A7745 if ARCH_R8A7745
select SYSC_R8A7779 if ARCH_R8A7779
@@ -14,7 +14,9 @@ config SOC_RENESAS
select SYSC_R8A7794 if ARCH_R8A7794
select SYSC_R8A7795 if ARCH_R8A7795
select SYSC_R8A7796 if ARCH_R8A7796
+ select SYSC_R8A77965 if ARCH_R8A77965
select SYSC_R8A77970 if ARCH_R8A77970
+ select SYSC_R8A77980 if ARCH_R8A77980
select SYSC_R8A77995 if ARCH_R8A77995
if SOC_RENESAS
@@ -56,10 +58,18 @@ config SYSC_R8A7796
bool "R-Car M3-W System Controller support" if COMPILE_TEST
select SYSC_RCAR
+config SYSC_R8A77965
+ bool "R-Car M3-N System Controller support" if COMPILE_TEST
+ select SYSC_RCAR
+
config SYSC_R8A77970
bool "R-Car V3M System Controller support" if COMPILE_TEST
select SYSC_RCAR
+config SYSC_R8A77980
+ bool "R-Car V3H System Controller support" if COMPILE_TEST
+ select SYSC_RCAR
+
config SYSC_R8A77995
bool "R-Car D3 System Controller support" if COMPILE_TEST
select SYSC_RCAR
diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile
index 845d62a08ce1..ccb5ec57a262 100644
--- a/drivers/soc/renesas/Makefile
+++ b/drivers/soc/renesas/Makefile
@@ -12,7 +12,9 @@ obj-$(CONFIG_SYSC_R8A7792) += r8a7792-sysc.o
obj-$(CONFIG_SYSC_R8A7794) += r8a7794-sysc.o
obj-$(CONFIG_SYSC_R8A7795) += r8a7795-sysc.o
obj-$(CONFIG_SYSC_R8A7796) += r8a7796-sysc.o
+obj-$(CONFIG_SYSC_R8A77965) += r8a77965-sysc.o
obj-$(CONFIG_SYSC_R8A77970) += r8a77970-sysc.o
+obj-$(CONFIG_SYSC_R8A77980) += r8a77980-sysc.o
obj-$(CONFIG_SYSC_R8A77995) += r8a77995-sysc.o
# Family
diff --git a/drivers/soc/renesas/r8a77965-sysc.c b/drivers/soc/renesas/r8a77965-sysc.c
new file mode 100644
index 000000000000..d7f7928e3c07
--- /dev/null
+++ b/drivers/soc/renesas/r8a77965-sysc.c
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Renesas R-Car M3-N System Controller
+ * Copyright (C) 2018 Jacopo Mondi <jacopo+renesas@jmondi.org>
+ *
+ * Based on Renesas R-Car M3-W System Controller
+ * Copyright (C) 2016 Glider bvba
+ */
+
+#include <linux/bug.h>
+#include <linux/kernel.h>
+
+#include <dt-bindings/power/r8a77965-sysc.h>
+
+#include "rcar-sysc.h"
+
+static const struct rcar_sysc_area r8a77965_areas[] __initconst = {
+ { "always-on", 0, 0, R8A77965_PD_ALWAYS_ON, -1, PD_ALWAYS_ON },
+ { "ca57-scu", 0x1c0, 0, R8A77965_PD_CA57_SCU, R8A77965_PD_ALWAYS_ON,
+ PD_SCU },
+ { "ca57-cpu0", 0x80, 0, R8A77965_PD_CA57_CPU0, R8A77965_PD_CA57_SCU,
+ PD_CPU_NOCR },
+ { "ca57-cpu1", 0x80, 1, R8A77965_PD_CA57_CPU1, R8A77965_PD_CA57_SCU,
+ PD_CPU_NOCR },
+ { "cr7", 0x240, 0, R8A77965_PD_CR7, R8A77965_PD_ALWAYS_ON },
+ { "a3vc", 0x380, 0, R8A77965_PD_A3VC, R8A77965_PD_ALWAYS_ON },
+ { "a3vp", 0x340, 0, R8A77965_PD_A3VP, R8A77965_PD_ALWAYS_ON },
+ { "a2vc1", 0x3c0, 1, R8A77965_PD_A2VC1, R8A77965_PD_A3VC },
+ { "3dg-a", 0x100, 0, R8A77965_PD_3DG_A, R8A77965_PD_ALWAYS_ON },
+ { "3dg-b", 0x100, 1, R8A77965_PD_3DG_B, R8A77965_PD_3DG_A },
+ { "a3ir", 0x180, 0, R8A77965_PD_A3IR, R8A77965_PD_ALWAYS_ON },
+};
+
+const struct rcar_sysc_info r8a77965_sysc_info __initconst = {
+ .areas = r8a77965_areas,
+ .num_areas = ARRAY_SIZE(r8a77965_areas),
+};
diff --git a/drivers/soc/renesas/r8a77970-sysc.c b/drivers/soc/renesas/r8a77970-sysc.c
index 8c614164718e..caf894f193ed 100644
--- a/drivers/soc/renesas/r8a77970-sysc.c
+++ b/drivers/soc/renesas/r8a77970-sysc.c
@@ -25,12 +25,12 @@ static const struct rcar_sysc_area r8a77970_areas[] __initconst = {
PD_CPU_NOCR },
{ "cr7", 0x240, 0, R8A77970_PD_CR7, R8A77970_PD_ALWAYS_ON },
{ "a3ir", 0x180, 0, R8A77970_PD_A3IR, R8A77970_PD_ALWAYS_ON },
- { "a2ir0", 0x400, 0, R8A77970_PD_A2IR0, R8A77970_PD_ALWAYS_ON },
- { "a2ir1", 0x400, 1, R8A77970_PD_A2IR1, R8A77970_PD_A2IR0 },
- { "a2ir2", 0x400, 2, R8A77970_PD_A2IR2, R8A77970_PD_A2IR0 },
- { "a2ir3", 0x400, 3, R8A77970_PD_A2IR3, R8A77970_PD_A2IR0 },
- { "a2sc0", 0x400, 4, R8A77970_PD_A2SC0, R8A77970_PD_ALWAYS_ON },
- { "a2sc1", 0x400, 5, R8A77970_PD_A2SC1, R8A77970_PD_A2SC0 },
+ { "a2ir0", 0x400, 0, R8A77970_PD_A2IR0, R8A77970_PD_A3IR },
+ { "a2ir1", 0x400, 1, R8A77970_PD_A2IR1, R8A77970_PD_A3IR },
+ { "a2ir2", 0x400, 2, R8A77970_PD_A2IR2, R8A77970_PD_A3IR },
+ { "a2ir3", 0x400, 3, R8A77970_PD_A2IR3, R8A77970_PD_A3IR },
+ { "a2sc0", 0x400, 4, R8A77970_PD_A2SC0, R8A77970_PD_A3IR },
+ { "a2sc1", 0x400, 5, R8A77970_PD_A2SC1, R8A77970_PD_A3IR },
};
const struct rcar_sysc_info r8a77970_sysc_info __initconst = {
diff --git a/drivers/soc/renesas/r8a77980-sysc.c b/drivers/soc/renesas/r8a77980-sysc.c
new file mode 100644
index 000000000000..9265fb525ef3
--- /dev/null
+++ b/drivers/soc/renesas/r8a77980-sysc.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Renesas R-Car V3H System Controller
+ *
+ * Copyright (C) 2018 Renesas Electronics Corp.
+ * Copyright (C) 2018 Cogent Embedded, Inc.
+ */
+
+#include <linux/bug.h>
+#include <linux/kernel.h>
+
+#include <dt-bindings/power/r8a77980-sysc.h>
+
+#include "rcar-sysc.h"
+
+static const struct rcar_sysc_area r8a77980_areas[] __initconst = {
+ { "always-on", 0, 0, R8A77980_PD_ALWAYS_ON, -1, PD_ALWAYS_ON },
+ { "ca53-scu", 0x140, 0, R8A77980_PD_CA53_SCU, R8A77980_PD_ALWAYS_ON,
+ PD_SCU },
+ { "ca53-cpu0", 0x200, 0, R8A77980_PD_CA53_CPU0, R8A77980_PD_CA53_SCU,
+ PD_CPU_NOCR },
+ { "ca53-cpu1", 0x200, 1, R8A77980_PD_CA53_CPU1, R8A77980_PD_CA53_SCU,
+ PD_CPU_NOCR },
+ { "ca53-cpu2", 0x200, 2, R8A77980_PD_CA53_CPU2, R8A77980_PD_CA53_SCU,
+ PD_CPU_NOCR },
+ { "ca53-cpu3", 0x200, 3, R8A77980_PD_CA53_CPU3, R8A77980_PD_CA53_SCU,
+ PD_CPU_NOCR },
+ { "cr7", 0x240, 0, R8A77980_PD_CR7, R8A77980_PD_ALWAYS_ON },
+ { "a3ir", 0x180, 0, R8A77980_PD_A3IR, R8A77980_PD_ALWAYS_ON },
+ { "a2ir0", 0x400, 0, R8A77980_PD_A2IR0, R8A77980_PD_A3IR },
+ { "a2ir1", 0x400, 1, R8A77980_PD_A2IR1, R8A77980_PD_A3IR },
+ { "a2ir2", 0x400, 2, R8A77980_PD_A2IR2, R8A77980_PD_A3IR },
+ { "a2ir3", 0x400, 3, R8A77980_PD_A2IR3, R8A77980_PD_A3IR },
+ { "a2ir4", 0x400, 4, R8A77980_PD_A2IR4, R8A77980_PD_A3IR },
+ { "a2ir5", 0x400, 5, R8A77980_PD_A2IR5, R8A77980_PD_A3IR },
+ { "a2sc0", 0x400, 6, R8A77980_PD_A2SC0, R8A77980_PD_A3IR },
+ { "a2sc1", 0x400, 7, R8A77980_PD_A2SC1, R8A77980_PD_A3IR },
+ { "a2sc2", 0x400, 8, R8A77980_PD_A2SC2, R8A77980_PD_A3IR },
+ { "a2sc3", 0x400, 9, R8A77980_PD_A2SC3, R8A77980_PD_A3IR },
+ { "a2sc4", 0x400, 10, R8A77980_PD_A2SC4, R8A77980_PD_A3IR },
+ { "a2pd0", 0x400, 11, R8A77980_PD_A2PD0, R8A77980_PD_A3IR },
+ { "a2pd1", 0x400, 12, R8A77980_PD_A2PD1, R8A77980_PD_A3IR },
+ { "a2cn", 0x400, 13, R8A77980_PD_A2CN, R8A77980_PD_A3IR },
+ { "a3vip", 0x2c0, 0, R8A77980_PD_A3VIP, R8A77980_PD_ALWAYS_ON },
+ { "a3vip1", 0x300, 0, R8A77980_PD_A3VIP1, R8A77980_PD_A3VIP },
+ { "a3vip2", 0x280, 0, R8A77980_PD_A3VIP2, R8A77980_PD_A3VIP },
+};
+
+const struct rcar_sysc_info r8a77980_sysc_info __initconst = {
+ .areas = r8a77980_areas,
+ .num_areas = ARRAY_SIZE(r8a77980_areas),
+};
diff --git a/drivers/soc/renesas/rcar-rst.c b/drivers/soc/renesas/rcar-rst.c
index 3316b028f231..8e9cb7996ab0 100644
--- a/drivers/soc/renesas/rcar-rst.c
+++ b/drivers/soc/renesas/rcar-rst.c
@@ -13,8 +13,18 @@
#include <linux/of_address.h>
#include <linux/soc/renesas/rcar-rst.h>
+#define WDTRSTCR_RESET 0xA55A0002
+#define WDTRSTCR 0x0054
+
+static int rcar_rst_enable_wdt_reset(void __iomem *base)
+{
+ iowrite32(WDTRSTCR_RESET, base + WDTRSTCR);
+ return 0;
+}
+
struct rst_config {
- unsigned int modemr; /* Mode Monitoring Register Offset */
+ unsigned int modemr; /* Mode Monitoring Register Offset */
+ int (*configure)(void *base); /* Platform specific configuration */
};
static const struct rst_config rcar_rst_gen1 __initconst = {
@@ -23,6 +33,11 @@ static const struct rst_config rcar_rst_gen1 __initconst = {
static const struct rst_config rcar_rst_gen2 __initconst = {
.modemr = 0x60,
+ .configure = rcar_rst_enable_wdt_reset,
+};
+
+static const struct rst_config rcar_rst_gen3 __initconst = {
+ .modemr = 0x60,
};
static const struct of_device_id rcar_rst_matches[] __initconst = {
@@ -38,11 +53,13 @@ static const struct of_device_id rcar_rst_matches[] __initconst = {
{ .compatible = "renesas,r8a7792-rst", .data = &rcar_rst_gen2 },
{ .compatible = "renesas,r8a7793-rst", .data = &rcar_rst_gen2 },
{ .compatible = "renesas,r8a7794-rst", .data = &rcar_rst_gen2 },
- /* R-Car Gen3 is handled like R-Car Gen2 */
- { .compatible = "renesas,r8a7795-rst", .data = &rcar_rst_gen2 },
- { .compatible = "renesas,r8a7796-rst", .data = &rcar_rst_gen2 },
- { .compatible = "renesas,r8a77970-rst", .data = &rcar_rst_gen2 },
- { .compatible = "renesas,r8a77995-rst", .data = &rcar_rst_gen2 },
+ /* R-Car Gen3 */
+ { .compatible = "renesas,r8a7795-rst", .data = &rcar_rst_gen3 },
+ { .compatible = "renesas,r8a7796-rst", .data = &rcar_rst_gen3 },
+ { .compatible = "renesas,r8a77965-rst", .data = &rcar_rst_gen3 },
+ { .compatible = "renesas,r8a77970-rst", .data = &rcar_rst_gen3 },
+ { .compatible = "renesas,r8a77980-rst", .data = &rcar_rst_gen3 },
+ { .compatible = "renesas,r8a77995-rst", .data = &rcar_rst_gen3 },
{ /* sentinel */ }
};
@@ -71,6 +88,14 @@ static int __init rcar_rst_init(void)
rcar_rst_base = base;
cfg = match->data;
saved_mode = ioread32(base + cfg->modemr);
+ if (cfg->configure) {
+ error = cfg->configure(base);
+ if (error) {
+ pr_warn("%pOF: Cannot run SoC specific configuration\n",
+ np);
+ goto out_put;
+ }
+ }
pr_debug("%pOF: MODE = 0x%08x\n", np, saved_mode);
diff --git a/drivers/soc/renesas/rcar-sysc.c b/drivers/soc/renesas/rcar-sysc.c
index 52c25a5e2646..faf20e719361 100644
--- a/drivers/soc/renesas/rcar-sysc.c
+++ b/drivers/soc/renesas/rcar-sysc.c
@@ -254,7 +254,7 @@ finalize:
pm_genpd_init(genpd, gov, false);
}
-static const struct of_device_id rcar_sysc_matches[] = {
+static const struct of_device_id rcar_sysc_matches[] __initconst = {
#ifdef CONFIG_SYSC_R8A7743
{ .compatible = "renesas,r8a7743-sysc", .data = &r8a7743_sysc_info },
#endif
@@ -284,9 +284,15 @@ static const struct of_device_id rcar_sysc_matches[] = {
#ifdef CONFIG_SYSC_R8A7796
{ .compatible = "renesas,r8a7796-sysc", .data = &r8a7796_sysc_info },
#endif
+#ifdef CONFIG_SYSC_R8A77965
+ { .compatible = "renesas,r8a77965-sysc", .data = &r8a77965_sysc_info },
+#endif
#ifdef CONFIG_SYSC_R8A77970
{ .compatible = "renesas,r8a77970-sysc", .data = &r8a77970_sysc_info },
#endif
+#ifdef CONFIG_SYSC_R8A77980
+ { .compatible = "renesas,r8a77980-sysc", .data = &r8a77980_sysc_info },
+#endif
#ifdef CONFIG_SYSC_R8A77995
{ .compatible = "renesas,r8a77995-sysc", .data = &r8a77995_sysc_info },
#endif
diff --git a/drivers/soc/renesas/rcar-sysc.h b/drivers/soc/renesas/rcar-sysc.h
index 9d9daf9eb91b..dcdc9ec8eba7 100644
--- a/drivers/soc/renesas/rcar-sysc.h
+++ b/drivers/soc/renesas/rcar-sysc.h
@@ -58,7 +58,9 @@ extern const struct rcar_sysc_info r8a7792_sysc_info;
extern const struct rcar_sysc_info r8a7794_sysc_info;
extern const struct rcar_sysc_info r8a7795_sysc_info;
extern const struct rcar_sysc_info r8a7796_sysc_info;
+extern const struct rcar_sysc_info r8a77965_sysc_info;
extern const struct rcar_sysc_info r8a77970_sysc_info;
+extern const struct rcar_sysc_info r8a77980_sysc_info;
extern const struct rcar_sysc_info r8a77995_sysc_info;
diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c
index 926b7fd6db2d..ea71c413c926 100644
--- a/drivers/soc/renesas/renesas-soc.c
+++ b/drivers/soc/renesas/renesas-soc.c
@@ -144,11 +144,21 @@ static const struct renesas_soc soc_rcar_m3_w __initconst __maybe_unused = {
.id = 0x52,
};
+static const struct renesas_soc soc_rcar_m3_n __initconst __maybe_unused = {
+ .family = &fam_rcar_gen3,
+ .id = 0x55,
+};
+
static const struct renesas_soc soc_rcar_v3m __initconst __maybe_unused = {
.family = &fam_rcar_gen3,
.id = 0x54,
};
+static const struct renesas_soc soc_rcar_v3h __initconst __maybe_unused = {
+ .family = &fam_rcar_gen3,
+ .id = 0x56,
+};
+
static const struct renesas_soc soc_rcar_d3 __initconst __maybe_unused = {
.family = &fam_rcar_gen3,
.id = 0x58,
@@ -209,9 +219,15 @@ static const struct of_device_id renesas_socs[] __initconst = {
#ifdef CONFIG_ARCH_R8A7796
{ .compatible = "renesas,r8a7796", .data = &soc_rcar_m3_w },
#endif
+#ifdef CONFIG_ARCH_R8A77965
+ { .compatible = "renesas,r8a77965", .data = &soc_rcar_m3_n },
+#endif
#ifdef CONFIG_ARCH_R8A77970
{ .compatible = "renesas,r8a77970", .data = &soc_rcar_v3m },
#endif
+#ifdef CONFIG_ARCH_R8A77980
+ { .compatible = "renesas,r8a77980", .data = &soc_rcar_v3h },
+#endif
#ifdef CONFIG_ARCH_R8A77995
{ .compatible = "renesas,r8a77995", .data = &soc_rcar_d3 },
#endif
diff --git a/drivers/soc/rockchip/grf.c b/drivers/soc/rockchip/grf.c
index 15e71fd6c513..96882ffde67e 100644
--- a/drivers/soc/rockchip/grf.c
+++ b/drivers/soc/rockchip/grf.c
@@ -43,6 +43,28 @@ static const struct rockchip_grf_info rk3036_grf __initconst = {
.num_values = ARRAY_SIZE(rk3036_defaults),
};
+#define RK3128_GRF_SOC_CON0 0x140
+
+static const struct rockchip_grf_value rk3128_defaults[] __initconst = {
+ { "jtag switching", RK3128_GRF_SOC_CON0, HIWORD_UPDATE(0, 1, 8) },
+};
+
+static const struct rockchip_grf_info rk3128_grf __initconst = {
+ .values = rk3128_defaults,
+ .num_values = ARRAY_SIZE(rk3128_defaults),
+};
+
+#define RK3228_GRF_SOC_CON6 0x418
+
+static const struct rockchip_grf_value rk3228_defaults[] __initconst = {
+ { "jtag switching", RK3228_GRF_SOC_CON6, HIWORD_UPDATE(0, 1, 8) },
+};
+
+static const struct rockchip_grf_info rk3228_grf __initconst = {
+ .values = rk3228_defaults,
+ .num_values = ARRAY_SIZE(rk3228_defaults),
+};
+
#define RK3288_GRF_SOC_CON0 0x244
static const struct rockchip_grf_value rk3288_defaults[] __initconst = {
@@ -92,6 +114,12 @@ static const struct of_device_id rockchip_grf_dt_match[] __initconst = {
.compatible = "rockchip,rk3036-grf",
.data = (void *)&rk3036_grf,
}, {
+ .compatible = "rockchip,rk3128-grf",
+ .data = (void *)&rk3128_grf,
+ }, {
+ .compatible = "rockchip,rk3228-grf",
+ .data = (void *)&rk3228_grf,
+ }, {
.compatible = "rockchip,rk3288-grf",
.data = (void *)&rk3288_grf,
}, {
diff --git a/drivers/soc/rockchip/pm_domains.c b/drivers/soc/rockchip/pm_domains.c
index 5c342167b9db..53efc386b1ad 100644
--- a/drivers/soc/rockchip/pm_domains.c
+++ b/drivers/soc/rockchip/pm_domains.c
@@ -67,7 +67,7 @@ struct rockchip_pm_domain {
struct regmap **qos_regmap;
u32 *qos_save_regs[MAX_QOS_REGS_NUM];
int num_clks;
- struct clk *clks[];
+ struct clk_bulk_data *clks;
};
struct rockchip_pmu {
@@ -274,13 +274,18 @@ static void rockchip_do_pmu_set_power_domain(struct rockchip_pm_domain *pd,
static int rockchip_pd_power(struct rockchip_pm_domain *pd, bool power_on)
{
- int i;
+ struct rockchip_pmu *pmu = pd->pmu;
+ int ret;
- mutex_lock(&pd->pmu->mutex);
+ mutex_lock(&pmu->mutex);
if (rockchip_pmu_domain_is_on(pd) != power_on) {
- for (i = 0; i < pd->num_clks; i++)
- clk_enable(pd->clks[i]);
+ ret = clk_bulk_enable(pd->num_clks, pd->clks);
+ if (ret < 0) {
+ dev_err(pmu->dev, "failed to enable clocks\n");
+ mutex_unlock(&pmu->mutex);
+ return ret;
+ }
if (!power_on) {
rockchip_pmu_save_qos(pd);
@@ -298,11 +303,10 @@ static int rockchip_pd_power(struct rockchip_pm_domain *pd, bool power_on)
rockchip_pmu_restore_qos(pd);
}
- for (i = pd->num_clks - 1; i >= 0; i--)
- clk_disable(pd->clks[i]);
+ clk_bulk_disable(pd->num_clks, pd->clks);
}
- mutex_unlock(&pd->pmu->mutex);
+ mutex_unlock(&pmu->mutex);
return 0;
}
@@ -364,8 +368,6 @@ static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
const struct rockchip_domain_info *pd_info;
struct rockchip_pm_domain *pd;
struct device_node *qos_node;
- struct clk *clk;
- int clk_cnt;
int i, j;
u32 id;
int error;
@@ -391,41 +393,41 @@ static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
return -EINVAL;
}
- clk_cnt = of_count_phandle_with_args(node, "clocks", "#clock-cells");
- pd = devm_kzalloc(pmu->dev,
- sizeof(*pd) + clk_cnt * sizeof(pd->clks[0]),
- GFP_KERNEL);
+ pd = devm_kzalloc(pmu->dev, sizeof(*pd), GFP_KERNEL);
if (!pd)
return -ENOMEM;
pd->info = pd_info;
pd->pmu = pmu;
- for (i = 0; i < clk_cnt; i++) {
- clk = of_clk_get(node, i);
- if (IS_ERR(clk)) {
- error = PTR_ERR(clk);
+ pd->num_clks = of_count_phandle_with_args(node, "clocks",
+ "#clock-cells");
+ if (pd->num_clks > 0) {
+ pd->clks = devm_kcalloc(pmu->dev, pd->num_clks,
+ sizeof(*pd->clks), GFP_KERNEL);
+ if (!pd->clks)
+ return -ENOMEM;
+ } else {
+ dev_dbg(pmu->dev, "%s: doesn't have clocks: %d\n",
+ node->name, pd->num_clks);
+ pd->num_clks = 0;
+ }
+
+ for (i = 0; i < pd->num_clks; i++) {
+ pd->clks[i].clk = of_clk_get(node, i);
+ if (IS_ERR(pd->clks[i].clk)) {
+ error = PTR_ERR(pd->clks[i].clk);
dev_err(pmu->dev,
"%s: failed to get clk at index %d: %d\n",
node->name, i, error);
- goto err_out;
- }
-
- error = clk_prepare(clk);
- if (error) {
- dev_err(pmu->dev,
- "%s: failed to prepare clk %pC (index %d): %d\n",
- node->name, clk, i, error);
- clk_put(clk);
- goto err_out;
+ return error;
}
-
- pd->clks[pd->num_clks++] = clk;
-
- dev_dbg(pmu->dev, "added clock '%pC' to domain '%s'\n",
- clk, node->name);
}
+ error = clk_bulk_prepare(pd->num_clks, pd->clks);
+ if (error)
+ goto err_put_clocks;
+
pd->num_qos = of_count_phandle_with_args(node, "pm_qos",
NULL);
@@ -435,7 +437,7 @@ static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
GFP_KERNEL);
if (!pd->qos_regmap) {
error = -ENOMEM;
- goto err_out;
+ goto err_unprepare_clocks;
}
for (j = 0; j < MAX_QOS_REGS_NUM; j++) {
@@ -445,7 +447,7 @@ static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
GFP_KERNEL);
if (!pd->qos_save_regs[j]) {
error = -ENOMEM;
- goto err_out;
+ goto err_unprepare_clocks;
}
}
@@ -453,13 +455,13 @@ static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
qos_node = of_parse_phandle(node, "pm_qos", j);
if (!qos_node) {
error = -ENODEV;
- goto err_out;
+ goto err_unprepare_clocks;
}
pd->qos_regmap[j] = syscon_node_to_regmap(qos_node);
if (IS_ERR(pd->qos_regmap[j])) {
error = -ENODEV;
of_node_put(qos_node);
- goto err_out;
+ goto err_unprepare_clocks;
}
of_node_put(qos_node);
}
@@ -470,7 +472,7 @@ static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
dev_err(pmu->dev,
"failed to power on domain '%s': %d\n",
node->name, error);
- goto err_out;
+ goto err_unprepare_clocks;
}
pd->genpd.name = node->name;
@@ -486,17 +488,16 @@ static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
pmu->genpd_data.domains[id] = &pd->genpd;
return 0;
-err_out:
- while (--i >= 0) {
- clk_unprepare(pd->clks[i]);
- clk_put(pd->clks[i]);
- }
+err_unprepare_clocks:
+ clk_bulk_unprepare(pd->num_clks, pd->clks);
+err_put_clocks:
+ clk_bulk_put(pd->num_clks, pd->clks);
return error;
}
static void rockchip_pm_remove_one_domain(struct rockchip_pm_domain *pd)
{
- int i, ret;
+ int ret;
/*
* We're in the error cleanup already, so we only complain,
@@ -507,10 +508,8 @@ static void rockchip_pm_remove_one_domain(struct rockchip_pm_domain *pd)
dev_err(pd->pmu->dev, "failed to remove domain '%s' : %d - state may be inconsistent\n",
pd->genpd.name, ret);
- for (i = 0; i < pd->num_clks; i++) {
- clk_unprepare(pd->clks[i]);
- clk_put(pd->clks[i]);
- }
+ clk_bulk_unprepare(pd->num_clks, pd->clks);
+ clk_bulk_put(pd->num_clks, pd->clks);
/* protect the zeroing of pm->num_clks */
mutex_lock(&pd->pmu->mutex);
diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c
index f56adbd9fb8b..d34ca201b8b7 100644
--- a/drivers/soc/samsung/exynos-pmu.c
+++ b/drivers/soc/samsung/exynos-pmu.c
@@ -85,10 +85,14 @@ static const struct of_device_id exynos_pmu_of_device_ids[] = {
.compatible = "samsung,exynos5250-pmu",
.data = exynos_pmu_data_arm_ptr(exynos5250_pmu_data),
}, {
+ .compatible = "samsung,exynos5410-pmu",
+ }, {
.compatible = "samsung,exynos5420-pmu",
.data = exynos_pmu_data_arm_ptr(exynos5420_pmu_data),
}, {
.compatible = "samsung,exynos5433-pmu",
+ }, {
+ .compatible = "samsung,exynos7-pmu",
},
{ /*sentinel*/ },
};
@@ -126,6 +130,9 @@ static int exynos_pmu_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, pmu_context);
+ if (devm_of_platform_populate(dev))
+ dev_err(dev, "Error populating children, reboot and poweroff might not work properly\n");
+
dev_dbg(dev, "Exynos PMU Driver probe done\n");
return 0;
}
diff --git a/drivers/soc/samsung/pm_domains.c b/drivers/soc/samsung/pm_domains.c
index b6a436594a19..caf45cf7aa8e 100644
--- a/drivers/soc/samsung/pm_domains.c
+++ b/drivers/soc/samsung/pm_domains.c
@@ -147,6 +147,12 @@ static __init const char *exynos_get_domain_name(struct device_node *node)
return kstrdup_const(name, GFP_KERNEL);
}
+static const char *soc_force_no_clk[] = {
+ "samsung,exynos5250-clock",
+ "samsung,exynos5420-clock",
+ "samsung,exynos5800-clock",
+};
+
static __init int exynos4_pm_init_power_domain(void)
{
struct device_node *np;
@@ -183,6 +189,11 @@ static __init int exynos4_pm_init_power_domain(void)
pd->pd.power_on = exynos_pd_power_on;
pd->local_pwr_cfg = pm_domain_cfg->local_pwr_cfg;
+ for (i = 0; i < ARRAY_SIZE(soc_force_no_clk); i++)
+ if (of_find_compatible_node(NULL, NULL,
+ soc_force_no_clk[i]))
+ goto no_clk;
+
for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) {
char clk_name[8];
diff --git a/drivers/soc/tegra/Kconfig b/drivers/soc/tegra/Kconfig
index 89ebe22a3e27..fe4481676da6 100644
--- a/drivers/soc/tegra/Kconfig
+++ b/drivers/soc/tegra/Kconfig
@@ -104,6 +104,16 @@ config ARCH_TEGRA_186_SOC
multi-format support, ISP for image capture processing and BPMP for
power management.
+config ARCH_TEGRA_194_SOC
+ bool "NVIDIA Tegra194 SoC"
+ select MAILBOX
+ select TEGRA_BPMP
+ select TEGRA_HSP_MBOX
+ select TEGRA_IVC
+ select SOC_TEGRA_PMC
+ help
+ Enable support for the NVIDIA Tegra194 SoC.
+
endif
endif
diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index ce62a47a6647..d9fcdb592b39 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -127,8 +127,7 @@ struct tegra_powergate {
unsigned int id;
struct clk **clks;
unsigned int num_clks;
- struct reset_control **resets;
- unsigned int num_resets;
+ struct reset_control *reset;
};
struct tegra_io_pad_soc {
@@ -153,6 +152,7 @@ struct tegra_pmc_soc {
bool has_tsense_reset;
bool has_gpu_clamps;
+ bool needs_mbist_war;
const struct tegra_io_pad_soc *io_pads;
unsigned int num_io_pads;
@@ -368,31 +368,8 @@ out:
return err;
}
-static int tegra_powergate_reset_assert(struct tegra_powergate *pg)
+int __weak tegra210_clk_handle_mbist_war(unsigned int id)
{
- unsigned int i;
- int err;
-
- for (i = 0; i < pg->num_resets; i++) {
- err = reset_control_assert(pg->resets[i]);
- if (err)
- return err;
- }
-
- return 0;
-}
-
-static int tegra_powergate_reset_deassert(struct tegra_powergate *pg)
-{
- unsigned int i;
- int err;
-
- for (i = 0; i < pg->num_resets; i++) {
- err = reset_control_deassert(pg->resets[i]);
- if (err)
- return err;
- }
-
return 0;
}
@@ -401,7 +378,7 @@ static int tegra_powergate_power_up(struct tegra_powergate *pg,
{
int err;
- err = tegra_powergate_reset_assert(pg);
+ err = reset_control_assert(pg->reset);
if (err)
return err;
@@ -425,12 +402,17 @@ static int tegra_powergate_power_up(struct tegra_powergate *pg,
usleep_range(10, 20);
- err = tegra_powergate_reset_deassert(pg);
+ err = reset_control_deassert(pg->reset);
if (err)
goto powergate_off;
usleep_range(10, 20);
+ if (pg->pmc->soc->needs_mbist_war)
+ err = tegra210_clk_handle_mbist_war(pg->id);
+ if (err)
+ goto disable_clks;
+
if (disable_clocks)
tegra_powergate_disable_clocks(pg);
@@ -456,7 +438,7 @@ static int tegra_powergate_power_down(struct tegra_powergate *pg)
usleep_range(10, 20);
- err = tegra_powergate_reset_assert(pg);
+ err = reset_control_assert(pg->reset);
if (err)
goto disable_clks;
@@ -475,7 +457,7 @@ static int tegra_powergate_power_down(struct tegra_powergate *pg)
assert_resets:
tegra_powergate_enable_clocks(pg);
usleep_range(10, 20);
- tegra_powergate_reset_deassert(pg);
+ reset_control_deassert(pg->reset);
usleep_range(10, 20);
disable_clks:
@@ -586,8 +568,8 @@ int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk,
pg.id = id;
pg.clks = &clk;
pg.num_clks = 1;
- pg.resets = &rst;
- pg.num_resets = 1;
+ pg.reset = rst;
+ pg.pmc = pmc;
err = tegra_powergate_power_up(&pg, false);
if (err)
@@ -775,45 +757,22 @@ err:
static int tegra_powergate_of_get_resets(struct tegra_powergate *pg,
struct device_node *np, bool off)
{
- struct reset_control *rst;
- unsigned int i, count;
int err;
- count = of_count_phandle_with_args(np, "resets", "#reset-cells");
- if (count == 0)
- return -ENODEV;
-
- pg->resets = kcalloc(count, sizeof(rst), GFP_KERNEL);
- if (!pg->resets)
- return -ENOMEM;
-
- for (i = 0; i < count; i++) {
- pg->resets[i] = of_reset_control_get_by_index(np, i);
- if (IS_ERR(pg->resets[i])) {
- err = PTR_ERR(pg->resets[i]);
- goto error;
- }
-
- if (off)
- err = reset_control_assert(pg->resets[i]);
- else
- err = reset_control_deassert(pg->resets[i]);
-
- if (err) {
- reset_control_put(pg->resets[i]);
- goto error;
- }
+ pg->reset = of_reset_control_array_get_exclusive(np);
+ if (IS_ERR(pg->reset)) {
+ err = PTR_ERR(pg->reset);
+ pr_err("failed to get device resets: %d\n", err);
+ return err;
}
- pg->num_resets = count;
-
- return 0;
-
-error:
- while (i--)
- reset_control_put(pg->resets[i]);
+ if (off)
+ err = reset_control_assert(pg->reset);
+ else
+ err = reset_control_deassert(pg->reset);
- kfree(pg->resets);
+ if (err)
+ reset_control_put(pg->reset);
return err;
}
@@ -905,10 +864,7 @@ remove_genpd:
pm_genpd_remove(&pg->genpd);
remove_resets:
- while (pg->num_resets--)
- reset_control_put(pg->resets[pg->num_resets]);
-
- kfree(pg->resets);
+ reset_control_put(pg->reset);
remove_clks:
while (pg->num_clks--)
@@ -1815,6 +1771,7 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = {
.cpu_powergates = tegra210_cpu_powergates,
.has_tsense_reset = true,
.has_gpu_clamps = true,
+ .needs_mbist_war = true,
.num_io_pads = ARRAY_SIZE(tegra210_io_pads),
.io_pads = tegra210_io_pads,
.regs = &tegra20_pmc_regs,
@@ -1920,6 +1877,7 @@ static const struct tegra_pmc_soc tegra186_pmc_soc = {
};
static const struct of_device_id tegra_pmc_match[] = {
+ { .compatible = "nvidia,tegra194-pmc", .data = &tegra186_pmc_soc },
{ .compatible = "nvidia,tegra186-pmc", .data = &tegra186_pmc_soc },
{ .compatible = "nvidia,tegra210-pmc", .data = &tegra210_pmc_soc },
{ .compatible = "nvidia,tegra132-pmc", .data = &tegra124_pmc_soc },
diff --git a/drivers/soc/ti/Kconfig b/drivers/soc/ti/Kconfig
index 39e152abe6b9..92770d84a288 100644
--- a/drivers/soc/ti/Kconfig
+++ b/drivers/soc/ti/Kconfig
@@ -28,6 +28,15 @@ config KEYSTONE_NAVIGATOR_DMA
If unsure, say N.
+config AMX3_PM
+ tristate "AMx3 Power Management"
+ depends on SOC_AM33XX || SOC_AM43XX
+ depends on WKUP_M3_IPC && TI_EMIF_SRAM && SRAM
+ help
+ Enable power management on AM335x and AM437x. Required for suspend to mem
+ and standby states on both AM335x and AM437x platforms and for deeper cpuidle
+ c-states on AM335x.
+
config WKUP_M3_IPC
tristate "TI AMx3 Wkup-M3 IPC Driver"
depends on WKUP_M3_RPROC
diff --git a/drivers/soc/ti/Makefile b/drivers/soc/ti/Makefile
index 8e205287f120..a22edc0b258a 100644
--- a/drivers/soc/ti/Makefile
+++ b/drivers/soc/ti/Makefile
@@ -5,5 +5,6 @@
obj-$(CONFIG_KEYSTONE_NAVIGATOR_QMSS) += knav_qmss.o
knav_qmss-y := knav_qmss_queue.o knav_qmss_acc.o
obj-$(CONFIG_KEYSTONE_NAVIGATOR_DMA) += knav_dma.o
+obj-$(CONFIG_AMX3_PM) += pm33xx.o
obj-$(CONFIG_WKUP_M3_IPC) += wkup_m3_ipc.o
obj-$(CONFIG_TI_SCI_PM_DOMAINS) += ti_sci_pm_domains.o
diff --git a/drivers/soc/ti/pm33xx.c b/drivers/soc/ti/pm33xx.c
new file mode 100644
index 000000000000..652739c7f718
--- /dev/null
+++ b/drivers/soc/ti/pm33xx.c
@@ -0,0 +1,349 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AM33XX Power Management Routines
+ *
+ * Copyright (C) 2012-2018 Texas Instruments Incorporated - http://www.ti.com/
+ * Vaibhav Bedia, Dave Gerlach
+ */
+
+#include <linux/cpu.h>
+#include <linux/err.h>
+#include <linux/genalloc.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_data/pm33xx.h>
+#include <linux/platform_device.h>
+#include <linux/sizes.h>
+#include <linux/sram.h>
+#include <linux/suspend.h>
+#include <linux/ti-emif-sram.h>
+#include <linux/wkup_m3_ipc.h>
+
+#include <asm/proc-fns.h>
+#include <asm/suspend.h>
+#include <asm/system_misc.h>
+
+#define AMX3_PM_SRAM_SYMBOL_OFFSET(sym) ((unsigned long)(sym) - \
+ (unsigned long)pm_sram->do_wfi)
+
+static int (*am33xx_do_wfi_sram)(unsigned long unused);
+static phys_addr_t am33xx_do_wfi_sram_phys;
+
+static struct gen_pool *sram_pool, *sram_pool_data;
+static unsigned long ocmcram_location, ocmcram_location_data;
+
+static struct am33xx_pm_platform_data *pm_ops;
+static struct am33xx_pm_sram_addr *pm_sram;
+
+static struct device *pm33xx_dev;
+static struct wkup_m3_ipc *m3_ipc;
+
+static u32 sram_suspend_address(unsigned long addr)
+{
+ return ((unsigned long)am33xx_do_wfi_sram +
+ AMX3_PM_SRAM_SYMBOL_OFFSET(addr));
+}
+
+#ifdef CONFIG_SUSPEND
+static int am33xx_pm_suspend(suspend_state_t suspend_state)
+{
+ int i, ret = 0;
+
+ ret = pm_ops->soc_suspend((unsigned long)suspend_state,
+ am33xx_do_wfi_sram);
+
+ if (ret) {
+ dev_err(pm33xx_dev, "PM: Kernel suspend failure\n");
+ } else {
+ i = m3_ipc->ops->request_pm_status(m3_ipc);
+
+ switch (i) {
+ case 0:
+ dev_info(pm33xx_dev,
+ "PM: Successfully put all powerdomains to target state\n");
+ break;
+ case 1:
+ dev_err(pm33xx_dev,
+ "PM: Could not transition all powerdomains to target state\n");
+ ret = -1;
+ break;
+ default:
+ dev_err(pm33xx_dev,
+ "PM: CM3 returned unknown result = %d\n", i);
+ ret = -1;
+ }
+ }
+
+ return ret;
+}
+
+static int am33xx_pm_enter(suspend_state_t suspend_state)
+{
+ int ret = 0;
+
+ switch (suspend_state) {
+ case PM_SUSPEND_MEM:
+ case PM_SUSPEND_STANDBY:
+ ret = am33xx_pm_suspend(suspend_state);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int am33xx_pm_begin(suspend_state_t state)
+{
+ int ret = -EINVAL;
+
+ switch (state) {
+ case PM_SUSPEND_MEM:
+ ret = m3_ipc->ops->prepare_low_power(m3_ipc, WKUP_M3_DEEPSLEEP);
+ break;
+ case PM_SUSPEND_STANDBY:
+ ret = m3_ipc->ops->prepare_low_power(m3_ipc, WKUP_M3_STANDBY);
+ break;
+ }
+
+ return ret;
+}
+
+static void am33xx_pm_end(void)
+{
+ m3_ipc->ops->finish_low_power(m3_ipc);
+}
+
+static int am33xx_pm_valid(suspend_state_t state)
+{
+ switch (state) {
+ case PM_SUSPEND_STANDBY:
+ case PM_SUSPEND_MEM:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static const struct platform_suspend_ops am33xx_pm_ops = {
+ .begin = am33xx_pm_begin,
+ .end = am33xx_pm_end,
+ .enter = am33xx_pm_enter,
+ .valid = am33xx_pm_valid,
+};
+#endif /* CONFIG_SUSPEND */
+
+static void am33xx_pm_set_ipc_ops(void)
+{
+ u32 resume_address;
+ int temp;
+
+ temp = ti_emif_get_mem_type();
+ if (temp < 0) {
+ dev_err(pm33xx_dev, "PM: Cannot determine memory type, no PM available\n");
+ return;
+ }
+ m3_ipc->ops->set_mem_type(m3_ipc, temp);
+
+ /* Physical resume address to be used by ROM code */
+ resume_address = am33xx_do_wfi_sram_phys +
+ *pm_sram->resume_offset + 0x4;
+
+ m3_ipc->ops->set_resume_address(m3_ipc, (void *)resume_address);
+}
+
+static void am33xx_pm_free_sram(void)
+{
+ gen_pool_free(sram_pool, ocmcram_location, *pm_sram->do_wfi_sz);
+ gen_pool_free(sram_pool_data, ocmcram_location_data,
+ sizeof(struct am33xx_pm_ro_sram_data));
+}
+
+/*
+ * Push the minimal suspend-resume code to SRAM
+ */
+static int am33xx_pm_alloc_sram(void)
+{
+ struct device_node *np;
+ int ret = 0;
+
+ np = of_find_compatible_node(NULL, NULL, "ti,omap3-mpu");
+ if (!np) {
+ np = of_find_compatible_node(NULL, NULL, "ti,omap4-mpu");
+ if (!np) {
+ dev_err(pm33xx_dev, "PM: %s: Unable to find device node for mpu\n",
+ __func__);
+ return -ENODEV;
+ }
+ }
+
+ sram_pool = of_gen_pool_get(np, "pm-sram", 0);
+ if (!sram_pool) {
+ dev_err(pm33xx_dev, "PM: %s: Unable to get sram pool for ocmcram\n",
+ __func__);
+ ret = -ENODEV;
+ goto mpu_put_node;
+ }
+
+ sram_pool_data = of_gen_pool_get(np, "pm-sram", 1);
+ if (!sram_pool_data) {
+ dev_err(pm33xx_dev, "PM: %s: Unable to get sram data pool for ocmcram\n",
+ __func__);
+ ret = -ENODEV;
+ goto mpu_put_node;
+ }
+
+ ocmcram_location = gen_pool_alloc(sram_pool, *pm_sram->do_wfi_sz);
+ if (!ocmcram_location) {
+ dev_err(pm33xx_dev, "PM: %s: Unable to allocate memory from ocmcram\n",
+ __func__);
+ ret = -ENOMEM;
+ goto mpu_put_node;
+ }
+
+ ocmcram_location_data = gen_pool_alloc(sram_pool_data,
+ sizeof(struct emif_regs_amx3));
+ if (!ocmcram_location_data) {
+ dev_err(pm33xx_dev, "PM: Unable to allocate memory from ocmcram\n");
+ gen_pool_free(sram_pool, ocmcram_location, *pm_sram->do_wfi_sz);
+ ret = -ENOMEM;
+ }
+
+mpu_put_node:
+ of_node_put(np);
+ return ret;
+}
+
+static int am33xx_push_sram_idle(void)
+{
+ struct am33xx_pm_ro_sram_data ro_sram_data;
+ int ret;
+ u32 table_addr, ro_data_addr;
+ void *copy_addr;
+
+ ro_sram_data.amx3_pm_sram_data_virt = ocmcram_location_data;
+ ro_sram_data.amx3_pm_sram_data_phys =
+ gen_pool_virt_to_phys(sram_pool_data, ocmcram_location_data);
+
+ /* Save physical address to calculate resume offset during pm init */
+ am33xx_do_wfi_sram_phys = gen_pool_virt_to_phys(sram_pool,
+ ocmcram_location);
+
+ am33xx_do_wfi_sram = sram_exec_copy(sram_pool, (void *)ocmcram_location,
+ pm_sram->do_wfi,
+ *pm_sram->do_wfi_sz);
+ if (!am33xx_do_wfi_sram) {
+ dev_err(pm33xx_dev,
+ "PM: %s: am33xx_do_wfi copy to sram failed\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ table_addr =
+ sram_suspend_address((unsigned long)pm_sram->emif_sram_table);
+ ret = ti_emif_copy_pm_function_table(sram_pool, (void *)table_addr);
+ if (ret) {
+ dev_dbg(pm33xx_dev,
+ "PM: %s: EMIF function copy failed\n", __func__);
+ return -EPROBE_DEFER;
+ }
+
+ ro_data_addr =
+ sram_suspend_address((unsigned long)pm_sram->ro_sram_data);
+ copy_addr = sram_exec_copy(sram_pool, (void *)ro_data_addr,
+ &ro_sram_data,
+ sizeof(ro_sram_data));
+ if (!copy_addr) {
+ dev_err(pm33xx_dev,
+ "PM: %s: ro_sram_data copy to sram failed\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int am33xx_pm_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ if (!of_machine_is_compatible("ti,am33xx") &&
+ !of_machine_is_compatible("ti,am43"))
+ return -ENODEV;
+
+ pm_ops = dev->platform_data;
+ if (!pm_ops) {
+ dev_err(dev, "PM: Cannot get core PM ops!\n");
+ return -ENODEV;
+ }
+
+ pm_sram = pm_ops->get_sram_addrs();
+ if (!pm_sram) {
+ dev_err(dev, "PM: Cannot get PM asm function addresses!!\n");
+ return -ENODEV;
+ }
+
+ pm33xx_dev = dev;
+
+ ret = am33xx_pm_alloc_sram();
+ if (ret)
+ return ret;
+
+ ret = am33xx_push_sram_idle();
+ if (ret)
+ goto err_free_sram;
+
+ m3_ipc = wkup_m3_ipc_get();
+ if (!m3_ipc) {
+ dev_dbg(dev, "PM: Cannot get wkup_m3_ipc handle\n");
+ ret = -EPROBE_DEFER;
+ goto err_free_sram;
+ }
+
+ am33xx_pm_set_ipc_ops();
+
+#ifdef CONFIG_SUSPEND
+ suspend_set_ops(&am33xx_pm_ops);
+#endif /* CONFIG_SUSPEND */
+
+ ret = pm_ops->init();
+ if (ret) {
+ dev_err(dev, "Unable to call core pm init!\n");
+ ret = -ENODEV;
+ goto err_put_wkup_m3_ipc;
+ }
+
+ return 0;
+
+err_put_wkup_m3_ipc:
+ wkup_m3_ipc_put(m3_ipc);
+err_free_sram:
+ am33xx_pm_free_sram();
+ pm33xx_dev = NULL;
+ return ret;
+}
+
+static int am33xx_pm_remove(struct platform_device *pdev)
+{
+ suspend_set_ops(NULL);
+ wkup_m3_ipc_put(m3_ipc);
+ am33xx_pm_free_sram();
+ return 0;
+}
+
+static struct platform_driver am33xx_pm_driver = {
+ .driver = {
+ .name = "pm33xx",
+ },
+ .probe = am33xx_pm_probe,
+ .remove = am33xx_pm_remove,
+};
+module_platform_driver(am33xx_pm_driver);
+
+MODULE_ALIAS("platform:pm33xx");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("am33xx power management driver");
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 603783976b81..2d4146ce2f1b 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -72,10 +72,10 @@ config SPI_ARMADA_3700
config SPI_ATMEL
tristate "Atmel SPI Controller"
depends on HAS_DMA
- depends on (ARCH_AT91 || AVR32 || COMPILE_TEST)
+ depends on ARCH_AT91 || COMPILE_TEST
help
This selects a driver for the Atmel SPI Controller, present on
- many AT32 (AVR32) and AT91 (ARM) chips.
+ many AT91 ARM chips.
config SPI_AU1550
tristate "Au1550/Au1200/Au1300 SPI Controller"
@@ -115,25 +115,6 @@ config SPI_BCM2835AUX
"universal SPI master", and the regular SPI controller.
This driver is for the universal/auxiliary SPI controller.
-config SPI_BFIN5XX
- tristate "SPI controller driver for ADI Blackfin5xx"
- depends on BLACKFIN && !BF60x
- help
- This is the SPI controller master driver for Blackfin 5xx processor.
-
-config SPI_ADI_V3
- tristate "SPI controller v3 for ADI"
- depends on BF60x
- help
- This is the SPI controller v3 master driver
- found on Blackfin 60x processor.
-
-config SPI_BFIN_SPORT
- tristate "SPI bus via Blackfin SPORT"
- depends on BLACKFIN
- help
- Enable support for a SPI bus via the Blackfin SPORT peripheral.
-
config SPI_BCM53XX
tristate "Broadcom BCM53xx SPI controller"
depends on ARCH_BCM_5301X
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 34c5f2832ddf..b935f10eb961 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -24,9 +24,6 @@ obj-$(CONFIG_SPI_BCM53XX) += spi-bcm53xx.o
obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o
obj-$(CONFIG_SPI_BCM63XX_HSSPI) += spi-bcm63xx-hsspi.o
obj-$(CONFIG_SPI_BCM_QSPI) += spi-iproc-qspi.o spi-brcmstb-qspi.o spi-bcm-qspi.o
-obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5xx.o
-obj-$(CONFIG_SPI_ADI_V3) += spi-adi-v3.o
-obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o
obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o
obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o
obj-$(CONFIG_SPI_CADENCE) += spi-cadence.o
diff --git a/drivers/spi/spi-adi-v3.c b/drivers/spi/spi-adi-v3.c
deleted file mode 100644
index a16b25dcd1e6..000000000000
--- a/drivers/spi/spi-adi-v3.c
+++ /dev/null
@@ -1,984 +0,0 @@
-/*
- * Analog Devices SPI3 controller driver
- *
- * Copyright (c) 2014 Analog Devices Inc.
- *
- * 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.
- *
- * 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.
- */
-
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/dma-mapping.h>
-#include <linux/errno.h>
-#include <linux/gpio.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/ioport.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/spi/spi.h>
-#include <linux/spi/adi_spi3.h>
-#include <linux/types.h>
-
-#include <asm/dma.h>
-#include <asm/portmux.h>
-
-enum adi_spi_state {
- START_STATE,
- RUNNING_STATE,
- DONE_STATE,
- ERROR_STATE
-};
-
-struct adi_spi_master;
-
-struct adi_spi_transfer_ops {
- void (*write) (struct adi_spi_master *);
- void (*read) (struct adi_spi_master *);
- void (*duplex) (struct adi_spi_master *);
-};
-
-/* runtime info for spi master */
-struct adi_spi_master {
- /* SPI framework hookup */
- struct spi_master *master;
-
- /* Regs base of SPI controller */
- struct adi_spi_regs __iomem *regs;
-
- /* Pin request list */
- u16 *pin_req;
-
- /* Message Transfer pump */
- struct tasklet_struct pump_transfers;
-
- /* Current message transfer state info */
- struct spi_message *cur_msg;
- struct spi_transfer *cur_transfer;
- struct adi_spi_device *cur_chip;
- unsigned transfer_len;
-
- /* transfer buffer */
- void *tx;
- void *tx_end;
- void *rx;
- void *rx_end;
-
- /* dma info */
- unsigned int tx_dma;
- unsigned int rx_dma;
- dma_addr_t tx_dma_addr;
- dma_addr_t rx_dma_addr;
- unsigned long dummy_buffer; /* used in unidirectional transfer */
- unsigned long tx_dma_size;
- unsigned long rx_dma_size;
- int tx_num;
- int rx_num;
-
- /* store register value for suspend/resume */
- u32 control;
- u32 ssel;
-
- unsigned long sclk;
- enum adi_spi_state state;
-
- const struct adi_spi_transfer_ops *ops;
-};
-
-struct adi_spi_device {
- u32 control;
- u32 clock;
- u32 ssel;
-
- u8 cs;
- u16 cs_chg_udelay; /* Some devices require > 255usec delay */
- u32 cs_gpio;
- u32 tx_dummy_val; /* tx value for rx only transfer */
- bool enable_dma;
- const struct adi_spi_transfer_ops *ops;
-};
-
-static void adi_spi_enable(struct adi_spi_master *drv_data)
-{
- u32 ctl;
-
- ctl = ioread32(&drv_data->regs->control);
- ctl |= SPI_CTL_EN;
- iowrite32(ctl, &drv_data->regs->control);
-}
-
-static void adi_spi_disable(struct adi_spi_master *drv_data)
-{
- u32 ctl;
-
- ctl = ioread32(&drv_data->regs->control);
- ctl &= ~SPI_CTL_EN;
- iowrite32(ctl, &drv_data->regs->control);
-}
-
-/* Caculate the SPI_CLOCK register value based on input HZ */
-static u32 hz_to_spi_clock(u32 sclk, u32 speed_hz)
-{
- u32 spi_clock = sclk / speed_hz;
-
- if (spi_clock)
- spi_clock--;
- return spi_clock;
-}
-
-static int adi_spi_flush(struct adi_spi_master *drv_data)
-{
- unsigned long limit = loops_per_jiffy << 1;
-
- /* wait for stop and clear stat */
- while (!(ioread32(&drv_data->regs->status) & SPI_STAT_SPIF) && --limit)
- cpu_relax();
-
- iowrite32(0xFFFFFFFF, &drv_data->regs->status);
-
- return limit;
-}
-
-/* Chip select operation functions for cs_change flag */
-static void adi_spi_cs_active(struct adi_spi_master *drv_data, struct adi_spi_device *chip)
-{
- if (likely(chip->cs < MAX_CTRL_CS)) {
- u32 reg;
- reg = ioread32(&drv_data->regs->ssel);
- reg &= ~chip->ssel;
- iowrite32(reg, &drv_data->regs->ssel);
- } else {
- gpio_set_value(chip->cs_gpio, 0);
- }
-}
-
-static void adi_spi_cs_deactive(struct adi_spi_master *drv_data,
- struct adi_spi_device *chip)
-{
- if (likely(chip->cs < MAX_CTRL_CS)) {
- u32 reg;
- reg = ioread32(&drv_data->regs->ssel);
- reg |= chip->ssel;
- iowrite32(reg, &drv_data->regs->ssel);
- } else {
- gpio_set_value(chip->cs_gpio, 1);
- }
-
- /* Move delay here for consistency */
- if (chip->cs_chg_udelay)
- udelay(chip->cs_chg_udelay);
-}
-
-/* enable or disable the pin muxed by GPIO and SPI CS to work as SPI CS */
-static inline void adi_spi_cs_enable(struct adi_spi_master *drv_data,
- struct adi_spi_device *chip)
-{
- if (chip->cs < MAX_CTRL_CS) {
- u32 reg;
- reg = ioread32(&drv_data->regs->ssel);
- reg |= chip->ssel >> 8;
- iowrite32(reg, &drv_data->regs->ssel);
- }
-}
-
-static inline void adi_spi_cs_disable(struct adi_spi_master *drv_data,
- struct adi_spi_device *chip)
-{
- if (chip->cs < MAX_CTRL_CS) {
- u32 reg;
- reg = ioread32(&drv_data->regs->ssel);
- reg &= ~(chip->ssel >> 8);
- iowrite32(reg, &drv_data->regs->ssel);
- }
-}
-
-/* stop controller and re-config current chip*/
-static void adi_spi_restore_state(struct adi_spi_master *drv_data)
-{
- struct adi_spi_device *chip = drv_data->cur_chip;
-
- /* Clear status and disable clock */
- iowrite32(0xFFFFFFFF, &drv_data->regs->status);
- iowrite32(0x0, &drv_data->regs->rx_control);
- iowrite32(0x0, &drv_data->regs->tx_control);
- adi_spi_disable(drv_data);
-
- /* Load the registers */
- iowrite32(chip->control, &drv_data->regs->control);
- iowrite32(chip->clock, &drv_data->regs->clock);
-
- adi_spi_enable(drv_data);
- drv_data->tx_num = drv_data->rx_num = 0;
- /* we always choose tx transfer initiate */
- iowrite32(SPI_RXCTL_REN, &drv_data->regs->rx_control);
- iowrite32(SPI_TXCTL_TEN | SPI_TXCTL_TTI, &drv_data->regs->tx_control);
- adi_spi_cs_active(drv_data, chip);
-}
-
-/* discard invalid rx data and empty rfifo */
-static inline void dummy_read(struct adi_spi_master *drv_data)
-{
- while (!(ioread32(&drv_data->regs->status) & SPI_STAT_RFE))
- ioread32(&drv_data->regs->rfifo);
-}
-
-static void adi_spi_u8_write(struct adi_spi_master *drv_data)
-{
- dummy_read(drv_data);
- while (drv_data->tx < drv_data->tx_end) {
- iowrite32(*(u8 *)(drv_data->tx++), &drv_data->regs->tfifo);
- while (ioread32(&drv_data->regs->status) & SPI_STAT_RFE)
- cpu_relax();
- ioread32(&drv_data->regs->rfifo);
- }
-}
-
-static void adi_spi_u8_read(struct adi_spi_master *drv_data)
-{
- u32 tx_val = drv_data->cur_chip->tx_dummy_val;
-
- dummy_read(drv_data);
- while (drv_data->rx < drv_data->rx_end) {
- iowrite32(tx_val, &drv_data->regs->tfifo);
- while (ioread32(&drv_data->regs->status) & SPI_STAT_RFE)
- cpu_relax();
- *(u8 *)(drv_data->rx++) = ioread32(&drv_data->regs->rfifo);
- }
-}
-
-static void adi_spi_u8_duplex(struct adi_spi_master *drv_data)
-{
- dummy_read(drv_data);
- while (drv_data->rx < drv_data->rx_end) {
- iowrite32(*(u8 *)(drv_data->tx++), &drv_data->regs->tfifo);
- while (ioread32(&drv_data->regs->status) & SPI_STAT_RFE)
- cpu_relax();
- *(u8 *)(drv_data->rx++) = ioread32(&drv_data->regs->rfifo);
- }
-}
-
-static const struct adi_spi_transfer_ops adi_spi_transfer_ops_u8 = {
- .write = adi_spi_u8_write,
- .read = adi_spi_u8_read,
- .duplex = adi_spi_u8_duplex,
-};
-
-static void adi_spi_u16_write(struct adi_spi_master *drv_data)
-{
- dummy_read(drv_data);
- while (drv_data->tx < drv_data->tx_end) {
- iowrite32(*(u16 *)drv_data->tx, &drv_data->regs->tfifo);
- drv_data->tx += 2;
- while (ioread32(&drv_data->regs->status) & SPI_STAT_RFE)
- cpu_relax();
- ioread32(&drv_data->regs->rfifo);
- }
-}
-
-static void adi_spi_u16_read(struct adi_spi_master *drv_data)
-{
- u32 tx_val = drv_data->cur_chip->tx_dummy_val;
-
- dummy_read(drv_data);
- while (drv_data->rx < drv_data->rx_end) {
- iowrite32(tx_val, &drv_data->regs->tfifo);
- while (ioread32(&drv_data->regs->status) & SPI_STAT_RFE)
- cpu_relax();
- *(u16 *)drv_data->rx = ioread32(&drv_data->regs->rfifo);
- drv_data->rx += 2;
- }
-}
-
-static void adi_spi_u16_duplex(struct adi_spi_master *drv_data)
-{
- dummy_read(drv_data);
- while (drv_data->rx < drv_data->rx_end) {
- iowrite32(*(u16 *)drv_data->tx, &drv_data->regs->tfifo);
- drv_data->tx += 2;
- while (ioread32(&drv_data->regs->status) & SPI_STAT_RFE)
- cpu_relax();
- *(u16 *)drv_data->rx = ioread32(&drv_data->regs->rfifo);
- drv_data->rx += 2;
- }
-}
-
-static const struct adi_spi_transfer_ops adi_spi_transfer_ops_u16 = {
- .write = adi_spi_u16_write,
- .read = adi_spi_u16_read,
- .duplex = adi_spi_u16_duplex,
-};
-
-static void adi_spi_u32_write(struct adi_spi_master *drv_data)
-{
- dummy_read(drv_data);
- while (drv_data->tx < drv_data->tx_end) {
- iowrite32(*(u32 *)drv_data->tx, &drv_data->regs->tfifo);
- drv_data->tx += 4;
- while (ioread32(&drv_data->regs->status) & SPI_STAT_RFE)
- cpu_relax();
- ioread32(&drv_data->regs->rfifo);
- }
-}
-
-static void adi_spi_u32_read(struct adi_spi_master *drv_data)
-{
- u32 tx_val = drv_data->cur_chip->tx_dummy_val;
-
- dummy_read(drv_data);
- while (drv_data->rx < drv_data->rx_end) {
- iowrite32(tx_val, &drv_data->regs->tfifo);
- while (ioread32(&drv_data->regs->status) & SPI_STAT_RFE)
- cpu_relax();
- *(u32 *)drv_data->rx = ioread32(&drv_data->regs->rfifo);
- drv_data->rx += 4;
- }
-}
-
-static void adi_spi_u32_duplex(struct adi_spi_master *drv_data)
-{
- dummy_read(drv_data);
- while (drv_data->rx < drv_data->rx_end) {
- iowrite32(*(u32 *)drv_data->tx, &drv_data->regs->tfifo);
- drv_data->tx += 4;
- while (ioread32(&drv_data->regs->status) & SPI_STAT_RFE)
- cpu_relax();
- *(u32 *)drv_data->rx = ioread32(&drv_data->regs->rfifo);
- drv_data->rx += 4;
- }
-}
-
-static const struct adi_spi_transfer_ops adi_spi_transfer_ops_u32 = {
- .write = adi_spi_u32_write,
- .read = adi_spi_u32_read,
- .duplex = adi_spi_u32_duplex,
-};
-
-
-/* test if there is more transfer to be done */
-static void adi_spi_next_transfer(struct adi_spi_master *drv)
-{
- struct spi_message *msg = drv->cur_msg;
- struct spi_transfer *t = drv->cur_transfer;
-
- /* Move to next transfer */
- if (t->transfer_list.next != &msg->transfers) {
- drv->cur_transfer = list_entry(t->transfer_list.next,
- struct spi_transfer, transfer_list);
- drv->state = RUNNING_STATE;
- } else {
- drv->state = DONE_STATE;
- drv->cur_transfer = NULL;
- }
-}
-
-static void adi_spi_giveback(struct adi_spi_master *drv_data)
-{
- struct adi_spi_device *chip = drv_data->cur_chip;
-
- adi_spi_cs_deactive(drv_data, chip);
- spi_finalize_current_message(drv_data->master);
-}
-
-static int adi_spi_setup_transfer(struct adi_spi_master *drv)
-{
- struct spi_transfer *t = drv->cur_transfer;
- u32 cr, cr_width;
-
- if (t->tx_buf) {
- drv->tx = (void *)t->tx_buf;
- drv->tx_end = drv->tx + t->len;
- } else {
- drv->tx = NULL;
- }
-
- if (t->rx_buf) {
- drv->rx = t->rx_buf;
- drv->rx_end = drv->rx + t->len;
- } else {
- drv->rx = NULL;
- }
-
- drv->transfer_len = t->len;
-
- /* bits per word setup */
- switch (t->bits_per_word) {
- case 8:
- cr_width = SPI_CTL_SIZE08;
- drv->ops = &adi_spi_transfer_ops_u8;
- break;
- case 16:
- cr_width = SPI_CTL_SIZE16;
- drv->ops = &adi_spi_transfer_ops_u16;
- break;
- case 32:
- cr_width = SPI_CTL_SIZE32;
- drv->ops = &adi_spi_transfer_ops_u32;
- break;
- default:
- return -EINVAL;
- }
- cr = ioread32(&drv->regs->control) & ~SPI_CTL_SIZE;
- cr |= cr_width;
- iowrite32(cr, &drv->regs->control);
-
- /* speed setup */
- iowrite32(hz_to_spi_clock(drv->sclk, t->speed_hz), &drv->regs->clock);
- return 0;
-}
-
-static int adi_spi_dma_xfer(struct adi_spi_master *drv_data)
-{
- struct spi_transfer *t = drv_data->cur_transfer;
- struct spi_message *msg = drv_data->cur_msg;
- struct adi_spi_device *chip = drv_data->cur_chip;
- u32 dma_config;
- unsigned long word_count, word_size;
- void *tx_buf, *rx_buf;
-
- switch (t->bits_per_word) {
- case 8:
- dma_config = WDSIZE_8 | PSIZE_8;
- word_count = drv_data->transfer_len;
- word_size = 1;
- break;
- case 16:
- dma_config = WDSIZE_16 | PSIZE_16;
- word_count = drv_data->transfer_len / 2;
- word_size = 2;
- break;
- default:
- dma_config = WDSIZE_32 | PSIZE_32;
- word_count = drv_data->transfer_len / 4;
- word_size = 4;
- break;
- }
-
- if (!drv_data->rx) {
- tx_buf = drv_data->tx;
- rx_buf = &drv_data->dummy_buffer;
- drv_data->tx_dma_size = drv_data->transfer_len;
- drv_data->rx_dma_size = sizeof(drv_data->dummy_buffer);
- set_dma_x_modify(drv_data->tx_dma, word_size);
- set_dma_x_modify(drv_data->rx_dma, 0);
- } else if (!drv_data->tx) {
- drv_data->dummy_buffer = chip->tx_dummy_val;
- tx_buf = &drv_data->dummy_buffer;
- rx_buf = drv_data->rx;
- drv_data->tx_dma_size = sizeof(drv_data->dummy_buffer);
- drv_data->rx_dma_size = drv_data->transfer_len;
- set_dma_x_modify(drv_data->tx_dma, 0);
- set_dma_x_modify(drv_data->rx_dma, word_size);
- } else {
- tx_buf = drv_data->tx;
- rx_buf = drv_data->rx;
- drv_data->tx_dma_size = drv_data->rx_dma_size
- = drv_data->transfer_len;
- set_dma_x_modify(drv_data->tx_dma, word_size);
- set_dma_x_modify(drv_data->rx_dma, word_size);
- }
-
- drv_data->tx_dma_addr = dma_map_single(&msg->spi->dev,
- (void *)tx_buf,
- drv_data->tx_dma_size,
- DMA_TO_DEVICE);
- if (dma_mapping_error(&msg->spi->dev,
- drv_data->tx_dma_addr))
- return -ENOMEM;
-
- drv_data->rx_dma_addr = dma_map_single(&msg->spi->dev,
- (void *)rx_buf,
- drv_data->rx_dma_size,
- DMA_FROM_DEVICE);
- if (dma_mapping_error(&msg->spi->dev,
- drv_data->rx_dma_addr)) {
- dma_unmap_single(&msg->spi->dev,
- drv_data->tx_dma_addr,
- drv_data->tx_dma_size,
- DMA_TO_DEVICE);
- return -ENOMEM;
- }
-
- dummy_read(drv_data);
- set_dma_x_count(drv_data->tx_dma, word_count);
- set_dma_x_count(drv_data->rx_dma, word_count);
- set_dma_start_addr(drv_data->tx_dma, drv_data->tx_dma_addr);
- set_dma_start_addr(drv_data->rx_dma, drv_data->rx_dma_addr);
- dma_config |= DMAFLOW_STOP | RESTART | DI_EN;
- set_dma_config(drv_data->tx_dma, dma_config);
- set_dma_config(drv_data->rx_dma, dma_config | WNR);
- enable_dma(drv_data->tx_dma);
- enable_dma(drv_data->rx_dma);
-
- iowrite32(SPI_RXCTL_REN | SPI_RXCTL_RDR_NE,
- &drv_data->regs->rx_control);
- iowrite32(SPI_TXCTL_TEN | SPI_TXCTL_TTI | SPI_TXCTL_TDR_NF,
- &drv_data->regs->tx_control);
-
- return 0;
-}
-
-static int adi_spi_pio_xfer(struct adi_spi_master *drv_data)
-{
- struct spi_message *msg = drv_data->cur_msg;
-
- if (!drv_data->rx) {
- /* write only half duplex */
- drv_data->ops->write(drv_data);
- if (drv_data->tx != drv_data->tx_end)
- return -EIO;
- } else if (!drv_data->tx) {
- /* read only half duplex */
- drv_data->ops->read(drv_data);
- if (drv_data->rx != drv_data->rx_end)
- return -EIO;
- } else {
- /* full duplex mode */
- drv_data->ops->duplex(drv_data);
- if (drv_data->tx != drv_data->tx_end)
- return -EIO;
- }
-
- if (!adi_spi_flush(drv_data))
- return -EIO;
- msg->actual_length += drv_data->transfer_len;
- tasklet_schedule(&drv_data->pump_transfers);
- return 0;
-}
-
-static void adi_spi_pump_transfers(unsigned long data)
-{
- struct adi_spi_master *drv_data = (struct adi_spi_master *)data;
- struct spi_message *msg = NULL;
- struct spi_transfer *t = NULL;
- struct adi_spi_device *chip = NULL;
- int ret;
-
- /* Get current state information */
- msg = drv_data->cur_msg;
- t = drv_data->cur_transfer;
- chip = drv_data->cur_chip;
-
- /* Handle for abort */
- if (drv_data->state == ERROR_STATE) {
- msg->status = -EIO;
- adi_spi_giveback(drv_data);
- return;
- }
-
- if (drv_data->state == RUNNING_STATE) {
- if (t->delay_usecs)
- udelay(t->delay_usecs);
- if (t->cs_change)
- adi_spi_cs_deactive(drv_data, chip);
- adi_spi_next_transfer(drv_data);
- t = drv_data->cur_transfer;
- }
- /* Handle end of message */
- if (drv_data->state == DONE_STATE) {
- msg->status = 0;
- adi_spi_giveback(drv_data);
- return;
- }
-
- if ((t->len == 0) || (t->tx_buf == NULL && t->rx_buf == NULL)) {
- /* Schedule next transfer tasklet */
- tasklet_schedule(&drv_data->pump_transfers);
- return;
- }
-
- ret = adi_spi_setup_transfer(drv_data);
- if (ret) {
- msg->status = ret;
- adi_spi_giveback(drv_data);
- }
-
- iowrite32(0xFFFFFFFF, &drv_data->regs->status);
- adi_spi_cs_active(drv_data, chip);
- drv_data->state = RUNNING_STATE;
-
- if (chip->enable_dma)
- ret = adi_spi_dma_xfer(drv_data);
- else
- ret = adi_spi_pio_xfer(drv_data);
- if (ret) {
- msg->status = ret;
- adi_spi_giveback(drv_data);
- }
-}
-
-static int adi_spi_transfer_one_message(struct spi_master *master,
- struct spi_message *m)
-{
- struct adi_spi_master *drv_data = spi_master_get_devdata(master);
-
- drv_data->cur_msg = m;
- drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi);
- adi_spi_restore_state(drv_data);
-
- drv_data->state = START_STATE;
- drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next,
- struct spi_transfer, transfer_list);
-
- tasklet_schedule(&drv_data->pump_transfers);
- return 0;
-}
-
-#define MAX_SPI_SSEL 7
-
-static const u16 ssel[][MAX_SPI_SSEL] = {
- {P_SPI0_SSEL1, P_SPI0_SSEL2, P_SPI0_SSEL3,
- P_SPI0_SSEL4, P_SPI0_SSEL5,
- P_SPI0_SSEL6, P_SPI0_SSEL7},
-
- {P_SPI1_SSEL1, P_SPI1_SSEL2, P_SPI1_SSEL3,
- P_SPI1_SSEL4, P_SPI1_SSEL5,
- P_SPI1_SSEL6, P_SPI1_SSEL7},
-
- {P_SPI2_SSEL1, P_SPI2_SSEL2, P_SPI2_SSEL3,
- P_SPI2_SSEL4, P_SPI2_SSEL5,
- P_SPI2_SSEL6, P_SPI2_SSEL7},
-};
-
-static int adi_spi_setup(struct spi_device *spi)
-{
- struct adi_spi_master *drv_data = spi_master_get_devdata(spi->master);
- struct adi_spi_device *chip = spi_get_ctldata(spi);
- u32 ctl_reg = SPI_CTL_ODM | SPI_CTL_PSSE;
- int ret = -EINVAL;
-
- if (!chip) {
- struct adi_spi3_chip *chip_info = spi->controller_data;
-
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (!chip)
- return -ENOMEM;
-
- if (chip_info) {
- if (chip_info->control & ~ctl_reg) {
- dev_err(&spi->dev,
- "do not set bits that the SPI framework manages\n");
- goto error;
- }
- chip->control = chip_info->control;
- chip->cs_chg_udelay = chip_info->cs_chg_udelay;
- chip->tx_dummy_val = chip_info->tx_dummy_val;
- chip->enable_dma = chip_info->enable_dma;
- }
- chip->cs = spi->chip_select;
-
- if (chip->cs < MAX_CTRL_CS) {
- chip->ssel = (1 << chip->cs) << 8;
- ret = peripheral_request(ssel[spi->master->bus_num]
- [chip->cs-1], dev_name(&spi->dev));
- if (ret) {
- dev_err(&spi->dev, "peripheral_request() error\n");
- goto error;
- }
- } else {
- chip->cs_gpio = chip->cs - MAX_CTRL_CS;
- ret = gpio_request_one(chip->cs_gpio, GPIOF_OUT_INIT_HIGH,
- dev_name(&spi->dev));
- if (ret) {
- dev_err(&spi->dev, "gpio_request_one() error\n");
- goto error;
- }
- }
- spi_set_ctldata(spi, chip);
- }
-
- /* force a default base state */
- chip->control &= ctl_reg;
-
- if (spi->mode & SPI_CPOL)
- chip->control |= SPI_CTL_CPOL;
- if (spi->mode & SPI_CPHA)
- chip->control |= SPI_CTL_CPHA;
- if (spi->mode & SPI_LSB_FIRST)
- chip->control |= SPI_CTL_LSBF;
- chip->control |= SPI_CTL_MSTR;
- /* we choose software to controll cs */
- chip->control &= ~SPI_CTL_ASSEL;
-
- chip->clock = hz_to_spi_clock(drv_data->sclk, spi->max_speed_hz);
-
- adi_spi_cs_enable(drv_data, chip);
- adi_spi_cs_deactive(drv_data, chip);
-
- return 0;
-error:
- if (chip) {
- kfree(chip);
- spi_set_ctldata(spi, NULL);
- }
-
- return ret;
-}
-
-static void adi_spi_cleanup(struct spi_device *spi)
-{
- struct adi_spi_device *chip = spi_get_ctldata(spi);
- struct adi_spi_master *drv_data = spi_master_get_devdata(spi->master);
-
- if (!chip)
- return;
-
- if (chip->cs < MAX_CTRL_CS) {
- peripheral_free(ssel[spi->master->bus_num]
- [chip->cs-1]);
- adi_spi_cs_disable(drv_data, chip);
- } else {
- gpio_free(chip->cs_gpio);
- }
-
- kfree(chip);
- spi_set_ctldata(spi, NULL);
-}
-
-static irqreturn_t adi_spi_tx_dma_isr(int irq, void *dev_id)
-{
- struct adi_spi_master *drv_data = dev_id;
- u32 dma_stat = get_dma_curr_irqstat(drv_data->tx_dma);
- u32 tx_ctl;
-
- clear_dma_irqstat(drv_data->tx_dma);
- if (dma_stat & DMA_DONE) {
- drv_data->tx_num++;
- } else {
- dev_err(&drv_data->master->dev,
- "spi tx dma error: %d\n", dma_stat);
- if (drv_data->tx)
- drv_data->state = ERROR_STATE;
- }
- tx_ctl = ioread32(&drv_data->regs->tx_control);
- tx_ctl &= ~SPI_TXCTL_TDR_NF;
- iowrite32(tx_ctl, &drv_data->regs->tx_control);
- return IRQ_HANDLED;
-}
-
-static irqreturn_t adi_spi_rx_dma_isr(int irq, void *dev_id)
-{
- struct adi_spi_master *drv_data = dev_id;
- struct spi_message *msg = drv_data->cur_msg;
- u32 dma_stat = get_dma_curr_irqstat(drv_data->rx_dma);
-
- clear_dma_irqstat(drv_data->rx_dma);
- if (dma_stat & DMA_DONE) {
- drv_data->rx_num++;
- /* we may fail on tx dma */
- if (drv_data->state != ERROR_STATE)
- msg->actual_length += drv_data->transfer_len;
- } else {
- drv_data->state = ERROR_STATE;
- dev_err(&drv_data->master->dev,
- "spi rx dma error: %d\n", dma_stat);
- }
- iowrite32(0, &drv_data->regs->tx_control);
- iowrite32(0, &drv_data->regs->rx_control);
- if (drv_data->rx_num != drv_data->tx_num)
- dev_dbg(&drv_data->master->dev,
- "dma interrupt missing: tx=%d,rx=%d\n",
- drv_data->tx_num, drv_data->rx_num);
- tasklet_schedule(&drv_data->pump_transfers);
- return IRQ_HANDLED;
-}
-
-static int adi_spi_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct adi_spi3_master *info = dev_get_platdata(dev);
- struct spi_master *master;
- struct adi_spi_master *drv_data;
- struct resource *mem, *res;
- unsigned int tx_dma, rx_dma;
- struct clk *sclk;
- int ret;
-
- if (!info) {
- dev_err(dev, "platform data missing!\n");
- return -ENODEV;
- }
-
- sclk = devm_clk_get(dev, "spi");
- if (IS_ERR(sclk)) {
- dev_err(dev, "can not get spi clock\n");
- return PTR_ERR(sclk);
- }
-
- res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (!res) {
- dev_err(dev, "can not get tx dma resource\n");
- return -ENXIO;
- }
- tx_dma = res->start;
-
- res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
- if (!res) {
- dev_err(dev, "can not get rx dma resource\n");
- return -ENXIO;
- }
- rx_dma = res->start;
-
- /* allocate master with space for drv_data */
- master = spi_alloc_master(dev, sizeof(*drv_data));
- if (!master) {
- dev_err(dev, "can not alloc spi_master\n");
- return -ENOMEM;
- }
- platform_set_drvdata(pdev, master);
-
- /* the mode bits supported by this driver */
- master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
-
- master->bus_num = pdev->id;
- master->num_chipselect = info->num_chipselect;
- master->cleanup = adi_spi_cleanup;
- master->setup = adi_spi_setup;
- master->transfer_one_message = adi_spi_transfer_one_message;
- master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) |
- SPI_BPW_MASK(8);
-
- drv_data = spi_master_get_devdata(master);
- drv_data->master = master;
- drv_data->tx_dma = tx_dma;
- drv_data->rx_dma = rx_dma;
- drv_data->pin_req = info->pin_req;
- drv_data->sclk = clk_get_rate(sclk);
-
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- drv_data->regs = devm_ioremap_resource(dev, mem);
- if (IS_ERR(drv_data->regs)) {
- ret = PTR_ERR(drv_data->regs);
- goto err_put_master;
- }
-
- /* request tx and rx dma */
- ret = request_dma(tx_dma, "SPI_TX_DMA");
- if (ret) {
- dev_err(dev, "can not request SPI TX DMA channel\n");
- goto err_put_master;
- }
- set_dma_callback(tx_dma, adi_spi_tx_dma_isr, drv_data);
-
- ret = request_dma(rx_dma, "SPI_RX_DMA");
- if (ret) {
- dev_err(dev, "can not request SPI RX DMA channel\n");
- goto err_free_tx_dma;
- }
- set_dma_callback(drv_data->rx_dma, adi_spi_rx_dma_isr, drv_data);
-
- /* request CLK, MOSI and MISO */
- ret = peripheral_request_list(drv_data->pin_req, "adi-spi3");
- if (ret < 0) {
- dev_err(dev, "can not request spi pins\n");
- goto err_free_rx_dma;
- }
-
- iowrite32(SPI_CTL_MSTR | SPI_CTL_CPHA, &drv_data->regs->control);
- iowrite32(0x0000FE00, &drv_data->regs->ssel);
- iowrite32(0x0, &drv_data->regs->delay);
-
- tasklet_init(&drv_data->pump_transfers,
- adi_spi_pump_transfers, (unsigned long)drv_data);
- /* register with the SPI framework */
- ret = devm_spi_register_master(dev, master);
- if (ret) {
- dev_err(dev, "can not register spi master\n");
- goto err_free_peripheral;
- }
-
- return ret;
-
-err_free_peripheral:
- peripheral_free_list(drv_data->pin_req);
-err_free_rx_dma:
- free_dma(rx_dma);
-err_free_tx_dma:
- free_dma(tx_dma);
-err_put_master:
- spi_master_put(master);
-
- return ret;
-}
-
-static int adi_spi_remove(struct platform_device *pdev)
-{
- struct spi_master *master = platform_get_drvdata(pdev);
- struct adi_spi_master *drv_data = spi_master_get_devdata(master);
-
- adi_spi_disable(drv_data);
- peripheral_free_list(drv_data->pin_req);
- free_dma(drv_data->rx_dma);
- free_dma(drv_data->tx_dma);
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int adi_spi_suspend(struct device *dev)
-{
- struct spi_master *master = dev_get_drvdata(dev);
- struct adi_spi_master *drv_data = spi_master_get_devdata(master);
-
- spi_master_suspend(master);
-
- drv_data->control = ioread32(&drv_data->regs->control);
- drv_data->ssel = ioread32(&drv_data->regs->ssel);
-
- iowrite32(SPI_CTL_MSTR | SPI_CTL_CPHA, &drv_data->regs->control);
- iowrite32(0x0000FE00, &drv_data->regs->ssel);
- dma_disable_irq(drv_data->rx_dma);
- dma_disable_irq(drv_data->tx_dma);
-
- return 0;
-}
-
-static int adi_spi_resume(struct device *dev)
-{
- struct spi_master *master = dev_get_drvdata(dev);
- struct adi_spi_master *drv_data = spi_master_get_devdata(master);
- int ret = 0;
-
- /* bootrom may modify spi and dma status when resume in spi boot mode */
- disable_dma(drv_data->rx_dma);
-
- dma_enable_irq(drv_data->rx_dma);
- dma_enable_irq(drv_data->tx_dma);
- iowrite32(drv_data->control, &drv_data->regs->control);
- iowrite32(drv_data->ssel, &drv_data->regs->ssel);
-
- ret = spi_master_resume(master);
- if (ret) {
- free_dma(drv_data->rx_dma);
- free_dma(drv_data->tx_dma);
- }
-
- return ret;
-}
-#endif
-static const struct dev_pm_ops adi_spi_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(adi_spi_suspend, adi_spi_resume)
-};
-
-MODULE_ALIAS("platform:adi-spi3");
-static struct platform_driver adi_spi_driver = {
- .driver = {
- .name = "adi-spi3",
- .pm = &adi_spi_pm_ops,
- },
- .remove = adi_spi_remove,
-};
-
-module_platform_driver_probe(adi_spi_driver, adi_spi_probe);
-
-MODULE_DESCRIPTION("Analog Devices SPI3 controller driver");
-MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
index 4a11fc0d4136..3f890d162934 100644
--- a/drivers/spi/spi-atmel.c
+++ b/drivers/spi/spi-atmel.c
@@ -768,14 +768,14 @@ static int atmel_spi_next_xfer_dma_submit(struct spi_master *master,
rxdesc = dmaengine_prep_slave_single(rxchan,
as->dma_addr_rx_bbuf,
xfer->len,
- DMA_FROM_DEVICE,
+ DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT |
DMA_CTRL_ACK);
} else {
rxdesc = dmaengine_prep_slave_sg(rxchan,
xfer->rx_sg.sgl,
xfer->rx_sg.nents,
- DMA_FROM_DEVICE,
+ DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT |
DMA_CTRL_ACK);
}
@@ -787,14 +787,14 @@ static int atmel_spi_next_xfer_dma_submit(struct spi_master *master,
memcpy(as->addr_tx_bbuf, xfer->tx_buf, xfer->len);
txdesc = dmaengine_prep_slave_single(txchan,
as->dma_addr_tx_bbuf,
- xfer->len, DMA_TO_DEVICE,
+ xfer->len, DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT |
DMA_CTRL_ACK);
} else {
txdesc = dmaengine_prep_slave_sg(txchan,
xfer->tx_sg.sgl,
xfer->tx_sg.nents,
- DMA_TO_DEVICE,
+ DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT |
DMA_CTRL_ACK);
}
@@ -1512,6 +1512,11 @@ static void atmel_spi_init(struct atmel_spi *as)
{
spi_writel(as, CR, SPI_BIT(SWRST));
spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */
+
+ /* It is recommended to enable FIFOs first thing after reset */
+ if (as->fifo_size)
+ spi_writel(as, CR, SPI_BIT(FIFOEN));
+
if (as->caps.has_wdrbt) {
spi_writel(as, MR, SPI_BIT(WDRBT) | SPI_BIT(MODFDIS)
| SPI_BIT(MSTR));
@@ -1522,9 +1527,6 @@ static void atmel_spi_init(struct atmel_spi *as)
if (as->use_pdc)
spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));
spi_writel(as, CR, SPI_BIT(SPIEN));
-
- if (as->fifo_size)
- spi_writel(as, CR, SPI_BIT(FIFOEN));
}
static int atmel_spi_probe(struct platform_device *pdev)
diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c
index a172ab299e80..1596d35498c5 100644
--- a/drivers/spi/spi-bcm-qspi.c
+++ b/drivers/spi/spi-bcm-qspi.c
@@ -1247,7 +1247,7 @@ int bcm_qspi_probe(struct platform_device *pdev,
qspi->base[MSPI] = devm_ioremap_resource(dev, res);
if (IS_ERR(qspi->base[MSPI])) {
ret = PTR_ERR(qspi->base[MSPI]);
- goto qspi_probe_err;
+ goto qspi_resource_err;
}
} else {
goto qspi_resource_err;
@@ -1258,7 +1258,7 @@ int bcm_qspi_probe(struct platform_device *pdev,
qspi->base[BSPI] = devm_ioremap_resource(dev, res);
if (IS_ERR(qspi->base[BSPI])) {
ret = PTR_ERR(qspi->base[BSPI]);
- goto qspi_probe_err;
+ goto qspi_resource_err;
}
qspi->bspi_mode = true;
} else {
diff --git a/drivers/spi/spi-bcm2835aux.c b/drivers/spi/spi-bcm2835aux.c
index 7428091d3f5b..1431cb98fe40 100644
--- a/drivers/spi/spi-bcm2835aux.c
+++ b/drivers/spi/spi-bcm2835aux.c
@@ -321,7 +321,6 @@ static int bcm2835aux_spi_transfer_one(struct spi_master *master,
struct bcm2835aux_spi *bs = spi_master_get_devdata(master);
unsigned long spi_hz, clk_hz, speed;
unsigned long spi_used_hz;
- unsigned long long xfer_time_us;
/* calculate the registers to handle
*
@@ -358,20 +357,21 @@ static int bcm2835aux_spi_transfer_one(struct spi_master *master,
bs->rx_len = tfr->len;
bs->pending = 0;
- /* calculate the estimated time in us the transfer runs
- * note that there are are 2 idle clocks after each
- * chunk getting transferred - in our case the chunk size
- * is 3 bytes, so we approximate this by 9 bits/byte
+ /* Calculate the estimated time in us the transfer runs. Note that
+ * there are are 2 idle clocks cycles after each chunk getting
+ * transferred - in our case the chunk size is 3 bytes, so we
+ * approximate this by 9 cycles/byte. This is used to find the number
+ * of Hz per byte per polling limit. E.g., we can transfer 1 byte in
+ * 30 µs per 300,000 Hz of bus clock.
*/
- xfer_time_us = tfr->len * 9 * 1000000;
- do_div(xfer_time_us, spi_used_hz);
-
+#define HZ_PER_BYTE ((9 * 1000000) / BCM2835_AUX_SPI_POLLING_LIMIT_US)
/* run in polling mode for short transfers */
- if (xfer_time_us < BCM2835_AUX_SPI_POLLING_LIMIT_US)
+ if (tfr->len < spi_used_hz / HZ_PER_BYTE)
return bcm2835aux_spi_transfer_one_poll(master, spi, tfr);
/* run in interrupt mode for all others */
return bcm2835aux_spi_transfer_one_irq(master, spi, tfr);
+#undef HZ_PER_BYTE
}
static int bcm2835aux_spi_prepare_message(struct spi_master *master,
diff --git a/drivers/spi/spi-bfin-sport.c b/drivers/spi/spi-bfin-sport.c
deleted file mode 100644
index 01d0ba9c5942..000000000000
--- a/drivers/spi/spi-bfin-sport.c
+++ /dev/null
@@ -1,919 +0,0 @@
-/*
- * SPI bus via the Blackfin SPORT peripheral
- *
- * Enter bugs at http://blackfin.uclinux.org/
- *
- * Copyright 2009-2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
- */
-
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/gpio.h>
-#include <linux/io.h>
-#include <linux/ioport.h>
-#include <linux/irq.h>
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/spi/spi.h>
-#include <linux/workqueue.h>
-
-#include <asm/portmux.h>
-#include <asm/bfin5xx_spi.h>
-#include <asm/blackfin.h>
-#include <asm/bfin_sport.h>
-#include <asm/cacheflush.h>
-
-#define DRV_NAME "bfin-sport-spi"
-#define DRV_DESC "SPI bus via the Blackfin SPORT"
-
-MODULE_AUTHOR("Cliff Cai");
-MODULE_DESCRIPTION(DRV_DESC);
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:bfin-sport-spi");
-
-enum bfin_sport_spi_state {
- START_STATE,
- RUNNING_STATE,
- DONE_STATE,
- ERROR_STATE,
-};
-
-struct bfin_sport_spi_master_data;
-
-struct bfin_sport_transfer_ops {
- void (*write) (struct bfin_sport_spi_master_data *);
- void (*read) (struct bfin_sport_spi_master_data *);
- void (*duplex) (struct bfin_sport_spi_master_data *);
-};
-
-struct bfin_sport_spi_master_data {
- /* Driver model hookup */
- struct device *dev;
-
- /* SPI framework hookup */
- struct spi_master *master;
-
- /* Regs base of SPI controller */
- struct sport_register __iomem *regs;
- int err_irq;
-
- /* Pin request list */
- u16 *pin_req;
-
- struct work_struct pump_messages;
- spinlock_t lock;
- struct list_head queue;
- int busy;
- bool run;
-
- /* Message Transfer pump */
- struct tasklet_struct pump_transfers;
-
- /* Current message transfer state info */
- enum bfin_sport_spi_state state;
- struct spi_message *cur_msg;
- struct spi_transfer *cur_transfer;
- struct bfin_sport_spi_slave_data *cur_chip;
- union {
- void *tx;
- u8 *tx8;
- u16 *tx16;
- };
- void *tx_end;
- union {
- void *rx;
- u8 *rx8;
- u16 *rx16;
- };
- void *rx_end;
-
- int cs_change;
- struct bfin_sport_transfer_ops *ops;
-};
-
-struct bfin_sport_spi_slave_data {
- u16 ctl_reg;
- u16 baud;
- u16 cs_chg_udelay; /* Some devices require > 255usec delay */
- u32 cs_gpio;
- u16 idle_tx_val;
- struct bfin_sport_transfer_ops *ops;
-};
-
-static void
-bfin_sport_spi_enable(struct bfin_sport_spi_master_data *drv_data)
-{
- bfin_write_or(&drv_data->regs->tcr1, TSPEN);
- bfin_write_or(&drv_data->regs->rcr1, TSPEN);
- SSYNC();
-}
-
-static void
-bfin_sport_spi_disable(struct bfin_sport_spi_master_data *drv_data)
-{
- bfin_write_and(&drv_data->regs->tcr1, ~TSPEN);
- bfin_write_and(&drv_data->regs->rcr1, ~TSPEN);
- SSYNC();
-}
-
-/* Caculate the SPI_BAUD register value based on input HZ */
-static u16
-bfin_sport_hz_to_spi_baud(u32 speed_hz)
-{
- u_long clk, sclk = get_sclk();
- int div = (sclk / (2 * speed_hz)) - 1;
-
- if (div < 0)
- div = 0;
-
- clk = sclk / (2 * (div + 1));
-
- if (clk > speed_hz)
- div++;
-
- return div;
-}
-
-/* Chip select operation functions for cs_change flag */
-static void
-bfin_sport_spi_cs_active(struct bfin_sport_spi_slave_data *chip)
-{
- gpio_direction_output(chip->cs_gpio, 0);
-}
-
-static void
-bfin_sport_spi_cs_deactive(struct bfin_sport_spi_slave_data *chip)
-{
- gpio_direction_output(chip->cs_gpio, 1);
- /* Move delay here for consistency */
- if (chip->cs_chg_udelay)
- udelay(chip->cs_chg_udelay);
-}
-
-static void
-bfin_sport_spi_stat_poll_complete(struct bfin_sport_spi_master_data *drv_data)
-{
- unsigned long timeout = jiffies + HZ;
- while (!(bfin_read(&drv_data->regs->stat) & RXNE)) {
- if (!time_before(jiffies, timeout))
- break;
- }
-}
-
-static void
-bfin_sport_spi_u8_writer(struct bfin_sport_spi_master_data *drv_data)
-{
- u16 dummy;
-
- while (drv_data->tx < drv_data->tx_end) {
- bfin_write(&drv_data->regs->tx16, *drv_data->tx8++);
- bfin_sport_spi_stat_poll_complete(drv_data);
- dummy = bfin_read(&drv_data->regs->rx16);
- }
-}
-
-static void
-bfin_sport_spi_u8_reader(struct bfin_sport_spi_master_data *drv_data)
-{
- u16 tx_val = drv_data->cur_chip->idle_tx_val;
-
- while (drv_data->rx < drv_data->rx_end) {
- bfin_write(&drv_data->regs->tx16, tx_val);
- bfin_sport_spi_stat_poll_complete(drv_data);
- *drv_data->rx8++ = bfin_read(&drv_data->regs->rx16);
- }
-}
-
-static void
-bfin_sport_spi_u8_duplex(struct bfin_sport_spi_master_data *drv_data)
-{
- while (drv_data->rx < drv_data->rx_end) {
- bfin_write(&drv_data->regs->tx16, *drv_data->tx8++);
- bfin_sport_spi_stat_poll_complete(drv_data);
- *drv_data->rx8++ = bfin_read(&drv_data->regs->rx16);
- }
-}
-
-static struct bfin_sport_transfer_ops bfin_sport_transfer_ops_u8 = {
- .write = bfin_sport_spi_u8_writer,
- .read = bfin_sport_spi_u8_reader,
- .duplex = bfin_sport_spi_u8_duplex,
-};
-
-static void
-bfin_sport_spi_u16_writer(struct bfin_sport_spi_master_data *drv_data)
-{
- u16 dummy;
-
- while (drv_data->tx < drv_data->tx_end) {
- bfin_write(&drv_data->regs->tx16, *drv_data->tx16++);
- bfin_sport_spi_stat_poll_complete(drv_data);
- dummy = bfin_read(&drv_data->regs->rx16);
- }
-}
-
-static void
-bfin_sport_spi_u16_reader(struct bfin_sport_spi_master_data *drv_data)
-{
- u16 tx_val = drv_data->cur_chip->idle_tx_val;
-
- while (drv_data->rx < drv_data->rx_end) {
- bfin_write(&drv_data->regs->tx16, tx_val);
- bfin_sport_spi_stat_poll_complete(drv_data);
- *drv_data->rx16++ = bfin_read(&drv_data->regs->rx16);
- }
-}
-
-static void
-bfin_sport_spi_u16_duplex(struct bfin_sport_spi_master_data *drv_data)
-{
- while (drv_data->rx < drv_data->rx_end) {
- bfin_write(&drv_data->regs->tx16, *drv_data->tx16++);
- bfin_sport_spi_stat_poll_complete(drv_data);
- *drv_data->rx16++ = bfin_read(&drv_data->regs->rx16);
- }
-}
-
-static struct bfin_sport_transfer_ops bfin_sport_transfer_ops_u16 = {
- .write = bfin_sport_spi_u16_writer,
- .read = bfin_sport_spi_u16_reader,
- .duplex = bfin_sport_spi_u16_duplex,
-};
-
-/* stop controller and re-config current chip */
-static void
-bfin_sport_spi_restore_state(struct bfin_sport_spi_master_data *drv_data)
-{
- struct bfin_sport_spi_slave_data *chip = drv_data->cur_chip;
-
- bfin_sport_spi_disable(drv_data);
- dev_dbg(drv_data->dev, "restoring spi ctl state\n");
-
- bfin_write(&drv_data->regs->tcr1, chip->ctl_reg);
- bfin_write(&drv_data->regs->tclkdiv, chip->baud);
- SSYNC();
-
- bfin_write(&drv_data->regs->rcr1, chip->ctl_reg & ~(ITCLK | ITFS));
- SSYNC();
-
- bfin_sport_spi_cs_active(chip);
-}
-
-/* test if there is more transfer to be done */
-static enum bfin_sport_spi_state
-bfin_sport_spi_next_transfer(struct bfin_sport_spi_master_data *drv_data)
-{
- struct spi_message *msg = drv_data->cur_msg;
- struct spi_transfer *trans = drv_data->cur_transfer;
-
- /* Move to next transfer */
- if (trans->transfer_list.next != &msg->transfers) {
- drv_data->cur_transfer =
- list_entry(trans->transfer_list.next,
- struct spi_transfer, transfer_list);
- return RUNNING_STATE;
- }
-
- return DONE_STATE;
-}
-
-/*
- * caller already set message->status;
- * dma and pio irqs are blocked give finished message back
- */
-static void
-bfin_sport_spi_giveback(struct bfin_sport_spi_master_data *drv_data)
-{
- struct bfin_sport_spi_slave_data *chip = drv_data->cur_chip;
- unsigned long flags;
- struct spi_message *msg;
-
- spin_lock_irqsave(&drv_data->lock, flags);
- msg = drv_data->cur_msg;
- drv_data->state = START_STATE;
- drv_data->cur_msg = NULL;
- drv_data->cur_transfer = NULL;
- drv_data->cur_chip = NULL;
- schedule_work(&drv_data->pump_messages);
- spin_unlock_irqrestore(&drv_data->lock, flags);
-
- if (!drv_data->cs_change)
- bfin_sport_spi_cs_deactive(chip);
-
- if (msg->complete)
- msg->complete(msg->context);
-}
-
-static irqreturn_t
-sport_err_handler(int irq, void *dev_id)
-{
- struct bfin_sport_spi_master_data *drv_data = dev_id;
- u16 status;
-
- dev_dbg(drv_data->dev, "%s enter\n", __func__);
- status = bfin_read(&drv_data->regs->stat) & (TOVF | TUVF | ROVF | RUVF);
-
- if (status) {
- bfin_write(&drv_data->regs->stat, status);
- SSYNC();
-
- bfin_sport_spi_disable(drv_data);
- dev_err(drv_data->dev, "status error:%s%s%s%s\n",
- status & TOVF ? " TOVF" : "",
- status & TUVF ? " TUVF" : "",
- status & ROVF ? " ROVF" : "",
- status & RUVF ? " RUVF" : "");
- }
-
- return IRQ_HANDLED;
-}
-
-static void
-bfin_sport_spi_pump_transfers(unsigned long data)
-{
- struct bfin_sport_spi_master_data *drv_data = (void *)data;
- struct spi_message *message = NULL;
- struct spi_transfer *transfer = NULL;
- struct spi_transfer *previous = NULL;
- struct bfin_sport_spi_slave_data *chip = NULL;
- unsigned int bits_per_word;
- u32 tranf_success = 1;
- u32 transfer_speed;
- u8 full_duplex = 0;
-
- /* Get current state information */
- message = drv_data->cur_msg;
- transfer = drv_data->cur_transfer;
- chip = drv_data->cur_chip;
-
- transfer_speed = bfin_sport_hz_to_spi_baud(transfer->speed_hz);
- bfin_write(&drv_data->regs->tclkdiv, transfer_speed);
- SSYNC();
-
- /*
- * if msg is error or done, report it back using complete() callback
- */
-
- /* Handle for abort */
- if (drv_data->state == ERROR_STATE) {
- dev_dbg(drv_data->dev, "transfer: we've hit an error\n");
- message->status = -EIO;
- bfin_sport_spi_giveback(drv_data);
- return;
- }
-
- /* Handle end of message */
- if (drv_data->state == DONE_STATE) {
- dev_dbg(drv_data->dev, "transfer: all done!\n");
- message->status = 0;
- bfin_sport_spi_giveback(drv_data);
- return;
- }
-
- /* Delay if requested at end of transfer */
- if (drv_data->state == RUNNING_STATE) {
- dev_dbg(drv_data->dev, "transfer: still running ...\n");
- previous = list_entry(transfer->transfer_list.prev,
- struct spi_transfer, transfer_list);
- if (previous->delay_usecs)
- udelay(previous->delay_usecs);
- }
-
- if (transfer->len == 0) {
- /* Move to next transfer of this msg */
- drv_data->state = bfin_sport_spi_next_transfer(drv_data);
- /* Schedule next transfer tasklet */
- tasklet_schedule(&drv_data->pump_transfers);
- }
-
- if (transfer->tx_buf != NULL) {
- drv_data->tx = (void *)transfer->tx_buf;
- drv_data->tx_end = drv_data->tx + transfer->len;
- dev_dbg(drv_data->dev, "tx_buf is %p, tx_end is %p\n",
- transfer->tx_buf, drv_data->tx_end);
- } else
- drv_data->tx = NULL;
-
- if (transfer->rx_buf != NULL) {
- full_duplex = transfer->tx_buf != NULL;
- drv_data->rx = transfer->rx_buf;
- drv_data->rx_end = drv_data->rx + transfer->len;
- dev_dbg(drv_data->dev, "rx_buf is %p, rx_end is %p\n",
- transfer->rx_buf, drv_data->rx_end);
- } else
- drv_data->rx = NULL;
-
- drv_data->cs_change = transfer->cs_change;
-
- /* Bits per word setup */
- bits_per_word = transfer->bits_per_word;
- if (bits_per_word == 16)
- drv_data->ops = &bfin_sport_transfer_ops_u16;
- else
- drv_data->ops = &bfin_sport_transfer_ops_u8;
- bfin_write(&drv_data->regs->tcr2, bits_per_word - 1);
- bfin_write(&drv_data->regs->tfsdiv, bits_per_word - 1);
- bfin_write(&drv_data->regs->rcr2, bits_per_word - 1);
-
- drv_data->state = RUNNING_STATE;
-
- if (drv_data->cs_change)
- bfin_sport_spi_cs_active(chip);
-
- dev_dbg(drv_data->dev,
- "now pumping a transfer: width is %d, len is %d\n",
- bits_per_word, transfer->len);
-
- /* PIO mode write then read */
- dev_dbg(drv_data->dev, "doing IO transfer\n");
-
- bfin_sport_spi_enable(drv_data);
- if (full_duplex) {
- /* full duplex mode */
- BUG_ON((drv_data->tx_end - drv_data->tx) !=
- (drv_data->rx_end - drv_data->rx));
- drv_data->ops->duplex(drv_data);
-
- if (drv_data->tx != drv_data->tx_end)
- tranf_success = 0;
- } else if (drv_data->tx != NULL) {
- /* write only half duplex */
-
- drv_data->ops->write(drv_data);
-
- if (drv_data->tx != drv_data->tx_end)
- tranf_success = 0;
- } else if (drv_data->rx != NULL) {
- /* read only half duplex */
-
- drv_data->ops->read(drv_data);
- if (drv_data->rx != drv_data->rx_end)
- tranf_success = 0;
- }
- bfin_sport_spi_disable(drv_data);
-
- if (!tranf_success) {
- dev_dbg(drv_data->dev, "IO write error!\n");
- drv_data->state = ERROR_STATE;
- } else {
- /* Update total byte transferred */
- message->actual_length += transfer->len;
- /* Move to next transfer of this msg */
- drv_data->state = bfin_sport_spi_next_transfer(drv_data);
- if (drv_data->cs_change)
- bfin_sport_spi_cs_deactive(chip);
- }
-
- /* Schedule next transfer tasklet */
- tasklet_schedule(&drv_data->pump_transfers);
-}
-
-/* pop a msg from queue and kick off real transfer */
-static void
-bfin_sport_spi_pump_messages(struct work_struct *work)
-{
- struct bfin_sport_spi_master_data *drv_data;
- unsigned long flags;
- struct spi_message *next_msg;
-
- drv_data = container_of(work, struct bfin_sport_spi_master_data, pump_messages);
-
- /* Lock queue and check for queue work */
- spin_lock_irqsave(&drv_data->lock, flags);
- if (list_empty(&drv_data->queue) || !drv_data->run) {
- /* pumper kicked off but no work to do */
- drv_data->busy = 0;
- spin_unlock_irqrestore(&drv_data->lock, flags);
- return;
- }
-
- /* Make sure we are not already running a message */
- if (drv_data->cur_msg) {
- spin_unlock_irqrestore(&drv_data->lock, flags);
- return;
- }
-
- /* Extract head of queue */
- next_msg = list_entry(drv_data->queue.next,
- struct spi_message, queue);
-
- drv_data->cur_msg = next_msg;
-
- /* Setup the SSP using the per chip configuration */
- drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi);
-
- list_del_init(&drv_data->cur_msg->queue);
-
- /* Initialize message state */
- drv_data->cur_msg->state = START_STATE;
- drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next,
- struct spi_transfer, transfer_list);
- bfin_sport_spi_restore_state(drv_data);
- dev_dbg(drv_data->dev, "got a message to pump, "
- "state is set to: baud %d, cs_gpio %i, ctl 0x%x\n",
- drv_data->cur_chip->baud, drv_data->cur_chip->cs_gpio,
- drv_data->cur_chip->ctl_reg);
-
- dev_dbg(drv_data->dev,
- "the first transfer len is %d\n",
- drv_data->cur_transfer->len);
-
- /* Mark as busy and launch transfers */
- tasklet_schedule(&drv_data->pump_transfers);
-
- drv_data->busy = 1;
- spin_unlock_irqrestore(&drv_data->lock, flags);
-}
-
-/*
- * got a msg to transfer, queue it in drv_data->queue.
- * And kick off message pumper
- */
-static int
-bfin_sport_spi_transfer(struct spi_device *spi, struct spi_message *msg)
-{
- struct bfin_sport_spi_master_data *drv_data = spi_master_get_devdata(spi->master);
- unsigned long flags;
-
- spin_lock_irqsave(&drv_data->lock, flags);
-
- if (!drv_data->run) {
- spin_unlock_irqrestore(&drv_data->lock, flags);
- return -ESHUTDOWN;
- }
-
- msg->actual_length = 0;
- msg->status = -EINPROGRESS;
- msg->state = START_STATE;
-
- dev_dbg(&spi->dev, "adding an msg in transfer()\n");
- list_add_tail(&msg->queue, &drv_data->queue);
-
- if (drv_data->run && !drv_data->busy)
- schedule_work(&drv_data->pump_messages);
-
- spin_unlock_irqrestore(&drv_data->lock, flags);
-
- return 0;
-}
-
-/* Called every time common spi devices change state */
-static int
-bfin_sport_spi_setup(struct spi_device *spi)
-{
- struct bfin_sport_spi_slave_data *chip, *first = NULL;
- int ret;
-
- /* Only alloc (or use chip_info) on first setup */
- chip = spi_get_ctldata(spi);
- if (chip == NULL) {
- struct bfin5xx_spi_chip *chip_info;
-
- chip = first = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (!chip)
- return -ENOMEM;
-
- /* platform chip_info isn't required */
- chip_info = spi->controller_data;
- if (chip_info) {
- /*
- * DITFS and TDTYPE are only thing we don't set, but
- * they probably shouldn't be changed by people.
- */
- if (chip_info->ctl_reg || chip_info->enable_dma) {
- ret = -EINVAL;
- dev_err(&spi->dev, "don't set ctl_reg/enable_dma fields\n");
- goto error;
- }
- chip->cs_chg_udelay = chip_info->cs_chg_udelay;
- chip->idle_tx_val = chip_info->idle_tx_val;
- }
- }
-
- /* translate common spi framework into our register
- * following configure contents are same for tx and rx.
- */
-
- if (spi->mode & SPI_CPHA)
- chip->ctl_reg &= ~TCKFE;
- else
- chip->ctl_reg |= TCKFE;
-
- if (spi->mode & SPI_LSB_FIRST)
- chip->ctl_reg |= TLSBIT;
- else
- chip->ctl_reg &= ~TLSBIT;
-
- /* Sport in master mode */
- chip->ctl_reg |= ITCLK | ITFS | TFSR | LATFS | LTFS;
-
- chip->baud = bfin_sport_hz_to_spi_baud(spi->max_speed_hz);
-
- chip->cs_gpio = spi->chip_select;
- ret = gpio_request(chip->cs_gpio, spi->modalias);
- if (ret)
- goto error;
-
- dev_dbg(&spi->dev, "setup spi chip %s, width is %d\n",
- spi->modalias, spi->bits_per_word);
- dev_dbg(&spi->dev, "ctl_reg is 0x%x, GPIO is %i\n",
- chip->ctl_reg, spi->chip_select);
-
- spi_set_ctldata(spi, chip);
-
- bfin_sport_spi_cs_deactive(chip);
-
- return ret;
-
- error:
- kfree(first);
- return ret;
-}
-
-/*
- * callback for spi framework.
- * clean driver specific data
- */
-static void
-bfin_sport_spi_cleanup(struct spi_device *spi)
-{
- struct bfin_sport_spi_slave_data *chip = spi_get_ctldata(spi);
-
- if (!chip)
- return;
-
- gpio_free(chip->cs_gpio);
-
- kfree(chip);
-}
-
-static int
-bfin_sport_spi_init_queue(struct bfin_sport_spi_master_data *drv_data)
-{
- INIT_LIST_HEAD(&drv_data->queue);
- spin_lock_init(&drv_data->lock);
-
- drv_data->run = false;
- drv_data->busy = 0;
-
- /* init transfer tasklet */
- tasklet_init(&drv_data->pump_transfers,
- bfin_sport_spi_pump_transfers, (unsigned long)drv_data);
-
- INIT_WORK(&drv_data->pump_messages, bfin_sport_spi_pump_messages);
-
- return 0;
-}
-
-static int
-bfin_sport_spi_start_queue(struct bfin_sport_spi_master_data *drv_data)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&drv_data->lock, flags);
-
- if (drv_data->run || drv_data->busy) {
- spin_unlock_irqrestore(&drv_data->lock, flags);
- return -EBUSY;
- }
-
- drv_data->run = true;
- drv_data->cur_msg = NULL;
- drv_data->cur_transfer = NULL;
- drv_data->cur_chip = NULL;
- spin_unlock_irqrestore(&drv_data->lock, flags);
-
- schedule_work(&drv_data->pump_messages);
-
- return 0;
-}
-
-static inline int
-bfin_sport_spi_stop_queue(struct bfin_sport_spi_master_data *drv_data)
-{
- unsigned long flags;
- unsigned limit = 500;
- int status = 0;
-
- spin_lock_irqsave(&drv_data->lock, flags);
-
- /*
- * This is a bit lame, but is optimized for the common execution path.
- * A wait_queue on the drv_data->busy could be used, but then the common
- * execution path (pump_messages) would be required to call wake_up or
- * friends on every SPI message. Do this instead
- */
- drv_data->run = false;
- while (!list_empty(&drv_data->queue) && drv_data->busy && limit--) {
- spin_unlock_irqrestore(&drv_data->lock, flags);
- msleep(10);
- spin_lock_irqsave(&drv_data->lock, flags);
- }
-
- if (!list_empty(&drv_data->queue) || drv_data->busy)
- status = -EBUSY;
-
- spin_unlock_irqrestore(&drv_data->lock, flags);
-
- return status;
-}
-
-static inline int
-bfin_sport_spi_destroy_queue(struct bfin_sport_spi_master_data *drv_data)
-{
- int status;
-
- status = bfin_sport_spi_stop_queue(drv_data);
- if (status)
- return status;
-
- flush_work(&drv_data->pump_messages);
-
- return 0;
-}
-
-static int bfin_sport_spi_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct bfin5xx_spi_master *platform_info;
- struct spi_master *master;
- struct resource *res, *ires;
- struct bfin_sport_spi_master_data *drv_data;
- int status;
-
- platform_info = dev_get_platdata(dev);
-
- /* Allocate master with space for drv_data */
- master = spi_alloc_master(dev, sizeof(*master) + 16);
- if (!master) {
- dev_err(dev, "cannot alloc spi_master\n");
- return -ENOMEM;
- }
-
- drv_data = spi_master_get_devdata(master);
- drv_data->master = master;
- drv_data->dev = dev;
- drv_data->pin_req = platform_info->pin_req;
-
- master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
- master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16);
- master->bus_num = pdev->id;
- master->num_chipselect = platform_info->num_chipselect;
- master->cleanup = bfin_sport_spi_cleanup;
- master->setup = bfin_sport_spi_setup;
- master->transfer = bfin_sport_spi_transfer;
-
- /* Find and map our resources */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
- dev_err(dev, "cannot get IORESOURCE_MEM\n");
- status = -ENOENT;
- goto out_error_get_res;
- }
-
- drv_data->regs = ioremap(res->start, resource_size(res));
- if (drv_data->regs == NULL) {
- dev_err(dev, "cannot map registers\n");
- status = -ENXIO;
- goto out_error_ioremap;
- }
-
- ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!ires) {
- dev_err(dev, "cannot get IORESOURCE_IRQ\n");
- status = -ENODEV;
- goto out_error_get_ires;
- }
- drv_data->err_irq = ires->start;
-
- /* Initial and start queue */
- status = bfin_sport_spi_init_queue(drv_data);
- if (status) {
- dev_err(dev, "problem initializing queue\n");
- goto out_error_queue_alloc;
- }
-
- status = bfin_sport_spi_start_queue(drv_data);
- if (status) {
- dev_err(dev, "problem starting queue\n");
- goto out_error_queue_alloc;
- }
-
- status = request_irq(drv_data->err_irq, sport_err_handler,
- 0, "sport_spi_err", drv_data);
- if (status) {
- dev_err(dev, "unable to request sport err irq\n");
- goto out_error_irq;
- }
-
- status = peripheral_request_list(drv_data->pin_req, DRV_NAME);
- if (status) {
- dev_err(dev, "requesting peripherals failed\n");
- goto out_error_peripheral;
- }
-
- /* Register with the SPI framework */
- platform_set_drvdata(pdev, drv_data);
- status = spi_register_master(master);
- if (status) {
- dev_err(dev, "problem registering spi master\n");
- goto out_error_master;
- }
-
- dev_info(dev, "%s, regs_base@%p\n", DRV_DESC, drv_data->regs);
- return 0;
-
- out_error_master:
- peripheral_free_list(drv_data->pin_req);
- out_error_peripheral:
- free_irq(drv_data->err_irq, drv_data);
- out_error_irq:
- out_error_queue_alloc:
- bfin_sport_spi_destroy_queue(drv_data);
- out_error_get_ires:
- iounmap(drv_data->regs);
- out_error_ioremap:
- out_error_get_res:
- spi_master_put(master);
-
- return status;
-}
-
-/* stop hardware and remove the driver */
-static int bfin_sport_spi_remove(struct platform_device *pdev)
-{
- struct bfin_sport_spi_master_data *drv_data = platform_get_drvdata(pdev);
- int status = 0;
-
- if (!drv_data)
- return 0;
-
- /* Remove the queue */
- status = bfin_sport_spi_destroy_queue(drv_data);
- if (status)
- return status;
-
- /* Disable the SSP at the peripheral and SOC level */
- bfin_sport_spi_disable(drv_data);
-
- /* Disconnect from the SPI framework */
- spi_unregister_master(drv_data->master);
-
- peripheral_free_list(drv_data->pin_req);
-
- return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int bfin_sport_spi_suspend(struct device *dev)
-{
- struct bfin_sport_spi_master_data *drv_data = dev_get_drvdata(dev);
- int status;
-
- status = bfin_sport_spi_stop_queue(drv_data);
- if (status)
- return status;
-
- /* stop hardware */
- bfin_sport_spi_disable(drv_data);
-
- return status;
-}
-
-static int bfin_sport_spi_resume(struct device *dev)
-{
- struct bfin_sport_spi_master_data *drv_data = dev_get_drvdata(dev);
- int status;
-
- /* Enable the SPI interface */
- bfin_sport_spi_enable(drv_data);
-
- /* Start the queue running */
- status = bfin_sport_spi_start_queue(drv_data);
- if (status)
- dev_err(drv_data->dev, "problem resuming queue\n");
-
- return status;
-}
-
-static SIMPLE_DEV_PM_OPS(bfin_sport_spi_pm_ops, bfin_sport_spi_suspend,
- bfin_sport_spi_resume);
-
-#define BFIN_SPORT_SPI_PM_OPS (&bfin_sport_spi_pm_ops)
-#else
-#define BFIN_SPORT_SPI_PM_OPS NULL
-#endif
-
-static struct platform_driver bfin_sport_spi_driver = {
- .driver = {
- .name = DRV_NAME,
- .pm = BFIN_SPORT_SPI_PM_OPS,
- },
- .probe = bfin_sport_spi_probe,
- .remove = bfin_sport_spi_remove,
-};
-module_platform_driver(bfin_sport_spi_driver);
diff --git a/drivers/spi/spi-bfin5xx.c b/drivers/spi/spi-bfin5xx.c
deleted file mode 100644
index 249c7a3677c9..000000000000
--- a/drivers/spi/spi-bfin5xx.c
+++ /dev/null
@@ -1,1462 +0,0 @@
-/*
- * Blackfin On-Chip SPI Driver
- *
- * Copyright 2004-2010 Analog Devices Inc.
- *
- * Enter bugs at http://blackfin.uclinux.org/
- *
- * Licensed under the GPL-2 or later.
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/gpio.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-#include <linux/ioport.h>
-#include <linux/irq.h>
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/dma-mapping.h>
-#include <linux/spi/spi.h>
-#include <linux/workqueue.h>
-
-#include <asm/dma.h>
-#include <asm/portmux.h>
-#include <asm/bfin5xx_spi.h>
-#include <asm/cacheflush.h>
-
-#define DRV_NAME "bfin-spi"
-#define DRV_AUTHOR "Bryan Wu, Luke Yang"
-#define DRV_DESC "Blackfin on-chip SPI Controller Driver"
-#define DRV_VERSION "1.0"
-
-MODULE_AUTHOR(DRV_AUTHOR);
-MODULE_DESCRIPTION(DRV_DESC);
-MODULE_LICENSE("GPL");
-
-#define START_STATE ((void *)0)
-#define RUNNING_STATE ((void *)1)
-#define DONE_STATE ((void *)2)
-#define ERROR_STATE ((void *)-1)
-
-struct bfin_spi_master_data;
-
-struct bfin_spi_transfer_ops {
- void (*write) (struct bfin_spi_master_data *);
- void (*read) (struct bfin_spi_master_data *);
- void (*duplex) (struct bfin_spi_master_data *);
-};
-
-struct bfin_spi_master_data {
- /* Driver model hookup */
- struct platform_device *pdev;
-
- /* SPI framework hookup */
- struct spi_master *master;
-
- /* Regs base of SPI controller */
- struct bfin_spi_regs __iomem *regs;
-
- /* Pin request list */
- u16 *pin_req;
-
- /* BFIN hookup */
- struct bfin5xx_spi_master *master_info;
-
- struct work_struct pump_messages;
- spinlock_t lock;
- struct list_head queue;
- int busy;
- bool running;
-
- /* Message Transfer pump */
- struct tasklet_struct pump_transfers;
-
- /* Current message transfer state info */
- struct spi_message *cur_msg;
- struct spi_transfer *cur_transfer;
- struct bfin_spi_slave_data *cur_chip;
- size_t len_in_bytes;
- size_t len;
- void *tx;
- void *tx_end;
- void *rx;
- void *rx_end;
-
- /* DMA stuffs */
- int dma_channel;
- int dma_mapped;
- int dma_requested;
- dma_addr_t rx_dma;
- dma_addr_t tx_dma;
-
- int irq_requested;
- int spi_irq;
-
- size_t rx_map_len;
- size_t tx_map_len;
- u8 n_bytes;
- u16 ctrl_reg;
- u16 flag_reg;
-
- int cs_change;
- const struct bfin_spi_transfer_ops *ops;
-};
-
-struct bfin_spi_slave_data {
- u16 ctl_reg;
- u16 baud;
- u16 flag;
-
- u8 chip_select_num;
- u8 enable_dma;
- u16 cs_chg_udelay; /* Some devices require > 255usec delay */
- u32 cs_gpio;
- u16 idle_tx_val;
- u8 pio_interrupt; /* use spi data irq */
- const struct bfin_spi_transfer_ops *ops;
-};
-
-static void bfin_spi_enable(struct bfin_spi_master_data *drv_data)
-{
- bfin_write_or(&drv_data->regs->ctl, BIT_CTL_ENABLE);
-}
-
-static void bfin_spi_disable(struct bfin_spi_master_data *drv_data)
-{
- bfin_write_and(&drv_data->regs->ctl, ~BIT_CTL_ENABLE);
-}
-
-/* Caculate the SPI_BAUD register value based on input HZ */
-static u16 hz_to_spi_baud(u32 speed_hz)
-{
- u_long sclk = get_sclk();
- u16 spi_baud = (sclk / (2 * speed_hz));
-
- if ((sclk % (2 * speed_hz)) > 0)
- spi_baud++;
-
- if (spi_baud < MIN_SPI_BAUD_VAL)
- spi_baud = MIN_SPI_BAUD_VAL;
-
- return spi_baud;
-}
-
-static int bfin_spi_flush(struct bfin_spi_master_data *drv_data)
-{
- unsigned long limit = loops_per_jiffy << 1;
-
- /* wait for stop and clear stat */
- while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_SPIF) && --limit)
- cpu_relax();
-
- bfin_write(&drv_data->regs->stat, BIT_STAT_CLR);
-
- return limit;
-}
-
-/* Chip select operation functions for cs_change flag */
-static void bfin_spi_cs_active(struct bfin_spi_master_data *drv_data, struct bfin_spi_slave_data *chip)
-{
- if (likely(chip->chip_select_num < MAX_CTRL_CS))
- bfin_write_and(&drv_data->regs->flg, ~chip->flag);
- else
- gpio_set_value(chip->cs_gpio, 0);
-}
-
-static void bfin_spi_cs_deactive(struct bfin_spi_master_data *drv_data,
- struct bfin_spi_slave_data *chip)
-{
- if (likely(chip->chip_select_num < MAX_CTRL_CS))
- bfin_write_or(&drv_data->regs->flg, chip->flag);
- else
- gpio_set_value(chip->cs_gpio, 1);
-
- /* Move delay here for consistency */
- if (chip->cs_chg_udelay)
- udelay(chip->cs_chg_udelay);
-}
-
-/* enable or disable the pin muxed by GPIO and SPI CS to work as SPI CS */
-static inline void bfin_spi_cs_enable(struct bfin_spi_master_data *drv_data,
- struct bfin_spi_slave_data *chip)
-{
- if (chip->chip_select_num < MAX_CTRL_CS)
- bfin_write_or(&drv_data->regs->flg, chip->flag >> 8);
-}
-
-static inline void bfin_spi_cs_disable(struct bfin_spi_master_data *drv_data,
- struct bfin_spi_slave_data *chip)
-{
- if (chip->chip_select_num < MAX_CTRL_CS)
- bfin_write_and(&drv_data->regs->flg, ~(chip->flag >> 8));
-}
-
-/* stop controller and re-config current chip*/
-static void bfin_spi_restore_state(struct bfin_spi_master_data *drv_data)
-{
- struct bfin_spi_slave_data *chip = drv_data->cur_chip;
-
- /* Clear status and disable clock */
- bfin_write(&drv_data->regs->stat, BIT_STAT_CLR);
- bfin_spi_disable(drv_data);
- dev_dbg(&drv_data->pdev->dev, "restoring spi ctl state\n");
-
- SSYNC();
-
- /* Load the registers */
- bfin_write(&drv_data->regs->ctl, chip->ctl_reg);
- bfin_write(&drv_data->regs->baud, chip->baud);
-
- bfin_spi_enable(drv_data);
- bfin_spi_cs_active(drv_data, chip);
-}
-
-/* used to kick off transfer in rx mode and read unwanted RX data */
-static inline void bfin_spi_dummy_read(struct bfin_spi_master_data *drv_data)
-{
- (void) bfin_read(&drv_data->regs->rdbr);
-}
-
-static void bfin_spi_u8_writer(struct bfin_spi_master_data *drv_data)
-{
- /* clear RXS (we check for RXS inside the loop) */
- bfin_spi_dummy_read(drv_data);
-
- while (drv_data->tx < drv_data->tx_end) {
- bfin_write(&drv_data->regs->tdbr, (*(u8 *) (drv_data->tx++)));
- /* wait until transfer finished.
- checking SPIF or TXS may not guarantee transfer completion */
- while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_RXS))
- cpu_relax();
- /* discard RX data and clear RXS */
- bfin_spi_dummy_read(drv_data);
- }
-}
-
-static void bfin_spi_u8_reader(struct bfin_spi_master_data *drv_data)
-{
- u16 tx_val = drv_data->cur_chip->idle_tx_val;
-
- /* discard old RX data and clear RXS */
- bfin_spi_dummy_read(drv_data);
-
- while (drv_data->rx < drv_data->rx_end) {
- bfin_write(&drv_data->regs->tdbr, tx_val);
- while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_RXS))
- cpu_relax();
- *(u8 *) (drv_data->rx++) = bfin_read(&drv_data->regs->rdbr);
- }
-}
-
-static void bfin_spi_u8_duplex(struct bfin_spi_master_data *drv_data)
-{
- /* discard old RX data and clear RXS */
- bfin_spi_dummy_read(drv_data);
-
- while (drv_data->rx < drv_data->rx_end) {
- bfin_write(&drv_data->regs->tdbr, (*(u8 *) (drv_data->tx++)));
- while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_RXS))
- cpu_relax();
- *(u8 *) (drv_data->rx++) = bfin_read(&drv_data->regs->rdbr);
- }
-}
-
-static const struct bfin_spi_transfer_ops bfin_bfin_spi_transfer_ops_u8 = {
- .write = bfin_spi_u8_writer,
- .read = bfin_spi_u8_reader,
- .duplex = bfin_spi_u8_duplex,
-};
-
-static void bfin_spi_u16_writer(struct bfin_spi_master_data *drv_data)
-{
- /* clear RXS (we check for RXS inside the loop) */
- bfin_spi_dummy_read(drv_data);
-
- while (drv_data->tx < drv_data->tx_end) {
- bfin_write(&drv_data->regs->tdbr, (*(u16 *) (drv_data->tx)));
- drv_data->tx += 2;
- /* wait until transfer finished.
- checking SPIF or TXS may not guarantee transfer completion */
- while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_RXS))
- cpu_relax();
- /* discard RX data and clear RXS */
- bfin_spi_dummy_read(drv_data);
- }
-}
-
-static void bfin_spi_u16_reader(struct bfin_spi_master_data *drv_data)
-{
- u16 tx_val = drv_data->cur_chip->idle_tx_val;
-
- /* discard old RX data and clear RXS */
- bfin_spi_dummy_read(drv_data);
-
- while (drv_data->rx < drv_data->rx_end) {
- bfin_write(&drv_data->regs->tdbr, tx_val);
- while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_RXS))
- cpu_relax();
- *(u16 *) (drv_data->rx) = bfin_read(&drv_data->regs->rdbr);
- drv_data->rx += 2;
- }
-}
-
-static void bfin_spi_u16_duplex(struct bfin_spi_master_data *drv_data)
-{
- /* discard old RX data and clear RXS */
- bfin_spi_dummy_read(drv_data);
-
- while (drv_data->rx < drv_data->rx_end) {
- bfin_write(&drv_data->regs->tdbr, (*(u16 *) (drv_data->tx)));
- drv_data->tx += 2;
- while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_RXS))
- cpu_relax();
- *(u16 *) (drv_data->rx) = bfin_read(&drv_data->regs->rdbr);
- drv_data->rx += 2;
- }
-}
-
-static const struct bfin_spi_transfer_ops bfin_bfin_spi_transfer_ops_u16 = {
- .write = bfin_spi_u16_writer,
- .read = bfin_spi_u16_reader,
- .duplex = bfin_spi_u16_duplex,
-};
-
-/* test if there is more transfer to be done */
-static void *bfin_spi_next_transfer(struct bfin_spi_master_data *drv_data)
-{
- struct spi_message *msg = drv_data->cur_msg;
- struct spi_transfer *trans = drv_data->cur_transfer;
-
- /* Move to next transfer */
- if (trans->transfer_list.next != &msg->transfers) {
- drv_data->cur_transfer =
- list_entry(trans->transfer_list.next,
- struct spi_transfer, transfer_list);
- return RUNNING_STATE;
- } else
- return DONE_STATE;
-}
-
-/*
- * caller already set message->status;
- * dma and pio irqs are blocked give finished message back
- */
-static void bfin_spi_giveback(struct bfin_spi_master_data *drv_data)
-{
- struct bfin_spi_slave_data *chip = drv_data->cur_chip;
- unsigned long flags;
- struct spi_message *msg;
-
- spin_lock_irqsave(&drv_data->lock, flags);
- msg = drv_data->cur_msg;
- drv_data->cur_msg = NULL;
- drv_data->cur_transfer = NULL;
- drv_data->cur_chip = NULL;
- schedule_work(&drv_data->pump_messages);
- spin_unlock_irqrestore(&drv_data->lock, flags);
-
- msg->state = NULL;
-
- if (!drv_data->cs_change)
- bfin_spi_cs_deactive(drv_data, chip);
-
- /* Not stop spi in autobuffer mode */
- if (drv_data->tx_dma != 0xFFFF)
- bfin_spi_disable(drv_data);
-
- if (msg->complete)
- msg->complete(msg->context);
-}
-
-/* spi data irq handler */
-static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id)
-{
- struct bfin_spi_master_data *drv_data = dev_id;
- struct bfin_spi_slave_data *chip = drv_data->cur_chip;
- struct spi_message *msg = drv_data->cur_msg;
- int n_bytes = drv_data->n_bytes;
- int loop = 0;
-
- /* wait until transfer finished. */
- while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_RXS))
- cpu_relax();
-
- if ((drv_data->tx && drv_data->tx >= drv_data->tx_end) ||
- (drv_data->rx && drv_data->rx >= (drv_data->rx_end - n_bytes))) {
- /* last read */
- if (drv_data->rx) {
- dev_dbg(&drv_data->pdev->dev, "last read\n");
- if (!(n_bytes % 2)) {
- u16 *buf = (u16 *)drv_data->rx;
- for (loop = 0; loop < n_bytes / 2; loop++)
- *buf++ = bfin_read(&drv_data->regs->rdbr);
- } else {
- u8 *buf = (u8 *)drv_data->rx;
- for (loop = 0; loop < n_bytes; loop++)
- *buf++ = bfin_read(&drv_data->regs->rdbr);
- }
- drv_data->rx += n_bytes;
- }
-
- msg->actual_length += drv_data->len_in_bytes;
- if (drv_data->cs_change)
- bfin_spi_cs_deactive(drv_data, chip);
- /* Move to next transfer */
- msg->state = bfin_spi_next_transfer(drv_data);
-
- disable_irq_nosync(drv_data->spi_irq);
-
- /* Schedule transfer tasklet */
- tasklet_schedule(&drv_data->pump_transfers);
- return IRQ_HANDLED;
- }
-
- if (drv_data->rx && drv_data->tx) {
- /* duplex */
- dev_dbg(&drv_data->pdev->dev, "duplex: write_TDBR\n");
- if (!(n_bytes % 2)) {
- u16 *buf = (u16 *)drv_data->rx;
- u16 *buf2 = (u16 *)drv_data->tx;
- for (loop = 0; loop < n_bytes / 2; loop++) {
- *buf++ = bfin_read(&drv_data->regs->rdbr);
- bfin_write(&drv_data->regs->tdbr, *buf2++);
- }
- } else {
- u8 *buf = (u8 *)drv_data->rx;
- u8 *buf2 = (u8 *)drv_data->tx;
- for (loop = 0; loop < n_bytes; loop++) {
- *buf++ = bfin_read(&drv_data->regs->rdbr);
- bfin_write(&drv_data->regs->tdbr, *buf2++);
- }
- }
- } else if (drv_data->rx) {
- /* read */
- dev_dbg(&drv_data->pdev->dev, "read: write_TDBR\n");
- if (!(n_bytes % 2)) {
- u16 *buf = (u16 *)drv_data->rx;
- for (loop = 0; loop < n_bytes / 2; loop++) {
- *buf++ = bfin_read(&drv_data->regs->rdbr);
- bfin_write(&drv_data->regs->tdbr, chip->idle_tx_val);
- }
- } else {
- u8 *buf = (u8 *)drv_data->rx;
- for (loop = 0; loop < n_bytes; loop++) {
- *buf++ = bfin_read(&drv_data->regs->rdbr);
- bfin_write(&drv_data->regs->tdbr, chip->idle_tx_val);
- }
- }
- } else if (drv_data->tx) {
- /* write */
- dev_dbg(&drv_data->pdev->dev, "write: write_TDBR\n");
- if (!(n_bytes % 2)) {
- u16 *buf = (u16 *)drv_data->tx;
- for (loop = 0; loop < n_bytes / 2; loop++) {
- bfin_read(&drv_data->regs->rdbr);
- bfin_write(&drv_data->regs->tdbr, *buf++);
- }
- } else {
- u8 *buf = (u8 *)drv_data->tx;
- for (loop = 0; loop < n_bytes; loop++) {
- bfin_read(&drv_data->regs->rdbr);
- bfin_write(&drv_data->regs->tdbr, *buf++);
- }
- }
- }
-
- if (drv_data->tx)
- drv_data->tx += n_bytes;
- if (drv_data->rx)
- drv_data->rx += n_bytes;
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t bfin_spi_dma_irq_handler(int irq, void *dev_id)
-{
- struct bfin_spi_master_data *drv_data = dev_id;
- struct bfin_spi_slave_data *chip = drv_data->cur_chip;
- struct spi_message *msg = drv_data->cur_msg;
- unsigned long timeout;
- unsigned short dmastat = get_dma_curr_irqstat(drv_data->dma_channel);
- u16 spistat = bfin_read(&drv_data->regs->stat);
-
- dev_dbg(&drv_data->pdev->dev,
- "in dma_irq_handler dmastat:0x%x spistat:0x%x\n",
- dmastat, spistat);
-
- if (drv_data->rx != NULL) {
- u16 cr = bfin_read(&drv_data->regs->ctl);
- /* discard old RX data and clear RXS */
- bfin_spi_dummy_read(drv_data);
- bfin_write(&drv_data->regs->ctl, cr & ~BIT_CTL_ENABLE); /* Disable SPI */
- bfin_write(&drv_data->regs->ctl, cr & ~BIT_CTL_TIMOD); /* Restore State */
- bfin_write(&drv_data->regs->stat, BIT_STAT_CLR); /* Clear Status */
- }
-
- clear_dma_irqstat(drv_data->dma_channel);
-
- /*
- * wait for the last transaction shifted out. HRM states:
- * at this point there may still be data in the SPI DMA FIFO waiting
- * to be transmitted ... software needs to poll TXS in the SPI_STAT
- * register until it goes low for 2 successive reads
- */
- if (drv_data->tx != NULL) {
- while ((bfin_read(&drv_data->regs->stat) & BIT_STAT_TXS) ||
- (bfin_read(&drv_data->regs->stat) & BIT_STAT_TXS))
- cpu_relax();
- }
-
- dev_dbg(&drv_data->pdev->dev,
- "in dma_irq_handler dmastat:0x%x spistat:0x%x\n",
- dmastat, bfin_read(&drv_data->regs->stat));
-
- timeout = jiffies + HZ;
- while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_SPIF))
- if (!time_before(jiffies, timeout)) {
- dev_warn(&drv_data->pdev->dev, "timeout waiting for SPIF\n");
- break;
- } else
- cpu_relax();
-
- if ((dmastat & DMA_ERR) && (spistat & BIT_STAT_RBSY)) {
- msg->state = ERROR_STATE;
- dev_err(&drv_data->pdev->dev, "dma receive: fifo/buffer overflow\n");
- } else {
- msg->actual_length += drv_data->len_in_bytes;
-
- if (drv_data->cs_change)
- bfin_spi_cs_deactive(drv_data, chip);
-
- /* Move to next transfer */
- msg->state = bfin_spi_next_transfer(drv_data);
- }
-
- /* Schedule transfer tasklet */
- tasklet_schedule(&drv_data->pump_transfers);
-
- /* free the irq handler before next transfer */
- dev_dbg(&drv_data->pdev->dev,
- "disable dma channel irq%d\n",
- drv_data->dma_channel);
- dma_disable_irq_nosync(drv_data->dma_channel);
-
- return IRQ_HANDLED;
-}
-
-static void bfin_spi_pump_transfers(unsigned long data)
-{
- struct bfin_spi_master_data *drv_data = (struct bfin_spi_master_data *)data;
- struct spi_message *message = NULL;
- struct spi_transfer *transfer = NULL;
- struct spi_transfer *previous = NULL;
- struct bfin_spi_slave_data *chip = NULL;
- unsigned int bits_per_word;
- u16 cr, cr_width = 0, dma_width, dma_config;
- u32 tranf_success = 1;
- u8 full_duplex = 0;
-
- /* Get current state information */
- message = drv_data->cur_msg;
- transfer = drv_data->cur_transfer;
- chip = drv_data->cur_chip;
-
- /*
- * if msg is error or done, report it back using complete() callback
- */
-
- /* Handle for abort */
- if (message->state == ERROR_STATE) {
- dev_dbg(&drv_data->pdev->dev, "transfer: we've hit an error\n");
- message->status = -EIO;
- bfin_spi_giveback(drv_data);
- return;
- }
-
- /* Handle end of message */
- if (message->state == DONE_STATE) {
- dev_dbg(&drv_data->pdev->dev, "transfer: all done!\n");
- message->status = 0;
- bfin_spi_flush(drv_data);
- bfin_spi_giveback(drv_data);
- return;
- }
-
- /* Delay if requested at end of transfer */
- if (message->state == RUNNING_STATE) {
- dev_dbg(&drv_data->pdev->dev, "transfer: still running ...\n");
- previous = list_entry(transfer->transfer_list.prev,
- struct spi_transfer, transfer_list);
- if (previous->delay_usecs)
- udelay(previous->delay_usecs);
- }
-
- /* Flush any existing transfers that may be sitting in the hardware */
- if (bfin_spi_flush(drv_data) == 0) {
- dev_err(&drv_data->pdev->dev, "pump_transfers: flush failed\n");
- message->status = -EIO;
- bfin_spi_giveback(drv_data);
- return;
- }
-
- if (transfer->len == 0) {
- /* Move to next transfer of this msg */
- message->state = bfin_spi_next_transfer(drv_data);
- /* Schedule next transfer tasklet */
- tasklet_schedule(&drv_data->pump_transfers);
- return;
- }
-
- if (transfer->tx_buf != NULL) {
- drv_data->tx = (void *)transfer->tx_buf;
- drv_data->tx_end = drv_data->tx + transfer->len;
- dev_dbg(&drv_data->pdev->dev, "tx_buf is %p, tx_end is %p\n",
- transfer->tx_buf, drv_data->tx_end);
- } else {
- drv_data->tx = NULL;
- }
-
- if (transfer->rx_buf != NULL) {
- full_duplex = transfer->tx_buf != NULL;
- drv_data->rx = transfer->rx_buf;
- drv_data->rx_end = drv_data->rx + transfer->len;
- dev_dbg(&drv_data->pdev->dev, "rx_buf is %p, rx_end is %p\n",
- transfer->rx_buf, drv_data->rx_end);
- } else {
- drv_data->rx = NULL;
- }
-
- drv_data->rx_dma = transfer->rx_dma;
- drv_data->tx_dma = transfer->tx_dma;
- drv_data->len_in_bytes = transfer->len;
- drv_data->cs_change = transfer->cs_change;
-
- /* Bits per word setup */
- bits_per_word = transfer->bits_per_word;
- if (bits_per_word == 16) {
- drv_data->n_bytes = bits_per_word/8;
- drv_data->len = (transfer->len) >> 1;
- cr_width = BIT_CTL_WORDSIZE;
- drv_data->ops = &bfin_bfin_spi_transfer_ops_u16;
- } else if (bits_per_word == 8) {
- drv_data->n_bytes = bits_per_word/8;
- drv_data->len = transfer->len;
- drv_data->ops = &bfin_bfin_spi_transfer_ops_u8;
- }
- cr = bfin_read(&drv_data->regs->ctl) & ~(BIT_CTL_TIMOD | BIT_CTL_WORDSIZE);
- cr |= cr_width;
- bfin_write(&drv_data->regs->ctl, cr);
-
- dev_dbg(&drv_data->pdev->dev,
- "transfer: drv_data->ops is %p, chip->ops is %p, u8_ops is %p\n",
- drv_data->ops, chip->ops, &bfin_bfin_spi_transfer_ops_u8);
-
- message->state = RUNNING_STATE;
- dma_config = 0;
-
- bfin_write(&drv_data->regs->baud, hz_to_spi_baud(transfer->speed_hz));
-
- bfin_write(&drv_data->regs->stat, BIT_STAT_CLR);
- bfin_spi_cs_active(drv_data, chip);
-
- dev_dbg(&drv_data->pdev->dev,
- "now pumping a transfer: width is %d, len is %d\n",
- cr_width, transfer->len);
-
- /*
- * Try to map dma buffer and do a dma transfer. If successful use,
- * different way to r/w according to the enable_dma settings and if
- * we are not doing a full duplex transfer (since the hardware does
- * not support full duplex DMA transfers).
- */
- if (!full_duplex && drv_data->cur_chip->enable_dma
- && drv_data->len > 6) {
-
- unsigned long dma_start_addr, flags;
-
- disable_dma(drv_data->dma_channel);
- clear_dma_irqstat(drv_data->dma_channel);
-
- /* config dma channel */
- dev_dbg(&drv_data->pdev->dev, "doing dma transfer\n");
- set_dma_x_count(drv_data->dma_channel, drv_data->len);
- if (cr_width == BIT_CTL_WORDSIZE) {
- set_dma_x_modify(drv_data->dma_channel, 2);
- dma_width = WDSIZE_16;
- } else {
- set_dma_x_modify(drv_data->dma_channel, 1);
- dma_width = WDSIZE_8;
- }
-
- /* poll for SPI completion before start */
- while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_SPIF))
- cpu_relax();
-
- /* dirty hack for autobuffer DMA mode */
- if (drv_data->tx_dma == 0xFFFF) {
- dev_dbg(&drv_data->pdev->dev,
- "doing autobuffer DMA out.\n");
-
- /* no irq in autobuffer mode */
- dma_config =
- (DMAFLOW_AUTO | RESTART | dma_width | DI_EN);
- set_dma_config(drv_data->dma_channel, dma_config);
- set_dma_start_addr(drv_data->dma_channel,
- (unsigned long)drv_data->tx);
- enable_dma(drv_data->dma_channel);
-
- /* start SPI transfer */
- bfin_write(&drv_data->regs->ctl, cr | BIT_CTL_TIMOD_DMA_TX);
-
- /* just return here, there can only be one transfer
- * in this mode
- */
- message->status = 0;
- bfin_spi_giveback(drv_data);
- return;
- }
-
- /* In dma mode, rx or tx must be NULL in one transfer */
- dma_config = (RESTART | dma_width | DI_EN);
- if (drv_data->rx != NULL) {
- /* set transfer mode, and enable SPI */
- dev_dbg(&drv_data->pdev->dev, "doing DMA in to %p (size %zx)\n",
- drv_data->rx, drv_data->len_in_bytes);
-
- /* invalidate caches, if needed */
- if (bfin_addr_dcacheable((unsigned long) drv_data->rx))
- invalidate_dcache_range((unsigned long) drv_data->rx,
- (unsigned long) (drv_data->rx +
- drv_data->len_in_bytes));
-
- dma_config |= WNR;
- dma_start_addr = (unsigned long)drv_data->rx;
- cr |= BIT_CTL_TIMOD_DMA_RX | BIT_CTL_SENDOPT;
-
- } else if (drv_data->tx != NULL) {
- dev_dbg(&drv_data->pdev->dev, "doing DMA out.\n");
-
- /* flush caches, if needed */
- if (bfin_addr_dcacheable((unsigned long) drv_data->tx))
- flush_dcache_range((unsigned long) drv_data->tx,
- (unsigned long) (drv_data->tx +
- drv_data->len_in_bytes));
-
- dma_start_addr = (unsigned long)drv_data->tx;
- cr |= BIT_CTL_TIMOD_DMA_TX;
-
- } else
- BUG();
-
- /* oh man, here there be monsters ... and i dont mean the
- * fluffy cute ones from pixar, i mean the kind that'll eat
- * your data, kick your dog, and love it all. do *not* try
- * and change these lines unless you (1) heavily test DMA
- * with SPI flashes on a loaded system (e.g. ping floods),
- * (2) know just how broken the DMA engine interaction with
- * the SPI peripheral is, and (3) have someone else to blame
- * when you screw it all up anyways.
- */
- set_dma_start_addr(drv_data->dma_channel, dma_start_addr);
- set_dma_config(drv_data->dma_channel, dma_config);
- local_irq_save(flags);
- SSYNC();
- bfin_write(&drv_data->regs->ctl, cr);
- enable_dma(drv_data->dma_channel);
- dma_enable_irq(drv_data->dma_channel);
- local_irq_restore(flags);
-
- return;
- }
-
- /*
- * We always use SPI_WRITE mode (transfer starts with TDBR write).
- * SPI_READ mode (transfer starts with RDBR read) seems to have
- * problems with setting up the output value in TDBR prior to the
- * start of the transfer.
- */
- bfin_write(&drv_data->regs->ctl, cr | BIT_CTL_TXMOD);
-
- if (chip->pio_interrupt) {
- /* SPI irq should have been disabled by now */
-
- /* discard old RX data and clear RXS */
- bfin_spi_dummy_read(drv_data);
-
- /* start transfer */
- if (drv_data->tx == NULL)
- bfin_write(&drv_data->regs->tdbr, chip->idle_tx_val);
- else {
- int loop;
- if (bits_per_word == 16) {
- u16 *buf = (u16 *)drv_data->tx;
- for (loop = 0; loop < bits_per_word / 16;
- loop++) {
- bfin_write(&drv_data->regs->tdbr, *buf++);
- }
- } else if (bits_per_word == 8) {
- u8 *buf = (u8 *)drv_data->tx;
- for (loop = 0; loop < bits_per_word / 8; loop++)
- bfin_write(&drv_data->regs->tdbr, *buf++);
- }
-
- drv_data->tx += drv_data->n_bytes;
- }
-
- /* once TDBR is empty, interrupt is triggered */
- enable_irq(drv_data->spi_irq);
- return;
- }
-
- /* IO mode */
- dev_dbg(&drv_data->pdev->dev, "doing IO transfer\n");
-
- if (full_duplex) {
- /* full duplex mode */
- BUG_ON((drv_data->tx_end - drv_data->tx) !=
- (drv_data->rx_end - drv_data->rx));
- dev_dbg(&drv_data->pdev->dev,
- "IO duplex: cr is 0x%x\n", cr);
-
- drv_data->ops->duplex(drv_data);
-
- if (drv_data->tx != drv_data->tx_end)
- tranf_success = 0;
- } else if (drv_data->tx != NULL) {
- /* write only half duplex */
- dev_dbg(&drv_data->pdev->dev,
- "IO write: cr is 0x%x\n", cr);
-
- drv_data->ops->write(drv_data);
-
- if (drv_data->tx != drv_data->tx_end)
- tranf_success = 0;
- } else if (drv_data->rx != NULL) {
- /* read only half duplex */
- dev_dbg(&drv_data->pdev->dev,
- "IO read: cr is 0x%x\n", cr);
-
- drv_data->ops->read(drv_data);
- if (drv_data->rx != drv_data->rx_end)
- tranf_success = 0;
- }
-
- if (!tranf_success) {
- dev_dbg(&drv_data->pdev->dev,
- "IO write error!\n");
- message->state = ERROR_STATE;
- } else {
- /* Update total byte transferred */
- message->actual_length += drv_data->len_in_bytes;
- /* Move to next transfer of this msg */
- message->state = bfin_spi_next_transfer(drv_data);
- if (drv_data->cs_change && message->state != DONE_STATE) {
- bfin_spi_flush(drv_data);
- bfin_spi_cs_deactive(drv_data, chip);
- }
- }
-
- /* Schedule next transfer tasklet */
- tasklet_schedule(&drv_data->pump_transfers);
-}
-
-/* pop a msg from queue and kick off real transfer */
-static void bfin_spi_pump_messages(struct work_struct *work)
-{
- struct bfin_spi_master_data *drv_data;
- unsigned long flags;
-
- drv_data = container_of(work, struct bfin_spi_master_data, pump_messages);
-
- /* Lock queue and check for queue work */
- spin_lock_irqsave(&drv_data->lock, flags);
- if (list_empty(&drv_data->queue) || !drv_data->running) {
- /* pumper kicked off but no work to do */
- drv_data->busy = 0;
- spin_unlock_irqrestore(&drv_data->lock, flags);
- return;
- }
-
- /* Make sure we are not already running a message */
- if (drv_data->cur_msg) {
- spin_unlock_irqrestore(&drv_data->lock, flags);
- return;
- }
-
- /* Extract head of queue */
- drv_data->cur_msg = list_entry(drv_data->queue.next,
- struct spi_message, queue);
-
- /* Setup the SSP using the per chip configuration */
- drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi);
- bfin_spi_restore_state(drv_data);
-
- list_del_init(&drv_data->cur_msg->queue);
-
- /* Initial message state */
- drv_data->cur_msg->state = START_STATE;
- drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next,
- struct spi_transfer, transfer_list);
-
- dev_dbg(&drv_data->pdev->dev,
- "got a message to pump, state is set to: baud "
- "%d, flag 0x%x, ctl 0x%x\n",
- drv_data->cur_chip->baud, drv_data->cur_chip->flag,
- drv_data->cur_chip->ctl_reg);
-
- dev_dbg(&drv_data->pdev->dev,
- "the first transfer len is %d\n",
- drv_data->cur_transfer->len);
-
- /* Mark as busy and launch transfers */
- tasklet_schedule(&drv_data->pump_transfers);
-
- drv_data->busy = 1;
- spin_unlock_irqrestore(&drv_data->lock, flags);
-}
-
-/*
- * got a msg to transfer, queue it in drv_data->queue.
- * And kick off message pumper
- */
-static int bfin_spi_transfer(struct spi_device *spi, struct spi_message *msg)
-{
- struct bfin_spi_master_data *drv_data = spi_master_get_devdata(spi->master);
- unsigned long flags;
-
- spin_lock_irqsave(&drv_data->lock, flags);
-
- if (!drv_data->running) {
- spin_unlock_irqrestore(&drv_data->lock, flags);
- return -ESHUTDOWN;
- }
-
- msg->actual_length = 0;
- msg->status = -EINPROGRESS;
- msg->state = START_STATE;
-
- dev_dbg(&spi->dev, "adding an msg in transfer() \n");
- list_add_tail(&msg->queue, &drv_data->queue);
-
- if (drv_data->running && !drv_data->busy)
- schedule_work(&drv_data->pump_messages);
-
- spin_unlock_irqrestore(&drv_data->lock, flags);
-
- return 0;
-}
-
-#define MAX_SPI_SSEL 7
-
-static const u16 ssel[][MAX_SPI_SSEL] = {
- {P_SPI0_SSEL1, P_SPI0_SSEL2, P_SPI0_SSEL3,
- P_SPI0_SSEL4, P_SPI0_SSEL5,
- P_SPI0_SSEL6, P_SPI0_SSEL7},
-
- {P_SPI1_SSEL1, P_SPI1_SSEL2, P_SPI1_SSEL3,
- P_SPI1_SSEL4, P_SPI1_SSEL5,
- P_SPI1_SSEL6, P_SPI1_SSEL7},
-
- {P_SPI2_SSEL1, P_SPI2_SSEL2, P_SPI2_SSEL3,
- P_SPI2_SSEL4, P_SPI2_SSEL5,
- P_SPI2_SSEL6, P_SPI2_SSEL7},
-};
-
-/* setup for devices (may be called multiple times -- not just first setup) */
-static int bfin_spi_setup(struct spi_device *spi)
-{
- struct bfin5xx_spi_chip *chip_info;
- struct bfin_spi_slave_data *chip = NULL;
- struct bfin_spi_master_data *drv_data = spi_master_get_devdata(spi->master);
- u16 bfin_ctl_reg;
- int ret = -EINVAL;
-
- /* Only alloc (or use chip_info) on first setup */
- chip_info = NULL;
- chip = spi_get_ctldata(spi);
- if (chip == NULL) {
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (!chip) {
- dev_err(&spi->dev, "cannot allocate chip data\n");
- ret = -ENOMEM;
- goto error;
- }
-
- chip->enable_dma = 0;
- chip_info = spi->controller_data;
- }
-
- /* Let people set non-standard bits directly */
- bfin_ctl_reg = BIT_CTL_OPENDRAIN | BIT_CTL_EMISO |
- BIT_CTL_PSSE | BIT_CTL_GM | BIT_CTL_SZ;
-
- /* chip_info isn't always needed */
- if (chip_info) {
- /* Make sure people stop trying to set fields via ctl_reg
- * when they should actually be using common SPI framework.
- * Currently we let through: WOM EMISO PSSE GM SZ.
- * Not sure if a user actually needs/uses any of these,
- * but let's assume (for now) they do.
- */
- if (chip_info->ctl_reg & ~bfin_ctl_reg) {
- dev_err(&spi->dev,
- "do not set bits in ctl_reg that the SPI framework manages\n");
- goto error;
- }
- chip->enable_dma = chip_info->enable_dma != 0
- && drv_data->master_info->enable_dma;
- chip->ctl_reg = chip_info->ctl_reg;
- chip->cs_chg_udelay = chip_info->cs_chg_udelay;
- chip->idle_tx_val = chip_info->idle_tx_val;
- chip->pio_interrupt = chip_info->pio_interrupt;
- } else {
- /* force a default base state */
- chip->ctl_reg &= bfin_ctl_reg;
- }
-
- /* translate common spi framework into our register */
- if (spi->mode & SPI_CPOL)
- chip->ctl_reg |= BIT_CTL_CPOL;
- if (spi->mode & SPI_CPHA)
- chip->ctl_reg |= BIT_CTL_CPHA;
- if (spi->mode & SPI_LSB_FIRST)
- chip->ctl_reg |= BIT_CTL_LSBF;
- /* we dont support running in slave mode (yet?) */
- chip->ctl_reg |= BIT_CTL_MASTER;
-
- /*
- * Notice: for blackfin, the speed_hz is the value of register
- * SPI_BAUD, not the real baudrate
- */
- chip->baud = hz_to_spi_baud(spi->max_speed_hz);
- chip->chip_select_num = spi->chip_select;
- if (chip->chip_select_num < MAX_CTRL_CS) {
- if (!(spi->mode & SPI_CPHA))
- dev_warn(&spi->dev,
- "Warning: SPI CPHA not set: Slave Select not under software control!\n"
- "See Documentation/blackfin/bfin-spi-notes.txt\n");
-
- chip->flag = (1 << spi->chip_select) << 8;
- } else
- chip->cs_gpio = chip->chip_select_num - MAX_CTRL_CS;
-
- if (chip->enable_dma && chip->pio_interrupt) {
- dev_err(&spi->dev,
- "enable_dma is set, do not set pio_interrupt\n");
- goto error;
- }
- /*
- * if any one SPI chip is registered and wants DMA, request the
- * DMA channel for it
- */
- if (chip->enable_dma && !drv_data->dma_requested) {
- /* register dma irq handler */
- ret = request_dma(drv_data->dma_channel, "BFIN_SPI_DMA");
- if (ret) {
- dev_err(&spi->dev,
- "Unable to request BlackFin SPI DMA channel\n");
- goto error;
- }
- drv_data->dma_requested = 1;
-
- ret = set_dma_callback(drv_data->dma_channel,
- bfin_spi_dma_irq_handler, drv_data);
- if (ret) {
- dev_err(&spi->dev, "Unable to set dma callback\n");
- goto error;
- }
- dma_disable_irq(drv_data->dma_channel);
- }
-
- if (chip->pio_interrupt && !drv_data->irq_requested) {
- ret = request_irq(drv_data->spi_irq, bfin_spi_pio_irq_handler,
- 0, "BFIN_SPI", drv_data);
- if (ret) {
- dev_err(&spi->dev, "Unable to register spi IRQ\n");
- goto error;
- }
- drv_data->irq_requested = 1;
- /* we use write mode, spi irq has to be disabled here */
- disable_irq(drv_data->spi_irq);
- }
-
- if (chip->chip_select_num >= MAX_CTRL_CS) {
- /* Only request on first setup */
- if (spi_get_ctldata(spi) == NULL) {
- ret = gpio_request(chip->cs_gpio, spi->modalias);
- if (ret) {
- dev_err(&spi->dev, "gpio_request() error\n");
- goto pin_error;
- }
- gpio_direction_output(chip->cs_gpio, 1);
- }
- }
-
- dev_dbg(&spi->dev, "setup spi chip %s, width is %d, dma is %d\n",
- spi->modalias, spi->bits_per_word, chip->enable_dma);
- dev_dbg(&spi->dev, "ctl_reg is 0x%x, flag_reg is 0x%x\n",
- chip->ctl_reg, chip->flag);
-
- spi_set_ctldata(spi, chip);
-
- dev_dbg(&spi->dev, "chip select number is %d\n", chip->chip_select_num);
- if (chip->chip_select_num < MAX_CTRL_CS) {
- ret = peripheral_request(ssel[spi->master->bus_num]
- [chip->chip_select_num-1], spi->modalias);
- if (ret) {
- dev_err(&spi->dev, "peripheral_request() error\n");
- goto pin_error;
- }
- }
-
- bfin_spi_cs_enable(drv_data, chip);
- bfin_spi_cs_deactive(drv_data, chip);
-
- return 0;
-
- pin_error:
- if (chip->chip_select_num >= MAX_CTRL_CS)
- gpio_free(chip->cs_gpio);
- else
- peripheral_free(ssel[spi->master->bus_num]
- [chip->chip_select_num - 1]);
- error:
- if (chip) {
- if (drv_data->dma_requested)
- free_dma(drv_data->dma_channel);
- drv_data->dma_requested = 0;
-
- kfree(chip);
- /* prevent free 'chip' twice */
- spi_set_ctldata(spi, NULL);
- }
-
- return ret;
-}
-
-/*
- * callback for spi framework.
- * clean driver specific data
- */
-static void bfin_spi_cleanup(struct spi_device *spi)
-{
- struct bfin_spi_slave_data *chip = spi_get_ctldata(spi);
- struct bfin_spi_master_data *drv_data = spi_master_get_devdata(spi->master);
-
- if (!chip)
- return;
-
- if (chip->chip_select_num < MAX_CTRL_CS) {
- peripheral_free(ssel[spi->master->bus_num]
- [chip->chip_select_num-1]);
- bfin_spi_cs_disable(drv_data, chip);
- } else
- gpio_free(chip->cs_gpio);
-
- kfree(chip);
- /* prevent free 'chip' twice */
- spi_set_ctldata(spi, NULL);
-}
-
-static int bfin_spi_init_queue(struct bfin_spi_master_data *drv_data)
-{
- INIT_LIST_HEAD(&drv_data->queue);
- spin_lock_init(&drv_data->lock);
-
- drv_data->running = false;
- drv_data->busy = 0;
-
- /* init transfer tasklet */
- tasklet_init(&drv_data->pump_transfers,
- bfin_spi_pump_transfers, (unsigned long)drv_data);
-
- INIT_WORK(&drv_data->pump_messages, bfin_spi_pump_messages);
-
- return 0;
-}
-
-static int bfin_spi_start_queue(struct bfin_spi_master_data *drv_data)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&drv_data->lock, flags);
-
- if (drv_data->running || drv_data->busy) {
- spin_unlock_irqrestore(&drv_data->lock, flags);
- return -EBUSY;
- }
-
- drv_data->running = true;
- drv_data->cur_msg = NULL;
- drv_data->cur_transfer = NULL;
- drv_data->cur_chip = NULL;
- spin_unlock_irqrestore(&drv_data->lock, flags);
-
- schedule_work(&drv_data->pump_messages);
-
- return 0;
-}
-
-static int bfin_spi_stop_queue(struct bfin_spi_master_data *drv_data)
-{
- unsigned long flags;
- unsigned limit = 500;
- int status = 0;
-
- spin_lock_irqsave(&drv_data->lock, flags);
-
- /*
- * This is a bit lame, but is optimized for the common execution path.
- * A wait_queue on the drv_data->busy could be used, but then the common
- * execution path (pump_messages) would be required to call wake_up or
- * friends on every SPI message. Do this instead
- */
- drv_data->running = false;
- while ((!list_empty(&drv_data->queue) || drv_data->busy) && limit--) {
- spin_unlock_irqrestore(&drv_data->lock, flags);
- msleep(10);
- spin_lock_irqsave(&drv_data->lock, flags);
- }
-
- if (!list_empty(&drv_data->queue) || drv_data->busy)
- status = -EBUSY;
-
- spin_unlock_irqrestore(&drv_data->lock, flags);
-
- return status;
-}
-
-static int bfin_spi_destroy_queue(struct bfin_spi_master_data *drv_data)
-{
- int status;
-
- status = bfin_spi_stop_queue(drv_data);
- if (status != 0)
- return status;
-
- flush_work(&drv_data->pump_messages);
-
- return 0;
-}
-
-static int bfin_spi_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct bfin5xx_spi_master *platform_info;
- struct spi_master *master;
- struct bfin_spi_master_data *drv_data;
- struct resource *res;
- int status = 0;
-
- platform_info = dev_get_platdata(dev);
-
- /* Allocate master with space for drv_data */
- master = spi_alloc_master(dev, sizeof(*drv_data));
- if (!master) {
- dev_err(&pdev->dev, "can not alloc spi_master\n");
- return -ENOMEM;
- }
-
- drv_data = spi_master_get_devdata(master);
- drv_data->master = master;
- drv_data->master_info = platform_info;
- drv_data->pdev = pdev;
- drv_data->pin_req = platform_info->pin_req;
-
- /* the spi->mode bits supported by this driver: */
- master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
- master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16);
- master->bus_num = pdev->id;
- master->num_chipselect = platform_info->num_chipselect;
- master->cleanup = bfin_spi_cleanup;
- master->setup = bfin_spi_setup;
- master->transfer = bfin_spi_transfer;
-
- /* Find and map our resources */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
- dev_err(dev, "Cannot get IORESOURCE_MEM\n");
- status = -ENOENT;
- goto out_error_get_res;
- }
-
- drv_data->regs = ioremap(res->start, resource_size(res));
- if (drv_data->regs == NULL) {
- dev_err(dev, "Cannot map IO\n");
- status = -ENXIO;
- goto out_error_ioremap;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (res == NULL) {
- dev_err(dev, "No DMA channel specified\n");
- status = -ENOENT;
- goto out_error_free_io;
- }
- drv_data->dma_channel = res->start;
-
- drv_data->spi_irq = platform_get_irq(pdev, 0);
- if (drv_data->spi_irq < 0) {
- dev_err(dev, "No spi pio irq specified\n");
- status = -ENOENT;
- goto out_error_free_io;
- }
-
- /* Initial and start queue */
- status = bfin_spi_init_queue(drv_data);
- if (status != 0) {
- dev_err(dev, "problem initializing queue\n");
- goto out_error_queue_alloc;
- }
-
- status = bfin_spi_start_queue(drv_data);
- if (status != 0) {
- dev_err(dev, "problem starting queue\n");
- goto out_error_queue_alloc;
- }
-
- status = peripheral_request_list(drv_data->pin_req, DRV_NAME);
- if (status != 0) {
- dev_err(&pdev->dev, ": Requesting Peripherals failed\n");
- goto out_error_queue_alloc;
- }
-
- /* Reset SPI registers. If these registers were used by the boot loader,
- * the sky may fall on your head if you enable the dma controller.
- */
- bfin_write(&drv_data->regs->ctl, BIT_CTL_CPHA | BIT_CTL_MASTER);
- bfin_write(&drv_data->regs->flg, 0xFF00);
-
- /* Register with the SPI framework */
- platform_set_drvdata(pdev, drv_data);
- status = spi_register_master(master);
- if (status != 0) {
- dev_err(dev, "problem registering spi master\n");
- goto out_error_queue_alloc;
- }
-
- dev_info(dev, "%s, Version %s, regs@%p, dma channel@%d\n",
- DRV_DESC, DRV_VERSION, drv_data->regs,
- drv_data->dma_channel);
- return status;
-
-out_error_queue_alloc:
- bfin_spi_destroy_queue(drv_data);
-out_error_free_io:
- iounmap(drv_data->regs);
-out_error_ioremap:
-out_error_get_res:
- spi_master_put(master);
-
- return status;
-}
-
-/* stop hardware and remove the driver */
-static int bfin_spi_remove(struct platform_device *pdev)
-{
- struct bfin_spi_master_data *drv_data = platform_get_drvdata(pdev);
- int status = 0;
-
- if (!drv_data)
- return 0;
-
- /* Remove the queue */
- status = bfin_spi_destroy_queue(drv_data);
- if (status != 0)
- return status;
-
- /* Disable the SSP at the peripheral and SOC level */
- bfin_spi_disable(drv_data);
-
- /* Release DMA */
- if (drv_data->master_info->enable_dma) {
- if (dma_channel_active(drv_data->dma_channel))
- free_dma(drv_data->dma_channel);
- }
-
- if (drv_data->irq_requested) {
- free_irq(drv_data->spi_irq, drv_data);
- drv_data->irq_requested = 0;
- }
-
- /* Disconnect from the SPI framework */
- spi_unregister_master(drv_data->master);
-
- peripheral_free_list(drv_data->pin_req);
-
- return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int bfin_spi_suspend(struct device *dev)
-{
- struct bfin_spi_master_data *drv_data = dev_get_drvdata(dev);
- int status = 0;
-
- status = bfin_spi_stop_queue(drv_data);
- if (status != 0)
- return status;
-
- drv_data->ctrl_reg = bfin_read(&drv_data->regs->ctl);
- drv_data->flag_reg = bfin_read(&drv_data->regs->flg);
-
- /*
- * reset SPI_CTL and SPI_FLG registers
- */
- bfin_write(&drv_data->regs->ctl, BIT_CTL_CPHA | BIT_CTL_MASTER);
- bfin_write(&drv_data->regs->flg, 0xFF00);
-
- return 0;
-}
-
-static int bfin_spi_resume(struct device *dev)
-{
- struct bfin_spi_master_data *drv_data = dev_get_drvdata(dev);
- int status = 0;
-
- bfin_write(&drv_data->regs->ctl, drv_data->ctrl_reg);
- bfin_write(&drv_data->regs->flg, drv_data->flag_reg);
-
- /* Start the queue running */
- status = bfin_spi_start_queue(drv_data);
- if (status != 0) {
- dev_err(dev, "problem starting queue (%d)\n", status);
- return status;
- }
-
- return 0;
-}
-
-static SIMPLE_DEV_PM_OPS(bfin_spi_pm_ops, bfin_spi_suspend, bfin_spi_resume);
-
-#define BFIN_SPI_PM_OPS (&bfin_spi_pm_ops)
-#else
-#define BFIN_SPI_PM_OPS NULL
-#endif
-
-MODULE_ALIAS("platform:bfin-spi");
-static struct platform_driver bfin_spi_driver = {
- .driver = {
- .name = DRV_NAME,
- .pm = BFIN_SPI_PM_OPS,
- },
- .probe = bfin_spi_probe,
- .remove = bfin_spi_remove,
-};
-
-static int __init bfin_spi_init(void)
-{
- return platform_driver_register(&bfin_spi_driver);
-}
-subsys_initcall(bfin_spi_init);
-
-static void __exit bfin_spi_exit(void)
-{
- platform_driver_unregister(&bfin_spi_driver);
-}
-module_exit(bfin_spi_exit);
diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c
index 837cb8d0bac6..3db905f5f345 100644
--- a/drivers/spi/spi-dw-mid.c
+++ b/drivers/spi/spi-dw-mid.c
@@ -112,10 +112,10 @@ static irqreturn_t dma_transfer(struct dw_spi *dws)
return IRQ_HANDLED;
}
-static bool mid_spi_can_dma(struct spi_master *master, struct spi_device *spi,
- struct spi_transfer *xfer)
+static bool mid_spi_can_dma(struct spi_controller *master,
+ struct spi_device *spi, struct spi_transfer *xfer)
{
- struct dw_spi *dws = spi_master_get_devdata(master);
+ struct dw_spi *dws = spi_controller_get_devdata(master);
if (!dws->dma_inited)
return false;
diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c
index 211cc7d75bf8..f693bfe95ab9 100644
--- a/drivers/spi/spi-dw.c
+++ b/drivers/spi/spi-dw.c
@@ -135,7 +135,7 @@ static inline void dw_spi_debugfs_remove(struct dw_spi *dws)
static void dw_spi_set_cs(struct spi_device *spi, bool enable)
{
- struct dw_spi *dws = spi_master_get_devdata(spi->master);
+ struct dw_spi *dws = spi_controller_get_devdata(spi->controller);
struct chip_data *chip = spi_get_ctldata(spi);
/* Chip select logic is inverted from spi_set_cs() */
@@ -250,8 +250,8 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws)
static irqreturn_t dw_spi_irq(int irq, void *dev_id)
{
- struct spi_master *master = dev_id;
- struct dw_spi *dws = spi_master_get_devdata(master);
+ struct spi_controller *master = dev_id;
+ struct dw_spi *dws = spi_controller_get_devdata(master);
u16 irq_status = dw_readl(dws, DW_SPI_ISR) & 0x3f;
if (!irq_status)
@@ -277,10 +277,10 @@ static int poll_transfer(struct dw_spi *dws)
return 0;
}
-static int dw_spi_transfer_one(struct spi_master *master,
+static int dw_spi_transfer_one(struct spi_controller *master,
struct spi_device *spi, struct spi_transfer *transfer)
{
- struct dw_spi *dws = spi_master_get_devdata(master);
+ struct dw_spi *dws = spi_controller_get_devdata(master);
struct chip_data *chip = spi_get_ctldata(spi);
u8 imask = 0;
u16 txlevel = 0;
@@ -383,10 +383,10 @@ static int dw_spi_transfer_one(struct spi_master *master,
return 1;
}
-static void dw_spi_handle_err(struct spi_master *master,
+static void dw_spi_handle_err(struct spi_controller *master,
struct spi_message *msg)
{
- struct dw_spi *dws = spi_master_get_devdata(master);
+ struct dw_spi *dws = spi_controller_get_devdata(master);
if (dws->dma_mapped)
dws->dma_ops->dma_stop(dws);
@@ -471,7 +471,7 @@ static void spi_hw_init(struct device *dev, struct dw_spi *dws)
int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
{
- struct spi_master *master;
+ struct spi_controller *master;
int ret;
BUG_ON(dws == NULL);
@@ -518,8 +518,8 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
}
}
- spi_master_set_devdata(master, dws);
- ret = devm_spi_register_master(dev, master);
+ spi_controller_set_devdata(master, dws);
+ ret = devm_spi_register_controller(dev, master);
if (ret) {
dev_err(&master->dev, "problem registering spi master\n");
goto err_dma_exit;
@@ -534,7 +534,7 @@ err_dma_exit:
spi_enable_chip(dws, 0);
free_irq(dws->irq, master);
err_free_master:
- spi_master_put(master);
+ spi_controller_put(master);
return ret;
}
EXPORT_SYMBOL_GPL(dw_spi_add_host);
@@ -556,7 +556,7 @@ int dw_spi_suspend_host(struct dw_spi *dws)
{
int ret;
- ret = spi_master_suspend(dws->master);
+ ret = spi_controller_suspend(dws->master);
if (ret)
return ret;
@@ -570,7 +570,7 @@ int dw_spi_resume_host(struct dw_spi *dws)
int ret;
spi_hw_init(&dws->master->dev, dws);
- ret = spi_master_resume(dws->master);
+ ret = spi_controller_resume(dws->master);
if (ret)
dev_err(&dws->master->dev, "fail to start queue (%d)\n", ret);
return ret;
diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h
index 5c07cf8f19e0..2cde2473b3e9 100644
--- a/drivers/spi/spi-dw.h
+++ b/drivers/spi/spi-dw.h
@@ -93,14 +93,14 @@ struct dw_spi_dma_ops {
int (*dma_init)(struct dw_spi *dws);
void (*dma_exit)(struct dw_spi *dws);
int (*dma_setup)(struct dw_spi *dws, struct spi_transfer *xfer);
- bool (*can_dma)(struct spi_master *master, struct spi_device *spi,
+ bool (*can_dma)(struct spi_controller *master, struct spi_device *spi,
struct spi_transfer *xfer);
int (*dma_transfer)(struct dw_spi *dws, struct spi_transfer *xfer);
void (*dma_stop)(struct dw_spi *dws);
};
struct dw_spi {
- struct spi_master *master;
+ struct spi_controller *master;
enum dw_ssi_type type;
void __iomem *regs;
diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c
index 1c34c9314c8a..b85a93cad44a 100644
--- a/drivers/spi/spi-gpio.c
+++ b/drivers/spi/spi-gpio.c
@@ -2,6 +2,7 @@
* SPI master driver using generic bitbanged GPIO
*
* Copyright (C) 2006,2008 David Brownell
+ * Copyright (C) 2017 Linus Walleij
*
* 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
@@ -16,10 +17,9 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/of.h>
#include <linux/of_device.h>
-#include <linux/of_gpio.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
@@ -44,7 +44,11 @@ struct spi_gpio {
struct spi_bitbang bitbang;
struct spi_gpio_platform_data pdata;
struct platform_device *pdev;
- unsigned long cs_gpios[0];
+ struct gpio_desc *sck;
+ struct gpio_desc *miso;
+ struct gpio_desc *mosi;
+ struct gpio_desc **cs_gpios;
+ bool has_cs;
};
/*----------------------------------------------------------------------*/
@@ -77,13 +81,6 @@ struct spi_gpio {
#define GENERIC_BITBANG /* vs tight inlines */
-/* all functions referencing these symbols must define pdata */
-#define SPI_MISO_GPIO ((pdata)->miso)
-#define SPI_MOSI_GPIO ((pdata)->mosi)
-#define SPI_SCK_GPIO ((pdata)->sck)
-
-#define SPI_N_CHIPSEL ((pdata)->num_chipselect)
-
#endif
/*----------------------------------------------------------------------*/
@@ -105,25 +102,27 @@ spi_to_pdata(const struct spi_device *spi)
return &spi_to_spi_gpio(spi)->pdata;
}
-/* this is #defined to avoid unused-variable warnings when inlining */
-#define pdata spi_to_pdata(spi)
-
+/* These helpers are in turn called by the bitbang inlines */
static inline void setsck(const struct spi_device *spi, int is_on)
{
- gpio_set_value_cansleep(SPI_SCK_GPIO, is_on);
+ struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi);
+
+ gpiod_set_value_cansleep(spi_gpio->sck, is_on);
}
static inline void setmosi(const struct spi_device *spi, int is_on)
{
- gpio_set_value_cansleep(SPI_MOSI_GPIO, is_on);
+ struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi);
+
+ gpiod_set_value_cansleep(spi_gpio->mosi, is_on);
}
static inline int getmiso(const struct spi_device *spi)
{
- return !!gpio_get_value_cansleep(SPI_MISO_GPIO);
-}
+ struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi);
-#undef pdata
+ return !!gpiod_get_value_cansleep(spi_gpio->miso);
+}
/*
* NOTE: this clocks "as fast as we can". It "should" be a function of the
@@ -216,123 +215,89 @@ static u32 spi_gpio_spec_txrx_word_mode3(struct spi_device *spi,
static void spi_gpio_chipselect(struct spi_device *spi, int is_active)
{
struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi);
- unsigned long cs = spi_gpio->cs_gpios[spi->chip_select];
- /* set initial clock polarity */
+ /* set initial clock line level */
if (is_active)
- setsck(spi, spi->mode & SPI_CPOL);
+ gpiod_set_value_cansleep(spi_gpio->sck, spi->mode & SPI_CPOL);
+
+ /* Drive chip select line, if we have one */
+ if (spi_gpio->has_cs) {
+ struct gpio_desc *cs = spi_gpio->cs_gpios[spi->chip_select];
- if (cs != SPI_GPIO_NO_CHIPSELECT) {
- /* SPI is normally active-low */
- gpio_set_value_cansleep(cs, (spi->mode & SPI_CS_HIGH) ? is_active : !is_active);
+ /* SPI chip selects are normally active-low */
+ gpiod_set_value_cansleep(cs, (spi->mode & SPI_CS_HIGH) ? is_active : !is_active);
}
}
static int spi_gpio_setup(struct spi_device *spi)
{
- unsigned long cs;
+ struct gpio_desc *cs;
int status = 0;
struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi);
- struct device_node *np = spi->master->dev.of_node;
-
- if (np) {
- /*
- * In DT environments, the CS GPIOs have already been
- * initialized from the "cs-gpios" property of the node.
- */
- cs = spi_gpio->cs_gpios[spi->chip_select];
- } else {
- /*
- * ... otherwise, take it from spi->controller_data
- */
- cs = (uintptr_t) spi->controller_data;
- }
- if (!spi->controller_state) {
- if (cs != SPI_GPIO_NO_CHIPSELECT) {
- status = gpio_request(cs, dev_name(&spi->dev));
- if (status)
- return status;
- status = gpio_direction_output(cs,
- !(spi->mode & SPI_CS_HIGH));
- }
- }
- if (!status) {
- /* in case it was initialized from static board data */
- spi_gpio->cs_gpios[spi->chip_select] = cs;
+ /*
+ * The CS GPIOs have already been
+ * initialized from the descriptor lookup.
+ */
+ cs = spi_gpio->cs_gpios[spi->chip_select];
+ if (!spi->controller_state && cs)
+ status = gpiod_direction_output(cs,
+ !(spi->mode & SPI_CS_HIGH));
+
+ if (!status)
status = spi_bitbang_setup(spi);
- }
- if (status) {
- if (!spi->controller_state && cs != SPI_GPIO_NO_CHIPSELECT)
- gpio_free(cs);
- }
return status;
}
static void spi_gpio_cleanup(struct spi_device *spi)
{
- struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi);
- unsigned long cs = spi_gpio->cs_gpios[spi->chip_select];
-
- if (cs != SPI_GPIO_NO_CHIPSELECT)
- gpio_free(cs);
spi_bitbang_cleanup(spi);
}
-static int spi_gpio_alloc(unsigned pin, const char *label, bool is_in)
-{
- int value;
-
- value = gpio_request(pin, label);
- if (value == 0) {
- if (is_in)
- value = gpio_direction_input(pin);
- else
- value = gpio_direction_output(pin, 0);
- }
- return value;
-}
-
-static int spi_gpio_request(struct spi_gpio_platform_data *pdata,
- const char *label, u16 *res_flags)
+/*
+ * It can be convenient to use this driver with pins that have alternate
+ * functions associated with a "native" SPI controller if a driver for that
+ * controller is not available, or is missing important functionality.
+ *
+ * On platforms which can do so, configure MISO with a weak pullup unless
+ * there's an external pullup on that signal. That saves power by avoiding
+ * floating signals. (A weak pulldown would save power too, but many
+ * drivers expect to see all-ones data as the no slave "response".)
+ */
+static int spi_gpio_request(struct device *dev,
+ struct spi_gpio *spi_gpio,
+ unsigned int num_chipselects,
+ u16 *mflags)
{
- int value;
-
- /* NOTE: SPI_*_GPIO symbols may reference "pdata" */
+ int i;
- if (SPI_MOSI_GPIO != SPI_GPIO_NO_MOSI) {
- value = spi_gpio_alloc(SPI_MOSI_GPIO, label, false);
- if (value)
- goto done;
- } else {
+ spi_gpio->mosi = devm_gpiod_get_optional(dev, "mosi", GPIOD_OUT_LOW);
+ if (IS_ERR(spi_gpio->mosi))
+ return PTR_ERR(spi_gpio->mosi);
+ if (!spi_gpio->mosi)
/* HW configuration without MOSI pin */
- *res_flags |= SPI_MASTER_NO_TX;
- }
+ *mflags |= SPI_MASTER_NO_TX;
- if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO) {
- value = spi_gpio_alloc(SPI_MISO_GPIO, label, true);
- if (value)
- goto free_mosi;
- } else {
+ spi_gpio->miso = devm_gpiod_get_optional(dev, "miso", GPIOD_IN);
+ if (IS_ERR(spi_gpio->miso))
+ return PTR_ERR(spi_gpio->miso);
+ if (!spi_gpio->miso)
/* HW configuration without MISO pin */
- *res_flags |= SPI_MASTER_NO_RX;
- }
+ *mflags |= SPI_MASTER_NO_RX;
- value = spi_gpio_alloc(SPI_SCK_GPIO, label, false);
- if (value)
- goto free_miso;
+ spi_gpio->sck = devm_gpiod_get(dev, "sck", GPIOD_OUT_LOW);
+ if (IS_ERR(spi_gpio->mosi))
+ return PTR_ERR(spi_gpio->mosi);
- goto done;
+ for (i = 0; i < num_chipselects; i++) {
+ spi_gpio->cs_gpios[i] = devm_gpiod_get_index(dev, "cs",
+ i, GPIOD_OUT_HIGH);
+ if (IS_ERR(spi_gpio->cs_gpios[i]))
+ return PTR_ERR(spi_gpio->cs_gpios[i]);
+ }
-free_miso:
- if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO)
- gpio_free(SPI_MISO_GPIO);
-free_mosi:
- if (SPI_MOSI_GPIO != SPI_GPIO_NO_MOSI)
- gpio_free(SPI_MOSI_GPIO);
-done:
- return value;
+ return 0;
}
#ifdef CONFIG_OF
@@ -358,26 +323,6 @@ static int spi_gpio_probe_dt(struct platform_device *pdev)
if (!pdata)
return -ENOMEM;
- ret = of_get_named_gpio(np, "gpio-sck", 0);
- if (ret < 0) {
- dev_err(&pdev->dev, "gpio-sck property not found\n");
- goto error_free;
- }
- pdata->sck = ret;
-
- ret = of_get_named_gpio(np, "gpio-miso", 0);
- if (ret < 0) {
- dev_info(&pdev->dev, "gpio-miso property not found, switching to no-rx mode\n");
- pdata->miso = SPI_GPIO_NO_MISO;
- } else
- pdata->miso = ret;
-
- ret = of_get_named_gpio(np, "gpio-mosi", 0);
- if (ret < 0) {
- dev_info(&pdev->dev, "gpio-mosi property not found, switching to no-tx mode\n");
- pdata->mosi = SPI_GPIO_NO_MOSI;
- } else
- pdata->mosi = ret;
ret = of_property_read_u32(np, "num-chipselects", &tmp);
if (ret < 0) {
@@ -409,7 +354,6 @@ static int spi_gpio_probe(struct platform_device *pdev)
struct spi_gpio_platform_data *pdata;
u16 master_flags = 0;
bool use_of = 0;
- int num_devices;
status = spi_gpio_probe_dt(pdev);
if (status < 0)
@@ -423,59 +367,41 @@ static int spi_gpio_probe(struct platform_device *pdev)
return -ENODEV;
#endif
- if (use_of && !SPI_N_CHIPSEL)
- num_devices = 1;
- else
- num_devices = SPI_N_CHIPSEL;
-
- status = spi_gpio_request(pdata, dev_name(&pdev->dev), &master_flags);
- if (status < 0)
- return status;
+ master = spi_alloc_master(&pdev->dev, sizeof(*spi_gpio));
+ if (!master)
+ return -ENOMEM;
- master = spi_alloc_master(&pdev->dev, sizeof(*spi_gpio) +
- (sizeof(unsigned long) * num_devices));
- if (!master) {
- status = -ENOMEM;
- goto gpio_free;
- }
spi_gpio = spi_master_get_devdata(master);
+
+ spi_gpio->cs_gpios = devm_kzalloc(&pdev->dev,
+ pdata->num_chipselect * sizeof(*spi_gpio->cs_gpios),
+ GFP_KERNEL);
+ if (!spi_gpio->cs_gpios)
+ return -ENOMEM;
+
platform_set_drvdata(pdev, spi_gpio);
+ /* Determine if we have chip selects connected */
+ spi_gpio->has_cs = !!pdata->num_chipselect;
+
spi_gpio->pdev = pdev;
if (pdata)
spi_gpio->pdata = *pdata;
+ status = spi_gpio_request(&pdev->dev, spi_gpio,
+ pdata->num_chipselect, &master_flags);
+ if (status)
+ return status;
+
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
master->flags = master_flags;
master->bus_num = pdev->id;
- master->num_chipselect = num_devices;
+ /* The master needs to think there is a chipselect even if not connected */
+ master->num_chipselect = spi_gpio->has_cs ? pdata->num_chipselect : 1;
master->setup = spi_gpio_setup;
master->cleanup = spi_gpio_cleanup;
#ifdef CONFIG_OF
master->dev.of_node = pdev->dev.of_node;
-
- if (use_of) {
- int i;
- struct device_node *np = pdev->dev.of_node;
-
- /*
- * In DT environments, take the CS GPIO from the "cs-gpios"
- * property of the node.
- */
-
- if (!SPI_N_CHIPSEL)
- spi_gpio->cs_gpios[0] = SPI_GPIO_NO_CHIPSELECT;
- else
- for (i = 0; i < SPI_N_CHIPSEL; i++) {
- status = of_get_named_gpio(np, "cs-gpios", i);
- if (status < 0) {
- dev_err(&pdev->dev,
- "invalid cs-gpios property\n");
- goto gpio_free;
- }
- spi_gpio->cs_gpios[i] = status;
- }
- }
#endif
spi_gpio->bitbang.master = master;
@@ -496,15 +422,8 @@ static int spi_gpio_probe(struct platform_device *pdev)
spi_gpio->bitbang.flags = SPI_CS_HIGH;
status = spi_bitbang_start(&spi_gpio->bitbang);
- if (status < 0) {
-gpio_free:
- if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO)
- gpio_free(SPI_MISO_GPIO);
- if (SPI_MOSI_GPIO != SPI_GPIO_NO_MOSI)
- gpio_free(SPI_MOSI_GPIO);
- gpio_free(SPI_SCK_GPIO);
+ if (status)
spi_master_put(master);
- }
return status;
}
@@ -520,11 +439,6 @@ static int spi_gpio_remove(struct platform_device *pdev)
/* stop() unregisters child devices too */
spi_bitbang_stop(&spi_gpio->bitbang);
- if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO)
- gpio_free(SPI_MISO_GPIO);
- if (SPI_MOSI_GPIO != SPI_GPIO_NO_MOSI)
- gpio_free(SPI_MOSI_GPIO);
- gpio_free(SPI_SCK_GPIO);
spi_master_put(spi_gpio->bitbang.master);
return 0;
diff --git a/drivers/spi/spi-jcore.c b/drivers/spi/spi-jcore.c
index dafed6280df3..702fe573a47b 100644
--- a/drivers/spi/spi-jcore.c
+++ b/drivers/spi/spi-jcore.c
@@ -184,10 +184,11 @@ static int jcore_spi_probe(struct platform_device *pdev)
*/
clock_freq = 50000000;
clk = devm_clk_get(&pdev->dev, "ref_clk");
- if (!IS_ERR_OR_NULL(clk)) {
- if (clk_enable(clk) == 0)
+ if (!IS_ERR(clk)) {
+ if (clk_prepare_enable(clk) == 0) {
clock_freq = clk_get_rate(clk);
- else
+ clk_disable_unprepare(clk);
+ } else
dev_warn(&pdev->dev, "could not enable ref_clk\n");
}
hw->clock_freq = clock_freq;
@@ -198,10 +199,8 @@ static int jcore_spi_probe(struct platform_device *pdev)
/* Register our spi controller */
err = devm_spi_register_master(&pdev->dev, master);
- if (err) {
- clk_disable(clk);
+ if (err)
goto exit;
- }
return 0;
diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c
index deca63e82ff6..d01a6adc726e 100644
--- a/drivers/spi/spi-orion.c
+++ b/drivers/spi/spi-orion.c
@@ -90,14 +90,19 @@ struct orion_direct_acc {
u32 size;
};
+struct orion_child_options {
+ struct orion_direct_acc direct_access;
+};
+
struct orion_spi {
struct spi_master *master;
void __iomem *base;
struct clk *clk;
struct clk *axi_clk;
const struct orion_spi_dev *devdata;
+ int unused_hw_gpio;
- struct orion_direct_acc direct_access[ORION_NUM_CHIPSELECTS];
+ struct orion_child_options child[ORION_NUM_CHIPSELECTS];
};
static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg)
@@ -324,13 +329,13 @@ static void orion_spi_set_cs(struct spi_device *spi, bool enable)
struct orion_spi *orion_spi;
int cs;
+ orion_spi = spi_master_get_devdata(spi->master);
+
if (gpio_is_valid(spi->cs_gpio))
- cs = 0;
+ cs = orion_spi->unused_hw_gpio;
else
cs = spi->chip_select;
- orion_spi = spi_master_get_devdata(spi->master);
-
orion_spi_clrbits(orion_spi, ORION_SPI_IF_CTRL_REG, ORION_SPI_CS_MASK);
orion_spi_setbits(orion_spi, ORION_SPI_IF_CTRL_REG,
ORION_SPI_CS(cs));
@@ -435,7 +440,7 @@ orion_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer)
* Use SPI direct write mode if base address is available. Otherwise
* fall back to PIO mode for this transfer.
*/
- if ((orion_spi->direct_access[cs].vaddr) && (xfer->tx_buf) &&
+ if ((orion_spi->child[cs].direct_access.vaddr) && (xfer->tx_buf) &&
(word_len == 8)) {
unsigned int cnt = count / 4;
unsigned int rem = count % 4;
@@ -444,12 +449,12 @@ orion_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer)
* Send the TX-data to the SPI device via the direct
* mapped address window
*/
- iowrite32_rep(orion_spi->direct_access[cs].vaddr,
+ iowrite32_rep(orion_spi->child[cs].direct_access.vaddr,
xfer->tx_buf, cnt);
if (rem) {
u32 *buf = (u32 *)xfer->tx_buf;
- iowrite8_rep(orion_spi->direct_access[cs].vaddr,
+ iowrite8_rep(orion_spi->child[cs].direct_access.vaddr,
&buf[cnt], rem);
}
@@ -498,6 +503,9 @@ static int orion_spi_transfer_one(struct spi_master *master,
static int orion_spi_setup(struct spi_device *spi)
{
+ if (gpio_is_valid(spi->cs_gpio)) {
+ gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH));
+ }
return orion_spi_setup_transfer(spi, NULL);
}
@@ -620,6 +628,7 @@ static int orion_spi_probe(struct platform_device *pdev)
spi = spi_master_get_devdata(master);
spi->master = master;
+ spi->unused_hw_gpio = -1;
of_id = of_match_device(orion_spi_of_match_table, &pdev->dev);
devdata = (of_id) ? of_id->data : &orion_spi_dev_data;
@@ -702,14 +711,14 @@ static int orion_spi_probe(struct platform_device *pdev)
* This needs to get extended for the direct SPI-NOR / SPI-NAND
* support, once this gets implemented.
*/
- spi->direct_access[cs].vaddr = devm_ioremap(&pdev->dev,
+ spi->child[cs].direct_access.vaddr = devm_ioremap(&pdev->dev,
r->start,
PAGE_SIZE);
- if (!spi->direct_access[cs].vaddr) {
+ if (!spi->child[cs].direct_access.vaddr) {
status = -ENOMEM;
goto out_rel_axi_clk;
}
- spi->direct_access[cs].size = PAGE_SIZE;
+ spi->child[cs].direct_access.size = PAGE_SIZE;
dev_info(&pdev->dev, "CS%d configured for direct access\n", cs);
}
@@ -731,8 +740,44 @@ static int orion_spi_probe(struct platform_device *pdev)
if (status < 0)
goto out_rel_pm;
+ if (master->cs_gpios) {
+ int i;
+ for (i = 0; i < master->num_chipselect; ++i) {
+ char *gpio_name;
+
+ if (!gpio_is_valid(master->cs_gpios[i])) {
+ continue;
+ }
+
+ gpio_name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
+ "%s-CS%d", dev_name(&pdev->dev), i);
+ if (!gpio_name) {
+ status = -ENOMEM;
+ goto out_rel_master;
+ }
+
+ status = devm_gpio_request(&pdev->dev,
+ master->cs_gpios[i], gpio_name);
+ if (status) {
+ dev_err(&pdev->dev,
+ "Can't request GPIO for CS %d\n",
+ master->cs_gpios[i]);
+ goto out_rel_master;
+ }
+ if (spi->unused_hw_gpio == -1) {
+ dev_info(&pdev->dev,
+ "Selected unused HW CS#%d for any GPIO CSes\n",
+ i);
+ spi->unused_hw_gpio = i;
+ }
+ }
+ }
+
+
return status;
+out_rel_master:
+ spi_unregister_master(master);
out_rel_pm:
pm_runtime_disable(&pdev->dev);
out_rel_axi_clk:
diff --git a/drivers/spi/spi-pxa2xx-dma.c b/drivers/spi/spi-pxa2xx-dma.c
index 04f3eecf5cf3..3d7f66080c57 100644
--- a/drivers/spi/spi-pxa2xx-dma.c
+++ b/drivers/spi/spi-pxa2xx-dma.c
@@ -191,7 +191,7 @@ int pxa2xx_spi_dma_setup(struct driver_data *drv_data)
{
struct pxa2xx_spi_master *pdata = drv_data->master_info;
struct device *dev = &drv_data->pdev->dev;
- struct spi_master *master = drv_data->master;
+ struct spi_controller *master = drv_data->master;
dma_cap_mask_t mask;
dma_cap_zero(mask);
@@ -215,7 +215,7 @@ int pxa2xx_spi_dma_setup(struct driver_data *drv_data)
void pxa2xx_spi_dma_release(struct driver_data *drv_data)
{
- struct spi_master *master = drv_data->master;
+ struct spi_controller *master = drv_data->master;
if (master->dma_rx) {
dmaengine_terminate_sync(master->dma_rx);
diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c
index b0822d1dba29..82dcb88fcfba 100644
--- a/drivers/spi/spi-pxa2xx.c
+++ b/drivers/spi/spi-pxa2xx.c
@@ -415,10 +415,17 @@ static void cs_deassert(struct driver_data *drv_data)
{
struct chip_data *chip =
spi_get_ctldata(drv_data->master->cur_msg->spi);
+ unsigned long timeout;
if (drv_data->ssp_type == CE4100_SSP)
return;
+ /* Wait until SSP becomes idle before deasserting the CS */
+ timeout = jiffies + msecs_to_jiffies(10);
+ while (pxa2xx_spi_read(drv_data, SSSR) & SSSR_BSY &&
+ !time_after(jiffies, timeout))
+ cpu_relax();
+
if (chip->cs_control) {
chip->cs_control(PXA2XX_CS_DEASSERT);
return;
@@ -563,7 +570,6 @@ static void giveback(struct driver_data *drv_data)
{
struct spi_transfer* last_transfer;
struct spi_message *msg;
- unsigned long timeout;
msg = drv_data->master->cur_msg;
drv_data->cur_transfer = NULL;
@@ -575,12 +581,6 @@ static void giveback(struct driver_data *drv_data)
if (last_transfer->delay_usecs)
udelay(last_transfer->delay_usecs);
- /* Wait until SSP becomes idle before deasserting the CS */
- timeout = jiffies + msecs_to_jiffies(10);
- while (pxa2xx_spi_read(drv_data, SSSR) & SSSR_BSY &&
- !time_after(jiffies, timeout))
- cpu_relax();
-
/* Drop chip select UNLESS cs_change is true or we are returning
* a message with an error, or next message is for another chip
*/
@@ -962,7 +962,7 @@ static unsigned int pxa2xx_ssp_get_clk_div(struct driver_data *drv_data,
return clk_div << 8;
}
-static bool pxa2xx_spi_can_dma(struct spi_master *master,
+static bool pxa2xx_spi_can_dma(struct spi_controller *master,
struct spi_device *spi,
struct spi_transfer *xfer)
{
@@ -976,7 +976,7 @@ static bool pxa2xx_spi_can_dma(struct spi_master *master,
static void pump_transfers(unsigned long data)
{
struct driver_data *drv_data = (struct driver_data *)data;
- struct spi_master *master = drv_data->master;
+ struct spi_controller *master = drv_data->master;
struct spi_message *message = master->cur_msg;
struct chip_data *chip = spi_get_ctldata(message->spi);
u32 dma_thresh = chip->dma_threshold;
@@ -1182,10 +1182,10 @@ static void pump_transfers(unsigned long data)
pxa2xx_spi_write(drv_data, SSCR1, cr1);
}
-static int pxa2xx_spi_transfer_one_message(struct spi_master *master,
+static int pxa2xx_spi_transfer_one_message(struct spi_controller *master,
struct spi_message *msg)
{
- struct driver_data *drv_data = spi_master_get_devdata(master);
+ struct driver_data *drv_data = spi_controller_get_devdata(master);
/* Initial message state*/
msg->state = START_STATE;
@@ -1198,9 +1198,9 @@ static int pxa2xx_spi_transfer_one_message(struct spi_master *master,
return 0;
}
-static int pxa2xx_spi_unprepare_transfer(struct spi_master *master)
+static int pxa2xx_spi_unprepare_transfer(struct spi_controller *master)
{
- struct driver_data *drv_data = spi_master_get_devdata(master);
+ struct driver_data *drv_data = spi_controller_get_devdata(master);
/* Disable the SSP now */
pxa2xx_spi_write(drv_data, SSCR0,
@@ -1212,7 +1212,8 @@ static int pxa2xx_spi_unprepare_transfer(struct spi_master *master)
static int setup_cs(struct spi_device *spi, struct chip_data *chip,
struct pxa2xx_spi_chip *chip_info)
{
- struct driver_data *drv_data = spi_master_get_devdata(spi->master);
+ struct driver_data *drv_data =
+ spi_controller_get_devdata(spi->controller);
struct gpio_desc *gpiod;
int err = 0;
@@ -1270,7 +1271,8 @@ static int setup(struct spi_device *spi)
struct pxa2xx_spi_chip *chip_info;
struct chip_data *chip;
const struct lpss_config *config;
- struct driver_data *drv_data = spi_master_get_devdata(spi->master);
+ struct driver_data *drv_data =
+ spi_controller_get_devdata(spi->controller);
uint tx_thres, tx_hi_thres, rx_thres;
switch (drv_data->ssp_type) {
@@ -1410,7 +1412,8 @@ static int setup(struct spi_device *spi)
static void cleanup(struct spi_device *spi)
{
struct chip_data *chip = spi_get_ctldata(spi);
- struct driver_data *drv_data = spi_master_get_devdata(spi->master);
+ struct driver_data *drv_data =
+ spi_controller_get_devdata(spi->controller);
if (!chip)
return;
@@ -1575,9 +1578,10 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev)
}
#endif
-static int pxa2xx_spi_fw_translate_cs(struct spi_master *master, unsigned cs)
+static int pxa2xx_spi_fw_translate_cs(struct spi_controller *master,
+ unsigned int cs)
{
- struct driver_data *drv_data = spi_master_get_devdata(master);
+ struct driver_data *drv_data = spi_controller_get_devdata(master);
if (has_acpi_companion(&drv_data->pdev->dev)) {
switch (drv_data->ssp_type) {
@@ -1602,7 +1606,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct pxa2xx_spi_master *platform_info;
- struct spi_master *master;
+ struct spi_controller *master;
struct driver_data *drv_data;
struct ssp_device *ssp;
const struct lpss_config *config;
@@ -1633,7 +1637,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
pxa_ssp_free(ssp);
return -ENOMEM;
}
- drv_data = spi_master_get_devdata(master);
+ drv_data = spi_controller_get_devdata(master);
drv_data->master = master;
drv_data->master_info = platform_info;
drv_data->pdev = pdev;
@@ -1651,7 +1655,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
master->unprepare_transfer_hardware = pxa2xx_spi_unprepare_transfer;
master->fw_translate_cs = pxa2xx_spi_fw_translate_cs;
master->auto_runtime_pm = true;
- master->flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX;
+ master->flags = SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX;
drv_data->ssp_type = ssp->type;
@@ -1793,7 +1797,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
/* Register with the SPI framework */
platform_set_drvdata(pdev, drv_data);
- status = devm_spi_register_master(&pdev->dev, master);
+ status = devm_spi_register_controller(&pdev->dev, master);
if (status != 0) {
dev_err(&pdev->dev, "problem registering spi master\n");
goto out_error_clock_enabled;
@@ -1802,12 +1806,14 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
return status;
out_error_clock_enabled:
+ pm_runtime_put_noidle(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
clk_disable_unprepare(ssp->clk);
pxa2xx_spi_dma_release(drv_data);
free_irq(ssp->irq, drv_data);
out_error_master_alloc:
- spi_master_put(master);
+ spi_controller_put(master);
pxa_ssp_free(ssp);
return status;
}
@@ -1858,7 +1864,7 @@ static int pxa2xx_spi_suspend(struct device *dev)
struct ssp_device *ssp = drv_data->ssp;
int status;
- status = spi_master_suspend(drv_data->master);
+ status = spi_controller_suspend(drv_data->master);
if (status != 0)
return status;
pxa2xx_spi_write(drv_data, SSCR0, 0);
@@ -1884,7 +1890,7 @@ static int pxa2xx_spi_resume(struct device *dev)
lpss_ssp_setup(drv_data);
/* Start the queue running */
- status = spi_master_resume(drv_data->master);
+ status = spi_controller_resume(drv_data->master);
if (status != 0) {
dev_err(dev, "problem starting queue (%d)\n", status);
return status;
diff --git a/drivers/spi/spi-pxa2xx.h b/drivers/spi/spi-pxa2xx.h
index 94f7b0713281..513ec6c6e25b 100644
--- a/drivers/spi/spi-pxa2xx.h
+++ b/drivers/spi/spi-pxa2xx.h
@@ -31,7 +31,7 @@ struct driver_data {
/* SPI framework hookup */
enum pxa_ssp_type ssp_type;
- struct spi_master *master;
+ struct spi_controller *master;
/* PXA hookup */
struct pxa2xx_spi_master *master_info;
diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c
index 0835a8d88fb8..95dc4d78618d 100644
--- a/drivers/spi/spi-rspi.c
+++ b/drivers/spi/spi-rspi.c
@@ -535,7 +535,7 @@ static int rspi_dma_transfer(struct rspi_data *rspi, struct sg_table *tx,
/* First prepare and submit the DMA request(s), as this may fail */
if (rx) {
desc_rx = dmaengine_prep_slave_sg(rspi->master->dma_rx,
- rx->sgl, rx->nents, DMA_FROM_DEVICE,
+ rx->sgl, rx->nents, DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc_rx) {
ret = -EAGAIN;
@@ -555,7 +555,7 @@ static int rspi_dma_transfer(struct rspi_data *rspi, struct sg_table *tx,
if (tx) {
desc_tx = dmaengine_prep_slave_sg(rspi->master->dma_tx,
- tx->sgl, tx->nents, DMA_TO_DEVICE,
+ tx->sgl, tx->nents, DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc_tx) {
ret = -EAGAIN;
diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c
index c5dcfb434a49..ae086aab57d5 100644
--- a/drivers/spi/spi-sh-msiof.c
+++ b/drivers/spi/spi-sh-msiof.c
@@ -752,7 +752,7 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx,
if (rx) {
ier_bits |= IER_RDREQE | IER_RDMAE;
desc_rx = dmaengine_prep_slave_single(p->master->dma_rx,
- p->rx_dma_addr, len, DMA_FROM_DEVICE,
+ p->rx_dma_addr, len, DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc_rx)
return -EAGAIN;
@@ -769,7 +769,7 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx,
dma_sync_single_for_device(p->master->dma_tx->device->dev,
p->tx_dma_addr, len, DMA_TO_DEVICE);
desc_tx = dmaengine_prep_slave_single(p->master->dma_tx,
- p->tx_dma_addr, len, DMA_TO_DEVICE,
+ p->tx_dma_addr, len, DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc_tx) {
ret = -EAGAIN;
diff --git a/drivers/spi/spi-sprd-adi.c b/drivers/spi/spi-sprd-adi.c
index 5993bdbf79e4..197d4b0d81af 100644
--- a/drivers/spi/spi-sprd-adi.c
+++ b/drivers/spi/spi-sprd-adi.c
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: GPL-2.0
*/
+#include <linux/delay.h>
#include <linux/hwspinlock.h>
#include <linux/init.h>
#include <linux/io.h>
@@ -12,6 +13,7 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
+#include <linux/reboot.h>
#include <linux/spi/spi.h>
#include <linux/sizes.h>
@@ -67,6 +69,40 @@
#define ADI_READ_TIMEOUT 2000
#define REG_ADDR_LOW_MASK GENMASK(11, 0)
+/* Registers definitions for PMIC watchdog controller */
+#define REG_WDG_LOAD_LOW 0x80
+#define REG_WDG_LOAD_HIGH 0x84
+#define REG_WDG_CTRL 0x88
+#define REG_WDG_LOCK 0xa0
+
+/* Bits definitions for register REG_WDG_CTRL */
+#define BIT_WDG_RUN BIT(1)
+#define BIT_WDG_RST BIT(3)
+
+/* Registers definitions for PMIC */
+#define PMIC_RST_STATUS 0xee8
+#define PMIC_MODULE_EN 0xc08
+#define PMIC_CLK_EN 0xc18
+#define BIT_WDG_EN BIT(2)
+
+/* Definition of PMIC reset status register */
+#define HWRST_STATUS_RECOVERY 0x20
+#define HWRST_STATUS_NORMAL 0x40
+#define HWRST_STATUS_ALARM 0x50
+#define HWRST_STATUS_SLEEP 0x60
+#define HWRST_STATUS_FASTBOOT 0x30
+#define HWRST_STATUS_SPECIAL 0x70
+#define HWRST_STATUS_PANIC 0x80
+#define HWRST_STATUS_CFTREBOOT 0x90
+#define HWRST_STATUS_AUTODLOADER 0xa0
+#define HWRST_STATUS_IQMODE 0xb0
+#define HWRST_STATUS_SPRDISK 0xc0
+
+/* Use default timeout 50 ms that converts to watchdog values */
+#define WDG_LOAD_VAL ((50 * 1000) / 32768)
+#define WDG_LOAD_MASK GENMASK(15, 0)
+#define WDG_UNLOCK_KEY 0xe551
+
struct sprd_adi {
struct spi_controller *ctlr;
struct device *dev;
@@ -74,6 +110,7 @@ struct sprd_adi {
struct hwspinlock *hwlock;
unsigned long slave_vbase;
unsigned long slave_pbase;
+ struct notifier_block restart_handler;
};
static int sprd_adi_check_paddr(struct sprd_adi *sadi, u32 paddr)
@@ -123,7 +160,17 @@ static int sprd_adi_fifo_is_full(struct sprd_adi *sadi)
static int sprd_adi_read(struct sprd_adi *sadi, u32 reg_paddr, u32 *read_val)
{
int read_timeout = ADI_READ_TIMEOUT;
+ unsigned long flags;
u32 val, rd_addr;
+ int ret;
+
+ ret = hwspin_lock_timeout_irqsave(sadi->hwlock,
+ ADI_HWSPINLOCK_TIMEOUT,
+ &flags);
+ if (ret) {
+ dev_err(sadi->dev, "get the hw lock failed\n");
+ return ret;
+ }
/*
* Set the physical register address need to read into RD_CMD register,
@@ -147,7 +194,8 @@ static int sprd_adi_read(struct sprd_adi *sadi, u32 reg_paddr, u32 *read_val)
if (read_timeout == 0) {
dev_err(sadi->dev, "ADI read timeout\n");
- return -EBUSY;
+ ret = -EBUSY;
+ goto out;
}
/*
@@ -161,21 +209,35 @@ static int sprd_adi_read(struct sprd_adi *sadi, u32 reg_paddr, u32 *read_val)
if (rd_addr != (reg_paddr & REG_ADDR_LOW_MASK)) {
dev_err(sadi->dev, "read error, reg addr = 0x%x, val = 0x%x\n",
reg_paddr, val);
- return -EIO;
+ ret = -EIO;
+ goto out;
}
*read_val = val & RD_VALUE_MASK;
- return 0;
+
+out:
+ hwspin_unlock_irqrestore(sadi->hwlock, &flags);
+ return ret;
}
-static int sprd_adi_write(struct sprd_adi *sadi, unsigned long reg, u32 val)
+static int sprd_adi_write(struct sprd_adi *sadi, u32 reg_paddr, u32 val)
{
+ unsigned long reg = sprd_adi_to_vaddr(sadi, reg_paddr);
u32 timeout = ADI_FIFO_DRAIN_TIMEOUT;
+ unsigned long flags;
int ret;
+ ret = hwspin_lock_timeout_irqsave(sadi->hwlock,
+ ADI_HWSPINLOCK_TIMEOUT,
+ &flags);
+ if (ret) {
+ dev_err(sadi->dev, "get the hw lock failed\n");
+ return ret;
+ }
+
ret = sprd_adi_drain_fifo(sadi);
if (ret < 0)
- return ret;
+ goto out;
/*
* we should wait for write fifo is empty before writing data to PMIC
@@ -192,10 +254,12 @@ static int sprd_adi_write(struct sprd_adi *sadi, unsigned long reg, u32 val)
if (timeout == 0) {
dev_err(sadi->dev, "write fifo is full\n");
- return -EBUSY;
+ ret = -EBUSY;
}
- return 0;
+out:
+ hwspin_unlock_irqrestore(sadi->hwlock, &flags);
+ return ret;
}
static int sprd_adi_transfer_one(struct spi_controller *ctlr,
@@ -203,7 +267,6 @@ static int sprd_adi_transfer_one(struct spi_controller *ctlr,
struct spi_transfer *t)
{
struct sprd_adi *sadi = spi_controller_get_devdata(ctlr);
- unsigned long flags, virt_reg;
u32 phy_reg, val;
int ret;
@@ -214,16 +277,7 @@ static int sprd_adi_transfer_one(struct spi_controller *ctlr,
if (ret)
return ret;
- ret = hwspin_lock_timeout_irqsave(sadi->hwlock,
- ADI_HWSPINLOCK_TIMEOUT,
- &flags);
- if (ret) {
- dev_err(sadi->dev, "get the hw lock failed\n");
- return ret;
- }
-
ret = sprd_adi_read(sadi, phy_reg, &val);
- hwspin_unlock_irqrestore(sadi->hwlock, &flags);
if (ret)
return ret;
@@ -241,19 +295,8 @@ static int sprd_adi_transfer_one(struct spi_controller *ctlr,
if (ret)
return ret;
- virt_reg = sprd_adi_to_vaddr(sadi, phy_reg);
val = *p;
-
- ret = hwspin_lock_timeout_irqsave(sadi->hwlock,
- ADI_HWSPINLOCK_TIMEOUT,
- &flags);
- if (ret) {
- dev_err(sadi->dev, "get the hw lock failed\n");
- return ret;
- }
-
- ret = sprd_adi_write(sadi, virt_reg, val);
- hwspin_unlock_irqrestore(sadi->hwlock, &flags);
+ ret = sprd_adi_write(sadi, phy_reg, val);
if (ret)
return ret;
} else {
@@ -264,6 +307,72 @@ static int sprd_adi_transfer_one(struct spi_controller *ctlr,
return 0;
}
+static int sprd_adi_restart_handler(struct notifier_block *this,
+ unsigned long mode, void *cmd)
+{
+ struct sprd_adi *sadi = container_of(this, struct sprd_adi,
+ restart_handler);
+ u32 val, reboot_mode = 0;
+
+ if (!cmd)
+ reboot_mode = HWRST_STATUS_NORMAL;
+ else if (!strncmp(cmd, "recovery", 8))
+ reboot_mode = HWRST_STATUS_RECOVERY;
+ else if (!strncmp(cmd, "alarm", 5))
+ reboot_mode = HWRST_STATUS_ALARM;
+ else if (!strncmp(cmd, "fastsleep", 9))
+ reboot_mode = HWRST_STATUS_SLEEP;
+ else if (!strncmp(cmd, "bootloader", 10))
+ reboot_mode = HWRST_STATUS_FASTBOOT;
+ else if (!strncmp(cmd, "panic", 5))
+ reboot_mode = HWRST_STATUS_PANIC;
+ else if (!strncmp(cmd, "special", 7))
+ reboot_mode = HWRST_STATUS_SPECIAL;
+ else if (!strncmp(cmd, "cftreboot", 9))
+ reboot_mode = HWRST_STATUS_CFTREBOOT;
+ else if (!strncmp(cmd, "autodloader", 11))
+ reboot_mode = HWRST_STATUS_AUTODLOADER;
+ else if (!strncmp(cmd, "iqmode", 6))
+ reboot_mode = HWRST_STATUS_IQMODE;
+ else if (!strncmp(cmd, "sprdisk", 7))
+ reboot_mode = HWRST_STATUS_SPRDISK;
+ else
+ reboot_mode = HWRST_STATUS_NORMAL;
+
+ /* Record the reboot mode */
+ sprd_adi_read(sadi, sadi->slave_pbase + PMIC_RST_STATUS, &val);
+ val |= reboot_mode;
+ sprd_adi_write(sadi, sadi->slave_pbase + PMIC_RST_STATUS, val);
+
+ /* Enable the interface clock of the watchdog */
+ sprd_adi_read(sadi, sadi->slave_pbase + PMIC_MODULE_EN, &val);
+ val |= BIT_WDG_EN;
+ sprd_adi_write(sadi, sadi->slave_pbase + PMIC_MODULE_EN, val);
+
+ /* Enable the work clock of the watchdog */
+ sprd_adi_read(sadi, sadi->slave_pbase + PMIC_CLK_EN, &val);
+ val |= BIT_WDG_EN;
+ sprd_adi_write(sadi, sadi->slave_pbase + PMIC_CLK_EN, val);
+
+ /* Unlock the watchdog */
+ sprd_adi_write(sadi, sadi->slave_pbase + REG_WDG_LOCK, WDG_UNLOCK_KEY);
+
+ /* Load the watchdog timeout value, 50ms is always enough. */
+ sprd_adi_write(sadi, sadi->slave_pbase + REG_WDG_LOAD_LOW,
+ WDG_LOAD_VAL & WDG_LOAD_MASK);
+ sprd_adi_write(sadi, sadi->slave_pbase + REG_WDG_LOAD_HIGH, 0);
+
+ /* Start the watchdog to reset system */
+ sprd_adi_read(sadi, sadi->slave_pbase + REG_WDG_CTRL, &val);
+ val |= BIT_WDG_RUN | BIT_WDG_RST;
+ sprd_adi_write(sadi, sadi->slave_pbase + REG_WDG_CTRL, val);
+
+ mdelay(1000);
+
+ dev_emerg(sadi->dev, "Unable to restart system\n");
+ return NOTIFY_DONE;
+}
+
static void sprd_adi_hw_init(struct sprd_adi *sadi)
{
struct device_node *np = sadi->dev->of_node;
@@ -377,6 +486,14 @@ static int sprd_adi_probe(struct platform_device *pdev)
goto free_hwlock;
}
+ sadi->restart_handler.notifier_call = sprd_adi_restart_handler;
+ sadi->restart_handler.priority = 128;
+ ret = register_restart_handler(&sadi->restart_handler);
+ if (ret) {
+ dev_err(&pdev->dev, "can not register restart handler\n");
+ goto free_hwlock;
+ }
+
return 0;
free_hwlock:
@@ -391,6 +508,7 @@ static int sprd_adi_remove(struct platform_device *pdev)
struct spi_controller *ctlr = dev_get_drvdata(&pdev->dev);
struct sprd_adi *sadi = spi_controller_get_devdata(ctlr);
+ unregister_restart_handler(&sadi->restart_handler);
hwspin_lock_free(sadi->hwlock);
return 0;
}
diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c
index 3e12d5f87ee4..6f7b946b5ced 100644
--- a/drivers/spi/spi-tegra20-slink.c
+++ b/drivers/spi/spi-tegra20-slink.c
@@ -276,10 +276,10 @@ static unsigned tegra_slink_calculate_curr_xfer_param(
tspi->bytes_per_word = DIV_ROUND_UP(bits_per_word, 8);
if (bits_per_word == 8 || bits_per_word == 16) {
- tspi->is_packed = 1;
+ tspi->is_packed = true;
tspi->words_per_32bit = 32/bits_per_word;
} else {
- tspi->is_packed = 0;
+ tspi->is_packed = false;
tspi->words_per_32bit = 1;
}
tspi->packed_size = tegra_slink_get_packed_size(tspi, t);
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index b33a727a0158..7b213faa0a2b 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -779,8 +779,14 @@ static int spi_map_buf(struct spi_controller *ctlr, struct device *dev,
for (i = 0; i < sgs; i++) {
if (vmalloced_buf || kmap_buf) {
- min = min_t(size_t,
- len, desc_len - offset_in_page(buf));
+ /*
+ * Next scatterlist entry size is the minimum between
+ * the desc_len and the remaining buffer length that
+ * fits in a page.
+ */
+ min = min_t(size_t, desc_len,
+ min_t(size_t, len,
+ PAGE_SIZE - offset_in_page(buf)));
if (vmalloced_buf)
vm_page = vmalloc_to_page(buf);
else
@@ -2254,12 +2260,6 @@ void spi_unregister_controller(struct spi_controller *ctlr)
mutex_lock(&board_lock);
found = idr_find(&spi_master_idr, id);
mutex_unlock(&board_lock);
- if (found != ctlr) {
- dev_dbg(&ctlr->dev,
- "attempting to delete unregistered controller [%s]\n",
- dev_name(&ctlr->dev));
- return;
- }
if (ctlr->queued) {
if (spi_destroy_queue(ctlr))
dev_err(&ctlr->dev, "queue remove failed\n");
@@ -2272,7 +2272,8 @@ void spi_unregister_controller(struct spi_controller *ctlr)
device_unregister(&ctlr->dev);
/* free bus id */
mutex_lock(&board_lock);
- idr_remove(&spi_master_idr, id);
+ if (found == ctlr)
+ idr_remove(&spi_master_idr, id);
mutex_unlock(&board_lock);
}
EXPORT_SYMBOL_GPL(spi_unregister_controller);
diff --git a/drivers/ssb/Kconfig b/drivers/ssb/Kconfig
index b3f5cae98ea6..9371651d8017 100644
--- a/drivers/ssb/Kconfig
+++ b/drivers/ssb/Kconfig
@@ -117,7 +117,7 @@ config SSB_SERIAL
config SSB_DRIVER_PCICORE_POSSIBLE
bool
- depends on SSB_PCIHOST
+ depends on SSB_PCIHOST && SSB = y
default y
config SSB_DRIVER_PCICORE
diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c
index 65420a9f0e82..116594413f66 100644
--- a/drivers/ssb/main.c
+++ b/drivers/ssb/main.c
@@ -522,7 +522,7 @@ static int ssb_devices_register(struct ssb_bus *bus)
/* Set dev to NULL to not unregister
* dev on error unwinding. */
sdev->dev = NULL;
- kfree(devwrap);
+ put_device(dev);
goto error;
}
dev_idx++;
@@ -1116,7 +1116,7 @@ static bool ssb_dma_translation_special_bit(struct ssb_device *dev)
chip_id == 43231 || chip_id == 43222);
}
- return 0;
+ return false;
}
u32 ssb_dma_translation(struct ssb_device *dev)
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index e95ab683331e..d5926f0d3f6c 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -24,8 +24,6 @@ menuconfig STAGING
if STAGING
-source "drivers/staging/irda/net/Kconfig"
-
source "drivers/staging/ipx/Kconfig"
source "drivers/staging/ncpfs/Kconfig"
@@ -114,12 +112,24 @@ source "drivers/staging/greybus/Kconfig"
source "drivers/staging/vc04_services/Kconfig"
-source "drivers/staging/ccree/Kconfig"
-
source "drivers/staging/typec/Kconfig"
source "drivers/staging/vboxvideo/Kconfig"
source "drivers/staging/pi433/Kconfig"
+source "drivers/staging/mt7621-pinctrl/Kconfig"
+
+source "drivers/staging/mt7621-gpio/Kconfig"
+
+source "drivers/staging/mt7621-spi/Kconfig"
+
+source "drivers/staging/mt7621-dma/Kconfig"
+
+source "drivers/staging/mt7621-mmc/Kconfig"
+
+source "drivers/staging/mt7621-eth/Kconfig"
+
+source "drivers/staging/mt7621-dts/Kconfig"
+
endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index af8cd6a3a1f6..919753c3d3f6 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -5,8 +5,6 @@ obj-y += media/
obj-y += typec/
obj-$(CONFIG_IPX) += ipx/
obj-$(CONFIG_NCP_FS) += ncpfs/
-obj-$(CONFIG_IRDA) += irda/net/
-obj-$(CONFIG_IRDA) += irda/drivers/
obj-$(CONFIG_PRISM2_USB) += wlan-ng/
obj-$(CONFIG_COMEDI) += comedi/
obj-$(CONFIG_FB_OLPC_DCON) += olpc_dcon/
@@ -49,6 +47,13 @@ obj-$(CONFIG_MOST) += most/
obj-$(CONFIG_KS7010) += ks7010/
obj-$(CONFIG_GREYBUS) += greybus/
obj-$(CONFIG_BCM2835_VCHIQ) += vc04_services/
-obj-$(CONFIG_CRYPTO_DEV_CCREE) += ccree/
obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/
obj-$(CONFIG_PI433) += pi433/
+obj-$(CONFIG_SOC_MT7621) += mt7621-pci/
+obj-$(CONFIG_SOC_MT7621) += mt7621-pinctrl/
+obj-$(CONFIG_SOC_MT7621) += mt7621-gpio/
+obj-$(CONFIG_SOC_MT7621) += mt7621-spi/
+obj-$(CONFIG_SOC_MT7621) += mt7621-dma/
+obj-$(CONFIG_SOC_MT7621) += mt7621-mmc/
+obj-$(CONFIG_SOC_MT7621) += mt7621-eth/
+obj-$(CONFIG_SOC_MT7621) += mt7621-dts/
diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c
index 86580b6df33d..a1a0025b59e0 100644
--- a/drivers/staging/android/ashmem.c
+++ b/drivers/staging/android/ashmem.c
@@ -321,7 +321,7 @@ out_unlock:
static loff_t ashmem_llseek(struct file *file, loff_t offset, int origin)
{
struct ashmem_area *asma = file->private_data;
- int ret;
+ loff_t ret;
mutex_lock(&ashmem_mutex);
diff --git a/drivers/staging/android/ion/Kconfig b/drivers/staging/android/ion/Kconfig
index 8f6494158d3d..898e9a834ccc 100644
--- a/drivers/staging/android/ion/Kconfig
+++ b/drivers/staging/android/ion/Kconfig
@@ -4,7 +4,7 @@ menuconfig ION
select GENERIC_ALLOCATOR
select DMA_SHARED_BUFFER
---help---
- Chose this option to enable the ION Memory Manager,
+ Choose this option to enable the ION Memory Manager,
used by Android to efficiently allocate buffers
from userspace that can be shared between drivers.
If you're not using Android its probably safe to
diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c
index 57e0d8035b2e..e74db7902549 100644
--- a/drivers/staging/android/ion/ion.c
+++ b/drivers/staging/android/ion/ion.c
@@ -33,11 +33,6 @@
static struct ion_device *internal_dev;
static int heap_id;
-bool ion_buffer_cached(struct ion_buffer *buffer)
-{
- return !!(buffer->flags & ION_FLAG_CACHED);
-}
-
/* this function should only be called while dev->lock is held */
static void ion_buffer_add(struct ion_device *dev,
struct ion_buffer *buffer)
@@ -187,7 +182,7 @@ static struct sg_table *dup_sg_table(struct sg_table *table)
new_sg = new_table->sgl;
for_each_sg(table->sgl, sg, table->nents, i) {
memcpy(new_sg, sg, sizeof(*sg));
- sg->dma_address = 0;
+ new_sg->dma_address = 0;
new_sg = sg_next(new_sg);
}
@@ -527,7 +522,6 @@ DEFINE_SIMPLE_ATTRIBUTE(debug_shrink_fops, debug_shrink_get,
void ion_device_add_heap(struct ion_heap *heap)
{
- struct dentry *debug_file;
struct ion_device *dev = internal_dev;
int ret;
@@ -561,16 +555,8 @@ void ion_device_add_heap(struct ion_heap *heap)
char debug_name[64];
snprintf(debug_name, 64, "%s_shrink", heap->name);
- debug_file = debugfs_create_file(debug_name,
- 0644, dev->debug_root, heap,
- &debug_shrink_fops);
- if (!debug_file) {
- char buf[256], *path;
-
- path = dentry_path(dev->debug_root, buf, 256);
- pr_err("Failed to create heap shrinker debugfs at %s/%s\n",
- path, debug_name);
- }
+ debugfs_create_file(debug_name, 0644, dev->debug_root,
+ heap, &debug_shrink_fops);
}
dev->heap_cnt++;
@@ -599,12 +585,6 @@ static int ion_device_create(void)
}
idev->debug_root = debugfs_create_dir("ion", NULL);
- if (!idev->debug_root) {
- pr_err("ion: failed to create debugfs root directory.\n");
- goto debugfs_done;
- }
-
-debugfs_done:
idev->buffers = RB_ROOT;
mutex_init(&idev->buffer_lock);
init_rwsem(&idev->lock);
diff --git a/drivers/staging/android/ion/ion.h b/drivers/staging/android/ion/ion.h
index a238f23c9116..ea0897812780 100644
--- a/drivers/staging/android/ion/ion.h
+++ b/drivers/staging/android/ion/ion.h
@@ -185,23 +185,6 @@ struct ion_heap {
};
/**
- * ion_buffer_cached - this ion buffer is cached
- * @buffer: buffer
- *
- * indicates whether this ion buffer is cached
- */
-bool ion_buffer_cached(struct ion_buffer *buffer);
-
-/**
- * ion_buffer_fault_user_mappings - fault in user mappings of this buffer
- * @buffer: buffer
- *
- * indicates whether userspace mappings of this buffer will be faulted
- * in, this can affect how buffers are allocated from the heap.
- */
-bool ion_buffer_fault_user_mappings(struct ion_buffer *buffer);
-
-/**
* ion_device_add_heap - adds a heap to the ion device
* @heap: the heap to add
*/
@@ -311,7 +294,6 @@ size_t ion_heap_freelist_size(struct ion_heap *heap);
* @gfp_mask: gfp_mask to use from alloc
* @order: order of pages in the pool
* @list: plist node for list of pools
- * @cached: it's cached pool or not
*
* Allows you to keep a pool of pre allocated pages to use from your heap.
* Keeping a pool of pages that is ready for dma, ie any cached mapping have
@@ -321,7 +303,6 @@ size_t ion_heap_freelist_size(struct ion_heap *heap);
struct ion_page_pool {
int high_count;
int low_count;
- bool cached;
struct list_head high_items;
struct list_head low_items;
struct mutex mutex;
@@ -330,8 +311,7 @@ struct ion_page_pool {
struct plist_node list;
};
-struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order,
- bool cached);
+struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order);
void ion_page_pool_destroy(struct ion_page_pool *pool);
struct page *ion_page_pool_alloc(struct ion_page_pool *pool);
void ion_page_pool_free(struct ion_page_pool *pool, struct page *page);
diff --git a/drivers/staging/android/ion/ion_page_pool.c b/drivers/staging/android/ion/ion_page_pool.c
index b3017f12835f..db8f61446917 100644
--- a/drivers/staging/android/ion/ion_page_pool.c
+++ b/drivers/staging/android/ion/ion_page_pool.c
@@ -5,24 +5,15 @@
* Copyright (C) 2011 Google, Inc.
*/
-#include <linux/debugfs.h>
-#include <linux/dma-mapping.h>
-#include <linux/err.h>
-#include <linux/fs.h>
#include <linux/list.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/swap.h>
#include "ion.h"
-static void *ion_page_pool_alloc_pages(struct ion_page_pool *pool)
+static inline struct page *ion_page_pool_alloc_pages(struct ion_page_pool *pool)
{
- struct page *page = alloc_pages(pool->gfp_mask, pool->order);
-
- if (!page)
- return NULL;
- return page;
+ return alloc_pages(pool->gfp_mask, pool->order);
}
static void ion_page_pool_free_pages(struct ion_page_pool *pool,
@@ -31,7 +22,7 @@ static void ion_page_pool_free_pages(struct ion_page_pool *pool,
__free_pages(page, pool->order);
}
-static int ion_page_pool_add(struct ion_page_pool *pool, struct page *page)
+static void ion_page_pool_add(struct ion_page_pool *pool, struct page *page)
{
mutex_lock(&pool->mutex);
if (PageHighMem(page)) {
@@ -42,7 +33,6 @@ static int ion_page_pool_add(struct ion_page_pool *pool, struct page *page)
pool->low_count++;
}
mutex_unlock(&pool->mutex);
- return 0;
}
static struct page *ion_page_pool_remove(struct ion_page_pool *pool, bool high)
@@ -84,13 +74,9 @@ struct page *ion_page_pool_alloc(struct ion_page_pool *pool)
void ion_page_pool_free(struct ion_page_pool *pool, struct page *page)
{
- int ret;
-
BUG_ON(pool->order != compound_order(page));
- ret = ion_page_pool_add(pool, page);
- if (ret)
- ion_page_pool_free_pages(pool, page);
+ ion_page_pool_add(pool, page);
}
static int ion_page_pool_total(struct ion_page_pool *pool, bool high)
@@ -137,8 +123,7 @@ int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask,
return freed;
}
-struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order,
- bool cached)
+struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order)
{
struct ion_page_pool *pool = kmalloc(sizeof(*pool), GFP_KERNEL);
@@ -152,8 +137,6 @@ struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order,
pool->order = order;
mutex_init(&pool->mutex);
plist_node_init(&pool->list, order);
- if (cached)
- pool->cached = true;
return pool;
}
@@ -162,9 +145,3 @@ void ion_page_pool_destroy(struct ion_page_pool *pool)
{
kfree(pool);
}
-
-static int __init ion_page_pool_init(void)
-{
- return 0;
-}
-device_initcall(ion_page_pool_init);
diff --git a/drivers/staging/android/ion/ion_system_heap.c b/drivers/staging/android/ion/ion_system_heap.c
index bc19cdd30637..701eb9f3b0f1 100644
--- a/drivers/staging/android/ion/ion_system_heap.c
+++ b/drivers/staging/android/ion/ion_system_heap.c
@@ -41,31 +41,16 @@ static inline unsigned int order_to_size(int order)
struct ion_system_heap {
struct ion_heap heap;
- struct ion_page_pool *uncached_pools[NUM_ORDERS];
- struct ion_page_pool *cached_pools[NUM_ORDERS];
+ struct ion_page_pool *pools[NUM_ORDERS];
};
-/**
- * The page from page-pool are all zeroed before. We need do cache
- * clean for cached buffer. The uncached buffer are always non-cached
- * since it's allocated. So no need for non-cached pages.
- */
static struct page *alloc_buffer_page(struct ion_system_heap *heap,
struct ion_buffer *buffer,
unsigned long order)
{
- bool cached = ion_buffer_cached(buffer);
- struct ion_page_pool *pool;
- struct page *page;
+ struct ion_page_pool *pool = heap->pools[order_to_index(order)];
- if (!cached)
- pool = heap->uncached_pools[order_to_index(order)];
- else
- pool = heap->cached_pools[order_to_index(order)];
-
- page = ion_page_pool_alloc(pool);
-
- return page;
+ return ion_page_pool_alloc(pool);
}
static void free_buffer_page(struct ion_system_heap *heap,
@@ -73,7 +58,6 @@ static void free_buffer_page(struct ion_system_heap *heap,
{
struct ion_page_pool *pool;
unsigned int order = compound_order(page);
- bool cached = ion_buffer_cached(buffer);
/* go to system */
if (buffer->private_flags & ION_PRIV_FLAG_SHRINKER_FREE) {
@@ -81,10 +65,7 @@ static void free_buffer_page(struct ion_system_heap *heap,
return;
}
- if (!cached)
- pool = heap->uncached_pools[order_to_index(order)];
- else
- pool = heap->cached_pools[order_to_index(order)];
+ pool = heap->pools[order_to_index(order)];
ion_page_pool_free(pool, page);
}
@@ -190,8 +171,7 @@ static void ion_system_heap_free(struct ion_buffer *buffer)
static int ion_system_heap_shrink(struct ion_heap *heap, gfp_t gfp_mask,
int nr_to_scan)
{
- struct ion_page_pool *uncached_pool;
- struct ion_page_pool *cached_pool;
+ struct ion_page_pool *pool;
struct ion_system_heap *sys_heap;
int nr_total = 0;
int i, nr_freed;
@@ -203,26 +183,15 @@ static int ion_system_heap_shrink(struct ion_heap *heap, gfp_t gfp_mask,
only_scan = 1;
for (i = 0; i < NUM_ORDERS; i++) {
- uncached_pool = sys_heap->uncached_pools[i];
- cached_pool = sys_heap->cached_pools[i];
+ pool = sys_heap->pools[i];
if (only_scan) {
- nr_total += ion_page_pool_shrink(uncached_pool,
+ nr_total += ion_page_pool_shrink(pool,
gfp_mask,
nr_to_scan);
- nr_total += ion_page_pool_shrink(cached_pool,
- gfp_mask,
- nr_to_scan);
} else {
- nr_freed = ion_page_pool_shrink(uncached_pool,
- gfp_mask,
- nr_to_scan);
- nr_to_scan -= nr_freed;
- nr_total += nr_freed;
- if (nr_to_scan <= 0)
- break;
- nr_freed = ion_page_pool_shrink(cached_pool,
+ nr_freed = ion_page_pool_shrink(pool,
gfp_mask,
nr_to_scan);
nr_to_scan -= nr_freed;
@@ -253,26 +222,16 @@ static int ion_system_heap_debug_show(struct ion_heap *heap, struct seq_file *s,
struct ion_page_pool *pool;
for (i = 0; i < NUM_ORDERS; i++) {
- pool = sys_heap->uncached_pools[i];
+ pool = sys_heap->pools[i];
- seq_printf(s, "%d order %u highmem pages uncached %lu total\n",
+ seq_printf(s, "%d order %u highmem pages %lu total\n",
pool->high_count, pool->order,
(PAGE_SIZE << pool->order) * pool->high_count);
- seq_printf(s, "%d order %u lowmem pages uncached %lu total\n",
+ seq_printf(s, "%d order %u lowmem pages %lu total\n",
pool->low_count, pool->order,
(PAGE_SIZE << pool->order) * pool->low_count);
}
- for (i = 0; i < NUM_ORDERS; i++) {
- pool = sys_heap->cached_pools[i];
-
- seq_printf(s, "%d order %u highmem pages cached %lu total\n",
- pool->high_count, pool->order,
- (PAGE_SIZE << pool->order) * pool->high_count);
- seq_printf(s, "%d order %u lowmem pages cached %lu total\n",
- pool->low_count, pool->order,
- (PAGE_SIZE << pool->order) * pool->low_count);
- }
return 0;
}
@@ -285,8 +244,7 @@ static void ion_system_heap_destroy_pools(struct ion_page_pool **pools)
ion_page_pool_destroy(pools[i]);
}
-static int ion_system_heap_create_pools(struct ion_page_pool **pools,
- bool cached)
+static int ion_system_heap_create_pools(struct ion_page_pool **pools)
{
int i;
gfp_t gfp_flags = low_order_gfp_flags;
@@ -297,7 +255,7 @@ static int ion_system_heap_create_pools(struct ion_page_pool **pools,
if (orders[i] > 4)
gfp_flags = high_order_gfp_flags;
- pool = ion_page_pool_create(gfp_flags, orders[i], cached);
+ pool = ion_page_pool_create(gfp_flags, orders[i]);
if (!pool)
goto err_create_pool;
pools[i] = pool;
@@ -320,18 +278,12 @@ static struct ion_heap *__ion_system_heap_create(void)
heap->heap.type = ION_HEAP_TYPE_SYSTEM;
heap->heap.flags = ION_HEAP_FLAG_DEFER_FREE;
- if (ion_system_heap_create_pools(heap->uncached_pools, false))
+ if (ion_system_heap_create_pools(heap->pools))
goto free_heap;
- if (ion_system_heap_create_pools(heap->cached_pools, true))
- goto destroy_uncached_pools;
-
heap->heap.debug_show = ion_system_heap_debug_show;
return &heap->heap;
-destroy_uncached_pools:
- ion_system_heap_destroy_pools(heap->uncached_pools);
-
free_heap:
kfree(heap);
return ERR_PTR(-ENOMEM);
diff --git a/drivers/staging/ccree/Kconfig b/drivers/staging/ccree/Kconfig
deleted file mode 100644
index c94dfe8adb63..000000000000
--- a/drivers/staging/ccree/Kconfig
+++ /dev/null
@@ -1,27 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-
-config CRYPTO_DEV_CCREE
- tristate "Support for ARM TrustZone CryptoCell C7XX family of Crypto accelerators"
- depends on CRYPTO && CRYPTO_HW && OF && HAS_DMA
- default n
- select CRYPTO_HASH
- select CRYPTO_BLKCIPHER
- select CRYPTO_DES
- select CRYPTO_AEAD
- select CRYPTO_AUTHENC
- select CRYPTO_SHA1
- select CRYPTO_MD5
- select CRYPTO_SHA256
- select CRYPTO_SHA512
- select CRYPTO_HMAC
- select CRYPTO_AES
- select CRYPTO_CBC
- select CRYPTO_ECB
- select CRYPTO_CTR
- select CRYPTO_XTS
- help
- Say 'Y' to enable a driver for the Arm TrustZone CryptoCell
- C7xx. Currently only the CryptoCell 712 REE is supported.
- Choose this if you wish to use hardware acceleration of
- cryptographic operations on the system REE.
- If unsure say Y.
diff --git a/drivers/staging/ccree/TODO b/drivers/staging/ccree/TODO
deleted file mode 100644
index b8e163d98f91..000000000000
--- a/drivers/staging/ccree/TODO
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-*************************************************************************
-* *
-* Arm Trust Zone CryptoCell REE Linux driver upstreaming TODO items *
-* *
-*************************************************************************
-
-1. ???
-
diff --git a/drivers/staging/ccree/cc_aead.c b/drivers/staging/ccree/cc_aead.c
deleted file mode 100644
index b58413172231..000000000000
--- a/drivers/staging/ccree/cc_aead.c
+++ /dev/null
@@ -1,2701 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <crypto/algapi.h>
-#include <crypto/internal/aead.h>
-#include <crypto/authenc.h>
-#include <crypto/des.h>
-#include <linux/rtnetlink.h>
-#include "cc_driver.h"
-#include "cc_buffer_mgr.h"
-#include "cc_aead.h"
-#include "cc_request_mgr.h"
-#include "cc_hash.h"
-#include "cc_sram_mgr.h"
-
-#define template_aead template_u.aead
-
-#define MAX_AEAD_SETKEY_SEQ 12
-#define MAX_AEAD_PROCESS_SEQ 23
-
-#define MAX_HMAC_DIGEST_SIZE (SHA256_DIGEST_SIZE)
-#define MAX_HMAC_BLOCK_SIZE (SHA256_BLOCK_SIZE)
-
-#define AES_CCM_RFC4309_NONCE_SIZE 3
-#define MAX_NONCE_SIZE CTR_RFC3686_NONCE_SIZE
-
-/* Value of each ICV_CMP byte (of 8) in case of success */
-#define ICV_VERIF_OK 0x01
-
-struct cc_aead_handle {
- cc_sram_addr_t sram_workspace_addr;
- struct list_head aead_list;
-};
-
-struct cc_hmac_s {
- u8 *padded_authkey;
- u8 *ipad_opad; /* IPAD, OPAD*/
- dma_addr_t padded_authkey_dma_addr;
- dma_addr_t ipad_opad_dma_addr;
-};
-
-struct cc_xcbc_s {
- u8 *xcbc_keys; /* K1,K2,K3 */
- dma_addr_t xcbc_keys_dma_addr;
-};
-
-struct cc_aead_ctx {
- struct cc_drvdata *drvdata;
- u8 ctr_nonce[MAX_NONCE_SIZE]; /* used for ctr3686 iv and aes ccm */
- u8 *enckey;
- dma_addr_t enckey_dma_addr;
- union {
- struct cc_hmac_s hmac;
- struct cc_xcbc_s xcbc;
- } auth_state;
- unsigned int enc_keylen;
- unsigned int auth_keylen;
- unsigned int authsize; /* Actual (reduced?) size of the MAC/ICv */
- enum drv_cipher_mode cipher_mode;
- enum cc_flow_mode flow_mode;
- enum drv_hash_mode auth_mode;
-};
-
-static inline bool valid_assoclen(struct aead_request *req)
-{
- return ((req->assoclen == 16) || (req->assoclen == 20));
-}
-
-static void cc_aead_exit(struct crypto_aead *tfm)
-{
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
- struct device *dev = drvdata_to_dev(ctx->drvdata);
-
- dev_dbg(dev, "Clearing context @%p for %s\n", crypto_aead_ctx(tfm),
- crypto_tfm_alg_name(&tfm->base));
-
- /* Unmap enckey buffer */
- if (ctx->enckey) {
- dma_free_coherent(dev, AES_MAX_KEY_SIZE, ctx->enckey,
- ctx->enckey_dma_addr);
- dev_dbg(dev, "Freed enckey DMA buffer enckey_dma_addr=%pad\n",
- &ctx->enckey_dma_addr);
- ctx->enckey_dma_addr = 0;
- ctx->enckey = NULL;
- }
-
- if (ctx->auth_mode == DRV_HASH_XCBC_MAC) { /* XCBC authetication */
- struct cc_xcbc_s *xcbc = &ctx->auth_state.xcbc;
-
- if (xcbc->xcbc_keys) {
- dma_free_coherent(dev, CC_AES_128_BIT_KEY_SIZE * 3,
- xcbc->xcbc_keys,
- xcbc->xcbc_keys_dma_addr);
- }
- dev_dbg(dev, "Freed xcbc_keys DMA buffer xcbc_keys_dma_addr=%pad\n",
- &xcbc->xcbc_keys_dma_addr);
- xcbc->xcbc_keys_dma_addr = 0;
- xcbc->xcbc_keys = NULL;
- } else if (ctx->auth_mode != DRV_HASH_NULL) { /* HMAC auth. */
- struct cc_hmac_s *hmac = &ctx->auth_state.hmac;
-
- if (hmac->ipad_opad) {
- dma_free_coherent(dev, 2 * MAX_HMAC_DIGEST_SIZE,
- hmac->ipad_opad,
- hmac->ipad_opad_dma_addr);
- dev_dbg(dev, "Freed ipad_opad DMA buffer ipad_opad_dma_addr=%pad\n",
- &hmac->ipad_opad_dma_addr);
- hmac->ipad_opad_dma_addr = 0;
- hmac->ipad_opad = NULL;
- }
- if (hmac->padded_authkey) {
- dma_free_coherent(dev, MAX_HMAC_BLOCK_SIZE,
- hmac->padded_authkey,
- hmac->padded_authkey_dma_addr);
- dev_dbg(dev, "Freed padded_authkey DMA buffer padded_authkey_dma_addr=%pad\n",
- &hmac->padded_authkey_dma_addr);
- hmac->padded_authkey_dma_addr = 0;
- hmac->padded_authkey = NULL;
- }
- }
-}
-
-static int cc_aead_init(struct crypto_aead *tfm)
-{
- struct aead_alg *alg = crypto_aead_alg(tfm);
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
- struct cc_crypto_alg *cc_alg =
- container_of(alg, struct cc_crypto_alg, aead_alg);
- struct device *dev = drvdata_to_dev(cc_alg->drvdata);
-
- dev_dbg(dev, "Initializing context @%p for %s\n", ctx,
- crypto_tfm_alg_name(&tfm->base));
-
- /* Initialize modes in instance */
- ctx->cipher_mode = cc_alg->cipher_mode;
- ctx->flow_mode = cc_alg->flow_mode;
- ctx->auth_mode = cc_alg->auth_mode;
- ctx->drvdata = cc_alg->drvdata;
- crypto_aead_set_reqsize(tfm, sizeof(struct aead_req_ctx));
-
- /* Allocate key buffer, cache line aligned */
- ctx->enckey = dma_alloc_coherent(dev, AES_MAX_KEY_SIZE,
- &ctx->enckey_dma_addr, GFP_KERNEL);
- if (!ctx->enckey) {
- dev_err(dev, "Failed allocating key buffer\n");
- goto init_failed;
- }
- dev_dbg(dev, "Allocated enckey buffer in context ctx->enckey=@%p\n",
- ctx->enckey);
-
- /* Set default authlen value */
-
- if (ctx->auth_mode == DRV_HASH_XCBC_MAC) { /* XCBC authetication */
- struct cc_xcbc_s *xcbc = &ctx->auth_state.xcbc;
- const unsigned int key_size = CC_AES_128_BIT_KEY_SIZE * 3;
-
- /* Allocate dma-coherent buffer for XCBC's K1+K2+K3 */
- /* (and temporary for user key - up to 256b) */
- xcbc->xcbc_keys = dma_alloc_coherent(dev, key_size,
- &xcbc->xcbc_keys_dma_addr,
- GFP_KERNEL);
- if (!xcbc->xcbc_keys) {
- dev_err(dev, "Failed allocating buffer for XCBC keys\n");
- goto init_failed;
- }
- } else if (ctx->auth_mode != DRV_HASH_NULL) { /* HMAC authentication */
- struct cc_hmac_s *hmac = &ctx->auth_state.hmac;
- const unsigned int digest_size = 2 * MAX_HMAC_DIGEST_SIZE;
- dma_addr_t *pkey_dma = &hmac->padded_authkey_dma_addr;
-
- /* Allocate dma-coherent buffer for IPAD + OPAD */
- hmac->ipad_opad = dma_alloc_coherent(dev, digest_size,
- &hmac->ipad_opad_dma_addr,
- GFP_KERNEL);
-
- if (!hmac->ipad_opad) {
- dev_err(dev, "Failed allocating IPAD/OPAD buffer\n");
- goto init_failed;
- }
-
- dev_dbg(dev, "Allocated authkey buffer in context ctx->authkey=@%p\n",
- hmac->ipad_opad);
-
- hmac->padded_authkey = dma_alloc_coherent(dev,
- MAX_HMAC_BLOCK_SIZE,
- pkey_dma,
- GFP_KERNEL);
-
- if (!hmac->padded_authkey) {
- dev_err(dev, "failed to allocate padded_authkey\n");
- goto init_failed;
- }
- } else {
- ctx->auth_state.hmac.ipad_opad = NULL;
- ctx->auth_state.hmac.padded_authkey = NULL;
- }
-
- return 0;
-
-init_failed:
- cc_aead_exit(tfm);
- return -ENOMEM;
-}
-
-static void cc_aead_complete(struct device *dev, void *cc_req, int err)
-{
- struct aead_request *areq = (struct aead_request *)cc_req;
- struct aead_req_ctx *areq_ctx = aead_request_ctx(areq);
- struct crypto_aead *tfm = crypto_aead_reqtfm(cc_req);
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
-
- cc_unmap_aead_request(dev, areq);
-
- /* Restore ordinary iv pointer */
- areq->iv = areq_ctx->backup_iv;
-
- if (err)
- goto done;
-
- if (areq_ctx->gen_ctx.op_type == DRV_CRYPTO_DIRECTION_DECRYPT) {
- if (memcmp(areq_ctx->mac_buf, areq_ctx->icv_virt_addr,
- ctx->authsize) != 0) {
- dev_dbg(dev, "Payload authentication failure, (auth-size=%d, cipher=%d)\n",
- ctx->authsize, ctx->cipher_mode);
- /* In case of payload authentication failure, MUST NOT
- * revealed the decrypted message --> zero its memory.
- */
- cc_zero_sgl(areq->dst, areq_ctx->cryptlen);
- err = -EBADMSG;
- }
- } else { /*ENCRYPT*/
- if (areq_ctx->is_icv_fragmented) {
- u32 skip = areq->cryptlen + areq_ctx->dst_offset;
-
- cc_copy_sg_portion(dev, areq_ctx->mac_buf,
- areq_ctx->dst_sgl, skip,
- (skip + ctx->authsize),
- CC_SG_FROM_BUF);
- }
-
- /* If an IV was generated, copy it back to the user provided
- * buffer.
- */
- if (areq_ctx->backup_giv) {
- if (ctx->cipher_mode == DRV_CIPHER_CTR)
- memcpy(areq_ctx->backup_giv, areq_ctx->ctr_iv +
- CTR_RFC3686_NONCE_SIZE,
- CTR_RFC3686_IV_SIZE);
- else if (ctx->cipher_mode == DRV_CIPHER_CCM)
- memcpy(areq_ctx->backup_giv, areq_ctx->ctr_iv +
- CCM_BLOCK_IV_OFFSET, CCM_BLOCK_IV_SIZE);
- }
- }
-done:
- aead_request_complete(areq, err);
-}
-
-static int xcbc_setkey(struct cc_hw_desc *desc, struct cc_aead_ctx *ctx)
-{
- /* Load the AES key */
- hw_desc_init(&desc[0]);
- /* We are using for the source/user key the same buffer
- * as for the output keys, * because after this key loading it
- * is not needed anymore
- */
- set_din_type(&desc[0], DMA_DLLI,
- ctx->auth_state.xcbc.xcbc_keys_dma_addr, ctx->auth_keylen,
- NS_BIT);
- set_cipher_mode(&desc[0], DRV_CIPHER_ECB);
- set_cipher_config0(&desc[0], DRV_CRYPTO_DIRECTION_ENCRYPT);
- set_key_size_aes(&desc[0], ctx->auth_keylen);
- set_flow_mode(&desc[0], S_DIN_to_AES);
- set_setup_mode(&desc[0], SETUP_LOAD_KEY0);
-
- hw_desc_init(&desc[1]);
- set_din_const(&desc[1], 0x01010101, CC_AES_128_BIT_KEY_SIZE);
- set_flow_mode(&desc[1], DIN_AES_DOUT);
- set_dout_dlli(&desc[1], ctx->auth_state.xcbc.xcbc_keys_dma_addr,
- AES_KEYSIZE_128, NS_BIT, 0);
-
- hw_desc_init(&desc[2]);
- set_din_const(&desc[2], 0x02020202, CC_AES_128_BIT_KEY_SIZE);
- set_flow_mode(&desc[2], DIN_AES_DOUT);
- set_dout_dlli(&desc[2], (ctx->auth_state.xcbc.xcbc_keys_dma_addr
- + AES_KEYSIZE_128),
- AES_KEYSIZE_128, NS_BIT, 0);
-
- hw_desc_init(&desc[3]);
- set_din_const(&desc[3], 0x03030303, CC_AES_128_BIT_KEY_SIZE);
- set_flow_mode(&desc[3], DIN_AES_DOUT);
- set_dout_dlli(&desc[3], (ctx->auth_state.xcbc.xcbc_keys_dma_addr
- + 2 * AES_KEYSIZE_128),
- AES_KEYSIZE_128, NS_BIT, 0);
-
- return 4;
-}
-
-static int hmac_setkey(struct cc_hw_desc *desc, struct cc_aead_ctx *ctx)
-{
- unsigned int hmac_pad_const[2] = { HMAC_IPAD_CONST, HMAC_OPAD_CONST };
- unsigned int digest_ofs = 0;
- unsigned int hash_mode = (ctx->auth_mode == DRV_HASH_SHA1) ?
- DRV_HASH_HW_SHA1 : DRV_HASH_HW_SHA256;
- unsigned int digest_size = (ctx->auth_mode == DRV_HASH_SHA1) ?
- CC_SHA1_DIGEST_SIZE : CC_SHA256_DIGEST_SIZE;
- struct cc_hmac_s *hmac = &ctx->auth_state.hmac;
-
- int idx = 0;
- int i;
-
- /* calc derived HMAC key */
- for (i = 0; i < 2; i++) {
- /* Load hash initial state */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], hash_mode);
- set_din_sram(&desc[idx],
- cc_larval_digest_addr(ctx->drvdata,
- ctx->auth_mode),
- digest_size);
- set_flow_mode(&desc[idx], S_DIN_to_HASH);
- set_setup_mode(&desc[idx], SETUP_LOAD_STATE0);
- idx++;
-
- /* Load the hash current length*/
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], hash_mode);
- set_din_const(&desc[idx], 0, HASH_LEN_SIZE);
- set_flow_mode(&desc[idx], S_DIN_to_HASH);
- set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
- idx++;
-
- /* Prepare ipad key */
- hw_desc_init(&desc[idx]);
- set_xor_val(&desc[idx], hmac_pad_const[i]);
- set_cipher_mode(&desc[idx], hash_mode);
- set_flow_mode(&desc[idx], S_DIN_to_HASH);
- set_setup_mode(&desc[idx], SETUP_LOAD_STATE1);
- idx++;
-
- /* Perform HASH update */
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_DLLI,
- hmac->padded_authkey_dma_addr,
- SHA256_BLOCK_SIZE, NS_BIT);
- set_cipher_mode(&desc[idx], hash_mode);
- set_xor_active(&desc[idx]);
- set_flow_mode(&desc[idx], DIN_HASH);
- idx++;
-
- /* Get the digset */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], hash_mode);
- set_dout_dlli(&desc[idx],
- (hmac->ipad_opad_dma_addr + digest_ofs),
- digest_size, NS_BIT, 0);
- set_flow_mode(&desc[idx], S_HASH_to_DOUT);
- set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
- set_cipher_config1(&desc[idx], HASH_PADDING_DISABLED);
- idx++;
-
- digest_ofs += digest_size;
- }
-
- return idx;
-}
-
-static int validate_keys_sizes(struct cc_aead_ctx *ctx)
-{
- struct device *dev = drvdata_to_dev(ctx->drvdata);
-
- dev_dbg(dev, "enc_keylen=%u authkeylen=%u\n",
- ctx->enc_keylen, ctx->auth_keylen);
-
- switch (ctx->auth_mode) {
- case DRV_HASH_SHA1:
- case DRV_HASH_SHA256:
- break;
- case DRV_HASH_XCBC_MAC:
- if (ctx->auth_keylen != AES_KEYSIZE_128 &&
- ctx->auth_keylen != AES_KEYSIZE_192 &&
- ctx->auth_keylen != AES_KEYSIZE_256)
- return -ENOTSUPP;
- break;
- case DRV_HASH_NULL: /* Not authenc (e.g., CCM) - no auth_key) */
- if (ctx->auth_keylen > 0)
- return -EINVAL;
- break;
- default:
- dev_err(dev, "Invalid auth_mode=%d\n", ctx->auth_mode);
- return -EINVAL;
- }
- /* Check cipher key size */
- if (ctx->flow_mode == S_DIN_to_DES) {
- if (ctx->enc_keylen != DES3_EDE_KEY_SIZE) {
- dev_err(dev, "Invalid cipher(3DES) key size: %u\n",
- ctx->enc_keylen);
- return -EINVAL;
- }
- } else { /* Default assumed to be AES ciphers */
- if (ctx->enc_keylen != AES_KEYSIZE_128 &&
- ctx->enc_keylen != AES_KEYSIZE_192 &&
- ctx->enc_keylen != AES_KEYSIZE_256) {
- dev_err(dev, "Invalid cipher(AES) key size: %u\n",
- ctx->enc_keylen);
- return -EINVAL;
- }
- }
-
- return 0; /* All tests of keys sizes passed */
-}
-
-/* This function prepers the user key so it can pass to the hmac processing
- * (copy to intenral buffer or hash in case of key longer than block
- */
-static int
-cc_get_plain_hmac_key(struct crypto_aead *tfm, const u8 *key,
- unsigned int keylen)
-{
- dma_addr_t key_dma_addr = 0;
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
- struct device *dev = drvdata_to_dev(ctx->drvdata);
- u32 larval_addr = cc_larval_digest_addr(ctx->drvdata, ctx->auth_mode);
- struct cc_crypto_req cc_req = {};
- unsigned int blocksize;
- unsigned int digestsize;
- unsigned int hashmode;
- unsigned int idx = 0;
- int rc = 0;
- struct cc_hw_desc desc[MAX_AEAD_SETKEY_SEQ];
- dma_addr_t padded_authkey_dma_addr =
- ctx->auth_state.hmac.padded_authkey_dma_addr;
-
- switch (ctx->auth_mode) { /* auth_key required and >0 */
- case DRV_HASH_SHA1:
- blocksize = SHA1_BLOCK_SIZE;
- digestsize = SHA1_DIGEST_SIZE;
- hashmode = DRV_HASH_HW_SHA1;
- break;
- case DRV_HASH_SHA256:
- default:
- blocksize = SHA256_BLOCK_SIZE;
- digestsize = SHA256_DIGEST_SIZE;
- hashmode = DRV_HASH_HW_SHA256;
- }
-
- if (keylen != 0) {
- key_dma_addr = dma_map_single(dev, (void *)key, keylen,
- DMA_TO_DEVICE);
- if (dma_mapping_error(dev, key_dma_addr)) {
- dev_err(dev, "Mapping key va=0x%p len=%u for DMA failed\n",
- key, keylen);
- return -ENOMEM;
- }
- if (keylen > blocksize) {
- /* Load hash initial state */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], hashmode);
- set_din_sram(&desc[idx], larval_addr, digestsize);
- set_flow_mode(&desc[idx], S_DIN_to_HASH);
- set_setup_mode(&desc[idx], SETUP_LOAD_STATE0);
- idx++;
-
- /* Load the hash current length*/
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], hashmode);
- set_din_const(&desc[idx], 0, HASH_LEN_SIZE);
- set_cipher_config1(&desc[idx], HASH_PADDING_ENABLED);
- set_flow_mode(&desc[idx], S_DIN_to_HASH);
- set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
- idx++;
-
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_DLLI,
- key_dma_addr, keylen, NS_BIT);
- set_flow_mode(&desc[idx], DIN_HASH);
- idx++;
-
- /* Get hashed key */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], hashmode);
- set_dout_dlli(&desc[idx], padded_authkey_dma_addr,
- digestsize, NS_BIT, 0);
- set_flow_mode(&desc[idx], S_HASH_to_DOUT);
- set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
- set_cipher_config1(&desc[idx], HASH_PADDING_DISABLED);
- set_cipher_config0(&desc[idx],
- HASH_DIGEST_RESULT_LITTLE_ENDIAN);
- idx++;
-
- hw_desc_init(&desc[idx]);
- set_din_const(&desc[idx], 0, (blocksize - digestsize));
- set_flow_mode(&desc[idx], BYPASS);
- set_dout_dlli(&desc[idx], (padded_authkey_dma_addr +
- digestsize), (blocksize - digestsize),
- NS_BIT, 0);
- idx++;
- } else {
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_DLLI, key_dma_addr,
- keylen, NS_BIT);
- set_flow_mode(&desc[idx], BYPASS);
- set_dout_dlli(&desc[idx], padded_authkey_dma_addr,
- keylen, NS_BIT, 0);
- idx++;
-
- if ((blocksize - keylen) != 0) {
- hw_desc_init(&desc[idx]);
- set_din_const(&desc[idx], 0,
- (blocksize - keylen));
- set_flow_mode(&desc[idx], BYPASS);
- set_dout_dlli(&desc[idx],
- (padded_authkey_dma_addr +
- keylen),
- (blocksize - keylen), NS_BIT, 0);
- idx++;
- }
- }
- } else {
- hw_desc_init(&desc[idx]);
- set_din_const(&desc[idx], 0, (blocksize - keylen));
- set_flow_mode(&desc[idx], BYPASS);
- set_dout_dlli(&desc[idx], padded_authkey_dma_addr,
- blocksize, NS_BIT, 0);
- idx++;
- }
-
- rc = cc_send_sync_request(ctx->drvdata, &cc_req, desc, idx);
- if (rc)
- dev_err(dev, "send_request() failed (rc=%d)\n", rc);
-
- if (key_dma_addr)
- dma_unmap_single(dev, key_dma_addr, keylen, DMA_TO_DEVICE);
-
- return rc;
-}
-
-static int
-cc_aead_setkey(struct crypto_aead *tfm, const u8 *key, unsigned int keylen)
-{
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
- struct rtattr *rta = (struct rtattr *)key;
- struct cc_crypto_req cc_req = {};
- struct crypto_authenc_key_param *param;
- struct cc_hw_desc desc[MAX_AEAD_SETKEY_SEQ];
- int seq_len = 0, rc = -EINVAL;
- struct device *dev = drvdata_to_dev(ctx->drvdata);
-
- dev_dbg(dev, "Setting key in context @%p for %s. key=%p keylen=%u\n",
- ctx, crypto_tfm_alg_name(crypto_aead_tfm(tfm)), key, keylen);
-
- /* STAT_PHASE_0: Init and sanity checks */
-
- if (ctx->auth_mode != DRV_HASH_NULL) { /* authenc() alg. */
- if (!RTA_OK(rta, keylen))
- goto badkey;
- if (rta->rta_type != CRYPTO_AUTHENC_KEYA_PARAM)
- goto badkey;
- if (RTA_PAYLOAD(rta) < sizeof(*param))
- goto badkey;
- param = RTA_DATA(rta);
- ctx->enc_keylen = be32_to_cpu(param->enckeylen);
- key += RTA_ALIGN(rta->rta_len);
- keylen -= RTA_ALIGN(rta->rta_len);
- if (keylen < ctx->enc_keylen)
- goto badkey;
- ctx->auth_keylen = keylen - ctx->enc_keylen;
-
- if (ctx->cipher_mode == DRV_CIPHER_CTR) {
- /* the nonce is stored in bytes at end of key */
- if (ctx->enc_keylen <
- (AES_MIN_KEY_SIZE + CTR_RFC3686_NONCE_SIZE))
- goto badkey;
- /* Copy nonce from last 4 bytes in CTR key to
- * first 4 bytes in CTR IV
- */
- memcpy(ctx->ctr_nonce, key + ctx->auth_keylen +
- ctx->enc_keylen - CTR_RFC3686_NONCE_SIZE,
- CTR_RFC3686_NONCE_SIZE);
- /* Set CTR key size */
- ctx->enc_keylen -= CTR_RFC3686_NONCE_SIZE;
- }
- } else { /* non-authenc - has just one key */
- ctx->enc_keylen = keylen;
- ctx->auth_keylen = 0;
- }
-
- rc = validate_keys_sizes(ctx);
- if (rc)
- goto badkey;
-
- /* STAT_PHASE_1: Copy key to ctx */
-
- /* Get key material */
- memcpy(ctx->enckey, key + ctx->auth_keylen, ctx->enc_keylen);
- if (ctx->enc_keylen == 24)
- memset(ctx->enckey + 24, 0, CC_AES_KEY_SIZE_MAX - 24);
- if (ctx->auth_mode == DRV_HASH_XCBC_MAC) {
- memcpy(ctx->auth_state.xcbc.xcbc_keys, key, ctx->auth_keylen);
- } else if (ctx->auth_mode != DRV_HASH_NULL) { /* HMAC */
- rc = cc_get_plain_hmac_key(tfm, key, ctx->auth_keylen);
- if (rc)
- goto badkey;
- }
-
- /* STAT_PHASE_2: Create sequence */
-
- switch (ctx->auth_mode) {
- case DRV_HASH_SHA1:
- case DRV_HASH_SHA256:
- seq_len = hmac_setkey(desc, ctx);
- break;
- case DRV_HASH_XCBC_MAC:
- seq_len = xcbc_setkey(desc, ctx);
- break;
- case DRV_HASH_NULL: /* non-authenc modes, e.g., CCM */
- break; /* No auth. key setup */
- default:
- dev_err(dev, "Unsupported authenc (%d)\n", ctx->auth_mode);
- rc = -ENOTSUPP;
- goto badkey;
- }
-
- /* STAT_PHASE_3: Submit sequence to HW */
-
- if (seq_len > 0) { /* For CCM there is no sequence to setup the key */
- rc = cc_send_sync_request(ctx->drvdata, &cc_req, desc, seq_len);
- if (rc) {
- dev_err(dev, "send_request() failed (rc=%d)\n", rc);
- goto setkey_error;
- }
- }
-
- /* Update STAT_PHASE_3 */
- return rc;
-
-badkey:
- crypto_aead_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
-
-setkey_error:
- return rc;
-}
-
-static int cc_rfc4309_ccm_setkey(struct crypto_aead *tfm, const u8 *key,
- unsigned int keylen)
-{
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
-
- if (keylen < 3)
- return -EINVAL;
-
- keylen -= 3;
- memcpy(ctx->ctr_nonce, key + keylen, 3);
-
- return cc_aead_setkey(tfm, key, keylen);
-}
-
-static int cc_aead_setauthsize(struct crypto_aead *authenc,
- unsigned int authsize)
-{
- struct cc_aead_ctx *ctx = crypto_aead_ctx(authenc);
- struct device *dev = drvdata_to_dev(ctx->drvdata);
-
- /* Unsupported auth. sizes */
- if (authsize == 0 ||
- authsize > crypto_aead_maxauthsize(authenc)) {
- return -ENOTSUPP;
- }
-
- ctx->authsize = authsize;
- dev_dbg(dev, "authlen=%d\n", ctx->authsize);
-
- return 0;
-}
-
-static int cc_rfc4309_ccm_setauthsize(struct crypto_aead *authenc,
- unsigned int authsize)
-{
- switch (authsize) {
- case 8:
- case 12:
- case 16:
- break;
- default:
- return -EINVAL;
- }
-
- return cc_aead_setauthsize(authenc, authsize);
-}
-
-static int cc_ccm_setauthsize(struct crypto_aead *authenc,
- unsigned int authsize)
-{
- switch (authsize) {
- case 4:
- case 6:
- case 8:
- case 10:
- case 12:
- case 14:
- case 16:
- break;
- default:
- return -EINVAL;
- }
-
- return cc_aead_setauthsize(authenc, authsize);
-}
-
-static void cc_set_assoc_desc(struct aead_request *areq, unsigned int flow_mode,
- struct cc_hw_desc desc[], unsigned int *seq_size)
-{
- struct crypto_aead *tfm = crypto_aead_reqtfm(areq);
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
- struct aead_req_ctx *areq_ctx = aead_request_ctx(areq);
- enum cc_req_dma_buf_type assoc_dma_type = areq_ctx->assoc_buff_type;
- unsigned int idx = *seq_size;
- struct device *dev = drvdata_to_dev(ctx->drvdata);
-
- switch (assoc_dma_type) {
- case CC_DMA_BUF_DLLI:
- dev_dbg(dev, "ASSOC buffer type DLLI\n");
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_DLLI, sg_dma_address(areq->src),
- areq->assoclen, NS_BIT);
- set_flow_mode(&desc[idx], flow_mode);
- if (ctx->auth_mode == DRV_HASH_XCBC_MAC &&
- areq_ctx->cryptlen > 0)
- set_din_not_last_indication(&desc[idx]);
- break;
- case CC_DMA_BUF_MLLI:
- dev_dbg(dev, "ASSOC buffer type MLLI\n");
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_MLLI, areq_ctx->assoc.sram_addr,
- areq_ctx->assoc.mlli_nents, NS_BIT);
- set_flow_mode(&desc[idx], flow_mode);
- if (ctx->auth_mode == DRV_HASH_XCBC_MAC &&
- areq_ctx->cryptlen > 0)
- set_din_not_last_indication(&desc[idx]);
- break;
- case CC_DMA_BUF_NULL:
- default:
- dev_err(dev, "Invalid ASSOC buffer type\n");
- }
-
- *seq_size = (++idx);
-}
-
-static void cc_proc_authen_desc(struct aead_request *areq,
- unsigned int flow_mode,
- struct cc_hw_desc desc[],
- unsigned int *seq_size, int direct)
-{
- struct aead_req_ctx *areq_ctx = aead_request_ctx(areq);
- enum cc_req_dma_buf_type data_dma_type = areq_ctx->data_buff_type;
- unsigned int idx = *seq_size;
- struct crypto_aead *tfm = crypto_aead_reqtfm(areq);
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
- struct device *dev = drvdata_to_dev(ctx->drvdata);
-
- switch (data_dma_type) {
- case CC_DMA_BUF_DLLI:
- {
- struct scatterlist *cipher =
- (direct == DRV_CRYPTO_DIRECTION_ENCRYPT) ?
- areq_ctx->dst_sgl : areq_ctx->src_sgl;
-
- unsigned int offset =
- (direct == DRV_CRYPTO_DIRECTION_ENCRYPT) ?
- areq_ctx->dst_offset : areq_ctx->src_offset;
- dev_dbg(dev, "AUTHENC: SRC/DST buffer type DLLI\n");
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_DLLI,
- (sg_dma_address(cipher) + offset),
- areq_ctx->cryptlen, NS_BIT);
- set_flow_mode(&desc[idx], flow_mode);
- break;
- }
- case CC_DMA_BUF_MLLI:
- {
- /* DOUBLE-PASS flow (as default)
- * assoc. + iv + data -compact in one table
- * if assoclen is ZERO only IV perform
- */
- cc_sram_addr_t mlli_addr = areq_ctx->assoc.sram_addr;
- u32 mlli_nents = areq_ctx->assoc.mlli_nents;
-
- if (areq_ctx->is_single_pass) {
- if (direct == DRV_CRYPTO_DIRECTION_ENCRYPT) {
- mlli_addr = areq_ctx->dst.sram_addr;
- mlli_nents = areq_ctx->dst.mlli_nents;
- } else {
- mlli_addr = areq_ctx->src.sram_addr;
- mlli_nents = areq_ctx->src.mlli_nents;
- }
- }
-
- dev_dbg(dev, "AUTHENC: SRC/DST buffer type MLLI\n");
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_MLLI, mlli_addr, mlli_nents,
- NS_BIT);
- set_flow_mode(&desc[idx], flow_mode);
- break;
- }
- case CC_DMA_BUF_NULL:
- default:
- dev_err(dev, "AUTHENC: Invalid SRC/DST buffer type\n");
- }
-
- *seq_size = (++idx);
-}
-
-static void cc_proc_cipher_desc(struct aead_request *areq,
- unsigned int flow_mode,
- struct cc_hw_desc desc[],
- unsigned int *seq_size)
-{
- unsigned int idx = *seq_size;
- struct aead_req_ctx *areq_ctx = aead_request_ctx(areq);
- enum cc_req_dma_buf_type data_dma_type = areq_ctx->data_buff_type;
- struct crypto_aead *tfm = crypto_aead_reqtfm(areq);
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
- struct device *dev = drvdata_to_dev(ctx->drvdata);
-
- if (areq_ctx->cryptlen == 0)
- return; /*null processing*/
-
- switch (data_dma_type) {
- case CC_DMA_BUF_DLLI:
- dev_dbg(dev, "CIPHER: SRC/DST buffer type DLLI\n");
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_DLLI,
- (sg_dma_address(areq_ctx->src_sgl) +
- areq_ctx->src_offset), areq_ctx->cryptlen,
- NS_BIT);
- set_dout_dlli(&desc[idx],
- (sg_dma_address(areq_ctx->dst_sgl) +
- areq_ctx->dst_offset),
- areq_ctx->cryptlen, NS_BIT, 0);
- set_flow_mode(&desc[idx], flow_mode);
- break;
- case CC_DMA_BUF_MLLI:
- dev_dbg(dev, "CIPHER: SRC/DST buffer type MLLI\n");
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_MLLI, areq_ctx->src.sram_addr,
- areq_ctx->src.mlli_nents, NS_BIT);
- set_dout_mlli(&desc[idx], areq_ctx->dst.sram_addr,
- areq_ctx->dst.mlli_nents, NS_BIT, 0);
- set_flow_mode(&desc[idx], flow_mode);
- break;
- case CC_DMA_BUF_NULL:
- default:
- dev_err(dev, "CIPHER: Invalid SRC/DST buffer type\n");
- }
-
- *seq_size = (++idx);
-}
-
-static void cc_proc_digest_desc(struct aead_request *req,
- struct cc_hw_desc desc[],
- unsigned int *seq_size)
-{
- struct crypto_aead *tfm = crypto_aead_reqtfm(req);
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
- struct aead_req_ctx *req_ctx = aead_request_ctx(req);
- unsigned int idx = *seq_size;
- unsigned int hash_mode = (ctx->auth_mode == DRV_HASH_SHA1) ?
- DRV_HASH_HW_SHA1 : DRV_HASH_HW_SHA256;
- int direct = req_ctx->gen_ctx.op_type;
-
- /* Get final ICV result */
- if (direct == DRV_CRYPTO_DIRECTION_ENCRYPT) {
- hw_desc_init(&desc[idx]);
- set_flow_mode(&desc[idx], S_HASH_to_DOUT);
- set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
- set_dout_dlli(&desc[idx], req_ctx->icv_dma_addr, ctx->authsize,
- NS_BIT, 1);
- set_queue_last_ind(&desc[idx]);
- if (ctx->auth_mode == DRV_HASH_XCBC_MAC) {
- set_aes_not_hash_mode(&desc[idx]);
- set_cipher_mode(&desc[idx], DRV_CIPHER_XCBC_MAC);
- } else {
- set_cipher_config0(&desc[idx],
- HASH_DIGEST_RESULT_LITTLE_ENDIAN);
- set_cipher_mode(&desc[idx], hash_mode);
- }
- } else { /*Decrypt*/
- /* Get ICV out from hardware */
- hw_desc_init(&desc[idx]);
- set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
- set_flow_mode(&desc[idx], S_HASH_to_DOUT);
- set_dout_dlli(&desc[idx], req_ctx->mac_buf_dma_addr,
- ctx->authsize, NS_BIT, 1);
- set_queue_last_ind(&desc[idx]);
- set_cipher_config0(&desc[idx],
- HASH_DIGEST_RESULT_LITTLE_ENDIAN);
- set_cipher_config1(&desc[idx], HASH_PADDING_DISABLED);
- if (ctx->auth_mode == DRV_HASH_XCBC_MAC) {
- set_cipher_mode(&desc[idx], DRV_CIPHER_XCBC_MAC);
- set_aes_not_hash_mode(&desc[idx]);
- } else {
- set_cipher_mode(&desc[idx], hash_mode);
- }
- }
-
- *seq_size = (++idx);
-}
-
-static void cc_set_cipher_desc(struct aead_request *req,
- struct cc_hw_desc desc[],
- unsigned int *seq_size)
-{
- struct crypto_aead *tfm = crypto_aead_reqtfm(req);
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
- struct aead_req_ctx *req_ctx = aead_request_ctx(req);
- unsigned int hw_iv_size = req_ctx->hw_iv_size;
- unsigned int idx = *seq_size;
- int direct = req_ctx->gen_ctx.op_type;
-
- /* Setup cipher state */
- hw_desc_init(&desc[idx]);
- set_cipher_config0(&desc[idx], direct);
- set_flow_mode(&desc[idx], ctx->flow_mode);
- set_din_type(&desc[idx], DMA_DLLI, req_ctx->gen_ctx.iv_dma_addr,
- hw_iv_size, NS_BIT);
- if (ctx->cipher_mode == DRV_CIPHER_CTR)
- set_setup_mode(&desc[idx], SETUP_LOAD_STATE1);
- else
- set_setup_mode(&desc[idx], SETUP_LOAD_STATE0);
- set_cipher_mode(&desc[idx], ctx->cipher_mode);
- idx++;
-
- /* Setup enc. key */
- hw_desc_init(&desc[idx]);
- set_cipher_config0(&desc[idx], direct);
- set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
- set_flow_mode(&desc[idx], ctx->flow_mode);
- if (ctx->flow_mode == S_DIN_to_AES) {
- set_din_type(&desc[idx], DMA_DLLI, ctx->enckey_dma_addr,
- ((ctx->enc_keylen == 24) ? CC_AES_KEY_SIZE_MAX :
- ctx->enc_keylen), NS_BIT);
- set_key_size_aes(&desc[idx], ctx->enc_keylen);
- } else {
- set_din_type(&desc[idx], DMA_DLLI, ctx->enckey_dma_addr,
- ctx->enc_keylen, NS_BIT);
- set_key_size_des(&desc[idx], ctx->enc_keylen);
- }
- set_cipher_mode(&desc[idx], ctx->cipher_mode);
- idx++;
-
- *seq_size = idx;
-}
-
-static void cc_proc_cipher(struct aead_request *req, struct cc_hw_desc desc[],
- unsigned int *seq_size, unsigned int data_flow_mode)
-{
- struct aead_req_ctx *req_ctx = aead_request_ctx(req);
- int direct = req_ctx->gen_ctx.op_type;
- unsigned int idx = *seq_size;
-
- if (req_ctx->cryptlen == 0)
- return; /*null processing*/
-
- cc_set_cipher_desc(req, desc, &idx);
- cc_proc_cipher_desc(req, data_flow_mode, desc, &idx);
- if (direct == DRV_CRYPTO_DIRECTION_ENCRYPT) {
- /* We must wait for DMA to write all cipher */
- hw_desc_init(&desc[idx]);
- set_din_no_dma(&desc[idx], 0, 0xfffff0);
- set_dout_no_dma(&desc[idx], 0, 0, 1);
- idx++;
- }
-
- *seq_size = idx;
-}
-
-static void cc_set_hmac_desc(struct aead_request *req, struct cc_hw_desc desc[],
- unsigned int *seq_size)
-{
- struct crypto_aead *tfm = crypto_aead_reqtfm(req);
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
- unsigned int hash_mode = (ctx->auth_mode == DRV_HASH_SHA1) ?
- DRV_HASH_HW_SHA1 : DRV_HASH_HW_SHA256;
- unsigned int digest_size = (ctx->auth_mode == DRV_HASH_SHA1) ?
- CC_SHA1_DIGEST_SIZE : CC_SHA256_DIGEST_SIZE;
- unsigned int idx = *seq_size;
-
- /* Loading hash ipad xor key state */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], hash_mode);
- set_din_type(&desc[idx], DMA_DLLI,
- ctx->auth_state.hmac.ipad_opad_dma_addr, digest_size,
- NS_BIT);
- set_flow_mode(&desc[idx], S_DIN_to_HASH);
- set_setup_mode(&desc[idx], SETUP_LOAD_STATE0);
- idx++;
-
- /* Load init. digest len (64 bytes) */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], hash_mode);
- set_din_sram(&desc[idx], cc_digest_len_addr(ctx->drvdata, hash_mode),
- HASH_LEN_SIZE);
- set_flow_mode(&desc[idx], S_DIN_to_HASH);
- set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
- idx++;
-
- *seq_size = idx;
-}
-
-static void cc_set_xcbc_desc(struct aead_request *req, struct cc_hw_desc desc[],
- unsigned int *seq_size)
-{
- struct crypto_aead *tfm = crypto_aead_reqtfm(req);
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
- unsigned int idx = *seq_size;
-
- /* Loading MAC state */
- hw_desc_init(&desc[idx]);
- set_din_const(&desc[idx], 0, CC_AES_BLOCK_SIZE);
- set_setup_mode(&desc[idx], SETUP_LOAD_STATE0);
- set_cipher_mode(&desc[idx], DRV_CIPHER_XCBC_MAC);
- set_cipher_config0(&desc[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
- set_key_size_aes(&desc[idx], CC_AES_128_BIT_KEY_SIZE);
- set_flow_mode(&desc[idx], S_DIN_to_HASH);
- set_aes_not_hash_mode(&desc[idx]);
- idx++;
-
- /* Setup XCBC MAC K1 */
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_DLLI,
- ctx->auth_state.xcbc.xcbc_keys_dma_addr,
- AES_KEYSIZE_128, NS_BIT);
- set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
- set_cipher_mode(&desc[idx], DRV_CIPHER_XCBC_MAC);
- set_cipher_config0(&desc[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
- set_key_size_aes(&desc[idx], CC_AES_128_BIT_KEY_SIZE);
- set_flow_mode(&desc[idx], S_DIN_to_HASH);
- set_aes_not_hash_mode(&desc[idx]);
- idx++;
-
- /* Setup XCBC MAC K2 */
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_DLLI,
- (ctx->auth_state.xcbc.xcbc_keys_dma_addr +
- AES_KEYSIZE_128), AES_KEYSIZE_128, NS_BIT);
- set_setup_mode(&desc[idx], SETUP_LOAD_STATE1);
- set_cipher_mode(&desc[idx], DRV_CIPHER_XCBC_MAC);
- set_cipher_config0(&desc[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
- set_key_size_aes(&desc[idx], CC_AES_128_BIT_KEY_SIZE);
- set_flow_mode(&desc[idx], S_DIN_to_HASH);
- set_aes_not_hash_mode(&desc[idx]);
- idx++;
-
- /* Setup XCBC MAC K3 */
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_DLLI,
- (ctx->auth_state.xcbc.xcbc_keys_dma_addr +
- 2 * AES_KEYSIZE_128), AES_KEYSIZE_128, NS_BIT);
- set_setup_mode(&desc[idx], SETUP_LOAD_STATE2);
- set_cipher_mode(&desc[idx], DRV_CIPHER_XCBC_MAC);
- set_cipher_config0(&desc[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
- set_key_size_aes(&desc[idx], CC_AES_128_BIT_KEY_SIZE);
- set_flow_mode(&desc[idx], S_DIN_to_HASH);
- set_aes_not_hash_mode(&desc[idx]);
- idx++;
-
- *seq_size = idx;
-}
-
-static void cc_proc_header_desc(struct aead_request *req,
- struct cc_hw_desc desc[],
- unsigned int *seq_size)
-{
- unsigned int idx = *seq_size;
- /* Hash associated data */
- if (req->assoclen > 0)
- cc_set_assoc_desc(req, DIN_HASH, desc, &idx);
-
- /* Hash IV */
- *seq_size = idx;
-}
-
-static void cc_proc_scheme_desc(struct aead_request *req,
- struct cc_hw_desc desc[],
- unsigned int *seq_size)
-{
- struct crypto_aead *tfm = crypto_aead_reqtfm(req);
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
- struct cc_aead_handle *aead_handle = ctx->drvdata->aead_handle;
- unsigned int hash_mode = (ctx->auth_mode == DRV_HASH_SHA1) ?
- DRV_HASH_HW_SHA1 : DRV_HASH_HW_SHA256;
- unsigned int digest_size = (ctx->auth_mode == DRV_HASH_SHA1) ?
- CC_SHA1_DIGEST_SIZE : CC_SHA256_DIGEST_SIZE;
- unsigned int idx = *seq_size;
-
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], hash_mode);
- set_dout_sram(&desc[idx], aead_handle->sram_workspace_addr,
- HASH_LEN_SIZE);
- set_flow_mode(&desc[idx], S_HASH_to_DOUT);
- set_setup_mode(&desc[idx], SETUP_WRITE_STATE1);
- set_cipher_do(&desc[idx], DO_PAD);
- idx++;
-
- /* Get final ICV result */
- hw_desc_init(&desc[idx]);
- set_dout_sram(&desc[idx], aead_handle->sram_workspace_addr,
- digest_size);
- set_flow_mode(&desc[idx], S_HASH_to_DOUT);
- set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
- set_cipher_config0(&desc[idx], HASH_DIGEST_RESULT_LITTLE_ENDIAN);
- set_cipher_mode(&desc[idx], hash_mode);
- idx++;
-
- /* Loading hash opad xor key state */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], hash_mode);
- set_din_type(&desc[idx], DMA_DLLI,
- (ctx->auth_state.hmac.ipad_opad_dma_addr + digest_size),
- digest_size, NS_BIT);
- set_flow_mode(&desc[idx], S_DIN_to_HASH);
- set_setup_mode(&desc[idx], SETUP_LOAD_STATE0);
- idx++;
-
- /* Load init. digest len (64 bytes) */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], hash_mode);
- set_din_sram(&desc[idx], cc_digest_len_addr(ctx->drvdata, hash_mode),
- HASH_LEN_SIZE);
- set_cipher_config1(&desc[idx], HASH_PADDING_ENABLED);
- set_flow_mode(&desc[idx], S_DIN_to_HASH);
- set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
- idx++;
-
- /* Perform HASH update */
- hw_desc_init(&desc[idx]);
- set_din_sram(&desc[idx], aead_handle->sram_workspace_addr,
- digest_size);
- set_flow_mode(&desc[idx], DIN_HASH);
- idx++;
-
- *seq_size = idx;
-}
-
-static void cc_mlli_to_sram(struct aead_request *req,
- struct cc_hw_desc desc[], unsigned int *seq_size)
-{
- struct aead_req_ctx *req_ctx = aead_request_ctx(req);
- struct crypto_aead *tfm = crypto_aead_reqtfm(req);
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
- struct device *dev = drvdata_to_dev(ctx->drvdata);
-
- if (req_ctx->assoc_buff_type == CC_DMA_BUF_MLLI ||
- req_ctx->data_buff_type == CC_DMA_BUF_MLLI ||
- !req_ctx->is_single_pass) {
- dev_dbg(dev, "Copy-to-sram: mlli_dma=%08x, mlli_size=%u\n",
- (unsigned int)ctx->drvdata->mlli_sram_addr,
- req_ctx->mlli_params.mlli_len);
- /* Copy MLLI table host-to-sram */
- hw_desc_init(&desc[*seq_size]);
- set_din_type(&desc[*seq_size], DMA_DLLI,
- req_ctx->mlli_params.mlli_dma_addr,
- req_ctx->mlli_params.mlli_len, NS_BIT);
- set_dout_sram(&desc[*seq_size],
- ctx->drvdata->mlli_sram_addr,
- req_ctx->mlli_params.mlli_len);
- set_flow_mode(&desc[*seq_size], BYPASS);
- (*seq_size)++;
- }
-}
-
-static enum cc_flow_mode cc_get_data_flow(enum drv_crypto_direction direct,
- enum cc_flow_mode setup_flow_mode,
- bool is_single_pass)
-{
- enum cc_flow_mode data_flow_mode;
-
- if (direct == DRV_CRYPTO_DIRECTION_ENCRYPT) {
- if (setup_flow_mode == S_DIN_to_AES)
- data_flow_mode = is_single_pass ?
- AES_to_HASH_and_DOUT : DIN_AES_DOUT;
- else
- data_flow_mode = is_single_pass ?
- DES_to_HASH_and_DOUT : DIN_DES_DOUT;
- } else { /* Decrypt */
- if (setup_flow_mode == S_DIN_to_AES)
- data_flow_mode = is_single_pass ?
- AES_and_HASH : DIN_AES_DOUT;
- else
- data_flow_mode = is_single_pass ?
- DES_and_HASH : DIN_DES_DOUT;
- }
-
- return data_flow_mode;
-}
-
-static void cc_hmac_authenc(struct aead_request *req, struct cc_hw_desc desc[],
- unsigned int *seq_size)
-{
- struct crypto_aead *tfm = crypto_aead_reqtfm(req);
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
- struct aead_req_ctx *req_ctx = aead_request_ctx(req);
- int direct = req_ctx->gen_ctx.op_type;
- unsigned int data_flow_mode =
- cc_get_data_flow(direct, ctx->flow_mode,
- req_ctx->is_single_pass);
-
- if (req_ctx->is_single_pass) {
- /**
- * Single-pass flow
- */
- cc_set_hmac_desc(req, desc, seq_size);
- cc_set_cipher_desc(req, desc, seq_size);
- cc_proc_header_desc(req, desc, seq_size);
- cc_proc_cipher_desc(req, data_flow_mode, desc, seq_size);
- cc_proc_scheme_desc(req, desc, seq_size);
- cc_proc_digest_desc(req, desc, seq_size);
- return;
- }
-
- /**
- * Double-pass flow
- * Fallback for unsupported single-pass modes,
- * i.e. using assoc. data of non-word-multiple
- */
- if (direct == DRV_CRYPTO_DIRECTION_ENCRYPT) {
- /* encrypt first.. */
- cc_proc_cipher(req, desc, seq_size, data_flow_mode);
- /* authenc after..*/
- cc_set_hmac_desc(req, desc, seq_size);
- cc_proc_authen_desc(req, DIN_HASH, desc, seq_size, direct);
- cc_proc_scheme_desc(req, desc, seq_size);
- cc_proc_digest_desc(req, desc, seq_size);
-
- } else { /*DECRYPT*/
- /* authenc first..*/
- cc_set_hmac_desc(req, desc, seq_size);
- cc_proc_authen_desc(req, DIN_HASH, desc, seq_size, direct);
- cc_proc_scheme_desc(req, desc, seq_size);
- /* decrypt after.. */
- cc_proc_cipher(req, desc, seq_size, data_flow_mode);
- /* read the digest result with setting the completion bit
- * must be after the cipher operation
- */
- cc_proc_digest_desc(req, desc, seq_size);
- }
-}
-
-static void
-cc_xcbc_authenc(struct aead_request *req, struct cc_hw_desc desc[],
- unsigned int *seq_size)
-{
- struct crypto_aead *tfm = crypto_aead_reqtfm(req);
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
- struct aead_req_ctx *req_ctx = aead_request_ctx(req);
- int direct = req_ctx->gen_ctx.op_type;
- unsigned int data_flow_mode =
- cc_get_data_flow(direct, ctx->flow_mode,
- req_ctx->is_single_pass);
-
- if (req_ctx->is_single_pass) {
- /**
- * Single-pass flow
- */
- cc_set_xcbc_desc(req, desc, seq_size);
- cc_set_cipher_desc(req, desc, seq_size);
- cc_proc_header_desc(req, desc, seq_size);
- cc_proc_cipher_desc(req, data_flow_mode, desc, seq_size);
- cc_proc_digest_desc(req, desc, seq_size);
- return;
- }
-
- /**
- * Double-pass flow
- * Fallback for unsupported single-pass modes,
- * i.e. using assoc. data of non-word-multiple
- */
- if (direct == DRV_CRYPTO_DIRECTION_ENCRYPT) {
- /* encrypt first.. */
- cc_proc_cipher(req, desc, seq_size, data_flow_mode);
- /* authenc after.. */
- cc_set_xcbc_desc(req, desc, seq_size);
- cc_proc_authen_desc(req, DIN_HASH, desc, seq_size, direct);
- cc_proc_digest_desc(req, desc, seq_size);
- } else { /*DECRYPT*/
- /* authenc first.. */
- cc_set_xcbc_desc(req, desc, seq_size);
- cc_proc_authen_desc(req, DIN_HASH, desc, seq_size, direct);
- /* decrypt after..*/
- cc_proc_cipher(req, desc, seq_size, data_flow_mode);
- /* read the digest result with setting the completion bit
- * must be after the cipher operation
- */
- cc_proc_digest_desc(req, desc, seq_size);
- }
-}
-
-static int validate_data_size(struct cc_aead_ctx *ctx,
- enum drv_crypto_direction direct,
- struct aead_request *req)
-{
- struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
- struct device *dev = drvdata_to_dev(ctx->drvdata);
- unsigned int assoclen = req->assoclen;
- unsigned int cipherlen = (direct == DRV_CRYPTO_DIRECTION_DECRYPT) ?
- (req->cryptlen - ctx->authsize) : req->cryptlen;
-
- if (direct == DRV_CRYPTO_DIRECTION_DECRYPT &&
- req->cryptlen < ctx->authsize)
- goto data_size_err;
-
- areq_ctx->is_single_pass = true; /*defaulted to fast flow*/
-
- switch (ctx->flow_mode) {
- case S_DIN_to_AES:
- if (ctx->cipher_mode == DRV_CIPHER_CBC &&
- !IS_ALIGNED(cipherlen, AES_BLOCK_SIZE))
- goto data_size_err;
- if (ctx->cipher_mode == DRV_CIPHER_CCM)
- break;
- if (ctx->cipher_mode == DRV_CIPHER_GCTR) {
- if (areq_ctx->plaintext_authenticate_only)
- areq_ctx->is_single_pass = false;
- break;
- }
-
- if (!IS_ALIGNED(assoclen, sizeof(u32)))
- areq_ctx->is_single_pass = false;
-
- if (ctx->cipher_mode == DRV_CIPHER_CTR &&
- !IS_ALIGNED(cipherlen, sizeof(u32)))
- areq_ctx->is_single_pass = false;
-
- break;
- case S_DIN_to_DES:
- if (!IS_ALIGNED(cipherlen, DES_BLOCK_SIZE))
- goto data_size_err;
- if (!IS_ALIGNED(assoclen, DES_BLOCK_SIZE))
- areq_ctx->is_single_pass = false;
- break;
- default:
- dev_err(dev, "Unexpected flow mode (%d)\n", ctx->flow_mode);
- goto data_size_err;
- }
-
- return 0;
-
-data_size_err:
- return -EINVAL;
-}
-
-static unsigned int format_ccm_a0(u8 *pa0_buff, u32 header_size)
-{
- unsigned int len = 0;
-
- if (header_size == 0)
- return 0;
-
- if (header_size < ((1UL << 16) - (1UL << 8))) {
- len = 2;
-
- pa0_buff[0] = (header_size >> 8) & 0xFF;
- pa0_buff[1] = header_size & 0xFF;
- } else {
- len = 6;
-
- pa0_buff[0] = 0xFF;
- pa0_buff[1] = 0xFE;
- pa0_buff[2] = (header_size >> 24) & 0xFF;
- pa0_buff[3] = (header_size >> 16) & 0xFF;
- pa0_buff[4] = (header_size >> 8) & 0xFF;
- pa0_buff[5] = header_size & 0xFF;
- }
-
- return len;
-}
-
-static int set_msg_len(u8 *block, unsigned int msglen, unsigned int csize)
-{
- __be32 data;
-
- memset(block, 0, csize);
- block += csize;
-
- if (csize >= 4)
- csize = 4;
- else if (msglen > (1 << (8 * csize)))
- return -EOVERFLOW;
-
- data = cpu_to_be32(msglen);
- memcpy(block - csize, (u8 *)&data + 4 - csize, csize);
-
- return 0;
-}
-
-static int cc_ccm(struct aead_request *req, struct cc_hw_desc desc[],
- unsigned int *seq_size)
-{
- struct crypto_aead *tfm = crypto_aead_reqtfm(req);
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
- struct aead_req_ctx *req_ctx = aead_request_ctx(req);
- unsigned int idx = *seq_size;
- unsigned int cipher_flow_mode;
- dma_addr_t mac_result;
-
- if (req_ctx->gen_ctx.op_type == DRV_CRYPTO_DIRECTION_DECRYPT) {
- cipher_flow_mode = AES_to_HASH_and_DOUT;
- mac_result = req_ctx->mac_buf_dma_addr;
- } else { /* Encrypt */
- cipher_flow_mode = AES_and_HASH;
- mac_result = req_ctx->icv_dma_addr;
- }
-
- /* load key */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], DRV_CIPHER_CTR);
- set_din_type(&desc[idx], DMA_DLLI, ctx->enckey_dma_addr,
- ((ctx->enc_keylen == 24) ? CC_AES_KEY_SIZE_MAX :
- ctx->enc_keylen), NS_BIT);
- set_key_size_aes(&desc[idx], ctx->enc_keylen);
- set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
- set_cipher_config0(&desc[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
- set_flow_mode(&desc[idx], S_DIN_to_AES);
- idx++;
-
- /* load ctr state */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], DRV_CIPHER_CTR);
- set_key_size_aes(&desc[idx], ctx->enc_keylen);
- set_din_type(&desc[idx], DMA_DLLI,
- req_ctx->gen_ctx.iv_dma_addr, AES_BLOCK_SIZE, NS_BIT);
- set_cipher_config0(&desc[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
- set_setup_mode(&desc[idx], SETUP_LOAD_STATE1);
- set_flow_mode(&desc[idx], S_DIN_to_AES);
- idx++;
-
- /* load MAC key */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], DRV_CIPHER_CBC_MAC);
- set_din_type(&desc[idx], DMA_DLLI, ctx->enckey_dma_addr,
- ((ctx->enc_keylen == 24) ? CC_AES_KEY_SIZE_MAX :
- ctx->enc_keylen), NS_BIT);
- set_key_size_aes(&desc[idx], ctx->enc_keylen);
- set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
- set_cipher_config0(&desc[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
- set_flow_mode(&desc[idx], S_DIN_to_HASH);
- set_aes_not_hash_mode(&desc[idx]);
- idx++;
-
- /* load MAC state */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], DRV_CIPHER_CBC_MAC);
- set_key_size_aes(&desc[idx], ctx->enc_keylen);
- set_din_type(&desc[idx], DMA_DLLI, req_ctx->mac_buf_dma_addr,
- AES_BLOCK_SIZE, NS_BIT);
- set_cipher_config0(&desc[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
- set_setup_mode(&desc[idx], SETUP_LOAD_STATE0);
- set_flow_mode(&desc[idx], S_DIN_to_HASH);
- set_aes_not_hash_mode(&desc[idx]);
- idx++;
-
- /* process assoc data */
- if (req->assoclen > 0) {
- cc_set_assoc_desc(req, DIN_HASH, desc, &idx);
- } else {
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_DLLI,
- sg_dma_address(&req_ctx->ccm_adata_sg),
- AES_BLOCK_SIZE + req_ctx->ccm_hdr_size, NS_BIT);
- set_flow_mode(&desc[idx], DIN_HASH);
- idx++;
- }
-
- /* process the cipher */
- if (req_ctx->cryptlen)
- cc_proc_cipher_desc(req, cipher_flow_mode, desc, &idx);
-
- /* Read temporal MAC */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], DRV_CIPHER_CBC_MAC);
- set_dout_dlli(&desc[idx], req_ctx->mac_buf_dma_addr, ctx->authsize,
- NS_BIT, 0);
- set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
- set_cipher_config0(&desc[idx], HASH_DIGEST_RESULT_LITTLE_ENDIAN);
- set_flow_mode(&desc[idx], S_HASH_to_DOUT);
- set_aes_not_hash_mode(&desc[idx]);
- idx++;
-
- /* load AES-CTR state (for last MAC calculation)*/
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], DRV_CIPHER_CTR);
- set_cipher_config0(&desc[idx], DRV_CRYPTO_DIRECTION_ENCRYPT);
- set_din_type(&desc[idx], DMA_DLLI, req_ctx->ccm_iv0_dma_addr,
- AES_BLOCK_SIZE, NS_BIT);
- set_key_size_aes(&desc[idx], ctx->enc_keylen);
- set_setup_mode(&desc[idx], SETUP_LOAD_STATE1);
- set_flow_mode(&desc[idx], S_DIN_to_AES);
- idx++;
-
- hw_desc_init(&desc[idx]);
- set_din_no_dma(&desc[idx], 0, 0xfffff0);
- set_dout_no_dma(&desc[idx], 0, 0, 1);
- idx++;
-
- /* encrypt the "T" value and store MAC in mac_state */
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_DLLI, req_ctx->mac_buf_dma_addr,
- ctx->authsize, NS_BIT);
- set_dout_dlli(&desc[idx], mac_result, ctx->authsize, NS_BIT, 1);
- set_queue_last_ind(&desc[idx]);
- set_flow_mode(&desc[idx], DIN_AES_DOUT);
- idx++;
-
- *seq_size = idx;
- return 0;
-}
-
-static int config_ccm_adata(struct aead_request *req)
-{
- struct crypto_aead *tfm = crypto_aead_reqtfm(req);
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
- struct device *dev = drvdata_to_dev(ctx->drvdata);
- struct aead_req_ctx *req_ctx = aead_request_ctx(req);
- //unsigned int size_of_a = 0, rem_a_size = 0;
- unsigned int lp = req->iv[0];
- /* Note: The code assume that req->iv[0] already contains the value
- * of L' of RFC3610
- */
- unsigned int l = lp + 1; /* This is L' of RFC 3610. */
- unsigned int m = ctx->authsize; /* This is M' of RFC 3610. */
- u8 *b0 = req_ctx->ccm_config + CCM_B0_OFFSET;
- u8 *a0 = req_ctx->ccm_config + CCM_A0_OFFSET;
- u8 *ctr_count_0 = req_ctx->ccm_config + CCM_CTR_COUNT_0_OFFSET;
- unsigned int cryptlen = (req_ctx->gen_ctx.op_type ==
- DRV_CRYPTO_DIRECTION_ENCRYPT) ?
- req->cryptlen :
- (req->cryptlen - ctx->authsize);
- int rc;
-
- memset(req_ctx->mac_buf, 0, AES_BLOCK_SIZE);
- memset(req_ctx->ccm_config, 0, AES_BLOCK_SIZE * 3);
-
- /* taken from crypto/ccm.c */
- /* 2 <= L <= 8, so 1 <= L' <= 7. */
- if (l < 2 || l > 8) {
- dev_err(dev, "illegal iv value %X\n", req->iv[0]);
- return -EINVAL;
- }
- memcpy(b0, req->iv, AES_BLOCK_SIZE);
-
- /* format control info per RFC 3610 and
- * NIST Special Publication 800-38C
- */
- *b0 |= (8 * ((m - 2) / 2));
- if (req->assoclen > 0)
- *b0 |= 64; /* Enable bit 6 if Adata exists. */
-
- rc = set_msg_len(b0 + 16 - l, cryptlen, l); /* Write L'. */
- if (rc) {
- dev_err(dev, "message len overflow detected");
- return rc;
- }
- /* END of "taken from crypto/ccm.c" */
-
- /* l(a) - size of associated data. */
- req_ctx->ccm_hdr_size = format_ccm_a0(a0, req->assoclen);
-
- memset(req->iv + 15 - req->iv[0], 0, req->iv[0] + 1);
- req->iv[15] = 1;
-
- memcpy(ctr_count_0, req->iv, AES_BLOCK_SIZE);
- ctr_count_0[15] = 0;
-
- return 0;
-}
-
-static void cc_proc_rfc4309_ccm(struct aead_request *req)
-{
- struct crypto_aead *tfm = crypto_aead_reqtfm(req);
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
- struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
-
- /* L' */
- memset(areq_ctx->ctr_iv, 0, AES_BLOCK_SIZE);
- /* For RFC 4309, always use 4 bytes for message length
- * (at most 2^32-1 bytes).
- */
- areq_ctx->ctr_iv[0] = 3;
-
- /* In RFC 4309 there is an 11-bytes nonce+IV part,
- * that we build here.
- */
- memcpy(areq_ctx->ctr_iv + CCM_BLOCK_NONCE_OFFSET, ctx->ctr_nonce,
- CCM_BLOCK_NONCE_SIZE);
- memcpy(areq_ctx->ctr_iv + CCM_BLOCK_IV_OFFSET, req->iv,
- CCM_BLOCK_IV_SIZE);
- req->iv = areq_ctx->ctr_iv;
- req->assoclen -= CCM_BLOCK_IV_SIZE;
-}
-
-static void cc_set_ghash_desc(struct aead_request *req,
- struct cc_hw_desc desc[], unsigned int *seq_size)
-{
- struct crypto_aead *tfm = crypto_aead_reqtfm(req);
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
- struct aead_req_ctx *req_ctx = aead_request_ctx(req);
- unsigned int idx = *seq_size;
-
- /* load key to AES*/
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], DRV_CIPHER_ECB);
- set_cipher_config0(&desc[idx], DRV_CRYPTO_DIRECTION_ENCRYPT);
- set_din_type(&desc[idx], DMA_DLLI, ctx->enckey_dma_addr,
- ctx->enc_keylen, NS_BIT);
- set_key_size_aes(&desc[idx], ctx->enc_keylen);
- set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
- set_flow_mode(&desc[idx], S_DIN_to_AES);
- idx++;
-
- /* process one zero block to generate hkey */
- hw_desc_init(&desc[idx]);
- set_din_const(&desc[idx], 0x0, AES_BLOCK_SIZE);
- set_dout_dlli(&desc[idx], req_ctx->hkey_dma_addr, AES_BLOCK_SIZE,
- NS_BIT, 0);
- set_flow_mode(&desc[idx], DIN_AES_DOUT);
- idx++;
-
- /* Memory Barrier */
- hw_desc_init(&desc[idx]);
- set_din_no_dma(&desc[idx], 0, 0xfffff0);
- set_dout_no_dma(&desc[idx], 0, 0, 1);
- idx++;
-
- /* Load GHASH subkey */
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_DLLI, req_ctx->hkey_dma_addr,
- AES_BLOCK_SIZE, NS_BIT);
- set_dout_no_dma(&desc[idx], 0, 0, 1);
- set_flow_mode(&desc[idx], S_DIN_to_HASH);
- set_aes_not_hash_mode(&desc[idx]);
- set_cipher_mode(&desc[idx], DRV_HASH_HW_GHASH);
- set_cipher_config1(&desc[idx], HASH_PADDING_ENABLED);
- set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
- idx++;
-
- /* Configure Hash Engine to work with GHASH.
- * Since it was not possible to extend HASH submodes to add GHASH,
- * The following command is necessary in order to
- * select GHASH (according to HW designers)
- */
- hw_desc_init(&desc[idx]);
- set_din_no_dma(&desc[idx], 0, 0xfffff0);
- set_dout_no_dma(&desc[idx], 0, 0, 1);
- set_flow_mode(&desc[idx], S_DIN_to_HASH);
- set_aes_not_hash_mode(&desc[idx]);
- set_cipher_mode(&desc[idx], DRV_HASH_HW_GHASH);
- set_cipher_do(&desc[idx], 1); //1=AES_SK RKEK
- set_cipher_config0(&desc[idx], DRV_CRYPTO_DIRECTION_ENCRYPT);
- set_cipher_config1(&desc[idx], HASH_PADDING_ENABLED);
- set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
- idx++;
-
- /* Load GHASH initial STATE (which is 0). (for any hash there is an
- * initial state)
- */
- hw_desc_init(&desc[idx]);
- set_din_const(&desc[idx], 0x0, AES_BLOCK_SIZE);
- set_dout_no_dma(&desc[idx], 0, 0, 1);
- set_flow_mode(&desc[idx], S_DIN_to_HASH);
- set_aes_not_hash_mode(&desc[idx]);
- set_cipher_mode(&desc[idx], DRV_HASH_HW_GHASH);
- set_cipher_config1(&desc[idx], HASH_PADDING_ENABLED);
- set_setup_mode(&desc[idx], SETUP_LOAD_STATE0);
- idx++;
-
- *seq_size = idx;
-}
-
-static void cc_set_gctr_desc(struct aead_request *req, struct cc_hw_desc desc[],
- unsigned int *seq_size)
-{
- struct crypto_aead *tfm = crypto_aead_reqtfm(req);
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
- struct aead_req_ctx *req_ctx = aead_request_ctx(req);
- unsigned int idx = *seq_size;
-
- /* load key to AES*/
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], DRV_CIPHER_GCTR);
- set_cipher_config0(&desc[idx], DRV_CRYPTO_DIRECTION_ENCRYPT);
- set_din_type(&desc[idx], DMA_DLLI, ctx->enckey_dma_addr,
- ctx->enc_keylen, NS_BIT);
- set_key_size_aes(&desc[idx], ctx->enc_keylen);
- set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
- set_flow_mode(&desc[idx], S_DIN_to_AES);
- idx++;
-
- if (req_ctx->cryptlen && !req_ctx->plaintext_authenticate_only) {
- /* load AES/CTR initial CTR value inc by 2*/
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], DRV_CIPHER_GCTR);
- set_key_size_aes(&desc[idx], ctx->enc_keylen);
- set_din_type(&desc[idx], DMA_DLLI,
- req_ctx->gcm_iv_inc2_dma_addr, AES_BLOCK_SIZE,
- NS_BIT);
- set_cipher_config0(&desc[idx], DRV_CRYPTO_DIRECTION_ENCRYPT);
- set_setup_mode(&desc[idx], SETUP_LOAD_STATE1);
- set_flow_mode(&desc[idx], S_DIN_to_AES);
- idx++;
- }
-
- *seq_size = idx;
-}
-
-static void cc_proc_gcm_result(struct aead_request *req,
- struct cc_hw_desc desc[],
- unsigned int *seq_size)
-{
- struct crypto_aead *tfm = crypto_aead_reqtfm(req);
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
- struct aead_req_ctx *req_ctx = aead_request_ctx(req);
- dma_addr_t mac_result;
- unsigned int idx = *seq_size;
-
- if (req_ctx->gen_ctx.op_type == DRV_CRYPTO_DIRECTION_DECRYPT) {
- mac_result = req_ctx->mac_buf_dma_addr;
- } else { /* Encrypt */
- mac_result = req_ctx->icv_dma_addr;
- }
-
- /* process(ghash) gcm_block_len */
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_DLLI, req_ctx->gcm_block_len_dma_addr,
- AES_BLOCK_SIZE, NS_BIT);
- set_flow_mode(&desc[idx], DIN_HASH);
- idx++;
-
- /* Store GHASH state after GHASH(Associated Data + Cipher +LenBlock) */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], DRV_HASH_HW_GHASH);
- set_din_no_dma(&desc[idx], 0, 0xfffff0);
- set_dout_dlli(&desc[idx], req_ctx->mac_buf_dma_addr, AES_BLOCK_SIZE,
- NS_BIT, 0);
- set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
- set_flow_mode(&desc[idx], S_HASH_to_DOUT);
- set_aes_not_hash_mode(&desc[idx]);
-
- idx++;
-
- /* load AES/CTR initial CTR value inc by 1*/
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], DRV_CIPHER_GCTR);
- set_key_size_aes(&desc[idx], ctx->enc_keylen);
- set_din_type(&desc[idx], DMA_DLLI, req_ctx->gcm_iv_inc1_dma_addr,
- AES_BLOCK_SIZE, NS_BIT);
- set_cipher_config0(&desc[idx], DRV_CRYPTO_DIRECTION_ENCRYPT);
- set_setup_mode(&desc[idx], SETUP_LOAD_STATE1);
- set_flow_mode(&desc[idx], S_DIN_to_AES);
- idx++;
-
- /* Memory Barrier */
- hw_desc_init(&desc[idx]);
- set_din_no_dma(&desc[idx], 0, 0xfffff0);
- set_dout_no_dma(&desc[idx], 0, 0, 1);
- idx++;
-
- /* process GCTR on stored GHASH and store MAC in mac_state*/
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], DRV_CIPHER_GCTR);
- set_din_type(&desc[idx], DMA_DLLI, req_ctx->mac_buf_dma_addr,
- AES_BLOCK_SIZE, NS_BIT);
- set_dout_dlli(&desc[idx], mac_result, ctx->authsize, NS_BIT, 1);
- set_queue_last_ind(&desc[idx]);
- set_flow_mode(&desc[idx], DIN_AES_DOUT);
- idx++;
-
- *seq_size = idx;
-}
-
-static int cc_gcm(struct aead_request *req, struct cc_hw_desc desc[],
- unsigned int *seq_size)
-{
- struct aead_req_ctx *req_ctx = aead_request_ctx(req);
- unsigned int cipher_flow_mode;
-
- if (req_ctx->gen_ctx.op_type == DRV_CRYPTO_DIRECTION_DECRYPT) {
- cipher_flow_mode = AES_and_HASH;
- } else { /* Encrypt */
- cipher_flow_mode = AES_to_HASH_and_DOUT;
- }
-
- //in RFC4543 no data to encrypt. just copy data from src to dest.
- if (req_ctx->plaintext_authenticate_only) {
- cc_proc_cipher_desc(req, BYPASS, desc, seq_size);
- cc_set_ghash_desc(req, desc, seq_size);
- /* process(ghash) assoc data */
- cc_set_assoc_desc(req, DIN_HASH, desc, seq_size);
- cc_set_gctr_desc(req, desc, seq_size);
- cc_proc_gcm_result(req, desc, seq_size);
- return 0;
- }
-
- // for gcm and rfc4106.
- cc_set_ghash_desc(req, desc, seq_size);
- /* process(ghash) assoc data */
- if (req->assoclen > 0)
- cc_set_assoc_desc(req, DIN_HASH, desc, seq_size);
- cc_set_gctr_desc(req, desc, seq_size);
- /* process(gctr+ghash) */
- if (req_ctx->cryptlen)
- cc_proc_cipher_desc(req, cipher_flow_mode, desc, seq_size);
- cc_proc_gcm_result(req, desc, seq_size);
-
- return 0;
-}
-
-static int config_gcm_context(struct aead_request *req)
-{
- struct crypto_aead *tfm = crypto_aead_reqtfm(req);
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
- struct aead_req_ctx *req_ctx = aead_request_ctx(req);
- struct device *dev = drvdata_to_dev(ctx->drvdata);
-
- unsigned int cryptlen = (req_ctx->gen_ctx.op_type ==
- DRV_CRYPTO_DIRECTION_ENCRYPT) ?
- req->cryptlen :
- (req->cryptlen - ctx->authsize);
- __be32 counter = cpu_to_be32(2);
-
- dev_dbg(dev, "%s() cryptlen = %d, req->assoclen = %d ctx->authsize = %d\n",
- __func__, cryptlen, req->assoclen, ctx->authsize);
-
- memset(req_ctx->hkey, 0, AES_BLOCK_SIZE);
-
- memset(req_ctx->mac_buf, 0, AES_BLOCK_SIZE);
-
- memcpy(req->iv + 12, &counter, 4);
- memcpy(req_ctx->gcm_iv_inc2, req->iv, 16);
-
- counter = cpu_to_be32(1);
- memcpy(req->iv + 12, &counter, 4);
- memcpy(req_ctx->gcm_iv_inc1, req->iv, 16);
-
- if (!req_ctx->plaintext_authenticate_only) {
- __be64 temp64;
-
- temp64 = cpu_to_be64(req->assoclen * 8);
- memcpy(&req_ctx->gcm_len_block.len_a, &temp64, sizeof(temp64));
- temp64 = cpu_to_be64(cryptlen * 8);
- memcpy(&req_ctx->gcm_len_block.len_c, &temp64, 8);
- } else {
- /* rfc4543=> all data(AAD,IV,Plain) are considered additional
- * data that is nothing is encrypted.
- */
- __be64 temp64;
-
- temp64 = cpu_to_be64((req->assoclen + GCM_BLOCK_RFC4_IV_SIZE +
- cryptlen) * 8);
- memcpy(&req_ctx->gcm_len_block.len_a, &temp64, sizeof(temp64));
- temp64 = 0;
- memcpy(&req_ctx->gcm_len_block.len_c, &temp64, 8);
- }
-
- return 0;
-}
-
-static void cc_proc_rfc4_gcm(struct aead_request *req)
-{
- struct crypto_aead *tfm = crypto_aead_reqtfm(req);
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
- struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
-
- memcpy(areq_ctx->ctr_iv + GCM_BLOCK_RFC4_NONCE_OFFSET,
- ctx->ctr_nonce, GCM_BLOCK_RFC4_NONCE_SIZE);
- memcpy(areq_ctx->ctr_iv + GCM_BLOCK_RFC4_IV_OFFSET, req->iv,
- GCM_BLOCK_RFC4_IV_SIZE);
- req->iv = areq_ctx->ctr_iv;
- req->assoclen -= GCM_BLOCK_RFC4_IV_SIZE;
-}
-
-static int cc_proc_aead(struct aead_request *req,
- enum drv_crypto_direction direct)
-{
- int rc = 0;
- int seq_len = 0;
- struct cc_hw_desc desc[MAX_AEAD_PROCESS_SEQ];
- struct crypto_aead *tfm = crypto_aead_reqtfm(req);
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
- struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
- struct device *dev = drvdata_to_dev(ctx->drvdata);
- struct cc_crypto_req cc_req = {};
-
- dev_dbg(dev, "%s context=%p req=%p iv=%p src=%p src_ofs=%d dst=%p dst_ofs=%d cryptolen=%d\n",
- ((direct == DRV_CRYPTO_DIRECTION_ENCRYPT) ? "Enc" : "Dec"),
- ctx, req, req->iv, sg_virt(req->src), req->src->offset,
- sg_virt(req->dst), req->dst->offset, req->cryptlen);
-
- /* STAT_PHASE_0: Init and sanity checks */
-
- /* Check data length according to mode */
- if (validate_data_size(ctx, direct, req)) {
- dev_err(dev, "Unsupported crypt/assoc len %d/%d.\n",
- req->cryptlen, req->assoclen);
- crypto_aead_set_flags(tfm, CRYPTO_TFM_RES_BAD_BLOCK_LEN);
- return -EINVAL;
- }
-
- /* Setup DX request structure */
- cc_req.user_cb = (void *)cc_aead_complete;
- cc_req.user_arg = (void *)req;
-
- /* Setup request context */
- areq_ctx->gen_ctx.op_type = direct;
- areq_ctx->req_authsize = ctx->authsize;
- areq_ctx->cipher_mode = ctx->cipher_mode;
-
- /* STAT_PHASE_1: Map buffers */
-
- if (ctx->cipher_mode == DRV_CIPHER_CTR) {
- /* Build CTR IV - Copy nonce from last 4 bytes in
- * CTR key to first 4 bytes in CTR IV
- */
- memcpy(areq_ctx->ctr_iv, ctx->ctr_nonce,
- CTR_RFC3686_NONCE_SIZE);
- if (!areq_ctx->backup_giv) /*User none-generated IV*/
- memcpy(areq_ctx->ctr_iv + CTR_RFC3686_NONCE_SIZE,
- req->iv, CTR_RFC3686_IV_SIZE);
- /* Initialize counter portion of counter block */
- *(__be32 *)(areq_ctx->ctr_iv + CTR_RFC3686_NONCE_SIZE +
- CTR_RFC3686_IV_SIZE) = cpu_to_be32(1);
-
- /* Replace with counter iv */
- req->iv = areq_ctx->ctr_iv;
- areq_ctx->hw_iv_size = CTR_RFC3686_BLOCK_SIZE;
- } else if ((ctx->cipher_mode == DRV_CIPHER_CCM) ||
- (ctx->cipher_mode == DRV_CIPHER_GCTR)) {
- areq_ctx->hw_iv_size = AES_BLOCK_SIZE;
- if (areq_ctx->ctr_iv != req->iv) {
- memcpy(areq_ctx->ctr_iv, req->iv,
- crypto_aead_ivsize(tfm));
- req->iv = areq_ctx->ctr_iv;
- }
- } else {
- areq_ctx->hw_iv_size = crypto_aead_ivsize(tfm);
- }
-
- if (ctx->cipher_mode == DRV_CIPHER_CCM) {
- rc = config_ccm_adata(req);
- if (rc) {
- dev_dbg(dev, "config_ccm_adata() returned with a failure %d!",
- rc);
- goto exit;
- }
- } else {
- areq_ctx->ccm_hdr_size = ccm_header_size_null;
- }
-
- if (ctx->cipher_mode == DRV_CIPHER_GCTR) {
- rc = config_gcm_context(req);
- if (rc) {
- dev_dbg(dev, "config_gcm_context() returned with a failure %d!",
- rc);
- goto exit;
- }
- }
-
- rc = cc_map_aead_request(ctx->drvdata, req);
- if (rc) {
- dev_err(dev, "map_request() failed\n");
- goto exit;
- }
-
- /* do we need to generate IV? */
- if (areq_ctx->backup_giv) {
- /* set the DMA mapped IV address*/
- if (ctx->cipher_mode == DRV_CIPHER_CTR) {
- cc_req.ivgen_dma_addr[0] =
- areq_ctx->gen_ctx.iv_dma_addr +
- CTR_RFC3686_NONCE_SIZE;
- cc_req.ivgen_dma_addr_len = 1;
- } else if (ctx->cipher_mode == DRV_CIPHER_CCM) {
- /* In ccm, the IV needs to exist both inside B0 and
- * inside the counter.It is also copied to iv_dma_addr
- * for other reasons (like returning it to the user).
- * So, using 3 (identical) IV outputs.
- */
- cc_req.ivgen_dma_addr[0] =
- areq_ctx->gen_ctx.iv_dma_addr +
- CCM_BLOCK_IV_OFFSET;
- cc_req.ivgen_dma_addr[1] =
- sg_dma_address(&areq_ctx->ccm_adata_sg) +
- CCM_B0_OFFSET + CCM_BLOCK_IV_OFFSET;
- cc_req.ivgen_dma_addr[2] =
- sg_dma_address(&areq_ctx->ccm_adata_sg) +
- CCM_CTR_COUNT_0_OFFSET + CCM_BLOCK_IV_OFFSET;
- cc_req.ivgen_dma_addr_len = 3;
- } else {
- cc_req.ivgen_dma_addr[0] =
- areq_ctx->gen_ctx.iv_dma_addr;
- cc_req.ivgen_dma_addr_len = 1;
- }
-
- /* set the IV size (8/16 B long)*/
- cc_req.ivgen_size = crypto_aead_ivsize(tfm);
- }
-
- /* STAT_PHASE_2: Create sequence */
-
- /* Load MLLI tables to SRAM if necessary */
- cc_mlli_to_sram(req, desc, &seq_len);
-
- /*TODO: move seq len by reference */
- switch (ctx->auth_mode) {
- case DRV_HASH_SHA1:
- case DRV_HASH_SHA256:
- cc_hmac_authenc(req, desc, &seq_len);
- break;
- case DRV_HASH_XCBC_MAC:
- cc_xcbc_authenc(req, desc, &seq_len);
- break;
- case DRV_HASH_NULL:
- if (ctx->cipher_mode == DRV_CIPHER_CCM)
- cc_ccm(req, desc, &seq_len);
- if (ctx->cipher_mode == DRV_CIPHER_GCTR)
- cc_gcm(req, desc, &seq_len);
- break;
- default:
- dev_err(dev, "Unsupported authenc (%d)\n", ctx->auth_mode);
- cc_unmap_aead_request(dev, req);
- rc = -ENOTSUPP;
- goto exit;
- }
-
- /* STAT_PHASE_3: Lock HW and push sequence */
-
- rc = cc_send_request(ctx->drvdata, &cc_req, desc, seq_len, &req->base);
-
- if (rc != -EINPROGRESS && rc != -EBUSY) {
- dev_err(dev, "send_request() failed (rc=%d)\n", rc);
- cc_unmap_aead_request(dev, req);
- }
-
-exit:
- return rc;
-}
-
-static int cc_aead_encrypt(struct aead_request *req)
-{
- struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
- int rc;
-
- /* No generated IV required */
- areq_ctx->backup_iv = req->iv;
- areq_ctx->backup_giv = NULL;
- areq_ctx->is_gcm4543 = false;
-
- areq_ctx->plaintext_authenticate_only = false;
-
- rc = cc_proc_aead(req, DRV_CRYPTO_DIRECTION_ENCRYPT);
- if (rc != -EINPROGRESS && rc != -EBUSY)
- req->iv = areq_ctx->backup_iv;
-
- return rc;
-}
-
-static int cc_rfc4309_ccm_encrypt(struct aead_request *req)
-{
- /* Very similar to cc_aead_encrypt() above. */
-
- struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
- struct crypto_aead *tfm = crypto_aead_reqtfm(req);
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
- struct device *dev = drvdata_to_dev(ctx->drvdata);
- int rc = -EINVAL;
-
- if (!valid_assoclen(req)) {
- dev_err(dev, "invalid Assoclen:%u\n", req->assoclen);
- goto out;
- }
-
- /* No generated IV required */
- areq_ctx->backup_iv = req->iv;
- areq_ctx->backup_giv = NULL;
- areq_ctx->is_gcm4543 = true;
-
- cc_proc_rfc4309_ccm(req);
-
- rc = cc_proc_aead(req, DRV_CRYPTO_DIRECTION_ENCRYPT);
- if (rc != -EINPROGRESS && rc != -EBUSY)
- req->iv = areq_ctx->backup_iv;
-out:
- return rc;
-}
-
-static int cc_aead_decrypt(struct aead_request *req)
-{
- struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
- int rc;
-
- /* No generated IV required */
- areq_ctx->backup_iv = req->iv;
- areq_ctx->backup_giv = NULL;
- areq_ctx->is_gcm4543 = false;
-
- areq_ctx->plaintext_authenticate_only = false;
-
- rc = cc_proc_aead(req, DRV_CRYPTO_DIRECTION_DECRYPT);
- if (rc != -EINPROGRESS && rc != -EBUSY)
- req->iv = areq_ctx->backup_iv;
-
- return rc;
-}
-
-static int cc_rfc4309_ccm_decrypt(struct aead_request *req)
-{
- struct crypto_aead *tfm = crypto_aead_reqtfm(req);
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
- struct device *dev = drvdata_to_dev(ctx->drvdata);
- struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
- int rc = -EINVAL;
-
- if (!valid_assoclen(req)) {
- dev_err(dev, "invalid Assoclen:%u\n", req->assoclen);
- goto out;
- }
-
- /* No generated IV required */
- areq_ctx->backup_iv = req->iv;
- areq_ctx->backup_giv = NULL;
-
- areq_ctx->is_gcm4543 = true;
- cc_proc_rfc4309_ccm(req);
-
- rc = cc_proc_aead(req, DRV_CRYPTO_DIRECTION_DECRYPT);
- if (rc != -EINPROGRESS && rc != -EBUSY)
- req->iv = areq_ctx->backup_iv;
-
-out:
- return rc;
-}
-
-static int cc_rfc4106_gcm_setkey(struct crypto_aead *tfm, const u8 *key,
- unsigned int keylen)
-{
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
- struct device *dev = drvdata_to_dev(ctx->drvdata);
-
- dev_dbg(dev, "%s() keylen %d, key %p\n", __func__, keylen, key);
-
- if (keylen < 4)
- return -EINVAL;
-
- keylen -= 4;
- memcpy(ctx->ctr_nonce, key + keylen, 4);
-
- return cc_aead_setkey(tfm, key, keylen);
-}
-
-static int cc_rfc4543_gcm_setkey(struct crypto_aead *tfm, const u8 *key,
- unsigned int keylen)
-{
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
- struct device *dev = drvdata_to_dev(ctx->drvdata);
-
- dev_dbg(dev, "%s() keylen %d, key %p\n", __func__, keylen, key);
-
- if (keylen < 4)
- return -EINVAL;
-
- keylen -= 4;
- memcpy(ctx->ctr_nonce, key + keylen, 4);
-
- return cc_aead_setkey(tfm, key, keylen);
-}
-
-static int cc_gcm_setauthsize(struct crypto_aead *authenc,
- unsigned int authsize)
-{
- switch (authsize) {
- case 4:
- case 8:
- case 12:
- case 13:
- case 14:
- case 15:
- case 16:
- break;
- default:
- return -EINVAL;
- }
-
- return cc_aead_setauthsize(authenc, authsize);
-}
-
-static int cc_rfc4106_gcm_setauthsize(struct crypto_aead *authenc,
- unsigned int authsize)
-{
- struct cc_aead_ctx *ctx = crypto_aead_ctx(authenc);
- struct device *dev = drvdata_to_dev(ctx->drvdata);
-
- dev_dbg(dev, "authsize %d\n", authsize);
-
- switch (authsize) {
- case 8:
- case 12:
- case 16:
- break;
- default:
- return -EINVAL;
- }
-
- return cc_aead_setauthsize(authenc, authsize);
-}
-
-static int cc_rfc4543_gcm_setauthsize(struct crypto_aead *authenc,
- unsigned int authsize)
-{
- struct cc_aead_ctx *ctx = crypto_aead_ctx(authenc);
- struct device *dev = drvdata_to_dev(ctx->drvdata);
-
- dev_dbg(dev, "authsize %d\n", authsize);
-
- if (authsize != 16)
- return -EINVAL;
-
- return cc_aead_setauthsize(authenc, authsize);
-}
-
-static int cc_rfc4106_gcm_encrypt(struct aead_request *req)
-{
- /* Very similar to cc_aead_encrypt() above. */
-
- struct crypto_aead *tfm = crypto_aead_reqtfm(req);
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
- struct device *dev = drvdata_to_dev(ctx->drvdata);
- struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
- int rc = -EINVAL;
-
- if (!valid_assoclen(req)) {
- dev_err(dev, "invalid Assoclen:%u\n", req->assoclen);
- goto out;
- }
-
- /* No generated IV required */
- areq_ctx->backup_iv = req->iv;
- areq_ctx->backup_giv = NULL;
-
- areq_ctx->plaintext_authenticate_only = false;
-
- cc_proc_rfc4_gcm(req);
- areq_ctx->is_gcm4543 = true;
-
- rc = cc_proc_aead(req, DRV_CRYPTO_DIRECTION_ENCRYPT);
- if (rc != -EINPROGRESS && rc != -EBUSY)
- req->iv = areq_ctx->backup_iv;
-out:
- return rc;
-}
-
-static int cc_rfc4543_gcm_encrypt(struct aead_request *req)
-{
- /* Very similar to cc_aead_encrypt() above. */
-
- struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
- int rc;
-
- //plaintext is not encryped with rfc4543
- areq_ctx->plaintext_authenticate_only = true;
-
- /* No generated IV required */
- areq_ctx->backup_iv = req->iv;
- areq_ctx->backup_giv = NULL;
-
- cc_proc_rfc4_gcm(req);
- areq_ctx->is_gcm4543 = true;
-
- rc = cc_proc_aead(req, DRV_CRYPTO_DIRECTION_ENCRYPT);
- if (rc != -EINPROGRESS && rc != -EBUSY)
- req->iv = areq_ctx->backup_iv;
-
- return rc;
-}
-
-static int cc_rfc4106_gcm_decrypt(struct aead_request *req)
-{
- /* Very similar to cc_aead_decrypt() above. */
-
- struct crypto_aead *tfm = crypto_aead_reqtfm(req);
- struct cc_aead_ctx *ctx = crypto_aead_ctx(tfm);
- struct device *dev = drvdata_to_dev(ctx->drvdata);
- struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
- int rc = -EINVAL;
-
- if (!valid_assoclen(req)) {
- dev_err(dev, "invalid Assoclen:%u\n", req->assoclen);
- goto out;
- }
-
- /* No generated IV required */
- areq_ctx->backup_iv = req->iv;
- areq_ctx->backup_giv = NULL;
-
- areq_ctx->plaintext_authenticate_only = false;
-
- cc_proc_rfc4_gcm(req);
- areq_ctx->is_gcm4543 = true;
-
- rc = cc_proc_aead(req, DRV_CRYPTO_DIRECTION_DECRYPT);
- if (rc != -EINPROGRESS && rc != -EBUSY)
- req->iv = areq_ctx->backup_iv;
-out:
- return rc;
-}
-
-static int cc_rfc4543_gcm_decrypt(struct aead_request *req)
-{
- /* Very similar to cc_aead_decrypt() above. */
-
- struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
- int rc;
-
- //plaintext is not decryped with rfc4543
- areq_ctx->plaintext_authenticate_only = true;
-
- /* No generated IV required */
- areq_ctx->backup_iv = req->iv;
- areq_ctx->backup_giv = NULL;
-
- cc_proc_rfc4_gcm(req);
- areq_ctx->is_gcm4543 = true;
-
- rc = cc_proc_aead(req, DRV_CRYPTO_DIRECTION_DECRYPT);
- if (rc != -EINPROGRESS && rc != -EBUSY)
- req->iv = areq_ctx->backup_iv;
-
- return rc;
-}
-
-/* DX Block aead alg */
-static struct cc_alg_template aead_algs[] = {
- {
- .name = "authenc(hmac(sha1),cbc(aes))",
- .driver_name = "authenc-hmac-sha1-cbc-aes-dx",
- .blocksize = AES_BLOCK_SIZE,
- .type = CRYPTO_ALG_TYPE_AEAD,
- .template_aead = {
- .setkey = cc_aead_setkey,
- .setauthsize = cc_aead_setauthsize,
- .encrypt = cc_aead_encrypt,
- .decrypt = cc_aead_decrypt,
- .init = cc_aead_init,
- .exit = cc_aead_exit,
- .ivsize = AES_BLOCK_SIZE,
- .maxauthsize = SHA1_DIGEST_SIZE,
- },
- .cipher_mode = DRV_CIPHER_CBC,
- .flow_mode = S_DIN_to_AES,
- .auth_mode = DRV_HASH_SHA1,
- },
- {
- .name = "authenc(hmac(sha1),cbc(des3_ede))",
- .driver_name = "authenc-hmac-sha1-cbc-des3-dx",
- .blocksize = DES3_EDE_BLOCK_SIZE,
- .type = CRYPTO_ALG_TYPE_AEAD,
- .template_aead = {
- .setkey = cc_aead_setkey,
- .setauthsize = cc_aead_setauthsize,
- .encrypt = cc_aead_encrypt,
- .decrypt = cc_aead_decrypt,
- .init = cc_aead_init,
- .exit = cc_aead_exit,
- .ivsize = DES3_EDE_BLOCK_SIZE,
- .maxauthsize = SHA1_DIGEST_SIZE,
- },
- .cipher_mode = DRV_CIPHER_CBC,
- .flow_mode = S_DIN_to_DES,
- .auth_mode = DRV_HASH_SHA1,
- },
- {
- .name = "authenc(hmac(sha256),cbc(aes))",
- .driver_name = "authenc-hmac-sha256-cbc-aes-dx",
- .blocksize = AES_BLOCK_SIZE,
- .type = CRYPTO_ALG_TYPE_AEAD,
- .template_aead = {
- .setkey = cc_aead_setkey,
- .setauthsize = cc_aead_setauthsize,
- .encrypt = cc_aead_encrypt,
- .decrypt = cc_aead_decrypt,
- .init = cc_aead_init,
- .exit = cc_aead_exit,
- .ivsize = AES_BLOCK_SIZE,
- .maxauthsize = SHA256_DIGEST_SIZE,
- },
- .cipher_mode = DRV_CIPHER_CBC,
- .flow_mode = S_DIN_to_AES,
- .auth_mode = DRV_HASH_SHA256,
- },
- {
- .name = "authenc(hmac(sha256),cbc(des3_ede))",
- .driver_name = "authenc-hmac-sha256-cbc-des3-dx",
- .blocksize = DES3_EDE_BLOCK_SIZE,
- .type = CRYPTO_ALG_TYPE_AEAD,
- .template_aead = {
- .setkey = cc_aead_setkey,
- .setauthsize = cc_aead_setauthsize,
- .encrypt = cc_aead_encrypt,
- .decrypt = cc_aead_decrypt,
- .init = cc_aead_init,
- .exit = cc_aead_exit,
- .ivsize = DES3_EDE_BLOCK_SIZE,
- .maxauthsize = SHA256_DIGEST_SIZE,
- },
- .cipher_mode = DRV_CIPHER_CBC,
- .flow_mode = S_DIN_to_DES,
- .auth_mode = DRV_HASH_SHA256,
- },
- {
- .name = "authenc(xcbc(aes),cbc(aes))",
- .driver_name = "authenc-xcbc-aes-cbc-aes-dx",
- .blocksize = AES_BLOCK_SIZE,
- .type = CRYPTO_ALG_TYPE_AEAD,
- .template_aead = {
- .setkey = cc_aead_setkey,
- .setauthsize = cc_aead_setauthsize,
- .encrypt = cc_aead_encrypt,
- .decrypt = cc_aead_decrypt,
- .init = cc_aead_init,
- .exit = cc_aead_exit,
- .ivsize = AES_BLOCK_SIZE,
- .maxauthsize = AES_BLOCK_SIZE,
- },
- .cipher_mode = DRV_CIPHER_CBC,
- .flow_mode = S_DIN_to_AES,
- .auth_mode = DRV_HASH_XCBC_MAC,
- },
- {
- .name = "authenc(hmac(sha1),rfc3686(ctr(aes)))",
- .driver_name = "authenc-hmac-sha1-rfc3686-ctr-aes-dx",
- .blocksize = 1,
- .type = CRYPTO_ALG_TYPE_AEAD,
- .template_aead = {
- .setkey = cc_aead_setkey,
- .setauthsize = cc_aead_setauthsize,
- .encrypt = cc_aead_encrypt,
- .decrypt = cc_aead_decrypt,
- .init = cc_aead_init,
- .exit = cc_aead_exit,
- .ivsize = CTR_RFC3686_IV_SIZE,
- .maxauthsize = SHA1_DIGEST_SIZE,
- },
- .cipher_mode = DRV_CIPHER_CTR,
- .flow_mode = S_DIN_to_AES,
- .auth_mode = DRV_HASH_SHA1,
- },
- {
- .name = "authenc(hmac(sha256),rfc3686(ctr(aes)))",
- .driver_name = "authenc-hmac-sha256-rfc3686-ctr-aes-dx",
- .blocksize = 1,
- .type = CRYPTO_ALG_TYPE_AEAD,
- .template_aead = {
- .setkey = cc_aead_setkey,
- .setauthsize = cc_aead_setauthsize,
- .encrypt = cc_aead_encrypt,
- .decrypt = cc_aead_decrypt,
- .init = cc_aead_init,
- .exit = cc_aead_exit,
- .ivsize = CTR_RFC3686_IV_SIZE,
- .maxauthsize = SHA256_DIGEST_SIZE,
- },
- .cipher_mode = DRV_CIPHER_CTR,
- .flow_mode = S_DIN_to_AES,
- .auth_mode = DRV_HASH_SHA256,
- },
- {
- .name = "authenc(xcbc(aes),rfc3686(ctr(aes)))",
- .driver_name = "authenc-xcbc-aes-rfc3686-ctr-aes-dx",
- .blocksize = 1,
- .type = CRYPTO_ALG_TYPE_AEAD,
- .template_aead = {
- .setkey = cc_aead_setkey,
- .setauthsize = cc_aead_setauthsize,
- .encrypt = cc_aead_encrypt,
- .decrypt = cc_aead_decrypt,
- .init = cc_aead_init,
- .exit = cc_aead_exit,
- .ivsize = CTR_RFC3686_IV_SIZE,
- .maxauthsize = AES_BLOCK_SIZE,
- },
- .cipher_mode = DRV_CIPHER_CTR,
- .flow_mode = S_DIN_to_AES,
- .auth_mode = DRV_HASH_XCBC_MAC,
- },
- {
- .name = "ccm(aes)",
- .driver_name = "ccm-aes-dx",
- .blocksize = 1,
- .type = CRYPTO_ALG_TYPE_AEAD,
- .template_aead = {
- .setkey = cc_aead_setkey,
- .setauthsize = cc_ccm_setauthsize,
- .encrypt = cc_aead_encrypt,
- .decrypt = cc_aead_decrypt,
- .init = cc_aead_init,
- .exit = cc_aead_exit,
- .ivsize = AES_BLOCK_SIZE,
- .maxauthsize = AES_BLOCK_SIZE,
- },
- .cipher_mode = DRV_CIPHER_CCM,
- .flow_mode = S_DIN_to_AES,
- .auth_mode = DRV_HASH_NULL,
- },
- {
- .name = "rfc4309(ccm(aes))",
- .driver_name = "rfc4309-ccm-aes-dx",
- .blocksize = 1,
- .type = CRYPTO_ALG_TYPE_AEAD,
- .template_aead = {
- .setkey = cc_rfc4309_ccm_setkey,
- .setauthsize = cc_rfc4309_ccm_setauthsize,
- .encrypt = cc_rfc4309_ccm_encrypt,
- .decrypt = cc_rfc4309_ccm_decrypt,
- .init = cc_aead_init,
- .exit = cc_aead_exit,
- .ivsize = CCM_BLOCK_IV_SIZE,
- .maxauthsize = AES_BLOCK_SIZE,
- },
- .cipher_mode = DRV_CIPHER_CCM,
- .flow_mode = S_DIN_to_AES,
- .auth_mode = DRV_HASH_NULL,
- },
- {
- .name = "gcm(aes)",
- .driver_name = "gcm-aes-dx",
- .blocksize = 1,
- .type = CRYPTO_ALG_TYPE_AEAD,
- .template_aead = {
- .setkey = cc_aead_setkey,
- .setauthsize = cc_gcm_setauthsize,
- .encrypt = cc_aead_encrypt,
- .decrypt = cc_aead_decrypt,
- .init = cc_aead_init,
- .exit = cc_aead_exit,
- .ivsize = 12,
- .maxauthsize = AES_BLOCK_SIZE,
- },
- .cipher_mode = DRV_CIPHER_GCTR,
- .flow_mode = S_DIN_to_AES,
- .auth_mode = DRV_HASH_NULL,
- },
- {
- .name = "rfc4106(gcm(aes))",
- .driver_name = "rfc4106-gcm-aes-dx",
- .blocksize = 1,
- .type = CRYPTO_ALG_TYPE_AEAD,
- .template_aead = {
- .setkey = cc_rfc4106_gcm_setkey,
- .setauthsize = cc_rfc4106_gcm_setauthsize,
- .encrypt = cc_rfc4106_gcm_encrypt,
- .decrypt = cc_rfc4106_gcm_decrypt,
- .init = cc_aead_init,
- .exit = cc_aead_exit,
- .ivsize = GCM_BLOCK_RFC4_IV_SIZE,
- .maxauthsize = AES_BLOCK_SIZE,
- },
- .cipher_mode = DRV_CIPHER_GCTR,
- .flow_mode = S_DIN_to_AES,
- .auth_mode = DRV_HASH_NULL,
- },
- {
- .name = "rfc4543(gcm(aes))",
- .driver_name = "rfc4543-gcm-aes-dx",
- .blocksize = 1,
- .type = CRYPTO_ALG_TYPE_AEAD,
- .template_aead = {
- .setkey = cc_rfc4543_gcm_setkey,
- .setauthsize = cc_rfc4543_gcm_setauthsize,
- .encrypt = cc_rfc4543_gcm_encrypt,
- .decrypt = cc_rfc4543_gcm_decrypt,
- .init = cc_aead_init,
- .exit = cc_aead_exit,
- .ivsize = GCM_BLOCK_RFC4_IV_SIZE,
- .maxauthsize = AES_BLOCK_SIZE,
- },
- .cipher_mode = DRV_CIPHER_GCTR,
- .flow_mode = S_DIN_to_AES,
- .auth_mode = DRV_HASH_NULL,
- },
-};
-
-static struct cc_crypto_alg *cc_create_aead_alg(struct cc_alg_template *tmpl,
- struct device *dev)
-{
- struct cc_crypto_alg *t_alg;
- struct aead_alg *alg;
-
- t_alg = kzalloc(sizeof(*t_alg), GFP_KERNEL);
- if (!t_alg)
- return ERR_PTR(-ENOMEM);
-
- alg = &tmpl->template_aead;
-
- snprintf(alg->base.cra_name, CRYPTO_MAX_ALG_NAME, "%s", tmpl->name);
- snprintf(alg->base.cra_driver_name, CRYPTO_MAX_ALG_NAME, "%s",
- tmpl->driver_name);
- alg->base.cra_module = THIS_MODULE;
- alg->base.cra_priority = CC_CRA_PRIO;
-
- alg->base.cra_ctxsize = sizeof(struct cc_aead_ctx);
- alg->base.cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_KERN_DRIVER_ONLY |
- tmpl->type;
- alg->init = cc_aead_init;
- alg->exit = cc_aead_exit;
-
- t_alg->aead_alg = *alg;
-
- t_alg->cipher_mode = tmpl->cipher_mode;
- t_alg->flow_mode = tmpl->flow_mode;
- t_alg->auth_mode = tmpl->auth_mode;
-
- return t_alg;
-}
-
-int cc_aead_free(struct cc_drvdata *drvdata)
-{
- struct cc_crypto_alg *t_alg, *n;
- struct cc_aead_handle *aead_handle =
- (struct cc_aead_handle *)drvdata->aead_handle;
-
- if (aead_handle) {
- /* Remove registered algs */
- list_for_each_entry_safe(t_alg, n, &aead_handle->aead_list,
- entry) {
- crypto_unregister_aead(&t_alg->aead_alg);
- list_del(&t_alg->entry);
- kfree(t_alg);
- }
- kfree(aead_handle);
- drvdata->aead_handle = NULL;
- }
-
- return 0;
-}
-
-int cc_aead_alloc(struct cc_drvdata *drvdata)
-{
- struct cc_aead_handle *aead_handle;
- struct cc_crypto_alg *t_alg;
- int rc = -ENOMEM;
- int alg;
- struct device *dev = drvdata_to_dev(drvdata);
-
- aead_handle = kmalloc(sizeof(*aead_handle), GFP_KERNEL);
- if (!aead_handle) {
- rc = -ENOMEM;
- goto fail0;
- }
-
- INIT_LIST_HEAD(&aead_handle->aead_list);
- drvdata->aead_handle = aead_handle;
-
- aead_handle->sram_workspace_addr = cc_sram_alloc(drvdata,
- MAX_HMAC_DIGEST_SIZE);
-
- if (aead_handle->sram_workspace_addr == NULL_SRAM_ADDR) {
- dev_err(dev, "SRAM pool exhausted\n");
- rc = -ENOMEM;
- goto fail1;
- }
-
- /* Linux crypto */
- for (alg = 0; alg < ARRAY_SIZE(aead_algs); alg++) {
- t_alg = cc_create_aead_alg(&aead_algs[alg], dev);
- if (IS_ERR(t_alg)) {
- rc = PTR_ERR(t_alg);
- dev_err(dev, "%s alg allocation failed\n",
- aead_algs[alg].driver_name);
- goto fail1;
- }
- t_alg->drvdata = drvdata;
- rc = crypto_register_aead(&t_alg->aead_alg);
- if (rc) {
- dev_err(dev, "%s alg registration failed\n",
- t_alg->aead_alg.base.cra_driver_name);
- goto fail2;
- } else {
- list_add_tail(&t_alg->entry, &aead_handle->aead_list);
- dev_dbg(dev, "Registered %s\n",
- t_alg->aead_alg.base.cra_driver_name);
- }
- }
-
- return 0;
-
-fail2:
- kfree(t_alg);
-fail1:
- cc_aead_free(drvdata);
-fail0:
- return rc;
-}
diff --git a/drivers/staging/ccree/cc_buffer_mgr.c b/drivers/staging/ccree/cc_buffer_mgr.c
deleted file mode 100644
index 14b2eabbf70a..000000000000
--- a/drivers/staging/ccree/cc_buffer_mgr.c
+++ /dev/null
@@ -1,1651 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
-
-#include <crypto/internal/aead.h>
-#include <crypto/authenc.h>
-#include <crypto/scatterwalk.h>
-#include <linux/dmapool.h>
-#include <linux/dma-mapping.h>
-
-#include "cc_buffer_mgr.h"
-#include "cc_lli_defs.h"
-#include "cc_cipher.h"
-#include "cc_hash.h"
-#include "cc_aead.h"
-
-enum dma_buffer_type {
- DMA_NULL_TYPE = -1,
- DMA_SGL_TYPE = 1,
- DMA_BUFF_TYPE = 2,
-};
-
-struct buff_mgr_handle {
- struct dma_pool *mlli_buffs_pool;
-};
-
-union buffer_array_entry {
- struct scatterlist *sgl;
- dma_addr_t buffer_dma;
-};
-
-struct buffer_array {
- unsigned int num_of_buffers;
- union buffer_array_entry entry[MAX_NUM_OF_BUFFERS_IN_MLLI];
- unsigned int offset[MAX_NUM_OF_BUFFERS_IN_MLLI];
- int nents[MAX_NUM_OF_BUFFERS_IN_MLLI];
- int total_data_len[MAX_NUM_OF_BUFFERS_IN_MLLI];
- enum dma_buffer_type type[MAX_NUM_OF_BUFFERS_IN_MLLI];
- bool is_last[MAX_NUM_OF_BUFFERS_IN_MLLI];
- u32 *mlli_nents[MAX_NUM_OF_BUFFERS_IN_MLLI];
-};
-
-static inline char *cc_dma_buf_type(enum cc_req_dma_buf_type type)
-{
- switch (type) {
- case CC_DMA_BUF_NULL:
- return "BUF_NULL";
- case CC_DMA_BUF_DLLI:
- return "BUF_DLLI";
- case CC_DMA_BUF_MLLI:
- return "BUF_MLLI";
- default:
- return "BUF_INVALID";
- }
-}
-
-/**
- * cc_copy_mac() - Copy MAC to temporary location
- *
- * @dev: device object
- * @req: aead request object
- * @dir: [IN] copy from/to sgl
- */
-static void cc_copy_mac(struct device *dev, struct aead_request *req,
- enum cc_sg_cpy_direct dir)
-{
- struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
- struct crypto_aead *tfm = crypto_aead_reqtfm(req);
- u32 skip = req->assoclen + req->cryptlen;
-
- if (areq_ctx->is_gcm4543)
- skip += crypto_aead_ivsize(tfm);
-
- cc_copy_sg_portion(dev, areq_ctx->backup_mac, req->src,
- (skip - areq_ctx->req_authsize), skip, dir);
-}
-
-/**
- * cc_get_sgl_nents() - Get scatterlist number of entries.
- *
- * @sg_list: SG list
- * @nbytes: [IN] Total SGL data bytes.
- * @lbytes: [OUT] Returns the amount of bytes at the last entry
- */
-static unsigned int cc_get_sgl_nents(struct device *dev,
- struct scatterlist *sg_list,
- unsigned int nbytes, u32 *lbytes,
- bool *is_chained)
-{
- unsigned int nents = 0;
-
- while (nbytes && sg_list) {
- if (sg_list->length) {
- nents++;
- /* get the number of bytes in the last entry */
- *lbytes = nbytes;
- nbytes -= (sg_list->length > nbytes) ?
- nbytes : sg_list->length;
- sg_list = sg_next(sg_list);
- } else {
- sg_list = (struct scatterlist *)sg_page(sg_list);
- if (is_chained)
- *is_chained = true;
- }
- }
- dev_dbg(dev, "nents %d last bytes %d\n", nents, *lbytes);
- return nents;
-}
-
-/**
- * cc_zero_sgl() - Zero scatter scatter list data.
- *
- * @sgl:
- */
-void cc_zero_sgl(struct scatterlist *sgl, u32 data_len)
-{
- struct scatterlist *current_sg = sgl;
- int sg_index = 0;
-
- while (sg_index <= data_len) {
- if (!current_sg) {
- /* reached the end of the sgl --> just return back */
- return;
- }
- memset(sg_virt(current_sg), 0, current_sg->length);
- sg_index += current_sg->length;
- current_sg = sg_next(current_sg);
- }
-}
-
-/**
- * cc_copy_sg_portion() - Copy scatter list data,
- * from to_skip to end, to dest and vice versa
- *
- * @dest:
- * @sg:
- * @to_skip:
- * @end:
- * @direct:
- */
-void cc_copy_sg_portion(struct device *dev, u8 *dest, struct scatterlist *sg,
- u32 to_skip, u32 end, enum cc_sg_cpy_direct direct)
-{
- u32 nents, lbytes;
-
- nents = cc_get_sgl_nents(dev, sg, end, &lbytes, NULL);
- sg_copy_buffer(sg, nents, (void *)dest, (end - to_skip + 1), to_skip,
- (direct == CC_SG_TO_BUF));
-}
-
-static int cc_render_buff_to_mlli(struct device *dev, dma_addr_t buff_dma,
- u32 buff_size, u32 *curr_nents,
- u32 **mlli_entry_pp)
-{
- u32 *mlli_entry_p = *mlli_entry_pp;
- u32 new_nents;
-
- /* Verify there is no memory overflow*/
- new_nents = (*curr_nents + buff_size / CC_MAX_MLLI_ENTRY_SIZE + 1);
- if (new_nents > MAX_NUM_OF_TOTAL_MLLI_ENTRIES)
- return -ENOMEM;
-
- /*handle buffer longer than 64 kbytes */
- while (buff_size > CC_MAX_MLLI_ENTRY_SIZE) {
- cc_lli_set_addr(mlli_entry_p, buff_dma);
- cc_lli_set_size(mlli_entry_p, CC_MAX_MLLI_ENTRY_SIZE);
- dev_dbg(dev, "entry[%d]: single_buff=0x%08X size=%08X\n",
- *curr_nents, mlli_entry_p[LLI_WORD0_OFFSET],
- mlli_entry_p[LLI_WORD1_OFFSET]);
- buff_dma += CC_MAX_MLLI_ENTRY_SIZE;
- buff_size -= CC_MAX_MLLI_ENTRY_SIZE;
- mlli_entry_p = mlli_entry_p + 2;
- (*curr_nents)++;
- }
- /*Last entry */
- cc_lli_set_addr(mlli_entry_p, buff_dma);
- cc_lli_set_size(mlli_entry_p, buff_size);
- dev_dbg(dev, "entry[%d]: single_buff=0x%08X size=%08X\n",
- *curr_nents, mlli_entry_p[LLI_WORD0_OFFSET],
- mlli_entry_p[LLI_WORD1_OFFSET]);
- mlli_entry_p = mlli_entry_p + 2;
- *mlli_entry_pp = mlli_entry_p;
- (*curr_nents)++;
- return 0;
-}
-
-static int cc_render_sg_to_mlli(struct device *dev, struct scatterlist *sgl,
- u32 sgl_data_len, u32 sgl_offset,
- u32 *curr_nents, u32 **mlli_entry_pp)
-{
- struct scatterlist *curr_sgl = sgl;
- u32 *mlli_entry_p = *mlli_entry_pp;
- s32 rc = 0;
-
- for ( ; (curr_sgl && sgl_data_len);
- curr_sgl = sg_next(curr_sgl)) {
- u32 entry_data_len =
- (sgl_data_len > sg_dma_len(curr_sgl) - sgl_offset) ?
- sg_dma_len(curr_sgl) - sgl_offset :
- sgl_data_len;
- sgl_data_len -= entry_data_len;
- rc = cc_render_buff_to_mlli(dev, sg_dma_address(curr_sgl) +
- sgl_offset, entry_data_len,
- curr_nents, &mlli_entry_p);
- if (rc)
- return rc;
-
- sgl_offset = 0;
- }
- *mlli_entry_pp = mlli_entry_p;
- return 0;
-}
-
-static int cc_generate_mlli(struct device *dev, struct buffer_array *sg_data,
- struct mlli_params *mlli_params, gfp_t flags)
-{
- u32 *mlli_p;
- u32 total_nents = 0, prev_total_nents = 0;
- int rc = 0, i;
-
- dev_dbg(dev, "NUM of SG's = %d\n", sg_data->num_of_buffers);
-
- /* Allocate memory from the pointed pool */
- mlli_params->mlli_virt_addr =
- dma_pool_alloc(mlli_params->curr_pool, flags,
- &mlli_params->mlli_dma_addr);
- if (!mlli_params->mlli_virt_addr) {
- dev_err(dev, "dma_pool_alloc() failed\n");
- rc = -ENOMEM;
- goto build_mlli_exit;
- }
- /* Point to start of MLLI */
- mlli_p = (u32 *)mlli_params->mlli_virt_addr;
- /* go over all SG's and link it to one MLLI table */
- for (i = 0; i < sg_data->num_of_buffers; i++) {
- union buffer_array_entry *entry = &sg_data->entry[i];
- u32 tot_len = sg_data->total_data_len[i];
- u32 offset = sg_data->offset[i];
-
- if (sg_data->type[i] == DMA_SGL_TYPE)
- rc = cc_render_sg_to_mlli(dev, entry->sgl, tot_len,
- offset, &total_nents,
- &mlli_p);
- else /*DMA_BUFF_TYPE*/
- rc = cc_render_buff_to_mlli(dev, entry->buffer_dma,
- tot_len, &total_nents,
- &mlli_p);
- if (rc)
- return rc;
-
- /* set last bit in the current table */
- if (sg_data->mlli_nents[i]) {
- /*Calculate the current MLLI table length for the
- *length field in the descriptor
- */
- *sg_data->mlli_nents[i] +=
- (total_nents - prev_total_nents);
- prev_total_nents = total_nents;
- }
- }
-
- /* Set MLLI size for the bypass operation */
- mlli_params->mlli_len = (total_nents * LLI_ENTRY_BYTE_SIZE);
-
- dev_dbg(dev, "MLLI params: virt_addr=%pK dma_addr=%pad mlli_len=0x%X\n",
- mlli_params->mlli_virt_addr, &mlli_params->mlli_dma_addr,
- mlli_params->mlli_len);
-
-build_mlli_exit:
- return rc;
-}
-
-static void cc_add_buffer_entry(struct device *dev,
- struct buffer_array *sgl_data,
- dma_addr_t buffer_dma, unsigned int buffer_len,
- bool is_last_entry, u32 *mlli_nents)
-{
- unsigned int index = sgl_data->num_of_buffers;
-
- dev_dbg(dev, "index=%u single_buff=%pad buffer_len=0x%08X is_last=%d\n",
- index, &buffer_dma, buffer_len, is_last_entry);
- sgl_data->nents[index] = 1;
- sgl_data->entry[index].buffer_dma = buffer_dma;
- sgl_data->offset[index] = 0;
- sgl_data->total_data_len[index] = buffer_len;
- sgl_data->type[index] = DMA_BUFF_TYPE;
- sgl_data->is_last[index] = is_last_entry;
- sgl_data->mlli_nents[index] = mlli_nents;
- if (sgl_data->mlli_nents[index])
- *sgl_data->mlli_nents[index] = 0;
- sgl_data->num_of_buffers++;
-}
-
-static void cc_add_sg_entry(struct device *dev, struct buffer_array *sgl_data,
- unsigned int nents, struct scatterlist *sgl,
- unsigned int data_len, unsigned int data_offset,
- bool is_last_table, u32 *mlli_nents)
-{
- unsigned int index = sgl_data->num_of_buffers;
-
- dev_dbg(dev, "index=%u nents=%u sgl=%pK data_len=0x%08X is_last=%d\n",
- index, nents, sgl, data_len, is_last_table);
- sgl_data->nents[index] = nents;
- sgl_data->entry[index].sgl = sgl;
- sgl_data->offset[index] = data_offset;
- sgl_data->total_data_len[index] = data_len;
- sgl_data->type[index] = DMA_SGL_TYPE;
- sgl_data->is_last[index] = is_last_table;
- sgl_data->mlli_nents[index] = mlli_nents;
- if (sgl_data->mlli_nents[index])
- *sgl_data->mlli_nents[index] = 0;
- sgl_data->num_of_buffers++;
-}
-
-static int cc_dma_map_sg(struct device *dev, struct scatterlist *sg, u32 nents,
- enum dma_data_direction direction)
-{
- u32 i, j;
- struct scatterlist *l_sg = sg;
-
- for (i = 0; i < nents; i++) {
- if (!l_sg)
- break;
- if (dma_map_sg(dev, l_sg, 1, direction) != 1) {
- dev_err(dev, "dma_map_page() sg buffer failed\n");
- goto err;
- }
- l_sg = sg_next(l_sg);
- }
- return nents;
-
-err:
- /* Restore mapped parts */
- for (j = 0; j < i; j++) {
- if (!sg)
- break;
- dma_unmap_sg(dev, sg, 1, direction);
- sg = sg_next(sg);
- }
- return 0;
-}
-
-static int cc_map_sg(struct device *dev, struct scatterlist *sg,
- unsigned int nbytes, int direction, u32 *nents,
- u32 max_sg_nents, u32 *lbytes, u32 *mapped_nents)
-{
- bool is_chained = false;
-
- if (sg_is_last(sg)) {
- /* One entry only case -set to DLLI */
- if (dma_map_sg(dev, sg, 1, direction) != 1) {
- dev_err(dev, "dma_map_sg() single buffer failed\n");
- return -ENOMEM;
- }
- dev_dbg(dev, "Mapped sg: dma_address=%pad page=%p addr=%pK offset=%u length=%u\n",
- &sg_dma_address(sg), sg_page(sg), sg_virt(sg),
- sg->offset, sg->length);
- *lbytes = nbytes;
- *nents = 1;
- *mapped_nents = 1;
- } else { /*sg_is_last*/
- *nents = cc_get_sgl_nents(dev, sg, nbytes, lbytes,
- &is_chained);
- if (*nents > max_sg_nents) {
- *nents = 0;
- dev_err(dev, "Too many fragments. current %d max %d\n",
- *nents, max_sg_nents);
- return -ENOMEM;
- }
- if (!is_chained) {
- /* In case of mmu the number of mapped nents might
- * be changed from the original sgl nents
- */
- *mapped_nents = dma_map_sg(dev, sg, *nents, direction);
- if (*mapped_nents == 0) {
- *nents = 0;
- dev_err(dev, "dma_map_sg() sg buffer failed\n");
- return -ENOMEM;
- }
- } else {
- /*In this case the driver maps entry by entry so it
- * must have the same nents before and after map
- */
- *mapped_nents = cc_dma_map_sg(dev, sg, *nents,
- direction);
- if (*mapped_nents != *nents) {
- *nents = *mapped_nents;
- dev_err(dev, "dma_map_sg() sg buffer failed\n");
- return -ENOMEM;
- }
- }
- }
-
- return 0;
-}
-
-static int
-cc_set_aead_conf_buf(struct device *dev, struct aead_req_ctx *areq_ctx,
- u8 *config_data, struct buffer_array *sg_data,
- unsigned int assoclen)
-{
- dev_dbg(dev, " handle additional data config set to DLLI\n");
- /* create sg for the current buffer */
- sg_init_one(&areq_ctx->ccm_adata_sg, config_data,
- AES_BLOCK_SIZE + areq_ctx->ccm_hdr_size);
- if (dma_map_sg(dev, &areq_ctx->ccm_adata_sg, 1, DMA_TO_DEVICE) != 1) {
- dev_err(dev, "dma_map_sg() config buffer failed\n");
- return -ENOMEM;
- }
- dev_dbg(dev, "Mapped curr_buff: dma_address=%pad page=%p addr=%pK offset=%u length=%u\n",
- &sg_dma_address(&areq_ctx->ccm_adata_sg),
- sg_page(&areq_ctx->ccm_adata_sg),
- sg_virt(&areq_ctx->ccm_adata_sg),
- areq_ctx->ccm_adata_sg.offset, areq_ctx->ccm_adata_sg.length);
- /* prepare for case of MLLI */
- if (assoclen > 0) {
- cc_add_sg_entry(dev, sg_data, 1, &areq_ctx->ccm_adata_sg,
- (AES_BLOCK_SIZE + areq_ctx->ccm_hdr_size),
- 0, false, NULL);
- }
- return 0;
-}
-
-static int cc_set_hash_buf(struct device *dev, struct ahash_req_ctx *areq_ctx,
- u8 *curr_buff, u32 curr_buff_cnt,
- struct buffer_array *sg_data)
-{
- dev_dbg(dev, " handle curr buff %x set to DLLI\n", curr_buff_cnt);
- /* create sg for the current buffer */
- sg_init_one(areq_ctx->buff_sg, curr_buff, curr_buff_cnt);
- if (dma_map_sg(dev, areq_ctx->buff_sg, 1, DMA_TO_DEVICE) != 1) {
- dev_err(dev, "dma_map_sg() src buffer failed\n");
- return -ENOMEM;
- }
- dev_dbg(dev, "Mapped curr_buff: dma_address=%pad page=%p addr=%pK offset=%u length=%u\n",
- &sg_dma_address(areq_ctx->buff_sg), sg_page(areq_ctx->buff_sg),
- sg_virt(areq_ctx->buff_sg), areq_ctx->buff_sg->offset,
- areq_ctx->buff_sg->length);
- areq_ctx->data_dma_buf_type = CC_DMA_BUF_DLLI;
- areq_ctx->curr_sg = areq_ctx->buff_sg;
- areq_ctx->in_nents = 0;
- /* prepare for case of MLLI */
- cc_add_sg_entry(dev, sg_data, 1, areq_ctx->buff_sg, curr_buff_cnt, 0,
- false, NULL);
- return 0;
-}
-
-void cc_unmap_blkcipher_request(struct device *dev, void *ctx,
- unsigned int ivsize, struct scatterlist *src,
- struct scatterlist *dst)
-{
- struct blkcipher_req_ctx *req_ctx = (struct blkcipher_req_ctx *)ctx;
-
- if (req_ctx->gen_ctx.iv_dma_addr) {
- dev_dbg(dev, "Unmapped iv: iv_dma_addr=%pad iv_size=%u\n",
- &req_ctx->gen_ctx.iv_dma_addr, ivsize);
- dma_unmap_single(dev, req_ctx->gen_ctx.iv_dma_addr,
- ivsize,
- req_ctx->is_giv ? DMA_BIDIRECTIONAL :
- DMA_TO_DEVICE);
- }
- /* Release pool */
- if (req_ctx->dma_buf_type == CC_DMA_BUF_MLLI &&
- req_ctx->mlli_params.mlli_virt_addr) {
- dma_pool_free(req_ctx->mlli_params.curr_pool,
- req_ctx->mlli_params.mlli_virt_addr,
- req_ctx->mlli_params.mlli_dma_addr);
- }
-
- dma_unmap_sg(dev, src, req_ctx->in_nents, DMA_BIDIRECTIONAL);
- dev_dbg(dev, "Unmapped req->src=%pK\n", sg_virt(src));
-
- if (src != dst) {
- dma_unmap_sg(dev, dst, req_ctx->out_nents, DMA_BIDIRECTIONAL);
- dev_dbg(dev, "Unmapped req->dst=%pK\n", sg_virt(dst));
- }
-}
-
-int cc_map_blkcipher_request(struct cc_drvdata *drvdata, void *ctx,
- unsigned int ivsize, unsigned int nbytes,
- void *info, struct scatterlist *src,
- struct scatterlist *dst, gfp_t flags)
-{
- struct blkcipher_req_ctx *req_ctx = (struct blkcipher_req_ctx *)ctx;
- struct mlli_params *mlli_params = &req_ctx->mlli_params;
- struct buff_mgr_handle *buff_mgr = drvdata->buff_mgr_handle;
- struct device *dev = drvdata_to_dev(drvdata);
- struct buffer_array sg_data;
- u32 dummy = 0;
- int rc = 0;
- u32 mapped_nents = 0;
-
- req_ctx->dma_buf_type = CC_DMA_BUF_DLLI;
- mlli_params->curr_pool = NULL;
- sg_data.num_of_buffers = 0;
-
- /* Map IV buffer */
- if (ivsize) {
- dump_byte_array("iv", (u8 *)info, ivsize);
- req_ctx->gen_ctx.iv_dma_addr =
- dma_map_single(dev, (void *)info,
- ivsize,
- req_ctx->is_giv ? DMA_BIDIRECTIONAL :
- DMA_TO_DEVICE);
- if (dma_mapping_error(dev, req_ctx->gen_ctx.iv_dma_addr)) {
- dev_err(dev, "Mapping iv %u B at va=%pK for DMA failed\n",
- ivsize, info);
- return -ENOMEM;
- }
- dev_dbg(dev, "Mapped iv %u B at va=%pK to dma=%pad\n",
- ivsize, info, &req_ctx->gen_ctx.iv_dma_addr);
- } else {
- req_ctx->gen_ctx.iv_dma_addr = 0;
- }
-
- /* Map the src SGL */
- rc = cc_map_sg(dev, src, nbytes, DMA_BIDIRECTIONAL, &req_ctx->in_nents,
- LLI_MAX_NUM_OF_DATA_ENTRIES, &dummy, &mapped_nents);
- if (rc) {
- rc = -ENOMEM;
- goto ablkcipher_exit;
- }
- if (mapped_nents > 1)
- req_ctx->dma_buf_type = CC_DMA_BUF_MLLI;
-
- if (src == dst) {
- /* Handle inplace operation */
- if (req_ctx->dma_buf_type == CC_DMA_BUF_MLLI) {
- req_ctx->out_nents = 0;
- cc_add_sg_entry(dev, &sg_data, req_ctx->in_nents, src,
- nbytes, 0, true,
- &req_ctx->in_mlli_nents);
- }
- } else {
- /* Map the dst sg */
- if (cc_map_sg(dev, dst, nbytes, DMA_BIDIRECTIONAL,
- &req_ctx->out_nents, LLI_MAX_NUM_OF_DATA_ENTRIES,
- &dummy, &mapped_nents)) {
- rc = -ENOMEM;
- goto ablkcipher_exit;
- }
- if (mapped_nents > 1)
- req_ctx->dma_buf_type = CC_DMA_BUF_MLLI;
-
- if (req_ctx->dma_buf_type == CC_DMA_BUF_MLLI) {
- cc_add_sg_entry(dev, &sg_data, req_ctx->in_nents, src,
- nbytes, 0, true,
- &req_ctx->in_mlli_nents);
- cc_add_sg_entry(dev, &sg_data, req_ctx->out_nents, dst,
- nbytes, 0, true,
- &req_ctx->out_mlli_nents);
- }
- }
-
- if (req_ctx->dma_buf_type == CC_DMA_BUF_MLLI) {
- mlli_params->curr_pool = buff_mgr->mlli_buffs_pool;
- rc = cc_generate_mlli(dev, &sg_data, mlli_params, flags);
- if (rc)
- goto ablkcipher_exit;
- }
-
- dev_dbg(dev, "areq_ctx->dma_buf_type = %s\n",
- cc_dma_buf_type(req_ctx->dma_buf_type));
-
- return 0;
-
-ablkcipher_exit:
- cc_unmap_blkcipher_request(dev, req_ctx, ivsize, src, dst);
- return rc;
-}
-
-void cc_unmap_aead_request(struct device *dev, struct aead_request *req)
-{
- struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
- unsigned int hw_iv_size = areq_ctx->hw_iv_size;
- struct crypto_aead *tfm = crypto_aead_reqtfm(req);
- struct cc_drvdata *drvdata = dev_get_drvdata(dev);
- u32 dummy;
- bool chained;
- u32 size_to_unmap = 0;
-
- if (areq_ctx->mac_buf_dma_addr) {
- dma_unmap_single(dev, areq_ctx->mac_buf_dma_addr,
- MAX_MAC_SIZE, DMA_BIDIRECTIONAL);
- }
-
- if (areq_ctx->cipher_mode == DRV_CIPHER_GCTR) {
- if (areq_ctx->hkey_dma_addr) {
- dma_unmap_single(dev, areq_ctx->hkey_dma_addr,
- AES_BLOCK_SIZE, DMA_BIDIRECTIONAL);
- }
-
- if (areq_ctx->gcm_block_len_dma_addr) {
- dma_unmap_single(dev, areq_ctx->gcm_block_len_dma_addr,
- AES_BLOCK_SIZE, DMA_TO_DEVICE);
- }
-
- if (areq_ctx->gcm_iv_inc1_dma_addr) {
- dma_unmap_single(dev, areq_ctx->gcm_iv_inc1_dma_addr,
- AES_BLOCK_SIZE, DMA_TO_DEVICE);
- }
-
- if (areq_ctx->gcm_iv_inc2_dma_addr) {
- dma_unmap_single(dev, areq_ctx->gcm_iv_inc2_dma_addr,
- AES_BLOCK_SIZE, DMA_TO_DEVICE);
- }
- }
-
- if (areq_ctx->ccm_hdr_size != ccm_header_size_null) {
- if (areq_ctx->ccm_iv0_dma_addr) {
- dma_unmap_single(dev, areq_ctx->ccm_iv0_dma_addr,
- AES_BLOCK_SIZE, DMA_TO_DEVICE);
- }
-
- dma_unmap_sg(dev, &areq_ctx->ccm_adata_sg, 1, DMA_TO_DEVICE);
- }
- if (areq_ctx->gen_ctx.iv_dma_addr) {
- dma_unmap_single(dev, areq_ctx->gen_ctx.iv_dma_addr,
- hw_iv_size, DMA_BIDIRECTIONAL);
- }
-
- /*In case a pool was set, a table was
- *allocated and should be released
- */
- if (areq_ctx->mlli_params.curr_pool) {
- dev_dbg(dev, "free MLLI buffer: dma=%pad virt=%pK\n",
- &areq_ctx->mlli_params.mlli_dma_addr,
- areq_ctx->mlli_params.mlli_virt_addr);
- dma_pool_free(areq_ctx->mlli_params.curr_pool,
- areq_ctx->mlli_params.mlli_virt_addr,
- areq_ctx->mlli_params.mlli_dma_addr);
- }
-
- dev_dbg(dev, "Unmapping src sgl: req->src=%pK areq_ctx->src.nents=%u areq_ctx->assoc.nents=%u assoclen:%u cryptlen=%u\n",
- sg_virt(req->src), areq_ctx->src.nents, areq_ctx->assoc.nents,
- req->assoclen, req->cryptlen);
- size_to_unmap = req->assoclen + req->cryptlen;
- if (areq_ctx->gen_ctx.op_type == DRV_CRYPTO_DIRECTION_ENCRYPT)
- size_to_unmap += areq_ctx->req_authsize;
- if (areq_ctx->is_gcm4543)
- size_to_unmap += crypto_aead_ivsize(tfm);
-
- dma_unmap_sg(dev, req->src,
- cc_get_sgl_nents(dev, req->src, size_to_unmap,
- &dummy, &chained),
- DMA_BIDIRECTIONAL);
- if (req->src != req->dst) {
- dev_dbg(dev, "Unmapping dst sgl: req->dst=%pK\n",
- sg_virt(req->dst));
- dma_unmap_sg(dev, req->dst,
- cc_get_sgl_nents(dev, req->dst, size_to_unmap,
- &dummy, &chained),
- DMA_BIDIRECTIONAL);
- }
- if (drvdata->coherent &&
- areq_ctx->gen_ctx.op_type == DRV_CRYPTO_DIRECTION_DECRYPT &&
- req->src == req->dst) {
- /* copy back mac from temporary location to deal with possible
- * data memory overriding that caused by cache coherence
- * problem.
- */
- cc_copy_mac(dev, req, CC_SG_FROM_BUF);
- }
-}
-
-static int cc_get_aead_icv_nents(struct device *dev, struct scatterlist *sgl,
- unsigned int sgl_nents, unsigned int authsize,
- u32 last_entry_data_size,
- bool *is_icv_fragmented)
-{
- unsigned int icv_max_size = 0;
- unsigned int icv_required_size = authsize > last_entry_data_size ?
- (authsize - last_entry_data_size) :
- authsize;
- unsigned int nents;
- unsigned int i;
-
- if (sgl_nents < MAX_ICV_NENTS_SUPPORTED) {
- *is_icv_fragmented = false;
- return 0;
- }
-
- for (i = 0 ; i < (sgl_nents - MAX_ICV_NENTS_SUPPORTED) ; i++) {
- if (!sgl)
- break;
- sgl = sg_next(sgl);
- }
-
- if (sgl)
- icv_max_size = sgl->length;
-
- if (last_entry_data_size > authsize) {
- /* ICV attached to data in last entry (not fragmented!) */
- nents = 0;
- *is_icv_fragmented = false;
- } else if (last_entry_data_size == authsize) {
- /* ICV placed in whole last entry (not fragmented!) */
- nents = 1;
- *is_icv_fragmented = false;
- } else if (icv_max_size > icv_required_size) {
- nents = 1;
- *is_icv_fragmented = true;
- } else if (icv_max_size == icv_required_size) {
- nents = 2;
- *is_icv_fragmented = true;
- } else {
- dev_err(dev, "Unsupported num. of ICV fragments (> %d)\n",
- MAX_ICV_NENTS_SUPPORTED);
- nents = -1; /*unsupported*/
- }
- dev_dbg(dev, "is_frag=%s icv_nents=%u\n",
- (*is_icv_fragmented ? "true" : "false"), nents);
-
- return nents;
-}
-
-static int cc_aead_chain_iv(struct cc_drvdata *drvdata,
- struct aead_request *req,
- struct buffer_array *sg_data,
- bool is_last, bool do_chain)
-{
- struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
- unsigned int hw_iv_size = areq_ctx->hw_iv_size;
- struct device *dev = drvdata_to_dev(drvdata);
- int rc = 0;
-
- if (!req->iv) {
- areq_ctx->gen_ctx.iv_dma_addr = 0;
- goto chain_iv_exit;
- }
-
- areq_ctx->gen_ctx.iv_dma_addr = dma_map_single(dev, req->iv,
- hw_iv_size,
- DMA_BIDIRECTIONAL);
- if (dma_mapping_error(dev, areq_ctx->gen_ctx.iv_dma_addr)) {
- dev_err(dev, "Mapping iv %u B at va=%pK for DMA failed\n",
- hw_iv_size, req->iv);
- rc = -ENOMEM;
- goto chain_iv_exit;
- }
-
- dev_dbg(dev, "Mapped iv %u B at va=%pK to dma=%pad\n",
- hw_iv_size, req->iv, &areq_ctx->gen_ctx.iv_dma_addr);
- // TODO: what about CTR?? ask Ron
- if (do_chain && areq_ctx->plaintext_authenticate_only) {
- struct crypto_aead *tfm = crypto_aead_reqtfm(req);
- unsigned int iv_size_to_authenc = crypto_aead_ivsize(tfm);
- unsigned int iv_ofs = GCM_BLOCK_RFC4_IV_OFFSET;
- /* Chain to given list */
- cc_add_buffer_entry(dev, sg_data,
- (areq_ctx->gen_ctx.iv_dma_addr + iv_ofs),
- iv_size_to_authenc, is_last,
- &areq_ctx->assoc.mlli_nents);
- areq_ctx->assoc_buff_type = CC_DMA_BUF_MLLI;
- }
-
-chain_iv_exit:
- return rc;
-}
-
-static int cc_aead_chain_assoc(struct cc_drvdata *drvdata,
- struct aead_request *req,
- struct buffer_array *sg_data,
- bool is_last, bool do_chain)
-{
- struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
- int rc = 0;
- u32 mapped_nents = 0;
- struct scatterlist *current_sg = req->src;
- struct crypto_aead *tfm = crypto_aead_reqtfm(req);
- unsigned int sg_index = 0;
- u32 size_of_assoc = req->assoclen;
- struct device *dev = drvdata_to_dev(drvdata);
-
- if (areq_ctx->is_gcm4543)
- size_of_assoc += crypto_aead_ivsize(tfm);
-
- if (!sg_data) {
- rc = -EINVAL;
- goto chain_assoc_exit;
- }
-
- if (req->assoclen == 0) {
- areq_ctx->assoc_buff_type = CC_DMA_BUF_NULL;
- areq_ctx->assoc.nents = 0;
- areq_ctx->assoc.mlli_nents = 0;
- dev_dbg(dev, "Chain assoc of length 0: buff_type=%s nents=%u\n",
- cc_dma_buf_type(areq_ctx->assoc_buff_type),
- areq_ctx->assoc.nents);
- goto chain_assoc_exit;
- }
-
- //iterate over the sgl to see how many entries are for associated data
- //it is assumed that if we reach here , the sgl is already mapped
- sg_index = current_sg->length;
- //the first entry in the scatter list contains all the associated data
- if (sg_index > size_of_assoc) {
- mapped_nents++;
- } else {
- while (sg_index <= size_of_assoc) {
- current_sg = sg_next(current_sg);
- /* if have reached the end of the sgl, then this is
- * unexpected
- */
- if (!current_sg) {
- dev_err(dev, "reached end of sg list. unexpected\n");
- return -EINVAL;
- }
- sg_index += current_sg->length;
- mapped_nents++;
- }
- }
- if (mapped_nents > LLI_MAX_NUM_OF_ASSOC_DATA_ENTRIES) {
- dev_err(dev, "Too many fragments. current %d max %d\n",
- mapped_nents, LLI_MAX_NUM_OF_ASSOC_DATA_ENTRIES);
- return -ENOMEM;
- }
- areq_ctx->assoc.nents = mapped_nents;
-
- /* in CCM case we have additional entry for
- * ccm header configurations
- */
- if (areq_ctx->ccm_hdr_size != ccm_header_size_null) {
- if ((mapped_nents + 1) > LLI_MAX_NUM_OF_ASSOC_DATA_ENTRIES) {
- dev_err(dev, "CCM case.Too many fragments. Current %d max %d\n",
- (areq_ctx->assoc.nents + 1),
- LLI_MAX_NUM_OF_ASSOC_DATA_ENTRIES);
- rc = -ENOMEM;
- goto chain_assoc_exit;
- }
- }
-
- if (mapped_nents == 1 && areq_ctx->ccm_hdr_size == ccm_header_size_null)
- areq_ctx->assoc_buff_type = CC_DMA_BUF_DLLI;
- else
- areq_ctx->assoc_buff_type = CC_DMA_BUF_MLLI;
-
- if (do_chain || areq_ctx->assoc_buff_type == CC_DMA_BUF_MLLI) {
- dev_dbg(dev, "Chain assoc: buff_type=%s nents=%u\n",
- cc_dma_buf_type(areq_ctx->assoc_buff_type),
- areq_ctx->assoc.nents);
- cc_add_sg_entry(dev, sg_data, areq_ctx->assoc.nents, req->src,
- req->assoclen, 0, is_last,
- &areq_ctx->assoc.mlli_nents);
- areq_ctx->assoc_buff_type = CC_DMA_BUF_MLLI;
- }
-
-chain_assoc_exit:
- return rc;
-}
-
-static void cc_prepare_aead_data_dlli(struct aead_request *req,
- u32 *src_last_bytes, u32 *dst_last_bytes)
-{
- struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
- enum drv_crypto_direction direct = areq_ctx->gen_ctx.op_type;
- unsigned int authsize = areq_ctx->req_authsize;
-
- areq_ctx->is_icv_fragmented = false;
- if (req->src == req->dst) {
- /*INPLACE*/
- areq_ctx->icv_dma_addr = sg_dma_address(areq_ctx->src_sgl) +
- (*src_last_bytes - authsize);
- areq_ctx->icv_virt_addr = sg_virt(areq_ctx->src_sgl) +
- (*src_last_bytes - authsize);
- } else if (direct == DRV_CRYPTO_DIRECTION_DECRYPT) {
- /*NON-INPLACE and DECRYPT*/
- areq_ctx->icv_dma_addr = sg_dma_address(areq_ctx->src_sgl) +
- (*src_last_bytes - authsize);
- areq_ctx->icv_virt_addr = sg_virt(areq_ctx->src_sgl) +
- (*src_last_bytes - authsize);
- } else {
- /*NON-INPLACE and ENCRYPT*/
- areq_ctx->icv_dma_addr = sg_dma_address(areq_ctx->dst_sgl) +
- (*dst_last_bytes - authsize);
- areq_ctx->icv_virt_addr = sg_virt(areq_ctx->dst_sgl) +
- (*dst_last_bytes - authsize);
- }
-}
-
-static int cc_prepare_aead_data_mlli(struct cc_drvdata *drvdata,
- struct aead_request *req,
- struct buffer_array *sg_data,
- u32 *src_last_bytes, u32 *dst_last_bytes,
- bool is_last_table)
-{
- struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
- enum drv_crypto_direction direct = areq_ctx->gen_ctx.op_type;
- unsigned int authsize = areq_ctx->req_authsize;
- int rc = 0, icv_nents;
- struct device *dev = drvdata_to_dev(drvdata);
- struct scatterlist *sg;
-
- if (req->src == req->dst) {
- /*INPLACE*/
- cc_add_sg_entry(dev, sg_data, areq_ctx->src.nents,
- areq_ctx->src_sgl, areq_ctx->cryptlen,
- areq_ctx->src_offset, is_last_table,
- &areq_ctx->src.mlli_nents);
-
- icv_nents = cc_get_aead_icv_nents(dev, areq_ctx->src_sgl,
- areq_ctx->src.nents,
- authsize, *src_last_bytes,
- &areq_ctx->is_icv_fragmented);
- if (icv_nents < 0) {
- rc = -ENOTSUPP;
- goto prepare_data_mlli_exit;
- }
-
- if (areq_ctx->is_icv_fragmented) {
- /* Backup happens only when ICV is fragmented, ICV
- * verification is made by CPU compare in order to
- * simplify MAC verification upon request completion
- */
- if (direct == DRV_CRYPTO_DIRECTION_DECRYPT) {
- /* In coherent platforms (e.g. ACP)
- * already copying ICV for any
- * INPLACE-DECRYPT operation, hence
- * we must neglect this code.
- */
- if (!drvdata->coherent)
- cc_copy_mac(dev, req, CC_SG_TO_BUF);
-
- areq_ctx->icv_virt_addr = areq_ctx->backup_mac;
- } else {
- areq_ctx->icv_virt_addr = areq_ctx->mac_buf;
- areq_ctx->icv_dma_addr =
- areq_ctx->mac_buf_dma_addr;
- }
- } else { /* Contig. ICV */
- sg = &areq_ctx->src_sgl[areq_ctx->src.nents - 1];
- /*Should hanlde if the sg is not contig.*/
- areq_ctx->icv_dma_addr = sg_dma_address(sg) +
- (*src_last_bytes - authsize);
- areq_ctx->icv_virt_addr = sg_virt(sg) +
- (*src_last_bytes - authsize);
- }
-
- } else if (direct == DRV_CRYPTO_DIRECTION_DECRYPT) {
- /*NON-INPLACE and DECRYPT*/
- cc_add_sg_entry(dev, sg_data, areq_ctx->src.nents,
- areq_ctx->src_sgl, areq_ctx->cryptlen,
- areq_ctx->src_offset, is_last_table,
- &areq_ctx->src.mlli_nents);
- cc_add_sg_entry(dev, sg_data, areq_ctx->dst.nents,
- areq_ctx->dst_sgl, areq_ctx->cryptlen,
- areq_ctx->dst_offset, is_last_table,
- &areq_ctx->dst.mlli_nents);
-
- icv_nents = cc_get_aead_icv_nents(dev, areq_ctx->src_sgl,
- areq_ctx->src.nents,
- authsize, *src_last_bytes,
- &areq_ctx->is_icv_fragmented);
- if (icv_nents < 0) {
- rc = -ENOTSUPP;
- goto prepare_data_mlli_exit;
- }
-
- /* Backup happens only when ICV is fragmented, ICV
- * verification is made by CPU compare in order to simplify
- * MAC verification upon request completion
- */
- if (areq_ctx->is_icv_fragmented) {
- cc_copy_mac(dev, req, CC_SG_TO_BUF);
- areq_ctx->icv_virt_addr = areq_ctx->backup_mac;
-
- } else { /* Contig. ICV */
- sg = &areq_ctx->src_sgl[areq_ctx->src.nents - 1];
- /*Should hanlde if the sg is not contig.*/
- areq_ctx->icv_dma_addr = sg_dma_address(sg) +
- (*src_last_bytes - authsize);
- areq_ctx->icv_virt_addr = sg_virt(sg) +
- (*src_last_bytes - authsize);
- }
-
- } else {
- /*NON-INPLACE and ENCRYPT*/
- cc_add_sg_entry(dev, sg_data, areq_ctx->dst.nents,
- areq_ctx->dst_sgl, areq_ctx->cryptlen,
- areq_ctx->dst_offset, is_last_table,
- &areq_ctx->dst.mlli_nents);
- cc_add_sg_entry(dev, sg_data, areq_ctx->src.nents,
- areq_ctx->src_sgl, areq_ctx->cryptlen,
- areq_ctx->src_offset, is_last_table,
- &areq_ctx->src.mlli_nents);
-
- icv_nents = cc_get_aead_icv_nents(dev, areq_ctx->dst_sgl,
- areq_ctx->dst.nents,
- authsize, *dst_last_bytes,
- &areq_ctx->is_icv_fragmented);
- if (icv_nents < 0) {
- rc = -ENOTSUPP;
- goto prepare_data_mlli_exit;
- }
-
- if (!areq_ctx->is_icv_fragmented) {
- sg = &areq_ctx->dst_sgl[areq_ctx->dst.nents - 1];
- /* Contig. ICV */
- areq_ctx->icv_dma_addr = sg_dma_address(sg) +
- (*dst_last_bytes - authsize);
- areq_ctx->icv_virt_addr = sg_virt(sg) +
- (*dst_last_bytes - authsize);
- } else {
- areq_ctx->icv_dma_addr = areq_ctx->mac_buf_dma_addr;
- areq_ctx->icv_virt_addr = areq_ctx->mac_buf;
- }
- }
-
-prepare_data_mlli_exit:
- return rc;
-}
-
-static int cc_aead_chain_data(struct cc_drvdata *drvdata,
- struct aead_request *req,
- struct buffer_array *sg_data,
- bool is_last_table, bool do_chain)
-{
- struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
- struct device *dev = drvdata_to_dev(drvdata);
- enum drv_crypto_direction direct = areq_ctx->gen_ctx.op_type;
- unsigned int authsize = areq_ctx->req_authsize;
- int src_last_bytes = 0, dst_last_bytes = 0;
- int rc = 0;
- u32 src_mapped_nents = 0, dst_mapped_nents = 0;
- u32 offset = 0;
- /* non-inplace mode */
- unsigned int size_for_map = req->assoclen + req->cryptlen;
- struct crypto_aead *tfm = crypto_aead_reqtfm(req);
- u32 sg_index = 0;
- bool chained = false;
- bool is_gcm4543 = areq_ctx->is_gcm4543;
- u32 size_to_skip = req->assoclen;
-
- if (is_gcm4543)
- size_to_skip += crypto_aead_ivsize(tfm);
-
- offset = size_to_skip;
-
- if (!sg_data)
- return -EINVAL;
-
- areq_ctx->src_sgl = req->src;
- areq_ctx->dst_sgl = req->dst;
-
- if (is_gcm4543)
- size_for_map += crypto_aead_ivsize(tfm);
-
- size_for_map += (direct == DRV_CRYPTO_DIRECTION_ENCRYPT) ?
- authsize : 0;
- src_mapped_nents = cc_get_sgl_nents(dev, req->src, size_for_map,
- &src_last_bytes, &chained);
- sg_index = areq_ctx->src_sgl->length;
- //check where the data starts
- while (sg_index <= size_to_skip) {
- offset -= areq_ctx->src_sgl->length;
- areq_ctx->src_sgl = sg_next(areq_ctx->src_sgl);
- //if have reached the end of the sgl, then this is unexpected
- if (!areq_ctx->src_sgl) {
- dev_err(dev, "reached end of sg list. unexpected\n");
- return -EINVAL;
- }
- sg_index += areq_ctx->src_sgl->length;
- src_mapped_nents--;
- }
- if (src_mapped_nents > LLI_MAX_NUM_OF_DATA_ENTRIES) {
- dev_err(dev, "Too many fragments. current %d max %d\n",
- src_mapped_nents, LLI_MAX_NUM_OF_DATA_ENTRIES);
- return -ENOMEM;
- }
-
- areq_ctx->src.nents = src_mapped_nents;
-
- areq_ctx->src_offset = offset;
-
- if (req->src != req->dst) {
- size_for_map = req->assoclen + req->cryptlen;
- size_for_map += (direct == DRV_CRYPTO_DIRECTION_ENCRYPT) ?
- authsize : 0;
- if (is_gcm4543)
- size_for_map += crypto_aead_ivsize(tfm);
-
- rc = cc_map_sg(dev, req->dst, size_for_map, DMA_BIDIRECTIONAL,
- &areq_ctx->dst.nents,
- LLI_MAX_NUM_OF_DATA_ENTRIES, &dst_last_bytes,
- &dst_mapped_nents);
- if (rc) {
- rc = -ENOMEM;
- goto chain_data_exit;
- }
- }
-
- dst_mapped_nents = cc_get_sgl_nents(dev, req->dst, size_for_map,
- &dst_last_bytes, &chained);
- sg_index = areq_ctx->dst_sgl->length;
- offset = size_to_skip;
-
- //check where the data starts
- while (sg_index <= size_to_skip) {
- offset -= areq_ctx->dst_sgl->length;
- areq_ctx->dst_sgl = sg_next(areq_ctx->dst_sgl);
- //if have reached the end of the sgl, then this is unexpected
- if (!areq_ctx->dst_sgl) {
- dev_err(dev, "reached end of sg list. unexpected\n");
- return -EINVAL;
- }
- sg_index += areq_ctx->dst_sgl->length;
- dst_mapped_nents--;
- }
- if (dst_mapped_nents > LLI_MAX_NUM_OF_DATA_ENTRIES) {
- dev_err(dev, "Too many fragments. current %d max %d\n",
- dst_mapped_nents, LLI_MAX_NUM_OF_DATA_ENTRIES);
- return -ENOMEM;
- }
- areq_ctx->dst.nents = dst_mapped_nents;
- areq_ctx->dst_offset = offset;
- if (src_mapped_nents > 1 ||
- dst_mapped_nents > 1 ||
- do_chain) {
- areq_ctx->data_buff_type = CC_DMA_BUF_MLLI;
- rc = cc_prepare_aead_data_mlli(drvdata, req, sg_data,
- &src_last_bytes,
- &dst_last_bytes, is_last_table);
- } else {
- areq_ctx->data_buff_type = CC_DMA_BUF_DLLI;
- cc_prepare_aead_data_dlli(req, &src_last_bytes,
- &dst_last_bytes);
- }
-
-chain_data_exit:
- return rc;
-}
-
-static void cc_update_aead_mlli_nents(struct cc_drvdata *drvdata,
- struct aead_request *req)
-{
- struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
- u32 curr_mlli_size = 0;
-
- if (areq_ctx->assoc_buff_type == CC_DMA_BUF_MLLI) {
- areq_ctx->assoc.sram_addr = drvdata->mlli_sram_addr;
- curr_mlli_size = areq_ctx->assoc.mlli_nents *
- LLI_ENTRY_BYTE_SIZE;
- }
-
- if (areq_ctx->data_buff_type == CC_DMA_BUF_MLLI) {
- /*Inplace case dst nents equal to src nents*/
- if (req->src == req->dst) {
- areq_ctx->dst.mlli_nents = areq_ctx->src.mlli_nents;
- areq_ctx->src.sram_addr = drvdata->mlli_sram_addr +
- curr_mlli_size;
- areq_ctx->dst.sram_addr = areq_ctx->src.sram_addr;
- if (!areq_ctx->is_single_pass)
- areq_ctx->assoc.mlli_nents +=
- areq_ctx->src.mlli_nents;
- } else {
- if (areq_ctx->gen_ctx.op_type ==
- DRV_CRYPTO_DIRECTION_DECRYPT) {
- areq_ctx->src.sram_addr =
- drvdata->mlli_sram_addr +
- curr_mlli_size;
- areq_ctx->dst.sram_addr =
- areq_ctx->src.sram_addr +
- areq_ctx->src.mlli_nents *
- LLI_ENTRY_BYTE_SIZE;
- if (!areq_ctx->is_single_pass)
- areq_ctx->assoc.mlli_nents +=
- areq_ctx->src.mlli_nents;
- } else {
- areq_ctx->dst.sram_addr =
- drvdata->mlli_sram_addr +
- curr_mlli_size;
- areq_ctx->src.sram_addr =
- areq_ctx->dst.sram_addr +
- areq_ctx->dst.mlli_nents *
- LLI_ENTRY_BYTE_SIZE;
- if (!areq_ctx->is_single_pass)
- areq_ctx->assoc.mlli_nents +=
- areq_ctx->dst.mlli_nents;
- }
- }
- }
-}
-
-int cc_map_aead_request(struct cc_drvdata *drvdata, struct aead_request *req)
-{
- struct aead_req_ctx *areq_ctx = aead_request_ctx(req);
- struct mlli_params *mlli_params = &areq_ctx->mlli_params;
- struct device *dev = drvdata_to_dev(drvdata);
- struct buffer_array sg_data;
- unsigned int authsize = areq_ctx->req_authsize;
- struct buff_mgr_handle *buff_mgr = drvdata->buff_mgr_handle;
- int rc = 0;
- struct crypto_aead *tfm = crypto_aead_reqtfm(req);
- bool is_gcm4543 = areq_ctx->is_gcm4543;
- dma_addr_t dma_addr;
- u32 mapped_nents = 0;
- u32 dummy = 0; /*used for the assoc data fragments */
- u32 size_to_map = 0;
- gfp_t flags = cc_gfp_flags(&req->base);
-
- mlli_params->curr_pool = NULL;
- sg_data.num_of_buffers = 0;
-
- /* copy mac to a temporary location to deal with possible
- * data memory overriding that caused by cache coherence problem.
- */
- if (drvdata->coherent &&
- areq_ctx->gen_ctx.op_type == DRV_CRYPTO_DIRECTION_DECRYPT &&
- req->src == req->dst)
- cc_copy_mac(dev, req, CC_SG_TO_BUF);
-
- /* cacluate the size for cipher remove ICV in decrypt*/
- areq_ctx->cryptlen = (areq_ctx->gen_ctx.op_type ==
- DRV_CRYPTO_DIRECTION_ENCRYPT) ?
- req->cryptlen :
- (req->cryptlen - authsize);
-
- dma_addr = dma_map_single(dev, areq_ctx->mac_buf, MAX_MAC_SIZE,
- DMA_BIDIRECTIONAL);
- if (dma_mapping_error(dev, dma_addr)) {
- dev_err(dev, "Mapping mac_buf %u B at va=%pK for DMA failed\n",
- MAX_MAC_SIZE, areq_ctx->mac_buf);
- rc = -ENOMEM;
- goto aead_map_failure;
- }
- areq_ctx->mac_buf_dma_addr = dma_addr;
-
- if (areq_ctx->ccm_hdr_size != ccm_header_size_null) {
- void *addr = areq_ctx->ccm_config + CCM_CTR_COUNT_0_OFFSET;
-
- dma_addr = dma_map_single(dev, addr, AES_BLOCK_SIZE,
- DMA_TO_DEVICE);
-
- if (dma_mapping_error(dev, dma_addr)) {
- dev_err(dev, "Mapping mac_buf %u B at va=%pK for DMA failed\n",
- AES_BLOCK_SIZE, addr);
- areq_ctx->ccm_iv0_dma_addr = 0;
- rc = -ENOMEM;
- goto aead_map_failure;
- }
- areq_ctx->ccm_iv0_dma_addr = dma_addr;
-
- if (cc_set_aead_conf_buf(dev, areq_ctx, areq_ctx->ccm_config,
- &sg_data, req->assoclen)) {
- rc = -ENOMEM;
- goto aead_map_failure;
- }
- }
-
- if (areq_ctx->cipher_mode == DRV_CIPHER_GCTR) {
- dma_addr = dma_map_single(dev, areq_ctx->hkey, AES_BLOCK_SIZE,
- DMA_BIDIRECTIONAL);
- if (dma_mapping_error(dev, dma_addr)) {
- dev_err(dev, "Mapping hkey %u B at va=%pK for DMA failed\n",
- AES_BLOCK_SIZE, areq_ctx->hkey);
- rc = -ENOMEM;
- goto aead_map_failure;
- }
- areq_ctx->hkey_dma_addr = dma_addr;
-
- dma_addr = dma_map_single(dev, &areq_ctx->gcm_len_block,
- AES_BLOCK_SIZE, DMA_TO_DEVICE);
- if (dma_mapping_error(dev, dma_addr)) {
- dev_err(dev, "Mapping gcm_len_block %u B at va=%pK for DMA failed\n",
- AES_BLOCK_SIZE, &areq_ctx->gcm_len_block);
- rc = -ENOMEM;
- goto aead_map_failure;
- }
- areq_ctx->gcm_block_len_dma_addr = dma_addr;
-
- dma_addr = dma_map_single(dev, areq_ctx->gcm_iv_inc1,
- AES_BLOCK_SIZE, DMA_TO_DEVICE);
-
- if (dma_mapping_error(dev, dma_addr)) {
- dev_err(dev, "Mapping gcm_iv_inc1 %u B at va=%pK for DMA failed\n",
- AES_BLOCK_SIZE, (areq_ctx->gcm_iv_inc1));
- areq_ctx->gcm_iv_inc1_dma_addr = 0;
- rc = -ENOMEM;
- goto aead_map_failure;
- }
- areq_ctx->gcm_iv_inc1_dma_addr = dma_addr;
-
- dma_addr = dma_map_single(dev, areq_ctx->gcm_iv_inc2,
- AES_BLOCK_SIZE, DMA_TO_DEVICE);
-
- if (dma_mapping_error(dev, dma_addr)) {
- dev_err(dev, "Mapping gcm_iv_inc2 %u B at va=%pK for DMA failed\n",
- AES_BLOCK_SIZE, (areq_ctx->gcm_iv_inc2));
- areq_ctx->gcm_iv_inc2_dma_addr = 0;
- rc = -ENOMEM;
- goto aead_map_failure;
- }
- areq_ctx->gcm_iv_inc2_dma_addr = dma_addr;
- }
-
- size_to_map = req->cryptlen + req->assoclen;
- if (areq_ctx->gen_ctx.op_type == DRV_CRYPTO_DIRECTION_ENCRYPT)
- size_to_map += authsize;
-
- if (is_gcm4543)
- size_to_map += crypto_aead_ivsize(tfm);
- rc = cc_map_sg(dev, req->src, size_to_map, DMA_BIDIRECTIONAL,
- &areq_ctx->src.nents,
- (LLI_MAX_NUM_OF_ASSOC_DATA_ENTRIES +
- LLI_MAX_NUM_OF_DATA_ENTRIES),
- &dummy, &mapped_nents);
- if (rc) {
- rc = -ENOMEM;
- goto aead_map_failure;
- }
-
- if (areq_ctx->is_single_pass) {
- /*
- * Create MLLI table for:
- * (1) Assoc. data
- * (2) Src/Dst SGLs
- * Note: IV is contg. buffer (not an SGL)
- */
- rc = cc_aead_chain_assoc(drvdata, req, &sg_data, true, false);
- if (rc)
- goto aead_map_failure;
- rc = cc_aead_chain_iv(drvdata, req, &sg_data, true, false);
- if (rc)
- goto aead_map_failure;
- rc = cc_aead_chain_data(drvdata, req, &sg_data, true, false);
- if (rc)
- goto aead_map_failure;
- } else { /* DOUBLE-PASS flow */
- /*
- * Prepare MLLI table(s) in this order:
- *
- * If ENCRYPT/DECRYPT (inplace):
- * (1) MLLI table for assoc
- * (2) IV entry (chained right after end of assoc)
- * (3) MLLI for src/dst (inplace operation)
- *
- * If ENCRYPT (non-inplace)
- * (1) MLLI table for assoc
- * (2) IV entry (chained right after end of assoc)
- * (3) MLLI for dst
- * (4) MLLI for src
- *
- * If DECRYPT (non-inplace)
- * (1) MLLI table for assoc
- * (2) IV entry (chained right after end of assoc)
- * (3) MLLI for src
- * (4) MLLI for dst
- */
- rc = cc_aead_chain_assoc(drvdata, req, &sg_data, false, true);
- if (rc)
- goto aead_map_failure;
- rc = cc_aead_chain_iv(drvdata, req, &sg_data, false, true);
- if (rc)
- goto aead_map_failure;
- rc = cc_aead_chain_data(drvdata, req, &sg_data, true, true);
- if (rc)
- goto aead_map_failure;
- }
-
- /* Mlli support -start building the MLLI according to the above
- * results
- */
- if (areq_ctx->assoc_buff_type == CC_DMA_BUF_MLLI ||
- areq_ctx->data_buff_type == CC_DMA_BUF_MLLI) {
- mlli_params->curr_pool = buff_mgr->mlli_buffs_pool;
- rc = cc_generate_mlli(dev, &sg_data, mlli_params, flags);
- if (rc)
- goto aead_map_failure;
-
- cc_update_aead_mlli_nents(drvdata, req);
- dev_dbg(dev, "assoc params mn %d\n",
- areq_ctx->assoc.mlli_nents);
- dev_dbg(dev, "src params mn %d\n", areq_ctx->src.mlli_nents);
- dev_dbg(dev, "dst params mn %d\n", areq_ctx->dst.mlli_nents);
- }
- return 0;
-
-aead_map_failure:
- cc_unmap_aead_request(dev, req);
- return rc;
-}
-
-int cc_map_hash_request_final(struct cc_drvdata *drvdata, void *ctx,
- struct scatterlist *src, unsigned int nbytes,
- bool do_update, gfp_t flags)
-{
- struct ahash_req_ctx *areq_ctx = (struct ahash_req_ctx *)ctx;
- struct device *dev = drvdata_to_dev(drvdata);
- u8 *curr_buff = cc_hash_buf(areq_ctx);
- u32 *curr_buff_cnt = cc_hash_buf_cnt(areq_ctx);
- struct mlli_params *mlli_params = &areq_ctx->mlli_params;
- struct buffer_array sg_data;
- struct buff_mgr_handle *buff_mgr = drvdata->buff_mgr_handle;
- u32 dummy = 0;
- u32 mapped_nents = 0;
-
- dev_dbg(dev, "final params : curr_buff=%pK curr_buff_cnt=0x%X nbytes = 0x%X src=%pK curr_index=%u\n",
- curr_buff, *curr_buff_cnt, nbytes, src, areq_ctx->buff_index);
- /* Init the type of the dma buffer */
- areq_ctx->data_dma_buf_type = CC_DMA_BUF_NULL;
- mlli_params->curr_pool = NULL;
- sg_data.num_of_buffers = 0;
- areq_ctx->in_nents = 0;
-
- if (nbytes == 0 && *curr_buff_cnt == 0) {
- /* nothing to do */
- return 0;
- }
-
- /*TODO: copy data in case that buffer is enough for operation */
- /* map the previous buffer */
- if (*curr_buff_cnt) {
- if (cc_set_hash_buf(dev, areq_ctx, curr_buff, *curr_buff_cnt,
- &sg_data)) {
- return -ENOMEM;
- }
- }
-
- if (src && nbytes > 0 && do_update) {
- if (cc_map_sg(dev, src, nbytes, DMA_TO_DEVICE,
- &areq_ctx->in_nents, LLI_MAX_NUM_OF_DATA_ENTRIES,
- &dummy, &mapped_nents)) {
- goto unmap_curr_buff;
- }
- if (src && mapped_nents == 1 &&
- areq_ctx->data_dma_buf_type == CC_DMA_BUF_NULL) {
- memcpy(areq_ctx->buff_sg, src,
- sizeof(struct scatterlist));
- areq_ctx->buff_sg->length = nbytes;
- areq_ctx->curr_sg = areq_ctx->buff_sg;
- areq_ctx->data_dma_buf_type = CC_DMA_BUF_DLLI;
- } else {
- areq_ctx->data_dma_buf_type = CC_DMA_BUF_MLLI;
- }
- }
-
- /*build mlli */
- if (areq_ctx->data_dma_buf_type == CC_DMA_BUF_MLLI) {
- mlli_params->curr_pool = buff_mgr->mlli_buffs_pool;
- /* add the src data to the sg_data */
- cc_add_sg_entry(dev, &sg_data, areq_ctx->in_nents, src, nbytes,
- 0, true, &areq_ctx->mlli_nents);
- if (cc_generate_mlli(dev, &sg_data, mlli_params, flags))
- goto fail_unmap_din;
- }
- /* change the buffer index for the unmap function */
- areq_ctx->buff_index = (areq_ctx->buff_index ^ 1);
- dev_dbg(dev, "areq_ctx->data_dma_buf_type = %s\n",
- cc_dma_buf_type(areq_ctx->data_dma_buf_type));
- return 0;
-
-fail_unmap_din:
- dma_unmap_sg(dev, src, areq_ctx->in_nents, DMA_TO_DEVICE);
-
-unmap_curr_buff:
- if (*curr_buff_cnt)
- dma_unmap_sg(dev, areq_ctx->buff_sg, 1, DMA_TO_DEVICE);
-
- return -ENOMEM;
-}
-
-int cc_map_hash_request_update(struct cc_drvdata *drvdata, void *ctx,
- struct scatterlist *src, unsigned int nbytes,
- unsigned int block_size, gfp_t flags)
-{
- struct ahash_req_ctx *areq_ctx = (struct ahash_req_ctx *)ctx;
- struct device *dev = drvdata_to_dev(drvdata);
- u8 *curr_buff = cc_hash_buf(areq_ctx);
- u32 *curr_buff_cnt = cc_hash_buf_cnt(areq_ctx);
- u8 *next_buff = cc_next_buf(areq_ctx);
- u32 *next_buff_cnt = cc_next_buf_cnt(areq_ctx);
- struct mlli_params *mlli_params = &areq_ctx->mlli_params;
- unsigned int update_data_len;
- u32 total_in_len = nbytes + *curr_buff_cnt;
- struct buffer_array sg_data;
- struct buff_mgr_handle *buff_mgr = drvdata->buff_mgr_handle;
- unsigned int swap_index = 0;
- u32 dummy = 0;
- u32 mapped_nents = 0;
-
- dev_dbg(dev, " update params : curr_buff=%pK curr_buff_cnt=0x%X nbytes=0x%X src=%pK curr_index=%u\n",
- curr_buff, *curr_buff_cnt, nbytes, src, areq_ctx->buff_index);
- /* Init the type of the dma buffer */
- areq_ctx->data_dma_buf_type = CC_DMA_BUF_NULL;
- mlli_params->curr_pool = NULL;
- areq_ctx->curr_sg = NULL;
- sg_data.num_of_buffers = 0;
- areq_ctx->in_nents = 0;
-
- if (total_in_len < block_size) {
- dev_dbg(dev, " less than one block: curr_buff=%pK *curr_buff_cnt=0x%X copy_to=%pK\n",
- curr_buff, *curr_buff_cnt, &curr_buff[*curr_buff_cnt]);
- areq_ctx->in_nents =
- cc_get_sgl_nents(dev, src, nbytes, &dummy, NULL);
- sg_copy_to_buffer(src, areq_ctx->in_nents,
- &curr_buff[*curr_buff_cnt], nbytes);
- *curr_buff_cnt += nbytes;
- return 1;
- }
-
- /* Calculate the residue size*/
- *next_buff_cnt = total_in_len & (block_size - 1);
- /* update data len */
- update_data_len = total_in_len - *next_buff_cnt;
-
- dev_dbg(dev, " temp length : *next_buff_cnt=0x%X update_data_len=0x%X\n",
- *next_buff_cnt, update_data_len);
-
- /* Copy the new residue to next buffer */
- if (*next_buff_cnt) {
- dev_dbg(dev, " handle residue: next buff %pK skip data %u residue %u\n",
- next_buff, (update_data_len - *curr_buff_cnt),
- *next_buff_cnt);
- cc_copy_sg_portion(dev, next_buff, src,
- (update_data_len - *curr_buff_cnt),
- nbytes, CC_SG_TO_BUF);
- /* change the buffer index for next operation */
- swap_index = 1;
- }
-
- if (*curr_buff_cnt) {
- if (cc_set_hash_buf(dev, areq_ctx, curr_buff, *curr_buff_cnt,
- &sg_data)) {
- return -ENOMEM;
- }
- /* change the buffer index for next operation */
- swap_index = 1;
- }
-
- if (update_data_len > *curr_buff_cnt) {
- if (cc_map_sg(dev, src, (update_data_len - *curr_buff_cnt),
- DMA_TO_DEVICE, &areq_ctx->in_nents,
- LLI_MAX_NUM_OF_DATA_ENTRIES, &dummy,
- &mapped_nents)) {
- goto unmap_curr_buff;
- }
- if (mapped_nents == 1 &&
- areq_ctx->data_dma_buf_type == CC_DMA_BUF_NULL) {
- /* only one entry in the SG and no previous data */
- memcpy(areq_ctx->buff_sg, src,
- sizeof(struct scatterlist));
- areq_ctx->buff_sg->length = update_data_len;
- areq_ctx->data_dma_buf_type = CC_DMA_BUF_DLLI;
- areq_ctx->curr_sg = areq_ctx->buff_sg;
- } else {
- areq_ctx->data_dma_buf_type = CC_DMA_BUF_MLLI;
- }
- }
-
- if (areq_ctx->data_dma_buf_type == CC_DMA_BUF_MLLI) {
- mlli_params->curr_pool = buff_mgr->mlli_buffs_pool;
- /* add the src data to the sg_data */
- cc_add_sg_entry(dev, &sg_data, areq_ctx->in_nents, src,
- (update_data_len - *curr_buff_cnt), 0, true,
- &areq_ctx->mlli_nents);
- if (cc_generate_mlli(dev, &sg_data, mlli_params, flags))
- goto fail_unmap_din;
- }
- areq_ctx->buff_index = (areq_ctx->buff_index ^ swap_index);
-
- return 0;
-
-fail_unmap_din:
- dma_unmap_sg(dev, src, areq_ctx->in_nents, DMA_TO_DEVICE);
-
-unmap_curr_buff:
- if (*curr_buff_cnt)
- dma_unmap_sg(dev, areq_ctx->buff_sg, 1, DMA_TO_DEVICE);
-
- return -ENOMEM;
-}
-
-void cc_unmap_hash_request(struct device *dev, void *ctx,
- struct scatterlist *src, bool do_revert)
-{
- struct ahash_req_ctx *areq_ctx = (struct ahash_req_ctx *)ctx;
- u32 *prev_len = cc_next_buf_cnt(areq_ctx);
-
- /*In case a pool was set, a table was
- *allocated and should be released
- */
- if (areq_ctx->mlli_params.curr_pool) {
- dev_dbg(dev, "free MLLI buffer: dma=%pad virt=%pK\n",
- &areq_ctx->mlli_params.mlli_dma_addr,
- areq_ctx->mlli_params.mlli_virt_addr);
- dma_pool_free(areq_ctx->mlli_params.curr_pool,
- areq_ctx->mlli_params.mlli_virt_addr,
- areq_ctx->mlli_params.mlli_dma_addr);
- }
-
- if (src && areq_ctx->in_nents) {
- dev_dbg(dev, "Unmapped sg src: virt=%pK dma=%pad len=0x%X\n",
- sg_virt(src), &sg_dma_address(src), sg_dma_len(src));
- dma_unmap_sg(dev, src,
- areq_ctx->in_nents, DMA_TO_DEVICE);
- }
-
- if (*prev_len) {
- dev_dbg(dev, "Unmapped buffer: areq_ctx->buff_sg=%pK dma=%pad len 0x%X\n",
- sg_virt(areq_ctx->buff_sg),
- &sg_dma_address(areq_ctx->buff_sg),
- sg_dma_len(areq_ctx->buff_sg));
- dma_unmap_sg(dev, areq_ctx->buff_sg, 1, DMA_TO_DEVICE);
- if (!do_revert) {
- /* clean the previous data length for update
- * operation
- */
- *prev_len = 0;
- } else {
- areq_ctx->buff_index ^= 1;
- }
- }
-}
-
-int cc_buffer_mgr_init(struct cc_drvdata *drvdata)
-{
- struct buff_mgr_handle *buff_mgr_handle;
- struct device *dev = drvdata_to_dev(drvdata);
-
- buff_mgr_handle = kmalloc(sizeof(*buff_mgr_handle), GFP_KERNEL);
- if (!buff_mgr_handle)
- return -ENOMEM;
-
- drvdata->buff_mgr_handle = buff_mgr_handle;
-
- buff_mgr_handle->mlli_buffs_pool =
- dma_pool_create("dx_single_mlli_tables", dev,
- MAX_NUM_OF_TOTAL_MLLI_ENTRIES *
- LLI_ENTRY_BYTE_SIZE,
- MLLI_TABLE_MIN_ALIGNMENT, 0);
-
- if (!buff_mgr_handle->mlli_buffs_pool)
- goto error;
-
- return 0;
-
-error:
- cc_buffer_mgr_fini(drvdata);
- return -ENOMEM;
-}
-
-int cc_buffer_mgr_fini(struct cc_drvdata *drvdata)
-{
- struct buff_mgr_handle *buff_mgr_handle = drvdata->buff_mgr_handle;
-
- if (buff_mgr_handle) {
- dma_pool_destroy(buff_mgr_handle->mlli_buffs_pool);
- kfree(drvdata->buff_mgr_handle);
- drvdata->buff_mgr_handle = NULL;
- }
- return 0;
-}
diff --git a/drivers/staging/ccree/cc_buffer_mgr.h b/drivers/staging/ccree/cc_buffer_mgr.h
deleted file mode 100644
index 99b752aa1077..000000000000
--- a/drivers/staging/ccree/cc_buffer_mgr.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
-
-/* \file cc_buffer_mgr.h
- * Buffer Manager
- */
-
-#ifndef __CC_BUFFER_MGR_H__
-#define __CC_BUFFER_MGR_H__
-
-#include <crypto/algapi.h>
-
-#include "cc_driver.h"
-
-enum cc_req_dma_buf_type {
- CC_DMA_BUF_NULL = 0,
- CC_DMA_BUF_DLLI,
- CC_DMA_BUF_MLLI
-};
-
-enum cc_sg_cpy_direct {
- CC_SG_TO_BUF = 0,
- CC_SG_FROM_BUF = 1
-};
-
-struct cc_mlli {
- cc_sram_addr_t sram_addr;
- unsigned int nents; //sg nents
- unsigned int mlli_nents; //mlli nents might be different than the above
-};
-
-struct mlli_params {
- struct dma_pool *curr_pool;
- u8 *mlli_virt_addr;
- dma_addr_t mlli_dma_addr;
- u32 mlli_len;
-};
-
-int cc_buffer_mgr_init(struct cc_drvdata *drvdata);
-
-int cc_buffer_mgr_fini(struct cc_drvdata *drvdata);
-
-int cc_map_blkcipher_request(struct cc_drvdata *drvdata, void *ctx,
- unsigned int ivsize, unsigned int nbytes,
- void *info, struct scatterlist *src,
- struct scatterlist *dst, gfp_t flags);
-
-void cc_unmap_blkcipher_request(struct device *dev, void *ctx,
- unsigned int ivsize,
- struct scatterlist *src,
- struct scatterlist *dst);
-
-int cc_map_aead_request(struct cc_drvdata *drvdata, struct aead_request *req);
-
-void cc_unmap_aead_request(struct device *dev, struct aead_request *req);
-
-int cc_map_hash_request_final(struct cc_drvdata *drvdata, void *ctx,
- struct scatterlist *src, unsigned int nbytes,
- bool do_update, gfp_t flags);
-
-int cc_map_hash_request_update(struct cc_drvdata *drvdata, void *ctx,
- struct scatterlist *src, unsigned int nbytes,
- unsigned int block_size, gfp_t flags);
-
-void cc_unmap_hash_request(struct device *dev, void *ctx,
- struct scatterlist *src, bool do_revert);
-
-void cc_copy_sg_portion(struct device *dev, u8 *dest, struct scatterlist *sg,
- u32 to_skip, u32 end, enum cc_sg_cpy_direct direct);
-
-void cc_zero_sgl(struct scatterlist *sgl, u32 data_len);
-
-#endif /*__BUFFER_MGR_H__*/
-
diff --git a/drivers/staging/ccree/cc_cipher.c b/drivers/staging/ccree/cc_cipher.c
deleted file mode 100644
index d4ac0ff2ffcf..000000000000
--- a/drivers/staging/ccree/cc_cipher.c
+++ /dev/null
@@ -1,1164 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <crypto/algapi.h>
-#include <crypto/internal/skcipher.h>
-#include <crypto/des.h>
-#include <crypto/xts.h>
-#include <crypto/scatterwalk.h>
-
-#include "cc_driver.h"
-#include "cc_lli_defs.h"
-#include "cc_buffer_mgr.h"
-#include "cc_cipher.h"
-#include "cc_request_mgr.h"
-
-#define MAX_ABLKCIPHER_SEQ_LEN 6
-
-#define template_ablkcipher template_u.ablkcipher
-
-#define CC_MIN_AES_XTS_SIZE 0x10
-#define CC_MAX_AES_XTS_SIZE 0x2000
-struct cc_cipher_handle {
- struct list_head blkcipher_alg_list;
-};
-
-struct cc_user_key_info {
- u8 *key;
- dma_addr_t key_dma_addr;
-};
-
-struct cc_hw_key_info {
- enum cc_hw_crypto_key key1_slot;
- enum cc_hw_crypto_key key2_slot;
-};
-
-struct cc_cipher_ctx {
- struct cc_drvdata *drvdata;
- int keylen;
- int key_round_number;
- int cipher_mode;
- int flow_mode;
- unsigned int flags;
- struct blkcipher_req_ctx *sync_ctx;
- struct cc_user_key_info user;
- struct cc_hw_key_info hw;
- struct crypto_shash *shash_tfm;
-};
-
-static void cc_cipher_complete(struct device *dev, void *cc_req, int err);
-
-static int validate_keys_sizes(struct cc_cipher_ctx *ctx_p, u32 size)
-{
- switch (ctx_p->flow_mode) {
- case S_DIN_to_AES:
- switch (size) {
- case CC_AES_128_BIT_KEY_SIZE:
- case CC_AES_192_BIT_KEY_SIZE:
- if (ctx_p->cipher_mode != DRV_CIPHER_XTS &&
- ctx_p->cipher_mode != DRV_CIPHER_ESSIV &&
- ctx_p->cipher_mode != DRV_CIPHER_BITLOCKER)
- return 0;
- break;
- case CC_AES_256_BIT_KEY_SIZE:
- return 0;
- case (CC_AES_192_BIT_KEY_SIZE * 2):
- case (CC_AES_256_BIT_KEY_SIZE * 2):
- if (ctx_p->cipher_mode == DRV_CIPHER_XTS ||
- ctx_p->cipher_mode == DRV_CIPHER_ESSIV ||
- ctx_p->cipher_mode == DRV_CIPHER_BITLOCKER)
- return 0;
- break;
- default:
- break;
- }
- case S_DIN_to_DES:
- if (size == DES3_EDE_KEY_SIZE || size == DES_KEY_SIZE)
- return 0;
- break;
- default:
- break;
- }
- return -EINVAL;
-}
-
-static int validate_data_size(struct cc_cipher_ctx *ctx_p,
- unsigned int size)
-{
- switch (ctx_p->flow_mode) {
- case S_DIN_to_AES:
- switch (ctx_p->cipher_mode) {
- case DRV_CIPHER_XTS:
- if (size >= CC_MIN_AES_XTS_SIZE &&
- size <= CC_MAX_AES_XTS_SIZE &&
- IS_ALIGNED(size, AES_BLOCK_SIZE))
- return 0;
- break;
- case DRV_CIPHER_CBC_CTS:
- if (size >= AES_BLOCK_SIZE)
- return 0;
- break;
- case DRV_CIPHER_OFB:
- case DRV_CIPHER_CTR:
- return 0;
- case DRV_CIPHER_ECB:
- case DRV_CIPHER_CBC:
- case DRV_CIPHER_ESSIV:
- case DRV_CIPHER_BITLOCKER:
- if (IS_ALIGNED(size, AES_BLOCK_SIZE))
- return 0;
- break;
- default:
- break;
- }
- break;
- case S_DIN_to_DES:
- if (IS_ALIGNED(size, DES_BLOCK_SIZE))
- return 0;
- break;
- default:
- break;
- }
- return -EINVAL;
-}
-
-static unsigned int get_max_keysize(struct crypto_tfm *tfm)
-{
- struct cc_crypto_alg *cc_alg =
- container_of(tfm->__crt_alg, struct cc_crypto_alg, crypto_alg);
-
- if ((cc_alg->crypto_alg.cra_flags & CRYPTO_ALG_TYPE_MASK) ==
- CRYPTO_ALG_TYPE_ABLKCIPHER)
- return cc_alg->crypto_alg.cra_ablkcipher.max_keysize;
-
- if ((cc_alg->crypto_alg.cra_flags & CRYPTO_ALG_TYPE_MASK) ==
- CRYPTO_ALG_TYPE_BLKCIPHER)
- return cc_alg->crypto_alg.cra_blkcipher.max_keysize;
-
- return 0;
-}
-
-static int cc_cipher_init(struct crypto_tfm *tfm)
-{
- struct cc_cipher_ctx *ctx_p = crypto_tfm_ctx(tfm);
- struct crypto_alg *alg = tfm->__crt_alg;
- struct cc_crypto_alg *cc_alg =
- container_of(alg, struct cc_crypto_alg, crypto_alg);
- struct device *dev = drvdata_to_dev(cc_alg->drvdata);
- int rc = 0;
- unsigned int max_key_buf_size = get_max_keysize(tfm);
- struct ablkcipher_tfm *ablktfm = &tfm->crt_ablkcipher;
-
- dev_dbg(dev, "Initializing context @%p for %s\n", ctx_p,
- crypto_tfm_alg_name(tfm));
-
- ablktfm->reqsize = sizeof(struct blkcipher_req_ctx);
-
- ctx_p->cipher_mode = cc_alg->cipher_mode;
- ctx_p->flow_mode = cc_alg->flow_mode;
- ctx_p->drvdata = cc_alg->drvdata;
-
- /* Allocate key buffer, cache line aligned */
- ctx_p->user.key = kmalloc(max_key_buf_size, GFP_KERNEL);
- if (!ctx_p->user.key)
- return -ENOMEM;
-
- dev_dbg(dev, "Allocated key buffer in context. key=@%p\n",
- ctx_p->user.key);
-
- /* Map key buffer */
- ctx_p->user.key_dma_addr = dma_map_single(dev, (void *)ctx_p->user.key,
- max_key_buf_size,
- DMA_TO_DEVICE);
- if (dma_mapping_error(dev, ctx_p->user.key_dma_addr)) {
- dev_err(dev, "Mapping Key %u B at va=%pK for DMA failed\n",
- max_key_buf_size, ctx_p->user.key);
- return -ENOMEM;
- }
- dev_dbg(dev, "Mapped key %u B at va=%pK to dma=%pad\n",
- max_key_buf_size, ctx_p->user.key, &ctx_p->user.key_dma_addr);
-
- if (ctx_p->cipher_mode == DRV_CIPHER_ESSIV) {
- /* Alloc hash tfm for essiv */
- ctx_p->shash_tfm = crypto_alloc_shash("sha256-generic", 0, 0);
- if (IS_ERR(ctx_p->shash_tfm)) {
- dev_err(dev, "Error allocating hash tfm for ESSIV.\n");
- return PTR_ERR(ctx_p->shash_tfm);
- }
- }
-
- return rc;
-}
-
-static void cc_cipher_exit(struct crypto_tfm *tfm)
-{
- struct cc_cipher_ctx *ctx_p = crypto_tfm_ctx(tfm);
- struct device *dev = drvdata_to_dev(ctx_p->drvdata);
- unsigned int max_key_buf_size = get_max_keysize(tfm);
-
- dev_dbg(dev, "Clearing context @%p for %s\n",
- crypto_tfm_ctx(tfm), crypto_tfm_alg_name(tfm));
-
- if (ctx_p->cipher_mode == DRV_CIPHER_ESSIV) {
- /* Free hash tfm for essiv */
- crypto_free_shash(ctx_p->shash_tfm);
- ctx_p->shash_tfm = NULL;
- }
-
- /* Unmap key buffer */
- dma_unmap_single(dev, ctx_p->user.key_dma_addr, max_key_buf_size,
- DMA_TO_DEVICE);
- dev_dbg(dev, "Unmapped key buffer key_dma_addr=%pad\n",
- &ctx_p->user.key_dma_addr);
-
- /* Free key buffer in context */
- kfree(ctx_p->user.key);
- dev_dbg(dev, "Free key buffer in context. key=@%p\n", ctx_p->user.key);
-}
-
-struct tdes_keys {
- u8 key1[DES_KEY_SIZE];
- u8 key2[DES_KEY_SIZE];
- u8 key3[DES_KEY_SIZE];
-};
-
-static const u8 zero_buff[] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
- 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
- 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
- 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
-
-/* The function verifies that tdes keys are not weak.*/
-static int cc_verify_3des_keys(const u8 *key, unsigned int keylen)
-{
- struct tdes_keys *tdes_key = (struct tdes_keys *)key;
-
- /* verify key1 != key2 and key3 != key2*/
- if ((memcmp((u8 *)tdes_key->key1, (u8 *)tdes_key->key2,
- sizeof(tdes_key->key1)) == 0) ||
- (memcmp((u8 *)tdes_key->key3, (u8 *)tdes_key->key2,
- sizeof(tdes_key->key3)) == 0)) {
- return -ENOEXEC;
- }
-
- return 0;
-}
-
-static enum cc_hw_crypto_key hw_key_to_cc_hw_key(int slot_num)
-{
- switch (slot_num) {
- case 0:
- return KFDE0_KEY;
- case 1:
- return KFDE1_KEY;
- case 2:
- return KFDE2_KEY;
- case 3:
- return KFDE3_KEY;
- }
- return END_OF_KEYS;
-}
-
-static int cc_cipher_setkey(struct crypto_ablkcipher *atfm, const u8 *key,
- unsigned int keylen)
-{
- struct crypto_tfm *tfm = crypto_ablkcipher_tfm(atfm);
- struct cc_cipher_ctx *ctx_p = crypto_tfm_ctx(tfm);
- struct device *dev = drvdata_to_dev(ctx_p->drvdata);
- u32 tmp[DES_EXPKEY_WORDS];
- unsigned int max_key_buf_size = get_max_keysize(tfm);
-
- dev_dbg(dev, "Setting key in context @%p for %s. keylen=%u\n",
- ctx_p, crypto_tfm_alg_name(tfm), keylen);
- dump_byte_array("key", (u8 *)key, keylen);
-
- /* STAT_PHASE_0: Init and sanity checks */
-
- if (validate_keys_sizes(ctx_p, keylen)) {
- dev_err(dev, "Unsupported key size %d.\n", keylen);
- crypto_tfm_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
- return -EINVAL;
- }
-
- if (cc_is_hw_key(tfm)) {
- /* setting HW key slots */
- struct arm_hw_key_info *hki = (struct arm_hw_key_info *)key;
-
- if (ctx_p->flow_mode != S_DIN_to_AES) {
- dev_err(dev, "HW key not supported for non-AES flows\n");
- return -EINVAL;
- }
-
- ctx_p->hw.key1_slot = hw_key_to_cc_hw_key(hki->hw_key1);
- if (ctx_p->hw.key1_slot == END_OF_KEYS) {
- dev_err(dev, "Unsupported hw key1 number (%d)\n",
- hki->hw_key1);
- return -EINVAL;
- }
-
- if (ctx_p->cipher_mode == DRV_CIPHER_XTS ||
- ctx_p->cipher_mode == DRV_CIPHER_ESSIV ||
- ctx_p->cipher_mode == DRV_CIPHER_BITLOCKER) {
- if (hki->hw_key1 == hki->hw_key2) {
- dev_err(dev, "Illegal hw key numbers (%d,%d)\n",
- hki->hw_key1, hki->hw_key2);
- return -EINVAL;
- }
- ctx_p->hw.key2_slot =
- hw_key_to_cc_hw_key(hki->hw_key2);
- if (ctx_p->hw.key2_slot == END_OF_KEYS) {
- dev_err(dev, "Unsupported hw key2 number (%d)\n",
- hki->hw_key2);
- return -EINVAL;
- }
- }
-
- ctx_p->keylen = keylen;
- dev_dbg(dev, "cc_is_hw_key ret 0");
-
- return 0;
- }
-
- // verify weak keys
- if (ctx_p->flow_mode == S_DIN_to_DES) {
- if (!des_ekey(tmp, key) &&
- (crypto_tfm_get_flags(tfm) & CRYPTO_TFM_REQ_WEAK_KEY)) {
- tfm->crt_flags |= CRYPTO_TFM_RES_WEAK_KEY;
- dev_dbg(dev, "weak DES key");
- return -EINVAL;
- }
- }
- if (ctx_p->cipher_mode == DRV_CIPHER_XTS &&
- xts_check_key(tfm, key, keylen)) {
- dev_dbg(dev, "weak XTS key");
- return -EINVAL;
- }
- if (ctx_p->flow_mode == S_DIN_to_DES &&
- keylen == DES3_EDE_KEY_SIZE &&
- cc_verify_3des_keys(key, keylen)) {
- dev_dbg(dev, "weak 3DES key");
- return -EINVAL;
- }
-
- /* STAT_PHASE_1: Copy key to ctx */
- dma_sync_single_for_cpu(dev, ctx_p->user.key_dma_addr,
- max_key_buf_size, DMA_TO_DEVICE);
-
- memcpy(ctx_p->user.key, key, keylen);
- if (keylen == 24)
- memset(ctx_p->user.key + 24, 0, CC_AES_KEY_SIZE_MAX - 24);
-
- if (ctx_p->cipher_mode == DRV_CIPHER_ESSIV) {
- /* sha256 for key2 - use sw implementation */
- int key_len = keylen >> 1;
- int err;
-
- SHASH_DESC_ON_STACK(desc, ctx_p->shash_tfm);
-
- desc->tfm = ctx_p->shash_tfm;
-
- err = crypto_shash_digest(desc, ctx_p->user.key, key_len,
- ctx_p->user.key + key_len);
- if (err) {
- dev_err(dev, "Failed to hash ESSIV key.\n");
- return err;
- }
- }
- dma_sync_single_for_device(dev, ctx_p->user.key_dma_addr,
- max_key_buf_size, DMA_TO_DEVICE);
- ctx_p->keylen = keylen;
-
- dev_dbg(dev, "return safely");
- return 0;
-}
-
-static void cc_setup_cipher_desc(struct crypto_tfm *tfm,
- struct blkcipher_req_ctx *req_ctx,
- unsigned int ivsize, unsigned int nbytes,
- struct cc_hw_desc desc[],
- unsigned int *seq_size)
-{
- struct cc_cipher_ctx *ctx_p = crypto_tfm_ctx(tfm);
- struct device *dev = drvdata_to_dev(ctx_p->drvdata);
- int cipher_mode = ctx_p->cipher_mode;
- int flow_mode = ctx_p->flow_mode;
- int direction = req_ctx->gen_ctx.op_type;
- dma_addr_t key_dma_addr = ctx_p->user.key_dma_addr;
- unsigned int key_len = ctx_p->keylen;
- dma_addr_t iv_dma_addr = req_ctx->gen_ctx.iv_dma_addr;
- unsigned int du_size = nbytes;
-
- struct cc_crypto_alg *cc_alg =
- container_of(tfm->__crt_alg, struct cc_crypto_alg, crypto_alg);
-
- if ((cc_alg->crypto_alg.cra_flags & CRYPTO_ALG_BULK_MASK) ==
- CRYPTO_ALG_BULK_DU_512)
- du_size = 512;
- if ((cc_alg->crypto_alg.cra_flags & CRYPTO_ALG_BULK_MASK) ==
- CRYPTO_ALG_BULK_DU_4096)
- du_size = 4096;
-
- switch (cipher_mode) {
- case DRV_CIPHER_CBC:
- case DRV_CIPHER_CBC_CTS:
- case DRV_CIPHER_CTR:
- case DRV_CIPHER_OFB:
- /* Load cipher state */
- hw_desc_init(&desc[*seq_size]);
- set_din_type(&desc[*seq_size], DMA_DLLI, iv_dma_addr, ivsize,
- NS_BIT);
- set_cipher_config0(&desc[*seq_size], direction);
- set_flow_mode(&desc[*seq_size], flow_mode);
- set_cipher_mode(&desc[*seq_size], cipher_mode);
- if (cipher_mode == DRV_CIPHER_CTR ||
- cipher_mode == DRV_CIPHER_OFB) {
- set_setup_mode(&desc[*seq_size], SETUP_LOAD_STATE1);
- } else {
- set_setup_mode(&desc[*seq_size], SETUP_LOAD_STATE0);
- }
- (*seq_size)++;
- /*FALLTHROUGH*/
- case DRV_CIPHER_ECB:
- /* Load key */
- hw_desc_init(&desc[*seq_size]);
- set_cipher_mode(&desc[*seq_size], cipher_mode);
- set_cipher_config0(&desc[*seq_size], direction);
- if (flow_mode == S_DIN_to_AES) {
- if (cc_is_hw_key(tfm)) {
- set_hw_crypto_key(&desc[*seq_size],
- ctx_p->hw.key1_slot);
- } else {
- set_din_type(&desc[*seq_size], DMA_DLLI,
- key_dma_addr, ((key_len == 24) ?
- AES_MAX_KEY_SIZE :
- key_len), NS_BIT);
- }
- set_key_size_aes(&desc[*seq_size], key_len);
- } else {
- /*des*/
- set_din_type(&desc[*seq_size], DMA_DLLI, key_dma_addr,
- key_len, NS_BIT);
- set_key_size_des(&desc[*seq_size], key_len);
- }
- set_flow_mode(&desc[*seq_size], flow_mode);
- set_setup_mode(&desc[*seq_size], SETUP_LOAD_KEY0);
- (*seq_size)++;
- break;
- case DRV_CIPHER_XTS:
- case DRV_CIPHER_ESSIV:
- case DRV_CIPHER_BITLOCKER:
- /* Load AES key */
- hw_desc_init(&desc[*seq_size]);
- set_cipher_mode(&desc[*seq_size], cipher_mode);
- set_cipher_config0(&desc[*seq_size], direction);
- if (cc_is_hw_key(tfm)) {
- set_hw_crypto_key(&desc[*seq_size],
- ctx_p->hw.key1_slot);
- } else {
- set_din_type(&desc[*seq_size], DMA_DLLI, key_dma_addr,
- (key_len / 2), NS_BIT);
- }
- set_key_size_aes(&desc[*seq_size], (key_len / 2));
- set_flow_mode(&desc[*seq_size], flow_mode);
- set_setup_mode(&desc[*seq_size], SETUP_LOAD_KEY0);
- (*seq_size)++;
-
- /* load XEX key */
- hw_desc_init(&desc[*seq_size]);
- set_cipher_mode(&desc[*seq_size], cipher_mode);
- set_cipher_config0(&desc[*seq_size], direction);
- if (cc_is_hw_key(tfm)) {
- set_hw_crypto_key(&desc[*seq_size],
- ctx_p->hw.key2_slot);
- } else {
- set_din_type(&desc[*seq_size], DMA_DLLI,
- (key_dma_addr + (key_len / 2)),
- (key_len / 2), NS_BIT);
- }
- set_xex_data_unit_size(&desc[*seq_size], du_size);
- set_flow_mode(&desc[*seq_size], S_DIN_to_AES2);
- set_key_size_aes(&desc[*seq_size], (key_len / 2));
- set_setup_mode(&desc[*seq_size], SETUP_LOAD_XEX_KEY);
- (*seq_size)++;
-
- /* Set state */
- hw_desc_init(&desc[*seq_size]);
- set_setup_mode(&desc[*seq_size], SETUP_LOAD_STATE1);
- set_cipher_mode(&desc[*seq_size], cipher_mode);
- set_cipher_config0(&desc[*seq_size], direction);
- set_key_size_aes(&desc[*seq_size], (key_len / 2));
- set_flow_mode(&desc[*seq_size], flow_mode);
- set_din_type(&desc[*seq_size], DMA_DLLI, iv_dma_addr,
- CC_AES_BLOCK_SIZE, NS_BIT);
- (*seq_size)++;
- break;
- default:
- dev_err(dev, "Unsupported cipher mode (%d)\n", cipher_mode);
- }
-}
-
-static void cc_setup_cipher_data(struct crypto_tfm *tfm,
- struct blkcipher_req_ctx *req_ctx,
- struct scatterlist *dst,
- struct scatterlist *src, unsigned int nbytes,
- void *areq, struct cc_hw_desc desc[],
- unsigned int *seq_size)
-{
- struct cc_cipher_ctx *ctx_p = crypto_tfm_ctx(tfm);
- struct device *dev = drvdata_to_dev(ctx_p->drvdata);
- unsigned int flow_mode = ctx_p->flow_mode;
-
- switch (ctx_p->flow_mode) {
- case S_DIN_to_AES:
- flow_mode = DIN_AES_DOUT;
- break;
- case S_DIN_to_DES:
- flow_mode = DIN_DES_DOUT;
- break;
- default:
- dev_err(dev, "invalid flow mode, flow_mode = %d\n", flow_mode);
- return;
- }
- /* Process */
- if (req_ctx->dma_buf_type == CC_DMA_BUF_DLLI) {
- dev_dbg(dev, " data params addr %pad length 0x%X\n",
- &sg_dma_address(src), nbytes);
- dev_dbg(dev, " data params addr %pad length 0x%X\n",
- &sg_dma_address(dst), nbytes);
- hw_desc_init(&desc[*seq_size]);
- set_din_type(&desc[*seq_size], DMA_DLLI, sg_dma_address(src),
- nbytes, NS_BIT);
- set_dout_dlli(&desc[*seq_size], sg_dma_address(dst),
- nbytes, NS_BIT, (!areq ? 0 : 1));
- if (areq)
- set_queue_last_ind(&desc[*seq_size]);
-
- set_flow_mode(&desc[*seq_size], flow_mode);
- (*seq_size)++;
- } else {
- /* bypass */
- dev_dbg(dev, " bypass params addr %pad length 0x%X addr 0x%08X\n",
- &req_ctx->mlli_params.mlli_dma_addr,
- req_ctx->mlli_params.mlli_len,
- (unsigned int)ctx_p->drvdata->mlli_sram_addr);
- hw_desc_init(&desc[*seq_size]);
- set_din_type(&desc[*seq_size], DMA_DLLI,
- req_ctx->mlli_params.mlli_dma_addr,
- req_ctx->mlli_params.mlli_len, NS_BIT);
- set_dout_sram(&desc[*seq_size],
- ctx_p->drvdata->mlli_sram_addr,
- req_ctx->mlli_params.mlli_len);
- set_flow_mode(&desc[*seq_size], BYPASS);
- (*seq_size)++;
-
- hw_desc_init(&desc[*seq_size]);
- set_din_type(&desc[*seq_size], DMA_MLLI,
- ctx_p->drvdata->mlli_sram_addr,
- req_ctx->in_mlli_nents, NS_BIT);
- if (req_ctx->out_nents == 0) {
- dev_dbg(dev, " din/dout params addr 0x%08X addr 0x%08X\n",
- (unsigned int)ctx_p->drvdata->mlli_sram_addr,
- (unsigned int)ctx_p->drvdata->mlli_sram_addr);
- set_dout_mlli(&desc[*seq_size],
- ctx_p->drvdata->mlli_sram_addr,
- req_ctx->in_mlli_nents, NS_BIT,
- (!areq ? 0 : 1));
- } else {
- dev_dbg(dev, " din/dout params addr 0x%08X addr 0x%08X\n",
- (unsigned int)ctx_p->drvdata->mlli_sram_addr,
- (unsigned int)ctx_p->drvdata->mlli_sram_addr +
- (u32)LLI_ENTRY_BYTE_SIZE * req_ctx->in_nents);
- set_dout_mlli(&desc[*seq_size],
- (ctx_p->drvdata->mlli_sram_addr +
- (LLI_ENTRY_BYTE_SIZE *
- req_ctx->in_mlli_nents)),
- req_ctx->out_mlli_nents, NS_BIT,
- (!areq ? 0 : 1));
- }
- if (areq)
- set_queue_last_ind(&desc[*seq_size]);
-
- set_flow_mode(&desc[*seq_size], flow_mode);
- (*seq_size)++;
- }
-}
-
-static void cc_cipher_complete(struct device *dev, void *cc_req, int err)
-{
- struct ablkcipher_request *areq = (struct ablkcipher_request *)cc_req;
- struct scatterlist *dst = areq->dst;
- struct scatterlist *src = areq->src;
- struct blkcipher_req_ctx *req_ctx = ablkcipher_request_ctx(areq);
- struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(areq);
- unsigned int ivsize = crypto_ablkcipher_ivsize(tfm);
- struct ablkcipher_request *req = (struct ablkcipher_request *)areq;
-
- cc_unmap_blkcipher_request(dev, req_ctx, ivsize, src, dst);
- kfree(req_ctx->iv);
-
- /*
- * The crypto API expects us to set the req->info to the last
- * ciphertext block. For encrypt, simply copy from the result.
- * For decrypt, we must copy from a saved buffer since this
- * could be an in-place decryption operation and the src is
- * lost by this point.
- */
- if (req_ctx->gen_ctx.op_type == DRV_CRYPTO_DIRECTION_DECRYPT) {
- memcpy(req->info, req_ctx->backup_info, ivsize);
- kfree(req_ctx->backup_info);
- } else if (!err) {
- scatterwalk_map_and_copy(req->info, req->dst,
- (req->nbytes - ivsize), ivsize, 0);
- }
-
- ablkcipher_request_complete(areq, err);
-}
-
-static int cc_cipher_process(struct ablkcipher_request *req,
- enum drv_crypto_direction direction)
-{
- struct crypto_ablkcipher *ablk_tfm = crypto_ablkcipher_reqtfm(req);
- struct crypto_tfm *tfm = crypto_ablkcipher_tfm(ablk_tfm);
- struct blkcipher_req_ctx *req_ctx = ablkcipher_request_ctx(req);
- unsigned int ivsize = crypto_ablkcipher_ivsize(ablk_tfm);
- struct scatterlist *dst = req->dst;
- struct scatterlist *src = req->src;
- unsigned int nbytes = req->nbytes;
- void *info = req->info;
- struct cc_cipher_ctx *ctx_p = crypto_tfm_ctx(tfm);
- struct device *dev = drvdata_to_dev(ctx_p->drvdata);
- struct cc_hw_desc desc[MAX_ABLKCIPHER_SEQ_LEN];
- struct cc_crypto_req cc_req = {};
- int rc, seq_len = 0, cts_restore_flag = 0;
- gfp_t flags = cc_gfp_flags(&req->base);
-
- dev_dbg(dev, "%s req=%p info=%p nbytes=%d\n",
- ((direction == DRV_CRYPTO_DIRECTION_ENCRYPT) ?
- "Encrypt" : "Decrypt"), req, info, nbytes);
-
- /* STAT_PHASE_0: Init and sanity checks */
-
- /* TODO: check data length according to mode */
- if (validate_data_size(ctx_p, nbytes)) {
- dev_err(dev, "Unsupported data size %d.\n", nbytes);
- crypto_tfm_set_flags(tfm, CRYPTO_TFM_RES_BAD_BLOCK_LEN);
- rc = -EINVAL;
- goto exit_process;
- }
- if (nbytes == 0) {
- /* No data to process is valid */
- rc = 0;
- goto exit_process;
- }
-
- /* The IV we are handed may be allocted from the stack so
- * we must copy it to a DMAable buffer before use.
- */
- req_ctx->iv = kmalloc(ivsize, flags);
- if (!req_ctx->iv) {
- rc = -ENOMEM;
- goto exit_process;
- }
- memcpy(req_ctx->iv, info, ivsize);
-
- /*For CTS in case of data size aligned to 16 use CBC mode*/
- if (((nbytes % AES_BLOCK_SIZE) == 0) &&
- ctx_p->cipher_mode == DRV_CIPHER_CBC_CTS) {
- ctx_p->cipher_mode = DRV_CIPHER_CBC;
- cts_restore_flag = 1;
- }
-
- /* Setup DX request structure */
- cc_req.user_cb = (void *)cc_cipher_complete;
- cc_req.user_arg = (void *)req;
-
-#ifdef ENABLE_CYCLE_COUNT
- cc_req.op_type = (direction == DRV_CRYPTO_DIRECTION_DECRYPT) ?
- STAT_OP_TYPE_DECODE : STAT_OP_TYPE_ENCODE;
-
-#endif
-
- /* Setup request context */
- req_ctx->gen_ctx.op_type = direction;
-
- /* STAT_PHASE_1: Map buffers */
-
- rc = cc_map_blkcipher_request(ctx_p->drvdata, req_ctx, ivsize, nbytes,
- req_ctx->iv, src, dst, flags);
- if (rc) {
- dev_err(dev, "map_request() failed\n");
- goto exit_process;
- }
-
- /* STAT_PHASE_2: Create sequence */
-
- /* Setup processing */
- cc_setup_cipher_desc(tfm, req_ctx, ivsize, nbytes, desc, &seq_len);
- /* Data processing */
- cc_setup_cipher_data(tfm, req_ctx, dst, src, nbytes, req, desc,
- &seq_len);
-
- /* do we need to generate IV? */
- if (req_ctx->is_giv) {
- cc_req.ivgen_dma_addr[0] = req_ctx->gen_ctx.iv_dma_addr;
- cc_req.ivgen_dma_addr_len = 1;
- /* set the IV size (8/16 B long)*/
- cc_req.ivgen_size = ivsize;
- }
-
- /* STAT_PHASE_3: Lock HW and push sequence */
-
- rc = cc_send_request(ctx_p->drvdata, &cc_req, desc, seq_len,
- &req->base);
- if (rc != -EINPROGRESS && rc != -EBUSY) {
- /* Failed to send the request or request completed
- * synchronously
- */
- cc_unmap_blkcipher_request(dev, req_ctx, ivsize, src, dst);
- }
-
-exit_process:
- if (cts_restore_flag)
- ctx_p->cipher_mode = DRV_CIPHER_CBC_CTS;
-
- if (rc != -EINPROGRESS && rc != -EBUSY) {
- kfree(req_ctx->backup_info);
- kfree(req_ctx->iv);
- }
-
- return rc;
-}
-
-static int cc_cipher_encrypt(struct ablkcipher_request *req)
-{
- struct blkcipher_req_ctx *req_ctx = ablkcipher_request_ctx(req);
-
- req_ctx->is_giv = false;
- req_ctx->backup_info = NULL;
-
- return cc_cipher_process(req, DRV_CRYPTO_DIRECTION_ENCRYPT);
-}
-
-static int cc_cipher_decrypt(struct ablkcipher_request *req)
-{
- struct crypto_ablkcipher *ablk_tfm = crypto_ablkcipher_reqtfm(req);
- struct blkcipher_req_ctx *req_ctx = ablkcipher_request_ctx(req);
- unsigned int ivsize = crypto_ablkcipher_ivsize(ablk_tfm);
- gfp_t flags = cc_gfp_flags(&req->base);
-
- /*
- * Allocate and save the last IV sized bytes of the source, which will
- * be lost in case of in-place decryption and might be needed for CTS.
- */
- req_ctx->backup_info = kmalloc(ivsize, flags);
- if (!req_ctx->backup_info)
- return -ENOMEM;
-
- scatterwalk_map_and_copy(req_ctx->backup_info, req->src,
- (req->nbytes - ivsize), ivsize, 0);
- req_ctx->is_giv = false;
-
- return cc_cipher_process(req, DRV_CRYPTO_DIRECTION_DECRYPT);
-}
-
-/* DX Block cipher alg */
-static struct cc_alg_template blkcipher_algs[] = {
- {
- .name = "xts(aes)",
- .driver_name = "xts-aes-dx",
- .blocksize = AES_BLOCK_SIZE,
- .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
- .template_ablkcipher = {
- .setkey = cc_cipher_setkey,
- .encrypt = cc_cipher_encrypt,
- .decrypt = cc_cipher_decrypt,
- .min_keysize = AES_MIN_KEY_SIZE * 2,
- .max_keysize = AES_MAX_KEY_SIZE * 2,
- .ivsize = AES_BLOCK_SIZE,
- .geniv = "eseqiv",
- },
- .cipher_mode = DRV_CIPHER_XTS,
- .flow_mode = S_DIN_to_AES,
- },
- {
- .name = "xts(aes)",
- .driver_name = "xts-aes-du512-dx",
- .blocksize = AES_BLOCK_SIZE,
- .type = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_BULK_DU_512,
- .template_ablkcipher = {
- .setkey = cc_cipher_setkey,
- .encrypt = cc_cipher_encrypt,
- .decrypt = cc_cipher_decrypt,
- .min_keysize = AES_MIN_KEY_SIZE * 2,
- .max_keysize = AES_MAX_KEY_SIZE * 2,
- .ivsize = AES_BLOCK_SIZE,
- },
- .cipher_mode = DRV_CIPHER_XTS,
- .flow_mode = S_DIN_to_AES,
- },
- {
- .name = "xts(aes)",
- .driver_name = "xts-aes-du4096-dx",
- .blocksize = AES_BLOCK_SIZE,
- .type = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_BULK_DU_4096,
- .template_ablkcipher = {
- .setkey = cc_cipher_setkey,
- .encrypt = cc_cipher_encrypt,
- .decrypt = cc_cipher_decrypt,
- .min_keysize = AES_MIN_KEY_SIZE * 2,
- .max_keysize = AES_MAX_KEY_SIZE * 2,
- .ivsize = AES_BLOCK_SIZE,
- },
- .cipher_mode = DRV_CIPHER_XTS,
- .flow_mode = S_DIN_to_AES,
- },
- {
- .name = "essiv(aes)",
- .driver_name = "essiv-aes-dx",
- .blocksize = AES_BLOCK_SIZE,
- .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
- .template_ablkcipher = {
- .setkey = cc_cipher_setkey,
- .encrypt = cc_cipher_encrypt,
- .decrypt = cc_cipher_decrypt,
- .min_keysize = AES_MIN_KEY_SIZE * 2,
- .max_keysize = AES_MAX_KEY_SIZE * 2,
- .ivsize = AES_BLOCK_SIZE,
- },
- .cipher_mode = DRV_CIPHER_ESSIV,
- .flow_mode = S_DIN_to_AES,
- },
- {
- .name = "essiv(aes)",
- .driver_name = "essiv-aes-du512-dx",
- .blocksize = AES_BLOCK_SIZE,
- .type = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_BULK_DU_512,
- .template_ablkcipher = {
- .setkey = cc_cipher_setkey,
- .encrypt = cc_cipher_encrypt,
- .decrypt = cc_cipher_decrypt,
- .min_keysize = AES_MIN_KEY_SIZE * 2,
- .max_keysize = AES_MAX_KEY_SIZE * 2,
- .ivsize = AES_BLOCK_SIZE,
- },
- .cipher_mode = DRV_CIPHER_ESSIV,
- .flow_mode = S_DIN_to_AES,
- },
- {
- .name = "essiv(aes)",
- .driver_name = "essiv-aes-du4096-dx",
- .blocksize = AES_BLOCK_SIZE,
- .type = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_BULK_DU_4096,
- .template_ablkcipher = {
- .setkey = cc_cipher_setkey,
- .encrypt = cc_cipher_encrypt,
- .decrypt = cc_cipher_decrypt,
- .min_keysize = AES_MIN_KEY_SIZE * 2,
- .max_keysize = AES_MAX_KEY_SIZE * 2,
- .ivsize = AES_BLOCK_SIZE,
- },
- .cipher_mode = DRV_CIPHER_ESSIV,
- .flow_mode = S_DIN_to_AES,
- },
- {
- .name = "bitlocker(aes)",
- .driver_name = "bitlocker-aes-dx",
- .blocksize = AES_BLOCK_SIZE,
- .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
- .template_ablkcipher = {
- .setkey = cc_cipher_setkey,
- .encrypt = cc_cipher_encrypt,
- .decrypt = cc_cipher_decrypt,
- .min_keysize = AES_MIN_KEY_SIZE * 2,
- .max_keysize = AES_MAX_KEY_SIZE * 2,
- .ivsize = AES_BLOCK_SIZE,
- },
- .cipher_mode = DRV_CIPHER_BITLOCKER,
- .flow_mode = S_DIN_to_AES,
- },
- {
- .name = "bitlocker(aes)",
- .driver_name = "bitlocker-aes-du512-dx",
- .blocksize = AES_BLOCK_SIZE,
- .type = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_BULK_DU_512,
- .template_ablkcipher = {
- .setkey = cc_cipher_setkey,
- .encrypt = cc_cipher_encrypt,
- .decrypt = cc_cipher_decrypt,
- .min_keysize = AES_MIN_KEY_SIZE * 2,
- .max_keysize = AES_MAX_KEY_SIZE * 2,
- .ivsize = AES_BLOCK_SIZE,
- },
- .cipher_mode = DRV_CIPHER_BITLOCKER,
- .flow_mode = S_DIN_to_AES,
- },
- {
- .name = "bitlocker(aes)",
- .driver_name = "bitlocker-aes-du4096-dx",
- .blocksize = AES_BLOCK_SIZE,
- .type = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_BULK_DU_4096,
- .template_ablkcipher = {
- .setkey = cc_cipher_setkey,
- .encrypt = cc_cipher_encrypt,
- .decrypt = cc_cipher_decrypt,
- .min_keysize = AES_MIN_KEY_SIZE * 2,
- .max_keysize = AES_MAX_KEY_SIZE * 2,
- .ivsize = AES_BLOCK_SIZE,
- },
- .cipher_mode = DRV_CIPHER_BITLOCKER,
- .flow_mode = S_DIN_to_AES,
- },
- {
- .name = "ecb(aes)",
- .driver_name = "ecb-aes-dx",
- .blocksize = AES_BLOCK_SIZE,
- .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
- .template_ablkcipher = {
- .setkey = cc_cipher_setkey,
- .encrypt = cc_cipher_encrypt,
- .decrypt = cc_cipher_decrypt,
- .min_keysize = AES_MIN_KEY_SIZE,
- .max_keysize = AES_MAX_KEY_SIZE,
- .ivsize = 0,
- },
- .cipher_mode = DRV_CIPHER_ECB,
- .flow_mode = S_DIN_to_AES,
- },
- {
- .name = "cbc(aes)",
- .driver_name = "cbc-aes-dx",
- .blocksize = AES_BLOCK_SIZE,
- .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
- .template_ablkcipher = {
- .setkey = cc_cipher_setkey,
- .encrypt = cc_cipher_encrypt,
- .decrypt = cc_cipher_decrypt,
- .min_keysize = AES_MIN_KEY_SIZE,
- .max_keysize = AES_MAX_KEY_SIZE,
- .ivsize = AES_BLOCK_SIZE,
- },
- .cipher_mode = DRV_CIPHER_CBC,
- .flow_mode = S_DIN_to_AES,
- },
- {
- .name = "ofb(aes)",
- .driver_name = "ofb-aes-dx",
- .blocksize = AES_BLOCK_SIZE,
- .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
- .template_ablkcipher = {
- .setkey = cc_cipher_setkey,
- .encrypt = cc_cipher_encrypt,
- .decrypt = cc_cipher_decrypt,
- .min_keysize = AES_MIN_KEY_SIZE,
- .max_keysize = AES_MAX_KEY_SIZE,
- .ivsize = AES_BLOCK_SIZE,
- },
- .cipher_mode = DRV_CIPHER_OFB,
- .flow_mode = S_DIN_to_AES,
- },
- {
- .name = "cts1(cbc(aes))",
- .driver_name = "cts1-cbc-aes-dx",
- .blocksize = AES_BLOCK_SIZE,
- .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
- .template_ablkcipher = {
- .setkey = cc_cipher_setkey,
- .encrypt = cc_cipher_encrypt,
- .decrypt = cc_cipher_decrypt,
- .min_keysize = AES_MIN_KEY_SIZE,
- .max_keysize = AES_MAX_KEY_SIZE,
- .ivsize = AES_BLOCK_SIZE,
- },
- .cipher_mode = DRV_CIPHER_CBC_CTS,
- .flow_mode = S_DIN_to_AES,
- },
- {
- .name = "ctr(aes)",
- .driver_name = "ctr-aes-dx",
- .blocksize = 1,
- .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
- .template_ablkcipher = {
- .setkey = cc_cipher_setkey,
- .encrypt = cc_cipher_encrypt,
- .decrypt = cc_cipher_decrypt,
- .min_keysize = AES_MIN_KEY_SIZE,
- .max_keysize = AES_MAX_KEY_SIZE,
- .ivsize = AES_BLOCK_SIZE,
- },
- .cipher_mode = DRV_CIPHER_CTR,
- .flow_mode = S_DIN_to_AES,
- },
- {
- .name = "cbc(des3_ede)",
- .driver_name = "cbc-3des-dx",
- .blocksize = DES3_EDE_BLOCK_SIZE,
- .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
- .template_ablkcipher = {
- .setkey = cc_cipher_setkey,
- .encrypt = cc_cipher_encrypt,
- .decrypt = cc_cipher_decrypt,
- .min_keysize = DES3_EDE_KEY_SIZE,
- .max_keysize = DES3_EDE_KEY_SIZE,
- .ivsize = DES3_EDE_BLOCK_SIZE,
- },
- .cipher_mode = DRV_CIPHER_CBC,
- .flow_mode = S_DIN_to_DES,
- },
- {
- .name = "ecb(des3_ede)",
- .driver_name = "ecb-3des-dx",
- .blocksize = DES3_EDE_BLOCK_SIZE,
- .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
- .template_ablkcipher = {
- .setkey = cc_cipher_setkey,
- .encrypt = cc_cipher_encrypt,
- .decrypt = cc_cipher_decrypt,
- .min_keysize = DES3_EDE_KEY_SIZE,
- .max_keysize = DES3_EDE_KEY_SIZE,
- .ivsize = 0,
- },
- .cipher_mode = DRV_CIPHER_ECB,
- .flow_mode = S_DIN_to_DES,
- },
- {
- .name = "cbc(des)",
- .driver_name = "cbc-des-dx",
- .blocksize = DES_BLOCK_SIZE,
- .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
- .template_ablkcipher = {
- .setkey = cc_cipher_setkey,
- .encrypt = cc_cipher_encrypt,
- .decrypt = cc_cipher_decrypt,
- .min_keysize = DES_KEY_SIZE,
- .max_keysize = DES_KEY_SIZE,
- .ivsize = DES_BLOCK_SIZE,
- },
- .cipher_mode = DRV_CIPHER_CBC,
- .flow_mode = S_DIN_to_DES,
- },
- {
- .name = "ecb(des)",
- .driver_name = "ecb-des-dx",
- .blocksize = DES_BLOCK_SIZE,
- .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
- .template_ablkcipher = {
- .setkey = cc_cipher_setkey,
- .encrypt = cc_cipher_encrypt,
- .decrypt = cc_cipher_decrypt,
- .min_keysize = DES_KEY_SIZE,
- .max_keysize = DES_KEY_SIZE,
- .ivsize = 0,
- },
- .cipher_mode = DRV_CIPHER_ECB,
- .flow_mode = S_DIN_to_DES,
- },
-};
-
-static
-struct cc_crypto_alg *cc_cipher_create_alg(struct cc_alg_template *template,
- struct device *dev)
-{
- struct cc_crypto_alg *t_alg;
- struct crypto_alg *alg;
-
- t_alg = kzalloc(sizeof(*t_alg), GFP_KERNEL);
- if (!t_alg)
- return ERR_PTR(-ENOMEM);
-
- alg = &t_alg->crypto_alg;
-
- snprintf(alg->cra_name, CRYPTO_MAX_ALG_NAME, "%s", template->name);
- snprintf(alg->cra_driver_name, CRYPTO_MAX_ALG_NAME, "%s",
- template->driver_name);
- alg->cra_module = THIS_MODULE;
- alg->cra_priority = CC_CRA_PRIO;
- alg->cra_blocksize = template->blocksize;
- alg->cra_alignmask = 0;
- alg->cra_ctxsize = sizeof(struct cc_cipher_ctx);
-
- alg->cra_init = cc_cipher_init;
- alg->cra_exit = cc_cipher_exit;
- alg->cra_type = &crypto_ablkcipher_type;
- alg->cra_ablkcipher = template->template_ablkcipher;
- alg->cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_KERN_DRIVER_ONLY |
- template->type;
-
- t_alg->cipher_mode = template->cipher_mode;
- t_alg->flow_mode = template->flow_mode;
-
- return t_alg;
-}
-
-int cc_cipher_free(struct cc_drvdata *drvdata)
-{
- struct cc_crypto_alg *t_alg, *n;
- struct cc_cipher_handle *blkcipher_handle = drvdata->blkcipher_handle;
-
- if (blkcipher_handle) {
- /* Remove registered algs */
- list_for_each_entry_safe(t_alg, n,
- &blkcipher_handle->blkcipher_alg_list,
- entry) {
- crypto_unregister_alg(&t_alg->crypto_alg);
- list_del(&t_alg->entry);
- kfree(t_alg);
- }
- kfree(blkcipher_handle);
- drvdata->blkcipher_handle = NULL;
- }
- return 0;
-}
-
-int cc_cipher_alloc(struct cc_drvdata *drvdata)
-{
- struct cc_cipher_handle *ablkcipher_handle;
- struct cc_crypto_alg *t_alg;
- struct device *dev = drvdata_to_dev(drvdata);
- int rc = -ENOMEM;
- int alg;
-
- ablkcipher_handle = kmalloc(sizeof(*ablkcipher_handle), GFP_KERNEL);
- if (!ablkcipher_handle)
- return -ENOMEM;
-
- INIT_LIST_HEAD(&ablkcipher_handle->blkcipher_alg_list);
- drvdata->blkcipher_handle = ablkcipher_handle;
-
- /* Linux crypto */
- dev_dbg(dev, "Number of algorithms = %zu\n",
- ARRAY_SIZE(blkcipher_algs));
- for (alg = 0; alg < ARRAY_SIZE(blkcipher_algs); alg++) {
- dev_dbg(dev, "creating %s\n", blkcipher_algs[alg].driver_name);
- t_alg = cc_cipher_create_alg(&blkcipher_algs[alg], dev);
- if (IS_ERR(t_alg)) {
- rc = PTR_ERR(t_alg);
- dev_err(dev, "%s alg allocation failed\n",
- blkcipher_algs[alg].driver_name);
- goto fail0;
- }
- t_alg->drvdata = drvdata;
-
- dev_dbg(dev, "registering %s\n",
- blkcipher_algs[alg].driver_name);
- rc = crypto_register_alg(&t_alg->crypto_alg);
- dev_dbg(dev, "%s alg registration rc = %x\n",
- t_alg->crypto_alg.cra_driver_name, rc);
- if (rc) {
- dev_err(dev, "%s alg registration failed\n",
- t_alg->crypto_alg.cra_driver_name);
- kfree(t_alg);
- goto fail0;
- } else {
- list_add_tail(&t_alg->entry,
- &ablkcipher_handle->blkcipher_alg_list);
- dev_dbg(dev, "Registered %s\n",
- t_alg->crypto_alg.cra_driver_name);
- }
- }
- return 0;
-
-fail0:
- cc_cipher_free(drvdata);
- return rc;
-}
diff --git a/drivers/staging/ccree/cc_cipher.h b/drivers/staging/ccree/cc_cipher.h
deleted file mode 100644
index 4c181c721723..000000000000
--- a/drivers/staging/ccree/cc_cipher.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
-
-/* \file cc_cipher.h
- * ARM CryptoCell Cipher Crypto API
- */
-
-#ifndef __CC_CIPHER_H__
-#define __CC_CIPHER_H__
-
-#include <linux/kernel.h>
-#include <crypto/algapi.h>
-#include "cc_driver.h"
-#include "cc_buffer_mgr.h"
-
-/* Crypto cipher flags */
-#define CC_CRYPTO_CIPHER_KEY_KFDE0 BIT(0)
-#define CC_CRYPTO_CIPHER_KEY_KFDE1 BIT(1)
-#define CC_CRYPTO_CIPHER_KEY_KFDE2 BIT(2)
-#define CC_CRYPTO_CIPHER_KEY_KFDE3 BIT(3)
-#define CC_CRYPTO_CIPHER_DU_SIZE_512B BIT(4)
-
-#define CC_CRYPTO_CIPHER_KEY_KFDE_MASK (CC_CRYPTO_CIPHER_KEY_KFDE0 | \
- CC_CRYPTO_CIPHER_KEY_KFDE1 | \
- CC_CRYPTO_CIPHER_KEY_KFDE2 | \
- CC_CRYPTO_CIPHER_KEY_KFDE3)
-
-struct blkcipher_req_ctx {
- struct async_gen_req_ctx gen_ctx;
- enum cc_req_dma_buf_type dma_buf_type;
- u32 in_nents;
- u32 in_mlli_nents;
- u32 out_nents;
- u32 out_mlli_nents;
- u8 *backup_info; /*store iv for generated IV flow*/
- u8 *iv;
- bool is_giv;
- struct mlli_params mlli_params;
-};
-
-int cc_cipher_alloc(struct cc_drvdata *drvdata);
-
-int cc_cipher_free(struct cc_drvdata *drvdata);
-
-#ifndef CRYPTO_ALG_BULK_MASK
-
-#define CRYPTO_ALG_BULK_DU_512 0x00002000
-#define CRYPTO_ALG_BULK_DU_4096 0x00004000
-#define CRYPTO_ALG_BULK_MASK (CRYPTO_ALG_BULK_DU_512 |\
- CRYPTO_ALG_BULK_DU_4096)
-#endif /* CRYPTO_ALG_BULK_MASK */
-
-#ifdef CRYPTO_TFM_REQ_HW_KEY
-
-static inline bool cc_is_hw_key(struct crypto_tfm *tfm)
-{
- return (crypto_tfm_get_flags(tfm) & CRYPTO_TFM_REQ_HW_KEY);
-}
-
-#else
-
-struct arm_hw_key_info {
- int hw_key1;
- int hw_key2;
-};
-
-static inline bool cc_is_hw_key(struct crypto_tfm *tfm)
-{
- return false;
-}
-
-#endif /* CRYPTO_TFM_REQ_HW_KEY */
-
-#endif /*__CC_CIPHER_H__*/
diff --git a/drivers/staging/ccree/cc_crypto_ctx.h b/drivers/staging/ccree/cc_crypto_ctx.h
deleted file mode 100644
index eb16842d7db9..000000000000
--- a/drivers/staging/ccree/cc_crypto_ctx.h
+++ /dev/null
@@ -1,170 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
-
-#ifndef _CC_CRYPTO_CTX_H_
-#define _CC_CRYPTO_CTX_H_
-
-#include <linux/types.h>
-
-/* context size */
-#ifndef CC_CTX_SIZE_LOG2
-#if (CC_DEV_SHA_MAX > 256)
-#define CC_CTX_SIZE_LOG2 8
-#else
-#define CC_CTX_SIZE_LOG2 7
-#endif
-#endif
-#define CC_CTX_SIZE BIT(CC_CTX_SIZE_LOG2)
-#define CC_DRV_CTX_SIZE_WORDS (CC_CTX_SIZE >> 2)
-
-#define CC_DRV_DES_IV_SIZE 8
-#define CC_DRV_DES_BLOCK_SIZE 8
-
-#define CC_DRV_DES_ONE_KEY_SIZE 8
-#define CC_DRV_DES_DOUBLE_KEY_SIZE 16
-#define CC_DRV_DES_TRIPLE_KEY_SIZE 24
-#define CC_DRV_DES_KEY_SIZE_MAX CC_DRV_DES_TRIPLE_KEY_SIZE
-
-#define CC_AES_IV_SIZE 16
-#define CC_AES_IV_SIZE_WORDS (CC_AES_IV_SIZE >> 2)
-
-#define CC_AES_BLOCK_SIZE 16
-#define CC_AES_BLOCK_SIZE_WORDS 4
-
-#define CC_AES_128_BIT_KEY_SIZE 16
-#define CC_AES_128_BIT_KEY_SIZE_WORDS (CC_AES_128_BIT_KEY_SIZE >> 2)
-#define CC_AES_192_BIT_KEY_SIZE 24
-#define CC_AES_192_BIT_KEY_SIZE_WORDS (CC_AES_192_BIT_KEY_SIZE >> 2)
-#define CC_AES_256_BIT_KEY_SIZE 32
-#define CC_AES_256_BIT_KEY_SIZE_WORDS (CC_AES_256_BIT_KEY_SIZE >> 2)
-#define CC_AES_KEY_SIZE_MAX CC_AES_256_BIT_KEY_SIZE
-#define CC_AES_KEY_SIZE_WORDS_MAX (CC_AES_KEY_SIZE_MAX >> 2)
-
-#define CC_MD5_DIGEST_SIZE 16
-#define CC_SHA1_DIGEST_SIZE 20
-#define CC_SHA224_DIGEST_SIZE 28
-#define CC_SHA256_DIGEST_SIZE 32
-#define CC_SHA256_DIGEST_SIZE_IN_WORDS 8
-#define CC_SHA384_DIGEST_SIZE 48
-#define CC_SHA512_DIGEST_SIZE 64
-
-#define CC_SHA1_BLOCK_SIZE 64
-#define CC_SHA1_BLOCK_SIZE_IN_WORDS 16
-#define CC_MD5_BLOCK_SIZE 64
-#define CC_MD5_BLOCK_SIZE_IN_WORDS 16
-#define CC_SHA224_BLOCK_SIZE 64
-#define CC_SHA256_BLOCK_SIZE 64
-#define CC_SHA256_BLOCK_SIZE_IN_WORDS 16
-#define CC_SHA1_224_256_BLOCK_SIZE 64
-#define CC_SHA384_BLOCK_SIZE 128
-#define CC_SHA512_BLOCK_SIZE 128
-
-#if (CC_DEV_SHA_MAX > 256)
-#define CC_DIGEST_SIZE_MAX CC_SHA512_DIGEST_SIZE
-#define CC_HASH_BLOCK_SIZE_MAX CC_SHA512_BLOCK_SIZE /*1024b*/
-#else /* Only up to SHA256 */
-#define CC_DIGEST_SIZE_MAX CC_SHA256_DIGEST_SIZE
-#define CC_HASH_BLOCK_SIZE_MAX CC_SHA256_BLOCK_SIZE /*512b*/
-#endif
-
-#define CC_HMAC_BLOCK_SIZE_MAX CC_HASH_BLOCK_SIZE_MAX
-
-#define CC_DRV_ALG_MAX_BLOCK_SIZE CC_HASH_BLOCK_SIZE_MAX
-
-enum drv_engine_type {
- DRV_ENGINE_NULL = 0,
- DRV_ENGINE_AES = 1,
- DRV_ENGINE_DES = 2,
- DRV_ENGINE_HASH = 3,
- DRV_ENGINE_RC4 = 4,
- DRV_ENGINE_DOUT = 5,
- DRV_ENGINE_RESERVE32B = S32_MAX,
-};
-
-enum drv_crypto_alg {
- DRV_CRYPTO_ALG_NULL = -1,
- DRV_CRYPTO_ALG_AES = 0,
- DRV_CRYPTO_ALG_DES = 1,
- DRV_CRYPTO_ALG_HASH = 2,
- DRV_CRYPTO_ALG_C2 = 3,
- DRV_CRYPTO_ALG_HMAC = 4,
- DRV_CRYPTO_ALG_AEAD = 5,
- DRV_CRYPTO_ALG_BYPASS = 6,
- DRV_CRYPTO_ALG_NUM = 7,
- DRV_CRYPTO_ALG_RESERVE32B = S32_MAX
-};
-
-enum drv_crypto_direction {
- DRV_CRYPTO_DIRECTION_NULL = -1,
- DRV_CRYPTO_DIRECTION_ENCRYPT = 0,
- DRV_CRYPTO_DIRECTION_DECRYPT = 1,
- DRV_CRYPTO_DIRECTION_DECRYPT_ENCRYPT = 3,
- DRV_CRYPTO_DIRECTION_RESERVE32B = S32_MAX
-};
-
-enum drv_cipher_mode {
- DRV_CIPHER_NULL_MODE = -1,
- DRV_CIPHER_ECB = 0,
- DRV_CIPHER_CBC = 1,
- DRV_CIPHER_CTR = 2,
- DRV_CIPHER_CBC_MAC = 3,
- DRV_CIPHER_XTS = 4,
- DRV_CIPHER_XCBC_MAC = 5,
- DRV_CIPHER_OFB = 6,
- DRV_CIPHER_CMAC = 7,
- DRV_CIPHER_CCM = 8,
- DRV_CIPHER_CBC_CTS = 11,
- DRV_CIPHER_GCTR = 12,
- DRV_CIPHER_ESSIV = 13,
- DRV_CIPHER_BITLOCKER = 14,
- DRV_CIPHER_RESERVE32B = S32_MAX
-};
-
-enum drv_hash_mode {
- DRV_HASH_NULL = -1,
- DRV_HASH_SHA1 = 0,
- DRV_HASH_SHA256 = 1,
- DRV_HASH_SHA224 = 2,
- DRV_HASH_SHA512 = 3,
- DRV_HASH_SHA384 = 4,
- DRV_HASH_MD5 = 5,
- DRV_HASH_CBC_MAC = 6,
- DRV_HASH_XCBC_MAC = 7,
- DRV_HASH_CMAC = 8,
- DRV_HASH_MODE_NUM = 9,
- DRV_HASH_RESERVE32B = S32_MAX
-};
-
-enum drv_hash_hw_mode {
- DRV_HASH_HW_MD5 = 0,
- DRV_HASH_HW_SHA1 = 1,
- DRV_HASH_HW_SHA256 = 2,
- DRV_HASH_HW_SHA224 = 10,
- DRV_HASH_HW_SHA512 = 4,
- DRV_HASH_HW_SHA384 = 12,
- DRV_HASH_HW_GHASH = 6,
- DRV_HASH_HW_RESERVE32B = S32_MAX
-};
-
-/* drv_crypto_key_type[1:0] is mapped to cipher_do[1:0] */
-/* drv_crypto_key_type[2] is mapped to cipher_config2 */
-enum drv_crypto_key_type {
- DRV_NULL_KEY = -1,
- DRV_USER_KEY = 0, /* 0x000 */
- DRV_ROOT_KEY = 1, /* 0x001 */
- DRV_PROVISIONING_KEY = 2, /* 0x010 */
- DRV_SESSION_KEY = 3, /* 0x011 */
- DRV_APPLET_KEY = 4, /* NA */
- DRV_PLATFORM_KEY = 5, /* 0x101 */
- DRV_CUSTOMER_KEY = 6, /* 0x110 */
- DRV_END_OF_KEYS = S32_MAX,
-};
-
-enum drv_crypto_padding_type {
- DRV_PADDING_NONE = 0,
- DRV_PADDING_PKCS7 = 1,
- DRV_PADDING_RESERVE32B = S32_MAX
-};
-
-#endif /* _CC_CRYPTO_CTX_H_ */
-
diff --git a/drivers/staging/ccree/cc_driver.c b/drivers/staging/ccree/cc_driver.c
deleted file mode 100644
index 3a1cb0c98648..000000000000
--- a/drivers/staging/ccree/cc_driver.c
+++ /dev/null
@@ -1,474 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-
-#include <linux/crypto.h>
-#include <linux/moduleparam.h>
-#include <linux/types.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/of.h>
-#include <linux/clk.h>
-#include <linux/of_address.h>
-
-#include "cc_driver.h"
-#include "cc_request_mgr.h"
-#include "cc_buffer_mgr.h"
-#include "cc_debugfs.h"
-#include "cc_cipher.h"
-#include "cc_aead.h"
-#include "cc_hash.h"
-#include "cc_ivgen.h"
-#include "cc_sram_mgr.h"
-#include "cc_pm.h"
-#include "cc_fips.h"
-
-bool cc_dump_desc;
-module_param_named(dump_desc, cc_dump_desc, bool, 0600);
-MODULE_PARM_DESC(cc_dump_desc, "Dump descriptors to kernel log as debugging aid");
-
-bool cc_dump_bytes;
-module_param_named(dump_bytes, cc_dump_bytes, bool, 0600);
-MODULE_PARM_DESC(cc_dump_bytes, "Dump buffers to kernel log as debugging aid");
-
-void __dump_byte_array(const char *name, const u8 *buf, size_t len)
-{
- char prefix[64];
-
- if (!buf)
- return;
-
- snprintf(prefix, sizeof(prefix), "%s[%zu]: ", name, len);
-
- print_hex_dump(KERN_DEBUG, prefix, DUMP_PREFIX_ADDRESS, 16, 1, buf,
- len, false);
-}
-
-static irqreturn_t cc_isr(int irq, void *dev_id)
-{
- struct cc_drvdata *drvdata = (struct cc_drvdata *)dev_id;
- struct device *dev = drvdata_to_dev(drvdata);
- u32 irr;
- u32 imr;
-
- /* STAT_OP_TYPE_GENERIC STAT_PHASE_0: Interrupt */
-
- /* read the interrupt status */
- irr = cc_ioread(drvdata, CC_REG(HOST_IRR));
- dev_dbg(dev, "Got IRR=0x%08X\n", irr);
- if (irr == 0) { /* Probably shared interrupt line */
- dev_err(dev, "Got interrupt with empty IRR\n");
- return IRQ_NONE;
- }
- imr = cc_ioread(drvdata, CC_REG(HOST_IMR));
-
- /* clear interrupt - must be before processing events */
- cc_iowrite(drvdata, CC_REG(HOST_ICR), irr);
-
- drvdata->irq = irr;
- /* Completion interrupt - most probable */
- if (irr & CC_COMP_IRQ_MASK) {
- /* Mask AXI completion interrupt - will be unmasked in
- * Deferred service handler
- */
- cc_iowrite(drvdata, CC_REG(HOST_IMR), imr | CC_COMP_IRQ_MASK);
- irr &= ~CC_COMP_IRQ_MASK;
- complete_request(drvdata);
- }
-#ifdef CONFIG_CRYPTO_FIPS
- /* TEE FIPS interrupt */
- if (irr & CC_GPR0_IRQ_MASK) {
- /* Mask interrupt - will be unmasked in Deferred service
- * handler
- */
- cc_iowrite(drvdata, CC_REG(HOST_IMR), imr | CC_GPR0_IRQ_MASK);
- irr &= ~CC_GPR0_IRQ_MASK;
- fips_handler(drvdata);
- }
-#endif
- /* AXI error interrupt */
- if (irr & CC_AXI_ERR_IRQ_MASK) {
- u32 axi_err;
-
- /* Read the AXI error ID */
- axi_err = cc_ioread(drvdata, CC_REG(AXIM_MON_ERR));
- dev_dbg(dev, "AXI completion error: axim_mon_err=0x%08X\n",
- axi_err);
-
- irr &= ~CC_AXI_ERR_IRQ_MASK;
- }
-
- if (irr) {
- dev_dbg(dev, "IRR includes unknown cause bits (0x%08X)\n",
- irr);
- /* Just warning */
- }
-
- return IRQ_HANDLED;
-}
-
-int init_cc_regs(struct cc_drvdata *drvdata, bool is_probe)
-{
- unsigned int val, cache_params;
- struct device *dev = drvdata_to_dev(drvdata);
-
- /* Unmask all AXI interrupt sources AXI_CFG1 register */
- val = cc_ioread(drvdata, CC_REG(AXIM_CFG));
- cc_iowrite(drvdata, CC_REG(AXIM_CFG), val & ~CC_AXI_IRQ_MASK);
- dev_dbg(dev, "AXIM_CFG=0x%08X\n",
- cc_ioread(drvdata, CC_REG(AXIM_CFG)));
-
- /* Clear all pending interrupts */
- val = cc_ioread(drvdata, CC_REG(HOST_IRR));
- dev_dbg(dev, "IRR=0x%08X\n", val);
- cc_iowrite(drvdata, CC_REG(HOST_ICR), val);
-
- /* Unmask relevant interrupt cause */
- val = (unsigned int)(~(CC_COMP_IRQ_MASK | CC_AXI_ERR_IRQ_MASK |
- CC_GPR0_IRQ_MASK));
- cc_iowrite(drvdata, CC_REG(HOST_IMR), val);
-
- cache_params = (drvdata->coherent ? CC_COHERENT_CACHE_PARAMS : 0x0);
-
- val = cc_ioread(drvdata, CC_REG(AXIM_CACHE_PARAMS));
-
- if (is_probe)
- dev_info(dev, "Cache params previous: 0x%08X\n", val);
-
- cc_iowrite(drvdata, CC_REG(AXIM_CACHE_PARAMS), cache_params);
- val = cc_ioread(drvdata, CC_REG(AXIM_CACHE_PARAMS));
-
- if (is_probe)
- dev_info(dev, "Cache params current: 0x%08X (expect: 0x%08X)\n",
- val, cache_params);
-
- return 0;
-}
-
-static int init_cc_resources(struct platform_device *plat_dev)
-{
- struct resource *req_mem_cc_regs = NULL;
- struct cc_drvdata *new_drvdata;
- struct device *dev = &plat_dev->dev;
- struct device_node *np = dev->of_node;
- u32 signature_val;
- u64 dma_mask;
- int rc = 0;
-
- new_drvdata = devm_kzalloc(dev, sizeof(*new_drvdata), GFP_KERNEL);
- if (!new_drvdata)
- return -ENOMEM;
-
- platform_set_drvdata(plat_dev, new_drvdata);
- new_drvdata->plat_dev = plat_dev;
-
- new_drvdata->clk = of_clk_get(np, 0);
- new_drvdata->coherent = of_dma_is_coherent(np);
-
- /* Get device resources */
- /* First CC registers space */
- req_mem_cc_regs = platform_get_resource(plat_dev, IORESOURCE_MEM, 0);
- /* Map registers space */
- new_drvdata->cc_base = devm_ioremap_resource(dev, req_mem_cc_regs);
- if (IS_ERR(new_drvdata->cc_base))
- return PTR_ERR(new_drvdata->cc_base);
-
- dev_dbg(dev, "Got MEM resource (%s): %pR\n", req_mem_cc_regs->name,
- req_mem_cc_regs);
- dev_dbg(dev, "CC registers mapped from %pa to 0x%p\n",
- &req_mem_cc_regs->start, new_drvdata->cc_base);
-
- /* Then IRQ */
- new_drvdata->irq = platform_get_irq(plat_dev, 0);
- if (new_drvdata->irq < 0) {
- dev_err(dev, "Failed getting IRQ resource\n");
- return new_drvdata->irq;
- }
-
- rc = devm_request_irq(dev, new_drvdata->irq, cc_isr,
- IRQF_SHARED, "arm_cc7x", new_drvdata);
- if (rc) {
- dev_err(dev, "Could not register to interrupt %d\n",
- new_drvdata->irq);
- return rc;
- }
- dev_dbg(dev, "Registered to IRQ: %d\n", new_drvdata->irq);
-
- init_completion(&new_drvdata->hw_queue_avail);
-
- if (!plat_dev->dev.dma_mask)
- plat_dev->dev.dma_mask = &plat_dev->dev.coherent_dma_mask;
-
- dma_mask = DMA_BIT_MASK(DMA_BIT_MASK_LEN);
- while (dma_mask > 0x7fffffffUL) {
- if (dma_supported(&plat_dev->dev, dma_mask)) {
- rc = dma_set_coherent_mask(&plat_dev->dev, dma_mask);
- if (!rc)
- break;
- }
- dma_mask >>= 1;
- }
-
- if (rc) {
- dev_err(dev, "Failed in dma_set_mask, mask=%par\n", &dma_mask);
- return rc;
- }
-
- rc = cc_clk_on(new_drvdata);
- if (rc) {
- dev_err(dev, "Failed to enable clock");
- return rc;
- }
-
- /* Verify correct mapping */
- signature_val = cc_ioread(new_drvdata, CC_REG(HOST_SIGNATURE));
- if (signature_val != CC_DEV_SIGNATURE) {
- dev_err(dev, "Invalid CC signature: SIGNATURE=0x%08X != expected=0x%08X\n",
- signature_val, (u32)CC_DEV_SIGNATURE);
- rc = -EINVAL;
- goto post_clk_err;
- }
- dev_dbg(dev, "CC SIGNATURE=0x%08X\n", signature_val);
-
- /* Display HW versions */
- dev_info(dev, "ARM CryptoCell %s Driver: HW version 0x%08X, Driver version %s\n",
- CC_DEV_NAME_STR,
- cc_ioread(new_drvdata, CC_REG(HOST_VERSION)),
- DRV_MODULE_VERSION);
-
- rc = init_cc_regs(new_drvdata, true);
- if (rc) {
- dev_err(dev, "init_cc_regs failed\n");
- goto post_clk_err;
- }
-
- rc = cc_debugfs_init(new_drvdata);
- if (rc) {
- dev_err(dev, "Failed registering debugfs interface\n");
- goto post_regs_err;
- }
-
- rc = cc_fips_init(new_drvdata);
- if (rc) {
- dev_err(dev, "CC_FIPS_INIT failed 0x%x\n", rc);
- goto post_debugfs_err;
- }
- rc = cc_sram_mgr_init(new_drvdata);
- if (rc) {
- dev_err(dev, "cc_sram_mgr_init failed\n");
- goto post_fips_init_err;
- }
-
- new_drvdata->mlli_sram_addr =
- cc_sram_alloc(new_drvdata, MAX_MLLI_BUFF_SIZE);
- if (new_drvdata->mlli_sram_addr == NULL_SRAM_ADDR) {
- dev_err(dev, "Failed to alloc MLLI Sram buffer\n");
- rc = -ENOMEM;
- goto post_sram_mgr_err;
- }
-
- rc = cc_req_mgr_init(new_drvdata);
- if (rc) {
- dev_err(dev, "cc_req_mgr_init failed\n");
- goto post_sram_mgr_err;
- }
-
- rc = cc_buffer_mgr_init(new_drvdata);
- if (rc) {
- dev_err(dev, "buffer_mgr_init failed\n");
- goto post_req_mgr_err;
- }
-
- rc = cc_pm_init(new_drvdata);
- if (rc) {
- dev_err(dev, "ssi_power_mgr_init failed\n");
- goto post_buf_mgr_err;
- }
-
- rc = cc_ivgen_init(new_drvdata);
- if (rc) {
- dev_err(dev, "cc_ivgen_init failed\n");
- goto post_power_mgr_err;
- }
-
- /* Allocate crypto algs */
- rc = cc_cipher_alloc(new_drvdata);
- if (rc) {
- dev_err(dev, "cc_cipher_alloc failed\n");
- goto post_ivgen_err;
- }
-
- /* hash must be allocated before aead since hash exports APIs */
- rc = cc_hash_alloc(new_drvdata);
- if (rc) {
- dev_err(dev, "cc_hash_alloc failed\n");
- goto post_cipher_err;
- }
-
- rc = cc_aead_alloc(new_drvdata);
- if (rc) {
- dev_err(dev, "cc_aead_alloc failed\n");
- goto post_hash_err;
- }
-
- /* If we got here and FIPS mode is enabled
- * it means all FIPS test passed, so let TEE
- * know we're good.
- */
- cc_set_ree_fips_status(new_drvdata, true);
-
- return 0;
-
-post_hash_err:
- cc_hash_free(new_drvdata);
-post_cipher_err:
- cc_cipher_free(new_drvdata);
-post_ivgen_err:
- cc_ivgen_fini(new_drvdata);
-post_power_mgr_err:
- cc_pm_fini(new_drvdata);
-post_buf_mgr_err:
- cc_buffer_mgr_fini(new_drvdata);
-post_req_mgr_err:
- cc_req_mgr_fini(new_drvdata);
-post_sram_mgr_err:
- cc_sram_mgr_fini(new_drvdata);
-post_fips_init_err:
- cc_fips_fini(new_drvdata);
-post_debugfs_err:
- cc_debugfs_fini(new_drvdata);
-post_regs_err:
- fini_cc_regs(new_drvdata);
-post_clk_err:
- cc_clk_off(new_drvdata);
- return rc;
-}
-
-void fini_cc_regs(struct cc_drvdata *drvdata)
-{
- /* Mask all interrupts */
- cc_iowrite(drvdata, CC_REG(HOST_IMR), 0xFFFFFFFF);
-}
-
-static void cleanup_cc_resources(struct platform_device *plat_dev)
-{
- struct cc_drvdata *drvdata =
- (struct cc_drvdata *)platform_get_drvdata(plat_dev);
-
- cc_aead_free(drvdata);
- cc_hash_free(drvdata);
- cc_cipher_free(drvdata);
- cc_ivgen_fini(drvdata);
- cc_pm_fini(drvdata);
- cc_buffer_mgr_fini(drvdata);
- cc_req_mgr_fini(drvdata);
- cc_sram_mgr_fini(drvdata);
- cc_fips_fini(drvdata);
- cc_debugfs_fini(drvdata);
- fini_cc_regs(drvdata);
- cc_clk_off(drvdata);
-}
-
-int cc_clk_on(struct cc_drvdata *drvdata)
-{
- struct clk *clk = drvdata->clk;
- int rc;
-
- if (IS_ERR(clk))
- /* Not all devices have a clock associated with CCREE */
- return 0;
-
- rc = clk_prepare_enable(clk);
- if (rc)
- return rc;
-
- return 0;
-}
-
-void cc_clk_off(struct cc_drvdata *drvdata)
-{
- struct clk *clk = drvdata->clk;
-
- if (IS_ERR(clk))
- /* Not all devices have a clock associated with CCREE */
- return;
-
- clk_disable_unprepare(clk);
-}
-
-static int cc7x_probe(struct platform_device *plat_dev)
-{
- int rc;
- struct device *dev = &plat_dev->dev;
-
- /* Map registers space */
- rc = init_cc_resources(plat_dev);
- if (rc)
- return rc;
-
- dev_info(dev, "ARM ccree device initialized\n");
-
- return 0;
-}
-
-static int cc7x_remove(struct platform_device *plat_dev)
-{
- struct device *dev = &plat_dev->dev;
-
- dev_dbg(dev, "Releasing cc7x resources...\n");
-
- cleanup_cc_resources(plat_dev);
-
- dev_info(dev, "ARM ccree device terminated\n");
-
- return 0;
-}
-
-static const struct of_device_id arm_cc7x_dev_of_match[] = {
- {.compatible = "arm,cryptocell-712-ree"},
- {}
-};
-MODULE_DEVICE_TABLE(of, arm_cc7x_dev_of_match);
-
-static struct platform_driver cc7x_driver = {
- .driver = {
- .name = "cc7xree",
- .of_match_table = arm_cc7x_dev_of_match,
-#ifdef CONFIG_PM
- .pm = &ccree_pm,
-#endif
- },
- .probe = cc7x_probe,
- .remove = cc7x_remove,
-};
-
-static int __init ccree_init(void)
-{
- int ret;
-
- cc_hash_global_init();
-
- ret = cc_debugfs_global_init();
- if (ret)
- return ret;
-
- return platform_driver_register(&cc7x_driver);
-}
-module_init(ccree_init);
-
-static void __exit ccree_exit(void)
-{
- platform_driver_unregister(&cc7x_driver);
- cc_debugfs_global_fini();
-}
-module_exit(ccree_exit);
-
-/* Module description */
-MODULE_DESCRIPTION("ARM TrustZone CryptoCell REE Driver");
-MODULE_VERSION(DRV_MODULE_VERSION);
-MODULE_AUTHOR("ARM");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/ccree/cc_driver.h b/drivers/staging/ccree/cc_driver.h
deleted file mode 100644
index 773ac591c45c..000000000000
--- a/drivers/staging/ccree/cc_driver.h
+++ /dev/null
@@ -1,194 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
-
-/* \file cc_driver.h
- * ARM CryptoCell Linux Crypto Driver
- */
-
-#ifndef __CC_DRIVER_H__
-#define __CC_DRIVER_H__
-
-#ifdef COMP_IN_WQ
-#include <linux/workqueue.h>
-#else
-#include <linux/interrupt.h>
-#endif
-#include <linux/dma-mapping.h>
-#include <crypto/algapi.h>
-#include <crypto/internal/skcipher.h>
-#include <crypto/aes.h>
-#include <crypto/sha.h>
-#include <crypto/aead.h>
-#include <crypto/authenc.h>
-#include <crypto/hash.h>
-#include <linux/version.h>
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-
-/* Registers definitions from shared/hw/ree_include */
-#include "cc_host_regs.h"
-#define CC_DEV_SHA_MAX 512
-#include "cc_crypto_ctx.h"
-#include "cc_hw_queue_defs.h"
-#include "cc_sram_mgr.h"
-
-extern bool cc_dump_desc;
-extern bool cc_dump_bytes;
-
-#define DRV_MODULE_VERSION "3.0"
-
-#define CC_DEV_NAME_STR "cc715ree"
-#define CC_COHERENT_CACHE_PARAMS 0xEEE
-
-/* Maximum DMA mask supported by IP */
-#define DMA_BIT_MASK_LEN 48
-
-#define CC_DEV_SIGNATURE 0xDCC71200UL
-
-#define CC_AXI_IRQ_MASK ((1 << CC_AXIM_CFG_BRESPMASK_BIT_SHIFT) | \
- (1 << CC_AXIM_CFG_RRESPMASK_BIT_SHIFT) | \
- (1 << CC_AXIM_CFG_INFLTMASK_BIT_SHIFT) | \
- (1 << CC_AXIM_CFG_COMPMASK_BIT_SHIFT))
-
-#define CC_AXI_ERR_IRQ_MASK BIT(CC_HOST_IRR_AXI_ERR_INT_BIT_SHIFT)
-
-#define CC_COMP_IRQ_MASK BIT(CC_HOST_IRR_AXIM_COMP_INT_BIT_SHIFT)
-
-#define AXIM_MON_COMP_VALUE GENMASK(CC_AXIM_MON_COMP_VALUE_BIT_SIZE + \
- CC_AXIM_MON_COMP_VALUE_BIT_SHIFT, \
- CC_AXIM_MON_COMP_VALUE_BIT_SHIFT)
-
-/* Register name mangling macro */
-#define CC_REG(reg_name) CC_ ## reg_name ## _REG_OFFSET
-
-/* TEE FIPS status interrupt */
-#define CC_GPR0_IRQ_MASK BIT(CC_HOST_IRR_GPR0_BIT_SHIFT)
-
-#define CC_CRA_PRIO 3000
-
-#define MIN_HW_QUEUE_SIZE 50 /* Minimum size required for proper function */
-
-#define MAX_REQUEST_QUEUE_SIZE 4096
-#define MAX_MLLI_BUFF_SIZE 2080
-#define MAX_ICV_NENTS_SUPPORTED 2
-
-/* Definitions for HW descriptors DIN/DOUT fields */
-#define NS_BIT 1
-#define AXI_ID 0
-/* AXI_ID is not actually the AXI ID of the transaction but the value of AXI_ID
- * field in the HW descriptor. The DMA engine +8 that value.
- */
-
-#define CC_MAX_IVGEN_DMA_ADDRESSES 3
-struct cc_crypto_req {
- void (*user_cb)(struct device *dev, void *req, int err);
- void *user_arg;
- dma_addr_t ivgen_dma_addr[CC_MAX_IVGEN_DMA_ADDRESSES];
- /* For the first 'ivgen_dma_addr_len' addresses of this array,
- * generated IV would be placed in it by send_request().
- * Same generated IV for all addresses!
- */
- /* Amount of 'ivgen_dma_addr' elements to be filled. */
- unsigned int ivgen_dma_addr_len;
- /* The generated IV size required, 8/16 B allowed. */
- unsigned int ivgen_size;
- struct completion seq_compl; /* request completion */
-};
-
-/**
- * struct cc_drvdata - driver private data context
- * @cc_base: virt address of the CC registers
- * @irq: device IRQ number
- * @irq_mask: Interrupt mask shadow (1 for masked interrupts)
- * @fw_ver: SeP loaded firmware version
- */
-struct cc_drvdata {
- void __iomem *cc_base;
- int irq;
- u32 irq_mask;
- u32 fw_ver;
- struct completion hw_queue_avail; /* wait for HW queue availability */
- struct platform_device *plat_dev;
- cc_sram_addr_t mlli_sram_addr;
- void *buff_mgr_handle;
- void *hash_handle;
- void *aead_handle;
- void *blkcipher_handle;
- void *request_mgr_handle;
- void *fips_handle;
- void *ivgen_handle;
- void *sram_mgr_handle;
- void *debugfs;
- struct clk *clk;
- bool coherent;
-};
-
-struct cc_crypto_alg {
- struct list_head entry;
- int cipher_mode;
- int flow_mode; /* Note: currently, refers to the cipher mode only. */
- int auth_mode;
- struct cc_drvdata *drvdata;
- struct crypto_alg crypto_alg;
- struct aead_alg aead_alg;
-};
-
-struct cc_alg_template {
- char name[CRYPTO_MAX_ALG_NAME];
- char driver_name[CRYPTO_MAX_ALG_NAME];
- unsigned int blocksize;
- u32 type;
- union {
- struct ablkcipher_alg ablkcipher;
- struct aead_alg aead;
- struct blkcipher_alg blkcipher;
- struct cipher_alg cipher;
- struct compress_alg compress;
- } template_u;
- int cipher_mode;
- int flow_mode; /* Note: currently, refers to the cipher mode only. */
- int auth_mode;
- struct cc_drvdata *drvdata;
-};
-
-struct async_gen_req_ctx {
- dma_addr_t iv_dma_addr;
- enum drv_crypto_direction op_type;
-};
-
-static inline struct device *drvdata_to_dev(struct cc_drvdata *drvdata)
-{
- return &drvdata->plat_dev->dev;
-}
-
-void __dump_byte_array(const char *name, const u8 *buf, size_t len);
-static inline void dump_byte_array(const char *name, const u8 *the_array,
- size_t size)
-{
- if (cc_dump_bytes)
- __dump_byte_array(name, the_array, size);
-}
-
-int init_cc_regs(struct cc_drvdata *drvdata, bool is_probe);
-void fini_cc_regs(struct cc_drvdata *drvdata);
-int cc_clk_on(struct cc_drvdata *drvdata);
-void cc_clk_off(struct cc_drvdata *drvdata);
-
-static inline void cc_iowrite(struct cc_drvdata *drvdata, u32 reg, u32 val)
-{
- iowrite32(val, (drvdata->cc_base + reg));
-}
-
-static inline u32 cc_ioread(struct cc_drvdata *drvdata, u32 reg)
-{
- return ioread32(drvdata->cc_base + reg);
-}
-
-static inline gfp_t cc_gfp_flags(struct crypto_async_request *req)
-{
- return (req->flags & CRYPTO_TFM_REQ_MAY_SLEEP) ?
- GFP_KERNEL : GFP_ATOMIC;
-}
-
-#endif /*__CC_DRIVER_H__*/
-
diff --git a/drivers/staging/ccree/cc_fips.c b/drivers/staging/ccree/cc_fips.c
deleted file mode 100644
index de08af976b7f..000000000000
--- a/drivers/staging/ccree/cc_fips.c
+++ /dev/null
@@ -1,111 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
-
-#include <linux/kernel.h>
-#include <linux/fips.h>
-
-#include "cc_driver.h"
-#include "cc_fips.h"
-
-static void fips_dsr(unsigned long devarg);
-
-struct cc_fips_handle {
- struct tasklet_struct tasklet;
-};
-
-/* The function called once at driver entry point to check
- * whether TEE FIPS error occurred.
- */
-static bool cc_get_tee_fips_status(struct cc_drvdata *drvdata)
-{
- u32 reg;
-
- reg = cc_ioread(drvdata, CC_REG(GPR_HOST));
- return (reg == (CC_FIPS_SYNC_TEE_STATUS | CC_FIPS_SYNC_MODULE_OK));
-}
-
-/*
- * This function should push the FIPS REE library status towards the TEE library
- * by writing the error state to HOST_GPR0 register.
- */
-void cc_set_ree_fips_status(struct cc_drvdata *drvdata, bool status)
-{
- int val = CC_FIPS_SYNC_REE_STATUS;
-
- val |= (status ? CC_FIPS_SYNC_MODULE_OK : CC_FIPS_SYNC_MODULE_ERROR);
-
- cc_iowrite(drvdata, CC_REG(HOST_GPR0), val);
-}
-
-void cc_fips_fini(struct cc_drvdata *drvdata)
-{
- struct cc_fips_handle *fips_h = drvdata->fips_handle;
-
- if (!fips_h)
- return; /* Not allocated */
-
- /* Kill tasklet */
- tasklet_kill(&fips_h->tasklet);
-
- kfree(fips_h);
- drvdata->fips_handle = NULL;
-}
-
-void fips_handler(struct cc_drvdata *drvdata)
-{
- struct cc_fips_handle *fips_handle_ptr = drvdata->fips_handle;
-
- tasklet_schedule(&fips_handle_ptr->tasklet);
-}
-
-static inline void tee_fips_error(struct device *dev)
-{
- if (fips_enabled)
- panic("ccree: TEE reported cryptographic error in fips mode!\n");
- else
- dev_err(dev, "TEE reported error!\n");
-}
-
-/* Deferred service handler, run as interrupt-fired tasklet */
-static void fips_dsr(unsigned long devarg)
-{
- struct cc_drvdata *drvdata = (struct cc_drvdata *)devarg;
- struct device *dev = drvdata_to_dev(drvdata);
- u32 irq, state, val;
-
- irq = (drvdata->irq & (CC_GPR0_IRQ_MASK));
-
- if (irq) {
- state = cc_ioread(drvdata, CC_REG(GPR_HOST));
-
- if (state != (CC_FIPS_SYNC_TEE_STATUS | CC_FIPS_SYNC_MODULE_OK))
- tee_fips_error(dev);
- }
-
- /* after verifing that there is nothing to do,
- * unmask AXI completion interrupt.
- */
- val = (CC_REG(HOST_IMR) & ~irq);
- cc_iowrite(drvdata, CC_REG(HOST_IMR), val);
-}
-
-/* The function called once at driver entry point .*/
-int cc_fips_init(struct cc_drvdata *p_drvdata)
-{
- struct cc_fips_handle *fips_h;
- struct device *dev = drvdata_to_dev(p_drvdata);
-
- fips_h = kzalloc(sizeof(*fips_h), GFP_KERNEL);
- if (!fips_h)
- return -ENOMEM;
-
- p_drvdata->fips_handle = fips_h;
-
- dev_dbg(dev, "Initializing fips tasklet\n");
- tasklet_init(&fips_h->tasklet, fips_dsr, (unsigned long)p_drvdata);
-
- if (!cc_get_tee_fips_status(p_drvdata))
- tee_fips_error(dev);
-
- return 0;
-}
diff --git a/drivers/staging/ccree/cc_fips.h b/drivers/staging/ccree/cc_fips.h
deleted file mode 100644
index 0d520030095b..000000000000
--- a/drivers/staging/ccree/cc_fips.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
-
-#ifndef __CC_FIPS_H__
-#define __CC_FIPS_H__
-
-#ifdef CONFIG_CRYPTO_FIPS
-
-enum cc_fips_status {
- CC_FIPS_SYNC_MODULE_OK = 0x0,
- CC_FIPS_SYNC_MODULE_ERROR = 0x1,
- CC_FIPS_SYNC_REE_STATUS = 0x4,
- CC_FIPS_SYNC_TEE_STATUS = 0x8,
- CC_FIPS_SYNC_STATUS_RESERVE32B = S32_MAX
-};
-
-int cc_fips_init(struct cc_drvdata *p_drvdata);
-void cc_fips_fini(struct cc_drvdata *drvdata);
-void fips_handler(struct cc_drvdata *drvdata);
-void cc_set_ree_fips_status(struct cc_drvdata *drvdata, bool ok);
-
-#else /* CONFIG_CRYPTO_FIPS */
-
-static inline int cc_fips_init(struct cc_drvdata *p_drvdata)
-{
- return 0;
-}
-
-static inline void cc_fips_fini(struct cc_drvdata *drvdata) {}
-static inline void cc_set_ree_fips_status(struct cc_drvdata *drvdata,
- bool ok) {}
-static inline void fips_handler(struct cc_drvdata *drvdata) {}
-
-#endif /* CONFIG_CRYPTO_FIPS */
-
-#endif /*__CC_FIPS_H__*/
-
diff --git a/drivers/staging/ccree/cc_hash.c b/drivers/staging/ccree/cc_hash.c
deleted file mode 100644
index 8afc39f10bb3..000000000000
--- a/drivers/staging/ccree/cc_hash.c
+++ /dev/null
@@ -1,2295 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <crypto/algapi.h>
-#include <crypto/hash.h>
-#include <crypto/md5.h>
-#include <crypto/internal/hash.h>
-
-#include "cc_driver.h"
-#include "cc_request_mgr.h"
-#include "cc_buffer_mgr.h"
-#include "cc_hash.h"
-#include "cc_sram_mgr.h"
-
-#define CC_MAX_HASH_SEQ_LEN 12
-#define CC_MAX_OPAD_KEYS_SIZE CC_MAX_HASH_BLCK_SIZE
-
-struct cc_hash_handle {
- cc_sram_addr_t digest_len_sram_addr; /* const value in SRAM*/
- cc_sram_addr_t larval_digest_sram_addr; /* const value in SRAM */
- struct list_head hash_list;
-};
-
-static const u32 digest_len_init[] = {
- 0x00000040, 0x00000000, 0x00000000, 0x00000000 };
-static const u32 md5_init[] = {
- SHA1_H3, SHA1_H2, SHA1_H1, SHA1_H0 };
-static const u32 sha1_init[] = {
- SHA1_H4, SHA1_H3, SHA1_H2, SHA1_H1, SHA1_H0 };
-static const u32 sha224_init[] = {
- SHA224_H7, SHA224_H6, SHA224_H5, SHA224_H4,
- SHA224_H3, SHA224_H2, SHA224_H1, SHA224_H0 };
-static const u32 sha256_init[] = {
- SHA256_H7, SHA256_H6, SHA256_H5, SHA256_H4,
- SHA256_H3, SHA256_H2, SHA256_H1, SHA256_H0 };
-#if (CC_DEV_SHA_MAX > 256)
-static const u32 digest_len_sha512_init[] = {
- 0x00000080, 0x00000000, 0x00000000, 0x00000000 };
-static u64 sha384_init[] = {
- SHA384_H7, SHA384_H6, SHA384_H5, SHA384_H4,
- SHA384_H3, SHA384_H2, SHA384_H1, SHA384_H0 };
-static u64 sha512_init[] = {
- SHA512_H7, SHA512_H6, SHA512_H5, SHA512_H4,
- SHA512_H3, SHA512_H2, SHA512_H1, SHA512_H0 };
-#endif
-
-static void cc_setup_xcbc(struct ahash_request *areq, struct cc_hw_desc desc[],
- unsigned int *seq_size);
-
-static void cc_setup_cmac(struct ahash_request *areq, struct cc_hw_desc desc[],
- unsigned int *seq_size);
-
-static const void *cc_larval_digest(struct device *dev, u32 mode);
-
-struct cc_hash_alg {
- struct list_head entry;
- int hash_mode;
- int hw_mode;
- int inter_digestsize;
- struct cc_drvdata *drvdata;
- struct ahash_alg ahash_alg;
-};
-
-struct hash_key_req_ctx {
- u32 keylen;
- dma_addr_t key_dma_addr;
-};
-
-/* hash per-session context */
-struct cc_hash_ctx {
- struct cc_drvdata *drvdata;
- /* holds the origin digest; the digest after "setkey" if HMAC,*
- * the initial digest if HASH.
- */
- u8 digest_buff[CC_MAX_HASH_DIGEST_SIZE] ____cacheline_aligned;
- u8 opad_tmp_keys_buff[CC_MAX_OPAD_KEYS_SIZE] ____cacheline_aligned;
-
- dma_addr_t opad_tmp_keys_dma_addr ____cacheline_aligned;
- dma_addr_t digest_buff_dma_addr;
- /* use for hmac with key large then mode block size */
- struct hash_key_req_ctx key_params;
- int hash_mode;
- int hw_mode;
- int inter_digestsize;
- struct completion setkey_comp;
- bool is_hmac;
-};
-
-static void cc_set_desc(struct ahash_req_ctx *areq_ctx, struct cc_hash_ctx *ctx,
- unsigned int flow_mode, struct cc_hw_desc desc[],
- bool is_not_last_data, unsigned int *seq_size);
-
-static void cc_set_endianity(u32 mode, struct cc_hw_desc *desc)
-{
- if (mode == DRV_HASH_MD5 || mode == DRV_HASH_SHA384 ||
- mode == DRV_HASH_SHA512) {
- set_bytes_swap(desc, 1);
- } else {
- set_cipher_config0(desc, HASH_DIGEST_RESULT_LITTLE_ENDIAN);
- }
-}
-
-static int cc_map_result(struct device *dev, struct ahash_req_ctx *state,
- unsigned int digestsize)
-{
- state->digest_result_dma_addr =
- dma_map_single(dev, state->digest_result_buff,
- digestsize, DMA_BIDIRECTIONAL);
- if (dma_mapping_error(dev, state->digest_result_dma_addr)) {
- dev_err(dev, "Mapping digest result buffer %u B for DMA failed\n",
- digestsize);
- return -ENOMEM;
- }
- dev_dbg(dev, "Mapped digest result buffer %u B at va=%pK to dma=%pad\n",
- digestsize, state->digest_result_buff,
- &state->digest_result_dma_addr);
-
- return 0;
-}
-
-static void cc_init_req(struct device *dev, struct ahash_req_ctx *state,
- struct cc_hash_ctx *ctx)
-{
- bool is_hmac = ctx->is_hmac;
-
- memset(state, 0, sizeof(*state));
-
- if (is_hmac) {
- if (ctx->hw_mode != DRV_CIPHER_XCBC_MAC &&
- ctx->hw_mode != DRV_CIPHER_CMAC) {
- dma_sync_single_for_cpu(dev, ctx->digest_buff_dma_addr,
- ctx->inter_digestsize,
- DMA_BIDIRECTIONAL);
-
- memcpy(state->digest_buff, ctx->digest_buff,
- ctx->inter_digestsize);
-#if (CC_DEV_SHA_MAX > 256)
- if (ctx->hash_mode == DRV_HASH_SHA512 ||
- ctx->hash_mode == DRV_HASH_SHA384)
- memcpy(state->digest_bytes_len,
- digest_len_sha512_init, HASH_LEN_SIZE);
- else
- memcpy(state->digest_bytes_len,
- digest_len_init, HASH_LEN_SIZE);
-#else
- memcpy(state->digest_bytes_len, digest_len_init,
- HASH_LEN_SIZE);
-#endif
- }
-
- if (ctx->hash_mode != DRV_HASH_NULL) {
- dma_sync_single_for_cpu(dev,
- ctx->opad_tmp_keys_dma_addr,
- ctx->inter_digestsize,
- DMA_BIDIRECTIONAL);
- memcpy(state->opad_digest_buff,
- ctx->opad_tmp_keys_buff, ctx->inter_digestsize);
- }
- } else { /*hash*/
- /* Copy the initial digests if hash flow. */
- const void *larval = cc_larval_digest(dev, ctx->hash_mode);
-
- memcpy(state->digest_buff, larval, ctx->inter_digestsize);
- }
-}
-
-static int cc_map_req(struct device *dev, struct ahash_req_ctx *state,
- struct cc_hash_ctx *ctx)
-{
- bool is_hmac = ctx->is_hmac;
-
- state->digest_buff_dma_addr =
- dma_map_single(dev, state->digest_buff,
- ctx->inter_digestsize, DMA_BIDIRECTIONAL);
- if (dma_mapping_error(dev, state->digest_buff_dma_addr)) {
- dev_err(dev, "Mapping digest len %d B at va=%pK for DMA failed\n",
- ctx->inter_digestsize, state->digest_buff);
- return -EINVAL;
- }
- dev_dbg(dev, "Mapped digest %d B at va=%pK to dma=%pad\n",
- ctx->inter_digestsize, state->digest_buff,
- &state->digest_buff_dma_addr);
-
- if (ctx->hw_mode != DRV_CIPHER_XCBC_MAC) {
- state->digest_bytes_len_dma_addr =
- dma_map_single(dev, state->digest_bytes_len,
- HASH_LEN_SIZE, DMA_BIDIRECTIONAL);
- if (dma_mapping_error(dev, state->digest_bytes_len_dma_addr)) {
- dev_err(dev, "Mapping digest len %u B at va=%pK for DMA failed\n",
- HASH_LEN_SIZE, state->digest_bytes_len);
- goto unmap_digest_buf;
- }
- dev_dbg(dev, "Mapped digest len %u B at va=%pK to dma=%pad\n",
- HASH_LEN_SIZE, state->digest_bytes_len,
- &state->digest_bytes_len_dma_addr);
- }
-
- if (is_hmac && ctx->hash_mode != DRV_HASH_NULL) {
- state->opad_digest_dma_addr =
- dma_map_single(dev, state->opad_digest_buff,
- ctx->inter_digestsize,
- DMA_BIDIRECTIONAL);
- if (dma_mapping_error(dev, state->opad_digest_dma_addr)) {
- dev_err(dev, "Mapping opad digest %d B at va=%pK for DMA failed\n",
- ctx->inter_digestsize,
- state->opad_digest_buff);
- goto unmap_digest_len;
- }
- dev_dbg(dev, "Mapped opad digest %d B at va=%pK to dma=%pad\n",
- ctx->inter_digestsize, state->opad_digest_buff,
- &state->opad_digest_dma_addr);
- }
-
- return 0;
-
-unmap_digest_len:
- if (state->digest_bytes_len_dma_addr) {
- dma_unmap_single(dev, state->digest_bytes_len_dma_addr,
- HASH_LEN_SIZE, DMA_BIDIRECTIONAL);
- state->digest_bytes_len_dma_addr = 0;
- }
-unmap_digest_buf:
- if (state->digest_buff_dma_addr) {
- dma_unmap_single(dev, state->digest_buff_dma_addr,
- ctx->inter_digestsize, DMA_BIDIRECTIONAL);
- state->digest_buff_dma_addr = 0;
- }
-
- return -EINVAL;
-}
-
-static void cc_unmap_req(struct device *dev, struct ahash_req_ctx *state,
- struct cc_hash_ctx *ctx)
-{
- if (state->digest_buff_dma_addr) {
- dma_unmap_single(dev, state->digest_buff_dma_addr,
- ctx->inter_digestsize, DMA_BIDIRECTIONAL);
- dev_dbg(dev, "Unmapped digest-buffer: digest_buff_dma_addr=%pad\n",
- &state->digest_buff_dma_addr);
- state->digest_buff_dma_addr = 0;
- }
- if (state->digest_bytes_len_dma_addr) {
- dma_unmap_single(dev, state->digest_bytes_len_dma_addr,
- HASH_LEN_SIZE, DMA_BIDIRECTIONAL);
- dev_dbg(dev, "Unmapped digest-bytes-len buffer: digest_bytes_len_dma_addr=%pad\n",
- &state->digest_bytes_len_dma_addr);
- state->digest_bytes_len_dma_addr = 0;
- }
- if (state->opad_digest_dma_addr) {
- dma_unmap_single(dev, state->opad_digest_dma_addr,
- ctx->inter_digestsize, DMA_BIDIRECTIONAL);
- dev_dbg(dev, "Unmapped opad-digest: opad_digest_dma_addr=%pad\n",
- &state->opad_digest_dma_addr);
- state->opad_digest_dma_addr = 0;
- }
-}
-
-static void cc_unmap_result(struct device *dev, struct ahash_req_ctx *state,
- unsigned int digestsize, u8 *result)
-{
- if (state->digest_result_dma_addr) {
- dma_unmap_single(dev, state->digest_result_dma_addr, digestsize,
- DMA_BIDIRECTIONAL);
- dev_dbg(dev, "unmpa digest result buffer va (%pK) pa (%pad) len %u\n",
- state->digest_result_buff,
- &state->digest_result_dma_addr, digestsize);
- memcpy(result, state->digest_result_buff, digestsize);
- }
- state->digest_result_dma_addr = 0;
-}
-
-static void cc_update_complete(struct device *dev, void *cc_req, int err)
-{
- struct ahash_request *req = (struct ahash_request *)cc_req;
- struct ahash_req_ctx *state = ahash_request_ctx(req);
- struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
- struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
-
- dev_dbg(dev, "req=%pK\n", req);
-
- cc_unmap_hash_request(dev, state, req->src, false);
- cc_unmap_req(dev, state, ctx);
- req->base.complete(&req->base, err);
-}
-
-static void cc_digest_complete(struct device *dev, void *cc_req, int err)
-{
- struct ahash_request *req = (struct ahash_request *)cc_req;
- struct ahash_req_ctx *state = ahash_request_ctx(req);
- struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
- struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
- u32 digestsize = crypto_ahash_digestsize(tfm);
-
- dev_dbg(dev, "req=%pK\n", req);
-
- cc_unmap_hash_request(dev, state, req->src, false);
- cc_unmap_result(dev, state, digestsize, req->result);
- cc_unmap_req(dev, state, ctx);
- req->base.complete(&req->base, err);
-}
-
-static void cc_hash_complete(struct device *dev, void *cc_req, int err)
-{
- struct ahash_request *req = (struct ahash_request *)cc_req;
- struct ahash_req_ctx *state = ahash_request_ctx(req);
- struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
- struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
- u32 digestsize = crypto_ahash_digestsize(tfm);
-
- dev_dbg(dev, "req=%pK\n", req);
-
- cc_unmap_hash_request(dev, state, req->src, false);
- cc_unmap_result(dev, state, digestsize, req->result);
- cc_unmap_req(dev, state, ctx);
- req->base.complete(&req->base, err);
-}
-
-static int cc_fin_result(struct cc_hw_desc *desc, struct ahash_request *req,
- int idx)
-{
- struct ahash_req_ctx *state = ahash_request_ctx(req);
- struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
- struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
- u32 digestsize = crypto_ahash_digestsize(tfm);
-
- /* Get final MAC result */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], ctx->hw_mode);
- /* TODO */
- set_dout_dlli(&desc[idx], state->digest_result_dma_addr, digestsize,
- NS_BIT, 1);
- set_queue_last_ind(&desc[idx]);
- set_flow_mode(&desc[idx], S_HASH_to_DOUT);
- set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
- set_cipher_config1(&desc[idx], HASH_PADDING_DISABLED);
- cc_set_endianity(ctx->hash_mode, &desc[idx]);
- idx++;
-
- return idx;
-}
-
-static int cc_fin_hmac(struct cc_hw_desc *desc, struct ahash_request *req,
- int idx)
-{
- struct ahash_req_ctx *state = ahash_request_ctx(req);
- struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
- struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
- u32 digestsize = crypto_ahash_digestsize(tfm);
-
- /* store the hash digest result in the context */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], ctx->hw_mode);
- set_dout_dlli(&desc[idx], state->digest_buff_dma_addr, digestsize,
- NS_BIT, 0);
- set_flow_mode(&desc[idx], S_HASH_to_DOUT);
- cc_set_endianity(ctx->hash_mode, &desc[idx]);
- set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
- idx++;
-
- /* Loading hash opad xor key state */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], ctx->hw_mode);
- set_din_type(&desc[idx], DMA_DLLI, state->opad_digest_dma_addr,
- ctx->inter_digestsize, NS_BIT);
- set_flow_mode(&desc[idx], S_DIN_to_HASH);
- set_setup_mode(&desc[idx], SETUP_LOAD_STATE0);
- idx++;
-
- /* Load the hash current length */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], ctx->hw_mode);
- set_din_sram(&desc[idx],
- cc_digest_len_addr(ctx->drvdata, ctx->hash_mode),
- HASH_LEN_SIZE);
- set_cipher_config1(&desc[idx], HASH_PADDING_ENABLED);
- set_flow_mode(&desc[idx], S_DIN_to_HASH);
- set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
- idx++;
-
- /* Memory Barrier: wait for IPAD/OPAD axi write to complete */
- hw_desc_init(&desc[idx]);
- set_din_no_dma(&desc[idx], 0, 0xfffff0);
- set_dout_no_dma(&desc[idx], 0, 0, 1);
- idx++;
-
- /* Perform HASH update */
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_DLLI, state->digest_buff_dma_addr,
- digestsize, NS_BIT);
- set_flow_mode(&desc[idx], DIN_HASH);
- idx++;
-
- return idx;
-}
-
-static int cc_hash_digest(struct ahash_request *req)
-{
- struct ahash_req_ctx *state = ahash_request_ctx(req);
- struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
- struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
- u32 digestsize = crypto_ahash_digestsize(tfm);
- struct scatterlist *src = req->src;
- unsigned int nbytes = req->nbytes;
- u8 *result = req->result;
- struct device *dev = drvdata_to_dev(ctx->drvdata);
- bool is_hmac = ctx->is_hmac;
- struct cc_crypto_req cc_req = {};
- struct cc_hw_desc desc[CC_MAX_HASH_SEQ_LEN];
- cc_sram_addr_t larval_digest_addr =
- cc_larval_digest_addr(ctx->drvdata, ctx->hash_mode);
- int idx = 0;
- int rc = 0;
- gfp_t flags = cc_gfp_flags(&req->base);
-
- dev_dbg(dev, "===== %s-digest (%d) ====\n", is_hmac ? "hmac" : "hash",
- nbytes);
-
- cc_init_req(dev, state, ctx);
-
- if (cc_map_req(dev, state, ctx)) {
- dev_err(dev, "map_ahash_source() failed\n");
- return -ENOMEM;
- }
-
- if (cc_map_result(dev, state, digestsize)) {
- dev_err(dev, "map_ahash_digest() failed\n");
- cc_unmap_req(dev, state, ctx);
- return -ENOMEM;
- }
-
- if (cc_map_hash_request_final(ctx->drvdata, state, src, nbytes, 1,
- flags)) {
- dev_err(dev, "map_ahash_request_final() failed\n");
- cc_unmap_result(dev, state, digestsize, result);
- cc_unmap_req(dev, state, ctx);
- return -ENOMEM;
- }
-
- /* Setup DX request structure */
- cc_req.user_cb = cc_digest_complete;
- cc_req.user_arg = req;
-
- /* If HMAC then load hash IPAD xor key, if HASH then load initial
- * digest
- */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], ctx->hw_mode);
- if (is_hmac) {
- set_din_type(&desc[idx], DMA_DLLI, state->digest_buff_dma_addr,
- ctx->inter_digestsize, NS_BIT);
- } else {
- set_din_sram(&desc[idx], larval_digest_addr,
- ctx->inter_digestsize);
- }
- set_flow_mode(&desc[idx], S_DIN_to_HASH);
- set_setup_mode(&desc[idx], SETUP_LOAD_STATE0);
- idx++;
-
- /* Load the hash current length */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], ctx->hw_mode);
-
- if (is_hmac) {
- set_din_type(&desc[idx], DMA_DLLI,
- state->digest_bytes_len_dma_addr, HASH_LEN_SIZE,
- NS_BIT);
- } else {
- set_din_const(&desc[idx], 0, HASH_LEN_SIZE);
- if (nbytes)
- set_cipher_config1(&desc[idx], HASH_PADDING_ENABLED);
- else
- set_cipher_do(&desc[idx], DO_PAD);
- }
- set_flow_mode(&desc[idx], S_DIN_to_HASH);
- set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
- idx++;
-
- cc_set_desc(state, ctx, DIN_HASH, desc, false, &idx);
-
- if (is_hmac) {
- /* HW last hash block padding (aka. "DO_PAD") */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], ctx->hw_mode);
- set_dout_dlli(&desc[idx], state->digest_buff_dma_addr,
- HASH_LEN_SIZE, NS_BIT, 0);
- set_flow_mode(&desc[idx], S_HASH_to_DOUT);
- set_setup_mode(&desc[idx], SETUP_WRITE_STATE1);
- set_cipher_do(&desc[idx], DO_PAD);
- idx++;
-
- idx = cc_fin_hmac(desc, req, idx);
- }
-
- idx = cc_fin_result(desc, req, idx);
-
- rc = cc_send_request(ctx->drvdata, &cc_req, desc, idx, &req->base);
- if (rc != -EINPROGRESS && rc != -EBUSY) {
- dev_err(dev, "send_request() failed (rc=%d)\n", rc);
- cc_unmap_hash_request(dev, state, src, true);
- cc_unmap_result(dev, state, digestsize, result);
- cc_unmap_req(dev, state, ctx);
- }
- return rc;
-}
-
-static int cc_restore_hash(struct cc_hw_desc *desc, struct cc_hash_ctx *ctx,
- struct ahash_req_ctx *state, int idx)
-{
- /* Restore hash digest */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], ctx->hw_mode);
- set_din_type(&desc[idx], DMA_DLLI, state->digest_buff_dma_addr,
- ctx->inter_digestsize, NS_BIT);
- set_flow_mode(&desc[idx], S_DIN_to_HASH);
- set_setup_mode(&desc[idx], SETUP_LOAD_STATE0);
- idx++;
-
- /* Restore hash current length */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], ctx->hw_mode);
- set_cipher_config1(&desc[idx], HASH_PADDING_DISABLED);
- set_din_type(&desc[idx], DMA_DLLI, state->digest_bytes_len_dma_addr,
- HASH_LEN_SIZE, NS_BIT);
- set_flow_mode(&desc[idx], S_DIN_to_HASH);
- set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
- idx++;
-
- cc_set_desc(state, ctx, DIN_HASH, desc, false, &idx);
-
- return idx;
-}
-
-static int cc_hash_update(struct ahash_request *req)
-{
- struct ahash_req_ctx *state = ahash_request_ctx(req);
- struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
- struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
- unsigned int block_size = crypto_tfm_alg_blocksize(&tfm->base);
- struct scatterlist *src = req->src;
- unsigned int nbytes = req->nbytes;
- struct device *dev = drvdata_to_dev(ctx->drvdata);
- struct cc_crypto_req cc_req = {};
- struct cc_hw_desc desc[CC_MAX_HASH_SEQ_LEN];
- u32 idx = 0;
- int rc;
- gfp_t flags = cc_gfp_flags(&req->base);
-
- dev_dbg(dev, "===== %s-update (%d) ====\n", ctx->is_hmac ?
- "hmac" : "hash", nbytes);
-
- if (nbytes == 0) {
- /* no real updates required */
- return 0;
- }
-
- rc = cc_map_hash_request_update(ctx->drvdata, state, src, nbytes,
- block_size, flags);
- if (rc) {
- if (rc == 1) {
- dev_dbg(dev, " data size not require HW update %x\n",
- nbytes);
- /* No hardware updates are required */
- return 0;
- }
- dev_err(dev, "map_ahash_request_update() failed\n");
- return -ENOMEM;
- }
-
- if (cc_map_req(dev, state, ctx)) {
- dev_err(dev, "map_ahash_source() failed\n");
- cc_unmap_hash_request(dev, state, src, true);
- return -EINVAL;
- }
-
- /* Setup DX request structure */
- cc_req.user_cb = cc_update_complete;
- cc_req.user_arg = req;
-
- idx = cc_restore_hash(desc, ctx, state, idx);
-
- /* store the hash digest result in context */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], ctx->hw_mode);
- set_dout_dlli(&desc[idx], state->digest_buff_dma_addr,
- ctx->inter_digestsize, NS_BIT, 0);
- set_flow_mode(&desc[idx], S_HASH_to_DOUT);
- set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
- idx++;
-
- /* store current hash length in context */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], ctx->hw_mode);
- set_dout_dlli(&desc[idx], state->digest_bytes_len_dma_addr,
- HASH_LEN_SIZE, NS_BIT, 1);
- set_queue_last_ind(&desc[idx]);
- set_flow_mode(&desc[idx], S_HASH_to_DOUT);
- set_setup_mode(&desc[idx], SETUP_WRITE_STATE1);
- idx++;
-
- rc = cc_send_request(ctx->drvdata, &cc_req, desc, idx, &req->base);
- if (rc != -EINPROGRESS && rc != -EBUSY) {
- dev_err(dev, "send_request() failed (rc=%d)\n", rc);
- cc_unmap_hash_request(dev, state, src, true);
- cc_unmap_req(dev, state, ctx);
- }
- return rc;
-}
-
-static int cc_hash_finup(struct ahash_request *req)
-{
- struct ahash_req_ctx *state = ahash_request_ctx(req);
- struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
- struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
- u32 digestsize = crypto_ahash_digestsize(tfm);
- struct scatterlist *src = req->src;
- unsigned int nbytes = req->nbytes;
- u8 *result = req->result;
- struct device *dev = drvdata_to_dev(ctx->drvdata);
- bool is_hmac = ctx->is_hmac;
- struct cc_crypto_req cc_req = {};
- struct cc_hw_desc desc[CC_MAX_HASH_SEQ_LEN];
- int idx = 0;
- int rc;
- gfp_t flags = cc_gfp_flags(&req->base);
-
- dev_dbg(dev, "===== %s-finup (%d) ====\n", is_hmac ? "hmac" : "hash",
- nbytes);
-
- if (cc_map_req(dev, state, ctx)) {
- dev_err(dev, "map_ahash_source() failed\n");
- return -EINVAL;
- }
-
- if (cc_map_hash_request_final(ctx->drvdata, state, src, nbytes, 1,
- flags)) {
- dev_err(dev, "map_ahash_request_final() failed\n");
- cc_unmap_req(dev, state, ctx);
- return -ENOMEM;
- }
- if (cc_map_result(dev, state, digestsize)) {
- dev_err(dev, "map_ahash_digest() failed\n");
- cc_unmap_hash_request(dev, state, src, true);
- cc_unmap_req(dev, state, ctx);
- return -ENOMEM;
- }
-
- /* Setup DX request structure */
- cc_req.user_cb = cc_hash_complete;
- cc_req.user_arg = req;
-
- idx = cc_restore_hash(desc, ctx, state, idx);
-
- if (is_hmac)
- idx = cc_fin_hmac(desc, req, idx);
-
- idx = cc_fin_result(desc, req, idx);
-
- rc = cc_send_request(ctx->drvdata, &cc_req, desc, idx, &req->base);
- if (rc != -EINPROGRESS && rc != -EBUSY) {
- dev_err(dev, "send_request() failed (rc=%d)\n", rc);
- cc_unmap_hash_request(dev, state, src, true);
- cc_unmap_result(dev, state, digestsize, result);
- cc_unmap_req(dev, state, ctx);
- }
- return rc;
-}
-
-static int cc_hash_final(struct ahash_request *req)
-{
- struct ahash_req_ctx *state = ahash_request_ctx(req);
- struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
- struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
- u32 digestsize = crypto_ahash_digestsize(tfm);
- struct scatterlist *src = req->src;
- unsigned int nbytes = req->nbytes;
- u8 *result = req->result;
- struct device *dev = drvdata_to_dev(ctx->drvdata);
- bool is_hmac = ctx->is_hmac;
- struct cc_crypto_req cc_req = {};
- struct cc_hw_desc desc[CC_MAX_HASH_SEQ_LEN];
- int idx = 0;
- int rc;
- gfp_t flags = cc_gfp_flags(&req->base);
-
- dev_dbg(dev, "===== %s-final (%d) ====\n", is_hmac ? "hmac" : "hash",
- nbytes);
-
- if (cc_map_req(dev, state, ctx)) {
- dev_err(dev, "map_ahash_source() failed\n");
- return -EINVAL;
- }
-
- if (cc_map_hash_request_final(ctx->drvdata, state, src, nbytes, 0,
- flags)) {
- dev_err(dev, "map_ahash_request_final() failed\n");
- cc_unmap_req(dev, state, ctx);
- return -ENOMEM;
- }
-
- if (cc_map_result(dev, state, digestsize)) {
- dev_err(dev, "map_ahash_digest() failed\n");
- cc_unmap_hash_request(dev, state, src, true);
- cc_unmap_req(dev, state, ctx);
- return -ENOMEM;
- }
-
- /* Setup DX request structure */
- cc_req.user_cb = cc_hash_complete;
- cc_req.user_arg = req;
-
- idx = cc_restore_hash(desc, ctx, state, idx);
-
- /* "DO-PAD" must be enabled only when writing current length to HW */
- hw_desc_init(&desc[idx]);
- set_cipher_do(&desc[idx], DO_PAD);
- set_cipher_mode(&desc[idx], ctx->hw_mode);
- set_dout_dlli(&desc[idx], state->digest_bytes_len_dma_addr,
- HASH_LEN_SIZE, NS_BIT, 0);
- set_setup_mode(&desc[idx], SETUP_WRITE_STATE1);
- set_flow_mode(&desc[idx], S_HASH_to_DOUT);
- idx++;
-
- if (is_hmac)
- idx = cc_fin_hmac(desc, req, idx);
-
- idx = cc_fin_result(desc, req, idx);
-
- rc = cc_send_request(ctx->drvdata, &cc_req, desc, idx, &req->base);
- if (rc != -EINPROGRESS && rc != -EBUSY) {
- dev_err(dev, "send_request() failed (rc=%d)\n", rc);
- cc_unmap_hash_request(dev, state, src, true);
- cc_unmap_result(dev, state, digestsize, result);
- cc_unmap_req(dev, state, ctx);
- }
- return rc;
-}
-
-static int cc_hash_init(struct ahash_request *req)
-{
- struct ahash_req_ctx *state = ahash_request_ctx(req);
- struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
- struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
- struct device *dev = drvdata_to_dev(ctx->drvdata);
-
- dev_dbg(dev, "===== init (%d) ====\n", req->nbytes);
-
- cc_init_req(dev, state, ctx);
-
- return 0;
-}
-
-static int cc_hash_setkey(struct crypto_ahash *ahash, const u8 *key,
- unsigned int keylen)
-{
- unsigned int hmac_pad_const[2] = { HMAC_IPAD_CONST, HMAC_OPAD_CONST };
- struct cc_crypto_req cc_req = {};
- struct cc_hash_ctx *ctx = NULL;
- int blocksize = 0;
- int digestsize = 0;
- int i, idx = 0, rc = 0;
- struct cc_hw_desc desc[CC_MAX_HASH_SEQ_LEN];
- cc_sram_addr_t larval_addr;
- struct device *dev;
-
- ctx = crypto_ahash_ctx(ahash);
- dev = drvdata_to_dev(ctx->drvdata);
- dev_dbg(dev, "start keylen: %d", keylen);
-
- blocksize = crypto_tfm_alg_blocksize(&ahash->base);
- digestsize = crypto_ahash_digestsize(ahash);
-
- larval_addr = cc_larval_digest_addr(ctx->drvdata, ctx->hash_mode);
-
- /* The keylen value distinguishes HASH in case keylen is ZERO bytes,
- * any NON-ZERO value utilizes HMAC flow
- */
- ctx->key_params.keylen = keylen;
- ctx->key_params.key_dma_addr = 0;
- ctx->is_hmac = true;
-
- if (keylen) {
- ctx->key_params.key_dma_addr =
- dma_map_single(dev, (void *)key, keylen, DMA_TO_DEVICE);
- if (dma_mapping_error(dev, ctx->key_params.key_dma_addr)) {
- dev_err(dev, "Mapping key va=0x%p len=%u for DMA failed\n",
- key, keylen);
- return -ENOMEM;
- }
- dev_dbg(dev, "mapping key-buffer: key_dma_addr=%pad keylen=%u\n",
- &ctx->key_params.key_dma_addr, ctx->key_params.keylen);
-
- if (keylen > blocksize) {
- /* Load hash initial state */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], ctx->hw_mode);
- set_din_sram(&desc[idx], larval_addr,
- ctx->inter_digestsize);
- set_flow_mode(&desc[idx], S_DIN_to_HASH);
- set_setup_mode(&desc[idx], SETUP_LOAD_STATE0);
- idx++;
-
- /* Load the hash current length*/
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], ctx->hw_mode);
- set_din_const(&desc[idx], 0, HASH_LEN_SIZE);
- set_cipher_config1(&desc[idx], HASH_PADDING_ENABLED);
- set_flow_mode(&desc[idx], S_DIN_to_HASH);
- set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
- idx++;
-
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_DLLI,
- ctx->key_params.key_dma_addr, keylen,
- NS_BIT);
- set_flow_mode(&desc[idx], DIN_HASH);
- idx++;
-
- /* Get hashed key */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], ctx->hw_mode);
- set_dout_dlli(&desc[idx], ctx->opad_tmp_keys_dma_addr,
- digestsize, NS_BIT, 0);
- set_flow_mode(&desc[idx], S_HASH_to_DOUT);
- set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
- set_cipher_config1(&desc[idx], HASH_PADDING_DISABLED);
- cc_set_endianity(ctx->hash_mode, &desc[idx]);
- idx++;
-
- hw_desc_init(&desc[idx]);
- set_din_const(&desc[idx], 0, (blocksize - digestsize));
- set_flow_mode(&desc[idx], BYPASS);
- set_dout_dlli(&desc[idx],
- (ctx->opad_tmp_keys_dma_addr +
- digestsize),
- (blocksize - digestsize), NS_BIT, 0);
- idx++;
- } else {
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_DLLI,
- ctx->key_params.key_dma_addr, keylen,
- NS_BIT);
- set_flow_mode(&desc[idx], BYPASS);
- set_dout_dlli(&desc[idx], ctx->opad_tmp_keys_dma_addr,
- keylen, NS_BIT, 0);
- idx++;
-
- if ((blocksize - keylen)) {
- hw_desc_init(&desc[idx]);
- set_din_const(&desc[idx], 0,
- (blocksize - keylen));
- set_flow_mode(&desc[idx], BYPASS);
- set_dout_dlli(&desc[idx],
- (ctx->opad_tmp_keys_dma_addr +
- keylen), (blocksize - keylen),
- NS_BIT, 0);
- idx++;
- }
- }
- } else {
- hw_desc_init(&desc[idx]);
- set_din_const(&desc[idx], 0, blocksize);
- set_flow_mode(&desc[idx], BYPASS);
- set_dout_dlli(&desc[idx], (ctx->opad_tmp_keys_dma_addr),
- blocksize, NS_BIT, 0);
- idx++;
- }
-
- rc = cc_send_sync_request(ctx->drvdata, &cc_req, desc, idx);
- if (rc) {
- dev_err(dev, "send_request() failed (rc=%d)\n", rc);
- goto out;
- }
-
- /* calc derived HMAC key */
- for (idx = 0, i = 0; i < 2; i++) {
- /* Load hash initial state */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], ctx->hw_mode);
- set_din_sram(&desc[idx], larval_addr, ctx->inter_digestsize);
- set_flow_mode(&desc[idx], S_DIN_to_HASH);
- set_setup_mode(&desc[idx], SETUP_LOAD_STATE0);
- idx++;
-
- /* Load the hash current length*/
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], ctx->hw_mode);
- set_din_const(&desc[idx], 0, HASH_LEN_SIZE);
- set_flow_mode(&desc[idx], S_DIN_to_HASH);
- set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
- idx++;
-
- /* Prepare ipad key */
- hw_desc_init(&desc[idx]);
- set_xor_val(&desc[idx], hmac_pad_const[i]);
- set_cipher_mode(&desc[idx], ctx->hw_mode);
- set_flow_mode(&desc[idx], S_DIN_to_HASH);
- set_setup_mode(&desc[idx], SETUP_LOAD_STATE1);
- idx++;
-
- /* Perform HASH update */
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_DLLI, ctx->opad_tmp_keys_dma_addr,
- blocksize, NS_BIT);
- set_cipher_mode(&desc[idx], ctx->hw_mode);
- set_xor_active(&desc[idx]);
- set_flow_mode(&desc[idx], DIN_HASH);
- idx++;
-
- /* Get the IPAD/OPAD xor key (Note, IPAD is the initial digest
- * of the first HASH "update" state)
- */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], ctx->hw_mode);
- if (i > 0) /* Not first iteration */
- set_dout_dlli(&desc[idx], ctx->opad_tmp_keys_dma_addr,
- ctx->inter_digestsize, NS_BIT, 0);
- else /* First iteration */
- set_dout_dlli(&desc[idx], ctx->digest_buff_dma_addr,
- ctx->inter_digestsize, NS_BIT, 0);
- set_flow_mode(&desc[idx], S_HASH_to_DOUT);
- set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
- idx++;
- }
-
- rc = cc_send_sync_request(ctx->drvdata, &cc_req, desc, idx);
-
-out:
- if (rc)
- crypto_ahash_set_flags(ahash, CRYPTO_TFM_RES_BAD_KEY_LEN);
-
- if (ctx->key_params.key_dma_addr) {
- dma_unmap_single(dev, ctx->key_params.key_dma_addr,
- ctx->key_params.keylen, DMA_TO_DEVICE);
- dev_dbg(dev, "Unmapped key-buffer: key_dma_addr=%pad keylen=%u\n",
- &ctx->key_params.key_dma_addr, ctx->key_params.keylen);
- }
- return rc;
-}
-
-static int cc_xcbc_setkey(struct crypto_ahash *ahash,
- const u8 *key, unsigned int keylen)
-{
- struct cc_crypto_req cc_req = {};
- struct cc_hash_ctx *ctx = crypto_ahash_ctx(ahash);
- struct device *dev = drvdata_to_dev(ctx->drvdata);
- int idx = 0, rc = 0;
- struct cc_hw_desc desc[CC_MAX_HASH_SEQ_LEN];
-
- dev_dbg(dev, "===== setkey (%d) ====\n", keylen);
-
- switch (keylen) {
- case AES_KEYSIZE_128:
- case AES_KEYSIZE_192:
- case AES_KEYSIZE_256:
- break;
- default:
- return -EINVAL;
- }
-
- ctx->key_params.keylen = keylen;
-
- ctx->key_params.key_dma_addr =
- dma_map_single(dev, (void *)key, keylen, DMA_TO_DEVICE);
- if (dma_mapping_error(dev, ctx->key_params.key_dma_addr)) {
- dev_err(dev, "Mapping key va=0x%p len=%u for DMA failed\n",
- key, keylen);
- return -ENOMEM;
- }
- dev_dbg(dev, "mapping key-buffer: key_dma_addr=%pad keylen=%u\n",
- &ctx->key_params.key_dma_addr, ctx->key_params.keylen);
-
- ctx->is_hmac = true;
- /* 1. Load the AES key */
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_DLLI, ctx->key_params.key_dma_addr,
- keylen, NS_BIT);
- set_cipher_mode(&desc[idx], DRV_CIPHER_ECB);
- set_cipher_config0(&desc[idx], DRV_CRYPTO_DIRECTION_ENCRYPT);
- set_key_size_aes(&desc[idx], keylen);
- set_flow_mode(&desc[idx], S_DIN_to_AES);
- set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
- idx++;
-
- hw_desc_init(&desc[idx]);
- set_din_const(&desc[idx], 0x01010101, CC_AES_128_BIT_KEY_SIZE);
- set_flow_mode(&desc[idx], DIN_AES_DOUT);
- set_dout_dlli(&desc[idx],
- (ctx->opad_tmp_keys_dma_addr + XCBC_MAC_K1_OFFSET),
- CC_AES_128_BIT_KEY_SIZE, NS_BIT, 0);
- idx++;
-
- hw_desc_init(&desc[idx]);
- set_din_const(&desc[idx], 0x02020202, CC_AES_128_BIT_KEY_SIZE);
- set_flow_mode(&desc[idx], DIN_AES_DOUT);
- set_dout_dlli(&desc[idx],
- (ctx->opad_tmp_keys_dma_addr + XCBC_MAC_K2_OFFSET),
- CC_AES_128_BIT_KEY_SIZE, NS_BIT, 0);
- idx++;
-
- hw_desc_init(&desc[idx]);
- set_din_const(&desc[idx], 0x03030303, CC_AES_128_BIT_KEY_SIZE);
- set_flow_mode(&desc[idx], DIN_AES_DOUT);
- set_dout_dlli(&desc[idx],
- (ctx->opad_tmp_keys_dma_addr + XCBC_MAC_K3_OFFSET),
- CC_AES_128_BIT_KEY_SIZE, NS_BIT, 0);
- idx++;
-
- rc = cc_send_sync_request(ctx->drvdata, &cc_req, desc, idx);
-
- if (rc)
- crypto_ahash_set_flags(ahash, CRYPTO_TFM_RES_BAD_KEY_LEN);
-
- dma_unmap_single(dev, ctx->key_params.key_dma_addr,
- ctx->key_params.keylen, DMA_TO_DEVICE);
- dev_dbg(dev, "Unmapped key-buffer: key_dma_addr=%pad keylen=%u\n",
- &ctx->key_params.key_dma_addr, ctx->key_params.keylen);
-
- return rc;
-}
-
-static int cc_cmac_setkey(struct crypto_ahash *ahash,
- const u8 *key, unsigned int keylen)
-{
- struct cc_hash_ctx *ctx = crypto_ahash_ctx(ahash);
- struct device *dev = drvdata_to_dev(ctx->drvdata);
-
- dev_dbg(dev, "===== setkey (%d) ====\n", keylen);
-
- ctx->is_hmac = true;
-
- switch (keylen) {
- case AES_KEYSIZE_128:
- case AES_KEYSIZE_192:
- case AES_KEYSIZE_256:
- break;
- default:
- return -EINVAL;
- }
-
- ctx->key_params.keylen = keylen;
-
- /* STAT_PHASE_1: Copy key to ctx */
-
- dma_sync_single_for_cpu(dev, ctx->opad_tmp_keys_dma_addr,
- keylen, DMA_TO_DEVICE);
-
- memcpy(ctx->opad_tmp_keys_buff, key, keylen);
- if (keylen == 24) {
- memset(ctx->opad_tmp_keys_buff + 24, 0,
- CC_AES_KEY_SIZE_MAX - 24);
- }
-
- dma_sync_single_for_device(dev, ctx->opad_tmp_keys_dma_addr,
- keylen, DMA_TO_DEVICE);
-
- ctx->key_params.keylen = keylen;
-
- return 0;
-}
-
-static void cc_free_ctx(struct cc_hash_ctx *ctx)
-{
- struct device *dev = drvdata_to_dev(ctx->drvdata);
-
- if (ctx->digest_buff_dma_addr) {
- dma_unmap_single(dev, ctx->digest_buff_dma_addr,
- sizeof(ctx->digest_buff), DMA_BIDIRECTIONAL);
- dev_dbg(dev, "Unmapped digest-buffer: digest_buff_dma_addr=%pad\n",
- &ctx->digest_buff_dma_addr);
- ctx->digest_buff_dma_addr = 0;
- }
- if (ctx->opad_tmp_keys_dma_addr) {
- dma_unmap_single(dev, ctx->opad_tmp_keys_dma_addr,
- sizeof(ctx->opad_tmp_keys_buff),
- DMA_BIDIRECTIONAL);
- dev_dbg(dev, "Unmapped opad-digest: opad_tmp_keys_dma_addr=%pad\n",
- &ctx->opad_tmp_keys_dma_addr);
- ctx->opad_tmp_keys_dma_addr = 0;
- }
-
- ctx->key_params.keylen = 0;
-}
-
-static int cc_alloc_ctx(struct cc_hash_ctx *ctx)
-{
- struct device *dev = drvdata_to_dev(ctx->drvdata);
-
- ctx->key_params.keylen = 0;
-
- ctx->digest_buff_dma_addr =
- dma_map_single(dev, (void *)ctx->digest_buff,
- sizeof(ctx->digest_buff), DMA_BIDIRECTIONAL);
- if (dma_mapping_error(dev, ctx->digest_buff_dma_addr)) {
- dev_err(dev, "Mapping digest len %zu B at va=%pK for DMA failed\n",
- sizeof(ctx->digest_buff), ctx->digest_buff);
- goto fail;
- }
- dev_dbg(dev, "Mapped digest %zu B at va=%pK to dma=%pad\n",
- sizeof(ctx->digest_buff), ctx->digest_buff,
- &ctx->digest_buff_dma_addr);
-
- ctx->opad_tmp_keys_dma_addr =
- dma_map_single(dev, (void *)ctx->opad_tmp_keys_buff,
- sizeof(ctx->opad_tmp_keys_buff),
- DMA_BIDIRECTIONAL);
- if (dma_mapping_error(dev, ctx->opad_tmp_keys_dma_addr)) {
- dev_err(dev, "Mapping opad digest %zu B at va=%pK for DMA failed\n",
- sizeof(ctx->opad_tmp_keys_buff),
- ctx->opad_tmp_keys_buff);
- goto fail;
- }
- dev_dbg(dev, "Mapped opad_tmp_keys %zu B at va=%pK to dma=%pad\n",
- sizeof(ctx->opad_tmp_keys_buff), ctx->opad_tmp_keys_buff,
- &ctx->opad_tmp_keys_dma_addr);
-
- ctx->is_hmac = false;
- return 0;
-
-fail:
- cc_free_ctx(ctx);
- return -ENOMEM;
-}
-
-static int cc_cra_init(struct crypto_tfm *tfm)
-{
- struct cc_hash_ctx *ctx = crypto_tfm_ctx(tfm);
- struct hash_alg_common *hash_alg_common =
- container_of(tfm->__crt_alg, struct hash_alg_common, base);
- struct ahash_alg *ahash_alg =
- container_of(hash_alg_common, struct ahash_alg, halg);
- struct cc_hash_alg *cc_alg =
- container_of(ahash_alg, struct cc_hash_alg, ahash_alg);
-
- crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm),
- sizeof(struct ahash_req_ctx));
-
- ctx->hash_mode = cc_alg->hash_mode;
- ctx->hw_mode = cc_alg->hw_mode;
- ctx->inter_digestsize = cc_alg->inter_digestsize;
- ctx->drvdata = cc_alg->drvdata;
-
- return cc_alloc_ctx(ctx);
-}
-
-static void cc_cra_exit(struct crypto_tfm *tfm)
-{
- struct cc_hash_ctx *ctx = crypto_tfm_ctx(tfm);
- struct device *dev = drvdata_to_dev(ctx->drvdata);
-
- dev_dbg(dev, "cc_cra_exit");
- cc_free_ctx(ctx);
-}
-
-static int cc_mac_update(struct ahash_request *req)
-{
- struct ahash_req_ctx *state = ahash_request_ctx(req);
- struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
- struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
- struct device *dev = drvdata_to_dev(ctx->drvdata);
- unsigned int block_size = crypto_tfm_alg_blocksize(&tfm->base);
- struct cc_crypto_req cc_req = {};
- struct cc_hw_desc desc[CC_MAX_HASH_SEQ_LEN];
- int rc;
- u32 idx = 0;
- gfp_t flags = cc_gfp_flags(&req->base);
-
- if (req->nbytes == 0) {
- /* no real updates required */
- return 0;
- }
-
- state->xcbc_count++;
-
- rc = cc_map_hash_request_update(ctx->drvdata, state, req->src,
- req->nbytes, block_size, flags);
- if (rc) {
- if (rc == 1) {
- dev_dbg(dev, " data size not require HW update %x\n",
- req->nbytes);
- /* No hardware updates are required */
- return 0;
- }
- dev_err(dev, "map_ahash_request_update() failed\n");
- return -ENOMEM;
- }
-
- if (cc_map_req(dev, state, ctx)) {
- dev_err(dev, "map_ahash_source() failed\n");
- return -EINVAL;
- }
-
- if (ctx->hw_mode == DRV_CIPHER_XCBC_MAC)
- cc_setup_xcbc(req, desc, &idx);
- else
- cc_setup_cmac(req, desc, &idx);
-
- cc_set_desc(state, ctx, DIN_AES_DOUT, desc, true, &idx);
-
- /* store the hash digest result in context */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], ctx->hw_mode);
- set_dout_dlli(&desc[idx], state->digest_buff_dma_addr,
- ctx->inter_digestsize, NS_BIT, 1);
- set_queue_last_ind(&desc[idx]);
- set_flow_mode(&desc[idx], S_AES_to_DOUT);
- set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
- idx++;
-
- /* Setup DX request structure */
- cc_req.user_cb = (void *)cc_update_complete;
- cc_req.user_arg = (void *)req;
-
- rc = cc_send_request(ctx->drvdata, &cc_req, desc, idx, &req->base);
- if (rc != -EINPROGRESS && rc != -EBUSY) {
- dev_err(dev, "send_request() failed (rc=%d)\n", rc);
- cc_unmap_hash_request(dev, state, req->src, true);
- cc_unmap_req(dev, state, ctx);
- }
- return rc;
-}
-
-static int cc_mac_final(struct ahash_request *req)
-{
- struct ahash_req_ctx *state = ahash_request_ctx(req);
- struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
- struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
- struct device *dev = drvdata_to_dev(ctx->drvdata);
- struct cc_crypto_req cc_req = {};
- struct cc_hw_desc desc[CC_MAX_HASH_SEQ_LEN];
- int idx = 0;
- int rc = 0;
- u32 key_size, key_len;
- u32 digestsize = crypto_ahash_digestsize(tfm);
- gfp_t flags = cc_gfp_flags(&req->base);
- u32 rem_cnt = *cc_hash_buf_cnt(state);
-
- if (ctx->hw_mode == DRV_CIPHER_XCBC_MAC) {
- key_size = CC_AES_128_BIT_KEY_SIZE;
- key_len = CC_AES_128_BIT_KEY_SIZE;
- } else {
- key_size = (ctx->key_params.keylen == 24) ? AES_MAX_KEY_SIZE :
- ctx->key_params.keylen;
- key_len = ctx->key_params.keylen;
- }
-
- dev_dbg(dev, "===== final xcbc reminder (%d) ====\n", rem_cnt);
-
- if (cc_map_req(dev, state, ctx)) {
- dev_err(dev, "map_ahash_source() failed\n");
- return -EINVAL;
- }
-
- if (cc_map_hash_request_final(ctx->drvdata, state, req->src,
- req->nbytes, 0, flags)) {
- dev_err(dev, "map_ahash_request_final() failed\n");
- cc_unmap_req(dev, state, ctx);
- return -ENOMEM;
- }
-
- if (cc_map_result(dev, state, digestsize)) {
- dev_err(dev, "map_ahash_digest() failed\n");
- cc_unmap_hash_request(dev, state, req->src, true);
- cc_unmap_req(dev, state, ctx);
- return -ENOMEM;
- }
-
- /* Setup DX request structure */
- cc_req.user_cb = (void *)cc_hash_complete;
- cc_req.user_arg = (void *)req;
-
- if (state->xcbc_count && rem_cnt == 0) {
- /* Load key for ECB decryption */
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], DRV_CIPHER_ECB);
- set_cipher_config0(&desc[idx], DRV_CRYPTO_DIRECTION_DECRYPT);
- set_din_type(&desc[idx], DMA_DLLI,
- (ctx->opad_tmp_keys_dma_addr + XCBC_MAC_K1_OFFSET),
- key_size, NS_BIT);
- set_key_size_aes(&desc[idx], key_len);
- set_flow_mode(&desc[idx], S_DIN_to_AES);
- set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
- idx++;
-
- /* Initiate decryption of block state to previous
- * block_state-XOR-M[n]
- */
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_DLLI, state->digest_buff_dma_addr,
- CC_AES_BLOCK_SIZE, NS_BIT);
- set_dout_dlli(&desc[idx], state->digest_buff_dma_addr,
- CC_AES_BLOCK_SIZE, NS_BIT, 0);
- set_flow_mode(&desc[idx], DIN_AES_DOUT);
- idx++;
-
- /* Memory Barrier: wait for axi write to complete */
- hw_desc_init(&desc[idx]);
- set_din_no_dma(&desc[idx], 0, 0xfffff0);
- set_dout_no_dma(&desc[idx], 0, 0, 1);
- idx++;
- }
-
- if (ctx->hw_mode == DRV_CIPHER_XCBC_MAC)
- cc_setup_xcbc(req, desc, &idx);
- else
- cc_setup_cmac(req, desc, &idx);
-
- if (state->xcbc_count == 0) {
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], ctx->hw_mode);
- set_key_size_aes(&desc[idx], key_len);
- set_cmac_size0_mode(&desc[idx]);
- set_flow_mode(&desc[idx], S_DIN_to_AES);
- idx++;
- } else if (rem_cnt > 0) {
- cc_set_desc(state, ctx, DIN_AES_DOUT, desc, false, &idx);
- } else {
- hw_desc_init(&desc[idx]);
- set_din_const(&desc[idx], 0x00, CC_AES_BLOCK_SIZE);
- set_flow_mode(&desc[idx], DIN_AES_DOUT);
- idx++;
- }
-
- /* Get final MAC result */
- hw_desc_init(&desc[idx]);
- /* TODO */
- set_dout_dlli(&desc[idx], state->digest_result_dma_addr,
- digestsize, NS_BIT, 1);
- set_queue_last_ind(&desc[idx]);
- set_flow_mode(&desc[idx], S_AES_to_DOUT);
- set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
- set_cipher_mode(&desc[idx], ctx->hw_mode);
- idx++;
-
- rc = cc_send_request(ctx->drvdata, &cc_req, desc, idx, &req->base);
- if (rc != -EINPROGRESS && rc != -EBUSY) {
- dev_err(dev, "send_request() failed (rc=%d)\n", rc);
- cc_unmap_hash_request(dev, state, req->src, true);
- cc_unmap_result(dev, state, digestsize, req->result);
- cc_unmap_req(dev, state, ctx);
- }
- return rc;
-}
-
-static int cc_mac_finup(struct ahash_request *req)
-{
- struct ahash_req_ctx *state = ahash_request_ctx(req);
- struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
- struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
- struct device *dev = drvdata_to_dev(ctx->drvdata);
- struct cc_crypto_req cc_req = {};
- struct cc_hw_desc desc[CC_MAX_HASH_SEQ_LEN];
- int idx = 0;
- int rc = 0;
- u32 key_len = 0;
- u32 digestsize = crypto_ahash_digestsize(tfm);
- gfp_t flags = cc_gfp_flags(&req->base);
-
- dev_dbg(dev, "===== finup xcbc(%d) ====\n", req->nbytes);
- if (state->xcbc_count > 0 && req->nbytes == 0) {
- dev_dbg(dev, "No data to update. Call to fdx_mac_final\n");
- return cc_mac_final(req);
- }
-
- if (cc_map_req(dev, state, ctx)) {
- dev_err(dev, "map_ahash_source() failed\n");
- return -EINVAL;
- }
-
- if (cc_map_hash_request_final(ctx->drvdata, state, req->src,
- req->nbytes, 1, flags)) {
- dev_err(dev, "map_ahash_request_final() failed\n");
- cc_unmap_req(dev, state, ctx);
- return -ENOMEM;
- }
- if (cc_map_result(dev, state, digestsize)) {
- dev_err(dev, "map_ahash_digest() failed\n");
- cc_unmap_hash_request(dev, state, req->src, true);
- cc_unmap_req(dev, state, ctx);
- return -ENOMEM;
- }
-
- /* Setup DX request structure */
- cc_req.user_cb = (void *)cc_hash_complete;
- cc_req.user_arg = (void *)req;
-
- if (ctx->hw_mode == DRV_CIPHER_XCBC_MAC) {
- key_len = CC_AES_128_BIT_KEY_SIZE;
- cc_setup_xcbc(req, desc, &idx);
- } else {
- key_len = ctx->key_params.keylen;
- cc_setup_cmac(req, desc, &idx);
- }
-
- if (req->nbytes == 0) {
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], ctx->hw_mode);
- set_key_size_aes(&desc[idx], key_len);
- set_cmac_size0_mode(&desc[idx]);
- set_flow_mode(&desc[idx], S_DIN_to_AES);
- idx++;
- } else {
- cc_set_desc(state, ctx, DIN_AES_DOUT, desc, false, &idx);
- }
-
- /* Get final MAC result */
- hw_desc_init(&desc[idx]);
- /* TODO */
- set_dout_dlli(&desc[idx], state->digest_result_dma_addr,
- digestsize, NS_BIT, 1);
- set_queue_last_ind(&desc[idx]);
- set_flow_mode(&desc[idx], S_AES_to_DOUT);
- set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
- set_cipher_mode(&desc[idx], ctx->hw_mode);
- idx++;
-
- rc = cc_send_request(ctx->drvdata, &cc_req, desc, idx, &req->base);
- if (rc != -EINPROGRESS && rc != -EBUSY) {
- dev_err(dev, "send_request() failed (rc=%d)\n", rc);
- cc_unmap_hash_request(dev, state, req->src, true);
- cc_unmap_result(dev, state, digestsize, req->result);
- cc_unmap_req(dev, state, ctx);
- }
- return rc;
-}
-
-static int cc_mac_digest(struct ahash_request *req)
-{
- struct ahash_req_ctx *state = ahash_request_ctx(req);
- struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
- struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
- struct device *dev = drvdata_to_dev(ctx->drvdata);
- u32 digestsize = crypto_ahash_digestsize(tfm);
- struct cc_crypto_req cc_req = {};
- struct cc_hw_desc desc[CC_MAX_HASH_SEQ_LEN];
- u32 key_len;
- int idx = 0;
- int rc;
- gfp_t flags = cc_gfp_flags(&req->base);
-
- dev_dbg(dev, "===== -digest mac (%d) ====\n", req->nbytes);
-
- cc_init_req(dev, state, ctx);
-
- if (cc_map_req(dev, state, ctx)) {
- dev_err(dev, "map_ahash_source() failed\n");
- return -ENOMEM;
- }
- if (cc_map_result(dev, state, digestsize)) {
- dev_err(dev, "map_ahash_digest() failed\n");
- cc_unmap_req(dev, state, ctx);
- return -ENOMEM;
- }
-
- if (cc_map_hash_request_final(ctx->drvdata, state, req->src,
- req->nbytes, 1, flags)) {
- dev_err(dev, "map_ahash_request_final() failed\n");
- cc_unmap_req(dev, state, ctx);
- return -ENOMEM;
- }
-
- /* Setup DX request structure */
- cc_req.user_cb = (void *)cc_digest_complete;
- cc_req.user_arg = (void *)req;
-
- if (ctx->hw_mode == DRV_CIPHER_XCBC_MAC) {
- key_len = CC_AES_128_BIT_KEY_SIZE;
- cc_setup_xcbc(req, desc, &idx);
- } else {
- key_len = ctx->key_params.keylen;
- cc_setup_cmac(req, desc, &idx);
- }
-
- if (req->nbytes == 0) {
- hw_desc_init(&desc[idx]);
- set_cipher_mode(&desc[idx], ctx->hw_mode);
- set_key_size_aes(&desc[idx], key_len);
- set_cmac_size0_mode(&desc[idx]);
- set_flow_mode(&desc[idx], S_DIN_to_AES);
- idx++;
- } else {
- cc_set_desc(state, ctx, DIN_AES_DOUT, desc, false, &idx);
- }
-
- /* Get final MAC result */
- hw_desc_init(&desc[idx]);
- set_dout_dlli(&desc[idx], state->digest_result_dma_addr,
- CC_AES_BLOCK_SIZE, NS_BIT, 1);
- set_queue_last_ind(&desc[idx]);
- set_flow_mode(&desc[idx], S_AES_to_DOUT);
- set_setup_mode(&desc[idx], SETUP_WRITE_STATE0);
- set_cipher_config0(&desc[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
- set_cipher_mode(&desc[idx], ctx->hw_mode);
- idx++;
-
- rc = cc_send_request(ctx->drvdata, &cc_req, desc, idx, &req->base);
- if (rc != -EINPROGRESS && rc != -EBUSY) {
- dev_err(dev, "send_request() failed (rc=%d)\n", rc);
- cc_unmap_hash_request(dev, state, req->src, true);
- cc_unmap_result(dev, state, digestsize, req->result);
- cc_unmap_req(dev, state, ctx);
- }
- return rc;
-}
-
-static int cc_hash_export(struct ahash_request *req, void *out)
-{
- struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
- struct cc_hash_ctx *ctx = crypto_ahash_ctx(ahash);
- struct ahash_req_ctx *state = ahash_request_ctx(req);
- u8 *curr_buff = cc_hash_buf(state);
- u32 curr_buff_cnt = *cc_hash_buf_cnt(state);
- const u32 tmp = CC_EXPORT_MAGIC;
-
- memcpy(out, &tmp, sizeof(u32));
- out += sizeof(u32);
-
- memcpy(out, state->digest_buff, ctx->inter_digestsize);
- out += ctx->inter_digestsize;
-
- memcpy(out, state->digest_bytes_len, HASH_LEN_SIZE);
- out += HASH_LEN_SIZE;
-
- memcpy(out, &curr_buff_cnt, sizeof(u32));
- out += sizeof(u32);
-
- memcpy(out, curr_buff, curr_buff_cnt);
-
- return 0;
-}
-
-static int cc_hash_import(struct ahash_request *req, const void *in)
-{
- struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
- struct cc_hash_ctx *ctx = crypto_ahash_ctx(ahash);
- struct device *dev = drvdata_to_dev(ctx->drvdata);
- struct ahash_req_ctx *state = ahash_request_ctx(req);
- u32 tmp;
-
- memcpy(&tmp, in, sizeof(u32));
- if (tmp != CC_EXPORT_MAGIC)
- return -EINVAL;
- in += sizeof(u32);
-
- cc_init_req(dev, state, ctx);
-
- memcpy(state->digest_buff, in, ctx->inter_digestsize);
- in += ctx->inter_digestsize;
-
- memcpy(state->digest_bytes_len, in, HASH_LEN_SIZE);
- in += HASH_LEN_SIZE;
-
- /* Sanity check the data as much as possible */
- memcpy(&tmp, in, sizeof(u32));
- if (tmp > CC_MAX_HASH_BLCK_SIZE)
- return -EINVAL;
- in += sizeof(u32);
-
- state->buf_cnt[0] = tmp;
- memcpy(state->buffers[0], in, tmp);
-
- return 0;
-}
-
-struct cc_hash_template {
- char name[CRYPTO_MAX_ALG_NAME];
- char driver_name[CRYPTO_MAX_ALG_NAME];
- char mac_name[CRYPTO_MAX_ALG_NAME];
- char mac_driver_name[CRYPTO_MAX_ALG_NAME];
- unsigned int blocksize;
- bool synchronize;
- struct ahash_alg template_ahash;
- int hash_mode;
- int hw_mode;
- int inter_digestsize;
- struct cc_drvdata *drvdata;
-};
-
-#define CC_STATE_SIZE(_x) \
- ((_x) + HASH_LEN_SIZE + CC_MAX_HASH_BLCK_SIZE + (2 * sizeof(u32)))
-
-/* hash descriptors */
-static struct cc_hash_template driver_hash[] = {
- //Asynchronize hash template
- {
- .name = "sha1",
- .driver_name = "sha1-dx",
- .mac_name = "hmac(sha1)",
- .mac_driver_name = "hmac-sha1-dx",
- .blocksize = SHA1_BLOCK_SIZE,
- .synchronize = false,
- .template_ahash = {
- .init = cc_hash_init,
- .update = cc_hash_update,
- .final = cc_hash_final,
- .finup = cc_hash_finup,
- .digest = cc_hash_digest,
- .export = cc_hash_export,
- .import = cc_hash_import,
- .setkey = cc_hash_setkey,
- .halg = {
- .digestsize = SHA1_DIGEST_SIZE,
- .statesize = CC_STATE_SIZE(SHA1_DIGEST_SIZE),
- },
- },
- .hash_mode = DRV_HASH_SHA1,
- .hw_mode = DRV_HASH_HW_SHA1,
- .inter_digestsize = SHA1_DIGEST_SIZE,
- },
- {
- .name = "sha256",
- .driver_name = "sha256-dx",
- .mac_name = "hmac(sha256)",
- .mac_driver_name = "hmac-sha256-dx",
- .blocksize = SHA256_BLOCK_SIZE,
- .template_ahash = {
- .init = cc_hash_init,
- .update = cc_hash_update,
- .final = cc_hash_final,
- .finup = cc_hash_finup,
- .digest = cc_hash_digest,
- .export = cc_hash_export,
- .import = cc_hash_import,
- .setkey = cc_hash_setkey,
- .halg = {
- .digestsize = SHA256_DIGEST_SIZE,
- .statesize = CC_STATE_SIZE(SHA256_DIGEST_SIZE)
- },
- },
- .hash_mode = DRV_HASH_SHA256,
- .hw_mode = DRV_HASH_HW_SHA256,
- .inter_digestsize = SHA256_DIGEST_SIZE,
- },
- {
- .name = "sha224",
- .driver_name = "sha224-dx",
- .mac_name = "hmac(sha224)",
- .mac_driver_name = "hmac-sha224-dx",
- .blocksize = SHA224_BLOCK_SIZE,
- .template_ahash = {
- .init = cc_hash_init,
- .update = cc_hash_update,
- .final = cc_hash_final,
- .finup = cc_hash_finup,
- .digest = cc_hash_digest,
- .export = cc_hash_export,
- .import = cc_hash_import,
- .setkey = cc_hash_setkey,
- .halg = {
- .digestsize = SHA224_DIGEST_SIZE,
- .statesize = CC_STATE_SIZE(SHA224_DIGEST_SIZE),
- },
- },
- .hash_mode = DRV_HASH_SHA224,
- .hw_mode = DRV_HASH_HW_SHA256,
- .inter_digestsize = SHA256_DIGEST_SIZE,
- },
-#if (CC_DEV_SHA_MAX > 256)
- {
- .name = "sha384",
- .driver_name = "sha384-dx",
- .mac_name = "hmac(sha384)",
- .mac_driver_name = "hmac-sha384-dx",
- .blocksize = SHA384_BLOCK_SIZE,
- .template_ahash = {
- .init = cc_hash_init,
- .update = cc_hash_update,
- .final = cc_hash_final,
- .finup = cc_hash_finup,
- .digest = cc_hash_digest,
- .export = cc_hash_export,
- .import = cc_hash_import,
- .setkey = cc_hash_setkey,
- .halg = {
- .digestsize = SHA384_DIGEST_SIZE,
- .statesize = CC_STATE_SIZE(SHA384_DIGEST_SIZE),
- },
- },
- .hash_mode = DRV_HASH_SHA384,
- .hw_mode = DRV_HASH_HW_SHA512,
- .inter_digestsize = SHA512_DIGEST_SIZE,
- },
- {
- .name = "sha512",
- .driver_name = "sha512-dx",
- .mac_name = "hmac(sha512)",
- .mac_driver_name = "hmac-sha512-dx",
- .blocksize = SHA512_BLOCK_SIZE,
- .template_ahash = {
- .init = cc_hash_init,
- .update = cc_hash_update,
- .final = cc_hash_final,
- .finup = cc_hash_finup,
- .digest = cc_hash_digest,
- .export = cc_hash_export,
- .import = cc_hash_import,
- .setkey = cc_hash_setkey,
- .halg = {
- .digestsize = SHA512_DIGEST_SIZE,
- .statesize = CC_STATE_SIZE(SHA512_DIGEST_SIZE),
- },
- },
- .hash_mode = DRV_HASH_SHA512,
- .hw_mode = DRV_HASH_HW_SHA512,
- .inter_digestsize = SHA512_DIGEST_SIZE,
- },
-#endif
- {
- .name = "md5",
- .driver_name = "md5-dx",
- .mac_name = "hmac(md5)",
- .mac_driver_name = "hmac-md5-dx",
- .blocksize = MD5_HMAC_BLOCK_SIZE,
- .template_ahash = {
- .init = cc_hash_init,
- .update = cc_hash_update,
- .final = cc_hash_final,
- .finup = cc_hash_finup,
- .digest = cc_hash_digest,
- .export = cc_hash_export,
- .import = cc_hash_import,
- .setkey = cc_hash_setkey,
- .halg = {
- .digestsize = MD5_DIGEST_SIZE,
- .statesize = CC_STATE_SIZE(MD5_DIGEST_SIZE),
- },
- },
- .hash_mode = DRV_HASH_MD5,
- .hw_mode = DRV_HASH_HW_MD5,
- .inter_digestsize = MD5_DIGEST_SIZE,
- },
- {
- .mac_name = "xcbc(aes)",
- .mac_driver_name = "xcbc-aes-dx",
- .blocksize = AES_BLOCK_SIZE,
- .template_ahash = {
- .init = cc_hash_init,
- .update = cc_mac_update,
- .final = cc_mac_final,
- .finup = cc_mac_finup,
- .digest = cc_mac_digest,
- .setkey = cc_xcbc_setkey,
- .export = cc_hash_export,
- .import = cc_hash_import,
- .halg = {
- .digestsize = AES_BLOCK_SIZE,
- .statesize = CC_STATE_SIZE(AES_BLOCK_SIZE),
- },
- },
- .hash_mode = DRV_HASH_NULL,
- .hw_mode = DRV_CIPHER_XCBC_MAC,
- .inter_digestsize = AES_BLOCK_SIZE,
- },
- {
- .mac_name = "cmac(aes)",
- .mac_driver_name = "cmac-aes-dx",
- .blocksize = AES_BLOCK_SIZE,
- .template_ahash = {
- .init = cc_hash_init,
- .update = cc_mac_update,
- .final = cc_mac_final,
- .finup = cc_mac_finup,
- .digest = cc_mac_digest,
- .setkey = cc_cmac_setkey,
- .export = cc_hash_export,
- .import = cc_hash_import,
- .halg = {
- .digestsize = AES_BLOCK_SIZE,
- .statesize = CC_STATE_SIZE(AES_BLOCK_SIZE),
- },
- },
- .hash_mode = DRV_HASH_NULL,
- .hw_mode = DRV_CIPHER_CMAC,
- .inter_digestsize = AES_BLOCK_SIZE,
- },
-};
-
-static struct cc_hash_alg *cc_alloc_hash_alg(struct cc_hash_template *template,
- struct device *dev, bool keyed)
-{
- struct cc_hash_alg *t_crypto_alg;
- struct crypto_alg *alg;
- struct ahash_alg *halg;
-
- t_crypto_alg = kzalloc(sizeof(*t_crypto_alg), GFP_KERNEL);
- if (!t_crypto_alg)
- return ERR_PTR(-ENOMEM);
-
- t_crypto_alg->ahash_alg = template->template_ahash;
- halg = &t_crypto_alg->ahash_alg;
- alg = &halg->halg.base;
-
- if (keyed) {
- snprintf(alg->cra_name, CRYPTO_MAX_ALG_NAME, "%s",
- template->mac_name);
- snprintf(alg->cra_driver_name, CRYPTO_MAX_ALG_NAME, "%s",
- template->mac_driver_name);
- } else {
- halg->setkey = NULL;
- snprintf(alg->cra_name, CRYPTO_MAX_ALG_NAME, "%s",
- template->name);
- snprintf(alg->cra_driver_name, CRYPTO_MAX_ALG_NAME, "%s",
- template->driver_name);
- }
- alg->cra_module = THIS_MODULE;
- alg->cra_ctxsize = sizeof(struct cc_hash_ctx);
- alg->cra_priority = CC_CRA_PRIO;
- alg->cra_blocksize = template->blocksize;
- alg->cra_alignmask = 0;
- alg->cra_exit = cc_cra_exit;
-
- alg->cra_init = cc_cra_init;
- alg->cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_TYPE_AHASH |
- CRYPTO_ALG_KERN_DRIVER_ONLY;
- alg->cra_type = &crypto_ahash_type;
-
- t_crypto_alg->hash_mode = template->hash_mode;
- t_crypto_alg->hw_mode = template->hw_mode;
- t_crypto_alg->inter_digestsize = template->inter_digestsize;
-
- return t_crypto_alg;
-}
-
-int cc_init_hash_sram(struct cc_drvdata *drvdata)
-{
- struct cc_hash_handle *hash_handle = drvdata->hash_handle;
- cc_sram_addr_t sram_buff_ofs = hash_handle->digest_len_sram_addr;
- unsigned int larval_seq_len = 0;
- struct cc_hw_desc larval_seq[CC_DIGEST_SIZE_MAX / sizeof(u32)];
- int rc = 0;
-
- /* Copy-to-sram digest-len */
- cc_set_sram_desc(digest_len_init, sram_buff_ofs,
- ARRAY_SIZE(digest_len_init), larval_seq,
- &larval_seq_len);
- rc = send_request_init(drvdata, larval_seq, larval_seq_len);
- if (rc)
- goto init_digest_const_err;
-
- sram_buff_ofs += sizeof(digest_len_init);
- larval_seq_len = 0;
-
-#if (CC_DEV_SHA_MAX > 256)
- /* Copy-to-sram digest-len for sha384/512 */
- cc_set_sram_desc(digest_len_sha512_init, sram_buff_ofs,
- ARRAY_SIZE(digest_len_sha512_init),
- larval_seq, &larval_seq_len);
- rc = send_request_init(drvdata, larval_seq, larval_seq_len);
- if (rc)
- goto init_digest_const_err;
-
- sram_buff_ofs += sizeof(digest_len_sha512_init);
- larval_seq_len = 0;
-#endif
-
- /* The initial digests offset */
- hash_handle->larval_digest_sram_addr = sram_buff_ofs;
-
- /* Copy-to-sram initial SHA* digests */
- cc_set_sram_desc(md5_init, sram_buff_ofs, ARRAY_SIZE(md5_init),
- larval_seq, &larval_seq_len);
- rc = send_request_init(drvdata, larval_seq, larval_seq_len);
- if (rc)
- goto init_digest_const_err;
- sram_buff_ofs += sizeof(md5_init);
- larval_seq_len = 0;
-
- cc_set_sram_desc(sha1_init, sram_buff_ofs,
- ARRAY_SIZE(sha1_init), larval_seq,
- &larval_seq_len);
- rc = send_request_init(drvdata, larval_seq, larval_seq_len);
- if (rc)
- goto init_digest_const_err;
- sram_buff_ofs += sizeof(sha1_init);
- larval_seq_len = 0;
-
- cc_set_sram_desc(sha224_init, sram_buff_ofs,
- ARRAY_SIZE(sha224_init), larval_seq,
- &larval_seq_len);
- rc = send_request_init(drvdata, larval_seq, larval_seq_len);
- if (rc)
- goto init_digest_const_err;
- sram_buff_ofs += sizeof(sha224_init);
- larval_seq_len = 0;
-
- cc_set_sram_desc(sha256_init, sram_buff_ofs,
- ARRAY_SIZE(sha256_init), larval_seq,
- &larval_seq_len);
- rc = send_request_init(drvdata, larval_seq, larval_seq_len);
- if (rc)
- goto init_digest_const_err;
- sram_buff_ofs += sizeof(sha256_init);
- larval_seq_len = 0;
-
-#if (CC_DEV_SHA_MAX > 256)
- cc_set_sram_desc((u32 *)sha384_init, sram_buff_ofs,
- (ARRAY_SIZE(sha384_init) * 2), larval_seq,
- &larval_seq_len);
- rc = send_request_init(drvdata, larval_seq, larval_seq_len);
- if (rc)
- goto init_digest_const_err;
- sram_buff_ofs += sizeof(sha384_init);
- larval_seq_len = 0;
-
- cc_set_sram_desc((u32 *)sha512_init, sram_buff_ofs,
- (ARRAY_SIZE(sha512_init) * 2), larval_seq,
- &larval_seq_len);
- rc = send_request_init(drvdata, larval_seq, larval_seq_len);
- if (rc)
- goto init_digest_const_err;
-#endif
-
-init_digest_const_err:
- return rc;
-}
-
-static void __init cc_swap_dwords(u32 *buf, unsigned long size)
-{
- int i;
- u32 tmp;
-
- for (i = 0; i < size; i += 2) {
- tmp = buf[i];
- buf[i] = buf[i + 1];
- buf[i + 1] = tmp;
- }
-}
-
-/*
- * Due to the way the HW works we need to swap every
- * double word in the SHA384 and SHA512 larval hashes
- */
-void __init cc_hash_global_init(void)
-{
- cc_swap_dwords((u32 *)&sha384_init, (ARRAY_SIZE(sha384_init) * 2));
- cc_swap_dwords((u32 *)&sha512_init, (ARRAY_SIZE(sha512_init) * 2));
-}
-
-int cc_hash_alloc(struct cc_drvdata *drvdata)
-{
- struct cc_hash_handle *hash_handle;
- cc_sram_addr_t sram_buff;
- u32 sram_size_to_alloc;
- struct device *dev = drvdata_to_dev(drvdata);
- int rc = 0;
- int alg;
-
- hash_handle = kzalloc(sizeof(*hash_handle), GFP_KERNEL);
- if (!hash_handle)
- return -ENOMEM;
-
- INIT_LIST_HEAD(&hash_handle->hash_list);
- drvdata->hash_handle = hash_handle;
-
- sram_size_to_alloc = sizeof(digest_len_init) +
-#if (CC_DEV_SHA_MAX > 256)
- sizeof(digest_len_sha512_init) +
- sizeof(sha384_init) +
- sizeof(sha512_init) +
-#endif
- sizeof(md5_init) +
- sizeof(sha1_init) +
- sizeof(sha224_init) +
- sizeof(sha256_init);
-
- sram_buff = cc_sram_alloc(drvdata, sram_size_to_alloc);
- if (sram_buff == NULL_SRAM_ADDR) {
- dev_err(dev, "SRAM pool exhausted\n");
- rc = -ENOMEM;
- goto fail;
- }
-
- /* The initial digest-len offset */
- hash_handle->digest_len_sram_addr = sram_buff;
-
- /*must be set before the alg registration as it is being used there*/
- rc = cc_init_hash_sram(drvdata);
- if (rc) {
- dev_err(dev, "Init digest CONST failed (rc=%d)\n", rc);
- goto fail;
- }
-
- /* ahash registration */
- for (alg = 0; alg < ARRAY_SIZE(driver_hash); alg++) {
- struct cc_hash_alg *t_alg;
- int hw_mode = driver_hash[alg].hw_mode;
-
- /* register hmac version */
- t_alg = cc_alloc_hash_alg(&driver_hash[alg], dev, true);
- if (IS_ERR(t_alg)) {
- rc = PTR_ERR(t_alg);
- dev_err(dev, "%s alg allocation failed\n",
- driver_hash[alg].driver_name);
- goto fail;
- }
- t_alg->drvdata = drvdata;
-
- rc = crypto_register_ahash(&t_alg->ahash_alg);
- if (rc) {
- dev_err(dev, "%s alg registration failed\n",
- driver_hash[alg].driver_name);
- kfree(t_alg);
- goto fail;
- } else {
- list_add_tail(&t_alg->entry, &hash_handle->hash_list);
- }
-
- if (hw_mode == DRV_CIPHER_XCBC_MAC ||
- hw_mode == DRV_CIPHER_CMAC)
- continue;
-
- /* register hash version */
- t_alg = cc_alloc_hash_alg(&driver_hash[alg], dev, false);
- if (IS_ERR(t_alg)) {
- rc = PTR_ERR(t_alg);
- dev_err(dev, "%s alg allocation failed\n",
- driver_hash[alg].driver_name);
- goto fail;
- }
- t_alg->drvdata = drvdata;
-
- rc = crypto_register_ahash(&t_alg->ahash_alg);
- if (rc) {
- dev_err(dev, "%s alg registration failed\n",
- driver_hash[alg].driver_name);
- kfree(t_alg);
- goto fail;
- } else {
- list_add_tail(&t_alg->entry, &hash_handle->hash_list);
- }
- }
-
- return 0;
-
-fail:
- kfree(drvdata->hash_handle);
- drvdata->hash_handle = NULL;
- return rc;
-}
-
-int cc_hash_free(struct cc_drvdata *drvdata)
-{
- struct cc_hash_alg *t_hash_alg, *hash_n;
- struct cc_hash_handle *hash_handle = drvdata->hash_handle;
-
- if (hash_handle) {
- list_for_each_entry_safe(t_hash_alg, hash_n,
- &hash_handle->hash_list, entry) {
- crypto_unregister_ahash(&t_hash_alg->ahash_alg);
- list_del(&t_hash_alg->entry);
- kfree(t_hash_alg);
- }
-
- kfree(hash_handle);
- drvdata->hash_handle = NULL;
- }
- return 0;
-}
-
-static void cc_setup_xcbc(struct ahash_request *areq, struct cc_hw_desc desc[],
- unsigned int *seq_size)
-{
- unsigned int idx = *seq_size;
- struct ahash_req_ctx *state = ahash_request_ctx(areq);
- struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq);
- struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
-
- /* Setup XCBC MAC K1 */
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_DLLI, (ctx->opad_tmp_keys_dma_addr +
- XCBC_MAC_K1_OFFSET),
- CC_AES_128_BIT_KEY_SIZE, NS_BIT);
- set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
- set_cipher_mode(&desc[idx], DRV_CIPHER_XCBC_MAC);
- set_cipher_config0(&desc[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
- set_key_size_aes(&desc[idx], CC_AES_128_BIT_KEY_SIZE);
- set_flow_mode(&desc[idx], S_DIN_to_AES);
- idx++;
-
- /* Setup XCBC MAC K2 */
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_DLLI,
- (ctx->opad_tmp_keys_dma_addr + XCBC_MAC_K2_OFFSET),
- CC_AES_128_BIT_KEY_SIZE, NS_BIT);
- set_setup_mode(&desc[idx], SETUP_LOAD_STATE1);
- set_cipher_mode(&desc[idx], DRV_CIPHER_XCBC_MAC);
- set_cipher_config0(&desc[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
- set_key_size_aes(&desc[idx], CC_AES_128_BIT_KEY_SIZE);
- set_flow_mode(&desc[idx], S_DIN_to_AES);
- idx++;
-
- /* Setup XCBC MAC K3 */
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_DLLI,
- (ctx->opad_tmp_keys_dma_addr + XCBC_MAC_K3_OFFSET),
- CC_AES_128_BIT_KEY_SIZE, NS_BIT);
- set_setup_mode(&desc[idx], SETUP_LOAD_STATE2);
- set_cipher_mode(&desc[idx], DRV_CIPHER_XCBC_MAC);
- set_cipher_config0(&desc[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
- set_key_size_aes(&desc[idx], CC_AES_128_BIT_KEY_SIZE);
- set_flow_mode(&desc[idx], S_DIN_to_AES);
- idx++;
-
- /* Loading MAC state */
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_DLLI, state->digest_buff_dma_addr,
- CC_AES_BLOCK_SIZE, NS_BIT);
- set_setup_mode(&desc[idx], SETUP_LOAD_STATE0);
- set_cipher_mode(&desc[idx], DRV_CIPHER_XCBC_MAC);
- set_cipher_config0(&desc[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
- set_key_size_aes(&desc[idx], CC_AES_128_BIT_KEY_SIZE);
- set_flow_mode(&desc[idx], S_DIN_to_AES);
- idx++;
- *seq_size = idx;
-}
-
-static void cc_setup_cmac(struct ahash_request *areq, struct cc_hw_desc desc[],
- unsigned int *seq_size)
-{
- unsigned int idx = *seq_size;
- struct ahash_req_ctx *state = ahash_request_ctx(areq);
- struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq);
- struct cc_hash_ctx *ctx = crypto_ahash_ctx(tfm);
-
- /* Setup CMAC Key */
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_DLLI, ctx->opad_tmp_keys_dma_addr,
- ((ctx->key_params.keylen == 24) ? AES_MAX_KEY_SIZE :
- ctx->key_params.keylen), NS_BIT);
- set_setup_mode(&desc[idx], SETUP_LOAD_KEY0);
- set_cipher_mode(&desc[idx], DRV_CIPHER_CMAC);
- set_cipher_config0(&desc[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
- set_key_size_aes(&desc[idx], ctx->key_params.keylen);
- set_flow_mode(&desc[idx], S_DIN_to_AES);
- idx++;
-
- /* Load MAC state */
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_DLLI, state->digest_buff_dma_addr,
- CC_AES_BLOCK_SIZE, NS_BIT);
- set_setup_mode(&desc[idx], SETUP_LOAD_STATE0);
- set_cipher_mode(&desc[idx], DRV_CIPHER_CMAC);
- set_cipher_config0(&desc[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
- set_key_size_aes(&desc[idx], ctx->key_params.keylen);
- set_flow_mode(&desc[idx], S_DIN_to_AES);
- idx++;
- *seq_size = idx;
-}
-
-static void cc_set_desc(struct ahash_req_ctx *areq_ctx,
- struct cc_hash_ctx *ctx, unsigned int flow_mode,
- struct cc_hw_desc desc[], bool is_not_last_data,
- unsigned int *seq_size)
-{
- unsigned int idx = *seq_size;
- struct device *dev = drvdata_to_dev(ctx->drvdata);
-
- if (areq_ctx->data_dma_buf_type == CC_DMA_BUF_DLLI) {
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_DLLI,
- sg_dma_address(areq_ctx->curr_sg),
- areq_ctx->curr_sg->length, NS_BIT);
- set_flow_mode(&desc[idx], flow_mode);
- idx++;
- } else {
- if (areq_ctx->data_dma_buf_type == CC_DMA_BUF_NULL) {
- dev_dbg(dev, " NULL mode\n");
- /* nothing to build */
- return;
- }
- /* bypass */
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_DLLI,
- areq_ctx->mlli_params.mlli_dma_addr,
- areq_ctx->mlli_params.mlli_len, NS_BIT);
- set_dout_sram(&desc[idx], ctx->drvdata->mlli_sram_addr,
- areq_ctx->mlli_params.mlli_len);
- set_flow_mode(&desc[idx], BYPASS);
- idx++;
- /* process */
- hw_desc_init(&desc[idx]);
- set_din_type(&desc[idx], DMA_MLLI,
- ctx->drvdata->mlli_sram_addr,
- areq_ctx->mlli_nents, NS_BIT);
- set_flow_mode(&desc[idx], flow_mode);
- idx++;
- }
- if (is_not_last_data)
- set_din_not_last_indication(&desc[(idx - 1)]);
- /* return updated desc sequence size */
- *seq_size = idx;
-}
-
-static const void *cc_larval_digest(struct device *dev, u32 mode)
-{
- switch (mode) {
- case DRV_HASH_MD5:
- return md5_init;
- case DRV_HASH_SHA1:
- return sha1_init;
- case DRV_HASH_SHA224:
- return sha224_init;
- case DRV_HASH_SHA256:
- return sha256_init;
-#if (CC_DEV_SHA_MAX > 256)
- case DRV_HASH_SHA384:
- return sha384_init;
- case DRV_HASH_SHA512:
- return sha512_init;
-#endif
- default:
- dev_err(dev, "Invalid hash mode (%d)\n", mode);
- return md5_init;
- }
-}
-
-/*!
- * Gets the address of the initial digest in SRAM
- * according to the given hash mode
- *
- * \param drvdata
- * \param mode The Hash mode. Supported modes: MD5/SHA1/SHA224/SHA256
- *
- * \return u32 The address of the initial digest in SRAM
- */
-cc_sram_addr_t cc_larval_digest_addr(void *drvdata, u32 mode)
-{
- struct cc_drvdata *_drvdata = (struct cc_drvdata *)drvdata;
- struct cc_hash_handle *hash_handle = _drvdata->hash_handle;
- struct device *dev = drvdata_to_dev(_drvdata);
-
- switch (mode) {
- case DRV_HASH_NULL:
- break; /*Ignore*/
- case DRV_HASH_MD5:
- return (hash_handle->larval_digest_sram_addr);
- case DRV_HASH_SHA1:
- return (hash_handle->larval_digest_sram_addr +
- sizeof(md5_init));
- case DRV_HASH_SHA224:
- return (hash_handle->larval_digest_sram_addr +
- sizeof(md5_init) +
- sizeof(sha1_init));
- case DRV_HASH_SHA256:
- return (hash_handle->larval_digest_sram_addr +
- sizeof(md5_init) +
- sizeof(sha1_init) +
- sizeof(sha224_init));
-#if (CC_DEV_SHA_MAX > 256)
- case DRV_HASH_SHA384:
- return (hash_handle->larval_digest_sram_addr +
- sizeof(md5_init) +
- sizeof(sha1_init) +
- sizeof(sha224_init) +
- sizeof(sha256_init));
- case DRV_HASH_SHA512:
- return (hash_handle->larval_digest_sram_addr +
- sizeof(md5_init) +
- sizeof(sha1_init) +
- sizeof(sha224_init) +
- sizeof(sha256_init) +
- sizeof(sha384_init));
-#endif
- default:
- dev_err(dev, "Invalid hash mode (%d)\n", mode);
- }
-
- /*This is valid wrong value to avoid kernel crash*/
- return hash_handle->larval_digest_sram_addr;
-}
-
-cc_sram_addr_t
-cc_digest_len_addr(void *drvdata, u32 mode)
-{
- struct cc_drvdata *_drvdata = (struct cc_drvdata *)drvdata;
- struct cc_hash_handle *hash_handle = _drvdata->hash_handle;
- cc_sram_addr_t digest_len_addr = hash_handle->digest_len_sram_addr;
-
- switch (mode) {
- case DRV_HASH_SHA1:
- case DRV_HASH_SHA224:
- case DRV_HASH_SHA256:
- case DRV_HASH_MD5:
- return digest_len_addr;
-#if (CC_DEV_SHA_MAX > 256)
- case DRV_HASH_SHA384:
- case DRV_HASH_SHA512:
- return digest_len_addr + sizeof(digest_len_init);
-#endif
- default:
- return digest_len_addr; /*to avoid kernel crash*/
- }
-}
-
diff --git a/drivers/staging/ccree/cc_hash.h b/drivers/staging/ccree/cc_hash.h
deleted file mode 100644
index aa42b8f4348d..000000000000
--- a/drivers/staging/ccree/cc_hash.h
+++ /dev/null
@@ -1,114 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
-
-/* \file cc_hash.h
- * ARM CryptoCell Hash Crypto API
- */
-
-#ifndef __CC_HASH_H__
-#define __CC_HASH_H__
-
-#include "cc_buffer_mgr.h"
-
-#define HMAC_IPAD_CONST 0x36363636
-#define HMAC_OPAD_CONST 0x5C5C5C5C
-#if (CC_DEV_SHA_MAX > 256)
-#define HASH_LEN_SIZE 16
-#define CC_MAX_HASH_DIGEST_SIZE SHA512_DIGEST_SIZE
-#define CC_MAX_HASH_BLCK_SIZE SHA512_BLOCK_SIZE
-#else
-#define HASH_LEN_SIZE 8
-#define CC_MAX_HASH_DIGEST_SIZE SHA256_DIGEST_SIZE
-#define CC_MAX_HASH_BLCK_SIZE SHA256_BLOCK_SIZE
-#endif
-
-#define XCBC_MAC_K1_OFFSET 0
-#define XCBC_MAC_K2_OFFSET 16
-#define XCBC_MAC_K3_OFFSET 32
-
-#define CC_EXPORT_MAGIC 0xC2EE1070U
-
-/* this struct was taken from drivers/crypto/nx/nx-aes-xcbc.c and it is used
- * for xcbc/cmac statesize
- */
-struct aeshash_state {
- u8 state[AES_BLOCK_SIZE];
- unsigned int count;
- u8 buffer[AES_BLOCK_SIZE];
-};
-
-/* ahash state */
-struct ahash_req_ctx {
- u8 buffers[2][CC_MAX_HASH_BLCK_SIZE] ____cacheline_aligned;
- u8 digest_result_buff[CC_MAX_HASH_DIGEST_SIZE] ____cacheline_aligned;
- u8 digest_buff[CC_MAX_HASH_DIGEST_SIZE] ____cacheline_aligned;
- u8 opad_digest_buff[CC_MAX_HASH_DIGEST_SIZE] ____cacheline_aligned;
- u8 digest_bytes_len[HASH_LEN_SIZE] ____cacheline_aligned;
- struct async_gen_req_ctx gen_ctx ____cacheline_aligned;
- enum cc_req_dma_buf_type data_dma_buf_type;
- dma_addr_t opad_digest_dma_addr;
- dma_addr_t digest_buff_dma_addr;
- dma_addr_t digest_bytes_len_dma_addr;
- dma_addr_t digest_result_dma_addr;
- u32 buf_cnt[2];
- u32 buff_index;
- u32 xcbc_count; /* count xcbc update operatations */
- struct scatterlist buff_sg[2];
- struct scatterlist *curr_sg;
- u32 in_nents;
- u32 mlli_nents;
- struct mlli_params mlli_params;
-};
-
-static inline u32 *cc_hash_buf_cnt(struct ahash_req_ctx *state)
-{
- return &state->buf_cnt[state->buff_index];
-}
-
-static inline u8 *cc_hash_buf(struct ahash_req_ctx *state)
-{
- return state->buffers[state->buff_index];
-}
-
-static inline u32 *cc_next_buf_cnt(struct ahash_req_ctx *state)
-{
- return &state->buf_cnt[state->buff_index ^ 1];
-}
-
-static inline u8 *cc_next_buf(struct ahash_req_ctx *state)
-{
- return state->buffers[state->buff_index ^ 1];
-}
-
-int cc_hash_alloc(struct cc_drvdata *drvdata);
-int cc_init_hash_sram(struct cc_drvdata *drvdata);
-int cc_hash_free(struct cc_drvdata *drvdata);
-
-/*!
- * Gets the initial digest length
- *
- * \param drvdata
- * \param mode The Hash mode. Supported modes:
- * MD5/SHA1/SHA224/SHA256/SHA384/SHA512
- *
- * \return u32 returns the address of the initial digest length in SRAM
- */
-cc_sram_addr_t
-cc_digest_len_addr(void *drvdata, u32 mode);
-
-/*!
- * Gets the address of the initial digest in SRAM
- * according to the given hash mode
- *
- * \param drvdata
- * \param mode The Hash mode. Supported modes:
- * MD5/SHA1/SHA224/SHA256/SHA384/SHA512
- *
- * \return u32 The address of the initial digest in SRAM
- */
-cc_sram_addr_t cc_larval_digest_addr(void *drvdata, u32 mode);
-
-void cc_hash_global_init(void);
-
-#endif /*__CC_HASH_H__*/
-
diff --git a/drivers/staging/ccree/cc_host_regs.h b/drivers/staging/ccree/cc_host_regs.h
deleted file mode 100644
index 69ef2fa0cb9b..000000000000
--- a/drivers/staging/ccree/cc_host_regs.h
+++ /dev/null
@@ -1,142 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
-
-#ifndef __CC_HOST_H__
-#define __CC_HOST_H__
-
-// --------------------------------------
-// BLOCK: HOST_P
-// --------------------------------------
-#define CC_HOST_IRR_REG_OFFSET 0xA00UL
-#define CC_HOST_IRR_DSCRPTR_COMPLETION_LOW_INT_BIT_SHIFT 0x2UL
-#define CC_HOST_IRR_DSCRPTR_COMPLETION_LOW_INT_BIT_SIZE 0x1UL
-#define CC_HOST_IRR_AXI_ERR_INT_BIT_SHIFT 0x8UL
-#define CC_HOST_IRR_AXI_ERR_INT_BIT_SIZE 0x1UL
-#define CC_HOST_IRR_GPR0_BIT_SHIFT 0xBUL
-#define CC_HOST_IRR_GPR0_BIT_SIZE 0x1UL
-#define CC_HOST_IRR_DSCRPTR_WATERMARK_INT_BIT_SHIFT 0x13UL
-#define CC_HOST_IRR_DSCRPTR_WATERMARK_INT_BIT_SIZE 0x1UL
-#define CC_HOST_IRR_AXIM_COMP_INT_BIT_SHIFT 0x17UL
-#define CC_HOST_IRR_AXIM_COMP_INT_BIT_SIZE 0x1UL
-#define CC_HOST_IMR_REG_OFFSET 0xA04UL
-#define CC_HOST_IMR_NOT_USED_MASK_BIT_SHIFT 0x1UL
-#define CC_HOST_IMR_NOT_USED_MASK_BIT_SIZE 0x1UL
-#define CC_HOST_IMR_DSCRPTR_COMPLETION_MASK_BIT_SHIFT 0x2UL
-#define CC_HOST_IMR_DSCRPTR_COMPLETION_MASK_BIT_SIZE 0x1UL
-#define CC_HOST_IMR_AXI_ERR_MASK_BIT_SHIFT 0x8UL
-#define CC_HOST_IMR_AXI_ERR_MASK_BIT_SIZE 0x1UL
-#define CC_HOST_IMR_GPR0_BIT_SHIFT 0xBUL
-#define CC_HOST_IMR_GPR0_BIT_SIZE 0x1UL
-#define CC_HOST_IMR_DSCRPTR_WATERMARK_MASK0_BIT_SHIFT 0x13UL
-#define CC_HOST_IMR_DSCRPTR_WATERMARK_MASK0_BIT_SIZE 0x1UL
-#define CC_HOST_IMR_AXIM_COMP_INT_MASK_BIT_SHIFT 0x17UL
-#define CC_HOST_IMR_AXIM_COMP_INT_MASK_BIT_SIZE 0x1UL
-#define CC_HOST_ICR_REG_OFFSET 0xA08UL
-#define CC_HOST_ICR_DSCRPTR_COMPLETION_BIT_SHIFT 0x2UL
-#define CC_HOST_ICR_DSCRPTR_COMPLETION_BIT_SIZE 0x1UL
-#define CC_HOST_ICR_AXI_ERR_CLEAR_BIT_SHIFT 0x8UL
-#define CC_HOST_ICR_AXI_ERR_CLEAR_BIT_SIZE 0x1UL
-#define CC_HOST_ICR_GPR_INT_CLEAR_BIT_SHIFT 0xBUL
-#define CC_HOST_ICR_GPR_INT_CLEAR_BIT_SIZE 0x1UL
-#define CC_HOST_ICR_DSCRPTR_WATERMARK_QUEUE0_CLEAR_BIT_SHIFT 0x13UL
-#define CC_HOST_ICR_DSCRPTR_WATERMARK_QUEUE0_CLEAR_BIT_SIZE 0x1UL
-#define CC_HOST_ICR_AXIM_COMP_INT_CLEAR_BIT_SHIFT 0x17UL
-#define CC_HOST_ICR_AXIM_COMP_INT_CLEAR_BIT_SIZE 0x1UL
-#define CC_HOST_SIGNATURE_REG_OFFSET 0xA24UL
-#define CC_HOST_SIGNATURE_VALUE_BIT_SHIFT 0x0UL
-#define CC_HOST_SIGNATURE_VALUE_BIT_SIZE 0x20UL
-#define CC_HOST_BOOT_REG_OFFSET 0xA28UL
-#define CC_HOST_BOOT_SYNTHESIS_CONFIG_BIT_SHIFT 0x0UL
-#define CC_HOST_BOOT_SYNTHESIS_CONFIG_BIT_SIZE 0x1UL
-#define CC_HOST_BOOT_LARGE_RKEK_LOCAL_BIT_SHIFT 0x1UL
-#define CC_HOST_BOOT_LARGE_RKEK_LOCAL_BIT_SIZE 0x1UL
-#define CC_HOST_BOOT_HASH_IN_FUSES_LOCAL_BIT_SHIFT 0x2UL
-#define CC_HOST_BOOT_HASH_IN_FUSES_LOCAL_BIT_SIZE 0x1UL
-#define CC_HOST_BOOT_EXT_MEM_SECURED_LOCAL_BIT_SHIFT 0x3UL
-#define CC_HOST_BOOT_EXT_MEM_SECURED_LOCAL_BIT_SIZE 0x1UL
-#define CC_HOST_BOOT_RKEK_ECC_EXISTS_LOCAL_N_BIT_SHIFT 0x5UL
-#define CC_HOST_BOOT_RKEK_ECC_EXISTS_LOCAL_N_BIT_SIZE 0x1UL
-#define CC_HOST_BOOT_SRAM_SIZE_LOCAL_BIT_SHIFT 0x6UL
-#define CC_HOST_BOOT_SRAM_SIZE_LOCAL_BIT_SIZE 0x3UL
-#define CC_HOST_BOOT_DSCRPTR_EXISTS_LOCAL_BIT_SHIFT 0x9UL
-#define CC_HOST_BOOT_DSCRPTR_EXISTS_LOCAL_BIT_SIZE 0x1UL
-#define CC_HOST_BOOT_PAU_EXISTS_LOCAL_BIT_SHIFT 0xAUL
-#define CC_HOST_BOOT_PAU_EXISTS_LOCAL_BIT_SIZE 0x1UL
-#define CC_HOST_BOOT_RNG_EXISTS_LOCAL_BIT_SHIFT 0xBUL
-#define CC_HOST_BOOT_RNG_EXISTS_LOCAL_BIT_SIZE 0x1UL
-#define CC_HOST_BOOT_PKA_EXISTS_LOCAL_BIT_SHIFT 0xCUL
-#define CC_HOST_BOOT_PKA_EXISTS_LOCAL_BIT_SIZE 0x1UL
-#define CC_HOST_BOOT_RC4_EXISTS_LOCAL_BIT_SHIFT 0xDUL
-#define CC_HOST_BOOT_RC4_EXISTS_LOCAL_BIT_SIZE 0x1UL
-#define CC_HOST_BOOT_SHA_512_PRSNT_LOCAL_BIT_SHIFT 0xEUL
-#define CC_HOST_BOOT_SHA_512_PRSNT_LOCAL_BIT_SIZE 0x1UL
-#define CC_HOST_BOOT_SHA_256_PRSNT_LOCAL_BIT_SHIFT 0xFUL
-#define CC_HOST_BOOT_SHA_256_PRSNT_LOCAL_BIT_SIZE 0x1UL
-#define CC_HOST_BOOT_MD5_PRSNT_LOCAL_BIT_SHIFT 0x10UL
-#define CC_HOST_BOOT_MD5_PRSNT_LOCAL_BIT_SIZE 0x1UL
-#define CC_HOST_BOOT_HASH_EXISTS_LOCAL_BIT_SHIFT 0x11UL
-#define CC_HOST_BOOT_HASH_EXISTS_LOCAL_BIT_SIZE 0x1UL
-#define CC_HOST_BOOT_C2_EXISTS_LOCAL_BIT_SHIFT 0x12UL
-#define CC_HOST_BOOT_C2_EXISTS_LOCAL_BIT_SIZE 0x1UL
-#define CC_HOST_BOOT_DES_EXISTS_LOCAL_BIT_SHIFT 0x13UL
-#define CC_HOST_BOOT_DES_EXISTS_LOCAL_BIT_SIZE 0x1UL
-#define CC_HOST_BOOT_AES_XCBC_MAC_EXISTS_LOCAL_BIT_SHIFT 0x14UL
-#define CC_HOST_BOOT_AES_XCBC_MAC_EXISTS_LOCAL_BIT_SIZE 0x1UL
-#define CC_HOST_BOOT_AES_CMAC_EXISTS_LOCAL_BIT_SHIFT 0x15UL
-#define CC_HOST_BOOT_AES_CMAC_EXISTS_LOCAL_BIT_SIZE 0x1UL
-#define CC_HOST_BOOT_AES_CCM_EXISTS_LOCAL_BIT_SHIFT 0x16UL
-#define CC_HOST_BOOT_AES_CCM_EXISTS_LOCAL_BIT_SIZE 0x1UL
-#define CC_HOST_BOOT_AES_XEX_HW_T_CALC_LOCAL_BIT_SHIFT 0x17UL
-#define CC_HOST_BOOT_AES_XEX_HW_T_CALC_LOCAL_BIT_SIZE 0x1UL
-#define CC_HOST_BOOT_AES_XEX_EXISTS_LOCAL_BIT_SHIFT 0x18UL
-#define CC_HOST_BOOT_AES_XEX_EXISTS_LOCAL_BIT_SIZE 0x1UL
-#define CC_HOST_BOOT_CTR_EXISTS_LOCAL_BIT_SHIFT 0x19UL
-#define CC_HOST_BOOT_CTR_EXISTS_LOCAL_BIT_SIZE 0x1UL
-#define CC_HOST_BOOT_AES_DIN_BYTE_RESOLUTION_LOCAL_BIT_SHIFT 0x1AUL
-#define CC_HOST_BOOT_AES_DIN_BYTE_RESOLUTION_LOCAL_BIT_SIZE 0x1UL
-#define CC_HOST_BOOT_TUNNELING_ENB_LOCAL_BIT_SHIFT 0x1BUL
-#define CC_HOST_BOOT_TUNNELING_ENB_LOCAL_BIT_SIZE 0x1UL
-#define CC_HOST_BOOT_SUPPORT_256_192_KEY_LOCAL_BIT_SHIFT 0x1CUL
-#define CC_HOST_BOOT_SUPPORT_256_192_KEY_LOCAL_BIT_SIZE 0x1UL
-#define CC_HOST_BOOT_ONLY_ENCRYPT_LOCAL_BIT_SHIFT 0x1DUL
-#define CC_HOST_BOOT_ONLY_ENCRYPT_LOCAL_BIT_SIZE 0x1UL
-#define CC_HOST_BOOT_AES_EXISTS_LOCAL_BIT_SHIFT 0x1EUL
-#define CC_HOST_BOOT_AES_EXISTS_LOCAL_BIT_SIZE 0x1UL
-#define CC_HOST_VERSION_REG_OFFSET 0xA40UL
-#define CC_HOST_VERSION_VALUE_BIT_SHIFT 0x0UL
-#define CC_HOST_VERSION_VALUE_BIT_SIZE 0x20UL
-#define CC_HOST_KFDE0_VALID_REG_OFFSET 0xA60UL
-#define CC_HOST_KFDE0_VALID_VALUE_BIT_SHIFT 0x0UL
-#define CC_HOST_KFDE0_VALID_VALUE_BIT_SIZE 0x1UL
-#define CC_HOST_KFDE1_VALID_REG_OFFSET 0xA64UL
-#define CC_HOST_KFDE1_VALID_VALUE_BIT_SHIFT 0x0UL
-#define CC_HOST_KFDE1_VALID_VALUE_BIT_SIZE 0x1UL
-#define CC_HOST_KFDE2_VALID_REG_OFFSET 0xA68UL
-#define CC_HOST_KFDE2_VALID_VALUE_BIT_SHIFT 0x0UL
-#define CC_HOST_KFDE2_VALID_VALUE_BIT_SIZE 0x1UL
-#define CC_HOST_KFDE3_VALID_REG_OFFSET 0xA6CUL
-#define CC_HOST_KFDE3_VALID_VALUE_BIT_SHIFT 0x0UL
-#define CC_HOST_KFDE3_VALID_VALUE_BIT_SIZE 0x1UL
-#define CC_HOST_GPR0_REG_OFFSET 0xA70UL
-#define CC_HOST_GPR0_VALUE_BIT_SHIFT 0x0UL
-#define CC_HOST_GPR0_VALUE_BIT_SIZE 0x20UL
-#define CC_GPR_HOST_REG_OFFSET 0xA74UL
-#define CC_GPR_HOST_VALUE_BIT_SHIFT 0x0UL
-#define CC_GPR_HOST_VALUE_BIT_SIZE 0x20UL
-#define CC_HOST_POWER_DOWN_EN_REG_OFFSET 0xA78UL
-#define CC_HOST_POWER_DOWN_EN_VALUE_BIT_SHIFT 0x0UL
-#define CC_HOST_POWER_DOWN_EN_VALUE_BIT_SIZE 0x1UL
-// --------------------------------------
-// BLOCK: HOST_SRAM
-// --------------------------------------
-#define CC_SRAM_DATA_REG_OFFSET 0xF00UL
-#define CC_SRAM_DATA_VALUE_BIT_SHIFT 0x0UL
-#define CC_SRAM_DATA_VALUE_BIT_SIZE 0x20UL
-#define CC_SRAM_ADDR_REG_OFFSET 0xF04UL
-#define CC_SRAM_ADDR_VALUE_BIT_SHIFT 0x0UL
-#define CC_SRAM_ADDR_VALUE_BIT_SIZE 0xFUL
-#define CC_SRAM_DATA_READY_REG_OFFSET 0xF08UL
-#define CC_SRAM_DATA_READY_VALUE_BIT_SHIFT 0x0UL
-#define CC_SRAM_DATA_READY_VALUE_BIT_SIZE 0x1UL
-
-#endif //__CC_HOST_H__
diff --git a/drivers/staging/ccree/cc_hw_queue_defs.h b/drivers/staging/ccree/cc_hw_queue_defs.h
deleted file mode 100644
index a79f28cec5ae..000000000000
--- a/drivers/staging/ccree/cc_hw_queue_defs.h
+++ /dev/null
@@ -1,590 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
-
-#ifndef __CC_HW_QUEUE_DEFS_H__
-#define __CC_HW_QUEUE_DEFS_H__
-
-#include <linux/types.h>
-
-#include "cc_kernel_regs.h"
-#include <linux/bitfield.h>
-
-/******************************************************************************
- * DEFINITIONS
- ******************************************************************************/
-
-#define HW_DESC_SIZE_WORDS 6
-/* Define max. available slots in HW queue */
-#define HW_QUEUE_SLOTS_MAX 15
-
-#define CC_REG_LOW(word, name) \
- (CC_DSCRPTR_QUEUE_WORD ## word ## _ ## name ## _BIT_SHIFT)
-
-#define CC_REG_HIGH(word, name) \
- (CC_REG_LOW(word, name) + \
- CC_DSCRPTR_QUEUE_WORD ## word ## _ ## name ## _BIT_SIZE - 1)
-
-#define CC_GENMASK(word, name) \
- GENMASK(CC_REG_HIGH(word, name), CC_REG_LOW(word, name))
-
-#define WORD0_VALUE CC_GENMASK(0, VALUE)
-#define WORD1_DIN_CONST_VALUE CC_GENMASK(1, DIN_CONST_VALUE)
-#define WORD1_DIN_DMA_MODE CC_GENMASK(1, DIN_DMA_MODE)
-#define WORD1_DIN_SIZE CC_GENMASK(1, DIN_SIZE)
-#define WORD1_NOT_LAST CC_GENMASK(1, NOT_LAST)
-#define WORD1_NS_BIT CC_GENMASK(1, NS_BIT)
-#define WORD2_VALUE CC_GENMASK(2, VALUE)
-#define WORD3_DOUT_DMA_MODE CC_GENMASK(3, DOUT_DMA_MODE)
-#define WORD3_DOUT_LAST_IND CC_GENMASK(3, DOUT_LAST_IND)
-#define WORD3_DOUT_SIZE CC_GENMASK(3, DOUT_SIZE)
-#define WORD3_HASH_XOR_BIT CC_GENMASK(3, HASH_XOR_BIT)
-#define WORD3_NS_BIT CC_GENMASK(3, NS_BIT)
-#define WORD3_QUEUE_LAST_IND CC_GENMASK(3, QUEUE_LAST_IND)
-#define WORD4_ACK_NEEDED CC_GENMASK(4, ACK_NEEDED)
-#define WORD4_AES_SEL_N_HASH CC_GENMASK(4, AES_SEL_N_HASH)
-#define WORD4_BYTES_SWAP CC_GENMASK(4, BYTES_SWAP)
-#define WORD4_CIPHER_CONF0 CC_GENMASK(4, CIPHER_CONF0)
-#define WORD4_CIPHER_CONF1 CC_GENMASK(4, CIPHER_CONF1)
-#define WORD4_CIPHER_CONF2 CC_GENMASK(4, CIPHER_CONF2)
-#define WORD4_CIPHER_DO CC_GENMASK(4, CIPHER_DO)
-#define WORD4_CIPHER_MODE CC_GENMASK(4, CIPHER_MODE)
-#define WORD4_CMAC_SIZE0 CC_GENMASK(4, CMAC_SIZE0)
-#define WORD4_DATA_FLOW_MODE CC_GENMASK(4, DATA_FLOW_MODE)
-#define WORD4_KEY_SIZE CC_GENMASK(4, KEY_SIZE)
-#define WORD4_SETUP_OPERATION CC_GENMASK(4, SETUP_OPERATION)
-#define WORD5_DIN_ADDR_HIGH CC_GENMASK(5, DIN_ADDR_HIGH)
-#define WORD5_DOUT_ADDR_HIGH CC_GENMASK(5, DOUT_ADDR_HIGH)
-
-/******************************************************************************
- * TYPE DEFINITIONS
- ******************************************************************************/
-
-struct cc_hw_desc {
- union {
- u32 word[HW_DESC_SIZE_WORDS];
- u16 hword[HW_DESC_SIZE_WORDS * 2];
- };
-};
-
-enum cc_axi_sec {
- AXI_SECURE = 0,
- AXI_NOT_SECURE = 1
-};
-
-enum cc_desc_direction {
- DESC_DIRECTION_ILLEGAL = -1,
- DESC_DIRECTION_ENCRYPT_ENCRYPT = 0,
- DESC_DIRECTION_DECRYPT_DECRYPT = 1,
- DESC_DIRECTION_DECRYPT_ENCRYPT = 3,
- DESC_DIRECTION_END = S32_MAX,
-};
-
-enum cc_dma_mode {
- DMA_MODE_NULL = -1,
- NO_DMA = 0,
- DMA_SRAM = 1,
- DMA_DLLI = 2,
- DMA_MLLI = 3,
- DMA_MODE_END = S32_MAX,
-};
-
-enum cc_flow_mode {
- FLOW_MODE_NULL = -1,
- /* data flows */
- BYPASS = 0,
- DIN_AES_DOUT = 1,
- AES_to_HASH = 2,
- AES_and_HASH = 3,
- DIN_DES_DOUT = 4,
- DES_to_HASH = 5,
- DES_and_HASH = 6,
- DIN_HASH = 7,
- DIN_HASH_and_BYPASS = 8,
- AESMAC_and_BYPASS = 9,
- AES_to_HASH_and_DOUT = 10,
- DIN_RC4_DOUT = 11,
- DES_to_HASH_and_DOUT = 12,
- AES_to_AES_to_HASH_and_DOUT = 13,
- AES_to_AES_to_HASH = 14,
- AES_to_HASH_and_AES = 15,
- DIN_AES_AESMAC = 17,
- HASH_to_DOUT = 18,
- /* setup flows */
- S_DIN_to_AES = 32,
- S_DIN_to_AES2 = 33,
- S_DIN_to_DES = 34,
- S_DIN_to_RC4 = 35,
- S_DIN_to_HASH = 37,
- S_AES_to_DOUT = 38,
- S_AES2_to_DOUT = 39,
- S_RC4_to_DOUT = 41,
- S_DES_to_DOUT = 42,
- S_HASH_to_DOUT = 43,
- SET_FLOW_ID = 44,
- FLOW_MODE_END = S32_MAX,
-};
-
-enum cc_tunnel_op {
- TUNNEL_OP_INVALID = -1,
- TUNNEL_OFF = 0,
- TUNNEL_ON = 1,
- TUNNEL_OP_END = S32_MAX,
-};
-
-enum cc_setup_op {
- SETUP_LOAD_NOP = 0,
- SETUP_LOAD_STATE0 = 1,
- SETUP_LOAD_STATE1 = 2,
- SETUP_LOAD_STATE2 = 3,
- SETUP_LOAD_KEY0 = 4,
- SETUP_LOAD_XEX_KEY = 5,
- SETUP_WRITE_STATE0 = 8,
- SETUP_WRITE_STATE1 = 9,
- SETUP_WRITE_STATE2 = 10,
- SETUP_WRITE_STATE3 = 11,
- SETUP_OP_END = S32_MAX,
-};
-
-enum cc_aes_mac_selector {
- AES_SK = 1,
- AES_CMAC_INIT = 2,
- AES_CMAC_SIZE0 = 3,
- AES_MAC_END = S32_MAX,
-};
-
-#define HW_KEY_MASK_CIPHER_DO 0x3
-#define HW_KEY_SHIFT_CIPHER_CFG2 2
-
-/* HwCryptoKey[1:0] is mapped to cipher_do[1:0] */
-/* HwCryptoKey[2:3] is mapped to cipher_config2[1:0] */
-enum cc_hw_crypto_key {
- USER_KEY = 0, /* 0x0000 */
- ROOT_KEY = 1, /* 0x0001 */
- PROVISIONING_KEY = 2, /* 0x0010 */ /* ==KCP */
- SESSION_KEY = 3, /* 0x0011 */
- RESERVED_KEY = 4, /* NA */
- PLATFORM_KEY = 5, /* 0x0101 */
- CUSTOMER_KEY = 6, /* 0x0110 */
- KFDE0_KEY = 7, /* 0x0111 */
- KFDE1_KEY = 9, /* 0x1001 */
- KFDE2_KEY = 10, /* 0x1010 */
- KFDE3_KEY = 11, /* 0x1011 */
- END_OF_KEYS = S32_MAX,
-};
-
-enum cc_hw_aes_key_size {
- AES_128_KEY = 0,
- AES_192_KEY = 1,
- AES_256_KEY = 2,
- END_OF_AES_KEYS = S32_MAX,
-};
-
-enum cc_hw_des_key_size {
- DES_ONE_KEY = 0,
- DES_TWO_KEYS = 1,
- DES_THREE_KEYS = 2,
- END_OF_DES_KEYS = S32_MAX,
-};
-
-enum cc_hash_conf_pad {
- HASH_PADDING_DISABLED = 0,
- HASH_PADDING_ENABLED = 1,
- HASH_DIGEST_RESULT_LITTLE_ENDIAN = 2,
- HASH_CONFIG1_PADDING_RESERVE32 = S32_MAX,
-};
-
-enum cc_hash_cipher_pad {
- DO_NOT_PAD = 0,
- DO_PAD = 1,
- HASH_CIPHER_DO_PADDING_RESERVE32 = S32_MAX,
-};
-
-/*****************************/
-/* Descriptor packing macros */
-/*****************************/
-
-/*
- * Init a HW descriptor struct
- * @pdesc: pointer HW descriptor struct
- */
-static inline void hw_desc_init(struct cc_hw_desc *pdesc)
-{
- memset(pdesc, 0, sizeof(struct cc_hw_desc));
-}
-
-/*
- * Indicates the end of current HW descriptors flow and release the HW engines.
- *
- * @pdesc: pointer HW descriptor struct
- */
-static inline void set_queue_last_ind(struct cc_hw_desc *pdesc)
-{
- pdesc->word[3] |= FIELD_PREP(WORD3_QUEUE_LAST_IND, 1);
-}
-
-/*
- * Set the DIN field of a HW descriptors
- *
- * @pdesc: pointer HW descriptor struct
- * @dma_mode: dmaMode The DMA mode: NO_DMA, SRAM, DLLI, MLLI, CONSTANT
- * @addr: dinAdr DIN address
- * @size: Data size in bytes
- * @axi_sec: AXI secure bit
- */
-static inline void set_din_type(struct cc_hw_desc *pdesc,
- enum cc_dma_mode dma_mode, dma_addr_t addr,
- u32 size, enum cc_axi_sec axi_sec)
-{
- pdesc->word[0] = (u32)addr;
-#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
- pdesc->word[5] |= FIELD_PREP(WORD5_DIN_ADDR_HIGH, ((u16)(addr >> 32)));
-#endif
- pdesc->word[1] |= FIELD_PREP(WORD1_DIN_DMA_MODE, dma_mode) |
- FIELD_PREP(WORD1_DIN_SIZE, size) |
- FIELD_PREP(WORD1_NS_BIT, axi_sec);
-}
-
-/*
- * Set the DIN field of a HW descriptors to NO DMA mode.
- * Used for NOP descriptor, register patches and other special modes.
- *
- * @pdesc: pointer HW descriptor struct
- * @addr: DIN address
- * @size: Data size in bytes
- */
-static inline void set_din_no_dma(struct cc_hw_desc *pdesc, u32 addr, u32 size)
-{
- pdesc->word[0] = addr;
- pdesc->word[1] |= FIELD_PREP(WORD1_DIN_SIZE, size);
-}
-
-/*
- * Set the DIN field of a HW descriptors to SRAM mode.
- * Note: No need to check SRAM alignment since host requests do not use SRAM and
- * adaptor will enforce alignment check.
- *
- * @pdesc: pointer HW descriptor struct
- * @addr: DIN address
- * @size Data size in bytes
- */
-static inline void set_din_sram(struct cc_hw_desc *pdesc, dma_addr_t addr,
- u32 size)
-{
- pdesc->word[0] = (u32)addr;
- pdesc->word[1] |= FIELD_PREP(WORD1_DIN_SIZE, size) |
- FIELD_PREP(WORD1_DIN_DMA_MODE, DMA_SRAM);
-}
-
-/*
- * Set the DIN field of a HW descriptors to CONST mode
- *
- * @pdesc: pointer HW descriptor struct
- * @val: DIN const value
- * @size: Data size in bytes
- */
-static inline void set_din_const(struct cc_hw_desc *pdesc, u32 val, u32 size)
-{
- pdesc->word[0] = val;
- pdesc->word[1] |= FIELD_PREP(WORD1_DIN_CONST_VALUE, 1) |
- FIELD_PREP(WORD1_DIN_DMA_MODE, DMA_SRAM) |
- FIELD_PREP(WORD1_DIN_SIZE, size);
-}
-
-/*
- * Set the DIN not last input data indicator
- *
- * @pdesc: pointer HW descriptor struct
- */
-static inline void set_din_not_last_indication(struct cc_hw_desc *pdesc)
-{
- pdesc->word[1] |= FIELD_PREP(WORD1_NOT_LAST, 1);
-}
-
-/*
- * Set the DOUT field of a HW descriptors
- *
- * @pdesc: pointer HW descriptor struct
- * @dma_mode: The DMA mode: NO_DMA, SRAM, DLLI, MLLI, CONSTANT
- * @addr: DOUT address
- * @size: Data size in bytes
- * @axi_sec: AXI secure bit
- */
-static inline void set_dout_type(struct cc_hw_desc *pdesc,
- enum cc_dma_mode dma_mode, dma_addr_t addr,
- u32 size, enum cc_axi_sec axi_sec)
-{
- pdesc->word[2] = (u32)addr;
-#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
- pdesc->word[5] |= FIELD_PREP(WORD5_DOUT_ADDR_HIGH, ((u16)(addr >> 32)));
-#endif
- pdesc->word[3] |= FIELD_PREP(WORD3_DOUT_DMA_MODE, dma_mode) |
- FIELD_PREP(WORD3_DOUT_SIZE, size) |
- FIELD_PREP(WORD3_NS_BIT, axi_sec);
-}
-
-/*
- * Set the DOUT field of a HW descriptors to DLLI type
- * The LAST INDICATION is provided by the user
- *
- * @pdesc pointer HW descriptor struct
- * @addr: DOUT address
- * @size: Data size in bytes
- * @last_ind: The last indication bit
- * @axi_sec: AXI secure bit
- */
-static inline void set_dout_dlli(struct cc_hw_desc *pdesc, dma_addr_t addr,
- u32 size, enum cc_axi_sec axi_sec,
- u32 last_ind)
-{
- set_dout_type(pdesc, DMA_DLLI, addr, size, axi_sec);
- pdesc->word[3] |= FIELD_PREP(WORD3_DOUT_LAST_IND, last_ind);
-}
-
-/*
- * Set the DOUT field of a HW descriptors to DLLI type
- * The LAST INDICATION is provided by the user
- *
- * @pdesc: pointer HW descriptor struct
- * @addr: DOUT address
- * @size: Data size in bytes
- * @last_ind: The last indication bit
- * @axi_sec: AXI secure bit
- */
-static inline void set_dout_mlli(struct cc_hw_desc *pdesc, dma_addr_t addr,
- u32 size, enum cc_axi_sec axi_sec,
- bool last_ind)
-{
- set_dout_type(pdesc, DMA_MLLI, addr, size, axi_sec);
- pdesc->word[3] |= FIELD_PREP(WORD3_DOUT_LAST_IND, last_ind);
-}
-
-/*
- * Set the DOUT field of a HW descriptors to NO DMA mode.
- * Used for NOP descriptor, register patches and other special modes.
- *
- * @pdesc: pointer HW descriptor struct
- * @addr: DOUT address
- * @size: Data size in bytes
- * @write_enable: Enables a write operation to a register
- */
-static inline void set_dout_no_dma(struct cc_hw_desc *pdesc, u32 addr,
- u32 size, bool write_enable)
-{
- pdesc->word[2] = addr;
- pdesc->word[3] |= FIELD_PREP(WORD3_DOUT_SIZE, size) |
- FIELD_PREP(WORD3_DOUT_LAST_IND, write_enable);
-}
-
-/*
- * Set the word for the XOR operation.
- *
- * @pdesc: pointer HW descriptor struct
- * @val: xor data value
- */
-static inline void set_xor_val(struct cc_hw_desc *pdesc, u32 val)
-{
- pdesc->word[2] = val;
-}
-
-/*
- * Sets the XOR indicator bit in the descriptor
- *
- * @pdesc: pointer HW descriptor struct
- */
-static inline void set_xor_active(struct cc_hw_desc *pdesc)
-{
- pdesc->word[3] |= FIELD_PREP(WORD3_HASH_XOR_BIT, 1);
-}
-
-/*
- * Select the AES engine instead of HASH engine when setting up combined mode
- * with AES XCBC MAC
- *
- * @pdesc: pointer HW descriptor struct
- */
-static inline void set_aes_not_hash_mode(struct cc_hw_desc *pdesc)
-{
- pdesc->word[4] |= FIELD_PREP(WORD4_AES_SEL_N_HASH, 1);
-}
-
-/*
- * Set the DOUT field of a HW descriptors to SRAM mode
- * Note: No need to check SRAM alignment since host requests do not use SRAM and
- * adaptor will enforce alignment check.
- *
- * @pdesc: pointer HW descriptor struct
- * @addr: DOUT address
- * @size: Data size in bytes
- */
-static inline void set_dout_sram(struct cc_hw_desc *pdesc, u32 addr, u32 size)
-{
- pdesc->word[2] = addr;
- pdesc->word[3] |= FIELD_PREP(WORD3_DOUT_DMA_MODE, DMA_SRAM) |
- FIELD_PREP(WORD3_DOUT_SIZE, size);
-}
-
-/*
- * Sets the data unit size for XEX mode in data_out_addr[15:0]
- *
- * @pdesc: pDesc pointer HW descriptor struct
- * @size: data unit size for XEX mode
- */
-static inline void set_xex_data_unit_size(struct cc_hw_desc *pdesc, u32 size)
-{
- pdesc->word[2] = size;
-}
-
-/*
- * Set the number of rounds for Multi2 in data_out_addr[15:0]
- *
- * @pdesc: pointer HW descriptor struct
- * @num: number of rounds for Multi2
- */
-static inline void set_multi2_num_rounds(struct cc_hw_desc *pdesc, u32 num)
-{
- pdesc->word[2] = num;
-}
-
-/*
- * Set the flow mode.
- *
- * @pdesc: pointer HW descriptor struct
- * @mode: Any one of the modes defined in [CC7x-DESC]
- */
-static inline void set_flow_mode(struct cc_hw_desc *pdesc,
- enum cc_flow_mode mode)
-{
- pdesc->word[4] |= FIELD_PREP(WORD4_DATA_FLOW_MODE, mode);
-}
-
-/*
- * Set the cipher mode.
- *
- * @pdesc: pointer HW descriptor struct
- * @mode: Any one of the modes defined in [CC7x-DESC]
- */
-static inline void set_cipher_mode(struct cc_hw_desc *pdesc,
- enum drv_cipher_mode mode)
-{
- pdesc->word[4] |= FIELD_PREP(WORD4_CIPHER_MODE, mode);
-}
-
-/*
- * Set the cipher configuration fields.
- *
- * @pdesc: pointer HW descriptor struct
- * @mode: Any one of the modes defined in [CC7x-DESC]
- */
-static inline void set_cipher_config0(struct cc_hw_desc *pdesc,
- enum drv_crypto_direction mode)
-{
- pdesc->word[4] |= FIELD_PREP(WORD4_CIPHER_CONF0, mode);
-}
-
-/*
- * Set the cipher configuration fields.
- *
- * @pdesc: pointer HW descriptor struct
- * @config: Any one of the modes defined in [CC7x-DESC]
- */
-static inline void set_cipher_config1(struct cc_hw_desc *pdesc,
- enum cc_hash_conf_pad config)
-{
- pdesc->word[4] |= FIELD_PREP(WORD4_CIPHER_CONF1, config);
-}
-
-/*
- * Set HW key configuration fields.
- *
- * @pdesc: pointer HW descriptor struct
- * @hw_key: The HW key slot asdefined in enum cc_hw_crypto_key
- */
-static inline void set_hw_crypto_key(struct cc_hw_desc *pdesc,
- enum cc_hw_crypto_key hw_key)
-{
- pdesc->word[4] |= FIELD_PREP(WORD4_CIPHER_DO,
- (hw_key & HW_KEY_MASK_CIPHER_DO)) |
- FIELD_PREP(WORD4_CIPHER_CONF2,
- (hw_key >> HW_KEY_SHIFT_CIPHER_CFG2));
-}
-
-/*
- * Set byte order of all setup-finalize descriptors.
- *
- * @pdesc: pointer HW descriptor struct
- * @config: Any one of the modes defined in [CC7x-DESC]
- */
-static inline void set_bytes_swap(struct cc_hw_desc *pdesc, bool config)
-{
- pdesc->word[4] |= FIELD_PREP(WORD4_BYTES_SWAP, config);
-}
-
-/*
- * Set CMAC_SIZE0 mode.
- *
- * @pdesc: pointer HW descriptor struct
- */
-static inline void set_cmac_size0_mode(struct cc_hw_desc *pdesc)
-{
- pdesc->word[4] |= FIELD_PREP(WORD4_CMAC_SIZE0, 1);
-}
-
-/*
- * Set key size descriptor field.
- *
- * @pdesc: pointer HW descriptor struct
- * @size: key size in bytes (NOT size code)
- */
-static inline void set_key_size(struct cc_hw_desc *pdesc, u32 size)
-{
- pdesc->word[4] |= FIELD_PREP(WORD4_KEY_SIZE, size);
-}
-
-/*
- * Set AES key size.
- *
- * @pdesc: pointer HW descriptor struct
- * @size: key size in bytes (NOT size code)
- */
-static inline void set_key_size_aes(struct cc_hw_desc *pdesc, u32 size)
-{
- set_key_size(pdesc, ((size >> 3) - 2));
-}
-
-/*
- * Set DES key size.
- *
- * @pdesc: pointer HW descriptor struct
- * @size: key size in bytes (NOT size code)
- */
-static inline void set_key_size_des(struct cc_hw_desc *pdesc, u32 size)
-{
- set_key_size(pdesc, ((size >> 3) - 1));
-}
-
-/*
- * Set the descriptor setup mode
- *
- * @pdesc: pointer HW descriptor struct
- * @mode: Any one of the setup modes defined in [CC7x-DESC]
- */
-static inline void set_setup_mode(struct cc_hw_desc *pdesc,
- enum cc_setup_op mode)
-{
- pdesc->word[4] |= FIELD_PREP(WORD4_SETUP_OPERATION, mode);
-}
-
-/*
- * Set the descriptor cipher DO
- *
- * @pdesc: pointer HW descriptor struct
- * @config: Any one of the cipher do defined in [CC7x-DESC]
- */
-static inline void set_cipher_do(struct cc_hw_desc *pdesc,
- enum cc_hash_cipher_pad config)
-{
- pdesc->word[4] |= FIELD_PREP(WORD4_CIPHER_DO,
- (config & HW_KEY_MASK_CIPHER_DO));
-}
-
-#endif /*__CC_HW_QUEUE_DEFS_H__*/
diff --git a/drivers/staging/ccree/cc_ivgen.c b/drivers/staging/ccree/cc_ivgen.c
deleted file mode 100644
index c47f419b277b..000000000000
--- a/drivers/staging/ccree/cc_ivgen.c
+++ /dev/null
@@ -1,280 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
-
-#include <crypto/ctr.h>
-#include "cc_driver.h"
-#include "cc_ivgen.h"
-#include "cc_request_mgr.h"
-#include "cc_sram_mgr.h"
-#include "cc_buffer_mgr.h"
-
-/* The max. size of pool *MUST* be <= SRAM total size */
-#define CC_IVPOOL_SIZE 1024
-/* The first 32B fraction of pool are dedicated to the
- * next encryption "key" & "IV" for pool regeneration
- */
-#define CC_IVPOOL_META_SIZE (CC_AES_IV_SIZE + AES_KEYSIZE_128)
-#define CC_IVPOOL_GEN_SEQ_LEN 4
-
-/**
- * struct cc_ivgen_ctx -IV pool generation context
- * @pool: the start address of the iv-pool resides in internal RAM
- * @ctr_key_dma: address of pool's encryption key material in internal RAM
- * @ctr_iv_dma: address of pool's counter iv in internal RAM
- * @next_iv_ofs: the offset to the next available IV in pool
- * @pool_meta: virt. address of the initial enc. key/IV
- * @pool_meta_dma: phys. address of the initial enc. key/IV
- */
-struct cc_ivgen_ctx {
- cc_sram_addr_t pool;
- cc_sram_addr_t ctr_key;
- cc_sram_addr_t ctr_iv;
- u32 next_iv_ofs;
- u8 *pool_meta;
- dma_addr_t pool_meta_dma;
-};
-
-/*!
- * Generates CC_IVPOOL_SIZE of random bytes by
- * encrypting 0's using AES128-CTR.
- *
- * \param ivgen iv-pool context
- * \param iv_seq IN/OUT array to the descriptors sequence
- * \param iv_seq_len IN/OUT pointer to the sequence length
- */
-static int cc_gen_iv_pool(struct cc_ivgen_ctx *ivgen_ctx,
- struct cc_hw_desc iv_seq[], unsigned int *iv_seq_len)
-{
- unsigned int idx = *iv_seq_len;
-
- if ((*iv_seq_len + CC_IVPOOL_GEN_SEQ_LEN) > CC_IVPOOL_SEQ_LEN) {
- /* The sequence will be longer than allowed */
- return -EINVAL;
- }
- /* Setup key */
- hw_desc_init(&iv_seq[idx]);
- set_din_sram(&iv_seq[idx], ivgen_ctx->ctr_key, AES_KEYSIZE_128);
- set_setup_mode(&iv_seq[idx], SETUP_LOAD_KEY0);
- set_cipher_config0(&iv_seq[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
- set_flow_mode(&iv_seq[idx], S_DIN_to_AES);
- set_key_size_aes(&iv_seq[idx], CC_AES_128_BIT_KEY_SIZE);
- set_cipher_mode(&iv_seq[idx], DRV_CIPHER_CTR);
- idx++;
-
- /* Setup cipher state */
- hw_desc_init(&iv_seq[idx]);
- set_din_sram(&iv_seq[idx], ivgen_ctx->ctr_iv, CC_AES_IV_SIZE);
- set_cipher_config0(&iv_seq[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
- set_flow_mode(&iv_seq[idx], S_DIN_to_AES);
- set_setup_mode(&iv_seq[idx], SETUP_LOAD_STATE1);
- set_key_size_aes(&iv_seq[idx], CC_AES_128_BIT_KEY_SIZE);
- set_cipher_mode(&iv_seq[idx], DRV_CIPHER_CTR);
- idx++;
-
- /* Perform dummy encrypt to skip first block */
- hw_desc_init(&iv_seq[idx]);
- set_din_const(&iv_seq[idx], 0, CC_AES_IV_SIZE);
- set_dout_sram(&iv_seq[idx], ivgen_ctx->pool, CC_AES_IV_SIZE);
- set_flow_mode(&iv_seq[idx], DIN_AES_DOUT);
- idx++;
-
- /* Generate IV pool */
- hw_desc_init(&iv_seq[idx]);
- set_din_const(&iv_seq[idx], 0, CC_IVPOOL_SIZE);
- set_dout_sram(&iv_seq[idx], ivgen_ctx->pool, CC_IVPOOL_SIZE);
- set_flow_mode(&iv_seq[idx], DIN_AES_DOUT);
- idx++;
-
- *iv_seq_len = idx; /* Update sequence length */
-
- /* queue ordering assures pool readiness */
- ivgen_ctx->next_iv_ofs = CC_IVPOOL_META_SIZE;
-
- return 0;
-}
-
-/*!
- * Generates the initial pool in SRAM.
- * This function should be invoked when resuming DX driver.
- *
- * \param drvdata
- *
- * \return int Zero for success, negative value otherwise.
- */
-int cc_init_iv_sram(struct cc_drvdata *drvdata)
-{
- struct cc_ivgen_ctx *ivgen_ctx = drvdata->ivgen_handle;
- struct cc_hw_desc iv_seq[CC_IVPOOL_SEQ_LEN];
- unsigned int iv_seq_len = 0;
- int rc;
-
- /* Generate initial enc. key/iv */
- get_random_bytes(ivgen_ctx->pool_meta, CC_IVPOOL_META_SIZE);
-
- /* The first 32B reserved for the enc. Key/IV */
- ivgen_ctx->ctr_key = ivgen_ctx->pool;
- ivgen_ctx->ctr_iv = ivgen_ctx->pool + AES_KEYSIZE_128;
-
- /* Copy initial enc. key and IV to SRAM at a single descriptor */
- hw_desc_init(&iv_seq[iv_seq_len]);
- set_din_type(&iv_seq[iv_seq_len], DMA_DLLI, ivgen_ctx->pool_meta_dma,
- CC_IVPOOL_META_SIZE, NS_BIT);
- set_dout_sram(&iv_seq[iv_seq_len], ivgen_ctx->pool,
- CC_IVPOOL_META_SIZE);
- set_flow_mode(&iv_seq[iv_seq_len], BYPASS);
- iv_seq_len++;
-
- /* Generate initial pool */
- rc = cc_gen_iv_pool(ivgen_ctx, iv_seq, &iv_seq_len);
- if (rc)
- return rc;
-
- /* Fire-and-forget */
- return send_request_init(drvdata, iv_seq, iv_seq_len);
-}
-
-/*!
- * Free iv-pool and ivgen context.
- *
- * \param drvdata
- */
-void cc_ivgen_fini(struct cc_drvdata *drvdata)
-{
- struct cc_ivgen_ctx *ivgen_ctx = drvdata->ivgen_handle;
- struct device *device = &drvdata->plat_dev->dev;
-
- if (!ivgen_ctx)
- return;
-
- if (ivgen_ctx->pool_meta) {
- memset(ivgen_ctx->pool_meta, 0, CC_IVPOOL_META_SIZE);
- dma_free_coherent(device, CC_IVPOOL_META_SIZE,
- ivgen_ctx->pool_meta,
- ivgen_ctx->pool_meta_dma);
- }
-
- ivgen_ctx->pool = NULL_SRAM_ADDR;
-
- /* release "this" context */
- kfree(ivgen_ctx);
-}
-
-/*!
- * Allocates iv-pool and maps resources.
- * This function generates the first IV pool.
- *
- * \param drvdata Driver's private context
- *
- * \return int Zero for success, negative value otherwise.
- */
-int cc_ivgen_init(struct cc_drvdata *drvdata)
-{
- struct cc_ivgen_ctx *ivgen_ctx;
- struct device *device = &drvdata->plat_dev->dev;
- int rc;
-
- /* Allocate "this" context */
- ivgen_ctx = kzalloc(sizeof(*ivgen_ctx), GFP_KERNEL);
- if (!ivgen_ctx)
- return -ENOMEM;
-
- drvdata->ivgen_handle = ivgen_ctx;
-
- /* Allocate pool's header for initial enc. key/IV */
- ivgen_ctx->pool_meta = dma_alloc_coherent(device, CC_IVPOOL_META_SIZE,
- &ivgen_ctx->pool_meta_dma,
- GFP_KERNEL);
- if (!ivgen_ctx->pool_meta) {
- dev_err(device, "Not enough memory to allocate DMA of pool_meta (%u B)\n",
- CC_IVPOOL_META_SIZE);
- rc = -ENOMEM;
- goto out;
- }
- /* Allocate IV pool in SRAM */
- ivgen_ctx->pool = cc_sram_alloc(drvdata, CC_IVPOOL_SIZE);
- if (ivgen_ctx->pool == NULL_SRAM_ADDR) {
- dev_err(device, "SRAM pool exhausted\n");
- rc = -ENOMEM;
- goto out;
- }
-
- return cc_init_iv_sram(drvdata);
-
-out:
- cc_ivgen_fini(drvdata);
- return rc;
-}
-
-/*!
- * Acquires 16 Bytes IV from the iv-pool
- *
- * \param drvdata Driver private context
- * \param iv_out_dma Array of physical IV out addresses
- * \param iv_out_dma_len Length of iv_out_dma array (additional elements
- * of iv_out_dma array are ignore)
- * \param iv_out_size May be 8 or 16 bytes long
- * \param iv_seq IN/OUT array to the descriptors sequence
- * \param iv_seq_len IN/OUT pointer to the sequence length
- *
- * \return int Zero for success, negative value otherwise.
- */
-int cc_get_iv(struct cc_drvdata *drvdata, dma_addr_t iv_out_dma[],
- unsigned int iv_out_dma_len, unsigned int iv_out_size,
- struct cc_hw_desc iv_seq[], unsigned int *iv_seq_len)
-{
- struct cc_ivgen_ctx *ivgen_ctx = drvdata->ivgen_handle;
- unsigned int idx = *iv_seq_len;
- struct device *dev = drvdata_to_dev(drvdata);
- unsigned int t;
-
- if (iv_out_size != CC_AES_IV_SIZE &&
- iv_out_size != CTR_RFC3686_IV_SIZE) {
- return -EINVAL;
- }
- if ((iv_out_dma_len + 1) > CC_IVPOOL_SEQ_LEN) {
- /* The sequence will be longer than allowed */
- return -EINVAL;
- }
-
- /* check that number of generated IV is limited to max dma address
- * iv buffer size
- */
- if (iv_out_dma_len > CC_MAX_IVGEN_DMA_ADDRESSES) {
- /* The sequence will be longer than allowed */
- return -EINVAL;
- }
-
- for (t = 0; t < iv_out_dma_len; t++) {
- /* Acquire IV from pool */
- hw_desc_init(&iv_seq[idx]);
- set_din_sram(&iv_seq[idx], (ivgen_ctx->pool +
- ivgen_ctx->next_iv_ofs),
- iv_out_size);
- set_dout_dlli(&iv_seq[idx], iv_out_dma[t], iv_out_size,
- NS_BIT, 0);
- set_flow_mode(&iv_seq[idx], BYPASS);
- idx++;
- }
-
- /* Bypass operation is proceeded by crypto sequence, hence must
- * assure bypass-write-transaction by a memory barrier
- */
- hw_desc_init(&iv_seq[idx]);
- set_din_no_dma(&iv_seq[idx], 0, 0xfffff0);
- set_dout_no_dma(&iv_seq[idx], 0, 0, 1);
- idx++;
-
- *iv_seq_len = idx; /* update seq length */
-
- /* Update iv index */
- ivgen_ctx->next_iv_ofs += iv_out_size;
-
- if ((CC_IVPOOL_SIZE - ivgen_ctx->next_iv_ofs) < CC_AES_IV_SIZE) {
- dev_dbg(dev, "Pool exhausted, regenerating iv-pool\n");
- /* pool is drained -regenerate it! */
- return cc_gen_iv_pool(ivgen_ctx, iv_seq, iv_seq_len);
- }
-
- return 0;
-}
-
diff --git a/drivers/staging/ccree/cc_kernel_regs.h b/drivers/staging/ccree/cc_kernel_regs.h
deleted file mode 100644
index fa994406d610..000000000000
--- a/drivers/staging/ccree/cc_kernel_regs.h
+++ /dev/null
@@ -1,167 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
-
-#ifndef __CC_CRYS_KERNEL_H__
-#define __CC_CRYS_KERNEL_H__
-
-// --------------------------------------
-// BLOCK: DSCRPTR
-// --------------------------------------
-#define CC_DSCRPTR_COMPLETION_COUNTER_REG_OFFSET 0xE00UL
-#define CC_DSCRPTR_COMPLETION_COUNTER_COMPLETION_COUNTER_BIT_SHIFT 0x0UL
-#define CC_DSCRPTR_COMPLETION_COUNTER_COMPLETION_COUNTER_BIT_SIZE 0x6UL
-#define CC_DSCRPTR_COMPLETION_COUNTER_OVERFLOW_COUNTER_BIT_SHIFT 0x6UL
-#define CC_DSCRPTR_COMPLETION_COUNTER_OVERFLOW_COUNTER_BIT_SIZE 0x1UL
-#define CC_DSCRPTR_SW_RESET_REG_OFFSET 0xE40UL
-#define CC_DSCRPTR_SW_RESET_VALUE_BIT_SHIFT 0x0UL
-#define CC_DSCRPTR_SW_RESET_VALUE_BIT_SIZE 0x1UL
-#define CC_DSCRPTR_QUEUE_SRAM_SIZE_REG_OFFSET 0xE60UL
-#define CC_DSCRPTR_QUEUE_SRAM_SIZE_NUM_OF_DSCRPTR_BIT_SHIFT 0x0UL
-#define CC_DSCRPTR_QUEUE_SRAM_SIZE_NUM_OF_DSCRPTR_BIT_SIZE 0xAUL
-#define CC_DSCRPTR_QUEUE_SRAM_SIZE_DSCRPTR_SRAM_SIZE_BIT_SHIFT 0xAUL
-#define CC_DSCRPTR_QUEUE_SRAM_SIZE_DSCRPTR_SRAM_SIZE_BIT_SIZE 0xCUL
-#define CC_DSCRPTR_QUEUE_SRAM_SIZE_SRAM_SIZE_BIT_SHIFT 0x16UL
-#define CC_DSCRPTR_QUEUE_SRAM_SIZE_SRAM_SIZE_BIT_SIZE 0x3UL
-#define CC_DSCRPTR_SINGLE_ADDR_EN_REG_OFFSET 0xE64UL
-#define CC_DSCRPTR_SINGLE_ADDR_EN_VALUE_BIT_SHIFT 0x0UL
-#define CC_DSCRPTR_SINGLE_ADDR_EN_VALUE_BIT_SIZE 0x1UL
-#define CC_DSCRPTR_MEASURE_CNTR_REG_OFFSET 0xE68UL
-#define CC_DSCRPTR_MEASURE_CNTR_VALUE_BIT_SHIFT 0x0UL
-#define CC_DSCRPTR_MEASURE_CNTR_VALUE_BIT_SIZE 0x20UL
-#define CC_DSCRPTR_QUEUE_WORD0_REG_OFFSET 0xE80UL
-#define CC_DSCRPTR_QUEUE_WORD0_VALUE_BIT_SHIFT 0x0UL
-#define CC_DSCRPTR_QUEUE_WORD0_VALUE_BIT_SIZE 0x20UL
-#define CC_DSCRPTR_QUEUE_WORD1_REG_OFFSET 0xE84UL
-#define CC_DSCRPTR_QUEUE_WORD1_DIN_DMA_MODE_BIT_SHIFT 0x0UL
-#define CC_DSCRPTR_QUEUE_WORD1_DIN_DMA_MODE_BIT_SIZE 0x2UL
-#define CC_DSCRPTR_QUEUE_WORD1_DIN_SIZE_BIT_SHIFT 0x2UL
-#define CC_DSCRPTR_QUEUE_WORD1_DIN_SIZE_BIT_SIZE 0x18UL
-#define CC_DSCRPTR_QUEUE_WORD1_NS_BIT_BIT_SHIFT 0x1AUL
-#define CC_DSCRPTR_QUEUE_WORD1_NS_BIT_BIT_SIZE 0x1UL
-#define CC_DSCRPTR_QUEUE_WORD1_DIN_CONST_VALUE_BIT_SHIFT 0x1BUL
-#define CC_DSCRPTR_QUEUE_WORD1_DIN_CONST_VALUE_BIT_SIZE 0x1UL
-#define CC_DSCRPTR_QUEUE_WORD1_NOT_LAST_BIT_SHIFT 0x1CUL
-#define CC_DSCRPTR_QUEUE_WORD1_NOT_LAST_BIT_SIZE 0x1UL
-#define CC_DSCRPTR_QUEUE_WORD1_LOCK_QUEUE_BIT_SHIFT 0x1DUL
-#define CC_DSCRPTR_QUEUE_WORD1_LOCK_QUEUE_BIT_SIZE 0x1UL
-#define CC_DSCRPTR_QUEUE_WORD1_NOT_USED_BIT_SHIFT 0x1EUL
-#define CC_DSCRPTR_QUEUE_WORD1_NOT_USED_BIT_SIZE 0x2UL
-#define CC_DSCRPTR_QUEUE_WORD2_REG_OFFSET 0xE88UL
-#define CC_DSCRPTR_QUEUE_WORD2_VALUE_BIT_SHIFT 0x0UL
-#define CC_DSCRPTR_QUEUE_WORD2_VALUE_BIT_SIZE 0x20UL
-#define CC_DSCRPTR_QUEUE_WORD3_REG_OFFSET 0xE8CUL
-#define CC_DSCRPTR_QUEUE_WORD3_DOUT_DMA_MODE_BIT_SHIFT 0x0UL
-#define CC_DSCRPTR_QUEUE_WORD3_DOUT_DMA_MODE_BIT_SIZE 0x2UL
-#define CC_DSCRPTR_QUEUE_WORD3_DOUT_SIZE_BIT_SHIFT 0x2UL
-#define CC_DSCRPTR_QUEUE_WORD3_DOUT_SIZE_BIT_SIZE 0x18UL
-#define CC_DSCRPTR_QUEUE_WORD3_NS_BIT_BIT_SHIFT 0x1AUL
-#define CC_DSCRPTR_QUEUE_WORD3_NS_BIT_BIT_SIZE 0x1UL
-#define CC_DSCRPTR_QUEUE_WORD3_DOUT_LAST_IND_BIT_SHIFT 0x1BUL
-#define CC_DSCRPTR_QUEUE_WORD3_DOUT_LAST_IND_BIT_SIZE 0x1UL
-#define CC_DSCRPTR_QUEUE_WORD3_HASH_XOR_BIT_BIT_SHIFT 0x1DUL
-#define CC_DSCRPTR_QUEUE_WORD3_HASH_XOR_BIT_BIT_SIZE 0x1UL
-#define CC_DSCRPTR_QUEUE_WORD3_NOT_USED_BIT_SHIFT 0x1EUL
-#define CC_DSCRPTR_QUEUE_WORD3_NOT_USED_BIT_SIZE 0x1UL
-#define CC_DSCRPTR_QUEUE_WORD3_QUEUE_LAST_IND_BIT_SHIFT 0x1FUL
-#define CC_DSCRPTR_QUEUE_WORD3_QUEUE_LAST_IND_BIT_SIZE 0x1UL
-#define CC_DSCRPTR_QUEUE_WORD4_REG_OFFSET 0xE90UL
-#define CC_DSCRPTR_QUEUE_WORD4_DATA_FLOW_MODE_BIT_SHIFT 0x0UL
-#define CC_DSCRPTR_QUEUE_WORD4_DATA_FLOW_MODE_BIT_SIZE 0x6UL
-#define CC_DSCRPTR_QUEUE_WORD4_AES_SEL_N_HASH_BIT_SHIFT 0x6UL
-#define CC_DSCRPTR_QUEUE_WORD4_AES_SEL_N_HASH_BIT_SIZE 0x1UL
-#define CC_DSCRPTR_QUEUE_WORD4_AES_XOR_CRYPTO_KEY_BIT_SHIFT 0x7UL
-#define CC_DSCRPTR_QUEUE_WORD4_AES_XOR_CRYPTO_KEY_BIT_SIZE 0x1UL
-#define CC_DSCRPTR_QUEUE_WORD4_ACK_NEEDED_BIT_SHIFT 0x8UL
-#define CC_DSCRPTR_QUEUE_WORD4_ACK_NEEDED_BIT_SIZE 0x2UL
-#define CC_DSCRPTR_QUEUE_WORD4_CIPHER_MODE_BIT_SHIFT 0xAUL
-#define CC_DSCRPTR_QUEUE_WORD4_CIPHER_MODE_BIT_SIZE 0x4UL
-#define CC_DSCRPTR_QUEUE_WORD4_CMAC_SIZE0_BIT_SHIFT 0xEUL
-#define CC_DSCRPTR_QUEUE_WORD4_CMAC_SIZE0_BIT_SIZE 0x1UL
-#define CC_DSCRPTR_QUEUE_WORD4_CIPHER_DO_BIT_SHIFT 0xFUL
-#define CC_DSCRPTR_QUEUE_WORD4_CIPHER_DO_BIT_SIZE 0x2UL
-#define CC_DSCRPTR_QUEUE_WORD4_CIPHER_CONF0_BIT_SHIFT 0x11UL
-#define CC_DSCRPTR_QUEUE_WORD4_CIPHER_CONF0_BIT_SIZE 0x2UL
-#define CC_DSCRPTR_QUEUE_WORD4_CIPHER_CONF1_BIT_SHIFT 0x13UL
-#define CC_DSCRPTR_QUEUE_WORD4_CIPHER_CONF1_BIT_SIZE 0x1UL
-#define CC_DSCRPTR_QUEUE_WORD4_CIPHER_CONF2_BIT_SHIFT 0x14UL
-#define CC_DSCRPTR_QUEUE_WORD4_CIPHER_CONF2_BIT_SIZE 0x2UL
-#define CC_DSCRPTR_QUEUE_WORD4_KEY_SIZE_BIT_SHIFT 0x16UL
-#define CC_DSCRPTR_QUEUE_WORD4_KEY_SIZE_BIT_SIZE 0x2UL
-#define CC_DSCRPTR_QUEUE_WORD4_SETUP_OPERATION_BIT_SHIFT 0x18UL
-#define CC_DSCRPTR_QUEUE_WORD4_SETUP_OPERATION_BIT_SIZE 0x4UL
-#define CC_DSCRPTR_QUEUE_WORD4_DIN_SRAM_ENDIANNESS_BIT_SHIFT 0x1CUL
-#define CC_DSCRPTR_QUEUE_WORD4_DIN_SRAM_ENDIANNESS_BIT_SIZE 0x1UL
-#define CC_DSCRPTR_QUEUE_WORD4_DOUT_SRAM_ENDIANNESS_BIT_SHIFT 0x1DUL
-#define CC_DSCRPTR_QUEUE_WORD4_DOUT_SRAM_ENDIANNESS_BIT_SIZE 0x1UL
-#define CC_DSCRPTR_QUEUE_WORD4_WORD_SWAP_BIT_SHIFT 0x1EUL
-#define CC_DSCRPTR_QUEUE_WORD4_WORD_SWAP_BIT_SIZE 0x1UL
-#define CC_DSCRPTR_QUEUE_WORD4_BYTES_SWAP_BIT_SHIFT 0x1FUL
-#define CC_DSCRPTR_QUEUE_WORD4_BYTES_SWAP_BIT_SIZE 0x1UL
-#define CC_DSCRPTR_QUEUE_WORD5_REG_OFFSET 0xE94UL
-#define CC_DSCRPTR_QUEUE_WORD5_DIN_ADDR_HIGH_BIT_SHIFT 0x0UL
-#define CC_DSCRPTR_QUEUE_WORD5_DIN_ADDR_HIGH_BIT_SIZE 0x10UL
-#define CC_DSCRPTR_QUEUE_WORD5_DOUT_ADDR_HIGH_BIT_SHIFT 0x10UL
-#define CC_DSCRPTR_QUEUE_WORD5_DOUT_ADDR_HIGH_BIT_SIZE 0x10UL
-#define CC_DSCRPTR_QUEUE_WATERMARK_REG_OFFSET 0xE98UL
-#define CC_DSCRPTR_QUEUE_WATERMARK_VALUE_BIT_SHIFT 0x0UL
-#define CC_DSCRPTR_QUEUE_WATERMARK_VALUE_BIT_SIZE 0xAUL
-#define CC_DSCRPTR_QUEUE_CONTENT_REG_OFFSET 0xE9CUL
-#define CC_DSCRPTR_QUEUE_CONTENT_VALUE_BIT_SHIFT 0x0UL
-#define CC_DSCRPTR_QUEUE_CONTENT_VALUE_BIT_SIZE 0xAUL
-// --------------------------------------
-// BLOCK: AXI_P
-// --------------------------------------
-#define CC_AXIM_MON_INFLIGHT_REG_OFFSET 0xB00UL
-#define CC_AXIM_MON_INFLIGHT_VALUE_BIT_SHIFT 0x0UL
-#define CC_AXIM_MON_INFLIGHT_VALUE_BIT_SIZE 0x8UL
-#define CC_AXIM_MON_INFLIGHTLAST_REG_OFFSET 0xB40UL
-#define CC_AXIM_MON_INFLIGHTLAST_VALUE_BIT_SHIFT 0x0UL
-#define CC_AXIM_MON_INFLIGHTLAST_VALUE_BIT_SIZE 0x8UL
-#define CC_AXIM_MON_COMP_REG_OFFSET 0xB80UL
-#define CC_AXIM_MON_COMP_VALUE_BIT_SHIFT 0x0UL
-#define CC_AXIM_MON_COMP_VALUE_BIT_SIZE 0x10UL
-#define CC_AXIM_MON_ERR_REG_OFFSET 0xBC4UL
-#define CC_AXIM_MON_ERR_BRESP_BIT_SHIFT 0x0UL
-#define CC_AXIM_MON_ERR_BRESP_BIT_SIZE 0x2UL
-#define CC_AXIM_MON_ERR_BID_BIT_SHIFT 0x2UL
-#define CC_AXIM_MON_ERR_BID_BIT_SIZE 0x4UL
-#define CC_AXIM_MON_ERR_RRESP_BIT_SHIFT 0x10UL
-#define CC_AXIM_MON_ERR_RRESP_BIT_SIZE 0x2UL
-#define CC_AXIM_MON_ERR_RID_BIT_SHIFT 0x12UL
-#define CC_AXIM_MON_ERR_RID_BIT_SIZE 0x4UL
-#define CC_AXIM_CFG_REG_OFFSET 0xBE8UL
-#define CC_AXIM_CFG_BRESPMASK_BIT_SHIFT 0x4UL
-#define CC_AXIM_CFG_BRESPMASK_BIT_SIZE 0x1UL
-#define CC_AXIM_CFG_RRESPMASK_BIT_SHIFT 0x5UL
-#define CC_AXIM_CFG_RRESPMASK_BIT_SIZE 0x1UL
-#define CC_AXIM_CFG_INFLTMASK_BIT_SHIFT 0x6UL
-#define CC_AXIM_CFG_INFLTMASK_BIT_SIZE 0x1UL
-#define CC_AXIM_CFG_COMPMASK_BIT_SHIFT 0x7UL
-#define CC_AXIM_CFG_COMPMASK_BIT_SIZE 0x1UL
-#define CC_AXIM_ACE_CONST_REG_OFFSET 0xBECUL
-#define CC_AXIM_ACE_CONST_ARDOMAIN_BIT_SHIFT 0x0UL
-#define CC_AXIM_ACE_CONST_ARDOMAIN_BIT_SIZE 0x2UL
-#define CC_AXIM_ACE_CONST_AWDOMAIN_BIT_SHIFT 0x2UL
-#define CC_AXIM_ACE_CONST_AWDOMAIN_BIT_SIZE 0x2UL
-#define CC_AXIM_ACE_CONST_ARBAR_BIT_SHIFT 0x4UL
-#define CC_AXIM_ACE_CONST_ARBAR_BIT_SIZE 0x2UL
-#define CC_AXIM_ACE_CONST_AWBAR_BIT_SHIFT 0x6UL
-#define CC_AXIM_ACE_CONST_AWBAR_BIT_SIZE 0x2UL
-#define CC_AXIM_ACE_CONST_ARSNOOP_BIT_SHIFT 0x8UL
-#define CC_AXIM_ACE_CONST_ARSNOOP_BIT_SIZE 0x4UL
-#define CC_AXIM_ACE_CONST_AWSNOOP_NOT_ALIGNED_BIT_SHIFT 0xCUL
-#define CC_AXIM_ACE_CONST_AWSNOOP_NOT_ALIGNED_BIT_SIZE 0x3UL
-#define CC_AXIM_ACE_CONST_AWSNOOP_ALIGNED_BIT_SHIFT 0xFUL
-#define CC_AXIM_ACE_CONST_AWSNOOP_ALIGNED_BIT_SIZE 0x3UL
-#define CC_AXIM_ACE_CONST_AWADDR_NOT_MASKED_BIT_SHIFT 0x12UL
-#define CC_AXIM_ACE_CONST_AWADDR_NOT_MASKED_BIT_SIZE 0x7UL
-#define CC_AXIM_ACE_CONST_AWLEN_VAL_BIT_SHIFT 0x19UL
-#define CC_AXIM_ACE_CONST_AWLEN_VAL_BIT_SIZE 0x4UL
-#define CC_AXIM_CACHE_PARAMS_REG_OFFSET 0xBF0UL
-#define CC_AXIM_CACHE_PARAMS_AWCACHE_LAST_BIT_SHIFT 0x0UL
-#define CC_AXIM_CACHE_PARAMS_AWCACHE_LAST_BIT_SIZE 0x4UL
-#define CC_AXIM_CACHE_PARAMS_AWCACHE_BIT_SHIFT 0x4UL
-#define CC_AXIM_CACHE_PARAMS_AWCACHE_BIT_SIZE 0x4UL
-#define CC_AXIM_CACHE_PARAMS_ARCACHE_BIT_SHIFT 0x8UL
-#define CC_AXIM_CACHE_PARAMS_ARCACHE_BIT_SIZE 0x4UL
-#endif // __CC_CRYS_KERNEL_H__
diff --git a/drivers/staging/ccree/cc_pm.h b/drivers/staging/ccree/cc_pm.h
deleted file mode 100644
index aac8190fea38..000000000000
--- a/drivers/staging/ccree/cc_pm.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
-
-/* \file cc_pm.h
- */
-
-#ifndef __CC_POWER_MGR_H__
-#define __CC_POWER_MGR_H__
-
-#include "cc_driver.h"
-
-#define CC_SUSPEND_TIMEOUT 3000
-
-#if defined(CONFIG_PM)
-
-extern const struct dev_pm_ops ccree_pm;
-
-int cc_pm_init(struct cc_drvdata *drvdata);
-void cc_pm_fini(struct cc_drvdata *drvdata);
-int cc_pm_suspend(struct device *dev);
-int cc_pm_resume(struct device *dev);
-int cc_pm_get(struct device *dev);
-int cc_pm_put_suspend(struct device *dev);
-
-#else
-
-static inline int cc_pm_init(struct cc_drvdata *drvdata)
-{
- return 0;
-}
-
-static inline void cc_pm_fini(struct cc_drvdata *drvdata) {}
-
-static inline int cc_pm_suspend(struct device *dev)
-{
- return 0;
-}
-
-static inline int cc_pm_resume(struct device *dev)
-{
- return 0;
-}
-
-static inline int cc_pm_get(struct device *dev)
-{
- return 0;
-}
-
-static inline int cc_pm_put_suspend(struct device *dev)
-{
- return 0;
-}
-
-#endif
-
-#endif /*__POWER_MGR_H__*/
-
diff --git a/drivers/staging/ccree/cc_request_mgr.c b/drivers/staging/ccree/cc_request_mgr.c
deleted file mode 100644
index 8a7f83407410..000000000000
--- a/drivers/staging/ccree/cc_request_mgr.c
+++ /dev/null
@@ -1,713 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
-
-#include <linux/kernel.h>
-#include "cc_driver.h"
-#include "cc_buffer_mgr.h"
-#include "cc_request_mgr.h"
-#include "cc_ivgen.h"
-#include "cc_pm.h"
-
-#define CC_MAX_POLL_ITER 10
-/* The highest descriptor count in used */
-#define CC_MAX_DESC_SEQ_LEN 23
-
-struct cc_req_mgr_handle {
- /* Request manager resources */
- unsigned int hw_queue_size; /* HW capability */
- unsigned int min_free_hw_slots;
- unsigned int max_used_sw_slots;
- struct cc_crypto_req req_queue[MAX_REQUEST_QUEUE_SIZE];
- u32 req_queue_head;
- u32 req_queue_tail;
- u32 axi_completed;
- u32 q_free_slots;
- /* This lock protects access to HW register
- * that must be single request at a time
- */
- spinlock_t hw_lock;
- struct cc_hw_desc compl_desc;
- u8 *dummy_comp_buff;
- dma_addr_t dummy_comp_buff_dma;
-
- /* backlog queue */
- struct list_head backlog;
- unsigned int bl_len;
- spinlock_t bl_lock; /* protect backlog queue */
-
-#ifdef COMP_IN_WQ
- struct workqueue_struct *workq;
- struct delayed_work compwork;
-#else
- struct tasklet_struct comptask;
-#endif
- bool is_runtime_suspended;
-};
-
-struct cc_bl_item {
- struct cc_crypto_req creq;
- struct cc_hw_desc desc[CC_MAX_DESC_SEQ_LEN];
- unsigned int len;
- struct list_head list;
- bool notif;
-};
-
-static void comp_handler(unsigned long devarg);
-#ifdef COMP_IN_WQ
-static void comp_work_handler(struct work_struct *work);
-#endif
-
-void cc_req_mgr_fini(struct cc_drvdata *drvdata)
-{
- struct cc_req_mgr_handle *req_mgr_h = drvdata->request_mgr_handle;
- struct device *dev = drvdata_to_dev(drvdata);
-
- if (!req_mgr_h)
- return; /* Not allocated */
-
- if (req_mgr_h->dummy_comp_buff_dma) {
- dma_free_coherent(dev, sizeof(u32), req_mgr_h->dummy_comp_buff,
- req_mgr_h->dummy_comp_buff_dma);
- }
-
- dev_dbg(dev, "max_used_hw_slots=%d\n", (req_mgr_h->hw_queue_size -
- req_mgr_h->min_free_hw_slots));
- dev_dbg(dev, "max_used_sw_slots=%d\n", req_mgr_h->max_used_sw_slots);
-
-#ifdef COMP_IN_WQ
- flush_workqueue(req_mgr_h->workq);
- destroy_workqueue(req_mgr_h->workq);
-#else
- /* Kill tasklet */
- tasklet_kill(&req_mgr_h->comptask);
-#endif
- memset(req_mgr_h, 0, sizeof(struct cc_req_mgr_handle));
- kfree(req_mgr_h);
- drvdata->request_mgr_handle = NULL;
-}
-
-int cc_req_mgr_init(struct cc_drvdata *drvdata)
-{
- struct cc_req_mgr_handle *req_mgr_h;
- struct device *dev = drvdata_to_dev(drvdata);
- int rc = 0;
-
- req_mgr_h = kzalloc(sizeof(*req_mgr_h), GFP_KERNEL);
- if (!req_mgr_h) {
- rc = -ENOMEM;
- goto req_mgr_init_err;
- }
-
- drvdata->request_mgr_handle = req_mgr_h;
-
- spin_lock_init(&req_mgr_h->hw_lock);
- spin_lock_init(&req_mgr_h->bl_lock);
- INIT_LIST_HEAD(&req_mgr_h->backlog);
-
-#ifdef COMP_IN_WQ
- dev_dbg(dev, "Initializing completion workqueue\n");
- req_mgr_h->workq = create_singlethread_workqueue("arm_cc7x_wq");
- if (!req_mgr_h->workq) {
- dev_err(dev, "Failed creating work queue\n");
- rc = -ENOMEM;
- goto req_mgr_init_err;
- }
- INIT_DELAYED_WORK(&req_mgr_h->compwork, comp_work_handler);
-#else
- dev_dbg(dev, "Initializing completion tasklet\n");
- tasklet_init(&req_mgr_h->comptask, comp_handler,
- (unsigned long)drvdata);
-#endif
- req_mgr_h->hw_queue_size = cc_ioread(drvdata,
- CC_REG(DSCRPTR_QUEUE_SRAM_SIZE));
- dev_dbg(dev, "hw_queue_size=0x%08X\n", req_mgr_h->hw_queue_size);
- if (req_mgr_h->hw_queue_size < MIN_HW_QUEUE_SIZE) {
- dev_err(dev, "Invalid HW queue size = %u (Min. required is %u)\n",
- req_mgr_h->hw_queue_size, MIN_HW_QUEUE_SIZE);
- rc = -ENOMEM;
- goto req_mgr_init_err;
- }
- req_mgr_h->min_free_hw_slots = req_mgr_h->hw_queue_size;
- req_mgr_h->max_used_sw_slots = 0;
-
- /* Allocate DMA word for "dummy" completion descriptor use */
- req_mgr_h->dummy_comp_buff =
- dma_alloc_coherent(dev, sizeof(u32),
- &req_mgr_h->dummy_comp_buff_dma,
- GFP_KERNEL);
- if (!req_mgr_h->dummy_comp_buff) {
- dev_err(dev, "Not enough memory to allocate DMA (%zu) dropped buffer\n",
- sizeof(u32));
- rc = -ENOMEM;
- goto req_mgr_init_err;
- }
-
- /* Init. "dummy" completion descriptor */
- hw_desc_init(&req_mgr_h->compl_desc);
- set_din_const(&req_mgr_h->compl_desc, 0, sizeof(u32));
- set_dout_dlli(&req_mgr_h->compl_desc, req_mgr_h->dummy_comp_buff_dma,
- sizeof(u32), NS_BIT, 1);
- set_flow_mode(&req_mgr_h->compl_desc, BYPASS);
- set_queue_last_ind(&req_mgr_h->compl_desc);
-
- return 0;
-
-req_mgr_init_err:
- cc_req_mgr_fini(drvdata);
- return rc;
-}
-
-static void enqueue_seq(struct cc_drvdata *drvdata, struct cc_hw_desc seq[],
- unsigned int seq_len)
-{
- int i, w;
- void __iomem *reg = drvdata->cc_base + CC_REG(DSCRPTR_QUEUE_WORD0);
- struct device *dev = drvdata_to_dev(drvdata);
-
- /*
- * We do indeed write all 6 command words to the same
- * register. The HW supports this.
- */
-
- for (i = 0; i < seq_len; i++) {
- for (w = 0; w <= 5; w++)
- writel_relaxed(seq[i].word[w], reg);
-
- if (cc_dump_desc)
- dev_dbg(dev, "desc[%02d]: 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X\n",
- i, seq[i].word[0], seq[i].word[1],
- seq[i].word[2], seq[i].word[3],
- seq[i].word[4], seq[i].word[5]);
- }
-}
-
-/*!
- * Completion will take place if and only if user requested completion
- * by cc_send_sync_request().
- *
- * \param dev
- * \param dx_compl_h The completion event to signal
- */
-static void request_mgr_complete(struct device *dev, void *dx_compl_h,
- int dummy)
-{
- struct completion *this_compl = dx_compl_h;
-
- complete(this_compl);
-}
-
-static int cc_queues_status(struct cc_drvdata *drvdata,
- struct cc_req_mgr_handle *req_mgr_h,
- unsigned int total_seq_len)
-{
- unsigned long poll_queue;
- struct device *dev = drvdata_to_dev(drvdata);
-
- /* SW queue is checked only once as it will not
- * be chaned during the poll because the spinlock_bh
- * is held by the thread
- */
- if (((req_mgr_h->req_queue_head + 1) & (MAX_REQUEST_QUEUE_SIZE - 1)) ==
- req_mgr_h->req_queue_tail) {
- dev_err(dev, "SW FIFO is full. req_queue_head=%d sw_fifo_len=%d\n",
- req_mgr_h->req_queue_head, MAX_REQUEST_QUEUE_SIZE);
- return -ENOSPC;
- }
-
- if (req_mgr_h->q_free_slots >= total_seq_len)
- return 0;
-
- /* Wait for space in HW queue. Poll constant num of iterations. */
- for (poll_queue = 0; poll_queue < CC_MAX_POLL_ITER ; poll_queue++) {
- req_mgr_h->q_free_slots =
- cc_ioread(drvdata, CC_REG(DSCRPTR_QUEUE_CONTENT));
- if (req_mgr_h->q_free_slots < req_mgr_h->min_free_hw_slots)
- req_mgr_h->min_free_hw_slots = req_mgr_h->q_free_slots;
-
- if (req_mgr_h->q_free_slots >= total_seq_len) {
- /* If there is enough place return */
- return 0;
- }
-
- dev_dbg(dev, "HW FIFO is full. q_free_slots=%d total_seq_len=%d\n",
- req_mgr_h->q_free_slots, total_seq_len);
- }
- /* No room in the HW queue try again later */
- dev_dbg(dev, "HW FIFO full, timeout. req_queue_head=%d sw_fifo_len=%d q_free_slots=%d total_seq_len=%d\n",
- req_mgr_h->req_queue_head, MAX_REQUEST_QUEUE_SIZE,
- req_mgr_h->q_free_slots, total_seq_len);
- return -ENOSPC;
-}
-
-/*!
- * Enqueue caller request to crypto hardware.
- * Need to be called with HW lock held and PM running
- *
- * \param drvdata
- * \param cc_req The request to enqueue
- * \param desc The crypto sequence
- * \param len The crypto sequence length
- * \param add_comp If "true": add an artificial dout DMA to mark completion
- *
- * \return int Returns -EINPROGRESS or error code
- */
-static int cc_do_send_request(struct cc_drvdata *drvdata,
- struct cc_crypto_req *cc_req,
- struct cc_hw_desc *desc, unsigned int len,
- bool add_comp, bool ivgen)
-{
- struct cc_req_mgr_handle *req_mgr_h = drvdata->request_mgr_handle;
- unsigned int used_sw_slots;
- unsigned int iv_seq_len = 0;
- unsigned int total_seq_len = len; /*initial sequence length*/
- struct cc_hw_desc iv_seq[CC_IVPOOL_SEQ_LEN];
- struct device *dev = drvdata_to_dev(drvdata);
- int rc;
-
- if (ivgen) {
- dev_dbg(dev, "Acquire IV from pool into %d DMA addresses %pad, %pad, %pad, IV-size=%u\n",
- cc_req->ivgen_dma_addr_len,
- &cc_req->ivgen_dma_addr[0],
- &cc_req->ivgen_dma_addr[1],
- &cc_req->ivgen_dma_addr[2],
- cc_req->ivgen_size);
-
- /* Acquire IV from pool */
- rc = cc_get_iv(drvdata, cc_req->ivgen_dma_addr,
- cc_req->ivgen_dma_addr_len,
- cc_req->ivgen_size, iv_seq, &iv_seq_len);
-
- if (rc) {
- dev_err(dev, "Failed to generate IV (rc=%d)\n", rc);
- return rc;
- }
-
- total_seq_len += iv_seq_len;
- }
-
- used_sw_slots = ((req_mgr_h->req_queue_head -
- req_mgr_h->req_queue_tail) &
- (MAX_REQUEST_QUEUE_SIZE - 1));
- if (used_sw_slots > req_mgr_h->max_used_sw_slots)
- req_mgr_h->max_used_sw_slots = used_sw_slots;
-
- /* Enqueue request - must be locked with HW lock*/
- req_mgr_h->req_queue[req_mgr_h->req_queue_head] = *cc_req;
- req_mgr_h->req_queue_head = (req_mgr_h->req_queue_head + 1) &
- (MAX_REQUEST_QUEUE_SIZE - 1);
- /* TODO: Use circ_buf.h ? */
-
- dev_dbg(dev, "Enqueue request head=%u\n", req_mgr_h->req_queue_head);
-
- /*
- * We are about to push command to the HW via the command registers
- * that may refernece hsot memory. We need to issue a memory barrier
- * to make sure there are no outstnading memory writes
- */
- wmb();
-
- /* STAT_PHASE_4: Push sequence */
- if (ivgen)
- enqueue_seq(drvdata, iv_seq, iv_seq_len);
-
- enqueue_seq(drvdata, desc, len);
-
- if (add_comp) {
- enqueue_seq(drvdata, &req_mgr_h->compl_desc, 1);
- total_seq_len++;
- }
-
- if (req_mgr_h->q_free_slots < total_seq_len) {
- /* This situation should never occur. Maybe indicating problem
- * with resuming power. Set the free slot count to 0 and hope
- * for the best.
- */
- dev_err(dev, "HW free slot count mismatch.");
- req_mgr_h->q_free_slots = 0;
- } else {
- /* Update the free slots in HW queue */
- req_mgr_h->q_free_slots -= total_seq_len;
- }
-
- /* Operation still in process */
- return -EINPROGRESS;
-}
-
-static void cc_enqueue_backlog(struct cc_drvdata *drvdata,
- struct cc_bl_item *bli)
-{
- struct cc_req_mgr_handle *mgr = drvdata->request_mgr_handle;
-
- spin_lock_bh(&mgr->bl_lock);
- list_add_tail(&bli->list, &mgr->backlog);
- ++mgr->bl_len;
- spin_unlock_bh(&mgr->bl_lock);
- tasklet_schedule(&mgr->comptask);
-}
-
-static void cc_proc_backlog(struct cc_drvdata *drvdata)
-{
- struct cc_req_mgr_handle *mgr = drvdata->request_mgr_handle;
- struct cc_bl_item *bli;
- struct cc_crypto_req *creq;
- struct crypto_async_request *req;
- bool ivgen;
- unsigned int total_len;
- struct device *dev = drvdata_to_dev(drvdata);
- int rc;
-
- spin_lock(&mgr->bl_lock);
-
- while (mgr->bl_len) {
- bli = list_first_entry(&mgr->backlog, struct cc_bl_item, list);
- spin_unlock(&mgr->bl_lock);
-
- creq = &bli->creq;
- req = (struct crypto_async_request *)creq->user_arg;
-
- /*
- * Notify the request we're moving out of the backlog
- * but only if we haven't done so already.
- */
- if (!bli->notif) {
- req->complete(req, -EINPROGRESS);
- bli->notif = true;
- }
-
- ivgen = !!creq->ivgen_dma_addr_len;
- total_len = bli->len + (ivgen ? CC_IVPOOL_SEQ_LEN : 0);
-
- spin_lock(&mgr->hw_lock);
-
- rc = cc_queues_status(drvdata, mgr, total_len);
- if (rc) {
- /*
- * There is still not room in the FIFO for
- * this request. Bail out. We'll return here
- * on the next completion irq.
- */
- spin_unlock(&mgr->hw_lock);
- return;
- }
-
- rc = cc_do_send_request(drvdata, &bli->creq, bli->desc,
- bli->len, false, ivgen);
-
- spin_unlock(&mgr->hw_lock);
-
- if (rc != -EINPROGRESS) {
- cc_pm_put_suspend(dev);
- creq->user_cb(dev, req, rc);
- }
-
- /* Remove ourselves from the backlog list */
- spin_lock(&mgr->bl_lock);
- list_del(&bli->list);
- --mgr->bl_len;
- }
-
- spin_unlock(&mgr->bl_lock);
-}
-
-int cc_send_request(struct cc_drvdata *drvdata, struct cc_crypto_req *cc_req,
- struct cc_hw_desc *desc, unsigned int len,
- struct crypto_async_request *req)
-{
- int rc;
- struct cc_req_mgr_handle *mgr = drvdata->request_mgr_handle;
- bool ivgen = !!cc_req->ivgen_dma_addr_len;
- unsigned int total_len = len + (ivgen ? CC_IVPOOL_SEQ_LEN : 0);
- struct device *dev = drvdata_to_dev(drvdata);
- bool backlog_ok = req->flags & CRYPTO_TFM_REQ_MAY_BACKLOG;
- gfp_t flags = cc_gfp_flags(req);
- struct cc_bl_item *bli;
-
- rc = cc_pm_get(dev);
- if (rc) {
- dev_err(dev, "ssi_power_mgr_runtime_get returned %x\n", rc);
- return rc;
- }
-
- spin_lock_bh(&mgr->hw_lock);
- rc = cc_queues_status(drvdata, mgr, total_len);
-
-#ifdef CC_DEBUG_FORCE_BACKLOG
- if (backlog_ok)
- rc = -ENOSPC;
-#endif /* CC_DEBUG_FORCE_BACKLOG */
-
- if (rc == -ENOSPC && backlog_ok) {
- spin_unlock_bh(&mgr->hw_lock);
-
- bli = kmalloc(sizeof(*bli), flags);
- if (!bli) {
- cc_pm_put_suspend(dev);
- return -ENOMEM;
- }
-
- memcpy(&bli->creq, cc_req, sizeof(*cc_req));
- memcpy(&bli->desc, desc, len * sizeof(*desc));
- bli->len = len;
- bli->notif = false;
- cc_enqueue_backlog(drvdata, bli);
- return -EBUSY;
- }
-
- if (!rc)
- rc = cc_do_send_request(drvdata, cc_req, desc, len, false,
- ivgen);
-
- spin_unlock_bh(&mgr->hw_lock);
- return rc;
-}
-
-int cc_send_sync_request(struct cc_drvdata *drvdata,
- struct cc_crypto_req *cc_req, struct cc_hw_desc *desc,
- unsigned int len)
-{
- int rc;
- struct device *dev = drvdata_to_dev(drvdata);
- struct cc_req_mgr_handle *mgr = drvdata->request_mgr_handle;
-
- init_completion(&cc_req->seq_compl);
- cc_req->user_cb = request_mgr_complete;
- cc_req->user_arg = &cc_req->seq_compl;
-
- rc = cc_pm_get(dev);
- if (rc) {
- dev_err(dev, "ssi_power_mgr_runtime_get returned %x\n", rc);
- return rc;
- }
-
- while (true) {
- spin_lock_bh(&mgr->hw_lock);
- rc = cc_queues_status(drvdata, mgr, len + 1);
-
- if (!rc)
- break;
-
- spin_unlock_bh(&mgr->hw_lock);
- if (rc != -EAGAIN) {
- cc_pm_put_suspend(dev);
- return rc;
- }
- wait_for_completion_interruptible(&drvdata->hw_queue_avail);
- reinit_completion(&drvdata->hw_queue_avail);
- }
-
- rc = cc_do_send_request(drvdata, cc_req, desc, len, true, false);
- spin_unlock_bh(&mgr->hw_lock);
-
- if (rc != -EINPROGRESS) {
- cc_pm_put_suspend(dev);
- return rc;
- }
-
- wait_for_completion(&cc_req->seq_compl);
- return 0;
-}
-
-/*!
- * Enqueue caller request to crypto hardware during init process.
- * assume this function is not called in middle of a flow,
- * since we set QUEUE_LAST_IND flag in the last descriptor.
- *
- * \param drvdata
- * \param desc The crypto sequence
- * \param len The crypto sequence length
- *
- * \return int Returns "0" upon success
- */
-int send_request_init(struct cc_drvdata *drvdata, struct cc_hw_desc *desc,
- unsigned int len)
-{
- struct cc_req_mgr_handle *req_mgr_h = drvdata->request_mgr_handle;
- unsigned int total_seq_len = len; /*initial sequence length*/
- int rc = 0;
-
- /* Wait for space in HW and SW FIFO. Poll for as much as FIFO_TIMEOUT.
- */
- rc = cc_queues_status(drvdata, req_mgr_h, total_seq_len);
- if (rc)
- return rc;
-
- set_queue_last_ind(&desc[(len - 1)]);
-
- /*
- * We are about to push command to the HW via the command registers
- * that may refernece hsot memory. We need to issue a memory barrier
- * to make sure there are no outstnading memory writes
- */
- wmb();
- enqueue_seq(drvdata, desc, len);
-
- /* Update the free slots in HW queue */
- req_mgr_h->q_free_slots =
- cc_ioread(drvdata, CC_REG(DSCRPTR_QUEUE_CONTENT));
-
- return 0;
-}
-
-void complete_request(struct cc_drvdata *drvdata)
-{
- struct cc_req_mgr_handle *request_mgr_handle =
- drvdata->request_mgr_handle;
-
- complete(&drvdata->hw_queue_avail);
-#ifdef COMP_IN_WQ
- queue_delayed_work(request_mgr_handle->workq,
- &request_mgr_handle->compwork, 0);
-#else
- tasklet_schedule(&request_mgr_handle->comptask);
-#endif
-}
-
-#ifdef COMP_IN_WQ
-static void comp_work_handler(struct work_struct *work)
-{
- struct cc_drvdata *drvdata =
- container_of(work, struct cc_drvdata, compwork.work);
-
- comp_handler((unsigned long)drvdata);
-}
-#endif
-
-static void proc_completions(struct cc_drvdata *drvdata)
-{
- struct cc_crypto_req *cc_req;
- struct device *dev = drvdata_to_dev(drvdata);
- struct cc_req_mgr_handle *request_mgr_handle =
- drvdata->request_mgr_handle;
- unsigned int *tail = &request_mgr_handle->req_queue_tail;
- unsigned int *head = &request_mgr_handle->req_queue_head;
-
- while (request_mgr_handle->axi_completed) {
- request_mgr_handle->axi_completed--;
-
- /* Dequeue request */
- if (*head == *tail) {
- /* We are supposed to handle a completion but our
- * queue is empty. This is not normal. Return and
- * hope for the best.
- */
- dev_err(dev, "Request queue is empty head == tail %u\n",
- *head);
- break;
- }
-
- cc_req = &request_mgr_handle->req_queue[*tail];
-
- if (cc_req->user_cb)
- cc_req->user_cb(dev, cc_req->user_arg, 0);
- *tail = (*tail + 1) & (MAX_REQUEST_QUEUE_SIZE - 1);
- dev_dbg(dev, "Dequeue request tail=%u\n", *tail);
- dev_dbg(dev, "Request completed. axi_completed=%d\n",
- request_mgr_handle->axi_completed);
- cc_pm_put_suspend(dev);
- }
-}
-
-static inline u32 cc_axi_comp_count(struct cc_drvdata *drvdata)
-{
- return FIELD_GET(AXIM_MON_COMP_VALUE,
- cc_ioread(drvdata, CC_REG(AXIM_MON_COMP)));
-}
-
-/* Deferred service handler, run as interrupt-fired tasklet */
-static void comp_handler(unsigned long devarg)
-{
- struct cc_drvdata *drvdata = (struct cc_drvdata *)devarg;
- struct cc_req_mgr_handle *request_mgr_handle =
- drvdata->request_mgr_handle;
-
- u32 irq;
-
- irq = (drvdata->irq & CC_COMP_IRQ_MASK);
-
- if (irq & CC_COMP_IRQ_MASK) {
- /* To avoid the interrupt from firing as we unmask it,
- * we clear it now
- */
- cc_iowrite(drvdata, CC_REG(HOST_ICR), CC_COMP_IRQ_MASK);
-
- /* Avoid race with above clear: Test completion counter
- * once more
- */
- request_mgr_handle->axi_completed +=
- cc_axi_comp_count(drvdata);
-
- while (request_mgr_handle->axi_completed) {
- do {
- proc_completions(drvdata);
- /* At this point (after proc_completions()),
- * request_mgr_handle->axi_completed is 0.
- */
- request_mgr_handle->axi_completed =
- cc_axi_comp_count(drvdata);
- } while (request_mgr_handle->axi_completed > 0);
-
- cc_iowrite(drvdata, CC_REG(HOST_ICR),
- CC_COMP_IRQ_MASK);
-
- request_mgr_handle->axi_completed +=
- cc_axi_comp_count(drvdata);
- }
- }
- /* after verifing that there is nothing to do,
- * unmask AXI completion interrupt
- */
- cc_iowrite(drvdata, CC_REG(HOST_IMR),
- cc_ioread(drvdata, CC_REG(HOST_IMR)) & ~irq);
-
- cc_proc_backlog(drvdata);
-}
-
-/*
- * resume the queue configuration - no need to take the lock as this happens
- * inside the spin lock protection
- */
-#if defined(CONFIG_PM)
-int cc_resume_req_queue(struct cc_drvdata *drvdata)
-{
- struct cc_req_mgr_handle *request_mgr_handle =
- drvdata->request_mgr_handle;
-
- spin_lock_bh(&request_mgr_handle->hw_lock);
- request_mgr_handle->is_runtime_suspended = false;
- spin_unlock_bh(&request_mgr_handle->hw_lock);
-
- return 0;
-}
-
-/*
- * suspend the queue configuration. Since it is used for the runtime suspend
- * only verify that the queue can be suspended.
- */
-int cc_suspend_req_queue(struct cc_drvdata *drvdata)
-{
- struct cc_req_mgr_handle *request_mgr_handle =
- drvdata->request_mgr_handle;
-
- /* lock the send_request */
- spin_lock_bh(&request_mgr_handle->hw_lock);
- if (request_mgr_handle->req_queue_head !=
- request_mgr_handle->req_queue_tail) {
- spin_unlock_bh(&request_mgr_handle->hw_lock);
- return -EBUSY;
- }
- request_mgr_handle->is_runtime_suspended = true;
- spin_unlock_bh(&request_mgr_handle->hw_lock);
-
- return 0;
-}
-
-bool cc_req_queue_suspended(struct cc_drvdata *drvdata)
-{
- struct cc_req_mgr_handle *request_mgr_handle =
- drvdata->request_mgr_handle;
-
- return request_mgr_handle->is_runtime_suspended;
-}
-
-#endif
-
diff --git a/drivers/staging/ccree/cc_sram_mgr.c b/drivers/staging/ccree/cc_sram_mgr.c
deleted file mode 100644
index d1f8a9cc1c0f..000000000000
--- a/drivers/staging/ccree/cc_sram_mgr.c
+++ /dev/null
@@ -1,107 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
-
-#include "cc_driver.h"
-#include "cc_sram_mgr.h"
-
-/**
- * struct cc_sram_ctx -Internal RAM context manager
- * @sram_free_offset: the offset to the non-allocated area
- */
-struct cc_sram_ctx {
- cc_sram_addr_t sram_free_offset;
-};
-
-/**
- * cc_sram_mgr_fini() - Cleanup SRAM pool.
- *
- * @drvdata: Associated device driver context
- */
-void cc_sram_mgr_fini(struct cc_drvdata *drvdata)
-{
- /* Free "this" context */
- kfree(drvdata->sram_mgr_handle);
-}
-
-/**
- * cc_sram_mgr_init() - Initializes SRAM pool.
- * The pool starts right at the beginning of SRAM.
- * Returns zero for success, negative value otherwise.
- *
- * @drvdata: Associated device driver context
- */
-int cc_sram_mgr_init(struct cc_drvdata *drvdata)
-{
- struct cc_sram_ctx *ctx;
-
- /* Allocate "this" context */
- ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
-
- if (!ctx)
- return -ENOMEM;
-
- drvdata->sram_mgr_handle = ctx;
-
- return 0;
-}
-
-/*!
- * Allocated buffer from SRAM pool.
- * Note: Caller is responsible to free the LAST allocated buffer.
- * This function does not taking care of any fragmentation may occur
- * by the order of calls to alloc/free.
- *
- * \param drvdata
- * \param size The requested bytes to allocate
- */
-cc_sram_addr_t cc_sram_alloc(struct cc_drvdata *drvdata, u32 size)
-{
- struct cc_sram_ctx *smgr_ctx = drvdata->sram_mgr_handle;
- struct device *dev = drvdata_to_dev(drvdata);
- cc_sram_addr_t p;
-
- if ((size & 0x3)) {
- dev_err(dev, "Requested buffer size (%u) is not multiple of 4",
- size);
- return NULL_SRAM_ADDR;
- }
- if (size > (CC_CC_SRAM_SIZE - smgr_ctx->sram_free_offset)) {
- dev_err(dev, "Not enough space to allocate %u B (at offset %llu)\n",
- size, smgr_ctx->sram_free_offset);
- return NULL_SRAM_ADDR;
- }
-
- p = smgr_ctx->sram_free_offset;
- smgr_ctx->sram_free_offset += size;
- dev_dbg(dev, "Allocated %u B @ %u\n", size, (unsigned int)p);
- return p;
-}
-
-/**
- * cc_set_sram_desc() - Create const descriptors sequence to
- * set values in given array into SRAM.
- * Note: each const value can't exceed word size.
- *
- * @src: A pointer to array of words to set as consts.
- * @dst: The target SRAM buffer to set into
- * @nelements: The number of words in "src" array
- * @seq: A pointer to the given IN/OUT descriptor sequence
- * @seq_len: A pointer to the given IN/OUT sequence length
- */
-void cc_set_sram_desc(const u32 *src, cc_sram_addr_t dst,
- unsigned int nelement, struct cc_hw_desc *seq,
- unsigned int *seq_len)
-{
- u32 i;
- unsigned int idx = *seq_len;
-
- for (i = 0; i < nelement; i++, idx++) {
- hw_desc_init(&seq[idx]);
- set_din_const(&seq[idx], src[i], sizeof(u32));
- set_dout_sram(&seq[idx], dst + (i * sizeof(u32)), sizeof(u32));
- set_flow_mode(&seq[idx], BYPASS);
- }
-
- *seq_len = idx;
-}
-
diff --git a/drivers/staging/comedi/drivers/adl_pci6208.c b/drivers/staging/comedi/drivers/adl_pci6208.c
index d39b4eabce8d..e21840e9002d 100644
--- a/drivers/staging/comedi/drivers/adl_pci6208.c
+++ b/drivers/staging/comedi/drivers/adl_pci6208.c
@@ -58,12 +58,11 @@ static int pci6208_ao_insn_write(struct comedi_device *dev,
unsigned int *data)
{
unsigned int chan = CR_CHAN(insn->chanspec);
- unsigned int val = s->readback[chan];
int ret;
int i;
for (i = 0; i < insn->n; i++) {
- val = data[i];
+ unsigned int val = data[i];
/* D/A transfer rate is 2.2us */
ret = comedi_timeout(dev, s, insn, pci6208_ao_eoc, 0);
diff --git a/drivers/staging/comedi/drivers/cb_pcidas64.c b/drivers/staging/comedi/drivers/cb_pcidas64.c
index b657beedd5ff..fdd81c3beb51 100644
--- a/drivers/staging/comedi/drivers/cb_pcidas64.c
+++ b/drivers/staging/comedi/drivers/cb_pcidas64.c
@@ -1488,11 +1488,10 @@ static int alloc_and_init_dma_members(struct comedi_device *dev)
for (i = 0; i < AO_DMA_RING_COUNT; i++) {
if (ao_cmd_is_supported(board)) {
devpriv->ao_buffer[i] =
- dma_alloc_coherent(&pcidev->dev,
- DMA_BUFFER_SIZE,
- &devpriv->
- ao_buffer_bus_addr[i],
- GFP_KERNEL);
+ dma_alloc_coherent(&pcidev->dev,
+ DMA_BUFFER_SIZE,
+ &devpriv->ao_buffer_bus_addr[i],
+ GFP_KERNEL);
if (!devpriv->ao_buffer[i])
return -ENOMEM;
}
@@ -1701,7 +1700,7 @@ static void i2c_write(struct comedi_device *dev, unsigned int address,
* eeprom and i2c bus
*/
- /* make sure we dont send anything to eeprom */
+ /* make sure we don't send anything to eeprom */
devpriv->plx_control_bits &= ~PLX_CNTRL_EECS;
i2c_stop(dev);
@@ -2463,20 +2462,21 @@ static int setup_channel_queue(struct comedi_device *dev,
writew(0, devpriv->main_iobase + ADC_QUEUE_CLEAR_REG);
/* load external queue */
for (i = 0; i < cmd->chanlist_len; i++) {
+ unsigned int chanspec = cmd->chanlist[i];
+ int use_differential;
+
bits = 0;
/* set channel */
- bits |= adc_chan_bits(CR_CHAN(cmd->
- chanlist[i]));
+ bits |= adc_chan_bits(CR_CHAN(chanspec));
/* set gain */
bits |= ai_range_bits_6xxx(dev,
- CR_RANGE(cmd->
- chanlist
- [i]));
+ CR_RANGE(chanspec));
/* set single-ended / differential */
- bits |= se_diff_bit_6xxx(dev,
- CR_AREF(cmd->
- chanlist[i]) ==
- AREF_DIFF);
+ use_differential = 0;
+ if (CR_AREF(chanspec) == AREF_DIFF)
+ use_differential = 1;
+ bits |= se_diff_bit_6xxx(dev, use_differential);
+
if (CR_AREF(cmd->chanlist[i]) == AREF_COMMON)
bits |= ADC_COMMON_BIT;
/* mark end of queue */
@@ -3248,17 +3248,15 @@ static int prep_ao_dma(struct comedi_device *dev, const struct comedi_cmd *cmd)
return 0;
}
-static inline int external_ai_queue_in_use(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_cmd *cmd)
+static inline int external_ai_queue_in_use(struct comedi_device *dev)
{
const struct pcidas64_board *board = dev->board_ptr;
- if (s->busy)
+ if (!dev->read_subdev->busy)
return 0;
if (board->layout == LAYOUT_4020)
return 0;
- else if (use_internal_queue_6xxx(cmd))
+ else if (use_internal_queue_6xxx(&dev->read_subdev->async->cmd))
return 0;
return 1;
}
@@ -3292,7 +3290,7 @@ static int ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
struct pcidas64_private *devpriv = dev->private;
struct comedi_cmd *cmd = &s->async->cmd;
- if (external_ai_queue_in_use(dev, s, cmd)) {
+ if (external_ai_queue_in_use(dev)) {
warn_external_queue(dev);
return -EBUSY;
}
diff --git a/drivers/staging/comedi/drivers/das16.c b/drivers/staging/comedi/drivers/das16.c
index 74ff204b585d..81eb51b1be25 100644
--- a/drivers/staging/comedi/drivers/das16.c
+++ b/drivers/staging/comedi/drivers/das16.c
@@ -619,7 +619,7 @@ static int das16_cmd_test(struct comedi_device *dev, struct comedi_subdevice *s,
/* Step 2b : and mutually compatible */
- /* make sure scan_begin_src and convert_src dont conflict */
+ /* make sure scan_begin_src and convert_src don't conflict */
if (cmd->scan_begin_src == TRIG_FOLLOW && cmd->convert_src == TRIG_NOW)
err |= -EINVAL;
if (cmd->scan_begin_src != TRIG_FOLLOW && cmd->convert_src != TRIG_NOW)
diff --git a/drivers/staging/comedi/drivers/das16m1.c b/drivers/staging/comedi/drivers/das16m1.c
index 72f8ed2c5008..4e36377b592a 100644
--- a/drivers/staging/comedi/drivers/das16m1.c
+++ b/drivers/staging/comedi/drivers/das16m1.c
@@ -407,7 +407,7 @@ static void das16m1_handler(struct comedi_device *dev, unsigned int status)
if (num_samples > cmd->stop_arg * cmd->chanlist_len)
num_samples = cmd->stop_arg * cmd->chanlist_len;
}
- /* make sure we dont try to get too many points if fifo has overrun */
+ /* make sure we don't try to get too many points if fifo has overrun */
if (num_samples > DAS16M1_AI_FIFO_SZ)
num_samples = DAS16M1_AI_FIFO_SZ;
insw(dev->iobase, devpriv->ai_buffer, num_samples);
diff --git a/drivers/staging/comedi/drivers/jr3_pci.c b/drivers/staging/comedi/drivers/jr3_pci.c
index 201f4f96c182..c3c88e6d298f 100644
--- a/drivers/staging/comedi/drivers/jr3_pci.c
+++ b/drivers/staging/comedi/drivers/jr3_pci.c
@@ -295,7 +295,6 @@ static int jr3_pci_open(struct comedi_device *dev)
struct comedi_subdevice *s;
int i;
- dev_dbg(dev->class_dev, "jr3_pci_open\n");
for (i = 0; i < dev->n_subdevices; i++) {
s = &dev->subdevices[i];
spriv = s->private;
diff --git a/drivers/staging/comedi/drivers/ni_atmio.c b/drivers/staging/comedi/drivers/ni_atmio.c
index b9e9ab548c4b..2b7bfe0dd7f3 100644
--- a/drivers/staging/comedi/drivers/ni_atmio.c
+++ b/drivers/staging/comedi/drivers/ni_atmio.c
@@ -224,10 +224,11 @@ static int ni_isapnp_find_board(struct pnp_dev **dev)
int i;
for (i = 0; i < ARRAY_SIZE(ni_boards); i++) {
- isapnp_dev = pnp_find_dev(NULL,
- ISAPNP_VENDOR('N', 'I', 'C'),
- ISAPNP_FUNCTION(ni_boards[i].
- isapnp_id), NULL);
+ isapnp_dev =
+ pnp_find_dev(NULL,
+ ISAPNP_VENDOR('N', 'I', 'C'),
+ ISAPNP_FUNCTION(ni_boards[i].isapnp_id),
+ NULL);
if (!isapnp_dev || !isapnp_dev->card)
continue;
diff --git a/drivers/staging/comedi/drivers/ni_mio_common.c b/drivers/staging/comedi/drivers/ni_mio_common.c
index 5d610af6799f..e40a2c0a9543 100644
--- a/drivers/staging/comedi/drivers/ni_mio_common.c
+++ b/drivers/staging/comedi/drivers/ni_mio_common.c
@@ -1275,6 +1275,8 @@ static void ack_a_interrupt(struct comedi_device *dev, unsigned short a_status)
ack |= NISTC_INTA_ACK_AI_START;
if (a_status & NISTC_AI_STATUS1_STOP)
ack |= NISTC_INTA_ACK_AI_STOP;
+ if (a_status & NISTC_AI_STATUS1_OVER)
+ ack |= NISTC_INTA_ACK_AI_ERR;
if (ack)
ni_stc_writew(dev, ack, NISTC_INTA_ACK_REG);
}
@@ -1965,7 +1967,8 @@ static void ni_cmd_set_mite_transfer(struct mite_ring *ring,
if (nbytes > sdev->async->prealloc_bufsz) {
if (cmd->stop_arg > 0)
dev_err(sdev->device->class_dev,
- "ni_cmd_set_mite_transfer: tried exact data transfer limits greater than buffer size\n");
+ "%s: tried exact data transfer limits greater than buffer size\n",
+ __func__);
/*
* we can only transfer up to the size of the buffer. In this
@@ -1978,7 +1981,8 @@ static void ni_cmd_set_mite_transfer(struct mite_ring *ring,
mite_init_ring_descriptors(ring, sdev, nbytes);
#else
dev_err(sdev->device->class_dev,
- "ni_cmd_set_mite_transfer: exact data transfer limits not implemented yet without DMA\n");
+ "%s: exact data transfer limits not implemented yet without DMA\n",
+ __func__);
#endif
}
@@ -4687,7 +4691,7 @@ static int cs5529_do_conversion(struct comedi_device *dev,
retval = cs5529_wait_for_idle(dev);
if (retval) {
dev_err(dev->class_dev,
- "timeout or signal in cs5529_do_conversion()\n");
+ "timeout or signal in %s()\n", __func__);
return -ETIME;
}
status = ni_ao_win_inw(dev, NI67XX_CAL_STATUS_REG);
diff --git a/drivers/staging/comedi/drivers/ni_stc.h b/drivers/staging/comedi/drivers/ni_stc.h
index cb9d4c3a1926..831088c5cabb 100644
--- a/drivers/staging/comedi/drivers/ni_stc.h
+++ b/drivers/staging/comedi/drivers/ni_stc.h
@@ -9,7 +9,7 @@
/*
* References:
* DAQ-STC Technical Reference Manual
-*/
+ */
#ifndef _COMEDI_NI_STC_H
#define _COMEDI_NI_STC_H
diff --git a/drivers/staging/comedi/drivers/quatech_daqp_cs.c b/drivers/staging/comedi/drivers/quatech_daqp_cs.c
index 802f51e46405..ea194aa01a64 100644
--- a/drivers/staging/comedi/drivers/quatech_daqp_cs.c
+++ b/drivers/staging/comedi/drivers/quatech_daqp_cs.c
@@ -248,7 +248,7 @@ static irqreturn_t daqp_interrupt(int irq, void *dev_id)
if (loop_limit <= 0) {
dev_warn(dev->class_dev,
- "loop_limit reached in daqp_interrupt()\n");
+ "loop_limit reached in %s()\n", __func__);
s->async->events |= COMEDI_CB_ERROR;
}
diff --git a/drivers/staging/comedi/drivers/s626.c b/drivers/staging/comedi/drivers/s626.c
index 0b3cfe934e14..f5af6f4069dc 100644
--- a/drivers/staging/comedi/drivers/s626.c
+++ b/drivers/staging/comedi/drivers/s626.c
@@ -1700,7 +1700,7 @@ static int s626_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
if (devpriv->ai_cmd_running) {
dev_err(dev->class_dev,
- "s626_ai_cmd: Another ai_cmd is running\n");
+ "%s: Another ai_cmd is running\n", __func__);
return -EBUSY;
}
/* disable interrupt */
@@ -2268,10 +2268,10 @@ static int s626_initialize(struct comedi_device *dev)
*/
{
struct comedi_subdevice *s = dev->read_subdev;
- uint8_t poll_list;
- uint16_t adc_data;
- uint16_t start_val;
- uint16_t index;
+ u8 poll_list;
+ u16 adc_data;
+ u16 start_val;
+ u16 index;
unsigned int data[16];
/* Create a simple polling list for analog input channel 0 */
diff --git a/drivers/staging/emxx_udc/emxx_udc.c b/drivers/staging/emxx_udc/emxx_udc.c
index 7517001fb8f0..3e51476a7045 100644
--- a/drivers/staging/emxx_udc/emxx_udc.c
+++ b/drivers/staging/emxx_udc/emxx_udc.c
@@ -1672,9 +1672,6 @@ static int std_req_set_configuration(struct nbu2ss_udc *udc)
/*-------------------------------------------------------------------------*/
static inline void _nbu2ss_read_request_data(struct nbu2ss_udc *udc, u32 *pdata)
{
- if ((!udc) && (!pdata))
- return;
-
*pdata = _nbu2ss_readl(&udc->p_regs->SETUP_DATA0);
pdata++;
*pdata = _nbu2ss_readl(&udc->p_regs->SETUP_DATA1);
@@ -2686,7 +2683,7 @@ static int nbu2ss_ep_queue(
if (req->unaligned) {
if (!ep->virt_buf)
- ep->virt_buf = (u8 *)dma_alloc_coherent(
+ ep->virt_buf = dma_alloc_coherent(
NULL, PAGE_SIZE,
&ep->phys_buf, GFP_ATOMIC | GFP_DMA);
if (ep->epnum > 0) {
@@ -2941,11 +2938,6 @@ static int nbu2ss_gad_get_frame(struct usb_gadget *pgadget)
}
udc = container_of(pgadget, struct nbu2ss_udc, gadget);
- if (!udc) {
- dev_err(&pgadget->dev, "%s, udc == NULL\n", __func__);
- return -EINVAL;
- }
-
data = gpio_get_value(VBUS_VALUE);
if (data == 0)
return -EINVAL;
diff --git a/drivers/staging/fsl-dpaa2/Kconfig b/drivers/staging/fsl-dpaa2/Kconfig
index dfff675b3055..bbb7af551696 100644
--- a/drivers/staging/fsl-dpaa2/Kconfig
+++ b/drivers/staging/fsl-dpaa2/Kconfig
@@ -4,7 +4,7 @@
config FSL_DPAA2
bool "Freescale DPAA2 devices"
- depends on FSL_MC_BUS && ARCH_LAYERSCAPE
+ depends on FSL_MC_BUS
---help---
Build drivers for Freescale DataPath Acceleration
Architecture (DPAA2) family of SoCs.
@@ -16,3 +16,11 @@ config FSL_DPAA2_ETH
---help---
Ethernet driver for Freescale DPAA2 SoCs, using the
Freescale MC bus driver
+
+config FSL_DPAA2_ETHSW
+ tristate "Freescale DPAA2 Ethernet Switch"
+ depends on FSL_DPAA2
+ depends on NET_SWITCHDEV
+ ---help---
+ Driver for Freescale DPAA2 Ethernet Switch. Select
+ BRIDGE to have support for bridge tools.
diff --git a/drivers/staging/fsl-dpaa2/Makefile b/drivers/staging/fsl-dpaa2/Makefile
index 0836ba8977b1..6cfd76b29970 100644
--- a/drivers/staging/fsl-dpaa2/Makefile
+++ b/drivers/staging/fsl-dpaa2/Makefile
@@ -3,3 +3,4 @@
#
obj-$(CONFIG_FSL_DPAA2_ETH) += ethernet/
+obj-$(CONFIG_FSL_DPAA2_ETHSW) += ethsw/
diff --git a/drivers/staging/fsl-dpaa2/ethernet/README b/drivers/staging/fsl-dpaa2/ethernet/README
index 410952ecf657..e3b5c90197e4 100644
--- a/drivers/staging/fsl-dpaa2/ethernet/README
+++ b/drivers/staging/fsl-dpaa2/ethernet/README
@@ -36,7 +36,7 @@ are treated as internal resources of other objects.
For a more detailed description of the DPAA2 architecture and its object
abstractions see:
- drivers/staging/fsl-mc/README.txt
+ Documentation/networking/dpaa2/overview.rst
Each Linux net device is built on top of a Datapath Network Interface (DPNI)
object and uses Buffer Pools (DPBPs), I/O Portals (DPIOs) and Concentrators
diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c
index 2817e67df3d5..553678d8b2eb 100644
--- a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c
+++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c
@@ -39,7 +39,7 @@
#include <linux/kthread.h>
#include <linux/iommu.h>
-#include "../../fsl-mc/include/mc.h"
+#include <linux/fsl/mc.h>
#include "dpaa2-eth.h"
/* CREATE_TRACE_POINTS only needs to be defined once. Other dpa files
@@ -324,7 +324,7 @@ static int consume_frames(struct dpaa2_eth_channel *ch)
}
fd = dpaa2_dq_fd(dq);
- fq = (struct dpaa2_eth_fq *)dpaa2_dq_fqd_ctx(dq);
+ fq = (struct dpaa2_eth_fq *)(uintptr_t)dpaa2_dq_fqd_ctx(dq);
fq->stats.frames++;
fq->consume(priv, ch, fd, &ch->napi, fq->flowid);
@@ -373,13 +373,15 @@ static int build_sg_fd(struct dpaa2_eth_priv *priv,
/* Prepare the HW SGT structure */
sgt_buf_size = priv->tx_data_offset +
- sizeof(struct dpaa2_sg_entry) * (1 + num_dma_bufs);
- sgt_buf = kzalloc(sgt_buf_size + DPAA2_ETH_TX_BUF_ALIGN, GFP_ATOMIC);
+ sizeof(struct dpaa2_sg_entry) * num_dma_bufs;
+ sgt_buf = netdev_alloc_frag(sgt_buf_size + DPAA2_ETH_TX_BUF_ALIGN);
if (unlikely(!sgt_buf)) {
err = -ENOMEM;
goto sgt_buf_alloc_failed;
}
sgt_buf = PTR_ALIGN(sgt_buf, DPAA2_ETH_TX_BUF_ALIGN);
+ memset(sgt_buf, 0, sgt_buf_size);
+
sgt = (struct dpaa2_sg_entry *)(sgt_buf + priv->tx_data_offset);
/* Fill in the HW SGT structure.
@@ -404,7 +406,7 @@ static int build_sg_fd(struct dpaa2_eth_priv *priv,
swa->skb = skb;
swa->scl = scl;
swa->num_sg = num_sg;
- swa->num_dma_bufs = num_dma_bufs;
+ swa->sgt_size = sgt_buf_size;
/* Separately map the SGT buffer */
addr = dma_map_single(dev, sgt_buf, sgt_buf_size, DMA_BIDIRECTIONAL);
@@ -421,7 +423,7 @@ static int build_sg_fd(struct dpaa2_eth_priv *priv,
return 0;
dma_map_single_failed:
- kfree(sgt_buf);
+ skb_free_frag(sgt_buf);
sgt_buf_alloc_failed:
dma_unmap_sg(dev, scl, num_sg, DMA_BIDIRECTIONAL);
dma_map_sg_failed:
@@ -487,9 +489,6 @@ static void free_tx_fd(const struct dpaa2_eth_priv *priv,
dma_addr_t fd_addr;
struct sk_buff **skbh, *skb;
unsigned char *buffer_start;
- int unmap_size;
- struct scatterlist *scl;
- int num_sg, num_dma_bufs;
struct dpaa2_eth_swa *swa;
u8 fd_format = dpaa2_fd_get_format(fd);
@@ -508,26 +507,22 @@ static void free_tx_fd(const struct dpaa2_eth_priv *priv,
} else if (fd_format == dpaa2_fd_sg) {
swa = (struct dpaa2_eth_swa *)skbh;
skb = swa->skb;
- scl = swa->scl;
- num_sg = swa->num_sg;
- num_dma_bufs = swa->num_dma_bufs;
/* Unmap the scatterlist */
- dma_unmap_sg(dev, scl, num_sg, DMA_BIDIRECTIONAL);
- kfree(scl);
+ dma_unmap_sg(dev, swa->scl, swa->num_sg, DMA_BIDIRECTIONAL);
+ kfree(swa->scl);
/* Unmap the SGT buffer */
- unmap_size = priv->tx_data_offset +
- sizeof(struct dpaa2_sg_entry) * (1 + num_dma_bufs);
- dma_unmap_single(dev, fd_addr, unmap_size, DMA_BIDIRECTIONAL);
+ dma_unmap_single(dev, fd_addr, swa->sgt_size,
+ DMA_BIDIRECTIONAL);
} else {
netdev_dbg(priv->net_dev, "Invalid FD format\n");
return;
}
- /* Free SGT buffer kmalloc'ed on tx */
+ /* Free SGT buffer allocated on tx */
if (fd_format != dpaa2_fd_single)
- kfree(skbh);
+ skb_free_frag(skbh);
/* Move on with skb release */
dev_kfree_skb(skb);
@@ -1844,6 +1839,21 @@ static int setup_dpni(struct fsl_mc_device *ls_dev)
return err;
}
+ /* Check if we can work with this DPNI object */
+ err = dpni_get_api_version(priv->mc_io, 0, &priv->dpni_ver_major,
+ &priv->dpni_ver_minor);
+ if (err) {
+ dev_err(dev, "dpni_get_api_version() failed\n");
+ goto close;
+ }
+ if (dpaa2_eth_cmp_dpni_ver(priv, DPNI_VER_MAJOR, DPNI_VER_MINOR) < 0) {
+ dev_err(dev, "DPNI version %u.%u not supported, need >= %u.%u\n",
+ priv->dpni_ver_major, priv->dpni_ver_minor,
+ DPNI_VER_MAJOR, DPNI_VER_MINOR);
+ err = -ENOTSUPP;
+ goto close;
+ }
+
ls_dev->mc_io = priv->mc_io;
ls_dev->mc_handle = priv->mc_token;
@@ -1864,7 +1874,6 @@ static int setup_dpni(struct fsl_mc_device *ls_dev)
if (err)
goto close;
-
return 0;
close:
@@ -1906,7 +1915,7 @@ static int setup_rx_flow(struct dpaa2_eth_priv *priv,
queue.destination.id = fq->channel->dpcon_id;
queue.destination.type = DPNI_DEST_DPCON;
queue.destination.priority = 1;
- queue.user_context = (u64)fq;
+ queue.user_context = (u64)(uintptr_t)fq;
err = dpni_set_queue(priv->mc_io, 0, priv->mc_token,
DPNI_QUEUE_RX, 0, fq->flowid,
DPNI_QUEUE_OPT_USER_CTX | DPNI_QUEUE_OPT_DEST,
@@ -1958,7 +1967,7 @@ static int setup_tx_flow(struct dpaa2_eth_priv *priv,
queue.destination.id = fq->channel->dpcon_id;
queue.destination.type = DPNI_DEST_DPCON;
queue.destination.priority = 0;
- queue.user_context = (u64)fq;
+ queue.user_context = (u64)(uintptr_t)fq;
err = dpni_set_queue(priv->mc_io, 0, priv->mc_token,
DPNI_QUEUE_TX_CONFIRM, 0, fq->flowid,
DPNI_QUEUE_OPT_USER_CTX | DPNI_QUEUE_OPT_DEST,
@@ -2316,11 +2325,6 @@ static int poll_link_state(void *arg)
return 0;
}
-static irqreturn_t dpni_irq0_handler(int irq_num, void *arg)
-{
- return IRQ_WAKE_THREAD;
-}
-
static irqreturn_t dpni_irq0_handler_thread(int irq_num, void *arg)
{
u32 status = ~0;
@@ -2355,8 +2359,7 @@ static int setup_irqs(struct fsl_mc_device *ls_dev)
irq = ls_dev->irqs[0];
err = devm_request_threaded_irq(&ls_dev->dev, irq->msi_desc->irq,
- dpni_irq0_handler,
- dpni_irq0_handler_thread,
+ NULL, dpni_irq0_handler_thread,
IRQF_NO_SUSPEND | IRQF_ONESHOT,
dev_name(&ls_dev->dev), &ls_dev->dev);
if (err < 0) {
@@ -2440,7 +2443,10 @@ static int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev)
err = fsl_mc_portal_allocate(dpni_dev, FSL_MC_IO_ATOMIC_CONTEXT_PORTAL,
&priv->mc_io);
if (err) {
- dev_err(dev, "MC portal allocation failed\n");
+ if (err == -ENXIO)
+ err = -EPROBE_DEFER;
+ else
+ dev_err(dev, "MC portal allocation failed\n");
goto err_portal_alloc;
}
@@ -2552,7 +2558,6 @@ static int dpaa2_eth_remove(struct fsl_mc_device *ls_dev)
priv = netdev_priv(net_dev);
unregister_netdev(net_dev);
- dev_info(net_dev->dev.parent, "Removed interface %s\n", net_dev->name);
if (priv->do_link_poll)
kthread_stop(priv->poll_thread);
@@ -2573,6 +2578,8 @@ static int dpaa2_eth_remove(struct fsl_mc_device *ls_dev)
dev_set_drvdata(dev, NULL);
free_netdev(net_dev);
+ dev_dbg(net_dev->dev.parent, "Removed interface %s\n", net_dev->name);
+
return 0;
}
diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h
index e577410fdf4f..54cea2fc6e58 100644
--- a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h
+++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h
@@ -35,11 +35,10 @@
#include <linux/netdevice.h>
#include <linux/if_vlan.h>
+#include <linux/fsl/mc.h>
#include "../../fsl-mc/include/dpaa2-io.h"
#include "../../fsl-mc/include/dpaa2-fd.h"
-#include "../../fsl-mc/include/dpbp.h"
-#include "../../fsl-mc/include/dpcon.h"
#include "dpni.h"
#include "dpni-cmd.h"
@@ -109,7 +108,7 @@ struct dpaa2_eth_swa {
struct sk_buff *skb;
struct scatterlist *scl;
int num_sg;
- int num_dma_bufs;
+ int sgt_size;
};
/* Annotation valid bits in FD FRC */
@@ -143,7 +142,7 @@ struct dpaa2_fas {
u8 ppid;
__le16 ifpid;
__le32 status;
-} __packed;
+};
/* Frame annotation status word is located in the first 8 bytes
* of the buffer's hardware annoatation area
@@ -252,11 +251,11 @@ struct dpaa2_eth_ch_stats {
/* Maximum number of queues associated with a DPNI */
#define DPAA2_ETH_MAX_RX_QUEUES 16
-#define DPAA2_ETH_MAX_TX_QUEUES NR_CPUS
+#define DPAA2_ETH_MAX_TX_QUEUES 16
#define DPAA2_ETH_MAX_QUEUES (DPAA2_ETH_MAX_RX_QUEUES + \
DPAA2_ETH_MAX_TX_QUEUES)
-#define DPAA2_ETH_MAX_DPCONS NR_CPUS
+#define DPAA2_ETH_MAX_DPCONS 16
enum dpaa2_eth_fq_type {
DPAA2_RX_FQ = 0,
@@ -286,7 +285,6 @@ struct dpaa2_eth_channel {
struct fsl_mc_device *dpcon;
int dpcon_id;
int ch_id;
- int dpio_id;
struct napi_struct napi;
struct dpaa2_io *dpio;
struct dpaa2_io_store *store;
@@ -313,6 +311,8 @@ struct dpaa2_eth_priv {
struct dpaa2_eth_channel *channel[DPAA2_ETH_MAX_DPCONS];
struct dpni_attr dpni_attrs;
+ u16 dpni_ver_major;
+ u16 dpni_ver_minor;
u16 tx_data_offset;
struct fsl_mc_device *dpbp_dev;
@@ -356,6 +356,14 @@ struct dpaa2_eth_priv {
extern const struct ethtool_ops dpaa2_ethtool_ops;
extern const char dpaa2_eth_drv_version[];
+static inline int dpaa2_eth_cmp_dpni_ver(struct dpaa2_eth_priv *priv,
+ u16 ver_major, u16 ver_minor)
+{
+ if (priv->dpni_ver_major == ver_major)
+ return priv->dpni_ver_minor - ver_minor;
+ return priv->dpni_ver_major - ver_major;
+}
+
/* Hardware only sees DPAA2_ETH_RX_BUF_SIZE, but the skb built around
* the buffer also needs space for its shared info struct, and we need
* to allocate enough to accommodate hardware alignment restrictions
diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c
index 070a3f2a0523..bfc8b64169ca 100644
--- a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c
+++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c
@@ -78,20 +78,13 @@ static void dpaa2_eth_get_drvinfo(struct net_device *net_dev,
struct ethtool_drvinfo *drvinfo)
{
struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
- u16 fw_major, fw_minor;
- int err;
strlcpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver));
strlcpy(drvinfo->version, dpaa2_eth_drv_version,
sizeof(drvinfo->version));
- err = dpni_get_api_version(priv->mc_io, 0, &fw_major, &fw_minor);
- if (!err)
- snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
- "%u.%u", fw_major, fw_minor);
- else
- strlcpy(drvinfo->fw_version, "N/A",
- sizeof(drvinfo->fw_version));
+ snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
+ "%u.%u", priv->dpni_ver_major, priv->dpni_ver_minor);
strlcpy(drvinfo->bus_info, dev_name(net_dev->dev.parent->parent),
sizeof(drvinfo->bus_info));
@@ -126,6 +119,8 @@ out:
return err;
}
+#define DPNI_DYNAMIC_LINK_SET_VER_MAJOR 7
+#define DPNI_DYNAMIC_LINK_SET_VER_MINOR 1
static int
dpaa2_eth_set_link_ksettings(struct net_device *net_dev,
const struct ethtool_link_ksettings *link_settings)
@@ -134,15 +129,16 @@ dpaa2_eth_set_link_ksettings(struct net_device *net_dev,
struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
int err = 0;
- netdev_dbg(net_dev, "Setting link parameters...");
-
- /* Due to a temporary MC limitation, the DPNI must be down
+ /* If using an older MC version, the DPNI must be down
* in order to be able to change link settings. Taking steps to let
* the user know that.
*/
- if (netif_running(net_dev)) {
- netdev_info(net_dev, "Sorry, interface must be brought down first.\n");
- return -EACCES;
+ if (dpaa2_eth_cmp_dpni_ver(priv, DPNI_DYNAMIC_LINK_SET_VER_MAJOR,
+ DPNI_DYNAMIC_LINK_SET_VER_MINOR) < 0) {
+ if (netif_running(net_dev)) {
+ netdev_info(net_dev, "Interface must be brought down first.\n");
+ return -EACCES;
+ }
}
cfg.rate = link_settings->base.speed;
diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpni-cmd.h b/drivers/staging/fsl-dpaa2/ethernet/dpni-cmd.h
index 3120e22496d0..d6f96f302cc6 100644
--- a/drivers/staging/fsl-dpaa2/ethernet/dpni-cmd.h
+++ b/drivers/staging/fsl-dpaa2/ethernet/dpni-cmd.h
@@ -539,8 +539,8 @@ struct dpni_rsp_get_taildrop {
};
struct dpni_rsp_get_api_version {
- u16 major;
- u16 minor;
+ __le16 major;
+ __le16 minor;
};
#endif /* _FSL_DPNI_CMD_H */
diff --git a/drivers/staging/fsl-dpaa2/ethernet/dpni.c b/drivers/staging/fsl-dpaa2/ethernet/dpni.c
index e8be76181c36..1a721c95a67a 100644
--- a/drivers/staging/fsl-dpaa2/ethernet/dpni.c
+++ b/drivers/staging/fsl-dpaa2/ethernet/dpni.c
@@ -32,7 +32,7 @@
*/
#include <linux/kernel.h>
#include <linux/errno.h>
-#include "../../fsl-mc/include/mc.h"
+#include <linux/fsl/mc.h>
#include "dpni.h"
#include "dpni-cmd.h"
@@ -122,7 +122,7 @@ int dpni_open(struct fsl_mc_io *mc_io,
int dpni_id,
u16 *token)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_cmd_open *cmd_params;
int err;
@@ -160,7 +160,7 @@ int dpni_close(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_CLOSE,
@@ -188,7 +188,7 @@ int dpni_set_pools(struct fsl_mc_io *mc_io,
u16 token,
const struct dpni_pools_cfg *cfg)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_cmd_set_pools *cmd_params;
int i;
@@ -222,7 +222,7 @@ int dpni_enable(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_ENABLE,
@@ -245,7 +245,7 @@ int dpni_disable(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_DISABLE,
@@ -270,7 +270,7 @@ int dpni_is_enabled(struct fsl_mc_io *mc_io,
u16 token,
int *en)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_rsp_is_enabled *rsp_params;
int err;
@@ -303,7 +303,7 @@ int dpni_reset(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_RESET,
@@ -335,7 +335,7 @@ int dpni_set_irq_enable(struct fsl_mc_io *mc_io,
u8 irq_index,
u8 en)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_cmd_set_irq_enable *cmd_params;
/* prepare command */
@@ -366,7 +366,7 @@ int dpni_get_irq_enable(struct fsl_mc_io *mc_io,
u8 irq_index,
u8 *en)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_cmd_get_irq_enable *cmd_params;
struct dpni_rsp_get_irq_enable *rsp_params;
@@ -413,7 +413,7 @@ int dpni_set_irq_mask(struct fsl_mc_io *mc_io,
u8 irq_index,
u32 mask)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_cmd_set_irq_mask *cmd_params;
/* prepare command */
@@ -447,7 +447,7 @@ int dpni_get_irq_mask(struct fsl_mc_io *mc_io,
u8 irq_index,
u32 *mask)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_cmd_get_irq_mask *cmd_params;
struct dpni_rsp_get_irq_mask *rsp_params;
int err;
@@ -489,7 +489,7 @@ int dpni_get_irq_status(struct fsl_mc_io *mc_io,
u8 irq_index,
u32 *status)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_cmd_get_irq_status *cmd_params;
struct dpni_rsp_get_irq_status *rsp_params;
int err;
@@ -532,7 +532,7 @@ int dpni_clear_irq_status(struct fsl_mc_io *mc_io,
u8 irq_index,
u32 status)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_cmd_clear_irq_status *cmd_params;
/* prepare command */
@@ -561,7 +561,7 @@ int dpni_get_attributes(struct fsl_mc_io *mc_io,
u16 token,
struct dpni_attr *attr)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_rsp_get_attr *rsp_params;
int err;
@@ -609,7 +609,7 @@ int dpni_set_errors_behavior(struct fsl_mc_io *mc_io,
u16 token,
struct dpni_error_cfg *cfg)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_cmd_set_errors_behavior *cmd_params;
/* prepare command */
@@ -641,7 +641,7 @@ int dpni_get_buffer_layout(struct fsl_mc_io *mc_io,
enum dpni_queue_type qtype,
struct dpni_buffer_layout *layout)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_cmd_get_buffer_layout *cmd_params;
struct dpni_rsp_get_buffer_layout *rsp_params;
int err;
@@ -689,7 +689,7 @@ int dpni_set_buffer_layout(struct fsl_mc_io *mc_io,
enum dpni_queue_type qtype,
const struct dpni_buffer_layout *layout)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_cmd_set_buffer_layout *cmd_params;
/* prepare command */
@@ -731,7 +731,7 @@ int dpni_set_offload(struct fsl_mc_io *mc_io,
enum dpni_offload type,
u32 config)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_cmd_set_offload *cmd_params;
cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_OFFLOAD,
@@ -750,7 +750,7 @@ int dpni_get_offload(struct fsl_mc_io *mc_io,
enum dpni_offload type,
u32 *config)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_cmd_get_offload *cmd_params;
struct dpni_rsp_get_offload *rsp_params;
int err;
@@ -792,7 +792,7 @@ int dpni_get_qdid(struct fsl_mc_io *mc_io,
enum dpni_queue_type qtype,
u16 *qdid)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_cmd_get_qdid *cmd_params;
struct dpni_rsp_get_qdid *rsp_params;
int err;
@@ -830,7 +830,7 @@ int dpni_get_tx_data_offset(struct fsl_mc_io *mc_io,
u16 token,
u16 *data_offset)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_rsp_get_tx_data_offset *rsp_params;
int err;
@@ -865,7 +865,7 @@ int dpni_set_link_cfg(struct fsl_mc_io *mc_io,
u16 token,
const struct dpni_link_cfg *cfg)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_cmd_set_link_cfg *cmd_params;
/* prepare command */
@@ -894,7 +894,7 @@ int dpni_get_link_state(struct fsl_mc_io *mc_io,
u16 token,
struct dpni_link_state *state)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_rsp_get_link_state *rsp_params;
int err;
@@ -933,7 +933,7 @@ int dpni_set_max_frame_length(struct fsl_mc_io *mc_io,
u16 token,
u16 max_frame_length)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_cmd_set_max_frame_length *cmd_params;
/* prepare command */
@@ -963,7 +963,7 @@ int dpni_get_max_frame_length(struct fsl_mc_io *mc_io,
u16 token,
u16 *max_frame_length)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_rsp_get_max_frame_length *rsp_params;
int err;
@@ -998,7 +998,7 @@ int dpni_set_multicast_promisc(struct fsl_mc_io *mc_io,
u16 token,
int en)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_cmd_set_multicast_promisc *cmd_params;
/* prepare command */
@@ -1026,7 +1026,7 @@ int dpni_get_multicast_promisc(struct fsl_mc_io *mc_io,
u16 token,
int *en)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_rsp_get_multicast_promisc *rsp_params;
int err;
@@ -1061,7 +1061,7 @@ int dpni_set_unicast_promisc(struct fsl_mc_io *mc_io,
u16 token,
int en)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_cmd_set_unicast_promisc *cmd_params;
/* prepare command */
@@ -1089,7 +1089,7 @@ int dpni_get_unicast_promisc(struct fsl_mc_io *mc_io,
u16 token,
int *en)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_rsp_get_unicast_promisc *rsp_params;
int err;
@@ -1124,7 +1124,7 @@ int dpni_set_primary_mac_addr(struct fsl_mc_io *mc_io,
u16 token,
const u8 mac_addr[6])
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_cmd_set_primary_mac_addr *cmd_params;
int i;
@@ -1154,7 +1154,7 @@ int dpni_get_primary_mac_addr(struct fsl_mc_io *mc_io,
u16 token,
u8 mac_addr[6])
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_rsp_get_primary_mac_addr *rsp_params;
int i, err;
@@ -1193,7 +1193,7 @@ int dpni_get_port_mac_addr(struct fsl_mc_io *mc_io,
u16 token,
u8 mac_addr[6])
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_rsp_get_port_mac_addr *rsp_params;
int i, err;
@@ -1229,7 +1229,7 @@ int dpni_add_mac_addr(struct fsl_mc_io *mc_io,
u16 token,
const u8 mac_addr[6])
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_cmd_add_mac_addr *cmd_params;
int i;
@@ -1259,7 +1259,7 @@ int dpni_remove_mac_addr(struct fsl_mc_io *mc_io,
u16 token,
const u8 mac_addr[6])
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_cmd_remove_mac_addr *cmd_params;
int i;
@@ -1293,7 +1293,7 @@ int dpni_clear_mac_filters(struct fsl_mc_io *mc_io,
int unicast,
int multicast)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_cmd_clear_mac_filters *cmd_params;
/* prepare command */
@@ -1327,7 +1327,7 @@ int dpni_set_rx_tc_dist(struct fsl_mc_io *mc_io,
u8 tc_id,
const struct dpni_rx_tc_dist_cfg *cfg)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_cmd_set_rx_tc_dist *cmd_params;
/* prepare command */
@@ -1371,7 +1371,7 @@ int dpni_set_queue(struct fsl_mc_io *mc_io,
u8 options,
const struct dpni_queue *queue)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_cmd_set_queue *cmd_params;
/* prepare command */
@@ -1419,7 +1419,7 @@ int dpni_get_queue(struct fsl_mc_io *mc_io,
struct dpni_queue *queue,
struct dpni_queue_id *qid)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_cmd_get_queue *cmd_params;
struct dpni_rsp_get_queue *rsp_params;
int err;
@@ -1473,7 +1473,7 @@ int dpni_get_statistics(struct fsl_mc_io *mc_io,
u8 page,
union dpni_statistics *stat)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_cmd_get_statistics *cmd_params;
struct dpni_rsp_get_statistics *rsp_params;
int i, err;
@@ -1522,7 +1522,7 @@ int dpni_set_taildrop(struct fsl_mc_io *mc_io,
u8 index,
struct dpni_taildrop *taildrop)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_cmd_set_taildrop *cmd_params;
/* prepare command */
@@ -1566,7 +1566,7 @@ int dpni_get_taildrop(struct fsl_mc_io *mc_io,
u8 index,
struct dpni_taildrop *taildrop)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpni_cmd_get_taildrop *cmd_params;
struct dpni_rsp_get_taildrop *rsp_params;
int err;
@@ -1610,7 +1610,7 @@ int dpni_get_api_version(struct fsl_mc_io *mc_io,
u16 *minor_ver)
{
struct dpni_rsp_get_api_version *rsp_params;
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
int err;
cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_API_VERSION,
diff --git a/drivers/staging/fsl-dpaa2/ethsw/Makefile b/drivers/staging/fsl-dpaa2/ethsw/Makefile
new file mode 100644
index 000000000000..f6f2cf798faf
--- /dev/null
+++ b/drivers/staging/fsl-dpaa2/ethsw/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the Freescale DPAA2 Ethernet Switch
+#
+# Copyright 2014-2017 Freescale Semiconductor Inc.
+# Copyright 2017-2018 NXP
+
+obj-$(CONFIG_FSL_DPAA2_ETHSW) += dpaa2-ethsw.o
+
+dpaa2-ethsw-objs := ethsw.o ethsw-ethtool.o dpsw.o
diff --git a/drivers/staging/fsl-dpaa2/ethsw/README b/drivers/staging/fsl-dpaa2/ethsw/README
new file mode 100644
index 000000000000..f6fc07f780d1
--- /dev/null
+++ b/drivers/staging/fsl-dpaa2/ethsw/README
@@ -0,0 +1,106 @@
+DPAA2 Ethernet Switch driver
+============================
+
+This file provides documentation for the DPAA2 Ethernet Switch driver
+
+
+Contents
+========
+ Supported Platforms
+ Architecture Overview
+ Creating an Ethernet Switch
+ Features
+
+
+ Supported Platforms
+===================
+This driver provides networking support for Freescale LS2085A, LS2088A
+DPAA2 SoCs.
+
+
+Architecture Overview
+=====================
+The Ethernet Switch in the DPAA2 architecture consists of several hardware
+resources that provide the functionality. These are allocated and
+configured via the Management Complex (MC) portals. MC abstracts most of
+these resources as DPAA2 objects and exposes ABIs through which they can
+be configured and controlled.
+
+For a more detailed description of the DPAA2 architecture and its object
+abstractions see:
+ drivers/staging/fsl-mc/README.txt
+
+The Ethernet Switch is built on top of a Datapath Switch (DPSW) object.
+
+Configuration interface:
+
+ ---------------------
+ | DPAA2 Switch driver |
+ ---------------------
+ .
+ .
+ ----------
+ | DPSW API |
+ ----------
+ . software
+ ================= . ==============
+ . hardware
+ ---------------------
+ | MC hardware portals |
+ ---------------------
+ .
+ .
+ ------
+ | DPSW |
+ ------
+
+Driver uses the switch device driver model and exposes each switch port as
+a network interface, which can be included in a bridge. Traffic switched
+between ports is offloaded into the hardware. Exposed network interfaces
+are not used for I/O, they are used just for configuration. This
+limitation is going to be addressed in the future.
+
+The DPSW can have ports connected to DPNIs or to PHYs via DPMACs.
+
+
+ [ethA] [ethB] [ethC] [ethD] [ethE] [ethF]
+ : : : : : :
+ : : : : : :
+[eth drv] [eth drv] [ ethsw drv ]
+ : : : : : : kernel
+========================================================================
+ : : : : : : hardware
+ [DPNI] [DPNI] [============= DPSW =================]
+ | | | | | |
+ | ---------- | [DPMAC] [DPMAC]
+ ------------------------------- | |
+ | |
+ [PHY] [PHY]
+
+For a more detailed description of the Ethernet switch device driver model
+see:
+ Documentation/networking/switchdev.txt
+
+Creating an Ethernet Switch
+===========================
+A device is created for the switch objects probed on the MC bus. Each DPSW
+has a number of properties which determine the configuration options and
+associated hardware resources.
+
+A DPSW object (and the other DPAA2 objects needed for a DPAA2 switch) can
+be added to a container on the MC bus in one of two ways: statically,
+through a Datapath Layout Binary file (DPL) that is parsed by MC at boot
+time; or created dynamically at runtime, via the DPAA2 objects APIs.
+
+Features
+========
+Driver configures DPSW to perform hardware switching offload of
+unicast/multicast/broadcast (VLAN tagged or untagged) traffic between its
+ports.
+
+It allows configuration of hardware learning, flooding, multicast groups,
+port VLAN configuration and STP state.
+
+Static entries can be added/removed from the FDB.
+
+Hardware statistics for each port are provided through ethtool -S option.
diff --git a/drivers/staging/fsl-dpaa2/ethsw/TODO b/drivers/staging/fsl-dpaa2/ethsw/TODO
new file mode 100644
index 000000000000..24b5e95a96f8
--- /dev/null
+++ b/drivers/staging/fsl-dpaa2/ethsw/TODO
@@ -0,0 +1,14 @@
+* Add I/O capabilities on switch port netdevices. This will allow control
+traffic to reach the CPU.
+* Add ACL to redirect control traffic to CPU.
+* Add support for displaying learned FDB entries
+* Add support for multiple FDBs and switch port partitioning
+* MC firmware uprev; the DPAA2 objects used by the Ethernet Switch driver
+need to be kept in sync with binary interface changes in MC
+* refine README file
+* cleanup
+
+NOTE: At least first three of the above are required before getting the
+DPAA2 Ethernet Switch driver out of staging. Another requirement is that
+dpio driver is moved to drivers/soc (this is required for I/O).
+
diff --git a/drivers/staging/fsl-dpaa2/ethsw/dpsw-cmd.h b/drivers/staging/fsl-dpaa2/ethsw/dpsw-cmd.h
new file mode 100644
index 000000000000..1c203e6e8035
--- /dev/null
+++ b/drivers/staging/fsl-dpaa2/ethsw/dpsw-cmd.h
@@ -0,0 +1,346 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2014-2016 Freescale Semiconductor Inc.
+ * Copyright 2017-2018 NXP
+ *
+ */
+
+#ifndef __FSL_DPSW_CMD_H
+#define __FSL_DPSW_CMD_H
+
+/* DPSW Version */
+#define DPSW_VER_MAJOR 8
+#define DPSW_VER_MINOR 0
+
+#define DPSW_CMD_BASE_VERSION 1
+#define DPSW_CMD_ID_OFFSET 4
+
+#define DPSW_CMD_ID(id) (((id) << DPSW_CMD_ID_OFFSET) | DPSW_CMD_BASE_VERSION)
+
+/* Command IDs */
+#define DPSW_CMDID_CLOSE DPSW_CMD_ID(0x800)
+#define DPSW_CMDID_OPEN DPSW_CMD_ID(0x802)
+
+#define DPSW_CMDID_GET_API_VERSION DPSW_CMD_ID(0xa02)
+
+#define DPSW_CMDID_ENABLE DPSW_CMD_ID(0x002)
+#define DPSW_CMDID_DISABLE DPSW_CMD_ID(0x003)
+#define DPSW_CMDID_GET_ATTR DPSW_CMD_ID(0x004)
+#define DPSW_CMDID_RESET DPSW_CMD_ID(0x005)
+
+#define DPSW_CMDID_SET_IRQ_ENABLE DPSW_CMD_ID(0x012)
+
+#define DPSW_CMDID_SET_IRQ_MASK DPSW_CMD_ID(0x014)
+
+#define DPSW_CMDID_GET_IRQ_STATUS DPSW_CMD_ID(0x016)
+#define DPSW_CMDID_CLEAR_IRQ_STATUS DPSW_CMD_ID(0x017)
+
+#define DPSW_CMDID_IF_SET_TCI DPSW_CMD_ID(0x030)
+#define DPSW_CMDID_IF_SET_STP DPSW_CMD_ID(0x031)
+
+#define DPSW_CMDID_IF_GET_COUNTER DPSW_CMD_ID(0x034)
+
+#define DPSW_CMDID_IF_ENABLE DPSW_CMD_ID(0x03D)
+#define DPSW_CMDID_IF_DISABLE DPSW_CMD_ID(0x03E)
+
+#define DPSW_CMDID_IF_SET_MAX_FRAME_LENGTH DPSW_CMD_ID(0x044)
+
+#define DPSW_CMDID_IF_GET_LINK_STATE DPSW_CMD_ID(0x046)
+#define DPSW_CMDID_IF_SET_FLOODING DPSW_CMD_ID(0x047)
+#define DPSW_CMDID_IF_SET_BROADCAST DPSW_CMD_ID(0x048)
+
+#define DPSW_CMDID_IF_SET_LINK_CFG DPSW_CMD_ID(0x04C)
+
+#define DPSW_CMDID_VLAN_ADD DPSW_CMD_ID(0x060)
+#define DPSW_CMDID_VLAN_ADD_IF DPSW_CMD_ID(0x061)
+#define DPSW_CMDID_VLAN_ADD_IF_UNTAGGED DPSW_CMD_ID(0x062)
+
+#define DPSW_CMDID_VLAN_REMOVE_IF DPSW_CMD_ID(0x064)
+#define DPSW_CMDID_VLAN_REMOVE_IF_UNTAGGED DPSW_CMD_ID(0x065)
+#define DPSW_CMDID_VLAN_REMOVE_IF_FLOODING DPSW_CMD_ID(0x066)
+#define DPSW_CMDID_VLAN_REMOVE DPSW_CMD_ID(0x067)
+
+#define DPSW_CMDID_FDB_ADD_UNICAST DPSW_CMD_ID(0x084)
+#define DPSW_CMDID_FDB_REMOVE_UNICAST DPSW_CMD_ID(0x085)
+#define DPSW_CMDID_FDB_ADD_MULTICAST DPSW_CMD_ID(0x086)
+#define DPSW_CMDID_FDB_REMOVE_MULTICAST DPSW_CMD_ID(0x087)
+#define DPSW_CMDID_FDB_SET_LEARNING_MODE DPSW_CMD_ID(0x088)
+
+/* Macros for accessing command fields smaller than 1byte */
+#define DPSW_MASK(field) \
+ GENMASK(DPSW_##field##_SHIFT + DPSW_##field##_SIZE - 1, \
+ DPSW_##field##_SHIFT)
+#define dpsw_set_field(var, field, val) \
+ ((var) |= (((val) << DPSW_##field##_SHIFT) & DPSW_MASK(field)))
+#define dpsw_get_field(var, field) \
+ (((var) & DPSW_MASK(field)) >> DPSW_##field##_SHIFT)
+#define dpsw_get_bit(var, bit) \
+ (((var) >> (bit)) & GENMASK(0, 0))
+
+struct dpsw_cmd_open {
+ __le32 dpsw_id;
+};
+
+#define DPSW_COMPONENT_TYPE_SHIFT 0
+#define DPSW_COMPONENT_TYPE_SIZE 4
+
+struct dpsw_cmd_create {
+ /* cmd word 0 */
+ __le16 num_ifs;
+ u8 max_fdbs;
+ u8 max_meters_per_if;
+ /* from LSB: only the first 4 bits */
+ u8 component_type;
+ u8 pad[3];
+ /* cmd word 1 */
+ __le16 max_vlans;
+ __le16 max_fdb_entries;
+ __le16 fdb_aging_time;
+ __le16 max_fdb_mc_groups;
+ /* cmd word 2 */
+ __le64 options;
+};
+
+struct dpsw_cmd_destroy {
+ __le32 dpsw_id;
+};
+
+#define DPSW_ENABLE_SHIFT 0
+#define DPSW_ENABLE_SIZE 1
+
+struct dpsw_rsp_is_enabled {
+ /* from LSB: enable:1 */
+ u8 enabled;
+};
+
+struct dpsw_cmd_set_irq_enable {
+ u8 enable_state;
+ u8 pad[3];
+ u8 irq_index;
+};
+
+struct dpsw_cmd_get_irq_enable {
+ __le32 pad;
+ u8 irq_index;
+};
+
+struct dpsw_rsp_get_irq_enable {
+ u8 enable_state;
+};
+
+struct dpsw_cmd_set_irq_mask {
+ __le32 mask;
+ u8 irq_index;
+};
+
+struct dpsw_cmd_get_irq_mask {
+ __le32 pad;
+ u8 irq_index;
+};
+
+struct dpsw_rsp_get_irq_mask {
+ __le32 mask;
+};
+
+struct dpsw_cmd_get_irq_status {
+ __le32 status;
+ u8 irq_index;
+};
+
+struct dpsw_rsp_get_irq_status {
+ __le32 status;
+};
+
+struct dpsw_cmd_clear_irq_status {
+ __le32 status;
+ u8 irq_index;
+};
+
+#define DPSW_COMPONENT_TYPE_SHIFT 0
+#define DPSW_COMPONENT_TYPE_SIZE 4
+
+struct dpsw_rsp_get_attr {
+ /* cmd word 0 */
+ __le16 num_ifs;
+ u8 max_fdbs;
+ u8 num_fdbs;
+ __le16 max_vlans;
+ __le16 num_vlans;
+ /* cmd word 1 */
+ __le16 max_fdb_entries;
+ __le16 fdb_aging_time;
+ __le32 dpsw_id;
+ /* cmd word 2 */
+ __le16 mem_size;
+ __le16 max_fdb_mc_groups;
+ u8 max_meters_per_if;
+ /* from LSB only the first 4 bits */
+ u8 component_type;
+ __le16 pad;
+ /* cmd word 3 */
+ __le64 options;
+};
+
+struct dpsw_cmd_if_set_flooding {
+ __le16 if_id;
+ /* from LSB: enable:1 */
+ u8 enable;
+};
+
+struct dpsw_cmd_if_set_broadcast {
+ __le16 if_id;
+ /* from LSB: enable:1 */
+ u8 enable;
+};
+
+#define DPSW_VLAN_ID_SHIFT 0
+#define DPSW_VLAN_ID_SIZE 12
+#define DPSW_DEI_SHIFT 12
+#define DPSW_DEI_SIZE 1
+#define DPSW_PCP_SHIFT 13
+#define DPSW_PCP_SIZE 3
+
+struct dpsw_cmd_if_set_tci {
+ __le16 if_id;
+ /* from LSB: VLAN_ID:12 DEI:1 PCP:3 */
+ __le16 conf;
+};
+
+#define DPSW_STATE_SHIFT 0
+#define DPSW_STATE_SIZE 4
+
+struct dpsw_cmd_if_set_stp {
+ __le16 if_id;
+ __le16 vlan_id;
+ /* only the first LSB 4 bits */
+ u8 state;
+};
+
+#define DPSW_COUNTER_TYPE_SHIFT 0
+#define DPSW_COUNTER_TYPE_SIZE 5
+
+struct dpsw_cmd_if_get_counter {
+ __le16 if_id;
+ /* from LSB: type:5 */
+ u8 type;
+};
+
+struct dpsw_rsp_if_get_counter {
+ __le64 pad;
+ __le64 counter;
+};
+
+struct dpsw_cmd_if {
+ __le16 if_id;
+};
+
+struct dpsw_cmd_if_set_max_frame_length {
+ __le16 if_id;
+ __le16 frame_length;
+};
+
+struct dpsw_cmd_if_set_link_cfg {
+ /* cmd word 0 */
+ __le16 if_id;
+ u8 pad[6];
+ /* cmd word 1 */
+ __le32 rate;
+ __le32 pad1;
+ /* cmd word 2 */
+ __le64 options;
+};
+
+struct dpsw_cmd_if_get_link_state {
+ __le16 if_id;
+};
+
+#define DPSW_UP_SHIFT 0
+#define DPSW_UP_SIZE 1
+
+struct dpsw_rsp_if_get_link_state {
+ /* cmd word 0 */
+ __le32 pad0;
+ u8 up;
+ u8 pad1[3];
+ /* cmd word 1 */
+ __le32 rate;
+ __le32 pad2;
+ /* cmd word 2 */
+ __le64 options;
+};
+
+struct dpsw_vlan_add {
+ __le16 fdb_id;
+ __le16 vlan_id;
+};
+
+struct dpsw_cmd_vlan_manage_if {
+ /* cmd word 0 */
+ __le16 pad0;
+ __le16 vlan_id;
+ __le32 pad1;
+ /* cmd word 1-4 */
+ __le64 if_id[4];
+};
+
+struct dpsw_cmd_vlan_remove {
+ __le16 pad;
+ __le16 vlan_id;
+};
+
+struct dpsw_cmd_fdb_add {
+ __le32 pad;
+ __le16 fdb_aging_time;
+ __le16 num_fdb_entries;
+};
+
+struct dpsw_rsp_fdb_add {
+ __le16 fdb_id;
+};
+
+struct dpsw_cmd_fdb_remove {
+ __le16 fdb_id;
+};
+
+#define DPSW_ENTRY_TYPE_SHIFT 0
+#define DPSW_ENTRY_TYPE_SIZE 4
+
+struct dpsw_cmd_fdb_unicast_op {
+ /* cmd word 0 */
+ __le16 fdb_id;
+ u8 mac_addr[6];
+ /* cmd word 1 */
+ __le16 if_egress;
+ /* only the first 4 bits from LSB */
+ u8 type;
+};
+
+struct dpsw_cmd_fdb_multicast_op {
+ /* cmd word 0 */
+ __le16 fdb_id;
+ __le16 num_ifs;
+ /* only the first 4 bits from LSB */
+ u8 type;
+ u8 pad[3];
+ /* cmd word 1 */
+ u8 mac_addr[6];
+ __le16 pad2;
+ /* cmd word 2-5 */
+ __le64 if_id[4];
+};
+
+#define DPSW_LEARNING_MODE_SHIFT 0
+#define DPSW_LEARNING_MODE_SIZE 4
+
+struct dpsw_cmd_fdb_set_learning_mode {
+ __le16 fdb_id;
+ /* only the first 4 bits from LSB */
+ u8 mode;
+};
+
+struct dpsw_rsp_get_api_version {
+ __le16 version_major;
+ __le16 version_minor;
+};
+
+#endif /* __FSL_DPSW_CMD_H */
diff --git a/drivers/staging/fsl-dpaa2/ethsw/dpsw.c b/drivers/staging/fsl-dpaa2/ethsw/dpsw.c
new file mode 100644
index 000000000000..9b9bc604b461
--- /dev/null
+++ b/drivers/staging/fsl-dpaa2/ethsw/dpsw.c
@@ -0,0 +1,1123 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2014-2016 Freescale Semiconductor Inc.
+ * Copyright 2017-2018 NXP
+ *
+ */
+
+#include <linux/fsl/mc.h>
+#include "dpsw.h"
+#include "dpsw-cmd.h"
+
+static void build_if_id_bitmap(__le64 *bmap,
+ const u16 *id,
+ const u16 num_ifs)
+{
+ int i;
+
+ for (i = 0; (i < num_ifs) && (i < DPSW_MAX_IF); i++) {
+ if (id[i] < DPSW_MAX_IF)
+ bmap[id[i] / 64] |= cpu_to_le64(BIT_MASK(id[i] % 64));
+ }
+}
+
+/**
+ * dpsw_open() - Open a control session for the specified object
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @dpsw_id: DPSW unique ID
+ * @token: Returned token; use in subsequent API calls
+ *
+ * This function can be used to open a control session for an
+ * already created object; an object may have been declared in
+ * the DPL or by calling the dpsw_create() function.
+ * This function returns a unique authentication token,
+ * associated with the specific object ID and the specific MC
+ * portal; this token must be used in all subsequent commands for
+ * this specific object
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpsw_open(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ int dpsw_id,
+ u16 *token)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpsw_cmd_open *cmd_params;
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_OPEN,
+ cmd_flags,
+ 0);
+ cmd_params = (struct dpsw_cmd_open *)cmd.params;
+ cmd_params->dpsw_id = cpu_to_le32(dpsw_id);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *token = mc_cmd_hdr_read_token(&cmd);
+
+ return 0;
+}
+
+/**
+ * dpsw_close() - Close the control session of the object
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ *
+ * After this function is called, no further operations are
+ * allowed on the object without opening a new control session.
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpsw_close(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token)
+{
+ struct fsl_mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_CLOSE,
+ cmd_flags,
+ token);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpsw_enable() - Enable DPSW functionality
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ *
+ * Return: Completion status. '0' on Success; Error code otherwise.
+ */
+int dpsw_enable(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token)
+{
+ struct fsl_mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_ENABLE,
+ cmd_flags,
+ token);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpsw_disable() - Disable DPSW functionality
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ *
+ * Return: Completion status. '0' on Success; Error code otherwise.
+ */
+int dpsw_disable(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token)
+{
+ struct fsl_mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_DISABLE,
+ cmd_flags,
+ token);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpsw_reset() - Reset the DPSW, returns the object to initial state.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpsw_reset(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token)
+{
+ struct fsl_mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_RESET,
+ cmd_flags,
+ token);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpsw_set_irq_enable() - Set overall interrupt state.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPCI object
+ * @irq_index: The interrupt index to configure
+ * @en: Interrupt state - enable = 1, disable = 0
+ *
+ * Allows GPP software to control when interrupts are generated.
+ * Each interrupt can have up to 32 causes. The enable/disable control's the
+ * overall interrupt state. if the interrupt is disabled no causes will cause
+ * an interrupt
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpsw_set_irq_enable(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u8 irq_index,
+ u8 en)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpsw_cmd_set_irq_enable *cmd_params;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_SET_IRQ_ENABLE,
+ cmd_flags,
+ token);
+ cmd_params = (struct dpsw_cmd_set_irq_enable *)cmd.params;
+ dpsw_set_field(cmd_params->enable_state, ENABLE, en);
+ cmd_params->irq_index = irq_index;
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpsw_set_irq_mask() - Set interrupt mask.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPCI object
+ * @irq_index: The interrupt index to configure
+ * @mask: Event mask to trigger interrupt;
+ * each bit:
+ * 0 = ignore event
+ * 1 = consider event for asserting IRQ
+ *
+ * Every interrupt can have up to 32 causes and the interrupt model supports
+ * masking/unmasking each cause independently
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpsw_set_irq_mask(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u8 irq_index,
+ u32 mask)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpsw_cmd_set_irq_mask *cmd_params;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_SET_IRQ_MASK,
+ cmd_flags,
+ token);
+ cmd_params = (struct dpsw_cmd_set_irq_mask *)cmd.params;
+ cmd_params->mask = cpu_to_le32(mask);
+ cmd_params->irq_index = irq_index;
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpsw_get_irq_status() - Get the current status of any pending interrupts
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ * @irq_index: The interrupt index to configure
+ * @status: Returned interrupts status - one bit per cause:
+ * 0 = no interrupt pending
+ * 1 = interrupt pending
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpsw_get_irq_status(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u8 irq_index,
+ u32 *status)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpsw_cmd_get_irq_status *cmd_params;
+ struct dpsw_rsp_get_irq_status *rsp_params;
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_GET_IRQ_STATUS,
+ cmd_flags,
+ token);
+ cmd_params = (struct dpsw_cmd_get_irq_status *)cmd.params;
+ cmd_params->status = cpu_to_le32(*status);
+ cmd_params->irq_index = irq_index;
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ rsp_params = (struct dpsw_rsp_get_irq_status *)cmd.params;
+ *status = le32_to_cpu(rsp_params->status);
+
+ return 0;
+}
+
+/**
+ * dpsw_clear_irq_status() - Clear a pending interrupt's status
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPCI object
+ * @irq_index: The interrupt index to configure
+ * @status: bits to clear (W1C) - one bit per cause:
+ * 0 = don't change
+ * 1 = clear status bit
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpsw_clear_irq_status(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u8 irq_index,
+ u32 status)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpsw_cmd_clear_irq_status *cmd_params;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_CLEAR_IRQ_STATUS,
+ cmd_flags,
+ token);
+ cmd_params = (struct dpsw_cmd_clear_irq_status *)cmd.params;
+ cmd_params->status = cpu_to_le32(status);
+ cmd_params->irq_index = irq_index;
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpsw_get_attributes() - Retrieve DPSW attributes
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ * @attr: Returned DPSW attributes
+ *
+ * Return: Completion status. '0' on Success; Error code otherwise.
+ */
+int dpsw_get_attributes(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ struct dpsw_attr *attr)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpsw_rsp_get_attr *rsp_params;
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_GET_ATTR,
+ cmd_flags,
+ token);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ rsp_params = (struct dpsw_rsp_get_attr *)cmd.params;
+ attr->num_ifs = le16_to_cpu(rsp_params->num_ifs);
+ attr->max_fdbs = rsp_params->max_fdbs;
+ attr->num_fdbs = rsp_params->num_fdbs;
+ attr->max_vlans = le16_to_cpu(rsp_params->max_vlans);
+ attr->num_vlans = le16_to_cpu(rsp_params->num_vlans);
+ attr->max_fdb_entries = le16_to_cpu(rsp_params->max_fdb_entries);
+ attr->fdb_aging_time = le16_to_cpu(rsp_params->fdb_aging_time);
+ attr->id = le32_to_cpu(rsp_params->dpsw_id);
+ attr->mem_size = le16_to_cpu(rsp_params->mem_size);
+ attr->max_fdb_mc_groups = le16_to_cpu(rsp_params->max_fdb_mc_groups);
+ attr->max_meters_per_if = rsp_params->max_meters_per_if;
+ attr->options = le64_to_cpu(rsp_params->options);
+ attr->component_type = dpsw_get_field(rsp_params->component_type,
+ COMPONENT_TYPE);
+
+ return 0;
+}
+
+/**
+ * dpsw_if_set_link_cfg() - Set the link configuration.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ * @if_id: Interface id
+ * @cfg: Link configuration
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpsw_if_set_link_cfg(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 if_id,
+ struct dpsw_link_cfg *cfg)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpsw_cmd_if_set_link_cfg *cmd_params;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_IF_SET_LINK_CFG,
+ cmd_flags,
+ token);
+ cmd_params = (struct dpsw_cmd_if_set_link_cfg *)cmd.params;
+ cmd_params->if_id = cpu_to_le16(if_id);
+ cmd_params->rate = cpu_to_le32(cfg->rate);
+ cmd_params->options = cpu_to_le64(cfg->options);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpsw_if_get_link_state - Return the link state
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ * @if_id: Interface id
+ * @state: Link state 1 - linkup, 0 - link down or disconnected
+ *
+ * @Return '0' on Success; Error code otherwise.
+ */
+int dpsw_if_get_link_state(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 if_id,
+ struct dpsw_link_state *state)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpsw_cmd_if_get_link_state *cmd_params;
+ struct dpsw_rsp_if_get_link_state *rsp_params;
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_IF_GET_LINK_STATE,
+ cmd_flags,
+ token);
+ cmd_params = (struct dpsw_cmd_if_get_link_state *)cmd.params;
+ cmd_params->if_id = cpu_to_le16(if_id);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ rsp_params = (struct dpsw_rsp_if_get_link_state *)cmd.params;
+ state->rate = le32_to_cpu(rsp_params->rate);
+ state->options = le64_to_cpu(rsp_params->options);
+ state->up = dpsw_get_field(rsp_params->up, UP);
+
+ return 0;
+}
+
+/**
+ * dpsw_if_set_flooding() - Enable Disable flooding for particular interface
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ * @if_id: Interface Identifier
+ * @en: 1 - enable, 0 - disable
+ *
+ * Return: Completion status. '0' on Success; Error code otherwise.
+ */
+int dpsw_if_set_flooding(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 if_id,
+ u8 en)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpsw_cmd_if_set_flooding *cmd_params;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_IF_SET_FLOODING,
+ cmd_flags,
+ token);
+ cmd_params = (struct dpsw_cmd_if_set_flooding *)cmd.params;
+ cmd_params->if_id = cpu_to_le16(if_id);
+ dpsw_set_field(cmd_params->enable, ENABLE, en);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpsw_if_set_broadcast() - Enable/disable broadcast for particular interface
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ * @if_id: Interface Identifier
+ * @en: 1 - enable, 0 - disable
+ *
+ * Return: Completion status. '0' on Success; Error code otherwise.
+ */
+int dpsw_if_set_broadcast(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 if_id,
+ u8 en)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpsw_cmd_if_set_broadcast *cmd_params;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_IF_SET_BROADCAST,
+ cmd_flags,
+ token);
+ cmd_params = (struct dpsw_cmd_if_set_broadcast *)cmd.params;
+ cmd_params->if_id = cpu_to_le16(if_id);
+ dpsw_set_field(cmd_params->enable, ENABLE, en);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpsw_if_set_tci() - Set default VLAN Tag Control Information (TCI)
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ * @if_id: Interface Identifier
+ * @cfg: Tag Control Information Configuration
+ *
+ * Return: Completion status. '0' on Success; Error code otherwise.
+ */
+int dpsw_if_set_tci(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 if_id,
+ const struct dpsw_tci_cfg *cfg)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpsw_cmd_if_set_tci *cmd_params;
+ u16 tmp_conf = 0;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_IF_SET_TCI,
+ cmd_flags,
+ token);
+ cmd_params = (struct dpsw_cmd_if_set_tci *)cmd.params;
+ cmd_params->if_id = cpu_to_le16(if_id);
+ dpsw_set_field(tmp_conf, VLAN_ID, cfg->vlan_id);
+ dpsw_set_field(tmp_conf, DEI, cfg->dei);
+ dpsw_set_field(tmp_conf, PCP, cfg->pcp);
+ cmd_params->conf = cpu_to_le16(tmp_conf);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpsw_if_set_stp() - Function sets Spanning Tree Protocol (STP) state.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ * @if_id: Interface Identifier
+ * @cfg: STP State configuration parameters
+ *
+ * The following STP states are supported -
+ * blocking, listening, learning, forwarding and disabled.
+ *
+ * Return: Completion status. '0' on Success; Error code otherwise.
+ */
+int dpsw_if_set_stp(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 if_id,
+ const struct dpsw_stp_cfg *cfg)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpsw_cmd_if_set_stp *cmd_params;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_IF_SET_STP,
+ cmd_flags,
+ token);
+ cmd_params = (struct dpsw_cmd_if_set_stp *)cmd.params;
+ cmd_params->if_id = cpu_to_le16(if_id);
+ cmd_params->vlan_id = cpu_to_le16(cfg->vlan_id);
+ dpsw_set_field(cmd_params->state, STATE, cfg->state);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpsw_if_get_counter() - Get specific counter of particular interface
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ * @if_id: Interface Identifier
+ * @type: Counter type
+ * @counter: return value
+ *
+ * Return: Completion status. '0' on Success; Error code otherwise.
+ */
+int dpsw_if_get_counter(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 if_id,
+ enum dpsw_counter type,
+ u64 *counter)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpsw_cmd_if_get_counter *cmd_params;
+ struct dpsw_rsp_if_get_counter *rsp_params;
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_IF_GET_COUNTER,
+ cmd_flags,
+ token);
+ cmd_params = (struct dpsw_cmd_if_get_counter *)cmd.params;
+ cmd_params->if_id = cpu_to_le16(if_id);
+ dpsw_set_field(cmd_params->type, COUNTER_TYPE, type);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ rsp_params = (struct dpsw_rsp_if_get_counter *)cmd.params;
+ *counter = le64_to_cpu(rsp_params->counter);
+
+ return 0;
+}
+
+/**
+ * dpsw_if_enable() - Enable Interface
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ * @if_id: Interface Identifier
+ *
+ * Return: Completion status. '0' on Success; Error code otherwise.
+ */
+int dpsw_if_enable(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 if_id)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpsw_cmd_if *cmd_params;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_IF_ENABLE,
+ cmd_flags,
+ token);
+ cmd_params = (struct dpsw_cmd_if *)cmd.params;
+ cmd_params->if_id = cpu_to_le16(if_id);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpsw_if_disable() - Disable Interface
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ * @if_id: Interface Identifier
+ *
+ * Return: Completion status. '0' on Success; Error code otherwise.
+ */
+int dpsw_if_disable(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 if_id)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpsw_cmd_if *cmd_params;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_IF_DISABLE,
+ cmd_flags,
+ token);
+ cmd_params = (struct dpsw_cmd_if *)cmd.params;
+ cmd_params->if_id = cpu_to_le16(if_id);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpsw_if_set_max_frame_length() - Set Maximum Receive frame length.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ * @if_id: Interface Identifier
+ * @frame_length: Maximum Frame Length
+ *
+ * Return: Completion status. '0' on Success; Error code otherwise.
+ */
+int dpsw_if_set_max_frame_length(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 if_id,
+ u16 frame_length)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpsw_cmd_if_set_max_frame_length *cmd_params;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_IF_SET_MAX_FRAME_LENGTH,
+ cmd_flags,
+ token);
+ cmd_params = (struct dpsw_cmd_if_set_max_frame_length *)cmd.params;
+ cmd_params->if_id = cpu_to_le16(if_id);
+ cmd_params->frame_length = cpu_to_le16(frame_length);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpsw_vlan_add() - Adding new VLAN to DPSW.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ * @vlan_id: VLAN Identifier
+ * @cfg: VLAN configuration
+ *
+ * Only VLAN ID and FDB ID are required parameters here.
+ * 12 bit VLAN ID is defined in IEEE802.1Q.
+ * Adding a duplicate VLAN ID is not allowed.
+ * FDB ID can be shared across multiple VLANs. Shared learning
+ * is obtained by calling dpsw_vlan_add for multiple VLAN IDs
+ * with same fdb_id
+ *
+ * Return: Completion status. '0' on Success; Error code otherwise.
+ */
+int dpsw_vlan_add(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 vlan_id,
+ const struct dpsw_vlan_cfg *cfg)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpsw_vlan_add *cmd_params;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_VLAN_ADD,
+ cmd_flags,
+ token);
+ cmd_params = (struct dpsw_vlan_add *)cmd.params;
+ cmd_params->fdb_id = cpu_to_le16(cfg->fdb_id);
+ cmd_params->vlan_id = cpu_to_le16(vlan_id);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpsw_vlan_add_if() - Adding a set of interfaces to an existing VLAN.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ * @vlan_id: VLAN Identifier
+ * @cfg: Set of interfaces to add
+ *
+ * It adds only interfaces not belonging to this VLAN yet,
+ * otherwise an error is generated and an entire command is
+ * ignored. This function can be called numerous times always
+ * providing required interfaces delta.
+ *
+ * Return: Completion status. '0' on Success; Error code otherwise.
+ */
+int dpsw_vlan_add_if(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 vlan_id,
+ const struct dpsw_vlan_if_cfg *cfg)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpsw_cmd_vlan_manage_if *cmd_params;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_VLAN_ADD_IF,
+ cmd_flags,
+ token);
+ cmd_params = (struct dpsw_cmd_vlan_manage_if *)cmd.params;
+ cmd_params->vlan_id = cpu_to_le16(vlan_id);
+ build_if_id_bitmap(cmd_params->if_id, cfg->if_id, cfg->num_ifs);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpsw_vlan_add_if_untagged() - Defining a set of interfaces that should be
+ * transmitted as untagged.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ * @vlan_id: VLAN Identifier
+ * @cfg: Set of interfaces that should be transmitted as untagged
+ *
+ * These interfaces should already belong to this VLAN.
+ * By default all interfaces are transmitted as tagged.
+ * Providing un-existing interface or untagged interface that is
+ * configured untagged already generates an error and the entire
+ * command is ignored.
+ *
+ * Return: Completion status. '0' on Success; Error code otherwise.
+ */
+int dpsw_vlan_add_if_untagged(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 vlan_id,
+ const struct dpsw_vlan_if_cfg *cfg)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpsw_cmd_vlan_manage_if *cmd_params;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_VLAN_ADD_IF_UNTAGGED,
+ cmd_flags,
+ token);
+ cmd_params = (struct dpsw_cmd_vlan_manage_if *)cmd.params;
+ cmd_params->vlan_id = cpu_to_le16(vlan_id);
+ build_if_id_bitmap(cmd_params->if_id, cfg->if_id, cfg->num_ifs);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpsw_vlan_remove_if() - Remove interfaces from an existing VLAN.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ * @vlan_id: VLAN Identifier
+ * @cfg: Set of interfaces that should be removed
+ *
+ * Interfaces must belong to this VLAN, otherwise an error
+ * is returned and an the command is ignored
+ *
+ * Return: Completion status. '0' on Success; Error code otherwise.
+ */
+int dpsw_vlan_remove_if(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 vlan_id,
+ const struct dpsw_vlan_if_cfg *cfg)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpsw_cmd_vlan_manage_if *cmd_params;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_VLAN_REMOVE_IF,
+ cmd_flags,
+ token);
+ cmd_params = (struct dpsw_cmd_vlan_manage_if *)cmd.params;
+ cmd_params->vlan_id = cpu_to_le16(vlan_id);
+ build_if_id_bitmap(cmd_params->if_id, cfg->if_id, cfg->num_ifs);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpsw_vlan_remove_if_untagged() - Define a set of interfaces that should be
+ * converted from transmitted as untagged to transmit as tagged.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ * @vlan_id: VLAN Identifier
+ * @cfg: Set of interfaces that should be removed
+ *
+ * Interfaces provided by API have to belong to this VLAN and
+ * configured untagged, otherwise an error is returned and the
+ * command is ignored
+ *
+ * Return: Completion status. '0' on Success; Error code otherwise.
+ */
+int dpsw_vlan_remove_if_untagged(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 vlan_id,
+ const struct dpsw_vlan_if_cfg *cfg)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpsw_cmd_vlan_manage_if *cmd_params;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_VLAN_REMOVE_IF_UNTAGGED,
+ cmd_flags,
+ token);
+ cmd_params = (struct dpsw_cmd_vlan_manage_if *)cmd.params;
+ cmd_params->vlan_id = cpu_to_le16(vlan_id);
+ build_if_id_bitmap(cmd_params->if_id, cfg->if_id, cfg->num_ifs);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpsw_vlan_remove() - Remove an entire VLAN
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ * @vlan_id: VLAN Identifier
+ *
+ * Return: Completion status. '0' on Success; Error code otherwise.
+ */
+int dpsw_vlan_remove(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 vlan_id)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpsw_cmd_vlan_remove *cmd_params;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_VLAN_REMOVE,
+ cmd_flags,
+ token);
+ cmd_params = (struct dpsw_cmd_vlan_remove *)cmd.params;
+ cmd_params->vlan_id = cpu_to_le16(vlan_id);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpsw_fdb_add_unicast() - Function adds an unicast entry into MAC lookup table
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ * @fdb_id: Forwarding Database Identifier
+ * @cfg: Unicast entry configuration
+ *
+ * Return: Completion status. '0' on Success; Error code otherwise.
+ */
+int dpsw_fdb_add_unicast(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 fdb_id,
+ const struct dpsw_fdb_unicast_cfg *cfg)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpsw_cmd_fdb_unicast_op *cmd_params;
+ int i;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_FDB_ADD_UNICAST,
+ cmd_flags,
+ token);
+ cmd_params = (struct dpsw_cmd_fdb_unicast_op *)cmd.params;
+ cmd_params->fdb_id = cpu_to_le16(fdb_id);
+ cmd_params->if_egress = cpu_to_le16(cfg->if_egress);
+ for (i = 0; i < 6; i++)
+ cmd_params->mac_addr[i] = cfg->mac_addr[5 - i];
+ dpsw_set_field(cmd_params->type, ENTRY_TYPE, cfg->type);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpsw_fdb_remove_unicast() - removes an entry from MAC lookup table
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ * @fdb_id: Forwarding Database Identifier
+ * @cfg: Unicast entry configuration
+ *
+ * Return: Completion status. '0' on Success; Error code otherwise.
+ */
+int dpsw_fdb_remove_unicast(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 fdb_id,
+ const struct dpsw_fdb_unicast_cfg *cfg)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpsw_cmd_fdb_unicast_op *cmd_params;
+ int i;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_FDB_REMOVE_UNICAST,
+ cmd_flags,
+ token);
+ cmd_params = (struct dpsw_cmd_fdb_unicast_op *)cmd.params;
+ cmd_params->fdb_id = cpu_to_le16(fdb_id);
+ for (i = 0; i < 6; i++)
+ cmd_params->mac_addr[i] = cfg->mac_addr[5 - i];
+ cmd_params->if_egress = cpu_to_le16(cfg->if_egress);
+ dpsw_set_field(cmd_params->type, ENTRY_TYPE, cfg->type);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpsw_fdb_add_multicast() - Add a set of egress interfaces to multi-cast group
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ * @fdb_id: Forwarding Database Identifier
+ * @cfg: Multicast entry configuration
+ *
+ * If group doesn't exist, it will be created.
+ * It adds only interfaces not belonging to this multicast group
+ * yet, otherwise error will be generated and the command is
+ * ignored.
+ * This function may be called numerous times always providing
+ * required interfaces delta.
+ *
+ * Return: Completion status. '0' on Success; Error code otherwise.
+ */
+int dpsw_fdb_add_multicast(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 fdb_id,
+ const struct dpsw_fdb_multicast_cfg *cfg)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpsw_cmd_fdb_multicast_op *cmd_params;
+ int i;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_FDB_ADD_MULTICAST,
+ cmd_flags,
+ token);
+ cmd_params = (struct dpsw_cmd_fdb_multicast_op *)cmd.params;
+ cmd_params->fdb_id = cpu_to_le16(fdb_id);
+ cmd_params->num_ifs = cpu_to_le16(cfg->num_ifs);
+ dpsw_set_field(cmd_params->type, ENTRY_TYPE, cfg->type);
+ build_if_id_bitmap(cmd_params->if_id, cfg->if_id, cfg->num_ifs);
+ for (i = 0; i < 6; i++)
+ cmd_params->mac_addr[i] = cfg->mac_addr[5 - i];
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpsw_fdb_remove_multicast() - Removing interfaces from an existing multicast
+ * group.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ * @fdb_id: Forwarding Database Identifier
+ * @cfg: Multicast entry configuration
+ *
+ * Interfaces provided by this API have to exist in the group,
+ * otherwise an error will be returned and an entire command
+ * ignored. If there is no interface left in the group,
+ * an entire group is deleted
+ *
+ * Return: Completion status. '0' on Success; Error code otherwise.
+ */
+int dpsw_fdb_remove_multicast(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 fdb_id,
+ const struct dpsw_fdb_multicast_cfg *cfg)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpsw_cmd_fdb_multicast_op *cmd_params;
+ int i;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_FDB_REMOVE_MULTICAST,
+ cmd_flags,
+ token);
+ cmd_params = (struct dpsw_cmd_fdb_multicast_op *)cmd.params;
+ cmd_params->fdb_id = cpu_to_le16(fdb_id);
+ cmd_params->num_ifs = cpu_to_le16(cfg->num_ifs);
+ dpsw_set_field(cmd_params->type, ENTRY_TYPE, cfg->type);
+ build_if_id_bitmap(cmd_params->if_id, cfg->if_id, cfg->num_ifs);
+ for (i = 0; i < 6; i++)
+ cmd_params->mac_addr[i] = cfg->mac_addr[5 - i];
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpsw_fdb_set_learning_mode() - Define FDB learning mode
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ * @fdb_id: Forwarding Database Identifier
+ * @mode: Learning mode
+ *
+ * Return: Completion status. '0' on Success; Error code otherwise.
+ */
+int dpsw_fdb_set_learning_mode(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 fdb_id,
+ enum dpsw_fdb_learning_mode mode)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpsw_cmd_fdb_set_learning_mode *cmd_params;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_FDB_SET_LEARNING_MODE,
+ cmd_flags,
+ token);
+ cmd_params = (struct dpsw_cmd_fdb_set_learning_mode *)cmd.params;
+ cmd_params->fdb_id = cpu_to_le16(fdb_id);
+ dpsw_set_field(cmd_params->mode, LEARNING_MODE, mode);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpsw_get_api_version() - Get Data Path Switch API version
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @major_ver: Major version of data path switch API
+ * @minor_ver: Minor version of data path switch API
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpsw_get_api_version(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 *major_ver,
+ u16 *minor_ver)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpsw_rsp_get_api_version *rsp_params;
+ int err;
+
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_GET_API_VERSION,
+ cmd_flags,
+ 0);
+
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ rsp_params = (struct dpsw_rsp_get_api_version *)cmd.params;
+ *major_ver = le16_to_cpu(rsp_params->version_major);
+ *minor_ver = le16_to_cpu(rsp_params->version_minor);
+
+ return 0;
+}
diff --git a/drivers/staging/fsl-dpaa2/ethsw/dpsw.h b/drivers/staging/fsl-dpaa2/ethsw/dpsw.h
new file mode 100644
index 000000000000..3335adde0193
--- /dev/null
+++ b/drivers/staging/fsl-dpaa2/ethsw/dpsw.h
@@ -0,0 +1,586 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2014-2016 Freescale Semiconductor Inc.
+ * Copyright 2017-2018 NXP
+ *
+ */
+
+#ifndef __FSL_DPSW_H
+#define __FSL_DPSW_H
+
+/* Data Path L2-Switch API
+ * Contains API for handling DPSW topology and functionality
+ */
+
+struct fsl_mc_io;
+
+/**
+ * DPSW general definitions
+ */
+
+/**
+ * Maximum number of traffic class priorities
+ */
+#define DPSW_MAX_PRIORITIES 8
+/**
+ * Maximum number of interfaces
+ */
+#define DPSW_MAX_IF 64
+
+int dpsw_open(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ int dpsw_id,
+ u16 *token);
+
+int dpsw_close(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token);
+
+/**
+ * DPSW options
+ */
+
+/**
+ * Disable flooding
+ */
+#define DPSW_OPT_FLOODING_DIS 0x0000000000000001ULL
+/**
+ * Disable Multicast
+ */
+#define DPSW_OPT_MULTICAST_DIS 0x0000000000000004ULL
+/**
+ * Support control interface
+ */
+#define DPSW_OPT_CTRL_IF_DIS 0x0000000000000010ULL
+/**
+ * Disable flooding metering
+ */
+#define DPSW_OPT_FLOODING_METERING_DIS 0x0000000000000020ULL
+/**
+ * Enable metering
+ */
+#define DPSW_OPT_METERING_EN 0x0000000000000040ULL
+
+/**
+ * enum dpsw_component_type - component type of a bridge
+ * @DPSW_COMPONENT_TYPE_C_VLAN: A C-VLAN component of an
+ * enterprise VLAN bridge or of a Provider Bridge used
+ * to process C-tagged frames
+ * @DPSW_COMPONENT_TYPE_S_VLAN: An S-VLAN component of a
+ * Provider Bridge
+ *
+ */
+enum dpsw_component_type {
+ DPSW_COMPONENT_TYPE_C_VLAN = 0,
+ DPSW_COMPONENT_TYPE_S_VLAN
+};
+
+/**
+ * struct dpsw_cfg - DPSW configuration
+ * @num_ifs: Number of external and internal interfaces
+ * @adv: Advanced parameters; default is all zeros;
+ * use this structure to change default settings
+ */
+struct dpsw_cfg {
+ u16 num_ifs;
+ /**
+ * struct adv - Advanced parameters
+ * @options: Enable/Disable DPSW features (bitmap)
+ * @max_vlans: Maximum Number of VLAN's; 0 - indicates default 16
+ * @max_meters_per_if: Number of meters per interface
+ * @max_fdbs: Maximum Number of FDB's; 0 - indicates default 16
+ * @max_fdb_entries: Number of FDB entries for default FDB table;
+ * 0 - indicates default 1024 entries.
+ * @fdb_aging_time: Default FDB aging time for default FDB table;
+ * 0 - indicates default 300 seconds
+ * @max_fdb_mc_groups: Number of multicast groups in each FDB table;
+ * 0 - indicates default 32
+ * @component_type: Indicates the component type of this bridge
+ */
+ struct {
+ u64 options;
+ u16 max_vlans;
+ u8 max_meters_per_if;
+ u8 max_fdbs;
+ u16 max_fdb_entries;
+ u16 fdb_aging_time;
+ u16 max_fdb_mc_groups;
+ enum dpsw_component_type component_type;
+ } adv;
+};
+
+int dpsw_enable(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token);
+
+int dpsw_disable(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token);
+
+int dpsw_reset(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token);
+
+/**
+ * DPSW IRQ Index and Events
+ */
+
+#define DPSW_IRQ_INDEX_IF 0x0000
+#define DPSW_IRQ_INDEX_L2SW 0x0001
+
+/**
+ * IRQ event - Indicates that the link state changed
+ */
+#define DPSW_IRQ_EVENT_LINK_CHANGED 0x0001
+
+/**
+ * struct dpsw_irq_cfg - IRQ configuration
+ * @addr: Address that must be written to signal a message-based interrupt
+ * @val: Value to write into irq_addr address
+ * @irq_num: A user defined number associated with this IRQ
+ */
+struct dpsw_irq_cfg {
+ u64 addr;
+ u32 val;
+ int irq_num;
+};
+
+int dpsw_set_irq_enable(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u8 irq_index,
+ u8 en);
+
+int dpsw_set_irq_mask(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u8 irq_index,
+ u32 mask);
+
+int dpsw_get_irq_status(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u8 irq_index,
+ u32 *status);
+
+int dpsw_clear_irq_status(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u8 irq_index,
+ u32 status);
+
+/**
+ * struct dpsw_attr - Structure representing DPSW attributes
+ * @id: DPSW object ID
+ * @options: Enable/Disable DPSW features
+ * @max_vlans: Maximum Number of VLANs
+ * @max_meters_per_if: Number of meters per interface
+ * @max_fdbs: Maximum Number of FDBs
+ * @max_fdb_entries: Number of FDB entries for default FDB table;
+ * 0 - indicates default 1024 entries.
+ * @fdb_aging_time: Default FDB aging time for default FDB table;
+ * 0 - indicates default 300 seconds
+ * @max_fdb_mc_groups: Number of multicast groups in each FDB table;
+ * 0 - indicates default 32
+ * @mem_size: DPSW frame storage memory size
+ * @num_ifs: Number of interfaces
+ * @num_vlans: Current number of VLANs
+ * @num_fdbs: Current number of FDBs
+ * @component_type: Component type of this bridge
+ */
+struct dpsw_attr {
+ int id;
+ u64 options;
+ u16 max_vlans;
+ u8 max_meters_per_if;
+ u8 max_fdbs;
+ u16 max_fdb_entries;
+ u16 fdb_aging_time;
+ u16 max_fdb_mc_groups;
+ u16 num_ifs;
+ u16 mem_size;
+ u16 num_vlans;
+ u8 num_fdbs;
+ enum dpsw_component_type component_type;
+};
+
+int dpsw_get_attributes(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ struct dpsw_attr *attr);
+
+/**
+ * enum dpsw_action - Action selection for special/control frames
+ * @DPSW_ACTION_DROP: Drop frame
+ * @DPSW_ACTION_REDIRECT: Redirect frame to control port
+ */
+enum dpsw_action {
+ DPSW_ACTION_DROP = 0,
+ DPSW_ACTION_REDIRECT = 1
+};
+
+/**
+ * Enable auto-negotiation
+ */
+#define DPSW_LINK_OPT_AUTONEG 0x0000000000000001ULL
+/**
+ * Enable half-duplex mode
+ */
+#define DPSW_LINK_OPT_HALF_DUPLEX 0x0000000000000002ULL
+/**
+ * Enable pause frames
+ */
+#define DPSW_LINK_OPT_PAUSE 0x0000000000000004ULL
+/**
+ * Enable a-symmetric pause frames
+ */
+#define DPSW_LINK_OPT_ASYM_PAUSE 0x0000000000000008ULL
+
+/**
+ * struct dpsw_link_cfg - Structure representing DPSW link configuration
+ * @rate: Rate
+ * @options: Mask of available options; use 'DPSW_LINK_OPT_<X>' values
+ */
+struct dpsw_link_cfg {
+ u32 rate;
+ u64 options;
+};
+
+int dpsw_if_set_link_cfg(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 if_id,
+ struct dpsw_link_cfg *cfg);
+/**
+ * struct dpsw_link_state - Structure representing DPSW link state
+ * @rate: Rate
+ * @options: Mask of available options; use 'DPSW_LINK_OPT_<X>' values
+ * @up: 0 - covers two cases: down and disconnected, 1 - up
+ */
+struct dpsw_link_state {
+ u32 rate;
+ u64 options;
+ u8 up;
+};
+
+int dpsw_if_get_link_state(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 if_id,
+ struct dpsw_link_state *state);
+
+int dpsw_if_set_flooding(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 if_id,
+ u8 en);
+
+int dpsw_if_set_broadcast(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 if_id,
+ u8 en);
+
+/**
+ * struct dpsw_tci_cfg - Tag Control Information (TCI) configuration
+ * @pcp: Priority Code Point (PCP): a 3-bit field which refers
+ * to the IEEE 802.1p priority
+ * @dei: Drop Eligible Indicator (DEI): a 1-bit field. May be used
+ * separately or in conjunction with PCP to indicate frames
+ * eligible to be dropped in the presence of congestion
+ * @vlan_id: VLAN Identifier (VID): a 12-bit field specifying the VLAN
+ * to which the frame belongs. The hexadecimal values
+ * of 0x000 and 0xFFF are reserved;
+ * all other values may be used as VLAN identifiers,
+ * allowing up to 4,094 VLANs
+ */
+struct dpsw_tci_cfg {
+ u8 pcp;
+ u8 dei;
+ u16 vlan_id;
+};
+
+int dpsw_if_set_tci(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 if_id,
+ const struct dpsw_tci_cfg *cfg);
+
+/**
+ * enum dpsw_stp_state - Spanning Tree Protocol (STP) states
+ * @DPSW_STP_STATE_BLOCKING: Blocking state
+ * @DPSW_STP_STATE_LISTENING: Listening state
+ * @DPSW_STP_STATE_LEARNING: Learning state
+ * @DPSW_STP_STATE_FORWARDING: Forwarding state
+ *
+ */
+enum dpsw_stp_state {
+ DPSW_STP_STATE_DISABLED = 0,
+ DPSW_STP_STATE_LISTENING = 1,
+ DPSW_STP_STATE_LEARNING = 2,
+ DPSW_STP_STATE_FORWARDING = 3,
+ DPSW_STP_STATE_BLOCKING = 0
+};
+
+/**
+ * struct dpsw_stp_cfg - Spanning Tree Protocol (STP) Configuration
+ * @vlan_id: VLAN ID STP state
+ * @state: STP state
+ */
+struct dpsw_stp_cfg {
+ u16 vlan_id;
+ enum dpsw_stp_state state;
+};
+
+int dpsw_if_set_stp(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 if_id,
+ const struct dpsw_stp_cfg *cfg);
+
+/**
+ * enum dpsw_accepted_frames - Types of frames to accept
+ * @DPSW_ADMIT_ALL: The device accepts VLAN tagged, untagged and
+ * priority tagged frames
+ * @DPSW_ADMIT_ONLY_VLAN_TAGGED: The device discards untagged frames or
+ * Priority-Tagged frames received on this interface.
+ *
+ */
+enum dpsw_accepted_frames {
+ DPSW_ADMIT_ALL = 1,
+ DPSW_ADMIT_ONLY_VLAN_TAGGED = 3
+};
+
+/**
+ * enum dpsw_counter - Counters types
+ * @DPSW_CNT_ING_FRAME: Counts ingress frames
+ * @DPSW_CNT_ING_BYTE: Counts ingress bytes
+ * @DPSW_CNT_ING_FLTR_FRAME: Counts filtered ingress frames
+ * @DPSW_CNT_ING_FRAME_DISCARD: Counts discarded ingress frame
+ * @DPSW_CNT_ING_MCAST_FRAME: Counts ingress multicast frames
+ * @DPSW_CNT_ING_MCAST_BYTE: Counts ingress multicast bytes
+ * @DPSW_CNT_ING_BCAST_FRAME: Counts ingress broadcast frames
+ * @DPSW_CNT_ING_BCAST_BYTES: Counts ingress broadcast bytes
+ * @DPSW_CNT_EGR_FRAME: Counts egress frames
+ * @DPSW_CNT_EGR_BYTE: Counts eEgress bytes
+ * @DPSW_CNT_EGR_FRAME_DISCARD: Counts discarded egress frames
+ * @DPSW_CNT_EGR_STP_FRAME_DISCARD: Counts egress STP discarded frames
+ */
+enum dpsw_counter {
+ DPSW_CNT_ING_FRAME = 0x0,
+ DPSW_CNT_ING_BYTE = 0x1,
+ DPSW_CNT_ING_FLTR_FRAME = 0x2,
+ DPSW_CNT_ING_FRAME_DISCARD = 0x3,
+ DPSW_CNT_ING_MCAST_FRAME = 0x4,
+ DPSW_CNT_ING_MCAST_BYTE = 0x5,
+ DPSW_CNT_ING_BCAST_FRAME = 0x6,
+ DPSW_CNT_ING_BCAST_BYTES = 0x7,
+ DPSW_CNT_EGR_FRAME = 0x8,
+ DPSW_CNT_EGR_BYTE = 0x9,
+ DPSW_CNT_EGR_FRAME_DISCARD = 0xa,
+ DPSW_CNT_EGR_STP_FRAME_DISCARD = 0xb
+};
+
+int dpsw_if_get_counter(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 if_id,
+ enum dpsw_counter type,
+ u64 *counter);
+
+int dpsw_if_enable(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 if_id);
+
+int dpsw_if_disable(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 if_id);
+
+int dpsw_if_set_max_frame_length(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 if_id,
+ u16 frame_length);
+
+/**
+ * struct dpsw_vlan_cfg - VLAN Configuration
+ * @fdb_id: Forwarding Data Base
+ */
+struct dpsw_vlan_cfg {
+ u16 fdb_id;
+};
+
+int dpsw_vlan_add(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 vlan_id,
+ const struct dpsw_vlan_cfg *cfg);
+
+/**
+ * struct dpsw_vlan_if_cfg - Set of VLAN Interfaces
+ * @num_ifs: The number of interfaces that are assigned to the egress
+ * list for this VLAN
+ * @if_id: The set of interfaces that are
+ * assigned to the egress list for this VLAN
+ */
+struct dpsw_vlan_if_cfg {
+ u16 num_ifs;
+ u16 if_id[DPSW_MAX_IF];
+};
+
+int dpsw_vlan_add_if(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 vlan_id,
+ const struct dpsw_vlan_if_cfg *cfg);
+
+int dpsw_vlan_add_if_untagged(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 vlan_id,
+ const struct dpsw_vlan_if_cfg *cfg);
+
+int dpsw_vlan_remove_if(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 vlan_id,
+ const struct dpsw_vlan_if_cfg *cfg);
+
+int dpsw_vlan_remove_if_untagged(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 vlan_id,
+ const struct dpsw_vlan_if_cfg *cfg);
+
+int dpsw_vlan_remove(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 vlan_id);
+
+/**
+ * enum dpsw_fdb_entry_type - FDB Entry type - Static/Dynamic
+ * @DPSW_FDB_ENTRY_STATIC: Static entry
+ * @DPSW_FDB_ENTRY_DINAMIC: Dynamic entry
+ */
+enum dpsw_fdb_entry_type {
+ DPSW_FDB_ENTRY_STATIC = 0,
+ DPSW_FDB_ENTRY_DINAMIC = 1
+};
+
+/**
+ * struct dpsw_fdb_unicast_cfg - Unicast entry configuration
+ * @type: Select static or dynamic entry
+ * @mac_addr: MAC address
+ * @if_egress: Egress interface ID
+ */
+struct dpsw_fdb_unicast_cfg {
+ enum dpsw_fdb_entry_type type;
+ u8 mac_addr[6];
+ u16 if_egress;
+};
+
+int dpsw_fdb_add_unicast(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 fdb_id,
+ const struct dpsw_fdb_unicast_cfg *cfg);
+
+int dpsw_fdb_remove_unicast(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 fdb_id,
+ const struct dpsw_fdb_unicast_cfg *cfg);
+
+/**
+ * struct dpsw_fdb_multicast_cfg - Multi-cast entry configuration
+ * @type: Select static or dynamic entry
+ * @mac_addr: MAC address
+ * @num_ifs: Number of external and internal interfaces
+ * @if_id: Egress interface IDs
+ */
+struct dpsw_fdb_multicast_cfg {
+ enum dpsw_fdb_entry_type type;
+ u8 mac_addr[6];
+ u16 num_ifs;
+ u16 if_id[DPSW_MAX_IF];
+};
+
+int dpsw_fdb_add_multicast(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 fdb_id,
+ const struct dpsw_fdb_multicast_cfg *cfg);
+
+int dpsw_fdb_remove_multicast(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 fdb_id,
+ const struct dpsw_fdb_multicast_cfg *cfg);
+
+/**
+ * enum dpsw_fdb_learning_mode - Auto-learning modes
+ * @DPSW_FDB_LEARNING_MODE_DIS: Disable Auto-learning
+ * @DPSW_FDB_LEARNING_MODE_HW: Enable HW auto-Learning
+ * @DPSW_FDB_LEARNING_MODE_NON_SECURE: Enable None secure learning by CPU
+ * @DPSW_FDB_LEARNING_MODE_SECURE: Enable secure learning by CPU
+ *
+ * NONE - SECURE LEARNING
+ * SMAC found DMAC found CTLU Action
+ * v v Forward frame to
+ * 1. DMAC destination
+ * - v Forward frame to
+ * 1. DMAC destination
+ * 2. Control interface
+ * v - Forward frame to
+ * 1. Flooding list of interfaces
+ * - - Forward frame to
+ * 1. Flooding list of interfaces
+ * 2. Control interface
+ * SECURE LEARING
+ * SMAC found DMAC found CTLU Action
+ * v v Forward frame to
+ * 1. DMAC destination
+ * - v Forward frame to
+ * 1. Control interface
+ * v - Forward frame to
+ * 1. Flooding list of interfaces
+ * - - Forward frame to
+ * 1. Control interface
+ */
+enum dpsw_fdb_learning_mode {
+ DPSW_FDB_LEARNING_MODE_DIS = 0,
+ DPSW_FDB_LEARNING_MODE_HW = 1,
+ DPSW_FDB_LEARNING_MODE_NON_SECURE = 2,
+ DPSW_FDB_LEARNING_MODE_SECURE = 3
+};
+
+int dpsw_fdb_set_learning_mode(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 fdb_id,
+ enum dpsw_fdb_learning_mode mode);
+
+/**
+ * struct dpsw_fdb_attr - FDB Attributes
+ * @max_fdb_entries: Number of FDB entries
+ * @fdb_aging_time: Aging time in seconds
+ * @learning_mode: Learning mode
+ * @num_fdb_mc_groups: Current number of multicast groups
+ * @max_fdb_mc_groups: Maximum number of multicast groups
+ */
+struct dpsw_fdb_attr {
+ u16 max_fdb_entries;
+ u16 fdb_aging_time;
+ enum dpsw_fdb_learning_mode learning_mode;
+ u16 num_fdb_mc_groups;
+ u16 max_fdb_mc_groups;
+};
+
+int dpsw_get_api_version(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 *major_ver,
+ u16 *minor_ver);
+
+#endif /* __FSL_DPSW_H */
diff --git a/drivers/staging/fsl-dpaa2/ethsw/ethsw-ethtool.c b/drivers/staging/fsl-dpaa2/ethsw/ethsw-ethtool.c
new file mode 100644
index 000000000000..926a0c053e18
--- /dev/null
+++ b/drivers/staging/fsl-dpaa2/ethsw/ethsw-ethtool.c
@@ -0,0 +1,182 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * DPAA2 Ethernet Switch ethtool support
+ *
+ * Copyright 2014-2016 Freescale Semiconductor Inc.
+ * Copyright 2017-2018 NXP
+ *
+ */
+
+#include "ethsw.h"
+
+static struct {
+ enum dpsw_counter id;
+ char name[ETH_GSTRING_LEN];
+} ethsw_ethtool_counters[] = {
+ {DPSW_CNT_ING_FRAME, "rx frames"},
+ {DPSW_CNT_ING_BYTE, "rx bytes"},
+ {DPSW_CNT_ING_FLTR_FRAME, "rx filtered frames"},
+ {DPSW_CNT_ING_FRAME_DISCARD, "rx discarded frames"},
+ {DPSW_CNT_ING_BCAST_FRAME, "rx b-cast frames"},
+ {DPSW_CNT_ING_BCAST_BYTES, "rx b-cast bytes"},
+ {DPSW_CNT_ING_MCAST_FRAME, "rx m-cast frames"},
+ {DPSW_CNT_ING_MCAST_BYTE, "rx m-cast bytes"},
+ {DPSW_CNT_EGR_FRAME, "tx frames"},
+ {DPSW_CNT_EGR_BYTE, "tx bytes"},
+ {DPSW_CNT_EGR_FRAME_DISCARD, "tx discarded frames"},
+
+};
+
+#define ETHSW_NUM_COUNTERS ARRAY_SIZE(ethsw_ethtool_counters)
+
+static void ethsw_get_drvinfo(struct net_device *netdev,
+ struct ethtool_drvinfo *drvinfo)
+{
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+ u16 version_major, version_minor;
+ int err;
+
+ strlcpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver));
+
+ err = dpsw_get_api_version(port_priv->ethsw_data->mc_io, 0,
+ &version_major,
+ &version_minor);
+ if (err)
+ strlcpy(drvinfo->fw_version, "N/A",
+ sizeof(drvinfo->fw_version));
+ else
+ snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
+ "%u.%u", version_major, version_minor);
+
+ strlcpy(drvinfo->bus_info, dev_name(netdev->dev.parent->parent),
+ sizeof(drvinfo->bus_info));
+}
+
+static int
+ethsw_get_link_ksettings(struct net_device *netdev,
+ struct ethtool_link_ksettings *link_ksettings)
+{
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+ struct dpsw_link_state state = {0};
+ int err = 0;
+
+ err = dpsw_if_get_link_state(port_priv->ethsw_data->mc_io, 0,
+ port_priv->ethsw_data->dpsw_handle,
+ port_priv->idx,
+ &state);
+ if (err) {
+ netdev_err(netdev, "ERROR %d getting link state", err);
+ goto out;
+ }
+
+ /* At the moment, we have no way of interrogating the DPMAC
+ * from the DPSW side or there may not exist a DPMAC at all.
+ * Report only autoneg state, duplexity and speed.
+ */
+ if (state.options & DPSW_LINK_OPT_AUTONEG)
+ link_ksettings->base.autoneg = AUTONEG_ENABLE;
+ if (!(state.options & DPSW_LINK_OPT_HALF_DUPLEX))
+ link_ksettings->base.duplex = DUPLEX_FULL;
+ link_ksettings->base.speed = state.rate;
+
+out:
+ return err;
+}
+
+static int
+ethsw_set_link_ksettings(struct net_device *netdev,
+ const struct ethtool_link_ksettings *link_ksettings)
+{
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+ struct dpsw_link_cfg cfg = {0};
+ int err = 0;
+
+ netdev_dbg(netdev, "Setting link parameters...");
+
+ /* Due to a temporary MC limitation, the DPSW port must be down
+ * in order to be able to change link settings. Taking steps to let
+ * the user know that.
+ */
+ if (netif_running(netdev)) {
+ netdev_info(netdev, "Sorry, interface must be brought down first.\n");
+ return -EACCES;
+ }
+
+ cfg.rate = link_ksettings->base.speed;
+ if (link_ksettings->base.autoneg == AUTONEG_ENABLE)
+ cfg.options |= DPSW_LINK_OPT_AUTONEG;
+ else
+ cfg.options &= ~DPSW_LINK_OPT_AUTONEG;
+ if (link_ksettings->base.duplex == DUPLEX_HALF)
+ cfg.options |= DPSW_LINK_OPT_HALF_DUPLEX;
+ else
+ cfg.options &= ~DPSW_LINK_OPT_HALF_DUPLEX;
+
+ err = dpsw_if_set_link_cfg(port_priv->ethsw_data->mc_io, 0,
+ port_priv->ethsw_data->dpsw_handle,
+ port_priv->idx,
+ &cfg);
+ if (err)
+ /* ethtool will be loud enough if we return an error; no point
+ * in putting our own error message on the console by default
+ */
+ netdev_dbg(netdev, "ERROR %d setting link cfg", err);
+
+ return err;
+}
+
+static int ethsw_ethtool_get_sset_count(struct net_device *dev, int sset)
+{
+ switch (sset) {
+ case ETH_SS_STATS:
+ return ETHSW_NUM_COUNTERS;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static void ethsw_ethtool_get_strings(struct net_device *netdev,
+ u32 stringset, u8 *data)
+{
+ int i;
+
+ switch (stringset) {
+ case ETH_SS_STATS:
+ for (i = 0; i < ETHSW_NUM_COUNTERS; i++)
+ memcpy(data + i * ETH_GSTRING_LEN,
+ ethsw_ethtool_counters[i].name, ETH_GSTRING_LEN);
+ break;
+ }
+}
+
+static void ethsw_ethtool_get_stats(struct net_device *netdev,
+ struct ethtool_stats *stats,
+ u64 *data)
+{
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+ int i, err;
+
+ memset(data, 0,
+ sizeof(u64) * ETHSW_NUM_COUNTERS);
+
+ for (i = 0; i < ETHSW_NUM_COUNTERS; i++) {
+ err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
+ port_priv->ethsw_data->dpsw_handle,
+ port_priv->idx,
+ ethsw_ethtool_counters[i].id,
+ &data[i]);
+ if (err)
+ netdev_err(netdev, "dpsw_if_get_counter[%s] err %d\n",
+ ethsw_ethtool_counters[i].name, err);
+ }
+}
+
+const struct ethtool_ops ethsw_port_ethtool_ops = {
+ .get_drvinfo = ethsw_get_drvinfo,
+ .get_link = ethtool_op_get_link,
+ .get_link_ksettings = ethsw_get_link_ksettings,
+ .set_link_ksettings = ethsw_set_link_ksettings,
+ .get_strings = ethsw_ethtool_get_strings,
+ .get_ethtool_stats = ethsw_ethtool_get_stats,
+ .get_sset_count = ethsw_ethtool_get_sset_count,
+};
diff --git a/drivers/staging/fsl-dpaa2/ethsw/ethsw.c b/drivers/staging/fsl-dpaa2/ethsw/ethsw.c
new file mode 100644
index 000000000000..c723a04bc3d6
--- /dev/null
+++ b/drivers/staging/fsl-dpaa2/ethsw/ethsw.c
@@ -0,0 +1,1508 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * DPAA2 Ethernet Switch driver
+ *
+ * Copyright 2014-2016 Freescale Semiconductor Inc.
+ * Copyright 2017-2018 NXP
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/interrupt.h>
+#include <linux/msi.h>
+#include <linux/kthread.h>
+#include <linux/workqueue.h>
+
+#include <linux/fsl/mc.h>
+
+#include "ethsw.h"
+
+static struct workqueue_struct *ethsw_owq;
+
+/* Minimal supported DPSW version */
+#define DPSW_MIN_VER_MAJOR 8
+#define DPSW_MIN_VER_MINOR 0
+
+#define DEFAULT_VLAN_ID 1
+
+static int ethsw_add_vlan(struct ethsw_core *ethsw, u16 vid)
+{
+ int err;
+
+ struct dpsw_vlan_cfg vcfg = {
+ .fdb_id = 0,
+ };
+
+ if (ethsw->vlans[vid]) {
+ dev_err(ethsw->dev, "VLAN already configured\n");
+ return -EEXIST;
+ }
+
+ err = dpsw_vlan_add(ethsw->mc_io, 0,
+ ethsw->dpsw_handle, vid, &vcfg);
+ if (err) {
+ dev_err(ethsw->dev, "dpsw_vlan_add err %d\n", err);
+ return err;
+ }
+ ethsw->vlans[vid] = ETHSW_VLAN_MEMBER;
+
+ return 0;
+}
+
+static int ethsw_port_set_tci(struct ethsw_port_priv *port_priv,
+ struct dpsw_tci_cfg *tci_cfg)
+{
+ struct ethsw_core *ethsw = port_priv->ethsw_data;
+ struct net_device *netdev = port_priv->netdev;
+ bool is_oper;
+ int err, ret;
+
+ /* Interface needs to be down to change PVID */
+ is_oper = netif_oper_up(netdev);
+ if (is_oper) {
+ err = dpsw_if_disable(ethsw->mc_io, 0,
+ ethsw->dpsw_handle,
+ port_priv->idx);
+ if (err) {
+ netdev_err(netdev, "dpsw_if_disable err %d\n", err);
+ return err;
+ }
+ }
+
+ err = dpsw_if_set_tci(ethsw->mc_io, 0, ethsw->dpsw_handle,
+ port_priv->idx, tci_cfg);
+ if (err) {
+ netdev_err(netdev, "dpsw_if_set_tci err %d\n", err);
+ goto set_tci_error;
+ }
+
+ /* Delete previous PVID info and mark the new one */
+ if (port_priv->pvid)
+ port_priv->vlans[port_priv->pvid] &= ~ETHSW_VLAN_PVID;
+ port_priv->vlans[tci_cfg->vlan_id] |= ETHSW_VLAN_PVID;
+ port_priv->pvid = tci_cfg->vlan_id;
+
+set_tci_error:
+ if (is_oper) {
+ ret = dpsw_if_enable(ethsw->mc_io, 0,
+ ethsw->dpsw_handle,
+ port_priv->idx);
+ if (ret) {
+ netdev_err(netdev, "dpsw_if_enable err %d\n", ret);
+ return ret;
+ }
+ }
+
+ return err;
+}
+
+static int ethsw_port_add_vlan(struct ethsw_port_priv *port_priv,
+ u16 vid, u16 flags)
+{
+ struct ethsw_core *ethsw = port_priv->ethsw_data;
+ struct net_device *netdev = port_priv->netdev;
+ struct dpsw_vlan_if_cfg vcfg;
+ int err;
+
+ if (port_priv->vlans[vid]) {
+ netdev_warn(netdev, "VLAN %d already configured\n", vid);
+ return -EEXIST;
+ }
+
+ vcfg.num_ifs = 1;
+ vcfg.if_id[0] = port_priv->idx;
+ err = dpsw_vlan_add_if(ethsw->mc_io, 0, ethsw->dpsw_handle, vid, &vcfg);
+ if (err) {
+ netdev_err(netdev, "dpsw_vlan_add_if err %d\n", err);
+ return err;
+ }
+
+ port_priv->vlans[vid] = ETHSW_VLAN_MEMBER;
+
+ if (flags & BRIDGE_VLAN_INFO_UNTAGGED) {
+ err = dpsw_vlan_add_if_untagged(ethsw->mc_io, 0,
+ ethsw->dpsw_handle,
+ vid, &vcfg);
+ if (err) {
+ netdev_err(netdev,
+ "dpsw_vlan_add_if_untagged err %d\n", err);
+ return err;
+ }
+ port_priv->vlans[vid] |= ETHSW_VLAN_UNTAGGED;
+ }
+
+ if (flags & BRIDGE_VLAN_INFO_PVID) {
+ struct dpsw_tci_cfg tci_cfg = {
+ .pcp = 0,
+ .dei = 0,
+ .vlan_id = vid,
+ };
+
+ err = ethsw_port_set_tci(port_priv, &tci_cfg);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int ethsw_set_learning(struct ethsw_core *ethsw, u8 flag)
+{
+ enum dpsw_fdb_learning_mode learn_mode;
+ int err;
+
+ if (flag)
+ learn_mode = DPSW_FDB_LEARNING_MODE_HW;
+ else
+ learn_mode = DPSW_FDB_LEARNING_MODE_DIS;
+
+ err = dpsw_fdb_set_learning_mode(ethsw->mc_io, 0, ethsw->dpsw_handle, 0,
+ learn_mode);
+ if (err) {
+ dev_err(ethsw->dev, "dpsw_fdb_set_learning_mode err %d\n", err);
+ return err;
+ }
+ ethsw->learning = !!flag;
+
+ return 0;
+}
+
+static int ethsw_port_set_flood(struct ethsw_port_priv *port_priv, u8 flag)
+{
+ int err;
+
+ err = dpsw_if_set_flooding(port_priv->ethsw_data->mc_io, 0,
+ port_priv->ethsw_data->dpsw_handle,
+ port_priv->idx, flag);
+ if (err) {
+ netdev_err(port_priv->netdev,
+ "dpsw_fdb_set_learning_mode err %d\n", err);
+ return err;
+ }
+ port_priv->flood = !!flag;
+
+ return 0;
+}
+
+static int ethsw_port_set_stp_state(struct ethsw_port_priv *port_priv, u8 state)
+{
+ struct dpsw_stp_cfg stp_cfg = {
+ .vlan_id = DEFAULT_VLAN_ID,
+ .state = state,
+ };
+ int err;
+
+ if (!netif_oper_up(port_priv->netdev) || state == port_priv->stp_state)
+ return 0; /* Nothing to do */
+
+ err = dpsw_if_set_stp(port_priv->ethsw_data->mc_io, 0,
+ port_priv->ethsw_data->dpsw_handle,
+ port_priv->idx, &stp_cfg);
+ if (err) {
+ netdev_err(port_priv->netdev,
+ "dpsw_if_set_stp err %d\n", err);
+ return err;
+ }
+
+ port_priv->stp_state = state;
+
+ return 0;
+}
+
+static int ethsw_dellink_switch(struct ethsw_core *ethsw, u16 vid)
+{
+ struct ethsw_port_priv *ppriv_local = NULL;
+ int i, err;
+
+ if (!ethsw->vlans[vid])
+ return -ENOENT;
+
+ err = dpsw_vlan_remove(ethsw->mc_io, 0, ethsw->dpsw_handle, vid);
+ if (err) {
+ dev_err(ethsw->dev, "dpsw_vlan_remove err %d\n", err);
+ return err;
+ }
+ ethsw->vlans[vid] = 0;
+
+ for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
+ ppriv_local = ethsw->ports[i];
+ ppriv_local->vlans[vid] = 0;
+ }
+
+ return 0;
+}
+
+static int ethsw_port_fdb_add_uc(struct ethsw_port_priv *port_priv,
+ const unsigned char *addr)
+{
+ struct dpsw_fdb_unicast_cfg entry = {0};
+ int err;
+
+ entry.if_egress = port_priv->idx;
+ entry.type = DPSW_FDB_ENTRY_STATIC;
+ ether_addr_copy(entry.mac_addr, addr);
+
+ err = dpsw_fdb_add_unicast(port_priv->ethsw_data->mc_io, 0,
+ port_priv->ethsw_data->dpsw_handle,
+ 0, &entry);
+ if (err)
+ netdev_err(port_priv->netdev,
+ "dpsw_fdb_add_unicast err %d\n", err);
+ return err;
+}
+
+static int ethsw_port_fdb_del_uc(struct ethsw_port_priv *port_priv,
+ const unsigned char *addr)
+{
+ struct dpsw_fdb_unicast_cfg entry = {0};
+ int err;
+
+ entry.if_egress = port_priv->idx;
+ entry.type = DPSW_FDB_ENTRY_STATIC;
+ ether_addr_copy(entry.mac_addr, addr);
+
+ err = dpsw_fdb_remove_unicast(port_priv->ethsw_data->mc_io, 0,
+ port_priv->ethsw_data->dpsw_handle,
+ 0, &entry);
+ /* Silently discard error for calling multiple times the del command */
+ if (err && err != -ENXIO)
+ netdev_err(port_priv->netdev,
+ "dpsw_fdb_remove_unicast err %d\n", err);
+ return err;
+}
+
+static int ethsw_port_fdb_add_mc(struct ethsw_port_priv *port_priv,
+ const unsigned char *addr)
+{
+ struct dpsw_fdb_multicast_cfg entry = {0};
+ int err;
+
+ ether_addr_copy(entry.mac_addr, addr);
+ entry.type = DPSW_FDB_ENTRY_STATIC;
+ entry.num_ifs = 1;
+ entry.if_id[0] = port_priv->idx;
+
+ err = dpsw_fdb_add_multicast(port_priv->ethsw_data->mc_io, 0,
+ port_priv->ethsw_data->dpsw_handle,
+ 0, &entry);
+ /* Silently discard error for calling multiple times the add command */
+ if (err && err != -ENXIO)
+ netdev_err(port_priv->netdev, "dpsw_fdb_add_multicast err %d\n",
+ err);
+ return err;
+}
+
+static int ethsw_port_fdb_del_mc(struct ethsw_port_priv *port_priv,
+ const unsigned char *addr)
+{
+ struct dpsw_fdb_multicast_cfg entry = {0};
+ int err;
+
+ ether_addr_copy(entry.mac_addr, addr);
+ entry.type = DPSW_FDB_ENTRY_STATIC;
+ entry.num_ifs = 1;
+ entry.if_id[0] = port_priv->idx;
+
+ err = dpsw_fdb_remove_multicast(port_priv->ethsw_data->mc_io, 0,
+ port_priv->ethsw_data->dpsw_handle,
+ 0, &entry);
+ /* Silently discard error for calling multiple times the del command */
+ if (err && err != -ENAVAIL)
+ netdev_err(port_priv->netdev,
+ "dpsw_fdb_remove_multicast err %d\n", err);
+ return err;
+}
+
+static void port_get_stats(struct net_device *netdev,
+ struct rtnl_link_stats64 *stats)
+{
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+ u64 tmp;
+ int err;
+
+ err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
+ port_priv->ethsw_data->dpsw_handle,
+ port_priv->idx,
+ DPSW_CNT_ING_FRAME, &stats->rx_packets);
+ if (err)
+ goto error;
+
+ err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
+ port_priv->ethsw_data->dpsw_handle,
+ port_priv->idx,
+ DPSW_CNT_EGR_FRAME, &stats->tx_packets);
+ if (err)
+ goto error;
+
+ err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
+ port_priv->ethsw_data->dpsw_handle,
+ port_priv->idx,
+ DPSW_CNT_ING_BYTE, &stats->rx_bytes);
+ if (err)
+ goto error;
+
+ err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
+ port_priv->ethsw_data->dpsw_handle,
+ port_priv->idx,
+ DPSW_CNT_EGR_BYTE, &stats->tx_bytes);
+ if (err)
+ goto error;
+
+ err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
+ port_priv->ethsw_data->dpsw_handle,
+ port_priv->idx,
+ DPSW_CNT_ING_FRAME_DISCARD,
+ &stats->rx_dropped);
+ if (err)
+ goto error;
+
+ err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
+ port_priv->ethsw_data->dpsw_handle,
+ port_priv->idx,
+ DPSW_CNT_ING_FLTR_FRAME,
+ &tmp);
+ if (err)
+ goto error;
+ stats->rx_dropped += tmp;
+
+ err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
+ port_priv->ethsw_data->dpsw_handle,
+ port_priv->idx,
+ DPSW_CNT_EGR_FRAME_DISCARD,
+ &stats->tx_dropped);
+ if (err)
+ goto error;
+
+ return;
+
+error:
+ netdev_err(netdev, "dpsw_if_get_counter err %d\n", err);
+}
+
+static bool port_has_offload_stats(const struct net_device *netdev,
+ int attr_id)
+{
+ return (attr_id == IFLA_OFFLOAD_XSTATS_CPU_HIT);
+}
+
+static int port_get_offload_stats(int attr_id,
+ const struct net_device *netdev,
+ void *sp)
+{
+ switch (attr_id) {
+ case IFLA_OFFLOAD_XSTATS_CPU_HIT:
+ port_get_stats((struct net_device *)netdev, sp);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int port_change_mtu(struct net_device *netdev, int mtu)
+{
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+ int err;
+
+ err = dpsw_if_set_max_frame_length(port_priv->ethsw_data->mc_io,
+ 0,
+ port_priv->ethsw_data->dpsw_handle,
+ port_priv->idx,
+ (u16)ETHSW_L2_MAX_FRM(mtu));
+ if (err) {
+ netdev_err(netdev,
+ "dpsw_if_set_max_frame_length() err %d\n", err);
+ return err;
+ }
+
+ netdev->mtu = mtu;
+ return 0;
+}
+
+static int port_carrier_state_sync(struct net_device *netdev)
+{
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+ struct dpsw_link_state state;
+ int err;
+
+ err = dpsw_if_get_link_state(port_priv->ethsw_data->mc_io, 0,
+ port_priv->ethsw_data->dpsw_handle,
+ port_priv->idx, &state);
+ if (err) {
+ netdev_err(netdev, "dpsw_if_get_link_state() err %d\n", err);
+ return err;
+ }
+
+ WARN_ONCE(state.up > 1, "Garbage read into link_state");
+
+ if (state.up != port_priv->link_state) {
+ if (state.up)
+ netif_carrier_on(netdev);
+ else
+ netif_carrier_off(netdev);
+ port_priv->link_state = state.up;
+ }
+ return 0;
+}
+
+static int port_open(struct net_device *netdev)
+{
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+ int err;
+
+ /* No need to allow Tx as control interface is disabled */
+ netif_tx_stop_all_queues(netdev);
+
+ err = dpsw_if_enable(port_priv->ethsw_data->mc_io, 0,
+ port_priv->ethsw_data->dpsw_handle,
+ port_priv->idx);
+ if (err) {
+ netdev_err(netdev, "dpsw_if_enable err %d\n", err);
+ return err;
+ }
+
+ /* sync carrier state */
+ err = port_carrier_state_sync(netdev);
+ if (err) {
+ netdev_err(netdev,
+ "port_carrier_state_sync err %d\n", err);
+ goto err_carrier_sync;
+ }
+
+ return 0;
+
+err_carrier_sync:
+ dpsw_if_disable(port_priv->ethsw_data->mc_io, 0,
+ port_priv->ethsw_data->dpsw_handle,
+ port_priv->idx);
+ return err;
+}
+
+static int port_stop(struct net_device *netdev)
+{
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+ int err;
+
+ err = dpsw_if_disable(port_priv->ethsw_data->mc_io, 0,
+ port_priv->ethsw_data->dpsw_handle,
+ port_priv->idx);
+ if (err) {
+ netdev_err(netdev, "dpsw_if_disable err %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static netdev_tx_t port_dropframe(struct sk_buff *skb,
+ struct net_device *netdev)
+{
+ /* we don't support I/O for now, drop the frame */
+ dev_kfree_skb_any(skb);
+
+ return NETDEV_TX_OK;
+}
+
+static const struct net_device_ops ethsw_port_ops = {
+ .ndo_open = port_open,
+ .ndo_stop = port_stop,
+
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = port_change_mtu,
+ .ndo_has_offload_stats = port_has_offload_stats,
+ .ndo_get_offload_stats = port_get_offload_stats,
+
+ .ndo_start_xmit = port_dropframe,
+};
+
+static void ethsw_links_state_update(struct ethsw_core *ethsw)
+{
+ int i;
+
+ for (i = 0; i < ethsw->sw_attr.num_ifs; i++)
+ port_carrier_state_sync(ethsw->ports[i]->netdev);
+}
+
+static irqreturn_t ethsw_irq0_handler_thread(int irq_num, void *arg)
+{
+ struct device *dev = (struct device *)arg;
+ struct ethsw_core *ethsw = dev_get_drvdata(dev);
+
+ /* Mask the events and the if_id reserved bits to be cleared on read */
+ u32 status = DPSW_IRQ_EVENT_LINK_CHANGED | 0xFFFF0000;
+ int err;
+
+ err = dpsw_get_irq_status(ethsw->mc_io, 0, ethsw->dpsw_handle,
+ DPSW_IRQ_INDEX_IF, &status);
+ if (err) {
+ dev_err(dev, "Can't get irq status (err %d)", err);
+
+ err = dpsw_clear_irq_status(ethsw->mc_io, 0, ethsw->dpsw_handle,
+ DPSW_IRQ_INDEX_IF, 0xFFFFFFFF);
+ if (err)
+ dev_err(dev, "Can't clear irq status (err %d)", err);
+ goto out;
+ }
+
+ if (status & DPSW_IRQ_EVENT_LINK_CHANGED)
+ ethsw_links_state_update(ethsw);
+
+out:
+ return IRQ_HANDLED;
+}
+
+static int ethsw_setup_irqs(struct fsl_mc_device *sw_dev)
+{
+ struct device *dev = &sw_dev->dev;
+ struct ethsw_core *ethsw = dev_get_drvdata(dev);
+ u32 mask = DPSW_IRQ_EVENT_LINK_CHANGED;
+ struct fsl_mc_device_irq *irq;
+ int err;
+
+ err = fsl_mc_allocate_irqs(sw_dev);
+ if (err) {
+ dev_err(dev, "MC irqs allocation failed\n");
+ return err;
+ }
+
+ if (WARN_ON(sw_dev->obj_desc.irq_count != DPSW_IRQ_NUM)) {
+ err = -EINVAL;
+ goto free_irq;
+ }
+
+ err = dpsw_set_irq_enable(ethsw->mc_io, 0, ethsw->dpsw_handle,
+ DPSW_IRQ_INDEX_IF, 0);
+ if (err) {
+ dev_err(dev, "dpsw_set_irq_enable err %d\n", err);
+ goto free_irq;
+ }
+
+ irq = sw_dev->irqs[DPSW_IRQ_INDEX_IF];
+
+ err = devm_request_threaded_irq(dev, irq->msi_desc->irq,
+ NULL,
+ ethsw_irq0_handler_thread,
+ IRQF_NO_SUSPEND | IRQF_ONESHOT,
+ dev_name(dev), dev);
+ if (err) {
+ dev_err(dev, "devm_request_threaded_irq(): %d", err);
+ goto free_irq;
+ }
+
+ err = dpsw_set_irq_mask(ethsw->mc_io, 0, ethsw->dpsw_handle,
+ DPSW_IRQ_INDEX_IF, mask);
+ if (err) {
+ dev_err(dev, "dpsw_set_irq_mask(): %d", err);
+ goto free_devm_irq;
+ }
+
+ err = dpsw_set_irq_enable(ethsw->mc_io, 0, ethsw->dpsw_handle,
+ DPSW_IRQ_INDEX_IF, 1);
+ if (err) {
+ dev_err(dev, "dpsw_set_irq_enable(): %d", err);
+ goto free_devm_irq;
+ }
+
+ return 0;
+
+free_devm_irq:
+ devm_free_irq(dev, irq->msi_desc->irq, dev);
+free_irq:
+ fsl_mc_free_irqs(sw_dev);
+ return err;
+}
+
+static void ethsw_teardown_irqs(struct fsl_mc_device *sw_dev)
+{
+ struct device *dev = &sw_dev->dev;
+ struct ethsw_core *ethsw = dev_get_drvdata(dev);
+ struct fsl_mc_device_irq *irq;
+ int err;
+
+ irq = sw_dev->irqs[DPSW_IRQ_INDEX_IF];
+ err = dpsw_set_irq_enable(ethsw->mc_io, 0, ethsw->dpsw_handle,
+ DPSW_IRQ_INDEX_IF, 0);
+ if (err)
+ dev_err(dev, "dpsw_set_irq_enable err %d\n", err);
+
+ fsl_mc_free_irqs(sw_dev);
+}
+
+static int swdev_port_attr_get(struct net_device *netdev,
+ struct switchdev_attr *attr)
+{
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+
+ switch (attr->id) {
+ case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
+ attr->u.ppid.id_len = 1;
+ attr->u.ppid.id[0] = port_priv->ethsw_data->dev_id;
+ break;
+ case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
+ attr->u.brport_flags =
+ (port_priv->ethsw_data->learning ? BR_LEARNING : 0) |
+ (port_priv->flood ? BR_FLOOD : 0);
+ break;
+ case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS_SUPPORT:
+ attr->u.brport_flags_support = BR_LEARNING | BR_FLOOD;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int port_attr_stp_state_set(struct net_device *netdev,
+ struct switchdev_trans *trans,
+ u8 state)
+{
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+
+ if (switchdev_trans_ph_prepare(trans))
+ return 0;
+
+ return ethsw_port_set_stp_state(port_priv, state);
+}
+
+static int port_attr_br_flags_set(struct net_device *netdev,
+ struct switchdev_trans *trans,
+ unsigned long flags)
+{
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+ int err = 0;
+
+ if (switchdev_trans_ph_prepare(trans))
+ return 0;
+
+ /* Learning is enabled per switch */
+ err = ethsw_set_learning(port_priv->ethsw_data, flags & BR_LEARNING);
+ if (err)
+ goto exit;
+
+ err = ethsw_port_set_flood(port_priv, flags & BR_FLOOD);
+
+exit:
+ return err;
+}
+
+static int swdev_port_attr_set(struct net_device *netdev,
+ const struct switchdev_attr *attr,
+ struct switchdev_trans *trans)
+{
+ int err = 0;
+
+ switch (attr->id) {
+ case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
+ err = port_attr_stp_state_set(netdev, trans,
+ attr->u.stp_state);
+ break;
+ case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
+ err = port_attr_br_flags_set(netdev, trans,
+ attr->u.brport_flags);
+ break;
+ case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
+ /* VLANs are supported by default */
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ break;
+ }
+
+ return err;
+}
+
+static int port_vlans_add(struct net_device *netdev,
+ const struct switchdev_obj_port_vlan *vlan,
+ struct switchdev_trans *trans)
+{
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+ int vid, err;
+
+ if (switchdev_trans_ph_prepare(trans))
+ return 0;
+
+ for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
+ if (!port_priv->ethsw_data->vlans[vid]) {
+ /* this is a new VLAN */
+ err = ethsw_add_vlan(port_priv->ethsw_data, vid);
+ if (err)
+ return err;
+
+ port_priv->ethsw_data->vlans[vid] |= ETHSW_VLAN_GLOBAL;
+ }
+ err = ethsw_port_add_vlan(port_priv, vid, vlan->flags);
+ if (err)
+ break;
+ }
+
+ return err;
+}
+
+static int port_lookup_address(struct net_device *netdev, int is_uc,
+ const unsigned char *addr)
+{
+ struct netdev_hw_addr_list *list = (is_uc) ? &netdev->uc : &netdev->mc;
+ struct netdev_hw_addr *ha;
+
+ netif_addr_lock_bh(netdev);
+ list_for_each_entry(ha, &list->list, list) {
+ if (ether_addr_equal(ha->addr, addr)) {
+ netif_addr_unlock_bh(netdev);
+ return 1;
+ }
+ }
+ netif_addr_unlock_bh(netdev);
+ return 0;
+}
+
+static int port_mdb_add(struct net_device *netdev,
+ const struct switchdev_obj_port_mdb *mdb,
+ struct switchdev_trans *trans)
+{
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+ int err;
+
+ if (switchdev_trans_ph_prepare(trans))
+ return 0;
+
+ /* Check if address is already set on this port */
+ if (port_lookup_address(netdev, 0, mdb->addr))
+ return -EEXIST;
+
+ err = ethsw_port_fdb_add_mc(port_priv, mdb->addr);
+ if (err)
+ return err;
+
+ err = dev_mc_add(netdev, mdb->addr);
+ if (err) {
+ netdev_err(netdev, "dev_mc_add err %d\n", err);
+ ethsw_port_fdb_del_mc(port_priv, mdb->addr);
+ }
+
+ return err;
+}
+
+static int swdev_port_obj_add(struct net_device *netdev,
+ const struct switchdev_obj *obj,
+ struct switchdev_trans *trans)
+{
+ int err;
+
+ switch (obj->id) {
+ case SWITCHDEV_OBJ_ID_PORT_VLAN:
+ err = port_vlans_add(netdev,
+ SWITCHDEV_OBJ_PORT_VLAN(obj),
+ trans);
+ break;
+ case SWITCHDEV_OBJ_ID_PORT_MDB:
+ err = port_mdb_add(netdev,
+ SWITCHDEV_OBJ_PORT_MDB(obj),
+ trans);
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ break;
+ }
+
+ return err;
+}
+
+static int ethsw_port_del_vlan(struct ethsw_port_priv *port_priv, u16 vid)
+{
+ struct ethsw_core *ethsw = port_priv->ethsw_data;
+ struct net_device *netdev = port_priv->netdev;
+ struct dpsw_vlan_if_cfg vcfg;
+ int i, err;
+
+ if (!port_priv->vlans[vid])
+ return -ENOENT;
+
+ if (port_priv->vlans[vid] & ETHSW_VLAN_PVID) {
+ struct dpsw_tci_cfg tci_cfg = { 0 };
+
+ err = ethsw_port_set_tci(port_priv, &tci_cfg);
+ if (err)
+ return err;
+ }
+
+ vcfg.num_ifs = 1;
+ vcfg.if_id[0] = port_priv->idx;
+ if (port_priv->vlans[vid] & ETHSW_VLAN_UNTAGGED) {
+ err = dpsw_vlan_remove_if_untagged(ethsw->mc_io, 0,
+ ethsw->dpsw_handle,
+ vid, &vcfg);
+ if (err) {
+ netdev_err(netdev,
+ "dpsw_vlan_remove_if_untagged err %d\n",
+ err);
+ }
+ port_priv->vlans[vid] &= ~ETHSW_VLAN_UNTAGGED;
+ }
+
+ if (port_priv->vlans[vid] & ETHSW_VLAN_MEMBER) {
+ err = dpsw_vlan_remove_if(ethsw->mc_io, 0, ethsw->dpsw_handle,
+ vid, &vcfg);
+ if (err) {
+ netdev_err(netdev,
+ "dpsw_vlan_remove_if err %d\n", err);
+ return err;
+ }
+ port_priv->vlans[vid] &= ~ETHSW_VLAN_MEMBER;
+
+ /* Delete VLAN from switch if it is no longer configured on
+ * any port
+ */
+ for (i = 0; i < ethsw->sw_attr.num_ifs; i++)
+ if (ethsw->ports[i]->vlans[vid] & ETHSW_VLAN_MEMBER)
+ return 0; /* Found a port member in VID */
+
+ ethsw->vlans[vid] &= ~ETHSW_VLAN_GLOBAL;
+
+ err = ethsw_dellink_switch(ethsw, vid);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int port_vlans_del(struct net_device *netdev,
+ const struct switchdev_obj_port_vlan *vlan)
+{
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+ int vid, err;
+
+ for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
+ err = ethsw_port_del_vlan(port_priv, vid);
+ if (err)
+ break;
+ }
+
+ return err;
+}
+
+static int port_mdb_del(struct net_device *netdev,
+ const struct switchdev_obj_port_mdb *mdb)
+{
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+ int err;
+
+ if (!port_lookup_address(netdev, 0, mdb->addr))
+ return -ENOENT;
+
+ err = ethsw_port_fdb_del_mc(port_priv, mdb->addr);
+ if (err)
+ return err;
+
+ err = dev_mc_del(netdev, mdb->addr);
+ if (err) {
+ netdev_err(netdev, "dev_mc_del err %d\n", err);
+ return err;
+ }
+
+ return err;
+}
+
+static int swdev_port_obj_del(struct net_device *netdev,
+ const struct switchdev_obj *obj)
+{
+ int err;
+
+ switch (obj->id) {
+ case SWITCHDEV_OBJ_ID_PORT_VLAN:
+ err = port_vlans_del(netdev, SWITCHDEV_OBJ_PORT_VLAN(obj));
+ break;
+ case SWITCHDEV_OBJ_ID_PORT_MDB:
+ err = port_mdb_del(netdev, SWITCHDEV_OBJ_PORT_MDB(obj));
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ break;
+ }
+ return err;
+}
+
+static const struct switchdev_ops ethsw_port_switchdev_ops = {
+ .switchdev_port_attr_get = swdev_port_attr_get,
+ .switchdev_port_attr_set = swdev_port_attr_set,
+ .switchdev_port_obj_add = swdev_port_obj_add,
+ .switchdev_port_obj_del = swdev_port_obj_del,
+};
+
+/* For the moment, only flood setting needs to be updated */
+static int port_bridge_join(struct net_device *netdev,
+ struct net_device *upper_dev)
+{
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+ struct ethsw_core *ethsw = port_priv->ethsw_data;
+ int i, err;
+
+ for (i = 0; i < ethsw->sw_attr.num_ifs; i++)
+ if (ethsw->ports[i]->bridge_dev &&
+ (ethsw->ports[i]->bridge_dev != upper_dev)) {
+ netdev_err(netdev,
+ "Another switch port is connected to %s\n",
+ ethsw->ports[i]->bridge_dev->name);
+ return -EINVAL;
+ }
+
+ /* Enable flooding */
+ err = ethsw_port_set_flood(port_priv, 1);
+ if (!err)
+ port_priv->bridge_dev = upper_dev;
+
+ return err;
+}
+
+static int port_bridge_leave(struct net_device *netdev)
+{
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+ int err;
+
+ /* Disable flooding */
+ err = ethsw_port_set_flood(port_priv, 0);
+ if (!err)
+ port_priv->bridge_dev = NULL;
+
+ return err;
+}
+
+static int port_netdevice_event(struct notifier_block *unused,
+ unsigned long event, void *ptr)
+{
+ struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
+ struct netdev_notifier_changeupper_info *info = ptr;
+ struct net_device *upper_dev;
+ int err = 0;
+
+ if (netdev->netdev_ops != &ethsw_port_ops)
+ return NOTIFY_DONE;
+
+ /* Handle just upper dev link/unlink for the moment */
+ if (event == NETDEV_CHANGEUPPER) {
+ upper_dev = info->upper_dev;
+ if (netif_is_bridge_master(upper_dev)) {
+ if (info->linking)
+ err = port_bridge_join(netdev, upper_dev);
+ else
+ err = port_bridge_leave(netdev);
+ }
+ }
+
+ return notifier_from_errno(err);
+}
+
+static struct notifier_block port_nb __read_mostly = {
+ .notifier_call = port_netdevice_event,
+};
+
+struct ethsw_switchdev_event_work {
+ struct work_struct work;
+ struct switchdev_notifier_fdb_info fdb_info;
+ struct net_device *dev;
+ unsigned long event;
+};
+
+static void ethsw_switchdev_event_work(struct work_struct *work)
+{
+ struct ethsw_switchdev_event_work *switchdev_work =
+ container_of(work, struct ethsw_switchdev_event_work, work);
+ struct net_device *dev = switchdev_work->dev;
+ struct switchdev_notifier_fdb_info *fdb_info;
+ struct ethsw_port_priv *port_priv;
+
+ rtnl_lock();
+ port_priv = netdev_priv(dev);
+ fdb_info = &switchdev_work->fdb_info;
+
+ switch (switchdev_work->event) {
+ case SWITCHDEV_FDB_ADD_TO_DEVICE:
+ if (is_unicast_ether_addr(fdb_info->addr))
+ ethsw_port_fdb_add_uc(netdev_priv(dev), fdb_info->addr);
+ else
+ ethsw_port_fdb_add_mc(netdev_priv(dev), fdb_info->addr);
+ break;
+ case SWITCHDEV_FDB_DEL_TO_DEVICE:
+ if (is_unicast_ether_addr(fdb_info->addr))
+ ethsw_port_fdb_del_uc(netdev_priv(dev), fdb_info->addr);
+ else
+ ethsw_port_fdb_del_mc(netdev_priv(dev), fdb_info->addr);
+ break;
+ }
+
+ rtnl_unlock();
+ kfree(switchdev_work->fdb_info.addr);
+ kfree(switchdev_work);
+ dev_put(dev);
+}
+
+/* Called under rcu_read_lock() */
+static int port_switchdev_event(struct notifier_block *unused,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
+ struct ethsw_switchdev_event_work *switchdev_work;
+ struct switchdev_notifier_fdb_info *fdb_info = ptr;
+
+ switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
+ if (!switchdev_work)
+ return NOTIFY_BAD;
+
+ INIT_WORK(&switchdev_work->work, ethsw_switchdev_event_work);
+ switchdev_work->dev = dev;
+ switchdev_work->event = event;
+
+ switch (event) {
+ case SWITCHDEV_FDB_ADD_TO_DEVICE:
+ case SWITCHDEV_FDB_DEL_TO_DEVICE:
+ memcpy(&switchdev_work->fdb_info, ptr,
+ sizeof(switchdev_work->fdb_info));
+ switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
+ if (!switchdev_work->fdb_info.addr)
+ goto err_addr_alloc;
+
+ ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
+ fdb_info->addr);
+
+ /* Take a reference on the device to avoid being freed. */
+ dev_hold(dev);
+ break;
+ default:
+ return NOTIFY_DONE;
+ }
+
+ queue_work(ethsw_owq, &switchdev_work->work);
+
+ return NOTIFY_DONE;
+
+err_addr_alloc:
+ kfree(switchdev_work);
+ return NOTIFY_BAD;
+}
+
+static struct notifier_block port_switchdev_nb = {
+ .notifier_call = port_switchdev_event,
+};
+
+static int ethsw_register_notifier(struct device *dev)
+{
+ int err;
+
+ err = register_netdevice_notifier(&port_nb);
+ if (err) {
+ dev_err(dev, "Failed to register netdev notifier\n");
+ return err;
+ }
+
+ err = register_switchdev_notifier(&port_switchdev_nb);
+ if (err) {
+ dev_err(dev, "Failed to register switchdev notifier\n");
+ goto err_switchdev_nb;
+ }
+
+ return 0;
+
+err_switchdev_nb:
+ unregister_netdevice_notifier(&port_nb);
+ return err;
+}
+
+static int ethsw_open(struct ethsw_core *ethsw)
+{
+ struct ethsw_port_priv *port_priv = NULL;
+ int i, err;
+
+ err = dpsw_enable(ethsw->mc_io, 0, ethsw->dpsw_handle);
+ if (err) {
+ dev_err(ethsw->dev, "dpsw_enable err %d\n", err);
+ return err;
+ }
+
+ for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
+ port_priv = ethsw->ports[i];
+ err = dev_open(port_priv->netdev);
+ if (err) {
+ netdev_err(port_priv->netdev, "dev_open err %d\n", err);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static int ethsw_stop(struct ethsw_core *ethsw)
+{
+ struct ethsw_port_priv *port_priv = NULL;
+ int i, err;
+
+ for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
+ port_priv = ethsw->ports[i];
+ dev_close(port_priv->netdev);
+ }
+
+ err = dpsw_disable(ethsw->mc_io, 0, ethsw->dpsw_handle);
+ if (err) {
+ dev_err(ethsw->dev, "dpsw_disable err %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static int ethsw_init(struct fsl_mc_device *sw_dev)
+{
+ struct device *dev = &sw_dev->dev;
+ struct ethsw_core *ethsw = dev_get_drvdata(dev);
+ u16 version_major, version_minor, i;
+ struct dpsw_stp_cfg stp_cfg;
+ int err;
+
+ ethsw->dev_id = sw_dev->obj_desc.id;
+
+ err = dpsw_open(ethsw->mc_io, 0, ethsw->dev_id, &ethsw->dpsw_handle);
+ if (err) {
+ dev_err(dev, "dpsw_open err %d\n", err);
+ return err;
+ }
+
+ err = dpsw_get_attributes(ethsw->mc_io, 0, ethsw->dpsw_handle,
+ &ethsw->sw_attr);
+ if (err) {
+ dev_err(dev, "dpsw_get_attributes err %d\n", err);
+ goto err_close;
+ }
+
+ err = dpsw_get_api_version(ethsw->mc_io, 0,
+ &version_major,
+ &version_minor);
+ if (err) {
+ dev_err(dev, "dpsw_get_api_version err %d\n", err);
+ goto err_close;
+ }
+
+ /* Minimum supported DPSW version check */
+ if (version_major < DPSW_MIN_VER_MAJOR ||
+ (version_major == DPSW_MIN_VER_MAJOR &&
+ version_minor < DPSW_MIN_VER_MINOR)) {
+ dev_err(dev, "DPSW version %d:%d not supported. Use %d.%d or greater.\n",
+ version_major,
+ version_minor,
+ DPSW_MIN_VER_MAJOR, DPSW_MIN_VER_MINOR);
+ err = -ENOTSUPP;
+ goto err_close;
+ }
+
+ err = dpsw_reset(ethsw->mc_io, 0, ethsw->dpsw_handle);
+ if (err) {
+ dev_err(dev, "dpsw_reset err %d\n", err);
+ goto err_close;
+ }
+
+ err = dpsw_fdb_set_learning_mode(ethsw->mc_io, 0, ethsw->dpsw_handle, 0,
+ DPSW_FDB_LEARNING_MODE_HW);
+ if (err) {
+ dev_err(dev, "dpsw_fdb_set_learning_mode err %d\n", err);
+ goto err_close;
+ }
+
+ stp_cfg.vlan_id = DEFAULT_VLAN_ID;
+ stp_cfg.state = DPSW_STP_STATE_FORWARDING;
+
+ for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
+ err = dpsw_if_set_stp(ethsw->mc_io, 0, ethsw->dpsw_handle, i,
+ &stp_cfg);
+ if (err) {
+ dev_err(dev, "dpsw_if_set_stp err %d for port %d\n",
+ err, i);
+ goto err_close;
+ }
+
+ err = dpsw_if_set_broadcast(ethsw->mc_io, 0,
+ ethsw->dpsw_handle, i, 1);
+ if (err) {
+ dev_err(dev,
+ "dpsw_if_set_broadcast err %d for port %d\n",
+ err, i);
+ goto err_close;
+ }
+ }
+
+ ethsw_owq = alloc_ordered_workqueue("%s_ordered", WQ_MEM_RECLAIM,
+ "ethsw");
+ if (!ethsw_owq) {
+ err = -ENOMEM;
+ goto err_close;
+ }
+
+ err = ethsw_register_notifier(dev);
+ if (err)
+ goto err_destroy_ordered_workqueue;
+
+ return 0;
+
+err_destroy_ordered_workqueue:
+ destroy_workqueue(ethsw_owq);
+
+err_close:
+ dpsw_close(ethsw->mc_io, 0, ethsw->dpsw_handle);
+ return err;
+}
+
+static int ethsw_port_init(struct ethsw_port_priv *port_priv, u16 port)
+{
+ const char def_mcast[ETH_ALEN] = {0x01, 0x00, 0x5e, 0x00, 0x00, 0x01};
+ struct net_device *netdev = port_priv->netdev;
+ struct ethsw_core *ethsw = port_priv->ethsw_data;
+ struct dpsw_tci_cfg tci_cfg = {0};
+ struct dpsw_vlan_if_cfg vcfg;
+ int err;
+
+ /* Switch starts with all ports configured to VLAN 1. Need to
+ * remove this setting to allow configuration at bridge join
+ */
+ vcfg.num_ifs = 1;
+ vcfg.if_id[0] = port_priv->idx;
+
+ err = dpsw_vlan_remove_if_untagged(ethsw->mc_io, 0, ethsw->dpsw_handle,
+ DEFAULT_VLAN_ID, &vcfg);
+ if (err) {
+ netdev_err(netdev, "dpsw_vlan_remove_if_untagged err %d\n",
+ err);
+ return err;
+ }
+
+ err = ethsw_port_set_tci(port_priv, &tci_cfg);
+ if (err)
+ return err;
+
+ err = dpsw_vlan_remove_if(ethsw->mc_io, 0, ethsw->dpsw_handle,
+ DEFAULT_VLAN_ID, &vcfg);
+ if (err) {
+ netdev_err(netdev, "dpsw_vlan_remove_if err %d\n", err);
+ return err;
+ }
+
+ err = ethsw_port_fdb_add_mc(port_priv, def_mcast);
+
+ return err;
+}
+
+static void ethsw_unregister_notifier(struct device *dev)
+{
+ int err;
+
+ err = unregister_switchdev_notifier(&port_switchdev_nb);
+ if (err)
+ dev_err(dev,
+ "Failed to unregister switchdev notifier (%d)\n", err);
+
+ err = unregister_netdevice_notifier(&port_nb);
+ if (err)
+ dev_err(dev,
+ "Failed to unregister netdev notifier (%d)\n", err);
+}
+
+static void ethsw_takedown(struct fsl_mc_device *sw_dev)
+{
+ struct device *dev = &sw_dev->dev;
+ struct ethsw_core *ethsw = dev_get_drvdata(dev);
+ int err;
+
+ ethsw_unregister_notifier(dev);
+
+ err = dpsw_close(ethsw->mc_io, 0, ethsw->dpsw_handle);
+ if (err)
+ dev_warn(dev, "dpsw_close err %d\n", err);
+}
+
+static int ethsw_remove(struct fsl_mc_device *sw_dev)
+{
+ struct ethsw_port_priv *port_priv;
+ struct ethsw_core *ethsw;
+ struct device *dev;
+ int i;
+
+ dev = &sw_dev->dev;
+ ethsw = dev_get_drvdata(dev);
+
+ ethsw_teardown_irqs(sw_dev);
+
+ destroy_workqueue(ethsw_owq);
+
+ rtnl_lock();
+ ethsw_stop(ethsw);
+ rtnl_unlock();
+
+ for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
+ port_priv = ethsw->ports[i];
+ unregister_netdev(port_priv->netdev);
+ free_netdev(port_priv->netdev);
+ }
+ kfree(ethsw->ports);
+
+ ethsw_takedown(sw_dev);
+ fsl_mc_portal_free(ethsw->mc_io);
+
+ kfree(ethsw);
+
+ dev_set_drvdata(dev, NULL);
+
+ return 0;
+}
+
+static int ethsw_probe_port(struct ethsw_core *ethsw, u16 port_idx)
+{
+ struct ethsw_port_priv *port_priv;
+ struct device *dev = ethsw->dev;
+ struct net_device *port_netdev;
+ int err;
+
+ port_netdev = alloc_etherdev(sizeof(struct ethsw_port_priv));
+ if (!port_netdev) {
+ dev_err(dev, "alloc_etherdev error\n");
+ return -ENOMEM;
+ }
+
+ port_priv = netdev_priv(port_netdev);
+ port_priv->netdev = port_netdev;
+ port_priv->ethsw_data = ethsw;
+
+ port_priv->idx = port_idx;
+ port_priv->stp_state = BR_STATE_FORWARDING;
+
+ /* Flooding is implicitly enabled */
+ port_priv->flood = true;
+
+ SET_NETDEV_DEV(port_netdev, dev);
+ port_netdev->netdev_ops = &ethsw_port_ops;
+ port_netdev->ethtool_ops = &ethsw_port_ethtool_ops;
+ port_netdev->switchdev_ops = &ethsw_port_switchdev_ops;
+
+ /* Set MTU limits */
+ port_netdev->min_mtu = ETH_MIN_MTU;
+ port_netdev->max_mtu = ETHSW_MAX_FRAME_LENGTH;
+
+ err = register_netdev(port_netdev);
+ if (err < 0) {
+ dev_err(dev, "register_netdev error %d\n", err);
+ free_netdev(port_netdev);
+ return err;
+ }
+
+ ethsw->ports[port_idx] = port_priv;
+
+ return ethsw_port_init(port_priv, port_idx);
+}
+
+static int ethsw_probe(struct fsl_mc_device *sw_dev)
+{
+ struct device *dev = &sw_dev->dev;
+ struct ethsw_core *ethsw;
+ int i, err;
+
+ /* Allocate switch core*/
+ ethsw = kzalloc(sizeof(*ethsw), GFP_KERNEL);
+
+ if (!ethsw)
+ return -ENOMEM;
+
+ ethsw->dev = dev;
+ dev_set_drvdata(dev, ethsw);
+
+ err = fsl_mc_portal_allocate(sw_dev, 0, &ethsw->mc_io);
+ if (err) {
+ if (err == -ENXIO)
+ err = -EPROBE_DEFER;
+ else
+ dev_err(dev, "fsl_mc_portal_allocate err %d\n", err);
+ goto err_free_drvdata;
+ }
+
+ err = ethsw_init(sw_dev);
+ if (err)
+ goto err_free_cmdport;
+
+ /* DEFAULT_VLAN_ID is implicitly configured on the switch */
+ ethsw->vlans[DEFAULT_VLAN_ID] = ETHSW_VLAN_MEMBER;
+
+ /* Learning is implicitly enabled */
+ ethsw->learning = true;
+
+ ethsw->ports = kcalloc(ethsw->sw_attr.num_ifs, sizeof(*ethsw->ports),
+ GFP_KERNEL);
+ if (!(ethsw->ports)) {
+ err = -ENOMEM;
+ goto err_takedown;
+ }
+
+ for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
+ err = ethsw_probe_port(ethsw, i);
+ if (err)
+ goto err_free_ports;
+ }
+
+ /* Switch starts up enabled */
+ rtnl_lock();
+ err = ethsw_open(ethsw);
+ rtnl_unlock();
+ if (err)
+ goto err_free_ports;
+
+ /* Setup IRQs */
+ err = ethsw_setup_irqs(sw_dev);
+ if (err)
+ goto err_stop;
+
+ dev_info(dev, "probed %d port switch\n", ethsw->sw_attr.num_ifs);
+ return 0;
+
+err_stop:
+ rtnl_lock();
+ ethsw_stop(ethsw);
+ rtnl_unlock();
+
+err_free_ports:
+ /* Cleanup registered ports only */
+ for (i--; i >= 0; i--) {
+ unregister_netdev(ethsw->ports[i]->netdev);
+ free_netdev(ethsw->ports[i]->netdev);
+ }
+ kfree(ethsw->ports);
+
+err_takedown:
+ ethsw_takedown(sw_dev);
+
+err_free_cmdport:
+ fsl_mc_portal_free(ethsw->mc_io);
+
+err_free_drvdata:
+ kfree(ethsw);
+ dev_set_drvdata(dev, NULL);
+
+ return err;
+}
+
+static const struct fsl_mc_device_id ethsw_match_id_table[] = {
+ {
+ .vendor = FSL_MC_VENDOR_FREESCALE,
+ .obj_type = "dpsw",
+ },
+ { .vendor = 0x0 }
+};
+MODULE_DEVICE_TABLE(fslmc, ethsw_match_id_table);
+
+static struct fsl_mc_driver eth_sw_drv = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = ethsw_probe,
+ .remove = ethsw_remove,
+ .match_id_table = ethsw_match_id_table
+};
+
+module_fsl_mc_driver(eth_sw_drv);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("DPAA2 Ethernet Switch Driver");
diff --git a/drivers/staging/fsl-dpaa2/ethsw/ethsw.h b/drivers/staging/fsl-dpaa2/ethsw/ethsw.h
new file mode 100644
index 000000000000..069c99bfba74
--- /dev/null
+++ b/drivers/staging/fsl-dpaa2/ethsw/ethsw.h
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * DPAA2 Ethernet Switch declarations
+ *
+ * Copyright 2014-2016 Freescale Semiconductor Inc.
+ * Copyright 2017-2018 NXP
+ *
+ */
+
+#ifndef __ETHSW_H
+#define __ETHSW_H
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/if_vlan.h>
+#include <uapi/linux/if_bridge.h>
+#include <net/switchdev.h>
+#include <linux/if_bridge.h>
+
+#include "dpsw.h"
+
+/* Number of IRQs supported */
+#define DPSW_IRQ_NUM 2
+
+#define ETHSW_VLAN_MEMBER 1
+#define ETHSW_VLAN_UNTAGGED 2
+#define ETHSW_VLAN_PVID 4
+#define ETHSW_VLAN_GLOBAL 8
+
+/* Maximum Frame Length supported by HW (currently 10k) */
+#define DPAA2_MFL (10 * 1024)
+#define ETHSW_MAX_FRAME_LENGTH (DPAA2_MFL - VLAN_ETH_HLEN - ETH_FCS_LEN)
+#define ETHSW_L2_MAX_FRM(mtu) ((mtu) + VLAN_ETH_HLEN + ETH_FCS_LEN)
+
+extern const struct ethtool_ops ethsw_port_ethtool_ops;
+
+struct ethsw_core;
+
+/* Per port private data */
+struct ethsw_port_priv {
+ struct net_device *netdev;
+ u16 idx;
+ struct ethsw_core *ethsw_data;
+ u8 link_state;
+ u8 stp_state;
+ bool flood;
+
+ u8 vlans[VLAN_VID_MASK + 1];
+ u16 pvid;
+ struct net_device *bridge_dev;
+};
+
+/* Switch data */
+struct ethsw_core {
+ struct device *dev;
+ struct fsl_mc_io *mc_io;
+ u16 dpsw_handle;
+ struct dpsw_attr sw_attr;
+ int dev_id;
+ struct ethsw_port_priv **ports;
+
+ u8 vlans[VLAN_VID_MASK + 1];
+ bool learning;
+};
+
+#endif /* __ETHSW_H */
diff --git a/drivers/staging/fsl-mc/TODO b/drivers/staging/fsl-mc/TODO
deleted file mode 100644
index 54a8bc69222e..000000000000
--- a/drivers/staging/fsl-mc/TODO
+++ /dev/null
@@ -1,18 +0,0 @@
-* Add at least one device driver for a DPAA2 object (child device of the
- fsl-mc bus). Most likely candidate for this is adding DPAA2 Ethernet
- driver support, which depends on drivers for several objects: DPNI,
- DPIO, DPMAC. Other pre-requisites include:
-
- * MC firmware uprev. The MC firmware upon which the fsl-mc
- bus driver and DPAA2 object drivers are based is continuing
- to evolve, so minor updates are needed to keep in sync with binary
- interface changes to the MC.
-
-* Cleanup
-
-Please send any patches to Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
-german.rivera@freescale.com, devel@driverdev.osuosl.org,
-linux-kernel@vger.kernel.org
-
-[1] https://lkml.org/lkml/2015/7/9/93
-[2] https://lkml.org/lkml/2015/7/7/712
diff --git a/drivers/staging/fsl-mc/bus/Kconfig b/drivers/staging/fsl-mc/bus/Kconfig
index b35ef7ee6901..342453035269 100644
--- a/drivers/staging/fsl-mc/bus/Kconfig
+++ b/drivers/staging/fsl-mc/bus/Kconfig
@@ -5,19 +5,9 @@
# Copyright (C) 2014-2016 Freescale Semiconductor, Inc.
#
-config FSL_MC_BUS
- bool "QorIQ DPAA2 fsl-mc bus driver"
- depends on OF && (ARCH_LAYERSCAPE || (COMPILE_TEST && (ARM || ARM64 || X86_LOCAL_APIC || PPC)))
- select GENERIC_MSI_IRQ_DOMAIN
- help
- Driver to enable the bus infrastructure for the QorIQ DPAA2
- architecture. The fsl-mc bus driver handles discovery of
- DPAA2 objects (which are represented as Linux devices) and
- binding objects to drivers.
-
config FSL_MC_DPIO
tristate "QorIQ DPAA2 DPIO driver"
- depends on FSL_MC_BUS && ARCH_LAYERSCAPE
+ depends on FSL_MC_BUS
help
Driver for the DPAA2 DPIO object. A DPIO provides queue and
buffer management facilities for software to interact with
diff --git a/drivers/staging/fsl-mc/bus/Makefile b/drivers/staging/fsl-mc/bus/Makefile
index 29059db95ecc..21d8ebc8ce21 100644
--- a/drivers/staging/fsl-mc/bus/Makefile
+++ b/drivers/staging/fsl-mc/bus/Makefile
@@ -4,19 +4,6 @@
#
# Copyright (C) 2014 Freescale Semiconductor, Inc.
#
-obj-$(CONFIG_FSL_MC_BUS) += mc-bus-driver.o
-
-mc-bus-driver-objs := fsl-mc-bus.o \
- mc-sys.o \
- mc-io.o \
- dprc.o \
- dprc-driver.o \
- fsl-mc-allocator.o \
- fsl-mc-msi.o \
- irq-gic-v3-its-fsl-mc-msi.o \
- dpmcp.o \
- dpbp.o \
- dpcon.o
# MC DPIO driver
obj-$(CONFIG_FSL_MC_DPIO) += dpio/
diff --git a/drivers/staging/fsl-mc/bus/dpbp-cmd.h b/drivers/staging/fsl-mc/bus/dpbp-cmd.h
deleted file mode 100644
index 0b7f5c041f19..000000000000
--- a/drivers/staging/fsl-mc/bus/dpbp-cmd.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
-/*
- * Copyright 2013-2016 Freescale Semiconductor Inc.
- *
- */
-#ifndef _FSL_DPBP_CMD_H
-#define _FSL_DPBP_CMD_H
-
-/* DPBP Version */
-#define DPBP_VER_MAJOR 3
-#define DPBP_VER_MINOR 2
-
-/* Command versioning */
-#define DPBP_CMD_BASE_VERSION 1
-#define DPBP_CMD_ID_OFFSET 4
-
-#define DPBP_CMD(id) (((id) << DPBP_CMD_ID_OFFSET) | DPBP_CMD_BASE_VERSION)
-
-/* Command IDs */
-#define DPBP_CMDID_CLOSE DPBP_CMD(0x800)
-#define DPBP_CMDID_OPEN DPBP_CMD(0x804)
-#define DPBP_CMDID_GET_API_VERSION DPBP_CMD(0xa04)
-
-#define DPBP_CMDID_ENABLE DPBP_CMD(0x002)
-#define DPBP_CMDID_DISABLE DPBP_CMD(0x003)
-#define DPBP_CMDID_GET_ATTR DPBP_CMD(0x004)
-#define DPBP_CMDID_RESET DPBP_CMD(0x005)
-#define DPBP_CMDID_IS_ENABLED DPBP_CMD(0x006)
-
-struct dpbp_cmd_open {
- __le32 dpbp_id;
-};
-
-struct dpbp_cmd_destroy {
- __le32 object_id;
-};
-
-#define DPBP_ENABLE 0x1
-
-struct dpbp_rsp_is_enabled {
- u8 enabled;
-};
-
-struct dpbp_rsp_get_attributes {
- /* response word 0 */
- __le16 pad;
- __le16 bpid;
- __le32 id;
- /* response word 1 */
- __le16 version_major;
- __le16 version_minor;
-};
-
-#endif /* _FSL_DPBP_CMD_H */
diff --git a/drivers/staging/fsl-mc/bus/dpbp.c b/drivers/staging/fsl-mc/bus/dpbp.c
deleted file mode 100644
index a4df84668d5b..000000000000
--- a/drivers/staging/fsl-mc/bus/dpbp.c
+++ /dev/null
@@ -1,253 +0,0 @@
-// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
-/*
- * Copyright 2013-2016 Freescale Semiconductor Inc.
- *
- */
-#include <linux/kernel.h>
-#include "../include/mc.h"
-#include "../include/dpbp.h"
-
-#include "dpbp-cmd.h"
-
-/**
- * dpbp_open() - Open a control session for the specified object.
- * @mc_io: Pointer to MC portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @dpbp_id: DPBP unique ID
- * @token: Returned token; use in subsequent API calls
- *
- * This function can be used to open a control session for an
- * already created object; an object may have been declared in
- * the DPL or by calling the dpbp_create function.
- * This function returns a unique authentication token,
- * associated with the specific object ID and the specific MC
- * portal; this token must be used in all subsequent commands for
- * this specific object
- *
- * Return: '0' on Success; Error code otherwise.
- */
-int dpbp_open(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- int dpbp_id,
- u16 *token)
-{
- struct mc_command cmd = { 0 };
- struct dpbp_cmd_open *cmd_params;
- int err;
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPBP_CMDID_OPEN,
- cmd_flags, 0);
- cmd_params = (struct dpbp_cmd_open *)cmd.params;
- cmd_params->dpbp_id = cpu_to_le32(dpbp_id);
-
- /* send command to mc*/
- err = mc_send_command(mc_io, &cmd);
- if (err)
- return err;
-
- /* retrieve response parameters */
- *token = mc_cmd_hdr_read_token(&cmd);
-
- return err;
-}
-EXPORT_SYMBOL_GPL(dpbp_open);
-
-/**
- * dpbp_close() - Close the control session of the object
- * @mc_io: Pointer to MC portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @token: Token of DPBP object
- *
- * After this function is called, no further operations are
- * allowed on the object without opening a new control session.
- *
- * Return: '0' on Success; Error code otherwise.
- */
-int dpbp_close(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token)
-{
- struct mc_command cmd = { 0 };
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPBP_CMDID_CLOSE, cmd_flags,
- token);
-
- /* send command to mc*/
- return mc_send_command(mc_io, &cmd);
-}
-EXPORT_SYMBOL_GPL(dpbp_close);
-
-/**
- * dpbp_enable() - Enable the DPBP.
- * @mc_io: Pointer to MC portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @token: Token of DPBP object
- *
- * Return: '0' on Success; Error code otherwise.
- */
-int dpbp_enable(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token)
-{
- struct mc_command cmd = { 0 };
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPBP_CMDID_ENABLE, cmd_flags,
- token);
-
- /* send command to mc*/
- return mc_send_command(mc_io, &cmd);
-}
-EXPORT_SYMBOL_GPL(dpbp_enable);
-
-/**
- * dpbp_disable() - Disable the DPBP.
- * @mc_io: Pointer to MC portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @token: Token of DPBP object
- *
- * Return: '0' on Success; Error code otherwise.
- */
-int dpbp_disable(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token)
-{
- struct mc_command cmd = { 0 };
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPBP_CMDID_DISABLE,
- cmd_flags, token);
-
- /* send command to mc*/
- return mc_send_command(mc_io, &cmd);
-}
-EXPORT_SYMBOL_GPL(dpbp_disable);
-
-/**
- * dpbp_is_enabled() - Check if the DPBP is enabled.
- * @mc_io: Pointer to MC portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @token: Token of DPBP object
- * @en: Returns '1' if object is enabled; '0' otherwise
- *
- * Return: '0' on Success; Error code otherwise.
- */
-int dpbp_is_enabled(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token,
- int *en)
-{
- struct mc_command cmd = { 0 };
- struct dpbp_rsp_is_enabled *rsp_params;
- int err;
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPBP_CMDID_IS_ENABLED, cmd_flags,
- token);
-
- /* send command to mc*/
- err = mc_send_command(mc_io, &cmd);
- if (err)
- return err;
-
- /* retrieve response parameters */
- rsp_params = (struct dpbp_rsp_is_enabled *)cmd.params;
- *en = rsp_params->enabled & DPBP_ENABLE;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(dpbp_is_enabled);
-
-/**
- * dpbp_reset() - Reset the DPBP, returns the object to initial state.
- * @mc_io: Pointer to MC portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @token: Token of DPBP object
- *
- * Return: '0' on Success; Error code otherwise.
- */
-int dpbp_reset(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token)
-{
- struct mc_command cmd = { 0 };
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPBP_CMDID_RESET,
- cmd_flags, token);
-
- /* send command to mc*/
- return mc_send_command(mc_io, &cmd);
-}
-EXPORT_SYMBOL_GPL(dpbp_reset);
-
-/**
- * dpbp_get_attributes - Retrieve DPBP attributes.
- *
- * @mc_io: Pointer to MC portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @token: Token of DPBP object
- * @attr: Returned object's attributes
- *
- * Return: '0' on Success; Error code otherwise.
- */
-int dpbp_get_attributes(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token,
- struct dpbp_attr *attr)
-{
- struct mc_command cmd = { 0 };
- struct dpbp_rsp_get_attributes *rsp_params;
- int err;
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPBP_CMDID_GET_ATTR,
- cmd_flags, token);
-
- /* send command to mc*/
- err = mc_send_command(mc_io, &cmd);
- if (err)
- return err;
-
- /* retrieve response parameters */
- rsp_params = (struct dpbp_rsp_get_attributes *)cmd.params;
- attr->bpid = le16_to_cpu(rsp_params->bpid);
- attr->id = le32_to_cpu(rsp_params->id);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(dpbp_get_attributes);
-
-/**
- * dpbp_get_api_version - Get Data Path Buffer Pool API version
- * @mc_io: Pointer to Mc portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @major_ver: Major version of Buffer Pool API
- * @minor_ver: Minor version of Buffer Pool API
- *
- * Return: '0' on Success; Error code otherwise.
- */
-int dpbp_get_api_version(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 *major_ver,
- u16 *minor_ver)
-{
- struct mc_command cmd = { 0 };
- int err;
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPBP_CMDID_GET_API_VERSION,
- cmd_flags, 0);
-
- /* send command to mc */
- err = mc_send_command(mc_io, &cmd);
- if (err)
- return err;
-
- /* retrieve response parameters */
- mc_cmd_read_api_version(&cmd, major_ver, minor_ver);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(dpbp_get_api_version);
diff --git a/drivers/staging/fsl-mc/bus/dpcon-cmd.h b/drivers/staging/fsl-mc/bus/dpcon-cmd.h
deleted file mode 100644
index 27fa09877970..000000000000
--- a/drivers/staging/fsl-mc/bus/dpcon-cmd.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
-/*
- * Copyright 2013-2016 Freescale Semiconductor Inc.
- *
- */
-#ifndef _FSL_DPCON_CMD_H
-#define _FSL_DPCON_CMD_H
-
-/* DPCON Version */
-#define DPCON_VER_MAJOR 3
-#define DPCON_VER_MINOR 2
-
-/* Command versioning */
-#define DPCON_CMD_BASE_VERSION 1
-#define DPCON_CMD_ID_OFFSET 4
-
-#define DPCON_CMD(id) (((id) << DPCON_CMD_ID_OFFSET) | DPCON_CMD_BASE_VERSION)
-
-/* Command IDs */
-#define DPCON_CMDID_CLOSE DPCON_CMD(0x800)
-#define DPCON_CMDID_OPEN DPCON_CMD(0x808)
-
-#define DPCON_CMDID_ENABLE DPCON_CMD(0x002)
-#define DPCON_CMDID_DISABLE DPCON_CMD(0x003)
-#define DPCON_CMDID_GET_ATTR DPCON_CMD(0x004)
-#define DPCON_CMDID_RESET DPCON_CMD(0x005)
-
-#define DPCON_CMDID_SET_NOTIFICATION DPCON_CMD(0x100)
-
-struct dpcon_cmd_open {
- __le32 dpcon_id;
-};
-
-#define DPCON_ENABLE 1
-
-struct dpcon_rsp_get_attr {
- /* response word 0 */
- __le32 id;
- __le16 qbman_ch_id;
- u8 num_priorities;
- u8 pad;
-};
-
-struct dpcon_cmd_set_notification {
- /* cmd word 0 */
- __le32 dpio_id;
- u8 priority;
- u8 pad[3];
- /* cmd word 1 */
- __le64 user_ctx;
-};
-
-#endif /* _FSL_DPCON_CMD_H */
diff --git a/drivers/staging/fsl-mc/bus/dpcon.c b/drivers/staging/fsl-mc/bus/dpcon.c
deleted file mode 100644
index 8f84d7b5465c..000000000000
--- a/drivers/staging/fsl-mc/bus/dpcon.c
+++ /dev/null
@@ -1,222 +0,0 @@
-// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
-/*
- * Copyright 2013-2016 Freescale Semiconductor Inc.
- *
- */
-#include <linux/kernel.h>
-#include "../include/mc.h"
-#include "../include/dpcon.h"
-
-#include "dpcon-cmd.h"
-
-/**
- * dpcon_open() - Open a control session for the specified object
- * @mc_io: Pointer to MC portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @dpcon_id: DPCON unique ID
- * @token: Returned token; use in subsequent API calls
- *
- * This function can be used to open a control session for an
- * already created object; an object may have been declared in
- * the DPL or by calling the dpcon_create() function.
- * This function returns a unique authentication token,
- * associated with the specific object ID and the specific MC
- * portal; this token must be used in all subsequent commands for
- * this specific object.
- *
- * Return: '0' on Success; Error code otherwise.
- */
-int dpcon_open(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- int dpcon_id,
- u16 *token)
-{
- struct mc_command cmd = { 0 };
- struct dpcon_cmd_open *dpcon_cmd;
- int err;
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPCON_CMDID_OPEN,
- cmd_flags,
- 0);
- dpcon_cmd = (struct dpcon_cmd_open *)cmd.params;
- dpcon_cmd->dpcon_id = cpu_to_le32(dpcon_id);
-
- /* send command to mc*/
- err = mc_send_command(mc_io, &cmd);
- if (err)
- return err;
-
- /* retrieve response parameters */
- *token = mc_cmd_hdr_read_token(&cmd);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(dpcon_open);
-
-/**
- * dpcon_close() - Close the control session of the object
- * @mc_io: Pointer to MC portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @token: Token of DPCON object
- *
- * After this function is called, no further operations are
- * allowed on the object without opening a new control session.
- *
- * Return: '0' on Success; Error code otherwise.
- */
-int dpcon_close(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token)
-{
- struct mc_command cmd = { 0 };
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPCON_CMDID_CLOSE,
- cmd_flags,
- token);
-
- /* send command to mc*/
- return mc_send_command(mc_io, &cmd);
-}
-EXPORT_SYMBOL_GPL(dpcon_close);
-
-/**
- * dpcon_enable() - Enable the DPCON
- * @mc_io: Pointer to MC portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @token: Token of DPCON object
- *
- * Return: '0' on Success; Error code otherwise
- */
-int dpcon_enable(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token)
-{
- struct mc_command cmd = { 0 };
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPCON_CMDID_ENABLE,
- cmd_flags,
- token);
-
- /* send command to mc*/
- return mc_send_command(mc_io, &cmd);
-}
-EXPORT_SYMBOL_GPL(dpcon_enable);
-
-/**
- * dpcon_disable() - Disable the DPCON
- * @mc_io: Pointer to MC portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @token: Token of DPCON object
- *
- * Return: '0' on Success; Error code otherwise
- */
-int dpcon_disable(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token)
-{
- struct mc_command cmd = { 0 };
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPCON_CMDID_DISABLE,
- cmd_flags,
- token);
-
- /* send command to mc*/
- return mc_send_command(mc_io, &cmd);
-}
-EXPORT_SYMBOL_GPL(dpcon_disable);
-
-/**
- * dpcon_reset() - Reset the DPCON, returns the object to initial state.
- * @mc_io: Pointer to MC portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @token: Token of DPCON object
- *
- * Return: '0' on Success; Error code otherwise.
- */
-int dpcon_reset(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token)
-{
- struct mc_command cmd = { 0 };
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPCON_CMDID_RESET,
- cmd_flags, token);
-
- /* send command to mc*/
- return mc_send_command(mc_io, &cmd);
-}
-EXPORT_SYMBOL_GPL(dpcon_reset);
-
-/**
- * dpcon_get_attributes() - Retrieve DPCON attributes.
- * @mc_io: Pointer to MC portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @token: Token of DPCON object
- * @attr: Object's attributes
- *
- * Return: '0' on Success; Error code otherwise.
- */
-int dpcon_get_attributes(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token,
- struct dpcon_attr *attr)
-{
- struct mc_command cmd = { 0 };
- struct dpcon_rsp_get_attr *dpcon_rsp;
- int err;
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPCON_CMDID_GET_ATTR,
- cmd_flags,
- token);
-
- /* send command to mc*/
- err = mc_send_command(mc_io, &cmd);
- if (err)
- return err;
-
- /* retrieve response parameters */
- dpcon_rsp = (struct dpcon_rsp_get_attr *)cmd.params;
- attr->id = le32_to_cpu(dpcon_rsp->id);
- attr->qbman_ch_id = le16_to_cpu(dpcon_rsp->qbman_ch_id);
- attr->num_priorities = dpcon_rsp->num_priorities;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(dpcon_get_attributes);
-
-/**
- * dpcon_set_notification() - Set DPCON notification destination
- * @mc_io: Pointer to MC portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @token: Token of DPCON object
- * @cfg: Notification parameters
- *
- * Return: '0' on Success; Error code otherwise
- */
-int dpcon_set_notification(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token,
- struct dpcon_notification_cfg *cfg)
-{
- struct mc_command cmd = { 0 };
- struct dpcon_cmd_set_notification *dpcon_cmd;
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPCON_CMDID_SET_NOTIFICATION,
- cmd_flags,
- token);
- dpcon_cmd = (struct dpcon_cmd_set_notification *)cmd.params;
- dpcon_cmd->dpio_id = cpu_to_le32(cfg->dpio_id);
- dpcon_cmd->priority = cfg->priority;
- dpcon_cmd->user_ctx = cpu_to_le64(cfg->user_ctx);
-
- /* send command to mc*/
- return mc_send_command(mc_io, &cmd);
-}
-EXPORT_SYMBOL_GPL(dpcon_set_notification);
diff --git a/drivers/staging/fsl-mc/bus/dpio/Makefile b/drivers/staging/fsl-mc/bus/dpio/Makefile
index 53ba84d7b884..b9ff24c76582 100644
--- a/drivers/staging/fsl-mc/bus/dpio/Makefile
+++ b/drivers/staging/fsl-mc/bus/dpio/Makefile
@@ -3,8 +3,6 @@
# QorIQ DPAA2 DPIO driver
#
-subdir-ccflags-y := -Werror
-
obj-$(CONFIG_FSL_MC_DPIO) += fsl-mc-dpio.o
fsl-mc-dpio-objs := dpio.o qbman-portal.o dpio-service.o dpio-driver.o
diff --git a/drivers/staging/fsl-mc/bus/dpio/dpio-driver.c b/drivers/staging/fsl-mc/bus/dpio/dpio-driver.c
index b8479ef64c71..182b38412a82 100644
--- a/drivers/staging/fsl-mc/bus/dpio/dpio-driver.c
+++ b/drivers/staging/fsl-mc/bus/dpio/dpio-driver.c
@@ -14,7 +14,7 @@
#include <linux/dma-mapping.h>
#include <linux/delay.h>
-#include "../../include/mc.h"
+#include <linux/fsl/mc.h>
#include "../../include/dpaa2-io.h"
#include "qbman-portal.h"
diff --git a/drivers/staging/fsl-mc/bus/dpio/dpio-service.c b/drivers/staging/fsl-mc/bus/dpio/dpio-service.c
index d3c8462d43e8..14ed2beb7432 100644
--- a/drivers/staging/fsl-mc/bus/dpio/dpio-service.c
+++ b/drivers/staging/fsl-mc/bus/dpio/dpio-service.c
@@ -5,7 +5,7 @@
*
*/
#include <linux/types.h>
-#include "../../include/mc.h"
+#include <linux/fsl/mc.h>
#include "../../include/dpaa2-io.h"
#include <linux/init.h>
#include <linux/module.h>
@@ -192,7 +192,7 @@ irqreturn_t dpaa2_io_irq(struct dpaa2_io *obj)
u64 q64;
q64 = qbman_result_SCN_ctx(dq);
- ctx = (void *)q64;
+ ctx = (void *)(uintptr_t)q64;
ctx->cb(ctx);
} else {
pr_crit("fsl-mc-dpio: Unrecognised/ignored DQRR entry\n");
@@ -237,7 +237,7 @@ int dpaa2_io_service_register(struct dpaa2_io *d,
return -ENODEV;
ctx->dpio_id = d->dpio_desc.dpio_id;
- ctx->qman64 = (u64)ctx;
+ ctx->qman64 = (u64)(uintptr_t)ctx;
ctx->dpio_private = d;
spin_lock_irqsave(&d->lock_notifications, irqflags);
list_add(&ctx->node, &d->notifications);
diff --git a/drivers/staging/fsl-mc/bus/dpio/dpio.c b/drivers/staging/fsl-mc/bus/dpio/dpio.c
index 20cdeae54a74..ff37c80e11a0 100644
--- a/drivers/staging/fsl-mc/bus/dpio/dpio.c
+++ b/drivers/staging/fsl-mc/bus/dpio/dpio.c
@@ -5,7 +5,7 @@
*
*/
#include <linux/kernel.h>
-#include "../../include/mc.h"
+#include <linux/fsl/mc.h>
#include "dpio.h"
#include "dpio-cmd.h"
@@ -37,7 +37,7 @@ int dpio_open(struct fsl_mc_io *mc_io,
int dpio_id,
u16 *token)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpio_cmd_open *dpio_cmd;
int err;
@@ -70,7 +70,7 @@ int dpio_close(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPIO_CMDID_CLOSE,
@@ -92,7 +92,7 @@ int dpio_enable(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPIO_CMDID_ENABLE,
@@ -114,7 +114,7 @@ int dpio_disable(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPIO_CMDID_DISABLE,
@@ -138,7 +138,7 @@ int dpio_get_attributes(struct fsl_mc_io *mc_io,
u16 token,
struct dpio_attr *attr)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
struct dpio_rsp_get_attr *dpio_rsp;
int err;
@@ -180,7 +180,7 @@ int dpio_get_api_version(struct fsl_mc_io *mc_io,
u16 *major_ver,
u16 *minor_ver)
{
- struct mc_command cmd = { 0 };
+ struct fsl_mc_command cmd = { 0 };
int err;
/* prepare command */
diff --git a/drivers/staging/fsl-mc/bus/dpio/qbman-portal.c b/drivers/staging/fsl-mc/bus/dpio/qbman-portal.c
index 376e9ed0297a..116fafb28640 100644
--- a/drivers/staging/fsl-mc/bus/dpio/qbman-portal.c
+++ b/drivers/staging/fsl-mc/bus/dpio/qbman-portal.c
@@ -497,7 +497,7 @@ void qbman_pull_desc_set_storage(struct qbman_pull_desc *d,
int stash)
{
/* save the virtual address */
- d->rsp_addr_virt = (u64)storage;
+ d->rsp_addr_virt = (u64)(uintptr_t)storage;
if (!storage) {
d->verb &= ~(1 << QB_VDQCR_VERB_RLS_SHIFT);
@@ -522,11 +522,6 @@ void qbman_pull_desc_set_numframes(struct qbman_pull_desc *d, u8 numframes)
d->numf = numframes - 1;
}
-void qbman_pull_desc_set_token(struct qbman_pull_desc *d, u8 token)
-{
- d->tok = token;
-}
-
/*
* Exactly one of the following descriptor "actions" should be set. (Calling any
* one of these will replace the effect of any prior call to one of these.)
@@ -590,7 +585,7 @@ int qbman_swp_pull(struct qbman_swp *s, struct qbman_pull_desc *d)
atomic_inc(&s->vdq.available);
return -EBUSY;
}
- s->vdq.storage = (void *)d->rsp_addr_virt;
+ s->vdq.storage = (void *)(uintptr_t)d->rsp_addr_virt;
p = qbman_get_cmd(s, QBMAN_CENA_SWP_VDQCR);
p->numf = d->numf;
p->tok = QMAN_DQ_TOKEN_VALID;
@@ -830,7 +825,7 @@ int qbman_swp_release(struct qbman_swp *s, const struct qbman_release_desc *d,
struct qbman_acquire_desc {
u8 verb;
u8 reserved;
- u16 bpid;
+ __le16 bpid;
u8 num;
u8 reserved2[59];
};
@@ -838,10 +833,10 @@ struct qbman_acquire_desc {
struct qbman_acquire_rslt {
u8 verb;
u8 rslt;
- u16 reserved;
+ __le16 reserved;
u8 num;
u8 reserved2[3];
- u64 buf[7];
+ __le64 buf[7];
};
/**
@@ -904,7 +899,7 @@ int qbman_swp_acquire(struct qbman_swp *s, u16 bpid, u64 *buffers,
struct qbman_alt_fq_state_desc {
u8 verb;
u8 reserved[3];
- u32 fqid;
+ __le32 fqid;
u8 reserved2[56];
};
@@ -927,7 +922,7 @@ int qbman_swp_alt_fq_state(struct qbman_swp *s, u32 fqid,
if (!p)
return -EBUSY;
- p->fqid = cpu_to_le32(fqid) & ALT_FQ_FQID_MASK;
+ p->fqid = cpu_to_le32(fqid & ALT_FQ_FQID_MASK);
/* Complete the management command */
r = qbman_swp_mc_complete(s, p, alt_fq_verb);
@@ -953,11 +948,11 @@ int qbman_swp_alt_fq_state(struct qbman_swp *s, u32 fqid,
struct qbman_cdan_ctrl_desc {
u8 verb;
u8 reserved;
- u16 ch;
+ __le16 ch;
u8 we;
u8 ctrl;
- u16 reserved2;
- u64 cdan_ctx;
+ __le16 reserved2;
+ __le64 cdan_ctx;
u8 reserved3[48];
};
@@ -965,7 +960,7 @@ struct qbman_cdan_ctrl_desc {
struct qbman_cdan_ctrl_rslt {
u8 verb;
u8 rslt;
- u16 ch;
+ __le16 ch;
u8 reserved[60];
};
diff --git a/drivers/staging/fsl-mc/bus/dpio/qbman-portal.h b/drivers/staging/fsl-mc/bus/dpio/qbman-portal.h
index fb8b9d35a3eb..4488a445b709 100644
--- a/drivers/staging/fsl-mc/bus/dpio/qbman-portal.h
+++ b/drivers/staging/fsl-mc/bus/dpio/qbman-portal.h
@@ -32,8 +32,8 @@ struct qbman_pull_desc {
u8 numf;
u8 tok;
u8 reserved;
- u32 dq_src;
- u64 rsp_addr;
+ __le32 dq_src;
+ __le64 rsp_addr;
u64 rsp_addr_virt;
u8 padding[40];
};
@@ -70,17 +70,17 @@ enum qbman_pull_type_e {
struct qbman_eq_desc {
u8 verb;
u8 dca;
- u16 seqnum;
- u16 orpid;
- u16 reserved1;
- u32 tgtid;
- u32 tag;
- u16 qdbin;
+ __le16 seqnum;
+ __le16 orpid;
+ __le16 reserved1;
+ __le32 tgtid;
+ __le32 tag;
+ __le16 qdbin;
u8 qpri;
u8 reserved[3];
u8 wae;
u8 rspid;
- u64 rsp_addr;
+ __le64 rsp_addr;
u8 fd[32];
};
@@ -88,9 +88,9 @@ struct qbman_eq_desc {
struct qbman_release_desc {
u8 verb;
u8 reserved;
- u16 bpid;
- u32 reserved2;
- u64 buf[7];
+ __le16 bpid;
+ __le32 reserved2;
+ __le64 buf[7];
};
/* Management command result codes */
diff --git a/drivers/staging/fsl-mc/bus/dpmcp.c b/drivers/staging/fsl-mc/bus/dpmcp.c
deleted file mode 100644
index be07c77520af..000000000000
--- a/drivers/staging/fsl-mc/bus/dpmcp.c
+++ /dev/null
@@ -1,99 +0,0 @@
-// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
-/*
- * Copyright 2013-2016 Freescale Semiconductor Inc.
- *
- */
-#include <linux/kernel.h>
-#include "../include/mc.h"
-
-#include "fsl-mc-private.h"
-
-/**
- * dpmcp_open() - Open a control session for the specified object.
- * @mc_io: Pointer to MC portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @dpmcp_id: DPMCP unique ID
- * @token: Returned token; use in subsequent API calls
- *
- * This function can be used to open a control session for an
- * already created object; an object may have been declared in
- * the DPL or by calling the dpmcp_create function.
- * This function returns a unique authentication token,
- * associated with the specific object ID and the specific MC
- * portal; this token must be used in all subsequent commands for
- * this specific object
- *
- * Return: '0' on Success; Error code otherwise.
- */
-int dpmcp_open(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- int dpmcp_id,
- u16 *token)
-{
- struct mc_command cmd = { 0 };
- struct dpmcp_cmd_open *cmd_params;
- int err;
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPMCP_CMDID_OPEN,
- cmd_flags, 0);
- cmd_params = (struct dpmcp_cmd_open *)cmd.params;
- cmd_params->dpmcp_id = cpu_to_le32(dpmcp_id);
-
- /* send command to mc*/
- err = mc_send_command(mc_io, &cmd);
- if (err)
- return err;
-
- /* retrieve response parameters */
- *token = mc_cmd_hdr_read_token(&cmd);
-
- return err;
-}
-
-/**
- * dpmcp_close() - Close the control session of the object
- * @mc_io: Pointer to MC portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @token: Token of DPMCP object
- *
- * After this function is called, no further operations are
- * allowed on the object without opening a new control session.
- *
- * Return: '0' on Success; Error code otherwise.
- */
-int dpmcp_close(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token)
-{
- struct mc_command cmd = { 0 };
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPMCP_CMDID_CLOSE,
- cmd_flags, token);
-
- /* send command to mc*/
- return mc_send_command(mc_io, &cmd);
-}
-
-/**
- * dpmcp_reset() - Reset the DPMCP, returns the object to initial state.
- * @mc_io: Pointer to MC portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @token: Token of DPMCP object
- *
- * Return: '0' on Success; Error code otherwise.
- */
-int dpmcp_reset(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token)
-{
- struct mc_command cmd = { 0 };
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPMCP_CMDID_RESET,
- cmd_flags, token);
-
- /* send command to mc*/
- return mc_send_command(mc_io, &cmd);
-}
diff --git a/drivers/staging/fsl-mc/bus/dprc-driver.c b/drivers/staging/fsl-mc/bus/dprc-driver.c
deleted file mode 100644
index b09075731e62..000000000000
--- a/drivers/staging/fsl-mc/bus/dprc-driver.c
+++ /dev/null
@@ -1,809 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Freescale data path resource container (DPRC) driver
- *
- * Copyright (C) 2014-2016 Freescale Semiconductor, Inc.
- * Author: German Rivera <German.Rivera@freescale.com>
- *
- */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/msi.h>
-#include "../include/mc.h"
-
-#include "fsl-mc-private.h"
-
-#define FSL_MC_DPRC_DRIVER_NAME "fsl_mc_dprc"
-
-struct fsl_mc_child_objs {
- int child_count;
- struct fsl_mc_obj_desc *child_array;
-};
-
-static bool fsl_mc_device_match(struct fsl_mc_device *mc_dev,
- struct fsl_mc_obj_desc *obj_desc)
-{
- return mc_dev->obj_desc.id == obj_desc->id &&
- strcmp(mc_dev->obj_desc.type, obj_desc->type) == 0;
-
-}
-
-static int __fsl_mc_device_remove_if_not_in_mc(struct device *dev, void *data)
-{
- int i;
- struct fsl_mc_child_objs *objs;
- struct fsl_mc_device *mc_dev;
-
- mc_dev = to_fsl_mc_device(dev);
- objs = data;
-
- for (i = 0; i < objs->child_count; i++) {
- struct fsl_mc_obj_desc *obj_desc = &objs->child_array[i];
-
- if (strlen(obj_desc->type) != 0 &&
- fsl_mc_device_match(mc_dev, obj_desc))
- break;
- }
-
- if (i == objs->child_count)
- fsl_mc_device_remove(mc_dev);
-
- return 0;
-}
-
-static int __fsl_mc_device_remove(struct device *dev, void *data)
-{
- fsl_mc_device_remove(to_fsl_mc_device(dev));
- return 0;
-}
-
-/**
- * dprc_remove_devices - Removes devices for objects removed from a DPRC
- *
- * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
- * @obj_desc_array: array of object descriptors for child objects currently
- * present in the DPRC in the MC.
- * @num_child_objects_in_mc: number of entries in obj_desc_array
- *
- * Synchronizes the state of the Linux bus driver with the actual state of
- * the MC by removing devices that represent MC objects that have
- * been dynamically removed in the physical DPRC.
- */
-static void dprc_remove_devices(struct fsl_mc_device *mc_bus_dev,
- struct fsl_mc_obj_desc *obj_desc_array,
- int num_child_objects_in_mc)
-{
- if (num_child_objects_in_mc != 0) {
- /*
- * Remove child objects that are in the DPRC in Linux,
- * but not in the MC:
- */
- struct fsl_mc_child_objs objs;
-
- objs.child_count = num_child_objects_in_mc;
- objs.child_array = obj_desc_array;
- device_for_each_child(&mc_bus_dev->dev, &objs,
- __fsl_mc_device_remove_if_not_in_mc);
- } else {
- /*
- * There are no child objects for this DPRC in the MC.
- * So, remove all the child devices from Linux:
- */
- device_for_each_child(&mc_bus_dev->dev, NULL,
- __fsl_mc_device_remove);
- }
-}
-
-static int __fsl_mc_device_match(struct device *dev, void *data)
-{
- struct fsl_mc_obj_desc *obj_desc = data;
- struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
-
- return fsl_mc_device_match(mc_dev, obj_desc);
-}
-
-static struct fsl_mc_device *fsl_mc_device_lookup(struct fsl_mc_obj_desc
- *obj_desc,
- struct fsl_mc_device
- *mc_bus_dev)
-{
- struct device *dev;
-
- dev = device_find_child(&mc_bus_dev->dev, obj_desc,
- __fsl_mc_device_match);
-
- return dev ? to_fsl_mc_device(dev) : NULL;
-}
-
-/**
- * check_plugged_state_change - Check change in an MC object's plugged state
- *
- * @mc_dev: pointer to the fsl-mc device for a given MC object
- * @obj_desc: pointer to the MC object's descriptor in the MC
- *
- * If the plugged state has changed from unplugged to plugged, the fsl-mc
- * device is bound to the corresponding device driver.
- * If the plugged state has changed from plugged to unplugged, the fsl-mc
- * device is unbound from the corresponding device driver.
- */
-static void check_plugged_state_change(struct fsl_mc_device *mc_dev,
- struct fsl_mc_obj_desc *obj_desc)
-{
- int error;
- u32 plugged_flag_at_mc =
- obj_desc->state & FSL_MC_OBJ_STATE_PLUGGED;
-
- if (plugged_flag_at_mc !=
- (mc_dev->obj_desc.state & FSL_MC_OBJ_STATE_PLUGGED)) {
- if (plugged_flag_at_mc) {
- mc_dev->obj_desc.state |= FSL_MC_OBJ_STATE_PLUGGED;
- error = device_attach(&mc_dev->dev);
- if (error < 0) {
- dev_err(&mc_dev->dev,
- "device_attach() failed: %d\n",
- error);
- }
- } else {
- mc_dev->obj_desc.state &= ~FSL_MC_OBJ_STATE_PLUGGED;
- device_release_driver(&mc_dev->dev);
- }
- }
-}
-
-/**
- * dprc_add_new_devices - Adds devices to the logical bus for a DPRC
- *
- * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
- * @obj_desc_array: array of device descriptors for child devices currently
- * present in the physical DPRC.
- * @num_child_objects_in_mc: number of entries in obj_desc_array
- *
- * Synchronizes the state of the Linux bus driver with the actual
- * state of the MC by adding objects that have been newly discovered
- * in the physical DPRC.
- */
-static void dprc_add_new_devices(struct fsl_mc_device *mc_bus_dev,
- struct fsl_mc_obj_desc *obj_desc_array,
- int num_child_objects_in_mc)
-{
- int error;
- int i;
-
- for (i = 0; i < num_child_objects_in_mc; i++) {
- struct fsl_mc_device *child_dev;
- struct fsl_mc_obj_desc *obj_desc = &obj_desc_array[i];
-
- if (strlen(obj_desc->type) == 0)
- continue;
-
- /*
- * Check if device is already known to Linux:
- */
- child_dev = fsl_mc_device_lookup(obj_desc, mc_bus_dev);
- if (child_dev) {
- check_plugged_state_change(child_dev, obj_desc);
- put_device(&child_dev->dev);
- continue;
- }
-
- error = fsl_mc_device_add(obj_desc, NULL, &mc_bus_dev->dev,
- &child_dev);
- if (error < 0)
- continue;
- }
-}
-
-/**
- * dprc_scan_objects - Discover objects in a DPRC
- *
- * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
- * @total_irq_count: If argument is provided the function populates the
- * total number of IRQs created by objects in the DPRC.
- *
- * Detects objects added and removed from a DPRC and synchronizes the
- * state of the Linux bus driver, MC by adding and removing
- * devices accordingly.
- * Two types of devices can be found in a DPRC: allocatable objects (e.g.,
- * dpbp, dpmcp) and non-allocatable devices (e.g., dprc, dpni).
- * All allocatable devices needed to be probed before all non-allocatable
- * devices, to ensure that device drivers for non-allocatable
- * devices can allocate any type of allocatable devices.
- * That is, we need to ensure that the corresponding resource pools are
- * populated before they can get allocation requests from probe callbacks
- * of the device drivers for the non-allocatable devices.
- */
-static int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev,
- unsigned int *total_irq_count)
-{
- int num_child_objects;
- int dprc_get_obj_failures;
- int error;
- unsigned int irq_count = mc_bus_dev->obj_desc.irq_count;
- struct fsl_mc_obj_desc *child_obj_desc_array = NULL;
- struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
-
- error = dprc_get_obj_count(mc_bus_dev->mc_io,
- 0,
- mc_bus_dev->mc_handle,
- &num_child_objects);
- if (error < 0) {
- dev_err(&mc_bus_dev->dev, "dprc_get_obj_count() failed: %d\n",
- error);
- return error;
- }
-
- if (num_child_objects != 0) {
- int i;
-
- child_obj_desc_array =
- devm_kmalloc_array(&mc_bus_dev->dev, num_child_objects,
- sizeof(*child_obj_desc_array),
- GFP_KERNEL);
- if (!child_obj_desc_array)
- return -ENOMEM;
-
- /*
- * Discover objects currently present in the physical DPRC:
- */
- dprc_get_obj_failures = 0;
- for (i = 0; i < num_child_objects; i++) {
- struct fsl_mc_obj_desc *obj_desc =
- &child_obj_desc_array[i];
-
- error = dprc_get_obj(mc_bus_dev->mc_io,
- 0,
- mc_bus_dev->mc_handle,
- i, obj_desc);
- if (error < 0) {
- dev_err(&mc_bus_dev->dev,
- "dprc_get_obj(i=%d) failed: %d\n",
- i, error);
- /*
- * Mark the obj entry as "invalid", by using the
- * empty string as obj type:
- */
- obj_desc->type[0] = '\0';
- obj_desc->id = error;
- dprc_get_obj_failures++;
- continue;
- }
-
- /*
- * add a quirk for all versions of dpsec < 4.0...none
- * are coherent regardless of what the MC reports.
- */
- if ((strcmp(obj_desc->type, "dpseci") == 0) &&
- (obj_desc->ver_major < 4))
- obj_desc->flags |=
- FSL_MC_OBJ_FLAG_NO_MEM_SHAREABILITY;
-
- irq_count += obj_desc->irq_count;
- dev_dbg(&mc_bus_dev->dev,
- "Discovered object: type %s, id %d\n",
- obj_desc->type, obj_desc->id);
- }
-
- if (dprc_get_obj_failures != 0) {
- dev_err(&mc_bus_dev->dev,
- "%d out of %d devices could not be retrieved\n",
- dprc_get_obj_failures, num_child_objects);
- }
- }
-
- /*
- * Allocate IRQ's before binding the scanned devices with their
- * respective drivers.
- */
- if (dev_get_msi_domain(&mc_bus_dev->dev) && !mc_bus->irq_resources) {
- if (irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS) {
- dev_warn(&mc_bus_dev->dev,
- "IRQs needed (%u) exceed IRQs preallocated (%u)\n",
- irq_count, FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS);
- }
-
- error = fsl_mc_populate_irq_pool(mc_bus,
- FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS);
- if (error < 0)
- return error;
- }
-
- if (total_irq_count)
- *total_irq_count = irq_count;
-
- dprc_remove_devices(mc_bus_dev, child_obj_desc_array,
- num_child_objects);
-
- dprc_add_new_devices(mc_bus_dev, child_obj_desc_array,
- num_child_objects);
-
- if (child_obj_desc_array)
- devm_kfree(&mc_bus_dev->dev, child_obj_desc_array);
-
- return 0;
-}
-
-/**
- * dprc_scan_container - Scans a physical DPRC and synchronizes Linux bus state
- *
- * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
- *
- * Scans the physical DPRC and synchronizes the state of the Linux
- * bus driver with the actual state of the MC by adding and removing
- * devices as appropriate.
- */
-static int dprc_scan_container(struct fsl_mc_device *mc_bus_dev)
-{
- int error;
- struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
-
- fsl_mc_init_all_resource_pools(mc_bus_dev);
-
- /*
- * Discover objects in the DPRC:
- */
- mutex_lock(&mc_bus->scan_mutex);
- error = dprc_scan_objects(mc_bus_dev, NULL);
- mutex_unlock(&mc_bus->scan_mutex);
- if (error < 0) {
- fsl_mc_cleanup_all_resource_pools(mc_bus_dev);
- return error;
- }
-
- return 0;
-}
-
-/**
- * dprc_irq0_handler - Regular ISR for DPRC interrupt 0
- *
- * @irq: IRQ number of the interrupt being handled
- * @arg: Pointer to device structure
- */
-static irqreturn_t dprc_irq0_handler(int irq_num, void *arg)
-{
- return IRQ_WAKE_THREAD;
-}
-
-/**
- * dprc_irq0_handler_thread - Handler thread function for DPRC interrupt 0
- *
- * @irq: IRQ number of the interrupt being handled
- * @arg: Pointer to device structure
- */
-static irqreturn_t dprc_irq0_handler_thread(int irq_num, void *arg)
-{
- int error;
- u32 status;
- struct device *dev = arg;
- struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
- struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
- struct fsl_mc_io *mc_io = mc_dev->mc_io;
- struct msi_desc *msi_desc = mc_dev->irqs[0]->msi_desc;
-
- dev_dbg(dev, "DPRC IRQ %d triggered on CPU %u\n",
- irq_num, smp_processor_id());
-
- if (!(mc_dev->flags & FSL_MC_IS_DPRC))
- return IRQ_HANDLED;
-
- mutex_lock(&mc_bus->scan_mutex);
- if (!msi_desc || msi_desc->irq != (u32)irq_num)
- goto out;
-
- status = 0;
- error = dprc_get_irq_status(mc_io, 0, mc_dev->mc_handle, 0,
- &status);
- if (error < 0) {
- dev_err(dev,
- "dprc_get_irq_status() failed: %d\n", error);
- goto out;
- }
-
- error = dprc_clear_irq_status(mc_io, 0, mc_dev->mc_handle, 0,
- status);
- if (error < 0) {
- dev_err(dev,
- "dprc_clear_irq_status() failed: %d\n", error);
- goto out;
- }
-
- if (status & (DPRC_IRQ_EVENT_OBJ_ADDED |
- DPRC_IRQ_EVENT_OBJ_REMOVED |
- DPRC_IRQ_EVENT_CONTAINER_DESTROYED |
- DPRC_IRQ_EVENT_OBJ_DESTROYED |
- DPRC_IRQ_EVENT_OBJ_CREATED)) {
- unsigned int irq_count;
-
- error = dprc_scan_objects(mc_dev, &irq_count);
- if (error < 0) {
- /*
- * If the error is -ENXIO, we ignore it, as it indicates
- * that the object scan was aborted, as we detected that
- * an object was removed from the DPRC in the MC, while
- * we were scanning the DPRC.
- */
- if (error != -ENXIO) {
- dev_err(dev, "dprc_scan_objects() failed: %d\n",
- error);
- }
-
- goto out;
- }
-
- if (irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS) {
- dev_warn(dev,
- "IRQs needed (%u) exceed IRQs preallocated (%u)\n",
- irq_count, FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS);
- }
- }
-
-out:
- mutex_unlock(&mc_bus->scan_mutex);
- return IRQ_HANDLED;
-}
-
-/*
- * Disable and clear interrupt for a given DPRC object
- */
-static int disable_dprc_irq(struct fsl_mc_device *mc_dev)
-{
- int error;
- struct fsl_mc_io *mc_io = mc_dev->mc_io;
-
- /*
- * Disable generation of interrupt, while we configure it:
- */
- error = dprc_set_irq_enable(mc_io, 0, mc_dev->mc_handle, 0, 0);
- if (error < 0) {
- dev_err(&mc_dev->dev,
- "Disabling DPRC IRQ failed: dprc_set_irq_enable() failed: %d\n",
- error);
- return error;
- }
-
- /*
- * Disable all interrupt causes for the interrupt:
- */
- error = dprc_set_irq_mask(mc_io, 0, mc_dev->mc_handle, 0, 0x0);
- if (error < 0) {
- dev_err(&mc_dev->dev,
- "Disabling DPRC IRQ failed: dprc_set_irq_mask() failed: %d\n",
- error);
- return error;
- }
-
- /*
- * Clear any leftover interrupts:
- */
- error = dprc_clear_irq_status(mc_io, 0, mc_dev->mc_handle, 0, ~0x0U);
- if (error < 0) {
- dev_err(&mc_dev->dev,
- "Disabling DPRC IRQ failed: dprc_clear_irq_status() failed: %d\n",
- error);
- return error;
- }
-
- return 0;
-}
-
-static int register_dprc_irq_handler(struct fsl_mc_device *mc_dev)
-{
- int error;
- struct fsl_mc_device_irq *irq = mc_dev->irqs[0];
-
- /*
- * NOTE: devm_request_threaded_irq() invokes the device-specific
- * function that programs the MSI physically in the device
- */
- error = devm_request_threaded_irq(&mc_dev->dev,
- irq->msi_desc->irq,
- dprc_irq0_handler,
- dprc_irq0_handler_thread,
- IRQF_NO_SUSPEND | IRQF_ONESHOT,
- dev_name(&mc_dev->dev),
- &mc_dev->dev);
- if (error < 0) {
- dev_err(&mc_dev->dev,
- "devm_request_threaded_irq() failed: %d\n",
- error);
- return error;
- }
-
- return 0;
-}
-
-static int enable_dprc_irq(struct fsl_mc_device *mc_dev)
-{
- int error;
-
- /*
- * Enable all interrupt causes for the interrupt:
- */
- error = dprc_set_irq_mask(mc_dev->mc_io, 0, mc_dev->mc_handle, 0,
- ~0x0u);
- if (error < 0) {
- dev_err(&mc_dev->dev,
- "Enabling DPRC IRQ failed: dprc_set_irq_mask() failed: %d\n",
- error);
-
- return error;
- }
-
- /*
- * Enable generation of the interrupt:
- */
- error = dprc_set_irq_enable(mc_dev->mc_io, 0, mc_dev->mc_handle, 0, 1);
- if (error < 0) {
- dev_err(&mc_dev->dev,
- "Enabling DPRC IRQ failed: dprc_set_irq_enable() failed: %d\n",
- error);
-
- return error;
- }
-
- return 0;
-}
-
-/*
- * Setup interrupt for a given DPRC device
- */
-static int dprc_setup_irq(struct fsl_mc_device *mc_dev)
-{
- int error;
-
- error = fsl_mc_allocate_irqs(mc_dev);
- if (error < 0)
- return error;
-
- error = disable_dprc_irq(mc_dev);
- if (error < 0)
- goto error_free_irqs;
-
- error = register_dprc_irq_handler(mc_dev);
- if (error < 0)
- goto error_free_irqs;
-
- error = enable_dprc_irq(mc_dev);
- if (error < 0)
- goto error_free_irqs;
-
- return 0;
-
-error_free_irqs:
- fsl_mc_free_irqs(mc_dev);
- return error;
-}
-
-/**
- * dprc_probe - callback invoked when a DPRC is being bound to this driver
- *
- * @mc_dev: Pointer to fsl-mc device representing a DPRC
- *
- * It opens the physical DPRC in the MC.
- * It scans the DPRC to discover the MC objects contained in it.
- * It creates the interrupt pool for the MC bus associated with the DPRC.
- * It configures the interrupts for the DPRC device itself.
- */
-static int dprc_probe(struct fsl_mc_device *mc_dev)
-{
- int error;
- size_t region_size;
- struct device *parent_dev = mc_dev->dev.parent;
- struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
- bool mc_io_created = false;
- bool msi_domain_set = false;
- u16 major_ver, minor_ver;
-
- if (!is_fsl_mc_bus_dprc(mc_dev))
- return -EINVAL;
-
- if (dev_get_msi_domain(&mc_dev->dev))
- return -EINVAL;
-
- if (!mc_dev->mc_io) {
- /*
- * This is a child DPRC:
- */
- if (!dev_is_fsl_mc(parent_dev))
- return -EINVAL;
-
- if (mc_dev->obj_desc.region_count == 0)
- return -EINVAL;
-
- region_size = resource_size(mc_dev->regions);
-
- error = fsl_create_mc_io(&mc_dev->dev,
- mc_dev->regions[0].start,
- region_size,
- NULL,
- FSL_MC_IO_ATOMIC_CONTEXT_PORTAL,
- &mc_dev->mc_io);
- if (error < 0)
- return error;
-
- mc_io_created = true;
-
- /*
- * Inherit parent MSI domain:
- */
- dev_set_msi_domain(&mc_dev->dev,
- dev_get_msi_domain(parent_dev));
- msi_domain_set = true;
- } else {
- /*
- * This is a root DPRC
- */
- struct irq_domain *mc_msi_domain;
-
- if (dev_is_fsl_mc(parent_dev))
- return -EINVAL;
-
- error = fsl_mc_find_msi_domain(parent_dev,
- &mc_msi_domain);
- if (error < 0) {
- dev_warn(&mc_dev->dev,
- "WARNING: MC bus without interrupt support\n");
- } else {
- dev_set_msi_domain(&mc_dev->dev, mc_msi_domain);
- msi_domain_set = true;
- }
- }
-
- error = dprc_open(mc_dev->mc_io, 0, mc_dev->obj_desc.id,
- &mc_dev->mc_handle);
- if (error < 0) {
- dev_err(&mc_dev->dev, "dprc_open() failed: %d\n", error);
- goto error_cleanup_msi_domain;
- }
-
- error = dprc_get_attributes(mc_dev->mc_io, 0, mc_dev->mc_handle,
- &mc_bus->dprc_attr);
- if (error < 0) {
- dev_err(&mc_dev->dev, "dprc_get_attributes() failed: %d\n",
- error);
- goto error_cleanup_open;
- }
-
- error = dprc_get_api_version(mc_dev->mc_io, 0,
- &major_ver,
- &minor_ver);
- if (error < 0) {
- dev_err(&mc_dev->dev, "dprc_get_api_version() failed: %d\n",
- error);
- goto error_cleanup_open;
- }
-
- if (major_ver < DPRC_MIN_VER_MAJOR ||
- (major_ver == DPRC_MIN_VER_MAJOR &&
- minor_ver < DPRC_MIN_VER_MINOR)) {
- dev_err(&mc_dev->dev,
- "ERROR: DPRC version %d.%d not supported\n",
- major_ver, minor_ver);
- error = -ENOTSUPP;
- goto error_cleanup_open;
- }
-
- mutex_init(&mc_bus->scan_mutex);
-
- /*
- * Discover MC objects in DPRC object:
- */
- error = dprc_scan_container(mc_dev);
- if (error < 0)
- goto error_cleanup_open;
-
- /*
- * Configure interrupt for the DPRC object associated with this MC bus:
- */
- error = dprc_setup_irq(mc_dev);
- if (error < 0)
- goto error_cleanup_open;
-
- dev_info(&mc_dev->dev, "DPRC device bound to driver");
- return 0;
-
-error_cleanup_open:
- (void)dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle);
-
-error_cleanup_msi_domain:
- if (msi_domain_set)
- dev_set_msi_domain(&mc_dev->dev, NULL);
-
- if (mc_io_created) {
- fsl_destroy_mc_io(mc_dev->mc_io);
- mc_dev->mc_io = NULL;
- }
-
- return error;
-}
-
-/*
- * Tear down interrupt for a given DPRC object
- */
-static void dprc_teardown_irq(struct fsl_mc_device *mc_dev)
-{
- struct fsl_mc_device_irq *irq = mc_dev->irqs[0];
-
- (void)disable_dprc_irq(mc_dev);
-
- devm_free_irq(&mc_dev->dev, irq->msi_desc->irq, &mc_dev->dev);
-
- fsl_mc_free_irqs(mc_dev);
-}
-
-/**
- * dprc_remove - callback invoked when a DPRC is being unbound from this driver
- *
- * @mc_dev: Pointer to fsl-mc device representing the DPRC
- *
- * It removes the DPRC's child objects from Linux (not from the MC) and
- * closes the DPRC device in the MC.
- * It tears down the interrupts that were configured for the DPRC device.
- * It destroys the interrupt pool associated with this MC bus.
- */
-static int dprc_remove(struct fsl_mc_device *mc_dev)
-{
- int error;
- struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
-
- if (!is_fsl_mc_bus_dprc(mc_dev))
- return -EINVAL;
- if (!mc_dev->mc_io)
- return -EINVAL;
-
- if (!mc_bus->irq_resources)
- return -EINVAL;
-
- if (dev_get_msi_domain(&mc_dev->dev))
- dprc_teardown_irq(mc_dev);
-
- device_for_each_child(&mc_dev->dev, NULL, __fsl_mc_device_remove);
-
- if (dev_get_msi_domain(&mc_dev->dev)) {
- fsl_mc_cleanup_irq_pool(mc_bus);
- dev_set_msi_domain(&mc_dev->dev, NULL);
- }
-
- fsl_mc_cleanup_all_resource_pools(mc_dev);
-
- error = dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle);
- if (error < 0)
- dev_err(&mc_dev->dev, "dprc_close() failed: %d\n", error);
-
- if (!fsl_mc_is_root_dprc(&mc_dev->dev)) {
- fsl_destroy_mc_io(mc_dev->mc_io);
- mc_dev->mc_io = NULL;
- }
-
- dev_info(&mc_dev->dev, "DPRC device unbound from driver");
- return 0;
-}
-
-static const struct fsl_mc_device_id match_id_table[] = {
- {
- .vendor = FSL_MC_VENDOR_FREESCALE,
- .obj_type = "dprc"},
- {.vendor = 0x0},
-};
-
-static struct fsl_mc_driver dprc_driver = {
- .driver = {
- .name = FSL_MC_DPRC_DRIVER_NAME,
- .owner = THIS_MODULE,
- .pm = NULL,
- },
- .match_id_table = match_id_table,
- .probe = dprc_probe,
- .remove = dprc_remove,
-};
-
-int __init dprc_driver_init(void)
-{
- return fsl_mc_driver_register(&dprc_driver);
-}
-
-void dprc_driver_exit(void)
-{
- fsl_mc_driver_unregister(&dprc_driver);
-}
diff --git a/drivers/staging/fsl-mc/bus/dprc.c b/drivers/staging/fsl-mc/bus/dprc.c
deleted file mode 100644
index 97f51726fa7e..000000000000
--- a/drivers/staging/fsl-mc/bus/dprc.c
+++ /dev/null
@@ -1,531 +0,0 @@
-// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
-/*
- * Copyright 2013-2016 Freescale Semiconductor Inc.
- *
- */
-#include <linux/kernel.h>
-#include "../include/mc.h"
-#include "fsl-mc-private.h"
-
-/**
- * dprc_open() - Open DPRC object for use
- * @mc_io: Pointer to MC portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @container_id: Container ID to open
- * @token: Returned token of DPRC object
- *
- * Return: '0' on Success; Error code otherwise.
- *
- * @warning Required before any operation on the object.
- */
-int dprc_open(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- int container_id,
- u16 *token)
-{
- struct mc_command cmd = { 0 };
- struct dprc_cmd_open *cmd_params;
- int err;
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPRC_CMDID_OPEN, cmd_flags,
- 0);
- cmd_params = (struct dprc_cmd_open *)cmd.params;
- cmd_params->container_id = cpu_to_le32(container_id);
-
- /* send command to mc*/
- err = mc_send_command(mc_io, &cmd);
- if (err)
- return err;
-
- /* retrieve response parameters */
- *token = mc_cmd_hdr_read_token(&cmd);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(dprc_open);
-
-/**
- * dprc_close() - Close the control session of the object
- * @mc_io: Pointer to MC portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @token: Token of DPRC object
- *
- * After this function is called, no further operations are
- * allowed on the object without opening a new control session.
- *
- * Return: '0' on Success; Error code otherwise.
- */
-int dprc_close(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token)
-{
- struct mc_command cmd = { 0 };
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPRC_CMDID_CLOSE, cmd_flags,
- token);
-
- /* send command to mc*/
- return mc_send_command(mc_io, &cmd);
-}
-EXPORT_SYMBOL_GPL(dprc_close);
-
-/**
- * dprc_set_irq() - Set IRQ information for the DPRC to trigger an interrupt.
- * @mc_io: Pointer to MC portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @token: Token of DPRC object
- * @irq_index: Identifies the interrupt index to configure
- * @irq_cfg: IRQ configuration
- *
- * Return: '0' on Success; Error code otherwise.
- */
-int dprc_set_irq(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token,
- u8 irq_index,
- struct dprc_irq_cfg *irq_cfg)
-{
- struct mc_command cmd = { 0 };
- struct dprc_cmd_set_irq *cmd_params;
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_IRQ,
- cmd_flags,
- token);
- cmd_params = (struct dprc_cmd_set_irq *)cmd.params;
- cmd_params->irq_val = cpu_to_le32(irq_cfg->val);
- cmd_params->irq_index = irq_index;
- cmd_params->irq_addr = cpu_to_le64(irq_cfg->paddr);
- cmd_params->irq_num = cpu_to_le32(irq_cfg->irq_num);
-
- /* send command to mc*/
- return mc_send_command(mc_io, &cmd);
-}
-
-/**
- * dprc_set_irq_enable() - Set overall interrupt state.
- * @mc_io: Pointer to MC portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @token: Token of DPRC object
- * @irq_index: The interrupt index to configure
- * @en: Interrupt state - enable = 1, disable = 0
- *
- * Allows GPP software to control when interrupts are generated.
- * Each interrupt can have up to 32 causes. The enable/disable control's the
- * overall interrupt state. if the interrupt is disabled no causes will cause
- * an interrupt.
- *
- * Return: '0' on Success; Error code otherwise.
- */
-int dprc_set_irq_enable(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token,
- u8 irq_index,
- u8 en)
-{
- struct mc_command cmd = { 0 };
- struct dprc_cmd_set_irq_enable *cmd_params;
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_IRQ_ENABLE,
- cmd_flags, token);
- cmd_params = (struct dprc_cmd_set_irq_enable *)cmd.params;
- cmd_params->enable = en & DPRC_ENABLE;
- cmd_params->irq_index = irq_index;
-
- /* send command to mc*/
- return mc_send_command(mc_io, &cmd);
-}
-
-/**
- * dprc_set_irq_mask() - Set interrupt mask.
- * @mc_io: Pointer to MC portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @token: Token of DPRC object
- * @irq_index: The interrupt index to configure
- * @mask: event mask to trigger interrupt;
- * each bit:
- * 0 = ignore event
- * 1 = consider event for asserting irq
- *
- * Every interrupt can have up to 32 causes and the interrupt model supports
- * masking/unmasking each cause independently
- *
- * Return: '0' on Success; Error code otherwise.
- */
-int dprc_set_irq_mask(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token,
- u8 irq_index,
- u32 mask)
-{
- struct mc_command cmd = { 0 };
- struct dprc_cmd_set_irq_mask *cmd_params;
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_IRQ_MASK,
- cmd_flags, token);
- cmd_params = (struct dprc_cmd_set_irq_mask *)cmd.params;
- cmd_params->mask = cpu_to_le32(mask);
- cmd_params->irq_index = irq_index;
-
- /* send command to mc*/
- return mc_send_command(mc_io, &cmd);
-}
-
-/**
- * dprc_get_irq_status() - Get the current status of any pending interrupts.
- * @mc_io: Pointer to MC portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @token: Token of DPRC object
- * @irq_index: The interrupt index to configure
- * @status: Returned interrupts status - one bit per cause:
- * 0 = no interrupt pending
- * 1 = interrupt pending
- *
- * Return: '0' on Success; Error code otherwise.
- */
-int dprc_get_irq_status(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token,
- u8 irq_index,
- u32 *status)
-{
- struct mc_command cmd = { 0 };
- struct dprc_cmd_get_irq_status *cmd_params;
- struct dprc_rsp_get_irq_status *rsp_params;
- int err;
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_IRQ_STATUS,
- cmd_flags, token);
- cmd_params = (struct dprc_cmd_get_irq_status *)cmd.params;
- cmd_params->status = cpu_to_le32(*status);
- cmd_params->irq_index = irq_index;
-
- /* send command to mc*/
- err = mc_send_command(mc_io, &cmd);
- if (err)
- return err;
-
- /* retrieve response parameters */
- rsp_params = (struct dprc_rsp_get_irq_status *)cmd.params;
- *status = le32_to_cpu(rsp_params->status);
-
- return 0;
-}
-
-/**
- * dprc_clear_irq_status() - Clear a pending interrupt's status
- * @mc_io: Pointer to MC portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @token: Token of DPRC object
- * @irq_index: The interrupt index to configure
- * @status: bits to clear (W1C) - one bit per cause:
- * 0 = don't change
- * 1 = clear status bit
- *
- * Return: '0' on Success; Error code otherwise.
- */
-int dprc_clear_irq_status(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token,
- u8 irq_index,
- u32 status)
-{
- struct mc_command cmd = { 0 };
- struct dprc_cmd_clear_irq_status *cmd_params;
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPRC_CMDID_CLEAR_IRQ_STATUS,
- cmd_flags, token);
- cmd_params = (struct dprc_cmd_clear_irq_status *)cmd.params;
- cmd_params->status = cpu_to_le32(status);
- cmd_params->irq_index = irq_index;
-
- /* send command to mc*/
- return mc_send_command(mc_io, &cmd);
-}
-
-/**
- * dprc_get_attributes() - Obtains container attributes
- * @mc_io: Pointer to MC portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @token: Token of DPRC object
- * @attributes Returned container attributes
- *
- * Return: '0' on Success; Error code otherwise.
- */
-int dprc_get_attributes(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token,
- struct dprc_attributes *attr)
-{
- struct mc_command cmd = { 0 };
- struct dprc_rsp_get_attributes *rsp_params;
- int err;
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_ATTR,
- cmd_flags,
- token);
-
- /* send command to mc*/
- err = mc_send_command(mc_io, &cmd);
- if (err)
- return err;
-
- /* retrieve response parameters */
- rsp_params = (struct dprc_rsp_get_attributes *)cmd.params;
- attr->container_id = le32_to_cpu(rsp_params->container_id);
- attr->icid = le16_to_cpu(rsp_params->icid);
- attr->options = le32_to_cpu(rsp_params->options);
- attr->portal_id = le32_to_cpu(rsp_params->portal_id);
-
- return 0;
-}
-
-/**
- * dprc_get_obj_count() - Obtains the number of objects in the DPRC
- * @mc_io: Pointer to MC portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @token: Token of DPRC object
- * @obj_count: Number of objects assigned to the DPRC
- *
- * Return: '0' on Success; Error code otherwise.
- */
-int dprc_get_obj_count(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token,
- int *obj_count)
-{
- struct mc_command cmd = { 0 };
- struct dprc_rsp_get_obj_count *rsp_params;
- int err;
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ_COUNT,
- cmd_flags, token);
-
- /* send command to mc*/
- err = mc_send_command(mc_io, &cmd);
- if (err)
- return err;
-
- /* retrieve response parameters */
- rsp_params = (struct dprc_rsp_get_obj_count *)cmd.params;
- *obj_count = le32_to_cpu(rsp_params->obj_count);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(dprc_get_obj_count);
-
-/**
- * dprc_get_obj() - Get general information on an object
- * @mc_io: Pointer to MC portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @token: Token of DPRC object
- * @obj_index: Index of the object to be queried (< obj_count)
- * @obj_desc: Returns the requested object descriptor
- *
- * The object descriptors are retrieved one by one by incrementing
- * obj_index up to (not including) the value of obj_count returned
- * from dprc_get_obj_count(). dprc_get_obj_count() must
- * be called prior to dprc_get_obj().
- *
- * Return: '0' on Success; Error code otherwise.
- */
-int dprc_get_obj(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token,
- int obj_index,
- struct fsl_mc_obj_desc *obj_desc)
-{
- struct mc_command cmd = { 0 };
- struct dprc_cmd_get_obj *cmd_params;
- struct dprc_rsp_get_obj *rsp_params;
- int err;
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ,
- cmd_flags,
- token);
- cmd_params = (struct dprc_cmd_get_obj *)cmd.params;
- cmd_params->obj_index = cpu_to_le32(obj_index);
-
- /* send command to mc*/
- err = mc_send_command(mc_io, &cmd);
- if (err)
- return err;
-
- /* retrieve response parameters */
- rsp_params = (struct dprc_rsp_get_obj *)cmd.params;
- obj_desc->id = le32_to_cpu(rsp_params->id);
- obj_desc->vendor = le16_to_cpu(rsp_params->vendor);
- obj_desc->irq_count = rsp_params->irq_count;
- obj_desc->region_count = rsp_params->region_count;
- obj_desc->state = le32_to_cpu(rsp_params->state);
- obj_desc->ver_major = le16_to_cpu(rsp_params->version_major);
- obj_desc->ver_minor = le16_to_cpu(rsp_params->version_minor);
- obj_desc->flags = le16_to_cpu(rsp_params->flags);
- strncpy(obj_desc->type, rsp_params->type, 16);
- obj_desc->type[15] = '\0';
- strncpy(obj_desc->label, rsp_params->label, 16);
- obj_desc->label[15] = '\0';
- return 0;
-}
-EXPORT_SYMBOL_GPL(dprc_get_obj);
-
-/**
- * dprc_set_obj_irq() - Set IRQ information for object to trigger an interrupt.
- * @mc_io: Pointer to MC portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @token: Token of DPRC object
- * @obj_type: Type of the object to set its IRQ
- * @obj_id: ID of the object to set its IRQ
- * @irq_index: The interrupt index to configure
- * @irq_cfg: IRQ configuration
- *
- * Return: '0' on Success; Error code otherwise.
- */
-int dprc_set_obj_irq(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token,
- char *obj_type,
- int obj_id,
- u8 irq_index,
- struct dprc_irq_cfg *irq_cfg)
-{
- struct mc_command cmd = { 0 };
- struct dprc_cmd_set_obj_irq *cmd_params;
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_OBJ_IRQ,
- cmd_flags,
- token);
- cmd_params = (struct dprc_cmd_set_obj_irq *)cmd.params;
- cmd_params->irq_val = cpu_to_le32(irq_cfg->val);
- cmd_params->irq_index = irq_index;
- cmd_params->irq_addr = cpu_to_le64(irq_cfg->paddr);
- cmd_params->irq_num = cpu_to_le32(irq_cfg->irq_num);
- cmd_params->obj_id = cpu_to_le32(obj_id);
- strncpy(cmd_params->obj_type, obj_type, 16);
- cmd_params->obj_type[15] = '\0';
-
- /* send command to mc*/
- return mc_send_command(mc_io, &cmd);
-}
-EXPORT_SYMBOL_GPL(dprc_set_obj_irq);
-
-/**
- * dprc_get_obj_region() - Get region information for a specified object.
- * @mc_io: Pointer to MC portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @token: Token of DPRC object
- * @obj_type; Object type as returned in dprc_get_obj()
- * @obj_id: Unique object instance as returned in dprc_get_obj()
- * @region_index: The specific region to query
- * @region_desc: Returns the requested region descriptor
- *
- * Return: '0' on Success; Error code otherwise.
- */
-int dprc_get_obj_region(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token,
- char *obj_type,
- int obj_id,
- u8 region_index,
- struct dprc_region_desc *region_desc)
-{
- struct mc_command cmd = { 0 };
- struct dprc_cmd_get_obj_region *cmd_params;
- struct dprc_rsp_get_obj_region *rsp_params;
- int err;
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ_REG,
- cmd_flags, token);
- cmd_params = (struct dprc_cmd_get_obj_region *)cmd.params;
- cmd_params->obj_id = cpu_to_le32(obj_id);
- cmd_params->region_index = region_index;
- strncpy(cmd_params->obj_type, obj_type, 16);
- cmd_params->obj_type[15] = '\0';
-
- /* send command to mc*/
- err = mc_send_command(mc_io, &cmd);
- if (err)
- return err;
-
- /* retrieve response parameters */
- rsp_params = (struct dprc_rsp_get_obj_region *)cmd.params;
- region_desc->base_offset = le64_to_cpu(rsp_params->base_addr);
- region_desc->size = le32_to_cpu(rsp_params->size);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(dprc_get_obj_region);
-
-/**
- * dprc_get_api_version - Get Data Path Resource Container API version
- * @mc_io: Pointer to Mc portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @major_ver: Major version of Data Path Resource Container API
- * @minor_ver: Minor version of Data Path Resource Container API
- *
- * Return: '0' on Success; Error code otherwise.
- */
-int dprc_get_api_version(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 *major_ver,
- u16 *minor_ver)
-{
- struct mc_command cmd = { 0 };
- int err;
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_API_VERSION,
- cmd_flags, 0);
-
- /* send command to mc */
- err = mc_send_command(mc_io, &cmd);
- if (err)
- return err;
-
- /* retrieve response parameters */
- mc_cmd_read_api_version(&cmd, major_ver, minor_ver);
-
- return 0;
-}
-
-/**
- * dprc_get_container_id - Get container ID associated with a given portal.
- * @mc_io: Pointer to Mc portal's I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @container_id: Requested container id
- *
- * Return: '0' on Success; Error code otherwise.
- */
-int dprc_get_container_id(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- int *container_id)
-{
- struct mc_command cmd = { 0 };
- int err;
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_CONT_ID,
- cmd_flags,
- 0);
-
- /* send command to mc*/
- err = mc_send_command(mc_io, &cmd);
- if (err)
- return err;
-
- /* retrieve response parameters */
- *container_id = (int)mc_cmd_read_object_id(&cmd);
-
- return 0;
-}
diff --git a/drivers/staging/fsl-mc/bus/fsl-mc-allocator.c b/drivers/staging/fsl-mc/bus/fsl-mc-allocator.c
deleted file mode 100644
index 8f313a41240b..000000000000
--- a/drivers/staging/fsl-mc/bus/fsl-mc-allocator.c
+++ /dev/null
@@ -1,648 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * fsl-mc object allocator driver
- *
- * Copyright (C) 2013-2016 Freescale Semiconductor, Inc.
- *
- */
-
-#include <linux/module.h>
-#include <linux/msi.h>
-#include "../include/mc.h"
-
-#include "fsl-mc-private.h"
-
-static bool __must_check fsl_mc_is_allocatable(struct fsl_mc_device *mc_dev)
-{
- return is_fsl_mc_bus_dpbp(mc_dev) ||
- is_fsl_mc_bus_dpmcp(mc_dev) ||
- is_fsl_mc_bus_dpcon(mc_dev);
-}
-
-/**
- * fsl_mc_resource_pool_add_device - add allocatable object to a resource
- * pool of a given fsl-mc bus
- *
- * @mc_bus: pointer to the fsl-mc bus
- * @pool_type: pool type
- * @mc_dev: pointer to allocatable fsl-mc device
- */
-static int __must_check fsl_mc_resource_pool_add_device(struct fsl_mc_bus
- *mc_bus,
- enum fsl_mc_pool_type
- pool_type,
- struct fsl_mc_device
- *mc_dev)
-{
- struct fsl_mc_resource_pool *res_pool;
- struct fsl_mc_resource *resource;
- struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev;
- int error = -EINVAL;
-
- if (pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES)
- goto out;
- if (!fsl_mc_is_allocatable(mc_dev))
- goto out;
- if (mc_dev->resource)
- goto out;
-
- res_pool = &mc_bus->resource_pools[pool_type];
- if (res_pool->type != pool_type)
- goto out;
- if (res_pool->mc_bus != mc_bus)
- goto out;
-
- mutex_lock(&res_pool->mutex);
-
- if (res_pool->max_count < 0)
- goto out_unlock;
- if (res_pool->free_count < 0 ||
- res_pool->free_count > res_pool->max_count)
- goto out_unlock;
-
- resource = devm_kzalloc(&mc_bus_dev->dev, sizeof(*resource),
- GFP_KERNEL);
- if (!resource) {
- error = -ENOMEM;
- dev_err(&mc_bus_dev->dev,
- "Failed to allocate memory for fsl_mc_resource\n");
- goto out_unlock;
- }
-
- resource->type = pool_type;
- resource->id = mc_dev->obj_desc.id;
- resource->data = mc_dev;
- resource->parent_pool = res_pool;
- INIT_LIST_HEAD(&resource->node);
- list_add_tail(&resource->node, &res_pool->free_list);
- mc_dev->resource = resource;
- res_pool->free_count++;
- res_pool->max_count++;
- error = 0;
-out_unlock:
- mutex_unlock(&res_pool->mutex);
-out:
- return error;
-}
-
-/**
- * fsl_mc_resource_pool_remove_device - remove an allocatable device from a
- * resource pool
- *
- * @mc_dev: pointer to allocatable fsl-mc device
- *
- * It permanently removes an allocatable fsl-mc device from the resource
- * pool. It's an error if the device is in use.
- */
-static int __must_check fsl_mc_resource_pool_remove_device(struct fsl_mc_device
- *mc_dev)
-{
- struct fsl_mc_device *mc_bus_dev;
- struct fsl_mc_bus *mc_bus;
- struct fsl_mc_resource_pool *res_pool;
- struct fsl_mc_resource *resource;
- int error = -EINVAL;
-
- if (!fsl_mc_is_allocatable(mc_dev))
- goto out;
-
- resource = mc_dev->resource;
- if (!resource || resource->data != mc_dev)
- goto out;
-
- mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
- mc_bus = to_fsl_mc_bus(mc_bus_dev);
- res_pool = resource->parent_pool;
- if (res_pool != &mc_bus->resource_pools[resource->type])
- goto out;
-
- mutex_lock(&res_pool->mutex);
-
- if (res_pool->max_count <= 0)
- goto out_unlock;
- if (res_pool->free_count <= 0 ||
- res_pool->free_count > res_pool->max_count)
- goto out_unlock;
-
- /*
- * If the device is currently allocated, its resource is not
- * in the free list and thus, the device cannot be removed.
- */
- if (list_empty(&resource->node)) {
- error = -EBUSY;
- dev_err(&mc_bus_dev->dev,
- "Device %s cannot be removed from resource pool\n",
- dev_name(&mc_dev->dev));
- goto out_unlock;
- }
-
- list_del_init(&resource->node);
- res_pool->free_count--;
- res_pool->max_count--;
-
- devm_kfree(&mc_bus_dev->dev, resource);
- mc_dev->resource = NULL;
- error = 0;
-out_unlock:
- mutex_unlock(&res_pool->mutex);
-out:
- return error;
-}
-
-static const char *const fsl_mc_pool_type_strings[] = {
- [FSL_MC_POOL_DPMCP] = "dpmcp",
- [FSL_MC_POOL_DPBP] = "dpbp",
- [FSL_MC_POOL_DPCON] = "dpcon",
- [FSL_MC_POOL_IRQ] = "irq",
-};
-
-static int __must_check object_type_to_pool_type(const char *object_type,
- enum fsl_mc_pool_type
- *pool_type)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(fsl_mc_pool_type_strings); i++) {
- if (strcmp(object_type, fsl_mc_pool_type_strings[i]) == 0) {
- *pool_type = i;
- return 0;
- }
- }
-
- return -EINVAL;
-}
-
-int __must_check fsl_mc_resource_allocate(struct fsl_mc_bus *mc_bus,
- enum fsl_mc_pool_type pool_type,
- struct fsl_mc_resource **new_resource)
-{
- struct fsl_mc_resource_pool *res_pool;
- struct fsl_mc_resource *resource;
- struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev;
- int error = -EINVAL;
-
- BUILD_BUG_ON(ARRAY_SIZE(fsl_mc_pool_type_strings) !=
- FSL_MC_NUM_POOL_TYPES);
-
- *new_resource = NULL;
- if (pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES)
- goto out;
-
- res_pool = &mc_bus->resource_pools[pool_type];
- if (res_pool->mc_bus != mc_bus)
- goto out;
-
- mutex_lock(&res_pool->mutex);
- resource = list_first_entry_or_null(&res_pool->free_list,
- struct fsl_mc_resource, node);
-
- if (!resource) {
- error = -ENXIO;
- dev_err(&mc_bus_dev->dev,
- "No more resources of type %s left\n",
- fsl_mc_pool_type_strings[pool_type]);
- goto out_unlock;
- }
-
- if (resource->type != pool_type)
- goto out_unlock;
- if (resource->parent_pool != res_pool)
- goto out_unlock;
- if (res_pool->free_count <= 0 ||
- res_pool->free_count > res_pool->max_count)
- goto out_unlock;
-
- list_del_init(&resource->node);
-
- res_pool->free_count--;
- error = 0;
-out_unlock:
- mutex_unlock(&res_pool->mutex);
- *new_resource = resource;
-out:
- return error;
-}
-EXPORT_SYMBOL_GPL(fsl_mc_resource_allocate);
-
-void fsl_mc_resource_free(struct fsl_mc_resource *resource)
-{
- struct fsl_mc_resource_pool *res_pool;
-
- res_pool = resource->parent_pool;
- if (resource->type != res_pool->type)
- return;
-
- mutex_lock(&res_pool->mutex);
- if (res_pool->free_count < 0 ||
- res_pool->free_count >= res_pool->max_count)
- goto out_unlock;
-
- if (!list_empty(&resource->node))
- goto out_unlock;
-
- list_add_tail(&resource->node, &res_pool->free_list);
- res_pool->free_count++;
-out_unlock:
- mutex_unlock(&res_pool->mutex);
-}
-EXPORT_SYMBOL_GPL(fsl_mc_resource_free);
-
-/**
- * fsl_mc_object_allocate - Allocates an fsl-mc object of the given
- * pool type from a given fsl-mc bus instance
- *
- * @mc_dev: fsl-mc device which is used in conjunction with the
- * allocated object
- * @pool_type: pool type
- * @new_mc_dev: pointer to area where the pointer to the allocated device
- * is to be returned
- *
- * Allocatable objects are always used in conjunction with some functional
- * device. This function allocates an object of the specified type from
- * the DPRC containing the functional device.
- *
- * NOTE: pool_type must be different from FSL_MC_POOL_MCP, since MC
- * portals are allocated using fsl_mc_portal_allocate(), instead of
- * this function.
- */
-int __must_check fsl_mc_object_allocate(struct fsl_mc_device *mc_dev,
- enum fsl_mc_pool_type pool_type,
- struct fsl_mc_device **new_mc_adev)
-{
- struct fsl_mc_device *mc_bus_dev;
- struct fsl_mc_bus *mc_bus;
- struct fsl_mc_device *mc_adev;
- int error = -EINVAL;
- struct fsl_mc_resource *resource = NULL;
-
- *new_mc_adev = NULL;
- if (mc_dev->flags & FSL_MC_IS_DPRC)
- goto error;
-
- if (!dev_is_fsl_mc(mc_dev->dev.parent))
- goto error;
-
- if (pool_type == FSL_MC_POOL_DPMCP)
- goto error;
-
- mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
- mc_bus = to_fsl_mc_bus(mc_bus_dev);
- error = fsl_mc_resource_allocate(mc_bus, pool_type, &resource);
- if (error < 0)
- goto error;
-
- mc_adev = resource->data;
- if (!mc_adev)
- goto error;
-
- *new_mc_adev = mc_adev;
- return 0;
-error:
- if (resource)
- fsl_mc_resource_free(resource);
-
- return error;
-}
-EXPORT_SYMBOL_GPL(fsl_mc_object_allocate);
-
-/**
- * fsl_mc_object_free - Returns an fsl-mc object to the resource
- * pool where it came from.
- * @mc_adev: Pointer to the fsl-mc device
- */
-void fsl_mc_object_free(struct fsl_mc_device *mc_adev)
-{
- struct fsl_mc_resource *resource;
-
- resource = mc_adev->resource;
- if (resource->type == FSL_MC_POOL_DPMCP)
- return;
- if (resource->data != mc_adev)
- return;
-
- fsl_mc_resource_free(resource);
-}
-EXPORT_SYMBOL_GPL(fsl_mc_object_free);
-
-/*
- * A DPRC and the devices in the DPRC all share the same GIC-ITS device
- * ID. A block of IRQs is pre-allocated and maintained in a pool
- * from which devices can allocate them when needed.
- */
-
-/*
- * Initialize the interrupt pool associated with an fsl-mc bus.
- * It allocates a block of IRQs from the GIC-ITS.
- */
-int fsl_mc_populate_irq_pool(struct fsl_mc_bus *mc_bus,
- unsigned int irq_count)
-{
- unsigned int i;
- struct msi_desc *msi_desc;
- struct fsl_mc_device_irq *irq_resources;
- struct fsl_mc_device_irq *mc_dev_irq;
- int error;
- struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev;
- struct fsl_mc_resource_pool *res_pool =
- &mc_bus->resource_pools[FSL_MC_POOL_IRQ];
-
- if (irq_count == 0 ||
- irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS)
- return -EINVAL;
-
- error = fsl_mc_msi_domain_alloc_irqs(&mc_bus_dev->dev, irq_count);
- if (error < 0)
- return error;
-
- irq_resources = devm_kzalloc(&mc_bus_dev->dev,
- sizeof(*irq_resources) * irq_count,
- GFP_KERNEL);
- if (!irq_resources) {
- error = -ENOMEM;
- goto cleanup_msi_irqs;
- }
-
- for (i = 0; i < irq_count; i++) {
- mc_dev_irq = &irq_resources[i];
-
- /*
- * NOTE: This mc_dev_irq's MSI addr/value pair will be set
- * by the fsl_mc_msi_write_msg() callback
- */
- mc_dev_irq->resource.type = res_pool->type;
- mc_dev_irq->resource.data = mc_dev_irq;
- mc_dev_irq->resource.parent_pool = res_pool;
- INIT_LIST_HEAD(&mc_dev_irq->resource.node);
- list_add_tail(&mc_dev_irq->resource.node, &res_pool->free_list);
- }
-
- for_each_msi_entry(msi_desc, &mc_bus_dev->dev) {
- mc_dev_irq = &irq_resources[msi_desc->fsl_mc.msi_index];
- mc_dev_irq->msi_desc = msi_desc;
- mc_dev_irq->resource.id = msi_desc->irq;
- }
-
- res_pool->max_count = irq_count;
- res_pool->free_count = irq_count;
- mc_bus->irq_resources = irq_resources;
- return 0;
-
-cleanup_msi_irqs:
- fsl_mc_msi_domain_free_irqs(&mc_bus_dev->dev);
- return error;
-}
-EXPORT_SYMBOL_GPL(fsl_mc_populate_irq_pool);
-
-/**
- * Teardown the interrupt pool associated with an fsl-mc bus.
- * It frees the IRQs that were allocated to the pool, back to the GIC-ITS.
- */
-void fsl_mc_cleanup_irq_pool(struct fsl_mc_bus *mc_bus)
-{
- struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev;
- struct fsl_mc_resource_pool *res_pool =
- &mc_bus->resource_pools[FSL_MC_POOL_IRQ];
-
- if (!mc_bus->irq_resources)
- return;
-
- if (res_pool->max_count == 0)
- return;
-
- if (res_pool->free_count != res_pool->max_count)
- return;
-
- INIT_LIST_HEAD(&res_pool->free_list);
- res_pool->max_count = 0;
- res_pool->free_count = 0;
- mc_bus->irq_resources = NULL;
- fsl_mc_msi_domain_free_irqs(&mc_bus_dev->dev);
-}
-EXPORT_SYMBOL_GPL(fsl_mc_cleanup_irq_pool);
-
-/**
- * Allocate the IRQs required by a given fsl-mc device.
- */
-int __must_check fsl_mc_allocate_irqs(struct fsl_mc_device *mc_dev)
-{
- int i;
- int irq_count;
- int res_allocated_count = 0;
- int error = -EINVAL;
- struct fsl_mc_device_irq **irqs = NULL;
- struct fsl_mc_bus *mc_bus;
- struct fsl_mc_resource_pool *res_pool;
-
- if (mc_dev->irqs)
- return -EINVAL;
-
- irq_count = mc_dev->obj_desc.irq_count;
- if (irq_count == 0)
- return -EINVAL;
-
- if (is_fsl_mc_bus_dprc(mc_dev))
- mc_bus = to_fsl_mc_bus(mc_dev);
- else
- mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent));
-
- if (!mc_bus->irq_resources)
- return -EINVAL;
-
- res_pool = &mc_bus->resource_pools[FSL_MC_POOL_IRQ];
- if (res_pool->free_count < irq_count) {
- dev_err(&mc_dev->dev,
- "Not able to allocate %u irqs for device\n", irq_count);
- return -ENOSPC;
- }
-
- irqs = devm_kzalloc(&mc_dev->dev, irq_count * sizeof(irqs[0]),
- GFP_KERNEL);
- if (!irqs)
- return -ENOMEM;
-
- for (i = 0; i < irq_count; i++) {
- struct fsl_mc_resource *resource;
-
- error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_IRQ,
- &resource);
- if (error < 0)
- goto error_resource_alloc;
-
- irqs[i] = to_fsl_mc_irq(resource);
- res_allocated_count++;
-
- irqs[i]->mc_dev = mc_dev;
- irqs[i]->dev_irq_index = i;
- }
-
- mc_dev->irqs = irqs;
- return 0;
-
-error_resource_alloc:
- for (i = 0; i < res_allocated_count; i++) {
- irqs[i]->mc_dev = NULL;
- fsl_mc_resource_free(&irqs[i]->resource);
- }
-
- return error;
-}
-EXPORT_SYMBOL_GPL(fsl_mc_allocate_irqs);
-
-/*
- * Frees the IRQs that were allocated for an fsl-mc device.
- */
-void fsl_mc_free_irqs(struct fsl_mc_device *mc_dev)
-{
- int i;
- int irq_count;
- struct fsl_mc_bus *mc_bus;
- struct fsl_mc_device_irq **irqs = mc_dev->irqs;
-
- if (!irqs)
- return;
-
- irq_count = mc_dev->obj_desc.irq_count;
-
- if (is_fsl_mc_bus_dprc(mc_dev))
- mc_bus = to_fsl_mc_bus(mc_dev);
- else
- mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent));
-
- if (!mc_bus->irq_resources)
- return;
-
- for (i = 0; i < irq_count; i++) {
- irqs[i]->mc_dev = NULL;
- fsl_mc_resource_free(&irqs[i]->resource);
- }
-
- mc_dev->irqs = NULL;
-}
-EXPORT_SYMBOL_GPL(fsl_mc_free_irqs);
-
-void fsl_mc_init_all_resource_pools(struct fsl_mc_device *mc_bus_dev)
-{
- int pool_type;
- struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
-
- for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++) {
- struct fsl_mc_resource_pool *res_pool =
- &mc_bus->resource_pools[pool_type];
-
- res_pool->type = pool_type;
- res_pool->max_count = 0;
- res_pool->free_count = 0;
- res_pool->mc_bus = mc_bus;
- INIT_LIST_HEAD(&res_pool->free_list);
- mutex_init(&res_pool->mutex);
- }
-}
-
-static void fsl_mc_cleanup_resource_pool(struct fsl_mc_device *mc_bus_dev,
- enum fsl_mc_pool_type pool_type)
-{
- struct fsl_mc_resource *resource;
- struct fsl_mc_resource *next;
- struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
- struct fsl_mc_resource_pool *res_pool =
- &mc_bus->resource_pools[pool_type];
- int free_count = 0;
-
- list_for_each_entry_safe(resource, next, &res_pool->free_list, node) {
- free_count++;
- devm_kfree(&mc_bus_dev->dev, resource);
- }
-}
-
-void fsl_mc_cleanup_all_resource_pools(struct fsl_mc_device *mc_bus_dev)
-{
- int pool_type;
-
- for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++)
- fsl_mc_cleanup_resource_pool(mc_bus_dev, pool_type);
-}
-
-/**
- * fsl_mc_allocator_probe - callback invoked when an allocatable device is
- * being added to the system
- */
-static int fsl_mc_allocator_probe(struct fsl_mc_device *mc_dev)
-{
- enum fsl_mc_pool_type pool_type;
- struct fsl_mc_device *mc_bus_dev;
- struct fsl_mc_bus *mc_bus;
- int error;
-
- if (!fsl_mc_is_allocatable(mc_dev))
- return -EINVAL;
-
- mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
- if (!dev_is_fsl_mc(&mc_bus_dev->dev))
- return -EINVAL;
-
- mc_bus = to_fsl_mc_bus(mc_bus_dev);
- error = object_type_to_pool_type(mc_dev->obj_desc.type, &pool_type);
- if (error < 0)
- return error;
-
- error = fsl_mc_resource_pool_add_device(mc_bus, pool_type, mc_dev);
- if (error < 0)
- return error;
-
- dev_dbg(&mc_dev->dev,
- "Allocatable fsl-mc device bound to fsl_mc_allocator driver");
- return 0;
-}
-
-/**
- * fsl_mc_allocator_remove - callback invoked when an allocatable device is
- * being removed from the system
- */
-static int fsl_mc_allocator_remove(struct fsl_mc_device *mc_dev)
-{
- int error;
-
- if (!fsl_mc_is_allocatable(mc_dev))
- return -EINVAL;
-
- if (mc_dev->resource) {
- error = fsl_mc_resource_pool_remove_device(mc_dev);
- if (error < 0)
- return error;
- }
-
- dev_dbg(&mc_dev->dev,
- "Allocatable fsl-mc device unbound from fsl_mc_allocator driver");
- return 0;
-}
-
-static const struct fsl_mc_device_id match_id_table[] = {
- {
- .vendor = FSL_MC_VENDOR_FREESCALE,
- .obj_type = "dpbp",
- },
- {
- .vendor = FSL_MC_VENDOR_FREESCALE,
- .obj_type = "dpmcp",
- },
- {
- .vendor = FSL_MC_VENDOR_FREESCALE,
- .obj_type = "dpcon",
- },
- {.vendor = 0x0},
-};
-
-static struct fsl_mc_driver fsl_mc_allocator_driver = {
- .driver = {
- .name = "fsl_mc_allocator",
- .pm = NULL,
- },
- .match_id_table = match_id_table,
- .probe = fsl_mc_allocator_probe,
- .remove = fsl_mc_allocator_remove,
-};
-
-int __init fsl_mc_allocator_driver_init(void)
-{
- return fsl_mc_driver_register(&fsl_mc_allocator_driver);
-}
diff --git a/drivers/staging/fsl-mc/bus/fsl-mc-bus.c b/drivers/staging/fsl-mc/bus/fsl-mc-bus.c
deleted file mode 100644
index 1b333c43aae9..000000000000
--- a/drivers/staging/fsl-mc/bus/fsl-mc-bus.c
+++ /dev/null
@@ -1,948 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Freescale Management Complex (MC) bus driver
- *
- * Copyright (C) 2014-2016 Freescale Semiconductor, Inc.
- * Author: German Rivera <German.Rivera@freescale.com>
- *
- */
-
-#define pr_fmt(fmt) "fsl-mc: " fmt
-
-#include <linux/module.h>
-#include <linux/of_device.h>
-#include <linux/of_address.h>
-#include <linux/ioport.h>
-#include <linux/slab.h>
-#include <linux/limits.h>
-#include <linux/bitops.h>
-#include <linux/msi.h>
-#include <linux/dma-mapping.h>
-
-#include "fsl-mc-private.h"
-
-/**
- * Default DMA mask for devices on a fsl-mc bus
- */
-#define FSL_MC_DEFAULT_DMA_MASK (~0ULL)
-
-/**
- * struct fsl_mc - Private data of a "fsl,qoriq-mc" platform device
- * @root_mc_bus_dev: fsl-mc device representing the root DPRC
- * @num_translation_ranges: number of entries in addr_translation_ranges
- * @translation_ranges: array of bus to system address translation ranges
- */
-struct fsl_mc {
- struct fsl_mc_device *root_mc_bus_dev;
- u8 num_translation_ranges;
- struct fsl_mc_addr_translation_range *translation_ranges;
-};
-
-/**
- * struct fsl_mc_addr_translation_range - bus to system address translation
- * range
- * @mc_region_type: Type of MC region for the range being translated
- * @start_mc_offset: Start MC offset of the range being translated
- * @end_mc_offset: MC offset of the first byte after the range (last MC
- * offset of the range is end_mc_offset - 1)
- * @start_phys_addr: system physical address corresponding to start_mc_addr
- */
-struct fsl_mc_addr_translation_range {
- enum dprc_region_type mc_region_type;
- u64 start_mc_offset;
- u64 end_mc_offset;
- phys_addr_t start_phys_addr;
-};
-
-/**
- * struct mc_version
- * @major: Major version number: incremented on API compatibility changes
- * @minor: Minor version number: incremented on API additions (that are
- * backward compatible); reset when major version is incremented
- * @revision: Internal revision number: incremented on implementation changes
- * and/or bug fixes that have no impact on API
- */
-struct mc_version {
- u32 major;
- u32 minor;
- u32 revision;
-};
-
-/**
- * fsl_mc_bus_match - device to driver matching callback
- * @dev: the fsl-mc device to match against
- * @drv: the device driver to search for matching fsl-mc object type
- * structures
- *
- * Returns 1 on success, 0 otherwise.
- */
-static int fsl_mc_bus_match(struct device *dev, struct device_driver *drv)
-{
- const struct fsl_mc_device_id *id;
- struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
- struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(drv);
- bool found = false;
-
- if (!mc_drv->match_id_table)
- goto out;
-
- /*
- * If the object is not 'plugged' don't match.
- * Only exception is the root DPRC, which is a special case.
- */
- if ((mc_dev->obj_desc.state & FSL_MC_OBJ_STATE_PLUGGED) == 0 &&
- !fsl_mc_is_root_dprc(&mc_dev->dev))
- goto out;
-
- /*
- * Traverse the match_id table of the given driver, trying to find
- * a matching for the given device.
- */
- for (id = mc_drv->match_id_table; id->vendor != 0x0; id++) {
- if (id->vendor == mc_dev->obj_desc.vendor &&
- strcmp(id->obj_type, mc_dev->obj_desc.type) == 0) {
- found = true;
-
- break;
- }
- }
-
-out:
- dev_dbg(dev, "%smatched\n", found ? "" : "not ");
- return found;
-}
-
-/**
- * fsl_mc_bus_uevent - callback invoked when a device is added
- */
-static int fsl_mc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
-{
- struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
-
- if (add_uevent_var(env, "MODALIAS=fsl-mc:v%08Xd%s",
- mc_dev->obj_desc.vendor,
- mc_dev->obj_desc.type))
- return -ENOMEM;
-
- return 0;
-}
-
-static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
-
- return sprintf(buf, "fsl-mc:v%08Xd%s\n", mc_dev->obj_desc.vendor,
- mc_dev->obj_desc.type);
-}
-static DEVICE_ATTR_RO(modalias);
-
-static struct attribute *fsl_mc_dev_attrs[] = {
- &dev_attr_modalias.attr,
- NULL,
-};
-
-ATTRIBUTE_GROUPS(fsl_mc_dev);
-
-struct bus_type fsl_mc_bus_type = {
- .name = "fsl-mc",
- .match = fsl_mc_bus_match,
- .uevent = fsl_mc_bus_uevent,
- .dev_groups = fsl_mc_dev_groups,
-};
-EXPORT_SYMBOL_GPL(fsl_mc_bus_type);
-
-struct device_type fsl_mc_bus_dprc_type = {
- .name = "fsl_mc_bus_dprc"
-};
-
-struct device_type fsl_mc_bus_dpni_type = {
- .name = "fsl_mc_bus_dpni"
-};
-
-struct device_type fsl_mc_bus_dpio_type = {
- .name = "fsl_mc_bus_dpio"
-};
-
-struct device_type fsl_mc_bus_dpsw_type = {
- .name = "fsl_mc_bus_dpsw"
-};
-
-struct device_type fsl_mc_bus_dpbp_type = {
- .name = "fsl_mc_bus_dpbp"
-};
-
-struct device_type fsl_mc_bus_dpcon_type = {
- .name = "fsl_mc_bus_dpcon"
-};
-
-struct device_type fsl_mc_bus_dpmcp_type = {
- .name = "fsl_mc_bus_dpmcp"
-};
-
-struct device_type fsl_mc_bus_dpmac_type = {
- .name = "fsl_mc_bus_dpmac"
-};
-
-struct device_type fsl_mc_bus_dprtc_type = {
- .name = "fsl_mc_bus_dprtc"
-};
-
-static struct device_type *fsl_mc_get_device_type(const char *type)
-{
- static const struct {
- struct device_type *dev_type;
- const char *type;
- } dev_types[] = {
- { &fsl_mc_bus_dprc_type, "dprc" },
- { &fsl_mc_bus_dpni_type, "dpni" },
- { &fsl_mc_bus_dpio_type, "dpio" },
- { &fsl_mc_bus_dpsw_type, "dpsw" },
- { &fsl_mc_bus_dpbp_type, "dpbp" },
- { &fsl_mc_bus_dpcon_type, "dpcon" },
- { &fsl_mc_bus_dpmcp_type, "dpmcp" },
- { &fsl_mc_bus_dpmac_type, "dpmac" },
- { &fsl_mc_bus_dprtc_type, "dprtc" },
- { NULL, NULL }
- };
- int i;
-
- for (i = 0; dev_types[i].dev_type; i++)
- if (!strcmp(dev_types[i].type, type))
- return dev_types[i].dev_type;
-
- return NULL;
-}
-
-static int fsl_mc_driver_probe(struct device *dev)
-{
- struct fsl_mc_driver *mc_drv;
- struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
- int error;
-
- mc_drv = to_fsl_mc_driver(dev->driver);
-
- error = mc_drv->probe(mc_dev);
- if (error < 0) {
- if (error != -EPROBE_DEFER)
- dev_err(dev, "%s failed: %d\n", __func__, error);
- return error;
- }
-
- return 0;
-}
-
-static int fsl_mc_driver_remove(struct device *dev)
-{
- struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
- struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
- int error;
-
- error = mc_drv->remove(mc_dev);
- if (error < 0) {
- dev_err(dev, "%s failed: %d\n", __func__, error);
- return error;
- }
-
- return 0;
-}
-
-static void fsl_mc_driver_shutdown(struct device *dev)
-{
- struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
- struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
-
- mc_drv->shutdown(mc_dev);
-}
-
-/**
- * __fsl_mc_driver_register - registers a child device driver with the
- * MC bus
- *
- * This function is implicitly invoked from the registration function of
- * fsl_mc device drivers, which is generated by the
- * module_fsl_mc_driver() macro.
- */
-int __fsl_mc_driver_register(struct fsl_mc_driver *mc_driver,
- struct module *owner)
-{
- int error;
-
- mc_driver->driver.owner = owner;
- mc_driver->driver.bus = &fsl_mc_bus_type;
-
- if (mc_driver->probe)
- mc_driver->driver.probe = fsl_mc_driver_probe;
-
- if (mc_driver->remove)
- mc_driver->driver.remove = fsl_mc_driver_remove;
-
- if (mc_driver->shutdown)
- mc_driver->driver.shutdown = fsl_mc_driver_shutdown;
-
- error = driver_register(&mc_driver->driver);
- if (error < 0) {
- pr_err("driver_register() failed for %s: %d\n",
- mc_driver->driver.name, error);
- return error;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(__fsl_mc_driver_register);
-
-/**
- * fsl_mc_driver_unregister - unregisters a device driver from the
- * MC bus
- */
-void fsl_mc_driver_unregister(struct fsl_mc_driver *mc_driver)
-{
- driver_unregister(&mc_driver->driver);
-}
-EXPORT_SYMBOL_GPL(fsl_mc_driver_unregister);
-
-/**
- * mc_get_version() - Retrieves the Management Complex firmware
- * version information
- * @mc_io: Pointer to opaque I/O object
- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
- * @mc_ver_info: Returned version information structure
- *
- * Return: '0' on Success; Error code otherwise.
- */
-static int mc_get_version(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- struct mc_version *mc_ver_info)
-{
- struct mc_command cmd = { 0 };
- struct dpmng_rsp_get_version *rsp_params;
- int err;
-
- /* prepare command */
- cmd.header = mc_encode_cmd_header(DPMNG_CMDID_GET_VERSION,
- cmd_flags,
- 0);
-
- /* send command to mc*/
- err = mc_send_command(mc_io, &cmd);
- if (err)
- return err;
-
- /* retrieve response parameters */
- rsp_params = (struct dpmng_rsp_get_version *)cmd.params;
- mc_ver_info->revision = le32_to_cpu(rsp_params->revision);
- mc_ver_info->major = le32_to_cpu(rsp_params->version_major);
- mc_ver_info->minor = le32_to_cpu(rsp_params->version_minor);
-
- return 0;
-}
-
-/**
- * fsl_mc_get_root_dprc - function to traverse to the root dprc
- */
-static void fsl_mc_get_root_dprc(struct device *dev,
- struct device **root_dprc_dev)
-{
- if (!dev) {
- *root_dprc_dev = NULL;
- } else if (!dev_is_fsl_mc(dev)) {
- *root_dprc_dev = NULL;
- } else {
- *root_dprc_dev = dev;
- while (dev_is_fsl_mc((*root_dprc_dev)->parent))
- *root_dprc_dev = (*root_dprc_dev)->parent;
- }
-}
-
-static int get_dprc_attr(struct fsl_mc_io *mc_io,
- int container_id, struct dprc_attributes *attr)
-{
- u16 dprc_handle;
- int error;
-
- error = dprc_open(mc_io, 0, container_id, &dprc_handle);
- if (error < 0) {
- dev_err(mc_io->dev, "dprc_open() failed: %d\n", error);
- return error;
- }
-
- memset(attr, 0, sizeof(struct dprc_attributes));
- error = dprc_get_attributes(mc_io, 0, dprc_handle, attr);
- if (error < 0) {
- dev_err(mc_io->dev, "dprc_get_attributes() failed: %d\n",
- error);
- goto common_cleanup;
- }
-
- error = 0;
-
-common_cleanup:
- (void)dprc_close(mc_io, 0, dprc_handle);
- return error;
-}
-
-static int get_dprc_icid(struct fsl_mc_io *mc_io,
- int container_id, u16 *icid)
-{
- struct dprc_attributes attr;
- int error;
-
- error = get_dprc_attr(mc_io, container_id, &attr);
- if (error == 0)
- *icid = attr.icid;
-
- return error;
-}
-
-static int translate_mc_addr(struct fsl_mc_device *mc_dev,
- enum dprc_region_type mc_region_type,
- u64 mc_offset, phys_addr_t *phys_addr)
-{
- int i;
- struct device *root_dprc_dev;
- struct fsl_mc *mc;
-
- fsl_mc_get_root_dprc(&mc_dev->dev, &root_dprc_dev);
- mc = dev_get_drvdata(root_dprc_dev->parent);
-
- if (mc->num_translation_ranges == 0) {
- /*
- * Do identity mapping:
- */
- *phys_addr = mc_offset;
- return 0;
- }
-
- for (i = 0; i < mc->num_translation_ranges; i++) {
- struct fsl_mc_addr_translation_range *range =
- &mc->translation_ranges[i];
-
- if (mc_region_type == range->mc_region_type &&
- mc_offset >= range->start_mc_offset &&
- mc_offset < range->end_mc_offset) {
- *phys_addr = range->start_phys_addr +
- (mc_offset - range->start_mc_offset);
- return 0;
- }
- }
-
- return -EFAULT;
-}
-
-static int fsl_mc_device_get_mmio_regions(struct fsl_mc_device *mc_dev,
- struct fsl_mc_device *mc_bus_dev)
-{
- int i;
- int error;
- struct resource *regions;
- struct fsl_mc_obj_desc *obj_desc = &mc_dev->obj_desc;
- struct device *parent_dev = mc_dev->dev.parent;
- enum dprc_region_type mc_region_type;
-
- if (is_fsl_mc_bus_dprc(mc_dev) ||
- is_fsl_mc_bus_dpmcp(mc_dev)) {
- mc_region_type = DPRC_REGION_TYPE_MC_PORTAL;
- } else if (is_fsl_mc_bus_dpio(mc_dev)) {
- mc_region_type = DPRC_REGION_TYPE_QBMAN_PORTAL;
- } else {
- /*
- * This function should not have been called for this MC object
- * type, as this object type is not supposed to have MMIO
- * regions
- */
- return -EINVAL;
- }
-
- regions = kmalloc_array(obj_desc->region_count,
- sizeof(regions[0]), GFP_KERNEL);
- if (!regions)
- return -ENOMEM;
-
- for (i = 0; i < obj_desc->region_count; i++) {
- struct dprc_region_desc region_desc;
-
- error = dprc_get_obj_region(mc_bus_dev->mc_io,
- 0,
- mc_bus_dev->mc_handle,
- obj_desc->type,
- obj_desc->id, i, &region_desc);
- if (error < 0) {
- dev_err(parent_dev,
- "dprc_get_obj_region() failed: %d\n", error);
- goto error_cleanup_regions;
- }
-
- error = translate_mc_addr(mc_dev, mc_region_type,
- region_desc.base_offset,
- &regions[i].start);
- if (error < 0) {
- dev_err(parent_dev,
- "Invalid MC offset: %#x (for %s.%d\'s region %d)\n",
- region_desc.base_offset,
- obj_desc->type, obj_desc->id, i);
- goto error_cleanup_regions;
- }
-
- regions[i].end = regions[i].start + region_desc.size - 1;
- regions[i].name = "fsl-mc object MMIO region";
- regions[i].flags = IORESOURCE_IO;
- if (region_desc.flags & DPRC_REGION_CACHEABLE)
- regions[i].flags |= IORESOURCE_CACHEABLE;
- }
-
- mc_dev->regions = regions;
- return 0;
-
-error_cleanup_regions:
- kfree(regions);
- return error;
-}
-
-/**
- * fsl_mc_is_root_dprc - function to check if a given device is a root dprc
- */
-bool fsl_mc_is_root_dprc(struct device *dev)
-{
- struct device *root_dprc_dev;
-
- fsl_mc_get_root_dprc(dev, &root_dprc_dev);
- if (!root_dprc_dev)
- return false;
- return dev == root_dprc_dev;
-}
-
-static void fsl_mc_device_release(struct device *dev)
-{
- struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
-
- kfree(mc_dev->regions);
-
- if (is_fsl_mc_bus_dprc(mc_dev))
- kfree(to_fsl_mc_bus(mc_dev));
- else
- kfree(mc_dev);
-}
-
-/**
- * Add a newly discovered fsl-mc device to be visible in Linux
- */
-int fsl_mc_device_add(struct fsl_mc_obj_desc *obj_desc,
- struct fsl_mc_io *mc_io,
- struct device *parent_dev,
- struct fsl_mc_device **new_mc_dev)
-{
- int error;
- struct fsl_mc_device *mc_dev = NULL;
- struct fsl_mc_bus *mc_bus = NULL;
- struct fsl_mc_device *parent_mc_dev;
-
- if (dev_is_fsl_mc(parent_dev))
- parent_mc_dev = to_fsl_mc_device(parent_dev);
- else
- parent_mc_dev = NULL;
-
- if (strcmp(obj_desc->type, "dprc") == 0) {
- /*
- * Allocate an MC bus device object:
- */
- mc_bus = kzalloc(sizeof(*mc_bus), GFP_KERNEL);
- if (!mc_bus)
- return -ENOMEM;
-
- mc_dev = &mc_bus->mc_dev;
- } else {
- /*
- * Allocate a regular fsl_mc_device object:
- */
- mc_dev = kzalloc(sizeof(*mc_dev), GFP_KERNEL);
- if (!mc_dev)
- return -ENOMEM;
- }
-
- mc_dev->obj_desc = *obj_desc;
- mc_dev->mc_io = mc_io;
- device_initialize(&mc_dev->dev);
- mc_dev->dev.parent = parent_dev;
- mc_dev->dev.bus = &fsl_mc_bus_type;
- mc_dev->dev.release = fsl_mc_device_release;
- mc_dev->dev.type = fsl_mc_get_device_type(obj_desc->type);
- if (!mc_dev->dev.type) {
- error = -ENODEV;
- dev_err(parent_dev, "unknown device type %s\n", obj_desc->type);
- goto error_cleanup_dev;
- }
- dev_set_name(&mc_dev->dev, "%s.%d", obj_desc->type, obj_desc->id);
-
- if (strcmp(obj_desc->type, "dprc") == 0) {
- struct fsl_mc_io *mc_io2;
-
- mc_dev->flags |= FSL_MC_IS_DPRC;
-
- /*
- * To get the DPRC's ICID, we need to open the DPRC
- * in get_dprc_icid(). For child DPRCs, we do so using the
- * parent DPRC's MC portal instead of the child DPRC's MC
- * portal, in case the child DPRC is already opened with
- * its own portal (e.g., the DPRC used by AIOP).
- *
- * NOTE: There cannot be more than one active open for a
- * given MC object, using the same MC portal.
- */
- if (parent_mc_dev) {
- /*
- * device being added is a child DPRC device
- */
- mc_io2 = parent_mc_dev->mc_io;
- } else {
- /*
- * device being added is the root DPRC device
- */
- if (!mc_io) {
- error = -EINVAL;
- goto error_cleanup_dev;
- }
-
- mc_io2 = mc_io;
- }
-
- error = get_dprc_icid(mc_io2, obj_desc->id, &mc_dev->icid);
- if (error < 0)
- goto error_cleanup_dev;
- } else {
- /*
- * A non-DPRC object has to be a child of a DPRC, use the
- * parent's ICID and interrupt domain.
- */
- mc_dev->icid = parent_mc_dev->icid;
- mc_dev->dma_mask = FSL_MC_DEFAULT_DMA_MASK;
- mc_dev->dev.dma_mask = &mc_dev->dma_mask;
- dev_set_msi_domain(&mc_dev->dev,
- dev_get_msi_domain(&parent_mc_dev->dev));
- }
-
- /*
- * Get MMIO regions for the device from the MC:
- *
- * NOTE: the root DPRC is a special case as its MMIO region is
- * obtained from the device tree
- */
- if (parent_mc_dev && obj_desc->region_count != 0) {
- error = fsl_mc_device_get_mmio_regions(mc_dev,
- parent_mc_dev);
- if (error < 0)
- goto error_cleanup_dev;
- }
-
- /* Objects are coherent, unless 'no shareability' flag set. */
- if (!(obj_desc->flags & FSL_MC_OBJ_FLAG_NO_MEM_SHAREABILITY))
- arch_setup_dma_ops(&mc_dev->dev, 0, 0, NULL, true);
-
- /*
- * The device-specific probe callback will get invoked by device_add()
- */
- error = device_add(&mc_dev->dev);
- if (error < 0) {
- dev_err(parent_dev,
- "device_add() failed for device %s: %d\n",
- dev_name(&mc_dev->dev), error);
- goto error_cleanup_dev;
- }
-
- dev_dbg(parent_dev, "added %s\n", dev_name(&mc_dev->dev));
-
- *new_mc_dev = mc_dev;
- return 0;
-
-error_cleanup_dev:
- kfree(mc_dev->regions);
- kfree(mc_bus);
- kfree(mc_dev);
-
- return error;
-}
-EXPORT_SYMBOL_GPL(fsl_mc_device_add);
-
-/**
- * fsl_mc_device_remove - Remove an fsl-mc device from being visible to
- * Linux
- *
- * @mc_dev: Pointer to an fsl-mc device
- */
-void fsl_mc_device_remove(struct fsl_mc_device *mc_dev)
-{
- /*
- * The device-specific remove callback will get invoked by device_del()
- */
- device_del(&mc_dev->dev);
- put_device(&mc_dev->dev);
-}
-EXPORT_SYMBOL_GPL(fsl_mc_device_remove);
-
-static int parse_mc_ranges(struct device *dev,
- int *paddr_cells,
- int *mc_addr_cells,
- int *mc_size_cells,
- const __be32 **ranges_start)
-{
- const __be32 *prop;
- int range_tuple_cell_count;
- int ranges_len;
- int tuple_len;
- struct device_node *mc_node = dev->of_node;
-
- *ranges_start = of_get_property(mc_node, "ranges", &ranges_len);
- if (!(*ranges_start) || !ranges_len) {
- dev_warn(dev,
- "missing or empty ranges property for device tree node '%s'\n",
- mc_node->name);
- return 0;
- }
-
- *paddr_cells = of_n_addr_cells(mc_node);
-
- prop = of_get_property(mc_node, "#address-cells", NULL);
- if (prop)
- *mc_addr_cells = be32_to_cpup(prop);
- else
- *mc_addr_cells = *paddr_cells;
-
- prop = of_get_property(mc_node, "#size-cells", NULL);
- if (prop)
- *mc_size_cells = be32_to_cpup(prop);
- else
- *mc_size_cells = of_n_size_cells(mc_node);
-
- range_tuple_cell_count = *paddr_cells + *mc_addr_cells +
- *mc_size_cells;
-
- tuple_len = range_tuple_cell_count * sizeof(__be32);
- if (ranges_len % tuple_len != 0) {
- dev_err(dev, "malformed ranges property '%s'\n", mc_node->name);
- return -EINVAL;
- }
-
- return ranges_len / tuple_len;
-}
-
-static int get_mc_addr_translation_ranges(struct device *dev,
- struct fsl_mc_addr_translation_range
- **ranges,
- u8 *num_ranges)
-{
- int ret;
- int paddr_cells;
- int mc_addr_cells;
- int mc_size_cells;
- int i;
- const __be32 *ranges_start;
- const __be32 *cell;
-
- ret = parse_mc_ranges(dev,
- &paddr_cells,
- &mc_addr_cells,
- &mc_size_cells,
- &ranges_start);
- if (ret < 0)
- return ret;
-
- *num_ranges = ret;
- if (!ret) {
- /*
- * Missing or empty ranges property ("ranges;") for the
- * 'fsl,qoriq-mc' node. In this case, identity mapping
- * will be used.
- */
- *ranges = NULL;
- return 0;
- }
-
- *ranges = devm_kcalloc(dev, *num_ranges,
- sizeof(struct fsl_mc_addr_translation_range),
- GFP_KERNEL);
- if (!(*ranges))
- return -ENOMEM;
-
- cell = ranges_start;
- for (i = 0; i < *num_ranges; ++i) {
- struct fsl_mc_addr_translation_range *range = &(*ranges)[i];
-
- range->mc_region_type = of_read_number(cell, 1);
- range->start_mc_offset = of_read_number(cell + 1,
- mc_addr_cells - 1);
- cell += mc_addr_cells;
- range->start_phys_addr = of_read_number(cell, paddr_cells);
- cell += paddr_cells;
- range->end_mc_offset = range->start_mc_offset +
- of_read_number(cell, mc_size_cells);
-
- cell += mc_size_cells;
- }
-
- return 0;
-}
-
-/**
- * fsl_mc_bus_probe - callback invoked when the root MC bus is being
- * added
- */
-static int fsl_mc_bus_probe(struct platform_device *pdev)
-{
- struct fsl_mc_obj_desc obj_desc;
- int error;
- struct fsl_mc *mc;
- struct fsl_mc_device *mc_bus_dev = NULL;
- struct fsl_mc_io *mc_io = NULL;
- int container_id;
- phys_addr_t mc_portal_phys_addr;
- u32 mc_portal_size;
- struct mc_version mc_version;
- struct resource res;
-
- mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL);
- if (!mc)
- return -ENOMEM;
-
- platform_set_drvdata(pdev, mc);
-
- /*
- * Get physical address of MC portal for the root DPRC:
- */
- error = of_address_to_resource(pdev->dev.of_node, 0, &res);
- if (error < 0) {
- dev_err(&pdev->dev,
- "of_address_to_resource() failed for %pOF\n",
- pdev->dev.of_node);
- return error;
- }
-
- mc_portal_phys_addr = res.start;
- mc_portal_size = resource_size(&res);
- error = fsl_create_mc_io(&pdev->dev, mc_portal_phys_addr,
- mc_portal_size, NULL,
- FSL_MC_IO_ATOMIC_CONTEXT_PORTAL, &mc_io);
- if (error < 0)
- return error;
-
- error = mc_get_version(mc_io, 0, &mc_version);
- if (error != 0) {
- dev_err(&pdev->dev,
- "mc_get_version() failed with error %d\n", error);
- goto error_cleanup_mc_io;
- }
-
- dev_info(&pdev->dev, "MC firmware version: %u.%u.%u\n",
- mc_version.major, mc_version.minor, mc_version.revision);
-
- error = get_mc_addr_translation_ranges(&pdev->dev,
- &mc->translation_ranges,
- &mc->num_translation_ranges);
- if (error < 0)
- goto error_cleanup_mc_io;
-
- error = dprc_get_container_id(mc_io, 0, &container_id);
- if (error < 0) {
- dev_err(&pdev->dev,
- "dprc_get_container_id() failed: %d\n", error);
- goto error_cleanup_mc_io;
- }
-
- memset(&obj_desc, 0, sizeof(struct fsl_mc_obj_desc));
- error = dprc_get_api_version(mc_io, 0,
- &obj_desc.ver_major,
- &obj_desc.ver_minor);
- if (error < 0)
- goto error_cleanup_mc_io;
-
- obj_desc.vendor = FSL_MC_VENDOR_FREESCALE;
- strcpy(obj_desc.type, "dprc");
- obj_desc.id = container_id;
- obj_desc.irq_count = 1;
- obj_desc.region_count = 0;
-
- error = fsl_mc_device_add(&obj_desc, mc_io, &pdev->dev, &mc_bus_dev);
- if (error < 0)
- goto error_cleanup_mc_io;
-
- mc->root_mc_bus_dev = mc_bus_dev;
- return 0;
-
-error_cleanup_mc_io:
- fsl_destroy_mc_io(mc_io);
- return error;
-}
-
-/**
- * fsl_mc_bus_remove - callback invoked when the root MC bus is being
- * removed
- */
-static int fsl_mc_bus_remove(struct platform_device *pdev)
-{
- struct fsl_mc *mc = platform_get_drvdata(pdev);
-
- if (!fsl_mc_is_root_dprc(&mc->root_mc_bus_dev->dev))
- return -EINVAL;
-
- fsl_mc_device_remove(mc->root_mc_bus_dev);
-
- fsl_destroy_mc_io(mc->root_mc_bus_dev->mc_io);
- mc->root_mc_bus_dev->mc_io = NULL;
-
- return 0;
-}
-
-static const struct of_device_id fsl_mc_bus_match_table[] = {
- {.compatible = "fsl,qoriq-mc",},
- {},
-};
-
-MODULE_DEVICE_TABLE(of, fsl_mc_bus_match_table);
-
-static struct platform_driver fsl_mc_bus_driver = {
- .driver = {
- .name = "fsl_mc_bus",
- .pm = NULL,
- .of_match_table = fsl_mc_bus_match_table,
- },
- .probe = fsl_mc_bus_probe,
- .remove = fsl_mc_bus_remove,
-};
-
-static int __init fsl_mc_bus_driver_init(void)
-{
- int error;
-
- error = bus_register(&fsl_mc_bus_type);
- if (error < 0) {
- pr_err("bus type registration failed: %d\n", error);
- goto error_cleanup_cache;
- }
-
- error = platform_driver_register(&fsl_mc_bus_driver);
- if (error < 0) {
- pr_err("platform_driver_register() failed: %d\n", error);
- goto error_cleanup_bus;
- }
-
- error = dprc_driver_init();
- if (error < 0)
- goto error_cleanup_driver;
-
- error = fsl_mc_allocator_driver_init();
- if (error < 0)
- goto error_cleanup_dprc_driver;
-
- return 0;
-
-error_cleanup_dprc_driver:
- dprc_driver_exit();
-
-error_cleanup_driver:
- platform_driver_unregister(&fsl_mc_bus_driver);
-
-error_cleanup_bus:
- bus_unregister(&fsl_mc_bus_type);
-
-error_cleanup_cache:
- return error;
-}
-postcore_initcall(fsl_mc_bus_driver_init);
diff --git a/drivers/staging/fsl-mc/bus/fsl-mc-msi.c b/drivers/staging/fsl-mc/bus/fsl-mc-msi.c
deleted file mode 100644
index 971ad87c584c..000000000000
--- a/drivers/staging/fsl-mc/bus/fsl-mc-msi.c
+++ /dev/null
@@ -1,284 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Freescale Management Complex (MC) bus driver MSI support
- *
- * Copyright (C) 2015-2016 Freescale Semiconductor, Inc.
- * Author: German Rivera <German.Rivera@freescale.com>
- *
- */
-
-#include <linux/of_device.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/irq.h>
-#include <linux/irqdomain.h>
-#include <linux/msi.h>
-#include "fsl-mc-private.h"
-
-#ifdef GENERIC_MSI_DOMAIN_OPS
-/*
- * Generate a unique ID identifying the interrupt (only used within the MSI
- * irqdomain. Combine the icid with the interrupt index.
- */
-static irq_hw_number_t fsl_mc_domain_calc_hwirq(struct fsl_mc_device *dev,
- struct msi_desc *desc)
-{
- /*
- * Make the base hwirq value for ICID*10000 so it is readable
- * as a decimal value in /proc/interrupts.
- */
- return (irq_hw_number_t)(desc->fsl_mc.msi_index + (dev->icid * 10000));
-}
-
-static void fsl_mc_msi_set_desc(msi_alloc_info_t *arg,
- struct msi_desc *desc)
-{
- arg->desc = desc;
- arg->hwirq = fsl_mc_domain_calc_hwirq(to_fsl_mc_device(desc->dev),
- desc);
-}
-#else
-#define fsl_mc_msi_set_desc NULL
-#endif
-
-static void fsl_mc_msi_update_dom_ops(struct msi_domain_info *info)
-{
- struct msi_domain_ops *ops = info->ops;
-
- if (!ops)
- return;
-
- /*
- * set_desc should not be set by the caller
- */
- if (!ops->set_desc)
- ops->set_desc = fsl_mc_msi_set_desc;
-}
-
-static void __fsl_mc_msi_write_msg(struct fsl_mc_device *mc_bus_dev,
- struct fsl_mc_device_irq *mc_dev_irq)
-{
- int error;
- struct fsl_mc_device *owner_mc_dev = mc_dev_irq->mc_dev;
- struct msi_desc *msi_desc = mc_dev_irq->msi_desc;
- struct dprc_irq_cfg irq_cfg;
-
- /*
- * msi_desc->msg.address is 0x0 when this function is invoked in
- * the free_irq() code path. In this case, for the MC, we don't
- * really need to "unprogram" the MSI, so we just return.
- */
- if (msi_desc->msg.address_lo == 0x0 && msi_desc->msg.address_hi == 0x0)
- return;
-
- if (!owner_mc_dev)
- return;
-
- irq_cfg.paddr = ((u64)msi_desc->msg.address_hi << 32) |
- msi_desc->msg.address_lo;
- irq_cfg.val = msi_desc->msg.data;
- irq_cfg.irq_num = msi_desc->irq;
-
- if (owner_mc_dev == mc_bus_dev) {
- /*
- * IRQ is for the mc_bus_dev's DPRC itself
- */
- error = dprc_set_irq(mc_bus_dev->mc_io,
- MC_CMD_FLAG_INTR_DIS | MC_CMD_FLAG_PRI,
- mc_bus_dev->mc_handle,
- mc_dev_irq->dev_irq_index,
- &irq_cfg);
- if (error < 0) {
- dev_err(&owner_mc_dev->dev,
- "dprc_set_irq() failed: %d\n", error);
- }
- } else {
- /*
- * IRQ is for for a child device of mc_bus_dev
- */
- error = dprc_set_obj_irq(mc_bus_dev->mc_io,
- MC_CMD_FLAG_INTR_DIS | MC_CMD_FLAG_PRI,
- mc_bus_dev->mc_handle,
- owner_mc_dev->obj_desc.type,
- owner_mc_dev->obj_desc.id,
- mc_dev_irq->dev_irq_index,
- &irq_cfg);
- if (error < 0) {
- dev_err(&owner_mc_dev->dev,
- "dprc_obj_set_irq() failed: %d\n", error);
- }
- }
-}
-
-/*
- * NOTE: This function is invoked with interrupts disabled
- */
-static void fsl_mc_msi_write_msg(struct irq_data *irq_data,
- struct msi_msg *msg)
-{
- struct msi_desc *msi_desc = irq_data_get_msi_desc(irq_data);
- struct fsl_mc_device *mc_bus_dev = to_fsl_mc_device(msi_desc->dev);
- struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
- struct fsl_mc_device_irq *mc_dev_irq =
- &mc_bus->irq_resources[msi_desc->fsl_mc.msi_index];
-
- msi_desc->msg = *msg;
-
- /*
- * Program the MSI (paddr, value) pair in the device:
- */
- __fsl_mc_msi_write_msg(mc_bus_dev, mc_dev_irq);
-}
-
-static void fsl_mc_msi_update_chip_ops(struct msi_domain_info *info)
-{
- struct irq_chip *chip = info->chip;
-
- if (!chip)
- return;
-
- /*
- * irq_write_msi_msg should not be set by the caller
- */
- if (!chip->irq_write_msi_msg)
- chip->irq_write_msi_msg = fsl_mc_msi_write_msg;
-}
-
-/**
- * fsl_mc_msi_create_irq_domain - Create a fsl-mc MSI interrupt domain
- * @np: Optional device-tree node of the interrupt controller
- * @info: MSI domain info
- * @parent: Parent irq domain
- *
- * Updates the domain and chip ops and creates a fsl-mc MSI
- * interrupt domain.
- *
- * Returns:
- * A domain pointer or NULL in case of failure.
- */
-struct irq_domain *fsl_mc_msi_create_irq_domain(struct fwnode_handle *fwnode,
- struct msi_domain_info *info,
- struct irq_domain *parent)
-{
- struct irq_domain *domain;
-
- if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS)
- fsl_mc_msi_update_dom_ops(info);
- if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
- fsl_mc_msi_update_chip_ops(info);
-
- domain = msi_create_irq_domain(fwnode, info, parent);
- if (domain)
- irq_domain_update_bus_token(domain, DOMAIN_BUS_FSL_MC_MSI);
-
- return domain;
-}
-
-int fsl_mc_find_msi_domain(struct device *mc_platform_dev,
- struct irq_domain **mc_msi_domain)
-{
- struct irq_domain *msi_domain;
- struct device_node *mc_of_node = mc_platform_dev->of_node;
-
- msi_domain = of_msi_get_domain(mc_platform_dev, mc_of_node,
- DOMAIN_BUS_FSL_MC_MSI);
- if (!msi_domain) {
- pr_err("Unable to find fsl-mc MSI domain for %pOF\n",
- mc_of_node);
-
- return -ENOENT;
- }
-
- *mc_msi_domain = msi_domain;
- return 0;
-}
-
-static void fsl_mc_msi_free_descs(struct device *dev)
-{
- struct msi_desc *desc, *tmp;
-
- list_for_each_entry_safe(desc, tmp, dev_to_msi_list(dev), list) {
- list_del(&desc->list);
- free_msi_entry(desc);
- }
-}
-
-static int fsl_mc_msi_alloc_descs(struct device *dev, unsigned int irq_count)
-
-{
- unsigned int i;
- int error;
- struct msi_desc *msi_desc;
-
- for (i = 0; i < irq_count; i++) {
- msi_desc = alloc_msi_entry(dev, 1, NULL);
- if (!msi_desc) {
- dev_err(dev, "Failed to allocate msi entry\n");
- error = -ENOMEM;
- goto cleanup_msi_descs;
- }
-
- msi_desc->fsl_mc.msi_index = i;
- INIT_LIST_HEAD(&msi_desc->list);
- list_add_tail(&msi_desc->list, dev_to_msi_list(dev));
- }
-
- return 0;
-
-cleanup_msi_descs:
- fsl_mc_msi_free_descs(dev);
- return error;
-}
-
-int fsl_mc_msi_domain_alloc_irqs(struct device *dev,
- unsigned int irq_count)
-{
- struct irq_domain *msi_domain;
- int error;
-
- if (!list_empty(dev_to_msi_list(dev)))
- return -EINVAL;
-
- error = fsl_mc_msi_alloc_descs(dev, irq_count);
- if (error < 0)
- return error;
-
- msi_domain = dev_get_msi_domain(dev);
- if (!msi_domain) {
- error = -EINVAL;
- goto cleanup_msi_descs;
- }
-
- /*
- * NOTE: Calling this function will trigger the invocation of the
- * its_fsl_mc_msi_prepare() callback
- */
- error = msi_domain_alloc_irqs(msi_domain, dev, irq_count);
-
- if (error) {
- dev_err(dev, "Failed to allocate IRQs\n");
- goto cleanup_msi_descs;
- }
-
- return 0;
-
-cleanup_msi_descs:
- fsl_mc_msi_free_descs(dev);
- return error;
-}
-
-void fsl_mc_msi_domain_free_irqs(struct device *dev)
-{
- struct irq_domain *msi_domain;
-
- msi_domain = dev_get_msi_domain(dev);
- if (!msi_domain)
- return;
-
- msi_domain_free_irqs(msi_domain, dev);
-
- if (list_empty(dev_to_msi_list(dev)))
- return;
-
- fsl_mc_msi_free_descs(dev);
-}
diff --git a/drivers/staging/fsl-mc/bus/fsl-mc-private.h b/drivers/staging/fsl-mc/bus/fsl-mc-private.h
deleted file mode 100644
index 83b89d6241f2..000000000000
--- a/drivers/staging/fsl-mc/bus/fsl-mc-private.h
+++ /dev/null
@@ -1,475 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Freescale Management Complex (MC) bus private declarations
- *
- * Copyright (C) 2016 Freescale Semiconductor, Inc.
- *
- */
-#ifndef _FSL_MC_PRIVATE_H_
-#define _FSL_MC_PRIVATE_H_
-
-#include "../include/mc.h"
-#include <linux/mutex.h>
-
-/*
- * Data Path Management Complex (DPMNG) General API
- */
-
-/* DPMNG command versioning */
-#define DPMNG_CMD_BASE_VERSION 1
-#define DPMNG_CMD_ID_OFFSET 4
-
-#define DPMNG_CMD(id) (((id) << DPMNG_CMD_ID_OFFSET) | DPMNG_CMD_BASE_VERSION)
-
-/* DPMNG command IDs */
-#define DPMNG_CMDID_GET_VERSION DPMNG_CMD(0x831)
-
-struct dpmng_rsp_get_version {
- __le32 revision;
- __le32 version_major;
- __le32 version_minor;
-};
-
-/*
- * Data Path Management Command Portal (DPMCP) API
- */
-
-/* Minimal supported DPMCP Version */
-#define DPMCP_MIN_VER_MAJOR 3
-#define DPMCP_MIN_VER_MINOR 0
-
-/* DPMCP command versioning */
-#define DPMCP_CMD_BASE_VERSION 1
-#define DPMCP_CMD_ID_OFFSET 4
-
-#define DPMCP_CMD(id) (((id) << DPMCP_CMD_ID_OFFSET) | DPMCP_CMD_BASE_VERSION)
-
-/* DPMCP command IDs */
-#define DPMCP_CMDID_CLOSE DPMCP_CMD(0x800)
-#define DPMCP_CMDID_OPEN DPMCP_CMD(0x80b)
-#define DPMCP_CMDID_RESET DPMCP_CMD(0x005)
-
-struct dpmcp_cmd_open {
- __le32 dpmcp_id;
-};
-
-/*
- * Initialization and runtime control APIs for DPMCP
- */
-int dpmcp_open(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- int dpmcp_id,
- u16 *token);
-
-int dpmcp_close(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token);
-
-int dpmcp_reset(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token);
-
-/*
- * Data Path Resource Container (DPRC) API
- */
-
-/* Minimal supported DPRC Version */
-#define DPRC_MIN_VER_MAJOR 6
-#define DPRC_MIN_VER_MINOR 0
-
-/* DPRC command versioning */
-#define DPRC_CMD_BASE_VERSION 1
-#define DPRC_CMD_ID_OFFSET 4
-
-#define DPRC_CMD(id) (((id) << DPRC_CMD_ID_OFFSET) | DPRC_CMD_BASE_VERSION)
-
-/* DPRC command IDs */
-#define DPRC_CMDID_CLOSE DPRC_CMD(0x800)
-#define DPRC_CMDID_OPEN DPRC_CMD(0x805)
-#define DPRC_CMDID_GET_API_VERSION DPRC_CMD(0xa05)
-
-#define DPRC_CMDID_GET_ATTR DPRC_CMD(0x004)
-
-#define DPRC_CMDID_SET_IRQ DPRC_CMD(0x010)
-#define DPRC_CMDID_SET_IRQ_ENABLE DPRC_CMD(0x012)
-#define DPRC_CMDID_SET_IRQ_MASK DPRC_CMD(0x014)
-#define DPRC_CMDID_GET_IRQ_STATUS DPRC_CMD(0x016)
-#define DPRC_CMDID_CLEAR_IRQ_STATUS DPRC_CMD(0x017)
-
-#define DPRC_CMDID_GET_CONT_ID DPRC_CMD(0x830)
-#define DPRC_CMDID_GET_OBJ_COUNT DPRC_CMD(0x159)
-#define DPRC_CMDID_GET_OBJ DPRC_CMD(0x15A)
-#define DPRC_CMDID_GET_OBJ_REG DPRC_CMD(0x15E)
-#define DPRC_CMDID_SET_OBJ_IRQ DPRC_CMD(0x15F)
-
-struct dprc_cmd_open {
- __le32 container_id;
-};
-
-struct dprc_cmd_set_irq {
- /* cmd word 0 */
- __le32 irq_val;
- u8 irq_index;
- u8 pad[3];
- /* cmd word 1 */
- __le64 irq_addr;
- /* cmd word 2 */
- __le32 irq_num;
-};
-
-#define DPRC_ENABLE 0x1
-
-struct dprc_cmd_set_irq_enable {
- u8 enable;
- u8 pad[3];
- u8 irq_index;
-};
-
-struct dprc_cmd_set_irq_mask {
- __le32 mask;
- u8 irq_index;
-};
-
-struct dprc_cmd_get_irq_status {
- __le32 status;
- u8 irq_index;
-};
-
-struct dprc_rsp_get_irq_status {
- __le32 status;
-};
-
-struct dprc_cmd_clear_irq_status {
- __le32 status;
- u8 irq_index;
-};
-
-struct dprc_rsp_get_attributes {
- /* response word 0 */
- __le32 container_id;
- __le16 icid;
- __le16 pad;
- /* response word 1 */
- __le32 options;
- __le32 portal_id;
-};
-
-struct dprc_rsp_get_obj_count {
- __le32 pad;
- __le32 obj_count;
-};
-
-struct dprc_cmd_get_obj {
- __le32 obj_index;
-};
-
-struct dprc_rsp_get_obj {
- /* response word 0 */
- __le32 pad0;
- __le32 id;
- /* response word 1 */
- __le16 vendor;
- u8 irq_count;
- u8 region_count;
- __le32 state;
- /* response word 2 */
- __le16 version_major;
- __le16 version_minor;
- __le16 flags;
- __le16 pad1;
- /* response word 3-4 */
- u8 type[16];
- /* response word 5-6 */
- u8 label[16];
-};
-
-struct dprc_cmd_get_obj_region {
- /* cmd word 0 */
- __le32 obj_id;
- __le16 pad0;
- u8 region_index;
- u8 pad1;
- /* cmd word 1-2 */
- __le64 pad2[2];
- /* cmd word 3-4 */
- u8 obj_type[16];
-};
-
-struct dprc_rsp_get_obj_region {
- /* response word 0 */
- __le64 pad;
- /* response word 1 */
- __le64 base_addr;
- /* response word 2 */
- __le32 size;
-};
-
-struct dprc_cmd_set_obj_irq {
- /* cmd word 0 */
- __le32 irq_val;
- u8 irq_index;
- u8 pad[3];
- /* cmd word 1 */
- __le64 irq_addr;
- /* cmd word 2 */
- __le32 irq_num;
- __le32 obj_id;
- /* cmd word 3-4 */
- u8 obj_type[16];
-};
-
-/*
- * DPRC API for managing and querying DPAA resources
- */
-int dprc_open(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- int container_id,
- u16 *token);
-
-int dprc_close(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token);
-
-/* DPRC IRQ events */
-
-/* IRQ event - Indicates that a new object added to the container */
-#define DPRC_IRQ_EVENT_OBJ_ADDED 0x00000001
-/* IRQ event - Indicates that an object was removed from the container */
-#define DPRC_IRQ_EVENT_OBJ_REMOVED 0x00000002
-/*
- * IRQ event - Indicates that one of the descendant containers that opened by
- * this container is destroyed
- */
-#define DPRC_IRQ_EVENT_CONTAINER_DESTROYED 0x00000010
-
-/*
- * IRQ event - Indicates that on one of the container's opened object is
- * destroyed
- */
-#define DPRC_IRQ_EVENT_OBJ_DESTROYED 0x00000020
-
-/* Irq event - Indicates that object is created at the container */
-#define DPRC_IRQ_EVENT_OBJ_CREATED 0x00000040
-
-/**
- * struct dprc_irq_cfg - IRQ configuration
- * @paddr: Address that must be written to signal a message-based interrupt
- * @val: Value to write into irq_addr address
- * @irq_num: A user defined number associated with this IRQ
- */
-struct dprc_irq_cfg {
- phys_addr_t paddr;
- u32 val;
- int irq_num;
-};
-
-int dprc_set_irq(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token,
- u8 irq_index,
- struct dprc_irq_cfg *irq_cfg);
-
-int dprc_set_irq_enable(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token,
- u8 irq_index,
- u8 en);
-
-int dprc_set_irq_mask(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token,
- u8 irq_index,
- u32 mask);
-
-int dprc_get_irq_status(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token,
- u8 irq_index,
- u32 *status);
-
-int dprc_clear_irq_status(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token,
- u8 irq_index,
- u32 status);
-
-/**
- * struct dprc_attributes - Container attributes
- * @container_id: Container's ID
- * @icid: Container's ICID
- * @portal_id: Container's portal ID
- * @options: Container's options as set at container's creation
- */
-struct dprc_attributes {
- int container_id;
- u16 icid;
- int portal_id;
- u64 options;
-};
-
-int dprc_get_attributes(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token,
- struct dprc_attributes *attributes);
-
-int dprc_get_obj_count(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token,
- int *obj_count);
-
-int dprc_get_obj(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token,
- int obj_index,
- struct fsl_mc_obj_desc *obj_desc);
-
-int dprc_set_obj_irq(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token,
- char *obj_type,
- int obj_id,
- u8 irq_index,
- struct dprc_irq_cfg *irq_cfg);
-
-/* Region flags */
-/* Cacheable - Indicates that region should be mapped as cacheable */
-#define DPRC_REGION_CACHEABLE 0x00000001
-
-/**
- * enum dprc_region_type - Region type
- * @DPRC_REGION_TYPE_MC_PORTAL: MC portal region
- * @DPRC_REGION_TYPE_QBMAN_PORTAL: Qbman portal region
- */
-enum dprc_region_type {
- DPRC_REGION_TYPE_MC_PORTAL,
- DPRC_REGION_TYPE_QBMAN_PORTAL
-};
-
-/**
- * struct dprc_region_desc - Mappable region descriptor
- * @base_offset: Region offset from region's base address.
- * For DPMCP and DPRC objects, region base is offset from SoC MC portals
- * base address; For DPIO, region base is offset from SoC QMan portals
- * base address
- * @size: Region size (in bytes)
- * @flags: Region attributes
- * @type: Portal region type
- */
-struct dprc_region_desc {
- u32 base_offset;
- u32 size;
- u32 flags;
- enum dprc_region_type type;
-};
-
-int dprc_get_obj_region(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token,
- char *obj_type,
- int obj_id,
- u8 region_index,
- struct dprc_region_desc *region_desc);
-
-int dprc_get_api_version(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 *major_ver,
- u16 *minor_ver);
-
-int dprc_get_container_id(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- int *container_id);
-
-/**
- * Maximum number of total IRQs that can be pre-allocated for an MC bus'
- * IRQ pool
- */
-#define FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS 256
-
-/**
- * struct fsl_mc_resource_pool - Pool of MC resources of a given
- * type
- * @type: type of resources in the pool
- * @max_count: maximum number of resources in the pool
- * @free_count: number of free resources in the pool
- * @mutex: mutex to serialize access to the pool's free list
- * @free_list: anchor node of list of free resources in the pool
- * @mc_bus: pointer to the MC bus that owns this resource pool
- */
-struct fsl_mc_resource_pool {
- enum fsl_mc_pool_type type;
- int max_count;
- int free_count;
- struct mutex mutex; /* serializes access to free_list */
- struct list_head free_list;
- struct fsl_mc_bus *mc_bus;
-};
-
-/**
- * struct fsl_mc_bus - logical bus that corresponds to a physical DPRC
- * @mc_dev: fsl-mc device for the bus device itself.
- * @resource_pools: array of resource pools (one pool per resource type)
- * for this MC bus. These resources represent allocatable entities
- * from the physical DPRC.
- * @irq_resources: Pointer to array of IRQ objects for the IRQ pool
- * @scan_mutex: Serializes bus scanning
- * @dprc_attr: DPRC attributes
- */
-struct fsl_mc_bus {
- struct fsl_mc_device mc_dev;
- struct fsl_mc_resource_pool resource_pools[FSL_MC_NUM_POOL_TYPES];
- struct fsl_mc_device_irq *irq_resources;
- struct mutex scan_mutex; /* serializes bus scanning */
- struct dprc_attributes dprc_attr;
-};
-
-#define to_fsl_mc_bus(_mc_dev) \
- container_of(_mc_dev, struct fsl_mc_bus, mc_dev)
-
-int __must_check fsl_mc_device_add(struct fsl_mc_obj_desc *obj_desc,
- struct fsl_mc_io *mc_io,
- struct device *parent_dev,
- struct fsl_mc_device **new_mc_dev);
-
-void fsl_mc_device_remove(struct fsl_mc_device *mc_dev);
-
-int __init dprc_driver_init(void);
-
-void dprc_driver_exit(void);
-
-int __init fsl_mc_allocator_driver_init(void);
-
-void fsl_mc_init_all_resource_pools(struct fsl_mc_device *mc_bus_dev);
-
-void fsl_mc_cleanup_all_resource_pools(struct fsl_mc_device *mc_bus_dev);
-
-int __must_check fsl_mc_resource_allocate(struct fsl_mc_bus *mc_bus,
- enum fsl_mc_pool_type pool_type,
- struct fsl_mc_resource
- **new_resource);
-
-void fsl_mc_resource_free(struct fsl_mc_resource *resource);
-
-int fsl_mc_msi_domain_alloc_irqs(struct device *dev,
- unsigned int irq_count);
-
-void fsl_mc_msi_domain_free_irqs(struct device *dev);
-
-int fsl_mc_find_msi_domain(struct device *mc_platform_dev,
- struct irq_domain **mc_msi_domain);
-
-int fsl_mc_populate_irq_pool(struct fsl_mc_bus *mc_bus,
- unsigned int irq_count);
-
-void fsl_mc_cleanup_irq_pool(struct fsl_mc_bus *mc_bus);
-
-int __must_check fsl_create_mc_io(struct device *dev,
- phys_addr_t mc_portal_phys_addr,
- u32 mc_portal_size,
- struct fsl_mc_device *dpmcp_dev,
- u32 flags, struct fsl_mc_io **new_mc_io);
-
-void fsl_destroy_mc_io(struct fsl_mc_io *mc_io);
-
-bool fsl_mc_is_root_dprc(struct device *dev);
-
-#endif /* _FSL_MC_PRIVATE_H_ */
diff --git a/drivers/staging/fsl-mc/bus/irq-gic-v3-its-fsl-mc-msi.c b/drivers/staging/fsl-mc/bus/irq-gic-v3-its-fsl-mc-msi.c
deleted file mode 100644
index fc2013aade51..000000000000
--- a/drivers/staging/fsl-mc/bus/irq-gic-v3-its-fsl-mc-msi.c
+++ /dev/null
@@ -1,102 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Freescale Management Complex (MC) bus driver MSI support
- *
- * Copyright (C) 2015-2016 Freescale Semiconductor, Inc.
- * Author: German Rivera <German.Rivera@freescale.com>
- *
- */
-
-#include <linux/of_device.h>
-#include <linux/of_address.h>
-#include <linux/irq.h>
-#include <linux/msi.h>
-#include <linux/of.h>
-#include <linux/of_irq.h>
-#include "../include/mc.h"
-
-static struct irq_chip its_msi_irq_chip = {
- .name = "ITS-fMSI",
- .irq_mask = irq_chip_mask_parent,
- .irq_unmask = irq_chip_unmask_parent,
- .irq_eoi = irq_chip_eoi_parent,
- .irq_set_affinity = msi_domain_set_affinity
-};
-
-static int its_fsl_mc_msi_prepare(struct irq_domain *msi_domain,
- struct device *dev,
- int nvec, msi_alloc_info_t *info)
-{
- struct fsl_mc_device *mc_bus_dev;
- struct msi_domain_info *msi_info;
-
- if (!dev_is_fsl_mc(dev))
- return -EINVAL;
-
- mc_bus_dev = to_fsl_mc_device(dev);
- if (!(mc_bus_dev->flags & FSL_MC_IS_DPRC))
- return -EINVAL;
-
- /*
- * Set the device Id to be passed to the GIC-ITS:
- *
- * NOTE: This device id corresponds to the IOMMU stream ID
- * associated with the DPRC object (ICID).
- */
-#ifdef GENERIC_MSI_DOMAIN_OPS
- info->scratchpad[0].ul = mc_bus_dev->icid;
-#endif
- msi_info = msi_get_domain_info(msi_domain->parent);
- return msi_info->ops->msi_prepare(msi_domain->parent, dev, nvec, info);
-}
-
-static struct msi_domain_ops its_fsl_mc_msi_ops __ro_after_init = {
- .msi_prepare = its_fsl_mc_msi_prepare,
-};
-
-static struct msi_domain_info its_fsl_mc_msi_domain_info = {
- .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
- .ops = &its_fsl_mc_msi_ops,
- .chip = &its_msi_irq_chip,
-};
-
-static const struct of_device_id its_device_id[] = {
- { .compatible = "arm,gic-v3-its", },
- {},
-};
-
-static int __init its_fsl_mc_msi_init(void)
-{
- struct device_node *np;
- struct irq_domain *parent;
- struct irq_domain *mc_msi_domain;
-
- for (np = of_find_matching_node(NULL, its_device_id); np;
- np = of_find_matching_node(np, its_device_id)) {
- if (!of_device_is_available(np))
- continue;
- if (!of_property_read_bool(np, "msi-controller"))
- continue;
-
- parent = irq_find_matching_host(np, DOMAIN_BUS_NEXUS);
- if (!parent || !msi_get_domain_info(parent)) {
- pr_err("%pOF: unable to locate ITS domain\n", np);
- continue;
- }
-
- mc_msi_domain = fsl_mc_msi_create_irq_domain(
- of_node_to_fwnode(np),
- &its_fsl_mc_msi_domain_info,
- parent);
- if (!mc_msi_domain) {
- pr_err("%pOF: unable to create fsl-mc domain\n", np);
- continue;
- }
-
- pr_info("fsl-mc MSI: %pOF domain created\n", np);
- }
-
- return 0;
-}
-
-early_initcall(its_fsl_mc_msi_init);
diff --git a/drivers/staging/fsl-mc/bus/mc-io.c b/drivers/staging/fsl-mc/bus/mc-io.c
deleted file mode 100644
index 7e6fb360ef12..000000000000
--- a/drivers/staging/fsl-mc/bus/mc-io.c
+++ /dev/null
@@ -1,268 +0,0 @@
-// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
-/*
- * Copyright 2013-2016 Freescale Semiconductor Inc.
- *
- */
-
-#include <linux/io.h>
-#include "../include/mc.h"
-
-#include "fsl-mc-private.h"
-
-static int fsl_mc_io_set_dpmcp(struct fsl_mc_io *mc_io,
- struct fsl_mc_device *dpmcp_dev)
-{
- int error;
-
- if (mc_io->dpmcp_dev)
- return -EINVAL;
-
- if (dpmcp_dev->mc_io)
- return -EINVAL;
-
- error = dpmcp_open(mc_io,
- 0,
- dpmcp_dev->obj_desc.id,
- &dpmcp_dev->mc_handle);
- if (error < 0)
- return error;
-
- mc_io->dpmcp_dev = dpmcp_dev;
- dpmcp_dev->mc_io = mc_io;
- return 0;
-}
-
-static void fsl_mc_io_unset_dpmcp(struct fsl_mc_io *mc_io)
-{
- int error;
- struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev;
-
- error = dpmcp_close(mc_io,
- 0,
- dpmcp_dev->mc_handle);
- if (error < 0) {
- dev_err(&dpmcp_dev->dev, "dpmcp_close() failed: %d\n",
- error);
- }
-
- mc_io->dpmcp_dev = NULL;
- dpmcp_dev->mc_io = NULL;
-}
-
-/**
- * Creates an MC I/O object
- *
- * @dev: device to be associated with the MC I/O object
- * @mc_portal_phys_addr: physical address of the MC portal to use
- * @mc_portal_size: size in bytes of the MC portal
- * @dpmcp-dev: Pointer to the DPMCP object associated with this MC I/O
- * object or NULL if none.
- * @flags: flags for the new MC I/O object
- * @new_mc_io: Area to return pointer to newly created MC I/O object
- *
- * Returns '0' on Success; Error code otherwise.
- */
-int __must_check fsl_create_mc_io(struct device *dev,
- phys_addr_t mc_portal_phys_addr,
- u32 mc_portal_size,
- struct fsl_mc_device *dpmcp_dev,
- u32 flags, struct fsl_mc_io **new_mc_io)
-{
- int error;
- struct fsl_mc_io *mc_io;
- void __iomem *mc_portal_virt_addr;
- struct resource *res;
-
- mc_io = devm_kzalloc(dev, sizeof(*mc_io), GFP_KERNEL);
- if (!mc_io)
- return -ENOMEM;
-
- mc_io->dev = dev;
- mc_io->flags = flags;
- mc_io->portal_phys_addr = mc_portal_phys_addr;
- mc_io->portal_size = mc_portal_size;
- if (flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)
- spin_lock_init(&mc_io->spinlock);
- else
- mutex_init(&mc_io->mutex);
-
- res = devm_request_mem_region(dev,
- mc_portal_phys_addr,
- mc_portal_size,
- "mc_portal");
- if (!res) {
- dev_err(dev,
- "devm_request_mem_region failed for MC portal %pa\n",
- &mc_portal_phys_addr);
- return -EBUSY;
- }
-
- mc_portal_virt_addr = devm_ioremap_nocache(dev,
- mc_portal_phys_addr,
- mc_portal_size);
- if (!mc_portal_virt_addr) {
- dev_err(dev,
- "devm_ioremap_nocache failed for MC portal %pa\n",
- &mc_portal_phys_addr);
- return -ENXIO;
- }
-
- mc_io->portal_virt_addr = mc_portal_virt_addr;
- if (dpmcp_dev) {
- error = fsl_mc_io_set_dpmcp(mc_io, dpmcp_dev);
- if (error < 0)
- goto error_destroy_mc_io;
- }
-
- *new_mc_io = mc_io;
- return 0;
-
-error_destroy_mc_io:
- fsl_destroy_mc_io(mc_io);
- return error;
-}
-
-/**
- * Destroys an MC I/O object
- *
- * @mc_io: MC I/O object to destroy
- */
-void fsl_destroy_mc_io(struct fsl_mc_io *mc_io)
-{
- struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev;
-
- if (dpmcp_dev)
- fsl_mc_io_unset_dpmcp(mc_io);
-
- devm_iounmap(mc_io->dev, mc_io->portal_virt_addr);
- devm_release_mem_region(mc_io->dev,
- mc_io->portal_phys_addr,
- mc_io->portal_size);
-
- mc_io->portal_virt_addr = NULL;
- devm_kfree(mc_io->dev, mc_io);
-}
-
-/**
- * fsl_mc_portal_allocate - Allocates an MC portal
- *
- * @mc_dev: MC device for which the MC portal is to be allocated
- * @mc_io_flags: Flags for the fsl_mc_io object that wraps the allocated
- * MC portal.
- * @new_mc_io: Pointer to area where the pointer to the fsl_mc_io object
- * that wraps the allocated MC portal is to be returned
- *
- * This function allocates an MC portal from the device's parent DPRC,
- * from the corresponding MC bus' pool of MC portals and wraps
- * it in a new fsl_mc_io object. If 'mc_dev' is a DPRC itself, the
- * portal is allocated from its own MC bus.
- */
-int __must_check fsl_mc_portal_allocate(struct fsl_mc_device *mc_dev,
- u16 mc_io_flags,
- struct fsl_mc_io **new_mc_io)
-{
- struct fsl_mc_device *mc_bus_dev;
- struct fsl_mc_bus *mc_bus;
- phys_addr_t mc_portal_phys_addr;
- size_t mc_portal_size;
- struct fsl_mc_device *dpmcp_dev;
- int error = -EINVAL;
- struct fsl_mc_resource *resource = NULL;
- struct fsl_mc_io *mc_io = NULL;
-
- if (mc_dev->flags & FSL_MC_IS_DPRC) {
- mc_bus_dev = mc_dev;
- } else {
- if (!dev_is_fsl_mc(mc_dev->dev.parent))
- return error;
-
- mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
- }
-
- mc_bus = to_fsl_mc_bus(mc_bus_dev);
- *new_mc_io = NULL;
- error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_DPMCP, &resource);
- if (error < 0)
- return error;
-
- error = -EINVAL;
- dpmcp_dev = resource->data;
-
- if (dpmcp_dev->obj_desc.ver_major < DPMCP_MIN_VER_MAJOR ||
- (dpmcp_dev->obj_desc.ver_major == DPMCP_MIN_VER_MAJOR &&
- dpmcp_dev->obj_desc.ver_minor < DPMCP_MIN_VER_MINOR)) {
- dev_err(&dpmcp_dev->dev,
- "ERROR: Version %d.%d of DPMCP not supported.\n",
- dpmcp_dev->obj_desc.ver_major,
- dpmcp_dev->obj_desc.ver_minor);
- error = -ENOTSUPP;
- goto error_cleanup_resource;
- }
-
- mc_portal_phys_addr = dpmcp_dev->regions[0].start;
- mc_portal_size = resource_size(dpmcp_dev->regions);
-
- error = fsl_create_mc_io(&mc_bus_dev->dev,
- mc_portal_phys_addr,
- mc_portal_size, dpmcp_dev,
- mc_io_flags, &mc_io);
- if (error < 0)
- goto error_cleanup_resource;
-
- *new_mc_io = mc_io;
- return 0;
-
-error_cleanup_resource:
- fsl_mc_resource_free(resource);
- return error;
-}
-EXPORT_SYMBOL_GPL(fsl_mc_portal_allocate);
-
-/**
- * fsl_mc_portal_free - Returns an MC portal to the pool of free MC portals
- * of a given MC bus
- *
- * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free
- */
-void fsl_mc_portal_free(struct fsl_mc_io *mc_io)
-{
- struct fsl_mc_device *dpmcp_dev;
- struct fsl_mc_resource *resource;
-
- /*
- * Every mc_io obtained by calling fsl_mc_portal_allocate() is supposed
- * to have a DPMCP object associated with.
- */
- dpmcp_dev = mc_io->dpmcp_dev;
-
- resource = dpmcp_dev->resource;
- if (!resource || resource->type != FSL_MC_POOL_DPMCP)
- return;
-
- if (resource->data != dpmcp_dev)
- return;
-
- fsl_destroy_mc_io(mc_io);
- fsl_mc_resource_free(resource);
-}
-EXPORT_SYMBOL_GPL(fsl_mc_portal_free);
-
-/**
- * fsl_mc_portal_reset - Resets the dpmcp object for a given fsl_mc_io object
- *
- * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free
- */
-int fsl_mc_portal_reset(struct fsl_mc_io *mc_io)
-{
- int error;
- struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev;
-
- error = dpmcp_reset(mc_io, 0, dpmcp_dev->mc_handle);
- if (error < 0) {
- dev_err(&dpmcp_dev->dev, "dpmcp_reset() failed: %d\n", error);
- return error;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(fsl_mc_portal_reset);
diff --git a/drivers/staging/fsl-mc/bus/mc-sys.c b/drivers/staging/fsl-mc/bus/mc-sys.c
deleted file mode 100644
index f09d75d9a976..000000000000
--- a/drivers/staging/fsl-mc/bus/mc-sys.c
+++ /dev/null
@@ -1,296 +0,0 @@
-// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
-/*
- * Copyright 2013-2016 Freescale Semiconductor Inc.
- *
- * I/O services to send MC commands to the MC hardware
- *
- */
-
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/ioport.h>
-#include <linux/device.h>
-#include <linux/io.h>
-#include <linux/io-64-nonatomic-hi-lo.h>
-#include "../include/mc.h"
-
-#include "fsl-mc-private.h"
-
-/**
- * Timeout in milliseconds to wait for the completion of an MC command
- */
-#define MC_CMD_COMPLETION_TIMEOUT_MS 500
-
-/*
- * usleep_range() min and max values used to throttle down polling
- * iterations while waiting for MC command completion
- */
-#define MC_CMD_COMPLETION_POLLING_MIN_SLEEP_USECS 10
-#define MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS 500
-
-static enum mc_cmd_status mc_cmd_hdr_read_status(struct mc_command *cmd)
-{
- struct mc_cmd_header *hdr = (struct mc_cmd_header *)&cmd->header;
-
- return (enum mc_cmd_status)hdr->status;
-}
-
-static u16 mc_cmd_hdr_read_cmdid(struct mc_command *cmd)
-{
- struct mc_cmd_header *hdr = (struct mc_cmd_header *)&cmd->header;
- u16 cmd_id = le16_to_cpu(hdr->cmd_id);
-
- return cmd_id;
-}
-
-static int mc_status_to_error(enum mc_cmd_status status)
-{
- static const int mc_status_to_error_map[] = {
- [MC_CMD_STATUS_OK] = 0,
- [MC_CMD_STATUS_AUTH_ERR] = -EACCES,
- [MC_CMD_STATUS_NO_PRIVILEGE] = -EPERM,
- [MC_CMD_STATUS_DMA_ERR] = -EIO,
- [MC_CMD_STATUS_CONFIG_ERR] = -ENXIO,
- [MC_CMD_STATUS_TIMEOUT] = -ETIMEDOUT,
- [MC_CMD_STATUS_NO_RESOURCE] = -ENAVAIL,
- [MC_CMD_STATUS_NO_MEMORY] = -ENOMEM,
- [MC_CMD_STATUS_BUSY] = -EBUSY,
- [MC_CMD_STATUS_UNSUPPORTED_OP] = -ENOTSUPP,
- [MC_CMD_STATUS_INVALID_STATE] = -ENODEV,
- };
-
- if ((u32)status >= ARRAY_SIZE(mc_status_to_error_map))
- return -EINVAL;
-
- return mc_status_to_error_map[status];
-}
-
-static const char *mc_status_to_string(enum mc_cmd_status status)
-{
- static const char *const status_strings[] = {
- [MC_CMD_STATUS_OK] = "Command completed successfully",
- [MC_CMD_STATUS_READY] = "Command ready to be processed",
- [MC_CMD_STATUS_AUTH_ERR] = "Authentication error",
- [MC_CMD_STATUS_NO_PRIVILEGE] = "No privilege",
- [MC_CMD_STATUS_DMA_ERR] = "DMA or I/O error",
- [MC_CMD_STATUS_CONFIG_ERR] = "Configuration error",
- [MC_CMD_STATUS_TIMEOUT] = "Operation timed out",
- [MC_CMD_STATUS_NO_RESOURCE] = "No resources",
- [MC_CMD_STATUS_NO_MEMORY] = "No memory available",
- [MC_CMD_STATUS_BUSY] = "Device is busy",
- [MC_CMD_STATUS_UNSUPPORTED_OP] = "Unsupported operation",
- [MC_CMD_STATUS_INVALID_STATE] = "Invalid state"
- };
-
- if ((unsigned int)status >= ARRAY_SIZE(status_strings))
- return "Unknown MC error";
-
- return status_strings[status];
-}
-
-/**
- * mc_write_command - writes a command to a Management Complex (MC) portal
- *
- * @portal: pointer to an MC portal
- * @cmd: pointer to a filled command
- */
-static inline void mc_write_command(struct mc_command __iomem *portal,
- struct mc_command *cmd)
-{
- int i;
-
- /* copy command parameters into the portal */
- for (i = 0; i < MC_CMD_NUM_OF_PARAMS; i++)
- /*
- * Data is already in the expected LE byte-order. Do an
- * extra LE -> CPU conversion so that the CPU -> LE done in
- * the device io write api puts it back in the right order.
- */
- writeq_relaxed(le64_to_cpu(cmd->params[i]), &portal->params[i]);
-
- /* submit the command by writing the header */
- writeq(le64_to_cpu(cmd->header), &portal->header);
-}
-
-/**
- * mc_read_response - reads the response for the last MC command from a
- * Management Complex (MC) portal
- *
- * @portal: pointer to an MC portal
- * @resp: pointer to command response buffer
- *
- * Returns MC_CMD_STATUS_OK on Success; Error code otherwise.
- */
-static inline enum mc_cmd_status mc_read_response(struct mc_command __iomem *
- portal,
- struct mc_command *resp)
-{
- int i;
- enum mc_cmd_status status;
-
- /* Copy command response header from MC portal: */
- resp->header = cpu_to_le64(readq_relaxed(&portal->header));
- status = mc_cmd_hdr_read_status(resp);
- if (status != MC_CMD_STATUS_OK)
- return status;
-
- /* Copy command response data from MC portal: */
- for (i = 0; i < MC_CMD_NUM_OF_PARAMS; i++)
- /*
- * Data is expected to be in LE byte-order. Do an
- * extra CPU -> LE to revert the LE -> CPU done in
- * the device io read api.
- */
- resp->params[i] =
- cpu_to_le64(readq_relaxed(&portal->params[i]));
-
- return status;
-}
-
-/**
- * Waits for the completion of an MC command doing preemptible polling.
- * uslepp_range() is called between polling iterations.
- *
- * @mc_io: MC I/O object to be used
- * @cmd: command buffer to receive MC response
- * @mc_status: MC command completion status
- */
-static int mc_polling_wait_preemptible(struct fsl_mc_io *mc_io,
- struct mc_command *cmd,
- enum mc_cmd_status *mc_status)
-{
- enum mc_cmd_status status;
- unsigned long jiffies_until_timeout =
- jiffies + msecs_to_jiffies(MC_CMD_COMPLETION_TIMEOUT_MS);
-
- /*
- * Wait for response from the MC hardware:
- */
- for (;;) {
- status = mc_read_response(mc_io->portal_virt_addr, cmd);
- if (status != MC_CMD_STATUS_READY)
- break;
-
- /*
- * TODO: When MC command completion interrupts are supported
- * call wait function here instead of usleep_range()
- */
- usleep_range(MC_CMD_COMPLETION_POLLING_MIN_SLEEP_USECS,
- MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS);
-
- if (time_after_eq(jiffies, jiffies_until_timeout)) {
- dev_dbg(mc_io->dev,
- "MC command timed out (portal: %pa, dprc handle: %#x, command: %#x)\n",
- &mc_io->portal_phys_addr,
- (unsigned int)mc_cmd_hdr_read_token(cmd),
- (unsigned int)mc_cmd_hdr_read_cmdid(cmd));
-
- return -ETIMEDOUT;
- }
- }
-
- *mc_status = status;
- return 0;
-}
-
-/**
- * Waits for the completion of an MC command doing atomic polling.
- * udelay() is called between polling iterations.
- *
- * @mc_io: MC I/O object to be used
- * @cmd: command buffer to receive MC response
- * @mc_status: MC command completion status
- */
-static int mc_polling_wait_atomic(struct fsl_mc_io *mc_io,
- struct mc_command *cmd,
- enum mc_cmd_status *mc_status)
-{
- enum mc_cmd_status status;
- unsigned long timeout_usecs = MC_CMD_COMPLETION_TIMEOUT_MS * 1000;
-
- BUILD_BUG_ON((MC_CMD_COMPLETION_TIMEOUT_MS * 1000) %
- MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS != 0);
-
- for (;;) {
- status = mc_read_response(mc_io->portal_virt_addr, cmd);
- if (status != MC_CMD_STATUS_READY)
- break;
-
- udelay(MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS);
- timeout_usecs -= MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS;
- if (timeout_usecs == 0) {
- dev_dbg(mc_io->dev,
- "MC command timed out (portal: %pa, dprc handle: %#x, command: %#x)\n",
- &mc_io->portal_phys_addr,
- (unsigned int)mc_cmd_hdr_read_token(cmd),
- (unsigned int)mc_cmd_hdr_read_cmdid(cmd));
-
- return -ETIMEDOUT;
- }
- }
-
- *mc_status = status;
- return 0;
-}
-
-/**
- * Sends a command to the MC device using the given MC I/O object
- *
- * @mc_io: MC I/O object to be used
- * @cmd: command to be sent
- *
- * Returns '0' on Success; Error code otherwise.
- */
-int mc_send_command(struct fsl_mc_io *mc_io, struct mc_command *cmd)
-{
- int error;
- enum mc_cmd_status status;
- unsigned long irq_flags = 0;
-
- if (in_irq() && !(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL))
- return -EINVAL;
-
- if (mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)
- spin_lock_irqsave(&mc_io->spinlock, irq_flags);
- else
- mutex_lock(&mc_io->mutex);
-
- /*
- * Send command to the MC hardware:
- */
- mc_write_command(mc_io->portal_virt_addr, cmd);
-
- /*
- * Wait for response from the MC hardware:
- */
- if (!(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL))
- error = mc_polling_wait_preemptible(mc_io, cmd, &status);
- else
- error = mc_polling_wait_atomic(mc_io, cmd, &status);
-
- if (error < 0)
- goto common_exit;
-
- if (status != MC_CMD_STATUS_OK) {
- dev_dbg(mc_io->dev,
- "MC command failed: portal: %pa, dprc handle: %#x, command: %#x, status: %s (%#x)\n",
- &mc_io->portal_phys_addr,
- (unsigned int)mc_cmd_hdr_read_token(cmd),
- (unsigned int)mc_cmd_hdr_read_cmdid(cmd),
- mc_status_to_string(status),
- (unsigned int)status);
-
- error = mc_status_to_error(status);
- goto common_exit;
- }
-
- error = 0;
-common_exit:
- if (mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)
- spin_unlock_irqrestore(&mc_io->spinlock, irq_flags);
- else
- mutex_unlock(&mc_io->mutex);
-
- return error;
-}
-EXPORT_SYMBOL_GPL(mc_send_command);
diff --git a/drivers/staging/fsl-mc/include/dpaa2-fd.h b/drivers/staging/fsl-mc/include/dpaa2-fd.h
index 3e022001f0b1..b55b89ba4eda 100644
--- a/drivers/staging/fsl-mc/include/dpaa2-fd.h
+++ b/drivers/staging/fsl-mc/include/dpaa2-fd.h
@@ -287,7 +287,7 @@ enum dpaa2_sg_format {
*/
static inline dma_addr_t dpaa2_sg_get_addr(const struct dpaa2_sg_entry *sg)
{
- return le64_to_cpu((dma_addr_t)sg->addr);
+ return (dma_addr_t)le64_to_cpu(sg->addr);
}
/**
@@ -418,8 +418,8 @@ static inline bool dpaa2_sg_is_final(const struct dpaa2_sg_entry *sg)
*/
static inline void dpaa2_sg_set_final(struct dpaa2_sg_entry *sg, bool final)
{
- sg->format_offset &= cpu_to_le16(~(SG_FINAL_FLAG_MASK
- << SG_FINAL_FLAG_SHIFT));
+ sg->format_offset &= cpu_to_le16((~(SG_FINAL_FLAG_MASK
+ << SG_FINAL_FLAG_SHIFT)) & 0xFFFF);
sg->format_offset |= cpu_to_le16(final << SG_FINAL_FLAG_SHIFT);
}
diff --git a/drivers/staging/fsl-mc/include/dpaa2-io.h b/drivers/staging/fsl-mc/include/dpaa2-io.h
index 9cb1eec87a9c..f71227d3df8d 100644
--- a/drivers/staging/fsl-mc/include/dpaa2-io.h
+++ b/drivers/staging/fsl-mc/include/dpaa2-io.h
@@ -80,7 +80,7 @@ struct dpaa2_io *dpaa2_io_service_select(int cpu);
* Used when a FQDAN/CDAN registration is made by drivers.
*/
struct dpaa2_io_notification_ctx {
- void (*cb)(struct dpaa2_io_notification_ctx *);
+ void (*cb)(struct dpaa2_io_notification_ctx *ctx);
int is_cdan;
u32 id;
int desired_cpu;
diff --git a/drivers/staging/fsl-mc/include/dpbp.h b/drivers/staging/fsl-mc/include/dpbp.h
deleted file mode 100644
index 4a1809604319..000000000000
--- a/drivers/staging/fsl-mc/include/dpbp.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
-/*
- * Copyright 2013-2016 Freescale Semiconductor Inc.
- *
- */
-#ifndef __FSL_DPBP_H
-#define __FSL_DPBP_H
-
-/*
- * Data Path Buffer Pool API
- * Contains initialization APIs and runtime control APIs for DPBP
- */
-
-struct fsl_mc_io;
-
-int dpbp_open(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- int dpbp_id,
- u16 *token);
-
-int dpbp_close(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token);
-
-int dpbp_enable(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token);
-
-int dpbp_disable(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token);
-
-int dpbp_is_enabled(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token,
- int *en);
-
-int dpbp_reset(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token);
-
-/**
- * struct dpbp_attr - Structure representing DPBP attributes
- * @id: DPBP object ID
- * @bpid: Hardware buffer pool ID; should be used as an argument in
- * acquire/release operations on buffers
- */
-struct dpbp_attr {
- int id;
- u16 bpid;
-};
-
-int dpbp_get_attributes(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token,
- struct dpbp_attr *attr);
-
-int dpbp_get_api_version(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 *major_ver,
- u16 *minor_ver);
-
-#endif /* __FSL_DPBP_H */
diff --git a/drivers/staging/fsl-mc/include/dpcon.h b/drivers/staging/fsl-mc/include/dpcon.h
deleted file mode 100644
index 062e90ad929b..000000000000
--- a/drivers/staging/fsl-mc/include/dpcon.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
-/*
- * Copyright 2013-2016 Freescale Semiconductor Inc.
- *
- */
-#ifndef __FSL_DPCON_H
-#define __FSL_DPCON_H
-
-/* Data Path Concentrator API
- * Contains initialization APIs and runtime control APIs for DPCON
- */
-
-struct fsl_mc_io;
-
-/** General DPCON macros */
-
-/**
- * Use it to disable notifications; see dpcon_set_notification()
- */
-#define DPCON_INVALID_DPIO_ID (int)(-1)
-
-int dpcon_open(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- int dpcon_id,
- u16 *token);
-
-int dpcon_close(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token);
-
-int dpcon_enable(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token);
-
-int dpcon_disable(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token);
-
-int dpcon_reset(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token);
-
-/**
- * struct dpcon_attr - Structure representing DPCON attributes
- * @id: DPCON object ID
- * @qbman_ch_id: Channel ID to be used by dequeue operation
- * @num_priorities: Number of priorities for the DPCON channel (1-8)
- */
-struct dpcon_attr {
- int id;
- u16 qbman_ch_id;
- u8 num_priorities;
-};
-
-int dpcon_get_attributes(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token,
- struct dpcon_attr *attr);
-
-/**
- * struct dpcon_notification_cfg - Structure representing notification params
- * @dpio_id: DPIO object ID; must be configured with a notification channel;
- * to disable notifications set it to 'DPCON_INVALID_DPIO_ID';
- * @priority: Priority selection within the DPIO channel; valid values
- * are 0-7, depending on the number of priorities in that channel
- * @user_ctx: User context value provided with each CDAN message
- */
-struct dpcon_notification_cfg {
- int dpio_id;
- u8 priority;
- u64 user_ctx;
-};
-
-int dpcon_set_notification(struct fsl_mc_io *mc_io,
- u32 cmd_flags,
- u16 token,
- struct dpcon_notification_cfg *cfg);
-
-#endif /* __FSL_DPCON_H */
diff --git a/drivers/staging/fsl-mc/include/mc.h b/drivers/staging/fsl-mc/include/mc.h
deleted file mode 100644
index 765ba41f5987..000000000000
--- a/drivers/staging/fsl-mc/include/mc.h
+++ /dev/null
@@ -1,454 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Freescale Management Complex (MC) bus public interface
- *
- * Copyright (C) 2014-2016 Freescale Semiconductor, Inc.
- * Author: German Rivera <German.Rivera@freescale.com>
- *
- */
-#ifndef _FSL_MC_H_
-#define _FSL_MC_H_
-
-#include <linux/device.h>
-#include <linux/mod_devicetable.h>
-#include <linux/interrupt.h>
-
-#define FSL_MC_VENDOR_FREESCALE 0x1957
-
-struct irq_domain;
-struct msi_domain_info;
-
-struct fsl_mc_device;
-struct fsl_mc_io;
-
-/**
- * struct fsl_mc_driver - MC object device driver object
- * @driver: Generic device driver
- * @match_id_table: table of supported device matching Ids
- * @probe: Function called when a device is added
- * @remove: Function called when a device is removed
- * @shutdown: Function called at shutdown time to quiesce the device
- * @suspend: Function called when a device is stopped
- * @resume: Function called when a device is resumed
- *
- * Generic DPAA device driver object for device drivers that are registered
- * with a DPRC bus. This structure is to be embedded in each device-specific
- * driver structure.
- */
-struct fsl_mc_driver {
- struct device_driver driver;
- const struct fsl_mc_device_id *match_id_table;
- int (*probe)(struct fsl_mc_device *dev);
- int (*remove)(struct fsl_mc_device *dev);
- void (*shutdown)(struct fsl_mc_device *dev);
- int (*suspend)(struct fsl_mc_device *dev, pm_message_t state);
- int (*resume)(struct fsl_mc_device *dev);
-};
-
-#define to_fsl_mc_driver(_drv) \
- container_of(_drv, struct fsl_mc_driver, driver)
-
-/**
- * enum fsl_mc_pool_type - Types of allocatable MC bus resources
- *
- * Entries in these enum are used as indices in the array of resource
- * pools of an fsl_mc_bus object.
- */
-enum fsl_mc_pool_type {
- FSL_MC_POOL_DPMCP = 0x0, /* corresponds to "dpmcp" in the MC */
- FSL_MC_POOL_DPBP, /* corresponds to "dpbp" in the MC */
- FSL_MC_POOL_DPCON, /* corresponds to "dpcon" in the MC */
- FSL_MC_POOL_IRQ,
-
- /*
- * NOTE: New resource pool types must be added before this entry
- */
- FSL_MC_NUM_POOL_TYPES
-};
-
-/**
- * struct fsl_mc_resource - MC generic resource
- * @type: type of resource
- * @id: unique MC resource Id within the resources of the same type
- * @data: pointer to resource-specific data if the resource is currently
- * allocated, or NULL if the resource is not currently allocated.
- * @parent_pool: pointer to the parent resource pool from which this
- * resource is allocated from.
- * @node: Node in the free list of the corresponding resource pool
- *
- * NOTE: This structure is to be embedded as a field of specific
- * MC resource structures.
- */
-struct fsl_mc_resource {
- enum fsl_mc_pool_type type;
- s32 id;
- void *data;
- struct fsl_mc_resource_pool *parent_pool;
- struct list_head node;
-};
-
-/**
- * struct fsl_mc_device_irq - MC object device message-based interrupt
- * @msi_desc: pointer to MSI descriptor allocated by fsl_mc_msi_alloc_descs()
- * @mc_dev: MC object device that owns this interrupt
- * @dev_irq_index: device-relative IRQ index
- * @resource: MC generic resource associated with the interrupt
- */
-struct fsl_mc_device_irq {
- struct msi_desc *msi_desc;
- struct fsl_mc_device *mc_dev;
- u8 dev_irq_index;
- struct fsl_mc_resource resource;
-};
-
-#define to_fsl_mc_irq(_mc_resource) \
- container_of(_mc_resource, struct fsl_mc_device_irq, resource)
-
-/* Opened state - Indicates that an object is open by at least one owner */
-#define FSL_MC_OBJ_STATE_OPEN 0x00000001
-/* Plugged state - Indicates that the object is plugged */
-#define FSL_MC_OBJ_STATE_PLUGGED 0x00000002
-
-/**
- * Shareability flag - Object flag indicating no memory shareability.
- * the object generates memory accesses that are non coherent with other
- * masters;
- * user is responsible for proper memory handling through IOMMU configuration.
- */
-#define FSL_MC_OBJ_FLAG_NO_MEM_SHAREABILITY 0x0001
-
-/**
- * struct fsl_mc_obj_desc - Object descriptor
- * @type: Type of object: NULL terminated string
- * @id: ID of logical object resource
- * @vendor: Object vendor identifier
- * @ver_major: Major version number
- * @ver_minor: Minor version number
- * @irq_count: Number of interrupts supported by the object
- * @region_count: Number of mappable regions supported by the object
- * @state: Object state: combination of FSL_MC_OBJ_STATE_ states
- * @label: Object label: NULL terminated string
- * @flags: Object's flags
- */
-struct fsl_mc_obj_desc {
- char type[16];
- int id;
- u16 vendor;
- u16 ver_major;
- u16 ver_minor;
- u8 irq_count;
- u8 region_count;
- u32 state;
- char label[16];
- u16 flags;
-};
-
-/**
- * Bit masks for a MC object device (struct fsl_mc_device) flags
- */
-#define FSL_MC_IS_DPRC 0x0001
-
-/**
- * struct fsl_mc_device - MC object device object
- * @dev: Linux driver model device object
- * @dma_mask: Default DMA mask
- * @flags: MC object device flags
- * @icid: Isolation context ID for the device
- * @mc_handle: MC handle for the corresponding MC object opened
- * @mc_io: Pointer to MC IO object assigned to this device or
- * NULL if none.
- * @obj_desc: MC description of the DPAA device
- * @regions: pointer to array of MMIO region entries
- * @irqs: pointer to array of pointers to interrupts allocated to this device
- * @resource: generic resource associated with this MC object device, if any.
- *
- * Generic device object for MC object devices that are "attached" to a
- * MC bus.
- *
- * NOTES:
- * - For a non-DPRC object its icid is the same as its parent DPRC's icid.
- * - The SMMU notifier callback gets invoked after device_add() has been
- * called for an MC object device, but before the device-specific probe
- * callback gets called.
- * - DP_OBJ_DPRC objects are the only MC objects that have built-in MC
- * portals. For all other MC objects, their device drivers are responsible for
- * allocating MC portals for them by calling fsl_mc_portal_allocate().
- * - Some types of MC objects (e.g., DP_OBJ_DPBP, DP_OBJ_DPCON) are
- * treated as resources that can be allocated/deallocated from the
- * corresponding resource pool in the object's parent DPRC, using the
- * fsl_mc_object_allocate()/fsl_mc_object_free() functions. These MC objects
- * are known as "allocatable" objects. For them, the corresponding
- * fsl_mc_device's 'resource' points to the associated resource object.
- * For MC objects that are not allocatable (e.g., DP_OBJ_DPRC, DP_OBJ_DPNI),
- * 'resource' is NULL.
- */
-struct fsl_mc_device {
- struct device dev;
- u64 dma_mask;
- u16 flags;
- u16 icid;
- u16 mc_handle;
- struct fsl_mc_io *mc_io;
- struct fsl_mc_obj_desc obj_desc;
- struct resource *regions;
- struct fsl_mc_device_irq **irqs;
- struct fsl_mc_resource *resource;
-};
-
-#define to_fsl_mc_device(_dev) \
- container_of(_dev, struct fsl_mc_device, dev)
-
-#define MC_CMD_NUM_OF_PARAMS 7
-
-struct mc_cmd_header {
- u8 src_id;
- u8 flags_hw;
- u8 status;
- u8 flags_sw;
- __le16 token;
- __le16 cmd_id;
-};
-
-struct mc_command {
- u64 header;
- u64 params[MC_CMD_NUM_OF_PARAMS];
-};
-
-enum mc_cmd_status {
- MC_CMD_STATUS_OK = 0x0, /* Completed successfully */
- MC_CMD_STATUS_READY = 0x1, /* Ready to be processed */
- MC_CMD_STATUS_AUTH_ERR = 0x3, /* Authentication error */
- MC_CMD_STATUS_NO_PRIVILEGE = 0x4, /* No privilege */
- MC_CMD_STATUS_DMA_ERR = 0x5, /* DMA or I/O error */
- MC_CMD_STATUS_CONFIG_ERR = 0x6, /* Configuration error */
- MC_CMD_STATUS_TIMEOUT = 0x7, /* Operation timed out */
- MC_CMD_STATUS_NO_RESOURCE = 0x8, /* No resources */
- MC_CMD_STATUS_NO_MEMORY = 0x9, /* No memory available */
- MC_CMD_STATUS_BUSY = 0xA, /* Device is busy */
- MC_CMD_STATUS_UNSUPPORTED_OP = 0xB, /* Unsupported operation */
- MC_CMD_STATUS_INVALID_STATE = 0xC /* Invalid state */
-};
-
-/*
- * MC command flags
- */
-
-/* High priority flag */
-#define MC_CMD_FLAG_PRI 0x80
-/* Command completion flag */
-#define MC_CMD_FLAG_INTR_DIS 0x01
-
-static inline u64 mc_encode_cmd_header(u16 cmd_id,
- u32 cmd_flags,
- u16 token)
-{
- u64 header = 0;
- struct mc_cmd_header *hdr = (struct mc_cmd_header *)&header;
-
- hdr->cmd_id = cpu_to_le16(cmd_id);
- hdr->token = cpu_to_le16(token);
- hdr->status = MC_CMD_STATUS_READY;
- if (cmd_flags & MC_CMD_FLAG_PRI)
- hdr->flags_hw = MC_CMD_FLAG_PRI;
- if (cmd_flags & MC_CMD_FLAG_INTR_DIS)
- hdr->flags_sw = MC_CMD_FLAG_INTR_DIS;
-
- return header;
-}
-
-static inline u16 mc_cmd_hdr_read_token(struct mc_command *cmd)
-{
- struct mc_cmd_header *hdr = (struct mc_cmd_header *)&cmd->header;
- u16 token = le16_to_cpu(hdr->token);
-
- return token;
-}
-
-struct mc_rsp_create {
- __le32 object_id;
-};
-
-struct mc_rsp_api_ver {
- __le16 major_ver;
- __le16 minor_ver;
-};
-
-static inline u32 mc_cmd_read_object_id(struct mc_command *cmd)
-{
- struct mc_rsp_create *rsp_params;
-
- rsp_params = (struct mc_rsp_create *)cmd->params;
- return le32_to_cpu(rsp_params->object_id);
-}
-
-static inline void mc_cmd_read_api_version(struct mc_command *cmd,
- u16 *major_ver,
- u16 *minor_ver)
-{
- struct mc_rsp_api_ver *rsp_params;
-
- rsp_params = (struct mc_rsp_api_ver *)cmd->params;
- *major_ver = le16_to_cpu(rsp_params->major_ver);
- *minor_ver = le16_to_cpu(rsp_params->minor_ver);
-}
-
-/**
- * Bit masks for a MC I/O object (struct fsl_mc_io) flags
- */
-#define FSL_MC_IO_ATOMIC_CONTEXT_PORTAL 0x0001
-
-/**
- * struct fsl_mc_io - MC I/O object to be passed-in to mc_send_command()
- * @dev: device associated with this Mc I/O object
- * @flags: flags for mc_send_command()
- * @portal_size: MC command portal size in bytes
- * @portal_phys_addr: MC command portal physical address
- * @portal_virt_addr: MC command portal virtual address
- * @dpmcp_dev: pointer to the DPMCP device associated with the MC portal.
- *
- * Fields are only meaningful if the FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag is not
- * set:
- * @mutex: Mutex to serialize mc_send_command() calls that use the same MC
- * portal, if the fsl_mc_io object was created with the
- * FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag off. mc_send_command() calls for this
- * fsl_mc_io object must be made only from non-atomic context.
- *
- * Fields are only meaningful if the FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag is
- * set:
- * @spinlock: Spinlock to serialize mc_send_command() calls that use the same MC
- * portal, if the fsl_mc_io object was created with the
- * FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag on. mc_send_command() calls for this
- * fsl_mc_io object can be made from atomic or non-atomic context.
- */
-struct fsl_mc_io {
- struct device *dev;
- u16 flags;
- u32 portal_size;
- phys_addr_t portal_phys_addr;
- void __iomem *portal_virt_addr;
- struct fsl_mc_device *dpmcp_dev;
- union {
- /*
- * This field is only meaningful if the
- * FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag is not set
- */
- struct mutex mutex; /* serializes mc_send_command() */
-
- /*
- * This field is only meaningful if the
- * FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag is set
- */
- spinlock_t spinlock; /* serializes mc_send_command() */
- };
-};
-
-int mc_send_command(struct fsl_mc_io *mc_io, struct mc_command *cmd);
-
-#ifdef CONFIG_FSL_MC_BUS
-#define dev_is_fsl_mc(_dev) ((_dev)->bus == &fsl_mc_bus_type)
-#else
-/* If fsl-mc bus is not present device cannot belong to fsl-mc bus */
-#define dev_is_fsl_mc(_dev) (0)
-#endif
-
-/*
- * module_fsl_mc_driver() - Helper macro for drivers that don't do
- * anything special in module init/exit. This eliminates a lot of
- * boilerplate. Each module may only use this macro once, and
- * calling it replaces module_init() and module_exit()
- */
-#define module_fsl_mc_driver(__fsl_mc_driver) \
- module_driver(__fsl_mc_driver, fsl_mc_driver_register, \
- fsl_mc_driver_unregister)
-
-/*
- * Macro to avoid include chaining to get THIS_MODULE
- */
-#define fsl_mc_driver_register(drv) \
- __fsl_mc_driver_register(drv, THIS_MODULE)
-
-int __must_check __fsl_mc_driver_register(struct fsl_mc_driver *fsl_mc_driver,
- struct module *owner);
-
-void fsl_mc_driver_unregister(struct fsl_mc_driver *driver);
-
-int __must_check fsl_mc_portal_allocate(struct fsl_mc_device *mc_dev,
- u16 mc_io_flags,
- struct fsl_mc_io **new_mc_io);
-
-void fsl_mc_portal_free(struct fsl_mc_io *mc_io);
-
-int fsl_mc_portal_reset(struct fsl_mc_io *mc_io);
-
-int __must_check fsl_mc_object_allocate(struct fsl_mc_device *mc_dev,
- enum fsl_mc_pool_type pool_type,
- struct fsl_mc_device **new_mc_adev);
-
-void fsl_mc_object_free(struct fsl_mc_device *mc_adev);
-
-struct irq_domain *fsl_mc_msi_create_irq_domain(struct fwnode_handle *fwnode,
- struct msi_domain_info *info,
- struct irq_domain *parent);
-
-int __must_check fsl_mc_allocate_irqs(struct fsl_mc_device *mc_dev);
-
-void fsl_mc_free_irqs(struct fsl_mc_device *mc_dev);
-
-extern struct bus_type fsl_mc_bus_type;
-
-extern struct device_type fsl_mc_bus_dprc_type;
-extern struct device_type fsl_mc_bus_dpni_type;
-extern struct device_type fsl_mc_bus_dpio_type;
-extern struct device_type fsl_mc_bus_dpsw_type;
-extern struct device_type fsl_mc_bus_dpbp_type;
-extern struct device_type fsl_mc_bus_dpcon_type;
-extern struct device_type fsl_mc_bus_dpmcp_type;
-extern struct device_type fsl_mc_bus_dpmac_type;
-extern struct device_type fsl_mc_bus_dprtc_type;
-
-static inline bool is_fsl_mc_bus_dprc(const struct fsl_mc_device *mc_dev)
-{
- return mc_dev->dev.type == &fsl_mc_bus_dprc_type;
-}
-
-static inline bool is_fsl_mc_bus_dpni(const struct fsl_mc_device *mc_dev)
-{
- return mc_dev->dev.type == &fsl_mc_bus_dpni_type;
-}
-
-static inline bool is_fsl_mc_bus_dpio(const struct fsl_mc_device *mc_dev)
-{
- return mc_dev->dev.type == &fsl_mc_bus_dpio_type;
-}
-
-static inline bool is_fsl_mc_bus_dpsw(const struct fsl_mc_device *mc_dev)
-{
- return mc_dev->dev.type == &fsl_mc_bus_dpsw_type;
-}
-
-static inline bool is_fsl_mc_bus_dpbp(const struct fsl_mc_device *mc_dev)
-{
- return mc_dev->dev.type == &fsl_mc_bus_dpbp_type;
-}
-
-static inline bool is_fsl_mc_bus_dpcon(const struct fsl_mc_device *mc_dev)
-{
- return mc_dev->dev.type == &fsl_mc_bus_dpcon_type;
-}
-
-static inline bool is_fsl_mc_bus_dpmcp(const struct fsl_mc_device *mc_dev)
-{
- return mc_dev->dev.type == &fsl_mc_bus_dpmcp_type;
-}
-
-static inline bool is_fsl_mc_bus_dpmac(const struct fsl_mc_device *mc_dev)
-{
- return mc_dev->dev.type == &fsl_mc_bus_dpmac_type;
-}
-
-static inline bool is_fsl_mc_bus_dprtc(const struct fsl_mc_device *mc_dev)
-{
- return mc_dev->dev.type == &fsl_mc_bus_dprtc_type;
-}
-
-#endif /* _FSL_MC_H_ */
diff --git a/drivers/staging/fsl-mc/overview.rst b/drivers/staging/fsl-mc/overview.rst
deleted file mode 100644
index 79fede4447d6..000000000000
--- a/drivers/staging/fsl-mc/overview.rst
+++ /dev/null
@@ -1,404 +0,0 @@
-.. include:: <isonum.txt>
-
-DPAA2 (Data Path Acceleration Architecture Gen2) Overview
-=========================================================
-
-:Copyright: |copy| 2015 Freescale Semiconductor Inc.
-:Copyright: |copy| 2018 NXP
-
-This document provides an overview of the Freescale DPAA2 architecture
-and how it is integrated into the Linux kernel.
-
-Introduction
-============
-
-DPAA2 is a hardware architecture designed for high-speeed network
-packet processing. DPAA2 consists of sophisticated mechanisms for
-processing Ethernet packets, queue management, buffer management,
-autonomous L2 switching, virtual Ethernet bridging, and accelerator
-(e.g. crypto) sharing.
-
-A DPAA2 hardware component called the Management Complex (or MC) manages the
-DPAA2 hardware resources. The MC provides an object-based abstraction for
-software drivers to use the DPAA2 hardware.
-The MC uses DPAA2 hardware resources such as queues, buffer pools, and
-network ports to create functional objects/devices such as network
-interfaces, an L2 switch, or accelerator instances.
-The MC provides memory-mapped I/O command interfaces (MC portals)
-which DPAA2 software drivers use to operate on DPAA2 objects.
-
-The diagram below shows an overview of the DPAA2 resource management
-architecture::
-
- +--------------------------------------+
- | OS |
- | DPAA2 drivers |
- | | |
- +-----------------------------|--------+
- |
- | (create,discover,connect
- | config,use,destroy)
- |
- DPAA2 |
- +------------------------| mc portal |-+
- | | |
- | +- - - - - - - - - - - - -V- - -+ |
- | | | |
- | | Management Complex (MC) | |
- | | | |
- | +- - - - - - - - - - - - - - - -+ |
- | |
- | Hardware Hardware |
- | Resources Objects |
- | --------- ------- |
- | -queues -DPRC |
- | -buffer pools -DPMCP |
- | -Eth MACs/ports -DPIO |
- | -network interface -DPNI |
- | profiles -DPMAC |
- | -queue portals -DPBP |
- | -MC portals ... |
- | ... |
- | |
- +--------------------------------------+
-
-
-The MC mediates operations such as create, discover,
-connect, configuration, and destroy. Fast-path operations
-on data, such as packet transmit/receive, are not mediated by
-the MC and are done directly using memory mapped regions in
-DPIO objects.
-
-Overview of DPAA2 Objects
-=========================
-
-The section provides a brief overview of some key DPAA2 objects.
-A simple scenario is described illustrating the objects involved
-in creating a network interfaces.
-
-DPRC (Datapath Resource Container)
-----------------------------------
-
-A DPRC is a container object that holds all the other
-types of DPAA2 objects. In the example diagram below there
-are 8 objects of 5 types (DPMCP, DPIO, DPBP, DPNI, and DPMAC)
-in the container.
-
-::
-
- +---------------------------------------------------------+
- | DPRC |
- | |
- | +-------+ +-------+ +-------+ +-------+ +-------+ |
- | | DPMCP | | DPIO | | DPBP | | DPNI | | DPMAC | |
- | +-------+ +-------+ +-------+ +---+---+ +---+---+ |
- | | DPMCP | | DPIO | |
- | +-------+ +-------+ |
- | | DPMCP | |
- | +-------+ |
- | |
- +---------------------------------------------------------+
-
-From the point of view of an OS, a DPRC behaves similar to a plug and
-play bus, like PCI. DPRC commands can be used to enumerate the contents
-of the DPRC, discover the hardware objects present (including mappable
-regions and interrupts).
-
-::
-
- DPRC.1 (bus)
- |
- +--+--------+-------+-------+-------+
- | | | | |
- DPMCP.1 DPIO.1 DPBP.1 DPNI.1 DPMAC.1
- DPMCP.2 DPIO.2
- DPMCP.3
-
-Hardware objects can be created and destroyed dynamically, providing
-the ability to hot plug/unplug objects in and out of the DPRC.
-
-A DPRC has a mappable MMIO region (an MC portal) that can be used
-to send MC commands. It has an interrupt for status events (like
-hotplug).
-All objects in a container share the same hardware "isolation context".
-This means that with respect to an IOMMU the isolation granularity
-is at the DPRC (container) level, not at the individual object
-level.
-
-DPRCs can be defined statically and populated with objects
-via a config file passed to the MC when firmware starts it.
-
-DPAA2 Objects for an Ethernet Network Interface
------------------------------------------------
-
-A typical Ethernet NIC is monolithic-- the NIC device contains TX/RX
-queuing mechanisms, configuration mechanisms, buffer management,
-physical ports, and interrupts. DPAA2 uses a more granular approach
-utilizing multiple hardware objects. Each object provides specialized
-functions. Groups of these objects are used by software to provide
-Ethernet network interface functionality. This approach provides
-efficient use of finite hardware resources, flexibility, and
-performance advantages.
-
-The diagram below shows the objects needed for a simple
-network interface configuration on a system with 2 CPUs.
-
-::
-
- +---+---+ +---+---+
- CPU0 CPU1
- +---+---+ +---+---+
- | |
- +---+---+ +---+---+
- DPIO DPIO
- +---+---+ +---+---+
- \ /
- \ /
- \ /
- +---+---+
- DPNI --- DPBP,DPMCP
- +---+---+
- |
- |
- +---+---+
- DPMAC
- +---+---+
- |
- port/PHY
-
-Below the objects are described. For each object a brief description
-is provided along with a summary of the kinds of operations the object
-supports and a summary of key resources of the object (MMIO regions
-and IRQs).
-
-DPMAC (Datapath Ethernet MAC)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Represents an Ethernet MAC, a hardware device that connects to an Ethernet
-PHY and allows physical transmission and reception of Ethernet frames.
-
-- MMIO regions: none
-- IRQs: DPNI link change
-- commands: set link up/down, link config, get stats,
- IRQ config, enable, reset
-
-DPNI (Datapath Network Interface)
-Contains TX/RX queues, network interface configuration, and RX buffer pool
-configuration mechanisms. The TX/RX queues are in memory and are identified
-by queue number.
-
-- MMIO regions: none
-- IRQs: link state
-- commands: port config, offload config, queue config,
- parse/classify config, IRQ config, enable, reset
-
-DPIO (Datapath I/O)
-~~~~~~~~~~~~~~~~~~~
-Provides interfaces to enqueue and dequeue
-packets and do hardware buffer pool management operations. The DPAA2
-architecture separates the mechanism to access queues (the DPIO object)
-from the queues themselves. The DPIO provides an MMIO interface to
-enqueue/dequeue packets. To enqueue something a descriptor is written
-to the DPIO MMIO region, which includes the target queue number.
-There will typically be one DPIO assigned to each CPU. This allows all
-CPUs to simultaneously perform enqueue/dequeued operations. DPIOs are
-expected to be shared by different DPAA2 drivers.
-
-- MMIO regions: queue operations, buffer management
-- IRQs: data availability, congestion notification, buffer
- pool depletion
-- commands: IRQ config, enable, reset
-
-DPBP (Datapath Buffer Pool)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Represents a hardware buffer pool.
-
-- MMIO regions: none
-- IRQs: none
-- commands: enable, reset
-
-DPMCP (Datapath MC Portal)
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-Provides an MC command portal.
-Used by drivers to send commands to the MC to manage
-objects.
-
-- MMIO regions: MC command portal
-- IRQs: command completion
-- commands: IRQ config, enable, reset
-
-Object Connections
-==================
-Some objects have explicit relationships that must
-be configured:
-
-- DPNI <--> DPMAC
-- DPNI <--> DPNI
-- DPNI <--> L2-switch-port
-
- A DPNI must be connected to something such as a DPMAC,
- another DPNI, or L2 switch port. The DPNI connection
- is made via a DPRC command.
-
-::
-
- +-------+ +-------+
- | DPNI | | DPMAC |
- +---+---+ +---+---+
- | |
- +==========+
-
-- DPNI <--> DPBP
-
- A network interface requires a 'buffer pool' (DPBP
- object) which provides a list of pointers to memory
- where received Ethernet data is to be copied. The
- Ethernet driver configures the DPBPs associated with
- the network interface.
-
-Interrupts
-==========
-All interrupts generated by DPAA2 objects are message
-interrupts. At the hardware level message interrupts
-generated by devices will normally have 3 components--
-1) a non-spoofable 'device-id' expressed on the hardware
-bus, 2) an address, 3) a data value.
-
-In the case of DPAA2 devices/objects, all objects in the
-same container/DPRC share the same 'device-id'.
-For ARM-based SoC this is the same as the stream ID.
-
-
-DPAA2 Linux Drivers Overview
-============================
-
-This section provides an overview of the Linux kernel drivers for
-DPAA2-- 1) the bus driver and associated "DPAA2 infrastructure"
-drivers and 2) functional object drivers (such as Ethernet).
-
-As described previously, a DPRC is a container that holds the other
-types of DPAA2 objects. It is functionally similar to a plug-and-play
-bus controller.
-Each object in the DPRC is a Linux "device" and is bound to a driver.
-The diagram below shows the Linux drivers involved in a networking
-scenario and the objects bound to each driver. A brief description
-of each driver follows.
-
-::
-
- +------------+
- | OS Network |
- | Stack |
- +------------+ +------------+
- | Allocator |. . . . . . . | Ethernet |
- |(DPMCP,DPBP)| | (DPNI) |
- +-.----------+ +---+---+----+
- . . ^ |
- . . <data avail, | | <enqueue,
- . . tx confirm> | | dequeue>
- +-------------+ . | |
- | DPRC driver | . +---+---V----+ +---------+
- | (DPRC) | . . . . . .| DPIO driver| | MAC |
- +----------+--+ | (DPIO) | | (DPMAC) |
- | +------+-----+ +-----+---+
- |<dev add/remove> | |
- | | |
- +--------+----------+ | +--+---+
- | MC-bus driver | | | PHY |
- | | | |driver|
- | /bus/fsl-mc | | +--+---+
- +-------------------+ | |
- | |
- ========================= HARDWARE =========|=================|======
- DPIO |
- | |
- DPNI---DPBP |
- | |
- DPMAC |
- | |
- PHY ---------------+
- ============================================|========================
-
-A brief description of each driver is provided below.
-
-MC-bus driver
--------------
-The MC-bus driver is a platform driver and is probed from a
-node in the device tree (compatible "fsl,qoriq-mc") passed in by boot
-firmware. It is responsible for bootstrapping the DPAA2 kernel
-infrastructure.
-Key functions include:
-
-- registering a new bus type named "fsl-mc" with the kernel,
- and implementing bus call-backs (e.g. match/uevent/dev_groups)
-- implementing APIs for DPAA2 driver registration and for device
- add/remove
-- creates an MSI IRQ domain
-- doing a 'device add' to expose the 'root' DPRC, in turn triggering
- a bind of the root DPRC to the DPRC driver
-
-The binding for the MC-bus device-tree node can be consulted at
-*Documentation/devicetree/bindings/misc/fsl,qoriq-mc.txt*.
-The sysfs bind/unbind interfaces for the MC-bus can be consulted at
-*Documentation/ABI/testing/sysfs-bus-fsl-mc*.
-
-DPRC driver
------------
-The DPRC driver is bound to DPRC objects and does runtime management
-of a bus instance. It performs the initial bus scan of the DPRC
-and handles interrupts for container events such as hot plug by
-re-scanning the DPRC.
-
-Allocator
----------
-Certain objects such as DPMCP and DPBP are generic and fungible,
-and are intended to be used by other drivers. For example,
-the DPAA2 Ethernet driver needs:
-
-- DPMCPs to send MC commands, to configure network interfaces
-- DPBPs for network buffer pools
-
-The allocator driver registers for these allocatable object types
-and those objects are bound to the allocator when the bus is probed.
-The allocator maintains a pool of objects that are available for
-allocation by other DPAA2 drivers.
-
-DPIO driver
------------
-The DPIO driver is bound to DPIO objects and provides services that allow
-other drivers such as the Ethernet driver to enqueue and dequeue data for
-their respective objects.
-Key services include:
-
-- data availability notifications
-- hardware queuing operations (enqueue and dequeue of data)
-- hardware buffer pool management
-
-To transmit a packet the Ethernet driver puts data on a queue and
-invokes a DPIO API. For receive, the Ethernet driver registers
-a data availability notification callback. To dequeue a packet
-a DPIO API is used.
-There is typically one DPIO object per physical CPU for optimum
-performance, allowing different CPUs to simultaneously enqueue
-and dequeue data.
-
-The DPIO driver operates on behalf of all DPAA2 drivers
-active in the kernel-- Ethernet, crypto, compression,
-etc.
-
-Ethernet driver
----------------
-The Ethernet driver is bound to a DPNI and implements the kernel
-interfaces needed to connect the DPAA2 network interface to
-the network stack.
-Each DPNI corresponds to a Linux network interface.
-
-MAC driver
-----------
-An Ethernet PHY is an off-chip, board specific component and is managed
-by the appropriate PHY driver via an mdio bus. The MAC driver
-plays a role of being a proxy between the PHY driver and the
-MC. It does this proxy via the MC commands to a DPMAC object.
-If the PHY driver signals a link change, the MAC driver notifies
-the MC via a DPMAC command. If a network interface is brought
-up or down, the MC notifies the DPMAC driver via an interrupt and
-the driver can take appropriate action.
diff --git a/drivers/staging/gdm724x/gdm_endian.c b/drivers/staging/gdm724x/gdm_endian.c
index d0b43e20ec06..4200391b1a97 100644
--- a/drivers/staging/gdm724x/gdm_endian.c
+++ b/drivers/staging/gdm724x/gdm_endian.c
@@ -14,41 +14,33 @@
#include <linux/kernel.h>
#include "gdm_endian.h"
-void gdm_set_endian(struct gdm_endian *ed, u8 dev_endian)
+__dev16 gdm_cpu_to_dev16(u8 dev_ed, u16 x)
{
- if (dev_endian == ENDIANNESS_BIG)
- ed->dev_ed = ENDIANNESS_BIG;
- else
- ed->dev_ed = ENDIANNESS_LITTLE;
-}
-
-__dev16 gdm_cpu_to_dev16(struct gdm_endian *ed, u16 x)
-{
- if (ed->dev_ed == ENDIANNESS_LITTLE)
+ if (dev_ed == ENDIANNESS_LITTLE)
return (__force __dev16)cpu_to_le16(x);
else
return (__force __dev16)cpu_to_be16(x);
}
-u16 gdm_dev16_to_cpu(struct gdm_endian *ed, __dev16 x)
+u16 gdm_dev16_to_cpu(u8 dev_ed, __dev16 x)
{
- if (ed->dev_ed == ENDIANNESS_LITTLE)
+ if (dev_ed == ENDIANNESS_LITTLE)
return le16_to_cpu((__force __le16)x);
else
return be16_to_cpu((__force __be16)x);
}
-__dev32 gdm_cpu_to_dev32(struct gdm_endian *ed, u32 x)
+__dev32 gdm_cpu_to_dev32(u8 dev_ed, u32 x)
{
- if (ed->dev_ed == ENDIANNESS_LITTLE)
+ if (dev_ed == ENDIANNESS_LITTLE)
return (__force __dev32)cpu_to_le32(x);
else
return (__force __dev32)cpu_to_be32(x);
}
-u32 gdm_dev32_to_cpu(struct gdm_endian *ed, __dev32 x)
+u32 gdm_dev32_to_cpu(u8 dev_ed, __dev32 x)
{
- if (ed->dev_ed == ENDIANNESS_LITTLE)
+ if (dev_ed == ENDIANNESS_LITTLE)
return le32_to_cpu((__force __le32)x);
else
return be32_to_cpu((__force __be32)x);
diff --git a/drivers/staging/gdm724x/gdm_endian.h b/drivers/staging/gdm724x/gdm_endian.h
index a785f30bb369..e58d29f868ba 100644
--- a/drivers/staging/gdm724x/gdm_endian.h
+++ b/drivers/staging/gdm724x/gdm_endian.h
@@ -32,14 +32,9 @@ enum {
ENDIANNESS_MAX
};
-struct gdm_endian {
- u8 dev_ed;
-};
-
-void gdm_set_endian(struct gdm_endian *ed, u8 dev_endian);
-__dev16 gdm_cpu_to_dev16(struct gdm_endian *ed, u16 x);
-u16 gdm_dev16_to_cpu(struct gdm_endian *ed, __dev16 x);
-__dev32 gdm_cpu_to_dev32(struct gdm_endian *ed, u32 x);
-u32 gdm_dev32_to_cpu(struct gdm_endian *ed, __dev32 x);
+__dev16 gdm_cpu_to_dev16(u8 dev_ed, u16 x);
+u16 gdm_dev16_to_cpu(u8 dev_ed, __dev16 x);
+__dev32 gdm_cpu_to_dev32(u8 dev_ed, u32 x);
+u32 gdm_dev32_to_cpu(u8 dev_ed, __dev32 x);
#endif /*__GDM_ENDIAN_H__*/
diff --git a/drivers/staging/gdm724x/gdm_lte.c b/drivers/staging/gdm724x/gdm_lte.c
index 0527b0d1c1d0..4f3c518304f2 100644
--- a/drivers/staging/gdm724x/gdm_lte.c
+++ b/drivers/staging/gdm724x/gdm_lte.c
@@ -185,6 +185,7 @@ static __sum16 icmp6_checksum(struct ipv6hdr *ipv6, u16 *ptr, int len)
unsigned short *w = ptr;
__wsum sum = 0;
int i;
+ u16 pa;
union {
struct {
@@ -204,9 +205,10 @@ static __sum16 icmp6_checksum(struct ipv6hdr *ipv6, u16 *ptr, int len)
pseudo_header.ph.ph_nxt = ipv6->nexthdr;
w = (u16 *)&pseudo_header;
- for (i = 0; i < ARRAY_SIZE(pseudo_header.pa); i++)
- sum = csum_add(sum, csum_unfold(
- (__force __sum16)pseudo_header.pa[i]));
+ for (i = 0; i < ARRAY_SIZE(pseudo_header.pa); i++) {
+ pa = pseudo_header.pa[i];
+ sum = csum_add(sum, csum_unfold((__force __sum16)pa));
+ }
w = ptr;
while (len > 1) {
@@ -308,7 +310,8 @@ static int gdm_lte_emulate_ndp(struct sk_buff *skb_in, u32 nic_type)
sizeof(struct neighbour_advertisement));
icmp6_out.icmp6_cksum = icmp6_checksum(&ipv6_out,
- (u16 *)icmp_na, sizeof(icmp_na));
+ (u16 *)icmp_na,
+ sizeof(icmp_na));
} else {
return -EINVAL;
}
@@ -508,8 +511,9 @@ static struct net_device_stats *gdm_lte_stats(struct net_device *dev)
static int gdm_lte_event_send(struct net_device *dev, char *buf, int len)
{
- struct nic *nic = netdev_priv(dev);
+ struct phy_dev *phy_dev = ((struct nic *)netdev_priv(dev))->phy_dev;
struct hci_packet *hci = (struct hci_packet *)buf;
+ int length;
int idx;
int ret;
@@ -517,11 +521,9 @@ static int gdm_lte_event_send(struct net_device *dev, char *buf, int len)
if (ret != 1)
return -EINVAL;
- return netlink_send(lte_event.sock, idx, 0, buf,
- gdm_dev16_to_cpu(
- nic->phy_dev->get_endian(
- nic->phy_dev->priv_dev), hci->len)
- + HCI_HEADER_SIZE);
+ length = gdm_dev16_to_cpu(phy_dev->get_endian(phy_dev->priv_dev),
+ hci->len) + HCI_HEADER_SIZE;
+ return netlink_send(lte_event.sock, idx, 0, buf, length);
}
static void gdm_lte_event_rcv(struct net_device *dev, u16 type,
@@ -683,7 +685,7 @@ static void gdm_lte_multi_sdu_pkt(struct phy_dev *phy_dev, char *buf, int len)
struct net_device *dev;
struct multi_sdu *multi_sdu = (struct multi_sdu *)buf;
struct sdu *sdu = NULL;
- struct gdm_endian *endian = phy_dev->get_endian(phy_dev->priv_dev);
+ u8 endian = phy_dev->get_endian(phy_dev->priv_dev);
u8 *data = (u8 *)multi_sdu->data;
u16 i = 0;
u16 num_packet;
@@ -728,33 +730,30 @@ static void gdm_lte_pdn_table(struct net_device *dev, char *buf, int len)
{
struct nic *nic = netdev_priv(dev);
struct hci_pdn_table_ind *pdn_table = (struct hci_pdn_table_ind *)buf;
+ u8 ed = nic->phy_dev->get_endian(nic->phy_dev->priv_dev);
- if (pdn_table->activate) {
- nic->pdn_table.activate = pdn_table->activate;
- nic->pdn_table.dft_eps_id = gdm_dev32_to_cpu(
- nic->phy_dev->get_endian(
- nic->phy_dev->priv_dev),
- pdn_table->dft_eps_id);
- nic->pdn_table.nic_type = gdm_dev32_to_cpu(
- nic->phy_dev->get_endian(
- nic->phy_dev->priv_dev),
- pdn_table->nic_type);
-
- netdev_info(dev, "pdn activated, nic_type=0x%x\n",
- nic->pdn_table.nic_type);
- } else {
+ if (!pdn_table->activate) {
memset(&nic->pdn_table, 0x00, sizeof(struct pdn_table));
netdev_info(dev, "pdn deactivated\n");
+
+ return;
}
+
+ nic->pdn_table.activate = pdn_table->activate;
+ nic->pdn_table.dft_eps_id = gdm_dev32_to_cpu(ed, pdn_table->dft_eps_id);
+ nic->pdn_table.nic_type = gdm_dev32_to_cpu(ed, pdn_table->nic_type);
+
+ netdev_info(dev, "pdn activated, nic_type=0x%x\n",
+ nic->pdn_table.nic_type);
}
static int gdm_lte_receive_pkt(struct phy_dev *phy_dev, char *buf, int len)
{
struct hci_packet *hci = (struct hci_packet *)buf;
struct hci_pdn_table_ind *pdn_table = (struct hci_pdn_table_ind *)buf;
- struct gdm_endian *endian = phy_dev->get_endian(phy_dev->priv_dev);
struct sdu *sdu;
struct net_device *dev;
+ u8 endian = phy_dev->get_endian(phy_dev->priv_dev);
int ret = 0;
u16 cmd_evt;
u32 nic_type;
@@ -896,12 +895,11 @@ int register_lte_device(struct phy_dev *phy_dev,
nic->phy_dev = phy_dev;
nic->nic_id = index;
- form_mac_address(
- net->dev_addr,
- nic->src_mac_addr,
- nic->dest_mac_addr,
- mac_address,
- index);
+ form_mac_address(net->dev_addr,
+ nic->src_mac_addr,
+ nic->dest_mac_addr,
+ mac_address,
+ index);
SET_NETDEV_DEV(net, dev);
SET_NETDEV_DEVTYPE(net, &wwan_type);
diff --git a/drivers/staging/gdm724x/gdm_lte.h b/drivers/staging/gdm724x/gdm_lte.h
index 3ecaff1a40cb..bad0855e4721 100644
--- a/drivers/staging/gdm724x/gdm_lte.h
+++ b/drivers/staging/gdm724x/gdm_lte.h
@@ -56,7 +56,7 @@ struct phy_dev {
int (*cb)(void *cb_data, void *data, int len,
int context),
void *cb_data, int context);
- struct gdm_endian * (*get_endian)(void *priv_dev);
+ u8 (*get_endian)(void *priv_dev);
};
struct nic {
diff --git a/drivers/staging/gdm724x/gdm_mux.c b/drivers/staging/gdm724x/gdm_mux.c
index 996b1f538aae..63921bad519e 100644
--- a/drivers/staging/gdm724x/gdm_mux.c
+++ b/drivers/staging/gdm724x/gdm_mux.c
@@ -657,7 +657,11 @@ static struct usb_driver gdm_mux_driver = {
static int __init gdm_usb_mux_init(void)
{
- register_lte_tty_driver();
+ int ret;
+
+ ret = register_lte_tty_driver();
+ if (ret)
+ return ret;
return usb_register(&gdm_mux_driver);
}
diff --git a/drivers/staging/gdm724x/gdm_tty.c b/drivers/staging/gdm724x/gdm_tty.c
index fc7682c18f20..3cdebb81ba63 100644
--- a/drivers/staging/gdm724x/gdm_tty.c
+++ b/drivers/staging/gdm724x/gdm_tty.c
@@ -37,13 +37,6 @@
#define MUX_TX_MAX_SIZE 2048
-#define gdm_tty_send(n, d, l, i, c, b) (\
- n->tty_dev->send_func(n->tty_dev->priv_dev, d, l, i, c, b))
-#define gdm_tty_recv(n, c) (\
- n->tty_dev->recv_func(n->tty_dev->priv_dev, c))
-#define gdm_tty_send_control(n, r, v, d, l) (\
- n->tty_dev->send_control(n->tty_dev->priv_dev, r, v, d, l))
-
#define GDM_TTY_READY(gdm) (gdm && gdm->tty_dev && gdm->port.count)
static struct tty_driver *gdm_driver[TTY_MAX_COUNT];
@@ -146,7 +139,8 @@ static int gdm_tty_recv_complete(void *data,
if (!GDM_TTY_READY(gdm)) {
if (complete == RECV_PACKET_PROCESS_COMPLETE)
- gdm_tty_recv(gdm, gdm_tty_recv_complete);
+ gdm->tty_dev->recv_func(gdm->tty_dev->priv_dev,
+ gdm_tty_recv_complete);
return TO_HOST_PORT_CLOSE;
}
@@ -160,7 +154,8 @@ static int gdm_tty_recv_complete(void *data,
}
if (complete == RECV_PACKET_PROCESS_COMPLETE)
- gdm_tty_recv(gdm, gdm_tty_recv_complete);
+ gdm->tty_dev->recv_func(gdm->tty_dev->priv_dev,
+ gdm_tty_recv_complete);
return 0;
}
@@ -191,13 +186,12 @@ static int gdm_tty_write(struct tty_struct *tty, const unsigned char *buf,
while (1) {
sending_len = min(MUX_TX_MAX_SIZE, remain);
- gdm_tty_send(gdm,
- (void *)(buf + sent_len),
- sending_len,
- gdm->index,
- gdm_tty_send_complete,
- gdm
- );
+ gdm->tty_dev->send_func(gdm->tty_dev->priv_dev,
+ (void *)(buf + sent_len),
+ sending_len,
+ gdm->index,
+ gdm_tty_send_complete,
+ gdm);
sent_len += sending_len;
remain -= sending_len;
if (remain <= 0)
@@ -256,7 +250,8 @@ int register_lte_tty_device(struct tty_dev *tty_dev, struct device *device)
}
for (i = 0; i < MAX_ISSUE_NUM; i++)
- gdm_tty_recv(gdm, gdm_tty_recv_complete);
+ gdm->tty_dev->recv_func(gdm->tty_dev->priv_dev,
+ gdm_tty_recv_complete);
return 0;
}
diff --git a/drivers/staging/gdm724x/gdm_usb.c b/drivers/staging/gdm724x/gdm_usb.c
index 87cd1f827455..c95bad4a8615 100644
--- a/drivers/staging/gdm724x/gdm_usb.c
+++ b/drivers/staging/gdm724x/gdm_usb.c
@@ -72,8 +72,8 @@ static int request_mac_address(struct lte_udev *udev)
int actual;
int ret = -1;
- hci->cmd_evt = gdm_cpu_to_dev16(&udev->gdm_ed, LTE_GET_INFORMATION);
- hci->len = gdm_cpu_to_dev16(&udev->gdm_ed, 1);
+ hci->cmd_evt = gdm_cpu_to_dev16(udev->gdm_ed, LTE_GET_INFORMATION);
+ hci->len = gdm_cpu_to_dev16(udev->gdm_ed, 1);
hci->data[0] = MAC_ADDRESS;
ret = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 2), buf, 5,
@@ -410,7 +410,7 @@ static void do_rx(struct work_struct *work)
phy_dev = r->cb_data;
udev = phy_dev->priv_dev;
hci = (struct hci_packet *)r->buf;
- cmd_evt = gdm_dev16_to_cpu(&udev->gdm_ed, hci->cmd_evt);
+ cmd_evt = gdm_dev16_to_cpu(udev->gdm_ed, hci->cmd_evt);
switch (cmd_evt) {
case LTE_GET_INFORMATION_RESULT:
@@ -604,7 +604,7 @@ static u32 packet_aggregation(struct lte_udev *udev, u8 *send_buf)
u16 num_packet = 0;
unsigned long flags;
- multi_sdu->cmd_evt = gdm_cpu_to_dev16(&udev->gdm_ed, LTE_TX_MULTI_SDU);
+ multi_sdu->cmd_evt = gdm_cpu_to_dev16(udev->gdm_ed, LTE_TX_MULTI_SDU);
while (num_packet < MAX_PACKET_IN_MULTI_SDU) {
spin_lock_irqsave(&tx->lock, flags);
@@ -635,8 +635,8 @@ static u32 packet_aggregation(struct lte_udev *udev, u8 *send_buf)
spin_unlock_irqrestore(&tx->lock, flags);
}
- multi_sdu->len = gdm_cpu_to_dev16(&udev->gdm_ed, send_len);
- multi_sdu->num_packet = gdm_cpu_to_dev16(&udev->gdm_ed, num_packet);
+ multi_sdu->len = gdm_cpu_to_dev16(udev->gdm_ed, send_len);
+ multi_sdu->num_packet = gdm_cpu_to_dev16(udev->gdm_ed, num_packet);
return send_len + offsetof(struct multi_sdu, data);
}
@@ -735,7 +735,7 @@ static int gdm_usb_sdu_send(void *priv_dev, void *data, int len,
}
sdu = (struct sdu *)t_sdu->buf;
- sdu->cmd_evt = gdm_cpu_to_dev16(&udev->gdm_ed, LTE_TX_SDU);
+ sdu->cmd_evt = gdm_cpu_to_dev16(udev->gdm_ed, LTE_TX_SDU);
if (nic_type == NIC_TYPE_ARP) {
send_len = len + SDU_PARAM_LEN;
memcpy(sdu->data, data, len);
@@ -745,10 +745,10 @@ static int gdm_usb_sdu_send(void *priv_dev, void *data, int len,
memcpy(sdu->data, data + ETH_HLEN, len - ETH_HLEN);
}
- sdu->len = gdm_cpu_to_dev16(&udev->gdm_ed, send_len);
- sdu->dft_eps_ID = gdm_cpu_to_dev32(&udev->gdm_ed, dft_eps_ID);
- sdu->bearer_ID = gdm_cpu_to_dev32(&udev->gdm_ed, eps_ID);
- sdu->nic_type = gdm_cpu_to_dev32(&udev->gdm_ed, nic_type);
+ sdu->len = gdm_cpu_to_dev16(udev->gdm_ed, send_len);
+ sdu->dft_eps_ID = gdm_cpu_to_dev32(udev->gdm_ed, dft_eps_ID);
+ sdu->bearer_ID = gdm_cpu_to_dev32(udev->gdm_ed, eps_ID);
+ sdu->nic_type = gdm_cpu_to_dev32(udev->gdm_ed, nic_type);
t_sdu->len = send_len + HCI_HEADER_SIZE;
t_sdu->callback = cb;
@@ -799,11 +799,11 @@ static int gdm_usb_hci_send(void *priv_dev, void *data, int len,
return 0;
}
-static struct gdm_endian *gdm_usb_get_endian(void *priv_dev)
+static u8 gdm_usb_get_endian(void *priv_dev)
{
struct lte_udev *udev = priv_dev;
- return &udev->gdm_ed;
+ return udev->gdm_ed;
}
static int gdm_usb_probe(struct usb_interface *intf,
@@ -859,9 +859,9 @@ static int gdm_usb_probe(struct usb_interface *intf,
* defaults to little endian
*/
if (idProduct == PID_GDM7243)
- gdm_set_endian(&udev->gdm_ed, ENDIANNESS_BIG);
+ udev->gdm_ed = ENDIANNESS_BIG;
else
- gdm_set_endian(&udev->gdm_ed, ENDIANNESS_LITTLE);
+ udev->gdm_ed = ENDIANNESS_LITTLE;
ret = request_mac_address(udev);
if (ret < 0) {
diff --git a/drivers/staging/gdm724x/gdm_usb.h b/drivers/staging/gdm724x/gdm_usb.h
index ffb3d995097d..701038685e23 100644
--- a/drivers/staging/gdm724x/gdm_usb.h
+++ b/drivers/staging/gdm724x/gdm_usb.h
@@ -93,11 +93,11 @@ struct rx_cxt {
struct lte_udev {
struct usb_device *usbdev;
- struct gdm_endian gdm_ed;
struct tx_cxt tx;
struct rx_cxt rx;
struct delayed_work work_tx;
struct delayed_work work_rx;
+ u8 gdm_ed;
u8 send_complete;
u8 tx_stop;
struct usb_interface *intf;
diff --git a/drivers/staging/goldfish/goldfish_nand.c b/drivers/staging/goldfish/goldfish_nand.c
index 52cc1363993e..f5e002ecba75 100644
--- a/drivers/staging/goldfish/goldfish_nand.c
+++ b/drivers/staging/goldfish/goldfish_nand.c
@@ -119,9 +119,6 @@ static int goldfish_nand_erase(struct mtd_info *mtd, struct erase_info *instr)
return -EIO;
}
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
-
return 0;
invalid_arg:
diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig
index 8abc1ab3c0c7..bd9445956511 100644
--- a/drivers/staging/iio/Kconfig
+++ b/drivers/staging/iio/Kconfig
@@ -14,6 +14,5 @@ source "drivers/staging/iio/impedance-analyzer/Kconfig"
source "drivers/staging/iio/light/Kconfig"
source "drivers/staging/iio/meter/Kconfig"
source "drivers/staging/iio/resolver/Kconfig"
-source "drivers/staging/iio/trigger/Kconfig"
endmenu
diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile
index 455bffc29649..e99a375c07b9 100644
--- a/drivers/staging/iio/Makefile
+++ b/drivers/staging/iio/Makefile
@@ -13,4 +13,3 @@ obj-y += impedance-analyzer/
obj-y += light/
obj-y += meter/
obj-y += resolver/
-obj-y += trigger/
diff --git a/drivers/staging/iio/accel/adis16201.c b/drivers/staging/iio/accel/adis16201.c
index 2ebd27536216..0fae8aaf1cf4 100644
--- a/drivers/staging/iio/accel/adis16201.c
+++ b/drivers/staging/iio/accel/adis16201.c
@@ -7,13 +7,13 @@
*/
#include <linux/delay.h>
-#include <linux/mutex.h>
#include <linux/device.h>
#include <linux/kernel.h>
-#include <linux/spi/spi.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/slab.h>
+#include <linux/spi/spi.h>
#include <linux/sysfs.h>
-#include <linux/module.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@@ -232,6 +232,7 @@ static int adis16201_read_raw(struct iio_dev *indio_dev,
*val = val16;
return IIO_VAL_INT;
}
+
return -EINVAL;
}
@@ -262,6 +263,7 @@ static int adis16201_write_raw(struct iio_dev *indio_dev,
addr = adis16201_addresses[chan->scan_index];
return adis_write_reg_16(st, addr, val16);
}
+
return -EINVAL;
}
@@ -336,6 +338,7 @@ static int adis16201_probe(struct spi_device *spi)
ret = adis_init(st, indio_dev, spi, &adis16201_data);
if (ret)
return ret;
+
ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL);
if (ret)
return ret;
@@ -348,6 +351,7 @@ static int adis16201_probe(struct spi_device *spi)
ret = iio_device_register(indio_dev);
if (ret < 0)
goto error_cleanup_buffer_trigger;
+
return 0;
error_cleanup_buffer_trigger:
diff --git a/drivers/staging/iio/accel/adis16209.c b/drivers/staging/iio/accel/adis16209.c
index 7fcef9a2590a..72a18cfe81ee 100644
--- a/drivers/staging/iio/accel/adis16209.c
+++ b/drivers/staging/iio/accel/adis16209.c
@@ -9,147 +9,82 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
-#include <linux/list.h>
-#include <linux/module.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/imu/adis.h>
-#define ADIS16209_STARTUP_DELAY 220 /* ms */
-
-/* Flash memory write count */
-#define ADIS16209_FLASH_CNT 0x00
-
-/* Output, power supply */
-#define ADIS16209_SUPPLY_OUT 0x02
-
-/* Output, x-axis accelerometer */
-#define ADIS16209_XACCL_OUT 0x04
-
-/* Output, y-axis accelerometer */
-#define ADIS16209_YACCL_OUT 0x06
+#define ADIS16209_STARTUP_DELAY_MS 220
+#define ADIS16209_FLASH_CNT_REG 0x00
+/* Data Output Register Definitions */
+#define ADIS16209_SUPPLY_OUT_REG 0x02
+#define ADIS16209_XACCL_OUT_REG 0x04
+#define ADIS16209_YACCL_OUT_REG 0x06
/* Output, auxiliary ADC input */
-#define ADIS16209_AUX_ADC 0x08
-
+#define ADIS16209_AUX_ADC_REG 0x08
/* Output, temperature */
-#define ADIS16209_TEMP_OUT 0x0A
-
-/* Output, x-axis inclination */
-#define ADIS16209_XINCL_OUT 0x0C
-
-/* Output, y-axis inclination */
-#define ADIS16209_YINCL_OUT 0x0E
-
+#define ADIS16209_TEMP_OUT_REG 0x0A
+/* Output, +/- 90 degrees X-axis inclination */
+#define ADIS16209_XINCL_OUT_REG 0x0C
+#define ADIS16209_YINCL_OUT_REG 0x0E
/* Output, +/-180 vertical rotational position */
-#define ADIS16209_ROT_OUT 0x10
-
-/* Calibration, x-axis acceleration offset null */
-#define ADIS16209_XACCL_NULL 0x12
-
-/* Calibration, y-axis acceleration offset null */
-#define ADIS16209_YACCL_NULL 0x14
-
-/* Calibration, x-axis inclination offset null */
-#define ADIS16209_XINCL_NULL 0x16
-
-/* Calibration, y-axis inclination offset null */
-#define ADIS16209_YINCL_NULL 0x18
-
-/* Calibration, vertical rotation offset null */
-#define ADIS16209_ROT_NULL 0x1A
-
-/* Alarm 1 amplitude threshold */
-#define ADIS16209_ALM_MAG1 0x20
-
-/* Alarm 2 amplitude threshold */
-#define ADIS16209_ALM_MAG2 0x22
-
-/* Alarm 1, sample period */
-#define ADIS16209_ALM_SMPL1 0x24
-
-/* Alarm 2, sample period */
-#define ADIS16209_ALM_SMPL2 0x26
-
-/* Alarm control */
-#define ADIS16209_ALM_CTRL 0x28
-
-/* Auxiliary DAC data */
-#define ADIS16209_AUX_DAC 0x30
-
-/* General-purpose digital input/output control */
-#define ADIS16209_GPIO_CTRL 0x32
-
-/* Miscellaneous control */
-#define ADIS16209_MSC_CTRL 0x34
-
-/* Internal sample period (rate) control */
-#define ADIS16209_SMPL_PRD 0x36
-
-/* Operation, filter configuration */
-#define ADIS16209_AVG_CNT 0x38
-
-/* Operation, sleep mode control */
-#define ADIS16209_SLP_CNT 0x3A
-
-/* Diagnostics, system status register */
-#define ADIS16209_DIAG_STAT 0x3C
-
-/* Operation, system command register */
-#define ADIS16209_GLOB_CMD 0x3E
-
-/* MSC_CTRL */
-
-/* Self-test at power-on: 1 = disabled, 0 = enabled */
-#define ADIS16209_MSC_CTRL_PWRUP_SELF_TEST BIT(10)
-
-/* Self-test enable */
-#define ADIS16209_MSC_CTRL_SELF_TEST_EN BIT(8)
-
-/* Data-ready enable: 1 = enabled, 0 = disabled */
-#define ADIS16209_MSC_CTRL_DATA_RDY_EN BIT(2)
+#define ADIS16209_ROT_OUT_REG 0x10
+/*
+ * Calibration Register Definitions.
+ * Acceleration, inclination or rotation offset null.
+ */
+#define ADIS16209_XACCL_NULL_REG 0x12
+#define ADIS16209_YACCL_NULL_REG 0x14
+#define ADIS16209_XINCL_NULL_REG 0x16
+#define ADIS16209_YINCL_NULL_REG 0x18
+#define ADIS16209_ROT_NULL_REG 0x1A
+
+/* Alarm Register Definitions */
+#define ADIS16209_ALM_MAG1_REG 0x20
+#define ADIS16209_ALM_MAG2_REG 0x22
+#define ADIS16209_ALM_SMPL1_REG 0x24
+#define ADIS16209_ALM_SMPL2_REG 0x26
+#define ADIS16209_ALM_CTRL_REG 0x28
+
+#define ADIS16209_AUX_DAC_REG 0x30
+#define ADIS16209_GPIO_CTRL_REG 0x32
+#define ADIS16209_SMPL_PRD_REG 0x36
+#define ADIS16209_AVG_CNT_REG 0x38
+#define ADIS16209_SLP_CNT_REG 0x3A
+
+#define ADIS16209_MSC_CTRL_REG 0x34
+#define ADIS16209_MSC_CTRL_PWRUP_SELF_TEST BIT(10)
+#define ADIS16209_MSC_CTRL_SELF_TEST_EN BIT(8)
+#define ADIS16209_MSC_CTRL_DATA_RDY_EN BIT(2)
/* Data-ready polarity: 1 = active high, 0 = active low */
-#define ADIS16209_MSC_CTRL_ACTIVE_HIGH BIT(1)
-
-/* Data-ready line selection: 1 = DIO2, 0 = DIO1 */
-#define ADIS16209_MSC_CTRL_DATA_RDY_DIO2 BIT(0)
-
-/* DIAG_STAT */
-
-/* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */
-#define ADIS16209_DIAG_STAT_ALARM2 BIT(9)
-
-/* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */
-#define ADIS16209_DIAG_STAT_ALARM1 BIT(8)
-
-/* Self-test diagnostic error flag: 1 = error condition, 0 = normal operation */
-#define ADIS16209_DIAG_STAT_SELFTEST_FAIL_BIT 5
-
-/* SPI communications failure */
-#define ADIS16209_DIAG_STAT_SPI_FAIL_BIT 3
-
-/* Flash update failure */
-#define ADIS16209_DIAG_STAT_FLASH_UPT_BIT 2
-
+#define ADIS16209_MSC_CTRL_ACTIVE_HIGH BIT(1)
+#define ADIS16209_MSC_CTRL_DATA_RDY_DIO2 BIT(0)
+
+#define ADIS16209_STAT_REG 0x3C
+#define ADIS16209_STAT_ALARM2 BIT(9)
+#define ADIS16209_STAT_ALARM1 BIT(8)
+#define ADIS16209_STAT_SELFTEST_FAIL_BIT 5
+#define ADIS16209_STAT_SPI_FAIL_BIT 3
+#define ADIS16209_STAT_FLASH_UPT_FAIL_BIT 2
/* Power supply above 3.625 V */
-#define ADIS16209_DIAG_STAT_POWER_HIGH_BIT 1
-
+#define ADIS16209_STAT_POWER_HIGH_BIT 1
/* Power supply below 3.15 V */
-#define ADIS16209_DIAG_STAT_POWER_LOW_BIT 0
+#define ADIS16209_STAT_POWER_LOW_BIT 0
-/* GLOB_CMD */
+#define ADIS16209_CMD_REG 0x3E
+#define ADIS16209_CMD_SW_RESET BIT(7)
+#define ADIS16209_CMD_CLEAR_STAT BIT(4)
+#define ADIS16209_CMD_FACTORY_CAL BIT(1)
-#define ADIS16209_GLOB_CMD_SW_RESET BIT(7)
-#define ADIS16209_GLOB_CMD_CLEAR_STAT BIT(4)
-#define ADIS16209_GLOB_CMD_FACTORY_CAL BIT(1)
-
-#define ADIS16209_ERROR_ACTIVE BIT(14)
+#define ADIS16209_ERROR_ACTIVE BIT(14)
enum adis16209_scan {
ADIS16209_SCAN_SUPPLY,
@@ -165,10 +100,10 @@ enum adis16209_scan {
static const u8 adis16209_addresses[8][1] = {
[ADIS16209_SCAN_SUPPLY] = { },
[ADIS16209_SCAN_AUX_ADC] = { },
- [ADIS16209_SCAN_ACC_X] = { ADIS16209_XACCL_NULL },
- [ADIS16209_SCAN_ACC_Y] = { ADIS16209_YACCL_NULL },
- [ADIS16209_SCAN_INCLI_X] = { ADIS16209_XINCL_NULL },
- [ADIS16209_SCAN_INCLI_Y] = { ADIS16209_YINCL_NULL },
+ [ADIS16209_SCAN_ACC_X] = { ADIS16209_XACCL_NULL_REG },
+ [ADIS16209_SCAN_ACC_Y] = { ADIS16209_YACCL_NULL_REG },
+ [ADIS16209_SCAN_INCLI_X] = { ADIS16209_XINCL_NULL_REG },
+ [ADIS16209_SCAN_INCLI_Y] = { ADIS16209_YINCL_NULL_REG },
[ADIS16209_SCAN_ROT] = { },
[ADIS16209_SCAN_TEMP] = { },
};
@@ -220,30 +155,50 @@ static int adis16209_read_raw(struct iio_dev *indio_dev,
switch (chan->type) {
case IIO_VOLTAGE:
*val = 0;
- if (chan->channel == 0)
+ switch (chan->channel) {
+ case 0:
*val2 = 305180; /* 0.30518 mV */
- else
+ break;
+ case 1:
*val2 = 610500; /* 0.6105 mV */
+ break;
+ default:
+ return -EINVAL;
+ }
return IIO_VAL_INT_PLUS_MICRO;
case IIO_TEMP:
- *val = -470; /* -0.47 C */
+ *val = -470;
*val2 = 0;
return IIO_VAL_INT_PLUS_MICRO;
case IIO_ACCEL:
+ /*
+ * IIO base unit for sensitivity of accelerometer
+ * is milli g.
+ * 1 LSB represents 0.244 mg.
+ */
*val = 0;
- *val2 = IIO_G_TO_M_S_2(244140); /* 0.244140 mg */
+ *val2 = IIO_G_TO_M_S_2(244140);
return IIO_VAL_INT_PLUS_NANO;
case IIO_INCLI:
case IIO_ROT:
+ /*
+ * IIO base units for rotation are degrees.
+ * 1 LSB represents 0.025 milli degrees.
+ */
*val = 0;
- *val2 = 25000; /* 0.025 degree */
+ *val2 = 25000;
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
break;
case IIO_CHAN_INFO_OFFSET:
- *val = 25000 / -470 - 0x4FE; /* 25 C = 0x4FE */
+ /*
+ * The raw ADC value is 0x4FE when the temperature
+ * is 25 degrees and the scale factor per milli
+ * degree celcius is -470.
+ */
+ *val = 25000 / -470 - 0x4FE;
return IIO_VAL_INT;
case IIO_CHAN_INFO_CALIBBIAS:
switch (chan->type) {
@@ -257,27 +212,27 @@ static int adis16209_read_raw(struct iio_dev *indio_dev,
ret = adis_read_reg_16(st, addr, &val16);
if (ret)
return ret;
- val16 &= (1 << bits) - 1;
- val16 = (s16)(val16 << (16 - bits)) >> (16 - bits);
- *val = val16;
+
+ *val = sign_extend32(val16, bits - 1);
return IIO_VAL_INT;
}
return -EINVAL;
}
static const struct iio_chan_spec adis16209_channels[] = {
- ADIS_SUPPLY_CHAN(ADIS16209_SUPPLY_OUT, ADIS16209_SCAN_SUPPLY, 0, 14),
- ADIS_TEMP_CHAN(ADIS16209_TEMP_OUT, ADIS16209_SCAN_TEMP, 0, 12),
- ADIS_ACCEL_CHAN(X, ADIS16209_XACCL_OUT, ADIS16209_SCAN_ACC_X,
+ ADIS_SUPPLY_CHAN(ADIS16209_SUPPLY_OUT_REG, ADIS16209_SCAN_SUPPLY,
+ 0, 14),
+ ADIS_TEMP_CHAN(ADIS16209_TEMP_OUT_REG, ADIS16209_SCAN_TEMP, 0, 12),
+ ADIS_ACCEL_CHAN(X, ADIS16209_XACCL_OUT_REG, ADIS16209_SCAN_ACC_X,
BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14),
- ADIS_ACCEL_CHAN(Y, ADIS16209_YACCL_OUT, ADIS16209_SCAN_ACC_Y,
+ ADIS_ACCEL_CHAN(Y, ADIS16209_YACCL_OUT_REG, ADIS16209_SCAN_ACC_Y,
BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14),
- ADIS_AUX_ADC_CHAN(ADIS16209_AUX_ADC, ADIS16209_SCAN_AUX_ADC, 0, 12),
- ADIS_INCLI_CHAN(X, ADIS16209_XINCL_OUT, ADIS16209_SCAN_INCLI_X,
+ ADIS_AUX_ADC_CHAN(ADIS16209_AUX_ADC_REG, ADIS16209_SCAN_AUX_ADC, 0, 12),
+ ADIS_INCLI_CHAN(X, ADIS16209_XINCL_OUT_REG, ADIS16209_SCAN_INCLI_X,
0, 0, 14),
- ADIS_INCLI_CHAN(Y, ADIS16209_YINCL_OUT, ADIS16209_SCAN_INCLI_Y,
+ ADIS_INCLI_CHAN(Y, ADIS16209_YINCL_OUT_REG, ADIS16209_SCAN_INCLI_Y,
0, 0, 14),
- ADIS_ROT_CHAN(X, ADIS16209_ROT_OUT, ADIS16209_SCAN_ROT, 0, 0, 14),
+ ADIS_ROT_CHAN(X, ADIS16209_ROT_OUT_REG, ADIS16209_SCAN_ROT, 0, 0, 14),
IIO_CHAN_SOFT_TIMESTAMP(8)
};
@@ -288,29 +243,29 @@ static const struct iio_info adis16209_info = {
};
static const char * const adis16209_status_error_msgs[] = {
- [ADIS16209_DIAG_STAT_SELFTEST_FAIL_BIT] = "Self test failure",
- [ADIS16209_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure",
- [ADIS16209_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed",
- [ADIS16209_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V",
- [ADIS16209_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 3.15V",
+ [ADIS16209_STAT_SELFTEST_FAIL_BIT] = "Self test failure",
+ [ADIS16209_STAT_SPI_FAIL_BIT] = "SPI failure",
+ [ADIS16209_STAT_FLASH_UPT_FAIL_BIT] = "Flash update failed",
+ [ADIS16209_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V",
+ [ADIS16209_STAT_POWER_LOW_BIT] = "Power supply below 3.15V",
};
static const struct adis_data adis16209_data = {
.read_delay = 30,
- .msc_ctrl_reg = ADIS16209_MSC_CTRL,
- .glob_cmd_reg = ADIS16209_GLOB_CMD,
- .diag_stat_reg = ADIS16209_DIAG_STAT,
+ .msc_ctrl_reg = ADIS16209_MSC_CTRL_REG,
+ .glob_cmd_reg = ADIS16209_CMD_REG,
+ .diag_stat_reg = ADIS16209_STAT_REG,
.self_test_mask = ADIS16209_MSC_CTRL_SELF_TEST_EN,
.self_test_no_autoclear = true,
- .startup_delay = ADIS16209_STARTUP_DELAY,
+ .startup_delay = ADIS16209_STARTUP_DELAY_MS,
.status_error_msgs = adis16209_status_error_msgs,
- .status_error_mask = BIT(ADIS16209_DIAG_STAT_SELFTEST_FAIL_BIT) |
- BIT(ADIS16209_DIAG_STAT_SPI_FAIL_BIT) |
- BIT(ADIS16209_DIAG_STAT_FLASH_UPT_BIT) |
- BIT(ADIS16209_DIAG_STAT_POWER_HIGH_BIT) |
- BIT(ADIS16209_DIAG_STAT_POWER_LOW_BIT),
+ .status_error_mask = BIT(ADIS16209_STAT_SELFTEST_FAIL_BIT) |
+ BIT(ADIS16209_STAT_SPI_FAIL_BIT) |
+ BIT(ADIS16209_STAT_FLASH_UPT_FAIL_BIT) |
+ BIT(ADIS16209_STAT_POWER_HIGH_BIT) |
+ BIT(ADIS16209_STAT_POWER_LOW_BIT),
};
static int adis16209_probe(struct spi_device *spi)
@@ -319,12 +274,10 @@ static int adis16209_probe(struct spi_device *spi)
struct adis *st;
struct iio_dev *indio_dev;
- /* setup the industrialio driver allocated elements */
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
st = iio_priv(indio_dev);
- /* this is only used for removal purposes */
spi_set_drvdata(spi, indio_dev);
indio_dev->name = spi->dev.driver->name;
@@ -341,7 +294,6 @@ static int adis16209_probe(struct spi_device *spi)
if (ret)
return ret;
- /* Get the device into a sane initial state */
ret = adis_initial_startup(st);
if (ret)
goto error_cleanup_buffer_trigger;
diff --git a/drivers/staging/iio/adc/ad7192.c b/drivers/staging/iio/adc/ad7192.c
index 425e8b82533b..df0499fc4802 100644
--- a/drivers/staging/iio/adc/ad7192.c
+++ b/drivers/staging/iio/adc/ad7192.c
@@ -301,8 +301,12 @@ static int ad7192_setup(struct ad7192_state *st,
if (pdata->unipolar_en)
st->conf |= AD7192_CONF_UNIPOLAR;
- if (pdata->burnout_curr_en)
+ if (pdata->burnout_curr_en && pdata->buf_en && !pdata->chop_en) {
st->conf |= AD7192_CONF_BURN;
+ } else if (pdata->burnout_curr_en) {
+ dev_warn(&st->sd.spi->dev,
+ "Can't enable burnout currents: see CHOP or buffer\n");
+ }
ret = ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode);
if (ret)
diff --git a/drivers/staging/iio/adc/ad7816.c b/drivers/staging/iio/adc/ad7816.c
index bfe180a475ee..bf76a8620bdb 100644
--- a/drivers/staging/iio/adc/ad7816.c
+++ b/drivers/staging/iio/adc/ad7816.c
@@ -254,7 +254,7 @@ static const struct attribute_group ad7816_attribute_group = {
static irqreturn_t ad7816_event_handler(int irq, void *private)
{
iio_push_event(private, IIO_EVENT_CODE_AD7816_OTI,
- iio_get_time_ns((struct iio_dev *)private));
+ iio_get_time_ns(private));
return IRQ_HANDLED;
}
diff --git a/drivers/staging/iio/addac/adt7316.c b/drivers/staging/iio/addac/adt7316.c
index 2d33632c00e8..3f22d1088713 100644
--- a/drivers/staging/iio/addac/adt7316.c
+++ b/drivers/staging/iio/addac/adt7316.c
@@ -2079,9 +2079,8 @@ static int adt7316_enable(struct device *dev)
return _adt7316_store_enabled(chip, 1);
}
-
-SIMPLE_DEV_PM_OPS(adt7316_pm_ops, adt7316_disable, adt7316_enable);
EXPORT_SYMBOL_GPL(adt7316_pm_ops);
+SIMPLE_DEV_PM_OPS(adt7316_pm_ops, adt7316_disable, adt7316_enable);
#endif
static const struct iio_info adt7316_info = {
diff --git a/drivers/staging/iio/cdc/ad7150.c b/drivers/staging/iio/cdc/ad7150.c
index 2fe916c48848..d16084d7068c 100644
--- a/drivers/staging/iio/cdc/ad7150.c
+++ b/drivers/staging/iio/cdc/ad7150.c
@@ -124,8 +124,9 @@ static int ad7150_read_raw(struct iio_dev *indio_dev,
}
static int ad7150_read_event_config(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan, enum iio_event_type type,
- enum iio_event_direction dir)
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
{
int ret;
u8 threshtype;
diff --git a/drivers/staging/iio/cdc/ad7152.c b/drivers/staging/iio/cdc/ad7152.c
index 19dc896603a1..25f51db05d2d 100644
--- a/drivers/staging/iio/cdc/ad7152.c
+++ b/drivers/staging/iio/cdc/ad7152.c
@@ -426,8 +426,8 @@ out:
}
static int ad7152_write_raw_get_fmt(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- long mask)
+ struct iio_chan_spec const *chan,
+ long mask)
{
switch (mask) {
case IIO_CHAN_INFO_SCALE:
@@ -493,7 +493,7 @@ static const struct iio_chan_spec ad7152_channels[] = {
*/
static int ad7152_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+ const struct i2c_device_id *id)
{
int ret = 0;
struct ad7152_chip_info *chip;
diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c
index c4a864725376..4882dbc81c53 100644
--- a/drivers/staging/iio/cdc/ad7746.c
+++ b/drivers/staging/iio/cdc/ad7746.c
@@ -217,7 +217,7 @@ static const unsigned char ad7746_cap_filter_rate_table[][2] = {
};
static int ad7746_select_channel(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan)
+ struct iio_chan_spec const *chan)
{
struct ad7746_chip_info *chip = iio_priv(indio_dev);
int ret, delay, idx;
@@ -487,13 +487,13 @@ static int ad7746_write_raw(struct iio_dev *indio_dev,
AD7746_CAPDAC_DACP(val) | AD7746_CAPDAC_DACEN : 0;
ret = i2c_smbus_write_byte_data(chip->client,
- AD7746_REG_CAPDACA,
- chip->capdac[chan->channel][0]);
+ AD7746_REG_CAPDACA,
+ chip->capdac[chan->channel][0]);
if (ret < 0)
goto out;
ret = i2c_smbus_write_byte_data(chip->client,
- AD7746_REG_CAPDACB,
- chip->capdac[chan->channel][1]);
+ AD7746_REG_CAPDACB,
+ chip->capdac[chan->channel][1]);
if (ret < 0)
goto out;
@@ -675,7 +675,7 @@ static const struct iio_info ad7746_info = {
*/
static int ad7746_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+ const struct i2c_device_id *id)
{
struct ad7746_platform_data *pdata = client->dev.platform_data;
struct ad7746_chip_info *chip;
diff --git a/drivers/staging/iio/light/tsl2x7x.c b/drivers/staging/iio/light/tsl2x7x.c
index 126e11530ce0..82681300e106 100644
--- a/drivers/staging/iio/light/tsl2x7x.c
+++ b/drivers/staging/iio/light/tsl2x7x.c
@@ -109,15 +109,15 @@
#define TSL2X7X_CNTL_INTPROXPON_ENBL 0x2F
/*Prox diode to use */
-#define TSL2X7X_DIODE0 0x10
-#define TSL2X7X_DIODE1 0x20
-#define TSL2X7X_DIODE_BOTH 0x30
+#define TSL2X7X_DIODE0 0x01
+#define TSL2X7X_DIODE1 0x02
+#define TSL2X7X_DIODE_BOTH 0x03
/* LED Power */
#define TSL2X7X_100_mA 0x00
-#define TSL2X7X_50_mA 0x40
-#define TSL2X7X_25_mA 0x80
-#define TSL2X7X_13_mA 0xD0
+#define TSL2X7X_50_mA 0x01
+#define TSL2X7X_25_mA 0x02
+#define TSL2X7X_13_mA 0x03
#define TSL2X7X_MAX_TIMER_CNT 0xFF
#define TSL2X7X_MIN_ITIME 3
@@ -228,7 +228,7 @@ static const struct tsl2x7x_settings tsl2x7x_default_settings = {
.als_time = 219, /* 101 ms */
.als_gain = 0,
.prx_time = 254, /* 5.4 ms */
- .prox_gain = 1,
+ .prox_gain = 0,
.wait_time = 245,
.prox_config = 0,
.als_gain_trim = 1000,
@@ -240,7 +240,9 @@ static const struct tsl2x7x_settings tsl2x7x_default_settings = {
.prox_thres_low = 0,
.prox_thres_high = 512,
.prox_max_samples_cal = 30,
- .prox_pulse_count = 8
+ .prox_pulse_count = 8,
+ .prox_diode = TSL2X7X_DIODE1,
+ .prox_power = TSL2X7X_100_mA
};
static const s16 tsl2x7x_als_gain[] = {
@@ -279,6 +281,49 @@ static const u8 device_channel_config[] = {
ALSPRX2
};
+static int tsl2x7x_clear_interrupts(struct tsl2X7X_chip *chip, int reg)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte(chip->client,
+ TSL2X7X_CMD_REG | TSL2X7X_CMD_SPL_FN | reg);
+ if (ret < 0)
+ dev_err(&chip->client->dev,
+ "%s: failed to clear interrupt status %x: %d\n",
+ __func__, reg, ret);
+
+ return ret;
+}
+
+static int tsl2x7x_read_status(struct tsl2X7X_chip *chip)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(chip->client,
+ TSL2X7X_CMD_REG | TSL2X7X_STATUS);
+ if (ret < 0)
+ dev_err(&chip->client->dev,
+ "%s: failed to read STATUS register: %d\n", __func__,
+ ret);
+
+ return ret;
+}
+
+static int tsl2x7x_write_control_reg(struct tsl2X7X_chip *chip, u8 data)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(chip->client,
+ TSL2X7X_CMD_REG | TSL2X7X_CNTRL, data);
+ if (ret < 0) {
+ dev_err(&chip->client->dev,
+ "%s: failed to write to control register %x: %d\n",
+ __func__, data, ret);
+ }
+
+ return ret;
+}
+
/**
* tsl2x7x_get_lux() - Reads and calculates current lux value.
* @indio_dev: pointer to IIO device
@@ -307,8 +352,7 @@ static int tsl2x7x_get_lux(struct iio_dev *indio_dev)
u32 ch0lux = 0;
u32 ch1lux = 0;
- if (mutex_trylock(&chip->als_mutex) == 0)
- return chip->als_cur_info.lux; /* busy, so return LAST VALUE */
+ mutex_lock(&chip->als_mutex);
if (chip->tsl2x7x_chip_status != TSL2X7X_CHIP_WORKING) {
/* device is not enabled */
@@ -318,13 +362,10 @@ static int tsl2x7x_get_lux(struct iio_dev *indio_dev)
goto out_unlock;
}
- ret = i2c_smbus_read_byte_data(chip->client,
- TSL2X7X_CMD_REG | TSL2X7X_STATUS);
- if (ret < 0) {
- dev_err(&chip->client->dev,
- "%s: Failed to read STATUS Reg\n", __func__);
+ ret = tsl2x7x_read_status(chip);
+ if (ret < 0)
goto out_unlock;
- }
+
/* is data new & valid */
if (!(ret & TSL2X7X_STA_ADC_VALID)) {
dev_err(&chip->client->dev,
@@ -346,16 +387,9 @@ static int tsl2x7x_get_lux(struct iio_dev *indio_dev)
buf[i] = ret;
}
- /* clear any existing interrupt status */
- ret = i2c_smbus_write_byte(chip->client,
- TSL2X7X_CMD_REG |
- TSL2X7X_CMD_SPL_FN |
- TSL2X7X_CMD_ALS_INT_CLR);
- if (ret < 0) {
- dev_err(&chip->client->dev,
- "i2c_write_command failed - err = %d\n", ret);
- goto out_unlock; /* have no data, so return failure */
- }
+ ret = tsl2x7x_clear_interrupts(chip, TSL2X7X_CMD_ALS_INT_CLR);
+ if (ret < 0)
+ goto out_unlock;
/* extract ALS/lux data */
ch0 = le16_to_cpup((const __le16 *)&buf[0]);
@@ -445,18 +479,11 @@ static int tsl2x7x_get_prox(struct iio_dev *indio_dev)
u8 chdata[2];
struct tsl2X7X_chip *chip = iio_priv(indio_dev);
- if (mutex_trylock(&chip->prox_mutex) == 0) {
- dev_err(&chip->client->dev,
- "%s: Can't get prox mutex\n", __func__);
- return -EBUSY;
- }
+ mutex_lock(&chip->prox_mutex);
- ret = i2c_smbus_read_byte_data(chip->client,
- TSL2X7X_CMD_REG | TSL2X7X_STATUS);
- if (ret < 0) {
- dev_err(&chip->client->dev, "i2c err=%d\n", ret);
+ ret = tsl2x7x_read_status(chip);
+ if (ret < 0)
goto prox_poll_err;
- }
switch (chip->id) {
case tsl2571:
@@ -464,16 +491,20 @@ static int tsl2x7x_get_prox(struct iio_dev *indio_dev)
case tmd2671:
case tsl2771:
case tmd2771:
- if (!(ret & TSL2X7X_STA_ADC_VALID))
+ if (!(ret & TSL2X7X_STA_ADC_VALID)) {
+ ret = -EINVAL;
goto prox_poll_err;
+ }
break;
case tsl2572:
case tsl2672:
case tmd2672:
case tsl2772:
case tmd2772:
- if (!(ret & TSL2X7X_STA_PRX_VALID))
+ if (!(ret & TSL2X7X_STA_PRX_VALID)) {
+ ret = -EINVAL;
goto prox_poll_err;
+ }
break;
}
@@ -487,14 +518,13 @@ static int tsl2x7x_get_prox(struct iio_dev *indio_dev)
chdata[i] = ret;
}
- chip->prox_data =
- le16_to_cpup((const __le16 *)&chdata[0]);
+ chip->prox_data = le16_to_cpup((const __le16 *)&chdata[0]);
+ ret = chip->prox_data;
prox_poll_err:
-
mutex_unlock(&chip->prox_mutex);
- return chip->prox_data;
+ return ret;
}
/**
@@ -582,15 +612,11 @@ static int tsl2x7x_chip_on(struct iio_dev *indio_dev)
int i;
int ret = 0;
u8 *dev_reg;
- u8 utmp;
int als_count;
int als_time;
struct tsl2X7X_chip *chip = iio_priv(indio_dev);
u8 reg_val = 0;
- if (chip->pdata && chip->pdata->power_on)
- chip->pdata->power_on(indio_dev);
-
/* Non calculated parameters */
chip->tsl2x7x_config[TSL2X7X_PRX_TIME] = chip->settings.prx_time;
chip->tsl2x7x_config[TSL2X7X_WAIT_TIME] = chip->settings.wait_time;
@@ -635,9 +661,10 @@ static int tsl2x7x_chip_on(struct iio_dev *indio_dev)
/* Set the gain based on tsl2x7x_settings struct */
chip->tsl2x7x_config[TSL2X7X_GAIN] =
- chip->settings.als_gain |
- (TSL2X7X_100_mA | TSL2X7X_DIODE1) |
- (chip->settings.prox_gain << 2);
+ (chip->settings.als_gain & 0xFF) |
+ ((chip->settings.prox_gain & 0xFF) << 2) |
+ (chip->settings.prox_diode << 4) |
+ (chip->settings.prox_power << 6);
/* set chip struct re scaling and saturation */
chip->als_saturation = als_count * 922; /* 90% of full scale */
@@ -647,14 +674,9 @@ static int tsl2x7x_chip_on(struct iio_dev *indio_dev)
* TSL2X7X Specific power-on / adc enable sequence
* Power on the device 1st.
*/
- utmp = TSL2X7X_CNTL_PWR_ON;
- ret = i2c_smbus_write_byte_data(chip->client,
- TSL2X7X_CMD_REG | TSL2X7X_CNTRL, utmp);
- if (ret < 0) {
- dev_err(&chip->client->dev,
- "%s: failed on CNTRL reg.\n", __func__);
+ ret = tsl2x7x_write_control_reg(chip, TSL2X7X_CNTL_PWR_ON);
+ if (ret < 0)
return ret;
- }
/*
* Use the following shadow copy for our delay before enabling ADC.
@@ -679,16 +701,12 @@ static int tsl2x7x_chip_on(struct iio_dev *indio_dev)
* NOW enable the ADC
* initialize the desired mode of operation
*/
- utmp = TSL2X7X_CNTL_PWR_ON |
- TSL2X7X_CNTL_ADC_ENBL |
- TSL2X7X_CNTL_PROX_DET_ENBL;
- ret = i2c_smbus_write_byte_data(chip->client,
- TSL2X7X_CMD_REG | TSL2X7X_CNTRL, utmp);
- if (ret < 0) {
- dev_err(&chip->client->dev,
- "%s: failed on 2nd CTRL reg.\n", __func__);
+ ret = tsl2x7x_write_control_reg(chip,
+ TSL2X7X_CNTL_PWR_ON |
+ TSL2X7X_CNTL_ADC_ENBL |
+ TSL2X7X_CNTL_PROX_DET_ENBL);
+ if (ret < 0)
return ret;
- }
chip->tsl2x7x_chip_status = TSL2X7X_CHIP_WORKING;
@@ -701,25 +719,14 @@ static int tsl2x7x_chip_on(struct iio_dev *indio_dev)
reg_val |= TSL2X7X_CNTL_PROX_DET_ENBL;
reg_val |= chip->settings.interrupts_en;
- ret = i2c_smbus_write_byte_data(chip->client,
- TSL2X7X_CMD_REG | TSL2X7X_CNTRL,
- reg_val);
+ ret = tsl2x7x_write_control_reg(chip, reg_val);
if (ret < 0)
- dev_err(&chip->client->dev,
- "%s: failed in tsl2x7x_IOCTL_INT_SET.\n",
- __func__);
+ return ret;
- /* Clear out any initial interrupts */
- ret = i2c_smbus_write_byte(chip->client,
- TSL2X7X_CMD_REG |
- TSL2X7X_CMD_SPL_FN |
- TSL2X7X_CMD_PROXALS_INT_CLR);
- if (ret < 0) {
- dev_err(&chip->client->dev,
- "%s: Failed to clear Int status\n",
- __func__);
- return ret;
- }
+ ret = tsl2x7x_clear_interrupts(chip,
+ TSL2X7X_CMD_PROXALS_INT_CLR);
+ if (ret < 0)
+ return ret;
}
return ret;
@@ -727,19 +734,11 @@ static int tsl2x7x_chip_on(struct iio_dev *indio_dev)
static int tsl2x7x_chip_off(struct iio_dev *indio_dev)
{
- int ret;
struct tsl2X7X_chip *chip = iio_priv(indio_dev);
/* turn device off */
chip->tsl2x7x_chip_status = TSL2X7X_CHIP_SUSPENDED;
-
- ret = i2c_smbus_write_byte_data(chip->client,
- TSL2X7X_CMD_REG | TSL2X7X_CNTRL, 0x00);
-
- if (chip->pdata && chip->pdata->power_off)
- chip->pdata->power_off(chip->client);
-
- return ret;
+ return tsl2x7x_write_control_reg(chip, 0x00);
}
/**
@@ -777,7 +776,7 @@ unlock:
}
static void tsl2x7x_prox_calculate(int *data, int length,
- struct tsl2x7x_prox_stat *statP)
+ struct tsl2x7x_prox_stat *stat)
{
int i;
int sample_sum;
@@ -787,21 +786,21 @@ static void tsl2x7x_prox_calculate(int *data, int length,
length = 1;
sample_sum = 0;
- statP->min = INT_MAX;
- statP->max = INT_MIN;
+ stat->min = INT_MAX;
+ stat->max = INT_MIN;
for (i = 0; i < length; i++) {
sample_sum += data[i];
- statP->min = min(statP->min, data[i]);
- statP->max = max(statP->max, data[i]);
+ stat->min = min(stat->min, data[i]);
+ stat->max = max(stat->max, data[i]);
}
- statP->mean = sample_sum / length;
+ stat->mean = sample_sum / length;
sample_sum = 0;
for (i = 0; i < length; i++) {
- tmp = data[i] - statP->mean;
+ tmp = data[i] - stat->mean;
sample_sum += tmp * tmp;
}
- statP->stddev = int_sqrt((long)sample_sum / length);
+ stat->stddev = int_sqrt((long)sample_sum / length);
}
/**
@@ -811,12 +810,12 @@ static void tsl2x7x_prox_calculate(int *data, int length,
* Calculates a standard deviation based on the samples,
* and sets the threshold accordingly.
*/
-static void tsl2x7x_prox_cal(struct iio_dev *indio_dev)
+static int tsl2x7x_prox_cal(struct iio_dev *indio_dev)
{
int prox_history[MAX_SAMPLES_CAL + 1];
- int i;
+ int i, ret;
struct tsl2x7x_prox_stat prox_stat_data[2];
- struct tsl2x7x_prox_stat *calP;
+ struct tsl2x7x_prox_stat *cal;
struct tsl2X7X_chip *chip = iio_priv(indio_dev);
u8 tmp_irq_settings;
u8 current_state = chip->tsl2x7x_chip_status;
@@ -829,40 +828,53 @@ static void tsl2x7x_prox_cal(struct iio_dev *indio_dev)
}
/* have to stop to change settings */
- tsl2x7x_chip_off(indio_dev);
+ ret = tsl2x7x_chip_off(indio_dev);
+ if (ret < 0)
+ return ret;
/* Enable proximity detection save just in case prox not wanted yet*/
tmp_irq_settings = chip->settings.interrupts_en;
chip->settings.interrupts_en |= TSL2X7X_CNTL_PROX_INT_ENBL;
/*turn on device if not already on*/
- tsl2x7x_chip_on(indio_dev);
+ ret = tsl2x7x_chip_on(indio_dev);
+ if (ret < 0)
+ return ret;
/*gather the samples*/
for (i = 0; i < chip->settings.prox_max_samples_cal; i++) {
usleep_range(15000, 17500);
- tsl2x7x_get_prox(indio_dev);
+ ret = tsl2x7x_get_prox(indio_dev);
+ if (ret < 0)
+ return ret;
prox_history[i] = chip->prox_data;
dev_info(&chip->client->dev, "2 i=%d prox data= %d\n",
i, chip->prox_data);
}
- tsl2x7x_chip_off(indio_dev);
- calP = &prox_stat_data[PROX_STAT_CAL];
+ ret = tsl2x7x_chip_off(indio_dev);
+ if (ret < 0)
+ return ret;
+ cal = &prox_stat_data[PROX_STAT_CAL];
tsl2x7x_prox_calculate(prox_history,
- chip->settings.prox_max_samples_cal, calP);
- chip->settings.prox_thres_high = (calP->max << 1) - calP->mean;
+ chip->settings.prox_max_samples_cal, cal);
+ chip->settings.prox_thres_high = (cal->max << 1) - cal->mean;
dev_info(&chip->client->dev, " cal min=%d mean=%d max=%d\n",
- calP->min, calP->mean, calP->max);
+ cal->min, cal->mean, cal->max);
dev_info(&chip->client->dev,
"%s proximity threshold set to %d\n",
chip->client->name, chip->settings.prox_thres_high);
/* back to the way they were */
chip->settings.interrupts_en = tmp_irq_settings;
- if (current_state == TSL2X7X_CHIP_WORKING)
- tsl2x7x_chip_on(indio_dev);
+ if (current_state == TSL2X7X_CHIP_WORKING) {
+ ret = tsl2x7x_chip_on(indio_dev);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
}
static ssize_t
@@ -931,8 +943,11 @@ static ssize_t in_illuminance0_calibrate_store(struct device *dev,
if (strtobool(buf, &value))
return -EINVAL;
- if (value)
- tsl2x7x_als_calibrate(indio_dev);
+ if (value) {
+ ret = tsl2x7x_als_calibrate(indio_dev);
+ if (ret < 0)
+ return ret;
+ }
ret = tsl2x7x_invoke_change(indio_dev);
if (ret < 0)
@@ -997,8 +1012,11 @@ static ssize_t in_illuminance0_lux_table_store(struct device *dev,
return -EINVAL;
}
- if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING)
- tsl2x7x_chip_off(indio_dev);
+ if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) {
+ ret = tsl2x7x_chip_off(indio_dev);
+ if (ret < 0)
+ return ret;
+ }
/* Zero out the table */
memset(chip->tsl2x7x_device_lux, 0, sizeof(chip->tsl2x7x_device_lux));
@@ -1022,8 +1040,11 @@ static ssize_t in_proximity0_calibrate_store(struct device *dev,
if (strtobool(buf, &value))
return -EINVAL;
- if (value)
- tsl2x7x_prox_cal(indio_dev);
+ if (value) {
+ ret = tsl2x7x_prox_cal(indio_dev);
+ if (ret < 0)
+ return ret;
+ }
ret = tsl2x7x_invoke_change(indio_dev);
if (ret < 0)
@@ -1402,13 +1423,13 @@ static irqreturn_t tsl2x7x_event_handler(int irq, void *private)
struct tsl2X7X_chip *chip = iio_priv(indio_dev);
s64 timestamp = iio_get_time_ns(indio_dev);
int ret;
- u8 value;
- value = i2c_smbus_read_byte_data(chip->client,
- TSL2X7X_CMD_REG | TSL2X7X_STATUS);
+ ret = tsl2x7x_read_status(chip);
+ if (ret < 0)
+ return ret;
/* What type of interrupt do we need to process */
- if (value & TSL2X7X_STA_PRX_INTR) {
+ if (ret & TSL2X7X_STA_PRX_INTR) {
tsl2x7x_get_prox(indio_dev); /* freshen data for ABI */
iio_push_event(indio_dev,
IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY,
@@ -1418,7 +1439,7 @@ static irqreturn_t tsl2x7x_event_handler(int irq, void *private)
timestamp);
}
- if (value & TSL2X7X_STA_ALS_INTR) {
+ if (ret & TSL2X7X_STA_ALS_INTR) {
tsl2x7x_get_lux(indio_dev); /* freshen data for ABI */
iio_push_event(indio_dev,
IIO_UNMOD_EVENT_CODE(IIO_LIGHT,
@@ -1427,14 +1448,10 @@ static irqreturn_t tsl2x7x_event_handler(int irq, void *private)
IIO_EV_DIR_EITHER),
timestamp);
}
- /* Clear interrupt now that we have handled it. */
- ret = i2c_smbus_write_byte(chip->client,
- TSL2X7X_CMD_REG | TSL2X7X_CMD_SPL_FN |
- TSL2X7X_CMD_PROXALS_INT_CLR);
+
+ ret = tsl2x7x_clear_interrupts(chip, TSL2X7X_CMD_PROXALS_INT_CLR);
if (ret < 0)
- dev_err(&chip->client->dev,
- "Failed to clear irq from event handler. err = %d\n",
- ret);
+ return ret;
return IRQ_HANDLED;
}
@@ -1461,7 +1478,6 @@ static struct attribute *tsl2x7x_ALSPRX_device_attrs[] = {
&dev_attr_in_illuminance0_target_input.attr,
&dev_attr_in_illuminance0_calibrate.attr,
&dev_attr_in_illuminance0_lux_table.attr,
- &iio_const_attr_in_proximity0_calibscale_available.dev_attr.attr,
NULL
};
@@ -1792,12 +1808,6 @@ static int tsl2x7x_suspend(struct device *dev)
chip->tsl2x7x_chip_status = TSL2X7X_CHIP_SUSPENDED;
}
- if (chip->pdata && chip->pdata->platform_power) {
- pm_message_t pmm = {PM_EVENT_SUSPEND};
-
- chip->pdata->platform_power(dev, pmm);
- }
-
return ret;
}
@@ -1807,12 +1817,6 @@ static int tsl2x7x_resume(struct device *dev)
struct tsl2X7X_chip *chip = iio_priv(indio_dev);
int ret = 0;
- if (chip->pdata && chip->pdata->platform_power) {
- pm_message_t pmm = {PM_EVENT_RESUME};
-
- chip->pdata->platform_power(dev, pmm);
- }
-
if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_SUSPENDED)
ret = tsl2x7x_chip_on(indio_dev);
diff --git a/drivers/staging/iio/light/tsl2x7x.h b/drivers/staging/iio/light/tsl2x7x.h
index df00f2ec1719..28b0e7fdc9b8 100644
--- a/drivers/staging/iio/light/tsl2x7x.h
+++ b/drivers/staging/iio/light/tsl2x7x.h
@@ -21,7 +21,6 @@
#ifndef __TSL2X7X_H
#define __TSL2X7X_H
-#include <linux/pm.h>
struct tsl2x7x_lux {
unsigned int ratio;
@@ -79,6 +78,8 @@ struct tsl2x7x_settings {
int prox_thres_high;
int prox_pulse_count;
int prox_max_samples_cal;
+ int prox_diode;
+ int prox_power;
};
/**
@@ -91,9 +92,6 @@ struct tsl2x7x_settings {
*
*/
struct tsl2X7X_platform_data {
- int (*platform_power)(struct device *dev, pm_message_t);
- int (*power_on)(struct iio_dev *indio_dev);
- int (*power_off)(struct i2c_client *dev);
struct tsl2x7x_lux platform_lux_table[TSL2X7X_MAX_LUX_TABLE_SIZE];
struct tsl2x7x_settings *platform_default_settings;
};
diff --git a/drivers/staging/iio/meter/ade7753.c b/drivers/staging/iio/meter/ade7753.c
index c44eb577dc35..275e8dfff836 100644
--- a/drivers/staging/iio/meter/ade7753.c
+++ b/drivers/staging/iio/meter/ade7753.c
@@ -388,14 +388,16 @@ static IIO_DEV_ATTR_VPERIOD(0444,
ade7753_read_16bit,
NULL,
ADE7753_PERIOD);
-static IIO_DEV_ATTR_CH_OFF(1, 0644,
- ade7753_read_8bit,
- ade7753_write_8bit,
- ADE7753_CH1OS);
-static IIO_DEV_ATTR_CH_OFF(2, 0644,
- ade7753_read_8bit,
- ade7753_write_8bit,
- ADE7753_CH2OS);
+
+static IIO_DEVICE_ATTR(choff_1, 0644,
+ ade7753_read_8bit,
+ ade7753_write_8bit,
+ ADE7753_CH1OS);
+
+static IIO_DEVICE_ATTR(choff_2, 0644,
+ ade7753_read_8bit,
+ ade7753_write_8bit,
+ ADE7753_CH2OS);
static int ade7753_set_irq(struct device *dev, bool enable)
{
diff --git a/drivers/staging/iio/meter/ade7754.c b/drivers/staging/iio/meter/ade7754.c
index 3a1e342d75fb..9aa067736715 100644
--- a/drivers/staging/iio/meter/ade7754.c
+++ b/drivers/staging/iio/meter/ade7754.c
@@ -132,7 +132,7 @@ static int ade7754_spi_write_reg_8(struct device *dev, u8 reg_address, u8 val)
}
static int ade7754_spi_write_reg_16(struct device *dev,
- u8 reg_address, u16 value)
+ u8 reg_address, u16 val)
{
int ret;
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
@@ -140,8 +140,8 @@ static int ade7754_spi_write_reg_16(struct device *dev,
mutex_lock(&st->buf_lock);
st->tx[0] = ADE7754_WRITE_REG(reg_address);
- st->tx[1] = (value >> 8) & 0xFF;
- st->tx[2] = value & 0xFF;
+ st->tx[1] = (val >> 8) & 0xFF;
+ st->tx[2] = val & 0xFF;
ret = spi_write(st->us, st->tx, 3);
mutex_unlock(&st->buf_lock);
diff --git a/drivers/staging/iio/meter/ade7758.h b/drivers/staging/iio/meter/ade7758.h
index 6ae78d8aa24f..2de81b53e786 100644
--- a/drivers/staging/iio/meter/ade7758.h
+++ b/drivers/staging/iio/meter/ade7758.h
@@ -111,7 +111,7 @@
* @trig: data ready trigger registered with iio
* @tx: transmit buffer
* @rx: receive buffer
- * @buf_lock: mutex to protect tx and rx
+ * @buf_lock: mutex to protect tx, rx, read and write frequency
**/
struct ade7758_state {
struct spi_device *us;
diff --git a/drivers/staging/iio/meter/ade7758_core.c b/drivers/staging/iio/meter/ade7758_core.c
index 7b7ffe5ed186..4e0dbf5c5705 100644
--- a/drivers/staging/iio/meter/ade7758_core.c
+++ b/drivers/staging/iio/meter/ade7758_core.c
@@ -24,17 +24,25 @@
#include "meter.h"
#include "ade7758.h"
-int ade7758_spi_write_reg_8(struct device *dev, u8 reg_address, u8 val)
+static int __ade7758_spi_write_reg_8(struct device *dev, u8 reg_address, u8 val)
{
- int ret;
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ade7758_state *st = iio_priv(indio_dev);
- mutex_lock(&st->buf_lock);
st->tx[0] = ADE7758_WRITE_REG(reg_address);
st->tx[1] = val;
- ret = spi_write(st->us, st->tx, 2);
+ return spi_write(st->us, st->tx, 2);
+}
+
+int ade7758_spi_write_reg_8(struct device *dev, u8 reg_address, u8 val)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7758_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&st->buf_lock);
+ ret = __ade7758_spi_write_reg_8(dev, reg_address, val);
mutex_unlock(&st->buf_lock);
return ret;
@@ -91,7 +99,7 @@ static int ade7758_spi_write_reg_24(struct device *dev, u8 reg_address,
return ret;
}
-int ade7758_spi_read_reg_8(struct device *dev, u8 reg_address, u8 *val)
+static int __ade7758_spi_read_reg_8(struct device *dev, u8 reg_address, u8 *val)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ade7758_state *st = iio_priv(indio_dev);
@@ -111,7 +119,6 @@ int ade7758_spi_read_reg_8(struct device *dev, u8 reg_address, u8 *val)
},
};
- mutex_lock(&st->buf_lock);
st->tx[0] = ADE7758_READ_REG(reg_address);
st->tx[1] = 0;
@@ -124,7 +131,19 @@ int ade7758_spi_read_reg_8(struct device *dev, u8 reg_address, u8 *val)
*val = st->rx[0];
error_ret:
+ return ret;
+}
+
+int ade7758_spi_read_reg_8(struct device *dev, u8 reg_address, u8 *val)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7758_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->buf_lock);
+ ret = __ade7758_spi_read_reg_8(dev, reg_address, val);
mutex_unlock(&st->buf_lock);
+
return ret;
}
@@ -484,6 +503,8 @@ static int ade7758_write_samp_freq(struct device *dev, int val)
{
int ret;
u8 reg, t;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7758_state *st = iio_priv(indio_dev);
switch (val) {
case 26040:
@@ -499,20 +520,23 @@ static int ade7758_write_samp_freq(struct device *dev, int val)
t = 3;
break;
default:
- ret = -EINVAL;
- goto out;
+ return -EINVAL;
}
- ret = ade7758_spi_read_reg_8(dev, ADE7758_WAVMODE, &reg);
+ mutex_lock(&st->buf_lock);
+
+ ret = __ade7758_spi_read_reg_8(dev, ADE7758_WAVMODE, &reg);
if (ret)
goto out;
reg &= ~(5 << 3);
reg |= t << 5;
- ret = ade7758_spi_write_reg_8(dev, ADE7758_WAVMODE, reg);
+ ret = __ade7758_spi_write_reg_8(dev, ADE7758_WAVMODE, reg);
out:
+ mutex_unlock(&st->buf_lock);
+
return ret;
}
@@ -526,9 +550,9 @@ static int ade7758_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
- mutex_lock(&indio_dev->mlock);
+
ret = ade7758_read_samp_freq(&indio_dev->dev, val);
- mutex_unlock(&indio_dev->mlock);
+
return ret;
default:
return -EINVAL;
@@ -547,9 +571,9 @@ static int ade7758_write_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_SAMP_FREQ:
if (val2)
return -EINVAL;
- mutex_lock(&indio_dev->mlock);
+
ret = ade7758_write_samp_freq(&indio_dev->dev, val);
- mutex_unlock(&indio_dev->mlock);
+
return ret;
default:
return -EINVAL;
diff --git a/drivers/staging/iio/meter/ade7758_trigger.c b/drivers/staging/iio/meter/ade7758_trigger.c
index 1f0d1a0cf889..4f6b338cffeb 100644
--- a/drivers/staging/iio/meter/ade7758_trigger.c
+++ b/drivers/staging/iio/meter/ade7758_trigger.c
@@ -30,11 +30,11 @@ static irqreturn_t ade7758_data_rdy_trig_poll(int irq, void *private)
* ade7758_data_rdy_trigger_set_state() set datardy interrupt state
**/
static int ade7758_data_rdy_trigger_set_state(struct iio_trigger *trig,
- bool state)
+ bool state)
{
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
- dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state);
+ dev_dbg(&indio_dev->dev, "(%d)\n", state);
return ade7758_set_irq(&indio_dev->dev, state);
}
@@ -63,8 +63,8 @@ int ade7758_probe_trigger(struct iio_dev *indio_dev)
int ret;
st->trig = iio_trigger_alloc("%s-dev%d",
- spi_get_device_id(st->us)->name,
- indio_dev->id);
+ spi_get_device_id(st->us)->name,
+ indio_dev->id);
if (!st->trig) {
ret = -ENOMEM;
goto error_ret;
diff --git a/drivers/staging/iio/meter/ade7759.c b/drivers/staging/iio/meter/ade7759.c
index d99cf508d8d0..c078b770fa53 100644
--- a/drivers/staging/iio/meter/ade7759.c
+++ b/drivers/staging/iio/meter/ade7759.c
@@ -72,8 +72,8 @@ struct ade7759_state {
};
static int ade7759_spi_write_reg_8(struct device *dev,
- u8 reg_address,
- u8 val)
+ u8 reg_address,
+ u8 val)
{
int ret;
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
@@ -91,8 +91,8 @@ static int ade7759_spi_write_reg_8(struct device *dev,
/*Unlocked version of ade7759_spi_write_reg_16 function */
static int __ade7759_spi_write_reg_16(struct device *dev,
- u8 reg_address,
- u16 value)
+ u8 reg_address,
+ u16 value)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ade7759_state *st = iio_priv(indio_dev);
@@ -104,8 +104,8 @@ static int __ade7759_spi_write_reg_16(struct device *dev,
}
static int ade7759_spi_write_reg_16(struct device *dev,
- u8 reg_address,
- u16 value)
+ u8 reg_address,
+ u16 value)
{
int ret;
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
@@ -119,8 +119,8 @@ static int ade7759_spi_write_reg_16(struct device *dev,
}
static int ade7759_spi_read_reg_8(struct device *dev,
- u8 reg_address,
- u8 *val)
+ u8 reg_address,
+ u8 *val)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ade7759_state *st = iio_priv(indio_dev);
@@ -128,8 +128,9 @@ static int ade7759_spi_read_reg_8(struct device *dev,
ret = spi_w8r8(st->us, ADE7759_READ_REG(reg_address));
if (ret < 0) {
- dev_err(&st->us->dev, "problem when reading 8 bit register 0x%02X",
- reg_address);
+ dev_err(&st->us->dev,
+ "problem when reading 8 bit register 0x%02X",
+ reg_address);
return ret;
}
*val = ret;
@@ -138,8 +139,8 @@ static int ade7759_spi_read_reg_8(struct device *dev,
}
static int ade7759_spi_read_reg_16(struct device *dev,
- u8 reg_address,
- u16 *val)
+ u8 reg_address,
+ u16 *val)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ade7759_state *st = iio_priv(indio_dev);
@@ -158,8 +159,8 @@ static int ade7759_spi_read_reg_16(struct device *dev,
}
static int ade7759_spi_read_reg_40(struct device *dev,
- u8 reg_address,
- u64 *val)
+ u8 reg_address,
+ u64 *val)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ade7759_state *st = iio_priv(indio_dev);
@@ -179,8 +180,9 @@ static int ade7759_spi_read_reg_40(struct device *dev,
ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));
if (ret) {
- dev_err(&st->us->dev, "problem when reading 40 bit register 0x%02X",
- reg_address);
+ dev_err(&st->us->dev,
+ "problem when reading 40 bit register 0x%02X",
+ reg_address);
goto error_ret;
}
*val = ((u64)st->rx[1] << 32) | ((u64)st->rx[2] << 24) |
@@ -192,8 +194,8 @@ error_ret:
}
static ssize_t ade7759_read_8bit(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+ struct device_attribute *attr,
+ char *buf)
{
int ret;
u8 val = 0;
@@ -207,8 +209,8 @@ static ssize_t ade7759_read_8bit(struct device *dev,
}
static ssize_t ade7759_read_16bit(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+ struct device_attribute *attr,
+ char *buf)
{
int ret;
u16 val = 0;
@@ -222,8 +224,8 @@ static ssize_t ade7759_read_16bit(struct device *dev,
}
static ssize_t ade7759_read_40bit(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+ struct device_attribute *attr,
+ char *buf)
{
int ret;
u64 val = 0;
@@ -237,9 +239,9 @@ static ssize_t ade7759_read_40bit(struct device *dev,
}
static ssize_t ade7759_write_8bit(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t len)
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
{
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
int ret;
@@ -255,9 +257,9 @@ error_ret:
}
static ssize_t ade7759_write_16bit(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t len)
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
{
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
int ret;
@@ -277,9 +279,7 @@ static int ade7759_reset(struct device *dev)
int ret;
u16 val;
- ret = ade7759_spi_read_reg_16(dev,
- ADE7759_MODE,
- &val);
+ ret = ade7759_spi_read_reg_16(dev, ADE7759_MODE, &val);
if (ret < 0)
return ret;
@@ -328,14 +328,16 @@ static IIO_DEV_ATTR_ACTIVE_POWER_GAIN(0644,
ade7759_read_16bit,
ade7759_write_16bit,
ADE7759_APGAIN);
-static IIO_DEV_ATTR_CH_OFF(1, 0644,
- ade7759_read_8bit,
- ade7759_write_8bit,
- ADE7759_CH1OS);
-static IIO_DEV_ATTR_CH_OFF(2, 0644,
- ade7759_read_8bit,
- ade7759_write_8bit,
- ADE7759_CH2OS);
+
+static IIO_DEVICE_ATTR(choff_1, 0644,
+ ade7759_read_8bit,
+ ade7759_write_8bit,
+ ADE7759_CH1OS);
+
+static IIO_DEVICE_ATTR(choff_2, 0644,
+ ade7759_read_8bit,
+ ade7759_write_8bit,
+ ADE7759_CH2OS);
static int ade7759_set_irq(struct device *dev, bool enable)
{
@@ -365,9 +367,7 @@ static int ade7759_stop_device(struct device *dev)
int ret;
u16 val;
- ret = ade7759_spi_read_reg_16(dev,
- ADE7759_MODE,
- &val);
+ ret = ade7759_spi_read_reg_16(dev, ADE7759_MODE, &val);
if (ret < 0) {
dev_err(dev, "unable to power down the device, error: %d\n",
ret);
@@ -404,16 +404,14 @@ err_ret:
}
static ssize_t ade7759_read_frequency(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+ struct device_attribute *attr,
+ char *buf)
{
int ret;
u16 t;
int sps;
- ret = ade7759_spi_read_reg_16(dev,
- ADE7759_MODE,
- &t);
+ ret = ade7759_spi_read_reg_16(dev, ADE7759_MODE, &t);
if (ret)
return ret;
@@ -424,9 +422,9 @@ static ssize_t ade7759_read_frequency(struct device *dev,
}
static ssize_t ade7759_write_frequency(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t len)
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ade7759_state *st = iio_priv(indio_dev);
diff --git a/drivers/staging/iio/meter/ade7854-i2c.c b/drivers/staging/iio/meter/ade7854-i2c.c
index 8106f8cceeab..317e4f0d8176 100644
--- a/drivers/staging/iio/meter/ade7854-i2c.c
+++ b/drivers/staging/iio/meter/ade7854-i2c.c
@@ -17,7 +17,7 @@
static int ade7854_i2c_write_reg_8(struct device *dev,
u16 reg_address,
- u8 value)
+ u8 val)
{
int ret;
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
@@ -26,7 +26,7 @@ static int ade7854_i2c_write_reg_8(struct device *dev,
mutex_lock(&st->buf_lock);
st->tx[0] = (reg_address >> 8) & 0xFF;
st->tx[1] = reg_address & 0xFF;
- st->tx[2] = value;
+ st->tx[2] = val;
ret = i2c_master_send(st->i2c, st->tx, 3);
mutex_unlock(&st->buf_lock);
@@ -36,7 +36,7 @@ static int ade7854_i2c_write_reg_8(struct device *dev,
static int ade7854_i2c_write_reg_16(struct device *dev,
u16 reg_address,
- u16 value)
+ u16 val)
{
int ret;
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
@@ -45,8 +45,8 @@ static int ade7854_i2c_write_reg_16(struct device *dev,
mutex_lock(&st->buf_lock);
st->tx[0] = (reg_address >> 8) & 0xFF;
st->tx[1] = reg_address & 0xFF;
- st->tx[2] = (value >> 8) & 0xFF;
- st->tx[3] = value & 0xFF;
+ st->tx[2] = (val >> 8) & 0xFF;
+ st->tx[3] = val & 0xFF;
ret = i2c_master_send(st->i2c, st->tx, 4);
mutex_unlock(&st->buf_lock);
@@ -56,7 +56,7 @@ static int ade7854_i2c_write_reg_16(struct device *dev,
static int ade7854_i2c_write_reg_24(struct device *dev,
u16 reg_address,
- u32 value)
+ u32 val)
{
int ret;
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
@@ -65,9 +65,9 @@ static int ade7854_i2c_write_reg_24(struct device *dev,
mutex_lock(&st->buf_lock);
st->tx[0] = (reg_address >> 8) & 0xFF;
st->tx[1] = reg_address & 0xFF;
- st->tx[2] = (value >> 16) & 0xFF;
- st->tx[3] = (value >> 8) & 0xFF;
- st->tx[4] = value & 0xFF;
+ st->tx[2] = (val >> 16) & 0xFF;
+ st->tx[3] = (val >> 8) & 0xFF;
+ st->tx[4] = val & 0xFF;
ret = i2c_master_send(st->i2c, st->tx, 5);
mutex_unlock(&st->buf_lock);
@@ -77,7 +77,7 @@ static int ade7854_i2c_write_reg_24(struct device *dev,
static int ade7854_i2c_write_reg_32(struct device *dev,
u16 reg_address,
- u32 value)
+ u32 val)
{
int ret;
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
@@ -86,10 +86,10 @@ static int ade7854_i2c_write_reg_32(struct device *dev,
mutex_lock(&st->buf_lock);
st->tx[0] = (reg_address >> 8) & 0xFF;
st->tx[1] = reg_address & 0xFF;
- st->tx[2] = (value >> 24) & 0xFF;
- st->tx[3] = (value >> 16) & 0xFF;
- st->tx[4] = (value >> 8) & 0xFF;
- st->tx[5] = value & 0xFF;
+ st->tx[2] = (val >> 24) & 0xFF;
+ st->tx[3] = (val >> 16) & 0xFF;
+ st->tx[4] = (val >> 8) & 0xFF;
+ st->tx[5] = val & 0xFF;
ret = i2c_master_send(st->i2c, st->tx, 6);
mutex_unlock(&st->buf_lock);
diff --git a/drivers/staging/iio/meter/ade7854-spi.c b/drivers/staging/iio/meter/ade7854-spi.c
index 63e200ffd1f2..4419b8f06197 100644
--- a/drivers/staging/iio/meter/ade7854-spi.c
+++ b/drivers/staging/iio/meter/ade7854-spi.c
@@ -16,8 +16,8 @@
#include "ade7854.h"
static int ade7854_spi_write_reg_8(struct device *dev,
- u16 reg_address,
- u8 value)
+ u16 reg_address,
+ u8 val)
{
int ret;
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
@@ -32,7 +32,7 @@ static int ade7854_spi_write_reg_8(struct device *dev,
st->tx[0] = ADE7854_WRITE_REG;
st->tx[1] = (reg_address >> 8) & 0xFF;
st->tx[2] = reg_address & 0xFF;
- st->tx[3] = value & 0xFF;
+ st->tx[3] = val & 0xFF;
ret = spi_sync_transfer(st->spi, &xfer, 1);
mutex_unlock(&st->buf_lock);
@@ -41,8 +41,8 @@ static int ade7854_spi_write_reg_8(struct device *dev,
}
static int ade7854_spi_write_reg_16(struct device *dev,
- u16 reg_address,
- u16 value)
+ u16 reg_address,
+ u16 val)
{
int ret;
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
@@ -57,8 +57,8 @@ static int ade7854_spi_write_reg_16(struct device *dev,
st->tx[0] = ADE7854_WRITE_REG;
st->tx[1] = (reg_address >> 8) & 0xFF;
st->tx[2] = reg_address & 0xFF;
- st->tx[3] = (value >> 8) & 0xFF;
- st->tx[4] = value & 0xFF;
+ st->tx[3] = (val >> 8) & 0xFF;
+ st->tx[4] = val & 0xFF;
ret = spi_sync_transfer(st->spi, &xfer, 1);
mutex_unlock(&st->buf_lock);
@@ -67,8 +67,8 @@ static int ade7854_spi_write_reg_16(struct device *dev,
}
static int ade7854_spi_write_reg_24(struct device *dev,
- u16 reg_address,
- u32 value)
+ u16 reg_address,
+ u32 val)
{
int ret;
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
@@ -83,9 +83,9 @@ static int ade7854_spi_write_reg_24(struct device *dev,
st->tx[0] = ADE7854_WRITE_REG;
st->tx[1] = (reg_address >> 8) & 0xFF;
st->tx[2] = reg_address & 0xFF;
- st->tx[3] = (value >> 16) & 0xFF;
- st->tx[4] = (value >> 8) & 0xFF;
- st->tx[5] = value & 0xFF;
+ st->tx[3] = (val >> 16) & 0xFF;
+ st->tx[4] = (val >> 8) & 0xFF;
+ st->tx[5] = val & 0xFF;
ret = spi_sync_transfer(st->spi, &xfer, 1);
mutex_unlock(&st->buf_lock);
@@ -94,8 +94,8 @@ static int ade7854_spi_write_reg_24(struct device *dev,
}
static int ade7854_spi_write_reg_32(struct device *dev,
- u16 reg_address,
- u32 value)
+ u16 reg_address,
+ u32 val)
{
int ret;
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
@@ -110,10 +110,10 @@ static int ade7854_spi_write_reg_32(struct device *dev,
st->tx[0] = ADE7854_WRITE_REG;
st->tx[1] = (reg_address >> 8) & 0xFF;
st->tx[2] = reg_address & 0xFF;
- st->tx[3] = (value >> 24) & 0xFF;
- st->tx[4] = (value >> 16) & 0xFF;
- st->tx[5] = (value >> 8) & 0xFF;
- st->tx[6] = value & 0xFF;
+ st->tx[3] = (val >> 24) & 0xFF;
+ st->tx[4] = (val >> 16) & 0xFF;
+ st->tx[5] = (val >> 8) & 0xFF;
+ st->tx[6] = val & 0xFF;
ret = spi_sync_transfer(st->spi, &xfer, 1);
mutex_unlock(&st->buf_lock);
@@ -122,8 +122,8 @@ static int ade7854_spi_write_reg_32(struct device *dev,
}
static int ade7854_spi_read_reg_8(struct device *dev,
- u16 reg_address,
- u8 *val)
+ u16 reg_address,
+ u8 *val)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ade7854_state *st = iio_priv(indio_dev);
@@ -149,7 +149,7 @@ static int ade7854_spi_read_reg_8(struct device *dev,
ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
if (ret) {
dev_err(&st->spi->dev, "problem when reading 8 bit register 0x%02X",
- reg_address);
+ reg_address);
goto error_ret;
}
*val = st->rx[0];
@@ -160,8 +160,8 @@ error_ret:
}
static int ade7854_spi_read_reg_16(struct device *dev,
- u16 reg_address,
- u16 *val)
+ u16 reg_address,
+ u16 *val)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ade7854_state *st = iio_priv(indio_dev);
@@ -186,7 +186,7 @@ static int ade7854_spi_read_reg_16(struct device *dev,
ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
if (ret) {
dev_err(&st->spi->dev, "problem when reading 16 bit register 0x%02X",
- reg_address);
+ reg_address);
goto error_ret;
}
*val = be16_to_cpup((const __be16 *)st->rx);
@@ -197,8 +197,8 @@ error_ret:
}
static int ade7854_spi_read_reg_24(struct device *dev,
- u16 reg_address,
- u32 *val)
+ u16 reg_address,
+ u32 *val)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ade7854_state *st = iio_priv(indio_dev);
@@ -224,7 +224,7 @@ static int ade7854_spi_read_reg_24(struct device *dev,
ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
if (ret) {
dev_err(&st->spi->dev, "problem when reading 24 bit register 0x%02X",
- reg_address);
+ reg_address);
goto error_ret;
}
*val = (st->rx[0] << 16) | (st->rx[1] << 8) | st->rx[2];
@@ -235,8 +235,8 @@ error_ret:
}
static int ade7854_spi_read_reg_32(struct device *dev,
- u16 reg_address,
- u32 *val)
+ u16 reg_address,
+ u32 *val)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ade7854_state *st = iio_priv(indio_dev);
@@ -262,7 +262,7 @@ static int ade7854_spi_read_reg_32(struct device *dev,
ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
if (ret) {
dev_err(&st->spi->dev, "problem when reading 32 bit register 0x%02X",
- reg_address);
+ reg_address);
goto error_ret;
}
*val = be32_to_cpup((const __be32 *)st->rx);
diff --git a/drivers/staging/iio/meter/ade7854.h b/drivers/staging/iio/meter/ade7854.h
index c27247a7891a..a82d38224cbd 100644
--- a/drivers/staging/iio/meter/ade7854.h
+++ b/drivers/staging/iio/meter/ade7854.h
@@ -152,20 +152,20 @@
* @rx: receive buffer
**/
struct ade7854_state {
- struct spi_device *spi;
- struct i2c_client *i2c;
- int (*read_reg_8)(struct device *, u16, u8 *);
- int (*read_reg_16)(struct device *, u16, u16 *);
- int (*read_reg_24)(struct device *, u16, u32 *);
- int (*read_reg_32)(struct device *, u16, u32 *);
- int (*write_reg_8)(struct device *, u16, u8);
- int (*write_reg_16)(struct device *, u16, u16);
- int (*write_reg_24)(struct device *, u16, u32);
- int (*write_reg_32)(struct device *, u16, u32);
- int irq;
- struct mutex buf_lock;
- u8 tx[ADE7854_MAX_TX] ____cacheline_aligned;
- u8 rx[ADE7854_MAX_RX];
+ struct spi_device *spi;
+ struct i2c_client *i2c;
+ int (*read_reg_8)(struct device *dev, u16 reg_address, u8 *val);
+ int (*read_reg_16)(struct device *dev, u16 reg_address, u16 *val);
+ int (*read_reg_24)(struct device *dev, u16 reg_address, u32 *val);
+ int (*read_reg_32)(struct device *dev, u16 reg_address, u32 *val);
+ int (*write_reg_8)(struct device *dev, u16 reg_address, u8 val);
+ int (*write_reg_16)(struct device *dev, u16 reg_address, u16 val);
+ int (*write_reg_24)(struct device *dev, u16 reg_address, u32 val);
+ int (*write_reg_32)(struct device *dev, u16 reg_address, u32 val);
+ int irq;
+ struct mutex buf_lock;
+ u8 tx[ADE7854_MAX_TX] ____cacheline_aligned;
+ u8 rx[ADE7854_MAX_RX];
};
diff --git a/drivers/staging/iio/meter/meter.h b/drivers/staging/iio/meter/meter.h
index edf26302fa57..5ed59bf30a25 100644
--- a/drivers/staging/iio/meter/meter.h
+++ b/drivers/staging/iio/meter/meter.h
@@ -348,9 +348,6 @@
#define IIO_DEV_ATTR_VPERIOD(_mode, _show, _store, _addr) \
IIO_DEVICE_ATTR(vperiod, _mode, _show, _store, _addr)
-#define IIO_DEV_ATTR_CH_OFF(_num, _mode, _show, _store, _addr) \
- IIO_DEVICE_ATTR(choff_##_num, _mode, _show, _store, _addr)
-
/* active energy register, AENERGY, is more than half full */
#define IIO_EVENT_ATTR_AENERGY_HALF_FULL(_evlist, _show, _store, _mask) \
IIO_EVENT_ATTR_SH(aenergy_half_full, _evlist, _show, _store, _mask)
diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c
index f8baab061eba..ac13b99bd9cb 100644
--- a/drivers/staging/iio/resolver/ad2s1210.c
+++ b/drivers/staging/iio/resolver/ad2s1210.c
@@ -35,8 +35,6 @@
#define AD2S1210_SET_RES1 0x02
#define AD2S1210_SET_RES0 0x01
-#define AD2S1210_SET_ENRESOLUTION (AD2S1210_SET_ENRES1 | \
- AD2S1210_SET_ENRES0)
#define AD2S1210_SET_RESOLUTION (AD2S1210_SET_RES1 | AD2S1210_SET_RES0)
#define AD2S1210_REG_POSITION 0x80
@@ -53,10 +51,6 @@
#define AD2S1210_REG_SOFT_RESET 0xF0
#define AD2S1210_REG_FAULT 0xFF
-/* pin SAMPLE, A0, A1, RES0, RES1, is controlled by driver */
-#define AD2S1210_SAA 3
-#define AD2S1210_PN (AD2S1210_SAA + AD2S1210_RES)
-
#define AD2S1210_MIN_CLKIN 6144000
#define AD2S1210_MAX_CLKIN 10240000
#define AD2S1210_MIN_EXCIT 2000
@@ -64,10 +58,6 @@
#define AD2S1210_MIN_FCW 0x4
#define AD2S1210_MAX_FCW 0x50
-/* default input clock on serial interface */
-#define AD2S1210_DEF_CLKIN 8192000
-/* clock period in nano second */
-#define AD2S1210_DEF_TCK (1000000000 / AD2S1210_DEF_CLKIN)
#define AD2S1210_DEF_EXCIT 10000
enum ad2s1210_mode {
@@ -86,7 +76,6 @@ struct ad2s1210_state {
unsigned int fclkin;
unsigned int fexcit;
bool hysteresis;
- bool old_data;
u8 resolution;
enum ad2s1210_mode mode;
u8 rx[2] ____cacheline_aligned;
@@ -117,7 +106,6 @@ static int ad2s1210_config_write(struct ad2s1210_state *st, u8 data)
ret = spi_write(st->sdev, st->tx, 1);
if (ret < 0)
return ret;
- st->old_data = true;
return 0;
}
@@ -139,7 +127,6 @@ static int ad2s1210_config_read(struct ad2s1210_state *st,
ret = spi_sync_transfer(st->sdev, &xfer, 1);
if (ret < 0)
return ret;
- st->old_data = true;
return st->rx[1];
}
@@ -165,9 +152,10 @@ int ad2s1210_update_frequency_control_word(struct ad2s1210_state *st)
static unsigned char ad2s1210_read_resolution_pin(struct ad2s1210_state *st)
{
- return ad2s1210_resolution_value[
- (gpio_get_value(st->pdata->res[0]) << 1) |
- gpio_get_value(st->pdata->res[1])];
+ int resolution = (gpio_get_value(st->pdata->res[0]) << 1) |
+ gpio_get_value(st->pdata->res[1]);
+
+ return ad2s1210_resolution_value[resolution];
}
static const int ad2s1210_res_pins[4][2] = {
diff --git a/drivers/staging/iio/trigger/Kconfig b/drivers/staging/iio/trigger/Kconfig
deleted file mode 100644
index 0b01d24cea51..000000000000
--- a/drivers/staging/iio/trigger/Kconfig
+++ /dev/null
@@ -1,19 +0,0 @@
- #
-# Industrial I/O standalone triggers
-#
-comment "Triggers - standalone"
-
-if IIO_TRIGGER
-
-config IIO_BFIN_TMR_TRIGGER
- tristate "Blackfin TIMER trigger"
- depends on BLACKFIN
- select BFIN_GPTIMERS
- help
- Provides support for using a Blackfin timer as IIO triggers.
- If unsure, say N (but it's safe to say "Y").
-
- To compile this driver as a module, choose M here: the
- module will be called iio-trig-bfin-timer.
-
-endif # IIO_TRIGGER
diff --git a/drivers/staging/iio/trigger/Makefile b/drivers/staging/iio/trigger/Makefile
deleted file mode 100644
index 1300a21363db..000000000000
--- a/drivers/staging/iio/trigger/Makefile
+++ /dev/null
@@ -1,5 +0,0 @@
-#
-# Makefile for triggers not associated with iio-devices
-#
-
-obj-$(CONFIG_IIO_BFIN_TMR_TRIGGER) += iio-trig-bfin-timer.o
diff --git a/drivers/staging/iio/trigger/iio-trig-bfin-timer.c b/drivers/staging/iio/trigger/iio-trig-bfin-timer.c
deleted file mode 100644
index 71f11d7472c0..000000000000
--- a/drivers/staging/iio/trigger/iio-trig-bfin-timer.c
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * Copyright 2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/delay.h>
-
-#include <asm/gptimers.h>
-#include <asm/portmux.h>
-
-#include <linux/iio/iio.h>
-#include <linux/iio/trigger.h>
-
-#include "iio-trig-bfin-timer.h"
-
-struct bfin_timer {
- unsigned short id, bit;
- unsigned long irqbit;
- int irq;
- int pin;
-};
-
-/*
- * this covers all hardware timer configurations on
- * all Blackfin derivatives out there today
- */
-
-static struct bfin_timer iio_bfin_timer_code[MAX_BLACKFIN_GPTIMERS] = {
- {TIMER0_id, TIMER0bit, TIMER_STATUS_TIMIL0, IRQ_TIMER0, P_TMR0},
- {TIMER1_id, TIMER1bit, TIMER_STATUS_TIMIL1, IRQ_TIMER1, P_TMR1},
- {TIMER2_id, TIMER2bit, TIMER_STATUS_TIMIL2, IRQ_TIMER2, P_TMR2},
-#if (MAX_BLACKFIN_GPTIMERS > 3)
- {TIMER3_id, TIMER3bit, TIMER_STATUS_TIMIL3, IRQ_TIMER3, P_TMR3},
- {TIMER4_id, TIMER4bit, TIMER_STATUS_TIMIL4, IRQ_TIMER4, P_TMR4},
- {TIMER5_id, TIMER5bit, TIMER_STATUS_TIMIL5, IRQ_TIMER5, P_TMR5},
- {TIMER6_id, TIMER6bit, TIMER_STATUS_TIMIL6, IRQ_TIMER6, P_TMR6},
- {TIMER7_id, TIMER7bit, TIMER_STATUS_TIMIL7, IRQ_TIMER7, P_TMR7},
-#endif
-#if (MAX_BLACKFIN_GPTIMERS > 8)
- {TIMER8_id, TIMER8bit, TIMER_STATUS_TIMIL8, IRQ_TIMER8, P_TMR8},
- {TIMER9_id, TIMER9bit, TIMER_STATUS_TIMIL9, IRQ_TIMER9, P_TMR9},
- {TIMER10_id, TIMER10bit, TIMER_STATUS_TIMIL10, IRQ_TIMER10, P_TMR10},
-#if (MAX_BLACKFIN_GPTIMERS > 11)
- {TIMER11_id, TIMER11bit, TIMER_STATUS_TIMIL11, IRQ_TIMER11, P_TMR11},
-#endif
-#endif
-};
-
-struct bfin_tmr_state {
- struct iio_trigger *trig;
- struct bfin_timer *t;
- unsigned int timer_num;
- bool output_enable;
- unsigned int duty;
- int irq;
-};
-
-static int iio_bfin_tmr_set_state(struct iio_trigger *trig, bool state)
-{
- struct bfin_tmr_state *st = iio_trigger_get_drvdata(trig);
-
- if (get_gptimer_period(st->t->id) == 0)
- return -EINVAL;
-
- if (state)
- enable_gptimers(st->t->bit);
- else
- disable_gptimers(st->t->bit);
-
- return 0;
-}
-
-static ssize_t frequency_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct iio_trigger *trig = to_iio_trigger(dev);
- struct bfin_tmr_state *st = iio_trigger_get_drvdata(trig);
- unsigned int val;
- bool enabled;
- int ret;
-
- ret = kstrtouint(buf, 10, &val);
- if (ret)
- return ret;
-
- if (val > 100000)
- return -EINVAL;
-
- enabled = get_enabled_gptimers() & st->t->bit;
-
- if (enabled)
- disable_gptimers(st->t->bit);
-
- if (!val)
- return count;
-
- val = get_sclk() / val;
- if (val <= 4 || val <= st->duty)
- return -EINVAL;
-
- set_gptimer_period(st->t->id, val);
- set_gptimer_pwidth(st->t->id, val - st->duty);
-
- if (enabled)
- enable_gptimers(st->t->bit);
-
- return count;
-}
-
-static ssize_t frequency_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct iio_trigger *trig = to_iio_trigger(dev);
- struct bfin_tmr_state *st = iio_trigger_get_drvdata(trig);
- unsigned int period = get_gptimer_period(st->t->id);
- unsigned long val;
-
- if (!period)
- val = 0;
- else
- val = get_sclk() / get_gptimer_period(st->t->id);
-
- return sprintf(buf, "%lu\n", val);
-}
-
-static DEVICE_ATTR_RW(frequency);
-
-static struct attribute *iio_bfin_tmr_trigger_attrs[] = {
- &dev_attr_frequency.attr,
- NULL,
-};
-
-static const struct attribute_group iio_bfin_tmr_trigger_attr_group = {
- .attrs = iio_bfin_tmr_trigger_attrs,
-};
-
-static const struct attribute_group *iio_bfin_tmr_trigger_attr_groups[] = {
- &iio_bfin_tmr_trigger_attr_group,
- NULL
-};
-
-static irqreturn_t iio_bfin_tmr_trigger_isr(int irq, void *devid)
-{
- struct bfin_tmr_state *st = devid;
-
- clear_gptimer_intr(st->t->id);
- iio_trigger_poll(st->trig);
-
- return IRQ_HANDLED;
-}
-
-static int iio_bfin_tmr_get_number(int irq)
-{
- int i;
-
- for (i = 0; i < MAX_BLACKFIN_GPTIMERS; i++)
- if (iio_bfin_timer_code[i].irq == irq)
- return i;
-
- return -ENODEV;
-}
-
-static const struct iio_trigger_ops iio_bfin_tmr_trigger_ops = {
- .set_trigger_state = iio_bfin_tmr_set_state,
-};
-
-static int iio_bfin_tmr_trigger_probe(struct platform_device *pdev)
-{
- struct iio_bfin_timer_trigger_pdata *pdata;
- struct bfin_tmr_state *st;
- unsigned int config;
- int ret;
-
- st = devm_kzalloc(&pdev->dev, sizeof(*st), GFP_KERNEL);
- if (!st)
- return -ENOMEM;
-
- st->irq = platform_get_irq(pdev, 0);
- if (st->irq < 0) {
- dev_err(&pdev->dev, "No IRQs specified");
- return st->irq;
- }
-
- ret = iio_bfin_tmr_get_number(st->irq);
- if (ret < 0)
- return ret;
-
- st->timer_num = ret;
- st->t = &iio_bfin_timer_code[st->timer_num];
-
- st->trig = iio_trigger_alloc("bfintmr%d", st->timer_num);
- if (!st->trig)
- return -ENOMEM;
-
- st->trig->ops = &iio_bfin_tmr_trigger_ops;
- st->trig->dev.groups = iio_bfin_tmr_trigger_attr_groups;
- iio_trigger_set_drvdata(st->trig, st);
- ret = iio_trigger_register(st->trig);
- if (ret)
- goto out;
-
- ret = request_irq(st->irq, iio_bfin_tmr_trigger_isr,
- 0, st->trig->name, st);
- if (ret) {
- dev_err(&pdev->dev,
- "request IRQ-%d failed", st->irq);
- goto out1;
- }
-
- config = PWM_OUT | PERIOD_CNT | IRQ_ENA;
-
- pdata = dev_get_platdata(&pdev->dev);
- if (pdata && pdata->output_enable) {
- unsigned long long val;
-
- st->output_enable = true;
-
- ret = peripheral_request(st->t->pin, st->trig->name);
- if (ret)
- goto out_free_irq;
-
- val = (unsigned long long)get_sclk() * pdata->duty_ns;
- do_div(val, NSEC_PER_SEC);
- st->duty = val;
-
- /**
- * The interrupt will be generated at the end of the period,
- * since we want the interrupt to be generated at end of the
- * pulse we invert both polarity and duty cycle, so that the
- * pulse will be generated directly before the interrupt.
- */
- if (pdata->active_low)
- config |= PULSE_HI;
- } else {
- st->duty = 1;
- config |= OUT_DIS;
- }
-
- set_gptimer_config(st->t->id, config);
-
- dev_info(&pdev->dev, "iio trigger Blackfin TMR%d, IRQ-%d",
- st->timer_num, st->irq);
- platform_set_drvdata(pdev, st);
-
- return 0;
-out_free_irq:
- free_irq(st->irq, st);
-out1:
- iio_trigger_unregister(st->trig);
-out:
- iio_trigger_free(st->trig);
- return ret;
-}
-
-static int iio_bfin_tmr_trigger_remove(struct platform_device *pdev)
-{
- struct bfin_tmr_state *st = platform_get_drvdata(pdev);
-
- disable_gptimers(st->t->bit);
- if (st->output_enable)
- peripheral_free(st->t->pin);
- free_irq(st->irq, st);
- iio_trigger_unregister(st->trig);
- iio_trigger_free(st->trig);
-
- return 0;
-}
-
-static struct platform_driver iio_bfin_tmr_trigger_driver = {
- .driver = {
- .name = "iio_bfin_tmr_trigger",
- },
- .probe = iio_bfin_tmr_trigger_probe,
- .remove = iio_bfin_tmr_trigger_remove,
-};
-
-module_platform_driver(iio_bfin_tmr_trigger_driver);
-
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
-MODULE_DESCRIPTION("Blackfin system timer based trigger for the iio subsystem");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:iio-trig-bfin-timer");
diff --git a/drivers/staging/iio/trigger/iio-trig-bfin-timer.h b/drivers/staging/iio/trigger/iio-trig-bfin-timer.h
deleted file mode 100644
index fb05a2a8397c..000000000000
--- a/drivers/staging/iio/trigger/iio-trig-bfin-timer.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef __IIO_BFIN_TIMER_TRIGGER_H__
-#define __IIO_BFIN_TIMER_TRIGGER_H__
-
-/**
- * struct iio_bfin_timer_trigger_pdata - timer trigger platform data
- * @output_enable: Enable external trigger pulse generation.
- * @active_low: Whether the trigger pulse is active low.
- * @duty_ns: Length of the trigger pulse in nanoseconds.
- *
- * This struct is used to configure the output pulse generation of the blackfin
- * timer trigger. If output_enable is set to true an external trigger signal
- * will generated on the pin corresponding to the timer. This is useful for
- * converters which needs an external signal to start conversion. active_low and
- * duty_ns are used to configure the type of the trigger pulse. If output_enable
- * is set to false no external trigger pulse will be generated and active_low
- * and duty_ns are ignored.
- **/
-struct iio_bfin_timer_trigger_pdata {
- bool output_enable;
- bool active_low;
- unsigned int duty_ns;
-};
-
-#endif
diff --git a/drivers/staging/ipx/af_ipx.c b/drivers/staging/ipx/af_ipx.c
index d21a9d128d3e..5703dd176787 100644
--- a/drivers/staging/ipx/af_ipx.c
+++ b/drivers/staging/ipx/af_ipx.c
@@ -1577,7 +1577,7 @@ out:
static int ipx_getname(struct socket *sock, struct sockaddr *uaddr,
- int *uaddr_len, int peer)
+ int peer)
{
struct ipx_address *addr;
struct sockaddr_ipx sipx;
@@ -1585,8 +1585,6 @@ static int ipx_getname(struct socket *sock, struct sockaddr *uaddr,
struct ipx_sock *ipxs = ipx_sk(sk);
int rc;
- *uaddr_len = sizeof(struct sockaddr_ipx);
-
lock_sock(sk);
if (peer) {
rc = -ENOTCONN;
@@ -1620,7 +1618,7 @@ static int ipx_getname(struct socket *sock, struct sockaddr *uaddr,
sipx.sipx_zero = 0;
memcpy(uaddr, &sipx, sizeof(sipx));
- rc = 0;
+ rc = sizeof(struct sockaddr_ipx);
out:
release_sock(sk);
return rc;
diff --git a/drivers/staging/irda/TODO b/drivers/staging/irda/TODO
deleted file mode 100644
index 7d98a5cffaff..000000000000
--- a/drivers/staging/irda/TODO
+++ /dev/null
@@ -1,4 +0,0 @@
-The irda code will be removed soon from the kernel tree as it is old and
-obsolete and broken.
-
-Don't worry about fixing up anything here, it's not needed.
diff --git a/drivers/staging/irda/drivers/Kconfig b/drivers/staging/irda/drivers/Kconfig
deleted file mode 100644
index e070e1222733..000000000000
--- a/drivers/staging/irda/drivers/Kconfig
+++ /dev/null
@@ -1,398 +0,0 @@
-menu "Infrared-port device drivers"
- depends on IRDA!=n
-
-comment "SIR device drivers"
-
-config IRTTY_SIR
- tristate "IrTTY (uses Linux serial driver)"
- depends on IRDA && TTY
- help
- Say Y here if you want to build support for the IrTTY line
- discipline. To compile it as a module, choose M here: the module
- will be called irtty-sir. IrTTY makes it possible to use Linux's
- own serial driver for all IrDA ports that are 16550 compatible.
- Most IrDA chips are 16550 compatible so you should probably say Y
- to this option. Using IrTTY will however limit the speed of the
- connection to 115200 bps (IrDA SIR mode).
-
- If unsure, say Y.
-
-config BFIN_SIR
- tristate "Blackfin SIR on UART"
- depends on BLACKFIN && IRDA
- default n
- help
- Say Y here if your want to enable SIR function on Blackfin UART
- devices.
-
- To activate this driver you can start irattach like:
- "irattach irda0 -s"
-
- Saying M, it will be built as a module named bfin_sir.
-
- Note that you need to turn off one of the serial drivers for SIR
- to use that UART.
-
-config BFIN_SIR0
- bool "Blackfin SIR on UART0"
- depends on BFIN_SIR && !SERIAL_BFIN_UART0
-
-config BFIN_SIR1
- bool "Blackfin SIR on UART1"
- depends on BFIN_SIR && !SERIAL_BFIN_UART1 && (!BF531 && !BF532 && !BF533 && !BF561)
-
-config BFIN_SIR2
- bool "Blackfin SIR on UART2"
- depends on BFIN_SIR && !SERIAL_BFIN_UART2 && (BF54x || BF538 || BF539)
-
-config BFIN_SIR3
- bool "Blackfin SIR on UART3"
- depends on BFIN_SIR && !SERIAL_BFIN_UART3 && (BF54x)
-
-choice
- prompt "SIR Mode"
- depends on BFIN_SIR
- default SIR_BFIN_DMA
-
-config SIR_BFIN_DMA
- bool "DMA mode"
- depends on !DMA_UNCACHED_NONE
-
-config SIR_BFIN_PIO
- bool "PIO mode"
-endchoice
-
-config SH_SIR
- tristate "SuperH SIR on UART"
- depends on IRDA && SUPERH && \
- (CPU_SUBTYPE_SH7722 || CPU_SUBTYPE_SH7723 || \
- CPU_SUBTYPE_SH7724)
- default n
- help
- Say Y here if your want to enable SIR function on SuperH UART
- devices.
-
-comment "Dongle support"
-
-config DONGLE
- bool "Serial dongle support"
- depends on IRTTY_SIR
- help
- Say Y here if you have an infrared device that connects to your
- computer's serial port. These devices are called dongles. Then say Y
- or M to the driver for your particular dongle below.
-
- Note that the answer to this question won't directly affect the
- kernel: saying N will just cause the configurator to skip all
- the questions about serial dongles.
-
-config ESI_DONGLE
- tristate "ESI JetEye PC dongle"
- depends on IRTTY_SIR && DONGLE && IRDA
- help
- Say Y here if you want to build support for the Extended Systems
- JetEye PC dongle. To compile it as a module, choose M here. The ESI
- dongle attaches to the normal 9-pin serial port connector, and can
- currently only be used by IrTTY. To activate support for ESI
- dongles you will have to start irattach like this:
- "irattach -d esi".
-
-config ACTISYS_DONGLE
- tristate "ACTiSYS IR-220L and IR220L+ dongle"
- depends on IRTTY_SIR && DONGLE && IRDA
- help
- Say Y here if you want to build support for the ACTiSYS IR-220L and
- IR220L+ dongles. To compile it as a module, choose M here. The
- ACTiSYS dongles attaches to the normal 9-pin serial port connector,
- and can currently only be used by IrTTY. To activate support for
- ACTiSYS dongles you will have to start irattach like this:
- "irattach -d actisys" or "irattach -d actisys+".
-
-config TEKRAM_DONGLE
- tristate "Tekram IrMate 210B dongle"
- depends on IRTTY_SIR && DONGLE && IRDA
- help
- Say Y here if you want to build support for the Tekram IrMate 210B
- dongle. To compile it as a module, choose M here. The Tekram dongle
- attaches to the normal 9-pin serial port connector, and can
- currently only be used by IrTTY. To activate support for Tekram
- dongles you will have to start irattach like this:
- "irattach -d tekram".
-
-config TOIM3232_DONGLE
- tristate "TOIM3232 IrDa dongle"
- depends on IRTTY_SIR && DONGLE && IRDA
- help
- Say Y here if you want to build support for the Vishay/Temic
- TOIM3232 and TOIM4232 based dongles.
- To compile it as a module, choose M here.
-
-config LITELINK_DONGLE
- tristate "Parallax LiteLink dongle"
- depends on IRTTY_SIR && DONGLE && IRDA
- help
- Say Y here if you want to build support for the Parallax Litelink
- dongle. To compile it as a module, choose M here. The Parallax
- dongle attaches to the normal 9-pin serial port connector, and can
- currently only be used by IrTTY. To activate support for Parallax
- dongles you will have to start irattach like this:
- "irattach -d litelink".
-
-config MA600_DONGLE
- tristate "Mobile Action MA600 dongle"
- depends on IRTTY_SIR && DONGLE && IRDA
- help
- Say Y here if you want to build support for the Mobile Action MA600
- dongle. To compile it as a module, choose M here. The MA600 dongle
- attaches to the normal 9-pin serial port connector, and can
- currently only be used by IrTTY. The driver should also support
- the MA620 USB version of the dongle, if the integrated USB-to-RS232
- converter is supported by usbserial. To activate support for
- MA600 dongle you will have to start irattach like this:
- "irattach -d ma600".
-
-config GIRBIL_DONGLE
- tristate "Greenwich GIrBIL dongle"
- depends on IRTTY_SIR && DONGLE && IRDA
- help
- Say Y here if you want to build support for the Greenwich GIrBIL
- dongle. If you want to compile it as a module, choose M here.
- The Greenwich dongle attaches to the normal 9-pin serial port
- connector, and can currently only be used by IrTTY. To activate
- support for Greenwich dongles you will have to start irattach
- like this: "irattach -d girbil".
-
-config MCP2120_DONGLE
- tristate "Microchip MCP2120"
- depends on IRTTY_SIR && DONGLE && IRDA
- help
- Say Y here if you want to build support for the Microchip MCP2120
- dongle. If you want to compile it as a module, choose M here.
- The MCP2120 dongle attaches to the normal 9-pin serial port
- connector, and can currently only be used by IrTTY. To activate
- support for MCP2120 dongles you will have to start irattach
- like this: "irattach -d mcp2120".
-
- You must build this dongle yourself. For more information see:
- <http://www.eyetap.org/~tangf/irda_sir_linux.html>
-
-config OLD_BELKIN_DONGLE
- tristate "Old Belkin dongle"
- depends on IRTTY_SIR && DONGLE && IRDA
- help
- Say Y here if you want to build support for the Adaptec Airport 1000
- and 2000 dongles. If you want to compile it as a module, choose
- M here. Some information is contained in the comments
- at the top of <file:drivers/net/irda/old_belkin-sir.c>.
-
-config ACT200L_DONGLE
- tristate "ACTiSYS IR-200L dongle"
- depends on IRTTY_SIR && DONGLE && IRDA
- help
- Say Y here if you want to build support for the ACTiSYS IR-200L
- dongle. If you want to compile it as a module, choose M here.
- The ACTiSYS IR-200L dongle attaches to the normal 9-pin serial
- port connector, and can currently only be used by IrTTY.
- To activate support for ACTiSYS IR-200L dongle you will have to
- start irattach like this: "irattach -d act200l".
-
-config KINGSUN_DONGLE
- tristate "KingSun/DonShine DS-620 IrDA-USB dongle"
- depends on IRDA && USB
- help
- Say Y or M here if you want to build support for the KingSun/DonShine
- DS-620 IrDA-USB bridge device driver.
-
- This USB bridge does not conform to the IrDA-USB device class
- specification, and therefore needs its own specific driver. This
- dongle supports SIR speed only (9600 bps).
-
- To compile it as a module, choose M here: the module will be called
- kingsun-sir.
-
-config KSDAZZLE_DONGLE
- tristate "KingSun Dazzle IrDA-USB dongle"
- depends on IRDA && USB
- help
- Say Y or M here if you want to build support for the KingSun Dazzle
- IrDA-USB bridge device driver.
-
- This USB bridge does not conform to the IrDA-USB device class
- specification, and therefore needs its own specific driver. This
- dongle supports SIR speeds only (9600 through 115200 bps).
-
- To compile it as a module, choose M here: the module will be called
- ksdazzle-sir.
-
-config KS959_DONGLE
- tristate "KingSun KS-959 IrDA-USB dongle"
- depends on IRDA && USB
- help
- Say Y or M here if you want to build support for the KingSun KS-959
- IrDA-USB bridge device driver.
-
- This USB bridge does not conform to the IrDA-USB device class
- specification, and therefore needs its own specific driver. This
- dongle supports SIR speeds only (9600 through 57600 bps).
-
- To compile it as a module, choose M here: the module will be called
- ks959-sir.
-
-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
- will be called irda-usb. IrDA-USB support the various IrDA USB
- dongles available and most of their peculiarities. Those dongles
- plug in the USB port of your computer, are plug and play, and
- support SIR and FIR (4Mbps) speeds. On the other hand, those
- dongles tend to be less efficient than a FIR chipset.
-
- Please note that the driver is still experimental. And of course,
- you will need both USB and IrDA support in your kernel...
-
-config SIGMATEL_FIR
- tristate "SigmaTel STIr4200 bridge"
- depends on IRDA && USB
- select CRC32
- ---help---
- Say Y here if you want to build support for the SigmaTel STIr4200
- USB IrDA FIR bridge device driver.
-
- USB bridge based on the SigmaTel STIr4200 don't conform to the
- IrDA-USB device class specification, and therefore need their
- own specific driver. Those dongles support SIR and FIR (4Mbps)
- speeds.
-
- To compile it as a module, choose M here: the module will be called
- stir4200.
-
-config NSC_FIR
- tristate "NSC PC87108/PC87338"
- depends on IRDA && ISA_DMA_API
- help
- Say Y here if you want to build support for the NSC PC87108 and
- PC87338 IrDA chipsets. This driver supports SIR,
- MIR and FIR (4Mbps) speeds.
-
- To compile it as a module, choose M here: the module will be called
- nsc-ircc.
-
-config WINBOND_FIR
- tristate "Winbond W83977AF (IR)"
- depends on IRDA && ISA_DMA_API
- help
- Say Y here if you want to build IrDA support for the Winbond
- W83977AF super-io chipset. This driver should be used for the IrDA
- chipset in the Corel NetWinder. The driver supports SIR, MIR and
- FIR (4Mbps) speeds.
-
- To compile it as a module, choose M here: the module will be called
- w83977af_ir.
-
-config TOSHIBA_FIR
- tristate "Toshiba Type-O IR Port"
- depends on IRDA && PCI && !64BIT && VIRT_TO_BUS
- help
- Say Y here if you want to build support for the Toshiba Type-O IR
- and Donau oboe chipsets. These chipsets are used by the Toshiba
- Libretto 100/110CT, Tecra 8100, Portege 7020 and many more laptops.
- To compile it as a module, choose M here: the module will be called
- donauboe.
-
-config AU1000_FIR
- tristate "Alchemy IrDA SIR/FIR"
- depends on IRDA && MIPS_ALCHEMY
- help
- Say Y/M here to build support the IrDA peripheral on the
- Alchemy Au1000 and Au1100 SoCs.
- Say M to build a module; it will be called au1k_ir.ko
-
-config SMC_IRCC_FIR
- tristate "SMSC IrCC"
- depends on IRDA && ISA_DMA_API
- help
- Say Y here if you want to build support for the SMC Infrared
- Communications Controller. It is used in a wide variety of
- laptops (Fujitsu, Sony, Compaq and some Toshiba).
- To compile it as a module, choose M here: the module will be called
- smsc-ircc2.o.
-
-config ALI_FIR
- tristate "ALi M5123 FIR"
- depends on IRDA && ISA_DMA_API
- help
- Say Y here if you want to build support for the ALi M5123 FIR
- Controller. The ALi M5123 FIR Controller is embedded in ALi M1543C,
- M1535, M1535D, M1535+, M1535D South Bridge. This driver supports
- SIR, MIR and FIR (4Mbps) speeds.
-
- To compile it as a module, choose M here: the module will be called
- ali-ircc.
-
-config VLSI_FIR
- tristate "VLSI 82C147 SIR/MIR/FIR"
- depends on IRDA && PCI
- help
- Say Y here if you want to build support for the VLSI 82C147
- PCI-IrDA Controller. This controller is used by the HP OmniBook 800
- and 5500 notebooks. The driver provides support for SIR, MIR and
- FIR (4Mbps) speeds.
-
- To compile it as a module, choose M here: the module will be called
- vlsi_ir.
-
-config SA1100_FIR
- tristate "SA1100 Internal IR"
- depends on ARCH_SA1100 && IRDA && DMA_SA11X0
-
-config VIA_FIR
- tristate "VIA VT8231/VT1211 SIR/MIR/FIR"
- depends on IRDA && ISA_DMA_API
- help
- Say Y here if you want to build support for the VIA VT8231
- and VIA VT1211 IrDA controllers, found on the motherboards using
- those VIA chipsets. To use this controller, you will need
- to plug a specific 5 pins FIR IrDA dongle in the specific
- motherboard connector. The driver provides support for SIR, MIR
- and FIR (4Mbps) speeds.
-
- You will need to specify the 'dongle_id' module parameter to
- indicate the FIR dongle attached to the controller.
-
- To compile it as a module, choose M here: the module will be called
- via-ircc.
-
-config PXA_FICP
- tristate "Intel PXA2xx Internal FICP"
- depends on ARCH_PXA && IRDA
- help
- Say Y or M here if you want to build support for the PXA2xx
- built-in IRDA interface which can support both SIR and FIR.
- This driver relies on platform specific helper routines so
- available capabilities may vary from one PXA2xx target to
- another.
-
-config MCS_FIR
- tristate "MosChip MCS7780 IrDA-USB dongle"
- depends on IRDA && USB
- select CRC32
- help
- Say Y or M here if you want to build support for the MosChip
- MCS7780 IrDA-USB bridge device driver.
-
- USB bridge based on the MosChip MCS7780 don't conform to the
- IrDA-USB device class specification, and therefore need their
- own specific driver. Those dongles support SIR and FIR (4Mbps)
- speeds.
-
- To compile it as a module, choose M here: the module will be called
- mcs7780.
-
-endmenu
-
diff --git a/drivers/staging/irda/drivers/Makefile b/drivers/staging/irda/drivers/Makefile
deleted file mode 100644
index e2901b135528..000000000000
--- a/drivers/staging/irda/drivers/Makefile
+++ /dev/null
@@ -1,44 +0,0 @@
-#
-# Makefile for the Linux IrDA infrared port device drivers.
-#
-# 9 Aug 2000, Christoph Hellwig <hch@infradead.org>
-# Rewritten to use lists instead of if-statements.
-#
-
-subdir-ccflags-y += -I$(srctree)/drivers/staging/irda/include
-
-# FIR drivers
-obj-$(CONFIG_USB_IRDA) += irda-usb.o
-obj-$(CONFIG_SIGMATEL_FIR) += stir4200.o
-obj-$(CONFIG_NSC_FIR) += nsc-ircc.o
-obj-$(CONFIG_WINBOND_FIR) += w83977af_ir.o
-obj-$(CONFIG_SA1100_FIR) += sa1100_ir.o
-obj-$(CONFIG_TOSHIBA_FIR) += donauboe.o
-obj-$(CONFIG_SMC_IRCC_FIR) += smsc-ircc2.o
-obj-$(CONFIG_ALI_FIR) += ali-ircc.o
-obj-$(CONFIG_VLSI_FIR) += vlsi_ir.o
-obj-$(CONFIG_VIA_FIR) += via-ircc.o
-obj-$(CONFIG_PXA_FICP) += pxaficp_ir.o
-obj-$(CONFIG_MCS_FIR) += mcs7780.o
-obj-$(CONFIG_AU1000_FIR) += au1k_ir.o
-# SIR drivers
-obj-$(CONFIG_IRTTY_SIR) += irtty-sir.o sir-dev.o
-obj-$(CONFIG_BFIN_SIR) += bfin_sir.o
-obj-$(CONFIG_SH_SIR) += sh_sir.o
-# dongle drivers for SIR drivers
-obj-$(CONFIG_ESI_DONGLE) += esi-sir.o
-obj-$(CONFIG_TEKRAM_DONGLE) += tekram-sir.o
-obj-$(CONFIG_ACTISYS_DONGLE) += actisys-sir.o
-obj-$(CONFIG_LITELINK_DONGLE) += litelink-sir.o
-obj-$(CONFIG_GIRBIL_DONGLE) += girbil-sir.o
-obj-$(CONFIG_OLD_BELKIN_DONGLE) += old_belkin-sir.o
-obj-$(CONFIG_MCP2120_DONGLE) += mcp2120-sir.o
-obj-$(CONFIG_ACT200L_DONGLE) += act200l-sir.o
-obj-$(CONFIG_MA600_DONGLE) += ma600-sir.o
-obj-$(CONFIG_TOIM3232_DONGLE) += toim3232-sir.o
-obj-$(CONFIG_KINGSUN_DONGLE) += kingsun-sir.o
-obj-$(CONFIG_KSDAZZLE_DONGLE) += ksdazzle-sir.o
-obj-$(CONFIG_KS959_DONGLE) += ks959-sir.o
-
-# The SIR helper module
-sir-dev-objs := sir_dev.o sir_dongle.o
diff --git a/drivers/staging/irda/drivers/act200l-sir.c b/drivers/staging/irda/drivers/act200l-sir.c
deleted file mode 100644
index e8917511e1aa..000000000000
--- a/drivers/staging/irda/drivers/act200l-sir.c
+++ /dev/null
@@ -1,250 +0,0 @@
-/*********************************************************************
- *
- * Filename: act200l.c
- * Version: 0.8
- * Description: Implementation for the ACTiSYS ACT-IR200L dongle
- * Status: Experimental.
- * Author: SHIMIZU Takuya <tshimizu@ga2.so-net.ne.jp>
- * Created at: Fri Aug 3 17:35:42 2001
- * Modified at: Fri Aug 17 10:22:40 2001
- * Modified by: SHIMIZU Takuya <tshimizu@ga2.so-net.ne.jp>
- *
- * Copyright (c) 2001 SHIMIZU Takuya, All Rights Reserved.
- *
- * 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/module.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-
-#include <net/irda/irda.h>
-
-#include "sir-dev.h"
-
-static int act200l_reset(struct sir_dev *dev);
-static int act200l_open(struct sir_dev *dev);
-static int act200l_close(struct sir_dev *dev);
-static int act200l_change_speed(struct sir_dev *dev, unsigned speed);
-
-/* Regsiter 0: Control register #1 */
-#define ACT200L_REG0 0x00
-#define ACT200L_TXEN 0x01 /* Enable transmitter */
-#define ACT200L_RXEN 0x02 /* Enable receiver */
-
-/* Register 1: Control register #2 */
-#define ACT200L_REG1 0x10
-#define ACT200L_LODB 0x01 /* Load new baud rate count value */
-#define ACT200L_WIDE 0x04 /* Expand the maximum allowable pulse */
-
-/* Register 4: Output Power register */
-#define ACT200L_REG4 0x40
-#define ACT200L_OP0 0x01 /* Enable LED1C output */
-#define ACT200L_OP1 0x02 /* Enable LED2C output */
-#define ACT200L_BLKR 0x04
-
-/* Register 5: Receive Mode register */
-#define ACT200L_REG5 0x50
-#define ACT200L_RWIDL 0x01 /* fixed 1.6us pulse mode */
-
-/* Register 6: Receive Sensitivity register #1 */
-#define ACT200L_REG6 0x60
-#define ACT200L_RS0 0x01 /* receive threshold bit 0 */
-#define ACT200L_RS1 0x02 /* receive threshold bit 1 */
-
-/* Register 7: Receive Sensitivity register #2 */
-#define ACT200L_REG7 0x70
-#define ACT200L_ENPOS 0x04 /* Ignore the falling edge */
-
-/* Register 8,9: Baud Rate Dvider register #1,#2 */
-#define ACT200L_REG8 0x80
-#define ACT200L_REG9 0x90
-
-#define ACT200L_2400 0x5f
-#define ACT200L_9600 0x17
-#define ACT200L_19200 0x0b
-#define ACT200L_38400 0x05
-#define ACT200L_57600 0x03
-#define ACT200L_115200 0x01
-
-/* Register 13: Control register #3 */
-#define ACT200L_REG13 0xd0
-#define ACT200L_SHDW 0x01 /* Enable access to shadow registers */
-
-/* Register 15: Status register */
-#define ACT200L_REG15 0xf0
-
-/* Register 21: Control register #4 */
-#define ACT200L_REG21 0x50
-#define ACT200L_EXCK 0x02 /* Disable clock output driver */
-#define ACT200L_OSCL 0x04 /* oscillator in low power, medium accuracy mode */
-
-static struct dongle_driver act200l = {
- .owner = THIS_MODULE,
- .driver_name = "ACTiSYS ACT-IR200L",
- .type = IRDA_ACT200L_DONGLE,
- .open = act200l_open,
- .close = act200l_close,
- .reset = act200l_reset,
- .set_speed = act200l_change_speed,
-};
-
-static int __init act200l_sir_init(void)
-{
- return irda_register_dongle(&act200l);
-}
-
-static void __exit act200l_sir_cleanup(void)
-{
- irda_unregister_dongle(&act200l);
-}
-
-static int act200l_open(struct sir_dev *dev)
-{
- struct qos_info *qos = &dev->qos;
-
- /* Power on the dongle */
- sirdev_set_dtr_rts(dev, TRUE, TRUE);
-
- /* Set the speeds we can accept */
- qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
- qos->min_turn_time.bits = 0x03;
- irda_qos_bits_to_value(qos);
-
- /* irda thread waits 50 msec for power settling */
-
- return 0;
-}
-
-static int act200l_close(struct sir_dev *dev)
-{
- /* Power off the dongle */
- sirdev_set_dtr_rts(dev, FALSE, FALSE);
-
- return 0;
-}
-
-/*
- * Function act200l_change_speed (dev, speed)
- *
- * Set the speed for the ACTiSYS ACT-IR200L type dongle.
- *
- */
-static int act200l_change_speed(struct sir_dev *dev, unsigned speed)
-{
- u8 control[3];
- int ret = 0;
-
- /* Clear DTR and set RTS to enter command mode */
- sirdev_set_dtr_rts(dev, FALSE, TRUE);
-
- switch (speed) {
- default:
- ret = -EINVAL;
- /* fall through */
- case 9600:
- control[0] = ACT200L_REG8 | (ACT200L_9600 & 0x0f);
- control[1] = ACT200L_REG9 | ((ACT200L_9600 >> 4) & 0x0f);
- break;
- case 19200:
- control[0] = ACT200L_REG8 | (ACT200L_19200 & 0x0f);
- control[1] = ACT200L_REG9 | ((ACT200L_19200 >> 4) & 0x0f);
- break;
- case 38400:
- control[0] = ACT200L_REG8 | (ACT200L_38400 & 0x0f);
- control[1] = ACT200L_REG9 | ((ACT200L_38400 >> 4) & 0x0f);
- break;
- case 57600:
- control[0] = ACT200L_REG8 | (ACT200L_57600 & 0x0f);
- control[1] = ACT200L_REG9 | ((ACT200L_57600 >> 4) & 0x0f);
- break;
- case 115200:
- control[0] = ACT200L_REG8 | (ACT200L_115200 & 0x0f);
- control[1] = ACT200L_REG9 | ((ACT200L_115200 >> 4) & 0x0f);
- break;
- }
- control[2] = ACT200L_REG1 | ACT200L_LODB | ACT200L_WIDE;
-
- /* Write control bytes */
- sirdev_raw_write(dev, control, 3);
- msleep(5);
-
- /* Go back to normal mode */
- sirdev_set_dtr_rts(dev, TRUE, TRUE);
-
- dev->speed = speed;
- return ret;
-}
-
-/*
- * Function act200l_reset (driver)
- *
- * Reset the ACTiSYS ACT-IR200L type dongle.
- */
-
-#define ACT200L_STATE_WAIT1_RESET (SIRDEV_STATE_DONGLE_RESET+1)
-#define ACT200L_STATE_WAIT2_RESET (SIRDEV_STATE_DONGLE_RESET+2)
-
-static int act200l_reset(struct sir_dev *dev)
-{
- unsigned state = dev->fsm.substate;
- unsigned delay = 0;
- static const u8 control[9] = {
- ACT200L_REG15,
- ACT200L_REG13 | ACT200L_SHDW,
- ACT200L_REG21 | ACT200L_EXCK | ACT200L_OSCL,
- ACT200L_REG13,
- ACT200L_REG7 | ACT200L_ENPOS,
- ACT200L_REG6 | ACT200L_RS0 | ACT200L_RS1,
- ACT200L_REG5 | ACT200L_RWIDL,
- ACT200L_REG4 | ACT200L_OP0 | ACT200L_OP1 | ACT200L_BLKR,
- ACT200L_REG0 | ACT200L_TXEN | ACT200L_RXEN
- };
- int ret = 0;
-
- switch (state) {
- case SIRDEV_STATE_DONGLE_RESET:
- /* Reset the dongle : set RTS low for 25 ms */
- sirdev_set_dtr_rts(dev, TRUE, FALSE);
- state = ACT200L_STATE_WAIT1_RESET;
- delay = 50;
- break;
-
- case ACT200L_STATE_WAIT1_RESET:
- /* Clear DTR and set RTS to enter command mode */
- sirdev_set_dtr_rts(dev, FALSE, TRUE);
-
- udelay(25); /* better wait for some short while */
-
- /* Write control bytes */
- sirdev_raw_write(dev, control, sizeof(control));
- state = ACT200L_STATE_WAIT2_RESET;
- delay = 15;
- break;
-
- case ACT200L_STATE_WAIT2_RESET:
- /* Go back to normal mode */
- sirdev_set_dtr_rts(dev, TRUE, TRUE);
- dev->speed = 9600;
- break;
- default:
- net_err_ratelimited("%s(), unknown state %d\n",
- __func__, state);
- ret = -1;
- break;
- }
- dev->fsm.substate = state;
- return (delay > 0) ? delay : ret;
-}
-
-MODULE_AUTHOR("SHIMIZU Takuya <tshimizu@ga2.so-net.ne.jp>");
-MODULE_DESCRIPTION("ACTiSYS ACT-IR200L dongle driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("irda-dongle-10"); /* IRDA_ACT200L_DONGLE */
-
-module_init(act200l_sir_init);
-module_exit(act200l_sir_cleanup);
diff --git a/drivers/staging/irda/drivers/actisys-sir.c b/drivers/staging/irda/drivers/actisys-sir.c
deleted file mode 100644
index e224b8b99517..000000000000
--- a/drivers/staging/irda/drivers/actisys-sir.c
+++ /dev/null
@@ -1,245 +0,0 @@
-/*********************************************************************
- *
- * Filename: actisys.c
- * Version: 1.1
- * Description: Implementation for the ACTiSYS IR-220L and IR-220L+
- * dongles
- * Status: Beta.
- * Authors: Dag Brattli <dagb@cs.uit.no> (initially)
- * Jean Tourrilhes <jt@hpl.hp.com> (new version)
- * Martin Diehl <mad@mdiehl.de> (new version for sir_dev)
- * Created at: Wed Oct 21 20:02:35 1998
- * Modified at: Sun Oct 27 22:02:13 2002
- * Modified by: Martin Diehl <mad@mdiehl.de>
- *
- * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
- * Copyright (c) 1999 Jean Tourrilhes
- * Copyright (c) 2002 Martin Diehl
- *
- * 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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-/*
- * Changelog
- *
- * 0.8 -> 0.9999 - Jean
- * o New initialisation procedure : much safer and correct
- * o New procedure the change speed : much faster and simpler
- * o Other cleanups & comments
- * Thanks to Lichen Wang @ Actisys for his excellent help...
- *
- * 1.0 -> 1.1 - Martin Diehl
- * modified for new sir infrastructure
- */
-
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-
-#include <net/irda/irda.h>
-
-#include "sir-dev.h"
-
-/*
- * Define the timing of the pulses we send to the dongle (to reset it, and
- * to toggle speeds). Basically, the limit here is the propagation speed of
- * the signals through the serial port, the dongle being much faster. Any
- * serial port support 115 kb/s, so we are sure that pulses 8.5 us wide can
- * go through cleanly . If you are on the wild side, you can try to lower
- * this value (Actisys recommended me 2 us, and 0 us work for me on a P233!)
- */
-#define MIN_DELAY 10 /* 10 us to be on the conservative side */
-
-static int actisys_open(struct sir_dev *);
-static int actisys_close(struct sir_dev *);
-static int actisys_change_speed(struct sir_dev *, unsigned);
-static int actisys_reset(struct sir_dev *);
-
-/* These are the baudrates supported, in the order available */
-/* Note : the 220L doesn't support 38400, but we will fix that below */
-static unsigned baud_rates[] = { 9600, 19200, 57600, 115200, 38400 };
-
-#define MAX_SPEEDS ARRAY_SIZE(baud_rates)
-
-static struct dongle_driver act220l = {
- .owner = THIS_MODULE,
- .driver_name = "Actisys ACT-220L",
- .type = IRDA_ACTISYS_DONGLE,
- .open = actisys_open,
- .close = actisys_close,
- .reset = actisys_reset,
- .set_speed = actisys_change_speed,
-};
-
-static struct dongle_driver act220l_plus = {
- .owner = THIS_MODULE,
- .driver_name = "Actisys ACT-220L+",
- .type = IRDA_ACTISYS_PLUS_DONGLE,
- .open = actisys_open,
- .close = actisys_close,
- .reset = actisys_reset,
- .set_speed = actisys_change_speed,
-};
-
-static int __init actisys_sir_init(void)
-{
- int ret;
-
- /* First, register an Actisys 220L dongle */
- ret = irda_register_dongle(&act220l);
- if (ret < 0)
- return ret;
-
- /* Now, register an Actisys 220L+ dongle */
- ret = irda_register_dongle(&act220l_plus);
- if (ret < 0) {
- irda_unregister_dongle(&act220l);
- return ret;
- }
- return 0;
-}
-
-static void __exit actisys_sir_cleanup(void)
-{
- /* We have to remove both dongles */
- irda_unregister_dongle(&act220l_plus);
- irda_unregister_dongle(&act220l);
-}
-
-static int actisys_open(struct sir_dev *dev)
-{
- struct qos_info *qos = &dev->qos;
-
- sirdev_set_dtr_rts(dev, TRUE, TRUE);
-
- /* Set the speeds we can accept */
- qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
-
- /* Remove support for 38400 if this is not a 220L+ dongle */
- if (dev->dongle_drv->type == IRDA_ACTISYS_DONGLE)
- qos->baud_rate.bits &= ~IR_38400;
-
- qos->min_turn_time.bits = 0x7f; /* Needs 0.01 ms */
- irda_qos_bits_to_value(qos);
-
- /* irda thread waits 50 msec for power settling */
-
- return 0;
-}
-
-static int actisys_close(struct sir_dev *dev)
-{
- /* Power off the dongle */
- sirdev_set_dtr_rts(dev, FALSE, FALSE);
-
- return 0;
-}
-
-/*
- * Function actisys_change_speed (task)
- *
- * Change speed of the ACTiSYS IR-220L and IR-220L+ type IrDA dongles.
- * To cycle through the available baud rates, pulse RTS low for a few us.
- *
- * First, we reset the dongle to always start from a known state.
- * Then, we cycle through the speeds by pulsing RTS low and then up.
- * The dongle allow us to pulse quite fast, se we can set speed in one go,
- * which is must faster ( < 100 us) and less complex than what is found
- * in some other dongle drivers...
- * Note that even if the new speed is the same as the current speed,
- * we reassert the speed. This make sure that things are all right,
- * and it's fast anyway...
- * By the way, this function will work for both type of dongles,
- * because the additional speed is at the end of the sequence...
- */
-static int actisys_change_speed(struct sir_dev *dev, unsigned speed)
-{
- int ret = 0;
- int i = 0;
-
- pr_debug("%s(), speed=%d (was %d)\n", __func__, speed, dev->speed);
-
- /* dongle was already resetted from irda_request state machine,
- * we are in known state (dongle default)
- */
-
- /*
- * Now, we can set the speed requested. Send RTS pulses until we
- * reach the target speed
- */
- for (i = 0; i < MAX_SPEEDS; i++) {
- if (speed == baud_rates[i]) {
- dev->speed = speed;
- break;
- }
- /* Set RTS low for 10 us */
- sirdev_set_dtr_rts(dev, TRUE, FALSE);
- udelay(MIN_DELAY);
-
- /* Set RTS high for 10 us */
- sirdev_set_dtr_rts(dev, TRUE, TRUE);
- udelay(MIN_DELAY);
- }
-
- /* Check if life is sweet... */
- if (i >= MAX_SPEEDS) {
- actisys_reset(dev);
- ret = -EINVAL; /* This should not happen */
- }
-
- /* Basta lavoro, on se casse d'ici... */
- return ret;
-}
-
-/*
- * Function actisys_reset (task)
- *
- * Reset the Actisys type dongle. Warning, this function must only be
- * called with a process context!
- *
- * We need to do two things in this function :
- * o first make sure that the dongle is in a state where it can operate
- * o second put the dongle in a know state
- *
- * The dongle is powered of the RTS and DTR lines. In the dongle, there
- * is a big capacitor to accommodate the current spikes. This capacitor
- * takes a least 50 ms to be charged. In theory, the Bios set those lines
- * up, so by the time we arrive here we should be set. It doesn't hurt
- * to be on the conservative side, so we will wait...
- * <Martin : move above comment to irda_config_fsm>
- * Then, we set the speed to 9600 b/s to get in a known state (see in
- * change_speed for details). It is needed because the IrDA stack
- * has tried to set the speed immediately after our first return,
- * so before we can be sure the dongle is up and running.
- */
-
-static int actisys_reset(struct sir_dev *dev)
-{
- /* Reset the dongle : set DTR low for 10 us */
- sirdev_set_dtr_rts(dev, FALSE, TRUE);
- udelay(MIN_DELAY);
-
- /* Go back to normal mode */
- sirdev_set_dtr_rts(dev, TRUE, TRUE);
-
- dev->speed = 9600; /* That's the default */
-
- return 0;
-}
-
-MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no> - Jean Tourrilhes <jt@hpl.hp.com>");
-MODULE_DESCRIPTION("ACTiSYS IR-220L and IR-220L+ dongle driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("irda-dongle-2"); /* IRDA_ACTISYS_DONGLE */
-MODULE_ALIAS("irda-dongle-3"); /* IRDA_ACTISYS_PLUS_DONGLE */
-
-module_init(actisys_sir_init);
-module_exit(actisys_sir_cleanup);
diff --git a/drivers/staging/irda/drivers/ali-ircc.c b/drivers/staging/irda/drivers/ali-ircc.c
deleted file mode 100644
index 589cd01797f4..000000000000
--- a/drivers/staging/irda/drivers/ali-ircc.c
+++ /dev/null
@@ -1,2217 +0,0 @@
-/*********************************************************************
- *
- * Filename: ali-ircc.h
- * Version: 0.5
- * Description: Driver for the ALI M1535D and M1543C FIR Controller
- * Status: Experimental.
- * Author: Benjamin Kong <benjamin_kong@ali.com.tw>
- * Created at: 2000/10/16 03:46PM
- * Modified at: 2001/1/3 02:55PM
- * Modified by: Benjamin Kong <benjamin_kong@ali.com.tw>
- * Modified at: 2003/11/6 and support for ALi south-bridge chipsets M1563
- * Modified by: Clear Zhang <clear_zhang@ali.com.tw>
- *
- * Copyright (c) 2000 Benjamin Kong <benjamin_kong@ali.com.tw>
- * All Rights Reserved
- *
- * 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/module.h>
-#include <linux/gfp.h>
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/rtnetlink.h>
-#include <linux/serial_reg.h>
-#include <linux/dma-mapping.h>
-#include <linux/platform_device.h>
-
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <asm/byteorder.h>
-
-#include <net/irda/wrapper.h>
-#include <net/irda/irda.h>
-#include <net/irda/irda_device.h>
-
-#include "ali-ircc.h"
-
-#define CHIP_IO_EXTENT 8
-#define BROKEN_DONGLE_ID
-
-#define ALI_IRCC_DRIVER_NAME "ali-ircc"
-
-/* Power Management */
-static int ali_ircc_suspend(struct platform_device *dev, pm_message_t state);
-static int ali_ircc_resume(struct platform_device *dev);
-
-static struct platform_driver ali_ircc_driver = {
- .suspend = ali_ircc_suspend,
- .resume = ali_ircc_resume,
- .driver = {
- .name = ALI_IRCC_DRIVER_NAME,
- },
-};
-
-/* Module parameters */
-static int qos_mtt_bits = 0x07; /* 1 ms or more */
-
-/* Use BIOS settions by default, but user may supply module parameters */
-static unsigned int io[] = { ~0, ~0, ~0, ~0 };
-static unsigned int irq[] = { 0, 0, 0, 0 };
-static unsigned int dma[] = { 0, 0, 0, 0 };
-
-static int ali_ircc_probe_53(ali_chip_t *chip, chipio_t *info);
-static int ali_ircc_init_43(ali_chip_t *chip, chipio_t *info);
-static int ali_ircc_init_53(ali_chip_t *chip, chipio_t *info);
-
-/* These are the currently known ALi south-bridge chipsets, the only one difference
- * is that M1543C doesn't support HP HDSL-3600
- */
-static ali_chip_t chips[] =
-{
- { "M1543", { 0x3f0, 0x370 }, 0x51, 0x23, 0x20, 0x43, ali_ircc_probe_53, ali_ircc_init_43 },
- { "M1535", { 0x3f0, 0x370 }, 0x51, 0x23, 0x20, 0x53, ali_ircc_probe_53, ali_ircc_init_53 },
- { "M1563", { 0x3f0, 0x370 }, 0x51, 0x23, 0x20, 0x63, ali_ircc_probe_53, ali_ircc_init_53 },
- { NULL }
-};
-
-/* Max 4 instances for now */
-static struct ali_ircc_cb *dev_self[] = { NULL, NULL, NULL, NULL };
-
-/* Dongle Types */
-static char *dongle_types[] = {
- "TFDS6000",
- "HP HSDL-3600",
- "HP HSDL-1100",
- "No dongle connected",
-};
-
-/* Some prototypes */
-static int ali_ircc_open(int i, chipio_t *info);
-
-static int ali_ircc_close(struct ali_ircc_cb *self);
-
-static int ali_ircc_setup(chipio_t *info);
-static int ali_ircc_is_receiving(struct ali_ircc_cb *self);
-static int ali_ircc_net_open(struct net_device *dev);
-static int ali_ircc_net_close(struct net_device *dev);
-static int ali_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-static void ali_ircc_change_speed(struct ali_ircc_cb *self, __u32 baud);
-
-/* SIR function */
-static netdev_tx_t ali_ircc_sir_hard_xmit(struct sk_buff *skb,
- struct net_device *dev);
-static irqreturn_t ali_ircc_sir_interrupt(struct ali_ircc_cb *self);
-static void ali_ircc_sir_receive(struct ali_ircc_cb *self);
-static void ali_ircc_sir_write_wakeup(struct ali_ircc_cb *self);
-static int ali_ircc_sir_write(int iobase, int fifo_size, __u8 *buf, int len);
-static void ali_ircc_sir_change_speed(struct ali_ircc_cb *priv, __u32 speed);
-
-/* FIR function */
-static netdev_tx_t ali_ircc_fir_hard_xmit(struct sk_buff *skb,
- struct net_device *dev);
-static void ali_ircc_fir_change_speed(struct ali_ircc_cb *priv, __u32 speed);
-static irqreturn_t ali_ircc_fir_interrupt(struct ali_ircc_cb *self);
-static int ali_ircc_dma_receive(struct ali_ircc_cb *self);
-static int ali_ircc_dma_receive_complete(struct ali_ircc_cb *self);
-static int ali_ircc_dma_xmit_complete(struct ali_ircc_cb *self);
-static void ali_ircc_dma_xmit(struct ali_ircc_cb *self);
-
-/* My Function */
-static int ali_ircc_read_dongle_id (int i, chipio_t *info);
-static void ali_ircc_change_dongle_speed(struct ali_ircc_cb *priv, int speed);
-
-/* ALi chip function */
-static void SIR2FIR(int iobase);
-static void FIR2SIR(int iobase);
-static void SetCOMInterrupts(struct ali_ircc_cb *self , unsigned char enable);
-
-/*
- * Function ali_ircc_init ()
- *
- * Initialize chip. Find out whay kinds of chips we are dealing with
- * and their configuration registers address
- */
-static int __init ali_ircc_init(void)
-{
- ali_chip_t *chip;
- chipio_t info;
- int ret;
- int cfg, cfg_base;
- int reg, revision;
- int i = 0;
-
- ret = platform_driver_register(&ali_ircc_driver);
- if (ret) {
- net_err_ratelimited("%s, Can't register driver!\n",
- ALI_IRCC_DRIVER_NAME);
- return ret;
- }
-
- ret = -ENODEV;
-
- /* Probe for all the ALi chipsets we know about */
- for (chip= chips; chip->name; chip++, i++)
- {
- pr_debug("%s(), Probing for %s ...\n", __func__, chip->name);
-
- /* Try all config registers for this chip */
- for (cfg=0; cfg<2; cfg++)
- {
- cfg_base = chip->cfg[cfg];
- if (!cfg_base)
- continue;
-
- memset(&info, 0, sizeof(chipio_t));
- info.cfg_base = cfg_base;
- info.fir_base = io[i];
- info.dma = dma[i];
- info.irq = irq[i];
-
-
- /* Enter Configuration */
- outb(chip->entr1, cfg_base);
- outb(chip->entr2, cfg_base);
-
- /* Select Logical Device 5 Registers (UART2) */
- outb(0x07, cfg_base);
- outb(0x05, cfg_base+1);
-
- /* Read Chip Identification Register */
- outb(chip->cid_index, cfg_base);
- reg = inb(cfg_base+1);
-
- if (reg == chip->cid_value)
- {
- pr_debug("%s(), Chip found at 0x%03x\n",
- __func__, cfg_base);
-
- outb(0x1F, cfg_base);
- revision = inb(cfg_base+1);
- pr_debug("%s(), Found %s chip, revision=%d\n",
- __func__, chip->name, revision);
-
- /*
- * If the user supplies the base address, then
- * we init the chip, if not we probe the values
- * set by the BIOS
- */
- if (io[i] < 2000)
- {
- chip->init(chip, &info);
- }
- else
- {
- chip->probe(chip, &info);
- }
-
- if (ali_ircc_open(i, &info) == 0)
- ret = 0;
- i++;
- }
- else
- {
- pr_debug("%s(), No %s chip at 0x%03x\n",
- __func__, chip->name, cfg_base);
- }
- /* Exit configuration */
- outb(0xbb, cfg_base);
- }
- }
-
- if (ret)
- platform_driver_unregister(&ali_ircc_driver);
-
- return ret;
-}
-
-/*
- * Function ali_ircc_cleanup ()
- *
- * Close all configured chips
- *
- */
-static void __exit ali_ircc_cleanup(void)
-{
- int i;
-
- for (i=0; i < ARRAY_SIZE(dev_self); i++) {
- if (dev_self[i])
- ali_ircc_close(dev_self[i]);
- }
-
- platform_driver_unregister(&ali_ircc_driver);
-
-}
-
-static const struct net_device_ops ali_ircc_sir_ops = {
- .ndo_open = ali_ircc_net_open,
- .ndo_stop = ali_ircc_net_close,
- .ndo_start_xmit = ali_ircc_sir_hard_xmit,
- .ndo_do_ioctl = ali_ircc_net_ioctl,
-};
-
-static const struct net_device_ops ali_ircc_fir_ops = {
- .ndo_open = ali_ircc_net_open,
- .ndo_stop = ali_ircc_net_close,
- .ndo_start_xmit = ali_ircc_fir_hard_xmit,
- .ndo_do_ioctl = ali_ircc_net_ioctl,
-};
-
-/*
- * Function ali_ircc_open (int i, chipio_t *inf)
- *
- * Open driver instance
- *
- */
-static int ali_ircc_open(int i, chipio_t *info)
-{
- struct net_device *dev;
- struct ali_ircc_cb *self;
- int dongle_id;
- int err;
-
- if (i >= ARRAY_SIZE(dev_self)) {
- net_err_ratelimited("%s(), maximum number of supported chips reached!\n",
- __func__);
- return -ENOMEM;
- }
-
- /* Set FIR FIFO and DMA Threshold */
- if ((ali_ircc_setup(info)) == -1)
- return -1;
-
- dev = alloc_irdadev(sizeof(*self));
- if (dev == NULL) {
- net_err_ratelimited("%s(), can't allocate memory for control block!\n",
- __func__);
- return -ENOMEM;
- }
-
- self = netdev_priv(dev);
- self->netdev = dev;
- spin_lock_init(&self->lock);
-
- /* Need to store self somewhere */
- dev_self[i] = self;
- self->index = i;
-
- /* Initialize IO */
- self->io.cfg_base = info->cfg_base; /* In ali_ircc_probe_53 assign */
- self->io.fir_base = info->fir_base; /* info->sir_base = info->fir_base */
- self->io.sir_base = info->sir_base; /* ALi SIR and FIR use the same address */
- self->io.irq = info->irq;
- self->io.fir_ext = CHIP_IO_EXTENT;
- self->io.dma = info->dma;
- self->io.fifo_size = 16; /* SIR: 16, FIR: 32 Benjamin 2000/11/1 */
-
- /* Reserve the ioports that we need */
- if (!request_region(self->io.fir_base, self->io.fir_ext,
- ALI_IRCC_DRIVER_NAME)) {
- net_warn_ratelimited("%s(), can't get iobase of 0x%03x\n",
- __func__, self->io.fir_base);
- err = -ENODEV;
- goto err_out1;
- }
-
- /* Initialize QoS for this device */
- irda_init_max_qos_capabilies(&self->qos);
-
- /* The only value we must override it the baudrate */
- self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|
- IR_115200|IR_576000|IR_1152000|(IR_4000000 << 8); // benjamin 2000/11/8 05:27PM
-
- self->qos.min_turn_time.bits = qos_mtt_bits;
-
- irda_qos_bits_to_value(&self->qos);
-
- /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */
- self->rx_buff.truesize = 14384;
- self->tx_buff.truesize = 14384;
-
- /* Allocate memory if needed */
- self->rx_buff.head =
- dma_zalloc_coherent(NULL, self->rx_buff.truesize,
- &self->rx_buff_dma, GFP_KERNEL);
- if (self->rx_buff.head == NULL) {
- err = -ENOMEM;
- goto err_out2;
- }
-
- self->tx_buff.head =
- dma_zalloc_coherent(NULL, self->tx_buff.truesize,
- &self->tx_buff_dma, GFP_KERNEL);
- if (self->tx_buff.head == NULL) {
- err = -ENOMEM;
- goto err_out3;
- }
-
- self->rx_buff.in_frame = FALSE;
- self->rx_buff.state = OUTSIDE_FRAME;
- self->tx_buff.data = self->tx_buff.head;
- self->rx_buff.data = self->rx_buff.head;
-
- /* Reset Tx queue info */
- self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0;
- self->tx_fifo.tail = self->tx_buff.head;
-
- /* Override the network functions we need to use */
- dev->netdev_ops = &ali_ircc_sir_ops;
-
- err = register_netdev(dev);
- if (err) {
- net_err_ratelimited("%s(), register_netdev() failed!\n",
- __func__);
- goto err_out4;
- }
- net_info_ratelimited("IrDA: Registered device %s\n", dev->name);
-
- /* Check dongle id */
- dongle_id = ali_ircc_read_dongle_id(i, info);
- net_info_ratelimited("%s(), %s, Found dongle: %s\n",
- __func__, ALI_IRCC_DRIVER_NAME,
- dongle_types[dongle_id]);
-
- self->io.dongle_id = dongle_id;
-
-
- return 0;
-
- err_out4:
- dma_free_coherent(NULL, self->tx_buff.truesize,
- self->tx_buff.head, self->tx_buff_dma);
- err_out3:
- dma_free_coherent(NULL, self->rx_buff.truesize,
- self->rx_buff.head, self->rx_buff_dma);
- err_out2:
- release_region(self->io.fir_base, self->io.fir_ext);
- err_out1:
- dev_self[i] = NULL;
- free_netdev(dev);
- return err;
-}
-
-
-/*
- * Function ali_ircc_close (self)
- *
- * Close driver instance
- *
- */
-static int __exit ali_ircc_close(struct ali_ircc_cb *self)
-{
- int iobase;
-
- IRDA_ASSERT(self != NULL, return -1;);
-
- iobase = self->io.fir_base;
-
- /* Remove netdevice */
- unregister_netdev(self->netdev);
-
- /* Release the PORT that this driver is using */
- pr_debug("%s(), Releasing Region %03x\n", __func__, self->io.fir_base);
- release_region(self->io.fir_base, self->io.fir_ext);
-
- if (self->tx_buff.head)
- dma_free_coherent(NULL, self->tx_buff.truesize,
- self->tx_buff.head, self->tx_buff_dma);
-
- if (self->rx_buff.head)
- dma_free_coherent(NULL, self->rx_buff.truesize,
- self->rx_buff.head, self->rx_buff_dma);
-
- dev_self[self->index] = NULL;
- free_netdev(self->netdev);
-
-
- return 0;
-}
-
-/*
- * Function ali_ircc_init_43 (chip, info)
- *
- * Initialize the ALi M1543 chip.
- */
-static int ali_ircc_init_43(ali_chip_t *chip, chipio_t *info)
-{
- /* All controller information like I/O address, DMA channel, IRQ
- * are set by BIOS
- */
-
- return 0;
-}
-
-/*
- * Function ali_ircc_init_53 (chip, info)
- *
- * Initialize the ALi M1535 chip.
- */
-static int ali_ircc_init_53(ali_chip_t *chip, chipio_t *info)
-{
- /* All controller information like I/O address, DMA channel, IRQ
- * are set by BIOS
- */
-
- return 0;
-}
-
-/*
- * Function ali_ircc_probe_53 (chip, info)
- *
- * Probes for the ALi M1535D or M1535
- */
-static int ali_ircc_probe_53(ali_chip_t *chip, chipio_t *info)
-{
- int cfg_base = info->cfg_base;
- int hi, low, reg;
-
-
- /* Enter Configuration */
- outb(chip->entr1, cfg_base);
- outb(chip->entr2, cfg_base);
-
- /* Select Logical Device 5 Registers (UART2) */
- outb(0x07, cfg_base);
- outb(0x05, cfg_base+1);
-
- /* Read address control register */
- outb(0x60, cfg_base);
- hi = inb(cfg_base+1);
- outb(0x61, cfg_base);
- low = inb(cfg_base+1);
- info->fir_base = (hi<<8) + low;
-
- info->sir_base = info->fir_base;
-
- pr_debug("%s(), probing fir_base=0x%03x\n", __func__, info->fir_base);
-
- /* Read IRQ control register */
- outb(0x70, cfg_base);
- reg = inb(cfg_base+1);
- info->irq = reg & 0x0f;
- pr_debug("%s(), probing irq=%d\n", __func__, info->irq);
-
- /* Read DMA channel */
- outb(0x74, cfg_base);
- reg = inb(cfg_base+1);
- info->dma = reg & 0x07;
-
- if(info->dma == 0x04)
- net_warn_ratelimited("%s(), No DMA channel assigned !\n",
- __func__);
- else
- pr_debug("%s(), probing dma=%d\n", __func__, info->dma);
-
- /* Read Enabled Status */
- outb(0x30, cfg_base);
- reg = inb(cfg_base+1);
- info->enabled = (reg & 0x80) && (reg & 0x01);
- pr_debug("%s(), probing enabled=%d\n", __func__, info->enabled);
-
- /* Read Power Status */
- outb(0x22, cfg_base);
- reg = inb(cfg_base+1);
- info->suspended = (reg & 0x20);
- pr_debug("%s(), probing suspended=%d\n", __func__, info->suspended);
-
- /* Exit configuration */
- outb(0xbb, cfg_base);
-
-
- return 0;
-}
-
-/*
- * Function ali_ircc_setup (info)
- *
- * Set FIR FIFO and DMA Threshold
- * Returns non-negative on success.
- *
- */
-static int ali_ircc_setup(chipio_t *info)
-{
- unsigned char tmp;
- int version;
- int iobase = info->fir_base;
-
-
- /* Locking comments :
- * Most operations here need to be protected. We are called before
- * the device instance is created in ali_ircc_open(), therefore
- * nobody can bother us - Jean II */
-
- /* Switch to FIR space */
- SIR2FIR(iobase);
-
- /* Master Reset */
- outb(0x40, iobase+FIR_MCR); // benjamin 2000/11/30 11:45AM
-
- /* Read FIR ID Version Register */
- switch_bank(iobase, BANK3);
- version = inb(iobase+FIR_ID_VR);
-
- /* Should be 0x00 in the M1535/M1535D */
- if(version != 0x00)
- {
- net_err_ratelimited("%s, Wrong chip version %02x\n",
- ALI_IRCC_DRIVER_NAME, version);
- return -1;
- }
-
- /* Set FIR FIFO Threshold Register */
- switch_bank(iobase, BANK1);
- outb(RX_FIFO_Threshold, iobase+FIR_FIFO_TR);
-
- /* Set FIR DMA Threshold Register */
- outb(RX_DMA_Threshold, iobase+FIR_DMA_TR);
-
- /* CRC enable */
- switch_bank(iobase, BANK2);
- outb(inb(iobase+FIR_IRDA_CR) | IRDA_CR_CRC, iobase+FIR_IRDA_CR);
-
- /* NDIS driver set TX Length here BANK2 Alias 3, Alias4*/
-
- /* Switch to Bank 0 */
- switch_bank(iobase, BANK0);
-
- tmp = inb(iobase+FIR_LCR_B);
- tmp &=~0x20; // disable SIP
- tmp |= 0x80; // these two steps make RX mode
- tmp &= 0xbf;
- outb(tmp, iobase+FIR_LCR_B);
-
- /* Disable Interrupt */
- outb(0x00, iobase+FIR_IER);
-
-
- /* Switch to SIR space */
- FIR2SIR(iobase);
-
- net_info_ratelimited("%s, driver loaded (Benjamin Kong)\n",
- ALI_IRCC_DRIVER_NAME);
-
- /* Enable receive interrupts */
- // outb(UART_IER_RDI, iobase+UART_IER); //benjamin 2000/11/23 01:25PM
- // Turn on the interrupts in ali_ircc_net_open
-
-
- return 0;
-}
-
-/*
- * Function ali_ircc_read_dongle_id (int index, info)
- *
- * Try to read dongle identification. This procedure needs to be executed
- * once after power-on/reset. It also needs to be used whenever you suspect
- * that the user may have plugged/unplugged the IrDA Dongle.
- */
-static int ali_ircc_read_dongle_id (int i, chipio_t *info)
-{
- int dongle_id, reg;
- int cfg_base = info->cfg_base;
-
-
- /* Enter Configuration */
- outb(chips[i].entr1, cfg_base);
- outb(chips[i].entr2, cfg_base);
-
- /* Select Logical Device 5 Registers (UART2) */
- outb(0x07, cfg_base);
- outb(0x05, cfg_base+1);
-
- /* Read Dongle ID */
- outb(0xf0, cfg_base);
- reg = inb(cfg_base+1);
- dongle_id = ((reg>>6)&0x02) | ((reg>>5)&0x01);
- pr_debug("%s(), probing dongle_id=%d, dongle_types=%s\n",
- __func__, dongle_id, dongle_types[dongle_id]);
-
- /* Exit configuration */
- outb(0xbb, cfg_base);
-
-
- return dongle_id;
-}
-
-/*
- * Function ali_ircc_interrupt (irq, dev_id, regs)
- *
- * An interrupt from the chip has arrived. Time to do some work
- *
- */
-static irqreturn_t ali_ircc_interrupt(int irq, void *dev_id)
-{
- struct net_device *dev = dev_id;
- struct ali_ircc_cb *self;
- int ret;
-
-
- self = netdev_priv(dev);
-
- spin_lock(&self->lock);
-
- /* Dispatch interrupt handler for the current speed */
- if (self->io.speed > 115200)
- ret = ali_ircc_fir_interrupt(self);
- else
- ret = ali_ircc_sir_interrupt(self);
-
- spin_unlock(&self->lock);
-
- return ret;
-}
-/*
- * Function ali_ircc_fir_interrupt(irq, struct ali_ircc_cb *self)
- *
- * Handle MIR/FIR interrupt
- *
- */
-static irqreturn_t ali_ircc_fir_interrupt(struct ali_ircc_cb *self)
-{
- __u8 eir, OldMessageCount;
- int iobase, tmp;
-
-
- iobase = self->io.fir_base;
-
- switch_bank(iobase, BANK0);
- self->InterruptID = inb(iobase+FIR_IIR);
- self->BusStatus = inb(iobase+FIR_BSR);
-
- OldMessageCount = (self->LineStatus + 1) & 0x07;
- self->LineStatus = inb(iobase+FIR_LSR);
- //self->ier = inb(iobase+FIR_IER); 2000/12/1 04:32PM
- eir = self->InterruptID & self->ier; /* Mask out the interesting ones */
-
- pr_debug("%s(), self->InterruptID = %x\n", __func__, self->InterruptID);
- pr_debug("%s(), self->LineStatus = %x\n", __func__, self->LineStatus);
- pr_debug("%s(), self->ier = %x\n", __func__, self->ier);
- pr_debug("%s(), eir = %x\n", __func__, eir);
-
- /* Disable interrupts */
- SetCOMInterrupts(self, FALSE);
-
- /* Tx or Rx Interrupt */
-
- if (eir & IIR_EOM)
- {
- if (self->io.direction == IO_XMIT) /* TX */
- {
- pr_debug("%s(), ******* IIR_EOM (Tx) *******\n",
- __func__);
-
- if(ali_ircc_dma_xmit_complete(self))
- {
- if (irda_device_txqueue_empty(self->netdev))
- {
- /* Prepare for receive */
- ali_ircc_dma_receive(self);
- self->ier = IER_EOM;
- }
- }
- else
- {
- self->ier = IER_EOM;
- }
-
- }
- else /* RX */
- {
- pr_debug("%s(), ******* IIR_EOM (Rx) *******\n",
- __func__);
-
- if(OldMessageCount > ((self->LineStatus+1) & 0x07))
- {
- self->rcvFramesOverflow = TRUE;
- pr_debug("%s(), ******* self->rcvFramesOverflow = TRUE ********\n",
- __func__);
- }
-
- if (ali_ircc_dma_receive_complete(self))
- {
- pr_debug("%s(), ******* receive complete ********\n",
- __func__);
-
- self->ier = IER_EOM;
- }
- else
- {
- pr_debug("%s(), ******* Not receive complete ********\n",
- __func__);
-
- self->ier = IER_EOM | IER_TIMER;
- }
-
- }
- }
- /* Timer Interrupt */
- else if (eir & IIR_TIMER)
- {
- if(OldMessageCount > ((self->LineStatus+1) & 0x07))
- {
- self->rcvFramesOverflow = TRUE;
- pr_debug("%s(), ******* self->rcvFramesOverflow = TRUE *******\n",
- __func__);
- }
- /* Disable Timer */
- switch_bank(iobase, BANK1);
- tmp = inb(iobase+FIR_CR);
- outb( tmp& ~CR_TIMER_EN, iobase+FIR_CR);
-
- /* Check if this is a Tx timer interrupt */
- if (self->io.direction == IO_XMIT)
- {
- ali_ircc_dma_xmit(self);
-
- /* Interrupt on EOM */
- self->ier = IER_EOM;
-
- }
- else /* Rx */
- {
- if(ali_ircc_dma_receive_complete(self))
- {
- self->ier = IER_EOM;
- }
- else
- {
- self->ier = IER_EOM | IER_TIMER;
- }
- }
- }
-
- /* Restore Interrupt */
- SetCOMInterrupts(self, TRUE);
-
- return IRQ_RETVAL(eir);
-}
-
-/*
- * Function ali_ircc_sir_interrupt (irq, self, eir)
- *
- * Handle SIR interrupt
- *
- */
-static irqreturn_t ali_ircc_sir_interrupt(struct ali_ircc_cb *self)
-{
- int iobase;
- int iir, lsr;
-
-
- iobase = self->io.sir_base;
-
- iir = inb(iobase+UART_IIR) & UART_IIR_ID;
- if (iir) {
- /* Clear interrupt */
- lsr = inb(iobase+UART_LSR);
-
- pr_debug("%s(), iir=%02x, lsr=%02x, iobase=%#x\n",
- __func__, iir, lsr, iobase);
-
- switch (iir)
- {
- case UART_IIR_RLSI:
- pr_debug("%s(), RLSI\n", __func__);
- break;
- case UART_IIR_RDI:
- /* Receive interrupt */
- ali_ircc_sir_receive(self);
- break;
- case UART_IIR_THRI:
- if (lsr & UART_LSR_THRE)
- {
- /* Transmitter ready for data */
- ali_ircc_sir_write_wakeup(self);
- }
- break;
- default:
- pr_debug("%s(), unhandled IIR=%#x\n",
- __func__, iir);
- break;
- }
-
- }
-
-
- return IRQ_RETVAL(iir);
-}
-
-
-/*
- * Function ali_ircc_sir_receive (self)
- *
- * Receive one frame from the infrared port
- *
- */
-static void ali_ircc_sir_receive(struct ali_ircc_cb *self)
-{
- int boguscount = 0;
- int iobase;
-
- IRDA_ASSERT(self != NULL, return;);
-
- iobase = self->io.sir_base;
-
- /*
- * Receive all characters in Rx FIFO, unwrap and unstuff them.
- * async_unwrap_char will deliver all found frames
- */
- do {
- async_unwrap_char(self->netdev, &self->netdev->stats, &self->rx_buff,
- inb(iobase+UART_RX));
-
- /* Make sure we don't stay here too long */
- if (boguscount++ > 32) {
- pr_debug("%s(), breaking!\n", __func__);
- break;
- }
- } while (inb(iobase+UART_LSR) & UART_LSR_DR);
-
-}
-
-/*
- * Function ali_ircc_sir_write_wakeup (tty)
- *
- * Called by the driver when there's room for more data. If we have
- * more packets to send, we send them here.
- *
- */
-static void ali_ircc_sir_write_wakeup(struct ali_ircc_cb *self)
-{
- int actual = 0;
- int iobase;
-
- IRDA_ASSERT(self != NULL, return;);
-
-
- iobase = self->io.sir_base;
-
- /* Finished with frame? */
- if (self->tx_buff.len > 0)
- {
- /* Write data left in transmit buffer */
- actual = ali_ircc_sir_write(iobase, self->io.fifo_size,
- self->tx_buff.data, self->tx_buff.len);
- self->tx_buff.data += actual;
- self->tx_buff.len -= actual;
- }
- else
- {
- if (self->new_speed)
- {
- /* We must wait until all data are gone */
- while(!(inb(iobase+UART_LSR) & UART_LSR_TEMT))
- pr_debug("%s(), UART_LSR_THRE\n", __func__);
-
- pr_debug("%s(), Changing speed! self->new_speed = %d\n",
- __func__, self->new_speed);
- ali_ircc_change_speed(self, self->new_speed);
- self->new_speed = 0;
-
- // benjamin 2000/11/10 06:32PM
- if (self->io.speed > 115200)
- {
- pr_debug("%s(), ali_ircc_change_speed from UART_LSR_TEMT\n",
- __func__);
-
- self->ier = IER_EOM;
- // SetCOMInterrupts(self, TRUE);
- return;
- }
- }
- else
- {
- netif_wake_queue(self->netdev);
- }
-
- self->netdev->stats.tx_packets++;
-
- /* Turn on receive interrupts */
- outb(UART_IER_RDI, iobase+UART_IER);
- }
-
-}
-
-static void ali_ircc_change_speed(struct ali_ircc_cb *self, __u32 baud)
-{
- struct net_device *dev = self->netdev;
- int iobase;
-
-
- pr_debug("%s(), setting speed = %d\n", __func__, baud);
-
- /* This function *must* be called with irq off and spin-lock.
- * - Jean II */
-
- iobase = self->io.fir_base;
-
- SetCOMInterrupts(self, FALSE); // 2000/11/24 11:43AM
-
- /* Go to MIR, FIR Speed */
- if (baud > 115200)
- {
-
-
- ali_ircc_fir_change_speed(self, baud);
-
- /* Install FIR xmit handler*/
- dev->netdev_ops = &ali_ircc_fir_ops;
-
- /* Enable Interuupt */
- self->ier = IER_EOM; // benjamin 2000/11/20 07:24PM
-
- /* Be ready for incoming frames */
- ali_ircc_dma_receive(self); // benajmin 2000/11/8 07:46PM not complete
- }
- /* Go to SIR Speed */
- else
- {
- ali_ircc_sir_change_speed(self, baud);
-
- /* Install SIR xmit handler*/
- dev->netdev_ops = &ali_ircc_sir_ops;
- }
-
-
- SetCOMInterrupts(self, TRUE); // 2000/11/24 11:43AM
-
- netif_wake_queue(self->netdev);
-
-}
-
-static void ali_ircc_fir_change_speed(struct ali_ircc_cb *priv, __u32 baud)
-{
-
- int iobase;
- struct ali_ircc_cb *self = priv;
- struct net_device *dev;
-
-
- IRDA_ASSERT(self != NULL, return;);
-
- dev = self->netdev;
- iobase = self->io.fir_base;
-
- pr_debug("%s(), self->io.speed = %d, change to speed = %d\n",
- __func__, self->io.speed, baud);
-
- /* Come from SIR speed */
- if(self->io.speed <=115200)
- {
- SIR2FIR(iobase);
- }
-
- /* Update accounting for new speed */
- self->io.speed = baud;
-
- // Set Dongle Speed mode
- ali_ircc_change_dongle_speed(self, baud);
-
-}
-
-/*
- * Function ali_sir_change_speed (self, speed)
- *
- * Set speed of IrDA port to specified baudrate
- *
- */
-static void ali_ircc_sir_change_speed(struct ali_ircc_cb *priv, __u32 speed)
-{
- struct ali_ircc_cb *self = priv;
- int iobase;
- int fcr; /* FIFO control reg */
- int lcr; /* Line control reg */
- int divisor;
-
-
- pr_debug("%s(), Setting speed to: %d\n", __func__, speed);
-
- IRDA_ASSERT(self != NULL, return;);
-
- iobase = self->io.sir_base;
-
- /* Come from MIR or FIR speed */
- if(self->io.speed >115200)
- {
- // Set Dongle Speed mode first
- ali_ircc_change_dongle_speed(self, speed);
-
- FIR2SIR(iobase);
- }
-
- // Clear Line and Auxiluary status registers 2000/11/24 11:47AM
-
- inb(iobase+UART_LSR);
- inb(iobase+UART_SCR);
-
- /* Update accounting for new speed */
- self->io.speed = speed;
-
- divisor = 115200/speed;
-
- fcr = UART_FCR_ENABLE_FIFO;
-
- /*
- * Use trigger level 1 to avoid 3 ms. timeout delay at 9600 bps, and
- * almost 1,7 ms at 19200 bps. At speeds above that we can just forget
- * about this timeout since it will always be fast enough.
- */
- if (self->io.speed < 38400)
- fcr |= UART_FCR_TRIGGER_1;
- else
- fcr |= UART_FCR_TRIGGER_14;
-
- /* IrDA ports use 8N1 */
- lcr = UART_LCR_WLEN8;
-
- outb(UART_LCR_DLAB | lcr, iobase+UART_LCR); /* Set DLAB */
- outb(divisor & 0xff, iobase+UART_DLL); /* Set speed */
- outb(divisor >> 8, iobase+UART_DLM);
- outb(lcr, iobase+UART_LCR); /* Set 8N1 */
- outb(fcr, iobase+UART_FCR); /* Enable FIFO's */
-
- /* without this, the connection will be broken after come back from FIR speed,
- but with this, the SIR connection is harder to established */
- outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase+UART_MCR);
-}
-
-static void ali_ircc_change_dongle_speed(struct ali_ircc_cb *priv, int speed)
-{
-
- struct ali_ircc_cb *self = priv;
- int iobase,dongle_id;
- int tmp = 0;
-
-
- iobase = self->io.fir_base; /* or iobase = self->io.sir_base; */
- dongle_id = self->io.dongle_id;
-
- /* We are already locked, no need to do it again */
-
- pr_debug("%s(), Set Speed for %s , Speed = %d\n",
- __func__, dongle_types[dongle_id], speed);
-
- switch_bank(iobase, BANK2);
- tmp = inb(iobase+FIR_IRDA_CR);
-
- /* IBM type dongle */
- if(dongle_id == 0)
- {
- if(speed == 4000000)
- {
- // __ __
- // SD/MODE __| |__ __
- // __ __
- // IRTX __ __| |__
- // T1 T2 T3 T4 T5
-
- tmp &= ~IRDA_CR_HDLC; // HDLC=0
- tmp |= IRDA_CR_CRC; // CRC=1
-
- switch_bank(iobase, BANK2);
- outb(tmp, iobase+FIR_IRDA_CR);
-
- // T1 -> SD/MODE:0 IRTX:0
- tmp &= ~0x09;
- tmp |= 0x02;
- outb(tmp, iobase+FIR_IRDA_CR);
- udelay(2);
-
- // T2 -> SD/MODE:1 IRTX:0
- tmp &= ~0x01;
- tmp |= 0x0a;
- outb(tmp, iobase+FIR_IRDA_CR);
- udelay(2);
-
- // T3 -> SD/MODE:1 IRTX:1
- tmp |= 0x0b;
- outb(tmp, iobase+FIR_IRDA_CR);
- udelay(2);
-
- // T4 -> SD/MODE:0 IRTX:1
- tmp &= ~0x08;
- tmp |= 0x03;
- outb(tmp, iobase+FIR_IRDA_CR);
- udelay(2);
-
- // T5 -> SD/MODE:0 IRTX:0
- tmp &= ~0x09;
- tmp |= 0x02;
- outb(tmp, iobase+FIR_IRDA_CR);
- udelay(2);
-
- // reset -> Normal TX output Signal
- outb(tmp & ~0x02, iobase+FIR_IRDA_CR);
- }
- else /* speed <=1152000 */
- {
- // __
- // SD/MODE __| |__
- //
- // IRTX ________
- // T1 T2 T3
-
- /* MIR 115200, 57600 */
- if (speed==1152000)
- {
- tmp |= 0xA0; //HDLC=1, 1.152Mbps=1
- }
- else
- {
- tmp &=~0x80; //HDLC 0.576Mbps
- tmp |= 0x20; //HDLC=1,
- }
-
- tmp |= IRDA_CR_CRC; // CRC=1
-
- switch_bank(iobase, BANK2);
- outb(tmp, iobase+FIR_IRDA_CR);
-
- /* MIR 115200, 57600 */
-
- //switch_bank(iobase, BANK2);
- // T1 -> SD/MODE:0 IRTX:0
- tmp &= ~0x09;
- tmp |= 0x02;
- outb(tmp, iobase+FIR_IRDA_CR);
- udelay(2);
-
- // T2 -> SD/MODE:1 IRTX:0
- tmp &= ~0x01;
- tmp |= 0x0a;
- outb(tmp, iobase+FIR_IRDA_CR);
-
- // T3 -> SD/MODE:0 IRTX:0
- tmp &= ~0x09;
- tmp |= 0x02;
- outb(tmp, iobase+FIR_IRDA_CR);
- udelay(2);
-
- // reset -> Normal TX output Signal
- outb(tmp & ~0x02, iobase+FIR_IRDA_CR);
- }
- }
- else if (dongle_id == 1) /* HP HDSL-3600 */
- {
- switch(speed)
- {
- case 4000000:
- tmp &= ~IRDA_CR_HDLC; // HDLC=0
- break;
-
- case 1152000:
- tmp |= 0xA0; // HDLC=1, 1.152Mbps=1
- break;
-
- case 576000:
- tmp &=~0x80; // HDLC 0.576Mbps
- tmp |= 0x20; // HDLC=1,
- break;
- }
-
- tmp |= IRDA_CR_CRC; // CRC=1
-
- switch_bank(iobase, BANK2);
- outb(tmp, iobase+FIR_IRDA_CR);
- }
- else /* HP HDSL-1100 */
- {
- if(speed <= 115200) /* SIR */
- {
-
- tmp &= ~IRDA_CR_FIR_SIN; // HP sin select = 0
-
- switch_bank(iobase, BANK2);
- outb(tmp, iobase+FIR_IRDA_CR);
- }
- else /* MIR FIR */
- {
-
- switch(speed)
- {
- case 4000000:
- tmp &= ~IRDA_CR_HDLC; // HDLC=0
- break;
-
- case 1152000:
- tmp |= 0xA0; // HDLC=1, 1.152Mbps=1
- break;
-
- case 576000:
- tmp &=~0x80; // HDLC 0.576Mbps
- tmp |= 0x20; // HDLC=1,
- break;
- }
-
- tmp |= IRDA_CR_CRC; // CRC=1
- tmp |= IRDA_CR_FIR_SIN; // HP sin select = 1
-
- switch_bank(iobase, BANK2);
- outb(tmp, iobase+FIR_IRDA_CR);
- }
- }
-
- switch_bank(iobase, BANK0);
-
-}
-
-/*
- * Function ali_ircc_sir_write (driver)
- *
- * Fill Tx FIFO with transmit data
- *
- */
-static int ali_ircc_sir_write(int iobase, int fifo_size, __u8 *buf, int len)
-{
- int actual = 0;
-
-
- /* Tx FIFO should be empty! */
- if (!(inb(iobase+UART_LSR) & UART_LSR_THRE)) {
- pr_debug("%s(), failed, fifo not empty!\n", __func__);
- return 0;
- }
-
- /* Fill FIFO with current frame */
- while ((fifo_size-- > 0) && (actual < len)) {
- /* Transmit next byte */
- outb(buf[actual], iobase+UART_TX);
-
- actual++;
- }
-
- return actual;
-}
-
-/*
- * Function ali_ircc_net_open (dev)
- *
- * Start the device
- *
- */
-static int ali_ircc_net_open(struct net_device *dev)
-{
- struct ali_ircc_cb *self;
- int iobase;
- char hwname[32];
-
-
- IRDA_ASSERT(dev != NULL, return -1;);
-
- self = netdev_priv(dev);
-
- IRDA_ASSERT(self != NULL, return 0;);
-
- iobase = self->io.fir_base;
-
- /* Request IRQ and install Interrupt Handler */
- if (request_irq(self->io.irq, ali_ircc_interrupt, 0, dev->name, dev))
- {
- net_warn_ratelimited("%s, unable to allocate irq=%d\n",
- ALI_IRCC_DRIVER_NAME, self->io.irq);
- return -EAGAIN;
- }
-
- /*
- * Always allocate the DMA channel after the IRQ, and clean up on
- * failure.
- */
- if (request_dma(self->io.dma, dev->name)) {
- net_warn_ratelimited("%s, unable to allocate dma=%d\n",
- ALI_IRCC_DRIVER_NAME, self->io.dma);
- free_irq(self->io.irq, dev);
- return -EAGAIN;
- }
-
- /* Turn on interrups */
- outb(UART_IER_RDI , iobase+UART_IER);
-
- /* Ready to play! */
- netif_start_queue(dev); //benjamin by irport
-
- /* Give self a hardware name */
- sprintf(hwname, "ALI-FIR @ 0x%03x", self->io.fir_base);
-
- /*
- * Open new IrLAP layer instance, now that everything should be
- * initialized properly
- */
- self->irlap = irlap_open(dev, &self->qos, hwname);
-
-
- return 0;
-}
-
-/*
- * Function ali_ircc_net_close (dev)
- *
- * Stop the device
- *
- */
-static int ali_ircc_net_close(struct net_device *dev)
-{
-
- struct ali_ircc_cb *self;
- //int iobase;
-
-
- IRDA_ASSERT(dev != NULL, return -1;);
-
- self = netdev_priv(dev);
- IRDA_ASSERT(self != NULL, return 0;);
-
- /* Stop device */
- netif_stop_queue(dev);
-
- /* Stop and remove instance of IrLAP */
- if (self->irlap)
- irlap_close(self->irlap);
- self->irlap = NULL;
-
- disable_dma(self->io.dma);
-
- /* Disable interrupts */
- SetCOMInterrupts(self, FALSE);
-
- free_irq(self->io.irq, dev);
- free_dma(self->io.dma);
-
-
- return 0;
-}
-
-/*
- * Function ali_ircc_fir_hard_xmit (skb, dev)
- *
- * Transmit the frame
- *
- */
-static netdev_tx_t ali_ircc_fir_hard_xmit(struct sk_buff *skb,
- struct net_device *dev)
-{
- struct ali_ircc_cb *self;
- unsigned long flags;
- int iobase;
- __u32 speed;
- int mtt, diff;
-
-
- self = netdev_priv(dev);
- iobase = self->io.fir_base;
-
- netif_stop_queue(dev);
-
- /* Make sure tests *& speed change are atomic */
- spin_lock_irqsave(&self->lock, flags);
-
- /* Note : you should make sure that speed changes are not going
- * to corrupt any outgoing frame. Look at nsc-ircc for the gory
- * details - Jean II */
-
- /* Check if we need to change the speed */
- speed = irda_get_next_speed(skb);
- if ((speed != self->io.speed) && (speed != -1)) {
- /* Check for empty frame */
- if (!skb->len) {
- ali_ircc_change_speed(self, speed);
- netif_trans_update(dev);
- spin_unlock_irqrestore(&self->lock, flags);
- dev_kfree_skb(skb);
- return NETDEV_TX_OK;
- } else
- self->new_speed = speed;
- }
-
- /* Register and copy this frame to DMA memory */
- self->tx_fifo.queue[self->tx_fifo.free].start = self->tx_fifo.tail;
- self->tx_fifo.queue[self->tx_fifo.free].len = skb->len;
- self->tx_fifo.tail += skb->len;
-
- dev->stats.tx_bytes += skb->len;
-
- skb_copy_from_linear_data(skb, self->tx_fifo.queue[self->tx_fifo.free].start,
- skb->len);
- self->tx_fifo.len++;
- self->tx_fifo.free++;
-
- /* Start transmit only if there is currently no transmit going on */
- if (self->tx_fifo.len == 1)
- {
- /* Check if we must wait the min turn time or not */
- mtt = irda_get_mtt(skb);
-
- if (mtt)
- {
- /* Check how much time we have used already */
- diff = ktime_us_delta(ktime_get(), self->stamp);
- /* self->stamp is set from ali_ircc_dma_receive_complete() */
-
- pr_debug("%s(), ******* diff = %d *******\n",
- __func__, diff);
-
- /* Check if the mtt is larger than the time we have
- * already used by all the protocol processing
- */
- if (mtt > diff)
- {
- mtt -= diff;
-
- /*
- * Use timer if delay larger than 1000 us, and
- * use udelay for smaller values which should
- * be acceptable
- */
- if (mtt > 500)
- {
- /* Adjust for timer resolution */
- mtt = (mtt+250) / 500; /* 4 discard, 5 get advanced, Let's round off */
-
- pr_debug("%s(), ************** mtt = %d ***********\n",
- __func__, mtt);
-
- /* Setup timer */
- if (mtt == 1) /* 500 us */
- {
- switch_bank(iobase, BANK1);
- outb(TIMER_IIR_500, iobase+FIR_TIMER_IIR);
- }
- else if (mtt == 2) /* 1 ms */
- {
- switch_bank(iobase, BANK1);
- outb(TIMER_IIR_1ms, iobase+FIR_TIMER_IIR);
- }
- else /* > 2ms -> 4ms */
- {
- switch_bank(iobase, BANK1);
- outb(TIMER_IIR_2ms, iobase+FIR_TIMER_IIR);
- }
-
-
- /* Start timer */
- outb(inb(iobase+FIR_CR) | CR_TIMER_EN, iobase+FIR_CR);
- self->io.direction = IO_XMIT;
-
- /* Enable timer interrupt */
- self->ier = IER_TIMER;
- SetCOMInterrupts(self, TRUE);
-
- /* Timer will take care of the rest */
- goto out;
- }
- else
- udelay(mtt);
- } // if (if (mtt > diff)
- }// if (mtt)
-
- /* Enable EOM interrupt */
- self->ier = IER_EOM;
- SetCOMInterrupts(self, TRUE);
-
- /* Transmit frame */
- ali_ircc_dma_xmit(self);
- } // if (self->tx_fifo.len == 1)
-
- out:
-
- /* Not busy transmitting anymore if window is not full */
- if (self->tx_fifo.free < MAX_TX_WINDOW)
- netif_wake_queue(self->netdev);
-
- /* Restore bank register */
- switch_bank(iobase, BANK0);
-
- netif_trans_update(dev);
- spin_unlock_irqrestore(&self->lock, flags);
- dev_kfree_skb(skb);
-
- return NETDEV_TX_OK;
-}
-
-
-static void ali_ircc_dma_xmit(struct ali_ircc_cb *self)
-{
- int iobase, tmp;
- unsigned char FIFO_OPTI, Hi, Lo;
-
-
-
- iobase = self->io.fir_base;
-
- /* FIFO threshold , this method comes from NDIS5 code */
-
- if(self->tx_fifo.queue[self->tx_fifo.ptr].len < TX_FIFO_Threshold)
- FIFO_OPTI = self->tx_fifo.queue[self->tx_fifo.ptr].len-1;
- else
- FIFO_OPTI = TX_FIFO_Threshold;
-
- /* Disable DMA */
- switch_bank(iobase, BANK1);
- outb(inb(iobase+FIR_CR) & ~CR_DMA_EN, iobase+FIR_CR);
-
- self->io.direction = IO_XMIT;
-
- irda_setup_dma(self->io.dma,
- ((u8 *)self->tx_fifo.queue[self->tx_fifo.ptr].start -
- self->tx_buff.head) + self->tx_buff_dma,
- self->tx_fifo.queue[self->tx_fifo.ptr].len,
- DMA_TX_MODE);
-
- /* Reset Tx FIFO */
- switch_bank(iobase, BANK0);
- outb(LCR_A_FIFO_RESET, iobase+FIR_LCR_A);
-
- /* Set Tx FIFO threshold */
- if (self->fifo_opti_buf!=FIFO_OPTI)
- {
- switch_bank(iobase, BANK1);
- outb(FIFO_OPTI, iobase+FIR_FIFO_TR) ;
- self->fifo_opti_buf=FIFO_OPTI;
- }
-
- /* Set Tx DMA threshold */
- switch_bank(iobase, BANK1);
- outb(TX_DMA_Threshold, iobase+FIR_DMA_TR);
-
- /* Set max Tx frame size */
- Hi = (self->tx_fifo.queue[self->tx_fifo.ptr].len >> 8) & 0x0f;
- Lo = self->tx_fifo.queue[self->tx_fifo.ptr].len & 0xff;
- switch_bank(iobase, BANK2);
- outb(Hi, iobase+FIR_TX_DSR_HI);
- outb(Lo, iobase+FIR_TX_DSR_LO);
-
- /* Disable SIP , Disable Brick Wall (we don't support in TX mode), Change to TX mode */
- switch_bank(iobase, BANK0);
- tmp = inb(iobase+FIR_LCR_B);
- tmp &= ~0x20; // Disable SIP
- outb(((unsigned char)(tmp & 0x3f) | LCR_B_TX_MODE) & ~LCR_B_BW, iobase+FIR_LCR_B);
- pr_debug("%s(), *** Change to TX mode: FIR_LCR_B = 0x%x ***\n",
- __func__, inb(iobase + FIR_LCR_B));
-
- outb(0, iobase+FIR_LSR);
-
- /* Enable DMA and Burst Mode */
- switch_bank(iobase, BANK1);
- outb(inb(iobase+FIR_CR) | CR_DMA_EN | CR_DMA_BURST, iobase+FIR_CR);
-
- switch_bank(iobase, BANK0);
-
-}
-
-static int ali_ircc_dma_xmit_complete(struct ali_ircc_cb *self)
-{
- int iobase;
- int ret = TRUE;
-
-
- iobase = self->io.fir_base;
-
- /* Disable DMA */
- switch_bank(iobase, BANK1);
- outb(inb(iobase+FIR_CR) & ~CR_DMA_EN, iobase+FIR_CR);
-
- /* Check for underrun! */
- switch_bank(iobase, BANK0);
- if((inb(iobase+FIR_LSR) & LSR_FRAME_ABORT) == LSR_FRAME_ABORT)
-
- {
- net_err_ratelimited("%s(), ********* LSR_FRAME_ABORT *********\n",
- __func__);
- self->netdev->stats.tx_errors++;
- self->netdev->stats.tx_fifo_errors++;
- }
- else
- {
- self->netdev->stats.tx_packets++;
- }
-
- /* Check if we need to change the speed */
- if (self->new_speed)
- {
- ali_ircc_change_speed(self, self->new_speed);
- self->new_speed = 0;
- }
-
- /* Finished with this frame, so prepare for next */
- self->tx_fifo.ptr++;
- self->tx_fifo.len--;
-
- /* Any frames to be sent back-to-back? */
- if (self->tx_fifo.len)
- {
- ali_ircc_dma_xmit(self);
-
- /* Not finished yet! */
- ret = FALSE;
- }
- else
- { /* Reset Tx FIFO info */
- self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0;
- self->tx_fifo.tail = self->tx_buff.head;
- }
-
- /* Make sure we have room for more frames */
- if (self->tx_fifo.free < MAX_TX_WINDOW) {
- /* Not busy transmitting anymore */
- /* Tell the network layer, that we can accept more frames */
- netif_wake_queue(self->netdev);
- }
-
- switch_bank(iobase, BANK0);
-
- return ret;
-}
-
-/*
- * Function ali_ircc_dma_receive (self)
- *
- * Get ready for receiving a frame. The device will initiate a DMA
- * if it starts to receive a frame.
- *
- */
-static int ali_ircc_dma_receive(struct ali_ircc_cb *self)
-{
- int iobase, tmp;
-
-
- iobase = self->io.fir_base;
-
- /* Reset Tx FIFO info */
- self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0;
- self->tx_fifo.tail = self->tx_buff.head;
-
- /* Disable DMA */
- switch_bank(iobase, BANK1);
- outb(inb(iobase+FIR_CR) & ~CR_DMA_EN, iobase+FIR_CR);
-
- /* Reset Message Count */
- switch_bank(iobase, BANK0);
- outb(0x07, iobase+FIR_LSR);
-
- self->rcvFramesOverflow = FALSE;
-
- self->LineStatus = inb(iobase+FIR_LSR) ;
-
- /* Reset Rx FIFO info */
- self->io.direction = IO_RECV;
- self->rx_buff.data = self->rx_buff.head;
-
- /* Reset Rx FIFO */
- // switch_bank(iobase, BANK0);
- outb(LCR_A_FIFO_RESET, iobase+FIR_LCR_A);
-
- self->st_fifo.len = self->st_fifo.pending_bytes = 0;
- self->st_fifo.tail = self->st_fifo.head = 0;
-
- irda_setup_dma(self->io.dma, self->rx_buff_dma, self->rx_buff.truesize,
- DMA_RX_MODE);
-
- /* Set Receive Mode,Brick Wall */
- //switch_bank(iobase, BANK0);
- tmp = inb(iobase+FIR_LCR_B);
- outb((unsigned char)(tmp &0x3f) | LCR_B_RX_MODE | LCR_B_BW , iobase + FIR_LCR_B); // 2000/12/1 05:16PM
- pr_debug("%s(), *** Change To RX mode: FIR_LCR_B = 0x%x ***\n",
- __func__, inb(iobase + FIR_LCR_B));
-
- /* Set Rx Threshold */
- switch_bank(iobase, BANK1);
- outb(RX_FIFO_Threshold, iobase+FIR_FIFO_TR);
- outb(RX_DMA_Threshold, iobase+FIR_DMA_TR);
-
- /* Enable DMA and Burst Mode */
- // switch_bank(iobase, BANK1);
- outb(CR_DMA_EN | CR_DMA_BURST, iobase+FIR_CR);
-
- switch_bank(iobase, BANK0);
- return 0;
-}
-
-static int ali_ircc_dma_receive_complete(struct ali_ircc_cb *self)
-{
- struct st_fifo *st_fifo;
- struct sk_buff *skb;
- __u8 status, MessageCount;
- int len, i, iobase, val;
-
- st_fifo = &self->st_fifo;
- iobase = self->io.fir_base;
-
- switch_bank(iobase, BANK0);
- MessageCount = inb(iobase+ FIR_LSR)&0x07;
-
- if (MessageCount > 0)
- pr_debug("%s(), Message count = %d\n", __func__, MessageCount);
-
- for (i=0; i<=MessageCount; i++)
- {
- /* Bank 0 */
- switch_bank(iobase, BANK0);
- status = inb(iobase+FIR_LSR);
-
- switch_bank(iobase, BANK2);
- len = inb(iobase+FIR_RX_DSR_HI) & 0x0f;
- len = len << 8;
- len |= inb(iobase+FIR_RX_DSR_LO);
-
- pr_debug("%s(), RX Length = 0x%.2x,\n", __func__ , len);
- pr_debug("%s(), RX Status = 0x%.2x,\n", __func__ , status);
-
- if (st_fifo->tail >= MAX_RX_WINDOW) {
- pr_debug("%s(), window is full!\n", __func__);
- continue;
- }
-
- st_fifo->entries[st_fifo->tail].status = status;
- st_fifo->entries[st_fifo->tail].len = len;
- st_fifo->pending_bytes += len;
- st_fifo->tail++;
- st_fifo->len++;
- }
-
- for (i=0; i<=MessageCount; i++)
- {
- /* Get first entry */
- status = st_fifo->entries[st_fifo->head].status;
- len = st_fifo->entries[st_fifo->head].len;
- st_fifo->pending_bytes -= len;
- st_fifo->head++;
- st_fifo->len--;
-
- /* Check for errors */
- if ((status & 0xd8) || self->rcvFramesOverflow || (len==0))
- {
- pr_debug("%s(), ************* RX Errors ************\n",
- __func__);
-
- /* Skip frame */
- self->netdev->stats.rx_errors++;
-
- self->rx_buff.data += len;
-
- if (status & LSR_FIFO_UR)
- {
- self->netdev->stats.rx_frame_errors++;
- pr_debug("%s(), ************* FIFO Errors ************\n",
- __func__);
- }
- if (status & LSR_FRAME_ERROR)
- {
- self->netdev->stats.rx_frame_errors++;
- pr_debug("%s(), ************* FRAME Errors ************\n",
- __func__);
- }
-
- if (status & LSR_CRC_ERROR)
- {
- self->netdev->stats.rx_crc_errors++;
- pr_debug("%s(), ************* CRC Errors ************\n",
- __func__);
- }
-
- if(self->rcvFramesOverflow)
- {
- self->netdev->stats.rx_frame_errors++;
- pr_debug("%s(), ************* Overran DMA buffer ************\n",
- __func__);
- }
- if(len == 0)
- {
- self->netdev->stats.rx_frame_errors++;
- pr_debug("%s(), ********** Receive Frame Size = 0 *********\n",
- __func__);
- }
- }
- else
- {
-
- if (st_fifo->pending_bytes < 32)
- {
- switch_bank(iobase, BANK0);
- val = inb(iobase+FIR_BSR);
- if ((val& BSR_FIFO_NOT_EMPTY)== 0x80)
- {
- pr_debug("%s(), ************* BSR_FIFO_NOT_EMPTY ************\n",
- __func__);
-
- /* Put this entry back in fifo */
- st_fifo->head--;
- st_fifo->len++;
- st_fifo->pending_bytes += len;
- st_fifo->entries[st_fifo->head].status = status;
- st_fifo->entries[st_fifo->head].len = len;
-
- /*
- * DMA not finished yet, so try again
- * later, set timer value, resolution
- * 500 us
- */
-
- switch_bank(iobase, BANK1);
- outb(TIMER_IIR_500, iobase+FIR_TIMER_IIR); // 2001/1/2 05:07PM
-
- /* Enable Timer */
- outb(inb(iobase+FIR_CR) | CR_TIMER_EN, iobase+FIR_CR);
-
- return FALSE; /* I'll be back! */
- }
- }
-
- /*
- * Remember the time we received this frame, so we can
- * reduce the min turn time a bit since we will know
- * how much time we have used for protocol processing
- */
- self->stamp = ktime_get();
-
- skb = dev_alloc_skb(len+1);
- if (!skb) {
- self->netdev->stats.rx_dropped++;
-
- return FALSE;
- }
-
- /* Make sure IP header gets aligned */
- skb_reserve(skb, 1);
-
- /* Copy frame without CRC, CRC is removed by hardware*/
- skb_put(skb, len);
- skb_copy_to_linear_data(skb, self->rx_buff.data, len);
-
- /* Move to next frame */
- self->rx_buff.data += len;
- self->netdev->stats.rx_bytes += len;
- self->netdev->stats.rx_packets++;
-
- skb->dev = self->netdev;
- skb_reset_mac_header(skb);
- skb->protocol = htons(ETH_P_IRDA);
- netif_rx(skb);
- }
- }
-
- switch_bank(iobase, BANK0);
-
- return TRUE;
-}
-
-
-
-/*
- * Function ali_ircc_sir_hard_xmit (skb, dev)
- *
- * Transmit the frame!
- *
- */
-static netdev_tx_t ali_ircc_sir_hard_xmit(struct sk_buff *skb,
- struct net_device *dev)
-{
- struct ali_ircc_cb *self;
- unsigned long flags;
- int iobase;
- __u32 speed;
-
-
- IRDA_ASSERT(dev != NULL, return NETDEV_TX_OK;);
-
- self = netdev_priv(dev);
- IRDA_ASSERT(self != NULL, return NETDEV_TX_OK;);
-
- iobase = self->io.sir_base;
-
- netif_stop_queue(dev);
-
- /* Make sure tests *& speed change are atomic */
- spin_lock_irqsave(&self->lock, flags);
-
- /* Note : you should make sure that speed changes are not going
- * to corrupt any outgoing frame. Look at nsc-ircc for the gory
- * details - Jean II */
-
- /* Check if we need to change the speed */
- speed = irda_get_next_speed(skb);
- if ((speed != self->io.speed) && (speed != -1)) {
- /* Check for empty frame */
- if (!skb->len) {
- ali_ircc_change_speed(self, speed);
- netif_trans_update(dev);
- spin_unlock_irqrestore(&self->lock, flags);
- dev_kfree_skb(skb);
- return NETDEV_TX_OK;
- } else
- self->new_speed = speed;
- }
-
- /* Init tx buffer */
- self->tx_buff.data = self->tx_buff.head;
-
- /* Copy skb to tx_buff while wrapping, stuffing and making CRC */
- self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data,
- self->tx_buff.truesize);
-
- self->netdev->stats.tx_bytes += self->tx_buff.len;
-
- /* Turn on transmit finished interrupt. Will fire immediately! */
- outb(UART_IER_THRI, iobase+UART_IER);
-
- netif_trans_update(dev);
- spin_unlock_irqrestore(&self->lock, flags);
-
- dev_kfree_skb(skb);
-
-
- return NETDEV_TX_OK;
-}
-
-
-/*
- * Function ali_ircc_net_ioctl (dev, rq, cmd)
- *
- * Process IOCTL commands for this device
- *
- */
-static int ali_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
- struct if_irda_req *irq = (struct if_irda_req *) rq;
- struct ali_ircc_cb *self;
- unsigned long flags;
- int ret = 0;
-
-
- IRDA_ASSERT(dev != NULL, return -1;);
-
- self = netdev_priv(dev);
-
- IRDA_ASSERT(self != NULL, return -1;);
-
- pr_debug("%s(), %s, (cmd=0x%X)\n", __func__ , dev->name, cmd);
-
- switch (cmd) {
- case SIOCSBANDWIDTH: /* Set bandwidth */
- pr_debug("%s(), SIOCSBANDWIDTH\n", __func__);
- /*
- * This function will also be used by IrLAP to change the
- * speed, so we still must allow for speed change within
- * interrupt context.
- */
- if (!in_interrupt() && !capable(CAP_NET_ADMIN))
- return -EPERM;
-
- spin_lock_irqsave(&self->lock, flags);
- ali_ircc_change_speed(self, irq->ifr_baudrate);
- spin_unlock_irqrestore(&self->lock, flags);
- break;
- case SIOCSMEDIABUSY: /* Set media busy */
- pr_debug("%s(), SIOCSMEDIABUSY\n", __func__);
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
- irda_device_set_media_busy(self->netdev, TRUE);
- break;
- case SIOCGRECEIVING: /* Check if we are receiving right now */
- pr_debug("%s(), SIOCGRECEIVING\n", __func__);
- /* This is protected */
- irq->ifr_receiving = ali_ircc_is_receiving(self);
- break;
- default:
- ret = -EOPNOTSUPP;
- }
-
-
- return ret;
-}
-
-/*
- * Function ali_ircc_is_receiving (self)
- *
- * Return TRUE is we are currently receiving a frame
- *
- */
-static int ali_ircc_is_receiving(struct ali_ircc_cb *self)
-{
- unsigned long flags;
- int status = FALSE;
- int iobase;
-
-
- IRDA_ASSERT(self != NULL, return FALSE;);
-
- spin_lock_irqsave(&self->lock, flags);
-
- if (self->io.speed > 115200)
- {
- iobase = self->io.fir_base;
-
- switch_bank(iobase, BANK1);
- if((inb(iobase+FIR_FIFO_FR) & 0x3f) != 0)
- {
- /* We are receiving something */
- pr_debug("%s(), We are receiving something\n",
- __func__);
- status = TRUE;
- }
- switch_bank(iobase, BANK0);
- }
- else
- {
- status = (self->rx_buff.state != OUTSIDE_FRAME);
- }
-
- spin_unlock_irqrestore(&self->lock, flags);
-
-
- return status;
-}
-
-static int ali_ircc_suspend(struct platform_device *dev, pm_message_t state)
-{
- struct ali_ircc_cb *self = platform_get_drvdata(dev);
-
- net_info_ratelimited("%s, Suspending\n", ALI_IRCC_DRIVER_NAME);
-
- if (self->io.suspended)
- return 0;
-
- ali_ircc_net_close(self->netdev);
-
- self->io.suspended = 1;
-
- return 0;
-}
-
-static int ali_ircc_resume(struct platform_device *dev)
-{
- struct ali_ircc_cb *self = platform_get_drvdata(dev);
-
- if (!self->io.suspended)
- return 0;
-
- ali_ircc_net_open(self->netdev);
-
- net_info_ratelimited("%s, Waking up\n", ALI_IRCC_DRIVER_NAME);
-
- self->io.suspended = 0;
-
- return 0;
-}
-
-/* ALi Chip Function */
-
-static void SetCOMInterrupts(struct ali_ircc_cb *self , unsigned char enable)
-{
-
- unsigned char newMask;
-
- int iobase = self->io.fir_base; /* or sir_base */
-
- pr_debug("%s(), -------- Start -------- ( Enable = %d )\n",
- __func__, enable);
-
- /* Enable the interrupt which we wish to */
- if (enable){
- if (self->io.direction == IO_XMIT)
- {
- if (self->io.speed > 115200) /* FIR, MIR */
- {
- newMask = self->ier;
- }
- else /* SIR */
- {
- newMask = UART_IER_THRI | UART_IER_RDI;
- }
- }
- else {
- if (self->io.speed > 115200) /* FIR, MIR */
- {
- newMask = self->ier;
- }
- else /* SIR */
- {
- newMask = UART_IER_RDI;
- }
- }
- }
- else /* Disable all the interrupts */
- {
- newMask = 0x00;
-
- }
-
- //SIR and FIR has different registers
- if (self->io.speed > 115200)
- {
- switch_bank(iobase, BANK0);
- outb(newMask, iobase+FIR_IER);
- }
- else
- outb(newMask, iobase+UART_IER);
-
-}
-
-static void SIR2FIR(int iobase)
-{
- //unsigned char tmp;
-
-
- /* Already protected (change_speed() or setup()), no need to lock.
- * Jean II */
-
- outb(0x28, iobase+UART_MCR);
- outb(0x68, iobase+UART_MCR);
- outb(0x88, iobase+UART_MCR);
-
- outb(0x60, iobase+FIR_MCR); /* Master Reset */
- outb(0x20, iobase+FIR_MCR); /* Master Interrupt Enable */
-
- //tmp = inb(iobase+FIR_LCR_B); /* SIP enable */
- //tmp |= 0x20;
- //outb(tmp, iobase+FIR_LCR_B);
-
-}
-
-static void FIR2SIR(int iobase)
-{
- unsigned char val;
-
-
- /* Already protected (change_speed() or setup()), no need to lock.
- * Jean II */
-
- outb(0x20, iobase+FIR_MCR); /* IRQ to low */
- outb(0x00, iobase+UART_IER);
-
- outb(0xA0, iobase+FIR_MCR); /* Don't set master reset */
- outb(0x00, iobase+UART_FCR);
- outb(0x07, iobase+UART_FCR);
-
- val = inb(iobase+UART_RX);
- val = inb(iobase+UART_LSR);
- val = inb(iobase+UART_MSR);
-
-}
-
-MODULE_AUTHOR("Benjamin Kong <benjamin_kong@ali.com.tw>");
-MODULE_DESCRIPTION("ALi FIR Controller Driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" ALI_IRCC_DRIVER_NAME);
-
-
-module_param_hw_array(io, int, ioport, NULL, 0);
-MODULE_PARM_DESC(io, "Base I/O addresses");
-module_param_hw_array(irq, int, irq, NULL, 0);
-MODULE_PARM_DESC(irq, "IRQ lines");
-module_param_hw_array(dma, int, dma, NULL, 0);
-MODULE_PARM_DESC(dma, "DMA channels");
-
-module_init(ali_ircc_init);
-module_exit(ali_ircc_cleanup);
diff --git a/drivers/staging/irda/drivers/ali-ircc.h b/drivers/staging/irda/drivers/ali-ircc.h
deleted file mode 100644
index c2d9747a5108..000000000000
--- a/drivers/staging/irda/drivers/ali-ircc.h
+++ /dev/null
@@ -1,227 +0,0 @@
-/*********************************************************************
- *
- * Filename: ali-ircc.h
- * Version: 0.5
- * Description: Driver for the ALI M1535D and M1543C FIR Controller
- * Status: Experimental.
- * Author: Benjamin Kong <benjamin_kong@ali.com.tw>
- * Created at: 2000/10/16 03:46PM
- * Modified at: 2001/1/3 02:56PM
- * Modified by: Benjamin Kong <benjamin_kong@ali.com.tw>
- *
- * Copyright (c) 2000 Benjamin Kong <benjamin_kong@ali.com.tw>
- * All Rights Reserved
- *
- * 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 ALI_IRCC_H
-#define ALI_IRCC_H
-
-#include <linux/ktime.h>
-
-#include <linux/spinlock.h>
-#include <linux/pm.h>
-#include <linux/types.h>
-#include <asm/io.h>
-
-/* SIR Register */
-/* Usr definition of linux/serial_reg.h */
-
-/* FIR Register */
-#define BANK0 0x20
-#define BANK1 0x21
-#define BANK2 0x22
-#define BANK3 0x23
-
-#define FIR_MCR 0x07 /* Master Control Register */
-
-/* Bank 0 */
-#define FIR_DR 0x00 /* Alias 0, FIR Data Register (R/W) */
-#define FIR_IER 0x01 /* Alias 1, FIR Interrupt Enable Register (R/W) */
-#define FIR_IIR 0x02 /* Alias 2, FIR Interrupt Identification Register (Read only) */
-#define FIR_LCR_A 0x03 /* Alias 3, FIR Line Control Register A (R/W) */
-#define FIR_LCR_B 0x04 /* Alias 4, FIR Line Control Register B (R/W) */
-#define FIR_LSR 0x05 /* Alias 5, FIR Line Status Register (R/W) */
-#define FIR_BSR 0x06 /* Alias 6, FIR Bus Status Register (Read only) */
-
-
- /* Alias 1 */
- #define IER_FIFO 0x10 /* FIR FIFO Interrupt Enable */
- #define IER_TIMER 0x20 /* Timer Interrupt Enable */
- #define IER_EOM 0x40 /* End of Message Interrupt Enable */
- #define IER_ACT 0x80 /* Active Frame Interrupt Enable */
-
- /* Alias 2 */
- #define IIR_FIFO 0x10 /* FIR FIFO Interrupt */
- #define IIR_TIMER 0x20 /* Timer Interrupt */
- #define IIR_EOM 0x40 /* End of Message Interrupt */
- #define IIR_ACT 0x80 /* Active Frame Interrupt */
-
- /* Alias 3 */
- #define LCR_A_FIFO_RESET 0x80 /* FIFO Reset */
-
- /* Alias 4 */
- #define LCR_B_BW 0x10 /* Brick Wall */
- #define LCR_B_SIP 0x20 /* SIP Enable */
- #define LCR_B_TX_MODE 0x40 /* Transmit Mode */
- #define LCR_B_RX_MODE 0x80 /* Receive Mode */
-
- /* Alias 5 */
- #define LSR_FIR_LSA 0x00 /* FIR Line Status Address */
- #define LSR_FRAME_ABORT 0x08 /* Frame Abort */
- #define LSR_CRC_ERROR 0x10 /* CRC Error */
- #define LSR_SIZE_ERROR 0x20 /* Size Error */
- #define LSR_FRAME_ERROR 0x40 /* Frame Error */
- #define LSR_FIFO_UR 0x80 /* FIFO Underrun */
- #define LSR_FIFO_OR 0x80 /* FIFO Overrun */
-
- /* Alias 6 */
- #define BSR_FIFO_NOT_EMPTY 0x80 /* FIFO Not Empty */
-
-/* Bank 1 */
-#define FIR_CR 0x00 /* Alias 0, FIR Configuration Register (R/W) */
-#define FIR_FIFO_TR 0x01 /* Alias 1, FIR FIFO Threshold Register (R/W) */
-#define FIR_DMA_TR 0x02 /* Alias 2, FIR DMA Threshold Register (R/W) */
-#define FIR_TIMER_IIR 0x03 /* Alias 3, FIR Timer interrupt interval register (W/O) */
-#define FIR_FIFO_FR 0x03 /* Alias 3, FIR FIFO Flag register (R/O) */
-#define FIR_FIFO_RAR 0x04 /* Alias 4, FIR FIFO Read Address register (R/O) */
-#define FIR_FIFO_WAR 0x05 /* Alias 5, FIR FIFO Write Address register (R/O) */
-#define FIR_TR 0x06 /* Alias 6, Test REgister (W/O) */
-
- /* Alias 0 */
- #define CR_DMA_EN 0x01 /* DMA Enable */
- #define CR_DMA_BURST 0x02 /* DMA Burst Mode */
- #define CR_TIMER_EN 0x08 /* Timer Enable */
-
- /* Alias 3 */
- #define TIMER_IIR_500 0x00 /* 500 us */
- #define TIMER_IIR_1ms 0x01 /* 1 ms */
- #define TIMER_IIR_2ms 0x02 /* 2 ms */
- #define TIMER_IIR_4ms 0x03 /* 4 ms */
-
-/* Bank 2 */
-#define FIR_IRDA_CR 0x00 /* Alias 0, IrDA Control Register (R/W) */
-#define FIR_BOF_CR 0x01 /* Alias 1, BOF Count Register (R/W) */
-#define FIR_BW_CR 0x02 /* Alias 2, Brick Wall Count Register (R/W) */
-#define FIR_TX_DSR_HI 0x03 /* Alias 3, TX Data Size Register (high) (R/W) */
-#define FIR_TX_DSR_LO 0x04 /* Alias 4, TX Data Size Register (low) (R/W) */
-#define FIR_RX_DSR_HI 0x05 /* Alias 5, RX Data Size Register (high) (R/W) */
-#define FIR_RX_DSR_LO 0x06 /* Alias 6, RX Data Size Register (low) (R/W) */
-
- /* Alias 0 */
- #define IRDA_CR_HDLC1152 0x80 /* 1.152Mbps HDLC Select */
- #define IRDA_CR_CRC 0X40 /* CRC Select. */
- #define IRDA_CR_HDLC 0x20 /* HDLC select. */
- #define IRDA_CR_HP_MODE 0x10 /* HP mode (read only) */
- #define IRDA_CR_SD_ST 0x08 /* SD/MODE State. */
- #define IRDA_CR_FIR_SIN 0x04 /* FIR SIN Select. */
- #define IRDA_CR_ITTX_0 0x02 /* SOUT State. IRTX force to 0 */
- #define IRDA_CR_ITTX_1 0x03 /* SOUT State. IRTX force to 1 */
-
-/* Bank 3 */
-#define FIR_ID_VR 0x00 /* Alias 0, FIR ID Version Register (R/O) */
-#define FIR_MODULE_CR 0x01 /* Alias 1, FIR Module Control Register (R/W) */
-#define FIR_IO_BASE_HI 0x02 /* Alias 2, FIR Higher I/O Base Address Register (R/O) */
-#define FIR_IO_BASE_LO 0x03 /* Alias 3, FIR Lower I/O Base Address Register (R/O) */
-#define FIR_IRQ_CR 0x04 /* Alias 4, FIR IRQ Channel Register (R/O) */
-#define FIR_DMA_CR 0x05 /* Alias 5, FIR DMA Channel Register (R/O) */
-
-struct ali_chip {
- char *name;
- int cfg[2];
- unsigned char entr1;
- unsigned char entr2;
- unsigned char cid_index;
- unsigned char cid_value;
- int (*probe)(struct ali_chip *chip, chipio_t *info);
- int (*init)(struct ali_chip *chip, chipio_t *info);
-};
-typedef struct ali_chip ali_chip_t;
-
-
-/* DMA modes needed */
-#define DMA_TX_MODE 0x08 /* Mem to I/O, ++, demand. */
-#define DMA_RX_MODE 0x04 /* I/O to mem, ++, demand. */
-
-#define MAX_TX_WINDOW 7
-#define MAX_RX_WINDOW 7
-
-#define TX_FIFO_Threshold 8
-#define RX_FIFO_Threshold 1
-#define TX_DMA_Threshold 1
-#define RX_DMA_Threshold 1
-
-/* For storing entries in the status FIFO */
-
-struct st_fifo_entry {
- int status;
- int len;
-};
-
-struct st_fifo {
- struct st_fifo_entry entries[MAX_RX_WINDOW];
- int pending_bytes;
- int head;
- int tail;
- int len;
-};
-
-struct frame_cb {
- void *start; /* Start of frame in DMA mem */
- int len; /* Length of frame in DMA mem */
-};
-
-struct tx_fifo {
- struct frame_cb queue[MAX_TX_WINDOW]; /* Info about frames in queue */
- int ptr; /* Currently being sent */
- int len; /* Length of queue */
- int free; /* Next free slot */
- void *tail; /* Next free start in DMA mem */
-};
-
-/* Private data for each instance */
-struct ali_ircc_cb {
-
- struct st_fifo st_fifo; /* Info about received frames */
- struct tx_fifo tx_fifo; /* Info about frames to be transmitted */
-
- struct net_device *netdev; /* Yes! we are some kind of netdevice */
-
- struct irlap_cb *irlap; /* The link layer we are binded to */
- struct qos_info qos; /* QoS capabilities for this device */
-
- chipio_t io; /* IrDA controller information */
- iobuff_t tx_buff; /* Transmit buffer */
- iobuff_t rx_buff; /* Receive buffer */
- dma_addr_t tx_buff_dma;
- dma_addr_t rx_buff_dma;
-
- __u8 ier; /* Interrupt enable register */
-
- __u8 InterruptID; /* Interrupt ID */
- __u8 BusStatus; /* Bus Status */
- __u8 LineStatus; /* Line Status */
-
- unsigned char rcvFramesOverflow;
-
- ktime_t stamp;
-
- spinlock_t lock; /* For serializing operations */
-
- __u32 new_speed;
- int index; /* Instance index */
-
- unsigned char fifo_opti_buf;
-};
-
-static inline void switch_bank(int iobase, int bank)
-{
- outb(bank, iobase+FIR_MCR);
-}
-
-#endif /* ALI_IRCC_H */
diff --git a/drivers/staging/irda/drivers/au1k_ir.c b/drivers/staging/irda/drivers/au1k_ir.c
deleted file mode 100644
index 73e3e4b041bf..000000000000
--- a/drivers/staging/irda/drivers/au1k_ir.c
+++ /dev/null
@@ -1,985 +0,0 @@
-/*
- * Alchemy Semi Au1000 IrDA driver
- *
- * Copyright 2001 MontaVista Software Inc.
- * Author: MontaVista Software, Inc.
- * ppopov@mvista.com or source@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, see <http://www.gnu.org/licenses/>.
- */
-
-#include <linux/clk.h>
-#include <linux/module.h>
-#include <linux/netdevice.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/types.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irmod.h>
-#include <net/irda/wrapper.h>
-#include <net/irda/irda_device.h>
-#include <asm/mach-au1x00/au1000.h>
-
-/* registers */
-#define IR_RING_PTR_STATUS 0x00
-#define IR_RING_BASE_ADDR_H 0x04
-#define IR_RING_BASE_ADDR_L 0x08
-#define IR_RING_SIZE 0x0C
-#define IR_RING_PROMPT 0x10
-#define IR_RING_ADDR_CMPR 0x14
-#define IR_INT_CLEAR 0x18
-#define IR_CONFIG_1 0x20
-#define IR_SIR_FLAGS 0x24
-#define IR_STATUS 0x28
-#define IR_READ_PHY_CONFIG 0x2C
-#define IR_WRITE_PHY_CONFIG 0x30
-#define IR_MAX_PKT_LEN 0x34
-#define IR_RX_BYTE_CNT 0x38
-#define IR_CONFIG_2 0x3C
-#define IR_ENABLE 0x40
-
-/* Config1 */
-#define IR_RX_INVERT_LED (1 << 0)
-#define IR_TX_INVERT_LED (1 << 1)
-#define IR_ST (1 << 2)
-#define IR_SF (1 << 3)
-#define IR_SIR (1 << 4)
-#define IR_MIR (1 << 5)
-#define IR_FIR (1 << 6)
-#define IR_16CRC (1 << 7)
-#define IR_TD (1 << 8)
-#define IR_RX_ALL (1 << 9)
-#define IR_DMA_ENABLE (1 << 10)
-#define IR_RX_ENABLE (1 << 11)
-#define IR_TX_ENABLE (1 << 12)
-#define IR_LOOPBACK (1 << 14)
-#define IR_SIR_MODE (IR_SIR | IR_DMA_ENABLE | \
- IR_RX_ALL | IR_RX_ENABLE | IR_SF | \
- IR_16CRC)
-
-/* ir_status */
-#define IR_RX_STATUS (1 << 9)
-#define IR_TX_STATUS (1 << 10)
-#define IR_PHYEN (1 << 15)
-
-/* ir_write_phy_config */
-#define IR_BR(x) (((x) & 0x3f) << 10) /* baud rate */
-#define IR_PW(x) (((x) & 0x1f) << 5) /* pulse width */
-#define IR_P(x) ((x) & 0x1f) /* preamble bits */
-
-/* Config2 */
-#define IR_MODE_INV (1 << 0)
-#define IR_ONE_PIN (1 << 1)
-#define IR_PHYCLK_40MHZ (0 << 2)
-#define IR_PHYCLK_48MHZ (1 << 2)
-#define IR_PHYCLK_56MHZ (2 << 2)
-#define IR_PHYCLK_64MHZ (3 << 2)
-#define IR_DP (1 << 4)
-#define IR_DA (1 << 5)
-#define IR_FLT_HIGH (0 << 6)
-#define IR_FLT_MEDHI (1 << 6)
-#define IR_FLT_MEDLO (2 << 6)
-#define IR_FLT_LO (3 << 6)
-#define IR_IEN (1 << 8)
-
-/* ir_enable */
-#define IR_HC (1 << 3) /* divide SBUS clock by 2 */
-#define IR_CE (1 << 2) /* clock enable */
-#define IR_C (1 << 1) /* coherency bit */
-#define IR_BE (1 << 0) /* set in big endian mode */
-
-#define NUM_IR_DESC 64
-#define RING_SIZE_4 0x0
-#define RING_SIZE_16 0x3
-#define RING_SIZE_64 0xF
-#define MAX_NUM_IR_DESC 64
-#define MAX_BUF_SIZE 2048
-
-/* Ring descriptor flags */
-#define AU_OWN (1 << 7) /* tx,rx */
-#define IR_DIS_CRC (1 << 6) /* tx */
-#define IR_BAD_CRC (1 << 5) /* tx */
-#define IR_NEED_PULSE (1 << 4) /* tx */
-#define IR_FORCE_UNDER (1 << 3) /* tx */
-#define IR_DISABLE_TX (1 << 2) /* tx */
-#define IR_HW_UNDER (1 << 0) /* tx */
-#define IR_TX_ERROR (IR_DIS_CRC | IR_BAD_CRC | IR_HW_UNDER)
-
-#define IR_PHY_ERROR (1 << 6) /* rx */
-#define IR_CRC_ERROR (1 << 5) /* rx */
-#define IR_MAX_LEN (1 << 4) /* rx */
-#define IR_FIFO_OVER (1 << 3) /* rx */
-#define IR_SIR_ERROR (1 << 2) /* rx */
-#define IR_RX_ERROR (IR_PHY_ERROR | IR_CRC_ERROR | \
- IR_MAX_LEN | IR_FIFO_OVER | IR_SIR_ERROR)
-
-struct db_dest {
- struct db_dest *pnext;
- volatile u32 *vaddr;
- dma_addr_t dma_addr;
-};
-
-struct ring_dest {
- u8 count_0; /* 7:0 */
- u8 count_1; /* 12:8 */
- u8 reserved;
- u8 flags;
- u8 addr_0; /* 7:0 */
- u8 addr_1; /* 15:8 */
- u8 addr_2; /* 23:16 */
- u8 addr_3; /* 31:24 */
-};
-
-/* Private data for each instance */
-struct au1k_private {
- void __iomem *iobase;
- int irq_rx, irq_tx;
-
- struct db_dest *pDBfree;
- struct db_dest db[2 * NUM_IR_DESC];
- volatile struct ring_dest *rx_ring[NUM_IR_DESC];
- volatile struct ring_dest *tx_ring[NUM_IR_DESC];
- struct db_dest *rx_db_inuse[NUM_IR_DESC];
- struct db_dest *tx_db_inuse[NUM_IR_DESC];
- u32 rx_head;
- u32 tx_head;
- u32 tx_tail;
- u32 tx_full;
-
- iobuff_t rx_buff;
-
- struct net_device *netdev;
- struct qos_info qos;
- struct irlap_cb *irlap;
-
- u8 open;
- u32 speed;
- u32 newspeed;
-
- struct resource *ioarea;
- struct au1k_irda_platform_data *platdata;
- struct clk *irda_clk;
-};
-
-static int qos_mtt_bits = 0x07; /* 1 ms or more */
-
-static void au1k_irda_plat_set_phy_mode(struct au1k_private *p, int mode)
-{
- if (p->platdata && p->platdata->set_phy_mode)
- p->platdata->set_phy_mode(mode);
-}
-
-static inline unsigned long irda_read(struct au1k_private *p,
- unsigned long ofs)
-{
- /*
- * IrDA peripheral bug. You have to read the register
- * twice to get the right value.
- */
- (void)__raw_readl(p->iobase + ofs);
- return __raw_readl(p->iobase + ofs);
-}
-
-static inline void irda_write(struct au1k_private *p, unsigned long ofs,
- unsigned long val)
-{
- __raw_writel(val, p->iobase + ofs);
- wmb();
-}
-
-/*
- * Buffer allocation/deallocation routines. The buffer descriptor returned
- * has the virtual and dma address of a buffer suitable for
- * both, receive and transmit operations.
- */
-static struct db_dest *GetFreeDB(struct au1k_private *aup)
-{
- struct db_dest *db;
- db = aup->pDBfree;
-
- if (db)
- aup->pDBfree = db->pnext;
- return db;
-}
-
-/*
- DMA memory allocation, derived from pci_alloc_consistent.
- However, the Au1000 data cache is coherent (when programmed
- so), therefore we return KSEG0 address, not KSEG1.
-*/
-static void *dma_alloc(size_t size, dma_addr_t *dma_handle)
-{
- void *ret;
- int gfp = GFP_ATOMIC | GFP_DMA;
-
- ret = (void *)__get_free_pages(gfp, get_order(size));
-
- if (ret != NULL) {
- memset(ret, 0, size);
- *dma_handle = virt_to_bus(ret);
- ret = (void *)KSEG0ADDR(ret);
- }
- return ret;
-}
-
-static void dma_free(void *vaddr, size_t size)
-{
- vaddr = (void *)KSEG0ADDR(vaddr);
- free_pages((unsigned long) vaddr, get_order(size));
-}
-
-
-static void setup_hw_rings(struct au1k_private *aup, u32 rx_base, u32 tx_base)
-{
- int i;
- for (i = 0; i < NUM_IR_DESC; i++) {
- aup->rx_ring[i] = (volatile struct ring_dest *)
- (rx_base + sizeof(struct ring_dest) * i);
- }
- for (i = 0; i < NUM_IR_DESC; i++) {
- aup->tx_ring[i] = (volatile struct ring_dest *)
- (tx_base + sizeof(struct ring_dest) * i);
- }
-}
-
-static int au1k_irda_init_iobuf(iobuff_t *io, int size)
-{
- io->head = kmalloc(size, GFP_KERNEL);
- if (io->head != NULL) {
- io->truesize = size;
- io->in_frame = FALSE;
- io->state = OUTSIDE_FRAME;
- io->data = io->head;
- }
- return io->head ? 0 : -ENOMEM;
-}
-
-/*
- * Set the IrDA communications speed.
- */
-static int au1k_irda_set_speed(struct net_device *dev, int speed)
-{
- struct au1k_private *aup = netdev_priv(dev);
- volatile struct ring_dest *ptxd;
- unsigned long control;
- int ret = 0, timeout = 10, i;
-
- if (speed == aup->speed)
- return ret;
-
- /* disable PHY first */
- au1k_irda_plat_set_phy_mode(aup, AU1000_IRDA_PHY_MODE_OFF);
- irda_write(aup, IR_STATUS, irda_read(aup, IR_STATUS) & ~IR_PHYEN);
-
- /* disable RX/TX */
- irda_write(aup, IR_CONFIG_1,
- irda_read(aup, IR_CONFIG_1) & ~(IR_RX_ENABLE | IR_TX_ENABLE));
- msleep(20);
- while (irda_read(aup, IR_STATUS) & (IR_RX_STATUS | IR_TX_STATUS)) {
- msleep(20);
- if (!timeout--) {
- netdev_err(dev, "rx/tx disable timeout\n");
- break;
- }
- }
-
- /* disable DMA */
- irda_write(aup, IR_CONFIG_1,
- irda_read(aup, IR_CONFIG_1) & ~IR_DMA_ENABLE);
- msleep(20);
-
- /* After we disable tx/rx. the index pointers go back to zero. */
- aup->tx_head = aup->tx_tail = aup->rx_head = 0;
- for (i = 0; i < NUM_IR_DESC; i++) {
- ptxd = aup->tx_ring[i];
- ptxd->flags = 0;
- ptxd->count_0 = 0;
- ptxd->count_1 = 0;
- }
-
- for (i = 0; i < NUM_IR_DESC; i++) {
- ptxd = aup->rx_ring[i];
- ptxd->count_0 = 0;
- ptxd->count_1 = 0;
- ptxd->flags = AU_OWN;
- }
-
- if (speed == 4000000)
- au1k_irda_plat_set_phy_mode(aup, AU1000_IRDA_PHY_MODE_FIR);
- else
- au1k_irda_plat_set_phy_mode(aup, AU1000_IRDA_PHY_MODE_SIR);
-
- switch (speed) {
- case 9600:
- irda_write(aup, IR_WRITE_PHY_CONFIG, IR_BR(11) | IR_PW(12));
- irda_write(aup, IR_CONFIG_1, IR_SIR_MODE);
- break;
- case 19200:
- irda_write(aup, IR_WRITE_PHY_CONFIG, IR_BR(5) | IR_PW(12));
- irda_write(aup, IR_CONFIG_1, IR_SIR_MODE);
- break;
- case 38400:
- irda_write(aup, IR_WRITE_PHY_CONFIG, IR_BR(2) | IR_PW(12));
- irda_write(aup, IR_CONFIG_1, IR_SIR_MODE);
- break;
- case 57600:
- irda_write(aup, IR_WRITE_PHY_CONFIG, IR_BR(1) | IR_PW(12));
- irda_write(aup, IR_CONFIG_1, IR_SIR_MODE);
- break;
- case 115200:
- irda_write(aup, IR_WRITE_PHY_CONFIG, IR_PW(12));
- irda_write(aup, IR_CONFIG_1, IR_SIR_MODE);
- break;
- case 4000000:
- irda_write(aup, IR_WRITE_PHY_CONFIG, IR_P(15));
- irda_write(aup, IR_CONFIG_1, IR_FIR | IR_DMA_ENABLE |
- IR_RX_ENABLE);
- break;
- default:
- netdev_err(dev, "unsupported speed %x\n", speed);
- ret = -EINVAL;
- break;
- }
-
- aup->speed = speed;
- irda_write(aup, IR_STATUS, irda_read(aup, IR_STATUS) | IR_PHYEN);
-
- control = irda_read(aup, IR_STATUS);
- irda_write(aup, IR_RING_PROMPT, 0);
-
- if (control & (1 << 14)) {
- netdev_err(dev, "configuration error\n");
- } else {
- if (control & (1 << 11))
- netdev_debug(dev, "Valid SIR config\n");
- if (control & (1 << 12))
- netdev_debug(dev, "Valid MIR config\n");
- if (control & (1 << 13))
- netdev_debug(dev, "Valid FIR config\n");
- if (control & (1 << 10))
- netdev_debug(dev, "TX enabled\n");
- if (control & (1 << 9))
- netdev_debug(dev, "RX enabled\n");
- }
-
- return ret;
-}
-
-static void update_rx_stats(struct net_device *dev, u32 status, u32 count)
-{
- struct net_device_stats *ps = &dev->stats;
-
- ps->rx_packets++;
-
- if (status & IR_RX_ERROR) {
- ps->rx_errors++;
- if (status & (IR_PHY_ERROR | IR_FIFO_OVER))
- ps->rx_missed_errors++;
- if (status & IR_MAX_LEN)
- ps->rx_length_errors++;
- if (status & IR_CRC_ERROR)
- ps->rx_crc_errors++;
- } else
- ps->rx_bytes += count;
-}
-
-static void update_tx_stats(struct net_device *dev, u32 status, u32 pkt_len)
-{
- struct net_device_stats *ps = &dev->stats;
-
- ps->tx_packets++;
- ps->tx_bytes += pkt_len;
-
- if (status & IR_TX_ERROR) {
- ps->tx_errors++;
- ps->tx_aborted_errors++;
- }
-}
-
-static void au1k_tx_ack(struct net_device *dev)
-{
- struct au1k_private *aup = netdev_priv(dev);
- volatile struct ring_dest *ptxd;
-
- ptxd = aup->tx_ring[aup->tx_tail];
- while (!(ptxd->flags & AU_OWN) && (aup->tx_tail != aup->tx_head)) {
- update_tx_stats(dev, ptxd->flags,
- (ptxd->count_1 << 8) | ptxd->count_0);
- ptxd->count_0 = 0;
- ptxd->count_1 = 0;
- wmb();
- aup->tx_tail = (aup->tx_tail + 1) & (NUM_IR_DESC - 1);
- ptxd = aup->tx_ring[aup->tx_tail];
-
- if (aup->tx_full) {
- aup->tx_full = 0;
- netif_wake_queue(dev);
- }
- }
-
- if (aup->tx_tail == aup->tx_head) {
- if (aup->newspeed) {
- au1k_irda_set_speed(dev, aup->newspeed);
- aup->newspeed = 0;
- } else {
- irda_write(aup, IR_CONFIG_1,
- irda_read(aup, IR_CONFIG_1) & ~IR_TX_ENABLE);
- irda_write(aup, IR_CONFIG_1,
- irda_read(aup, IR_CONFIG_1) | IR_RX_ENABLE);
- irda_write(aup, IR_RING_PROMPT, 0);
- }
- }
-}
-
-static int au1k_irda_rx(struct net_device *dev)
-{
- struct au1k_private *aup = netdev_priv(dev);
- volatile struct ring_dest *prxd;
- struct sk_buff *skb;
- struct db_dest *pDB;
- u32 flags, count;
-
- prxd = aup->rx_ring[aup->rx_head];
- flags = prxd->flags;
-
- while (!(flags & AU_OWN)) {
- pDB = aup->rx_db_inuse[aup->rx_head];
- count = (prxd->count_1 << 8) | prxd->count_0;
- if (!(flags & IR_RX_ERROR)) {
- /* good frame */
- update_rx_stats(dev, flags, count);
- skb = alloc_skb(count + 1, GFP_ATOMIC);
- if (skb == NULL) {
- dev->stats.rx_dropped++;
- continue;
- }
- skb_reserve(skb, 1);
- if (aup->speed == 4000000)
- skb_put(skb, count);
- else
- skb_put(skb, count - 2);
- skb_copy_to_linear_data(skb, (void *)pDB->vaddr,
- count - 2);
- skb->dev = dev;
- skb_reset_mac_header(skb);
- skb->protocol = htons(ETH_P_IRDA);
- netif_rx(skb);
- prxd->count_0 = 0;
- prxd->count_1 = 0;
- }
- prxd->flags |= AU_OWN;
- aup->rx_head = (aup->rx_head + 1) & (NUM_IR_DESC - 1);
- irda_write(aup, IR_RING_PROMPT, 0);
-
- /* next descriptor */
- prxd = aup->rx_ring[aup->rx_head];
- flags = prxd->flags;
-
- }
- return 0;
-}
-
-static irqreturn_t au1k_irda_interrupt(int dummy, void *dev_id)
-{
- struct net_device *dev = dev_id;
- struct au1k_private *aup = netdev_priv(dev);
-
- irda_write(aup, IR_INT_CLEAR, 0); /* ack irda interrupts */
-
- au1k_irda_rx(dev);
- au1k_tx_ack(dev);
-
- return IRQ_HANDLED;
-}
-
-static int au1k_init(struct net_device *dev)
-{
- struct au1k_private *aup = netdev_priv(dev);
- u32 enable, ring_address, phyck;
- struct clk *c;
- int i;
-
- c = clk_get(NULL, "irda_clk");
- if (IS_ERR(c))
- return PTR_ERR(c);
- i = clk_prepare_enable(c);
- if (i) {
- clk_put(c);
- return i;
- }
-
- switch (clk_get_rate(c)) {
- case 40000000:
- phyck = IR_PHYCLK_40MHZ;
- break;
- case 48000000:
- phyck = IR_PHYCLK_48MHZ;
- break;
- case 56000000:
- phyck = IR_PHYCLK_56MHZ;
- break;
- case 64000000:
- phyck = IR_PHYCLK_64MHZ;
- break;
- default:
- clk_disable_unprepare(c);
- clk_put(c);
- return -EINVAL;
- }
- aup->irda_clk = c;
-
- enable = IR_HC | IR_CE | IR_C;
-#ifndef CONFIG_CPU_LITTLE_ENDIAN
- enable |= IR_BE;
-#endif
- aup->tx_head = 0;
- aup->tx_tail = 0;
- aup->rx_head = 0;
-
- for (i = 0; i < NUM_IR_DESC; i++)
- aup->rx_ring[i]->flags = AU_OWN;
-
- irda_write(aup, IR_ENABLE, enable);
- msleep(20);
-
- /* disable PHY */
- au1k_irda_plat_set_phy_mode(aup, AU1000_IRDA_PHY_MODE_OFF);
- irda_write(aup, IR_STATUS, irda_read(aup, IR_STATUS) & ~IR_PHYEN);
- msleep(20);
-
- irda_write(aup, IR_MAX_PKT_LEN, MAX_BUF_SIZE);
-
- ring_address = (u32)virt_to_phys((void *)aup->rx_ring[0]);
- irda_write(aup, IR_RING_BASE_ADDR_H, ring_address >> 26);
- irda_write(aup, IR_RING_BASE_ADDR_L, (ring_address >> 10) & 0xffff);
-
- irda_write(aup, IR_RING_SIZE,
- (RING_SIZE_64 << 8) | (RING_SIZE_64 << 12));
-
- irda_write(aup, IR_CONFIG_2, phyck | IR_ONE_PIN);
- irda_write(aup, IR_RING_ADDR_CMPR, 0);
-
- au1k_irda_set_speed(dev, 9600);
- return 0;
-}
-
-static int au1k_irda_start(struct net_device *dev)
-{
- struct au1k_private *aup = netdev_priv(dev);
- char hwname[32];
- int retval;
-
- retval = au1k_init(dev);
- if (retval) {
- netdev_err(dev, "error in au1k_init\n");
- return retval;
- }
-
- retval = request_irq(aup->irq_tx, &au1k_irda_interrupt, 0,
- dev->name, dev);
- if (retval) {
- netdev_err(dev, "unable to get IRQ %d\n", dev->irq);
- return retval;
- }
- retval = request_irq(aup->irq_rx, &au1k_irda_interrupt, 0,
- dev->name, dev);
- if (retval) {
- free_irq(aup->irq_tx, dev);
- netdev_err(dev, "unable to get IRQ %d\n", dev->irq);
- return retval;
- }
-
- /* Give self a hardware name */
- sprintf(hwname, "Au1000 SIR/FIR");
- aup->irlap = irlap_open(dev, &aup->qos, hwname);
- netif_start_queue(dev);
-
- /* int enable */
- irda_write(aup, IR_CONFIG_2, irda_read(aup, IR_CONFIG_2) | IR_IEN);
-
- /* power up */
- au1k_irda_plat_set_phy_mode(aup, AU1000_IRDA_PHY_MODE_SIR);
-
- return 0;
-}
-
-static int au1k_irda_stop(struct net_device *dev)
-{
- struct au1k_private *aup = netdev_priv(dev);
-
- au1k_irda_plat_set_phy_mode(aup, AU1000_IRDA_PHY_MODE_OFF);
-
- /* disable interrupts */
- irda_write(aup, IR_CONFIG_2, irda_read(aup, IR_CONFIG_2) & ~IR_IEN);
- irda_write(aup, IR_CONFIG_1, 0);
- irda_write(aup, IR_ENABLE, 0); /* disable clock */
-
- if (aup->irlap) {
- irlap_close(aup->irlap);
- aup->irlap = NULL;
- }
-
- netif_stop_queue(dev);
-
- /* disable the interrupt */
- free_irq(aup->irq_tx, dev);
- free_irq(aup->irq_rx, dev);
-
- clk_disable_unprepare(aup->irda_clk);
- clk_put(aup->irda_clk);
-
- return 0;
-}
-
-/*
- * Au1000 transmit routine.
- */
-static int au1k_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct au1k_private *aup = netdev_priv(dev);
- int speed = irda_get_next_speed(skb);
- volatile struct ring_dest *ptxd;
- struct db_dest *pDB;
- u32 len, flags;
-
- if (speed != aup->speed && speed != -1)
- aup->newspeed = speed;
-
- if ((skb->len == 0) && (aup->newspeed)) {
- if (aup->tx_tail == aup->tx_head) {
- au1k_irda_set_speed(dev, speed);
- aup->newspeed = 0;
- }
- dev_kfree_skb(skb);
- return NETDEV_TX_OK;
- }
-
- ptxd = aup->tx_ring[aup->tx_head];
- flags = ptxd->flags;
-
- if (flags & AU_OWN) {
- netdev_debug(dev, "tx_full\n");
- netif_stop_queue(dev);
- aup->tx_full = 1;
- return 1;
- } else if (((aup->tx_head + 1) & (NUM_IR_DESC - 1)) == aup->tx_tail) {
- netdev_debug(dev, "tx_full\n");
- netif_stop_queue(dev);
- aup->tx_full = 1;
- return 1;
- }
-
- pDB = aup->tx_db_inuse[aup->tx_head];
-
-#if 0
- if (irda_read(aup, IR_RX_BYTE_CNT) != 0) {
- netdev_debug(dev, "tx warning: rx byte cnt %x\n",
- irda_read(aup, IR_RX_BYTE_CNT));
- }
-#endif
-
- if (aup->speed == 4000000) {
- /* FIR */
- skb_copy_from_linear_data(skb, (void *)pDB->vaddr, skb->len);
- ptxd->count_0 = skb->len & 0xff;
- ptxd->count_1 = (skb->len >> 8) & 0xff;
- } else {
- /* SIR */
- len = async_wrap_skb(skb, (u8 *)pDB->vaddr, MAX_BUF_SIZE);
- ptxd->count_0 = len & 0xff;
- ptxd->count_1 = (len >> 8) & 0xff;
- ptxd->flags |= IR_DIS_CRC;
- }
- ptxd->flags |= AU_OWN;
- wmb();
-
- irda_write(aup, IR_CONFIG_1,
- irda_read(aup, IR_CONFIG_1) | IR_TX_ENABLE);
- irda_write(aup, IR_RING_PROMPT, 0);
-
- dev_kfree_skb(skb);
- aup->tx_head = (aup->tx_head + 1) & (NUM_IR_DESC - 1);
- return NETDEV_TX_OK;
-}
-
-/*
- * The Tx ring has been full longer than the watchdog timeout
- * value. The transmitter must be hung?
- */
-static void au1k_tx_timeout(struct net_device *dev)
-{
- u32 speed;
- struct au1k_private *aup = netdev_priv(dev);
-
- netdev_err(dev, "tx timeout\n");
- speed = aup->speed;
- aup->speed = 0;
- au1k_irda_set_speed(dev, speed);
- aup->tx_full = 0;
- netif_wake_queue(dev);
-}
-
-static int au1k_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd)
-{
- struct if_irda_req *rq = (struct if_irda_req *)ifreq;
- struct au1k_private *aup = netdev_priv(dev);
- int ret = -EOPNOTSUPP;
-
- switch (cmd) {
- case SIOCSBANDWIDTH:
- if (capable(CAP_NET_ADMIN)) {
- /*
- * We are unable to set the speed if the
- * device is not running.
- */
- if (aup->open)
- ret = au1k_irda_set_speed(dev,
- rq->ifr_baudrate);
- else {
- netdev_err(dev, "ioctl: !netif_running\n");
- ret = 0;
- }
- }
- break;
-
- case SIOCSMEDIABUSY:
- ret = -EPERM;
- if (capable(CAP_NET_ADMIN)) {
- irda_device_set_media_busy(dev, TRUE);
- ret = 0;
- }
- break;
-
- case SIOCGRECEIVING:
- rq->ifr_receiving = 0;
- break;
- default:
- break;
- }
- return ret;
-}
-
-static const struct net_device_ops au1k_irda_netdev_ops = {
- .ndo_open = au1k_irda_start,
- .ndo_stop = au1k_irda_stop,
- .ndo_start_xmit = au1k_irda_hard_xmit,
- .ndo_tx_timeout = au1k_tx_timeout,
- .ndo_do_ioctl = au1k_irda_ioctl,
-};
-
-static int au1k_irda_net_init(struct net_device *dev)
-{
- struct au1k_private *aup = netdev_priv(dev);
- struct db_dest *pDB, *pDBfree;
- int i, err, retval = 0;
- dma_addr_t temp;
-
- err = au1k_irda_init_iobuf(&aup->rx_buff, 14384);
- if (err)
- goto out1;
-
- dev->netdev_ops = &au1k_irda_netdev_ops;
-
- irda_init_max_qos_capabilies(&aup->qos);
-
- /* The only value we must override it the baudrate */
- aup->qos.baud_rate.bits = IR_9600 | IR_19200 | IR_38400 |
- IR_57600 | IR_115200 | IR_576000 | (IR_4000000 << 8);
-
- aup->qos.min_turn_time.bits = qos_mtt_bits;
- irda_qos_bits_to_value(&aup->qos);
-
- retval = -ENOMEM;
-
- /* Tx ring follows rx ring + 512 bytes */
- /* we need a 1k aligned buffer */
- aup->rx_ring[0] = (struct ring_dest *)
- dma_alloc(2 * MAX_NUM_IR_DESC * (sizeof(struct ring_dest)),
- &temp);
- if (!aup->rx_ring[0])
- goto out2;
-
- /* allocate the data buffers */
- aup->db[0].vaddr =
- dma_alloc(MAX_BUF_SIZE * 2 * NUM_IR_DESC, &temp);
- if (!aup->db[0].vaddr)
- goto out3;
-
- setup_hw_rings(aup, (u32)aup->rx_ring[0], (u32)aup->rx_ring[0] + 512);
-
- pDBfree = NULL;
- pDB = aup->db;
- for (i = 0; i < (2 * NUM_IR_DESC); i++) {
- pDB->pnext = pDBfree;
- pDBfree = pDB;
- pDB->vaddr =
- (u32 *)((unsigned)aup->db[0].vaddr + (MAX_BUF_SIZE * i));
- pDB->dma_addr = (dma_addr_t)virt_to_bus(pDB->vaddr);
- pDB++;
- }
- aup->pDBfree = pDBfree;
-
- /* attach a data buffer to each descriptor */
- for (i = 0; i < NUM_IR_DESC; i++) {
- pDB = GetFreeDB(aup);
- if (!pDB)
- goto out3;
- aup->rx_ring[i]->addr_0 = (u8)(pDB->dma_addr & 0xff);
- aup->rx_ring[i]->addr_1 = (u8)((pDB->dma_addr >> 8) & 0xff);
- aup->rx_ring[i]->addr_2 = (u8)((pDB->dma_addr >> 16) & 0xff);
- aup->rx_ring[i]->addr_3 = (u8)((pDB->dma_addr >> 24) & 0xff);
- aup->rx_db_inuse[i] = pDB;
- }
- for (i = 0; i < NUM_IR_DESC; i++) {
- pDB = GetFreeDB(aup);
- if (!pDB)
- goto out3;
- aup->tx_ring[i]->addr_0 = (u8)(pDB->dma_addr & 0xff);
- aup->tx_ring[i]->addr_1 = (u8)((pDB->dma_addr >> 8) & 0xff);
- aup->tx_ring[i]->addr_2 = (u8)((pDB->dma_addr >> 16) & 0xff);
- aup->tx_ring[i]->addr_3 = (u8)((pDB->dma_addr >> 24) & 0xff);
- aup->tx_ring[i]->count_0 = 0;
- aup->tx_ring[i]->count_1 = 0;
- aup->tx_ring[i]->flags = 0;
- aup->tx_db_inuse[i] = pDB;
- }
-
- return 0;
-
-out3:
- dma_free((void *)aup->rx_ring[0],
- 2 * MAX_NUM_IR_DESC * (sizeof(struct ring_dest)));
-out2:
- kfree(aup->rx_buff.head);
-out1:
- netdev_err(dev, "au1k_irda_net_init() failed. Returns %d\n");
- return retval;
-}
-
-static int au1k_irda_probe(struct platform_device *pdev)
-{
- struct au1k_private *aup;
- struct net_device *dev;
- struct resource *r;
- struct clk *c;
- int err;
-
- dev = alloc_irdadev(sizeof(struct au1k_private));
- if (!dev)
- return -ENOMEM;
-
- aup = netdev_priv(dev);
-
- aup->platdata = pdev->dev.platform_data;
-
- err = -EINVAL;
- r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!r)
- goto out;
-
- aup->irq_tx = r->start;
-
- r = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
- if (!r)
- goto out;
-
- aup->irq_rx = r->start;
-
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!r)
- goto out;
-
- err = -EBUSY;
- aup->ioarea = request_mem_region(r->start, resource_size(r),
- pdev->name);
- if (!aup->ioarea)
- goto out;
-
- /* bail out early if clock doesn't exist */
- c = clk_get(NULL, "irda_clk");
- if (IS_ERR(c)) {
- err = PTR_ERR(c);
- goto out;
- }
- clk_put(c);
-
- aup->iobase = ioremap_nocache(r->start, resource_size(r));
- if (!aup->iobase)
- goto out2;
-
- dev->irq = aup->irq_rx;
-
- err = au1k_irda_net_init(dev);
- if (err)
- goto out3;
- err = register_netdev(dev);
- if (err)
- goto out4;
-
- platform_set_drvdata(pdev, dev);
-
- netdev_info(dev, "IrDA: Registered device\n");
- return 0;
-
-out4:
- dma_free((void *)aup->db[0].vaddr,
- MAX_BUF_SIZE * 2 * NUM_IR_DESC);
- dma_free((void *)aup->rx_ring[0],
- 2 * MAX_NUM_IR_DESC * (sizeof(struct ring_dest)));
- kfree(aup->rx_buff.head);
-out3:
- iounmap(aup->iobase);
-out2:
- release_resource(aup->ioarea);
- kfree(aup->ioarea);
-out:
- free_netdev(dev);
- return err;
-}
-
-static int au1k_irda_remove(struct platform_device *pdev)
-{
- struct net_device *dev = platform_get_drvdata(pdev);
- struct au1k_private *aup = netdev_priv(dev);
-
- unregister_netdev(dev);
-
- dma_free((void *)aup->db[0].vaddr,
- MAX_BUF_SIZE * 2 * NUM_IR_DESC);
- dma_free((void *)aup->rx_ring[0],
- 2 * MAX_NUM_IR_DESC * (sizeof(struct ring_dest)));
- kfree(aup->rx_buff.head);
-
- iounmap(aup->iobase);
- release_resource(aup->ioarea);
- kfree(aup->ioarea);
-
- free_netdev(dev);
-
- return 0;
-}
-
-static struct platform_driver au1k_irda_driver = {
- .driver = {
- .name = "au1000-irda",
- },
- .probe = au1k_irda_probe,
- .remove = au1k_irda_remove,
-};
-
-module_platform_driver(au1k_irda_driver);
-
-MODULE_AUTHOR("Pete Popov <ppopov@mvista.com>");
-MODULE_DESCRIPTION("Au1000 IrDA Device Driver");
diff --git a/drivers/staging/irda/drivers/bfin_sir.c b/drivers/staging/irda/drivers/bfin_sir.c
deleted file mode 100644
index 59e409b68349..000000000000
--- a/drivers/staging/irda/drivers/bfin_sir.c
+++ /dev/null
@@ -1,819 +0,0 @@
-/*
- * Blackfin Infra-red Driver
- *
- * Copyright 2006-2009 Analog Devices Inc.
- *
- * Enter bugs at http://blackfin.uclinux.org/
- *
- * Licensed under the GPL-2 or later.
- *
- */
-#include "bfin_sir.h"
-
-#ifdef CONFIG_SIR_BFIN_DMA
-#define DMA_SIR_RX_XCNT 10
-#define DMA_SIR_RX_YCNT (PAGE_SIZE / DMA_SIR_RX_XCNT)
-#define DMA_SIR_RX_FLUSH_JIFS (HZ * 4 / 250)
-#endif
-
-#if ANOMALY_05000447
-static int max_rate = 57600;
-#else
-static int max_rate = 115200;
-#endif
-
-static void bfin_sir_rx_dma_timeout(struct timer_list *t);
-
-static void turnaround_delay(int mtt)
-{
- long ticks;
-
- mtt = mtt < 10000 ? 10000 : mtt;
- ticks = 1 + mtt / (USEC_PER_SEC / HZ);
- schedule_timeout_uninterruptible(ticks);
-}
-
-static void bfin_sir_init_ports(struct bfin_sir_port *sp, struct platform_device *pdev)
-{
- int i;
- struct resource *res;
-
- for (i = 0; i < pdev->num_resources; i++) {
- res = &pdev->resource[i];
- switch (res->flags) {
- case IORESOURCE_MEM:
- sp->membase = (void __iomem *)res->start;
- break;
- case IORESOURCE_IRQ:
- sp->irq = res->start;
- break;
- case IORESOURCE_DMA:
- sp->rx_dma_channel = res->start;
- sp->tx_dma_channel = res->end;
- break;
- default:
- break;
- }
- }
-
- sp->clk = get_sclk();
-#ifdef CONFIG_SIR_BFIN_DMA
- sp->tx_done = 1;
- timer_setup(&sp->rx_dma_timer, bfin_sir_rx_dma_timeout, 0);
-#endif
-}
-
-static void bfin_sir_stop_tx(struct bfin_sir_port *port)
-{
-#ifdef CONFIG_SIR_BFIN_DMA
- disable_dma(port->tx_dma_channel);
-#endif
-
- while (!(UART_GET_LSR(port) & THRE)) {
- cpu_relax();
- continue;
- }
-
- UART_CLEAR_IER(port, ETBEI);
-}
-
-static void bfin_sir_enable_tx(struct bfin_sir_port *port)
-{
- UART_SET_IER(port, ETBEI);
-}
-
-static void bfin_sir_stop_rx(struct bfin_sir_port *port)
-{
- UART_CLEAR_IER(port, ERBFI);
-}
-
-static void bfin_sir_enable_rx(struct bfin_sir_port *port)
-{
- UART_SET_IER(port, ERBFI);
-}
-
-static int bfin_sir_set_speed(struct bfin_sir_port *port, int speed)
-{
- int ret = -EINVAL;
- unsigned int quot;
- unsigned short val, lsr, lcr;
- static int utime;
- int count = 10;
-
- lcr = WLS(8);
-
- switch (speed) {
- case 9600:
- case 19200:
- case 38400:
- case 57600:
- case 115200:
-
- /*
- * IRDA is not affected by anomaly 05000230, so there is no
- * need to tweak the divisor like he UART driver (which will
- * slightly speed up the baud rate on us).
- */
- quot = (port->clk + (8 * speed)) / (16 * speed);
-
- do {
- udelay(utime);
- lsr = UART_GET_LSR(port);
- } while (!(lsr & TEMT) && count--);
-
- /* The useconds for 1 bits to transmit */
- utime = 1000000 / speed + 1;
-
- /* Clear UCEN bit to reset the UART state machine
- * and control registers
- */
- val = UART_GET_GCTL(port);
- val &= ~UCEN;
- UART_PUT_GCTL(port, val);
-
- /* Set DLAB in LCR to Access THR RBR IER */
- UART_SET_DLAB(port);
- SSYNC();
-
- UART_PUT_DLL(port, quot & 0xFF);
- UART_PUT_DLH(port, (quot >> 8) & 0xFF);
- SSYNC();
-
- /* Clear DLAB in LCR */
- UART_CLEAR_DLAB(port);
- SSYNC();
-
- UART_PUT_LCR(port, lcr);
-
- val = UART_GET_GCTL(port);
- val |= UCEN;
- UART_PUT_GCTL(port, val);
-
- ret = 0;
- break;
- default:
- printk(KERN_WARNING "bfin_sir: Invalid speed %d\n", speed);
- break;
- }
-
- val = UART_GET_GCTL(port);
- /* If not add the 'RPOLC', we can't catch the receive interrupt.
- * It's related with the HW layout and the IR transiver.
- */
- val |= UMOD_IRDA | RPOLC;
- UART_PUT_GCTL(port, val);
- return ret;
-}
-
-static int bfin_sir_is_receiving(struct net_device *dev)
-{
- struct bfin_sir_self *self = netdev_priv(dev);
- struct bfin_sir_port *port = self->sir_port;
-
- if (!(UART_GET_IER(port) & ERBFI))
- return 0;
- return self->rx_buff.state != OUTSIDE_FRAME;
-}
-
-#ifdef CONFIG_SIR_BFIN_PIO
-static void bfin_sir_tx_chars(struct net_device *dev)
-{
- unsigned int chr;
- struct bfin_sir_self *self = netdev_priv(dev);
- struct bfin_sir_port *port = self->sir_port;
-
- if (self->tx_buff.len != 0) {
- chr = *(self->tx_buff.data);
- UART_PUT_CHAR(port, chr);
- self->tx_buff.data++;
- self->tx_buff.len--;
- } else {
- self->stats.tx_packets++;
- self->stats.tx_bytes += self->tx_buff.data - self->tx_buff.head;
- if (self->newspeed) {
- bfin_sir_set_speed(port, self->newspeed);
- self->speed = self->newspeed;
- self->newspeed = 0;
- }
- bfin_sir_stop_tx(port);
- bfin_sir_enable_rx(port);
- /* I'm hungry! */
- netif_wake_queue(dev);
- }
-}
-
-static void bfin_sir_rx_chars(struct net_device *dev)
-{
- struct bfin_sir_self *self = netdev_priv(dev);
- struct bfin_sir_port *port = self->sir_port;
- unsigned char ch;
-
- UART_CLEAR_LSR(port);
- ch = UART_GET_CHAR(port);
- async_unwrap_char(dev, &self->stats, &self->rx_buff, ch);
-}
-
-static irqreturn_t bfin_sir_rx_int(int irq, void *dev_id)
-{
- struct net_device *dev = dev_id;
- struct bfin_sir_self *self = netdev_priv(dev);
- struct bfin_sir_port *port = self->sir_port;
-
- spin_lock(&self->lock);
- while ((UART_GET_LSR(port) & DR))
- bfin_sir_rx_chars(dev);
- spin_unlock(&self->lock);
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t bfin_sir_tx_int(int irq, void *dev_id)
-{
- struct net_device *dev = dev_id;
- struct bfin_sir_self *self = netdev_priv(dev);
- struct bfin_sir_port *port = self->sir_port;
-
- spin_lock(&self->lock);
- if (UART_GET_LSR(port) & THRE)
- bfin_sir_tx_chars(dev);
- spin_unlock(&self->lock);
-
- return IRQ_HANDLED;
-}
-#endif /* CONFIG_SIR_BFIN_PIO */
-
-#ifdef CONFIG_SIR_BFIN_DMA
-static void bfin_sir_dma_tx_chars(struct net_device *dev)
-{
- struct bfin_sir_self *self = netdev_priv(dev);
- struct bfin_sir_port *port = self->sir_port;
-
- if (!port->tx_done)
- return;
- port->tx_done = 0;
-
- if (self->tx_buff.len == 0) {
- self->stats.tx_packets++;
- if (self->newspeed) {
- bfin_sir_set_speed(port, self->newspeed);
- self->speed = self->newspeed;
- self->newspeed = 0;
- }
- bfin_sir_enable_rx(port);
- port->tx_done = 1;
- netif_wake_queue(dev);
- return;
- }
-
- blackfin_dcache_flush_range((unsigned long)(self->tx_buff.data),
- (unsigned long)(self->tx_buff.data+self->tx_buff.len));
- set_dma_config(port->tx_dma_channel,
- set_bfin_dma_config(DIR_READ, DMA_FLOW_STOP,
- INTR_ON_BUF, DIMENSION_LINEAR, DATA_SIZE_8,
- DMA_SYNC_RESTART));
- set_dma_start_addr(port->tx_dma_channel,
- (unsigned long)(self->tx_buff.data));
- set_dma_x_count(port->tx_dma_channel, self->tx_buff.len);
- set_dma_x_modify(port->tx_dma_channel, 1);
- enable_dma(port->tx_dma_channel);
-}
-
-static irqreturn_t bfin_sir_dma_tx_int(int irq, void *dev_id)
-{
- struct net_device *dev = dev_id;
- struct bfin_sir_self *self = netdev_priv(dev);
- struct bfin_sir_port *port = self->sir_port;
-
- spin_lock(&self->lock);
- if (!(get_dma_curr_irqstat(port->tx_dma_channel) & DMA_RUN)) {
- clear_dma_irqstat(port->tx_dma_channel);
- bfin_sir_stop_tx(port);
-
- self->stats.tx_packets++;
- self->stats.tx_bytes += self->tx_buff.len;
- self->tx_buff.len = 0;
- if (self->newspeed) {
- bfin_sir_set_speed(port, self->newspeed);
- self->speed = self->newspeed;
- self->newspeed = 0;
- }
- bfin_sir_enable_rx(port);
- /* I'm hungry! */
- netif_wake_queue(dev);
- port->tx_done = 1;
- }
- spin_unlock(&self->lock);
-
- return IRQ_HANDLED;
-}
-
-static void bfin_sir_dma_rx_chars(struct net_device *dev)
-{
- struct bfin_sir_self *self = netdev_priv(dev);
- struct bfin_sir_port *port = self->sir_port;
- int i;
-
- UART_CLEAR_LSR(port);
-
- for (i = port->rx_dma_buf.head; i < port->rx_dma_buf.tail; i++)
- async_unwrap_char(dev, &self->stats, &self->rx_buff, port->rx_dma_buf.buf[i]);
-}
-
-static void bfin_sir_rx_dma_timeout(struct timer_list *t)
-{
- struct bfin_sir_port *port = from_timer(port, t, rx_dma_timer);
- struct net_device *dev = port->dev;
- struct bfin_sir_self *self = netdev_priv(dev);
-
- int x_pos, pos;
- unsigned long flags;
-
- spin_lock_irqsave(&self->lock, flags);
- x_pos = DMA_SIR_RX_XCNT - get_dma_curr_xcount(port->rx_dma_channel);
- if (x_pos == DMA_SIR_RX_XCNT)
- x_pos = 0;
-
- pos = port->rx_dma_nrows * DMA_SIR_RX_XCNT + x_pos;
-
- if (pos > port->rx_dma_buf.tail) {
- port->rx_dma_buf.tail = pos;
- bfin_sir_dma_rx_chars(dev);
- port->rx_dma_buf.head = port->rx_dma_buf.tail;
- }
- spin_unlock_irqrestore(&self->lock, flags);
-}
-
-static irqreturn_t bfin_sir_dma_rx_int(int irq, void *dev_id)
-{
- struct net_device *dev = dev_id;
- struct bfin_sir_self *self = netdev_priv(dev);
- struct bfin_sir_port *port = self->sir_port;
- unsigned short irqstat;
-
- spin_lock(&self->lock);
-
- port->rx_dma_nrows++;
- port->rx_dma_buf.tail = DMA_SIR_RX_XCNT * port->rx_dma_nrows;
- bfin_sir_dma_rx_chars(dev);
- if (port->rx_dma_nrows >= DMA_SIR_RX_YCNT) {
- port->rx_dma_nrows = 0;
- port->rx_dma_buf.tail = 0;
- }
- port->rx_dma_buf.head = port->rx_dma_buf.tail;
-
- irqstat = get_dma_curr_irqstat(port->rx_dma_channel);
- clear_dma_irqstat(port->rx_dma_channel);
- spin_unlock(&self->lock);
-
- mod_timer(&port->rx_dma_timer, jiffies + DMA_SIR_RX_FLUSH_JIFS);
- return IRQ_HANDLED;
-}
-#endif /* CONFIG_SIR_BFIN_DMA */
-
-static int bfin_sir_startup(struct bfin_sir_port *port, struct net_device *dev)
-{
-#ifdef CONFIG_SIR_BFIN_DMA
- dma_addr_t dma_handle;
-#endif /* CONFIG_SIR_BFIN_DMA */
-
- if (request_dma(port->rx_dma_channel, "BFIN_UART_RX") < 0) {
- dev_warn(&dev->dev, "Unable to attach SIR RX DMA channel\n");
- return -EBUSY;
- }
-
- if (request_dma(port->tx_dma_channel, "BFIN_UART_TX") < 0) {
- dev_warn(&dev->dev, "Unable to attach SIR TX DMA channel\n");
- free_dma(port->rx_dma_channel);
- return -EBUSY;
- }
-
-#ifdef CONFIG_SIR_BFIN_DMA
-
- set_dma_callback(port->rx_dma_channel, bfin_sir_dma_rx_int, dev);
- set_dma_callback(port->tx_dma_channel, bfin_sir_dma_tx_int, dev);
-
- port->rx_dma_buf.buf = dma_alloc_coherent(NULL, PAGE_SIZE,
- &dma_handle, GFP_DMA);
- port->rx_dma_buf.head = 0;
- port->rx_dma_buf.tail = 0;
- port->rx_dma_nrows = 0;
-
- set_dma_config(port->rx_dma_channel,
- set_bfin_dma_config(DIR_WRITE, DMA_FLOW_AUTO,
- INTR_ON_ROW, DIMENSION_2D,
- DATA_SIZE_8, DMA_SYNC_RESTART));
- set_dma_x_count(port->rx_dma_channel, DMA_SIR_RX_XCNT);
- set_dma_x_modify(port->rx_dma_channel, 1);
- set_dma_y_count(port->rx_dma_channel, DMA_SIR_RX_YCNT);
- set_dma_y_modify(port->rx_dma_channel, 1);
- set_dma_start_addr(port->rx_dma_channel, (unsigned long)port->rx_dma_buf.buf);
- enable_dma(port->rx_dma_channel);
-
-
-#else
-
- if (request_irq(port->irq, bfin_sir_rx_int, 0, "BFIN_SIR_RX", dev)) {
- dev_warn(&dev->dev, "Unable to attach SIR RX interrupt\n");
- return -EBUSY;
- }
-
- if (request_irq(port->irq+1, bfin_sir_tx_int, 0, "BFIN_SIR_TX", dev)) {
- dev_warn(&dev->dev, "Unable to attach SIR TX interrupt\n");
- free_irq(port->irq, dev);
- return -EBUSY;
- }
-#endif
-
- return 0;
-}
-
-static void bfin_sir_shutdown(struct bfin_sir_port *port, struct net_device *dev)
-{
- unsigned short val;
-
- bfin_sir_stop_rx(port);
-
- val = UART_GET_GCTL(port);
- val &= ~(UCEN | UMOD_MASK | RPOLC);
- UART_PUT_GCTL(port, val);
-
-#ifdef CONFIG_SIR_BFIN_DMA
- disable_dma(port->tx_dma_channel);
- disable_dma(port->rx_dma_channel);
- del_timer(&(port->rx_dma_timer));
- dma_free_coherent(NULL, PAGE_SIZE, port->rx_dma_buf.buf, 0);
-#else
- free_irq(port->irq+1, dev);
- free_irq(port->irq, dev);
-#endif
- free_dma(port->tx_dma_channel);
- free_dma(port->rx_dma_channel);
-}
-
-#ifdef CONFIG_PM
-static int bfin_sir_suspend(struct platform_device *pdev, pm_message_t state)
-{
- struct bfin_sir_port *sir_port;
- struct net_device *dev;
- struct bfin_sir_self *self;
-
- sir_port = platform_get_drvdata(pdev);
- if (!sir_port)
- return 0;
-
- dev = sir_port->dev;
- self = netdev_priv(dev);
- if (self->open) {
- flush_work(&self->work);
- bfin_sir_shutdown(self->sir_port, dev);
- netif_device_detach(dev);
- }
-
- return 0;
-}
-static int bfin_sir_resume(struct platform_device *pdev)
-{
- struct bfin_sir_port *sir_port;
- struct net_device *dev;
- struct bfin_sir_self *self;
- struct bfin_sir_port *port;
-
- sir_port = platform_get_drvdata(pdev);
- if (!sir_port)
- return 0;
-
- dev = sir_port->dev;
- self = netdev_priv(dev);
- port = self->sir_port;
- if (self->open) {
- if (self->newspeed) {
- self->speed = self->newspeed;
- self->newspeed = 0;
- }
- bfin_sir_startup(port, dev);
- bfin_sir_set_speed(port, 9600);
- bfin_sir_enable_rx(port);
- netif_device_attach(dev);
- }
- return 0;
-}
-#else
-#define bfin_sir_suspend NULL
-#define bfin_sir_resume NULL
-#endif
-
-static void bfin_sir_send_work(struct work_struct *work)
-{
- struct bfin_sir_self *self = container_of(work, struct bfin_sir_self, work);
- struct net_device *dev = self->sir_port->dev;
- struct bfin_sir_port *port = self->sir_port;
- unsigned short val;
- int tx_cnt = 10;
-
- while (bfin_sir_is_receiving(dev) && --tx_cnt)
- turnaround_delay(self->mtt);
-
- bfin_sir_stop_rx(port);
-
- /* To avoid losting RX interrupt, we reset IR function before
- * sending data. We also can set the speed, which will
- * reset all the UART.
- */
- val = UART_GET_GCTL(port);
- val &= ~(UMOD_MASK | RPOLC);
- UART_PUT_GCTL(port, val);
- SSYNC();
- val |= UMOD_IRDA | RPOLC;
- UART_PUT_GCTL(port, val);
- SSYNC();
- /* bfin_sir_set_speed(port, self->speed); */
-
-#ifdef CONFIG_SIR_BFIN_DMA
- bfin_sir_dma_tx_chars(dev);
-#endif
- bfin_sir_enable_tx(port);
- netif_trans_update(dev);
-}
-
-static int bfin_sir_hard_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct bfin_sir_self *self = netdev_priv(dev);
- int speed = irda_get_next_speed(skb);
-
- netif_stop_queue(dev);
-
- self->mtt = irda_get_mtt(skb);
-
- if (speed != self->speed && speed != -1)
- self->newspeed = speed;
-
- self->tx_buff.data = self->tx_buff.head;
- if (skb->len == 0)
- self->tx_buff.len = 0;
- else
- self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, self->tx_buff.truesize);
-
- schedule_work(&self->work);
- dev_kfree_skb(skb);
-
- return 0;
-}
-
-static int bfin_sir_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd)
-{
- struct if_irda_req *rq = (struct if_irda_req *)ifreq;
- struct bfin_sir_self *self = netdev_priv(dev);
- struct bfin_sir_port *port = self->sir_port;
- int ret = 0;
-
- switch (cmd) {
- case SIOCSBANDWIDTH:
- if (capable(CAP_NET_ADMIN)) {
- if (self->open) {
- ret = bfin_sir_set_speed(port, rq->ifr_baudrate);
- bfin_sir_enable_rx(port);
- } else {
- dev_warn(&dev->dev, "SIOCSBANDWIDTH: !netif_running\n");
- ret = 0;
- }
- }
- break;
-
- case SIOCSMEDIABUSY:
- ret = -EPERM;
- if (capable(CAP_NET_ADMIN)) {
- irda_device_set_media_busy(dev, TRUE);
- ret = 0;
- }
- break;
-
- case SIOCGRECEIVING:
- rq->ifr_receiving = bfin_sir_is_receiving(dev);
- break;
-
- default:
- ret = -EOPNOTSUPP;
- break;
- }
-
- return ret;
-}
-
-static struct net_device_stats *bfin_sir_stats(struct net_device *dev)
-{
- struct bfin_sir_self *self = netdev_priv(dev);
-
- return &self->stats;
-}
-
-static int bfin_sir_open(struct net_device *dev)
-{
- struct bfin_sir_self *self = netdev_priv(dev);
- struct bfin_sir_port *port = self->sir_port;
- int err;
-
- self->newspeed = 0;
- self->speed = 9600;
-
- spin_lock_init(&self->lock);
-
- err = bfin_sir_startup(port, dev);
- if (err)
- goto err_startup;
-
- bfin_sir_set_speed(port, 9600);
-
- self->irlap = irlap_open(dev, &self->qos, DRIVER_NAME);
- if (!self->irlap) {
- err = -ENOMEM;
- goto err_irlap;
- }
-
- INIT_WORK(&self->work, bfin_sir_send_work);
-
- /*
- * Now enable the interrupt then start the queue
- */
- self->open = 1;
- bfin_sir_enable_rx(port);
-
- netif_start_queue(dev);
-
- return 0;
-
-err_irlap:
- self->open = 0;
- bfin_sir_shutdown(port, dev);
-err_startup:
- return err;
-}
-
-static int bfin_sir_stop(struct net_device *dev)
-{
- struct bfin_sir_self *self = netdev_priv(dev);
-
- flush_work(&self->work);
- bfin_sir_shutdown(self->sir_port, dev);
-
- if (self->rxskb) {
- dev_kfree_skb(self->rxskb);
- self->rxskb = NULL;
- }
-
- /* Stop IrLAP */
- if (self->irlap) {
- irlap_close(self->irlap);
- self->irlap = NULL;
- }
-
- netif_stop_queue(dev);
- self->open = 0;
-
- return 0;
-}
-
-static int bfin_sir_init_iobuf(iobuff_t *io, int size)
-{
- io->head = kmalloc(size, GFP_KERNEL);
- if (!io->head)
- return -ENOMEM;
- io->truesize = size;
- io->in_frame = FALSE;
- io->state = OUTSIDE_FRAME;
- io->data = io->head;
- return 0;
-}
-
-static const struct net_device_ops bfin_sir_ndo = {
- .ndo_open = bfin_sir_open,
- .ndo_stop = bfin_sir_stop,
- .ndo_start_xmit = bfin_sir_hard_xmit,
- .ndo_do_ioctl = bfin_sir_ioctl,
- .ndo_get_stats = bfin_sir_stats,
-};
-
-static int bfin_sir_probe(struct platform_device *pdev)
-{
- struct net_device *dev;
- struct bfin_sir_self *self;
- unsigned int baudrate_mask;
- struct bfin_sir_port *sir_port;
- int err;
-
- if (pdev->id >= 0 && pdev->id < ARRAY_SIZE(per) && \
- per[pdev->id][3] == pdev->id) {
- err = peripheral_request_list(per[pdev->id], DRIVER_NAME);
- if (err)
- return err;
- } else {
- dev_err(&pdev->dev, "Invalid pdev id, please check board file\n");
- return -ENODEV;
- }
-
- err = -ENOMEM;
- sir_port = kmalloc(sizeof(*sir_port), GFP_KERNEL);
- if (!sir_port)
- goto err_mem_0;
-
- bfin_sir_init_ports(sir_port, pdev);
-
- dev = alloc_irdadev(sizeof(*self));
- if (!dev)
- goto err_mem_1;
-
- self = netdev_priv(dev);
- self->dev = &pdev->dev;
- self->sir_port = sir_port;
- sir_port->dev = dev;
-
- err = bfin_sir_init_iobuf(&self->rx_buff, IRDA_SKB_MAX_MTU);
- if (err)
- goto err_mem_2;
- err = bfin_sir_init_iobuf(&self->tx_buff, IRDA_SIR_MAX_FRAME);
- if (err)
- goto err_mem_3;
-
- dev->netdev_ops = &bfin_sir_ndo;
- dev->irq = sir_port->irq;
-
- irda_init_max_qos_capabilies(&self->qos);
-
- baudrate_mask = IR_9600;
-
- switch (max_rate) {
- case 115200:
- baudrate_mask |= IR_115200;
- case 57600:
- baudrate_mask |= IR_57600;
- case 38400:
- baudrate_mask |= IR_38400;
- case 19200:
- baudrate_mask |= IR_19200;
- case 9600:
- break;
- default:
- dev_warn(&pdev->dev, "Invalid maximum baud rate, using 9600\n");
- }
-
- self->qos.baud_rate.bits &= baudrate_mask;
-
- self->qos.min_turn_time.bits = 1; /* 10 ms or more */
-
- irda_qos_bits_to_value(&self->qos);
-
- err = register_netdev(dev);
-
- if (err) {
- kfree(self->tx_buff.head);
-err_mem_3:
- kfree(self->rx_buff.head);
-err_mem_2:
- free_netdev(dev);
-err_mem_1:
- kfree(sir_port);
-err_mem_0:
- peripheral_free_list(per[pdev->id]);
- } else
- platform_set_drvdata(pdev, sir_port);
-
- return err;
-}
-
-static int bfin_sir_remove(struct platform_device *pdev)
-{
- struct bfin_sir_port *sir_port;
- struct net_device *dev = NULL;
- struct bfin_sir_self *self;
-
- sir_port = platform_get_drvdata(pdev);
- if (!sir_port)
- return 0;
- dev = sir_port->dev;
- self = netdev_priv(dev);
- unregister_netdev(dev);
- kfree(self->tx_buff.head);
- kfree(self->rx_buff.head);
- free_netdev(dev);
- kfree(sir_port);
-
- return 0;
-}
-
-static struct platform_driver bfin_ir_driver = {
- .probe = bfin_sir_probe,
- .remove = bfin_sir_remove,
- .suspend = bfin_sir_suspend,
- .resume = bfin_sir_resume,
- .driver = {
- .name = DRIVER_NAME,
- },
-};
-
-module_platform_driver(bfin_ir_driver);
-
-module_param(max_rate, int, 0);
-MODULE_PARM_DESC(max_rate, "Maximum baud rate (115200, 57600, 38400, 19200, 9600)");
-
-MODULE_AUTHOR("Graf Yang <graf.yang@analog.com>");
-MODULE_DESCRIPTION("Blackfin IrDA driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/staging/irda/drivers/bfin_sir.h b/drivers/staging/irda/drivers/bfin_sir.h
deleted file mode 100644
index d47cf14bb4a5..000000000000
--- a/drivers/staging/irda/drivers/bfin_sir.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Blackfin Infra-red Driver
- *
- * Copyright 2006-2009 Analog Devices Inc.
- *
- * Enter bugs at http://blackfin.uclinux.org/
- *
- * Licensed under the GPL-2 or later.
- *
- */
-
-#include <linux/serial.h>
-#include <linux/module.h>
-#include <linux/netdevice.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/platform_device.h>
-#include <linux/dma-mapping.h>
-#include <linux/slab.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/wrapper.h>
-#include <net/irda/irda_device.h>
-
-#include <asm/irq.h>
-#include <asm/cacheflush.h>
-#include <asm/dma.h>
-#include <asm/portmux.h>
-#undef DRIVER_NAME
-
-#ifdef CONFIG_SIR_BFIN_DMA
-struct dma_rx_buf {
- char *buf;
- int head;
- int tail;
-};
-#endif
-
-struct bfin_sir_port {
- unsigned char __iomem *membase;
- unsigned int irq;
- unsigned int lsr;
- unsigned long clk;
- struct net_device *dev;
-#ifdef CONFIG_SIR_BFIN_DMA
- int tx_done;
- struct dma_rx_buf rx_dma_buf;
- struct timer_list rx_dma_timer;
- int rx_dma_nrows;
-#endif
- unsigned int tx_dma_channel;
- unsigned int rx_dma_channel;
-};
-
-struct bfin_sir_port_res {
- unsigned long base_addr;
- int irq;
- unsigned int rx_dma_channel;
- unsigned int tx_dma_channel;
-};
-
-struct bfin_sir_self {
- struct bfin_sir_port *sir_port;
- spinlock_t lock;
- unsigned int open;
- int speed;
- int newspeed;
-
- struct sk_buff *txskb;
- struct sk_buff *rxskb;
- struct net_device_stats stats;
- struct device *dev;
- struct irlap_cb *irlap;
- struct qos_info qos;
-
- iobuff_t tx_buff;
- iobuff_t rx_buff;
-
- struct work_struct work;
- int mtt;
-};
-
-#define DRIVER_NAME "bfin_sir"
-
-#include <asm/bfin_serial.h>
-
-static const unsigned short per[][4] = {
- /* rx pin tx pin NULL uart_number */
- {P_UART0_RX, P_UART0_TX, 0, 0},
- {P_UART1_RX, P_UART1_TX, 0, 1},
- {P_UART2_RX, P_UART2_TX, 0, 2},
- {P_UART3_RX, P_UART3_TX, 0, 3},
-};
diff --git a/drivers/staging/irda/drivers/donauboe.c b/drivers/staging/irda/drivers/donauboe.c
deleted file mode 100644
index b337e6d23a88..000000000000
--- a/drivers/staging/irda/drivers/donauboe.c
+++ /dev/null
@@ -1,1732 +0,0 @@
-/*****************************************************************
- *
- * Filename: donauboe.c
- * Version: 2.17
- * Description: Driver for the Toshiba OBOE (or type-O or 701)
- * FIR Chipset, also supports the DONAUOBOE (type-DO
- * or d01) FIR chipset which as far as I know is
- * register compatible.
- * Documentation: http://libxg.free.fr/irda/lib-irda.html
- * Status: Experimental.
- * Author: James McKenzie <james@fishsoup.dhs.org>
- * Created at: Sat May 8 12:35:27 1999
- * Modified: Paul Bristow <paul.bristow@technologist.com>
- * Modified: Mon Nov 11 19:10:05 1999
- * Modified: James McKenzie <james@fishsoup.dhs.org>
- * Modified: Thu Mar 16 12:49:00 2000 (Substantial rewrite)
- * Modified: Sat Apr 29 00:23:03 2000 (Added DONAUOBOE support)
- * Modified: Wed May 24 23:45:02 2000 (Fixed chipio_t structure)
- * Modified: 2.13 Christian Gennerat <christian.gennerat@polytechnique.org>
- * Modified: 2.13 dim jan 07 21:57:39 2001 (tested with kernel 2.4 & irnet/ppp)
- * Modified: 2.14 Christian Gennerat <christian.gennerat@polytechnique.org>
- * Modified: 2.14 lun fev 05 17:55:59 2001 (adapted to patch-2.4.1-pre8-irda1)
- * Modified: 2.15 Martin Lucina <mato@kotelna.sk>
- * Modified: 2.15 Fri Jun 21 20:40:59 2002 (sync with 2.4.18, substantial fixes)
- * Modified: 2.16 Martin Lucina <mato@kotelna.sk>
- * Modified: 2.16 Sat Jun 22 18:54:29 2002 (fix freeregion, default to verbose)
- * Modified: 2.17 Christian Gennerat <christian.gennerat@polytechnique.org>
- * Modified: 2.17 jeu sep 12 08:50:20 2002 (save_flags();cli(); replaced by spinlocks)
- * Modified: 2.18 Christian Gennerat <christian.gennerat@polytechnique.org>
- * Modified: 2.18 ven jan 10 03:14:16 2003 Change probe default options
- *
- * Copyright (c) 1999 James McKenzie, All Rights Reserved.
- *
- * 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.
- *
- * Neither James McKenzie nor Cambridge University admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- * Applicable Models : Libretto 100/110CT and many more.
- * Toshiba refers to this chip as the type-O IR port,
- * or the type-DO IR port.
- *
- ********************************************************************/
-
-/* Look at toshoboe.h (currently in include/net/irda) for details of */
-/* Where to get documentation on the chip */
-
-/* See below for a description of the logic in this driver */
-
-/* User servicable parts */
-/* USE_PROBE Create the code which probes the chip and does a few tests */
-/* do_probe module parameter Enable this code */
-/* Probe code is very useful for understanding how the hardware works */
-/* Use it with various combinations of TT_LEN, RX_LEN */
-/* Strongly recommended, disable if the probe fails on your machine */
-/* and send me <james@fishsoup.dhs.org> the output of dmesg */
-#define USE_PROBE 1
-#undef USE_PROBE
-
-/* Trace Transmit ring, interrupts, Receive ring or not ? */
-#define PROBE_VERBOSE 1
-
-/* Debug option, examine sent and received raw data */
-/* Irdadump is better, but does not see all packets. enable it if you want. */
-#undef DUMP_PACKETS
-
-/* MIR mode has not been tested. Some behaviour is different */
-/* Seems to work against an Ericsson R520 for me. -Martin */
-#define USE_MIR
-
-/* Schedule back to back hardware transmits wherever possible, otherwise */
-/* we need an interrupt for every frame, unset if oboe works for a bit and */
-/* then hangs */
-#define OPTIMIZE_TX
-
-/* Set the number of slots in the rings */
-/* If you get rx/tx fifo overflows at high bitrates, you can try increasing */
-/* these */
-
-#define RING_SIZE (OBOE_RING_SIZE_RX8 | OBOE_RING_SIZE_TX8)
-#define TX_SLOTS 8
-#define RX_SLOTS 8
-
-
-/* Less user servicable parts below here */
-
-/* Test, Transmit and receive buffer sizes, adjust at your peril */
-/* remarks: nfs usually needs 1k blocks */
-/* remarks: in SIR mode, CRC is received, -> RX_LEN=TX_LEN+2 */
-/* remarks: test accepts large blocks. Standard is 0x80 */
-/* When TT_LEN > RX_LEN (SIR mode) data is stored in successive slots. */
-/* When 3 or more slots are needed for each test packet, */
-/* data received in the first slots is overwritten, even */
-/* if OBOE_CTL_RX_HW_OWNS is not set, without any error! */
-#define TT_LEN 0x80
-#define TX_LEN 0xc00
-#define RX_LEN 0xc04
-/* Real transmitted length (SIR mode) is about 14+(2%*TX_LEN) more */
-/* long than user-defined length (see async_wrap_skb) and is less then 4K */
-/* Real received length is (max RX_LEN) differs from user-defined */
-/* length only b the CRC (2 or 4 bytes) */
-#define BUF_SAFETY 0x7a
-#define RX_BUF_SZ (RX_LEN)
-#define TX_BUF_SZ (TX_LEN+BUF_SAFETY)
-
-
-/* Logic of the netdev part of this driver */
-
-/* The RX ring is filled with buffers, when a packet arrives */
-/* it is DMA'd into the buffer which is marked used and RxDone called */
-/* RxDone forms an skb (and checks the CRC if in SIR mode) and ships */
-/* the packet off upstairs */
-
-/* The transmitter on the oboe chip can work in one of two modes */
-/* for each ring->tx[] the transmitter can either */
-/* a) transmit the packet, leave the trasmitter enabled and proceed to */
-/* the next ring */
-/* OR */
-/* b) transmit the packet, switch off the transmitter and issue TxDone */
-
-/* All packets are entered into the ring in mode b), if the ring was */
-/* empty the transmitter is started. */
-
-/* If OPTIMIZE_TX is defined then in TxDone if the ring contains */
-/* more than one packet, all but the last are set to mode a) [HOWEVER */
-/* the hardware may not notice this, this is why we start in mode b) ] */
-/* then restart the transmitter */
-
-/* If OPTIMIZE_TX is not defined then we just restart the transmitter */
-/* if the ring isn't empty */
-
-/* Speed changes are delayed until the TxRing is empty */
-/* mtt is handled by generating packets with bad CRCs, before the data */
-
-/* TODO: */
-/* check the mtt works ok */
-/* finish the watchdog */
-
-/* No user servicable parts below here */
-
-#include <linux/module.h>
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/rtnetlink.h>
-
-#include <asm/io.h>
-
-#include <net/irda/wrapper.h>
-#include <net/irda/irda.h>
-//#include <net/irda/irmod.h>
-//#include <net/irda/irlap_frame.h>
-#include <net/irda/irda_device.h>
-#include <net/irda/crc.h>
-
-#include "donauboe.h"
-
-#define INB(port) inb_p(port)
-#define OUTB(val,port) outb_p(val,port)
-#define OUTBP(val,port) outb_p(val,port)
-
-#define PROMPT OUTB(OBOE_PROMPT_BIT,OBOE_PROMPT);
-
-#if PROBE_VERBOSE
-#define PROBE_DEBUG(args...) (printk (args))
-#else
-#define PROBE_DEBUG(args...) ;
-#endif
-
-/* Set the DMA to be byte at a time */
-#define CONFIG0H_DMA_OFF OBOE_CONFIG0H_RCVANY
-#define CONFIG0H_DMA_ON_NORX CONFIG0H_DMA_OFF| OBOE_CONFIG0H_ENDMAC
-#define CONFIG0H_DMA_ON CONFIG0H_DMA_ON_NORX | OBOE_CONFIG0H_ENRX
-
-static const struct pci_device_id toshoboe_pci_tbl[] = {
- { PCI_VENDOR_ID_TOSHIBA, PCI_DEVICE_ID_FIR701, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_TOSHIBA, PCI_DEVICE_ID_FIRD01, PCI_ANY_ID, PCI_ANY_ID, },
- { } /* Terminating entry */
-};
-MODULE_DEVICE_TABLE(pci, toshoboe_pci_tbl);
-
-#define DRIVER_NAME "toshoboe"
-static char *driver_name = DRIVER_NAME;
-
-static int max_baud = 4000000;
-#ifdef USE_PROBE
-static bool do_probe = false;
-#endif
-
-
-/**********************************************************************/
-static int
-toshoboe_checkfcs (unsigned char *buf, int len)
-{
- int i;
- union
- {
- __u16 value;
- __u8 bytes[2];
- }
- fcs;
-
- fcs.value = INIT_FCS;
-
- for (i = 0; i < len; ++i)
- fcs.value = irda_fcs (fcs.value, *(buf++));
-
- return fcs.value == GOOD_FCS;
-}
-
-/***********************************************************************/
-/* Generic chip handling code */
-#ifdef DUMP_PACKETS
-static unsigned char dump[50];
-static void
-_dumpbufs (unsigned char *data, int len, char tete)
-{
-int i,j;
-char head=tete;
-for (i=0;i<len;i+=16) {
- for (j=0;j<16 && i+j<len;j++) { sprintf(&dump[3*j],"%02x.",data[i+j]); }
- dump [3*j]=0;
- pr_debug("%c%s\n", head, dump);
- head='+';
- }
-}
-#endif
-
-#ifdef USE_PROBE
-/* Dump the registers */
-static void
-toshoboe_dumpregs (struct toshoboe_cb *self)
-{
- __u32 ringbase;
-
- ringbase = INB (OBOE_RING_BASE0) << 10;
- ringbase |= INB (OBOE_RING_BASE1) << 18;
- ringbase |= INB (OBOE_RING_BASE2) << 26;
-
- printk (KERN_ERR DRIVER_NAME ": Register dump:\n");
- printk (KERN_ERR "Interrupts: Tx:%d Rx:%d TxUnder:%d RxOver:%d Sip:%d\n",
- self->int_tx, self->int_rx, self->int_txunder, self->int_rxover,
- self->int_sip);
- printk (KERN_ERR "RX %02x TX %02x RingBase %08x\n",
- INB (OBOE_RXSLOT), INB (OBOE_TXSLOT), ringbase);
- printk (KERN_ERR "RING_SIZE %02x IER %02x ISR %02x\n",
- INB (OBOE_RING_SIZE), INB (OBOE_IER), INB (OBOE_ISR));
- printk (KERN_ERR "CONFIG1 %02x STATUS %02x\n",
- INB (OBOE_CONFIG1), INB (OBOE_STATUS));
- printk (KERN_ERR "CONFIG0 %02x%02x ENABLE %02x%02x\n",
- INB (OBOE_CONFIG0H), INB (OBOE_CONFIG0L),
- INB (OBOE_ENABLEH), INB (OBOE_ENABLEL));
- printk (KERN_ERR "NEW_PCONFIG %02x%02x CURR_PCONFIG %02x%02x\n",
- INB (OBOE_NEW_PCONFIGH), INB (OBOE_NEW_PCONFIGL),
- INB (OBOE_CURR_PCONFIGH), INB (OBOE_CURR_PCONFIGL));
- printk (KERN_ERR "MAXLEN %02x%02x RXCOUNT %02x%02x\n",
- INB (OBOE_MAXLENH), INB (OBOE_MAXLENL),
- INB (OBOE_RXCOUNTL), INB (OBOE_RXCOUNTH));
-
- if (self->ring)
- {
- int i;
- ringbase = virt_to_bus (self->ring);
- printk (KERN_ERR "Ring at %08x:\n", ringbase);
- printk (KERN_ERR "RX:");
- for (i = 0; i < RX_SLOTS; ++i)
- printk (" (%d,%02x)",self->ring->rx[i].len,self->ring->rx[i].control);
- printk ("\n");
- printk (KERN_ERR "TX:");
- for (i = 0; i < RX_SLOTS; ++i)
- printk (" (%d,%02x)",self->ring->tx[i].len,self->ring->tx[i].control);
- printk ("\n");
- }
-}
-#endif
-
-/*Don't let the chip look at memory */
-static void
-toshoboe_disablebm (struct toshoboe_cb *self)
-{
- __u8 command;
- pci_read_config_byte (self->pdev, PCI_COMMAND, &command);
- command &= ~PCI_COMMAND_MASTER;
- pci_write_config_byte (self->pdev, PCI_COMMAND, command);
-
-}
-
-/* Shutdown the chip and point the taskfile reg somewhere else */
-static void
-toshoboe_stopchip (struct toshoboe_cb *self)
-{
- /*Disable interrupts */
- OUTB (0x0, OBOE_IER);
- /*Disable DMA, Disable Rx, Disable Tx */
- OUTB (CONFIG0H_DMA_OFF, OBOE_CONFIG0H);
- /*Disable SIR MIR FIR, Tx and Rx */
- OUTB (0x00, OBOE_ENABLEH);
- /*Point the ring somewhere safe */
- OUTB (0x3f, OBOE_RING_BASE2);
- OUTB (0xff, OBOE_RING_BASE1);
- OUTB (0xff, OBOE_RING_BASE0);
-
- OUTB (RX_LEN >> 8, OBOE_MAXLENH);
- OUTB (RX_LEN & 0xff, OBOE_MAXLENL);
-
- /*Acknoledge any pending interrupts */
- OUTB (0xff, OBOE_ISR);
-
- /*Why */
- OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH);
-
- /*switch it off */
- OUTB (OBOE_CONFIG1_OFF, OBOE_CONFIG1);
-
- toshoboe_disablebm (self);
-}
-
-/* Transmitter initialization */
-static void
-toshoboe_start_DMA (struct toshoboe_cb *self, int opts)
-{
- OUTB (0x0, OBOE_ENABLEH);
- OUTB (CONFIG0H_DMA_ON | opts, OBOE_CONFIG0H);
- OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH);
- PROMPT;
-}
-
-/*Set the baud rate */
-static void
-toshoboe_setbaud (struct toshoboe_cb *self)
-{
- __u16 pconfig = 0;
- __u8 config0l = 0;
-
- pr_debug("%s(%d/%d)\n", __func__, self->speed, self->io.speed);
-
- switch (self->speed)
- {
- case 2400:
- case 4800:
- case 9600:
- case 19200:
- case 38400:
- case 57600:
- case 115200:
-#ifdef USE_MIR
- case 1152000:
-#endif
- case 4000000:
- break;
- default:
-
- printk (KERN_ERR DRIVER_NAME ": switch to unsupported baudrate %d\n",
- self->speed);
- return;
- }
-
- switch (self->speed)
- {
- /* For SIR the preamble is done by adding XBOFs */
- /* to the packet */
- /* set to filtered SIR mode, filter looks for BOF and EOF */
- case 2400:
- pconfig |= 47 << OBOE_PCONFIG_BAUDSHIFT;
- pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT;
- break;
- case 4800:
- pconfig |= 23 << OBOE_PCONFIG_BAUDSHIFT;
- pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT;
- break;
- case 9600:
- pconfig |= 11 << OBOE_PCONFIG_BAUDSHIFT;
- pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT;
- break;
- case 19200:
- pconfig |= 5 << OBOE_PCONFIG_BAUDSHIFT;
- pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT;
- break;
- case 38400:
- pconfig |= 2 << OBOE_PCONFIG_BAUDSHIFT;
- pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT;
- break;
- case 57600:
- pconfig |= 1 << OBOE_PCONFIG_BAUDSHIFT;
- pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT;
- break;
- case 115200:
- pconfig |= 0 << OBOE_PCONFIG_BAUDSHIFT;
- pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT;
- break;
- default:
- /*Set to packet based reception */
- OUTB (RX_LEN >> 8, OBOE_MAXLENH);
- OUTB (RX_LEN & 0xff, OBOE_MAXLENL);
- break;
- }
-
- switch (self->speed)
- {
- case 2400:
- case 4800:
- case 9600:
- case 19200:
- case 38400:
- case 57600:
- case 115200:
- config0l = OBOE_CONFIG0L_ENSIR;
- if (self->async)
- {
- /*Set to character based reception */
- /*System will lock if MAXLEN=0 */
- /*so have to be careful */
- OUTB (0x01, OBOE_MAXLENH);
- OUTB (0x01, OBOE_MAXLENL);
- OUTB (0x00, OBOE_MAXLENH);
- }
- else
- {
- /*Set to packet based reception */
- config0l |= OBOE_CONFIG0L_ENSIRF;
- OUTB (RX_LEN >> 8, OBOE_MAXLENH);
- OUTB (RX_LEN & 0xff, OBOE_MAXLENL);
- }
- break;
-
-#ifdef USE_MIR
- /* MIR mode */
- /* Set for 16 bit CRC and enable MIR */
- /* Preamble now handled by the chip */
- case 1152000:
- pconfig |= 0 << OBOE_PCONFIG_BAUDSHIFT;
- pconfig |= 8 << OBOE_PCONFIG_WIDTHSHIFT;
- pconfig |= 1 << OBOE_PCONFIG_PREAMBLESHIFT;
- config0l = OBOE_CONFIG0L_CRC16 | OBOE_CONFIG0L_ENMIR;
- break;
-#endif
- /* FIR mode */
- /* Set for 32 bit CRC and enable FIR */
- /* Preamble handled by the chip */
- case 4000000:
- pconfig |= 0 << OBOE_PCONFIG_BAUDSHIFT;
- /* Documentation says 14, but toshiba use 15 in their drivers */
- pconfig |= 15 << OBOE_PCONFIG_PREAMBLESHIFT;
- config0l = OBOE_CONFIG0L_ENFIR;
- break;
- }
-
- /* Copy into new PHY config buffer */
- OUTBP (pconfig >> 8, OBOE_NEW_PCONFIGH);
- OUTB (pconfig & 0xff, OBOE_NEW_PCONFIGL);
- OUTB (config0l, OBOE_CONFIG0L);
-
- /* Now make OBOE copy from new PHY to current PHY */
- OUTB (0x0, OBOE_ENABLEH);
- OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH);
- PROMPT;
-
- /* speed change executed */
- self->new_speed = 0;
- self->io.speed = self->speed;
-}
-
-/*Let the chip look at memory */
-static void
-toshoboe_enablebm (struct toshoboe_cb *self)
-{
- pci_set_master (self->pdev);
-}
-
-/*setup the ring */
-static void
-toshoboe_initring (struct toshoboe_cb *self)
-{
- int i;
-
- for (i = 0; i < TX_SLOTS; ++i)
- {
- self->ring->tx[i].len = 0;
- self->ring->tx[i].control = 0x00;
- self->ring->tx[i].address = virt_to_bus (self->tx_bufs[i]);
- }
-
- for (i = 0; i < RX_SLOTS; ++i)
- {
- self->ring->rx[i].len = RX_LEN;
- self->ring->rx[i].len = 0;
- self->ring->rx[i].address = virt_to_bus (self->rx_bufs[i]);
- self->ring->rx[i].control = OBOE_CTL_RX_HW_OWNS;
- }
-}
-
-static void
-toshoboe_resetptrs (struct toshoboe_cb *self)
-{
- /* Can reset pointers by twidling DMA */
- OUTB (0x0, OBOE_ENABLEH);
- OUTBP (CONFIG0H_DMA_OFF, OBOE_CONFIG0H);
- OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH);
-
- self->rxs = inb_p (OBOE_RXSLOT) & OBOE_SLOT_MASK;
- self->txs = inb_p (OBOE_TXSLOT) & OBOE_SLOT_MASK;
-}
-
-/* Called in locked state */
-static void
-toshoboe_initptrs (struct toshoboe_cb *self)
-{
-
- /* spin_lock_irqsave(self->spinlock, flags); */
- /* save_flags (flags); */
-
- /* Can reset pointers by twidling DMA */
- toshoboe_resetptrs (self);
-
- OUTB (0x0, OBOE_ENABLEH);
- OUTB (CONFIG0H_DMA_ON, OBOE_CONFIG0H);
- OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH);
-
- self->txpending = 0;
-
- /* spin_unlock_irqrestore(self->spinlock, flags); */
- /* restore_flags (flags); */
-}
-
-/* Wake the chip up and get it looking at the rings */
-/* Called in locked state */
-static void
-toshoboe_startchip (struct toshoboe_cb *self)
-{
- __u32 physaddr;
-
- toshoboe_initring (self);
- toshoboe_enablebm (self);
- OUTBP (OBOE_CONFIG1_RESET, OBOE_CONFIG1);
- OUTBP (OBOE_CONFIG1_ON, OBOE_CONFIG1);
-
- /* Stop the clocks */
- OUTB (0, OBOE_ENABLEH);
-
- /*Set size of rings */
- OUTB (RING_SIZE, OBOE_RING_SIZE);
-
- /*Acknoledge any pending interrupts */
- OUTB (0xff, OBOE_ISR);
-
- /*Enable ints */
- OUTB (OBOE_INT_TXDONE | OBOE_INT_RXDONE |
- OBOE_INT_TXUNDER | OBOE_INT_RXOVER | OBOE_INT_SIP , OBOE_IER);
-
- /*Acknoledge any pending interrupts */
- OUTB (0xff, OBOE_ISR);
-
- /*Set the maximum packet length to 0xfff (4095) */
- OUTB (RX_LEN >> 8, OBOE_MAXLENH);
- OUTB (RX_LEN & 0xff, OBOE_MAXLENL);
-
- /*Shutdown DMA */
- OUTB (CONFIG0H_DMA_OFF, OBOE_CONFIG0H);
-
- /*Find out where the rings live */
- physaddr = virt_to_bus (self->ring);
-
- IRDA_ASSERT ((physaddr & 0x3ff) == 0,
- printk (KERN_ERR DRIVER_NAME "ring not correctly aligned\n");
- return;);
-
- OUTB ((physaddr >> 10) & 0xff, OBOE_RING_BASE0);
- OUTB ((physaddr >> 18) & 0xff, OBOE_RING_BASE1);
- OUTB ((physaddr >> 26) & 0x3f, OBOE_RING_BASE2);
-
- /*Enable DMA controller in byte mode and RX */
- OUTB (CONFIG0H_DMA_ON, OBOE_CONFIG0H);
-
- /* Start up the clocks */
- OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH);
-
- /*set to sensible speed */
- self->speed = 9600;
- toshoboe_setbaud (self);
- toshoboe_initptrs (self);
-}
-
-static void
-toshoboe_isntstuck (struct toshoboe_cb *self)
-{
-}
-
-static void
-toshoboe_checkstuck (struct toshoboe_cb *self)
-{
- unsigned long flags;
-
- if (0)
- {
- spin_lock_irqsave(&self->spinlock, flags);
-
- /* This will reset the chip completely */
- printk (KERN_ERR DRIVER_NAME ": Resetting chip\n");
-
- toshoboe_stopchip (self);
- toshoboe_startchip (self);
- spin_unlock_irqrestore(&self->spinlock, flags);
- }
-}
-
-/*Generate packet of about mtt us long */
-static int
-toshoboe_makemttpacket (struct toshoboe_cb *self, void *buf, int mtt)
-{
- int xbofs;
-
- xbofs = ((int) (mtt/100)) * (int) (self->speed);
- xbofs=xbofs/80000; /*Eight bits per byte, and mtt is in us*/
- xbofs++;
-
- pr_debug(DRIVER_NAME ": generated mtt of %d bytes for %d us at %d baud\n",
- xbofs, mtt, self->speed);
-
- if (xbofs > TX_LEN)
- {
- printk (KERN_ERR DRIVER_NAME ": wanted %d bytes MTT but TX_LEN is %d\n",
- xbofs, TX_LEN);
- xbofs = TX_LEN;
- }
-
- /*xbofs will do for SIR, MIR and FIR,SIR mode doesn't generate a checksum anyway */
- memset (buf, XBOF, xbofs);
-
- return xbofs;
-}
-
-#ifdef USE_PROBE
-/***********************************************************************/
-/* Probe code */
-
-static void
-toshoboe_dumptx (struct toshoboe_cb *self)
-{
- int i;
- PROBE_DEBUG(KERN_WARNING "TX:");
- for (i = 0; i < RX_SLOTS; ++i)
- PROBE_DEBUG(" (%d,%02x)",self->ring->tx[i].len,self->ring->tx[i].control);
- PROBE_DEBUG(" [%d]\n",self->speed);
-}
-
-static void
-toshoboe_dumprx (struct toshoboe_cb *self, int score)
-{
- int i;
- PROBE_DEBUG(" %d\nRX:",score);
- for (i = 0; i < RX_SLOTS; ++i)
- PROBE_DEBUG(" (%d,%02x)",self->ring->rx[i].len,self->ring->rx[i].control);
- PROBE_DEBUG("\n");
-}
-
-static inline int
-stuff_byte (__u8 byte, __u8 * buf)
-{
- switch (byte)
- {
- case BOF: /* FALLTHROUGH */
- case EOF: /* FALLTHROUGH */
- case CE:
- /* Insert transparently coded */
- buf[0] = CE; /* Send link escape */
- buf[1] = byte ^ IRDA_TRANS; /* Complement bit 5 */
- return 2;
- /* break; */
- default:
- /* Non-special value, no transparency required */
- buf[0] = byte;
- return 1;
- /* break; */
- }
-}
-
-static irqreturn_t
-toshoboe_probeinterrupt (int irq, void *dev_id)
-{
- struct toshoboe_cb *self = dev_id;
- __u8 irqstat;
-
- irqstat = INB (OBOE_ISR);
-
-/* was it us */
- if (!(irqstat & OBOE_INT_MASK))
- return IRQ_NONE;
-
-/* Ack all the interrupts */
- OUTB (irqstat, OBOE_ISR);
-
- if (irqstat & OBOE_INT_TXDONE)
- {
- int txp;
-
- self->int_tx++;
- PROBE_DEBUG("T");
-
- txp = INB (OBOE_TXSLOT) & OBOE_SLOT_MASK;
- if (self->ring->tx[txp].control & OBOE_CTL_TX_HW_OWNS)
- {
- self->int_tx+=100;
- PROBE_DEBUG("S");
- toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP);
- }
- }
-
- if (irqstat & OBOE_INT_RXDONE) {
- self->int_rx++;
- PROBE_DEBUG("R"); }
- if (irqstat & OBOE_INT_TXUNDER) {
- self->int_txunder++;
- PROBE_DEBUG("U"); }
- if (irqstat & OBOE_INT_RXOVER) {
- self->int_rxover++;
- PROBE_DEBUG("O"); }
- if (irqstat & OBOE_INT_SIP) {
- self->int_sip++;
- PROBE_DEBUG("I"); }
- return IRQ_HANDLED;
-}
-
-static int
-toshoboe_maketestpacket (unsigned char *buf, int badcrc, int fir)
-{
- int i;
- int len = 0;
- union
- {
- __u16 value;
- __u8 bytes[2];
- }
- fcs;
-
- if (fir)
- {
- memset (buf, 0, TT_LEN);
- return TT_LEN;
- }
-
- fcs.value = INIT_FCS;
-
- memset (buf, XBOF, 10);
- len += 10;
- buf[len++] = BOF;
-
- for (i = 0; i < TT_LEN; ++i)
- {
- len += stuff_byte (i, buf + len);
- fcs.value = irda_fcs (fcs.value, i);
- }
-
- len += stuff_byte (fcs.bytes[0] ^ badcrc, buf + len);
- len += stuff_byte (fcs.bytes[1] ^ badcrc, buf + len);
- buf[len++] = EOF;
- len++;
- return len;
-}
-
-static int
-toshoboe_probefail (struct toshoboe_cb *self, char *msg)
-{
- printk (KERN_ERR DRIVER_NAME "probe(%d) failed %s\n",self-> speed, msg);
- toshoboe_dumpregs (self);
- toshoboe_stopchip (self);
- free_irq (self->io.irq, (void *) self);
- return 0;
-}
-
-static int
-toshoboe_numvalidrcvs (struct toshoboe_cb *self)
-{
- int i, ret = 0;
- for (i = 0; i < RX_SLOTS; ++i)
- if ((self->ring->rx[i].control & 0xe0) == 0)
- ret++;
-
- return ret;
-}
-
-static int
-toshoboe_numrcvs (struct toshoboe_cb *self)
-{
- int i, ret = 0;
- for (i = 0; i < RX_SLOTS; ++i)
- if (!(self->ring->rx[i].control & OBOE_CTL_RX_HW_OWNS))
- ret++;
-
- return ret;
-}
-
-static int
-toshoboe_probe (struct toshoboe_cb *self)
-{
- int i, j, n;
-#ifdef USE_MIR
- static const int bauds[] = { 9600, 115200, 4000000, 1152000 };
-#else
- static const int bauds[] = { 9600, 115200, 4000000 };
-#endif
- unsigned long flags;
-
- if (request_irq (self->io.irq, toshoboe_probeinterrupt,
- self->io.irqflags, "toshoboe", (void *) self))
- {
- printk (KERN_ERR DRIVER_NAME ": probe failed to allocate irq %d\n",
- self->io.irq);
- return 0;
- }
-
- /* test 1: SIR filter and back to back */
-
- for (j = 0; j < ARRAY_SIZE(bauds); ++j)
- {
- int fir = (j > 1);
- toshoboe_stopchip (self);
-
-
- spin_lock_irqsave(&self->spinlock, flags);
- /*Address is already setup */
- toshoboe_startchip (self);
- self->int_rx = self->int_tx = 0;
- self->speed = bauds[j];
- toshoboe_setbaud (self);
- toshoboe_initptrs (self);
- spin_unlock_irqrestore(&self->spinlock, flags);
-
- self->ring->tx[self->txs].control =
-/* (FIR only) OBOE_CTL_TX_SIP needed for switching to next slot */
-/* MIR: all received data is stored in one slot */
- (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX
- : OBOE_CTL_TX_HW_OWNS ;
- self->ring->tx[self->txs].len =
- toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir);
- self->txs++;
- self->txs %= TX_SLOTS;
-
- self->ring->tx[self->txs].control =
- (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_SIP
- : OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX ;
- self->ring->tx[self->txs].len =
- toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir);
- self->txs++;
- self->txs %= TX_SLOTS;
-
- self->ring->tx[self->txs].control =
- (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX
- : OBOE_CTL_TX_HW_OWNS ;
- self->ring->tx[self->txs].len =
- toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir);
- self->txs++;
- self->txs %= TX_SLOTS;
-
- self->ring->tx[self->txs].control =
- (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX
- | OBOE_CTL_TX_SIP | OBOE_CTL_TX_BAD_CRC
- : OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX ;
- self->ring->tx[self->txs].len =
- toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir);
- self->txs++;
- self->txs %= TX_SLOTS;
-
- toshoboe_dumptx (self);
- /* Turn on TX and RX and loopback */
- toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP);
-
- i = 0;
- n = fir ? 1 : 4;
- while (toshoboe_numvalidrcvs (self) != n)
- {
- if (i > 4800)
- return toshoboe_probefail (self, "filter test");
- udelay ((9600*(TT_LEN+16))/self->speed);
- i++;
- }
-
- n = fir ? 203 : 102;
- while ((toshoboe_numrcvs(self) != self->int_rx) || (self->int_tx != n))
- {
- if (i > 4800)
- return toshoboe_probefail (self, "interrupt test");
- udelay ((9600*(TT_LEN+16))/self->speed);
- i++;
- }
- toshoboe_dumprx (self,i);
-
- }
-
- /* test 2: SIR in char at a time */
-
- toshoboe_stopchip (self);
- self->int_rx = self->int_tx = 0;
-
- spin_lock_irqsave(&self->spinlock, flags);
- toshoboe_startchip (self);
- spin_unlock_irqrestore(&self->spinlock, flags);
-
- self->async = 1;
- self->speed = 115200;
- toshoboe_setbaud (self);
- self->ring->tx[self->txs].control =
- OBOE_CTL_TX_RTCENTX | OBOE_CTL_TX_HW_OWNS;
- self->ring->tx[self->txs].len = 4;
-
- ((unsigned char *) self->tx_bufs[self->txs])[0] = 'f';
- ((unsigned char *) self->tx_bufs[self->txs])[1] = 'i';
- ((unsigned char *) self->tx_bufs[self->txs])[2] = 's';
- ((unsigned char *) self->tx_bufs[self->txs])[3] = 'h';
- toshoboe_dumptx (self);
- toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP);
-
- i = 0;
- while (toshoboe_numvalidrcvs (self) != 4)
- {
- if (i > 100)
- return toshoboe_probefail (self, "Async test");
- udelay (100);
- i++;
- }
-
- while ((toshoboe_numrcvs (self) != self->int_rx) || (self->int_tx != 1))
- {
- if (i > 100)
- return toshoboe_probefail (self, "Async interrupt test");
- udelay (100);
- i++;
- }
- toshoboe_dumprx (self,i);
-
- self->async = 0;
- self->speed = 9600;
- toshoboe_setbaud (self);
- toshoboe_stopchip (self);
-
- free_irq (self->io.irq, (void *) self);
-
- printk (KERN_WARNING DRIVER_NAME ": Self test passed ok\n");
-
- return 1;
-}
-#endif
-
-/******************************************************************/
-/* Netdev style code */
-
-/* Transmit something */
-static netdev_tx_t
-toshoboe_hard_xmit (struct sk_buff *skb, struct net_device *dev)
-{
- struct toshoboe_cb *self;
- __s32 speed;
- int mtt, len, ctl;
- unsigned long flags;
- struct irda_skb_cb *cb = (struct irda_skb_cb *) skb->cb;
-
- self = netdev_priv(dev);
-
- IRDA_ASSERT (self != NULL, return NETDEV_TX_OK; );
-
- pr_debug("%s.tx:%x(%x)%x\n",
- __func__, skb->len, self->txpending, INB(OBOE_ENABLEH));
- if (!cb->magic) {
- pr_debug("%s.Not IrLAP:%x\n", __func__, cb->magic);
-#ifdef DUMP_PACKETS
- _dumpbufs(skb->data,skb->len,'>');
-#endif
- }
-
- /* change speed pending, wait for its execution */
- if (self->new_speed)
- return NETDEV_TX_BUSY;
-
- /* device stopped (apm) wait for restart */
- if (self->stopped)
- return NETDEV_TX_BUSY;
-
- toshoboe_checkstuck (self);
-
- /* Check if we need to change the speed */
- /* But not now. Wait after transmission if mtt not required */
- speed=irda_get_next_speed(skb);
- if ((speed != self->io.speed) && (speed != -1))
- {
- spin_lock_irqsave(&self->spinlock, flags);
-
- if (self->txpending || skb->len)
- {
- self->new_speed = speed;
- pr_debug("%s: Queued TxDone scheduled speed change %d\n" ,
- __func__, speed);
- /* if no data, that's all! */
- if (!skb->len)
- {
- spin_unlock_irqrestore(&self->spinlock, flags);
- dev_kfree_skb (skb);
- return NETDEV_TX_OK;
- }
- /* True packet, go on, but */
- /* do not accept anything before change speed execution */
- netif_stop_queue(dev);
- /* ready to process TxDone interrupt */
- spin_unlock_irqrestore(&self->spinlock, flags);
- }
- else
- {
- /* idle and no data, change speed now */
- self->speed = speed;
- toshoboe_setbaud (self);
- spin_unlock_irqrestore(&self->spinlock, flags);
- dev_kfree_skb (skb);
- return NETDEV_TX_OK;
- }
-
- }
-
- if ((mtt = irda_get_mtt(skb)))
- {
- /* This is fair since the queue should be empty anyway */
- spin_lock_irqsave(&self->spinlock, flags);
-
- if (self->txpending)
- {
- spin_unlock_irqrestore(&self->spinlock, flags);
- return NETDEV_TX_BUSY;
- }
-
- /* If in SIR mode we need to generate a string of XBOFs */
- /* In MIR and FIR we need to generate a string of data */
- /* which we will add a wrong checksum to */
-
- mtt = toshoboe_makemttpacket (self, self->tx_bufs[self->txs], mtt);
- pr_debug("%s.mtt:%x(%x)%d\n", __func__, skb->len, mtt, self->txpending);
- if (mtt)
- {
- self->ring->tx[self->txs].len = mtt & 0xfff;
-
- ctl = OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX;
- if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_FIRON)
- {
- ctl |= OBOE_CTL_TX_BAD_CRC | OBOE_CTL_TX_SIP ;
- }
-#ifdef USE_MIR
- else if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_MIRON)
- {
- ctl |= OBOE_CTL_TX_BAD_CRC;
- }
-#endif
- self->ring->tx[self->txs].control = ctl;
-
- OUTB (0x0, OBOE_ENABLEH);
- /* It is only a timer. Do not send mtt packet outside! */
- toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP);
-
- self->txpending++;
-
- self->txs++;
- self->txs %= TX_SLOTS;
-
- }
- else
- {
- printk(KERN_ERR DRIVER_NAME ": problem with mtt packet - ignored\n");
- }
- spin_unlock_irqrestore(&self->spinlock, flags);
- }
-
-#ifdef DUMP_PACKETS
-dumpbufs(skb->data,skb->len,'>');
-#endif
-
- spin_lock_irqsave(&self->spinlock, flags);
-
- if (self->ring->tx[self->txs].control & OBOE_CTL_TX_HW_OWNS)
- {
- pr_debug("%s.ful:%x(%x)%x\n",
- __func__, skb->len, self->ring->tx[self->txs].control,
- self->txpending);
- toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX);
- spin_unlock_irqrestore(&self->spinlock, flags);
- return NETDEV_TX_BUSY;
- }
-
- if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_SIRON)
- {
- len = async_wrap_skb (skb, self->tx_bufs[self->txs], TX_BUF_SZ);
- }
- else
- {
- len = skb->len;
- skb_copy_from_linear_data(skb, self->tx_bufs[self->txs], len);
- }
- self->ring->tx[self->txs].len = len & 0x0fff;
-
- /*Sometimes the HW doesn't see us assert RTCENTX in the interrupt code */
- /*later this plays safe, we garuntee the last packet to be transmitted */
- /*has RTCENTX set */
-
- ctl = OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX;
- if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_FIRON)
- {
- ctl |= OBOE_CTL_TX_SIP ;
- }
- self->ring->tx[self->txs].control = ctl;
-
- /* If transmitter is idle start in one-shot mode */
-
- if (!self->txpending)
- toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX);
-
- self->txpending++;
-
- self->txs++;
- self->txs %= TX_SLOTS;
-
- spin_unlock_irqrestore(&self->spinlock, flags);
- dev_kfree_skb (skb);
-
- return NETDEV_TX_OK;
-}
-
-/*interrupt handler */
-static irqreturn_t
-toshoboe_interrupt (int irq, void *dev_id)
-{
- struct toshoboe_cb *self = dev_id;
- __u8 irqstat;
- struct sk_buff *skb = NULL;
-
- irqstat = INB (OBOE_ISR);
-
-/* was it us */
- if (!(irqstat & OBOE_INT_MASK))
- return IRQ_NONE;
-
-/* Ack all the interrupts */
- OUTB (irqstat, OBOE_ISR);
-
- toshoboe_isntstuck (self);
-
-/* Txdone */
- if (irqstat & OBOE_INT_TXDONE)
- {
- int txp, txpc;
- int i;
-
- txp = self->txpending;
- self->txpending = 0;
-
- for (i = 0; i < TX_SLOTS; ++i)
- {
- if (self->ring->tx[i].control & OBOE_CTL_TX_HW_OWNS)
- self->txpending++;
- }
- pr_debug("%s.txd(%x)%x/%x\n", __func__, irqstat, txp, self->txpending);
-
- txp = INB (OBOE_TXSLOT) & OBOE_SLOT_MASK;
-
- /* Got anything queued ? start it together */
- if (self->ring->tx[txp].control & OBOE_CTL_TX_HW_OWNS)
- {
- txpc = txp;
-#ifdef OPTIMIZE_TX
- while (self->ring->tx[txpc].control & OBOE_CTL_TX_HW_OWNS)
- {
- txp = txpc;
- txpc++;
- txpc %= TX_SLOTS;
- self->netdev->stats.tx_packets++;
- if (self->ring->tx[txpc].control & OBOE_CTL_TX_HW_OWNS)
- self->ring->tx[txp].control &= ~OBOE_CTL_TX_RTCENTX;
- }
- self->netdev->stats.tx_packets--;
-#else
- self->netdev->stats.tx_packets++;
-#endif
- toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX);
- }
-
- if ((!self->txpending) && (self->new_speed))
- {
- self->speed = self->new_speed;
- pr_debug("%s: Executed TxDone scheduled speed change %d\n",
- __func__, self->speed);
- toshoboe_setbaud (self);
- }
-
- /* Tell network layer that we want more frames */
- if (!self->new_speed)
- netif_wake_queue(self->netdev);
- }
-
- if (irqstat & OBOE_INT_RXDONE)
- {
- while (!(self->ring->rx[self->rxs].control & OBOE_CTL_RX_HW_OWNS))
- {
- int len = self->ring->rx[self->rxs].len;
- skb = NULL;
- pr_debug("%s.rcv:%x(%x)\n", __func__
- , len, self->ring->rx[self->rxs].control);
-
-#ifdef DUMP_PACKETS
-dumpbufs(self->rx_bufs[self->rxs],len,'<');
-#endif
-
- if (self->ring->rx[self->rxs].control == 0)
- {
- __u8 enable = INB (OBOE_ENABLEH);
-
- /* In SIR mode we need to check the CRC as this */
- /* hasn't been done by the hardware */
- if (enable & OBOE_ENABLEH_SIRON)
- {
- if (!toshoboe_checkfcs (self->rx_bufs[self->rxs], len))
- len = 0;
- /*Trim off the CRC */
- if (len > 1)
- len -= 2;
- else
- len = 0;
- pr_debug("%s.SIR:%x(%x)\n", __func__, len, enable);
- }
-
-#ifdef USE_MIR
- else if (enable & OBOE_ENABLEH_MIRON)
- {
- if (len > 1)
- len -= 2;
- else
- len = 0;
- pr_debug("%s.MIR:%x(%x)\n", __func__, len, enable);
- }
-#endif
- else if (enable & OBOE_ENABLEH_FIRON)
- {
- if (len > 3)
- len -= 4; /*FIXME: check this */
- else
- len = 0;
- pr_debug("%s.FIR:%x(%x)\n", __func__, len, enable);
- }
- else
- pr_debug("%s.?IR:%x(%x)\n", __func__, len, enable);
-
- if (len)
- {
- skb = dev_alloc_skb (len + 1);
- if (skb)
- {
- skb_reserve (skb, 1);
-
- skb_put (skb, len);
- skb_copy_to_linear_data(skb, self->rx_bufs[self->rxs],
- len);
- self->netdev->stats.rx_packets++;
- skb->dev = self->netdev;
- skb_reset_mac_header(skb);
- skb->protocol = htons (ETH_P_IRDA);
- }
- else
- {
- printk (KERN_INFO
- "%s(), memory squeeze, dropping frame.\n",
- __func__);
- }
- }
- }
- else
- {
- /* TODO: =========================================== */
- /* if OBOE_CTL_RX_LENGTH, our buffers are too small */
- /* (MIR or FIR) data is lost. */
- /* (SIR) data is splitted in several slots. */
- /* we have to join all the received buffers received */
- /*in a large buffer before checking CRC. */
- pr_debug("%s.err:%x(%x)\n", __func__
- , len, self->ring->rx[self->rxs].control);
- }
-
- self->ring->rx[self->rxs].len = 0x0;
- self->ring->rx[self->rxs].control = OBOE_CTL_RX_HW_OWNS;
-
- self->rxs++;
- self->rxs %= RX_SLOTS;
-
- if (skb)
- netif_rx (skb);
-
- }
- }
-
- if (irqstat & OBOE_INT_TXUNDER)
- {
- printk (KERN_WARNING DRIVER_NAME ": tx fifo underflow\n");
- }
- if (irqstat & OBOE_INT_RXOVER)
- {
- printk (KERN_WARNING DRIVER_NAME ": rx fifo overflow\n");
- }
-/* This must be useful for something... */
- if (irqstat & OBOE_INT_SIP)
- {
- self->int_sip++;
- pr_debug("%s.sip:%x(%x)%x\n",
- __func__, self->int_sip, irqstat, self->txpending);
- }
- return IRQ_HANDLED;
-}
-
-
-static int
-toshoboe_net_open (struct net_device *dev)
-{
- struct toshoboe_cb *self;
- unsigned long flags;
- int rc;
-
- self = netdev_priv(dev);
-
- if (self->async)
- return -EBUSY;
-
- if (self->stopped)
- return 0;
-
- rc = request_irq (self->io.irq, toshoboe_interrupt,
- IRQF_SHARED, dev->name, self);
- if (rc)
- return rc;
-
- spin_lock_irqsave(&self->spinlock, flags);
- toshoboe_startchip (self);
- spin_unlock_irqrestore(&self->spinlock, flags);
-
- /* Ready to play! */
- netif_start_queue(dev);
-
- /*
- * Open new IrLAP layer instance, now that everything should be
- * initialized properly
- */
- self->irlap = irlap_open (dev, &self->qos, driver_name);
-
- self->irdad = 1;
-
- return 0;
-}
-
-static int
-toshoboe_net_close (struct net_device *dev)
-{
- struct toshoboe_cb *self;
-
- IRDA_ASSERT (dev != NULL, return -1; );
- self = netdev_priv(dev);
-
- /* Stop device */
- netif_stop_queue(dev);
-
- /* Stop and remove instance of IrLAP */
- if (self->irlap)
- irlap_close (self->irlap);
- self->irlap = NULL;
-
- self->irdad = 0;
-
- free_irq (self->io.irq, (void *) self);
-
- if (!self->stopped)
- {
- toshoboe_stopchip (self);
- }
-
- return 0;
-}
-
-/*
- * Function toshoboe_net_ioctl (dev, rq, cmd)
- *
- * Process IOCTL commands for this device
- *
- */
-static int
-toshoboe_net_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
-{
- struct if_irda_req *irq = (struct if_irda_req *) rq;
- struct toshoboe_cb *self;
- unsigned long flags;
- int ret = 0;
-
- IRDA_ASSERT (dev != NULL, return -1; );
-
- self = netdev_priv(dev);
-
- IRDA_ASSERT (self != NULL, return -1; );
-
- pr_debug("%s(), %s, (cmd=0x%X)\n", __func__, dev->name, cmd);
-
- /* Disable interrupts & save flags */
- spin_lock_irqsave(&self->spinlock, flags);
-
- switch (cmd)
- {
- case SIOCSBANDWIDTH: /* Set bandwidth */
- /* This function will also be used by IrLAP to change the
- * speed, so we still must allow for speed change within
- * interrupt context.
- */
- pr_debug("%s(BANDWIDTH), %s, (%X/%ld\n",
- __func__, dev->name, INB(OBOE_STATUS), irq->ifr_baudrate);
- if (!in_interrupt () && !capable (CAP_NET_ADMIN)) {
- ret = -EPERM;
- goto out;
- }
-
- /* self->speed=irq->ifr_baudrate; */
- /* toshoboe_setbaud(self); */
- /* Just change speed once - inserted by Paul Bristow */
- self->new_speed = irq->ifr_baudrate;
- break;
- case SIOCSMEDIABUSY: /* Set media busy */
- pr_debug("%s(MEDIABUSY), %s, (%X/%x)\n",
- __func__, dev->name,
- INB(OBOE_STATUS), capable(CAP_NET_ADMIN));
- if (!capable (CAP_NET_ADMIN)) {
- ret = -EPERM;
- goto out;
- }
- irda_device_set_media_busy (self->netdev, TRUE);
- break;
- case SIOCGRECEIVING: /* Check if we are receiving right now */
- irq->ifr_receiving = (INB (OBOE_STATUS) & OBOE_STATUS_RXBUSY) ? 1 : 0;
- pr_debug("%s(RECEIVING), %s, (%X/%x)\n",
- __func__, dev->name, INB(OBOE_STATUS), irq->ifr_receiving);
- break;
- default:
- pr_debug("%s(?), %s, (cmd=0x%X)\n", __func__, dev->name, cmd);
- ret = -EOPNOTSUPP;
- }
-out:
- spin_unlock_irqrestore(&self->spinlock, flags);
- return ret;
-
-}
-
-MODULE_DESCRIPTION("Toshiba OBOE IrDA Device Driver");
-MODULE_AUTHOR("James McKenzie <james@fishsoup.dhs.org>");
-MODULE_LICENSE("GPL");
-
-module_param (max_baud, int, 0);
-MODULE_PARM_DESC(max_baud, "Maximum baud rate");
-
-#ifdef USE_PROBE
-module_param (do_probe, bool, 0);
-MODULE_PARM_DESC(do_probe, "Enable/disable chip probing and self-test");
-#endif
-
-static void
-toshoboe_close (struct pci_dev *pci_dev)
-{
- int i;
- struct toshoboe_cb *self = pci_get_drvdata(pci_dev);
-
- IRDA_ASSERT (self != NULL, return; );
-
- if (!self->stopped)
- {
- toshoboe_stopchip (self);
- }
-
- release_region (self->io.fir_base, self->io.fir_ext);
-
- for (i = 0; i < TX_SLOTS; ++i)
- {
- kfree (self->tx_bufs[i]);
- self->tx_bufs[i] = NULL;
- }
-
- for (i = 0; i < RX_SLOTS; ++i)
- {
- kfree (self->rx_bufs[i]);
- self->rx_bufs[i] = NULL;
- }
-
- unregister_netdev(self->netdev);
-
- kfree (self->ringbuf);
- self->ringbuf = NULL;
- self->ring = NULL;
-
- free_netdev(self->netdev);
-}
-
-static const struct net_device_ops toshoboe_netdev_ops = {
- .ndo_open = toshoboe_net_open,
- .ndo_stop = toshoboe_net_close,
- .ndo_start_xmit = toshoboe_hard_xmit,
- .ndo_do_ioctl = toshoboe_net_ioctl,
-};
-
-static int
-toshoboe_open (struct pci_dev *pci_dev, const struct pci_device_id *pdid)
-{
- struct toshoboe_cb *self;
- struct net_device *dev;
- int i = 0;
- int ok = 0;
- int err;
-
- if ((err=pci_enable_device(pci_dev)))
- return err;
-
- dev = alloc_irdadev(sizeof (struct toshoboe_cb));
- if (dev == NULL)
- {
- printk (KERN_ERR DRIVER_NAME ": can't allocate memory for "
- "IrDA control block\n");
- return -ENOMEM;
- }
-
- self = netdev_priv(dev);
- self->netdev = dev;
- self->pdev = pci_dev;
- self->base = pci_resource_start(pci_dev,0);
-
- self->io.fir_base = self->base;
- self->io.fir_ext = OBOE_IO_EXTENT;
- self->io.irq = pci_dev->irq;
- self->io.irqflags = IRQF_SHARED;
-
- self->speed = self->io.speed = 9600;
- self->async = 0;
-
- /* Lock the port that we need */
- if (NULL==request_region (self->io.fir_base, self->io.fir_ext, driver_name))
- {
- printk (KERN_ERR DRIVER_NAME ": can't get iobase of 0x%03x\n"
- ,self->io.fir_base);
- err = -EBUSY;
- goto freeself;
- }
-
- spin_lock_init(&self->spinlock);
-
- irda_init_max_qos_capabilies (&self->qos);
- self->qos.baud_rate.bits = 0;
-
- if (max_baud >= 2400)
- self->qos.baud_rate.bits |= IR_2400;
- /*if (max_baud>=4800) idev->qos.baud_rate.bits|=IR_4800; */
- if (max_baud >= 9600)
- self->qos.baud_rate.bits |= IR_9600;
- if (max_baud >= 19200)
- self->qos.baud_rate.bits |= IR_19200;
- if (max_baud >= 115200)
- self->qos.baud_rate.bits |= IR_115200;
-#ifdef USE_MIR
- if (max_baud >= 1152000)
- {
- self->qos.baud_rate.bits |= IR_1152000;
- }
-#endif
- if (max_baud >= 4000000)
- {
- self->qos.baud_rate.bits |= (IR_4000000 << 8);
- }
-
- /*FIXME: work this out... */
- self->qos.min_turn_time.bits = 0xff;
-
- irda_qos_bits_to_value (&self->qos);
-
- /* Allocate twice the size to guarantee alignment */
- self->ringbuf = kmalloc(OBOE_RING_LEN << 1, GFP_KERNEL);
- if (!self->ringbuf)
- {
- err = -ENOMEM;
- goto freeregion;
- }
-
-#if (BITS_PER_LONG == 64)
-#error broken on 64-bit: casts pointer to 32-bit, and then back to pointer.
-#endif
-
- /*We need to align the taskfile on a taskfile size boundary */
- {
- unsigned long addr;
-
- addr = (__u32) self->ringbuf;
- addr &= ~(OBOE_RING_LEN - 1);
- addr += OBOE_RING_LEN;
- self->ring = (struct OboeRing *) addr;
- }
-
- memset (self->ring, 0, OBOE_RING_LEN);
- self->io.mem_base = (__u32) self->ring;
-
- ok = 1;
- for (i = 0; i < TX_SLOTS; ++i)
- {
- self->tx_bufs[i] = kmalloc (TX_BUF_SZ, GFP_KERNEL);
- if (!self->tx_bufs[i])
- ok = 0;
- }
-
- for (i = 0; i < RX_SLOTS; ++i)
- {
- self->rx_bufs[i] = kmalloc (RX_BUF_SZ, GFP_KERNEL);
- if (!self->rx_bufs[i])
- ok = 0;
- }
-
- if (!ok)
- {
- err = -ENOMEM;
- goto freebufs;
- }
-
-
-#ifdef USE_PROBE
- if (do_probe)
- if (!toshoboe_probe (self))
- {
- err = -ENODEV;
- goto freebufs;
- }
-#endif
-
- SET_NETDEV_DEV(dev, &pci_dev->dev);
- dev->netdev_ops = &toshoboe_netdev_ops;
-
- err = register_netdev(dev);
- if (err)
- {
- printk (KERN_ERR DRIVER_NAME ": register_netdev() failed\n");
- err = -ENOMEM;
- goto freebufs;
- }
- printk (KERN_INFO "IrDA: Registered device %s\n", dev->name);
-
- pci_set_drvdata(pci_dev,self);
-
- printk (KERN_INFO DRIVER_NAME ": Using multiple tasks\n");
-
- return 0;
-
-freebufs:
- for (i = 0; i < TX_SLOTS; ++i)
- kfree (self->tx_bufs[i]);
- for (i = 0; i < RX_SLOTS; ++i)
- kfree (self->rx_bufs[i]);
- kfree(self->ringbuf);
-
-freeregion:
- release_region (self->io.fir_base, self->io.fir_ext);
-
-freeself:
- free_netdev(dev);
-
- return err;
-}
-
-static int
-toshoboe_gotosleep (struct pci_dev *pci_dev, pm_message_t crap)
-{
- struct toshoboe_cb *self = pci_get_drvdata(pci_dev);
- unsigned long flags;
- int i = 10;
-
- if (!self || self->stopped)
- return 0;
-
- if ((!self->irdad) && (!self->async))
- return 0;
-
-/* Flush all packets */
- while ((i--) && (self->txpending))
- msleep(10);
-
- spin_lock_irqsave(&self->spinlock, flags);
-
- toshoboe_stopchip (self);
- self->stopped = 1;
- self->txpending = 0;
-
- spin_unlock_irqrestore(&self->spinlock, flags);
- return 0;
-}
-
-static int
-toshoboe_wakeup (struct pci_dev *pci_dev)
-{
- struct toshoboe_cb *self = pci_get_drvdata(pci_dev);
- unsigned long flags;
-
- if (!self || !self->stopped)
- return 0;
-
- if ((!self->irdad) && (!self->async))
- return 0;
-
- spin_lock_irqsave(&self->spinlock, flags);
-
- toshoboe_startchip (self);
- self->stopped = 0;
-
- netif_wake_queue(self->netdev);
- spin_unlock_irqrestore(&self->spinlock, flags);
- return 0;
-}
-
-static struct pci_driver donauboe_pci_driver = {
- .name = "donauboe",
- .id_table = toshoboe_pci_tbl,
- .probe = toshoboe_open,
- .remove = toshoboe_close,
- .suspend = toshoboe_gotosleep,
- .resume = toshoboe_wakeup
-};
-
-module_pci_driver(donauboe_pci_driver);
diff --git a/drivers/staging/irda/drivers/donauboe.h b/drivers/staging/irda/drivers/donauboe.h
deleted file mode 100644
index d92d54e839b9..000000000000
--- a/drivers/staging/irda/drivers/donauboe.h
+++ /dev/null
@@ -1,362 +0,0 @@
-/*********************************************************************
- *
- * Filename: toshoboe.h
- * Version: 2.16
- * Description: Driver for the Toshiba OBOE (or type-O or 701)
- * FIR Chipset, also supports the DONAUOBOE (type-DO
- * or d01) FIR chipset which as far as I know is
- * register compatible.
- * Status: Experimental.
- * Author: James McKenzie <james@fishsoup.dhs.org>
- * Created at: Sat May 8 12:35:27 1999
- * Modified: 2.16 Martin Lucina <mato@kotelna.sk>
- * Modified: 2.16 Sat Jun 22 18:54:29 2002 (sync headers)
- * Modified: 2.17 Christian Gennerat <christian.gennerat@polytechnique.org>
- * Modified: 2.17 jeu sep 12 08:50:20 2002 (add lock to be used by spinlocks)
- *
- * Copyright (c) 1999 James McKenzie, All Rights Reserved.
- *
- * 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.
- *
- * Neither James McKenzie nor Cambridge University admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- * Applicable Models : Libretto 100/110CT and many more.
- * Toshiba refers to this chip as the type-O IR port,
- * or the type-DO IR port.
- *
- * IrDA chip set list from Toshiba Computer Engineering Corp.
- * model method maker controller Version
- * Portege 320CT FIR,SIR Toshiba Oboe(Triangle)
- * Portege 3010CT FIR,SIR Toshiba Oboe(Sydney)
- * Portege 3015CT FIR,SIR Toshiba Oboe(Sydney)
- * Portege 3020CT FIR,SIR Toshiba Oboe(Sydney)
- * Portege 7020CT FIR,SIR ? ?
- *
- * Satell. 4090XCDT FIR,SIR ? ?
- *
- * Libretto 100CT FIR,SIR Toshiba Oboe
- * Libretto 1000CT FIR,SIR Toshiba Oboe
- *
- * TECRA750DVD FIR,SIR Toshiba Oboe(Triangle) REV ID=14h
- * TECRA780 FIR,SIR Toshiba Oboe(Sandlot) REV ID=32h,33h
- * TECRA750CDT FIR,SIR Toshiba Oboe(Triangle) REV ID=13h,14h
- * TECRA8000 FIR,SIR Toshiba Oboe(ISKUR) REV ID=23h
- *
- ********************************************************************/
-
-/* The documentation for this chip is allegedly released */
-/* However I have not seen it, not have I managed to contact */
-/* anyone who has. HOWEVER the chip bears a striking resemblance */
-/* to the IrDA controller in the Toshiba RISC TMPR3922 chip */
-/* the documentation for this is freely available at */
-/* http://www.madingley.org/james/resources/toshoboe/TMPR3922.pdf */
-/* The mapping between the registers in that document and the */
-/* Registers in the 701 oboe chip are as follows */
-
-
-/* 3922 reg 701 regs, by bit numbers */
-/* 7- 0 15- 8 24-16 31-25 */
-/* $28 0x0 0x1 */
-/* $2c SEE NOTE 1 */
-/* $30 0x6 0x7 */
-/* $34 0x8 0x9 SEE NOTE 2 */
-/* $38 0x10 0x11 */
-/* $3C 0xe SEE NOTE 3 */
-/* $40 0x12 0x13 */
-/* $44 0x14 0x15 */
-/* $48 0x16 0x17 */
-/* $4c 0x18 0x19 */
-/* $50 0x1a 0x1b */
-
-/* FIXME: could be 0x1b 0x1a here */
-
-/* $54 0x1d 0x1c */
-/* $5C 0xf SEE NOTE 4 */
-/* $130 SEE NOTE 5 */
-/* $134 SEE NOTE 6 */
-/* */
-/* NOTES: */
-/* 1. The pointer to ring is packed in most unceremoniusly */
-/* 701 Register Address bits (A9-A0 must be zero) */
-/* 0x4: A17 A16 A15 A14 A13 A12 A11 A10 */
-/* 0x5: A25 A24 A23 A22 A21 A20 A19 A18 */
-/* 0x2: 0 0 A31 A30 A29 A28 A27 A26 */
-/* */
-/* 2. The M$ drivers do a write 0x1 to 0x9, however the 3922 */
-/* documentation would suggest that a write of 0x1 to 0x8 */
-/* would be more appropriate. */
-/* */
-/* 3. This assignment is tenuous at best, register 0xe seems to */
-/* have bits arranged 0 0 0 R/W R/W R/W R/W R/W */
-/* if either of the lower two bits are set the chip seems to */
-/* switch off */
-/* */
-/* 4. Bits 7-4 seem to be different 4 seems just to be generic */
-/* receiver busy flag */
-/* */
-/* 5. and 6. The IER and ISR have a different bit assignment */
-/* The lower three bits of both read back as ones */
-/* ISR is register 0xc, IER is register 0xd */
-/* 7 6 5 4 3 2 1 0 */
-/* 0xc: TxDone RxDone TxUndr RxOver SipRcv 1 1 1 */
-/* 0xd: TxDone RxDone TxUndr RxOver SipRcv 1 1 1 */
-/* TxDone xmitt done (generated only if generate interrupt bit */
-/* is set in the ring) */
-/* RxDone recv completed (or other recv condition if you set it */
-/* up */
-/* TxUnder underflow in Transmit FIFO */
-/* RxOver overflow in Recv FIFO */
-/* SipRcv received serial gap (or other condition you set) */
-/* Interrupts are enabled by writing a one to the IER register */
-/* Interrupts are cleared by writing a one to the ISR register */
-/* */
-/* 6. The remaining registers: 0x6 and 0x3 appear to be */
-/* reserved parts of 16 or 32 bit registersthe remainder */
-/* 0xa 0xb 0x1e 0x1f could possibly be (by their behaviour) */
-/* the Unicast Filter register at $58. */
-/* */
-/* 7. While the core obviously expects 32 bit accesses all the */
-/* M$ drivers do 8 bit accesses, infact the Miniport ones */
-/* write and read back the byte serveral times (why?) */
-
-
-#ifndef TOSHOBOE_H
-#define TOSHOBOE_H
-
-/* Registers */
-
-#define OBOE_IO_EXTENT 0x1f
-
-/*Receive and transmit slot pointers */
-#define OBOE_REG(i) (i+(self->base))
-#define OBOE_RXSLOT OBOE_REG(0x0)
-#define OBOE_TXSLOT OBOE_REG(0x1)
-#define OBOE_SLOT_MASK 0x3f
-
-#define OBOE_TXRING_OFFSET 0x200
-#define OBOE_TXRING_OFFSET_IN_SLOTS 0x40
-
-/*pointer to the ring */
-#define OBOE_RING_BASE0 OBOE_REG(0x4)
-#define OBOE_RING_BASE1 OBOE_REG(0x5)
-#define OBOE_RING_BASE2 OBOE_REG(0x2)
-#define OBOE_RING_BASE3 OBOE_REG(0x3)
-
-/*Number of slots in the ring */
-#define OBOE_RING_SIZE OBOE_REG(0x7)
-#define OBOE_RING_SIZE_RX4 0x00
-#define OBOE_RING_SIZE_RX8 0x01
-#define OBOE_RING_SIZE_RX16 0x03
-#define OBOE_RING_SIZE_RX32 0x07
-#define OBOE_RING_SIZE_RX64 0x0f
-#define OBOE_RING_SIZE_TX4 0x00
-#define OBOE_RING_SIZE_TX8 0x10
-#define OBOE_RING_SIZE_TX16 0x30
-#define OBOE_RING_SIZE_TX32 0x70
-#define OBOE_RING_SIZE_TX64 0xf0
-
-#define OBOE_RING_MAX_SIZE 64
-
-/*Causes the gubbins to re-examine the ring */
-#define OBOE_PROMPT OBOE_REG(0x9)
-#define OBOE_PROMPT_BIT 0x1
-
-/* Interrupt Status Register */
-#define OBOE_ISR OBOE_REG(0xc)
-/* Interrupt Enable Register */
-#define OBOE_IER OBOE_REG(0xd)
-/* Interrupt bits for IER and ISR */
-#define OBOE_INT_TXDONE 0x80
-#define OBOE_INT_RXDONE 0x40
-#define OBOE_INT_TXUNDER 0x20
-#define OBOE_INT_RXOVER 0x10
-#define OBOE_INT_SIP 0x08
-#define OBOE_INT_MASK 0xf8
-
-/*Reset Register */
-#define OBOE_CONFIG1 OBOE_REG(0xe)
-#define OBOE_CONFIG1_RST 0x01
-#define OBOE_CONFIG1_DISABLE 0x02
-#define OBOE_CONFIG1_4 0x08
-#define OBOE_CONFIG1_8 0x08
-
-#define OBOE_CONFIG1_ON 0x8
-#define OBOE_CONFIG1_RESET 0xf
-#define OBOE_CONFIG1_OFF 0xe
-
-#define OBOE_STATUS OBOE_REG(0xf)
-#define OBOE_STATUS_RXBUSY 0x10
-#define OBOE_STATUS_FIRRX 0x04
-#define OBOE_STATUS_MIRRX 0x02
-#define OBOE_STATUS_SIRRX 0x01
-
-
-/*Speed control registers */
-#define OBOE_CONFIG0L OBOE_REG(0x10)
-#define OBOE_CONFIG0H OBOE_REG(0x11)
-
-#define OBOE_CONFIG0H_TXONLOOP 0x80 /*Transmit when looping (dangerous) */
-#define OBOE_CONFIG0H_LOOP 0x40 /*Loopback Tx->Rx */
-#define OBOE_CONFIG0H_ENTX 0x10 /*Enable Tx */
-#define OBOE_CONFIG0H_ENRX 0x08 /*Enable Rx */
-#define OBOE_CONFIG0H_ENDMAC 0x04 /*Enable/reset* the DMA controller */
-#define OBOE_CONFIG0H_RCVANY 0x02 /*DMA mode 1=bytes, 0=dwords */
-
-#define OBOE_CONFIG0L_CRC16 0x80 /*CRC 1=16 bit 0=32 bit */
-#define OBOE_CONFIG0L_ENFIR 0x40 /*Enable FIR */
-#define OBOE_CONFIG0L_ENMIR 0x20 /*Enable MIR */
-#define OBOE_CONFIG0L_ENSIR 0x10 /*Enable SIR */
-#define OBOE_CONFIG0L_ENSIRF 0x08 /*Enable SIR framer */
-#define OBOE_CONFIG0L_SIRTEST 0x04 /*Enable SIR framer in MIR and FIR */
-#define OBOE_CONFIG0L_INVERTTX 0x02 /*Invert Tx Line */
-#define OBOE_CONFIG0L_INVERTRX 0x01 /*Invert Rx Line */
-
-#define OBOE_BOF OBOE_REG(0x12)
-#define OBOE_EOF OBOE_REG(0x13)
-
-#define OBOE_ENABLEL OBOE_REG(0x14)
-#define OBOE_ENABLEH OBOE_REG(0x15)
-
-#define OBOE_ENABLEH_PHYANDCLOCK 0x80 /*Toggle low to copy config in */
-#define OBOE_ENABLEH_CONFIGERR 0x40
-#define OBOE_ENABLEH_FIRON 0x20
-#define OBOE_ENABLEH_MIRON 0x10
-#define OBOE_ENABLEH_SIRON 0x08
-#define OBOE_ENABLEH_ENTX 0x04
-#define OBOE_ENABLEH_ENRX 0x02
-#define OBOE_ENABLEH_CRC16 0x01
-
-#define OBOE_ENABLEL_BROADCAST 0x01
-
-#define OBOE_CURR_PCONFIGL OBOE_REG(0x16) /*Current config */
-#define OBOE_CURR_PCONFIGH OBOE_REG(0x17)
-
-#define OBOE_NEW_PCONFIGL OBOE_REG(0x18)
-#define OBOE_NEW_PCONFIGH OBOE_REG(0x19)
-
-#define OBOE_PCONFIGH_BAUDMASK 0xfc
-#define OBOE_PCONFIGH_WIDTHMASK 0x04
-#define OBOE_PCONFIGL_WIDTHMASK 0xe0
-#define OBOE_PCONFIGL_PREAMBLEMASK 0x1f
-
-#define OBOE_PCONFIG_BAUDMASK 0xfc00
-#define OBOE_PCONFIG_BAUDSHIFT 10
-#define OBOE_PCONFIG_WIDTHMASK 0x04e0
-#define OBOE_PCONFIG_WIDTHSHIFT 5
-#define OBOE_PCONFIG_PREAMBLEMASK 0x001f
-#define OBOE_PCONFIG_PREAMBLESHIFT 0
-
-#define OBOE_MAXLENL OBOE_REG(0x1a)
-#define OBOE_MAXLENH OBOE_REG(0x1b)
-
-#define OBOE_RXCOUNTH OBOE_REG(0x1c) /*Reset on recipt */
-#define OBOE_RXCOUNTL OBOE_REG(0x1d) /*of whole packet */
-
-/* The PCI ID of the OBOE chip */
-#ifndef PCI_DEVICE_ID_FIR701
-#define PCI_DEVICE_ID_FIR701 0x0701
-#endif
-
-#ifndef PCI_DEVICE_ID_FIRD01
-#define PCI_DEVICE_ID_FIRD01 0x0d01
-#endif
-
-struct OboeSlot
-{
- __u16 len; /*Tweleve bits of packet length */
- __u8 unused;
- __u8 control; /*Slot control/status see below */
- __u32 address; /*Slot buffer address */
-}
-__packed;
-
-#define OBOE_NTASKS OBOE_TXRING_OFFSET_IN_SLOTS
-
-struct OboeRing
-{
- struct OboeSlot rx[OBOE_NTASKS];
- struct OboeSlot tx[OBOE_NTASKS];
-};
-
-#define OBOE_RING_LEN (sizeof(struct OboeRing))
-
-
-#define OBOE_CTL_TX_HW_OWNS 0x80 /*W/R This slot owned by the hardware */
-#define OBOE_CTL_TX_DISTX_CRC 0x40 /*W Disable CRC generation for [FM]IR */
-#define OBOE_CTL_TX_BAD_CRC 0x20 /*W Generate bad CRC */
-#define OBOE_CTL_TX_SIP 0x10 /*W Generate an SIP after xmittion */
-#define OBOE_CTL_TX_MKUNDER 0x08 /*W Generate an underrun error */
-#define OBOE_CTL_TX_RTCENTX 0x04 /*W Enable receiver and generate TXdone */
- /* After this slot is processed */
-#define OBOE_CTL_TX_UNDER 0x01 /*R Set by hardware to indicate underrun */
-
-
-#define OBOE_CTL_RX_HW_OWNS 0x80 /*W/R This slot owned by hardware */
-#define OBOE_CTL_RX_PHYERR 0x40 /*R Decoder error on receiption */
-#define OBOE_CTL_RX_CRCERR 0x20 /*R CRC error only set for [FM]IR */
-#define OBOE_CTL_RX_LENGTH 0x10 /*R Packet > max Rx length */
-#define OBOE_CTL_RX_OVER 0x08 /*R set to indicate an overflow */
-#define OBOE_CTL_RX_SIRBAD 0x04 /*R SIR had BOF in packet or ABORT sequence */
-#define OBOE_CTL_RX_RXEOF 0x02 /*R Finished receiving on this slot */
-
-
-struct toshoboe_cb
-{
- struct net_device *netdev; /* Yes! we are some kind of netdevice */
- struct tty_driver ttydev;
-
- struct irlap_cb *irlap; /* The link layer we are binded to */
-
- chipio_t io; /* IrDA controller information */
- struct qos_info qos; /* QoS capabilities for this device */
-
- __u32 flags; /* Interface flags */
-
- struct pci_dev *pdev; /*PCI device */
- int base; /*IO base */
-
-
- int txpending; /*how many tx's are pending */
- int txs, rxs; /*Which slots are we at */
-
- int irdad; /*Driver under control of netdev end */
- int async; /*Driver under control of async end */
-
-
- int stopped; /*Stopped by some or other APM stuff */
-
- int filter; /*In SIR mode do we want to receive
- frames or byte ranges */
-
- void *ringbuf; /*The ring buffer */
- struct OboeRing *ring; /*The ring */
-
- void *tx_bufs[OBOE_RING_MAX_SIZE]; /*The buffers */
- void *rx_bufs[OBOE_RING_MAX_SIZE];
-
-
- int speed; /*Current setting of the speed */
- int new_speed; /*Set to request a speed change */
-
-/* The spinlock protect critical parts of the driver.
- * Locking is done like this :
- * spin_lock_irqsave(&self->spinlock, flags);
- * Releasing the lock :
- * spin_unlock_irqrestore(&self->spinlock, flags);
- */
- spinlock_t spinlock;
- /* Used for the probe and diagnostics code */
- int int_rx;
- int int_tx;
- int int_txunder;
- int int_rxover;
- int int_sip;
-};
-
-
-#endif
diff --git a/drivers/staging/irda/drivers/esi-sir.c b/drivers/staging/irda/drivers/esi-sir.c
deleted file mode 100644
index eb7aa6430bea..000000000000
--- a/drivers/staging/irda/drivers/esi-sir.c
+++ /dev/null
@@ -1,157 +0,0 @@
-/*********************************************************************
- *
- * Filename: esi.c
- * Version: 1.6
- * Description: Driver for the Extended Systems JetEye PC dongle
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sat Feb 21 18:54:38 1998
- * Modified at: Sun Oct 27 22:01:04 2002
- * Modified by: Martin Diehl <mad@mdiehl.de>
- *
- * Copyright (c) 1999 Dag Brattli, <dagb@cs.uit.no>,
- * Copyright (c) 1998 Thomas Davis, <ratbert@radiks.net>,
- * Copyright (c) 2002 Martin Diehl, <mad@mdiehl.de>,
- * All Rights Reserved.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-
-#include <net/irda/irda.h>
-
-#include "sir-dev.h"
-
-static int esi_open(struct sir_dev *);
-static int esi_close(struct sir_dev *);
-static int esi_change_speed(struct sir_dev *, unsigned);
-static int esi_reset(struct sir_dev *);
-
-static struct dongle_driver esi = {
- .owner = THIS_MODULE,
- .driver_name = "JetEye PC ESI-9680 PC",
- .type = IRDA_ESI_DONGLE,
- .open = esi_open,
- .close = esi_close,
- .reset = esi_reset,
- .set_speed = esi_change_speed,
-};
-
-static int __init esi_sir_init(void)
-{
- return irda_register_dongle(&esi);
-}
-
-static void __exit esi_sir_cleanup(void)
-{
- irda_unregister_dongle(&esi);
-}
-
-static int esi_open(struct sir_dev *dev)
-{
- struct qos_info *qos = &dev->qos;
-
- /* Power up and set dongle to 9600 baud */
- sirdev_set_dtr_rts(dev, FALSE, TRUE);
-
- qos->baud_rate.bits &= IR_9600|IR_19200|IR_115200;
- qos->min_turn_time.bits = 0x01; /* Needs at least 10 ms */
- irda_qos_bits_to_value(qos);
-
- /* irda thread waits 50 msec for power settling */
-
- return 0;
-}
-
-static int esi_close(struct sir_dev *dev)
-{
- /* Power off dongle */
- sirdev_set_dtr_rts(dev, FALSE, FALSE);
-
- return 0;
-}
-
-/*
- * Function esi_change_speed (task)
- *
- * Set the speed for the Extended Systems JetEye PC ESI-9680 type dongle
- * Apparently (see old esi-driver) no delays are needed here...
- *
- */
-static int esi_change_speed(struct sir_dev *dev, unsigned speed)
-{
- int ret = 0;
- int dtr, rts;
-
- switch (speed) {
- case 19200:
- dtr = TRUE;
- rts = FALSE;
- break;
- case 115200:
- dtr = rts = TRUE;
- break;
- default:
- ret = -EINVAL;
- speed = 9600;
- /* fall through */
- case 9600:
- dtr = FALSE;
- rts = TRUE;
- break;
- }
-
- /* Change speed of dongle */
- sirdev_set_dtr_rts(dev, dtr, rts);
- dev->speed = speed;
-
- return ret;
-}
-
-/*
- * Function esi_reset (task)
- *
- * Reset dongle;
- *
- */
-static int esi_reset(struct sir_dev *dev)
-{
- sirdev_set_dtr_rts(dev, FALSE, FALSE);
-
- /* Hm, the old esi-driver left the dongle unpowered relying on
- * the following speed change to repower. This might work for
- * the esi because we only need the modem lines. However, now the
- * general rule is reset must bring the dongle to some working
- * well-known state because speed change might write to registers.
- * The old esi-driver didn't any delay here - let's hope it' fine.
- */
-
- sirdev_set_dtr_rts(dev, FALSE, TRUE);
- dev->speed = 9600;
-
- return 0;
-}
-
-MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
-MODULE_DESCRIPTION("Extended Systems JetEye PC dongle driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("irda-dongle-1"); /* IRDA_ESI_DONGLE */
-
-module_init(esi_sir_init);
-module_exit(esi_sir_cleanup);
-
diff --git a/drivers/staging/irda/drivers/girbil-sir.c b/drivers/staging/irda/drivers/girbil-sir.c
deleted file mode 100644
index 7e0a5b8c6d53..000000000000
--- a/drivers/staging/irda/drivers/girbil-sir.c
+++ /dev/null
@@ -1,252 +0,0 @@
-/*********************************************************************
- *
- * Filename: girbil.c
- * Version: 1.2
- * Description: Implementation for the Greenwich GIrBIL dongle
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sat Feb 6 21:02:33 1999
- * Modified at: Fri Dec 17 09:13:20 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1999 Dag Brattli, All Rights Reserved.
- *
- * 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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-
-#include <net/irda/irda.h>
-
-#include "sir-dev.h"
-
-static int girbil_reset(struct sir_dev *dev);
-static int girbil_open(struct sir_dev *dev);
-static int girbil_close(struct sir_dev *dev);
-static int girbil_change_speed(struct sir_dev *dev, unsigned speed);
-
-/* Control register 1 */
-#define GIRBIL_TXEN 0x01 /* Enable transmitter */
-#define GIRBIL_RXEN 0x02 /* Enable receiver */
-#define GIRBIL_ECAN 0x04 /* Cancel self emitted data */
-#define GIRBIL_ECHO 0x08 /* Echo control characters */
-
-/* LED Current Register (0x2) */
-#define GIRBIL_HIGH 0x20
-#define GIRBIL_MEDIUM 0x21
-#define GIRBIL_LOW 0x22
-
-/* Baud register (0x3) */
-#define GIRBIL_2400 0x30
-#define GIRBIL_4800 0x31
-#define GIRBIL_9600 0x32
-#define GIRBIL_19200 0x33
-#define GIRBIL_38400 0x34
-#define GIRBIL_57600 0x35
-#define GIRBIL_115200 0x36
-
-/* Mode register (0x4) */
-#define GIRBIL_IRDA 0x40
-#define GIRBIL_ASK 0x41
-
-/* Control register 2 (0x5) */
-#define GIRBIL_LOAD 0x51 /* Load the new baud rate value */
-
-static struct dongle_driver girbil = {
- .owner = THIS_MODULE,
- .driver_name = "Greenwich GIrBIL",
- .type = IRDA_GIRBIL_DONGLE,
- .open = girbil_open,
- .close = girbil_close,
- .reset = girbil_reset,
- .set_speed = girbil_change_speed,
-};
-
-static int __init girbil_sir_init(void)
-{
- return irda_register_dongle(&girbil);
-}
-
-static void __exit girbil_sir_cleanup(void)
-{
- irda_unregister_dongle(&girbil);
-}
-
-static int girbil_open(struct sir_dev *dev)
-{
- struct qos_info *qos = &dev->qos;
-
- /* Power on dongle */
- sirdev_set_dtr_rts(dev, TRUE, TRUE);
-
- qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
- qos->min_turn_time.bits = 0x03;
- irda_qos_bits_to_value(qos);
-
- /* irda thread waits 50 msec for power settling */
-
- return 0;
-}
-
-static int girbil_close(struct sir_dev *dev)
-{
- /* Power off dongle */
- sirdev_set_dtr_rts(dev, FALSE, FALSE);
-
- return 0;
-}
-
-/*
- * Function girbil_change_speed (dev, speed)
- *
- * Set the speed for the Girbil type dongle.
- *
- */
-
-#define GIRBIL_STATE_WAIT_SPEED (SIRDEV_STATE_DONGLE_SPEED + 1)
-
-static int girbil_change_speed(struct sir_dev *dev, unsigned speed)
-{
- unsigned state = dev->fsm.substate;
- unsigned delay = 0;
- u8 control[2];
- static int ret = 0;
-
- /* dongle alread reset - port and dongle at default speed */
-
- switch(state) {
-
- case SIRDEV_STATE_DONGLE_SPEED:
-
- /* Set DTR and Clear RTS to enter command mode */
- sirdev_set_dtr_rts(dev, FALSE, TRUE);
-
- udelay(25); /* better wait a little while */
-
- ret = 0;
- switch (speed) {
- default:
- ret = -EINVAL;
- /* fall through */
- case 9600:
- control[0] = GIRBIL_9600;
- break;
- case 19200:
- control[0] = GIRBIL_19200;
- break;
- case 34800:
- control[0] = GIRBIL_38400;
- break;
- case 57600:
- control[0] = GIRBIL_57600;
- break;
- case 115200:
- control[0] = GIRBIL_115200;
- break;
- }
- control[1] = GIRBIL_LOAD;
-
- /* Write control bytes */
- sirdev_raw_write(dev, control, 2);
-
- dev->speed = speed;
-
- state = GIRBIL_STATE_WAIT_SPEED;
- delay = 100;
- break;
-
- case GIRBIL_STATE_WAIT_SPEED:
- /* Go back to normal mode */
- sirdev_set_dtr_rts(dev, TRUE, TRUE);
-
- udelay(25); /* better wait a little while */
- break;
-
- default:
- net_err_ratelimited("%s - undefined state %d\n",
- __func__, state);
- ret = -EINVAL;
- break;
- }
- dev->fsm.substate = state;
- return (delay > 0) ? delay : ret;
-}
-
-/*
- * Function girbil_reset (driver)
- *
- * This function resets the girbil dongle.
- *
- * Algorithm:
- * 0. set RTS, and wait at least 5 ms
- * 1. clear RTS
- */
-
-
-#define GIRBIL_STATE_WAIT1_RESET (SIRDEV_STATE_DONGLE_RESET + 1)
-#define GIRBIL_STATE_WAIT2_RESET (SIRDEV_STATE_DONGLE_RESET + 2)
-#define GIRBIL_STATE_WAIT3_RESET (SIRDEV_STATE_DONGLE_RESET + 3)
-
-static int girbil_reset(struct sir_dev *dev)
-{
- unsigned state = dev->fsm.substate;
- unsigned delay = 0;
- u8 control = GIRBIL_TXEN | GIRBIL_RXEN;
- int ret = 0;
-
- switch (state) {
- case SIRDEV_STATE_DONGLE_RESET:
- /* Reset dongle */
- sirdev_set_dtr_rts(dev, TRUE, FALSE);
- /* Sleep at least 5 ms */
- delay = 20;
- state = GIRBIL_STATE_WAIT1_RESET;
- break;
-
- case GIRBIL_STATE_WAIT1_RESET:
- /* Set DTR and clear RTS to enter command mode */
- sirdev_set_dtr_rts(dev, FALSE, TRUE);
- delay = 20;
- state = GIRBIL_STATE_WAIT2_RESET;
- break;
-
- case GIRBIL_STATE_WAIT2_RESET:
- /* Write control byte */
- sirdev_raw_write(dev, &control, 1);
- delay = 20;
- state = GIRBIL_STATE_WAIT3_RESET;
- break;
-
- case GIRBIL_STATE_WAIT3_RESET:
- /* Go back to normal mode */
- sirdev_set_dtr_rts(dev, TRUE, TRUE);
- dev->speed = 9600;
- break;
-
- default:
- net_err_ratelimited("%s(), undefined state %d\n",
- __func__, state);
- ret = -1;
- break;
- }
- dev->fsm.substate = state;
- return (delay > 0) ? delay : ret;
-}
-
-MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
-MODULE_DESCRIPTION("Greenwich GIrBIL dongle driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("irda-dongle-4"); /* IRDA_GIRBIL_DONGLE */
-
-module_init(girbil_sir_init);
-module_exit(girbil_sir_cleanup);
diff --git a/drivers/staging/irda/drivers/irda-usb.c b/drivers/staging/irda/drivers/irda-usb.c
deleted file mode 100644
index bda6bdc6c70b..000000000000
--- a/drivers/staging/irda/drivers/irda-usb.c
+++ /dev/null
@@ -1,1906 +0,0 @@
-/*****************************************************************************
- *
- * Filename: irda-usb.c
- * Version: 0.10
- * Description: IrDA-USB Driver
- * Status: Experimental
- * Author: Dag Brattli <dag@brattli.net>
- *
- * Copyright (C) 2000, Roman Weissgaerber <weissg@vienna.at>
- * Copyright (C) 2001, Dag Brattli <dag@brattli.net>
- * Copyright (C) 2001, Jean Tourrilhes <jt@hpl.hp.com>
- * Copyright (C) 2004, SigmaTel, Inc. <irquality@sigmatel.com>
- * Copyright (C) 2005, Milan Beno <beno@pobox.sk>
- * Copyright (C) 2006, Nick Fedchik <nick@fedchik.org.ua>
- *
- * 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.
- *
- *****************************************************************************/
-
-/*
- * IMPORTANT NOTE
- * --------------
- *
- * As of kernel 2.5.20, this is the state of compliance and testing of
- * this driver (irda-usb) with regards to the USB low level drivers...
- *
- * This driver has been tested SUCCESSFULLY with the following drivers :
- * o usb-uhci-hcd (For Intel/Via USB controllers)
- * o uhci-hcd (Alternate/JE driver for Intel/Via USB controllers)
- * o ohci-hcd (For other USB controllers)
- *
- * This driver has NOT been tested with the following drivers :
- * o ehci-hcd (USB 2.0 controllers)
- *
- * Note that all HCD drivers do URB_ZERO_PACKET and timeout properly,
- * so we don't have to worry about that anymore.
- * One common problem is the failure to set the address on the dongle,
- * but this happens before the driver gets loaded...
- *
- * Jean II
- */
-
-/*------------------------------------------------------------------*/
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <linux/slab.h>
-#include <linux/rtnetlink.h>
-#include <linux/usb.h>
-#include <linux/firmware.h>
-
-#include "irda-usb.h"
-
-/*------------------------------------------------------------------*/
-
-static int qos_mtt_bits = 0;
-
-/* These are the currently known IrDA USB dongles. Add new dongles here */
-static const struct usb_device_id dongles[] = {
- /* ACTiSYS Corp., ACT-IR2000U FIR-USB Adapter */
- { USB_DEVICE(0x9c4, 0x011), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW },
- /* Look like ACTiSYS, Report : IBM Corp., IBM UltraPort IrDA */
- { USB_DEVICE(0x4428, 0x012), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW },
- /* KC Technology Inc., KC-180 USB IrDA Device */
- { USB_DEVICE(0x50f, 0x180), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW },
- /* Extended Systems, Inc., XTNDAccess IrDA USB (ESI-9685) */
- { USB_DEVICE(0x8e9, 0x100), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW },
- /* SigmaTel STIR4210/4220/4116 USB IrDA (VFIR) Bridge */
- { USB_DEVICE(0x66f, 0x4210), .driver_info = IUC_STIR421X | IUC_SPEED_BUG },
- { USB_DEVICE(0x66f, 0x4220), .driver_info = IUC_STIR421X | IUC_SPEED_BUG },
- { USB_DEVICE(0x66f, 0x4116), .driver_info = IUC_STIR421X | IUC_SPEED_BUG },
- { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS |
- USB_DEVICE_ID_MATCH_INT_SUBCLASS,
- .bInterfaceClass = USB_CLASS_APP_SPEC,
- .bInterfaceSubClass = USB_CLASS_IRDA,
- .driver_info = IUC_DEFAULT, },
- { }, /* The end */
-};
-
-/*
- * Important note :
- * Devices based on the SigmaTel chipset (0x66f, 0x4200) are not designed
- * using the "USB-IrDA specification" (yes, there exist such a thing), and
- * therefore not supported by this driver (don't add them above).
- * There is a Linux driver, stir4200, that support those USB devices.
- * Jean II
- */
-
-MODULE_DEVICE_TABLE(usb, dongles);
-
-/*------------------------------------------------------------------*/
-
-static void irda_usb_init_qos(struct irda_usb_cb *self) ;
-static struct irda_class_desc *irda_usb_find_class_desc(struct usb_interface *intf);
-static void irda_usb_disconnect(struct usb_interface *intf);
-static void irda_usb_change_speed_xbofs(struct irda_usb_cb *self);
-static netdev_tx_t irda_usb_hard_xmit(struct sk_buff *skb,
- struct net_device *dev);
-static int irda_usb_open(struct irda_usb_cb *self);
-static void irda_usb_close(struct irda_usb_cb *self);
-static void speed_bulk_callback(struct urb *urb);
-static void write_bulk_callback(struct urb *urb);
-static void irda_usb_receive(struct urb *urb);
-static void irda_usb_rx_defer_expired(struct timer_list *t);
-static int irda_usb_net_open(struct net_device *dev);
-static int irda_usb_net_close(struct net_device *dev);
-static int irda_usb_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-static void irda_usb_net_timeout(struct net_device *dev);
-
-/************************ TRANSMIT ROUTINES ************************/
-/*
- * Receive packets from the IrDA stack and send them on the USB pipe.
- * Handle speed change, timeout and lot's of ugliness...
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Function irda_usb_build_header(self, skb, header)
- *
- * Builds USB-IrDA outbound header
- *
- * When we send an IrDA frame over an USB pipe, we add to it a 1 byte
- * header. This function create this header with the proper values.
- *
- * Important note : the USB-IrDA spec 1.0 say very clearly in chapter 5.4.2.2
- * that the setting of the link speed and xbof number in this outbound header
- * should be applied *AFTER* the frame has been sent.
- * Unfortunately, some devices are not compliant with that... It seems that
- * reading the spec is far too difficult...
- * Jean II
- */
-static void irda_usb_build_header(struct irda_usb_cb *self,
- __u8 *header,
- int force)
-{
- /* Here we check if we have an STIR421x chip,
- * and if either speed or xbofs (or both) needs
- * to be changed.
- */
- if (self->capability & IUC_STIR421X &&
- ((self->new_speed != -1) || (self->new_xbofs != -1))) {
-
- /* With STIR421x, speed and xBOFs must be set at the same
- * time, even if only one of them changes.
- */
- if (self->new_speed == -1)
- self->new_speed = self->speed ;
-
- if (self->new_xbofs == -1)
- self->new_xbofs = self->xbofs ;
- }
-
- /* Set the link speed */
- if (self->new_speed != -1) {
- /* Hum... Ugly hack :-(
- * Some device are not compliant with the spec and change
- * parameters *before* sending the frame. - Jean II
- */
- if ((self->capability & IUC_SPEED_BUG) &&
- (!force) && (self->speed != -1)) {
- /* No speed and xbofs change here
- * (we'll do it later in the write callback) */
- pr_debug("%s(), not changing speed yet\n", __func__);
- *header = 0;
- return;
- }
-
- pr_debug("%s(), changing speed to %d\n",
- __func__, self->new_speed);
- self->speed = self->new_speed;
- /* We will do ` self->new_speed = -1; ' in the completion
- * handler just in case the current URB fail - Jean II */
-
- switch (self->speed) {
- case 2400:
- *header = SPEED_2400;
- break;
- default:
- case 9600:
- *header = SPEED_9600;
- break;
- case 19200:
- *header = SPEED_19200;
- break;
- case 38400:
- *header = SPEED_38400;
- break;
- case 57600:
- *header = SPEED_57600;
- break;
- case 115200:
- *header = SPEED_115200;
- break;
- case 576000:
- *header = SPEED_576000;
- break;
- case 1152000:
- *header = SPEED_1152000;
- break;
- case 4000000:
- *header = SPEED_4000000;
- self->new_xbofs = 0;
- break;
- case 16000000:
- *header = SPEED_16000000;
- self->new_xbofs = 0;
- break;
- }
- } else
- /* No change */
- *header = 0;
-
- /* Set the negotiated additional XBOFS */
- if (self->new_xbofs != -1) {
- pr_debug("%s(), changing xbofs to %d\n",
- __func__, self->new_xbofs);
- self->xbofs = self->new_xbofs;
- /* We will do ` self->new_xbofs = -1; ' in the completion
- * handler just in case the current URB fail - Jean II */
-
- switch (self->xbofs) {
- case 48:
- *header |= 0x10;
- break;
- case 28:
- case 24: /* USB spec 1.0 says 24 */
- *header |= 0x20;
- break;
- default:
- case 12:
- *header |= 0x30;
- break;
- case 5: /* Bug in IrLAP spec? (should be 6) */
- case 6:
- *header |= 0x40;
- break;
- case 3:
- *header |= 0x50;
- break;
- case 2:
- *header |= 0x60;
- break;
- case 1:
- *header |= 0x70;
- break;
- case 0:
- *header |= 0x80;
- break;
- }
- }
-}
-
-/*
-* calculate turnaround time for SigmaTel header
-*/
-static __u8 get_turnaround_time(struct sk_buff *skb)
-{
- int turnaround_time = irda_get_mtt(skb);
-
- if ( turnaround_time == 0 )
- return 0;
- else if ( turnaround_time <= 10 )
- return 1;
- else if ( turnaround_time <= 50 )
- return 2;
- else if ( turnaround_time <= 100 )
- return 3;
- else if ( turnaround_time <= 500 )
- return 4;
- else if ( turnaround_time <= 1000 )
- return 5;
- else if ( turnaround_time <= 5000 )
- return 6;
- else
- return 7;
-}
-
-
-/*------------------------------------------------------------------*/
-/*
- * Send a command to change the speed of the dongle
- * Need to be called with spinlock on.
- */
-static void irda_usb_change_speed_xbofs(struct irda_usb_cb *self)
-{
- __u8 *frame;
- struct urb *urb;
- int ret;
-
- pr_debug("%s(), speed=%d, xbofs=%d\n", __func__,
- self->new_speed, self->new_xbofs);
-
- /* Grab the speed URB */
- urb = self->speed_urb;
- if (urb->status != 0) {
- net_warn_ratelimited("%s(), URB still in use!\n", __func__);
- return;
- }
-
- /* Allocate the fake frame */
- frame = self->speed_buff;
-
- /* Set the new speed and xbofs in this fake frame */
- irda_usb_build_header(self, frame, 1);
-
- if (self->capability & IUC_STIR421X) {
- if (frame[0] == 0) return ; // do nothing if no change
- frame[1] = 0; // other parameters don't change here
- frame[2] = 0;
- }
-
- /* Submit the 0 length IrDA frame to trigger new speed settings */
- usb_fill_bulk_urb(urb, self->usbdev,
- usb_sndbulkpipe(self->usbdev, self->bulk_out_ep),
- frame, IRDA_USB_SPEED_MTU,
- speed_bulk_callback, self);
- urb->transfer_buffer_length = self->header_length;
- urb->transfer_flags = 0;
-
- /* Irq disabled -> GFP_ATOMIC */
- ret = usb_submit_urb(urb, GFP_ATOMIC);
- if (ret)
- net_warn_ratelimited("%s(), failed Speed URB\n", __func__);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Speed URB callback
- * Now, we can only get called for the speed URB.
- */
-static void speed_bulk_callback(struct urb *urb)
-{
- struct irda_usb_cb *self = urb->context;
-
- /* We should always have a context */
- IRDA_ASSERT(self != NULL, return;);
- /* We should always be called for the speed URB */
- IRDA_ASSERT(urb == self->speed_urb, return;);
-
- /* Check for timeout and other USB nasties */
- if (urb->status != 0) {
- /* I get a lot of -ECONNABORTED = -103 here - Jean II */
- pr_debug("%s(), URB complete status %d, transfer_flags 0x%04X\n",
- __func__, urb->status, urb->transfer_flags);
-
- /* Don't do anything here, that might confuse the USB layer.
- * Instead, we will wait for irda_usb_net_timeout(), the
- * network layer watchdog, to fix the situation.
- * Jean II */
- /* A reset of the dongle might be welcomed here - Jean II */
- return;
- }
-
- /* urb is now available */
- //urb->status = 0; -> tested above
-
- /* New speed and xbof is now committed in hardware */
- self->new_speed = -1;
- self->new_xbofs = -1;
-
- /* Allow the stack to send more packets */
- netif_wake_queue(self->netdev);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Send an IrDA frame to the USB dongle (for transmission)
- */
-static netdev_tx_t irda_usb_hard_xmit(struct sk_buff *skb,
- struct net_device *netdev)
-{
- struct irda_usb_cb *self = netdev_priv(netdev);
- struct urb *urb = self->tx_urb;
- unsigned long flags;
- s32 speed;
- s16 xbofs;
- int res, mtt;
-
- pr_debug("%s() on %s\n", __func__, netdev->name);
-
- netif_stop_queue(netdev);
-
- /* Protect us from USB callbacks, net watchdog and else. */
- spin_lock_irqsave(&self->lock, flags);
-
- /* Check if the device is still there.
- * We need to check self->present under the spinlock because
- * of irda_usb_disconnect() is synchronous - Jean II */
- if (!self->present) {
- pr_debug("%s(), Device is gone...\n", __func__);
- goto drop;
- }
-
- /* Check if we need to change the number of xbofs */
- xbofs = irda_get_next_xbofs(skb);
- if ((xbofs != self->xbofs) && (xbofs != -1)) {
- self->new_xbofs = xbofs;
- }
-
- /* Check if we need to change the speed */
- speed = irda_get_next_speed(skb);
- if ((speed != self->speed) && (speed != -1)) {
- /* Set the desired speed */
- self->new_speed = speed;
-
- /* Check for empty frame */
- if (!skb->len) {
- /* IrLAP send us an empty frame to make us change the
- * speed. Changing speed with the USB adapter is in
- * fact sending an empty frame to the adapter, so we
- * could just let the present function do its job.
- * However, we would wait for min turn time,
- * do an extra memcpy and increment packet counters...
- * Jean II */
- irda_usb_change_speed_xbofs(self);
- netif_trans_update(netdev);
- /* Will netif_wake_queue() in callback */
- goto drop;
- }
- }
-
- if (urb->status != 0) {
- net_warn_ratelimited("%s(), URB still in use!\n", __func__);
- goto drop;
- }
-
- skb_copy_from_linear_data(skb, self->tx_buff + self->header_length, skb->len);
-
- /* Change setting for next frame */
- if (self->capability & IUC_STIR421X) {
- __u8 turnaround_time;
- __u8* frame = self->tx_buff;
- turnaround_time = get_turnaround_time( skb );
- irda_usb_build_header(self, frame, 0);
- frame[2] = turnaround_time;
- if ((skb->len != 0) &&
- ((skb->len % 128) == 0) &&
- ((skb->len % 512) != 0)) {
- /* add extra byte for special SigmaTel feature */
- frame[1] = 1;
- skb_put(skb, 1);
- } else {
- frame[1] = 0;
- }
- } else {
- irda_usb_build_header(self, self->tx_buff, 0);
- }
-
- /* FIXME: Make macro out of this one */
- ((struct irda_skb_cb *)skb->cb)->context = self;
-
- usb_fill_bulk_urb(urb, self->usbdev,
- usb_sndbulkpipe(self->usbdev, self->bulk_out_ep),
- self->tx_buff, skb->len + self->header_length,
- write_bulk_callback, skb);
-
- /* This flag (URB_ZERO_PACKET) indicates that what we send is not
- * a continuous stream of data but separate packets.
- * In this case, the USB layer will insert an empty USB frame (TD)
- * after each of our packets that is exact multiple of the frame size.
- * This is how the dongle will detect the end of packet - Jean II */
- urb->transfer_flags = URB_ZERO_PACKET;
-
- /* Generate min turn time. FIXME: can we do better than this? */
- /* Trying to a turnaround time at this level is trying to measure
- * processor clock cycle with a wrist-watch, approximate at best...
- *
- * What we know is the last time we received a frame over USB.
- * Due to latency over USB that depend on the USB load, we don't
- * know when this frame was received over IrDA (a few ms before ?)
- * Then, same story for our outgoing frame...
- *
- * In theory, the USB dongle is supposed to handle the turnaround
- * by itself (spec 1.0, chater 4, page 6). Who knows ??? That's
- * why this code is enabled only for dongles that doesn't meet
- * the spec.
- * Jean II */
- if (self->capability & IUC_NO_TURN) {
- mtt = irda_get_mtt(skb);
- if (mtt) {
- int diff;
- diff = ktime_us_delta(ktime_get(), self->stamp);
-#ifdef IU_USB_MIN_RTT
- /* Factor in USB delays -> Get rid of udelay() that
- * would be lost in the noise - Jean II */
- diff += IU_USB_MIN_RTT;
-#endif /* IU_USB_MIN_RTT */
-
- /* Check if the mtt is larger than the time we have
- * already used by all the protocol processing
- */
- if (mtt > diff) {
- mtt -= diff;
- if (mtt > 1000)
- mdelay(mtt/1000);
- else
- udelay(mtt);
- }
- }
- }
-
- /* Ask USB to send the packet - Irq disabled -> GFP_ATOMIC */
- if ((res = usb_submit_urb(urb, GFP_ATOMIC))) {
- net_warn_ratelimited("%s(), failed Tx URB\n", __func__);
- netdev->stats.tx_errors++;
- /* Let USB recover : We will catch that in the watchdog */
- /*netif_start_queue(netdev);*/
- } else {
- /* Increment packet stats */
- netdev->stats.tx_packets++;
- netdev->stats.tx_bytes += skb->len;
-
- netif_trans_update(netdev);
- }
- spin_unlock_irqrestore(&self->lock, flags);
-
- return NETDEV_TX_OK;
-
-drop:
- /* Drop silently the skb and exit */
- dev_kfree_skb(skb);
- spin_unlock_irqrestore(&self->lock, flags);
- return NETDEV_TX_OK;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Note : this function will be called only for tx_urb...
- */
-static void write_bulk_callback(struct urb *urb)
-{
- unsigned long flags;
- struct sk_buff *skb = urb->context;
- struct irda_usb_cb *self = ((struct irda_skb_cb *) skb->cb)->context;
-
- /* We should always have a context */
- IRDA_ASSERT(self != NULL, return;);
- /* We should always be called for the speed URB */
- IRDA_ASSERT(urb == self->tx_urb, return;);
-
- /* Free up the skb */
- dev_kfree_skb_any(skb);
- urb->context = NULL;
-
- /* Check for timeout and other USB nasties */
- if (urb->status != 0) {
- /* I get a lot of -ECONNABORTED = -103 here - Jean II */
- pr_debug("%s(), URB complete status %d, transfer_flags 0x%04X\n",
- __func__, urb->status, urb->transfer_flags);
-
- /* Don't do anything here, that might confuse the USB layer,
- * and we could go in recursion and blow the kernel stack...
- * Instead, we will wait for irda_usb_net_timeout(), the
- * network layer watchdog, to fix the situation.
- * Jean II */
- /* A reset of the dongle might be welcomed here - Jean II */
- return;
- }
-
- /* urb is now available */
- //urb->status = 0; -> tested above
-
- /* Make sure we read self->present properly */
- spin_lock_irqsave(&self->lock, flags);
-
- /* If the network is closed, stop everything */
- if ((!self->netopen) || (!self->present)) {
- pr_debug("%s(), Network is gone...\n", __func__);
- spin_unlock_irqrestore(&self->lock, flags);
- return;
- }
-
- /* If changes to speed or xbofs is pending... */
- if ((self->new_speed != -1) || (self->new_xbofs != -1)) {
- if ((self->new_speed != self->speed) ||
- (self->new_xbofs != self->xbofs)) {
- /* We haven't changed speed yet (because of
- * IUC_SPEED_BUG), so do it now - Jean II */
- pr_debug("%s(), Changing speed now...\n", __func__);
- irda_usb_change_speed_xbofs(self);
- } else {
- /* New speed and xbof is now committed in hardware */
- self->new_speed = -1;
- self->new_xbofs = -1;
- /* Done, waiting for next packet */
- netif_wake_queue(self->netdev);
- }
- } else {
- /* Otherwise, allow the stack to send more packets */
- netif_wake_queue(self->netdev);
- }
- spin_unlock_irqrestore(&self->lock, flags);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Watchdog timer from the network layer.
- * After a predetermined timeout, if we don't give confirmation that
- * the packet has been sent (i.e. no call to netif_wake_queue()),
- * the network layer will call this function.
- * Note that URB that we submit have also a timeout. When the URB timeout
- * expire, the normal URB callback is called (write_bulk_callback()).
- */
-static void irda_usb_net_timeout(struct net_device *netdev)
-{
- unsigned long flags;
- struct irda_usb_cb *self = netdev_priv(netdev);
- struct urb *urb;
- int done = 0; /* If we have made any progress */
-
- pr_debug("%s(), Network layer thinks we timed out!\n", __func__);
- IRDA_ASSERT(self != NULL, return;);
-
- /* Protect us from USB callbacks, net Tx and else. */
- spin_lock_irqsave(&self->lock, flags);
-
- /* self->present *MUST* be read under spinlock */
- if (!self->present) {
- net_warn_ratelimited("%s(), device not present!\n", __func__);
- netif_stop_queue(netdev);
- spin_unlock_irqrestore(&self->lock, flags);
- return;
- }
-
- /* Check speed URB */
- urb = self->speed_urb;
- if (urb->status != 0) {
- pr_debug("%s: Speed change timed out, urb->status=%d, urb->transfer_flags=0x%04X\n",
- netdev->name, urb->status, urb->transfer_flags);
-
- switch (urb->status) {
- case -EINPROGRESS:
- usb_unlink_urb(urb);
- /* Note : above will *NOT* call netif_wake_queue()
- * in completion handler, we will come back here.
- * Jean II */
- done = 1;
- break;
- case -ECONNRESET:
- case -ENOENT: /* urb unlinked by us */
- default: /* ??? - Play safe */
- urb->status = 0;
- netif_wake_queue(self->netdev);
- done = 1;
- break;
- }
- }
-
- /* Check Tx URB */
- urb = self->tx_urb;
- if (urb->status != 0) {
- struct sk_buff *skb = urb->context;
-
- pr_debug("%s: Tx timed out, urb->status=%d, urb->transfer_flags=0x%04X\n",
- netdev->name, urb->status, urb->transfer_flags);
-
- /* Increase error count */
- netdev->stats.tx_errors++;
-
-#ifdef IU_BUG_KICK_TIMEOUT
- /* Can't be a bad idea to reset the speed ;-) - Jean II */
- if(self->new_speed == -1)
- self->new_speed = self->speed;
- if(self->new_xbofs == -1)
- self->new_xbofs = self->xbofs;
- irda_usb_change_speed_xbofs(self);
-#endif /* IU_BUG_KICK_TIMEOUT */
-
- switch (urb->status) {
- case -EINPROGRESS:
- usb_unlink_urb(urb);
- /* Note : above will *NOT* call netif_wake_queue()
- * in completion handler, because urb->status will
- * be -ENOENT. We will fix that at the next watchdog,
- * leaving more time to USB to recover...
- * Jean II */
- done = 1;
- break;
- case -ECONNRESET:
- case -ENOENT: /* urb unlinked by us */
- default: /* ??? - Play safe */
- if(skb != NULL) {
- dev_kfree_skb_any(skb);
- urb->context = NULL;
- }
- urb->status = 0;
- netif_wake_queue(self->netdev);
- done = 1;
- break;
- }
- }
- spin_unlock_irqrestore(&self->lock, flags);
-
- /* Maybe we need a reset */
- /* Note : Some drivers seem to use a usb_set_interface() when they
- * need to reset the hardware. Hum...
- */
-
- /* if(done == 0) */
-}
-
-/************************* RECEIVE ROUTINES *************************/
-/*
- * Receive packets from the USB layer stack and pass them to the IrDA stack.
- * Try to work around USB failures...
- */
-
-/*
- * Note :
- * Some of you may have noticed that most dongle have an interrupt in pipe
- * that we don't use. Here is the little secret...
- * When we hang a Rx URB on the bulk in pipe, it generates some USB traffic
- * in every USB frame. This is unnecessary overhead.
- * The interrupt in pipe will generate an event every time a packet is
- * received. Reading an interrupt pipe adds minimal overhead, but has some
- * latency (~1ms).
- * If we are connected (speed != 9600), we want to minimise latency, so
- * we just always hang the Rx URB and ignore the interrupt.
- * If we are not connected (speed == 9600), there is usually no Rx traffic,
- * and we want to minimise the USB overhead. In this case we should wait
- * on the interrupt pipe and hang the Rx URB only when an interrupt is
- * received.
- * Jean II
- *
- * Note : don't read the above as what we are currently doing, but as
- * something we could do with KC dongle. Also don't forget that the
- * interrupt pipe is not part of the original standard, so this would
- * need to be optional...
- * Jean II
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Submit a Rx URB to the USB layer to handle reception of a frame
- * Mostly called by the completion callback of the previous URB.
- *
- * Jean II
- */
-static void irda_usb_submit(struct irda_usb_cb *self, struct sk_buff *skb, struct urb *urb)
-{
- struct irda_skb_cb *cb;
- int ret;
-
- /* This should never happen */
- IRDA_ASSERT(skb != NULL, return;);
- IRDA_ASSERT(urb != NULL, return;);
-
- /* Save ourselves in the skb */
- cb = (struct irda_skb_cb *) skb->cb;
- cb->context = self;
-
- /* Reinitialize URB */
- usb_fill_bulk_urb(urb, self->usbdev,
- usb_rcvbulkpipe(self->usbdev, self->bulk_in_ep),
- skb->data, IRDA_SKB_MAX_MTU,
- irda_usb_receive, skb);
- urb->status = 0;
-
- /* Can be called from irda_usb_receive (irq handler) -> GFP_ATOMIC */
- ret = usb_submit_urb(urb, GFP_ATOMIC);
- if (ret) {
- /* If this ever happen, we are in deep s***.
- * Basically, the Rx path will stop... */
- net_warn_ratelimited("%s(), Failed to submit Rx URB %d\n",
- __func__, ret);
- }
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irda_usb_receive(urb)
- *
- * Called by the USB subsystem when a frame has been received
- *
- */
-static void irda_usb_receive(struct urb *urb)
-{
- struct sk_buff *skb = (struct sk_buff *) urb->context;
- struct irda_usb_cb *self;
- struct irda_skb_cb *cb;
- struct sk_buff *newskb;
- struct sk_buff *dataskb;
- struct urb *next_urb;
- unsigned int len, docopy;
-
- pr_debug("%s(), len=%d\n", __func__, urb->actual_length);
-
- /* Find ourselves */
- cb = (struct irda_skb_cb *) skb->cb;
- IRDA_ASSERT(cb != NULL, return;);
- self = (struct irda_usb_cb *) cb->context;
- IRDA_ASSERT(self != NULL, return;);
-
- /* If the network is closed or the device gone, stop everything */
- if ((!self->netopen) || (!self->present)) {
- pr_debug("%s(), Network is gone!\n", __func__);
- /* Don't re-submit the URB : will stall the Rx path */
- return;
- }
-
- /* Check the status */
- if (urb->status != 0) {
- switch (urb->status) {
- case -EILSEQ:
- self->netdev->stats.rx_crc_errors++;
- /* Also precursor to a hot-unplug on UHCI. */
- /* Fallthrough... */
- 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:
- /* That's usually a hot-unplug. Submit will fail... */
- case -ETIME:
- /* Usually precursor to a hot-unplug on OHCI. */
- default:
- self->netdev->stats.rx_errors++;
- pr_debug("%s(), RX status %d, transfer_flags 0x%04X\n",
- __func__, urb->status, urb->transfer_flags);
- break;
- }
- /* If we received an error, we don't want to resubmit the
- * Rx URB straight away but to give the USB layer a little
- * bit of breathing room.
- * We are in the USB thread context, therefore there is a
- * danger of recursion (new URB we submit fails, we come
- * back here).
- * With recent USB stack (2.6.15+), I'm seeing that on
- * hot unplug of the dongle...
- * Lowest effective timer is 10ms...
- * Jean II */
- self->rx_defer_timer_urb = urb;
- mod_timer(&self->rx_defer_timer,
- jiffies + msecs_to_jiffies(10));
-
- return;
- }
-
- /* Check for empty frames */
- if (urb->actual_length <= self->header_length) {
- net_warn_ratelimited("%s(), empty frame!\n", __func__);
- goto done;
- }
-
- /*
- * Remember the time we received this frame, so we can
- * reduce the min turn time a bit since we will know
- * how much time we have used for protocol processing
- */
- self->stamp = ktime_get();
-
- /* Check if we need to copy the data to a new skb or not.
- * For most frames, we use ZeroCopy and pass the already
- * allocated skb up the stack.
- * If the frame is small, it is more efficient to copy it
- * to save memory (copy will be fast anyway - that's
- * called Rx-copy-break). Jean II */
- docopy = (urb->actual_length < IRDA_RX_COPY_THRESHOLD);
-
- /* Allocate a new skb */
- if (self->capability & IUC_STIR421X)
- newskb = dev_alloc_skb(docopy ? urb->actual_length :
- IRDA_SKB_MAX_MTU +
- USB_IRDA_STIR421X_HEADER);
- else
- newskb = dev_alloc_skb(docopy ? urb->actual_length :
- IRDA_SKB_MAX_MTU);
-
- if (!newskb) {
- self->netdev->stats.rx_dropped++;
- /* We could deliver the current skb, but this would stall
- * the Rx path. Better drop the packet... Jean II */
- goto done;
- }
-
- /* Make sure IP header get aligned (IrDA header is 5 bytes) */
- /* But IrDA-USB header is 1 byte. Jean II */
- //skb_reserve(newskb, USB_IRDA_HEADER - 1);
-
- if(docopy) {
- /* Copy packet, so we can recycle the original */
- skb_copy_from_linear_data(skb, newskb->data, urb->actual_length);
- /* Deliver this new skb */
- dataskb = newskb;
- /* And hook the old skb to the URB
- * Note : we don't need to "clean up" the old skb,
- * as we never touched it. Jean II */
- } else {
- /* We are using ZeroCopy. Deliver old skb */
- dataskb = skb;
- /* And hook the new skb to the URB */
- skb = newskb;
- }
-
- /* Set proper length on skb & remove USB-IrDA header */
- skb_put(dataskb, urb->actual_length);
- skb_pull(dataskb, self->header_length);
-
- /* Ask the networking layer to queue the packet for the IrDA stack */
- dataskb->dev = self->netdev;
- skb_reset_mac_header(dataskb);
- dataskb->protocol = htons(ETH_P_IRDA);
- len = dataskb->len;
- netif_rx(dataskb);
-
- /* Keep stats up to date */
- self->netdev->stats.rx_bytes += len;
- self->netdev->stats.rx_packets++;
-
-done:
- /* Note : at this point, the URB we've just received (urb)
- * is still referenced by the USB layer. For example, if we
- * have received a -ECONNRESET, uhci_cleanup_unlink() will
- * continue to process it (in fact, cleaning it up).
- * If we were to submit this URB, disaster would ensue.
- * Therefore, we submit our idle URB, and put this URB in our
- * idle slot....
- * Jean II */
- /* Note : with this scheme, we could submit the idle URB before
- * processing the Rx URB. I don't think it would buy us anything as
- * we are running in the USB thread context. Jean II */
- next_urb = self->idle_rx_urb;
-
- /* Recycle Rx URB : Now, the idle URB is the present one */
- urb->context = NULL;
- self->idle_rx_urb = urb;
-
- /* Submit the idle URB to replace the URB we've just received.
- * Do it last to avoid race conditions... Jean II */
- irda_usb_submit(self, skb, next_urb);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * In case of errors, we want the USB layer to have time to recover.
- * Now, it is time to resubmit ouur Rx URB...
- */
-static void irda_usb_rx_defer_expired(struct timer_list *t)
-{
- struct irda_usb_cb *self = from_timer(self, t, rx_defer_timer);
- struct urb *urb = self->rx_defer_timer_urb;
- struct sk_buff *skb = (struct sk_buff *) urb->context;
- struct urb *next_urb;
-
- /* Same stuff as when Rx is done, see above... */
- next_urb = self->idle_rx_urb;
- urb->context = NULL;
- self->idle_rx_urb = urb;
- irda_usb_submit(self, skb, next_urb);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Callbak from IrDA layer. IrDA wants to know if we have
- * started receiving anything.
- */
-static int irda_usb_is_receiving(struct irda_usb_cb *self)
-{
- /* Note : because of the way UHCI works, it's almost impossible
- * to get this info. The Controller DMA directly to memory and
- * signal only when the whole frame is finished. To know if the
- * first TD of the URB has been filled or not seems hard work...
- *
- * The other solution would be to use the "receiving" command
- * on the default decriptor with a usb_control_msg(), but that
- * would add USB traffic and would return result only in the
- * next USB frame (~1ms).
- *
- * I've been told that current dongles send status info on their
- * interrupt endpoint, and that's what the Windows driver uses
- * to know this info. Unfortunately, this is not yet in the spec...
- *
- * Jean II
- */
-
- return 0; /* For now */
-}
-
-#define STIR421X_PATCH_PRODUCT_VER "Product Version: "
-#define STIR421X_PATCH_STMP_TAG "STMP"
-#define STIR421X_PATCH_CODE_OFFSET 512 /* patch image starts before here */
-/* marks end of patch file header (PC DOS text file EOF character) */
-#define STIR421X_PATCH_END_OF_HDR_TAG 0x1A
-#define STIR421X_PATCH_BLOCK_SIZE 1023
-
-/*
- * Function stir421x_fwupload (struct irda_usb_cb *self,
- * unsigned char *patch,
- * const unsigned int patch_len)
- *
- * Upload firmware code to SigmaTel 421X IRDA-USB dongle
- */
-static int stir421x_fw_upload(struct irda_usb_cb *self,
- const unsigned char *patch,
- const unsigned int patch_len)
-{
- int ret = -ENOMEM;
- int actual_len = 0;
- unsigned int i;
- unsigned int block_size = 0;
- unsigned char *patch_block;
-
- patch_block = kzalloc(STIR421X_PATCH_BLOCK_SIZE, GFP_KERNEL);
- if (patch_block == NULL)
- return -ENOMEM;
-
- /* break up patch into 1023-byte sections */
- for (i = 0; i < patch_len; i += block_size) {
- block_size = patch_len - i;
-
- if (block_size > STIR421X_PATCH_BLOCK_SIZE)
- block_size = STIR421X_PATCH_BLOCK_SIZE;
-
- /* upload the patch section */
- memcpy(patch_block, patch + i, block_size);
-
- ret = usb_bulk_msg(self->usbdev,
- usb_sndbulkpipe(self->usbdev,
- self->bulk_out_ep),
- patch_block, block_size,
- &actual_len, msecs_to_jiffies(500));
- pr_debug("%s(): Bulk send %u bytes, ret=%d\n",
- __func__, actual_len, ret);
-
- if (ret < 0)
- break;
-
- mdelay(10);
- }
-
- kfree(patch_block);
-
- return ret;
- }
-
-/*
- * Function stir421x_patch_device(struct irda_usb_cb *self)
- *
- * Get a firmware code from userspase using hotplug request_firmware() call
- */
-static int stir421x_patch_device(struct irda_usb_cb *self)
-{
- unsigned int i;
- int ret;
- char stir421x_fw_name[12];
- const struct firmware *fw;
- const unsigned char *fw_version_ptr; /* pointer to version string */
- unsigned long fw_version = 0;
-
- /*
- * Known firmware patch file names for STIR421x dongles
- * are "42101001.sb" or "42101002.sb"
- */
- sprintf(stir421x_fw_name, "4210%4X.sb",
- le16_to_cpu(self->usbdev->descriptor.bcdDevice));
- ret = request_firmware(&fw, stir421x_fw_name, &self->usbdev->dev);
- if (ret < 0)
- return ret;
-
- /* We get a patch from userspace */
- net_info_ratelimited("%s(): Received firmware %s (%zu bytes)\n",
- __func__, stir421x_fw_name, fw->size);
-
- ret = -EINVAL;
-
- /* Get the bcd product version */
- if (!memcmp(fw->data, STIR421X_PATCH_PRODUCT_VER,
- sizeof(STIR421X_PATCH_PRODUCT_VER) - 1)) {
- fw_version_ptr = fw->data +
- sizeof(STIR421X_PATCH_PRODUCT_VER) - 1;
-
- /* Let's check if the product version is dotted */
- if (fw_version_ptr[3] == '.' &&
- fw_version_ptr[7] == '.') {
- unsigned long major, minor, build;
- major = simple_strtoul(fw_version_ptr, NULL, 10);
- minor = simple_strtoul(fw_version_ptr + 4, NULL, 10);
- build = simple_strtoul(fw_version_ptr + 8, NULL, 10);
-
- fw_version = (major << 12)
- + (minor << 8)
- + ((build / 10) << 4)
- + (build % 10);
-
- pr_debug("%s(): Firmware Product version %ld\n",
- __func__, fw_version);
- }
- }
-
- if (self->usbdev->descriptor.bcdDevice == cpu_to_le16(fw_version)) {
- /*
- * If we're here, we've found a correct patch
- * The actual image starts after the "STMP" keyword
- * so forward to the firmware header tag
- */
- for (i = 0; i < fw->size && fw->data[i] !=
- STIR421X_PATCH_END_OF_HDR_TAG; i++) ;
- /* here we check for the out of buffer case */
- if (i < STIR421X_PATCH_CODE_OFFSET && i < fw->size &&
- STIR421X_PATCH_END_OF_HDR_TAG == fw->data[i]) {
- if (!memcmp(fw->data + i + 1, STIR421X_PATCH_STMP_TAG,
- sizeof(STIR421X_PATCH_STMP_TAG) - 1)) {
-
- /* We can upload the patch to the target */
- i += sizeof(STIR421X_PATCH_STMP_TAG);
- ret = stir421x_fw_upload(self, &fw->data[i],
- fw->size - i);
- }
- }
- }
-
- release_firmware(fw);
-
- return ret;
-}
-
-
-/********************** IRDA DEVICE CALLBACKS **********************/
-/*
- * Main calls from the IrDA/Network subsystem.
- * Mostly registering a new irda-usb device and removing it....
- * We only deal with the IrDA side of the business, the USB side will
- * be dealt with below...
- */
-
-
-/*------------------------------------------------------------------*/
-/*
- * Function irda_usb_net_open (dev)
- *
- * Network device is taken up. Usually this is done by "ifconfig irda0 up"
- *
- * Note : don't mess with self->netopen - Jean II
- */
-static int irda_usb_net_open(struct net_device *netdev)
-{
- struct irda_usb_cb *self;
- unsigned long flags;
- char hwname[16];
- int i;
-
- IRDA_ASSERT(netdev != NULL, return -1;);
- self = netdev_priv(netdev);
- IRDA_ASSERT(self != NULL, return -1;);
-
- spin_lock_irqsave(&self->lock, flags);
- /* Can only open the device if it's there */
- if(!self->present) {
- spin_unlock_irqrestore(&self->lock, flags);
- net_warn_ratelimited("%s(), device not present!\n", __func__);
- return -1;
- }
-
- if(self->needspatch) {
- spin_unlock_irqrestore(&self->lock, flags);
- net_warn_ratelimited("%s(), device needs patch\n", __func__);
- return -EIO ;
- }
-
- /* Initialise default speed and xbofs value
- * (IrLAP will change that soon) */
- self->speed = -1;
- self->xbofs = -1;
- self->new_speed = -1;
- self->new_xbofs = -1;
-
- /* To do *before* submitting Rx urbs and starting net Tx queue
- * Jean II */
- self->netopen = 1;
- spin_unlock_irqrestore(&self->lock, flags);
-
- /*
- * Now that everything should be initialized properly,
- * Open new IrLAP layer instance to take care of us...
- * Note : will send immediately a speed change...
- */
- sprintf(hwname, "usb#%d", self->usbdev->devnum);
- self->irlap = irlap_open(netdev, &self->qos, hwname);
- IRDA_ASSERT(self->irlap != NULL, return -1;);
-
- /* Allow IrLAP to send data to us */
- netif_start_queue(netdev);
-
- /* We submit all the Rx URB except for one that we keep idle.
- * Need to be initialised before submitting other USBs, because
- * in some cases as soon as we submit the URBs the USB layer
- * will trigger a dummy receive - Jean II */
- self->idle_rx_urb = self->rx_urb[IU_MAX_ACTIVE_RX_URBS];
- self->idle_rx_urb->context = NULL;
-
- /* Now that we can pass data to IrLAP, allow the USB layer
- * to send us some data... */
- for (i = 0; i < IU_MAX_ACTIVE_RX_URBS; i++) {
- struct sk_buff *skb = dev_alloc_skb(IRDA_SKB_MAX_MTU);
- if (!skb) {
- /* If this ever happen, we are in deep s***.
- * Basically, we can't start the Rx path... */
- return -1;
- }
- //skb_reserve(newskb, USB_IRDA_HEADER - 1);
- irda_usb_submit(self, skb, self->rx_urb[i]);
- }
-
- /* Ready to play !!! */
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irda_usb_net_close (self)
- *
- * Network device is taken down. Usually this is done by
- * "ifconfig irda0 down"
- */
-static int irda_usb_net_close(struct net_device *netdev)
-{
- struct irda_usb_cb *self;
- int i;
-
- IRDA_ASSERT(netdev != NULL, return -1;);
- self = netdev_priv(netdev);
- IRDA_ASSERT(self != NULL, return -1;);
-
- /* Clear this flag *before* unlinking the urbs and *before*
- * stopping the network Tx queue - Jean II */
- self->netopen = 0;
-
- /* Stop network Tx queue */
- netif_stop_queue(netdev);
-
- /* Kill defered Rx URB */
- del_timer(&self->rx_defer_timer);
-
- /* Deallocate all the Rx path buffers (URBs and skb) */
- for (i = 0; i < self->max_rx_urb; i++) {
- struct urb *urb = self->rx_urb[i];
- struct sk_buff *skb = (struct sk_buff *) urb->context;
- /* Cancel the receive command */
- usb_kill_urb(urb);
- /* The skb is ours, free it */
- if(skb) {
- dev_kfree_skb(skb);
- urb->context = NULL;
- }
- }
- /* Cancel Tx and speed URB - need to be synchronous to avoid races */
- usb_kill_urb(self->tx_urb);
- usb_kill_urb(self->speed_urb);
-
- /* Stop and remove instance of IrLAP */
- if (self->irlap)
- irlap_close(self->irlap);
- self->irlap = NULL;
-
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * IOCTLs : Extra out-of-band network commands...
- */
-static int irda_usb_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
- unsigned long flags;
- struct if_irda_req *irq = (struct if_irda_req *) rq;
- struct irda_usb_cb *self;
- int ret = 0;
-
- IRDA_ASSERT(dev != NULL, return -1;);
- self = netdev_priv(dev);
- IRDA_ASSERT(self != NULL, return -1;);
-
- pr_debug("%s(), %s, (cmd=0x%X)\n", __func__, dev->name, cmd);
-
- switch (cmd) {
- case SIOCSBANDWIDTH: /* Set bandwidth */
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
- /* Protect us from USB callbacks, net watchdog and else. */
- spin_lock_irqsave(&self->lock, flags);
- /* Check if the device is still there */
- if(self->present) {
- /* Set the desired speed */
- self->new_speed = irq->ifr_baudrate;
- irda_usb_change_speed_xbofs(self);
- }
- spin_unlock_irqrestore(&self->lock, flags);
- break;
- case SIOCSMEDIABUSY: /* Set media busy */
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
- /* Check if the IrDA stack is still there */
- if(self->netopen)
- irda_device_set_media_busy(self->netdev, TRUE);
- break;
- case SIOCGRECEIVING: /* Check if we are receiving right now */
- irq->ifr_receiving = irda_usb_is_receiving(self);
- break;
- default:
- ret = -EOPNOTSUPP;
- }
-
- return ret;
-}
-
-/*------------------------------------------------------------------*/
-
-/********************* IRDA CONFIG SUBROUTINES *********************/
-/*
- * Various subroutines dealing with IrDA and network stuff we use to
- * configure and initialise each irda-usb instance.
- * These functions are used below in the main calls of the driver...
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Set proper values in the IrDA QOS structure
- */
-static inline void irda_usb_init_qos(struct irda_usb_cb *self)
-{
- struct irda_class_desc *desc;
-
-
- desc = self->irda_desc;
-
- /* Initialize QoS for this device */
- irda_init_max_qos_capabilies(&self->qos);
-
- /* See spec section 7.2 for meaning.
- * Values are little endian (as most USB stuff), the IrDA stack
- * use it in native order (see parameters.c). - Jean II */
- self->qos.baud_rate.bits = le16_to_cpu(desc->wBaudRate);
- self->qos.min_turn_time.bits = desc->bmMinTurnaroundTime;
- self->qos.additional_bofs.bits = desc->bmAdditionalBOFs;
- self->qos.window_size.bits = desc->bmWindowSize;
- self->qos.data_size.bits = desc->bmDataSize;
-
- pr_debug("%s(), dongle says speed=0x%X, size=0x%X, window=0x%X, bofs=0x%X, turn=0x%X\n",
- __func__, self->qos.baud_rate.bits, self->qos.data_size.bits,
- self->qos.window_size.bits, self->qos.additional_bofs.bits,
- self->qos.min_turn_time.bits);
-
- /* Don't always trust what the dongle tell us */
- if(self->capability & IUC_SIR_ONLY)
- self->qos.baud_rate.bits &= 0x00ff;
- if(self->capability & IUC_SMALL_PKT)
- self->qos.data_size.bits = 0x07;
- if(self->capability & IUC_NO_WINDOW)
- self->qos.window_size.bits = 0x01;
- if(self->capability & IUC_MAX_WINDOW)
- self->qos.window_size.bits = 0x7f;
- if(self->capability & IUC_MAX_XBOFS)
- self->qos.additional_bofs.bits = 0x01;
-
-#if 1
- /* Module parameter can override the rx window size */
- if (qos_mtt_bits)
- self->qos.min_turn_time.bits = qos_mtt_bits;
-#endif
- /*
- * Note : most of those values apply only for the receive path,
- * the transmit path will be set differently - Jean II
- */
- irda_qos_bits_to_value(&self->qos);
-}
-
-/*------------------------------------------------------------------*/
-static const struct net_device_ops irda_usb_netdev_ops = {
- .ndo_open = irda_usb_net_open,
- .ndo_stop = irda_usb_net_close,
- .ndo_do_ioctl = irda_usb_net_ioctl,
- .ndo_start_xmit = irda_usb_hard_xmit,
- .ndo_tx_timeout = irda_usb_net_timeout,
-};
-
-/*
- * Initialise the network side of the irda-usb instance
- * Called when a new USB instance is registered in irda_usb_probe()
- */
-static inline int irda_usb_open(struct irda_usb_cb *self)
-{
- struct net_device *netdev = self->netdev;
-
- netdev->netdev_ops = &irda_usb_netdev_ops;
-
- irda_usb_init_qos(self);
-
- return register_netdev(netdev);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Cleanup the network side of the irda-usb instance
- * Called when a USB instance is removed in irda_usb_disconnect()
- */
-static inline void irda_usb_close(struct irda_usb_cb *self)
-{
- /* Remove netdevice */
- unregister_netdev(self->netdev);
-
- /* Remove the speed buffer */
- kfree(self->speed_buff);
- self->speed_buff = NULL;
-
- kfree(self->tx_buff);
- self->tx_buff = NULL;
-}
-
-/********************** USB CONFIG SUBROUTINES **********************/
-/*
- * Various subroutines dealing with USB stuff we use to configure and
- * initialise each irda-usb instance.
- * These functions are used below in the main calls of the driver...
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Function irda_usb_parse_endpoints(dev, ifnum)
- *
- * Parse the various endpoints and find the one we need.
- *
- * The endpoint are the pipes used to communicate with the USB device.
- * The spec defines 2 endpoints of type bulk transfer, one in, and one out.
- * These are used to pass frames back and forth with the dongle.
- * Most dongle have also an interrupt endpoint, that will be probably
- * documented in the next spec...
- */
-static inline int irda_usb_parse_endpoints(struct irda_usb_cb *self, struct usb_host_endpoint *endpoint, int ennum)
-{
- int i; /* Endpoint index in table */
-
- /* Init : no endpoints */
- self->bulk_in_ep = 0;
- self->bulk_out_ep = 0;
- self->bulk_int_ep = 0;
-
- /* Let's look at all those endpoints */
- for(i = 0; i < ennum; i++) {
- /* All those variables will get optimised by the compiler,
- * so let's aim for clarity... - Jean II */
- __u8 ep; /* Endpoint address */
- __u8 dir; /* Endpoint direction */
- __u8 attr; /* Endpoint attribute */
- __u16 psize; /* Endpoint max packet size in bytes */
-
- /* Get endpoint address, direction and attribute */
- ep = endpoint[i].desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
- dir = endpoint[i].desc.bEndpointAddress & USB_ENDPOINT_DIR_MASK;
- attr = endpoint[i].desc.bmAttributes;
- psize = le16_to_cpu(endpoint[i].desc.wMaxPacketSize);
-
- /* Is it a bulk endpoint ??? */
- if(attr == USB_ENDPOINT_XFER_BULK) {
- /* We need to find an IN and an OUT */
- if(dir == USB_DIR_IN) {
- /* This is our Rx endpoint */
- self->bulk_in_ep = ep;
- } else {
- /* This is our Tx endpoint */
- self->bulk_out_ep = ep;
- self->bulk_out_mtu = psize;
- }
- } else {
- if((attr == USB_ENDPOINT_XFER_INT) &&
- (dir == USB_DIR_IN)) {
- /* This is our interrupt endpoint */
- self->bulk_int_ep = ep;
- } else {
- net_err_ratelimited("%s(), Unrecognised endpoint %02X\n",
- __func__, ep);
- }
- }
- }
-
- pr_debug("%s(), And our endpoints are : in=%02X, out=%02X (%d), int=%02X\n",
- __func__, self->bulk_in_ep, self->bulk_out_ep,
- self->bulk_out_mtu, self->bulk_int_ep);
-
- return (self->bulk_in_ep != 0) && (self->bulk_out_ep != 0);
-}
-
-#ifdef IU_DUMP_CLASS_DESC
-/*------------------------------------------------------------------*/
-/*
- * Function usb_irda_dump_class_desc(desc)
- *
- * Prints out the contents of the IrDA class descriptor
- *
- */
-static inline void irda_usb_dump_class_desc(struct irda_class_desc *desc)
-{
- /* Values are little endian */
- printk("bLength=%x\n", desc->bLength);
- printk("bDescriptorType=%x\n", desc->bDescriptorType);
- printk("bcdSpecRevision=%x\n", le16_to_cpu(desc->bcdSpecRevision));
- printk("bmDataSize=%x\n", desc->bmDataSize);
- printk("bmWindowSize=%x\n", desc->bmWindowSize);
- printk("bmMinTurnaroundTime=%d\n", desc->bmMinTurnaroundTime);
- printk("wBaudRate=%x\n", le16_to_cpu(desc->wBaudRate));
- printk("bmAdditionalBOFs=%x\n", desc->bmAdditionalBOFs);
- printk("bIrdaRateSniff=%x\n", desc->bIrdaRateSniff);
- printk("bMaxUnicastList=%x\n", desc->bMaxUnicastList);
-}
-#endif /* IU_DUMP_CLASS_DESC */
-
-/*------------------------------------------------------------------*/
-/*
- * Function irda_usb_find_class_desc(intf)
- *
- * Returns instance of IrDA class descriptor, or NULL if not found
- *
- * The class descriptor is some extra info that IrDA USB devices will
- * offer to us, describing their IrDA characteristics. We will use that in
- * irda_usb_init_qos()
- */
-static inline struct irda_class_desc *irda_usb_find_class_desc(struct usb_interface *intf)
-{
- struct usb_device *dev = interface_to_usbdev (intf);
- struct irda_class_desc *desc;
- int ret;
-
- desc = kzalloc(sizeof(*desc), GFP_KERNEL);
- if (!desc)
- return NULL;
-
- /* USB-IrDA class spec 1.0:
- * 6.1.3: Standard "Get Descriptor" Device Request is not
- * appropriate to retrieve class-specific descriptor
- * 6.2.5: Class Specific "Get Class Descriptor" Interface Request
- * is mandatory and returns the USB-IrDA class descriptor
- */
-
- ret = usb_control_msg(dev, usb_rcvctrlpipe(dev,0),
- IU_REQ_GET_CLASS_DESC,
- USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- 0, intf->altsetting->desc.bInterfaceNumber, desc,
- sizeof(*desc), 500);
-
- pr_debug("%s(), ret=%d\n", __func__, ret);
- if (ret < sizeof(*desc)) {
- net_warn_ratelimited("usb-irda: class_descriptor read %s (%d)\n",
- ret < 0 ? "failed" : "too short", ret);
- }
- else if (desc->bDescriptorType != USB_DT_IRDA) {
- net_warn_ratelimited("usb-irda: bad class_descriptor type\n");
- }
- else {
-#ifdef IU_DUMP_CLASS_DESC
- irda_usb_dump_class_desc(desc);
-#endif /* IU_DUMP_CLASS_DESC */
-
- return desc;
- }
- kfree(desc);
- return NULL;
-}
-
-/*********************** USB DEVICE CALLBACKS ***********************/
-/*
- * Main calls from the USB subsystem.
- * Mostly registering a new irda-usb device and removing it....
- */
-
-/*------------------------------------------------------------------*/
-/*
- * This routine is called by the USB subsystem for each new device
- * in the system. We need to check if the device is ours, and in
- * this case start handling it.
- * The USB layer protect us from reentrancy (via BKL), so we don't need
- * to spinlock in there... Jean II
- */
-static int irda_usb_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- struct net_device *net;
- struct usb_device *dev = interface_to_usbdev(intf);
- struct irda_usb_cb *self;
- struct usb_host_interface *interface;
- struct irda_class_desc *irda_desc;
- int ret = -ENOMEM;
- int i; /* Driver instance index / Rx URB index */
-
- /* Note : the probe make sure to call us only for devices that
- * matches the list of dongle (top of the file). So, we
- * don't need to check if the dongle is really ours.
- * Jean II */
-
- net_info_ratelimited("IRDA-USB found at address %d, Vendor: %x, Product: %x\n",
- dev->devnum, le16_to_cpu(dev->descriptor.idVendor),
- le16_to_cpu(dev->descriptor.idProduct));
-
- net = alloc_irdadev(sizeof(*self));
- if (!net)
- goto err_out;
-
- SET_NETDEV_DEV(net, &intf->dev);
- self = netdev_priv(net);
- self->netdev = net;
- spin_lock_init(&self->lock);
- timer_setup(&self->rx_defer_timer, irda_usb_rx_defer_expired, 0);
-
- self->capability = id->driver_info;
- self->needspatch = ((self->capability & IUC_STIR421X) != 0);
-
- /* Create all of the needed urbs */
- if (self->capability & IUC_STIR421X) {
- self->max_rx_urb = IU_SIGMATEL_MAX_RX_URBS;
- self->header_length = USB_IRDA_STIR421X_HEADER;
- } else {
- self->max_rx_urb = IU_MAX_RX_URBS;
- self->header_length = USB_IRDA_HEADER;
- }
-
- self->rx_urb = kcalloc(self->max_rx_urb, sizeof(struct urb *),
- GFP_KERNEL);
- if (!self->rx_urb)
- goto err_free_net;
-
- for (i = 0; i < self->max_rx_urb; i++) {
- self->rx_urb[i] = usb_alloc_urb(0, GFP_KERNEL);
- if (!self->rx_urb[i]) {
- goto err_out_1;
- }
- }
- self->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!self->tx_urb) {
- goto err_out_1;
- }
- self->speed_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!self->speed_urb) {
- goto err_out_2;
- }
-
- /* Is this really necessary? (no, except maybe for broken devices) */
- if (usb_reset_configuration (dev) < 0) {
- dev_err(&intf->dev, "reset_configuration failed\n");
- ret = -EIO;
- goto err_out_3;
- }
-
- /* Is this really necessary? */
- /* Note : some driver do hardcode the interface number, some others
- * specify an alternate, but very few driver do like this.
- * Jean II */
- ret = usb_set_interface(dev, intf->altsetting->desc.bInterfaceNumber, 0);
- pr_debug("usb-irda: set interface %d result %d\n",
- intf->altsetting->desc.bInterfaceNumber, ret);
- switch (ret) {
- case 0:
- break;
- case -EPIPE: /* -EPIPE = -32 */
- /* Martin Diehl says if we get a -EPIPE we should
- * be fine and we don't need to do a usb_clear_halt().
- * - Jean II */
- pr_debug("%s(), Received -EPIPE, ignoring...\n",
- __func__);
- break;
- default:
- pr_debug("%s(), Unknown error %d\n", __func__, ret);
- ret = -EIO;
- goto err_out_3;
- }
-
- /* Find our endpoints */
- interface = intf->cur_altsetting;
- if(!irda_usb_parse_endpoints(self, interface->endpoint,
- interface->desc.bNumEndpoints)) {
- net_err_ratelimited("%s(), Bogus endpoints...\n", __func__);
- ret = -EIO;
- goto err_out_3;
- }
-
- self->usbdev = dev;
-
- /* Find IrDA class descriptor */
- irda_desc = irda_usb_find_class_desc(intf);
- ret = -ENODEV;
- if (!irda_desc)
- goto err_out_3;
-
- if (self->needspatch) {
- ret = usb_control_msg (self->usbdev, usb_sndctrlpipe (self->usbdev, 0),
- 0x02, 0x40, 0, 0, NULL, 0, 500);
- if (ret < 0) {
- pr_debug("usb_control_msg failed %d\n", ret);
- goto err_out_3;
- } else {
- mdelay(10);
- }
- }
-
- self->irda_desc = irda_desc;
- self->present = 1;
- self->netopen = 0;
- self->usbintf = intf;
-
- /* Allocate the buffer for speed changes */
- /* Don't change this buffer size and allocation without doing
- * some heavy and complete testing. Don't ask why :-(
- * Jean II */
- ret = -ENOMEM;
- self->speed_buff = kzalloc(IRDA_USB_SPEED_MTU, GFP_KERNEL);
- if (!self->speed_buff)
- goto err_out_3;
-
- self->tx_buff = kzalloc(IRDA_SKB_MAX_MTU + self->header_length,
- GFP_KERNEL);
- if (!self->tx_buff)
- goto err_out_4;
-
- ret = irda_usb_open(self);
- if (ret)
- goto err_out_5;
-
- net_info_ratelimited("IrDA: Registered device %s\n", net->name);
- usb_set_intfdata(intf, self);
-
- if (self->needspatch) {
- /* Now we fetch and upload the firmware patch */
- ret = stir421x_patch_device(self);
- self->needspatch = (ret < 0);
- if (self->needspatch) {
- net_err_ratelimited("STIR421X: Couldn't upload patch\n");
- goto err_out_6;
- }
-
- /* replace IrDA class descriptor with what patched device is now reporting */
- irda_desc = irda_usb_find_class_desc (self->usbintf);
- if (!irda_desc) {
- ret = -ENODEV;
- goto err_out_6;
- }
- kfree(self->irda_desc);
- self->irda_desc = irda_desc;
- irda_usb_init_qos(self);
- }
-
- return 0;
-err_out_6:
- unregister_netdev(self->netdev);
-err_out_5:
- kfree(self->tx_buff);
-err_out_4:
- kfree(self->speed_buff);
-err_out_3:
- /* Free all urbs that we may have created */
- usb_free_urb(self->speed_urb);
-err_out_2:
- usb_free_urb(self->tx_urb);
-err_out_1:
- for (i = 0; i < self->max_rx_urb; i++)
- usb_free_urb(self->rx_urb[i]);
- kfree(self->rx_urb);
-err_free_net:
- free_netdev(net);
-err_out:
- return ret;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * The current irda-usb device is removed, the USB layer tell us
- * to shut it down...
- * One of the constraints is that when we exit this function,
- * we cannot use the usb_device no more. Gone. Destroyed. kfree().
- * Most other subsystem allow you to destroy the instance at a time
- * when it's convenient to you, to postpone it to a later date, but
- * not the USB subsystem.
- * So, we must make bloody sure that everything gets deactivated.
- * Jean II
- */
-static void irda_usb_disconnect(struct usb_interface *intf)
-{
- unsigned long flags;
- struct irda_usb_cb *self = usb_get_intfdata(intf);
- int i;
-
- usb_set_intfdata(intf, NULL);
- if (!self)
- return;
-
- /* Make sure that the Tx path is not executing. - Jean II */
- spin_lock_irqsave(&self->lock, flags);
-
- /* Oups ! We are not there any more.
- * This will stop/desactivate the Tx path. - Jean II */
- self->present = 0;
-
- /* Kill defered Rx URB */
- del_timer(&self->rx_defer_timer);
-
- /* We need to have irq enabled to unlink the URBs. That's OK,
- * at this point the Tx path is gone - Jean II */
- spin_unlock_irqrestore(&self->lock, flags);
-
- /* Hum... Check if networking is still active (avoid races) */
- if((self->netopen) || (self->irlap)) {
- /* Accept no more transmissions */
- /*netif_device_detach(self->netdev);*/
- netif_stop_queue(self->netdev);
- /* Stop all the receive URBs. Must be synchronous. */
- for (i = 0; i < self->max_rx_urb; i++)
- usb_kill_urb(self->rx_urb[i]);
- /* Cancel Tx and speed URB.
- * Make sure it's synchronous to avoid races. */
- usb_kill_urb(self->tx_urb);
- usb_kill_urb(self->speed_urb);
- }
-
- /* Cleanup the device stuff */
- irda_usb_close(self);
- /* No longer attached to USB bus */
- self->usbdev = NULL;
- self->usbintf = NULL;
-
- /* Clean up our urbs */
- for (i = 0; i < self->max_rx_urb; i++)
- usb_free_urb(self->rx_urb[i]);
- kfree(self->rx_urb);
- /* Clean up Tx and speed URB */
- usb_free_urb(self->tx_urb);
- usb_free_urb(self->speed_urb);
-
- /* Free self and network device */
- free_netdev(self->netdev);
- pr_debug("%s(), USB IrDA Disconnected\n", __func__);
-}
-
-#ifdef CONFIG_PM
-/* USB suspend, so power off the transmitter/receiver */
-static int irda_usb_suspend(struct usb_interface *intf, pm_message_t message)
-{
- struct irda_usb_cb *self = usb_get_intfdata(intf);
- int i;
-
- netif_device_detach(self->netdev);
-
- if (self->tx_urb != NULL)
- usb_kill_urb(self->tx_urb);
- if (self->speed_urb != NULL)
- usb_kill_urb(self->speed_urb);
- for (i = 0; i < self->max_rx_urb; i++) {
- if (self->rx_urb[i] != NULL)
- usb_kill_urb(self->rx_urb[i]);
- }
- return 0;
-}
-
-/* Coming out of suspend, so reset hardware */
-static int irda_usb_resume(struct usb_interface *intf)
-{
- struct irda_usb_cb *self = usb_get_intfdata(intf);
- int i;
-
- for (i = 0; i < self->max_rx_urb; i++) {
- if (self->rx_urb[i] != NULL)
- usb_submit_urb(self->rx_urb[i], GFP_KERNEL);
- }
-
- netif_device_attach(self->netdev);
- return 0;
-}
-#endif
-
-/*------------------------------------------------------------------*/
-/*
- * USB device callbacks
- */
-static struct usb_driver irda_driver = {
- .name = "irda-usb",
- .probe = irda_usb_probe,
- .disconnect = irda_usb_disconnect,
- .id_table = dongles,
-#ifdef CONFIG_PM
- .suspend = irda_usb_suspend,
- .resume = irda_usb_resume,
-#endif
-};
-
-module_usb_driver(irda_driver);
-
-/*
- * Module parameters
- */
-module_param(qos_mtt_bits, int, 0);
-MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time");
-MODULE_AUTHOR("Roman Weissgaerber <weissg@vienna.at>, Dag Brattli <dag@brattli.net>, Jean Tourrilhes <jt@hpl.hp.com> and Nick Fedchik <nick@fedchik.org.ua>");
-MODULE_DESCRIPTION("IrDA-USB Dongle Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/staging/irda/drivers/irda-usb.h b/drivers/staging/irda/drivers/irda-usb.h
deleted file mode 100644
index 56ee8c16c5e2..000000000000
--- a/drivers/staging/irda/drivers/irda-usb.h
+++ /dev/null
@@ -1,175 +0,0 @@
-/*****************************************************************************
- *
- * Filename: irda-usb.h
- * Version: 0.10
- * Description: IrDA-USB Driver
- * Status: Experimental
- * Author: Dag Brattli <dag@brattli.net>
- *
- * Copyright (C) 2001, Roman Weissgaerber <weissg@vienna.at>
- * Copyright (C) 2000, Dag Brattli <dag@brattli.net>
- * Copyright (C) 2001, Jean Tourrilhes <jt@hpl.hp.com>
- * Copyright (C) 2004, SigmaTel, Inc. <irquality@sigmatel.com>
- * Copyright (C) 2005, Milan Beno <beno@pobox.sk>
- * Copyright (C) 2006, Nick FEdchik <nick@fedchik.org.ua>
- *
- * 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/ktime.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irda_device.h> /* struct irlap_cb */
-
-#define RX_COPY_THRESHOLD 200
-#define IRDA_USB_MAX_MTU 2051
-#define IRDA_USB_SPEED_MTU 64 /* Weird, but work like this */
-
-/* Maximum number of active URB on the Rx path
- * This is the amount of buffers the we keep between the USB harware and the
- * IrDA stack.
- *
- * Note : the network layer does also queue the packets between us and the
- * IrDA stack, and is actually pretty fast and efficient in doing that.
- * Therefore, we don't need to have a large number of URBs, and we can
- * perfectly live happy with only one. We certainly don't need to keep the
- * full IrTTP window around here...
- * I repeat for those who have trouble to understand : 1 URB is plenty
- * good enough to handle back-to-back (brickwalled) frames. I tried it,
- * it works (it's the hardware that has trouble doing it).
- *
- * Having 2 URBs would allow the USB stack to process one URB while we take
- * care of the other and then swap the URBs...
- * On the other hand, increasing the number of URB will have penalities
- * in term of latency and will interact with the link management in IrLAP...
- * Jean II */
-#define IU_MAX_ACTIVE_RX_URBS 1 /* Don't touch !!! */
-
-/* When a Rx URB is passed back to us, we can't reuse it immediately,
- * because it may still be referenced by the USB layer. Therefore we
- * need to keep one extra URB in the Rx path.
- * Jean II */
-#define IU_MAX_RX_URBS (IU_MAX_ACTIVE_RX_URBS + 1)
-
-/* Various ugly stuff to try to workaround generic problems */
-/* Send speed command in case of timeout, just for trying to get things sane */
-#define IU_BUG_KICK_TIMEOUT
-/* Show the USB class descriptor */
-#undef IU_DUMP_CLASS_DESC
-/* Assume a minimum round trip latency for USB transfer (in us)...
- * USB transfer are done in the next USB slot if there is no traffic
- * (1/19 msec) and is done at 12 Mb/s :
- * Waiting for slot + tx = (53us + 16us) * 2 = 137us minimum.
- * Rx notification will only be done at the end of the USB frame period :
- * OHCI : frame period = 1ms
- * UHCI : frame period = 1ms, but notification can take 2 or 3 ms :-(
- * EHCI : frame period = 125us */
-#define IU_USB_MIN_RTT 500 /* This should be safe in most cases */
-
-/* Inbound header */
-#define MEDIA_BUSY 0x80
-
-#define SPEED_2400 0x01
-#define SPEED_9600 0x02
-#define SPEED_19200 0x03
-#define SPEED_38400 0x04
-#define SPEED_57600 0x05
-#define SPEED_115200 0x06
-#define SPEED_576000 0x07
-#define SPEED_1152000 0x08
-#define SPEED_4000000 0x09
-#define SPEED_16000000 0x0a
-
-/* Basic capabilities */
-#define IUC_DEFAULT 0x00 /* Basic device compliant with 1.0 spec */
-/* Main bugs */
-#define IUC_SPEED_BUG 0x01 /* Device doesn't set speed after the frame */
-#define IUC_NO_WINDOW 0x02 /* Device doesn't behave with big Rx window */
-#define IUC_NO_TURN 0x04 /* Device doesn't do turnaround by itself */
-/* Not currently used */
-#define IUC_SIR_ONLY 0x08 /* Device doesn't behave at FIR speeds */
-#define IUC_SMALL_PKT 0x10 /* Device doesn't behave with big Rx packets */
-#define IUC_MAX_WINDOW 0x20 /* Device underestimate the Rx window */
-#define IUC_MAX_XBOFS 0x40 /* Device need more xbofs than advertised */
-#define IUC_STIR421X 0x80 /* SigmaTel 4210/4220/4116 VFIR */
-
-/* USB class definitions */
-#define USB_IRDA_HEADER 0x01
-#define USB_CLASS_IRDA 0x02 /* USB_CLASS_APP_SPEC subclass */
-#define USB_DT_IRDA 0x21
-#define USB_IRDA_STIR421X_HEADER 0x03
-#define IU_SIGMATEL_MAX_RX_URBS (IU_MAX_ACTIVE_RX_URBS + \
- USB_IRDA_STIR421X_HEADER)
-
-struct irda_class_desc {
- __u8 bLength;
- __u8 bDescriptorType;
- __le16 bcdSpecRevision;
- __u8 bmDataSize;
- __u8 bmWindowSize;
- __u8 bmMinTurnaroundTime;
- __le16 wBaudRate;
- __u8 bmAdditionalBOFs;
- __u8 bIrdaRateSniff;
- __u8 bMaxUnicastList;
-} __packed;
-
-/* class specific interface request to get the IrDA-USB class descriptor
- * (6.2.5, USB-IrDA class spec 1.0) */
-
-#define IU_REQ_GET_CLASS_DESC 0x06
-#define STIR421X_MAX_PATCH_DOWNLOAD_SIZE 1023
-
-struct irda_usb_cb {
- struct irda_class_desc *irda_desc;
- struct usb_device *usbdev; /* init: probe_irda */
- struct usb_interface *usbintf; /* init: probe_irda */
- int netopen; /* Device is active for network */
- int present; /* Device is present on the bus */
- __u32 capability; /* Capability of the hardware */
- __u8 bulk_in_ep; /* Rx Endpoint assignments */
- __u8 bulk_out_ep; /* Tx Endpoint assignments */
- __u16 bulk_out_mtu; /* Max Tx packet size in bytes */
- __u8 bulk_int_ep; /* Interrupt Endpoint assignments */
-
- __u8 max_rx_urb;
- struct urb **rx_urb; /* URBs used to receive data frames */
- struct urb *idle_rx_urb; /* Pointer to idle URB in Rx path */
- struct urb *tx_urb; /* URB used to send data frames */
- struct urb *speed_urb; /* URB used to send speed commands */
-
- struct net_device *netdev; /* Yes! we are some kind of netdev. */
- struct irlap_cb *irlap; /* The link layer we are binded to */
- struct qos_info qos;
- char *speed_buff; /* Buffer for speed changes */
- char *tx_buff;
-
- ktime_t stamp;
-
- spinlock_t lock; /* For serializing Tx operations */
-
- __u16 xbofs; /* Current xbofs setting */
- __s16 new_xbofs; /* xbofs we need to set */
- __u32 speed; /* Current speed */
- __s32 new_speed; /* speed we need to set */
-
- __u8 header_length; /* USB-IrDA frame header size */
- int needspatch; /* device needs firmware patch */
-
- struct timer_list rx_defer_timer; /* Wait for Rx error to clear */
- struct urb *rx_defer_timer_urb; /* URB attached to rx_defer_timer */
-};
-
diff --git a/drivers/staging/irda/drivers/irtty-sir.c b/drivers/staging/irda/drivers/irtty-sir.c
deleted file mode 100644
index 7a20a9a4663a..000000000000
--- a/drivers/staging/irda/drivers/irtty-sir.c
+++ /dev/null
@@ -1,570 +0,0 @@
-/*********************************************************************
- *
- * Filename: irtty-sir.c
- * Version: 2.0
- * Description: IrDA line discipline implementation
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Tue Dec 9 21:18:38 1997
- * Modified at: Sun Oct 27 22:13:30 2002
- * Modified by: Martin Diehl <mad@mdiehl.de>
- * Sources: slip.c by Laurence Culhane, <loz@holmes.demon.co.uk>
- * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
- *
- * Copyright (c) 1998-2000 Dag Brattli,
- * Copyright (c) 2002 Martin Diehl,
- * All Rights Reserved.
- *
- * 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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/tty.h>
-#include <linux/init.h>
-#include <linux/uaccess.h>
-#include <linux/delay.h>
-#include <linux/mutex.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irda_device.h>
-
-#include "sir-dev.h"
-#include "irtty-sir.h"
-
-static int qos_mtt_bits = 0x03; /* 5 ms or more */
-
-module_param(qos_mtt_bits, int, 0);
-MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time");
-
-/* ------------------------------------------------------- */
-
-/* device configuration callbacks always invoked with irda-thread context */
-
-/* find out, how many chars we have in buffers below us
- * this is allowed to lie, i.e. return less chars than we
- * actually have. The returned value is used to determine
- * how long the irdathread should wait before doing the
- * real blocking wait_until_sent()
- */
-
-static int irtty_chars_in_buffer(struct sir_dev *dev)
-{
- struct sirtty_cb *priv = dev->priv;
-
- IRDA_ASSERT(priv != NULL, return -1;);
- IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -1;);
-
- return tty_chars_in_buffer(priv->tty);
-}
-
-/* Wait (sleep) until underlaying hardware finished transmission
- * i.e. hardware buffers are drained
- * this must block and not return before all characters are really sent
- *
- * If the tty sits on top of a 16550A-like uart, there are typically
- * up to 16 bytes in the fifo - f.e. 9600 bps 8N1 needs 16.7 msec
- *
- * With usbserial the uart-fifo is basically replaced by the converter's
- * outgoing endpoint buffer, which can usually hold 64 bytes (at least).
- * With pl2303 it appears we are safe with 60msec here.
- *
- * I really wish all serial drivers would provide
- * correct implementation of wait_until_sent()
- */
-
-#define USBSERIAL_TX_DONE_DELAY 60
-
-static void irtty_wait_until_sent(struct sir_dev *dev)
-{
- struct sirtty_cb *priv = dev->priv;
- struct tty_struct *tty;
-
- IRDA_ASSERT(priv != NULL, return;);
- IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return;);
-
- tty = priv->tty;
- if (tty->ops->wait_until_sent) {
- tty->ops->wait_until_sent(tty, msecs_to_jiffies(100));
- }
- else {
- msleep(USBSERIAL_TX_DONE_DELAY);
- }
-}
-
-/*
- * Function irtty_change_speed (dev, speed)
- *
- * Change the speed of the serial port.
- *
- * This may sleep in set_termios (usbserial driver f.e.) and must
- * not be called from interrupt/timer/tasklet therefore.
- * All such invocations are deferred to kIrDAd now so we can sleep there.
- */
-
-static int irtty_change_speed(struct sir_dev *dev, unsigned speed)
-{
- struct sirtty_cb *priv = dev->priv;
- struct tty_struct *tty;
- struct ktermios old_termios;
- int cflag;
-
- IRDA_ASSERT(priv != NULL, return -1;);
- IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -1;);
-
- tty = priv->tty;
-
- down_write(&tty->termios_rwsem);
- old_termios = tty->termios;
- cflag = tty->termios.c_cflag;
- tty_encode_baud_rate(tty, speed, speed);
- if (tty->ops->set_termios)
- tty->ops->set_termios(tty, &old_termios);
- priv->io.speed = speed;
- up_write(&tty->termios_rwsem);
-
- return 0;
-}
-
-/*
- * Function irtty_set_dtr_rts (dev, dtr, rts)
- *
- * This function can be used by dongles etc. to set or reset the status
- * of the dtr and rts lines
- */
-
-static int irtty_set_dtr_rts(struct sir_dev *dev, int dtr, int rts)
-{
- struct sirtty_cb *priv = dev->priv;
- int set = 0;
- int clear = 0;
-
- IRDA_ASSERT(priv != NULL, return -1;);
- IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -1;);
-
- if (rts)
- set |= TIOCM_RTS;
- else
- clear |= TIOCM_RTS;
- if (dtr)
- set |= TIOCM_DTR;
- else
- clear |= TIOCM_DTR;
-
- /*
- * We can't use ioctl() because it expects a non-null file structure,
- * and we don't have that here.
- * This function is not yet defined for all tty driver, so
- * let's be careful... Jean II
- */
- IRDA_ASSERT(priv->tty->ops->tiocmset != NULL, return -1;);
- priv->tty->ops->tiocmset(priv->tty, set, clear);
-
- return 0;
-}
-
-/* ------------------------------------------------------- */
-
-/* called from sir_dev when there is more data to send
- * context is either netdev->hard_xmit or some transmit-completion bh
- * i.e. we are under spinlock here and must not sleep.
- */
-
-static int irtty_do_write(struct sir_dev *dev, const unsigned char *ptr, size_t len)
-{
- struct sirtty_cb *priv = dev->priv;
- struct tty_struct *tty;
- int writelen;
-
- IRDA_ASSERT(priv != NULL, return -1;);
- IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -1;);
-
- tty = priv->tty;
- if (!tty->ops->write)
- return 0;
- set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
- writelen = tty_write_room(tty);
- if (writelen > len)
- writelen = len;
- return tty->ops->write(tty, ptr, writelen);
-}
-
-/* ------------------------------------------------------- */
-
-/* irda line discipline callbacks */
-
-/*
- * Function irtty_receive_buf( tty, cp, count)
- *
- * Handle the 'receiver data ready' interrupt. This function is called
- * by the 'tty_io' module in the kernel when a block of IrDA data has
- * been received, which can now be decapsulated and delivered for
- * further processing
- *
- * calling context depends on underlying driver and tty->port->low_latency!
- * for example (low_latency: 1 / 0):
- * serial.c: uart-interrupt / softint
- * usbserial: urb-complete-interrupt / softint
- */
-
-static void irtty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
- char *fp, int count)
-{
- struct sir_dev *dev;
- struct sirtty_cb *priv = tty->disc_data;
- int i;
-
- IRDA_ASSERT(priv != NULL, return;);
- IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return;);
-
- if (unlikely(count==0)) /* yes, this happens */
- return;
-
- dev = priv->dev;
- if (!dev) {
- net_warn_ratelimited("%s(), not ready yet!\n", __func__);
- return;
- }
-
- for (i = 0; i < count; i++) {
- /*
- * Characters received with a parity error, etc?
- */
- if (fp && *fp++) {
- pr_debug("Framing or parity error!\n");
- sirdev_receive(dev, NULL, 0); /* notify sir_dev (updating stats) */
- return;
- }
- }
-
- sirdev_receive(dev, cp, count);
-}
-
-/*
- * Function irtty_write_wakeup (tty)
- *
- * Called by the driver when there's room for more data. If we have
- * more packets to send, we send them here.
- *
- */
-static void irtty_write_wakeup(struct tty_struct *tty)
-{
- struct sirtty_cb *priv = tty->disc_data;
-
- IRDA_ASSERT(priv != NULL, return;);
- IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return;);
-
- clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
- if (priv->dev)
- sirdev_write_complete(priv->dev);
-}
-
-/* ------------------------------------------------------- */
-
-/*
- * Function irtty_stop_receiver (tty, stop)
- *
- */
-
-static inline void irtty_stop_receiver(struct tty_struct *tty, int stop)
-{
- struct ktermios old_termios;
- int cflag;
-
- down_write(&tty->termios_rwsem);
- old_termios = tty->termios;
- cflag = tty->termios.c_cflag;
-
- if (stop)
- cflag &= ~CREAD;
- else
- cflag |= CREAD;
-
- tty->termios.c_cflag = cflag;
- if (tty->ops->set_termios)
- tty->ops->set_termios(tty, &old_termios);
- up_write(&tty->termios_rwsem);
-}
-
-/*****************************************************************/
-
-/* serialize ldisc open/close with sir_dev */
-static DEFINE_MUTEX(irtty_mutex);
-
-/* notifier from sir_dev when irda% device gets opened (ifup) */
-
-static int irtty_start_dev(struct sir_dev *dev)
-{
- struct sirtty_cb *priv;
- struct tty_struct *tty;
-
- /* serialize with ldisc open/close */
- mutex_lock(&irtty_mutex);
-
- priv = dev->priv;
- if (unlikely(!priv || priv->magic!=IRTTY_MAGIC)) {
- mutex_unlock(&irtty_mutex);
- return -ESTALE;
- }
-
- tty = priv->tty;
-
- if (tty->ops->start)
- tty->ops->start(tty);
- /* Make sure we can receive more data */
- irtty_stop_receiver(tty, FALSE);
-
- mutex_unlock(&irtty_mutex);
- return 0;
-}
-
-/* notifier from sir_dev when irda% device gets closed (ifdown) */
-
-static int irtty_stop_dev(struct sir_dev *dev)
-{
- struct sirtty_cb *priv;
- struct tty_struct *tty;
-
- /* serialize with ldisc open/close */
- mutex_lock(&irtty_mutex);
-
- priv = dev->priv;
- if (unlikely(!priv || priv->magic!=IRTTY_MAGIC)) {
- mutex_unlock(&irtty_mutex);
- return -ESTALE;
- }
-
- tty = priv->tty;
-
- /* Make sure we don't receive more data */
- irtty_stop_receiver(tty, TRUE);
- if (tty->ops->stop)
- tty->ops->stop(tty);
-
- mutex_unlock(&irtty_mutex);
-
- return 0;
-}
-
-/* ------------------------------------------------------- */
-
-static struct sir_driver sir_tty_drv = {
- .owner = THIS_MODULE,
- .driver_name = "sir_tty",
- .start_dev = irtty_start_dev,
- .stop_dev = irtty_stop_dev,
- .do_write = irtty_do_write,
- .chars_in_buffer = irtty_chars_in_buffer,
- .wait_until_sent = irtty_wait_until_sent,
- .set_speed = irtty_change_speed,
- .set_dtr_rts = irtty_set_dtr_rts,
-};
-
-/* ------------------------------------------------------- */
-
-/*
- * Function irtty_ioctl (tty, file, cmd, arg)
- *
- * The Swiss army knife of system calls :-)
- *
- */
-static int irtty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct irtty_info { char name[6]; } info;
- struct sir_dev *dev;
- struct sirtty_cb *priv = tty->disc_data;
- int err = 0;
-
- IRDA_ASSERT(priv != NULL, return -ENODEV;);
- IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -EBADR;);
-
- pr_debug("%s(cmd=0x%X)\n", __func__, cmd);
-
- dev = priv->dev;
- IRDA_ASSERT(dev != NULL, return -1;);
-
- switch (cmd) {
- case IRTTY_IOCTDONGLE:
- /* this call blocks for completion */
- err = sirdev_set_dongle(dev, (IRDA_DONGLE) arg);
- break;
-
- case IRTTY_IOCGET:
- IRDA_ASSERT(dev->netdev != NULL, return -1;);
-
- memset(&info, 0, sizeof(info));
- strncpy(info.name, dev->netdev->name, sizeof(info.name)-1);
-
- if (copy_to_user((void __user *)arg, &info, sizeof(info)))
- err = -EFAULT;
- break;
- default:
- err = tty_mode_ioctl(tty, file, cmd, arg);
- break;
- }
- return err;
-}
-
-
-/*
- * Function irtty_open(tty)
- *
- * This function is called by the TTY module when the IrDA line
- * discipline is called for. Because we are sure the tty line exists,
- * we only have to link it to a free IrDA channel.
- */
-static int irtty_open(struct tty_struct *tty)
-{
- struct sir_dev *dev;
- struct sirtty_cb *priv;
- int ret = 0;
-
- /* Module stuff handled via irda_ldisc.owner - Jean II */
-
- /* stop the underlying driver */
- irtty_stop_receiver(tty, TRUE);
- if (tty->ops->stop)
- tty->ops->stop(tty);
-
- tty_driver_flush_buffer(tty);
-
- /* apply mtt override */
- sir_tty_drv.qos_mtt_bits = qos_mtt_bits;
-
- /* get a sir device instance for this driver */
- dev = sirdev_get_instance(&sir_tty_drv, tty->name);
- if (!dev) {
- ret = -ENODEV;
- goto out;
- }
-
- /* allocate private device info block */
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- if (!priv) {
- ret = -ENOMEM;
- goto out_put;
- }
-
- priv->magic = IRTTY_MAGIC;
- priv->tty = tty;
- priv->dev = dev;
-
- /* serialize with start_dev - in case we were racing with ifup */
- mutex_lock(&irtty_mutex);
-
- dev->priv = priv;
- tty->disc_data = priv;
- tty->receive_room = 65536;
-
- mutex_unlock(&irtty_mutex);
-
- pr_debug("%s - %s: irda line discipline opened\n", __func__, tty->name);
-
- return 0;
-
-out_put:
- sirdev_put_instance(dev);
-out:
- return ret;
-}
-
-/*
- * Function irtty_close (tty)
- *
- * Close down a IrDA channel. This means flushing out any pending queues,
- * and then restoring the TTY line discipline to what it was before it got
- * hooked to IrDA (which usually is TTY again).
- */
-static void irtty_close(struct tty_struct *tty)
-{
- struct sirtty_cb *priv = tty->disc_data;
-
- IRDA_ASSERT(priv != NULL, return;);
- IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return;);
-
- /* Hm, with a dongle attached the dongle driver wants
- * to close the dongle - which requires the use of
- * some tty write and/or termios or ioctl operations.
- * Are we allowed to call those when already requested
- * to shutdown the ldisc?
- * If not, we should somehow mark the dev being staled.
- * Question remains, how to close the dongle in this case...
- * For now let's assume we are granted to issue tty driver calls
- * until we return here from the ldisc close. I'm just wondering
- * how this behaves with hotpluggable serial hardware like
- * rs232-pcmcia card or usb-serial...
- *
- * priv->tty = NULL?;
- */
-
- /* we are dead now */
- tty->disc_data = NULL;
-
- sirdev_put_instance(priv->dev);
-
- /* Stop tty */
- clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
- if (tty->ops->stop)
- tty->ops->stop(tty);
-
- kfree(priv);
-
- pr_debug("%s - %s: irda line discipline closed\n", __func__, tty->name);
-}
-
-/* ------------------------------------------------------- */
-
-static struct tty_ldisc_ops irda_ldisc = {
- .magic = TTY_LDISC_MAGIC,
- .name = "irda",
- .flags = 0,
- .open = irtty_open,
- .close = irtty_close,
- .read = NULL,
- .write = NULL,
- .ioctl = irtty_ioctl,
- .poll = NULL,
- .receive_buf = irtty_receive_buf,
- .write_wakeup = irtty_write_wakeup,
- .owner = THIS_MODULE,
-};
-
-/* ------------------------------------------------------- */
-
-static int __init irtty_sir_init(void)
-{
- int err;
-
- if ((err = tty_register_ldisc(N_IRDA, &irda_ldisc)) != 0)
- net_err_ratelimited("IrDA: can't register line discipline (err = %d)\n",
- err);
- return err;
-}
-
-static void __exit irtty_sir_cleanup(void)
-{
- int err;
-
- if ((err = tty_unregister_ldisc(N_IRDA))) {
- net_err_ratelimited("%s(), can't unregister line discipline (err = %d)\n",
- __func__, err);
- }
-}
-
-module_init(irtty_sir_init);
-module_exit(irtty_sir_cleanup);
-
-MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
-MODULE_DESCRIPTION("IrDA TTY device driver");
-MODULE_ALIAS_LDISC(N_IRDA);
-MODULE_LICENSE("GPL");
-
diff --git a/drivers/staging/irda/drivers/irtty-sir.h b/drivers/staging/irda/drivers/irtty-sir.h
deleted file mode 100644
index b132d8f6eb13..000000000000
--- a/drivers/staging/irda/drivers/irtty-sir.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*********************************************************************
- *
- * sir_tty.h: definitions for the irtty_sir client driver (former irtty)
- *
- * Copyright (c) 2002 Martin Diehl
- *
- * 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 IRTTYSIR_H
-#define IRTTYSIR_H
-
-#include <net/irda/irda.h>
-#include <net/irda/irda_device.h> // chipio_t
-
-#define IRTTY_IOC_MAGIC 'e'
-#define IRTTY_IOCTDONGLE _IO(IRTTY_IOC_MAGIC, 1)
-#define IRTTY_IOCGET _IOR(IRTTY_IOC_MAGIC, 2, struct irtty_info)
-#define IRTTY_IOC_MAXNR 2
-
-struct sirtty_cb {
- magic_t magic;
-
- struct sir_dev *dev;
- struct tty_struct *tty;
-
- chipio_t io; /* IrDA controller information */
-};
-
-#endif
diff --git a/drivers/staging/irda/drivers/kingsun-sir.c b/drivers/staging/irda/drivers/kingsun-sir.c
deleted file mode 100644
index 4fd4ac2fe09f..000000000000
--- a/drivers/staging/irda/drivers/kingsun-sir.c
+++ /dev/null
@@ -1,634 +0,0 @@
-/*****************************************************************************
-*
-* Filename: kingsun-sir.c
-* Version: 0.1.1
-* Description: Irda KingSun/DonShine USB Dongle
-* Status: Experimental
-* Author: Alex Villacís Lasso <a_villacis@palosanto.com>
-*
-* Based on stir4200 and mcs7780 drivers, with (strange?) differences
-*
-* 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.
-*
-* 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.
-*
-*****************************************************************************/
-
-/*
- * This is my current (2007-04-25) understanding of how this dongle is supposed
- * to work. This is based on reverse-engineering and examination of the packet
- * data sent and received by the WinXP driver using USBSnoopy. Feel free to
- * update here as more of this dongle is known:
- *
- * General: Unlike the other USB IrDA dongles, this particular dongle exposes,
- * not two bulk (in and out) endpoints, but two *interrupt* ones. This dongle,
- * like the bulk based ones (stir4200.c and mcs7780.c), requires polling in
- * order to receive data.
- * Transmission: Just like stir4200, this dongle uses a raw stream of data,
- * which needs to be wrapped and escaped in a similar way as in stir4200.c.
- * Reception: Poll-based, as in stir4200. Each read returns the contents of a
- * 8-byte buffer, of which the first byte (LSB) indicates the number of bytes
- * (1-7) of valid data contained within the remaining 7 bytes. For example, if
- * the buffer had the following contents:
- * 06 ff ff ff c0 01 04 aa
- * This means that (06) there are 6 bytes of valid data. The byte 0xaa at the
- * end is garbage (left over from a previous reception) and is discarded.
- * If a read returns an "impossible" value as the length of valid data (such as
- * 0x36) in the first byte, then the buffer is uninitialized (as is the case of
- * first plug-in) and its contents should be discarded. There is currently no
- * evidence that the top 5 bits of the 1st byte of the buffer can have values
- * other than 0 once reception begins.
- * Once valid bytes are collected, the assembled stream is a sequence of
- * wrapped IrDA frames that is unwrapped and unescaped as in stir4200.c.
- * BIG FAT WARNING: the dongle does *not* reset the RX buffer in any way after
- * a successful read from the host, which means that in absence of further
- * reception, repeated reads from the dongle will return the exact same
- * contents repeatedly. Attempts to be smart and cache a previous read seem
- * to result in corrupted packets, so this driver depends on the unwrap logic
- * to sort out any repeated reads.
- * Speed change: no commands observed so far to change speed, assumed fixed
- * 9600bps (SIR).
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/usb.h>
-#include <linux/device.h>
-#include <linux/crc32.h>
-
-#include <asm/unaligned.h>
-#include <asm/byteorder.h>
-#include <linux/uaccess.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/wrapper.h>
-#include <net/irda/crc.h>
-
-/*
- * According to lsusb, 0x07c0 is assigned to
- * "Code Mercenaries Hard- und Software GmbH"
- */
-#define KING_VENDOR_ID 0x07c0
-#define KING_PRODUCT_ID 0x4200
-
-/* These are the currently known USB ids */
-static const struct usb_device_id dongles[] = {
- /* KingSun Co,Ltd IrDA/USB Bridge */
- { USB_DEVICE(KING_VENDOR_ID, KING_PRODUCT_ID) },
- { }
-};
-
-MODULE_DEVICE_TABLE(usb, dongles);
-
-#define KINGSUN_MTT 0x07
-
-#define KINGSUN_FIFO_SIZE 4096
-#define KINGSUN_EP_IN 0
-#define KINGSUN_EP_OUT 1
-
-struct kingsun_cb {
- struct usb_device *usbdev; /* init: probe_irda */
- struct net_device *netdev; /* network layer */
- struct irlap_cb *irlap; /* The link layer we are binded to */
-
- struct qos_info qos;
-
- __u8 *in_buf; /* receive buffer */
- __u8 *out_buf; /* transmit buffer */
- __u8 max_rx; /* max. atomic read from dongle
- (usually 8), also size of in_buf */
- __u8 max_tx; /* max. atomic write to dongle
- (usually 8) */
-
- iobuff_t rx_buff; /* receive unwrap state machine */
- spinlock_t lock;
- int receiving;
-
- __u8 ep_in;
- __u8 ep_out;
-
- struct urb *tx_urb;
- struct urb *rx_urb;
-};
-
-/* Callback transmission routine */
-static void kingsun_send_irq(struct urb *urb)
-{
- struct kingsun_cb *kingsun = urb->context;
- struct net_device *netdev = kingsun->netdev;
-
- /* in process of stopping, just drop data */
- if (!netif_running(kingsun->netdev)) {
- dev_err(&kingsun->usbdev->dev,
- "kingsun_send_irq: Network not running!\n");
- return;
- }
-
- /* unlink, shutdown, unplug, other nasties */
- if (urb->status != 0) {
- dev_err(&kingsun->usbdev->dev,
- "kingsun_send_irq: urb asynchronously failed - %d\n",
- urb->status);
- }
- netif_wake_queue(netdev);
-}
-
-/*
- * Called from net/core when new frame is available.
- */
-static netdev_tx_t kingsun_hard_xmit(struct sk_buff *skb,
- struct net_device *netdev)
-{
- struct kingsun_cb *kingsun;
- int wraplen;
- int ret = 0;
-
- netif_stop_queue(netdev);
-
- /* the IRDA wrapping routines don't deal with non linear skb */
- SKB_LINEAR_ASSERT(skb);
-
- kingsun = netdev_priv(netdev);
-
- spin_lock(&kingsun->lock);
-
- /* Append data to the end of whatever data remains to be transmitted */
- wraplen = async_wrap_skb(skb,
- kingsun->out_buf,
- KINGSUN_FIFO_SIZE);
-
- /* Calculate how much data can be transmitted in this urb */
- usb_fill_int_urb(kingsun->tx_urb, kingsun->usbdev,
- usb_sndintpipe(kingsun->usbdev, kingsun->ep_out),
- kingsun->out_buf, wraplen, kingsun_send_irq,
- kingsun, 1);
-
- if ((ret = usb_submit_urb(kingsun->tx_urb, GFP_ATOMIC))) {
- dev_err(&kingsun->usbdev->dev,
- "kingsun_hard_xmit: failed tx_urb submit: %d\n", ret);
- switch (ret) {
- case -ENODEV:
- case -EPIPE:
- break;
- default:
- netdev->stats.tx_errors++;
- netif_start_queue(netdev);
- }
- } else {
- netdev->stats.tx_packets++;
- netdev->stats.tx_bytes += skb->len;
- }
-
- dev_kfree_skb(skb);
- spin_unlock(&kingsun->lock);
-
- return NETDEV_TX_OK;
-}
-
-/* Receive callback function */
-static void kingsun_rcv_irq(struct urb *urb)
-{
- struct kingsun_cb *kingsun = urb->context;
- int ret;
-
- /* in process of stopping, just drop data */
- if (!netif_running(kingsun->netdev)) {
- kingsun->receiving = 0;
- return;
- }
-
- /* unlink, shutdown, unplug, other nasties */
- if (urb->status != 0) {
- dev_err(&kingsun->usbdev->dev,
- "kingsun_rcv_irq: urb asynchronously failed - %d\n",
- urb->status);
- kingsun->receiving = 0;
- return;
- }
-
- if (urb->actual_length == kingsun->max_rx) {
- __u8 *bytes = urb->transfer_buffer;
- int i;
-
- /* The very first byte in the buffer indicates the length of
- valid data in the read. This byte must be in the range
- 1..kingsun->max_rx -1 . Values outside this range indicate
- an uninitialized Rx buffer when the dongle has just been
- plugged in. */
- if (bytes[0] >= 1 && bytes[0] < kingsun->max_rx) {
- for (i = 1; i <= bytes[0]; i++) {
- async_unwrap_char(kingsun->netdev,
- &kingsun->netdev->stats,
- &kingsun->rx_buff, bytes[i]);
- }
- kingsun->receiving =
- (kingsun->rx_buff.state != OUTSIDE_FRAME)
- ? 1 : 0;
- }
- } else if (urb->actual_length > 0) {
- dev_err(&kingsun->usbdev->dev,
- "%s(): Unexpected response length, expected %d got %d\n",
- __func__, kingsun->max_rx, urb->actual_length);
- }
- /* This urb has already been filled in kingsun_net_open */
- ret = usb_submit_urb(urb, GFP_ATOMIC);
-}
-
-/*
- * Function kingsun_net_open (dev)
- *
- * Network device is taken up. Usually this is done by "ifconfig irda0 up"
- */
-static int kingsun_net_open(struct net_device *netdev)
-{
- struct kingsun_cb *kingsun = netdev_priv(netdev);
- int err = -ENOMEM;
- char hwname[16];
-
- /* At this point, urbs are NULL, and skb is NULL (see kingsun_probe) */
- kingsun->receiving = 0;
-
- /* Initialize for SIR to copy data directly into skb. */
- kingsun->rx_buff.in_frame = FALSE;
- kingsun->rx_buff.state = OUTSIDE_FRAME;
- kingsun->rx_buff.truesize = IRDA_SKB_MAX_MTU;
- kingsun->rx_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU);
- if (!kingsun->rx_buff.skb)
- goto free_mem;
-
- skb_reserve(kingsun->rx_buff.skb, 1);
- kingsun->rx_buff.head = kingsun->rx_buff.skb->data;
-
- kingsun->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!kingsun->rx_urb)
- goto free_mem;
-
- kingsun->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!kingsun->tx_urb)
- goto free_mem;
-
- /*
- * Now that everything should be initialized properly,
- * Open new IrLAP layer instance to take care of us...
- */
- sprintf(hwname, "usb#%d", kingsun->usbdev->devnum);
- kingsun->irlap = irlap_open(netdev, &kingsun->qos, hwname);
- if (!kingsun->irlap) {
- dev_err(&kingsun->usbdev->dev, "irlap_open failed\n");
- goto free_mem;
- }
-
- /* Start first reception */
- usb_fill_int_urb(kingsun->rx_urb, kingsun->usbdev,
- usb_rcvintpipe(kingsun->usbdev, kingsun->ep_in),
- kingsun->in_buf, kingsun->max_rx,
- kingsun_rcv_irq, kingsun, 1);
- kingsun->rx_urb->status = 0;
- err = usb_submit_urb(kingsun->rx_urb, GFP_KERNEL);
- if (err) {
- dev_err(&kingsun->usbdev->dev,
- "first urb-submit failed: %d\n", err);
- goto close_irlap;
- }
-
- netif_start_queue(netdev);
-
- /* Situation at this point:
- - all work buffers allocated
- - urbs allocated and ready to fill
- - max rx packet known (in max_rx)
- - unwrap state machine initialized, in state outside of any frame
- - receive request in progress
- - IrLAP layer started, about to hand over packets to send
- */
-
- return 0;
-
- close_irlap:
- irlap_close(kingsun->irlap);
- free_mem:
- if (kingsun->tx_urb) {
- usb_free_urb(kingsun->tx_urb);
- kingsun->tx_urb = NULL;
- }
- if (kingsun->rx_urb) {
- usb_free_urb(kingsun->rx_urb);
- kingsun->rx_urb = NULL;
- }
- if (kingsun->rx_buff.skb) {
- kfree_skb(kingsun->rx_buff.skb);
- kingsun->rx_buff.skb = NULL;
- kingsun->rx_buff.head = NULL;
- }
- return err;
-}
-
-/*
- * Function kingsun_net_close (kingsun)
- *
- * Network device is taken down. Usually this is done by
- * "ifconfig irda0 down"
- */
-static int kingsun_net_close(struct net_device *netdev)
-{
- struct kingsun_cb *kingsun = netdev_priv(netdev);
-
- /* Stop transmit processing */
- netif_stop_queue(netdev);
-
- /* Mop up receive && transmit urb's */
- usb_kill_urb(kingsun->tx_urb);
- usb_kill_urb(kingsun->rx_urb);
-
- usb_free_urb(kingsun->tx_urb);
- usb_free_urb(kingsun->rx_urb);
-
- kingsun->tx_urb = NULL;
- kingsun->rx_urb = NULL;
-
- kfree_skb(kingsun->rx_buff.skb);
- kingsun->rx_buff.skb = NULL;
- kingsun->rx_buff.head = NULL;
- kingsun->rx_buff.in_frame = FALSE;
- kingsun->rx_buff.state = OUTSIDE_FRAME;
- kingsun->receiving = 0;
-
- /* Stop and remove instance of IrLAP */
- if (kingsun->irlap)
- irlap_close(kingsun->irlap);
-
- kingsun->irlap = NULL;
-
- return 0;
-}
-
-/*
- * IOCTLs : Extra out-of-band network commands...
- */
-static int kingsun_net_ioctl(struct net_device *netdev, struct ifreq *rq,
- int cmd)
-{
- struct if_irda_req *irq = (struct if_irda_req *) rq;
- struct kingsun_cb *kingsun = netdev_priv(netdev);
- int ret = 0;
-
- switch (cmd) {
- case SIOCSBANDWIDTH: /* Set bandwidth */
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
-
- /* Check if the device is still there */
- if (netif_device_present(kingsun->netdev))
- /* No observed commands for speed change */
- ret = -EOPNOTSUPP;
- break;
-
- case SIOCSMEDIABUSY: /* Set media busy */
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
-
- /* Check if the IrDA stack is still there */
- if (netif_running(kingsun->netdev))
- irda_device_set_media_busy(kingsun->netdev, TRUE);
- break;
-
- case SIOCGRECEIVING:
- /* Only approximately true */
- irq->ifr_receiving = kingsun->receiving;
- break;
-
- default:
- ret = -EOPNOTSUPP;
- }
-
- return ret;
-}
-
-static const struct net_device_ops kingsun_ops = {
- .ndo_start_xmit = kingsun_hard_xmit,
- .ndo_open = kingsun_net_open,
- .ndo_stop = kingsun_net_close,
- .ndo_do_ioctl = kingsun_net_ioctl,
-};
-
-/*
- * This routine is called by the USB subsystem for each new device
- * in the system. We need to check if the device is ours, and in
- * this case start handling it.
- */
-static int kingsun_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- struct usb_host_interface *interface;
- struct usb_endpoint_descriptor *endpoint;
-
- struct usb_device *dev = interface_to_usbdev(intf);
- struct kingsun_cb *kingsun = NULL;
- struct net_device *net = NULL;
- int ret = -ENOMEM;
- int pipe, maxp_in, maxp_out;
- __u8 ep_in;
- __u8 ep_out;
-
- /* Check that there really are two interrupt endpoints.
- Check based on the one in drivers/usb/input/usbmouse.c
- */
- interface = intf->cur_altsetting;
- if (interface->desc.bNumEndpoints != 2) {
- dev_err(&intf->dev,
- "kingsun-sir: expected 2 endpoints, found %d\n",
- interface->desc.bNumEndpoints);
- return -ENODEV;
- }
- endpoint = &interface->endpoint[KINGSUN_EP_IN].desc;
- if (!usb_endpoint_is_int_in(endpoint)) {
- dev_err(&intf->dev,
- "kingsun-sir: endpoint 0 is not interrupt IN\n");
- return -ENODEV;
- }
-
- ep_in = endpoint->bEndpointAddress;
- pipe = usb_rcvintpipe(dev, ep_in);
- maxp_in = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
- if (maxp_in > 255 || maxp_in <= 1) {
- dev_err(&intf->dev,
- "endpoint 0 has max packet size %d not in range\n",
- maxp_in);
- return -ENODEV;
- }
-
- endpoint = &interface->endpoint[KINGSUN_EP_OUT].desc;
- if (!usb_endpoint_is_int_out(endpoint)) {
- dev_err(&intf->dev,
- "kingsun-sir: endpoint 1 is not interrupt OUT\n");
- return -ENODEV;
- }
-
- ep_out = endpoint->bEndpointAddress;
- pipe = usb_sndintpipe(dev, ep_out);
- maxp_out = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
-
- /* Allocate network device container. */
- net = alloc_irdadev(sizeof(*kingsun));
- if(!net)
- goto err_out1;
-
- SET_NETDEV_DEV(net, &intf->dev);
- kingsun = netdev_priv(net);
- kingsun->irlap = NULL;
- kingsun->tx_urb = NULL;
- kingsun->rx_urb = NULL;
- kingsun->ep_in = ep_in;
- kingsun->ep_out = ep_out;
- kingsun->in_buf = NULL;
- kingsun->out_buf = NULL;
- kingsun->max_rx = (__u8)maxp_in;
- kingsun->max_tx = (__u8)maxp_out;
- kingsun->netdev = net;
- kingsun->usbdev = dev;
- kingsun->rx_buff.in_frame = FALSE;
- kingsun->rx_buff.state = OUTSIDE_FRAME;
- kingsun->rx_buff.skb = NULL;
- kingsun->receiving = 0;
- spin_lock_init(&kingsun->lock);
-
- /* Allocate input buffer */
- kingsun->in_buf = kmalloc(kingsun->max_rx, GFP_KERNEL);
- if (!kingsun->in_buf)
- goto free_mem;
-
- /* Allocate output buffer */
- kingsun->out_buf = kmalloc(KINGSUN_FIFO_SIZE, GFP_KERNEL);
- if (!kingsun->out_buf)
- goto free_mem;
-
- printk(KERN_INFO "KingSun/DonShine IRDA/USB found at address %d, "
- "Vendor: %x, Product: %x\n",
- dev->devnum, le16_to_cpu(dev->descriptor.idVendor),
- le16_to_cpu(dev->descriptor.idProduct));
-
- /* Initialize QoS for this device */
- irda_init_max_qos_capabilies(&kingsun->qos);
-
- /* That's the Rx capability. */
- kingsun->qos.baud_rate.bits &= IR_9600;
- kingsun->qos.min_turn_time.bits &= KINGSUN_MTT;
- irda_qos_bits_to_value(&kingsun->qos);
-
- /* Override the network functions we need to use */
- net->netdev_ops = &kingsun_ops;
-
- ret = register_netdev(net);
- if (ret != 0)
- goto free_mem;
-
- dev_info(&net->dev, "IrDA: Registered KingSun/DonShine device %s\n",
- net->name);
-
- usb_set_intfdata(intf, kingsun);
-
- /* Situation at this point:
- - all work buffers allocated
- - urbs not allocated, set to NULL
- - max rx packet known (in max_rx)
- - unwrap state machine (partially) initialized, but skb == NULL
- */
-
- return 0;
-
-free_mem:
- kfree(kingsun->out_buf);
- kfree(kingsun->in_buf);
- free_netdev(net);
-err_out1:
- return ret;
-}
-
-/*
- * The current device is removed, the USB layer tell us to shut it down...
- */
-static void kingsun_disconnect(struct usb_interface *intf)
-{
- struct kingsun_cb *kingsun = usb_get_intfdata(intf);
-
- if (!kingsun)
- return;
-
- unregister_netdev(kingsun->netdev);
-
- /* Mop up receive && transmit urb's */
- if (kingsun->tx_urb != NULL) {
- usb_kill_urb(kingsun->tx_urb);
- usb_free_urb(kingsun->tx_urb);
- kingsun->tx_urb = NULL;
- }
- if (kingsun->rx_urb != NULL) {
- usb_kill_urb(kingsun->rx_urb);
- usb_free_urb(kingsun->rx_urb);
- kingsun->rx_urb = NULL;
- }
-
- kfree(kingsun->out_buf);
- kfree(kingsun->in_buf);
- free_netdev(kingsun->netdev);
-
- usb_set_intfdata(intf, NULL);
-}
-
-#ifdef CONFIG_PM
-/* USB suspend, so power off the transmitter/receiver */
-static int kingsun_suspend(struct usb_interface *intf, pm_message_t message)
-{
- struct kingsun_cb *kingsun = usb_get_intfdata(intf);
-
- netif_device_detach(kingsun->netdev);
- if (kingsun->tx_urb != NULL) usb_kill_urb(kingsun->tx_urb);
- if (kingsun->rx_urb != NULL) usb_kill_urb(kingsun->rx_urb);
- return 0;
-}
-
-/* Coming out of suspend, so reset hardware */
-static int kingsun_resume(struct usb_interface *intf)
-{
- struct kingsun_cb *kingsun = usb_get_intfdata(intf);
-
- if (kingsun->rx_urb != NULL)
- usb_submit_urb(kingsun->rx_urb, GFP_KERNEL);
- netif_device_attach(kingsun->netdev);
-
- return 0;
-}
-#endif
-
-/*
- * USB device callbacks
- */
-static struct usb_driver irda_driver = {
- .name = "kingsun-sir",
- .probe = kingsun_probe,
- .disconnect = kingsun_disconnect,
- .id_table = dongles,
-#ifdef CONFIG_PM
- .suspend = kingsun_suspend,
- .resume = kingsun_resume,
-#endif
-};
-
-module_usb_driver(irda_driver);
-
-MODULE_AUTHOR("Alex Villacís Lasso <a_villacis@palosanto.com>");
-MODULE_DESCRIPTION("IrDA-USB Dongle Driver for KingSun/DonShine");
-MODULE_LICENSE("GPL");
diff --git a/drivers/staging/irda/drivers/ks959-sir.c b/drivers/staging/irda/drivers/ks959-sir.c
deleted file mode 100644
index 8025741e7586..000000000000
--- a/drivers/staging/irda/drivers/ks959-sir.c
+++ /dev/null
@@ -1,912 +0,0 @@
-/*****************************************************************************
-*
-* Filename: ks959-sir.c
-* Version: 0.1.2
-* Description: Irda KingSun KS-959 USB Dongle
-* Status: Experimental
-* Author: Alex Villacís Lasso <a_villacis@palosanto.com>
-* with help from Domen Puncer <domen@coderock.org>
-*
-* Based on stir4200, mcs7780, kingsun-sir 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.
-*
-* 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.
-*
-*****************************************************************************/
-
-/*
- * Following is my most current (2007-07-17) understanding of how the Kingsun
- * KS-959 dongle is supposed to work. This information was deduced by
- * reverse-engineering and examining the USB traffic captured with USBSnoopy
- * from the WinXP driver. Feel free to update here as more of the dongle is
- * known.
- *
- * My most sincere thanks must go to Domen Puncer <domen@coderock.org> for
- * invaluable help in cracking the obfuscation and padding required for this
- * dongle.
- *
- * General: This dongle exposes one interface with one interrupt IN endpoint.
- * However, the interrupt endpoint is NOT used at all for this dongle. Instead,
- * this dongle uses control transfers for everything, including sending and
- * receiving the IrDA frame data. Apparently the interrupt endpoint is just a
- * dummy to ensure the dongle has a valid interface to present to the PC.And I
- * thought the DonShine dongle was weird... In addition, this dongle uses
- * obfuscation (?!?!), applied at the USB level, to hide the traffic, both sent
- * and received, from the dongle. I call it obfuscation because the XOR keying
- * and padding required to produce an USB traffic acceptable for the dongle can
- * not be explained by any other technical requirement.
- *
- * Transmission: To transmit an IrDA frame, the driver must prepare a control
- * URB with the following as a setup packet:
- * bRequestType USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE
- * bRequest 0x09
- * wValue <length of valid data before padding, little endian>
- * wIndex 0x0000
- * wLength <length of padded data>
- * The payload packet must be manually wrapped and escaped (as in stir4200.c),
- * then padded and obfuscated before being sent. Both padding and obfuscation
- * are implemented in the procedure obfuscate_tx_buffer(). Suffice to say, the
- * designer/programmer of the dongle used his name as a source for the
- * obfuscation. WTF?!
- * Apparently the dongle cannot handle payloads larger than 256 bytes. The
- * driver has to perform fragmentation in order to send anything larger than
- * this limit.
- *
- * Reception: To receive data, the driver must poll the dongle regularly (like
- * kingsun-sir.c) with control URBs and the following as a setup packet:
- * bRequestType USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE
- * bRequest 0x01
- * wValue 0x0200
- * wIndex 0x0000
- * wLength 0x0800 (size of available buffer)
- * If there is data to be read, it will be returned as the response payload.
- * This data is (apparently) not padded, but it is obfuscated. To de-obfuscate
- * it, the driver must XOR every byte, in sequence, with a value that starts at
- * 1 and is incremented with each byte processed, and then with 0x55. The value
- * incremented with each byte processed overflows as an unsigned char. The
- * resulting bytes form a wrapped SIR frame that is unwrapped and unescaped
- * as in stir4200.c The incremented value is NOT reset with each frame, but is
- * kept across the entire session with the dongle. Also, the dongle inserts an
- * extra garbage byte with value 0x95 (after decoding) every 0xff bytes, which
- * must be skipped.
- *
- * Speed change: To change the speed of the dongle, the driver prepares a
- * control URB with the following as a setup packet:
- * bRequestType USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE
- * bRequest 0x09
- * wValue 0x0200
- * wIndex 0x0001
- * wLength 0x0008 (length of the payload)
- * The payload is a 8-byte record, apparently identical to the one used in
- * drivers/usb/serial/cypress_m8.c to change speed:
- * __u32 baudSpeed;
- * unsigned int dataBits : 2; // 0 - 5 bits 3 - 8 bits
- * unsigned int : 1;
- * unsigned int stopBits : 1;
- * unsigned int parityEnable : 1;
- * unsigned int parityType : 1;
- * unsigned int : 1;
- * unsigned int reset : 1;
- * unsigned char reserved[3]; // set to 0
- *
- * For now only SIR speeds have been observed with this dongle. Therefore,
- * nothing is known on what changes (if any) must be done to frame wrapping /
- * unwrapping for higher than SIR speeds. This driver assumes no change is
- * necessary and announces support for all the way to 57600 bps. Although the
- * package announces support for up to 4MBps, tests with a Sony Ericcson K300
- * phone show corruption when receiving large frames at 115200 bps, the highest
- * speed announced by the phone. However, transmission at 115200 bps is OK. Go
- * figure. Since I don't know whether the phone or the dongle is at fault, max
- * announced speed is 57600 bps until someone produces a device that can run
- * at higher speeds with this dongle.
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/usb.h>
-#include <linux/device.h>
-#include <linux/crc32.h>
-
-#include <asm/unaligned.h>
-#include <asm/byteorder.h>
-#include <linux/uaccess.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/wrapper.h>
-#include <net/irda/crc.h>
-
-#define KS959_VENDOR_ID 0x07d0
-#define KS959_PRODUCT_ID 0x4959
-
-/* These are the currently known USB ids */
-static const struct usb_device_id dongles[] = {
- /* KingSun Co,Ltd IrDA/USB Bridge */
- {USB_DEVICE(KS959_VENDOR_ID, KS959_PRODUCT_ID)},
- {}
-};
-
-MODULE_DEVICE_TABLE(usb, dongles);
-
-#define KINGSUN_MTT 0x07
-#define KINGSUN_REQ_RECV 0x01
-#define KINGSUN_REQ_SEND 0x09
-
-#define KINGSUN_RCV_FIFO_SIZE 2048 /* Max length we can receive */
-#define KINGSUN_SND_FIFO_SIZE 2048 /* Max packet we can send */
-#define KINGSUN_SND_PACKET_SIZE 256 /* Max packet dongle can handle */
-
-struct ks959_speedparams {
- __le32 baudrate; /* baud rate, little endian */
- __u8 flags;
- __u8 reserved[3];
-} __packed;
-
-#define KS_DATA_5_BITS 0x00
-#define KS_DATA_6_BITS 0x01
-#define KS_DATA_7_BITS 0x02
-#define KS_DATA_8_BITS 0x03
-
-#define KS_STOP_BITS_1 0x00
-#define KS_STOP_BITS_2 0x08
-
-#define KS_PAR_DISABLE 0x00
-#define KS_PAR_EVEN 0x10
-#define KS_PAR_ODD 0x30
-#define KS_RESET 0x80
-
-struct ks959_cb {
- struct usb_device *usbdev; /* init: probe_irda */
- struct net_device *netdev; /* network layer */
- struct irlap_cb *irlap; /* The link layer we are binded to */
-
- struct qos_info qos;
-
- struct usb_ctrlrequest *tx_setuprequest;
- struct urb *tx_urb;
- __u8 *tx_buf_clear;
- unsigned int tx_buf_clear_used;
- unsigned int tx_buf_clear_sent;
- __u8 *tx_buf_xored;
-
- struct usb_ctrlrequest *rx_setuprequest;
- struct urb *rx_urb;
- __u8 *rx_buf;
- __u8 rx_variable_xormask;
- iobuff_t rx_unwrap_buff;
-
- struct usb_ctrlrequest *speed_setuprequest;
- struct urb *speed_urb;
- struct ks959_speedparams speedparams;
- unsigned int new_speed;
-
- spinlock_t lock;
- int receiving;
-};
-
-/* Procedure to perform the obfuscation/padding expected by the dongle
- *
- * buf_cleartext (IN) Cleartext version of the IrDA frame to transmit
- * len_cleartext (IN) Length of the cleartext version of IrDA frame
- * buf_xoredtext (OUT) Obfuscated version of frame built by proc
- * len_maxbuf (OUT) Maximum space available at buf_xoredtext
- *
- * (return) length of obfuscated frame with padding
- *
- * If not enough space (as indicated by len_maxbuf vs. required padding),
- * zero is returned
- *
- * The value of lookup_string is actually a required portion of the algorithm.
- * Seems the designer of the dongle wanted to state who exactly is responsible
- * for implementing obfuscation. Send your best (or other) wishes to him ]:-)
- */
-static unsigned int obfuscate_tx_buffer(const __u8 * buf_cleartext,
- unsigned int len_cleartext,
- __u8 * buf_xoredtext,
- unsigned int len_maxbuf)
-{
- unsigned int len_xoredtext;
-
- /* Calculate required length with padding, check for necessary space */
- len_xoredtext = ((len_cleartext + 7) & ~0x7) + 0x10;
- if (len_xoredtext <= len_maxbuf) {
- static const __u8 lookup_string[] = "wangshuofei19710";
- __u8 xor_mask;
-
- /* Unlike the WinXP driver, we *do* clear out the padding */
- memset(buf_xoredtext, 0, len_xoredtext);
-
- xor_mask = lookup_string[(len_cleartext & 0x0f) ^ 0x06] ^ 0x55;
-
- while (len_cleartext-- > 0) {
- *buf_xoredtext++ = *buf_cleartext++ ^ xor_mask;
- }
- } else {
- len_xoredtext = 0;
- }
- return len_xoredtext;
-}
-
-/* Callback transmission routine */
-static void ks959_speed_irq(struct urb *urb)
-{
- /* unlink, shutdown, unplug, other nasties */
- if (urb->status != 0) {
- dev_err(&urb->dev->dev,
- "ks959_speed_irq: urb asynchronously failed - %d\n",
- urb->status);
- }
-}
-
-/* Send a control request to change speed of the dongle */
-static int ks959_change_speed(struct ks959_cb *kingsun, unsigned speed)
-{
- static unsigned int supported_speeds[] = { 2400, 9600, 19200, 38400,
- 57600, 115200, 576000, 1152000, 4000000, 0
- };
- int err;
- unsigned int i;
-
- if (kingsun->speed_setuprequest == NULL || kingsun->speed_urb == NULL)
- return -ENOMEM;
-
- /* Check that requested speed is among the supported ones */
- for (i = 0; supported_speeds[i] && supported_speeds[i] != speed; i++) ;
- if (supported_speeds[i] == 0)
- return -EOPNOTSUPP;
-
- memset(&(kingsun->speedparams), 0, sizeof(struct ks959_speedparams));
- kingsun->speedparams.baudrate = cpu_to_le32(speed);
- kingsun->speedparams.flags = KS_DATA_8_BITS;
-
- /* speed_setuprequest pre-filled in ks959_probe */
- usb_fill_control_urb(kingsun->speed_urb, kingsun->usbdev,
- usb_sndctrlpipe(kingsun->usbdev, 0),
- (unsigned char *)kingsun->speed_setuprequest,
- &(kingsun->speedparams),
- sizeof(struct ks959_speedparams), ks959_speed_irq,
- kingsun);
- kingsun->speed_urb->status = 0;
- err = usb_submit_urb(kingsun->speed_urb, GFP_ATOMIC);
-
- return err;
-}
-
-/* Submit one fragment of an IrDA frame to the dongle */
-static void ks959_send_irq(struct urb *urb);
-static int ks959_submit_tx_fragment(struct ks959_cb *kingsun)
-{
- unsigned int padlen;
- unsigned int wraplen;
- int ret;
-
- /* Check whether current plaintext can produce a padded buffer that fits
- within the range handled by the dongle */
- wraplen = (KINGSUN_SND_PACKET_SIZE & ~0x7) - 0x10;
- if (wraplen > kingsun->tx_buf_clear_used)
- wraplen = kingsun->tx_buf_clear_used;
-
- /* Perform dongle obfuscation. Also remove the portion of the frame that
- was just obfuscated and will now be sent to the dongle. */
- padlen = obfuscate_tx_buffer(kingsun->tx_buf_clear, wraplen,
- kingsun->tx_buf_xored,
- KINGSUN_SND_PACKET_SIZE);
-
- /* Calculate how much data can be transmitted in this urb */
- kingsun->tx_setuprequest->wValue = cpu_to_le16(wraplen);
- kingsun->tx_setuprequest->wLength = cpu_to_le16(padlen);
- /* Rest of the fields were filled in ks959_probe */
- usb_fill_control_urb(kingsun->tx_urb, kingsun->usbdev,
- usb_sndctrlpipe(kingsun->usbdev, 0),
- (unsigned char *)kingsun->tx_setuprequest,
- kingsun->tx_buf_xored, padlen,
- ks959_send_irq, kingsun);
- kingsun->tx_urb->status = 0;
- ret = usb_submit_urb(kingsun->tx_urb, GFP_ATOMIC);
-
- /* Remember how much data was sent, in order to update at callback */
- kingsun->tx_buf_clear_sent = (ret == 0) ? wraplen : 0;
- return ret;
-}
-
-/* Callback transmission routine */
-static void ks959_send_irq(struct urb *urb)
-{
- struct ks959_cb *kingsun = urb->context;
- struct net_device *netdev = kingsun->netdev;
- int ret = 0;
-
- /* in process of stopping, just drop data */
- if (!netif_running(kingsun->netdev)) {
- dev_err(&kingsun->usbdev->dev,
- "ks959_send_irq: Network not running!\n");
- return;
- }
-
- /* unlink, shutdown, unplug, other nasties */
- if (urb->status != 0) {
- dev_err(&kingsun->usbdev->dev,
- "ks959_send_irq: urb asynchronously failed - %d\n",
- urb->status);
- return;
- }
-
- if (kingsun->tx_buf_clear_used > 0) {
- /* Update data remaining to be sent */
- if (kingsun->tx_buf_clear_sent < kingsun->tx_buf_clear_used) {
- memmove(kingsun->tx_buf_clear,
- kingsun->tx_buf_clear +
- kingsun->tx_buf_clear_sent,
- kingsun->tx_buf_clear_used -
- kingsun->tx_buf_clear_sent);
- }
- kingsun->tx_buf_clear_used -= kingsun->tx_buf_clear_sent;
- kingsun->tx_buf_clear_sent = 0;
-
- if (kingsun->tx_buf_clear_used > 0) {
- /* There is more data to be sent */
- if ((ret = ks959_submit_tx_fragment(kingsun)) != 0) {
- dev_err(&kingsun->usbdev->dev,
- "ks959_send_irq: failed tx_urb submit: %d\n",
- ret);
- switch (ret) {
- case -ENODEV:
- case -EPIPE:
- break;
- default:
- netdev->stats.tx_errors++;
- netif_start_queue(netdev);
- }
- }
- } else {
- /* All data sent, send next speed && wake network queue */
- if (kingsun->new_speed != -1 &&
- cpu_to_le32(kingsun->new_speed) !=
- kingsun->speedparams.baudrate)
- ks959_change_speed(kingsun, kingsun->new_speed);
-
- netif_wake_queue(netdev);
- }
- }
-}
-
-/*
- * Called from net/core when new frame is available.
- */
-static netdev_tx_t ks959_hard_xmit(struct sk_buff *skb,
- struct net_device *netdev)
-{
- struct ks959_cb *kingsun;
- unsigned int wraplen;
- int ret = 0;
-
- netif_stop_queue(netdev);
-
- /* the IRDA wrapping routines don't deal with non linear skb */
- SKB_LINEAR_ASSERT(skb);
-
- kingsun = netdev_priv(netdev);
-
- spin_lock(&kingsun->lock);
- kingsun->new_speed = irda_get_next_speed(skb);
-
- /* Append data to the end of whatever data remains to be transmitted */
- wraplen =
- async_wrap_skb(skb, kingsun->tx_buf_clear, KINGSUN_SND_FIFO_SIZE);
- kingsun->tx_buf_clear_used = wraplen;
-
- if ((ret = ks959_submit_tx_fragment(kingsun)) != 0) {
- dev_err(&kingsun->usbdev->dev,
- "ks959_hard_xmit: failed tx_urb submit: %d\n", ret);
- switch (ret) {
- case -ENODEV:
- case -EPIPE:
- break;
- default:
- netdev->stats.tx_errors++;
- netif_start_queue(netdev);
- }
- } else {
- netdev->stats.tx_packets++;
- netdev->stats.tx_bytes += skb->len;
-
- }
-
- dev_kfree_skb(skb);
- spin_unlock(&kingsun->lock);
-
- return NETDEV_TX_OK;
-}
-
-/* Receive callback function */
-static void ks959_rcv_irq(struct urb *urb)
-{
- struct ks959_cb *kingsun = urb->context;
- int ret;
-
- /* in process of stopping, just drop data */
- if (!netif_running(kingsun->netdev)) {
- kingsun->receiving = 0;
- return;
- }
-
- /* unlink, shutdown, unplug, other nasties */
- if (urb->status != 0) {
- dev_err(&kingsun->usbdev->dev,
- "kingsun_rcv_irq: urb asynchronously failed - %d\n",
- urb->status);
- kingsun->receiving = 0;
- return;
- }
-
- if (urb->actual_length > 0) {
- __u8 *bytes = urb->transfer_buffer;
- unsigned int i;
-
- for (i = 0; i < urb->actual_length; i++) {
- /* De-obfuscation implemented here: variable portion of
- xormask is incremented, and then used with the encoded
- byte for the XOR. The result of the operation is used
- to unwrap the SIR frame. */
- kingsun->rx_variable_xormask++;
- bytes[i] =
- bytes[i] ^ kingsun->rx_variable_xormask ^ 0x55u;
-
- /* rx_variable_xormask doubles as an index counter so we
- can skip the byte at 0xff (wrapped around to 0).
- */
- if (kingsun->rx_variable_xormask != 0) {
- async_unwrap_char(kingsun->netdev,
- &kingsun->netdev->stats,
- &kingsun->rx_unwrap_buff,
- bytes[i]);
- }
- }
- kingsun->receiving =
- (kingsun->rx_unwrap_buff.state != OUTSIDE_FRAME) ? 1 : 0;
- }
-
- /* This urb has already been filled in kingsun_net_open. Setup
- packet must be re-filled, but it is assumed that urb keeps the
- pointer to the initial setup packet, as well as the payload buffer.
- Setup packet is already pre-filled at ks959_probe.
- */
- urb->status = 0;
- ret = usb_submit_urb(urb, GFP_ATOMIC);
-}
-
-/*
- * Function kingsun_net_open (dev)
- *
- * Network device is taken up. Usually this is done by "ifconfig irda0 up"
- */
-static int ks959_net_open(struct net_device *netdev)
-{
- struct ks959_cb *kingsun = netdev_priv(netdev);
- int err = -ENOMEM;
- char hwname[16];
-
- /* At this point, urbs are NULL, and skb is NULL (see kingsun_probe) */
- kingsun->receiving = 0;
-
- /* Initialize for SIR to copy data directly into skb. */
- kingsun->rx_unwrap_buff.in_frame = FALSE;
- kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME;
- kingsun->rx_unwrap_buff.truesize = IRDA_SKB_MAX_MTU;
- kingsun->rx_unwrap_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU);
- if (!kingsun->rx_unwrap_buff.skb)
- goto free_mem;
-
- skb_reserve(kingsun->rx_unwrap_buff.skb, 1);
- kingsun->rx_unwrap_buff.head = kingsun->rx_unwrap_buff.skb->data;
-
- kingsun->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!kingsun->rx_urb)
- goto free_mem;
-
- kingsun->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!kingsun->tx_urb)
- goto free_mem;
-
- kingsun->speed_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!kingsun->speed_urb)
- goto free_mem;
-
- /* Initialize speed for dongle */
- kingsun->new_speed = 9600;
- err = ks959_change_speed(kingsun, 9600);
- if (err < 0)
- goto free_mem;
-
- /*
- * Now that everything should be initialized properly,
- * Open new IrLAP layer instance to take care of us...
- */
- sprintf(hwname, "usb#%d", kingsun->usbdev->devnum);
- kingsun->irlap = irlap_open(netdev, &kingsun->qos, hwname);
- if (!kingsun->irlap) {
- err = -ENOMEM;
- dev_err(&kingsun->usbdev->dev, "irlap_open failed\n");
- goto free_mem;
- }
-
- /* Start reception. Setup request already pre-filled in ks959_probe */
- usb_fill_control_urb(kingsun->rx_urb, kingsun->usbdev,
- usb_rcvctrlpipe(kingsun->usbdev, 0),
- (unsigned char *)kingsun->rx_setuprequest,
- kingsun->rx_buf, KINGSUN_RCV_FIFO_SIZE,
- ks959_rcv_irq, kingsun);
- kingsun->rx_urb->status = 0;
- err = usb_submit_urb(kingsun->rx_urb, GFP_KERNEL);
- if (err) {
- dev_err(&kingsun->usbdev->dev,
- "first urb-submit failed: %d\n", err);
- goto close_irlap;
- }
-
- netif_start_queue(netdev);
-
- /* Situation at this point:
- - all work buffers allocated
- - urbs allocated and ready to fill
- - max rx packet known (in max_rx)
- - unwrap state machine initialized, in state outside of any frame
- - receive request in progress
- - IrLAP layer started, about to hand over packets to send
- */
-
- return 0;
-
- close_irlap:
- irlap_close(kingsun->irlap);
- free_mem:
- usb_free_urb(kingsun->speed_urb);
- kingsun->speed_urb = NULL;
- usb_free_urb(kingsun->tx_urb);
- kingsun->tx_urb = NULL;
- usb_free_urb(kingsun->rx_urb);
- kingsun->rx_urb = NULL;
- if (kingsun->rx_unwrap_buff.skb) {
- kfree_skb(kingsun->rx_unwrap_buff.skb);
- kingsun->rx_unwrap_buff.skb = NULL;
- kingsun->rx_unwrap_buff.head = NULL;
- }
- return err;
-}
-
-/*
- * Function kingsun_net_close (kingsun)
- *
- * Network device is taken down. Usually this is done by
- * "ifconfig irda0 down"
- */
-static int ks959_net_close(struct net_device *netdev)
-{
- struct ks959_cb *kingsun = netdev_priv(netdev);
-
- /* Stop transmit processing */
- netif_stop_queue(netdev);
-
- /* Mop up receive && transmit urb's */
- usb_kill_urb(kingsun->tx_urb);
- usb_free_urb(kingsun->tx_urb);
- kingsun->tx_urb = NULL;
-
- usb_kill_urb(kingsun->speed_urb);
- usb_free_urb(kingsun->speed_urb);
- kingsun->speed_urb = NULL;
-
- usb_kill_urb(kingsun->rx_urb);
- usb_free_urb(kingsun->rx_urb);
- kingsun->rx_urb = NULL;
-
- kfree_skb(kingsun->rx_unwrap_buff.skb);
- kingsun->rx_unwrap_buff.skb = NULL;
- kingsun->rx_unwrap_buff.head = NULL;
- kingsun->rx_unwrap_buff.in_frame = FALSE;
- kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME;
- kingsun->receiving = 0;
-
- /* Stop and remove instance of IrLAP */
- if (kingsun->irlap)
- irlap_close(kingsun->irlap);
-
- kingsun->irlap = NULL;
-
- return 0;
-}
-
-/*
- * IOCTLs : Extra out-of-band network commands...
- */
-static int ks959_net_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
-{
- struct if_irda_req *irq = (struct if_irda_req *)rq;
- struct ks959_cb *kingsun = netdev_priv(netdev);
- int ret = 0;
-
- switch (cmd) {
- case SIOCSBANDWIDTH: /* Set bandwidth */
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
-
- /* Check if the device is still there */
- if (netif_device_present(kingsun->netdev))
- return ks959_change_speed(kingsun, irq->ifr_baudrate);
- break;
-
- case SIOCSMEDIABUSY: /* Set media busy */
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
-
- /* Check if the IrDA stack is still there */
- if (netif_running(kingsun->netdev))
- irda_device_set_media_busy(kingsun->netdev, TRUE);
- break;
-
- case SIOCGRECEIVING:
- /* Only approximately true */
- irq->ifr_receiving = kingsun->receiving;
- break;
-
- default:
- ret = -EOPNOTSUPP;
- }
-
- return ret;
-}
-
-static const struct net_device_ops ks959_ops = {
- .ndo_start_xmit = ks959_hard_xmit,
- .ndo_open = ks959_net_open,
- .ndo_stop = ks959_net_close,
- .ndo_do_ioctl = ks959_net_ioctl,
-};
-/*
- * This routine is called by the USB subsystem for each new device
- * in the system. We need to check if the device is ours, and in
- * this case start handling it.
- */
-static int ks959_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- struct usb_device *dev = interface_to_usbdev(intf);
- struct ks959_cb *kingsun = NULL;
- struct net_device *net = NULL;
- int ret = -ENOMEM;
-
- /* Allocate network device container. */
- net = alloc_irdadev(sizeof(*kingsun));
- if (!net)
- goto err_out1;
-
- SET_NETDEV_DEV(net, &intf->dev);
- kingsun = netdev_priv(net);
- kingsun->netdev = net;
- kingsun->usbdev = dev;
- kingsun->irlap = NULL;
- kingsun->tx_setuprequest = NULL;
- kingsun->tx_urb = NULL;
- kingsun->tx_buf_clear = NULL;
- kingsun->tx_buf_xored = NULL;
- kingsun->tx_buf_clear_used = 0;
- kingsun->tx_buf_clear_sent = 0;
-
- kingsun->rx_setuprequest = NULL;
- kingsun->rx_urb = NULL;
- kingsun->rx_buf = NULL;
- kingsun->rx_variable_xormask = 0;
- kingsun->rx_unwrap_buff.in_frame = FALSE;
- kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME;
- kingsun->rx_unwrap_buff.skb = NULL;
- kingsun->receiving = 0;
- spin_lock_init(&kingsun->lock);
-
- kingsun->speed_setuprequest = NULL;
- kingsun->speed_urb = NULL;
- kingsun->speedparams.baudrate = 0;
-
- /* Allocate input buffer */
- kingsun->rx_buf = kmalloc(KINGSUN_RCV_FIFO_SIZE, GFP_KERNEL);
- if (!kingsun->rx_buf)
- goto free_mem;
-
- /* Allocate input setup packet */
- kingsun->rx_setuprequest =
- kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
- if (!kingsun->rx_setuprequest)
- goto free_mem;
- kingsun->rx_setuprequest->bRequestType =
- USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
- kingsun->rx_setuprequest->bRequest = KINGSUN_REQ_RECV;
- kingsun->rx_setuprequest->wValue = cpu_to_le16(0x0200);
- kingsun->rx_setuprequest->wIndex = 0;
- kingsun->rx_setuprequest->wLength = cpu_to_le16(KINGSUN_RCV_FIFO_SIZE);
-
- /* Allocate output buffer */
- kingsun->tx_buf_clear = kmalloc(KINGSUN_SND_FIFO_SIZE, GFP_KERNEL);
- if (!kingsun->tx_buf_clear)
- goto free_mem;
- kingsun->tx_buf_xored = kmalloc(KINGSUN_SND_PACKET_SIZE, GFP_KERNEL);
- if (!kingsun->tx_buf_xored)
- goto free_mem;
-
- /* Allocate and initialize output setup packet */
- kingsun->tx_setuprequest =
- kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
- if (!kingsun->tx_setuprequest)
- goto free_mem;
- kingsun->tx_setuprequest->bRequestType =
- USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
- kingsun->tx_setuprequest->bRequest = KINGSUN_REQ_SEND;
- kingsun->tx_setuprequest->wValue = 0;
- kingsun->tx_setuprequest->wIndex = 0;
- kingsun->tx_setuprequest->wLength = 0;
-
- /* Allocate and initialize speed setup packet */
- kingsun->speed_setuprequest =
- kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
- if (!kingsun->speed_setuprequest)
- goto free_mem;
- kingsun->speed_setuprequest->bRequestType =
- USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
- kingsun->speed_setuprequest->bRequest = KINGSUN_REQ_SEND;
- kingsun->speed_setuprequest->wValue = cpu_to_le16(0x0200);
- kingsun->speed_setuprequest->wIndex = cpu_to_le16(0x0001);
- kingsun->speed_setuprequest->wLength =
- cpu_to_le16(sizeof(struct ks959_speedparams));
-
- printk(KERN_INFO "KingSun KS-959 IRDA/USB found at address %d, "
- "Vendor: %x, Product: %x\n",
- dev->devnum, le16_to_cpu(dev->descriptor.idVendor),
- le16_to_cpu(dev->descriptor.idProduct));
-
- /* Initialize QoS for this device */
- irda_init_max_qos_capabilies(&kingsun->qos);
-
- /* Baud rates known to be supported. Please uncomment if devices (other
- than a SonyEriccson K300 phone) can be shown to support higher speed
- with this dongle.
- */
- kingsun->qos.baud_rate.bits =
- IR_2400 | IR_9600 | IR_19200 | IR_38400 | IR_57600;
- kingsun->qos.min_turn_time.bits &= KINGSUN_MTT;
- irda_qos_bits_to_value(&kingsun->qos);
-
- /* Override the network functions we need to use */
- net->netdev_ops = &ks959_ops;
-
- ret = register_netdev(net);
- if (ret != 0)
- goto free_mem;
-
- dev_info(&net->dev, "IrDA: Registered KingSun KS-959 device %s\n",
- net->name);
-
- usb_set_intfdata(intf, kingsun);
-
- /* Situation at this point:
- - all work buffers allocated
- - setup requests pre-filled
- - urbs not allocated, set to NULL
- - max rx packet known (is KINGSUN_FIFO_SIZE)
- - unwrap state machine (partially) initialized, but skb == NULL
- */
-
- return 0;
-
- free_mem:
- kfree(kingsun->speed_setuprequest);
- kfree(kingsun->tx_setuprequest);
- kfree(kingsun->tx_buf_xored);
- kfree(kingsun->tx_buf_clear);
- kfree(kingsun->rx_setuprequest);
- kfree(kingsun->rx_buf);
- free_netdev(net);
- err_out1:
- return ret;
-}
-
-/*
- * The current device is removed, the USB layer tell us to shut it down...
- */
-static void ks959_disconnect(struct usb_interface *intf)
-{
- struct ks959_cb *kingsun = usb_get_intfdata(intf);
-
- if (!kingsun)
- return;
-
- unregister_netdev(kingsun->netdev);
-
- /* Mop up receive && transmit urb's */
- if (kingsun->speed_urb != NULL) {
- usb_kill_urb(kingsun->speed_urb);
- usb_free_urb(kingsun->speed_urb);
- kingsun->speed_urb = NULL;
- }
- if (kingsun->tx_urb != NULL) {
- usb_kill_urb(kingsun->tx_urb);
- usb_free_urb(kingsun->tx_urb);
- kingsun->tx_urb = NULL;
- }
- if (kingsun->rx_urb != NULL) {
- usb_kill_urb(kingsun->rx_urb);
- usb_free_urb(kingsun->rx_urb);
- kingsun->rx_urb = NULL;
- }
-
- kfree(kingsun->speed_setuprequest);
- kfree(kingsun->tx_setuprequest);
- kfree(kingsun->tx_buf_xored);
- kfree(kingsun->tx_buf_clear);
- kfree(kingsun->rx_setuprequest);
- kfree(kingsun->rx_buf);
- free_netdev(kingsun->netdev);
-
- usb_set_intfdata(intf, NULL);
-}
-
-#ifdef CONFIG_PM
-/* USB suspend, so power off the transmitter/receiver */
-static int ks959_suspend(struct usb_interface *intf, pm_message_t message)
-{
- struct ks959_cb *kingsun = usb_get_intfdata(intf);
-
- netif_device_detach(kingsun->netdev);
- if (kingsun->speed_urb != NULL)
- usb_kill_urb(kingsun->speed_urb);
- if (kingsun->tx_urb != NULL)
- usb_kill_urb(kingsun->tx_urb);
- if (kingsun->rx_urb != NULL)
- usb_kill_urb(kingsun->rx_urb);
- return 0;
-}
-
-/* Coming out of suspend, so reset hardware */
-static int ks959_resume(struct usb_interface *intf)
-{
- struct ks959_cb *kingsun = usb_get_intfdata(intf);
-
- if (kingsun->rx_urb != NULL) {
- /* Setup request already filled in ks959_probe */
- usb_submit_urb(kingsun->rx_urb, GFP_KERNEL);
- }
- netif_device_attach(kingsun->netdev);
-
- return 0;
-}
-#endif
-
-/*
- * USB device callbacks
- */
-static struct usb_driver irda_driver = {
- .name = "ks959-sir",
- .probe = ks959_probe,
- .disconnect = ks959_disconnect,
- .id_table = dongles,
-#ifdef CONFIG_PM
- .suspend = ks959_suspend,
- .resume = ks959_resume,
-#endif
-};
-
-module_usb_driver(irda_driver);
-
-MODULE_AUTHOR("Alex Villacís Lasso <a_villacis@palosanto.com>");
-MODULE_DESCRIPTION("IrDA-USB Dongle Driver for KingSun KS-959");
-MODULE_LICENSE("GPL");
diff --git a/drivers/staging/irda/drivers/ksdazzle-sir.c b/drivers/staging/irda/drivers/ksdazzle-sir.c
deleted file mode 100644
index d2a0755df596..000000000000
--- a/drivers/staging/irda/drivers/ksdazzle-sir.c
+++ /dev/null
@@ -1,813 +0,0 @@
-/*****************************************************************************
-*
-* Filename: ksdazzle.c
-* Version: 0.1.2
-* Description: Irda KingSun Dazzle USB Dongle
-* Status: Experimental
-* Author: Alex Villacís Lasso <a_villacis@palosanto.com>
-*
-* Based on stir4200, mcs7780, kingsun-sir 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.
-*
-* 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.
-*
-*****************************************************************************/
-
-/*
- * Following is my most current (2007-07-26) understanding of how the Kingsun
- * 07D0:4100 dongle (sometimes known as the MA-660) is supposed to work. This
- * information was deduced by examining the USB traffic captured with USBSnoopy
- * from the WinXP driver. Feel free to update here as more of the dongle is
- * known.
- *
- * General: This dongle exposes one interface with two interrupt endpoints, one
- * IN and one OUT. In this regard, it is similar to what the Kingsun/Donshine
- * dongle (07c0:4200) exposes. Traffic is raw and needs to be wrapped and
- * unwrapped manually as in stir4200, kingsun-sir, and ks959-sir.
- *
- * Transmission: To transmit an IrDA frame, it is necessary to wrap it, then
- * split it into multiple segments of up to 7 bytes each, and transmit each in
- * sequence. It seems that sending a single big block (like kingsun-sir does)
- * won't work with this dongle. Each segment needs to be prefixed with a value
- * equal to (unsigned char)0xF8 + <number of bytes in segment>, inside a payload
- * of exactly 8 bytes. For example, a segment of 1 byte gets prefixed by 0xF9,
- * and one of 7 bytes gets prefixed by 0xFF. The bytes at the end of the
- * payload, not considered by the prefix, are ignored (set to 0 by this
- * implementation).
- *
- * Reception: To receive data, the driver must poll the dongle regularly (like
- * kingsun-sir.c) with interrupt URBs. If data is available, it will be returned
- * in payloads from 0 to 8 bytes long. When concatenated, these payloads form
- * a raw IrDA stream that needs to be unwrapped as in stir4200 and kingsun-sir
- *
- * Speed change: To change the speed of the dongle, the driver prepares a
- * control URB with the following as a setup packet:
- * bRequestType USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE
- * bRequest 0x09
- * wValue 0x0200
- * wIndex 0x0001
- * wLength 0x0008 (length of the payload)
- * The payload is a 8-byte record, apparently identical to the one used in
- * drivers/usb/serial/cypress_m8.c to change speed:
- * __u32 baudSpeed;
- * unsigned int dataBits : 2; // 0 - 5 bits 3 - 8 bits
- * unsigned int : 1;
- * unsigned int stopBits : 1;
- * unsigned int parityEnable : 1;
- * unsigned int parityType : 1;
- * unsigned int : 1;
- * unsigned int reset : 1;
- * unsigned char reserved[3]; // set to 0
- *
- * For now only SIR speeds have been observed with this dongle. Therefore,
- * nothing is known on what changes (if any) must be done to frame wrapping /
- * unwrapping for higher than SIR speeds. This driver assumes no change is
- * necessary and announces support for all the way to 115200 bps.
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/usb.h>
-#include <linux/device.h>
-#include <linux/crc32.h>
-
-#include <asm/unaligned.h>
-#include <asm/byteorder.h>
-#include <linux/uaccess.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/wrapper.h>
-#include <net/irda/crc.h>
-
-#define KSDAZZLE_VENDOR_ID 0x07d0
-#define KSDAZZLE_PRODUCT_ID 0x4100
-
-/* These are the currently known USB ids */
-static const struct usb_device_id dongles[] = {
- /* KingSun Co,Ltd IrDA/USB Bridge */
- {USB_DEVICE(KSDAZZLE_VENDOR_ID, KSDAZZLE_PRODUCT_ID)},
- {}
-};
-
-MODULE_DEVICE_TABLE(usb, dongles);
-
-#define KINGSUN_MTT 0x07
-#define KINGSUN_REQ_RECV 0x01
-#define KINGSUN_REQ_SEND 0x09
-
-#define KINGSUN_SND_FIFO_SIZE 2048 /* Max packet we can send */
-#define KINGSUN_RCV_MAX 2048 /* Max transfer we can receive */
-
-struct ksdazzle_speedparams {
- __le32 baudrate; /* baud rate, little endian */
- __u8 flags;
- __u8 reserved[3];
-} __packed;
-
-#define KS_DATA_5_BITS 0x00
-#define KS_DATA_6_BITS 0x01
-#define KS_DATA_7_BITS 0x02
-#define KS_DATA_8_BITS 0x03
-
-#define KS_STOP_BITS_1 0x00
-#define KS_STOP_BITS_2 0x08
-
-#define KS_PAR_DISABLE 0x00
-#define KS_PAR_EVEN 0x10
-#define KS_PAR_ODD 0x30
-#define KS_RESET 0x80
-
-#define KINGSUN_EP_IN 0
-#define KINGSUN_EP_OUT 1
-
-struct ksdazzle_cb {
- struct usb_device *usbdev; /* init: probe_irda */
- struct net_device *netdev; /* network layer */
- struct irlap_cb *irlap; /* The link layer we are binded to */
-
- struct qos_info qos;
-
- struct urb *tx_urb;
- __u8 *tx_buf_clear;
- unsigned int tx_buf_clear_used;
- unsigned int tx_buf_clear_sent;
- __u8 tx_payload[8];
-
- struct urb *rx_urb;
- __u8 *rx_buf;
- iobuff_t rx_unwrap_buff;
-
- struct usb_ctrlrequest *speed_setuprequest;
- struct urb *speed_urb;
- struct ksdazzle_speedparams speedparams;
- unsigned int new_speed;
-
- __u8 ep_in;
- __u8 ep_out;
-
- spinlock_t lock;
- int receiving;
-};
-
-/* Callback transmission routine */
-static void ksdazzle_speed_irq(struct urb *urb)
-{
- /* unlink, shutdown, unplug, other nasties */
- if (urb->status != 0)
- dev_err(&urb->dev->dev,
- "ksdazzle_speed_irq: urb asynchronously failed - %d\n",
- urb->status);
-}
-
-/* Send a control request to change speed of the dongle */
-static int ksdazzle_change_speed(struct ksdazzle_cb *kingsun, unsigned speed)
-{
- static unsigned int supported_speeds[] = { 2400, 9600, 19200, 38400,
- 57600, 115200, 576000, 1152000, 4000000, 0
- };
- int err;
- unsigned int i;
-
- if (kingsun->speed_setuprequest == NULL || kingsun->speed_urb == NULL)
- return -ENOMEM;
-
- /* Check that requested speed is among the supported ones */
- for (i = 0; supported_speeds[i] && supported_speeds[i] != speed; i++) ;
- if (supported_speeds[i] == 0)
- return -EOPNOTSUPP;
-
- memset(&(kingsun->speedparams), 0, sizeof(struct ksdazzle_speedparams));
- kingsun->speedparams.baudrate = cpu_to_le32(speed);
- kingsun->speedparams.flags = KS_DATA_8_BITS;
-
- /* speed_setuprequest pre-filled in ksdazzle_probe */
- usb_fill_control_urb(kingsun->speed_urb, kingsun->usbdev,
- usb_sndctrlpipe(kingsun->usbdev, 0),
- (unsigned char *)kingsun->speed_setuprequest,
- &(kingsun->speedparams),
- sizeof(struct ksdazzle_speedparams),
- ksdazzle_speed_irq, kingsun);
- kingsun->speed_urb->status = 0;
- err = usb_submit_urb(kingsun->speed_urb, GFP_ATOMIC);
-
- return err;
-}
-
-/* Submit one fragment of an IrDA frame to the dongle */
-static void ksdazzle_send_irq(struct urb *urb);
-static int ksdazzle_submit_tx_fragment(struct ksdazzle_cb *kingsun)
-{
- unsigned int wraplen;
- int ret;
-
- /* We can send at most 7 bytes of payload at a time */
- wraplen = 7;
- if (wraplen > kingsun->tx_buf_clear_used)
- wraplen = kingsun->tx_buf_clear_used;
-
- /* Prepare payload prefix with used length */
- memset(kingsun->tx_payload, 0, 8);
- kingsun->tx_payload[0] = (unsigned char)0xf8 + wraplen;
- memcpy(kingsun->tx_payload + 1, kingsun->tx_buf_clear, wraplen);
-
- usb_fill_int_urb(kingsun->tx_urb, kingsun->usbdev,
- usb_sndintpipe(kingsun->usbdev, kingsun->ep_out),
- kingsun->tx_payload, 8, ksdazzle_send_irq, kingsun, 1);
- kingsun->tx_urb->status = 0;
- ret = usb_submit_urb(kingsun->tx_urb, GFP_ATOMIC);
-
- /* Remember how much data was sent, in order to update at callback */
- kingsun->tx_buf_clear_sent = (ret == 0) ? wraplen : 0;
- return ret;
-}
-
-/* Callback transmission routine */
-static void ksdazzle_send_irq(struct urb *urb)
-{
- struct ksdazzle_cb *kingsun = urb->context;
- struct net_device *netdev = kingsun->netdev;
- int ret = 0;
-
- /* in process of stopping, just drop data */
- if (!netif_running(kingsun->netdev)) {
- dev_err(&kingsun->usbdev->dev,
- "ksdazzle_send_irq: Network not running!\n");
- return;
- }
-
- /* unlink, shutdown, unplug, other nasties */
- if (urb->status != 0) {
- dev_err(&kingsun->usbdev->dev,
- "ksdazzle_send_irq: urb asynchronously failed - %d\n",
- urb->status);
- return;
- }
-
- if (kingsun->tx_buf_clear_used > 0) {
- /* Update data remaining to be sent */
- if (kingsun->tx_buf_clear_sent < kingsun->tx_buf_clear_used) {
- memmove(kingsun->tx_buf_clear,
- kingsun->tx_buf_clear +
- kingsun->tx_buf_clear_sent,
- kingsun->tx_buf_clear_used -
- kingsun->tx_buf_clear_sent);
- }
- kingsun->tx_buf_clear_used -= kingsun->tx_buf_clear_sent;
- kingsun->tx_buf_clear_sent = 0;
-
- if (kingsun->tx_buf_clear_used > 0) {
- /* There is more data to be sent */
- if ((ret = ksdazzle_submit_tx_fragment(kingsun)) != 0) {
- dev_err(&kingsun->usbdev->dev,
- "ksdazzle_send_irq: failed tx_urb submit: %d\n",
- ret);
- switch (ret) {
- case -ENODEV:
- case -EPIPE:
- break;
- default:
- netdev->stats.tx_errors++;
- netif_start_queue(netdev);
- }
- }
- } else {
- /* All data sent, send next speed && wake network queue */
- if (kingsun->new_speed != -1 &&
- cpu_to_le32(kingsun->new_speed) !=
- kingsun->speedparams.baudrate)
- ksdazzle_change_speed(kingsun,
- kingsun->new_speed);
-
- netif_wake_queue(netdev);
- }
- }
-}
-
-/*
- * Called from net/core when new frame is available.
- */
-static netdev_tx_t ksdazzle_hard_xmit(struct sk_buff *skb,
- struct net_device *netdev)
-{
- struct ksdazzle_cb *kingsun;
- unsigned int wraplen;
- int ret = 0;
-
- netif_stop_queue(netdev);
-
- /* the IRDA wrapping routines don't deal with non linear skb */
- SKB_LINEAR_ASSERT(skb);
-
- kingsun = netdev_priv(netdev);
-
- spin_lock(&kingsun->lock);
- kingsun->new_speed = irda_get_next_speed(skb);
-
- /* Append data to the end of whatever data remains to be transmitted */
- wraplen =
- async_wrap_skb(skb, kingsun->tx_buf_clear, KINGSUN_SND_FIFO_SIZE);
- kingsun->tx_buf_clear_used = wraplen;
-
- if ((ret = ksdazzle_submit_tx_fragment(kingsun)) != 0) {
- dev_err(&kingsun->usbdev->dev,
- "ksdazzle_hard_xmit: failed tx_urb submit: %d\n", ret);
- switch (ret) {
- case -ENODEV:
- case -EPIPE:
- break;
- default:
- netdev->stats.tx_errors++;
- netif_start_queue(netdev);
- }
- } else {
- netdev->stats.tx_packets++;
- netdev->stats.tx_bytes += skb->len;
-
- }
-
- dev_kfree_skb(skb);
- spin_unlock(&kingsun->lock);
-
- return NETDEV_TX_OK;
-}
-
-/* Receive callback function */
-static void ksdazzle_rcv_irq(struct urb *urb)
-{
- struct ksdazzle_cb *kingsun = urb->context;
- struct net_device *netdev = kingsun->netdev;
-
- /* in process of stopping, just drop data */
- if (!netif_running(netdev)) {
- kingsun->receiving = 0;
- return;
- }
-
- /* unlink, shutdown, unplug, other nasties */
- if (urb->status != 0) {
- dev_err(&kingsun->usbdev->dev,
- "ksdazzle_rcv_irq: urb asynchronously failed - %d\n",
- urb->status);
- kingsun->receiving = 0;
- return;
- }
-
- if (urb->actual_length > 0) {
- __u8 *bytes = urb->transfer_buffer;
- unsigned int i;
-
- for (i = 0; i < urb->actual_length; i++) {
- async_unwrap_char(netdev, &netdev->stats,
- &kingsun->rx_unwrap_buff, bytes[i]);
- }
- kingsun->receiving =
- (kingsun->rx_unwrap_buff.state != OUTSIDE_FRAME) ? 1 : 0;
- }
-
- /* This urb has already been filled in ksdazzle_net_open. It is assumed that
- urb keeps the pointer to the payload buffer.
- */
- urb->status = 0;
- usb_submit_urb(urb, GFP_ATOMIC);
-}
-
-/*
- * Function ksdazzle_net_open (dev)
- *
- * Network device is taken up. Usually this is done by "ifconfig irda0 up"
- */
-static int ksdazzle_net_open(struct net_device *netdev)
-{
- struct ksdazzle_cb *kingsun = netdev_priv(netdev);
- int err = -ENOMEM;
- char hwname[16];
-
- /* At this point, urbs are NULL, and skb is NULL (see ksdazzle_probe) */
- kingsun->receiving = 0;
-
- /* Initialize for SIR to copy data directly into skb. */
- kingsun->rx_unwrap_buff.in_frame = FALSE;
- kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME;
- kingsun->rx_unwrap_buff.truesize = IRDA_SKB_MAX_MTU;
- kingsun->rx_unwrap_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU);
- if (!kingsun->rx_unwrap_buff.skb)
- goto free_mem;
-
- skb_reserve(kingsun->rx_unwrap_buff.skb, 1);
- kingsun->rx_unwrap_buff.head = kingsun->rx_unwrap_buff.skb->data;
-
- kingsun->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!kingsun->rx_urb)
- goto free_mem;
-
- kingsun->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!kingsun->tx_urb)
- goto free_mem;
-
- kingsun->speed_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!kingsun->speed_urb)
- goto free_mem;
-
- /* Initialize speed for dongle */
- kingsun->new_speed = 9600;
- err = ksdazzle_change_speed(kingsun, 9600);
- if (err < 0)
- goto free_mem;
-
- /*
- * Now that everything should be initialized properly,
- * Open new IrLAP layer instance to take care of us...
- */
- sprintf(hwname, "usb#%d", kingsun->usbdev->devnum);
- kingsun->irlap = irlap_open(netdev, &kingsun->qos, hwname);
- if (!kingsun->irlap) {
- err = -ENOMEM;
- dev_err(&kingsun->usbdev->dev, "irlap_open failed\n");
- goto free_mem;
- }
-
- /* Start reception. */
- usb_fill_int_urb(kingsun->rx_urb, kingsun->usbdev,
- usb_rcvintpipe(kingsun->usbdev, kingsun->ep_in),
- kingsun->rx_buf, KINGSUN_RCV_MAX, ksdazzle_rcv_irq,
- kingsun, 1);
- kingsun->rx_urb->status = 0;
- err = usb_submit_urb(kingsun->rx_urb, GFP_KERNEL);
- if (err) {
- dev_err(&kingsun->usbdev->dev, "first urb-submit failed: %d\n", err);
- goto close_irlap;
- }
-
- netif_start_queue(netdev);
-
- /* Situation at this point:
- - all work buffers allocated
- - urbs allocated and ready to fill
- - max rx packet known (in max_rx)
- - unwrap state machine initialized, in state outside of any frame
- - receive request in progress
- - IrLAP layer started, about to hand over packets to send
- */
-
- return 0;
-
- close_irlap:
- irlap_close(kingsun->irlap);
- free_mem:
- usb_free_urb(kingsun->speed_urb);
- kingsun->speed_urb = NULL;
- usb_free_urb(kingsun->tx_urb);
- kingsun->tx_urb = NULL;
- usb_free_urb(kingsun->rx_urb);
- kingsun->rx_urb = NULL;
- if (kingsun->rx_unwrap_buff.skb) {
- kfree_skb(kingsun->rx_unwrap_buff.skb);
- kingsun->rx_unwrap_buff.skb = NULL;
- kingsun->rx_unwrap_buff.head = NULL;
- }
- return err;
-}
-
-/*
- * Function ksdazzle_net_close (dev)
- *
- * Network device is taken down. Usually this is done by
- * "ifconfig irda0 down"
- */
-static int ksdazzle_net_close(struct net_device *netdev)
-{
- struct ksdazzle_cb *kingsun = netdev_priv(netdev);
-
- /* Stop transmit processing */
- netif_stop_queue(netdev);
-
- /* Mop up receive && transmit urb's */
- usb_kill_urb(kingsun->tx_urb);
- usb_free_urb(kingsun->tx_urb);
- kingsun->tx_urb = NULL;
-
- usb_kill_urb(kingsun->speed_urb);
- usb_free_urb(kingsun->speed_urb);
- kingsun->speed_urb = NULL;
-
- usb_kill_urb(kingsun->rx_urb);
- usb_free_urb(kingsun->rx_urb);
- kingsun->rx_urb = NULL;
-
- kfree_skb(kingsun->rx_unwrap_buff.skb);
- kingsun->rx_unwrap_buff.skb = NULL;
- kingsun->rx_unwrap_buff.head = NULL;
- kingsun->rx_unwrap_buff.in_frame = FALSE;
- kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME;
- kingsun->receiving = 0;
-
- /* Stop and remove instance of IrLAP */
- irlap_close(kingsun->irlap);
-
- kingsun->irlap = NULL;
-
- return 0;
-}
-
-/*
- * IOCTLs : Extra out-of-band network commands...
- */
-static int ksdazzle_net_ioctl(struct net_device *netdev, struct ifreq *rq,
- int cmd)
-{
- struct if_irda_req *irq = (struct if_irda_req *)rq;
- struct ksdazzle_cb *kingsun = netdev_priv(netdev);
- int ret = 0;
-
- switch (cmd) {
- case SIOCSBANDWIDTH: /* Set bandwidth */
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
-
- /* Check if the device is still there */
- if (netif_device_present(kingsun->netdev))
- return ksdazzle_change_speed(kingsun,
- irq->ifr_baudrate);
- break;
-
- case SIOCSMEDIABUSY: /* Set media busy */
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
-
- /* Check if the IrDA stack is still there */
- if (netif_running(kingsun->netdev))
- irda_device_set_media_busy(kingsun->netdev, TRUE);
- break;
-
- case SIOCGRECEIVING:
- /* Only approximately true */
- irq->ifr_receiving = kingsun->receiving;
- break;
-
- default:
- ret = -EOPNOTSUPP;
- }
-
- return ret;
-}
-
-static const struct net_device_ops ksdazzle_ops = {
- .ndo_start_xmit = ksdazzle_hard_xmit,
- .ndo_open = ksdazzle_net_open,
- .ndo_stop = ksdazzle_net_close,
- .ndo_do_ioctl = ksdazzle_net_ioctl,
-};
-
-/*
- * This routine is called by the USB subsystem for each new device
- * in the system. We need to check if the device is ours, and in
- * this case start handling it.
- */
-static int ksdazzle_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- struct usb_host_interface *interface;
- struct usb_endpoint_descriptor *endpoint;
-
- struct usb_device *dev = interface_to_usbdev(intf);
- struct ksdazzle_cb *kingsun = NULL;
- struct net_device *net = NULL;
- int ret = -ENOMEM;
- int pipe, maxp_in, maxp_out;
- __u8 ep_in;
- __u8 ep_out;
-
- /* Check that there really are two interrupt endpoints. Check based on the
- one in drivers/usb/input/usbmouse.c
- */
- interface = intf->cur_altsetting;
- if (interface->desc.bNumEndpoints != 2) {
- dev_err(&intf->dev, "ksdazzle: expected 2 endpoints, found %d\n",
- interface->desc.bNumEndpoints);
- return -ENODEV;
- }
- endpoint = &interface->endpoint[KINGSUN_EP_IN].desc;
- if (!usb_endpoint_is_int_in(endpoint)) {
- dev_err(&intf->dev,
- "ksdazzle: endpoint 0 is not interrupt IN\n");
- return -ENODEV;
- }
-
- ep_in = endpoint->bEndpointAddress;
- pipe = usb_rcvintpipe(dev, ep_in);
- maxp_in = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
- if (maxp_in > 255 || maxp_in <= 1) {
- dev_err(&intf->dev,
- "ksdazzle: endpoint 0 has max packet size %d not in range [2..255]\n",
- maxp_in);
- return -ENODEV;
- }
-
- endpoint = &interface->endpoint[KINGSUN_EP_OUT].desc;
- if (!usb_endpoint_is_int_out(endpoint)) {
- dev_err(&intf->dev,
- "ksdazzle: endpoint 1 is not interrupt OUT\n");
- return -ENODEV;
- }
-
- ep_out = endpoint->bEndpointAddress;
- pipe = usb_sndintpipe(dev, ep_out);
- maxp_out = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
-
- /* Allocate network device container. */
- net = alloc_irdadev(sizeof(*kingsun));
- if (!net)
- goto err_out1;
-
- SET_NETDEV_DEV(net, &intf->dev);
- kingsun = netdev_priv(net);
- kingsun->netdev = net;
- kingsun->usbdev = dev;
- kingsun->ep_in = ep_in;
- kingsun->ep_out = ep_out;
- kingsun->irlap = NULL;
- kingsun->tx_urb = NULL;
- kingsun->tx_buf_clear = NULL;
- kingsun->tx_buf_clear_used = 0;
- kingsun->tx_buf_clear_sent = 0;
-
- kingsun->rx_urb = NULL;
- kingsun->rx_buf = NULL;
- kingsun->rx_unwrap_buff.in_frame = FALSE;
- kingsun->rx_unwrap_buff.state = OUTSIDE_FRAME;
- kingsun->rx_unwrap_buff.skb = NULL;
- kingsun->receiving = 0;
- spin_lock_init(&kingsun->lock);
-
- kingsun->speed_setuprequest = NULL;
- kingsun->speed_urb = NULL;
- kingsun->speedparams.baudrate = 0;
-
- /* Allocate input buffer */
- kingsun->rx_buf = kmalloc(KINGSUN_RCV_MAX, GFP_KERNEL);
- if (!kingsun->rx_buf)
- goto free_mem;
-
- /* Allocate output buffer */
- kingsun->tx_buf_clear = kmalloc(KINGSUN_SND_FIFO_SIZE, GFP_KERNEL);
- if (!kingsun->tx_buf_clear)
- goto free_mem;
-
- /* Allocate and initialize speed setup packet */
- kingsun->speed_setuprequest =
- kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
- if (!kingsun->speed_setuprequest)
- goto free_mem;
- kingsun->speed_setuprequest->bRequestType =
- USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
- kingsun->speed_setuprequest->bRequest = KINGSUN_REQ_SEND;
- kingsun->speed_setuprequest->wValue = cpu_to_le16(0x0200);
- kingsun->speed_setuprequest->wIndex = cpu_to_le16(0x0001);
- kingsun->speed_setuprequest->wLength =
- cpu_to_le16(sizeof(struct ksdazzle_speedparams));
-
- printk(KERN_INFO "KingSun/Dazzle IRDA/USB found at address %d, "
- "Vendor: %x, Product: %x\n",
- dev->devnum, le16_to_cpu(dev->descriptor.idVendor),
- le16_to_cpu(dev->descriptor.idProduct));
-
- /* Initialize QoS for this device */
- irda_init_max_qos_capabilies(&kingsun->qos);
-
- /* Baud rates known to be supported. Please uncomment if devices (other
- than a SonyEriccson K300 phone) can be shown to support higher speeds
- with this dongle.
- */
- kingsun->qos.baud_rate.bits =
- IR_2400 | IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200;
- kingsun->qos.min_turn_time.bits &= KINGSUN_MTT;
- irda_qos_bits_to_value(&kingsun->qos);
-
- /* Override the network functions we need to use */
- net->netdev_ops = &ksdazzle_ops;
-
- ret = register_netdev(net);
- if (ret != 0)
- goto free_mem;
-
- dev_info(&net->dev, "IrDA: Registered KingSun/Dazzle device %s\n",
- net->name);
-
- usb_set_intfdata(intf, kingsun);
-
- /* Situation at this point:
- - all work buffers allocated
- - setup requests pre-filled
- - urbs not allocated, set to NULL
- - max rx packet known (is KINGSUN_FIFO_SIZE)
- - unwrap state machine (partially) initialized, but skb == NULL
- */
-
- return 0;
-
- free_mem:
- kfree(kingsun->speed_setuprequest);
- kfree(kingsun->tx_buf_clear);
- kfree(kingsun->rx_buf);
- free_netdev(net);
- err_out1:
- return ret;
-}
-
-/*
- * The current device is removed, the USB layer tell us to shut it down...
- */
-static void ksdazzle_disconnect(struct usb_interface *intf)
-{
- struct ksdazzle_cb *kingsun = usb_get_intfdata(intf);
-
- if (!kingsun)
- return;
-
- unregister_netdev(kingsun->netdev);
-
- /* Mop up receive && transmit urb's */
- usb_kill_urb(kingsun->speed_urb);
- usb_free_urb(kingsun->speed_urb);
- kingsun->speed_urb = NULL;
-
- usb_kill_urb(kingsun->tx_urb);
- usb_free_urb(kingsun->tx_urb);
- kingsun->tx_urb = NULL;
-
- usb_kill_urb(kingsun->rx_urb);
- usb_free_urb(kingsun->rx_urb);
- kingsun->rx_urb = NULL;
-
- kfree(kingsun->speed_setuprequest);
- kfree(kingsun->tx_buf_clear);
- kfree(kingsun->rx_buf);
- free_netdev(kingsun->netdev);
-
- usb_set_intfdata(intf, NULL);
-}
-
-#ifdef CONFIG_PM
-/* USB suspend, so power off the transmitter/receiver */
-static int ksdazzle_suspend(struct usb_interface *intf, pm_message_t message)
-{
- struct ksdazzle_cb *kingsun = usb_get_intfdata(intf);
-
- netif_device_detach(kingsun->netdev);
- if (kingsun->speed_urb != NULL)
- usb_kill_urb(kingsun->speed_urb);
- if (kingsun->tx_urb != NULL)
- usb_kill_urb(kingsun->tx_urb);
- if (kingsun->rx_urb != NULL)
- usb_kill_urb(kingsun->rx_urb);
- return 0;
-}
-
-/* Coming out of suspend, so reset hardware */
-static int ksdazzle_resume(struct usb_interface *intf)
-{
- struct ksdazzle_cb *kingsun = usb_get_intfdata(intf);
-
- if (kingsun->rx_urb != NULL) {
- /* Setup request already filled in ksdazzle_probe */
- usb_submit_urb(kingsun->rx_urb, GFP_KERNEL);
- }
- netif_device_attach(kingsun->netdev);
-
- return 0;
-}
-#endif
-
-/*
- * USB device callbacks
- */
-static struct usb_driver irda_driver = {
- .name = "ksdazzle-sir",
- .probe = ksdazzle_probe,
- .disconnect = ksdazzle_disconnect,
- .id_table = dongles,
-#ifdef CONFIG_PM
- .suspend = ksdazzle_suspend,
- .resume = ksdazzle_resume,
-#endif
-};
-
-module_usb_driver(irda_driver);
-
-MODULE_AUTHOR("Alex Villacís Lasso <a_villacis@palosanto.com>");
-MODULE_DESCRIPTION("IrDA-USB Dongle Driver for KingSun Dazzle");
-MODULE_LICENSE("GPL");
diff --git a/drivers/staging/irda/drivers/litelink-sir.c b/drivers/staging/irda/drivers/litelink-sir.c
deleted file mode 100644
index 8eefcb44bac3..000000000000
--- a/drivers/staging/irda/drivers/litelink-sir.c
+++ /dev/null
@@ -1,199 +0,0 @@
-/*********************************************************************
- *
- * Filename: litelink.c
- * Version: 1.1
- * Description: Driver for the Parallax LiteLink dongle
- * Status: Stable
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Fri May 7 12:50:33 1999
- * Modified at: Fri Dec 17 09:14:23 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1999 Dag Brattli, All Rights Reserved.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-/*
- * Modified at: Thu Jan 15 2003
- * Modified by: Eugene Crosser <crosser@average.org>
- *
- * Convert to "new" IRDA infrastructure for kernel 2.6
- */
-
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-
-#include <net/irda/irda.h>
-
-#include "sir-dev.h"
-
-#define MIN_DELAY 25 /* 15 us, but wait a little more to be sure */
-#define MAX_DELAY 10000 /* 1 ms */
-
-static int litelink_open(struct sir_dev *dev);
-static int litelink_close(struct sir_dev *dev);
-static int litelink_change_speed(struct sir_dev *dev, unsigned speed);
-static int litelink_reset(struct sir_dev *dev);
-
-/* These are the baudrates supported - 9600 must be last one! */
-static unsigned baud_rates[] = { 115200, 57600, 38400, 19200, 9600 };
-
-static struct dongle_driver litelink = {
- .owner = THIS_MODULE,
- .driver_name = "Parallax LiteLink",
- .type = IRDA_LITELINK_DONGLE,
- .open = litelink_open,
- .close = litelink_close,
- .reset = litelink_reset,
- .set_speed = litelink_change_speed,
-};
-
-static int __init litelink_sir_init(void)
-{
- return irda_register_dongle(&litelink);
-}
-
-static void __exit litelink_sir_cleanup(void)
-{
- irda_unregister_dongle(&litelink);
-}
-
-static int litelink_open(struct sir_dev *dev)
-{
- struct qos_info *qos = &dev->qos;
-
- /* Power up dongle */
- sirdev_set_dtr_rts(dev, TRUE, TRUE);
-
- /* Set the speeds we can accept */
- qos->baud_rate.bits &= IR_115200|IR_57600|IR_38400|IR_19200|IR_9600;
- qos->min_turn_time.bits = 0x7f; /* Needs 0.01 ms */
- irda_qos_bits_to_value(qos);
-
- /* irda thread waits 50 msec for power settling */
-
- return 0;
-}
-
-static int litelink_close(struct sir_dev *dev)
-{
- /* Power off dongle */
- sirdev_set_dtr_rts(dev, FALSE, FALSE);
-
- return 0;
-}
-
-/*
- * Function litelink_change_speed (task)
- *
- * Change speed of the Litelink dongle. To cycle through the available
- * baud rates, pulse RTS low for a few ms.
- */
-static int litelink_change_speed(struct sir_dev *dev, unsigned speed)
-{
- int i;
-
- /* dongle already reset by irda-thread - current speed (dongle and
- * port) is the default speed (115200 for litelink!)
- */
-
- /* Cycle through avaiable baudrates until we reach the correct one */
- for (i = 0; baud_rates[i] != speed; i++) {
-
- /* end-of-list reached due to invalid speed request */
- if (baud_rates[i] == 9600)
- break;
-
- /* Set DTR, clear RTS */
- sirdev_set_dtr_rts(dev, FALSE, TRUE);
-
- /* Sleep a minimum of 15 us */
- udelay(MIN_DELAY);
-
- /* Set DTR, Set RTS */
- sirdev_set_dtr_rts(dev, TRUE, TRUE);
-
- /* Sleep a minimum of 15 us */
- udelay(MIN_DELAY);
- }
-
- dev->speed = baud_rates[i];
-
- /* invalid baudrate should not happen - but if, we return -EINVAL and
- * the dongle configured for 9600 so the stack has a chance to recover
- */
-
- return (dev->speed == speed) ? 0 : -EINVAL;
-}
-
-/*
- * Function litelink_reset (task)
- *
- * Reset the Litelink type dongle.
- *
- */
-static int litelink_reset(struct sir_dev *dev)
-{
- /* probably the power-up can be dropped here, but with only
- * 15 usec delay it's not worth the risk unless somebody with
- * the hardware confirms it doesn't break anything...
- */
-
- /* Power on dongle */
- sirdev_set_dtr_rts(dev, TRUE, TRUE);
-
- /* Sleep a minimum of 15 us */
- udelay(MIN_DELAY);
-
- /* Clear RTS to reset dongle */
- sirdev_set_dtr_rts(dev, TRUE, FALSE);
-
- /* Sleep a minimum of 15 us */
- udelay(MIN_DELAY);
-
- /* Go back to normal mode */
- sirdev_set_dtr_rts(dev, TRUE, TRUE);
-
- /* Sleep a minimum of 15 us */
- udelay(MIN_DELAY);
-
- /* This dongles speed defaults to 115200 bps */
- dev->speed = 115200;
-
- return 0;
-}
-
-MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
-MODULE_DESCRIPTION("Parallax Litelink dongle driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("irda-dongle-5"); /* IRDA_LITELINK_DONGLE */
-
-/*
- * Function init_module (void)
- *
- * Initialize Litelink module
- *
- */
-module_init(litelink_sir_init);
-
-/*
- * Function cleanup_module (void)
- *
- * Cleanup Litelink module
- *
- */
-module_exit(litelink_sir_cleanup);
diff --git a/drivers/staging/irda/drivers/ma600-sir.c b/drivers/staging/irda/drivers/ma600-sir.c
deleted file mode 100644
index a764817b47f1..000000000000
--- a/drivers/staging/irda/drivers/ma600-sir.c
+++ /dev/null
@@ -1,253 +0,0 @@
-/*********************************************************************
- *
- * Filename: ma600.c
- * Version: 0.1
- * Description: Implementation of the MA600 dongle
- * Status: Experimental.
- * Author: Leung <95Etwl@alumni.ee.ust.hk> http://www.engsvr.ust/~eetwl95
- * Created at: Sat Jun 10 20:02:35 2000
- * Modified at: Sat Aug 16 09:34:13 2003
- * Modified by: Martin Diehl <mad@mdiehl.de> (modified for new sir_dev)
- *
- * Note: very thanks to Mr. Maru Wang <maru@mobileaction.com.tw> for providing
- * information on the MA600 dongle
- *
- * Copyright (c) 2000 Leung, All Rights Reserved.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-
-#include <net/irda/irda.h>
-
-#include "sir-dev.h"
-
-static int ma600_open(struct sir_dev *);
-static int ma600_close(struct sir_dev *);
-static int ma600_change_speed(struct sir_dev *, unsigned);
-static int ma600_reset(struct sir_dev *);
-
-/* control byte for MA600 */
-#define MA600_9600 0x00
-#define MA600_19200 0x01
-#define MA600_38400 0x02
-#define MA600_57600 0x03
-#define MA600_115200 0x04
-#define MA600_DEV_ID1 0x05
-#define MA600_DEV_ID2 0x06
-#define MA600_2400 0x08
-
-static struct dongle_driver ma600 = {
- .owner = THIS_MODULE,
- .driver_name = "MA600",
- .type = IRDA_MA600_DONGLE,
- .open = ma600_open,
- .close = ma600_close,
- .reset = ma600_reset,
- .set_speed = ma600_change_speed,
-};
-
-
-static int __init ma600_sir_init(void)
-{
- return irda_register_dongle(&ma600);
-}
-
-static void __exit ma600_sir_cleanup(void)
-{
- irda_unregister_dongle(&ma600);
-}
-
-/*
- Power on:
- (0) Clear RTS and DTR for 1 second
- (1) Set RTS and DTR for 1 second
- (2) 9600 bps now
- Note: assume RTS, DTR are clear before
-*/
-static int ma600_open(struct sir_dev *dev)
-{
- struct qos_info *qos = &dev->qos;
-
- sirdev_set_dtr_rts(dev, TRUE, TRUE);
-
- /* Explicitly set the speeds we can accept */
- qos->baud_rate.bits &= IR_2400|IR_9600|IR_19200|IR_38400
- |IR_57600|IR_115200;
- /* Hm, 0x01 means 10ms - for >= 1ms we would need 0x07 */
- qos->min_turn_time.bits = 0x01; /* Needs at least 1 ms */
- irda_qos_bits_to_value(qos);
-
- /* irda thread waits 50 msec for power settling */
-
- return 0;
-}
-
-static int ma600_close(struct sir_dev *dev)
-{
- /* Power off dongle */
- sirdev_set_dtr_rts(dev, FALSE, FALSE);
-
- return 0;
-}
-
-static __u8 get_control_byte(__u32 speed)
-{
- __u8 byte;
-
- switch (speed) {
- default:
- case 115200:
- byte = MA600_115200;
- break;
- case 57600:
- byte = MA600_57600;
- break;
- case 38400:
- byte = MA600_38400;
- break;
- case 19200:
- byte = MA600_19200;
- break;
- case 9600:
- byte = MA600_9600;
- break;
- case 2400:
- byte = MA600_2400;
- break;
- }
-
- return byte;
-}
-
-/*
- * Function ma600_change_speed (dev, speed)
- *
- * Set the speed for the MA600 type dongle.
- *
- * The dongle has already been reset to a known state (dongle default)
- * We cycle through speeds by pulsing RTS low and then high.
- */
-
-/*
- * Function ma600_change_speed (dev, speed)
- *
- * Set the speed for the MA600 type dongle.
- *
- * Algorithm
- * 1. Reset (already done by irda thread state machine)
- * 2. clear RTS, set DTR and wait for 1ms
- * 3. send Control Byte to the MA600 through TXD to set new baud rate
- * wait until the stop bit of Control Byte is sent (for 9600 baud rate,
- * it takes about 10 msec)
- * 4. set RTS, set DTR (return to NORMAL Operation)
- * 5. wait at least 10 ms, new setting (baud rate, etc) takes effect here
- * after
- */
-
-/* total delays are only about 20ms - let's just sleep for now to
- * avoid the state machine complexity before we get things working
- */
-
-static int ma600_change_speed(struct sir_dev *dev, unsigned speed)
-{
- u8 byte;
-
- pr_debug("%s(), speed=%d (was %d)\n", __func__,
- speed, dev->speed);
-
- /* dongle already reset, dongle and port at default speed (9600) */
-
- /* Set RTS low for 1 ms */
- sirdev_set_dtr_rts(dev, TRUE, FALSE);
- mdelay(1);
-
- /* Write control byte */
- byte = get_control_byte(speed);
- sirdev_raw_write(dev, &byte, sizeof(byte));
-
- /* Wait at least 10ms: fake wait_until_sent - 10 bits at 9600 baud*/
- msleep(15); /* old ma600 uses 15ms */
-
-#if 1
- /* read-back of the control byte. ma600 is the first dongle driver
- * which uses this so there might be some unidentified issues.
- * Disable this in case of problems with readback.
- */
-
- sirdev_raw_read(dev, &byte, sizeof(byte));
- if (byte != get_control_byte(speed)) {
- net_warn_ratelimited("%s(): bad control byte read-back %02x != %02x\n",
- __func__, (unsigned)byte,
- (unsigned)get_control_byte(speed));
- return -1;
- }
- else
- pr_debug("%s() control byte write read OK\n", __func__);
-#endif
-
- /* Set DTR, Set RTS */
- sirdev_set_dtr_rts(dev, TRUE, TRUE);
-
- /* Wait at least 10ms */
- msleep(10);
-
- /* dongle is now switched to the new speed */
- dev->speed = speed;
-
- return 0;
-}
-
-/*
- * Function ma600_reset (dev)
- *
- * This function resets the ma600 dongle.
- *
- * Algorithm:
- * 0. DTR=0, RTS=1 and wait 10 ms
- * 1. DTR=1, RTS=1 and wait 10 ms
- * 2. 9600 bps now
- */
-
-/* total delays are only about 20ms - let's just sleep for now to
- * avoid the state machine complexity before we get things working
- */
-
-static int ma600_reset(struct sir_dev *dev)
-{
- /* Reset the dongle : set DTR low for 10 ms */
- sirdev_set_dtr_rts(dev, FALSE, TRUE);
- msleep(10);
-
- /* Go back to normal mode */
- sirdev_set_dtr_rts(dev, TRUE, TRUE);
- msleep(10);
-
- dev->speed = 9600; /* That's the dongle-default */
-
- return 0;
-}
-
-MODULE_AUTHOR("Leung <95Etwl@alumni.ee.ust.hk> http://www.engsvr.ust/~eetwl95");
-MODULE_DESCRIPTION("MA600 dongle driver version 0.1");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("irda-dongle-11"); /* IRDA_MA600_DONGLE */
-
-module_init(ma600_sir_init);
-module_exit(ma600_sir_cleanup);
-
diff --git a/drivers/staging/irda/drivers/mcp2120-sir.c b/drivers/staging/irda/drivers/mcp2120-sir.c
deleted file mode 100644
index 2e33f91bfe8f..000000000000
--- a/drivers/staging/irda/drivers/mcp2120-sir.c
+++ /dev/null
@@ -1,224 +0,0 @@
-/*********************************************************************
- *
- *
- * Filename: mcp2120.c
- * Version: 1.0
- * Description: Implementation for the MCP2120 (Microchip)
- * Status: Experimental.
- * Author: Felix Tang (tangf@eyetap.org)
- * Created at: Sun Mar 31 19:32:12 EST 2002
- * Based on code by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 2002 Felix Tang, All Rights Reserved.
- *
- * 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/module.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-
-#include <net/irda/irda.h>
-
-#include "sir-dev.h"
-
-static int mcp2120_reset(struct sir_dev *dev);
-static int mcp2120_open(struct sir_dev *dev);
-static int mcp2120_close(struct sir_dev *dev);
-static int mcp2120_change_speed(struct sir_dev *dev, unsigned speed);
-
-#define MCP2120_9600 0x87
-#define MCP2120_19200 0x8B
-#define MCP2120_38400 0x85
-#define MCP2120_57600 0x83
-#define MCP2120_115200 0x81
-
-#define MCP2120_COMMIT 0x11
-
-static struct dongle_driver mcp2120 = {
- .owner = THIS_MODULE,
- .driver_name = "Microchip MCP2120",
- .type = IRDA_MCP2120_DONGLE,
- .open = mcp2120_open,
- .close = mcp2120_close,
- .reset = mcp2120_reset,
- .set_speed = mcp2120_change_speed,
-};
-
-static int __init mcp2120_sir_init(void)
-{
- return irda_register_dongle(&mcp2120);
-}
-
-static void __exit mcp2120_sir_cleanup(void)
-{
- irda_unregister_dongle(&mcp2120);
-}
-
-static int mcp2120_open(struct sir_dev *dev)
-{
- struct qos_info *qos = &dev->qos;
-
- /* seems no explicit power-on required here and reset switching it on anyway */
-
- qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
- qos->min_turn_time.bits = 0x01;
- irda_qos_bits_to_value(qos);
-
- return 0;
-}
-
-static int mcp2120_close(struct sir_dev *dev)
-{
- /* Power off dongle */
- /* reset and inhibit mcp2120 */
- sirdev_set_dtr_rts(dev, TRUE, TRUE);
- // sirdev_set_dtr_rts(dev, FALSE, FALSE);
-
- return 0;
-}
-
-/*
- * Function mcp2120_change_speed (dev, speed)
- *
- * Set the speed for the MCP2120.
- *
- */
-
-#define MCP2120_STATE_WAIT_SPEED (SIRDEV_STATE_DONGLE_SPEED+1)
-
-static int mcp2120_change_speed(struct sir_dev *dev, unsigned speed)
-{
- unsigned state = dev->fsm.substate;
- unsigned delay = 0;
- u8 control[2];
- static int ret = 0;
-
- switch (state) {
- case SIRDEV_STATE_DONGLE_SPEED:
- /* Set DTR to enter command mode */
- sirdev_set_dtr_rts(dev, TRUE, FALSE);
- udelay(500);
-
- ret = 0;
- switch (speed) {
- default:
- speed = 9600;
- ret = -EINVAL;
- /* fall through */
- case 9600:
- control[0] = MCP2120_9600;
- //printk("mcp2120 9600\n");
- break;
- case 19200:
- control[0] = MCP2120_19200;
- //printk("mcp2120 19200\n");
- break;
- case 34800:
- control[0] = MCP2120_38400;
- //printk("mcp2120 38400\n");
- break;
- case 57600:
- control[0] = MCP2120_57600;
- //printk("mcp2120 57600\n");
- break;
- case 115200:
- control[0] = MCP2120_115200;
- //printk("mcp2120 115200\n");
- break;
- }
- control[1] = MCP2120_COMMIT;
-
- /* Write control bytes */
- sirdev_raw_write(dev, control, 2);
- dev->speed = speed;
-
- state = MCP2120_STATE_WAIT_SPEED;
- delay = 100;
- //printk("mcp2120_change_speed: dongle_speed\n");
- break;
-
- case MCP2120_STATE_WAIT_SPEED:
- /* Go back to normal mode */
- sirdev_set_dtr_rts(dev, FALSE, FALSE);
- //printk("mcp2120_change_speed: mcp_wait\n");
- break;
-
- default:
- net_err_ratelimited("%s(), undefine state %d\n",
- __func__, state);
- ret = -EINVAL;
- break;
- }
- dev->fsm.substate = state;
- return (delay > 0) ? delay : ret;
-}
-
-/*
- * Function mcp2120_reset (driver)
- *
- * This function resets the mcp2120 dongle.
- *
- * Info: -set RTS to reset mcp2120
- * -set DTR to set mcp2120 software command mode
- * -mcp2120 defaults to 9600 baud after reset
- *
- * Algorithm:
- * 0. Set RTS to reset mcp2120.
- * 1. Clear RTS and wait for device reset timer of 30 ms (max).
- *
- */
-
-#define MCP2120_STATE_WAIT1_RESET (SIRDEV_STATE_DONGLE_RESET+1)
-#define MCP2120_STATE_WAIT2_RESET (SIRDEV_STATE_DONGLE_RESET+2)
-
-static int mcp2120_reset(struct sir_dev *dev)
-{
- unsigned state = dev->fsm.substate;
- unsigned delay = 0;
- int ret = 0;
-
- switch (state) {
- case SIRDEV_STATE_DONGLE_RESET:
- //printk("mcp2120_reset: dongle_reset\n");
- /* Reset dongle by setting RTS*/
- sirdev_set_dtr_rts(dev, TRUE, TRUE);
- state = MCP2120_STATE_WAIT1_RESET;
- delay = 50;
- break;
-
- case MCP2120_STATE_WAIT1_RESET:
- //printk("mcp2120_reset: mcp2120_wait1\n");
- /* clear RTS and wait for at least 30 ms. */
- sirdev_set_dtr_rts(dev, FALSE, FALSE);
- state = MCP2120_STATE_WAIT2_RESET;
- delay = 50;
- break;
-
- case MCP2120_STATE_WAIT2_RESET:
- //printk("mcp2120_reset mcp2120_wait2\n");
- /* Go back to normal mode */
- sirdev_set_dtr_rts(dev, FALSE, FALSE);
- break;
-
- default:
- net_err_ratelimited("%s(), undefined state %d\n",
- __func__, state);
- ret = -EINVAL;
- break;
- }
- dev->fsm.substate = state;
- return (delay > 0) ? delay : ret;
-}
-
-MODULE_AUTHOR("Felix Tang <tangf@eyetap.org>");
-MODULE_DESCRIPTION("Microchip MCP2120");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("irda-dongle-9"); /* IRDA_MCP2120_DONGLE */
-
-module_init(mcp2120_sir_init);
-module_exit(mcp2120_sir_cleanup);
diff --git a/drivers/staging/irda/drivers/mcs7780.c b/drivers/staging/irda/drivers/mcs7780.c
deleted file mode 100644
index d52e9f4b9770..000000000000
--- a/drivers/staging/irda/drivers/mcs7780.c
+++ /dev/null
@@ -1,990 +0,0 @@
-/*****************************************************************************
-*
-* Filename: mcs7780.c
-* Version: 0.4-alpha
-* Description: Irda MosChip USB Dongle Driver
-* Authors: Lukasz Stelmach <stlman@poczta.fm>
-* Brian Pugh <bpugh@cs.pdx.edu>
-* Judy Fischbach <jfisch@cs.pdx.edu>
-*
-* Based on stir4200 driver, but some things done differently.
-* Based on earlier driver by Paul Stewart <stewart@parc.com>
-*
-* Copyright (C) 2000, Roman Weissgaerber <weissg@vienna.at>
-* Copyright (C) 2001, Dag Brattli <dag@brattli.net>
-* Copyright (C) 2001, Jean Tourrilhes <jt@hpl.hp.com>
-* Copyright (C) 2004, Stephen Hemminger <shemminger@osdl.org>
-* Copyright (C) 2005, Lukasz Stelmach <stlman@poczta.fm>
-* Copyright (C) 2005, Brian Pugh <bpugh@cs.pdx.edu>
-* Copyright (C) 2005, Judy Fischbach <jfisch@cs.pdx.edu>
-*
-* 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.
-*
-*****************************************************************************/
-
-/*
- * MCS7780 is a simple USB to IrDA bridge by MosChip. It is neither
- * compatibile with irda-usb nor with stir4200. Although it is quite
- * similar to the later as far as general idea of operation is concerned.
- * That is it requires the software to do all the framing job at SIR speeds.
- * The hardware does take care of the framing at MIR and FIR speeds.
- * It supports all speeds from 2400 through 4Mbps
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/usb.h>
-#include <linux/device.h>
-#include <linux/crc32.h>
-
-#include <asm/unaligned.h>
-#include <asm/byteorder.h>
-#include <linux/uaccess.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/wrapper.h>
-#include <net/irda/crc.h>
-
-#include "mcs7780.h"
-
-#define MCS_VENDOR_ID 0x9710
-#define MCS_PRODUCT_ID 0x7780
-
-static const struct usb_device_id mcs_table[] = {
- /* MosChip Corp., MCS7780 FIR-USB Adapter */
- {USB_DEVICE(MCS_VENDOR_ID, MCS_PRODUCT_ID)},
- {},
-};
-
-MODULE_AUTHOR("Brian Pugh <bpugh@cs.pdx.edu>");
-MODULE_DESCRIPTION("IrDA-USB Dongle Driver for MosChip MCS7780");
-MODULE_VERSION("0.3alpha");
-MODULE_LICENSE("GPL");
-
-MODULE_DEVICE_TABLE(usb, mcs_table);
-
-static int qos_mtt_bits = 0x07 /* > 1ms */ ;
-module_param(qos_mtt_bits, int, 0);
-MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time");
-
-static int receive_mode = 0x1;
-module_param(receive_mode, int, 0);
-MODULE_PARM_DESC(receive_mode,
- "Receive mode of the device (1:fast, 0:slow, default:1)");
-
-static int sir_tweak = 1;
-module_param(sir_tweak, int, 0444);
-MODULE_PARM_DESC(sir_tweak,
- "Default pulse width (1:1.6us, 0:3/16 bit, default:1).");
-
-static int transceiver_type = MCS_TSC_VISHAY;
-module_param(transceiver_type, int, 0444);
-MODULE_PARM_DESC(transceiver_type, "IR transceiver type, see mcs7780.h.");
-
-static struct usb_driver mcs_driver = {
- .name = "mcs7780",
- .probe = mcs_probe,
- .disconnect = mcs_disconnect,
- .id_table = mcs_table,
-};
-
-/* speed flag selection by direct addressing.
-addr = (speed >> 8) & 0x0f
-
-0x1 57600 0x2 115200 0x4 1152000 0x5 9600
-0x6 38400 0x9 2400 0xa 576000 0xb 19200
-
-4Mbps (or 2400) must be checked separately. Since it also has
-to be programmed in a different manner that is not a big problem.
-*/
-static __u16 mcs_speed_set[16] = { 0,
- MCS_SPEED_57600,
- MCS_SPEED_115200,
- 0,
- MCS_SPEED_1152000,
- MCS_SPEED_9600,
- MCS_SPEED_38400,
- 0, 0,
- MCS_SPEED_2400,
- MCS_SPEED_576000,
- MCS_SPEED_19200,
- 0, 0, 0,
-};
-
-/* Set given 16 bit register with a 16 bit value. Send control message
- * to set dongle register. */
-static int mcs_set_reg(struct mcs_cb *mcs, __u16 reg, __u16 val)
-{
- struct usb_device *dev = mcs->usbdev;
- return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ,
- MCS_WR_RTYPE, val, reg, NULL, 0,
- msecs_to_jiffies(MCS_CTRL_TIMEOUT));
-}
-
-/* Get 16 bit register value. Send contol message to read dongle register. */
-static int mcs_get_reg(struct mcs_cb *mcs, __u16 reg, __u16 * val)
-{
- struct usb_device *dev = mcs->usbdev;
- void *dmabuf;
- int ret;
-
- dmabuf = kmalloc(sizeof(__u16), GFP_KERNEL);
- if (!dmabuf)
- return -ENOMEM;
-
- ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ,
- MCS_RD_RTYPE, 0, reg, dmabuf, 2,
- msecs_to_jiffies(MCS_CTRL_TIMEOUT));
-
- memcpy(val, dmabuf, sizeof(__u16));
- kfree(dmabuf);
-
- return ret;
-}
-
-/* Setup a communication between mcs7780 and TFDU chips. It is described
- * in more detail in the data sheet. The setup sequence puts the the
- * vishay tranceiver into high speed mode. It will also receive SIR speed
- * packets but at reduced sensitivity.
- */
-
-/* 0: OK 1:ERROR */
-static inline int mcs_setup_transceiver_vishay(struct mcs_cb *mcs)
-{
- int ret = 0;
- __u16 rval;
-
- /* mcs_get_reg should read exactly two bytes from the dongle */
- ret = mcs_get_reg(mcs, MCS_XCVR_REG, &rval);
- if (unlikely(ret != 2)) {
- ret = -EIO;
- goto error;
- }
-
- /* The MCS_XCVR_CONF bit puts the transceiver into configuration
- * mode. The MCS_MODE0 bit must start out high (1) and then
- * transition to low and the MCS_STFIR and MCS_MODE1 bits must
- * be low.
- */
- rval |= (MCS_MODE0 | MCS_XCVR_CONF);
- rval &= ~MCS_STFIR;
- rval &= ~MCS_MODE1;
- ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval);
- if (unlikely(ret))
- goto error;
-
- rval &= ~MCS_MODE0;
- ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval);
- if (unlikely(ret))
- goto error;
-
- rval &= ~MCS_XCVR_CONF;
- ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval);
- if (unlikely(ret))
- goto error;
-
- ret = 0;
-error:
- return ret;
-}
-
-/* Setup a communication between mcs7780 and agilent chip. */
-static inline int mcs_setup_transceiver_agilent(struct mcs_cb *mcs)
-{
- net_warn_ratelimited("This transceiver type is not supported yet\n");
- return 1;
-}
-
-/* Setup a communication between mcs7780 and sharp chip. */
-static inline int mcs_setup_transceiver_sharp(struct mcs_cb *mcs)
-{
- net_warn_ratelimited("This transceiver type is not supported yet\n");
- return 1;
-}
-
-/* Common setup for all transceivers */
-static inline int mcs_setup_transceiver(struct mcs_cb *mcs)
-{
- int ret = 0;
- __u16 rval;
- const char *msg;
-
- msg = "Basic transceiver setup error";
-
- /* read value of MODE Register, set the DRIVER and RESET bits
- * and write value back out to MODE Register
- */
- ret = mcs_get_reg(mcs, MCS_MODE_REG, &rval);
- if(unlikely(ret != 2))
- goto error;
- rval |= MCS_DRIVER; /* put the mcs7780 into configuration mode. */
- ret = mcs_set_reg(mcs, MCS_MODE_REG, rval);
- if(unlikely(ret))
- goto error;
-
- rval = 0; /* set min pulse width to 0 initially. */
- ret = mcs_set_reg(mcs, MCS_MINRXPW_REG, rval);
- if(unlikely(ret))
- goto error;
-
- ret = mcs_get_reg(mcs, MCS_MODE_REG, &rval);
- if(unlikely(ret != 2))
- goto error;
-
- rval &= ~MCS_FIR; /* turn off fir mode. */
- if(mcs->sir_tweak)
- rval |= MCS_SIR16US; /* 1.6us pulse width */
- else
- rval &= ~MCS_SIR16US; /* 3/16 bit time pulse width */
-
- /* make sure ask mode and back to back packets are off. */
- rval &= ~(MCS_BBTG | MCS_ASK);
-
- rval &= ~MCS_SPEED_MASK;
- rval |= MCS_SPEED_9600; /* make sure initial speed is 9600. */
- mcs->speed = 9600;
- mcs->new_speed = 0; /* new_speed is set to 0 */
- rval &= ~MCS_PLLPWDN; /* disable power down. */
-
- /* make sure device determines direction and that the auto send sip
- * pulse are on.
- */
- rval |= MCS_DTD | MCS_SIPEN;
-
- ret = mcs_set_reg(mcs, MCS_MODE_REG, rval);
- if(unlikely(ret))
- goto error;
-
- msg = "transceiver model specific setup error";
- switch (mcs->transceiver_type) {
- case MCS_TSC_VISHAY:
- ret = mcs_setup_transceiver_vishay(mcs);
- break;
-
- case MCS_TSC_SHARP:
- ret = mcs_setup_transceiver_sharp(mcs);
- break;
-
- case MCS_TSC_AGILENT:
- ret = mcs_setup_transceiver_agilent(mcs);
- break;
-
- default:
- net_warn_ratelimited("Unknown transceiver type: %d\n",
- mcs->transceiver_type);
- ret = 1;
- }
- if (unlikely(ret))
- goto error;
-
- /* If transceiver is not SHARP, then if receive mode set
- * on the RXFAST bit in the XCVR Register otherwise unset it
- */
- if (mcs->transceiver_type != MCS_TSC_SHARP) {
-
- ret = mcs_get_reg(mcs, MCS_XCVR_REG, &rval);
- if (unlikely(ret != 2))
- goto error;
- if (mcs->receive_mode)
- rval |= MCS_RXFAST;
- else
- rval &= ~MCS_RXFAST;
- ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval);
- if (unlikely(ret))
- goto error;
- }
-
- msg = "transceiver reset";
-
- ret = mcs_get_reg(mcs, MCS_MODE_REG, &rval);
- if (unlikely(ret != 2))
- goto error;
-
- /* reset the mcs7780 so all changes take effect. */
- rval &= ~MCS_RESET;
- ret = mcs_set_reg(mcs, MCS_MODE_REG, rval);
- if (unlikely(ret))
- goto error;
- else
- return ret;
-
-error:
- net_err_ratelimited("%s\n", msg);
- return ret;
-}
-
-/* Wraps the data in format for SIR */
-static inline int mcs_wrap_sir_skb(struct sk_buff *skb, __u8 * buf)
-{
- int wraplen;
-
- /* 2: full frame length, including "the length" */
- wraplen = async_wrap_skb(skb, buf + 2, 4094);
-
- wraplen += 2;
- buf[0] = wraplen & 0xff;
- buf[1] = (wraplen >> 8) & 0xff;
-
- return wraplen;
-}
-
-/* Wraps the data in format for FIR */
-static unsigned mcs_wrap_fir_skb(const struct sk_buff *skb, __u8 *buf)
-{
- unsigned int len = 0;
- __u32 fcs = ~(crc32_le(~0, skb->data, skb->len));
-
- /* add 2 bytes for length value and 4 bytes for fcs. */
- len = skb->len + 6;
-
- /* The mcs7780 requires that the first two bytes are the packet
- * length in little endian order. Note: the length value includes
- * the two bytes for the length value itself.
- */
- buf[0] = len & 0xff;
- buf[1] = (len >> 8) & 0xff;
- /* copy the data into the tx buffer. */
- skb_copy_from_linear_data(skb, buf + 2, skb->len);
- /* put the fcs in the last four bytes in little endian order. */
- buf[len - 4] = fcs & 0xff;
- buf[len - 3] = (fcs >> 8) & 0xff;
- buf[len - 2] = (fcs >> 16) & 0xff;
- buf[len - 1] = (fcs >> 24) & 0xff;
-
- return len;
-}
-
-/* Wraps the data in format for MIR */
-static unsigned mcs_wrap_mir_skb(const struct sk_buff *skb, __u8 *buf)
-{
- __u16 fcs = 0;
- int len = skb->len + 4;
-
- fcs = ~(irda_calc_crc16(~fcs, skb->data, skb->len));
- /* put the total packet length in first. Note: packet length
- * value includes the two bytes that hold the packet length
- * itself.
- */
- buf[0] = len & 0xff;
- buf[1] = (len >> 8) & 0xff;
- /* copy the data */
- skb_copy_from_linear_data(skb, buf + 2, skb->len);
- /* put the fcs in last two bytes in little endian order. */
- buf[len - 2] = fcs & 0xff;
- buf[len - 1] = (fcs >> 8) & 0xff;
-
- return len;
-}
-
-/* Unwrap received packets at MIR speed. A 16 bit crc_ccitt checksum is
- * used for the fcs. When performed over the entire packet the result
- * should be GOOD_FCS = 0xf0b8. Hands the unwrapped data off to the IrDA
- * layer via a sk_buff.
- */
-static void mcs_unwrap_mir(struct mcs_cb *mcs, __u8 *buf, int len)
-{
- __u16 fcs;
- int new_len;
- struct sk_buff *skb;
-
- /* Assume that the frames are going to fill a single packet
- * rather than span multiple packets.
- */
-
- new_len = len - 2;
- if(unlikely(new_len <= 0)) {
- net_err_ratelimited("%s short frame length %d\n",
- mcs->netdev->name, new_len);
- ++mcs->netdev->stats.rx_errors;
- ++mcs->netdev->stats.rx_length_errors;
- return;
- }
- fcs = 0;
- fcs = irda_calc_crc16(~fcs, buf, len);
-
- if(fcs != GOOD_FCS) {
- net_err_ratelimited("crc error calc 0x%x len %d\n",
- fcs, new_len);
- mcs->netdev->stats.rx_errors++;
- mcs->netdev->stats.rx_crc_errors++;
- return;
- }
-
- skb = dev_alloc_skb(new_len + 1);
- if(unlikely(!skb)) {
- ++mcs->netdev->stats.rx_dropped;
- return;
- }
-
- skb_reserve(skb, 1);
- skb_copy_to_linear_data(skb, buf, new_len);
- skb_put(skb, new_len);
- skb_reset_mac_header(skb);
- skb->protocol = htons(ETH_P_IRDA);
- skb->dev = mcs->netdev;
-
- netif_rx(skb);
-
- mcs->netdev->stats.rx_packets++;
- mcs->netdev->stats.rx_bytes += new_len;
-}
-
-/* Unwrap received packets at FIR speed. A 32 bit crc_ccitt checksum is
- * used for the fcs. Hands the unwrapped data off to the IrDA
- * layer via a sk_buff.
- */
-static void mcs_unwrap_fir(struct mcs_cb *mcs, __u8 *buf, int len)
-{
- __u32 fcs;
- int new_len;
- struct sk_buff *skb;
-
- /* Assume that the frames are going to fill a single packet
- * rather than span multiple packets. This is most likely a false
- * assumption.
- */
-
- new_len = len - 4;
- if(unlikely(new_len <= 0)) {
- net_err_ratelimited("%s short frame length %d\n",
- mcs->netdev->name, new_len);
- ++mcs->netdev->stats.rx_errors;
- ++mcs->netdev->stats.rx_length_errors;
- return;
- }
-
- fcs = ~(crc32_le(~0, buf, new_len));
- if(fcs != get_unaligned_le32(buf + new_len)) {
- net_err_ratelimited("crc error calc 0x%x len %d\n",
- fcs, new_len);
- mcs->netdev->stats.rx_errors++;
- mcs->netdev->stats.rx_crc_errors++;
- return;
- }
-
- skb = dev_alloc_skb(new_len + 1);
- if(unlikely(!skb)) {
- ++mcs->netdev->stats.rx_dropped;
- return;
- }
-
- skb_reserve(skb, 1);
- skb_copy_to_linear_data(skb, buf, new_len);
- skb_put(skb, new_len);
- skb_reset_mac_header(skb);
- skb->protocol = htons(ETH_P_IRDA);
- skb->dev = mcs->netdev;
-
- netif_rx(skb);
-
- mcs->netdev->stats.rx_packets++;
- mcs->netdev->stats.rx_bytes += new_len;
-}
-
-
-/* Allocates urbs for both receive and transmit.
- * If alloc fails return error code 0 (fail) otherwise
- * return error code 1 (success).
- */
-static inline int mcs_setup_urbs(struct mcs_cb *mcs)
-{
- mcs->rx_urb = NULL;
-
- mcs->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!mcs->tx_urb)
- return 0;
-
- mcs->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!mcs->rx_urb) {
- usb_free_urb(mcs->tx_urb);
- mcs->tx_urb = NULL;
- return 0;
- }
-
- return 1;
-}
-
-/* Sets up state to be initially outside frame, gets receive urb,
- * sets status to successful and then submits the urb to start
- * receiving the data.
- */
-static inline int mcs_receive_start(struct mcs_cb *mcs)
-{
- mcs->rx_buff.in_frame = FALSE;
- mcs->rx_buff.state = OUTSIDE_FRAME;
-
- usb_fill_bulk_urb(mcs->rx_urb, mcs->usbdev,
- usb_rcvbulkpipe(mcs->usbdev, mcs->ep_in),
- mcs->in_buf, 4096, mcs_receive_irq, mcs);
-
- mcs->rx_urb->status = 0;
- return usb_submit_urb(mcs->rx_urb, GFP_KERNEL);
-}
-
-/* Finds the in and out endpoints for the mcs control block */
-static inline int mcs_find_endpoints(struct mcs_cb *mcs,
- struct usb_host_endpoint *ep, int epnum)
-{
- int i;
- int ret = 0;
-
- /* If no place to store the endpoints just return */
- if (!ep)
- return ret;
-
- /* cycle through all endpoints, find the first two that are DIR_IN */
- for (i = 0; i < epnum; i++) {
- if (ep[i].desc.bEndpointAddress & USB_DIR_IN)
- mcs->ep_in = ep[i].desc.bEndpointAddress;
- else
- mcs->ep_out = ep[i].desc.bEndpointAddress;
-
- /* MosChip says that the chip has only two bulk
- * endpoints. Find one for each direction and move on.
- */
- if ((mcs->ep_in != 0) && (mcs->ep_out != 0)) {
- ret = 1;
- break;
- }
- }
-
- return ret;
-}
-
-static void mcs_speed_work(struct work_struct *work)
-{
- struct mcs_cb *mcs = container_of(work, struct mcs_cb, work);
- struct net_device *netdev = mcs->netdev;
-
- mcs_speed_change(mcs);
- netif_wake_queue(netdev);
-}
-
-/* Function to change the speed of the mcs7780. Fully supports SIR,
- * MIR, and FIR speeds.
- */
-static int mcs_speed_change(struct mcs_cb *mcs)
-{
- int ret = 0;
- int rst = 0;
- int cnt = 0;
- __u16 nspeed;
- __u16 rval;
-
- nspeed = mcs_speed_set[(mcs->new_speed >> 8) & 0x0f];
-
- do {
- mcs_get_reg(mcs, MCS_RESV_REG, &rval);
- } while(cnt++ < 100 && (rval & MCS_IRINTX));
-
- if (cnt > 100) {
- net_err_ratelimited("unable to change speed\n");
- ret = -EIO;
- goto error;
- }
-
- mcs_get_reg(mcs, MCS_MODE_REG, &rval);
-
- /* MINRXPW values recommended by MosChip */
- if (mcs->new_speed <= 115200) {
- rval &= ~MCS_FIR;
-
- rst = mcs->speed > 115200;
- if (rst)
- mcs_set_reg(mcs, MCS_MINRXPW_REG, 0);
-
- } else if (mcs->new_speed <= 1152000) {
- rval &= ~MCS_FIR;
-
- rst = !(mcs->speed == 576000 || mcs->speed == 1152000);
- if (rst)
- mcs_set_reg(mcs, MCS_MINRXPW_REG, 5);
-
- } else {
- rval |= MCS_FIR;
-
- rst = mcs->speed != 4000000;
- if (rst)
- mcs_set_reg(mcs, MCS_MINRXPW_REG, 5);
-
- }
-
- rval &= ~MCS_SPEED_MASK;
- rval |= nspeed;
-
- ret = mcs_set_reg(mcs, MCS_MODE_REG, rval);
- if (unlikely(ret))
- goto error;
-
- if (rst)
- switch (mcs->transceiver_type) {
- case MCS_TSC_VISHAY:
- ret = mcs_setup_transceiver_vishay(mcs);
- break;
-
- case MCS_TSC_SHARP:
- ret = mcs_setup_transceiver_sharp(mcs);
- break;
-
- case MCS_TSC_AGILENT:
- ret = mcs_setup_transceiver_agilent(mcs);
- break;
-
- default:
- ret = 1;
- net_warn_ratelimited("Unknown transceiver type: %d\n",
- mcs->transceiver_type);
- }
- if (unlikely(ret))
- goto error;
-
- mcs_get_reg(mcs, MCS_MODE_REG, &rval);
- rval &= ~MCS_RESET;
- ret = mcs_set_reg(mcs, MCS_MODE_REG, rval);
-
- mcs->speed = mcs->new_speed;
-error:
- mcs->new_speed = 0;
- return ret;
-}
-
-/* Ioctl calls not supported at this time. Can be an area of future work. */
-static int mcs_net_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
-{
- /* struct if_irda_req *irq = (struct if_irda_req *)rq; */
- /* struct mcs_cb *mcs = netdev_priv(netdev); */
- int ret = 0;
-
- switch (cmd) {
- default:
- ret = -EOPNOTSUPP;
- }
-
- return ret;
-}
-
-/* Network device is taken down, done by "ifconfig irda0 down" */
-static int mcs_net_close(struct net_device *netdev)
-{
- int ret = 0;
- struct mcs_cb *mcs = netdev_priv(netdev);
-
- /* Stop transmit processing */
- netif_stop_queue(netdev);
-
- kfree_skb(mcs->rx_buff.skb);
-
- /* kill and free the receive and transmit URBs */
- usb_kill_urb(mcs->rx_urb);
- usb_free_urb(mcs->rx_urb);
- usb_kill_urb(mcs->tx_urb);
- usb_free_urb(mcs->tx_urb);
-
- /* Stop and remove instance of IrLAP */
- if (mcs->irlap)
- irlap_close(mcs->irlap);
-
- mcs->irlap = NULL;
- return ret;
-}
-
-/* Network device is taken up, done by "ifconfig irda0 up" */
-static int mcs_net_open(struct net_device *netdev)
-{
- struct mcs_cb *mcs = netdev_priv(netdev);
- char hwname[16];
- int ret = 0;
-
- ret = usb_clear_halt(mcs->usbdev,
- usb_sndbulkpipe(mcs->usbdev, mcs->ep_in));
- if (ret)
- goto error1;
- ret = usb_clear_halt(mcs->usbdev,
- usb_rcvbulkpipe(mcs->usbdev, mcs->ep_out));
- if (ret)
- goto error1;
-
- ret = mcs_setup_transceiver(mcs);
- if (ret)
- goto error1;
-
- ret = -ENOMEM;
-
- /* Initialize for SIR/FIR to copy data directly into skb. */
- mcs->receiving = 0;
- mcs->rx_buff.truesize = IRDA_SKB_MAX_MTU;
- mcs->rx_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU);
- if (!mcs->rx_buff.skb)
- goto error1;
-
- skb_reserve(mcs->rx_buff.skb, 1);
- mcs->rx_buff.head = mcs->rx_buff.skb->data;
-
- /*
- * Now that everything should be initialized properly,
- * Open new IrLAP layer instance to take care of us...
- * Note : will send immediately a speed change...
- */
- sprintf(hwname, "usb#%d", mcs->usbdev->devnum);
- mcs->irlap = irlap_open(netdev, &mcs->qos, hwname);
- if (!mcs->irlap) {
- net_err_ratelimited("mcs7780: irlap_open failed\n");
- goto error2;
- }
-
- if (!mcs_setup_urbs(mcs))
- goto error3;
-
- ret = mcs_receive_start(mcs);
- if (ret)
- goto error4;
-
- netif_start_queue(netdev);
- return 0;
-
-error4:
- usb_free_urb(mcs->rx_urb);
- usb_free_urb(mcs->tx_urb);
-error3:
- irlap_close(mcs->irlap);
-error2:
- kfree_skb(mcs->rx_buff.skb);
-error1:
- return ret;
-}
-
-/* Receive callback function. */
-static void mcs_receive_irq(struct urb *urb)
-{
- __u8 *bytes;
- struct mcs_cb *mcs = urb->context;
- int i;
- int ret;
-
- if (!netif_running(mcs->netdev))
- return;
-
- if (urb->status)
- return;
-
- if (urb->actual_length > 0) {
- bytes = urb->transfer_buffer;
-
- /* MCS returns frames without BOF and EOF
- * I assume it returns whole frames.
- */
- /* SIR speed */
- if(mcs->speed < 576000) {
- async_unwrap_char(mcs->netdev, &mcs->netdev->stats,
- &mcs->rx_buff, 0xc0);
-
- for (i = 0; i < urb->actual_length; i++)
- async_unwrap_char(mcs->netdev, &mcs->netdev->stats,
- &mcs->rx_buff, bytes[i]);
-
- async_unwrap_char(mcs->netdev, &mcs->netdev->stats,
- &mcs->rx_buff, 0xc1);
- }
- /* MIR speed */
- else if(mcs->speed == 576000 || mcs->speed == 1152000) {
- mcs_unwrap_mir(mcs, urb->transfer_buffer,
- urb->actual_length);
- }
- /* FIR speed */
- else {
- mcs_unwrap_fir(mcs, urb->transfer_buffer,
- urb->actual_length);
- }
- }
-
- ret = usb_submit_urb(urb, GFP_ATOMIC);
-}
-
-/* Transmit callback function. */
-static void mcs_send_irq(struct urb *urb)
-{
- struct mcs_cb *mcs = urb->context;
- struct net_device *ndev = mcs->netdev;
-
- if (unlikely(mcs->new_speed))
- schedule_work(&mcs->work);
- else
- netif_wake_queue(ndev);
-}
-
-/* Transmit callback function. */
-static netdev_tx_t mcs_hard_xmit(struct sk_buff *skb,
- struct net_device *ndev)
-{
- unsigned long flags;
- struct mcs_cb *mcs;
- int wraplen;
- int ret = 0;
-
- netif_stop_queue(ndev);
- mcs = netdev_priv(ndev);
-
- spin_lock_irqsave(&mcs->lock, flags);
-
- mcs->new_speed = irda_get_next_speed(skb);
- if (likely(mcs->new_speed == mcs->speed))
- mcs->new_speed = 0;
-
- /* SIR speed */
- if(mcs->speed < 576000) {
- wraplen = mcs_wrap_sir_skb(skb, mcs->out_buf);
- }
- /* MIR speed */
- else if(mcs->speed == 576000 || mcs->speed == 1152000) {
- wraplen = mcs_wrap_mir_skb(skb, mcs->out_buf);
- }
- /* FIR speed */
- else {
- wraplen = mcs_wrap_fir_skb(skb, mcs->out_buf);
- }
- usb_fill_bulk_urb(mcs->tx_urb, mcs->usbdev,
- usb_sndbulkpipe(mcs->usbdev, mcs->ep_out),
- mcs->out_buf, wraplen, mcs_send_irq, mcs);
-
- if ((ret = usb_submit_urb(mcs->tx_urb, GFP_ATOMIC))) {
- net_err_ratelimited("failed tx_urb: %d\n", ret);
- switch (ret) {
- case -ENODEV:
- case -EPIPE:
- break;
- default:
- mcs->netdev->stats.tx_errors++;
- netif_start_queue(ndev);
- }
- } else {
- mcs->netdev->stats.tx_packets++;
- mcs->netdev->stats.tx_bytes += skb->len;
- }
-
- dev_kfree_skb(skb);
- spin_unlock_irqrestore(&mcs->lock, flags);
- return NETDEV_TX_OK;
-}
-
-static const struct net_device_ops mcs_netdev_ops = {
- .ndo_open = mcs_net_open,
- .ndo_stop = mcs_net_close,
- .ndo_start_xmit = mcs_hard_xmit,
- .ndo_do_ioctl = mcs_net_ioctl,
-};
-
-/*
- * This function is called by the USB subsystem for each new device in the
- * system. Need to verify the device and if it is, then start handling it.
- */
-static int mcs_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- struct usb_device *udev = interface_to_usbdev(intf);
- struct net_device *ndev = NULL;
- struct mcs_cb *mcs;
- int ret = -ENOMEM;
-
- ndev = alloc_irdadev(sizeof(*mcs));
- if (!ndev)
- goto error1;
-
- pr_debug("MCS7780 USB-IrDA bridge found at %d.\n", udev->devnum);
-
- SET_NETDEV_DEV(ndev, &intf->dev);
-
- ret = usb_reset_configuration(udev);
- if (ret != 0) {
- net_err_ratelimited("mcs7780: usb reset configuration failed\n");
- goto error2;
- }
-
- mcs = netdev_priv(ndev);
- mcs->usbdev = udev;
- mcs->netdev = ndev;
- spin_lock_init(&mcs->lock);
-
- /* Initialize QoS for this device */
- irda_init_max_qos_capabilies(&mcs->qos);
-
- /* That's the Rx capability. */
- mcs->qos.baud_rate.bits &=
- IR_2400 | IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200
- | IR_576000 | IR_1152000 | (IR_4000000 << 8);
-
-
- mcs->qos.min_turn_time.bits &= qos_mtt_bits;
- irda_qos_bits_to_value(&mcs->qos);
-
- /* Speed change work initialisation*/
- INIT_WORK(&mcs->work, mcs_speed_work);
-
- ndev->netdev_ops = &mcs_netdev_ops;
-
- if (!intf->cur_altsetting) {
- ret = -ENOMEM;
- goto error2;
- }
-
- ret = mcs_find_endpoints(mcs, intf->cur_altsetting->endpoint,
- intf->cur_altsetting->desc.bNumEndpoints);
- if (!ret) {
- ret = -ENODEV;
- goto error2;
- }
-
- ret = register_netdev(ndev);
- if (ret != 0)
- goto error2;
-
- pr_debug("IrDA: Registered MosChip MCS7780 device as %s\n",
- ndev->name);
-
- mcs->transceiver_type = transceiver_type;
- mcs->sir_tweak = sir_tweak;
- mcs->receive_mode = receive_mode;
-
- usb_set_intfdata(intf, mcs);
- return 0;
-
-error2:
- free_netdev(ndev);
-
-error1:
- return ret;
-}
-
-/* The current device is removed, the USB layer tells us to shut down. */
-static void mcs_disconnect(struct usb_interface *intf)
-{
- struct mcs_cb *mcs = usb_get_intfdata(intf);
-
- if (!mcs)
- return;
-
- cancel_work_sync(&mcs->work);
-
- unregister_netdev(mcs->netdev);
- free_netdev(mcs->netdev);
-
- usb_set_intfdata(intf, NULL);
- pr_debug("MCS7780 now disconnected.\n");
-}
-
-module_usb_driver(mcs_driver);
diff --git a/drivers/staging/irda/drivers/mcs7780.h b/drivers/staging/irda/drivers/mcs7780.h
deleted file mode 100644
index a6e8f7dbafc9..000000000000
--- a/drivers/staging/irda/drivers/mcs7780.h
+++ /dev/null
@@ -1,165 +0,0 @@
-/*****************************************************************************
-*
-* Filename: mcs7780.h
-* Version: 0.2-alpha
-* Description: Irda MosChip USB Dongle
-* Status: Experimental
-* Authors: Lukasz Stelmach <stlman@poczta.fm>
-* Brian Pugh <bpugh@cs.pdx.edu>
-*
-* Copyright (C) 2005, Lukasz Stelmach <stlman@poczta.fm>
-* Copyright (C) 2005, Brian Pugh <bpugh@cs.pdx.edu>
-*
-* 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.
-*
-*****************************************************************************/
-#ifndef _MCS7780_H
-#define _MCS7780_H
-
-#define MCS_MODE_SIR 0
-#define MCS_MODE_MIR 1
-#define MCS_MODE_FIR 2
-
-#define MCS_CTRL_TIMEOUT 500
-#define MCS_XMIT_TIMEOUT 500
-/* Possible transceiver types */
-#define MCS_TSC_VISHAY 0 /* Vishay TFD, default choice */
-#define MCS_TSC_AGILENT 1 /* Agilent 3602/3600 */
-#define MCS_TSC_SHARP 2 /* Sharp GP2W1000YP */
-
-/* Requests */
-#define MCS_RD_RTYPE 0xC0
-#define MCS_WR_RTYPE 0x40
-#define MCS_RDREQ 0x0F
-#define MCS_WRREQ 0x0E
-
-/* Register 0x00 */
-#define MCS_MODE_REG 0
-#define MCS_FIR ((__u16)0x0001)
-#define MCS_SIR16US ((__u16)0x0002)
-#define MCS_BBTG ((__u16)0x0004)
-#define MCS_ASK ((__u16)0x0008)
-#define MCS_PARITY ((__u16)0x0010)
-
-/* SIR/MIR speed constants */
-#define MCS_SPEED_SHIFT 5
-#define MCS_SPEED_MASK ((__u16)0x00E0)
-#define MCS_SPEED(x) ((x & MCS_SPEED_MASK) >> MCS_SPEED_SHIFT)
-#define MCS_SPEED_2400 ((0 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK)
-#define MCS_SPEED_9600 ((1 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK)
-#define MCS_SPEED_19200 ((2 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK)
-#define MCS_SPEED_38400 ((3 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK)
-#define MCS_SPEED_57600 ((4 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK)
-#define MCS_SPEED_115200 ((5 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK)
-#define MCS_SPEED_576000 ((6 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK)
-#define MCS_SPEED_1152000 ((7 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK)
-
-#define MCS_PLLPWDN ((__u16)0x0100)
-#define MCS_DRIVER ((__u16)0x0200)
-#define MCS_DTD ((__u16)0x0400)
-#define MCS_DIR ((__u16)0x0800)
-#define MCS_SIPEN ((__u16)0x1000)
-#define MCS_SENDSIP ((__u16)0x2000)
-#define MCS_CHGDIR ((__u16)0x4000)
-#define MCS_RESET ((__u16)0x8000)
-
-/* Register 0x02 */
-#define MCS_XCVR_REG 2
-#define MCS_MODE0 ((__u16)0x0001)
-#define MCS_STFIR ((__u16)0x0002)
-#define MCS_XCVR_CONF ((__u16)0x0004)
-#define MCS_RXFAST ((__u16)0x0008)
-/* TXCUR [6:4] */
-#define MCS_TXCUR_SHIFT 4
-#define MCS_TXCUR_MASK ((__u16)0x0070)
-#define MCS_TXCUR(x) ((x & MCS_TXCUR_MASK) >> MCS_TXCUR_SHIFT)
-#define MCS_SETTXCUR(x,y) \
- ((x & ~MCS_TXCUR_MASK) | (y << MCS_TXCUR_SHIFT) & MCS_TXCUR_MASK)
-
-#define MCS_MODE1 ((__u16)0x0080)
-#define MCS_SMODE0 ((__u16)0x0100)
-#define MCS_SMODE1 ((__u16)0x0200)
-#define MCS_INVTX ((__u16)0x0400)
-#define MCS_INVRX ((__u16)0x0800)
-
-#define MCS_MINRXPW_REG 4
-
-#define MCS_RESV_REG 7
-#define MCS_IRINTX ((__u16)0x0001)
-#define MCS_IRINRX ((__u16)0x0002)
-
-struct mcs_cb {
- struct usb_device *usbdev; /* init: probe_irda */
- struct net_device *netdev; /* network layer */
- struct irlap_cb *irlap; /* The link layer we are binded to */
- struct qos_info qos;
- unsigned int speed; /* Current speed */
- unsigned int new_speed; /* new speed */
-
- struct work_struct work; /* Change speed work */
-
- struct sk_buff *tx_pending;
- char in_buf[4096]; /* transmit/receive buffer */
- char out_buf[4096]; /* transmit/receive buffer */
- __u8 *fifo_status;
-
- iobuff_t rx_buff; /* receive unwrap state machine */
- spinlock_t lock;
- int receiving;
-
- __u8 ep_in;
- __u8 ep_out;
-
- struct urb *rx_urb;
- struct urb *tx_urb;
-
- int transceiver_type;
- int sir_tweak;
- int receive_mode;
-};
-
-static int mcs_set_reg(struct mcs_cb *mcs, __u16 reg, __u16 val);
-static int mcs_get_reg(struct mcs_cb *mcs, __u16 reg, __u16 * val);
-
-static inline int mcs_setup_transceiver_vishay(struct mcs_cb *mcs);
-static inline int mcs_setup_transceiver_agilent(struct mcs_cb *mcs);
-static inline int mcs_setup_transceiver_sharp(struct mcs_cb *mcs);
-static inline int mcs_setup_transceiver(struct mcs_cb *mcs);
-static inline int mcs_wrap_sir_skb(struct sk_buff *skb, __u8 * buf);
-static unsigned mcs_wrap_fir_skb(const struct sk_buff *skb, __u8 *buf);
-static unsigned mcs_wrap_mir_skb(const struct sk_buff *skb, __u8 *buf);
-static void mcs_unwrap_mir(struct mcs_cb *mcs, __u8 *buf, int len);
-static void mcs_unwrap_fir(struct mcs_cb *mcs, __u8 *buf, int len);
-static inline int mcs_setup_urbs(struct mcs_cb *mcs);
-static inline int mcs_receive_start(struct mcs_cb *mcs);
-static inline int mcs_find_endpoints(struct mcs_cb *mcs,
- struct usb_host_endpoint *ep, int epnum);
-
-static int mcs_speed_change(struct mcs_cb *mcs);
-
-static int mcs_net_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd);
-static int mcs_net_close(struct net_device *netdev);
-static int mcs_net_open(struct net_device *netdev);
-
-static void mcs_receive_irq(struct urb *urb);
-static void mcs_send_irq(struct urb *urb);
-static netdev_tx_t mcs_hard_xmit(struct sk_buff *skb,
- struct net_device *netdev);
-
-static int mcs_probe(struct usb_interface *intf,
- const struct usb_device_id *id);
-static void mcs_disconnect(struct usb_interface *intf);
-
-#endif /* _MCS7780_H */
diff --git a/drivers/staging/irda/drivers/nsc-ircc.c b/drivers/staging/irda/drivers/nsc-ircc.c
deleted file mode 100644
index 7beae147be11..000000000000
--- a/drivers/staging/irda/drivers/nsc-ircc.c
+++ /dev/null
@@ -1,2410 +0,0 @@
-/*********************************************************************
- *
- * Filename: nsc-ircc.c
- * Version: 1.0
- * Description: Driver for the NSC PC'108 and PC'338 IrDA chipsets
- * Status: Stable.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sat Nov 7 21:43:15 1998
- * Modified at: Wed Mar 1 11:29:34 2000
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>
- * Copyright (c) 1998 Lichen Wang, <lwang@actisys.com>
- * Copyright (c) 1998 Actisys Corp., www.actisys.com
- * Copyright (c) 2000-2004 Jean Tourrilhes <jt@hpl.hp.com>
- * All Rights Reserved
- *
- * 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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- * Notice that all functions that needs to access the chip in _any_
- * way, must save BSR register on entry, and restore it on exit.
- * It is _very_ important to follow this policy!
- *
- * __u8 bank;
- *
- * bank = inb(iobase+BSR);
- *
- * do_your_stuff_here();
- *
- * outb(bank, iobase+BSR);
- *
- * If you find bugs in this file, its very likely that the same bug
- * will also be in w83977af_ir.c since the implementations are quite
- * similar.
- *
- ********************************************************************/
-
-#include <linux/module.h>
-#include <linux/gfp.h>
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/rtnetlink.h>
-#include <linux/dma-mapping.h>
-#include <linux/pnp.h>
-#include <linux/platform_device.h>
-
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <asm/byteorder.h>
-
-#include <net/irda/wrapper.h>
-#include <net/irda/irda.h>
-#include <net/irda/irda_device.h>
-
-#include "nsc-ircc.h"
-
-#define CHIP_IO_EXTENT 8
-#define BROKEN_DONGLE_ID
-
-static char *driver_name = "nsc-ircc";
-
-/* Power Management */
-#define NSC_IRCC_DRIVER_NAME "nsc-ircc"
-static int nsc_ircc_suspend(struct platform_device *dev, pm_message_t state);
-static int nsc_ircc_resume(struct platform_device *dev);
-
-static struct platform_driver nsc_ircc_driver = {
- .suspend = nsc_ircc_suspend,
- .resume = nsc_ircc_resume,
- .driver = {
- .name = NSC_IRCC_DRIVER_NAME,
- },
-};
-
-/* Module parameters */
-static int qos_mtt_bits = 0x07; /* 1 ms or more */
-static int dongle_id;
-
-/* Use BIOS settions by default, but user may supply module parameters */
-static unsigned int io[] = { ~0, ~0, ~0, ~0, ~0 };
-static unsigned int irq[] = { 0, 0, 0, 0, 0 };
-static unsigned int dma[] = { 0, 0, 0, 0, 0 };
-
-static int nsc_ircc_probe_108(nsc_chip_t *chip, chipio_t *info);
-static int nsc_ircc_probe_338(nsc_chip_t *chip, chipio_t *info);
-static int nsc_ircc_probe_39x(nsc_chip_t *chip, chipio_t *info);
-static int nsc_ircc_init_108(nsc_chip_t *chip, chipio_t *info);
-static int nsc_ircc_init_338(nsc_chip_t *chip, chipio_t *info);
-static int nsc_ircc_init_39x(nsc_chip_t *chip, chipio_t *info);
-#ifdef CONFIG_PNP
-static int nsc_ircc_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *id);
-#endif
-
-/* These are the known NSC chips */
-static nsc_chip_t chips[] = {
-/* Name, {cfg registers}, chip id index reg, chip id expected value, revision mask */
- { "PC87108", { 0x150, 0x398, 0xea }, 0x05, 0x10, 0xf0,
- nsc_ircc_probe_108, nsc_ircc_init_108 },
- { "PC87338", { 0x398, 0x15c, 0x2e }, 0x08, 0xb0, 0xf8,
- nsc_ircc_probe_338, nsc_ircc_init_338 },
- /* Contributed by Steffen Pingel - IBM X40 */
- { "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,
- nsc_ircc_probe_39x, nsc_ircc_init_39x },
- /* IBM ThinkPads using PC8738x (T60/X60/Z60) */
- { "IBM-PC8738x", { 0x2e, 0x4e, 0x0 }, 0x20, 0xf4, 0xff,
- nsc_ircc_probe_39x, nsc_ircc_init_39x },
- /* IBM ThinkPads using PC8394T (T43/R52/?) */
- { "IBM-PC8394T", { 0x2e, 0x4e, 0x0 }, 0x20, 0xf9, 0xff,
- nsc_ircc_probe_39x, nsc_ircc_init_39x },
- { NULL }
-};
-
-static struct nsc_ircc_cb *dev_self[] = { NULL, NULL, NULL, NULL, NULL };
-
-static char *dongle_types[] = {
- "Differential serial interface",
- "Differential serial interface",
- "Reserved",
- "Reserved",
- "Sharp RY5HD01",
- "Reserved",
- "Single-ended serial interface",
- "Consumer-IR only",
- "HP HSDL-2300, HP HSDL-3600/HSDL-3610",
- "IBM31T1100 or Temic TFDS6000/TFDS6500",
- "Reserved",
- "Reserved",
- "HP HSDL-1100/HSDL-2100",
- "HP HSDL-1100/HSDL-2100",
- "Supports SIR Mode only",
- "No dongle connected",
-};
-
-/* PNP probing */
-static chipio_t pnp_info;
-static const struct pnp_device_id nsc_ircc_pnp_table[] = {
- { .id = "NSC6001", .driver_data = 0 },
- { .id = "HWPC224", .driver_data = 0 },
- { .id = "IBM0071", .driver_data = NSC_FORCE_DONGLE_TYPE9 },
- { }
-};
-
-MODULE_DEVICE_TABLE(pnp, nsc_ircc_pnp_table);
-
-static struct pnp_driver nsc_ircc_pnp_driver = {
-#ifdef CONFIG_PNP
- .name = "nsc-ircc",
- .id_table = nsc_ircc_pnp_table,
- .probe = nsc_ircc_pnp_probe,
-#endif
-};
-
-/* Some prototypes */
-static int nsc_ircc_open(chipio_t *info);
-static int nsc_ircc_close(struct nsc_ircc_cb *self);
-static int nsc_ircc_setup(chipio_t *info);
-static void nsc_ircc_pio_receive(struct nsc_ircc_cb *self);
-static int nsc_ircc_dma_receive(struct nsc_ircc_cb *self);
-static int nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase);
-static netdev_tx_t nsc_ircc_hard_xmit_sir(struct sk_buff *skb,
- struct net_device *dev);
-static netdev_tx_t nsc_ircc_hard_xmit_fir(struct sk_buff *skb,
- struct net_device *dev);
-static int nsc_ircc_pio_write(int iobase, __u8 *buf, int len, int fifo_size);
-static void nsc_ircc_dma_xmit(struct nsc_ircc_cb *self, int iobase);
-static __u8 nsc_ircc_change_speed(struct nsc_ircc_cb *self, __u32 baud);
-static int nsc_ircc_is_receiving(struct nsc_ircc_cb *self);
-static int nsc_ircc_read_dongle_id (int iobase);
-static void nsc_ircc_init_dongle_interface (int iobase, int dongle_id);
-
-static int nsc_ircc_net_open(struct net_device *dev);
-static int nsc_ircc_net_close(struct net_device *dev);
-static int nsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-
-/* Globals */
-static int pnp_registered;
-static int pnp_succeeded;
-
-/*
- * Function nsc_ircc_init ()
- *
- * Initialize chip. Just try to find out how many chips we are dealing with
- * and where they are
- */
-static int __init nsc_ircc_init(void)
-{
- chipio_t info;
- nsc_chip_t *chip;
- int ret;
- int cfg_base;
- int cfg, id;
- int reg;
- int i = 0;
-
- ret = platform_driver_register(&nsc_ircc_driver);
- if (ret) {
- net_err_ratelimited("%s, Can't register driver!\n",
- driver_name);
- return ret;
- }
-
- /* Register with PnP subsystem to detect disable ports */
- ret = pnp_register_driver(&nsc_ircc_pnp_driver);
-
- if (!ret)
- pnp_registered = 1;
-
- ret = -ENODEV;
-
- /* Probe for all the NSC chipsets we know about */
- for (chip = chips; chip->name ; chip++) {
- pr_debug("%s(), Probing for %s ...\n", __func__,
- chip->name);
-
- /* Try all config registers for this chip */
- for (cfg = 0; cfg < ARRAY_SIZE(chip->cfg); cfg++) {
- cfg_base = chip->cfg[cfg];
- if (!cfg_base)
- continue;
-
- /* Read index register */
- reg = inb(cfg_base);
- if (reg == 0xff) {
- pr_debug("%s() no chip at 0x%03x\n",
- __func__, cfg_base);
- continue;
- }
-
- /* Read chip identification register */
- outb(chip->cid_index, cfg_base);
- id = inb(cfg_base+1);
- if ((id & chip->cid_mask) == chip->cid_value) {
- pr_debug("%s() Found %s chip, revision=%d\n",
- __func__, chip->name,
- id & ~chip->cid_mask);
-
- /*
- * If we found a correct PnP setting,
- * we first try it.
- */
- if (pnp_succeeded) {
- memset(&info, 0, sizeof(chipio_t));
- info.cfg_base = cfg_base;
- info.fir_base = pnp_info.fir_base;
- info.dma = pnp_info.dma;
- info.irq = pnp_info.irq;
-
- if (info.fir_base < 0x2000) {
- net_info_ratelimited("%s, chip->init\n",
- driver_name);
- chip->init(chip, &info);
- } else
- chip->probe(chip, &info);
-
- if (nsc_ircc_open(&info) >= 0)
- ret = 0;
- }
-
- /*
- * Opening based on PnP values failed.
- * Let's fallback to user values, or probe
- * the chip.
- */
- if (ret) {
- pr_debug("%s, PnP init failed\n",
- driver_name);
- memset(&info, 0, sizeof(chipio_t));
- info.cfg_base = cfg_base;
- info.fir_base = io[i];
- info.dma = dma[i];
- info.irq = irq[i];
-
- /*
- * If the user supplies the base address, then
- * we init the chip, if not we probe the values
- * set by the BIOS
- */
- if (io[i] < 0x2000) {
- chip->init(chip, &info);
- } else
- chip->probe(chip, &info);
-
- if (nsc_ircc_open(&info) >= 0)
- ret = 0;
- }
- i++;
- } else {
- pr_debug("%s(), Wrong chip id=0x%02x\n",
- __func__, id);
- }
- }
- }
-
- if (ret) {
- platform_driver_unregister(&nsc_ircc_driver);
- pnp_unregister_driver(&nsc_ircc_pnp_driver);
- pnp_registered = 0;
- }
-
- return ret;
-}
-
-/*
- * Function nsc_ircc_cleanup ()
- *
- * Close all configured chips
- *
- */
-static void __exit nsc_ircc_cleanup(void)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(dev_self); i++) {
- if (dev_self[i])
- nsc_ircc_close(dev_self[i]);
- }
-
- platform_driver_unregister(&nsc_ircc_driver);
-
- if (pnp_registered)
- pnp_unregister_driver(&nsc_ircc_pnp_driver);
-
- pnp_registered = 0;
-}
-
-static const struct net_device_ops nsc_ircc_sir_ops = {
- .ndo_open = nsc_ircc_net_open,
- .ndo_stop = nsc_ircc_net_close,
- .ndo_start_xmit = nsc_ircc_hard_xmit_sir,
- .ndo_do_ioctl = nsc_ircc_net_ioctl,
-};
-
-static const struct net_device_ops nsc_ircc_fir_ops = {
- .ndo_open = nsc_ircc_net_open,
- .ndo_stop = nsc_ircc_net_close,
- .ndo_start_xmit = nsc_ircc_hard_xmit_fir,
- .ndo_do_ioctl = nsc_ircc_net_ioctl,
-};
-
-/*
- * Function nsc_ircc_open (iobase, irq)
- *
- * Open driver instance
- *
- */
-static int __init nsc_ircc_open(chipio_t *info)
-{
- struct net_device *dev;
- struct nsc_ircc_cb *self;
- void *ret;
- int err, chip_index;
-
- for (chip_index = 0; chip_index < ARRAY_SIZE(dev_self); chip_index++) {
- if (!dev_self[chip_index])
- break;
- }
-
- if (chip_index == ARRAY_SIZE(dev_self)) {
- net_err_ratelimited("%s(), maximum number of supported chips reached!\n",
- __func__);
- return -ENOMEM;
- }
-
- net_info_ratelimited("%s, Found chip at base=0x%03x\n",
- driver_name, info->cfg_base);
-
- if ((nsc_ircc_setup(info)) == -1)
- return -1;
-
- net_info_ratelimited("%s, driver loaded (Dag Brattli)\n", driver_name);
-
- dev = alloc_irdadev(sizeof(struct nsc_ircc_cb));
- if (dev == NULL) {
- net_err_ratelimited("%s(), can't allocate memory for control block!\n",
- __func__);
- return -ENOMEM;
- }
-
- self = netdev_priv(dev);
- self->netdev = dev;
- spin_lock_init(&self->lock);
-
- /* Need to store self somewhere */
- dev_self[chip_index] = self;
- self->index = chip_index;
-
- /* Initialize IO */
- self->io.cfg_base = info->cfg_base;
- self->io.fir_base = info->fir_base;
- self->io.irq = info->irq;
- self->io.fir_ext = CHIP_IO_EXTENT;
- self->io.dma = info->dma;
- self->io.fifo_size = 32;
-
- /* Reserve the ioports that we need */
- ret = request_region(self->io.fir_base, self->io.fir_ext, driver_name);
- if (!ret) {
- net_warn_ratelimited("%s(), can't get iobase of 0x%03x\n",
- __func__, self->io.fir_base);
- err = -ENODEV;
- goto out1;
- }
-
- /* Initialize QoS for this device */
- irda_init_max_qos_capabilies(&self->qos);
-
- /* The only value we must override it the baudrate */
- self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|
- IR_115200|IR_576000|IR_1152000 |(IR_4000000 << 8);
-
- self->qos.min_turn_time.bits = qos_mtt_bits;
- irda_qos_bits_to_value(&self->qos);
-
- /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */
- self->rx_buff.truesize = 14384;
- self->tx_buff.truesize = 14384;
-
- /* Allocate memory if needed */
- self->rx_buff.head =
- dma_zalloc_coherent(NULL, self->rx_buff.truesize,
- &self->rx_buff_dma, GFP_KERNEL);
- if (self->rx_buff.head == NULL) {
- err = -ENOMEM;
- goto out2;
-
- }
-
- self->tx_buff.head =
- dma_zalloc_coherent(NULL, self->tx_buff.truesize,
- &self->tx_buff_dma, GFP_KERNEL);
- if (self->tx_buff.head == NULL) {
- err = -ENOMEM;
- goto out3;
- }
-
- self->rx_buff.in_frame = FALSE;
- self->rx_buff.state = OUTSIDE_FRAME;
- self->tx_buff.data = self->tx_buff.head;
- self->rx_buff.data = self->rx_buff.head;
-
- /* Reset Tx queue info */
- self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0;
- self->tx_fifo.tail = self->tx_buff.head;
-
- /* Override the network functions we need to use */
- dev->netdev_ops = &nsc_ircc_sir_ops;
-
- err = register_netdev(dev);
- if (err) {
- net_err_ratelimited("%s(), register_netdev() failed!\n",
- __func__);
- goto out4;
- }
- net_info_ratelimited("IrDA: Registered device %s\n", dev->name);
-
- /* Check if user has supplied a valid dongle id or not */
- if ((dongle_id <= 0) ||
- (dongle_id >= ARRAY_SIZE(dongle_types))) {
- dongle_id = nsc_ircc_read_dongle_id(self->io.fir_base);
-
- net_info_ratelimited("%s, Found dongle: %s\n",
- driver_name, dongle_types[dongle_id]);
- } else {
- net_info_ratelimited("%s, Using dongle: %s\n",
- driver_name, dongle_types[dongle_id]);
- }
-
- self->io.dongle_id = dongle_id;
- nsc_ircc_init_dongle_interface(self->io.fir_base, dongle_id);
-
- self->pldev = platform_device_register_simple(NSC_IRCC_DRIVER_NAME,
- self->index, NULL, 0);
- if (IS_ERR(self->pldev)) {
- err = PTR_ERR(self->pldev);
- goto out5;
- }
- platform_set_drvdata(self->pldev, self);
-
- return chip_index;
-
- out5:
- unregister_netdev(dev);
- out4:
- dma_free_coherent(NULL, self->tx_buff.truesize,
- self->tx_buff.head, self->tx_buff_dma);
- out3:
- dma_free_coherent(NULL, self->rx_buff.truesize,
- self->rx_buff.head, self->rx_buff_dma);
- out2:
- release_region(self->io.fir_base, self->io.fir_ext);
- out1:
- free_netdev(dev);
- dev_self[chip_index] = NULL;
- return err;
-}
-
-/*
- * Function nsc_ircc_close (self)
- *
- * Close driver instance
- *
- */
-static int __exit nsc_ircc_close(struct nsc_ircc_cb *self)
-{
- int iobase;
-
- IRDA_ASSERT(self != NULL, return -1;);
-
- iobase = self->io.fir_base;
-
- platform_device_unregister(self->pldev);
-
- /* Remove netdevice */
- unregister_netdev(self->netdev);
-
- /* Release the PORT that this driver is using */
- pr_debug("%s(), Releasing Region %03x\n",
- __func__, self->io.fir_base);
- release_region(self->io.fir_base, self->io.fir_ext);
-
- if (self->tx_buff.head)
- dma_free_coherent(NULL, self->tx_buff.truesize,
- self->tx_buff.head, self->tx_buff_dma);
-
- if (self->rx_buff.head)
- dma_free_coherent(NULL, self->rx_buff.truesize,
- self->rx_buff.head, self->rx_buff_dma);
-
- dev_self[self->index] = NULL;
- free_netdev(self->netdev);
-
- return 0;
-}
-
-/*
- * Function nsc_ircc_init_108 (iobase, cfg_base, irq, dma)
- *
- * Initialize the NSC '108 chip
- *
- */
-static int nsc_ircc_init_108(nsc_chip_t *chip, chipio_t *info)
-{
- int cfg_base = info->cfg_base;
- __u8 temp=0;
-
- outb(2, cfg_base); /* Mode Control Register (MCTL) */
- outb(0x00, cfg_base+1); /* Disable device */
-
- /* Base Address and Interrupt Control Register (BAIC) */
- outb(CFG_108_BAIC, cfg_base);
- switch (info->fir_base) {
- case 0x3e8: outb(0x14, cfg_base+1); break;
- case 0x2e8: outb(0x15, cfg_base+1); break;
- case 0x3f8: outb(0x16, cfg_base+1); break;
- case 0x2f8: outb(0x17, cfg_base+1); break;
- default: net_err_ratelimited("%s(), invalid base_address\n", __func__);
- }
-
- /* Control Signal Routing Register (CSRT) */
- switch (info->irq) {
- case 3: temp = 0x01; break;
- case 4: temp = 0x02; break;
- case 5: temp = 0x03; break;
- case 7: temp = 0x04; break;
- case 9: temp = 0x05; break;
- case 11: temp = 0x06; break;
- case 15: temp = 0x07; break;
- default: net_err_ratelimited("%s(), invalid irq\n", __func__);
- }
- outb(CFG_108_CSRT, cfg_base);
-
- switch (info->dma) {
- case 0: outb(0x08+temp, cfg_base+1); break;
- case 1: outb(0x10+temp, cfg_base+1); break;
- case 3: outb(0x18+temp, cfg_base+1); break;
- default: net_err_ratelimited("%s(), invalid dma\n", __func__);
- }
-
- outb(CFG_108_MCTL, cfg_base); /* Mode Control Register (MCTL) */
- outb(0x03, cfg_base+1); /* Enable device */
-
- return 0;
-}
-
-/*
- * Function nsc_ircc_probe_108 (chip, info)
- *
- *
- *
- */
-static int nsc_ircc_probe_108(nsc_chip_t *chip, chipio_t *info)
-{
- int cfg_base = info->cfg_base;
- int reg;
-
- /* Read address and interrupt control register (BAIC) */
- outb(CFG_108_BAIC, cfg_base);
- reg = inb(cfg_base+1);
-
- switch (reg & 0x03) {
- case 0:
- info->fir_base = 0x3e8;
- break;
- case 1:
- info->fir_base = 0x2e8;
- break;
- case 2:
- info->fir_base = 0x3f8;
- break;
- case 3:
- info->fir_base = 0x2f8;
- break;
- }
- info->sir_base = info->fir_base;
- pr_debug("%s(), probing fir_base=0x%03x\n", __func__,
- info->fir_base);
-
- /* Read control signals routing register (CSRT) */
- outb(CFG_108_CSRT, cfg_base);
- reg = inb(cfg_base+1);
-
- switch (reg & 0x07) {
- case 0:
- info->irq = -1;
- break;
- case 1:
- info->irq = 3;
- break;
- case 2:
- info->irq = 4;
- break;
- case 3:
- info->irq = 5;
- break;
- case 4:
- info->irq = 7;
- break;
- case 5:
- info->irq = 9;
- break;
- case 6:
- info->irq = 11;
- break;
- case 7:
- info->irq = 15;
- break;
- }
- pr_debug("%s(), probing irq=%d\n", __func__, info->irq);
-
- /* Currently we only read Rx DMA but it will also be used for Tx */
- switch ((reg >> 3) & 0x03) {
- case 0:
- info->dma = -1;
- break;
- case 1:
- info->dma = 0;
- break;
- case 2:
- info->dma = 1;
- break;
- case 3:
- info->dma = 3;
- break;
- }
- pr_debug("%s(), probing dma=%d\n", __func__, info->dma);
-
- /* Read mode control register (MCTL) */
- outb(CFG_108_MCTL, cfg_base);
- reg = inb(cfg_base+1);
-
- info->enabled = reg & 0x01;
- info->suspended = !((reg >> 1) & 0x01);
-
- return 0;
-}
-
-/*
- * Function nsc_ircc_init_338 (chip, info)
- *
- * Initialize the NSC '338 chip. Remember that the 87338 needs two
- * consecutive writes to the data registers while CPU interrupts are
- * disabled. The 97338 does not require this, but shouldn't be any
- * harm if we do it anyway.
- */
-static int nsc_ircc_init_338(nsc_chip_t *chip, chipio_t *info)
-{
- /* No init yet */
-
- return 0;
-}
-
-/*
- * Function nsc_ircc_probe_338 (chip, info)
- *
- *
- *
- */
-static int nsc_ircc_probe_338(nsc_chip_t *chip, chipio_t *info)
-{
- int cfg_base = info->cfg_base;
- int reg, com = 0;
- int pnp;
-
- /* Read function enable register (FER) */
- outb(CFG_338_FER, cfg_base);
- reg = inb(cfg_base+1);
-
- info->enabled = (reg >> 2) & 0x01;
-
- /* Check if we are in Legacy or PnP mode */
- outb(CFG_338_PNP0, cfg_base);
- reg = inb(cfg_base+1);
-
- pnp = (reg >> 3) & 0x01;
- if (pnp) {
- pr_debug("(), Chip is in PnP mode\n");
- outb(0x46, cfg_base);
- reg = (inb(cfg_base+1) & 0xfe) << 2;
-
- outb(0x47, cfg_base);
- reg |= ((inb(cfg_base+1) & 0xfc) << 8);
-
- info->fir_base = reg;
- } else {
- /* Read function address register (FAR) */
- outb(CFG_338_FAR, cfg_base);
- reg = inb(cfg_base+1);
-
- switch ((reg >> 4) & 0x03) {
- case 0:
- info->fir_base = 0x3f8;
- break;
- case 1:
- info->fir_base = 0x2f8;
- break;
- case 2:
- com = 3;
- break;
- case 3:
- com = 4;
- break;
- }
-
- if (com) {
- switch ((reg >> 6) & 0x03) {
- case 0:
- if (com == 3)
- info->fir_base = 0x3e8;
- else
- info->fir_base = 0x2e8;
- break;
- case 1:
- if (com == 3)
- info->fir_base = 0x338;
- else
- info->fir_base = 0x238;
- break;
- case 2:
- if (com == 3)
- info->fir_base = 0x2e8;
- else
- info->fir_base = 0x2e0;
- break;
- case 3:
- if (com == 3)
- info->fir_base = 0x220;
- else
- info->fir_base = 0x228;
- break;
- }
- }
- }
- info->sir_base = info->fir_base;
-
- /* Read PnP register 1 (PNP1) */
- outb(CFG_338_PNP1, cfg_base);
- reg = inb(cfg_base+1);
-
- info->irq = reg >> 4;
-
- /* Read PnP register 3 (PNP3) */
- outb(CFG_338_PNP3, cfg_base);
- reg = inb(cfg_base+1);
-
- info->dma = (reg & 0x07) - 1;
-
- /* Read power and test register (PTR) */
- outb(CFG_338_PTR, cfg_base);
- reg = inb(cfg_base+1);
-
- info->suspended = reg & 0x01;
-
- return 0;
-}
-
-
-/*
- * Function nsc_ircc_init_39x (chip, info)
- *
- * Now that we know it's a '39x (see probe below), we need to
- * configure it so we can use it.
- *
- * The NSC '338 chip is a Super I/O chip with a "bank" architecture,
- * the configuration of the different functionality (serial, parallel,
- * floppy...) are each in a different bank (Logical Device Number).
- * The base address, irq and dma configuration registers are common
- * to all functionalities (index 0x30 to 0x7F).
- * There is only one configuration register specific to the
- * serial port, CFG_39X_SPC.
- * JeanII
- *
- * Note : this code was written by Jan Frey <janfrey@web.de>
- */
-static int nsc_ircc_init_39x(nsc_chip_t *chip, chipio_t *info)
-{
- int cfg_base = info->cfg_base;
- int enabled;
-
- /* User is sure about his config... accept it. */
- pr_debug("%s(): nsc_ircc_init_39x (user settings): io=0x%04x, irq=%d, dma=%d\n",
- __func__, info->fir_base, info->irq, info->dma);
-
- /* Access bank for SP2 */
- outb(CFG_39X_LDN, cfg_base);
- outb(0x02, cfg_base+1);
-
- /* Configure SP2 */
-
- /* We want to enable the device if not enabled */
- outb(CFG_39X_ACT, cfg_base);
- enabled = inb(cfg_base+1) & 0x01;
-
- if (!enabled) {
- /* Enable the device */
- outb(CFG_39X_SIOCF1, cfg_base);
- outb(0x01, cfg_base+1);
- /* May want to update info->enabled. Jean II */
- }
-
- /* Enable UART bank switching (bit 7) ; Sets the chip to normal
- * power mode (wake up from sleep mode) (bit 1) */
- outb(CFG_39X_SPC, cfg_base);
- outb(0x82, cfg_base+1);
-
- return 0;
-}
-
-/*
- * Function nsc_ircc_probe_39x (chip, info)
- *
- * Test if we really have a '39x chip at the given address
- *
- * Note : this code was written by Jan Frey <janfrey@web.de>
- */
-static int nsc_ircc_probe_39x(nsc_chip_t *chip, chipio_t *info)
-{
- int cfg_base = info->cfg_base;
- int reg1, reg2, irq, irqt, dma1, dma2;
- int enabled, susp;
-
- pr_debug("%s(), nsc_ircc_probe_39x, base=%d\n",
- __func__, cfg_base);
-
- /* This function should be executed with irq off to avoid
- * another driver messing with the Super I/O bank - Jean II */
-
- /* Access bank for SP2 */
- outb(CFG_39X_LDN, cfg_base);
- outb(0x02, cfg_base+1);
-
- /* Read infos about SP2 ; store in info struct */
- outb(CFG_39X_BASEH, cfg_base);
- reg1 = inb(cfg_base+1);
- outb(CFG_39X_BASEL, cfg_base);
- reg2 = inb(cfg_base+1);
- info->fir_base = (reg1 << 8) | reg2;
-
- outb(CFG_39X_IRQNUM, cfg_base);
- irq = inb(cfg_base+1);
- outb(CFG_39X_IRQSEL, cfg_base);
- irqt = inb(cfg_base+1);
- info->irq = irq;
-
- outb(CFG_39X_DMA0, cfg_base);
- dma1 = inb(cfg_base+1);
- outb(CFG_39X_DMA1, cfg_base);
- dma2 = inb(cfg_base+1);
- info->dma = dma1 -1;
-
- outb(CFG_39X_ACT, cfg_base);
- info->enabled = enabled = inb(cfg_base+1) & 0x01;
-
- outb(CFG_39X_SPC, cfg_base);
- susp = 1 - ((inb(cfg_base+1) & 0x02) >> 1);
-
- pr_debug("%s(): io=0x%02x%02x, irq=%d (type %d), rxdma=%d, txdma=%d, enabled=%d (suspended=%d)\n",
- __func__, reg1, reg2, irq, irqt, dma1, dma2, enabled, susp);
-
- /* Configure SP2 */
-
- /* We want to enable the device if not enabled */
- outb(CFG_39X_ACT, cfg_base);
- enabled = inb(cfg_base+1) & 0x01;
-
- if (!enabled) {
- /* Enable the device */
- outb(CFG_39X_SIOCF1, cfg_base);
- outb(0x01, cfg_base+1);
- /* May want to update info->enabled. Jean II */
- }
-
- /* Enable UART bank switching (bit 7) ; Sets the chip to normal
- * power mode (wake up from sleep mode) (bit 1) */
- outb(CFG_39X_SPC, cfg_base);
- outb(0x82, cfg_base+1);
-
- return 0;
-}
-
-#ifdef CONFIG_PNP
-/* PNP probing */
-static int nsc_ircc_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *id)
-{
- memset(&pnp_info, 0, sizeof(chipio_t));
- pnp_info.irq = -1;
- pnp_info.dma = -1;
- pnp_succeeded = 1;
-
- if (id->driver_data & NSC_FORCE_DONGLE_TYPE9)
- dongle_id = 0x9;
-
- /* There doesn't seem to be any way of getting the cfg_base.
- * On my box, cfg_base is in the PnP descriptor of the
- * motherboard. Oh well... Jean II */
-
- if (pnp_port_valid(dev, 0) &&
- !(pnp_port_flags(dev, 0) & IORESOURCE_DISABLED))
- pnp_info.fir_base = pnp_port_start(dev, 0);
-
- if (pnp_irq_valid(dev, 0) &&
- !(pnp_irq_flags(dev, 0) & IORESOURCE_DISABLED))
- pnp_info.irq = pnp_irq(dev, 0);
-
- if (pnp_dma_valid(dev, 0) &&
- !(pnp_dma_flags(dev, 0) & IORESOURCE_DISABLED))
- pnp_info.dma = pnp_dma(dev, 0);
-
- pr_debug("%s() : From PnP, found firbase 0x%03X ; irq %d ; dma %d.\n",
- __func__, pnp_info.fir_base, pnp_info.irq, pnp_info.dma);
-
- if((pnp_info.fir_base == 0) ||
- (pnp_info.irq == -1) || (pnp_info.dma == -1)) {
- /* Returning an error will disable the device. Yuck ! */
- //return -EINVAL;
- pnp_succeeded = 0;
- }
-
- return 0;
-}
-#endif
-
-/*
- * Function nsc_ircc_setup (info)
- *
- * Returns non-negative on success.
- *
- */
-static int nsc_ircc_setup(chipio_t *info)
-{
- int version;
- int iobase = info->fir_base;
-
- /* Read the Module ID */
- switch_bank(iobase, BANK3);
- version = inb(iobase+MID);
-
- pr_debug("%s() Driver %s Found chip version %02x\n",
- __func__, driver_name, version);
-
- /* Should be 0x2? */
- if (0x20 != (version & 0xf0)) {
- net_err_ratelimited("%s, Wrong chip version %02x\n",
- driver_name, version);
- return -1;
- }
-
- /* Switch to advanced mode */
- switch_bank(iobase, BANK2);
- outb(ECR1_EXT_SL, iobase+ECR1);
- switch_bank(iobase, BANK0);
-
- /* Set FIFO threshold to TX17, RX16, reset and enable FIFO's */
- switch_bank(iobase, BANK0);
- outb(FCR_RXTH|FCR_TXTH|FCR_TXSR|FCR_RXSR|FCR_FIFO_EN, iobase+FCR);
-
- outb(0x03, iobase+LCR); /* 8 bit word length */
- outb(MCR_SIR, iobase+MCR); /* Start at SIR-mode, also clears LSR*/
-
- /* Set FIFO size to 32 */
- switch_bank(iobase, BANK2);
- outb(EXCR2_RFSIZ|EXCR2_TFSIZ, iobase+EXCR2);
-
- /* IRCR2: FEND_MD is not set */
- switch_bank(iobase, BANK5);
- outb(0x02, iobase+4);
-
- /* Make sure that some defaults are OK */
- switch_bank(iobase, BANK6);
- outb(0x20, iobase+0); /* Set 32 bits FIR CRC */
- outb(0x0a, iobase+1); /* Set MIR pulse width */
- outb(0x0d, iobase+2); /* Set SIR pulse width to 1.6us */
- outb(0x2a, iobase+4); /* Set beginning frag, and preamble length */
-
- /* Enable receive interrupts */
- switch_bank(iobase, BANK0);
- outb(IER_RXHDL_IE, iobase+IER);
-
- return 0;
-}
-
-/*
- * Function nsc_ircc_read_dongle_id (void)
- *
- * Try to read dongle identification. This procedure needs to be executed
- * once after power-on/reset. It also needs to be used whenever you suspect
- * that the user may have plugged/unplugged the IrDA Dongle.
- */
-static int nsc_ircc_read_dongle_id (int iobase)
-{
- int dongle_id;
- __u8 bank;
-
- bank = inb(iobase+BSR);
-
- /* Select Bank 7 */
- switch_bank(iobase, BANK7);
-
- /* IRCFG4: IRSL0_DS and IRSL21_DS are cleared */
- outb(0x00, iobase+7);
-
- /* ID0, 1, and 2 are pulled up/down very slowly */
- udelay(50);
-
- /* IRCFG1: read the ID bits */
- dongle_id = inb(iobase+4) & 0x0f;
-
-#ifdef BROKEN_DONGLE_ID
- if (dongle_id == 0x0a)
- dongle_id = 0x09;
-#endif
- /* Go back to bank 0 before returning */
- switch_bank(iobase, BANK0);
-
- outb(bank, iobase+BSR);
-
- return dongle_id;
-}
-
-/*
- * Function nsc_ircc_init_dongle_interface (iobase, dongle_id)
- *
- * This function initializes the dongle for the transceiver that is
- * used. This procedure needs to be executed once after
- * power-on/reset. It also needs to be used whenever you suspect that
- * the dongle is changed.
- */
-static void nsc_ircc_init_dongle_interface (int iobase, int dongle_id)
-{
- int bank;
-
- /* Save current bank */
- bank = inb(iobase+BSR);
-
- /* Select Bank 7 */
- switch_bank(iobase, BANK7);
-
- /* IRCFG4: set according to dongle_id */
- switch (dongle_id) {
- case 0x00: /* same as */
- case 0x01: /* Differential serial interface */
- pr_debug("%s(), %s not defined by irda yet\n",
- __func__, dongle_types[dongle_id]);
- break;
- case 0x02: /* same as */
- case 0x03: /* Reserved */
- pr_debug("%s(), %s not defined by irda yet\n",
- __func__, dongle_types[dongle_id]);
- break;
- case 0x04: /* Sharp RY5HD01 */
- break;
- case 0x05: /* Reserved, but this is what the Thinkpad reports */
- pr_debug("%s(), %s not defined by irda yet\n",
- __func__, dongle_types[dongle_id]);
- break;
- case 0x06: /* Single-ended serial interface */
- pr_debug("%s(), %s not defined by irda yet\n",
- __func__, dongle_types[dongle_id]);
- break;
- case 0x07: /* Consumer-IR only */
- pr_debug("%s(), %s is not for IrDA mode\n",
- __func__, dongle_types[dongle_id]);
- break;
- case 0x08: /* HP HSDL-2300, HP HSDL-3600/HSDL-3610 */
- pr_debug("%s(), %s\n",
- __func__, dongle_types[dongle_id]);
- break;
- case 0x09: /* IBM31T1100 or Temic TFDS6000/TFDS6500 */
- outb(0x28, iobase+7); /* Set irsl[0-2] as output */
- break;
- case 0x0A: /* same as */
- case 0x0B: /* Reserved */
- pr_debug("%s(), %s not defined by irda yet\n",
- __func__, dongle_types[dongle_id]);
- break;
- case 0x0C: /* same as */
- case 0x0D: /* HP HSDL-1100/HSDL-2100 */
- /*
- * Set irsl0 as input, irsl[1-2] as output, and separate
- * inputs are used for SIR and MIR/FIR
- */
- outb(0x48, iobase+7);
- break;
- case 0x0E: /* Supports SIR Mode only */
- outb(0x28, iobase+7); /* Set irsl[0-2] as output */
- break;
- case 0x0F: /* No dongle connected */
- pr_debug("%s(), %s\n",
- __func__, dongle_types[dongle_id]);
-
- switch_bank(iobase, BANK0);
- outb(0x62, iobase+MCR);
- break;
- default:
- pr_debug("%s(), invalid dongle_id %#x",
- __func__, dongle_id);
- }
-
- /* IRCFG1: IRSL1 and 2 are set to IrDA mode */
- outb(0x00, iobase+4);
-
- /* Restore bank register */
- outb(bank, iobase+BSR);
-
-} /* set_up_dongle_interface */
-
-/*
- * Function nsc_ircc_change_dongle_speed (iobase, speed, dongle_id)
- *
- * Change speed of the attach dongle
- *
- */
-static void nsc_ircc_change_dongle_speed(int iobase, int speed, int dongle_id)
-{
- __u8 bank;
-
- /* Save current bank */
- bank = inb(iobase+BSR);
-
- /* Select Bank 7 */
- switch_bank(iobase, BANK7);
-
- /* IRCFG1: set according to dongle_id */
- switch (dongle_id) {
- case 0x00: /* same as */
- case 0x01: /* Differential serial interface */
- pr_debug("%s(), %s not defined by irda yet\n",
- __func__, dongle_types[dongle_id]);
- break;
- case 0x02: /* same as */
- case 0x03: /* Reserved */
- pr_debug("%s(), %s not defined by irda yet\n",
- __func__, dongle_types[dongle_id]);
- break;
- case 0x04: /* Sharp RY5HD01 */
- break;
- case 0x05: /* Reserved */
- pr_debug("%s(), %s not defined by irda yet\n",
- __func__, dongle_types[dongle_id]);
- break;
- case 0x06: /* Single-ended serial interface */
- pr_debug("%s(), %s not defined by irda yet\n",
- __func__, dongle_types[dongle_id]);
- break;
- case 0x07: /* Consumer-IR only */
- pr_debug("%s(), %s is not for IrDA mode\n",
- __func__, dongle_types[dongle_id]);
- break;
- case 0x08: /* HP HSDL-2300, HP HSDL-3600/HSDL-3610 */
- pr_debug("%s(), %s\n",
- __func__, dongle_types[dongle_id]);
- outb(0x00, iobase+4);
- if (speed > 115200)
- outb(0x01, iobase+4);
- break;
- case 0x09: /* IBM31T1100 or Temic TFDS6000/TFDS6500 */
- outb(0x01, iobase+4);
-
- if (speed == 4000000) {
- /* There was a cli() there, but we now are already
- * under spin_lock_irqsave() - JeanII */
- outb(0x81, iobase+4);
- outb(0x80, iobase+4);
- } else
- outb(0x00, iobase+4);
- break;
- case 0x0A: /* same as */
- case 0x0B: /* Reserved */
- pr_debug("%s(), %s not defined by irda yet\n",
- __func__, dongle_types[dongle_id]);
- break;
- case 0x0C: /* same as */
- case 0x0D: /* HP HSDL-1100/HSDL-2100 */
- break;
- case 0x0E: /* Supports SIR Mode only */
- break;
- case 0x0F: /* No dongle connected */
- pr_debug("%s(), %s is not for IrDA mode\n",
- __func__, dongle_types[dongle_id]);
-
- switch_bank(iobase, BANK0);
- outb(0x62, iobase+MCR);
- break;
- default:
- pr_debug("%s(), invalid data_rate\n", __func__);
- }
- /* Restore bank register */
- outb(bank, iobase+BSR);
-}
-
-/*
- * Function nsc_ircc_change_speed (self, baud)
- *
- * Change the speed of the device
- *
- * This function *must* be called with irq off and spin-lock.
- */
-static __u8 nsc_ircc_change_speed(struct nsc_ircc_cb *self, __u32 speed)
-{
- struct net_device *dev;
- __u8 mcr = MCR_SIR;
- int iobase;
- __u8 bank;
- __u8 ier; /* Interrupt enable register */
-
- pr_debug("%s(), speed=%d\n", __func__, speed);
-
- IRDA_ASSERT(self != NULL, return 0;);
-
- dev = self->netdev;
- iobase = self->io.fir_base;
-
- /* Update accounting for new speed */
- self->io.speed = speed;
-
- /* Save current bank */
- bank = inb(iobase+BSR);
-
- /* Disable interrupts */
- switch_bank(iobase, BANK0);
- outb(0, iobase+IER);
-
- /* Select Bank 2 */
- switch_bank(iobase, BANK2);
-
- outb(0x00, iobase+BGDH);
- switch (speed) {
- case 9600: outb(0x0c, iobase+BGDL); break;
- case 19200: outb(0x06, iobase+BGDL); break;
- case 38400: outb(0x03, iobase+BGDL); break;
- case 57600: outb(0x02, iobase+BGDL); break;
- case 115200: outb(0x01, iobase+BGDL); break;
- case 576000:
- switch_bank(iobase, BANK5);
-
- /* IRCR2: MDRS is set */
- outb(inb(iobase+4) | 0x04, iobase+4);
-
- mcr = MCR_MIR;
- pr_debug("%s(), handling baud of 576000\n", __func__);
- break;
- case 1152000:
- mcr = MCR_MIR;
- pr_debug("%s(), handling baud of 1152000\n", __func__);
- break;
- case 4000000:
- mcr = MCR_FIR;
- pr_debug("%s(), handling baud of 4000000\n", __func__);
- break;
- default:
- mcr = MCR_FIR;
- pr_debug("%s(), unknown baud rate of %d\n",
- __func__, speed);
- break;
- }
-
- /* Set appropriate speed mode */
- switch_bank(iobase, BANK0);
- outb(mcr | MCR_TX_DFR, iobase+MCR);
-
- /* Give some hits to the transceiver */
- nsc_ircc_change_dongle_speed(iobase, speed, self->io.dongle_id);
-
- /* Set FIFO threshold to TX17, RX16 */
- switch_bank(iobase, BANK0);
- outb(0x00, iobase+FCR);
- outb(FCR_FIFO_EN, iobase+FCR);
- outb(FCR_RXTH| /* Set Rx FIFO threshold */
- FCR_TXTH| /* Set Tx FIFO threshold */
- FCR_TXSR| /* Reset Tx FIFO */
- FCR_RXSR| /* Reset Rx FIFO */
- FCR_FIFO_EN, /* Enable FIFOs */
- iobase+FCR);
-
- /* Set FIFO size to 32 */
- switch_bank(iobase, BANK2);
- outb(EXCR2_RFSIZ|EXCR2_TFSIZ, iobase+EXCR2);
-
- /* Enable some interrupts so we can receive frames */
- switch_bank(iobase, BANK0);
- if (speed > 115200) {
- /* Install FIR xmit handler */
- dev->netdev_ops = &nsc_ircc_fir_ops;
- ier = IER_SFIF_IE;
- nsc_ircc_dma_receive(self);
- } else {
- /* Install SIR xmit handler */
- dev->netdev_ops = &nsc_ircc_sir_ops;
- ier = IER_RXHDL_IE;
- }
- /* Set our current interrupt mask */
- outb(ier, iobase+IER);
-
- /* Restore BSR */
- outb(bank, iobase+BSR);
-
- /* Make sure interrupt handlers keep the proper interrupt mask */
- return ier;
-}
-
-/*
- * Function nsc_ircc_hard_xmit (skb, dev)
- *
- * Transmit the frame!
- *
- */
-static netdev_tx_t nsc_ircc_hard_xmit_sir(struct sk_buff *skb,
- struct net_device *dev)
-{
- struct nsc_ircc_cb *self;
- unsigned long flags;
- int iobase;
- __s32 speed;
- __u8 bank;
-
- self = netdev_priv(dev);
-
- IRDA_ASSERT(self != NULL, return NETDEV_TX_OK;);
-
- iobase = self->io.fir_base;
-
- netif_stop_queue(dev);
-
- /* Make sure tests *& speed change are atomic */
- spin_lock_irqsave(&self->lock, flags);
-
- /* Check if we need to change the speed */
- speed = irda_get_next_speed(skb);
- if ((speed != self->io.speed) && (speed != -1)) {
- /* Check for empty frame. */
- if (!skb->len) {
- /* If we just sent a frame, we get called before
- * the last bytes get out (because of the SIR FIFO).
- * If this is the case, let interrupt handler change
- * the speed itself... Jean II */
- if (self->io.direction == IO_RECV) {
- nsc_ircc_change_speed(self, speed);
- /* TODO : For SIR->SIR, the next packet
- * may get corrupted - Jean II */
- netif_wake_queue(dev);
- } else {
- self->new_speed = speed;
- /* Queue will be restarted after speed change
- * to make sure packets gets through the
- * proper xmit handler - Jean II */
- }
- netif_trans_update(dev);
- spin_unlock_irqrestore(&self->lock, flags);
- dev_kfree_skb(skb);
- return NETDEV_TX_OK;
- } else
- self->new_speed = speed;
- }
-
- /* Save current bank */
- bank = inb(iobase+BSR);
-
- self->tx_buff.data = self->tx_buff.head;
-
- self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data,
- self->tx_buff.truesize);
-
- dev->stats.tx_bytes += self->tx_buff.len;
-
- /* Add interrupt on tx low level (will fire immediately) */
- switch_bank(iobase, BANK0);
- outb(IER_TXLDL_IE, iobase+IER);
-
- /* Restore bank register */
- outb(bank, iobase+BSR);
-
- netif_trans_update(dev);
- spin_unlock_irqrestore(&self->lock, flags);
-
- dev_kfree_skb(skb);
-
- return NETDEV_TX_OK;
-}
-
-static netdev_tx_t nsc_ircc_hard_xmit_fir(struct sk_buff *skb,
- struct net_device *dev)
-{
- struct nsc_ircc_cb *self;
- unsigned long flags;
- int iobase;
- __s32 speed;
- __u8 bank;
- int mtt, diff;
-
- self = netdev_priv(dev);
- iobase = self->io.fir_base;
-
- netif_stop_queue(dev);
-
- /* Make sure tests *& speed change are atomic */
- spin_lock_irqsave(&self->lock, flags);
-
- /* Check if we need to change the speed */
- speed = irda_get_next_speed(skb);
- if ((speed != self->io.speed) && (speed != -1)) {
- /* Check for empty frame. */
- if (!skb->len) {
- /* If we are currently transmitting, defer to
- * interrupt handler. - Jean II */
- if(self->tx_fifo.len == 0) {
- nsc_ircc_change_speed(self, speed);
- netif_wake_queue(dev);
- } else {
- self->new_speed = speed;
- /* Keep queue stopped :
- * the speed change operation may change the
- * xmit handler, and we want to make sure
- * the next packet get through the proper
- * Tx path, so block the Tx queue until
- * the speed change has been done.
- * Jean II */
- }
- netif_trans_update(dev);
- spin_unlock_irqrestore(&self->lock, flags);
- dev_kfree_skb(skb);
- return NETDEV_TX_OK;
- } else {
- /* Change speed after current frame */
- self->new_speed = speed;
- }
- }
-
- /* Save current bank */
- bank = inb(iobase+BSR);
-
- /* Register and copy this frame to DMA memory */
- self->tx_fifo.queue[self->tx_fifo.free].start = self->tx_fifo.tail;
- self->tx_fifo.queue[self->tx_fifo.free].len = skb->len;
- self->tx_fifo.tail += skb->len;
-
- dev->stats.tx_bytes += skb->len;
-
- skb_copy_from_linear_data(skb, self->tx_fifo.queue[self->tx_fifo.free].start,
- skb->len);
- self->tx_fifo.len++;
- self->tx_fifo.free++;
-
- /* Start transmit only if there is currently no transmit going on */
- if (self->tx_fifo.len == 1) {
- /* Check if we must wait the min turn time or not */
- mtt = irda_get_mtt(skb);
- if (mtt) {
- /* Check how much time we have used already */
- diff = ktime_us_delta(ktime_get(), self->stamp);
-
- /* Check if the mtt is larger than the time we have
- * already used by all the protocol processing
- */
- if (mtt > diff) {
- mtt -= diff;
-
- /*
- * Use timer if delay larger than 125 us, and
- * use udelay for smaller values which should
- * be acceptable
- */
- if (mtt > 125) {
- /* Adjust for timer resolution */
- mtt = mtt / 125;
-
- /* Setup timer */
- switch_bank(iobase, BANK4);
- outb(mtt & 0xff, iobase+TMRL);
- outb((mtt >> 8) & 0x0f, iobase+TMRH);
-
- /* Start timer */
- outb(IRCR1_TMR_EN, iobase+IRCR1);
- self->io.direction = IO_XMIT;
-
- /* Enable timer interrupt */
- switch_bank(iobase, BANK0);
- outb(IER_TMR_IE, iobase+IER);
-
- /* Timer will take care of the rest */
- goto out;
- } else
- udelay(mtt);
- }
- }
- /* Enable DMA interrupt */
- switch_bank(iobase, BANK0);
- outb(IER_DMA_IE, iobase+IER);
-
- /* Transmit frame */
- nsc_ircc_dma_xmit(self, iobase);
- }
- out:
- /* Not busy transmitting anymore if window is not full,
- * and if we don't need to change speed */
- if ((self->tx_fifo.free < MAX_TX_WINDOW) && (self->new_speed == 0))
- netif_wake_queue(self->netdev);
-
- /* Restore bank register */
- outb(bank, iobase+BSR);
-
- netif_trans_update(dev);
- spin_unlock_irqrestore(&self->lock, flags);
- dev_kfree_skb(skb);
-
- return NETDEV_TX_OK;
-}
-
-/*
- * Function nsc_ircc_dma_xmit (self, iobase)
- *
- * Transmit data using DMA
- *
- */
-static void nsc_ircc_dma_xmit(struct nsc_ircc_cb *self, int iobase)
-{
- int bsr;
-
- /* Save current bank */
- bsr = inb(iobase+BSR);
-
- /* Disable DMA */
- switch_bank(iobase, BANK0);
- outb(inb(iobase+MCR) & ~MCR_DMA_EN, iobase+MCR);
-
- self->io.direction = IO_XMIT;
-
- /* Choose transmit DMA channel */
- switch_bank(iobase, BANK2);
- outb(ECR1_DMASWP|ECR1_DMANF|ECR1_EXT_SL, iobase+ECR1);
-
- irda_setup_dma(self->io.dma,
- ((u8 *)self->tx_fifo.queue[self->tx_fifo.ptr].start -
- self->tx_buff.head) + self->tx_buff_dma,
- self->tx_fifo.queue[self->tx_fifo.ptr].len,
- DMA_TX_MODE);
-
- /* Enable DMA and SIR interaction pulse */
- switch_bank(iobase, BANK0);
- outb(inb(iobase+MCR)|MCR_TX_DFR|MCR_DMA_EN|MCR_IR_PLS, iobase+MCR);
-
- /* Restore bank register */
- outb(bsr, iobase+BSR);
-}
-
-/*
- * Function nsc_ircc_pio_xmit (self, iobase)
- *
- * Transmit data using PIO. Returns the number of bytes that actually
- * got transferred
- *
- */
-static int nsc_ircc_pio_write(int iobase, __u8 *buf, int len, int fifo_size)
-{
- int actual = 0;
- __u8 bank;
-
- /* Save current bank */
- bank = inb(iobase+BSR);
-
- switch_bank(iobase, BANK0);
- if (!(inb_p(iobase+LSR) & LSR_TXEMP)) {
- pr_debug("%s(), warning, FIFO not empty yet!\n",
- __func__);
-
- /* FIFO may still be filled to the Tx interrupt threshold */
- fifo_size -= 17;
- }
-
- /* Fill FIFO with current frame */
- while ((fifo_size-- > 0) && (actual < len)) {
- /* Transmit next byte */
- outb(buf[actual++], iobase+TXD);
- }
-
- pr_debug("%s(), fifo_size %d ; %d sent of %d\n",
- __func__, fifo_size, actual, len);
-
- /* Restore bank */
- outb(bank, iobase+BSR);
-
- return actual;
-}
-
-/*
- * Function nsc_ircc_dma_xmit_complete (self)
- *
- * The transfer of a frame in finished. This function will only be called
- * by the interrupt handler
- *
- */
-static int nsc_ircc_dma_xmit_complete(struct nsc_ircc_cb *self)
-{
- int iobase;
- __u8 bank;
- int ret = TRUE;
-
- iobase = self->io.fir_base;
-
- /* Save current bank */
- bank = inb(iobase+BSR);
-
- /* Disable DMA */
- switch_bank(iobase, BANK0);
- outb(inb(iobase+MCR) & ~MCR_DMA_EN, iobase+MCR);
-
- /* Check for underrun! */
- if (inb(iobase+ASCR) & ASCR_TXUR) {
- self->netdev->stats.tx_errors++;
- self->netdev->stats.tx_fifo_errors++;
-
- /* Clear bit, by writing 1 into it */
- outb(ASCR_TXUR, iobase+ASCR);
- } else {
- self->netdev->stats.tx_packets++;
- }
-
- /* Finished with this frame, so prepare for next */
- self->tx_fifo.ptr++;
- self->tx_fifo.len--;
-
- /* Any frames to be sent back-to-back? */
- if (self->tx_fifo.len) {
- nsc_ircc_dma_xmit(self, iobase);
-
- /* Not finished yet! */
- ret = FALSE;
- } else {
- /* Reset Tx FIFO info */
- self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0;
- self->tx_fifo.tail = self->tx_buff.head;
- }
-
- /* Make sure we have room for more frames and
- * that we don't need to change speed */
- if ((self->tx_fifo.free < MAX_TX_WINDOW) && (self->new_speed == 0)) {
- /* Not busy transmitting anymore */
- /* Tell the network layer, that we can accept more frames */
- netif_wake_queue(self->netdev);
- }
-
- /* Restore bank */
- outb(bank, iobase+BSR);
-
- return ret;
-}
-
-/*
- * Function nsc_ircc_dma_receive (self)
- *
- * Get ready for receiving a frame. The device will initiate a DMA
- * if it starts to receive a frame.
- *
- */
-static int nsc_ircc_dma_receive(struct nsc_ircc_cb *self)
-{
- int iobase;
- __u8 bsr;
-
- iobase = self->io.fir_base;
-
- /* Reset Tx FIFO info */
- self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0;
- self->tx_fifo.tail = self->tx_buff.head;
-
- /* Save current bank */
- bsr = inb(iobase+BSR);
-
- /* Disable DMA */
- switch_bank(iobase, BANK0);
- outb(inb(iobase+MCR) & ~MCR_DMA_EN, iobase+MCR);
-
- /* Choose DMA Rx, DMA Fairness, and Advanced mode */
- switch_bank(iobase, BANK2);
- outb(ECR1_DMANF|ECR1_EXT_SL, iobase+ECR1);
-
- self->io.direction = IO_RECV;
- self->rx_buff.data = self->rx_buff.head;
-
- /* Reset Rx FIFO. This will also flush the ST_FIFO */
- switch_bank(iobase, BANK0);
- outb(FCR_RXSR|FCR_FIFO_EN, iobase+FCR);
-
- self->st_fifo.len = self->st_fifo.pending_bytes = 0;
- self->st_fifo.tail = self->st_fifo.head = 0;
-
- irda_setup_dma(self->io.dma, self->rx_buff_dma, self->rx_buff.truesize,
- DMA_RX_MODE);
-
- /* Enable DMA */
- switch_bank(iobase, BANK0);
- outb(inb(iobase+MCR)|MCR_DMA_EN, iobase+MCR);
-
- /* Restore bank register */
- outb(bsr, iobase+BSR);
-
- return 0;
-}
-
-/*
- * Function nsc_ircc_dma_receive_complete (self)
- *
- * Finished with receiving frames
- *
- *
- */
-static int nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase)
-{
- struct st_fifo *st_fifo;
- struct sk_buff *skb;
- __u8 status;
- __u8 bank;
- int len;
-
- st_fifo = &self->st_fifo;
-
- /* Save current bank */
- bank = inb(iobase+BSR);
-
- /* Read all entries in status FIFO */
- switch_bank(iobase, BANK5);
- while ((status = inb(iobase+FRM_ST)) & FRM_ST_VLD) {
- /* We must empty the status FIFO no matter what */
- len = inb(iobase+RFLFL) | ((inb(iobase+RFLFH) & 0x1f) << 8);
-
- if (st_fifo->tail >= MAX_RX_WINDOW) {
- pr_debug("%s(), window is full!\n", __func__);
- continue;
- }
-
- st_fifo->entries[st_fifo->tail].status = status;
- st_fifo->entries[st_fifo->tail].len = len;
- st_fifo->pending_bytes += len;
- st_fifo->tail++;
- st_fifo->len++;
- }
- /* Try to process all entries in status FIFO */
- while (st_fifo->len > 0) {
- /* Get first entry */
- status = st_fifo->entries[st_fifo->head].status;
- len = st_fifo->entries[st_fifo->head].len;
- st_fifo->pending_bytes -= len;
- st_fifo->head++;
- st_fifo->len--;
-
- /* Check for errors */
- if (status & FRM_ST_ERR_MSK) {
- if (status & FRM_ST_LOST_FR) {
- /* Add number of lost frames to stats */
- self->netdev->stats.rx_errors += len;
- } else {
- /* Skip frame */
- self->netdev->stats.rx_errors++;
-
- self->rx_buff.data += len;
-
- if (status & FRM_ST_MAX_LEN)
- self->netdev->stats.rx_length_errors++;
-
- if (status & FRM_ST_PHY_ERR)
- self->netdev->stats.rx_frame_errors++;
-
- if (status & FRM_ST_BAD_CRC)
- self->netdev->stats.rx_crc_errors++;
- }
- /* The errors below can be reported in both cases */
- if (status & FRM_ST_OVR1)
- self->netdev->stats.rx_fifo_errors++;
-
- if (status & FRM_ST_OVR2)
- self->netdev->stats.rx_fifo_errors++;
- } else {
- /*
- * First we must make sure that the frame we
- * want to deliver is all in main memory. If we
- * cannot tell, then we check if the Rx FIFO is
- * empty. If not then we will have to take a nap
- * and try again later.
- */
- if (st_fifo->pending_bytes < self->io.fifo_size) {
- switch_bank(iobase, BANK0);
- if (inb(iobase+LSR) & LSR_RXDA) {
- /* Put this entry back in fifo */
- st_fifo->head--;
- st_fifo->len++;
- st_fifo->pending_bytes += len;
- st_fifo->entries[st_fifo->head].status = status;
- st_fifo->entries[st_fifo->head].len = len;
- /*
- * DMA not finished yet, so try again
- * later, set timer value, resolution
- * 125 us
- */
- switch_bank(iobase, BANK4);
- outb(0x02, iobase+TMRL); /* x 125 us */
- outb(0x00, iobase+TMRH);
-
- /* Start timer */
- outb(IRCR1_TMR_EN, iobase+IRCR1);
-
- /* Restore bank register */
- outb(bank, iobase+BSR);
-
- return FALSE; /* I'll be back! */
- }
- }
-
- /*
- * Remember the time we received this frame, so we can
- * reduce the min turn time a bit since we will know
- * how much time we have used for protocol processing
- */
- self->stamp = ktime_get();
-
- skb = dev_alloc_skb(len+1);
- if (skb == NULL) {
- self->netdev->stats.rx_dropped++;
-
- /* Restore bank register */
- outb(bank, iobase+BSR);
-
- return FALSE;
- }
-
- /* Make sure IP header gets aligned */
- skb_reserve(skb, 1);
-
- /* Copy frame without CRC */
- if (self->io.speed < 4000000) {
- skb_put(skb, len-2);
- skb_copy_to_linear_data(skb,
- self->rx_buff.data,
- len - 2);
- } else {
- skb_put(skb, len-4);
- skb_copy_to_linear_data(skb,
- self->rx_buff.data,
- len - 4);
- }
-
- /* Move to next frame */
- self->rx_buff.data += len;
- self->netdev->stats.rx_bytes += len;
- self->netdev->stats.rx_packets++;
-
- skb->dev = self->netdev;
- skb_reset_mac_header(skb);
- skb->protocol = htons(ETH_P_IRDA);
- netif_rx(skb);
- }
- }
- /* Restore bank register */
- outb(bank, iobase+BSR);
-
- return TRUE;
-}
-
-/*
- * Function nsc_ircc_pio_receive (self)
- *
- * Receive all data in receiver FIFO
- *
- */
-static void nsc_ircc_pio_receive(struct nsc_ircc_cb *self)
-{
- __u8 byte;
- int iobase;
-
- iobase = self->io.fir_base;
-
- /* Receive all characters in Rx FIFO */
- do {
- byte = inb(iobase+RXD);
- async_unwrap_char(self->netdev, &self->netdev->stats,
- &self->rx_buff, byte);
- } while (inb(iobase+LSR) & LSR_RXDA); /* Data available */
-}
-
-/*
- * Function nsc_ircc_sir_interrupt (self, eir)
- *
- * Handle SIR interrupt
- *
- */
-static void nsc_ircc_sir_interrupt(struct nsc_ircc_cb *self, int eir)
-{
- int actual;
-
- /* Check if transmit FIFO is low on data */
- if (eir & EIR_TXLDL_EV) {
- /* Write data left in transmit buffer */
- actual = nsc_ircc_pio_write(self->io.fir_base,
- self->tx_buff.data,
- self->tx_buff.len,
- self->io.fifo_size);
- self->tx_buff.data += actual;
- self->tx_buff.len -= actual;
-
- self->io.direction = IO_XMIT;
-
- /* Check if finished */
- if (self->tx_buff.len > 0)
- self->ier = IER_TXLDL_IE;
- else {
-
- self->netdev->stats.tx_packets++;
- netif_wake_queue(self->netdev);
- self->ier = IER_TXEMP_IE;
- }
-
- }
- /* Check if transmission has completed */
- if (eir & EIR_TXEMP_EV) {
- /* Turn around and get ready to receive some data */
- self->io.direction = IO_RECV;
- self->ier = IER_RXHDL_IE;
- /* Check if we need to change the speed?
- * Need to be after self->io.direction to avoid race with
- * nsc_ircc_hard_xmit_sir() - Jean II */
- if (self->new_speed) {
- pr_debug("%s(), Changing speed!\n", __func__);
- self->ier = nsc_ircc_change_speed(self,
- self->new_speed);
- self->new_speed = 0;
- netif_wake_queue(self->netdev);
-
- /* Check if we are going to FIR */
- if (self->io.speed > 115200) {
- /* No need to do anymore SIR stuff */
- return;
- }
- }
- }
-
- /* Rx FIFO threshold or timeout */
- if (eir & EIR_RXHDL_EV) {
- nsc_ircc_pio_receive(self);
-
- /* Keep receiving */
- self->ier = IER_RXHDL_IE;
- }
-}
-
-/*
- * Function nsc_ircc_fir_interrupt (self, eir)
- *
- * Handle MIR/FIR interrupt
- *
- */
-static void nsc_ircc_fir_interrupt(struct nsc_ircc_cb *self, int iobase,
- int eir)
-{
- __u8 bank;
-
- bank = inb(iobase+BSR);
-
- /* Status FIFO event*/
- if (eir & EIR_SFIF_EV) {
- /* Check if DMA has finished */
- if (nsc_ircc_dma_receive_complete(self, iobase)) {
- /* Wait for next status FIFO interrupt */
- self->ier = IER_SFIF_IE;
- } else {
- self->ier = IER_SFIF_IE | IER_TMR_IE;
- }
- } else if (eir & EIR_TMR_EV) { /* Timer finished */
- /* Disable timer */
- switch_bank(iobase, BANK4);
- outb(0, iobase+IRCR1);
-
- /* Clear timer event */
- switch_bank(iobase, BANK0);
- outb(ASCR_CTE, iobase+ASCR);
-
- /* Check if this is a Tx timer interrupt */
- if (self->io.direction == IO_XMIT) {
- nsc_ircc_dma_xmit(self, iobase);
-
- /* Interrupt on DMA */
- self->ier = IER_DMA_IE;
- } else {
- /* Check (again) if DMA has finished */
- if (nsc_ircc_dma_receive_complete(self, iobase)) {
- self->ier = IER_SFIF_IE;
- } else {
- self->ier = IER_SFIF_IE | IER_TMR_IE;
- }
- }
- } else if (eir & EIR_DMA_EV) {
- /* Finished with all transmissions? */
- if (nsc_ircc_dma_xmit_complete(self)) {
- if(self->new_speed != 0) {
- /* As we stop the Tx queue, the speed change
- * need to be done when the Tx fifo is
- * empty. Ask for a Tx done interrupt */
- self->ier = IER_TXEMP_IE;
- } else {
- /* Check if there are more frames to be
- * transmitted */
- if (irda_device_txqueue_empty(self->netdev)) {
- /* Prepare for receive */
- nsc_ircc_dma_receive(self);
- self->ier = IER_SFIF_IE;
- } else
- net_warn_ratelimited("%s(), potential Tx queue lockup !\n",
- __func__);
- }
- } else {
- /* Not finished yet, so interrupt on DMA again */
- self->ier = IER_DMA_IE;
- }
- } else if (eir & EIR_TXEMP_EV) {
- /* The Tx FIFO has totally drained out, so now we can change
- * the speed... - Jean II */
- self->ier = nsc_ircc_change_speed(self, self->new_speed);
- self->new_speed = 0;
- netif_wake_queue(self->netdev);
- /* Note : nsc_ircc_change_speed() restarted Rx fifo */
- }
-
- outb(bank, iobase+BSR);
-}
-
-/*
- * Function nsc_ircc_interrupt (irq, dev_id, regs)
- *
- * An interrupt from the chip has arrived. Time to do some work
- *
- */
-static irqreturn_t nsc_ircc_interrupt(int irq, void *dev_id)
-{
- struct net_device *dev = dev_id;
- struct nsc_ircc_cb *self;
- __u8 bsr, eir;
- int iobase;
-
- self = netdev_priv(dev);
-
- spin_lock(&self->lock);
-
- iobase = self->io.fir_base;
-
- bsr = inb(iobase+BSR); /* Save current bank */
-
- switch_bank(iobase, BANK0);
- self->ier = inb(iobase+IER);
- eir = inb(iobase+EIR) & self->ier; /* Mask out the interesting ones */
-
- outb(0, iobase+IER); /* Disable interrupts */
-
- if (eir) {
- /* Dispatch interrupt handler for the current speed */
- if (self->io.speed > 115200)
- nsc_ircc_fir_interrupt(self, iobase, eir);
- else
- nsc_ircc_sir_interrupt(self, eir);
- }
-
- outb(self->ier, iobase+IER); /* Restore interrupts */
- outb(bsr, iobase+BSR); /* Restore bank register */
-
- spin_unlock(&self->lock);
- return IRQ_RETVAL(eir);
-}
-
-/*
- * Function nsc_ircc_is_receiving (self)
- *
- * Return TRUE is we are currently receiving a frame
- *
- */
-static int nsc_ircc_is_receiving(struct nsc_ircc_cb *self)
-{
- unsigned long flags;
- int status = FALSE;
- int iobase;
- __u8 bank;
-
- IRDA_ASSERT(self != NULL, return FALSE;);
-
- spin_lock_irqsave(&self->lock, flags);
-
- if (self->io.speed > 115200) {
- iobase = self->io.fir_base;
-
- /* Check if rx FIFO is not empty */
- bank = inb(iobase+BSR);
- switch_bank(iobase, BANK2);
- if ((inb(iobase+RXFLV) & 0x3f) != 0) {
- /* We are receiving something */
- status = TRUE;
- }
- outb(bank, iobase+BSR);
- } else
- status = (self->rx_buff.state != OUTSIDE_FRAME);
-
- spin_unlock_irqrestore(&self->lock, flags);
-
- return status;
-}
-
-/*
- * Function nsc_ircc_net_open (dev)
- *
- * Start the device
- *
- */
-static int nsc_ircc_net_open(struct net_device *dev)
-{
- struct nsc_ircc_cb *self;
- int iobase;
- char hwname[32];
- __u8 bank;
-
-
- IRDA_ASSERT(dev != NULL, return -1;);
- self = netdev_priv(dev);
-
- IRDA_ASSERT(self != NULL, return 0;);
-
- iobase = self->io.fir_base;
-
- if (request_irq(self->io.irq, nsc_ircc_interrupt, 0, dev->name, dev)) {
- net_warn_ratelimited("%s, unable to allocate irq=%d\n",
- driver_name, self->io.irq);
- return -EAGAIN;
- }
- /*
- * Always allocate the DMA channel after the IRQ, and clean up on
- * failure.
- */
- if (request_dma(self->io.dma, dev->name)) {
- net_warn_ratelimited("%s, unable to allocate dma=%d\n",
- driver_name, self->io.dma);
- free_irq(self->io.irq, dev);
- return -EAGAIN;
- }
-
- /* Save current bank */
- bank = inb(iobase+BSR);
-
- /* turn on interrupts */
- switch_bank(iobase, BANK0);
- outb(IER_LS_IE | IER_RXHDL_IE, iobase+IER);
-
- /* Restore bank register */
- outb(bank, iobase+BSR);
-
- /* Ready to play! */
- netif_start_queue(dev);
-
- /* Give self a hardware name */
- sprintf(hwname, "NSC-FIR @ 0x%03x", self->io.fir_base);
-
- /*
- * Open new IrLAP layer instance, now that everything should be
- * initialized properly
- */
- self->irlap = irlap_open(dev, &self->qos, hwname);
-
- return 0;
-}
-
-/*
- * Function nsc_ircc_net_close (dev)
- *
- * Stop the device
- *
- */
-static int nsc_ircc_net_close(struct net_device *dev)
-{
- struct nsc_ircc_cb *self;
- int iobase;
- __u8 bank;
-
-
- IRDA_ASSERT(dev != NULL, return -1;);
-
- self = netdev_priv(dev);
- IRDA_ASSERT(self != NULL, return 0;);
-
- /* Stop device */
- netif_stop_queue(dev);
-
- /* Stop and remove instance of IrLAP */
- if (self->irlap)
- irlap_close(self->irlap);
- self->irlap = NULL;
-
- iobase = self->io.fir_base;
-
- disable_dma(self->io.dma);
-
- /* Save current bank */
- bank = inb(iobase+BSR);
-
- /* Disable interrupts */
- switch_bank(iobase, BANK0);
- outb(0, iobase+IER);
-
- free_irq(self->io.irq, dev);
- free_dma(self->io.dma);
-
- /* Restore bank register */
- outb(bank, iobase+BSR);
-
- return 0;
-}
-
-/*
- * Function nsc_ircc_net_ioctl (dev, rq, cmd)
- *
- * Process IOCTL commands for this device
- *
- */
-static int nsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
- struct if_irda_req *irq = (struct if_irda_req *) rq;
- struct nsc_ircc_cb *self;
- unsigned long flags;
- int ret = 0;
-
- IRDA_ASSERT(dev != NULL, return -1;);
-
- self = netdev_priv(dev);
-
- IRDA_ASSERT(self != NULL, return -1;);
-
- pr_debug("%s(), %s, (cmd=0x%X)\n", __func__, dev->name, cmd);
-
- switch (cmd) {
- case SIOCSBANDWIDTH: /* Set bandwidth */
- if (!capable(CAP_NET_ADMIN)) {
- ret = -EPERM;
- break;
- }
- spin_lock_irqsave(&self->lock, flags);
- nsc_ircc_change_speed(self, irq->ifr_baudrate);
- spin_unlock_irqrestore(&self->lock, flags);
- break;
- case SIOCSMEDIABUSY: /* Set media busy */
- if (!capable(CAP_NET_ADMIN)) {
- ret = -EPERM;
- break;
- }
- irda_device_set_media_busy(self->netdev, TRUE);
- break;
- case SIOCGRECEIVING: /* Check if we are receiving right now */
- /* This is already protected */
- irq->ifr_receiving = nsc_ircc_is_receiving(self);
- break;
- default:
- ret = -EOPNOTSUPP;
- }
- return ret;
-}
-
-static int nsc_ircc_suspend(struct platform_device *dev, pm_message_t state)
-{
- struct nsc_ircc_cb *self = platform_get_drvdata(dev);
- int bank;
- unsigned long flags;
- int iobase = self->io.fir_base;
-
- if (self->io.suspended)
- return 0;
-
- pr_debug("%s, Suspending\n", driver_name);
-
- rtnl_lock();
- if (netif_running(self->netdev)) {
- netif_device_detach(self->netdev);
- spin_lock_irqsave(&self->lock, flags);
- /* Save current bank */
- bank = inb(iobase+BSR);
-
- /* Disable interrupts */
- switch_bank(iobase, BANK0);
- outb(0, iobase+IER);
-
- /* Restore bank register */
- outb(bank, iobase+BSR);
-
- spin_unlock_irqrestore(&self->lock, flags);
- free_irq(self->io.irq, self->netdev);
- disable_dma(self->io.dma);
- }
- self->io.suspended = 1;
- rtnl_unlock();
-
- return 0;
-}
-
-static int nsc_ircc_resume(struct platform_device *dev)
-{
- struct nsc_ircc_cb *self = platform_get_drvdata(dev);
- unsigned long flags;
-
- if (!self->io.suspended)
- return 0;
-
- pr_debug("%s, Waking up\n", driver_name);
-
- rtnl_lock();
- nsc_ircc_setup(&self->io);
- nsc_ircc_init_dongle_interface(self->io.fir_base, self->io.dongle_id);
-
- if (netif_running(self->netdev)) {
- if (request_irq(self->io.irq, nsc_ircc_interrupt, 0,
- self->netdev->name, self->netdev)) {
- net_warn_ratelimited("%s, unable to allocate irq=%d\n",
- driver_name, self->io.irq);
-
- /*
- * Don't fail resume process, just kill this
- * network interface
- */
- unregister_netdevice(self->netdev);
- } else {
- spin_lock_irqsave(&self->lock, flags);
- nsc_ircc_change_speed(self, self->io.speed);
- spin_unlock_irqrestore(&self->lock, flags);
- netif_device_attach(self->netdev);
- }
-
- } else {
- spin_lock_irqsave(&self->lock, flags);
- nsc_ircc_change_speed(self, 9600);
- spin_unlock_irqrestore(&self->lock, flags);
- }
- self->io.suspended = 0;
- rtnl_unlock();
-
- return 0;
-}
-
-MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
-MODULE_DESCRIPTION("NSC IrDA Device Driver");
-MODULE_LICENSE("GPL");
-
-
-module_param(qos_mtt_bits, int, 0);
-MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time");
-module_param_hw_array(io, int, ioport, NULL, 0);
-MODULE_PARM_DESC(io, "Base I/O addresses");
-module_param_hw_array(irq, int, irq, NULL, 0);
-MODULE_PARM_DESC(irq, "IRQ lines");
-module_param_hw_array(dma, int, dma, NULL, 0);
-MODULE_PARM_DESC(dma, "DMA channels");
-module_param(dongle_id, int, 0);
-MODULE_PARM_DESC(dongle_id, "Type-id of used dongle");
-
-module_init(nsc_ircc_init);
-module_exit(nsc_ircc_cleanup);
-
diff --git a/drivers/staging/irda/drivers/nsc-ircc.h b/drivers/staging/irda/drivers/nsc-ircc.h
deleted file mode 100644
index 7be5acb56532..000000000000
--- a/drivers/staging/irda/drivers/nsc-ircc.h
+++ /dev/null
@@ -1,281 +0,0 @@
-/*********************************************************************
- *
- * Filename: nsc-ircc.h
- * Version:
- * Description:
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Fri Nov 13 14:37:40 1998
- * Modified at: Sun Jan 23 17:47:00 2000
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>
- * Copyright (c) 1998 Lichen Wang, <lwang@actisys.com>
- * Copyright (c) 1998 Actisys Corp., www.actisys.com
- * All Rights Reserved
- *
- * 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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#ifndef NSC_IRCC_H
-#define NSC_IRCC_H
-
-#include <linux/ktime.h>
-
-#include <linux/spinlock.h>
-#include <linux/pm.h>
-#include <linux/types.h>
-#include <asm/io.h>
-
-/* Features for chips (set in driver_data) */
-#define NSC_FORCE_DONGLE_TYPE9 0x00000001
-
-/* DMA modes needed */
-#define DMA_TX_MODE 0x08 /* Mem to I/O, ++, demand. */
-#define DMA_RX_MODE 0x04 /* I/O to mem, ++, demand. */
-
-/* Config registers for the '108 */
-#define CFG_108_BAIC 0x00
-#define CFG_108_CSRT 0x01
-#define CFG_108_MCTL 0x02
-
-/* Config registers for the '338 */
-#define CFG_338_FER 0x00
-#define CFG_338_FAR 0x01
-#define CFG_338_PTR 0x02
-#define CFG_338_PNP0 0x1b
-#define CFG_338_PNP1 0x1c
-#define CFG_338_PNP3 0x4f
-
-/* Config registers for the '39x (in the logical device bank) */
-#define CFG_39X_LDN 0x07 /* Logical device number (Super I/O bank) */
-#define CFG_39X_SIOCF1 0x21 /* SuperI/O Config */
-#define CFG_39X_ACT 0x30 /* Device activation */
-#define CFG_39X_BASEH 0x60 /* Device base address (high bits) */
-#define CFG_39X_BASEL 0x61 /* Device base address (low bits) */
-#define CFG_39X_IRQNUM 0x70 /* Interrupt number & wake up enable */
-#define CFG_39X_IRQSEL 0x71 /* Interrupt select (edge/level + polarity) */
-#define CFG_39X_DMA0 0x74 /* DMA 0 configuration */
-#define CFG_39X_DMA1 0x75 /* DMA 1 configuration */
-#define CFG_39X_SPC 0xF0 /* Serial port configuration register */
-
-/* Flags for configuration register CRF0 */
-#define APEDCRC 0x02
-#define ENBNKSEL 0x01
-
-/* Set 0 */
-#define TXD 0x00 /* Transmit data port */
-#define RXD 0x00 /* Receive data port */
-
-/* Register 1 */
-#define IER 0x01 /* Interrupt Enable Register*/
-#define IER_RXHDL_IE 0x01 /* Receiver high data level interrupt */
-#define IER_TXLDL_IE 0x02 /* Transeiver low data level interrupt */
-#define IER_LS_IE 0x04//* Link Status Interrupt */
-#define IER_ETXURI 0x04 /* Tx underrun */
-#define IER_DMA_IE 0x10 /* DMA finished interrupt */
-#define IER_TXEMP_IE 0x20
-#define IER_SFIF_IE 0x40 /* Frame status FIFO intr */
-#define IER_TMR_IE 0x80 /* Timer event */
-
-#define FCR 0x02 /* (write only) */
-#define FCR_FIFO_EN 0x01 /* Enable FIFO's */
-#define FCR_RXSR 0x02 /* Rx FIFO soft reset */
-#define FCR_TXSR 0x04 /* Tx FIFO soft reset */
-#define FCR_RXTH 0x40 /* Rx FIFO threshold (set to 16) */
-#define FCR_TXTH 0x20 /* Tx FIFO threshold (set to 17) */
-
-#define EIR 0x02 /* (read only) */
-#define EIR_RXHDL_EV 0x01
-#define EIR_TXLDL_EV 0x02
-#define EIR_LS_EV 0x04
-#define EIR_DMA_EV 0x10
-#define EIR_TXEMP_EV 0x20
-#define EIR_SFIF_EV 0x40
-#define EIR_TMR_EV 0x80
-
-#define LCR 0x03 /* Link control register */
-#define LCR_WLS_8 0x03 /* 8 bits */
-
-#define BSR 0x03 /* Bank select register */
-#define BSR_BKSE 0x80
-#define BANK0 LCR_WLS_8 /* Must make sure that we set 8N1 */
-#define BANK1 0x80
-#define BANK2 0xe0
-#define BANK3 0xe4
-#define BANK4 0xe8
-#define BANK5 0xec
-#define BANK6 0xf0
-#define BANK7 0xf4
-
-#define MCR 0x04 /* Mode Control Register */
-#define MCR_MODE_MASK ~(0xd0)
-#define MCR_UART 0x00
-#define MCR_RESERVED 0x20
-#define MCR_SHARP_IR 0x40
-#define MCR_SIR 0x60
-#define MCR_MIR 0x80
-#define MCR_FIR 0xa0
-#define MCR_CEIR 0xb0
-#define MCR_IR_PLS 0x10
-#define MCR_DMA_EN 0x04
-#define MCR_EN_IRQ 0x08
-#define MCR_TX_DFR 0x08
-
-#define LSR 0x05 /* Link status register */
-#define LSR_RXDA 0x01 /* Receiver data available */
-#define LSR_TXRDY 0x20 /* Transmitter ready */
-#define LSR_TXEMP 0x40 /* Transmitter empty */
-
-#define ASCR 0x07 /* Auxiliary Status and Control Register */
-#define ASCR_RXF_TOUT 0x01 /* Rx FIFO timeout */
-#define ASCR_FEND_INF 0x02 /* Frame end bytes in rx FIFO */
-#define ASCR_S_EOT 0x04 /* Set end of transmission */
-#define ASCT_RXBSY 0x20 /* Rx busy */
-#define ASCR_TXUR 0x40 /* Transeiver underrun */
-#define ASCR_CTE 0x80 /* Clear timer event */
-
-/* Bank 2 */
-#define BGDL 0x00 /* Baud Generator Divisor Port (Low Byte) */
-#define BGDH 0x01 /* Baud Generator Divisor Port (High Byte) */
-
-#define ECR1 0x02 /* Extended Control Register 1 */
-#define ECR1_EXT_SL 0x01 /* Extended Mode Select */
-#define ECR1_DMANF 0x02 /* DMA Fairness */
-#define ECR1_DMATH 0x04 /* DMA Threshold */
-#define ECR1_DMASWP 0x08 /* DMA Swap */
-
-#define EXCR2 0x04
-#define EXCR2_TFSIZ 0x01 /* Rx FIFO size = 32 */
-#define EXCR2_RFSIZ 0x04 /* Tx FIFO size = 32 */
-
-#define TXFLV 0x06 /* Tx FIFO level */
-#define RXFLV 0x07 /* Rx FIFO level */
-
-/* Bank 3 */
-#define MID 0x00
-
-/* Bank 4 */
-#define TMRL 0x00 /* Timer low byte */
-#define TMRH 0x01 /* Timer high byte */
-#define IRCR1 0x02 /* Infrared control register 1 */
-#define IRCR1_TMR_EN 0x01 /* Timer enable */
-
-#define TFRLL 0x04
-#define TFRLH 0x05
-#define RFRLL 0x06
-#define RFRLH 0x07
-
-/* Bank 5 */
-#define IRCR2 0x04 /* Infrared control register 2 */
-#define IRCR2_MDRS 0x04 /* MIR data rate select */
-#define IRCR2_FEND_MD 0x20 /* */
-
-#define FRM_ST 0x05 /* Frame status FIFO */
-#define FRM_ST_VLD 0x80 /* Frame status FIFO data valid */
-#define FRM_ST_ERR_MSK 0x5f
-#define FRM_ST_LOST_FR 0x40 /* Frame lost */
-#define FRM_ST_MAX_LEN 0x10 /* Max frame len exceeded */
-#define FRM_ST_PHY_ERR 0x08 /* Physical layer error */
-#define FRM_ST_BAD_CRC 0x04
-#define FRM_ST_OVR1 0x02 /* Rx FIFO overrun */
-#define FRM_ST_OVR2 0x01 /* Frame status FIFO overrun */
-
-#define RFLFL 0x06
-#define RFLFH 0x07
-
-/* Bank 6 */
-#define IR_CFG2 0x00
-#define IR_CFG2_DIS_CRC 0x02
-
-/* Bank 7 */
-#define IRM_CR 0x07 /* Infrared module control register */
-#define IRM_CR_IRX_MSL 0x40
-#define IRM_CR_AF_MNT 0x80 /* Automatic format */
-
-/* NSC chip information */
-struct nsc_chip {
- char *name; /* Name of chipset */
- int cfg[3]; /* Config registers */
- u_int8_t cid_index; /* Chip identification index reg */
- u_int8_t cid_value; /* Chip identification expected value */
- u_int8_t cid_mask; /* Chip identification revision mask */
-
- /* Functions for probing and initializing the specific chip */
- int (*probe)(struct nsc_chip *chip, chipio_t *info);
- int (*init)(struct nsc_chip *chip, chipio_t *info);
-};
-typedef struct nsc_chip nsc_chip_t;
-
-/* For storing entries in the status FIFO */
-struct st_fifo_entry {
- int status;
- int len;
-};
-
-#define MAX_TX_WINDOW 7
-#define MAX_RX_WINDOW 7
-
-struct st_fifo {
- struct st_fifo_entry entries[MAX_RX_WINDOW];
- int pending_bytes;
- int head;
- int tail;
- int len;
-};
-
-struct frame_cb {
- void *start; /* Start of frame in DMA mem */
- int len; /* Length of frame in DMA mem */
-};
-
-struct tx_fifo {
- struct frame_cb queue[MAX_TX_WINDOW]; /* Info about frames in queue */
- int ptr; /* Currently being sent */
- int len; /* Length of queue */
- int free; /* Next free slot */
- void *tail; /* Next free start in DMA mem */
-};
-
-/* Private data for each instance */
-struct nsc_ircc_cb {
- struct st_fifo st_fifo; /* Info about received frames */
- struct tx_fifo tx_fifo; /* Info about frames to be transmitted */
-
- struct net_device *netdev; /* Yes! we are some kind of netdevice */
-
- struct irlap_cb *irlap; /* The link layer we are binded to */
- struct qos_info qos; /* QoS capabilities for this device */
-
- chipio_t io; /* IrDA controller information */
- iobuff_t tx_buff; /* Transmit buffer */
- iobuff_t rx_buff; /* Receive buffer */
- dma_addr_t tx_buff_dma;
- dma_addr_t rx_buff_dma;
-
- __u8 ier; /* Interrupt enable register */
-
- ktime_t stamp;
-
- spinlock_t lock; /* For serializing operations */
-
- __u32 new_speed;
- int index; /* Instance index */
-
- struct platform_device *pldev;
-};
-
-static inline void switch_bank(int iobase, int bank)
-{
- outb(bank, iobase+BSR);
-}
-
-#endif /* NSC_IRCC_H */
diff --git a/drivers/staging/irda/drivers/old_belkin-sir.c b/drivers/staging/irda/drivers/old_belkin-sir.c
deleted file mode 100644
index a7c2e990ae69..000000000000
--- a/drivers/staging/irda/drivers/old_belkin-sir.c
+++ /dev/null
@@ -1,146 +0,0 @@
-/*********************************************************************
- *
- * Filename: old_belkin.c
- * Version: 1.1
- * Description: Driver for the Belkin (old) SmartBeam dongle
- * Status: Experimental...
- * Author: Jean Tourrilhes <jt@hpl.hp.com>
- * Created at: 22/11/99
- * Modified at: Fri Dec 17 09:13:32 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1999 Jean Tourrilhes, All Rights Reserved.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-
-#include <net/irda/irda.h>
-// #include <net/irda/irda_device.h>
-
-#include "sir-dev.h"
-
-/*
- * Belkin is selling a dongle called the SmartBeam.
- * In fact, there is two hardware version of this dongle, of course with
- * the same name and looking the exactly same (grrr...).
- * I guess that I've got the old one, because inside I don't have
- * a jumper for IrDA/ASK...
- *
- * As far as I can make it from info on their web site, the old dongle
- * support only 9600 b/s, which make our life much simpler as far as
- * the driver is concerned, but you might not like it very much ;-)
- * The new SmartBeam does 115 kb/s, and I've not tested it...
- *
- * Belkin claim that the correct driver for the old dongle (in Windows)
- * is the generic Parallax 9500a driver, but the Linux LiteLink driver
- * fails for me (probably because Linux-IrDA doesn't rate fallback),
- * so I created this really dumb driver...
- *
- * In fact, this driver doesn't do much. The only thing it does is to
- * prevent Linux-IrDA to use any other speed than 9600 b/s ;-) This
- * driver is called "old_belkin" so that when the new SmartBeam is supported
- * its driver can be called "belkin" instead of "new_belkin".
- *
- * Note : this driver was written without any info/help from Belkin,
- * so a lot of info here might be totally wrong. Blame me ;-)
- */
-
-static int old_belkin_open(struct sir_dev *dev);
-static int old_belkin_close(struct sir_dev *dev);
-static int old_belkin_change_speed(struct sir_dev *dev, unsigned speed);
-static int old_belkin_reset(struct sir_dev *dev);
-
-static struct dongle_driver old_belkin = {
- .owner = THIS_MODULE,
- .driver_name = "Old Belkin SmartBeam",
- .type = IRDA_OLD_BELKIN_DONGLE,
- .open = old_belkin_open,
- .close = old_belkin_close,
- .reset = old_belkin_reset,
- .set_speed = old_belkin_change_speed,
-};
-
-static int __init old_belkin_sir_init(void)
-{
- return irda_register_dongle(&old_belkin);
-}
-
-static void __exit old_belkin_sir_cleanup(void)
-{
- irda_unregister_dongle(&old_belkin);
-}
-
-static int old_belkin_open(struct sir_dev *dev)
-{
- struct qos_info *qos = &dev->qos;
-
- /* Power on dongle */
- sirdev_set_dtr_rts(dev, TRUE, TRUE);
-
- /* Not too fast, please... */
- qos->baud_rate.bits &= IR_9600;
- /* Needs at least 10 ms (totally wild guess, can do probably better) */
- qos->min_turn_time.bits = 0x01;
- irda_qos_bits_to_value(qos);
-
- /* irda thread waits 50 msec for power settling */
-
- return 0;
-}
-
-static int old_belkin_close(struct sir_dev *dev)
-{
- /* Power off dongle */
- sirdev_set_dtr_rts(dev, FALSE, FALSE);
-
- return 0;
-}
-
-/*
- * Function old_belkin_change_speed (task)
- *
- * With only one speed available, not much to do...
- */
-static int old_belkin_change_speed(struct sir_dev *dev, unsigned speed)
-{
- dev->speed = 9600;
- return (speed==dev->speed) ? 0 : -EINVAL;
-}
-
-/*
- * Function old_belkin_reset (task)
- *
- * Reset the Old-Belkin type dongle.
- *
- */
-static int old_belkin_reset(struct sir_dev *dev)
-{
- /* This dongles speed "defaults" to 9600 bps ;-) */
- dev->speed = 9600;
-
- return 0;
-}
-
-MODULE_AUTHOR("Jean Tourrilhes <jt@hpl.hp.com>");
-MODULE_DESCRIPTION("Belkin (old) SmartBeam dongle driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("irda-dongle-7"); /* IRDA_OLD_BELKIN_DONGLE */
-
-module_init(old_belkin_sir_init);
-module_exit(old_belkin_sir_cleanup);
diff --git a/drivers/staging/irda/drivers/pxaficp_ir.c b/drivers/staging/irda/drivers/pxaficp_ir.c
deleted file mode 100644
index 2ea00a6531f9..000000000000
--- a/drivers/staging/irda/drivers/pxaficp_ir.c
+++ /dev/null
@@ -1,1075 +0,0 @@
-/*
- * linux/drivers/net/irda/pxaficp_ir.c
- *
- * Based on sa1100_ir.c by Russell King
- *
- * Changes copyright (C) 2003-2005 MontaVista Software, Inc.
- *
- * 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.
- *
- * Infra-red driver (SIR/FIR) for the PXA2xx embedded microprocessor
- *
- */
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/platform_device.h>
-#include <linux/clk.h>
-#include <linux/dmaengine.h>
-#include <linux/dma-mapping.h>
-#include <linux/dma/pxa-dma.h>
-#include <linux/gpio.h>
-#include <linux/slab.h>
-#include <linux/sched/clock.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irmod.h>
-#include <net/irda/wrapper.h>
-#include <net/irda/irda_device.h>
-
-#include <linux/platform_data/irda-pxaficp.h>
-#undef __REG
-#define __REG(x) ((x) & 0xffff)
-#include <mach/regs-uart.h>
-
-#define ICCR0 0x0000 /* ICP Control Register 0 */
-#define ICCR1 0x0004 /* ICP Control Register 1 */
-#define ICCR2 0x0008 /* ICP Control Register 2 */
-#define ICDR 0x000c /* ICP Data Register */
-#define ICSR0 0x0014 /* ICP Status Register 0 */
-#define ICSR1 0x0018 /* ICP Status Register 1 */
-
-#define ICCR0_AME (1 << 7) /* Address match enable */
-#define ICCR0_TIE (1 << 6) /* Transmit FIFO interrupt enable */
-#define ICCR0_RIE (1 << 5) /* Receive FIFO interrupt enable */
-#define ICCR0_RXE (1 << 4) /* Receive enable */
-#define ICCR0_TXE (1 << 3) /* Transmit enable */
-#define ICCR0_TUS (1 << 2) /* Transmit FIFO underrun select */
-#define ICCR0_LBM (1 << 1) /* Loopback mode */
-#define ICCR0_ITR (1 << 0) /* IrDA transmission */
-
-#define ICCR2_RXP (1 << 3) /* Receive Pin Polarity select */
-#define ICCR2_TXP (1 << 2) /* Transmit Pin Polarity select */
-#define ICCR2_TRIG (3 << 0) /* Receive FIFO Trigger threshold */
-#define ICCR2_TRIG_8 (0 << 0) /* >= 8 bytes */
-#define ICCR2_TRIG_16 (1 << 0) /* >= 16 bytes */
-#define ICCR2_TRIG_32 (2 << 0) /* >= 32 bytes */
-
-#define ICSR0_EOC (1 << 6) /* DMA End of Descriptor Chain */
-#define ICSR0_FRE (1 << 5) /* Framing error */
-#define ICSR0_RFS (1 << 4) /* Receive FIFO service request */
-#define ICSR0_TFS (1 << 3) /* Transnit FIFO service request */
-#define ICSR0_RAB (1 << 2) /* Receiver abort */
-#define ICSR0_TUR (1 << 1) /* Trunsmit FIFO underun */
-#define ICSR0_EIF (1 << 0) /* End/Error in FIFO */
-
-#define ICSR1_ROR (1 << 6) /* Receiver FIFO underrun */
-#define ICSR1_CRE (1 << 5) /* CRC error */
-#define ICSR1_EOF (1 << 4) /* End of frame */
-#define ICSR1_TNF (1 << 3) /* Transmit FIFO not full */
-#define ICSR1_RNE (1 << 2) /* Receive FIFO not empty */
-#define ICSR1_TBY (1 << 1) /* Tramsmiter busy flag */
-#define ICSR1_RSY (1 << 0) /* Recevier synchronized flag */
-
-#define IrSR_RXPL_NEG_IS_ZERO (1<<4)
-#define IrSR_RXPL_POS_IS_ZERO 0x0
-#define IrSR_TXPL_NEG_IS_ZERO (1<<3)
-#define IrSR_TXPL_POS_IS_ZERO 0x0
-#define IrSR_XMODE_PULSE_1_6 (1<<2)
-#define IrSR_XMODE_PULSE_3_16 0x0
-#define IrSR_RCVEIR_IR_MODE (1<<1)
-#define IrSR_RCVEIR_UART_MODE 0x0
-#define IrSR_XMITIR_IR_MODE (1<<0)
-#define IrSR_XMITIR_UART_MODE 0x0
-
-#define IrSR_IR_RECEIVE_ON (\
- IrSR_RXPL_NEG_IS_ZERO | \
- IrSR_TXPL_POS_IS_ZERO | \
- IrSR_XMODE_PULSE_3_16 | \
- IrSR_RCVEIR_IR_MODE | \
- IrSR_XMITIR_UART_MODE)
-
-#define IrSR_IR_TRANSMIT_ON (\
- IrSR_RXPL_NEG_IS_ZERO | \
- IrSR_TXPL_POS_IS_ZERO | \
- IrSR_XMODE_PULSE_3_16 | \
- IrSR_RCVEIR_UART_MODE | \
- IrSR_XMITIR_IR_MODE)
-
-/* macros for registers read/write */
-#define ficp_writel(irda, val, off) \
- do { \
- dev_vdbg(irda->dev, \
- "%s():%d ficp_writel(0x%x, %s)\n", \
- __func__, __LINE__, (val), #off); \
- writel_relaxed((val), (irda)->irda_base + (off)); \
- } while (0)
-
-#define ficp_readl(irda, off) \
- ({ \
- unsigned int _v; \
- _v = readl_relaxed((irda)->irda_base + (off)); \
- dev_vdbg(irda->dev, \
- "%s():%d ficp_readl(%s): 0x%x\n", \
- __func__, __LINE__, #off, _v); \
- _v; \
- })
-
-#define stuart_writel(irda, val, off) \
- do { \
- dev_vdbg(irda->dev, \
- "%s():%d stuart_writel(0x%x, %s)\n", \
- __func__, __LINE__, (val), #off); \
- writel_relaxed((val), (irda)->stuart_base + (off)); \
- } while (0)
-
-#define stuart_readl(irda, off) \
- ({ \
- unsigned int _v; \
- _v = readl_relaxed((irda)->stuart_base + (off)); \
- dev_vdbg(irda->dev, \
- "%s():%d stuart_readl(%s): 0x%x\n", \
- __func__, __LINE__, #off, _v); \
- _v; \
- })
-
-struct pxa_irda {
- int speed;
- int newspeed;
- unsigned long long last_clk;
-
- void __iomem *stuart_base;
- void __iomem *irda_base;
- unsigned char *dma_rx_buff;
- unsigned char *dma_tx_buff;
- dma_addr_t dma_rx_buff_phy;
- dma_addr_t dma_tx_buff_phy;
- unsigned int dma_tx_buff_len;
- struct dma_chan *txdma;
- struct dma_chan *rxdma;
- dma_cookie_t rx_cookie;
- dma_cookie_t tx_cookie;
- int drcmr_rx;
- int drcmr_tx;
-
- int uart_irq;
- int icp_irq;
-
- struct irlap_cb *irlap;
- struct qos_info qos;
-
- iobuff_t tx_buff;
- iobuff_t rx_buff;
-
- struct device *dev;
- struct pxaficp_platform_data *pdata;
- struct clk *fir_clk;
- struct clk *sir_clk;
- struct clk *cur_clk;
-};
-
-static int pxa_irda_set_speed(struct pxa_irda *si, int speed);
-
-static inline void pxa_irda_disable_clk(struct pxa_irda *si)
-{
- if (si->cur_clk)
- clk_disable_unprepare(si->cur_clk);
- si->cur_clk = NULL;
-}
-
-static inline void pxa_irda_enable_firclk(struct pxa_irda *si)
-{
- si->cur_clk = si->fir_clk;
- clk_prepare_enable(si->fir_clk);
-}
-
-static inline void pxa_irda_enable_sirclk(struct pxa_irda *si)
-{
- si->cur_clk = si->sir_clk;
- clk_prepare_enable(si->sir_clk);
-}
-
-
-#define IS_FIR(si) ((si)->speed >= 4000000)
-#define IRDA_FRAME_SIZE_LIMIT 2047
-
-static void pxa_irda_fir_dma_rx_irq(void *data);
-static void pxa_irda_fir_dma_tx_irq(void *data);
-
-inline static void pxa_irda_fir_dma_rx_start(struct pxa_irda *si)
-{
- struct dma_async_tx_descriptor *tx;
-
- tx = dmaengine_prep_slave_single(si->rxdma, si->dma_rx_buff_phy,
- IRDA_FRAME_SIZE_LIMIT, DMA_FROM_DEVICE,
- DMA_PREP_INTERRUPT);
- if (!tx) {
- dev_err(si->dev, "prep_slave_sg() failed\n");
- return;
- }
- tx->callback = pxa_irda_fir_dma_rx_irq;
- tx->callback_param = si;
- si->rx_cookie = dmaengine_submit(tx);
- dma_async_issue_pending(si->rxdma);
-}
-
-inline static void pxa_irda_fir_dma_tx_start(struct pxa_irda *si)
-{
- struct dma_async_tx_descriptor *tx;
-
- tx = dmaengine_prep_slave_single(si->txdma, si->dma_tx_buff_phy,
- si->dma_tx_buff_len, DMA_TO_DEVICE,
- DMA_PREP_INTERRUPT);
- if (!tx) {
- dev_err(si->dev, "prep_slave_sg() failed\n");
- return;
- }
- tx->callback = pxa_irda_fir_dma_tx_irq;
- tx->callback_param = si;
- si->tx_cookie = dmaengine_submit(tx);
- dma_async_issue_pending(si->rxdma);
-}
-
-/*
- * Set the IrDA communications mode.
- */
-static void pxa_irda_set_mode(struct pxa_irda *si, int mode)
-{
- if (si->pdata->transceiver_mode)
- si->pdata->transceiver_mode(si->dev, mode);
- else {
- if (gpio_is_valid(si->pdata->gpio_pwdown))
- gpio_set_value(si->pdata->gpio_pwdown,
- !(mode & IR_OFF) ^
- !si->pdata->gpio_pwdown_inverted);
- pxa2xx_transceiver_mode(si->dev, mode);
- }
-}
-
-/*
- * Set the IrDA communications speed.
- */
-static int pxa_irda_set_speed(struct pxa_irda *si, int speed)
-{
- unsigned long flags;
- unsigned int divisor;
-
- switch (speed) {
- case 9600: case 19200: case 38400:
- case 57600: case 115200:
-
- /* refer to PXA250/210 Developer's Manual 10-7 */
- /* BaudRate = 14.7456 MHz / (16*Divisor) */
- divisor = 14745600 / (16 * speed);
-
- local_irq_save(flags);
-
- if (IS_FIR(si)) {
- /* stop RX DMA */
- dmaengine_terminate_all(si->rxdma);
- /* disable FICP */
- ficp_writel(si, 0, ICCR0);
- pxa_irda_disable_clk(si);
-
- /* set board transceiver to SIR mode */
- pxa_irda_set_mode(si, IR_SIRMODE);
-
- /* enable the STUART clock */
- pxa_irda_enable_sirclk(si);
- }
-
- /* disable STUART first */
- stuart_writel(si, 0, STIER);
-
- /* access DLL & DLH */
- stuart_writel(si, stuart_readl(si, STLCR) | LCR_DLAB, STLCR);
- stuart_writel(si, divisor & 0xff, STDLL);
- stuart_writel(si, divisor >> 8, STDLH);
- stuart_writel(si, stuart_readl(si, STLCR) & ~LCR_DLAB, STLCR);
-
- si->speed = speed;
- stuart_writel(si, IrSR_IR_RECEIVE_ON | IrSR_XMODE_PULSE_1_6,
- STISR);
- stuart_writel(si, IER_UUE | IER_RLSE | IER_RAVIE | IER_RTIOE,
- STIER);
-
- local_irq_restore(flags);
- break;
-
- case 4000000:
- local_irq_save(flags);
-
- /* disable STUART */
- stuart_writel(si, 0, STIER);
- stuart_writel(si, 0, STISR);
- pxa_irda_disable_clk(si);
-
- /* disable FICP first */
- ficp_writel(si, 0, ICCR0);
-
- /* set board transceiver to FIR mode */
- pxa_irda_set_mode(si, IR_FIRMODE);
-
- /* enable the FICP clock */
- pxa_irda_enable_firclk(si);
-
- si->speed = speed;
- pxa_irda_fir_dma_rx_start(si);
- ficp_writel(si, ICCR0_ITR | ICCR0_RXE, ICCR0);
-
- local_irq_restore(flags);
- break;
-
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-/* SIR interrupt service routine. */
-static irqreturn_t pxa_irda_sir_irq(int irq, void *dev_id)
-{
- struct net_device *dev = dev_id;
- struct pxa_irda *si = netdev_priv(dev);
- int iir, lsr, data;
-
- iir = stuart_readl(si, STIIR);
-
- switch (iir & 0x0F) {
- case 0x06: /* Receiver Line Status */
- lsr = stuart_readl(si, STLSR);
- while (lsr & LSR_FIFOE) {
- data = stuart_readl(si, STRBR);
- if (lsr & (LSR_OE | LSR_PE | LSR_FE | LSR_BI)) {
- printk(KERN_DEBUG "pxa_ir: sir receiving error\n");
- dev->stats.rx_errors++;
- if (lsr & LSR_FE)
- dev->stats.rx_frame_errors++;
- if (lsr & LSR_OE)
- dev->stats.rx_fifo_errors++;
- } else {
- dev->stats.rx_bytes++;
- async_unwrap_char(dev, &dev->stats,
- &si->rx_buff, data);
- }
- lsr = stuart_readl(si, STLSR);
- }
- si->last_clk = sched_clock();
- break;
-
- case 0x04: /* Received Data Available */
- /* forth through */
-
- case 0x0C: /* Character Timeout Indication */
- do {
- dev->stats.rx_bytes++;
- async_unwrap_char(dev, &dev->stats, &si->rx_buff,
- stuart_readl(si, STRBR));
- } while (stuart_readl(si, STLSR) & LSR_DR);
- si->last_clk = sched_clock();
- break;
-
- case 0x02: /* Transmit FIFO Data Request */
- while ((si->tx_buff.len) &&
- (stuart_readl(si, STLSR) & LSR_TDRQ)) {
- stuart_writel(si, *si->tx_buff.data++, STTHR);
- si->tx_buff.len -= 1;
- }
-
- if (si->tx_buff.len == 0) {
- dev->stats.tx_packets++;
- dev->stats.tx_bytes += si->tx_buff.data - si->tx_buff.head;
-
- /* We need to ensure that the transmitter has finished. */
- while ((stuart_readl(si, STLSR) & LSR_TEMT) == 0)
- cpu_relax();
- si->last_clk = sched_clock();
-
- /*
- * Ok, we've finished transmitting. Now enable
- * the receiver. Sometimes we get a receive IRQ
- * immediately after a transmit...
- */
- if (si->newspeed) {
- pxa_irda_set_speed(si, si->newspeed);
- si->newspeed = 0;
- } else {
- /* enable IR Receiver, disable IR Transmitter */
- stuart_writel(si, IrSR_IR_RECEIVE_ON |
- IrSR_XMODE_PULSE_1_6, STISR);
- /* enable STUART and receive interrupts */
- stuart_writel(si, IER_UUE | IER_RLSE |
- IER_RAVIE | IER_RTIOE, STIER);
- }
- /* I'm hungry! */
- netif_wake_queue(dev);
- }
- break;
- }
-
- return IRQ_HANDLED;
-}
-
-/* FIR Receive DMA interrupt handler */
-static void pxa_irda_fir_dma_rx_irq(void *data)
-{
- struct net_device *dev = data;
- struct pxa_irda *si = netdev_priv(dev);
-
- dmaengine_terminate_all(si->rxdma);
- netdev_dbg(dev, "pxa_ir: fir rx dma bus error\n");
-}
-
-/* FIR Transmit DMA interrupt handler */
-static void pxa_irda_fir_dma_tx_irq(void *data)
-{
- struct net_device *dev = data;
- struct pxa_irda *si = netdev_priv(dev);
-
- dmaengine_terminate_all(si->txdma);
- if (dmaengine_tx_status(si->txdma, si->tx_cookie, NULL) == DMA_ERROR) {
- dev->stats.tx_errors++;
- } else {
- dev->stats.tx_packets++;
- dev->stats.tx_bytes += si->dma_tx_buff_len;
- }
-
- while (ficp_readl(si, ICSR1) & ICSR1_TBY)
- cpu_relax();
- si->last_clk = sched_clock();
-
- /*
- * HACK: It looks like the TBY bit is dropped too soon.
- * Without this delay things break.
- */
- udelay(120);
-
- if (si->newspeed) {
- pxa_irda_set_speed(si, si->newspeed);
- si->newspeed = 0;
- } else {
- int i = 64;
-
- ficp_writel(si, 0, ICCR0);
- pxa_irda_fir_dma_rx_start(si);
- while ((ficp_readl(si, ICSR1) & ICSR1_RNE) && i--)
- ficp_readl(si, ICDR);
- ficp_writel(si, ICCR0_ITR | ICCR0_RXE, ICCR0);
-
- if (i < 0)
- printk(KERN_ERR "pxa_ir: cannot clear Rx FIFO!\n");
- }
- netif_wake_queue(dev);
-}
-
-/* EIF(Error in FIFO/End in Frame) handler for FIR */
-static void pxa_irda_fir_irq_eif(struct pxa_irda *si, struct net_device *dev, int icsr0)
-{
- unsigned int len, stat, data;
- struct dma_tx_state state;
-
- /* Get the current data position. */
-
- dmaengine_tx_status(si->rxdma, si->rx_cookie, &state);
- len = IRDA_FRAME_SIZE_LIMIT - state.residue;
-
- do {
- /* Read Status, and then Data. */
- stat = ficp_readl(si, ICSR1);
- rmb();
- data = ficp_readl(si, ICDR);
-
- if (stat & (ICSR1_CRE | ICSR1_ROR)) {
- dev->stats.rx_errors++;
- if (stat & ICSR1_CRE) {
- printk(KERN_DEBUG "pxa_ir: fir receive CRC error\n");
- dev->stats.rx_crc_errors++;
- }
- if (stat & ICSR1_ROR) {
- printk(KERN_DEBUG "pxa_ir: fir receive overrun\n");
- dev->stats.rx_over_errors++;
- }
- } else {
- si->dma_rx_buff[len++] = data;
- }
- /* If we hit the end of frame, there's no point in continuing. */
- if (stat & ICSR1_EOF)
- break;
- } while (ficp_readl(si, ICSR0) & ICSR0_EIF);
-
- if (stat & ICSR1_EOF) {
- /* end of frame. */
- struct sk_buff *skb;
-
- if (icsr0 & ICSR0_FRE) {
- printk(KERN_ERR "pxa_ir: dropping erroneous frame\n");
- dev->stats.rx_dropped++;
- return;
- }
-
- skb = alloc_skb(len+1,GFP_ATOMIC);
- if (!skb) {
- printk(KERN_ERR "pxa_ir: fir out of memory for receive skb\n");
- dev->stats.rx_dropped++;
- return;
- }
-
- /* Align IP header to 20 bytes */
- skb_reserve(skb, 1);
- skb_copy_to_linear_data(skb, si->dma_rx_buff, len);
- skb_put(skb, len);
-
- /* Feed it to IrLAP */
- skb->dev = dev;
- skb_reset_mac_header(skb);
- skb->protocol = htons(ETH_P_IRDA);
- netif_rx(skb);
-
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += len;
- }
-}
-
-/* FIR interrupt handler */
-static irqreturn_t pxa_irda_fir_irq(int irq, void *dev_id)
-{
- struct net_device *dev = dev_id;
- struct pxa_irda *si = netdev_priv(dev);
- int icsr0, i = 64;
-
- /* stop RX DMA */
- dmaengine_terminate_all(si->rxdma);
- si->last_clk = sched_clock();
- icsr0 = ficp_readl(si, ICSR0);
-
- if (icsr0 & (ICSR0_FRE | ICSR0_RAB)) {
- if (icsr0 & ICSR0_FRE) {
- printk(KERN_DEBUG "pxa_ir: fir receive frame error\n");
- dev->stats.rx_frame_errors++;
- } else {
- printk(KERN_DEBUG "pxa_ir: fir receive abort\n");
- dev->stats.rx_errors++;
- }
- ficp_writel(si, icsr0 & (ICSR0_FRE | ICSR0_RAB), ICSR0);
- }
-
- if (icsr0 & ICSR0_EIF) {
- /* An error in FIFO occurred, or there is a end of frame */
- pxa_irda_fir_irq_eif(si, dev, icsr0);
- }
-
- ficp_writel(si, 0, ICCR0);
- pxa_irda_fir_dma_rx_start(si);
- while ((ficp_readl(si, ICSR1) & ICSR1_RNE) && i--)
- ficp_readl(si, ICDR);
- ficp_writel(si, ICCR0_ITR | ICCR0_RXE, ICCR0);
-
- if (i < 0)
- printk(KERN_ERR "pxa_ir: cannot clear Rx FIFO!\n");
-
- return IRQ_HANDLED;
-}
-
-/* hard_xmit interface of irda device */
-static int pxa_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct pxa_irda *si = netdev_priv(dev);
- int speed = irda_get_next_speed(skb);
-
- /*
- * Does this packet contain a request to change the interface
- * speed? If so, remember it until we complete the transmission
- * of this frame.
- */
- if (speed != si->speed && speed != -1)
- si->newspeed = speed;
-
- /*
- * If this is an empty frame, we can bypass a lot.
- */
- if (skb->len == 0) {
- if (si->newspeed) {
- si->newspeed = 0;
- pxa_irda_set_speed(si, speed);
- }
- dev_kfree_skb(skb);
- return NETDEV_TX_OK;
- }
-
- netif_stop_queue(dev);
-
- if (!IS_FIR(si)) {
- si->tx_buff.data = si->tx_buff.head;
- si->tx_buff.len = async_wrap_skb(skb, si->tx_buff.data, si->tx_buff.truesize);
-
- /* Disable STUART interrupts and switch to transmit mode. */
- stuart_writel(si, 0, STIER);
- stuart_writel(si, IrSR_IR_TRANSMIT_ON | IrSR_XMODE_PULSE_1_6,
- STISR);
-
- /* enable STUART and transmit interrupts */
- stuart_writel(si, IER_UUE | IER_TIE, STIER);
- } else {
- unsigned long mtt = irda_get_mtt(skb);
-
- si->dma_tx_buff_len = skb->len;
- skb_copy_from_linear_data(skb, si->dma_tx_buff, skb->len);
-
- if (mtt)
- while ((sched_clock() - si->last_clk) * 1000 < mtt)
- cpu_relax();
-
- /* stop RX DMA, disable FICP */
- dmaengine_terminate_all(si->rxdma);
- ficp_writel(si, 0, ICCR0);
-
- pxa_irda_fir_dma_tx_start(si);
- ficp_writel(si, ICCR0_ITR | ICCR0_TXE, ICCR0);
- }
-
- dev_kfree_skb(skb);
- return NETDEV_TX_OK;
-}
-
-static int pxa_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd)
-{
- struct if_irda_req *rq = (struct if_irda_req *)ifreq;
- struct pxa_irda *si = netdev_priv(dev);
- int ret;
-
- switch (cmd) {
- case SIOCSBANDWIDTH:
- ret = -EPERM;
- if (capable(CAP_NET_ADMIN)) {
- /*
- * We are unable to set the speed if the
- * device is not running.
- */
- if (netif_running(dev)) {
- ret = pxa_irda_set_speed(si,
- rq->ifr_baudrate);
- } else {
- printk(KERN_INFO "pxa_ir: SIOCSBANDWIDTH: !netif_running\n");
- ret = 0;
- }
- }
- break;
-
- case SIOCSMEDIABUSY:
- ret = -EPERM;
- if (capable(CAP_NET_ADMIN)) {
- irda_device_set_media_busy(dev, TRUE);
- ret = 0;
- }
- break;
-
- case SIOCGRECEIVING:
- ret = 0;
- rq->ifr_receiving = IS_FIR(si) ? 0
- : si->rx_buff.state != OUTSIDE_FRAME;
- break;
-
- default:
- ret = -EOPNOTSUPP;
- break;
- }
-
- return ret;
-}
-
-static void pxa_irda_startup(struct pxa_irda *si)
-{
- /* Disable STUART interrupts */
- stuart_writel(si, 0, STIER);
- /* enable STUART interrupt to the processor */
- stuart_writel(si, MCR_OUT2, STMCR);
- /* configure SIR frame format: StartBit - Data 7 ... Data 0 - Stop Bit */
- stuart_writel(si, LCR_WLS0 | LCR_WLS1, STLCR);
- /* enable FIFO, we use FIFO to improve performance */
- stuart_writel(si, FCR_TRFIFOE | FCR_ITL_32, STFCR);
-
- /* disable FICP */
- ficp_writel(si, 0, ICCR0);
- /* configure FICP ICCR2 */
- ficp_writel(si, ICCR2_TXP | ICCR2_TRIG_32, ICCR2);
-
- /* force SIR reinitialization */
- si->speed = 4000000;
- pxa_irda_set_speed(si, 9600);
-
- printk(KERN_DEBUG "pxa_ir: irda startup\n");
-}
-
-static void pxa_irda_shutdown(struct pxa_irda *si)
-{
- unsigned long flags;
-
- local_irq_save(flags);
-
- /* disable STUART and interrupt */
- stuart_writel(si, 0, STIER);
- /* disable STUART SIR mode */
- stuart_writel(si, 0, STISR);
-
- /* disable DMA */
- dmaengine_terminate_all(si->rxdma);
- dmaengine_terminate_all(si->txdma);
- /* disable FICP */
- ficp_writel(si, 0, ICCR0);
-
- /* disable the STUART or FICP clocks */
- pxa_irda_disable_clk(si);
-
- local_irq_restore(flags);
-
- /* power off board transceiver */
- pxa_irda_set_mode(si, IR_OFF);
-
- printk(KERN_DEBUG "pxa_ir: irda shutdown\n");
-}
-
-static int pxa_irda_start(struct net_device *dev)
-{
- struct pxa_irda *si = netdev_priv(dev);
- dma_cap_mask_t mask;
- struct dma_slave_config config;
- struct pxad_param param;
- int err;
-
- si->speed = 9600;
-
- err = request_irq(si->uart_irq, pxa_irda_sir_irq, 0, dev->name, dev);
- if (err)
- goto err_irq1;
-
- err = request_irq(si->icp_irq, pxa_irda_fir_irq, 0, dev->name, dev);
- if (err)
- goto err_irq2;
-
- /*
- * The interrupt must remain disabled for now.
- */
- disable_irq(si->uart_irq);
- disable_irq(si->icp_irq);
-
- err = -EBUSY;
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
- param.prio = PXAD_PRIO_LOWEST;
-
- memset(&config, 0, sizeof(config));
- config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
- config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
- config.src_addr = (dma_addr_t)si->irda_base + ICDR;
- config.dst_addr = (dma_addr_t)si->irda_base + ICDR;
- config.src_maxburst = 32;
- config.dst_maxburst = 32;
-
- param.drcmr = si->drcmr_rx;
- si->rxdma = dma_request_slave_channel_compat(mask, pxad_filter_fn,
- &param, &dev->dev, "rx");
- if (!si->rxdma)
- goto err_rx_dma;
-
- param.drcmr = si->drcmr_tx;
- si->txdma = dma_request_slave_channel_compat(mask, pxad_filter_fn,
- &param, &dev->dev, "tx");
- if (!si->txdma)
- goto err_tx_dma;
-
- err = dmaengine_slave_config(si->rxdma, &config);
- if (err)
- goto err_dma_rx_buff;
- err = dmaengine_slave_config(si->txdma, &config);
- if (err)
- goto err_dma_rx_buff;
-
- err = -ENOMEM;
- si->dma_rx_buff = dma_alloc_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT,
- &si->dma_rx_buff_phy, GFP_KERNEL);
- if (!si->dma_rx_buff)
- goto err_dma_rx_buff;
-
- si->dma_tx_buff = dma_alloc_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT,
- &si->dma_tx_buff_phy, GFP_KERNEL);
- if (!si->dma_tx_buff)
- goto err_dma_tx_buff;
-
- /* Setup the serial port for the initial speed. */
- pxa_irda_startup(si);
-
- /*
- * Open a new IrLAP layer instance.
- */
- si->irlap = irlap_open(dev, &si->qos, "pxa");
- err = -ENOMEM;
- if (!si->irlap)
- goto err_irlap;
-
- /*
- * Now enable the interrupt and start the queue
- */
- enable_irq(si->uart_irq);
- enable_irq(si->icp_irq);
- netif_start_queue(dev);
-
- printk(KERN_DEBUG "pxa_ir: irda driver opened\n");
-
- return 0;
-
-err_irlap:
- pxa_irda_shutdown(si);
- dma_free_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT, si->dma_tx_buff, si->dma_tx_buff_phy);
-err_dma_tx_buff:
- dma_free_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT, si->dma_rx_buff, si->dma_rx_buff_phy);
-err_dma_rx_buff:
- dma_release_channel(si->txdma);
-err_tx_dma:
- dma_release_channel(si->rxdma);
-err_rx_dma:
- free_irq(si->icp_irq, dev);
-err_irq2:
- free_irq(si->uart_irq, dev);
-err_irq1:
-
- return err;
-}
-
-static int pxa_irda_stop(struct net_device *dev)
-{
- struct pxa_irda *si = netdev_priv(dev);
-
- netif_stop_queue(dev);
-
- pxa_irda_shutdown(si);
-
- /* Stop IrLAP */
- if (si->irlap) {
- irlap_close(si->irlap);
- si->irlap = NULL;
- }
-
- free_irq(si->uart_irq, dev);
- free_irq(si->icp_irq, dev);
-
- dmaengine_terminate_all(si->rxdma);
- dmaengine_terminate_all(si->txdma);
- dma_release_channel(si->rxdma);
- dma_release_channel(si->txdma);
-
- if (si->dma_rx_buff)
- dma_free_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT, si->dma_tx_buff, si->dma_tx_buff_phy);
- if (si->dma_tx_buff)
- dma_free_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT, si->dma_rx_buff, si->dma_rx_buff_phy);
-
- printk(KERN_DEBUG "pxa_ir: irda driver closed\n");
- return 0;
-}
-
-static int pxa_irda_suspend(struct platform_device *_dev, pm_message_t state)
-{
- struct net_device *dev = platform_get_drvdata(_dev);
- struct pxa_irda *si;
-
- if (dev && netif_running(dev)) {
- si = netdev_priv(dev);
- netif_device_detach(dev);
- pxa_irda_shutdown(si);
- }
-
- return 0;
-}
-
-static int pxa_irda_resume(struct platform_device *_dev)
-{
- struct net_device *dev = platform_get_drvdata(_dev);
- struct pxa_irda *si;
-
- if (dev && netif_running(dev)) {
- si = netdev_priv(dev);
- pxa_irda_startup(si);
- netif_device_attach(dev);
- netif_wake_queue(dev);
- }
-
- return 0;
-}
-
-
-static int pxa_irda_init_iobuf(iobuff_t *io, int size)
-{
- io->head = kmalloc(size, GFP_KERNEL | GFP_DMA);
- if (io->head != NULL) {
- io->truesize = size;
- io->in_frame = FALSE;
- io->state = OUTSIDE_FRAME;
- io->data = io->head;
- }
- return io->head ? 0 : -ENOMEM;
-}
-
-static const struct net_device_ops pxa_irda_netdev_ops = {
- .ndo_open = pxa_irda_start,
- .ndo_stop = pxa_irda_stop,
- .ndo_start_xmit = pxa_irda_hard_xmit,
- .ndo_do_ioctl = pxa_irda_ioctl,
-};
-
-static int pxa_irda_probe(struct platform_device *pdev)
-{
- struct net_device *dev;
- struct resource *res;
- struct pxa_irda *si;
- void __iomem *ficp, *stuart;
- unsigned int baudrate_mask;
- int err;
-
- if (!pdev->dev.platform_data)
- return -ENODEV;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- ficp = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(ficp)) {
- dev_err(&pdev->dev, "resource ficp not defined\n");
- return PTR_ERR(ficp);
- }
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- stuart = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(stuart)) {
- dev_err(&pdev->dev, "resource stuart not defined\n");
- return PTR_ERR(stuart);
- }
-
- dev = alloc_irdadev(sizeof(struct pxa_irda));
- if (!dev) {
- err = -ENOMEM;
- goto err_mem_1;
- }
-
- SET_NETDEV_DEV(dev, &pdev->dev);
- si = netdev_priv(dev);
- si->dev = &pdev->dev;
- si->pdata = pdev->dev.platform_data;
-
- si->irda_base = ficp;
- si->stuart_base = stuart;
- si->uart_irq = platform_get_irq(pdev, 0);
- si->icp_irq = platform_get_irq(pdev, 1);
-
- si->sir_clk = devm_clk_get(&pdev->dev, "UARTCLK");
- si->fir_clk = devm_clk_get(&pdev->dev, "FICPCLK");
- if (IS_ERR(si->sir_clk) || IS_ERR(si->fir_clk)) {
- err = PTR_ERR(IS_ERR(si->sir_clk) ? si->sir_clk : si->fir_clk);
- goto err_mem_4;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (res)
- si->drcmr_rx = res->start;
- res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
- if (res)
- si->drcmr_tx = res->start;
-
- /*
- * Initialise the SIR buffers
- */
- err = pxa_irda_init_iobuf(&si->rx_buff, 14384);
- if (err)
- goto err_mem_4;
- err = pxa_irda_init_iobuf(&si->tx_buff, 4000);
- if (err)
- goto err_mem_5;
-
- if (gpio_is_valid(si->pdata->gpio_pwdown)) {
- err = gpio_request(si->pdata->gpio_pwdown, "IrDA switch");
- if (err)
- goto err_startup;
- err = gpio_direction_output(si->pdata->gpio_pwdown,
- !si->pdata->gpio_pwdown_inverted);
- if (err) {
- gpio_free(si->pdata->gpio_pwdown);
- goto err_startup;
- }
- }
-
- if (si->pdata->startup) {
- err = si->pdata->startup(si->dev);
- if (err)
- goto err_startup;
- }
-
- if (gpio_is_valid(si->pdata->gpio_pwdown) && si->pdata->startup)
- dev_warn(si->dev, "gpio_pwdown and startup() both defined!\n");
-
- dev->netdev_ops = &pxa_irda_netdev_ops;
-
- irda_init_max_qos_capabilies(&si->qos);
-
- baudrate_mask = 0;
- if (si->pdata->transceiver_cap & IR_SIRMODE)
- baudrate_mask |= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
- if (si->pdata->transceiver_cap & IR_FIRMODE)
- baudrate_mask |= IR_4000000 << 8;
-
- si->qos.baud_rate.bits &= baudrate_mask;
- si->qos.min_turn_time.bits = 7; /* 1ms or more */
-
- irda_qos_bits_to_value(&si->qos);
-
- err = register_netdev(dev);
-
- if (err == 0)
- platform_set_drvdata(pdev, dev);
-
- if (err) {
- if (si->pdata->shutdown)
- si->pdata->shutdown(si->dev);
-err_startup:
- kfree(si->tx_buff.head);
-err_mem_5:
- kfree(si->rx_buff.head);
-err_mem_4:
- free_netdev(dev);
- }
-err_mem_1:
- return err;
-}
-
-static int pxa_irda_remove(struct platform_device *_dev)
-{
- struct net_device *dev = platform_get_drvdata(_dev);
-
- if (dev) {
- struct pxa_irda *si = netdev_priv(dev);
- unregister_netdev(dev);
- if (gpio_is_valid(si->pdata->gpio_pwdown))
- gpio_free(si->pdata->gpio_pwdown);
- if (si->pdata->shutdown)
- si->pdata->shutdown(si->dev);
- kfree(si->tx_buff.head);
- kfree(si->rx_buff.head);
- free_netdev(dev);
- }
-
- return 0;
-}
-
-static struct platform_driver pxa_ir_driver = {
- .driver = {
- .name = "pxa2xx-ir",
- },
- .probe = pxa_irda_probe,
- .remove = pxa_irda_remove,
- .suspend = pxa_irda_suspend,
- .resume = pxa_irda_resume,
-};
-
-module_platform_driver(pxa_ir_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:pxa2xx-ir");
diff --git a/drivers/staging/irda/drivers/sa1100_ir.c b/drivers/staging/irda/drivers/sa1100_ir.c
deleted file mode 100644
index b6e44ff4e373..000000000000
--- a/drivers/staging/irda/drivers/sa1100_ir.c
+++ /dev/null
@@ -1,1150 +0,0 @@
-/*
- * linux/drivers/net/irda/sa1100_ir.c
- *
- * Copyright (C) 2000-2001 Russell King
- *
- * 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.
- *
- * Infra-red driver for the StrongARM SA1100 embedded microprocessor
- *
- * Note that we don't have to worry about the SA1111's DMA bugs in here,
- * so we use the straight forward dma_map_* functions with a null pointer.
- *
- * This driver takes one kernel command line parameter, sa1100ir=, with
- * the following options:
- * max_rate:baudrate - set the maximum baud rate
- * power_level:level - set the transmitter power level
- * tx_lpm:0|1 - set transmit low power mode
- */
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/types.h>
-#include <linux/init.h>
-#include <linux/errno.h>
-#include <linux/netdevice.h>
-#include <linux/slab.h>
-#include <linux/rtnetlink.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/platform_device.h>
-#include <linux/dma-mapping.h>
-#include <linux/dmaengine.h>
-#include <linux/sa11x0-dma.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/wrapper.h>
-#include <net/irda/irda_device.h>
-
-#include <mach/hardware.h>
-#include <linux/platform_data/irda-sa11x0.h>
-
-static int power_level = 3;
-static int tx_lpm;
-static int max_rate = 4000000;
-
-struct sa1100_buf {
- struct device *dev;
- struct sk_buff *skb;
- struct scatterlist sg;
- struct dma_chan *chan;
- dma_cookie_t cookie;
-};
-
-struct sa1100_irda {
- unsigned char utcr4;
- unsigned char power;
- unsigned char open;
-
- int speed;
- int newspeed;
-
- struct sa1100_buf dma_rx;
- struct sa1100_buf dma_tx;
-
- struct device *dev;
- struct irda_platform_data *pdata;
- struct irlap_cb *irlap;
- struct qos_info qos;
-
- iobuff_t tx_buff;
- iobuff_t rx_buff;
-
- int (*tx_start)(struct sk_buff *, struct net_device *, struct sa1100_irda *);
- irqreturn_t (*irq)(struct net_device *, struct sa1100_irda *);
-};
-
-static int sa1100_irda_set_speed(struct sa1100_irda *, int);
-
-#define IS_FIR(si) ((si)->speed >= 4000000)
-
-#define HPSIR_MAX_RXLEN 2047
-
-static struct dma_slave_config sa1100_irda_sir_tx = {
- .direction = DMA_TO_DEVICE,
- .dst_addr = __PREG(Ser2UTDR),
- .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
- .dst_maxburst = 4,
-};
-
-static struct dma_slave_config sa1100_irda_fir_rx = {
- .direction = DMA_FROM_DEVICE,
- .src_addr = __PREG(Ser2HSDR),
- .src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
- .src_maxburst = 8,
-};
-
-static struct dma_slave_config sa1100_irda_fir_tx = {
- .direction = DMA_TO_DEVICE,
- .dst_addr = __PREG(Ser2HSDR),
- .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
- .dst_maxburst = 8,
-};
-
-static unsigned sa1100_irda_dma_xferred(struct sa1100_buf *buf)
-{
- struct dma_chan *chan = buf->chan;
- struct dma_tx_state state;
- enum dma_status status;
-
- status = chan->device->device_tx_status(chan, buf->cookie, &state);
- if (status != DMA_PAUSED)
- return 0;
-
- return sg_dma_len(&buf->sg) - state.residue;
-}
-
-static int sa1100_irda_dma_request(struct device *dev, struct sa1100_buf *buf,
- const char *name, struct dma_slave_config *cfg)
-{
- dma_cap_mask_t m;
- int ret;
-
- dma_cap_zero(m);
- dma_cap_set(DMA_SLAVE, m);
-
- buf->chan = dma_request_channel(m, sa11x0_dma_filter_fn, (void *)name);
- if (!buf->chan) {
- dev_err(dev, "unable to request DMA channel for %s\n",
- name);
- return -ENOENT;
- }
-
- ret = dmaengine_slave_config(buf->chan, cfg);
- if (ret)
- dev_warn(dev, "DMA slave_config for %s returned %d\n",
- name, ret);
-
- buf->dev = buf->chan->device->dev;
-
- return 0;
-}
-
-static void sa1100_irda_dma_start(struct sa1100_buf *buf,
- enum dma_transfer_direction dir, dma_async_tx_callback cb, void *cb_p)
-{
- struct dma_async_tx_descriptor *desc;
- struct dma_chan *chan = buf->chan;
-
- desc = dmaengine_prep_slave_sg(chan, &buf->sg, 1, dir,
- DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
- if (desc) {
- desc->callback = cb;
- desc->callback_param = cb_p;
- buf->cookie = dmaengine_submit(desc);
- dma_async_issue_pending(chan);
- }
-}
-
-/*
- * Allocate and map the receive buffer, unless it is already allocated.
- */
-static int sa1100_irda_rx_alloc(struct sa1100_irda *si)
-{
- if (si->dma_rx.skb)
- return 0;
-
- si->dma_rx.skb = alloc_skb(HPSIR_MAX_RXLEN + 1, GFP_ATOMIC);
- if (!si->dma_rx.skb) {
- printk(KERN_ERR "sa1100_ir: out of memory for RX SKB\n");
- return -ENOMEM;
- }
-
- /*
- * Align any IP headers that may be contained
- * within the frame.
- */
- skb_reserve(si->dma_rx.skb, 1);
-
- sg_set_buf(&si->dma_rx.sg, si->dma_rx.skb->data, HPSIR_MAX_RXLEN);
- if (dma_map_sg(si->dma_rx.dev, &si->dma_rx.sg, 1, DMA_FROM_DEVICE) == 0) {
- dev_kfree_skb_any(si->dma_rx.skb);
- return -ENOMEM;
- }
-
- return 0;
-}
-
-/*
- * We want to get here as soon as possible, and get the receiver setup.
- * We use the existing buffer.
- */
-static void sa1100_irda_rx_dma_start(struct sa1100_irda *si)
-{
- if (!si->dma_rx.skb) {
- printk(KERN_ERR "sa1100_ir: rx buffer went missing\n");
- return;
- }
-
- /*
- * First empty receive FIFO
- */
- Ser2HSCR0 = HSCR0_HSSP;
-
- /*
- * Enable the DMA, receiver and receive interrupt.
- */
- dmaengine_terminate_all(si->dma_rx.chan);
- sa1100_irda_dma_start(&si->dma_rx, DMA_DEV_TO_MEM, NULL, NULL);
-
- Ser2HSCR0 = HSCR0_HSSP | HSCR0_RXE;
-}
-
-static void sa1100_irda_check_speed(struct sa1100_irda *si)
-{
- if (si->newspeed) {
- sa1100_irda_set_speed(si, si->newspeed);
- si->newspeed = 0;
- }
-}
-
-/*
- * HP-SIR format support.
- */
-static void sa1100_irda_sirtxdma_irq(void *id)
-{
- struct net_device *dev = id;
- struct sa1100_irda *si = netdev_priv(dev);
-
- dma_unmap_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, DMA_TO_DEVICE);
- dev_kfree_skb(si->dma_tx.skb);
- si->dma_tx.skb = NULL;
-
- dev->stats.tx_packets++;
- dev->stats.tx_bytes += sg_dma_len(&si->dma_tx.sg);
-
- /* We need to ensure that the transmitter has finished. */
- do
- rmb();
- while (Ser2UTSR1 & UTSR1_TBY);
-
- /*
- * Ok, we've finished transmitting. Now enable the receiver.
- * Sometimes we get a receive IRQ immediately after a transmit...
- */
- Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID;
- Ser2UTCR3 = UTCR3_RIE | UTCR3_RXE | UTCR3_TXE;
-
- sa1100_irda_check_speed(si);
-
- /* I'm hungry! */
- netif_wake_queue(dev);
-}
-
-static int sa1100_irda_sir_tx_start(struct sk_buff *skb, struct net_device *dev,
- struct sa1100_irda *si)
-{
- si->tx_buff.data = si->tx_buff.head;
- si->tx_buff.len = async_wrap_skb(skb, si->tx_buff.data,
- si->tx_buff.truesize);
-
- si->dma_tx.skb = skb;
- sg_set_buf(&si->dma_tx.sg, si->tx_buff.data, si->tx_buff.len);
- if (dma_map_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, DMA_TO_DEVICE) == 0) {
- si->dma_tx.skb = NULL;
- netif_wake_queue(dev);
- dev->stats.tx_dropped++;
- return NETDEV_TX_OK;
- }
-
- sa1100_irda_dma_start(&si->dma_tx, DMA_MEM_TO_DEV, sa1100_irda_sirtxdma_irq, dev);
-
- /*
- * The mean turn-around time is enforced by XBOF padding,
- * so we don't have to do anything special here.
- */
- Ser2UTCR3 = UTCR3_TXE;
-
- return NETDEV_TX_OK;
-}
-
-static irqreturn_t sa1100_irda_sir_irq(struct net_device *dev, struct sa1100_irda *si)
-{
- int status;
-
- status = Ser2UTSR0;
-
- /*
- * Deal with any receive errors first. The bytes in error may be
- * the only bytes in the receive FIFO, so we do this first.
- */
- while (status & UTSR0_EIF) {
- int stat, data;
-
- stat = Ser2UTSR1;
- data = Ser2UTDR;
-
- if (stat & (UTSR1_FRE | UTSR1_ROR)) {
- dev->stats.rx_errors++;
- if (stat & UTSR1_FRE)
- dev->stats.rx_frame_errors++;
- if (stat & UTSR1_ROR)
- dev->stats.rx_fifo_errors++;
- } else
- async_unwrap_char(dev, &dev->stats, &si->rx_buff, data);
-
- status = Ser2UTSR0;
- }
-
- /*
- * We must clear certain bits.
- */
- Ser2UTSR0 = status & (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
-
- if (status & UTSR0_RFS) {
- /*
- * There are at least 4 bytes in the FIFO. Read 3 bytes
- * and leave the rest to the block below.
- */
- async_unwrap_char(dev, &dev->stats, &si->rx_buff, Ser2UTDR);
- async_unwrap_char(dev, &dev->stats, &si->rx_buff, Ser2UTDR);
- async_unwrap_char(dev, &dev->stats, &si->rx_buff, Ser2UTDR);
- }
-
- if (status & (UTSR0_RFS | UTSR0_RID)) {
- /*
- * Fifo contains more than 1 character.
- */
- do {
- async_unwrap_char(dev, &dev->stats, &si->rx_buff,
- Ser2UTDR);
- } while (Ser2UTSR1 & UTSR1_RNE);
-
- }
-
- return IRQ_HANDLED;
-}
-
-/*
- * FIR format support.
- */
-static void sa1100_irda_firtxdma_irq(void *id)
-{
- struct net_device *dev = id;
- struct sa1100_irda *si = netdev_priv(dev);
- struct sk_buff *skb;
-
- /*
- * Wait for the transmission to complete. Unfortunately,
- * the hardware doesn't give us an interrupt to indicate
- * "end of frame".
- */
- do
- rmb();
- while (!(Ser2HSSR0 & HSSR0_TUR) || Ser2HSSR1 & HSSR1_TBY);
-
- /*
- * Clear the transmit underrun bit.
- */
- Ser2HSSR0 = HSSR0_TUR;
-
- /*
- * Do we need to change speed? Note that we're lazy
- * here - we don't free the old dma_rx.skb. We don't need
- * to allocate a buffer either.
- */
- sa1100_irda_check_speed(si);
-
- /*
- * Start reception. This disables the transmitter for
- * us. This will be using the existing RX buffer.
- */
- sa1100_irda_rx_dma_start(si);
-
- /* Account and free the packet. */
- skb = si->dma_tx.skb;
- if (skb) {
- dma_unmap_sg(si->dma_tx.dev, &si->dma_tx.sg, 1,
- DMA_TO_DEVICE);
- dev->stats.tx_packets ++;
- dev->stats.tx_bytes += skb->len;
- dev_kfree_skb_irq(skb);
- si->dma_tx.skb = NULL;
- }
-
- /*
- * Make sure that the TX queue is available for sending
- * (for retries). TX has priority over RX at all times.
- */
- netif_wake_queue(dev);
-}
-
-static int sa1100_irda_fir_tx_start(struct sk_buff *skb, struct net_device *dev,
- struct sa1100_irda *si)
-{
- int mtt = irda_get_mtt(skb);
-
- si->dma_tx.skb = skb;
- sg_set_buf(&si->dma_tx.sg, skb->data, skb->len);
- if (dma_map_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, DMA_TO_DEVICE) == 0) {
- si->dma_tx.skb = NULL;
- netif_wake_queue(dev);
- dev->stats.tx_dropped++;
- dev_kfree_skb(skb);
- return NETDEV_TX_OK;
- }
-
- sa1100_irda_dma_start(&si->dma_tx, DMA_MEM_TO_DEV, sa1100_irda_firtxdma_irq, dev);
-
- /*
- * If we have a mean turn-around time, impose the specified
- * specified delay. We could shorten this by timing from
- * the point we received the packet.
- */
- if (mtt)
- udelay(mtt);
-
- Ser2HSCR0 = HSCR0_HSSP | HSCR0_TXE;
-
- return NETDEV_TX_OK;
-}
-
-static void sa1100_irda_fir_error(struct sa1100_irda *si, struct net_device *dev)
-{
- struct sk_buff *skb = si->dma_rx.skb;
- unsigned int len, stat, data;
-
- if (!skb) {
- printk(KERN_ERR "sa1100_ir: SKB is NULL!\n");
- return;
- }
-
- /*
- * Get the current data position.
- */
- len = sa1100_irda_dma_xferred(&si->dma_rx);
- if (len > HPSIR_MAX_RXLEN)
- len = HPSIR_MAX_RXLEN;
- dma_unmap_sg(si->dma_rx.dev, &si->dma_rx.sg, 1, DMA_FROM_DEVICE);
-
- do {
- /*
- * Read Status, and then Data.
- */
- stat = Ser2HSSR1;
- rmb();
- data = Ser2HSDR;
-
- if (stat & (HSSR1_CRE | HSSR1_ROR)) {
- dev->stats.rx_errors++;
- if (stat & HSSR1_CRE)
- dev->stats.rx_crc_errors++;
- if (stat & HSSR1_ROR)
- dev->stats.rx_frame_errors++;
- } else
- skb->data[len++] = data;
-
- /*
- * If we hit the end of frame, there's
- * no point in continuing.
- */
- if (stat & HSSR1_EOF)
- break;
- } while (Ser2HSSR0 & HSSR0_EIF);
-
- if (stat & HSSR1_EOF) {
- si->dma_rx.skb = NULL;
-
- skb_put(skb, len);
- skb->dev = dev;
- skb_reset_mac_header(skb);
- skb->protocol = htons(ETH_P_IRDA);
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += len;
-
- /*
- * Before we pass the buffer up, allocate a new one.
- */
- sa1100_irda_rx_alloc(si);
-
- netif_rx(skb);
- } else {
- /*
- * Remap the buffer - it was previously mapped, and we
- * hope that this succeeds.
- */
- dma_map_sg(si->dma_rx.dev, &si->dma_rx.sg, 1, DMA_FROM_DEVICE);
- }
-}
-
-/*
- * We only have to handle RX events here; transmit events go via the TX
- * DMA handler. We disable RX, process, and the restart RX.
- */
-static irqreturn_t sa1100_irda_fir_irq(struct net_device *dev, struct sa1100_irda *si)
-{
- /*
- * Stop RX DMA
- */
- dmaengine_pause(si->dma_rx.chan);
-
- /*
- * Framing error - we throw away the packet completely.
- * Clearing RXE flushes the error conditions and data
- * from the fifo.
- */
- if (Ser2HSSR0 & (HSSR0_FRE | HSSR0_RAB)) {
- dev->stats.rx_errors++;
-
- if (Ser2HSSR0 & HSSR0_FRE)
- dev->stats.rx_frame_errors++;
-
- /*
- * Clear out the DMA...
- */
- Ser2HSCR0 = HSCR0_HSSP;
-
- /*
- * Clear selected status bits now, so we
- * don't miss them next time around.
- */
- Ser2HSSR0 = HSSR0_FRE | HSSR0_RAB;
- }
-
- /*
- * Deal with any receive errors. The any of the lowest
- * 8 bytes in the FIFO may contain an error. We must read
- * them one by one. The "error" could even be the end of
- * packet!
- */
- if (Ser2HSSR0 & HSSR0_EIF)
- sa1100_irda_fir_error(si, dev);
-
- /*
- * No matter what happens, we must restart reception.
- */
- sa1100_irda_rx_dma_start(si);
-
- return IRQ_HANDLED;
-}
-
-/*
- * Set the IrDA communications speed.
- */
-static int sa1100_irda_set_speed(struct sa1100_irda *si, int speed)
-{
- unsigned long flags;
- int brd, ret = -EINVAL;
-
- switch (speed) {
- case 9600: case 19200: case 38400:
- case 57600: case 115200:
- brd = 3686400 / (16 * speed) - 1;
-
- /* Stop the receive DMA, and configure transmit. */
- if (IS_FIR(si)) {
- dmaengine_terminate_all(si->dma_rx.chan);
- dmaengine_slave_config(si->dma_tx.chan,
- &sa1100_irda_sir_tx);
- }
-
- local_irq_save(flags);
-
- Ser2UTCR3 = 0;
- Ser2HSCR0 = HSCR0_UART;
-
- Ser2UTCR1 = brd >> 8;
- Ser2UTCR2 = brd;
-
- /*
- * Clear status register
- */
- Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID;
- Ser2UTCR3 = UTCR3_RIE | UTCR3_RXE | UTCR3_TXE;
-
- if (si->pdata->set_speed)
- si->pdata->set_speed(si->dev, speed);
-
- si->speed = speed;
- si->tx_start = sa1100_irda_sir_tx_start;
- si->irq = sa1100_irda_sir_irq;
-
- local_irq_restore(flags);
- ret = 0;
- break;
-
- case 4000000:
- if (!IS_FIR(si))
- dmaengine_slave_config(si->dma_tx.chan,
- &sa1100_irda_fir_tx);
-
- local_irq_save(flags);
-
- Ser2HSSR0 = 0xff;
- Ser2HSCR0 = HSCR0_HSSP;
- Ser2UTCR3 = 0;
-
- si->speed = speed;
- si->tx_start = sa1100_irda_fir_tx_start;
- si->irq = sa1100_irda_fir_irq;
-
- if (si->pdata->set_speed)
- si->pdata->set_speed(si->dev, speed);
-
- sa1100_irda_rx_alloc(si);
- sa1100_irda_rx_dma_start(si);
-
- local_irq_restore(flags);
-
- break;
-
- default:
- break;
- }
-
- return ret;
-}
-
-/*
- * Control the power state of the IrDA transmitter.
- * State:
- * 0 - off
- * 1 - short range, lowest power
- * 2 - medium range, medium power
- * 3 - maximum range, high power
- *
- * Currently, only assabet is known to support this.
- */
-static int
-__sa1100_irda_set_power(struct sa1100_irda *si, unsigned int state)
-{
- int ret = 0;
- if (si->pdata->set_power)
- ret = si->pdata->set_power(si->dev, state);
- return ret;
-}
-
-static inline int
-sa1100_set_power(struct sa1100_irda *si, unsigned int state)
-{
- int ret;
-
- ret = __sa1100_irda_set_power(si, state);
- if (ret == 0)
- si->power = state;
-
- return ret;
-}
-
-static irqreturn_t sa1100_irda_irq(int irq, void *dev_id)
-{
- struct net_device *dev = dev_id;
- struct sa1100_irda *si = netdev_priv(dev);
-
- return si->irq(dev, si);
-}
-
-static int sa1100_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct sa1100_irda *si = netdev_priv(dev);
- int speed = irda_get_next_speed(skb);
-
- /*
- * Does this packet contain a request to change the interface
- * speed? If so, remember it until we complete the transmission
- * of this frame.
- */
- if (speed != si->speed && speed != -1)
- si->newspeed = speed;
-
- /* If this is an empty frame, we can bypass a lot. */
- if (skb->len == 0) {
- sa1100_irda_check_speed(si);
- dev_kfree_skb(skb);
- return NETDEV_TX_OK;
- }
-
- netif_stop_queue(dev);
-
- /* We must not already have a skb to transmit... */
- BUG_ON(si->dma_tx.skb);
-
- return si->tx_start(skb, dev, si);
-}
-
-static int
-sa1100_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd)
-{
- struct if_irda_req *rq = (struct if_irda_req *)ifreq;
- struct sa1100_irda *si = netdev_priv(dev);
- int ret = -EOPNOTSUPP;
-
- switch (cmd) {
- case SIOCSBANDWIDTH:
- if (capable(CAP_NET_ADMIN)) {
- /*
- * We are unable to set the speed if the
- * device is not running.
- */
- if (si->open) {
- ret = sa1100_irda_set_speed(si,
- rq->ifr_baudrate);
- } else {
- printk("sa1100_irda_ioctl: SIOCSBANDWIDTH: !netif_running\n");
- ret = 0;
- }
- }
- break;
-
- case SIOCSMEDIABUSY:
- ret = -EPERM;
- if (capable(CAP_NET_ADMIN)) {
- irda_device_set_media_busy(dev, TRUE);
- ret = 0;
- }
- break;
-
- case SIOCGRECEIVING:
- rq->ifr_receiving = IS_FIR(si) ? 0
- : si->rx_buff.state != OUTSIDE_FRAME;
- break;
-
- default:
- break;
- }
-
- return ret;
-}
-
-static int sa1100_irda_startup(struct sa1100_irda *si)
-{
- int ret;
-
- /*
- * Ensure that the ports for this device are setup correctly.
- */
- if (si->pdata->startup) {
- ret = si->pdata->startup(si->dev);
- if (ret)
- return ret;
- }
-
- /*
- * Configure PPC for IRDA - we want to drive TXD2 low.
- * We also want to drive this pin low during sleep.
- */
- PPSR &= ~PPC_TXD2;
- PSDR &= ~PPC_TXD2;
- PPDR |= PPC_TXD2;
-
- /*
- * Enable HP-SIR modulation, and ensure that the port is disabled.
- */
- Ser2UTCR3 = 0;
- Ser2HSCR0 = HSCR0_UART;
- Ser2UTCR4 = si->utcr4;
- Ser2UTCR0 = UTCR0_8BitData;
- Ser2HSCR2 = HSCR2_TrDataH | HSCR2_RcDataL;
-
- /*
- * Clear status register
- */
- Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID;
-
- ret = sa1100_irda_set_speed(si, si->speed = 9600);
- if (ret) {
- Ser2UTCR3 = 0;
- Ser2HSCR0 = 0;
-
- if (si->pdata->shutdown)
- si->pdata->shutdown(si->dev);
- }
-
- return ret;
-}
-
-static void sa1100_irda_shutdown(struct sa1100_irda *si)
-{
- /*
- * Stop all DMA activity.
- */
- dmaengine_terminate_all(si->dma_rx.chan);
- dmaengine_terminate_all(si->dma_tx.chan);
-
- /* Disable the port. */
- Ser2UTCR3 = 0;
- Ser2HSCR0 = 0;
-
- if (si->pdata->shutdown)
- si->pdata->shutdown(si->dev);
-}
-
-static int sa1100_irda_start(struct net_device *dev)
-{
- struct sa1100_irda *si = netdev_priv(dev);
- int err;
-
- si->speed = 9600;
-
- err = sa1100_irda_dma_request(si->dev, &si->dma_rx, "Ser2ICPRc",
- &sa1100_irda_fir_rx);
- if (err)
- goto err_rx_dma;
-
- err = sa1100_irda_dma_request(si->dev, &si->dma_tx, "Ser2ICPTr",
- &sa1100_irda_sir_tx);
- if (err)
- goto err_tx_dma;
-
- /*
- * Setup the serial port for the specified speed.
- */
- err = sa1100_irda_startup(si);
- if (err)
- goto err_startup;
-
- /*
- * Open a new IrLAP layer instance.
- */
- si->irlap = irlap_open(dev, &si->qos, "sa1100");
- err = -ENOMEM;
- if (!si->irlap)
- goto err_irlap;
-
- err = request_irq(dev->irq, sa1100_irda_irq, 0, dev->name, dev);
- if (err)
- goto err_irq;
-
- /*
- * Now enable the interrupt and start the queue
- */
- si->open = 1;
- sa1100_set_power(si, power_level); /* low power mode */
-
- netif_start_queue(dev);
- return 0;
-
-err_irq:
- irlap_close(si->irlap);
-err_irlap:
- si->open = 0;
- sa1100_irda_shutdown(si);
-err_startup:
- dma_release_channel(si->dma_tx.chan);
-err_tx_dma:
- dma_release_channel(si->dma_rx.chan);
-err_rx_dma:
- return err;
-}
-
-static int sa1100_irda_stop(struct net_device *dev)
-{
- struct sa1100_irda *si = netdev_priv(dev);
- struct sk_buff *skb;
-
- netif_stop_queue(dev);
-
- si->open = 0;
- sa1100_irda_shutdown(si);
-
- /*
- * If we have been doing any DMA activity, make sure we
- * tidy that up cleanly.
- */
- skb = si->dma_rx.skb;
- if (skb) {
- dma_unmap_sg(si->dma_rx.dev, &si->dma_rx.sg, 1,
- DMA_FROM_DEVICE);
- dev_kfree_skb(skb);
- si->dma_rx.skb = NULL;
- }
-
- skb = si->dma_tx.skb;
- if (skb) {
- dma_unmap_sg(si->dma_tx.dev, &si->dma_tx.sg, 1,
- DMA_TO_DEVICE);
- dev_kfree_skb(skb);
- si->dma_tx.skb = NULL;
- }
-
- /* Stop IrLAP */
- if (si->irlap) {
- irlap_close(si->irlap);
- si->irlap = NULL;
- }
-
- /*
- * Free resources
- */
- dma_release_channel(si->dma_tx.chan);
- dma_release_channel(si->dma_rx.chan);
- free_irq(dev->irq, dev);
-
- sa1100_set_power(si, 0);
-
- return 0;
-}
-
-static int sa1100_irda_init_iobuf(iobuff_t *io, int size)
-{
- io->head = kmalloc(size, GFP_KERNEL | GFP_DMA);
- if (io->head != NULL) {
- io->truesize = size;
- io->in_frame = FALSE;
- io->state = OUTSIDE_FRAME;
- io->data = io->head;
- }
- return io->head ? 0 : -ENOMEM;
-}
-
-static const struct net_device_ops sa1100_irda_netdev_ops = {
- .ndo_open = sa1100_irda_start,
- .ndo_stop = sa1100_irda_stop,
- .ndo_start_xmit = sa1100_irda_hard_xmit,
- .ndo_do_ioctl = sa1100_irda_ioctl,
-};
-
-static int sa1100_irda_probe(struct platform_device *pdev)
-{
- struct net_device *dev;
- struct sa1100_irda *si;
- unsigned int baudrate_mask;
- int err, irq;
-
- if (!pdev->dev.platform_data)
- return -EINVAL;
-
- irq = platform_get_irq(pdev, 0);
- if (irq <= 0)
- return irq < 0 ? irq : -ENXIO;
-
- err = request_mem_region(__PREG(Ser2UTCR0), 0x24, "IrDA") ? 0 : -EBUSY;
- if (err)
- goto err_mem_1;
- err = request_mem_region(__PREG(Ser2HSCR0), 0x1c, "IrDA") ? 0 : -EBUSY;
- if (err)
- goto err_mem_2;
- err = request_mem_region(__PREG(Ser2HSCR2), 0x04, "IrDA") ? 0 : -EBUSY;
- if (err)
- goto err_mem_3;
-
- dev = alloc_irdadev(sizeof(struct sa1100_irda));
- if (!dev) {
- err = -ENOMEM;
- goto err_mem_4;
- }
-
- SET_NETDEV_DEV(dev, &pdev->dev);
-
- si = netdev_priv(dev);
- si->dev = &pdev->dev;
- si->pdata = pdev->dev.platform_data;
-
- sg_init_table(&si->dma_rx.sg, 1);
- sg_init_table(&si->dma_tx.sg, 1);
-
- /*
- * Initialise the HP-SIR buffers
- */
- err = sa1100_irda_init_iobuf(&si->rx_buff, 14384);
- if (err)
- goto err_mem_5;
- err = sa1100_irda_init_iobuf(&si->tx_buff, IRDA_SIR_MAX_FRAME);
- if (err)
- goto err_mem_5;
-
- dev->netdev_ops = &sa1100_irda_netdev_ops;
- dev->irq = irq;
-
- irda_init_max_qos_capabilies(&si->qos);
-
- /*
- * We support original IRDA up to 115k2. (we don't currently
- * support 4Mbps). Min Turn Time set to 1ms or greater.
- */
- baudrate_mask = IR_9600;
-
- switch (max_rate) {
- case 4000000: baudrate_mask |= IR_4000000 << 8;
- case 115200: baudrate_mask |= IR_115200;
- case 57600: baudrate_mask |= IR_57600;
- case 38400: baudrate_mask |= IR_38400;
- case 19200: baudrate_mask |= IR_19200;
- }
-
- si->qos.baud_rate.bits &= baudrate_mask;
- si->qos.min_turn_time.bits = 7;
-
- irda_qos_bits_to_value(&si->qos);
-
- si->utcr4 = UTCR4_HPSIR;
- if (tx_lpm)
- si->utcr4 |= UTCR4_Z1_6us;
-
- /*
- * Initially enable HP-SIR modulation, and ensure that the port
- * is disabled.
- */
- Ser2UTCR3 = 0;
- Ser2UTCR4 = si->utcr4;
- Ser2HSCR0 = HSCR0_UART;
-
- err = register_netdev(dev);
- if (err == 0)
- platform_set_drvdata(pdev, dev);
-
- if (err) {
- err_mem_5:
- kfree(si->tx_buff.head);
- kfree(si->rx_buff.head);
- free_netdev(dev);
- err_mem_4:
- release_mem_region(__PREG(Ser2HSCR2), 0x04);
- err_mem_3:
- release_mem_region(__PREG(Ser2HSCR0), 0x1c);
- err_mem_2:
- release_mem_region(__PREG(Ser2UTCR0), 0x24);
- }
- err_mem_1:
- return err;
-}
-
-static int sa1100_irda_remove(struct platform_device *pdev)
-{
- struct net_device *dev = platform_get_drvdata(pdev);
-
- if (dev) {
- struct sa1100_irda *si = netdev_priv(dev);
- unregister_netdev(dev);
- kfree(si->tx_buff.head);
- kfree(si->rx_buff.head);
- free_netdev(dev);
- }
-
- release_mem_region(__PREG(Ser2HSCR2), 0x04);
- release_mem_region(__PREG(Ser2HSCR0), 0x1c);
- release_mem_region(__PREG(Ser2UTCR0), 0x24);
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-/*
- * Suspend the IrDA interface.
- */
-static int sa1100_irda_suspend(struct platform_device *pdev, pm_message_t state)
-{
- struct net_device *dev = platform_get_drvdata(pdev);
- struct sa1100_irda *si;
-
- if (!dev)
- return 0;
-
- si = netdev_priv(dev);
- if (si->open) {
- /*
- * Stop the transmit queue
- */
- netif_device_detach(dev);
- disable_irq(dev->irq);
- sa1100_irda_shutdown(si);
- __sa1100_irda_set_power(si, 0);
- }
-
- return 0;
-}
-
-/*
- * Resume the IrDA interface.
- */
-static int sa1100_irda_resume(struct platform_device *pdev)
-{
- struct net_device *dev = platform_get_drvdata(pdev);
- struct sa1100_irda *si;
-
- if (!dev)
- return 0;
-
- si = netdev_priv(dev);
- if (si->open) {
- /*
- * If we missed a speed change, initialise at the new speed
- * directly. It is debatable whether this is actually
- * required, but in the interests of continuing from where
- * we left off it is desirable. The converse argument is
- * that we should re-negotiate at 9600 baud again.
- */
- if (si->newspeed) {
- si->speed = si->newspeed;
- si->newspeed = 0;
- }
-
- sa1100_irda_startup(si);
- __sa1100_irda_set_power(si, si->power);
- enable_irq(dev->irq);
-
- /*
- * This automatically wakes up the queue
- */
- netif_device_attach(dev);
- }
-
- return 0;
-}
-#else
-#define sa1100_irda_suspend NULL
-#define sa1100_irda_resume NULL
-#endif
-
-static struct platform_driver sa1100ir_driver = {
- .probe = sa1100_irda_probe,
- .remove = sa1100_irda_remove,
- .suspend = sa1100_irda_suspend,
- .resume = sa1100_irda_resume,
- .driver = {
- .name = "sa11x0-ir",
- },
-};
-
-static int __init sa1100_irda_init(void)
-{
- /*
- * Limit power level a sensible range.
- */
- if (power_level < 1)
- power_level = 1;
- if (power_level > 3)
- power_level = 3;
-
- return platform_driver_register(&sa1100ir_driver);
-}
-
-static void __exit sa1100_irda_exit(void)
-{
- platform_driver_unregister(&sa1100ir_driver);
-}
-
-module_init(sa1100_irda_init);
-module_exit(sa1100_irda_exit);
-module_param(power_level, int, 0);
-module_param(tx_lpm, int, 0);
-module_param(max_rate, int, 0);
-
-MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
-MODULE_DESCRIPTION("StrongARM SA1100 IrDA driver");
-MODULE_LICENSE("GPL");
-MODULE_PARM_DESC(power_level, "IrDA power level, 1 (low) to 3 (high)");
-MODULE_PARM_DESC(tx_lpm, "Enable transmitter low power (1.6us) mode");
-MODULE_PARM_DESC(max_rate, "Maximum baud rate (4000000, 115200, 57600, 38400, 19200, 9600)");
-MODULE_ALIAS("platform:sa11x0-ir");
diff --git a/drivers/staging/irda/drivers/sh_sir.c b/drivers/staging/irda/drivers/sh_sir.c
deleted file mode 100644
index 0d0687cc454a..000000000000
--- a/drivers/staging/irda/drivers/sh_sir.c
+++ /dev/null
@@ -1,810 +0,0 @@
-/*
- * SuperH IrDA Driver
- *
- * Copyright (C) 2009 Renesas Solutions Corp.
- * Kuninori Morimoto <morimoto.kuninori@renesas.com>
- *
- * Based on bfin_sir.c
- * Copyright 2006-2009 Analog Devices Inc.
- *
- * 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.
- */
-
-#include <linux/io.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <net/irda/wrapper.h>
-#include <net/irda/irda_device.h>
-#include <asm/clock.h>
-
-#define DRIVER_NAME "sh_sir"
-
-#define RX_PHASE (1 << 0)
-#define TX_PHASE (1 << 1)
-#define TX_COMP_PHASE (1 << 2) /* tx complete */
-#define NONE_PHASE (1 << 31)
-
-#define IRIF_RINTCLR 0x0016 /* DMA rx interrupt source clear */
-#define IRIF_TINTCLR 0x0018 /* DMA tx interrupt source clear */
-#define IRIF_SIR0 0x0020 /* IrDA-SIR10 control */
-#define IRIF_SIR1 0x0022 /* IrDA-SIR10 baudrate error correction */
-#define IRIF_SIR2 0x0024 /* IrDA-SIR10 baudrate count */
-#define IRIF_SIR3 0x0026 /* IrDA-SIR10 status */
-#define IRIF_SIR_FRM 0x0028 /* Hardware frame processing set */
-#define IRIF_SIR_EOF 0x002A /* EOF value */
-#define IRIF_SIR_FLG 0x002C /* Flag clear */
-#define IRIF_UART_STS2 0x002E /* UART status 2 */
-#define IRIF_UART0 0x0030 /* UART control */
-#define IRIF_UART1 0x0032 /* UART status */
-#define IRIF_UART2 0x0034 /* UART mode */
-#define IRIF_UART3 0x0036 /* UART transmit data */
-#define IRIF_UART4 0x0038 /* UART receive data */
-#define IRIF_UART5 0x003A /* UART interrupt mask */
-#define IRIF_UART6 0x003C /* UART baud rate error correction */
-#define IRIF_UART7 0x003E /* UART baud rate count set */
-#define IRIF_CRC0 0x0040 /* CRC engine control */
-#define IRIF_CRC1 0x0042 /* CRC engine input data */
-#define IRIF_CRC2 0x0044 /* CRC engine calculation */
-#define IRIF_CRC3 0x0046 /* CRC engine output data 1 */
-#define IRIF_CRC4 0x0048 /* CRC engine output data 2 */
-
-/* IRIF_SIR0 */
-#define IRTPW (1 << 1) /* transmit pulse width select */
-#define IRERRC (1 << 0) /* Clear receive pulse width error */
-
-/* IRIF_SIR3 */
-#define IRERR (1 << 0) /* received pulse width Error */
-
-/* IRIF_SIR_FRM */
-#define EOFD (1 << 9) /* EOF detection flag */
-#define FRER (1 << 8) /* Frame Error bit */
-#define FRP (1 << 0) /* Frame processing set */
-
-/* IRIF_UART_STS2 */
-#define IRSME (1 << 6) /* Receive Sum Error flag */
-#define IROVE (1 << 5) /* Receive Overrun Error flag */
-#define IRFRE (1 << 4) /* Receive Framing Error flag */
-#define IRPRE (1 << 3) /* Receive Parity Error flag */
-
-/* IRIF_UART0_*/
-#define TBEC (1 << 2) /* Transmit Data Clear */
-#define RIE (1 << 1) /* Receive Enable */
-#define TIE (1 << 0) /* Transmit Enable */
-
-/* IRIF_UART1 */
-#define URSME (1 << 6) /* Receive Sum Error Flag */
-#define UROVE (1 << 5) /* Receive Overrun Error Flag */
-#define URFRE (1 << 4) /* Receive Framing Error Flag */
-#define URPRE (1 << 3) /* Receive Parity Error Flag */
-#define RBF (1 << 2) /* Receive Buffer Full Flag */
-#define TSBE (1 << 1) /* Transmit Shift Buffer Empty Flag */
-#define TBE (1 << 0) /* Transmit Buffer Empty flag */
-#define TBCOMP (TSBE | TBE)
-
-/* IRIF_UART5 */
-#define RSEIM (1 << 6) /* Receive Sum Error Flag IRQ Mask */
-#define RBFIM (1 << 2) /* Receive Buffer Full Flag IRQ Mask */
-#define TSBEIM (1 << 1) /* Transmit Shift Buffer Empty Flag IRQ Mask */
-#define TBEIM (1 << 0) /* Transmit Buffer Empty Flag IRQ Mask */
-#define RX_MASK (RSEIM | RBFIM)
-
-/* IRIF_CRC0 */
-#define CRC_RST (1 << 15) /* CRC Engine Reset */
-#define CRC_CT_MASK 0x0FFF
-
-/************************************************************************
-
-
- structure
-
-
-************************************************************************/
-struct sh_sir_self {
- void __iomem *membase;
- unsigned int irq;
- struct clk *clk;
-
- struct net_device *ndev;
-
- struct irlap_cb *irlap;
- struct qos_info qos;
-
- iobuff_t tx_buff;
- iobuff_t rx_buff;
-};
-
-/************************************************************************
-
-
- common function
-
-
-************************************************************************/
-static void sh_sir_write(struct sh_sir_self *self, u32 offset, u16 data)
-{
- iowrite16(data, self->membase + offset);
-}
-
-static u16 sh_sir_read(struct sh_sir_self *self, u32 offset)
-{
- return ioread16(self->membase + offset);
-}
-
-static void sh_sir_update_bits(struct sh_sir_self *self, u32 offset,
- u16 mask, u16 data)
-{
- u16 old, new;
-
- old = sh_sir_read(self, offset);
- new = (old & ~mask) | data;
- if (old != new)
- sh_sir_write(self, offset, new);
-}
-
-/************************************************************************
-
-
- CRC function
-
-
-************************************************************************/
-static void sh_sir_crc_reset(struct sh_sir_self *self)
-{
- sh_sir_write(self, IRIF_CRC0, CRC_RST);
-}
-
-static void sh_sir_crc_add(struct sh_sir_self *self, u8 data)
-{
- sh_sir_write(self, IRIF_CRC1, (u16)data);
-}
-
-static u16 sh_sir_crc_cnt(struct sh_sir_self *self)
-{
- return CRC_CT_MASK & sh_sir_read(self, IRIF_CRC0);
-}
-
-static u16 sh_sir_crc_out(struct sh_sir_self *self)
-{
- return sh_sir_read(self, IRIF_CRC4);
-}
-
-static int sh_sir_crc_init(struct sh_sir_self *self)
-{
- struct device *dev = &self->ndev->dev;
- int ret = -EIO;
- u16 val;
-
- sh_sir_crc_reset(self);
-
- sh_sir_crc_add(self, 0xCC);
- sh_sir_crc_add(self, 0xF5);
- sh_sir_crc_add(self, 0xF1);
- sh_sir_crc_add(self, 0xA7);
-
- val = sh_sir_crc_cnt(self);
- if (4 != val) {
- dev_err(dev, "CRC count error %x\n", val);
- goto crc_init_out;
- }
-
- val = sh_sir_crc_out(self);
- if (0x51DF != val) {
- dev_err(dev, "CRC result error%x\n", val);
- goto crc_init_out;
- }
-
- ret = 0;
-
-crc_init_out:
-
- sh_sir_crc_reset(self);
- return ret;
-}
-
-/************************************************************************
-
-
- baud rate functions
-
-
-************************************************************************/
-#define SCLK_BASE 1843200 /* 1.8432MHz */
-
-static u32 sh_sir_find_sclk(struct clk *irda_clk)
-{
- struct cpufreq_frequency_table *freq_table = irda_clk->freq_table;
- struct cpufreq_frequency_table *pos;
- struct clk *pclk = clk_get(NULL, "peripheral_clk");
- u32 limit, min = 0xffffffff, tmp;
- int index = 0;
-
- limit = clk_get_rate(pclk);
- clk_put(pclk);
-
- /* IrDA can not set over peripheral_clk */
- cpufreq_for_each_valid_entry_idx(pos, freq_table, index) {
- u32 freq = pos->frequency;
-
- /* IrDA should not over peripheral_clk */
- if (freq > limit)
- continue;
-
- tmp = freq % SCLK_BASE;
- if (tmp < min) {
- min = tmp;
- break;
- }
- }
-
- return freq_table[index].frequency;
-}
-
-#define ERR_ROUNDING(a) ((a + 5000) / 10000)
-static int sh_sir_set_baudrate(struct sh_sir_self *self, u32 baudrate)
-{
- struct clk *clk;
- struct device *dev = &self->ndev->dev;
- u32 rate;
- u16 uabca, uabc;
- u16 irbca, irbc;
- u32 min, rerr, tmp;
- int i;
-
- /* Baud Rate Error Correction x 10000 */
- u32 rate_err_array[] = {
- 0, 625, 1250, 1875,
- 2500, 3125, 3750, 4375,
- 5000, 5625, 6250, 6875,
- 7500, 8125, 8750, 9375,
- };
-
- /*
- * FIXME
- *
- * it support 9600 only now
- */
- switch (baudrate) {
- case 9600:
- break;
- default:
- dev_err(dev, "un-supported baudrate %d\n", baudrate);
- return -EIO;
- }
-
- clk = clk_get(NULL, "irda_clk");
- if (IS_ERR(clk)) {
- dev_err(dev, "can not get irda_clk\n");
- return -EIO;
- }
-
- clk_set_rate(clk, sh_sir_find_sclk(clk));
- rate = clk_get_rate(clk);
- clk_put(clk);
-
- dev_dbg(dev, "selected sclk = %d\n", rate);
-
- /*
- * CALCULATION
- *
- * 1843200 = system rate / (irbca + (irbc + 1))
- */
-
- irbc = rate / SCLK_BASE;
-
- tmp = rate - (SCLK_BASE * irbc);
- tmp *= 10000;
-
- rerr = tmp / SCLK_BASE;
-
- min = 0xffffffff;
- irbca = 0;
- for (i = 0; i < ARRAY_SIZE(rate_err_array); i++) {
- tmp = abs(rate_err_array[i] - rerr);
- if (min > tmp) {
- min = tmp;
- irbca = i;
- }
- }
-
- tmp = rate / (irbc + ERR_ROUNDING(rate_err_array[irbca]));
- if ((SCLK_BASE / 100) < abs(tmp - SCLK_BASE))
- dev_warn(dev, "IrDA freq error margin over %d\n", tmp);
-
- dev_dbg(dev, "target = %d, result = %d, infrared = %d.%d\n",
- SCLK_BASE, tmp, irbc, rate_err_array[irbca]);
-
- irbca = (irbca & 0xF) << 4;
- irbc = (irbc - 1) & 0xF;
-
- if (!irbc) {
- dev_err(dev, "sh_sir can not set 0 in IRIF_SIR2\n");
- return -EIO;
- }
-
- sh_sir_write(self, IRIF_SIR0, IRTPW | IRERRC);
- sh_sir_write(self, IRIF_SIR1, irbca);
- sh_sir_write(self, IRIF_SIR2, irbc);
-
- /*
- * CALCULATION
- *
- * BaudRate[bps] = system rate / (uabca + (uabc + 1) x 16)
- */
-
- uabc = rate / baudrate;
- uabc = (uabc / 16) - 1;
- uabc = (uabc + 1) * 16;
-
- tmp = rate - (uabc * baudrate);
- tmp *= 10000;
-
- rerr = tmp / baudrate;
-
- min = 0xffffffff;
- uabca = 0;
- for (i = 0; i < ARRAY_SIZE(rate_err_array); i++) {
- tmp = abs(rate_err_array[i] - rerr);
- if (min > tmp) {
- min = tmp;
- uabca = i;
- }
- }
-
- tmp = rate / (uabc + ERR_ROUNDING(rate_err_array[uabca]));
- if ((baudrate / 100) < abs(tmp - baudrate))
- dev_warn(dev, "UART freq error margin over %d\n", tmp);
-
- dev_dbg(dev, "target = %d, result = %d, uart = %d.%d\n",
- baudrate, tmp,
- uabc, rate_err_array[uabca]);
-
- uabca = (uabca & 0xF) << 4;
- uabc = (uabc / 16) - 1;
-
- sh_sir_write(self, IRIF_UART6, uabca);
- sh_sir_write(self, IRIF_UART7, uabc);
-
- return 0;
-}
-
-/************************************************************************
-
-
- iobuf function
-
-
-************************************************************************/
-static int __sh_sir_init_iobuf(iobuff_t *io, int size)
-{
- io->head = kmalloc(size, GFP_KERNEL);
- if (!io->head)
- return -ENOMEM;
-
- io->truesize = size;
- io->in_frame = FALSE;
- io->state = OUTSIDE_FRAME;
- io->data = io->head;
-
- return 0;
-}
-
-static void sh_sir_remove_iobuf(struct sh_sir_self *self)
-{
- kfree(self->rx_buff.head);
- kfree(self->tx_buff.head);
-
- self->rx_buff.head = NULL;
- self->tx_buff.head = NULL;
-}
-
-static int sh_sir_init_iobuf(struct sh_sir_self *self, int rxsize, int txsize)
-{
- int err = -ENOMEM;
-
- if (self->rx_buff.head ||
- self->tx_buff.head) {
- dev_err(&self->ndev->dev, "iobuff has already existed.");
- return err;
- }
-
- err = __sh_sir_init_iobuf(&self->rx_buff, rxsize);
- if (err)
- goto iobuf_err;
-
- err = __sh_sir_init_iobuf(&self->tx_buff, txsize);
-
-iobuf_err:
- if (err)
- sh_sir_remove_iobuf(self);
-
- return err;
-}
-
-/************************************************************************
-
-
- status function
-
-
-************************************************************************/
-static void sh_sir_clear_all_err(struct sh_sir_self *self)
-{
- /* Clear error flag for receive pulse width */
- sh_sir_update_bits(self, IRIF_SIR0, IRERRC, IRERRC);
-
- /* Clear frame / EOF error flag */
- sh_sir_write(self, IRIF_SIR_FLG, 0xffff);
-
- /* Clear all status error */
- sh_sir_write(self, IRIF_UART_STS2, 0);
-}
-
-static void sh_sir_set_phase(struct sh_sir_self *self, int phase)
-{
- u16 uart5 = 0;
- u16 uart0 = 0;
-
- switch (phase) {
- case TX_PHASE:
- uart5 = TBEIM;
- uart0 = TBEC | TIE;
- break;
- case TX_COMP_PHASE:
- uart5 = TSBEIM;
- uart0 = TIE;
- break;
- case RX_PHASE:
- uart5 = RX_MASK;
- uart0 = RIE;
- break;
- default:
- break;
- }
-
- sh_sir_write(self, IRIF_UART5, uart5);
- sh_sir_write(self, IRIF_UART0, uart0);
-}
-
-static int sh_sir_is_which_phase(struct sh_sir_self *self)
-{
- u16 val = sh_sir_read(self, IRIF_UART5);
-
- if (val & TBEIM)
- return TX_PHASE;
-
- if (val & TSBEIM)
- return TX_COMP_PHASE;
-
- if (val & RX_MASK)
- return RX_PHASE;
-
- return NONE_PHASE;
-}
-
-static void sh_sir_tx(struct sh_sir_self *self, int phase)
-{
- switch (phase) {
- case TX_PHASE:
- if (0 >= self->tx_buff.len) {
- sh_sir_set_phase(self, TX_COMP_PHASE);
- } else {
- sh_sir_write(self, IRIF_UART3, self->tx_buff.data[0]);
- self->tx_buff.len--;
- self->tx_buff.data++;
- }
- break;
- case TX_COMP_PHASE:
- sh_sir_set_phase(self, RX_PHASE);
- netif_wake_queue(self->ndev);
- break;
- default:
- dev_err(&self->ndev->dev, "should not happen\n");
- break;
- }
-}
-
-static int sh_sir_read_data(struct sh_sir_self *self)
-{
- u16 val = 0;
- int timeout = 1024;
-
- while (timeout--) {
- val = sh_sir_read(self, IRIF_UART1);
-
- /* data get */
- if (val & RBF) {
- if (val & (URSME | UROVE | URFRE | URPRE))
- break;
-
- return (int)sh_sir_read(self, IRIF_UART4);
- }
-
- udelay(1);
- }
-
- dev_err(&self->ndev->dev, "UART1 %04x : STATUS %04x\n",
- val, sh_sir_read(self, IRIF_UART_STS2));
-
- /* read data register for clear error */
- sh_sir_read(self, IRIF_UART4);
-
- return -1;
-}
-
-static void sh_sir_rx(struct sh_sir_self *self)
-{
- int timeout = 1024;
- int data;
-
- while (timeout--) {
- data = sh_sir_read_data(self);
- if (data < 0)
- break;
-
- async_unwrap_char(self->ndev, &self->ndev->stats,
- &self->rx_buff, (u8)data);
-
- if (EOFD & sh_sir_read(self, IRIF_SIR_FRM))
- continue;
-
- break;
- }
-}
-
-static irqreturn_t sh_sir_irq(int irq, void *dev_id)
-{
- struct sh_sir_self *self = dev_id;
- struct device *dev = &self->ndev->dev;
- int phase = sh_sir_is_which_phase(self);
-
- switch (phase) {
- case TX_COMP_PHASE:
- case TX_PHASE:
- sh_sir_tx(self, phase);
- break;
- case RX_PHASE:
- if (sh_sir_read(self, IRIF_SIR3))
- dev_err(dev, "rcv pulse width error occurred\n");
-
- sh_sir_rx(self);
- sh_sir_clear_all_err(self);
- break;
- default:
- dev_err(dev, "unknown interrupt\n");
- }
-
- return IRQ_HANDLED;
-}
-
-/************************************************************************
-
-
- net_device_ops function
-
-
-************************************************************************/
-static int sh_sir_hard_xmit(struct sk_buff *skb, struct net_device *ndev)
-{
- struct sh_sir_self *self = netdev_priv(ndev);
- int speed = irda_get_next_speed(skb);
-
- if ((0 < speed) &&
- (9600 != speed)) {
- dev_err(&ndev->dev, "support 9600 only (%d)\n", speed);
- return -EIO;
- }
-
- netif_stop_queue(ndev);
-
- self->tx_buff.data = self->tx_buff.head;
- self->tx_buff.len = 0;
- if (skb->len)
- self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data,
- self->tx_buff.truesize);
-
- sh_sir_set_phase(self, TX_PHASE);
- dev_kfree_skb(skb);
-
- return 0;
-}
-
-static int sh_sir_ioctl(struct net_device *ndev, struct ifreq *ifreq, int cmd)
-{
- /*
- * FIXME
- *
- * This function is needed for irda framework.
- * But nothing to do now
- */
- return 0;
-}
-
-static struct net_device_stats *sh_sir_stats(struct net_device *ndev)
-{
- struct sh_sir_self *self = netdev_priv(ndev);
-
- return &self->ndev->stats;
-}
-
-static int sh_sir_open(struct net_device *ndev)
-{
- struct sh_sir_self *self = netdev_priv(ndev);
- int err;
-
- clk_enable(self->clk);
- err = sh_sir_crc_init(self);
- if (err)
- goto open_err;
-
- sh_sir_set_baudrate(self, 9600);
-
- self->irlap = irlap_open(ndev, &self->qos, DRIVER_NAME);
- if (!self->irlap) {
- err = -ENODEV;
- goto open_err;
- }
-
- /*
- * Now enable the interrupt then start the queue
- */
- sh_sir_update_bits(self, IRIF_SIR_FRM, FRP, FRP);
- sh_sir_read(self, IRIF_UART1); /* flag clear */
- sh_sir_read(self, IRIF_UART4); /* flag clear */
- sh_sir_set_phase(self, RX_PHASE);
-
- netif_start_queue(ndev);
-
- dev_info(&self->ndev->dev, "opened\n");
-
- return 0;
-
-open_err:
- clk_disable(self->clk);
-
- return err;
-}
-
-static int sh_sir_stop(struct net_device *ndev)
-{
- struct sh_sir_self *self = netdev_priv(ndev);
-
- /* Stop IrLAP */
- if (self->irlap) {
- irlap_close(self->irlap);
- self->irlap = NULL;
- }
-
- netif_stop_queue(ndev);
-
- dev_info(&ndev->dev, "stopped\n");
-
- return 0;
-}
-
-static const struct net_device_ops sh_sir_ndo = {
- .ndo_open = sh_sir_open,
- .ndo_stop = sh_sir_stop,
- .ndo_start_xmit = sh_sir_hard_xmit,
- .ndo_do_ioctl = sh_sir_ioctl,
- .ndo_get_stats = sh_sir_stats,
-};
-
-/************************************************************************
-
-
- platform_driver function
-
-
-************************************************************************/
-static int sh_sir_probe(struct platform_device *pdev)
-{
- struct net_device *ndev;
- struct sh_sir_self *self;
- struct resource *res;
- char clk_name[8];
- int irq;
- int err = -ENOMEM;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- irq = platform_get_irq(pdev, 0);
- if (!res || irq < 0) {
- dev_err(&pdev->dev, "Not enough platform resources.\n");
- goto exit;
- }
-
- ndev = alloc_irdadev(sizeof(*self));
- if (!ndev)
- goto exit;
-
- self = netdev_priv(ndev);
- self->membase = ioremap_nocache(res->start, resource_size(res));
- if (!self->membase) {
- err = -ENXIO;
- dev_err(&pdev->dev, "Unable to ioremap.\n");
- goto err_mem_1;
- }
-
- err = sh_sir_init_iobuf(self, IRDA_SKB_MAX_MTU, IRDA_SIR_MAX_FRAME);
- if (err)
- goto err_mem_2;
-
- snprintf(clk_name, sizeof(clk_name), "irda%d", pdev->id);
- self->clk = clk_get(&pdev->dev, clk_name);
- if (IS_ERR(self->clk)) {
- dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
- err = -ENODEV;
- goto err_mem_3;
- }
-
- irda_init_max_qos_capabilies(&self->qos);
-
- ndev->netdev_ops = &sh_sir_ndo;
- ndev->irq = irq;
-
- self->ndev = ndev;
- self->qos.baud_rate.bits &= IR_9600; /* FIXME */
- self->qos.min_turn_time.bits = 1; /* 10 ms or more */
-
- irda_qos_bits_to_value(&self->qos);
-
- err = register_netdev(ndev);
- if (err)
- goto err_mem_4;
-
- platform_set_drvdata(pdev, ndev);
- err = devm_request_irq(&pdev->dev, irq, sh_sir_irq, 0, "sh_sir", self);
- if (err) {
- dev_warn(&pdev->dev, "Unable to attach sh_sir interrupt\n");
- goto err_mem_4;
- }
-
- dev_info(&pdev->dev, "SuperH IrDA probed\n");
-
- goto exit;
-
-err_mem_4:
- clk_put(self->clk);
-err_mem_3:
- sh_sir_remove_iobuf(self);
-err_mem_2:
- iounmap(self->membase);
-err_mem_1:
- free_netdev(ndev);
-exit:
- return err;
-}
-
-static int sh_sir_remove(struct platform_device *pdev)
-{
- struct net_device *ndev = platform_get_drvdata(pdev);
- struct sh_sir_self *self = netdev_priv(ndev);
-
- if (!self)
- return 0;
-
- unregister_netdev(ndev);
- clk_put(self->clk);
- sh_sir_remove_iobuf(self);
- iounmap(self->membase);
- free_netdev(ndev);
-
- return 0;
-}
-
-static struct platform_driver sh_sir_driver = {
- .probe = sh_sir_probe,
- .remove = sh_sir_remove,
- .driver = {
- .name = DRIVER_NAME,
- },
-};
-
-module_platform_driver(sh_sir_driver);
-
-MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>");
-MODULE_DESCRIPTION("SuperH IrDA driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/staging/irda/drivers/sir-dev.h b/drivers/staging/irda/drivers/sir-dev.h
deleted file mode 100644
index f50b9c1c0639..000000000000
--- a/drivers/staging/irda/drivers/sir-dev.h
+++ /dev/null
@@ -1,191 +0,0 @@
-/*********************************************************************
- *
- * sir.h: include file for irda-sir device abstraction layer
- *
- * Copyright (c) 2002 Martin Diehl
- *
- * 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 IRDA_SIR_H
-#define IRDA_SIR_H
-
-#include <linux/netdevice.h>
-#include <linux/workqueue.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irda_device.h> // iobuff_t
-
-struct sir_fsm {
- struct semaphore sem;
- struct delayed_work work;
- unsigned state, substate;
- int param;
- int result;
-};
-
-#define SIRDEV_STATE_WAIT_TX_COMPLETE 0x0100
-
-/* substates for wait_tx_complete */
-#define SIRDEV_STATE_WAIT_XMIT 0x0101
-#define SIRDEV_STATE_WAIT_UNTIL_SENT 0x0102
-#define SIRDEV_STATE_TX_DONE 0x0103
-
-#define SIRDEV_STATE_DONGLE_OPEN 0x0300
-
-/* 0x0301-0x03ff reserved for individual dongle substates */
-
-#define SIRDEV_STATE_DONGLE_CLOSE 0x0400
-
-/* 0x0401-0x04ff reserved for individual dongle substates */
-
-#define SIRDEV_STATE_SET_DTR_RTS 0x0500
-
-#define SIRDEV_STATE_SET_SPEED 0x0700
-#define SIRDEV_STATE_DONGLE_CHECK 0x0800
-#define SIRDEV_STATE_DONGLE_RESET 0x0900
-
-/* 0x0901-0x09ff reserved for individual dongle substates */
-
-#define SIRDEV_STATE_DONGLE_SPEED 0x0a00
-/* 0x0a01-0x0aff reserved for individual dongle substates */
-
-#define SIRDEV_STATE_PORT_SPEED 0x0b00
-#define SIRDEV_STATE_DONE 0x0c00
-#define SIRDEV_STATE_ERROR 0x0d00
-#define SIRDEV_STATE_COMPLETE 0x0e00
-
-#define SIRDEV_STATE_DEAD 0xffff
-
-
-struct sir_dev;
-
-struct dongle_driver {
-
- struct module *owner;
-
- const char *driver_name;
-
- IRDA_DONGLE type;
-
- int (*open)(struct sir_dev *dev);
- int (*close)(struct sir_dev *dev);
- int (*reset)(struct sir_dev *dev);
- int (*set_speed)(struct sir_dev *dev, unsigned speed);
-
- struct list_head dongle_list;
-};
-
-struct sir_driver {
-
- struct module *owner;
-
- const char *driver_name;
-
- int qos_mtt_bits;
-
- int (*chars_in_buffer)(struct sir_dev *dev);
- void (*wait_until_sent)(struct sir_dev *dev);
- int (*set_speed)(struct sir_dev *dev, unsigned speed);
- int (*set_dtr_rts)(struct sir_dev *dev, int dtr, int rts);
-
- int (*do_write)(struct sir_dev *dev, const unsigned char *ptr, size_t len);
-
- int (*start_dev)(struct sir_dev *dev);
- int (*stop_dev)(struct sir_dev *dev);
-};
-
-
-/* exported */
-
-int irda_register_dongle(struct dongle_driver *new);
-int irda_unregister_dongle(struct dongle_driver *drv);
-
-struct sir_dev *sirdev_get_instance(const struct sir_driver *drv,
- const char *name);
-int sirdev_put_instance(struct sir_dev *self);
-
-int sirdev_set_dongle(struct sir_dev *dev, IRDA_DONGLE type);
-void sirdev_write_complete(struct sir_dev *dev);
-int sirdev_receive(struct sir_dev *dev, const unsigned char *cp, size_t count);
-
-/* low level helpers for SIR device/dongle setup */
-int sirdev_raw_write(struct sir_dev *dev, const char *buf, int len);
-int sirdev_raw_read(struct sir_dev *dev, char *buf, int len);
-int sirdev_set_dtr_rts(struct sir_dev *dev, int dtr, int rts);
-
-/* not exported */
-
-int sirdev_get_dongle(struct sir_dev *self, IRDA_DONGLE type);
-int sirdev_put_dongle(struct sir_dev *self);
-
-void sirdev_enable_rx(struct sir_dev *dev);
-int sirdev_schedule_request(struct sir_dev *dev, int state, unsigned param);
-
-/* inline helpers */
-
-static inline int sirdev_schedule_speed(struct sir_dev *dev, unsigned speed)
-{
- return sirdev_schedule_request(dev, SIRDEV_STATE_SET_SPEED, speed);
-}
-
-static inline int sirdev_schedule_dongle_open(struct sir_dev *dev, int dongle_id)
-{
- return sirdev_schedule_request(dev, SIRDEV_STATE_DONGLE_OPEN, dongle_id);
-}
-
-static inline int sirdev_schedule_dongle_close(struct sir_dev *dev)
-{
- return sirdev_schedule_request(dev, SIRDEV_STATE_DONGLE_CLOSE, 0);
-}
-
-static inline int sirdev_schedule_dtr_rts(struct sir_dev *dev, int dtr, int rts)
-{
- int dtrrts;
-
- dtrrts = ((dtr) ? 0x02 : 0x00) | ((rts) ? 0x01 : 0x00);
- return sirdev_schedule_request(dev, SIRDEV_STATE_SET_DTR_RTS, dtrrts);
-}
-
-#if 0
-static inline int sirdev_schedule_mode(struct sir_dev *dev, int mode)
-{
- return sirdev_schedule_request(dev, SIRDEV_STATE_SET_MODE, mode);
-}
-#endif
-
-
-struct sir_dev {
- struct net_device *netdev;
-
- struct irlap_cb *irlap;
-
- struct qos_info qos;
-
- char hwname[32];
-
- struct sir_fsm fsm;
- atomic_t enable_rx;
- int raw_tx;
- spinlock_t tx_lock;
-
- u32 new_speed;
- u32 flags;
-
- unsigned speed;
-
- iobuff_t tx_buff; /* Transmit buffer */
- iobuff_t rx_buff; /* Receive buffer */
- struct sk_buff *tx_skb;
-
- const struct dongle_driver * dongle_drv;
- const struct sir_driver * drv;
- void *priv;
-
-};
-
-#endif /* IRDA_SIR_H */
diff --git a/drivers/staging/irda/drivers/sir_dev.c b/drivers/staging/irda/drivers/sir_dev.c
deleted file mode 100644
index 6af26a7d787c..000000000000
--- a/drivers/staging/irda/drivers/sir_dev.c
+++ /dev/null
@@ -1,987 +0,0 @@
-/*********************************************************************
- *
- * sir_dev.c: irda sir network device
- *
- * Copyright (c) 2002 Martin Diehl
- *
- * 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/hardirq.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/wrapper.h>
-#include <net/irda/irda_device.h>
-
-#include "sir-dev.h"
-
-
-static struct workqueue_struct *irda_sir_wq;
-
-/* STATE MACHINE */
-
-/* substate handler of the config-fsm to handle the cases where we want
- * to wait for transmit completion before changing the port configuration
- */
-
-static int sirdev_tx_complete_fsm(struct sir_dev *dev)
-{
- struct sir_fsm *fsm = &dev->fsm;
- unsigned next_state, delay;
- unsigned bytes_left;
-
- do {
- next_state = fsm->substate; /* default: stay in current substate */
- delay = 0;
-
- switch(fsm->substate) {
-
- case SIRDEV_STATE_WAIT_XMIT:
- if (dev->drv->chars_in_buffer)
- bytes_left = dev->drv->chars_in_buffer(dev);
- else
- bytes_left = 0;
- if (!bytes_left) {
- next_state = SIRDEV_STATE_WAIT_UNTIL_SENT;
- break;
- }
-
- if (dev->speed > 115200)
- delay = (bytes_left*8*10000) / (dev->speed/100);
- else if (dev->speed > 0)
- delay = (bytes_left*10*10000) / (dev->speed/100);
- else
- delay = 0;
- /* expected delay (usec) until remaining bytes are sent */
- if (delay < 100) {
- udelay(delay);
- delay = 0;
- break;
- }
- /* sleep some longer delay (msec) */
- delay = (delay+999) / 1000;
- break;
-
- case SIRDEV_STATE_WAIT_UNTIL_SENT:
- /* block until underlaying hardware buffer are empty */
- if (dev->drv->wait_until_sent)
- dev->drv->wait_until_sent(dev);
- next_state = SIRDEV_STATE_TX_DONE;
- break;
-
- case SIRDEV_STATE_TX_DONE:
- return 0;
-
- default:
- net_err_ratelimited("%s - undefined state\n", __func__);
- return -EINVAL;
- }
- fsm->substate = next_state;
- } while (delay == 0);
- return delay;
-}
-
-/*
- * Function sirdev_config_fsm
- *
- * State machine to handle the configuration of the device (and attached dongle, if any).
- * This handler is scheduled for execution in kIrDAd context, so we can sleep.
- * however, kIrDAd is shared by all sir_dev devices so we better don't sleep there too
- * long. Instead, for longer delays we start a timer to reschedule us later.
- * On entry, fsm->sem is always locked and the netdev xmit queue stopped.
- * Both must be unlocked/restarted on completion - but only on final exit.
- */
-
-static void sirdev_config_fsm(struct work_struct *work)
-{
- struct sir_dev *dev = container_of(work, struct sir_dev, fsm.work.work);
- struct sir_fsm *fsm = &dev->fsm;
- int next_state;
- int ret = -1;
- unsigned delay;
-
- pr_debug("%s(), <%ld>\n", __func__, jiffies);
-
- do {
- pr_debug("%s - state=0x%04x / substate=0x%04x\n",
- __func__, fsm->state, fsm->substate);
-
- next_state = fsm->state;
- delay = 0;
-
- switch(fsm->state) {
-
- case SIRDEV_STATE_DONGLE_OPEN:
- if (dev->dongle_drv != NULL) {
- ret = sirdev_put_dongle(dev);
- if (ret) {
- fsm->result = -EINVAL;
- next_state = SIRDEV_STATE_ERROR;
- break;
- }
- }
-
- /* Initialize dongle */
- ret = sirdev_get_dongle(dev, fsm->param);
- if (ret) {
- fsm->result = ret;
- next_state = SIRDEV_STATE_ERROR;
- break;
- }
-
- /* Dongles are powered through the modem control lines which
- * were just set during open. Before resetting, let's wait for
- * the power to stabilize. This is what some dongle drivers did
- * in open before, while others didn't - should be safe anyway.
- */
-
- delay = 50;
- fsm->substate = SIRDEV_STATE_DONGLE_RESET;
- next_state = SIRDEV_STATE_DONGLE_RESET;
-
- fsm->param = 9600;
-
- break;
-
- case SIRDEV_STATE_DONGLE_CLOSE:
- /* shouldn't we just treat this as success=? */
- if (dev->dongle_drv == NULL) {
- fsm->result = -EINVAL;
- next_state = SIRDEV_STATE_ERROR;
- break;
- }
-
- ret = sirdev_put_dongle(dev);
- if (ret) {
- fsm->result = ret;
- next_state = SIRDEV_STATE_ERROR;
- break;
- }
- next_state = SIRDEV_STATE_DONE;
- break;
-
- case SIRDEV_STATE_SET_DTR_RTS:
- ret = sirdev_set_dtr_rts(dev,
- (fsm->param&0x02) ? TRUE : FALSE,
- (fsm->param&0x01) ? TRUE : FALSE);
- next_state = SIRDEV_STATE_DONE;
- break;
-
- case SIRDEV_STATE_SET_SPEED:
- fsm->substate = SIRDEV_STATE_WAIT_XMIT;
- next_state = SIRDEV_STATE_DONGLE_CHECK;
- break;
-
- case SIRDEV_STATE_DONGLE_CHECK:
- ret = sirdev_tx_complete_fsm(dev);
- if (ret < 0) {
- fsm->result = ret;
- next_state = SIRDEV_STATE_ERROR;
- break;
- }
- if ((delay=ret) != 0)
- break;
-
- if (dev->dongle_drv) {
- fsm->substate = SIRDEV_STATE_DONGLE_RESET;
- next_state = SIRDEV_STATE_DONGLE_RESET;
- }
- else {
- dev->speed = fsm->param;
- next_state = SIRDEV_STATE_PORT_SPEED;
- }
- break;
-
- case SIRDEV_STATE_DONGLE_RESET:
- if (dev->dongle_drv->reset) {
- ret = dev->dongle_drv->reset(dev);
- if (ret < 0) {
- fsm->result = ret;
- next_state = SIRDEV_STATE_ERROR;
- break;
- }
- }
- else
- ret = 0;
- if ((delay=ret) == 0) {
- /* set serial port according to dongle default speed */
- if (dev->drv->set_speed)
- dev->drv->set_speed(dev, dev->speed);
- fsm->substate = SIRDEV_STATE_DONGLE_SPEED;
- next_state = SIRDEV_STATE_DONGLE_SPEED;
- }
- break;
-
- case SIRDEV_STATE_DONGLE_SPEED:
- if (dev->dongle_drv->set_speed) {
- ret = dev->dongle_drv->set_speed(dev, fsm->param);
- if (ret < 0) {
- fsm->result = ret;
- next_state = SIRDEV_STATE_ERROR;
- break;
- }
- }
- else
- ret = 0;
- if ((delay=ret) == 0)
- next_state = SIRDEV_STATE_PORT_SPEED;
- break;
-
- case SIRDEV_STATE_PORT_SPEED:
- /* Finally we are ready to change the serial port speed */
- if (dev->drv->set_speed)
- dev->drv->set_speed(dev, dev->speed);
- dev->new_speed = 0;
- next_state = SIRDEV_STATE_DONE;
- break;
-
- case SIRDEV_STATE_DONE:
- /* Signal network layer so it can send more frames */
- netif_wake_queue(dev->netdev);
- next_state = SIRDEV_STATE_COMPLETE;
- break;
-
- default:
- net_err_ratelimited("%s - undefined state\n", __func__);
- fsm->result = -EINVAL;
- /* fall thru */
-
- case SIRDEV_STATE_ERROR:
- net_err_ratelimited("%s - error: %d\n",
- __func__, fsm->result);
-
-#if 0 /* don't enable this before we have netdev->tx_timeout to recover */
- netif_stop_queue(dev->netdev);
-#else
- netif_wake_queue(dev->netdev);
-#endif
- /* fall thru */
-
- case SIRDEV_STATE_COMPLETE:
- /* config change finished, so we are not busy any longer */
- sirdev_enable_rx(dev);
- up(&fsm->sem);
- return;
- }
- fsm->state = next_state;
- } while(!delay);
-
- queue_delayed_work(irda_sir_wq, &fsm->work, msecs_to_jiffies(delay));
-}
-
-/* schedule some device configuration task for execution by kIrDAd
- * on behalf of the above state machine.
- * can be called from process or interrupt/tasklet context.
- */
-
-int sirdev_schedule_request(struct sir_dev *dev, int initial_state, unsigned param)
-{
- struct sir_fsm *fsm = &dev->fsm;
-
- pr_debug("%s - state=0x%04x / param=%u\n", __func__,
- initial_state, param);
-
- if (down_trylock(&fsm->sem)) {
- if (in_interrupt() || in_atomic() || irqs_disabled()) {
- pr_debug("%s(), state machine busy!\n", __func__);
- return -EWOULDBLOCK;
- } else
- down(&fsm->sem);
- }
-
- if (fsm->state == SIRDEV_STATE_DEAD) {
- /* race with sirdev_close should never happen */
- net_err_ratelimited("%s(), instance staled!\n", __func__);
- up(&fsm->sem);
- return -ESTALE; /* or better EPIPE? */
- }
-
- netif_stop_queue(dev->netdev);
- atomic_set(&dev->enable_rx, 0);
-
- fsm->state = initial_state;
- fsm->param = param;
- fsm->result = 0;
-
- INIT_DELAYED_WORK(&fsm->work, sirdev_config_fsm);
- queue_delayed_work(irda_sir_wq, &fsm->work, 0);
- return 0;
-}
-
-
-/***************************************************************************/
-
-void sirdev_enable_rx(struct sir_dev *dev)
-{
- if (unlikely(atomic_read(&dev->enable_rx)))
- return;
-
- /* flush rx-buffer - should also help in case of problems with echo cancelation */
- dev->rx_buff.data = dev->rx_buff.head;
- dev->rx_buff.len = 0;
- dev->rx_buff.in_frame = FALSE;
- dev->rx_buff.state = OUTSIDE_FRAME;
- atomic_set(&dev->enable_rx, 1);
-}
-
-static int sirdev_is_receiving(struct sir_dev *dev)
-{
- if (!atomic_read(&dev->enable_rx))
- return 0;
-
- return dev->rx_buff.state != OUTSIDE_FRAME;
-}
-
-int sirdev_set_dongle(struct sir_dev *dev, IRDA_DONGLE type)
-{
- int err;
-
- pr_debug("%s : requesting dongle %d.\n", __func__, type);
-
- err = sirdev_schedule_dongle_open(dev, type);
- if (unlikely(err))
- return err;
- down(&dev->fsm.sem); /* block until config change completed */
- err = dev->fsm.result;
- up(&dev->fsm.sem);
- return err;
-}
-EXPORT_SYMBOL(sirdev_set_dongle);
-
-/* used by dongle drivers for dongle programming */
-
-int sirdev_raw_write(struct sir_dev *dev, const char *buf, int len)
-{
- unsigned long flags;
- int ret;
-
- if (unlikely(len > dev->tx_buff.truesize))
- return -ENOSPC;
-
- spin_lock_irqsave(&dev->tx_lock, flags); /* serialize with other tx operations */
- while (dev->tx_buff.len > 0) { /* wait until tx idle */
- spin_unlock_irqrestore(&dev->tx_lock, flags);
- msleep(10);
- spin_lock_irqsave(&dev->tx_lock, flags);
- }
-
- dev->tx_buff.data = dev->tx_buff.head;
- memcpy(dev->tx_buff.data, buf, len);
- dev->tx_buff.len = len;
-
- ret = dev->drv->do_write(dev, dev->tx_buff.data, dev->tx_buff.len);
- if (ret > 0) {
- pr_debug("%s(), raw-tx started\n", __func__);
-
- dev->tx_buff.data += ret;
- dev->tx_buff.len -= ret;
- dev->raw_tx = 1;
- ret = len; /* all data is going to be sent */
- }
- spin_unlock_irqrestore(&dev->tx_lock, flags);
- return ret;
-}
-EXPORT_SYMBOL(sirdev_raw_write);
-
-/* seems some dongle drivers may need this */
-
-int sirdev_raw_read(struct sir_dev *dev, char *buf, int len)
-{
- int count;
-
- if (atomic_read(&dev->enable_rx))
- return -EIO; /* fail if we expect irda-frames */
-
- count = (len < dev->rx_buff.len) ? len : dev->rx_buff.len;
-
- if (count > 0) {
- memcpy(buf, dev->rx_buff.data, count);
- dev->rx_buff.data += count;
- dev->rx_buff.len -= count;
- }
-
- /* remaining stuff gets flushed when re-enabling normal rx */
-
- return count;
-}
-EXPORT_SYMBOL(sirdev_raw_read);
-
-int sirdev_set_dtr_rts(struct sir_dev *dev, int dtr, int rts)
-{
- int ret = -ENXIO;
- if (dev->drv->set_dtr_rts)
- ret = dev->drv->set_dtr_rts(dev, dtr, rts);
- return ret;
-}
-EXPORT_SYMBOL(sirdev_set_dtr_rts);
-
-/**********************************************************************/
-
-/* called from client driver - likely with bh-context - to indicate
- * it made some progress with transmission. Hence we send the next
- * chunk, if any, or complete the skb otherwise
- */
-
-void sirdev_write_complete(struct sir_dev *dev)
-{
- unsigned long flags;
- struct sk_buff *skb;
- int actual = 0;
- int err;
-
- spin_lock_irqsave(&dev->tx_lock, flags);
-
- pr_debug("%s() - dev->tx_buff.len = %d\n",
- __func__, dev->tx_buff.len);
-
- if (likely(dev->tx_buff.len > 0)) {
- /* Write data left in transmit buffer */
- actual = dev->drv->do_write(dev, dev->tx_buff.data, dev->tx_buff.len);
-
- if (likely(actual>0)) {
- dev->tx_buff.data += actual;
- dev->tx_buff.len -= actual;
- }
- else if (unlikely(actual<0)) {
- /* could be dropped later when we have tx_timeout to recover */
- net_err_ratelimited("%s: drv->do_write failed (%d)\n",
- __func__, actual);
- if ((skb=dev->tx_skb) != NULL) {
- dev->tx_skb = NULL;
- dev_kfree_skb_any(skb);
- dev->netdev->stats.tx_errors++;
- dev->netdev->stats.tx_dropped++;
- }
- dev->tx_buff.len = 0;
- }
- if (dev->tx_buff.len > 0)
- goto done; /* more data to send later */
- }
-
- if (unlikely(dev->raw_tx != 0)) {
- /* in raw mode we are just done now after the buffer was sent
- * completely. Since this was requested by some dongle driver
- * running under the control of the irda-thread we must take
- * care here not to re-enable the queue. The queue will be
- * restarted when the irda-thread has completed the request.
- */
-
- pr_debug("%s(), raw-tx done\n", __func__);
- dev->raw_tx = 0;
- goto done; /* no post-frame handling in raw mode */
- }
-
- /* we have finished now sending this skb.
- * update statistics and free the skb.
- * finally we check and trigger a pending speed change, if any.
- * if not we switch to rx mode and wake the queue for further
- * packets.
- * note the scheduled speed request blocks until the lower
- * client driver and the corresponding hardware has really
- * finished sending all data (xmit fifo drained f.e.)
- * before the speed change gets finally done and the queue
- * re-activated.
- */
-
- pr_debug("%s(), finished with frame!\n", __func__);
-
- if ((skb=dev->tx_skb) != NULL) {
- dev->tx_skb = NULL;
- dev->netdev->stats.tx_packets++;
- dev->netdev->stats.tx_bytes += skb->len;
- dev_kfree_skb_any(skb);
- }
-
- if (unlikely(dev->new_speed > 0)) {
- pr_debug("%s(), Changing speed!\n", __func__);
- err = sirdev_schedule_speed(dev, dev->new_speed);
- if (unlikely(err)) {
- /* should never happen
- * forget the speed change and hope the stack recovers
- */
- net_err_ratelimited("%s - schedule speed change failed: %d\n",
- __func__, err);
- netif_wake_queue(dev->netdev);
- }
- /* else: success
- * speed change in progress now
- * on completion dev->new_speed gets cleared,
- * rx-reenabled and the queue restarted
- */
- }
- else {
- sirdev_enable_rx(dev);
- netif_wake_queue(dev->netdev);
- }
-
-done:
- spin_unlock_irqrestore(&dev->tx_lock, flags);
-}
-EXPORT_SYMBOL(sirdev_write_complete);
-
-/* called from client driver - likely with bh-context - to give us
- * some more received bytes. We put them into the rx-buffer,
- * normally unwrapping and building LAP-skb's (unless rx disabled)
- */
-
-int sirdev_receive(struct sir_dev *dev, const unsigned char *cp, size_t count)
-{
- if (!dev || !dev->netdev) {
- net_warn_ratelimited("%s(), not ready yet!\n", __func__);
- return -1;
- }
-
- if (!dev->irlap) {
- net_warn_ratelimited("%s - too early: %p / %zd!\n",
- __func__, cp, count);
- return -1;
- }
-
- if (cp==NULL) {
- /* error already at lower level receive
- * just update stats and set media busy
- */
- irda_device_set_media_busy(dev->netdev, TRUE);
- dev->netdev->stats.rx_dropped++;
- pr_debug("%s; rx-drop: %zd\n", __func__, count);
- return 0;
- }
-
- /* Read the characters into the buffer */
- if (likely(atomic_read(&dev->enable_rx))) {
- while (count--)
- /* Unwrap and destuff one byte */
- async_unwrap_char(dev->netdev, &dev->netdev->stats,
- &dev->rx_buff, *cp++);
- } else {
- while (count--) {
- /* rx not enabled: save the raw bytes and never
- * trigger any netif_rx. The received bytes are flushed
- * later when we re-enable rx but might be read meanwhile
- * by the dongle driver.
- */
- dev->rx_buff.data[dev->rx_buff.len++] = *cp++;
-
- /* What should we do when the buffer is full? */
- if (unlikely(dev->rx_buff.len == dev->rx_buff.truesize))
- dev->rx_buff.len = 0;
- }
- }
-
- return 0;
-}
-EXPORT_SYMBOL(sirdev_receive);
-
-/**********************************************************************/
-
-/* callbacks from network layer */
-
-static netdev_tx_t sirdev_hard_xmit(struct sk_buff *skb,
- struct net_device *ndev)
-{
- struct sir_dev *dev = netdev_priv(ndev);
- unsigned long flags;
- int actual = 0;
- int err;
- s32 speed;
-
- IRDA_ASSERT(dev != NULL, return NETDEV_TX_OK;);
-
- netif_stop_queue(ndev);
-
- pr_debug("%s(), skb->len = %d\n", __func__, skb->len);
-
- speed = irda_get_next_speed(skb);
- if ((speed != dev->speed) && (speed != -1)) {
- if (!skb->len) {
- err = sirdev_schedule_speed(dev, speed);
- if (unlikely(err == -EWOULDBLOCK)) {
- /* Failed to initiate the speed change, likely the fsm
- * is still busy (pretty unlikely, but...)
- * We refuse to accept the skb and return with the queue
- * stopped so the network layer will retry after the
- * fsm completes and wakes the queue.
- */
- return NETDEV_TX_BUSY;
- }
- else if (unlikely(err)) {
- /* other fatal error - forget the speed change and
- * hope the stack will recover somehow
- */
- netif_start_queue(ndev);
- }
- /* else: success
- * speed change in progress now
- * on completion the queue gets restarted
- */
-
- dev_kfree_skb_any(skb);
- return NETDEV_TX_OK;
- } else
- dev->new_speed = speed;
- }
-
- /* Init tx buffer*/
- dev->tx_buff.data = dev->tx_buff.head;
-
- /* Check problems */
- if(spin_is_locked(&dev->tx_lock)) {
- pr_debug("%s(), write not completed\n", __func__);
- }
-
- /* serialize with write completion */
- spin_lock_irqsave(&dev->tx_lock, flags);
-
- /* Copy skb to tx_buff while wrapping, stuffing and making CRC */
- dev->tx_buff.len = async_wrap_skb(skb, dev->tx_buff.data, dev->tx_buff.truesize);
-
- /* transmission will start now - disable receive.
- * if we are just in the middle of an incoming frame,
- * treat it as collision. probably it's a good idea to
- * reset the rx_buf OUTSIDE_FRAME in this case too?
- */
- atomic_set(&dev->enable_rx, 0);
- if (unlikely(sirdev_is_receiving(dev)))
- dev->netdev->stats.collisions++;
-
- actual = dev->drv->do_write(dev, dev->tx_buff.data, dev->tx_buff.len);
-
- if (likely(actual > 0)) {
- dev->tx_skb = skb;
- dev->tx_buff.data += actual;
- dev->tx_buff.len -= actual;
- }
- else if (unlikely(actual < 0)) {
- /* could be dropped later when we have tx_timeout to recover */
- net_err_ratelimited("%s: drv->do_write failed (%d)\n",
- __func__, actual);
- dev_kfree_skb_any(skb);
- dev->netdev->stats.tx_errors++;
- dev->netdev->stats.tx_dropped++;
- netif_wake_queue(ndev);
- }
- spin_unlock_irqrestore(&dev->tx_lock, flags);
-
- return NETDEV_TX_OK;
-}
-
-/* called from network layer with rtnl hold */
-
-static int sirdev_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
-{
- struct if_irda_req *irq = (struct if_irda_req *) rq;
- struct sir_dev *dev = netdev_priv(ndev);
- int ret = 0;
-
- IRDA_ASSERT(dev != NULL, return -1;);
-
- pr_debug("%s(), %s, (cmd=0x%X)\n", __func__, ndev->name, cmd);
-
- switch (cmd) {
- case SIOCSBANDWIDTH: /* Set bandwidth */
- if (!capable(CAP_NET_ADMIN))
- ret = -EPERM;
- else
- ret = sirdev_schedule_speed(dev, irq->ifr_baudrate);
- /* cannot sleep here for completion
- * we are called from network layer with rtnl hold
- */
- break;
-
- case SIOCSDONGLE: /* Set dongle */
- if (!capable(CAP_NET_ADMIN))
- ret = -EPERM;
- else
- ret = sirdev_schedule_dongle_open(dev, irq->ifr_dongle);
- /* cannot sleep here for completion
- * we are called from network layer with rtnl hold
- */
- break;
-
- case SIOCSMEDIABUSY: /* Set media busy */
- if (!capable(CAP_NET_ADMIN))
- ret = -EPERM;
- else
- irda_device_set_media_busy(dev->netdev, TRUE);
- break;
-
- case SIOCGRECEIVING: /* Check if we are receiving right now */
- irq->ifr_receiving = sirdev_is_receiving(dev);
- break;
-
- case SIOCSDTRRTS:
- if (!capable(CAP_NET_ADMIN))
- ret = -EPERM;
- else
- ret = sirdev_schedule_dtr_rts(dev, irq->ifr_dtr, irq->ifr_rts);
- /* cannot sleep here for completion
- * we are called from network layer with rtnl hold
- */
- break;
-
- case SIOCSMODE:
-#if 0
- if (!capable(CAP_NET_ADMIN))
- ret = -EPERM;
- else
- ret = sirdev_schedule_mode(dev, irq->ifr_mode);
- /* cannot sleep here for completion
- * we are called from network layer with rtnl hold
- */
- break;
-#endif
- default:
- ret = -EOPNOTSUPP;
- }
-
- return ret;
-}
-
-/* ----------------------------------------------------------------------------- */
-
-#define SIRBUF_ALLOCSIZE 4269 /* worst case size of a wrapped IrLAP frame */
-
-static int sirdev_alloc_buffers(struct sir_dev *dev)
-{
- dev->tx_buff.truesize = SIRBUF_ALLOCSIZE;
- dev->rx_buff.truesize = IRDA_SKB_MAX_MTU;
-
- /* Bootstrap ZeroCopy Rx */
- dev->rx_buff.skb = __netdev_alloc_skb(dev->netdev, dev->rx_buff.truesize,
- GFP_KERNEL);
- if (dev->rx_buff.skb == NULL)
- return -ENOMEM;
- skb_reserve(dev->rx_buff.skb, 1);
- dev->rx_buff.head = dev->rx_buff.skb->data;
-
- dev->tx_buff.head = kmalloc(dev->tx_buff.truesize, GFP_KERNEL);
- if (dev->tx_buff.head == NULL) {
- kfree_skb(dev->rx_buff.skb);
- dev->rx_buff.skb = NULL;
- dev->rx_buff.head = NULL;
- return -ENOMEM;
- }
-
- dev->tx_buff.data = dev->tx_buff.head;
- dev->rx_buff.data = dev->rx_buff.head;
- dev->tx_buff.len = 0;
- dev->rx_buff.len = 0;
-
- dev->rx_buff.in_frame = FALSE;
- dev->rx_buff.state = OUTSIDE_FRAME;
- return 0;
-};
-
-static void sirdev_free_buffers(struct sir_dev *dev)
-{
- kfree_skb(dev->rx_buff.skb);
- kfree(dev->tx_buff.head);
- dev->rx_buff.head = dev->tx_buff.head = NULL;
- dev->rx_buff.skb = NULL;
-}
-
-static int sirdev_open(struct net_device *ndev)
-{
- struct sir_dev *dev = netdev_priv(ndev);
- const struct sir_driver *drv = dev->drv;
-
- if (!drv)
- return -ENODEV;
-
- /* increase the reference count of the driver module before doing serious stuff */
- if (!try_module_get(drv->owner))
- return -ESTALE;
-
- if (sirdev_alloc_buffers(dev))
- goto errout_dec;
-
- if (!dev->drv->start_dev || dev->drv->start_dev(dev))
- goto errout_free;
-
- sirdev_enable_rx(dev);
- dev->raw_tx = 0;
-
- netif_start_queue(ndev);
- dev->irlap = irlap_open(ndev, &dev->qos, dev->hwname);
- if (!dev->irlap)
- goto errout_stop;
-
- netif_wake_queue(ndev);
-
- pr_debug("%s - done, speed = %d\n", __func__, dev->speed);
-
- return 0;
-
-errout_stop:
- atomic_set(&dev->enable_rx, 0);
- if (dev->drv->stop_dev)
- dev->drv->stop_dev(dev);
-errout_free:
- sirdev_free_buffers(dev);
-errout_dec:
- module_put(drv->owner);
- return -EAGAIN;
-}
-
-static int sirdev_close(struct net_device *ndev)
-{
- struct sir_dev *dev = netdev_priv(ndev);
- const struct sir_driver *drv;
-
-/* pr_debug("%s\n", __func__); */
-
- netif_stop_queue(ndev);
-
- down(&dev->fsm.sem); /* block on pending config completion */
-
- atomic_set(&dev->enable_rx, 0);
-
- if (unlikely(!dev->irlap))
- goto out;
- irlap_close(dev->irlap);
- dev->irlap = NULL;
-
- drv = dev->drv;
- if (unlikely(!drv || !dev->priv))
- goto out;
-
- if (drv->stop_dev)
- drv->stop_dev(dev);
-
- sirdev_free_buffers(dev);
- module_put(drv->owner);
-
-out:
- dev->speed = 0;
- up(&dev->fsm.sem);
- return 0;
-}
-
-static const struct net_device_ops sirdev_ops = {
- .ndo_start_xmit = sirdev_hard_xmit,
- .ndo_open = sirdev_open,
- .ndo_stop = sirdev_close,
- .ndo_do_ioctl = sirdev_ioctl,
-};
-/* ----------------------------------------------------------------------------- */
-
-struct sir_dev * sirdev_get_instance(const struct sir_driver *drv, const char *name)
-{
- struct net_device *ndev;
- struct sir_dev *dev;
-
- pr_debug("%s - %s\n", __func__, name);
-
- /* instead of adding tests to protect against drv->do_write==NULL
- * at several places we refuse to create a sir_dev instance for
- * drivers which don't implement do_write.
- */
- if (!drv || !drv->do_write)
- return NULL;
-
- /*
- * Allocate new instance of the device
- */
- ndev = alloc_irdadev(sizeof(*dev));
- if (ndev == NULL) {
- net_err_ratelimited("%s - Can't allocate memory for IrDA control block!\n",
- __func__);
- goto out;
- }
- dev = netdev_priv(ndev);
-
- irda_init_max_qos_capabilies(&dev->qos);
- dev->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
- dev->qos.min_turn_time.bits = drv->qos_mtt_bits;
- irda_qos_bits_to_value(&dev->qos);
-
- strncpy(dev->hwname, name, sizeof(dev->hwname)-1);
-
- atomic_set(&dev->enable_rx, 0);
- dev->tx_skb = NULL;
-
- spin_lock_init(&dev->tx_lock);
- sema_init(&dev->fsm.sem, 1);
-
- dev->drv = drv;
- dev->netdev = ndev;
-
- /* Override the network functions we need to use */
- ndev->netdev_ops = &sirdev_ops;
-
- if (register_netdev(ndev)) {
- net_err_ratelimited("%s(), register_netdev() failed!\n",
- __func__);
- goto out_freenetdev;
- }
-
- return dev;
-
-out_freenetdev:
- free_netdev(ndev);
-out:
- return NULL;
-}
-EXPORT_SYMBOL(sirdev_get_instance);
-
-int sirdev_put_instance(struct sir_dev *dev)
-{
- int err = 0;
-
- pr_debug("%s\n", __func__);
-
- atomic_set(&dev->enable_rx, 0);
-
- netif_carrier_off(dev->netdev);
- netif_device_detach(dev->netdev);
-
- if (dev->dongle_drv)
- err = sirdev_schedule_dongle_close(dev);
- if (err)
- net_err_ratelimited("%s - error %d\n", __func__, err);
-
- sirdev_close(dev->netdev);
-
- down(&dev->fsm.sem);
- dev->fsm.state = SIRDEV_STATE_DEAD; /* mark staled */
- dev->dongle_drv = NULL;
- dev->priv = NULL;
- up(&dev->fsm.sem);
-
- /* Remove netdevice */
- unregister_netdev(dev->netdev);
-
- free_netdev(dev->netdev);
-
- return 0;
-}
-EXPORT_SYMBOL(sirdev_put_instance);
-
-static int __init sir_wq_init(void)
-{
- irda_sir_wq = create_singlethread_workqueue("irda_sir_wq");
- if (!irda_sir_wq)
- return -ENOMEM;
- return 0;
-}
-
-static void __exit sir_wq_exit(void)
-{
- destroy_workqueue(irda_sir_wq);
-}
-
-module_init(sir_wq_init);
-module_exit(sir_wq_exit);
-
-MODULE_AUTHOR("Martin Diehl <info@mdiehl.de>");
-MODULE_DESCRIPTION("IrDA SIR core");
-MODULE_LICENSE("GPL");
diff --git a/drivers/staging/irda/drivers/sir_dongle.c b/drivers/staging/irda/drivers/sir_dongle.c
deleted file mode 100644
index 7436f73ff1bb..000000000000
--- a/drivers/staging/irda/drivers/sir_dongle.c
+++ /dev/null
@@ -1,133 +0,0 @@
-/*********************************************************************
- *
- * sir_dongle.c: manager for serial dongle protocol drivers
- *
- * Copyright (c) 2002 Martin Diehl
- *
- * 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/module.h>
-#include <linux/kernel.h>
-#include <linux/kmod.h>
-#include <linux/mutex.h>
-
-#include <net/irda/irda.h>
-
-#include "sir-dev.h"
-
-/**************************************************************************
- *
- * dongle registration and attachment
- *
- */
-
-static LIST_HEAD(dongle_list); /* list of registered dongle drivers */
-static DEFINE_MUTEX(dongle_list_lock); /* protects the list */
-
-int irda_register_dongle(struct dongle_driver *new)
-{
- struct list_head *entry;
- struct dongle_driver *drv;
-
- pr_debug("%s : registering dongle \"%s\" (%d).\n",
- __func__, new->driver_name, new->type);
-
- mutex_lock(&dongle_list_lock);
- list_for_each(entry, &dongle_list) {
- drv = list_entry(entry, struct dongle_driver, dongle_list);
- if (new->type == drv->type) {
- mutex_unlock(&dongle_list_lock);
- return -EEXIST;
- }
- }
- list_add(&new->dongle_list, &dongle_list);
- mutex_unlock(&dongle_list_lock);
- return 0;
-}
-EXPORT_SYMBOL(irda_register_dongle);
-
-int irda_unregister_dongle(struct dongle_driver *drv)
-{
- mutex_lock(&dongle_list_lock);
- list_del(&drv->dongle_list);
- mutex_unlock(&dongle_list_lock);
- return 0;
-}
-EXPORT_SYMBOL(irda_unregister_dongle);
-
-int sirdev_get_dongle(struct sir_dev *dev, IRDA_DONGLE type)
-{
- struct list_head *entry;
- const struct dongle_driver *drv = NULL;
- int err = -EINVAL;
-
- request_module("irda-dongle-%d", type);
-
- if (dev->dongle_drv != NULL)
- return -EBUSY;
-
- /* serialize access to the list of registered dongles */
- mutex_lock(&dongle_list_lock);
-
- list_for_each(entry, &dongle_list) {
- drv = list_entry(entry, struct dongle_driver, dongle_list);
- if (drv->type == type)
- break;
- else
- drv = NULL;
- }
-
- if (!drv) {
- err = -ENODEV;
- goto out_unlock; /* no such dongle */
- }
-
- /* handling of SMP races with dongle module removal - three cases:
- * 1) dongle driver was already unregistered - then we haven't found the
- * requested dongle above and are already out here
- * 2) the module is already marked deleted but the driver is still
- * registered - then the try_module_get() below will fail
- * 3) the try_module_get() below succeeds before the module is marked
- * deleted - then sys_delete_module() fails and prevents the removal
- * because the module is in use.
- */
-
- if (!try_module_get(drv->owner)) {
- err = -ESTALE;
- goto out_unlock; /* rmmod already pending */
- }
- dev->dongle_drv = drv;
-
- if (!drv->open || (err=drv->open(dev))!=0)
- goto out_reject; /* failed to open driver */
-
- mutex_unlock(&dongle_list_lock);
- return 0;
-
-out_reject:
- dev->dongle_drv = NULL;
- module_put(drv->owner);
-out_unlock:
- mutex_unlock(&dongle_list_lock);
- return err;
-}
-
-int sirdev_put_dongle(struct sir_dev *dev)
-{
- const struct dongle_driver *drv = dev->dongle_drv;
-
- if (drv) {
- if (drv->close)
- drv->close(dev); /* close this dongle instance */
-
- dev->dongle_drv = NULL; /* unlink the dongle driver */
- module_put(drv->owner);/* decrement driver's module refcount */
- }
-
- return 0;
-}
diff --git a/drivers/staging/irda/drivers/smsc-ircc2.c b/drivers/staging/irda/drivers/smsc-ircc2.c
deleted file mode 100644
index 19a55cba6beb..000000000000
--- a/drivers/staging/irda/drivers/smsc-ircc2.c
+++ /dev/null
@@ -1,3026 +0,0 @@
-/*********************************************************************
- *
- * Description: Driver for the SMC Infrared Communications Controller
- * Author: Daniele Peri (peri@csai.unipa.it)
- * Created at:
- * Modified at:
- * Modified by:
- *
- * Copyright (c) 2002 Daniele Peri
- * All Rights Reserved.
- * Copyright (c) 2002 Jean Tourrilhes
- * Copyright (c) 2006 Linus Walleij
- *
- *
- * Based on smc-ircc.c:
- *
- * Copyright (c) 2001 Stefani Seibold
- * Copyright (c) 1999-2001 Dag Brattli
- * Copyright (c) 1998-1999 Thomas Davis,
- *
- * and irport.c:
- *
- * Copyright (c) 1997, 1998, 1999-2000 Dag Brattli, All Rights Reserved.
- *
- *
- * 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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/rtnetlink.h>
-#include <linux/serial_reg.h>
-#include <linux/dma-mapping.h>
-#include <linux/pnp.h>
-#include <linux/platform_device.h>
-#include <linux/gfp.h>
-
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <asm/byteorder.h>
-
-#include <linux/spinlock.h>
-#include <linux/pm.h>
-#ifdef CONFIG_PCI
-#include <linux/pci.h>
-#endif
-
-#include <net/irda/wrapper.h>
-#include <net/irda/irda.h>
-#include <net/irda/irda_device.h>
-
-#include "smsc-ircc2.h"
-#include "smsc-sio.h"
-
-
-MODULE_AUTHOR("Daniele Peri <peri@csai.unipa.it>");
-MODULE_DESCRIPTION("SMC IrCC SIR/FIR controller driver");
-MODULE_LICENSE("GPL");
-
-static bool smsc_nopnp = true;
-module_param_named(nopnp, smsc_nopnp, bool, 0);
-MODULE_PARM_DESC(nopnp, "Do not use PNP to detect controller settings, defaults to true");
-
-#define DMA_INVAL 255
-static int ircc_dma = DMA_INVAL;
-module_param_hw(ircc_dma, int, dma, 0);
-MODULE_PARM_DESC(ircc_dma, "DMA channel");
-
-#define IRQ_INVAL 255
-static int ircc_irq = IRQ_INVAL;
-module_param_hw(ircc_irq, int, irq, 0);
-MODULE_PARM_DESC(ircc_irq, "IRQ line");
-
-static int ircc_fir;
-module_param_hw(ircc_fir, int, ioport, 0);
-MODULE_PARM_DESC(ircc_fir, "FIR Base Address");
-
-static int ircc_sir;
-module_param_hw(ircc_sir, int, ioport, 0);
-MODULE_PARM_DESC(ircc_sir, "SIR Base Address");
-
-static int ircc_cfg;
-module_param_hw(ircc_cfg, int, ioport, 0);
-MODULE_PARM_DESC(ircc_cfg, "Configuration register base address");
-
-static int ircc_transceiver;
-module_param(ircc_transceiver, int, 0);
-MODULE_PARM_DESC(ircc_transceiver, "Transceiver type");
-
-/* Types */
-
-#ifdef CONFIG_PCI
-struct smsc_ircc_subsystem_configuration {
- unsigned short vendor; /* PCI vendor ID */
- unsigned short device; /* PCI vendor ID */
- unsigned short subvendor; /* PCI subsystem vendor ID */
- unsigned short subdevice; /* PCI subsystem device ID */
- unsigned short sir_io; /* I/O port for SIR */
- unsigned short fir_io; /* I/O port for FIR */
- unsigned char fir_irq; /* FIR IRQ */
- unsigned char fir_dma; /* FIR DMA */
- unsigned short cfg_base; /* I/O port for chip configuration */
- int (*preconfigure)(struct pci_dev *dev, struct smsc_ircc_subsystem_configuration *conf); /* Preconfig function */
- const char *name; /* name shown as info */
-};
-#endif
-
-struct smsc_transceiver {
- char *name;
- void (*set_for_speed)(int fir_base, u32 speed);
- int (*probe)(int fir_base);
-};
-
-struct smsc_chip {
- char *name;
- #if 0
- u8 type;
- #endif
- u16 flags;
- u8 devid;
- u8 rev;
-};
-
-struct smsc_chip_address {
- unsigned int cfg_base;
- unsigned int type;
-};
-
-/* Private data for each instance */
-struct smsc_ircc_cb {
- struct net_device *netdev; /* Yes! we are some kind of netdevice */
- struct irlap_cb *irlap; /* The link layer we are binded to */
-
- chipio_t io; /* IrDA controller information */
- iobuff_t tx_buff; /* Transmit buffer */
- iobuff_t rx_buff; /* Receive buffer */
- dma_addr_t tx_buff_dma;
- dma_addr_t rx_buff_dma;
-
- struct qos_info qos; /* QoS capabilities for this device */
-
- spinlock_t lock; /* For serializing operations */
-
- __u32 new_speed;
- __u32 flags; /* Interface flags */
-
- int tx_buff_offsets[10]; /* Offsets between frames in tx_buff */
- int tx_len; /* Number of frames in tx_buff */
-
- int transceiver;
- struct platform_device *pldev;
-};
-
-/* Constants */
-
-#define SMSC_IRCC2_DRIVER_NAME "smsc-ircc2"
-
-#define SMSC_IRCC2_C_IRDA_FALLBACK_SPEED 9600
-#define SMSC_IRCC2_C_DEFAULT_TRANSCEIVER 1
-#define SMSC_IRCC2_C_NET_TIMEOUT 0
-#define SMSC_IRCC2_C_SIR_STOP 0
-
-static const char *driver_name = SMSC_IRCC2_DRIVER_NAME;
-
-/* Prototypes */
-
-static int smsc_ircc_open(unsigned int firbase, unsigned int sirbase, u8 dma, u8 irq);
-static int smsc_ircc_present(unsigned int fir_base, unsigned int sir_base);
-static void smsc_ircc_setup_io(struct smsc_ircc_cb *self, unsigned int fir_base, unsigned int sir_base, u8 dma, u8 irq);
-static void smsc_ircc_setup_qos(struct smsc_ircc_cb *self);
-static void smsc_ircc_init_chip(struct smsc_ircc_cb *self);
-static int __exit smsc_ircc_close(struct smsc_ircc_cb *self);
-static int smsc_ircc_dma_receive(struct smsc_ircc_cb *self);
-static void smsc_ircc_dma_receive_complete(struct smsc_ircc_cb *self);
-static void smsc_ircc_sir_receive(struct smsc_ircc_cb *self);
-static netdev_tx_t smsc_ircc_hard_xmit_sir(struct sk_buff *skb,
- struct net_device *dev);
-static netdev_tx_t smsc_ircc_hard_xmit_fir(struct sk_buff *skb,
- struct net_device *dev);
-static void smsc_ircc_dma_xmit(struct smsc_ircc_cb *self, int bofs);
-static void smsc_ircc_dma_xmit_complete(struct smsc_ircc_cb *self);
-static void smsc_ircc_change_speed(struct smsc_ircc_cb *self, u32 speed);
-static void smsc_ircc_set_sir_speed(struct smsc_ircc_cb *self, u32 speed);
-static irqreturn_t smsc_ircc_interrupt(int irq, void *dev_id);
-static irqreturn_t smsc_ircc_interrupt_sir(struct net_device *dev);
-static void smsc_ircc_sir_start(struct smsc_ircc_cb *self);
-#if SMSC_IRCC2_C_SIR_STOP
-static void smsc_ircc_sir_stop(struct smsc_ircc_cb *self);
-#endif
-static void smsc_ircc_sir_write_wakeup(struct smsc_ircc_cb *self);
-static int smsc_ircc_sir_write(int iobase, int fifo_size, __u8 *buf, int len);
-static int smsc_ircc_net_open(struct net_device *dev);
-static int smsc_ircc_net_close(struct net_device *dev);
-static int smsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-#if SMSC_IRCC2_C_NET_TIMEOUT
-static void smsc_ircc_timeout(struct net_device *dev);
-#endif
-static int smsc_ircc_is_receiving(struct smsc_ircc_cb *self);
-static void smsc_ircc_probe_transceiver(struct smsc_ircc_cb *self);
-static void smsc_ircc_set_transceiver_for_speed(struct smsc_ircc_cb *self, u32 speed);
-static void smsc_ircc_sir_wait_hw_transmitter_finish(struct smsc_ircc_cb *self);
-
-/* Probing */
-static int __init smsc_ircc_look_for_chips(void);
-static const struct smsc_chip * __init smsc_ircc_probe(unsigned short cfg_base, u8 reg, const struct smsc_chip *chip, char *type);
-static int __init smsc_superio_flat(const struct smsc_chip *chips, unsigned short cfg_base, char *type);
-static int __init smsc_superio_paged(const struct smsc_chip *chips, unsigned short cfg_base, char *type);
-static int __init smsc_superio_fdc(unsigned short cfg_base);
-static int __init smsc_superio_lpc(unsigned short cfg_base);
-#ifdef CONFIG_PCI
-static int __init preconfigure_smsc_chip(struct smsc_ircc_subsystem_configuration *conf);
-static int __init preconfigure_through_82801(struct pci_dev *dev, struct smsc_ircc_subsystem_configuration *conf);
-static void __init preconfigure_ali_port(struct pci_dev *dev,
- unsigned short port);
-static int __init preconfigure_through_ali(struct pci_dev *dev, struct smsc_ircc_subsystem_configuration *conf);
-static int __init smsc_ircc_preconfigure_subsystems(unsigned short ircc_cfg,
- unsigned short ircc_fir,
- unsigned short ircc_sir,
- unsigned char ircc_dma,
- unsigned char ircc_irq);
-#endif
-
-/* Transceivers specific functions */
-
-static void smsc_ircc_set_transceiver_toshiba_sat1800(int fir_base, u32 speed);
-static int smsc_ircc_probe_transceiver_toshiba_sat1800(int fir_base);
-static void smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select(int fir_base, u32 speed);
-static int smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select(int fir_base);
-static void smsc_ircc_set_transceiver_smsc_ircc_atc(int fir_base, u32 speed);
-static int smsc_ircc_probe_transceiver_smsc_ircc_atc(int fir_base);
-
-/* Power Management */
-
-static int smsc_ircc_suspend(struct platform_device *dev, pm_message_t state);
-static int smsc_ircc_resume(struct platform_device *dev);
-
-static struct platform_driver smsc_ircc_driver = {
- .suspend = smsc_ircc_suspend,
- .resume = smsc_ircc_resume,
- .driver = {
- .name = SMSC_IRCC2_DRIVER_NAME,
- },
-};
-
-/* Transceivers for SMSC-ircc */
-
-static struct smsc_transceiver smsc_transceivers[] =
-{
- { "Toshiba Satellite 1800 (GP data pin select)", smsc_ircc_set_transceiver_toshiba_sat1800, smsc_ircc_probe_transceiver_toshiba_sat1800 },
- { "Fast pin select", smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select, smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select },
- { "ATC IRMode", smsc_ircc_set_transceiver_smsc_ircc_atc, smsc_ircc_probe_transceiver_smsc_ircc_atc },
- { NULL, NULL }
-};
-#define SMSC_IRCC2_C_NUMBER_OF_TRANSCEIVERS (ARRAY_SIZE(smsc_transceivers) - 1)
-
-/* SMC SuperIO chipsets definitions */
-
-#define KEY55_1 0 /* SuperIO Configuration mode with Key <0x55> */
-#define KEY55_2 1 /* SuperIO Configuration mode with Key <0x55,0x55> */
-#define NoIRDA 2 /* SuperIO Chip has no IRDA Port */
-#define SIR 0 /* SuperIO Chip has only slow IRDA */
-#define FIR 4 /* SuperIO Chip has fast IRDA */
-#define SERx4 8 /* SuperIO Chip supports 115,2 KBaud * 4=460,8 KBaud */
-
-static struct smsc_chip __initdata fdc_chips_flat[] =
-{
- /* Base address 0x3f0 or 0x370 */
- { "37C44", KEY55_1|NoIRDA, 0x00, 0x00 }, /* This chip cannot be detected */
- { "37C665GT", KEY55_2|NoIRDA, 0x65, 0x01 },
- { "37C665GT", KEY55_2|NoIRDA, 0x66, 0x01 },
- { "37C669", KEY55_2|SIR|SERx4, 0x03, 0x02 },
- { "37C669", KEY55_2|SIR|SERx4, 0x04, 0x02 }, /* ID? */
- { "37C78", KEY55_2|NoIRDA, 0x78, 0x00 },
- { "37N769", KEY55_1|FIR|SERx4, 0x28, 0x00 },
- { "37N869", KEY55_1|FIR|SERx4, 0x29, 0x00 },
- { NULL }
-};
-
-static struct smsc_chip __initdata fdc_chips_paged[] =
-{
- /* Base address 0x3f0 or 0x370 */
- { "37B72X", KEY55_1|SIR|SERx4, 0x4c, 0x00 },
- { "37B77X", KEY55_1|SIR|SERx4, 0x43, 0x00 },
- { "37B78X", KEY55_1|SIR|SERx4, 0x44, 0x00 },
- { "37B80X", KEY55_1|SIR|SERx4, 0x42, 0x00 },
- { "37C67X", KEY55_1|FIR|SERx4, 0x40, 0x00 },
- { "37C93X", KEY55_2|SIR|SERx4, 0x02, 0x01 },
- { "37C93XAPM", KEY55_1|SIR|SERx4, 0x30, 0x01 },
- { "37C93XFR", KEY55_2|FIR|SERx4, 0x03, 0x01 },
- { "37M707", KEY55_1|SIR|SERx4, 0x42, 0x00 },
- { "37M81X", KEY55_1|SIR|SERx4, 0x4d, 0x00 },
- { "37N958FR", KEY55_1|FIR|SERx4, 0x09, 0x04 },
- { "37N971", KEY55_1|FIR|SERx4, 0x0a, 0x00 },
- { "37N972", KEY55_1|FIR|SERx4, 0x0b, 0x00 },
- { NULL }
-};
-
-static struct smsc_chip __initdata lpc_chips_flat[] =
-{
- /* Base address 0x2E or 0x4E */
- { "47N227", KEY55_1|FIR|SERx4, 0x5a, 0x00 },
- { "47N227", KEY55_1|FIR|SERx4, 0x7a, 0x00 },
- { "47N267", KEY55_1|FIR|SERx4, 0x5e, 0x00 },
- { NULL }
-};
-
-static struct smsc_chip __initdata lpc_chips_paged[] =
-{
- /* Base address 0x2E or 0x4E */
- { "47B27X", KEY55_1|SIR|SERx4, 0x51, 0x00 },
- { "47B37X", KEY55_1|SIR|SERx4, 0x52, 0x00 },
- { "47M10X", KEY55_1|SIR|SERx4, 0x59, 0x00 },
- { "47M120", KEY55_1|NoIRDA|SERx4, 0x5c, 0x00 },
- { "47M13X", KEY55_1|SIR|SERx4, 0x59, 0x00 },
- { "47M14X", KEY55_1|SIR|SERx4, 0x5f, 0x00 },
- { "47N252", KEY55_1|FIR|SERx4, 0x0e, 0x00 },
- { "47S42X", KEY55_1|SIR|SERx4, 0x57, 0x00 },
- { NULL }
-};
-
-#define SMSCSIO_TYPE_FDC 1
-#define SMSCSIO_TYPE_LPC 2
-#define SMSCSIO_TYPE_FLAT 4
-#define SMSCSIO_TYPE_PAGED 8
-
-static struct smsc_chip_address __initdata possible_addresses[] =
-{
- { 0x3f0, SMSCSIO_TYPE_FDC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED },
- { 0x370, SMSCSIO_TYPE_FDC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED },
- { 0xe0, SMSCSIO_TYPE_FDC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED },
- { 0x2e, SMSCSIO_TYPE_LPC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED },
- { 0x4e, SMSCSIO_TYPE_LPC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED },
- { 0, 0 }
-};
-
-/* Globals */
-
-static struct smsc_ircc_cb *dev_self[] = { NULL, NULL };
-static unsigned short dev_count;
-
-static inline void register_bank(int iobase, int bank)
-{
- outb(((inb(iobase + IRCC_MASTER) & 0xf0) | (bank & 0x07)),
- iobase + IRCC_MASTER);
-}
-
-/* PNP hotplug support */
-static const struct pnp_device_id smsc_ircc_pnp_table[] = {
- { .id = "SMCf010", .driver_data = 0 },
- /* and presumably others */
- { }
-};
-MODULE_DEVICE_TABLE(pnp, smsc_ircc_pnp_table);
-
-static int pnp_driver_registered;
-
-#ifdef CONFIG_PNP
-static int smsc_ircc_pnp_probe(struct pnp_dev *dev,
- const struct pnp_device_id *dev_id)
-{
- unsigned int firbase, sirbase;
- u8 dma, irq;
-
- if (!(pnp_port_valid(dev, 0) && pnp_port_valid(dev, 1) &&
- pnp_dma_valid(dev, 0) && pnp_irq_valid(dev, 0)))
- return -EINVAL;
-
- sirbase = pnp_port_start(dev, 0);
- firbase = pnp_port_start(dev, 1);
- dma = pnp_dma(dev, 0);
- irq = pnp_irq(dev, 0);
-
- if (smsc_ircc_open(firbase, sirbase, dma, irq))
- return -ENODEV;
-
- return 0;
-}
-
-static struct pnp_driver smsc_ircc_pnp_driver = {
- .name = "smsc-ircc2",
- .id_table = smsc_ircc_pnp_table,
- .probe = smsc_ircc_pnp_probe,
-};
-#else /* CONFIG_PNP */
-static struct pnp_driver smsc_ircc_pnp_driver;
-#endif
-
-/*******************************************************************************
- *
- *
- * SMSC-ircc stuff
- *
- *
- *******************************************************************************/
-
-static int __init smsc_ircc_legacy_probe(void)
-{
- int ret = 0;
-
-#ifdef CONFIG_PCI
- if (smsc_ircc_preconfigure_subsystems(ircc_cfg, ircc_fir, ircc_sir, ircc_dma, ircc_irq) < 0) {
- /* Ignore errors from preconfiguration */
- net_err_ratelimited("%s, Preconfiguration failed !\n",
- driver_name);
- }
-#endif
-
- if (ircc_fir > 0 && ircc_sir > 0) {
- net_info_ratelimited(" Overriding FIR address 0x%04x\n",
- ircc_fir);
- net_info_ratelimited(" Overriding SIR address 0x%04x\n",
- ircc_sir);
-
- if (smsc_ircc_open(ircc_fir, ircc_sir, ircc_dma, ircc_irq))
- ret = -ENODEV;
- } else {
- ret = -ENODEV;
-
- /* try user provided configuration register base address */
- if (ircc_cfg > 0) {
- net_info_ratelimited(" Overriding configuration address 0x%04x\n",
- ircc_cfg);
- if (!smsc_superio_fdc(ircc_cfg))
- ret = 0;
- if (!smsc_superio_lpc(ircc_cfg))
- ret = 0;
- }
-
- if (smsc_ircc_look_for_chips() > 0)
- ret = 0;
- }
- return ret;
-}
-
-/*
- * Function smsc_ircc_init ()
- *
- * Initialize chip. Just try to find out how many chips we are dealing with
- * and where they are
- */
-static int __init smsc_ircc_init(void)
-{
- int ret;
-
- pr_debug("%s\n", __func__);
-
- ret = platform_driver_register(&smsc_ircc_driver);
- if (ret) {
- net_err_ratelimited("%s, Can't register driver!\n",
- driver_name);
- return ret;
- }
-
- dev_count = 0;
-
- if (smsc_nopnp || !pnp_platform_devices ||
- ircc_cfg || ircc_fir || ircc_sir ||
- ircc_dma != DMA_INVAL || ircc_irq != IRQ_INVAL) {
- ret = smsc_ircc_legacy_probe();
- } else {
- if (pnp_register_driver(&smsc_ircc_pnp_driver) == 0)
- pnp_driver_registered = 1;
- }
-
- if (ret) {
- if (pnp_driver_registered)
- pnp_unregister_driver(&smsc_ircc_pnp_driver);
- platform_driver_unregister(&smsc_ircc_driver);
- }
-
- return ret;
-}
-
-static netdev_tx_t smsc_ircc_net_xmit(struct sk_buff *skb,
- struct net_device *dev)
-{
- struct smsc_ircc_cb *self = netdev_priv(dev);
-
- if (self->io.speed > 115200)
- return smsc_ircc_hard_xmit_fir(skb, dev);
- else
- return smsc_ircc_hard_xmit_sir(skb, dev);
-}
-
-static const struct net_device_ops smsc_ircc_netdev_ops = {
- .ndo_open = smsc_ircc_net_open,
- .ndo_stop = smsc_ircc_net_close,
- .ndo_do_ioctl = smsc_ircc_net_ioctl,
- .ndo_start_xmit = smsc_ircc_net_xmit,
-#if SMSC_IRCC2_C_NET_TIMEOUT
- .ndo_tx_timeout = smsc_ircc_timeout,
-#endif
-};
-
-/*
- * Function smsc_ircc_open (firbase, sirbase, dma, irq)
- *
- * Try to open driver instance
- *
- */
-static int smsc_ircc_open(unsigned int fir_base, unsigned int sir_base, u8 dma, u8 irq)
-{
- struct smsc_ircc_cb *self;
- struct net_device *dev;
- int err;
-
- pr_debug("%s\n", __func__);
-
- err = smsc_ircc_present(fir_base, sir_base);
- if (err)
- goto err_out;
-
- err = -ENOMEM;
- if (dev_count >= ARRAY_SIZE(dev_self)) {
- net_warn_ratelimited("%s(), too many devices!\n", __func__);
- goto err_out1;
- }
-
- /*
- * Allocate new instance of the driver
- */
- dev = alloc_irdadev(sizeof(struct smsc_ircc_cb));
- if (!dev) {
- net_warn_ratelimited("%s() can't allocate net device\n",
- __func__);
- goto err_out1;
- }
-
-#if SMSC_IRCC2_C_NET_TIMEOUT
- dev->watchdog_timeo = HZ * 2; /* Allow enough time for speed change */
-#endif
- dev->netdev_ops = &smsc_ircc_netdev_ops;
-
- self = netdev_priv(dev);
- self->netdev = dev;
-
- /* Make ifconfig display some details */
- dev->base_addr = self->io.fir_base = fir_base;
- dev->irq = self->io.irq = irq;
-
- /* Need to store self somewhere */
- dev_self[dev_count] = self;
- spin_lock_init(&self->lock);
-
- self->rx_buff.truesize = SMSC_IRCC2_RX_BUFF_TRUESIZE;
- self->tx_buff.truesize = SMSC_IRCC2_TX_BUFF_TRUESIZE;
-
- self->rx_buff.head =
- dma_zalloc_coherent(NULL, self->rx_buff.truesize,
- &self->rx_buff_dma, GFP_KERNEL);
- if (self->rx_buff.head == NULL)
- goto err_out2;
-
- self->tx_buff.head =
- dma_zalloc_coherent(NULL, self->tx_buff.truesize,
- &self->tx_buff_dma, GFP_KERNEL);
- if (self->tx_buff.head == NULL)
- goto err_out3;
-
- self->rx_buff.in_frame = FALSE;
- self->rx_buff.state = OUTSIDE_FRAME;
- self->tx_buff.data = self->tx_buff.head;
- self->rx_buff.data = self->rx_buff.head;
-
- smsc_ircc_setup_io(self, fir_base, sir_base, dma, irq);
- smsc_ircc_setup_qos(self);
- smsc_ircc_init_chip(self);
-
- if (ircc_transceiver > 0 &&
- ircc_transceiver < SMSC_IRCC2_C_NUMBER_OF_TRANSCEIVERS)
- self->transceiver = ircc_transceiver;
- else
- smsc_ircc_probe_transceiver(self);
-
- err = register_netdev(self->netdev);
- if (err) {
- net_err_ratelimited("%s, Network device registration failed!\n",
- driver_name);
- goto err_out4;
- }
-
- self->pldev = platform_device_register_simple(SMSC_IRCC2_DRIVER_NAME,
- dev_count, NULL, 0);
- if (IS_ERR(self->pldev)) {
- err = PTR_ERR(self->pldev);
- goto err_out5;
- }
- platform_set_drvdata(self->pldev, self);
-
- net_info_ratelimited("IrDA: Registered device %s\n", dev->name);
- dev_count++;
-
- return 0;
-
- err_out5:
- unregister_netdev(self->netdev);
-
- err_out4:
- dma_free_coherent(NULL, self->tx_buff.truesize,
- self->tx_buff.head, self->tx_buff_dma);
- err_out3:
- dma_free_coherent(NULL, self->rx_buff.truesize,
- self->rx_buff.head, self->rx_buff_dma);
- err_out2:
- free_netdev(self->netdev);
- dev_self[dev_count] = NULL;
- err_out1:
- release_region(fir_base, SMSC_IRCC2_FIR_CHIP_IO_EXTENT);
- release_region(sir_base, SMSC_IRCC2_SIR_CHIP_IO_EXTENT);
- err_out:
- return err;
-}
-
-/*
- * Function smsc_ircc_present(fir_base, sir_base)
- *
- * Check the smsc-ircc chip presence
- *
- */
-static int smsc_ircc_present(unsigned int fir_base, unsigned int sir_base)
-{
- unsigned char low, high, chip, config, dma, irq, version;
-
- if (!request_region(fir_base, SMSC_IRCC2_FIR_CHIP_IO_EXTENT,
- driver_name)) {
- net_warn_ratelimited("%s: can't get fir_base of 0x%03x\n",
- __func__, fir_base);
- goto out1;
- }
-
- if (!request_region(sir_base, SMSC_IRCC2_SIR_CHIP_IO_EXTENT,
- driver_name)) {
- net_warn_ratelimited("%s: can't get sir_base of 0x%03x\n",
- __func__, sir_base);
- goto out2;
- }
-
- register_bank(fir_base, 3);
-
- high = inb(fir_base + IRCC_ID_HIGH);
- low = inb(fir_base + IRCC_ID_LOW);
- chip = inb(fir_base + IRCC_CHIP_ID);
- version = inb(fir_base + IRCC_VERSION);
- config = inb(fir_base + IRCC_INTERFACE);
- dma = config & IRCC_INTERFACE_DMA_MASK;
- irq = (config & IRCC_INTERFACE_IRQ_MASK) >> 4;
-
- if (high != 0x10 || low != 0xb8 || (chip != 0xf1 && chip != 0xf2)) {
- net_warn_ratelimited("%s(), addr 0x%04x - no device found!\n",
- __func__, fir_base);
- goto out3;
- }
- net_info_ratelimited("SMsC IrDA Controller found\n IrCC version %d.%d, firport 0x%03x, sirport 0x%03x dma=%d, irq=%d\n",
- chip & 0x0f, version,
- fir_base, sir_base, dma, irq);
-
- return 0;
-
- out3:
- release_region(sir_base, SMSC_IRCC2_SIR_CHIP_IO_EXTENT);
- out2:
- release_region(fir_base, SMSC_IRCC2_FIR_CHIP_IO_EXTENT);
- out1:
- return -ENODEV;
-}
-
-/*
- * Function smsc_ircc_setup_io(self, fir_base, sir_base, dma, irq)
- *
- * Setup I/O
- *
- */
-static void smsc_ircc_setup_io(struct smsc_ircc_cb *self,
- unsigned int fir_base, unsigned int sir_base,
- u8 dma, u8 irq)
-{
- unsigned char config, chip_dma, chip_irq;
-
- register_bank(fir_base, 3);
- config = inb(fir_base + IRCC_INTERFACE);
- chip_dma = config & IRCC_INTERFACE_DMA_MASK;
- chip_irq = (config & IRCC_INTERFACE_IRQ_MASK) >> 4;
-
- self->io.fir_base = fir_base;
- self->io.sir_base = sir_base;
- self->io.fir_ext = SMSC_IRCC2_FIR_CHIP_IO_EXTENT;
- self->io.sir_ext = SMSC_IRCC2_SIR_CHIP_IO_EXTENT;
- self->io.fifo_size = SMSC_IRCC2_FIFO_SIZE;
- self->io.speed = SMSC_IRCC2_C_IRDA_FALLBACK_SPEED;
-
- if (irq != IRQ_INVAL) {
- if (irq != chip_irq)
- net_info_ratelimited("%s, Overriding IRQ - chip says %d, using %d\n",
- driver_name, chip_irq, irq);
- self->io.irq = irq;
- } else
- self->io.irq = chip_irq;
-
- if (dma != DMA_INVAL) {
- if (dma != chip_dma)
- net_info_ratelimited("%s, Overriding DMA - chip says %d, using %d\n",
- driver_name, chip_dma, dma);
- self->io.dma = dma;
- } else
- self->io.dma = chip_dma;
-
-}
-
-/*
- * Function smsc_ircc_setup_qos(self)
- *
- * Setup qos
- *
- */
-static void smsc_ircc_setup_qos(struct smsc_ircc_cb *self)
-{
- /* Initialize QoS for this device */
- irda_init_max_qos_capabilies(&self->qos);
-
- self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|
- IR_115200|IR_576000|IR_1152000|(IR_4000000 << 8);
-
- self->qos.min_turn_time.bits = SMSC_IRCC2_MIN_TURN_TIME;
- self->qos.window_size.bits = SMSC_IRCC2_WINDOW_SIZE;
- irda_qos_bits_to_value(&self->qos);
-}
-
-/*
- * Function smsc_ircc_init_chip(self)
- *
- * Init chip
- *
- */
-static void smsc_ircc_init_chip(struct smsc_ircc_cb *self)
-{
- int iobase = self->io.fir_base;
-
- register_bank(iobase, 0);
- outb(IRCC_MASTER_RESET, iobase + IRCC_MASTER);
- outb(0x00, iobase + IRCC_MASTER);
-
- register_bank(iobase, 1);
- outb(((inb(iobase + IRCC_SCE_CFGA) & 0x87) | IRCC_CFGA_IRDA_SIR_A),
- iobase + IRCC_SCE_CFGA);
-
-#ifdef smsc_669 /* Uses pin 88/89 for Rx/Tx */
- outb(((inb(iobase + IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_COM),
- iobase + IRCC_SCE_CFGB);
-#else
- outb(((inb(iobase + IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_IR),
- iobase + IRCC_SCE_CFGB);
-#endif
- (void) inb(iobase + IRCC_FIFO_THRESHOLD);
- outb(SMSC_IRCC2_FIFO_THRESHOLD, iobase + IRCC_FIFO_THRESHOLD);
-
- register_bank(iobase, 4);
- outb((inb(iobase + IRCC_CONTROL) & 0x30), iobase + IRCC_CONTROL);
-
- register_bank(iobase, 0);
- outb(0, iobase + IRCC_LCR_A);
-
- smsc_ircc_set_sir_speed(self, SMSC_IRCC2_C_IRDA_FALLBACK_SPEED);
-
- /* Power on device */
- outb(0x00, iobase + IRCC_MASTER);
-}
-
-/*
- * Function smsc_ircc_net_ioctl (dev, rq, cmd)
- *
- * Process IOCTL commands for this device
- *
- */
-static int smsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
- struct if_irda_req *irq = (struct if_irda_req *) rq;
- struct smsc_ircc_cb *self;
- unsigned long flags;
- int ret = 0;
-
- IRDA_ASSERT(dev != NULL, return -1;);
-
- self = netdev_priv(dev);
-
- IRDA_ASSERT(self != NULL, return -1;);
-
- pr_debug("%s(), %s, (cmd=0x%X)\n", __func__, dev->name, cmd);
-
- switch (cmd) {
- case SIOCSBANDWIDTH: /* Set bandwidth */
- if (!capable(CAP_NET_ADMIN))
- ret = -EPERM;
- else {
- /* Make sure we are the only one touching
- * self->io.speed and the hardware - Jean II */
- spin_lock_irqsave(&self->lock, flags);
- smsc_ircc_change_speed(self, irq->ifr_baudrate);
- spin_unlock_irqrestore(&self->lock, flags);
- }
- break;
- case SIOCSMEDIABUSY: /* Set media busy */
- if (!capable(CAP_NET_ADMIN)) {
- ret = -EPERM;
- break;
- }
-
- irda_device_set_media_busy(self->netdev, TRUE);
- break;
- case SIOCGRECEIVING: /* Check if we are receiving right now */
- irq->ifr_receiving = smsc_ircc_is_receiving(self);
- break;
- #if 0
- case SIOCSDTRRTS:
- if (!capable(CAP_NET_ADMIN)) {
- ret = -EPERM;
- break;
- }
- smsc_ircc_sir_set_dtr_rts(dev, irq->ifr_dtr, irq->ifr_rts);
- break;
- #endif
- default:
- ret = -EOPNOTSUPP;
- }
-
- return ret;
-}
-
-#if SMSC_IRCC2_C_NET_TIMEOUT
-/*
- * Function smsc_ircc_timeout (struct net_device *dev)
- *
- * The networking timeout management.
- *
- */
-
-static void smsc_ircc_timeout(struct net_device *dev)
-{
- struct smsc_ircc_cb *self = netdev_priv(dev);
- unsigned long flags;
-
- net_warn_ratelimited("%s: transmit timed out, changing speed to: %d\n",
- dev->name, self->io.speed);
- spin_lock_irqsave(&self->lock, flags);
- smsc_ircc_sir_start(self);
- smsc_ircc_change_speed(self, self->io.speed);
- netif_trans_update(dev); /* prevent tx timeout */
- netif_wake_queue(dev);
- spin_unlock_irqrestore(&self->lock, flags);
-}
-#endif
-
-/*
- * Function smsc_ircc_hard_xmit_sir (struct sk_buff *skb, struct net_device *dev)
- *
- * Transmits the current frame until FIFO is full, then
- * waits until the next transmit interrupt, and continues until the
- * frame is transmitted.
- */
-static netdev_tx_t smsc_ircc_hard_xmit_sir(struct sk_buff *skb,
- struct net_device *dev)
-{
- struct smsc_ircc_cb *self;
- unsigned long flags;
- s32 speed;
-
- pr_debug("%s\n", __func__);
-
- IRDA_ASSERT(dev != NULL, return NETDEV_TX_OK;);
-
- self = netdev_priv(dev);
- IRDA_ASSERT(self != NULL, return NETDEV_TX_OK;);
-
- netif_stop_queue(dev);
-
- /* Make sure test of self->io.speed & speed change are atomic */
- spin_lock_irqsave(&self->lock, flags);
-
- /* Check if we need to change the speed */
- speed = irda_get_next_speed(skb);
- if (speed != self->io.speed && speed != -1) {
- /* Check for empty frame */
- if (!skb->len) {
- /*
- * We send frames one by one in SIR mode (no
- * pipelining), so at this point, if we were sending
- * a previous frame, we just received the interrupt
- * telling us it is finished (UART_IIR_THRI).
- * Therefore, waiting for the transmitter to really
- * finish draining the fifo won't take too long.
- * And the interrupt handler is not expected to run.
- * - Jean II */
- smsc_ircc_sir_wait_hw_transmitter_finish(self);
- smsc_ircc_change_speed(self, speed);
- spin_unlock_irqrestore(&self->lock, flags);
- dev_kfree_skb(skb);
- return NETDEV_TX_OK;
- }
- self->new_speed = speed;
- }
-
- /* Init tx buffer */
- self->tx_buff.data = self->tx_buff.head;
-
- /* Copy skb to tx_buff while wrapping, stuffing and making CRC */
- self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data,
- self->tx_buff.truesize);
-
- dev->stats.tx_bytes += self->tx_buff.len;
-
- /* Turn on transmit finished interrupt. Will fire immediately! */
- outb(UART_IER_THRI, self->io.sir_base + UART_IER);
-
- spin_unlock_irqrestore(&self->lock, flags);
-
- dev_kfree_skb(skb);
-
- return NETDEV_TX_OK;
-}
-
-/*
- * Function smsc_ircc_set_fir_speed (self, baud)
- *
- * Change the speed of the device
- *
- */
-static void smsc_ircc_set_fir_speed(struct smsc_ircc_cb *self, u32 speed)
-{
- int fir_base, ir_mode, ctrl, fast;
-
- IRDA_ASSERT(self != NULL, return;);
- fir_base = self->io.fir_base;
-
- self->io.speed = speed;
-
- switch (speed) {
- default:
- case 576000:
- ir_mode = IRCC_CFGA_IRDA_HDLC;
- ctrl = IRCC_CRC;
- fast = 0;
- pr_debug("%s(), handling baud of 576000\n", __func__);
- break;
- case 1152000:
- ir_mode = IRCC_CFGA_IRDA_HDLC;
- ctrl = IRCC_1152 | IRCC_CRC;
- fast = IRCC_LCR_A_FAST | IRCC_LCR_A_GP_DATA;
- pr_debug("%s(), handling baud of 1152000\n",
- __func__);
- break;
- case 4000000:
- ir_mode = IRCC_CFGA_IRDA_4PPM;
- ctrl = IRCC_CRC;
- fast = IRCC_LCR_A_FAST;
- pr_debug("%s(), handling baud of 4000000\n",
- __func__);
- break;
- }
- #if 0
- Now in tranceiver!
- /* This causes an interrupt */
- register_bank(fir_base, 0);
- outb((inb(fir_base + IRCC_LCR_A) & 0xbf) | fast, fir_base + IRCC_LCR_A);
- #endif
-
- register_bank(fir_base, 1);
- outb(((inb(fir_base + IRCC_SCE_CFGA) & IRCC_SCE_CFGA_BLOCK_CTRL_BITS_MASK) | ir_mode), fir_base + IRCC_SCE_CFGA);
-
- register_bank(fir_base, 4);
- outb((inb(fir_base + IRCC_CONTROL) & 0x30) | ctrl, fir_base + IRCC_CONTROL);
-}
-
-/*
- * Function smsc_ircc_fir_start(self)
- *
- * Change the speed of the device
- *
- */
-static void smsc_ircc_fir_start(struct smsc_ircc_cb *self)
-{
- struct net_device *dev;
- int fir_base;
-
- pr_debug("%s\n", __func__);
-
- IRDA_ASSERT(self != NULL, return;);
- dev = self->netdev;
- IRDA_ASSERT(dev != NULL, return;);
-
- fir_base = self->io.fir_base;
-
- /* Reset everything */
-
- /* Clear FIFO */
- outb(inb(fir_base + IRCC_LCR_A) | IRCC_LCR_A_FIFO_RESET, fir_base + IRCC_LCR_A);
-
- /* Enable interrupt */
- /*outb(IRCC_IER_ACTIVE_FRAME|IRCC_IER_EOM, fir_base + IRCC_IER);*/
-
- register_bank(fir_base, 1);
-
- /* Select the TX/RX interface */
-#ifdef SMSC_669 /* Uses pin 88/89 for Rx/Tx */
- outb(((inb(fir_base + IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_COM),
- fir_base + IRCC_SCE_CFGB);
-#else
- outb(((inb(fir_base + IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_IR),
- fir_base + IRCC_SCE_CFGB);
-#endif
- (void) inb(fir_base + IRCC_FIFO_THRESHOLD);
-
- /* Enable SCE interrupts */
- outb(0, fir_base + IRCC_MASTER);
- register_bank(fir_base, 0);
- outb(IRCC_IER_ACTIVE_FRAME | IRCC_IER_EOM, fir_base + IRCC_IER);
- outb(IRCC_MASTER_INT_EN, fir_base + IRCC_MASTER);
-}
-
-/*
- * Function smsc_ircc_fir_stop(self, baud)
- *
- * Change the speed of the device
- *
- */
-static void smsc_ircc_fir_stop(struct smsc_ircc_cb *self)
-{
- int fir_base;
-
- pr_debug("%s\n", __func__);
-
- IRDA_ASSERT(self != NULL, return;);
-
- fir_base = self->io.fir_base;
- register_bank(fir_base, 0);
- /*outb(IRCC_MASTER_RESET, fir_base + IRCC_MASTER);*/
- outb(inb(fir_base + IRCC_LCR_B) & IRCC_LCR_B_SIP_ENABLE, fir_base + IRCC_LCR_B);
-}
-
-
-/*
- * Function smsc_ircc_change_speed(self, baud)
- *
- * Change the speed of the device
- *
- * This function *must* be called with spinlock held, because it may
- * be called from the irq handler. - Jean II
- */
-static void smsc_ircc_change_speed(struct smsc_ircc_cb *self, u32 speed)
-{
- struct net_device *dev;
- int last_speed_was_sir;
-
- pr_debug("%s() changing speed to: %d\n", __func__, speed);
-
- IRDA_ASSERT(self != NULL, return;);
- dev = self->netdev;
-
- last_speed_was_sir = self->io.speed <= SMSC_IRCC2_MAX_SIR_SPEED;
-
- #if 0
- /* Temp Hack */
- speed= 1152000;
- self->io.speed = speed;
- last_speed_was_sir = 0;
- smsc_ircc_fir_start(self);
- #endif
-
- if (self->io.speed == 0)
- smsc_ircc_sir_start(self);
-
- #if 0
- if (!last_speed_was_sir) speed = self->io.speed;
- #endif
-
- if (self->io.speed != speed)
- smsc_ircc_set_transceiver_for_speed(self, speed);
-
- self->io.speed = speed;
-
- if (speed <= SMSC_IRCC2_MAX_SIR_SPEED) {
- if (!last_speed_was_sir) {
- smsc_ircc_fir_stop(self);
- smsc_ircc_sir_start(self);
- }
- smsc_ircc_set_sir_speed(self, speed);
- } else {
- if (last_speed_was_sir) {
- #if SMSC_IRCC2_C_SIR_STOP
- smsc_ircc_sir_stop(self);
- #endif
- smsc_ircc_fir_start(self);
- }
- smsc_ircc_set_fir_speed(self, speed);
-
- #if 0
- self->tx_buff.len = 10;
- self->tx_buff.data = self->tx_buff.head;
-
- smsc_ircc_dma_xmit(self, 4000);
- #endif
- /* Be ready for incoming frames */
- smsc_ircc_dma_receive(self);
- }
-
- netif_wake_queue(dev);
-}
-
-/*
- * Function smsc_ircc_set_sir_speed (self, speed)
- *
- * Set speed of IrDA port to specified baudrate
- *
- */
-static void smsc_ircc_set_sir_speed(struct smsc_ircc_cb *self, __u32 speed)
-{
- int iobase;
- int fcr; /* FIFO control reg */
- int lcr; /* Line control reg */
- int divisor;
-
- pr_debug("%s(), Setting speed to: %d\n", __func__, speed);
-
- IRDA_ASSERT(self != NULL, return;);
- iobase = self->io.sir_base;
-
- /* Update accounting for new speed */
- self->io.speed = speed;
-
- /* Turn off interrupts */
- outb(0, iobase + UART_IER);
-
- divisor = SMSC_IRCC2_MAX_SIR_SPEED / speed;
-
- fcr = UART_FCR_ENABLE_FIFO;
-
- /*
- * Use trigger level 1 to avoid 3 ms. timeout delay at 9600 bps, and
- * almost 1,7 ms at 19200 bps. At speeds above that we can just forget
- * about this timeout since it will always be fast enough.
- */
- fcr |= self->io.speed < 38400 ?
- UART_FCR_TRIGGER_1 : UART_FCR_TRIGGER_14;
-
- /* IrDA ports use 8N1 */
- lcr = UART_LCR_WLEN8;
-
- outb(UART_LCR_DLAB | lcr, iobase + UART_LCR); /* Set DLAB */
- outb(divisor & 0xff, iobase + UART_DLL); /* Set speed */
- outb(divisor >> 8, iobase + UART_DLM);
- outb(lcr, iobase + UART_LCR); /* Set 8N1 */
- outb(fcr, iobase + UART_FCR); /* Enable FIFO's */
-
- /* Turn on interrups */
- outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);
-
- pr_debug("%s() speed changed to: %d\n", __func__, speed);
-}
-
-
-/*
- * Function smsc_ircc_hard_xmit_fir (skb, dev)
- *
- * Transmit the frame!
- *
- */
-static netdev_tx_t smsc_ircc_hard_xmit_fir(struct sk_buff *skb,
- struct net_device *dev)
-{
- struct smsc_ircc_cb *self;
- unsigned long flags;
- s32 speed;
- int mtt;
-
- IRDA_ASSERT(dev != NULL, return NETDEV_TX_OK;);
- self = netdev_priv(dev);
- IRDA_ASSERT(self != NULL, return NETDEV_TX_OK;);
-
- netif_stop_queue(dev);
-
- /* Make sure test of self->io.speed & speed change are atomic */
- spin_lock_irqsave(&self->lock, flags);
-
- /* Check if we need to change the speed after this frame */
- speed = irda_get_next_speed(skb);
- if (speed != self->io.speed && speed != -1) {
- /* Check for empty frame */
- if (!skb->len) {
- /* Note : you should make sure that speed changes
- * are not going to corrupt any outgoing frame.
- * Look at nsc-ircc for the gory details - Jean II */
- smsc_ircc_change_speed(self, speed);
- spin_unlock_irqrestore(&self->lock, flags);
- dev_kfree_skb(skb);
- return NETDEV_TX_OK;
- }
-
- self->new_speed = speed;
- }
-
- skb_copy_from_linear_data(skb, self->tx_buff.head, skb->len);
-
- self->tx_buff.len = skb->len;
- self->tx_buff.data = self->tx_buff.head;
-
- mtt = irda_get_mtt(skb);
- if (mtt) {
- int bofs;
-
- /*
- * Compute how many BOFs (STA or PA's) we need to waste the
- * min turn time given the speed of the link.
- */
- bofs = mtt * (self->io.speed / 1000) / 8000;
- if (bofs > 4095)
- bofs = 4095;
-
- smsc_ircc_dma_xmit(self, bofs);
- } else {
- /* Transmit frame */
- smsc_ircc_dma_xmit(self, 0);
- }
-
- spin_unlock_irqrestore(&self->lock, flags);
- dev_kfree_skb(skb);
-
- return NETDEV_TX_OK;
-}
-
-/*
- * Function smsc_ircc_dma_xmit (self, bofs)
- *
- * Transmit data using DMA
- *
- */
-static void smsc_ircc_dma_xmit(struct smsc_ircc_cb *self, int bofs)
-{
- int iobase = self->io.fir_base;
- u8 ctrl;
-
- pr_debug("%s\n", __func__);
-#if 1
- /* Disable Rx */
- register_bank(iobase, 0);
- outb(0x00, iobase + IRCC_LCR_B);
-#endif
- register_bank(iobase, 1);
- outb(inb(iobase + IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE,
- iobase + IRCC_SCE_CFGB);
-
- self->io.direction = IO_XMIT;
-
- /* Set BOF additional count for generating the min turn time */
- register_bank(iobase, 4);
- outb(bofs & 0xff, iobase + IRCC_BOF_COUNT_LO);
- ctrl = inb(iobase + IRCC_CONTROL) & 0xf0;
- outb(ctrl | ((bofs >> 8) & 0x0f), iobase + IRCC_BOF_COUNT_HI);
-
- /* Set max Tx frame size */
- outb(self->tx_buff.len >> 8, iobase + IRCC_TX_SIZE_HI);
- outb(self->tx_buff.len & 0xff, iobase + IRCC_TX_SIZE_LO);
-
- /*outb(UART_MCR_OUT2, self->io.sir_base + UART_MCR);*/
-
- /* Enable burst mode chip Tx DMA */
- register_bank(iobase, 1);
- outb(inb(iobase + IRCC_SCE_CFGB) | IRCC_CFGB_DMA_ENABLE |
- IRCC_CFGB_DMA_BURST, iobase + IRCC_SCE_CFGB);
-
- /* Setup DMA controller (must be done after enabling chip DMA) */
- irda_setup_dma(self->io.dma, self->tx_buff_dma, self->tx_buff.len,
- DMA_TX_MODE);
-
- /* Enable interrupt */
-
- register_bank(iobase, 0);
- outb(IRCC_IER_ACTIVE_FRAME | IRCC_IER_EOM, iobase + IRCC_IER);
- outb(IRCC_MASTER_INT_EN, iobase + IRCC_MASTER);
-
- /* Enable transmit */
- outb(IRCC_LCR_B_SCE_TRANSMIT | IRCC_LCR_B_SIP_ENABLE, iobase + IRCC_LCR_B);
-}
-
-/*
- * Function smsc_ircc_dma_xmit_complete (self)
- *
- * The transfer of a frame in finished. This function will only be called
- * by the interrupt handler
- *
- */
-static void smsc_ircc_dma_xmit_complete(struct smsc_ircc_cb *self)
-{
- int iobase = self->io.fir_base;
-
- pr_debug("%s\n", __func__);
-#if 0
- /* Disable Tx */
- register_bank(iobase, 0);
- outb(0x00, iobase + IRCC_LCR_B);
-#endif
- register_bank(iobase, 1);
- outb(inb(iobase + IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE,
- iobase + IRCC_SCE_CFGB);
-
- /* Check for underrun! */
- register_bank(iobase, 0);
- if (inb(iobase + IRCC_LSR) & IRCC_LSR_UNDERRUN) {
- self->netdev->stats.tx_errors++;
- self->netdev->stats.tx_fifo_errors++;
-
- /* Reset error condition */
- register_bank(iobase, 0);
- outb(IRCC_MASTER_ERROR_RESET, iobase + IRCC_MASTER);
- outb(0x00, iobase + IRCC_MASTER);
- } else {
- self->netdev->stats.tx_packets++;
- self->netdev->stats.tx_bytes += self->tx_buff.len;
- }
-
- /* Check if it's time to change the speed */
- if (self->new_speed) {
- smsc_ircc_change_speed(self, self->new_speed);
- self->new_speed = 0;
- }
-
- netif_wake_queue(self->netdev);
-}
-
-/*
- * Function smsc_ircc_dma_receive(self)
- *
- * Get ready for receiving a frame. The device will initiate a DMA
- * if it starts to receive a frame.
- *
- */
-static int smsc_ircc_dma_receive(struct smsc_ircc_cb *self)
-{
- int iobase = self->io.fir_base;
-#if 0
- /* Turn off chip DMA */
- register_bank(iobase, 1);
- outb(inb(iobase + IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE,
- iobase + IRCC_SCE_CFGB);
-#endif
-
- /* Disable Tx */
- register_bank(iobase, 0);
- outb(0x00, iobase + IRCC_LCR_B);
-
- /* Turn off chip DMA */
- register_bank(iobase, 1);
- outb(inb(iobase + IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE,
- iobase + IRCC_SCE_CFGB);
-
- self->io.direction = IO_RECV;
- self->rx_buff.data = self->rx_buff.head;
-
- /* Set max Rx frame size */
- register_bank(iobase, 4);
- outb((2050 >> 8) & 0x0f, iobase + IRCC_RX_SIZE_HI);
- outb(2050 & 0xff, iobase + IRCC_RX_SIZE_LO);
-
- /* Setup DMA controller */
- irda_setup_dma(self->io.dma, self->rx_buff_dma, self->rx_buff.truesize,
- DMA_RX_MODE);
-
- /* Enable burst mode chip Rx DMA */
- register_bank(iobase, 1);
- outb(inb(iobase + IRCC_SCE_CFGB) | IRCC_CFGB_DMA_ENABLE |
- IRCC_CFGB_DMA_BURST, iobase + IRCC_SCE_CFGB);
-
- /* Enable interrupt */
- register_bank(iobase, 0);
- outb(IRCC_IER_ACTIVE_FRAME | IRCC_IER_EOM, iobase + IRCC_IER);
- outb(IRCC_MASTER_INT_EN, iobase + IRCC_MASTER);
-
- /* Enable receiver */
- register_bank(iobase, 0);
- outb(IRCC_LCR_B_SCE_RECEIVE | IRCC_LCR_B_SIP_ENABLE,
- iobase + IRCC_LCR_B);
-
- return 0;
-}
-
-/*
- * Function smsc_ircc_dma_receive_complete(self)
- *
- * Finished with receiving frames
- *
- */
-static void smsc_ircc_dma_receive_complete(struct smsc_ircc_cb *self)
-{
- struct sk_buff *skb;
- int len, msgcnt, lsr;
- int iobase = self->io.fir_base;
-
- register_bank(iobase, 0);
-
- pr_debug("%s\n", __func__);
-#if 0
- /* Disable Rx */
- register_bank(iobase, 0);
- outb(0x00, iobase + IRCC_LCR_B);
-#endif
- register_bank(iobase, 0);
- outb(inb(iobase + IRCC_LSAR) & ~IRCC_LSAR_ADDRESS_MASK, iobase + IRCC_LSAR);
- lsr= inb(iobase + IRCC_LSR);
- msgcnt = inb(iobase + IRCC_LCR_B) & 0x08;
-
- pr_debug("%s: dma count = %d\n", __func__,
- get_dma_residue(self->io.dma));
-
- len = self->rx_buff.truesize - get_dma_residue(self->io.dma);
-
- /* Look for errors */
- if (lsr & (IRCC_LSR_FRAME_ERROR | IRCC_LSR_CRC_ERROR | IRCC_LSR_SIZE_ERROR)) {
- self->netdev->stats.rx_errors++;
- if (lsr & IRCC_LSR_FRAME_ERROR)
- self->netdev->stats.rx_frame_errors++;
- if (lsr & IRCC_LSR_CRC_ERROR)
- self->netdev->stats.rx_crc_errors++;
- if (lsr & IRCC_LSR_SIZE_ERROR)
- self->netdev->stats.rx_length_errors++;
- if (lsr & (IRCC_LSR_UNDERRUN | IRCC_LSR_OVERRUN))
- self->netdev->stats.rx_length_errors++;
- return;
- }
-
- /* Remove CRC */
- len -= self->io.speed < 4000000 ? 2 : 4;
-
- if (len < 2 || len > 2050) {
- net_warn_ratelimited("%s(), bogus len=%d\n", __func__, len);
- return;
- }
- pr_debug("%s: msgcnt = %d, len=%d\n", __func__, msgcnt, len);
-
- skb = dev_alloc_skb(len + 1);
- if (!skb)
- return;
-
- /* Make sure IP header gets aligned */
- skb_reserve(skb, 1);
-
- skb_put_data(skb, self->rx_buff.data, len);
- self->netdev->stats.rx_packets++;
- self->netdev->stats.rx_bytes += len;
-
- skb->dev = self->netdev;
- skb_reset_mac_header(skb);
- skb->protocol = htons(ETH_P_IRDA);
- netif_rx(skb);
-}
-
-/*
- * Function smsc_ircc_sir_receive (self)
- *
- * Receive one frame from the infrared port
- *
- */
-static void smsc_ircc_sir_receive(struct smsc_ircc_cb *self)
-{
- int boguscount = 0;
- int iobase;
-
- IRDA_ASSERT(self != NULL, return;);
-
- iobase = self->io.sir_base;
-
- /*
- * Receive all characters in Rx FIFO, unwrap and unstuff them.
- * async_unwrap_char will deliver all found frames
- */
- do {
- async_unwrap_char(self->netdev, &self->netdev->stats, &self->rx_buff,
- inb(iobase + UART_RX));
-
- /* Make sure we don't stay here to long */
- if (boguscount++ > 32) {
- pr_debug("%s(), breaking!\n", __func__);
- break;
- }
- } while (inb(iobase + UART_LSR) & UART_LSR_DR);
-}
-
-
-/*
- * Function smsc_ircc_interrupt (irq, dev_id, regs)
- *
- * An interrupt from the chip has arrived. Time to do some work
- *
- */
-static irqreturn_t smsc_ircc_interrupt(int dummy, void *dev_id)
-{
- struct net_device *dev = dev_id;
- struct smsc_ircc_cb *self = netdev_priv(dev);
- int iobase, iir, lcra, lsr;
- irqreturn_t ret = IRQ_NONE;
-
- /* Serialise the interrupt handler in various CPUs, stop Tx path */
- spin_lock(&self->lock);
-
- /* Check if we should use the SIR interrupt handler */
- if (self->io.speed <= SMSC_IRCC2_MAX_SIR_SPEED) {
- ret = smsc_ircc_interrupt_sir(dev);
- goto irq_ret_unlock;
- }
-
- iobase = self->io.fir_base;
-
- register_bank(iobase, 0);
- iir = inb(iobase + IRCC_IIR);
- if (iir == 0)
- goto irq_ret_unlock;
- ret = IRQ_HANDLED;
-
- /* Disable interrupts */
- outb(0, iobase + IRCC_IER);
- lcra = inb(iobase + IRCC_LCR_A);
- lsr = inb(iobase + IRCC_LSR);
-
- pr_debug("%s(), iir = 0x%02x\n", __func__, iir);
-
- if (iir & IRCC_IIR_EOM) {
- if (self->io.direction == IO_RECV)
- smsc_ircc_dma_receive_complete(self);
- else
- smsc_ircc_dma_xmit_complete(self);
-
- smsc_ircc_dma_receive(self);
- }
-
- if (iir & IRCC_IIR_ACTIVE_FRAME) {
- /*printk(KERN_WARNING "%s(): Active Frame\n", __func__);*/
- }
-
- /* Enable interrupts again */
-
- register_bank(iobase, 0);
- outb(IRCC_IER_ACTIVE_FRAME | IRCC_IER_EOM, iobase + IRCC_IER);
-
- irq_ret_unlock:
- spin_unlock(&self->lock);
-
- return ret;
-}
-
-/*
- * Function irport_interrupt_sir (irq, dev_id)
- *
- * Interrupt handler for SIR modes
- */
-static irqreturn_t smsc_ircc_interrupt_sir(struct net_device *dev)
-{
- struct smsc_ircc_cb *self = netdev_priv(dev);
- int boguscount = 0;
- int iobase;
- int iir, lsr;
-
- /* Already locked coming here in smsc_ircc_interrupt() */
- /*spin_lock(&self->lock);*/
-
- iobase = self->io.sir_base;
-
- iir = inb(iobase + UART_IIR) & UART_IIR_ID;
- if (iir == 0)
- return IRQ_NONE;
- while (iir) {
- /* Clear interrupt */
- lsr = inb(iobase + UART_LSR);
-
- pr_debug("%s(), iir=%02x, lsr=%02x, iobase=%#x\n",
- __func__, iir, lsr, iobase);
-
- switch (iir) {
- case UART_IIR_RLSI:
- pr_debug("%s(), RLSI\n", __func__);
- break;
- case UART_IIR_RDI:
- /* Receive interrupt */
- smsc_ircc_sir_receive(self);
- break;
- case UART_IIR_THRI:
- if (lsr & UART_LSR_THRE)
- /* Transmitter ready for data */
- smsc_ircc_sir_write_wakeup(self);
- break;
- default:
- pr_debug("%s(), unhandled IIR=%#x\n",
- __func__, iir);
- break;
- }
-
- /* Make sure we don't stay here to long */
- if (boguscount++ > 100)
- break;
-
- iir = inb(iobase + UART_IIR) & UART_IIR_ID;
- }
- /*spin_unlock(&self->lock);*/
- return IRQ_HANDLED;
-}
-
-
-#if 0 /* unused */
-/*
- * Function ircc_is_receiving (self)
- *
- * Return TRUE is we are currently receiving a frame
- *
- */
-static int ircc_is_receiving(struct smsc_ircc_cb *self)
-{
- int status = FALSE;
- /* int iobase; */
-
- pr_debug("%s\n", __func__);
-
- IRDA_ASSERT(self != NULL, return FALSE;);
-
- pr_debug("%s: dma count = %d\n", __func__,
- get_dma_residue(self->io.dma));
-
- status = (self->rx_buff.state != OUTSIDE_FRAME);
-
- return status;
-}
-#endif /* unused */
-
-static int smsc_ircc_request_irq(struct smsc_ircc_cb *self)
-{
- int error;
-
- error = request_irq(self->io.irq, smsc_ircc_interrupt, 0,
- self->netdev->name, self->netdev);
- if (error)
- pr_debug("%s(), unable to allocate irq=%d, err=%d\n",
- __func__, self->io.irq, error);
-
- return error;
-}
-
-static void smsc_ircc_start_interrupts(struct smsc_ircc_cb *self)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&self->lock, flags);
-
- self->io.speed = 0;
- smsc_ircc_change_speed(self, SMSC_IRCC2_C_IRDA_FALLBACK_SPEED);
-
- spin_unlock_irqrestore(&self->lock, flags);
-}
-
-static void smsc_ircc_stop_interrupts(struct smsc_ircc_cb *self)
-{
- int iobase = self->io.fir_base;
- unsigned long flags;
-
- spin_lock_irqsave(&self->lock, flags);
-
- register_bank(iobase, 0);
- outb(0, iobase + IRCC_IER);
- outb(IRCC_MASTER_RESET, iobase + IRCC_MASTER);
- outb(0x00, iobase + IRCC_MASTER);
-
- spin_unlock_irqrestore(&self->lock, flags);
-}
-
-
-/*
- * Function smsc_ircc_net_open (dev)
- *
- * Start the device
- *
- */
-static int smsc_ircc_net_open(struct net_device *dev)
-{
- struct smsc_ircc_cb *self;
- char hwname[16];
-
- pr_debug("%s\n", __func__);
-
- IRDA_ASSERT(dev != NULL, return -1;);
- self = netdev_priv(dev);
- IRDA_ASSERT(self != NULL, return 0;);
-
- if (self->io.suspended) {
- pr_debug("%s(), device is suspended\n", __func__);
- return -EAGAIN;
- }
-
- if (request_irq(self->io.irq, smsc_ircc_interrupt, 0, dev->name,
- (void *) dev)) {
- pr_debug("%s(), unable to allocate irq=%d\n",
- __func__, self->io.irq);
- return -EAGAIN;
- }
-
- smsc_ircc_start_interrupts(self);
-
- /* Give self a hardware name */
- /* It would be cool to offer the chip revision here - Jean II */
- sprintf(hwname, "SMSC @ 0x%03x", self->io.fir_base);
-
- /*
- * Open new IrLAP layer instance, now that everything should be
- * initialized properly
- */
- self->irlap = irlap_open(dev, &self->qos, hwname);
-
- /*
- * Always allocate the DMA channel after the IRQ,
- * and clean up on failure.
- */
- if (request_dma(self->io.dma, dev->name)) {
- smsc_ircc_net_close(dev);
-
- net_warn_ratelimited("%s(), unable to allocate DMA=%d\n",
- __func__, self->io.dma);
- return -EAGAIN;
- }
-
- netif_start_queue(dev);
-
- return 0;
-}
-
-/*
- * Function smsc_ircc_net_close (dev)
- *
- * Stop the device
- *
- */
-static int smsc_ircc_net_close(struct net_device *dev)
-{
- struct smsc_ircc_cb *self;
-
- pr_debug("%s\n", __func__);
-
- IRDA_ASSERT(dev != NULL, return -1;);
- self = netdev_priv(dev);
- IRDA_ASSERT(self != NULL, return 0;);
-
- /* Stop device */
- netif_stop_queue(dev);
-
- /* Stop and remove instance of IrLAP */
- if (self->irlap)
- irlap_close(self->irlap);
- self->irlap = NULL;
-
- smsc_ircc_stop_interrupts(self);
-
- /* if we are called from smsc_ircc_resume we don't have IRQ reserved */
- if (!self->io.suspended)
- free_irq(self->io.irq, dev);
-
- disable_dma(self->io.dma);
- free_dma(self->io.dma);
-
- return 0;
-}
-
-static int smsc_ircc_suspend(struct platform_device *dev, pm_message_t state)
-{
- struct smsc_ircc_cb *self = platform_get_drvdata(dev);
-
- if (!self->io.suspended) {
- pr_debug("%s, Suspending\n", driver_name);
-
- rtnl_lock();
- if (netif_running(self->netdev)) {
- netif_device_detach(self->netdev);
- smsc_ircc_stop_interrupts(self);
- free_irq(self->io.irq, self->netdev);
- disable_dma(self->io.dma);
- }
- self->io.suspended = 1;
- rtnl_unlock();
- }
-
- return 0;
-}
-
-static int smsc_ircc_resume(struct platform_device *dev)
-{
- struct smsc_ircc_cb *self = platform_get_drvdata(dev);
-
- if (self->io.suspended) {
- pr_debug("%s, Waking up\n", driver_name);
-
- rtnl_lock();
- smsc_ircc_init_chip(self);
- if (netif_running(self->netdev)) {
- if (smsc_ircc_request_irq(self)) {
- /*
- * Don't fail resume process, just kill this
- * network interface
- */
- unregister_netdevice(self->netdev);
- } else {
- enable_dma(self->io.dma);
- smsc_ircc_start_interrupts(self);
- netif_device_attach(self->netdev);
- }
- }
- self->io.suspended = 0;
- rtnl_unlock();
- }
- return 0;
-}
-
-/*
- * Function smsc_ircc_close (self)
- *
- * Close driver instance
- *
- */
-static int __exit smsc_ircc_close(struct smsc_ircc_cb *self)
-{
- pr_debug("%s\n", __func__);
-
- IRDA_ASSERT(self != NULL, return -1;);
-
- platform_device_unregister(self->pldev);
-
- /* Remove netdevice */
- unregister_netdev(self->netdev);
-
- smsc_ircc_stop_interrupts(self);
-
- /* Release the PORTS that this driver is using */
- pr_debug("%s(), releasing 0x%03x\n", __func__,
- self->io.fir_base);
-
- release_region(self->io.fir_base, self->io.fir_ext);
-
- pr_debug("%s(), releasing 0x%03x\n", __func__,
- self->io.sir_base);
-
- release_region(self->io.sir_base, self->io.sir_ext);
-
- if (self->tx_buff.head)
- dma_free_coherent(NULL, self->tx_buff.truesize,
- self->tx_buff.head, self->tx_buff_dma);
-
- if (self->rx_buff.head)
- dma_free_coherent(NULL, self->rx_buff.truesize,
- self->rx_buff.head, self->rx_buff_dma);
-
- free_netdev(self->netdev);
-
- return 0;
-}
-
-static void __exit smsc_ircc_cleanup(void)
-{
- int i;
-
- pr_debug("%s\n", __func__);
-
- for (i = 0; i < 2; i++) {
- if (dev_self[i])
- smsc_ircc_close(dev_self[i]);
- }
-
- if (pnp_driver_registered)
- pnp_unregister_driver(&smsc_ircc_pnp_driver);
-
- platform_driver_unregister(&smsc_ircc_driver);
-}
-
-/*
- * Start SIR operations
- *
- * This function *must* be called with spinlock held, because it may
- * be called from the irq handler (via smsc_ircc_change_speed()). - Jean II
- */
-static void smsc_ircc_sir_start(struct smsc_ircc_cb *self)
-{
- struct net_device *dev;
- int fir_base, sir_base;
-
- pr_debug("%s\n", __func__);
-
- IRDA_ASSERT(self != NULL, return;);
- dev = self->netdev;
- IRDA_ASSERT(dev != NULL, return;);
-
- fir_base = self->io.fir_base;
- sir_base = self->io.sir_base;
-
- /* Reset everything */
- outb(IRCC_MASTER_RESET, fir_base + IRCC_MASTER);
-
- #if SMSC_IRCC2_C_SIR_STOP
- /*smsc_ircc_sir_stop(self);*/
- #endif
-
- register_bank(fir_base, 1);
- outb(((inb(fir_base + IRCC_SCE_CFGA) & IRCC_SCE_CFGA_BLOCK_CTRL_BITS_MASK) | IRCC_CFGA_IRDA_SIR_A), fir_base + IRCC_SCE_CFGA);
-
- /* Initialize UART */
- outb(UART_LCR_WLEN8, sir_base + UART_LCR); /* Reset DLAB */
- outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), sir_base + UART_MCR);
-
- /* Turn on interrups */
- outb(UART_IER_RLSI | UART_IER_RDI |UART_IER_THRI, sir_base + UART_IER);
-
- pr_debug("%s() - exit\n", __func__);
-
- outb(0x00, fir_base + IRCC_MASTER);
-}
-
-#if SMSC_IRCC2_C_SIR_STOP
-void smsc_ircc_sir_stop(struct smsc_ircc_cb *self)
-{
- int iobase;
-
- pr_debug("%s\n", __func__);
- iobase = self->io.sir_base;
-
- /* Reset UART */
- outb(0, iobase + UART_MCR);
-
- /* Turn off interrupts */
- outb(0, iobase + UART_IER);
-}
-#endif
-
-/*
- * Function smsc_sir_write_wakeup (self)
- *
- * Called by the SIR interrupt handler when there's room for more data.
- * If we have more packets to send, we send them here.
- *
- */
-static void smsc_ircc_sir_write_wakeup(struct smsc_ircc_cb *self)
-{
- int actual = 0;
- int iobase;
- int fcr;
-
- IRDA_ASSERT(self != NULL, return;);
-
- pr_debug("%s\n", __func__);
-
- iobase = self->io.sir_base;
-
- /* Finished with frame? */
- if (self->tx_buff.len > 0) {
- /* Write data left in transmit buffer */
- actual = smsc_ircc_sir_write(iobase, self->io.fifo_size,
- self->tx_buff.data, self->tx_buff.len);
- self->tx_buff.data += actual;
- self->tx_buff.len -= actual;
- } else {
-
- /*if (self->tx_buff.len ==0) {*/
-
- /*
- * Now serial buffer is almost free & we can start
- * transmission of another packet. But first we must check
- * if we need to change the speed of the hardware
- */
- if (self->new_speed) {
- pr_debug("%s(), Changing speed to %d.\n",
- __func__, self->new_speed);
- smsc_ircc_sir_wait_hw_transmitter_finish(self);
- smsc_ircc_change_speed(self, self->new_speed);
- self->new_speed = 0;
- } else {
- /* Tell network layer that we want more frames */
- netif_wake_queue(self->netdev);
- }
- self->netdev->stats.tx_packets++;
-
- if (self->io.speed <= 115200) {
- /*
- * Reset Rx FIFO to make sure that all reflected transmit data
- * is discarded. This is needed for half duplex operation
- */
- fcr = UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR;
- fcr |= self->io.speed < 38400 ?
- UART_FCR_TRIGGER_1 : UART_FCR_TRIGGER_14;
-
- outb(fcr, iobase + UART_FCR);
-
- /* Turn on receive interrupts */
- outb(UART_IER_RDI, iobase + UART_IER);
- }
- }
-}
-
-/*
- * Function smsc_ircc_sir_write (iobase, fifo_size, buf, len)
- *
- * Fill Tx FIFO with transmit data
- *
- */
-static int smsc_ircc_sir_write(int iobase, int fifo_size, __u8 *buf, int len)
-{
- int actual = 0;
-
- /* Tx FIFO should be empty! */
- if (!(inb(iobase + UART_LSR) & UART_LSR_THRE)) {
- net_warn_ratelimited("%s(), failed, fifo not empty!\n",
- __func__);
- return 0;
- }
-
- /* Fill FIFO with current frame */
- while (fifo_size-- > 0 && actual < len) {
- /* Transmit next byte */
- outb(buf[actual], iobase + UART_TX);
- actual++;
- }
- return actual;
-}
-
-/*
- * Function smsc_ircc_is_receiving (self)
- *
- * Returns true is we are currently receiving data
- *
- */
-static int smsc_ircc_is_receiving(struct smsc_ircc_cb *self)
-{
- return self->rx_buff.state != OUTSIDE_FRAME;
-}
-
-
-/*
- * Function smsc_ircc_probe_transceiver(self)
- *
- * Tries to find the used Transceiver
- *
- */
-static void smsc_ircc_probe_transceiver(struct smsc_ircc_cb *self)
-{
- unsigned int i;
-
- IRDA_ASSERT(self != NULL, return;);
-
- for (i = 0; smsc_transceivers[i].name != NULL; i++)
- if (smsc_transceivers[i].probe(self->io.fir_base)) {
- net_info_ratelimited(" %s transceiver found\n",
- smsc_transceivers[i].name);
- self->transceiver= i + 1;
- return;
- }
-
- net_info_ratelimited("No transceiver found. Defaulting to %s\n",
- smsc_transceivers[SMSC_IRCC2_C_DEFAULT_TRANSCEIVER].name);
-
- self->transceiver = SMSC_IRCC2_C_DEFAULT_TRANSCEIVER;
-}
-
-
-/*
- * Function smsc_ircc_set_transceiver_for_speed(self, speed)
- *
- * Set the transceiver according to the speed
- *
- */
-static void smsc_ircc_set_transceiver_for_speed(struct smsc_ircc_cb *self, u32 speed)
-{
- unsigned int trx;
-
- trx = self->transceiver;
- if (trx > 0)
- smsc_transceivers[trx - 1].set_for_speed(self->io.fir_base, speed);
-}
-
-/*
- * Function smsc_ircc_wait_hw_transmitter_finish ()
- *
- * Wait for the real end of HW transmission
- *
- * The UART is a strict FIFO, and we get called only when we have finished
- * pushing data to the FIFO, so the maximum amount of time we must wait
- * is only for the FIFO to drain out.
- *
- * We use a simple calibrated loop. We may need to adjust the loop
- * delay (udelay) to balance I/O traffic and latency. And we also need to
- * adjust the maximum timeout.
- * It would probably be better to wait for the proper interrupt,
- * but it doesn't seem to be available.
- *
- * We can't use jiffies or kernel timers because :
- * 1) We are called from the interrupt handler, which disable softirqs,
- * so jiffies won't be increased
- * 2) Jiffies granularity is usually very coarse (10ms), and we don't
- * want to wait that long to detect stuck hardware.
- * Jean II
- */
-
-static void smsc_ircc_sir_wait_hw_transmitter_finish(struct smsc_ircc_cb *self)
-{
- int iobase = self->io.sir_base;
- int count = SMSC_IRCC2_HW_TRANSMITTER_TIMEOUT_US;
-
- /* Calibrated busy loop */
- while (count-- > 0 && !(inb(iobase + UART_LSR) & UART_LSR_TEMT))
- udelay(1);
-
- if (count < 0)
- pr_debug("%s(): stuck transmitter\n", __func__);
-}
-
-
-/* PROBING
- *
- * REVISIT we can be told about the device by PNP, and should use that info
- * instead of probing hardware and creating a platform_device ...
- */
-
-static int __init smsc_ircc_look_for_chips(void)
-{
- struct smsc_chip_address *address;
- char *type;
- unsigned int cfg_base, found;
-
- found = 0;
- address = possible_addresses;
-
- while (address->cfg_base) {
- cfg_base = address->cfg_base;
-
- /*printk(KERN_WARNING "%s(): probing: 0x%02x for: 0x%02x\n", __func__, cfg_base, address->type);*/
-
- if (address->type & SMSCSIO_TYPE_FDC) {
- type = "FDC";
- if (address->type & SMSCSIO_TYPE_FLAT)
- if (!smsc_superio_flat(fdc_chips_flat, cfg_base, type))
- found++;
-
- if (address->type & SMSCSIO_TYPE_PAGED)
- if (!smsc_superio_paged(fdc_chips_paged, cfg_base, type))
- found++;
- }
- if (address->type & SMSCSIO_TYPE_LPC) {
- type = "LPC";
- if (address->type & SMSCSIO_TYPE_FLAT)
- if (!smsc_superio_flat(lpc_chips_flat, cfg_base, type))
- found++;
-
- if (address->type & SMSCSIO_TYPE_PAGED)
- if (!smsc_superio_paged(lpc_chips_paged, cfg_base, type))
- found++;
- }
- address++;
- }
- return found;
-}
-
-/*
- * Function smsc_superio_flat (chip, base, type)
- *
- * Try to get configuration of a smc SuperIO chip with flat register model
- *
- */
-static int __init smsc_superio_flat(const struct smsc_chip *chips, unsigned short cfgbase, char *type)
-{
- unsigned short firbase, sirbase;
- u8 mode, dma, irq;
- int ret = -ENODEV;
-
- pr_debug("%s\n", __func__);
-
- if (smsc_ircc_probe(cfgbase, SMSCSIOFLAT_DEVICEID_REG, chips, type) == NULL)
- return ret;
-
- outb(SMSCSIOFLAT_UARTMODE0C_REG, cfgbase);
- mode = inb(cfgbase + 1);
-
- /*printk(KERN_WARNING "%s(): mode: 0x%02x\n", __func__, mode);*/
-
- if (!(mode & SMSCSIOFLAT_UART2MODE_VAL_IRDA))
- net_warn_ratelimited("%s(): IrDA not enabled\n", __func__);
-
- outb(SMSCSIOFLAT_UART2BASEADDR_REG, cfgbase);
- sirbase = inb(cfgbase + 1) << 2;
-
- /* FIR iobase */
- outb(SMSCSIOFLAT_FIRBASEADDR_REG, cfgbase);
- firbase = inb(cfgbase + 1) << 3;
-
- /* DMA */
- outb(SMSCSIOFLAT_FIRDMASELECT_REG, cfgbase);
- dma = inb(cfgbase + 1) & SMSCSIOFLAT_FIRDMASELECT_MASK;
-
- /* IRQ */
- outb(SMSCSIOFLAT_UARTIRQSELECT_REG, cfgbase);
- irq = inb(cfgbase + 1) & SMSCSIOFLAT_UART2IRQSELECT_MASK;
-
- net_info_ratelimited("%s(): fir: 0x%02x, sir: 0x%02x, dma: %02d, irq: %d, mode: 0x%02x\n",
- __func__, firbase, sirbase, dma, irq, mode);
-
- if (firbase && smsc_ircc_open(firbase, sirbase, dma, irq) == 0)
- ret = 0;
-
- /* Exit configuration */
- outb(SMSCSIO_CFGEXITKEY, cfgbase);
-
- return ret;
-}
-
-/*
- * Function smsc_superio_paged (chip, base, type)
- *
- * Try to get configuration of a smc SuperIO chip with paged register model
- *
- */
-static int __init smsc_superio_paged(const struct smsc_chip *chips, unsigned short cfg_base, char *type)
-{
- unsigned short fir_io, sir_io;
- int ret = -ENODEV;
-
- pr_debug("%s\n", __func__);
-
- if (smsc_ircc_probe(cfg_base, 0x20, chips, type) == NULL)
- return ret;
-
- /* Select logical device (UART2) */
- outb(0x07, cfg_base);
- outb(0x05, cfg_base + 1);
-
- /* SIR iobase */
- outb(0x60, cfg_base);
- sir_io = inb(cfg_base + 1) << 8;
- outb(0x61, cfg_base);
- sir_io |= inb(cfg_base + 1);
-
- /* Read FIR base */
- outb(0x62, cfg_base);
- fir_io = inb(cfg_base + 1) << 8;
- outb(0x63, cfg_base);
- fir_io |= inb(cfg_base + 1);
- outb(0x2b, cfg_base); /* ??? */
-
- if (fir_io && smsc_ircc_open(fir_io, sir_io, ircc_dma, ircc_irq) == 0)
- ret = 0;
-
- /* Exit configuration */
- outb(SMSCSIO_CFGEXITKEY, cfg_base);
-
- return ret;
-}
-
-
-static int __init smsc_access(unsigned short cfg_base, unsigned char reg)
-{
- pr_debug("%s\n", __func__);
-
- outb(reg, cfg_base);
- return inb(cfg_base) != reg ? -1 : 0;
-}
-
-static const struct smsc_chip * __init smsc_ircc_probe(unsigned short cfg_base, u8 reg, const struct smsc_chip *chip, char *type)
-{
- u8 devid, xdevid, rev;
-
- pr_debug("%s\n", __func__);
-
- /* Leave configuration */
-
- outb(SMSCSIO_CFGEXITKEY, cfg_base);
-
- if (inb(cfg_base) == SMSCSIO_CFGEXITKEY) /* not a smc superio chip */
- return NULL;
-
- outb(reg, cfg_base);
-
- xdevid = inb(cfg_base + 1);
-
- /* Enter configuration */
-
- outb(SMSCSIO_CFGACCESSKEY, cfg_base);
-
- #if 0
- if (smsc_access(cfg_base,0x55)) /* send second key and check */
- return NULL;
- #endif
-
- /* probe device ID */
-
- if (smsc_access(cfg_base, reg))
- return NULL;
-
- devid = inb(cfg_base + 1);
-
- if (devid == 0 || devid == 0xff) /* typical values for unused port */
- return NULL;
-
- /* probe revision ID */
-
- if (smsc_access(cfg_base, reg + 1))
- return NULL;
-
- rev = inb(cfg_base + 1);
-
- if (rev >= 128) /* i think this will make no sense */
- return NULL;
-
- if (devid == xdevid) /* protection against false positives */
- return NULL;
-
- /* Check for expected device ID; are there others? */
-
- while (chip->devid != devid) {
-
- chip++;
-
- if (chip->name == NULL)
- return NULL;
- }
-
- net_info_ratelimited("found SMC SuperIO Chip (devid=0x%02x rev=%02X base=0x%04x): %s%s\n",
- devid, rev, cfg_base, type, chip->name);
-
- if (chip->rev > rev) {
- net_info_ratelimited("Revision higher than expected\n");
- return NULL;
- }
-
- if (chip->flags & NoIRDA)
- net_info_ratelimited("chipset does not support IRDA\n");
-
- return chip;
-}
-
-static int __init smsc_superio_fdc(unsigned short cfg_base)
-{
- int ret = -1;
-
- if (!request_region(cfg_base, 2, driver_name)) {
- net_warn_ratelimited("%s: can't get cfg_base of 0x%03x\n",
- __func__, cfg_base);
- } else {
- if (!smsc_superio_flat(fdc_chips_flat, cfg_base, "FDC") ||
- !smsc_superio_paged(fdc_chips_paged, cfg_base, "FDC"))
- ret = 0;
-
- release_region(cfg_base, 2);
- }
-
- return ret;
-}
-
-static int __init smsc_superio_lpc(unsigned short cfg_base)
-{
- int ret = -1;
-
- if (!request_region(cfg_base, 2, driver_name)) {
- net_warn_ratelimited("%s: can't get cfg_base of 0x%03x\n",
- __func__, cfg_base);
- } else {
- if (!smsc_superio_flat(lpc_chips_flat, cfg_base, "LPC") ||
- !smsc_superio_paged(lpc_chips_paged, cfg_base, "LPC"))
- ret = 0;
-
- release_region(cfg_base, 2);
- }
- return ret;
-}
-
-/*
- * Look for some specific subsystem setups that need
- * pre-configuration not properly done by the BIOS (especially laptops)
- * This code is based in part on smcinit.c, tosh1800-smcinit.c
- * and tosh2450-smcinit.c. The table lists the device entries
- * for ISA bridges with an LPC (Low Pin Count) controller which
- * handles the communication with the SMSC device. After the LPC
- * controller is initialized through PCI, the SMSC device is initialized
- * through a dedicated port in the ISA port-mapped I/O area, this latter
- * area is used to configure the SMSC device with default
- * SIR and FIR I/O ports, DMA and IRQ. Different vendors have
- * used different sets of parameters and different control port
- * addresses making a subsystem device table necessary.
- */
-#ifdef CONFIG_PCI
-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 = PCI_VENDOR_ID_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 = PCI_VENDOR_ID_INTEL, /* Intel 82801DBM LPC bridge */
- .device = 0x24cc,
- .subvendor = 0x103c,
- .subdevice = 0x088c,
- /* Quite certain these are the same for nc8000 as for nc6000 */
- .sir_io = 0x02f8,
- .fir_io = 0x0130,
- .fir_irq = 0x05,
- .fir_dma = 0x03,
- .cfg_base = 0x004e,
- .preconfigure = preconfigure_through_82801,
- .name = "HP nc8000 family",
- },
- {
- .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801DBM LPC bridge */
- .device = 0x24cc,
- .subvendor = 0x103c,
- .subdevice = 0x0890,
- .sir_io = 0x02f8,
- .fir_io = 0x0130,
- .fir_irq = 0x05,
- .fir_dma = 0x03,
- .cfg_base = 0x004e,
- .preconfigure = preconfigure_through_82801,
- .name = "HP nc6000 family",
- },
- {
- .vendor = PCI_VENDOR_ID_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 */
- .vendor = PCI_VENDOR_ID_INTEL,
- .device = 0x24c0,
- .subvendor = 0x1179,
- .subdevice = 0xffff, /* 0xffff is "any" */
- .sir_io = 0x03f8,
- .fir_io = 0x0130,
- .fir_irq = 0x07,
- .fir_dma = 0x01,
- .cfg_base = 0x002e,
- .preconfigure = preconfigure_through_82801,
- .name = "Toshiba laptop with Intel 82801DB/DBL LPC bridge",
- },
- {
- .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801CAM ISA bridge */
- .device = 0x248c,
- .subvendor = 0x1179,
- .subdevice = 0xffff, /* 0xffff is "any" */
- .sir_io = 0x03f8,
- .fir_io = 0x0130,
- .fir_irq = 0x03,
- .fir_dma = 0x03,
- .cfg_base = 0x002e,
- .preconfigure = preconfigure_through_82801,
- .name = "Toshiba laptop with Intel 82801CAM ISA bridge",
- },
- {
- /* 82801DBM (ICH4-M) LPC Interface Bridge */
- .vendor = PCI_VENDOR_ID_INTEL,
- .device = 0x24cc,
- .subvendor = 0x1179,
- .subdevice = 0xffff, /* 0xffff is "any" */
- .sir_io = 0x03f8,
- .fir_io = 0x0130,
- .fir_irq = 0x03,
- .fir_dma = 0x03,
- .cfg_base = 0x002e,
- .preconfigure = preconfigure_through_82801,
- .name = "Toshiba laptop with Intel 8281DBM LPC bridge",
- },
- {
- /* ALi M1533/M1535 PCI to ISA Bridge [Aladdin IV/V/V+] */
- .vendor = PCI_VENDOR_ID_AL,
- .device = 0x1533,
- .subvendor = 0x1179,
- .subdevice = 0xffff, /* 0xffff is "any" */
- .sir_io = 0x02e8,
- .fir_io = 0x02f8,
- .fir_irq = 0x07,
- .fir_dma = 0x03,
- .cfg_base = 0x002e,
- .preconfigure = preconfigure_through_ali,
- .name = "Toshiba laptop with ALi ISA bridge",
- },
- { } // Terminator
-};
-
-
-/*
- * This sets up the basic SMSC parameters
- * (FIR port, SIR port, FIR DMA, FIR IRQ)
- * through the chip configuration port.
- */
-static int __init preconfigure_smsc_chip(struct
- smsc_ircc_subsystem_configuration
- *conf)
-{
- unsigned short iobase = conf->cfg_base;
- unsigned char tmpbyte;
-
- outb(LPC47N227_CFGACCESSKEY, iobase); // enter configuration state
- outb(SMSCSIOFLAT_DEVICEID_REG, iobase); // set for device ID
- tmpbyte = inb(iobase +1); // Read device ID
- pr_debug("Detected Chip id: 0x%02x, setting up registers...\n",
- tmpbyte);
-
- /* Disable UART1 and set up SIR I/O port */
- outb(0x24, iobase); // select CR24 - UART1 base addr
- outb(0x00, iobase + 1); // disable UART1
- outb(SMSCSIOFLAT_UART2BASEADDR_REG, iobase); // select CR25 - UART2 base addr
- outb( (conf->sir_io >> 2), iobase + 1); // bits 2-9 of 0x3f8
- tmpbyte = inb(iobase + 1);
- if (tmpbyte != (conf->sir_io >> 2) ) {
- net_warn_ratelimited("ERROR: could not configure SIR ioport\n");
- net_warn_ratelimited("Try to supply ircc_cfg argument\n");
- return -ENXIO;
- }
-
- /* Set up FIR IRQ channel for UART2 */
- outb(SMSCSIOFLAT_UARTIRQSELECT_REG, iobase); // select CR28 - UART1,2 IRQ select
- tmpbyte = inb(iobase + 1);
- tmpbyte &= SMSCSIOFLAT_UART1IRQSELECT_MASK; // Do not touch the UART1 portion
- tmpbyte |= (conf->fir_irq & SMSCSIOFLAT_UART2IRQSELECT_MASK);
- outb(tmpbyte, iobase + 1);
- tmpbyte = inb(iobase + 1) & SMSCSIOFLAT_UART2IRQSELECT_MASK;
- if (tmpbyte != conf->fir_irq) {
- net_warn_ratelimited("ERROR: could not configure FIR IRQ channel\n");
- return -ENXIO;
- }
-
- /* Set up FIR I/O port */
- outb(SMSCSIOFLAT_FIRBASEADDR_REG, iobase); // CR2B - SCE (FIR) base addr
- outb((conf->fir_io >> 3), iobase + 1);
- tmpbyte = inb(iobase + 1);
- if (tmpbyte != (conf->fir_io >> 3) ) {
- net_warn_ratelimited("ERROR: could not configure FIR I/O port\n");
- return -ENXIO;
- }
-
- /* Set up FIR DMA channel */
- outb(SMSCSIOFLAT_FIRDMASELECT_REG, iobase); // CR2C - SCE (FIR) DMA select
- outb((conf->fir_dma & LPC47N227_FIRDMASELECT_MASK), iobase + 1); // DMA
- tmpbyte = inb(iobase + 1) & LPC47N227_FIRDMASELECT_MASK;
- if (tmpbyte != (conf->fir_dma & LPC47N227_FIRDMASELECT_MASK)) {
- net_warn_ratelimited("ERROR: could not configure FIR DMA channel\n");
- return -ENXIO;
- }
-
- outb(SMSCSIOFLAT_UARTMODE0C_REG, iobase); // CR0C - UART mode
- tmpbyte = inb(iobase + 1);
- tmpbyte &= ~SMSCSIOFLAT_UART2MODE_MASK |
- SMSCSIOFLAT_UART2MODE_VAL_IRDA;
- outb(tmpbyte, iobase + 1); // enable IrDA (HPSIR) mode, high speed
-
- outb(LPC47N227_APMBOOTDRIVE_REG, iobase); // CR07 - Auto Pwr Mgt/boot drive sel
- tmpbyte = inb(iobase + 1);
- outb(tmpbyte | LPC47N227_UART2AUTOPWRDOWN_MASK, iobase + 1); // enable UART2 autopower down
-
- /* This one was not part of tosh1800 */
- outb(0x0a, iobase); // CR0a - ecp fifo / ir mux
- tmpbyte = inb(iobase + 1);
- outb(tmpbyte | 0x40, iobase + 1); // send active device to ir port
-
- outb(LPC47N227_UART12POWER_REG, iobase); // CR02 - UART 1,2 power
- tmpbyte = inb(iobase + 1);
- outb(tmpbyte | LPC47N227_UART2POWERDOWN_MASK, iobase + 1); // UART2 power up mode, UART1 power down
-
- outb(LPC47N227_FDCPOWERVALIDCONF_REG, iobase); // CR00 - FDC Power/valid config cycle
- tmpbyte = inb(iobase + 1);
- outb(tmpbyte | LPC47N227_VALID_MASK, iobase + 1); // valid config cycle done
-
- outb(LPC47N227_CFGEXITKEY, iobase); // Exit configuration
-
- return 0;
-}
-
-/* 82801CAM generic registers */
-#define VID 0x00
-#define DID 0x02
-#define PIRQ_A_D_ROUT 0x60
-#define SIRQ_CNTL 0x64
-#define PIRQ_E_H_ROUT 0x68
-#define PCI_DMA_C 0x90
-/* LPC-specific registers */
-#define COM_DEC 0xe0
-#define GEN1_DEC 0xe4
-#define LPC_EN 0xe6
-#define GEN2_DEC 0xec
-/*
- * Sets up the I/O range using the 82801CAM ISA bridge, 82801DBM LPC bridge
- * or Intel 82801DB/DBL (ICH4/ICH4-L) LPC Interface Bridge.
- * They all work the same way!
- */
-static int __init preconfigure_through_82801(struct pci_dev *dev,
- struct
- smsc_ircc_subsystem_configuration
- *conf)
-{
- unsigned short tmpword;
- unsigned char tmpbyte;
-
- net_info_ratelimited("Setting up Intel 82801 controller and SMSC device\n");
- /*
- * Select the range for the COMA COM port (SIR)
- * Register COM_DEC:
- * Bit 7: reserved
- * Bit 6-4, COMB decode range
- * Bit 3: reserved
- * Bit 2-0, COMA decode range
- *
- * Decode ranges:
- * 000 = 0x3f8-0x3ff (COM1)
- * 001 = 0x2f8-0x2ff (COM2)
- * 010 = 0x220-0x227
- * 011 = 0x228-0x22f
- * 100 = 0x238-0x23f
- * 101 = 0x2e8-0x2ef (COM4)
- * 110 = 0x338-0x33f
- * 111 = 0x3e8-0x3ef (COM3)
- */
- pci_read_config_byte(dev, COM_DEC, &tmpbyte);
- tmpbyte &= 0xf8; /* mask COMA bits */
- switch(conf->sir_io) {
- case 0x3f8:
- tmpbyte |= 0x00;
- break;
- case 0x2f8:
- tmpbyte |= 0x01;
- break;
- case 0x220:
- tmpbyte |= 0x02;
- break;
- case 0x228:
- tmpbyte |= 0x03;
- break;
- case 0x238:
- tmpbyte |= 0x04;
- break;
- case 0x2e8:
- tmpbyte |= 0x05;
- break;
- case 0x338:
- tmpbyte |= 0x06;
- break;
- case 0x3e8:
- tmpbyte |= 0x07;
- break;
- default:
- tmpbyte |= 0x01; /* COM2 default */
- }
- pr_debug("COM_DEC (write): 0x%02x\n", tmpbyte);
- pci_write_config_byte(dev, COM_DEC, tmpbyte);
-
- /* Enable Low Pin Count interface */
- pci_read_config_word(dev, LPC_EN, &tmpword);
- /* These seem to be set up at all times,
- * just make sure it is properly set.
- */
- switch(conf->cfg_base) {
- case 0x04e:
- tmpword |= 0x2000;
- break;
- case 0x02e:
- tmpword |= 0x1000;
- break;
- case 0x062:
- tmpword |= 0x0800;
- break;
- case 0x060:
- tmpword |= 0x0400;
- break;
- default:
- net_warn_ratelimited("Uncommon I/O base address: 0x%04x\n",
- conf->cfg_base);
- break;
- }
- tmpword &= 0xfffd; /* disable LPC COMB */
- tmpword |= 0x0001; /* set bit 0 : enable LPC COMA addr range (GEN2) */
- pr_debug("LPC_EN (write): 0x%04x\n", tmpword);
- pci_write_config_word(dev, LPC_EN, tmpword);
-
- /*
- * Configure LPC DMA channel
- * PCI_DMA_C bits:
- * Bit 15-14: DMA channel 7 select
- * Bit 13-12: DMA channel 6 select
- * Bit 11-10: DMA channel 5 select
- * Bit 9-8: Reserved
- * Bit 7-6: DMA channel 3 select
- * Bit 5-4: DMA channel 2 select
- * Bit 3-2: DMA channel 1 select
- * Bit 1-0: DMA channel 0 select
- * 00 = Reserved value
- * 01 = PC/PCI DMA
- * 10 = Reserved value
- * 11 = LPC I/F DMA
- */
- pci_read_config_word(dev, PCI_DMA_C, &tmpword);
- switch(conf->fir_dma) {
- case 0x07:
- tmpword |= 0xc000;
- break;
- case 0x06:
- tmpword |= 0x3000;
- break;
- case 0x05:
- tmpword |= 0x0c00;
- break;
- case 0x03:
- tmpword |= 0x00c0;
- break;
- case 0x02:
- tmpword |= 0x0030;
- break;
- case 0x01:
- tmpword |= 0x000c;
- break;
- case 0x00:
- tmpword |= 0x0003;
- break;
- default:
- break; /* do not change settings */
- }
- pr_debug("PCI_DMA_C (write): 0x%04x\n", tmpword);
- pci_write_config_word(dev, PCI_DMA_C, tmpword);
-
- /*
- * GEN2_DEC bits:
- * Bit 15-4: Generic I/O range
- * Bit 3-1: reserved (read as 0)
- * Bit 0: enable GEN2 range on LPC I/F
- */
- tmpword = conf->fir_io & 0xfff8;
- tmpword |= 0x0001;
- pr_debug("GEN2_DEC (write): 0x%04x\n", tmpword);
- pci_write_config_word(dev, GEN2_DEC, tmpword);
-
- /* Pre-configure chip */
- return preconfigure_smsc_chip(conf);
-}
-
-/*
- * Pre-configure a certain port on the ALi 1533 bridge.
- * This is based on reverse-engineering since ALi does not
- * provide any data sheet for the 1533 chip.
- */
-static void __init preconfigure_ali_port(struct pci_dev *dev,
- unsigned short port)
-{
- unsigned char reg;
- /* These bits obviously control the different ports */
- unsigned char mask;
- unsigned char tmpbyte;
-
- switch(port) {
- case 0x0130:
- case 0x0178:
- reg = 0xb0;
- mask = 0x80;
- break;
- case 0x03f8:
- reg = 0xb4;
- mask = 0x80;
- break;
- case 0x02f8:
- reg = 0xb4;
- mask = 0x30;
- break;
- case 0x02e8:
- reg = 0xb4;
- mask = 0x08;
- break;
- default:
- net_err_ratelimited("Failed to configure unsupported port on ALi 1533 bridge: 0x%04x\n",
- port);
- return;
- }
-
- pci_read_config_byte(dev, reg, &tmpbyte);
- /* Turn on the right bits */
- tmpbyte |= mask;
- pci_write_config_byte(dev, reg, tmpbyte);
- net_info_ratelimited("Activated ALi 1533 ISA bridge port 0x%04x\n",
- port);
-}
-
-static int __init preconfigure_through_ali(struct pci_dev *dev,
- struct
- smsc_ircc_subsystem_configuration
- *conf)
-{
- /* Configure the two ports on the ALi 1533 */
- preconfigure_ali_port(dev, conf->sir_io);
- preconfigure_ali_port(dev, conf->fir_io);
-
- /* Pre-configure chip */
- return preconfigure_smsc_chip(conf);
-}
-
-static int __init smsc_ircc_preconfigure_subsystems(unsigned short ircc_cfg,
- unsigned short ircc_fir,
- unsigned short ircc_sir,
- unsigned char ircc_dma,
- unsigned char ircc_irq)
-{
- struct pci_dev *dev = NULL;
- unsigned short ss_vendor = 0x0000;
- unsigned short ss_device = 0x0000;
- int ret = 0;
-
- for_each_pci_dev(dev) {
- struct smsc_ircc_subsystem_configuration *conf;
-
- /*
- * Cache the subsystem vendor/device:
- * some manufacturers fail to set this for all components,
- * so we save it in case there is just 0x0000 0x0000 on the
- * device we want to check.
- */
- if (dev->subsystem_vendor != 0x0000U) {
- ss_vendor = dev->subsystem_vendor;
- ss_device = dev->subsystem_device;
- }
- conf = subsystem_configurations;
- for( ; conf->subvendor; conf++) {
- if(conf->vendor == dev->vendor &&
- conf->device == dev->device &&
- conf->subvendor == ss_vendor &&
- /* Sometimes these are cached values */
- (conf->subdevice == ss_device ||
- conf->subdevice == 0xffff)) {
- struct smsc_ircc_subsystem_configuration
- tmpconf;
-
- memcpy(&tmpconf, conf,
- sizeof(struct smsc_ircc_subsystem_configuration));
-
- /*
- * Override the default values with anything
- * passed in as parameter
- */
- if (ircc_cfg != 0)
- tmpconf.cfg_base = ircc_cfg;
- if (ircc_fir != 0)
- tmpconf.fir_io = ircc_fir;
- if (ircc_sir != 0)
- tmpconf.sir_io = ircc_sir;
- if (ircc_dma != DMA_INVAL)
- tmpconf.fir_dma = ircc_dma;
- if (ircc_irq != IRQ_INVAL)
- tmpconf.fir_irq = ircc_irq;
-
- net_info_ratelimited("Detected unconfigured %s SMSC IrDA chip, pre-configuring device\n",
- conf->name);
- if (conf->preconfigure)
- ret = conf->preconfigure(dev, &tmpconf);
- else
- ret = -ENODEV;
- }
- }
- }
-
- return ret;
-}
-#endif // CONFIG_PCI
-
-/************************************************
- *
- * Transceivers specific functions
- *
- ************************************************/
-
-
-/*
- * Function smsc_ircc_set_transceiver_smsc_ircc_atc(fir_base, speed)
- *
- * Program transceiver through smsc-ircc ATC circuitry
- *
- */
-
-static void smsc_ircc_set_transceiver_smsc_ircc_atc(int fir_base, u32 speed)
-{
- unsigned long jiffies_now, jiffies_timeout;
- u8 val;
-
- jiffies_now = jiffies;
- jiffies_timeout = jiffies + SMSC_IRCC2_ATC_PROGRAMMING_TIMEOUT_JIFFIES;
-
- /* ATC */
- register_bank(fir_base, 4);
- outb((inb(fir_base + IRCC_ATC) & IRCC_ATC_MASK) | IRCC_ATC_nPROGREADY|IRCC_ATC_ENABLE,
- fir_base + IRCC_ATC);
-
- while ((val = (inb(fir_base + IRCC_ATC) & IRCC_ATC_nPROGREADY)) &&
- !time_after(jiffies, jiffies_timeout))
- /* empty */;
-
- if (val)
- net_warn_ratelimited("%s(): ATC: 0x%02x\n",
- __func__, inb(fir_base + IRCC_ATC));
-}
-
-/*
- * Function smsc_ircc_probe_transceiver_smsc_ircc_atc(fir_base)
- *
- * Probe transceiver smsc-ircc ATC circuitry
- *
- */
-
-static int smsc_ircc_probe_transceiver_smsc_ircc_atc(int fir_base)
-{
- return 0;
-}
-
-/*
- * Function smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select(self, speed)
- *
- * Set transceiver
- *
- */
-
-static void smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select(int fir_base, u32 speed)
-{
- u8 fast_mode;
-
- switch (speed) {
- default:
- case 576000 :
- fast_mode = 0;
- break;
- case 1152000 :
- case 4000000 :
- fast_mode = IRCC_LCR_A_FAST;
- break;
- }
- register_bank(fir_base, 0);
- outb((inb(fir_base + IRCC_LCR_A) & 0xbf) | fast_mode, fir_base + IRCC_LCR_A);
-}
-
-/*
- * Function smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select(fir_base)
- *
- * Probe transceiver
- *
- */
-
-static int smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select(int fir_base)
-{
- return 0;
-}
-
-/*
- * Function smsc_ircc_set_transceiver_toshiba_sat1800(fir_base, speed)
- *
- * Set transceiver
- *
- */
-
-static void smsc_ircc_set_transceiver_toshiba_sat1800(int fir_base, u32 speed)
-{
- u8 fast_mode;
-
- switch (speed) {
- default:
- case 576000 :
- fast_mode = 0;
- break;
- case 1152000 :
- case 4000000 :
- fast_mode = /*IRCC_LCR_A_FAST |*/ IRCC_LCR_A_GP_DATA;
- break;
-
- }
- /* This causes an interrupt */
- register_bank(fir_base, 0);
- outb((inb(fir_base + IRCC_LCR_A) & 0xbf) | fast_mode, fir_base + IRCC_LCR_A);
-}
-
-/*
- * Function smsc_ircc_probe_transceiver_toshiba_sat1800(fir_base)
- *
- * Probe transceiver
- *
- */
-
-static int smsc_ircc_probe_transceiver_toshiba_sat1800(int fir_base)
-{
- return 0;
-}
-
-
-module_init(smsc_ircc_init);
-module_exit(smsc_ircc_cleanup);
diff --git a/drivers/staging/irda/drivers/smsc-ircc2.h b/drivers/staging/irda/drivers/smsc-ircc2.h
deleted file mode 100644
index 4829fa22cb29..000000000000
--- a/drivers/staging/irda/drivers/smsc-ircc2.h
+++ /dev/null
@@ -1,191 +0,0 @@
-/*********************************************************************
- *
- * Description: Definitions for the SMC IrCC chipset
- * Status: Experimental.
- * Author: Daniele Peri (peri@csai.unipa.it)
- *
- * Copyright (c) 2002 Daniele Peri
- * All Rights Reserved.
- *
- * Based on smc-ircc.h:
- *
- * Copyright (c) 1999-2000, Dag Brattli <dagb@cs.uit.no>
- * Copyright (c) 1998-1999, Thomas Davis (tadavis@jps.net>
- * All Rights Reserved
- *
- *
- * 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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#ifndef SMSC_IRCC2_H
-#define SMSC_IRCC2_H
-
-/* DMA modes needed */
-#define DMA_TX_MODE 0x08 /* Mem to I/O, ++, demand. */
-#define DMA_RX_MODE 0x04 /* I/O to mem, ++, demand. */
-
-/* Master Control Register */
-#define IRCC_MASTER 0x07
-#define IRCC_MASTER_POWERDOWN 0x80
-#define IRCC_MASTER_RESET 0x40
-#define IRCC_MASTER_INT_EN 0x20
-#define IRCC_MASTER_ERROR_RESET 0x10
-
-/* Register block 0 */
-
-/* Interrupt Identification */
-#define IRCC_IIR 0x01
-#define IRCC_IIR_ACTIVE_FRAME 0x80
-#define IRCC_IIR_EOM 0x40
-#define IRCC_IIR_RAW_MODE 0x20
-#define IRCC_IIR_FIFO 0x10
-
-/* Interrupt Enable */
-#define IRCC_IER 0x02
-#define IRCC_IER_ACTIVE_FRAME 0x80
-#define IRCC_IER_EOM 0x40
-#define IRCC_IER_RAW_MODE 0x20
-#define IRCC_IER_FIFO 0x10
-
-/* Line Status Register */
-#define IRCC_LSR 0x03
-#define IRCC_LSR_UNDERRUN 0x80
-#define IRCC_LSR_OVERRUN 0x40
-#define IRCC_LSR_FRAME_ERROR 0x20
-#define IRCC_LSR_SIZE_ERROR 0x10
-#define IRCC_LSR_CRC_ERROR 0x80
-#define IRCC_LSR_FRAME_ABORT 0x40
-
-/* Line Status Address Register */
-#define IRCC_LSAR 0x03
-#define IRCC_LSAR_ADDRESS_MASK 0x07
-
-/* Line Control Register A */
-#define IRCC_LCR_A 0x04
-#define IRCC_LCR_A_FIFO_RESET 0x80
-#define IRCC_LCR_A_FAST 0x40
-#define IRCC_LCR_A_GP_DATA 0x20
-#define IRCC_LCR_A_RAW_TX 0x10
-#define IRCC_LCR_A_RAW_RX 0x08
-#define IRCC_LCR_A_ABORT 0x04
-#define IRCC_LCR_A_DATA_DONE 0x02
-
-/* Line Control Register B */
-#define IRCC_LCR_B 0x05
-#define IRCC_LCR_B_SCE_DISABLED 0x00
-#define IRCC_LCR_B_SCE_TRANSMIT 0x40
-#define IRCC_LCR_B_SCE_RECEIVE 0x80
-#define IRCC_LCR_B_SCE_UNDEFINED 0xc0
-#define IRCC_LCR_B_SIP_ENABLE 0x20
-#define IRCC_LCR_B_BRICK_WALL 0x10
-
-/* Bus Status Register */
-#define IRCC_BSR 0x06
-#define IRCC_BSR_NOT_EMPTY 0x80
-#define IRCC_BSR_FIFO_FULL 0x40
-#define IRCC_BSR_TIMEOUT 0x20
-
-/* Register block 1 */
-
-#define IRCC_FIFO_THRESHOLD 0x02
-
-#define IRCC_SCE_CFGA 0x00
-#define IRCC_CFGA_AUX_IR 0x80
-#define IRCC_CFGA_HALF_DUPLEX 0x04
-#define IRCC_CFGA_TX_POLARITY 0x02
-#define IRCC_CFGA_RX_POLARITY 0x01
-
-#define IRCC_CFGA_COM 0x00
-#define IRCC_SCE_CFGA_BLOCK_CTRL_BITS_MASK 0x87
-#define IRCC_CFGA_IRDA_SIR_A 0x08
-#define IRCC_CFGA_ASK_SIR 0x10
-#define IRCC_CFGA_IRDA_SIR_B 0x18
-#define IRCC_CFGA_IRDA_HDLC 0x20
-#define IRCC_CFGA_IRDA_4PPM 0x28
-#define IRCC_CFGA_CONSUMER 0x30
-#define IRCC_CFGA_RAW_IR 0x38
-#define IRCC_CFGA_OTHER 0x40
-
-#define IRCC_IR_HDLC 0x04
-#define IRCC_IR_4PPM 0x01
-#define IRCC_IR_CONSUMER 0x02
-
-#define IRCC_SCE_CFGB 0x01
-#define IRCC_CFGB_LOOPBACK 0x20
-#define IRCC_CFGB_LPBCK_TX_CRC 0x10
-#define IRCC_CFGB_NOWAIT 0x08
-#define IRCC_CFGB_STRING_MOVE 0x04
-#define IRCC_CFGB_DMA_BURST 0x02
-#define IRCC_CFGB_DMA_ENABLE 0x01
-
-#define IRCC_CFGB_MUX_COM 0x00
-#define IRCC_CFGB_MUX_IR 0x40
-#define IRCC_CFGB_MUX_AUX 0x80
-#define IRCC_CFGB_MUX_INACTIVE 0xc0
-
-/* Register block 3 - Identification Registers! */
-#define IRCC_ID_HIGH 0x00 /* 0x10 */
-#define IRCC_ID_LOW 0x01 /* 0xB8 */
-#define IRCC_CHIP_ID 0x02 /* 0xF1 */
-#define IRCC_VERSION 0x03 /* 0x01 */
-#define IRCC_INTERFACE 0x04 /* low 4 = DMA, high 4 = IRQ */
-#define IRCC_INTERFACE_DMA_MASK 0x0F /* low 4 = DMA, high 4 = IRQ */
-#define IRCC_INTERFACE_IRQ_MASK 0xF0 /* low 4 = DMA, high 4 = IRQ */
-
-/* Register block 4 - IrDA */
-#define IRCC_CONTROL 0x00
-#define IRCC_BOF_COUNT_LO 0x01 /* Low byte */
-#define IRCC_BOF_COUNT_HI 0x00 /* High nibble (bit 0-3) */
-#define IRCC_BRICKWALL_CNT_LO 0x02 /* Low byte */
-#define IRCC_BRICKWALL_CNT_HI 0x03 /* High nibble (bit 4-7) */
-#define IRCC_TX_SIZE_LO 0x04 /* Low byte */
-#define IRCC_TX_SIZE_HI 0x03 /* High nibble (bit 0-3) */
-#define IRCC_RX_SIZE_HI 0x05 /* High nibble (bit 0-3) */
-#define IRCC_RX_SIZE_LO 0x06 /* Low byte */
-
-#define IRCC_1152 0x80
-#define IRCC_CRC 0x40
-
-/* Register block 5 - IrDA */
-#define IRCC_ATC 0x00
-#define IRCC_ATC_nPROGREADY 0x80
-#define IRCC_ATC_SPEED 0x40
-#define IRCC_ATC_ENABLE 0x20
-#define IRCC_ATC_MASK 0xE0
-
-
-#define IRCC_IRHALFDUPLEX_TIMEOUT 0x01
-
-#define IRCC_SCE_TX_DELAY_TIMER 0x02
-
-/*
- * Other definitions
- */
-
-#define SMSC_IRCC2_MAX_SIR_SPEED 115200
-#define SMSC_IRCC2_FIR_CHIP_IO_EXTENT 8
-#define SMSC_IRCC2_SIR_CHIP_IO_EXTENT 8
-#define SMSC_IRCC2_FIFO_SIZE 16
-#define SMSC_IRCC2_FIFO_THRESHOLD 64
-/* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */
-#define SMSC_IRCC2_RX_BUFF_TRUESIZE 14384
-#define SMSC_IRCC2_TX_BUFF_TRUESIZE 14384
-#define SMSC_IRCC2_MIN_TURN_TIME 0x07
-#define SMSC_IRCC2_WINDOW_SIZE 0x07
-/* Maximum wait for hw transmitter to finish */
-#define SMSC_IRCC2_HW_TRANSMITTER_TIMEOUT_US 1000 /* 1 ms */
-/* Maximum wait for ATC transceiver programming to finish */
-#define SMSC_IRCC2_ATC_PROGRAMMING_TIMEOUT_JIFFIES 1
-#endif /* SMSC_IRCC2_H */
diff --git a/drivers/staging/irda/drivers/smsc-sio.h b/drivers/staging/irda/drivers/smsc-sio.h
deleted file mode 100644
index 59e20e653ebe..000000000000
--- a/drivers/staging/irda/drivers/smsc-sio.h
+++ /dev/null
@@ -1,100 +0,0 @@
-#ifndef SMSC_SIO_H
-#define SMSC_SIO_H
-
-/******************************************
- Keys. They should work with every SMsC SIO
- ******************************************/
-
-#define SMSCSIO_CFGACCESSKEY 0x55
-#define SMSCSIO_CFGEXITKEY 0xaa
-
-/*****************************
- * Generic SIO Flat (!?) *
- *****************************/
-
-/* Register 0x0d */
-#define SMSCSIOFLAT_DEVICEID_REG 0x0d
-
-/* Register 0x0c */
-#define SMSCSIOFLAT_UARTMODE0C_REG 0x0c
-#define SMSCSIOFLAT_UART2MODE_MASK 0x38
-#define SMSCSIOFLAT_UART2MODE_VAL_COM 0x00
-#define SMSCSIOFLAT_UART2MODE_VAL_IRDA 0x08
-#define SMSCSIOFLAT_UART2MODE_VAL_ASKIR 0x10
-
-/* Register 0x25 */
-#define SMSCSIOFLAT_UART2BASEADDR_REG 0x25
-
-/* Register 0x2b */
-#define SMSCSIOFLAT_FIRBASEADDR_REG 0x2b
-
-/* Register 0x2c */
-#define SMSCSIOFLAT_FIRDMASELECT_REG 0x2c
-#define SMSCSIOFLAT_FIRDMASELECT_MASK 0x0f
-
-/* Register 0x28 */
-#define SMSCSIOFLAT_UARTIRQSELECT_REG 0x28
-#define SMSCSIOFLAT_UART2IRQSELECT_MASK 0x0f
-#define SMSCSIOFLAT_UART1IRQSELECT_MASK 0xf0
-#define SMSCSIOFLAT_UARTIRQSELECT_VAL_NONE 0x00
-
-
-/*********************
- * LPC47N227 *
- *********************/
-
-#define LPC47N227_CFGACCESSKEY 0x55
-#define LPC47N227_CFGEXITKEY 0xaa
-
-/* Register 0x00 */
-#define LPC47N227_FDCPOWERVALIDCONF_REG 0x00
-#define LPC47N227_FDCPOWER_MASK 0x08
-#define LPC47N227_VALID_MASK 0x80
-
-/* Register 0x02 */
-#define LPC47N227_UART12POWER_REG 0x02
-#define LPC47N227_UART1POWERDOWN_MASK 0x08
-#define LPC47N227_UART2POWERDOWN_MASK 0x80
-
-/* Register 0x07 */
-#define LPC47N227_APMBOOTDRIVE_REG 0x07
-#define LPC47N227_PARPORT2AUTOPWRDOWN_MASK 0x10 /* auto power down on if set */
-#define LPC47N227_UART2AUTOPWRDOWN_MASK 0x20 /* auto power down on if set */
-#define LPC47N227_UART1AUTOPWRDOWN_MASK 0x40 /* auto power down on if set */
-
-/* Register 0x0c */
-#define LPC47N227_UARTMODE0C_REG 0x0c
-#define LPC47N227_UART2MODE_MASK 0x38
-#define LPC47N227_UART2MODE_VAL_COM 0x00
-#define LPC47N227_UART2MODE_VAL_IRDA 0x08
-#define LPC47N227_UART2MODE_VAL_ASKIR 0x10
-
-/* Register 0x0d */
-#define LPC47N227_DEVICEID_REG 0x0d
-#define LPC47N227_DEVICEID_DEFVAL 0x5a
-
-/* Register 0x0e */
-#define LPC47N227_REVISIONID_REG 0x0e
-
-/* Register 0x25 */
-#define LPC47N227_UART2BASEADDR_REG 0x25
-
-/* Register 0x28 */
-#define LPC47N227_UARTIRQSELECT_REG 0x28
-#define LPC47N227_UART2IRQSELECT_MASK 0x0f
-#define LPC47N227_UART1IRQSELECT_MASK 0xf0
-#define LPC47N227_UARTIRQSELECT_VAL_NONE 0x00
-
-/* Register 0x2b */
-#define LPC47N227_FIRBASEADDR_REG 0x2b
-
-/* Register 0x2c */
-#define LPC47N227_FIRDMASELECT_REG 0x2c
-#define LPC47N227_FIRDMASELECT_MASK 0x0f
-#define LPC47N227_FIRDMASELECT_VAL_DMA1 0x01 /* 47n227 has three dma channels */
-#define LPC47N227_FIRDMASELECT_VAL_DMA2 0x02
-#define LPC47N227_FIRDMASELECT_VAL_DMA3 0x03
-#define LPC47N227_FIRDMASELECT_VAL_NONE 0x0f
-
-
-#endif
diff --git a/drivers/staging/irda/drivers/stir4200.c b/drivers/staging/irda/drivers/stir4200.c
deleted file mode 100644
index ee2cb70b688d..000000000000
--- a/drivers/staging/irda/drivers/stir4200.c
+++ /dev/null
@@ -1,1134 +0,0 @@
-/*****************************************************************************
-*
-* Filename: stir4200.c
-* Version: 0.4
-* Description: Irda SigmaTel USB Dongle
-* Status: Experimental
-* Author: Stephen Hemminger <shemminger@osdl.org>
-*
-* Based on earlier driver by Paul Stewart <stewart@parc.com>
-*
-* Copyright (C) 2000, Roman Weissgaerber <weissg@vienna.at>
-* Copyright (C) 2001, Dag Brattli <dag@brattli.net>
-* Copyright (C) 2001, Jean Tourrilhes <jt@hpl.hp.com>
-* Copyright (C) 2004, Stephen Hemminger <shemminger@osdl.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.
-*
-* 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.
-*
-*****************************************************************************/
-
-/*
- * This dongle does no framing, and requires polling to receive the
- * data. The STIr4200 has bulk in and out endpoints just like
- * usr-irda devices, but the data it sends and receives is raw; like
- * irtty, it needs to call the wrap and unwrap functions to add and
- * remove SOF/BOF and escape characters to/from the frame.
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-
-#include <linux/kernel.h>
-#include <linux/sched/signal.h>
-#include <linux/ktime.h>
-#include <linux/types.h>
-#include <linux/time.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/usb.h>
-#include <linux/crc32.h>
-#include <linux/kthread.h>
-#include <linux/freezer.h>
-#include <net/irda/irda.h>
-#include <net/irda/irda_device.h>
-#include <net/irda/wrapper.h>
-#include <net/irda/crc.h>
-#include <asm/byteorder.h>
-#include <asm/unaligned.h>
-
-MODULE_AUTHOR("Stephen Hemminger <shemminger@linux-foundation.org>");
-MODULE_DESCRIPTION("IrDA-USB Dongle Driver for SigmaTel STIr4200");
-MODULE_LICENSE("GPL");
-
-static int qos_mtt_bits = 0x07; /* 1 ms or more */
-module_param(qos_mtt_bits, int, 0);
-MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time");
-
-static int rx_sensitivity = 1; /* FIR 0..4, SIR 0..6 */
-module_param(rx_sensitivity, int, 0);
-MODULE_PARM_DESC(rx_sensitivity, "Set Receiver sensitivity (0-6, 0 is most sensitive)");
-
-static int tx_power = 0; /* 0 = highest ... 3 = lowest */
-module_param(tx_power, int, 0);
-MODULE_PARM_DESC(tx_power, "Set Transmitter power (0-3, 0 is highest power)");
-
-#define STIR_IRDA_HEADER 4
-#define CTRL_TIMEOUT 100 /* milliseconds */
-#define TRANSMIT_TIMEOUT 200 /* milliseconds */
-#define STIR_FIFO_SIZE 4096
-#define FIFO_REGS_SIZE 3
-
-enum FirChars {
- FIR_CE = 0x7d,
- FIR_XBOF = 0x7f,
- FIR_EOF = 0x7e,
-};
-
-enum StirRequests {
- REQ_WRITE_REG = 0x00,
- REQ_READ_REG = 0x01,
- REQ_READ_ROM = 0x02,
- REQ_WRITE_SINGLE = 0x03,
-};
-
-/* Register offsets */
-enum StirRegs {
- REG_RSVD=0,
- REG_MODE,
- REG_PDCLK,
- REG_CTRL1,
- REG_CTRL2,
- REG_FIFOCTL,
- REG_FIFOLSB,
- REG_FIFOMSB,
- REG_DPLL,
- REG_IRDIG,
- REG_TEST=15,
-};
-
-enum StirModeMask {
- MODE_FIR = 0x80,
- MODE_SIR = 0x20,
- MODE_ASK = 0x10,
- MODE_FASTRX = 0x08,
- MODE_FFRSTEN = 0x04,
- MODE_NRESET = 0x02,
- MODE_2400 = 0x01,
-};
-
-enum StirPdclkMask {
- PDCLK_4000000 = 0x02,
- PDCLK_115200 = 0x09,
- PDCLK_57600 = 0x13,
- PDCLK_38400 = 0x1D,
- PDCLK_19200 = 0x3B,
- PDCLK_9600 = 0x77,
- PDCLK_2400 = 0xDF,
-};
-
-enum StirCtrl1Mask {
- CTRL1_SDMODE = 0x80,
- CTRL1_RXSLOW = 0x40,
- CTRL1_TXPWD = 0x10,
- CTRL1_RXPWD = 0x08,
- CTRL1_SRESET = 0x01,
-};
-
-enum StirCtrl2Mask {
- CTRL2_SPWIDTH = 0x08,
- CTRL2_REVID = 0x03,
-};
-
-enum StirFifoCtlMask {
- FIFOCTL_DIR = 0x10,
- FIFOCTL_CLR = 0x08,
- FIFOCTL_EMPTY = 0x04,
-};
-
-enum StirDiagMask {
- IRDIG_RXHIGH = 0x80,
- IRDIG_RXLOW = 0x40,
-};
-
-enum StirTestMask {
- TEST_PLLDOWN = 0x80,
- TEST_LOOPIR = 0x40,
- TEST_LOOPUSB = 0x20,
- TEST_TSTENA = 0x10,
- TEST_TSTOSC = 0x0F,
-};
-
-struct stir_cb {
- struct usb_device *usbdev; /* init: probe_irda */
- struct net_device *netdev; /* network layer */
- struct irlap_cb *irlap; /* The link layer we are binded to */
-
- struct qos_info qos;
- unsigned speed; /* Current speed */
-
- struct task_struct *thread; /* transmit thread */
-
- struct sk_buff *tx_pending;
- void *io_buf; /* transmit/receive buffer */
- __u8 *fifo_status;
-
- iobuff_t rx_buff; /* receive unwrap state machine */
- ktime_t rx_time;
- int receiving;
- struct urb *rx_urb;
-};
-
-
-/* These are the currently known USB ids */
-static const struct usb_device_id dongles[] = {
- /* SigmaTel, Inc, STIr4200 IrDA/USB Bridge */
- { USB_DEVICE(0x066f, 0x4200) },
- { }
-};
-
-MODULE_DEVICE_TABLE(usb, dongles);
-
-/* Send control message to set dongle register */
-static int write_reg(struct stir_cb *stir, __u16 reg, __u8 value)
-{
- struct usb_device *dev = stir->usbdev;
-
- pr_debug("%s: write reg %d = 0x%x\n",
- stir->netdev->name, reg, value);
- return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- REQ_WRITE_SINGLE,
- USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE,
- value, reg, NULL, 0,
- CTRL_TIMEOUT);
-}
-
-/* Send control message to read multiple registers */
-static inline int read_reg(struct stir_cb *stir, __u16 reg,
- __u8 *data, __u16 count)
-{
- struct usb_device *dev = stir->usbdev;
-
- return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
- REQ_READ_REG,
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0, reg, data, count,
- CTRL_TIMEOUT);
-}
-
-static inline int isfir(u32 speed)
-{
- return speed == 4000000;
-}
-
-/*
- * Prepare a FIR IrDA frame for transmission to the USB dongle. The
- * FIR transmit frame is documented in the datasheet. It consists of
- * a two byte 0x55 0xAA sequence, two little-endian length bytes, a
- * sequence of exactly 16 XBOF bytes of 0x7E, two BOF bytes of 0x7E,
- * then the data escaped as follows:
- *
- * 0x7D -> 0x7D 0x5D
- * 0x7E -> 0x7D 0x5E
- * 0x7F -> 0x7D 0x5F
- *
- * Then, 4 bytes of little endian (stuffed) FCS follow, then two
- * trailing EOF bytes of 0x7E.
- */
-static inline __u8 *stuff_fir(__u8 *p, __u8 c)
-{
- switch(c) {
- case 0x7d:
- case 0x7e:
- case 0x7f:
- *p++ = 0x7d;
- c ^= IRDA_TRANS;
- /* fall through */
- default:
- *p++ = c;
- }
- return p;
-}
-
-/* Take raw data in skb and put it wrapped into buf */
-static unsigned wrap_fir_skb(const struct sk_buff *skb, __u8 *buf)
-{
- __u8 *ptr = buf;
- __u32 fcs = ~(crc32_le(~0, skb->data, skb->len));
- __u16 wraplen;
- int i;
-
- /* Header */
- buf[0] = 0x55;
- buf[1] = 0xAA;
-
- ptr = buf + STIR_IRDA_HEADER;
- memset(ptr, 0x7f, 16);
- ptr += 16;
-
- /* BOF */
- *ptr++ = 0x7e;
- *ptr++ = 0x7e;
-
- /* Address / Control / Information */
- for (i = 0; i < skb->len; i++)
- ptr = stuff_fir(ptr, skb->data[i]);
-
- /* FCS */
- ptr = stuff_fir(ptr, fcs & 0xff);
- ptr = stuff_fir(ptr, (fcs >> 8) & 0xff);
- ptr = stuff_fir(ptr, (fcs >> 16) & 0xff);
- ptr = stuff_fir(ptr, (fcs >> 24) & 0xff);
-
- /* EOFs */
- *ptr++ = 0x7e;
- *ptr++ = 0x7e;
-
- /* Total length, minus the header */
- wraplen = (ptr - buf) - STIR_IRDA_HEADER;
- buf[2] = wraplen & 0xff;
- buf[3] = (wraplen >> 8) & 0xff;
-
- return wraplen + STIR_IRDA_HEADER;
-}
-
-static unsigned wrap_sir_skb(struct sk_buff *skb, __u8 *buf)
-{
- __u16 wraplen;
-
- wraplen = async_wrap_skb(skb, buf + STIR_IRDA_HEADER,
- STIR_FIFO_SIZE - STIR_IRDA_HEADER);
- buf[0] = 0x55;
- buf[1] = 0xAA;
- buf[2] = wraplen & 0xff;
- buf[3] = (wraplen >> 8) & 0xff;
-
- return wraplen + STIR_IRDA_HEADER;
-}
-
-/*
- * Frame is fully formed in the rx_buff so check crc
- * and pass up to irlap
- * setup for next receive
- */
-static void fir_eof(struct stir_cb *stir)
-{
- iobuff_t *rx_buff = &stir->rx_buff;
- int len = rx_buff->len - 4;
- struct sk_buff *skb, *nskb;
- __u32 fcs;
-
- if (unlikely(len <= 0)) {
- pr_debug("%s: short frame len %d\n",
- stir->netdev->name, len);
-
- ++stir->netdev->stats.rx_errors;
- ++stir->netdev->stats.rx_length_errors;
- return;
- }
-
- fcs = ~(crc32_le(~0, rx_buff->data, len));
- if (fcs != get_unaligned_le32(rx_buff->data + len)) {
- pr_debug("crc error calc 0x%x len %d\n", fcs, len);
- stir->netdev->stats.rx_errors++;
- stir->netdev->stats.rx_crc_errors++;
- return;
- }
-
- /* if frame is short then just copy it */
- if (len < IRDA_RX_COPY_THRESHOLD) {
- nskb = dev_alloc_skb(len + 1);
- if (unlikely(!nskb)) {
- ++stir->netdev->stats.rx_dropped;
- return;
- }
- skb_reserve(nskb, 1);
- skb = nskb;
- skb_copy_to_linear_data(nskb, rx_buff->data, len);
- } else {
- nskb = dev_alloc_skb(rx_buff->truesize);
- if (unlikely(!nskb)) {
- ++stir->netdev->stats.rx_dropped;
- return;
- }
- skb_reserve(nskb, 1);
- skb = rx_buff->skb;
- rx_buff->skb = nskb;
- rx_buff->head = nskb->data;
- }
-
- skb_put(skb, len);
-
- skb_reset_mac_header(skb);
- skb->protocol = htons(ETH_P_IRDA);
- skb->dev = stir->netdev;
-
- netif_rx(skb);
-
- stir->netdev->stats.rx_packets++;
- stir->netdev->stats.rx_bytes += len;
-
- rx_buff->data = rx_buff->head;
- rx_buff->len = 0;
-}
-
-/* Unwrap FIR stuffed data and bump it to IrLAP */
-static void stir_fir_chars(struct stir_cb *stir,
- const __u8 *bytes, int len)
-{
- iobuff_t *rx_buff = &stir->rx_buff;
- int i;
-
- for (i = 0; i < len; i++) {
- __u8 byte = bytes[i];
-
- switch(rx_buff->state) {
- case OUTSIDE_FRAME:
- /* ignore garbage till start of frame */
- if (unlikely(byte != FIR_EOF))
- continue;
- /* Now receiving frame */
- rx_buff->state = BEGIN_FRAME;
-
- /* Time to initialize receive buffer */
- rx_buff->data = rx_buff->head;
- rx_buff->len = 0;
- continue;
-
- case LINK_ESCAPE:
- if (byte == FIR_EOF) {
- pr_debug("%s: got EOF after escape\n",
- stir->netdev->name);
- goto frame_error;
- }
- rx_buff->state = INSIDE_FRAME;
- byte ^= IRDA_TRANS;
- break;
-
- case BEGIN_FRAME:
- /* ignore multiple BOF/EOF */
- if (byte == FIR_EOF)
- continue;
- rx_buff->state = INSIDE_FRAME;
- rx_buff->in_frame = TRUE;
-
- /* fall through */
- case INSIDE_FRAME:
- switch(byte) {
- case FIR_CE:
- rx_buff->state = LINK_ESCAPE;
- continue;
- case FIR_XBOF:
- /* 0x7f is not used in this framing */
- pr_debug("%s: got XBOF without escape\n",
- stir->netdev->name);
- goto frame_error;
- case FIR_EOF:
- rx_buff->state = OUTSIDE_FRAME;
- rx_buff->in_frame = FALSE;
- fir_eof(stir);
- continue;
- }
- break;
- }
-
- /* add byte to rx buffer */
- if (unlikely(rx_buff->len >= rx_buff->truesize)) {
- pr_debug("%s: fir frame exceeds %d\n",
- stir->netdev->name, rx_buff->truesize);
- ++stir->netdev->stats.rx_over_errors;
- goto error_recovery;
- }
-
- rx_buff->data[rx_buff->len++] = byte;
- continue;
-
- frame_error:
- ++stir->netdev->stats.rx_frame_errors;
-
- error_recovery:
- ++stir->netdev->stats.rx_errors;
- rx_buff->state = OUTSIDE_FRAME;
- rx_buff->in_frame = FALSE;
- }
-}
-
-/* Unwrap SIR stuffed data and bump it up to IrLAP */
-static void stir_sir_chars(struct stir_cb *stir,
- const __u8 *bytes, int len)
-{
- int i;
-
- for (i = 0; i < len; i++)
- async_unwrap_char(stir->netdev, &stir->netdev->stats,
- &stir->rx_buff, bytes[i]);
-}
-
-static inline void unwrap_chars(struct stir_cb *stir,
- const __u8 *bytes, int length)
-{
- if (isfir(stir->speed))
- stir_fir_chars(stir, bytes, length);
- else
- stir_sir_chars(stir, bytes, length);
-}
-
-/* Mode parameters for each speed */
-static const struct {
- unsigned speed;
- __u8 pdclk;
-} stir_modes[] = {
- { 2400, PDCLK_2400 },
- { 9600, PDCLK_9600 },
- { 19200, PDCLK_19200 },
- { 38400, PDCLK_38400 },
- { 57600, PDCLK_57600 },
- { 115200, PDCLK_115200 },
- { 4000000, PDCLK_4000000 },
-};
-
-
-/*
- * Setup chip for speed.
- * Called at startup to initialize the chip
- * and on speed changes.
- *
- * Note: Write multiple registers doesn't appear to work
- */
-static int change_speed(struct stir_cb *stir, unsigned speed)
-{
- int i, err;
- __u8 mode;
-
- for (i = 0; i < ARRAY_SIZE(stir_modes); ++i) {
- if (speed == stir_modes[i].speed)
- goto found;
- }
-
- dev_warn(&stir->netdev->dev, "invalid speed %d\n", speed);
- return -EINVAL;
-
- found:
- pr_debug("speed change from %d to %d\n", stir->speed, speed);
-
- /* Reset modulator */
- err = write_reg(stir, REG_CTRL1, CTRL1_SRESET);
- if (err)
- goto out;
-
- /* Undocumented magic to tweak the DPLL */
- err = write_reg(stir, REG_DPLL, 0x15);
- if (err)
- goto out;
-
- /* Set clock */
- err = write_reg(stir, REG_PDCLK, stir_modes[i].pdclk);
- if (err)
- goto out;
-
- mode = MODE_NRESET | MODE_FASTRX;
- if (isfir(speed))
- mode |= MODE_FIR | MODE_FFRSTEN;
- else
- mode |= MODE_SIR;
-
- if (speed == 2400)
- mode |= MODE_2400;
-
- err = write_reg(stir, REG_MODE, mode);
- if (err)
- goto out;
-
- /* This resets TEMIC style transceiver if any. */
- err = write_reg(stir, REG_CTRL1,
- CTRL1_SDMODE | (tx_power & 3) << 1);
- if (err)
- goto out;
-
- err = write_reg(stir, REG_CTRL1, (tx_power & 3) << 1);
- if (err)
- goto out;
-
- /* Reset sensitivity */
- err = write_reg(stir, REG_CTRL2, (rx_sensitivity & 7) << 5);
- out:
- stir->speed = speed;
- return err;
-}
-
-/*
- * Called from net/core when new frame is available.
- */
-static netdev_tx_t stir_hard_xmit(struct sk_buff *skb,
- struct net_device *netdev)
-{
- struct stir_cb *stir = netdev_priv(netdev);
-
- netif_stop_queue(netdev);
-
- /* the IRDA wrapping routines don't deal with non linear skb */
- SKB_LINEAR_ASSERT(skb);
-
- skb = xchg(&stir->tx_pending, skb);
- wake_up_process(stir->thread);
-
- /* this should never happen unless stop/wakeup problem */
- if (unlikely(skb)) {
- WARN_ON(1);
- dev_kfree_skb(skb);
- }
-
- return NETDEV_TX_OK;
-}
-
-/*
- * Wait for the transmit FIFO to have space for next data
- *
- * If space < 0 then wait till FIFO completely drains.
- * FYI: can take up to 13 seconds at 2400baud.
- */
-static int fifo_txwait(struct stir_cb *stir, int space)
-{
- int err;
- unsigned long count, status;
- unsigned long prev_count = 0x1fff;
-
- /* Read FIFO status and count */
- for (;; prev_count = count) {
- err = read_reg(stir, REG_FIFOCTL, stir->fifo_status,
- FIFO_REGS_SIZE);
- if (unlikely(err != FIFO_REGS_SIZE)) {
- dev_warn(&stir->netdev->dev,
- "FIFO register read error: %d\n", err);
-
- return err;
- }
-
- status = stir->fifo_status[0];
- count = (unsigned)(stir->fifo_status[2] & 0x1f) << 8
- | stir->fifo_status[1];
-
- pr_debug("fifo status 0x%lx count %lu\n", status, count);
-
- /* is fifo receiving already, or empty */
- if (!(status & FIFOCTL_DIR) ||
- (status & FIFOCTL_EMPTY))
- return 0;
-
- if (signal_pending(current))
- return -EINTR;
-
- /* shutting down? */
- if (!netif_running(stir->netdev) ||
- !netif_device_present(stir->netdev))
- return -ESHUTDOWN;
-
- /* only waiting for some space */
- if (space >= 0 && STIR_FIFO_SIZE - 4 > space + count)
- return 0;
-
- /* queue confused */
- if (prev_count < count)
- break;
-
- /* estimate transfer time for remaining chars */
- msleep((count * 8000) / stir->speed);
- }
-
- err = write_reg(stir, REG_FIFOCTL, FIFOCTL_CLR);
- if (err)
- return err;
- err = write_reg(stir, REG_FIFOCTL, 0);
- if (err)
- return err;
-
- return 0;
-}
-
-
-/* Wait for turnaround delay before starting transmit. */
-static void turnaround_delay(const struct stir_cb *stir, long us)
-{
- long ticks;
-
- if (us <= 0)
- return;
-
- us -= ktime_us_delta(ktime_get(), stir->rx_time);
-
- if (us < 10)
- return;
-
- ticks = us / (1000000 / HZ);
- if (ticks > 0)
- schedule_timeout_interruptible(1 + ticks);
- else
- udelay(us);
-}
-
-/*
- * Start receiver by submitting a request to the receive pipe.
- * If nothing is available it will return after rx_interval.
- */
-static int receive_start(struct stir_cb *stir)
-{
- /* reset state */
- stir->receiving = 1;
-
- stir->rx_buff.in_frame = FALSE;
- stir->rx_buff.state = OUTSIDE_FRAME;
-
- stir->rx_urb->status = 0;
- return usb_submit_urb(stir->rx_urb, GFP_KERNEL);
-}
-
-/* Stop all pending receive Urb's */
-static void receive_stop(struct stir_cb *stir)
-{
- stir->receiving = 0;
- usb_kill_urb(stir->rx_urb);
-
- if (stir->rx_buff.in_frame)
- stir->netdev->stats.collisions++;
-}
-/*
- * Wrap data in socket buffer and send it.
- */
-static void stir_send(struct stir_cb *stir, struct sk_buff *skb)
-{
- unsigned wraplen;
- int first_frame = 0;
-
- /* if receiving, need to turnaround */
- if (stir->receiving) {
- receive_stop(stir);
- turnaround_delay(stir, irda_get_mtt(skb));
- first_frame = 1;
- }
-
- if (isfir(stir->speed))
- wraplen = wrap_fir_skb(skb, stir->io_buf);
- else
- wraplen = wrap_sir_skb(skb, stir->io_buf);
-
- /* check for space available in fifo */
- if (!first_frame)
- fifo_txwait(stir, wraplen);
-
- stir->netdev->stats.tx_packets++;
- stir->netdev->stats.tx_bytes += skb->len;
- netif_trans_update(stir->netdev);
- pr_debug("send %d (%d)\n", skb->len, wraplen);
-
- if (usb_bulk_msg(stir->usbdev, usb_sndbulkpipe(stir->usbdev, 1),
- stir->io_buf, wraplen,
- NULL, TRANSMIT_TIMEOUT))
- stir->netdev->stats.tx_errors++;
-}
-
-/*
- * Transmit state machine thread
- */
-static int stir_transmit_thread(void *arg)
-{
- struct stir_cb *stir = arg;
- struct net_device *dev = stir->netdev;
- struct sk_buff *skb;
-
- while (!kthread_should_stop()) {
-#ifdef CONFIG_PM
- /* if suspending, then power off and wait */
- if (unlikely(freezing(current))) {
- if (stir->receiving)
- receive_stop(stir);
- else
- fifo_txwait(stir, -1);
-
- write_reg(stir, REG_CTRL1, CTRL1_TXPWD|CTRL1_RXPWD);
-
- try_to_freeze();
-
- if (change_speed(stir, stir->speed))
- break;
- }
-#endif
-
- /* if something to send? */
- skb = xchg(&stir->tx_pending, NULL);
- if (skb) {
- unsigned new_speed = irda_get_next_speed(skb);
- netif_wake_queue(dev);
-
- if (skb->len > 0)
- stir_send(stir, skb);
- dev_kfree_skb(skb);
-
- if ((new_speed != -1) && (stir->speed != new_speed)) {
- if (fifo_txwait(stir, -1) ||
- change_speed(stir, new_speed))
- break;
- }
- continue;
- }
-
- /* nothing to send? start receiving */
- if (!stir->receiving &&
- irda_device_txqueue_empty(dev)) {
- /* Wait otherwise chip gets confused. */
- if (fifo_txwait(stir, -1))
- break;
-
- if (unlikely(receive_start(stir))) {
- if (net_ratelimit())
- dev_info(&dev->dev,
- "%s: receive usb submit failed\n",
- stir->netdev->name);
- stir->receiving = 0;
- msleep(10);
- continue;
- }
- }
-
- /* sleep if nothing to send */
- set_current_state(TASK_INTERRUPTIBLE);
- schedule();
-
- }
- return 0;
-}
-
-
-/*
- * USB bulk receive completion callback.
- * Wakes up every ms (usb round trip) with wrapped
- * data.
- */
-static void stir_rcv_irq(struct urb *urb)
-{
- struct stir_cb *stir = urb->context;
- int err;
-
- /* in process of stopping, just drop data */
- if (!netif_running(stir->netdev))
- return;
-
- /* unlink, shutdown, unplug, other nasties */
- if (urb->status != 0)
- return;
-
- if (urb->actual_length > 0) {
- pr_debug("receive %d\n", urb->actual_length);
- unwrap_chars(stir, urb->transfer_buffer,
- urb->actual_length);
-
- stir->rx_time = ktime_get();
- }
-
- /* kernel thread is stopping receiver don't resubmit */
- if (!stir->receiving)
- return;
-
- /* resubmit existing urb */
- err = usb_submit_urb(urb, GFP_ATOMIC);
-
- /* in case of error, the kernel thread will restart us */
- if (err) {
- dev_warn(&stir->netdev->dev, "usb receive submit error: %d\n",
- err);
- stir->receiving = 0;
- wake_up_process(stir->thread);
- }
-}
-
-/*
- * Function stir_net_open (dev)
- *
- * Network device is taken up. Usually this is done by "ifconfig irda0 up"
- */
-static int stir_net_open(struct net_device *netdev)
-{
- struct stir_cb *stir = netdev_priv(netdev);
- int err;
- char hwname[16];
-
- err = usb_clear_halt(stir->usbdev, usb_sndbulkpipe(stir->usbdev, 1));
- if (err)
- goto err_out1;
- err = usb_clear_halt(stir->usbdev, usb_rcvbulkpipe(stir->usbdev, 2));
- if (err)
- goto err_out1;
-
- err = change_speed(stir, 9600);
- if (err)
- goto err_out1;
-
- err = -ENOMEM;
-
- /* Initialize for SIR/FIR to copy data directly into skb. */
- stir->receiving = 0;
- stir->rx_buff.truesize = IRDA_SKB_MAX_MTU;
- stir->rx_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU);
- if (!stir->rx_buff.skb)
- goto err_out1;
-
- skb_reserve(stir->rx_buff.skb, 1);
- stir->rx_buff.head = stir->rx_buff.skb->data;
- stir->rx_time = ktime_get();
-
- stir->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!stir->rx_urb)
- goto err_out2;
-
- stir->io_buf = kmalloc(STIR_FIFO_SIZE, GFP_KERNEL);
- if (!stir->io_buf)
- goto err_out3;
-
- usb_fill_bulk_urb(stir->rx_urb, stir->usbdev,
- usb_rcvbulkpipe(stir->usbdev, 2),
- stir->io_buf, STIR_FIFO_SIZE,
- stir_rcv_irq, stir);
-
- stir->fifo_status = kmalloc(FIFO_REGS_SIZE, GFP_KERNEL);
- if (!stir->fifo_status)
- goto err_out4;
-
- /*
- * Now that everything should be initialized properly,
- * Open new IrLAP layer instance to take care of us...
- * Note : will send immediately a speed change...
- */
- sprintf(hwname, "usb#%d", stir->usbdev->devnum);
- stir->irlap = irlap_open(netdev, &stir->qos, hwname);
- if (!stir->irlap) {
- dev_err(&stir->usbdev->dev, "irlap_open failed\n");
- goto err_out5;
- }
-
- /** Start kernel thread for transmit. */
- stir->thread = kthread_run(stir_transmit_thread, stir,
- "%s", stir->netdev->name);
- if (IS_ERR(stir->thread)) {
- err = PTR_ERR(stir->thread);
- dev_err(&stir->usbdev->dev, "unable to start kernel thread\n");
- goto err_out6;
- }
-
- netif_start_queue(netdev);
-
- return 0;
-
- err_out6:
- irlap_close(stir->irlap);
- err_out5:
- kfree(stir->fifo_status);
- err_out4:
- kfree(stir->io_buf);
- err_out3:
- usb_free_urb(stir->rx_urb);
- err_out2:
- kfree_skb(stir->rx_buff.skb);
- err_out1:
- return err;
-}
-
-/*
- * Function stir_net_close (stir)
- *
- * Network device is taken down. Usually this is done by
- * "ifconfig irda0 down"
- */
-static int stir_net_close(struct net_device *netdev)
-{
- struct stir_cb *stir = netdev_priv(netdev);
-
- /* Stop transmit processing */
- netif_stop_queue(netdev);
-
- /* Kill transmit thread */
- kthread_stop(stir->thread);
- kfree(stir->fifo_status);
-
- /* Mop up receive urb's */
- usb_kill_urb(stir->rx_urb);
-
- kfree(stir->io_buf);
- usb_free_urb(stir->rx_urb);
- kfree_skb(stir->rx_buff.skb);
-
- /* Stop and remove instance of IrLAP */
- if (stir->irlap)
- irlap_close(stir->irlap);
-
- stir->irlap = NULL;
-
- return 0;
-}
-
-/*
- * IOCTLs : Extra out-of-band network commands...
- */
-static int stir_net_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
-{
- struct if_irda_req *irq = (struct if_irda_req *) rq;
- struct stir_cb *stir = netdev_priv(netdev);
- int ret = 0;
-
- switch (cmd) {
- case SIOCSBANDWIDTH: /* Set bandwidth */
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
-
- /* Check if the device is still there */
- if (netif_device_present(stir->netdev))
- ret = change_speed(stir, irq->ifr_baudrate);
- break;
-
- case SIOCSMEDIABUSY: /* Set media busy */
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
-
- /* Check if the IrDA stack is still there */
- if (netif_running(stir->netdev))
- irda_device_set_media_busy(stir->netdev, TRUE);
- break;
-
- case SIOCGRECEIVING:
- /* Only approximately true */
- irq->ifr_receiving = stir->receiving;
- break;
-
- default:
- ret = -EOPNOTSUPP;
- }
-
- return ret;
-}
-
-static const struct net_device_ops stir_netdev_ops = {
- .ndo_open = stir_net_open,
- .ndo_stop = stir_net_close,
- .ndo_start_xmit = stir_hard_xmit,
- .ndo_do_ioctl = stir_net_ioctl,
-};
-
-/*
- * This routine is called by the USB subsystem for each new device
- * in the system. We need to check if the device is ours, and in
- * this case start handling it.
- * Note : it might be worth protecting this function by a global
- * spinlock... Or not, because maybe USB already deal with that...
- */
-static int stir_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- struct usb_device *dev = interface_to_usbdev(intf);
- struct stir_cb *stir = NULL;
- struct net_device *net;
- int ret = -ENOMEM;
-
- /* Allocate network device container. */
- net = alloc_irdadev(sizeof(*stir));
- if(!net)
- goto err_out1;
-
- SET_NETDEV_DEV(net, &intf->dev);
- stir = netdev_priv(net);
- stir->netdev = net;
- stir->usbdev = dev;
-
- ret = usb_reset_configuration(dev);
- if (ret != 0) {
- dev_err(&intf->dev, "usb reset configuration failed\n");
- goto err_out2;
- }
-
- printk(KERN_INFO "SigmaTel STIr4200 IRDA/USB found at address %d, "
- "Vendor: %x, Product: %x\n",
- dev->devnum, le16_to_cpu(dev->descriptor.idVendor),
- le16_to_cpu(dev->descriptor.idProduct));
-
- /* Initialize QoS for this device */
- irda_init_max_qos_capabilies(&stir->qos);
-
- /* That's the Rx capability. */
- stir->qos.baud_rate.bits &= IR_2400 | IR_9600 | IR_19200 |
- IR_38400 | IR_57600 | IR_115200 |
- (IR_4000000 << 8);
- stir->qos.min_turn_time.bits &= qos_mtt_bits;
- irda_qos_bits_to_value(&stir->qos);
-
- /* Override the network functions we need to use */
- net->netdev_ops = &stir_netdev_ops;
-
- ret = register_netdev(net);
- if (ret != 0)
- goto err_out2;
-
- dev_info(&intf->dev, "IrDA: Registered SigmaTel device %s\n",
- net->name);
-
- usb_set_intfdata(intf, stir);
-
- return 0;
-
-err_out2:
- free_netdev(net);
-err_out1:
- return ret;
-}
-
-/*
- * The current device is removed, the USB layer tell us to shut it down...
- */
-static void stir_disconnect(struct usb_interface *intf)
-{
- struct stir_cb *stir = usb_get_intfdata(intf);
-
- if (!stir)
- return;
-
- unregister_netdev(stir->netdev);
- free_netdev(stir->netdev);
-
- usb_set_intfdata(intf, NULL);
-}
-
-#ifdef CONFIG_PM
-/* USB suspend, so power off the transmitter/receiver */
-static int stir_suspend(struct usb_interface *intf, pm_message_t message)
-{
- struct stir_cb *stir = usb_get_intfdata(intf);
-
- netif_device_detach(stir->netdev);
- return 0;
-}
-
-/* Coming out of suspend, so reset hardware */
-static int stir_resume(struct usb_interface *intf)
-{
- struct stir_cb *stir = usb_get_intfdata(intf);
-
- netif_device_attach(stir->netdev);
-
- /* receiver restarted when send thread wakes up */
- return 0;
-}
-#endif
-
-/*
- * USB device callbacks
- */
-static struct usb_driver irda_driver = {
- .name = "stir4200",
- .probe = stir_probe,
- .disconnect = stir_disconnect,
- .id_table = dongles,
-#ifdef CONFIG_PM
- .suspend = stir_suspend,
- .resume = stir_resume,
-#endif
-};
-
-module_usb_driver(irda_driver);
diff --git a/drivers/staging/irda/drivers/tekram-sir.c b/drivers/staging/irda/drivers/tekram-sir.c
deleted file mode 100644
index 9dcf0c103b9d..000000000000
--- a/drivers/staging/irda/drivers/tekram-sir.c
+++ /dev/null
@@ -1,225 +0,0 @@
-/*********************************************************************
- *
- * Filename: tekram.c
- * Version: 1.3
- * Description: Implementation of the Tekram IrMate IR-210B dongle
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Wed Oct 21 20:02:35 1998
- * Modified at: Sun Oct 27 22:02:38 2002
- * Modified by: Martin Diehl <mad@mdiehl.de>
- *
- * Copyright (c) 1998-1999 Dag Brattli,
- * Copyright (c) 2002 Martin Diehl,
- * All Rights Reserved.
- *
- * 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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-
-#include <net/irda/irda.h>
-
-#include "sir-dev.h"
-
-static int tekram_delay = 150; /* default is 150 ms */
-module_param(tekram_delay, int, 0);
-MODULE_PARM_DESC(tekram_delay, "tekram dongle write complete delay");
-
-static int tekram_open(struct sir_dev *);
-static int tekram_close(struct sir_dev *);
-static int tekram_change_speed(struct sir_dev *, unsigned);
-static int tekram_reset(struct sir_dev *);
-
-#define TEKRAM_115200 0x00
-#define TEKRAM_57600 0x01
-#define TEKRAM_38400 0x02
-#define TEKRAM_19200 0x03
-#define TEKRAM_9600 0x04
-
-#define TEKRAM_PW 0x10 /* Pulse select bit */
-
-static struct dongle_driver tekram = {
- .owner = THIS_MODULE,
- .driver_name = "Tekram IR-210B",
- .type = IRDA_TEKRAM_DONGLE,
- .open = tekram_open,
- .close = tekram_close,
- .reset = tekram_reset,
- .set_speed = tekram_change_speed,
-};
-
-static int __init tekram_sir_init(void)
-{
- if (tekram_delay < 1 || tekram_delay > 500)
- tekram_delay = 200;
- pr_debug("%s - using %d ms delay\n",
- tekram.driver_name, tekram_delay);
- return irda_register_dongle(&tekram);
-}
-
-static void __exit tekram_sir_cleanup(void)
-{
- irda_unregister_dongle(&tekram);
-}
-
-static int tekram_open(struct sir_dev *dev)
-{
- struct qos_info *qos = &dev->qos;
-
- sirdev_set_dtr_rts(dev, TRUE, TRUE);
-
- qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
- qos->min_turn_time.bits = 0x01; /* Needs at least 10 ms */
- irda_qos_bits_to_value(qos);
-
- /* irda thread waits 50 msec for power settling */
-
- return 0;
-}
-
-static int tekram_close(struct sir_dev *dev)
-{
- /* Power off dongle */
- sirdev_set_dtr_rts(dev, FALSE, FALSE);
-
- return 0;
-}
-
-/*
- * Function tekram_change_speed (dev, state, speed)
- *
- * Set the speed for the Tekram IRMate 210 type dongle. Warning, this
- * function must be called with a process context!
- *
- * Algorithm
- * 1. clear DTR
- * 2. set RTS, and wait at least 7 us
- * 3. send Control Byte to the IR-210 through TXD to set new baud rate
- * wait until the stop bit of Control Byte is sent (for 9600 baud rate,
- * it takes about 100 msec)
- *
- * [oops, why 100 msec? sending 1 byte (10 bits) takes 1.05 msec
- * - is this probably to compensate for delays in tty layer?]
- *
- * 5. clear RTS (return to NORMAL Operation)
- * 6. wait at least 50 us, new setting (baud rate, etc) takes effect here
- * after
- */
-
-#define TEKRAM_STATE_WAIT_SPEED (SIRDEV_STATE_DONGLE_SPEED + 1)
-
-static int tekram_change_speed(struct sir_dev *dev, unsigned speed)
-{
- unsigned state = dev->fsm.substate;
- unsigned delay = 0;
- u8 byte;
- static int ret = 0;
-
- switch(state) {
- case SIRDEV_STATE_DONGLE_SPEED:
-
- switch (speed) {
- default:
- speed = 9600;
- ret = -EINVAL;
- /* fall thru */
- case 9600:
- byte = TEKRAM_PW|TEKRAM_9600;
- break;
- case 19200:
- byte = TEKRAM_PW|TEKRAM_19200;
- break;
- case 38400:
- byte = TEKRAM_PW|TEKRAM_38400;
- break;
- case 57600:
- byte = TEKRAM_PW|TEKRAM_57600;
- break;
- case 115200:
- byte = TEKRAM_115200;
- break;
- }
-
- /* Set DTR, Clear RTS */
- sirdev_set_dtr_rts(dev, TRUE, FALSE);
-
- /* Wait at least 7us */
- udelay(14);
-
- /* Write control byte */
- sirdev_raw_write(dev, &byte, 1);
-
- dev->speed = speed;
-
- state = TEKRAM_STATE_WAIT_SPEED;
- delay = tekram_delay;
- break;
-
- case TEKRAM_STATE_WAIT_SPEED:
- /* Set DTR, Set RTS */
- sirdev_set_dtr_rts(dev, TRUE, TRUE);
- udelay(50);
- break;
-
- default:
- net_err_ratelimited("%s - undefined state %d\n",
- __func__, state);
- ret = -EINVAL;
- break;
- }
-
- dev->fsm.substate = state;
- return (delay > 0) ? delay : ret;
-}
-
-/*
- * Function tekram_reset (driver)
- *
- * This function resets the tekram dongle. Warning, this function
- * must be called with a process context!!
- *
- * Algorithm:
- * 0. Clear RTS and DTR, and wait 50 ms (power off the IR-210 )
- * 1. clear RTS
- * 2. set DTR, and wait at least 1 ms
- * 3. clear DTR to SPACE state, wait at least 50 us for further
- * operation
- */
-
-static int tekram_reset(struct sir_dev *dev)
-{
- /* Clear DTR, Set RTS */
- sirdev_set_dtr_rts(dev, FALSE, TRUE);
-
- /* Should sleep 1 ms */
- msleep(1);
-
- /* Set DTR, Set RTS */
- sirdev_set_dtr_rts(dev, TRUE, TRUE);
-
- /* Wait at least 50 us */
- udelay(75);
-
- dev->speed = 9600;
-
- return 0;
-}
-
-MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
-MODULE_DESCRIPTION("Tekram IrMate IR-210B dongle driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("irda-dongle-0"); /* IRDA_TEKRAM_DONGLE */
-
-module_init(tekram_sir_init);
-module_exit(tekram_sir_cleanup);
diff --git a/drivers/staging/irda/drivers/toim3232-sir.c b/drivers/staging/irda/drivers/toim3232-sir.c
deleted file mode 100644
index b977d6d33e74..000000000000
--- a/drivers/staging/irda/drivers/toim3232-sir.c
+++ /dev/null
@@ -1,358 +0,0 @@
-/*********************************************************************
- *
- * Filename: toim3232-sir.c
- * Version: 1.0
- * Description: Implementation of dongles based on the Vishay/Temic
- * TOIM3232 SIR Endec chipset. Currently only the
- * IRWave IR320ST-2 is tested, although it should work
- * with any TOIM3232 or TOIM4232 chipset based RS232
- * dongle with minimal modification.
- * Based heavily on the Tekram driver (tekram.c),
- * with thanks to Dag Brattli and Martin Diehl.
- * Status: Experimental.
- * Author: David Basden <davidb-irda@rcpt.to>
- * Created at: Thu Feb 09 23:47:32 2006
- *
- * Copyright (c) 2006 David Basden.
- * Copyright (c) 1998-1999 Dag Brattli,
- * Copyright (c) 2002 Martin Diehl,
- * All Rights Reserved.
- *
- * 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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-/*
- * This driver has currently only been tested on the IRWave IR320ST-2
- *
- * PROTOCOL:
- *
- * The protocol for talking to the TOIM3232 is quite easy, and is
- * designed to interface with RS232 with only level convertors. The
- * BR/~D line on the chip is brought high to signal 'command mode',
- * where a command byte is sent to select the baudrate of the RS232
- * interface and the pulse length of the IRDA output. When BR/~D
- * is brought low, the dongle then changes to the selected baudrate,
- * and the RS232 interface is used for data until BR/~D is brought
- * high again. The initial speed for the TOIMx323 after RESET is
- * 9600 baud. The baudrate for command-mode is the last selected
- * baud-rate, or 9600 after a RESET.
- *
- * The dongle I have (below) adds some extra hardware on the front end,
- * but this is mostly directed towards pariasitic power from the RS232
- * line rather than changing very much about how to communicate with
- * the TOIM3232.
- *
- * The protocol to talk to the TOIM4232 chipset seems to be almost
- * identical to the TOIM3232 (and the 4232 datasheet is more detailed)
- * so this code will probably work on that as well, although I haven't
- * tested it on that hardware.
- *
- * Target dongle variations that might be common:
- *
- * DTR and RTS function:
- * The data sheet for the 4232 has a sample implementation that hooks the
- * DTR and RTS lines to the RESET and BaudRate/~Data lines of the
- * chip (through line-converters). Given both DTR and RTS would have to
- * be held low in normal operation, and the TOIMx232 requires +5V to
- * signal ground, most dongle designers would almost certainly choose
- * an implementation that kept at least one of DTR or RTS high in
- * normal operation to provide power to the dongle, but will likely
- * vary between designs.
- *
- * User specified command bits:
- * There are two user-controllable output lines from the TOIMx232 that
- * can be set low or high by setting the appropriate bits in the
- * high-nibble of the command byte (when setting speed and pulse length).
- * These might be used to switch on and off added hardware or extra
- * dongle features.
- *
- *
- * Target hardware: IRWave IR320ST-2
- *
- * The IRWave IR320ST-2 is a simple dongle based on the Vishay/Temic
- * TOIM3232 SIR Endec and the Vishay/Temic TFDS4500 SIR IRDA transceiver.
- * It uses a hex inverter and some discrete components to buffer and
- * line convert the RS232 down to 5V.
- *
- * The dongle is powered through a voltage regulator, fed by a large
- * capacitor. To switch the dongle on, DTR is brought high to charge
- * the capacitor and drive the voltage regulator. DTR isn't associated
- * with any control lines on the TOIM3232. Parisitic power is also taken
- * from the RTS, TD and RD lines when brought high, but through resistors.
- * When DTR is low, the circuit might lose power even with RTS high.
- *
- * RTS is inverted and attached to the BR/~D input pin. When RTS
- * is high, BR/~D is low, and the TOIM3232 is in the normal 'data' mode.
- * RTS is brought low, BR/~D is high, and the TOIM3232 is in 'command
- * mode'.
- *
- * For some unknown reason, the RESET line isn't actually connected
- * to anything. This means to reset the dongle to get it to a known
- * state (9600 baud) you must drop DTR and RTS low, wait for the power
- * capacitor to discharge, and then bring DTR (and RTS for data mode)
- * high again, and wait for the capacitor to charge, the power supply
- * to stabilise, and the oscillator clock to stabilise.
- *
- * Fortunately, if the current baudrate is known, the chipset can
- * easily change speed by entering command mode without having to
- * reset the dongle first.
- *
- * Major Components:
- *
- * - Vishay/Temic TOIM3232 SIR Endec to change RS232 pulse timings
- * to IRDA pulse timings
- * - 3.6864MHz crystal to drive TOIM3232 clock oscillator
- * - DM74lS04M Inverting Hex line buffer for RS232 input buffering
- * and level conversion
- * - PJ2951AC 150mA voltage regulator
- * - Vishay/Temic TFDS4500 SIR IRDA front-end transceiver
- *
- */
-
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-
-#include <net/irda/irda.h>
-
-#include "sir-dev.h"
-
-static int toim3232delay = 150; /* default is 150 ms */
-module_param(toim3232delay, int, 0);
-MODULE_PARM_DESC(toim3232delay, "toim3232 dongle write complete delay");
-
-static int toim3232_open(struct sir_dev *);
-static int toim3232_close(struct sir_dev *);
-static int toim3232_change_speed(struct sir_dev *, unsigned);
-static int toim3232_reset(struct sir_dev *);
-
-#define TOIM3232_115200 0x00
-#define TOIM3232_57600 0x01
-#define TOIM3232_38400 0x02
-#define TOIM3232_19200 0x03
-#define TOIM3232_9600 0x06
-#define TOIM3232_2400 0x0A
-
-#define TOIM3232_PW 0x10 /* Pulse select bit */
-
-static struct dongle_driver toim3232 = {
- .owner = THIS_MODULE,
- .driver_name = "Vishay TOIM3232",
- .type = IRDA_TOIM3232_DONGLE,
- .open = toim3232_open,
- .close = toim3232_close,
- .reset = toim3232_reset,
- .set_speed = toim3232_change_speed,
-};
-
-static int __init toim3232_sir_init(void)
-{
- if (toim3232delay < 1 || toim3232delay > 500)
- toim3232delay = 200;
- pr_debug("%s - using %d ms delay\n",
- toim3232.driver_name, toim3232delay);
- return irda_register_dongle(&toim3232);
-}
-
-static void __exit toim3232_sir_cleanup(void)
-{
- irda_unregister_dongle(&toim3232);
-}
-
-static int toim3232_open(struct sir_dev *dev)
-{
- struct qos_info *qos = &dev->qos;
-
- /* Pull the lines high to start with.
- *
- * For the IR320ST-2, we need to charge the main supply capacitor to
- * switch the device on. We keep DTR high throughout to do this.
- * When RTS, TD and RD are high, they will also trickle-charge the
- * cap. RTS is high for data transmission, and low for baud rate select.
- * -- DGB
- */
- sirdev_set_dtr_rts(dev, TRUE, TRUE);
-
- /* The TOI3232 supports many speeds between 1200bps and 115000bps.
- * We really only care about those supported by the IRDA spec, but
- * 38400 seems to be implemented in many places */
- qos->baud_rate.bits &= IR_2400|IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
-
- /* From the tekram driver. Not sure what a reasonable value is -- DGB */
- qos->min_turn_time.bits = 0x01; /* Needs at least 10 ms */
- irda_qos_bits_to_value(qos);
-
- /* irda thread waits 50 msec for power settling */
-
- return 0;
-}
-
-static int toim3232_close(struct sir_dev *dev)
-{
- /* Power off dongle */
- sirdev_set_dtr_rts(dev, FALSE, FALSE);
-
- return 0;
-}
-
-/*
- * Function toim3232change_speed (dev, state, speed)
- *
- * Set the speed for the TOIM3232 based dongle. Warning, this
- * function must be called with a process context!
- *
- * Algorithm
- * 1. keep DTR high but clear RTS to bring into baud programming mode
- * 2. wait at least 7us to enter programming mode
- * 3. send control word to set baud rate and timing
- * 4. wait at least 1us
- * 5. bring RTS high to enter DATA mode (RS232 is passed through to transceiver)
- * 6. should take effect immediately (although probably worth waiting)
- */
-
-#define TOIM3232_STATE_WAIT_SPEED (SIRDEV_STATE_DONGLE_SPEED + 1)
-
-static int toim3232_change_speed(struct sir_dev *dev, unsigned speed)
-{
- unsigned state = dev->fsm.substate;
- unsigned delay = 0;
- u8 byte;
- static int ret = 0;
-
- switch(state) {
- case SIRDEV_STATE_DONGLE_SPEED:
-
- /* Figure out what we are going to send as a control byte */
- switch (speed) {
- case 2400:
- byte = TOIM3232_PW|TOIM3232_2400;
- break;
- default:
- speed = 9600;
- ret = -EINVAL;
- /* fall thru */
- case 9600:
- byte = TOIM3232_PW|TOIM3232_9600;
- break;
- case 19200:
- byte = TOIM3232_PW|TOIM3232_19200;
- break;
- case 38400:
- byte = TOIM3232_PW|TOIM3232_38400;
- break;
- case 57600:
- byte = TOIM3232_PW|TOIM3232_57600;
- break;
- case 115200:
- byte = TOIM3232_115200;
- break;
- }
-
- /* Set DTR, Clear RTS: Go into baud programming mode */
- sirdev_set_dtr_rts(dev, TRUE, FALSE);
-
- /* Wait at least 7us */
- udelay(14);
-
- /* Write control byte */
- sirdev_raw_write(dev, &byte, 1);
-
- dev->speed = speed;
-
- state = TOIM3232_STATE_WAIT_SPEED;
- delay = toim3232delay;
- break;
-
- case TOIM3232_STATE_WAIT_SPEED:
- /* Have transmitted control byte * Wait for 'at least 1us' */
- udelay(14);
-
- /* Set DTR, Set RTS: Go into normal data mode */
- sirdev_set_dtr_rts(dev, TRUE, TRUE);
-
- /* Wait (TODO: check this is needed) */
- udelay(50);
- break;
-
- default:
- printk(KERN_ERR "%s - undefined state %d\n", __func__, state);
- ret = -EINVAL;
- break;
- }
-
- dev->fsm.substate = state;
- return (delay > 0) ? delay : ret;
-}
-
-/*
- * Function toim3232reset (driver)
- *
- * This function resets the toim3232 dongle. Warning, this function
- * must be called with a process context!!
- *
- * What we should do is:
- * 0. Pull RESET high
- * 1. Wait for at least 7us
- * 2. Pull RESET low
- * 3. Wait for at least 7us
- * 4. Pull BR/~D high
- * 5. Wait for at least 7us
- * 6. Send control byte to set baud rate
- * 7. Wait at least 1us after stop bit
- * 8. Pull BR/~D low
- * 9. Should then be in data mode
- *
- * Because the IR320ST-2 doesn't have the RESET line connected for some reason,
- * we'll have to do something else.
- *
- * The default speed after a RESET is 9600, so lets try just bringing it up in
- * data mode after switching it off, waiting for the supply capacitor to
- * discharge, and then switch it back on. This isn't actually pulling RESET
- * high, but it seems to have the same effect.
- *
- * This behaviour will probably work on dongles that have the RESET line connected,
- * but if not, add a flag for the IR320ST-2, and implment the above-listed proper
- * behaviour.
- *
- * RTS is inverted and then fed to BR/~D, so to put it in programming mode, we
- * need to have pull RTS low
- */
-
-static int toim3232_reset(struct sir_dev *dev)
-{
- /* Switch off both DTR and RTS to switch off dongle */
- sirdev_set_dtr_rts(dev, FALSE, FALSE);
-
- /* Should sleep a while. This might be evil doing it this way.*/
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(msecs_to_jiffies(50));
-
- /* Set DTR, Set RTS (data mode) */
- sirdev_set_dtr_rts(dev, TRUE, TRUE);
-
- /* Wait at least 10 ms for power to stabilize again */
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(msecs_to_jiffies(10));
-
- /* Speed should now be 9600 */
- dev->speed = 9600;
-
- return 0;
-}
-
-MODULE_AUTHOR("David Basden <davidb-linux@rcpt.to>");
-MODULE_DESCRIPTION("Vishay/Temic TOIM3232 based dongle driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("irda-dongle-12"); /* IRDA_TOIM3232_DONGLE */
-
-module_init(toim3232_sir_init);
-module_exit(toim3232_sir_cleanup);
diff --git a/drivers/staging/irda/drivers/via-ircc.c b/drivers/staging/irda/drivers/via-ircc.c
deleted file mode 100644
index ca4442a9d631..000000000000
--- a/drivers/staging/irda/drivers/via-ircc.c
+++ /dev/null
@@ -1,1593 +0,0 @@
-/********************************************************************
- Filename: via-ircc.c
- Version: 1.0
- Description: Driver for the VIA VT8231/VT8233 IrDA chipsets
- Author: VIA Technologies,inc
- Date : 08/06/2003
-
-Copyright (c) 1998-2003 VIA Technologies, Inc.
-
-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 WARRANTIES OR REPRESENTATIONS; 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, see <http://www.gnu.org/licenses/>.
-
-F01 Oct/02/02: Modify code for V0.11(move out back to back transfer)
-F02 Oct/28/02: Add SB device ID for 3147 and 3177.
- Comment :
- jul/09/2002 : only implement two kind of dongle currently.
- Oct/02/2002 : work on VT8231 and VT8233 .
- Aug/06/2003 : change driver format to pci driver .
-
-2004-02-16: <sda@bdit.de>
-- Removed unneeded 'legacy' pci stuff.
-- Make sure SIR mode is set (hw_init()) before calling mode-dependent stuff.
-- On speed change from core, don't send SIR frame with new speed.
- Use current speed and change speeds later.
-- Make module-param dongle_id actually work.
-- New dongle_id 17 (0x11): TDFS4500. Single-ended SIR only.
- Tested with home-grown PCB on EPIA boards.
-- Code cleanup.
-
- ********************************************************************/
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/rtnetlink.h>
-#include <linux/pci.h>
-#include <linux/dma-mapping.h>
-#include <linux/gfp.h>
-
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <asm/byteorder.h>
-
-#include <linux/pm.h>
-
-#include <net/irda/wrapper.h>
-#include <net/irda/irda.h>
-#include <net/irda/irda_device.h>
-
-#include "via-ircc.h"
-
-#define VIA_MODULE_NAME "via-ircc"
-#define CHIP_IO_EXTENT 0x40
-
-static char *driver_name = VIA_MODULE_NAME;
-
-/* Module parameters */
-static int qos_mtt_bits = 0x07; /* 1 ms or more */
-static int dongle_id = 0; /* default: probe */
-
-/* We can't guess the type of connected dongle, user *must* supply it. */
-module_param(dongle_id, int, 0);
-
-/* Some prototypes */
-static int via_ircc_open(struct pci_dev *pdev, chipio_t *info,
- unsigned int id);
-static int via_ircc_dma_receive(struct via_ircc_cb *self);
-static int via_ircc_dma_receive_complete(struct via_ircc_cb *self,
- int iobase);
-static netdev_tx_t via_ircc_hard_xmit_sir(struct sk_buff *skb,
- struct net_device *dev);
-static netdev_tx_t via_ircc_hard_xmit_fir(struct sk_buff *skb,
- struct net_device *dev);
-static void via_hw_init(struct via_ircc_cb *self);
-static void via_ircc_change_speed(struct via_ircc_cb *self, __u32 baud);
-static irqreturn_t via_ircc_interrupt(int irq, void *dev_id);
-static int via_ircc_is_receiving(struct via_ircc_cb *self);
-static int via_ircc_read_dongle_id(int iobase);
-
-static int via_ircc_net_open(struct net_device *dev);
-static int via_ircc_net_close(struct net_device *dev);
-static int via_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq,
- int cmd);
-static void via_ircc_change_dongle_speed(int iobase, int speed,
- int dongle_id);
-static int RxTimerHandler(struct via_ircc_cb *self, int iobase);
-static void hwreset(struct via_ircc_cb *self);
-static int via_ircc_dma_xmit(struct via_ircc_cb *self, u16 iobase);
-static int upload_rxdata(struct via_ircc_cb *self, int iobase);
-static int via_init_one(struct pci_dev *pcidev, const struct pci_device_id *id);
-static void via_remove_one(struct pci_dev *pdev);
-
-/* FIXME : Should use udelay() instead, even if we are x86 only - Jean II */
-static void iodelay(int udelay)
-{
- u8 data;
- int i;
-
- for (i = 0; i < udelay; i++) {
- data = inb(0x80);
- }
-}
-
-static const struct pci_device_id via_pci_tbl[] = {
- { PCI_VENDOR_ID_VIA, 0x8231, PCI_ANY_ID, PCI_ANY_ID,0,0,0 },
- { PCI_VENDOR_ID_VIA, 0x3109, PCI_ANY_ID, PCI_ANY_ID,0,0,1 },
- { PCI_VENDOR_ID_VIA, 0x3074, PCI_ANY_ID, PCI_ANY_ID,0,0,2 },
- { PCI_VENDOR_ID_VIA, 0x3147, PCI_ANY_ID, PCI_ANY_ID,0,0,3 },
- { PCI_VENDOR_ID_VIA, 0x3177, PCI_ANY_ID, PCI_ANY_ID,0,0,4 },
- { 0, }
-};
-
-MODULE_DEVICE_TABLE(pci,via_pci_tbl);
-
-
-static struct pci_driver via_driver = {
- .name = VIA_MODULE_NAME,
- .id_table = via_pci_tbl,
- .probe = via_init_one,
- .remove = via_remove_one,
-};
-
-
-/*
- * Function via_ircc_init ()
- *
- * Initialize chip. Just find out chip type and resource.
- */
-static int __init via_ircc_init(void)
-{
- int rc;
-
- rc = pci_register_driver(&via_driver);
- if (rc < 0) {
- pr_debug("%s(): error rc = %d, returning -ENODEV...\n",
- __func__, rc);
- return -ENODEV;
- }
- return 0;
-}
-
-static int via_init_one(struct pci_dev *pcidev, const struct pci_device_id *id)
-{
- int rc;
- u8 temp,oldPCI_40,oldPCI_44,bTmp,bTmp1;
- u16 Chipset,FirDRQ1,FirDRQ0,FirIRQ,FirIOBase;
- chipio_t info;
-
- pr_debug("%s(): Device ID=(0X%X)\n", __func__, id->device);
-
- rc = pci_enable_device (pcidev);
- if (rc) {
- pr_debug("%s(): error rc = %d\n", __func__, rc);
- return -ENODEV;
- }
-
- // South Bridge exist
- if ( ReadLPCReg(0x20) != 0x3C )
- Chipset=0x3096;
- else
- Chipset=0x3076;
-
- if (Chipset==0x3076) {
- pr_debug("%s(): Chipset = 3076\n", __func__);
-
- WriteLPCReg(7,0x0c );
- temp=ReadLPCReg(0x30);//check if BIOS Enable Fir
- if((temp&0x01)==1) { // BIOS close or no FIR
- WriteLPCReg(0x1d, 0x82 );
- WriteLPCReg(0x23,0x18);
- temp=ReadLPCReg(0xF0);
- if((temp&0x01)==0) {
- temp=(ReadLPCReg(0x74)&0x03); //DMA
- FirDRQ0=temp + 4;
- temp=(ReadLPCReg(0x74)&0x0C) >> 2;
- FirDRQ1=temp + 4;
- } else {
- temp=(ReadLPCReg(0x74)&0x0C) >> 2; //DMA
- FirDRQ0=temp + 4;
- FirDRQ1=FirDRQ0;
- }
- FirIRQ=(ReadLPCReg(0x70)&0x0f); //IRQ
- FirIOBase=ReadLPCReg(0x60 ) << 8; //IO Space :high byte
- FirIOBase=FirIOBase| ReadLPCReg(0x61) ; //low byte
- FirIOBase=FirIOBase ;
- info.fir_base=FirIOBase;
- info.irq=FirIRQ;
- info.dma=FirDRQ1;
- info.dma2=FirDRQ0;
- pci_read_config_byte(pcidev,0x40,&bTmp);
- pci_write_config_byte(pcidev,0x40,((bTmp | 0x08) & 0xfe));
- pci_read_config_byte(pcidev,0x42,&bTmp);
- pci_write_config_byte(pcidev,0x42,(bTmp | 0xf0));
- pci_write_config_byte(pcidev,0x5a,0xc0);
- WriteLPCReg(0x28, 0x70 );
- rc = via_ircc_open(pcidev, &info, 0x3076);
- } else
- rc = -ENODEV; //IR not turn on
- } else { //Not VT1211
- pr_debug("%s(): Chipset = 3096\n", __func__);
-
- pci_read_config_byte(pcidev,0x67,&bTmp);//check if BIOS Enable Fir
- if((bTmp&0x01)==1) { // BIOS enable FIR
- //Enable Double DMA clock
- pci_read_config_byte(pcidev,0x42,&oldPCI_40);
- pci_write_config_byte(pcidev,0x42,oldPCI_40 | 0x80);
- pci_read_config_byte(pcidev,0x40,&oldPCI_40);
- pci_write_config_byte(pcidev,0x40,oldPCI_40 & 0xf7);
- pci_read_config_byte(pcidev,0x44,&oldPCI_44);
- pci_write_config_byte(pcidev,0x44,0x4e);
- //---------- read configuration from Function0 of south bridge
- if((bTmp&0x02)==0) {
- pci_read_config_byte(pcidev,0x44,&bTmp1); //DMA
- FirDRQ0 = (bTmp1 & 0x30) >> 4;
- pci_read_config_byte(pcidev,0x44,&bTmp1);
- FirDRQ1 = (bTmp1 & 0xc0) >> 6;
- } else {
- pci_read_config_byte(pcidev,0x44,&bTmp1); //DMA
- FirDRQ0 = (bTmp1 & 0x30) >> 4 ;
- FirDRQ1=0;
- }
- pci_read_config_byte(pcidev,0x47,&bTmp1); //IRQ
- FirIRQ = bTmp1 & 0x0f;
-
- pci_read_config_byte(pcidev,0x69,&bTmp);
- FirIOBase = bTmp << 8;//hight byte
- pci_read_config_byte(pcidev,0x68,&bTmp);
- FirIOBase = (FirIOBase | bTmp ) & 0xfff0;
- //-------------------------
- info.fir_base=FirIOBase;
- info.irq=FirIRQ;
- info.dma=FirDRQ1;
- info.dma2=FirDRQ0;
- rc = via_ircc_open(pcidev, &info, 0x3096);
- } else
- rc = -ENODEV; //IR not turn on !!!!!
- }//Not VT1211
-
- pr_debug("%s(): End - rc = %d\n", __func__, rc);
- return rc;
-}
-
-static void __exit via_ircc_cleanup(void)
-{
- /* Cleanup all instances of the driver */
- pci_unregister_driver (&via_driver);
-}
-
-static const struct net_device_ops via_ircc_sir_ops = {
- .ndo_start_xmit = via_ircc_hard_xmit_sir,
- .ndo_open = via_ircc_net_open,
- .ndo_stop = via_ircc_net_close,
- .ndo_do_ioctl = via_ircc_net_ioctl,
-};
-static const struct net_device_ops via_ircc_fir_ops = {
- .ndo_start_xmit = via_ircc_hard_xmit_fir,
- .ndo_open = via_ircc_net_open,
- .ndo_stop = via_ircc_net_close,
- .ndo_do_ioctl = via_ircc_net_ioctl,
-};
-
-/*
- * Function via_ircc_open(pdev, iobase, irq)
- *
- * Open driver instance
- *
- */
-static int via_ircc_open(struct pci_dev *pdev, chipio_t *info, unsigned int id)
-{
- struct net_device *dev;
- struct via_ircc_cb *self;
- int err;
-
- /* Allocate new instance of the driver */
- dev = alloc_irdadev(sizeof(struct via_ircc_cb));
- if (dev == NULL)
- return -ENOMEM;
-
- self = netdev_priv(dev);
- self->netdev = dev;
- spin_lock_init(&self->lock);
-
- pci_set_drvdata(pdev, self);
-
- /* Initialize Resource */
- self->io.cfg_base = info->cfg_base;
- self->io.fir_base = info->fir_base;
- self->io.irq = info->irq;
- self->io.fir_ext = CHIP_IO_EXTENT;
- self->io.dma = info->dma;
- self->io.dma2 = info->dma2;
- self->io.fifo_size = 32;
- self->chip_id = id;
- self->st_fifo.len = 0;
- self->RxDataReady = 0;
-
- /* Reserve the ioports that we need */
- if (!request_region(self->io.fir_base, self->io.fir_ext, driver_name)) {
- pr_debug("%s(), can't get iobase of 0x%03x\n",
- __func__, self->io.fir_base);
- err = -ENODEV;
- goto err_out1;
- }
-
- /* Initialize QoS for this device */
- irda_init_max_qos_capabilies(&self->qos);
-
- /* Check if user has supplied the dongle id or not */
- if (!dongle_id)
- dongle_id = via_ircc_read_dongle_id(self->io.fir_base);
- self->io.dongle_id = dongle_id;
-
- /* The only value we must override it the baudrate */
- /* Maximum speeds and capabilities are dongle-dependent. */
- switch( self->io.dongle_id ){
- case 0x0d:
- self->qos.baud_rate.bits =
- IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200 |
- IR_576000 | IR_1152000 | (IR_4000000 << 8);
- break;
- default:
- self->qos.baud_rate.bits =
- IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200;
- break;
- }
-
- /* Following was used for testing:
- *
- * self->qos.baud_rate.bits = IR_9600;
- *
- * Is is no good, as it prohibits (error-prone) speed-changes.
- */
-
- self->qos.min_turn_time.bits = qos_mtt_bits;
- irda_qos_bits_to_value(&self->qos);
-
- /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */
- self->rx_buff.truesize = 14384 + 2048;
- self->tx_buff.truesize = 14384 + 2048;
-
- /* Allocate memory if needed */
- self->rx_buff.head =
- dma_zalloc_coherent(&pdev->dev, self->rx_buff.truesize,
- &self->rx_buff_dma, GFP_KERNEL);
- if (self->rx_buff.head == NULL) {
- err = -ENOMEM;
- goto err_out2;
- }
-
- self->tx_buff.head =
- dma_zalloc_coherent(&pdev->dev, self->tx_buff.truesize,
- &self->tx_buff_dma, GFP_KERNEL);
- if (self->tx_buff.head == NULL) {
- err = -ENOMEM;
- goto err_out3;
- }
-
- self->rx_buff.in_frame = FALSE;
- self->rx_buff.state = OUTSIDE_FRAME;
- self->tx_buff.data = self->tx_buff.head;
- self->rx_buff.data = self->rx_buff.head;
-
- /* Reset Tx queue info */
- self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0;
- self->tx_fifo.tail = self->tx_buff.head;
-
- /* Override the network functions we need to use */
- dev->netdev_ops = &via_ircc_sir_ops;
-
- err = register_netdev(dev);
- if (err)
- goto err_out4;
-
- net_info_ratelimited("IrDA: Registered device %s (via-ircc)\n",
- dev->name);
-
- /* Initialise the hardware..
- */
- self->io.speed = 9600;
- via_hw_init(self);
- return 0;
- err_out4:
- dma_free_coherent(&pdev->dev, self->tx_buff.truesize,
- self->tx_buff.head, self->tx_buff_dma);
- err_out3:
- dma_free_coherent(&pdev->dev, self->rx_buff.truesize,
- self->rx_buff.head, self->rx_buff_dma);
- err_out2:
- release_region(self->io.fir_base, self->io.fir_ext);
- err_out1:
- free_netdev(dev);
- return err;
-}
-
-/*
- * Function via_remove_one(pdev)
- *
- * Close driver instance
- *
- */
-static void via_remove_one(struct pci_dev *pdev)
-{
- struct via_ircc_cb *self = pci_get_drvdata(pdev);
- int iobase;
-
- iobase = self->io.fir_base;
-
- ResetChip(iobase, 5); //hardware reset.
- /* Remove netdevice */
- unregister_netdev(self->netdev);
-
- /* Release the PORT that this driver is using */
- pr_debug("%s(), Releasing Region %03x\n",
- __func__, self->io.fir_base);
- release_region(self->io.fir_base, self->io.fir_ext);
- if (self->tx_buff.head)
- dma_free_coherent(&pdev->dev, self->tx_buff.truesize,
- self->tx_buff.head, self->tx_buff_dma);
- if (self->rx_buff.head)
- dma_free_coherent(&pdev->dev, self->rx_buff.truesize,
- self->rx_buff.head, self->rx_buff_dma);
-
- free_netdev(self->netdev);
-
- pci_disable_device(pdev);
-}
-
-/*
- * Function via_hw_init(self)
- *
- * Returns non-negative on success.
- *
- * Formerly via_ircc_setup
- */
-static void via_hw_init(struct via_ircc_cb *self)
-{
- int iobase = self->io.fir_base;
-
- SetMaxRxPacketSize(iobase, 0x0fff); //set to max:4095
- // FIFO Init
- EnRXFIFOReadyInt(iobase, OFF);
- EnRXFIFOHalfLevelInt(iobase, OFF);
- EnTXFIFOHalfLevelInt(iobase, OFF);
- EnTXFIFOUnderrunEOMInt(iobase, ON);
- EnTXFIFOReadyInt(iobase, OFF);
- InvertTX(iobase, OFF);
- InvertRX(iobase, OFF);
-
- if (ReadLPCReg(0x20) == 0x3c)
- WriteLPCReg(0xF0, 0); // for VT1211
- /* Int Init */
- EnRXSpecInt(iobase, ON);
-
- /* The following is basically hwreset */
- /* If this is the case, why not just call hwreset() ? Jean II */
- ResetChip(iobase, 5);
- EnableDMA(iobase, OFF);
- EnableTX(iobase, OFF);
- EnableRX(iobase, OFF);
- EnRXDMA(iobase, OFF);
- EnTXDMA(iobase, OFF);
- RXStart(iobase, OFF);
- TXStart(iobase, OFF);
- InitCard(iobase);
- CommonInit(iobase);
- SIRFilter(iobase, ON);
- SetSIR(iobase, ON);
- CRC16(iobase, ON);
- EnTXCRC(iobase, 0);
- WriteReg(iobase, I_ST_CT_0, 0x00);
- SetBaudRate(iobase, 9600);
- SetPulseWidth(iobase, 12);
- SetSendPreambleCount(iobase, 0);
-
- self->io.speed = 9600;
- self->st_fifo.len = 0;
-
- via_ircc_change_dongle_speed(iobase, self->io.speed,
- self->io.dongle_id);
-
- WriteReg(iobase, I_ST_CT_0, 0x80);
-}
-
-/*
- * Function via_ircc_read_dongle_id (void)
- *
- */
-static int via_ircc_read_dongle_id(int iobase)
-{
- net_err_ratelimited("via-ircc: dongle probing not supported, please specify dongle_id module parameter\n");
- return 9; /* Default to IBM */
-}
-
-/*
- * Function via_ircc_change_dongle_speed (iobase, speed, dongle_id)
- * Change speed of the attach dongle
- * only implement two type of dongle currently.
- */
-static void via_ircc_change_dongle_speed(int iobase, int speed,
- int dongle_id)
-{
- u8 mode = 0;
-
- /* speed is unused, as we use IsSIROn()/IsMIROn() */
- speed = speed;
-
- pr_debug("%s(): change_dongle_speed to %d for 0x%x, %d\n",
- __func__, speed, iobase, dongle_id);
-
- switch (dongle_id) {
-
- /* Note: The dongle_id's listed here are derived from
- * nsc-ircc.c */
-
- case 0x08: /* HP HSDL-2300, HP HSDL-3600/HSDL-3610 */
- UseOneRX(iobase, ON); // use one RX pin RX1,RX2
- InvertTX(iobase, OFF);
- InvertRX(iobase, OFF);
-
- EnRX2(iobase, ON); //sir to rx2
- EnGPIOtoRX2(iobase, OFF);
-
- if (IsSIROn(iobase)) { //sir
- // Mode select Off
- SlowIRRXLowActive(iobase, ON);
- udelay(1000);
- SlowIRRXLowActive(iobase, OFF);
- } else {
- if (IsMIROn(iobase)) { //mir
- // Mode select On
- SlowIRRXLowActive(iobase, OFF);
- udelay(20);
- } else { // fir
- if (IsFIROn(iobase)) { //fir
- // Mode select On
- SlowIRRXLowActive(iobase, OFF);
- udelay(20);
- }
- }
- }
- break;
-
- case 0x09: /* IBM31T1100 or Temic TFDS6000/TFDS6500 */
- UseOneRX(iobase, ON); //use ONE RX....RX1
- InvertTX(iobase, OFF);
- InvertRX(iobase, OFF); // invert RX pin
-
- EnRX2(iobase, ON);
- EnGPIOtoRX2(iobase, OFF);
- if (IsSIROn(iobase)) { //sir
- // Mode select On
- SlowIRRXLowActive(iobase, ON);
- udelay(20);
- // Mode select Off
- SlowIRRXLowActive(iobase, OFF);
- }
- if (IsMIROn(iobase)) { //mir
- // Mode select On
- SlowIRRXLowActive(iobase, OFF);
- udelay(20);
- // Mode select Off
- SlowIRRXLowActive(iobase, ON);
- } else { // fir
- if (IsFIROn(iobase)) { //fir
- // Mode select On
- SlowIRRXLowActive(iobase, OFF);
- // TX On
- WriteTX(iobase, ON);
- udelay(20);
- // Mode select OFF
- SlowIRRXLowActive(iobase, ON);
- udelay(20);
- // TX Off
- WriteTX(iobase, OFF);
- }
- }
- break;
-
- case 0x0d:
- UseOneRX(iobase, OFF); // use two RX pin RX1,RX2
- InvertTX(iobase, OFF);
- InvertRX(iobase, OFF);
- SlowIRRXLowActive(iobase, OFF);
- if (IsSIROn(iobase)) { //sir
- EnGPIOtoRX2(iobase, OFF);
- WriteGIO(iobase, OFF);
- EnRX2(iobase, OFF); //sir to rx2
- } else { // fir mir
- EnGPIOtoRX2(iobase, OFF);
- WriteGIO(iobase, OFF);
- EnRX2(iobase, OFF); //fir to rx
- }
- break;
-
- case 0x11: /* Temic TFDS4500 */
-
- pr_debug("%s: Temic TFDS4500: One RX pin, TX normal, RX inverted\n",
- __func__);
-
- UseOneRX(iobase, ON); //use ONE RX....RX1
- InvertTX(iobase, OFF);
- InvertRX(iobase, ON); // invert RX pin
-
- EnRX2(iobase, ON); //sir to rx2
- EnGPIOtoRX2(iobase, OFF);
-
- if( IsSIROn(iobase) ){ //sir
-
- // Mode select On
- SlowIRRXLowActive(iobase, ON);
- udelay(20);
- // Mode select Off
- SlowIRRXLowActive(iobase, OFF);
-
- } else{
- pr_debug("%s: Warning: TFDS4500 not running in SIR mode !\n",
- __func__);
- }
- break;
-
- case 0x0ff: /* Vishay */
- if (IsSIROn(iobase))
- mode = 0;
- else if (IsMIROn(iobase))
- mode = 1;
- else if (IsFIROn(iobase))
- mode = 2;
- else if (IsVFIROn(iobase))
- mode = 5; //VFIR-16
- SI_SetMode(iobase, mode);
- break;
-
- default:
- net_err_ratelimited("%s: Error: dongle_id %d unsupported !\n",
- __func__, dongle_id);
- }
-}
-
-/*
- * Function via_ircc_change_speed (self, baud)
- *
- * Change the speed of the device
- *
- */
-static void via_ircc_change_speed(struct via_ircc_cb *self, __u32 speed)
-{
- struct net_device *dev = self->netdev;
- u16 iobase;
- u8 value = 0, bTmp;
-
- iobase = self->io.fir_base;
- /* Update accounting for new speed */
- self->io.speed = speed;
- pr_debug("%s: change_speed to %d bps.\n", __func__, speed);
-
- WriteReg(iobase, I_ST_CT_0, 0x0);
-
- /* Controller mode sellection */
- switch (speed) {
- case 2400:
- case 9600:
- case 19200:
- case 38400:
- case 57600:
- case 115200:
- value = (115200/speed)-1;
- SetSIR(iobase, ON);
- CRC16(iobase, ON);
- break;
- case 576000:
- /* FIXME: this can't be right, as it's the same as 115200,
- * and 576000 is MIR, not SIR. */
- value = 0;
- SetSIR(iobase, ON);
- CRC16(iobase, ON);
- break;
- case 1152000:
- value = 0;
- SetMIR(iobase, ON);
- /* FIXME: CRC ??? */
- break;
- case 4000000:
- value = 0;
- SetFIR(iobase, ON);
- SetPulseWidth(iobase, 0);
- SetSendPreambleCount(iobase, 14);
- CRC16(iobase, OFF);
- EnTXCRC(iobase, ON);
- break;
- case 16000000:
- value = 0;
- SetVFIR(iobase, ON);
- /* FIXME: CRC ??? */
- break;
- default:
- value = 0;
- break;
- }
-
- /* Set baudrate to 0x19[2..7] */
- bTmp = (ReadReg(iobase, I_CF_H_1) & 0x03);
- bTmp |= value << 2;
- WriteReg(iobase, I_CF_H_1, bTmp);
-
- /* Some dongles may need to be informed about speed changes. */
- via_ircc_change_dongle_speed(iobase, speed, self->io.dongle_id);
-
- /* Set FIFO size to 64 */
- SetFIFO(iobase, 64);
-
- /* Enable IR */
- WriteReg(iobase, I_ST_CT_0, 0x80);
-
- // EnTXFIFOHalfLevelInt(iobase,ON);
-
- /* Enable some interrupts so we can receive frames */
- //EnAllInt(iobase,ON);
-
- if (IsSIROn(iobase)) {
- SIRFilter(iobase, ON);
- SIRRecvAny(iobase, ON);
- } else {
- SIRFilter(iobase, OFF);
- SIRRecvAny(iobase, OFF);
- }
-
- if (speed > 115200) {
- /* Install FIR xmit handler */
- dev->netdev_ops = &via_ircc_fir_ops;
- via_ircc_dma_receive(self);
- } else {
- /* Install SIR xmit handler */
- dev->netdev_ops = &via_ircc_sir_ops;
- }
- netif_wake_queue(dev);
-}
-
-/*
- * Function via_ircc_hard_xmit (skb, dev)
- *
- * Transmit the frame!
- *
- */
-static netdev_tx_t via_ircc_hard_xmit_sir(struct sk_buff *skb,
- struct net_device *dev)
-{
- struct via_ircc_cb *self;
- unsigned long flags;
- u16 iobase;
- __u32 speed;
-
- self = netdev_priv(dev);
- IRDA_ASSERT(self != NULL, return NETDEV_TX_OK;);
- iobase = self->io.fir_base;
-
- netif_stop_queue(dev);
- /* Check if we need to change the speed */
- speed = irda_get_next_speed(skb);
- if ((speed != self->io.speed) && (speed != -1)) {
- /* Check for empty frame */
- if (!skb->len) {
- via_ircc_change_speed(self, speed);
- netif_trans_update(dev);
- dev_kfree_skb(skb);
- return NETDEV_TX_OK;
- } else
- self->new_speed = speed;
- }
- InitCard(iobase);
- CommonInit(iobase);
- SIRFilter(iobase, ON);
- SetSIR(iobase, ON);
- CRC16(iobase, ON);
- EnTXCRC(iobase, 0);
- WriteReg(iobase, I_ST_CT_0, 0x00);
-
- spin_lock_irqsave(&self->lock, flags);
- self->tx_buff.data = self->tx_buff.head;
- self->tx_buff.len =
- async_wrap_skb(skb, self->tx_buff.data,
- self->tx_buff.truesize);
-
- dev->stats.tx_bytes += self->tx_buff.len;
- /* Send this frame with old speed */
- SetBaudRate(iobase, self->io.speed);
- SetPulseWidth(iobase, 12);
- SetSendPreambleCount(iobase, 0);
- WriteReg(iobase, I_ST_CT_0, 0x80);
-
- EnableTX(iobase, ON);
- EnableRX(iobase, OFF);
-
- ResetChip(iobase, 0);
- ResetChip(iobase, 1);
- ResetChip(iobase, 2);
- ResetChip(iobase, 3);
- ResetChip(iobase, 4);
-
- EnAllInt(iobase, ON);
- EnTXDMA(iobase, ON);
- EnRXDMA(iobase, OFF);
-
- irda_setup_dma(self->io.dma, self->tx_buff_dma, self->tx_buff.len,
- DMA_TX_MODE);
-
- SetSendByte(iobase, self->tx_buff.len);
- RXStart(iobase, OFF);
- TXStart(iobase, ON);
-
- netif_trans_update(dev);
- spin_unlock_irqrestore(&self->lock, flags);
- dev_kfree_skb(skb);
- return NETDEV_TX_OK;
-}
-
-static netdev_tx_t via_ircc_hard_xmit_fir(struct sk_buff *skb,
- struct net_device *dev)
-{
- struct via_ircc_cb *self;
- u16 iobase;
- __u32 speed;
- unsigned long flags;
-
- self = netdev_priv(dev);
- iobase = self->io.fir_base;
-
- if (self->st_fifo.len)
- return NETDEV_TX_OK;
- if (self->chip_id == 0x3076)
- iodelay(1500);
- else
- udelay(1500);
- netif_stop_queue(dev);
- speed = irda_get_next_speed(skb);
- if ((speed != self->io.speed) && (speed != -1)) {
- if (!skb->len) {
- via_ircc_change_speed(self, speed);
- netif_trans_update(dev);
- dev_kfree_skb(skb);
- return NETDEV_TX_OK;
- } else
- self->new_speed = speed;
- }
- spin_lock_irqsave(&self->lock, flags);
- self->tx_fifo.queue[self->tx_fifo.free].start = self->tx_fifo.tail;
- self->tx_fifo.queue[self->tx_fifo.free].len = skb->len;
-
- self->tx_fifo.tail += skb->len;
- dev->stats.tx_bytes += skb->len;
- skb_copy_from_linear_data(skb,
- self->tx_fifo.queue[self->tx_fifo.free].start, skb->len);
- self->tx_fifo.len++;
- self->tx_fifo.free++;
-//F01 if (self->tx_fifo.len == 1) {
- via_ircc_dma_xmit(self, iobase);
-//F01 }
-//F01 if (self->tx_fifo.free < (MAX_TX_WINDOW -1 )) netif_wake_queue(self->netdev);
- netif_trans_update(dev);
- dev_kfree_skb(skb);
- spin_unlock_irqrestore(&self->lock, flags);
- return NETDEV_TX_OK;
-
-}
-
-static int via_ircc_dma_xmit(struct via_ircc_cb *self, u16 iobase)
-{
- EnTXDMA(iobase, OFF);
- self->io.direction = IO_XMIT;
- EnPhys(iobase, ON);
- EnableTX(iobase, ON);
- EnableRX(iobase, OFF);
- ResetChip(iobase, 0);
- ResetChip(iobase, 1);
- ResetChip(iobase, 2);
- ResetChip(iobase, 3);
- ResetChip(iobase, 4);
- EnAllInt(iobase, ON);
- EnTXDMA(iobase, ON);
- EnRXDMA(iobase, OFF);
- irda_setup_dma(self->io.dma,
- ((u8 *)self->tx_fifo.queue[self->tx_fifo.ptr].start -
- self->tx_buff.head) + self->tx_buff_dma,
- self->tx_fifo.queue[self->tx_fifo.ptr].len, DMA_TX_MODE);
- pr_debug("%s: tx_fifo.ptr=%x,len=%x,tx_fifo.len=%x..\n",
- __func__, self->tx_fifo.ptr,
- self->tx_fifo.queue[self->tx_fifo.ptr].len,
- self->tx_fifo.len);
-
- SetSendByte(iobase, self->tx_fifo.queue[self->tx_fifo.ptr].len);
- RXStart(iobase, OFF);
- TXStart(iobase, ON);
- return 0;
-
-}
-
-/*
- * Function via_ircc_dma_xmit_complete (self)
- *
- * The transfer of a frame in finished. This function will only be called
- * by the interrupt handler
- *
- */
-static int via_ircc_dma_xmit_complete(struct via_ircc_cb *self)
-{
- int iobase;
- u8 Tx_status;
-
- iobase = self->io.fir_base;
- /* Disable DMA */
-// DisableDmaChannel(self->io.dma);
- /* Check for underrun! */
- /* Clear bit, by writing 1 into it */
- Tx_status = GetTXStatus(iobase);
- if (Tx_status & 0x08) {
- self->netdev->stats.tx_errors++;
- self->netdev->stats.tx_fifo_errors++;
- hwreset(self);
- /* how to clear underrun? */
- } else {
- self->netdev->stats.tx_packets++;
- ResetChip(iobase, 3);
- ResetChip(iobase, 4);
- }
- /* Check if we need to change the speed */
- if (self->new_speed) {
- via_ircc_change_speed(self, self->new_speed);
- self->new_speed = 0;
- }
-
- /* Finished with this frame, so prepare for next */
- if (IsFIROn(iobase)) {
- if (self->tx_fifo.len) {
- self->tx_fifo.len--;
- self->tx_fifo.ptr++;
- }
- }
- pr_debug("%s: tx_fifo.len=%x ,tx_fifo.ptr=%x,tx_fifo.free=%x...\n",
- __func__,
- self->tx_fifo.len, self->tx_fifo.ptr, self->tx_fifo.free);
-/* F01_S
- // Any frames to be sent back-to-back?
- if (self->tx_fifo.len) {
- // Not finished yet!
- via_ircc_dma_xmit(self, iobase);
- ret = FALSE;
- } else {
-F01_E*/
- // Reset Tx FIFO info
- self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0;
- self->tx_fifo.tail = self->tx_buff.head;
-//F01 }
-
- // Make sure we have room for more frames
-//F01 if (self->tx_fifo.free < (MAX_TX_WINDOW -1 )) {
- // Not busy transmitting anymore
- // Tell the network layer, that we can accept more frames
- netif_wake_queue(self->netdev);
-//F01 }
- return TRUE;
-}
-
-/*
- * Function via_ircc_dma_receive (self)
- *
- * Set configuration for receive a frame.
- *
- */
-static int via_ircc_dma_receive(struct via_ircc_cb *self)
-{
- int iobase;
-
- iobase = self->io.fir_base;
-
- self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0;
- self->tx_fifo.tail = self->tx_buff.head;
- self->RxDataReady = 0;
- self->io.direction = IO_RECV;
- self->rx_buff.data = self->rx_buff.head;
- self->st_fifo.len = self->st_fifo.pending_bytes = 0;
- self->st_fifo.tail = self->st_fifo.head = 0;
-
- EnPhys(iobase, ON);
- EnableTX(iobase, OFF);
- EnableRX(iobase, ON);
-
- ResetChip(iobase, 0);
- ResetChip(iobase, 1);
- ResetChip(iobase, 2);
- ResetChip(iobase, 3);
- ResetChip(iobase, 4);
-
- EnAllInt(iobase, ON);
- EnTXDMA(iobase, OFF);
- EnRXDMA(iobase, ON);
- irda_setup_dma(self->io.dma2, self->rx_buff_dma,
- self->rx_buff.truesize, DMA_RX_MODE);
- TXStart(iobase, OFF);
- RXStart(iobase, ON);
-
- return 0;
-}
-
-/*
- * Function via_ircc_dma_receive_complete (self)
- *
- * Controller Finished with receiving frames,
- * and this routine is call by ISR
- *
- */
-static int via_ircc_dma_receive_complete(struct via_ircc_cb *self,
- int iobase)
-{
- struct st_fifo *st_fifo;
- struct sk_buff *skb;
- int len, i;
- u8 status = 0;
-
- iobase = self->io.fir_base;
- st_fifo = &self->st_fifo;
-
- if (self->io.speed < 4000000) { //Speed below FIR
- len = GetRecvByte(iobase, self);
- skb = dev_alloc_skb(len + 1);
- if (skb == NULL)
- return FALSE;
- // Make sure IP header gets aligned
- skb_reserve(skb, 1);
- skb_put(skb, len - 2);
- if (self->chip_id == 0x3076) {
- for (i = 0; i < len - 2; i++)
- skb->data[i] = self->rx_buff.data[i * 2];
- } else {
- if (self->chip_id == 0x3096) {
- for (i = 0; i < len - 2; i++)
- skb->data[i] =
- self->rx_buff.data[i];
- }
- }
- // Move to next frame
- self->rx_buff.data += len;
- self->netdev->stats.rx_bytes += len;
- self->netdev->stats.rx_packets++;
- skb->dev = self->netdev;
- skb_reset_mac_header(skb);
- skb->protocol = htons(ETH_P_IRDA);
- netif_rx(skb);
- return TRUE;
- }
-
- else { //FIR mode
- len = GetRecvByte(iobase, self);
- if (len == 0)
- return TRUE; //interrupt only, data maybe move by RxT
- if (((len - 4) < 2) || ((len - 4) > 2048)) {
- pr_debug("%s(): Trouble:len=%x,CurCount=%x,LastCount=%x\n",
- __func__, len, RxCurCount(iobase, self),
- self->RxLastCount);
- hwreset(self);
- return FALSE;
- }
- pr_debug("%s(): fifo.len=%x,len=%x,CurCount=%x..\n",
- __func__,
- st_fifo->len, len - 4, RxCurCount(iobase, self));
-
- st_fifo->entries[st_fifo->tail].status = status;
- st_fifo->entries[st_fifo->tail].len = len;
- st_fifo->pending_bytes += len;
- st_fifo->tail++;
- st_fifo->len++;
- if (st_fifo->tail > MAX_RX_WINDOW)
- st_fifo->tail = 0;
- self->RxDataReady = 0;
-
- // It maybe have MAX_RX_WINDOW package receive by
- // receive_complete before Timer IRQ
-/* F01_S
- if (st_fifo->len < (MAX_RX_WINDOW+2 )) {
- RXStart(iobase,ON);
- SetTimer(iobase,4);
- }
- else {
-F01_E */
- EnableRX(iobase, OFF);
- EnRXDMA(iobase, OFF);
- RXStart(iobase, OFF);
-//F01_S
- // Put this entry back in fifo
- if (st_fifo->head > MAX_RX_WINDOW)
- st_fifo->head = 0;
- status = st_fifo->entries[st_fifo->head].status;
- len = st_fifo->entries[st_fifo->head].len;
- st_fifo->head++;
- st_fifo->len--;
-
- skb = dev_alloc_skb(len + 1 - 4);
- /*
- * if frame size, data ptr, or skb ptr are wrong, then get next
- * entry.
- */
- if ((skb == NULL) || (skb->data == NULL) ||
- (self->rx_buff.data == NULL) || (len < 6)) {
- self->netdev->stats.rx_dropped++;
- kfree_skb(skb);
- return TRUE;
- }
- skb_reserve(skb, 1);
- skb_put(skb, len - 4);
-
- skb_copy_to_linear_data(skb, self->rx_buff.data, len - 4);
- pr_debug("%s(): len=%x.rx_buff=%p\n", __func__,
- len - 4, self->rx_buff.data);
-
- // Move to next frame
- self->rx_buff.data += len;
- self->netdev->stats.rx_bytes += len;
- self->netdev->stats.rx_packets++;
- skb->dev = self->netdev;
- skb_reset_mac_header(skb);
- skb->protocol = htons(ETH_P_IRDA);
- netif_rx(skb);
-
-//F01_E
- } //FIR
- return TRUE;
-
-}
-
-/*
- * if frame is received , but no INT ,then use this routine to upload frame.
- */
-static int upload_rxdata(struct via_ircc_cb *self, int iobase)
-{
- struct sk_buff *skb;
- int len;
- struct st_fifo *st_fifo;
- st_fifo = &self->st_fifo;
-
- len = GetRecvByte(iobase, self);
-
- pr_debug("%s(): len=%x\n", __func__, len);
-
- if ((len - 4) < 2) {
- self->netdev->stats.rx_dropped++;
- return FALSE;
- }
-
- skb = dev_alloc_skb(len + 1);
- if (skb == NULL) {
- self->netdev->stats.rx_dropped++;
- return FALSE;
- }
- skb_reserve(skb, 1);
- skb_put(skb, len - 4 + 1);
- skb_copy_to_linear_data(skb, self->rx_buff.data, len - 4 + 1);
- st_fifo->tail++;
- st_fifo->len++;
- if (st_fifo->tail > MAX_RX_WINDOW)
- st_fifo->tail = 0;
- // Move to next frame
- self->rx_buff.data += len;
- self->netdev->stats.rx_bytes += len;
- self->netdev->stats.rx_packets++;
- skb->dev = self->netdev;
- skb_reset_mac_header(skb);
- skb->protocol = htons(ETH_P_IRDA);
- netif_rx(skb);
- if (st_fifo->len < (MAX_RX_WINDOW + 2)) {
- RXStart(iobase, ON);
- } else {
- EnableRX(iobase, OFF);
- EnRXDMA(iobase, OFF);
- RXStart(iobase, OFF);
- }
- return TRUE;
-}
-
-/*
- * Implement back to back receive , use this routine to upload data.
- */
-
-static int RxTimerHandler(struct via_ircc_cb *self, int iobase)
-{
- struct st_fifo *st_fifo;
- struct sk_buff *skb;
- int len;
- u8 status;
-
- st_fifo = &self->st_fifo;
-
- if (CkRxRecv(iobase, self)) {
- // if still receiving ,then return ,don't upload frame
- self->RetryCount = 0;
- SetTimer(iobase, 20);
- self->RxDataReady++;
- return FALSE;
- } else
- self->RetryCount++;
-
- if ((self->RetryCount >= 1) ||
- ((st_fifo->pending_bytes + 2048) > self->rx_buff.truesize) ||
- (st_fifo->len >= (MAX_RX_WINDOW))) {
- while (st_fifo->len > 0) { //upload frame
- // Put this entry back in fifo
- if (st_fifo->head > MAX_RX_WINDOW)
- st_fifo->head = 0;
- status = st_fifo->entries[st_fifo->head].status;
- len = st_fifo->entries[st_fifo->head].len;
- st_fifo->head++;
- st_fifo->len--;
-
- skb = dev_alloc_skb(len + 1 - 4);
- /*
- * if frame size, data ptr, or skb ptr are wrong,
- * then get next entry.
- */
- if ((skb == NULL) || (skb->data == NULL) ||
- (self->rx_buff.data == NULL) || (len < 6)) {
- self->netdev->stats.rx_dropped++;
- continue;
- }
- skb_reserve(skb, 1);
- skb_put(skb, len - 4);
- skb_copy_to_linear_data(skb, self->rx_buff.data, len - 4);
-
- pr_debug("%s(): len=%x.head=%x\n", __func__,
- len - 4, st_fifo->head);
-
- // Move to next frame
- self->rx_buff.data += len;
- self->netdev->stats.rx_bytes += len;
- self->netdev->stats.rx_packets++;
- skb->dev = self->netdev;
- skb_reset_mac_header(skb);
- skb->protocol = htons(ETH_P_IRDA);
- netif_rx(skb);
- } //while
- self->RetryCount = 0;
-
- pr_debug("%s(): End of upload HostStatus=%x,RxStatus=%x\n",
- __func__, GetHostStatus(iobase), GetRXStatus(iobase));
-
- /*
- * if frame is receive complete at this routine ,then upload
- * frame.
- */
- if ((GetRXStatus(iobase) & 0x10) &&
- (RxCurCount(iobase, self) != self->RxLastCount)) {
- upload_rxdata(self, iobase);
- if (irda_device_txqueue_empty(self->netdev))
- via_ircc_dma_receive(self);
- }
- } // timer detect complete
- else
- SetTimer(iobase, 4);
- return TRUE;
-
-}
-
-
-
-/*
- * Function via_ircc_interrupt (irq, dev_id)
- *
- * An interrupt from the chip has arrived. Time to do some work
- *
- */
-static irqreturn_t via_ircc_interrupt(int dummy, void *dev_id)
-{
- struct net_device *dev = dev_id;
- struct via_ircc_cb *self = netdev_priv(dev);
- int iobase;
- u8 iHostIntType, iRxIntType, iTxIntType;
-
- iobase = self->io.fir_base;
- spin_lock(&self->lock);
- iHostIntType = GetHostStatus(iobase);
-
- pr_debug("%s(): iHostIntType %02x: %s %s %s %02x\n",
- __func__, iHostIntType,
- (iHostIntType & 0x40) ? "Timer" : "",
- (iHostIntType & 0x20) ? "Tx" : "",
- (iHostIntType & 0x10) ? "Rx" : "",
- (iHostIntType & 0x0e) >> 1);
-
- if ((iHostIntType & 0x40) != 0) { //Timer Event
- self->EventFlag.TimeOut++;
- ClearTimerInt(iobase, 1);
- if (self->io.direction == IO_XMIT) {
- via_ircc_dma_xmit(self, iobase);
- }
- if (self->io.direction == IO_RECV) {
- /*
- * frame ready hold too long, must reset.
- */
- if (self->RxDataReady > 30) {
- hwreset(self);
- if (irda_device_txqueue_empty(self->netdev)) {
- via_ircc_dma_receive(self);
- }
- } else { // call this to upload frame.
- RxTimerHandler(self, iobase);
- }
- } //RECV
- } //Timer Event
- if ((iHostIntType & 0x20) != 0) { //Tx Event
- iTxIntType = GetTXStatus(iobase);
-
- pr_debug("%s(): iTxIntType %02x: %s %s %s %s\n",
- __func__, iTxIntType,
- (iTxIntType & 0x08) ? "FIFO underr." : "",
- (iTxIntType & 0x04) ? "EOM" : "",
- (iTxIntType & 0x02) ? "FIFO ready" : "",
- (iTxIntType & 0x01) ? "Early EOM" : "");
-
- if (iTxIntType & 0x4) {
- self->EventFlag.EOMessage++; // read and will auto clean
- if (via_ircc_dma_xmit_complete(self)) {
- if (irda_device_txqueue_empty
- (self->netdev)) {
- via_ircc_dma_receive(self);
- }
- } else {
- self->EventFlag.Unknown++;
- }
- } //EOP
- } //Tx Event
- //----------------------------------------
- if ((iHostIntType & 0x10) != 0) { //Rx Event
- /* Check if DMA has finished */
- iRxIntType = GetRXStatus(iobase);
-
- pr_debug("%s(): iRxIntType %02x: %s %s %s %s %s %s %s\n",
- __func__, iRxIntType,
- (iRxIntType & 0x80) ? "PHY err." : "",
- (iRxIntType & 0x40) ? "CRC err" : "",
- (iRxIntType & 0x20) ? "FIFO overr." : "",
- (iRxIntType & 0x10) ? "EOF" : "",
- (iRxIntType & 0x08) ? "RxData" : "",
- (iRxIntType & 0x02) ? "RxMaxLen" : "",
- (iRxIntType & 0x01) ? "SIR bad" : "");
- if (!iRxIntType)
- pr_debug("%s(): RxIRQ =0\n", __func__);
-
- if (iRxIntType & 0x10) {
- if (via_ircc_dma_receive_complete(self, iobase)) {
-//F01 if(!(IsFIROn(iobase))) via_ircc_dma_receive(self);
- via_ircc_dma_receive(self);
- }
- } // No ERR
- else { //ERR
- pr_debug("%s(): RxIRQ ERR:iRxIntType=%x,HostIntType=%x,CurCount=%x,RxLastCount=%x_____\n",
- __func__, iRxIntType, iHostIntType,
- RxCurCount(iobase, self), self->RxLastCount);
-
- if (iRxIntType & 0x20) { //FIFO OverRun ERR
- ResetChip(iobase, 0);
- ResetChip(iobase, 1);
- } else { //PHY,CRC ERR
-
- if (iRxIntType != 0x08)
- hwreset(self); //F01
- }
- via_ircc_dma_receive(self);
- } //ERR
-
- } //Rx Event
- spin_unlock(&self->lock);
- return IRQ_RETVAL(iHostIntType);
-}
-
-static void hwreset(struct via_ircc_cb *self)
-{
- int iobase;
- iobase = self->io.fir_base;
-
- ResetChip(iobase, 5);
- EnableDMA(iobase, OFF);
- EnableTX(iobase, OFF);
- EnableRX(iobase, OFF);
- EnRXDMA(iobase, OFF);
- EnTXDMA(iobase, OFF);
- RXStart(iobase, OFF);
- TXStart(iobase, OFF);
- InitCard(iobase);
- CommonInit(iobase);
- SIRFilter(iobase, ON);
- SetSIR(iobase, ON);
- CRC16(iobase, ON);
- EnTXCRC(iobase, 0);
- WriteReg(iobase, I_ST_CT_0, 0x00);
- SetBaudRate(iobase, 9600);
- SetPulseWidth(iobase, 12);
- SetSendPreambleCount(iobase, 0);
- WriteReg(iobase, I_ST_CT_0, 0x80);
-
- /* Restore speed. */
- via_ircc_change_speed(self, self->io.speed);
-
- self->st_fifo.len = 0;
-}
-
-/*
- * Function via_ircc_is_receiving (self)
- *
- * Return TRUE is we are currently receiving a frame
- *
- */
-static int via_ircc_is_receiving(struct via_ircc_cb *self)
-{
- int status = FALSE;
- int iobase;
-
- IRDA_ASSERT(self != NULL, return FALSE;);
-
- iobase = self->io.fir_base;
- if (CkRxRecv(iobase, self))
- status = TRUE;
-
- pr_debug("%s(): status=%x....\n", __func__, status);
-
- return status;
-}
-
-
-/*
- * Function via_ircc_net_open (dev)
- *
- * Start the device
- *
- */
-static int via_ircc_net_open(struct net_device *dev)
-{
- struct via_ircc_cb *self;
- int iobase;
- char hwname[32];
-
- IRDA_ASSERT(dev != NULL, return -1;);
- self = netdev_priv(dev);
- dev->stats.rx_packets = 0;
- IRDA_ASSERT(self != NULL, return 0;);
- iobase = self->io.fir_base;
- if (request_irq(self->io.irq, via_ircc_interrupt, 0, dev->name, dev)) {
- net_warn_ratelimited("%s, unable to allocate irq=%d\n",
- driver_name, self->io.irq);
- return -EAGAIN;
- }
- /*
- * Always allocate the DMA channel after the IRQ, and clean up on
- * failure.
- */
- if (request_dma(self->io.dma, dev->name)) {
- net_warn_ratelimited("%s, unable to allocate dma=%d\n",
- driver_name, self->io.dma);
- free_irq(self->io.irq, dev);
- return -EAGAIN;
- }
- if (self->io.dma2 != self->io.dma) {
- if (request_dma(self->io.dma2, dev->name)) {
- net_warn_ratelimited("%s, unable to allocate dma2=%d\n",
- driver_name, self->io.dma2);
- free_irq(self->io.irq, dev);
- free_dma(self->io.dma);
- return -EAGAIN;
- }
- }
-
-
- /* turn on interrupts */
- EnAllInt(iobase, ON);
- EnInternalLoop(iobase, OFF);
- EnExternalLoop(iobase, OFF);
-
- /* */
- via_ircc_dma_receive(self);
-
- /* Ready to play! */
- netif_start_queue(dev);
-
- /*
- * Open new IrLAP layer instance, now that everything should be
- * initialized properly
- */
- sprintf(hwname, "VIA @ 0x%x", iobase);
- self->irlap = irlap_open(dev, &self->qos, hwname);
-
- self->RxLastCount = 0;
-
- return 0;
-}
-
-/*
- * Function via_ircc_net_close (dev)
- *
- * Stop the device
- *
- */
-static int via_ircc_net_close(struct net_device *dev)
-{
- struct via_ircc_cb *self;
- int iobase;
-
- IRDA_ASSERT(dev != NULL, return -1;);
- self = netdev_priv(dev);
- IRDA_ASSERT(self != NULL, return 0;);
-
- /* Stop device */
- netif_stop_queue(dev);
- /* Stop and remove instance of IrLAP */
- if (self->irlap)
- irlap_close(self->irlap);
- self->irlap = NULL;
- iobase = self->io.fir_base;
- EnTXDMA(iobase, OFF);
- EnRXDMA(iobase, OFF);
- DisableDmaChannel(self->io.dma);
-
- /* Disable interrupts */
- EnAllInt(iobase, OFF);
- free_irq(self->io.irq, dev);
- free_dma(self->io.dma);
- if (self->io.dma2 != self->io.dma)
- free_dma(self->io.dma2);
-
- return 0;
-}
-
-/*
- * Function via_ircc_net_ioctl (dev, rq, cmd)
- *
- * Process IOCTL commands for this device
- *
- */
-static int via_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq,
- int cmd)
-{
- struct if_irda_req *irq = (struct if_irda_req *) rq;
- struct via_ircc_cb *self;
- unsigned long flags;
- int ret = 0;
-
- IRDA_ASSERT(dev != NULL, return -1;);
- self = netdev_priv(dev);
- IRDA_ASSERT(self != NULL, return -1;);
- pr_debug("%s(), %s, (cmd=0x%X)\n", __func__, dev->name,
- cmd);
- /* Disable interrupts & save flags */
- spin_lock_irqsave(&self->lock, flags);
- switch (cmd) {
- case SIOCSBANDWIDTH: /* Set bandwidth */
- if (!capable(CAP_NET_ADMIN)) {
- ret = -EPERM;
- goto out;
- }
- via_ircc_change_speed(self, irq->ifr_baudrate);
- break;
- case SIOCSMEDIABUSY: /* Set media busy */
- if (!capable(CAP_NET_ADMIN)) {
- ret = -EPERM;
- goto out;
- }
- irda_device_set_media_busy(self->netdev, TRUE);
- break;
- case SIOCGRECEIVING: /* Check if we are receiving right now */
- irq->ifr_receiving = via_ircc_is_receiving(self);
- break;
- default:
- ret = -EOPNOTSUPP;
- }
- out:
- spin_unlock_irqrestore(&self->lock, flags);
- return ret;
-}
-
-MODULE_AUTHOR("VIA Technologies,inc");
-MODULE_DESCRIPTION("VIA IrDA Device Driver");
-MODULE_LICENSE("GPL");
-
-module_init(via_ircc_init);
-module_exit(via_ircc_cleanup);
diff --git a/drivers/staging/irda/drivers/via-ircc.h b/drivers/staging/irda/drivers/via-ircc.h
deleted file mode 100644
index ac1525573398..000000000000
--- a/drivers/staging/irda/drivers/via-ircc.h
+++ /dev/null
@@ -1,846 +0,0 @@
-/*********************************************************************
- *
- * Filename: via-ircc.h
- * Version: 1.0
- * Description: Driver for the VIA VT8231/VT8233 IrDA chipsets
- * Author: VIA Technologies, inc
- * Date : 08/06/2003
-
-Copyright (c) 1998-2003 VIA Technologies, Inc.
-
-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 WARRANTIES OR REPRESENTATIONS; 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, see <http://www.gnu.org/licenses/>.
-
- * Comment:
- * jul/08/2002 : Rx buffer length should use Rx ring ptr.
- * Oct/28/2002 : Add SB id for 3147 and 3177.
- * jul/09/2002 : only implement two kind of dongle currently.
- * Oct/02/2002 : work on VT8231 and VT8233 .
- * Aug/06/2003 : change driver format to pci driver .
- ********************************************************************/
-#ifndef via_IRCC_H
-#define via_IRCC_H
-#include <linux/spinlock.h>
-#include <linux/pm.h>
-#include <linux/types.h>
-#include <asm/io.h>
-
-#define MAX_TX_WINDOW 7
-#define MAX_RX_WINDOW 7
-
-struct st_fifo_entry {
- int status;
- int len;
-};
-
-struct st_fifo {
- struct st_fifo_entry entries[MAX_RX_WINDOW + 2];
- int pending_bytes;
- int head;
- int tail;
- int len;
-};
-
-struct frame_cb {
- void *start; /* Start of frame in DMA mem */
- int len; /* Length of frame in DMA mem */
-};
-
-struct tx_fifo {
- struct frame_cb queue[MAX_TX_WINDOW + 2]; /* Info about frames in queue */
- int ptr; /* Currently being sent */
- int len; /* Length of queue */
- int free; /* Next free slot */
- void *tail; /* Next free start in DMA mem */
-};
-
-
-struct eventflag // for keeping track of Interrupt Events
-{
- //--------tx part
- unsigned char TxFIFOUnderRun;
- unsigned char EOMessage;
- unsigned char TxFIFOReady;
- unsigned char EarlyEOM;
- //--------rx part
- unsigned char PHYErr;
- unsigned char CRCErr;
- unsigned char RxFIFOOverRun;
- unsigned char EOPacket;
- unsigned char RxAvail;
- unsigned char TooLargePacket;
- unsigned char SIRBad;
- //--------unknown
- unsigned char Unknown;
- //----------
- unsigned char TimeOut;
- unsigned char RxDMATC;
- unsigned char TxDMATC;
-};
-
-/* Private data for each instance */
-struct via_ircc_cb {
- struct st_fifo st_fifo; /* Info about received frames */
- struct tx_fifo tx_fifo; /* Info about frames to be transmitted */
-
- struct net_device *netdev; /* Yes! we are some kind of netdevice */
-
- struct irlap_cb *irlap; /* The link layer we are binded to */
- struct qos_info qos; /* QoS capabilities for this device */
-
- chipio_t io; /* IrDA controller information */
- iobuff_t tx_buff; /* Transmit buffer */
- iobuff_t rx_buff; /* Receive buffer */
- dma_addr_t tx_buff_dma;
- dma_addr_t rx_buff_dma;
-
- __u8 ier; /* Interrupt enable register */
-
- spinlock_t lock; /* For serializing operations */
-
- __u32 flags; /* Interface flags */
- __u32 new_speed;
- int index; /* Instance index */
-
- struct eventflag EventFlag;
- unsigned int chip_id; /* to remember chip id */
- unsigned int RetryCount;
- unsigned int RxDataReady;
- unsigned int RxLastCount;
-};
-
-
-//---------I=Infrared, H=Host, M=Misc, T=Tx, R=Rx, ST=Status,
-// CF=Config, CT=Control, L=Low, H=High, C=Count
-#define I_CF_L_0 0x10
-#define I_CF_H_0 0x11
-#define I_SIR_BOF 0x12
-#define I_SIR_EOF 0x13
-#define I_ST_CT_0 0x15
-#define I_ST_L_1 0x16
-#define I_ST_H_1 0x17
-#define I_CF_L_1 0x18
-#define I_CF_H_1 0x19
-#define I_CF_L_2 0x1a
-#define I_CF_H_2 0x1b
-#define I_CF_3 0x1e
-#define H_CT 0x20
-#define H_ST 0x21
-#define M_CT 0x22
-#define TX_CT_1 0x23
-#define TX_CT_2 0x24
-#define TX_ST 0x25
-#define RX_CT 0x26
-#define RX_ST 0x27
-#define RESET 0x28
-#define P_ADDR 0x29
-#define RX_C_L 0x2a
-#define RX_C_H 0x2b
-#define RX_P_L 0x2c
-#define RX_P_H 0x2d
-#define TX_C_L 0x2e
-#define TX_C_H 0x2f
-#define TIMER 0x32
-#define I_CF_4 0x33
-#define I_T_C_L 0x34
-#define I_T_C_H 0x35
-#define VERSION 0x3f
-//-------------------------------
-#define StartAddr 0x10 // the first register address
-#define EndAddr 0x3f // the last register address
-#define GetBit(val,bit) val = (unsigned char) ((val>>bit) & 0x1)
- // Returns the bit
-#define SetBit(val,bit) val= (unsigned char ) (val | (0x1 << bit))
- // Sets bit to 1
-#define ResetBit(val,bit) val= (unsigned char ) (val & ~(0x1 << bit))
- // Sets bit to 0
-
-#define OFF 0
-#define ON 1
-#define DMA_TX_MODE 0x08
-#define DMA_RX_MODE 0x04
-
-#define DMA1 0
-#define DMA2 0xc0
-#define MASK1 DMA1+0x0a
-#define MASK2 DMA2+0x14
-
-#define Clk_bit 0x40
-#define Tx_bit 0x01
-#define Rd_Valid 0x08
-#define RxBit 0x08
-
-static void DisableDmaChannel(unsigned int channel)
-{
- switch (channel) { // 8 Bit DMA channels DMAC1
- case 0:
- outb(4, MASK1); //mask channel 0
- break;
- case 1:
- outb(5, MASK1); //Mask channel 1
- break;
- case 2:
- outb(6, MASK1); //Mask channel 2
- break;
- case 3:
- outb(7, MASK1); //Mask channel 3
- break;
- case 5:
- outb(5, MASK2); //Mask channel 5
- break;
- case 6:
- outb(6, MASK2); //Mask channel 6
- break;
- case 7:
- outb(7, MASK2); //Mask channel 7
- break;
- default:
- break;
- }
-}
-
-static unsigned char ReadLPCReg(int iRegNum)
-{
- unsigned char iVal;
-
- outb(0x87, 0x2e);
- outb(0x87, 0x2e);
- outb(iRegNum, 0x2e);
- iVal = inb(0x2f);
- outb(0xaa, 0x2e);
-
- return iVal;
-}
-
-static void WriteLPCReg(int iRegNum, unsigned char iVal)
-{
-
- outb(0x87, 0x2e);
- outb(0x87, 0x2e);
- outb(iRegNum, 0x2e);
- outb(iVal, 0x2f);
- outb(0xAA, 0x2e);
-}
-
-static __u8 ReadReg(unsigned int BaseAddr, int iRegNum)
-{
- return (__u8) inb(BaseAddr + iRegNum);
-}
-
-static void WriteReg(unsigned int BaseAddr, int iRegNum, unsigned char iVal)
-{
- outb(iVal, BaseAddr + iRegNum);
-}
-
-static int WriteRegBit(unsigned int BaseAddr, unsigned char RegNum,
- unsigned char BitPos, unsigned char value)
-{
- __u8 Rtemp, Wtemp;
-
- if (BitPos > 7) {
- return -1;
- }
- if ((RegNum < StartAddr) || (RegNum > EndAddr))
- return -1;
- Rtemp = ReadReg(BaseAddr, RegNum);
- if (value == 0)
- Wtemp = ResetBit(Rtemp, BitPos);
- else {
- if (value == 1)
- Wtemp = SetBit(Rtemp, BitPos);
- else
- return -1;
- }
- WriteReg(BaseAddr, RegNum, Wtemp);
- return 0;
-}
-
-static __u8 CheckRegBit(unsigned int BaseAddr, unsigned char RegNum,
- unsigned char BitPos)
-{
- __u8 temp;
-
- if (BitPos > 7)
- return 0xff;
- if ((RegNum < StartAddr) || (RegNum > EndAddr)) {
-// printf("what is the register %x!\n",RegNum);
- }
- temp = ReadReg(BaseAddr, RegNum);
- return GetBit(temp, BitPos);
-}
-
-static void SetMaxRxPacketSize(__u16 iobase, __u16 size)
-{
- __u16 low, high;
- if ((size & 0xe000) == 0) {
- low = size & 0x00ff;
- high = (size & 0x1f00) >> 8;
- WriteReg(iobase, I_CF_L_2, low);
- WriteReg(iobase, I_CF_H_2, high);
-
- }
-
-}
-
-//for both Rx and Tx
-
-static void SetFIFO(__u16 iobase, __u16 value)
-{
- switch (value) {
- case 128:
- WriteRegBit(iobase, 0x11, 0, 0);
- WriteRegBit(iobase, 0x11, 7, 1);
- break;
- case 64:
- WriteRegBit(iobase, 0x11, 0, 0);
- WriteRegBit(iobase, 0x11, 7, 0);
- break;
- case 32:
- WriteRegBit(iobase, 0x11, 0, 1);
- WriteRegBit(iobase, 0x11, 7, 0);
- break;
- default:
- WriteRegBit(iobase, 0x11, 0, 0);
- WriteRegBit(iobase, 0x11, 7, 0);
- }
-
-}
-
-#define CRC16(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,7,val) //0 for 32 CRC
-/*
-#define SetVFIR(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,5,val)
-#define SetFIR(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,6,val)
-#define SetMIR(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,5,val)
-#define SetSIR(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,4,val)
-*/
-#define SIRFilter(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,3,val)
-#define Filter(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,2,val)
-#define InvertTX(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,1,val)
-#define InvertRX(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,0,val)
-//****************************I_CF_H_0
-#define EnableTX(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,4,val)
-#define EnableRX(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,3,val)
-#define EnableDMA(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,2,val)
-#define SIRRecvAny(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,1,val)
-#define DiableTrans(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,0,val)
-//***************************I_SIR_BOF,I_SIR_EOF
-#define SetSIRBOF(BaseAddr,val) WriteReg(BaseAddr,I_SIR_BOF,val)
-#define SetSIREOF(BaseAddr,val) WriteReg(BaseAddr,I_SIR_EOF,val)
-#define GetSIRBOF(BaseAddr) ReadReg(BaseAddr,I_SIR_BOF)
-#define GetSIREOF(BaseAddr) ReadReg(BaseAddr,I_SIR_EOF)
-//*******************I_ST_CT_0
-#define EnPhys(BaseAddr,val) WriteRegBit(BaseAddr,I_ST_CT_0,7,val)
-#define IsModeError(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,6) //RO
-#define IsVFIROn(BaseAddr) CheckRegBit(BaseAddr,0x14,0) //RO for VT1211 only
-#define IsFIROn(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,5) //RO
-#define IsMIROn(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,4) //RO
-#define IsSIROn(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,3) //RO
-#define IsEnableTX(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,2) //RO
-#define IsEnableRX(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,1) //RO
-#define Is16CRC(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,0) //RO
-//***************************I_CF_3
-#define DisableAdjacentPulseWidth(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_3,5,val) //1 disable
-#define DisablePulseWidthAdjust(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_3,4,val) //1 disable
-#define UseOneRX(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_3,1,val) //0 use two RX
-#define SlowIRRXLowActive(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_3,0,val) //0 show RX high=1 in SIR
-//***************************H_CT
-#define EnAllInt(BaseAddr,val) WriteRegBit(BaseAddr,H_CT,7,val)
-#define TXStart(BaseAddr,val) WriteRegBit(BaseAddr,H_CT,6,val)
-#define RXStart(BaseAddr,val) WriteRegBit(BaseAddr,H_CT,5,val)
-#define ClearRXInt(BaseAddr,val) WriteRegBit(BaseAddr,H_CT,4,val) // 1 clear
-//*****************H_ST
-#define IsRXInt(BaseAddr) CheckRegBit(BaseAddr,H_ST,4)
-#define GetIntIndentify(BaseAddr) ((ReadReg(BaseAddr,H_ST)&0xf1) >>1)
-#define IsHostBusy(BaseAddr) CheckRegBit(BaseAddr,H_ST,0)
-#define GetHostStatus(BaseAddr) ReadReg(BaseAddr,H_ST) //RO
-//**************************M_CT
-#define EnTXDMA(BaseAddr,val) WriteRegBit(BaseAddr,M_CT,7,val)
-#define EnRXDMA(BaseAddr,val) WriteRegBit(BaseAddr,M_CT,6,val)
-#define SwapDMA(BaseAddr,val) WriteRegBit(BaseAddr,M_CT,5,val)
-#define EnInternalLoop(BaseAddr,val) WriteRegBit(BaseAddr,M_CT,4,val)
-#define EnExternalLoop(BaseAddr,val) WriteRegBit(BaseAddr,M_CT,3,val)
-//**************************TX_CT_1
-#define EnTXFIFOHalfLevelInt(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_1,4,val) //half empty int (1 half)
-#define EnTXFIFOUnderrunEOMInt(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_1,5,val)
-#define EnTXFIFOReadyInt(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_1,6,val) //int when reach it threshold (setting by bit 4)
-//**************************TX_CT_2
-#define ForceUnderrun(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_2,7,val) // force an underrun int
-#define EnTXCRC(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_2,6,val) //1 for FIR,MIR...0 (not SIR)
-#define ForceBADCRC(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_2,5,val) //force an bad CRC
-#define SendSIP(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_2,4,val) //send indication pulse for prevent SIR disturb
-#define ClearEnTX(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_2,3,val) // opposite to EnTX
-//*****************TX_ST
-#define GetTXStatus(BaseAddr) ReadReg(BaseAddr,TX_ST) //RO
-//**************************RX_CT
-#define EnRXSpecInt(BaseAddr,val) WriteRegBit(BaseAddr,RX_CT,0,val)
-#define EnRXFIFOReadyInt(BaseAddr,val) WriteRegBit(BaseAddr,RX_CT,1,val) //enable int when reach it threshold (setting by bit 7)
-#define EnRXFIFOHalfLevelInt(BaseAddr,val) WriteRegBit(BaseAddr,RX_CT,7,val) //enable int when (1) half full...or (0) just not full
-//*****************RX_ST
-#define GetRXStatus(BaseAddr) ReadReg(BaseAddr,RX_ST) //RO
-//***********************P_ADDR
-#define SetPacketAddr(BaseAddr,addr) WriteReg(BaseAddr,P_ADDR,addr)
-//***********************I_CF_4
-#define EnGPIOtoRX2(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_4,7,val)
-#define EnTimerInt(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_4,1,val)
-#define ClearTimerInt(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_4,0,val)
-//***********************I_T_C_L
-#define WriteGIO(BaseAddr,val) WriteRegBit(BaseAddr,I_T_C_L,7,val)
-#define ReadGIO(BaseAddr) CheckRegBit(BaseAddr,I_T_C_L,7)
-#define ReadRX(BaseAddr) CheckRegBit(BaseAddr,I_T_C_L,3) //RO
-#define WriteTX(BaseAddr,val) WriteRegBit(BaseAddr,I_T_C_L,0,val)
-//***********************I_T_C_H
-#define EnRX2(BaseAddr,val) WriteRegBit(BaseAddr,I_T_C_H,7,val)
-#define ReadRX2(BaseAddr) CheckRegBit(BaseAddr,I_T_C_H,7)
-//**********************Version
-#define GetFIRVersion(BaseAddr) ReadReg(BaseAddr,VERSION)
-
-
-static void SetTimer(__u16 iobase, __u8 count)
-{
- EnTimerInt(iobase, OFF);
- WriteReg(iobase, TIMER, count);
- EnTimerInt(iobase, ON);
-}
-
-
-static void SetSendByte(__u16 iobase, __u32 count)
-{
- __u32 low, high;
-
- if ((count & 0xf000) == 0) {
- low = count & 0x00ff;
- high = (count & 0x0f00) >> 8;
- WriteReg(iobase, TX_C_L, low);
- WriteReg(iobase, TX_C_H, high);
- }
-}
-
-static void ResetChip(__u16 iobase, __u8 type)
-{
- __u8 value;
-
- value = (type + 2) << 4;
- WriteReg(iobase, RESET, type);
-}
-
-static int CkRxRecv(__u16 iobase, struct via_ircc_cb *self)
-{
- __u8 low, high;
- __u16 wTmp = 0, wTmp1 = 0, wTmp_new = 0;
-
- low = ReadReg(iobase, RX_C_L);
- high = ReadReg(iobase, RX_C_H);
- wTmp1 = high;
- wTmp = (wTmp1 << 8) | low;
- udelay(10);
- low = ReadReg(iobase, RX_C_L);
- high = ReadReg(iobase, RX_C_H);
- wTmp1 = high;
- wTmp_new = (wTmp1 << 8) | low;
- if (wTmp_new != wTmp)
- return 1;
- else
- return 0;
-
-}
-
-static __u16 RxCurCount(__u16 iobase, struct via_ircc_cb * self)
-{
- __u8 low, high;
- __u16 wTmp = 0, wTmp1 = 0;
-
- low = ReadReg(iobase, RX_P_L);
- high = ReadReg(iobase, RX_P_H);
- wTmp1 = high;
- wTmp = (wTmp1 << 8) | low;
- return wTmp;
-}
-
-/* This Routine can only use in recevie_complete
- * for it will update last count.
- */
-
-static __u16 GetRecvByte(__u16 iobase, struct via_ircc_cb * self)
-{
- __u8 low, high;
- __u16 wTmp, wTmp1, ret;
-
- low = ReadReg(iobase, RX_P_L);
- high = ReadReg(iobase, RX_P_H);
- wTmp1 = high;
- wTmp = (wTmp1 << 8) | low;
-
-
- if (wTmp >= self->RxLastCount)
- ret = wTmp - self->RxLastCount;
- else
- ret = (0x8000 - self->RxLastCount) + wTmp;
- self->RxLastCount = wTmp;
-
-/* RX_P is more actually the RX_C
- low=ReadReg(iobase,RX_C_L);
- high=ReadReg(iobase,RX_C_H);
-
- if(!(high&0xe000)) {
- temp=(high<<8)+low;
- return temp;
- }
- else return 0;
-*/
- return ret;
-}
-
-static void Sdelay(__u16 scale)
-{
- __u8 bTmp;
- int i, j;
-
- for (j = 0; j < scale; j++) {
- for (i = 0; i < 0x20; i++) {
- bTmp = inb(0xeb);
- outb(bTmp, 0xeb);
- }
- }
-}
-
-static void Tdelay(__u16 scale)
-{
- __u8 bTmp;
- int i, j;
-
- for (j = 0; j < scale; j++) {
- for (i = 0; i < 0x50; i++) {
- bTmp = inb(0xeb);
- outb(bTmp, 0xeb);
- }
- }
-}
-
-
-static void ActClk(__u16 iobase, __u8 value)
-{
- __u8 bTmp;
- bTmp = ReadReg(iobase, 0x34);
- if (value)
- WriteReg(iobase, 0x34, bTmp | Clk_bit);
- else
- WriteReg(iobase, 0x34, bTmp & ~Clk_bit);
-}
-
-static void ClkTx(__u16 iobase, __u8 Clk, __u8 Tx)
-{
- __u8 bTmp;
-
- bTmp = ReadReg(iobase, 0x34);
- if (Clk == 0)
- bTmp &= ~Clk_bit;
- else {
- if (Clk == 1)
- bTmp |= Clk_bit;
- }
- WriteReg(iobase, 0x34, bTmp);
- Sdelay(1);
- if (Tx == 0)
- bTmp &= ~Tx_bit;
- else {
- if (Tx == 1)
- bTmp |= Tx_bit;
- }
- WriteReg(iobase, 0x34, bTmp);
-}
-
-static void Wr_Byte(__u16 iobase, __u8 data)
-{
- __u8 bData = data;
-// __u8 btmp;
- int i;
-
- ClkTx(iobase, 0, 1);
-
- Tdelay(2);
- ActClk(iobase, 1);
- Tdelay(1);
-
- for (i = 0; i < 8; i++) { //LDN
-
- if ((bData >> i) & 0x01) {
- ClkTx(iobase, 0, 1); //bit data = 1;
- } else {
- ClkTx(iobase, 0, 0); //bit data = 1;
- }
- Tdelay(2);
- Sdelay(1);
- ActClk(iobase, 1); //clk hi
- Tdelay(1);
- }
-}
-
-static __u8 Rd_Indx(__u16 iobase, __u8 addr, __u8 index)
-{
- __u8 data = 0, bTmp, data_bit;
- int i;
-
- bTmp = addr | (index << 1) | 0;
- ClkTx(iobase, 0, 0);
- Tdelay(2);
- ActClk(iobase, 1);
- udelay(1);
- Wr_Byte(iobase, bTmp);
- Sdelay(1);
- ClkTx(iobase, 0, 0);
- Tdelay(2);
- for (i = 0; i < 10; i++) {
- ActClk(iobase, 1);
- Tdelay(1);
- ActClk(iobase, 0);
- Tdelay(1);
- ClkTx(iobase, 0, 1);
- Tdelay(1);
- bTmp = ReadReg(iobase, 0x34);
- if (!(bTmp & Rd_Valid))
- break;
- }
- if (!(bTmp & Rd_Valid)) {
- for (i = 0; i < 8; i++) {
- ActClk(iobase, 1);
- Tdelay(1);
- ActClk(iobase, 0);
- bTmp = ReadReg(iobase, 0x34);
- data_bit = 1 << i;
- if (bTmp & RxBit)
- data |= data_bit;
- else
- data &= ~data_bit;
- Tdelay(2);
- }
- } else {
- for (i = 0; i < 2; i++) {
- ActClk(iobase, 1);
- Tdelay(1);
- ActClk(iobase, 0);
- Tdelay(2);
- }
- bTmp = ReadReg(iobase, 0x34);
- }
- for (i = 0; i < 1; i++) {
- ActClk(iobase, 1);
- Tdelay(1);
- ActClk(iobase, 0);
- Tdelay(2);
- }
- ClkTx(iobase, 0, 0);
- Tdelay(1);
- for (i = 0; i < 3; i++) {
- ActClk(iobase, 1);
- Tdelay(1);
- ActClk(iobase, 0);
- Tdelay(2);
- }
- return data;
-}
-
-static void Wr_Indx(__u16 iobase, __u8 addr, __u8 index, __u8 data)
-{
- int i;
- __u8 bTmp;
-
- ClkTx(iobase, 0, 0);
- udelay(2);
- ActClk(iobase, 1);
- udelay(1);
- bTmp = addr | (index << 1) | 1;
- Wr_Byte(iobase, bTmp);
- Wr_Byte(iobase, data);
- for (i = 0; i < 2; i++) {
- ClkTx(iobase, 0, 0);
- Tdelay(2);
- ActClk(iobase, 1);
- Tdelay(1);
- }
- ActClk(iobase, 0);
-}
-
-static void ResetDongle(__u16 iobase)
-{
- int i;
- ClkTx(iobase, 0, 0);
- Tdelay(1);
- for (i = 0; i < 30; i++) {
- ActClk(iobase, 1);
- Tdelay(1);
- ActClk(iobase, 0);
- Tdelay(1);
- }
- ActClk(iobase, 0);
-}
-
-static void SetSITmode(__u16 iobase)
-{
-
- __u8 bTmp;
-
- bTmp = ReadLPCReg(0x28);
- WriteLPCReg(0x28, bTmp | 0x10); //select ITMOFF
- bTmp = ReadReg(iobase, 0x35);
- WriteReg(iobase, 0x35, bTmp | 0x40); // Driver ITMOFF
- WriteReg(iobase, 0x28, bTmp | 0x80); // enable All interrupt
-}
-
-static void SI_SetMode(__u16 iobase, int mode)
-{
- //__u32 dTmp;
- __u8 bTmp;
-
- WriteLPCReg(0x28, 0x70); // S/W Reset
- SetSITmode(iobase);
- ResetDongle(iobase);
- udelay(10);
- Wr_Indx(iobase, 0x40, 0x0, 0x17); //RX ,APEN enable,Normal power
- Wr_Indx(iobase, 0x40, 0x1, mode); //Set Mode
- Wr_Indx(iobase, 0x40, 0x2, 0xff); //Set power to FIR VFIR > 1m
- bTmp = Rd_Indx(iobase, 0x40, 1);
-}
-
-static void InitCard(__u16 iobase)
-{
- ResetChip(iobase, 5);
- WriteReg(iobase, I_ST_CT_0, 0x00); // open CHIP on
- SetSIRBOF(iobase, 0xc0); // hardware default value
- SetSIREOF(iobase, 0xc1);
-}
-
-static void CommonInit(__u16 iobase)
-{
-// EnTXCRC(iobase,0);
- SwapDMA(iobase, OFF);
- SetMaxRxPacketSize(iobase, 0x0fff); //set to max:4095
- EnRXFIFOReadyInt(iobase, OFF);
- EnRXFIFOHalfLevelInt(iobase, OFF);
- EnTXFIFOHalfLevelInt(iobase, OFF);
- EnTXFIFOUnderrunEOMInt(iobase, ON);
-// EnTXFIFOReadyInt(iobase,ON);
- InvertTX(iobase, OFF);
- InvertRX(iobase, OFF);
-// WriteLPCReg(0xF0,0); //(if VT1211 then do this)
- if (IsSIROn(iobase)) {
- SIRFilter(iobase, ON);
- SIRRecvAny(iobase, ON);
- } else {
- SIRFilter(iobase, OFF);
- SIRRecvAny(iobase, OFF);
- }
- EnRXSpecInt(iobase, ON);
- WriteReg(iobase, I_ST_CT_0, 0x80);
- EnableDMA(iobase, ON);
-}
-
-static void SetBaudRate(__u16 iobase, __u32 rate)
-{
- __u8 value = 11, temp;
-
- if (IsSIROn(iobase)) {
- switch (rate) {
- case (__u32) (2400L):
- value = 47;
- break;
- case (__u32) (9600L):
- value = 11;
- break;
- case (__u32) (19200L):
- value = 5;
- break;
- case (__u32) (38400L):
- value = 2;
- break;
- case (__u32) (57600L):
- value = 1;
- break;
- case (__u32) (115200L):
- value = 0;
- break;
- default:
- break;
- }
- } else if (IsMIROn(iobase)) {
- value = 0; // will automatically be fixed in 1.152M
- } else if (IsFIROn(iobase)) {
- value = 0; // will automatically be fixed in 4M
- }
- temp = (ReadReg(iobase, I_CF_H_1) & 0x03);
- temp |= value << 2;
- WriteReg(iobase, I_CF_H_1, temp);
-}
-
-static void SetPulseWidth(__u16 iobase, __u8 width)
-{
- __u8 temp, temp1, temp2;
-
- temp = (ReadReg(iobase, I_CF_L_1) & 0x1f);
- temp1 = (ReadReg(iobase, I_CF_H_1) & 0xfc);
- temp2 = (width & 0x07) << 5;
- temp |= temp2;
- temp2 = (width & 0x18) >> 3;
- temp1 |= temp2;
- WriteReg(iobase, I_CF_L_1, temp);
- WriteReg(iobase, I_CF_H_1, temp1);
-}
-
-static void SetSendPreambleCount(__u16 iobase, __u8 count)
-{
- __u8 temp;
-
- temp = ReadReg(iobase, I_CF_L_1) & 0xe0;
- temp |= count;
- WriteReg(iobase, I_CF_L_1, temp);
-
-}
-
-static void SetVFIR(__u16 BaseAddr, __u8 val)
-{
- __u8 tmp;
-
- tmp = ReadReg(BaseAddr, I_CF_L_0);
- WriteReg(BaseAddr, I_CF_L_0, tmp & 0x8f);
- WriteRegBit(BaseAddr, I_CF_H_0, 5, val);
-}
-
-static void SetFIR(__u16 BaseAddr, __u8 val)
-{
- __u8 tmp;
-
- WriteRegBit(BaseAddr, I_CF_H_0, 5, 0);
- tmp = ReadReg(BaseAddr, I_CF_L_0);
- WriteReg(BaseAddr, I_CF_L_0, tmp & 0x8f);
- WriteRegBit(BaseAddr, I_CF_L_0, 6, val);
-}
-
-static void SetMIR(__u16 BaseAddr, __u8 val)
-{
- __u8 tmp;
-
- WriteRegBit(BaseAddr, I_CF_H_0, 5, 0);
- tmp = ReadReg(BaseAddr, I_CF_L_0);
- WriteReg(BaseAddr, I_CF_L_0, tmp & 0x8f);
- WriteRegBit(BaseAddr, I_CF_L_0, 5, val);
-}
-
-static void SetSIR(__u16 BaseAddr, __u8 val)
-{
- __u8 tmp;
-
- WriteRegBit(BaseAddr, I_CF_H_0, 5, 0);
- tmp = ReadReg(BaseAddr, I_CF_L_0);
- WriteReg(BaseAddr, I_CF_L_0, tmp & 0x8f);
- WriteRegBit(BaseAddr, I_CF_L_0, 4, val);
-}
-
-#endif /* via_IRCC_H */
diff --git a/drivers/staging/irda/drivers/vlsi_ir.c b/drivers/staging/irda/drivers/vlsi_ir.c
deleted file mode 100644
index 3dff3c55ddf5..000000000000
--- a/drivers/staging/irda/drivers/vlsi_ir.c
+++ /dev/null
@@ -1,1872 +0,0 @@
-/*********************************************************************
- *
- * vlsi_ir.c: VLSI82C147 PCI IrDA controller driver for Linux
- *
- * Copyright (c) 2001-2003 Martin Diehl
- *
- * 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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/module.h>
-
-#define DRIVER_NAME "vlsi_ir"
-#define DRIVER_VERSION "v0.5"
-#define DRIVER_DESCRIPTION "IrDA SIR/MIR/FIR driver for VLSI 82C147"
-#define DRIVER_AUTHOR "Martin Diehl <info@mdiehl.de>"
-
-MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_LICENSE("GPL");
-
-/********************************************************/
-
-#include <linux/kernel.h>
-#include <linux/ktime.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/slab.h>
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <linux/delay.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-#include <linux/math64.h>
-#include <linux/mutex.h>
-#include <linux/uaccess.h>
-#include <asm/byteorder.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irda_device.h>
-#include <net/irda/wrapper.h>
-#include <net/irda/crc.h>
-
-#include "vlsi_ir.h"
-
-/********************************************************/
-
-static /* const */ char drivername[] = DRIVER_NAME;
-
-static const struct pci_device_id vlsi_irda_table[] = {
- {
- .class = PCI_CLASS_WIRELESS_IRDA << 8,
- .class_mask = PCI_CLASS_SUBCLASS_MASK << 8,
- .vendor = PCI_VENDOR_ID_VLSI,
- .device = PCI_DEVICE_ID_VLSI_82C147,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- },
- { /* all zeroes */ }
-};
-
-MODULE_DEVICE_TABLE(pci, vlsi_irda_table);
-
-/********************************************************/
-
-/* clksrc: which clock source to be used
- * 0: auto - try PLL, fallback to 40MHz XCLK
- * 1: on-chip 48MHz PLL
- * 2: external 48MHz XCLK
- * 3: external 40MHz XCLK (HP OB-800)
- */
-
-static int clksrc = 0; /* default is 0(auto) */
-module_param(clksrc, int, 0);
-MODULE_PARM_DESC(clksrc, "clock input source selection");
-
-/* ringsize: size of the tx and rx descriptor rings
- * independent for tx and rx
- * specify as ringsize=tx[,rx]
- * allowed values: 4, 8, 16, 32, 64
- * Due to the IrDA 1.x max. allowed window size=7,
- * there should be no gain when using rings larger than 8
- */
-
-static int ringsize[] = {8,8}; /* default is tx=8 / rx=8 */
-module_param_array(ringsize, int, NULL, 0);
-MODULE_PARM_DESC(ringsize, "TX, RX ring descriptor size");
-
-/* sirpulse: tuning of the SIR pulse width within IrPHY 1.3 limits
- * 0: very short, 1.5us (exception: 6us at 2.4 kbaud)
- * 1: nominal 3/16 bittime width
- * note: IrDA compliant peer devices should be happy regardless
- * which one is used. Primary goal is to save some power
- * on the sender's side - at 9.6kbaud for example the short
- * pulse width saves more than 90% of the transmitted IR power.
- */
-
-static int sirpulse = 1; /* default is 3/16 bittime */
-module_param(sirpulse, int, 0);
-MODULE_PARM_DESC(sirpulse, "SIR pulse width tuning");
-
-/* qos_mtt_bits: encoded min-turn-time value we require the peer device
- * to use before transmitting to us. "Type 1" (per-station)
- * bitfield according to IrLAP definition (section 6.6.8)
- * Don't know which transceiver is used by my OB800 - the
- * pretty common HP HDLS-1100 requires 1 msec - so lets use this.
- */
-
-static int qos_mtt_bits = 0x07; /* default is 1 ms or more */
-module_param(qos_mtt_bits, int, 0);
-MODULE_PARM_DESC(qos_mtt_bits, "IrLAP bitfield representing min-turn-time");
-
-/********************************************************/
-
-static void vlsi_reg_debug(unsigned iobase, const char *s)
-{
- int i;
-
- printk(KERN_DEBUG "%s: ", s);
- for (i = 0; i < 0x20; i++)
- printk("%02x", (unsigned)inb((iobase+i)));
- printk("\n");
-}
-
-static void vlsi_ring_debug(struct vlsi_ring *r)
-{
- struct ring_descr *rd;
- unsigned i;
-
- printk(KERN_DEBUG "%s - ring %p / size %u / mask 0x%04x / len %u / dir %d / hw %p\n",
- __func__, r, r->size, r->mask, r->len, r->dir, r->rd[0].hw);
- printk(KERN_DEBUG "%s - head = %d / tail = %d\n", __func__,
- atomic_read(&r->head) & r->mask, atomic_read(&r->tail) & r->mask);
- for (i = 0; i < r->size; i++) {
- rd = &r->rd[i];
- printk(KERN_DEBUG "%s - ring descr %u: ", __func__, i);
- printk("skb=%p data=%p hw=%p\n", rd->skb, rd->buf, rd->hw);
- printk(KERN_DEBUG "%s - hw: status=%02x count=%u addr=0x%08x\n",
- __func__, (unsigned) rd_get_status(rd),
- (unsigned) rd_get_count(rd), (unsigned) rd_get_addr(rd));
- }
-}
-
-/********************************************************/
-
-/* needed regardless of CONFIG_PROC_FS */
-static struct proc_dir_entry *vlsi_proc_root = NULL;
-
-#ifdef CONFIG_PROC_FS
-
-static void vlsi_proc_pdev(struct seq_file *seq, struct pci_dev *pdev)
-{
- unsigned iobase = pci_resource_start(pdev, 0);
- unsigned i;
-
- seq_printf(seq, "\n%s (vid/did: [%04x:%04x])\n",
- pci_name(pdev), (int)pdev->vendor, (int)pdev->device);
- seq_printf(seq, "pci-power-state: %u\n", (unsigned) pdev->current_state);
- seq_printf(seq, "resources: irq=%u / io=0x%04x / dma_mask=0x%016Lx\n",
- pdev->irq, (unsigned)pci_resource_start(pdev, 0), (unsigned long long)pdev->dma_mask);
- seq_puts(seq, "hw registers: ");
- for (i = 0; i < 0x20; i++)
- seq_printf(seq, "%02x", (unsigned)inb((iobase+i)));
- seq_putc(seq, '\n');
-}
-
-static void vlsi_proc_ndev(struct seq_file *seq, struct net_device *ndev)
-{
- vlsi_irda_dev_t *idev = netdev_priv(ndev);
- u8 byte;
- u16 word;
- s32 sec, usec;
- unsigned iobase = ndev->base_addr;
-
- seq_printf(seq, "\n%s link state: %s / %s / %s / %s\n", ndev->name,
- netif_device_present(ndev) ? "attached" : "detached",
- netif_running(ndev) ? "running" : "not running",
- netif_carrier_ok(ndev) ? "carrier ok" : "no carrier",
- netif_queue_stopped(ndev) ? "queue stopped" : "queue running");
-
- if (!netif_running(ndev))
- return;
-
- seq_puts(seq, "\nhw-state:\n");
- pci_read_config_byte(idev->pdev, VLSI_PCI_IRMISC, &byte);
- seq_printf(seq, "IRMISC:%s%s%s uart%s",
- (byte&IRMISC_IRRAIL) ? " irrail" : "",
- (byte&IRMISC_IRPD) ? " irpd" : "",
- (byte&IRMISC_UARTTST) ? " uarttest" : "",
- (byte&IRMISC_UARTEN) ? "@" : " disabled\n");
- if (byte&IRMISC_UARTEN) {
- seq_printf(seq, "0x%s\n",
- (byte&2) ? ((byte&1) ? "3e8" : "2e8")
- : ((byte&1) ? "3f8" : "2f8"));
- }
- pci_read_config_byte(idev->pdev, VLSI_PCI_CLKCTL, &byte);
- seq_printf(seq, "CLKCTL: PLL %s%s%s / clock %s / wakeup %s\n",
- (byte&CLKCTL_PD_INV) ? "powered" : "down",
- (byte&CLKCTL_LOCK) ? " locked" : "",
- (byte&CLKCTL_EXTCLK) ? ((byte&CLKCTL_XCKSEL)?" / 40 MHz XCLK":" / 48 MHz XCLK") : "",
- (byte&CLKCTL_CLKSTP) ? "stopped" : "running",
- (byte&CLKCTL_WAKE) ? "enabled" : "disabled");
- pci_read_config_byte(idev->pdev, VLSI_PCI_MSTRPAGE, &byte);
- seq_printf(seq, "MSTRPAGE: 0x%02x\n", (unsigned)byte);
-
- byte = inb(iobase+VLSI_PIO_IRINTR);
- seq_printf(seq, "IRINTR:%s%s%s%s%s%s%s%s\n",
- (byte&IRINTR_ACTEN) ? " ACTEN" : "",
- (byte&IRINTR_RPKTEN) ? " RPKTEN" : "",
- (byte&IRINTR_TPKTEN) ? " TPKTEN" : "",
- (byte&IRINTR_OE_EN) ? " OE_EN" : "",
- (byte&IRINTR_ACTIVITY) ? " ACTIVITY" : "",
- (byte&IRINTR_RPKTINT) ? " RPKTINT" : "",
- (byte&IRINTR_TPKTINT) ? " TPKTINT" : "",
- (byte&IRINTR_OE_INT) ? " OE_INT" : "");
- word = inw(iobase+VLSI_PIO_RINGPTR);
- seq_printf(seq, "RINGPTR: rx=%u / tx=%u\n", RINGPTR_GET_RX(word), RINGPTR_GET_TX(word));
- word = inw(iobase+VLSI_PIO_RINGBASE);
- seq_printf(seq, "RINGBASE: busmap=0x%08x\n",
- ((unsigned)word << 10)|(MSTRPAGE_VALUE<<24));
- word = inw(iobase+VLSI_PIO_RINGSIZE);
- seq_printf(seq, "RINGSIZE: rx=%u / tx=%u\n", RINGSIZE_TO_RXSIZE(word),
- RINGSIZE_TO_TXSIZE(word));
-
- word = inw(iobase+VLSI_PIO_IRCFG);
- seq_printf(seq, "IRCFG:%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
- (word&IRCFG_LOOP) ? " LOOP" : "",
- (word&IRCFG_ENTX) ? " ENTX" : "",
- (word&IRCFG_ENRX) ? " ENRX" : "",
- (word&IRCFG_MSTR) ? " MSTR" : "",
- (word&IRCFG_RXANY) ? " RXANY" : "",
- (word&IRCFG_CRC16) ? " CRC16" : "",
- (word&IRCFG_FIR) ? " FIR" : "",
- (word&IRCFG_MIR) ? " MIR" : "",
- (word&IRCFG_SIR) ? " SIR" : "",
- (word&IRCFG_SIRFILT) ? " SIRFILT" : "",
- (word&IRCFG_SIRTEST) ? " SIRTEST" : "",
- (word&IRCFG_TXPOL) ? " TXPOL" : "",
- (word&IRCFG_RXPOL) ? " RXPOL" : "");
- word = inw(iobase+VLSI_PIO_IRENABLE);
- seq_printf(seq, "IRENABLE:%s%s%s%s%s%s%s%s\n",
- (word&IRENABLE_PHYANDCLOCK) ? " PHYANDCLOCK" : "",
- (word&IRENABLE_CFGER) ? " CFGERR" : "",
- (word&IRENABLE_FIR_ON) ? " FIR_ON" : "",
- (word&IRENABLE_MIR_ON) ? " MIR_ON" : "",
- (word&IRENABLE_SIR_ON) ? " SIR_ON" : "",
- (word&IRENABLE_ENTXST) ? " ENTXST" : "",
- (word&IRENABLE_ENRXST) ? " ENRXST" : "",
- (word&IRENABLE_CRC16_ON) ? " CRC16_ON" : "");
- word = inw(iobase+VLSI_PIO_PHYCTL);
- seq_printf(seq, "PHYCTL: baud-divisor=%u / pulsewidth=%u / preamble=%u\n",
- (unsigned)PHYCTL_TO_BAUD(word),
- (unsigned)PHYCTL_TO_PLSWID(word),
- (unsigned)PHYCTL_TO_PREAMB(word));
- word = inw(iobase+VLSI_PIO_NPHYCTL);
- seq_printf(seq, "NPHYCTL: baud-divisor=%u / pulsewidth=%u / preamble=%u\n",
- (unsigned)PHYCTL_TO_BAUD(word),
- (unsigned)PHYCTL_TO_PLSWID(word),
- (unsigned)PHYCTL_TO_PREAMB(word));
- word = inw(iobase+VLSI_PIO_MAXPKT);
- seq_printf(seq, "MAXPKT: max. rx packet size = %u\n", word);
- word = inw(iobase+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK;
- seq_printf(seq, "RCVBCNT: rx-fifo filling level = %u\n", word);
-
- seq_puts(seq, "\nsw-state:\n");
- seq_printf(seq, "IrPHY setup: %d baud - %s encoding\n", idev->baud,
- (idev->mode==IFF_SIR)?"SIR":((idev->mode==IFF_MIR)?"MIR":"FIR"));
- sec = div_s64_rem(ktime_us_delta(ktime_get(), idev->last_rx),
- USEC_PER_SEC, &usec);
- seq_printf(seq, "last rx: %ul.%06u sec\n", sec, usec);
-
- seq_printf(seq, "RX: packets=%lu / bytes=%lu / errors=%lu / dropped=%lu",
- ndev->stats.rx_packets, ndev->stats.rx_bytes, ndev->stats.rx_errors,
- ndev->stats.rx_dropped);
- seq_printf(seq, " / overrun=%lu / length=%lu / frame=%lu / crc=%lu\n",
- ndev->stats.rx_over_errors, ndev->stats.rx_length_errors,
- ndev->stats.rx_frame_errors, ndev->stats.rx_crc_errors);
- seq_printf(seq, "TX: packets=%lu / bytes=%lu / errors=%lu / dropped=%lu / fifo=%lu\n",
- ndev->stats.tx_packets, ndev->stats.tx_bytes, ndev->stats.tx_errors,
- ndev->stats.tx_dropped, ndev->stats.tx_fifo_errors);
-
-}
-
-static void vlsi_proc_ring(struct seq_file *seq, struct vlsi_ring *r)
-{
- struct ring_descr *rd;
- unsigned i, j;
- int h, t;
-
- seq_printf(seq, "size %u / mask 0x%04x / len %u / dir %d / hw %p\n",
- r->size, r->mask, r->len, r->dir, r->rd[0].hw);
- h = atomic_read(&r->head) & r->mask;
- t = atomic_read(&r->tail) & r->mask;
- seq_printf(seq, "head = %d / tail = %d ", h, t);
- if (h == t)
- seq_puts(seq, "(empty)\n");
- else {
- if (((t+1)&r->mask) == h)
- seq_puts(seq, "(full)\n");
- else
- seq_printf(seq, "(level = %d)\n", ((unsigned)(t-h) & r->mask));
- rd = &r->rd[h];
- j = (unsigned) rd_get_count(rd);
- seq_printf(seq, "current: rd = %d / status = %02x / len = %u\n",
- h, (unsigned)rd_get_status(rd), j);
- if (j > 0) {
- seq_printf(seq, " data: %*ph\n",
- min_t(unsigned, j, 20), rd->buf);
- }
- }
- for (i = 0; i < r->size; i++) {
- rd = &r->rd[i];
- seq_printf(seq, "> ring descr %u: ", i);
- seq_printf(seq, "skb=%p data=%p hw=%p\n", rd->skb, rd->buf, rd->hw);
- seq_printf(seq, " hw: status=%02x count=%u busaddr=0x%08x\n",
- (unsigned) rd_get_status(rd),
- (unsigned) rd_get_count(rd), (unsigned) rd_get_addr(rd));
- }
-}
-
-static int vlsi_seq_show(struct seq_file *seq, void *v)
-{
- struct net_device *ndev = seq->private;
- vlsi_irda_dev_t *idev = netdev_priv(ndev);
- unsigned long flags;
-
- seq_printf(seq, "\n%s %s\n\n", DRIVER_NAME, DRIVER_VERSION);
- seq_printf(seq, "clksrc: %s\n",
- (clksrc>=2) ? ((clksrc==3)?"40MHz XCLK":"48MHz XCLK")
- : ((clksrc==1)?"48MHz PLL":"autodetect"));
- seq_printf(seq, "ringsize: tx=%d / rx=%d\n",
- ringsize[0], ringsize[1]);
- seq_printf(seq, "sirpulse: %s\n", (sirpulse)?"3/16 bittime":"short");
- seq_printf(seq, "qos_mtt_bits: 0x%02x\n", (unsigned)qos_mtt_bits);
-
- spin_lock_irqsave(&idev->lock, flags);
- if (idev->pdev != NULL) {
- vlsi_proc_pdev(seq, idev->pdev);
-
- if (idev->pdev->current_state == 0)
- vlsi_proc_ndev(seq, ndev);
- else
- seq_printf(seq, "\nPCI controller down - resume_ok = %d\n",
- idev->resume_ok);
- if (netif_running(ndev) && idev->rx_ring && idev->tx_ring) {
- seq_puts(seq, "\n--------- RX ring -----------\n\n");
- vlsi_proc_ring(seq, idev->rx_ring);
- seq_puts(seq, "\n--------- TX ring -----------\n\n");
- vlsi_proc_ring(seq, idev->tx_ring);
- }
- }
- seq_putc(seq, '\n');
- spin_unlock_irqrestore(&idev->lock, flags);
-
- return 0;
-}
-
-static int vlsi_seq_open(struct inode *inode, struct file *file)
-{
- return single_open(file, vlsi_seq_show, PDE_DATA(inode));
-}
-
-static const struct file_operations vlsi_proc_fops = {
- .owner = THIS_MODULE,
- .open = vlsi_seq_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-#define VLSI_PROC_FOPS (&vlsi_proc_fops)
-
-#else /* CONFIG_PROC_FS */
-#define VLSI_PROC_FOPS NULL
-#endif
-
-/********************************************************/
-
-static struct vlsi_ring *vlsi_alloc_ring(struct pci_dev *pdev, struct ring_descr_hw *hwmap,
- unsigned size, unsigned len, int dir)
-{
- struct vlsi_ring *r;
- struct ring_descr *rd;
- unsigned i, j;
- dma_addr_t busaddr;
-
- if (!size || ((size-1)&size)!=0) /* must be >0 and power of 2 */
- return NULL;
-
- r = kmalloc(sizeof(*r) + size * sizeof(struct ring_descr), GFP_KERNEL);
- if (!r)
- return NULL;
- memset(r, 0, sizeof(*r));
-
- r->pdev = pdev;
- r->dir = dir;
- r->len = len;
- r->rd = (struct ring_descr *)(r+1);
- r->mask = size - 1;
- r->size = size;
- atomic_set(&r->head, 0);
- atomic_set(&r->tail, 0);
-
- for (i = 0; i < size; i++) {
- rd = r->rd + i;
- memset(rd, 0, sizeof(*rd));
- rd->hw = hwmap + i;
- rd->buf = kmalloc(len, GFP_KERNEL|GFP_DMA);
- if (rd->buf)
- busaddr = pci_map_single(pdev, rd->buf, len, dir);
- if (rd->buf == NULL || pci_dma_mapping_error(pdev, busaddr)) {
- if (rd->buf) {
- net_err_ratelimited("%s: failed to create PCI-MAP for %p\n",
- __func__, rd->buf);
- kfree(rd->buf);
- rd->buf = NULL;
- }
- for (j = 0; j < i; j++) {
- rd = r->rd + j;
- busaddr = rd_get_addr(rd);
- rd_set_addr_status(rd, 0, 0);
- pci_unmap_single(pdev, busaddr, len, dir);
- kfree(rd->buf);
- rd->buf = NULL;
- }
- kfree(r);
- return NULL;
- }
- rd_set_addr_status(rd, busaddr, 0);
- /* initially, the dma buffer is owned by the CPU */
- rd->skb = NULL;
- }
- return r;
-}
-
-static int vlsi_free_ring(struct vlsi_ring *r)
-{
- struct ring_descr *rd;
- unsigned i;
- dma_addr_t busaddr;
-
- for (i = 0; i < r->size; i++) {
- rd = r->rd + i;
- if (rd->skb)
- dev_kfree_skb_any(rd->skb);
- busaddr = rd_get_addr(rd);
- rd_set_addr_status(rd, 0, 0);
- if (busaddr)
- pci_unmap_single(r->pdev, busaddr, r->len, r->dir);
- kfree(rd->buf);
- }
- kfree(r);
- return 0;
-}
-
-static int vlsi_create_hwif(vlsi_irda_dev_t *idev)
-{
- char *ringarea;
- struct ring_descr_hw *hwmap;
-
- idev->virtaddr = NULL;
- idev->busaddr = 0;
-
- ringarea = pci_zalloc_consistent(idev->pdev, HW_RING_AREA_SIZE,
- &idev->busaddr);
- if (!ringarea)
- goto out;
-
- hwmap = (struct ring_descr_hw *)ringarea;
- idev->rx_ring = vlsi_alloc_ring(idev->pdev, hwmap, ringsize[1],
- XFER_BUF_SIZE, PCI_DMA_FROMDEVICE);
- if (idev->rx_ring == NULL)
- goto out_unmap;
-
- hwmap += MAX_RING_DESCR;
- idev->tx_ring = vlsi_alloc_ring(idev->pdev, hwmap, ringsize[0],
- XFER_BUF_SIZE, PCI_DMA_TODEVICE);
- if (idev->tx_ring == NULL)
- goto out_free_rx;
-
- idev->virtaddr = ringarea;
- return 0;
-
-out_free_rx:
- vlsi_free_ring(idev->rx_ring);
-out_unmap:
- idev->rx_ring = idev->tx_ring = NULL;
- pci_free_consistent(idev->pdev, HW_RING_AREA_SIZE, ringarea, idev->busaddr);
- idev->busaddr = 0;
-out:
- return -ENOMEM;
-}
-
-static int vlsi_destroy_hwif(vlsi_irda_dev_t *idev)
-{
- vlsi_free_ring(idev->rx_ring);
- vlsi_free_ring(idev->tx_ring);
- idev->rx_ring = idev->tx_ring = NULL;
-
- if (idev->busaddr)
- pci_free_consistent(idev->pdev,HW_RING_AREA_SIZE,idev->virtaddr,idev->busaddr);
-
- idev->virtaddr = NULL;
- idev->busaddr = 0;
-
- return 0;
-}
-
-/********************************************************/
-
-static int vlsi_process_rx(struct vlsi_ring *r, struct ring_descr *rd)
-{
- u16 status;
- int crclen, len = 0;
- struct sk_buff *skb;
- int ret = 0;
- struct net_device *ndev = pci_get_drvdata(r->pdev);
- vlsi_irda_dev_t *idev = netdev_priv(ndev);
-
- pci_dma_sync_single_for_cpu(r->pdev, rd_get_addr(rd), r->len, r->dir);
- /* dma buffer now owned by the CPU */
- status = rd_get_status(rd);
- if (status & RD_RX_ERROR) {
- if (status & RD_RX_OVER)
- ret |= VLSI_RX_OVER;
- if (status & RD_RX_LENGTH)
- ret |= VLSI_RX_LENGTH;
- if (status & RD_RX_PHYERR)
- ret |= VLSI_RX_FRAME;
- if (status & RD_RX_CRCERR)
- ret |= VLSI_RX_CRC;
- goto done;
- }
-
- len = rd_get_count(rd);
- crclen = (idev->mode==IFF_FIR) ? sizeof(u32) : sizeof(u16);
- len -= crclen; /* remove trailing CRC */
- if (len <= 0) {
- pr_debug("%s: strange frame (len=%d)\n", __func__, len);
- ret |= VLSI_RX_DROP;
- goto done;
- }
-
- if (idev->mode == IFF_SIR) { /* hw checks CRC in MIR, FIR mode */
-
- /* rd->buf is a streaming PCI_DMA_FROMDEVICE map. Doing the
- * endian-adjustment there just in place will dirty a cache line
- * which belongs to the map and thus we must be sure it will
- * get flushed before giving the buffer back to hardware.
- * vlsi_fill_rx() will do this anyway - but here we rely on.
- */
- le16_to_cpus(rd->buf+len);
- if (irda_calc_crc16(INIT_FCS,rd->buf,len+crclen) != GOOD_FCS) {
- pr_debug("%s: crc error\n", __func__);
- ret |= VLSI_RX_CRC;
- goto done;
- }
- }
-
- if (!rd->skb) {
- net_warn_ratelimited("%s: rx packet lost\n", __func__);
- ret |= VLSI_RX_DROP;
- goto done;
- }
-
- skb = rd->skb;
- rd->skb = NULL;
- skb->dev = ndev;
- skb_put_data(skb, rd->buf, len);
- skb_reset_mac_header(skb);
- if (in_interrupt())
- netif_rx(skb);
- else
- netif_rx_ni(skb);
-
-done:
- rd_set_status(rd, 0);
- rd_set_count(rd, 0);
- /* buffer still owned by CPU */
-
- return (ret) ? -ret : len;
-}
-
-static void vlsi_fill_rx(struct vlsi_ring *r)
-{
- struct ring_descr *rd;
-
- for (rd = ring_last(r); rd != NULL; rd = ring_put(r)) {
- if (rd_is_active(rd)) {
- net_warn_ratelimited("%s: driver bug: rx descr race with hw\n",
- __func__);
- vlsi_ring_debug(r);
- break;
- }
- if (!rd->skb) {
- rd->skb = dev_alloc_skb(IRLAP_SKB_ALLOCSIZE);
- if (rd->skb) {
- skb_reserve(rd->skb,1);
- rd->skb->protocol = htons(ETH_P_IRDA);
- }
- else
- break; /* probably not worth logging? */
- }
- /* give dma buffer back to busmaster */
- pci_dma_sync_single_for_device(r->pdev, rd_get_addr(rd), r->len, r->dir);
- rd_activate(rd);
- }
-}
-
-static void vlsi_rx_interrupt(struct net_device *ndev)
-{
- vlsi_irda_dev_t *idev = netdev_priv(ndev);
- struct vlsi_ring *r = idev->rx_ring;
- struct ring_descr *rd;
- int ret;
-
- for (rd = ring_first(r); rd != NULL; rd = ring_get(r)) {
-
- if (rd_is_active(rd))
- break;
-
- ret = vlsi_process_rx(r, rd);
-
- if (ret < 0) {
- ret = -ret;
- ndev->stats.rx_errors++;
- if (ret & VLSI_RX_DROP)
- ndev->stats.rx_dropped++;
- if (ret & VLSI_RX_OVER)
- ndev->stats.rx_over_errors++;
- if (ret & VLSI_RX_LENGTH)
- ndev->stats.rx_length_errors++;
- if (ret & VLSI_RX_FRAME)
- ndev->stats.rx_frame_errors++;
- if (ret & VLSI_RX_CRC)
- ndev->stats.rx_crc_errors++;
- }
- else if (ret > 0) {
- ndev->stats.rx_packets++;
- ndev->stats.rx_bytes += ret;
- }
- }
-
- idev->last_rx = ktime_get(); /* remember "now" for later mtt delay */
-
- vlsi_fill_rx(r);
-
- if (ring_first(r) == NULL) {
- /* we are in big trouble, if this should ever happen */
- net_err_ratelimited("%s: rx ring exhausted!\n", __func__);
- vlsi_ring_debug(r);
- }
- else
- outw(0, ndev->base_addr+VLSI_PIO_PROMPT);
-}
-
-/* caller must have stopped the controller from busmastering */
-
-static void vlsi_unarm_rx(vlsi_irda_dev_t *idev)
-{
- struct net_device *ndev = pci_get_drvdata(idev->pdev);
- struct vlsi_ring *r = idev->rx_ring;
- struct ring_descr *rd;
- int ret;
-
- for (rd = ring_first(r); rd != NULL; rd = ring_get(r)) {
-
- ret = 0;
- if (rd_is_active(rd)) {
- rd_set_status(rd, 0);
- if (rd_get_count(rd)) {
- pr_debug("%s - dropping rx packet\n", __func__);
- ret = -VLSI_RX_DROP;
- }
- rd_set_count(rd, 0);
- pci_dma_sync_single_for_cpu(r->pdev, rd_get_addr(rd), r->len, r->dir);
- if (rd->skb) {
- dev_kfree_skb_any(rd->skb);
- rd->skb = NULL;
- }
- }
- else
- ret = vlsi_process_rx(r, rd);
-
- if (ret < 0) {
- ret = -ret;
- ndev->stats.rx_errors++;
- if (ret & VLSI_RX_DROP)
- ndev->stats.rx_dropped++;
- if (ret & VLSI_RX_OVER)
- ndev->stats.rx_over_errors++;
- if (ret & VLSI_RX_LENGTH)
- ndev->stats.rx_length_errors++;
- if (ret & VLSI_RX_FRAME)
- ndev->stats.rx_frame_errors++;
- if (ret & VLSI_RX_CRC)
- ndev->stats.rx_crc_errors++;
- }
- else if (ret > 0) {
- ndev->stats.rx_packets++;
- ndev->stats.rx_bytes += ret;
- }
- }
-}
-
-/********************************************************/
-
-static int vlsi_process_tx(struct vlsi_ring *r, struct ring_descr *rd)
-{
- u16 status;
- int len;
- int ret;
-
- pci_dma_sync_single_for_cpu(r->pdev, rd_get_addr(rd), r->len, r->dir);
- /* dma buffer now owned by the CPU */
- status = rd_get_status(rd);
- if (status & RD_TX_UNDRN)
- ret = VLSI_TX_FIFO;
- else
- ret = 0;
- rd_set_status(rd, 0);
-
- if (rd->skb) {
- len = rd->skb->len;
- dev_kfree_skb_any(rd->skb);
- rd->skb = NULL;
- }
- else /* tx-skb already freed? - should never happen */
- len = rd_get_count(rd); /* incorrect for SIR! (due to wrapping) */
-
- rd_set_count(rd, 0);
- /* dma buffer still owned by the CPU */
-
- return (ret) ? -ret : len;
-}
-
-static int vlsi_set_baud(vlsi_irda_dev_t *idev, unsigned iobase)
-{
- u16 nphyctl;
- u16 config;
- unsigned mode;
- int ret;
- int baudrate;
- int fifocnt;
-
- baudrate = idev->new_baud;
- pr_debug("%s: %d -> %d\n", __func__, idev->baud, idev->new_baud);
- if (baudrate == 4000000) {
- mode = IFF_FIR;
- config = IRCFG_FIR;
- nphyctl = PHYCTL_FIR;
- }
- else if (baudrate == 1152000) {
- mode = IFF_MIR;
- config = IRCFG_MIR | IRCFG_CRC16;
- nphyctl = PHYCTL_MIR(clksrc==3);
- }
- else {
- mode = IFF_SIR;
- config = IRCFG_SIR | IRCFG_SIRFILT | IRCFG_RXANY;
- switch(baudrate) {
- default:
- net_warn_ratelimited("%s: undefined baudrate %d - fallback to 9600!\n",
- __func__, baudrate);
- baudrate = 9600;
- /* fallthru */
- case 2400:
- case 9600:
- case 19200:
- case 38400:
- case 57600:
- case 115200:
- nphyctl = PHYCTL_SIR(baudrate,sirpulse,clksrc==3);
- break;
- }
- }
- config |= IRCFG_MSTR | IRCFG_ENRX;
-
- fifocnt = inw(iobase+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK;
- if (fifocnt != 0) {
- pr_debug("%s: rx fifo not empty(%d)\n", __func__, fifocnt);
- }
-
- outw(0, iobase+VLSI_PIO_IRENABLE);
- outw(config, iobase+VLSI_PIO_IRCFG);
- outw(nphyctl, iobase+VLSI_PIO_NPHYCTL);
- wmb();
- outw(IRENABLE_PHYANDCLOCK, iobase+VLSI_PIO_IRENABLE);
- mb();
-
- udelay(1); /* chip applies IRCFG on next rising edge of its 8MHz clock */
-
- /* read back settings for validation */
-
- config = inw(iobase+VLSI_PIO_IRENABLE) & IRENABLE_MASK;
-
- if (mode == IFF_FIR)
- config ^= IRENABLE_FIR_ON;
- else if (mode == IFF_MIR)
- config ^= (IRENABLE_MIR_ON|IRENABLE_CRC16_ON);
- else
- config ^= IRENABLE_SIR_ON;
-
- if (config != (IRENABLE_PHYANDCLOCK|IRENABLE_ENRXST)) {
- net_warn_ratelimited("%s: failed to set %s mode!\n",
- __func__,
- mode == IFF_SIR ? "SIR" :
- mode == IFF_MIR ? "MIR" : "FIR");
- ret = -1;
- }
- else {
- if (inw(iobase+VLSI_PIO_PHYCTL) != nphyctl) {
- net_warn_ratelimited("%s: failed to apply baudrate %d\n",
- __func__, baudrate);
- ret = -1;
- }
- else {
- idev->mode = mode;
- idev->baud = baudrate;
- idev->new_baud = 0;
- ret = 0;
- }
- }
-
- if (ret)
- vlsi_reg_debug(iobase,__func__);
-
- return ret;
-}
-
-static netdev_tx_t vlsi_hard_start_xmit(struct sk_buff *skb,
- struct net_device *ndev)
-{
- vlsi_irda_dev_t *idev = netdev_priv(ndev);
- struct vlsi_ring *r = idev->tx_ring;
- struct ring_descr *rd;
- unsigned long flags;
- unsigned iobase = ndev->base_addr;
- u8 status;
- u16 config;
- int mtt, diff;
- int len, speed;
- char *msg = NULL;
-
- speed = irda_get_next_speed(skb);
- spin_lock_irqsave(&idev->lock, flags);
- if (speed != -1 && speed != idev->baud) {
- netif_stop_queue(ndev);
- idev->new_baud = speed;
- status = RD_TX_CLRENTX; /* stop tx-ring after this frame */
- }
- else
- status = 0;
-
- if (skb->len == 0) {
- /* handle zero packets - should be speed change */
- if (status == 0) {
- msg = "bogus zero-length packet";
- goto drop_unlock;
- }
-
- /* due to the completely asynch tx operation we might have
- * IrLAP racing with the hardware here, f.e. if the controller
- * is just sending the last packet with current speed while
- * the LAP is already switching the speed using synchronous
- * len=0 packet. Immediate execution would lead to hw lockup
- * requiring a powercycle to reset. Good candidate to trigger
- * this is the final UA:RSP packet after receiving a DISC:CMD
- * when getting the LAP down.
- * Note that we are not protected by the queue_stop approach
- * because the final UA:RSP arrives _without_ request to apply
- * new-speed-after-this-packet - hence the driver doesn't know
- * this was the last packet and doesn't stop the queue. So the
- * forced switch to default speed from LAP gets through as fast
- * as only some 10 usec later while the UA:RSP is still processed
- * by the hardware and we would get screwed.
- */
-
- if (ring_first(idev->tx_ring) == NULL) {
- /* no race - tx-ring already empty */
- vlsi_set_baud(idev, iobase);
- netif_wake_queue(ndev);
- }
- else
- ;
- /* keep the speed change pending like it would
- * for any len>0 packet. tx completion interrupt
- * will apply it when the tx ring becomes empty.
- */
- spin_unlock_irqrestore(&idev->lock, flags);
- dev_kfree_skb_any(skb);
- return NETDEV_TX_OK;
- }
-
- /* sanity checks - simply drop the packet */
-
- rd = ring_last(r);
- if (!rd) {
- msg = "ring full, but queue wasn't stopped";
- goto drop_unlock;
- }
-
- if (rd_is_active(rd)) {
- msg = "entry still owned by hw";
- goto drop_unlock;
- }
-
- if (!rd->buf) {
- msg = "tx ring entry without pci buffer";
- goto drop_unlock;
- }
-
- if (rd->skb) {
- msg = "ring entry with old skb still attached";
- goto drop_unlock;
- }
-
- /* no need for serialization or interrupt disable during mtt */
- spin_unlock_irqrestore(&idev->lock, flags);
-
- if ((mtt = irda_get_mtt(skb)) > 0) {
- diff = ktime_us_delta(ktime_get(), idev->last_rx);
- if (mtt > diff)
- udelay(mtt - diff);
- /* must not sleep here - called under netif_tx_lock! */
- }
-
- /* tx buffer already owned by CPU due to pci_dma_sync_single_for_cpu()
- * after subsequent tx-completion
- */
-
- if (idev->mode == IFF_SIR) {
- status |= RD_TX_DISCRC; /* no hw-crc creation */
- len = async_wrap_skb(skb, rd->buf, r->len);
-
- /* Some rare worst case situation in SIR mode might lead to
- * potential buffer overflow. The wrapper detects this, returns
- * with a shortened frame (without FCS/EOF) but doesn't provide
- * any error indication about the invalid packet which we are
- * going to transmit.
- * Therefore we log if the buffer got filled to the point, where the
- * wrapper would abort, i.e. when there are less than 5 bytes left to
- * allow appending the FCS/EOF.
- */
-
- if (len >= r->len-5)
- net_warn_ratelimited("%s: possible buffer overflow with SIR wrapping!\n",
- __func__);
- }
- else {
- /* hw deals with MIR/FIR mode wrapping */
- status |= RD_TX_PULSE; /* send 2 us highspeed indication pulse */
- len = skb->len;
- if (len > r->len) {
- msg = "frame exceeds tx buffer length";
- goto drop;
- }
- else
- skb_copy_from_linear_data(skb, rd->buf, len);
- }
-
- rd->skb = skb; /* remember skb for tx-complete stats */
-
- rd_set_count(rd, len);
- rd_set_status(rd, status); /* not yet active! */
-
- /* give dma buffer back to busmaster-hw (flush caches to make
- * CPU-driven changes visible from the pci bus).
- */
-
- pci_dma_sync_single_for_device(r->pdev, rd_get_addr(rd), r->len, r->dir);
-
-/* Switching to TX mode here races with the controller
- * which may stop TX at any time when fetching an inactive descriptor
- * or one with CLR_ENTX set. So we switch on TX only, if TX was not running
- * _after_ the new descriptor was activated on the ring. This ensures
- * we will either find TX already stopped or we can be sure, there
- * will be a TX-complete interrupt even if the chip stopped doing
- * TX just after we found it still running. The ISR will then find
- * the non-empty ring and restart TX processing. The enclosing
- * spinlock provides the correct serialization to prevent race with isr.
- */
-
- spin_lock_irqsave(&idev->lock,flags);
-
- rd_activate(rd);
-
- if (!(inw(iobase+VLSI_PIO_IRENABLE) & IRENABLE_ENTXST)) {
- int fifocnt;
-
- fifocnt = inw(ndev->base_addr+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK;
- if (fifocnt != 0) {
- pr_debug("%s: rx fifo not empty(%d)\n",
- __func__, fifocnt);
- }
-
- config = inw(iobase+VLSI_PIO_IRCFG);
- mb();
- outw(config | IRCFG_ENTX, iobase+VLSI_PIO_IRCFG);
- wmb();
- outw(0, iobase+VLSI_PIO_PROMPT);
- }
-
- if (ring_put(r) == NULL) {
- netif_stop_queue(ndev);
- pr_debug("%s: tx ring full - queue stopped\n", __func__);
- }
- spin_unlock_irqrestore(&idev->lock, flags);
-
- return NETDEV_TX_OK;
-
-drop_unlock:
- spin_unlock_irqrestore(&idev->lock, flags);
-drop:
- net_warn_ratelimited("%s: dropping packet - %s\n", __func__, msg);
- dev_kfree_skb_any(skb);
- ndev->stats.tx_errors++;
- ndev->stats.tx_dropped++;
- /* Don't even think about returning NET_XMIT_DROP (=1) here!
- * In fact any retval!=0 causes the packet scheduler to requeue the
- * packet for later retry of transmission - which isn't exactly
- * what we want after we've just called dev_kfree_skb_any ;-)
- */
- return NETDEV_TX_OK;
-}
-
-static void vlsi_tx_interrupt(struct net_device *ndev)
-{
- vlsi_irda_dev_t *idev = netdev_priv(ndev);
- struct vlsi_ring *r = idev->tx_ring;
- struct ring_descr *rd;
- unsigned iobase;
- int ret;
- u16 config;
-
- for (rd = ring_first(r); rd != NULL; rd = ring_get(r)) {
-
- if (rd_is_active(rd))
- break;
-
- ret = vlsi_process_tx(r, rd);
-
- if (ret < 0) {
- ret = -ret;
- ndev->stats.tx_errors++;
- if (ret & VLSI_TX_DROP)
- ndev->stats.tx_dropped++;
- if (ret & VLSI_TX_FIFO)
- ndev->stats.tx_fifo_errors++;
- }
- else if (ret > 0){
- ndev->stats.tx_packets++;
- ndev->stats.tx_bytes += ret;
- }
- }
-
- iobase = ndev->base_addr;
-
- if (idev->new_baud && rd == NULL) /* tx ring empty and speed change pending */
- vlsi_set_baud(idev, iobase);
-
- config = inw(iobase+VLSI_PIO_IRCFG);
- if (rd == NULL) /* tx ring empty: re-enable rx */
- outw((config & ~IRCFG_ENTX) | IRCFG_ENRX, iobase+VLSI_PIO_IRCFG);
-
- else if (!(inw(iobase+VLSI_PIO_IRENABLE) & IRENABLE_ENTXST)) {
- int fifocnt;
-
- fifocnt = inw(iobase+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK;
- if (fifocnt != 0) {
- pr_debug("%s: rx fifo not empty(%d)\n",
- __func__, fifocnt);
- }
- outw(config | IRCFG_ENTX, iobase+VLSI_PIO_IRCFG);
- }
-
- outw(0, iobase+VLSI_PIO_PROMPT);
-
- if (netif_queue_stopped(ndev) && !idev->new_baud) {
- netif_wake_queue(ndev);
- pr_debug("%s: queue awoken\n", __func__);
- }
-}
-
-/* caller must have stopped the controller from busmastering */
-
-static void vlsi_unarm_tx(vlsi_irda_dev_t *idev)
-{
- struct net_device *ndev = pci_get_drvdata(idev->pdev);
- struct vlsi_ring *r = idev->tx_ring;
- struct ring_descr *rd;
- int ret;
-
- for (rd = ring_first(r); rd != NULL; rd = ring_get(r)) {
-
- ret = 0;
- if (rd_is_active(rd)) {
- rd_set_status(rd, 0);
- rd_set_count(rd, 0);
- pci_dma_sync_single_for_cpu(r->pdev, rd_get_addr(rd), r->len, r->dir);
- if (rd->skb) {
- dev_kfree_skb_any(rd->skb);
- rd->skb = NULL;
- }
- pr_debug("%s - dropping tx packet\n", __func__);
- ret = -VLSI_TX_DROP;
- }
- else
- ret = vlsi_process_tx(r, rd);
-
- if (ret < 0) {
- ret = -ret;
- ndev->stats.tx_errors++;
- if (ret & VLSI_TX_DROP)
- ndev->stats.tx_dropped++;
- if (ret & VLSI_TX_FIFO)
- ndev->stats.tx_fifo_errors++;
- }
- else if (ret > 0){
- ndev->stats.tx_packets++;
- ndev->stats.tx_bytes += ret;
- }
- }
-
-}
-
-/********************************************************/
-
-static int vlsi_start_clock(struct pci_dev *pdev)
-{
- u8 clkctl, lock;
- int i, count;
-
- if (clksrc < 2) { /* auto or PLL: try PLL */
- clkctl = CLKCTL_PD_INV | CLKCTL_CLKSTP;
- pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl);
-
- /* procedure to detect PLL lock synchronisation:
- * after 0.5 msec initial delay we expect to find 3 PLL lock
- * indications within 10 msec for successful PLL detection.
- */
- udelay(500);
- count = 0;
- for (i = 500; i <= 10000; i += 50) { /* max 10 msec */
- pci_read_config_byte(pdev, VLSI_PCI_CLKCTL, &lock);
- if (lock&CLKCTL_LOCK) {
- if (++count >= 3)
- break;
- }
- udelay(50);
- }
- if (count < 3) {
- if (clksrc == 1) { /* explicitly asked for PLL hence bail out */
- net_err_ratelimited("%s: no PLL or failed to lock!\n",
- __func__);
- clkctl = CLKCTL_CLKSTP;
- pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl);
- return -1;
- }
- else /* was: clksrc=0(auto) */
- clksrc = 3; /* fallback to 40MHz XCLK (OB800) */
-
- pr_debug("%s: PLL not locked, fallback to clksrc=%d\n",
- __func__, clksrc);
- }
- else
- clksrc = 1; /* got successful PLL lock */
- }
-
- if (clksrc != 1) {
- /* we get here if either no PLL detected in auto-mode or
- an external clock source was explicitly specified */
-
- clkctl = CLKCTL_EXTCLK | CLKCTL_CLKSTP;
- if (clksrc == 3)
- clkctl |= CLKCTL_XCKSEL;
- pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl);
-
- /* no way to test for working XCLK */
- }
- else
- pci_read_config_byte(pdev, VLSI_PCI_CLKCTL, &clkctl);
-
- /* ok, now going to connect the chip with the clock source */
-
- clkctl &= ~CLKCTL_CLKSTP;
- pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl);
-
- return 0;
-}
-
-static void vlsi_stop_clock(struct pci_dev *pdev)
-{
- u8 clkctl;
-
- /* disconnect chip from clock source */
- pci_read_config_byte(pdev, VLSI_PCI_CLKCTL, &clkctl);
- clkctl |= CLKCTL_CLKSTP;
- pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl);
-
- /* disable all clock sources */
- clkctl &= ~(CLKCTL_EXTCLK | CLKCTL_PD_INV);
- pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl);
-}
-
-/********************************************************/
-
-/* writing all-zero to the VLSI PCI IO register area seems to prevent
- * some occasional situations where the hardware fails (symptoms are
- * what appears as stalled tx/rx state machines, i.e. everything ok for
- * receive or transmit but hw makes no progress or is unable to access
- * the bus memory locations).
- * Best place to call this is immediately after/before the internal clock
- * gets started/stopped.
- */
-
-static inline void vlsi_clear_regs(unsigned iobase)
-{
- unsigned i;
- const unsigned chip_io_extent = 32;
-
- for (i = 0; i < chip_io_extent; i += sizeof(u16))
- outw(0, iobase + i);
-}
-
-static int vlsi_init_chip(struct pci_dev *pdev)
-{
- struct net_device *ndev = pci_get_drvdata(pdev);
- vlsi_irda_dev_t *idev = netdev_priv(ndev);
- unsigned iobase;
- u16 ptr;
-
- /* start the clock and clean the registers */
-
- if (vlsi_start_clock(pdev)) {
- net_err_ratelimited("%s: no valid clock source\n", __func__);
- return -1;
- }
- iobase = ndev->base_addr;
- vlsi_clear_regs(iobase);
-
- outb(IRINTR_INT_MASK, iobase+VLSI_PIO_IRINTR); /* w/c pending IRQ, disable all INT */
-
- outw(0, iobase+VLSI_PIO_IRENABLE); /* disable IrPHY-interface */
-
- /* disable everything, particularly IRCFG_MSTR - (also resetting the RING_PTR) */
-
- outw(0, iobase+VLSI_PIO_IRCFG);
- wmb();
-
- outw(MAX_PACKET_LENGTH, iobase+VLSI_PIO_MAXPKT); /* max possible value=0x0fff */
-
- outw(BUS_TO_RINGBASE(idev->busaddr), iobase+VLSI_PIO_RINGBASE);
-
- outw(TX_RX_TO_RINGSIZE(idev->tx_ring->size, idev->rx_ring->size),
- iobase+VLSI_PIO_RINGSIZE);
-
- ptr = inw(iobase+VLSI_PIO_RINGPTR);
- atomic_set(&idev->rx_ring->head, RINGPTR_GET_RX(ptr));
- atomic_set(&idev->rx_ring->tail, RINGPTR_GET_RX(ptr));
- atomic_set(&idev->tx_ring->head, RINGPTR_GET_TX(ptr));
- atomic_set(&idev->tx_ring->tail, RINGPTR_GET_TX(ptr));
-
- vlsi_set_baud(idev, iobase); /* idev->new_baud used as provided by caller */
-
- outb(IRINTR_INT_MASK, iobase+VLSI_PIO_IRINTR); /* just in case - w/c pending IRQ's */
- wmb();
-
- /* DO NOT BLINDLY ENABLE IRINTR_ACTEN!
- * basically every received pulse fires an ACTIVITY-INT
- * leading to >>1000 INT's per second instead of few 10
- */
-
- outb(IRINTR_RPKTEN|IRINTR_TPKTEN, iobase+VLSI_PIO_IRINTR);
-
- return 0;
-}
-
-static int vlsi_start_hw(vlsi_irda_dev_t *idev)
-{
- struct pci_dev *pdev = idev->pdev;
- struct net_device *ndev = pci_get_drvdata(pdev);
- unsigned iobase = ndev->base_addr;
- u8 byte;
-
- /* we don't use the legacy UART, disable its address decoding */
-
- pci_read_config_byte(pdev, VLSI_PCI_IRMISC, &byte);
- byte &= ~(IRMISC_UARTEN | IRMISC_UARTTST);
- pci_write_config_byte(pdev, VLSI_PCI_IRMISC, byte);
-
- /* enable PCI busmaster access to our 16MB page */
-
- pci_write_config_byte(pdev, VLSI_PCI_MSTRPAGE, MSTRPAGE_VALUE);
- pci_set_master(pdev);
-
- if (vlsi_init_chip(pdev) < 0) {
- pci_disable_device(pdev);
- return -1;
- }
-
- vlsi_fill_rx(idev->rx_ring);
-
- idev->last_rx = ktime_get(); /* first mtt may start from now on */
-
- outw(0, iobase+VLSI_PIO_PROMPT); /* kick hw state machine */
-
- return 0;
-}
-
-static int vlsi_stop_hw(vlsi_irda_dev_t *idev)
-{
- struct pci_dev *pdev = idev->pdev;
- struct net_device *ndev = pci_get_drvdata(pdev);
- unsigned iobase = ndev->base_addr;
- unsigned long flags;
-
- spin_lock_irqsave(&idev->lock,flags);
- outw(0, iobase+VLSI_PIO_IRENABLE);
- outw(0, iobase+VLSI_PIO_IRCFG); /* disable everything */
-
- /* disable and w/c irqs */
- outb(0, iobase+VLSI_PIO_IRINTR);
- wmb();
- outb(IRINTR_INT_MASK, iobase+VLSI_PIO_IRINTR);
- spin_unlock_irqrestore(&idev->lock,flags);
-
- vlsi_unarm_tx(idev);
- vlsi_unarm_rx(idev);
-
- vlsi_clear_regs(iobase);
- vlsi_stop_clock(pdev);
-
- pci_disable_device(pdev);
-
- return 0;
-}
-
-/**************************************************************/
-
-static void vlsi_tx_timeout(struct net_device *ndev)
-{
- vlsi_irda_dev_t *idev = netdev_priv(ndev);
-
-
- vlsi_reg_debug(ndev->base_addr, __func__);
- vlsi_ring_debug(idev->tx_ring);
-
- if (netif_running(ndev))
- netif_stop_queue(ndev);
-
- vlsi_stop_hw(idev);
-
- /* now simply restart the whole thing */
-
- if (!idev->new_baud)
- idev->new_baud = idev->baud; /* keep current baudrate */
-
- if (vlsi_start_hw(idev))
- net_err_ratelimited("%s: failed to restart hw - %s(%s) unusable!\n",
- __func__, pci_name(idev->pdev), ndev->name);
- else
- netif_start_queue(ndev);
-}
-
-static int vlsi_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
-{
- vlsi_irda_dev_t *idev = netdev_priv(ndev);
- struct if_irda_req *irq = (struct if_irda_req *) rq;
- unsigned long flags;
- u16 fifocnt;
- int ret = 0;
-
- switch (cmd) {
- case SIOCSBANDWIDTH:
- if (!capable(CAP_NET_ADMIN)) {
- ret = -EPERM;
- break;
- }
- spin_lock_irqsave(&idev->lock, flags);
- idev->new_baud = irq->ifr_baudrate;
- /* when called from userland there might be a minor race window here
- * if the stack tries to change speed concurrently - which would be
- * pretty strange anyway with the userland having full control...
- */
- vlsi_set_baud(idev, ndev->base_addr);
- spin_unlock_irqrestore(&idev->lock, flags);
- break;
- case SIOCSMEDIABUSY:
- if (!capable(CAP_NET_ADMIN)) {
- ret = -EPERM;
- break;
- }
- irda_device_set_media_busy(ndev, TRUE);
- break;
- case SIOCGRECEIVING:
- /* the best we can do: check whether there are any bytes in rx fifo.
- * The trustable window (in case some data arrives just afterwards)
- * may be as short as 1usec or so at 4Mbps.
- */
- fifocnt = inw(ndev->base_addr+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK;
- irq->ifr_receiving = (fifocnt!=0) ? 1 : 0;
- break;
- default:
- net_warn_ratelimited("%s: notsupp - cmd=%04x\n",
- __func__, cmd);
- ret = -EOPNOTSUPP;
- }
-
- return ret;
-}
-
-/********************************************************/
-
-static irqreturn_t vlsi_interrupt(int irq, void *dev_instance)
-{
- struct net_device *ndev = dev_instance;
- vlsi_irda_dev_t *idev = netdev_priv(ndev);
- unsigned iobase;
- u8 irintr;
- int boguscount = 5;
- unsigned long flags;
- int handled = 0;
-
- iobase = ndev->base_addr;
- spin_lock_irqsave(&idev->lock,flags);
- do {
- irintr = inb(iobase+VLSI_PIO_IRINTR);
- mb();
- outb(irintr, iobase+VLSI_PIO_IRINTR); /* acknowledge asap */
-
- if (!(irintr&=IRINTR_INT_MASK)) /* not our INT - probably shared */
- break;
-
- handled = 1;
-
- if (unlikely(!(irintr & ~IRINTR_ACTIVITY)))
- break; /* nothing todo if only activity */
-
- if (irintr&IRINTR_RPKTINT)
- vlsi_rx_interrupt(ndev);
-
- if (irintr&IRINTR_TPKTINT)
- vlsi_tx_interrupt(ndev);
-
- } while (--boguscount > 0);
- spin_unlock_irqrestore(&idev->lock,flags);
-
- if (boguscount <= 0)
- net_info_ratelimited("%s: too much work in interrupt!\n",
- __func__);
- return IRQ_RETVAL(handled);
-}
-
-/********************************************************/
-
-static int vlsi_open(struct net_device *ndev)
-{
- vlsi_irda_dev_t *idev = netdev_priv(ndev);
- int err = -EAGAIN;
- char hwname[32];
-
- if (pci_request_regions(idev->pdev, drivername)) {
- net_warn_ratelimited("%s: io resource busy\n", __func__);
- goto errout;
- }
- ndev->base_addr = pci_resource_start(idev->pdev,0);
- ndev->irq = idev->pdev->irq;
-
- /* under some rare occasions the chip apparently comes up with
- * IRQ's pending. We better w/c pending IRQ and disable them all
- */
-
- outb(IRINTR_INT_MASK, ndev->base_addr+VLSI_PIO_IRINTR);
-
- if (request_irq(ndev->irq, vlsi_interrupt, IRQF_SHARED,
- drivername, ndev)) {
- net_warn_ratelimited("%s: couldn't get IRQ: %d\n",
- __func__, ndev->irq);
- goto errout_io;
- }
-
- if ((err = vlsi_create_hwif(idev)) != 0)
- goto errout_irq;
-
- sprintf(hwname, "VLSI-FIR @ 0x%04x", (unsigned)ndev->base_addr);
- idev->irlap = irlap_open(ndev,&idev->qos,hwname);
- if (!idev->irlap)
- goto errout_free_ring;
-
- idev->last_rx = ktime_get(); /* first mtt may start from now on */
-
- idev->new_baud = 9600; /* start with IrPHY using 9600(SIR) mode */
-
- if ((err = vlsi_start_hw(idev)) != 0)
- goto errout_close_irlap;
-
- netif_start_queue(ndev);
-
- net_info_ratelimited("%s: device %s operational\n",
- __func__, ndev->name);
-
- return 0;
-
-errout_close_irlap:
- irlap_close(idev->irlap);
-errout_free_ring:
- vlsi_destroy_hwif(idev);
-errout_irq:
- free_irq(ndev->irq,ndev);
-errout_io:
- pci_release_regions(idev->pdev);
-errout:
- return err;
-}
-
-static int vlsi_close(struct net_device *ndev)
-{
- vlsi_irda_dev_t *idev = netdev_priv(ndev);
-
- netif_stop_queue(ndev);
-
- if (idev->irlap)
- irlap_close(idev->irlap);
- idev->irlap = NULL;
-
- vlsi_stop_hw(idev);
-
- vlsi_destroy_hwif(idev);
-
- free_irq(ndev->irq,ndev);
-
- pci_release_regions(idev->pdev);
-
- net_info_ratelimited("%s: device %s stopped\n", __func__, ndev->name);
-
- return 0;
-}
-
-static const struct net_device_ops vlsi_netdev_ops = {
- .ndo_open = vlsi_open,
- .ndo_stop = vlsi_close,
- .ndo_start_xmit = vlsi_hard_start_xmit,
- .ndo_do_ioctl = vlsi_ioctl,
- .ndo_tx_timeout = vlsi_tx_timeout,
-};
-
-static int vlsi_irda_init(struct net_device *ndev)
-{
- vlsi_irda_dev_t *idev = netdev_priv(ndev);
- struct pci_dev *pdev = idev->pdev;
-
- ndev->irq = pdev->irq;
- ndev->base_addr = pci_resource_start(pdev,0);
-
- /* PCI busmastering
- * see include file for details why we need these 2 masks, in this order!
- */
-
- if (pci_set_dma_mask(pdev,DMA_MASK_USED_BY_HW) ||
- pci_set_dma_mask(pdev,DMA_MASK_MSTRPAGE)) {
- net_err_ratelimited("%s: aborting due to PCI BM-DMA address limitations\n",
- __func__);
- return -1;
- }
-
- irda_init_max_qos_capabilies(&idev->qos);
-
- /* the VLSI82C147 does not support 576000! */
-
- idev->qos.baud_rate.bits = IR_2400 | IR_9600
- | IR_19200 | IR_38400 | IR_57600 | IR_115200
- | IR_1152000 | (IR_4000000 << 8);
-
- idev->qos.min_turn_time.bits = qos_mtt_bits;
-
- irda_qos_bits_to_value(&idev->qos);
-
- /* currently no public media definitions for IrDA */
-
- ndev->flags |= IFF_PORTSEL | IFF_AUTOMEDIA;
- ndev->if_port = IF_PORT_UNKNOWN;
-
- ndev->netdev_ops = &vlsi_netdev_ops;
- ndev->watchdog_timeo = 500*HZ/1000; /* max. allowed turn time for IrLAP */
-
- SET_NETDEV_DEV(ndev, &pdev->dev);
-
- return 0;
-}
-
-/**************************************************************/
-
-static int
-vlsi_irda_probe(struct pci_dev *pdev, const struct pci_device_id *id)
-{
- struct net_device *ndev;
- vlsi_irda_dev_t *idev;
-
- if (pci_enable_device(pdev))
- goto out;
- else
- pdev->current_state = 0; /* hw must be running now */
-
- net_info_ratelimited("%s: IrDA PCI controller %s detected\n",
- drivername, pci_name(pdev));
-
- if ( !pci_resource_start(pdev,0) ||
- !(pci_resource_flags(pdev,0) & IORESOURCE_IO) ) {
- net_err_ratelimited("%s: bar 0 invalid", __func__);
- goto out_disable;
- }
-
- ndev = alloc_irdadev(sizeof(*idev));
- if (ndev==NULL) {
- net_err_ratelimited("%s: Unable to allocate device memory.\n",
- __func__);
- goto out_disable;
- }
-
- idev = netdev_priv(ndev);
-
- spin_lock_init(&idev->lock);
- mutex_init(&idev->mtx);
- mutex_lock(&idev->mtx);
- idev->pdev = pdev;
-
- if (vlsi_irda_init(ndev) < 0)
- goto out_freedev;
-
- if (register_netdev(ndev) < 0) {
- net_err_ratelimited("%s: register_netdev failed\n", __func__);
- goto out_freedev;
- }
-
- if (vlsi_proc_root != NULL) {
- struct proc_dir_entry *ent;
-
- ent = proc_create_data(ndev->name, S_IFREG|S_IRUGO,
- vlsi_proc_root, VLSI_PROC_FOPS, ndev);
- if (!ent) {
- net_warn_ratelimited("%s: failed to create proc entry\n",
- __func__);
- } else {
- proc_set_size(ent, 0);
- }
- idev->proc_entry = ent;
- }
- net_info_ratelimited("%s: registered device %s\n",
- drivername, ndev->name);
-
- pci_set_drvdata(pdev, ndev);
- mutex_unlock(&idev->mtx);
-
- return 0;
-
-out_freedev:
- mutex_unlock(&idev->mtx);
- free_netdev(ndev);
-out_disable:
- pci_disable_device(pdev);
-out:
- return -ENODEV;
-}
-
-static void vlsi_irda_remove(struct pci_dev *pdev)
-{
- struct net_device *ndev = pci_get_drvdata(pdev);
- vlsi_irda_dev_t *idev;
-
- if (!ndev) {
- net_err_ratelimited("%s: lost netdevice?\n", drivername);
- return;
- }
-
- unregister_netdev(ndev);
-
- idev = netdev_priv(ndev);
- mutex_lock(&idev->mtx);
- if (idev->proc_entry) {
- remove_proc_entry(ndev->name, vlsi_proc_root);
- idev->proc_entry = NULL;
- }
- mutex_unlock(&idev->mtx);
-
- free_netdev(ndev);
-
- net_info_ratelimited("%s: %s removed\n", drivername, pci_name(pdev));
-}
-
-#ifdef CONFIG_PM
-
-/* The Controller doesn't provide PCI PM capabilities as defined by PCI specs.
- * Some of the Linux PCI-PM code however depends on this, for example in
- * pci_set_power_state(). So we have to take care to perform the required
- * operations on our own (particularly reflecting the pdev->current_state)
- * otherwise we might get cheated by pci-pm.
- */
-
-
-static int vlsi_irda_suspend(struct pci_dev *pdev, pm_message_t state)
-{
- struct net_device *ndev = pci_get_drvdata(pdev);
- vlsi_irda_dev_t *idev;
-
- if (!ndev) {
- net_err_ratelimited("%s - %s: no netdevice\n",
- __func__, pci_name(pdev));
- return 0;
- }
- idev = netdev_priv(ndev);
- mutex_lock(&idev->mtx);
- if (pdev->current_state != 0) { /* already suspended */
- if (state.event > pdev->current_state) { /* simply go deeper */
- pci_set_power_state(pdev, pci_choose_state(pdev, state));
- pdev->current_state = state.event;
- }
- else
- net_err_ratelimited("%s - %s: invalid suspend request %u -> %u\n",
- __func__, pci_name(pdev),
- pdev->current_state, state.event);
- mutex_unlock(&idev->mtx);
- return 0;
- }
-
- if (netif_running(ndev)) {
- netif_device_detach(ndev);
- vlsi_stop_hw(idev);
- pci_save_state(pdev);
- if (!idev->new_baud)
- /* remember speed settings to restore on resume */
- idev->new_baud = idev->baud;
- }
-
- pci_set_power_state(pdev, pci_choose_state(pdev, state));
- pdev->current_state = state.event;
- idev->resume_ok = 1;
- mutex_unlock(&idev->mtx);
- return 0;
-}
-
-static int vlsi_irda_resume(struct pci_dev *pdev)
-{
- struct net_device *ndev = pci_get_drvdata(pdev);
- vlsi_irda_dev_t *idev;
-
- if (!ndev) {
- net_err_ratelimited("%s - %s: no netdevice\n",
- __func__, pci_name(pdev));
- return 0;
- }
- idev = netdev_priv(ndev);
- mutex_lock(&idev->mtx);
- if (pdev->current_state == 0) {
- mutex_unlock(&idev->mtx);
- net_warn_ratelimited("%s - %s: already resumed\n",
- __func__, pci_name(pdev));
- return 0;
- }
-
- pci_set_power_state(pdev, PCI_D0);
- pdev->current_state = PM_EVENT_ON;
-
- if (!idev->resume_ok) {
- /* should be obsolete now - but used to happen due to:
- * - pci layer initially setting pdev->current_state = 4 (unknown)
- * - pci layer did not walk the save_state-tree (might be APM problem)
- * so we could not refuse to suspend from undefined state
- * - vlsi_irda_suspend detected invalid state and refused to save
- * configuration for resume - but was too late to stop suspending
- * - vlsi_irda_resume got screwed when trying to resume from garbage
- *
- * now we explicitly set pdev->current_state = 0 after enabling the
- * device and independently resume_ok should catch any garbage config.
- */
- net_warn_ratelimited("%s - hm, nothing to resume?\n", __func__);
- mutex_unlock(&idev->mtx);
- return 0;
- }
-
- if (netif_running(ndev)) {
- pci_restore_state(pdev);
- vlsi_start_hw(idev);
- netif_device_attach(ndev);
- }
- idev->resume_ok = 0;
- mutex_unlock(&idev->mtx);
- return 0;
-}
-
-#endif /* CONFIG_PM */
-
-/*********************************************************/
-
-static struct pci_driver vlsi_irda_driver = {
- .name = drivername,
- .id_table = vlsi_irda_table,
- .probe = vlsi_irda_probe,
- .remove = vlsi_irda_remove,
-#ifdef CONFIG_PM
- .suspend = vlsi_irda_suspend,
- .resume = vlsi_irda_resume,
-#endif
-};
-
-#define PROC_DIR ("driver/" DRIVER_NAME)
-
-static int __init vlsi_mod_init(void)
-{
- int i, ret;
-
- if (clksrc < 0 || clksrc > 3) {
- net_err_ratelimited("%s: invalid clksrc=%d\n",
- drivername, clksrc);
- return -1;
- }
-
- for (i = 0; i < 2; i++) {
- switch(ringsize[i]) {
- case 4:
- case 8:
- case 16:
- case 32:
- case 64:
- break;
- default:
- net_warn_ratelimited("%s: invalid %s ringsize %d, using default=8\n",
- drivername,
- i ? "rx" : "tx",
- ringsize[i]);
- ringsize[i] = 8;
- break;
- }
- }
-
- sirpulse = !!sirpulse;
-
- /* proc_mkdir returns NULL if !CONFIG_PROC_FS.
- * Failure to create the procfs entry is handled like running
- * without procfs - it's not required for the driver to work.
- */
- vlsi_proc_root = proc_mkdir(PROC_DIR, NULL);
-
- ret = pci_register_driver(&vlsi_irda_driver);
-
- if (ret && vlsi_proc_root)
- remove_proc_entry(PROC_DIR, NULL);
- return ret;
-
-}
-
-static void __exit vlsi_mod_exit(void)
-{
- pci_unregister_driver(&vlsi_irda_driver);
- if (vlsi_proc_root)
- remove_proc_entry(PROC_DIR, NULL);
-}
-
-module_init(vlsi_mod_init);
-module_exit(vlsi_mod_exit);
diff --git a/drivers/staging/irda/drivers/vlsi_ir.h b/drivers/staging/irda/drivers/vlsi_ir.h
deleted file mode 100644
index f9db2ce4c5c6..000000000000
--- a/drivers/staging/irda/drivers/vlsi_ir.h
+++ /dev/null
@@ -1,757 +0,0 @@
-
-/*********************************************************************
- *
- * vlsi_ir.h: VLSI82C147 PCI IrDA controller driver for Linux
- *
- * Version: 0.5
- *
- * Copyright (c) 2001-2003 Martin Diehl
- *
- * 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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#ifndef IRDA_VLSI_FIR_H
-#define IRDA_VLSI_FIR_H
-
-/* ================================================================
- * compatibility stuff
- */
-
-/* definitions not present in pci_ids.h */
-
-#ifndef PCI_CLASS_WIRELESS_IRDA
-#define PCI_CLASS_WIRELESS_IRDA 0x0d00
-#endif
-
-#ifndef PCI_CLASS_SUBCLASS_MASK
-#define PCI_CLASS_SUBCLASS_MASK 0xffff
-#endif
-
-/* ================================================================ */
-
-/* non-standard PCI registers */
-
-enum vlsi_pci_regs {
- VLSI_PCI_CLKCTL = 0x40, /* chip clock input control */
- VLSI_PCI_MSTRPAGE = 0x41, /* addr [31:24] for all busmaster cycles */
- VLSI_PCI_IRMISC = 0x42 /* mainly legacy UART related */
-};
-
-/* ------------------------------------------ */
-
-/* VLSI_PCI_CLKCTL: Clock Control Register (u8, rw) */
-
-/* Three possible clock sources: either on-chip 48MHz PLL or
- * external clock applied to EXTCLK pin. External clock may
- * be either 48MHz or 40MHz, which is indicated by XCKSEL.
- * CLKSTP controls whether the selected clock source gets
- * connected to the IrDA block.
- *
- * On my HP OB-800 the BIOS sets external 40MHz clock as source
- * when IrDA enabled and I've never detected any PLL lock success.
- * Apparently the 14.3...MHz OSC input required for the PLL to work
- * is not connected and the 40MHz EXTCLK is provided externally.
- * At least this is what makes the driver working for me.
- */
-
-enum vlsi_pci_clkctl {
-
- /* PLL control */
-
- CLKCTL_PD_INV = 0x04, /* PD#: inverted power down signal,
- * i.e. PLL is powered, if PD_INV set */
- CLKCTL_LOCK = 0x40, /* (ro) set, if PLL is locked */
-
- /* clock source selection */
-
- CLKCTL_EXTCLK = 0x20, /* set to select external clock input, not PLL */
- CLKCTL_XCKSEL = 0x10, /* set to indicate EXTCLK is 40MHz, not 48MHz */
-
- /* IrDA block control */
-
- CLKCTL_CLKSTP = 0x80, /* set to disconnect from selected clock source */
- CLKCTL_WAKE = 0x08 /* set to enable wakeup feature: whenever IR activity
- * is detected, PD_INV gets set(?) and CLKSTP cleared */
-};
-
-/* ------------------------------------------ */
-
-/* VLSI_PCI_MSTRPAGE: Master Page Register (u8, rw) and busmastering stuff */
-
-#define DMA_MASK_USED_BY_HW 0xffffffff
-#define DMA_MASK_MSTRPAGE 0x00ffffff
-#define MSTRPAGE_VALUE (DMA_MASK_MSTRPAGE >> 24)
-
- /* PCI busmastering is somewhat special for this guy - in short:
- *
- * We select to operate using fixed MSTRPAGE=0, use ISA DMA
- * address restrictions to make the PCI BM api aware of this,
- * but ensure the hardware is dealing with real 32bit access.
- *
- * In detail:
- * The chip executes normal 32bit busmaster cycles, i.e.
- * drives all 32 address lines. These addresses however are
- * composed of [0:23] taken from various busaddr-pointers
- * and [24:31] taken from the MSTRPAGE register in the VLSI82C147
- * config space. Therefore _all_ busmastering must be
- * targeted to/from one single 16MB (busaddr-) superpage!
- * The point is to make sure all the allocations for memory
- * locations with busmaster access (ring descriptors, buffers)
- * are indeed bus-mappable to the same 16MB range (for x86 this
- * means they must reside in the same 16MB physical memory address
- * range). The only constraint we have which supports "several objects
- * mappable to common 16MB range" paradigma, is the old ISA DMA
- * restriction to the first 16MB of physical address range.
- * Hence the approach here is to enable PCI busmaster support using
- * the correct 32bit dma-mask used by the chip. Afterwards the device's
- * dma-mask gets restricted to 24bit, which must be honoured somehow by
- * all allocations for memory areas to be exposed to the chip ...
- *
- * Note:
- * Don't be surprised to get "Setting latency timer..." messages every
- * time when PCI busmastering is enabled for the chip.
- * The chip has its PCI latency timer RO fixed at 0 - which is not a
- * problem here, because it is never requesting _burst_ transactions.
- */
-
-/* ------------------------------------------ */
-
-/* VLSI_PCIIRMISC: IR Miscellaneous Register (u8, rw) */
-
-/* legacy UART emulation - not used by this driver - would require:
- * (see below for some register-value definitions)
- *
- * - IRMISC_UARTEN must be set to enable UART address decoding
- * - IRMISC_UARTSEL configured
- * - IRCFG_MASTER must be cleared
- * - IRCFG_SIR must be set
- * - IRENABLE_PHYANDCLOCK must be asserted 0->1 (and hence IRENABLE_SIR_ON)
- */
-
-enum vlsi_pci_irmisc {
-
- /* IR transceiver control */
-
- IRMISC_IRRAIL = 0x40, /* (ro?) IR rail power indication (and control?)
- * 0=3.3V / 1=5V. Probably set during power-on?
- * unclear - not touched by driver */
- IRMISC_IRPD = 0x08, /* transceiver power down, if set */
-
- /* legacy UART control */
-
- IRMISC_UARTTST = 0x80, /* UART test mode - "always write 0" */
- IRMISC_UARTEN = 0x04, /* enable UART address decoding */
-
- /* bits [1:0] IRMISC_UARTSEL to select legacy UART address */
-
- IRMISC_UARTSEL_3f8 = 0x00,
- IRMISC_UARTSEL_2f8 = 0x01,
- IRMISC_UARTSEL_3e8 = 0x02,
- IRMISC_UARTSEL_2e8 = 0x03
-};
-
-/* ================================================================ */
-
-/* registers mapped to 32 byte PCI IO space */
-
-/* note: better access all registers at the indicated u8/u16 size
- * although some of them contain only 1 byte of information.
- * some of them (particaluarly PROMPT and IRCFG) ignore
- * access when using the wrong addressing mode!
- */
-
-enum vlsi_pio_regs {
- VLSI_PIO_IRINTR = 0x00, /* interrupt enable/request (u8, rw) */
- VLSI_PIO_RINGPTR = 0x02, /* rx/tx ring pointer (u16, ro) */
- VLSI_PIO_RINGBASE = 0x04, /* [23:10] of ring address (u16, rw) */
- VLSI_PIO_RINGSIZE = 0x06, /* rx/tx ring size (u16, rw) */
- VLSI_PIO_PROMPT = 0x08, /* triggers ring processing (u16, wo) */
- /* 0x0a-0x0f: reserved / duplicated UART regs */
- VLSI_PIO_IRCFG = 0x10, /* configuration select (u16, rw) */
- VLSI_PIO_SIRFLAG = 0x12, /* BOF/EOF for filtered SIR (u16, ro) */
- VLSI_PIO_IRENABLE = 0x14, /* enable and status register (u16, rw/ro) */
- VLSI_PIO_PHYCTL = 0x16, /* physical layer current status (u16, ro) */
- VLSI_PIO_NPHYCTL = 0x18, /* next physical layer select (u16, rw) */
- VLSI_PIO_MAXPKT = 0x1a, /* [11:0] max len for packet receive (u16, rw) */
- VLSI_PIO_RCVBCNT = 0x1c /* current receive-FIFO byte count (u16, ro) */
- /* 0x1e-0x1f: reserved / duplicated UART regs */
-};
-
-/* ------------------------------------------ */
-
-/* VLSI_PIO_IRINTR: Interrupt Register (u8, rw) */
-
-/* enable-bits:
- * 1 = enable / 0 = disable
- * interrupt condition bits:
- * set according to corresponding interrupt source
- * (regardless of the state of the enable bits)
- * enable bit status indicates whether interrupt gets raised
- * write-to-clear
- * note: RPKTINT and TPKTINT behave different in legacy UART mode (which we don't use :-)
- */
-
-enum vlsi_pio_irintr {
- IRINTR_ACTEN = 0x80, /* activity interrupt enable */
- IRINTR_ACTIVITY = 0x40, /* activity monitor (traffic detected) */
- IRINTR_RPKTEN = 0x20, /* receive packet interrupt enable*/
- IRINTR_RPKTINT = 0x10, /* rx-packet transferred from fifo to memory finished */
- IRINTR_TPKTEN = 0x08, /* transmit packet interrupt enable */
- IRINTR_TPKTINT = 0x04, /* last bit of tx-packet+crc shifted to ir-pulser */
- IRINTR_OE_EN = 0x02, /* UART rx fifo overrun error interrupt enable */
- IRINTR_OE_INT = 0x01 /* UART rx fifo overrun error (read LSR to clear) */
-};
-
-/* we use this mask to check whether the (shared PCI) interrupt is ours */
-
-#define IRINTR_INT_MASK (IRINTR_ACTIVITY|IRINTR_RPKTINT|IRINTR_TPKTINT)
-
-/* ------------------------------------------ */
-
-/* VLSI_PIO_RINGPTR: Ring Pointer Read-Back Register (u16, ro) */
-
-/* _both_ ring pointers are indices relative to the _entire_ rx,tx-ring!
- * i.e. the referenced descriptor is located
- * at RINGBASE + PTR * sizeof(descr) for rx and tx
- * therefore, the tx-pointer has offset MAX_RING_DESCR
- */
-
-#define MAX_RING_DESCR 64 /* tx, rx rings may contain up to 64 descr each */
-
-#define RINGPTR_RX_MASK (MAX_RING_DESCR-1)
-#define RINGPTR_TX_MASK ((MAX_RING_DESCR-1)<<8)
-
-#define RINGPTR_GET_RX(p) ((p)&RINGPTR_RX_MASK)
-#define RINGPTR_GET_TX(p) (((p)&RINGPTR_TX_MASK)>>8)
-
-/* ------------------------------------------ */
-
-/* VLSI_PIO_RINGBASE: Ring Pointer Base Address Register (u16, ro) */
-
-/* Contains [23:10] part of the ring base (bus-) address
- * which must be 1k-alinged. [31:24] is taken from
- * VLSI_PCI_MSTRPAGE above.
- * The controller initiates non-burst PCI BM cycles to
- * fetch and update the descriptors in the ring.
- * Once fetched, the descriptor remains cached onchip
- * until it gets closed and updated due to the ring
- * processing state machine.
- * The entire ring area is split in rx and tx areas with each
- * area consisting of 64 descriptors of 8 bytes each.
- * The rx(tx) ring is located at ringbase+0 (ringbase+64*8).
- */
-
-#define BUS_TO_RINGBASE(p) (((p)>>10)&0x3fff)
-
-/* ------------------------------------------ */
-
-/* VLSI_PIO_RINGSIZE: Ring Size Register (u16, rw) */
-
-/* bit mask to indicate the ring size to be used for rx and tx.
- * possible values encoded bits
- * 4 0000
- * 8 0001
- * 16 0011
- * 32 0111
- * 64 1111
- * located at [15:12] for tx and [11:8] for rx ([7:0] unused)
- *
- * note: probably a good idea to have IRCFG_MSTR cleared when writing
- * this so the state machines are stopped and the RINGPTR is reset!
- */
-
-#define SIZE_TO_BITS(num) ((((num)-1)>>2)&0x0f)
-#define TX_RX_TO_RINGSIZE(tx,rx) ((SIZE_TO_BITS(tx)<<12)|(SIZE_TO_BITS(rx)<<8))
-#define RINGSIZE_TO_RXSIZE(rs) ((((rs)&0x0f00)>>6)+4)
-#define RINGSIZE_TO_TXSIZE(rs) ((((rs)&0xf000)>>10)+4)
-
-
-/* ------------------------------------------ */
-
-/* VLSI_PIO_PROMPT: Ring Prompting Register (u16, write-to-start) */
-
-/* writing any value kicks the ring processing state machines
- * for both tx, rx rings as follows:
- * - active rings (currently owning an active descriptor)
- * ignore the prompt and continue
- * - idle rings fetch the next descr from the ring and start
- * their processing
- */
-
-/* ------------------------------------------ */
-
-/* VLSI_PIO_IRCFG: IR Config Register (u16, rw) */
-
-/* notes:
- * - not more than one SIR/MIR/FIR bit must be set at any time
- * - SIR, MIR, FIR and CRC16 select the configuration which will
- * be applied on next 0->1 transition of IRENABLE_PHYANDCLOCK (see below).
- * - besides allowing the PCI interface to execute busmaster cycles
- * and therefore the ring SM to operate, the MSTR bit has side-effects:
- * when MSTR is cleared, the RINGPTR's get reset and the legacy UART mode
- * (in contrast to busmaster access mode) gets enabled.
- * - clearing ENRX or setting ENTX while data is received may stall the
- * receive fifo until ENRX reenabled _and_ another packet arrives
- * - SIRFILT means the chip performs the required unwrapping of hardware
- * headers (XBOF's, BOF/EOF) and un-escaping in the _receive_ direction.
- * Only the resulting IrLAP payload is copied to the receive buffers -
- * but with the 16bit FCS still encluded. Question remains, whether it
- * was already checked or we should do it before passing the packet to IrLAP?
- */
-
-enum vlsi_pio_ircfg {
- IRCFG_LOOP = 0x4000, /* enable loopback test mode */
- IRCFG_ENTX = 0x1000, /* transmit enable */
- IRCFG_ENRX = 0x0800, /* receive enable */
- IRCFG_MSTR = 0x0400, /* master enable */
- IRCFG_RXANY = 0x0200, /* receive any packet */
- IRCFG_CRC16 = 0x0080, /* 16bit (not 32bit) CRC select for MIR/FIR */
- IRCFG_FIR = 0x0040, /* FIR 4PPM encoding mode enable */
- IRCFG_MIR = 0x0020, /* MIR HDLC encoding mode enable */
- IRCFG_SIR = 0x0010, /* SIR encoding mode enable */
- IRCFG_SIRFILT = 0x0008, /* enable SIR decode filter (receiver unwrapping) */
- IRCFG_SIRTEST = 0x0004, /* allow SIR decode filter when not in SIR mode */
- IRCFG_TXPOL = 0x0002, /* invert tx polarity when set */
- IRCFG_RXPOL = 0x0001 /* invert rx polarity when set */
-};
-
-/* ------------------------------------------ */
-
-/* VLSI_PIO_SIRFLAG: SIR Flag Register (u16, ro) */
-
-/* register contains hardcoded BOF=0xc0 at [7:0] and EOF=0xc1 at [15:8]
- * which is used for unwrapping received frames in SIR decode-filter mode
- */
-
-/* ------------------------------------------ */
-
-/* VLSI_PIO_IRENABLE: IR Enable Register (u16, rw/ro) */
-
-/* notes:
- * - IREN acts as gate for latching the configured IR mode information
- * from IRCFG and IRPHYCTL when IREN=reset and applying them when
- * IREN gets set afterwards.
- * - ENTXST reflects IRCFG_ENTX
- * - ENRXST = IRCFG_ENRX && (!IRCFG_ENTX || IRCFG_LOOP)
- */
-
-enum vlsi_pio_irenable {
- IRENABLE_PHYANDCLOCK = 0x8000, /* enable IR phy and gate the mode config (rw) */
- IRENABLE_CFGER = 0x4000, /* mode configuration error (ro) */
- IRENABLE_FIR_ON = 0x2000, /* FIR on status (ro) */
- IRENABLE_MIR_ON = 0x1000, /* MIR on status (ro) */
- IRENABLE_SIR_ON = 0x0800, /* SIR on status (ro) */
- IRENABLE_ENTXST = 0x0400, /* transmit enable status (ro) */
- IRENABLE_ENRXST = 0x0200, /* Receive enable status (ro) */
- IRENABLE_CRC16_ON = 0x0100 /* 16bit (not 32bit) CRC enabled status (ro) */
-};
-
-#define IRENABLE_MASK 0xff00 /* Read mask */
-
-/* ------------------------------------------ */
-
-/* VLSI_PIO_PHYCTL: IR Physical Layer Current Control Register (u16, ro) */
-
-/* read-back of the currently applied physical layer status.
- * applied from VLSI_PIO_NPHYCTL at rising edge of IRENABLE_PHYANDCLOCK
- * contents identical to VLSI_PIO_NPHYCTL (see below)
- */
-
-/* ------------------------------------------ */
-
-/* VLSI_PIO_NPHYCTL: IR Physical Layer Next Control Register (u16, rw) */
-
-/* latched during IRENABLE_PHYANDCLOCK=0 and applied at 0-1 transition
- *
- * consists of BAUD[15:10], PLSWID[9:5] and PREAMB[4:0] bits defined as follows:
- *
- * SIR-mode: BAUD = (115.2kHz / baudrate) - 1
- * PLSWID = (pulsetime * freq / (BAUD+1)) - 1
- * where pulsetime is the requested IrPHY pulse width
- * and freq is 8(16)MHz for 40(48)MHz primary input clock
- * PREAMB: don't care for SIR
- *
- * The nominal SIR pulse width is 3/16 bit time so we have PLSWID=12
- * fixed for all SIR speeds at 40MHz input clock (PLSWID=24 at 48MHz).
- * IrPHY also allows shorter pulses down to the nominal pulse duration
- * at 115.2kbaud (minus some tolerance) which is 1.41 usec.
- * Using the expression PLSWID = 12/(BAUD+1)-1 (multiplied by two for 48MHz)
- * we get the minimum acceptable PLSWID values according to the VLSI
- * specification, which provides 1.5 usec pulse width for all speeds (except
- * for 2.4kbaud getting 6usec). This is fine with IrPHY v1.3 specs and
- * reduces the transceiver power which drains the battery. At 9.6kbaud for
- * example this amounts to more than 90% battery power saving!
- *
- * MIR-mode: BAUD = 0
- * PLSWID = 9(10) for 40(48) MHz input clock
- * to get nominal MIR pulse width
- * PREAMB = 1
- *
- * FIR-mode: BAUD = 0
- * PLSWID: don't care
- * PREAMB = 15
- */
-
-#define PHYCTL_BAUD_SHIFT 10
-#define PHYCTL_BAUD_MASK 0xfc00
-#define PHYCTL_PLSWID_SHIFT 5
-#define PHYCTL_PLSWID_MASK 0x03e0
-#define PHYCTL_PREAMB_SHIFT 0
-#define PHYCTL_PREAMB_MASK 0x001f
-
-#define PHYCTL_TO_BAUD(bwp) (((bwp)&PHYCTL_BAUD_MASK)>>PHYCTL_BAUD_SHIFT)
-#define PHYCTL_TO_PLSWID(bwp) (((bwp)&PHYCTL_PLSWID_MASK)>>PHYCTL_PLSWID_SHIFT)
-#define PHYCTL_TO_PREAMB(bwp) (((bwp)&PHYCTL_PREAMB_MASK)>>PHYCTL_PREAMB_SHIFT)
-
-#define BWP_TO_PHYCTL(b,w,p) ((((b)<<PHYCTL_BAUD_SHIFT)&PHYCTL_BAUD_MASK) \
- | (((w)<<PHYCTL_PLSWID_SHIFT)&PHYCTL_PLSWID_MASK) \
- | (((p)<<PHYCTL_PREAMB_SHIFT)&PHYCTL_PREAMB_MASK))
-
-#define BAUD_BITS(br) ((115200/(br))-1)
-
-static inline unsigned
-calc_width_bits(unsigned baudrate, unsigned widthselect, unsigned clockselect)
-{
- unsigned tmp;
-
- if (widthselect) /* nominal 3/16 puls width */
- return (clockselect) ? 12 : 24;
-
- tmp = ((clockselect) ? 12 : 24) / (BAUD_BITS(baudrate)+1);
-
- /* intermediate result of integer division needed here */
-
- return (tmp>0) ? (tmp-1) : 0;
-}
-
-#define PHYCTL_SIR(br,ws,cs) BWP_TO_PHYCTL(BAUD_BITS(br),calc_width_bits((br),(ws),(cs)),0)
-#define PHYCTL_MIR(cs) BWP_TO_PHYCTL(0,((cs)?9:10),1)
-#define PHYCTL_FIR BWP_TO_PHYCTL(0,0,15)
-
-/* quite ugly, I know. But implementing these calculations here avoids
- * having magic numbers in the code and allows some playing with pulsewidths
- * without risk to violate the standards.
- * FWIW, here is the table for reference:
- *
- * baudrate BAUD min-PLSWID nom-PLSWID PREAMB
- * 2400 47 0(0) 12(24) 0
- * 9600 11 0(0) 12(24) 0
- * 19200 5 1(2) 12(24) 0
- * 38400 2 3(6) 12(24) 0
- * 57600 1 5(10) 12(24) 0
- * 115200 0 11(22) 12(24) 0
- * MIR 0 - 9(10) 1
- * FIR 0 - 0 15
- *
- * note: x(y) means x-value for 40MHz / y-value for 48MHz primary input clock
- */
-
-/* ------------------------------------------ */
-
-
-/* VLSI_PIO_MAXPKT: Maximum Packet Length register (u16, rw) */
-
-/* maximum acceptable length for received packets */
-
-/* hw imposed limitation - register uses only [11:0] */
-#define MAX_PACKET_LENGTH 0x0fff
-
-/* IrLAP I-field (apparently not defined elsewhere) */
-#define IRDA_MTU 2048
-
-/* complete packet consists of A(1)+C(1)+I(<=IRDA_MTU) */
-#define IRLAP_SKB_ALLOCSIZE (1+1+IRDA_MTU)
-
-/* the buffers we use to exchange frames with the hardware need to be
- * larger than IRLAP_SKB_ALLOCSIZE because we may have up to 4 bytes FCS
- * appended and, in SIR mode, a lot of frame wrapping bytes. The worst
- * case appears to be a SIR packet with I-size==IRDA_MTU and all bytes
- * requiring to be escaped to provide transparency. Furthermore, the peer
- * might ask for quite a number of additional XBOFs:
- * up to 115+48 XBOFS 163
- * regular BOF 1
- * A-field 1
- * C-field 1
- * I-field, IRDA_MTU, all escaped 4096
- * FCS (16 bit at SIR, escaped) 4
- * EOF 1
- * AFAICS nothing in IrLAP guarantees A/C field not to need escaping
- * (f.e. 0xc0/0xc1 - i.e. BOF/EOF - are legal values there) so in the
- * worst case we have 4269 bytes total frame size.
- * However, the VLSI uses 12 bits only for all buffer length values,
- * which limits the maximum useable buffer size <= 4095.
- * Note this is not a limitation in the receive case because we use
- * the SIR filtering mode where the hw unwraps the frame and only the
- * bare packet+fcs is stored into the buffer - in contrast to the SIR
- * tx case where we have to pass frame-wrapped packets to the hw.
- * If this would ever become an issue in real life, the only workaround
- * I see would be using the legacy UART emulation in SIR mode.
- */
-
-#define XFER_BUF_SIZE MAX_PACKET_LENGTH
-
-/* ------------------------------------------ */
-
-/* VLSI_PIO_RCVBCNT: Receive Byte Count Register (u16, ro) */
-
-/* receive packet counter gets incremented on every non-filtered
- * byte which was put in the receive fifo and reset for each
- * new packet. Used to decide whether we are just in the middle
- * of receiving
- */
-
-/* better apply the [11:0] mask when reading, as some docs say the
- * reserved [15:12] would return 1 when reading - which is wrong AFAICS
- */
-#define RCVBCNT_MASK 0x0fff
-
-/******************************************************************/
-
-/* descriptors for rx/tx ring
- *
- * accessed by hardware - don't change!
- *
- * the descriptor is owned by hardware, when the ACTIVE status bit
- * is set and nothing (besides reading status to test the bit)
- * shall be done. The bit gets cleared by hw, when the descriptor
- * gets closed. Premature reaping of descriptors owned be the chip
- * can be achieved by disabling IRCFG_MSTR
- *
- * Attention: Writing addr overwrites status!
- *
- * ### FIXME: depends on endianess (but there ain't no non-i586 ob800 ;-)
- */
-
-struct ring_descr_hw {
- volatile __le16 rd_count; /* tx/rx count [11:0] */
- __le16 reserved;
- union {
- __le32 addr; /* [23:0] of the buffer's busaddress */
- struct {
- u8 addr_res[3];
- volatile u8 status; /* descriptor status */
- } __packed rd_s;
- } __packed rd_u;
-} __packed;
-
-#define rd_addr rd_u.addr
-#define rd_status rd_u.rd_s.status
-
-/* ring descriptor status bits */
-
-#define RD_ACTIVE 0x80 /* descriptor owned by hw (both TX,RX) */
-
-/* TX ring descriptor status */
-
-#define RD_TX_DISCRC 0x40 /* do not send CRC (for SIR) */
-#define RD_TX_BADCRC 0x20 /* force a bad CRC */
-#define RD_TX_PULSE 0x10 /* send indication pulse after this frame (MIR/FIR) */
-#define RD_TX_FRCEUND 0x08 /* force underrun */
-#define RD_TX_CLRENTX 0x04 /* clear ENTX after this frame */
-#define RD_TX_UNDRN 0x01 /* TX fifo underrun (probably PCI problem) */
-
-/* RX ring descriptor status */
-
-#define RD_RX_PHYERR 0x40 /* physical encoding error */
-#define RD_RX_CRCERR 0x20 /* CRC error (MIR/FIR) */
-#define RD_RX_LENGTH 0x10 /* frame exceeds buffer length */
-#define RD_RX_OVER 0x08 /* RX fifo overrun (probably PCI problem) */
-#define RD_RX_SIRBAD 0x04 /* EOF missing: BOF follows BOF (SIR, filtered) */
-
-#define RD_RX_ERROR 0x7c /* any error in received frame */
-
-/* the memory required to hold the 2 descriptor rings */
-#define HW_RING_AREA_SIZE (2 * MAX_RING_DESCR * sizeof(struct ring_descr_hw))
-
-/******************************************************************/
-
-/* sw-ring descriptors consists of a bus-mapped transfer buffer with
- * associated skb and a pointer to the hw entry descriptor
- */
-
-struct ring_descr {
- struct ring_descr_hw *hw;
- struct sk_buff *skb;
- void *buf;
-};
-
-/* wrappers for operations on hw-exposed ring descriptors
- * access to the hw-part of the descriptors must use these.
- */
-
-static inline int rd_is_active(struct ring_descr *rd)
-{
- return (rd->hw->rd_status & RD_ACTIVE) != 0;
-}
-
-static inline void rd_activate(struct ring_descr *rd)
-{
- rd->hw->rd_status |= RD_ACTIVE;
-}
-
-static inline void rd_set_status(struct ring_descr *rd, u8 s)
-{
- rd->hw->rd_status = s; /* may pass ownership to the hardware */
-}
-
-static inline void rd_set_addr_status(struct ring_descr *rd, dma_addr_t a, u8 s)
-{
- /* order is important for two reasons:
- * - overlayed: writing addr overwrites status
- * - we want to write status last so we have valid address in
- * case status has RD_ACTIVE set
- */
-
- if ((a & ~DMA_MASK_MSTRPAGE)>>24 != MSTRPAGE_VALUE) {
- net_err_ratelimited("%s: pci busaddr inconsistency!\n",
- __func__);
- dump_stack();
- return;
- }
-
- a &= DMA_MASK_MSTRPAGE; /* clear highbyte to make sure we won't write
- * to status - just in case MSTRPAGE_VALUE!=0
- */
- rd->hw->rd_addr = cpu_to_le32(a);
- wmb();
- rd_set_status(rd, s); /* may pass ownership to the hardware */
-}
-
-static inline void rd_set_count(struct ring_descr *rd, u16 c)
-{
- rd->hw->rd_count = cpu_to_le16(c);
-}
-
-static inline u8 rd_get_status(struct ring_descr *rd)
-{
- return rd->hw->rd_status;
-}
-
-static inline dma_addr_t rd_get_addr(struct ring_descr *rd)
-{
- dma_addr_t a;
-
- a = le32_to_cpu(rd->hw->rd_addr);
- return (a & DMA_MASK_MSTRPAGE) | (MSTRPAGE_VALUE << 24);
-}
-
-static inline u16 rd_get_count(struct ring_descr *rd)
-{
- return le16_to_cpu(rd->hw->rd_count);
-}
-
-/******************************************************************/
-
-/* sw descriptor rings for rx, tx:
- *
- * operations follow producer-consumer paradigm, with the hw
- * in the middle doing the processing.
- * ring size must be power of two.
- *
- * producer advances r->tail after inserting for processing
- * consumer advances r->head after removing processed rd
- * ring is empty if head==tail / full if (tail+1)==head
- */
-
-struct vlsi_ring {
- struct pci_dev *pdev;
- int dir;
- unsigned len;
- unsigned size;
- unsigned mask;
- atomic_t head, tail;
- struct ring_descr *rd;
-};
-
-/* ring processing helpers */
-
-static inline struct ring_descr *ring_last(struct vlsi_ring *r)
-{
- int t;
-
- t = atomic_read(&r->tail) & r->mask;
- return (((t+1) & r->mask) == (atomic_read(&r->head) & r->mask)) ? NULL : &r->rd[t];
-}
-
-static inline struct ring_descr *ring_put(struct vlsi_ring *r)
-{
- atomic_inc(&r->tail);
- return ring_last(r);
-}
-
-static inline struct ring_descr *ring_first(struct vlsi_ring *r)
-{
- int h;
-
- h = atomic_read(&r->head) & r->mask;
- return (h == (atomic_read(&r->tail) & r->mask)) ? NULL : &r->rd[h];
-}
-
-static inline struct ring_descr *ring_get(struct vlsi_ring *r)
-{
- atomic_inc(&r->head);
- return ring_first(r);
-}
-
-/******************************************************************/
-
-/* our private compound VLSI-PCI-IRDA device information */
-
-typedef struct vlsi_irda_dev {
- struct pci_dev *pdev;
-
- struct irlap_cb *irlap;
-
- struct qos_info qos;
-
- unsigned mode;
- int baud, new_baud;
-
- dma_addr_t busaddr;
- void *virtaddr;
- struct vlsi_ring *tx_ring, *rx_ring;
-
- ktime_t last_rx;
-
- spinlock_t lock;
- struct mutex mtx;
-
- u8 resume_ok;
- struct proc_dir_entry *proc_entry;
-
-} vlsi_irda_dev_t;
-
-/********************************************************/
-
-/* the remapped error flags we use for returning from frame
- * post-processing in vlsi_process_tx/rx() after it was completed
- * by the hardware. These functions either return the >=0 number
- * of transferred bytes in case of success or the negative (-)
- * of the or'ed error flags.
- */
-
-#define VLSI_TX_DROP 0x0001
-#define VLSI_TX_FIFO 0x0002
-
-#define VLSI_RX_DROP 0x0100
-#define VLSI_RX_OVER 0x0200
-#define VLSI_RX_LENGTH 0x0400
-#define VLSI_RX_FRAME 0x0800
-#define VLSI_RX_CRC 0x1000
-
-/********************************************************/
-
-#endif /* IRDA_VLSI_FIR_H */
-
diff --git a/drivers/staging/irda/drivers/w83977af.h b/drivers/staging/irda/drivers/w83977af.h
deleted file mode 100644
index 04476c2e9121..000000000000
--- a/drivers/staging/irda/drivers/w83977af.h
+++ /dev/null
@@ -1,53 +0,0 @@
-#ifndef W83977AF_H
-#define W83977AF_H
-
-#define W977_EFIO_BASE 0x370
-#define W977_EFIO2_BASE 0x3f0
-#define W977_DEVICE_IR 0x06
-
-
-/*
- * Enter extended function mode
- */
-static inline void w977_efm_enter(unsigned int efio)
-{
- outb(0x87, efio);
- outb(0x87, efio);
-}
-
-/*
- * Select a device to configure
- */
-
-static inline void w977_select_device(__u8 devnum, unsigned int efio)
-{
- outb(0x07, efio);
- outb(devnum, efio+1);
-}
-
-/*
- * Write a byte to a register
- */
-static inline void w977_write_reg(__u8 reg, __u8 value, unsigned int efio)
-{
- outb(reg, efio);
- outb(value, efio+1);
-}
-
-/*
- * read a byte from a register
- */
-static inline __u8 w977_read_reg(__u8 reg, unsigned int efio)
-{
- outb(reg, efio);
- return inb(efio+1);
-}
-
-/*
- * Exit extended function mode
- */
-static inline void w977_efm_exit(unsigned int efio)
-{
- outb(0xAA, efio);
-}
-#endif
diff --git a/drivers/staging/irda/drivers/w83977af_ir.c b/drivers/staging/irda/drivers/w83977af_ir.c
deleted file mode 100644
index 282b6c9ae05b..000000000000
--- a/drivers/staging/irda/drivers/w83977af_ir.c
+++ /dev/null
@@ -1,1285 +0,0 @@
-/*********************************************************************
- *
- * Filename: w83977af_ir.c
- * Version: 1.0
- * Description: FIR driver for the Winbond W83977AF Super I/O chip
- * Status: Experimental.
- * Author: Paul VanderSpek
- * Created at: Wed Nov 4 11:46:16 1998
- * Modified at: Fri Jan 28 12:10:59 2000
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>
- * Copyright (c) 1998-1999 Rebel.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.
- *
- * Neither Paul VanderSpek nor Rebel.com admit liability nor provide
- * warranty for any of this software. This material is provided "AS-IS"
- * and at no charge.
- *
- * If you find bugs in this file, its very likely that the same bug
- * will also be in pc87108.c since the implementations are quite
- * similar.
- *
- * Notice that all functions that needs to access the chip in _any_
- * way, must save BSR register on entry, and restore it on exit.
- * It is _very_ important to follow this policy!
- *
- * __u8 bank;
- *
- * bank = inb( iobase+BSR);
- *
- * do_your_stuff_here();
- *
- * outb( bank, iobase+BSR);
- *
- ********************************************************************/
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/rtnetlink.h>
-#include <linux/dma-mapping.h>
-#include <linux/gfp.h>
-
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <asm/byteorder.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/wrapper.h>
-#include <net/irda/irda_device.h>
-#include "w83977af.h"
-#include "w83977af_ir.h"
-
-#define CONFIG_USE_W977_PNP /* Currently needed */
-#define PIO_MAX_SPEED 115200
-
-static char *driver_name = "w83977af_ir";
-static int qos_mtt_bits = 0x07; /* 1 ms or more */
-
-#define CHIP_IO_EXTENT 8
-
-static unsigned int io[] = { 0x180, ~0, ~0, ~0 };
-#ifdef CONFIG_ARCH_NETWINDER /* Adjust to NetWinder differences */
-static unsigned int irq[] = { 6, 0, 0, 0 };
-#else
-static unsigned int irq[] = { 11, 0, 0, 0 };
-#endif
-static unsigned int dma[] = { 1, 0, 0, 0 };
-static unsigned int efbase[] = { W977_EFIO_BASE, W977_EFIO2_BASE };
-static unsigned int efio = W977_EFIO_BASE;
-
-static struct w83977af_ir *dev_self[] = { NULL, NULL, NULL, NULL};
-
-/* Some prototypes */
-static int w83977af_open(int i, unsigned int iobase, unsigned int irq,
- unsigned int dma);
-static int w83977af_close(struct w83977af_ir *self);
-static int w83977af_probe(int iobase, int irq, int dma);
-static int w83977af_dma_receive(struct w83977af_ir *self);
-static int w83977af_dma_receive_complete(struct w83977af_ir *self);
-static netdev_tx_t w83977af_hard_xmit(struct sk_buff *skb,
- struct net_device *dev);
-static int w83977af_pio_write(int iobase, __u8 *buf, int len, int fifo_size);
-static void w83977af_dma_write(struct w83977af_ir *self, int iobase);
-static void w83977af_change_speed(struct w83977af_ir *self, __u32 speed);
-static int w83977af_is_receiving(struct w83977af_ir *self);
-
-static int w83977af_net_open(struct net_device *dev);
-static int w83977af_net_close(struct net_device *dev);
-static int w83977af_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-
-/*
- * Function w83977af_init ()
- *
- * Initialize chip. Just try to find out how many chips we are dealing with
- * and where they are
- */
-static int __init w83977af_init(void)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(dev_self) && io[i] < 2000; i++) {
- if (w83977af_open(i, io[i], irq[i], dma[i]) == 0)
- return 0;
- }
- return -ENODEV;
-}
-
-/*
- * Function w83977af_cleanup ()
- *
- * Close all configured chips
- *
- */
-static void __exit w83977af_cleanup(void)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(dev_self); i++) {
- if (dev_self[i])
- w83977af_close(dev_self[i]);
- }
-}
-
-static const struct net_device_ops w83977_netdev_ops = {
- .ndo_open = w83977af_net_open,
- .ndo_stop = w83977af_net_close,
- .ndo_start_xmit = w83977af_hard_xmit,
- .ndo_do_ioctl = w83977af_net_ioctl,
-};
-
-/*
- * Function w83977af_open (iobase, irq)
- *
- * Open driver instance
- *
- */
-static int w83977af_open(int i, unsigned int iobase, unsigned int irq,
- unsigned int dma)
-{
- struct net_device *dev;
- struct w83977af_ir *self;
- int err;
-
- /* Lock the port that we need */
- if (!request_region(iobase, CHIP_IO_EXTENT, driver_name)) {
- pr_debug("%s: can't get iobase of 0x%03x\n",
- __func__, iobase);
- return -ENODEV;
- }
-
- if (w83977af_probe(iobase, irq, dma) == -1) {
- err = -1;
- goto err_out;
- }
- /*
- * Allocate new instance of the driver
- */
- dev = alloc_irdadev(sizeof(struct w83977af_ir));
- if (!dev) {
- pr_err("IrDA: Can't allocate memory for IrDA control block!\n");
- err = -ENOMEM;
- goto err_out;
- }
-
- self = netdev_priv(dev);
- spin_lock_init(&self->lock);
-
- /* Initialize IO */
- self->io.fir_base = iobase;
- self->io.irq = irq;
- self->io.fir_ext = CHIP_IO_EXTENT;
- self->io.dma = dma;
- self->io.fifo_size = 32;
-
- /* Initialize QoS for this device */
- irda_init_max_qos_capabilies(&self->qos);
-
- /* The only value we must override it the baudrate */
-
- /* FIXME: The HP HDLS-1100 does not support 1152000! */
- self->qos.baud_rate.bits = IR_9600 | IR_19200 | IR_38400 | IR_57600 |
- IR_115200 | IR_576000 | IR_1152000 | (IR_4000000 << 8);
-
- /* The HP HDLS-1100 needs 1 ms according to the specs */
- self->qos.min_turn_time.bits = qos_mtt_bits;
- irda_qos_bits_to_value(&self->qos);
-
- /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */
- self->rx_buff.truesize = 14384;
- self->tx_buff.truesize = 4000;
-
- /* Allocate memory if needed */
- self->rx_buff.head =
- dma_zalloc_coherent(NULL, self->rx_buff.truesize,
- &self->rx_buff_dma, GFP_KERNEL);
- if (!self->rx_buff.head) {
- err = -ENOMEM;
- goto err_out1;
- }
-
- self->tx_buff.head =
- dma_zalloc_coherent(NULL, self->tx_buff.truesize,
- &self->tx_buff_dma, GFP_KERNEL);
- if (!self->tx_buff.head) {
- err = -ENOMEM;
- goto err_out2;
- }
-
- self->rx_buff.in_frame = FALSE;
- self->rx_buff.state = OUTSIDE_FRAME;
- self->tx_buff.data = self->tx_buff.head;
- self->rx_buff.data = self->rx_buff.head;
- self->netdev = dev;
-
- dev->netdev_ops = &w83977_netdev_ops;
-
- err = register_netdev(dev);
- if (err) {
- net_err_ratelimited("%s:, register_netdevice() failed!\n",
- __func__);
- goto err_out3;
- }
- net_info_ratelimited("IrDA: Registered device %s\n", dev->name);
-
- /* Need to store self somewhere */
- dev_self[i] = self;
-
- return 0;
-err_out3:
- dma_free_coherent(NULL, self->tx_buff.truesize,
- self->tx_buff.head, self->tx_buff_dma);
-err_out2:
- dma_free_coherent(NULL, self->rx_buff.truesize,
- self->rx_buff.head, self->rx_buff_dma);
-err_out1:
- free_netdev(dev);
-err_out:
- release_region(iobase, CHIP_IO_EXTENT);
- return err;
-}
-
-/*
- * Function w83977af_close (self)
- *
- * Close driver instance
- *
- */
-static int w83977af_close(struct w83977af_ir *self)
-{
- int iobase;
-
- iobase = self->io.fir_base;
-
-#ifdef CONFIG_USE_W977_PNP
- /* enter PnP configuration mode */
- w977_efm_enter(efio);
-
- w977_select_device(W977_DEVICE_IR, efio);
-
- /* Deactivate device */
- w977_write_reg(0x30, 0x00, efio);
-
- w977_efm_exit(efio);
-#endif /* CONFIG_USE_W977_PNP */
-
- /* Remove netdevice */
- unregister_netdev(self->netdev);
-
- /* Release the PORT that this driver is using */
- pr_debug("%s: Releasing Region %03x\n", __func__, self->io.fir_base);
- release_region(self->io.fir_base, self->io.fir_ext);
-
- if (self->tx_buff.head)
- dma_free_coherent(NULL, self->tx_buff.truesize,
- self->tx_buff.head, self->tx_buff_dma);
-
- if (self->rx_buff.head)
- dma_free_coherent(NULL, self->rx_buff.truesize,
- self->rx_buff.head, self->rx_buff_dma);
-
- free_netdev(self->netdev);
-
- return 0;
-}
-
-static int w83977af_probe(int iobase, int irq, int dma)
-{
- int version;
- int i;
-
- for (i = 0; i < 2; i++) {
-#ifdef CONFIG_USE_W977_PNP
- /* Enter PnP configuration mode */
- w977_efm_enter(efbase[i]);
-
- w977_select_device(W977_DEVICE_IR, efbase[i]);
-
- /* Configure PnP port, IRQ, and DMA channel */
- w977_write_reg(0x60, (iobase >> 8) & 0xff, efbase[i]);
- w977_write_reg(0x61, (iobase) & 0xff, efbase[i]);
-
- w977_write_reg(0x70, irq, efbase[i]);
-#ifdef CONFIG_ARCH_NETWINDER
- /* Netwinder uses 1 higher than Linux */
- w977_write_reg(0x74, dma + 1, efbase[i]);
-#else
- w977_write_reg(0x74, dma, efbase[i]);
-#endif /* CONFIG_ARCH_NETWINDER */
- w977_write_reg(0x75, 0x04, efbase[i]);/* Disable Tx DMA */
-
- /* Set append hardware CRC, enable IR bank selection */
- w977_write_reg(0xf0, APEDCRC | ENBNKSEL, efbase[i]);
-
- /* Activate device */
- w977_write_reg(0x30, 0x01, efbase[i]);
-
- w977_efm_exit(efbase[i]);
-#endif /* CONFIG_USE_W977_PNP */
- /* Disable Advanced mode */
- switch_bank(iobase, SET2);
- outb(iobase + 2, 0x00);
-
- /* Turn on UART (global) interrupts */
- switch_bank(iobase, SET0);
- outb(HCR_EN_IRQ, iobase + HCR);
-
- /* Switch to advanced mode */
- switch_bank(iobase, SET2);
- outb(inb(iobase + ADCR1) | ADCR1_ADV_SL, iobase + ADCR1);
-
- /* Set default IR-mode */
- switch_bank(iobase, SET0);
- outb(HCR_SIR, iobase + HCR);
-
- /* Read the Advanced IR ID */
- switch_bank(iobase, SET3);
- version = inb(iobase + AUID);
-
- /* Should be 0x1? */
- if (0x10 == (version & 0xf0)) {
- efio = efbase[i];
-
- /* Set FIFO size to 32 */
- switch_bank(iobase, SET2);
- outb(ADCR2_RXFS32 | ADCR2_TXFS32, iobase + ADCR2);
-
- /* Set FIFO threshold to TX17, RX16 */
- switch_bank(iobase, SET0);
- outb(UFR_RXTL | UFR_TXTL | UFR_TXF_RST | UFR_RXF_RST |
- UFR_EN_FIFO, iobase + UFR);
-
- /* Receiver frame length */
- switch_bank(iobase, SET4);
- outb(2048 & 0xff, iobase + 6);
- outb((2048 >> 8) & 0x1f, iobase + 7);
-
- /*
- * Init HP HSDL-1100 transceiver.
- *
- * Set IRX_MSL since we have 2 * receive paths IRRX,
- * and IRRXH. Clear IRSL0D since we want IRSL0 * to
- * be a input pin used for IRRXH
- *
- * IRRX pin 37 connected to receiver
- * IRTX pin 38 connected to transmitter
- * FIRRX pin 39 connected to receiver (IRSL0)
- * CIRRX pin 40 connected to pin 37
- */
- switch_bank(iobase, SET7);
- outb(0x40, iobase + 7);
-
- net_info_ratelimited("W83977AF (IR) driver loaded. Version: 0x%02x\n",
- version);
-
- return 0;
- } else {
- /* Try next extented function register address */
- pr_debug("%s: Wrong chip version\n", __func__);
- }
- }
- return -1;
-}
-
-static void w83977af_change_speed(struct w83977af_ir *self, __u32 speed)
-{
- int ir_mode = HCR_SIR;
- int iobase;
- __u8 set;
-
- iobase = self->io.fir_base;
-
- /* Update accounting for new speed */
- self->io.speed = speed;
-
- /* Save current bank */
- set = inb(iobase + SSR);
-
- /* Disable interrupts */
- switch_bank(iobase, SET0);
- outb(0, iobase + ICR);
-
- /* Select Set 2 */
- switch_bank(iobase, SET2);
- outb(0x00, iobase + ABHL);
-
- switch (speed) {
- case 9600: outb(0x0c, iobase + ABLL); break;
- case 19200: outb(0x06, iobase + ABLL); break;
- case 38400: outb(0x03, iobase + ABLL); break;
- case 57600: outb(0x02, iobase + ABLL); break;
- case 115200: outb(0x01, iobase + ABLL); break;
- case 576000:
- ir_mode = HCR_MIR_576;
- pr_debug("%s: handling baud of 576000\n", __func__);
- break;
- case 1152000:
- ir_mode = HCR_MIR_1152;
- pr_debug("%s: handling baud of 1152000\n", __func__);
- break;
- case 4000000:
- ir_mode = HCR_FIR;
- pr_debug("%s: handling baud of 4000000\n", __func__);
- break;
- default:
- ir_mode = HCR_FIR;
- pr_debug("%s: unknown baud rate of %d\n", __func__, speed);
- break;
- }
-
- /* Set speed mode */
- switch_bank(iobase, SET0);
- outb(ir_mode, iobase + HCR);
-
- /* set FIFO size to 32 */
- switch_bank(iobase, SET2);
- outb(ADCR2_RXFS32 | ADCR2_TXFS32, iobase + ADCR2);
-
- /* set FIFO threshold to TX17, RX16 */
- switch_bank(iobase, SET0);
- outb(0x00, iobase + UFR); /* Reset */
- outb(UFR_EN_FIFO, iobase + UFR); /* First we must enable FIFO */
- outb(0xa7, iobase + UFR);
-
- netif_wake_queue(self->netdev);
-
- /* Enable some interrupts so we can receive frames */
- switch_bank(iobase, SET0);
- if (speed > PIO_MAX_SPEED) {
- outb(ICR_EFSFI, iobase + ICR);
- w83977af_dma_receive(self);
- } else {
- outb(ICR_ERBRI, iobase + ICR);
- }
-
- /* Restore SSR */
- outb(set, iobase + SSR);
-}
-
-/*
- * Function w83977af_hard_xmit (skb, dev)
- *
- * Sets up a DMA transfer to send the current frame.
- *
- */
-static netdev_tx_t w83977af_hard_xmit(struct sk_buff *skb,
- struct net_device *dev)
-{
- struct w83977af_ir *self;
- __s32 speed;
- int iobase;
- __u8 set;
- int mtt;
-
- self = netdev_priv(dev);
-
- iobase = self->io.fir_base;
-
- pr_debug("%s: %ld, skb->len=%d\n", __func__, jiffies, (int)skb->len);
-
- /* Lock transmit buffer */
- netif_stop_queue(dev);
-
- /* Check if we need to change the speed */
- speed = irda_get_next_speed(skb);
- if ((speed != self->io.speed) && (speed != -1)) {
- /* Check for empty frame */
- if (!skb->len) {
- w83977af_change_speed(self, speed);
- dev_kfree_skb(skb);
- return NETDEV_TX_OK;
- }
- self->new_speed = speed;
- }
-
- /* Save current set */
- set = inb(iobase + SSR);
-
- /* Decide if we should use PIO or DMA transfer */
- if (self->io.speed > PIO_MAX_SPEED) {
- self->tx_buff.data = self->tx_buff.head;
- skb_copy_from_linear_data(skb, self->tx_buff.data, skb->len);
- self->tx_buff.len = skb->len;
-
- mtt = irda_get_mtt(skb);
- pr_debug("%s: %ld, mtt=%d\n", __func__, jiffies, mtt);
- if (mtt > 1000)
- mdelay(mtt / 1000);
- else if (mtt)
- udelay(mtt);
-
- /* Enable DMA interrupt */
- switch_bank(iobase, SET0);
- outb(ICR_EDMAI, iobase + ICR);
- w83977af_dma_write(self, iobase);
- } else {
- self->tx_buff.data = self->tx_buff.head;
- self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data,
- self->tx_buff.truesize);
-
- /* Add interrupt on tx low level (will fire immediately) */
- switch_bank(iobase, SET0);
- outb(ICR_ETXTHI, iobase + ICR);
- }
- dev_kfree_skb(skb);
-
- /* Restore set register */
- outb(set, iobase + SSR);
-
- return NETDEV_TX_OK;
-}
-
-/*
- * Function w83977af_dma_write (self, iobase)
- *
- * Send frame using DMA
- *
- */
-static void w83977af_dma_write(struct w83977af_ir *self, int iobase)
-{
- __u8 set;
-
- pr_debug("%s: len=%d\n", __func__, self->tx_buff.len);
-
- /* Save current set */
- set = inb(iobase + SSR);
-
- /* Disable DMA */
- switch_bank(iobase, SET0);
- outb(inb(iobase + HCR) & ~HCR_EN_DMA, iobase + HCR);
-
- /* Choose transmit DMA channel */
- switch_bank(iobase, SET2);
- outb(ADCR1_D_CHSW | /*ADCR1_DMA_F|*/ADCR1_ADV_SL, iobase + ADCR1);
- irda_setup_dma(self->io.dma, self->tx_buff_dma, self->tx_buff.len,
- DMA_MODE_WRITE);
- self->io.direction = IO_XMIT;
-
- /* Enable DMA */
- switch_bank(iobase, SET0);
- outb(inb(iobase + HCR) | HCR_EN_DMA | HCR_TX_WT, iobase + HCR);
-
- /* Restore set register */
- outb(set, iobase + SSR);
-}
-
-/*
- * Function w83977af_pio_write (iobase, buf, len, fifo_size)
- *
- *
- *
- */
-static int w83977af_pio_write(int iobase, __u8 *buf, int len, int fifo_size)
-{
- int actual = 0;
- __u8 set;
-
- /* Save current bank */
- set = inb(iobase + SSR);
-
- switch_bank(iobase, SET0);
- if (!(inb_p(iobase + USR) & USR_TSRE)) {
- pr_debug("%s: warning, FIFO not empty yet!\n", __func__);
-
- fifo_size -= 17;
- pr_debug("%s: %d bytes left in tx fifo\n", __func__, fifo_size);
- }
-
- /* Fill FIFO with current frame */
- while ((fifo_size-- > 0) && (actual < len)) {
- /* Transmit next byte */
- outb(buf[actual++], iobase + TBR);
- }
-
- pr_debug("%s: fifo_size %d ; %d sent of %d\n",
- __func__, fifo_size, actual, len);
-
- /* Restore bank */
- outb(set, iobase + SSR);
-
- return actual;
-}
-
-/*
- * Function w83977af_dma_xmit_complete (self)
- *
- * The transfer of a frame in finished. So do the necessary things
- *
- *
- */
-static void w83977af_dma_xmit_complete(struct w83977af_ir *self)
-{
- int iobase;
- __u8 set;
-
- pr_debug("%s: %ld\n", __func__, jiffies);
-
- IRDA_ASSERT(self, return;);
-
- iobase = self->io.fir_base;
-
- /* Save current set */
- set = inb(iobase + SSR);
-
- /* Disable DMA */
- switch_bank(iobase, SET0);
- outb(inb(iobase + HCR) & ~HCR_EN_DMA, iobase + HCR);
-
- /* Check for underrun! */
- if (inb(iobase + AUDR) & AUDR_UNDR) {
- pr_debug("%s: Transmit underrun!\n", __func__);
-
- self->netdev->stats.tx_errors++;
- self->netdev->stats.tx_fifo_errors++;
-
- /* Clear bit, by writing 1 to it */
- outb(AUDR_UNDR, iobase + AUDR);
- } else {
- self->netdev->stats.tx_packets++;
- }
-
- if (self->new_speed) {
- w83977af_change_speed(self, self->new_speed);
- self->new_speed = 0;
- }
-
- /* Unlock tx_buff and request another frame */
- /* Tell the network layer, that we want more frames */
- netif_wake_queue(self->netdev);
-
- /* Restore set */
- outb(set, iobase + SSR);
-}
-
-/*
- * Function w83977af_dma_receive (self)
- *
- * Get ready for receiving a frame. The device will initiate a DMA
- * if it starts to receive a frame.
- *
- */
-static int w83977af_dma_receive(struct w83977af_ir *self)
-{
- int iobase;
- __u8 set;
-#ifdef CONFIG_ARCH_NETWINDER
- unsigned long flags;
- __u8 hcr;
-#endif
- IRDA_ASSERT(self, return -1;);
-
- pr_debug("%s\n", __func__);
-
- iobase = self->io.fir_base;
-
- /* Save current set */
- set = inb(iobase + SSR);
-
- /* Disable DMA */
- switch_bank(iobase, SET0);
- outb(inb(iobase + HCR) & ~HCR_EN_DMA, iobase + HCR);
-
- /* Choose DMA Rx, DMA Fairness, and Advanced mode */
- switch_bank(iobase, SET2);
- outb((inb(iobase + ADCR1) & ~ADCR1_D_CHSW)/*|ADCR1_DMA_F*/ | ADCR1_ADV_SL,
- iobase + ADCR1);
-
- self->io.direction = IO_RECV;
- self->rx_buff.data = self->rx_buff.head;
-
-#ifdef CONFIG_ARCH_NETWINDER
- spin_lock_irqsave(&self->lock, flags);
-
- disable_dma(self->io.dma);
- clear_dma_ff(self->io.dma);
- set_dma_mode(self->io.dma, DMA_MODE_READ);
- set_dma_addr(self->io.dma, self->rx_buff_dma);
- set_dma_count(self->io.dma, self->rx_buff.truesize);
-#else
- irda_setup_dma(self->io.dma, self->rx_buff_dma, self->rx_buff.truesize,
- DMA_MODE_READ);
-#endif
- /*
- * Reset Rx FIFO. This will also flush the ST_FIFO, it's very
- * important that we don't reset the Tx FIFO since it might not
- * be finished transmitting yet
- */
- switch_bank(iobase, SET0);
- outb(UFR_RXTL | UFR_TXTL | UFR_RXF_RST | UFR_EN_FIFO, iobase + UFR);
- self->st_fifo.len = self->st_fifo.tail = self->st_fifo.head = 0;
-
- /* Enable DMA */
- switch_bank(iobase, SET0);
-#ifdef CONFIG_ARCH_NETWINDER
- hcr = inb(iobase + HCR);
- outb(hcr | HCR_EN_DMA, iobase + HCR);
- enable_dma(self->io.dma);
- spin_unlock_irqrestore(&self->lock, flags);
-#else
- outb(inb(iobase + HCR) | HCR_EN_DMA, iobase + HCR);
-#endif
- /* Restore set */
- outb(set, iobase + SSR);
-
- return 0;
-}
-
-/*
- * Function w83977af_receive_complete (self)
- *
- * Finished with receiving a frame
- *
- */
-static int w83977af_dma_receive_complete(struct w83977af_ir *self)
-{
- struct sk_buff *skb;
- struct st_fifo *st_fifo;
- int len;
- int iobase;
- __u8 set;
- __u8 status;
-
- pr_debug("%s\n", __func__);
-
- st_fifo = &self->st_fifo;
-
- iobase = self->io.fir_base;
-
- /* Save current set */
- set = inb(iobase + SSR);
-
- iobase = self->io.fir_base;
-
- /* Read status FIFO */
- switch_bank(iobase, SET5);
- while ((status = inb(iobase + FS_FO)) & FS_FO_FSFDR) {
- st_fifo->entries[st_fifo->tail].status = status;
-
- st_fifo->entries[st_fifo->tail].len = inb(iobase + RFLFL);
- st_fifo->entries[st_fifo->tail].len |= inb(iobase + RFLFH) << 8;
-
- st_fifo->tail++;
- st_fifo->len++;
- }
-
- while (st_fifo->len) {
- /* Get first entry */
- status = st_fifo->entries[st_fifo->head].status;
- len = st_fifo->entries[st_fifo->head].len;
- st_fifo->head++;
- st_fifo->len--;
-
- /* Check for errors */
- if (status & FS_FO_ERR_MSK) {
- if (status & FS_FO_LST_FR) {
- /* Add number of lost frames to stats */
- self->netdev->stats.rx_errors += len;
- } else {
- /* Skip frame */
- self->netdev->stats.rx_errors++;
-
- self->rx_buff.data += len;
-
- if (status & FS_FO_MX_LEX)
- self->netdev->stats.rx_length_errors++;
-
- if (status & FS_FO_PHY_ERR)
- self->netdev->stats.rx_frame_errors++;
-
- if (status & FS_FO_CRC_ERR)
- self->netdev->stats.rx_crc_errors++;
- }
- /* The errors below can be reported in both cases */
- if (status & FS_FO_RX_OV)
- self->netdev->stats.rx_fifo_errors++;
-
- if (status & FS_FO_FSF_OV)
- self->netdev->stats.rx_fifo_errors++;
-
- } else {
- /* Check if we have transferred all data to memory */
- switch_bank(iobase, SET0);
- if (inb(iobase + USR) & USR_RDR)
- udelay(80); /* Should be enough!? */
-
- skb = dev_alloc_skb(len + 1);
- if (!skb) {
- pr_info("%s: memory squeeze, dropping frame\n",
- __func__);
- /* Restore set register */
- outb(set, iobase + SSR);
-
- return FALSE;
- }
-
- /* Align to 20 bytes */
- skb_reserve(skb, 1);
-
- /* Copy frame without CRC */
- if (self->io.speed < 4000000) {
- skb_put(skb, len - 2);
- skb_copy_to_linear_data(skb,
- self->rx_buff.data,
- len - 2);
- } else {
- skb_put(skb, len - 4);
- skb_copy_to_linear_data(skb,
- self->rx_buff.data,
- len - 4);
- }
-
- /* Move to next frame */
- self->rx_buff.data += len;
- self->netdev->stats.rx_packets++;
-
- skb->dev = self->netdev;
- skb_reset_mac_header(skb);
- skb->protocol = htons(ETH_P_IRDA);
- netif_rx(skb);
- }
- }
- /* Restore set register */
- outb(set, iobase + SSR);
-
- return TRUE;
-}
-
-/*
- * Function pc87108_pio_receive (self)
- *
- * Receive all data in receiver FIFO
- *
- */
-static void w83977af_pio_receive(struct w83977af_ir *self)
-{
- __u8 byte = 0x00;
- int iobase;
-
- IRDA_ASSERT(self, return;);
-
- iobase = self->io.fir_base;
-
- /* Receive all characters in Rx FIFO */
- do {
- byte = inb(iobase + RBR);
- async_unwrap_char(self->netdev, &self->netdev->stats, &self->rx_buff,
- byte);
- } while (inb(iobase + USR) & USR_RDR); /* Data available */
-}
-
-/*
- * Function w83977af_sir_interrupt (self, eir)
- *
- * Handle SIR interrupt
- *
- */
-static __u8 w83977af_sir_interrupt(struct w83977af_ir *self, int isr)
-{
- int actual;
- __u8 new_icr = 0;
- __u8 set;
- int iobase;
-
- pr_debug("%s: isr=%#x\n", __func__, isr);
-
- iobase = self->io.fir_base;
- /* Transmit FIFO low on data */
- if (isr & ISR_TXTH_I) {
- /* Write data left in transmit buffer */
- actual = w83977af_pio_write(self->io.fir_base,
- self->tx_buff.data,
- self->tx_buff.len,
- self->io.fifo_size);
-
- self->tx_buff.data += actual;
- self->tx_buff.len -= actual;
-
- self->io.direction = IO_XMIT;
-
- /* Check if finished */
- if (self->tx_buff.len > 0) {
- new_icr |= ICR_ETXTHI;
- } else {
- set = inb(iobase + SSR);
- switch_bank(iobase, SET0);
- outb(AUDR_SFEND, iobase + AUDR);
- outb(set, iobase + SSR);
-
- self->netdev->stats.tx_packets++;
-
- /* Feed me more packets */
- netif_wake_queue(self->netdev);
- new_icr |= ICR_ETBREI;
- }
- }
- /* Check if transmission has completed */
- if (isr & ISR_TXEMP_I) {
- /* Check if we need to change the speed? */
- if (self->new_speed) {
- pr_debug("%s: Changing speed!\n", __func__);
- w83977af_change_speed(self, self->new_speed);
- self->new_speed = 0;
- }
-
- /* Turn around and get ready to receive some data */
- self->io.direction = IO_RECV;
- new_icr |= ICR_ERBRI;
- }
-
- /* Rx FIFO threshold or timeout */
- if (isr & ISR_RXTH_I) {
- w83977af_pio_receive(self);
-
- /* Keep receiving */
- new_icr |= ICR_ERBRI;
- }
- return new_icr;
-}
-
-/*
- * Function pc87108_fir_interrupt (self, eir)
- *
- * Handle MIR/FIR interrupt
- *
- */
-static __u8 w83977af_fir_interrupt(struct w83977af_ir *self, int isr)
-{
- __u8 new_icr = 0;
- __u8 set;
- int iobase;
-
- iobase = self->io.fir_base;
- set = inb(iobase + SSR);
-
- /* End of frame detected in FIFO */
- if (isr & (ISR_FEND_I | ISR_FSF_I)) {
- if (w83977af_dma_receive_complete(self)) {
- /* Wait for next status FIFO interrupt */
- new_icr |= ICR_EFSFI;
- } else {
- /* DMA not finished yet */
-
- /* Set timer value, resolution 1 ms */
- switch_bank(iobase, SET4);
- outb(0x01, iobase + TMRL); /* 1 ms */
- outb(0x00, iobase + TMRH);
-
- /* Start timer */
- outb(IR_MSL_EN_TMR, iobase + IR_MSL);
-
- new_icr |= ICR_ETMRI;
- }
- }
- /* Timer finished */
- if (isr & ISR_TMR_I) {
- /* Disable timer */
- switch_bank(iobase, SET4);
- outb(0, iobase + IR_MSL);
-
- /* Clear timer event */
- /* switch_bank(iobase, SET0); */
-/* outb(ASCR_CTE, iobase+ASCR); */
-
- /* Check if this is a TX timer interrupt */
- if (self->io.direction == IO_XMIT) {
- w83977af_dma_write(self, iobase);
-
- new_icr |= ICR_EDMAI;
- } else {
- /* Check if DMA has now finished */
- w83977af_dma_receive_complete(self);
-
- new_icr |= ICR_EFSFI;
- }
- }
- /* Finished with DMA */
- if (isr & ISR_DMA_I) {
- w83977af_dma_xmit_complete(self);
-
- /* Check if there are more frames to be transmitted */
- /* if (irda_device_txqueue_empty(self)) { */
-
- /* Prepare for receive
- *
- * ** Netwinder Tx DMA likes that we do this anyway **
- */
- w83977af_dma_receive(self);
- new_icr = ICR_EFSFI;
- /* } */
- }
-
- /* Restore set */
- outb(set, iobase + SSR);
-
- return new_icr;
-}
-
-/*
- * Function w83977af_interrupt (irq, dev_id, regs)
- *
- * An interrupt from the chip has arrived. Time to do some work
- *
- */
-static irqreturn_t w83977af_interrupt(int irq, void *dev_id)
-{
- struct net_device *dev = dev_id;
- struct w83977af_ir *self;
- __u8 set, icr, isr;
- int iobase;
-
- self = netdev_priv(dev);
-
- iobase = self->io.fir_base;
-
- /* Save current bank */
- set = inb(iobase + SSR);
- switch_bank(iobase, SET0);
-
- icr = inb(iobase + ICR);
- isr = inb(iobase + ISR) & icr; /* Mask out the interesting ones */
-
- outb(0, iobase + ICR); /* Disable interrupts */
-
- if (isr) {
- /* Dispatch interrupt handler for the current speed */
- if (self->io.speed > PIO_MAX_SPEED)
- icr = w83977af_fir_interrupt(self, isr);
- else
- icr = w83977af_sir_interrupt(self, isr);
- }
-
- outb(icr, iobase + ICR); /* Restore (new) interrupts */
- outb(set, iobase + SSR); /* Restore bank register */
- return IRQ_RETVAL(isr);
-}
-
-/*
- * Function w83977af_is_receiving (self)
- *
- * Return TRUE is we are currently receiving a frame
- *
- */
-static int w83977af_is_receiving(struct w83977af_ir *self)
-{
- int status = FALSE;
- int iobase;
- __u8 set;
-
- IRDA_ASSERT(self, return FALSE;);
-
- if (self->io.speed > 115200) {
- iobase = self->io.fir_base;
-
- /* Check if rx FIFO is not empty */
- set = inb(iobase + SSR);
- switch_bank(iobase, SET2);
- if ((inb(iobase + RXFDTH) & 0x3f) != 0) {
- /* We are receiving something */
- status = TRUE;
- }
- outb(set, iobase + SSR);
- } else {
- status = (self->rx_buff.state != OUTSIDE_FRAME);
- }
-
- return status;
-}
-
-/*
- * Function w83977af_net_open (dev)
- *
- * Start the device
- *
- */
-static int w83977af_net_open(struct net_device *dev)
-{
- struct w83977af_ir *self;
- int iobase;
- char hwname[32];
- __u8 set;
-
- IRDA_ASSERT(dev, return -1;);
- self = netdev_priv(dev);
-
- IRDA_ASSERT(self, return 0;);
-
- iobase = self->io.fir_base;
-
- if (request_irq(self->io.irq, w83977af_interrupt, 0, dev->name,
- (void *)dev)) {
- return -EAGAIN;
- }
- /*
- * Always allocate the DMA channel after the IRQ,
- * and clean up on failure.
- */
- if (request_dma(self->io.dma, dev->name)) {
- free_irq(self->io.irq, dev);
- return -EAGAIN;
- }
-
- /* Save current set */
- set = inb(iobase + SSR);
-
- /* Enable some interrupts so we can receive frames again */
- switch_bank(iobase, SET0);
- if (self->io.speed > 115200) {
- outb(ICR_EFSFI, iobase + ICR);
- w83977af_dma_receive(self);
- } else {
- outb(ICR_ERBRI, iobase + ICR);
- }
-
- /* Restore bank register */
- outb(set, iobase + SSR);
-
- /* Ready to play! */
- netif_start_queue(dev);
-
- /* Give self a hardware name */
- sprintf(hwname, "w83977af @ 0x%03x", self->io.fir_base);
-
- /*
- * Open new IrLAP layer instance, now that everything should be
- * initialized properly
- */
- self->irlap = irlap_open(dev, &self->qos, hwname);
-
- return 0;
-}
-
-/*
- * Function w83977af_net_close (dev)
- *
- * Stop the device
- *
- */
-static int w83977af_net_close(struct net_device *dev)
-{
- struct w83977af_ir *self;
- int iobase;
- __u8 set;
-
- IRDA_ASSERT(dev, return -1;);
-
- self = netdev_priv(dev);
-
- IRDA_ASSERT(self, return 0;);
-
- iobase = self->io.fir_base;
-
- /* Stop device */
- netif_stop_queue(dev);
-
- /* Stop and remove instance of IrLAP */
- if (self->irlap)
- irlap_close(self->irlap);
- self->irlap = NULL;
-
- disable_dma(self->io.dma);
-
- /* Save current set */
- set = inb(iobase + SSR);
-
- /* Disable interrupts */
- switch_bank(iobase, SET0);
- outb(0, iobase + ICR);
-
- free_irq(self->io.irq, dev);
- free_dma(self->io.dma);
-
- /* Restore bank register */
- outb(set, iobase + SSR);
-
- return 0;
-}
-
-/*
- * Function w83977af_net_ioctl (dev, rq, cmd)
- *
- * Process IOCTL commands for this device
- *
- */
-static int w83977af_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
- struct if_irda_req *irq = (struct if_irda_req *)rq;
- struct w83977af_ir *self;
- unsigned long flags;
- int ret = 0;
-
- IRDA_ASSERT(dev, return -1;);
-
- self = netdev_priv(dev);
-
- IRDA_ASSERT(self, return -1;);
-
- pr_debug("%s: %s, (cmd=0x%X)\n", __func__, dev->name, cmd);
-
- spin_lock_irqsave(&self->lock, flags);
-
- switch (cmd) {
- case SIOCSBANDWIDTH: /* Set bandwidth */
- if (!capable(CAP_NET_ADMIN)) {
- ret = -EPERM;
- goto out;
- }
- w83977af_change_speed(self, irq->ifr_baudrate);
- break;
- case SIOCSMEDIABUSY: /* Set media busy */
- if (!capable(CAP_NET_ADMIN)) {
- ret = -EPERM;
- goto out;
- }
- irda_device_set_media_busy(self->netdev, TRUE);
- break;
- case SIOCGRECEIVING: /* Check if we are receiving right now */
- irq->ifr_receiving = w83977af_is_receiving(self);
- break;
- default:
- ret = -EOPNOTSUPP;
- }
-out:
- spin_unlock_irqrestore(&self->lock, flags);
- return ret;
-}
-
-MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
-MODULE_DESCRIPTION("Winbond W83977AF IrDA Device Driver");
-MODULE_LICENSE("GPL");
-
-module_param(qos_mtt_bits, int, 0);
-MODULE_PARM_DESC(qos_mtt_bits, "Mimimum Turn Time");
-module_param_hw_array(io, int, ioport, NULL, 0);
-MODULE_PARM_DESC(io, "Base I/O addresses");
-module_param_hw_array(irq, int, irq, NULL, 0);
-MODULE_PARM_DESC(irq, "IRQ lines");
-
-/*
- * Function init_module (void)
- *
- *
- *
- */
-module_init(w83977af_init);
-
-/*
- * Function cleanup_module (void)
- *
- *
- *
- */
-module_exit(w83977af_cleanup);
diff --git a/drivers/staging/irda/drivers/w83977af_ir.h b/drivers/staging/irda/drivers/w83977af_ir.h
deleted file mode 100644
index fefe9b11e200..000000000000
--- a/drivers/staging/irda/drivers/w83977af_ir.h
+++ /dev/null
@@ -1,198 +0,0 @@
-/*********************************************************************
- *
- * Filename: w83977af_ir.h
- * Version:
- * Description:
- * Status: Experimental.
- * Author: Paul VanderSpek
- * Created at: Thu Nov 19 13:55:34 1998
- * Modified at: Tue Jan 11 13:08:19 2000
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-2000 Dag Brattli, All Rights Reserved.
- *
- * 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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#ifndef W83977AF_IR_H
-#define W83977AF_IR_H
-
-#include <asm/io.h>
-#include <linux/types.h>
-
-/* Flags for configuration register CRF0 */
-#define ENBNKSEL 0x01
-#define APEDCRC 0x02
-#define TXW4C 0x04
-#define RXW4C 0x08
-
-/* Bank 0 */
-#define RBR 0x00 /* Receiver buffer register */
-#define TBR 0x00 /* Transmitter buffer register */
-
-#define ICR 0x01 /* Interrupt configuration register */
-#define ICR_ERBRI 0x01 /* Receiver buffer register interrupt */
-#define ICR_ETBREI 0x02 /* Transeiver empty interrupt */
-#define ICR_EUSRI 0x04//* IR status interrupt */
-#define ICR_EHSRI 0x04
-#define ICR_ETXURI 0x04 /* Tx underrun */
-#define ICR_EDMAI 0x10 /* DMA interrupt */
-#define ICR_ETXTHI 0x20 /* Transmitter threshold interrupt */
-#define ICR_EFSFI 0x40 /* Frame status FIFO interrupt */
-#define ICR_ETMRI 0x80 /* Timer interrupt */
-
-#define UFR 0x02 /* FIFO control register */
-#define UFR_EN_FIFO 0x01 /* Enable FIFO's */
-#define UFR_RXF_RST 0x02 /* Reset Rx FIFO */
-#define UFR_TXF_RST 0x04 /* Reset Tx FIFO */
-#define UFR_RXTL 0x80 /* Rx FIFO threshold (set to 16) */
-#define UFR_TXTL 0x20 /* Tx FIFO threshold (set to 17) */
-
-#define ISR 0x02 /* Interrupt status register */
-#define ISR_RXTH_I 0x01 /* Receive threshold interrupt */
-#define ISR_TXEMP_I 0x02 /* Transmitter empty interrupt */
-#define ISR_FEND_I 0x04
-#define ISR_DMA_I 0x10
-#define ISR_TXTH_I 0x20 /* Transmitter threshold interrupt */
-#define ISR_FSF_I 0x40
-#define ISR_TMR_I 0x80 /* Timer interrupt */
-
-#define UCR 0x03 /* Uart control register */
-#define UCR_DLS8 0x03 /* 8N1 */
-
-#define SSR 0x03 /* Sets select register */
-#define SET0 UCR_DLS8 /* Make sure we keep 8N1 */
-#define SET1 (0x80|UCR_DLS8) /* Make sure we keep 8N1 */
-#define SET2 0xE0
-#define SET3 0xE4
-#define SET4 0xE8
-#define SET5 0xEC
-#define SET6 0xF0
-#define SET7 0xF4
-
-#define HCR 0x04
-#define HCR_MODE_MASK ~(0xD0)
-#define HCR_SIR 0x60
-#define HCR_MIR_576 0x20
-#define HCR_MIR_1152 0x80
-#define HCR_FIR 0xA0
-#define HCR_EN_DMA 0x04
-#define HCR_EN_IRQ 0x08
-#define HCR_TX_WT 0x08
-
-#define USR 0x05 /* IR status register */
-#define USR_RDR 0x01 /* Receive data ready */
-#define USR_TSRE 0x40 /* Transmitter empty? */
-
-#define AUDR 0x07
-#define AUDR_SFEND 0x08 /* Set a frame end */
-#define AUDR_RXBSY 0x20 /* Rx busy */
-#define AUDR_UNDR 0x40 /* Transeiver underrun */
-
-/* Set 2 */
-#define ABLL 0x00 /* Advanced baud rate divisor latch (low byte) */
-#define ABHL 0x01 /* Advanced baud rate divisor latch (high byte) */
-
-#define ADCR1 0x02
-#define ADCR1_ADV_SL 0x01
-#define ADCR1_D_CHSW 0x08 /* the specs are wrong. its bit 3, not 4 */
-#define ADCR1_DMA_F 0x02
-
-#define ADCR2 0x04
-#define ADCR2_TXFS32 0x01
-#define ADCR2_RXFS32 0x04
-
-#define RXFDTH 0x07
-
-/* Set 3 */
-#define AUID 0x00
-
-/* Set 4 */
-#define TMRL 0x00 /* Timer value register (low byte) */
-#define TMRH 0x01 /* Timer value register (high byte) */
-
-#define IR_MSL 0x02 /* Infrared mode select */
-#define IR_MSL_EN_TMR 0x01 /* Enable timer */
-
-#define TFRLL 0x04 /* Transmitter frame length (low byte) */
-#define TFRLH 0x05 /* Transmitter frame length (high byte) */
-#define RFRLL 0x06 /* Receiver frame length (low byte) */
-#define RFRLH 0x07 /* Receiver frame length (high byte) */
-
-/* Set 5 */
-
-#define FS_FO 0x05 /* Frame status FIFO */
-#define FS_FO_FSFDR 0x80 /* Frame status FIFO data ready */
-#define FS_FO_LST_FR 0x40 /* Frame lost */
-#define FS_FO_MX_LEX 0x10 /* Max frame len exceeded */
-#define FS_FO_PHY_ERR 0x08 /* Physical layer error */
-#define FS_FO_CRC_ERR 0x04
-#define FS_FO_RX_OV 0x02 /* Receive overrun */
-#define FS_FO_FSF_OV 0x01 /* Frame status FIFO overrun */
-#define FS_FO_ERR_MSK 0x5f /* Error mask */
-
-#define RFLFL 0x06
-#define RFLFH 0x07
-
-/* Set 6 */
-#define IR_CFG2 0x00
-#define IR_CFG2_DIS_CRC 0x02
-
-/* Set 7 */
-#define IRM_CR 0x07 /* Infrared module control register */
-#define IRM_CR_IRX_MSL 0x40
-#define IRM_CR_AF_MNT 0x80 /* Automatic format */
-
-/* For storing entries in the status FIFO */
-struct st_fifo_entry {
- int status;
- int len;
-};
-
-struct st_fifo {
- struct st_fifo_entry entries[10];
- int head;
- int tail;
- int len;
-};
-
-/* Private data for each instance */
-struct w83977af_ir {
- struct st_fifo st_fifo;
-
- int tx_buff_offsets[10]; /* Offsets between frames in tx_buff */
- int tx_len; /* Number of frames in tx_buff */
-
- struct net_device *netdev; /* Yes! we are some kind of netdevice */
-
- struct irlap_cb *irlap; /* The link layer we are binded to */
- struct qos_info qos; /* QoS capabilities for this device */
-
- chipio_t io; /* IrDA controller information */
- iobuff_t tx_buff; /* Transmit buffer */
- iobuff_t rx_buff; /* Receive buffer */
- dma_addr_t tx_buff_dma;
- dma_addr_t rx_buff_dma;
-
- /* Note : currently locking is *very* incomplete, but this
- * will get you started. Check in nsc-ircc.c for a proper
- * locking strategy. - Jean II */
- spinlock_t lock; /* For serializing operations */
-
- __u32 new_speed;
-};
-
-static inline void switch_bank( int iobase, int set)
-{
- outb(set, iobase+SSR);
-}
-
-#endif
diff --git a/drivers/staging/irda/include/net/irda/af_irda.h b/drivers/staging/irda/include/net/irda/af_irda.h
deleted file mode 100644
index 0df574931522..000000000000
--- a/drivers/staging/irda/include/net/irda/af_irda.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*********************************************************************
- *
- * Filename: af_irda.h
- * Version: 1.0
- * Description: IrDA sockets declarations
- * Status: Stable
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Tue Dec 9 21:13:12 1997
- * Modified at: Fri Jan 28 13:16:32 2000
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-2000 Dag Brattli, All Rights Reserved.
- * Copyright (c) 2000-2002 Jean Tourrilhes <jt@hpl.hp.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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#ifndef AF_IRDA_H
-#define AF_IRDA_H
-
-#include <linux/irda.h>
-#include <net/irda/irda.h>
-#include <net/irda/iriap.h> /* struct iriap_cb */
-#include <net/irda/irias_object.h> /* struct ias_value */
-#include <net/irda/irlmp.h> /* struct lsap_cb */
-#include <net/irda/irttp.h> /* struct tsap_cb */
-#include <net/irda/discovery.h> /* struct discovery_t */
-#include <net/sock.h>
-
-/* IrDA Socket */
-struct irda_sock {
- /* struct sock has to be the first member of irda_sock */
- struct sock sk;
- __u32 saddr; /* my local address */
- __u32 daddr; /* peer address */
-
- struct lsap_cb *lsap; /* LSAP used by Ultra */
- __u8 pid; /* Protocol IP (PID) used by Ultra */
-
- struct tsap_cb *tsap; /* TSAP used by this connection */
- __u8 dtsap_sel; /* remote TSAP address */
- __u8 stsap_sel; /* local TSAP address */
-
- __u32 max_sdu_size_rx;
- __u32 max_sdu_size_tx;
- __u32 max_data_size;
- __u8 max_header_size;
- struct qos_info qos_tx;
-
- __u16_host_order mask; /* Hint bits mask */
- __u16_host_order hints; /* Hint bits */
-
- void *ckey; /* IrLMP client handle */
- void *skey; /* IrLMP service handle */
-
- struct ias_object *ias_obj; /* Our service name + lsap in IAS */
- struct iriap_cb *iriap; /* Used to query remote IAS */
- struct ias_value *ias_result; /* Result of remote IAS query */
-
- hashbin_t *cachelog; /* Result of discovery query */
- __u32 cachedaddr; /* Result of selective discovery query */
-
- int nslots; /* Number of slots to use for discovery */
-
- int errno; /* status of the IAS query */
-
- wait_queue_head_t query_wait; /* Wait for the answer to a query */
- struct timer_list watchdog; /* Timeout for discovery */
-
- LOCAL_FLOW tx_flow;
- LOCAL_FLOW rx_flow;
-};
-
-static inline struct irda_sock *irda_sk(struct sock *sk)
-{
- return (struct irda_sock *)sk;
-}
-
-#endif /* AF_IRDA_H */
diff --git a/drivers/staging/irda/include/net/irda/crc.h b/drivers/staging/irda/include/net/irda/crc.h
deleted file mode 100644
index f202296df9bb..000000000000
--- a/drivers/staging/irda/include/net/irda/crc.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*********************************************************************
- *
- * Filename: crc.h
- * Version:
- * Description: CRC routines
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Mon Aug 4 20:40:53 1997
- * Modified at: Sun May 2 20:25:23 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- ********************************************************************/
-
-#ifndef IRDA_CRC_H
-#define IRDA_CRC_H
-
-#include <linux/types.h>
-#include <linux/crc-ccitt.h>
-
-#define INIT_FCS 0xffff /* Initial FCS value */
-#define GOOD_FCS 0xf0b8 /* Good final FCS value */
-
-/* Recompute the FCS with one more character appended. */
-#define irda_fcs(fcs, c) crc_ccitt_byte(fcs, c)
-
-/* Recompute the FCS with len bytes appended. */
-#define irda_calc_crc16(fcs, buf, len) crc_ccitt(fcs, buf, len)
-
-#endif
diff --git a/drivers/staging/irda/include/net/irda/discovery.h b/drivers/staging/irda/include/net/irda/discovery.h
deleted file mode 100644
index 63ae32530567..000000000000
--- a/drivers/staging/irda/include/net/irda/discovery.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*********************************************************************
- *
- * Filename: discovery.h
- * Version:
- * Description:
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Tue Apr 6 16:53:53 1999
- * Modified at: Tue Oct 5 10:05:10 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1999 Dag Brattli, All Rights Reserved.
- * Copyright (c) 2000-2002 Jean Tourrilhes <jt@hpl.hp.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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#ifndef DISCOVERY_H
-#define DISCOVERY_H
-
-#include <asm/param.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irqueue.h> /* irda_queue_t */
-#include <net/irda/irlap_event.h> /* LAP_REASON */
-
-#define DISCOVERY_EXPIRE_TIMEOUT (2*sysctl_discovery_timeout*HZ)
-#define DISCOVERY_DEFAULT_SLOTS 0
-
-/*
- * This type is used by the protocols that transmit 16 bits words in
- * little endian format. A little endian machine stores MSB of word in
- * byte[1] and LSB in byte[0]. A big endian machine stores MSB in byte[0]
- * and LSB in byte[1].
- *
- * This structure is used in the code for things that are endian neutral
- * but that fit in a word so that we can manipulate them efficiently.
- * By endian neutral, I mean things that are really an array of bytes,
- * and always used as such, for example the hint bits. Jean II
- */
-typedef union {
- __u16 word;
- __u8 byte[2];
-} __u16_host_order;
-
-/* Types of discovery */
-typedef enum {
- DISCOVERY_LOG, /* What's in our discovery log */
- DISCOVERY_ACTIVE, /* Doing our own discovery on the medium */
- DISCOVERY_PASSIVE, /* Peer doing discovery on the medium */
- EXPIRY_TIMEOUT, /* Entry expired due to timeout */
-} DISCOVERY_MODE;
-
-#define NICKNAME_MAX_LEN 21
-
-/* Basic discovery information about a peer */
-typedef struct irda_device_info discinfo_t; /* linux/irda.h */
-
-/*
- * The DISCOVERY structure is used for both discovery requests and responses
- */
-typedef struct discovery_t {
- irda_queue_t q; /* Must be first! */
-
- discinfo_t data; /* Basic discovery information */
- int name_len; /* Length of nickname */
-
- LAP_REASON condition; /* More info about the discovery */
- int gen_addr_bit; /* Need to generate a new device
- * address? */
- int nslots; /* Number of slots to use when
- * discovering */
- unsigned long timestamp; /* Last time discovered */
- unsigned long firststamp; /* First time discovered */
-} discovery_t;
-
-void irlmp_add_discovery(hashbin_t *cachelog, discovery_t *discovery);
-void irlmp_add_discovery_log(hashbin_t *cachelog, hashbin_t *log);
-void irlmp_expire_discoveries(hashbin_t *log, __u32 saddr, int force);
-struct irda_device_info *irlmp_copy_discoveries(hashbin_t *log, int *pn,
- __u16 mask, int old_entries);
-
-#endif
diff --git a/drivers/staging/irda/include/net/irda/ircomm_core.h b/drivers/staging/irda/include/net/irda/ircomm_core.h
deleted file mode 100644
index 2a580ce9edad..000000000000
--- a/drivers/staging/irda/include/net/irda/ircomm_core.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/*********************************************************************
- *
- * Filename: ircomm_core.h
- * Version:
- * Description:
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Wed Jun 9 08:58:43 1999
- * Modified at: Mon Dec 13 11:52:29 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1999 Dag Brattli, All Rights Reserved.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#ifndef IRCOMM_CORE_H
-#define IRCOMM_CORE_H
-
-#include <net/irda/irda.h>
-#include <net/irda/irqueue.h>
-#include <net/irda/ircomm_event.h>
-
-#define IRCOMM_MAGIC 0x98347298
-#define IRCOMM_HEADER_SIZE 1
-
-struct ircomm_cb; /* Forward decl. */
-
-/*
- * A small call-table, so we don't have to check the service-type whenever
- * we want to do something
- */
-typedef struct {
- int (*data_request)(struct ircomm_cb *, struct sk_buff *, int clen);
- int (*connect_request)(struct ircomm_cb *, struct sk_buff *,
- struct ircomm_info *);
- int (*connect_response)(struct ircomm_cb *, struct sk_buff *);
- int (*disconnect_request)(struct ircomm_cb *, struct sk_buff *,
- struct ircomm_info *);
-} call_t;
-
-struct ircomm_cb {
- irda_queue_t queue;
- magic_t magic;
-
- notify_t notify;
- call_t issue;
-
- int state;
- int line; /* Which TTY line we are using */
-
- struct tsap_cb *tsap;
- struct lsap_cb *lsap;
-
- __u8 dlsap_sel; /* Destination LSAP/TSAP selector */
- __u8 slsap_sel; /* Source LSAP/TSAP selector */
-
- __u32 saddr; /* Source device address (link we are using) */
- __u32 daddr; /* Destination device address */
-
- int max_header_size; /* Header space we must reserve for each frame */
- int max_data_size; /* The amount of data we can fill in each frame */
-
- LOCAL_FLOW flow_status; /* Used by ircomm_lmp */
- int pkt_count; /* Number of frames we have sent to IrLAP */
-
- __u8 service_type;
-};
-
-extern hashbin_t *ircomm;
-
-struct ircomm_cb *ircomm_open(notify_t *notify, __u8 service_type, int line);
-int ircomm_close(struct ircomm_cb *self);
-
-int ircomm_data_request(struct ircomm_cb *self, struct sk_buff *skb);
-void ircomm_data_indication(struct ircomm_cb *self, struct sk_buff *skb);
-void ircomm_process_data(struct ircomm_cb *self, struct sk_buff *skb);
-int ircomm_control_request(struct ircomm_cb *self, struct sk_buff *skb);
-int ircomm_connect_request(struct ircomm_cb *self, __u8 dlsap_sel,
- __u32 saddr, __u32 daddr, struct sk_buff *skb,
- __u8 service_type);
-void ircomm_connect_indication(struct ircomm_cb *self, struct sk_buff *skb,
- struct ircomm_info *info);
-void ircomm_connect_confirm(struct ircomm_cb *self, struct sk_buff *skb,
- struct ircomm_info *info);
-int ircomm_connect_response(struct ircomm_cb *self, struct sk_buff *userdata);
-int ircomm_disconnect_request(struct ircomm_cb *self, struct sk_buff *userdata);
-void ircomm_disconnect_indication(struct ircomm_cb *self, struct sk_buff *skb,
- struct ircomm_info *info);
-void ircomm_flow_request(struct ircomm_cb *self, LOCAL_FLOW flow);
-
-#define ircomm_is_connected(self) (self->state == IRCOMM_CONN)
-
-#endif
diff --git a/drivers/staging/irda/include/net/irda/ircomm_event.h b/drivers/staging/irda/include/net/irda/ircomm_event.h
deleted file mode 100644
index 5bbc32998d57..000000000000
--- a/drivers/staging/irda/include/net/irda/ircomm_event.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*********************************************************************
- *
- * Filename: ircomm_event.h
- * Version:
- * Description:
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sun Jun 6 23:51:13 1999
- * Modified at: Thu Jun 10 08:36:25 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1999 Dag Brattli, All Rights Reserved.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#ifndef IRCOMM_EVENT_H
-#define IRCOMM_EVENT_H
-
-#include <net/irda/irmod.h>
-
-typedef enum {
- IRCOMM_IDLE,
- IRCOMM_WAITI,
- IRCOMM_WAITR,
- IRCOMM_CONN,
-} IRCOMM_STATE;
-
-/* IrCOMM Events */
-typedef enum {
- IRCOMM_CONNECT_REQUEST,
- IRCOMM_CONNECT_RESPONSE,
- IRCOMM_TTP_CONNECT_INDICATION,
- IRCOMM_LMP_CONNECT_INDICATION,
- IRCOMM_TTP_CONNECT_CONFIRM,
- IRCOMM_LMP_CONNECT_CONFIRM,
-
- IRCOMM_LMP_DISCONNECT_INDICATION,
- IRCOMM_TTP_DISCONNECT_INDICATION,
- IRCOMM_DISCONNECT_REQUEST,
-
- IRCOMM_TTP_DATA_INDICATION,
- IRCOMM_LMP_DATA_INDICATION,
- IRCOMM_DATA_REQUEST,
- IRCOMM_CONTROL_REQUEST,
- IRCOMM_CONTROL_INDICATION,
-} IRCOMM_EVENT;
-
-/*
- * Used for passing information through the state-machine
- */
-struct ircomm_info {
- __u32 saddr; /* Source device address */
- __u32 daddr; /* Destination device address */
- __u8 dlsap_sel;
- LM_REASON reason; /* Reason for disconnect */
- __u32 max_data_size;
- __u32 max_header_size;
-
- struct qos_info *qos;
-};
-
-extern const char *const ircomm_state[];
-
-struct ircomm_cb; /* Forward decl. */
-
-int ircomm_do_event(struct ircomm_cb *self, IRCOMM_EVENT event,
- struct sk_buff *skb, struct ircomm_info *info);
-void ircomm_next_state(struct ircomm_cb *self, IRCOMM_STATE state);
-
-#endif
diff --git a/drivers/staging/irda/include/net/irda/ircomm_lmp.h b/drivers/staging/irda/include/net/irda/ircomm_lmp.h
deleted file mode 100644
index 5042a5021a04..000000000000
--- a/drivers/staging/irda/include/net/irda/ircomm_lmp.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*********************************************************************
- *
- * Filename: ircomm_lmp.h
- * Version:
- * Description:
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Wed Jun 9 10:06:07 1999
- * Modified at: Fri Aug 13 07:32:32 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1999 Dag Brattli, All Rights Reserved.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#ifndef IRCOMM_LMP_H
-#define IRCOMM_LMP_H
-
-#include <net/irda/ircomm_core.h>
-
-int ircomm_open_lsap(struct ircomm_cb *self);
-
-#endif
diff --git a/drivers/staging/irda/include/net/irda/ircomm_param.h b/drivers/staging/irda/include/net/irda/ircomm_param.h
deleted file mode 100644
index 1f67432321c4..000000000000
--- a/drivers/staging/irda/include/net/irda/ircomm_param.h
+++ /dev/null
@@ -1,147 +0,0 @@
-/*********************************************************************
- *
- * Filename: ircomm_param.h
- * Version: 1.0
- * Description: Parameter handling for the IrCOMM protocol
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Mon Jun 7 08:47:28 1999
- * Modified at: Wed Aug 25 13:46:33 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1999 Dag Brattli, All Rights Reserved.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#ifndef IRCOMM_PARAMS_H
-#define IRCOMM_PARAMS_H
-
-#include <net/irda/parameters.h>
-
-/* Parameters common to all service types */
-#define IRCOMM_SERVICE_TYPE 0x00
-#define IRCOMM_PORT_TYPE 0x01 /* Only used in LM-IAS */
-#define IRCOMM_PORT_NAME 0x02 /* Only used in LM-IAS */
-
-/* Parameters for both 3 wire and 9 wire */
-#define IRCOMM_DATA_RATE 0x10
-#define IRCOMM_DATA_FORMAT 0x11
-#define IRCOMM_FLOW_CONTROL 0x12
-#define IRCOMM_XON_XOFF 0x13
-#define IRCOMM_ENQ_ACK 0x14
-#define IRCOMM_LINE_STATUS 0x15
-#define IRCOMM_BREAK 0x16
-
-/* Parameters for 9 wire */
-#define IRCOMM_DTE 0x20
-#define IRCOMM_DCE 0x21
-#define IRCOMM_POLL 0x22
-
-/* Service type (details) */
-#define IRCOMM_3_WIRE_RAW 0x01
-#define IRCOMM_3_WIRE 0x02
-#define IRCOMM_9_WIRE 0x04
-#define IRCOMM_CENTRONICS 0x08
-
-/* Port type (details) */
-#define IRCOMM_SERIAL 0x00
-#define IRCOMM_PARALLEL 0x01
-
-/* Data format (details) */
-#define IRCOMM_WSIZE_5 0x00
-#define IRCOMM_WSIZE_6 0x01
-#define IRCOMM_WSIZE_7 0x02
-#define IRCOMM_WSIZE_8 0x03
-
-#define IRCOMM_1_STOP_BIT 0x00
-#define IRCOMM_2_STOP_BIT 0x04 /* 1.5 if char len 5 */
-
-#define IRCOMM_PARITY_DISABLE 0x00
-#define IRCOMM_PARITY_ENABLE 0x08
-
-#define IRCOMM_PARITY_ODD 0x00
-#define IRCOMM_PARITY_EVEN 0x10
-#define IRCOMM_PARITY_MARK 0x20
-#define IRCOMM_PARITY_SPACE 0x30
-
-/* Flow control */
-#define IRCOMM_XON_XOFF_IN 0x01
-#define IRCOMM_XON_XOFF_OUT 0x02
-#define IRCOMM_RTS_CTS_IN 0x04
-#define IRCOMM_RTS_CTS_OUT 0x08
-#define IRCOMM_DSR_DTR_IN 0x10
-#define IRCOMM_DSR_DTR_OUT 0x20
-#define IRCOMM_ENQ_ACK_IN 0x40
-#define IRCOMM_ENQ_ACK_OUT 0x80
-
-/* Line status */
-#define IRCOMM_OVERRUN_ERROR 0x02
-#define IRCOMM_PARITY_ERROR 0x04
-#define IRCOMM_FRAMING_ERROR 0x08
-
-/* DTE (Data terminal equipment) line settings */
-#define IRCOMM_DELTA_DTR 0x01
-#define IRCOMM_DELTA_RTS 0x02
-#define IRCOMM_DTR 0x04
-#define IRCOMM_RTS 0x08
-
-/* DCE (Data communications equipment) line settings */
-#define IRCOMM_DELTA_CTS 0x01 /* Clear to send has changed */
-#define IRCOMM_DELTA_DSR 0x02 /* Data set ready has changed */
-#define IRCOMM_DELTA_RI 0x04 /* Ring indicator has changed */
-#define IRCOMM_DELTA_CD 0x08 /* Carrier detect has changed */
-#define IRCOMM_CTS 0x10 /* Clear to send is high */
-#define IRCOMM_DSR 0x20 /* Data set ready is high */
-#define IRCOMM_RI 0x40 /* Ring indicator is high */
-#define IRCOMM_CD 0x80 /* Carrier detect is high */
-#define IRCOMM_DCE_DELTA_ANY 0x0f
-
-/*
- * Parameter state
- */
-struct ircomm_params {
- /* General control params */
- __u8 service_type;
- __u8 port_type;
- char port_name[32];
-
- /* Control params for 3- and 9-wire service type */
- __u32 data_rate; /* Data rate in bps */
- __u8 data_format;
- __u8 flow_control;
- char xonxoff[2];
- char enqack[2];
- __u8 line_status;
- __u8 _break;
-
- __u8 null_modem;
-
- /* Control params for 9-wire service type */
- __u8 dte;
- __u8 dce;
- __u8 poll;
-
- /* Control params for Centronics service type */
-};
-
-struct ircomm_tty_cb; /* Forward decl. */
-
-int ircomm_param_request(struct ircomm_tty_cb *self, __u8 pi, int flush);
-
-extern pi_param_info_t ircomm_param_info;
-
-#endif /* IRCOMM_PARAMS_H */
-
diff --git a/drivers/staging/irda/include/net/irda/ircomm_ttp.h b/drivers/staging/irda/include/net/irda/ircomm_ttp.h
deleted file mode 100644
index c5627288bca3..000000000000
--- a/drivers/staging/irda/include/net/irda/ircomm_ttp.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*********************************************************************
- *
- * Filename: ircomm_ttp.h
- * Version:
- * Description:
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Wed Jun 9 10:06:07 1999
- * Modified at: Fri Aug 13 07:32:22 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1999 Dag Brattli, All Rights Reserved.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#ifndef IRCOMM_TTP_H
-#define IRCOMM_TTP_H
-
-#include <net/irda/ircomm_core.h>
-
-int ircomm_open_tsap(struct ircomm_cb *self);
-
-#endif
-
diff --git a/drivers/staging/irda/include/net/irda/ircomm_tty.h b/drivers/staging/irda/include/net/irda/ircomm_tty.h
deleted file mode 100644
index 8d4f588974bc..000000000000
--- a/drivers/staging/irda/include/net/irda/ircomm_tty.h
+++ /dev/null
@@ -1,121 +0,0 @@
-/*********************************************************************
- *
- * Filename: ircomm_tty.h
- * Version:
- * Description:
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sun Jun 6 23:24:22 1999
- * Modified at: Fri Jan 28 13:16:57 2000
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#ifndef IRCOMM_TTY_H
-#define IRCOMM_TTY_H
-
-#include <linux/serial.h>
-#include <linux/termios.h>
-#include <linux/timer.h>
-#include <linux/tty.h> /* struct tty_struct */
-
-#include <net/irda/irias_object.h>
-#include <net/irda/ircomm_core.h>
-#include <net/irda/ircomm_param.h>
-
-#define IRCOMM_TTY_PORTS 32
-#define IRCOMM_TTY_MAGIC 0x3432
-#define IRCOMM_TTY_MAJOR 161
-#define IRCOMM_TTY_MINOR 0
-
-/* This is used as an initial value to max_header_size before the proper
- * value is filled in (5 for ttp, 4 for lmp). This allow us to detect
- * the state of the underlying connection. - Jean II */
-#define IRCOMM_TTY_HDR_UNINITIALISED 16
-/* Same for payload size. See qos.c for the smallest max data size */
-#define IRCOMM_TTY_DATA_UNINITIALISED (64 - IRCOMM_TTY_HDR_UNINITIALISED)
-
-/*
- * IrCOMM TTY driver state
- */
-struct ircomm_tty_cb {
- irda_queue_t queue; /* Must be first */
- struct tty_port port;
- magic_t magic;
-
- int state; /* Connect state */
-
- struct ircomm_cb *ircomm; /* IrCOMM layer instance */
-
- struct sk_buff *tx_skb; /* Transmit buffer */
- struct sk_buff *ctrl_skb; /* Control data buffer */
-
- /* Parameters */
- struct ircomm_params settings;
-
- __u8 service_type; /* The service that we support */
- int client; /* True if we are a client */
- LOCAL_FLOW flow; /* IrTTP flow status */
-
- int line;
-
- __u8 dlsap_sel;
- __u8 slsap_sel;
-
- __u32 saddr;
- __u32 daddr;
-
- __u32 max_data_size; /* Max data we can transmit in one packet */
- __u32 max_header_size; /* The amount of header space we must reserve */
- __u32 tx_data_size; /* Max data size of current tx_skb */
-
- struct iriap_cb *iriap; /* Instance used for querying remote IAS */
- struct ias_object* obj;
- void *skey;
- void *ckey;
-
- struct timer_list watchdog_timer;
- struct work_struct tqueue;
-
- /* Protect concurent access to :
- * o self->ctrl_skb
- * o self->tx_skb
- * Maybe other things may gain to be protected as well...
- * Jean II */
- spinlock_t spinlock;
-};
-
-void ircomm_tty_start(struct tty_struct *tty);
-void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self);
-
-int ircomm_tty_tiocmget(struct tty_struct *tty);
-int ircomm_tty_tiocmset(struct tty_struct *tty, unsigned int set,
- unsigned int clear);
-int ircomm_tty_ioctl(struct tty_struct *tty, unsigned int cmd,
- unsigned long arg);
-void ircomm_tty_set_termios(struct tty_struct *tty,
- struct ktermios *old_termios);
-
-#endif
-
-
-
-
-
-
-
diff --git a/drivers/staging/irda/include/net/irda/ircomm_tty_attach.h b/drivers/staging/irda/include/net/irda/ircomm_tty_attach.h
deleted file mode 100644
index 20dcbdf258cf..000000000000
--- a/drivers/staging/irda/include/net/irda/ircomm_tty_attach.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*********************************************************************
- *
- * Filename: ircomm_tty_attach.h
- * Version:
- * Description:
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Wed Jun 9 15:55:18 1999
- * Modified at: Fri Dec 10 21:04:55 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1999 Dag Brattli, All Rights Reserved.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#ifndef IRCOMM_TTY_ATTACH_H
-#define IRCOMM_TTY_ATTACH_H
-
-#include <net/irda/ircomm_tty.h>
-
-typedef enum {
- IRCOMM_TTY_IDLE,
- IRCOMM_TTY_SEARCH,
- IRCOMM_TTY_QUERY_PARAMETERS,
- IRCOMM_TTY_QUERY_LSAP_SEL,
- IRCOMM_TTY_SETUP,
- IRCOMM_TTY_READY,
-} IRCOMM_TTY_STATE;
-
-/* IrCOMM TTY Events */
-typedef enum {
- IRCOMM_TTY_ATTACH_CABLE,
- IRCOMM_TTY_DETACH_CABLE,
- IRCOMM_TTY_DATA_REQUEST,
- IRCOMM_TTY_DATA_INDICATION,
- IRCOMM_TTY_DISCOVERY_REQUEST,
- IRCOMM_TTY_DISCOVERY_INDICATION,
- IRCOMM_TTY_CONNECT_CONFIRM,
- IRCOMM_TTY_CONNECT_INDICATION,
- IRCOMM_TTY_DISCONNECT_REQUEST,
- IRCOMM_TTY_DISCONNECT_INDICATION,
- IRCOMM_TTY_WD_TIMER_EXPIRED,
- IRCOMM_TTY_GOT_PARAMETERS,
- IRCOMM_TTY_GOT_LSAPSEL,
-} IRCOMM_TTY_EVENT;
-
-/* Used for passing information through the state-machine */
-struct ircomm_tty_info {
- __u32 saddr; /* Source device address */
- __u32 daddr; /* Destination device address */
- __u8 dlsap_sel;
-};
-
-extern const char *const ircomm_state[];
-extern const char *const ircomm_tty_state[];
-
-int ircomm_tty_do_event(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event,
- struct sk_buff *skb, struct ircomm_tty_info *info);
-
-
-int ircomm_tty_attach_cable(struct ircomm_tty_cb *self);
-void ircomm_tty_detach_cable(struct ircomm_tty_cb *self);
-void ircomm_tty_connect_confirm(void *instance, void *sap,
- struct qos_info *qos,
- __u32 max_sdu_size,
- __u8 max_header_size,
- struct sk_buff *skb);
-void ircomm_tty_disconnect_indication(void *instance, void *sap,
- LM_REASON reason,
- struct sk_buff *skb);
-void ircomm_tty_connect_indication(void *instance, void *sap,
- struct qos_info *qos,
- __u32 max_sdu_size,
- __u8 max_header_size,
- struct sk_buff *skb);
-int ircomm_tty_send_initial_parameters(struct ircomm_tty_cb *self);
-void ircomm_tty_link_established(struct ircomm_tty_cb *self);
-
-#endif /* IRCOMM_TTY_ATTACH_H */
diff --git a/drivers/staging/irda/include/net/irda/irda.h b/drivers/staging/irda/include/net/irda/irda.h
deleted file mode 100644
index 92c8fb575213..000000000000
--- a/drivers/staging/irda/include/net/irda/irda.h
+++ /dev/null
@@ -1,115 +0,0 @@
-/*********************************************************************
- *
- * Filename: irda.h
- * Version: 1.0
- * Description: IrDA common include file for kernel internal use
- * Status: Stable
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Tue Dec 9 21:13:12 1997
- * Modified at: Fri Jan 28 13:16:32 2000
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-2000 Dag Brattli, All Rights Reserved.
- * Copyright (c) 2000-2002 Jean Tourrilhes <jt@hpl.hp.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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#ifndef NET_IRDA_H
-#define NET_IRDA_H
-
-#include <linux/skbuff.h> /* struct sk_buff */
-#include <linux/kernel.h>
-#include <linux/if.h> /* sa_family_t in <linux/irda.h> */
-#include <linux/irda.h>
-
-typedef __u32 magic_t;
-
-#ifndef TRUE
-#define TRUE 1
-#endif
-
-#ifndef FALSE
-#define FALSE 0
-#endif
-
-/* Hack to do small backoff when setting media busy in IrLAP */
-#ifndef SMALL
-#define SMALL 5
-#endif
-
-#ifndef IRDA_MIN /* Lets not mix this MIN with other header files */
-#define IRDA_MIN(a, b) (((a) < (b)) ? (a) : (b))
-#endif
-
-#ifndef IRDA_ALIGN
-# define IRDA_ALIGN __attribute__((aligned))
-#endif
-
-#ifdef CONFIG_IRDA_DEBUG
-#define IRDA_ASSERT(expr, func) \
-do { if(!(expr)) { \
- printk( "Assertion failed! %s:%s:%d %s\n", \
- __FILE__,__func__,__LINE__,(#expr) ); \
- func } } while (0)
-#define IRDA_ASSERT_LABEL(label) label
-#else
-#define IRDA_ASSERT(expr, func) do { (void)(expr); } while (0)
-#define IRDA_ASSERT_LABEL(label)
-#endif /* CONFIG_IRDA_DEBUG */
-
-/*
- * Magic numbers used by Linux-IrDA. Random numbers which must be unique to
- * give the best protection
- */
-
-#define IRTTY_MAGIC 0x2357
-#define LAP_MAGIC 0x1357
-#define LMP_MAGIC 0x4321
-#define LMP_LSAP_MAGIC 0x69333
-#define LMP_LAP_MAGIC 0x3432
-#define IRDA_DEVICE_MAGIC 0x63454
-#define IAS_MAGIC 0x007
-#define TTP_MAGIC 0x241169
-#define TTP_TSAP_MAGIC 0x4345
-#define IROBEX_MAGIC 0x341324
-#define HB_MAGIC 0x64534
-#define IRLAN_MAGIC 0x754
-#define IAS_OBJECT_MAGIC 0x34234
-#define IAS_ATTRIB_MAGIC 0x45232
-#define IRDA_TASK_MAGIC 0x38423
-
-#define IAS_DEVICE_ID 0x0000 /* Defined by IrDA, IrLMP section 4.1 (page 68) */
-#define IAS_PNP_ID 0xd342
-#define IAS_OBEX_ID 0x34323
-#define IAS_IRLAN_ID 0x34234
-#define IAS_IRCOMM_ID 0x2343
-#define IAS_IRLPT_ID 0x9876
-
-struct net_device;
-struct packet_type;
-
-void irda_proc_register(void);
-void irda_proc_unregister(void);
-
-int irda_sysctl_register(void);
-void irda_sysctl_unregister(void);
-
-int irsock_init(void);
-void irsock_cleanup(void);
-
-int irda_nl_register(void);
-void irda_nl_unregister(void);
-
-int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev,
- struct packet_type *ptype, struct net_device *orig_dev);
-
-#endif /* NET_IRDA_H */
diff --git a/drivers/staging/irda/include/net/irda/irda_device.h b/drivers/staging/irda/include/net/irda/irda_device.h
deleted file mode 100644
index 664bf8178412..000000000000
--- a/drivers/staging/irda/include/net/irda/irda_device.h
+++ /dev/null
@@ -1,285 +0,0 @@
-/*********************************************************************
- *
- * Filename: irda_device.h
- * Version: 0.9
- * Description: Contains various declarations used by the drivers
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Tue Apr 14 12:41:42 1998
- * Modified at: Mon Mar 20 09:08:57 2000
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
- * Copyright (c) 1998 Thomas Davis, <ratbert@radiks.net>,
- * Copyright (c) 2000-2002 Jean Tourrilhes <jt@hpl.hp.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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-/*
- * This header contains all the IrDA definitions a driver really
- * needs, and therefore the driver should not need to include
- * any other IrDA headers - Jean II
- */
-
-#ifndef IRDA_DEVICE_H
-#define IRDA_DEVICE_H
-
-#include <linux/tty.h>
-#include <linux/netdevice.h>
-#include <linux/spinlock.h>
-#include <linux/skbuff.h> /* struct sk_buff */
-#include <linux/irda.h>
-#include <linux/types.h>
-
-#include <net/pkt_sched.h>
-#include <net/irda/irda.h>
-#include <net/irda/qos.h> /* struct qos_info */
-#include <net/irda/irqueue.h> /* irda_queue_t */
-
-/* A few forward declarations (to make compiler happy) */
-struct irlap_cb;
-
-/* Some non-standard interface flags (should not conflict with any in if.h) */
-#define IFF_SIR 0x0001 /* Supports SIR speeds */
-#define IFF_MIR 0x0002 /* Supports MIR speeds */
-#define IFF_FIR 0x0004 /* Supports FIR speeds */
-#define IFF_VFIR 0x0008 /* Supports VFIR speeds */
-#define IFF_PIO 0x0010 /* Supports PIO transfer of data */
-#define IFF_DMA 0x0020 /* Supports DMA transfer of data */
-#define IFF_SHM 0x0040 /* Supports shared memory data transfers */
-#define IFF_DONGLE 0x0080 /* Interface has a dongle attached */
-#define IFF_AIR 0x0100 /* Supports Advanced IR (AIR) standards */
-
-#define IO_XMIT 0x01
-#define IO_RECV 0x02
-
-typedef enum {
- IRDA_IRLAP, /* IrDA mode, and deliver to IrLAP */
- IRDA_RAW, /* IrDA mode */
- SHARP_ASK,
- TV_REMOTE, /* Also known as Consumer Electronics IR */
-} INFRARED_MODE;
-
-typedef enum {
- IRDA_TASK_INIT, /* All tasks are initialized with this state */
- IRDA_TASK_DONE, /* Signals that the task is finished */
- IRDA_TASK_WAIT,
- IRDA_TASK_WAIT1,
- IRDA_TASK_WAIT2,
- IRDA_TASK_WAIT3,
- IRDA_TASK_CHILD_INIT, /* Initializing child task */
- IRDA_TASK_CHILD_WAIT, /* Waiting for child task to finish */
- IRDA_TASK_CHILD_DONE /* Child task is finished */
-} IRDA_TASK_STATE;
-
-struct irda_task;
-typedef int (*IRDA_TASK_CALLBACK) (struct irda_task *task);
-
-struct irda_task {
- irda_queue_t q;
- magic_t magic;
-
- IRDA_TASK_STATE state;
- IRDA_TASK_CALLBACK function;
- IRDA_TASK_CALLBACK finished;
-
- struct irda_task *parent;
- struct timer_list timer;
-
- void *instance; /* Instance being called */
- void *param; /* Parameter to be used by instance */
-};
-
-/* Dongle info */
-struct dongle_reg;
-typedef struct {
- struct dongle_reg *issue; /* Registration info */
- struct net_device *dev; /* Device we are attached to */
- struct irda_task *speed_task; /* Task handling speed change */
- struct irda_task *reset_task; /* Task handling reset */
- __u32 speed; /* Current speed */
-
- /* Callbacks to the IrDA device driver */
- int (*set_mode)(struct net_device *, int mode);
- int (*read)(struct net_device *dev, __u8 *buf, int len);
- int (*write)(struct net_device *dev, __u8 *buf, int len);
- int (*set_dtr_rts)(struct net_device *dev, int dtr, int rts);
-} dongle_t;
-
-/* Dongle registration info */
-struct dongle_reg {
- irda_queue_t q; /* Must be first */
- IRDA_DONGLE type;
-
- void (*open)(dongle_t *dongle, struct qos_info *qos);
- void (*close)(dongle_t *dongle);
- int (*reset)(struct irda_task *task);
- int (*change_speed)(struct irda_task *task);
- struct module *owner;
-};
-
-/*
- * Per-packet information we need to hide inside sk_buff
- * (must not exceed 48 bytes, check with struct sk_buff)
- * The default_qdisc_pad field is a temporary hack.
- */
-struct irda_skb_cb {
- unsigned int default_qdisc_pad;
- magic_t magic; /* Be sure that we can trust the information */
- __u32 next_speed; /* The Speed to be set *after* this frame */
- __u16 mtt; /* Minimum turn around time */
- __u16 xbofs; /* Number of xbofs required, used by SIR mode */
- __u16 next_xbofs; /* Number of xbofs required *after* this frame */
- void *context; /* May be used by drivers */
- void (*destructor)(struct sk_buff *skb); /* Used for flow control */
- __u16 xbofs_delay; /* Number of xbofs used for generating the mtt */
- __u8 line; /* Used by IrCOMM in IrLPT mode */
-};
-
-/* Chip specific info */
-typedef struct {
- int cfg_base; /* Config register IO base */
- int sir_base; /* SIR IO base */
- int fir_base; /* FIR IO base */
- int mem_base; /* Shared memory base */
- int sir_ext; /* Length of SIR iobase */
- int fir_ext; /* Length of FIR iobase */
- int irq, irq2; /* Interrupts used */
- int dma, dma2; /* DMA channel(s) used */
- int fifo_size; /* FIFO size */
- int irqflags; /* interrupt flags (ie, IRQF_SHARED) */
- int direction; /* Link direction, used by some FIR drivers */
- int enabled; /* Powered on? */
- int suspended; /* Suspended by APM */
- __u32 speed; /* Currently used speed */
- __u32 new_speed; /* Speed we must change to when Tx is finished */
- int dongle_id; /* Dongle or transceiver currently used */
-} chipio_t;
-
-/* IO buffer specific info (inspired by struct sk_buff) */
-typedef struct {
- int state; /* Receiving state (transmit state not used) */
- int in_frame; /* True if receiving frame */
-
- __u8 *head; /* start of buffer */
- __u8 *data; /* start of data in buffer */
-
- int len; /* current length of data */
- int truesize; /* total allocated size of buffer */
- __u16 fcs;
-
- struct sk_buff *skb; /* ZeroCopy Rx in async_unwrap_char() */
-} iobuff_t;
-
-/* Maximum SIR frame (skb) that we expect to receive *unwrapped*.
- * Max LAP MTU (I field) is 2048 bytes max (IrLAP 1.1, chapt 6.6.5, p40).
- * Max LAP header is 2 bytes (for now).
- * Max CRC is 2 bytes at SIR, 4 bytes at FIR.
- * Need 1 byte for skb_reserve() to align IP header for IrLAN.
- * Add a few extra bytes just to be safe (buffer is power of two anyway)
- * Jean II */
-#define IRDA_SKB_MAX_MTU 2064
-/* Maximum SIR frame that we expect to send, wrapped (i.e. with XBOFS
- * and escaped characters on top of above). */
-#define IRDA_SIR_MAX_FRAME 4269
-
-/* The SIR unwrapper async_unwrap_char() will use a Rx-copy-break mechanism
- * when using the optional ZeroCopy Rx, where only small frames are memcpy
- * to a smaller skb to save memory. This is the threshold under which copy
- * will happen (and over which it won't happen).
- * Some FIR drivers may use this #define as well...
- * This is the same value as various Ethernet drivers. - Jean II */
-#define IRDA_RX_COPY_THRESHOLD 256
-
-/* Function prototypes */
-int irda_device_init(void);
-void irda_device_cleanup(void);
-
-/* IrLAP entry points used by the drivers.
- * We declare them here to avoid the driver pulling a whole bunch stack
- * headers they don't really need - Jean II */
-struct irlap_cb *irlap_open(struct net_device *dev, struct qos_info *qos,
- const char *hw_name);
-void irlap_close(struct irlap_cb *self);
-
-/* Interface to be uses by IrLAP */
-void irda_device_set_media_busy(struct net_device *dev, int status);
-int irda_device_is_media_busy(struct net_device *dev);
-int irda_device_is_receiving(struct net_device *dev);
-
-/* Interface for internal use */
-static inline int irda_device_txqueue_empty(const struct net_device *dev)
-{
- return qdisc_all_tx_empty(dev);
-}
-int irda_device_set_raw_mode(struct net_device* self, int status);
-struct net_device *alloc_irdadev(int sizeof_priv);
-
-void irda_setup_dma(int channel, dma_addr_t buffer, int count, int mode);
-
-/*
- * Function irda_get_mtt (skb)
- *
- * Utility function for getting the minimum turnaround time out of
- * the skb, where it has been hidden in the cb field.
- */
-static inline __u16 irda_get_mtt(const struct sk_buff *skb)
-{
- const struct irda_skb_cb *cb = (const struct irda_skb_cb *) skb->cb;
- return (cb->magic == LAP_MAGIC) ? cb->mtt : 10000;
-}
-
-/*
- * Function irda_get_next_speed (skb)
- *
- * Extract the speed that should be set *after* this frame from the skb
- *
- * Note : return -1 for user space frames
- */
-static inline __u32 irda_get_next_speed(const struct sk_buff *skb)
-{
- const struct irda_skb_cb *cb = (const struct irda_skb_cb *) skb->cb;
- return (cb->magic == LAP_MAGIC) ? cb->next_speed : -1;
-}
-
-/*
- * Function irda_get_next_xbofs (skb)
- *
- * Extract the xbofs that should be set for this frame from the skb
- *
- * Note : default to 10 for user space frames
- */
-static inline __u16 irda_get_xbofs(const struct sk_buff *skb)
-{
- const struct irda_skb_cb *cb = (const struct irda_skb_cb *) skb->cb;
- return (cb->magic == LAP_MAGIC) ? cb->xbofs : 10;
-}
-
-/*
- * Function irda_get_next_xbofs (skb)
- *
- * Extract the xbofs that should be set *after* this frame from the skb
- *
- * Note : return -1 for user space frames
- */
-static inline __u16 irda_get_next_xbofs(const struct sk_buff *skb)
-{
- const struct irda_skb_cb *cb = (const struct irda_skb_cb *) skb->cb;
- return (cb->magic == LAP_MAGIC) ? cb->next_xbofs : -1;
-}
-#endif /* IRDA_DEVICE_H */
-
-
diff --git a/drivers/staging/irda/include/net/irda/iriap.h b/drivers/staging/irda/include/net/irda/iriap.h
deleted file mode 100644
index fcc896491a95..000000000000
--- a/drivers/staging/irda/include/net/irda/iriap.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/*********************************************************************
- *
- * Filename: iriap.h
- * Version: 0.5
- * Description: Information Access Protocol (IAP)
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Thu Aug 21 00:02:07 1997
- * Modified at: Sat Dec 25 16:42:09 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1997-1999 Dag Brattli <dagb@cs.uit.no>,
- * All Rights Reserved.
- *
- * 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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#ifndef IRIAP_H
-#define IRIAP_H
-
-#include <linux/types.h>
-#include <linux/skbuff.h>
-
-#include <net/irda/iriap_event.h>
-#include <net/irda/irias_object.h>
-#include <net/irda/irqueue.h> /* irda_queue_t */
-#include <net/irda/timer.h> /* struct timer_list */
-
-#define IAP_LST 0x80
-#define IAP_ACK 0x40
-
-#define IAS_SERVER 0
-#define IAS_CLIENT 1
-
-/* IrIAP Op-codes */
-#define GET_INFO_BASE 0x01
-#define GET_OBJECTS 0x02
-#define GET_VALUE 0x03
-#define GET_VALUE_BY_CLASS 0x04
-#define GET_OBJECT_INFO 0x05
-#define GET_ATTRIB_NAMES 0x06
-
-#define IAS_SUCCESS 0
-#define IAS_CLASS_UNKNOWN 1
-#define IAS_ATTRIB_UNKNOWN 2
-#define IAS_DISCONNECT 10
-
-typedef void (*CONFIRM_CALLBACK)(int result, __u16 obj_id,
- struct ias_value *value, void *priv);
-
-struct iriap_cb {
- irda_queue_t q; /* Must be first */
- magic_t magic; /* Magic cookie */
-
- int mode; /* Client or server */
-
- __u32 saddr;
- __u32 daddr;
- __u8 operation;
-
- struct sk_buff *request_skb;
- struct lsap_cb *lsap;
- __u8 slsap_sel;
-
- /* Client states */
- IRIAP_STATE client_state;
- IRIAP_STATE call_state;
-
- /* Server states */
- IRIAP_STATE server_state;
- IRIAP_STATE r_connect_state;
-
- CONFIRM_CALLBACK confirm;
- void *priv; /* Used to identify client */
-
- __u8 max_header_size;
- __u32 max_data_size;
-
- struct timer_list watchdog_timer;
-};
-
-int iriap_init(void);
-void iriap_cleanup(void);
-
-struct iriap_cb *iriap_open(__u8 slsap_sel, int mode, void *priv,
- CONFIRM_CALLBACK callback);
-void iriap_close(struct iriap_cb *self);
-
-int iriap_getvaluebyclass_request(struct iriap_cb *self,
- __u32 saddr, __u32 daddr,
- char *name, char *attr);
-void iriap_connect_request(struct iriap_cb *self);
-void iriap_send_ack( struct iriap_cb *self);
-void iriap_call_indication(struct iriap_cb *self, struct sk_buff *skb);
-
-void iriap_register_server(void);
-
-#endif
-
-
diff --git a/drivers/staging/irda/include/net/irda/iriap_event.h b/drivers/staging/irda/include/net/irda/iriap_event.h
deleted file mode 100644
index 89747f06d9eb..000000000000
--- a/drivers/staging/irda/include/net/irda/iriap_event.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/*********************************************************************
- *
- * Filename: iriap_event.h
- * Version:
- * Description:
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Mon Aug 4 20:40:53 1997
- * Modified at: Sun Oct 31 22:02:54 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
- *
- * 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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#ifndef IRIAP_FSM_H
-#define IRIAP_FSM_H
-
-/* Forward because of circular include dependecies */
-struct iriap_cb;
-
-/* IrIAP states */
-typedef enum {
- /* Client */
- S_DISCONNECT,
- S_CONNECTING,
- S_CALL,
-
- /* S-Call */
- S_MAKE_CALL,
- S_CALLING,
- S_OUTSTANDING,
- S_REPLYING,
- S_WAIT_FOR_CALL,
- S_WAIT_ACTIVE,
-
- /* Server */
- R_DISCONNECT,
- R_CALL,
-
- /* R-Connect */
- R_WAITING,
- R_WAIT_ACTIVE,
- R_RECEIVING,
- R_EXECUTE,
- R_RETURNING,
-} IRIAP_STATE;
-
-typedef enum {
- IAP_CALL_REQUEST,
- IAP_CALL_REQUEST_GVBC,
- IAP_CALL_RESPONSE,
- IAP_RECV_F_LST,
- IAP_LM_DISCONNECT_INDICATION,
- IAP_LM_CONNECT_INDICATION,
- IAP_LM_CONNECT_CONFIRM,
-} IRIAP_EVENT;
-
-void iriap_next_client_state (struct iriap_cb *self, IRIAP_STATE state);
-void iriap_next_call_state (struct iriap_cb *self, IRIAP_STATE state);
-void iriap_next_server_state (struct iriap_cb *self, IRIAP_STATE state);
-void iriap_next_r_connect_state(struct iriap_cb *self, IRIAP_STATE state);
-
-
-void iriap_do_client_event(struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb);
-void iriap_do_call_event (struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb);
-
-void iriap_do_server_event (struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb);
-void iriap_do_r_connect_event(struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb);
-
-#endif /* IRIAP_FSM_H */
-
diff --git a/drivers/staging/irda/include/net/irda/irias_object.h b/drivers/staging/irda/include/net/irda/irias_object.h
deleted file mode 100644
index 83f78081799c..000000000000
--- a/drivers/staging/irda/include/net/irda/irias_object.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/*********************************************************************
- *
- * Filename: irias_object.h
- * Version:
- * Description:
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Thu Oct 1 22:49:50 1998
- * Modified at: Wed Dec 15 11:20:57 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
- *
- * 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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#ifndef LM_IAS_OBJECT_H
-#define LM_IAS_OBJECT_H
-
-#include <net/irda/irda.h>
-#include <net/irda/irqueue.h>
-
-/* LM-IAS Attribute types */
-#define IAS_MISSING 0
-#define IAS_INTEGER 1
-#define IAS_OCT_SEQ 2
-#define IAS_STRING 3
-
-/* Object ownership of attributes (user or kernel) */
-#define IAS_KERNEL_ATTR 0
-#define IAS_USER_ATTR 1
-
-/*
- * LM-IAS Object
- */
-struct ias_object {
- irda_queue_t q; /* Must be first! */
- magic_t magic;
-
- char *name;
- int id;
- hashbin_t *attribs;
-};
-
-/*
- * Values used by LM-IAS attributes
- */
-struct ias_value {
- __u8 type; /* Value description */
- __u8 owner; /* Managed from user/kernel space */
- int charset; /* Only used by string type */
- int len;
-
- /* Value */
- union {
- int integer;
- char *string;
- __u8 *oct_seq;
- } t;
-};
-
-/*
- * Attributes used by LM-IAS objects
- */
-struct ias_attrib {
- irda_queue_t q; /* Must be first! */
- int magic;
-
- char *name; /* Attribute name */
- struct ias_value *value; /* Attribute value */
-};
-
-struct ias_object *irias_new_object(char *name, int id);
-void irias_insert_object(struct ias_object *obj);
-int irias_delete_object(struct ias_object *obj);
-int irias_delete_attrib(struct ias_object *obj, struct ias_attrib *attrib,
- int cleanobject);
-void __irias_delete_object(struct ias_object *obj);
-
-void irias_add_integer_attrib(struct ias_object *obj, char *name, int value,
- int user);
-void irias_add_string_attrib(struct ias_object *obj, char *name, char *value,
- int user);
-void irias_add_octseq_attrib(struct ias_object *obj, char *name, __u8 *octets,
- int len, int user);
-int irias_object_change_attribute(char *obj_name, char *attrib_name,
- struct ias_value *new_value);
-struct ias_object *irias_find_object(char *name);
-struct ias_attrib *irias_find_attrib(struct ias_object *obj, char *name);
-
-struct ias_value *irias_new_string_value(char *string);
-struct ias_value *irias_new_integer_value(int integer);
-struct ias_value *irias_new_octseq_value(__u8 *octseq , int len);
-struct ias_value *irias_new_missing_value(void);
-void irias_delete_value(struct ias_value *value);
-
-extern struct ias_value irias_missing;
-extern hashbin_t *irias_objects;
-
-#endif
diff --git a/drivers/staging/irda/include/net/irda/irlan_client.h b/drivers/staging/irda/include/net/irda/irlan_client.h
deleted file mode 100644
index fa8455eda280..000000000000
--- a/drivers/staging/irda/include/net/irda/irlan_client.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*********************************************************************
- *
- * Filename: irlan_client.h
- * Version: 0.3
- * Description: IrDA LAN access layer
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sun Aug 31 20:14:37 1997
- * Modified at: Thu Apr 22 14:13:34 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
- *
- * 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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#ifndef IRLAN_CLIENT_H
-#define IRLAN_CLIENT_H
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-
-#include <net/irda/irias_object.h>
-#include <net/irda/irlan_event.h>
-
-void irlan_client_discovery_indication(discinfo_t *, DISCOVERY_MODE, void *);
-void irlan_client_wakeup(struct irlan_cb *self, __u32 saddr, __u32 daddr);
-
-void irlan_client_parse_response(struct irlan_cb *self, struct sk_buff *skb);
-void irlan_client_get_value_confirm(int result, __u16 obj_id,
- struct ias_value *value, void *priv);
-#endif
diff --git a/drivers/staging/irda/include/net/irda/irlan_common.h b/drivers/staging/irda/include/net/irda/irlan_common.h
deleted file mode 100644
index 550c2d6ec7ff..000000000000
--- a/drivers/staging/irda/include/net/irda/irlan_common.h
+++ /dev/null
@@ -1,230 +0,0 @@
-/*********************************************************************
- *
- * Filename: irlan_common.h
- * Version: 0.8
- * Description: IrDA LAN access layer
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sun Aug 31 20:14:37 1997
- * Modified at: Sun Oct 31 19:41:24 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
- * All Rights Reserved.
- *
- * 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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#ifndef IRLAN_H
-#define IRLAN_H
-
-#include <asm/param.h> /* for HZ */
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <linux/if_ether.h>
-
-#include <net/irda/irttp.h>
-
-#define IRLAN_MTU 1518
-#define IRLAN_TIMEOUT 10*HZ /* 10 seconds */
-
-/* Command packet types */
-#define CMD_GET_PROVIDER_INFO 0
-#define CMD_GET_MEDIA_CHAR 1
-#define CMD_OPEN_DATA_CHANNEL 2
-#define CMD_CLOSE_DATA_CHAN 3
-#define CMD_RECONNECT_DATA_CHAN 4
-#define CMD_FILTER_OPERATION 5
-
-/* Some responses */
-#define RSP_SUCCESS 0
-#define RSP_INSUFFICIENT_RESOURCES 1
-#define RSP_INVALID_COMMAND_FORMAT 2
-#define RSP_COMMAND_NOT_SUPPORTED 3
-#define RSP_PARAM_NOT_SUPPORTED 4
-#define RSP_VALUE_NOT_SUPPORTED 5
-#define RSP_NOT_OPEN 6
-#define RSP_AUTHENTICATION_REQUIRED 7
-#define RSP_INVALID_PASSWORD 8
-#define RSP_PROTOCOL_ERROR 9
-#define RSP_ASYNCHRONOUS_ERROR 255
-
-/* Media types */
-#define MEDIA_802_3 1
-#define MEDIA_802_5 2
-
-/* Filter parameters */
-#define DATA_CHAN 1
-#define FILTER_TYPE 2
-#define FILTER_MODE 3
-
-/* Filter types */
-#define IRLAN_DIRECTED 0x01
-#define IRLAN_FUNCTIONAL 0x02
-#define IRLAN_GROUP 0x04
-#define IRLAN_MAC_FRAME 0x08
-#define IRLAN_MULTICAST 0x10
-#define IRLAN_BROADCAST 0x20
-#define IRLAN_IPX_SOCKET 0x40
-
-/* Filter modes */
-#define ALL 1
-#define FILTER 2
-#define NONE 3
-
-/* Filter operations */
-#define GET 1
-#define CLEAR 2
-#define ADD 3
-#define REMOVE 4
-#define DYNAMIC 5
-
-/* Access types */
-#define ACCESS_DIRECT 1
-#define ACCESS_PEER 2
-#define ACCESS_HOSTED 3
-
-#define IRLAN_BYTE 0
-#define IRLAN_SHORT 1
-#define IRLAN_ARRAY 2
-
-/* IrLAN sits on top if IrTTP */
-#define IRLAN_MAX_HEADER (TTP_HEADER+LMP_HEADER)
-/* 1 byte for the command code and 1 byte for the parameter count */
-#define IRLAN_CMD_HEADER 2
-
-#define IRLAN_STRING_PARAMETER_LEN(name, value) (1 + strlen((name)) + 2 \
- + strlen ((value)))
-#define IRLAN_BYTE_PARAMETER_LEN(name) (1 + strlen((name)) + 2 + 1)
-#define IRLAN_SHORT_PARAMETER_LEN(name) (1 + strlen((name)) + 2 + 2)
-
-/*
- * IrLAN client
- */
-struct irlan_client_cb {
- int state;
-
- int open_retries;
-
- struct tsap_cb *tsap_ctrl;
- __u32 max_sdu_size;
- __u8 max_header_size;
-
- int access_type; /* Access type of provider */
- __u8 reconnect_key[255];
- __u8 key_len;
-
- __u16 recv_arb_val;
- __u16 max_frame;
- int filter_type;
-
- int unicast_open;
- int broadcast_open;
-
- int tx_busy;
- struct sk_buff_head txq; /* Transmit control queue */
-
- struct iriap_cb *iriap;
-
- struct timer_list kick_timer;
-};
-
-/*
- * IrLAN provider
- */
-struct irlan_provider_cb {
- int state;
-
- struct tsap_cb *tsap_ctrl;
- __u32 max_sdu_size;
- __u8 max_header_size;
-
- /*
- * Store some values here which are used by the provider to parse
- * the filter operations
- */
- int data_chan;
- int filter_type;
- int filter_mode;
- int filter_operation;
- int filter_entry;
- int access_type; /* Access type */
- __u16 send_arb_val;
-
- __u8 mac_address[ETH_ALEN]; /* Generated MAC address for peer device */
-};
-
-/*
- * IrLAN control block
- */
-struct irlan_cb {
- int magic;
- struct list_head dev_list;
- struct net_device *dev; /* Ethernet device structure*/
-
- __u32 saddr; /* Source device address */
- __u32 daddr; /* Destination device address */
- int disconnect_reason; /* Why we got disconnected */
-
- int media; /* Media type */
- __u8 version[2]; /* IrLAN version */
-
- struct tsap_cb *tsap_data; /* Data TSAP */
-
- int use_udata; /* Use Unit Data transfers */
-
- __u8 stsap_sel_data; /* Source data TSAP selector */
- __u8 dtsap_sel_data; /* Destination data TSAP selector */
- __u8 dtsap_sel_ctrl; /* Destination ctrl TSAP selector */
-
- struct irlan_client_cb client; /* Client specific fields */
- struct irlan_provider_cb provider; /* Provider specific fields */
-
- __u32 max_sdu_size;
- __u8 max_header_size;
-
- wait_queue_head_t open_wait;
- struct timer_list watchdog_timer;
-};
-
-void irlan_close(struct irlan_cb *self);
-void irlan_close_tsaps(struct irlan_cb *self);
-
-int irlan_register_netdev(struct irlan_cb *self);
-void irlan_ias_register(struct irlan_cb *self, __u8 tsap_sel);
-void irlan_start_watchdog_timer(struct irlan_cb *self, int timeout);
-
-void irlan_open_data_tsap(struct irlan_cb *self);
-
-int irlan_run_ctrl_tx_queue(struct irlan_cb *self);
-
-struct irlan_cb *irlan_get_any(void);
-void irlan_get_provider_info(struct irlan_cb *self);
-void irlan_get_media_char(struct irlan_cb *self);
-void irlan_open_data_channel(struct irlan_cb *self);
-void irlan_close_data_channel(struct irlan_cb *self);
-void irlan_set_multicast_filter(struct irlan_cb *self, int status);
-void irlan_set_broadcast_filter(struct irlan_cb *self, int status);
-
-int irlan_insert_byte_param(struct sk_buff *skb, char *param, __u8 value);
-int irlan_insert_short_param(struct sk_buff *skb, char *param, __u16 value);
-int irlan_insert_string_param(struct sk_buff *skb, char *param, char *value);
-int irlan_insert_array_param(struct sk_buff *skb, char *name, __u8 *value,
- __u16 value_len);
-
-int irlan_extract_param(__u8 *buf, char *name, char *value, __u16 *len);
-
-#endif
-
-
diff --git a/drivers/staging/irda/include/net/irda/irlan_eth.h b/drivers/staging/irda/include/net/irda/irlan_eth.h
deleted file mode 100644
index de5c81691f33..000000000000
--- a/drivers/staging/irda/include/net/irda/irlan_eth.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*********************************************************************
- *
- * Filename: irlan_eth.h
- * Version:
- * Description:
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Thu Oct 15 08:36:58 1998
- * Modified at: Fri May 14 23:29:00 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
- *
- * 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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#ifndef IRLAN_ETH_H
-#define IRLAN_ETH_H
-
-struct net_device *alloc_irlandev(const char *name);
-int irlan_eth_receive(void *instance, void *sap, struct sk_buff *skb);
-
-void irlan_eth_flow_indication( void *instance, void *sap, LOCAL_FLOW flow);
-#endif
diff --git a/drivers/staging/irda/include/net/irda/irlan_event.h b/drivers/staging/irda/include/net/irda/irlan_event.h
deleted file mode 100644
index 018b5a77e610..000000000000
--- a/drivers/staging/irda/include/net/irda/irlan_event.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*********************************************************************
- *
- * Filename: irlan_event.h
- * Version:
- * Description: LAN access
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sun Aug 31 20:14:37 1997
- * Modified at: Tue Feb 2 09:45:17 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1997 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
- *
- * 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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#ifndef IRLAN_EVENT_H
-#define IRLAN_EVENT_H
-
-#include <linux/kernel.h>
-#include <linux/skbuff.h>
-
-#include <net/irda/irlan_common.h>
-
-typedef enum {
- IRLAN_IDLE,
- IRLAN_QUERY,
- IRLAN_CONN,
- IRLAN_INFO,
- IRLAN_MEDIA,
- IRLAN_OPEN,
- IRLAN_WAIT,
- IRLAN_ARB,
- IRLAN_DATA,
- IRLAN_CLOSE,
- IRLAN_SYNC
-} IRLAN_STATE;
-
-typedef enum {
- IRLAN_DISCOVERY_INDICATION,
- IRLAN_IAS_PROVIDER_AVAIL,
- IRLAN_IAS_PROVIDER_NOT_AVAIL,
- IRLAN_LAP_DISCONNECT,
- IRLAN_LMP_DISCONNECT,
- IRLAN_CONNECT_COMPLETE,
- IRLAN_DATA_INDICATION,
- IRLAN_DATA_CONNECT_INDICATION,
- IRLAN_RETRY_CONNECT,
-
- IRLAN_CONNECT_INDICATION,
- IRLAN_GET_INFO_CMD,
- IRLAN_GET_MEDIA_CMD,
- IRLAN_OPEN_DATA_CMD,
- IRLAN_FILTER_CONFIG_CMD,
-
- IRLAN_CHECK_CON_ARB,
- IRLAN_PROVIDER_SIGNAL,
-
- IRLAN_WATCHDOG_TIMEOUT,
-} IRLAN_EVENT;
-
-extern const char * const irlan_state[];
-
-void irlan_do_client_event(struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb);
-
-void irlan_do_provider_event(struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb);
-
-void irlan_next_client_state(struct irlan_cb *self, IRLAN_STATE state);
-void irlan_next_provider_state(struct irlan_cb *self, IRLAN_STATE state);
-
-#endif
diff --git a/drivers/staging/irda/include/net/irda/irlan_filter.h b/drivers/staging/irda/include/net/irda/irlan_filter.h
deleted file mode 100644
index a5a2539485bd..000000000000
--- a/drivers/staging/irda/include/net/irda/irlan_filter.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*********************************************************************
- *
- * Filename: irlan_filter.h
- * Version:
- * Description:
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Fri Jan 29 15:24:08 1999
- * Modified at: Sun Feb 7 23:35:31 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998 Dag Brattli, All Rights Reserved.
- *
- * 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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#ifndef IRLAN_FILTER_H
-#define IRLAN_FILTER_H
-
-void irlan_check_command_param(struct irlan_cb *self, char *param,
- char *value);
-void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb);
-#ifdef CONFIG_PROC_FS
-void irlan_print_filter(struct seq_file *seq, int filter_type);
-#endif
-
-#endif /* IRLAN_FILTER_H */
diff --git a/drivers/staging/irda/include/net/irda/irlan_provider.h b/drivers/staging/irda/include/net/irda/irlan_provider.h
deleted file mode 100644
index 92f3b0e1029b..000000000000
--- a/drivers/staging/irda/include/net/irda/irlan_provider.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*********************************************************************
- *
- * Filename: irlan_provider.h
- * Version: 0.1
- * Description: IrDA LAN access layer
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sun Aug 31 20:14:37 1997
- * Modified at: Sun May 9 12:26:11 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
- *
- * 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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#ifndef IRLAN_SERVER_H
-#define IRLAN_SERVER_H
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-
-#include <net/irda/irlan_common.h>
-
-void irlan_provider_ctrl_disconnect_indication(void *instance, void *sap,
- LM_REASON reason,
- struct sk_buff *skb);
-
-
-void irlan_provider_connect_response(struct irlan_cb *, struct tsap_cb *);
-
-int irlan_parse_open_data_cmd(struct irlan_cb *self, struct sk_buff *skb);
-int irlan_provider_parse_command(struct irlan_cb *self, int cmd,
- struct sk_buff *skb);
-
-void irlan_provider_send_reply(struct irlan_cb *self, int command,
- int ret_code);
-int irlan_provider_open_ctrl_tsap(struct irlan_cb *self);
-
-#endif
-
-
diff --git a/drivers/staging/irda/include/net/irda/irlap.h b/drivers/staging/irda/include/net/irda/irlap.h
deleted file mode 100644
index 6f23e820618c..000000000000
--- a/drivers/staging/irda/include/net/irda/irlap.h
+++ /dev/null
@@ -1,311 +0,0 @@
-/*********************************************************************
- *
- * Filename: irlap.h
- * Version: 0.8
- * Description: An IrDA LAP driver for Linux
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Mon Aug 4 20:40:53 1997
- * Modified at: Fri Dec 10 13:21:17 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
- * All Rights Reserved.
- * Copyright (c) 2000-2002 Jean Tourrilhes <jt@hpl.hp.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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#ifndef IRLAP_H
-#define IRLAP_H
-
-#include <linux/types.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <linux/timer.h>
-
-#include <net/irda/irqueue.h> /* irda_queue_t */
-#include <net/irda/qos.h> /* struct qos_info */
-#include <net/irda/discovery.h> /* discovery_t */
-#include <net/irda/irlap_event.h> /* IRLAP_STATE, ... */
-#include <net/irda/irmod.h> /* struct notify_t */
-
-#define CONFIG_IRDA_DYNAMIC_WINDOW 1
-
-#define LAP_RELIABLE 1
-#define LAP_UNRELIABLE 0
-
-#define LAP_ADDR_HEADER 1 /* IrLAP Address Header */
-#define LAP_CTRL_HEADER 1 /* IrLAP Control Header */
-
-/* May be different when we get VFIR */
-#define LAP_MAX_HEADER (LAP_ADDR_HEADER + LAP_CTRL_HEADER)
-
-/* Each IrDA device gets a random 32 bits IRLAP device address */
-#define LAP_ALEN 4
-
-#define BROADCAST 0xffffffff /* Broadcast device address */
-#define CBROADCAST 0xfe /* Connection broadcast address */
-#define XID_FORMAT 0x01 /* Discovery XID format */
-
-/* Nobody seems to use this constant. */
-#define LAP_WINDOW_SIZE 8
-/* We keep the LAP queue very small to minimise the amount of buffering.
- * this improve latency and reduce resource consumption.
- * This work only because we have synchronous refilling of IrLAP through
- * the flow control mechanism (via scheduler and IrTTP).
- * 2 buffers is the minimum we can work with, one that we send while polling
- * IrTTP, and another to know that we should not send the pf bit.
- * Jean II */
-#define LAP_HIGH_THRESHOLD 2
-/* Some rare non TTP clients don't implement flow control, and
- * so don't comply with the above limit (and neither with this one).
- * For IAP and management, it doesn't matter, because they never transmit much.
- *.For IrLPT, this should be fixed.
- * - Jean II */
-#define LAP_MAX_QUEUE 10
-/* Please note that all IrDA management frames (LMP/TTP conn req/disc and
- * IAS queries) fall in the second category and are sent to LAP even if TTP
- * is stopped. This means that those frames will wait only a maximum of
- * two (2) data frames before beeing sent on the "wire", which speed up
- * new socket setup when the link is saturated.
- * Same story for two sockets competing for the medium : if one saturates
- * the LAP, when the other want to transmit it only has to wait for
- * maximum three (3) packets (2 + one scheduling), which improve performance
- * of delay sensitive applications.
- * Jean II */
-
-#define NR_EXPECTED 1
-#define NR_UNEXPECTED 0
-#define NR_INVALID -1
-
-#define NS_EXPECTED 1
-#define NS_UNEXPECTED 0
-#define NS_INVALID -1
-
-/*
- * Meta information passed within the IrLAP state machine
- */
-struct irlap_info {
- __u8 caddr; /* Connection address */
- __u8 control; /* Frame type */
- __u8 cmd;
-
- __u32 saddr;
- __u32 daddr;
-
- int pf; /* Poll/final bit set */
-
- __u8 nr; /* Sequence number of next frame expected */
- __u8 ns; /* Sequence number of frame sent */
-
- int S; /* Number of slots */
- int slot; /* Random chosen slot */
- int s; /* Current slot */
-
- discovery_t *discovery; /* Discovery information */
-};
-
-/* Main structure of IrLAP */
-struct irlap_cb {
- irda_queue_t q; /* Must be first */
- magic_t magic;
-
- /* Device we are attached to */
- struct net_device *netdev;
- char hw_name[2*IFNAMSIZ + 1];
-
- /* Connection state */
- volatile IRLAP_STATE state; /* Current state */
-
- /* Timers used by IrLAP */
- struct timer_list query_timer;
- struct timer_list slot_timer;
- struct timer_list discovery_timer;
- struct timer_list final_timer;
- struct timer_list poll_timer;
- struct timer_list wd_timer;
- struct timer_list backoff_timer;
-
- /* Media busy stuff */
- struct timer_list media_busy_timer;
- int media_busy;
-
- /* Timeouts which will be different with different turn time */
- int slot_timeout;
- int poll_timeout;
- int final_timeout;
- int wd_timeout;
-
- struct sk_buff_head txq; /* Frames to be transmitted */
- struct sk_buff_head txq_ultra;
-
- __u8 caddr; /* Connection address */
- __u32 saddr; /* Source device address */
- __u32 daddr; /* Destination device address */
-
- int retry_count; /* Times tried to establish connection */
- int add_wait; /* True if we are waiting for frame */
-
- __u8 connect_pending;
- __u8 disconnect_pending;
-
- /* To send a faster RR if tx queue empty */
-#ifdef CONFIG_IRDA_FAST_RR
- int fast_RR_timeout;
- int fast_RR;
-#endif /* CONFIG_IRDA_FAST_RR */
-
- int N1; /* N1 * F-timer = Negitiated link disconnect warning threshold */
- int N2; /* N2 * F-timer = Negitiated link disconnect time */
- int N3; /* Connection retry count */
-
- int local_busy;
- int remote_busy;
- int xmitflag;
-
- __u8 vs; /* Next frame to be sent */
- __u8 vr; /* Next frame to be received */
- __u8 va; /* Last frame acked */
- int window; /* Nr of I-frames allowed to send */
- int window_size; /* Current negotiated window size */
-
-#ifdef CONFIG_IRDA_DYNAMIC_WINDOW
- __u32 line_capacity; /* Number of bytes allowed to send */
- __u32 bytes_left; /* Number of bytes still allowed to transmit */
-#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */
-
- struct sk_buff_head wx_list;
-
- __u8 ack_required;
-
- /* XID parameters */
- __u8 S; /* Number of slots */
- __u8 slot; /* Random chosen slot */
- __u8 s; /* Current slot */
- int frame_sent; /* Have we sent reply? */
-
- hashbin_t *discovery_log;
- discovery_t *discovery_cmd;
-
- __u32 speed; /* Link speed */
-
- struct qos_info qos_tx; /* QoS requested by peer */
- struct qos_info qos_rx; /* QoS requested by self */
- struct qos_info *qos_dev; /* QoS supported by device */
-
- notify_t notify; /* Callbacks to IrLMP */
-
- int mtt_required; /* Minimum turnaround time required */
- int xbofs_delay; /* Nr of XBOF's used to MTT */
- int bofs_count; /* Negotiated extra BOFs */
- int next_bofs; /* Negotiated extra BOFs after next frame */
-
- int mode; /* IrLAP mode (primary, secondary or monitor) */
-};
-
-/*
- * Function prototypes
- */
-int irlap_init(void);
-void irlap_cleanup(void);
-
-struct irlap_cb *irlap_open(struct net_device *dev, struct qos_info *qos,
- const char *hw_name);
-void irlap_close(struct irlap_cb *self);
-
-void irlap_connect_request(struct irlap_cb *self, __u32 daddr,
- struct qos_info *qos, int sniff);
-void irlap_connect_response(struct irlap_cb *self, struct sk_buff *skb);
-void irlap_connect_indication(struct irlap_cb *self, struct sk_buff *skb);
-void irlap_connect_confirm(struct irlap_cb *, struct sk_buff *skb);
-
-void irlap_data_indication(struct irlap_cb *, struct sk_buff *, int unreliable);
-void irlap_data_request(struct irlap_cb *, struct sk_buff *, int unreliable);
-
-#ifdef CONFIG_IRDA_ULTRA
-void irlap_unitdata_request(struct irlap_cb *, struct sk_buff *);
-void irlap_unitdata_indication(struct irlap_cb *, struct sk_buff *);
-#endif /* CONFIG_IRDA_ULTRA */
-
-void irlap_disconnect_request(struct irlap_cb *);
-void irlap_disconnect_indication(struct irlap_cb *, LAP_REASON reason);
-
-void irlap_status_indication(struct irlap_cb *, int quality_of_link);
-
-void irlap_test_request(__u8 *info, int len);
-
-void irlap_discovery_request(struct irlap_cb *, discovery_t *discovery);
-void irlap_discovery_confirm(struct irlap_cb *, hashbin_t *discovery_log);
-void irlap_discovery_indication(struct irlap_cb *, discovery_t *discovery);
-
-void irlap_reset_indication(struct irlap_cb *self);
-void irlap_reset_confirm(void);
-
-void irlap_update_nr_received(struct irlap_cb *, int nr);
-int irlap_validate_nr_received(struct irlap_cb *, int nr);
-int irlap_validate_ns_received(struct irlap_cb *, int ns);
-
-int irlap_generate_rand_time_slot(int S, int s);
-void irlap_initiate_connection_state(struct irlap_cb *);
-void irlap_flush_all_queues(struct irlap_cb *);
-void irlap_wait_min_turn_around(struct irlap_cb *, struct qos_info *);
-
-void irlap_apply_default_connection_parameters(struct irlap_cb *self);
-void irlap_apply_connection_parameters(struct irlap_cb *self, int now);
-
-#define IRLAP_GET_HEADER_SIZE(self) (LAP_MAX_HEADER)
-#define IRLAP_GET_TX_QUEUE_LEN(self) skb_queue_len(&self->txq)
-
-/* Return TRUE if the node is in primary mode (i.e. master)
- * - Jean II */
-static inline int irlap_is_primary(struct irlap_cb *self)
-{
- int ret;
- switch(self->state) {
- case LAP_XMIT_P:
- case LAP_NRM_P:
- ret = 1;
- break;
- case LAP_XMIT_S:
- case LAP_NRM_S:
- ret = 0;
- break;
- default:
- ret = -1;
- }
- return ret;
-}
-
-/* Clear a pending IrLAP disconnect. - Jean II */
-static inline void irlap_clear_disconnect(struct irlap_cb *self)
-{
- self->disconnect_pending = FALSE;
-}
-
-/*
- * Function irlap_next_state (self, state)
- *
- * Switches state and provides debug information
- *
- */
-static inline void irlap_next_state(struct irlap_cb *self, IRLAP_STATE state)
-{
- /*
- if (!self || self->magic != LAP_MAGIC)
- return;
-
- pr_debug("next LAP state = %s\n", irlap_state[state]);
- */
- self->state = state;
-}
-
-#endif
diff --git a/drivers/staging/irda/include/net/irda/irlap_event.h b/drivers/staging/irda/include/net/irda/irlap_event.h
deleted file mode 100644
index e4325fee1267..000000000000
--- a/drivers/staging/irda/include/net/irda/irlap_event.h
+++ /dev/null
@@ -1,129 +0,0 @@
-/*********************************************************************
- *
- *
- * Filename: irlap_event.h
- * Version: 0.1
- * Description:
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sat Aug 16 00:59:29 1997
- * Modified at: Tue Dec 21 11:20:30 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
- * All Rights Reserved.
- * Copyright (c) 2000-2002 Jean Tourrilhes <jt@hpl.hp.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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#ifndef IRLAP_EVENT_H
-#define IRLAP_EVENT_H
-
-#include <net/irda/irda.h>
-
-/* A few forward declarations (to make compiler happy) */
-struct irlap_cb;
-struct irlap_info;
-
-/* IrLAP States */
-typedef enum {
- LAP_NDM, /* Normal disconnected mode */
- LAP_QUERY,
- LAP_REPLY,
- LAP_CONN, /* Connect indication */
- LAP_SETUP, /* Setting up connection */
- LAP_OFFLINE, /* A really boring state */
- LAP_XMIT_P,
- LAP_PCLOSE,
- LAP_NRM_P, /* Normal response mode as primary */
- LAP_RESET_WAIT,
- LAP_RESET,
- LAP_NRM_S, /* Normal response mode as secondary */
- LAP_XMIT_S,
- LAP_SCLOSE,
- LAP_RESET_CHECK,
-} IRLAP_STATE;
-
-/* IrLAP Events */
-typedef enum {
- /* Services events */
- DISCOVERY_REQUEST,
- CONNECT_REQUEST,
- CONNECT_RESPONSE,
- DISCONNECT_REQUEST,
- DATA_REQUEST,
- RESET_REQUEST,
- RESET_RESPONSE,
-
- /* Send events */
- SEND_I_CMD,
- SEND_UI_FRAME,
-
- /* Receive events */
- RECV_DISCOVERY_XID_CMD,
- RECV_DISCOVERY_XID_RSP,
- RECV_SNRM_CMD,
- RECV_TEST_CMD,
- RECV_TEST_RSP,
- RECV_UA_RSP,
- RECV_DM_RSP,
- RECV_RD_RSP,
- RECV_I_CMD,
- RECV_I_RSP,
- RECV_UI_FRAME,
- RECV_FRMR_RSP,
- RECV_RR_CMD,
- RECV_RR_RSP,
- RECV_RNR_CMD,
- RECV_RNR_RSP,
- RECV_REJ_CMD,
- RECV_REJ_RSP,
- RECV_SREJ_CMD,
- RECV_SREJ_RSP,
- RECV_DISC_CMD,
-
- /* Timer events */
- SLOT_TIMER_EXPIRED,
- QUERY_TIMER_EXPIRED,
- FINAL_TIMER_EXPIRED,
- POLL_TIMER_EXPIRED,
- DISCOVERY_TIMER_EXPIRED,
- WD_TIMER_EXPIRED,
- BACKOFF_TIMER_EXPIRED,
- MEDIA_BUSY_TIMER_EXPIRED,
-} IRLAP_EVENT;
-
-/*
- * Disconnect reason code
- */
-typedef enum { /* FIXME check the two first reason codes */
- LAP_DISC_INDICATION=1, /* Received a disconnect request from peer */
- LAP_NO_RESPONSE, /* To many retransmits without response */
- LAP_RESET_INDICATION, /* To many retransmits, or invalid nr/ns */
- LAP_FOUND_NONE, /* No devices were discovered */
- LAP_MEDIA_BUSY,
- LAP_PRIMARY_CONFLICT,
-} LAP_REASON;
-
-extern const char *const irlap_state[];
-
-void irlap_do_event(struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info);
-void irlap_print_event(IRLAP_EVENT event);
-
-int irlap_qos_negotiate(struct irlap_cb *self, struct sk_buff *skb);
-
-#endif
diff --git a/drivers/staging/irda/include/net/irda/irlap_frame.h b/drivers/staging/irda/include/net/irda/irlap_frame.h
deleted file mode 100644
index cbc12a926e5f..000000000000
--- a/drivers/staging/irda/include/net/irda/irlap_frame.h
+++ /dev/null
@@ -1,167 +0,0 @@
-/*********************************************************************
- *
- * Filename: irlap_frame.h
- * Version: 0.9
- * Description: IrLAP frame declarations
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Tue Aug 19 10:27:26 1997
- * Modified at: Sat Dec 25 21:07:26 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1997-1999 Dag Brattli <dagb@cs.uit.no>,
- * All Rights Reserved.
- * Copyright (c) 2000-2002 Jean Tourrilhes <jt@hpl.hp.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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#ifndef IRLAP_FRAME_H
-#define IRLAP_FRAME_H
-
-#include <linux/skbuff.h>
-
-#include <net/irda/irda.h>
-
-/* A few forward declarations (to make compiler happy) */
-struct irlap_cb;
-struct discovery_t;
-
-/* Frame types and templates */
-#define INVALID 0xff
-
-/* Unnumbered (U) commands */
-#define SNRM_CMD 0x83 /* Set Normal Response Mode */
-#define DISC_CMD 0x43 /* Disconnect */
-#define XID_CMD 0x2f /* Exchange Station Identification */
-#define TEST_CMD 0xe3 /* Test */
-
-/* Unnumbered responses */
-#define RNRM_RSP 0x83 /* Request Normal Response Mode */
-#define UA_RSP 0x63 /* Unnumbered Acknowledgement */
-#define FRMR_RSP 0x87 /* Frame Reject */
-#define DM_RSP 0x0f /* Disconnect Mode */
-#define RD_RSP 0x43 /* Request Disconnection */
-#define XID_RSP 0xaf /* Exchange Station Identification */
-#define TEST_RSP 0xe3 /* Test frame */
-
-/* Supervisory (S) */
-#define RR 0x01 /* Receive Ready */
-#define REJ 0x09 /* Reject */
-#define RNR 0x05 /* Receive Not Ready */
-#define SREJ 0x0d /* Selective Reject */
-
-/* Information (I) */
-#define I_FRAME 0x00 /* Information Format */
-#define UI_FRAME 0x03 /* Unnumbered Information */
-
-#define CMD_FRAME 0x01
-#define RSP_FRAME 0x00
-
-#define PF_BIT 0x10 /* Poll/final bit */
-
-/* Some IrLAP field lengths */
-/*
- * Only baud rate triplet is 4 bytes (PV can be 2 bytes).
- * All others params (7) are 3 bytes, so that's 7*3 + 1*4 bytes.
- */
-#define IRLAP_NEGOCIATION_PARAMS_LEN 25
-#define IRLAP_DISCOVERY_INFO_LEN 32
-
-struct disc_frame {
- __u8 caddr; /* Connection address */
- __u8 control;
-} __packed;
-
-struct xid_frame {
- __u8 caddr; /* Connection address */
- __u8 control;
- __u8 ident; /* Should always be XID_FORMAT */
- __le32 saddr; /* Source device address */
- __le32 daddr; /* Destination device address */
- __u8 flags; /* Discovery flags */
- __u8 slotnr;
- __u8 version;
-} __packed;
-
-struct test_frame {
- __u8 caddr; /* Connection address */
- __u8 control;
- __le32 saddr; /* Source device address */
- __le32 daddr; /* Destination device address */
-} __packed;
-
-struct ua_frame {
- __u8 caddr;
- __u8 control;
- __le32 saddr; /* Source device address */
- __le32 daddr; /* Dest device address */
-} __packed;
-
-struct dm_frame {
- __u8 caddr; /* Connection address */
- __u8 control;
-} __packed;
-
-struct rd_frame {
- __u8 caddr; /* Connection address */
- __u8 control;
-} __packed;
-
-struct rr_frame {
- __u8 caddr; /* Connection address */
- __u8 control;
-} __packed;
-
-struct i_frame {
- __u8 caddr;
- __u8 control;
-} __packed;
-
-struct snrm_frame {
- __u8 caddr;
- __u8 control;
- __le32 saddr;
- __le32 daddr;
- __u8 ncaddr;
-} __packed;
-
-void irlap_queue_xmit(struct irlap_cb *self, struct sk_buff *skb);
-void irlap_send_discovery_xid_frame(struct irlap_cb *, int S, __u8 s,
- __u8 command,
- struct discovery_t *discovery);
-void irlap_send_snrm_frame(struct irlap_cb *, struct qos_info *);
-void irlap_send_test_frame(struct irlap_cb *self, __u8 caddr, __u32 daddr,
- struct sk_buff *cmd);
-void irlap_send_ua_response_frame(struct irlap_cb *, struct qos_info *);
-void irlap_send_dm_frame(struct irlap_cb *self);
-void irlap_send_rd_frame(struct irlap_cb *self);
-void irlap_send_disc_frame(struct irlap_cb *self);
-void irlap_send_rr_frame(struct irlap_cb *self, int command);
-
-void irlap_send_data_primary(struct irlap_cb *, struct sk_buff *);
-void irlap_send_data_primary_poll(struct irlap_cb *, struct sk_buff *);
-void irlap_send_data_secondary(struct irlap_cb *, struct sk_buff *);
-void irlap_send_data_secondary_final(struct irlap_cb *, struct sk_buff *);
-void irlap_resend_rejected_frames(struct irlap_cb *, int command);
-void irlap_resend_rejected_frame(struct irlap_cb *self, int command);
-
-void irlap_send_ui_frame(struct irlap_cb *self, struct sk_buff *skb,
- __u8 caddr, int command);
-
-int irlap_insert_qos_negotiation_params(struct irlap_cb *self,
- struct sk_buff *skb);
-
-#endif
diff --git a/drivers/staging/irda/include/net/irda/irlmp.h b/drivers/staging/irda/include/net/irda/irlmp.h
deleted file mode 100644
index f132924cc9da..000000000000
--- a/drivers/staging/irda/include/net/irda/irlmp.h
+++ /dev/null
@@ -1,295 +0,0 @@
-/*********************************************************************
- *
- * Filename: irlmp.h
- * Version: 0.9
- * Description: IrDA Link Management Protocol (LMP) layer
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sun Aug 17 20:54:32 1997
- * Modified at: Fri Dec 10 13:23:01 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
- * All Rights Reserved.
- * Copyright (c) 2000-2002 Jean Tourrilhes <jt@hpl.hp.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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#ifndef IRLMP_H
-#define IRLMP_H
-
-#include <asm/param.h> /* for HZ */
-
-#include <linux/types.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/qos.h>
-#include <net/irda/irlap.h> /* LAP_MAX_HEADER, ... */
-#include <net/irda/irlmp_event.h>
-#include <net/irda/irqueue.h>
-#include <net/irda/discovery.h>
-
-/* LSAP-SEL's */
-#define LSAP_MASK 0x7f
-#define LSAP_IAS 0x00
-#define LSAP_ANY 0xff
-#define LSAP_MAX 0x6f /* 0x70-0x7f are reserved */
-#define LSAP_CONNLESS 0x70 /* Connectionless LSAP, mostly used for Ultra */
-
-#define DEV_ADDR_ANY 0xffffffff
-
-#define LMP_HEADER 2 /* Dest LSAP + Source LSAP */
-#define LMP_CONTROL_HEADER 4 /* LMP_HEADER + opcode + parameter */
-#define LMP_PID_HEADER 1 /* Used by Ultra */
-#define LMP_MAX_HEADER (LMP_CONTROL_HEADER+LAP_MAX_HEADER)
-
-#define LM_MAX_CONNECTIONS 10
-
-#define LM_IDLE_TIMEOUT 2*HZ /* 2 seconds for now */
-
-typedef enum {
- S_PNP = 0,
- S_PDA,
- S_COMPUTER,
- S_PRINTER,
- S_MODEM,
- S_FAX,
- S_LAN,
- S_TELEPHONY,
- S_COMM,
- S_OBEX,
- S_ANY,
- S_END,
-} SERVICE;
-
-/* For selective discovery */
-typedef void (*DISCOVERY_CALLBACK1) (discinfo_t *, DISCOVERY_MODE, void *);
-/* For expiry (the same) */
-typedef void (*DISCOVERY_CALLBACK2) (discinfo_t *, DISCOVERY_MODE, void *);
-
-typedef struct {
- irda_queue_t queue; /* Must be first */
-
- __u16_host_order hints; /* Hint bits */
-} irlmp_service_t;
-
-typedef struct {
- irda_queue_t queue; /* Must be first */
-
- __u16_host_order hint_mask;
-
- DISCOVERY_CALLBACK1 disco_callback; /* Selective discovery */
- DISCOVERY_CALLBACK2 expir_callback; /* Selective expiration */
- void *priv; /* Used to identify client */
-} irlmp_client_t;
-
-/*
- * Information about each logical LSAP connection
- */
-struct lsap_cb {
- irda_queue_t queue; /* Must be first */
- magic_t magic;
-
- unsigned long connected; /* set_bit used on this */
- int persistent;
-
- __u8 slsap_sel; /* Source (this) LSAP address */
- __u8 dlsap_sel; /* Destination LSAP address (if connected) */
-#ifdef CONFIG_IRDA_ULTRA
- __u8 pid; /* Used by connectionless LSAP */
-#endif /* CONFIG_IRDA_ULTRA */
- struct sk_buff *conn_skb; /* Store skb here while connecting */
-
- struct timer_list watchdog_timer;
-
- LSAP_STATE lsap_state; /* Connection state */
- notify_t notify; /* Indication/Confirm entry points */
- struct qos_info qos; /* QoS for this connection */
-
- struct lap_cb *lap; /* Pointer to LAP connection structure */
-};
-
-/*
- * Used for caching the last slsap->dlsap->handle mapping
- *
- * We don't need to keep/match the remote address in the cache because
- * we are associated with a specific LAP (which implies it).
- * Jean II
- */
-typedef struct {
- int valid;
-
- __u8 slsap_sel;
- __u8 dlsap_sel;
- struct lsap_cb *lsap;
-} CACHE_ENTRY;
-
-/*
- * Information about each registered IrLAP layer
- */
-struct lap_cb {
- irda_queue_t queue; /* Must be first */
- magic_t magic;
-
- int reason; /* LAP disconnect reason */
-
- IRLMP_STATE lap_state;
-
- struct irlap_cb *irlap; /* Instance of IrLAP layer */
- hashbin_t *lsaps; /* LSAP associated with this link */
- struct lsap_cb *flow_next; /* Next lsap to be polled for Tx */
-
- __u8 caddr; /* Connection address */
- __u32 saddr; /* Source device address */
- __u32 daddr; /* Destination device address */
-
- struct qos_info *qos; /* LAP QoS for this session */
- struct timer_list idle_timer;
-
-#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
- /* The lsap cache was moved from struct irlmp_cb to here because
- * it must be associated with the specific LAP. Also, this
- * improves performance. - Jean II */
- CACHE_ENTRY cache; /* Caching last slsap->dlsap->handle mapping */
-#endif
-};
-
-/*
- * Main structure for IrLMP
- */
-struct irlmp_cb {
- magic_t magic;
-
- __u8 conflict_flag;
-
- discovery_t discovery_cmd; /* Discovery command to use by IrLAP */
- discovery_t discovery_rsp; /* Discovery response to use by IrLAP */
-
- /* Last lsap picked automatically by irlmp_find_free_slsap() */
- int last_lsap_sel;
-
- struct timer_list discovery_timer;
-
- hashbin_t *links; /* IrLAP connection table */
- hashbin_t *unconnected_lsaps;
- hashbin_t *clients;
- hashbin_t *services;
-
- hashbin_t *cachelog; /* Current discovery log */
-
- int running;
-
- __u16_host_order hints; /* Hint bits */
-};
-
-/* Prototype declarations */
-int irlmp_init(void);
-void irlmp_cleanup(void);
-struct lsap_cb *irlmp_open_lsap(__u8 slsap, notify_t *notify, __u8 pid);
-void irlmp_close_lsap( struct lsap_cb *self);
-
-__u16 irlmp_service_to_hint(int service);
-void *irlmp_register_service(__u16 hints);
-int irlmp_unregister_service(void *handle);
-void *irlmp_register_client(__u16 hint_mask, DISCOVERY_CALLBACK1 disco_clb,
- DISCOVERY_CALLBACK2 expir_clb, void *priv);
-int irlmp_unregister_client(void *handle);
-int irlmp_update_client(void *handle, __u16 hint_mask,
- DISCOVERY_CALLBACK1 disco_clb,
- DISCOVERY_CALLBACK2 expir_clb, void *priv);
-
-void irlmp_register_link(struct irlap_cb *, __u32 saddr, notify_t *);
-void irlmp_unregister_link(__u32 saddr);
-
-int irlmp_connect_request(struct lsap_cb *, __u8 dlsap_sel,
- __u32 saddr, __u32 daddr,
- struct qos_info *, struct sk_buff *);
-void irlmp_connect_indication(struct lsap_cb *self, struct sk_buff *skb);
-int irlmp_connect_response(struct lsap_cb *, struct sk_buff *);
-void irlmp_connect_confirm(struct lsap_cb *, struct sk_buff *);
-struct lsap_cb *irlmp_dup(struct lsap_cb *self, void *instance);
-
-void irlmp_disconnect_indication(struct lsap_cb *self, LM_REASON reason,
- struct sk_buff *userdata);
-int irlmp_disconnect_request(struct lsap_cb *, struct sk_buff *userdata);
-
-void irlmp_discovery_confirm(hashbin_t *discovery_log, DISCOVERY_MODE mode);
-void irlmp_discovery_request(int nslots);
-discinfo_t *irlmp_get_discoveries(int *pn, __u16 mask, int nslots);
-void irlmp_do_expiry(void);
-void irlmp_do_discovery(int nslots);
-discovery_t *irlmp_get_discovery_response(void);
-void irlmp_discovery_expiry(discinfo_t *expiry, int number);
-
-int irlmp_data_request(struct lsap_cb *, struct sk_buff *);
-void irlmp_data_indication(struct lsap_cb *, struct sk_buff *);
-
-int irlmp_udata_request(struct lsap_cb *, struct sk_buff *);
-void irlmp_udata_indication(struct lsap_cb *, struct sk_buff *);
-
-#ifdef CONFIG_IRDA_ULTRA
-int irlmp_connless_data_request(struct lsap_cb *, struct sk_buff *, __u8);
-void irlmp_connless_data_indication(struct lsap_cb *, struct sk_buff *);
-#endif /* CONFIG_IRDA_ULTRA */
-
-void irlmp_status_indication(struct lap_cb *, LINK_STATUS link, LOCK_STATUS lock);
-void irlmp_flow_indication(struct lap_cb *self, LOCAL_FLOW flow);
-
-LM_REASON irlmp_convert_lap_reason(LAP_REASON);
-
-static inline __u32 irlmp_get_saddr(const struct lsap_cb *self)
-{
- return (self && self->lap) ? self->lap->saddr : 0;
-}
-
-static inline __u32 irlmp_get_daddr(const struct lsap_cb *self)
-{
- return (self && self->lap) ? self->lap->daddr : 0;
-}
-
-const char *irlmp_reason_str(LM_REASON reason);
-
-extern int sysctl_discovery_timeout;
-extern int sysctl_discovery_slots;
-extern int sysctl_discovery;
-extern int sysctl_lap_keepalive_time; /* in ms, default is LM_IDLE_TIMEOUT */
-extern struct irlmp_cb *irlmp;
-
-/* Check if LAP queue is full.
- * Used by IrTTP for low control, see comments in irlap.h - Jean II */
-static inline int irlmp_lap_tx_queue_full(struct lsap_cb *self)
-{
- if (self == NULL)
- return 0;
- if (self->lap == NULL)
- return 0;
- if (self->lap->irlap == NULL)
- return 0;
-
- return IRLAP_GET_TX_QUEUE_LEN(self->lap->irlap) >= LAP_HIGH_THRESHOLD;
-}
-
-/* After doing a irlmp_dup(), this get one of the two socket back into
- * a state where it's waiting incoming connections.
- * Note : this can be used *only* if the socket is not yet connected
- * (i.e. NO irlmp_connect_response() done on this socket).
- * - Jean II */
-static inline void irlmp_listen(struct lsap_cb *self)
-{
- self->dlsap_sel = LSAP_ANY;
- self->lap = NULL;
- self->lsap_state = LSAP_DISCONNECTED;
- /* Started when we received the LM_CONNECT_INDICATION */
- del_timer(&self->watchdog_timer);
-}
-
-#endif
diff --git a/drivers/staging/irda/include/net/irda/irlmp_event.h b/drivers/staging/irda/include/net/irda/irlmp_event.h
deleted file mode 100644
index a1a082fe384e..000000000000
--- a/drivers/staging/irda/include/net/irda/irlmp_event.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/*********************************************************************
- *
- * Filename: irlmp_event.h
- * Version: 0.1
- * Description: IrDA-LMP event handling
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Mon Aug 4 20:40:53 1997
- * Modified at: Thu Jul 8 12:18:54 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1997, 1999 Dag Brattli <dagb@cs.uit.no>,
- * All Rights Reserved.
- * Copyright (c) 2000-2002 Jean Tourrilhes <jt@hpl.hp.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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#ifndef IRLMP_EVENT_H
-#define IRLMP_EVENT_H
-
-/* A few forward declarations (to make compiler happy) */
-struct irlmp_cb;
-struct lsap_cb;
-struct lap_cb;
-struct discovery_t;
-
-/* LAP states */
-typedef enum {
- /* IrLAP connection control states */
- LAP_STANDBY, /* No LAP connection */
- LAP_U_CONNECT, /* Starting LAP connection */
- LAP_ACTIVE, /* LAP connection is active */
-} IRLMP_STATE;
-
-/* LSAP connection control states */
-typedef enum {
- LSAP_DISCONNECTED, /* No LSAP connection */
- LSAP_CONNECT, /* Connect indication from peer */
- LSAP_CONNECT_PEND, /* Connect request from service user */
- LSAP_DATA_TRANSFER_READY, /* LSAP connection established */
- LSAP_SETUP, /* Trying to set up LSAP connection */
- LSAP_SETUP_PEND, /* Request to start LAP connection */
-} LSAP_STATE;
-
-typedef enum {
- /* LSAP events */
- LM_CONNECT_REQUEST,
- LM_CONNECT_CONFIRM,
- LM_CONNECT_RESPONSE,
- LM_CONNECT_INDICATION,
-
- LM_DISCONNECT_INDICATION,
- LM_DISCONNECT_REQUEST,
-
- LM_DATA_REQUEST,
- LM_UDATA_REQUEST,
- LM_DATA_INDICATION,
- LM_UDATA_INDICATION,
-
- LM_WATCHDOG_TIMEOUT,
-
- /* IrLAP events */
- LM_LAP_CONNECT_REQUEST,
- LM_LAP_CONNECT_INDICATION,
- LM_LAP_CONNECT_CONFIRM,
- LM_LAP_DISCONNECT_INDICATION,
- LM_LAP_DISCONNECT_REQUEST,
- LM_LAP_DISCOVERY_REQUEST,
- LM_LAP_DISCOVERY_CONFIRM,
- LM_LAP_IDLE_TIMEOUT,
-} IRLMP_EVENT;
-
-extern const char *const irlmp_state[];
-extern const char *const irlsap_state[];
-
-void irlmp_watchdog_timer_expired(struct timer_list *t);
-void irlmp_discovery_timer_expired(struct timer_list *t);
-void irlmp_idle_timer_expired(struct timer_list *t);
-
-void irlmp_do_lap_event(struct lap_cb *self, IRLMP_EVENT event,
- struct sk_buff *skb);
-int irlmp_do_lsap_event(struct lsap_cb *self, IRLMP_EVENT event,
- struct sk_buff *skb);
-
-#endif /* IRLMP_EVENT_H */
-
-
-
-
diff --git a/drivers/staging/irda/include/net/irda/irlmp_frame.h b/drivers/staging/irda/include/net/irda/irlmp_frame.h
deleted file mode 100644
index 1906eb71422e..000000000000
--- a/drivers/staging/irda/include/net/irda/irlmp_frame.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*********************************************************************
- *
- * Filename: irlmp_frame.h
- * Version: 0.9
- * Description:
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Tue Aug 19 02:09:59 1997
- * Modified at: Fri Dec 10 13:21:53 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1997, 1999 Dag Brattli <dagb@cs.uit.no>,
- * All Rights Reserved.
- *
- * 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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#ifndef IRMLP_FRAME_H
-#define IRMLP_FRAME_H
-
-#include <linux/skbuff.h>
-
-#include <net/irda/discovery.h>
-
-/* IrLMP frame opcodes */
-#define CONNECT_CMD 0x01
-#define CONNECT_CNF 0x81
-#define DISCONNECT 0x02
-#define ACCESSMODE_CMD 0x03
-#define ACCESSMODE_CNF 0x83
-
-#define CONTROL_BIT 0x80
-
-void irlmp_send_data_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap,
- int expedited, struct sk_buff *skb);
-void irlmp_send_lcf_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap,
- __u8 opcode, struct sk_buff *skb);
-void irlmp_link_data_indication(struct lap_cb *, struct sk_buff *,
- int unreliable);
-#ifdef CONFIG_IRDA_ULTRA
-void irlmp_link_unitdata_indication(struct lap_cb *, struct sk_buff *);
-#endif /* CONFIG_IRDA_ULTRA */
-
-void irlmp_link_connect_indication(struct lap_cb *, __u32 saddr, __u32 daddr,
- struct qos_info *qos, struct sk_buff *skb);
-void irlmp_link_connect_request(__u32 daddr);
-void irlmp_link_connect_confirm(struct lap_cb *self, struct qos_info *qos,
- struct sk_buff *skb);
-void irlmp_link_disconnect_indication(struct lap_cb *, struct irlap_cb *,
- LAP_REASON reason, struct sk_buff *);
-void irlmp_link_discovery_confirm(struct lap_cb *self, hashbin_t *log);
-void irlmp_link_discovery_indication(struct lap_cb *, discovery_t *discovery);
-
-#endif
diff --git a/drivers/staging/irda/include/net/irda/irmod.h b/drivers/staging/irda/include/net/irda/irmod.h
deleted file mode 100644
index 86f0dbb8ee5d..000000000000
--- a/drivers/staging/irda/include/net/irda/irmod.h
+++ /dev/null
@@ -1,109 +0,0 @@
-/*********************************************************************
- *
- * Filename: irmod.h
- * Version: 0.3
- * Description: IrDA module and utilities functions
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Mon Dec 15 13:58:52 1997
- * Modified at: Fri Jan 28 13:15:24 2000
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-2000 Dag Brattli, All Rights Reserved.
- * Copyright (c) 2000-2002 Jean Tourrilhes <jt@hpl.hp.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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charg.
- *
- ********************************************************************/
-
-#ifndef IRMOD_H
-#define IRMOD_H
-
-/* Misc status information */
-typedef enum {
- STATUS_OK,
- STATUS_ABORTED,
- STATUS_NO_ACTIVITY,
- STATUS_NOISY,
- STATUS_REMOTE,
-} LINK_STATUS;
-
-typedef enum {
- LOCK_NO_CHANGE,
- LOCK_LOCKED,
- LOCK_UNLOCKED,
-} LOCK_STATUS;
-
-typedef enum { FLOW_STOP, FLOW_START } LOCAL_FLOW;
-
-/*
- * IrLMP disconnect reasons. The order is very important, since they
- * correspond to disconnect reasons sent in IrLMP disconnect frames, so
- * please do not touch :-)
- */
-typedef enum {
- LM_USER_REQUEST = 1, /* User request */
- LM_LAP_DISCONNECT, /* Unexpected IrLAP disconnect */
- LM_CONNECT_FAILURE, /* Failed to establish IrLAP connection */
- LM_LAP_RESET, /* IrLAP reset */
- LM_INIT_DISCONNECT, /* Link Management initiated disconnect */
- LM_LSAP_NOTCONN, /* Data delivered on unconnected LSAP */
- LM_NON_RESP_CLIENT, /* Non responsive LM-MUX client */
- LM_NO_AVAIL_CLIENT, /* No available LM-MUX client */
- LM_CONN_HALF_OPEN, /* Connection is half open */
- LM_BAD_SOURCE_ADDR, /* Illegal source address (i.e 0x00) */
-} LM_REASON;
-#define LM_UNKNOWN 0xff /* Unspecified disconnect reason */
-
-/* A few forward declarations (to make compiler happy) */
-struct qos_info; /* in <net/irda/qos.h> */
-
-/*
- * Notify structure used between transport and link management layers
- */
-typedef struct {
- int (*data_indication)(void *priv, void *sap, struct sk_buff *skb);
- int (*udata_indication)(void *priv, void *sap, struct sk_buff *skb);
- void (*connect_confirm)(void *instance, void *sap,
- struct qos_info *qos, __u32 max_sdu_size,
- __u8 max_header_size, struct sk_buff *skb);
- void (*connect_indication)(void *instance, void *sap,
- struct qos_info *qos, __u32 max_sdu_size,
- __u8 max_header_size, struct sk_buff *skb);
- void (*disconnect_indication)(void *instance, void *sap,
- LM_REASON reason, struct sk_buff *);
- void (*flow_indication)(void *instance, void *sap, LOCAL_FLOW flow);
- void (*status_indication)(void *instance,
- LINK_STATUS link, LOCK_STATUS lock);
- void *instance; /* Layer instance pointer */
- char name[16]; /* Name of layer */
-} notify_t;
-
-#define NOTIFY_MAX_NAME 16
-
-/* Zero the notify structure */
-void irda_notify_init(notify_t *notify);
-
-/* Locking wrapper - Note the inverted logic on irda_lock().
- * Those function basically return false if the lock is already in the
- * position you want to set it. - Jean II */
-#define irda_lock(lock) (! test_and_set_bit(0, (void *) (lock)))
-#define irda_unlock(lock) (test_and_clear_bit(0, (void *) (lock)))
-
-#endif /* IRMOD_H */
-
-
-
-
-
-
-
-
-
diff --git a/drivers/staging/irda/include/net/irda/irqueue.h b/drivers/staging/irda/include/net/irda/irqueue.h
deleted file mode 100644
index 37f512bd6733..000000000000
--- a/drivers/staging/irda/include/net/irda/irqueue.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/*********************************************************************
- *
- * Filename: irqueue.h
- * Version: 0.3
- * Description: General queue implementation
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Tue Jun 9 13:26:50 1998
- * Modified at: Thu Oct 7 13:25:16 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (C) 1998-1999, Aage Kvalnes <aage@cs.uit.no>
- * Copyright (c) 1998, Dag Brattli
- * All Rights Reserved.
- *
- * This code is taken from the Vortex Operating System written by Aage
- * Kvalnes and has been ported to Linux and Linux/IR by Dag Brattli
- *
- * 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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/types.h>
-#include <linux/spinlock.h>
-
-#ifndef IRDA_QUEUE_H
-#define IRDA_QUEUE_H
-
-#define NAME_SIZE 32
-
-/*
- * Hash types (some flags can be xored)
- * See comments in irqueue.c for which one to use...
- */
-#define HB_NOLOCK 0 /* No concurent access prevention */
-#define HB_LOCK 1 /* Prevent concurent write with global lock */
-
-/*
- * Hash defines
- */
-#define HASHBIN_SIZE 8
-#define HASHBIN_MASK 0x7
-
-#ifndef IRDA_ALIGN
-#define IRDA_ALIGN __attribute__((aligned))
-#endif
-
-#define Q_NULL { NULL, NULL, "", 0 }
-
-typedef void (*FREE_FUNC)(void *arg);
-
-struct irda_queue {
- struct irda_queue *q_next;
- struct irda_queue *q_prev;
-
- char q_name[NAME_SIZE];
- long q_hash; /* Must be able to cast a (void *) */
-};
-typedef struct irda_queue irda_queue_t;
-
-typedef struct hashbin_t {
- __u32 magic;
- int hb_type;
- int hb_size;
- spinlock_t hb_spinlock; /* HB_LOCK - Can be used by the user */
-
- irda_queue_t* hb_queue[HASHBIN_SIZE] IRDA_ALIGN;
-
- irda_queue_t* hb_current;
-} hashbin_t;
-
-hashbin_t *hashbin_new(int type);
-int hashbin_delete(hashbin_t* hashbin, FREE_FUNC func);
-int hashbin_clear(hashbin_t* hashbin, FREE_FUNC free_func);
-void hashbin_insert(hashbin_t* hashbin, irda_queue_t* entry, long hashv,
- const char* name);
-void* hashbin_remove(hashbin_t* hashbin, long hashv, const char* name);
-void* hashbin_remove_first(hashbin_t *hashbin);
-void* hashbin_remove_this( hashbin_t* hashbin, irda_queue_t* entry);
-void* hashbin_find(hashbin_t* hashbin, long hashv, const char* name);
-void* hashbin_lock_find(hashbin_t* hashbin, long hashv, const char* name);
-void* hashbin_find_next(hashbin_t* hashbin, long hashv, const char* name,
- void ** pnext);
-irda_queue_t *hashbin_get_first(hashbin_t *hashbin);
-irda_queue_t *hashbin_get_next(hashbin_t *hashbin);
-
-#define HASHBIN_GET_SIZE(hashbin) hashbin->hb_size
-
-#endif
diff --git a/drivers/staging/irda/include/net/irda/irttp.h b/drivers/staging/irda/include/net/irda/irttp.h
deleted file mode 100644
index 98682d4bae8f..000000000000
--- a/drivers/staging/irda/include/net/irda/irttp.h
+++ /dev/null
@@ -1,210 +0,0 @@
-/*********************************************************************
- *
- * Filename: irttp.h
- * Version: 1.0
- * Description: Tiny Transport Protocol (TTP) definitions
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sun Aug 31 20:14:31 1997
- * Modified at: Sun Dec 12 13:09:07 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
- * All Rights Reserved.
- * Copyright (c) 2000-2002 Jean Tourrilhes <jt@hpl.hp.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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#ifndef IRTTP_H
-#define IRTTP_H
-
-#include <linux/types.h>
-#include <linux/skbuff.h>
-#include <linux/spinlock.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irlmp.h> /* struct lsap_cb */
-#include <net/irda/qos.h> /* struct qos_info */
-#include <net/irda/irqueue.h>
-
-#define TTP_MAX_CONNECTIONS LM_MAX_CONNECTIONS
-#define TTP_HEADER 1
-#define TTP_MAX_HEADER (TTP_HEADER + LMP_MAX_HEADER)
-#define TTP_SAR_HEADER 5
-#define TTP_PARAMETERS 0x80
-#define TTP_MORE 0x80
-
-/* Transmission queue sizes */
-/* Worst case scenario, two window of data - Jean II */
-#define TTP_TX_MAX_QUEUE 14
-/* We need to keep at least 5 frames to make sure that we can refill
- * appropriately the LAP layer. LAP keeps only two buffers, and we need
- * to have 7 to make a full window - Jean II */
-#define TTP_TX_LOW_THRESHOLD 5
-/* Most clients are synchronous with respect to flow control, so we can
- * keep a low number of Tx buffers in TTP - Jean II */
-#define TTP_TX_HIGH_THRESHOLD 7
-
-/* Receive queue sizes */
-/* Minimum of credit that the peer should hold.
- * If the peer has less credits than 9 frames, we will explicitly send
- * him some credits (through irttp_give_credit() and a specific frame).
- * Note that when we give credits it's likely that it won't be sent in
- * this LAP window, but in the next one. So, we make sure that the peer
- * has something to send while waiting for credits (one LAP window == 7
- * + 1 frames while he process the credits). - Jean II */
-#define TTP_RX_MIN_CREDIT 8
-/* This is the default maximum number of credits held by the peer, so the
- * default maximum number of frames he can send us before needing flow
- * control answer from us (this may be negociated differently at TSAP setup).
- * We want to minimise the number of times we have to explicitly send some
- * credit to the peer, hoping we can piggyback it on the return data. In
- * particular, it doesn't make sense for us to send credit more than once
- * per LAP window.
- * Moreover, giving credits has some latency, so we need strictly more than
- * a LAP window, otherwise we may already have credits in our Tx queue.
- * But on the other hand, we don't want to keep too many Rx buffer here
- * before starting to flow control the other end, so make it exactly one
- * LAP window + 1 + MIN_CREDITS. - Jean II */
-#define TTP_RX_DEFAULT_CREDIT 16
-/* Maximum number of credits we can allow the peer to have, and therefore
- * maximum Rx queue size.
- * Note that we try to deliver packets to the higher layer every time we
- * receive something, so in normal mode the Rx queue will never contains
- * more than one or two packets. - Jean II */
-#define TTP_RX_MAX_CREDIT 21
-
-/* What clients should use when calling ttp_open_tsap() */
-#define DEFAULT_INITIAL_CREDIT TTP_RX_DEFAULT_CREDIT
-
-/* Some priorities for disconnect requests */
-#define P_NORMAL 0
-#define P_HIGH 1
-
-#define TTP_SAR_DISABLE 0
-#define TTP_SAR_UNBOUND 0xffffffff
-
-/* Parameters */
-#define TTP_MAX_SDU_SIZE 0x01
-
-/*
- * This structure contains all data associated with one instance of a TTP
- * connection.
- */
-struct tsap_cb {
- irda_queue_t q; /* Must be first */
- magic_t magic; /* Just in case */
-
- __u8 stsap_sel; /* Source TSAP */
- __u8 dtsap_sel; /* Destination TSAP */
-
- struct lsap_cb *lsap; /* Corresponding LSAP to this TSAP */
-
- __u8 connected; /* TSAP connected */
-
- __u8 initial_credit; /* Initial credit to give peer */
-
- int avail_credit; /* Available credit to return to peer */
- int remote_credit; /* Credit held by peer TTP entity */
- int send_credit; /* Credit held by local TTP entity */
-
- struct sk_buff_head tx_queue; /* Frames to be transmitted */
- struct sk_buff_head rx_queue; /* Received frames */
- struct sk_buff_head rx_fragments;
- int tx_queue_lock;
- int rx_queue_lock;
- spinlock_t lock;
-
- notify_t notify; /* Callbacks to client layer */
-
- struct net_device_stats stats;
- struct timer_list todo_timer;
-
- __u32 max_seg_size; /* Max data that fit into an IrLAP frame */
- __u8 max_header_size;
-
- int rx_sdu_busy; /* RxSdu.busy */
- __u32 rx_sdu_size; /* Current size of a partially received frame */
- __u32 rx_max_sdu_size; /* Max receive user data size */
-
- int tx_sdu_busy; /* TxSdu.busy */
- __u32 tx_max_sdu_size; /* Max transmit user data size */
-
- int close_pend; /* Close, but disconnect_pend */
- unsigned long disconnect_pend; /* Disconnect, but still data to send */
- struct sk_buff *disconnect_skb;
-};
-
-struct irttp_cb {
- magic_t magic;
- hashbin_t *tsaps;
-};
-
-int irttp_init(void);
-void irttp_cleanup(void);
-
-struct tsap_cb *irttp_open_tsap(__u8 stsap_sel, int credit, notify_t *notify);
-int irttp_close_tsap(struct tsap_cb *self);
-
-int irttp_data_request(struct tsap_cb *self, struct sk_buff *skb);
-int irttp_udata_request(struct tsap_cb *self, struct sk_buff *skb);
-
-int irttp_connect_request(struct tsap_cb *self, __u8 dtsap_sel,
- __u32 saddr, __u32 daddr,
- struct qos_info *qos, __u32 max_sdu_size,
- struct sk_buff *userdata);
-int irttp_connect_response(struct tsap_cb *self, __u32 max_sdu_size,
- struct sk_buff *userdata);
-int irttp_disconnect_request(struct tsap_cb *self, struct sk_buff *skb,
- int priority);
-void irttp_flow_request(struct tsap_cb *self, LOCAL_FLOW flow);
-struct tsap_cb *irttp_dup(struct tsap_cb *self, void *instance);
-
-static inline __u32 irttp_get_saddr(struct tsap_cb *self)
-{
- return irlmp_get_saddr(self->lsap);
-}
-
-static inline __u32 irttp_get_daddr(struct tsap_cb *self)
-{
- return irlmp_get_daddr(self->lsap);
-}
-
-static inline __u32 irttp_get_max_seg_size(struct tsap_cb *self)
-{
- return self->max_seg_size;
-}
-
-/* After doing a irttp_dup(), this get one of the two socket back into
- * a state where it's waiting incoming connections.
- * Note : this can be used *only* if the socket is not yet connected
- * (i.e. NO irttp_connect_response() done on this socket).
- * - Jean II */
-static inline void irttp_listen(struct tsap_cb *self)
-{
- irlmp_listen(self->lsap);
- self->dtsap_sel = LSAP_ANY;
-}
-
-/* Return TRUE if the node is in primary mode (i.e. master)
- * - Jean II */
-static inline int irttp_is_primary(struct tsap_cb *self)
-{
- if ((self == NULL) ||
- (self->lsap == NULL) ||
- (self->lsap->lap == NULL) ||
- (self->lsap->lap->irlap == NULL))
- return -2;
- return irlap_is_primary(self->lsap->lap->irlap);
-}
-
-#endif /* IRTTP_H */
diff --git a/drivers/staging/irda/include/net/irda/parameters.h b/drivers/staging/irda/include/net/irda/parameters.h
deleted file mode 100644
index 2d9cd0007cba..000000000000
--- a/drivers/staging/irda/include/net/irda/parameters.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/*********************************************************************
- *
- * Filename: parameters.h
- * Version: 1.0
- * Description: A more general way to handle (pi,pl,pv) parameters
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Mon Jun 7 08:47:28 1999
- * Modified at: Sun Jan 30 14:05:14 2000
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- *
- * Michel Dänzer <daenzer@debian.org>, 10/2001
- * - simplify irda_pv_t to avoid endianness issues
- *
- ********************************************************************/
-
-#ifndef IRDA_PARAMS_H
-#define IRDA_PARAMS_H
-
-/*
- * The currently supported types. Beware not to change the sequence since
- * it a good reason why the sized integers has a value equal to their size
- */
-typedef enum {
- PV_INTEGER, /* Integer of any (pl) length */
- PV_INT_8_BITS, /* Integer of 8 bits in length */
- PV_INT_16_BITS, /* Integer of 16 bits in length */
- PV_STRING, /* \0 terminated string */
- PV_INT_32_BITS, /* Integer of 32 bits in length */
- PV_OCT_SEQ, /* Octet sequence */
- PV_NO_VALUE /* Does not contain any value (pl=0) */
-} PV_TYPE;
-
-/* Bit 7 of type field */
-#define PV_BIG_ENDIAN 0x80
-#define PV_LITTLE_ENDIAN 0x00
-#define PV_MASK 0x7f /* To mask away endian bit */
-
-#define PV_PUT 0
-#define PV_GET 1
-
-typedef union {
- char *c;
- __u32 i;
- __u32 *ip;
-} irda_pv_t;
-
-typedef struct {
- __u8 pi;
- __u8 pl;
- irda_pv_t pv;
-} irda_param_t;
-
-typedef int (*PI_HANDLER)(void *self, irda_param_t *param, int get);
-typedef int (*PV_HANDLER)(void *self, __u8 *buf, int len, __u8 pi,
- PV_TYPE type, PI_HANDLER func);
-
-typedef struct {
- const PI_HANDLER func; /* Handler for this parameter identifier */
- PV_TYPE type; /* Data type for this parameter */
-} pi_minor_info_t;
-
-typedef struct {
- const pi_minor_info_t *pi_minor_call_table;
- int len;
-} pi_major_info_t;
-
-typedef struct {
- const pi_major_info_t *tables;
- int len;
- __u8 pi_mask;
- int pi_major_offset;
-} pi_param_info_t;
-
-int irda_param_pack(__u8 *buf, char *fmt, ...);
-
-int irda_param_insert(void *self, __u8 pi, __u8 *buf, int len,
- pi_param_info_t *info);
-int irda_param_extract_all(void *self, __u8 *buf, int len,
- pi_param_info_t *info);
-
-#define irda_param_insert_byte(buf,pi,pv) irda_param_pack(buf,"bbb",pi,1,pv)
-
-#endif /* IRDA_PARAMS_H */
-
diff --git a/drivers/staging/irda/include/net/irda/qos.h b/drivers/staging/irda/include/net/irda/qos.h
deleted file mode 100644
index a0315b50ac27..000000000000
--- a/drivers/staging/irda/include/net/irda/qos.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/*********************************************************************
- *
- * Filename: qos.h
- * Version: 1.0
- * Description: Quality of Service definitions
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Fri Sep 19 23:21:09 1997
- * Modified at: Thu Dec 2 13:51:54 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1999 Dag Brattli, All Rights Reserved.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#ifndef IRDA_QOS_H
-#define IRDA_QOS_H
-
-#include <linux/skbuff.h>
-
-#include <net/irda/parameters.h>
-
-#define PI_BAUD_RATE 0x01
-#define PI_MAX_TURN_TIME 0x82
-#define PI_DATA_SIZE 0x83
-#define PI_WINDOW_SIZE 0x84
-#define PI_ADD_BOFS 0x85
-#define PI_MIN_TURN_TIME 0x86
-#define PI_LINK_DISC 0x08
-
-#define IR_115200_MAX 0x3f
-
-/* Baud rates (first byte) */
-#define IR_2400 0x01
-#define IR_9600 0x02
-#define IR_19200 0x04
-#define IR_38400 0x08
-#define IR_57600 0x10
-#define IR_115200 0x20
-#define IR_576000 0x40
-#define IR_1152000 0x80
-
-/* Baud rates (second byte) */
-#define IR_4000000 0x01
-#define IR_16000000 0x02
-
-/* Quality of Service information */
-struct qos_value {
- __u32 value;
- __u16 bits; /* LSB is first byte, MSB is second byte */
-};
-
-struct qos_info {
- magic_t magic;
-
- struct qos_value baud_rate; /* IR_11520O | ... */
- struct qos_value max_turn_time;
- struct qos_value data_size;
- struct qos_value window_size;
- struct qos_value additional_bofs;
- struct qos_value min_turn_time;
- struct qos_value link_disc_time;
-
- struct qos_value power;
-};
-
-extern int sysctl_max_baud_rate;
-extern int sysctl_max_inactive_time;
-
-void irda_init_max_qos_capabilies(struct qos_info *qos);
-void irda_qos_compute_intersection(struct qos_info *, struct qos_info *);
-
-__u32 irlap_max_line_capacity(__u32 speed, __u32 max_turn_time);
-
-void irda_qos_bits_to_value(struct qos_info *qos);
-
-/* So simple, how could we not inline those two ?
- * Note : one byte is 10 bits if you include start and stop bits
- * Jean II */
-#define irlap_min_turn_time_in_bytes(speed, min_turn_time) ( \
- speed * min_turn_time / 10000000 \
-)
-#define irlap_xbofs_in_usec(speed, xbofs) ( \
- xbofs * 10000000 / speed \
-)
-
-#endif
-
diff --git a/drivers/staging/irda/include/net/irda/timer.h b/drivers/staging/irda/include/net/irda/timer.h
deleted file mode 100644
index 6dab15f5dae1..000000000000
--- a/drivers/staging/irda/include/net/irda/timer.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/*********************************************************************
- *
- * Filename: timer.h
- * Version:
- * Description:
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sat Aug 16 00:59:29 1997
- * Modified at: Thu Oct 7 12:25:24 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1997, 1998-1999 Dag Brattli <dagb@cs.uit.no>,
- * All Rights Reserved.
- * Copyright (c) 2000-2002 Jean Tourrilhes <jt@hpl.hp.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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#ifndef TIMER_H
-#define TIMER_H
-
-#include <linux/timer.h>
-#include <linux/jiffies.h>
-
-#include <asm/param.h> /* for HZ */
-
-#include <net/irda/irda.h>
-
-/* A few forward declarations (to make compiler happy) */
-struct irlmp_cb;
-struct irlap_cb;
-struct lsap_cb;
-struct lap_cb;
-
-/*
- * Timeout definitions, some defined in IrLAP 6.13.5 - p. 92
- */
-#define POLL_TIMEOUT (450*HZ/1000) /* Must never exceed 500 ms */
-#define FINAL_TIMEOUT (500*HZ/1000) /* Must never exceed 500 ms */
-
-/*
- * Normally twice of p-timer. Note 3, IrLAP 6.3.11.2 - p. 60 suggests
- * at least twice duration of the P-timer.
- */
-#define WD_TIMEOUT (POLL_TIMEOUT*2)
-
-#define MEDIABUSY_TIMEOUT (500*HZ/1000) /* 500 msec */
-#define SMALLBUSY_TIMEOUT (100*HZ/1000) /* 100 msec - IrLAP 6.13.4 */
-
-/*
- * Slot timer must never exceed 85 ms, and must always be at least 25 ms,
- * suggested to 75-85 msec by IrDA lite. This doesn't work with a lot of
- * devices, and other stackes uses a lot more, so it's best we do it as well
- * (Note : this is the default value and sysctl overrides it - Jean II)
- */
-#define SLOT_TIMEOUT (90*HZ/1000)
-
-/*
- * The latest discovery frame (XID) is longer due to the extra discovery
- * information (hints, device name...). This is its extra length.
- * We use that when setting the query timeout. Jean II
- */
-#define XIDEXTRA_TIMEOUT (34*HZ/1000) /* 34 msec */
-
-#define WATCHDOG_TIMEOUT (20*HZ) /* 20 sec */
-
-static inline void irda_start_timer(struct timer_list *ptimer, int timeout,
- void (*callback)(struct timer_list *))
-{
- ptimer->function = callback;
-
- /* Set new value for timer (update or add timer).
- * We use mod_timer() because it's more efficient and also
- * safer with respect to race conditions - Jean II */
- mod_timer(ptimer, jiffies + timeout);
-}
-
-
-void irlap_start_slot_timer(struct irlap_cb *self, int timeout);
-void irlap_start_query_timer(struct irlap_cb *self, int S, int s);
-void irlap_start_final_timer(struct irlap_cb *self, int timeout);
-void irlap_start_wd_timer(struct irlap_cb *self, int timeout);
-void irlap_start_backoff_timer(struct irlap_cb *self, int timeout);
-
-void irlap_start_mbusy_timer(struct irlap_cb *self, int timeout);
-void irlap_stop_mbusy_timer(struct irlap_cb *);
-
-void irlmp_start_watchdog_timer(struct lsap_cb *, int timeout);
-void irlmp_start_discovery_timer(struct irlmp_cb *, int timeout);
-void irlmp_start_idle_timer(struct lap_cb *, int timeout);
-void irlmp_stop_idle_timer(struct lap_cb *self);
-
-#endif
-
diff --git a/drivers/staging/irda/include/net/irda/wrapper.h b/drivers/staging/irda/include/net/irda/wrapper.h
deleted file mode 100644
index eef53ebe3d76..000000000000
--- a/drivers/staging/irda/include/net/irda/wrapper.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*********************************************************************
- *
- * Filename: wrapper.h
- * Version: 1.2
- * Description: IrDA SIR async wrapper layer
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Mon Aug 4 20:40:53 1997
- * Modified at: Tue Jan 11 12:37:29 2000
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>,
- * All Rights Reserved.
- *
- * 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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#ifndef WRAPPER_H
-#define WRAPPER_H
-
-#include <linux/types.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-
-#include <net/irda/irda_device.h> /* iobuff_t */
-
-#define BOF 0xc0 /* Beginning of frame */
-#define XBOF 0xff
-#define EOF 0xc1 /* End of frame */
-#define CE 0x7d /* Control escape */
-
-#define STA BOF /* Start flag */
-#define STO EOF /* End flag */
-
-#define IRDA_TRANS 0x20 /* Asynchronous transparency modifier */
-
-/* States for receiving a frame in async mode */
-enum {
- OUTSIDE_FRAME,
- BEGIN_FRAME,
- LINK_ESCAPE,
- INSIDE_FRAME
-};
-
-/* Proto definitions */
-int async_wrap_skb(struct sk_buff *skb, __u8 *tx_buff, int buffsize);
-void async_unwrap_char(struct net_device *dev, struct net_device_stats *stats,
- iobuff_t *buf, __u8 byte);
-
-#endif
diff --git a/drivers/staging/irda/net/Kconfig b/drivers/staging/irda/net/Kconfig
deleted file mode 100644
index 6abeae6c666a..000000000000
--- a/drivers/staging/irda/net/Kconfig
+++ /dev/null
@@ -1,96 +0,0 @@
-#
-# IrDA protocol configuration
-#
-
-menuconfig IRDA
- depends on NET && !S390
- tristate "IrDA (infrared) subsystem support"
- select CRC_CCITT
- ---help---
- Say Y here if you want to build support for the IrDA (TM) protocols.
- The Infrared Data Associations (tm) specifies standards for wireless
- infrared communication and is supported by most laptops and PDA's.
-
- To use Linux support for the IrDA (tm) protocols, you will also need
- some user-space utilities like irattach. For more information, see
- the file <file:Documentation/networking/irda.txt>. You also want to
- read the IR-HOWTO, available at
- <http://www.tldp.org/docs.html#howto>.
-
- If you want to exchange bits of data (vCal, vCard) with a PDA, you
- will need to install some OBEX application, such as OpenObex :
- <http://sourceforge.net/projects/openobex/>
-
- To compile this support as a module, choose M here: the module will
- be called irda.
-
-comment "IrDA protocols"
- depends on IRDA
-
-source "drivers/staging/irda/net/irlan/Kconfig"
-
-source "drivers/staging/irda/net/irnet/Kconfig"
-
-source "drivers/staging/irda/net/ircomm/Kconfig"
-
-config IRDA_ULTRA
- bool "Ultra (connectionless) protocol"
- depends on IRDA
- help
- Say Y here to support the connectionless Ultra IRDA protocol.
- Ultra allows to exchange data over IrDA with really simple devices
- (watch, beacon) without the overhead of the IrDA protocol (no handshaking,
- no management frames, simple fixed header).
- Ultra is available as a special socket : socket(AF_IRDA, SOCK_DGRAM, 1);
-
-comment "IrDA options"
- depends on IRDA
-
-config IRDA_CACHE_LAST_LSAP
- bool "Cache last LSAP"
- depends on IRDA
- help
- Say Y here if you want IrLMP to cache the last LSAP used. This
- makes sense since most frames will be sent/received on the same
- connection. Enabling this option will save a hash-lookup per frame.
-
- If unsure, say Y.
-
-config IRDA_FAST_RR
- bool "Fast RRs (low latency)"
- depends on IRDA
- ---help---
- Say Y here is you want IrLAP to send fast RR (Receive Ready) frames
- when acting as a primary station.
- Disabling this option will make latency over IrDA very bad. Enabling
- this option will make the IrDA stack send more packet than strictly
- necessary, thus reduce your battery life (but not that much).
-
- Fast RR will make IrLAP send out a RR frame immediately when
- receiving a frame if its own transmit queue is currently empty. This
- will give a lot of speed improvement when receiving much data since
- the secondary station will not have to wait the max. turn around
- time (usually 500ms) before it is allowed to transmit the next time.
- If the transmit queue of the secondary is also empty, the primary will
- start backing-off before sending another RR frame, waiting longer
- each time until the back-off reaches the max. turn around time.
- This back-off increase in controlled via
- /proc/sys/net/irda/fast_poll_increase
-
- If unsure, say Y.
-
-config IRDA_DEBUG
- bool "Debug information"
- depends on IRDA
- help
- Say Y here if you want the IrDA subsystem to write debug information
- to your syslog. You can change the debug level in
- /proc/sys/net/irda/debug .
- When this option is enabled, the IrDA also perform many extra internal
- verifications which will usually prevent the kernel to crash in case of
- bugs.
-
- If unsure, say Y (since it makes it easier to find the bugs).
-
-source "drivers/staging/irda/drivers/Kconfig"
-
diff --git a/drivers/staging/irda/net/Makefile b/drivers/staging/irda/net/Makefile
deleted file mode 100644
index bd1a635b88cf..000000000000
--- a/drivers/staging/irda/net/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-#
-# Makefile for the Linux IrDA protocol layer.
-#
-
-subdir-ccflags-y += -I$(srctree)/drivers/staging/irda/include
-
-obj-$(CONFIG_IRDA) += irda.o
-obj-$(CONFIG_IRLAN) += irlan/
-obj-$(CONFIG_IRNET) += irnet/
-obj-$(CONFIG_IRCOMM) += ircomm/
-
-irda-y := iriap.o iriap_event.o irlmp.o irlmp_event.o irlmp_frame.o \
- irlap.o irlap_event.o irlap_frame.o timer.o qos.o irqueue.o \
- irttp.o irda_device.o irias_object.o wrapper.o af_irda.o \
- discovery.o parameters.o irnetlink.o irmod.o
-irda-$(CONFIG_PROC_FS) += irproc.o
-irda-$(CONFIG_SYSCTL) += irsysctl.o
diff --git a/drivers/staging/irda/net/af_irda.c b/drivers/staging/irda/net/af_irda.c
deleted file mode 100644
index 2f1e9ab3d6d0..000000000000
--- a/drivers/staging/irda/net/af_irda.c
+++ /dev/null
@@ -1,2694 +0,0 @@
-/*********************************************************************
- *
- * Filename: af_irda.c
- * Version: 0.9
- * Description: IrDA sockets implementation
- * Status: Stable
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sun May 31 10:12:43 1998
- * Modified at: Sat Dec 25 21:10:23 1999
- * Modified by: Dag Brattli <dag@brattli.net>
- * Sources: af_netroom.c, af_ax25.c, af_rose.c, af_x25.c etc.
- *
- * Copyright (c) 1999 Dag Brattli <dagb@cs.uit.no>
- * Copyright (c) 1999-2003 Jean Tourrilhes <jt@hpl.hp.com>
- * All Rights Reserved.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- *
- * Linux-IrDA now supports four different types of IrDA sockets:
- *
- * o SOCK_STREAM: TinyTP connections with SAR disabled. The
- * max SDU size is 0 for conn. of this type
- * o SOCK_SEQPACKET: TinyTP connections with SAR enabled. TTP may
- * fragment the messages, but will preserve
- * the message boundaries
- * o SOCK_DGRAM: IRDAPROTO_UNITDATA: TinyTP connections with Unitdata
- * (unreliable) transfers
- * IRDAPROTO_ULTRA: Connectionless and unreliable data
- *
- ********************************************************************/
-
-#include <linux/capability.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/sockios.h>
-#include <linux/slab.h>
-#include <linux/sched/signal.h>
-#include <linux/init.h>
-#include <linux/net.h>
-#include <linux/irda.h>
-#include <linux/poll.h>
-
-#include <asm/ioctls.h> /* TIOCOUTQ, TIOCINQ */
-#include <linux/uaccess.h>
-
-#include <net/sock.h>
-#include <net/tcp_states.h>
-
-#include <net/irda/af_irda.h>
-
-static int irda_create(struct net *net, struct socket *sock, int protocol, int kern);
-
-static const struct proto_ops irda_stream_ops;
-static const struct proto_ops irda_seqpacket_ops;
-static const struct proto_ops irda_dgram_ops;
-
-#ifdef CONFIG_IRDA_ULTRA
-static const struct proto_ops irda_ultra_ops;
-#define ULTRA_MAX_DATA 382
-#endif /* CONFIG_IRDA_ULTRA */
-
-#define IRDA_MAX_HEADER (TTP_MAX_HEADER)
-
-/*
- * Function irda_data_indication (instance, sap, skb)
- *
- * Received some data from TinyTP. Just queue it on the receive queue
- *
- */
-static int irda_data_indication(void *instance, void *sap, struct sk_buff *skb)
-{
- struct irda_sock *self;
- struct sock *sk;
- int err;
-
- self = instance;
- sk = instance;
-
- err = sock_queue_rcv_skb(sk, skb);
- if (err) {
- pr_debug("%s(), error: no more mem!\n", __func__);
- self->rx_flow = FLOW_STOP;
-
- /* When we return error, TTP will need to requeue the skb */
- return err;
- }
-
- return 0;
-}
-
-/*
- * Function irda_disconnect_indication (instance, sap, reason, skb)
- *
- * Connection has been closed. Check reason to find out why
- *
- */
-static void irda_disconnect_indication(void *instance, void *sap,
- LM_REASON reason, struct sk_buff *skb)
-{
- struct irda_sock *self;
- struct sock *sk;
-
- self = instance;
-
- pr_debug("%s(%p)\n", __func__, self);
-
- /* Don't care about it, but let's not leak it */
- if(skb)
- dev_kfree_skb(skb);
-
- sk = instance;
- if (sk == NULL) {
- pr_debug("%s(%p) : BUG : sk is NULL\n",
- __func__, self);
- return;
- }
-
- /* Prevent race conditions with irda_release() and irda_shutdown() */
- bh_lock_sock(sk);
- if (!sock_flag(sk, SOCK_DEAD) && sk->sk_state != TCP_CLOSE) {
- sk->sk_state = TCP_CLOSE;
- sk->sk_shutdown |= SEND_SHUTDOWN;
-
- sk->sk_state_change(sk);
-
- /* Close our TSAP.
- * If we leave it open, IrLMP put it back into the list of
- * unconnected LSAPs. The problem is that any incoming request
- * can then be matched to this socket (and it will be, because
- * it is at the head of the list). This would prevent any
- * listening socket waiting on the same TSAP to get those
- * requests. Some apps forget to close sockets, or hang to it
- * a bit too long, so we may stay in this dead state long
- * enough to be noticed...
- * Note : all socket function do check sk->sk_state, so we are
- * safe...
- * Jean II
- */
- if (self->tsap) {
- irttp_close_tsap(self->tsap);
- self->tsap = NULL;
- }
- }
- bh_unlock_sock(sk);
-
- /* Note : once we are there, there is not much you want to do
- * with the socket anymore, apart from closing it.
- * For example, bind() and connect() won't reset sk->sk_err,
- * sk->sk_shutdown and sk->sk_flags to valid values...
- * Jean II
- */
-}
-
-/*
- * Function irda_connect_confirm (instance, sap, qos, max_sdu_size, skb)
- *
- * Connections has been confirmed by the remote device
- *
- */
-static void irda_connect_confirm(void *instance, void *sap,
- struct qos_info *qos,
- __u32 max_sdu_size, __u8 max_header_size,
- struct sk_buff *skb)
-{
- struct irda_sock *self;
- struct sock *sk;
-
- self = instance;
-
- pr_debug("%s(%p)\n", __func__, self);
-
- sk = instance;
- if (sk == NULL) {
- dev_kfree_skb(skb);
- return;
- }
-
- dev_kfree_skb(skb);
- // Should be ??? skb_queue_tail(&sk->sk_receive_queue, skb);
-
- /* How much header space do we need to reserve */
- self->max_header_size = max_header_size;
-
- /* IrTTP max SDU size in transmit direction */
- self->max_sdu_size_tx = max_sdu_size;
-
- /* Find out what the largest chunk of data that we can transmit is */
- switch (sk->sk_type) {
- case SOCK_STREAM:
- if (max_sdu_size != 0) {
- net_err_ratelimited("%s: max_sdu_size must be 0\n",
- __func__);
- return;
- }
- self->max_data_size = irttp_get_max_seg_size(self->tsap);
- break;
- case SOCK_SEQPACKET:
- if (max_sdu_size == 0) {
- net_err_ratelimited("%s: max_sdu_size cannot be 0\n",
- __func__);
- return;
- }
- self->max_data_size = max_sdu_size;
- break;
- default:
- self->max_data_size = irttp_get_max_seg_size(self->tsap);
- }
-
- pr_debug("%s(), max_data_size=%d\n", __func__,
- self->max_data_size);
-
- memcpy(&self->qos_tx, qos, sizeof(struct qos_info));
-
- /* We are now connected! */
- sk->sk_state = TCP_ESTABLISHED;
- sk->sk_state_change(sk);
-}
-
-/*
- * Function irda_connect_indication(instance, sap, qos, max_sdu_size, userdata)
- *
- * Incoming connection
- *
- */
-static void irda_connect_indication(void *instance, void *sap,
- struct qos_info *qos, __u32 max_sdu_size,
- __u8 max_header_size, struct sk_buff *skb)
-{
- struct irda_sock *self;
- struct sock *sk;
-
- self = instance;
-
- pr_debug("%s(%p)\n", __func__, self);
-
- sk = instance;
- if (sk == NULL) {
- dev_kfree_skb(skb);
- return;
- }
-
- /* How much header space do we need to reserve */
- self->max_header_size = max_header_size;
-
- /* IrTTP max SDU size in transmit direction */
- self->max_sdu_size_tx = max_sdu_size;
-
- /* Find out what the largest chunk of data that we can transmit is */
- switch (sk->sk_type) {
- case SOCK_STREAM:
- if (max_sdu_size != 0) {
- net_err_ratelimited("%s: max_sdu_size must be 0\n",
- __func__);
- kfree_skb(skb);
- return;
- }
- self->max_data_size = irttp_get_max_seg_size(self->tsap);
- break;
- case SOCK_SEQPACKET:
- if (max_sdu_size == 0) {
- net_err_ratelimited("%s: max_sdu_size cannot be 0\n",
- __func__);
- kfree_skb(skb);
- return;
- }
- self->max_data_size = max_sdu_size;
- break;
- default:
- self->max_data_size = irttp_get_max_seg_size(self->tsap);
- }
-
- pr_debug("%s(), max_data_size=%d\n", __func__,
- self->max_data_size);
-
- memcpy(&self->qos_tx, qos, sizeof(struct qos_info));
-
- skb_queue_tail(&sk->sk_receive_queue, skb);
- sk->sk_state_change(sk);
-}
-
-/*
- * Function irda_connect_response (handle)
- *
- * Accept incoming connection
- *
- */
-static void irda_connect_response(struct irda_sock *self)
-{
- struct sk_buff *skb;
-
- skb = alloc_skb(TTP_MAX_HEADER + TTP_SAR_HEADER, GFP_KERNEL);
- if (skb == NULL) {
- pr_debug("%s() Unable to allocate sk_buff!\n",
- __func__);
- return;
- }
-
- /* Reserve space for MUX_CONTROL and LAP header */
- skb_reserve(skb, IRDA_MAX_HEADER);
-
- irttp_connect_response(self->tsap, self->max_sdu_size_rx, skb);
-}
-
-/*
- * Function irda_flow_indication (instance, sap, flow)
- *
- * Used by TinyTP to tell us if it can accept more data or not
- *
- */
-static void irda_flow_indication(void *instance, void *sap, LOCAL_FLOW flow)
-{
- struct irda_sock *self;
- struct sock *sk;
-
- self = instance;
- sk = instance;
- BUG_ON(sk == NULL);
-
- switch (flow) {
- case FLOW_STOP:
- pr_debug("%s(), IrTTP wants us to slow down\n",
- __func__);
- self->tx_flow = flow;
- break;
- case FLOW_START:
- self->tx_flow = flow;
- pr_debug("%s(), IrTTP wants us to start again\n",
- __func__);
- wake_up_interruptible(sk_sleep(sk));
- break;
- default:
- pr_debug("%s(), Unknown flow command!\n", __func__);
- /* Unknown flow command, better stop */
- self->tx_flow = flow;
- break;
- }
-}
-
-/*
- * Function irda_getvalue_confirm (obj_id, value, priv)
- *
- * Got answer from remote LM-IAS, just pass object to requester...
- *
- * Note : duplicate from above, but we need our own version that
- * doesn't touch the dtsap_sel and save the full value structure...
- */
-static void irda_getvalue_confirm(int result, __u16 obj_id,
- struct ias_value *value, void *priv)
-{
- struct irda_sock *self;
-
- self = priv;
- if (!self) {
- net_warn_ratelimited("%s: lost myself!\n", __func__);
- return;
- }
-
- pr_debug("%s(%p)\n", __func__, self);
-
- /* We probably don't need to make any more queries */
- iriap_close(self->iriap);
- self->iriap = NULL;
-
- /* Check if request succeeded */
- if (result != IAS_SUCCESS) {
- pr_debug("%s(), IAS query failed! (%d)\n", __func__,
- result);
-
- self->errno = result; /* We really need it later */
-
- /* Wake up any processes waiting for result */
- wake_up_interruptible(&self->query_wait);
-
- return;
- }
-
- /* Pass the object to the caller (so the caller must delete it) */
- self->ias_result = value;
- self->errno = 0;
-
- /* Wake up any processes waiting for result */
- wake_up_interruptible(&self->query_wait);
-}
-
-/*
- * Function irda_selective_discovery_indication (discovery)
- *
- * Got a selective discovery indication from IrLMP.
- *
- * IrLMP is telling us that this node is new and matching our hint bit
- * filter. Wake up any process waiting for answer...
- */
-static void irda_selective_discovery_indication(discinfo_t *discovery,
- DISCOVERY_MODE mode,
- void *priv)
-{
- struct irda_sock *self;
-
- self = priv;
- if (!self) {
- net_warn_ratelimited("%s: lost myself!\n", __func__);
- return;
- }
-
- /* Pass parameter to the caller */
- self->cachedaddr = discovery->daddr;
-
- /* Wake up process if its waiting for device to be discovered */
- wake_up_interruptible(&self->query_wait);
-}
-
-/*
- * Function irda_discovery_timeout (priv)
- *
- * Timeout in the selective discovery process
- *
- * We were waiting for a node to be discovered, but nothing has come up
- * so far. Wake up the user and tell him that we failed...
- */
-static void irda_discovery_timeout(struct timer_list *t)
-{
- struct irda_sock *self;
-
- self = from_timer(self, t, watchdog);
- BUG_ON(self == NULL);
-
- /* Nothing for the caller */
- self->cachelog = NULL;
- self->cachedaddr = 0;
- self->errno = -ETIME;
-
- /* Wake up process if its still waiting... */
- wake_up_interruptible(&self->query_wait);
-}
-
-/*
- * Function irda_open_tsap (self)
- *
- * Open local Transport Service Access Point (TSAP)
- *
- */
-static int irda_open_tsap(struct irda_sock *self, __u8 tsap_sel, char *name)
-{
- notify_t notify;
-
- if (self->tsap) {
- pr_debug("%s: busy!\n", __func__);
- return -EBUSY;
- }
-
- /* Initialize callbacks to be used by the IrDA stack */
- irda_notify_init(&notify);
- notify.connect_confirm = irda_connect_confirm;
- notify.connect_indication = irda_connect_indication;
- notify.disconnect_indication = irda_disconnect_indication;
- notify.data_indication = irda_data_indication;
- notify.udata_indication = irda_data_indication;
- notify.flow_indication = irda_flow_indication;
- notify.instance = self;
- strncpy(notify.name, name, NOTIFY_MAX_NAME);
-
- self->tsap = irttp_open_tsap(tsap_sel, DEFAULT_INITIAL_CREDIT,
- &notify);
- if (self->tsap == NULL) {
- pr_debug("%s(), Unable to allocate TSAP!\n",
- __func__);
- return -ENOMEM;
- }
- /* Remember which TSAP selector we actually got */
- self->stsap_sel = self->tsap->stsap_sel;
-
- return 0;
-}
-
-/*
- * Function irda_open_lsap (self)
- *
- * Open local Link Service Access Point (LSAP). Used for opening Ultra
- * sockets
- */
-#ifdef CONFIG_IRDA_ULTRA
-static int irda_open_lsap(struct irda_sock *self, int pid)
-{
- notify_t notify;
-
- if (self->lsap) {
- net_warn_ratelimited("%s(), busy!\n", __func__);
- return -EBUSY;
- }
-
- /* Initialize callbacks to be used by the IrDA stack */
- irda_notify_init(&notify);
- notify.udata_indication = irda_data_indication;
- notify.instance = self;
- strncpy(notify.name, "Ultra", NOTIFY_MAX_NAME);
-
- self->lsap = irlmp_open_lsap(LSAP_CONNLESS, &notify, pid);
- if (self->lsap == NULL) {
- pr_debug("%s(), Unable to allocate LSAP!\n", __func__);
- return -ENOMEM;
- }
-
- return 0;
-}
-#endif /* CONFIG_IRDA_ULTRA */
-
-/*
- * Function irda_find_lsap_sel (self, name)
- *
- * Try to lookup LSAP selector in remote LM-IAS
- *
- * Basically, we start a IAP query, and then go to sleep. When the query
- * return, irda_getvalue_confirm will wake us up, and we can examine the
- * result of the query...
- * Note that in some case, the query fail even before we go to sleep,
- * creating some races...
- */
-static int irda_find_lsap_sel(struct irda_sock *self, char *name)
-{
- pr_debug("%s(%p, %s)\n", __func__, self, name);
-
- if (self->iriap) {
- net_warn_ratelimited("%s(): busy with a previous query\n",
- __func__);
- return -EBUSY;
- }
-
- self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
- irda_getvalue_confirm);
- if(self->iriap == NULL)
- return -ENOMEM;
-
- /* Treat unexpected wakeup as disconnect */
- self->errno = -EHOSTUNREACH;
-
- /* Query remote LM-IAS */
- iriap_getvaluebyclass_request(self->iriap, self->saddr, self->daddr,
- name, "IrDA:TinyTP:LsapSel");
-
- /* Wait for answer, if not yet finished (or failed) */
- if (wait_event_interruptible(self->query_wait, (self->iriap==NULL)))
- /* Treat signals as disconnect */
- return -EHOSTUNREACH;
-
- /* Check what happened */
- if (self->errno)
- {
- /* Requested object/attribute doesn't exist */
- if((self->errno == IAS_CLASS_UNKNOWN) ||
- (self->errno == IAS_ATTRIB_UNKNOWN))
- return -EADDRNOTAVAIL;
- else
- return -EHOSTUNREACH;
- }
-
- /* Get the remote TSAP selector */
- switch (self->ias_result->type) {
- case IAS_INTEGER:
- pr_debug("%s() int=%d\n",
- __func__, self->ias_result->t.integer);
-
- if (self->ias_result->t.integer != -1)
- self->dtsap_sel = self->ias_result->t.integer;
- else
- self->dtsap_sel = 0;
- break;
- default:
- self->dtsap_sel = 0;
- pr_debug("%s(), bad type!\n", __func__);
- break;
- }
- if (self->ias_result)
- irias_delete_value(self->ias_result);
-
- if (self->dtsap_sel)
- return 0;
-
- return -EADDRNOTAVAIL;
-}
-
-/*
- * Function irda_discover_daddr_and_lsap_sel (self, name)
- *
- * This try to find a device with the requested service.
- *
- * It basically look into the discovery log. For each address in the list,
- * it queries the LM-IAS of the device to find if this device offer
- * the requested service.
- * If there is more than one node supporting the service, we complain
- * to the user (it should move devices around).
- * The, we set both the destination address and the lsap selector to point
- * on the service on the unique device we have found.
- *
- * Note : this function fails if there is more than one device in range,
- * because IrLMP doesn't disconnect the LAP when the last LSAP is closed.
- * Moreover, we would need to wait the LAP disconnection...
- */
-static int irda_discover_daddr_and_lsap_sel(struct irda_sock *self, char *name)
-{
- discinfo_t *discoveries; /* Copy of the discovery log */
- int number; /* Number of nodes in the log */
- int i;
- int err = -ENETUNREACH;
- __u32 daddr = DEV_ADDR_ANY; /* Address we found the service on */
- __u8 dtsap_sel = 0x0; /* TSAP associated with it */
-
- pr_debug("%s(), name=%s\n", __func__, name);
-
- /* Ask lmp for the current discovery log
- * Note : we have to use irlmp_get_discoveries(), as opposed
- * to play with the cachelog directly, because while we are
- * making our ias query, le log might change... */
- discoveries = irlmp_get_discoveries(&number, self->mask.word,
- self->nslots);
- /* Check if the we got some results */
- if (discoveries == NULL)
- return -ENETUNREACH; /* No nodes discovered */
-
- /*
- * Now, check all discovered devices (if any), and connect
- * client only about the services that the client is
- * interested in...
- */
- for(i = 0; i < number; i++) {
- /* Try the address in the log */
- self->daddr = discoveries[i].daddr;
- self->saddr = 0x0;
- pr_debug("%s(), trying daddr = %08x\n",
- __func__, self->daddr);
-
- /* Query remote LM-IAS for this service */
- err = irda_find_lsap_sel(self, name);
- switch (err) {
- case 0:
- /* We found the requested service */
- if(daddr != DEV_ADDR_ANY) {
- pr_debug("%s(), discovered service ''%s'' in two different devices !!!\n",
- __func__, name);
- self->daddr = DEV_ADDR_ANY;
- kfree(discoveries);
- return -ENOTUNIQ;
- }
- /* First time we found that one, save it ! */
- daddr = self->daddr;
- dtsap_sel = self->dtsap_sel;
- break;
- case -EADDRNOTAVAIL:
- /* Requested service simply doesn't exist on this node */
- break;
- default:
- /* Something bad did happen :-( */
- pr_debug("%s(), unexpected IAS query failure\n",
- __func__);
- self->daddr = DEV_ADDR_ANY;
- kfree(discoveries);
- return -EHOSTUNREACH;
- }
- }
- /* Cleanup our copy of the discovery log */
- kfree(discoveries);
-
- /* Check out what we found */
- if(daddr == DEV_ADDR_ANY) {
- pr_debug("%s(), cannot discover service ''%s'' in any device !!!\n",
- __func__, name);
- self->daddr = DEV_ADDR_ANY;
- return -EADDRNOTAVAIL;
- }
-
- /* Revert back to discovered device & service */
- self->daddr = daddr;
- self->saddr = 0x0;
- self->dtsap_sel = dtsap_sel;
-
- pr_debug("%s(), discovered requested service ''%s'' at address %08x\n",
- __func__, name, self->daddr);
-
- return 0;
-}
-
-/*
- * Function irda_getname (sock, uaddr, uaddr_len, peer)
- *
- * Return the our own, or peers socket address (sockaddr_irda)
- *
- */
-static int irda_getname(struct socket *sock, struct sockaddr *uaddr,
- int *uaddr_len, int peer)
-{
- struct sockaddr_irda saddr;
- struct sock *sk = sock->sk;
- struct irda_sock *self = irda_sk(sk);
-
- memset(&saddr, 0, sizeof(saddr));
- if (peer) {
- if (sk->sk_state != TCP_ESTABLISHED)
- return -ENOTCONN;
-
- saddr.sir_family = AF_IRDA;
- saddr.sir_lsap_sel = self->dtsap_sel;
- saddr.sir_addr = self->daddr;
- } else {
- saddr.sir_family = AF_IRDA;
- saddr.sir_lsap_sel = self->stsap_sel;
- saddr.sir_addr = self->saddr;
- }
-
- pr_debug("%s(), tsap_sel = %#x\n", __func__, saddr.sir_lsap_sel);
- pr_debug("%s(), addr = %08x\n", __func__, saddr.sir_addr);
-
- /* uaddr_len come to us uninitialised */
- *uaddr_len = sizeof (struct sockaddr_irda);
- memcpy(uaddr, &saddr, *uaddr_len);
-
- return 0;
-}
-
-/*
- * Function irda_listen (sock, backlog)
- *
- * Just move to the listen state
- *
- */
-static int irda_listen(struct socket *sock, int backlog)
-{
- struct sock *sk = sock->sk;
- int err = -EOPNOTSUPP;
-
- lock_sock(sk);
-
- if ((sk->sk_type != SOCK_STREAM) && (sk->sk_type != SOCK_SEQPACKET) &&
- (sk->sk_type != SOCK_DGRAM))
- goto out;
-
- if (sk->sk_state != TCP_LISTEN) {
- sk->sk_max_ack_backlog = backlog;
- sk->sk_state = TCP_LISTEN;
-
- err = 0;
- }
-out:
- release_sock(sk);
-
- return err;
-}
-
-/*
- * Function irda_bind (sock, uaddr, addr_len)
- *
- * Used by servers to register their well known TSAP
- *
- */
-static int irda_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
-{
- struct sock *sk = sock->sk;
- struct sockaddr_irda *addr = (struct sockaddr_irda *) uaddr;
- struct irda_sock *self = irda_sk(sk);
- int err;
-
- pr_debug("%s(%p)\n", __func__, self);
-
- if (addr_len != sizeof(struct sockaddr_irda))
- return -EINVAL;
-
- lock_sock(sk);
-#ifdef CONFIG_IRDA_ULTRA
- /* Special care for Ultra sockets */
- if ((sk->sk_type == SOCK_DGRAM) &&
- (sk->sk_protocol == IRDAPROTO_ULTRA)) {
- self->pid = addr->sir_lsap_sel;
- err = -EOPNOTSUPP;
- if (self->pid & 0x80) {
- pr_debug("%s(), extension in PID not supp!\n",
- __func__);
- goto out;
- }
- err = irda_open_lsap(self, self->pid);
- if (err < 0)
- goto out;
-
- /* Pretend we are connected */
- sock->state = SS_CONNECTED;
- sk->sk_state = TCP_ESTABLISHED;
- err = 0;
-
- goto out;
- }
-#endif /* CONFIG_IRDA_ULTRA */
-
- self->ias_obj = irias_new_object(addr->sir_name, jiffies);
- err = -ENOMEM;
- if (self->ias_obj == NULL)
- goto out;
-
- err = irda_open_tsap(self, addr->sir_lsap_sel, addr->sir_name);
- if (err < 0) {
- irias_delete_object(self->ias_obj);
- self->ias_obj = NULL;
- goto out;
- }
-
- /* Register with LM-IAS */
- irias_add_integer_attrib(self->ias_obj, "IrDA:TinyTP:LsapSel",
- self->stsap_sel, IAS_KERNEL_ATTR);
- irias_insert_object(self->ias_obj);
-
- err = 0;
-out:
- release_sock(sk);
- return err;
-}
-
-/*
- * Function irda_accept (sock, newsock, flags)
- *
- * Wait for incoming connection
- *
- */
-static int irda_accept(struct socket *sock, struct socket *newsock, int flags,
- bool kern)
-{
- struct sock *sk = sock->sk;
- struct irda_sock *new, *self = irda_sk(sk);
- struct sock *newsk;
- struct sk_buff *skb = NULL;
- int err;
-
- err = irda_create(sock_net(sk), newsock, sk->sk_protocol, kern);
- if (err)
- return err;
-
- err = -EINVAL;
-
- lock_sock(sk);
- if (sock->state != SS_UNCONNECTED)
- goto out;
-
- err = -EOPNOTSUPP;
- if ((sk->sk_type != SOCK_STREAM) && (sk->sk_type != SOCK_SEQPACKET) &&
- (sk->sk_type != SOCK_DGRAM))
- goto out;
-
- err = -EINVAL;
- if (sk->sk_state != TCP_LISTEN)
- goto out;
-
- /*
- * The read queue this time is holding sockets ready to use
- * hooked into the SABM we saved
- */
-
- /*
- * We can perform the accept only if there is incoming data
- * on the listening socket.
- * So, we will block the caller until we receive any data.
- * If the caller was waiting on select() or poll() before
- * calling us, the data is waiting for us ;-)
- * Jean II
- */
- while (1) {
- skb = skb_dequeue(&sk->sk_receive_queue);
- if (skb)
- break;
-
- /* Non blocking operation */
- err = -EWOULDBLOCK;
- if (flags & O_NONBLOCK)
- goto out;
-
- err = wait_event_interruptible(*(sk_sleep(sk)),
- skb_peek(&sk->sk_receive_queue));
- if (err)
- goto out;
- }
-
- newsk = newsock->sk;
- err = -EIO;
- if (newsk == NULL)
- goto out;
-
- newsk->sk_state = TCP_ESTABLISHED;
-
- new = irda_sk(newsk);
-
- /* Now attach up the new socket */
- new->tsap = irttp_dup(self->tsap, new);
- err = -EPERM; /* value does not seem to make sense. -arnd */
- if (!new->tsap) {
- pr_debug("%s(), dup failed!\n", __func__);
- goto out;
- }
-
- new->stsap_sel = new->tsap->stsap_sel;
- new->dtsap_sel = new->tsap->dtsap_sel;
- new->saddr = irttp_get_saddr(new->tsap);
- new->daddr = irttp_get_daddr(new->tsap);
-
- new->max_sdu_size_tx = self->max_sdu_size_tx;
- new->max_sdu_size_rx = self->max_sdu_size_rx;
- new->max_data_size = self->max_data_size;
- new->max_header_size = self->max_header_size;
-
- memcpy(&new->qos_tx, &self->qos_tx, sizeof(struct qos_info));
-
- /* Clean up the original one to keep it in listen state */
- irttp_listen(self->tsap);
-
- sk->sk_ack_backlog--;
-
- newsock->state = SS_CONNECTED;
-
- irda_connect_response(new);
- err = 0;
-out:
- kfree_skb(skb);
- release_sock(sk);
- return err;
-}
-
-/*
- * Function irda_connect (sock, uaddr, addr_len, flags)
- *
- * Connect to a IrDA device
- *
- * The main difference with a "standard" connect is that with IrDA we need
- * to resolve the service name into a TSAP selector (in TCP, port number
- * doesn't have to be resolved).
- * Because of this service name resolution, we can offer "auto-connect",
- * where we connect to a service without specifying a destination address.
- *
- * Note : by consulting "errno", the user space caller may learn the cause
- * of the failure. Most of them are visible in the function, others may come
- * from subroutines called and are listed here :
- * o EBUSY : already processing a connect
- * o EHOSTUNREACH : bad addr->sir_addr argument
- * o EADDRNOTAVAIL : bad addr->sir_name argument
- * o ENOTUNIQ : more than one node has addr->sir_name (auto-connect)
- * o ENETUNREACH : no node found on the network (auto-connect)
- */
-static int irda_connect(struct socket *sock, struct sockaddr *uaddr,
- int addr_len, int flags)
-{
- struct sock *sk = sock->sk;
- struct sockaddr_irda *addr = (struct sockaddr_irda *) uaddr;
- struct irda_sock *self = irda_sk(sk);
- int err;
-
- pr_debug("%s(%p)\n", __func__, self);
-
- lock_sock(sk);
- /* Don't allow connect for Ultra sockets */
- err = -ESOCKTNOSUPPORT;
- if ((sk->sk_type == SOCK_DGRAM) && (sk->sk_protocol == IRDAPROTO_ULTRA))
- goto out;
-
- if (sk->sk_state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) {
- sock->state = SS_CONNECTED;
- err = 0;
- goto out; /* Connect completed during a ERESTARTSYS event */
- }
-
- if (sk->sk_state == TCP_CLOSE && sock->state == SS_CONNECTING) {
- sock->state = SS_UNCONNECTED;
- err = -ECONNREFUSED;
- goto out;
- }
-
- err = -EISCONN; /* No reconnect on a seqpacket socket */
- if (sk->sk_state == TCP_ESTABLISHED)
- goto out;
-
- sk->sk_state = TCP_CLOSE;
- sock->state = SS_UNCONNECTED;
-
- err = -EINVAL;
- if (addr_len != sizeof(struct sockaddr_irda))
- goto out;
-
- /* Check if user supplied any destination device address */
- if ((!addr->sir_addr) || (addr->sir_addr == DEV_ADDR_ANY)) {
- /* Try to find one suitable */
- err = irda_discover_daddr_and_lsap_sel(self, addr->sir_name);
- if (err) {
- pr_debug("%s(), auto-connect failed!\n", __func__);
- goto out;
- }
- } else {
- /* Use the one provided by the user */
- self->daddr = addr->sir_addr;
- pr_debug("%s(), daddr = %08x\n", __func__, self->daddr);
-
- /* If we don't have a valid service name, we assume the
- * user want to connect on a specific LSAP. Prevent
- * the use of invalid LSAPs (IrLMP 1.1 p10). Jean II */
- if((addr->sir_name[0] != '\0') ||
- (addr->sir_lsap_sel >= 0x70)) {
- /* Query remote LM-IAS using service name */
- err = irda_find_lsap_sel(self, addr->sir_name);
- if (err) {
- pr_debug("%s(), connect failed!\n", __func__);
- goto out;
- }
- } else {
- /* Directly connect to the remote LSAP
- * specified by the sir_lsap field.
- * Please use with caution, in IrDA LSAPs are
- * dynamic and there is no "well-known" LSAP. */
- self->dtsap_sel = addr->sir_lsap_sel;
- }
- }
-
- /* Check if we have opened a local TSAP */
- if (!self->tsap) {
- err = irda_open_tsap(self, LSAP_ANY, addr->sir_name);
- if (err)
- goto out;
- }
-
- /* Move to connecting socket, start sending Connect Requests */
- sock->state = SS_CONNECTING;
- sk->sk_state = TCP_SYN_SENT;
-
- /* Connect to remote device */
- err = irttp_connect_request(self->tsap, self->dtsap_sel,
- self->saddr, self->daddr, NULL,
- self->max_sdu_size_rx, NULL);
- if (err) {
- pr_debug("%s(), connect failed!\n", __func__);
- goto out;
- }
-
- /* Now the loop */
- err = -EINPROGRESS;
- if (sk->sk_state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
- goto out;
-
- err = -ERESTARTSYS;
- if (wait_event_interruptible(*(sk_sleep(sk)),
- (sk->sk_state != TCP_SYN_SENT)))
- goto out;
-
- if (sk->sk_state != TCP_ESTABLISHED) {
- sock->state = SS_UNCONNECTED;
- err = sock_error(sk);
- if (!err)
- err = -ECONNRESET;
- goto out;
- }
-
- sock->state = SS_CONNECTED;
-
- /* At this point, IrLMP has assigned our source address */
- self->saddr = irttp_get_saddr(self->tsap);
- err = 0;
-out:
- release_sock(sk);
- return err;
-}
-
-static struct proto irda_proto = {
- .name = "IRDA",
- .owner = THIS_MODULE,
- .obj_size = sizeof(struct irda_sock),
-};
-
-/*
- * Function irda_create (sock, protocol)
- *
- * Create IrDA socket
- *
- */
-static int irda_create(struct net *net, struct socket *sock, int protocol,
- int kern)
-{
- struct sock *sk;
- struct irda_sock *self;
-
- if (protocol < 0 || protocol > SK_PROTOCOL_MAX)
- return -EINVAL;
-
- if (net != &init_net)
- return -EAFNOSUPPORT;
-
- /* Check for valid socket type */
- switch (sock->type) {
- case SOCK_STREAM: /* For TTP connections with SAR disabled */
- case SOCK_SEQPACKET: /* For TTP connections with SAR enabled */
- case SOCK_DGRAM: /* For TTP Unitdata or LMP Ultra transfers */
- break;
- default:
- return -ESOCKTNOSUPPORT;
- }
-
- /* Allocate networking socket */
- sk = sk_alloc(net, PF_IRDA, GFP_KERNEL, &irda_proto, kern);
- if (sk == NULL)
- return -ENOMEM;
-
- self = irda_sk(sk);
- pr_debug("%s() : self is %p\n", __func__, self);
-
- init_waitqueue_head(&self->query_wait);
-
- switch (sock->type) {
- case SOCK_STREAM:
- sock->ops = &irda_stream_ops;
- self->max_sdu_size_rx = TTP_SAR_DISABLE;
- break;
- case SOCK_SEQPACKET:
- sock->ops = &irda_seqpacket_ops;
- self->max_sdu_size_rx = TTP_SAR_UNBOUND;
- break;
- case SOCK_DGRAM:
- switch (protocol) {
-#ifdef CONFIG_IRDA_ULTRA
- case IRDAPROTO_ULTRA:
- sock->ops = &irda_ultra_ops;
- /* Initialise now, because we may send on unbound
- * sockets. Jean II */
- self->max_data_size = ULTRA_MAX_DATA - LMP_PID_HEADER;
- self->max_header_size = IRDA_MAX_HEADER + LMP_PID_HEADER;
- break;
-#endif /* CONFIG_IRDA_ULTRA */
- case IRDAPROTO_UNITDATA:
- sock->ops = &irda_dgram_ops;
- /* We let Unitdata conn. be like seqpack conn. */
- self->max_sdu_size_rx = TTP_SAR_UNBOUND;
- break;
- default:
- sk_free(sk);
- return -ESOCKTNOSUPPORT;
- }
- break;
- default:
- sk_free(sk);
- return -ESOCKTNOSUPPORT;
- }
-
- /* Initialise networking socket struct */
- sock_init_data(sock, sk); /* Note : set sk->sk_refcnt to 1 */
- sk->sk_family = PF_IRDA;
- sk->sk_protocol = protocol;
-
- /* Register as a client with IrLMP */
- self->ckey = irlmp_register_client(0, NULL, NULL, NULL);
- self->mask.word = 0xffff;
- self->rx_flow = self->tx_flow = FLOW_START;
- self->nslots = DISCOVERY_DEFAULT_SLOTS;
- self->daddr = DEV_ADDR_ANY; /* Until we get connected */
- self->saddr = 0x0; /* so IrLMP assign us any link */
- return 0;
-}
-
-/*
- * Function irda_destroy_socket (self)
- *
- * Destroy socket
- *
- */
-static void irda_destroy_socket(struct irda_sock *self)
-{
- pr_debug("%s(%p)\n", __func__, self);
-
- /* Unregister with IrLMP */
- irlmp_unregister_client(self->ckey);
- irlmp_unregister_service(self->skey);
-
- /* Unregister with LM-IAS */
- if (self->ias_obj) {
- irias_delete_object(self->ias_obj);
- self->ias_obj = NULL;
- }
-
- if (self->iriap) {
- iriap_close(self->iriap);
- self->iriap = NULL;
- }
-
- if (self->tsap) {
- irttp_disconnect_request(self->tsap, NULL, P_NORMAL);
- irttp_close_tsap(self->tsap);
- self->tsap = NULL;
- }
-#ifdef CONFIG_IRDA_ULTRA
- if (self->lsap) {
- irlmp_close_lsap(self->lsap);
- self->lsap = NULL;
- }
-#endif /* CONFIG_IRDA_ULTRA */
-}
-
-/*
- * Function irda_release (sock)
- */
-static int irda_release(struct socket *sock)
-{
- struct sock *sk = sock->sk;
-
- if (sk == NULL)
- return 0;
-
- lock_sock(sk);
- sk->sk_state = TCP_CLOSE;
- sk->sk_shutdown |= SEND_SHUTDOWN;
- sk->sk_state_change(sk);
-
- /* Destroy IrDA socket */
- irda_destroy_socket(irda_sk(sk));
-
- sock_orphan(sk);
- sock->sk = NULL;
- release_sock(sk);
-
- /* Purge queues (see sock_init_data()) */
- skb_queue_purge(&sk->sk_receive_queue);
-
- /* Destroy networking socket if we are the last reference on it,
- * i.e. if(sk->sk_refcnt == 0) -> sk_free(sk) */
- sock_put(sk);
-
- /* Notes on socket locking and deallocation... - Jean II
- * In theory we should put pairs of sock_hold() / sock_put() to
- * prevent the socket to be destroyed whenever there is an
- * outstanding request or outstanding incoming packet or event.
- *
- * 1) This may include IAS request, both in connect and getsockopt.
- * Unfortunately, the situation is a bit more messy than it looks,
- * because we close iriap and kfree(self) above.
- *
- * 2) This may include selective discovery in getsockopt.
- * Same stuff as above, irlmp registration and self are gone.
- *
- * Probably 1 and 2 may not matter, because it's all triggered
- * by a process and the socket layer already prevent the
- * socket to go away while a process is holding it, through
- * sockfd_put() and fput()...
- *
- * 3) This may include deferred TSAP closure. In particular,
- * we may receive a late irda_disconnect_indication()
- * Fortunately, (tsap_cb *)->close_pend should protect us
- * from that.
- *
- * I did some testing on SMP, and it looks solid. And the socket
- * memory leak is now gone... - Jean II
- */
-
- return 0;
-}
-
-/*
- * Function irda_sendmsg (sock, msg, len)
- *
- * Send message down to TinyTP. This function is used for both STREAM and
- * SEQPACK services. This is possible since it forces the client to
- * fragment the message if necessary
- */
-static int irda_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
-{
- struct sock *sk = sock->sk;
- struct irda_sock *self;
- struct sk_buff *skb;
- int err = -EPIPE;
-
- pr_debug("%s(), len=%zd\n", __func__, len);
-
- /* Note : socket.c set MSG_EOR on SEQPACKET sockets */
- if (msg->msg_flags & ~(MSG_DONTWAIT | MSG_EOR | MSG_CMSG_COMPAT |
- MSG_NOSIGNAL)) {
- return -EINVAL;
- }
-
- lock_sock(sk);
-
- if (sk->sk_shutdown & SEND_SHUTDOWN)
- goto out_err;
-
- if (sk->sk_state != TCP_ESTABLISHED) {
- err = -ENOTCONN;
- goto out;
- }
-
- self = irda_sk(sk);
-
- /* Check if IrTTP is wants us to slow down */
-
- if (wait_event_interruptible(*(sk_sleep(sk)),
- (self->tx_flow != FLOW_STOP || sk->sk_state != TCP_ESTABLISHED))) {
- err = -ERESTARTSYS;
- goto out;
- }
-
- /* Check if we are still connected */
- if (sk->sk_state != TCP_ESTABLISHED) {
- err = -ENOTCONN;
- goto out;
- }
-
- /* Check that we don't send out too big frames */
- if (len > self->max_data_size) {
- pr_debug("%s(), Chopping frame from %zd to %d bytes!\n",
- __func__, len, self->max_data_size);
- len = self->max_data_size;
- }
-
- skb = sock_alloc_send_skb(sk, len + self->max_header_size + 16,
- msg->msg_flags & MSG_DONTWAIT, &err);
- if (!skb)
- goto out_err;
-
- skb_reserve(skb, self->max_header_size + 16);
- skb_reset_transport_header(skb);
- skb_put(skb, len);
- err = memcpy_from_msg(skb_transport_header(skb), msg, len);
- if (err) {
- kfree_skb(skb);
- goto out_err;
- }
-
- /*
- * Just send the message to TinyTP, and let it deal with possible
- * errors. No need to duplicate all that here
- */
- err = irttp_data_request(self->tsap, skb);
- if (err) {
- pr_debug("%s(), err=%d\n", __func__, err);
- goto out_err;
- }
-
- release_sock(sk);
- /* Tell client how much data we actually sent */
- return len;
-
-out_err:
- err = sk_stream_error(sk, msg->msg_flags, err);
-out:
- release_sock(sk);
- return err;
-
-}
-
-/*
- * Function irda_recvmsg_dgram (sock, msg, size, flags)
- *
- * Try to receive message and copy it to user. The frame is discarded
- * after being read, regardless of how much the user actually read
- */
-static int irda_recvmsg_dgram(struct socket *sock, struct msghdr *msg,
- size_t size, int flags)
-{
- struct sock *sk = sock->sk;
- struct irda_sock *self = irda_sk(sk);
- struct sk_buff *skb;
- size_t copied;
- int err;
-
- skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
- flags & MSG_DONTWAIT, &err);
- if (!skb)
- return err;
-
- skb_reset_transport_header(skb);
- copied = skb->len;
-
- if (copied > size) {
- pr_debug("%s(), Received truncated frame (%zd < %zd)!\n",
- __func__, copied, size);
- copied = size;
- msg->msg_flags |= MSG_TRUNC;
- }
- skb_copy_datagram_msg(skb, 0, msg, copied);
-
- skb_free_datagram(sk, skb);
-
- /*
- * Check if we have previously stopped IrTTP and we know
- * have more free space in our rx_queue. If so tell IrTTP
- * to start delivering frames again before our rx_queue gets
- * empty
- */
- if (self->rx_flow == FLOW_STOP) {
- if ((atomic_read(&sk->sk_rmem_alloc) << 2) <= sk->sk_rcvbuf) {
- pr_debug("%s(), Starting IrTTP\n", __func__);
- self->rx_flow = FLOW_START;
- irttp_flow_request(self->tsap, FLOW_START);
- }
- }
-
- return copied;
-}
-
-/*
- * Function irda_recvmsg_stream (sock, msg, size, flags)
- */
-static int irda_recvmsg_stream(struct socket *sock, struct msghdr *msg,
- size_t size, int flags)
-{
- struct sock *sk = sock->sk;
- struct irda_sock *self = irda_sk(sk);
- int noblock = flags & MSG_DONTWAIT;
- size_t copied = 0;
- int target, err;
- long timeo;
-
- if ((err = sock_error(sk)) < 0)
- return err;
-
- if (sock->flags & __SO_ACCEPTCON)
- return -EINVAL;
-
- err =-EOPNOTSUPP;
- if (flags & MSG_OOB)
- return -EOPNOTSUPP;
-
- err = 0;
- target = sock_rcvlowat(sk, flags & MSG_WAITALL, size);
- timeo = sock_rcvtimeo(sk, noblock);
-
- do {
- int chunk;
- struct sk_buff *skb = skb_dequeue(&sk->sk_receive_queue);
-
- if (skb == NULL) {
- DEFINE_WAIT(wait);
- err = 0;
-
- if (copied >= target)
- break;
-
- prepare_to_wait_exclusive(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
-
- /*
- * POSIX 1003.1g mandates this order.
- */
- err = sock_error(sk);
- if (err)
- ;
- else if (sk->sk_shutdown & RCV_SHUTDOWN)
- ;
- else if (noblock)
- err = -EAGAIN;
- else if (signal_pending(current))
- err = sock_intr_errno(timeo);
- else if (sk->sk_state != TCP_ESTABLISHED)
- err = -ENOTCONN;
- else if (skb_peek(&sk->sk_receive_queue) == NULL)
- /* Wait process until data arrives */
- schedule();
-
- finish_wait(sk_sleep(sk), &wait);
-
- if (err)
- return err;
- if (sk->sk_shutdown & RCV_SHUTDOWN)
- break;
-
- continue;
- }
-
- chunk = min_t(unsigned int, skb->len, size);
- if (memcpy_to_msg(msg, skb->data, chunk)) {
- skb_queue_head(&sk->sk_receive_queue, skb);
- if (copied == 0)
- copied = -EFAULT;
- break;
- }
- copied += chunk;
- size -= chunk;
-
- /* Mark read part of skb as used */
- if (!(flags & MSG_PEEK)) {
- skb_pull(skb, chunk);
-
- /* put the skb back if we didn't use it up.. */
- if (skb->len) {
- pr_debug("%s(), back on q!\n",
- __func__);
- skb_queue_head(&sk->sk_receive_queue, skb);
- break;
- }
-
- kfree_skb(skb);
- } else {
- pr_debug("%s() questionable!?\n", __func__);
-
- /* put message back and return */
- skb_queue_head(&sk->sk_receive_queue, skb);
- break;
- }
- } while (size);
-
- /*
- * Check if we have previously stopped IrTTP and we know
- * have more free space in our rx_queue. If so tell IrTTP
- * to start delivering frames again before our rx_queue gets
- * empty
- */
- if (self->rx_flow == FLOW_STOP) {
- if ((atomic_read(&sk->sk_rmem_alloc) << 2) <= sk->sk_rcvbuf) {
- pr_debug("%s(), Starting IrTTP\n", __func__);
- self->rx_flow = FLOW_START;
- irttp_flow_request(self->tsap, FLOW_START);
- }
- }
-
- return copied;
-}
-
-/*
- * Function irda_sendmsg_dgram (sock, msg, len)
- *
- * Send message down to TinyTP for the unreliable sequenced
- * packet service...
- *
- */
-static int irda_sendmsg_dgram(struct socket *sock, struct msghdr *msg,
- size_t len)
-{
- struct sock *sk = sock->sk;
- struct irda_sock *self;
- struct sk_buff *skb;
- int err;
-
- pr_debug("%s(), len=%zd\n", __func__, len);
-
- if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT))
- return -EINVAL;
-
- lock_sock(sk);
-
- if (sk->sk_shutdown & SEND_SHUTDOWN) {
- send_sig(SIGPIPE, current, 0);
- err = -EPIPE;
- goto out;
- }
-
- err = -ENOTCONN;
- if (sk->sk_state != TCP_ESTABLISHED)
- goto out;
-
- self = irda_sk(sk);
-
- /*
- * Check that we don't send out too big frames. This is an unreliable
- * service, so we have no fragmentation and no coalescence
- */
- if (len > self->max_data_size) {
- pr_debug("%s(), Warning too much data! Chopping frame from %zd to %d bytes!\n",
- __func__, len, self->max_data_size);
- len = self->max_data_size;
- }
-
- skb = sock_alloc_send_skb(sk, len + self->max_header_size,
- msg->msg_flags & MSG_DONTWAIT, &err);
- err = -ENOBUFS;
- if (!skb)
- goto out;
-
- skb_reserve(skb, self->max_header_size);
- skb_reset_transport_header(skb);
-
- pr_debug("%s(), appending user data\n", __func__);
- skb_put(skb, len);
- err = memcpy_from_msg(skb_transport_header(skb), msg, len);
- if (err) {
- kfree_skb(skb);
- goto out;
- }
-
- /*
- * Just send the message to TinyTP, and let it deal with possible
- * errors. No need to duplicate all that here
- */
- err = irttp_udata_request(self->tsap, skb);
- if (err) {
- pr_debug("%s(), err=%d\n", __func__, err);
- goto out;
- }
-
- release_sock(sk);
- return len;
-
-out:
- release_sock(sk);
- return err;
-}
-
-/*
- * Function irda_sendmsg_ultra (sock, msg, len)
- *
- * Send message down to IrLMP for the unreliable Ultra
- * packet service...
- */
-#ifdef CONFIG_IRDA_ULTRA
-static int irda_sendmsg_ultra(struct socket *sock, struct msghdr *msg,
- size_t len)
-{
- struct sock *sk = sock->sk;
- struct irda_sock *self;
- __u8 pid = 0;
- int bound = 0;
- struct sk_buff *skb;
- int err;
-
- pr_debug("%s(), len=%zd\n", __func__, len);
-
- err = -EINVAL;
- if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT))
- return -EINVAL;
-
- lock_sock(sk);
-
- err = -EPIPE;
- if (sk->sk_shutdown & SEND_SHUTDOWN) {
- send_sig(SIGPIPE, current, 0);
- goto out;
- }
-
- self = irda_sk(sk);
-
- /* Check if an address was specified with sendto. Jean II */
- if (msg->msg_name) {
- DECLARE_SOCKADDR(struct sockaddr_irda *, addr, msg->msg_name);
- err = -EINVAL;
- /* Check address, extract pid. Jean II */
- if (msg->msg_namelen < sizeof(*addr))
- goto out;
- if (addr->sir_family != AF_IRDA)
- goto out;
-
- pid = addr->sir_lsap_sel;
- if (pid & 0x80) {
- pr_debug("%s(), extension in PID not supp!\n",
- __func__);
- err = -EOPNOTSUPP;
- goto out;
- }
- } else {
- /* Check that the socket is properly bound to an Ultra
- * port. Jean II */
- if ((self->lsap == NULL) ||
- (sk->sk_state != TCP_ESTABLISHED)) {
- pr_debug("%s(), socket not bound to Ultra PID.\n",
- __func__);
- err = -ENOTCONN;
- goto out;
- }
- /* Use PID from socket */
- bound = 1;
- }
-
- /*
- * Check that we don't send out too big frames. This is an unreliable
- * service, so we have no fragmentation and no coalescence
- */
- if (len > self->max_data_size) {
- pr_debug("%s(), Warning too much data! Chopping frame from %zd to %d bytes!\n",
- __func__, len, self->max_data_size);
- len = self->max_data_size;
- }
-
- skb = sock_alloc_send_skb(sk, len + self->max_header_size,
- msg->msg_flags & MSG_DONTWAIT, &err);
- err = -ENOBUFS;
- if (!skb)
- goto out;
-
- skb_reserve(skb, self->max_header_size);
- skb_reset_transport_header(skb);
-
- pr_debug("%s(), appending user data\n", __func__);
- skb_put(skb, len);
- err = memcpy_from_msg(skb_transport_header(skb), msg, len);
- if (err) {
- kfree_skb(skb);
- goto out;
- }
-
- err = irlmp_connless_data_request((bound ? self->lsap : NULL),
- skb, pid);
- if (err)
- pr_debug("%s(), err=%d\n", __func__, err);
-out:
- release_sock(sk);
- return err ? : len;
-}
-#endif /* CONFIG_IRDA_ULTRA */
-
-/*
- * Function irda_shutdown (sk, how)
- */
-static int irda_shutdown(struct socket *sock, int how)
-{
- struct sock *sk = sock->sk;
- struct irda_sock *self = irda_sk(sk);
-
- pr_debug("%s(%p)\n", __func__, self);
-
- lock_sock(sk);
-
- sk->sk_state = TCP_CLOSE;
- sk->sk_shutdown |= SEND_SHUTDOWN;
- sk->sk_state_change(sk);
-
- if (self->iriap) {
- iriap_close(self->iriap);
- self->iriap = NULL;
- }
-
- if (self->tsap) {
- irttp_disconnect_request(self->tsap, NULL, P_NORMAL);
- irttp_close_tsap(self->tsap);
- self->tsap = NULL;
- }
-
- /* A few cleanup so the socket look as good as new... */
- self->rx_flow = self->tx_flow = FLOW_START; /* needed ??? */
- self->daddr = DEV_ADDR_ANY; /* Until we get re-connected */
- self->saddr = 0x0; /* so IrLMP assign us any link */
-
- release_sock(sk);
-
- return 0;
-}
-
-/*
- * Function irda_poll (file, sock, wait)
- */
-static __poll_t irda_poll(struct file * file, struct socket *sock,
- poll_table *wait)
-{
- struct sock *sk = sock->sk;
- struct irda_sock *self = irda_sk(sk);
- __poll_t mask;
-
- poll_wait(file, sk_sleep(sk), wait);
- mask = 0;
-
- /* Exceptional events? */
- if (sk->sk_err)
- mask |= EPOLLERR;
- if (sk->sk_shutdown & RCV_SHUTDOWN) {
- pr_debug("%s(), POLLHUP\n", __func__);
- mask |= EPOLLHUP;
- }
-
- /* Readable? */
- if (!skb_queue_empty(&sk->sk_receive_queue)) {
- pr_debug("Socket is readable\n");
- mask |= EPOLLIN | EPOLLRDNORM;
- }
-
- /* Connection-based need to check for termination and startup */
- switch (sk->sk_type) {
- case SOCK_STREAM:
- if (sk->sk_state == TCP_CLOSE) {
- pr_debug("%s(), POLLHUP\n", __func__);
- mask |= EPOLLHUP;
- }
-
- if (sk->sk_state == TCP_ESTABLISHED) {
- if ((self->tx_flow == FLOW_START) &&
- sock_writeable(sk))
- {
- mask |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND;
- }
- }
- break;
- case SOCK_SEQPACKET:
- if ((self->tx_flow == FLOW_START) &&
- sock_writeable(sk))
- {
- mask |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND;
- }
- break;
- case SOCK_DGRAM:
- if (sock_writeable(sk))
- mask |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND;
- break;
- default:
- break;
- }
-
- return mask;
-}
-
-/*
- * Function irda_ioctl (sock, cmd, arg)
- */
-static int irda_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
-{
- struct sock *sk = sock->sk;
- int err;
-
- pr_debug("%s(), cmd=%#x\n", __func__, cmd);
-
- err = -EINVAL;
- switch (cmd) {
- case TIOCOUTQ: {
- long amount;
-
- amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk);
- if (amount < 0)
- amount = 0;
- err = put_user(amount, (unsigned int __user *)arg);
- break;
- }
-
- case TIOCINQ: {
- struct sk_buff *skb;
- long amount = 0L;
- /* These two are safe on a single CPU system as only user tasks fiddle here */
- if ((skb = skb_peek(&sk->sk_receive_queue)) != NULL)
- amount = skb->len;
- err = put_user(amount, (unsigned int __user *)arg);
- break;
- }
-
- case SIOCGSTAMP:
- if (sk != NULL)
- err = sock_get_timestamp(sk, (struct timeval __user *)arg);
- break;
-
- case SIOCGIFADDR:
- case SIOCSIFADDR:
- case SIOCGIFDSTADDR:
- case SIOCSIFDSTADDR:
- case SIOCGIFBRDADDR:
- case SIOCSIFBRDADDR:
- case SIOCGIFNETMASK:
- case SIOCSIFNETMASK:
- case SIOCGIFMETRIC:
- case SIOCSIFMETRIC:
- break;
- default:
- pr_debug("%s(), doing device ioctl!\n", __func__);
- err = -ENOIOCTLCMD;
- }
-
- return err;
-}
-
-#ifdef CONFIG_COMPAT
-/*
- * Function irda_ioctl (sock, cmd, arg)
- */
-static int irda_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
-{
- /*
- * All IRDA's ioctl are standard ones.
- */
- return -ENOIOCTLCMD;
-}
-#endif
-
-/*
- * Function irda_setsockopt (sock, level, optname, optval, optlen)
- *
- * Set some options for the socket
- *
- */
-static int irda_setsockopt(struct socket *sock, int level, int optname,
- char __user *optval, unsigned int optlen)
-{
- struct sock *sk = sock->sk;
- struct irda_sock *self = irda_sk(sk);
- struct irda_ias_set *ias_opt;
- struct ias_object *ias_obj;
- struct ias_attrib * ias_attr; /* Attribute in IAS object */
- int opt, free_ias = 0, err = 0;
-
- pr_debug("%s(%p)\n", __func__, self);
-
- if (level != SOL_IRLMP)
- return -ENOPROTOOPT;
-
- lock_sock(sk);
-
- switch (optname) {
- case IRLMP_IAS_SET:
- /* The user want to add an attribute to an existing IAS object
- * (in the IAS database) or to create a new object with this
- * attribute.
- * We first query IAS to know if the object exist, and then
- * create the right attribute...
- */
-
- if (optlen != sizeof(struct irda_ias_set)) {
- err = -EINVAL;
- goto out;
- }
-
- /* Copy query to the driver. */
- ias_opt = memdup_user(optval, optlen);
- if (IS_ERR(ias_opt)) {
- err = PTR_ERR(ias_opt);
- goto out;
- }
-
- /* Find the object we target.
- * If the user gives us an empty string, we use the object
- * associated with this socket. This will workaround
- * duplicated class name - Jean II */
- if(ias_opt->irda_class_name[0] == '\0') {
- if(self->ias_obj == NULL) {
- kfree(ias_opt);
- err = -EINVAL;
- goto out;
- }
- ias_obj = self->ias_obj;
- } else
- ias_obj = irias_find_object(ias_opt->irda_class_name);
-
- /* Only ROOT can mess with the global IAS database.
- * Users can only add attributes to the object associated
- * with the socket they own - Jean II */
- if((!capable(CAP_NET_ADMIN)) &&
- ((ias_obj == NULL) || (ias_obj != self->ias_obj))) {
- kfree(ias_opt);
- err = -EPERM;
- goto out;
- }
-
- /* If the object doesn't exist, create it */
- if(ias_obj == (struct ias_object *) NULL) {
- /* Create a new object */
- ias_obj = irias_new_object(ias_opt->irda_class_name,
- jiffies);
- if (ias_obj == NULL) {
- kfree(ias_opt);
- err = -ENOMEM;
- goto out;
- }
- free_ias = 1;
- }
-
- /* Do we have the attribute already ? */
- if(irias_find_attrib(ias_obj, ias_opt->irda_attrib_name)) {
- kfree(ias_opt);
- if (free_ias) {
- kfree(ias_obj->name);
- kfree(ias_obj);
- }
- err = -EINVAL;
- goto out;
- }
-
- /* Look at the type */
- switch(ias_opt->irda_attrib_type) {
- case IAS_INTEGER:
- /* Add an integer attribute */
- irias_add_integer_attrib(
- ias_obj,
- ias_opt->irda_attrib_name,
- ias_opt->attribute.irda_attrib_int,
- IAS_USER_ATTR);
- break;
- case IAS_OCT_SEQ:
- /* Check length */
- if(ias_opt->attribute.irda_attrib_octet_seq.len >
- IAS_MAX_OCTET_STRING) {
- kfree(ias_opt);
- if (free_ias) {
- kfree(ias_obj->name);
- kfree(ias_obj);
- }
-
- err = -EINVAL;
- goto out;
- }
- /* Add an octet sequence attribute */
- irias_add_octseq_attrib(
- ias_obj,
- ias_opt->irda_attrib_name,
- ias_opt->attribute.irda_attrib_octet_seq.octet_seq,
- ias_opt->attribute.irda_attrib_octet_seq.len,
- IAS_USER_ATTR);
- break;
- case IAS_STRING:
- /* Should check charset & co */
- /* Check length */
- /* The length is encoded in a __u8, and
- * IAS_MAX_STRING == 256, so there is no way
- * userspace can pass us a string too large.
- * Jean II */
- /* NULL terminate the string (avoid troubles) */
- ias_opt->attribute.irda_attrib_string.string[ias_opt->attribute.irda_attrib_string.len] = '\0';
- /* Add a string attribute */
- irias_add_string_attrib(
- ias_obj,
- ias_opt->irda_attrib_name,
- ias_opt->attribute.irda_attrib_string.string,
- IAS_USER_ATTR);
- break;
- default :
- kfree(ias_opt);
- if (free_ias) {
- kfree(ias_obj->name);
- kfree(ias_obj);
- }
- err = -EINVAL;
- goto out;
- }
- irias_insert_object(ias_obj);
- kfree(ias_opt);
- break;
- case IRLMP_IAS_DEL:
- /* The user want to delete an object from our local IAS
- * database. We just need to query the IAS, check is the
- * object is not owned by the kernel and delete it.
- */
-
- if (optlen != sizeof(struct irda_ias_set)) {
- err = -EINVAL;
- goto out;
- }
-
- /* Copy query to the driver. */
- ias_opt = memdup_user(optval, optlen);
- if (IS_ERR(ias_opt)) {
- err = PTR_ERR(ias_opt);
- goto out;
- }
-
- /* Find the object we target.
- * If the user gives us an empty string, we use the object
- * associated with this socket. This will workaround
- * duplicated class name - Jean II */
- if(ias_opt->irda_class_name[0] == '\0')
- ias_obj = self->ias_obj;
- else
- ias_obj = irias_find_object(ias_opt->irda_class_name);
- if(ias_obj == (struct ias_object *) NULL) {
- kfree(ias_opt);
- err = -EINVAL;
- goto out;
- }
-
- /* Only ROOT can mess with the global IAS database.
- * Users can only del attributes from the object associated
- * with the socket they own - Jean II */
- if((!capable(CAP_NET_ADMIN)) &&
- ((ias_obj == NULL) || (ias_obj != self->ias_obj))) {
- kfree(ias_opt);
- err = -EPERM;
- goto out;
- }
-
- /* Find the attribute (in the object) we target */
- ias_attr = irias_find_attrib(ias_obj,
- ias_opt->irda_attrib_name);
- if(ias_attr == (struct ias_attrib *) NULL) {
- kfree(ias_opt);
- err = -EINVAL;
- goto out;
- }
-
- /* Check is the user space own the object */
- if(ias_attr->value->owner != IAS_USER_ATTR) {
- pr_debug("%s(), attempting to delete a kernel attribute\n",
- __func__);
- kfree(ias_opt);
- err = -EPERM;
- goto out;
- }
-
- /* Remove the attribute (and maybe the object) */
- irias_delete_attrib(ias_obj, ias_attr, 1);
- kfree(ias_opt);
- break;
- case IRLMP_MAX_SDU_SIZE:
- if (optlen < sizeof(int)) {
- err = -EINVAL;
- goto out;
- }
-
- if (get_user(opt, (int __user *)optval)) {
- err = -EFAULT;
- goto out;
- }
-
- /* Only possible for a seqpacket service (TTP with SAR) */
- if (sk->sk_type != SOCK_SEQPACKET) {
- pr_debug("%s(), setting max_sdu_size = %d\n",
- __func__, opt);
- self->max_sdu_size_rx = opt;
- } else {
- net_warn_ratelimited("%s: not allowed to set MAXSDUSIZE for this socket type!\n",
- __func__);
- err = -ENOPROTOOPT;
- goto out;
- }
- break;
- case IRLMP_HINTS_SET:
- if (optlen < sizeof(int)) {
- err = -EINVAL;
- goto out;
- }
-
- /* The input is really a (__u8 hints[2]), easier as an int */
- if (get_user(opt, (int __user *)optval)) {
- err = -EFAULT;
- goto out;
- }
-
- /* Unregister any old registration */
- irlmp_unregister_service(self->skey);
-
- self->skey = irlmp_register_service((__u16) opt);
- break;
- case IRLMP_HINT_MASK_SET:
- /* As opposed to the previous case which set the hint bits
- * that we advertise, this one set the filter we use when
- * making a discovery (nodes which don't match any hint
- * bit in the mask are not reported).
- */
- if (optlen < sizeof(int)) {
- err = -EINVAL;
- goto out;
- }
-
- /* The input is really a (__u8 hints[2]), easier as an int */
- if (get_user(opt, (int __user *)optval)) {
- err = -EFAULT;
- goto out;
- }
-
- /* Set the new hint mask */
- self->mask.word = (__u16) opt;
- /* Mask out extension bits */
- self->mask.word &= 0x7f7f;
- /* Check if no bits */
- if(!self->mask.word)
- self->mask.word = 0xFFFF;
-
- break;
- default:
- err = -ENOPROTOOPT;
- break;
- }
-
-out:
- release_sock(sk);
-
- return err;
-}
-
-/*
- * Function irda_extract_ias_value(ias_opt, ias_value)
- *
- * Translate internal IAS value structure to the user space representation
- *
- * The external representation of IAS values, as we exchange them with
- * user space program is quite different from the internal representation,
- * as stored in the IAS database (because we need a flat structure for
- * crossing kernel boundary).
- * This function transform the former in the latter. We also check
- * that the value type is valid.
- */
-static int irda_extract_ias_value(struct irda_ias_set *ias_opt,
- struct ias_value *ias_value)
-{
- /* Look at the type */
- switch (ias_value->type) {
- case IAS_INTEGER:
- /* Copy the integer */
- ias_opt->attribute.irda_attrib_int = ias_value->t.integer;
- break;
- case IAS_OCT_SEQ:
- /* Set length */
- ias_opt->attribute.irda_attrib_octet_seq.len = ias_value->len;
- /* Copy over */
- memcpy(ias_opt->attribute.irda_attrib_octet_seq.octet_seq,
- ias_value->t.oct_seq, ias_value->len);
- break;
- case IAS_STRING:
- /* Set length */
- ias_opt->attribute.irda_attrib_string.len = ias_value->len;
- ias_opt->attribute.irda_attrib_string.charset = ias_value->charset;
- /* Copy over */
- memcpy(ias_opt->attribute.irda_attrib_string.string,
- ias_value->t.string, ias_value->len);
- /* NULL terminate the string (avoid troubles) */
- ias_opt->attribute.irda_attrib_string.string[ias_value->len] = '\0';
- break;
- case IAS_MISSING:
- default :
- return -EINVAL;
- }
-
- /* Copy type over */
- ias_opt->irda_attrib_type = ias_value->type;
-
- return 0;
-}
-
-/*
- * Function irda_getsockopt (sock, level, optname, optval, optlen)
- */
-static int irda_getsockopt(struct socket *sock, int level, int optname,
- char __user *optval, int __user *optlen)
-{
- struct sock *sk = sock->sk;
- struct irda_sock *self = irda_sk(sk);
- struct irda_device_list list = { 0 };
- struct irda_device_info *discoveries;
- struct irda_ias_set * ias_opt; /* IAS get/query params */
- struct ias_object * ias_obj; /* Object in IAS */
- struct ias_attrib * ias_attr; /* Attribute in IAS object */
- int daddr = DEV_ADDR_ANY; /* Dest address for IAS queries */
- int val = 0;
- int len = 0;
- int err = 0;
- int offset, total;
-
- pr_debug("%s(%p)\n", __func__, self);
-
- if (level != SOL_IRLMP)
- return -ENOPROTOOPT;
-
- if (get_user(len, optlen))
- return -EFAULT;
-
- if(len < 0)
- return -EINVAL;
-
- lock_sock(sk);
-
- switch (optname) {
- case IRLMP_ENUMDEVICES:
-
- /* Offset to first device entry */
- offset = sizeof(struct irda_device_list) -
- sizeof(struct irda_device_info);
-
- if (len < offset) {
- err = -EINVAL;
- goto out;
- }
-
- /* Ask lmp for the current discovery log */
- discoveries = irlmp_get_discoveries(&list.len, self->mask.word,
- self->nslots);
- /* Check if the we got some results */
- if (discoveries == NULL) {
- err = -EAGAIN;
- goto out; /* Didn't find any devices */
- }
-
- /* Write total list length back to client */
- if (copy_to_user(optval, &list, offset))
- err = -EFAULT;
-
- /* Copy the list itself - watch for overflow */
- if (list.len > 2048) {
- err = -EINVAL;
- goto bed;
- }
- total = offset + (list.len * sizeof(struct irda_device_info));
- if (total > len)
- total = len;
- if (copy_to_user(optval+offset, discoveries, total - offset))
- err = -EFAULT;
-
- /* Write total number of bytes used back to client */
- if (put_user(total, optlen))
- err = -EFAULT;
-bed:
- /* Free up our buffer */
- kfree(discoveries);
- break;
- case IRLMP_MAX_SDU_SIZE:
- val = self->max_data_size;
- len = sizeof(int);
- if (put_user(len, optlen)) {
- err = -EFAULT;
- goto out;
- }
-
- if (copy_to_user(optval, &val, len)) {
- err = -EFAULT;
- goto out;
- }
-
- break;
- case IRLMP_IAS_GET:
- /* The user want an object from our local IAS database.
- * We just need to query the IAS and return the value
- * that we found */
-
- /* Check that the user has allocated the right space for us */
- if (len != sizeof(struct irda_ias_set)) {
- err = -EINVAL;
- goto out;
- }
-
- /* Copy query to the driver. */
- ias_opt = memdup_user(optval, len);
- if (IS_ERR(ias_opt)) {
- err = PTR_ERR(ias_opt);
- goto out;
- }
-
- /* Find the object we target.
- * If the user gives us an empty string, we use the object
- * associated with this socket. This will workaround
- * duplicated class name - Jean II */
- if(ias_opt->irda_class_name[0] == '\0')
- ias_obj = self->ias_obj;
- else
- ias_obj = irias_find_object(ias_opt->irda_class_name);
- if(ias_obj == (struct ias_object *) NULL) {
- kfree(ias_opt);
- err = -EINVAL;
- goto out;
- }
-
- /* Find the attribute (in the object) we target */
- ias_attr = irias_find_attrib(ias_obj,
- ias_opt->irda_attrib_name);
- if(ias_attr == (struct ias_attrib *) NULL) {
- kfree(ias_opt);
- err = -EINVAL;
- goto out;
- }
-
- /* Translate from internal to user structure */
- err = irda_extract_ias_value(ias_opt, ias_attr->value);
- if(err) {
- kfree(ias_opt);
- goto out;
- }
-
- /* Copy reply to the user */
- if (copy_to_user(optval, ias_opt,
- sizeof(struct irda_ias_set))) {
- kfree(ias_opt);
- err = -EFAULT;
- goto out;
- }
- /* Note : don't need to put optlen, we checked it */
- kfree(ias_opt);
- break;
- case IRLMP_IAS_QUERY:
- /* The user want an object from a remote IAS database.
- * We need to use IAP to query the remote database and
- * then wait for the answer to come back. */
-
- /* Check that the user has allocated the right space for us */
- if (len != sizeof(struct irda_ias_set)) {
- err = -EINVAL;
- goto out;
- }
-
- /* Copy query to the driver. */
- ias_opt = memdup_user(optval, len);
- if (IS_ERR(ias_opt)) {
- err = PTR_ERR(ias_opt);
- goto out;
- }
-
- /* At this point, there are two cases...
- * 1) the socket is connected - that's the easy case, we
- * just query the device we are connected to...
- * 2) the socket is not connected - the user doesn't want
- * to connect and/or may not have a valid service name
- * (so can't create a fake connection). In this case,
- * we assume that the user pass us a valid destination
- * address in the requesting structure...
- */
- if(self->daddr != DEV_ADDR_ANY) {
- /* We are connected - reuse known daddr */
- daddr = self->daddr;
- } else {
- /* We are not connected, we must specify a valid
- * destination address */
- daddr = ias_opt->daddr;
- if((!daddr) || (daddr == DEV_ADDR_ANY)) {
- kfree(ias_opt);
- err = -EINVAL;
- goto out;
- }
- }
-
- /* Check that we can proceed with IAP */
- if (self->iriap) {
- net_warn_ratelimited("%s: busy with a previous query\n",
- __func__);
- kfree(ias_opt);
- err = -EBUSY;
- goto out;
- }
-
- self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
- irda_getvalue_confirm);
-
- if (self->iriap == NULL) {
- kfree(ias_opt);
- err = -ENOMEM;
- goto out;
- }
-
- /* Treat unexpected wakeup as disconnect */
- self->errno = -EHOSTUNREACH;
-
- /* Query remote LM-IAS */
- iriap_getvaluebyclass_request(self->iriap,
- self->saddr, daddr,
- ias_opt->irda_class_name,
- ias_opt->irda_attrib_name);
-
- /* Wait for answer, if not yet finished (or failed) */
- if (wait_event_interruptible(self->query_wait,
- (self->iriap == NULL))) {
- /* pending request uses copy of ias_opt-content
- * we can free it regardless! */
- kfree(ias_opt);
- /* Treat signals as disconnect */
- err = -EHOSTUNREACH;
- goto out;
- }
-
- /* Check what happened */
- if (self->errno)
- {
- kfree(ias_opt);
- /* Requested object/attribute doesn't exist */
- if((self->errno == IAS_CLASS_UNKNOWN) ||
- (self->errno == IAS_ATTRIB_UNKNOWN))
- err = -EADDRNOTAVAIL;
- else
- err = -EHOSTUNREACH;
-
- goto out;
- }
-
- /* Translate from internal to user structure */
- err = irda_extract_ias_value(ias_opt, self->ias_result);
- if (self->ias_result)
- irias_delete_value(self->ias_result);
- if (err) {
- kfree(ias_opt);
- goto out;
- }
-
- /* Copy reply to the user */
- if (copy_to_user(optval, ias_opt,
- sizeof(struct irda_ias_set))) {
- kfree(ias_opt);
- err = -EFAULT;
- goto out;
- }
- /* Note : don't need to put optlen, we checked it */
- kfree(ias_opt);
- break;
- case IRLMP_WAITDEVICE:
- /* This function is just another way of seeing life ;-)
- * IRLMP_ENUMDEVICES assumes that you have a static network,
- * and that you just want to pick one of the devices present.
- * On the other hand, in here we assume that no device is
- * present and that at some point in the future a device will
- * come into range. When this device arrive, we just wake
- * up the caller, so that he has time to connect to it before
- * the device goes away...
- * Note : once the node has been discovered for more than a
- * few second, it won't trigger this function, unless it
- * goes away and come back changes its hint bits (so we
- * might call it IRLMP_WAITNEWDEVICE).
- */
-
- /* Check that the user is passing us an int */
- if (len != sizeof(int)) {
- err = -EINVAL;
- goto out;
- }
- /* Get timeout in ms (max time we block the caller) */
- if (get_user(val, (int __user *)optval)) {
- err = -EFAULT;
- goto out;
- }
-
- /* Tell IrLMP we want to be notified */
- irlmp_update_client(self->ckey, self->mask.word,
- irda_selective_discovery_indication,
- NULL, (void *) self);
-
- /* Do some discovery (and also return cached results) */
- irlmp_discovery_request(self->nslots);
-
- /* Wait until a node is discovered */
- if (!self->cachedaddr) {
- pr_debug("%s(), nothing discovered yet, going to sleep...\n",
- __func__);
-
- /* Set watchdog timer to expire in <val> ms. */
- self->errno = 0;
- timer_setup(&self->watchdog, irda_discovery_timeout, 0);
- mod_timer(&self->watchdog,
- jiffies + msecs_to_jiffies(val));
-
- /* Wait for IR-LMP to call us back */
- err = __wait_event_interruptible(self->query_wait,
- (self->cachedaddr != 0 || self->errno == -ETIME));
-
- /* If watchdog is still activated, kill it! */
- del_timer(&(self->watchdog));
-
- pr_debug("%s(), ...waking up !\n", __func__);
-
- if (err != 0)
- goto out;
- }
- else
- pr_debug("%s(), found immediately !\n",
- __func__);
-
- /* Tell IrLMP that we have been notified */
- irlmp_update_client(self->ckey, self->mask.word,
- NULL, NULL, NULL);
-
- /* Check if the we got some results */
- if (!self->cachedaddr) {
- err = -EAGAIN; /* Didn't find any devices */
- goto out;
- }
- daddr = self->cachedaddr;
- /* Cleanup */
- self->cachedaddr = 0;
-
- /* We return the daddr of the device that trigger the
- * wakeup. As irlmp pass us only the new devices, we
- * are sure that it's not an old device.
- * If the user want more details, he should query
- * the whole discovery log and pick one device...
- */
- if (put_user(daddr, (int __user *)optval)) {
- err = -EFAULT;
- goto out;
- }
-
- break;
- default:
- err = -ENOPROTOOPT;
- }
-
-out:
-
- release_sock(sk);
-
- return err;
-}
-
-static const struct net_proto_family irda_family_ops = {
- .family = PF_IRDA,
- .create = irda_create,
- .owner = THIS_MODULE,
-};
-
-static const struct proto_ops irda_stream_ops = {
- .family = PF_IRDA,
- .owner = THIS_MODULE,
- .release = irda_release,
- .bind = irda_bind,
- .connect = irda_connect,
- .socketpair = sock_no_socketpair,
- .accept = irda_accept,
- .getname = irda_getname,
- .poll = irda_poll,
- .ioctl = irda_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = irda_compat_ioctl,
-#endif
- .listen = irda_listen,
- .shutdown = irda_shutdown,
- .setsockopt = irda_setsockopt,
- .getsockopt = irda_getsockopt,
- .sendmsg = irda_sendmsg,
- .recvmsg = irda_recvmsg_stream,
- .mmap = sock_no_mmap,
- .sendpage = sock_no_sendpage,
-};
-
-static const struct proto_ops irda_seqpacket_ops = {
- .family = PF_IRDA,
- .owner = THIS_MODULE,
- .release = irda_release,
- .bind = irda_bind,
- .connect = irda_connect,
- .socketpair = sock_no_socketpair,
- .accept = irda_accept,
- .getname = irda_getname,
- .poll = datagram_poll,
- .ioctl = irda_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = irda_compat_ioctl,
-#endif
- .listen = irda_listen,
- .shutdown = irda_shutdown,
- .setsockopt = irda_setsockopt,
- .getsockopt = irda_getsockopt,
- .sendmsg = irda_sendmsg,
- .recvmsg = irda_recvmsg_dgram,
- .mmap = sock_no_mmap,
- .sendpage = sock_no_sendpage,
-};
-
-static const struct proto_ops irda_dgram_ops = {
- .family = PF_IRDA,
- .owner = THIS_MODULE,
- .release = irda_release,
- .bind = irda_bind,
- .connect = irda_connect,
- .socketpair = sock_no_socketpair,
- .accept = irda_accept,
- .getname = irda_getname,
- .poll = datagram_poll,
- .ioctl = irda_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = irda_compat_ioctl,
-#endif
- .listen = irda_listen,
- .shutdown = irda_shutdown,
- .setsockopt = irda_setsockopt,
- .getsockopt = irda_getsockopt,
- .sendmsg = irda_sendmsg_dgram,
- .recvmsg = irda_recvmsg_dgram,
- .mmap = sock_no_mmap,
- .sendpage = sock_no_sendpage,
-};
-
-#ifdef CONFIG_IRDA_ULTRA
-static const struct proto_ops irda_ultra_ops = {
- .family = PF_IRDA,
- .owner = THIS_MODULE,
- .release = irda_release,
- .bind = irda_bind,
- .connect = sock_no_connect,
- .socketpair = sock_no_socketpair,
- .accept = sock_no_accept,
- .getname = irda_getname,
- .poll = datagram_poll,
- .ioctl = irda_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = irda_compat_ioctl,
-#endif
- .listen = sock_no_listen,
- .shutdown = irda_shutdown,
- .setsockopt = irda_setsockopt,
- .getsockopt = irda_getsockopt,
- .sendmsg = irda_sendmsg_ultra,
- .recvmsg = irda_recvmsg_dgram,
- .mmap = sock_no_mmap,
- .sendpage = sock_no_sendpage,
-};
-#endif /* CONFIG_IRDA_ULTRA */
-
-/*
- * Function irsock_init (pro)
- *
- * Initialize IrDA protocol
- *
- */
-int __init irsock_init(void)
-{
- int rc = proto_register(&irda_proto, 0);
-
- if (rc == 0)
- rc = sock_register(&irda_family_ops);
-
- return rc;
-}
-
-/*
- * Function irsock_cleanup (void)
- *
- * Remove IrDA protocol
- *
- */
-void irsock_cleanup(void)
-{
- sock_unregister(PF_IRDA);
- proto_unregister(&irda_proto);
-}
diff --git a/drivers/staging/irda/net/discovery.c b/drivers/staging/irda/net/discovery.c
deleted file mode 100644
index 1e54954a4081..000000000000
--- a/drivers/staging/irda/net/discovery.c
+++ /dev/null
@@ -1,417 +0,0 @@
-/*********************************************************************
- *
- * Filename: discovery.c
- * Version: 0.1
- * Description: Routines for handling discoveries at the IrLMP layer
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Tue Apr 6 15:33:50 1999
- * Modified at: Sat Oct 9 17:11:31 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- * Modified at: Fri May 28 3:11 CST 1999
- * Modified by: Horst von Brand <vonbrand@sleipnir.valparaiso.cl>
- *
- * Copyright (c) 1999 Dag Brattli, All Rights Reserved.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/string.h>
-#include <linux/socket.h>
-#include <linux/fs.h>
-#include <linux/seq_file.h>
-#include <linux/slab.h>
-#include <linux/export.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irlmp.h>
-
-#include <net/irda/discovery.h>
-
-#include <asm/unaligned.h>
-
-/*
- * Function irlmp_add_discovery (cachelog, discovery)
- *
- * Add a new discovery to the cachelog, and remove any old discoveries
- * from the same device
- *
- * Note : we try to preserve the time this device was *first* discovered
- * (as opposed to the time of last discovery used for cleanup). This is
- * used by clients waiting for discovery events to tell if the device
- * discovered is "new" or just the same old one. They can't rely there
- * on a binary flag (new/old), because not all discovery events are
- * propagated to them, and they might not always listen, so they would
- * miss some new devices popping up...
- * Jean II
- */
-void irlmp_add_discovery(hashbin_t *cachelog, discovery_t *new)
-{
- discovery_t *discovery, *node;
- unsigned long flags;
-
- /* Set time of first discovery if node is new (see below) */
- new->firststamp = new->timestamp;
-
- spin_lock_irqsave(&cachelog->hb_spinlock, flags);
-
- /*
- * Remove all discoveries of devices that has previously been
- * discovered on the same link with the same name (info), or the
- * same daddr. We do this since some devices (mostly PDAs) change
- * their device address between every discovery.
- */
- discovery = (discovery_t *) hashbin_get_first(cachelog);
- while (discovery != NULL ) {
- node = discovery;
-
- /* Be sure to stay one item ahead */
- discovery = (discovery_t *) hashbin_get_next(cachelog);
-
- if ((node->data.saddr == new->data.saddr) &&
- ((node->data.daddr == new->data.daddr) ||
- (strcmp(node->data.info, new->data.info) == 0)))
- {
- /* This discovery is a previous discovery
- * from the same device, so just remove it
- */
- hashbin_remove_this(cachelog, (irda_queue_t *) node);
- /* Check if hints bits are unchanged */
- if (get_unaligned((__u16 *)node->data.hints) == get_unaligned((__u16 *)new->data.hints))
- /* Set time of first discovery for this node */
- new->firststamp = node->firststamp;
- kfree(node);
- }
- }
-
- /* Insert the new and updated version */
- hashbin_insert(cachelog, (irda_queue_t *) new, new->data.daddr, NULL);
-
- spin_unlock_irqrestore(&cachelog->hb_spinlock, flags);
-}
-
-/*
- * Function irlmp_add_discovery_log (cachelog, log)
- *
- * Merge a disovery log into the cachelog.
- *
- */
-void irlmp_add_discovery_log(hashbin_t *cachelog, hashbin_t *log)
-{
- discovery_t *discovery;
-
- /*
- * If log is missing this means that IrLAP was unable to perform the
- * discovery, so restart discovery again with just the half timeout
- * of the normal one.
- */
- /* Well... It means that there was nobody out there - Jean II */
- if (log == NULL) {
- /* irlmp_start_discovery_timer(irlmp, 150); */
- return;
- }
-
- /*
- * Locking : we are the only owner of this discovery log, so
- * no need to lock it.
- * We just need to lock the global log in irlmp_add_discovery().
- */
- discovery = (discovery_t *) hashbin_remove_first(log);
- while (discovery != NULL) {
- irlmp_add_discovery(cachelog, discovery);
-
- discovery = (discovery_t *) hashbin_remove_first(log);
- }
-
- /* Delete the now empty log */
- hashbin_delete(log, (FREE_FUNC) kfree);
-}
-
-/*
- * Function irlmp_expire_discoveries (log, saddr, force)
- *
- * Go through all discoveries and expire all that has stayed too long
- *
- * Note : this assume that IrLAP won't change its saddr, which
- * currently is a valid assumption...
- */
-void irlmp_expire_discoveries(hashbin_t *log, __u32 saddr, int force)
-{
- discovery_t * discovery;
- discovery_t * curr;
- unsigned long flags;
- discinfo_t * buffer = NULL;
- int n; /* Size of the full log */
- int i = 0; /* How many we expired */
-
- IRDA_ASSERT(log != NULL, return;);
- spin_lock_irqsave(&log->hb_spinlock, flags);
-
- discovery = (discovery_t *) hashbin_get_first(log);
- while (discovery != NULL) {
- /* Be sure to be one item ahead */
- curr = discovery;
- discovery = (discovery_t *) hashbin_get_next(log);
-
- /* Test if it's time to expire this discovery */
- if ((curr->data.saddr == saddr) &&
- (force ||
- ((jiffies - curr->timestamp) > DISCOVERY_EXPIRE_TIMEOUT)))
- {
- /* Create buffer as needed.
- * As this function get called a lot and most time
- * we don't have anything to put in the log (we are
- * quite picky), we can save a lot of overhead
- * by not calling kmalloc. Jean II */
- if(buffer == NULL) {
- /* Create the client specific buffer */
- n = HASHBIN_GET_SIZE(log);
- buffer = kmalloc(n * sizeof(struct irda_device_info), GFP_ATOMIC);
- if (!buffer) {
- spin_unlock_irqrestore(&log->hb_spinlock, flags);
- return;
- }
-
- }
-
- /* Copy discovery information */
- memcpy(&(buffer[i]), &(curr->data),
- sizeof(discinfo_t));
- i++;
-
- /* Remove it from the log */
- curr = hashbin_remove_this(log, (irda_queue_t *) curr);
- kfree(curr);
- }
- }
-
- /* Drop the spinlock before calling the higher layers, as
- * we can't guarantee they won't call us back and create a
- * deadlock. We will work on our own private data, so we
- * don't care to be interrupted. - Jean II */
- spin_unlock_irqrestore(&log->hb_spinlock, flags);
-
- if(buffer == NULL)
- return;
-
- /* Tell IrLMP and registered clients about it */
- irlmp_discovery_expiry(buffer, i);
-
- /* Free up our buffer */
- kfree(buffer);
-}
-
-#if 0
-/*
- * Function irlmp_dump_discoveries (log)
- *
- * Print out all discoveries in log
- *
- */
-void irlmp_dump_discoveries(hashbin_t *log)
-{
- discovery_t *discovery;
-
- IRDA_ASSERT(log != NULL, return;);
-
- discovery = (discovery_t *) hashbin_get_first(log);
- while (discovery != NULL) {
- pr_debug("Discovery:\n");
- pr_debug(" daddr=%08x\n", discovery->data.daddr);
- pr_debug(" saddr=%08x\n", discovery->data.saddr);
- pr_debug(" nickname=%s\n", discovery->data.info);
-
- discovery = (discovery_t *) hashbin_get_next(log);
- }
-}
-#endif
-
-/*
- * Function irlmp_copy_discoveries (log, pn, mask)
- *
- * Copy all discoveries in a buffer
- *
- * This function implement a safe way for lmp clients to access the
- * discovery log. The basic problem is that we don't want the log
- * to change (add/remove) while the client is reading it. If the
- * lmp client manipulate directly the hashbin, he is sure to get
- * into troubles...
- * The idea is that we copy all the current discovery log in a buffer
- * which is specific to the client and pass this copy to him. As we
- * do this operation with the spinlock grabbed, we are safe...
- * Note : we don't want those clients to grab the spinlock, because
- * we have no control on how long they will hold it...
- * Note : we choose to copy the log in "struct irda_device_info" to
- * save space...
- * Note : the client must kfree himself() the log...
- * Jean II
- */
-struct irda_device_info *irlmp_copy_discoveries(hashbin_t *log, int *pn,
- __u16 mask, int old_entries)
-{
- discovery_t * discovery;
- unsigned long flags;
- discinfo_t * buffer = NULL;
- int j_timeout = (sysctl_discovery_timeout * HZ);
- int n; /* Size of the full log */
- int i = 0; /* How many we picked */
-
- IRDA_ASSERT(pn != NULL, return NULL;);
- IRDA_ASSERT(log != NULL, return NULL;);
-
- /* Save spin lock */
- spin_lock_irqsave(&log->hb_spinlock, flags);
-
- discovery = (discovery_t *) hashbin_get_first(log);
- while (discovery != NULL) {
- /* Mask out the ones we don't want :
- * We want to match the discovery mask, and to get only
- * the most recent one (unless we want old ones) */
- if ((get_unaligned((__u16 *)discovery->data.hints) & mask) &&
- ((old_entries) ||
- ((jiffies - discovery->firststamp) < j_timeout))) {
- /* Create buffer as needed.
- * As this function get called a lot and most time
- * we don't have anything to put in the log (we are
- * quite picky), we can save a lot of overhead
- * by not calling kmalloc. Jean II */
- if(buffer == NULL) {
- /* Create the client specific buffer */
- n = HASHBIN_GET_SIZE(log);
- buffer = kmalloc(n * sizeof(struct irda_device_info), GFP_ATOMIC);
- if (!buffer) {
- spin_unlock_irqrestore(&log->hb_spinlock, flags);
- return NULL;
- }
-
- }
-
- /* Copy discovery information */
- memcpy(&(buffer[i]), &(discovery->data),
- sizeof(discinfo_t));
- i++;
- }
- discovery = (discovery_t *) hashbin_get_next(log);
- }
-
- spin_unlock_irqrestore(&log->hb_spinlock, flags);
-
- /* Get the actual number of device in the buffer and return */
- *pn = i;
- return buffer;
-}
-
-#ifdef CONFIG_PROC_FS
-static inline discovery_t *discovery_seq_idx(loff_t pos)
-
-{
- discovery_t *discovery;
-
- for (discovery = (discovery_t *) hashbin_get_first(irlmp->cachelog);
- discovery != NULL;
- discovery = (discovery_t *) hashbin_get_next(irlmp->cachelog)) {
- if (pos-- == 0)
- break;
- }
-
- return discovery;
-}
-
-static void *discovery_seq_start(struct seq_file *seq, loff_t *pos)
-{
- spin_lock_irq(&irlmp->cachelog->hb_spinlock);
- return *pos ? discovery_seq_idx(*pos - 1) : SEQ_START_TOKEN;
-}
-
-static void *discovery_seq_next(struct seq_file *seq, void *v, loff_t *pos)
-{
- ++*pos;
- return (v == SEQ_START_TOKEN)
- ? (void *) hashbin_get_first(irlmp->cachelog)
- : (void *) hashbin_get_next(irlmp->cachelog);
-}
-
-static void discovery_seq_stop(struct seq_file *seq, void *v)
-{
- spin_unlock_irq(&irlmp->cachelog->hb_spinlock);
-}
-
-static int discovery_seq_show(struct seq_file *seq, void *v)
-{
- if (v == SEQ_START_TOKEN)
- seq_puts(seq, "IrLMP: Discovery log:\n\n");
- else {
- const discovery_t *discovery = v;
-
- seq_printf(seq, "nickname: %s, hint: 0x%02x%02x",
- discovery->data.info,
- discovery->data.hints[0],
- discovery->data.hints[1]);
-#if 0
- if ( discovery->data.hints[0] & HINT_PNP)
- seq_puts(seq, "PnP Compatible ");
- if ( discovery->data.hints[0] & HINT_PDA)
- seq_puts(seq, "PDA/Palmtop ");
- if ( discovery->data.hints[0] & HINT_COMPUTER)
- seq_puts(seq, "Computer ");
- if ( discovery->data.hints[0] & HINT_PRINTER)
- seq_puts(seq, "Printer ");
- if ( discovery->data.hints[0] & HINT_MODEM)
- seq_puts(seq, "Modem ");
- if ( discovery->data.hints[0] & HINT_FAX)
- seq_puts(seq, "Fax ");
- if ( discovery->data.hints[0] & HINT_LAN)
- seq_puts(seq, "LAN Access ");
-
- if ( discovery->data.hints[1] & HINT_TELEPHONY)
- seq_puts(seq, "Telephony ");
- if ( discovery->data.hints[1] & HINT_FILE_SERVER)
- seq_puts(seq, "File Server ");
- if ( discovery->data.hints[1] & HINT_COMM)
- seq_puts(seq, "IrCOMM ");
- if ( discovery->data.hints[1] & HINT_OBEX)
- seq_puts(seq, "IrOBEX ");
-#endif
- seq_printf(seq,", saddr: 0x%08x, daddr: 0x%08x\n\n",
- discovery->data.saddr,
- discovery->data.daddr);
-
- seq_putc(seq, '\n');
- }
- return 0;
-}
-
-static const struct seq_operations discovery_seq_ops = {
- .start = discovery_seq_start,
- .next = discovery_seq_next,
- .stop = discovery_seq_stop,
- .show = discovery_seq_show,
-};
-
-static int discovery_seq_open(struct inode *inode, struct file *file)
-{
- IRDA_ASSERT(irlmp != NULL, return -EINVAL;);
-
- return seq_open(file, &discovery_seq_ops);
-}
-
-const struct file_operations discovery_seq_fops = {
- .owner = THIS_MODULE,
- .open = discovery_seq_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = seq_release,
-};
-#endif
diff --git a/drivers/staging/irda/net/ircomm/Kconfig b/drivers/staging/irda/net/ircomm/Kconfig
deleted file mode 100644
index 19492c1707b7..000000000000
--- a/drivers/staging/irda/net/ircomm/Kconfig
+++ /dev/null
@@ -1,12 +0,0 @@
-config IRCOMM
- tristate "IrCOMM protocol"
- depends on IRDA && TTY
- help
- Say Y here if you want to build support for the IrCOMM protocol.
- To compile it as modules, choose M here: the modules will be
- called ircomm and ircomm_tty.
- IrCOMM implements serial port emulation, and makes it possible to
- use all existing applications that understands TTY's with an
- infrared link. Thus you should be able to use application like PPP,
- minicom and others.
-
diff --git a/drivers/staging/irda/net/ircomm/Makefile b/drivers/staging/irda/net/ircomm/Makefile
deleted file mode 100644
index ab23b5ba7e33..000000000000
--- a/drivers/staging/irda/net/ircomm/Makefile
+++ /dev/null
@@ -1,8 +0,0 @@
-#
-# Makefile for the Linux IrDA IrCOMM protocol layer.
-#
-
-obj-$(CONFIG_IRCOMM) += ircomm.o ircomm-tty.o
-
-ircomm-y := ircomm_core.o ircomm_event.o ircomm_lmp.o ircomm_ttp.o
-ircomm-tty-y := ircomm_tty.o ircomm_tty_attach.o ircomm_tty_ioctl.o ircomm_param.o
diff --git a/drivers/staging/irda/net/ircomm/ircomm_core.c b/drivers/staging/irda/net/ircomm/ircomm_core.c
deleted file mode 100644
index 3af219545f6d..000000000000
--- a/drivers/staging/irda/net/ircomm/ircomm_core.c
+++ /dev/null
@@ -1,563 +0,0 @@
-/*********************************************************************
- *
- * Filename: ircomm_core.c
- * Version: 1.0
- * Description: IrCOMM service interface
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sun Jun 6 20:37:34 1999
- * Modified at: Tue Dec 21 13:26:41 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1999 Dag Brattli, All Rights Reserved.
- * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/module.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irmod.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/iriap.h>
-#include <net/irda/irttp.h>
-#include <net/irda/irias_object.h>
-
-#include <net/irda/ircomm_event.h>
-#include <net/irda/ircomm_lmp.h>
-#include <net/irda/ircomm_ttp.h>
-#include <net/irda/ircomm_param.h>
-#include <net/irda/ircomm_core.h>
-
-static int __ircomm_close(struct ircomm_cb *self);
-static void ircomm_control_indication(struct ircomm_cb *self,
- struct sk_buff *skb, int clen);
-
-#ifdef CONFIG_PROC_FS
-extern struct proc_dir_entry *proc_irda;
-static int ircomm_seq_open(struct inode *, struct file *);
-
-static const struct file_operations ircomm_proc_fops = {
- .owner = THIS_MODULE,
- .open = ircomm_seq_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = seq_release,
-};
-#endif /* CONFIG_PROC_FS */
-
-hashbin_t *ircomm = NULL;
-
-static int __init ircomm_init(void)
-{
- ircomm = hashbin_new(HB_LOCK);
- if (ircomm == NULL) {
- net_err_ratelimited("%s(), can't allocate hashbin!\n",
- __func__);
- return -ENOMEM;
- }
-
-#ifdef CONFIG_PROC_FS
- { struct proc_dir_entry *ent;
- ent = proc_create("ircomm", 0, proc_irda, &ircomm_proc_fops);
- if (!ent) {
- printk(KERN_ERR "ircomm_init: can't create /proc entry!\n");
- return -ENODEV;
- }
- }
-#endif /* CONFIG_PROC_FS */
-
- net_info_ratelimited("IrCOMM protocol (Dag Brattli)\n");
-
- return 0;
-}
-
-static void __exit ircomm_cleanup(void)
-{
- hashbin_delete(ircomm, (FREE_FUNC) __ircomm_close);
-
-#ifdef CONFIG_PROC_FS
- remove_proc_entry("ircomm", proc_irda);
-#endif /* CONFIG_PROC_FS */
-}
-
-/*
- * Function ircomm_open (client_notify)
- *
- * Start a new IrCOMM instance
- *
- */
-struct ircomm_cb *ircomm_open(notify_t *notify, __u8 service_type, int line)
-{
- struct ircomm_cb *self = NULL;
- int ret;
-
- pr_debug("%s(), service_type=0x%02x\n", __func__ ,
- service_type);
-
- IRDA_ASSERT(ircomm != NULL, return NULL;);
-
- self = kzalloc(sizeof(struct ircomm_cb), GFP_KERNEL);
- if (self == NULL)
- return NULL;
-
- self->notify = *notify;
- self->magic = IRCOMM_MAGIC;
-
- /* Check if we should use IrLMP or IrTTP */
- if (service_type & IRCOMM_3_WIRE_RAW) {
- self->flow_status = FLOW_START;
- ret = ircomm_open_lsap(self);
- } else
- ret = ircomm_open_tsap(self);
-
- if (ret < 0) {
- kfree(self);
- return NULL;
- }
-
- self->service_type = service_type;
- self->line = line;
-
- hashbin_insert(ircomm, (irda_queue_t *) self, line, NULL);
-
- ircomm_next_state(self, IRCOMM_IDLE);
-
- return self;
-}
-
-EXPORT_SYMBOL(ircomm_open);
-
-/*
- * Function ircomm_close_instance (self)
- *
- * Remove IrCOMM instance
- *
- */
-static int __ircomm_close(struct ircomm_cb *self)
-{
- /* Disconnect link if any */
- ircomm_do_event(self, IRCOMM_DISCONNECT_REQUEST, NULL, NULL);
-
- /* Remove TSAP */
- if (self->tsap) {
- irttp_close_tsap(self->tsap);
- self->tsap = NULL;
- }
-
- /* Remove LSAP */
- if (self->lsap) {
- irlmp_close_lsap(self->lsap);
- self->lsap = NULL;
- }
- self->magic = 0;
-
- kfree(self);
-
- return 0;
-}
-
-/*
- * Function ircomm_close (self)
- *
- * Closes and removes the specified IrCOMM instance
- *
- */
-int ircomm_close(struct ircomm_cb *self)
-{
- struct ircomm_cb *entry;
-
- IRDA_ASSERT(self != NULL, return -EIO;);
- IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EIO;);
-
- entry = hashbin_remove(ircomm, self->line, NULL);
-
- IRDA_ASSERT(entry == self, return -1;);
-
- return __ircomm_close(self);
-}
-
-EXPORT_SYMBOL(ircomm_close);
-
-/*
- * Function ircomm_connect_request (self, service_type)
- *
- * Impl. of this function is differ from one of the reference. This
- * function does discovery as well as sending connect request
- *
- */
-int ircomm_connect_request(struct ircomm_cb *self, __u8 dlsap_sel,
- __u32 saddr, __u32 daddr, struct sk_buff *skb,
- __u8 service_type)
-{
- struct ircomm_info info;
- int ret;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
-
- self->service_type= service_type;
-
- info.dlsap_sel = dlsap_sel;
- info.saddr = saddr;
- info.daddr = daddr;
-
- ret = ircomm_do_event(self, IRCOMM_CONNECT_REQUEST, skb, &info);
-
- return ret;
-}
-
-EXPORT_SYMBOL(ircomm_connect_request);
-
-/*
- * Function ircomm_connect_indication (self, qos, skb)
- *
- * Notify user layer about the incoming connection
- *
- */
-void ircomm_connect_indication(struct ircomm_cb *self, struct sk_buff *skb,
- struct ircomm_info *info)
-{
- /*
- * If there are any data hiding in the control channel, we must
- * deliver it first. The side effect is that the control channel
- * will be removed from the skb
- */
- if (self->notify.connect_indication)
- self->notify.connect_indication(self->notify.instance, self,
- info->qos, info->max_data_size,
- info->max_header_size, skb);
- else {
- pr_debug("%s(), missing handler\n", __func__);
- }
-}
-
-/*
- * Function ircomm_connect_response (self, userdata, max_sdu_size)
- *
- * User accepts connection
- *
- */
-int ircomm_connect_response(struct ircomm_cb *self, struct sk_buff *userdata)
-{
- int ret;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
-
- ret = ircomm_do_event(self, IRCOMM_CONNECT_RESPONSE, userdata, NULL);
-
- return ret;
-}
-
-EXPORT_SYMBOL(ircomm_connect_response);
-
-/*
- * Function connect_confirm (self, skb)
- *
- * Notify user layer that the link is now connected
- *
- */
-void ircomm_connect_confirm(struct ircomm_cb *self, struct sk_buff *skb,
- struct ircomm_info *info)
-{
- if (self->notify.connect_confirm )
- self->notify.connect_confirm(self->notify.instance,
- self, info->qos,
- info->max_data_size,
- info->max_header_size, skb);
- else {
- pr_debug("%s(), missing handler\n", __func__);
- }
-}
-
-/*
- * Function ircomm_data_request (self, userdata)
- *
- * Send IrCOMM data to peer device
- *
- */
-int ircomm_data_request(struct ircomm_cb *self, struct sk_buff *skb)
-{
- int ret;
-
- IRDA_ASSERT(self != NULL, return -EFAULT;);
- IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EFAULT;);
- IRDA_ASSERT(skb != NULL, return -EFAULT;);
-
- ret = ircomm_do_event(self, IRCOMM_DATA_REQUEST, skb, NULL);
-
- return ret;
-}
-
-EXPORT_SYMBOL(ircomm_data_request);
-
-/*
- * Function ircomm_data_indication (self, skb)
- *
- * Data arrived, so deliver it to user
- *
- */
-void ircomm_data_indication(struct ircomm_cb *self, struct sk_buff *skb)
-{
- IRDA_ASSERT(skb->len > 0, return;);
-
- if (self->notify.data_indication)
- self->notify.data_indication(self->notify.instance, self, skb);
- else {
- pr_debug("%s(), missing handler\n", __func__);
- }
-}
-
-/*
- * Function ircomm_process_data (self, skb)
- *
- * Data arrived which may contain control channel data
- *
- */
-void ircomm_process_data(struct ircomm_cb *self, struct sk_buff *skb)
-{
- int clen;
-
- IRDA_ASSERT(skb->len > 0, return;);
-
- clen = skb->data[0];
-
- /*
- * Input validation check: a stir4200/mcp2150 combinations sometimes
- * results in frames with clen > remaining packet size. These are
- * illegal; if we throw away just this frame then it seems to carry on
- * fine
- */
- if (unlikely(skb->len < (clen + 1))) {
- pr_debug("%s() throwing away illegal frame\n",
- __func__);
- return;
- }
-
- /*
- * If there are any data hiding in the control channel, we must
- * deliver it first. The side effect is that the control channel
- * will be removed from the skb
- */
- if (clen > 0)
- ircomm_control_indication(self, skb, clen);
-
- /* Remove control channel from data channel */
- skb_pull(skb, clen+1);
-
- if (skb->len)
- ircomm_data_indication(self, skb);
- else {
- pr_debug("%s(), data was control info only!\n",
- __func__);
- }
-}
-
-/*
- * Function ircomm_control_request (self, params)
- *
- * Send control data to peer device
- *
- */
-int ircomm_control_request(struct ircomm_cb *self, struct sk_buff *skb)
-{
- int ret;
-
- IRDA_ASSERT(self != NULL, return -EFAULT;);
- IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EFAULT;);
- IRDA_ASSERT(skb != NULL, return -EFAULT;);
-
- ret = ircomm_do_event(self, IRCOMM_CONTROL_REQUEST, skb, NULL);
-
- return ret;
-}
-
-EXPORT_SYMBOL(ircomm_control_request);
-
-/*
- * Function ircomm_control_indication (self, skb)
- *
- * Data has arrived on the control channel
- *
- */
-static void ircomm_control_indication(struct ircomm_cb *self,
- struct sk_buff *skb, int clen)
-{
- /* Use udata for delivering data on the control channel */
- if (self->notify.udata_indication) {
- struct sk_buff *ctrl_skb;
-
- /* We don't own the skb, so clone it */
- ctrl_skb = skb_clone(skb, GFP_ATOMIC);
- if (!ctrl_skb)
- return;
-
- /* Remove data channel from control channel */
- skb_trim(ctrl_skb, clen+1);
-
- self->notify.udata_indication(self->notify.instance, self,
- ctrl_skb);
-
- /* Drop reference count -
- * see ircomm_tty_control_indication(). */
- dev_kfree_skb(ctrl_skb);
- } else {
- pr_debug("%s(), missing handler\n", __func__);
- }
-}
-
-/*
- * Function ircomm_disconnect_request (self, userdata, priority)
- *
- * User layer wants to disconnect the IrCOMM connection
- *
- */
-int ircomm_disconnect_request(struct ircomm_cb *self, struct sk_buff *userdata)
-{
- struct ircomm_info info;
- int ret;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
-
- ret = ircomm_do_event(self, IRCOMM_DISCONNECT_REQUEST, userdata,
- &info);
- return ret;
-}
-
-EXPORT_SYMBOL(ircomm_disconnect_request);
-
-/*
- * Function disconnect_indication (self, skb)
- *
- * Tell user that the link has been disconnected
- *
- */
-void ircomm_disconnect_indication(struct ircomm_cb *self, struct sk_buff *skb,
- struct ircomm_info *info)
-{
- IRDA_ASSERT(info != NULL, return;);
-
- if (self->notify.disconnect_indication) {
- self->notify.disconnect_indication(self->notify.instance, self,
- info->reason, skb);
- } else {
- pr_debug("%s(), missing handler\n", __func__);
- }
-}
-
-/*
- * Function ircomm_flow_request (self, flow)
- *
- *
- *
- */
-void ircomm_flow_request(struct ircomm_cb *self, LOCAL_FLOW flow)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
-
- if (self->service_type == IRCOMM_3_WIRE_RAW)
- return;
-
- irttp_flow_request(self->tsap, flow);
-}
-
-EXPORT_SYMBOL(ircomm_flow_request);
-
-#ifdef CONFIG_PROC_FS
-static void *ircomm_seq_start(struct seq_file *seq, loff_t *pos)
-{
- struct ircomm_cb *self;
- loff_t off = 0;
-
- spin_lock_irq(&ircomm->hb_spinlock);
-
- for (self = (struct ircomm_cb *) hashbin_get_first(ircomm);
- self != NULL;
- self = (struct ircomm_cb *) hashbin_get_next(ircomm)) {
- if (off++ == *pos)
- break;
-
- }
- return self;
-}
-
-static void *ircomm_seq_next(struct seq_file *seq, void *v, loff_t *pos)
-{
- ++*pos;
-
- return (void *) hashbin_get_next(ircomm);
-}
-
-static void ircomm_seq_stop(struct seq_file *seq, void *v)
-{
- spin_unlock_irq(&ircomm->hb_spinlock);
-}
-
-static int ircomm_seq_show(struct seq_file *seq, void *v)
-{
- const struct ircomm_cb *self = v;
-
- IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EINVAL; );
-
- if(self->line < 0x10)
- seq_printf(seq, "ircomm%d", self->line);
- else
- seq_printf(seq, "irlpt%d", self->line - 0x10);
-
- seq_printf(seq,
- " state: %s, slsap_sel: %#02x, dlsap_sel: %#02x, mode:",
- ircomm_state[ self->state],
- self->slsap_sel, self->dlsap_sel);
-
- if(self->service_type & IRCOMM_3_WIRE_RAW)
- seq_printf(seq, " 3-wire-raw");
- if(self->service_type & IRCOMM_3_WIRE)
- seq_printf(seq, " 3-wire");
- if(self->service_type & IRCOMM_9_WIRE)
- seq_printf(seq, " 9-wire");
- if(self->service_type & IRCOMM_CENTRONICS)
- seq_printf(seq, " Centronics");
- seq_putc(seq, '\n');
-
- return 0;
-}
-
-static const struct seq_operations ircomm_seq_ops = {
- .start = ircomm_seq_start,
- .next = ircomm_seq_next,
- .stop = ircomm_seq_stop,
- .show = ircomm_seq_show,
-};
-
-static int ircomm_seq_open(struct inode *inode, struct file *file)
-{
- return seq_open(file, &ircomm_seq_ops);
-}
-#endif /* CONFIG_PROC_FS */
-
-MODULE_AUTHOR("Dag Brattli <dag@brattli.net>");
-MODULE_DESCRIPTION("IrCOMM protocol");
-MODULE_LICENSE("GPL");
-
-module_init(ircomm_init);
-module_exit(ircomm_cleanup);
diff --git a/drivers/staging/irda/net/ircomm/ircomm_event.c b/drivers/staging/irda/net/ircomm/ircomm_event.c
deleted file mode 100644
index b0730ac9f388..000000000000
--- a/drivers/staging/irda/net/ircomm/ircomm_event.c
+++ /dev/null
@@ -1,246 +0,0 @@
-/*********************************************************************
- *
- * Filename: ircomm_event.c
- * Version: 1.0
- * Description: IrCOMM layer state machine
- * Status: Stable
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sun Jun 6 20:33:11 1999
- * Modified at: Sun Dec 12 13:44:32 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1999 Dag Brattli, All Rights Reserved.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/proc_fs.h>
-#include <linux/init.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/iriap.h>
-#include <net/irda/irttp.h>
-#include <net/irda/irias_object.h>
-
-#include <net/irda/ircomm_core.h>
-#include <net/irda/ircomm_event.h>
-
-static int ircomm_state_idle(struct ircomm_cb *self, IRCOMM_EVENT event,
- struct sk_buff *skb, struct ircomm_info *info);
-static int ircomm_state_waiti(struct ircomm_cb *self, IRCOMM_EVENT event,
- struct sk_buff *skb, struct ircomm_info *info);
-static int ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event,
- struct sk_buff *skb, struct ircomm_info *info);
-static int ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event,
- struct sk_buff *skb, struct ircomm_info *info);
-
-const char *const ircomm_state[] = {
- "IRCOMM_IDLE",
- "IRCOMM_WAITI",
- "IRCOMM_WAITR",
- "IRCOMM_CONN",
-};
-
-static const char *const ircomm_event[] __maybe_unused = {
- "IRCOMM_CONNECT_REQUEST",
- "IRCOMM_CONNECT_RESPONSE",
- "IRCOMM_TTP_CONNECT_INDICATION",
- "IRCOMM_LMP_CONNECT_INDICATION",
- "IRCOMM_TTP_CONNECT_CONFIRM",
- "IRCOMM_LMP_CONNECT_CONFIRM",
-
- "IRCOMM_LMP_DISCONNECT_INDICATION",
- "IRCOMM_TTP_DISCONNECT_INDICATION",
- "IRCOMM_DISCONNECT_REQUEST",
-
- "IRCOMM_TTP_DATA_INDICATION",
- "IRCOMM_LMP_DATA_INDICATION",
- "IRCOMM_DATA_REQUEST",
- "IRCOMM_CONTROL_REQUEST",
- "IRCOMM_CONTROL_INDICATION",
-};
-
-static int (*state[])(struct ircomm_cb *self, IRCOMM_EVENT event,
- struct sk_buff *skb, struct ircomm_info *info) =
-{
- ircomm_state_idle,
- ircomm_state_waiti,
- ircomm_state_waitr,
- ircomm_state_conn,
-};
-
-/*
- * Function ircomm_state_idle (self, event, skb)
- *
- * IrCOMM is currently idle
- *
- */
-static int ircomm_state_idle(struct ircomm_cb *self, IRCOMM_EVENT event,
- struct sk_buff *skb, struct ircomm_info *info)
-{
- int ret = 0;
-
- switch (event) {
- case IRCOMM_CONNECT_REQUEST:
- ircomm_next_state(self, IRCOMM_WAITI);
- ret = self->issue.connect_request(self, skb, info);
- break;
- case IRCOMM_TTP_CONNECT_INDICATION:
- case IRCOMM_LMP_CONNECT_INDICATION:
- ircomm_next_state(self, IRCOMM_WAITR);
- ircomm_connect_indication(self, skb, info);
- break;
- default:
- pr_debug("%s(), unknown event: %s\n", __func__ ,
- ircomm_event[event]);
- ret = -EINVAL;
- }
- return ret;
-}
-
-/*
- * Function ircomm_state_waiti (self, event, skb)
- *
- * The IrCOMM user has requested an IrCOMM connection to the remote
- * device and is awaiting confirmation
- */
-static int ircomm_state_waiti(struct ircomm_cb *self, IRCOMM_EVENT event,
- struct sk_buff *skb, struct ircomm_info *info)
-{
- int ret = 0;
-
- switch (event) {
- case IRCOMM_TTP_CONNECT_CONFIRM:
- case IRCOMM_LMP_CONNECT_CONFIRM:
- ircomm_next_state(self, IRCOMM_CONN);
- ircomm_connect_confirm(self, skb, info);
- break;
- case IRCOMM_TTP_DISCONNECT_INDICATION:
- case IRCOMM_LMP_DISCONNECT_INDICATION:
- ircomm_next_state(self, IRCOMM_IDLE);
- ircomm_disconnect_indication(self, skb, info);
- break;
- default:
- pr_debug("%s(), unknown event: %s\n", __func__ ,
- ircomm_event[event]);
- ret = -EINVAL;
- }
- return ret;
-}
-
-/*
- * Function ircomm_state_waitr (self, event, skb)
- *
- * IrCOMM has received an incoming connection request and is awaiting
- * response from the user
- */
-static int ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event,
- struct sk_buff *skb, struct ircomm_info *info)
-{
- int ret = 0;
-
- switch (event) {
- case IRCOMM_CONNECT_RESPONSE:
- ircomm_next_state(self, IRCOMM_CONN);
- ret = self->issue.connect_response(self, skb);
- break;
- case IRCOMM_DISCONNECT_REQUEST:
- ircomm_next_state(self, IRCOMM_IDLE);
- ret = self->issue.disconnect_request(self, skb, info);
- break;
- case IRCOMM_TTP_DISCONNECT_INDICATION:
- case IRCOMM_LMP_DISCONNECT_INDICATION:
- ircomm_next_state(self, IRCOMM_IDLE);
- ircomm_disconnect_indication(self, skb, info);
- break;
- default:
- pr_debug("%s(), unknown event = %s\n", __func__ ,
- ircomm_event[event]);
- ret = -EINVAL;
- }
- return ret;
-}
-
-/*
- * Function ircomm_state_conn (self, event, skb)
- *
- * IrCOMM is connected to the peer IrCOMM device
- *
- */
-static int ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event,
- struct sk_buff *skb, struct ircomm_info *info)
-{
- int ret = 0;
-
- switch (event) {
- case IRCOMM_DATA_REQUEST:
- ret = self->issue.data_request(self, skb, 0);
- break;
- case IRCOMM_TTP_DATA_INDICATION:
- ircomm_process_data(self, skb);
- break;
- case IRCOMM_LMP_DATA_INDICATION:
- ircomm_data_indication(self, skb);
- break;
- case IRCOMM_CONTROL_REQUEST:
- /* Just send a separate frame for now */
- ret = self->issue.data_request(self, skb, skb->len);
- break;
- case IRCOMM_TTP_DISCONNECT_INDICATION:
- case IRCOMM_LMP_DISCONNECT_INDICATION:
- ircomm_next_state(self, IRCOMM_IDLE);
- ircomm_disconnect_indication(self, skb, info);
- break;
- case IRCOMM_DISCONNECT_REQUEST:
- ircomm_next_state(self, IRCOMM_IDLE);
- ret = self->issue.disconnect_request(self, skb, info);
- break;
- default:
- pr_debug("%s(), unknown event = %s\n", __func__ ,
- ircomm_event[event]);
- ret = -EINVAL;
- }
- return ret;
-}
-
-/*
- * Function ircomm_do_event (self, event, skb)
- *
- * Process event
- *
- */
-int ircomm_do_event(struct ircomm_cb *self, IRCOMM_EVENT event,
- struct sk_buff *skb, struct ircomm_info *info)
-{
- pr_debug("%s: state=%s, event=%s\n", __func__ ,
- ircomm_state[self->state], ircomm_event[event]);
-
- return (*state[self->state])(self, event, skb, info);
-}
-
-/*
- * Function ircomm_next_state (self, state)
- *
- * Switch state
- *
- */
-void ircomm_next_state(struct ircomm_cb *self, IRCOMM_STATE state)
-{
- self->state = state;
-
- pr_debug("%s: next state=%s, service type=%d\n", __func__ ,
- ircomm_state[self->state], self->service_type);
-}
diff --git a/drivers/staging/irda/net/ircomm/ircomm_lmp.c b/drivers/staging/irda/net/ircomm/ircomm_lmp.c
deleted file mode 100644
index e4cc847bb933..000000000000
--- a/drivers/staging/irda/net/ircomm/ircomm_lmp.c
+++ /dev/null
@@ -1,350 +0,0 @@
-/*********************************************************************
- *
- * Filename: ircomm_lmp.c
- * Version: 1.0
- * Description: Interface between IrCOMM and IrLMP
- * Status: Stable
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sun Jun 6 20:48:27 1999
- * Modified at: Sun Dec 12 13:44:17 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- * Sources: Previous IrLPT work by Thomas Davis
- *
- * Copyright (c) 1999 Dag Brattli, All Rights Reserved.
- * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/init.h>
-#include <linux/gfp.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/iriap.h>
-#include <net/irda/irda_device.h> /* struct irda_skb_cb */
-
-#include <net/irda/ircomm_event.h>
-#include <net/irda/ircomm_lmp.h>
-
-
-/*
- * Function ircomm_lmp_connect_request (self, userdata)
- *
- *
- *
- */
-static int ircomm_lmp_connect_request(struct ircomm_cb *self,
- struct sk_buff *userdata,
- struct ircomm_info *info)
-{
- int ret = 0;
-
- /* Don't forget to refcount it - should be NULL anyway */
- if(userdata)
- skb_get(userdata);
-
- ret = irlmp_connect_request(self->lsap, info->dlsap_sel,
- info->saddr, info->daddr, NULL, userdata);
- return ret;
-}
-
-/*
- * Function ircomm_lmp_connect_response (self, skb)
- *
- *
- *
- */
-static int ircomm_lmp_connect_response(struct ircomm_cb *self,
- struct sk_buff *userdata)
-{
- struct sk_buff *tx_skb;
-
- /* Any userdata supplied? */
- if (userdata == NULL) {
- tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
- if (!tx_skb)
- return -ENOMEM;
-
- /* Reserve space for MUX and LAP header */
- skb_reserve(tx_skb, LMP_MAX_HEADER);
- } else {
- /*
- * Check that the client has reserved enough space for
- * headers
- */
- IRDA_ASSERT(skb_headroom(userdata) >= LMP_MAX_HEADER,
- return -1;);
-
- /* Don't forget to refcount it - should be NULL anyway */
- skb_get(userdata);
- tx_skb = userdata;
- }
-
- return irlmp_connect_response(self->lsap, tx_skb);
-}
-
-static int ircomm_lmp_disconnect_request(struct ircomm_cb *self,
- struct sk_buff *userdata,
- struct ircomm_info *info)
-{
- struct sk_buff *tx_skb;
- int ret;
-
- if (!userdata) {
- tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
- if (!tx_skb)
- return -ENOMEM;
-
- /* Reserve space for MUX and LAP header */
- skb_reserve(tx_skb, LMP_MAX_HEADER);
- userdata = tx_skb;
- } else {
- /* Don't forget to refcount it - should be NULL anyway */
- skb_get(userdata);
- }
-
- ret = irlmp_disconnect_request(self->lsap, userdata);
-
- return ret;
-}
-
-/*
- * Function ircomm_lmp_flow_control (skb)
- *
- * This function is called when a data frame we have sent to IrLAP has
- * been deallocated. We do this to make sure we don't flood IrLAP with
- * frames, since we are not using the IrTTP flow control mechanism
- */
-static void ircomm_lmp_flow_control(struct sk_buff *skb)
-{
- struct irda_skb_cb *cb;
- struct ircomm_cb *self;
- int line;
-
- IRDA_ASSERT(skb != NULL, return;);
-
- cb = (struct irda_skb_cb *) skb->cb;
-
- line = cb->line;
-
- self = (struct ircomm_cb *) hashbin_lock_find(ircomm, line, NULL);
- if (!self) {
- pr_debug("%s(), didn't find myself\n", __func__);
- return;
- }
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
-
- self->pkt_count--;
-
- if ((self->pkt_count < 2) && (self->flow_status == FLOW_STOP)) {
- pr_debug("%s(), asking TTY to start again!\n", __func__);
- self->flow_status = FLOW_START;
- if (self->notify.flow_indication)
- self->notify.flow_indication(self->notify.instance,
- self, FLOW_START);
- }
-}
-
-/*
- * Function ircomm_lmp_data_request (self, userdata)
- *
- * Send data frame to peer device
- *
- */
-static int ircomm_lmp_data_request(struct ircomm_cb *self,
- struct sk_buff *skb,
- int not_used)
-{
- struct irda_skb_cb *cb;
- int ret;
-
- IRDA_ASSERT(skb != NULL, return -1;);
-
- cb = (struct irda_skb_cb *) skb->cb;
-
- cb->line = self->line;
-
- pr_debug("%s(), sending frame\n", __func__);
-
- /* Don't forget to refcount it - see ircomm_tty_do_softint() */
- skb_get(skb);
-
- skb_orphan(skb);
- skb->destructor = ircomm_lmp_flow_control;
-
- if ((self->pkt_count++ > 7) && (self->flow_status == FLOW_START)) {
- pr_debug("%s(), asking TTY to slow down!\n", __func__);
- self->flow_status = FLOW_STOP;
- if (self->notify.flow_indication)
- self->notify.flow_indication(self->notify.instance,
- self, FLOW_STOP);
- }
- ret = irlmp_data_request(self->lsap, skb);
- if (ret) {
- net_err_ratelimited("%s(), failed\n", __func__);
- /* irlmp_data_request already free the packet */
- }
-
- return ret;
-}
-
-/*
- * Function ircomm_lmp_data_indication (instance, sap, skb)
- *
- * Incoming data which we must deliver to the state machine, to check
- * we are still connected.
- */
-static int ircomm_lmp_data_indication(void *instance, void *sap,
- struct sk_buff *skb)
-{
- struct ircomm_cb *self = (struct ircomm_cb *) instance;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
- IRDA_ASSERT(skb != NULL, return -1;);
-
- ircomm_do_event(self, IRCOMM_LMP_DATA_INDICATION, skb, NULL);
-
- /* Drop reference count - see ircomm_tty_data_indication(). */
- dev_kfree_skb(skb);
-
- return 0;
-}
-
-/*
- * Function ircomm_lmp_connect_confirm (instance, sap, qos, max_sdu_size,
- * max_header_size, skb)
- *
- * Connection has been confirmed by peer device
- *
- */
-static void ircomm_lmp_connect_confirm(void *instance, void *sap,
- struct qos_info *qos,
- __u32 max_seg_size,
- __u8 max_header_size,
- struct sk_buff *skb)
-{
- struct ircomm_cb *self = (struct ircomm_cb *) instance;
- struct ircomm_info info;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
- IRDA_ASSERT(skb != NULL, return;);
- IRDA_ASSERT(qos != NULL, return;);
-
- info.max_data_size = max_seg_size;
- info.max_header_size = max_header_size;
- info.qos = qos;
-
- ircomm_do_event(self, IRCOMM_LMP_CONNECT_CONFIRM, skb, &info);
-
- /* Drop reference count - see ircomm_tty_connect_confirm(). */
- dev_kfree_skb(skb);
-}
-
-/*
- * Function ircomm_lmp_connect_indication (instance, sap, qos, max_sdu_size,
- * max_header_size, skb)
- *
- * Peer device wants to make a connection with us
- *
- */
-static void ircomm_lmp_connect_indication(void *instance, void *sap,
- struct qos_info *qos,
- __u32 max_seg_size,
- __u8 max_header_size,
- struct sk_buff *skb)
-{
- struct ircomm_cb *self = (struct ircomm_cb *)instance;
- struct ircomm_info info;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
- IRDA_ASSERT(skb != NULL, return;);
- IRDA_ASSERT(qos != NULL, return;);
-
- info.max_data_size = max_seg_size;
- info.max_header_size = max_header_size;
- info.qos = qos;
-
- ircomm_do_event(self, IRCOMM_LMP_CONNECT_INDICATION, skb, &info);
-
- /* Drop reference count - see ircomm_tty_connect_indication(). */
- dev_kfree_skb(skb);
-}
-
-/*
- * Function ircomm_lmp_disconnect_indication (instance, sap, reason, skb)
- *
- * Peer device has closed the connection, or the link went down for some
- * other reason
- */
-static void ircomm_lmp_disconnect_indication(void *instance, void *sap,
- LM_REASON reason,
- struct sk_buff *skb)
-{
- struct ircomm_cb *self = (struct ircomm_cb *) instance;
- struct ircomm_info info;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
-
- info.reason = reason;
-
- ircomm_do_event(self, IRCOMM_LMP_DISCONNECT_INDICATION, skb, &info);
-
- /* Drop reference count - see ircomm_tty_disconnect_indication(). */
- if(skb)
- dev_kfree_skb(skb);
-}
-/*
- * Function ircomm_open_lsap (self)
- *
- * Open LSAP. This function will only be used when using "raw" services
- *
- */
-int ircomm_open_lsap(struct ircomm_cb *self)
-{
- notify_t notify;
-
- /* Register callbacks */
- irda_notify_init(&notify);
- notify.data_indication = ircomm_lmp_data_indication;
- notify.connect_confirm = ircomm_lmp_connect_confirm;
- notify.connect_indication = ircomm_lmp_connect_indication;
- notify.disconnect_indication = ircomm_lmp_disconnect_indication;
- notify.instance = self;
- strlcpy(notify.name, "IrCOMM", sizeof(notify.name));
-
- self->lsap = irlmp_open_lsap(LSAP_ANY, &notify, 0);
- if (!self->lsap) {
- pr_debug("%sfailed to allocate tsap\n", __func__);
- return -1;
- }
- self->slsap_sel = self->lsap->slsap_sel;
-
- /*
- * Initialize the call-table for issuing commands
- */
- self->issue.data_request = ircomm_lmp_data_request;
- self->issue.connect_request = ircomm_lmp_connect_request;
- self->issue.connect_response = ircomm_lmp_connect_response;
- self->issue.disconnect_request = ircomm_lmp_disconnect_request;
-
- return 0;
-}
diff --git a/drivers/staging/irda/net/ircomm/ircomm_param.c b/drivers/staging/irda/net/ircomm/ircomm_param.c
deleted file mode 100644
index 5728e76ca6d5..000000000000
--- a/drivers/staging/irda/net/ircomm/ircomm_param.c
+++ /dev/null
@@ -1,501 +0,0 @@
-/*********************************************************************
- *
- * Filename: ircomm_param.c
- * Version: 1.0
- * Description: Parameter handling for the IrCOMM protocol
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Mon Jun 7 10:25:11 1999
- * Modified at: Sun Jan 30 14:32:03 2000
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/gfp.h>
-#include <linux/workqueue.h>
-#include <linux/interrupt.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/parameters.h>
-
-#include <net/irda/ircomm_core.h>
-#include <net/irda/ircomm_tty_attach.h>
-#include <net/irda/ircomm_tty.h>
-
-#include <net/irda/ircomm_param.h>
-
-static int ircomm_param_service_type(void *instance, irda_param_t *param,
- int get);
-static int ircomm_param_port_type(void *instance, irda_param_t *param,
- int get);
-static int ircomm_param_port_name(void *instance, irda_param_t *param,
- int get);
-static int ircomm_param_service_type(void *instance, irda_param_t *param,
- int get);
-static int ircomm_param_data_rate(void *instance, irda_param_t *param,
- int get);
-static int ircomm_param_data_format(void *instance, irda_param_t *param,
- int get);
-static int ircomm_param_flow_control(void *instance, irda_param_t *param,
- int get);
-static int ircomm_param_xon_xoff(void *instance, irda_param_t *param, int get);
-static int ircomm_param_enq_ack(void *instance, irda_param_t *param, int get);
-static int ircomm_param_line_status(void *instance, irda_param_t *param,
- int get);
-static int ircomm_param_dte(void *instance, irda_param_t *param, int get);
-static int ircomm_param_dce(void *instance, irda_param_t *param, int get);
-static int ircomm_param_poll(void *instance, irda_param_t *param, int get);
-
-static const pi_minor_info_t pi_minor_call_table_common[] = {
- { ircomm_param_service_type, PV_INT_8_BITS },
- { ircomm_param_port_type, PV_INT_8_BITS },
- { ircomm_param_port_name, PV_STRING }
-};
-static const pi_minor_info_t pi_minor_call_table_non_raw[] = {
- { ircomm_param_data_rate, PV_INT_32_BITS | PV_BIG_ENDIAN },
- { ircomm_param_data_format, PV_INT_8_BITS },
- { ircomm_param_flow_control, PV_INT_8_BITS },
- { ircomm_param_xon_xoff, PV_INT_16_BITS },
- { ircomm_param_enq_ack, PV_INT_16_BITS },
- { ircomm_param_line_status, PV_INT_8_BITS }
-};
-static const pi_minor_info_t pi_minor_call_table_9_wire[] = {
- { ircomm_param_dte, PV_INT_8_BITS },
- { ircomm_param_dce, PV_INT_8_BITS },
- { ircomm_param_poll, PV_NO_VALUE },
-};
-
-static const pi_major_info_t pi_major_call_table[] = {
- { pi_minor_call_table_common, 3 },
- { pi_minor_call_table_non_raw, 6 },
- { pi_minor_call_table_9_wire, 3 }
-/* { pi_minor_call_table_centronics } */
-};
-
-pi_param_info_t ircomm_param_info = { pi_major_call_table, 3, 0x0f, 4 };
-
-/*
- * Function ircomm_param_request (self, pi, flush)
- *
- * Queue a parameter for the control channel
- *
- */
-int ircomm_param_request(struct ircomm_tty_cb *self, __u8 pi, int flush)
-{
- unsigned long flags;
- struct sk_buff *skb;
- int count;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
- /* Make sure we don't send parameters for raw mode */
- if (self->service_type == IRCOMM_3_WIRE_RAW)
- return 0;
-
- spin_lock_irqsave(&self->spinlock, flags);
-
- skb = self->ctrl_skb;
- if (!skb) {
- skb = alloc_skb(256, GFP_ATOMIC);
- if (!skb) {
- spin_unlock_irqrestore(&self->spinlock, flags);
- return -ENOMEM;
- }
-
- skb_reserve(skb, self->max_header_size);
- self->ctrl_skb = skb;
- }
- /*
- * Inserting is a little bit tricky since we don't know how much
- * room we will need. But this should hopefully work OK
- */
- count = irda_param_insert(self, pi, skb_tail_pointer(skb),
- skb_tailroom(skb), &ircomm_param_info);
- if (count < 0) {
- net_warn_ratelimited("%s(), no room for parameter!\n",
- __func__);
- spin_unlock_irqrestore(&self->spinlock, flags);
- return -1;
- }
- skb_put(skb, count);
- pr_debug("%s(), skb->len=%d\n", __func__, skb->len);
-
- spin_unlock_irqrestore(&self->spinlock, flags);
-
- if (flush) {
- /* ircomm_tty_do_softint will take care of the rest */
- schedule_work(&self->tqueue);
- }
-
- return count;
-}
-
-/*
- * Function ircomm_param_service_type (self, buf, len)
- *
- * Handle service type, this function will both be called after the LM-IAS
- * query and then the remote device sends its initial parameters
- *
- */
-static int ircomm_param_service_type(void *instance, irda_param_t *param,
- int get)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
- __u8 service_type = (__u8) param->pv.i;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
- if (get) {
- param->pv.i = self->settings.service_type;
- return 0;
- }
-
- /* Find all common service types */
- service_type &= self->service_type;
- if (!service_type) {
- pr_debug("%s(), No common service type to use!\n", __func__);
- return -1;
- }
- pr_debug("%s(), services in common=%02x\n", __func__ ,
- service_type);
-
- /*
- * Now choose a preferred service type of those available
- */
- if (service_type & IRCOMM_CENTRONICS)
- self->settings.service_type = IRCOMM_CENTRONICS;
- else if (service_type & IRCOMM_9_WIRE)
- self->settings.service_type = IRCOMM_9_WIRE;
- else if (service_type & IRCOMM_3_WIRE)
- self->settings.service_type = IRCOMM_3_WIRE;
- else if (service_type & IRCOMM_3_WIRE_RAW)
- self->settings.service_type = IRCOMM_3_WIRE_RAW;
-
- pr_debug("%s(), resulting service type=0x%02x\n", __func__ ,
- self->settings.service_type);
-
- /*
- * Now the line is ready for some communication. Check if we are a
- * server, and send over some initial parameters.
- * Client do it in ircomm_tty_state_setup().
- * Note : we may get called from ircomm_tty_getvalue_confirm(),
- * therefore before we even have open any socket. And self->client
- * is initialised to TRUE only later. So, we check if the link is
- * really initialised. - Jean II
- */
- if ((self->max_header_size != IRCOMM_TTY_HDR_UNINITIALISED) &&
- (!self->client) &&
- (self->settings.service_type != IRCOMM_3_WIRE_RAW))
- {
- /* Init connection */
- ircomm_tty_send_initial_parameters(self);
- ircomm_tty_link_established(self);
- }
-
- return 0;
-}
-
-/*
- * Function ircomm_param_port_type (self, param)
- *
- * The port type parameter tells if the devices are serial or parallel.
- * Since we only advertise serial service, this parameter should only
- * be equal to IRCOMM_SERIAL.
- */
-static int ircomm_param_port_type(void *instance, irda_param_t *param, int get)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
- if (get)
- param->pv.i = IRCOMM_SERIAL;
- else {
- self->settings.port_type = (__u8) param->pv.i;
-
- pr_debug("%s(), port type=%d\n", __func__ ,
- self->settings.port_type);
- }
- return 0;
-}
-
-/*
- * Function ircomm_param_port_name (self, param)
- *
- * Exchange port name
- *
- */
-static int ircomm_param_port_name(void *instance, irda_param_t *param, int get)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
- if (get) {
- pr_debug("%s(), not imp!\n", __func__);
- } else {
- pr_debug("%s(), port-name=%s\n", __func__ , param->pv.c);
- strncpy(self->settings.port_name, param->pv.c, 32);
- }
-
- return 0;
-}
-
-/*
- * Function ircomm_param_data_rate (self, param)
- *
- * Exchange data rate to be used in this settings
- *
- */
-static int ircomm_param_data_rate(void *instance, irda_param_t *param, int get)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
- if (get)
- param->pv.i = self->settings.data_rate;
- else
- self->settings.data_rate = param->pv.i;
-
- pr_debug("%s(), data rate = %d\n", __func__ , param->pv.i);
-
- return 0;
-}
-
-/*
- * Function ircomm_param_data_format (self, param)
- *
- * Exchange data format to be used in this settings
- *
- */
-static int ircomm_param_data_format(void *instance, irda_param_t *param,
- int get)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
- if (get)
- param->pv.i = self->settings.data_format;
- else
- self->settings.data_format = (__u8) param->pv.i;
-
- return 0;
-}
-
-/*
- * Function ircomm_param_flow_control (self, param)
- *
- * Exchange flow control settings to be used in this settings
- *
- */
-static int ircomm_param_flow_control(void *instance, irda_param_t *param,
- int get)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
- if (get)
- param->pv.i = self->settings.flow_control;
- else
- self->settings.flow_control = (__u8) param->pv.i;
-
- pr_debug("%s(), flow control = 0x%02x\n", __func__ , (__u8)param->pv.i);
-
- return 0;
-}
-
-/*
- * Function ircomm_param_xon_xoff (self, param)
- *
- * Exchange XON/XOFF characters
- *
- */
-static int ircomm_param_xon_xoff(void *instance, irda_param_t *param, int get)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
- if (get) {
- param->pv.i = self->settings.xonxoff[0];
- param->pv.i |= self->settings.xonxoff[1] << 8;
- } else {
- self->settings.xonxoff[0] = (__u16) param->pv.i & 0xff;
- self->settings.xonxoff[1] = (__u16) param->pv.i >> 8;
- }
-
- pr_debug("%s(), XON/XOFF = 0x%02x,0x%02x\n", __func__ ,
- param->pv.i & 0xff, param->pv.i >> 8);
-
- return 0;
-}
-
-/*
- * Function ircomm_param_enq_ack (self, param)
- *
- * Exchange ENQ/ACK characters
- *
- */
-static int ircomm_param_enq_ack(void *instance, irda_param_t *param, int get)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
- if (get) {
- param->pv.i = self->settings.enqack[0];
- param->pv.i |= self->settings.enqack[1] << 8;
- } else {
- self->settings.enqack[0] = (__u16) param->pv.i & 0xff;
- self->settings.enqack[1] = (__u16) param->pv.i >> 8;
- }
-
- pr_debug("%s(), ENQ/ACK = 0x%02x,0x%02x\n", __func__ ,
- param->pv.i & 0xff, param->pv.i >> 8);
-
- return 0;
-}
-
-/*
- * Function ircomm_param_line_status (self, param)
- *
- *
- *
- */
-static int ircomm_param_line_status(void *instance, irda_param_t *param,
- int get)
-{
- pr_debug("%s(), not impl.\n", __func__);
-
- return 0;
-}
-
-/*
- * Function ircomm_param_dte (instance, param)
- *
- * If we get here, there must be some sort of null-modem connection, and
- * we are probably working in server mode as well.
- */
-static int ircomm_param_dte(void *instance, irda_param_t *param, int get)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
- __u8 dte;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
- if (get)
- param->pv.i = self->settings.dte;
- else {
- dte = (__u8) param->pv.i;
-
- self->settings.dce = 0;
-
- if (dte & IRCOMM_DELTA_DTR)
- self->settings.dce |= (IRCOMM_DELTA_DSR|
- IRCOMM_DELTA_RI |
- IRCOMM_DELTA_CD);
- if (dte & IRCOMM_DTR)
- self->settings.dce |= (IRCOMM_DSR|
- IRCOMM_RI |
- IRCOMM_CD);
-
- if (dte & IRCOMM_DELTA_RTS)
- self->settings.dce |= IRCOMM_DELTA_CTS;
- if (dte & IRCOMM_RTS)
- self->settings.dce |= IRCOMM_CTS;
-
- /* Take appropriate actions */
- ircomm_tty_check_modem_status(self);
-
- /* Null modem cable emulator */
- self->settings.null_modem = TRUE;
- }
-
- return 0;
-}
-
-/*
- * Function ircomm_param_dce (instance, param)
- *
- *
- *
- */
-static int ircomm_param_dce(void *instance, irda_param_t *param, int get)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
- __u8 dce;
-
- pr_debug("%s(), dce = 0x%02x\n", __func__ , (__u8)param->pv.i);
-
- dce = (__u8) param->pv.i;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
- self->settings.dce = dce;
-
- /* Check if any of the settings have changed */
- if (dce & 0x0f) {
- if (dce & IRCOMM_DELTA_CTS) {
- pr_debug("%s(), CTS\n", __func__);
- }
- }
-
- ircomm_tty_check_modem_status(self);
-
- return 0;
-}
-
-/*
- * Function ircomm_param_poll (instance, param)
- *
- * Called when the peer device is polling for the line settings
- *
- */
-static int ircomm_param_poll(void *instance, irda_param_t *param, int get)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
- /* Poll parameters are always of length 0 (just a signal) */
- if (!get) {
- /* Respond with DTE line settings */
- ircomm_param_request(self, IRCOMM_DTE, TRUE);
- }
- return 0;
-}
-
-
-
-
-
diff --git a/drivers/staging/irda/net/ircomm/ircomm_ttp.c b/drivers/staging/irda/net/ircomm/ircomm_ttp.c
deleted file mode 100644
index 4b81e0934770..000000000000
--- a/drivers/staging/irda/net/ircomm/ircomm_ttp.c
+++ /dev/null
@@ -1,350 +0,0 @@
-/*********************************************************************
- *
- * Filename: ircomm_ttp.c
- * Version: 1.0
- * Description: Interface between IrCOMM and IrTTP
- * Status: Stable
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sun Jun 6 20:48:27 1999
- * Modified at: Mon Dec 13 11:35:13 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1999 Dag Brattli, All Rights Reserved.
- * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/init.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/iriap.h>
-#include <net/irda/irttp.h>
-
-#include <net/irda/ircomm_event.h>
-#include <net/irda/ircomm_ttp.h>
-
-static int ircomm_ttp_data_indication(void *instance, void *sap,
- struct sk_buff *skb);
-static void ircomm_ttp_connect_confirm(void *instance, void *sap,
- struct qos_info *qos,
- __u32 max_sdu_size,
- __u8 max_header_size,
- struct sk_buff *skb);
-static void ircomm_ttp_connect_indication(void *instance, void *sap,
- struct qos_info *qos,
- __u32 max_sdu_size,
- __u8 max_header_size,
- struct sk_buff *skb);
-static void ircomm_ttp_flow_indication(void *instance, void *sap,
- LOCAL_FLOW cmd);
-static void ircomm_ttp_disconnect_indication(void *instance, void *sap,
- LM_REASON reason,
- struct sk_buff *skb);
-static int ircomm_ttp_data_request(struct ircomm_cb *self,
- struct sk_buff *skb,
- int clen);
-static int ircomm_ttp_connect_request(struct ircomm_cb *self,
- struct sk_buff *userdata,
- struct ircomm_info *info);
-static int ircomm_ttp_connect_response(struct ircomm_cb *self,
- struct sk_buff *userdata);
-static int ircomm_ttp_disconnect_request(struct ircomm_cb *self,
- struct sk_buff *userdata,
- struct ircomm_info *info);
-
-/*
- * Function ircomm_open_tsap (self)
- *
- *
- *
- */
-int ircomm_open_tsap(struct ircomm_cb *self)
-{
- notify_t notify;
-
- /* Register callbacks */
- irda_notify_init(&notify);
- notify.data_indication = ircomm_ttp_data_indication;
- notify.connect_confirm = ircomm_ttp_connect_confirm;
- notify.connect_indication = ircomm_ttp_connect_indication;
- notify.flow_indication = ircomm_ttp_flow_indication;
- notify.disconnect_indication = ircomm_ttp_disconnect_indication;
- notify.instance = self;
- strlcpy(notify.name, "IrCOMM", sizeof(notify.name));
-
- self->tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT,
- &notify);
- if (!self->tsap) {
- pr_debug("%sfailed to allocate tsap\n", __func__);
- return -1;
- }
- self->slsap_sel = self->tsap->stsap_sel;
-
- /*
- * Initialize the call-table for issuing commands
- */
- self->issue.data_request = ircomm_ttp_data_request;
- self->issue.connect_request = ircomm_ttp_connect_request;
- self->issue.connect_response = ircomm_ttp_connect_response;
- self->issue.disconnect_request = ircomm_ttp_disconnect_request;
-
- return 0;
-}
-
-/*
- * Function ircomm_ttp_connect_request (self, userdata)
- *
- *
- *
- */
-static int ircomm_ttp_connect_request(struct ircomm_cb *self,
- struct sk_buff *userdata,
- struct ircomm_info *info)
-{
- int ret = 0;
-
- /* Don't forget to refcount it - should be NULL anyway */
- if(userdata)
- skb_get(userdata);
-
- ret = irttp_connect_request(self->tsap, info->dlsap_sel,
- info->saddr, info->daddr, NULL,
- TTP_SAR_DISABLE, userdata);
-
- return ret;
-}
-
-/*
- * Function ircomm_ttp_connect_response (self, skb)
- *
- *
- *
- */
-static int ircomm_ttp_connect_response(struct ircomm_cb *self,
- struct sk_buff *userdata)
-{
- int ret;
-
- /* Don't forget to refcount it - should be NULL anyway */
- if(userdata)
- skb_get(userdata);
-
- ret = irttp_connect_response(self->tsap, TTP_SAR_DISABLE, userdata);
-
- return ret;
-}
-
-/*
- * Function ircomm_ttp_data_request (self, userdata)
- *
- * Send IrCOMM data to IrTTP layer. Currently we do not try to combine
- * control data with pure data, so they will be sent as separate frames.
- * Should not be a big problem though, since control frames are rare. But
- * some of them are sent after connection establishment, so this can
- * increase the latency a bit.
- */
-static int ircomm_ttp_data_request(struct ircomm_cb *self,
- struct sk_buff *skb,
- int clen)
-{
- int ret;
-
- IRDA_ASSERT(skb != NULL, return -1;);
-
- pr_debug("%s(), clen=%d\n", __func__ , clen);
-
- /*
- * Insert clen field, currently we either send data only, or control
- * only frames, to make things easier and avoid queueing
- */
- IRDA_ASSERT(skb_headroom(skb) >= IRCOMM_HEADER_SIZE, return -1;);
-
- /* Don't forget to refcount it - see ircomm_tty_do_softint() */
- skb_get(skb);
-
- skb_push(skb, IRCOMM_HEADER_SIZE);
-
- skb->data[0] = clen;
-
- ret = irttp_data_request(self->tsap, skb);
- if (ret) {
- net_err_ratelimited("%s(), failed\n", __func__);
- /* irttp_data_request already free the packet */
- }
-
- return ret;
-}
-
-/*
- * Function ircomm_ttp_data_indication (instance, sap, skb)
- *
- * Incoming data
- *
- */
-static int ircomm_ttp_data_indication(void *instance, void *sap,
- struct sk_buff *skb)
-{
- struct ircomm_cb *self = (struct ircomm_cb *) instance;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
- IRDA_ASSERT(skb != NULL, return -1;);
-
- ircomm_do_event(self, IRCOMM_TTP_DATA_INDICATION, skb, NULL);
-
- /* Drop reference count - see ircomm_tty_data_indication(). */
- dev_kfree_skb(skb);
-
- return 0;
-}
-
-static void ircomm_ttp_connect_confirm(void *instance, void *sap,
- struct qos_info *qos,
- __u32 max_sdu_size,
- __u8 max_header_size,
- struct sk_buff *skb)
-{
- struct ircomm_cb *self = (struct ircomm_cb *) instance;
- struct ircomm_info info;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
- IRDA_ASSERT(skb != NULL, return;);
- IRDA_ASSERT(qos != NULL, goto out;);
-
- if (max_sdu_size != TTP_SAR_DISABLE) {
- net_err_ratelimited("%s(), SAR not allowed for IrCOMM!\n",
- __func__);
- goto out;
- }
-
- info.max_data_size = irttp_get_max_seg_size(self->tsap)
- - IRCOMM_HEADER_SIZE;
- info.max_header_size = max_header_size + IRCOMM_HEADER_SIZE;
- info.qos = qos;
-
- ircomm_do_event(self, IRCOMM_TTP_CONNECT_CONFIRM, skb, &info);
-
-out:
- /* Drop reference count - see ircomm_tty_connect_confirm(). */
- dev_kfree_skb(skb);
-}
-
-/*
- * Function ircomm_ttp_connect_indication (instance, sap, qos, max_sdu_size,
- * max_header_size, skb)
- *
- *
- *
- */
-static void ircomm_ttp_connect_indication(void *instance, void *sap,
- struct qos_info *qos,
- __u32 max_sdu_size,
- __u8 max_header_size,
- struct sk_buff *skb)
-{
- struct ircomm_cb *self = (struct ircomm_cb *)instance;
- struct ircomm_info info;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
- IRDA_ASSERT(skb != NULL, return;);
- IRDA_ASSERT(qos != NULL, goto out;);
-
- if (max_sdu_size != TTP_SAR_DISABLE) {
- net_err_ratelimited("%s(), SAR not allowed for IrCOMM!\n",
- __func__);
- goto out;
- }
-
- info.max_data_size = irttp_get_max_seg_size(self->tsap)
- - IRCOMM_HEADER_SIZE;
- info.max_header_size = max_header_size + IRCOMM_HEADER_SIZE;
- info.qos = qos;
-
- ircomm_do_event(self, IRCOMM_TTP_CONNECT_INDICATION, skb, &info);
-
-out:
- /* Drop reference count - see ircomm_tty_connect_indication(). */
- dev_kfree_skb(skb);
-}
-
-/*
- * Function ircomm_ttp_disconnect_request (self, userdata, info)
- *
- *
- *
- */
-static int ircomm_ttp_disconnect_request(struct ircomm_cb *self,
- struct sk_buff *userdata,
- struct ircomm_info *info)
-{
- int ret;
-
- /* Don't forget to refcount it - should be NULL anyway */
- if(userdata)
- skb_get(userdata);
-
- ret = irttp_disconnect_request(self->tsap, userdata, P_NORMAL);
-
- return ret;
-}
-
-/*
- * Function ircomm_ttp_disconnect_indication (instance, sap, reason, skb)
- *
- *
- *
- */
-static void ircomm_ttp_disconnect_indication(void *instance, void *sap,
- LM_REASON reason,
- struct sk_buff *skb)
-{
- struct ircomm_cb *self = (struct ircomm_cb *) instance;
- struct ircomm_info info;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
-
- info.reason = reason;
-
- ircomm_do_event(self, IRCOMM_TTP_DISCONNECT_INDICATION, skb, &info);
-
- /* Drop reference count - see ircomm_tty_disconnect_indication(). */
- if(skb)
- dev_kfree_skb(skb);
-}
-
-/*
- * Function ircomm_ttp_flow_indication (instance, sap, cmd)
- *
- * Layer below is telling us to start or stop the flow of data
- *
- */
-static void ircomm_ttp_flow_indication(void *instance, void *sap,
- LOCAL_FLOW cmd)
-{
- struct ircomm_cb *self = (struct ircomm_cb *) instance;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
-
- if (self->notify.flow_indication)
- self->notify.flow_indication(self->notify.instance, self, cmd);
-}
-
-
diff --git a/drivers/staging/irda/net/ircomm/ircomm_tty.c b/drivers/staging/irda/net/ircomm/ircomm_tty.c
deleted file mode 100644
index 473abfaffe7b..000000000000
--- a/drivers/staging/irda/net/ircomm/ircomm_tty.c
+++ /dev/null
@@ -1,1329 +0,0 @@
-/*********************************************************************
- *
- * Filename: ircomm_tty.c
- * Version: 1.0
- * Description: IrCOMM serial TTY driver
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sun Jun 6 21:00:56 1999
- * Modified at: Wed Feb 23 00:09:02 2000
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- * Sources: serial.c and previous IrCOMM work by Takahide Higuchi
- *
- * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
- * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/fs.h>
-#include <linux/slab.h>
-#include <linux/sched/signal.h>
-#include <linux/seq_file.h>
-#include <linux/termios.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/interrupt.h>
-#include <linux/device.h> /* for MODULE_ALIAS_CHARDEV_MAJOR */
-
-#include <linux/uaccess.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irmod.h>
-
-#include <net/irda/ircomm_core.h>
-#include <net/irda/ircomm_param.h>
-#include <net/irda/ircomm_tty_attach.h>
-#include <net/irda/ircomm_tty.h>
-
-static int ircomm_tty_install(struct tty_driver *driver,
- struct tty_struct *tty);
-static int ircomm_tty_open(struct tty_struct *tty, struct file *filp);
-static void ircomm_tty_close(struct tty_struct * tty, struct file *filp);
-static int ircomm_tty_write(struct tty_struct * tty,
- const unsigned char *buf, int count);
-static int ircomm_tty_write_room(struct tty_struct *tty);
-static void ircomm_tty_throttle(struct tty_struct *tty);
-static void ircomm_tty_unthrottle(struct tty_struct *tty);
-static int ircomm_tty_chars_in_buffer(struct tty_struct *tty);
-static void ircomm_tty_flush_buffer(struct tty_struct *tty);
-static void ircomm_tty_send_xchar(struct tty_struct *tty, char ch);
-static void ircomm_tty_wait_until_sent(struct tty_struct *tty, int timeout);
-static void ircomm_tty_hangup(struct tty_struct *tty);
-static void ircomm_tty_do_softint(struct work_struct *work);
-static void ircomm_tty_shutdown(struct ircomm_tty_cb *self);
-static void ircomm_tty_stop(struct tty_struct *tty);
-
-static int ircomm_tty_data_indication(void *instance, void *sap,
- struct sk_buff *skb);
-static int ircomm_tty_control_indication(void *instance, void *sap,
- struct sk_buff *skb);
-static void ircomm_tty_flow_indication(void *instance, void *sap,
- LOCAL_FLOW cmd);
-#ifdef CONFIG_PROC_FS
-static const struct file_operations ircomm_tty_proc_fops;
-#endif /* CONFIG_PROC_FS */
-static struct tty_driver *driver;
-
-static hashbin_t *ircomm_tty = NULL;
-
-static const struct tty_operations ops = {
- .install = ircomm_tty_install,
- .open = ircomm_tty_open,
- .close = ircomm_tty_close,
- .write = ircomm_tty_write,
- .write_room = ircomm_tty_write_room,
- .chars_in_buffer = ircomm_tty_chars_in_buffer,
- .flush_buffer = ircomm_tty_flush_buffer,
- .ioctl = ircomm_tty_ioctl, /* ircomm_tty_ioctl.c */
- .tiocmget = ircomm_tty_tiocmget, /* ircomm_tty_ioctl.c */
- .tiocmset = ircomm_tty_tiocmset, /* ircomm_tty_ioctl.c */
- .throttle = ircomm_tty_throttle,
- .unthrottle = ircomm_tty_unthrottle,
- .send_xchar = ircomm_tty_send_xchar,
- .set_termios = ircomm_tty_set_termios,
- .stop = ircomm_tty_stop,
- .start = ircomm_tty_start,
- .hangup = ircomm_tty_hangup,
- .wait_until_sent = ircomm_tty_wait_until_sent,
-#ifdef CONFIG_PROC_FS
- .proc_fops = &ircomm_tty_proc_fops,
-#endif /* CONFIG_PROC_FS */
-};
-
-static void ircomm_port_raise_dtr_rts(struct tty_port *port, int raise)
-{
- struct ircomm_tty_cb *self = container_of(port, struct ircomm_tty_cb,
- port);
- /*
- * Here, we use to lock those two guys, but as ircomm_param_request()
- * does it itself, I don't see the point (and I see the deadlock).
- * Jean II
- */
- if (raise)
- self->settings.dte |= IRCOMM_RTS | IRCOMM_DTR;
- else
- self->settings.dte &= ~(IRCOMM_RTS | IRCOMM_DTR);
-
- ircomm_param_request(self, IRCOMM_DTE, TRUE);
-}
-
-static int ircomm_port_carrier_raised(struct tty_port *port)
-{
- struct ircomm_tty_cb *self = container_of(port, struct ircomm_tty_cb,
- port);
- return self->settings.dce & IRCOMM_CD;
-}
-
-static const struct tty_port_operations ircomm_port_ops = {
- .dtr_rts = ircomm_port_raise_dtr_rts,
- .carrier_raised = ircomm_port_carrier_raised,
-};
-
-/*
- * Function ircomm_tty_init()
- *
- * Init IrCOMM TTY layer/driver
- *
- */
-static int __init ircomm_tty_init(void)
-{
- driver = alloc_tty_driver(IRCOMM_TTY_PORTS);
- if (!driver)
- return -ENOMEM;
- ircomm_tty = hashbin_new(HB_LOCK);
- if (ircomm_tty == NULL) {
- net_err_ratelimited("%s(), can't allocate hashbin!\n",
- __func__);
- put_tty_driver(driver);
- return -ENOMEM;
- }
-
- driver->driver_name = "ircomm";
- driver->name = "ircomm";
- driver->major = IRCOMM_TTY_MAJOR;
- driver->minor_start = IRCOMM_TTY_MINOR;
- driver->type = TTY_DRIVER_TYPE_SERIAL;
- driver->subtype = SERIAL_TYPE_NORMAL;
- driver->init_termios = tty_std_termios;
- driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
- driver->flags = TTY_DRIVER_REAL_RAW;
- tty_set_operations(driver, &ops);
- if (tty_register_driver(driver)) {
- net_err_ratelimited("%s(): Couldn't register serial driver\n",
- __func__);
- put_tty_driver(driver);
- return -1;
- }
- return 0;
-}
-
-static void __exit __ircomm_tty_cleanup(struct ircomm_tty_cb *self)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
- ircomm_tty_shutdown(self);
-
- self->magic = 0;
- tty_port_destroy(&self->port);
- kfree(self);
-}
-
-/*
- * Function ircomm_tty_cleanup ()
- *
- * Remove IrCOMM TTY layer/driver
- *
- */
-static void __exit ircomm_tty_cleanup(void)
-{
- int ret;
-
- ret = tty_unregister_driver(driver);
- if (ret) {
- net_err_ratelimited("%s(), failed to unregister driver\n",
- __func__);
- return;
- }
-
- hashbin_delete(ircomm_tty, (FREE_FUNC) __ircomm_tty_cleanup);
- put_tty_driver(driver);
-}
-
-/*
- * Function ircomm_startup (self)
- *
- *
- *
- */
-static int ircomm_tty_startup(struct ircomm_tty_cb *self)
-{
- notify_t notify;
- int ret = -ENODEV;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
- /* Check if already open */
- if (tty_port_initialized(&self->port)) {
- pr_debug("%s(), already open so break out!\n", __func__);
- return 0;
- }
- tty_port_set_initialized(&self->port, 1);
-
- /* Register with IrCOMM */
- irda_notify_init(&notify);
- /* These callbacks we must handle ourselves */
- notify.data_indication = ircomm_tty_data_indication;
- notify.udata_indication = ircomm_tty_control_indication;
- notify.flow_indication = ircomm_tty_flow_indication;
-
- /* Use the ircomm_tty interface for these ones */
- notify.disconnect_indication = ircomm_tty_disconnect_indication;
- notify.connect_confirm = ircomm_tty_connect_confirm;
- notify.connect_indication = ircomm_tty_connect_indication;
- strlcpy(notify.name, "ircomm_tty", sizeof(notify.name));
- notify.instance = self;
-
- if (!self->ircomm) {
- self->ircomm = ircomm_open(&notify, self->service_type,
- self->line);
- }
- if (!self->ircomm)
- goto err;
-
- self->slsap_sel = self->ircomm->slsap_sel;
-
- /* Connect IrCOMM link with remote device */
- ret = ircomm_tty_attach_cable(self);
- if (ret < 0) {
- net_err_ratelimited("%s(), error attaching cable!\n", __func__);
- goto err;
- }
-
- return 0;
-err:
- tty_port_set_initialized(&self->port, 0);
- return ret;
-}
-
-/*
- * Function ircomm_block_til_ready (self, filp)
- *
- *
- *
- */
-static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self,
- struct tty_struct *tty, struct file *filp)
-{
- struct tty_port *port = &self->port;
- DECLARE_WAITQUEUE(wait, current);
- int retval;
- int do_clocal = 0;
- unsigned long flags;
-
- /*
- * If non-blocking mode is set, or the port is not enabled,
- * then make the check up front and then exit.
- */
- if (tty_io_error(tty)) {
- tty_port_set_active(port, 1);
- return 0;
- }
-
- if (filp->f_flags & O_NONBLOCK) {
- /* nonblock mode is set */
- if (C_BAUD(tty))
- tty_port_raise_dtr_rts(port);
- tty_port_set_active(port, 1);
- pr_debug("%s(), O_NONBLOCK requested!\n", __func__);
- return 0;
- }
-
- if (C_CLOCAL(tty)) {
- pr_debug("%s(), doing CLOCAL!\n", __func__);
- do_clocal = 1;
- }
-
- /* Wait for carrier detect and the line to become
- * free (i.e., not in use by the callout). While we are in
- * this loop, port->count is dropped by one, so that
- * mgsl_close() knows when to free things. We restore it upon
- * exit, either normal or abnormal.
- */
-
- retval = 0;
- add_wait_queue(&port->open_wait, &wait);
-
- pr_debug("%s(%d):block_til_ready before block on %s open_count=%d\n",
- __FILE__, __LINE__, tty->driver->name, port->count);
-
- spin_lock_irqsave(&port->lock, flags);
- port->count--;
- port->blocked_open++;
- spin_unlock_irqrestore(&port->lock, flags);
-
- while (1) {
- if (C_BAUD(tty) && tty_port_initialized(port))
- tty_port_raise_dtr_rts(port);
-
- set_current_state(TASK_INTERRUPTIBLE);
-
- if (tty_hung_up_p(filp) || !tty_port_initialized(port)) {
- retval = (port->flags & ASYNC_HUP_NOTIFY) ?
- -EAGAIN : -ERESTARTSYS;
- break;
- }
-
- /*
- * Check if link is ready now. Even if CLOCAL is
- * specified, we cannot return before the IrCOMM link is
- * ready
- */
- if ((do_clocal || tty_port_carrier_raised(port)) &&
- self->state == IRCOMM_TTY_READY)
- {
- break;
- }
-
- if (signal_pending(current)) {
- retval = -ERESTARTSYS;
- break;
- }
-
- pr_debug("%s(%d):block_til_ready blocking on %s open_count=%d\n",
- __FILE__, __LINE__, tty->driver->name, port->count);
-
- schedule();
- }
-
- __set_current_state(TASK_RUNNING);
- remove_wait_queue(&port->open_wait, &wait);
-
- spin_lock_irqsave(&port->lock, flags);
- if (!tty_hung_up_p(filp))
- port->count++;
- port->blocked_open--;
- spin_unlock_irqrestore(&port->lock, flags);
-
- pr_debug("%s(%d):block_til_ready after blocking on %s open_count=%d\n",
- __FILE__, __LINE__, tty->driver->name, port->count);
-
- if (!retval)
- tty_port_set_active(port, 1);
-
- return retval;
-}
-
-
-static int ircomm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
-{
- struct ircomm_tty_cb *self;
- unsigned int line = tty->index;
-
- /* Check if instance already exists */
- self = hashbin_lock_find(ircomm_tty, line, NULL);
- if (!self) {
- /* No, so make new instance */
- self = kzalloc(sizeof(struct ircomm_tty_cb), GFP_KERNEL);
- if (self == NULL)
- return -ENOMEM;
-
- tty_port_init(&self->port);
- self->port.ops = &ircomm_port_ops;
- self->magic = IRCOMM_TTY_MAGIC;
- self->flow = FLOW_STOP;
-
- self->line = line;
- INIT_WORK(&self->tqueue, ircomm_tty_do_softint);
- self->max_header_size = IRCOMM_TTY_HDR_UNINITIALISED;
- self->max_data_size = IRCOMM_TTY_DATA_UNINITIALISED;
-
- /* Init some important stuff */
- timer_setup(&self->watchdog_timer, NULL, 0);
- spin_lock_init(&self->spinlock);
-
- /*
- * Force TTY into raw mode by default which is usually what
- * we want for IrCOMM and IrLPT. This way applications will
- * not have to twiddle with printcap etc.
- *
- * Note this is completely usafe and doesn't work properly
- */
- tty->termios.c_iflag = 0;
- tty->termios.c_oflag = 0;
-
- /* Insert into hash */
- hashbin_insert(ircomm_tty, (irda_queue_t *) self, line, NULL);
- }
-
- tty->driver_data = self;
-
- return tty_port_install(&self->port, driver, tty);
-}
-
-/*
- * Function ircomm_tty_open (tty, filp)
- *
- * This routine is called when a particular tty device is opened. This
- * routine is mandatory; if this routine is not filled in, the attempted
- * open will fail with ENODEV.
- */
-static int ircomm_tty_open(struct tty_struct *tty, struct file *filp)
-{
- struct ircomm_tty_cb *self = tty->driver_data;
- unsigned long flags;
- int ret;
-
- /* ++ is not atomic, so this should be protected - Jean II */
- spin_lock_irqsave(&self->port.lock, flags);
- self->port.count++;
- spin_unlock_irqrestore(&self->port.lock, flags);
- tty_port_tty_set(&self->port, tty);
-
- pr_debug("%s(), %s%d, count = %d\n", __func__ , tty->driver->name,
- self->line, self->port.count);
-
- /* Not really used by us, but lets do it anyway */
- self->port.low_latency = (self->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0;
-
- /* Check if this is a "normal" ircomm device, or an irlpt device */
- if (self->line < 0x10) {
- self->service_type = IRCOMM_3_WIRE | IRCOMM_9_WIRE;
- self->settings.service_type = IRCOMM_9_WIRE; /* 9 wire as default */
- /* Jan Kiszka -> add DSR/RI -> Conform to IrCOMM spec */
- self->settings.dce = IRCOMM_CTS | IRCOMM_CD | IRCOMM_DSR | IRCOMM_RI; /* Default line settings */
- pr_debug("%s(), IrCOMM device\n", __func__);
- } else {
- pr_debug("%s(), IrLPT device\n", __func__);
- self->service_type = IRCOMM_3_WIRE_RAW;
- self->settings.service_type = IRCOMM_3_WIRE_RAW; /* Default */
- }
-
- ret = ircomm_tty_startup(self);
- if (ret)
- return ret;
-
- ret = ircomm_tty_block_til_ready(self, tty, filp);
- if (ret) {
- pr_debug("%s(), returning after block_til_ready with %d\n",
- __func__, ret);
-
- return ret;
- }
- return 0;
-}
-
-/*
- * Function ircomm_tty_close (tty, filp)
- *
- * This routine is called when a particular tty device is closed.
- *
- */
-static void ircomm_tty_close(struct tty_struct *tty, struct file *filp)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
- struct tty_port *port = &self->port;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
- if (tty_port_close_start(port, tty, filp) == 0)
- return;
-
- ircomm_tty_shutdown(self);
-
- tty_driver_flush_buffer(tty);
-
- tty_port_close_end(port, tty);
- tty_port_tty_set(port, NULL);
-}
-
-/*
- * Function ircomm_tty_flush_buffer (tty)
- *
- *
- *
- */
-static void ircomm_tty_flush_buffer(struct tty_struct *tty)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
- /*
- * Let do_softint() do this to avoid race condition with
- * do_softint() ;-)
- */
- schedule_work(&self->tqueue);
-}
-
-/*
- * Function ircomm_tty_do_softint (work)
- *
- * We use this routine to give the write wakeup to the user at at a
- * safe time (as fast as possible after write have completed). This
- * can be compared to the Tx interrupt.
- */
-static void ircomm_tty_do_softint(struct work_struct *work)
-{
- struct ircomm_tty_cb *self =
- container_of(work, struct ircomm_tty_cb, tqueue);
- struct tty_struct *tty;
- unsigned long flags;
- struct sk_buff *skb, *ctrl_skb;
-
- if (!self || self->magic != IRCOMM_TTY_MAGIC)
- return;
-
- tty = tty_port_tty_get(&self->port);
- if (!tty)
- return;
-
- /* Unlink control buffer */
- spin_lock_irqsave(&self->spinlock, flags);
-
- ctrl_skb = self->ctrl_skb;
- self->ctrl_skb = NULL;
-
- spin_unlock_irqrestore(&self->spinlock, flags);
-
- /* Flush control buffer if any */
- if(ctrl_skb) {
- if(self->flow == FLOW_START)
- ircomm_control_request(self->ircomm, ctrl_skb);
- /* Drop reference count - see ircomm_ttp_data_request(). */
- dev_kfree_skb(ctrl_skb);
- }
-
- if (tty->hw_stopped)
- goto put;
-
- /* Unlink transmit buffer */
- spin_lock_irqsave(&self->spinlock, flags);
-
- skb = self->tx_skb;
- self->tx_skb = NULL;
-
- spin_unlock_irqrestore(&self->spinlock, flags);
-
- /* Flush transmit buffer if any */
- if (skb) {
- ircomm_tty_do_event(self, IRCOMM_TTY_DATA_REQUEST, skb, NULL);
- /* Drop reference count - see ircomm_ttp_data_request(). */
- dev_kfree_skb(skb);
- }
-
- /* Check if user (still) wants to be waken up */
- tty_wakeup(tty);
-put:
- tty_kref_put(tty);
-}
-
-/*
- * Function ircomm_tty_write (tty, buf, count)
- *
- * This routine is called by the kernel to write a series of characters
- * to the tty device. The characters may come from user space or kernel
- * space. This routine will return the number of characters actually
- * accepted for writing. This routine is mandatory.
- */
-static int ircomm_tty_write(struct tty_struct *tty,
- const unsigned char *buf, int count)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
- unsigned long flags;
- struct sk_buff *skb;
- int tailroom = 0;
- int len = 0;
- int size;
-
- pr_debug("%s(), count=%d, hw_stopped=%d\n", __func__ , count,
- tty->hw_stopped);
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
- /* We may receive packets from the TTY even before we have finished
- * our setup. Not cool.
- * The problem is that we don't know the final header and data size
- * to create the proper skb, so any skb we would create would have
- * bogus header and data size, so need care.
- * We use a bogus header size to safely detect this condition.
- * Another problem is that hw_stopped was set to 0 way before it
- * should be, so we would drop this skb. It should now be fixed.
- * One option is to not accept data until we are properly setup.
- * But, I suspect that when it happens, the ppp line discipline
- * just "drops" the data, which might screw up connect scripts.
- * The second option is to create a "safe skb", with large header
- * and small size (see ircomm_tty_open() for values).
- * We just need to make sure that when the real values get filled,
- * we don't mess up the original "safe skb" (see tx_data_size).
- * Jean II */
- if (self->max_header_size == IRCOMM_TTY_HDR_UNINITIALISED) {
- pr_debug("%s() : not initialised\n", __func__);
-#ifdef IRCOMM_NO_TX_BEFORE_INIT
- /* We didn't consume anything, TTY will retry */
- return 0;
-#endif
- }
-
- if (count < 1)
- return 0;
-
- /* Protect our manipulation of self->tx_skb and related */
- spin_lock_irqsave(&self->spinlock, flags);
-
- /* Fetch current transmit buffer */
- skb = self->tx_skb;
-
- /*
- * Send out all the data we get, possibly as multiple fragmented
- * frames, but this will only happen if the data is larger than the
- * max data size. The normal case however is just the opposite, and
- * this function may be called multiple times, and will then actually
- * defragment the data and send it out as one packet as soon as
- * possible, but at a safer point in time
- */
- while (count) {
- size = count;
-
- /* Adjust data size to the max data size */
- if (size > self->max_data_size)
- size = self->max_data_size;
-
- /*
- * Do we already have a buffer ready for transmit, or do
- * we need to allocate a new frame
- */
- if (skb) {
- /*
- * Any room for more data at the end of the current
- * transmit buffer? Cannot use skb_tailroom, since
- * dev_alloc_skb gives us a larger skb than we
- * requested
- * Note : use tx_data_size, because max_data_size
- * may have changed and we don't want to overwrite
- * the skb. - Jean II
- */
- if ((tailroom = (self->tx_data_size - skb->len)) > 0) {
- /* Adjust data to tailroom */
- if (size > tailroom)
- size = tailroom;
- } else {
- /*
- * Current transmit frame is full, so break
- * out, so we can send it as soon as possible
- */
- break;
- }
- } else {
- /* Prepare a full sized frame */
- skb = alloc_skb(self->max_data_size+
- self->max_header_size,
- GFP_ATOMIC);
- if (!skb) {
- spin_unlock_irqrestore(&self->spinlock, flags);
- return -ENOBUFS;
- }
- skb_reserve(skb, self->max_header_size);
- self->tx_skb = skb;
- /* Remember skb size because max_data_size may
- * change later on - Jean II */
- self->tx_data_size = self->max_data_size;
- }
-
- /* Copy data */
- skb_put_data(skb, buf + len, size);
-
- count -= size;
- len += size;
- }
-
- spin_unlock_irqrestore(&self->spinlock, flags);
-
- /*
- * Schedule a new thread which will transmit the frame as soon
- * as possible, but at a safe point in time. We do this so the
- * "user" can give us data multiple times, as PPP does (because of
- * its 256 byte tx buffer). We will then defragment and send out
- * all this data as one single packet.
- */
- schedule_work(&self->tqueue);
-
- return len;
-}
-
-/*
- * Function ircomm_tty_write_room (tty)
- *
- * This routine returns the numbers of characters the tty driver will
- * accept for queuing to be written. This number is subject to change as
- * output buffers get emptied, or if the output flow control is acted.
- */
-static int ircomm_tty_write_room(struct tty_struct *tty)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
- unsigned long flags;
- int ret;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
-#ifdef IRCOMM_NO_TX_BEFORE_INIT
- /* max_header_size tells us if the channel is initialised or not. */
- if (self->max_header_size == IRCOMM_TTY_HDR_UNINITIALISED)
- /* Don't bother us yet */
- return 0;
-#endif
-
- /* Check if we are allowed to transmit any data.
- * hw_stopped is the regular flow control.
- * Jean II */
- if (tty->hw_stopped)
- ret = 0;
- else {
- spin_lock_irqsave(&self->spinlock, flags);
- if (self->tx_skb)
- ret = self->tx_data_size - self->tx_skb->len;
- else
- ret = self->max_data_size;
- spin_unlock_irqrestore(&self->spinlock, flags);
- }
- pr_debug("%s(), ret=%d\n", __func__ , ret);
-
- return ret;
-}
-
-/*
- * Function ircomm_tty_wait_until_sent (tty, timeout)
- *
- * This routine waits until the device has written out all of the
- * characters in its transmitter FIFO.
- */
-static void ircomm_tty_wait_until_sent(struct tty_struct *tty, int timeout)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
- unsigned long orig_jiffies, poll_time;
- unsigned long flags;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
- orig_jiffies = jiffies;
-
- /* Set poll time to 200 ms */
- poll_time = msecs_to_jiffies(200);
- if (timeout)
- poll_time = min_t(unsigned long, timeout, poll_time);
-
- spin_lock_irqsave(&self->spinlock, flags);
- while (self->tx_skb && self->tx_skb->len) {
- spin_unlock_irqrestore(&self->spinlock, flags);
- schedule_timeout_interruptible(poll_time);
- spin_lock_irqsave(&self->spinlock, flags);
- if (signal_pending(current))
- break;
- if (timeout && time_after(jiffies, orig_jiffies + timeout))
- break;
- }
- spin_unlock_irqrestore(&self->spinlock, flags);
- __set_current_state(TASK_RUNNING);
-}
-
-/*
- * Function ircomm_tty_throttle (tty)
- *
- * This routine notifies the tty driver that input buffers for the line
- * discipline are close to full, and it should somehow signal that no
- * more characters should be sent to the tty.
- */
-static void ircomm_tty_throttle(struct tty_struct *tty)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
- /* Software flow control? */
- if (I_IXOFF(tty))
- ircomm_tty_send_xchar(tty, STOP_CHAR(tty));
-
- /* Hardware flow control? */
- if (C_CRTSCTS(tty)) {
- self->settings.dte &= ~IRCOMM_RTS;
- self->settings.dte |= IRCOMM_DELTA_RTS;
-
- ircomm_param_request(self, IRCOMM_DTE, TRUE);
- }
-
- ircomm_flow_request(self->ircomm, FLOW_STOP);
-}
-
-/*
- * Function ircomm_tty_unthrottle (tty)
- *
- * This routine notifies the tty drivers that it should signals that
- * characters can now be sent to the tty without fear of overrunning the
- * input buffers of the line disciplines.
- */
-static void ircomm_tty_unthrottle(struct tty_struct *tty)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
- /* Using software flow control? */
- if (I_IXOFF(tty))
- ircomm_tty_send_xchar(tty, START_CHAR(tty));
-
- /* Using hardware flow control? */
- if (C_CRTSCTS(tty)) {
- self->settings.dte |= (IRCOMM_RTS|IRCOMM_DELTA_RTS);
-
- ircomm_param_request(self, IRCOMM_DTE, TRUE);
- pr_debug("%s(), FLOW_START\n", __func__);
- }
- ircomm_flow_request(self->ircomm, FLOW_START);
-}
-
-/*
- * Function ircomm_tty_chars_in_buffer (tty)
- *
- * Indicates if there are any data in the buffer
- *
- */
-static int ircomm_tty_chars_in_buffer(struct tty_struct *tty)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
- unsigned long flags;
- int len = 0;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
- spin_lock_irqsave(&self->spinlock, flags);
-
- if (self->tx_skb)
- len = self->tx_skb->len;
-
- spin_unlock_irqrestore(&self->spinlock, flags);
-
- return len;
-}
-
-static void ircomm_tty_shutdown(struct ircomm_tty_cb *self)
-{
- unsigned long flags;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
- if (!tty_port_initialized(&self->port))
- return;
- tty_port_set_initialized(&self->port, 0);
-
- ircomm_tty_detach_cable(self);
-
- spin_lock_irqsave(&self->spinlock, flags);
-
- del_timer(&self->watchdog_timer);
-
- /* Free parameter buffer */
- if (self->ctrl_skb) {
- dev_kfree_skb(self->ctrl_skb);
- self->ctrl_skb = NULL;
- }
-
- /* Free transmit buffer */
- if (self->tx_skb) {
- dev_kfree_skb(self->tx_skb);
- self->tx_skb = NULL;
- }
-
- if (self->ircomm) {
- ircomm_close(self->ircomm);
- self->ircomm = NULL;
- }
-
- spin_unlock_irqrestore(&self->spinlock, flags);
-}
-
-/*
- * Function ircomm_tty_hangup (tty)
- *
- * This routine notifies the tty driver that it should hangup the tty
- * device.
- *
- */
-static void ircomm_tty_hangup(struct tty_struct *tty)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
- struct tty_port *port = &self->port;
- unsigned long flags;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
- /* ircomm_tty_flush_buffer(tty); */
- ircomm_tty_shutdown(self);
-
- spin_lock_irqsave(&port->lock, flags);
- if (port->tty) {
- set_bit(TTY_IO_ERROR, &port->tty->flags);
- tty_kref_put(port->tty);
- }
- port->tty = NULL;
- port->count = 0;
- spin_unlock_irqrestore(&port->lock, flags);
- tty_port_set_active(port, 0);
-
- wake_up_interruptible(&port->open_wait);
-}
-
-/*
- * Function ircomm_tty_send_xchar (tty, ch)
- *
- * This routine is used to send a high-priority XON/XOFF character to
- * the device.
- */
-static void ircomm_tty_send_xchar(struct tty_struct *tty, char ch)
-{
- pr_debug("%s(), not impl\n", __func__);
-}
-
-/*
- * Function ircomm_tty_start (tty)
- *
- * This routine notifies the tty driver that it resume sending
- * characters to the tty device.
- */
-void ircomm_tty_start(struct tty_struct *tty)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
-
- ircomm_flow_request(self->ircomm, FLOW_START);
-}
-
-/*
- * Function ircomm_tty_stop (tty)
- *
- * This routine notifies the tty driver that it should stop outputting
- * characters to the tty device.
- */
-static void ircomm_tty_stop(struct tty_struct *tty)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
- ircomm_flow_request(self->ircomm, FLOW_STOP);
-}
-
-/*
- * Function ircomm_check_modem_status (self)
- *
- * Check for any changes in the DCE's line settings. This function should
- * be called whenever the dce parameter settings changes, to update the
- * flow control settings and other things
- */
-void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self)
-{
- struct tty_struct *tty;
- int status;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
- tty = tty_port_tty_get(&self->port);
-
- status = self->settings.dce;
-
- if (status & IRCOMM_DCE_DELTA_ANY) {
- /*wake_up_interruptible(&self->delta_msr_wait);*/
- }
- if (tty_port_check_carrier(&self->port) && (status & IRCOMM_DELTA_CD)) {
- pr_debug("%s(), ircomm%d CD now %s...\n", __func__ , self->line,
- (status & IRCOMM_CD) ? "on" : "off");
-
- if (status & IRCOMM_CD) {
- wake_up_interruptible(&self->port.open_wait);
- } else {
- pr_debug("%s(), Doing serial hangup..\n", __func__);
- if (tty)
- tty_hangup(tty);
-
- /* Hangup will remote the tty, so better break out */
- goto put;
- }
- }
- if (tty && tty_port_cts_enabled(&self->port)) {
- if (tty->hw_stopped) {
- if (status & IRCOMM_CTS) {
- pr_debug("%s(), CTS tx start...\n", __func__);
- tty->hw_stopped = 0;
-
- /* Wake up processes blocked on open */
- wake_up_interruptible(&self->port.open_wait);
-
- schedule_work(&self->tqueue);
- goto put;
- }
- } else {
- if (!(status & IRCOMM_CTS)) {
- pr_debug("%s(), CTS tx stop...\n", __func__);
- tty->hw_stopped = 1;
- }
- }
- }
-put:
- tty_kref_put(tty);
-}
-
-/*
- * Function ircomm_tty_data_indication (instance, sap, skb)
- *
- * Handle incoming data, and deliver it to the line discipline
- *
- */
-static int ircomm_tty_data_indication(void *instance, void *sap,
- struct sk_buff *skb)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
- struct tty_struct *tty;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
- IRDA_ASSERT(skb != NULL, return -1;);
-
- tty = tty_port_tty_get(&self->port);
- if (!tty) {
- pr_debug("%s(), no tty!\n", __func__);
- return 0;
- }
-
- /*
- * If we receive data when hardware is stopped then something is wrong.
- * We try to poll the peers line settings to check if we are up todate.
- * Devices like WinCE can do this, and since they don't send any
- * params, we can just as well declare the hardware for running.
- */
- if (tty->hw_stopped && (self->flow == FLOW_START)) {
- pr_debug("%s(), polling for line settings!\n", __func__);
- ircomm_param_request(self, IRCOMM_POLL, TRUE);
-
- /* We can just as well declare the hardware for running */
- ircomm_tty_send_initial_parameters(self);
- ircomm_tty_link_established(self);
- }
- tty_kref_put(tty);
-
- /*
- * Use flip buffer functions since the code may be called from interrupt
- * context
- */
- tty_insert_flip_string(&self->port, skb->data, skb->len);
- tty_flip_buffer_push(&self->port);
-
- /* No need to kfree_skb - see ircomm_ttp_data_indication() */
-
- return 0;
-}
-
-/*
- * Function ircomm_tty_control_indication (instance, sap, skb)
- *
- * Parse all incoming parameters (easy!)
- *
- */
-static int ircomm_tty_control_indication(void *instance, void *sap,
- struct sk_buff *skb)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
- int clen;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
- IRDA_ASSERT(skb != NULL, return -1;);
-
- clen = skb->data[0];
-
- irda_param_extract_all(self, skb->data+1, IRDA_MIN(skb->len-1, clen),
- &ircomm_param_info);
-
- /* No need to kfree_skb - see ircomm_control_indication() */
-
- return 0;
-}
-
-/*
- * Function ircomm_tty_flow_indication (instance, sap, cmd)
- *
- * This function is called by IrTTP when it wants us to slow down the
- * transmission of data. We just mark the hardware as stopped, and wait
- * for IrTTP to notify us that things are OK again.
- */
-static void ircomm_tty_flow_indication(void *instance, void *sap,
- LOCAL_FLOW cmd)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
- struct tty_struct *tty;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
- tty = tty_port_tty_get(&self->port);
-
- switch (cmd) {
- case FLOW_START:
- pr_debug("%s(), hw start!\n", __func__);
- if (tty)
- tty->hw_stopped = 0;
-
- /* ircomm_tty_do_softint will take care of the rest */
- schedule_work(&self->tqueue);
- break;
- default: /* If we get here, something is very wrong, better stop */
- case FLOW_STOP:
- pr_debug("%s(), hw stopped!\n", __func__);
- if (tty)
- tty->hw_stopped = 1;
- break;
- }
-
- tty_kref_put(tty);
- self->flow = cmd;
-}
-
-#ifdef CONFIG_PROC_FS
-static void ircomm_tty_line_info(struct ircomm_tty_cb *self, struct seq_file *m)
-{
- struct tty_struct *tty;
- char sep;
-
- seq_printf(m, "State: %s\n", ircomm_tty_state[self->state]);
-
- seq_puts(m, "Service type: ");
- if (self->service_type & IRCOMM_9_WIRE)
- seq_puts(m, "9_WIRE");
- else if (self->service_type & IRCOMM_3_WIRE)
- seq_puts(m, "3_WIRE");
- else if (self->service_type & IRCOMM_3_WIRE_RAW)
- seq_puts(m, "3_WIRE_RAW");
- else
- seq_puts(m, "No common service type!\n");
- seq_putc(m, '\n');
-
- seq_printf(m, "Port name: %s\n", self->settings.port_name);
-
- seq_printf(m, "DTE status:");
- sep = ' ';
- if (self->settings.dte & IRCOMM_RTS) {
- seq_printf(m, "%cRTS", sep);
- sep = '|';
- }
- if (self->settings.dte & IRCOMM_DTR) {
- seq_printf(m, "%cDTR", sep);
- sep = '|';
- }
- seq_putc(m, '\n');
-
- seq_puts(m, "DCE status:");
- sep = ' ';
- if (self->settings.dce & IRCOMM_CTS) {
- seq_printf(m, "%cCTS", sep);
- sep = '|';
- }
- if (self->settings.dce & IRCOMM_DSR) {
- seq_printf(m, "%cDSR", sep);
- sep = '|';
- }
- if (self->settings.dce & IRCOMM_CD) {
- seq_printf(m, "%cCD", sep);
- sep = '|';
- }
- if (self->settings.dce & IRCOMM_RI) {
- seq_printf(m, "%cRI", sep);
- sep = '|';
- }
- seq_putc(m, '\n');
-
- seq_puts(m, "Configuration: ");
- if (!self->settings.null_modem)
- seq_puts(m, "DTE <-> DCE\n");
- else
- seq_puts(m, "DTE <-> DTE (null modem emulation)\n");
-
- seq_printf(m, "Data rate: %d\n", self->settings.data_rate);
-
- seq_puts(m, "Flow control:");
- sep = ' ';
- if (self->settings.flow_control & IRCOMM_XON_XOFF_IN) {
- seq_printf(m, "%cXON_XOFF_IN", sep);
- sep = '|';
- }
- if (self->settings.flow_control & IRCOMM_XON_XOFF_OUT) {
- seq_printf(m, "%cXON_XOFF_OUT", sep);
- sep = '|';
- }
- if (self->settings.flow_control & IRCOMM_RTS_CTS_IN) {
- seq_printf(m, "%cRTS_CTS_IN", sep);
- sep = '|';
- }
- if (self->settings.flow_control & IRCOMM_RTS_CTS_OUT) {
- seq_printf(m, "%cRTS_CTS_OUT", sep);
- sep = '|';
- }
- if (self->settings.flow_control & IRCOMM_DSR_DTR_IN) {
- seq_printf(m, "%cDSR_DTR_IN", sep);
- sep = '|';
- }
- if (self->settings.flow_control & IRCOMM_DSR_DTR_OUT) {
- seq_printf(m, "%cDSR_DTR_OUT", sep);
- sep = '|';
- }
- if (self->settings.flow_control & IRCOMM_ENQ_ACK_IN) {
- seq_printf(m, "%cENQ_ACK_IN", sep);
- sep = '|';
- }
- if (self->settings.flow_control & IRCOMM_ENQ_ACK_OUT) {
- seq_printf(m, "%cENQ_ACK_OUT", sep);
- sep = '|';
- }
- seq_putc(m, '\n');
-
- seq_puts(m, "Flags:");
- sep = ' ';
- if (tty_port_cts_enabled(&self->port)) {
- seq_printf(m, "%cASYNC_CTS_FLOW", sep);
- sep = '|';
- }
- if (tty_port_check_carrier(&self->port)) {
- seq_printf(m, "%cASYNC_CHECK_CD", sep);
- sep = '|';
- }
- if (tty_port_initialized(&self->port)) {
- seq_printf(m, "%cASYNC_INITIALIZED", sep);
- sep = '|';
- }
- if (self->port.flags & ASYNC_LOW_LATENCY) {
- seq_printf(m, "%cASYNC_LOW_LATENCY", sep);
- sep = '|';
- }
- if (tty_port_active(&self->port)) {
- seq_printf(m, "%cASYNC_NORMAL_ACTIVE", sep);
- sep = '|';
- }
- seq_putc(m, '\n');
-
- seq_printf(m, "Role: %s\n", self->client ? "client" : "server");
- seq_printf(m, "Open count: %d\n", self->port.count);
- seq_printf(m, "Max data size: %d\n", self->max_data_size);
- seq_printf(m, "Max header size: %d\n", self->max_header_size);
-
- tty = tty_port_tty_get(&self->port);
- if (tty) {
- seq_printf(m, "Hardware: %s\n",
- tty->hw_stopped ? "Stopped" : "Running");
- tty_kref_put(tty);
- }
-}
-
-static int ircomm_tty_proc_show(struct seq_file *m, void *v)
-{
- struct ircomm_tty_cb *self;
- unsigned long flags;
-
- spin_lock_irqsave(&ircomm_tty->hb_spinlock, flags);
-
- self = (struct ircomm_tty_cb *) hashbin_get_first(ircomm_tty);
- while (self != NULL) {
- if (self->magic != IRCOMM_TTY_MAGIC)
- break;
-
- ircomm_tty_line_info(self, m);
- self = (struct ircomm_tty_cb *) hashbin_get_next(ircomm_tty);
- }
- spin_unlock_irqrestore(&ircomm_tty->hb_spinlock, flags);
- return 0;
-}
-
-static int ircomm_tty_proc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, ircomm_tty_proc_show, NULL);
-}
-
-static const struct file_operations ircomm_tty_proc_fops = {
- .owner = THIS_MODULE,
- .open = ircomm_tty_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-#endif /* CONFIG_PROC_FS */
-
-MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
-MODULE_DESCRIPTION("IrCOMM serial TTY driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_CHARDEV_MAJOR(IRCOMM_TTY_MAJOR);
-
-module_init(ircomm_tty_init);
-module_exit(ircomm_tty_cleanup);
diff --git a/drivers/staging/irda/net/ircomm/ircomm_tty_attach.c b/drivers/staging/irda/net/ircomm/ircomm_tty_attach.c
deleted file mode 100644
index e2d5ce8ba0db..000000000000
--- a/drivers/staging/irda/net/ircomm/ircomm_tty_attach.c
+++ /dev/null
@@ -1,987 +0,0 @@
-/*********************************************************************
- *
- * Filename: ircomm_tty_attach.c
- * Version:
- * Description: Code for attaching the serial driver to IrCOMM
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sat Jun 5 17:42:00 1999
- * Modified at: Tue Jan 4 14:20:49 2000
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
- * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/init.h>
-#include <linux/sched.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/iriap.h>
-#include <net/irda/irttp.h>
-#include <net/irda/irias_object.h>
-#include <net/irda/parameters.h>
-
-#include <net/irda/ircomm_core.h>
-#include <net/irda/ircomm_param.h>
-#include <net/irda/ircomm_event.h>
-
-#include <net/irda/ircomm_tty.h>
-#include <net/irda/ircomm_tty_attach.h>
-
-static void ircomm_tty_ias_register(struct ircomm_tty_cb *self);
-static void ircomm_tty_discovery_indication(discinfo_t *discovery,
- DISCOVERY_MODE mode,
- void *priv);
-static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id,
- struct ias_value *value, void *priv);
-static void ircomm_tty_start_watchdog_timer(struct ircomm_tty_cb *self,
- int timeout);
-static void ircomm_tty_watchdog_timer_expired(struct timer_list *timer);
-
-static int ircomm_tty_state_idle(struct ircomm_tty_cb *self,
- IRCOMM_TTY_EVENT event,
- struct sk_buff *skb,
- struct ircomm_tty_info *info);
-static int ircomm_tty_state_search(struct ircomm_tty_cb *self,
- IRCOMM_TTY_EVENT event,
- struct sk_buff *skb,
- struct ircomm_tty_info *info);
-static int ircomm_tty_state_query_parameters(struct ircomm_tty_cb *self,
- IRCOMM_TTY_EVENT event,
- struct sk_buff *skb,
- struct ircomm_tty_info *info);
-static int ircomm_tty_state_query_lsap_sel(struct ircomm_tty_cb *self,
- IRCOMM_TTY_EVENT event,
- struct sk_buff *skb,
- struct ircomm_tty_info *info);
-static int ircomm_tty_state_setup(struct ircomm_tty_cb *self,
- IRCOMM_TTY_EVENT event,
- struct sk_buff *skb,
- struct ircomm_tty_info *info);
-static int ircomm_tty_state_ready(struct ircomm_tty_cb *self,
- IRCOMM_TTY_EVENT event,
- struct sk_buff *skb,
- struct ircomm_tty_info *info);
-
-const char *const ircomm_tty_state[] = {
- "IRCOMM_TTY_IDLE",
- "IRCOMM_TTY_SEARCH",
- "IRCOMM_TTY_QUERY_PARAMETERS",
- "IRCOMM_TTY_QUERY_LSAP_SEL",
- "IRCOMM_TTY_SETUP",
- "IRCOMM_TTY_READY",
- "*** ERROR *** ",
-};
-
-static const char *const ircomm_tty_event[] __maybe_unused = {
- "IRCOMM_TTY_ATTACH_CABLE",
- "IRCOMM_TTY_DETACH_CABLE",
- "IRCOMM_TTY_DATA_REQUEST",
- "IRCOMM_TTY_DATA_INDICATION",
- "IRCOMM_TTY_DISCOVERY_REQUEST",
- "IRCOMM_TTY_DISCOVERY_INDICATION",
- "IRCOMM_TTY_CONNECT_CONFIRM",
- "IRCOMM_TTY_CONNECT_INDICATION",
- "IRCOMM_TTY_DISCONNECT_REQUEST",
- "IRCOMM_TTY_DISCONNECT_INDICATION",
- "IRCOMM_TTY_WD_TIMER_EXPIRED",
- "IRCOMM_TTY_GOT_PARAMETERS",
- "IRCOMM_TTY_GOT_LSAPSEL",
- "*** ERROR ****",
-};
-
-static int (*state[])(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event,
- struct sk_buff *skb, struct ircomm_tty_info *info) =
-{
- ircomm_tty_state_idle,
- ircomm_tty_state_search,
- ircomm_tty_state_query_parameters,
- ircomm_tty_state_query_lsap_sel,
- ircomm_tty_state_setup,
- ircomm_tty_state_ready,
-};
-
-/*
- * Function ircomm_tty_attach_cable (driver)
- *
- * Try to attach cable (IrCOMM link). This function will only return
- * when the link has been connected, or if an error condition occurs.
- * If success, the return value is the resulting service type.
- */
-int ircomm_tty_attach_cable(struct ircomm_tty_cb *self)
-{
- struct tty_struct *tty;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
- /* Check if somebody has already connected to us */
- if (ircomm_is_connected(self->ircomm)) {
- pr_debug("%s(), already connected!\n", __func__);
- return 0;
- }
-
- /* Make sure nobody tries to write before the link is up */
- tty = tty_port_tty_get(&self->port);
- if (tty) {
- tty->hw_stopped = 1;
- tty_kref_put(tty);
- }
-
- ircomm_tty_ias_register(self);
-
- ircomm_tty_do_event(self, IRCOMM_TTY_ATTACH_CABLE, NULL, NULL);
-
- return 0;
-}
-
-/*
- * Function ircomm_detach_cable (driver)
- *
- * Detach cable, or cable has been detached by peer
- *
- */
-void ircomm_tty_detach_cable(struct ircomm_tty_cb *self)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
- del_timer(&self->watchdog_timer);
-
- /* Remove discovery handler */
- if (self->ckey) {
- irlmp_unregister_client(self->ckey);
- self->ckey = NULL;
- }
- /* Remove IrCOMM hint bits */
- if (self->skey) {
- irlmp_unregister_service(self->skey);
- self->skey = NULL;
- }
-
- if (self->iriap) {
- iriap_close(self->iriap);
- self->iriap = NULL;
- }
-
- /* Remove LM-IAS object */
- if (self->obj) {
- irias_delete_object(self->obj);
- self->obj = NULL;
- }
-
- ircomm_tty_do_event(self, IRCOMM_TTY_DETACH_CABLE, NULL, NULL);
-
- /* Reset some values */
- self->daddr = self->saddr = 0;
- self->dlsap_sel = self->slsap_sel = 0;
-
- memset(&self->settings, 0, sizeof(struct ircomm_params));
-}
-
-/*
- * Function ircomm_tty_ias_register (self)
- *
- * Register with LM-IAS depending on which service type we are
- *
- */
-static void ircomm_tty_ias_register(struct ircomm_tty_cb *self)
-{
- __u8 oct_seq[6];
- __u16 hints;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
- /* Compute hint bits based on service */
- hints = irlmp_service_to_hint(S_COMM);
- if (self->service_type & IRCOMM_3_WIRE_RAW)
- hints |= irlmp_service_to_hint(S_PRINTER);
-
- /* Advertise IrCOMM hint bit in discovery */
- if (!self->skey)
- self->skey = irlmp_register_service(hints);
- /* Set up a discovery handler */
- if (!self->ckey)
- self->ckey = irlmp_register_client(hints,
- ircomm_tty_discovery_indication,
- NULL, (void *) self);
-
- /* If already done, no need to do it again */
- if (self->obj)
- return;
-
- if (self->service_type & IRCOMM_3_WIRE_RAW) {
- /* Register IrLPT with LM-IAS */
- self->obj = irias_new_object("IrLPT", IAS_IRLPT_ID);
- irias_add_integer_attrib(self->obj, "IrDA:IrLMP:LsapSel",
- self->slsap_sel, IAS_KERNEL_ATTR);
- } else {
- /* Register IrCOMM with LM-IAS */
- self->obj = irias_new_object("IrDA:IrCOMM", IAS_IRCOMM_ID);
- irias_add_integer_attrib(self->obj, "IrDA:TinyTP:LsapSel",
- self->slsap_sel, IAS_KERNEL_ATTR);
-
- /* Code the parameters into the buffer */
- irda_param_pack(oct_seq, "bbbbbb",
- IRCOMM_SERVICE_TYPE, 1, self->service_type,
- IRCOMM_PORT_TYPE, 1, IRCOMM_SERIAL);
-
- /* Register parameters with LM-IAS */
- irias_add_octseq_attrib(self->obj, "Parameters", oct_seq, 6,
- IAS_KERNEL_ATTR);
- }
- irias_insert_object(self->obj);
-}
-
-/*
- * Function ircomm_tty_ias_unregister (self)
- *
- * Remove our IAS object and client hook while connected.
- *
- */
-static void ircomm_tty_ias_unregister(struct ircomm_tty_cb *self)
-{
- /* Remove LM-IAS object now so it is not reused.
- * IrCOMM deals very poorly with multiple incoming connections.
- * It should looks a lot more like IrNET, and "dup" a server TSAP
- * to the application TSAP (based on various rules).
- * This is a cheap workaround allowing multiple clients to
- * connect to us. It will not always work.
- * Each IrCOMM socket has an IAS entry. Incoming connection will
- * pick the first one found. So, when we are fully connected,
- * we remove our IAS entries so that the next IAS entry is used.
- * We do that for *both* client and server, because a server
- * can also create client instances.
- * Jean II */
- if (self->obj) {
- irias_delete_object(self->obj);
- self->obj = NULL;
- }
-
-#if 0
- /* Remove discovery handler.
- * While we are connected, we no longer need to receive
- * discovery events. This would be the case if there is
- * multiple IrLAP interfaces. Jean II */
- if (self->ckey) {
- irlmp_unregister_client(self->ckey);
- self->ckey = NULL;
- }
-#endif
-}
-
-/*
- * Function ircomm_send_initial_parameters (self)
- *
- * Send initial parameters to the remote IrCOMM device. These parameters
- * must be sent before any data.
- */
-int ircomm_tty_send_initial_parameters(struct ircomm_tty_cb *self)
-{
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
- if (self->service_type & IRCOMM_3_WIRE_RAW)
- return 0;
-
- /*
- * Set default values, but only if the application for some reason
- * haven't set them already
- */
- pr_debug("%s(), data-rate = %d\n", __func__ ,
- self->settings.data_rate);
- if (!self->settings.data_rate)
- self->settings.data_rate = 9600;
- pr_debug("%s(), data-format = %d\n", __func__ ,
- self->settings.data_format);
- if (!self->settings.data_format)
- self->settings.data_format = IRCOMM_WSIZE_8; /* 8N1 */
-
- pr_debug("%s(), flow-control = %d\n", __func__ ,
- self->settings.flow_control);
- /*self->settings.flow_control = IRCOMM_RTS_CTS_IN|IRCOMM_RTS_CTS_OUT;*/
-
- /* Do not set delta values for the initial parameters */
- self->settings.dte = IRCOMM_DTR | IRCOMM_RTS;
-
- /* Only send service type parameter when we are the client */
- if (self->client)
- ircomm_param_request(self, IRCOMM_SERVICE_TYPE, FALSE);
- ircomm_param_request(self, IRCOMM_DATA_RATE, FALSE);
- ircomm_param_request(self, IRCOMM_DATA_FORMAT, FALSE);
-
- /* For a 3 wire service, we just flush the last parameter and return */
- if (self->settings.service_type == IRCOMM_3_WIRE) {
- ircomm_param_request(self, IRCOMM_FLOW_CONTROL, TRUE);
- return 0;
- }
-
- /* Only 9-wire service types continue here */
- ircomm_param_request(self, IRCOMM_FLOW_CONTROL, FALSE);
-#if 0
- ircomm_param_request(self, IRCOMM_XON_XOFF, FALSE);
- ircomm_param_request(self, IRCOMM_ENQ_ACK, FALSE);
-#endif
- /* Notify peer that we are ready to receive data */
- ircomm_param_request(self, IRCOMM_DTE, TRUE);
-
- return 0;
-}
-
-/*
- * Function ircomm_tty_discovery_indication (discovery)
- *
- * Remote device is discovered, try query the remote IAS to see which
- * device it is, and which services it has.
- *
- */
-static void ircomm_tty_discovery_indication(discinfo_t *discovery,
- DISCOVERY_MODE mode,
- void *priv)
-{
- struct ircomm_tty_cb *self;
- struct ircomm_tty_info info;
-
- /* Important note :
- * We need to drop all passive discoveries.
- * The LSAP management of IrComm is deficient and doesn't deal
- * with the case of two instance connecting to each other
- * simultaneously (it will deadlock in LMP).
- * The proper fix would be to use the same technique as in IrNET,
- * to have one server socket and separate instances for the
- * connecting/connected socket.
- * The workaround is to drop passive discovery, which drastically
- * reduce the probability of this happening.
- * Jean II */
- if(mode == DISCOVERY_PASSIVE)
- return;
-
- info.daddr = discovery->daddr;
- info.saddr = discovery->saddr;
-
- self = priv;
- ircomm_tty_do_event(self, IRCOMM_TTY_DISCOVERY_INDICATION,
- NULL, &info);
-}
-
-/*
- * Function ircomm_tty_disconnect_indication (instance, sap, reason, skb)
- *
- * Link disconnected
- *
- */
-void ircomm_tty_disconnect_indication(void *instance, void *sap,
- LM_REASON reason,
- struct sk_buff *skb)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
- struct tty_struct *tty;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
- tty = tty_port_tty_get(&self->port);
- if (!tty)
- return;
-
- /* This will stop control data transfers */
- self->flow = FLOW_STOP;
-
- /* Stop data transfers */
- tty->hw_stopped = 1;
-
- ircomm_tty_do_event(self, IRCOMM_TTY_DISCONNECT_INDICATION, NULL,
- NULL);
- tty_kref_put(tty);
-}
-
-/*
- * Function ircomm_tty_getvalue_confirm (result, obj_id, value, priv)
- *
- * Got result from the IAS query we make
- *
- */
-static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id,
- struct ias_value *value,
- void *priv)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) priv;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
- /* We probably don't need to make any more queries */
- iriap_close(self->iriap);
- self->iriap = NULL;
-
- /* Check if request succeeded */
- if (result != IAS_SUCCESS) {
- pr_debug("%s(), got NULL value!\n", __func__);
- return;
- }
-
- switch (value->type) {
- case IAS_OCT_SEQ:
- pr_debug("%s(), got octet sequence\n", __func__);
-
- irda_param_extract_all(self, value->t.oct_seq, value->len,
- &ircomm_param_info);
-
- ircomm_tty_do_event(self, IRCOMM_TTY_GOT_PARAMETERS, NULL,
- NULL);
- break;
- case IAS_INTEGER:
- /* Got LSAP selector */
- pr_debug("%s(), got lsapsel = %d\n", __func__ ,
- value->t.integer);
-
- if (value->t.integer == -1) {
- pr_debug("%s(), invalid value!\n", __func__);
- } else
- self->dlsap_sel = value->t.integer;
-
- ircomm_tty_do_event(self, IRCOMM_TTY_GOT_LSAPSEL, NULL, NULL);
- break;
- case IAS_MISSING:
- pr_debug("%s(), got IAS_MISSING\n", __func__);
- break;
- default:
- pr_debug("%s(), got unknown type!\n", __func__);
- break;
- }
- irias_delete_value(value);
-}
-
-/*
- * Function ircomm_tty_connect_confirm (instance, sap, qos, max_sdu_size, skb)
- *
- * Connection confirmed
- *
- */
-void ircomm_tty_connect_confirm(void *instance, void *sap,
- struct qos_info *qos,
- __u32 max_data_size,
- __u8 max_header_size,
- struct sk_buff *skb)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
- self->client = TRUE;
- self->max_data_size = max_data_size;
- self->max_header_size = max_header_size;
- self->flow = FLOW_START;
-
- ircomm_tty_do_event(self, IRCOMM_TTY_CONNECT_CONFIRM, NULL, NULL);
-
- /* No need to kfree_skb - see ircomm_ttp_connect_confirm() */
-}
-
-/*
- * Function ircomm_tty_connect_indication (instance, sap, qos, max_sdu_size,
- * skb)
- *
- * we are discovered and being requested to connect by remote device !
- *
- */
-void ircomm_tty_connect_indication(void *instance, void *sap,
- struct qos_info *qos,
- __u32 max_data_size,
- __u8 max_header_size,
- struct sk_buff *skb)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
- int clen;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
- self->client = FALSE;
- self->max_data_size = max_data_size;
- self->max_header_size = max_header_size;
- self->flow = FLOW_START;
-
- clen = skb->data[0];
- if (clen)
- irda_param_extract_all(self, skb->data+1,
- IRDA_MIN(skb->len, clen),
- &ircomm_param_info);
-
- ircomm_tty_do_event(self, IRCOMM_TTY_CONNECT_INDICATION, NULL, NULL);
-
- /* No need to kfree_skb - see ircomm_ttp_connect_indication() */
-}
-
-/*
- * Function ircomm_tty_link_established (self)
- *
- * Called when the IrCOMM link is established
- *
- */
-void ircomm_tty_link_established(struct ircomm_tty_cb *self)
-{
- struct tty_struct *tty;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
- tty = tty_port_tty_get(&self->port);
- if (!tty)
- return;
-
- del_timer(&self->watchdog_timer);
-
- /*
- * IrCOMM link is now up, and if we are not using hardware
- * flow-control, then declare the hardware as running. Otherwise we
- * will have to wait for the peer device (DCE) to raise the CTS
- * line.
- */
- if (tty_port_cts_enabled(&self->port) &&
- ((self->settings.dce & IRCOMM_CTS) == 0)) {
- pr_debug("%s(), waiting for CTS ...\n", __func__);
- goto put;
- } else {
- pr_debug("%s(), starting hardware!\n", __func__);
-
- tty->hw_stopped = 0;
-
- /* Wake up processes blocked on open */
- wake_up_interruptible(&self->port.open_wait);
- }
-
- schedule_work(&self->tqueue);
-put:
- tty_kref_put(tty);
-}
-
-/*
- * Function ircomm_tty_start_watchdog_timer (self, timeout)
- *
- * Start the watchdog timer. This timer is used to make sure that any
- * connection attempt is successful, and if not, we will retry after
- * the timeout
- */
-static void ircomm_tty_start_watchdog_timer(struct ircomm_tty_cb *self,
- int timeout)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
- irda_start_timer(&self->watchdog_timer, timeout,
- ircomm_tty_watchdog_timer_expired);
-}
-
-/*
- * Function ircomm_tty_watchdog_timer_expired (data)
- *
- * Called when the connect procedure have taken to much time.
- *
- */
-static void ircomm_tty_watchdog_timer_expired(struct timer_list *t)
-{
- struct ircomm_tty_cb *self = from_timer(self, t, watchdog_timer);
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
- ircomm_tty_do_event(self, IRCOMM_TTY_WD_TIMER_EXPIRED, NULL, NULL);
-}
-
-
-/*
- * Function ircomm_tty_do_event (self, event, skb)
- *
- * Process event
- *
- */
-int ircomm_tty_do_event(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event,
- struct sk_buff *skb, struct ircomm_tty_info *info)
-{
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
- pr_debug("%s: state=%s, event=%s\n", __func__ ,
- ircomm_tty_state[self->state], ircomm_tty_event[event]);
-
- return (*state[self->state])(self, event, skb, info);
-}
-
-/*
- * Function ircomm_tty_next_state (self, state)
- *
- * Switch state
- *
- */
-static inline void ircomm_tty_next_state(struct ircomm_tty_cb *self, IRCOMM_TTY_STATE state)
-{
- /*
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
- pr_debug("%s: next state=%s, service type=%d\n", __func__ ,
- ircomm_tty_state[self->state], self->service_type);
- */
- self->state = state;
-}
-
-/*
- * Function ircomm_tty_state_idle (self, event, skb, info)
- *
- * Just hanging around
- *
- */
-static int ircomm_tty_state_idle(struct ircomm_tty_cb *self,
- IRCOMM_TTY_EVENT event,
- struct sk_buff *skb,
- struct ircomm_tty_info *info)
-{
- int ret = 0;
-
- pr_debug("%s: state=%s, event=%s\n", __func__ ,
- ircomm_tty_state[self->state], ircomm_tty_event[event]);
- switch (event) {
- case IRCOMM_TTY_ATTACH_CABLE:
- /* Try to discover any remote devices */
- ircomm_tty_start_watchdog_timer(self, 3*HZ);
- ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
-
- irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS);
- break;
- case IRCOMM_TTY_DISCOVERY_INDICATION:
- self->daddr = info->daddr;
- self->saddr = info->saddr;
-
- if (self->iriap) {
- net_warn_ratelimited("%s(), busy with a previous query\n",
- __func__);
- return -EBUSY;
- }
-
- self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
- ircomm_tty_getvalue_confirm);
-
- iriap_getvaluebyclass_request(self->iriap,
- self->saddr, self->daddr,
- "IrDA:IrCOMM", "Parameters");
-
- ircomm_tty_start_watchdog_timer(self, 3*HZ);
- ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_PARAMETERS);
- break;
- case IRCOMM_TTY_CONNECT_INDICATION:
- del_timer(&self->watchdog_timer);
-
- /* Accept connection */
- ircomm_connect_response(self->ircomm, NULL);
- ircomm_tty_next_state(self, IRCOMM_TTY_READY);
- break;
- case IRCOMM_TTY_WD_TIMER_EXPIRED:
- /* Just stay idle */
- break;
- case IRCOMM_TTY_DETACH_CABLE:
- ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
- break;
- default:
- pr_debug("%s(), unknown event: %s\n", __func__ ,
- ircomm_tty_event[event]);
- ret = -EINVAL;
- }
- return ret;
-}
-
-/*
- * Function ircomm_tty_state_search (self, event, skb, info)
- *
- * Trying to discover an IrCOMM device
- *
- */
-static int ircomm_tty_state_search(struct ircomm_tty_cb *self,
- IRCOMM_TTY_EVENT event,
- struct sk_buff *skb,
- struct ircomm_tty_info *info)
-{
- int ret = 0;
-
- pr_debug("%s: state=%s, event=%s\n", __func__ ,
- ircomm_tty_state[self->state], ircomm_tty_event[event]);
-
- switch (event) {
- case IRCOMM_TTY_DISCOVERY_INDICATION:
- self->daddr = info->daddr;
- self->saddr = info->saddr;
-
- if (self->iriap) {
- net_warn_ratelimited("%s(), busy with a previous query\n",
- __func__);
- return -EBUSY;
- }
-
- self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
- ircomm_tty_getvalue_confirm);
-
- if (self->service_type == IRCOMM_3_WIRE_RAW) {
- iriap_getvaluebyclass_request(self->iriap, self->saddr,
- self->daddr, "IrLPT",
- "IrDA:IrLMP:LsapSel");
- ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_LSAP_SEL);
- } else {
- iriap_getvaluebyclass_request(self->iriap, self->saddr,
- self->daddr,
- "IrDA:IrCOMM",
- "Parameters");
-
- ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_PARAMETERS);
- }
- ircomm_tty_start_watchdog_timer(self, 3*HZ);
- break;
- case IRCOMM_TTY_CONNECT_INDICATION:
- del_timer(&self->watchdog_timer);
- ircomm_tty_ias_unregister(self);
-
- /* Accept connection */
- ircomm_connect_response(self->ircomm, NULL);
- ircomm_tty_next_state(self, IRCOMM_TTY_READY);
- break;
- case IRCOMM_TTY_WD_TIMER_EXPIRED:
-#if 1
- /* Give up */
-#else
- /* Try to discover any remote devices */
- ircomm_tty_start_watchdog_timer(self, 3*HZ);
- irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS);
-#endif
- break;
- case IRCOMM_TTY_DETACH_CABLE:
- ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
- break;
- default:
- pr_debug("%s(), unknown event: %s\n", __func__ ,
- ircomm_tty_event[event]);
- ret = -EINVAL;
- }
- return ret;
-}
-
-/*
- * Function ircomm_tty_state_query (self, event, skb, info)
- *
- * Querying the remote LM-IAS for IrCOMM parameters
- *
- */
-static int ircomm_tty_state_query_parameters(struct ircomm_tty_cb *self,
- IRCOMM_TTY_EVENT event,
- struct sk_buff *skb,
- struct ircomm_tty_info *info)
-{
- int ret = 0;
-
- pr_debug("%s: state=%s, event=%s\n", __func__ ,
- ircomm_tty_state[self->state], ircomm_tty_event[event]);
-
- switch (event) {
- case IRCOMM_TTY_GOT_PARAMETERS:
- if (self->iriap) {
- net_warn_ratelimited("%s(), busy with a previous query\n",
- __func__);
- return -EBUSY;
- }
-
- self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
- ircomm_tty_getvalue_confirm);
-
- iriap_getvaluebyclass_request(self->iriap, self->saddr,
- self->daddr, "IrDA:IrCOMM",
- "IrDA:TinyTP:LsapSel");
-
- ircomm_tty_start_watchdog_timer(self, 3*HZ);
- ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_LSAP_SEL);
- break;
- case IRCOMM_TTY_WD_TIMER_EXPIRED:
- /* Go back to search mode */
- ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
- ircomm_tty_start_watchdog_timer(self, 3*HZ);
- break;
- case IRCOMM_TTY_CONNECT_INDICATION:
- del_timer(&self->watchdog_timer);
- ircomm_tty_ias_unregister(self);
-
- /* Accept connection */
- ircomm_connect_response(self->ircomm, NULL);
- ircomm_tty_next_state(self, IRCOMM_TTY_READY);
- break;
- case IRCOMM_TTY_DETACH_CABLE:
- ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
- break;
- default:
- pr_debug("%s(), unknown event: %s\n", __func__ ,
- ircomm_tty_event[event]);
- ret = -EINVAL;
- }
- return ret;
-}
-
-/*
- * Function ircomm_tty_state_query_lsap_sel (self, event, skb, info)
- *
- * Query remote LM-IAS for the LSAP selector which we can connect to
- *
- */
-static int ircomm_tty_state_query_lsap_sel(struct ircomm_tty_cb *self,
- IRCOMM_TTY_EVENT event,
- struct sk_buff *skb,
- struct ircomm_tty_info *info)
-{
- int ret = 0;
-
- pr_debug("%s: state=%s, event=%s\n", __func__ ,
- ircomm_tty_state[self->state], ircomm_tty_event[event]);
-
- switch (event) {
- case IRCOMM_TTY_GOT_LSAPSEL:
- /* Connect to remote device */
- ret = ircomm_connect_request(self->ircomm, self->dlsap_sel,
- self->saddr, self->daddr,
- NULL, self->service_type);
- ircomm_tty_start_watchdog_timer(self, 3*HZ);
- ircomm_tty_next_state(self, IRCOMM_TTY_SETUP);
- break;
- case IRCOMM_TTY_WD_TIMER_EXPIRED:
- /* Go back to search mode */
- ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
- ircomm_tty_start_watchdog_timer(self, 3*HZ);
- break;
- case IRCOMM_TTY_CONNECT_INDICATION:
- del_timer(&self->watchdog_timer);
- ircomm_tty_ias_unregister(self);
-
- /* Accept connection */
- ircomm_connect_response(self->ircomm, NULL);
- ircomm_tty_next_state(self, IRCOMM_TTY_READY);
- break;
- case IRCOMM_TTY_DETACH_CABLE:
- ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
- break;
- default:
- pr_debug("%s(), unknown event: %s\n", __func__ ,
- ircomm_tty_event[event]);
- ret = -EINVAL;
- }
- return ret;
-}
-
-/*
- * Function ircomm_tty_state_setup (self, event, skb, info)
- *
- * Trying to connect
- *
- */
-static int ircomm_tty_state_setup(struct ircomm_tty_cb *self,
- IRCOMM_TTY_EVENT event,
- struct sk_buff *skb,
- struct ircomm_tty_info *info)
-{
- int ret = 0;
-
- pr_debug("%s: state=%s, event=%s\n", __func__ ,
- ircomm_tty_state[self->state], ircomm_tty_event[event]);
-
- switch (event) {
- case IRCOMM_TTY_CONNECT_CONFIRM:
- del_timer(&self->watchdog_timer);
- ircomm_tty_ias_unregister(self);
-
- /*
- * Send initial parameters. This will also send out queued
- * parameters waiting for the connection to come up
- */
- ircomm_tty_send_initial_parameters(self);
- ircomm_tty_link_established(self);
- ircomm_tty_next_state(self, IRCOMM_TTY_READY);
- break;
- case IRCOMM_TTY_CONNECT_INDICATION:
- del_timer(&self->watchdog_timer);
- ircomm_tty_ias_unregister(self);
-
- /* Accept connection */
- ircomm_connect_response(self->ircomm, NULL);
- ircomm_tty_next_state(self, IRCOMM_TTY_READY);
- break;
- case IRCOMM_TTY_WD_TIMER_EXPIRED:
- /* Go back to search mode */
- ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
- ircomm_tty_start_watchdog_timer(self, 3*HZ);
- break;
- case IRCOMM_TTY_DETACH_CABLE:
- /* ircomm_disconnect_request(self->ircomm, NULL); */
- ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
- break;
- default:
- pr_debug("%s(), unknown event: %s\n", __func__ ,
- ircomm_tty_event[event]);
- ret = -EINVAL;
- }
- return ret;
-}
-
-/*
- * Function ircomm_tty_state_ready (self, event, skb, info)
- *
- * IrCOMM is now connected
- *
- */
-static int ircomm_tty_state_ready(struct ircomm_tty_cb *self,
- IRCOMM_TTY_EVENT event,
- struct sk_buff *skb,
- struct ircomm_tty_info *info)
-{
- int ret = 0;
-
- switch (event) {
- case IRCOMM_TTY_DATA_REQUEST:
- ret = ircomm_data_request(self->ircomm, skb);
- break;
- case IRCOMM_TTY_DETACH_CABLE:
- ircomm_disconnect_request(self->ircomm, NULL);
- ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
- break;
- case IRCOMM_TTY_DISCONNECT_INDICATION:
- ircomm_tty_ias_register(self);
- ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
- ircomm_tty_start_watchdog_timer(self, 3*HZ);
-
- if (tty_port_check_carrier(&self->port)) {
- /* Drop carrier */
- self->settings.dce = IRCOMM_DELTA_CD;
- ircomm_tty_check_modem_status(self);
- } else {
- pr_debug("%s(), hanging up!\n", __func__);
- tty_port_tty_hangup(&self->port, false);
- }
- break;
- default:
- pr_debug("%s(), unknown event: %s\n", __func__ ,
- ircomm_tty_event[event]);
- ret = -EINVAL;
- }
- return ret;
-}
-
diff --git a/drivers/staging/irda/net/ircomm/ircomm_tty_ioctl.c b/drivers/staging/irda/net/ircomm/ircomm_tty_ioctl.c
deleted file mode 100644
index 171c3dee760e..000000000000
--- a/drivers/staging/irda/net/ircomm/ircomm_tty_ioctl.c
+++ /dev/null
@@ -1,291 +0,0 @@
-/*********************************************************************
- *
- * Filename: ircomm_tty_ioctl.c
- * Version:
- * Description:
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Thu Jun 10 14:39:09 1999
- * Modified at: Wed Jan 5 14:45:43 2000
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/init.h>
-#include <linux/fs.h>
-#include <linux/termios.h>
-#include <linux/tty.h>
-#include <linux/serial.h>
-
-#include <linux/uaccess.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irmod.h>
-
-#include <net/irda/ircomm_core.h>
-#include <net/irda/ircomm_param.h>
-#include <net/irda/ircomm_tty_attach.h>
-#include <net/irda/ircomm_tty.h>
-
-#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
-
-/*
- * Function ircomm_tty_change_speed (driver)
- *
- * Change speed of the driver. If the remote device is a DCE, then this
- * should make it change the speed of its serial port
- */
-static void ircomm_tty_change_speed(struct ircomm_tty_cb *self,
- struct tty_struct *tty)
-{
- unsigned int cflag, cval;
- int baud;
-
- if (!self->ircomm)
- return;
-
- cflag = tty->termios.c_cflag;
-
- /* byte size and parity */
- switch (cflag & CSIZE) {
- case CS5: cval = IRCOMM_WSIZE_5; break;
- case CS6: cval = IRCOMM_WSIZE_6; break;
- case CS7: cval = IRCOMM_WSIZE_7; break;
- case CS8: cval = IRCOMM_WSIZE_8; break;
- default: cval = IRCOMM_WSIZE_5; break;
- }
- if (cflag & CSTOPB)
- cval |= IRCOMM_2_STOP_BIT;
-
- if (cflag & PARENB)
- cval |= IRCOMM_PARITY_ENABLE;
- if (!(cflag & PARODD))
- cval |= IRCOMM_PARITY_EVEN;
-
- /* Determine divisor based on baud rate */
- baud = tty_get_baud_rate(tty);
- if (!baud)
- baud = 9600; /* B0 transition handled in rs_set_termios */
-
- self->settings.data_rate = baud;
- ircomm_param_request(self, IRCOMM_DATA_RATE, FALSE);
-
- /* CTS flow control flag and modem status interrupts */
- tty_port_set_cts_flow(&self->port, cflag & CRTSCTS);
- if (cflag & CRTSCTS) {
- self->settings.flow_control |= IRCOMM_RTS_CTS_IN;
- /* This got me. Bummer. Jean II */
- if (self->service_type == IRCOMM_3_WIRE_RAW)
- net_warn_ratelimited("%s(), enabling RTS/CTS on link that doesn't support it (3-wire-raw)\n",
- __func__);
- } else {
- self->settings.flow_control &= ~IRCOMM_RTS_CTS_IN;
- }
- tty_port_set_check_carrier(&self->port, ~cflag & CLOCAL);
-
- self->settings.data_format = cval;
-
- ircomm_param_request(self, IRCOMM_DATA_FORMAT, FALSE);
- ircomm_param_request(self, IRCOMM_FLOW_CONTROL, TRUE);
-}
-
-/*
- * Function ircomm_tty_set_termios (tty, old_termios)
- *
- * This routine allows the tty driver to be notified when device's
- * termios settings have changed. Note that a well-designed tty driver
- * should be prepared to accept the case where old == NULL, and try to
- * do something rational.
- */
-void ircomm_tty_set_termios(struct tty_struct *tty,
- struct ktermios *old_termios)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
- unsigned int cflag = tty->termios.c_cflag;
-
- if ((cflag == old_termios->c_cflag) &&
- (RELEVANT_IFLAG(tty->termios.c_iflag) ==
- RELEVANT_IFLAG(old_termios->c_iflag)))
- {
- return;
- }
-
- ircomm_tty_change_speed(self, tty);
-
- /* Handle transition to B0 status */
- if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) {
- self->settings.dte &= ~(IRCOMM_DTR|IRCOMM_RTS);
- ircomm_param_request(self, IRCOMM_DTE, TRUE);
- }
-
- /* Handle transition away from B0 status */
- if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {
- self->settings.dte |= IRCOMM_DTR;
- if (!C_CRTSCTS(tty) || !tty_throttled(tty))
- self->settings.dte |= IRCOMM_RTS;
- ircomm_param_request(self, IRCOMM_DTE, TRUE);
- }
-
- /* Handle turning off CRTSCTS */
- if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty))
- {
- tty->hw_stopped = 0;
- ircomm_tty_start(tty);
- }
-}
-
-/*
- * Function ircomm_tty_tiocmget (tty)
- *
- *
- *
- */
-int ircomm_tty_tiocmget(struct tty_struct *tty)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
- unsigned int result;
-
- if (tty_io_error(tty))
- return -EIO;
-
- result = ((self->settings.dte & IRCOMM_RTS) ? TIOCM_RTS : 0)
- | ((self->settings.dte & IRCOMM_DTR) ? TIOCM_DTR : 0)
- | ((self->settings.dce & IRCOMM_CD) ? TIOCM_CAR : 0)
- | ((self->settings.dce & IRCOMM_RI) ? TIOCM_RNG : 0)
- | ((self->settings.dce & IRCOMM_DSR) ? TIOCM_DSR : 0)
- | ((self->settings.dce & IRCOMM_CTS) ? TIOCM_CTS : 0);
- return result;
-}
-
-/*
- * Function ircomm_tty_tiocmset (tty, set, clear)
- *
- *
- *
- */
-int ircomm_tty_tiocmset(struct tty_struct *tty,
- unsigned int set, unsigned int clear)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
-
- if (tty_io_error(tty))
- return -EIO;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
- if (set & TIOCM_RTS)
- self->settings.dte |= IRCOMM_RTS;
- if (set & TIOCM_DTR)
- self->settings.dte |= IRCOMM_DTR;
-
- if (clear & TIOCM_RTS)
- self->settings.dte &= ~IRCOMM_RTS;
- if (clear & TIOCM_DTR)
- self->settings.dte &= ~IRCOMM_DTR;
-
- if ((set|clear) & TIOCM_RTS)
- self->settings.dte |= IRCOMM_DELTA_RTS;
- if ((set|clear) & TIOCM_DTR)
- self->settings.dte |= IRCOMM_DELTA_DTR;
-
- ircomm_param_request(self, IRCOMM_DTE, TRUE);
-
- return 0;
-}
-
-/*
- * Function get_serial_info (driver, retinfo)
- *
- *
- *
- */
-static int ircomm_tty_get_serial_info(struct ircomm_tty_cb *self,
- struct serial_struct __user *retinfo)
-{
- struct serial_struct info;
-
- memset(&info, 0, sizeof(info));
- info.line = self->line;
- info.flags = self->port.flags;
- info.baud_base = self->settings.data_rate;
- info.close_delay = self->port.close_delay;
- info.closing_wait = self->port.closing_wait;
-
- /* For compatibility */
- info.type = PORT_16550A;
-
- if (copy_to_user(retinfo, &info, sizeof(*retinfo)))
- return -EFAULT;
-
- return 0;
-}
-
-/*
- * Function set_serial_info (driver, new_info)
- *
- *
- *
- */
-static int ircomm_tty_set_serial_info(struct ircomm_tty_cb *self,
- struct serial_struct __user *new_info)
-{
- return 0;
-}
-
-/*
- * Function ircomm_tty_ioctl (tty, cmd, arg)
- *
- *
- *
- */
-int ircomm_tty_ioctl(struct tty_struct *tty,
- unsigned int cmd, unsigned long arg)
-{
- struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
- int ret = 0;
-
- if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
- (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
- (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
- if (tty_io_error(tty))
- return -EIO;
- }
-
- switch (cmd) {
- case TIOCGSERIAL:
- ret = ircomm_tty_get_serial_info(self, (struct serial_struct __user *) arg);
- break;
- case TIOCSSERIAL:
- ret = ircomm_tty_set_serial_info(self, (struct serial_struct __user *) arg);
- break;
- case TIOCMIWAIT:
- pr_debug("(), TIOCMIWAIT, not impl!\n");
- break;
-
- case TIOCGICOUNT:
- pr_debug("%s(), TIOCGICOUNT not impl!\n", __func__);
- return 0;
- default:
- ret = -ENOIOCTLCMD; /* ioctls which we must ignore */
- }
- return ret;
-}
-
-
-
diff --git a/drivers/staging/irda/net/irda_device.c b/drivers/staging/irda/net/irda_device.c
deleted file mode 100644
index 682b4eea15e0..000000000000
--- a/drivers/staging/irda/net/irda_device.c
+++ /dev/null
@@ -1,316 +0,0 @@
-/*********************************************************************
- *
- * Filename: irda_device.c
- * Version: 0.9
- * Description: Utility functions used by the device drivers
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sat Oct 9 09:22:27 1999
- * Modified at: Sun Jan 23 17:41:24 2000
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
- * Copyright (c) 2000-2001 Jean Tourrilhes <jt@hpl.hp.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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/string.h>
-#include <linux/proc_fs.h>
-#include <linux/skbuff.h>
-#include <linux/capability.h>
-#include <linux/if.h>
-#include <linux/if_ether.h>
-#include <linux/if_arp.h>
-#include <linux/netdevice.h>
-#include <linux/init.h>
-#include <linux/tty.h>
-#include <linux/kmod.h>
-#include <linux/spinlock.h>
-#include <linux/slab.h>
-#include <linux/export.h>
-
-#include <asm/ioctls.h>
-#include <linux/uaccess.h>
-#include <asm/dma.h>
-#include <asm/io.h>
-
-#include <net/irda/irda_device.h>
-#include <net/irda/irlap.h>
-#include <net/irda/timer.h>
-#include <net/irda/wrapper.h>
-
-static void __irda_task_delete(struct irda_task *task);
-
-static hashbin_t *dongles;
-static hashbin_t *tasks;
-
-static void irda_task_timer_expired(struct timer_list *timer);
-
-int __init irda_device_init(void)
-{
- dongles = hashbin_new(HB_NOLOCK);
- if (!dongles) {
- net_warn_ratelimited("IrDA: Can't allocate dongles hashbin!\n");
- return -ENOMEM;
- }
- spin_lock_init(&dongles->hb_spinlock);
-
- tasks = hashbin_new(HB_LOCK);
- if (!tasks) {
- net_warn_ratelimited("IrDA: Can't allocate tasks hashbin!\n");
- hashbin_delete(dongles, NULL);
- return -ENOMEM;
- }
-
- /* We no longer initialise the driver ourselves here, we let
- * the system do it for us... - Jean II
- */
-
- return 0;
-}
-
-static void leftover_dongle(void *arg)
-{
- struct dongle_reg *reg = arg;
-
- net_warn_ratelimited("IrDA: Dongle type %x not unregistered\n",
- reg->type);
-}
-
-void irda_device_cleanup(void)
-{
- hashbin_delete(tasks, (FREE_FUNC) __irda_task_delete);
-
- hashbin_delete(dongles, leftover_dongle);
-}
-
-/*
- * Function irda_device_set_media_busy (self, status)
- *
- * Called when we have detected that another station is transmitting
- * in contention mode.
- */
-void irda_device_set_media_busy(struct net_device *dev, int status)
-{
- struct irlap_cb *self;
-
- pr_debug("%s(%s)\n", __func__, status ? "TRUE" : "FALSE");
-
- self = (struct irlap_cb *)dev->atalk_ptr;
-
- /* Some drivers may enable the receive interrupt before calling
- * irlap_open(), or they may disable the receive interrupt
- * after calling irlap_close().
- * The IrDA stack is protected from this in irlap_driver_rcv().
- * However, the driver calls directly the wrapper, that calls
- * us directly. Make sure we protect ourselves.
- * Jean II
- */
- if (!self || self->magic != LAP_MAGIC)
- return;
-
- if (status) {
- self->media_busy = TRUE;
- if (status == SMALL)
- irlap_start_mbusy_timer(self, SMALLBUSY_TIMEOUT);
- else
- irlap_start_mbusy_timer(self, MEDIABUSY_TIMEOUT);
- pr_debug("Media busy!\n");
- } else {
- self->media_busy = FALSE;
- irlap_stop_mbusy_timer(self);
- }
-}
-EXPORT_SYMBOL(irda_device_set_media_busy);
-
-/*
- * Function irda_device_is_receiving (dev)
- *
- * Check if the device driver is currently receiving data
- *
- */
-int irda_device_is_receiving(struct net_device *dev)
-{
- struct if_irda_req req;
- int ret;
-
- if (!dev->netdev_ops->ndo_do_ioctl) {
- net_err_ratelimited("%s: do_ioctl not impl. by device driver\n",
- __func__);
- return -1;
- }
-
- ret = (dev->netdev_ops->ndo_do_ioctl)(dev, (struct ifreq *) &req,
- SIOCGRECEIVING);
- if (ret < 0)
- return ret;
-
- return req.ifr_receiving;
-}
-
-static void __irda_task_delete(struct irda_task *task)
-{
- del_timer(&task->timer);
-
- kfree(task);
-}
-
-static void irda_task_delete(struct irda_task *task)
-{
- /* Unregister task */
- hashbin_remove(tasks, (long)task, NULL);
-
- __irda_task_delete(task);
-}
-
-/*
- * Function irda_task_kick (task)
- *
- * Tries to execute a task possible multiple times until the task is either
- * finished, or askes for a timeout. When a task is finished, we do post
- * processing, and notify the parent task, that is waiting for this task
- * to complete.
- */
-static int irda_task_kick(struct irda_task *task)
-{
- int finished = TRUE;
- int count = 0;
- int timeout;
-
- IRDA_ASSERT(task != NULL, return -1;);
- IRDA_ASSERT(task->magic == IRDA_TASK_MAGIC, return -1;);
-
- /* Execute task until it's finished, or askes for a timeout */
- do {
- timeout = task->function(task);
- if (count++ > 100) {
- net_err_ratelimited("%s: error in task handler!\n",
- __func__);
- irda_task_delete(task);
- return TRUE;
- }
- } while ((timeout == 0) && (task->state != IRDA_TASK_DONE));
-
- if (timeout < 0) {
- net_err_ratelimited("%s: Error executing task!\n", __func__);
- irda_task_delete(task);
- return TRUE;
- }
-
- /* Check if we are finished */
- if (task->state == IRDA_TASK_DONE) {
- del_timer(&task->timer);
-
- /* Do post processing */
- if (task->finished)
- task->finished(task);
-
- /* Notify parent */
- if (task->parent) {
- /* Check if parent is waiting for us to complete */
- if (task->parent->state == IRDA_TASK_CHILD_WAIT) {
- task->parent->state = IRDA_TASK_CHILD_DONE;
-
- /* Stop timer now that we are here */
- del_timer(&task->parent->timer);
-
- /* Kick parent task */
- irda_task_kick(task->parent);
- }
- }
- irda_task_delete(task);
- } else if (timeout > 0) {
- irda_start_timer(&task->timer, timeout,
- irda_task_timer_expired);
- finished = FALSE;
- } else {
- pr_debug("%s(), not finished, and no timeout!\n",
- __func__);
- finished = FALSE;
- }
-
- return finished;
-}
-
-/*
- * Function irda_task_timer_expired (data)
- *
- * Task time has expired. We now try to execute task (again), and restart
- * the timer if the task has not finished yet
- */
-static void irda_task_timer_expired(struct timer_list *t)
-{
- struct irda_task *task = from_timer(task, t, timer);
-
- irda_task_kick(task);
-}
-
-/*
- * Function irda_device_setup (dev)
- *
- * This function should be used by low level device drivers in a similar way
- * as ether_setup() is used by normal network device drivers
- */
-static void irda_device_setup(struct net_device *dev)
-{
- dev->hard_header_len = 0;
- dev->addr_len = LAP_ALEN;
-
- dev->type = ARPHRD_IRDA;
- dev->tx_queue_len = 8; /* Window size + 1 s-frame */
-
- memset(dev->broadcast, 0xff, LAP_ALEN);
-
- dev->mtu = 2048;
- dev->flags = IFF_NOARP;
-}
-
-/*
- * Funciton alloc_irdadev
- * Allocates and sets up an IRDA device in a manner similar to
- * alloc_etherdev.
- */
-struct net_device *alloc_irdadev(int sizeof_priv)
-{
- return alloc_netdev(sizeof_priv, "irda%d", NET_NAME_UNKNOWN,
- irda_device_setup);
-}
-EXPORT_SYMBOL(alloc_irdadev);
-
-#ifdef CONFIG_ISA_DMA_API
-/*
- * Function setup_dma (idev, buffer, count, mode)
- *
- * Setup the DMA channel. Commonly used by LPC FIR drivers
- *
- */
-void irda_setup_dma(int channel, dma_addr_t buffer, int count, int mode)
-{
- unsigned long flags;
-
- flags = claim_dma_lock();
-
- disable_dma(channel);
- clear_dma_ff(channel);
- set_dma_mode(channel, mode);
- set_dma_addr(channel, buffer);
- set_dma_count(channel, count);
- enable_dma(channel);
-
- release_dma_lock(flags);
-}
-EXPORT_SYMBOL(irda_setup_dma);
-#endif
diff --git a/drivers/staging/irda/net/iriap.c b/drivers/staging/irda/net/iriap.c
deleted file mode 100644
index d64192e9db8b..000000000000
--- a/drivers/staging/irda/net/iriap.c
+++ /dev/null
@@ -1,1085 +0,0 @@
-/*********************************************************************
- *
- * Filename: iriap.c
- * Version: 0.8
- * Description: Information Access Protocol (IAP)
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Thu Aug 21 00:02:07 1997
- * Modified at: Sat Dec 25 16:42:42 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
- * All Rights Reserved.
- * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/skbuff.h>
-#include <linux/fs.h>
-#include <linux/string.h>
-#include <linux/init.h>
-#include <linux/seq_file.h>
-#include <linux/slab.h>
-
-#include <asm/byteorder.h>
-#include <asm/unaligned.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irttp.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/irias_object.h>
-#include <net/irda/iriap_event.h>
-#include <net/irda/iriap.h>
-
-/* FIXME: This one should go in irlmp.c */
-static const char *const ias_charset_types[] __maybe_unused = {
- "CS_ASCII",
- "CS_ISO_8859_1",
- "CS_ISO_8859_2",
- "CS_ISO_8859_3",
- "CS_ISO_8859_4",
- "CS_ISO_8859_5",
- "CS_ISO_8859_6",
- "CS_ISO_8859_7",
- "CS_ISO_8859_8",
- "CS_ISO_8859_9",
- "CS_UNICODE"
-};
-
-static hashbin_t *iriap = NULL;
-static void *service_handle;
-
-static void __iriap_close(struct iriap_cb *self);
-static int iriap_register_lsap(struct iriap_cb *self, __u8 slsap_sel, int mode);
-static void iriap_disconnect_indication(void *instance, void *sap,
- LM_REASON reason, struct sk_buff *skb);
-static void iriap_connect_indication(void *instance, void *sap,
- struct qos_info *qos, __u32 max_sdu_size,
- __u8 max_header_size,
- struct sk_buff *skb);
-static void iriap_connect_confirm(void *instance, void *sap,
- struct qos_info *qos,
- __u32 max_sdu_size, __u8 max_header_size,
- struct sk_buff *skb);
-static int iriap_data_indication(void *instance, void *sap,
- struct sk_buff *skb);
-
-static void iriap_watchdog_timer_expired(struct timer_list *t);
-
-static inline void iriap_start_watchdog_timer(struct iriap_cb *self,
- int timeout)
-{
- irda_start_timer(&self->watchdog_timer, timeout,
- iriap_watchdog_timer_expired);
-}
-
-static struct lock_class_key irias_objects_key;
-
-/*
- * Function iriap_init (void)
- *
- * Initializes the IrIAP layer, called by the module initialization code
- * in irmod.c
- */
-int __init iriap_init(void)
-{
- struct ias_object *obj;
- struct iriap_cb *server;
- __u8 oct_seq[6];
- __u16 hints;
-
- /* Allocate master array */
- iriap = hashbin_new(HB_LOCK);
- if (!iriap)
- return -ENOMEM;
-
- /* Object repository - defined in irias_object.c */
- irias_objects = hashbin_new(HB_LOCK);
- if (!irias_objects) {
- net_warn_ratelimited("%s: Can't allocate irias_objects hashbin!\n",
- __func__);
- hashbin_delete(iriap, NULL);
- return -ENOMEM;
- }
-
- lockdep_set_class_and_name(&irias_objects->hb_spinlock, &irias_objects_key,
- "irias_objects");
-
- /*
- * Register some default services for IrLMP
- */
- hints = irlmp_service_to_hint(S_COMPUTER);
- service_handle = irlmp_register_service(hints);
-
- /* Register the Device object with LM-IAS */
- obj = irias_new_object("Device", IAS_DEVICE_ID);
- irias_add_string_attrib(obj, "DeviceName", "Linux", IAS_KERNEL_ATTR);
-
- oct_seq[0] = 0x01; /* Version 1 */
- oct_seq[1] = 0x00; /* IAS support bits */
- oct_seq[2] = 0x00; /* LM-MUX support bits */
-#ifdef CONFIG_IRDA_ULTRA
- oct_seq[2] |= 0x04; /* Connectionless Data support */
-#endif
- irias_add_octseq_attrib(obj, "IrLMPSupport", oct_seq, 3,
- IAS_KERNEL_ATTR);
- irias_insert_object(obj);
-
- /*
- * Register server support with IrLMP so we can accept incoming
- * connections
- */
- server = iriap_open(LSAP_IAS, IAS_SERVER, NULL, NULL);
- if (!server) {
- pr_debug("%s(), unable to open server\n", __func__);
- return -1;
- }
- iriap_register_lsap(server, LSAP_IAS, IAS_SERVER);
-
- return 0;
-}
-
-/*
- * Function iriap_cleanup (void)
- *
- * Initializes the IrIAP layer, called by the module cleanup code in
- * irmod.c
- */
-void iriap_cleanup(void)
-{
- irlmp_unregister_service(service_handle);
-
- hashbin_delete(iriap, (FREE_FUNC) __iriap_close);
- hashbin_delete(irias_objects, (FREE_FUNC) __irias_delete_object);
-}
-
-/*
- * Function iriap_open (void)
- *
- * Opens an instance of the IrIAP layer, and registers with IrLMP
- */
-struct iriap_cb *iriap_open(__u8 slsap_sel, int mode, void *priv,
- CONFIRM_CALLBACK callback)
-{
- struct iriap_cb *self;
-
- self = kzalloc(sizeof(*self), GFP_ATOMIC);
- if (!self)
- return NULL;
-
- /*
- * Initialize instance
- */
-
- self->magic = IAS_MAGIC;
- self->mode = mode;
- if (mode == IAS_CLIENT) {
- if (iriap_register_lsap(self, slsap_sel, mode)) {
- kfree(self);
- return NULL;
- }
- }
-
- self->confirm = callback;
- self->priv = priv;
-
- /* iriap_getvaluebyclass_request() will construct packets before
- * we connect, so this must have a sane value... Jean II */
- self->max_header_size = LMP_MAX_HEADER;
-
- timer_setup(&self->watchdog_timer, NULL, 0);
-
- hashbin_insert(iriap, (irda_queue_t *) self, (long) self, NULL);
-
- /* Initialize state machines */
- iriap_next_client_state(self, S_DISCONNECT);
- iriap_next_call_state(self, S_MAKE_CALL);
- iriap_next_server_state(self, R_DISCONNECT);
- iriap_next_r_connect_state(self, R_WAITING);
-
- return self;
-}
-EXPORT_SYMBOL(iriap_open);
-
-/*
- * Function __iriap_close (self)
- *
- * Removes (deallocates) the IrIAP instance
- *
- */
-static void __iriap_close(struct iriap_cb *self)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
- del_timer(&self->watchdog_timer);
-
- if (self->request_skb)
- dev_kfree_skb(self->request_skb);
-
- self->magic = 0;
-
- kfree(self);
-}
-
-/*
- * Function iriap_close (void)
- *
- * Closes IrIAP and deregisters with IrLMP
- */
-void iriap_close(struct iriap_cb *self)
-{
- struct iriap_cb *entry;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
- if (self->lsap) {
- irlmp_close_lsap(self->lsap);
- self->lsap = NULL;
- }
-
- entry = (struct iriap_cb *) hashbin_remove(iriap, (long) self, NULL);
- IRDA_ASSERT(entry == self, return;);
-
- __iriap_close(self);
-}
-EXPORT_SYMBOL(iriap_close);
-
-static int iriap_register_lsap(struct iriap_cb *self, __u8 slsap_sel, int mode)
-{
- notify_t notify;
-
- irda_notify_init(&notify);
- notify.connect_confirm = iriap_connect_confirm;
- notify.connect_indication = iriap_connect_indication;
- notify.disconnect_indication = iriap_disconnect_indication;
- notify.data_indication = iriap_data_indication;
- notify.instance = self;
- if (mode == IAS_CLIENT)
- strcpy(notify.name, "IrIAS cli");
- else
- strcpy(notify.name, "IrIAS srv");
-
- self->lsap = irlmp_open_lsap(slsap_sel, &notify, 0);
- if (self->lsap == NULL) {
- net_err_ratelimited("%s: Unable to allocated LSAP!\n",
- __func__);
- return -1;
- }
- self->slsap_sel = self->lsap->slsap_sel;
-
- return 0;
-}
-
-/*
- * Function iriap_disconnect_indication (handle, reason)
- *
- * Got disconnect, so clean up everything associated with this connection
- *
- */
-static void iriap_disconnect_indication(void *instance, void *sap,
- LM_REASON reason,
- struct sk_buff *skb)
-{
- struct iriap_cb *self;
-
- pr_debug("%s(), reason=%s [%d]\n", __func__,
- irlmp_reason_str(reason), reason);
-
- self = instance;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
- IRDA_ASSERT(iriap != NULL, return;);
-
- del_timer(&self->watchdog_timer);
-
- /* Not needed */
- if (skb)
- dev_kfree_skb(skb);
-
- if (self->mode == IAS_CLIENT) {
- pr_debug("%s(), disconnect as client\n", __func__);
-
-
- iriap_do_client_event(self, IAP_LM_DISCONNECT_INDICATION,
- NULL);
- /*
- * Inform service user that the request failed by sending
- * it a NULL value. Warning, the client might close us, so
- * remember no to use self anymore after calling confirm
- */
- if (self->confirm)
- self->confirm(IAS_DISCONNECT, 0, NULL, self->priv);
- } else {
- pr_debug("%s(), disconnect as server\n", __func__);
- iriap_do_server_event(self, IAP_LM_DISCONNECT_INDICATION,
- NULL);
- iriap_close(self);
- }
-}
-
-/*
- * Function iriap_disconnect_request (handle)
- */
-static void iriap_disconnect_request(struct iriap_cb *self)
-{
- struct sk_buff *tx_skb;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
- tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
- if (tx_skb == NULL) {
- pr_debug("%s(), Could not allocate an sk_buff of length %d\n",
- __func__, LMP_MAX_HEADER);
- return;
- }
-
- /*
- * Reserve space for MUX control and LAP header
- */
- skb_reserve(tx_skb, LMP_MAX_HEADER);
-
- irlmp_disconnect_request(self->lsap, tx_skb);
-}
-
-/*
- * Function iriap_getvaluebyclass (addr, name, attr)
- *
- * Retrieve all values from attribute in all objects with given class
- * name
- */
-int iriap_getvaluebyclass_request(struct iriap_cb *self,
- __u32 saddr, __u32 daddr,
- char *name, char *attr)
-{
- struct sk_buff *tx_skb;
- int name_len, attr_len, skb_len;
- __u8 *frame;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IAS_MAGIC, return -1;);
-
- /* Client must supply the destination device address */
- if (!daddr)
- return -1;
-
- self->daddr = daddr;
- self->saddr = saddr;
-
- /*
- * Save operation, so we know what the later indication is about
- */
- self->operation = GET_VALUE_BY_CLASS;
-
- /* Give ourselves 10 secs to finish this operation */
- iriap_start_watchdog_timer(self, 10*HZ);
-
- name_len = strlen(name); /* Up to IAS_MAX_CLASSNAME = 60 */
- attr_len = strlen(attr); /* Up to IAS_MAX_ATTRIBNAME = 60 */
-
- skb_len = self->max_header_size+2+name_len+1+attr_len+4;
- tx_skb = alloc_skb(skb_len, GFP_ATOMIC);
- if (!tx_skb)
- return -ENOMEM;
-
- /* Reserve space for MUX and LAP header */
- skb_reserve(tx_skb, self->max_header_size);
- skb_put(tx_skb, 3+name_len+attr_len);
- frame = tx_skb->data;
-
- /* Build frame */
- frame[0] = IAP_LST | GET_VALUE_BY_CLASS;
- frame[1] = name_len; /* Insert length of name */
- memcpy(frame+2, name, name_len); /* Insert name */
- frame[2+name_len] = attr_len; /* Insert length of attr */
- memcpy(frame+3+name_len, attr, attr_len); /* Insert attr */
-
- iriap_do_client_event(self, IAP_CALL_REQUEST_GVBC, tx_skb);
-
- /* Drop reference count - see state_s_disconnect(). */
- dev_kfree_skb(tx_skb);
-
- return 0;
-}
-EXPORT_SYMBOL(iriap_getvaluebyclass_request);
-
-/*
- * Function iriap_getvaluebyclass_confirm (self, skb)
- *
- * Got result from GetValueByClass command. Parse it and return result
- * to service user.
- *
- */
-static void iriap_getvaluebyclass_confirm(struct iriap_cb *self,
- struct sk_buff *skb)
-{
- struct ias_value *value;
- int charset;
- __u32 value_len;
- __u32 tmp_cpu32;
- __u16 obj_id;
- __u16 len;
- __u8 type;
- __u8 *fp;
- int n;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
- IRDA_ASSERT(skb != NULL, return;);
-
- /* Initialize variables */
- fp = skb->data;
- n = 2;
-
- /* Get length, MSB first */
- len = get_unaligned_be16(fp + n);
- n += 2;
-
- pr_debug("%s(), len=%d\n", __func__, len);
-
- /* Get object ID, MSB first */
- obj_id = get_unaligned_be16(fp + n);
- n += 2;
-
- type = fp[n++];
- pr_debug("%s(), Value type = %d\n", __func__, type);
-
- switch (type) {
- case IAS_INTEGER:
- memcpy(&tmp_cpu32, fp+n, 4); n += 4;
- be32_to_cpus(&tmp_cpu32);
- value = irias_new_integer_value(tmp_cpu32);
-
- /* Legal values restricted to 0x01-0x6f, page 15 irttp */
- pr_debug("%s(), lsap=%d\n", __func__, value->t.integer);
- break;
- case IAS_STRING:
- charset = fp[n++];
-
- switch (charset) {
- case CS_ASCII:
- break;
-/* case CS_ISO_8859_1: */
-/* case CS_ISO_8859_2: */
-/* case CS_ISO_8859_3: */
-/* case CS_ISO_8859_4: */
-/* case CS_ISO_8859_5: */
-/* case CS_ISO_8859_6: */
-/* case CS_ISO_8859_7: */
-/* case CS_ISO_8859_8: */
-/* case CS_ISO_8859_9: */
-/* case CS_UNICODE: */
- default:
- pr_debug("%s(), charset [%d] %s, not supported\n",
- __func__, charset,
- charset < ARRAY_SIZE(ias_charset_types) ?
- ias_charset_types[charset] :
- "(unknown)");
-
- /* Aborting, close connection! */
- iriap_disconnect_request(self);
- return;
- /* break; */
- }
- value_len = fp[n++];
- pr_debug("%s(), strlen=%d\n", __func__, value_len);
-
- /* Make sure the string is null-terminated */
- if (n + value_len < skb->len)
- fp[n + value_len] = 0x00;
- pr_debug("Got string %s\n", fp+n);
-
- /* Will truncate to IAS_MAX_STRING bytes */
- value = irias_new_string_value(fp+n);
- break;
- case IAS_OCT_SEQ:
- value_len = get_unaligned_be16(fp + n);
- n += 2;
-
- /* Will truncate to IAS_MAX_OCTET_STRING bytes */
- value = irias_new_octseq_value(fp+n, value_len);
- break;
- default:
- value = irias_new_missing_value();
- break;
- }
-
- /* Finished, close connection! */
- iriap_disconnect_request(self);
-
- /* Warning, the client might close us, so remember no to use self
- * anymore after calling confirm
- */
- if (self->confirm)
- self->confirm(IAS_SUCCESS, obj_id, value, self->priv);
- else {
- pr_debug("%s(), missing handler!\n", __func__);
- irias_delete_value(value);
- }
-}
-
-/*
- * Function iriap_getvaluebyclass_response ()
- *
- * Send answer back to remote LM-IAS
- *
- */
-static void iriap_getvaluebyclass_response(struct iriap_cb *self,
- __u16 obj_id,
- __u8 ret_code,
- struct ias_value *value)
-{
- struct sk_buff *tx_skb;
- int n;
- __be32 tmp_be32;
- __be16 tmp_be16;
- __u8 *fp;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
- IRDA_ASSERT(value != NULL, return;);
- IRDA_ASSERT(value->len <= 1024, return;);
-
- /* Initialize variables */
- n = 0;
-
- /*
- * We must adjust the size of the response after the length of the
- * value. We add 32 bytes because of the 6 bytes for the frame and
- * max 5 bytes for the value coding.
- */
- tx_skb = alloc_skb(value->len + self->max_header_size + 32,
- GFP_ATOMIC);
- if (!tx_skb)
- return;
-
- /* Reserve space for MUX and LAP header */
- skb_reserve(tx_skb, self->max_header_size);
- skb_put(tx_skb, 6);
-
- fp = tx_skb->data;
-
- /* Build frame */
- fp[n++] = GET_VALUE_BY_CLASS | IAP_LST;
- fp[n++] = ret_code;
-
- /* Insert list length (MSB first) */
- tmp_be16 = htons(0x0001);
- memcpy(fp+n, &tmp_be16, 2); n += 2;
-
- /* Insert object identifier ( MSB first) */
- tmp_be16 = cpu_to_be16(obj_id);
- memcpy(fp+n, &tmp_be16, 2); n += 2;
-
- switch (value->type) {
- case IAS_STRING:
- skb_put(tx_skb, 3 + value->len);
- fp[n++] = value->type;
- fp[n++] = 0; /* ASCII */
- fp[n++] = (__u8) value->len;
- memcpy(fp+n, value->t.string, value->len); n+=value->len;
- break;
- case IAS_INTEGER:
- skb_put(tx_skb, 5);
- fp[n++] = value->type;
-
- tmp_be32 = cpu_to_be32(value->t.integer);
- memcpy(fp+n, &tmp_be32, 4); n += 4;
- break;
- case IAS_OCT_SEQ:
- skb_put(tx_skb, 3 + value->len);
- fp[n++] = value->type;
-
- tmp_be16 = cpu_to_be16(value->len);
- memcpy(fp+n, &tmp_be16, 2); n += 2;
- memcpy(fp+n, value->t.oct_seq, value->len); n+=value->len;
- break;
- case IAS_MISSING:
- pr_debug("%s: sending IAS_MISSING\n", __func__);
- skb_put(tx_skb, 1);
- fp[n++] = value->type;
- break;
- default:
- pr_debug("%s(), type not implemented!\n", __func__);
- break;
- }
- iriap_do_r_connect_event(self, IAP_CALL_RESPONSE, tx_skb);
-
- /* Drop reference count - see state_r_execute(). */
- dev_kfree_skb(tx_skb);
-}
-
-/*
- * Function iriap_getvaluebyclass_indication (self, skb)
- *
- * getvaluebyclass is requested from peer LM-IAS
- *
- */
-static void iriap_getvaluebyclass_indication(struct iriap_cb *self,
- struct sk_buff *skb)
-{
- struct ias_object *obj;
- struct ias_attrib *attrib;
- int name_len;
- int attr_len;
- char name[IAS_MAX_CLASSNAME + 1]; /* 60 bytes */
- char attr[IAS_MAX_ATTRIBNAME + 1]; /* 60 bytes */
- __u8 *fp;
- int n;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
- IRDA_ASSERT(skb != NULL, return;);
-
- fp = skb->data;
- n = 1;
-
- name_len = fp[n++];
-
- IRDA_ASSERT(name_len < IAS_MAX_CLASSNAME + 1, return;);
-
- memcpy(name, fp+n, name_len); n+=name_len;
- name[name_len] = '\0';
-
- attr_len = fp[n++];
-
- IRDA_ASSERT(attr_len < IAS_MAX_ATTRIBNAME + 1, return;);
-
- memcpy(attr, fp+n, attr_len); n+=attr_len;
- attr[attr_len] = '\0';
-
- pr_debug("LM-IAS: Looking up %s: %s\n", name, attr);
- obj = irias_find_object(name);
-
- if (obj == NULL) {
- pr_debug("LM-IAS: Object %s not found\n", name);
- iriap_getvaluebyclass_response(self, 0x1235, IAS_CLASS_UNKNOWN,
- &irias_missing);
- return;
- }
- pr_debug("LM-IAS: found %s, id=%d\n", obj->name, obj->id);
-
- attrib = irias_find_attrib(obj, attr);
- if (attrib == NULL) {
- pr_debug("LM-IAS: Attribute %s not found\n", attr);
- iriap_getvaluebyclass_response(self, obj->id,
- IAS_ATTRIB_UNKNOWN,
- &irias_missing);
- return;
- }
-
- /* We have a match; send the value. */
- iriap_getvaluebyclass_response(self, obj->id, IAS_SUCCESS,
- attrib->value);
-}
-
-/*
- * Function iriap_send_ack (void)
- *
- * Currently not used
- *
- */
-void iriap_send_ack(struct iriap_cb *self)
-{
- struct sk_buff *tx_skb;
- __u8 *frame;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
- tx_skb = alloc_skb(LMP_MAX_HEADER + 1, GFP_ATOMIC);
- if (!tx_skb)
- return;
-
- /* Reserve space for MUX and LAP header */
- skb_reserve(tx_skb, self->max_header_size);
- skb_put(tx_skb, 1);
- frame = tx_skb->data;
-
- /* Build frame */
- frame[0] = IAP_LST | IAP_ACK | self->operation;
-
- irlmp_data_request(self->lsap, tx_skb);
-}
-
-void iriap_connect_request(struct iriap_cb *self)
-{
- int ret;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
- ret = irlmp_connect_request(self->lsap, LSAP_IAS,
- self->saddr, self->daddr,
- NULL, NULL);
- if (ret < 0) {
- pr_debug("%s(), connect failed!\n", __func__);
- self->confirm(IAS_DISCONNECT, 0, NULL, self->priv);
- }
-}
-
-/*
- * Function iriap_connect_confirm (handle, skb)
- *
- * LSAP connection confirmed!
- *
- */
-static void iriap_connect_confirm(void *instance, void *sap,
- struct qos_info *qos, __u32 max_seg_size,
- __u8 max_header_size,
- struct sk_buff *skb)
-{
- struct iriap_cb *self;
-
- self = instance;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
- IRDA_ASSERT(skb != NULL, return;);
-
- self->max_data_size = max_seg_size;
- self->max_header_size = max_header_size;
-
- del_timer(&self->watchdog_timer);
-
- iriap_do_client_event(self, IAP_LM_CONNECT_CONFIRM, skb);
-
- /* Drop reference count - see state_s_make_call(). */
- dev_kfree_skb(skb);
-}
-
-/*
- * Function iriap_connect_indication ( handle, skb)
- *
- * Remote LM-IAS is requesting connection
- *
- */
-static void iriap_connect_indication(void *instance, void *sap,
- struct qos_info *qos, __u32 max_seg_size,
- __u8 max_header_size,
- struct sk_buff *skb)
-{
- struct iriap_cb *self, *new;
-
- self = instance;
-
- IRDA_ASSERT(skb != NULL, return;);
- IRDA_ASSERT(self != NULL, goto out;);
- IRDA_ASSERT(self->magic == IAS_MAGIC, goto out;);
-
- /* Start new server */
- new = iriap_open(LSAP_IAS, IAS_SERVER, NULL, NULL);
- if (!new) {
- pr_debug("%s(), open failed\n", __func__);
- goto out;
- }
-
- /* Now attach up the new "socket" */
- new->lsap = irlmp_dup(self->lsap, new);
- if (!new->lsap) {
- pr_debug("%s(), dup failed!\n", __func__);
- goto out;
- }
-
- new->max_data_size = max_seg_size;
- new->max_header_size = max_header_size;
-
- /* Clean up the original one to keep it in listen state */
- irlmp_listen(self->lsap);
-
- iriap_do_server_event(new, IAP_LM_CONNECT_INDICATION, skb);
-
-out:
- /* Drop reference count - see state_r_disconnect(). */
- dev_kfree_skb(skb);
-}
-
-/*
- * Function iriap_data_indication (handle, skb)
- *
- * Receives data from connection identified by handle from IrLMP
- *
- */
-static int iriap_data_indication(void *instance, void *sap,
- struct sk_buff *skb)
-{
- struct iriap_cb *self;
- __u8 *frame;
- __u8 opcode;
-
- self = instance;
-
- IRDA_ASSERT(skb != NULL, return 0;);
- IRDA_ASSERT(self != NULL, goto out;);
- IRDA_ASSERT(self->magic == IAS_MAGIC, goto out;);
-
- frame = skb->data;
-
- if (self->mode == IAS_SERVER) {
- /* Call server */
- pr_debug("%s(), Calling server!\n", __func__);
- iriap_do_r_connect_event(self, IAP_RECV_F_LST, skb);
- goto out;
- }
- opcode = frame[0];
- if (~opcode & IAP_LST) {
- net_warn_ratelimited("%s:, IrIAS multiframe commands or results is not implemented yet!\n",
- __func__);
- goto out;
- }
-
- /* Check for ack frames since they don't contain any data */
- if (opcode & IAP_ACK) {
- pr_debug("%s() Got ack frame!\n", __func__);
- goto out;
- }
-
- opcode &= ~IAP_LST; /* Mask away LST bit */
-
- switch (opcode) {
- case GET_INFO_BASE:
- pr_debug("IrLMP GetInfoBaseDetails not implemented!\n");
- break;
- case GET_VALUE_BY_CLASS:
- iriap_do_call_event(self, IAP_RECV_F_LST, NULL);
-
- switch (frame[1]) {
- case IAS_SUCCESS:
- iriap_getvaluebyclass_confirm(self, skb);
- break;
- case IAS_CLASS_UNKNOWN:
- pr_debug("%s(), No such class!\n", __func__);
- /* Finished, close connection! */
- iriap_disconnect_request(self);
-
- /*
- * Warning, the client might close us, so remember
- * no to use self anymore after calling confirm
- */
- if (self->confirm)
- self->confirm(IAS_CLASS_UNKNOWN, 0, NULL,
- self->priv);
- break;
- case IAS_ATTRIB_UNKNOWN:
- pr_debug("%s(), No such attribute!\n", __func__);
- /* Finished, close connection! */
- iriap_disconnect_request(self);
-
- /*
- * Warning, the client might close us, so remember
- * no to use self anymore after calling confirm
- */
- if (self->confirm)
- self->confirm(IAS_ATTRIB_UNKNOWN, 0, NULL,
- self->priv);
- break;
- }
- break;
- default:
- pr_debug("%s(), Unknown op-code: %02x\n", __func__,
- opcode);
- break;
- }
-
-out:
- /* Cleanup - sub-calls will have done skb_get() as needed. */
- dev_kfree_skb(skb);
- return 0;
-}
-
-/*
- * Function iriap_call_indication (self, skb)
- *
- * Received call to server from peer LM-IAS
- *
- */
-void iriap_call_indication(struct iriap_cb *self, struct sk_buff *skb)
-{
- __u8 *fp;
- __u8 opcode;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
- IRDA_ASSERT(skb != NULL, return;);
-
- fp = skb->data;
-
- opcode = fp[0];
- if (~opcode & 0x80) {
- net_warn_ratelimited("%s: IrIAS multiframe commands or results is not implemented yet!\n",
- __func__);
- return;
- }
- opcode &= 0x7f; /* Mask away LST bit */
-
- switch (opcode) {
- case GET_INFO_BASE:
- net_warn_ratelimited("%s: GetInfoBaseDetails not implemented yet!\n",
- __func__);
- break;
- case GET_VALUE_BY_CLASS:
- iriap_getvaluebyclass_indication(self, skb);
- break;
- }
- /* skb will be cleaned up in iriap_data_indication */
-}
-
-/*
- * Function iriap_watchdog_timer_expired (data)
- *
- * Query has taken too long time, so abort
- *
- */
-static void iriap_watchdog_timer_expired(struct timer_list *t)
-{
- struct iriap_cb *self = from_timer(self, t, watchdog_timer);
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
- /* iriap_close(self); */
-}
-
-#ifdef CONFIG_PROC_FS
-
-static const char *const ias_value_types[] = {
- "IAS_MISSING",
- "IAS_INTEGER",
- "IAS_OCT_SEQ",
- "IAS_STRING"
-};
-
-static inline struct ias_object *irias_seq_idx(loff_t pos)
-{
- struct ias_object *obj;
-
- for (obj = (struct ias_object *) hashbin_get_first(irias_objects);
- obj; obj = (struct ias_object *) hashbin_get_next(irias_objects)) {
- if (pos-- == 0)
- break;
- }
-
- return obj;
-}
-
-static void *irias_seq_start(struct seq_file *seq, loff_t *pos)
-{
- spin_lock_irq(&irias_objects->hb_spinlock);
-
- return *pos ? irias_seq_idx(*pos - 1) : SEQ_START_TOKEN;
-}
-
-static void *irias_seq_next(struct seq_file *seq, void *v, loff_t *pos)
-{
- ++*pos;
-
- return (v == SEQ_START_TOKEN)
- ? (void *) hashbin_get_first(irias_objects)
- : (void *) hashbin_get_next(irias_objects);
-}
-
-static void irias_seq_stop(struct seq_file *seq, void *v)
-{
- spin_unlock_irq(&irias_objects->hb_spinlock);
-}
-
-static int irias_seq_show(struct seq_file *seq, void *v)
-{
- if (v == SEQ_START_TOKEN)
- seq_puts(seq, "LM-IAS Objects:\n");
- else {
- struct ias_object *obj = v;
- struct ias_attrib *attrib;
-
- IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return -EINVAL;);
-
- seq_printf(seq, "name: %s, id=%d\n",
- obj->name, obj->id);
-
- /* Careful for priority inversions here !
- * All other uses of attrib spinlock are independent of
- * the object spinlock, so we are safe. Jean II */
- spin_lock(&obj->attribs->hb_spinlock);
-
- /* List all attributes for this object */
- for (attrib = (struct ias_attrib *) hashbin_get_first(obj->attribs);
- attrib != NULL;
- attrib = (struct ias_attrib *) hashbin_get_next(obj->attribs)) {
-
- IRDA_ASSERT(attrib->magic == IAS_ATTRIB_MAGIC,
- goto outloop; );
-
- seq_printf(seq, " - Attribute name: \"%s\", ",
- attrib->name);
- seq_printf(seq, "value[%s]: ",
- ias_value_types[attrib->value->type]);
-
- switch (attrib->value->type) {
- case IAS_INTEGER:
- seq_printf(seq, "%d\n",
- attrib->value->t.integer);
- break;
- case IAS_STRING:
- seq_printf(seq, "\"%s\"\n",
- attrib->value->t.string);
- break;
- case IAS_OCT_SEQ:
- seq_printf(seq, "octet sequence (%d bytes)\n",
- attrib->value->len);
- break;
- case IAS_MISSING:
- seq_puts(seq, "missing\n");
- break;
- default:
- seq_printf(seq, "type %d?\n",
- attrib->value->type);
- }
- seq_putc(seq, '\n');
-
- }
- IRDA_ASSERT_LABEL(outloop:)
- spin_unlock(&obj->attribs->hb_spinlock);
- }
-
- return 0;
-}
-
-static const struct seq_operations irias_seq_ops = {
- .start = irias_seq_start,
- .next = irias_seq_next,
- .stop = irias_seq_stop,
- .show = irias_seq_show,
-};
-
-static int irias_seq_open(struct inode *inode, struct file *file)
-{
- IRDA_ASSERT( irias_objects != NULL, return -EINVAL;);
-
- return seq_open(file, &irias_seq_ops);
-}
-
-const struct file_operations irias_seq_fops = {
- .owner = THIS_MODULE,
- .open = irias_seq_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = seq_release,
-};
-
-#endif /* PROC_FS */
diff --git a/drivers/staging/irda/net/iriap_event.c b/drivers/staging/irda/net/iriap_event.c
deleted file mode 100644
index e6098b2e048a..000000000000
--- a/drivers/staging/irda/net/iriap_event.c
+++ /dev/null
@@ -1,496 +0,0 @@
-/*********************************************************************
- *
- * Filename: iriap_event.c
- * Version: 0.1
- * Description: IAP Finite State Machine
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Thu Aug 21 00:02:07 1997
- * Modified at: Wed Mar 1 11:28:34 2000
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1997, 1999-2000 Dag Brattli <dagb@cs.uit.no>,
- * All Rights Reserved.
- * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/slab.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/iriap.h>
-#include <net/irda/iriap_event.h>
-
-static void state_s_disconnect (struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb);
-static void state_s_connecting (struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb);
-static void state_s_call (struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb);
-
-static void state_s_make_call (struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb);
-static void state_s_calling (struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb);
-static void state_s_outstanding (struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb);
-static void state_s_replying (struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb);
-static void state_s_wait_for_call(struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb);
-static void state_s_wait_active (struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb);
-
-static void state_r_disconnect (struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb);
-static void state_r_call (struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb);
-static void state_r_waiting (struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb);
-static void state_r_wait_active (struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb);
-static void state_r_receiving (struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb);
-static void state_r_execute (struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb);
-static void state_r_returning (struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb);
-
-static void (*iriap_state[])(struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb) = {
- /* Client FSM */
- state_s_disconnect,
- state_s_connecting,
- state_s_call,
-
- /* S-Call FSM */
- state_s_make_call,
- state_s_calling,
- state_s_outstanding,
- state_s_replying,
- state_s_wait_for_call,
- state_s_wait_active,
-
- /* Server FSM */
- state_r_disconnect,
- state_r_call,
-
- /* R-Connect FSM */
- state_r_waiting,
- state_r_wait_active,
- state_r_receiving,
- state_r_execute,
- state_r_returning,
-};
-
-void iriap_next_client_state(struct iriap_cb *self, IRIAP_STATE state)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
- self->client_state = state;
-}
-
-void iriap_next_call_state(struct iriap_cb *self, IRIAP_STATE state)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
- self->call_state = state;
-}
-
-void iriap_next_server_state(struct iriap_cb *self, IRIAP_STATE state)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
- self->server_state = state;
-}
-
-void iriap_next_r_connect_state(struct iriap_cb *self, IRIAP_STATE state)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
- self->r_connect_state = state;
-}
-
-void iriap_do_client_event(struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
- (*iriap_state[ self->client_state]) (self, event, skb);
-}
-
-void iriap_do_call_event(struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
- (*iriap_state[ self->call_state]) (self, event, skb);
-}
-
-void iriap_do_server_event(struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
- (*iriap_state[ self->server_state]) (self, event, skb);
-}
-
-void iriap_do_r_connect_event(struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
- (*iriap_state[ self->r_connect_state]) (self, event, skb);
-}
-
-
-/*
- * Function state_s_disconnect (event, skb)
- *
- * S-Disconnect, The device has no LSAP connection to a particular
- * remote device.
- */
-static void state_s_disconnect(struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
- switch (event) {
- case IAP_CALL_REQUEST_GVBC:
- iriap_next_client_state(self, S_CONNECTING);
- IRDA_ASSERT(self->request_skb == NULL, return;);
- /* Don't forget to refcount it -
- * see iriap_getvaluebyclass_request(). */
- skb_get(skb);
- self->request_skb = skb;
- iriap_connect_request(self);
- break;
- case IAP_LM_DISCONNECT_INDICATION:
- break;
- default:
- pr_debug("%s(), Unknown event %d\n", __func__, event);
- break;
- }
-}
-
-/*
- * Function state_s_connecting (self, event, skb)
- *
- * S-Connecting
- *
- */
-static void state_s_connecting(struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
- switch (event) {
- case IAP_LM_CONNECT_CONFIRM:
- /*
- * Jump to S-Call FSM
- */
- iriap_do_call_event(self, IAP_CALL_REQUEST, skb);
- /* iriap_call_request(self, 0,0,0); */
- iriap_next_client_state(self, S_CALL);
- break;
- case IAP_LM_DISCONNECT_INDICATION:
- /* Abort calls */
- iriap_next_call_state(self, S_MAKE_CALL);
- iriap_next_client_state(self, S_DISCONNECT);
- break;
- default:
- pr_debug("%s(), Unknown event %d\n", __func__, event);
- break;
- }
-}
-
-/*
- * Function state_s_call (self, event, skb)
- *
- * S-Call, The device can process calls to a specific remote
- * device. Whenever the LSAP connection is disconnected, this state
- * catches that event and clears up
- */
-static void state_s_call(struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb)
-{
- IRDA_ASSERT(self != NULL, return;);
-
- switch (event) {
- case IAP_LM_DISCONNECT_INDICATION:
- /* Abort calls */
- iriap_next_call_state(self, S_MAKE_CALL);
- iriap_next_client_state(self, S_DISCONNECT);
- break;
- default:
- pr_debug("state_s_call: Unknown event %d\n", event);
- break;
- }
-}
-
-/*
- * Function state_s_make_call (event, skb)
- *
- * S-Make-Call
- *
- */
-static void state_s_make_call(struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb)
-{
- struct sk_buff *tx_skb;
-
- IRDA_ASSERT(self != NULL, return;);
-
- switch (event) {
- case IAP_CALL_REQUEST:
- /* Already refcounted - see state_s_disconnect() */
- tx_skb = self->request_skb;
- self->request_skb = NULL;
-
- irlmp_data_request(self->lsap, tx_skb);
- iriap_next_call_state(self, S_OUTSTANDING);
- break;
- default:
- pr_debug("%s(), Unknown event %d\n", __func__, event);
- break;
- }
-}
-
-/*
- * Function state_s_calling (event, skb)
- *
- * S-Calling
- *
- */
-static void state_s_calling(struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb)
-{
- pr_debug("%s(), Not implemented\n", __func__);
-}
-
-/*
- * Function state_s_outstanding (event, skb)
- *
- * S-Outstanding, The device is waiting for a response to a command
- *
- */
-static void state_s_outstanding(struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb)
-{
- IRDA_ASSERT(self != NULL, return;);
-
- switch (event) {
- case IAP_RECV_F_LST:
- /*iriap_send_ack(self);*/
- /*LM_Idle_request(idle); */
-
- iriap_next_call_state(self, S_WAIT_FOR_CALL);
- break;
- default:
- pr_debug("%s(), Unknown event %d\n", __func__, event);
- break;
- }
-}
-
-/*
- * Function state_s_replying (event, skb)
- *
- * S-Replying, The device is collecting a multiple part response
- */
-static void state_s_replying(struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb)
-{
- pr_debug("%s(), Not implemented\n", __func__);
-}
-
-/*
- * Function state_s_wait_for_call (event, skb)
- *
- * S-Wait-for-Call
- *
- */
-static void state_s_wait_for_call(struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb)
-{
- pr_debug("%s(), Not implemented\n", __func__);
-}
-
-
-/*
- * Function state_s_wait_active (event, skb)
- *
- * S-Wait-Active
- *
- */
-static void state_s_wait_active(struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb)
-{
- pr_debug("%s(), Not implemented\n", __func__);
-}
-
-/**************************************************************************
- *
- * Server FSM
- *
- **************************************************************************/
-
-/*
- * Function state_r_disconnect (self, event, skb)
- *
- * LM-IAS server is disconnected (not processing any requests!)
- *
- */
-static void state_r_disconnect(struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb)
-{
- struct sk_buff *tx_skb;
-
- switch (event) {
- case IAP_LM_CONNECT_INDICATION:
- tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
- if (tx_skb == NULL)
- return;
-
- /* Reserve space for MUX_CONTROL and LAP header */
- skb_reserve(tx_skb, LMP_MAX_HEADER);
-
- irlmp_connect_response(self->lsap, tx_skb);
- /*LM_Idle_request(idle); */
-
- iriap_next_server_state(self, R_CALL);
-
- /*
- * Jump to R-Connect FSM, we skip R-Waiting since we do not
- * care about LM_Idle_request()!
- */
- iriap_next_r_connect_state(self, R_RECEIVING);
- break;
- default:
- pr_debug("%s(), unknown event %d\n", __func__, event);
- break;
- }
-}
-
-/*
- * Function state_r_call (self, event, skb)
- */
-static void state_r_call(struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb)
-{
- switch (event) {
- case IAP_LM_DISCONNECT_INDICATION:
- /* Abort call */
- iriap_next_server_state(self, R_DISCONNECT);
- iriap_next_r_connect_state(self, R_WAITING);
- break;
- default:
- pr_debug("%s(), unknown event!\n", __func__);
- break;
- }
-}
-
-/*
- * R-Connect FSM
- */
-
-/*
- * Function state_r_waiting (self, event, skb)
- */
-static void state_r_waiting(struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb)
-{
- pr_debug("%s(), Not implemented\n", __func__);
-}
-
-static void state_r_wait_active(struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb)
-{
- pr_debug("%s(), Not implemented\n", __func__);
-}
-
-/*
- * Function state_r_receiving (self, event, skb)
- *
- * We are receiving a command
- *
- */
-static void state_r_receiving(struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb)
-{
- switch (event) {
- case IAP_RECV_F_LST:
- iriap_next_r_connect_state(self, R_EXECUTE);
-
- iriap_call_indication(self, skb);
- break;
- default:
- pr_debug("%s(), unknown event!\n", __func__);
- break;
- }
-}
-
-/*
- * Function state_r_execute (self, event, skb)
- *
- * The server is processing the request
- *
- */
-static void state_r_execute(struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb)
-{
- IRDA_ASSERT(skb != NULL, return;);
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
- switch (event) {
- case IAP_CALL_RESPONSE:
- /*
- * Since we don't implement the Waiting state, we return
- * to state Receiving instead, DB.
- */
- iriap_next_r_connect_state(self, R_RECEIVING);
-
- /* Don't forget to refcount it - see
- * iriap_getvaluebyclass_response(). */
- skb_get(skb);
-
- irlmp_data_request(self->lsap, skb);
- break;
- default:
- pr_debug("%s(), unknown event!\n", __func__);
- break;
- }
-}
-
-static void state_r_returning(struct iriap_cb *self, IRIAP_EVENT event,
- struct sk_buff *skb)
-{
- pr_debug("%s(), event=%d\n", __func__, event);
-
- switch (event) {
- case IAP_RECV_F_LST:
- break;
- default:
- break;
- }
-}
diff --git a/drivers/staging/irda/net/irias_object.c b/drivers/staging/irda/net/irias_object.c
deleted file mode 100644
index 53b86d0e1630..000000000000
--- a/drivers/staging/irda/net/irias_object.c
+++ /dev/null
@@ -1,555 +0,0 @@
-/*********************************************************************
- *
- * Filename: irias_object.c
- * Version: 0.3
- * Description: IAS object database and functions
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Thu Oct 1 22:50:04 1998
- * Modified at: Wed Dec 15 11:23:16 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
- *
- * 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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/socket.h>
-#include <linux/module.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irias_object.h>
-
-hashbin_t *irias_objects;
-
-/*
- * Used when a missing value needs to be returned
- */
-struct ias_value irias_missing = { IAS_MISSING, 0, 0, 0, {0}};
-
-
-/*
- * Function ias_new_object (name, id)
- *
- * Create a new IAS object
- *
- */
-struct ias_object *irias_new_object( char *name, int id)
-{
- struct ias_object *obj;
-
- obj = kzalloc(sizeof(struct ias_object), GFP_ATOMIC);
- if (obj == NULL) {
- net_warn_ratelimited("%s(), Unable to allocate object!\n",
- __func__);
- return NULL;
- }
-
- obj->magic = IAS_OBJECT_MAGIC;
- obj->name = kstrndup(name, IAS_MAX_CLASSNAME, GFP_ATOMIC);
- if (!obj->name) {
- net_warn_ratelimited("%s(), Unable to allocate name!\n",
- __func__);
- kfree(obj);
- return NULL;
- }
- obj->id = id;
-
- /* Locking notes : the attrib spinlock has lower precendence
- * than the objects spinlock. Never grap the objects spinlock
- * while holding any attrib spinlock (risk of deadlock). Jean II */
- obj->attribs = hashbin_new(HB_LOCK);
-
- if (obj->attribs == NULL) {
- net_warn_ratelimited("%s(), Unable to allocate attribs!\n",
- __func__);
- kfree(obj->name);
- kfree(obj);
- return NULL;
- }
-
- return obj;
-}
-EXPORT_SYMBOL(irias_new_object);
-
-/*
- * Function irias_delete_attrib (attrib)
- *
- * Delete given attribute and deallocate all its memory
- *
- */
-static void __irias_delete_attrib(struct ias_attrib *attrib)
-{
- IRDA_ASSERT(attrib != NULL, return;);
- IRDA_ASSERT(attrib->magic == IAS_ATTRIB_MAGIC, return;);
-
- kfree(attrib->name);
-
- irias_delete_value(attrib->value);
- attrib->magic = ~IAS_ATTRIB_MAGIC;
-
- kfree(attrib);
-}
-
-void __irias_delete_object(struct ias_object *obj)
-{
- IRDA_ASSERT(obj != NULL, return;);
- IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;);
-
- kfree(obj->name);
-
- hashbin_delete(obj->attribs, (FREE_FUNC) __irias_delete_attrib);
-
- obj->magic = ~IAS_OBJECT_MAGIC;
-
- kfree(obj);
-}
-
-/*
- * Function irias_delete_object (obj)
- *
- * Remove object from hashbin and deallocate all attributes associated with
- * with this object and the object itself
- *
- */
-int irias_delete_object(struct ias_object *obj)
-{
- struct ias_object *node;
-
- IRDA_ASSERT(obj != NULL, return -1;);
- IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return -1;);
-
- /* Remove from list */
- node = hashbin_remove_this(irias_objects, (irda_queue_t *) obj);
- if (!node)
- pr_debug("%s(), object already removed!\n",
- __func__);
-
- /* Destroy */
- __irias_delete_object(obj);
-
- return 0;
-}
-EXPORT_SYMBOL(irias_delete_object);
-
-/*
- * Function irias_delete_attrib (obj)
- *
- * Remove attribute from hashbin and, if it was the last attribute of
- * the object, remove the object as well.
- *
- */
-int irias_delete_attrib(struct ias_object *obj, struct ias_attrib *attrib,
- int cleanobject)
-{
- struct ias_attrib *node;
-
- IRDA_ASSERT(obj != NULL, return -1;);
- IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return -1;);
- IRDA_ASSERT(attrib != NULL, return -1;);
-
- /* Remove attribute from object */
- node = hashbin_remove_this(obj->attribs, (irda_queue_t *) attrib);
- if (!node)
- return 0; /* Already removed or non-existent */
-
- /* Deallocate attribute */
- __irias_delete_attrib(node);
-
- /* Check if object has still some attributes, destroy it if none.
- * At first glance, this look dangerous, as the kernel reference
- * various IAS objects. However, we only use this function on
- * user attributes, not kernel attributes, so there is no risk
- * of deleting a kernel object this way. Jean II */
- node = (struct ias_attrib *) hashbin_get_first(obj->attribs);
- if (cleanobject && !node)
- irias_delete_object(obj);
-
- return 0;
-}
-
-/*
- * Function irias_insert_object (obj)
- *
- * Insert an object into the LM-IAS database
- *
- */
-void irias_insert_object(struct ias_object *obj)
-{
- IRDA_ASSERT(obj != NULL, return;);
- IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;);
-
- hashbin_insert(irias_objects, (irda_queue_t *) obj, 0, obj->name);
-}
-EXPORT_SYMBOL(irias_insert_object);
-
-/*
- * Function irias_find_object (name)
- *
- * Find object with given name
- *
- */
-struct ias_object *irias_find_object(char *name)
-{
- IRDA_ASSERT(name != NULL, return NULL;);
-
- /* Unsafe (locking), object might change */
- return hashbin_lock_find(irias_objects, 0, name);
-}
-EXPORT_SYMBOL(irias_find_object);
-
-/*
- * Function irias_find_attrib (obj, name)
- *
- * Find named attribute in object
- *
- */
-struct ias_attrib *irias_find_attrib(struct ias_object *obj, char *name)
-{
- struct ias_attrib *attrib;
-
- IRDA_ASSERT(obj != NULL, return NULL;);
- IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return NULL;);
- IRDA_ASSERT(name != NULL, return NULL;);
-
- attrib = hashbin_lock_find(obj->attribs, 0, name);
- if (attrib == NULL)
- return NULL;
-
- /* Unsafe (locking), attrib might change */
- return attrib;
-}
-
-/*
- * Function irias_add_attribute (obj, attrib)
- *
- * Add attribute to object
- *
- */
-static void irias_add_attrib(struct ias_object *obj, struct ias_attrib *attrib,
- int owner)
-{
- IRDA_ASSERT(obj != NULL, return;);
- IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;);
-
- IRDA_ASSERT(attrib != NULL, return;);
- IRDA_ASSERT(attrib->magic == IAS_ATTRIB_MAGIC, return;);
-
- /* Set if attrib is owned by kernel or user space */
- attrib->value->owner = owner;
-
- hashbin_insert(obj->attribs, (irda_queue_t *) attrib, 0, attrib->name);
-}
-
-/*
- * Function irias_object_change_attribute (obj_name, attrib_name, new_value)
- *
- * Change the value of an objects attribute.
- *
- */
-int irias_object_change_attribute(char *obj_name, char *attrib_name,
- struct ias_value *new_value)
-{
- struct ias_object *obj;
- struct ias_attrib *attrib;
- unsigned long flags;
-
- /* Find object */
- obj = hashbin_lock_find(irias_objects, 0, obj_name);
- if (obj == NULL) {
- net_warn_ratelimited("%s: Unable to find object: %s\n",
- __func__, obj_name);
- return -1;
- }
-
- /* Slightly unsafe (obj might get removed under us) */
- spin_lock_irqsave(&obj->attribs->hb_spinlock, flags);
-
- /* Find attribute */
- attrib = hashbin_find(obj->attribs, 0, attrib_name);
- if (attrib == NULL) {
- net_warn_ratelimited("%s: Unable to find attribute: %s\n",
- __func__, attrib_name);
- spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags);
- return -1;
- }
-
- if ( attrib->value->type != new_value->type) {
- pr_debug("%s(), changing value type not allowed!\n",
- __func__);
- spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags);
- return -1;
- }
-
- /* Delete old value */
- irias_delete_value(attrib->value);
-
- /* Insert new value */
- attrib->value = new_value;
-
- /* Success */
- spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags);
- return 0;
-}
-EXPORT_SYMBOL(irias_object_change_attribute);
-
-/*
- * Function irias_object_add_integer_attrib (obj, name, value)
- *
- * Add an integer attribute to an LM-IAS object
- *
- */
-void irias_add_integer_attrib(struct ias_object *obj, char *name, int value,
- int owner)
-{
- struct ias_attrib *attrib;
-
- IRDA_ASSERT(obj != NULL, return;);
- IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;);
- IRDA_ASSERT(name != NULL, return;);
-
- attrib = kzalloc(sizeof(struct ias_attrib), GFP_ATOMIC);
- if (attrib == NULL) {
- net_warn_ratelimited("%s: Unable to allocate attribute!\n",
- __func__);
- return;
- }
-
- attrib->magic = IAS_ATTRIB_MAGIC;
- attrib->name = kstrndup(name, IAS_MAX_ATTRIBNAME, GFP_ATOMIC);
-
- /* Insert value */
- attrib->value = irias_new_integer_value(value);
- if (!attrib->name || !attrib->value) {
- net_warn_ratelimited("%s: Unable to allocate attribute!\n",
- __func__);
- if (attrib->value)
- irias_delete_value(attrib->value);
- kfree(attrib->name);
- kfree(attrib);
- return;
- }
-
- irias_add_attrib(obj, attrib, owner);
-}
-EXPORT_SYMBOL(irias_add_integer_attrib);
-
- /*
- * Function irias_add_octseq_attrib (obj, name, octet_seq, len)
- *
- * Add a octet sequence attribute to an LM-IAS object
- *
- */
-
-void irias_add_octseq_attrib(struct ias_object *obj, char *name, __u8 *octets,
- int len, int owner)
-{
- struct ias_attrib *attrib;
-
- IRDA_ASSERT(obj != NULL, return;);
- IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;);
-
- IRDA_ASSERT(name != NULL, return;);
- IRDA_ASSERT(octets != NULL, return;);
-
- attrib = kzalloc(sizeof(struct ias_attrib), GFP_ATOMIC);
- if (attrib == NULL) {
- net_warn_ratelimited("%s: Unable to allocate attribute!\n",
- __func__);
- return;
- }
-
- attrib->magic = IAS_ATTRIB_MAGIC;
- attrib->name = kstrndup(name, IAS_MAX_ATTRIBNAME, GFP_ATOMIC);
-
- attrib->value = irias_new_octseq_value( octets, len);
- if (!attrib->name || !attrib->value) {
- net_warn_ratelimited("%s: Unable to allocate attribute!\n",
- __func__);
- if (attrib->value)
- irias_delete_value(attrib->value);
- kfree(attrib->name);
- kfree(attrib);
- return;
- }
-
- irias_add_attrib(obj, attrib, owner);
-}
-EXPORT_SYMBOL(irias_add_octseq_attrib);
-
-/*
- * Function irias_object_add_string_attrib (obj, string)
- *
- * Add a string attribute to an LM-IAS object
- *
- */
-void irias_add_string_attrib(struct ias_object *obj, char *name, char *value,
- int owner)
-{
- struct ias_attrib *attrib;
-
- IRDA_ASSERT(obj != NULL, return;);
- IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;);
-
- IRDA_ASSERT(name != NULL, return;);
- IRDA_ASSERT(value != NULL, return;);
-
- attrib = kzalloc(sizeof( struct ias_attrib), GFP_ATOMIC);
- if (attrib == NULL) {
- net_warn_ratelimited("%s: Unable to allocate attribute!\n",
- __func__);
- return;
- }
-
- attrib->magic = IAS_ATTRIB_MAGIC;
- attrib->name = kstrndup(name, IAS_MAX_ATTRIBNAME, GFP_ATOMIC);
-
- attrib->value = irias_new_string_value(value);
- if (!attrib->name || !attrib->value) {
- net_warn_ratelimited("%s: Unable to allocate attribute!\n",
- __func__);
- if (attrib->value)
- irias_delete_value(attrib->value);
- kfree(attrib->name);
- kfree(attrib);
- return;
- }
-
- irias_add_attrib(obj, attrib, owner);
-}
-EXPORT_SYMBOL(irias_add_string_attrib);
-
-/*
- * Function irias_new_integer_value (integer)
- *
- * Create new IAS integer value
- *
- */
-struct ias_value *irias_new_integer_value(int integer)
-{
- struct ias_value *value;
-
- value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC);
- if (value == NULL)
- return NULL;
-
- value->type = IAS_INTEGER;
- value->len = 4;
- value->t.integer = integer;
-
- return value;
-}
-EXPORT_SYMBOL(irias_new_integer_value);
-
-/*
- * Function irias_new_string_value (string)
- *
- * Create new IAS string value
- *
- * Per IrLMP 1.1, 4.3.3.2, strings are up to 256 chars - Jean II
- */
-struct ias_value *irias_new_string_value(char *string)
-{
- struct ias_value *value;
-
- value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC);
- if (value == NULL)
- return NULL;
-
- value->type = IAS_STRING;
- value->charset = CS_ASCII;
- value->t.string = kstrndup(string, IAS_MAX_STRING, GFP_ATOMIC);
- if (!value->t.string) {
- net_warn_ratelimited("%s: Unable to kmalloc!\n", __func__);
- kfree(value);
- return NULL;
- }
-
- value->len = strlen(value->t.string);
-
- return value;
-}
-
-/*
- * Function irias_new_octseq_value (octets, len)
- *
- * Create new IAS octet-sequence value
- *
- * Per IrLMP 1.1, 4.3.3.2, octet-sequence are up to 1024 bytes - Jean II
- */
-struct ias_value *irias_new_octseq_value(__u8 *octseq , int len)
-{
- struct ias_value *value;
-
- value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC);
- if (value == NULL)
- return NULL;
-
- value->type = IAS_OCT_SEQ;
- /* Check length */
- if(len > IAS_MAX_OCTET_STRING)
- len = IAS_MAX_OCTET_STRING;
- value->len = len;
-
- value->t.oct_seq = kmemdup(octseq, len, GFP_ATOMIC);
- if (value->t.oct_seq == NULL){
- net_warn_ratelimited("%s: Unable to kmalloc!\n", __func__);
- kfree(value);
- return NULL;
- }
- return value;
-}
-
-struct ias_value *irias_new_missing_value(void)
-{
- struct ias_value *value;
-
- value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC);
- if (value == NULL)
- return NULL;
-
- value->type = IAS_MISSING;
-
- return value;
-}
-
-/*
- * Function irias_delete_value (value)
- *
- * Delete IAS value
- *
- */
-void irias_delete_value(struct ias_value *value)
-{
- IRDA_ASSERT(value != NULL, return;);
-
- switch (value->type) {
- case IAS_INTEGER: /* Fallthrough */
- case IAS_MISSING:
- /* No need to deallocate */
- break;
- case IAS_STRING:
- /* Deallocate string */
- kfree(value->t.string);
- break;
- case IAS_OCT_SEQ:
- /* Deallocate byte stream */
- kfree(value->t.oct_seq);
- break;
- default:
- pr_debug("%s(), Unknown value type!\n", __func__);
- break;
- }
- kfree(value);
-}
-EXPORT_SYMBOL(irias_delete_value);
diff --git a/drivers/staging/irda/net/irlan/Kconfig b/drivers/staging/irda/net/irlan/Kconfig
deleted file mode 100644
index 951abc2e3a7f..000000000000
--- a/drivers/staging/irda/net/irlan/Kconfig
+++ /dev/null
@@ -1,14 +0,0 @@
-config IRLAN
- tristate "IrLAN protocol"
- depends on IRDA
- help
- Say Y here if you want to build support for the IrLAN protocol.
- To compile it as a module, choose M here: the module will be called
- irlan. IrLAN emulates an Ethernet and makes it possible to put up
- a wireless LAN using infrared beams.
-
- The IrLAN protocol can be used to talk with infrared access points
- like the HP NetbeamIR, or the ESI JetEye NET. You can also connect
- to another Linux machine running the IrLAN protocol for ad-hoc
- networking!
-
diff --git a/drivers/staging/irda/net/irlan/Makefile b/drivers/staging/irda/net/irlan/Makefile
deleted file mode 100644
index 94eefbc8e6b9..000000000000
--- a/drivers/staging/irda/net/irlan/Makefile
+++ /dev/null
@@ -1,7 +0,0 @@
-#
-# Makefile for the Linux IrDA IrLAN protocol layer.
-#
-
-obj-$(CONFIG_IRLAN) += irlan.o
-
-irlan-y := irlan_common.o irlan_eth.o irlan_event.o irlan_client.o irlan_provider.o irlan_filter.o irlan_provider_event.o irlan_client_event.o
diff --git a/drivers/staging/irda/net/irlan/irlan_client.c b/drivers/staging/irda/net/irlan/irlan_client.c
deleted file mode 100644
index 0b65e80849ae..000000000000
--- a/drivers/staging/irda/net/irlan/irlan_client.c
+++ /dev/null
@@ -1,559 +0,0 @@
-/*********************************************************************
- *
- * Filename: irlan_client.c
- * Version: 0.9
- * Description: IrDA LAN Access Protocol (IrLAN) Client
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sun Aug 31 20:14:37 1997
- * Modified at: Tue Dec 14 15:47:02 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- * Sources: skeleton.c by Donald Becker <becker@CESDIS.gsfc.nasa.gov>
- * slip.c by Laurence Culhane, <loz@holmes.demon.co.uk>
- * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
- *
- * Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
- * All Rights Reserved.
- *
- * 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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/if_arp.h>
-#include <linux/bitops.h>
-#include <net/arp.h>
-
-#include <asm/byteorder.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irttp.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/irias_object.h>
-#include <net/irda/iriap.h>
-#include <net/irda/timer.h>
-
-#include <net/irda/irlan_common.h>
-#include <net/irda/irlan_event.h>
-#include <net/irda/irlan_eth.h>
-#include <net/irda/irlan_provider.h>
-#include <net/irda/irlan_client.h>
-
-#undef CONFIG_IRLAN_GRATUITOUS_ARP
-
-static void irlan_client_ctrl_disconnect_indication(void *instance, void *sap,
- LM_REASON reason,
- struct sk_buff *);
-static int irlan_client_ctrl_data_indication(void *instance, void *sap,
- struct sk_buff *skb);
-static void irlan_client_ctrl_connect_confirm(void *instance, void *sap,
- struct qos_info *qos,
- __u32 max_sdu_size,
- __u8 max_header_size,
- struct sk_buff *);
-static void irlan_check_response_param(struct irlan_cb *self, char *param,
- char *value, int val_len);
-static void irlan_client_open_ctrl_tsap(struct irlan_cb *self);
-
-static void irlan_client_kick_timer_expired(struct timer_list *t)
-{
- struct irlan_cb *self = from_timer(self, t, client.kick_timer);
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- /*
- * If we are in peer mode, the client may not have got the discovery
- * indication it needs to make progress. If the client is still in
- * IDLE state, we must kick it to, but only if the provider is not IDLE
- */
- if ((self->provider.access_type == ACCESS_PEER) &&
- (self->client.state == IRLAN_IDLE) &&
- (self->provider.state != IRLAN_IDLE)) {
- irlan_client_wakeup(self, self->saddr, self->daddr);
- }
-}
-
-static void irlan_client_start_kick_timer(struct irlan_cb *self, int timeout)
-{
- irda_start_timer(&self->client.kick_timer, timeout,
- irlan_client_kick_timer_expired);
-}
-
-/*
- * Function irlan_client_wakeup (self, saddr, daddr)
- *
- * Wake up client
- *
- */
-void irlan_client_wakeup(struct irlan_cb *self, __u32 saddr, __u32 daddr)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- /*
- * Check if we are already awake, or if we are a provider in direct
- * mode (in that case we must leave the client idle
- */
- if ((self->client.state != IRLAN_IDLE) ||
- (self->provider.access_type == ACCESS_DIRECT))
- {
- pr_debug("%s(), already awake!\n", __func__);
- return;
- }
-
- /* Addresses may have changed! */
- self->saddr = saddr;
- self->daddr = daddr;
-
- if (self->disconnect_reason == LM_USER_REQUEST) {
- pr_debug("%s(), still stopped by user\n", __func__);
- return;
- }
-
- /* Open TSAPs */
- irlan_client_open_ctrl_tsap(self);
- irlan_open_data_tsap(self);
-
- irlan_do_client_event(self, IRLAN_DISCOVERY_INDICATION, NULL);
-
- /* Start kick timer */
- irlan_client_start_kick_timer(self, 2*HZ);
-}
-
-/*
- * Function irlan_discovery_indication (daddr)
- *
- * Remote device with IrLAN server support discovered
- *
- */
-void irlan_client_discovery_indication(discinfo_t *discovery,
- DISCOVERY_MODE mode,
- void *priv)
-{
- struct irlan_cb *self;
- __u32 saddr, daddr;
-
- IRDA_ASSERT(discovery != NULL, return;);
-
- /*
- * I didn't check it, but I bet that IrLAN suffer from the same
- * deficiency as IrComm and doesn't handle two instances
- * simultaneously connecting to each other.
- * Same workaround, drop passive discoveries.
- * Jean II */
- if(mode == DISCOVERY_PASSIVE)
- return;
-
- saddr = discovery->saddr;
- daddr = discovery->daddr;
-
- /* Find instance */
- rcu_read_lock();
- self = irlan_get_any();
- if (self) {
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, goto out;);
-
- pr_debug("%s(), Found instance (%08x)!\n", __func__ ,
- daddr);
-
- irlan_client_wakeup(self, saddr, daddr);
- }
-IRDA_ASSERT_LABEL(out:)
- rcu_read_unlock();
-}
-
-/*
- * Function irlan_client_data_indication (handle, skb)
- *
- * This function gets the data that is received on the control channel
- *
- */
-static int irlan_client_ctrl_data_indication(void *instance, void *sap,
- struct sk_buff *skb)
-{
- struct irlan_cb *self;
-
- self = instance;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
- IRDA_ASSERT(skb != NULL, return -1;);
-
- irlan_do_client_event(self, IRLAN_DATA_INDICATION, skb);
-
- /* Ready for a new command */
- pr_debug("%s(), clearing tx_busy\n", __func__);
- self->client.tx_busy = FALSE;
-
- /* Check if we have some queued commands waiting to be sent */
- irlan_run_ctrl_tx_queue(self);
-
- return 0;
-}
-
-static void irlan_client_ctrl_disconnect_indication(void *instance, void *sap,
- LM_REASON reason,
- struct sk_buff *userdata)
-{
- struct irlan_cb *self;
- struct tsap_cb *tsap;
- struct sk_buff *skb;
-
- pr_debug("%s(), reason=%d\n", __func__ , reason);
-
- self = instance;
- tsap = sap;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
- IRDA_ASSERT(tsap != NULL, return;);
- IRDA_ASSERT(tsap->magic == TTP_TSAP_MAGIC, return;);
-
- IRDA_ASSERT(tsap == self->client.tsap_ctrl, return;);
-
- /* Remove frames queued on the control channel */
- while ((skb = skb_dequeue(&self->client.txq)) != NULL) {
- dev_kfree_skb(skb);
- }
- self->client.tx_busy = FALSE;
-
- irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL);
-}
-
-/*
- * Function irlan_client_open_tsaps (self)
- *
- * Initialize callbacks and open IrTTP TSAPs
- *
- */
-static void irlan_client_open_ctrl_tsap(struct irlan_cb *self)
-{
- struct tsap_cb *tsap;
- notify_t notify;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- /* Check if already open */
- if (self->client.tsap_ctrl)
- return;
-
- irda_notify_init(&notify);
-
- /* Set up callbacks */
- notify.data_indication = irlan_client_ctrl_data_indication;
- notify.connect_confirm = irlan_client_ctrl_connect_confirm;
- notify.disconnect_indication = irlan_client_ctrl_disconnect_indication;
- notify.instance = self;
- strlcpy(notify.name, "IrLAN ctrl (c)", sizeof(notify.name));
-
- tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT, &notify);
- if (!tsap) {
- pr_debug("%s(), Got no tsap!\n", __func__);
- return;
- }
- self->client.tsap_ctrl = tsap;
-}
-
-/*
- * Function irlan_client_connect_confirm (handle, skb)
- *
- * Connection to peer IrLAN laye confirmed
- *
- */
-static void irlan_client_ctrl_connect_confirm(void *instance, void *sap,
- struct qos_info *qos,
- __u32 max_sdu_size,
- __u8 max_header_size,
- struct sk_buff *skb)
-{
- struct irlan_cb *self;
-
- self = instance;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- self->client.max_sdu_size = max_sdu_size;
- self->client.max_header_size = max_header_size;
-
- /* TODO: we could set the MTU depending on the max_sdu_size */
-
- irlan_do_client_event(self, IRLAN_CONNECT_COMPLETE, NULL);
-}
-
-/*
- * Function print_ret_code (code)
- *
- * Print return code of request to peer IrLAN layer.
- *
- */
-static void print_ret_code(__u8 code)
-{
- switch(code) {
- case 0:
- printk(KERN_INFO "Success\n");
- break;
- case 1:
- net_warn_ratelimited("IrLAN: Insufficient resources\n");
- break;
- case 2:
- net_warn_ratelimited("IrLAN: Invalid command format\n");
- break;
- case 3:
- net_warn_ratelimited("IrLAN: Command not supported\n");
- break;
- case 4:
- net_warn_ratelimited("IrLAN: Parameter not supported\n");
- break;
- case 5:
- net_warn_ratelimited("IrLAN: Value not supported\n");
- break;
- case 6:
- net_warn_ratelimited("IrLAN: Not open\n");
- break;
- case 7:
- net_warn_ratelimited("IrLAN: Authentication required\n");
- break;
- case 8:
- net_warn_ratelimited("IrLAN: Invalid password\n");
- break;
- case 9:
- net_warn_ratelimited("IrLAN: Protocol error\n");
- break;
- case 255:
- net_warn_ratelimited("IrLAN: Asynchronous status\n");
- break;
- }
-}
-
-/*
- * Function irlan_client_parse_response (self, skb)
- *
- * Extract all parameters from received buffer, then feed them to
- * check_params for parsing
- */
-void irlan_client_parse_response(struct irlan_cb *self, struct sk_buff *skb)
-{
- __u8 *frame;
- __u8 *ptr;
- int count;
- int ret;
- __u16 val_len;
- int i;
- char *name;
- char *value;
-
- IRDA_ASSERT(skb != NULL, return;);
-
- pr_debug("%s() skb->len=%d\n", __func__ , (int)skb->len);
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- if (!skb) {
- net_err_ratelimited("%s(), Got NULL skb!\n", __func__);
- return;
- }
- frame = skb->data;
-
- /*
- * Check return code and print it if not success
- */
- if (frame[0]) {
- print_ret_code(frame[0]);
- return;
- }
-
- name = kmalloc(255, GFP_ATOMIC);
- if (!name)
- return;
- value = kmalloc(1016, GFP_ATOMIC);
- if (!value) {
- kfree(name);
- return;
- }
-
- /* How many parameters? */
- count = frame[1];
-
- pr_debug("%s(), got %d parameters\n", __func__ , count);
-
- ptr = frame+2;
-
- /* For all parameters */
- for (i=0; i<count;i++) {
- ret = irlan_extract_param(ptr, name, value, &val_len);
- if (ret < 0) {
- pr_debug("%s(), IrLAN, Error!\n", __func__);
- break;
- }
- ptr += ret;
- irlan_check_response_param(self, name, value, val_len);
- }
- /* Cleanup */
- kfree(name);
- kfree(value);
-}
-
-/*
- * Function irlan_check_response_param (self, param, value, val_len)
- *
- * Check which parameter is received and update local variables
- *
- */
-static void irlan_check_response_param(struct irlan_cb *self, char *param,
- char *value, int val_len)
-{
- __u16 tmp_cpu; /* Temporary value in host order */
- __u8 *bytes;
- int i;
-
- pr_debug("%s(), parm=%s\n", __func__ , param);
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- /* Media type */
- if (strcmp(param, "MEDIA") == 0) {
- if (strcmp(value, "802.3") == 0)
- self->media = MEDIA_802_3;
- else
- self->media = MEDIA_802_5;
- return;
- }
- if (strcmp(param, "FILTER_TYPE") == 0) {
- if (strcmp(value, "DIRECTED") == 0)
- self->client.filter_type |= IRLAN_DIRECTED;
- else if (strcmp(value, "FUNCTIONAL") == 0)
- self->client.filter_type |= IRLAN_FUNCTIONAL;
- else if (strcmp(value, "GROUP") == 0)
- self->client.filter_type |= IRLAN_GROUP;
- else if (strcmp(value, "MAC_FRAME") == 0)
- self->client.filter_type |= IRLAN_MAC_FRAME;
- else if (strcmp(value, "MULTICAST") == 0)
- self->client.filter_type |= IRLAN_MULTICAST;
- else if (strcmp(value, "BROADCAST") == 0)
- self->client.filter_type |= IRLAN_BROADCAST;
- else if (strcmp(value, "IPX_SOCKET") == 0)
- self->client.filter_type |= IRLAN_IPX_SOCKET;
-
- }
- if (strcmp(param, "ACCESS_TYPE") == 0) {
- if (strcmp(value, "DIRECT") == 0)
- self->client.access_type = ACCESS_DIRECT;
- else if (strcmp(value, "PEER") == 0)
- self->client.access_type = ACCESS_PEER;
- else if (strcmp(value, "HOSTED") == 0)
- self->client.access_type = ACCESS_HOSTED;
- else {
- pr_debug("%s(), unknown access type!\n", __func__);
- }
- }
- /* IRLAN version */
- if (strcmp(param, "IRLAN_VER") == 0) {
- pr_debug("IrLAN version %d.%d\n", (__u8)value[0],
- (__u8)value[1]);
-
- self->version[0] = value[0];
- self->version[1] = value[1];
- return;
- }
- /* Which remote TSAP to use for data channel */
- if (strcmp(param, "DATA_CHAN") == 0) {
- self->dtsap_sel_data = value[0];
- pr_debug("Data TSAP = %02x\n", self->dtsap_sel_data);
- return;
- }
- if (strcmp(param, "CON_ARB") == 0) {
- memcpy(&tmp_cpu, value, 2); /* Align value */
- le16_to_cpus(&tmp_cpu); /* Convert to host order */
- self->client.recv_arb_val = tmp_cpu;
- pr_debug("%s(), receive arb val=%d\n", __func__ ,
- self->client.recv_arb_val);
- }
- if (strcmp(param, "MAX_FRAME") == 0) {
- memcpy(&tmp_cpu, value, 2); /* Align value */
- le16_to_cpus(&tmp_cpu); /* Convert to host order */
- self->client.max_frame = tmp_cpu;
- pr_debug("%s(), max frame=%d\n", __func__ ,
- self->client.max_frame);
- }
-
- /* RECONNECT_KEY, in case the link goes down! */
- if (strcmp(param, "RECONNECT_KEY") == 0) {
- pr_debug("Got reconnect key: ");
- /* for (i = 0; i < val_len; i++) */
-/* printk("%02x", value[i]); */
- memcpy(self->client.reconnect_key, value, val_len);
- self->client.key_len = val_len;
- pr_debug("\n");
- }
- /* FILTER_ENTRY, have we got an ethernet address? */
- if (strcmp(param, "FILTER_ENTRY") == 0) {
- bytes = value;
- pr_debug("Ethernet address = %pM\n", bytes);
- for (i = 0; i < 6; i++)
- self->dev->dev_addr[i] = bytes[i];
- }
-}
-
-/*
- * Function irlan_client_get_value_confirm (obj_id, value)
- *
- * Got results from remote LM-IAS
- *
- */
-void irlan_client_get_value_confirm(int result, __u16 obj_id,
- struct ias_value *value, void *priv)
-{
- struct irlan_cb *self;
-
- IRDA_ASSERT(priv != NULL, return;);
-
- self = priv;
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- /* We probably don't need to make any more queries */
- iriap_close(self->client.iriap);
- self->client.iriap = NULL;
-
- /* Check if request succeeded */
- if (result != IAS_SUCCESS) {
- pr_debug("%s(), got NULL value!\n", __func__);
- irlan_do_client_event(self, IRLAN_IAS_PROVIDER_NOT_AVAIL,
- NULL);
- return;
- }
-
- switch (value->type) {
- case IAS_INTEGER:
- self->dtsap_sel_ctrl = value->t.integer;
-
- if (value->t.integer != -1) {
- irlan_do_client_event(self, IRLAN_IAS_PROVIDER_AVAIL,
- NULL);
- return;
- }
- irias_delete_value(value);
- break;
- default:
- pr_debug("%s(), unknown type!\n", __func__);
- break;
- }
- irlan_do_client_event(self, IRLAN_IAS_PROVIDER_NOT_AVAIL, NULL);
-}
diff --git a/drivers/staging/irda/net/irlan/irlan_client_event.c b/drivers/staging/irda/net/irlan/irlan_client_event.c
deleted file mode 100644
index cc93fabbbb19..000000000000
--- a/drivers/staging/irda/net/irlan/irlan_client_event.c
+++ /dev/null
@@ -1,511 +0,0 @@
-/*********************************************************************
- *
- * Filename: irlan_client_event.c
- * Version: 0.9
- * Description: IrLAN client state machine
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sun Aug 31 20:14:37 1997
- * Modified at: Sun Dec 26 21:52:24 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
- * All Rights Reserved.
- *
- * 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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/skbuff.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/timer.h>
-#include <net/irda/irmod.h>
-#include <net/irda/iriap.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/irttp.h>
-
-#include <net/irda/irlan_common.h>
-#include <net/irda/irlan_client.h>
-#include <net/irda/irlan_event.h>
-
-static int irlan_client_state_idle (struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb);
-static int irlan_client_state_query(struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb);
-static int irlan_client_state_conn (struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb);
-static int irlan_client_state_info (struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb);
-static int irlan_client_state_media(struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb);
-static int irlan_client_state_open (struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb);
-static int irlan_client_state_wait (struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb);
-static int irlan_client_state_arb (struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb);
-static int irlan_client_state_data (struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb);
-static int irlan_client_state_close(struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb);
-static int irlan_client_state_sync (struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb);
-
-static int (*state[])(struct irlan_cb *, IRLAN_EVENT event, struct sk_buff *) =
-{
- irlan_client_state_idle,
- irlan_client_state_query,
- irlan_client_state_conn,
- irlan_client_state_info,
- irlan_client_state_media,
- irlan_client_state_open,
- irlan_client_state_wait,
- irlan_client_state_arb,
- irlan_client_state_data,
- irlan_client_state_close,
- irlan_client_state_sync
-};
-
-void irlan_do_client_event(struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- (*state[ self->client.state]) (self, event, skb);
-}
-
-/*
- * Function irlan_client_state_idle (event, skb, info)
- *
- * IDLE, We are waiting for an indication that there is a provider
- * available.
- */
-static int irlan_client_state_idle(struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb)
-{
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
-
- switch (event) {
- case IRLAN_DISCOVERY_INDICATION:
- if (self->client.iriap) {
- net_warn_ratelimited("%s(), busy with a previous query\n",
- __func__);
- return -EBUSY;
- }
-
- self->client.iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
- irlan_client_get_value_confirm);
- /* Get some values from peer IAS */
- irlan_next_client_state(self, IRLAN_QUERY);
- iriap_getvaluebyclass_request(self->client.iriap,
- self->saddr, self->daddr,
- "IrLAN", "IrDA:TinyTP:LsapSel");
- break;
- case IRLAN_WATCHDOG_TIMEOUT:
- pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
- break;
- default:
- pr_debug("%s(), Unknown event %d\n", __func__ , event);
- break;
- }
- if (skb)
- dev_kfree_skb(skb);
-
- return 0;
-}
-
-/*
- * Function irlan_client_state_query (event, skb, info)
- *
- * QUERY, We have queryed the remote IAS and is ready to connect
- * to provider, just waiting for the confirm.
- *
- */
-static int irlan_client_state_query(struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb)
-{
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
-
- switch(event) {
- case IRLAN_IAS_PROVIDER_AVAIL:
- IRDA_ASSERT(self->dtsap_sel_ctrl != 0, return -1;);
-
- self->client.open_retries = 0;
-
- irttp_connect_request(self->client.tsap_ctrl,
- self->dtsap_sel_ctrl,
- self->saddr, self->daddr, NULL,
- IRLAN_MTU, NULL);
- irlan_next_client_state(self, IRLAN_CONN);
- break;
- case IRLAN_IAS_PROVIDER_NOT_AVAIL:
- pr_debug("%s(), IAS_PROVIDER_NOT_AVAIL\n", __func__);
- irlan_next_client_state(self, IRLAN_IDLE);
-
- /* Give the client a kick! */
- if ((self->provider.access_type == ACCESS_PEER) &&
- (self->provider.state != IRLAN_IDLE))
- irlan_client_wakeup(self, self->saddr, self->daddr);
- break;
- case IRLAN_LMP_DISCONNECT:
- case IRLAN_LAP_DISCONNECT:
- irlan_next_client_state(self, IRLAN_IDLE);
- break;
- case IRLAN_WATCHDOG_TIMEOUT:
- pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
- break;
- default:
- pr_debug("%s(), Unknown event %d\n", __func__ , event);
- break;
- }
- if (skb)
- dev_kfree_skb(skb);
-
- return 0;
-}
-
-/*
- * Function irlan_client_state_conn (event, skb, info)
- *
- * CONN, We have connected to a provider but has not issued any
- * commands yet.
- *
- */
-static int irlan_client_state_conn(struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb)
-{
- IRDA_ASSERT(self != NULL, return -1;);
-
- switch (event) {
- case IRLAN_CONNECT_COMPLETE:
- /* Send getinfo cmd */
- irlan_get_provider_info(self);
- irlan_next_client_state(self, IRLAN_INFO);
- break;
- case IRLAN_LMP_DISCONNECT:
- case IRLAN_LAP_DISCONNECT:
- irlan_next_client_state(self, IRLAN_IDLE);
- break;
- case IRLAN_WATCHDOG_TIMEOUT:
- pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
- break;
- default:
- pr_debug("%s(), Unknown event %d\n", __func__ , event);
- break;
- }
- if (skb)
- dev_kfree_skb(skb);
-
- return 0;
-}
-
-/*
- * Function irlan_client_state_info (self, event, skb, info)
- *
- * INFO, We have issued a GetInfo command and is awaiting a reply.
- */
-static int irlan_client_state_info(struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb)
-{
- IRDA_ASSERT(self != NULL, return -1;);
-
- switch (event) {
- case IRLAN_DATA_INDICATION:
- IRDA_ASSERT(skb != NULL, return -1;);
-
- irlan_client_parse_response(self, skb);
-
- irlan_next_client_state(self, IRLAN_MEDIA);
-
- irlan_get_media_char(self);
- break;
-
- case IRLAN_LMP_DISCONNECT:
- case IRLAN_LAP_DISCONNECT:
- irlan_next_client_state(self, IRLAN_IDLE);
- break;
- case IRLAN_WATCHDOG_TIMEOUT:
- pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
- break;
- default:
- pr_debug("%s(), Unknown event %d\n", __func__ , event);
- break;
- }
- if (skb)
- dev_kfree_skb(skb);
-
- return 0;
-}
-
-/*
- * Function irlan_client_state_media (self, event, skb, info)
- *
- * MEDIA, The irlan_client has issued a GetMedia command and is awaiting a
- * reply.
- *
- */
-static int irlan_client_state_media(struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb)
-{
- IRDA_ASSERT(self != NULL, return -1;);
-
- switch(event) {
- case IRLAN_DATA_INDICATION:
- irlan_client_parse_response(self, skb);
- irlan_open_data_channel(self);
- irlan_next_client_state(self, IRLAN_OPEN);
- break;
- case IRLAN_LMP_DISCONNECT:
- case IRLAN_LAP_DISCONNECT:
- irlan_next_client_state(self, IRLAN_IDLE);
- break;
- case IRLAN_WATCHDOG_TIMEOUT:
- pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
- break;
- default:
- pr_debug("%s(), Unknown event %d\n", __func__ , event);
- break;
- }
- if (skb)
- dev_kfree_skb(skb);
-
- return 0;
-}
-
-/*
- * Function irlan_client_state_open (self, event, skb, info)
- *
- * OPEN, The irlan_client has issued a OpenData command and is awaiting a
- * reply
- *
- */
-static int irlan_client_state_open(struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb)
-{
- struct qos_info qos;
-
- IRDA_ASSERT(self != NULL, return -1;);
-
- switch(event) {
- case IRLAN_DATA_INDICATION:
- irlan_client_parse_response(self, skb);
-
- /*
- * Check if we have got the remote TSAP for data
- * communications
- */
- IRDA_ASSERT(self->dtsap_sel_data != 0, return -1;);
-
- /* Check which access type we are dealing with */
- switch (self->client.access_type) {
- case ACCESS_PEER:
- if (self->provider.state == IRLAN_OPEN) {
-
- irlan_next_client_state(self, IRLAN_ARB);
- irlan_do_client_event(self, IRLAN_CHECK_CON_ARB,
- NULL);
- } else {
-
- irlan_next_client_state(self, IRLAN_WAIT);
- }
- break;
- case ACCESS_DIRECT:
- case ACCESS_HOSTED:
- qos.link_disc_time.bits = 0x01; /* 3 secs */
-
- irttp_connect_request(self->tsap_data,
- self->dtsap_sel_data,
- self->saddr, self->daddr, &qos,
- IRLAN_MTU, NULL);
-
- irlan_next_client_state(self, IRLAN_DATA);
- break;
- default:
- pr_debug("%s(), unknown access type!\n", __func__);
- break;
- }
- break;
- case IRLAN_LMP_DISCONNECT:
- case IRLAN_LAP_DISCONNECT:
- irlan_next_client_state(self, IRLAN_IDLE);
- break;
- case IRLAN_WATCHDOG_TIMEOUT:
- pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
- break;
- default:
- pr_debug("%s(), Unknown event %d\n", __func__ , event);
- break;
- }
-
- if (skb)
- dev_kfree_skb(skb);
-
- return 0;
-}
-
-/*
- * Function irlan_client_state_wait (self, event, skb, info)
- *
- * WAIT, The irlan_client is waiting for the local provider to enter the
- * provider OPEN state.
- *
- */
-static int irlan_client_state_wait(struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb)
-{
- IRDA_ASSERT(self != NULL, return -1;);
-
- switch(event) {
- case IRLAN_PROVIDER_SIGNAL:
- irlan_next_client_state(self, IRLAN_ARB);
- irlan_do_client_event(self, IRLAN_CHECK_CON_ARB, NULL);
- break;
- case IRLAN_LMP_DISCONNECT:
- case IRLAN_LAP_DISCONNECT:
- irlan_next_client_state(self, IRLAN_IDLE);
- break;
- case IRLAN_WATCHDOG_TIMEOUT:
- pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
- break;
- default:
- pr_debug("%s(), Unknown event %d\n", __func__ , event);
- break;
- }
- if (skb)
- dev_kfree_skb(skb);
-
- return 0;
-}
-
-static int irlan_client_state_arb(struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb)
-{
- struct qos_info qos;
-
- IRDA_ASSERT(self != NULL, return -1;);
-
- switch(event) {
- case IRLAN_CHECK_CON_ARB:
- if (self->client.recv_arb_val == self->provider.send_arb_val) {
- irlan_next_client_state(self, IRLAN_CLOSE);
- irlan_close_data_channel(self);
- } else if (self->client.recv_arb_val <
- self->provider.send_arb_val)
- {
- qos.link_disc_time.bits = 0x01; /* 3 secs */
-
- irlan_next_client_state(self, IRLAN_DATA);
- irttp_connect_request(self->tsap_data,
- self->dtsap_sel_data,
- self->saddr, self->daddr, &qos,
- IRLAN_MTU, NULL);
- } else if (self->client.recv_arb_val >
- self->provider.send_arb_val)
- {
- pr_debug("%s(), lost the battle :-(\n", __func__);
- }
- break;
- case IRLAN_DATA_CONNECT_INDICATION:
- irlan_next_client_state(self, IRLAN_DATA);
- break;
- case IRLAN_LMP_DISCONNECT:
- case IRLAN_LAP_DISCONNECT:
- irlan_next_client_state(self, IRLAN_IDLE);
- break;
- case IRLAN_WATCHDOG_TIMEOUT:
- pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
- break;
- default:
- pr_debug("%s(), Unknown event %d\n", __func__ , event);
- break;
- }
- if (skb)
- dev_kfree_skb(skb);
-
- return 0;
-}
-
-/*
- * Function irlan_client_state_data (self, event, skb, info)
- *
- * DATA, The data channel is connected, allowing data transfers between
- * the local and remote machines.
- *
- */
-static int irlan_client_state_data(struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb)
-{
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
-
- switch(event) {
- case IRLAN_DATA_INDICATION:
- irlan_client_parse_response(self, skb);
- break;
- case IRLAN_LMP_DISCONNECT: /* FALLTHROUGH */
- case IRLAN_LAP_DISCONNECT:
- irlan_next_client_state(self, IRLAN_IDLE);
- break;
- default:
- pr_debug("%s(), Unknown event %d\n", __func__ , event);
- break;
- }
- if (skb)
- dev_kfree_skb(skb);
-
- return 0;
-}
-
-/*
- * Function irlan_client_state_close (self, event, skb, info)
- *
- *
- *
- */
-static int irlan_client_state_close(struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb)
-{
- if (skb)
- dev_kfree_skb(skb);
-
- return 0;
-}
-
-/*
- * Function irlan_client_state_sync (self, event, skb, info)
- *
- *
- *
- */
-static int irlan_client_state_sync(struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb)
-{
- if (skb)
- dev_kfree_skb(skb);
-
- return 0;
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/drivers/staging/irda/net/irlan/irlan_common.c b/drivers/staging/irda/net/irlan/irlan_common.c
deleted file mode 100644
index fdcd7147007d..000000000000
--- a/drivers/staging/irda/net/irlan/irlan_common.c
+++ /dev/null
@@ -1,1176 +0,0 @@
-/*********************************************************************
- *
- * Filename: irlan_common.c
- * Version: 0.9
- * Description: IrDA LAN Access Protocol Implementation
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sun Aug 31 20:14:37 1997
- * Modified at: Sun Dec 26 21:53:10 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1997, 1999 Dag Brattli <dagb@cs.uit.no>,
- * All Rights Reserved.
- *
- * 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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/module.h>
-
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/gfp.h>
-#include <linux/init.h>
-#include <linux/errno.h>
-#include <linux/proc_fs.h>
-#include <linux/sched.h>
-#include <linux/seq_file.h>
-#include <linux/random.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/rtnetlink.h>
-#include <linux/moduleparam.h>
-#include <linux/bitops.h>
-
-#include <asm/byteorder.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irttp.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/iriap.h>
-#include <net/irda/timer.h>
-
-#include <net/irda/irlan_common.h>
-#include <net/irda/irlan_client.h>
-#include <net/irda/irlan_provider.h>
-#include <net/irda/irlan_eth.h>
-#include <net/irda/irlan_filter.h>
-
-
-/* extern char sysctl_devname[]; */
-
-/*
- * Master structure
- */
-static LIST_HEAD(irlans);
-
-static void *ckey;
-static void *skey;
-
-/* Module parameters */
-static bool eth; /* Use "eth" or "irlan" name for devices */
-static int access = ACCESS_PEER; /* PEER, DIRECT or HOSTED */
-
-#ifdef CONFIG_PROC_FS
-static const char *const irlan_access[] = {
- "UNKNOWN",
- "DIRECT",
- "PEER",
- "HOSTED"
-};
-
-static const char *const irlan_media[] = {
- "UNKNOWN",
- "802.3",
- "802.5"
-};
-
-extern struct proc_dir_entry *proc_irda;
-
-static int irlan_seq_open(struct inode *inode, struct file *file);
-
-static const struct file_operations irlan_fops = {
- .owner = THIS_MODULE,
- .open = irlan_seq_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = seq_release,
-};
-
-extern struct proc_dir_entry *proc_irda;
-#endif /* CONFIG_PROC_FS */
-
-static struct irlan_cb __init *irlan_open(__u32 saddr, __u32 daddr);
-static void __irlan_close(struct irlan_cb *self);
-static int __irlan_insert_param(struct sk_buff *skb, char *param, int type,
- __u8 value_byte, __u16 value_short,
- __u8 *value_array, __u16 value_len);
-static void irlan_open_unicast_addr(struct irlan_cb *self);
-static void irlan_get_unicast_addr(struct irlan_cb *self);
-void irlan_close_tsaps(struct irlan_cb *self);
-
-/*
- * Function irlan_init (void)
- *
- * Initialize IrLAN layer
- *
- */
-static int __init irlan_init(void)
-{
- struct irlan_cb *new;
- __u16 hints;
-
-#ifdef CONFIG_PROC_FS
- { struct proc_dir_entry *proc;
- proc = proc_create("irlan", 0, proc_irda, &irlan_fops);
- if (!proc) {
- printk(KERN_ERR "irlan_init: can't create /proc entry!\n");
- return -ENODEV;
- }
- }
-#endif /* CONFIG_PROC_FS */
-
- hints = irlmp_service_to_hint(S_LAN);
-
- /* Register with IrLMP as a client */
- ckey = irlmp_register_client(hints, &irlan_client_discovery_indication,
- NULL, NULL);
- if (!ckey)
- goto err_ckey;
-
- /* Register with IrLMP as a service */
- skey = irlmp_register_service(hints);
- if (!skey)
- goto err_skey;
-
- /* Start the master IrLAN instance (the only one for now) */
- new = irlan_open(DEV_ADDR_ANY, DEV_ADDR_ANY);
- if (!new)
- goto err_open;
-
- /* The master will only open its (listen) control TSAP */
- irlan_provider_open_ctrl_tsap(new);
-
- /* Do some fast discovery! */
- irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS);
-
- return 0;
-
-err_open:
- irlmp_unregister_service(skey);
-err_skey:
- irlmp_unregister_client(ckey);
-err_ckey:
-#ifdef CONFIG_PROC_FS
- remove_proc_entry("irlan", proc_irda);
-#endif /* CONFIG_PROC_FS */
-
- return -ENOMEM;
-}
-
-static void __exit irlan_cleanup(void)
-{
- struct irlan_cb *self, *next;
-
- irlmp_unregister_client(ckey);
- irlmp_unregister_service(skey);
-
-#ifdef CONFIG_PROC_FS
- remove_proc_entry("irlan", proc_irda);
-#endif /* CONFIG_PROC_FS */
-
- /* Cleanup any leftover network devices */
- rtnl_lock();
- list_for_each_entry_safe(self, next, &irlans, dev_list) {
- __irlan_close(self);
- }
- rtnl_unlock();
-}
-
-/*
- * Function irlan_open (void)
- *
- * Open new instance of a client/provider, we should only register the
- * network device if this instance is ment for a particular client/provider
- */
-static struct irlan_cb __init *irlan_open(__u32 saddr, __u32 daddr)
-{
- struct net_device *dev;
- struct irlan_cb *self;
-
- /* Create network device with irlan */
- dev = alloc_irlandev(eth ? "eth%d" : "irlan%d");
- if (!dev)
- return NULL;
-
- self = netdev_priv(dev);
- self->dev = dev;
-
- /*
- * Initialize local device structure
- */
- self->magic = IRLAN_MAGIC;
- self->saddr = saddr;
- self->daddr = daddr;
-
- /* Provider access can only be PEER, DIRECT, or HOSTED */
- self->provider.access_type = access;
- if (access == ACCESS_DIRECT) {
- /*
- * Since we are emulating an IrLAN sever we will have to
- * give ourself an ethernet address!
- */
- dev->dev_addr[0] = 0x40;
- dev->dev_addr[1] = 0x00;
- dev->dev_addr[2] = 0x00;
- dev->dev_addr[3] = 0x00;
- get_random_bytes(dev->dev_addr+4, 1);
- get_random_bytes(dev->dev_addr+5, 1);
- }
-
- self->media = MEDIA_802_3;
- self->disconnect_reason = LM_USER_REQUEST;
- timer_setup(&self->watchdog_timer, NULL, 0);
- timer_setup(&self->client.kick_timer, NULL, 0);
- init_waitqueue_head(&self->open_wait);
-
- skb_queue_head_init(&self->client.txq);
-
- irlan_next_client_state(self, IRLAN_IDLE);
- irlan_next_provider_state(self, IRLAN_IDLE);
-
- if (register_netdev(dev)) {
- pr_debug("%s(), register_netdev() failed!\n",
- __func__);
- self = NULL;
- free_netdev(dev);
- } else {
- rtnl_lock();
- list_add_rcu(&self->dev_list, &irlans);
- rtnl_unlock();
- }
-
- return self;
-}
-/*
- * Function __irlan_close (self)
- *
- * This function closes and deallocates the IrLAN client instances. Be
- * aware that other functions which calls client_close() must
- * remove self from irlans list first.
- */
-static void __irlan_close(struct irlan_cb *self)
-{
- ASSERT_RTNL();
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- del_timer_sync(&self->watchdog_timer);
- del_timer_sync(&self->client.kick_timer);
-
- /* Close all open connections and remove TSAPs */
- irlan_close_tsaps(self);
-
- if (self->client.iriap)
- iriap_close(self->client.iriap);
-
- /* Remove frames queued on the control channel */
- skb_queue_purge(&self->client.txq);
-
- /* Unregister and free self via destructor */
- unregister_netdevice(self->dev);
-}
-
-/* Find any instance of irlan, used for client discovery wakeup */
-struct irlan_cb *irlan_get_any(void)
-{
- struct irlan_cb *self;
-
- list_for_each_entry_rcu(self, &irlans, dev_list) {
- return self;
- }
- return NULL;
-}
-
-/*
- * Function irlan_connect_indication (instance, sap, qos, max_sdu_size, skb)
- *
- * Here we receive the connect indication for the data channel
- *
- */
-static void irlan_connect_indication(void *instance, void *sap,
- struct qos_info *qos,
- __u32 max_sdu_size,
- __u8 max_header_size,
- struct sk_buff *skb)
-{
- struct irlan_cb *self;
- struct tsap_cb *tsap;
-
- self = instance;
- tsap = sap;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
- IRDA_ASSERT(tsap == self->tsap_data,return;);
-
- self->max_sdu_size = max_sdu_size;
- self->max_header_size = max_header_size;
-
- pr_debug("%s: We are now connected!\n", __func__);
-
- del_timer(&self->watchdog_timer);
-
- /* If you want to pass the skb to *both* state machines, you will
- * need to skb_clone() it, so that you don't free it twice.
- * As the state machines don't need it, git rid of it here...
- * Jean II */
- if (skb)
- dev_kfree_skb(skb);
-
- irlan_do_provider_event(self, IRLAN_DATA_CONNECT_INDICATION, NULL);
- irlan_do_client_event(self, IRLAN_DATA_CONNECT_INDICATION, NULL);
-
- if (self->provider.access_type == ACCESS_PEER) {
- /*
- * Data channel is open, so we are now allowed to
- * configure the remote filter
- */
- irlan_get_unicast_addr(self);
- irlan_open_unicast_addr(self);
- }
- /* Ready to transfer Ethernet frames (at last) */
- netif_start_queue(self->dev); /* Clear reason */
-}
-
-static void irlan_connect_confirm(void *instance, void *sap,
- struct qos_info *qos,
- __u32 max_sdu_size,
- __u8 max_header_size,
- struct sk_buff *skb)
-{
- struct irlan_cb *self;
-
- self = instance;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- self->max_sdu_size = max_sdu_size;
- self->max_header_size = max_header_size;
-
- /* TODO: we could set the MTU depending on the max_sdu_size */
-
- pr_debug("%s: We are now connected!\n", __func__);
- del_timer(&self->watchdog_timer);
-
- /*
- * Data channel is open, so we are now allowed to configure the remote
- * filter
- */
- irlan_get_unicast_addr(self);
- irlan_open_unicast_addr(self);
-
- /* Open broadcast and multicast filter by default */
- irlan_set_broadcast_filter(self, TRUE);
- irlan_set_multicast_filter(self, TRUE);
-
- /* Ready to transfer Ethernet frames */
- netif_start_queue(self->dev);
- self->disconnect_reason = 0; /* Clear reason */
- wake_up_interruptible(&self->open_wait);
-}
-
-/*
- * Function irlan_client_disconnect_indication (handle)
- *
- * Callback function for the IrTTP layer. Indicates a disconnection of
- * the specified connection (handle)
- */
-static void irlan_disconnect_indication(void *instance,
- void *sap, LM_REASON reason,
- struct sk_buff *userdata)
-{
- struct irlan_cb *self;
- struct tsap_cb *tsap;
-
- pr_debug("%s(), reason=%d\n", __func__ , reason);
-
- self = instance;
- tsap = sap;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
- IRDA_ASSERT(tsap != NULL, return;);
- IRDA_ASSERT(tsap->magic == TTP_TSAP_MAGIC, return;);
-
- IRDA_ASSERT(tsap == self->tsap_data, return;);
-
- pr_debug("IrLAN, data channel disconnected by peer!\n");
-
- /* Save reason so we know if we should try to reconnect or not */
- self->disconnect_reason = reason;
-
- switch (reason) {
- case LM_USER_REQUEST: /* User request */
- pr_debug("%s(), User requested\n", __func__);
- break;
- case LM_LAP_DISCONNECT: /* Unexpected IrLAP disconnect */
- pr_debug("%s(), Unexpected IrLAP disconnect\n", __func__);
- break;
- case LM_CONNECT_FAILURE: /* Failed to establish IrLAP connection */
- pr_debug("%s(), IrLAP connect failed\n", __func__);
- break;
- case LM_LAP_RESET: /* IrLAP reset */
- pr_debug("%s(), IrLAP reset\n", __func__);
- break;
- case LM_INIT_DISCONNECT:
- pr_debug("%s(), IrLMP connect failed\n", __func__);
- break;
- default:
- net_err_ratelimited("%s(), Unknown disconnect reason\n",
- __func__);
- break;
- }
-
- /* If you want to pass the skb to *both* state machines, you will
- * need to skb_clone() it, so that you don't free it twice.
- * As the state machines don't need it, git rid of it here...
- * Jean II */
- if (userdata)
- dev_kfree_skb(userdata);
-
- irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL);
- irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL);
-
- wake_up_interruptible(&self->open_wait);
-}
-
-void irlan_open_data_tsap(struct irlan_cb *self)
-{
- struct tsap_cb *tsap;
- notify_t notify;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- /* Check if already open */
- if (self->tsap_data)
- return;
-
- irda_notify_init(&notify);
-
- notify.data_indication = irlan_eth_receive;
- notify.udata_indication = irlan_eth_receive;
- notify.connect_indication = irlan_connect_indication;
- notify.connect_confirm = irlan_connect_confirm;
- notify.flow_indication = irlan_eth_flow_indication;
- notify.disconnect_indication = irlan_disconnect_indication;
- notify.instance = self;
- strlcpy(notify.name, "IrLAN data", sizeof(notify.name));
-
- tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT, &notify);
- if (!tsap) {
- pr_debug("%s(), Got no tsap!\n", __func__);
- return;
- }
- self->tsap_data = tsap;
-
- /*
- * This is the data TSAP selector which we will pass to the client
- * when the client ask for it.
- */
- self->stsap_sel_data = self->tsap_data->stsap_sel;
-}
-
-void irlan_close_tsaps(struct irlan_cb *self)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- /* Disconnect and close all open TSAP connections */
- if (self->tsap_data) {
- irttp_disconnect_request(self->tsap_data, NULL, P_NORMAL);
- irttp_close_tsap(self->tsap_data);
- self->tsap_data = NULL;
- }
- if (self->client.tsap_ctrl) {
- irttp_disconnect_request(self->client.tsap_ctrl, NULL,
- P_NORMAL);
- irttp_close_tsap(self->client.tsap_ctrl);
- self->client.tsap_ctrl = NULL;
- }
- if (self->provider.tsap_ctrl) {
- irttp_disconnect_request(self->provider.tsap_ctrl, NULL,
- P_NORMAL);
- irttp_close_tsap(self->provider.tsap_ctrl);
- self->provider.tsap_ctrl = NULL;
- }
- self->disconnect_reason = LM_USER_REQUEST;
-}
-
-/*
- * Function irlan_ias_register (self, tsap_sel)
- *
- * Register with LM-IAS
- *
- */
-void irlan_ias_register(struct irlan_cb *self, __u8 tsap_sel)
-{
- struct ias_object *obj;
- struct ias_value *new_value;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- /*
- * Check if object has already been registered by a previous provider.
- * If that is the case, we just change the value of the attribute
- */
- if (!irias_find_object("IrLAN")) {
- obj = irias_new_object("IrLAN", IAS_IRLAN_ID);
- irias_add_integer_attrib(obj, "IrDA:TinyTP:LsapSel", tsap_sel,
- IAS_KERNEL_ATTR);
- irias_insert_object(obj);
- } else {
- new_value = irias_new_integer_value(tsap_sel);
- irias_object_change_attribute("IrLAN", "IrDA:TinyTP:LsapSel",
- new_value);
- }
-
- /* Register PnP object only if not registered before */
- if (!irias_find_object("PnP")) {
- obj = irias_new_object("PnP", IAS_PNP_ID);
-#if 0
- irias_add_string_attrib(obj, "Name", sysctl_devname,
- IAS_KERNEL_ATTR);
-#else
- irias_add_string_attrib(obj, "Name", "Linux", IAS_KERNEL_ATTR);
-#endif
- irias_add_string_attrib(obj, "DeviceID", "HWP19F0",
- IAS_KERNEL_ATTR);
- irias_add_integer_attrib(obj, "CompCnt", 1, IAS_KERNEL_ATTR);
- if (self->provider.access_type == ACCESS_PEER)
- irias_add_string_attrib(obj, "Comp#01", "PNP8389",
- IAS_KERNEL_ATTR);
- else
- irias_add_string_attrib(obj, "Comp#01", "PNP8294",
- IAS_KERNEL_ATTR);
-
- irias_add_string_attrib(obj, "Manufacturer",
- "Linux-IrDA Project", IAS_KERNEL_ATTR);
- irias_insert_object(obj);
- }
-}
-
-/*
- * Function irlan_run_ctrl_tx_queue (self)
- *
- * Try to send the next command in the control transmit queue
- *
- */
-int irlan_run_ctrl_tx_queue(struct irlan_cb *self)
-{
- struct sk_buff *skb;
-
- if (irda_lock(&self->client.tx_busy) == FALSE)
- return -EBUSY;
-
- skb = skb_dequeue(&self->client.txq);
- if (!skb) {
- self->client.tx_busy = FALSE;
- return 0;
- }
-
- /* Check that it's really possible to send commands */
- if ((self->client.tsap_ctrl == NULL) ||
- (self->client.state == IRLAN_IDLE))
- {
- self->client.tx_busy = FALSE;
- dev_kfree_skb(skb);
- return -1;
- }
- pr_debug("%s(), sending ...\n", __func__);
-
- return irttp_data_request(self->client.tsap_ctrl, skb);
-}
-
-/*
- * Function irlan_ctrl_data_request (self, skb)
- *
- * This function makes sure that commands on the control channel is being
- * sent in a command/response fashion
- */
-static void irlan_ctrl_data_request(struct irlan_cb *self, struct sk_buff *skb)
-{
- /* Queue command */
- skb_queue_tail(&self->client.txq, skb);
-
- /* Try to send command */
- irlan_run_ctrl_tx_queue(self);
-}
-
-/*
- * Function irlan_get_provider_info (self)
- *
- * Send Get Provider Information command to peer IrLAN layer
- *
- */
-void irlan_get_provider_info(struct irlan_cb *self)
-{
- struct sk_buff *skb;
- __u8 *frame;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER,
- GFP_ATOMIC);
- if (!skb)
- return;
-
- /* Reserve space for TTP, LMP, and LAP header */
- skb_reserve(skb, self->client.max_header_size);
- skb_put(skb, 2);
-
- frame = skb->data;
-
- frame[0] = CMD_GET_PROVIDER_INFO;
- frame[1] = 0x00; /* Zero parameters */
-
- irlan_ctrl_data_request(self, skb);
-}
-
-/*
- * Function irlan_open_data_channel (self)
- *
- * Send an Open Data Command to provider
- *
- */
-void irlan_open_data_channel(struct irlan_cb *self)
-{
- struct sk_buff *skb;
- __u8 *frame;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER +
- IRLAN_STRING_PARAMETER_LEN("MEDIA", "802.3") +
- IRLAN_STRING_PARAMETER_LEN("ACCESS_TYPE", "DIRECT"),
- GFP_ATOMIC);
- if (!skb)
- return;
-
- skb_reserve(skb, self->client.max_header_size);
- skb_put(skb, 2);
-
- frame = skb->data;
-
- /* Build frame */
- frame[0] = CMD_OPEN_DATA_CHANNEL;
- frame[1] = 0x02; /* Two parameters */
-
- irlan_insert_string_param(skb, "MEDIA", "802.3");
- irlan_insert_string_param(skb, "ACCESS_TYPE", "DIRECT");
- /* irlan_insert_string_param(skb, "MODE", "UNRELIABLE"); */
-
-/* self->use_udata = TRUE; */
-
- irlan_ctrl_data_request(self, skb);
-}
-
-void irlan_close_data_channel(struct irlan_cb *self)
-{
- struct sk_buff *skb;
- __u8 *frame;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- /* Check if the TSAP is still there */
- if (self->client.tsap_ctrl == NULL)
- return;
-
- skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER +
- IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN"),
- GFP_ATOMIC);
- if (!skb)
- return;
-
- skb_reserve(skb, self->client.max_header_size);
- skb_put(skb, 2);
-
- frame = skb->data;
-
- /* Build frame */
- frame[0] = CMD_CLOSE_DATA_CHAN;
- frame[1] = 0x01; /* One parameter */
-
- irlan_insert_byte_param(skb, "DATA_CHAN", self->dtsap_sel_data);
-
- irlan_ctrl_data_request(self, skb);
-}
-
-/*
- * Function irlan_open_unicast_addr (self)
- *
- * Make IrLAN provider accept ethernet frames addressed to the unicast
- * address.
- *
- */
-static void irlan_open_unicast_addr(struct irlan_cb *self)
-{
- struct sk_buff *skb;
- __u8 *frame;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER +
- IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN") +
- IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "DIRECTED") +
- IRLAN_STRING_PARAMETER_LEN("FILTER_MODE", "FILTER"),
- GFP_ATOMIC);
- if (!skb)
- return;
-
- /* Reserve space for TTP, LMP, and LAP header */
- skb_reserve(skb, self->max_header_size);
- skb_put(skb, 2);
-
- frame = skb->data;
-
- frame[0] = CMD_FILTER_OPERATION;
- frame[1] = 0x03; /* Three parameters */
- irlan_insert_byte_param(skb, "DATA_CHAN" , self->dtsap_sel_data);
- irlan_insert_string_param(skb, "FILTER_TYPE", "DIRECTED");
- irlan_insert_string_param(skb, "FILTER_MODE", "FILTER");
-
- irlan_ctrl_data_request(self, skb);
-}
-
-/*
- * Function irlan_set_broadcast_filter (self, status)
- *
- * Make IrLAN provider accept ethernet frames addressed to the broadcast
- * address. Be careful with the use of this one, since there may be a lot
- * of broadcast traffic out there. We can still function without this
- * one but then _we_ have to initiate all communication with other
- * hosts, since ARP request for this host will not be answered.
- */
-void irlan_set_broadcast_filter(struct irlan_cb *self, int status)
-{
- struct sk_buff *skb;
- __u8 *frame;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER +
- IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN") +
- IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "BROADCAST") +
- /* We may waste one byte here...*/
- IRLAN_STRING_PARAMETER_LEN("FILTER_MODE", "FILTER"),
- GFP_ATOMIC);
- if (!skb)
- return;
-
- /* Reserve space for TTP, LMP, and LAP header */
- skb_reserve(skb, self->client.max_header_size);
- skb_put(skb, 2);
-
- frame = skb->data;
-
- frame[0] = CMD_FILTER_OPERATION;
- frame[1] = 0x03; /* Three parameters */
- irlan_insert_byte_param(skb, "DATA_CHAN", self->dtsap_sel_data);
- irlan_insert_string_param(skb, "FILTER_TYPE", "BROADCAST");
- if (status)
- irlan_insert_string_param(skb, "FILTER_MODE", "FILTER");
- else
- irlan_insert_string_param(skb, "FILTER_MODE", "NONE");
-
- irlan_ctrl_data_request(self, skb);
-}
-
-/*
- * Function irlan_set_multicast_filter (self, status)
- *
- * Make IrLAN provider accept ethernet frames addressed to the multicast
- * address.
- *
- */
-void irlan_set_multicast_filter(struct irlan_cb *self, int status)
-{
- struct sk_buff *skb;
- __u8 *frame;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER +
- IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN") +
- IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "MULTICAST") +
- /* We may waste one byte here...*/
- IRLAN_STRING_PARAMETER_LEN("FILTER_MODE", "NONE"),
- GFP_ATOMIC);
- if (!skb)
- return;
-
- /* Reserve space for TTP, LMP, and LAP header */
- skb_reserve(skb, self->client.max_header_size);
- skb_put(skb, 2);
-
- frame = skb->data;
-
- frame[0] = CMD_FILTER_OPERATION;
- frame[1] = 0x03; /* Three parameters */
- irlan_insert_byte_param(skb, "DATA_CHAN", self->dtsap_sel_data);
- irlan_insert_string_param(skb, "FILTER_TYPE", "MULTICAST");
- if (status)
- irlan_insert_string_param(skb, "FILTER_MODE", "ALL");
- else
- irlan_insert_string_param(skb, "FILTER_MODE", "NONE");
-
- irlan_ctrl_data_request(self, skb);
-}
-
-/*
- * Function irlan_get_unicast_addr (self)
- *
- * Retrieves the unicast address from the IrLAN provider. This address
- * will be inserted into the devices structure, so the ethernet layer
- * can construct its packets.
- *
- */
-static void irlan_get_unicast_addr(struct irlan_cb *self)
-{
- struct sk_buff *skb;
- __u8 *frame;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER +
- IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN") +
- IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "DIRECTED") +
- IRLAN_STRING_PARAMETER_LEN("FILTER_OPERATION",
- "DYNAMIC"),
- GFP_ATOMIC);
- if (!skb)
- return;
-
- /* Reserve space for TTP, LMP, and LAP header */
- skb_reserve(skb, self->client.max_header_size);
- skb_put(skb, 2);
-
- frame = skb->data;
-
- frame[0] = CMD_FILTER_OPERATION;
- frame[1] = 0x03; /* Three parameters */
- irlan_insert_byte_param(skb, "DATA_CHAN", self->dtsap_sel_data);
- irlan_insert_string_param(skb, "FILTER_TYPE", "DIRECTED");
- irlan_insert_string_param(skb, "FILTER_OPERATION", "DYNAMIC");
-
- irlan_ctrl_data_request(self, skb);
-}
-
-/*
- * Function irlan_get_media_char (self)
- *
- *
- *
- */
-void irlan_get_media_char(struct irlan_cb *self)
-{
- struct sk_buff *skb;
- __u8 *frame;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER +
- IRLAN_STRING_PARAMETER_LEN("MEDIA", "802.3"),
- GFP_ATOMIC);
-
- if (!skb)
- return;
-
- /* Reserve space for TTP, LMP, and LAP header */
- skb_reserve(skb, self->client.max_header_size);
- skb_put(skb, 2);
-
- frame = skb->data;
-
- /* Build frame */
- frame[0] = CMD_GET_MEDIA_CHAR;
- frame[1] = 0x01; /* One parameter */
-
- irlan_insert_string_param(skb, "MEDIA", "802.3");
- irlan_ctrl_data_request(self, skb);
-}
-
-/*
- * Function insert_byte_param (skb, param, value)
- *
- * Insert byte parameter into frame
- *
- */
-int irlan_insert_byte_param(struct sk_buff *skb, char *param, __u8 value)
-{
- return __irlan_insert_param(skb, param, IRLAN_BYTE, value, 0, NULL, 0);
-}
-
-int irlan_insert_short_param(struct sk_buff *skb, char *param, __u16 value)
-{
- return __irlan_insert_param(skb, param, IRLAN_SHORT, 0, value, NULL, 0);
-}
-
-/*
- * Function insert_string (skb, param, value)
- *
- * Insert string parameter into frame
- *
- */
-int irlan_insert_string_param(struct sk_buff *skb, char *param, char *string)
-{
- int string_len = strlen(string);
-
- return __irlan_insert_param(skb, param, IRLAN_ARRAY, 0, 0, string,
- string_len);
-}
-
-/*
- * Function insert_array_param(skb, param, value, len_value)
- *
- * Insert array parameter into frame
- *
- */
-int irlan_insert_array_param(struct sk_buff *skb, char *name, __u8 *array,
- __u16 array_len)
-{
- return __irlan_insert_param(skb, name, IRLAN_ARRAY, 0, 0, array,
- array_len);
-}
-
-/*
- * Function insert_param (skb, param, value, byte)
- *
- * Insert parameter at end of buffer, structure of a parameter is:
- *
- * -----------------------------------------------------------------------
- * | Name Length[1] | Param Name[1..255] | Val Length[2] | Value[0..1016]|
- * -----------------------------------------------------------------------
- */
-static int __irlan_insert_param(struct sk_buff *skb, char *param, int type,
- __u8 value_byte, __u16 value_short,
- __u8 *value_array, __u16 value_len)
-{
- __u8 *frame;
- __u8 param_len;
- __le16 tmp_le; /* Temporary value in little endian format */
- int n=0;
-
- if (skb == NULL) {
- pr_debug("%s(), Got NULL skb\n", __func__);
- return 0;
- }
-
- param_len = strlen(param);
- switch (type) {
- case IRLAN_BYTE:
- value_len = 1;
- break;
- case IRLAN_SHORT:
- value_len = 2;
- break;
- case IRLAN_ARRAY:
- IRDA_ASSERT(value_array != NULL, return 0;);
- IRDA_ASSERT(value_len > 0, return 0;);
- break;
- default:
- pr_debug("%s(), Unknown parameter type!\n", __func__);
- return 0;
- }
-
- /* Insert at end of sk-buffer */
- frame = skb_tail_pointer(skb);
-
- /* Make space for data */
- if (skb_tailroom(skb) < (param_len+value_len+3)) {
- pr_debug("%s(), No more space at end of skb\n", __func__);
- return 0;
- }
- skb_put(skb, param_len+value_len+3);
-
- /* Insert parameter length */
- frame[n++] = param_len;
-
- /* Insert parameter */
- memcpy(frame+n, param, param_len); n += param_len;
-
- /* Insert value length (2 byte little endian format, LSB first) */
- tmp_le = cpu_to_le16(value_len);
- memcpy(frame+n, &tmp_le, 2); n += 2; /* To avoid alignment problems */
-
- /* Insert value */
- switch (type) {
- case IRLAN_BYTE:
- frame[n++] = value_byte;
- break;
- case IRLAN_SHORT:
- tmp_le = cpu_to_le16(value_short);
- memcpy(frame+n, &tmp_le, 2); n += 2;
- break;
- case IRLAN_ARRAY:
- memcpy(frame+n, value_array, value_len); n+=value_len;
- break;
- default:
- break;
- }
- IRDA_ASSERT(n == (param_len+value_len+3), return 0;);
-
- return param_len+value_len+3;
-}
-
-/*
- * Function irlan_extract_param (buf, name, value, len)
- *
- * Extracts a single parameter name/value pair from buffer and updates
- * the buffer pointer to point to the next name/value pair.
- */
-int irlan_extract_param(__u8 *buf, char *name, char *value, __u16 *len)
-{
- __u8 name_len;
- __u16 val_len;
- int n=0;
-
- /* get length of parameter name (1 byte) */
- name_len = buf[n++];
-
- if (name_len > 254) {
- pr_debug("%s(), name_len > 254\n", __func__);
- return -RSP_INVALID_COMMAND_FORMAT;
- }
-
- /* get parameter name */
- memcpy(name, buf+n, name_len);
- name[name_len] = '\0';
- n+=name_len;
-
- /*
- * Get length of parameter value (2 bytes in little endian
- * format)
- */
- memcpy(&val_len, buf+n, 2); /* To avoid alignment problems */
- le16_to_cpus(&val_len); n+=2;
-
- if (val_len >= 1016) {
- pr_debug("%s(), parameter length to long\n", __func__);
- return -RSP_INVALID_COMMAND_FORMAT;
- }
- *len = val_len;
-
- /* get parameter value */
- memcpy(value, buf+n, val_len);
- value[val_len] = '\0';
- n+=val_len;
-
- pr_debug("Parameter: %s ", name);
- pr_debug("Value: %s\n", value);
-
- return n;
-}
-
-#ifdef CONFIG_PROC_FS
-
-/*
- * Start of reading /proc entries.
- * Return entry at pos,
- * or start_token to indicate print header line
- * or NULL if end of file
- */
-static void *irlan_seq_start(struct seq_file *seq, loff_t *pos)
-{
- rcu_read_lock();
- return seq_list_start_head(&irlans, *pos);
-}
-
-/* Return entry after v, and increment pos */
-static void *irlan_seq_next(struct seq_file *seq, void *v, loff_t *pos)
-{
- return seq_list_next(v, &irlans, pos);
-}
-
-/* End of reading /proc file */
-static void irlan_seq_stop(struct seq_file *seq, void *v)
-{
- rcu_read_unlock();
-}
-
-
-/*
- * Show one entry in /proc file.
- */
-static int irlan_seq_show(struct seq_file *seq, void *v)
-{
- if (v == &irlans)
- seq_puts(seq, "IrLAN instances:\n");
- else {
- struct irlan_cb *self = list_entry(v, struct irlan_cb, dev_list);
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
-
- seq_printf(seq,"ifname: %s,\n",
- self->dev->name);
- seq_printf(seq,"client state: %s, ",
- irlan_state[ self->client.state]);
- seq_printf(seq,"provider state: %s,\n",
- irlan_state[ self->provider.state]);
- seq_printf(seq,"saddr: %#08x, ",
- self->saddr);
- seq_printf(seq,"daddr: %#08x\n",
- self->daddr);
- seq_printf(seq,"version: %d.%d,\n",
- self->version[1], self->version[0]);
- seq_printf(seq,"access type: %s\n",
- irlan_access[self->client.access_type]);
- seq_printf(seq,"media: %s\n",
- irlan_media[self->media]);
-
- seq_printf(seq,"local filter:\n");
- seq_printf(seq,"remote filter: ");
- irlan_print_filter(seq, self->client.filter_type);
- seq_printf(seq,"tx busy: %s\n",
- netif_queue_stopped(self->dev) ? "TRUE" : "FALSE");
-
- seq_putc(seq,'\n');
- }
- return 0;
-}
-
-static const struct seq_operations irlan_seq_ops = {
- .start = irlan_seq_start,
- .next = irlan_seq_next,
- .stop = irlan_seq_stop,
- .show = irlan_seq_show,
-};
-
-static int irlan_seq_open(struct inode *inode, struct file *file)
-{
- return seq_open(file, &irlan_seq_ops);
-}
-#endif
-
-MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
-MODULE_DESCRIPTION("The Linux IrDA LAN protocol");
-MODULE_LICENSE("GPL");
-
-module_param(eth, bool, 0);
-MODULE_PARM_DESC(eth, "Name devices ethX (0) or irlanX (1)");
-module_param(access, int, 0);
-MODULE_PARM_DESC(access, "Access type DIRECT=1, PEER=2, HOSTED=3");
-
-module_init(irlan_init);
-module_exit(irlan_cleanup);
-
diff --git a/drivers/staging/irda/net/irlan/irlan_eth.c b/drivers/staging/irda/net/irlan/irlan_eth.c
deleted file mode 100644
index 3be852808a9d..000000000000
--- a/drivers/staging/irda/net/irlan/irlan_eth.c
+++ /dev/null
@@ -1,340 +0,0 @@
-/*********************************************************************
- *
- * Filename: irlan_eth.c
- * Version:
- * Description:
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Thu Oct 15 08:37:58 1998
- * Modified at: Tue Mar 21 09:06:41 2000
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- * Sources: skeleton.c by Donald Becker <becker@CESDIS.gsfc.nasa.gov>
- * slip.c by Laurence Culhane, <loz@holmes.demon.co.uk>
- * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
- *
- * Copyright (c) 1998-2000 Dag Brattli, All Rights Reserved.
- *
- * 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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/inetdevice.h>
-#include <linux/if_arp.h>
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <net/arp.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irmod.h>
-#include <net/irda/irlan_common.h>
-#include <net/irda/irlan_client.h>
-#include <net/irda/irlan_event.h>
-#include <net/irda/irlan_eth.h>
-
-static int irlan_eth_open(struct net_device *dev);
-static int irlan_eth_close(struct net_device *dev);
-static netdev_tx_t irlan_eth_xmit(struct sk_buff *skb,
- struct net_device *dev);
-static void irlan_eth_set_multicast_list(struct net_device *dev);
-
-static const struct net_device_ops irlan_eth_netdev_ops = {
- .ndo_open = irlan_eth_open,
- .ndo_stop = irlan_eth_close,
- .ndo_start_xmit = irlan_eth_xmit,
- .ndo_set_rx_mode = irlan_eth_set_multicast_list,
- .ndo_validate_addr = eth_validate_addr,
-};
-
-/*
- * Function irlan_eth_setup (dev)
- *
- * The network device initialization function.
- *
- */
-static void irlan_eth_setup(struct net_device *dev)
-{
- ether_setup(dev);
-
- dev->netdev_ops = &irlan_eth_netdev_ops;
- dev->needs_free_netdev = true;
- dev->min_mtu = 0;
- dev->max_mtu = ETH_MAX_MTU;
-
- /*
- * Lets do all queueing in IrTTP instead of this device driver.
- * Queueing here as well can introduce some strange latency
- * problems, which we will avoid by setting the queue size to 0.
- */
- /*
- * The bugs in IrTTP and IrLAN that created this latency issue
- * have now been fixed, and we can propagate flow control properly
- * to the network layer. However, this requires a minimal queue of
- * packets for the device.
- * Without flow control, the Tx Queue is 14 (ttp) + 0 (dev) = 14
- * With flow control, the Tx Queue is 7 (ttp) + 4 (dev) = 11
- * See irlan_eth_flow_indication()...
- * Note : this number was randomly selected and would need to
- * be adjusted.
- * Jean II */
- dev->tx_queue_len = 4;
-}
-
-/*
- * Function alloc_irlandev
- *
- * Allocate network device and control block
- *
- */
-struct net_device *alloc_irlandev(const char *name)
-{
- return alloc_netdev(sizeof(struct irlan_cb), name, NET_NAME_UNKNOWN,
- irlan_eth_setup);
-}
-
-/*
- * Function irlan_eth_open (dev)
- *
- * Network device has been opened by user
- *
- */
-static int irlan_eth_open(struct net_device *dev)
-{
- struct irlan_cb *self = netdev_priv(dev);
-
- /* Ready to play! */
- netif_stop_queue(dev); /* Wait until data link is ready */
-
- /* We are now open, so time to do some work */
- self->disconnect_reason = 0;
- irlan_client_wakeup(self, self->saddr, self->daddr);
-
- /* Make sure we have a hardware address before we return,
- so DHCP clients gets happy */
- return wait_event_interruptible(self->open_wait,
- !self->tsap_data->connected);
-}
-
-/*
- * Function irlan_eth_close (dev)
- *
- * Stop the ether network device, his function will usually be called by
- * ifconfig down. We should now disconnect the link, We start the
- * close timer, so that the instance will be removed if we are unable
- * to discover the remote device after the disconnect.
- */
-static int irlan_eth_close(struct net_device *dev)
-{
- struct irlan_cb *self = netdev_priv(dev);
-
- /* Stop device */
- netif_stop_queue(dev);
-
- irlan_close_data_channel(self);
- irlan_close_tsaps(self);
-
- irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL);
- irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL);
-
- /* Remove frames queued on the control channel */
- skb_queue_purge(&self->client.txq);
-
- self->client.tx_busy = 0;
-
- return 0;
-}
-
-/*
- * Function irlan_eth_tx (skb)
- *
- * Transmits ethernet frames over IrDA link.
- *
- */
-static netdev_tx_t irlan_eth_xmit(struct sk_buff *skb,
- struct net_device *dev)
-{
- struct irlan_cb *self = netdev_priv(dev);
- int ret;
- unsigned int len;
-
- /* skb headroom large enough to contain all IrDA-headers? */
- if ((skb_headroom(skb) < self->max_header_size) || (skb_shared(skb))) {
- struct sk_buff *new_skb =
- skb_realloc_headroom(skb, self->max_header_size);
-
- /* We have to free the original skb anyway */
- dev_kfree_skb(skb);
-
- /* Did the realloc succeed? */
- if (new_skb == NULL)
- return NETDEV_TX_OK;
-
- /* Use the new skb instead */
- skb = new_skb;
- }
-
- netif_trans_update(dev);
-
- len = skb->len;
- /* Now queue the packet in the transport layer */
- if (self->use_udata)
- ret = irttp_udata_request(self->tsap_data, skb);
- else
- ret = irttp_data_request(self->tsap_data, skb);
-
- if (ret < 0) {
- /*
- * IrTTPs tx queue is full, so we just have to
- * drop the frame! You might think that we should
- * just return -1 and don't deallocate the frame,
- * but that is dangerous since it's possible that
- * we have replaced the original skb with a new
- * one with larger headroom, and that would really
- * confuse do_dev_queue_xmit() in dev.c! I have
- * tried :-) DB
- */
- /* irttp_data_request already free the packet */
- dev->stats.tx_dropped++;
- } else {
- dev->stats.tx_packets++;
- dev->stats.tx_bytes += len;
- }
-
- return NETDEV_TX_OK;
-}
-
-/*
- * Function irlan_eth_receive (handle, skb)
- *
- * This function gets the data that is received on the data channel
- *
- */
-int irlan_eth_receive(void *instance, void *sap, struct sk_buff *skb)
-{
- struct irlan_cb *self = instance;
- struct net_device *dev = self->dev;
-
- if (skb == NULL) {
- dev->stats.rx_dropped++;
- return 0;
- }
- if (skb->len < ETH_HLEN) {
- pr_debug("%s() : IrLAN frame too short (%d)\n",
- __func__, skb->len);
- dev->stats.rx_dropped++;
- dev_kfree_skb(skb);
- return 0;
- }
-
- /*
- * Adopt this frame! Important to set all these fields since they
- * might have been previously set by the low level IrDA network
- * device driver
- */
- skb->protocol = eth_type_trans(skb, dev); /* Remove eth header */
-
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += skb->len;
-
- netif_rx(skb); /* Eat it! */
-
- return 0;
-}
-
-/*
- * Function irlan_eth_flow (status)
- *
- * Do flow control between IP/Ethernet and IrLAN/IrTTP. This is done by
- * controlling the queue stop/start.
- *
- * The IrDA link layer has the advantage to have flow control, and
- * IrTTP now properly handles that. Flow controlling the higher layers
- * prevent us to drop Tx packets in here (up to 15% for a TCP socket,
- * more for UDP socket).
- * Also, this allow us to reduce the overall transmit queue, which means
- * less latency in case of mixed traffic.
- * Jean II
- */
-void irlan_eth_flow_indication(void *instance, void *sap, LOCAL_FLOW flow)
-{
- struct irlan_cb *self;
- struct net_device *dev;
-
- self = instance;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- dev = self->dev;
-
- IRDA_ASSERT(dev != NULL, return;);
-
- pr_debug("%s() : flow %s ; running %d\n", __func__,
- flow == FLOW_STOP ? "FLOW_STOP" : "FLOW_START",
- netif_running(dev));
-
- switch (flow) {
- case FLOW_STOP:
- /* IrTTP is full, stop higher layers */
- netif_stop_queue(dev);
- break;
- case FLOW_START:
- default:
- /* Tell upper layers that its time to transmit frames again */
- /* Schedule network layer */
- netif_wake_queue(dev);
- break;
- }
-}
-
-/*
- * Function set_multicast_list (dev)
- *
- * Configure the filtering of the device
- *
- */
-#define HW_MAX_ADDRS 4 /* Must query to get it! */
-static void irlan_eth_set_multicast_list(struct net_device *dev)
-{
- struct irlan_cb *self = netdev_priv(dev);
-
- /* Check if data channel has been connected yet */
- if (self->client.state != IRLAN_DATA) {
- pr_debug("%s(), delaying!\n", __func__);
- return;
- }
-
- if (dev->flags & IFF_PROMISC) {
- /* Enable promiscuous mode */
- net_warn_ratelimited("Promiscuous mode not implemented by IrLAN!\n");
- } else if ((dev->flags & IFF_ALLMULTI) ||
- netdev_mc_count(dev) > HW_MAX_ADDRS) {
- /* Disable promiscuous mode, use normal mode. */
- pr_debug("%s(), Setting multicast filter\n", __func__);
- /* hardware_set_filter(NULL); */
-
- irlan_set_multicast_filter(self, TRUE);
- } else if (!netdev_mc_empty(dev)) {
- pr_debug("%s(), Setting multicast filter\n", __func__);
- /* Walk the address list, and load the filter */
- /* hardware_set_filter(dev->mc_list); */
-
- irlan_set_multicast_filter(self, TRUE);
- } else {
- pr_debug("%s(), Clearing multicast filter\n", __func__);
- irlan_set_multicast_filter(self, FALSE);
- }
-
- if (dev->flags & IFF_BROADCAST)
- irlan_set_broadcast_filter(self, TRUE);
- else
- irlan_set_broadcast_filter(self, FALSE);
-}
diff --git a/drivers/staging/irda/net/irlan/irlan_event.c b/drivers/staging/irda/net/irlan/irlan_event.c
deleted file mode 100644
index 9a1cc11c16f6..000000000000
--- a/drivers/staging/irda/net/irlan/irlan_event.c
+++ /dev/null
@@ -1,60 +0,0 @@
-/*********************************************************************
- *
- * Filename: irlan_event.c
- * Version:
- * Description:
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Tue Oct 20 09:10:16 1998
- * Modified at: Sat Oct 30 12:59:01 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
- *
- * 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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <net/irda/irlan_event.h>
-
-const char * const irlan_state[] = {
- "IRLAN_IDLE",
- "IRLAN_QUERY",
- "IRLAN_CONN",
- "IRLAN_INFO",
- "IRLAN_MEDIA",
- "IRLAN_OPEN",
- "IRLAN_WAIT",
- "IRLAN_ARB",
- "IRLAN_DATA",
- "IRLAN_CLOSE",
- "IRLAN_SYNC",
-};
-
-void irlan_next_client_state(struct irlan_cb *self, IRLAN_STATE state)
-{
- pr_debug("%s(), %s\n", __func__ , irlan_state[state]);
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- self->client.state = state;
-}
-
-void irlan_next_provider_state(struct irlan_cb *self, IRLAN_STATE state)
-{
- pr_debug("%s(), %s\n", __func__ , irlan_state[state]);
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- self->provider.state = state;
-}
-
diff --git a/drivers/staging/irda/net/irlan/irlan_filter.c b/drivers/staging/irda/net/irlan/irlan_filter.c
deleted file mode 100644
index e755e90b2f26..000000000000
--- a/drivers/staging/irda/net/irlan/irlan_filter.c
+++ /dev/null
@@ -1,240 +0,0 @@
-/*********************************************************************
- *
- * Filename: irlan_filter.c
- * Version:
- * Description:
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Fri Jan 29 11:16:38 1999
- * Modified at: Sat Oct 30 12:58:45 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
- *
- * 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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/skbuff.h>
-#include <linux/random.h>
-#include <linux/seq_file.h>
-
-#include <net/irda/irlan_common.h>
-#include <net/irda/irlan_filter.h>
-
-/*
- * Function irlan_filter_request (self, skb)
- *
- * Handle filter request from client peer device
- *
- */
-void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- if ((self->provider.filter_type == IRLAN_DIRECTED) &&
- (self->provider.filter_operation == DYNAMIC))
- {
- pr_debug("Giving peer a dynamic Ethernet address\n");
- self->provider.mac_address[0] = 0x40;
- self->provider.mac_address[1] = 0x00;
- self->provider.mac_address[2] = 0x00;
- self->provider.mac_address[3] = 0x00;
-
- /* Use arbitration value to generate MAC address */
- if (self->provider.access_type == ACCESS_PEER) {
- self->provider.mac_address[4] =
- self->provider.send_arb_val & 0xff;
- self->provider.mac_address[5] =
- (self->provider.send_arb_val >> 8) & 0xff;
- } else {
- /* Just generate something for now */
- get_random_bytes(self->provider.mac_address+4, 1);
- get_random_bytes(self->provider.mac_address+5, 1);
- }
-
- skb->data[0] = 0x00; /* Success */
- skb->data[1] = 0x03;
- irlan_insert_string_param(skb, "FILTER_MODE", "NONE");
- irlan_insert_short_param(skb, "MAX_ENTRY", 0x0001);
- irlan_insert_array_param(skb, "FILTER_ENTRY",
- self->provider.mac_address, 6);
- return;
- }
-
- if ((self->provider.filter_type == IRLAN_DIRECTED) &&
- (self->provider.filter_mode == FILTER))
- {
- pr_debug("Directed filter on\n");
- skb->data[0] = 0x00; /* Success */
- skb->data[1] = 0x00;
- return;
- }
- if ((self->provider.filter_type == IRLAN_DIRECTED) &&
- (self->provider.filter_mode == NONE))
- {
- pr_debug("Directed filter off\n");
- skb->data[0] = 0x00; /* Success */
- skb->data[1] = 0x00;
- return;
- }
-
- if ((self->provider.filter_type == IRLAN_BROADCAST) &&
- (self->provider.filter_mode == FILTER))
- {
- pr_debug("Broadcast filter on\n");
- skb->data[0] = 0x00; /* Success */
- skb->data[1] = 0x00;
- return;
- }
- if ((self->provider.filter_type == IRLAN_BROADCAST) &&
- (self->provider.filter_mode == NONE))
- {
- pr_debug("Broadcast filter off\n");
- skb->data[0] = 0x00; /* Success */
- skb->data[1] = 0x00;
- return;
- }
- if ((self->provider.filter_type == IRLAN_MULTICAST) &&
- (self->provider.filter_mode == FILTER))
- {
- pr_debug("Multicast filter on\n");
- skb->data[0] = 0x00; /* Success */
- skb->data[1] = 0x00;
- return;
- }
- if ((self->provider.filter_type == IRLAN_MULTICAST) &&
- (self->provider.filter_mode == NONE))
- {
- pr_debug("Multicast filter off\n");
- skb->data[0] = 0x00; /* Success */
- skb->data[1] = 0x00;
- return;
- }
- if ((self->provider.filter_type == IRLAN_MULTICAST) &&
- (self->provider.filter_operation == GET))
- {
- pr_debug("Multicast filter get\n");
- skb->data[0] = 0x00; /* Success? */
- skb->data[1] = 0x02;
- irlan_insert_string_param(skb, "FILTER_MODE", "NONE");
- irlan_insert_short_param(skb, "MAX_ENTRY", 16);
- return;
- }
- skb->data[0] = 0x00; /* Command not supported */
- skb->data[1] = 0x00;
-
- pr_debug("Not implemented!\n");
-}
-
-/*
- * Function check_request_param (self, param, value)
- *
- * Check parameters in request from peer device
- *
- */
-void irlan_check_command_param(struct irlan_cb *self, char *param, char *value)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- pr_debug("%s, %s\n", param, value);
-
- /*
- * This is experimental!! DB.
- */
- if (strcmp(param, "MODE") == 0) {
- self->use_udata = TRUE;
- return;
- }
-
- /*
- * FILTER_TYPE
- */
- if (strcmp(param, "FILTER_TYPE") == 0) {
- if (strcmp(value, "DIRECTED") == 0) {
- self->provider.filter_type = IRLAN_DIRECTED;
- return;
- }
- if (strcmp(value, "MULTICAST") == 0) {
- self->provider.filter_type = IRLAN_MULTICAST;
- return;
- }
- if (strcmp(value, "BROADCAST") == 0) {
- self->provider.filter_type = IRLAN_BROADCAST;
- return;
- }
- }
- /*
- * FILTER_MODE
- */
- if (strcmp(param, "FILTER_MODE") == 0) {
- if (strcmp(value, "ALL") == 0) {
- self->provider.filter_mode = ALL;
- return;
- }
- if (strcmp(value, "FILTER") == 0) {
- self->provider.filter_mode = FILTER;
- return;
- }
- if (strcmp(value, "NONE") == 0) {
- self->provider.filter_mode = FILTER;
- return;
- }
- }
- /*
- * FILTER_OPERATION
- */
- if (strcmp(param, "FILTER_OPERATION") == 0) {
- if (strcmp(value, "DYNAMIC") == 0) {
- self->provider.filter_operation = DYNAMIC;
- return;
- }
- if (strcmp(value, "GET") == 0) {
- self->provider.filter_operation = GET;
- return;
- }
- }
-}
-
-/*
- * Function irlan_print_filter (filter_type, buf)
- *
- * Print status of filter. Used by /proc file system
- *
- */
-#ifdef CONFIG_PROC_FS
-#define MASK2STR(m,s) { .mask = m, .str = s }
-
-void irlan_print_filter(struct seq_file *seq, int filter_type)
-{
- static struct {
- int mask;
- const char *str;
- } filter_mask2str[] = {
- MASK2STR(IRLAN_DIRECTED, "DIRECTED"),
- MASK2STR(IRLAN_FUNCTIONAL, "FUNCTIONAL"),
- MASK2STR(IRLAN_GROUP, "GROUP"),
- MASK2STR(IRLAN_MAC_FRAME, "MAC_FRAME"),
- MASK2STR(IRLAN_MULTICAST, "MULTICAST"),
- MASK2STR(IRLAN_BROADCAST, "BROADCAST"),
- MASK2STR(IRLAN_IPX_SOCKET, "IPX_SOCKET"),
- MASK2STR(0, NULL)
- }, *p;
-
- for (p = filter_mask2str; p->str; p++) {
- if (filter_type & p->mask)
- seq_printf(seq, "%s ", p->str);
- }
- seq_putc(seq, '\n');
-}
-#undef MASK2STR
-#endif
diff --git a/drivers/staging/irda/net/irlan/irlan_provider.c b/drivers/staging/irda/net/irlan/irlan_provider.c
deleted file mode 100644
index 15c292cf2644..000000000000
--- a/drivers/staging/irda/net/irlan/irlan_provider.c
+++ /dev/null
@@ -1,408 +0,0 @@
-/*********************************************************************
- *
- * Filename: irlan_provider.c
- * Version: 0.9
- * Description: IrDA LAN Access Protocol Implementation
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sun Aug 31 20:14:37 1997
- * Modified at: Sat Oct 30 12:52:10 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- * Sources: skeleton.c by Donald Becker <becker@CESDIS.gsfc.nasa.gov>
- * slip.c by Laurence Culhane, <loz@holmes.demon.co.uk>
- * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
- *
- * Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
- * All Rights Reserved.
- *
- * 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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/init.h>
-#include <linux/random.h>
-#include <linux/bitops.h>
-#include <linux/slab.h>
-
-#include <asm/byteorder.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irttp.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/irias_object.h>
-#include <net/irda/iriap.h>
-#include <net/irda/timer.h>
-
-#include <net/irda/irlan_common.h>
-#include <net/irda/irlan_eth.h>
-#include <net/irda/irlan_event.h>
-#include <net/irda/irlan_provider.h>
-#include <net/irda/irlan_filter.h>
-#include <net/irda/irlan_client.h>
-
-static void irlan_provider_connect_indication(void *instance, void *sap,
- struct qos_info *qos,
- __u32 max_sdu_size,
- __u8 max_header_size,
- struct sk_buff *skb);
-
-/*
- * Function irlan_provider_control_data_indication (handle, skb)
- *
- * This function gets the data that is received on the control channel
- *
- */
-static int irlan_provider_data_indication(void *instance, void *sap,
- struct sk_buff *skb)
-{
- struct irlan_cb *self;
- __u8 code;
-
- self = instance;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
-
- IRDA_ASSERT(skb != NULL, return -1;);
-
- code = skb->data[0];
- switch(code) {
- case CMD_GET_PROVIDER_INFO:
- pr_debug("Got GET_PROVIDER_INFO command!\n");
- irlan_do_provider_event(self, IRLAN_GET_INFO_CMD, skb);
- break;
-
- case CMD_GET_MEDIA_CHAR:
- pr_debug("Got GET_MEDIA_CHAR command!\n");
- irlan_do_provider_event(self, IRLAN_GET_MEDIA_CMD, skb);
- break;
- case CMD_OPEN_DATA_CHANNEL:
- pr_debug("Got OPEN_DATA_CHANNEL command!\n");
- irlan_do_provider_event(self, IRLAN_OPEN_DATA_CMD, skb);
- break;
- case CMD_FILTER_OPERATION:
- pr_debug("Got FILTER_OPERATION command!\n");
- irlan_do_provider_event(self, IRLAN_FILTER_CONFIG_CMD, skb);
- break;
- case CMD_RECONNECT_DATA_CHAN:
- pr_debug("%s(), Got RECONNECT_DATA_CHAN command\n", __func__);
- pr_debug("%s(), NOT IMPLEMENTED\n", __func__);
- break;
- case CMD_CLOSE_DATA_CHAN:
- pr_debug("Got CLOSE_DATA_CHAN command!\n");
- pr_debug("%s(), NOT IMPLEMENTED\n", __func__);
- break;
- default:
- pr_debug("%s(), Unknown command!\n", __func__);
- break;
- }
- return 0;
-}
-
-/*
- * Function irlan_provider_connect_indication (handle, skb, priv)
- *
- * Got connection from peer IrLAN client
- *
- */
-static void irlan_provider_connect_indication(void *instance, void *sap,
- struct qos_info *qos,
- __u32 max_sdu_size,
- __u8 max_header_size,
- struct sk_buff *skb)
-{
- struct irlan_cb *self;
- struct tsap_cb *tsap;
-
- self = instance;
- tsap = sap;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- IRDA_ASSERT(tsap == self->provider.tsap_ctrl,return;);
- IRDA_ASSERT(self->provider.state == IRLAN_IDLE, return;);
-
- self->provider.max_sdu_size = max_sdu_size;
- self->provider.max_header_size = max_header_size;
-
- irlan_do_provider_event(self, IRLAN_CONNECT_INDICATION, NULL);
-
- /*
- * If we are in peer mode, the client may not have got the discovery
- * indication it needs to make progress. If the client is still in
- * IDLE state, we must kick it.
- */
- if ((self->provider.access_type == ACCESS_PEER) &&
- (self->client.state == IRLAN_IDLE))
- {
- irlan_client_wakeup(self, self->saddr, self->daddr);
- }
-}
-
-/*
- * Function irlan_provider_connect_response (handle)
- *
- * Accept incoming connection
- *
- */
-void irlan_provider_connect_response(struct irlan_cb *self,
- struct tsap_cb *tsap)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- /* Just accept */
- irttp_connect_response(tsap, IRLAN_MTU, NULL);
-}
-
-static void irlan_provider_disconnect_indication(void *instance, void *sap,
- LM_REASON reason,
- struct sk_buff *userdata)
-{
- struct irlan_cb *self;
- struct tsap_cb *tsap;
-
- pr_debug("%s(), reason=%d\n", __func__ , reason);
-
- self = instance;
- tsap = sap;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
- IRDA_ASSERT(tsap != NULL, return;);
- IRDA_ASSERT(tsap->magic == TTP_TSAP_MAGIC, return;);
-
- IRDA_ASSERT(tsap == self->provider.tsap_ctrl, return;);
-
- irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL);
-}
-
-/*
- * Function irlan_parse_open_data_cmd (self, skb)
- *
- *
- *
- */
-int irlan_parse_open_data_cmd(struct irlan_cb *self, struct sk_buff *skb)
-{
- int ret;
-
- ret = irlan_provider_parse_command(self, CMD_OPEN_DATA_CHANNEL, skb);
-
- /* Open data channel */
- irlan_open_data_tsap(self);
-
- return ret;
-}
-
-/*
- * Function parse_command (skb)
- *
- * Extract all parameters from received buffer, then feed them to
- * check_params for parsing
- *
- */
-int irlan_provider_parse_command(struct irlan_cb *self, int cmd,
- struct sk_buff *skb)
-{
- __u8 *frame;
- __u8 *ptr;
- int count;
- __u16 val_len;
- int i;
- char *name;
- char *value;
- int ret = RSP_SUCCESS;
-
- IRDA_ASSERT(skb != NULL, return -RSP_PROTOCOL_ERROR;);
-
- pr_debug("%s(), skb->len=%d\n", __func__ , (int)skb->len);
-
- IRDA_ASSERT(self != NULL, return -RSP_PROTOCOL_ERROR;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -RSP_PROTOCOL_ERROR;);
-
- if (!skb)
- return -RSP_PROTOCOL_ERROR;
-
- frame = skb->data;
-
- name = kmalloc(255, GFP_ATOMIC);
- if (!name)
- return -RSP_INSUFFICIENT_RESOURCES;
- value = kmalloc(1016, GFP_ATOMIC);
- if (!value) {
- kfree(name);
- return -RSP_INSUFFICIENT_RESOURCES;
- }
-
- /* How many parameters? */
- count = frame[1];
-
- pr_debug("Got %d parameters\n", count);
-
- ptr = frame+2;
-
- /* For all parameters */
- for (i=0; i<count;i++) {
- ret = irlan_extract_param(ptr, name, value, &val_len);
- if (ret < 0) {
- pr_debug("%s(), IrLAN, Error!\n", __func__);
- break;
- }
- ptr+=ret;
- ret = RSP_SUCCESS;
- irlan_check_command_param(self, name, value);
- }
- /* Cleanup */
- kfree(name);
- kfree(value);
-
- return ret;
-}
-
-/*
- * Function irlan_provider_send_reply (self, info)
- *
- * Send reply to query to peer IrLAN layer
- *
- */
-void irlan_provider_send_reply(struct irlan_cb *self, int command,
- int ret_code)
-{
- struct sk_buff *skb;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
- skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER +
- /* Bigger param length comes from CMD_GET_MEDIA_CHAR */
- IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "DIRECTED") +
- IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "BROADCAST") +
- IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "MULTICAST") +
- IRLAN_STRING_PARAMETER_LEN("ACCESS_TYPE", "HOSTED"),
- GFP_ATOMIC);
-
- if (!skb)
- return;
-
- /* Reserve space for TTP, LMP, and LAP header */
- skb_reserve(skb, self->provider.max_header_size);
- skb_put(skb, 2);
-
- switch (command) {
- case CMD_GET_PROVIDER_INFO:
- skb->data[0] = 0x00; /* Success */
- skb->data[1] = 0x02; /* 2 parameters */
- switch (self->media) {
- case MEDIA_802_3:
- irlan_insert_string_param(skb, "MEDIA", "802.3");
- break;
- case MEDIA_802_5:
- irlan_insert_string_param(skb, "MEDIA", "802.5");
- break;
- default:
- pr_debug("%s(), unknown media type!\n", __func__);
- break;
- }
- irlan_insert_short_param(skb, "IRLAN_VER", 0x0101);
- break;
-
- case CMD_GET_MEDIA_CHAR:
- skb->data[0] = 0x00; /* Success */
- skb->data[1] = 0x05; /* 5 parameters */
- irlan_insert_string_param(skb, "FILTER_TYPE", "DIRECTED");
- irlan_insert_string_param(skb, "FILTER_TYPE", "BROADCAST");
- irlan_insert_string_param(skb, "FILTER_TYPE", "MULTICAST");
-
- switch (self->provider.access_type) {
- case ACCESS_DIRECT:
- irlan_insert_string_param(skb, "ACCESS_TYPE", "DIRECT");
- break;
- case ACCESS_PEER:
- irlan_insert_string_param(skb, "ACCESS_TYPE", "PEER");
- break;
- case ACCESS_HOSTED:
- irlan_insert_string_param(skb, "ACCESS_TYPE", "HOSTED");
- break;
- default:
- pr_debug("%s(), Unknown access type\n", __func__);
- break;
- }
- irlan_insert_short_param(skb, "MAX_FRAME", 0x05ee);
- break;
- case CMD_OPEN_DATA_CHANNEL:
- skb->data[0] = 0x00; /* Success */
- if (self->provider.send_arb_val) {
- skb->data[1] = 0x03; /* 3 parameters */
- irlan_insert_short_param(skb, "CON_ARB",
- self->provider.send_arb_val);
- } else
- skb->data[1] = 0x02; /* 2 parameters */
- irlan_insert_byte_param(skb, "DATA_CHAN", self->stsap_sel_data);
- irlan_insert_string_param(skb, "RECONNECT_KEY", "LINUX RULES!");
- break;
- case CMD_FILTER_OPERATION:
- irlan_filter_request(self, skb);
- break;
- default:
- pr_debug("%s(), Unknown command!\n", __func__);
- break;
- }
-
- irttp_data_request(self->provider.tsap_ctrl, skb);
-}
-
-/*
- * Function irlan_provider_register(void)
- *
- * Register provider support so we can accept incoming connections.
- *
- */
-int irlan_provider_open_ctrl_tsap(struct irlan_cb *self)
-{
- struct tsap_cb *tsap;
- notify_t notify;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
-
- /* Check if already open */
- if (self->provider.tsap_ctrl)
- return -1;
-
- /*
- * First register well known control TSAP
- */
- irda_notify_init(&notify);
- notify.data_indication = irlan_provider_data_indication;
- notify.connect_indication = irlan_provider_connect_indication;
- notify.disconnect_indication = irlan_provider_disconnect_indication;
- notify.instance = self;
- strlcpy(notify.name, "IrLAN ctrl (p)", sizeof(notify.name));
-
- tsap = irttp_open_tsap(LSAP_ANY, 1, &notify);
- if (!tsap) {
- pr_debug("%s(), Got no tsap!\n", __func__);
- return -1;
- }
- self->provider.tsap_ctrl = tsap;
-
- /* Register with LM-IAS */
- irlan_ias_register(self, tsap->stsap_sel);
-
- return 0;
-}
-
diff --git a/drivers/staging/irda/net/irlan/irlan_provider_event.c b/drivers/staging/irda/net/irlan/irlan_provider_event.c
deleted file mode 100644
index 9c4f7f51d6b5..000000000000
--- a/drivers/staging/irda/net/irlan/irlan_provider_event.c
+++ /dev/null
@@ -1,233 +0,0 @@
-/*********************************************************************
- *
- * Filename: irlan_provider_event.c
- * Version: 0.9
- * Description: IrLAN provider state machine)
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sun Aug 31 20:14:37 1997
- * Modified at: Sat Oct 30 12:52:41 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
- *
- * 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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <net/irda/irda.h>
-#include <net/irda/iriap.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/irttp.h>
-
-#include <net/irda/irlan_provider.h>
-#include <net/irda/irlan_event.h>
-
-static int irlan_provider_state_idle(struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb);
-static int irlan_provider_state_info(struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb);
-static int irlan_provider_state_open(struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb);
-static int irlan_provider_state_data(struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb);
-
-static int (*state[])(struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb) =
-{
- irlan_provider_state_idle,
- NULL, /* Query */
- NULL, /* Info */
- irlan_provider_state_info,
- NULL, /* Media */
- irlan_provider_state_open,
- NULL, /* Wait */
- NULL, /* Arb */
- irlan_provider_state_data,
- NULL, /* Close */
- NULL, /* Sync */
-};
-
-void irlan_do_provider_event(struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb)
-{
- IRDA_ASSERT(*state[ self->provider.state] != NULL, return;);
-
- (*state[self->provider.state]) (self, event, skb);
-}
-
-/*
- * Function irlan_provider_state_idle (event, skb, info)
- *
- * IDLE, We are waiting for an indication that there is a provider
- * available.
- */
-static int irlan_provider_state_idle(struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb)
-{
- IRDA_ASSERT(self != NULL, return -1;);
-
- switch(event) {
- case IRLAN_CONNECT_INDICATION:
- irlan_provider_connect_response( self, self->provider.tsap_ctrl);
- irlan_next_provider_state( self, IRLAN_INFO);
- break;
- default:
- pr_debug("%s(), Unknown event %d\n", __func__ , event);
- break;
- }
- if (skb)
- dev_kfree_skb(skb);
-
- return 0;
-}
-
-/*
- * Function irlan_provider_state_info (self, event, skb, info)
- *
- * INFO, We have issued a GetInfo command and is awaiting a reply.
- */
-static int irlan_provider_state_info(struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb)
-{
- int ret;
-
- IRDA_ASSERT(self != NULL, return -1;);
-
- switch(event) {
- case IRLAN_GET_INFO_CMD:
- /* Be sure to use 802.3 in case of peer mode */
- if (self->provider.access_type == ACCESS_PEER) {
- self->media = MEDIA_802_3;
-
- /* Check if client has started yet */
- if (self->client.state == IRLAN_IDLE) {
- /* This should get the client going */
- irlmp_discovery_request(8);
- }
- }
-
- irlan_provider_send_reply(self, CMD_GET_PROVIDER_INFO,
- RSP_SUCCESS);
- /* Keep state */
- break;
- case IRLAN_GET_MEDIA_CMD:
- irlan_provider_send_reply(self, CMD_GET_MEDIA_CHAR,
- RSP_SUCCESS);
- /* Keep state */
- break;
- case IRLAN_OPEN_DATA_CMD:
- ret = irlan_parse_open_data_cmd(self, skb);
- if (self->provider.access_type == ACCESS_PEER) {
- /* FIXME: make use of random functions! */
- self->provider.send_arb_val = (jiffies & 0xffff);
- }
- irlan_provider_send_reply(self, CMD_OPEN_DATA_CHANNEL, ret);
-
- if (ret == RSP_SUCCESS) {
- irlan_next_provider_state(self, IRLAN_OPEN);
-
- /* Signal client that we are now open */
- irlan_do_client_event(self, IRLAN_PROVIDER_SIGNAL, NULL);
- }
- break;
- case IRLAN_LMP_DISCONNECT: /* FALLTHROUGH */
- case IRLAN_LAP_DISCONNECT:
- irlan_next_provider_state(self, IRLAN_IDLE);
- break;
- default:
- pr_debug("%s(), Unknown event %d\n", __func__ , event);
- break;
- }
- if (skb)
- dev_kfree_skb(skb);
-
- return 0;
-}
-
-/*
- * Function irlan_provider_state_open (self, event, skb, info)
- *
- * OPEN, The client has issued a OpenData command and is awaiting a
- * reply
- *
- */
-static int irlan_provider_state_open(struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb)
-{
- IRDA_ASSERT(self != NULL, return -1;);
-
- switch(event) {
- case IRLAN_FILTER_CONFIG_CMD:
- irlan_provider_parse_command(self, CMD_FILTER_OPERATION, skb);
- irlan_provider_send_reply(self, CMD_FILTER_OPERATION,
- RSP_SUCCESS);
- /* Keep state */
- break;
- case IRLAN_DATA_CONNECT_INDICATION:
- irlan_next_provider_state(self, IRLAN_DATA);
- irlan_provider_connect_response(self, self->tsap_data);
- break;
- case IRLAN_LMP_DISCONNECT: /* FALLTHROUGH */
- case IRLAN_LAP_DISCONNECT:
- irlan_next_provider_state(self, IRLAN_IDLE);
- break;
- default:
- pr_debug("%s(), Unknown event %d\n", __func__ , event);
- break;
- }
- if (skb)
- dev_kfree_skb(skb);
-
- return 0;
-}
-
-/*
- * Function irlan_provider_state_data (self, event, skb, info)
- *
- * DATA, The data channel is connected, allowing data transfers between
- * the local and remote machines.
- *
- */
-static int irlan_provider_state_data(struct irlan_cb *self, IRLAN_EVENT event,
- struct sk_buff *skb)
-{
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
-
- switch(event) {
- case IRLAN_FILTER_CONFIG_CMD:
- irlan_provider_parse_command(self, CMD_FILTER_OPERATION, skb);
- irlan_provider_send_reply(self, CMD_FILTER_OPERATION,
- RSP_SUCCESS);
- break;
- case IRLAN_LMP_DISCONNECT: /* FALLTHROUGH */
- case IRLAN_LAP_DISCONNECT:
- irlan_next_provider_state(self, IRLAN_IDLE);
- break;
- default:
- pr_debug("%s(), Unknown event %d\n", __func__ , event);
- break;
- }
- if (skb)
- dev_kfree_skb(skb);
-
- return 0;
-}
-
-
-
-
-
-
-
-
-
-
diff --git a/drivers/staging/irda/net/irlap.c b/drivers/staging/irda/net/irlap.c
deleted file mode 100644
index d7d894423b4f..000000000000
--- a/drivers/staging/irda/net/irlap.c
+++ /dev/null
@@ -1,1207 +0,0 @@
-/*********************************************************************
- *
- * Filename: irlap.c
- * Version: 1.0
- * Description: IrLAP implementation for Linux
- * Status: Stable
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Mon Aug 4 20:40:53 1997
- * Modified at: Tue Dec 14 09:26:44 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
- * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/skbuff.h>
-#include <linux/delay.h>
-#include <linux/proc_fs.h>
-#include <linux/init.h>
-#include <linux/random.h>
-#include <linux/module.h>
-#include <linux/seq_file.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irda_device.h>
-#include <net/irda/irqueue.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/irlmp_frame.h>
-#include <net/irda/irlap_frame.h>
-#include <net/irda/irlap.h>
-#include <net/irda/timer.h>
-#include <net/irda/qos.h>
-
-static hashbin_t *irlap = NULL;
-int sysctl_slot_timeout = SLOT_TIMEOUT * 1000 / HZ;
-
-/* This is the delay of missed pf period before generating an event
- * to the application. The spec mandate 3 seconds, but in some cases
- * it's way too long. - Jean II */
-int sysctl_warn_noreply_time = 3;
-
-extern void irlap_queue_xmit(struct irlap_cb *self, struct sk_buff *skb);
-static void __irlap_close(struct irlap_cb *self);
-static void irlap_init_qos_capabilities(struct irlap_cb *self,
- struct qos_info *qos_user);
-
-static const char *const lap_reasons[] __maybe_unused = {
- "ERROR, NOT USED",
- "LAP_DISC_INDICATION",
- "LAP_NO_RESPONSE",
- "LAP_RESET_INDICATION",
- "LAP_FOUND_NONE",
- "LAP_MEDIA_BUSY",
- "LAP_PRIMARY_CONFLICT",
- "ERROR, NOT USED",
-};
-
-int __init irlap_init(void)
-{
- /* Check if the compiler did its job properly.
- * May happen on some ARM configuration, check with Russell King. */
- IRDA_ASSERT(sizeof(struct xid_frame) == 14, ;);
- IRDA_ASSERT(sizeof(struct test_frame) == 10, ;);
- IRDA_ASSERT(sizeof(struct ua_frame) == 10, ;);
- IRDA_ASSERT(sizeof(struct snrm_frame) == 11, ;);
-
- /* Allocate master array */
- irlap = hashbin_new(HB_LOCK);
- if (irlap == NULL) {
- net_err_ratelimited("%s: can't allocate irlap hashbin!\n",
- __func__);
- return -ENOMEM;
- }
-
- return 0;
-}
-
-void irlap_cleanup(void)
-{
- IRDA_ASSERT(irlap != NULL, return;);
-
- hashbin_delete(irlap, (FREE_FUNC) __irlap_close);
-}
-
-/*
- * Function irlap_open (driver)
- *
- * Initialize IrLAP layer
- *
- */
-struct irlap_cb *irlap_open(struct net_device *dev, struct qos_info *qos,
- const char *hw_name)
-{
- struct irlap_cb *self;
-
- /* Initialize the irlap structure. */
- self = kzalloc(sizeof(struct irlap_cb), GFP_KERNEL);
- if (self == NULL)
- return NULL;
-
- self->magic = LAP_MAGIC;
-
- /* Make a binding between the layers */
- self->netdev = dev;
- self->qos_dev = qos;
- /* Copy hardware name */
- if(hw_name != NULL) {
- strlcpy(self->hw_name, hw_name, sizeof(self->hw_name));
- } else {
- self->hw_name[0] = '\0';
- }
-
- /* FIXME: should we get our own field? */
- dev->atalk_ptr = self;
-
- self->state = LAP_OFFLINE;
-
- /* Initialize transmit queue */
- skb_queue_head_init(&self->txq);
- skb_queue_head_init(&self->txq_ultra);
- skb_queue_head_init(&self->wx_list);
-
- /* My unique IrLAP device address! */
- /* We don't want the broadcast address, neither the NULL address
- * (most often used to signify "invalid"), and we don't want an
- * address already in use (otherwise connect won't be able
- * to select the proper link). - Jean II */
- do {
- get_random_bytes(&self->saddr, sizeof(self->saddr));
- } while ((self->saddr == 0x0) || (self->saddr == BROADCAST) ||
- (hashbin_lock_find(irlap, self->saddr, NULL)) );
- /* Copy to the driver */
- memcpy(dev->dev_addr, &self->saddr, 4);
-
- timer_setup(&self->slot_timer, NULL, 0);
- timer_setup(&self->query_timer, NULL, 0);
- timer_setup(&self->discovery_timer, NULL, 0);
- timer_setup(&self->final_timer, NULL, 0);
- timer_setup(&self->poll_timer, NULL, 0);
- timer_setup(&self->wd_timer, NULL, 0);
- timer_setup(&self->backoff_timer, NULL, 0);
- timer_setup(&self->media_busy_timer, NULL, 0);
-
- irlap_apply_default_connection_parameters(self);
-
- self->N3 = 3; /* # connections attempts to try before giving up */
-
- self->state = LAP_NDM;
-
- hashbin_insert(irlap, (irda_queue_t *) self, self->saddr, NULL);
-
- irlmp_register_link(self, self->saddr, &self->notify);
-
- return self;
-}
-EXPORT_SYMBOL(irlap_open);
-
-/*
- * Function __irlap_close (self)
- *
- * Remove IrLAP and all allocated memory. Stop any pending timers.
- *
- */
-static void __irlap_close(struct irlap_cb *self)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
- /* Stop timers */
- del_timer(&self->slot_timer);
- del_timer(&self->query_timer);
- del_timer(&self->discovery_timer);
- del_timer(&self->final_timer);
- del_timer(&self->poll_timer);
- del_timer(&self->wd_timer);
- del_timer(&self->backoff_timer);
- del_timer(&self->media_busy_timer);
-
- irlap_flush_all_queues(self);
-
- self->magic = 0;
-
- kfree(self);
-}
-
-/*
- * Function irlap_close (self)
- *
- * Remove IrLAP instance
- *
- */
-void irlap_close(struct irlap_cb *self)
-{
- struct irlap_cb *lap;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
- /* We used to send a LAP_DISC_INDICATION here, but this was
- * racy. This has been move within irlmp_unregister_link()
- * itself. Jean II */
-
- /* Kill the LAP and all LSAPs on top of it */
- irlmp_unregister_link(self->saddr);
- self->notify.instance = NULL;
-
- /* Be sure that we manage to remove ourself from the hash */
- lap = hashbin_remove(irlap, self->saddr, NULL);
- if (!lap) {
- pr_debug("%s(), Didn't find myself!\n", __func__);
- return;
- }
- __irlap_close(lap);
-}
-EXPORT_SYMBOL(irlap_close);
-
-/*
- * Function irlap_connect_indication (self, skb)
- *
- * Another device is attempting to make a connection
- *
- */
-void irlap_connect_indication(struct irlap_cb *self, struct sk_buff *skb)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
- irlap_init_qos_capabilities(self, NULL); /* No user QoS! */
-
- irlmp_link_connect_indication(self->notify.instance, self->saddr,
- self->daddr, &self->qos_tx, skb);
-}
-
-/*
- * Function irlap_connect_response (self, skb)
- *
- * Service user has accepted incoming connection
- *
- */
-void irlap_connect_response(struct irlap_cb *self, struct sk_buff *userdata)
-{
- irlap_do_event(self, CONNECT_RESPONSE, userdata, NULL);
-}
-
-/*
- * Function irlap_connect_request (self, daddr, qos_user, sniff)
- *
- * Request connection with another device, sniffing is not implemented
- * yet.
- *
- */
-void irlap_connect_request(struct irlap_cb *self, __u32 daddr,
- struct qos_info *qos_user, int sniff)
-{
- pr_debug("%s(), daddr=0x%08x\n", __func__, daddr);
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
- self->daddr = daddr;
-
- /*
- * If the service user specifies QoS values for this connection,
- * then use them
- */
- irlap_init_qos_capabilities(self, qos_user);
-
- if ((self->state == LAP_NDM) && !self->media_busy)
- irlap_do_event(self, CONNECT_REQUEST, NULL, NULL);
- else
- self->connect_pending = TRUE;
-}
-
-/*
- * Function irlap_connect_confirm (self, skb)
- *
- * Connection request has been accepted
- *
- */
-void irlap_connect_confirm(struct irlap_cb *self, struct sk_buff *skb)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
- irlmp_link_connect_confirm(self->notify.instance, &self->qos_tx, skb);
-}
-
-/*
- * Function irlap_data_indication (self, skb)
- *
- * Received data frames from IR-port, so we just pass them up to
- * IrLMP for further processing
- *
- */
-void irlap_data_indication(struct irlap_cb *self, struct sk_buff *skb,
- int unreliable)
-{
- /* Hide LAP header from IrLMP layer */
- skb_pull(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER);
-
- irlmp_link_data_indication(self->notify.instance, skb, unreliable);
-}
-
-
-/*
- * Function irlap_data_request (self, skb)
- *
- * Queue data for transmission, must wait until XMIT state
- *
- */
-void irlap_data_request(struct irlap_cb *self, struct sk_buff *skb,
- int unreliable)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
- IRDA_ASSERT(skb_headroom(skb) >= (LAP_ADDR_HEADER+LAP_CTRL_HEADER),
- return;);
- skb_push(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER);
-
- /*
- * Must set frame format now so that the rest of the code knows
- * if its dealing with an I or an UI frame
- */
- if (unreliable)
- skb->data[1] = UI_FRAME;
- else
- skb->data[1] = I_FRAME;
-
- /* Don't forget to refcount it - see irlmp_connect_request(). */
- skb_get(skb);
-
- /* Add at the end of the queue (keep ordering) - Jean II */
- skb_queue_tail(&self->txq, skb);
-
- /*
- * Send event if this frame only if we are in the right state
- * FIXME: udata should be sent first! (skb_queue_head?)
- */
- if ((self->state == LAP_XMIT_P) || (self->state == LAP_XMIT_S)) {
- /* If we are not already processing the Tx queue, trigger
- * transmission immediately - Jean II */
- if((skb_queue_len(&self->txq) <= 1) && (!self->local_busy))
- irlap_do_event(self, DATA_REQUEST, skb, NULL);
- /* Otherwise, the packets will be sent normally at the
- * next pf-poll - Jean II */
- }
-}
-
-/*
- * Function irlap_unitdata_request (self, skb)
- *
- * Send Ultra data. This is data that must be sent outside any connection
- *
- */
-#ifdef CONFIG_IRDA_ULTRA
-void irlap_unitdata_request(struct irlap_cb *self, struct sk_buff *skb)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
- IRDA_ASSERT(skb_headroom(skb) >= (LAP_ADDR_HEADER+LAP_CTRL_HEADER),
- return;);
- skb_push(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER);
-
- skb->data[0] = CBROADCAST;
- skb->data[1] = UI_FRAME;
-
- /* Don't need to refcount, see irlmp_connless_data_request() */
-
- skb_queue_tail(&self->txq_ultra, skb);
-
- irlap_do_event(self, SEND_UI_FRAME, NULL, NULL);
-}
-#endif /*CONFIG_IRDA_ULTRA */
-
-/*
- * Function irlap_udata_indication (self, skb)
- *
- * Receive Ultra data. This is data that is received outside any connection
- *
- */
-#ifdef CONFIG_IRDA_ULTRA
-void irlap_unitdata_indication(struct irlap_cb *self, struct sk_buff *skb)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
- IRDA_ASSERT(skb != NULL, return;);
-
- /* Hide LAP header from IrLMP layer */
- skb_pull(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER);
-
- irlmp_link_unitdata_indication(self->notify.instance, skb);
-}
-#endif /* CONFIG_IRDA_ULTRA */
-
-/*
- * Function irlap_disconnect_request (void)
- *
- * Request to disconnect connection by service user
- */
-void irlap_disconnect_request(struct irlap_cb *self)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
- /* Don't disconnect until all data frames are successfully sent */
- if (!skb_queue_empty(&self->txq)) {
- self->disconnect_pending = TRUE;
- return;
- }
-
- /* Check if we are in the right state for disconnecting */
- switch (self->state) {
- case LAP_XMIT_P: /* FALLTHROUGH */
- case LAP_XMIT_S: /* FALLTHROUGH */
- case LAP_CONN: /* FALLTHROUGH */
- case LAP_RESET_WAIT: /* FALLTHROUGH */
- case LAP_RESET_CHECK:
- irlap_do_event(self, DISCONNECT_REQUEST, NULL, NULL);
- break;
- default:
- pr_debug("%s(), disconnect pending!\n", __func__);
- self->disconnect_pending = TRUE;
- break;
- }
-}
-
-/*
- * Function irlap_disconnect_indication (void)
- *
- * Disconnect request from other device
- *
- */
-void irlap_disconnect_indication(struct irlap_cb *self, LAP_REASON reason)
-{
- pr_debug("%s(), reason=%s\n", __func__, lap_reasons[reason]);
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
- /* Flush queues */
- irlap_flush_all_queues(self);
-
- switch (reason) {
- case LAP_RESET_INDICATION:
- pr_debug("%s(), Sending reset request!\n", __func__);
- irlap_do_event(self, RESET_REQUEST, NULL, NULL);
- break;
- case LAP_NO_RESPONSE: /* FALLTHROUGH */
- case LAP_DISC_INDICATION: /* FALLTHROUGH */
- case LAP_FOUND_NONE: /* FALLTHROUGH */
- case LAP_MEDIA_BUSY:
- irlmp_link_disconnect_indication(self->notify.instance, self,
- reason, NULL);
- break;
- default:
- net_err_ratelimited("%s: Unknown reason %d\n",
- __func__, reason);
- }
-}
-
-/*
- * Function irlap_discovery_request (gen_addr_bit)
- *
- * Start one single discovery operation.
- *
- */
-void irlap_discovery_request(struct irlap_cb *self, discovery_t *discovery)
-{
- struct irlap_info info;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
- IRDA_ASSERT(discovery != NULL, return;);
-
- pr_debug("%s(), nslots = %d\n", __func__, discovery->nslots);
-
- IRDA_ASSERT((discovery->nslots == 1) || (discovery->nslots == 6) ||
- (discovery->nslots == 8) || (discovery->nslots == 16),
- return;);
-
- /* Discovery is only possible in NDM mode */
- if (self->state != LAP_NDM) {
- pr_debug("%s(), discovery only possible in NDM mode\n",
- __func__);
- irlap_discovery_confirm(self, NULL);
- /* Note : in theory, if we are not in NDM, we could postpone
- * the discovery like we do for connection request.
- * In practice, it's not worth it. If the media was busy,
- * it's likely next time around it won't be busy. If we are
- * in REPLY state, we will get passive discovery info & event.
- * Jean II */
- return;
- }
-
- /* Check if last discovery request finished in time, or if
- * it was aborted due to the media busy flag. */
- if (self->discovery_log != NULL) {
- hashbin_delete(self->discovery_log, (FREE_FUNC) kfree);
- self->discovery_log = NULL;
- }
-
- /* All operations will occur at predictable time, no need to lock */
- self->discovery_log = hashbin_new(HB_NOLOCK);
-
- if (self->discovery_log == NULL) {
- net_warn_ratelimited("%s(), Unable to allocate discovery log!\n",
- __func__);
- return;
- }
-
- info.S = discovery->nslots; /* Number of slots */
- info.s = 0; /* Current slot */
-
- self->discovery_cmd = discovery;
- info.discovery = discovery;
-
- /* sysctl_slot_timeout bounds are checked in irsysctl.c - Jean II */
- self->slot_timeout = msecs_to_jiffies(sysctl_slot_timeout);
-
- irlap_do_event(self, DISCOVERY_REQUEST, NULL, &info);
-}
-
-/*
- * Function irlap_discovery_confirm (log)
- *
- * A device has been discovered in front of this station, we
- * report directly to LMP.
- */
-void irlap_discovery_confirm(struct irlap_cb *self, hashbin_t *discovery_log)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
- IRDA_ASSERT(self->notify.instance != NULL, return;);
-
- /*
- * Check for successful discovery, since we are then allowed to clear
- * the media busy condition (IrLAP 6.13.4 - p.94). This should allow
- * us to make connection attempts much faster and easier (i.e. no
- * collisions).
- * Setting media busy to false will also generate an event allowing
- * to process pending events in NDM state machine.
- * Note : the spec doesn't define what's a successful discovery is.
- * If we want Ultra to work, it's successful even if there is
- * nobody discovered - Jean II
- */
- if (discovery_log)
- irda_device_set_media_busy(self->netdev, FALSE);
-
- /* Inform IrLMP */
- irlmp_link_discovery_confirm(self->notify.instance, discovery_log);
-}
-
-/*
- * Function irlap_discovery_indication (log)
- *
- * Somebody is trying to discover us!
- *
- */
-void irlap_discovery_indication(struct irlap_cb *self, discovery_t *discovery)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
- IRDA_ASSERT(discovery != NULL, return;);
-
- IRDA_ASSERT(self->notify.instance != NULL, return;);
-
- /* A device is very likely to connect immediately after it performs
- * a successful discovery. This means that in our case, we are much
- * more likely to receive a connection request over the medium.
- * So, we backoff to avoid collisions.
- * IrLAP spec 6.13.4 suggest 100ms...
- * Note : this little trick actually make a *BIG* difference. If I set
- * my Linux box with discovery enabled and one Ultra frame sent every
- * second, my Palm has no trouble connecting to it every time !
- * Jean II */
- irda_device_set_media_busy(self->netdev, SMALL);
-
- irlmp_link_discovery_indication(self->notify.instance, discovery);
-}
-
-/*
- * Function irlap_status_indication (quality_of_link)
- */
-void irlap_status_indication(struct irlap_cb *self, int quality_of_link)
-{
- switch (quality_of_link) {
- case STATUS_NO_ACTIVITY:
- net_info_ratelimited("IrLAP, no activity on link!\n");
- break;
- case STATUS_NOISY:
- net_info_ratelimited("IrLAP, noisy link!\n");
- break;
- default:
- break;
- }
- irlmp_status_indication(self->notify.instance,
- quality_of_link, LOCK_NO_CHANGE);
-}
-
-/*
- * Function irlap_reset_indication (void)
- */
-void irlap_reset_indication(struct irlap_cb *self)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
- if (self->state == LAP_RESET_WAIT)
- irlap_do_event(self, RESET_REQUEST, NULL, NULL);
- else
- irlap_do_event(self, RESET_RESPONSE, NULL, NULL);
-}
-
-/*
- * Function irlap_reset_confirm (void)
- */
-void irlap_reset_confirm(void)
-{
-}
-
-/*
- * Function irlap_generate_rand_time_slot (S, s)
- *
- * Generate a random time slot between s and S-1 where
- * S = Number of slots (0 -> S-1)
- * s = Current slot
- */
-int irlap_generate_rand_time_slot(int S, int s)
-{
- static int rand;
- int slot;
-
- IRDA_ASSERT((S - s) > 0, return 0;);
-
- rand += jiffies;
- rand ^= (rand << 12);
- rand ^= (rand >> 20);
-
- slot = s + rand % (S-s);
-
- IRDA_ASSERT((slot >= s) || (slot < S), return 0;);
-
- return slot;
-}
-
-/*
- * Function irlap_update_nr_received (nr)
- *
- * Remove all acknowledged frames in current window queue. This code is
- * not intuitive and you should not try to change it. If you think it
- * contains bugs, please mail a patch to the author instead.
- */
-void irlap_update_nr_received(struct irlap_cb *self, int nr)
-{
- struct sk_buff *skb = NULL;
- int count = 0;
-
- /*
- * Remove all the ack-ed frames from the window queue.
- */
-
- /*
- * Optimize for the common case. It is most likely that the receiver
- * will acknowledge all the frames we have sent! So in that case we
- * delete all frames stored in window.
- */
- if (nr == self->vs) {
- while ((skb = skb_dequeue(&self->wx_list)) != NULL) {
- dev_kfree_skb(skb);
- }
- /* The last acked frame is the next to send minus one */
- self->va = nr - 1;
- } else {
- /* Remove all acknowledged frames in current window */
- while ((skb_peek(&self->wx_list) != NULL) &&
- (((self->va+1) % 8) != nr))
- {
- skb = skb_dequeue(&self->wx_list);
- dev_kfree_skb(skb);
-
- self->va = (self->va + 1) % 8;
- count++;
- }
- }
-
- /* Advance window */
- self->window = self->window_size - skb_queue_len(&self->wx_list);
-}
-
-/*
- * Function irlap_validate_ns_received (ns)
- *
- * Validate the next to send (ns) field from received frame.
- */
-int irlap_validate_ns_received(struct irlap_cb *self, int ns)
-{
- /* ns as expected? */
- if (ns == self->vr)
- return NS_EXPECTED;
- /*
- * Stations are allowed to treat invalid NS as unexpected NS
- * IrLAP, Recv ... with-invalid-Ns. p. 84
- */
- return NS_UNEXPECTED;
-
- /* return NR_INVALID; */
-}
-/*
- * Function irlap_validate_nr_received (nr)
- *
- * Validate the next to receive (nr) field from received frame.
- *
- */
-int irlap_validate_nr_received(struct irlap_cb *self, int nr)
-{
- /* nr as expected? */
- if (nr == self->vs) {
- pr_debug("%s(), expected!\n", __func__);
- return NR_EXPECTED;
- }
-
- /*
- * unexpected nr? (but within current window), first we check if the
- * ns numbers of the frames in the current window wrap.
- */
- if (self->va < self->vs) {
- if ((nr >= self->va) && (nr <= self->vs))
- return NR_UNEXPECTED;
- } else {
- if ((nr >= self->va) || (nr <= self->vs))
- return NR_UNEXPECTED;
- }
-
- /* Invalid nr! */
- return NR_INVALID;
-}
-
-/*
- * Function irlap_initiate_connection_state ()
- *
- * Initialize the connection state parameters
- *
- */
-void irlap_initiate_connection_state(struct irlap_cb *self)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
- /* Next to send and next to receive */
- self->vs = self->vr = 0;
-
- /* Last frame which got acked (0 - 1) % 8 */
- self->va = 7;
-
- self->window = 1;
-
- self->remote_busy = FALSE;
- self->retry_count = 0;
-}
-
-/*
- * Function irlap_wait_min_turn_around (self, qos)
- *
- * Wait negotiated minimum turn around time, this function actually sets
- * the number of BOS's that must be sent before the next transmitted
- * frame in order to delay for the specified amount of time. This is
- * done to avoid using timers, and the forbidden udelay!
- */
-void irlap_wait_min_turn_around(struct irlap_cb *self, struct qos_info *qos)
-{
- __u32 min_turn_time;
- __u32 speed;
-
- /* Get QoS values. */
- speed = qos->baud_rate.value;
- min_turn_time = qos->min_turn_time.value;
-
- /* No need to calculate XBOFs for speeds over 115200 bps */
- if (speed > 115200) {
- self->mtt_required = min_turn_time;
- return;
- }
-
- /*
- * Send additional BOF's for the next frame for the requested
- * min turn time, so now we must calculate how many chars (XBOF's) we
- * must send for the requested time period (min turn time)
- */
- self->xbofs_delay = irlap_min_turn_time_in_bytes(speed, min_turn_time);
-}
-
-/*
- * Function irlap_flush_all_queues (void)
- *
- * Flush all queues
- *
- */
-void irlap_flush_all_queues(struct irlap_cb *self)
-{
- struct sk_buff* skb;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
- /* Free transmission queue */
- while ((skb = skb_dequeue(&self->txq)) != NULL)
- dev_kfree_skb(skb);
-
- while ((skb = skb_dequeue(&self->txq_ultra)) != NULL)
- dev_kfree_skb(skb);
-
- /* Free sliding window buffered packets */
- while ((skb = skb_dequeue(&self->wx_list)) != NULL)
- dev_kfree_skb(skb);
-}
-
-/*
- * Function irlap_setspeed (self, speed)
- *
- * Change the speed of the IrDA port
- *
- */
-static void irlap_change_speed(struct irlap_cb *self, __u32 speed, int now)
-{
- struct sk_buff *skb;
-
- pr_debug("%s(), setting speed to %d\n", __func__, speed);
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
- self->speed = speed;
-
- /* Change speed now, or just piggyback speed on frames */
- if (now) {
- /* Send down empty frame to trigger speed change */
- skb = alloc_skb(0, GFP_ATOMIC);
- if (skb)
- irlap_queue_xmit(self, skb);
- }
-}
-
-/*
- * Function irlap_init_qos_capabilities (self, qos)
- *
- * Initialize QoS for this IrLAP session, What we do is to compute the
- * intersection of the QoS capabilities for the user, driver and for
- * IrLAP itself. Normally, IrLAP will not specify any values, but it can
- * be used to restrict certain values.
- */
-static void irlap_init_qos_capabilities(struct irlap_cb *self,
- struct qos_info *qos_user)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
- IRDA_ASSERT(self->netdev != NULL, return;);
-
- /* Start out with the maximum QoS support possible */
- irda_init_max_qos_capabilies(&self->qos_rx);
-
- /* Apply drivers QoS capabilities */
- irda_qos_compute_intersection(&self->qos_rx, self->qos_dev);
-
- /*
- * Check for user supplied QoS parameters. The service user is only
- * allowed to supply these values. We check each parameter since the
- * user may not have set all of them.
- */
- if (qos_user) {
- pr_debug("%s(), Found user specified QoS!\n", __func__);
-
- if (qos_user->baud_rate.bits)
- self->qos_rx.baud_rate.bits &= qos_user->baud_rate.bits;
-
- if (qos_user->max_turn_time.bits)
- self->qos_rx.max_turn_time.bits &= qos_user->max_turn_time.bits;
- if (qos_user->data_size.bits)
- self->qos_rx.data_size.bits &= qos_user->data_size.bits;
-
- if (qos_user->link_disc_time.bits)
- self->qos_rx.link_disc_time.bits &= qos_user->link_disc_time.bits;
- }
-
- /* Use 500ms in IrLAP for now */
- self->qos_rx.max_turn_time.bits &= 0x01;
-
- /* Set data size */
- /*self->qos_rx.data_size.bits &= 0x03;*/
-
- irda_qos_bits_to_value(&self->qos_rx);
-}
-
-/*
- * Function irlap_apply_default_connection_parameters (void, now)
- *
- * Use the default connection and transmission parameters
- */
-void irlap_apply_default_connection_parameters(struct irlap_cb *self)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
- /* xbofs : Default value in NDM */
- self->next_bofs = 12;
- self->bofs_count = 12;
-
- /* NDM Speed is 9600 */
- irlap_change_speed(self, 9600, TRUE);
-
- /* Set mbusy when going to NDM state */
- irda_device_set_media_busy(self->netdev, TRUE);
-
- /*
- * Generate random connection address for this session, which must
- * be 7 bits wide and different from 0x00 and 0xfe
- */
- while ((self->caddr == 0x00) || (self->caddr == 0xfe)) {
- get_random_bytes(&self->caddr, sizeof(self->caddr));
- self->caddr &= 0xfe;
- }
-
- /* Use default values until connection has been negitiated */
- self->slot_timeout = sysctl_slot_timeout;
- self->final_timeout = FINAL_TIMEOUT;
- self->poll_timeout = POLL_TIMEOUT;
- self->wd_timeout = WD_TIMEOUT;
-
- /* Set some default values */
- self->qos_tx.baud_rate.value = 9600;
- self->qos_rx.baud_rate.value = 9600;
- self->qos_tx.max_turn_time.value = 0;
- self->qos_rx.max_turn_time.value = 0;
- self->qos_tx.min_turn_time.value = 0;
- self->qos_rx.min_turn_time.value = 0;
- self->qos_tx.data_size.value = 64;
- self->qos_rx.data_size.value = 64;
- self->qos_tx.window_size.value = 1;
- self->qos_rx.window_size.value = 1;
- self->qos_tx.additional_bofs.value = 12;
- self->qos_rx.additional_bofs.value = 12;
- self->qos_tx.link_disc_time.value = 0;
- self->qos_rx.link_disc_time.value = 0;
-
- irlap_flush_all_queues(self);
-
- self->disconnect_pending = FALSE;
- self->connect_pending = FALSE;
-}
-
-/*
- * Function irlap_apply_connection_parameters (qos, now)
- *
- * Initialize IrLAP with the negotiated QoS values
- *
- * If 'now' is false, the speed and xbofs will be changed after the next
- * frame is sent.
- * If 'now' is true, the speed and xbofs is changed immediately
- */
-void irlap_apply_connection_parameters(struct irlap_cb *self, int now)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
- /* Set the negotiated xbofs value */
- self->next_bofs = self->qos_tx.additional_bofs.value;
- if (now)
- self->bofs_count = self->next_bofs;
-
- /* Set the negotiated link speed (may need the new xbofs value) */
- irlap_change_speed(self, self->qos_tx.baud_rate.value, now);
-
- self->window_size = self->qos_tx.window_size.value;
- self->window = self->qos_tx.window_size.value;
-
-#ifdef CONFIG_IRDA_DYNAMIC_WINDOW
- /*
- * Calculate how many bytes it is possible to transmit before the
- * link must be turned around
- */
- self->line_capacity =
- irlap_max_line_capacity(self->qos_tx.baud_rate.value,
- self->qos_tx.max_turn_time.value);
- self->bytes_left = self->line_capacity;
-#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */
-
-
- /*
- * Initialize timeout values, some of the rules are listed on
- * page 92 in IrLAP.
- */
- IRDA_ASSERT(self->qos_tx.max_turn_time.value != 0, return;);
- IRDA_ASSERT(self->qos_rx.max_turn_time.value != 0, return;);
- /* The poll timeout applies only to the primary station.
- * It defines the maximum time the primary stay in XMIT mode
- * before timeout and turning the link around (sending a RR).
- * Or, this is how much we can keep the pf bit in primary mode.
- * Therefore, it must be lower or equal than our *OWN* max turn around.
- * Jean II */
- self->poll_timeout = msecs_to_jiffies(
- self->qos_tx.max_turn_time.value);
- /* The Final timeout applies only to the primary station.
- * It defines the maximum time the primary wait (mostly in RECV mode)
- * for an answer from the secondary station before polling it again.
- * Therefore, it must be greater or equal than our *PARTNER*
- * max turn around time - Jean II */
- self->final_timeout = msecs_to_jiffies(
- self->qos_rx.max_turn_time.value);
- /* The Watchdog Bit timeout applies only to the secondary station.
- * It defines the maximum time the secondary wait (mostly in RECV mode)
- * for poll from the primary station before getting annoyed.
- * Therefore, it must be greater or equal than our *PARTNER*
- * max turn around time - Jean II */
- self->wd_timeout = self->final_timeout * 2;
-
- /*
- * N1 and N2 are maximum retry count for *both* the final timer
- * and the wd timer (with a factor 2) as defined above.
- * After N1 retry of a timer, we give a warning to the user.
- * After N2 retry, we consider the link dead and disconnect it.
- * Jean II
- */
-
- /*
- * Set N1 to 0 if Link Disconnect/Threshold Time = 3 and set it to
- * 3 seconds otherwise. See page 71 in IrLAP for more details.
- * Actually, it's not always 3 seconds, as we allow to set
- * it via sysctl... Max maxtt is 500ms, and N1 need to be multiple
- * of 2, so 1 second is minimum we can allow. - Jean II
- */
- if (self->qos_tx.link_disc_time.value == sysctl_warn_noreply_time)
- /*
- * If we set N1 to 0, it will trigger immediately, which is
- * not what we want. What we really want is to disable it,
- * Jean II
- */
- self->N1 = -2; /* Disable - Need to be multiple of 2*/
- else
- self->N1 = sysctl_warn_noreply_time * 1000 /
- self->qos_rx.max_turn_time.value;
-
- pr_debug("Setting N1 = %d\n", self->N1);
-
- /* Set N2 to match our own disconnect time */
- self->N2 = self->qos_tx.link_disc_time.value * 1000 /
- self->qos_rx.max_turn_time.value;
- pr_debug("Setting N2 = %d\n", self->N2);
-}
-
-#ifdef CONFIG_PROC_FS
-struct irlap_iter_state {
- int id;
-};
-
-static void *irlap_seq_start(struct seq_file *seq, loff_t *pos)
-{
- struct irlap_iter_state *iter = seq->private;
- struct irlap_cb *self;
-
- /* Protect our access to the tsap list */
- spin_lock_irq(&irlap->hb_spinlock);
- iter->id = 0;
-
- for (self = (struct irlap_cb *) hashbin_get_first(irlap);
- self; self = (struct irlap_cb *) hashbin_get_next(irlap)) {
- if (iter->id == *pos)
- break;
- ++iter->id;
- }
-
- return self;
-}
-
-static void *irlap_seq_next(struct seq_file *seq, void *v, loff_t *pos)
-{
- struct irlap_iter_state *iter = seq->private;
-
- ++*pos;
- ++iter->id;
- return (void *) hashbin_get_next(irlap);
-}
-
-static void irlap_seq_stop(struct seq_file *seq, void *v)
-{
- spin_unlock_irq(&irlap->hb_spinlock);
-}
-
-static int irlap_seq_show(struct seq_file *seq, void *v)
-{
- const struct irlap_iter_state *iter = seq->private;
- const struct irlap_cb *self = v;
-
- IRDA_ASSERT(self->magic == LAP_MAGIC, return -EINVAL;);
-
- seq_printf(seq, "irlap%d ", iter->id);
- seq_printf(seq, "state: %s\n",
- irlap_state[self->state]);
-
- seq_printf(seq, " device name: %s, ",
- (self->netdev) ? self->netdev->name : "bug");
- seq_printf(seq, "hardware name: %s\n", self->hw_name);
-
- seq_printf(seq, " caddr: %#02x, ", self->caddr);
- seq_printf(seq, "saddr: %#08x, ", self->saddr);
- seq_printf(seq, "daddr: %#08x\n", self->daddr);
-
- seq_printf(seq, " win size: %d, ",
- self->window_size);
- seq_printf(seq, "win: %d, ", self->window);
-#ifdef CONFIG_IRDA_DYNAMIC_WINDOW
- seq_printf(seq, "line capacity: %d, ",
- self->line_capacity);
- seq_printf(seq, "bytes left: %d\n", self->bytes_left);
-#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */
- seq_printf(seq, " tx queue len: %d ",
- skb_queue_len(&self->txq));
- seq_printf(seq, "win queue len: %d ",
- skb_queue_len(&self->wx_list));
- seq_printf(seq, "rbusy: %s", self->remote_busy ?
- "TRUE" : "FALSE");
- seq_printf(seq, " mbusy: %s\n", self->media_busy ?
- "TRUE" : "FALSE");
-
- seq_printf(seq, " retrans: %d ", self->retry_count);
- seq_printf(seq, "vs: %d ", self->vs);
- seq_printf(seq, "vr: %d ", self->vr);
- seq_printf(seq, "va: %d\n", self->va);
-
- seq_printf(seq, " qos\tbps\tmaxtt\tdsize\twinsize\taddbofs\tmintt\tldisc\tcomp\n");
-
- seq_printf(seq, " tx\t%d\t",
- self->qos_tx.baud_rate.value);
- seq_printf(seq, "%d\t",
- self->qos_tx.max_turn_time.value);
- seq_printf(seq, "%d\t",
- self->qos_tx.data_size.value);
- seq_printf(seq, "%d\t",
- self->qos_tx.window_size.value);
- seq_printf(seq, "%d\t",
- self->qos_tx.additional_bofs.value);
- seq_printf(seq, "%d\t",
- self->qos_tx.min_turn_time.value);
- seq_printf(seq, "%d\t",
- self->qos_tx.link_disc_time.value);
- seq_printf(seq, "\n");
-
- seq_printf(seq, " rx\t%d\t",
- self->qos_rx.baud_rate.value);
- seq_printf(seq, "%d\t",
- self->qos_rx.max_turn_time.value);
- seq_printf(seq, "%d\t",
- self->qos_rx.data_size.value);
- seq_printf(seq, "%d\t",
- self->qos_rx.window_size.value);
- seq_printf(seq, "%d\t",
- self->qos_rx.additional_bofs.value);
- seq_printf(seq, "%d\t",
- self->qos_rx.min_turn_time.value);
- seq_printf(seq, "%d\n",
- self->qos_rx.link_disc_time.value);
-
- return 0;
-}
-
-static const struct seq_operations irlap_seq_ops = {
- .start = irlap_seq_start,
- .next = irlap_seq_next,
- .stop = irlap_seq_stop,
- .show = irlap_seq_show,
-};
-
-static int irlap_seq_open(struct inode *inode, struct file *file)
-{
- if (irlap == NULL)
- return -EINVAL;
-
- return seq_open_private(file, &irlap_seq_ops,
- sizeof(struct irlap_iter_state));
-}
-
-const struct file_operations irlap_seq_fops = {
- .owner = THIS_MODULE,
- .open = irlap_seq_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = seq_release_private,
-};
-
-#endif /* CONFIG_PROC_FS */
diff --git a/drivers/staging/irda/net/irlap_event.c b/drivers/staging/irda/net/irlap_event.c
deleted file mode 100644
index 634188b07e0a..000000000000
--- a/drivers/staging/irda/net/irlap_event.c
+++ /dev/null
@@ -1,2316 +0,0 @@
-/*********************************************************************
- *
- * Filename: irlap_event.c
- * Version: 0.9
- * Description: IrLAP state machine implementation
- * Status: Experimental.
- * Author: Dag Brattli <dag@brattli.net>
- * Created at: Sat Aug 16 00:59:29 1997
- * Modified at: Sat Dec 25 21:07:57 1999
- * Modified by: Dag Brattli <dag@brattli.net>
- *
- * Copyright (c) 1998-2000 Dag Brattli <dag@brattli.net>,
- * Copyright (c) 1998 Thomas Davis <ratbert@radiks.net>
- * All Rights Reserved.
- * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/skbuff.h>
-#include <linux/slab.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irlap_event.h>
-
-#include <net/irda/timer.h>
-#include <net/irda/irlap.h>
-#include <net/irda/irlap_frame.h>
-#include <net/irda/qos.h>
-#include <net/irda/parameters.h>
-#include <net/irda/irlmp.h> /* irlmp_flow_indication(), ... */
-
-#include <net/irda/irda_device.h>
-
-#ifdef CONFIG_IRDA_FAST_RR
-int sysctl_fast_poll_increase = 50;
-#endif
-
-static int irlap_state_ndm (struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_query (struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_reply (struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_conn (struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_setup (struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_offline(struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_xmit_p (struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_pclose (struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_nrm_p (struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_reset_wait(struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_reset (struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_nrm_s (struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_xmit_s (struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_sclose (struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_reset_check(struct irlap_cb *, IRLAP_EVENT event,
- struct sk_buff *, struct irlap_info *);
-
-static const char *const irlap_event[] __maybe_unused = {
- "DISCOVERY_REQUEST",
- "CONNECT_REQUEST",
- "CONNECT_RESPONSE",
- "DISCONNECT_REQUEST",
- "DATA_REQUEST",
- "RESET_REQUEST",
- "RESET_RESPONSE",
- "SEND_I_CMD",
- "SEND_UI_FRAME",
- "RECV_DISCOVERY_XID_CMD",
- "RECV_DISCOVERY_XID_RSP",
- "RECV_SNRM_CMD",
- "RECV_TEST_CMD",
- "RECV_TEST_RSP",
- "RECV_UA_RSP",
- "RECV_DM_RSP",
- "RECV_RD_RSP",
- "RECV_I_CMD",
- "RECV_I_RSP",
- "RECV_UI_FRAME",
- "RECV_FRMR_RSP",
- "RECV_RR_CMD",
- "RECV_RR_RSP",
- "RECV_RNR_CMD",
- "RECV_RNR_RSP",
- "RECV_REJ_CMD",
- "RECV_REJ_RSP",
- "RECV_SREJ_CMD",
- "RECV_SREJ_RSP",
- "RECV_DISC_CMD",
- "SLOT_TIMER_EXPIRED",
- "QUERY_TIMER_EXPIRED",
- "FINAL_TIMER_EXPIRED",
- "POLL_TIMER_EXPIRED",
- "DISCOVERY_TIMER_EXPIRED",
- "WD_TIMER_EXPIRED",
- "BACKOFF_TIMER_EXPIRED",
- "MEDIA_BUSY_TIMER_EXPIRED",
-};
-
-const char *const irlap_state[] = {
- "LAP_NDM",
- "LAP_QUERY",
- "LAP_REPLY",
- "LAP_CONN",
- "LAP_SETUP",
- "LAP_OFFLINE",
- "LAP_XMIT_P",
- "LAP_PCLOSE",
- "LAP_NRM_P",
- "LAP_RESET_WAIT",
- "LAP_RESET",
- "LAP_NRM_S",
- "LAP_XMIT_S",
- "LAP_SCLOSE",
- "LAP_RESET_CHECK",
-};
-
-static int (*state[])(struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info) =
-{
- irlap_state_ndm,
- irlap_state_query,
- irlap_state_reply,
- irlap_state_conn,
- irlap_state_setup,
- irlap_state_offline,
- irlap_state_xmit_p,
- irlap_state_pclose,
- irlap_state_nrm_p,
- irlap_state_reset_wait,
- irlap_state_reset,
- irlap_state_nrm_s,
- irlap_state_xmit_s,
- irlap_state_sclose,
- irlap_state_reset_check,
-};
-
-/*
- * Function irda_poll_timer_expired (data)
- *
- * Poll timer has expired. Normally we must now send a RR frame to the
- * remote device
- */
-static void irlap_poll_timer_expired(struct timer_list *t)
-{
- struct irlap_cb *self = from_timer(self, t, poll_timer);
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
- irlap_do_event(self, POLL_TIMER_EXPIRED, NULL, NULL);
-}
-
-/*
- * Calculate and set time before we will have to send back the pf bit
- * to the peer. Use in primary.
- * Make sure that state is XMIT_P/XMIT_S when calling this function
- * (and that nobody messed up with the state). - Jean II
- */
-static void irlap_start_poll_timer(struct irlap_cb *self, int timeout)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-#ifdef CONFIG_IRDA_FAST_RR
- /*
- * Send out the RR frames faster if our own transmit queue is empty, or
- * if the peer is busy. The effect is a much faster conversation
- */
- if (skb_queue_empty(&self->txq) || self->remote_busy) {
- if (self->fast_RR == TRUE) {
- /*
- * Assert that the fast poll timer has not reached the
- * normal poll timer yet
- */
- if (self->fast_RR_timeout < timeout) {
- /*
- * FIXME: this should be a more configurable
- * function
- */
- self->fast_RR_timeout +=
- (sysctl_fast_poll_increase * HZ/1000);
-
- /* Use this fast(er) timeout instead */
- timeout = self->fast_RR_timeout;
- }
- } else {
- self->fast_RR = TRUE;
-
- /* Start with just 0 ms */
- self->fast_RR_timeout = 0;
- timeout = 0;
- }
- } else
- self->fast_RR = FALSE;
-
- pr_debug("%s(), timeout=%d (%ld)\n", __func__, timeout, jiffies);
-#endif /* CONFIG_IRDA_FAST_RR */
-
- if (timeout == 0)
- irlap_do_event(self, POLL_TIMER_EXPIRED, NULL, NULL);
- else
- irda_start_timer(&self->poll_timer, timeout,
- irlap_poll_timer_expired);
-}
-
-/*
- * Function irlap_do_event (event, skb, info)
- *
- * Rushes through the state machine without any delay. If state == XMIT
- * then send queued data frames.
- */
-void irlap_do_event(struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info)
-{
- int ret;
-
- if (!self || self->magic != LAP_MAGIC)
- return;
-
- pr_debug("%s(), event = %s, state = %s\n", __func__,
- irlap_event[event], irlap_state[self->state]);
-
- ret = (*state[self->state])(self, event, skb, info);
-
- /*
- * Check if there are any pending events that needs to be executed
- */
- switch (self->state) {
- case LAP_XMIT_P: /* FALLTHROUGH */
- case LAP_XMIT_S:
- /*
- * We just received the pf bit and are at the beginning
- * of a new LAP transmit window.
- * Check if there are any queued data frames, and do not
- * try to disconnect link if we send any data frames, since
- * that will change the state away form XMIT
- */
- pr_debug("%s() : queue len = %d\n", __func__,
- skb_queue_len(&self->txq));
-
- if (!skb_queue_empty(&self->txq)) {
- /* Prevent race conditions with irlap_data_request() */
- self->local_busy = TRUE;
-
- /* Theory of operation.
- * We send frames up to when we fill the window or
- * reach line capacity. Those frames will queue up
- * in the device queue, and the driver will slowly
- * send them.
- * After each frame that we send, we poll the higher
- * layer for more data. It's the right time to do
- * that because the link layer need to perform the mtt
- * and then send the first frame, so we can afford
- * to send a bit of time in kernel space.
- * The explicit flow indication allow to minimise
- * buffers (== lower latency), to avoid higher layer
- * polling via timers (== less context switches) and
- * to implement a crude scheduler - Jean II */
-
- /* Try to send away all queued data frames */
- while ((skb = skb_dequeue(&self->txq)) != NULL) {
- /* Send one frame */
- ret = (*state[self->state])(self, SEND_I_CMD,
- skb, NULL);
- /* Drop reference count.
- * It will be increase as needed in
- * irlap_send_data_xxx() */
- kfree_skb(skb);
-
- /* Poll the higher layers for one more frame */
- irlmp_flow_indication(self->notify.instance,
- FLOW_START);
-
- if (ret == -EPROTO)
- break; /* Try again later! */
- }
- /* Finished transmitting */
- self->local_busy = FALSE;
- } else if (self->disconnect_pending) {
- self->disconnect_pending = FALSE;
-
- ret = (*state[self->state])(self, DISCONNECT_REQUEST,
- NULL, NULL);
- }
- break;
-/* case LAP_NDM: */
-/* case LAP_CONN: */
-/* case LAP_RESET_WAIT: */
-/* case LAP_RESET_CHECK: */
- default:
- break;
- }
-}
-
-/*
- * Function irlap_state_ndm (event, skb, frame)
- *
- * NDM (Normal Disconnected Mode) state
- *
- */
-static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info)
-{
- discovery_t *discovery_rsp;
- int ret = 0;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
- switch (event) {
- case CONNECT_REQUEST:
- IRDA_ASSERT(self->netdev != NULL, return -1;);
-
- if (self->media_busy) {
- /* Note : this will never happen, because we test
- * media busy in irlap_connect_request() and
- * postpone the event... - Jean II */
- pr_debug("%s(), CONNECT_REQUEST: media busy!\n",
- __func__);
-
- /* Always switch state before calling upper layers */
- irlap_next_state(self, LAP_NDM);
-
- irlap_disconnect_indication(self, LAP_MEDIA_BUSY);
- } else {
- irlap_send_snrm_frame(self, &self->qos_rx);
-
- /* Start Final-bit timer */
- irlap_start_final_timer(self, self->final_timeout);
-
- self->retry_count = 0;
- irlap_next_state(self, LAP_SETUP);
- }
- break;
- case RECV_SNRM_CMD:
- /* Check if the frame contains and I field */
- if (info) {
- self->daddr = info->daddr;
- self->caddr = info->caddr;
-
- irlap_next_state(self, LAP_CONN);
-
- irlap_connect_indication(self, skb);
- } else {
- pr_debug("%s(), SNRM frame does not contain an I field!\n",
- __func__);
- }
- break;
- case DISCOVERY_REQUEST:
- IRDA_ASSERT(info != NULL, return -1;);
-
- if (self->media_busy) {
- pr_debug("%s(), DISCOVERY_REQUEST: media busy!\n",
- __func__);
- /* irlap->log.condition = MEDIA_BUSY; */
-
- /* This will make IrLMP try again */
- irlap_discovery_confirm(self, NULL);
- /* Note : the discovery log is not cleaned up here,
- * it will be done in irlap_discovery_request()
- * Jean II */
- return 0;
- }
-
- self->S = info->S;
- self->s = info->s;
- irlap_send_discovery_xid_frame(self, info->S, info->s, TRUE,
- info->discovery);
- self->frame_sent = FALSE;
- self->s++;
-
- irlap_start_slot_timer(self, self->slot_timeout);
- irlap_next_state(self, LAP_QUERY);
- break;
- case RECV_DISCOVERY_XID_CMD:
- IRDA_ASSERT(info != NULL, return -1;);
-
- /* Assert that this is not the final slot */
- if (info->s <= info->S) {
- self->slot = irlap_generate_rand_time_slot(info->S,
- info->s);
- if (self->slot == info->s) {
- discovery_rsp = irlmp_get_discovery_response();
- discovery_rsp->data.daddr = info->daddr;
-
- irlap_send_discovery_xid_frame(self, info->S,
- self->slot,
- FALSE,
- discovery_rsp);
- self->frame_sent = TRUE;
- } else
- self->frame_sent = FALSE;
-
- /*
- * Go to reply state until end of discovery to
- * inhibit our own transmissions. Set the timer
- * to not stay forever there... Jean II
- */
- irlap_start_query_timer(self, info->S, info->s);
- irlap_next_state(self, LAP_REPLY);
- } else {
- /* This is the final slot. How is it possible ?
- * This would happen is both discoveries are just slightly
- * offset (if they are in sync, all packets are lost).
- * Most often, all the discovery requests will be received
- * in QUERY state (see my comment there), except for the
- * last frame that will come here.
- * The big trouble when it happen is that active discovery
- * doesn't happen, because nobody answer the discoveries
- * frame of the other guy, so the log shows up empty.
- * What should we do ?
- * Not much. It's too late to answer those discovery frames,
- * so we just pass the info to IrLMP who will put it in the
- * log (and post an event).
- * Another cause would be devices that do discovery much
- * slower than us, however the latest fixes should minimise
- * those cases...
- * Jean II
- */
- pr_debug("%s(), Receiving final discovery request, missed the discovery slots :-(\n",
- __func__);
-
- /* Last discovery request -> in the log */
- irlap_discovery_indication(self, info->discovery);
- }
- break;
- case MEDIA_BUSY_TIMER_EXPIRED:
- /* A bunch of events may be postponed because the media is
- * busy (usually immediately after we close a connection),
- * or while we are doing discovery (state query/reply).
- * In all those cases, the media busy flag will be cleared
- * when it's OK for us to process those postponed events.
- * This event is not mentioned in the state machines in the
- * IrLAP spec. It's because they didn't consider Ultra and
- * postponing connection request is optional.
- * Jean II */
-#ifdef CONFIG_IRDA_ULTRA
- /* Send any pending Ultra frames if any */
- if (!skb_queue_empty(&self->txq_ultra)) {
- /* We don't send the frame, just post an event.
- * Also, previously this code was in timer.c...
- * Jean II */
- ret = (*state[self->state])(self, SEND_UI_FRAME,
- NULL, NULL);
- }
-#endif /* CONFIG_IRDA_ULTRA */
- /* Check if we should try to connect.
- * This code was previously in irlap_do_event() */
- if (self->connect_pending) {
- self->connect_pending = FALSE;
-
- /* This one *should* not pend in this state, except
- * if a socket try to connect and immediately
- * disconnect. - clear - Jean II */
- if (self->disconnect_pending)
- irlap_disconnect_indication(self, LAP_DISC_INDICATION);
- else
- ret = (*state[self->state])(self,
- CONNECT_REQUEST,
- NULL, NULL);
- self->disconnect_pending = FALSE;
- }
- /* Note : one way to test if this code works well (including
- * media busy and small busy) is to create a user space
- * application generating an Ultra packet every 3.05 sec (or
- * 2.95 sec) and to see how it interact with discovery.
- * It's fairly easy to check that no packet is lost, that the
- * packets are postponed during discovery and that after
- * discovery indication you have a 100ms "gap".
- * As connection request and Ultra are now processed the same
- * way, this avoid the tedious job of trying IrLAP connection
- * in all those cases...
- * Jean II */
- break;
-#ifdef CONFIG_IRDA_ULTRA
- case SEND_UI_FRAME:
- {
- int i;
- /* Only allowed to repeat an operation twice */
- for (i=0; ((i<2) && (self->media_busy == FALSE)); i++) {
- skb = skb_dequeue(&self->txq_ultra);
- if (skb)
- irlap_send_ui_frame(self, skb, CBROADCAST,
- CMD_FRAME);
- else
- break;
- /* irlap_send_ui_frame() won't increase skb reference
- * count, so no dev_kfree_skb() - Jean II */
- }
- if (i == 2) {
- /* Force us to listen 500 ms again */
- irda_device_set_media_busy(self->netdev, TRUE);
- }
- break;
- }
- case RECV_UI_FRAME:
- /* Only accept broadcast frames in NDM mode */
- if (info->caddr != CBROADCAST) {
- pr_debug("%s(), not a broadcast frame!\n",
- __func__);
- } else
- irlap_unitdata_indication(self, skb);
- break;
-#endif /* CONFIG_IRDA_ULTRA */
- case RECV_TEST_CMD:
- /* Remove test frame header */
- skb_pull(skb, sizeof(struct test_frame));
-
- /*
- * Send response. This skb will not be sent out again, and
- * will only be used to send out the same info as the cmd
- */
- irlap_send_test_frame(self, CBROADCAST, info->daddr, skb);
- break;
- case RECV_TEST_RSP:
- pr_debug("%s() not implemented!\n", __func__);
- break;
- default:
- pr_debug("%s(), Unknown event %s\n", __func__,
- irlap_event[event]);
-
- ret = -1;
- break;
- }
- return ret;
-}
-
-/*
- * Function irlap_state_query (event, skb, info)
- *
- * QUERY state
- *
- */
-static int irlap_state_query(struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info)
-{
- int ret = 0;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
- switch (event) {
- case RECV_DISCOVERY_XID_RSP:
- IRDA_ASSERT(info != NULL, return -1;);
- IRDA_ASSERT(info->discovery != NULL, return -1;);
-
- pr_debug("%s(), daddr=%08x\n", __func__,
- info->discovery->data.daddr);
-
- if (!self->discovery_log) {
- net_warn_ratelimited("%s: discovery log is gone! maybe the discovery timeout has been set too short?\n",
- __func__);
- break;
- }
- hashbin_insert(self->discovery_log,
- (irda_queue_t *) info->discovery,
- info->discovery->data.daddr, NULL);
-
- /* Keep state */
- /* irlap_next_state(self, LAP_QUERY); */
-
- break;
- case RECV_DISCOVERY_XID_CMD:
- /* Yes, it is possible to receive those frames in this mode.
- * Note that most often the last discovery request won't
- * occur here but in NDM state (see my comment there).
- * What should we do ?
- * Not much. We are currently performing our own discovery,
- * therefore we can't answer those frames. We don't want
- * to change state either. We just pass the info to
- * IrLMP who will put it in the log (and post an event).
- * Jean II
- */
-
- IRDA_ASSERT(info != NULL, return -1;);
-
- pr_debug("%s(), Receiving discovery request (s = %d) while performing discovery :-(\n",
- __func__, info->s);
-
- /* Last discovery request ? */
- if (info->s == 0xff)
- irlap_discovery_indication(self, info->discovery);
- break;
- case SLOT_TIMER_EXPIRED:
- /*
- * Wait a little longer if we detect an incoming frame. This
- * is not mentioned in the spec, but is a good thing to do,
- * since we want to work even with devices that violate the
- * timing requirements.
- */
- if (irda_device_is_receiving(self->netdev) && !self->add_wait) {
- pr_debug("%s(), device is slow to answer, waiting some more!\n",
- __func__);
- irlap_start_slot_timer(self, msecs_to_jiffies(10));
- self->add_wait = TRUE;
- return ret;
- }
- self->add_wait = FALSE;
-
- if (self->s < self->S) {
- irlap_send_discovery_xid_frame(self, self->S,
- self->s, TRUE,
- self->discovery_cmd);
- self->s++;
- irlap_start_slot_timer(self, self->slot_timeout);
-
- /* Keep state */
- irlap_next_state(self, LAP_QUERY);
- } else {
- /* This is the final slot! */
- irlap_send_discovery_xid_frame(self, self->S, 0xff,
- TRUE,
- self->discovery_cmd);
-
- /* Always switch state before calling upper layers */
- irlap_next_state(self, LAP_NDM);
-
- /*
- * We are now finished with the discovery procedure,
- * so now we must return the results
- */
- irlap_discovery_confirm(self, self->discovery_log);
-
- /* IrLMP should now have taken care of the log */
- self->discovery_log = NULL;
- }
- break;
- default:
- pr_debug("%s(), Unknown event %s\n", __func__,
- irlap_event[event]);
-
- ret = -1;
- break;
- }
- return ret;
-}
-
-/*
- * Function irlap_state_reply (self, event, skb, info)
- *
- * REPLY, we have received a XID discovery frame from a device and we
- * are waiting for the right time slot to send a response XID frame
- *
- */
-static int irlap_state_reply(struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info)
-{
- discovery_t *discovery_rsp;
- int ret=0;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
- switch (event) {
- case QUERY_TIMER_EXPIRED:
- pr_debug("%s(), QUERY_TIMER_EXPIRED <%ld>\n",
- __func__, jiffies);
- irlap_next_state(self, LAP_NDM);
- break;
- case RECV_DISCOVERY_XID_CMD:
- IRDA_ASSERT(info != NULL, return -1;);
- /* Last frame? */
- if (info->s == 0xff) {
- del_timer(&self->query_timer);
-
- /* info->log.condition = REMOTE; */
-
- /* Always switch state before calling upper layers */
- irlap_next_state(self, LAP_NDM);
-
- irlap_discovery_indication(self, info->discovery);
- } else {
- /* If it's our slot, send our reply */
- if ((info->s >= self->slot) && (!self->frame_sent)) {
- discovery_rsp = irlmp_get_discovery_response();
- discovery_rsp->data.daddr = info->daddr;
-
- irlap_send_discovery_xid_frame(self, info->S,
- self->slot,
- FALSE,
- discovery_rsp);
-
- self->frame_sent = TRUE;
- }
- /* Readjust our timer to accommodate devices
- * doing faster or slower discovery than us...
- * Jean II */
- irlap_start_query_timer(self, info->S, info->s);
-
- /* Keep state */
- //irlap_next_state(self, LAP_REPLY);
- }
- break;
- default:
- pr_debug("%s(), Unknown event %d, %s\n", __func__,
- event, irlap_event[event]);
-
- ret = -1;
- break;
- }
- return ret;
-}
-
-/*
- * Function irlap_state_conn (event, skb, info)
- *
- * CONN, we have received a SNRM command and is waiting for the upper
- * layer to accept or refuse connection
- *
- */
-static int irlap_state_conn(struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info)
-{
- int ret = 0;
-
- pr_debug("%s(), event=%s\n", __func__, irlap_event[event]);
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
- switch (event) {
- case CONNECT_RESPONSE:
- skb_pull(skb, sizeof(struct snrm_frame));
-
- IRDA_ASSERT(self->netdev != NULL, return -1;);
-
- irlap_qos_negotiate(self, skb);
-
- irlap_initiate_connection_state(self);
-
- /*
- * Applying the parameters now will make sure we change speed
- * *after* we have sent the next frame
- */
- irlap_apply_connection_parameters(self, FALSE);
-
- /*
- * Sending this frame will force a speed change after it has
- * been sent (i.e. the frame will be sent at 9600).
- */
- irlap_send_ua_response_frame(self, &self->qos_rx);
-
-#if 0
- /*
- * We are allowed to send two frames, but this may increase
- * the connect latency, so lets not do it for now.
- */
- /* This is full of good intentions, but doesn't work in
- * practice.
- * After sending the first UA response, we switch the
- * dongle to the negotiated speed, which is usually
- * different than 9600 kb/s.
- * From there, there is two solutions :
- * 1) The other end has received the first UA response :
- * it will set up the connection, move to state LAP_NRM_P,
- * and will ignore and drop the second UA response.
- * Actually, it's even worse : the other side will almost
- * immediately send a RR that will likely collide with the
- * UA response (depending on negotiated turnaround).
- * 2) The other end has not received the first UA response,
- * will stay at 9600 and will never see the second UA response.
- * Jean II */
- irlap_send_ua_response_frame(self, &self->qos_rx);
-#endif
-
- /*
- * The WD-timer could be set to the duration of the P-timer
- * for this case, but it is recommended to use twice the
- * value (note 3 IrLAP p. 60).
- */
- irlap_start_wd_timer(self, self->wd_timeout);
- irlap_next_state(self, LAP_NRM_S);
-
- break;
- case RECV_DISCOVERY_XID_CMD:
- pr_debug("%s(), event RECV_DISCOVER_XID_CMD!\n",
- __func__);
- irlap_next_state(self, LAP_NDM);
-
- break;
- case DISCONNECT_REQUEST:
- pr_debug("%s(), Disconnect request!\n", __func__);
- irlap_send_dm_frame(self);
- irlap_next_state( self, LAP_NDM);
- irlap_disconnect_indication(self, LAP_DISC_INDICATION);
- break;
- default:
- pr_debug("%s(), Unknown event %d, %s\n", __func__,
- event, irlap_event[event]);
-
- ret = -1;
- break;
- }
-
- return ret;
-}
-
-/*
- * Function irlap_state_setup (event, skb, frame)
- *
- * SETUP state, The local layer has transmitted a SNRM command frame to
- * a remote peer layer and is awaiting a reply .
- *
- */
-static int irlap_state_setup(struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info)
-{
- int ret = 0;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
- switch (event) {
- case FINAL_TIMER_EXPIRED:
- if (self->retry_count < self->N3) {
-/*
- * Perform random backoff, Wait a random number of time units, minimum
- * duration half the time taken to transmitt a SNRM frame, maximum duration
- * 1.5 times the time taken to transmit a SNRM frame. So this time should
- * between 15 msecs and 45 msecs.
- */
- irlap_start_backoff_timer(self, msecs_to_jiffies(20 +
- (jiffies % 30)));
- } else {
- /* Always switch state before calling upper layers */
- irlap_next_state(self, LAP_NDM);
-
- irlap_disconnect_indication(self, LAP_FOUND_NONE);
- }
- break;
- case BACKOFF_TIMER_EXPIRED:
- irlap_send_snrm_frame(self, &self->qos_rx);
- irlap_start_final_timer(self, self->final_timeout);
- self->retry_count++;
- break;
- case RECV_SNRM_CMD:
- pr_debug("%s(), SNRM battle!\n", __func__);
-
- IRDA_ASSERT(skb != NULL, return 0;);
- IRDA_ASSERT(info != NULL, return 0;);
-
- /*
- * The device with the largest device address wins the battle
- * (both have sent a SNRM command!)
- */
- if (info &&(info->daddr > self->saddr)) {
- del_timer(&self->final_timer);
- irlap_initiate_connection_state(self);
-
- IRDA_ASSERT(self->netdev != NULL, return -1;);
-
- skb_pull(skb, sizeof(struct snrm_frame));
-
- irlap_qos_negotiate(self, skb);
-
- /* Send UA frame and then change link settings */
- irlap_apply_connection_parameters(self, FALSE);
- irlap_send_ua_response_frame(self, &self->qos_rx);
-
- irlap_next_state(self, LAP_NRM_S);
- irlap_connect_confirm(self, skb);
-
- /*
- * The WD-timer could be set to the duration of the
- * P-timer for this case, but it is recommended
- * to use twice the value (note 3 IrLAP p. 60).
- */
- irlap_start_wd_timer(self, self->wd_timeout);
- } else {
- /* We just ignore the other device! */
- irlap_next_state(self, LAP_SETUP);
- }
- break;
- case RECV_UA_RSP:
- /* Stop F-timer */
- del_timer(&self->final_timer);
-
- /* Initiate connection state */
- irlap_initiate_connection_state(self);
-
- /* Negotiate connection parameters */
- IRDA_ASSERT(skb->len > 10, return -1;);
-
- skb_pull(skb, sizeof(struct ua_frame));
-
- IRDA_ASSERT(self->netdev != NULL, return -1;);
-
- irlap_qos_negotiate(self, skb);
-
- /* Set the new link setting *now* (before the rr frame) */
- irlap_apply_connection_parameters(self, TRUE);
- self->retry_count = 0;
-
- /* Wait for turnaround time to give a chance to the other
- * device to be ready to receive us.
- * Note : the time to switch speed is typically larger
- * than the turnaround time, but as we don't have the other
- * side speed switch time, that's our best guess...
- * Jean II */
- irlap_wait_min_turn_around(self, &self->qos_tx);
-
- /* This frame will actually be sent at the new speed */
- irlap_send_rr_frame(self, CMD_FRAME);
-
- /* The timer is set to half the normal timer to quickly
- * detect a failure to negotiate the new connection
- * parameters. IrLAP 6.11.3.2, note 3.
- * Note that currently we don't process this failure
- * properly, as we should do a quick disconnect.
- * Jean II */
- irlap_start_final_timer(self, self->final_timeout/2);
- irlap_next_state(self, LAP_NRM_P);
-
- irlap_connect_confirm(self, skb);
- break;
- case RECV_DM_RSP: /* FALLTHROUGH */
- case RECV_DISC_CMD:
- del_timer(&self->final_timer);
- irlap_next_state(self, LAP_NDM);
-
- irlap_disconnect_indication(self, LAP_DISC_INDICATION);
- break;
- default:
- pr_debug("%s(), Unknown event %d, %s\n", __func__,
- event, irlap_event[event]);
-
- ret = -1;
- break;
- }
- return ret;
-}
-
-/*
- * Function irlap_state_offline (self, event, skb, info)
- *
- * OFFLINE state, not used for now!
- *
- */
-static int irlap_state_offline(struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info)
-{
- pr_debug("%s(), Unknown event\n", __func__);
-
- return -1;
-}
-
-/*
- * Function irlap_state_xmit_p (self, event, skb, info)
- *
- * XMIT, Only the primary station has right to transmit, and we
- * therefore do not expect to receive any transmissions from other
- * stations.
- *
- */
-static int irlap_state_xmit_p(struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info)
-{
- int ret = 0;
-
- switch (event) {
- case SEND_I_CMD:
- /*
- * Only send frame if send-window > 0.
- */
- if ((self->window > 0) && (!self->remote_busy)) {
- int nextfit;
-#ifdef CONFIG_IRDA_DYNAMIC_WINDOW
- struct sk_buff *skb_next;
-
- /* With DYNAMIC_WINDOW, we keep the window size
- * maximum, and adapt on the packets we are sending.
- * At 115k, we can send only 2 packets of 2048 bytes
- * in a 500 ms turnaround. Without this option, we
- * would always limit the window to 2. With this
- * option, if we send smaller packets, we can send
- * up to 7 of them (always depending on QoS).
- * Jean II */
-
- /* Look at the next skb. This is safe, as we are
- * the only consumer of the Tx queue (if we are not,
- * we have other problems) - Jean II */
- skb_next = skb_peek(&self->txq);
-
- /* Check if a subsequent skb exist and would fit in
- * the current window (with respect to turnaround
- * time).
- * This allow us to properly mark the current packet
- * with the pf bit, to avoid falling back on the
- * second test below, and avoid waiting the
- * end of the window and sending a extra RR.
- * Note : (skb_next != NULL) <=> (skb_queue_len() > 0)
- * Jean II */
- nextfit = ((skb_next != NULL) &&
- ((skb_next->len + skb->len) <=
- self->bytes_left));
-
- /*
- * The current packet may not fit ! Because of test
- * above, this should not happen any more !!!
- * Test if we have transmitted more bytes over the
- * link than its possible to do with the current
- * speed and turn-around-time.
- */
- if((!nextfit) && (skb->len > self->bytes_left)) {
- pr_debug("%s(), Not allowed to transmit more bytes!\n",
- __func__);
- /* Requeue the skb */
- skb_queue_head(&self->txq, skb_get(skb));
- /*
- * We should switch state to LAP_NRM_P, but
- * that is not possible since we must be sure
- * that we poll the other side. Since we have
- * used up our time, the poll timer should
- * trigger anyway now, so we just wait for it
- * DB
- */
- /*
- * Sorry, but that's not totally true. If
- * we send 2000B packets, we may wait another
- * 1000B until our turnaround expire. That's
- * why we need to be proactive in avoiding
- * coming here. - Jean II
- */
- return -EPROTO;
- }
-
- /* Subtract space used by this skb */
- self->bytes_left -= skb->len;
-#else /* CONFIG_IRDA_DYNAMIC_WINDOW */
- /* Window has been adjusted for the max packet
- * size, so much simpler... - Jean II */
- nextfit = !skb_queue_empty(&self->txq);
-#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */
- /*
- * Send data with poll bit cleared only if window > 1
- * and there is more frames after this one to be sent
- */
- if ((self->window > 1) && (nextfit)) {
- /* More packet to send in current window */
- irlap_send_data_primary(self, skb);
- irlap_next_state(self, LAP_XMIT_P);
- } else {
- /* Final packet of window */
- irlap_send_data_primary_poll(self, skb);
-
- /*
- * Make sure state machine does not try to send
- * any more frames
- */
- ret = -EPROTO;
- }
-#ifdef CONFIG_IRDA_FAST_RR
- /* Peer may want to reply immediately */
- self->fast_RR = FALSE;
-#endif /* CONFIG_IRDA_FAST_RR */
- } else {
- pr_debug("%s(), Unable to send! remote busy?\n",
- __func__);
- skb_queue_head(&self->txq, skb_get(skb));
-
- /*
- * The next ret is important, because it tells
- * irlap_next_state _not_ to deliver more frames
- */
- ret = -EPROTO;
- }
- break;
- case POLL_TIMER_EXPIRED:
- pr_debug("%s(), POLL_TIMER_EXPIRED <%ld>\n",
- __func__, jiffies);
- irlap_send_rr_frame(self, CMD_FRAME);
- /* Return to NRM properly - Jean II */
- self->window = self->window_size;
-#ifdef CONFIG_IRDA_DYNAMIC_WINDOW
- /* Allowed to transmit a maximum number of bytes again. */
- self->bytes_left = self->line_capacity;
-#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */
- irlap_start_final_timer(self, self->final_timeout);
- irlap_next_state(self, LAP_NRM_P);
- break;
- case DISCONNECT_REQUEST:
- del_timer(&self->poll_timer);
- irlap_wait_min_turn_around(self, &self->qos_tx);
- irlap_send_disc_frame(self);
- irlap_flush_all_queues(self);
- irlap_start_final_timer(self, self->final_timeout);
- self->retry_count = 0;
- irlap_next_state(self, LAP_PCLOSE);
- break;
- case DATA_REQUEST:
- /* Nothing to do, irlap_do_event() will send the packet
- * when we return... - Jean II */
- break;
- default:
- pr_debug("%s(), Unknown event %s\n",
- __func__, irlap_event[event]);
-
- ret = -EINVAL;
- break;
- }
- return ret;
-}
-
-/*
- * Function irlap_state_pclose (event, skb, info)
- *
- * PCLOSE state
- */
-static int irlap_state_pclose(struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info)
-{
- int ret = 0;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
- switch (event) {
- case RECV_UA_RSP: /* FALLTHROUGH */
- case RECV_DM_RSP:
- del_timer(&self->final_timer);
-
- /* Set new link parameters */
- irlap_apply_default_connection_parameters(self);
-
- /* Always switch state before calling upper layers */
- irlap_next_state(self, LAP_NDM);
-
- irlap_disconnect_indication(self, LAP_DISC_INDICATION);
- break;
- case FINAL_TIMER_EXPIRED:
- if (self->retry_count < self->N3) {
- irlap_wait_min_turn_around(self, &self->qos_tx);
- irlap_send_disc_frame(self);
- irlap_start_final_timer(self, self->final_timeout);
- self->retry_count++;
- /* Keep state */
- } else {
- irlap_apply_default_connection_parameters(self);
-
- /* Always switch state before calling upper layers */
- irlap_next_state(self, LAP_NDM);
-
- irlap_disconnect_indication(self, LAP_NO_RESPONSE);
- }
- break;
- default:
- pr_debug("%s(), Unknown event %d\n", __func__, event);
-
- ret = -1;
- break;
- }
- return ret;
-}
-
-/*
- * Function irlap_state_nrm_p (self, event, skb, info)
- *
- * NRM_P (Normal Response Mode as Primary), The primary station has given
- * permissions to a secondary station to transmit IrLAP resonse frames
- * (by sending a frame with the P bit set). The primary station will not
- * transmit any frames and is expecting to receive frames only from the
- * secondary to which transmission permissions has been given.
- */
-static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info)
-{
- int ret = 0;
- int ns_status;
- int nr_status;
-
- switch (event) {
- case RECV_I_RSP: /* Optimize for the common case */
- if (unlikely(skb->len <= LAP_ADDR_HEADER + LAP_CTRL_HEADER)) {
- /*
- * Input validation check: a stir4200/mcp2150
- * combination sometimes results in an empty i:rsp.
- * This makes no sense; we can just ignore the frame
- * and send an rr:cmd immediately. This happens before
- * changing nr or ns so triggers a retransmit
- */
- irlap_wait_min_turn_around(self, &self->qos_tx);
- irlap_send_rr_frame(self, CMD_FRAME);
- /* Keep state */
- break;
- }
- /* FIXME: must check for remote_busy below */
-#ifdef CONFIG_IRDA_FAST_RR
- /*
- * Reset the fast_RR so we can use the fast RR code with
- * full speed the next time since peer may have more frames
- * to transmitt
- */
- self->fast_RR = FALSE;
-#endif /* CONFIG_IRDA_FAST_RR */
- IRDA_ASSERT( info != NULL, return -1;);
-
- ns_status = irlap_validate_ns_received(self, info->ns);
- nr_status = irlap_validate_nr_received(self, info->nr);
-
- /*
- * Check for expected I(nformation) frame
- */
- if ((ns_status == NS_EXPECTED) && (nr_status == NR_EXPECTED)) {
-
- /* Update Vr (next frame for us to receive) */
- self->vr = (self->vr + 1) % 8;
-
- /* Update Nr received, cleanup our retry queue */
- irlap_update_nr_received(self, info->nr);
-
- /*
- * Got expected NR, so reset the
- * retry_count. This is not done by IrLAP spec,
- * which is strange!
- */
- self->retry_count = 0;
- self->ack_required = TRUE;
-
- /* poll bit cleared? */
- if (!info->pf) {
- /* Keep state, do not move this line */
- irlap_next_state(self, LAP_NRM_P);
-
- irlap_data_indication(self, skb, FALSE);
- } else {
- /* No longer waiting for pf */
- del_timer(&self->final_timer);
-
- irlap_wait_min_turn_around(self, &self->qos_tx);
-
- /* Call higher layer *before* changing state
- * to give them a chance to send data in the
- * next LAP frame.
- * Jean II */
- irlap_data_indication(self, skb, FALSE);
-
- /* XMIT states are the most dangerous state
- * to be in, because user requests are
- * processed directly and may change state.
- * On the other hand, in NDM_P, those
- * requests are queued and we will process
- * them when we return to irlap_do_event().
- * Jean II
- */
- irlap_next_state(self, LAP_XMIT_P);
-
- /* This is the last frame.
- * Make sure it's always called in XMIT state.
- * - Jean II */
- irlap_start_poll_timer(self, self->poll_timeout);
- }
- break;
-
- }
- /* Unexpected next to send (Ns) */
- if ((ns_status == NS_UNEXPECTED) && (nr_status == NR_EXPECTED))
- {
- if (!info->pf) {
- irlap_update_nr_received(self, info->nr);
-
- /*
- * Wait until the last frame before doing
- * anything
- */
-
- /* Keep state */
- irlap_next_state(self, LAP_NRM_P);
- } else {
- pr_debug("%s(), missing or duplicate frame!\n",
- __func__);
-
- /* Update Nr received */
- irlap_update_nr_received(self, info->nr);
-
- irlap_wait_min_turn_around(self, &self->qos_tx);
- irlap_send_rr_frame(self, CMD_FRAME);
-
- self->ack_required = FALSE;
-
- irlap_start_final_timer(self, self->final_timeout);
- irlap_next_state(self, LAP_NRM_P);
- }
- break;
- }
- /*
- * Unexpected next to receive (Nr)
- */
- if ((ns_status == NS_EXPECTED) && (nr_status == NR_UNEXPECTED))
- {
- if (info->pf) {
- self->vr = (self->vr + 1) % 8;
-
- /* Update Nr received */
- irlap_update_nr_received(self, info->nr);
-
- /* Resend rejected frames */
- irlap_resend_rejected_frames(self, CMD_FRAME);
-
- self->ack_required = FALSE;
-
- /* Make sure we account for the time
- * to transmit our frames. See comemnts
- * in irlap_send_data_primary_poll().
- * Jean II */
- irlap_start_final_timer(self, 2 * self->final_timeout);
-
- /* Keep state, do not move this line */
- irlap_next_state(self, LAP_NRM_P);
-
- irlap_data_indication(self, skb, FALSE);
- } else {
- /*
- * Do not resend frames until the last
- * frame has arrived from the other
- * device. This is not documented in
- * IrLAP!!
- */
- self->vr = (self->vr + 1) % 8;
-
- /* Update Nr received */
- irlap_update_nr_received(self, info->nr);
-
- self->ack_required = FALSE;
-
- /* Keep state, do not move this line!*/
- irlap_next_state(self, LAP_NRM_P);
-
- irlap_data_indication(self, skb, FALSE);
- }
- break;
- }
- /*
- * Unexpected next to send (Ns) and next to receive (Nr)
- * Not documented by IrLAP!
- */
- if ((ns_status == NS_UNEXPECTED) &&
- (nr_status == NR_UNEXPECTED))
- {
- pr_debug("%s(), unexpected nr and ns!\n",
- __func__);
- if (info->pf) {
- /* Resend rejected frames */
- irlap_resend_rejected_frames(self, CMD_FRAME);
-
- /* Give peer some time to retransmit!
- * But account for our own Tx. */
- irlap_start_final_timer(self, 2 * self->final_timeout);
-
- /* Keep state, do not move this line */
- irlap_next_state(self, LAP_NRM_P);
- } else {
- /* Update Nr received */
- /* irlap_update_nr_received( info->nr); */
-
- self->ack_required = FALSE;
- }
- break;
- }
-
- /*
- * Invalid NR or NS
- */
- if ((nr_status == NR_INVALID) || (ns_status == NS_INVALID)) {
- if (info->pf) {
- del_timer(&self->final_timer);
-
- irlap_next_state(self, LAP_RESET_WAIT);
-
- irlap_disconnect_indication(self, LAP_RESET_INDICATION);
- self->xmitflag = TRUE;
- } else {
- del_timer(&self->final_timer);
-
- irlap_disconnect_indication(self, LAP_RESET_INDICATION);
-
- self->xmitflag = FALSE;
- }
- break;
- }
- pr_debug("%s(), Not implemented!\n", __func__);
- pr_debug("%s(), event=%s, ns_status=%d, nr_status=%d\n",
- __func__, irlap_event[event], ns_status, nr_status);
- break;
- case RECV_UI_FRAME:
- /* Poll bit cleared? */
- if (!info->pf) {
- irlap_data_indication(self, skb, TRUE);
- irlap_next_state(self, LAP_NRM_P);
- } else {
- del_timer(&self->final_timer);
- irlap_data_indication(self, skb, TRUE);
- irlap_next_state(self, LAP_XMIT_P);
- pr_debug("%s: RECV_UI_FRAME: next state %s\n",
- __func__, irlap_state[self->state]);
- irlap_start_poll_timer(self, self->poll_timeout);
- }
- break;
- case RECV_RR_RSP:
- /*
- * If you get a RR, the remote isn't busy anymore,
- * no matter what the NR
- */
- self->remote_busy = FALSE;
-
- /* Stop final timer */
- del_timer(&self->final_timer);
-
- /*
- * Nr as expected?
- */
- ret = irlap_validate_nr_received(self, info->nr);
- if (ret == NR_EXPECTED) {
- /* Update Nr received */
- irlap_update_nr_received(self, info->nr);
-
- /*
- * Got expected NR, so reset the retry_count. This
- * is not done by the IrLAP standard , which is
- * strange! DB.
- */
- self->retry_count = 0;
- irlap_wait_min_turn_around(self, &self->qos_tx);
-
- irlap_next_state(self, LAP_XMIT_P);
-
- /* Start poll timer */
- irlap_start_poll_timer(self, self->poll_timeout);
- } else if (ret == NR_UNEXPECTED) {
- IRDA_ASSERT(info != NULL, return -1;);
- /*
- * Unexpected nr!
- */
-
- /* Update Nr received */
- irlap_update_nr_received(self, info->nr);
-
- pr_debug("RECV_RR_FRAME: Retrans:%d, nr=%d, va=%d, vs=%d, vr=%d\n",
- self->retry_count, info->nr, self->va,
- self->vs, self->vr);
-
- /* Resend rejected frames */
- irlap_resend_rejected_frames(self, CMD_FRAME);
- irlap_start_final_timer(self, self->final_timeout * 2);
-
- irlap_next_state(self, LAP_NRM_P);
- } else if (ret == NR_INVALID) {
- pr_debug("%s(), Received RR with invalid nr !\n",
- __func__);
-
- irlap_next_state(self, LAP_RESET_WAIT);
-
- irlap_disconnect_indication(self, LAP_RESET_INDICATION);
- self->xmitflag = TRUE;
- }
- break;
- case RECV_RNR_RSP:
- IRDA_ASSERT(info != NULL, return -1;);
-
- /* Stop final timer */
- del_timer(&self->final_timer);
- self->remote_busy = TRUE;
-
- /* Update Nr received */
- irlap_update_nr_received(self, info->nr);
- irlap_next_state(self, LAP_XMIT_P);
-
- /* Start poll timer */
- irlap_start_poll_timer(self, self->poll_timeout);
- break;
- case RECV_FRMR_RSP:
- del_timer(&self->final_timer);
- self->xmitflag = TRUE;
- irlap_next_state(self, LAP_RESET_WAIT);
- irlap_reset_indication(self);
- break;
- case FINAL_TIMER_EXPIRED:
- /*
- * We are allowed to wait for additional 300 ms if
- * final timer expires when we are in the middle
- * of receiving a frame (page 45, IrLAP). Check that
- * we only do this once for each frame.
- */
- if (irda_device_is_receiving(self->netdev) && !self->add_wait) {
- pr_debug("FINAL_TIMER_EXPIRED when receiving a frame! Waiting a little bit more!\n");
- irlap_start_final_timer(self, msecs_to_jiffies(300));
-
- /*
- * Don't allow this to happen one more time in a row,
- * or else we can get a pretty tight loop here if
- * if we only receive half a frame. DB.
- */
- self->add_wait = TRUE;
- break;
- }
- self->add_wait = FALSE;
-
- /* N2 is the disconnect timer. Until we reach it, we retry */
- if (self->retry_count < self->N2) {
- if (skb_peek(&self->wx_list) == NULL) {
- /* Retry sending the pf bit to the secondary */
- pr_debug("nrm_p: resending rr");
- irlap_wait_min_turn_around(self, &self->qos_tx);
- irlap_send_rr_frame(self, CMD_FRAME);
- } else {
- pr_debug("nrm_p: resend frames");
- irlap_resend_rejected_frames(self, CMD_FRAME);
- }
-
- irlap_start_final_timer(self, self->final_timeout);
- self->retry_count++;
- pr_debug("irlap_state_nrm_p: FINAL_TIMER_EXPIRED: retry_count=%d\n",
- self->retry_count);
-
- /* Early warning event. I'm using a pretty liberal
- * interpretation of the spec and generate an event
- * every time the timer is multiple of N1 (and not
- * only the first time). This allow application
- * to know precisely if connectivity restart...
- * Jean II */
- if((self->retry_count % self->N1) == 0)
- irlap_status_indication(self,
- STATUS_NO_ACTIVITY);
-
- /* Keep state */
- } else {
- irlap_apply_default_connection_parameters(self);
-
- /* Always switch state before calling upper layers */
- irlap_next_state(self, LAP_NDM);
- irlap_disconnect_indication(self, LAP_NO_RESPONSE);
- }
- break;
- case RECV_REJ_RSP:
- irlap_update_nr_received(self, info->nr);
- if (self->remote_busy) {
- irlap_wait_min_turn_around(self, &self->qos_tx);
- irlap_send_rr_frame(self, CMD_FRAME);
- } else
- irlap_resend_rejected_frames(self, CMD_FRAME);
- irlap_start_final_timer(self, 2 * self->final_timeout);
- break;
- case RECV_SREJ_RSP:
- irlap_update_nr_received(self, info->nr);
- if (self->remote_busy) {
- irlap_wait_min_turn_around(self, &self->qos_tx);
- irlap_send_rr_frame(self, CMD_FRAME);
- } else
- irlap_resend_rejected_frame(self, CMD_FRAME);
- irlap_start_final_timer(self, 2 * self->final_timeout);
- break;
- case RECV_RD_RSP:
- pr_debug("%s(), RECV_RD_RSP\n", __func__);
-
- irlap_flush_all_queues(self);
- irlap_next_state(self, LAP_XMIT_P);
- /* Call back the LAP state machine to do a proper disconnect */
- irlap_disconnect_request(self);
- break;
- default:
- pr_debug("%s(), Unknown event %s\n",
- __func__, irlap_event[event]);
-
- ret = -1;
- break;
- }
- return ret;
-}
-
-/*
- * Function irlap_state_reset_wait (event, skb, info)
- *
- * We have informed the service user of a reset condition, and is
- * awaiting reset of disconnect request.
- *
- */
-static int irlap_state_reset_wait(struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info)
-{
- int ret = 0;
-
- pr_debug("%s(), event = %s\n", __func__, irlap_event[event]);
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
- switch (event) {
- case RESET_REQUEST:
- if (self->xmitflag) {
- irlap_wait_min_turn_around(self, &self->qos_tx);
- irlap_send_snrm_frame(self, NULL);
- irlap_start_final_timer(self, self->final_timeout);
- irlap_next_state(self, LAP_RESET);
- } else {
- irlap_start_final_timer(self, self->final_timeout);
- irlap_next_state(self, LAP_RESET);
- }
- break;
- case DISCONNECT_REQUEST:
- irlap_wait_min_turn_around( self, &self->qos_tx);
- irlap_send_disc_frame( self);
- irlap_flush_all_queues( self);
- irlap_start_final_timer( self, self->final_timeout);
- self->retry_count = 0;
- irlap_next_state( self, LAP_PCLOSE);
- break;
- default:
- pr_debug("%s(), Unknown event %s\n", __func__,
- irlap_event[event]);
-
- ret = -1;
- break;
- }
- return ret;
-}
-
-/*
- * Function irlap_state_reset (self, event, skb, info)
- *
- * We have sent a SNRM reset command to the peer layer, and is awaiting
- * reply.
- *
- */
-static int irlap_state_reset(struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info)
-{
- int ret = 0;
-
- pr_debug("%s(), event = %s\n", __func__, irlap_event[event]);
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
- switch (event) {
- case RECV_DISC_CMD:
- del_timer(&self->final_timer);
-
- irlap_apply_default_connection_parameters(self);
-
- /* Always switch state before calling upper layers */
- irlap_next_state(self, LAP_NDM);
-
- irlap_disconnect_indication(self, LAP_NO_RESPONSE);
-
- break;
- case RECV_UA_RSP:
- del_timer(&self->final_timer);
-
- /* Initiate connection state */
- irlap_initiate_connection_state(self);
-
- irlap_reset_confirm();
-
- self->remote_busy = FALSE;
-
- irlap_next_state(self, LAP_XMIT_P);
-
- irlap_start_poll_timer(self, self->poll_timeout);
-
- break;
- case FINAL_TIMER_EXPIRED:
- if (self->retry_count < 3) {
- irlap_wait_min_turn_around(self, &self->qos_tx);
-
- IRDA_ASSERT(self->netdev != NULL, return -1;);
- irlap_send_snrm_frame(self, self->qos_dev);
-
- self->retry_count++; /* Experimental!! */
-
- irlap_start_final_timer(self, self->final_timeout);
- irlap_next_state(self, LAP_RESET);
- } else if (self->retry_count >= self->N3) {
- irlap_apply_default_connection_parameters(self);
-
- /* Always switch state before calling upper layers */
- irlap_next_state(self, LAP_NDM);
-
- irlap_disconnect_indication(self, LAP_NO_RESPONSE);
- }
- break;
- case RECV_SNRM_CMD:
- /*
- * SNRM frame is not allowed to contain an I-field in this
- * state
- */
- if (!info) {
- pr_debug("%s(), RECV_SNRM_CMD\n", __func__);
- irlap_initiate_connection_state(self);
- irlap_wait_min_turn_around(self, &self->qos_tx);
- irlap_send_ua_response_frame(self, &self->qos_rx);
- irlap_reset_confirm();
- irlap_start_wd_timer(self, self->wd_timeout);
- irlap_next_state(self, LAP_NDM);
- } else {
- pr_debug("%s(), SNRM frame contained an I field!\n",
- __func__);
- }
- break;
- default:
- pr_debug("%s(), Unknown event %s\n",
- __func__, irlap_event[event]);
-
- ret = -1;
- break;
- }
- return ret;
-}
-
-/*
- * Function irlap_state_xmit_s (event, skb, info)
- *
- * XMIT_S, The secondary station has been given the right to transmit,
- * and we therefore do not expect to receive any transmissions from other
- * stations.
- */
-static int irlap_state_xmit_s(struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info)
-{
- int ret = 0;
-
- pr_debug("%s(), event=%s\n", __func__, irlap_event[event]);
-
- IRDA_ASSERT(self != NULL, return -ENODEV;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return -EBADR;);
-
- switch (event) {
- case SEND_I_CMD:
- /*
- * Send frame only if send window > 0
- */
- if ((self->window > 0) && (!self->remote_busy)) {
- int nextfit;
-#ifdef CONFIG_IRDA_DYNAMIC_WINDOW
- struct sk_buff *skb_next;
-
- /*
- * Same deal as in irlap_state_xmit_p(), so see
- * the comments at that point.
- * We are the secondary, so there are only subtle
- * differences. - Jean II
- */
-
- /* Check if a subsequent skb exist and would fit in
- * the current window (with respect to turnaround
- * time). - Jean II */
- skb_next = skb_peek(&self->txq);
- nextfit = ((skb_next != NULL) &&
- ((skb_next->len + skb->len) <=
- self->bytes_left));
-
- /*
- * Test if we have transmitted more bytes over the
- * link than its possible to do with the current
- * speed and turn-around-time.
- */
- if((!nextfit) && (skb->len > self->bytes_left)) {
- pr_debug("%s(), Not allowed to transmit more bytes!\n",
- __func__);
- /* Requeue the skb */
- skb_queue_head(&self->txq, skb_get(skb));
-
- /*
- * Switch to NRM_S, this is only possible
- * when we are in secondary mode, since we
- * must be sure that we don't miss any RR
- * frames
- */
- self->window = self->window_size;
- self->bytes_left = self->line_capacity;
- irlap_start_wd_timer(self, self->wd_timeout);
-
- irlap_next_state(self, LAP_NRM_S);
- /* Slight difference with primary :
- * here we would wait for the other side to
- * expire the turnaround. - Jean II */
-
- return -EPROTO; /* Try again later */
- }
- /* Subtract space used by this skb */
- self->bytes_left -= skb->len;
-#else /* CONFIG_IRDA_DYNAMIC_WINDOW */
- /* Window has been adjusted for the max packet
- * size, so much simpler... - Jean II */
- nextfit = !skb_queue_empty(&self->txq);
-#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */
- /*
- * Send data with final bit cleared only if window > 1
- * and there is more frames to be sent
- */
- if ((self->window > 1) && (nextfit)) {
- irlap_send_data_secondary(self, skb);
- irlap_next_state(self, LAP_XMIT_S);
- } else {
- irlap_send_data_secondary_final(self, skb);
- irlap_next_state(self, LAP_NRM_S);
-
- /*
- * Make sure state machine does not try to send
- * any more frames
- */
- ret = -EPROTO;
- }
- } else {
- pr_debug("%s(), Unable to send!\n", __func__);
- skb_queue_head(&self->txq, skb_get(skb));
- ret = -EPROTO;
- }
- break;
- case DISCONNECT_REQUEST:
- irlap_send_rd_frame(self);
- irlap_flush_all_queues(self);
- irlap_start_wd_timer(self, self->wd_timeout);
- irlap_next_state(self, LAP_SCLOSE);
- break;
- case DATA_REQUEST:
- /* Nothing to do, irlap_do_event() will send the packet
- * when we return... - Jean II */
- break;
- default:
- pr_debug("%s(), Unknown event %s\n", __func__,
- irlap_event[event]);
-
- ret = -EINVAL;
- break;
- }
- return ret;
-}
-
-/*
- * Function irlap_state_nrm_s (event, skb, info)
- *
- * NRM_S (Normal Response Mode as Secondary) state, in this state we are
- * expecting to receive frames from the primary station
- *
- */
-static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info)
-{
- int ns_status;
- int nr_status;
- int ret = 0;
-
- pr_debug("%s(), event=%s\n", __func__, irlap_event[event]);
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
- switch (event) {
- case RECV_I_CMD: /* Optimize for the common case */
- /* FIXME: must check for remote_busy below */
- pr_debug("%s(), event=%s nr=%d, vs=%d, ns=%d, vr=%d, pf=%d\n",
- __func__, irlap_event[event], info->nr,
- self->vs, info->ns, self->vr, info->pf);
-
- self->retry_count = 0;
-
- ns_status = irlap_validate_ns_received(self, info->ns);
- nr_status = irlap_validate_nr_received(self, info->nr);
- /*
- * Check for expected I(nformation) frame
- */
- if ((ns_status == NS_EXPECTED) && (nr_status == NR_EXPECTED)) {
-
- /* Update Vr (next frame for us to receive) */
- self->vr = (self->vr + 1) % 8;
-
- /* Update Nr received */
- irlap_update_nr_received(self, info->nr);
-
- /*
- * poll bit cleared?
- */
- if (!info->pf) {
-
- self->ack_required = TRUE;
-
- /*
- * Starting WD-timer here is optional, but
- * not recommended. Note 6 IrLAP p. 83
- */
-#if 0
- irda_start_timer(WD_TIMER, self->wd_timeout);
-#endif
- /* Keep state, do not move this line */
- irlap_next_state(self, LAP_NRM_S);
-
- irlap_data_indication(self, skb, FALSE);
- break;
- } else {
- /*
- * We should wait before sending RR, and
- * also before changing to XMIT_S
- * state. (note 1, IrLAP p. 82)
- */
- irlap_wait_min_turn_around(self, &self->qos_tx);
-
- /*
- * Give higher layers a chance to
- * immediately reply with some data before
- * we decide if we should send a RR frame
- * or not
- */
- irlap_data_indication(self, skb, FALSE);
-
- /* Any pending data requests? */
- if (!skb_queue_empty(&self->txq) &&
- (self->window > 0))
- {
- self->ack_required = TRUE;
-
- del_timer(&self->wd_timer);
-
- irlap_next_state(self, LAP_XMIT_S);
- } else {
- irlap_send_rr_frame(self, RSP_FRAME);
- irlap_start_wd_timer(self,
- self->wd_timeout);
-
- /* Keep the state */
- irlap_next_state(self, LAP_NRM_S);
- }
- break;
- }
- }
- /*
- * Check for Unexpected next to send (Ns)
- */
- if ((ns_status == NS_UNEXPECTED) && (nr_status == NR_EXPECTED))
- {
- /* Unexpected next to send, with final bit cleared */
- if (!info->pf) {
- irlap_update_nr_received(self, info->nr);
-
- irlap_start_wd_timer(self, self->wd_timeout);
- } else {
- /* Update Nr received */
- irlap_update_nr_received(self, info->nr);
-
- irlap_wait_min_turn_around(self, &self->qos_tx);
- irlap_send_rr_frame(self, RSP_FRAME);
-
- irlap_start_wd_timer(self, self->wd_timeout);
- }
- break;
- }
-
- /*
- * Unexpected Next to Receive(NR) ?
- */
- if ((ns_status == NS_EXPECTED) && (nr_status == NR_UNEXPECTED))
- {
- if (info->pf) {
- pr_debug("RECV_I_RSP: frame(s) lost\n");
-
- self->vr = (self->vr + 1) % 8;
-
- /* Update Nr received */
- irlap_update_nr_received(self, info->nr);
-
- /* Resend rejected frames */
- irlap_resend_rejected_frames(self, RSP_FRAME);
-
- /* Keep state, do not move this line */
- irlap_next_state(self, LAP_NRM_S);
-
- irlap_data_indication(self, skb, FALSE);
- irlap_start_wd_timer(self, self->wd_timeout);
- break;
- }
- /*
- * This is not documented in IrLAP!! Unexpected NR
- * with poll bit cleared
- */
- if (!info->pf) {
- self->vr = (self->vr + 1) % 8;
-
- /* Update Nr received */
- irlap_update_nr_received(self, info->nr);
-
- /* Keep state, do not move this line */
- irlap_next_state(self, LAP_NRM_S);
-
- irlap_data_indication(self, skb, FALSE);
- irlap_start_wd_timer(self, self->wd_timeout);
- }
- break;
- }
-
- if (ret == NR_INVALID) {
- pr_debug("NRM_S, NR_INVALID not implemented!\n");
- }
- if (ret == NS_INVALID) {
- pr_debug("NRM_S, NS_INVALID not implemented!\n");
- }
- break;
- case RECV_UI_FRAME:
- /*
- * poll bit cleared?
- */
- if (!info->pf) {
- irlap_data_indication(self, skb, TRUE);
- irlap_next_state(self, LAP_NRM_S); /* Keep state */
- } else {
- /*
- * Any pending data requests?
- */
- if (!skb_queue_empty(&self->txq) &&
- (self->window > 0) && !self->remote_busy)
- {
- irlap_data_indication(self, skb, TRUE);
-
- del_timer(&self->wd_timer);
-
- irlap_next_state(self, LAP_XMIT_S);
- } else {
- irlap_data_indication(self, skb, TRUE);
-
- irlap_wait_min_turn_around(self, &self->qos_tx);
-
- irlap_send_rr_frame(self, RSP_FRAME);
- self->ack_required = FALSE;
-
- irlap_start_wd_timer(self, self->wd_timeout);
-
- /* Keep the state */
- irlap_next_state(self, LAP_NRM_S);
- }
- }
- break;
- case RECV_RR_CMD:
- self->retry_count = 0;
-
- /*
- * Nr as expected?
- */
- nr_status = irlap_validate_nr_received(self, info->nr);
- if (nr_status == NR_EXPECTED) {
- if (!skb_queue_empty(&self->txq) &&
- (self->window > 0)) {
- self->remote_busy = FALSE;
-
- /* Update Nr received */
- irlap_update_nr_received(self, info->nr);
- del_timer(&self->wd_timer);
-
- irlap_wait_min_turn_around(self, &self->qos_tx);
- irlap_next_state(self, LAP_XMIT_S);
- } else {
- self->remote_busy = FALSE;
- /* Update Nr received */
- irlap_update_nr_received(self, info->nr);
- irlap_wait_min_turn_around(self, &self->qos_tx);
- irlap_start_wd_timer(self, self->wd_timeout);
-
- /* Note : if the link is idle (this case),
- * we never go in XMIT_S, so we never get a
- * chance to process any DISCONNECT_REQUEST.
- * Do it now ! - Jean II */
- if (self->disconnect_pending) {
- /* Disconnect */
- irlap_send_rd_frame(self);
- irlap_flush_all_queues(self);
-
- irlap_next_state(self, LAP_SCLOSE);
- } else {
- /* Just send back pf bit */
- irlap_send_rr_frame(self, RSP_FRAME);
-
- irlap_next_state(self, LAP_NRM_S);
- }
- }
- } else if (nr_status == NR_UNEXPECTED) {
- self->remote_busy = FALSE;
- irlap_update_nr_received(self, info->nr);
- irlap_resend_rejected_frames(self, RSP_FRAME);
-
- irlap_start_wd_timer(self, self->wd_timeout);
-
- /* Keep state */
- irlap_next_state(self, LAP_NRM_S);
- } else {
- pr_debug("%s(), invalid nr not implemented!\n",
- __func__);
- }
- break;
- case RECV_SNRM_CMD:
- /* SNRM frame is not allowed to contain an I-field */
- if (!info) {
- del_timer(&self->wd_timer);
- pr_debug("%s(), received SNRM cmd\n", __func__);
- irlap_next_state(self, LAP_RESET_CHECK);
-
- irlap_reset_indication(self);
- } else {
- pr_debug("%s(), SNRM frame contained an I-field!\n",
- __func__);
-
- }
- break;
- case RECV_REJ_CMD:
- irlap_update_nr_received(self, info->nr);
- if (self->remote_busy) {
- irlap_wait_min_turn_around(self, &self->qos_tx);
- irlap_send_rr_frame(self, RSP_FRAME);
- } else
- irlap_resend_rejected_frames(self, RSP_FRAME);
- irlap_start_wd_timer(self, self->wd_timeout);
- break;
- case RECV_SREJ_CMD:
- irlap_update_nr_received(self, info->nr);
- if (self->remote_busy) {
- irlap_wait_min_turn_around(self, &self->qos_tx);
- irlap_send_rr_frame(self, RSP_FRAME);
- } else
- irlap_resend_rejected_frame(self, RSP_FRAME);
- irlap_start_wd_timer(self, self->wd_timeout);
- break;
- case WD_TIMER_EXPIRED:
- /*
- * Wait until retry_count * n matches negotiated threshold/
- * disconnect time (note 2 in IrLAP p. 82)
- *
- * Similar to irlap_state_nrm_p() -> FINAL_TIMER_EXPIRED
- * Note : self->wd_timeout = (self->final_timeout * 2),
- * which explain why we use (self->N2 / 2) here !!!
- * Jean II
- */
- pr_debug("%s(), retry_count = %d\n", __func__,
- self->retry_count);
-
- if (self->retry_count < (self->N2 / 2)) {
- /* No retry, just wait for primary */
- irlap_start_wd_timer(self, self->wd_timeout);
- self->retry_count++;
-
- if((self->retry_count % (self->N1 / 2)) == 0)
- irlap_status_indication(self,
- STATUS_NO_ACTIVITY);
- } else {
- irlap_apply_default_connection_parameters(self);
-
- /* Always switch state before calling upper layers */
- irlap_next_state(self, LAP_NDM);
- irlap_disconnect_indication(self, LAP_NO_RESPONSE);
- }
- break;
- case RECV_DISC_CMD:
- /* Always switch state before calling upper layers */
- irlap_next_state(self, LAP_NDM);
-
- /* Send disconnect response */
- irlap_wait_min_turn_around(self, &self->qos_tx);
- irlap_send_ua_response_frame(self, NULL);
-
- del_timer(&self->wd_timer);
- irlap_flush_all_queues(self);
- /* Set default link parameters */
- irlap_apply_default_connection_parameters(self);
-
- irlap_disconnect_indication(self, LAP_DISC_INDICATION);
- break;
- case RECV_DISCOVERY_XID_CMD:
- irlap_wait_min_turn_around(self, &self->qos_tx);
- irlap_send_rr_frame(self, RSP_FRAME);
- self->ack_required = TRUE;
- irlap_start_wd_timer(self, self->wd_timeout);
- irlap_next_state(self, LAP_NRM_S);
-
- break;
- case RECV_TEST_CMD:
- /* Remove test frame header (only LAP header in NRM) */
- skb_pull(skb, LAP_ADDR_HEADER + LAP_CTRL_HEADER);
-
- irlap_wait_min_turn_around(self, &self->qos_tx);
- irlap_start_wd_timer(self, self->wd_timeout);
-
- /* Send response (info will be copied) */
- irlap_send_test_frame(self, self->caddr, info->daddr, skb);
- break;
- default:
- pr_debug("%s(), Unknown event %d, (%s)\n", __func__,
- event, irlap_event[event]);
-
- ret = -EINVAL;
- break;
- }
- return ret;
-}
-
-/*
- * Function irlap_state_sclose (self, event, skb, info)
- */
-static int irlap_state_sclose(struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info)
-{
- IRDA_ASSERT(self != NULL, return -ENODEV;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return -EBADR;);
-
- switch (event) {
- case RECV_DISC_CMD:
- /* Always switch state before calling upper layers */
- irlap_next_state(self, LAP_NDM);
-
- /* Send disconnect response */
- irlap_wait_min_turn_around(self, &self->qos_tx);
- irlap_send_ua_response_frame(self, NULL);
-
- del_timer(&self->wd_timer);
- /* Set default link parameters */
- irlap_apply_default_connection_parameters(self);
-
- irlap_disconnect_indication(self, LAP_DISC_INDICATION);
- break;
- case RECV_DM_RSP:
- /* IrLAP-1.1 p.82: in SCLOSE, S and I type RSP frames
- * shall take us down into default NDM state, like DM_RSP
- */
- case RECV_RR_RSP:
- case RECV_RNR_RSP:
- case RECV_REJ_RSP:
- case RECV_SREJ_RSP:
- case RECV_I_RSP:
- /* Always switch state before calling upper layers */
- irlap_next_state(self, LAP_NDM);
-
- del_timer(&self->wd_timer);
- irlap_apply_default_connection_parameters(self);
-
- irlap_disconnect_indication(self, LAP_DISC_INDICATION);
- break;
- case WD_TIMER_EXPIRED:
- /* Always switch state before calling upper layers */
- irlap_next_state(self, LAP_NDM);
-
- irlap_apply_default_connection_parameters(self);
-
- irlap_disconnect_indication(self, LAP_DISC_INDICATION);
- break;
- default:
- /* IrLAP-1.1 p.82: in SCLOSE, basically any received frame
- * with pf=1 shall restart the wd-timer and resend the rd:rsp
- */
- if (info != NULL && info->pf) {
- del_timer(&self->wd_timer);
- irlap_wait_min_turn_around(self, &self->qos_tx);
- irlap_send_rd_frame(self);
- irlap_start_wd_timer(self, self->wd_timeout);
- break; /* stay in SCLOSE */
- }
-
- pr_debug("%s(), Unknown event %d, (%s)\n", __func__,
- event, irlap_event[event]);
-
- break;
- }
-
- return -1;
-}
-
-static int irlap_state_reset_check( struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb,
- struct irlap_info *info)
-{
- int ret = 0;
-
- pr_debug("%s(), event=%s\n", __func__, irlap_event[event]);
-
- IRDA_ASSERT(self != NULL, return -ENODEV;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return -EBADR;);
-
- switch (event) {
- case RESET_RESPONSE:
- irlap_send_ua_response_frame(self, &self->qos_rx);
- irlap_initiate_connection_state(self);
- irlap_start_wd_timer(self, WD_TIMEOUT);
- irlap_flush_all_queues(self);
-
- irlap_next_state(self, LAP_NRM_S);
- break;
- case DISCONNECT_REQUEST:
- irlap_wait_min_turn_around(self, &self->qos_tx);
- irlap_send_rd_frame(self);
- irlap_start_wd_timer(self, WD_TIMEOUT);
- irlap_next_state(self, LAP_SCLOSE);
- break;
- default:
- pr_debug("%s(), Unknown event %d, (%s)\n", __func__,
- event, irlap_event[event]);
-
- ret = -EINVAL;
- break;
- }
- return ret;
-}
diff --git a/drivers/staging/irda/net/irlap_frame.c b/drivers/staging/irda/net/irlap_frame.c
deleted file mode 100644
index debda3de4726..000000000000
--- a/drivers/staging/irda/net/irlap_frame.c
+++ /dev/null
@@ -1,1407 +0,0 @@
-/*********************************************************************
- *
- * Filename: irlap_frame.c
- * Version: 1.0
- * Description: Build and transmit IrLAP frames
- * Status: Stable
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Tue Aug 19 10:27:26 1997
- * Modified at: Wed Jan 5 08:59:04 2000
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>,
- * All Rights Reserved.
- * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/skbuff.h>
-#include <linux/if.h>
-#include <linux/if_ether.h>
-#include <linux/netdevice.h>
-#include <linux/irda.h>
-#include <linux/slab.h>
-
-#include <net/pkt_sched.h>
-#include <net/sock.h>
-
-#include <asm/byteorder.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irda_device.h>
-#include <net/irda/irlap.h>
-#include <net/irda/wrapper.h>
-#include <net/irda/timer.h>
-#include <net/irda/irlap_frame.h>
-#include <net/irda/qos.h>
-
-static void irlap_send_i_frame(struct irlap_cb *self, struct sk_buff *skb,
- int command);
-
-/*
- * Function irlap_insert_info (self, skb)
- *
- * Insert minimum turnaround time and speed information into the skb. We
- * need to do this since it's per packet relevant information. Safe to
- * have this function inlined since it's only called from one place
- */
-static inline void irlap_insert_info(struct irlap_cb *self,
- struct sk_buff *skb)
-{
- struct irda_skb_cb *cb = (struct irda_skb_cb *) skb->cb;
-
- /*
- * Insert MTT (min. turn time) and speed into skb, so that the
- * device driver knows which settings to use
- */
- cb->magic = LAP_MAGIC;
- cb->mtt = self->mtt_required;
- cb->next_speed = self->speed;
-
- /* Reset */
- self->mtt_required = 0;
-
- /*
- * Delay equals negotiated BOFs count, plus the number of BOFs to
- * force the negotiated minimum turnaround time
- */
- cb->xbofs = self->bofs_count;
- cb->next_xbofs = self->next_bofs;
- cb->xbofs_delay = self->xbofs_delay;
-
- /* Reset XBOF's delay (used only for getting min turn time) */
- self->xbofs_delay = 0;
- /* Put the correct xbofs value for the next packet */
- self->bofs_count = self->next_bofs;
-}
-
-/*
- * Function irlap_queue_xmit (self, skb)
- *
- * A little wrapper for dev_queue_xmit, so we can insert some common
- * code into it.
- */
-void irlap_queue_xmit(struct irlap_cb *self, struct sk_buff *skb)
-{
- /* Some common init stuff */
- skb->dev = self->netdev;
- skb_reset_mac_header(skb);
- skb_reset_network_header(skb);
- skb_reset_transport_header(skb);
- skb->protocol = htons(ETH_P_IRDA);
- skb->priority = TC_PRIO_BESTEFFORT;
-
- irlap_insert_info(self, skb);
-
- if (unlikely(self->mode & IRDA_MODE_MONITOR)) {
- pr_debug("%s(): %s is in monitor mode\n", __func__,
- self->netdev->name);
- dev_kfree_skb(skb);
- return;
- }
-
- dev_queue_xmit(skb);
-}
-
-/*
- * Function irlap_send_snrm_cmd (void)
- *
- * Transmits a connect SNRM command frame
- */
-void irlap_send_snrm_frame(struct irlap_cb *self, struct qos_info *qos)
-{
- struct sk_buff *tx_skb;
- struct snrm_frame *frame;
- int ret;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
- /* Allocate frame */
- tx_skb = alloc_skb(sizeof(struct snrm_frame) +
- IRLAP_NEGOCIATION_PARAMS_LEN,
- GFP_ATOMIC);
- if (!tx_skb)
- return;
-
- frame = skb_put(tx_skb, 2);
-
- /* Insert connection address field */
- if (qos)
- frame->caddr = CMD_FRAME | CBROADCAST;
- else
- frame->caddr = CMD_FRAME | self->caddr;
-
- /* Insert control field */
- frame->control = SNRM_CMD | PF_BIT;
-
- /*
- * If we are establishing a connection then insert QoS parameters
- */
- if (qos) {
- skb_put(tx_skb, 9); /* 25 left */
- frame->saddr = cpu_to_le32(self->saddr);
- frame->daddr = cpu_to_le32(self->daddr);
-
- frame->ncaddr = self->caddr;
-
- ret = irlap_insert_qos_negotiation_params(self, tx_skb);
- if (ret < 0) {
- dev_kfree_skb(tx_skb);
- return;
- }
- }
- irlap_queue_xmit(self, tx_skb);
-}
-
-/*
- * Function irlap_recv_snrm_cmd (skb, info)
- *
- * Received SNRM (Set Normal Response Mode) command frame
- *
- */
-static void irlap_recv_snrm_cmd(struct irlap_cb *self, struct sk_buff *skb,
- struct irlap_info *info)
-{
- struct snrm_frame *frame;
-
- if (pskb_may_pull(skb,sizeof(struct snrm_frame))) {
- frame = (struct snrm_frame *) skb->data;
-
- /* Copy the new connection address ignoring the C/R bit */
- info->caddr = frame->ncaddr & 0xFE;
-
- /* Check if the new connection address is valid */
- if ((info->caddr == 0x00) || (info->caddr == 0xfe)) {
- pr_debug("%s(), invalid connection address!\n",
- __func__);
- return;
- }
-
- /* Copy peer device address */
- info->daddr = le32_to_cpu(frame->saddr);
- info->saddr = le32_to_cpu(frame->daddr);
-
- /* Only accept if addressed directly to us */
- if (info->saddr != self->saddr) {
- pr_debug("%s(), not addressed to us!\n",
- __func__);
- return;
- }
- irlap_do_event(self, RECV_SNRM_CMD, skb, info);
- } else {
- /* Signal that this SNRM frame does not contain and I-field */
- irlap_do_event(self, RECV_SNRM_CMD, skb, NULL);
- }
-}
-
-/*
- * Function irlap_send_ua_response_frame (qos)
- *
- * Send UA (Unnumbered Acknowledgement) frame
- *
- */
-void irlap_send_ua_response_frame(struct irlap_cb *self, struct qos_info *qos)
-{
- struct sk_buff *tx_skb;
- struct ua_frame *frame;
- int ret;
-
- pr_debug("%s() <%ld>\n", __func__, jiffies);
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
- /* Allocate frame */
- tx_skb = alloc_skb(sizeof(struct ua_frame) +
- IRLAP_NEGOCIATION_PARAMS_LEN,
- GFP_ATOMIC);
- if (!tx_skb)
- return;
-
- frame = skb_put(tx_skb, 10);
-
- /* Build UA response */
- frame->caddr = self->caddr;
- frame->control = UA_RSP | PF_BIT;
-
- frame->saddr = cpu_to_le32(self->saddr);
- frame->daddr = cpu_to_le32(self->daddr);
-
- /* Should we send QoS negotiation parameters? */
- if (qos) {
- ret = irlap_insert_qos_negotiation_params(self, tx_skb);
- if (ret < 0) {
- dev_kfree_skb(tx_skb);
- return;
- }
- }
-
- irlap_queue_xmit(self, tx_skb);
-}
-
-
-/*
- * Function irlap_send_dm_frame (void)
- *
- * Send disconnected mode (DM) frame
- *
- */
-void irlap_send_dm_frame( struct irlap_cb *self)
-{
- struct sk_buff *tx_skb = NULL;
- struct dm_frame *frame;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
- tx_skb = alloc_skb(sizeof(struct dm_frame), GFP_ATOMIC);
- if (!tx_skb)
- return;
-
- frame = skb_put(tx_skb, 2);
-
- if (self->state == LAP_NDM)
- frame->caddr = CBROADCAST;
- else
- frame->caddr = self->caddr;
-
- frame->control = DM_RSP | PF_BIT;
-
- irlap_queue_xmit(self, tx_skb);
-}
-
-/*
- * Function irlap_send_disc_frame (void)
- *
- * Send disconnect (DISC) frame
- *
- */
-void irlap_send_disc_frame(struct irlap_cb *self)
-{
- struct sk_buff *tx_skb = NULL;
- struct disc_frame *frame;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
- tx_skb = alloc_skb(sizeof(struct disc_frame), GFP_ATOMIC);
- if (!tx_skb)
- return;
-
- frame = skb_put(tx_skb, 2);
-
- frame->caddr = self->caddr | CMD_FRAME;
- frame->control = DISC_CMD | PF_BIT;
-
- irlap_queue_xmit(self, tx_skb);
-}
-
-/*
- * Function irlap_send_discovery_xid_frame (S, s, command)
- *
- * Build and transmit a XID (eXchange station IDentifier) discovery
- * frame.
- */
-void irlap_send_discovery_xid_frame(struct irlap_cb *self, int S, __u8 s,
- __u8 command, discovery_t *discovery)
-{
- struct sk_buff *tx_skb = NULL;
- struct xid_frame *frame;
- __u32 bcast = BROADCAST;
- __u8 *info;
-
- pr_debug("%s(), s=%d, S=%d, command=%d\n", __func__,
- s, S, command);
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
- IRDA_ASSERT(discovery != NULL, return;);
-
- tx_skb = alloc_skb(sizeof(struct xid_frame) + IRLAP_DISCOVERY_INFO_LEN,
- GFP_ATOMIC);
- if (!tx_skb)
- return;
-
- skb_put(tx_skb, 14);
- frame = (struct xid_frame *) tx_skb->data;
-
- if (command) {
- frame->caddr = CBROADCAST | CMD_FRAME;
- frame->control = XID_CMD | PF_BIT;
- } else {
- frame->caddr = CBROADCAST;
- frame->control = XID_RSP | PF_BIT;
- }
- frame->ident = XID_FORMAT;
-
- frame->saddr = cpu_to_le32(self->saddr);
-
- if (command)
- frame->daddr = cpu_to_le32(bcast);
- else
- frame->daddr = cpu_to_le32(discovery->data.daddr);
-
- switch (S) {
- case 1:
- frame->flags = 0x00;
- break;
- case 6:
- frame->flags = 0x01;
- break;
- case 8:
- frame->flags = 0x02;
- break;
- case 16:
- frame->flags = 0x03;
- break;
- default:
- frame->flags = 0x02;
- break;
- }
-
- frame->slotnr = s;
- frame->version = 0x00;
-
- /*
- * Provide info for final slot only in commands, and for all
- * responses. Send the second byte of the hint only if the
- * EXTENSION bit is set in the first byte.
- */
- if (!command || (frame->slotnr == 0xff)) {
- int len;
-
- if (discovery->data.hints[0] & HINT_EXTENSION) {
- info = skb_put(tx_skb, 2);
- info[0] = discovery->data.hints[0];
- info[1] = discovery->data.hints[1];
- } else {
- info = skb_put(tx_skb, 1);
- info[0] = discovery->data.hints[0];
- }
- info = skb_put(tx_skb, 1);
- info[0] = discovery->data.charset;
-
- len = IRDA_MIN(discovery->name_len, skb_tailroom(tx_skb));
- skb_put_data(tx_skb, discovery->data.info, len);
- }
- irlap_queue_xmit(self, tx_skb);
-}
-
-/*
- * Function irlap_recv_discovery_xid_rsp (skb, info)
- *
- * Received a XID discovery response
- *
- */
-static void irlap_recv_discovery_xid_rsp(struct irlap_cb *self,
- struct sk_buff *skb,
- struct irlap_info *info)
-{
- struct xid_frame *xid;
- discovery_t *discovery = NULL;
- __u8 *discovery_info;
- char *text;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
- if (!pskb_may_pull(skb, sizeof(struct xid_frame))) {
- net_err_ratelimited("%s: frame too short!\n", __func__);
- return;
- }
-
- xid = (struct xid_frame *) skb->data;
-
- info->daddr = le32_to_cpu(xid->saddr);
- info->saddr = le32_to_cpu(xid->daddr);
-
- /* Make sure frame is addressed to us */
- if ((info->saddr != self->saddr) && (info->saddr != BROADCAST)) {
- pr_debug("%s(), frame is not addressed to us!\n",
- __func__);
- return;
- }
-
- if ((discovery = kzalloc(sizeof(discovery_t), GFP_ATOMIC)) == NULL) {
- net_warn_ratelimited("%s: kmalloc failed!\n", __func__);
- return;
- }
-
- discovery->data.daddr = info->daddr;
- discovery->data.saddr = self->saddr;
- discovery->timestamp = jiffies;
-
- pr_debug("%s(), daddr=%08x\n", __func__,
- discovery->data.daddr);
-
- discovery_info = skb_pull(skb, sizeof(struct xid_frame));
-
- /* Get info returned from peer */
- discovery->data.hints[0] = discovery_info[0];
- if (discovery_info[0] & HINT_EXTENSION) {
- pr_debug("EXTENSION\n");
- discovery->data.hints[1] = discovery_info[1];
- discovery->data.charset = discovery_info[2];
- text = (char *) &discovery_info[3];
- } else {
- discovery->data.hints[1] = 0;
- discovery->data.charset = discovery_info[1];
- text = (char *) &discovery_info[2];
- }
- /*
- * Terminate info string, should be safe since this is where the
- * FCS bytes resides.
- */
- skb->data[skb->len] = '\0';
- strncpy(discovery->data.info, text, NICKNAME_MAX_LEN);
- discovery->name_len = strlen(discovery->data.info);
-
- info->discovery = discovery;
-
- irlap_do_event(self, RECV_DISCOVERY_XID_RSP, skb, info);
-}
-
-/*
- * Function irlap_recv_discovery_xid_cmd (skb, info)
- *
- * Received a XID discovery command
- *
- */
-static void irlap_recv_discovery_xid_cmd(struct irlap_cb *self,
- struct sk_buff *skb,
- struct irlap_info *info)
-{
- struct xid_frame *xid;
- discovery_t *discovery = NULL;
- __u8 *discovery_info;
- char *text;
-
- if (!pskb_may_pull(skb, sizeof(struct xid_frame))) {
- net_err_ratelimited("%s: frame too short!\n", __func__);
- return;
- }
-
- xid = (struct xid_frame *) skb->data;
-
- info->daddr = le32_to_cpu(xid->saddr);
- info->saddr = le32_to_cpu(xid->daddr);
-
- /* Make sure frame is addressed to us */
- if ((info->saddr != self->saddr) && (info->saddr != BROADCAST)) {
- pr_debug("%s(), frame is not addressed to us!\n",
- __func__);
- return;
- }
-
- switch (xid->flags & 0x03) {
- case 0x00:
- info->S = 1;
- break;
- case 0x01:
- info->S = 6;
- break;
- case 0x02:
- info->S = 8;
- break;
- case 0x03:
- info->S = 16;
- break;
- default:
- /* Error!! */
- return;
- }
- info->s = xid->slotnr;
-
- discovery_info = skb_pull(skb, sizeof(struct xid_frame));
-
- /*
- * Check if last frame
- */
- if (info->s == 0xff) {
- /* Check if things are sane at this point... */
- if((discovery_info == NULL) ||
- !pskb_may_pull(skb, 3)) {
- net_err_ratelimited("%s: discovery frame too short!\n",
- __func__);
- return;
- }
-
- /*
- * We now have some discovery info to deliver!
- */
- discovery = kzalloc(sizeof(discovery_t), GFP_ATOMIC);
- if (!discovery)
- return;
-
- discovery->data.daddr = info->daddr;
- discovery->data.saddr = self->saddr;
- discovery->timestamp = jiffies;
-
- discovery->data.hints[0] = discovery_info[0];
- if (discovery_info[0] & HINT_EXTENSION) {
- discovery->data.hints[1] = discovery_info[1];
- discovery->data.charset = discovery_info[2];
- text = (char *) &discovery_info[3];
- } else {
- discovery->data.hints[1] = 0;
- discovery->data.charset = discovery_info[1];
- text = (char *) &discovery_info[2];
- }
- /*
- * Terminate string, should be safe since this is where the
- * FCS bytes resides.
- */
- skb->data[skb->len] = '\0';
- strncpy(discovery->data.info, text, NICKNAME_MAX_LEN);
- discovery->name_len = strlen(discovery->data.info);
-
- info->discovery = discovery;
- } else
- info->discovery = NULL;
-
- irlap_do_event(self, RECV_DISCOVERY_XID_CMD, skb, info);
-}
-
-/*
- * Function irlap_send_rr_frame (self, command)
- *
- * Build and transmit RR (Receive Ready) frame. Notice that it is currently
- * only possible to send RR frames with the poll bit set.
- */
-void irlap_send_rr_frame(struct irlap_cb *self, int command)
-{
- struct sk_buff *tx_skb;
- struct rr_frame *frame;
-
- tx_skb = alloc_skb(sizeof(struct rr_frame), GFP_ATOMIC);
- if (!tx_skb)
- return;
-
- frame = skb_put(tx_skb, 2);
-
- frame->caddr = self->caddr;
- frame->caddr |= (command) ? CMD_FRAME : 0;
-
- frame->control = RR | PF_BIT | (self->vr << 5);
-
- irlap_queue_xmit(self, tx_skb);
-}
-
-/*
- * Function irlap_send_rd_frame (self)
- *
- * Request disconnect. Used by a secondary station to request the
- * disconnection of the link.
- */
-void irlap_send_rd_frame(struct irlap_cb *self)
-{
- struct sk_buff *tx_skb;
- struct rd_frame *frame;
-
- tx_skb = alloc_skb(sizeof(struct rd_frame), GFP_ATOMIC);
- if (!tx_skb)
- return;
-
- frame = skb_put(tx_skb, 2);
-
- frame->caddr = self->caddr;
- frame->control = RD_RSP | PF_BIT;
-
- irlap_queue_xmit(self, tx_skb);
-}
-
-/*
- * Function irlap_recv_rr_frame (skb, info)
- *
- * Received RR (Receive Ready) frame from peer station, no harm in
- * making it inline since its called only from one single place
- * (irlap_driver_rcv).
- */
-static inline void irlap_recv_rr_frame(struct irlap_cb *self,
- struct sk_buff *skb,
- struct irlap_info *info, int command)
-{
- info->nr = skb->data[1] >> 5;
-
- /* Check if this is a command or a response frame */
- if (command)
- irlap_do_event(self, RECV_RR_CMD, skb, info);
- else
- irlap_do_event(self, RECV_RR_RSP, skb, info);
-}
-
-/*
- * Function irlap_recv_rnr_frame (self, skb, info)
- *
- * Received RNR (Receive Not Ready) frame from peer station
- *
- */
-static void irlap_recv_rnr_frame(struct irlap_cb *self, struct sk_buff *skb,
- struct irlap_info *info, int command)
-{
- info->nr = skb->data[1] >> 5;
-
- pr_debug("%s(), nr=%d, %ld\n", __func__, info->nr, jiffies);
-
- if (command)
- irlap_do_event(self, RECV_RNR_CMD, skb, info);
- else
- irlap_do_event(self, RECV_RNR_RSP, skb, info);
-}
-
-static void irlap_recv_rej_frame(struct irlap_cb *self, struct sk_buff *skb,
- struct irlap_info *info, int command)
-{
- info->nr = skb->data[1] >> 5;
-
- /* Check if this is a command or a response frame */
- if (command)
- irlap_do_event(self, RECV_REJ_CMD, skb, info);
- else
- irlap_do_event(self, RECV_REJ_RSP, skb, info);
-}
-
-static void irlap_recv_srej_frame(struct irlap_cb *self, struct sk_buff *skb,
- struct irlap_info *info, int command)
-{
- info->nr = skb->data[1] >> 5;
-
- /* Check if this is a command or a response frame */
- if (command)
- irlap_do_event(self, RECV_SREJ_CMD, skb, info);
- else
- irlap_do_event(self, RECV_SREJ_RSP, skb, info);
-}
-
-static void irlap_recv_disc_frame(struct irlap_cb *self, struct sk_buff *skb,
- struct irlap_info *info, int command)
-{
- /* Check if this is a command or a response frame */
- if (command)
- irlap_do_event(self, RECV_DISC_CMD, skb, info);
- else
- irlap_do_event(self, RECV_RD_RSP, skb, info);
-}
-
-/*
- * Function irlap_recv_ua_frame (skb, frame)
- *
- * Received UA (Unnumbered Acknowledgement) frame
- *
- */
-static inline void irlap_recv_ua_frame(struct irlap_cb *self,
- struct sk_buff *skb,
- struct irlap_info *info)
-{
- irlap_do_event(self, RECV_UA_RSP, skb, info);
-}
-
-/*
- * Function irlap_send_data_primary(self, skb)
- *
- * Send I-frames as the primary station but without the poll bit set
- *
- */
-void irlap_send_data_primary(struct irlap_cb *self, struct sk_buff *skb)
-{
- struct sk_buff *tx_skb;
-
- if (skb->data[1] == I_FRAME) {
-
- /*
- * Insert frame sequence number (Vs) in control field before
- * inserting into transmit window queue.
- */
- skb->data[1] = I_FRAME | (self->vs << 1);
-
- /*
- * Insert frame in store, in case of retransmissions
- * Increase skb reference count, see irlap_do_event()
- */
- skb_get(skb);
- skb_queue_tail(&self->wx_list, skb);
-
- /* Copy buffer */
- tx_skb = skb_clone(skb, GFP_ATOMIC);
- if (tx_skb == NULL) {
- return;
- }
-
- self->vs = (self->vs + 1) % 8;
- self->ack_required = FALSE;
- self->window -= 1;
-
- irlap_send_i_frame( self, tx_skb, CMD_FRAME);
- } else {
- pr_debug("%s(), sending unreliable frame\n", __func__);
- irlap_send_ui_frame(self, skb_get(skb), self->caddr, CMD_FRAME);
- self->window -= 1;
- }
-}
-/*
- * Function irlap_send_data_primary_poll (self, skb)
- *
- * Send I(nformation) frame as primary with poll bit set
- */
-void irlap_send_data_primary_poll(struct irlap_cb *self, struct sk_buff *skb)
-{
- struct sk_buff *tx_skb;
- int transmission_time;
-
- /* Stop P timer */
- del_timer(&self->poll_timer);
-
- /* Is this reliable or unreliable data? */
- if (skb->data[1] == I_FRAME) {
-
- /*
- * Insert frame sequence number (Vs) in control field before
- * inserting into transmit window queue.
- */
- skb->data[1] = I_FRAME | (self->vs << 1);
-
- /*
- * Insert frame in store, in case of retransmissions
- * Increase skb reference count, see irlap_do_event()
- */
- skb_get(skb);
- skb_queue_tail(&self->wx_list, skb);
-
- /* Copy buffer */
- tx_skb = skb_clone(skb, GFP_ATOMIC);
- if (tx_skb == NULL) {
- return;
- }
-
- /*
- * Set poll bit if necessary. We do this to the copied
- * skb, since retransmitted need to set or clear the poll
- * bit depending on when they are sent.
- */
- tx_skb->data[1] |= PF_BIT;
-
- self->vs = (self->vs + 1) % 8;
- self->ack_required = FALSE;
-
- irlap_next_state(self, LAP_NRM_P);
- irlap_send_i_frame(self, tx_skb, CMD_FRAME);
- } else {
- pr_debug("%s(), sending unreliable frame\n", __func__);
-
- if (self->ack_required) {
- irlap_send_ui_frame(self, skb_get(skb), self->caddr, CMD_FRAME);
- irlap_next_state(self, LAP_NRM_P);
- irlap_send_rr_frame(self, CMD_FRAME);
- self->ack_required = FALSE;
- } else {
- skb->data[1] |= PF_BIT;
- irlap_next_state(self, LAP_NRM_P);
- irlap_send_ui_frame(self, skb_get(skb), self->caddr, CMD_FRAME);
- }
- }
-
- /* How much time we took for transmission of all frames.
- * We don't know, so let assume we used the full window. Jean II */
- transmission_time = self->final_timeout;
-
- /* Reset parameter so that we can fill next window */
- self->window = self->window_size;
-
-#ifdef CONFIG_IRDA_DYNAMIC_WINDOW
- /* Remove what we have not used. Just do a prorata of the
- * bytes left in window to window capacity.
- * See max_line_capacities[][] in qos.c for details. Jean II */
- transmission_time -= (self->final_timeout * self->bytes_left
- / self->line_capacity);
- pr_debug("%s() adjusting transmission_time : ft=%d, bl=%d, lc=%d -> tt=%d\n",
- __func__, self->final_timeout, self->bytes_left,
- self->line_capacity, transmission_time);
-
- /* We are allowed to transmit a maximum number of bytes again. */
- self->bytes_left = self->line_capacity;
-#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */
-
- /*
- * The network layer has a intermediate buffer between IrLAP
- * and the IrDA driver which can contain 8 frames. So, even
- * though IrLAP is currently sending the *last* frame of the
- * tx-window, the driver most likely has only just started
- * sending the *first* frame of the same tx-window.
- * I.e. we are always at the very beginning of or Tx window.
- * Now, we are supposed to set the final timer from the end
- * of our tx-window to let the other peer reply. So, we need
- * to add extra time to compensate for the fact that we
- * are really at the start of tx-window, otherwise the final timer
- * might expire before he can answer...
- * Jean II
- */
- irlap_start_final_timer(self, self->final_timeout + transmission_time);
-
- /*
- * The clever amongst you might ask why we do this adjustement
- * only here, and not in all the other cases in irlap_event.c.
- * In all those other case, we only send a very short management
- * frame (few bytes), so the adjustement would be lost in the
- * noise...
- * The exception of course is irlap_resend_rejected_frame().
- * Jean II */
-}
-
-/*
- * Function irlap_send_data_secondary_final (self, skb)
- *
- * Send I(nformation) frame as secondary with final bit set
- *
- */
-void irlap_send_data_secondary_final(struct irlap_cb *self,
- struct sk_buff *skb)
-{
- struct sk_buff *tx_skb = NULL;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
- IRDA_ASSERT(skb != NULL, return;);
-
- /* Is this reliable or unreliable data? */
- if (skb->data[1] == I_FRAME) {
-
- /*
- * Insert frame sequence number (Vs) in control field before
- * inserting into transmit window queue.
- */
- skb->data[1] = I_FRAME | (self->vs << 1);
-
- /*
- * Insert frame in store, in case of retransmissions
- * Increase skb reference count, see irlap_do_event()
- */
- skb_get(skb);
- skb_queue_tail(&self->wx_list, skb);
-
- tx_skb = skb_clone(skb, GFP_ATOMIC);
- if (tx_skb == NULL) {
- return;
- }
-
- tx_skb->data[1] |= PF_BIT;
-
- self->vs = (self->vs + 1) % 8;
- self->ack_required = FALSE;
-
- irlap_send_i_frame(self, tx_skb, RSP_FRAME);
- } else {
- if (self->ack_required) {
- irlap_send_ui_frame(self, skb_get(skb), self->caddr, RSP_FRAME);
- irlap_send_rr_frame(self, RSP_FRAME);
- self->ack_required = FALSE;
- } else {
- skb->data[1] |= PF_BIT;
- irlap_send_ui_frame(self, skb_get(skb), self->caddr, RSP_FRAME);
- }
- }
-
- self->window = self->window_size;
-#ifdef CONFIG_IRDA_DYNAMIC_WINDOW
- /* We are allowed to transmit a maximum number of bytes again. */
- self->bytes_left = self->line_capacity;
-#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */
-
- irlap_start_wd_timer(self, self->wd_timeout);
-}
-
-/*
- * Function irlap_send_data_secondary (self, skb)
- *
- * Send I(nformation) frame as secondary without final bit set
- *
- */
-void irlap_send_data_secondary(struct irlap_cb *self, struct sk_buff *skb)
-{
- struct sk_buff *tx_skb = NULL;
-
- /* Is this reliable or unreliable data? */
- if (skb->data[1] == I_FRAME) {
-
- /*
- * Insert frame sequence number (Vs) in control field before
- * inserting into transmit window queue.
- */
- skb->data[1] = I_FRAME | (self->vs << 1);
-
- /*
- * Insert frame in store, in case of retransmissions
- * Increase skb reference count, see irlap_do_event()
- */
- skb_get(skb);
- skb_queue_tail(&self->wx_list, skb);
-
- tx_skb = skb_clone(skb, GFP_ATOMIC);
- if (tx_skb == NULL) {
- return;
- }
-
- self->vs = (self->vs + 1) % 8;
- self->ack_required = FALSE;
- self->window -= 1;
-
- irlap_send_i_frame(self, tx_skb, RSP_FRAME);
- } else {
- irlap_send_ui_frame(self, skb_get(skb), self->caddr, RSP_FRAME);
- self->window -= 1;
- }
-}
-
-/*
- * Function irlap_resend_rejected_frames (nr)
- *
- * Resend frames which has not been acknowledged. Should be safe to
- * traverse the list without locking it since this function will only be
- * called from interrupt context (BH)
- */
-void irlap_resend_rejected_frames(struct irlap_cb *self, int command)
-{
- struct sk_buff *tx_skb;
- struct sk_buff *skb;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
- /* Resend unacknowledged frame(s) */
- skb_queue_walk(&self->wx_list, skb) {
- irlap_wait_min_turn_around(self, &self->qos_tx);
-
- /* We copy the skb to be retransmitted since we will have to
- * modify it. Cloning will confuse packet sniffers
- */
- /* tx_skb = skb_clone( skb, GFP_ATOMIC); */
- tx_skb = skb_copy(skb, GFP_ATOMIC);
- if (!tx_skb) {
- pr_debug("%s(), unable to copy\n", __func__);
- return;
- }
-
- /* Clear old Nr field + poll bit */
- tx_skb->data[1] &= 0x0f;
-
- /*
- * Set poll bit on the last frame retransmitted
- */
- if (skb_queue_is_last(&self->wx_list, skb))
- tx_skb->data[1] |= PF_BIT; /* Set p/f bit */
- else
- tx_skb->data[1] &= ~PF_BIT; /* Clear p/f bit */
-
- irlap_send_i_frame(self, tx_skb, command);
- }
-#if 0 /* Not yet */
- /*
- * We can now fill the window with additional data frames
- */
- while (!skb_queue_empty(&self->txq)) {
-
- pr_debug("%s(), sending additional frames!\n", __func__);
- if (self->window > 0) {
- skb = skb_dequeue( &self->txq);
- IRDA_ASSERT(skb != NULL, return;);
-
- /*
- * If send window > 1 then send frame with pf
- * bit cleared
- */
- if ((self->window > 1) &&
- !skb_queue_empty(&self->txq)) {
- irlap_send_data_primary(self, skb);
- } else {
- irlap_send_data_primary_poll(self, skb);
- }
- kfree_skb(skb);
- }
- }
-#endif
-}
-
-void irlap_resend_rejected_frame(struct irlap_cb *self, int command)
-{
- struct sk_buff *tx_skb;
- struct sk_buff *skb;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
- /* Resend unacknowledged frame(s) */
- skb = skb_peek(&self->wx_list);
- if (skb != NULL) {
- irlap_wait_min_turn_around(self, &self->qos_tx);
-
- /* We copy the skb to be retransmitted since we will have to
- * modify it. Cloning will confuse packet sniffers
- */
- /* tx_skb = skb_clone( skb, GFP_ATOMIC); */
- tx_skb = skb_copy(skb, GFP_ATOMIC);
- if (!tx_skb) {
- pr_debug("%s(), unable to copy\n", __func__);
- return;
- }
-
- /* Clear old Nr field + poll bit */
- tx_skb->data[1] &= 0x0f;
-
- /* Set poll/final bit */
- tx_skb->data[1] |= PF_BIT; /* Set p/f bit */
-
- irlap_send_i_frame(self, tx_skb, command);
- }
-}
-
-/*
- * Function irlap_send_ui_frame (self, skb, command)
- *
- * Contruct and transmit an Unnumbered Information (UI) frame
- *
- */
-void irlap_send_ui_frame(struct irlap_cb *self, struct sk_buff *skb,
- __u8 caddr, int command)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
- IRDA_ASSERT(skb != NULL, return;);
-
- /* Insert connection address */
- skb->data[0] = caddr | ((command) ? CMD_FRAME : 0);
-
- irlap_queue_xmit(self, skb);
-}
-
-/*
- * Function irlap_send_i_frame (skb)
- *
- * Contruct and transmit Information (I) frame
- */
-static void irlap_send_i_frame(struct irlap_cb *self, struct sk_buff *skb,
- int command)
-{
- /* Insert connection address */
- skb->data[0] = self->caddr;
- skb->data[0] |= (command) ? CMD_FRAME : 0;
-
- /* Insert next to receive (Vr) */
- skb->data[1] |= (self->vr << 5); /* insert nr */
-
- irlap_queue_xmit(self, skb);
-}
-
-/*
- * Function irlap_recv_i_frame (skb, frame)
- *
- * Receive and parse an I (Information) frame, no harm in making it inline
- * since it's called only from one single place (irlap_driver_rcv).
- */
-static inline void irlap_recv_i_frame(struct irlap_cb *self,
- struct sk_buff *skb,
- struct irlap_info *info, int command)
-{
- info->nr = skb->data[1] >> 5; /* Next to receive */
- info->pf = skb->data[1] & PF_BIT; /* Final bit */
- info->ns = (skb->data[1] >> 1) & 0x07; /* Next to send */
-
- /* Check if this is a command or a response frame */
- if (command)
- irlap_do_event(self, RECV_I_CMD, skb, info);
- else
- irlap_do_event(self, RECV_I_RSP, skb, info);
-}
-
-/*
- * Function irlap_recv_ui_frame (self, skb, info)
- *
- * Receive and parse an Unnumbered Information (UI) frame
- *
- */
-static void irlap_recv_ui_frame(struct irlap_cb *self, struct sk_buff *skb,
- struct irlap_info *info)
-{
- info->pf = skb->data[1] & PF_BIT; /* Final bit */
-
- irlap_do_event(self, RECV_UI_FRAME, skb, info);
-}
-
-/*
- * Function irlap_recv_frmr_frame (skb, frame)
- *
- * Received Frame Reject response.
- *
- */
-static void irlap_recv_frmr_frame(struct irlap_cb *self, struct sk_buff *skb,
- struct irlap_info *info)
-{
- __u8 *frame;
- int w, x, y, z;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
- IRDA_ASSERT(skb != NULL, return;);
- IRDA_ASSERT(info != NULL, return;);
-
- if (!pskb_may_pull(skb, 4)) {
- net_err_ratelimited("%s: frame too short!\n", __func__);
- return;
- }
-
- frame = skb->data;
-
- info->nr = frame[2] >> 5; /* Next to receive */
- info->pf = frame[2] & PF_BIT; /* Final bit */
- info->ns = (frame[2] >> 1) & 0x07; /* Next to send */
-
- w = frame[3] & 0x01;
- x = frame[3] & 0x02;
- y = frame[3] & 0x04;
- z = frame[3] & 0x08;
-
- if (w) {
- pr_debug("Rejected control field is undefined or not implemented\n");
- }
- if (x) {
- pr_debug("Rejected control field was invalid because it contained a non permitted I field\n");
- }
- if (y) {
- pr_debug("Received I field exceeded the maximum negotiated for the existing connection or exceeded the maximum this station supports if no connection exists\n");
- }
- if (z) {
- pr_debug("Rejected control field control field contained an invalid Nr count\n");
- }
- irlap_do_event(self, RECV_FRMR_RSP, skb, info);
-}
-
-/*
- * Function irlap_send_test_frame (self, daddr)
- *
- * Send a test frame response
- *
- */
-void irlap_send_test_frame(struct irlap_cb *self, __u8 caddr, __u32 daddr,
- struct sk_buff *cmd)
-{
- struct sk_buff *tx_skb;
- struct test_frame *frame;
-
- tx_skb = alloc_skb(cmd->len + sizeof(struct test_frame), GFP_ATOMIC);
- if (!tx_skb)
- return;
-
- /* Broadcast frames must include saddr and daddr fields */
- if (caddr == CBROADCAST) {
- frame = skb_put(tx_skb, sizeof(struct test_frame));
-
- /* Insert the swapped addresses */
- frame->saddr = cpu_to_le32(self->saddr);
- frame->daddr = cpu_to_le32(daddr);
- } else
- frame = skb_put(tx_skb, LAP_ADDR_HEADER + LAP_CTRL_HEADER);
-
- frame->caddr = caddr;
- frame->control = TEST_RSP | PF_BIT;
-
- /* Copy info */
- skb_put_data(tx_skb, cmd->data, cmd->len);
-
- /* Return to sender */
- irlap_wait_min_turn_around(self, &self->qos_tx);
- irlap_queue_xmit(self, tx_skb);
-}
-
-/*
- * Function irlap_recv_test_frame (self, skb)
- *
- * Receive a test frame
- *
- */
-static void irlap_recv_test_frame(struct irlap_cb *self, struct sk_buff *skb,
- struct irlap_info *info, int command)
-{
- struct test_frame *frame;
-
- if (!pskb_may_pull(skb, sizeof(*frame))) {
- net_err_ratelimited("%s: frame too short!\n", __func__);
- return;
- }
- frame = (struct test_frame *) skb->data;
-
- /* Broadcast frames must carry saddr and daddr fields */
- if (info->caddr == CBROADCAST) {
- if (skb->len < sizeof(struct test_frame)) {
- pr_debug("%s() test frame too short!\n",
- __func__);
- return;
- }
-
- /* Read and swap addresses */
- info->daddr = le32_to_cpu(frame->saddr);
- info->saddr = le32_to_cpu(frame->daddr);
-
- /* Make sure frame is addressed to us */
- if ((info->saddr != self->saddr) &&
- (info->saddr != BROADCAST)) {
- return;
- }
- }
-
- if (command)
- irlap_do_event(self, RECV_TEST_CMD, skb, info);
- else
- irlap_do_event(self, RECV_TEST_RSP, skb, info);
-}
-
-/*
- * Function irlap_driver_rcv (skb, netdev, ptype)
- *
- * Called when a frame is received. Dispatches the right receive function
- * for processing of the frame.
- *
- * Note on skb management :
- * After calling the higher layers of the IrDA stack, we always
- * kfree() the skb, which drop the reference count (and potentially
- * destroy it).
- * If a higher layer of the stack want to keep the skb around (to put
- * in a queue or pass it to the higher layer), it will need to use
- * skb_get() to keep a reference on it. This is usually done at the
- * LMP level in irlmp.c.
- * Jean II
- */
-int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev,
- struct packet_type *ptype, struct net_device *orig_dev)
-{
- struct irlap_info info;
- struct irlap_cb *self;
- int command;
- __u8 control;
- int ret = -1;
-
- if (!net_eq(dev_net(dev), &init_net))
- goto out;
-
- /* FIXME: should we get our own field? */
- self = (struct irlap_cb *) dev->atalk_ptr;
-
- /* If the net device is down, then IrLAP is gone! */
- if (!self || self->magic != LAP_MAGIC)
- goto err;
-
- /* We are no longer an "old" protocol, so we need to handle
- * share and non linear skbs. This should never happen, so
- * we don't need to be clever about it. Jean II */
- if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
- net_err_ratelimited("%s: can't clone shared skb!\n", __func__);
- goto err;
- }
-
- /* Check if frame is large enough for parsing */
- if (!pskb_may_pull(skb, 2)) {
- net_err_ratelimited("%s: frame too short!\n", __func__);
- goto err;
- }
-
- command = skb->data[0] & CMD_FRAME;
- info.caddr = skb->data[0] & CBROADCAST;
-
- info.pf = skb->data[1] & PF_BIT;
- info.control = skb->data[1] & ~PF_BIT; /* Mask away poll/final bit */
-
- control = info.control;
-
- /* First we check if this frame has a valid connection address */
- if ((info.caddr != self->caddr) && (info.caddr != CBROADCAST)) {
- pr_debug("%s(), wrong connection address!\n",
- __func__);
- goto out;
- }
- /*
- * Optimize for the common case and check if the frame is an
- * I(nformation) frame. Only I-frames have bit 0 set to 0
- */
- if (~control & 0x01) {
- irlap_recv_i_frame(self, skb, &info, command);
- goto out;
- }
- /*
- * We now check is the frame is an S(upervisory) frame. Only
- * S-frames have bit 0 set to 1 and bit 1 set to 0
- */
- if (~control & 0x02) {
- /*
- * Received S(upervisory) frame, check which frame type it is
- * only the first nibble is of interest
- */
- switch (control & 0x0f) {
- case RR:
- irlap_recv_rr_frame(self, skb, &info, command);
- break;
- case RNR:
- irlap_recv_rnr_frame(self, skb, &info, command);
- break;
- case REJ:
- irlap_recv_rej_frame(self, skb, &info, command);
- break;
- case SREJ:
- irlap_recv_srej_frame(self, skb, &info, command);
- break;
- default:
- net_warn_ratelimited("%s: Unknown S-frame %02x received!\n",
- __func__, info.control);
- break;
- }
- goto out;
- }
- /*
- * This must be a C(ontrol) frame
- */
- switch (control) {
- case XID_RSP:
- irlap_recv_discovery_xid_rsp(self, skb, &info);
- break;
- case XID_CMD:
- irlap_recv_discovery_xid_cmd(self, skb, &info);
- break;
- case SNRM_CMD:
- irlap_recv_snrm_cmd(self, skb, &info);
- break;
- case DM_RSP:
- irlap_do_event(self, RECV_DM_RSP, skb, &info);
- break;
- case DISC_CMD: /* And RD_RSP since they have the same value */
- irlap_recv_disc_frame(self, skb, &info, command);
- break;
- case TEST_CMD:
- irlap_recv_test_frame(self, skb, &info, command);
- break;
- case UA_RSP:
- irlap_recv_ua_frame(self, skb, &info);
- break;
- case FRMR_RSP:
- irlap_recv_frmr_frame(self, skb, &info);
- break;
- case UI_FRAME:
- irlap_recv_ui_frame(self, skb, &info);
- break;
- default:
- net_warn_ratelimited("%s: Unknown frame %02x received!\n",
- __func__, info.control);
- break;
- }
-out:
- ret = 0;
-err:
- /* Always drop our reference on the skb */
- dev_kfree_skb(skb);
- return ret;
-}
diff --git a/drivers/staging/irda/net/irlmp.c b/drivers/staging/irda/net/irlmp.c
deleted file mode 100644
index 7af618fb66c0..000000000000
--- a/drivers/staging/irda/net/irlmp.c
+++ /dev/null
@@ -1,1996 +0,0 @@
-/*********************************************************************
- *
- * Filename: irlmp.c
- * Version: 1.0
- * Description: IrDA Link Management Protocol (LMP) layer
- * Status: Stable.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sun Aug 17 20:54:32 1997
- * Modified at: Wed Jan 5 11:26:03 2000
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>,
- * All Rights Reserved.
- * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/skbuff.h>
-#include <linux/types.h>
-#include <linux/proc_fs.h>
-#include <linux/init.h>
-#include <linux/kmod.h>
-#include <linux/random.h>
-#include <linux/seq_file.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/timer.h>
-#include <net/irda/qos.h>
-#include <net/irda/irlap.h>
-#include <net/irda/iriap.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/irlmp_frame.h>
-
-#include <asm/unaligned.h>
-
-static __u8 irlmp_find_free_slsap(void);
-static int irlmp_slsap_inuse(__u8 slsap_sel);
-
-/* Master structure */
-struct irlmp_cb *irlmp = NULL;
-
-/* These can be altered by the sysctl interface */
-int sysctl_discovery = 0;
-int sysctl_discovery_timeout = 3; /* 3 seconds by default */
-int sysctl_discovery_slots = 6; /* 6 slots by default */
-int sysctl_lap_keepalive_time = LM_IDLE_TIMEOUT * 1000 / HZ;
-char sysctl_devname[65];
-
-static const char *irlmp_reasons[] = {
- "ERROR, NOT USED",
- "LM_USER_REQUEST",
- "LM_LAP_DISCONNECT",
- "LM_CONNECT_FAILURE",
- "LM_LAP_RESET",
- "LM_INIT_DISCONNECT",
- "ERROR, NOT USED",
- "UNKNOWN",
-};
-
-const char *irlmp_reason_str(LM_REASON reason)
-{
- reason = min_t(size_t, reason, ARRAY_SIZE(irlmp_reasons) - 1);
- return irlmp_reasons[reason];
-}
-
-/*
- * Function irlmp_init (void)
- *
- * Create (allocate) the main IrLMP structure
- *
- */
-int __init irlmp_init(void)
-{
- /* Initialize the irlmp structure. */
- irlmp = kzalloc( sizeof(struct irlmp_cb), GFP_KERNEL);
- if (irlmp == NULL)
- return -ENOMEM;
-
- irlmp->magic = LMP_MAGIC;
-
- irlmp->clients = hashbin_new(HB_LOCK);
- irlmp->services = hashbin_new(HB_LOCK);
- irlmp->links = hashbin_new(HB_LOCK);
- irlmp->unconnected_lsaps = hashbin_new(HB_LOCK);
- irlmp->cachelog = hashbin_new(HB_NOLOCK);
-
- if ((irlmp->clients == NULL) ||
- (irlmp->services == NULL) ||
- (irlmp->links == NULL) ||
- (irlmp->unconnected_lsaps == NULL) ||
- (irlmp->cachelog == NULL)) {
- return -ENOMEM;
- }
-
- spin_lock_init(&irlmp->cachelog->hb_spinlock);
-
- irlmp->last_lsap_sel = 0x0f; /* Reserved 0x00-0x0f */
- strcpy(sysctl_devname, "Linux");
-
- timer_setup(&irlmp->discovery_timer, NULL, 0);
-
- /* Do discovery every 3 seconds, conditionally */
- if (sysctl_discovery)
- irlmp_start_discovery_timer(irlmp,
- sysctl_discovery_timeout*HZ);
-
- return 0;
-}
-
-/*
- * Function irlmp_cleanup (void)
- *
- * Remove IrLMP layer
- *
- */
-void irlmp_cleanup(void)
-{
- /* Check for main structure */
- IRDA_ASSERT(irlmp != NULL, return;);
- IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return;);
-
- del_timer(&irlmp->discovery_timer);
-
- hashbin_delete(irlmp->links, (FREE_FUNC) kfree);
- hashbin_delete(irlmp->unconnected_lsaps, (FREE_FUNC) kfree);
- hashbin_delete(irlmp->clients, (FREE_FUNC) kfree);
- hashbin_delete(irlmp->services, (FREE_FUNC) kfree);
- hashbin_delete(irlmp->cachelog, (FREE_FUNC) kfree);
-
- /* De-allocate main structure */
- kfree(irlmp);
- irlmp = NULL;
-}
-
-/*
- * Function irlmp_open_lsap (slsap, notify)
- *
- * Register with IrLMP and create a local LSAP,
- * returns handle to LSAP.
- */
-struct lsap_cb *irlmp_open_lsap(__u8 slsap_sel, notify_t *notify, __u8 pid)
-{
- struct lsap_cb *self;
-
- IRDA_ASSERT(notify != NULL, return NULL;);
- IRDA_ASSERT(irlmp != NULL, return NULL;);
- IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return NULL;);
- IRDA_ASSERT(notify->instance != NULL, return NULL;);
-
- /* Does the client care which Source LSAP selector it gets? */
- if (slsap_sel == LSAP_ANY) {
- slsap_sel = irlmp_find_free_slsap();
- if (!slsap_sel)
- return NULL;
- } else if (irlmp_slsap_inuse(slsap_sel))
- return NULL;
-
- /* Allocate new instance of a LSAP connection */
- self = kzalloc(sizeof(struct lsap_cb), GFP_ATOMIC);
- if (self == NULL)
- return NULL;
-
- self->magic = LMP_LSAP_MAGIC;
- self->slsap_sel = slsap_sel;
-
- /* Fix connectionless LSAP's */
- if (slsap_sel == LSAP_CONNLESS) {
-#ifdef CONFIG_IRDA_ULTRA
- self->dlsap_sel = LSAP_CONNLESS;
- self->pid = pid;
-#endif /* CONFIG_IRDA_ULTRA */
- } else
- self->dlsap_sel = LSAP_ANY;
- /* self->connected = FALSE; -> already NULL via memset() */
-
- timer_setup(&self->watchdog_timer, NULL, 0);
-
- self->notify = *notify;
-
- self->lsap_state = LSAP_DISCONNECTED;
-
- /* Insert into queue of unconnected LSAPs */
- hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) self,
- (long) self, NULL);
-
- return self;
-}
-EXPORT_SYMBOL(irlmp_open_lsap);
-
-/*
- * Function __irlmp_close_lsap (self)
- *
- * Remove an instance of LSAP
- */
-static void __irlmp_close_lsap(struct lsap_cb *self)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
-
- /*
- * Set some of the variables to preset values
- */
- self->magic = 0;
- del_timer(&self->watchdog_timer); /* Important! */
-
- if (self->conn_skb)
- dev_kfree_skb(self->conn_skb);
-
- kfree(self);
-}
-
-/*
- * Function irlmp_close_lsap (self)
- *
- * Close and remove LSAP
- *
- */
-void irlmp_close_lsap(struct lsap_cb *self)
-{
- struct lap_cb *lap;
- struct lsap_cb *lsap = NULL;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
-
- /*
- * Find out if we should remove this LSAP from a link or from the
- * list of unconnected lsaps (not associated with a link)
- */
- lap = self->lap;
- if (lap) {
- IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return;);
- /* We might close a LSAP before it has completed the
- * connection setup. In those case, higher layers won't
- * send a proper disconnect request. Harmless, except
- * that we will forget to close LAP... - Jean II */
- if(self->lsap_state != LSAP_DISCONNECTED) {
- self->lsap_state = LSAP_DISCONNECTED;
- irlmp_do_lap_event(self->lap,
- LM_LAP_DISCONNECT_REQUEST, NULL);
- }
- /* Now, remove from the link */
- lsap = hashbin_remove(lap->lsaps, (long) self, NULL);
-#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
- lap->cache.valid = FALSE;
-#endif
- }
- self->lap = NULL;
- /* Check if we found the LSAP! If not then try the unconnected lsaps */
- if (!lsap) {
- lsap = hashbin_remove(irlmp->unconnected_lsaps, (long) self,
- NULL);
- }
- if (!lsap) {
- pr_debug("%s(), Looks like somebody has removed me already!\n",
- __func__);
- return;
- }
- __irlmp_close_lsap(self);
-}
-EXPORT_SYMBOL(irlmp_close_lsap);
-
-/*
- * Function irlmp_register_irlap (saddr, notify)
- *
- * Register IrLAP layer with IrLMP. There is possible to have multiple
- * instances of the IrLAP layer, each connected to different IrDA ports
- *
- */
-void irlmp_register_link(struct irlap_cb *irlap, __u32 saddr, notify_t *notify)
-{
- struct lap_cb *lap;
-
- IRDA_ASSERT(irlmp != NULL, return;);
- IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return;);
- IRDA_ASSERT(notify != NULL, return;);
-
- /*
- * Allocate new instance of a LSAP connection
- */
- lap = kzalloc(sizeof(struct lap_cb), GFP_KERNEL);
- if (lap == NULL)
- return;
-
- lap->irlap = irlap;
- lap->magic = LMP_LAP_MAGIC;
- lap->saddr = saddr;
- lap->daddr = DEV_ADDR_ANY;
-#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
- lap->cache.valid = FALSE;
-#endif
- lap->lsaps = hashbin_new(HB_LOCK);
- if (lap->lsaps == NULL) {
- net_warn_ratelimited("%s(), unable to kmalloc lsaps\n",
- __func__);
- kfree(lap);
- return;
- }
-
- lap->lap_state = LAP_STANDBY;
-
- timer_setup(&lap->idle_timer, NULL, 0);
-
- /*
- * Insert into queue of LMP links
- */
- hashbin_insert(irlmp->links, (irda_queue_t *) lap, lap->saddr, NULL);
-
- /*
- * We set only this variable so IrLAP can tell us on which link the
- * different events happened on
- */
- irda_notify_init(notify);
- notify->instance = lap;
-}
-
-/*
- * Function irlmp_unregister_irlap (saddr)
- *
- * IrLAP layer has been removed!
- *
- */
-void irlmp_unregister_link(__u32 saddr)
-{
- struct lap_cb *link;
-
- /* We must remove ourselves from the hashbin *first*. This ensure
- * that no more LSAPs will be open on this link and no discovery
- * will be triggered anymore. Jean II */
- link = hashbin_remove(irlmp->links, saddr, NULL);
- if (link) {
- IRDA_ASSERT(link->magic == LMP_LAP_MAGIC, return;);
-
- /* Kill all the LSAPs on this link. Jean II */
- link->reason = LAP_DISC_INDICATION;
- link->daddr = DEV_ADDR_ANY;
- irlmp_do_lap_event(link, LM_LAP_DISCONNECT_INDICATION, NULL);
-
- /* Remove all discoveries discovered at this link */
- irlmp_expire_discoveries(irlmp->cachelog, link->saddr, TRUE);
-
- /* Final cleanup */
- del_timer(&link->idle_timer);
- link->magic = 0;
- hashbin_delete(link->lsaps, (FREE_FUNC) __irlmp_close_lsap);
- kfree(link);
- }
-}
-
-/*
- * Function irlmp_connect_request (handle, dlsap, userdata)
- *
- * Connect with a peer LSAP
- *
- */
-int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel,
- __u32 saddr, __u32 daddr,
- struct qos_info *qos, struct sk_buff *userdata)
-{
- struct sk_buff *tx_skb = userdata;
- struct lap_cb *lap;
- struct lsap_cb *lsap;
- int ret;
-
- IRDA_ASSERT(self != NULL, return -EBADR;);
- IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -EBADR;);
-
- pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x, saddr=%08x, daddr=%08x\n",
- __func__, self->slsap_sel, dlsap_sel, saddr, daddr);
-
- if (test_bit(0, &self->connected)) {
- ret = -EISCONN;
- goto err;
- }
-
- /* Client must supply destination device address */
- if (!daddr) {
- ret = -EINVAL;
- goto err;
- }
-
- /* Any userdata? */
- if (tx_skb == NULL) {
- tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
- if (!tx_skb)
- return -ENOMEM;
-
- skb_reserve(tx_skb, LMP_MAX_HEADER);
- }
-
- /* Make room for MUX control header (3 bytes) */
- IRDA_ASSERT(skb_headroom(tx_skb) >= LMP_CONTROL_HEADER, return -1;);
- skb_push(tx_skb, LMP_CONTROL_HEADER);
-
- self->dlsap_sel = dlsap_sel;
-
- /*
- * Find the link to where we should try to connect since there may
- * be more than one IrDA port on this machine. If the client has
- * passed us the saddr (and already knows which link to use), then
- * we use that to find the link, if not then we have to look in the
- * discovery log and check if any of the links has discovered a
- * device with the given daddr
- */
- if ((!saddr) || (saddr == DEV_ADDR_ANY)) {
- discovery_t *discovery;
- unsigned long flags;
-
- spin_lock_irqsave(&irlmp->cachelog->hb_spinlock, flags);
- if (daddr != DEV_ADDR_ANY)
- discovery = hashbin_find(irlmp->cachelog, daddr, NULL);
- else {
- pr_debug("%s(), no daddr\n", __func__);
- discovery = (discovery_t *)
- hashbin_get_first(irlmp->cachelog);
- }
-
- if (discovery) {
- saddr = discovery->data.saddr;
- daddr = discovery->data.daddr;
- }
- spin_unlock_irqrestore(&irlmp->cachelog->hb_spinlock, flags);
- }
- lap = hashbin_lock_find(irlmp->links, saddr, NULL);
- if (lap == NULL) {
- pr_debug("%s(), Unable to find a usable link!\n", __func__);
- ret = -EHOSTUNREACH;
- goto err;
- }
-
- /* Check if LAP is disconnected or already connected */
- if (lap->daddr == DEV_ADDR_ANY)
- lap->daddr = daddr;
- else if (lap->daddr != daddr) {
- /* Check if some LSAPs are active on this LAP */
- if (HASHBIN_GET_SIZE(lap->lsaps) == 0) {
- /* No active connection, but LAP hasn't been
- * disconnected yet (waiting for timeout in LAP).
- * Maybe we could give LAP a bit of help in this case.
- */
- pr_debug("%s(), sorry, but I'm waiting for LAP to timeout!\n",
- __func__);
- ret = -EAGAIN;
- goto err;
- }
-
- /* LAP is already connected to a different node, and LAP
- * can only talk to one node at a time */
- pr_debug("%s(), sorry, but link is busy!\n", __func__);
- ret = -EBUSY;
- goto err;
- }
-
- self->lap = lap;
-
- /*
- * Remove LSAP from list of unconnected LSAPs and insert it into the
- * list of connected LSAPs for the particular link
- */
- lsap = hashbin_remove(irlmp->unconnected_lsaps, (long) self, NULL);
-
- IRDA_ASSERT(lsap != NULL, return -1;);
- IRDA_ASSERT(lsap->magic == LMP_LSAP_MAGIC, return -1;);
- IRDA_ASSERT(lsap->lap != NULL, return -1;);
- IRDA_ASSERT(lsap->lap->magic == LMP_LAP_MAGIC, return -1;);
-
- hashbin_insert(self->lap->lsaps, (irda_queue_t *) self, (long) self,
- NULL);
-
- set_bit(0, &self->connected); /* TRUE */
-
- /*
- * User supplied qos specifications?
- */
- if (qos)
- self->qos = *qos;
-
- irlmp_do_lsap_event(self, LM_CONNECT_REQUEST, tx_skb);
-
- /* Drop reference count - see irlap_data_request(). */
- dev_kfree_skb(tx_skb);
-
- return 0;
-
-err:
- /* Cleanup */
- if(tx_skb)
- dev_kfree_skb(tx_skb);
- return ret;
-}
-EXPORT_SYMBOL(irlmp_connect_request);
-
-/*
- * Function irlmp_connect_indication (self)
- *
- * Incoming connection
- *
- */
-void irlmp_connect_indication(struct lsap_cb *self, struct sk_buff *skb)
-{
- int max_seg_size;
- int lap_header_size;
- int max_header_size;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
- IRDA_ASSERT(skb != NULL, return;);
- IRDA_ASSERT(self->lap != NULL, return;);
-
- pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x\n",
- __func__, self->slsap_sel, self->dlsap_sel);
-
- /* Note : self->lap is set in irlmp_link_data_indication(),
- * (case CONNECT_CMD:) because we have no way to set it here.
- * Similarly, self->dlsap_sel is usually set in irlmp_find_lsap().
- * Jean II */
-
- self->qos = *self->lap->qos;
-
- max_seg_size = self->lap->qos->data_size.value-LMP_HEADER;
- lap_header_size = IRLAP_GET_HEADER_SIZE(self->lap->irlap);
- max_header_size = LMP_HEADER + lap_header_size;
-
- /* Hide LMP_CONTROL_HEADER header from layer above */
- skb_pull(skb, LMP_CONTROL_HEADER);
-
- if (self->notify.connect_indication) {
- /* Don't forget to refcount it - see irlap_driver_rcv(). */
- skb_get(skb);
- self->notify.connect_indication(self->notify.instance, self,
- &self->qos, max_seg_size,
- max_header_size, skb);
- }
-}
-
-/*
- * Function irlmp_connect_response (handle, userdata)
- *
- * Service user is accepting connection
- *
- */
-int irlmp_connect_response(struct lsap_cb *self, struct sk_buff *userdata)
-{
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
- IRDA_ASSERT(userdata != NULL, return -1;);
-
- /* We set the connected bit and move the lsap to the connected list
- * in the state machine itself. Jean II */
-
- pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x\n",
- __func__, self->slsap_sel, self->dlsap_sel);
-
- /* Make room for MUX control header (3 bytes) */
- IRDA_ASSERT(skb_headroom(userdata) >= LMP_CONTROL_HEADER, return -1;);
- skb_push(userdata, LMP_CONTROL_HEADER);
-
- irlmp_do_lsap_event(self, LM_CONNECT_RESPONSE, userdata);
-
- /* Drop reference count - see irlap_data_request(). */
- dev_kfree_skb(userdata);
-
- return 0;
-}
-EXPORT_SYMBOL(irlmp_connect_response);
-
-/*
- * Function irlmp_connect_confirm (handle, skb)
- *
- * LSAP connection confirmed peer device!
- */
-void irlmp_connect_confirm(struct lsap_cb *self, struct sk_buff *skb)
-{
- int max_header_size;
- int lap_header_size;
- int max_seg_size;
-
- IRDA_ASSERT(skb != NULL, return;);
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
- IRDA_ASSERT(self->lap != NULL, return;);
-
- self->qos = *self->lap->qos;
-
- max_seg_size = self->lap->qos->data_size.value-LMP_HEADER;
- lap_header_size = IRLAP_GET_HEADER_SIZE(self->lap->irlap);
- max_header_size = LMP_HEADER + lap_header_size;
-
- pr_debug("%s(), max_header_size=%d\n",
- __func__, max_header_size);
-
- /* Hide LMP_CONTROL_HEADER header from layer above */
- skb_pull(skb, LMP_CONTROL_HEADER);
-
- if (self->notify.connect_confirm) {
- /* Don't forget to refcount it - see irlap_driver_rcv() */
- skb_get(skb);
- self->notify.connect_confirm(self->notify.instance, self,
- &self->qos, max_seg_size,
- max_header_size, skb);
- }
-}
-
-/*
- * Function irlmp_dup (orig, instance)
- *
- * Duplicate LSAP, can be used by servers to confirm a connection on a
- * new LSAP so it can keep listening on the old one.
- *
- */
-struct lsap_cb *irlmp_dup(struct lsap_cb *orig, void *instance)
-{
- struct lsap_cb *new;
- unsigned long flags;
-
- spin_lock_irqsave(&irlmp->unconnected_lsaps->hb_spinlock, flags);
-
- /* Only allowed to duplicate unconnected LSAP's, and only LSAPs
- * that have received a connect indication. Jean II */
- if ((!hashbin_find(irlmp->unconnected_lsaps, (long) orig, NULL)) ||
- (orig->lap == NULL)) {
- pr_debug("%s(), invalid LSAP (wrong state)\n",
- __func__);
- spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock,
- flags);
- return NULL;
- }
-
- /* Allocate a new instance */
- new = kmemdup(orig, sizeof(*new), GFP_ATOMIC);
- if (!new) {
- pr_debug("%s(), unable to kmalloc\n", __func__);
- spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock,
- flags);
- return NULL;
- }
- /* new->lap = orig->lap; => done in the memcpy() */
- /* new->slsap_sel = orig->slsap_sel; => done in the memcpy() */
- new->conn_skb = NULL;
-
- spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags);
-
- /* Not everything is the same */
- new->notify.instance = instance;
-
- timer_setup(&new->watchdog_timer, NULL, 0);
-
- hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) new,
- (long) new, NULL);
-
-#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
- /* Make sure that we invalidate the LSAP cache */
- new->lap->cache.valid = FALSE;
-#endif /* CONFIG_IRDA_CACHE_LAST_LSAP */
-
- return new;
-}
-
-/*
- * Function irlmp_disconnect_request (handle, userdata)
- *
- * The service user is requesting disconnection, this will not remove the
- * LSAP, but only mark it as disconnected
- */
-int irlmp_disconnect_request(struct lsap_cb *self, struct sk_buff *userdata)
-{
- struct lsap_cb *lsap;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
- IRDA_ASSERT(userdata != NULL, return -1;);
-
- /* Already disconnected ?
- * There is a race condition between irlmp_disconnect_indication()
- * and us that might mess up the hashbins below. This fixes it.
- * Jean II */
- if (! test_and_clear_bit(0, &self->connected)) {
- pr_debug("%s(), already disconnected!\n", __func__);
- dev_kfree_skb(userdata);
- return -1;
- }
-
- skb_push(userdata, LMP_CONTROL_HEADER);
-
- /*
- * Do the event before the other stuff since we must know
- * which lap layer that the frame should be transmitted on
- */
- irlmp_do_lsap_event(self, LM_DISCONNECT_REQUEST, userdata);
-
- /* Drop reference count - see irlap_data_request(). */
- dev_kfree_skb(userdata);
-
- /*
- * Remove LSAP from list of connected LSAPs for the particular link
- * and insert it into the list of unconnected LSAPs
- */
- IRDA_ASSERT(self->lap != NULL, return -1;);
- IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;);
- IRDA_ASSERT(self->lap->lsaps != NULL, return -1;);
-
- lsap = hashbin_remove(self->lap->lsaps, (long) self, NULL);
-#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
- self->lap->cache.valid = FALSE;
-#endif
-
- IRDA_ASSERT(lsap != NULL, return -1;);
- IRDA_ASSERT(lsap->magic == LMP_LSAP_MAGIC, return -1;);
- IRDA_ASSERT(lsap == self, return -1;);
-
- hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) self,
- (long) self, NULL);
-
- /* Reset some values */
- self->dlsap_sel = LSAP_ANY;
- self->lap = NULL;
-
- return 0;
-}
-EXPORT_SYMBOL(irlmp_disconnect_request);
-
-/*
- * Function irlmp_disconnect_indication (reason, userdata)
- *
- * LSAP is being closed!
- */
-void irlmp_disconnect_indication(struct lsap_cb *self, LM_REASON reason,
- struct sk_buff *skb)
-{
- struct lsap_cb *lsap;
-
- pr_debug("%s(), reason=%s [%d]\n", __func__,
- irlmp_reason_str(reason), reason);
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
-
- pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x\n",
- __func__, self->slsap_sel, self->dlsap_sel);
-
- /* Already disconnected ?
- * There is a race condition between irlmp_disconnect_request()
- * and us that might mess up the hashbins below. This fixes it.
- * Jean II */
- if (! test_and_clear_bit(0, &self->connected)) {
- pr_debug("%s(), already disconnected!\n", __func__);
- return;
- }
-
- /*
- * Remove association between this LSAP and the link it used
- */
- IRDA_ASSERT(self->lap != NULL, return;);
- IRDA_ASSERT(self->lap->lsaps != NULL, return;);
-
- lsap = hashbin_remove(self->lap->lsaps, (long) self, NULL);
-#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
- self->lap->cache.valid = FALSE;
-#endif
-
- IRDA_ASSERT(lsap != NULL, return;);
- IRDA_ASSERT(lsap == self, return;);
- hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) lsap,
- (long) lsap, NULL);
-
- self->dlsap_sel = LSAP_ANY;
- self->lap = NULL;
-
- /*
- * Inform service user
- */
- if (self->notify.disconnect_indication) {
- /* Don't forget to refcount it - see irlap_driver_rcv(). */
- if(skb)
- skb_get(skb);
- self->notify.disconnect_indication(self->notify.instance,
- self, reason, skb);
- } else {
- pr_debug("%s(), no handler\n", __func__);
- }
-}
-
-/*
- * Function irlmp_do_expiry (void)
- *
- * Do a cleanup of the discovery log (remove old entries)
- *
- * Note : separate from irlmp_do_discovery() so that we can handle
- * passive discovery properly.
- */
-void irlmp_do_expiry(void)
-{
- struct lap_cb *lap;
-
- /*
- * Expire discovery on all links which are *not* connected.
- * On links which are connected, we can't do discovery
- * anymore and can't refresh the log, so we freeze the
- * discovery log to keep info about the device we are
- * connected to.
- * This info is mandatory if we want irlmp_connect_request()
- * to work properly. - Jean II
- */
- lap = (struct lap_cb *) hashbin_get_first(irlmp->links);
- while (lap != NULL) {
- IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return;);
-
- if (lap->lap_state == LAP_STANDBY) {
- /* Expire discoveries discovered on this link */
- irlmp_expire_discoveries(irlmp->cachelog, lap->saddr,
- FALSE);
- }
- lap = (struct lap_cb *) hashbin_get_next(irlmp->links);
- }
-}
-
-/*
- * Function irlmp_do_discovery (nslots)
- *
- * Do some discovery on all links
- *
- * Note : log expiry is done above.
- */
-void irlmp_do_discovery(int nslots)
-{
- struct lap_cb *lap;
- __u16 *data_hintsp;
-
- /* Make sure the value is sane */
- if ((nslots != 1) && (nslots != 6) && (nslots != 8) && (nslots != 16)){
- net_warn_ratelimited("%s: invalid value for number of slots!\n",
- __func__);
- nslots = sysctl_discovery_slots = 8;
- }
-
- /* Construct new discovery info to be used by IrLAP, */
- data_hintsp = (__u16 *) irlmp->discovery_cmd.data.hints;
- put_unaligned(irlmp->hints.word, data_hintsp);
-
- /*
- * Set character set for device name (we use ASCII), and
- * copy device name. Remember to make room for a \0 at the
- * end
- */
- irlmp->discovery_cmd.data.charset = CS_ASCII;
- strncpy(irlmp->discovery_cmd.data.info, sysctl_devname,
- NICKNAME_MAX_LEN);
- irlmp->discovery_cmd.name_len = strlen(irlmp->discovery_cmd.data.info);
- irlmp->discovery_cmd.nslots = nslots;
-
- /*
- * Try to send discovery packets on all links
- */
- lap = (struct lap_cb *) hashbin_get_first(irlmp->links);
- while (lap != NULL) {
- IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return;);
-
- if (lap->lap_state == LAP_STANDBY) {
- /* Try to discover */
- irlmp_do_lap_event(lap, LM_LAP_DISCOVERY_REQUEST,
- NULL);
- }
- lap = (struct lap_cb *) hashbin_get_next(irlmp->links);
- }
-}
-
-/*
- * Function irlmp_discovery_request (nslots)
- *
- * Do a discovery of devices in front of the computer
- *
- * If the caller has registered a client discovery callback, this
- * allow him to receive the full content of the discovery log through
- * this callback (as normally he will receive only new discoveries).
- */
-void irlmp_discovery_request(int nslots)
-{
- /* Return current cached discovery log (in full) */
- irlmp_discovery_confirm(irlmp->cachelog, DISCOVERY_LOG);
-
- /*
- * Start a single discovery operation if discovery is not already
- * running
- */
- if (!sysctl_discovery) {
- /* Check if user wants to override the default */
- if (nslots == DISCOVERY_DEFAULT_SLOTS)
- nslots = sysctl_discovery_slots;
-
- irlmp_do_discovery(nslots);
- /* Note : we never do expiry here. Expiry will run on the
- * discovery timer regardless of the state of sysctl_discovery
- * Jean II */
- }
-}
-EXPORT_SYMBOL(irlmp_discovery_request);
-
-/*
- * Function irlmp_get_discoveries (pn, mask, slots)
- *
- * Return the current discovery log
- *
- * If discovery is not enabled, you should call this function again
- * after 1 or 2 seconds (i.e. after discovery has been done).
- */
-struct irda_device_info *irlmp_get_discoveries(int *pn, __u16 mask, int nslots)
-{
- /* If discovery is not enabled, it's likely that the discovery log
- * will be empty. So, we trigger a single discovery, so that next
- * time the user call us there might be some results in the log.
- * Jean II
- */
- if (!sysctl_discovery) {
- /* Check if user wants to override the default */
- if (nslots == DISCOVERY_DEFAULT_SLOTS)
- nslots = sysctl_discovery_slots;
-
- /* Start discovery - will complete sometime later */
- irlmp_do_discovery(nslots);
- /* Note : we never do expiry here. Expiry will run on the
- * discovery timer regardless of the state of sysctl_discovery
- * Jean II */
- }
-
- /* Return current cached discovery log */
- return irlmp_copy_discoveries(irlmp->cachelog, pn, mask, TRUE);
-}
-EXPORT_SYMBOL(irlmp_get_discoveries);
-
-/*
- * Function irlmp_notify_client (log)
- *
- * Notify all about discovered devices
- *
- * Clients registered with IrLMP are :
- * o IrComm
- * o IrLAN
- * o Any socket (in any state - ouch, that may be a lot !)
- * The client may have defined a callback to be notified in case of
- * partial/selective discovery based on the hints that it passed to IrLMP.
- */
-static inline void
-irlmp_notify_client(irlmp_client_t *client,
- hashbin_t *log, DISCOVERY_MODE mode)
-{
- discinfo_t *discoveries; /* Copy of the discovery log */
- int number; /* Number of nodes in the log */
- int i;
-
- /* Check if client wants or not partial/selective log (optimisation) */
- if (!client->disco_callback)
- return;
-
- /*
- * Locking notes :
- * the old code was manipulating the log directly, which was
- * very racy. Now, we use copy_discoveries, that protects
- * itself while dumping the log for us.
- * The overhead of the copy is compensated by the fact that
- * we only pass new discoveries in normal mode and don't
- * pass the same old entry every 3s to the caller as we used
- * to do (virtual function calling is expensive).
- * Jean II
- */
-
- /*
- * Now, check all discovered devices (if any), and notify client
- * only about the services that the client is interested in
- * We also notify only about the new devices unless the caller
- * explicitly request a dump of the log. Jean II
- */
- discoveries = irlmp_copy_discoveries(log, &number,
- client->hint_mask.word,
- (mode == DISCOVERY_LOG));
- /* Check if the we got some results */
- if (discoveries == NULL)
- return; /* No nodes discovered */
-
- /* Pass all entries to the listener */
- for(i = 0; i < number; i++)
- client->disco_callback(&(discoveries[i]), mode, client->priv);
-
- /* Free up our buffer */
- kfree(discoveries);
-}
-
-/*
- * Function irlmp_discovery_confirm ( self, log)
- *
- * Some device(s) answered to our discovery request! Check to see which
- * device it is, and give indication to the client(s)
- *
- */
-void irlmp_discovery_confirm(hashbin_t *log, DISCOVERY_MODE mode)
-{
- irlmp_client_t *client;
- irlmp_client_t *client_next;
-
- IRDA_ASSERT(log != NULL, return;);
-
- if (!(HASHBIN_GET_SIZE(log)))
- return;
-
- /* For each client - notify callback may touch client list */
- client = (irlmp_client_t *) hashbin_get_first(irlmp->clients);
- while (NULL != hashbin_find_next(irlmp->clients, (long) client, NULL,
- (void *) &client_next) ) {
- /* Check if we should notify client */
- irlmp_notify_client(client, log, mode);
-
- client = client_next;
- }
-}
-
-/*
- * Function irlmp_discovery_expiry (expiry)
- *
- * This device is no longer been discovered, and therefore it is being
- * purged from the discovery log. Inform all clients who have
- * registered for this event...
- *
- * Note : called exclusively from discovery.c
- * Note : this is no longer called under discovery spinlock, so the
- * client can do whatever he wants in the callback.
- */
-void irlmp_discovery_expiry(discinfo_t *expiries, int number)
-{
- irlmp_client_t *client;
- irlmp_client_t *client_next;
- int i;
-
- IRDA_ASSERT(expiries != NULL, return;);
-
- /* For each client - notify callback may touch client list */
- client = (irlmp_client_t *) hashbin_get_first(irlmp->clients);
- while (NULL != hashbin_find_next(irlmp->clients, (long) client, NULL,
- (void *) &client_next) ) {
-
- /* Pass all entries to the listener */
- for(i = 0; i < number; i++) {
- /* Check if we should notify client */
- if ((client->expir_callback) &&
- (client->hint_mask.word &
- get_unaligned((__u16 *)expiries[i].hints)
- & 0x7f7f) )
- client->expir_callback(&(expiries[i]),
- EXPIRY_TIMEOUT,
- client->priv);
- }
-
- /* Next client */
- client = client_next;
- }
-}
-
-/*
- * Function irlmp_get_discovery_response ()
- *
- * Used by IrLAP to get the discovery info it needs when answering
- * discovery requests by other devices.
- */
-discovery_t *irlmp_get_discovery_response(void)
-{
- IRDA_ASSERT(irlmp != NULL, return NULL;);
-
- put_unaligned(irlmp->hints.word, (__u16 *)irlmp->discovery_rsp.data.hints);
-
- /*
- * Set character set for device name (we use ASCII), and
- * copy device name. Remember to make room for a \0 at the
- * end
- */
- irlmp->discovery_rsp.data.charset = CS_ASCII;
-
- strncpy(irlmp->discovery_rsp.data.info, sysctl_devname,
- NICKNAME_MAX_LEN);
- irlmp->discovery_rsp.name_len = strlen(irlmp->discovery_rsp.data.info);
-
- return &irlmp->discovery_rsp;
-}
-
-/*
- * Function irlmp_data_request (self, skb)
- *
- * Send some data to peer device
- *
- * Note on skb management :
- * After calling the lower layers of the IrDA stack, we always
- * kfree() the skb, which drop the reference count (and potentially
- * destroy it).
- * IrLMP and IrLAP may queue the packet, and in those cases will need
- * to use skb_get() to keep it around.
- * Jean II
- */
-int irlmp_data_request(struct lsap_cb *self, struct sk_buff *userdata)
-{
- int ret;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
-
- /* Make room for MUX header */
- IRDA_ASSERT(skb_headroom(userdata) >= LMP_HEADER, return -1;);
- skb_push(userdata, LMP_HEADER);
-
- ret = irlmp_do_lsap_event(self, LM_DATA_REQUEST, userdata);
-
- /* Drop reference count - see irlap_data_request(). */
- dev_kfree_skb(userdata);
-
- return ret;
-}
-EXPORT_SYMBOL(irlmp_data_request);
-
-/*
- * Function irlmp_data_indication (handle, skb)
- *
- * Got data from LAP layer so pass it up to upper layer
- *
- */
-void irlmp_data_indication(struct lsap_cb *self, struct sk_buff *skb)
-{
- /* Hide LMP header from layer above */
- skb_pull(skb, LMP_HEADER);
-
- if (self->notify.data_indication) {
- /* Don't forget to refcount it - see irlap_driver_rcv(). */
- skb_get(skb);
- self->notify.data_indication(self->notify.instance, self, skb);
- }
-}
-
-/*
- * Function irlmp_udata_request (self, skb)
- */
-int irlmp_udata_request(struct lsap_cb *self, struct sk_buff *userdata)
-{
- int ret;
-
- IRDA_ASSERT(userdata != NULL, return -1;);
-
- /* Make room for MUX header */
- IRDA_ASSERT(skb_headroom(userdata) >= LMP_HEADER, return -1;);
- skb_push(userdata, LMP_HEADER);
-
- ret = irlmp_do_lsap_event(self, LM_UDATA_REQUEST, userdata);
-
- /* Drop reference count - see irlap_data_request(). */
- dev_kfree_skb(userdata);
-
- return ret;
-}
-
-/*
- * Function irlmp_udata_indication (self, skb)
- *
- * Send unreliable data (but still within the connection)
- *
- */
-void irlmp_udata_indication(struct lsap_cb *self, struct sk_buff *skb)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
- IRDA_ASSERT(skb != NULL, return;);
-
- /* Hide LMP header from layer above */
- skb_pull(skb, LMP_HEADER);
-
- if (self->notify.udata_indication) {
- /* Don't forget to refcount it - see irlap_driver_rcv(). */
- skb_get(skb);
- self->notify.udata_indication(self->notify.instance, self,
- skb);
- }
-}
-
-/*
- * Function irlmp_connless_data_request (self, skb)
- */
-#ifdef CONFIG_IRDA_ULTRA
-int irlmp_connless_data_request(struct lsap_cb *self, struct sk_buff *userdata,
- __u8 pid)
-{
- struct sk_buff *clone_skb;
- struct lap_cb *lap;
-
- IRDA_ASSERT(userdata != NULL, return -1;);
-
- /* Make room for MUX and PID header */
- IRDA_ASSERT(skb_headroom(userdata) >= LMP_HEADER+LMP_PID_HEADER,
- return -1;);
-
- /* Insert protocol identifier */
- skb_push(userdata, LMP_PID_HEADER);
- if(self != NULL)
- userdata->data[0] = self->pid;
- else
- userdata->data[0] = pid;
-
- /* Connectionless sockets must use 0x70 */
- skb_push(userdata, LMP_HEADER);
- userdata->data[0] = userdata->data[1] = LSAP_CONNLESS;
-
- /* Try to send Connectionless packets out on all links */
- lap = (struct lap_cb *) hashbin_get_first(irlmp->links);
- while (lap != NULL) {
- IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return -1;);
-
- clone_skb = skb_clone(userdata, GFP_ATOMIC);
- if (!clone_skb) {
- dev_kfree_skb(userdata);
- return -ENOMEM;
- }
-
- irlap_unitdata_request(lap->irlap, clone_skb);
- /* irlap_unitdata_request() don't increase refcount,
- * so no dev_kfree_skb() - Jean II */
-
- lap = (struct lap_cb *) hashbin_get_next(irlmp->links);
- }
- dev_kfree_skb(userdata);
-
- return 0;
-}
-#endif /* CONFIG_IRDA_ULTRA */
-
-/*
- * Function irlmp_connless_data_indication (self, skb)
- *
- * Receive unreliable data outside any connection. Mostly used by Ultra
- *
- */
-#ifdef CONFIG_IRDA_ULTRA
-void irlmp_connless_data_indication(struct lsap_cb *self, struct sk_buff *skb)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
- IRDA_ASSERT(skb != NULL, return;);
-
- /* Hide LMP and PID header from layer above */
- skb_pull(skb, LMP_HEADER+LMP_PID_HEADER);
-
- if (self->notify.udata_indication) {
- /* Don't forget to refcount it - see irlap_driver_rcv(). */
- skb_get(skb);
- self->notify.udata_indication(self->notify.instance, self,
- skb);
- }
-}
-#endif /* CONFIG_IRDA_ULTRA */
-
-/*
- * Propagate status indication from LAP to LSAPs (via LMP)
- * This don't trigger any change of state in lap_cb, lmp_cb or lsap_cb,
- * and the event is stateless, therefore we can bypass both state machines
- * and send the event direct to the LSAP user.
- * Jean II
- */
-void irlmp_status_indication(struct lap_cb *self,
- LINK_STATUS link, LOCK_STATUS lock)
-{
- struct lsap_cb *next;
- struct lsap_cb *curr;
-
- /* Send status_indication to all LSAPs using this link */
- curr = (struct lsap_cb *) hashbin_get_first( self->lsaps);
- while (NULL != hashbin_find_next(self->lsaps, (long) curr, NULL,
- (void *) &next) ) {
- IRDA_ASSERT(curr->magic == LMP_LSAP_MAGIC, return;);
- /*
- * Inform service user if he has requested it
- */
- if (curr->notify.status_indication != NULL)
- curr->notify.status_indication(curr->notify.instance,
- link, lock);
- else
- pr_debug("%s(), no handler\n", __func__);
-
- curr = next;
- }
-}
-
-/*
- * Receive flow control indication from LAP.
- * LAP want us to send it one more frame. We implement a simple round
- * robin scheduler between the active sockets so that we get a bit of
- * fairness. Note that the round robin is far from perfect, but it's
- * better than nothing.
- * We then poll the selected socket so that we can do synchronous
- * refilling of IrLAP (which allow to minimise the number of buffers).
- * Jean II
- */
-void irlmp_flow_indication(struct lap_cb *self, LOCAL_FLOW flow)
-{
- struct lsap_cb *next;
- struct lsap_cb *curr;
- int lsap_todo;
-
- IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
- IRDA_ASSERT(flow == FLOW_START, return;);
-
- /* Get the number of lsap. That's the only safe way to know
- * that we have looped around... - Jean II */
- lsap_todo = HASHBIN_GET_SIZE(self->lsaps);
- pr_debug("%s() : %d lsaps to scan\n", __func__, lsap_todo);
-
- /* Poll lsap in order until the queue is full or until we
- * tried them all.
- * Most often, the current LSAP will have something to send,
- * so we will go through this loop only once. - Jean II */
- while((lsap_todo--) &&
- (IRLAP_GET_TX_QUEUE_LEN(self->irlap) < LAP_HIGH_THRESHOLD)) {
- /* Try to find the next lsap we should poll. */
- next = self->flow_next;
- /* If we have no lsap, restart from first one */
- if(next == NULL)
- next = (struct lsap_cb *) hashbin_get_first(self->lsaps);
- /* Verify current one and find the next one */
- curr = hashbin_find_next(self->lsaps, (long) next, NULL,
- (void *) &self->flow_next);
- /* Uh-oh... Paranoia */
- if(curr == NULL)
- break;
- pr_debug("%s() : curr is %p, next was %p and is now %p, still %d to go - queue len = %d\n",
- __func__, curr, next, self->flow_next, lsap_todo,
- IRLAP_GET_TX_QUEUE_LEN(self->irlap));
-
- /* Inform lsap user that it can send one more packet. */
- if (curr->notify.flow_indication != NULL)
- curr->notify.flow_indication(curr->notify.instance,
- curr, flow);
- else
- pr_debug("%s(), no handler\n", __func__);
- }
-}
-
-#if 0
-/*
- * Function irlmp_hint_to_service (hint)
- *
- * Returns a list of all servics contained in the given hint bits. This
- * function assumes that the hint bits have the size of two bytes only
- */
-__u8 *irlmp_hint_to_service(__u8 *hint)
-{
- __u8 *service;
- int i = 0;
-
- /*
- * Allocate array to store services in. 16 entries should be safe
- * since we currently only support 2 hint bytes
- */
- service = kmalloc(16, GFP_ATOMIC);
- if (!service)
- return NULL;
-
- if (!hint[0]) {
- pr_debug("<None>\n");
- kfree(service);
- return NULL;
- }
- if (hint[0] & HINT_PNP)
- pr_debug("PnP Compatible ");
- if (hint[0] & HINT_PDA)
- pr_debug("PDA/Palmtop ");
- if (hint[0] & HINT_COMPUTER)
- pr_debug("Computer ");
- if (hint[0] & HINT_PRINTER) {
- pr_debug("Printer ");
- service[i++] = S_PRINTER;
- }
- if (hint[0] & HINT_MODEM)
- pr_debug("Modem ");
- if (hint[0] & HINT_FAX)
- pr_debug("Fax ");
- if (hint[0] & HINT_LAN) {
- pr_debug("LAN Access ");
- service[i++] = S_LAN;
- }
- /*
- * Test if extension byte exists. This byte will usually be
- * there, but this is not really required by the standard.
- * (IrLMP p. 29)
- */
- if (hint[0] & HINT_EXTENSION) {
- if (hint[1] & HINT_TELEPHONY) {
- pr_debug("Telephony ");
- service[i++] = S_TELEPHONY;
- }
- if (hint[1] & HINT_FILE_SERVER)
- pr_debug("File Server ");
-
- if (hint[1] & HINT_COMM) {
- pr_debug("IrCOMM ");
- service[i++] = S_COMM;
- }
- if (hint[1] & HINT_OBEX) {
- pr_debug("IrOBEX ");
- service[i++] = S_OBEX;
- }
- }
- pr_debug("\n");
-
- /* So that client can be notified about any discovery */
- service[i++] = S_ANY;
-
- service[i] = S_END;
-
- return service;
-}
-#endif
-
-static const __u16 service_hint_mapping[S_END][2] = {
- { HINT_PNP, 0 }, /* S_PNP */
- { HINT_PDA, 0 }, /* S_PDA */
- { HINT_COMPUTER, 0 }, /* S_COMPUTER */
- { HINT_PRINTER, 0 }, /* S_PRINTER */
- { HINT_MODEM, 0 }, /* S_MODEM */
- { HINT_FAX, 0 }, /* S_FAX */
- { HINT_LAN, 0 }, /* S_LAN */
- { HINT_EXTENSION, HINT_TELEPHONY }, /* S_TELEPHONY */
- { HINT_EXTENSION, HINT_COMM }, /* S_COMM */
- { HINT_EXTENSION, HINT_OBEX }, /* S_OBEX */
- { 0xFF, 0xFF }, /* S_ANY */
-};
-
-/*
- * Function irlmp_service_to_hint (service)
- *
- * Converts a service type, to a hint bit
- *
- * Returns: a 16 bit hint value, with the service bit set
- */
-__u16 irlmp_service_to_hint(int service)
-{
- __u16_host_order hint;
-
- hint.byte[0] = service_hint_mapping[service][0];
- hint.byte[1] = service_hint_mapping[service][1];
-
- return hint.word;
-}
-EXPORT_SYMBOL(irlmp_service_to_hint);
-
-/*
- * Function irlmp_register_service (service)
- *
- * Register local service with IrLMP
- *
- */
-void *irlmp_register_service(__u16 hints)
-{
- irlmp_service_t *service;
-
- pr_debug("%s(), hints = %04x\n", __func__, hints);
-
- /* Make a new registration */
- service = kmalloc(sizeof(irlmp_service_t), GFP_ATOMIC);
- if (!service)
- return NULL;
-
- service->hints.word = hints;
- hashbin_insert(irlmp->services, (irda_queue_t *) service,
- (long) service, NULL);
-
- irlmp->hints.word |= hints;
-
- return (void *)service;
-}
-EXPORT_SYMBOL(irlmp_register_service);
-
-/*
- * Function irlmp_unregister_service (handle)
- *
- * Unregister service with IrLMP.
- *
- * Returns: 0 on success, -1 on error
- */
-int irlmp_unregister_service(void *handle)
-{
- irlmp_service_t *service;
- unsigned long flags;
-
- if (!handle)
- return -1;
-
- /* Caller may call with invalid handle (it's legal) - Jean II */
- service = hashbin_lock_find(irlmp->services, (long) handle, NULL);
- if (!service) {
- pr_debug("%s(), Unknown service!\n", __func__);
- return -1;
- }
-
- hashbin_remove_this(irlmp->services, (irda_queue_t *) service);
- kfree(service);
-
- /* Remove old hint bits */
- irlmp->hints.word = 0;
-
- /* Refresh current hint bits */
- spin_lock_irqsave(&irlmp->services->hb_spinlock, flags);
- service = (irlmp_service_t *) hashbin_get_first(irlmp->services);
- while (service) {
- irlmp->hints.word |= service->hints.word;
-
- service = (irlmp_service_t *)hashbin_get_next(irlmp->services);
- }
- spin_unlock_irqrestore(&irlmp->services->hb_spinlock, flags);
- return 0;
-}
-EXPORT_SYMBOL(irlmp_unregister_service);
-
-/*
- * Function irlmp_register_client (hint_mask, callback1, callback2)
- *
- * Register a local client with IrLMP
- * First callback is selective discovery (based on hints)
- * Second callback is for selective discovery expiries
- *
- * Returns: handle > 0 on success, 0 on error
- */
-void *irlmp_register_client(__u16 hint_mask, DISCOVERY_CALLBACK1 disco_clb,
- DISCOVERY_CALLBACK2 expir_clb, void *priv)
-{
- irlmp_client_t *client;
-
- IRDA_ASSERT(irlmp != NULL, return NULL;);
-
- /* Make a new registration */
- client = kmalloc(sizeof(irlmp_client_t), GFP_ATOMIC);
- if (!client)
- return NULL;
-
- /* Register the details */
- client->hint_mask.word = hint_mask;
- client->disco_callback = disco_clb;
- client->expir_callback = expir_clb;
- client->priv = priv;
-
- hashbin_insert(irlmp->clients, (irda_queue_t *) client,
- (long) client, NULL);
-
- return (void *) client;
-}
-EXPORT_SYMBOL(irlmp_register_client);
-
-/*
- * Function irlmp_update_client (handle, hint_mask, callback1, callback2)
- *
- * Updates specified client (handle) with possibly new hint_mask and
- * callback
- *
- * Returns: 0 on success, -1 on error
- */
-int irlmp_update_client(void *handle, __u16 hint_mask,
- DISCOVERY_CALLBACK1 disco_clb,
- DISCOVERY_CALLBACK2 expir_clb, void *priv)
-{
- irlmp_client_t *client;
-
- if (!handle)
- return -1;
-
- client = hashbin_lock_find(irlmp->clients, (long) handle, NULL);
- if (!client) {
- pr_debug("%s(), Unknown client!\n", __func__);
- return -1;
- }
-
- client->hint_mask.word = hint_mask;
- client->disco_callback = disco_clb;
- client->expir_callback = expir_clb;
- client->priv = priv;
-
- return 0;
-}
-EXPORT_SYMBOL(irlmp_update_client);
-
-/*
- * Function irlmp_unregister_client (handle)
- *
- * Returns: 0 on success, -1 on error
- *
- */
-int irlmp_unregister_client(void *handle)
-{
- struct irlmp_client *client;
-
- if (!handle)
- return -1;
-
- /* Caller may call with invalid handle (it's legal) - Jean II */
- client = hashbin_lock_find(irlmp->clients, (long) handle, NULL);
- if (!client) {
- pr_debug("%s(), Unknown client!\n", __func__);
- return -1;
- }
-
- pr_debug("%s(), removing client!\n", __func__);
- hashbin_remove_this(irlmp->clients, (irda_queue_t *) client);
- kfree(client);
-
- return 0;
-}
-EXPORT_SYMBOL(irlmp_unregister_client);
-
-/*
- * Function irlmp_slsap_inuse (slsap)
- *
- * Check if the given source LSAP selector is in use
- *
- * This function is clearly not very efficient. On the mitigating side, the
- * stack make sure that in 99% of the cases, we are called only once
- * for each socket allocation. We could probably keep a bitmap
- * of the allocated LSAP, but I'm not sure the complexity is worth it.
- * Jean II
- */
-static int irlmp_slsap_inuse(__u8 slsap_sel)
-{
- struct lsap_cb *self;
- struct lap_cb *lap;
- unsigned long flags;
-
- IRDA_ASSERT(irlmp != NULL, return TRUE;);
- IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return TRUE;);
- IRDA_ASSERT(slsap_sel != LSAP_ANY, return TRUE;);
-
-#ifdef CONFIG_IRDA_ULTRA
- /* Accept all bindings to the connectionless LSAP */
- if (slsap_sel == LSAP_CONNLESS)
- return FALSE;
-#endif /* CONFIG_IRDA_ULTRA */
-
- /* Valid values are between 0 and 127 (0x0-0x6F) */
- if (slsap_sel > LSAP_MAX)
- return TRUE;
-
- /*
- * Check if slsap is already in use. To do this we have to loop over
- * every IrLAP connection and check every LSAP associated with each
- * the connection.
- */
- spin_lock_irqsave_nested(&irlmp->links->hb_spinlock, flags,
- SINGLE_DEPTH_NESTING);
- lap = (struct lap_cb *) hashbin_get_first(irlmp->links);
- while (lap != NULL) {
- IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, goto errlap;);
-
- /* Careful for priority inversions here !
- * irlmp->links is never taken while another IrDA
- * spinlock is held, so we are safe. Jean II */
- spin_lock(&lap->lsaps->hb_spinlock);
-
- /* For this IrLAP, check all the LSAPs */
- self = (struct lsap_cb *) hashbin_get_first(lap->lsaps);
- while (self != NULL) {
- IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC,
- goto errlsap;);
-
- if (self->slsap_sel == slsap_sel) {
- pr_debug("Source LSAP selector=%02x in use\n",
- self->slsap_sel);
- goto errlsap;
- }
- self = (struct lsap_cb*) hashbin_get_next(lap->lsaps);
- }
- spin_unlock(&lap->lsaps->hb_spinlock);
-
- /* Next LAP */
- lap = (struct lap_cb *) hashbin_get_next(irlmp->links);
- }
- spin_unlock_irqrestore(&irlmp->links->hb_spinlock, flags);
-
- /*
- * Server sockets are typically waiting for connections and
- * therefore reside in the unconnected list. We don't want
- * to give out their LSAPs for obvious reasons...
- * Jean II
- */
- spin_lock_irqsave(&irlmp->unconnected_lsaps->hb_spinlock, flags);
-
- self = (struct lsap_cb *) hashbin_get_first(irlmp->unconnected_lsaps);
- while (self != NULL) {
- IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, goto erruncon;);
- if (self->slsap_sel == slsap_sel) {
- pr_debug("Source LSAP selector=%02x in use (unconnected)\n",
- self->slsap_sel);
- goto erruncon;
- }
- self = (struct lsap_cb*) hashbin_get_next(irlmp->unconnected_lsaps);
- }
- spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags);
-
- return FALSE;
-
- /* Error exit from within one of the two nested loops.
- * Make sure we release the right spinlock in the righ order.
- * Jean II */
-errlsap:
- spin_unlock(&lap->lsaps->hb_spinlock);
-IRDA_ASSERT_LABEL(errlap:)
- spin_unlock_irqrestore(&irlmp->links->hb_spinlock, flags);
- return TRUE;
-
- /* Error exit from within the unconnected loop.
- * Just one spinlock to release... Jean II */
-erruncon:
- spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags);
- return TRUE;
-}
-
-/*
- * Function irlmp_find_free_slsap ()
- *
- * Find a free source LSAP to use. This function is called if the service
- * user has requested a source LSAP equal to LM_ANY
- */
-static __u8 irlmp_find_free_slsap(void)
-{
- __u8 lsap_sel;
- int wrapped = 0;
-
- IRDA_ASSERT(irlmp != NULL, return -1;);
- IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return -1;);
-
- /* Most users don't really care which LSAPs they are given,
- * and therefore we automatically give them a free LSAP.
- * This function try to find a suitable LSAP, i.e. which is
- * not in use and is within the acceptable range. Jean II */
-
- do {
- /* Always increment to LSAP number before using it.
- * In theory, we could reuse the last LSAP number, as long
- * as it is no longer in use. Some IrDA stack do that.
- * However, the previous socket may be half closed, i.e.
- * we closed it, we think it's no longer in use, but the
- * other side did not receive our close and think it's
- * active and still send data on it.
- * This is similar to what is done with PIDs and TCP ports.
- * Also, this reduce the number of calls to irlmp_slsap_inuse()
- * which is an expensive function to call.
- * Jean II */
- irlmp->last_lsap_sel++;
-
- /* Check if we need to wraparound (0x70-0x7f are reserved) */
- if (irlmp->last_lsap_sel > LSAP_MAX) {
- /* 0x00-0x10 are also reserved for well know ports */
- irlmp->last_lsap_sel = 0x10;
-
- /* Make sure we terminate the loop */
- if (wrapped++) {
- net_err_ratelimited("%s: no more free LSAPs !\n",
- __func__);
- return 0;
- }
- }
-
- /* If the LSAP is in use, try the next one.
- * Despite the autoincrement, we need to check if the lsap
- * is really in use or not, first because LSAP may be
- * directly allocated in irlmp_open_lsap(), and also because
- * we may wraparound on old sockets. Jean II */
- } while (irlmp_slsap_inuse(irlmp->last_lsap_sel));
-
- /* Got it ! */
- lsap_sel = irlmp->last_lsap_sel;
- pr_debug("%s(), found free lsap_sel=%02x\n",
- __func__, lsap_sel);
-
- return lsap_sel;
-}
-
-/*
- * Function irlmp_convert_lap_reason (lap_reason)
- *
- * Converts IrLAP disconnect reason codes to IrLMP disconnect reason
- * codes
- *
- */
-LM_REASON irlmp_convert_lap_reason( LAP_REASON lap_reason)
-{
- int reason = LM_LAP_DISCONNECT;
-
- switch (lap_reason) {
- case LAP_DISC_INDICATION: /* Received a disconnect request from peer */
- pr_debug("%s(), LAP_DISC_INDICATION\n", __func__);
- reason = LM_USER_REQUEST;
- break;
- case LAP_NO_RESPONSE: /* To many retransmits without response */
- pr_debug("%s(), LAP_NO_RESPONSE\n", __func__);
- reason = LM_LAP_DISCONNECT;
- break;
- case LAP_RESET_INDICATION:
- pr_debug("%s(), LAP_RESET_INDICATION\n", __func__);
- reason = LM_LAP_RESET;
- break;
- case LAP_FOUND_NONE:
- case LAP_MEDIA_BUSY:
- case LAP_PRIMARY_CONFLICT:
- pr_debug("%s(), LAP_FOUND_NONE, LAP_MEDIA_BUSY or LAP_PRIMARY_CONFLICT\n",
- __func__);
- reason = LM_CONNECT_FAILURE;
- break;
- default:
- pr_debug("%s(), Unknown IrLAP disconnect reason %d!\n",
- __func__, lap_reason);
- reason = LM_LAP_DISCONNECT;
- break;
- }
-
- return reason;
-}
-
-#ifdef CONFIG_PROC_FS
-
-struct irlmp_iter_state {
- hashbin_t *hashbin;
-};
-
-#define LSAP_START_TOKEN ((void *)1)
-#define LINK_START_TOKEN ((void *)2)
-
-static void *irlmp_seq_hb_idx(struct irlmp_iter_state *iter, loff_t *off)
-{
- void *element;
-
- spin_lock_irq(&iter->hashbin->hb_spinlock);
- for (element = hashbin_get_first(iter->hashbin);
- element != NULL;
- element = hashbin_get_next(iter->hashbin)) {
- if (!off || (*off)-- == 0) {
- /* NB: hashbin left locked */
- return element;
- }
- }
- spin_unlock_irq(&iter->hashbin->hb_spinlock);
- iter->hashbin = NULL;
- return NULL;
-}
-
-
-static void *irlmp_seq_start(struct seq_file *seq, loff_t *pos)
-{
- struct irlmp_iter_state *iter = seq->private;
- void *v;
- loff_t off = *pos;
-
- iter->hashbin = NULL;
- if (off-- == 0)
- return LSAP_START_TOKEN;
-
- iter->hashbin = irlmp->unconnected_lsaps;
- v = irlmp_seq_hb_idx(iter, &off);
- if (v)
- return v;
-
- if (off-- == 0)
- return LINK_START_TOKEN;
-
- iter->hashbin = irlmp->links;
- return irlmp_seq_hb_idx(iter, &off);
-}
-
-static void *irlmp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
-{
- struct irlmp_iter_state *iter = seq->private;
-
- ++*pos;
-
- if (v == LSAP_START_TOKEN) { /* start of list of lsaps */
- iter->hashbin = irlmp->unconnected_lsaps;
- v = irlmp_seq_hb_idx(iter, NULL);
- return v ? v : LINK_START_TOKEN;
- }
-
- if (v == LINK_START_TOKEN) { /* start of list of links */
- iter->hashbin = irlmp->links;
- return irlmp_seq_hb_idx(iter, NULL);
- }
-
- v = hashbin_get_next(iter->hashbin);
-
- if (v == NULL) { /* no more in this hash bin */
- spin_unlock_irq(&iter->hashbin->hb_spinlock);
-
- if (iter->hashbin == irlmp->unconnected_lsaps)
- v = LINK_START_TOKEN;
-
- iter->hashbin = NULL;
- }
- return v;
-}
-
-static void irlmp_seq_stop(struct seq_file *seq, void *v)
-{
- struct irlmp_iter_state *iter = seq->private;
-
- if (iter->hashbin)
- spin_unlock_irq(&iter->hashbin->hb_spinlock);
-}
-
-static int irlmp_seq_show(struct seq_file *seq, void *v)
-{
- const struct irlmp_iter_state *iter = seq->private;
- struct lsap_cb *self = v;
-
- if (v == LSAP_START_TOKEN)
- seq_puts(seq, "Unconnected LSAPs:\n");
- else if (v == LINK_START_TOKEN)
- seq_puts(seq, "\nRegistered Link Layers:\n");
- else if (iter->hashbin == irlmp->unconnected_lsaps) {
- self = v;
- IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -EINVAL; );
- seq_printf(seq, "lsap state: %s, ",
- irlsap_state[ self->lsap_state]);
- seq_printf(seq,
- "slsap_sel: %#02x, dlsap_sel: %#02x, ",
- self->slsap_sel, self->dlsap_sel);
- seq_printf(seq, "(%s)", self->notify.name);
- seq_printf(seq, "\n");
- } else if (iter->hashbin == irlmp->links) {
- struct lap_cb *lap = v;
-
- seq_printf(seq, "lap state: %s, ",
- irlmp_state[lap->lap_state]);
-
- seq_printf(seq, "saddr: %#08x, daddr: %#08x, ",
- lap->saddr, lap->daddr);
- seq_printf(seq, "num lsaps: %d",
- HASHBIN_GET_SIZE(lap->lsaps));
- seq_printf(seq, "\n");
-
- /* Careful for priority inversions here !
- * All other uses of attrib spinlock are independent of
- * the object spinlock, so we are safe. Jean II */
- spin_lock(&lap->lsaps->hb_spinlock);
-
- seq_printf(seq, "\n Connected LSAPs:\n");
- for (self = (struct lsap_cb *) hashbin_get_first(lap->lsaps);
- self != NULL;
- self = (struct lsap_cb *)hashbin_get_next(lap->lsaps)) {
- IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC,
- goto outloop;);
- seq_printf(seq, " lsap state: %s, ",
- irlsap_state[ self->lsap_state]);
- seq_printf(seq,
- "slsap_sel: %#02x, dlsap_sel: %#02x, ",
- self->slsap_sel, self->dlsap_sel);
- seq_printf(seq, "(%s)", self->notify.name);
- seq_putc(seq, '\n');
-
- }
- IRDA_ASSERT_LABEL(outloop:)
- spin_unlock(&lap->lsaps->hb_spinlock);
- seq_putc(seq, '\n');
- } else
- return -EINVAL;
-
- return 0;
-}
-
-static const struct seq_operations irlmp_seq_ops = {
- .start = irlmp_seq_start,
- .next = irlmp_seq_next,
- .stop = irlmp_seq_stop,
- .show = irlmp_seq_show,
-};
-
-static int irlmp_seq_open(struct inode *inode, struct file *file)
-{
- IRDA_ASSERT(irlmp != NULL, return -EINVAL;);
-
- return seq_open_private(file, &irlmp_seq_ops,
- sizeof(struct irlmp_iter_state));
-}
-
-const struct file_operations irlmp_seq_fops = {
- .owner = THIS_MODULE,
- .open = irlmp_seq_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = seq_release_private,
-};
-
-#endif /* PROC_FS */
diff --git a/drivers/staging/irda/net/irlmp_event.c b/drivers/staging/irda/net/irlmp_event.c
deleted file mode 100644
index ddad0994b6dc..000000000000
--- a/drivers/staging/irda/net/irlmp_event.c
+++ /dev/null
@@ -1,886 +0,0 @@
-/*********************************************************************
- *
- * Filename: irlmp_event.c
- * Version: 0.8
- * Description: An IrDA LMP event driver for Linux
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Mon Aug 4 20:40:53 1997
- * Modified at: Tue Dec 14 23:04:16 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
- * All Rights Reserved.
- * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/kernel.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/timer.h>
-#include <net/irda/irlap.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/irlmp_frame.h>
-#include <net/irda/irlmp_event.h>
-
-const char *const irlmp_state[] = {
- "LAP_STANDBY",
- "LAP_U_CONNECT",
- "LAP_ACTIVE",
-};
-
-const char *const irlsap_state[] = {
- "LSAP_DISCONNECTED",
- "LSAP_CONNECT",
- "LSAP_CONNECT_PEND",
- "LSAP_DATA_TRANSFER_READY",
- "LSAP_SETUP",
- "LSAP_SETUP_PEND",
-};
-
-static const char *const irlmp_event[] __maybe_unused = {
- "LM_CONNECT_REQUEST",
- "LM_CONNECT_CONFIRM",
- "LM_CONNECT_RESPONSE",
- "LM_CONNECT_INDICATION",
-
- "LM_DISCONNECT_INDICATION",
- "LM_DISCONNECT_REQUEST",
-
- "LM_DATA_REQUEST",
- "LM_UDATA_REQUEST",
- "LM_DATA_INDICATION",
- "LM_UDATA_INDICATION",
-
- "LM_WATCHDOG_TIMEOUT",
-
- /* IrLAP events */
- "LM_LAP_CONNECT_REQUEST",
- "LM_LAP_CONNECT_INDICATION",
- "LM_LAP_CONNECT_CONFIRM",
- "LM_LAP_DISCONNECT_INDICATION",
- "LM_LAP_DISCONNECT_REQUEST",
- "LM_LAP_DISCOVERY_REQUEST",
- "LM_LAP_DISCOVERY_CONFIRM",
- "LM_LAP_IDLE_TIMEOUT",
-};
-
-/* LAP Connection control proto declarations */
-static void irlmp_state_standby (struct lap_cb *, IRLMP_EVENT,
- struct sk_buff *);
-static void irlmp_state_u_connect(struct lap_cb *, IRLMP_EVENT,
- struct sk_buff *);
-static void irlmp_state_active (struct lap_cb *, IRLMP_EVENT,
- struct sk_buff *);
-
-/* LSAP Connection control proto declarations */
-static int irlmp_state_disconnected(struct lsap_cb *, IRLMP_EVENT,
- struct sk_buff *);
-static int irlmp_state_connect (struct lsap_cb *, IRLMP_EVENT,
- struct sk_buff *);
-static int irlmp_state_connect_pend(struct lsap_cb *, IRLMP_EVENT,
- struct sk_buff *);
-static int irlmp_state_dtr (struct lsap_cb *, IRLMP_EVENT,
- struct sk_buff *);
-static int irlmp_state_setup (struct lsap_cb *, IRLMP_EVENT,
- struct sk_buff *);
-static int irlmp_state_setup_pend (struct lsap_cb *, IRLMP_EVENT,
- struct sk_buff *);
-
-static void (*lap_state[]) (struct lap_cb *, IRLMP_EVENT, struct sk_buff *) =
-{
- irlmp_state_standby,
- irlmp_state_u_connect,
- irlmp_state_active,
-};
-
-static int (*lsap_state[])( struct lsap_cb *, IRLMP_EVENT, struct sk_buff *) =
-{
- irlmp_state_disconnected,
- irlmp_state_connect,
- irlmp_state_connect_pend,
- irlmp_state_dtr,
- irlmp_state_setup,
- irlmp_state_setup_pend
-};
-
-static inline void irlmp_next_lap_state(struct lap_cb *self,
- IRLMP_STATE state)
-{
- /*
- pr_debug("%s(), LMP LAP = %s\n", __func__, irlmp_state[state]);
- */
- self->lap_state = state;
-}
-
-static inline void irlmp_next_lsap_state(struct lsap_cb *self,
- LSAP_STATE state)
-{
- /*
- IRDA_ASSERT(self != NULL, return;);
- pr_debug("%s(), LMP LSAP = %s\n", __func__, irlsap_state[state]);
- */
- self->lsap_state = state;
-}
-
-/* Do connection control events */
-int irlmp_do_lsap_event(struct lsap_cb *self, IRLMP_EVENT event,
- struct sk_buff *skb)
-{
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
-
- pr_debug("%s(), EVENT = %s, STATE = %s\n",
- __func__, irlmp_event[event], irlsap_state[self->lsap_state]);
-
- return (*lsap_state[self->lsap_state]) (self, event, skb);
-}
-
-/*
- * Function do_lap_event (event, skb, info)
- *
- * Do IrLAP control events
- *
- */
-void irlmp_do_lap_event(struct lap_cb *self, IRLMP_EVENT event,
- struct sk_buff *skb)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
-
- pr_debug("%s(), EVENT = %s, STATE = %s\n", __func__,
- irlmp_event[event],
- irlmp_state[self->lap_state]);
-
- (*lap_state[self->lap_state]) (self, event, skb);
-}
-
-void irlmp_discovery_timer_expired(struct timer_list *t)
-{
- /* We always cleanup the log (active & passive discovery) */
- irlmp_do_expiry();
-
- irlmp_do_discovery(sysctl_discovery_slots);
-
- /* Restart timer */
- irlmp_start_discovery_timer(irlmp, sysctl_discovery_timeout * HZ);
-}
-
-void irlmp_watchdog_timer_expired(struct timer_list *t)
-{
- struct lsap_cb *self = from_timer(self, t, watchdog_timer);
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
-
- irlmp_do_lsap_event(self, LM_WATCHDOG_TIMEOUT, NULL);
-}
-
-void irlmp_idle_timer_expired(struct timer_list *t)
-{
- struct lap_cb *self = from_timer(self, t, idle_timer);
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
-
- irlmp_do_lap_event(self, LM_LAP_IDLE_TIMEOUT, NULL);
-}
-
-/*
- * Send an event on all LSAPs attached to this LAP.
- */
-static inline void
-irlmp_do_all_lsap_event(hashbin_t * lsap_hashbin,
- IRLMP_EVENT event)
-{
- struct lsap_cb *lsap;
- struct lsap_cb *lsap_next;
-
- /* Note : this function use the new hashbin_find_next()
- * function, instead of the old hashbin_get_next().
- * This make sure that we are always pointing one lsap
- * ahead, so that if the current lsap is removed as the
- * result of sending the event, we don't care.
- * Also, as we store the context ourselves, if an enumeration
- * of the same lsap hashbin happens as the result of sending the
- * event, we don't care.
- * The only problem is if the next lsap is removed. In that case,
- * hashbin_find_next() will return NULL and we will abort the
- * enumeration. - Jean II */
-
- /* Also : we don't accept any skb in input. We can *NOT* pass
- * the same skb to multiple clients safely, we would need to
- * skb_clone() it. - Jean II */
-
- lsap = (struct lsap_cb *) hashbin_get_first(lsap_hashbin);
-
- while (NULL != hashbin_find_next(lsap_hashbin,
- (long) lsap,
- NULL,
- (void *) &lsap_next) ) {
- irlmp_do_lsap_event(lsap, event, NULL);
- lsap = lsap_next;
- }
-}
-
-/*********************************************************************
- *
- * LAP connection control states
- *
- ********************************************************************/
-
-/*
- * Function irlmp_state_standby (event, skb, info)
- *
- * STANDBY, The IrLAP connection does not exist.
- *
- */
-static void irlmp_state_standby(struct lap_cb *self, IRLMP_EVENT event,
- struct sk_buff *skb)
-{
- IRDA_ASSERT(self->irlap != NULL, return;);
-
- switch (event) {
- case LM_LAP_DISCOVERY_REQUEST:
- /* irlmp_next_station_state( LMP_DISCOVER); */
-
- irlap_discovery_request(self->irlap, &irlmp->discovery_cmd);
- break;
- case LM_LAP_CONNECT_INDICATION:
- /* It's important to switch state first, to avoid IrLMP to
- * think that the link is free since IrLMP may then start
- * discovery before the connection is properly set up. DB.
- */
- irlmp_next_lap_state(self, LAP_ACTIVE);
-
- /* Just accept connection TODO, this should be fixed */
- irlap_connect_response(self->irlap, skb);
- break;
- case LM_LAP_CONNECT_REQUEST:
- pr_debug("%s() LS_CONNECT_REQUEST\n", __func__);
-
- irlmp_next_lap_state(self, LAP_U_CONNECT);
-
- /* FIXME: need to set users requested QoS */
- irlap_connect_request(self->irlap, self->daddr, NULL, 0);
- break;
- case LM_LAP_DISCONNECT_INDICATION:
- pr_debug("%s(), Error LM_LAP_DISCONNECT_INDICATION\n",
- __func__);
-
- irlmp_next_lap_state(self, LAP_STANDBY);
- break;
- default:
- pr_debug("%s(), Unknown event %s\n",
- __func__, irlmp_event[event]);
- break;
- }
-}
-
-/*
- * Function irlmp_state_u_connect (event, skb, info)
- *
- * U_CONNECT, The layer above has tried to open an LSAP connection but
- * since the IrLAP connection does not exist, we must first start an
- * IrLAP connection. We are now waiting response from IrLAP.
- * */
-static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT event,
- struct sk_buff *skb)
-{
- pr_debug("%s(), event=%s\n", __func__, irlmp_event[event]);
-
- switch (event) {
- case LM_LAP_CONNECT_INDICATION:
- /* It's important to switch state first, to avoid IrLMP to
- * think that the link is free since IrLMP may then start
- * discovery before the connection is properly set up. DB.
- */
- irlmp_next_lap_state(self, LAP_ACTIVE);
-
- /* Just accept connection TODO, this should be fixed */
- irlap_connect_response(self->irlap, skb);
-
- /* Tell LSAPs that they can start sending data */
- irlmp_do_all_lsap_event(self->lsaps, LM_LAP_CONNECT_CONFIRM);
-
- /* Note : by the time we get there (LAP retries and co),
- * the lsaps may already have gone. This avoid getting stuck
- * forever in LAP_ACTIVE state - Jean II */
- if (HASHBIN_GET_SIZE(self->lsaps) == 0) {
- pr_debug("%s() NO LSAPs !\n", __func__);
- irlmp_start_idle_timer(self, LM_IDLE_TIMEOUT);
- }
- break;
- case LM_LAP_CONNECT_REQUEST:
- /* Already trying to connect */
- break;
- case LM_LAP_CONNECT_CONFIRM:
- /* For all lsap_ce E Associated do LS_Connect_confirm */
- irlmp_next_lap_state(self, LAP_ACTIVE);
-
- /* Tell LSAPs that they can start sending data */
- irlmp_do_all_lsap_event(self->lsaps, LM_LAP_CONNECT_CONFIRM);
-
- /* Note : by the time we get there (LAP retries and co),
- * the lsaps may already have gone. This avoid getting stuck
- * forever in LAP_ACTIVE state - Jean II */
- if (HASHBIN_GET_SIZE(self->lsaps) == 0) {
- pr_debug("%s() NO LSAPs !\n", __func__);
- irlmp_start_idle_timer(self, LM_IDLE_TIMEOUT);
- }
- break;
- case LM_LAP_DISCONNECT_INDICATION:
- pr_debug("%s(), LM_LAP_DISCONNECT_INDICATION\n", __func__);
- irlmp_next_lap_state(self, LAP_STANDBY);
-
- /* Send disconnect event to all LSAPs using this link */
- irlmp_do_all_lsap_event(self->lsaps,
- LM_LAP_DISCONNECT_INDICATION);
- break;
- case LM_LAP_DISCONNECT_REQUEST:
- pr_debug("%s(), LM_LAP_DISCONNECT_REQUEST\n", __func__);
-
- /* One of the LSAP did timeout or was closed, if it was
- * the last one, try to get out of here - Jean II */
- if (HASHBIN_GET_SIZE(self->lsaps) <= 1) {
- irlap_disconnect_request(self->irlap);
- }
- break;
- default:
- pr_debug("%s(), Unknown event %s\n",
- __func__, irlmp_event[event]);
- break;
- }
-}
-
-/*
- * Function irlmp_state_active (event, skb, info)
- *
- * ACTIVE, IrLAP connection is active
- *
- */
-static void irlmp_state_active(struct lap_cb *self, IRLMP_EVENT event,
- struct sk_buff *skb)
-{
- switch (event) {
- case LM_LAP_CONNECT_REQUEST:
- pr_debug("%s(), LS_CONNECT_REQUEST\n", __func__);
-
- /*
- * IrLAP may have a pending disconnect. We tried to close
- * IrLAP, but it was postponed because the link was
- * busy or we were still sending packets. As we now
- * need it, make sure it stays on. Jean II
- */
- irlap_clear_disconnect(self->irlap);
-
- /*
- * LAP connection already active, just bounce back! Since we
- * don't know which LSAP that tried to do this, we have to
- * notify all LSAPs using this LAP, but that should be safe to
- * do anyway.
- */
- irlmp_do_all_lsap_event(self->lsaps, LM_LAP_CONNECT_CONFIRM);
-
- /* Needed by connect indication */
- irlmp_do_all_lsap_event(irlmp->unconnected_lsaps,
- LM_LAP_CONNECT_CONFIRM);
- /* Keep state */
- break;
- case LM_LAP_DISCONNECT_REQUEST:
- /*
- * Need to find out if we should close IrLAP or not. If there
- * is only one LSAP connection left on this link, that LSAP
- * must be the one that tries to close IrLAP. It will be
- * removed later and moved to the list of unconnected LSAPs
- */
- if (HASHBIN_GET_SIZE(self->lsaps) > 0) {
- /* Timer value is checked in irsysctl - Jean II */
- irlmp_start_idle_timer(self, sysctl_lap_keepalive_time * HZ / 1000);
- } else {
- /* No more connections, so close IrLAP */
-
- /* We don't want to change state just yet, because
- * we want to reflect accurately the real state of
- * the LAP, not the state we wish it was in,
- * so that we don't lose LM_LAP_CONNECT_REQUEST.
- * In some cases, IrLAP won't close the LAP
- * immediately. For example, it might still be
- * retrying packets or waiting for the pf bit.
- * As the LAP always send a DISCONNECT_INDICATION
- * in PCLOSE or SCLOSE, just change state on that.
- * Jean II */
- irlap_disconnect_request(self->irlap);
- }
- break;
- case LM_LAP_IDLE_TIMEOUT:
- if (HASHBIN_GET_SIZE(self->lsaps) == 0) {
- /* Same reasoning as above - keep state */
- irlap_disconnect_request(self->irlap);
- }
- break;
- case LM_LAP_DISCONNECT_INDICATION:
- irlmp_next_lap_state(self, LAP_STANDBY);
-
- /* In some case, at this point our side has already closed
- * all lsaps, and we are waiting for the idle_timer to
- * expire. If another device reconnect immediately, the
- * idle timer will expire in the midle of the connection
- * initialisation, screwing up things a lot...
- * Therefore, we must stop the timer... */
- irlmp_stop_idle_timer(self);
-
- /*
- * Inform all connected LSAP's using this link
- */
- irlmp_do_all_lsap_event(self->lsaps,
- LM_LAP_DISCONNECT_INDICATION);
-
- /* Force an expiry of the discovery log.
- * Now that the LAP is free, the system may attempt to
- * connect to another device. Unfortunately, our entries
- * are stale. There is a small window (<3s) before the
- * normal discovery will run and where irlmp_connect_request()
- * can get the wrong info, so make sure things get
- * cleaned *NOW* ;-) - Jean II */
- irlmp_do_expiry();
- break;
- default:
- pr_debug("%s(), Unknown event %s\n",
- __func__, irlmp_event[event]);
- break;
- }
-}
-
-/*********************************************************************
- *
- * LSAP connection control states
- *
- ********************************************************************/
-
-/*
- * Function irlmp_state_disconnected (event, skb, info)
- *
- * DISCONNECTED
- *
- */
-static int irlmp_state_disconnected(struct lsap_cb *self, IRLMP_EVENT event,
- struct sk_buff *skb)
-{
- int ret = 0;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
-
- switch (event) {
-#ifdef CONFIG_IRDA_ULTRA
- case LM_UDATA_INDICATION:
- /* This is most bizarre. Those packets are aka unreliable
- * connected, aka IrLPT or SOCK_DGRAM/IRDAPROTO_UNITDATA.
- * Why do we pass them as Ultra ??? Jean II */
- irlmp_connless_data_indication(self, skb);
- break;
-#endif /* CONFIG_IRDA_ULTRA */
- case LM_CONNECT_REQUEST:
- pr_debug("%s(), LM_CONNECT_REQUEST\n", __func__);
-
- if (self->conn_skb) {
- net_warn_ratelimited("%s: busy with another request!\n",
- __func__);
- return -EBUSY;
- }
- /* Don't forget to refcount it (see irlmp_connect_request()) */
- skb_get(skb);
- self->conn_skb = skb;
-
- irlmp_next_lsap_state(self, LSAP_SETUP_PEND);
-
- /* Start watchdog timer (5 secs for now) */
- irlmp_start_watchdog_timer(self, 5*HZ);
-
- irlmp_do_lap_event(self->lap, LM_LAP_CONNECT_REQUEST, NULL);
- break;
- case LM_CONNECT_INDICATION:
- if (self->conn_skb) {
- net_warn_ratelimited("%s: busy with another request!\n",
- __func__);
- return -EBUSY;
- }
- /* Don't forget to refcount it (see irlap_driver_rcv()) */
- skb_get(skb);
- self->conn_skb = skb;
-
- irlmp_next_lsap_state(self, LSAP_CONNECT_PEND);
-
- /* Start watchdog timer
- * This is not mentionned in the spec, but there is a rare
- * race condition that can get the socket stuck.
- * If we receive this event while our LAP is closing down,
- * the LM_LAP_CONNECT_REQUEST get lost and we get stuck in
- * CONNECT_PEND state forever.
- * The other cause of getting stuck down there is if the
- * higher layer never reply to the CONNECT_INDICATION.
- * Anyway, it make sense to make sure that we always have
- * a backup plan. 1 second is plenty (should be immediate).
- * Jean II */
- irlmp_start_watchdog_timer(self, 1*HZ);
-
- irlmp_do_lap_event(self->lap, LM_LAP_CONNECT_REQUEST, NULL);
- break;
- default:
- pr_debug("%s(), Unknown event %s on LSAP %#02x\n",
- __func__, irlmp_event[event], self->slsap_sel);
- break;
- }
- return ret;
-}
-
-/*
- * Function irlmp_state_connect (self, event, skb)
- *
- * CONNECT
- *
- */
-static int irlmp_state_connect(struct lsap_cb *self, IRLMP_EVENT event,
- struct sk_buff *skb)
-{
- struct lsap_cb *lsap;
- int ret = 0;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
-
- switch (event) {
- case LM_CONNECT_RESPONSE:
- /*
- * Bind this LSAP to the IrLAP link where the connect was
- * received
- */
- lsap = hashbin_remove(irlmp->unconnected_lsaps, (long) self,
- NULL);
-
- IRDA_ASSERT(lsap == self, return -1;);
- IRDA_ASSERT(self->lap != NULL, return -1;);
- IRDA_ASSERT(self->lap->lsaps != NULL, return -1;);
-
- hashbin_insert(self->lap->lsaps, (irda_queue_t *) self,
- (long) self, NULL);
-
- set_bit(0, &self->connected); /* TRUE */
-
- irlmp_send_lcf_pdu(self->lap, self->dlsap_sel,
- self->slsap_sel, CONNECT_CNF, skb);
-
- del_timer(&self->watchdog_timer);
-
- irlmp_next_lsap_state(self, LSAP_DATA_TRANSFER_READY);
- break;
- case LM_WATCHDOG_TIMEOUT:
- /* May happen, who knows...
- * Jean II */
- pr_debug("%s() WATCHDOG_TIMEOUT!\n", __func__);
-
- /* Disconnect, get out... - Jean II */
- self->lap = NULL;
- self->dlsap_sel = LSAP_ANY;
- irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
- break;
- default:
- /* LM_LAP_DISCONNECT_INDICATION : Should never happen, we
- * are *not* yet bound to the IrLAP link. Jean II */
- pr_debug("%s(), Unknown event %s on LSAP %#02x\n",
- __func__, irlmp_event[event], self->slsap_sel);
- break;
- }
- return ret;
-}
-
-/*
- * Function irlmp_state_connect_pend (event, skb, info)
- *
- * CONNECT_PEND
- *
- */
-static int irlmp_state_connect_pend(struct lsap_cb *self, IRLMP_EVENT event,
- struct sk_buff *skb)
-{
- struct sk_buff *tx_skb;
- int ret = 0;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
-
- switch (event) {
- case LM_CONNECT_REQUEST:
- /* Keep state */
- break;
- case LM_CONNECT_RESPONSE:
- pr_debug("%s(), LM_CONNECT_RESPONSE, no indication issued yet\n",
- __func__);
- /* Keep state */
- break;
- case LM_DISCONNECT_REQUEST:
- pr_debug("%s(), LM_DISCONNECT_REQUEST, not yet bound to IrLAP connection\n",
- __func__);
- /* Keep state */
- break;
- case LM_LAP_CONNECT_CONFIRM:
- pr_debug("%s(), LS_CONNECT_CONFIRM\n", __func__);
- irlmp_next_lsap_state(self, LSAP_CONNECT);
-
- tx_skb = self->conn_skb;
- self->conn_skb = NULL;
-
- irlmp_connect_indication(self, tx_skb);
- /* Drop reference count - see irlmp_connect_indication(). */
- dev_kfree_skb(tx_skb);
- break;
- case LM_WATCHDOG_TIMEOUT:
- /* Will happen in some rare cases because of a race condition.
- * Just make sure we don't stay there forever...
- * Jean II */
- pr_debug("%s() WATCHDOG_TIMEOUT!\n", __func__);
-
- /* Go back to disconnected mode, keep the socket waiting */
- self->lap = NULL;
- self->dlsap_sel = LSAP_ANY;
- if(self->conn_skb)
- dev_kfree_skb(self->conn_skb);
- self->conn_skb = NULL;
- irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
- break;
- default:
- /* LM_LAP_DISCONNECT_INDICATION : Should never happen, we
- * are *not* yet bound to the IrLAP link. Jean II */
- pr_debug("%s(), Unknown event %s on LSAP %#02x\n",
- __func__, irlmp_event[event], self->slsap_sel);
- break;
- }
- return ret;
-}
-
-/*
- * Function irlmp_state_dtr (self, event, skb)
- *
- * DATA_TRANSFER_READY
- *
- */
-static int irlmp_state_dtr(struct lsap_cb *self, IRLMP_EVENT event,
- struct sk_buff *skb)
-{
- LM_REASON reason;
- int ret = 0;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
- IRDA_ASSERT(self->lap != NULL, return -1;);
-
- switch (event) {
- case LM_DATA_REQUEST: /* Optimize for the common case */
- irlmp_send_data_pdu(self->lap, self->dlsap_sel,
- self->slsap_sel, FALSE, skb);
- break;
- case LM_DATA_INDICATION: /* Optimize for the common case */
- irlmp_data_indication(self, skb);
- break;
- case LM_UDATA_REQUEST:
- IRDA_ASSERT(skb != NULL, return -1;);
- irlmp_send_data_pdu(self->lap, self->dlsap_sel,
- self->slsap_sel, TRUE, skb);
- break;
- case LM_UDATA_INDICATION:
- irlmp_udata_indication(self, skb);
- break;
- case LM_CONNECT_REQUEST:
- pr_debug("%s(), LM_CONNECT_REQUEST, error, LSAP already connected\n",
- __func__);
- /* Keep state */
- break;
- case LM_CONNECT_RESPONSE:
- pr_debug("%s(), LM_CONNECT_RESPONSE, error, LSAP already connected\n",
- __func__);
- /* Keep state */
- break;
- case LM_DISCONNECT_REQUEST:
- irlmp_send_lcf_pdu(self->lap, self->dlsap_sel, self->slsap_sel,
- DISCONNECT, skb);
- irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
- /* Called only from irlmp_disconnect_request(), will
- * unbind from LAP over there. Jean II */
-
- /* Try to close the LAP connection if its still there */
- if (self->lap) {
- pr_debug("%s(), trying to close IrLAP\n",
- __func__);
- irlmp_do_lap_event(self->lap,
- LM_LAP_DISCONNECT_REQUEST,
- NULL);
- }
- break;
- case LM_LAP_DISCONNECT_INDICATION:
- irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
-
- reason = irlmp_convert_lap_reason(self->lap->reason);
-
- irlmp_disconnect_indication(self, reason, NULL);
- break;
- case LM_DISCONNECT_INDICATION:
- irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
-
- IRDA_ASSERT(self->lap != NULL, return -1;);
- IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;);
-
- IRDA_ASSERT(skb != NULL, return -1;);
- IRDA_ASSERT(skb->len > 3, return -1;);
- reason = skb->data[3];
-
- /* Try to close the LAP connection */
- pr_debug("%s(), trying to close IrLAP\n", __func__);
- irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL);
-
- irlmp_disconnect_indication(self, reason, skb);
- break;
- default:
- pr_debug("%s(), Unknown event %s on LSAP %#02x\n",
- __func__, irlmp_event[event], self->slsap_sel);
- break;
- }
- return ret;
-}
-
-/*
- * Function irlmp_state_setup (event, skb, info)
- *
- * SETUP, Station Control has set up the underlying IrLAP connection.
- * An LSAP connection request has been transmitted to the peer
- * LSAP-Connection Control FSM and we are awaiting reply.
- */
-static int irlmp_state_setup(struct lsap_cb *self, IRLMP_EVENT event,
- struct sk_buff *skb)
-{
- LM_REASON reason;
- int ret = 0;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
-
- switch (event) {
- case LM_CONNECT_CONFIRM:
- irlmp_next_lsap_state(self, LSAP_DATA_TRANSFER_READY);
-
- del_timer(&self->watchdog_timer);
-
- irlmp_connect_confirm(self, skb);
- break;
- case LM_DISCONNECT_INDICATION:
- irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
-
- IRDA_ASSERT(self->lap != NULL, return -1;);
- IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;);
-
- IRDA_ASSERT(skb != NULL, return -1;);
- IRDA_ASSERT(skb->len > 3, return -1;);
- reason = skb->data[3];
-
- /* Try to close the LAP connection */
- pr_debug("%s(), trying to close IrLAP\n", __func__);
- irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL);
-
- irlmp_disconnect_indication(self, reason, skb);
- break;
- case LM_LAP_DISCONNECT_INDICATION:
- irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
-
- del_timer(&self->watchdog_timer);
-
- IRDA_ASSERT(self->lap != NULL, return -1;);
- IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;);
-
- reason = irlmp_convert_lap_reason(self->lap->reason);
-
- irlmp_disconnect_indication(self, reason, skb);
- break;
- case LM_WATCHDOG_TIMEOUT:
- pr_debug("%s() WATCHDOG_TIMEOUT!\n", __func__);
-
- IRDA_ASSERT(self->lap != NULL, return -1;);
- irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL);
- irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
-
- irlmp_disconnect_indication(self, LM_CONNECT_FAILURE, NULL);
- break;
- default:
- pr_debug("%s(), Unknown event %s on LSAP %#02x\n",
- __func__, irlmp_event[event], self->slsap_sel);
- break;
- }
- return ret;
-}
-
-/*
- * Function irlmp_state_setup_pend (event, skb, info)
- *
- * SETUP_PEND, An LM_CONNECT_REQUEST has been received from the service
- * user to set up an LSAP connection. A request has been sent to the
- * LAP FSM to set up the underlying IrLAP connection, and we
- * are awaiting confirm.
- */
-static int irlmp_state_setup_pend(struct lsap_cb *self, IRLMP_EVENT event,
- struct sk_buff *skb)
-{
- struct sk_buff *tx_skb;
- LM_REASON reason;
- int ret = 0;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(irlmp != NULL, return -1;);
-
- switch (event) {
- case LM_LAP_CONNECT_CONFIRM:
- IRDA_ASSERT(self->conn_skb != NULL, return -1;);
-
- tx_skb = self->conn_skb;
- self->conn_skb = NULL;
-
- irlmp_send_lcf_pdu(self->lap, self->dlsap_sel,
- self->slsap_sel, CONNECT_CMD, tx_skb);
- /* Drop reference count - see irlap_data_request(). */
- dev_kfree_skb(tx_skb);
-
- irlmp_next_lsap_state(self, LSAP_SETUP);
- break;
- case LM_WATCHDOG_TIMEOUT:
- pr_debug("%s() : WATCHDOG_TIMEOUT !\n", __func__);
-
- IRDA_ASSERT(self->lap != NULL, return -1;);
- irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL);
- irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
-
- irlmp_disconnect_indication(self, LM_CONNECT_FAILURE, NULL);
- break;
- case LM_LAP_DISCONNECT_INDICATION: /* LS_Disconnect.indication */
- del_timer( &self->watchdog_timer);
-
- irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
-
- reason = irlmp_convert_lap_reason(self->lap->reason);
-
- irlmp_disconnect_indication(self, reason, NULL);
- break;
- default:
- pr_debug("%s(), Unknown event %s on LSAP %#02x\n",
- __func__, irlmp_event[event], self->slsap_sel);
- break;
- }
- return ret;
-}
diff --git a/drivers/staging/irda/net/irlmp_frame.c b/drivers/staging/irda/net/irlmp_frame.c
deleted file mode 100644
index 38b0f994bc7b..000000000000
--- a/drivers/staging/irda/net/irlmp_frame.c
+++ /dev/null
@@ -1,476 +0,0 @@
-/*********************************************************************
- *
- * Filename: irlmp_frame.c
- * Version: 0.9
- * Description: IrLMP frame implementation
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Tue Aug 19 02:09:59 1997
- * Modified at: Mon Dec 13 13:41:12 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>
- * All Rights Reserved.
- * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/skbuff.h>
-#include <linux/kernel.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irlap.h>
-#include <net/irda/timer.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/irlmp_frame.h>
-#include <net/irda/discovery.h>
-
-static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap,
- __u8 slsap, int status, hashbin_t *);
-
-inline void irlmp_send_data_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap,
- int expedited, struct sk_buff *skb)
-{
- skb->data[0] = dlsap;
- skb->data[1] = slsap;
-
- if (expedited) {
- pr_debug("%s(), sending expedited data\n", __func__);
- irlap_data_request(self->irlap, skb, TRUE);
- } else
- irlap_data_request(self->irlap, skb, FALSE);
-}
-
-/*
- * Function irlmp_send_lcf_pdu (dlsap, slsap, opcode,skb)
- *
- * Send Link Control Frame to IrLAP
- */
-void irlmp_send_lcf_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap,
- __u8 opcode, struct sk_buff *skb)
-{
- __u8 *frame;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
- IRDA_ASSERT(skb != NULL, return;);
-
- frame = skb->data;
-
- frame[0] = dlsap | CONTROL_BIT;
- frame[1] = slsap;
-
- frame[2] = opcode;
-
- if (opcode == DISCONNECT)
- frame[3] = 0x01; /* Service user request */
- else
- frame[3] = 0x00; /* rsvd */
-
- irlap_data_request(self->irlap, skb, FALSE);
-}
-
-/*
- * Function irlmp_input (skb)
- *
- * Used by IrLAP to pass received data frames to IrLMP layer
- *
- */
-void irlmp_link_data_indication(struct lap_cb *self, struct sk_buff *skb,
- int unreliable)
-{
- struct lsap_cb *lsap;
- __u8 slsap_sel; /* Source (this) LSAP address */
- __u8 dlsap_sel; /* Destination LSAP address */
- __u8 *fp;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
- IRDA_ASSERT(skb->len > 2, return;);
-
- fp = skb->data;
-
- /*
- * The next statements may be confusing, but we do this so that
- * destination LSAP of received frame is source LSAP in our view
- */
- slsap_sel = fp[0] & LSAP_MASK;
- dlsap_sel = fp[1];
-
- /*
- * Check if this is an incoming connection, since we must deal with
- * it in a different way than other established connections.
- */
- if ((fp[0] & CONTROL_BIT) && (fp[2] == CONNECT_CMD)) {
- pr_debug("%s(), incoming connection, source LSAP=%d, dest LSAP=%d\n",
- __func__, slsap_sel, dlsap_sel);
-
- /* Try to find LSAP among the unconnected LSAPs */
- lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, CONNECT_CMD,
- irlmp->unconnected_lsaps);
-
- /* Maybe LSAP was already connected, so try one more time */
- if (!lsap) {
- pr_debug("%s(), incoming connection for LSAP already connected\n",
- __func__);
- lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, 0,
- self->lsaps);
- }
- } else
- lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, 0,
- self->lsaps);
-
- if (lsap == NULL) {
- pr_debug("IrLMP, Sorry, no LSAP for received frame!\n");
- pr_debug("%s(), slsap_sel = %02x, dlsap_sel = %02x\n",
- __func__, slsap_sel, dlsap_sel);
- if (fp[0] & CONTROL_BIT) {
- pr_debug("%s(), received control frame %02x\n",
- __func__, fp[2]);
- } else {
- pr_debug("%s(), received data frame\n", __func__);
- }
- return;
- }
-
- /*
- * Check if we received a control frame?
- */
- if (fp[0] & CONTROL_BIT) {
- switch (fp[2]) {
- case CONNECT_CMD:
- lsap->lap = self;
- irlmp_do_lsap_event(lsap, LM_CONNECT_INDICATION, skb);
- break;
- case CONNECT_CNF:
- irlmp_do_lsap_event(lsap, LM_CONNECT_CONFIRM, skb);
- break;
- case DISCONNECT:
- pr_debug("%s(), Disconnect indication!\n",
- __func__);
- irlmp_do_lsap_event(lsap, LM_DISCONNECT_INDICATION,
- skb);
- break;
- case ACCESSMODE_CMD:
- pr_debug("Access mode cmd not implemented!\n");
- break;
- case ACCESSMODE_CNF:
- pr_debug("Access mode cnf not implemented!\n");
- break;
- default:
- pr_debug("%s(), Unknown control frame %02x\n",
- __func__, fp[2]);
- break;
- }
- } else if (unreliable) {
- /* Optimize and bypass the state machine if possible */
- if (lsap->lsap_state == LSAP_DATA_TRANSFER_READY)
- irlmp_udata_indication(lsap, skb);
- else
- irlmp_do_lsap_event(lsap, LM_UDATA_INDICATION, skb);
- } else {
- /* Optimize and bypass the state machine if possible */
- if (lsap->lsap_state == LSAP_DATA_TRANSFER_READY)
- irlmp_data_indication(lsap, skb);
- else
- irlmp_do_lsap_event(lsap, LM_DATA_INDICATION, skb);
- }
-}
-
-/*
- * Function irlmp_link_unitdata_indication (self, skb)
- *
- *
- *
- */
-#ifdef CONFIG_IRDA_ULTRA
-void irlmp_link_unitdata_indication(struct lap_cb *self, struct sk_buff *skb)
-{
- struct lsap_cb *lsap;
- __u8 slsap_sel; /* Source (this) LSAP address */
- __u8 dlsap_sel; /* Destination LSAP address */
- __u8 pid; /* Protocol identifier */
- __u8 *fp;
- unsigned long flags;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
- IRDA_ASSERT(skb->len > 2, return;);
-
- fp = skb->data;
-
- /*
- * The next statements may be confusing, but we do this so that
- * destination LSAP of received frame is source LSAP in our view
- */
- slsap_sel = fp[0] & LSAP_MASK;
- dlsap_sel = fp[1];
- pid = fp[2];
-
- if (pid & 0x80) {
- pr_debug("%s(), extension in PID not supp!\n",
- __func__);
- return;
- }
-
- /* Check if frame is addressed to the connectionless LSAP */
- if ((slsap_sel != LSAP_CONNLESS) || (dlsap_sel != LSAP_CONNLESS)) {
- pr_debug("%s(), dropping frame!\n", __func__);
- return;
- }
-
- /* Search the connectionless LSAP */
- spin_lock_irqsave(&irlmp->unconnected_lsaps->hb_spinlock, flags);
- lsap = (struct lsap_cb *) hashbin_get_first(irlmp->unconnected_lsaps);
- while (lsap != NULL) {
- /*
- * Check if source LSAP and dest LSAP selectors and PID match.
- */
- if ((lsap->slsap_sel == slsap_sel) &&
- (lsap->dlsap_sel == dlsap_sel) &&
- (lsap->pid == pid))
- {
- break;
- }
- lsap = (struct lsap_cb *) hashbin_get_next(irlmp->unconnected_lsaps);
- }
- spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags);
-
- if (lsap)
- irlmp_connless_data_indication(lsap, skb);
- else {
- pr_debug("%s(), found no matching LSAP!\n", __func__);
- }
-}
-#endif /* CONFIG_IRDA_ULTRA */
-
-/*
- * Function irlmp_link_disconnect_indication (reason, userdata)
- *
- * IrLAP has disconnected
- *
- */
-void irlmp_link_disconnect_indication(struct lap_cb *lap,
- struct irlap_cb *irlap,
- LAP_REASON reason,
- struct sk_buff *skb)
-{
- IRDA_ASSERT(lap != NULL, return;);
- IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return;);
-
- lap->reason = reason;
- lap->daddr = DEV_ADDR_ANY;
-
- /* FIXME: must do something with the skb if any */
-
- /*
- * Inform station state machine
- */
- irlmp_do_lap_event(lap, LM_LAP_DISCONNECT_INDICATION, NULL);
-}
-
-/*
- * Function irlmp_link_connect_indication (qos)
- *
- * Incoming LAP connection!
- *
- */
-void irlmp_link_connect_indication(struct lap_cb *self, __u32 saddr,
- __u32 daddr, struct qos_info *qos,
- struct sk_buff *skb)
-{
- /* Copy QoS settings for this session */
- self->qos = qos;
-
- /* Update destination device address */
- self->daddr = daddr;
- IRDA_ASSERT(self->saddr == saddr, return;);
-
- irlmp_do_lap_event(self, LM_LAP_CONNECT_INDICATION, skb);
-}
-
-/*
- * Function irlmp_link_connect_confirm (qos)
- *
- * LAP connection confirmed!
- *
- */
-void irlmp_link_connect_confirm(struct lap_cb *self, struct qos_info *qos,
- struct sk_buff *skb)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
- IRDA_ASSERT(qos != NULL, return;);
-
- /* Don't need use the skb for now */
-
- /* Copy QoS settings for this session */
- self->qos = qos;
-
- irlmp_do_lap_event(self, LM_LAP_CONNECT_CONFIRM, NULL);
-}
-
-/*
- * Function irlmp_link_discovery_indication (self, log)
- *
- * Device is discovering us
- *
- * It's not an answer to our own discoveries, just another device trying
- * to perform discovery, but we don't want to miss the opportunity
- * to exploit this information, because :
- * o We may not actively perform discovery (just passive discovery)
- * o This type of discovery is much more reliable. In some cases, it
- * seem that less than 50% of our discoveries get an answer, while
- * we always get ~100% of these.
- * o Make faster discovery, statistically divide time of discovery
- * events by 2 (important for the latency aspect and user feel)
- * o Even is we do active discovery, the other node might not
- * answer our discoveries (ex: Palm). The Palm will just perform
- * one active discovery and connect directly to us.
- *
- * However, when both devices discover each other, they might attempt to
- * connect to each other following the discovery event, and it would create
- * collisions on the medium (SNRM battle).
- * The "fix" for that is to disable all connection requests in IrLAP
- * for 100ms after a discovery indication by setting the media_busy flag.
- * Previously, we used to postpone the event which was quite ugly. Now
- * that IrLAP takes care of this problem, just pass the event up...
- *
- * Jean II
- */
-void irlmp_link_discovery_indication(struct lap_cb *self,
- discovery_t *discovery)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
-
- /* Add to main log, cleanup */
- irlmp_add_discovery(irlmp->cachelog, discovery);
-
- /* Just handle it the same way as a discovery confirm,
- * bypass the LM_LAP state machine (see below) */
- irlmp_discovery_confirm(irlmp->cachelog, DISCOVERY_PASSIVE);
-}
-
-/*
- * Function irlmp_link_discovery_confirm (self, log)
- *
- * Called by IrLAP with a list of discoveries after the discovery
- * request has been carried out. A NULL log is received if IrLAP
- * was unable to carry out the discovery request
- *
- */
-void irlmp_link_discovery_confirm(struct lap_cb *self, hashbin_t *log)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
-
- /* Add to main log, cleanup */
- irlmp_add_discovery_log(irlmp->cachelog, log);
-
- /* Propagate event to various LSAPs registered for it.
- * We bypass the LM_LAP state machine because
- * 1) We do it regardless of the LM_LAP state
- * 2) It doesn't affect the LM_LAP state
- * 3) Faster, slimer, simpler, ...
- * Jean II */
- irlmp_discovery_confirm(irlmp->cachelog, DISCOVERY_ACTIVE);
-}
-
-#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
-static inline void irlmp_update_cache(struct lap_cb *lap,
- struct lsap_cb *lsap)
-{
- /* Prevent concurrent read to get garbage */
- lap->cache.valid = FALSE;
- /* Update cache entry */
- lap->cache.dlsap_sel = lsap->dlsap_sel;
- lap->cache.slsap_sel = lsap->slsap_sel;
- lap->cache.lsap = lsap;
- lap->cache.valid = TRUE;
-}
-#endif
-
-/*
- * Function irlmp_find_handle (self, dlsap_sel, slsap_sel, status, queue)
- *
- * Find handle associated with destination and source LSAP
- *
- * Any IrDA connection (LSAP/TSAP) is uniquely identified by
- * 3 parameters, the local lsap, the remote lsap and the remote address.
- * We may initiate multiple connections to the same remote service
- * (they will have different local lsap), a remote device may initiate
- * multiple connections to the same local service (they will have
- * different remote lsap), or multiple devices may connect to the same
- * service and may use the same remote lsap (and they will have
- * different remote address).
- * So, where is the remote address ? Each LAP connection is made with
- * a single remote device, so imply a specific remote address.
- * Jean II
- */
-static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap_sel,
- __u8 slsap_sel, int status,
- hashbin_t *queue)
-{
- struct lsap_cb *lsap;
- unsigned long flags;
-
- /*
- * Optimize for the common case. We assume that the last frame
- * received is in the same connection as the last one, so check in
- * cache first to avoid the linear search
- */
-#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
- if ((self->cache.valid) &&
- (self->cache.slsap_sel == slsap_sel) &&
- (self->cache.dlsap_sel == dlsap_sel))
- {
- return self->cache.lsap;
- }
-#endif
-
- spin_lock_irqsave(&queue->hb_spinlock, flags);
-
- lsap = (struct lsap_cb *) hashbin_get_first(queue);
- while (lsap != NULL) {
- /*
- * If this is an incoming connection, then the destination
- * LSAP selector may have been specified as LM_ANY so that
- * any client can connect. In that case we only need to check
- * if the source LSAP (in our view!) match!
- */
- if ((status == CONNECT_CMD) &&
- (lsap->slsap_sel == slsap_sel) &&
- (lsap->dlsap_sel == LSAP_ANY)) {
- /* This is where the dest lsap sel is set on incoming
- * lsaps */
- lsap->dlsap_sel = dlsap_sel;
- break;
- }
- /*
- * Check if source LSAP and dest LSAP selectors match.
- */
- if ((lsap->slsap_sel == slsap_sel) &&
- (lsap->dlsap_sel == dlsap_sel))
- break;
-
- lsap = (struct lsap_cb *) hashbin_get_next(queue);
- }
-#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
- if(lsap)
- irlmp_update_cache(self, lsap);
-#endif
- spin_unlock_irqrestore(&queue->hb_spinlock, flags);
-
- /* Return what we've found or NULL */
- return lsap;
-}
diff --git a/drivers/staging/irda/net/irmod.c b/drivers/staging/irda/net/irmod.c
deleted file mode 100644
index 4319f4ff66b0..000000000000
--- a/drivers/staging/irda/net/irmod.c
+++ /dev/null
@@ -1,199 +0,0 @@
-/*********************************************************************
- *
- * Filename: irmod.c
- * Version: 0.9
- * Description: IrDA stack main entry points
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Mon Dec 15 13:55:39 1997
- * Modified at: Wed Jan 5 15:12:41 2000
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1997, 1999-2000 Dag Brattli, All Rights Reserved.
- * Copyright (c) 2000-2004 Jean Tourrilhes <jt@hpl.hp.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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-/*
- * This file contains the main entry points of the IrDA stack.
- * They are in this file and not af_irda.c because some developpers
- * are using the IrDA stack without the socket API (compiling out
- * af_irda.c).
- * Jean II
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irmod.h> /* notify_t */
-#include <net/irda/irlap.h> /* irlap_init */
-#include <net/irda/irlmp.h> /* irlmp_init */
-#include <net/irda/iriap.h> /* iriap_init */
-#include <net/irda/irttp.h> /* irttp_init */
-#include <net/irda/irda_device.h> /* irda_device_init */
-
-/* Packet type handler.
- * Tell the kernel how IrDA packets should be handled.
- */
-static struct packet_type irda_packet_type __read_mostly = {
- .type = cpu_to_be16(ETH_P_IRDA),
- .func = irlap_driver_rcv, /* Packet type handler irlap_frame.c */
-};
-
-/*
- * Function irda_notify_init (notify)
- *
- * Used for initializing the notify structure
- *
- */
-void irda_notify_init(notify_t *notify)
-{
- notify->data_indication = NULL;
- notify->udata_indication = NULL;
- notify->connect_confirm = NULL;
- notify->connect_indication = NULL;
- notify->disconnect_indication = NULL;
- notify->flow_indication = NULL;
- notify->status_indication = NULL;
- notify->instance = NULL;
- strlcpy(notify->name, "Unknown", sizeof(notify->name));
-}
-EXPORT_SYMBOL(irda_notify_init);
-
-/*
- * Function irda_init (void)
- *
- * Protocol stack initialisation entry point.
- * Initialise the various components of the IrDA stack
- */
-static int __init irda_init(void)
-{
- int ret = 0;
-
- /* Lower layer of the stack */
- irlmp_init();
- irlap_init();
-
- /* Driver/dongle support */
- irda_device_init();
-
- /* Higher layers of the stack */
- iriap_init();
- irttp_init();
- ret = irsock_init();
- if (ret < 0)
- goto out_err_1;
-
- /* Add IrDA packet type (Start receiving packets) */
- dev_add_pack(&irda_packet_type);
-
- /* External APIs */
-#ifdef CONFIG_PROC_FS
- irda_proc_register();
-#endif
-#ifdef CONFIG_SYSCTL
- ret = irda_sysctl_register();
- if (ret < 0)
- goto out_err_2;
-#endif
-
- ret = irda_nl_register();
- if (ret < 0)
- goto out_err_3;
-
- return 0;
-
- out_err_3:
-#ifdef CONFIG_SYSCTL
- irda_sysctl_unregister();
- out_err_2:
-#endif
-#ifdef CONFIG_PROC_FS
- irda_proc_unregister();
-#endif
-
- /* Remove IrDA packet type (stop receiving packets) */
- dev_remove_pack(&irda_packet_type);
-
- /* Remove higher layers */
- irsock_cleanup();
- out_err_1:
- irttp_cleanup();
- iriap_cleanup();
-
- /* Remove lower layers */
- irda_device_cleanup();
- irlap_cleanup(); /* Must be done before irlmp_cleanup()! DB */
-
- /* Remove middle layer */
- irlmp_cleanup();
-
-
- return ret;
-}
-
-/*
- * Function irda_cleanup (void)
- *
- * Protocol stack cleanup/removal entry point.
- * Cleanup the various components of the IrDA stack
- */
-static void __exit irda_cleanup(void)
-{
- /* Remove External APIs */
- irda_nl_unregister();
-
-#ifdef CONFIG_SYSCTL
- irda_sysctl_unregister();
-#endif
-#ifdef CONFIG_PROC_FS
- irda_proc_unregister();
-#endif
-
- /* Remove IrDA packet type (stop receiving packets) */
- dev_remove_pack(&irda_packet_type);
-
- /* Remove higher layers */
- irsock_cleanup();
- irttp_cleanup();
- iriap_cleanup();
-
- /* Remove lower layers */
- irda_device_cleanup();
- irlap_cleanup(); /* Must be done before irlmp_cleanup()! DB */
-
- /* Remove middle layer */
- irlmp_cleanup();
-}
-
-/*
- * The IrDA stack must be initialised *before* drivers get initialised,
- * and *before* higher protocols (IrLAN/IrCOMM/IrNET) get initialised,
- * otherwise bad things will happen (hashbins will be NULL for example).
- * Those modules are at module_init()/device_initcall() level.
- *
- * On the other hand, it needs to be initialised *after* the basic
- * networking, the /proc/net filesystem and sysctl module. Those are
- * currently initialised in .../init/main.c (before initcalls).
- * Also, IrDA drivers needs to be initialised *after* the random number
- * generator (main stack and higher layer init don't need it anymore).
- *
- * Jean II
- */
-device_initcall(irda_init);
-module_exit(irda_cleanup);
-
-MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no> & Jean Tourrilhes <jt@hpl.hp.com>");
-MODULE_DESCRIPTION("The Linux IrDA Protocol Stack");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_NETPROTO(PF_IRDA);
diff --git a/drivers/staging/irda/net/irnet/Kconfig b/drivers/staging/irda/net/irnet/Kconfig
deleted file mode 100644
index 28c557f0fdd2..000000000000
--- a/drivers/staging/irda/net/irnet/Kconfig
+++ /dev/null
@@ -1,13 +0,0 @@
-config IRNET
- tristate "IrNET protocol"
- depends on IRDA && PPP
- help
- Say Y here if you want to build support for the IrNET protocol.
- To compile it as a module, choose M here: the module will be
- called irnet. IrNET is a PPP driver, so you will also need a
- working PPP subsystem (driver, daemon and config)...
-
- IrNET is an alternate way to transfer TCP/IP traffic over IrDA. It
- uses synchronous PPP over a set of point to point IrDA sockets. You
- can use it between Linux machine or with W2k.
-
diff --git a/drivers/staging/irda/net/irnet/Makefile b/drivers/staging/irda/net/irnet/Makefile
deleted file mode 100644
index 61c365c8a2a0..000000000000
--- a/drivers/staging/irda/net/irnet/Makefile
+++ /dev/null
@@ -1,7 +0,0 @@
-#
-# Makefile for the Linux IrDA IrNET protocol layer.
-#
-
-obj-$(CONFIG_IRNET) += irnet.o
-
-irnet-y := irnet_ppp.o irnet_irda.o
diff --git a/drivers/staging/irda/net/irnet/irnet.h b/drivers/staging/irda/net/irnet/irnet.h
deleted file mode 100644
index 9d451f8ed47a..000000000000
--- a/drivers/staging/irda/net/irnet/irnet.h
+++ /dev/null
@@ -1,522 +0,0 @@
-/*
- * IrNET protocol module : Synchronous PPP over an IrDA socket.
- *
- * Jean II - HPL `00 - <jt@hpl.hp.com>
- *
- * This file contains definitions and declarations global to the IrNET module,
- * all grouped in one place...
- * This file is a *private* header, so other modules don't want to know
- * what's in there...
- *
- * Note : as most part of the Linux kernel, this module is available
- * under the GNU General Public License (GPL).
- */
-
-#ifndef IRNET_H
-#define IRNET_H
-
-/************************** DOCUMENTATION ***************************/
-/*
- * What is IrNET
- * -------------
- * IrNET is a protocol allowing to carry TCP/IP traffic between two
- * IrDA peers in an efficient fashion. It is a thin layer, passing PPP
- * packets to IrTTP and vice versa. It uses PPP in synchronous mode,
- * because IrTTP offer a reliable sequenced packet service (as opposed
- * to a byte stream). In fact, you could see IrNET as carrying TCP/IP
- * in a IrDA socket, using PPP to provide the glue.
- *
- * The main difference with traditional PPP over IrCOMM is that we
- * avoid the framing and serial emulation which are a performance
- * bottleneck. It also allows multipoint communications in a sensible
- * fashion.
- *
- * The main difference with IrLAN is that we use PPP for the link
- * management, which is more standard, interoperable and flexible than
- * the IrLAN protocol. For example, PPP adds authentication,
- * encryption, compression, header compression and automated routing
- * setup. And, as IrNET let PPP do the hard work, the implementation
- * is much simpler than IrLAN.
- *
- * The Linux implementation
- * ------------------------
- * IrNET is written on top of the Linux-IrDA stack, and interface with
- * the generic Linux PPP driver. Because IrNET depend on recent
- * changes of the PPP driver interface, IrNET will work only with very
- * recent kernel (2.3.99-pre6 and up).
- *
- * The present implementation offer the following features :
- * o simple user interface using pppd
- * o efficient implementation (interface directly to PPP and IrTTP)
- * o addressing (you can specify the name of the IrNET recipient)
- * o multipoint operation (limited by IrLAP specification)
- * o information in /proc/net/irda/irnet
- * o IrNET events on /dev/irnet (for user space daemon)
- * o IrNET daemon (irnetd) to automatically handle incoming requests
- * o Windows 2000 compatibility (tested, but need more work)
- * Currently missing :
- * o Lot's of testing (that's your job)
- * o Connection retries (may be too hard to do)
- * o Check pppd persist mode
- * o User space daemon (to automatically handle incoming requests)
- *
- * The setup is not currently the most easy, but this should get much
- * better when everything will get integrated...
- *
- * Acknowledgements
- * ----------------
- * This module is based on :
- * o The PPP driver (ppp_synctty/ppp_generic) by Paul Mackerras
- * o The IrLAN protocol (irlan_common/XXX) by Dag Brattli
- * o The IrSock interface (af_irda) by Dag Brattli
- * o Some other bits from the kernel and my drivers...
- * Infinite thanks to those brave souls for providing the infrastructure
- * upon which IrNET is built.
- *
- * Thanks to all my colleagues in HP for helping me. In particular,
- * thanks to Salil Pradhan and Bill Serra for W2k testing...
- * Thanks to Luiz Magalhaes for irnetd and much testing...
- *
- * Thanks to Alan Cox for answering lot's of my stupid questions, and
- * to Paul Mackerras answering my questions on how to best integrate
- * IrNET and pppd.
- *
- * Jean II
- *
- * Note on some implementations choices...
- * ------------------------------------
- * 1) Direct interface vs tty/socket
- * I could have used a tty interface to hook to ppp and use the full
- * socket API to connect to IrDA. The code would have been easier to
- * maintain, and maybe the code would have been smaller...
- * Instead, we hook directly to ppp_generic and to IrTTP, which make
- * things more complicated...
- *
- * The first reason is flexibility : this allow us to create IrNET
- * instances on demand (no /dev/ircommX crap) and to allow linkname
- * specification on pppd command line...
- *
- * Second reason is speed optimisation. If you look closely at the
- * transmit and receive paths, you will notice that they are "super lean"
- * (that's why they look ugly), with no function calls and as little data
- * copy and modification as I could...
- *
- * 2) irnetd in user space
- * irnetd is implemented in user space, which is necessary to call pppd.
- * This also give maximum benefits in term of flexibility and customability,
- * and allow to offer the event channel, useful for other stuff like debug.
- *
- * On the other hand, this require a loose coordination between the
- * present module and irnetd. One critical area is how incoming request
- * are handled.
- * When irnet receive an incoming request, it send an event to irnetd and
- * drop the incoming IrNET socket.
- * irnetd start a pppd instance, which create a new IrNET socket. This new
- * socket is then connected in the originating node to the pppd instance.
- * At this point, in the originating node, the first socket is closed.
- *
- * I admit, this is a bit messy and waste some resources. The alternative
- * is caching incoming socket, and that's also quite messy and waste
- * resources.
- * We also make connection time slower. For example, on a 115 kb/s link it
- * adds 60ms to the connection time (770 ms). However, this is slower than
- * the time it takes to fire up pppd on my P133...
- *
- *
- * History :
- * -------
- *
- * v1 - 15.5.00 - Jean II
- * o Basic IrNET (hook to ppp_generic & IrTTP - incl. multipoint)
- * o control channel on /dev/irnet (set name/address)
- * o event channel on /dev/irnet (for user space daemon)
- *
- * v2 - 5.6.00 - Jean II
- * o Enable DROP_NOT_READY to avoid PPP timeouts & other weirdness...
- * o Add DISCONNECT_TO event and rename DISCONNECT_FROM.
- * o Set official device number alloaction on /dev/irnet
- *
- * v3 - 30.8.00 - Jean II
- * o Update to latest Linux-IrDA changes :
- * - queue_t => irda_queue_t
- * o Update to ppp-2.4.0 :
- * - move irda_irnet_connect from PPPIOCATTACH to TIOCSETD
- * o Add EXPIRE event (depend on new IrDA-Linux patch)
- * o Switch from `hashbin_remove' to `hashbin_remove_this' to fix
- * a multilink bug... (depend on new IrDA-Linux patch)
- * o fix a self->daddr to self->raddr in irda_irnet_connect to fix
- * another multilink bug (darn !)
- * o Remove LINKNAME_IOCTL cruft
- *
- * v3b - 31.8.00 - Jean II
- * o Dump discovery log at event channel startup
- *
- * v4 - 28.9.00 - Jean II
- * o Fix interaction between poll/select and dump discovery log
- * o Add IRNET_BLOCKED_LINK event (depend on new IrDA-Linux patch)
- * o Add IRNET_NOANSWER_FROM event (mostly to help support)
- * o Release flow control in disconnect_indication
- * o Block packets while connecting (speed up connections)
- *
- * v5 - 11.01.01 - Jean II
- * o Init self->max_header_size, just in case...
- * o Set up ap->chan.hdrlen, to get zero copy on tx side working.
- * o avoid tx->ttp->flow->ppp->tx->... loop, by checking flow state
- * Thanks to Christian Gennerat for finding this bug !
- * ---
- * o Declare the proper MTU/MRU that we can support
- * (but PPP doesn't read the MTU value :-()
- * o Declare hashbin HB_NOLOCK instead of HB_LOCAL to avoid
- * disabling and enabling irq twice
- *
- * v6 - 31.05.01 - Jean II
- * o Print source address in Found, Discovery, Expiry & Request events
- * o Print requested source address in /proc/net/irnet
- * o Change control channel input. Allow multiple commands in one line.
- * o Add saddr command to change ap->rsaddr (and use that in IrDA)
- * ---
- * o Make the IrDA connection procedure totally asynchronous.
- * Heavy rewrite of the IAS query code and the whole connection
- * procedure. Now, irnet_connect() no longer need to be called from
- * a process context...
- * o Enable IrDA connect retries in ppp_irnet_send(). The good thing
- * is that IrDA connect retries are directly driven by PPP LCP
- * retries (we retry for each LCP packet), so that everything
- * is transparently controlled from pppd lcp-max-configure.
- * o Add ttp_connect flag to prevent rentry on the connect procedure
- * o Test and fixups to eliminate side effects of retries
- *
- * v7 - 22.08.01 - Jean II
- * o Cleanup : Change "saddr = 0x0" to "saddr = DEV_ADDR_ANY"
- * o Fix bug in BLOCK_WHEN_CONNECT introduced in v6 : due to the
- * asynchronous IAS query, self->tsap is NULL when PPP send the
- * first packet. This was preventing "connect-delay 0" to work.
- * Change the test in ppp_irnet_send() to self->ttp_connect.
- *
- * v8 - 1.11.01 - Jean II
- * o Tighten the use of self->ttp_connect and self->ttp_open to
- * prevent various race conditions.
- * o Avoid leaking discovery log and skb
- * o Replace "self" with "server" in irnet_connect_indication() to
- * better detect cut'n'paste error ;-)
- *
- * v9 - 29.11.01 - Jean II
- * o Fix event generation in disconnect indication that I broke in v8
- * It was always generation "No-Answer" because I was testing ttp_open
- * just after clearing it. *blush*.
- * o Use newly created irttp_listen() to fix potential crash when LAP
- * destroyed before irnet module removed.
- *
- * v10 - 4.3.2 - Jean II
- * o When receiving a disconnect indication, don't reenable the
- * PPP Tx queue, this will trigger a reconnect. Instead, close
- * the channel, which will kill pppd...
- *
- * v11 - 20.3.02 - Jean II
- * o Oops ! v10 fix disabled IrNET retries and passive behaviour.
- * Better fix in irnet_disconnect_indication() :
- * - if connected, kill pppd via hangup.
- * - if not connected, reenable ppp Tx, which trigger IrNET retry.
- *
- * v12 - 10.4.02 - Jean II
- * o Fix race condition in irnet_connect_indication().
- * If the socket was already trying to connect, drop old connection
- * and use new one only if acting as primary. See comments.
- *
- * v13 - 30.5.02 - Jean II
- * o Update module init code
- *
- * v14 - 20.2.03 - Jean II
- * o Add discovery hint bits in the control channel.
- * o Remove obsolete MOD_INC/DEC_USE_COUNT in favor of .owner
- *
- * v15 - 7.4.03 - Jean II
- * o Replace spin_lock_irqsave() with spin_lock_bh() so that we can
- * use ppp_unit_number(). It's probably also better overall...
- * o Disable call to ppp_unregister_channel(), because we can't do it.
- */
-
-/***************************** INCLUDES *****************************/
-
-#include <linux/module.h>
-
-#include <linux/kernel.h>
-#include <linux/skbuff.h>
-#include <linux/tty.h>
-#include <linux/proc_fs.h>
-#include <linux/netdevice.h>
-#include <linux/poll.h>
-#include <linux/capability.h>
-#include <linux/ctype.h> /* isspace() */
-#include <linux/string.h> /* skip_spaces() */
-#include <linux/uaccess.h>
-#include <linux/init.h>
-
-#include <linux/ppp_defs.h>
-#include <linux/ppp-ioctl.h>
-#include <linux/ppp_channel.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/iriap.h>
-#include <net/irda/irias_object.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/irttp.h>
-#include <net/irda/discovery.h>
-
-/***************************** OPTIONS *****************************/
-/*
- * Define or undefine to compile or not some optional part of the
- * IrNET driver...
- * Note : the present defaults make sense, play with that at your
- * own risk...
- */
-/* IrDA side of the business... */
-#define DISCOVERY_NOMASK /* To enable W2k compatibility... */
-#define ADVERTISE_HINT /* Advertise IrLAN hint bit */
-#define ALLOW_SIMULT_CONNECT /* This seem to work, cross fingers... */
-#define DISCOVERY_EVENTS /* Query the discovery log to post events */
-#define INITIAL_DISCOVERY /* Dump current discovery log as events */
-#undef STREAM_COMPAT /* Not needed - potentially messy */
-#undef CONNECT_INDIC_KICK /* Might mess IrDA, not needed */
-#undef FAIL_SEND_DISCONNECT /* Might mess IrDA, not needed */
-#undef PASS_CONNECT_PACKETS /* Not needed ? Safe */
-#undef MISSING_PPP_API /* Stuff I wish I could do */
-
-/* PPP side of the business */
-#define BLOCK_WHEN_CONNECT /* Block packets when connecting */
-#define CONNECT_IN_SEND /* Retry IrDA connection procedure */
-#undef FLUSH_TO_PPP /* Not sure about this one, let's play safe */
-#undef SECURE_DEVIRNET /* Bah... */
-
-/****************************** DEBUG ******************************/
-
-/*
- * This set of flags enable and disable all the various warning,
- * error and debug message of this driver.
- * Each section can be enabled and disabled independently
- */
-/* In the PPP part */
-#define DEBUG_CTRL_TRACE 0 /* Control channel */
-#define DEBUG_CTRL_INFO 0 /* various info */
-#define DEBUG_CTRL_ERROR 1 /* problems */
-#define DEBUG_FS_TRACE 0 /* filesystem callbacks */
-#define DEBUG_FS_INFO 0 /* various info */
-#define DEBUG_FS_ERROR 1 /* problems */
-#define DEBUG_PPP_TRACE 0 /* PPP related functions */
-#define DEBUG_PPP_INFO 0 /* various info */
-#define DEBUG_PPP_ERROR 1 /* problems */
-#define DEBUG_MODULE_TRACE 0 /* module insertion/removal */
-#define DEBUG_MODULE_ERROR 1 /* problems */
-
-/* In the IrDA part */
-#define DEBUG_IRDA_SR_TRACE 0 /* IRDA subroutines */
-#define DEBUG_IRDA_SR_INFO 0 /* various info */
-#define DEBUG_IRDA_SR_ERROR 1 /* problems */
-#define DEBUG_IRDA_SOCK_TRACE 0 /* IRDA main socket functions */
-#define DEBUG_IRDA_SOCK_INFO 0 /* various info */
-#define DEBUG_IRDA_SOCK_ERROR 1 /* problems */
-#define DEBUG_IRDA_SERV_TRACE 0 /* The IrNET server */
-#define DEBUG_IRDA_SERV_INFO 0 /* various info */
-#define DEBUG_IRDA_SERV_ERROR 1 /* problems */
-#define DEBUG_IRDA_TCB_TRACE 0 /* IRDA IrTTP callbacks */
-#define DEBUG_IRDA_CB_INFO 0 /* various info */
-#define DEBUG_IRDA_CB_ERROR 1 /* problems */
-#define DEBUG_IRDA_OCB_TRACE 0 /* IRDA other callbacks */
-#define DEBUG_IRDA_OCB_INFO 0 /* various info */
-#define DEBUG_IRDA_OCB_ERROR 1 /* problems */
-
-#define DEBUG_ASSERT 0 /* Verify all assertions */
-
-/*
- * These are the macros we are using to actually print the debug
- * statements. Don't look at it, it's ugly...
- *
- * One of the trick is that, as the DEBUG_XXX are constant, the
- * compiler will optimise away the if() in all cases.
- */
-/* All error messages (will show up in the normal logs) */
-#define DERROR(dbg, format, args...) \
- {if(DEBUG_##dbg) \
- printk(KERN_INFO "irnet: %s(): " format, __func__ , ##args);}
-
-/* Normal debug message (will show up in /var/log/debug) */
-#define DEBUG(dbg, format, args...) \
- {if(DEBUG_##dbg) \
- printk(KERN_DEBUG "irnet: %s(): " format, __func__ , ##args);}
-
-/* Entering a function (trace) */
-#define DENTER(dbg, format, args...) \
- {if(DEBUG_##dbg) \
- printk(KERN_DEBUG "irnet: -> %s" format, __func__ , ##args);}
-
-/* Entering and exiting a function in one go (trace) */
-#define DPASS(dbg, format, args...) \
- {if(DEBUG_##dbg) \
- printk(KERN_DEBUG "irnet: <>%s" format, __func__ , ##args);}
-
-/* Exiting a function (trace) */
-#define DEXIT(dbg, format, args...) \
- {if(DEBUG_##dbg) \
- printk(KERN_DEBUG "irnet: <-%s()" format, __func__ , ##args);}
-
-/* Exit a function with debug */
-#define DRETURN(ret, dbg, args...) \
- {DEXIT(dbg, ": " args);\
- return ret; }
-
-/* Exit a function on failed condition */
-#define DABORT(cond, ret, dbg, args...) \
- {if(cond) {\
- DERROR(dbg, args);\
- return ret; }}
-
-/* Invalid assertion, print out an error and exit... */
-#define DASSERT(cond, ret, dbg, args...) \
- {if((DEBUG_ASSERT) && !(cond)) {\
- DERROR(dbg, "Invalid assertion: " args);\
- return ret; }}
-
-/************************ CONSTANTS & MACROS ************************/
-
-/* Paranoia */
-#define IRNET_MAGIC 0xB00754
-
-/* Number of control events in the control channel buffer... */
-#define IRNET_MAX_EVENTS 8 /* Should be more than enough... */
-
-/****************************** TYPES ******************************/
-
-/*
- * This is the main structure where we store all the data pertaining to
- * one instance of irnet.
- * Note : in irnet functions, a pointer this structure is usually called
- * "ap" or "self". If the code is borrowed from the IrDA stack, it tend
- * to be called "self", and if it is borrowed from the PPP driver it is
- * "ap". Apart from that, it's exactly the same structure ;-)
- */
-typedef struct irnet_socket
-{
- /* ------------------- Instance management ------------------- */
- /* We manage a linked list of IrNET socket instances */
- irda_queue_t q; /* Must be first - for hasbin */
- int magic; /* Paranoia */
-
- /* --------------------- FileSystem part --------------------- */
- /* "pppd" interact directly with us on a /dev/ file */
- struct file * file; /* File descriptor of this instance */
- /* TTY stuff - to keep "pppd" happy */
- struct ktermios termios; /* Various tty flags */
- /* Stuff for the control channel */
- int event_index; /* Last read in the event log */
-
- /* ------------------------- PPP part ------------------------- */
- /* We interface directly to the ppp_generic driver in the kernel */
- int ppp_open; /* registered with ppp_generic */
- struct ppp_channel chan; /* Interface to generic ppp layer */
-
- int mru; /* Max size of PPP payload */
- u32 xaccm[8]; /* Asynchronous character map (just */
- u32 raccm; /* to please pppd - dummy) */
- unsigned int flags; /* PPP flags (compression, ...) */
- unsigned int rbits; /* Unused receive flags ??? */
- struct work_struct disconnect_work; /* Process context disconnection */
- /* ------------------------ IrTTP part ------------------------ */
- /* We create a pseudo "socket" over the IrDA tranport */
- unsigned long ttp_open; /* Set when IrTTP is ready */
- unsigned long ttp_connect; /* Set when IrTTP is connecting */
- struct tsap_cb * tsap; /* IrTTP instance (the connection) */
-
- char rname[NICKNAME_MAX_LEN + 1];
- /* IrDA nickname of destination */
- __u32 rdaddr; /* Requested peer IrDA address */
- __u32 rsaddr; /* Requested local IrDA address */
- __u32 daddr; /* actual peer IrDA address */
- __u32 saddr; /* my local IrDA address */
- __u8 dtsap_sel; /* Remote TSAP selector */
- __u8 stsap_sel; /* Local TSAP selector */
-
- __u32 max_sdu_size_rx;/* Socket parameters used for IrTTP */
- __u32 max_sdu_size_tx;
- __u32 max_data_size;
- __u8 max_header_size;
- LOCAL_FLOW tx_flow; /* State of the Tx path in IrTTP */
-
- /* ------------------- IrLMP and IrIAS part ------------------- */
- /* Used for IrDA Discovery and socket name resolution */
- void * ckey; /* IrLMP client handle */
- __u16 mask; /* Hint bits mask (filter discov.)*/
- int nslots; /* Number of slots for discovery */
-
- struct iriap_cb * iriap; /* Used to query remote IAS */
- int errno; /* status of the IAS query */
-
- /* -------------------- Discovery log part -------------------- */
- /* Used by initial discovery on the control channel
- * and by irnet_discover_daddr_and_lsap_sel() */
- struct irda_device_info *discoveries; /* Copy of the discovery log */
- int disco_index; /* Last read in the discovery log */
- int disco_number; /* Size of the discovery log */
-
- struct mutex lock;
-
-} irnet_socket;
-
-/*
- * This is the various event that we will generate on the control channel
- */
-typedef enum irnet_event
-{
- IRNET_DISCOVER, /* New IrNET node discovered */
- IRNET_EXPIRE, /* IrNET node expired */
- IRNET_CONNECT_TO, /* IrNET socket has connected to other node */
- IRNET_CONNECT_FROM, /* Other node has connected to IrNET socket */
- IRNET_REQUEST_FROM, /* Non satisfied connection request */
- IRNET_NOANSWER_FROM, /* Failed connection request */
- IRNET_BLOCKED_LINK, /* Link (IrLAP) is blocked for > 3s */
- IRNET_DISCONNECT_FROM, /* IrNET socket has disconnected */
- IRNET_DISCONNECT_TO /* Closing IrNET socket */
-} irnet_event;
-
-/*
- * This is the storage for an event and its arguments
- */
-typedef struct irnet_log
-{
- irnet_event event;
- int unit;
- __u32 saddr;
- __u32 daddr;
- char name[NICKNAME_MAX_LEN + 1]; /* 21 + 1 */
- __u16_host_order hints; /* Discovery hint bits */
-} irnet_log;
-
-/*
- * This is the storage for all events and related stuff...
- */
-typedef struct irnet_ctrl_channel
-{
- irnet_log log[IRNET_MAX_EVENTS]; /* Event log */
- int index; /* Current index in log */
- spinlock_t spinlock; /* Serialize access to the event log */
- wait_queue_head_t rwait; /* processes blocked on read (or poll) */
-} irnet_ctrl_channel;
-
-/**************************** PROTOTYPES ****************************/
-/*
- * Global functions of the IrNET module
- * Note : we list here also functions called from one file to the other.
- */
-
-/* -------------------------- IRDA PART -------------------------- */
-int irda_irnet_create(irnet_socket *); /* Initialise an IrNET socket */
-int irda_irnet_connect(irnet_socket *); /* Try to connect over IrDA */
-void irda_irnet_destroy(irnet_socket *); /* Teardown an IrNET socket */
-int irda_irnet_init(void); /* Initialise IrDA part of IrNET */
-void irda_irnet_cleanup(void); /* Teardown IrDA part of IrNET */
-
-/**************************** VARIABLES ****************************/
-
-/* Control channel stuff - allocated in irnet_irda.h */
-extern struct irnet_ctrl_channel irnet_events;
-
-#endif /* IRNET_H */
diff --git a/drivers/staging/irda/net/irnet/irnet_irda.c b/drivers/staging/irda/net/irnet/irnet_irda.c
deleted file mode 100644
index e390bceeb2f8..000000000000
--- a/drivers/staging/irda/net/irnet/irnet_irda.c
+++ /dev/null
@@ -1,1885 +0,0 @@
-/*
- * IrNET protocol module : Synchronous PPP over an IrDA socket.
- *
- * Jean II - HPL `00 - <jt@hpl.hp.com>
- *
- * This file implement the IRDA interface of IrNET.
- * Basically, we sit on top of IrTTP. We set up IrTTP, IrIAS properly,
- * and exchange frames with IrTTP.
- */
-
-#include "irnet_irda.h" /* Private header */
-#include <linux/sched.h>
-#include <linux/seq_file.h>
-#include <linux/slab.h>
-#include <asm/unaligned.h>
-
-/*
- * PPP disconnect work: we need to make sure we're in
- * process context when calling ppp_unregister_channel().
- */
-static void irnet_ppp_disconnect(struct work_struct *work)
-{
- irnet_socket * self =
- container_of(work, irnet_socket, disconnect_work);
-
- if (self == NULL)
- return;
- /*
- * If we were connected, cleanup & close the PPP
- * channel, which will kill pppd (hangup) and the rest.
- */
- if (self->ppp_open && !self->ttp_open && !self->ttp_connect) {
- ppp_unregister_channel(&self->chan);
- self->ppp_open = 0;
- }
-}
-
-/************************* CONTROL CHANNEL *************************/
-/*
- * When ppp is not active, /dev/irnet act as a control channel.
- * Writing allow to set up the IrDA destination of the IrNET channel,
- * and any application may be read events happening on IrNET...
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Post an event to the control channel...
- * Put the event in the log, and then wait all process blocked on read
- * so they can read the log...
- */
-static void
-irnet_post_event(irnet_socket * ap,
- irnet_event event,
- __u32 saddr,
- __u32 daddr,
- char * name,
- __u16 hints)
-{
- int index; /* In the log */
-
- DENTER(CTRL_TRACE, "(ap=0x%p, event=%d, daddr=%08x, name=``%s'')\n",
- ap, event, daddr, name);
-
- /* Protect this section via spinlock.
- * Note : as we are the only event producer, we only need to exclude
- * ourself when touching the log, which is nice and easy.
- */
- spin_lock_bh(&irnet_events.spinlock);
-
- /* Copy the event in the log */
- index = irnet_events.index;
- irnet_events.log[index].event = event;
- irnet_events.log[index].daddr = daddr;
- irnet_events.log[index].saddr = saddr;
- /* Try to copy IrDA nickname */
- if(name)
- strcpy(irnet_events.log[index].name, name);
- else
- irnet_events.log[index].name[0] = '\0';
- /* Copy hints */
- irnet_events.log[index].hints.word = hints;
- /* Try to get ppp unit number */
- if((ap != (irnet_socket *) NULL) && (ap->ppp_open))
- irnet_events.log[index].unit = ppp_unit_number(&ap->chan);
- else
- irnet_events.log[index].unit = -1;
-
- /* Increment the index
- * Note that we increment the index only after the event is written,
- * to make sure that the readers don't get garbage... */
- irnet_events.index = (index + 1) % IRNET_MAX_EVENTS;
-
- DEBUG(CTRL_INFO, "New event index is %d\n", irnet_events.index);
-
- /* Spin lock end */
- spin_unlock_bh(&irnet_events.spinlock);
-
- /* Now : wake up everybody waiting for events... */
- wake_up_interruptible_all(&irnet_events.rwait);
-
- DEXIT(CTRL_TRACE, "\n");
-}
-
-/************************* IRDA SUBROUTINES *************************/
-/*
- * These are a bunch of subroutines called from other functions
- * down there, mostly common code or to improve readability...
- *
- * Note : we duplicate quite heavily some routines of af_irda.c,
- * because our input structure (self) is quite different
- * (struct irnet instead of struct irda_sock), which make sharing
- * the same code impossible (at least, without templates).
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Function irda_open_tsap (self)
- *
- * Open local Transport Service Access Point (TSAP)
- *
- * Create a IrTTP instance for us and set all the IrTTP callbacks.
- */
-static inline int
-irnet_open_tsap(irnet_socket * self)
-{
- notify_t notify; /* Callback structure */
-
- DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self);
-
- DABORT(self->tsap != NULL, -EBUSY, IRDA_SR_ERROR, "Already busy !\n");
-
- /* Initialize IrTTP callbacks to be used by the IrDA stack */
- irda_notify_init(&notify);
- notify.connect_confirm = irnet_connect_confirm;
- notify.connect_indication = irnet_connect_indication;
- notify.disconnect_indication = irnet_disconnect_indication;
- notify.data_indication = irnet_data_indication;
- /*notify.udata_indication = NULL;*/
- notify.flow_indication = irnet_flow_indication;
- notify.status_indication = irnet_status_indication;
- notify.instance = self;
- strlcpy(notify.name, IRNET_NOTIFY_NAME, sizeof(notify.name));
-
- /* Open an IrTTP instance */
- self->tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT,
- &notify);
- DABORT(self->tsap == NULL, -ENOMEM,
- IRDA_SR_ERROR, "Unable to allocate TSAP !\n");
-
- /* Remember which TSAP selector we actually got */
- self->stsap_sel = self->tsap->stsap_sel;
-
- DEXIT(IRDA_SR_TRACE, " - tsap=0x%p, sel=0x%X\n",
- self->tsap, self->stsap_sel);
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_ias_to_tsap (self, result, value)
- *
- * Examine an IAS object and extract TSAP
- *
- * We do an IAP query to find the TSAP associated with the IrNET service.
- * When IrIAP pass us the result of the query, this function look at
- * the return values to check for failures and extract the TSAP if
- * possible.
- * Also deallocate value
- * The failure is in self->errno
- * Return TSAP or -1
- */
-static inline __u8
-irnet_ias_to_tsap(irnet_socket * self,
- int result,
- struct ias_value * value)
-{
- __u8 dtsap_sel = 0; /* TSAP we are looking for */
-
- DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self);
-
- /* By default, no error */
- self->errno = 0;
-
- /* Check if request succeeded */
- switch(result)
- {
- /* Standard errors : service not available */
- case IAS_CLASS_UNKNOWN:
- case IAS_ATTRIB_UNKNOWN:
- DEBUG(IRDA_SR_INFO, "IAS object doesn't exist ! (%d)\n", result);
- self->errno = -EADDRNOTAVAIL;
- break;
-
- /* Other errors, most likely IrDA stack failure */
- default :
- DEBUG(IRDA_SR_INFO, "IAS query failed ! (%d)\n", result);
- self->errno = -EHOSTUNREACH;
- break;
-
- /* Success : we got what we wanted */
- case IAS_SUCCESS:
- break;
- }
-
- /* Check what was returned to us */
- if(value != NULL)
- {
- /* What type of argument have we got ? */
- switch(value->type)
- {
- case IAS_INTEGER:
- DEBUG(IRDA_SR_INFO, "result=%d\n", value->t.integer);
- if(value->t.integer != -1)
- /* Get the remote TSAP selector */
- dtsap_sel = value->t.integer;
- else
- self->errno = -EADDRNOTAVAIL;
- break;
- default:
- self->errno = -EADDRNOTAVAIL;
- DERROR(IRDA_SR_ERROR, "bad type ! (0x%X)\n", value->type);
- break;
- }
-
- /* Cleanup */
- irias_delete_value(value);
- }
- else /* value == NULL */
- {
- /* Nothing returned to us - usually result != SUCCESS */
- if(!(self->errno))
- {
- DERROR(IRDA_SR_ERROR,
- "IrDA bug : result == SUCCESS && value == NULL\n");
- self->errno = -EHOSTUNREACH;
- }
- }
- DEXIT(IRDA_SR_TRACE, "\n");
-
- /* Return the TSAP */
- return dtsap_sel;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_find_lsap_sel (self)
- *
- * Try to lookup LSAP selector in remote LM-IAS
- *
- * Basically, we start a IAP query, and then go to sleep. When the query
- * return, irnet_getvalue_confirm will wake us up, and we can examine the
- * result of the query...
- * Note that in some case, the query fail even before we go to sleep,
- * creating some races...
- */
-static inline int
-irnet_find_lsap_sel(irnet_socket * self)
-{
- DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self);
-
- /* This should not happen */
- DABORT(self->iriap, -EBUSY, IRDA_SR_ERROR, "busy with a previous query.\n");
-
- /* Create an IAP instance, will be closed in irnet_getvalue_confirm() */
- self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
- irnet_getvalue_confirm);
-
- /* Treat unexpected signals as disconnect */
- self->errno = -EHOSTUNREACH;
-
- /* Query remote LM-IAS */
- iriap_getvaluebyclass_request(self->iriap, self->rsaddr, self->daddr,
- IRNET_SERVICE_NAME, IRNET_IAS_VALUE);
-
- /* The above request is non-blocking.
- * After a while, IrDA will call us back in irnet_getvalue_confirm()
- * We will then call irnet_ias_to_tsap() and finish the
- * connection procedure */
-
- DEXIT(IRDA_SR_TRACE, "\n");
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_connect_tsap (self)
- *
- * Initialise the TTP socket and initiate TTP connection
- *
- */
-static inline int
-irnet_connect_tsap(irnet_socket * self)
-{
- int err;
-
- DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self);
-
- /* Open a local TSAP (an IrTTP instance) */
- err = irnet_open_tsap(self);
- if(err != 0)
- {
- clear_bit(0, &self->ttp_connect);
- DERROR(IRDA_SR_ERROR, "connect aborted!\n");
- return err;
- }
-
- /* Connect to remote device */
- err = irttp_connect_request(self->tsap, self->dtsap_sel,
- self->rsaddr, self->daddr, NULL,
- self->max_sdu_size_rx, NULL);
- if(err != 0)
- {
- clear_bit(0, &self->ttp_connect);
- DERROR(IRDA_SR_ERROR, "connect aborted!\n");
- return err;
- }
-
- /* The above call is non-blocking.
- * After a while, the IrDA stack will either call us back in
- * irnet_connect_confirm() or irnet_disconnect_indication()
- * See you there ;-) */
-
- DEXIT(IRDA_SR_TRACE, "\n");
- return err;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_discover_next_daddr (self)
- *
- * Query the IrNET TSAP of the next device in the log.
- *
- * Used in the TSAP discovery procedure.
- */
-static inline int
-irnet_discover_next_daddr(irnet_socket * self)
-{
- /* Close the last instance of IrIAP, and open a new one.
- * We can't reuse the IrIAP instance in the IrIAP callback */
- if(self->iriap)
- {
- iriap_close(self->iriap);
- self->iriap = NULL;
- }
- /* Create a new IAP instance */
- self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
- irnet_discovervalue_confirm);
- if(self->iriap == NULL)
- return -ENOMEM;
-
- /* Next discovery - before the call to avoid races */
- self->disco_index++;
-
- /* Check if we have one more address to try */
- if(self->disco_index < self->disco_number)
- {
- /* Query remote LM-IAS */
- iriap_getvaluebyclass_request(self->iriap,
- self->discoveries[self->disco_index].saddr,
- self->discoveries[self->disco_index].daddr,
- IRNET_SERVICE_NAME, IRNET_IAS_VALUE);
- /* The above request is non-blocking.
- * After a while, IrDA will call us back in irnet_discovervalue_confirm()
- * We will then call irnet_ias_to_tsap() and come back here again... */
- return 0;
- }
- else
- return 1;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_discover_daddr_and_lsap_sel (self)
- *
- * This try to find a device with the requested service.
- *
- * Initiate a TSAP discovery procedure.
- * It basically look into the discovery log. For each address in the list,
- * it queries the LM-IAS of the device to find if this device offer
- * the requested service.
- * If there is more than one node supporting the service, we complain
- * to the user (it should move devices around).
- * If we find one node which have the requested TSAP, we connect to it.
- *
- * This function just start the whole procedure. It request the discovery
- * log and submit the first IAS query.
- * The bulk of the job is handled in irnet_discovervalue_confirm()
- *
- * Note : this procedure fails if there is more than one device in range
- * on the same dongle, because IrLMP doesn't disconnect the LAP when the
- * last LSAP is closed. Moreover, we would need to wait the LAP
- * disconnection...
- */
-static inline int
-irnet_discover_daddr_and_lsap_sel(irnet_socket * self)
-{
- int ret;
-
- DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self);
-
- /* Ask lmp for the current discovery log */
- self->discoveries = irlmp_get_discoveries(&self->disco_number, self->mask,
- DISCOVERY_DEFAULT_SLOTS);
-
- /* Check if the we got some results */
- if(self->discoveries == NULL)
- {
- self->disco_number = -1;
- clear_bit(0, &self->ttp_connect);
- DRETURN(-ENETUNREACH, IRDA_SR_INFO, "No Cachelog...\n");
- }
- DEBUG(IRDA_SR_INFO, "Got the log (0x%p), size is %d\n",
- self->discoveries, self->disco_number);
-
- /* Start with the first discovery */
- self->disco_index = -1;
- self->daddr = DEV_ADDR_ANY;
-
- /* This will fail if the log is empty - this is non-blocking */
- ret = irnet_discover_next_daddr(self);
- if(ret)
- {
- /* Close IAP */
- if(self->iriap)
- iriap_close(self->iriap);
- self->iriap = NULL;
-
- /* Cleanup our copy of the discovery log */
- kfree(self->discoveries);
- self->discoveries = NULL;
-
- clear_bit(0, &self->ttp_connect);
- DRETURN(-ENETUNREACH, IRDA_SR_INFO, "Cachelog empty...\n");
- }
-
- /* Follow me in irnet_discovervalue_confirm() */
-
- DEXIT(IRDA_SR_TRACE, "\n");
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_dname_to_daddr (self)
- *
- * Convert an IrDA nickname to a valid IrDA address
- *
- * It basically look into the discovery log until there is a match.
- */
-static inline int
-irnet_dname_to_daddr(irnet_socket * self)
-{
- struct irda_device_info *discoveries; /* Copy of the discovery log */
- int number; /* Number of nodes in the log */
- int i;
-
- DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self);
-
- /* Ask lmp for the current discovery log */
- discoveries = irlmp_get_discoveries(&number, 0xffff,
- DISCOVERY_DEFAULT_SLOTS);
- /* Check if the we got some results */
- if(discoveries == NULL)
- DRETURN(-ENETUNREACH, IRDA_SR_INFO, "Cachelog empty...\n");
-
- /*
- * Now, check all discovered devices (if any), and connect
- * client only about the services that the client is
- * interested in...
- */
- for(i = 0; i < number; i++)
- {
- /* Does the name match ? */
- if(!strncmp(discoveries[i].info, self->rname, NICKNAME_MAX_LEN))
- {
- /* Yes !!! Get it.. */
- self->daddr = discoveries[i].daddr;
- DEBUG(IRDA_SR_INFO, "discovered device ``%s'' at address 0x%08x.\n",
- self->rname, self->daddr);
- kfree(discoveries);
- DEXIT(IRDA_SR_TRACE, "\n");
- return 0;
- }
- }
- /* No luck ! */
- DEBUG(IRDA_SR_INFO, "cannot discover device ``%s'' !!!\n", self->rname);
- kfree(discoveries);
- return -EADDRNOTAVAIL;
-}
-
-
-/************************* SOCKET ROUTINES *************************/
-/*
- * This are the main operations on IrNET sockets, basically to create
- * and destroy IrNET sockets. These are called from the PPP part...
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Create a IrNET instance : just initialise some parameters...
- */
-int
-irda_irnet_create(irnet_socket * self)
-{
- DENTER(IRDA_SOCK_TRACE, "(self=0x%p)\n", self);
-
- self->magic = IRNET_MAGIC; /* Paranoia */
-
- self->ttp_open = 0; /* Prevent higher layer from accessing IrTTP */
- self->ttp_connect = 0; /* Not connecting yet */
- self->rname[0] = '\0'; /* May be set via control channel */
- self->rdaddr = DEV_ADDR_ANY; /* May be set via control channel */
- self->rsaddr = DEV_ADDR_ANY; /* May be set via control channel */
- self->daddr = DEV_ADDR_ANY; /* Until we get connected */
- self->saddr = DEV_ADDR_ANY; /* Until we get connected */
- self->max_sdu_size_rx = TTP_SAR_UNBOUND;
-
- /* Register as a client with IrLMP */
- self->ckey = irlmp_register_client(0, NULL, NULL, NULL);
-#ifdef DISCOVERY_NOMASK
- self->mask = 0xffff; /* For W2k compatibility */
-#else /* DISCOVERY_NOMASK */
- self->mask = irlmp_service_to_hint(S_LAN);
-#endif /* DISCOVERY_NOMASK */
- self->tx_flow = FLOW_START; /* Flow control from IrTTP */
-
- INIT_WORK(&self->disconnect_work, irnet_ppp_disconnect);
-
- DEXIT(IRDA_SOCK_TRACE, "\n");
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Connect to the other side :
- * o convert device name to an address
- * o find the socket number (dlsap)
- * o Establish the connection
- *
- * Note : We no longer mimic af_irda. The IAS query for finding the TSAP
- * is done asynchronously, like the TTP connection. This allow us to
- * call this function from any context (not only process).
- * The downside is that following what's happening in there is tricky
- * because it involve various functions all over the place...
- */
-int
-irda_irnet_connect(irnet_socket * self)
-{
- int err;
-
- DENTER(IRDA_SOCK_TRACE, "(self=0x%p)\n", self);
-
- /* Check if we are already trying to connect.
- * Because irda_irnet_connect() can be called directly by pppd plus
- * packet retries in ppp_generic and connect may take time, plus we may
- * race with irnet_connect_indication(), we need to be careful there... */
- if(test_and_set_bit(0, &self->ttp_connect))
- DRETURN(-EBUSY, IRDA_SOCK_INFO, "Already connecting...\n");
- if((self->iriap != NULL) || (self->tsap != NULL))
- DERROR(IRDA_SOCK_ERROR, "Socket not cleaned up...\n");
-
- /* Insert ourselves in the hashbin so that the IrNET server can find us.
- * Notes : 4th arg is string of 32 char max and must be null terminated
- * When 4th arg is used (string), 3rd arg isn't (int)
- * Can't re-insert (MUST remove first) so check for that... */
- if((irnet_server.running) && (self->q.q_next == NULL))
- {
- spin_lock_bh(&irnet_server.spinlock);
- hashbin_insert(irnet_server.list, (irda_queue_t *) self, 0, self->rname);
- spin_unlock_bh(&irnet_server.spinlock);
- DEBUG(IRDA_SOCK_INFO, "Inserted ``%s'' in hashbin...\n", self->rname);
- }
-
- /* If we don't have anything (no address, no name) */
- if((self->rdaddr == DEV_ADDR_ANY) && (self->rname[0] == '\0'))
- {
- /* Try to find a suitable address */
- if((err = irnet_discover_daddr_and_lsap_sel(self)) != 0)
- DRETURN(err, IRDA_SOCK_INFO, "auto-connect failed!\n");
- /* In most cases, the call above is non-blocking */
- }
- else
- {
- /* If we have only the name (no address), try to get an address */
- if(self->rdaddr == DEV_ADDR_ANY)
- {
- if((err = irnet_dname_to_daddr(self)) != 0)
- DRETURN(err, IRDA_SOCK_INFO, "name connect failed!\n");
- }
- else
- /* Use the requested destination address */
- self->daddr = self->rdaddr;
-
- /* Query remote LM-IAS to find LSAP selector */
- irnet_find_lsap_sel(self);
- /* The above call is non blocking */
- }
-
- /* At this point, we are waiting for the IrDA stack to call us back,
- * or we have already failed.
- * We will finish the connection procedure in irnet_connect_tsap().
- */
- DEXIT(IRDA_SOCK_TRACE, "\n");
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irda_irnet_destroy(self)
- *
- * Destroy irnet instance
- *
- * Note : this need to be called from a process context.
- */
-void
-irda_irnet_destroy(irnet_socket * self)
-{
- DENTER(IRDA_SOCK_TRACE, "(self=0x%p)\n", self);
- if(self == NULL)
- return;
-
- /* Remove ourselves from hashbin (if we are queued in hashbin)
- * Note : `irnet_server.running' protect us from calls in hashbin_delete() */
- if((irnet_server.running) && (self->q.q_next != NULL))
- {
- struct irnet_socket * entry;
- DEBUG(IRDA_SOCK_INFO, "Removing from hash..\n");
- spin_lock_bh(&irnet_server.spinlock);
- entry = hashbin_remove_this(irnet_server.list, (irda_queue_t *) self);
- self->q.q_next = NULL;
- spin_unlock_bh(&irnet_server.spinlock);
- DASSERT(entry == self, , IRDA_SOCK_ERROR, "Can't remove from hash.\n");
- }
-
- /* If we were connected, post a message */
- if(test_bit(0, &self->ttp_open))
- {
- /* Note : as the disconnect comes from ppp_generic, the unit number
- * doesn't exist anymore when we post the event, so we need to pass
- * NULL as the first arg... */
- irnet_post_event(NULL, IRNET_DISCONNECT_TO,
- self->saddr, self->daddr, self->rname, 0);
- }
-
- /* Prevent various IrDA callbacks from messing up things
- * Need to be first */
- clear_bit(0, &self->ttp_connect);
-
- /* Prevent higher layer from accessing IrTTP */
- clear_bit(0, &self->ttp_open);
-
- /* Unregister with IrLMP */
- irlmp_unregister_client(self->ckey);
-
- /* Unregister with LM-IAS */
- if(self->iriap)
- {
- iriap_close(self->iriap);
- self->iriap = NULL;
- }
-
- /* Cleanup eventual discoveries from connection attempt or control channel */
- if(self->discoveries != NULL)
- {
- /* Cleanup our copy of the discovery log */
- kfree(self->discoveries);
- self->discoveries = NULL;
- }
-
- /* Close our IrTTP connection */
- if(self->tsap)
- {
- DEBUG(IRDA_SOCK_INFO, "Closing our TTP connection.\n");
- irttp_disconnect_request(self->tsap, NULL, P_NORMAL);
- irttp_close_tsap(self->tsap);
- self->tsap = NULL;
- }
- self->stsap_sel = 0;
-
- DEXIT(IRDA_SOCK_TRACE, "\n");
-}
-
-
-/************************** SERVER SOCKET **************************/
-/*
- * The IrNET service is composed of one server socket and a variable
- * number of regular IrNET sockets. The server socket is supposed to
- * handle incoming connections and redirect them to one IrNET sockets.
- * It's a superset of the regular IrNET socket, but has a very distinct
- * behaviour...
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_daddr_to_dname (self)
- *
- * Convert an IrDA address to a IrDA nickname
- *
- * It basically look into the discovery log until there is a match.
- */
-static inline int
-irnet_daddr_to_dname(irnet_socket * self)
-{
- struct irda_device_info *discoveries; /* Copy of the discovery log */
- int number; /* Number of nodes in the log */
- int i;
-
- DENTER(IRDA_SERV_TRACE, "(self=0x%p)\n", self);
-
- /* Ask lmp for the current discovery log */
- discoveries = irlmp_get_discoveries(&number, 0xffff,
- DISCOVERY_DEFAULT_SLOTS);
- /* Check if the we got some results */
- if (discoveries == NULL)
- DRETURN(-ENETUNREACH, IRDA_SERV_INFO, "Cachelog empty...\n");
-
- /* Now, check all discovered devices (if any) */
- for(i = 0; i < number; i++)
- {
- /* Does the name match ? */
- if(discoveries[i].daddr == self->daddr)
- {
- /* Yes !!! Get it.. */
- strlcpy(self->rname, discoveries[i].info, sizeof(self->rname));
- self->rname[sizeof(self->rname) - 1] = '\0';
- DEBUG(IRDA_SERV_INFO, "Device 0x%08x is in fact ``%s''.\n",
- self->daddr, self->rname);
- kfree(discoveries);
- DEXIT(IRDA_SERV_TRACE, "\n");
- return 0;
- }
- }
- /* No luck ! */
- DEXIT(IRDA_SERV_INFO, ": cannot discover device 0x%08x !!!\n", self->daddr);
- kfree(discoveries);
- return -EADDRNOTAVAIL;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irda_find_socket (self)
- *
- * Find the correct IrNET socket
- *
- * Look into the list of IrNET sockets and finds one with the right
- * properties...
- */
-static inline irnet_socket *
-irnet_find_socket(irnet_socket * self)
-{
- irnet_socket * new = (irnet_socket *) NULL;
- int err;
-
- DENTER(IRDA_SERV_TRACE, "(self=0x%p)\n", self);
-
- /* Get the addresses of the requester */
- self->daddr = irttp_get_daddr(self->tsap);
- self->saddr = irttp_get_saddr(self->tsap);
-
- /* Try to get the IrDA nickname of the requester */
- err = irnet_daddr_to_dname(self);
-
- /* Protect access to the instance list */
- spin_lock_bh(&irnet_server.spinlock);
-
- /* So now, try to get an socket having specifically
- * requested that nickname */
- if(err == 0)
- {
- new = (irnet_socket *) hashbin_find(irnet_server.list,
- 0, self->rname);
- if(new)
- DEBUG(IRDA_SERV_INFO, "Socket 0x%p matches rname ``%s''.\n",
- new, new->rname);
- }
-
- /* If no name matches, try to find an socket by the destination address */
- /* It can be either the requested destination address (set via the
- * control channel), or the current destination address if the
- * socket is in the middle of a connection request */
- if(new == (irnet_socket *) NULL)
- {
- new = (irnet_socket *) hashbin_get_first(irnet_server.list);
- while(new !=(irnet_socket *) NULL)
- {
- /* Does it have the same address ? */
- if((new->rdaddr == self->daddr) || (new->daddr == self->daddr))
- {
- /* Yes !!! Get it.. */
- DEBUG(IRDA_SERV_INFO, "Socket 0x%p matches daddr %#08x.\n",
- new, self->daddr);
- break;
- }
- new = (irnet_socket *) hashbin_get_next(irnet_server.list);
- }
- }
-
- /* If we don't have any socket, get the first unconnected socket */
- if(new == (irnet_socket *) NULL)
- {
- new = (irnet_socket *) hashbin_get_first(irnet_server.list);
- while(new !=(irnet_socket *) NULL)
- {
- /* Is it available ? */
- if(!(test_bit(0, &new->ttp_open)) && (new->rdaddr == DEV_ADDR_ANY) &&
- (new->rname[0] == '\0') && (new->ppp_open))
- {
- /* Yes !!! Get it.. */
- DEBUG(IRDA_SERV_INFO, "Socket 0x%p is free.\n",
- new);
- break;
- }
- new = (irnet_socket *) hashbin_get_next(irnet_server.list);
- }
- }
-
- /* Spin lock end */
- spin_unlock_bh(&irnet_server.spinlock);
-
- DEXIT(IRDA_SERV_TRACE, " - new = 0x%p\n", new);
- return new;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irda_connect_socket (self)
- *
- * Connect an incoming connection to the socket
- *
- */
-static inline int
-irnet_connect_socket(irnet_socket * server,
- irnet_socket * new,
- struct qos_info * qos,
- __u32 max_sdu_size,
- __u8 max_header_size)
-{
- DENTER(IRDA_SERV_TRACE, "(server=0x%p, new=0x%p)\n",
- server, new);
-
- /* Now attach up the new socket */
- new->tsap = irttp_dup(server->tsap, new);
- DABORT(new->tsap == NULL, -1, IRDA_SERV_ERROR, "dup failed!\n");
-
- /* Set up all the relevant parameters on the new socket */
- new->stsap_sel = new->tsap->stsap_sel;
- new->dtsap_sel = new->tsap->dtsap_sel;
- new->saddr = irttp_get_saddr(new->tsap);
- new->daddr = irttp_get_daddr(new->tsap);
-
- new->max_header_size = max_header_size;
- new->max_sdu_size_tx = max_sdu_size;
- new->max_data_size = max_sdu_size;
-#ifdef STREAM_COMPAT
- /* If we want to receive "stream sockets" */
- if(max_sdu_size == 0)
- new->max_data_size = irttp_get_max_seg_size(new->tsap);
-#endif /* STREAM_COMPAT */
-
- /* Clean up the original one to keep it in listen state */
- irttp_listen(server->tsap);
-
- /* Send a connection response on the new socket */
- irttp_connect_response(new->tsap, new->max_sdu_size_rx, NULL);
-
- /* Allow PPP to send its junk over the new socket... */
- set_bit(0, &new->ttp_open);
-
- /* Not connecting anymore, and clean up last possible remains
- * of connection attempts on the socket */
- clear_bit(0, &new->ttp_connect);
- if(new->iriap)
- {
- iriap_close(new->iriap);
- new->iriap = NULL;
- }
- if(new->discoveries != NULL)
- {
- kfree(new->discoveries);
- new->discoveries = NULL;
- }
-
-#ifdef CONNECT_INDIC_KICK
- /* As currently we don't block packets in ppp_irnet_send() while passive,
- * this is not really needed...
- * Also, not doing it give IrDA a chance to finish the setup properly
- * before being swamped with packets... */
- ppp_output_wakeup(&new->chan);
-#endif /* CONNECT_INDIC_KICK */
-
- /* Notify the control channel */
- irnet_post_event(new, IRNET_CONNECT_FROM,
- new->saddr, new->daddr, server->rname, 0);
-
- DEXIT(IRDA_SERV_TRACE, "\n");
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irda_disconnect_server (self)
- *
- * Cleanup the server socket when the incoming connection abort
- *
- */
-static inline void
-irnet_disconnect_server(irnet_socket * self,
- struct sk_buff *skb)
-{
- DENTER(IRDA_SERV_TRACE, "(self=0x%p)\n", self);
-
- /* Put the received packet in the black hole */
- kfree_skb(skb);
-
-#ifdef FAIL_SEND_DISCONNECT
- /* Tell the other party we don't want to be connected */
- /* Hum... Is it the right thing to do ? And do we need to send
- * a connect response before ? It looks ok without this... */
- irttp_disconnect_request(self->tsap, NULL, P_NORMAL);
-#endif /* FAIL_SEND_DISCONNECT */
-
- /* Notify the control channel (see irnet_find_socket()) */
- irnet_post_event(NULL, IRNET_REQUEST_FROM,
- self->saddr, self->daddr, self->rname, 0);
-
- /* Clean up the server to keep it in listen state */
- irttp_listen(self->tsap);
-
- DEXIT(IRDA_SERV_TRACE, "\n");
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irda_setup_server (self)
- *
- * Create a IrTTP server and set it up...
- *
- * Register the IrLAN hint bit, create a IrTTP instance for us,
- * set all the IrTTP callbacks and create an IrIAS entry...
- */
-static inline int
-irnet_setup_server(void)
-{
- __u16 hints;
-
- DENTER(IRDA_SERV_TRACE, "()\n");
-
- /* Initialise the regular socket part of the server */
- irda_irnet_create(&irnet_server.s);
-
- /* Open a local TSAP (an IrTTP instance) for the server */
- irnet_open_tsap(&irnet_server.s);
-
- /* PPP part setup */
- irnet_server.s.ppp_open = 0;
- irnet_server.s.chan.private = NULL;
- irnet_server.s.file = NULL;
-
- /* Get the hint bit corresponding to IrLAN */
- /* Note : we overload the IrLAN hint bit. As it is only a "hint", and as
- * we provide roughly the same functionality as IrLAN, this is ok.
- * In fact, the situation is similar as JetSend overloading the Obex hint
- */
- hints = irlmp_service_to_hint(S_LAN);
-
-#ifdef ADVERTISE_HINT
- /* Register with IrLMP as a service (advertise our hint bit) */
- irnet_server.skey = irlmp_register_service(hints);
-#endif /* ADVERTISE_HINT */
-
- /* Register with LM-IAS (so that people can connect to us) */
- irnet_server.ias_obj = irias_new_object(IRNET_SERVICE_NAME, jiffies);
- irias_add_integer_attrib(irnet_server.ias_obj, IRNET_IAS_VALUE,
- irnet_server.s.stsap_sel, IAS_KERNEL_ATTR);
- irias_insert_object(irnet_server.ias_obj);
-
-#ifdef DISCOVERY_EVENTS
- /* Tell IrLMP we want to be notified of newly discovered nodes */
- irlmp_update_client(irnet_server.s.ckey, hints,
- irnet_discovery_indication, irnet_expiry_indication,
- (void *) &irnet_server.s);
-#endif
-
- DEXIT(IRDA_SERV_TRACE, " - self=0x%p\n", &irnet_server.s);
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irda_destroy_server (self)
- *
- * Destroy the IrTTP server...
- *
- * Reverse of the previous function...
- */
-static inline void
-irnet_destroy_server(void)
-{
- DENTER(IRDA_SERV_TRACE, "()\n");
-
-#ifdef ADVERTISE_HINT
- /* Unregister with IrLMP */
- irlmp_unregister_service(irnet_server.skey);
-#endif /* ADVERTISE_HINT */
-
- /* Unregister with LM-IAS */
- if(irnet_server.ias_obj)
- irias_delete_object(irnet_server.ias_obj);
-
- /* Cleanup the socket part */
- irda_irnet_destroy(&irnet_server.s);
-
- DEXIT(IRDA_SERV_TRACE, "\n");
-}
-
-
-/************************ IRDA-TTP CALLBACKS ************************/
-/*
- * When we create a IrTTP instance, we pass to it a set of callbacks
- * that IrTTP will call in case of various events.
- * We take care of those events here.
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_data_indication (instance, sap, skb)
- *
- * Received some data from TinyTP. Just queue it on the receive queue
- *
- */
-static int
-irnet_data_indication(void * instance,
- void * sap,
- struct sk_buff *skb)
-{
- irnet_socket * ap = (irnet_socket *) instance;
- unsigned char * p;
- int code = 0;
-
- DENTER(IRDA_TCB_TRACE, "(self/ap=0x%p, skb=0x%p)\n",
- ap, skb);
- DASSERT(skb != NULL, 0, IRDA_CB_ERROR, "skb is NULL !!!\n");
-
- /* Check is ppp is ready to receive our packet */
- if(!ap->ppp_open)
- {
- DERROR(IRDA_CB_ERROR, "PPP not ready, dropping packet...\n");
- /* When we return error, TTP will need to requeue the skb and
- * will stop the sender. IrTTP will stall until we send it a
- * flow control request... */
- return -ENOMEM;
- }
-
- /* strip address/control field if present */
- p = skb->data;
- if((p[0] == PPP_ALLSTATIONS) && (p[1] == PPP_UI))
- {
- /* chop off address/control */
- if(skb->len < 3)
- goto err_exit;
- p = skb_pull(skb, 2);
- }
-
- /* decompress protocol field if compressed */
- if(p[0] & 1)
- {
- /* protocol is compressed */
- *(u8 *)skb_push(skb, 1) = 0;
- }
- else
- if(skb->len < 2)
- goto err_exit;
-
- /* pass to generic ppp layer */
- /* Note : how do I know if ppp can accept or not the packet ? This is
- * essential if I want to manage flow control smoothly... */
- ppp_input(&ap->chan, skb);
-
- DEXIT(IRDA_TCB_TRACE, "\n");
- return 0;
-
- err_exit:
- DERROR(IRDA_CB_ERROR, "Packet too small, dropping...\n");
- kfree_skb(skb);
- ppp_input_error(&ap->chan, code);
- return 0; /* Don't return an error code, only for flow control... */
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_disconnect_indication (instance, sap, reason, skb)
- *
- * Connection has been closed. Chech reason to find out why
- *
- * Note : there are many cases where we come here :
- * o attempted to connect, timeout
- * o connected, link is broken, LAP has timeout
- * o connected, other side close the link
- * o connection request on the server not handled
- */
-static void
-irnet_disconnect_indication(void * instance,
- void * sap,
- LM_REASON reason,
- struct sk_buff *skb)
-{
- irnet_socket * self = (irnet_socket *) instance;
- int test_open;
- int test_connect;
-
- DENTER(IRDA_TCB_TRACE, "(self=0x%p)\n", self);
- DASSERT(self != NULL, , IRDA_CB_ERROR, "Self is NULL !!!\n");
-
- /* Don't care about it, but let's not leak it */
- if(skb)
- dev_kfree_skb(skb);
-
- /* Prevent higher layer from accessing IrTTP */
- test_open = test_and_clear_bit(0, &self->ttp_open);
- /* Not connecting anymore...
- * (note : TSAP is open, so IAP callbacks are no longer pending...) */
- test_connect = test_and_clear_bit(0, &self->ttp_connect);
-
- /* If both self->ttp_open and self->ttp_connect are NULL, it mean that we
- * have a race condition with irda_irnet_destroy() or
- * irnet_connect_indication(), so don't mess up tsap...
- */
- if(!(test_open || test_connect))
- {
- DERROR(IRDA_CB_ERROR, "Race condition detected...\n");
- return;
- }
-
- /* If we were active, notify the control channel */
- if(test_open)
- irnet_post_event(self, IRNET_DISCONNECT_FROM,
- self->saddr, self->daddr, self->rname, 0);
- else
- /* If we were trying to connect, notify the control channel */
- if((self->tsap) && (self != &irnet_server.s))
- irnet_post_event(self, IRNET_NOANSWER_FROM,
- self->saddr, self->daddr, self->rname, 0);
-
- /* Close our IrTTP connection, cleanup tsap */
- if((self->tsap) && (self != &irnet_server.s))
- {
- DEBUG(IRDA_CB_INFO, "Closing our TTP connection.\n");
- irttp_close_tsap(self->tsap);
- self->tsap = NULL;
- }
- /* Cleanup the socket in case we want to reconnect in ppp_output_wakeup() */
- self->stsap_sel = 0;
- self->daddr = DEV_ADDR_ANY;
- self->tx_flow = FLOW_START;
-
- /* Deal with the ppp instance if it's still alive */
- if(self->ppp_open)
- {
- if(test_open)
- {
- /* ppp_unregister_channel() wants a user context. */
- schedule_work(&self->disconnect_work);
- }
- else
- {
- /* If we were trying to connect, flush (drain) ppp_generic
- * Tx queue (most often we have blocked it), which will
- * trigger an other attempt to connect. If we are passive,
- * this will empty the Tx queue after last try. */
- ppp_output_wakeup(&self->chan);
- }
- }
-
- DEXIT(IRDA_TCB_TRACE, "\n");
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_connect_confirm (instance, sap, qos, max_sdu_size, skb)
- *
- * Connections has been confirmed by the remote device
- *
- */
-static void
-irnet_connect_confirm(void * instance,
- void * sap,
- struct qos_info *qos,
- __u32 max_sdu_size,
- __u8 max_header_size,
- struct sk_buff *skb)
-{
- irnet_socket * self = (irnet_socket *) instance;
-
- DENTER(IRDA_TCB_TRACE, "(self=0x%p)\n", self);
-
- /* Check if socket is closing down (via irda_irnet_destroy()) */
- if(! test_bit(0, &self->ttp_connect))
- {
- DERROR(IRDA_CB_ERROR, "Socket no longer connecting. Ouch !\n");
- return;
- }
-
- /* How much header space do we need to reserve */
- self->max_header_size = max_header_size;
-
- /* IrTTP max SDU size in transmit direction */
- self->max_sdu_size_tx = max_sdu_size;
- self->max_data_size = max_sdu_size;
-#ifdef STREAM_COMPAT
- if(max_sdu_size == 0)
- self->max_data_size = irttp_get_max_seg_size(self->tsap);
-#endif /* STREAM_COMPAT */
-
- /* At this point, IrLMP has assigned our source address */
- self->saddr = irttp_get_saddr(self->tsap);
-
- /* Allow higher layer to access IrTTP */
- set_bit(0, &self->ttp_open);
- clear_bit(0, &self->ttp_connect); /* Not racy, IrDA traffic is serial */
- /* Give a kick in the ass of ppp_generic so that he sends us some data */
- ppp_output_wakeup(&self->chan);
-
- /* Check size of received packet */
- if(skb->len > 0)
- {
-#ifdef PASS_CONNECT_PACKETS
- DEBUG(IRDA_CB_INFO, "Passing connect packet to PPP.\n");
- /* Try to pass it to PPP */
- irnet_data_indication(instance, sap, skb);
-#else /* PASS_CONNECT_PACKETS */
- DERROR(IRDA_CB_ERROR, "Dropping non empty packet.\n");
- kfree_skb(skb); /* Note : will be optimised with other kfree... */
-#endif /* PASS_CONNECT_PACKETS */
- }
- else
- kfree_skb(skb);
-
- /* Notify the control channel */
- irnet_post_event(self, IRNET_CONNECT_TO,
- self->saddr, self->daddr, self->rname, 0);
-
- DEXIT(IRDA_TCB_TRACE, "\n");
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_flow_indication (instance, sap, flow)
- *
- * Used by TinyTP to tell us if it can accept more data or not
- *
- */
-static void
-irnet_flow_indication(void * instance,
- void * sap,
- LOCAL_FLOW flow)
-{
- irnet_socket * self = (irnet_socket *) instance;
- LOCAL_FLOW oldflow = self->tx_flow;
-
- DENTER(IRDA_TCB_TRACE, "(self=0x%p, flow=%d)\n", self, flow);
-
- /* Update our state */
- self->tx_flow = flow;
-
- /* Check what IrTTP want us to do... */
- switch(flow)
- {
- case FLOW_START:
- DEBUG(IRDA_CB_INFO, "IrTTP wants us to start again\n");
- /* Check if we really need to wake up PPP */
- if(oldflow == FLOW_STOP)
- ppp_output_wakeup(&self->chan);
- else
- DEBUG(IRDA_CB_INFO, "But we were already transmitting !!!\n");
- break;
- case FLOW_STOP:
- DEBUG(IRDA_CB_INFO, "IrTTP wants us to slow down\n");
- break;
- default:
- DEBUG(IRDA_CB_INFO, "Unknown flow command!\n");
- break;
- }
-
- DEXIT(IRDA_TCB_TRACE, "\n");
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_status_indication (instance, sap, reason, skb)
- *
- * Link (IrLAP) status report.
- *
- */
-static void
-irnet_status_indication(void * instance,
- LINK_STATUS link,
- LOCK_STATUS lock)
-{
- irnet_socket * self = (irnet_socket *) instance;
-
- DENTER(IRDA_TCB_TRACE, "(self=0x%p)\n", self);
- DASSERT(self != NULL, , IRDA_CB_ERROR, "Self is NULL !!!\n");
-
- /* We can only get this event if we are connected */
- switch(link)
- {
- case STATUS_NO_ACTIVITY:
- irnet_post_event(self, IRNET_BLOCKED_LINK,
- self->saddr, self->daddr, self->rname, 0);
- break;
- default:
- DEBUG(IRDA_CB_INFO, "Unknown status...\n");
- }
-
- DEXIT(IRDA_TCB_TRACE, "\n");
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_connect_indication(instance, sap, qos, max_sdu_size, userdata)
- *
- * Incoming connection
- *
- * In theory, this function is called only on the server socket.
- * Some other node is attempting to connect to the IrNET service, and has
- * sent a connection request on our server socket.
- * We just redirect the connection to the relevant IrNET socket.
- *
- * Note : we also make sure that between 2 irnet nodes, there can
- * exist only one irnet connection.
- */
-static void
-irnet_connect_indication(void * instance,
- void * sap,
- struct qos_info *qos,
- __u32 max_sdu_size,
- __u8 max_header_size,
- struct sk_buff *skb)
-{
- irnet_socket * server = &irnet_server.s;
- irnet_socket * new = (irnet_socket *) NULL;
-
- DENTER(IRDA_TCB_TRACE, "(server=0x%p)\n", server);
- DASSERT(instance == &irnet_server, , IRDA_CB_ERROR,
- "Invalid instance (0x%p) !!!\n", instance);
- DASSERT(sap == irnet_server.s.tsap, , IRDA_CB_ERROR, "Invalid sap !!!\n");
-
- /* Try to find the most appropriate IrNET socket */
- new = irnet_find_socket(server);
-
- /* After all this hard work, do we have an socket ? */
- if(new == (irnet_socket *) NULL)
- {
- DEXIT(IRDA_CB_INFO, ": No socket waiting for this connection.\n");
- irnet_disconnect_server(server, skb);
- return;
- }
-
- /* Is the socket already busy ? */
- if(test_bit(0, &new->ttp_open))
- {
- DEXIT(IRDA_CB_INFO, ": Socket already connected.\n");
- irnet_disconnect_server(server, skb);
- return;
- }
-
- /* The following code is a bit tricky, so need comments ;-)
- */
- /* If ttp_connect is set, the socket is trying to connect to the other
- * end and may have sent a IrTTP connection request and is waiting for
- * a connection response (that may never come).
- * Now, the pain is that the socket may have opened a tsap and is
- * waiting on it, while the other end is trying to connect to it on
- * another tsap.
- * Because IrNET can be peer to peer, we need to workaround this.
- * Furthermore, the way the irnetd script is implemented, the
- * target will create a second IrNET connection back to the
- * originator and expect the originator to bind this new connection
- * to the original PPPD instance.
- * And of course, if we don't use irnetd, we can have a race when
- * both side try to connect simultaneously, which could leave both
- * connections half closed (yuck).
- * Conclusions :
- * 1) The "originator" must accept the new connection and get rid
- * of the old one so that irnetd works
- * 2) One side must deny the new connection to avoid races,
- * but both side must agree on which side it is...
- * Most often, the originator is primary at the LAP layer.
- * Jean II
- */
- /* Now, let's look at the way I wrote the test...
- * We need to clear up the ttp_connect flag atomically to prevent
- * irnet_disconnect_indication() to mess up the tsap we are going to close.
- * We want to clear the ttp_connect flag only if we close the tsap,
- * otherwise we will never close it, so we need to check for primary
- * *before* doing the test on the flag.
- * And of course, ALLOW_SIMULT_CONNECT can disable this entirely...
- * Jean II
- */
-
- /* Socket already connecting ? On primary ? */
- if(0
-#ifdef ALLOW_SIMULT_CONNECT
- || ((irttp_is_primary(server->tsap) == 1) && /* primary */
- (test_and_clear_bit(0, &new->ttp_connect)))
-#endif /* ALLOW_SIMULT_CONNECT */
- )
- {
- DERROR(IRDA_CB_ERROR, "Socket already connecting, but going to reuse it !\n");
-
- /* Cleanup the old TSAP if necessary - IrIAP will be cleaned up later */
- if(new->tsap != NULL)
- {
- /* Close the old connection the new socket was attempting,
- * so that we can hook it up to the new connection.
- * It's now safe to do it... */
- irttp_close_tsap(new->tsap);
- new->tsap = NULL;
- }
- }
- else
- {
- /* Three options :
- * 1) socket was not connecting or connected : ttp_connect should be 0.
- * 2) we don't want to connect the socket because we are secondary or
- * ALLOW_SIMULT_CONNECT is undefined. ttp_connect should be 1.
- * 3) we are half way in irnet_disconnect_indication(), and it's a
- * nice race condition... Fortunately, we can detect that by checking
- * if tsap is still alive. On the other hand, we can't be in
- * irda_irnet_destroy() otherwise we would not have found this
- * socket in the hashbin.
- * Jean II */
- if((test_bit(0, &new->ttp_connect)) || (new->tsap != NULL))
- {
- /* Don't mess this socket, somebody else in in charge... */
- DERROR(IRDA_CB_ERROR, "Race condition detected, socket in use, abort connect...\n");
- irnet_disconnect_server(server, skb);
- return;
- }
- }
-
- /* So : at this point, we have a socket, and it is idle. Good ! */
- irnet_connect_socket(server, new, qos, max_sdu_size, max_header_size);
-
- /* Check size of received packet */
- if(skb->len > 0)
- {
-#ifdef PASS_CONNECT_PACKETS
- DEBUG(IRDA_CB_INFO, "Passing connect packet to PPP.\n");
- /* Try to pass it to PPP */
- irnet_data_indication(new, new->tsap, skb);
-#else /* PASS_CONNECT_PACKETS */
- DERROR(IRDA_CB_ERROR, "Dropping non empty packet.\n");
- kfree_skb(skb); /* Note : will be optimised with other kfree... */
-#endif /* PASS_CONNECT_PACKETS */
- }
- else
- kfree_skb(skb);
-
- DEXIT(IRDA_TCB_TRACE, "\n");
-}
-
-
-/********************** IRDA-IAS/LMP CALLBACKS **********************/
-/*
- * These are the callbacks called by other layers of the IrDA stack,
- * mainly LMP for discovery and IAS for name queries.
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_getvalue_confirm (result, obj_id, value, priv)
- *
- * Got answer from remote LM-IAS, just connect
- *
- * This is the reply to a IAS query we were doing to find the TSAP of
- * the device we want to connect to.
- * If we have found a valid TSAP, just initiate the TTP connection
- * on this TSAP.
- */
-static void
-irnet_getvalue_confirm(int result,
- __u16 obj_id,
- struct ias_value *value,
- void * priv)
-{
- irnet_socket * self = (irnet_socket *) priv;
-
- DENTER(IRDA_OCB_TRACE, "(self=0x%p)\n", self);
- DASSERT(self != NULL, , IRDA_OCB_ERROR, "Self is NULL !!!\n");
-
- /* Check if already connected (via irnet_connect_socket())
- * or socket is closing down (via irda_irnet_destroy()) */
- if(! test_bit(0, &self->ttp_connect))
- {
- DERROR(IRDA_OCB_ERROR, "Socket no longer connecting. Ouch !\n");
- return;
- }
-
- /* We probably don't need to make any more queries */
- iriap_close(self->iriap);
- self->iriap = NULL;
-
- /* Post process the IAS reply */
- self->dtsap_sel = irnet_ias_to_tsap(self, result, value);
-
- /* If error, just go out */
- if(self->errno)
- {
- clear_bit(0, &self->ttp_connect);
- DERROR(IRDA_OCB_ERROR, "IAS connect failed ! (0x%X)\n", self->errno);
- return;
- }
-
- DEBUG(IRDA_OCB_INFO, "daddr = %08x, lsap = %d, starting IrTTP connection\n",
- self->daddr, self->dtsap_sel);
-
- /* Start up TTP - non blocking */
- irnet_connect_tsap(self);
-
- DEXIT(IRDA_OCB_TRACE, "\n");
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_discovervalue_confirm (result, obj_id, value, priv)
- *
- * Handle the TSAP discovery procedure state machine.
- * Got answer from remote LM-IAS, try next device
- *
- * We are doing a TSAP discovery procedure, and we got an answer to
- * a IAS query we were doing to find the TSAP on one of the address
- * in the discovery log.
- *
- * If we have found a valid TSAP for the first time, save it. If it's
- * not the first time we found one, complain.
- *
- * If we have more addresses in the log, just initiate a new query.
- * Note that those query may fail (see irnet_discover_daddr_and_lsap_sel())
- *
- * Otherwise, wrap up the procedure (cleanup), check if we have found
- * any device and connect to it.
- */
-static void
-irnet_discovervalue_confirm(int result,
- __u16 obj_id,
- struct ias_value *value,
- void * priv)
-{
- irnet_socket * self = (irnet_socket *) priv;
- __u8 dtsap_sel; /* TSAP we are looking for */
-
- DENTER(IRDA_OCB_TRACE, "(self=0x%p)\n", self);
- DASSERT(self != NULL, , IRDA_OCB_ERROR, "Self is NULL !!!\n");
-
- /* Check if already connected (via irnet_connect_socket())
- * or socket is closing down (via irda_irnet_destroy()) */
- if(! test_bit(0, &self->ttp_connect))
- {
- DERROR(IRDA_OCB_ERROR, "Socket no longer connecting. Ouch !\n");
- return;
- }
-
- /* Post process the IAS reply */
- dtsap_sel = irnet_ias_to_tsap(self, result, value);
-
- /* Have we got something ? */
- if(self->errno == 0)
- {
- /* We found the requested service */
- if(self->daddr != DEV_ADDR_ANY)
- {
- DERROR(IRDA_OCB_ERROR, "More than one device in range supports IrNET...\n");
- }
- else
- {
- /* First time we found that one, save it ! */
- self->daddr = self->discoveries[self->disco_index].daddr;
- self->dtsap_sel = dtsap_sel;
- }
- }
-
- /* If no failure */
- if((self->errno == -EADDRNOTAVAIL) || (self->errno == 0))
- {
- int ret;
-
- /* Search the next node */
- ret = irnet_discover_next_daddr(self);
- if(!ret)
- {
- /* In this case, the above request was non-blocking.
- * We will return here after a while... */
- return;
- }
- /* In this case, we have processed the last discovery item */
- }
-
- /* No more queries to be done (failure or last one) */
-
- /* We probably don't need to make any more queries */
- iriap_close(self->iriap);
- self->iriap = NULL;
-
- /* No more items : remove the log and signal termination */
- DEBUG(IRDA_OCB_INFO, "Cleaning up log (0x%p)\n",
- self->discoveries);
- if(self->discoveries != NULL)
- {
- /* Cleanup our copy of the discovery log */
- kfree(self->discoveries);
- self->discoveries = NULL;
- }
- self->disco_number = -1;
-
- /* Check out what we found */
- if(self->daddr == DEV_ADDR_ANY)
- {
- self->daddr = DEV_ADDR_ANY;
- clear_bit(0, &self->ttp_connect);
- DEXIT(IRDA_OCB_TRACE, ": cannot discover IrNET in any device !!!\n");
- return;
- }
-
- /* We have a valid address - just connect */
-
- DEBUG(IRDA_OCB_INFO, "daddr = %08x, lsap = %d, starting IrTTP connection\n",
- self->daddr, self->dtsap_sel);
-
- /* Start up TTP - non blocking */
- irnet_connect_tsap(self);
-
- DEXIT(IRDA_OCB_TRACE, "\n");
-}
-
-#ifdef DISCOVERY_EVENTS
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_discovery_indication (discovery)
- *
- * Got a discovery indication from IrLMP, post an event
- *
- * Note : IrLMP take care of matching the hint mask for us, and also
- * check if it is a "new" node for us...
- *
- * As IrLMP filter on the IrLAN hint bit, we get both IrLAN and IrNET
- * nodes, so it's only at connection time that we will know if the
- * node support IrNET, IrLAN or both. The other solution is to check
- * in IAS the PNP ids and service name.
- * Note : even if a node support IrNET (or IrLAN), it's no guarantee
- * that we will be able to connect to it, the node might already be
- * busy...
- *
- * One last thing : in some case, this function will trigger duplicate
- * discovery events. On the other hand, we should catch all
- * discoveries properly (i.e. not miss one). Filtering duplicate here
- * is to messy, so we leave that to user space...
- */
-static void
-irnet_discovery_indication(discinfo_t * discovery,
- DISCOVERY_MODE mode,
- void * priv)
-{
- irnet_socket * self = &irnet_server.s;
-
- DENTER(IRDA_OCB_TRACE, "(self=0x%p)\n", self);
- DASSERT(priv == &irnet_server, , IRDA_OCB_ERROR,
- "Invalid instance (0x%p) !!!\n", priv);
-
- DEBUG(IRDA_OCB_INFO, "Discovered new IrNET/IrLAN node %s...\n",
- discovery->info);
-
- /* Notify the control channel */
- irnet_post_event(NULL, IRNET_DISCOVER,
- discovery->saddr, discovery->daddr, discovery->info,
- get_unaligned((__u16 *)discovery->hints));
-
- DEXIT(IRDA_OCB_TRACE, "\n");
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_expiry_indication (expiry)
- *
- * Got a expiry indication from IrLMP, post an event
- *
- * Note : IrLMP take care of matching the hint mask for us, we only
- * check if it is a "new" node...
- */
-static void
-irnet_expiry_indication(discinfo_t * expiry,
- DISCOVERY_MODE mode,
- void * priv)
-{
- irnet_socket * self = &irnet_server.s;
-
- DENTER(IRDA_OCB_TRACE, "(self=0x%p)\n", self);
- DASSERT(priv == &irnet_server, , IRDA_OCB_ERROR,
- "Invalid instance (0x%p) !!!\n", priv);
-
- DEBUG(IRDA_OCB_INFO, "IrNET/IrLAN node %s expired...\n",
- expiry->info);
-
- /* Notify the control channel */
- irnet_post_event(NULL, IRNET_EXPIRE,
- expiry->saddr, expiry->daddr, expiry->info,
- get_unaligned((__u16 *)expiry->hints));
-
- DEXIT(IRDA_OCB_TRACE, "\n");
-}
-#endif /* DISCOVERY_EVENTS */
-
-
-/*********************** PROC ENTRY CALLBACKS ***********************/
-/*
- * We create a instance in the /proc filesystem, and here we take care
- * of that...
- */
-
-#ifdef CONFIG_PROC_FS
-static int
-irnet_proc_show(struct seq_file *m, void *v)
-{
- irnet_socket * self;
- char * state;
- int i = 0;
-
- /* Get the IrNET server information... */
- seq_printf(m, "IrNET server - ");
- seq_printf(m, "IrDA state: %s, ",
- (irnet_server.running ? "running" : "dead"));
- seq_printf(m, "stsap_sel: %02x, ", irnet_server.s.stsap_sel);
- seq_printf(m, "dtsap_sel: %02x\n", irnet_server.s.dtsap_sel);
-
- /* Do we need to continue ? */
- if(!irnet_server.running)
- return 0;
-
- /* Protect access to the instance list */
- spin_lock_bh(&irnet_server.spinlock);
-
- /* Get the sockets one by one... */
- self = (irnet_socket *) hashbin_get_first(irnet_server.list);
- while(self != NULL)
- {
- /* Start printing info about the socket. */
- seq_printf(m, "\nIrNET socket %d - ", i++);
-
- /* First, get the requested configuration */
- seq_printf(m, "Requested IrDA name: \"%s\", ", self->rname);
- seq_printf(m, "daddr: %08x, ", self->rdaddr);
- seq_printf(m, "saddr: %08x\n", self->rsaddr);
-
- /* Second, get all the PPP info */
- seq_printf(m, " PPP state: %s",
- (self->ppp_open ? "registered" : "unregistered"));
- if(self->ppp_open)
- {
- seq_printf(m, ", unit: ppp%d",
- ppp_unit_number(&self->chan));
- seq_printf(m, ", channel: %d",
- ppp_channel_index(&self->chan));
- seq_printf(m, ", mru: %d",
- self->mru);
- /* Maybe add self->flags ? Later... */
- }
-
- /* Then, get all the IrDA specific info... */
- if(self->ttp_open)
- state = "connected";
- else
- if(self->tsap != NULL)
- state = "connecting";
- else
- if(self->iriap != NULL)
- state = "searching";
- else
- if(self->ttp_connect)
- state = "weird";
- else
- state = "idle";
- seq_printf(m, "\n IrDA state: %s, ", state);
- seq_printf(m, "daddr: %08x, ", self->daddr);
- seq_printf(m, "stsap_sel: %02x, ", self->stsap_sel);
- seq_printf(m, "dtsap_sel: %02x\n", self->dtsap_sel);
-
- /* Next socket, please... */
- self = (irnet_socket *) hashbin_get_next(irnet_server.list);
- }
-
- /* Spin lock end */
- spin_unlock_bh(&irnet_server.spinlock);
-
- return 0;
-}
-
-static int irnet_proc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, irnet_proc_show, NULL);
-}
-
-static const struct file_operations irnet_proc_fops = {
- .owner = THIS_MODULE,
- .open = irnet_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-#endif /* PROC_FS */
-
-
-/********************** CONFIGURATION/CLEANUP **********************/
-/*
- * Initialisation and teardown of the IrDA part, called at module
- * insertion and removal...
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Prepare the IrNET layer for operation...
- */
-int __init
-irda_irnet_init(void)
-{
- int err = 0;
-
- DENTER(MODULE_TRACE, "()\n");
-
- /* Pure paranoia - should be redundant */
- memset(&irnet_server, 0, sizeof(struct irnet_root));
-
- /* Setup start of irnet instance list */
- irnet_server.list = hashbin_new(HB_NOLOCK);
- DABORT(irnet_server.list == NULL, -ENOMEM,
- MODULE_ERROR, "Can't allocate hashbin!\n");
- /* Init spinlock for instance list */
- spin_lock_init(&irnet_server.spinlock);
-
- /* Initialise control channel */
- init_waitqueue_head(&irnet_events.rwait);
- irnet_events.index = 0;
- /* Init spinlock for event logging */
- spin_lock_init(&irnet_events.spinlock);
-
-#ifdef CONFIG_PROC_FS
- /* Add a /proc file for irnet infos */
- proc_create("irnet", 0, proc_irda, &irnet_proc_fops);
-#endif /* CONFIG_PROC_FS */
-
- /* Setup the IrNET server */
- err = irnet_setup_server();
-
- if(!err)
- /* We are no longer functional... */
- irnet_server.running = 1;
-
- DEXIT(MODULE_TRACE, "\n");
- return err;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Cleanup at exit...
- */
-void __exit
-irda_irnet_cleanup(void)
-{
- DENTER(MODULE_TRACE, "()\n");
-
- /* We are no longer there... */
- irnet_server.running = 0;
-
-#ifdef CONFIG_PROC_FS
- /* Remove our /proc file */
- remove_proc_entry("irnet", proc_irda);
-#endif /* CONFIG_PROC_FS */
-
- /* Remove our IrNET server from existence */
- irnet_destroy_server();
-
- /* Remove all instances of IrNET socket still present */
- hashbin_delete(irnet_server.list, (FREE_FUNC) irda_irnet_destroy);
-
- DEXIT(MODULE_TRACE, "\n");
-}
diff --git a/drivers/staging/irda/net/irnet/irnet_irda.h b/drivers/staging/irda/net/irnet/irnet_irda.h
deleted file mode 100644
index 3e408952a3f1..000000000000
--- a/drivers/staging/irda/net/irnet/irnet_irda.h
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * IrNET protocol module : Synchronous PPP over an IrDA socket.
- *
- * Jean II - HPL `00 - <jt@hpl.hp.com>
- *
- * This file contains all definitions and declarations necessary for the
- * IRDA part of the IrNET module (dealing with IrTTP, IrIAS and co).
- * This file is a private header, so other modules don't want to know
- * what's in there...
- */
-
-#ifndef IRNET_IRDA_H
-#define IRNET_IRDA_H
-
-/***************************** INCLUDES *****************************/
-/* Please add other headers in irnet.h */
-
-#include "irnet.h" /* Module global include */
-
-/************************ CONSTANTS & MACROS ************************/
-
-/*
- * Name of the service (socket name) used by IrNET
- */
-/* IAS object name (or part of it) */
-#define IRNET_SERVICE_NAME "IrNetv1"
-/* IAS attribute */
-#define IRNET_IAS_VALUE "IrDA:TinyTP:LsapSel"
-/* LMP notify name for client (only for /proc/net/irda/irlmp) */
-#define IRNET_NOTIFY_NAME "IrNET socket"
-/* LMP notify name for server (only for /proc/net/irda/irlmp) */
-#define IRNET_NOTIFY_NAME_SERV "IrNET server"
-
-/****************************** TYPES ******************************/
-
-/*
- * This is the main structure where we store all the data pertaining to
- * the IrNET server (listen for connection requests) and the root
- * of the IrNET socket list
- */
-typedef struct irnet_root
-{
- irnet_socket s; /* To pretend we are a client... */
-
- /* Generic stuff */
- int magic; /* Paranoia */
- int running; /* Are we operational ? */
-
- /* Link list of all IrNET instances opened */
- hashbin_t * list;
- spinlock_t spinlock; /* Serialize access to the list */
- /* Note : the way hashbin has been designed is absolutely not
- * reentrant, beware... So, we blindly protect all with spinlock */
-
- /* Handle for the hint bit advertised in IrLMP */
- void * skey;
-
- /* Server socket part */
- struct ias_object * ias_obj; /* Our service name + lsap in IAS */
-
-} irnet_root;
-
-
-/**************************** PROTOTYPES ****************************/
-
-/* ----------------------- CONTROL CHANNEL ----------------------- */
-static void
- irnet_post_event(irnet_socket *,
- irnet_event,
- __u32,
- __u32,
- char *,
- __u16);
-/* ----------------------- IRDA SUBROUTINES ----------------------- */
-static inline int
- irnet_open_tsap(irnet_socket *);
-static inline __u8
- irnet_ias_to_tsap(irnet_socket *,
- int,
- struct ias_value *);
-static inline int
- irnet_find_lsap_sel(irnet_socket *);
-static inline int
- irnet_connect_tsap(irnet_socket *);
-static inline int
- irnet_discover_next_daddr(irnet_socket *);
-static inline int
- irnet_discover_daddr_and_lsap_sel(irnet_socket *);
-static inline int
- irnet_dname_to_daddr(irnet_socket *);
-/* ------------------------ SERVER SOCKET ------------------------ */
-static inline int
- irnet_daddr_to_dname(irnet_socket *);
-static inline irnet_socket *
- irnet_find_socket(irnet_socket *);
-static inline int
- irnet_connect_socket(irnet_socket *,
- irnet_socket *,
- struct qos_info *,
- __u32,
- __u8);
-static inline void
- irnet_disconnect_server(irnet_socket *,
- struct sk_buff *);
-static inline int
- irnet_setup_server(void);
-static inline void
- irnet_destroy_server(void);
-/* ---------------------- IRDA-TTP CALLBACKS ---------------------- */
-static int
- irnet_data_indication(void *, /* instance */
- void *, /* sap */
- struct sk_buff *);
-static void
- irnet_disconnect_indication(void *,
- void *,
- LM_REASON,
- struct sk_buff *);
-static void
- irnet_connect_confirm(void *,
- void *,
- struct qos_info *,
- __u32,
- __u8,
- struct sk_buff *);
-static void
- irnet_flow_indication(void *,
- void *,
- LOCAL_FLOW);
-static void
- irnet_status_indication(void *,
- LINK_STATUS,
- LOCK_STATUS);
-static void
- irnet_connect_indication(void *,
- void *,
- struct qos_info *,
- __u32,
- __u8,
- struct sk_buff *);
-/* -------------------- IRDA-IAS/LMP CALLBACKS -------------------- */
-static void
- irnet_getvalue_confirm(int,
- __u16,
- struct ias_value *,
- void *);
-static void
- irnet_discovervalue_confirm(int,
- __u16,
- struct ias_value *,
- void *);
-#ifdef DISCOVERY_EVENTS
-static void
- irnet_discovery_indication(discinfo_t *,
- DISCOVERY_MODE,
- void *);
-static void
- irnet_expiry_indication(discinfo_t *,
- DISCOVERY_MODE,
- void *);
-#endif
-
-/**************************** VARIABLES ****************************/
-
-/*
- * The IrNET server. Listen to connection requests and co...
- */
-static struct irnet_root irnet_server;
-
-/* Control channel stuff (note : extern) */
-struct irnet_ctrl_channel irnet_events;
-
-/* The /proc/net/irda directory, defined elsewhere... */
-#ifdef CONFIG_PROC_FS
-extern struct proc_dir_entry *proc_irda;
-#endif /* CONFIG_PROC_FS */
-
-#endif /* IRNET_IRDA_H */
diff --git a/drivers/staging/irda/net/irnet/irnet_ppp.c b/drivers/staging/irda/net/irnet/irnet_ppp.c
deleted file mode 100644
index c90a158af4b7..000000000000
--- a/drivers/staging/irda/net/irnet/irnet_ppp.c
+++ /dev/null
@@ -1,1189 +0,0 @@
-/*
- * IrNET protocol module : Synchronous PPP over an IrDA socket.
- *
- * Jean II - HPL `00 - <jt@hpl.hp.com>
- *
- * This file implement the PPP interface and /dev/irnet character device.
- * The PPP interface hook to the ppp_generic module, handle all our
- * relationship to the PPP code in the kernel (and by extension to pppd),
- * and exchange PPP frames with this module (send/receive).
- * The /dev/irnet device is used primarily for 2 functions :
- * 1) as a stub for pppd (the ppp daemon), so that we can appropriately
- * generate PPP sessions (we pretend we are a tty).
- * 2) as a control channel (write commands, read events)
- */
-
-#include <linux/sched/signal.h>
-#include <linux/slab.h>
-
-#include "irnet_ppp.h" /* Private header */
-/* Please put other headers in irnet.h - Thanks */
-
-/* Generic PPP callbacks (to call us) */
-static const struct ppp_channel_ops irnet_ppp_ops = {
- .start_xmit = ppp_irnet_send,
- .ioctl = ppp_irnet_ioctl
-};
-
-/************************* CONTROL CHANNEL *************************/
-/*
- * When a pppd instance is not active on /dev/irnet, it acts as a control
- * channel.
- * Writing allow to set up the IrDA destination of the IrNET channel,
- * and any application may be read events happening in IrNET...
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Write is used to send a command to configure a IrNET channel
- * before it is open by pppd. The syntax is : "command argument"
- * Currently there is only two defined commands :
- * o name : set the requested IrDA nickname of the IrNET peer.
- * o addr : set the requested IrDA address of the IrNET peer.
- * Note : the code is crude, but effective...
- */
-static inline ssize_t
-irnet_ctrl_write(irnet_socket * ap,
- const char __user *buf,
- size_t count)
-{
- char command[IRNET_MAX_COMMAND];
- char * start; /* Current command being processed */
- char * next; /* Next command to process */
- int length; /* Length of current command */
-
- DENTER(CTRL_TRACE, "(ap=0x%p, count=%zd)\n", ap, count);
-
- /* Check for overflow... */
- DABORT(count >= IRNET_MAX_COMMAND, -ENOMEM,
- CTRL_ERROR, "Too much data !!!\n");
-
- /* Get the data in the driver */
- if(copy_from_user(command, buf, count))
- {
- DERROR(CTRL_ERROR, "Invalid user space pointer.\n");
- return -EFAULT;
- }
-
- /* Safe terminate the string */
- command[count] = '\0';
- DEBUG(CTRL_INFO, "Command line received is ``%s'' (%zd).\n",
- command, count);
-
- /* Check every commands in the command line */
- next = command;
- while(next != NULL)
- {
- /* Look at the next command */
- start = next;
-
- /* Scrap whitespaces before the command */
- start = skip_spaces(start);
-
- /* ',' is our command separator */
- next = strchr(start, ',');
- if(next)
- {
- *next = '\0'; /* Terminate command */
- length = next - start; /* Length */
- next++; /* Skip the '\0' */
- }
- else
- length = strlen(start);
-
- DEBUG(CTRL_INFO, "Found command ``%s'' (%d).\n", start, length);
-
- /* Check if we recognised one of the known command
- * We can't use "switch" with strings, so hack with "continue" */
-
- /* First command : name -> Requested IrDA nickname */
- if(!strncmp(start, "name", 4))
- {
- /* Copy the name only if is included and not "any" */
- if((length > 5) && (strcmp(start + 5, "any")))
- {
- /* Strip out trailing whitespaces */
- while(isspace(start[length - 1]))
- length--;
-
- DABORT(length < 5 || length > NICKNAME_MAX_LEN + 5,
- -EINVAL, CTRL_ERROR, "Invalid nickname.\n");
-
- /* Copy the name for later reuse */
- memcpy(ap->rname, start + 5, length - 5);
- ap->rname[length - 5] = '\0';
- }
- else
- ap->rname[0] = '\0';
- DEBUG(CTRL_INFO, "Got rname = ``%s''\n", ap->rname);
-
- /* Restart the loop */
- continue;
- }
-
- /* Second command : addr, daddr -> Requested IrDA destination address
- * Also process : saddr -> Requested IrDA source address */
- if((!strncmp(start, "addr", 4)) ||
- (!strncmp(start, "daddr", 5)) ||
- (!strncmp(start, "saddr", 5)))
- {
- __u32 addr = DEV_ADDR_ANY;
-
- /* Copy the address only if is included and not "any" */
- if((length > 5) && (strcmp(start + 5, "any")))
- {
- char * begp = start + 5;
- char * endp;
-
- /* Scrap whitespaces before the command */
- begp = skip_spaces(begp);
-
- /* Convert argument to a number (last arg is the base) */
- addr = simple_strtoul(begp, &endp, 16);
- /* Has it worked ? (endp should be start + length) */
- DABORT(endp <= (start + 5), -EINVAL,
- CTRL_ERROR, "Invalid address.\n");
- }
- /* Which type of address ? */
- if(start[0] == 's')
- {
- /* Save it */
- ap->rsaddr = addr;
- DEBUG(CTRL_INFO, "Got rsaddr = %08x\n", ap->rsaddr);
- }
- else
- {
- /* Save it */
- ap->rdaddr = addr;
- DEBUG(CTRL_INFO, "Got rdaddr = %08x\n", ap->rdaddr);
- }
-
- /* Restart the loop */
- continue;
- }
-
- /* Other possible command : connect N (number of retries) */
-
- /* No command matched -> Failed... */
- DABORT(1, -EINVAL, CTRL_ERROR, "Not a recognised IrNET command.\n");
- }
-
- /* Success : we have parsed all commands successfully */
- return count;
-}
-
-#ifdef INITIAL_DISCOVERY
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_get_discovery_log (self)
- *
- * Query the content on the discovery log if not done
- *
- * This function query the current content of the discovery log
- * at the startup of the event channel and save it in the internal struct.
- */
-static void
-irnet_get_discovery_log(irnet_socket * ap)
-{
- __u16 mask = irlmp_service_to_hint(S_LAN);
-
- /* Ask IrLMP for the current discovery log */
- ap->discoveries = irlmp_get_discoveries(&ap->disco_number, mask,
- DISCOVERY_DEFAULT_SLOTS);
-
- /* Check if the we got some results */
- if(ap->discoveries == NULL)
- ap->disco_number = -1;
-
- DEBUG(CTRL_INFO, "Got the log (0x%p), size is %d\n",
- ap->discoveries, ap->disco_number);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_read_discovery_log (self, event)
- *
- * Read the content on the discovery log
- *
- * This function dump the current content of the discovery log
- * at the startup of the event channel.
- * Return 1 if wrote an event on the control channel...
- *
- * State of the ap->disco_XXX variables :
- * Socket creation : discoveries = NULL ; disco_index = 0 ; disco_number = 0
- * While reading : discoveries = ptr ; disco_index = X ; disco_number = Y
- * After reading : discoveries = NULL ; disco_index = Y ; disco_number = -1
- */
-static inline int
-irnet_read_discovery_log(irnet_socket *ap, char *event, int buf_size)
-{
- int done_event = 0;
-
- DENTER(CTRL_TRACE, "(ap=0x%p, event=0x%p)\n",
- ap, event);
-
- /* Test if we have some work to do or we have already finished */
- if(ap->disco_number == -1)
- {
- DEBUG(CTRL_INFO, "Already done\n");
- return 0;
- }
-
- /* Test if it's the first time and therefore we need to get the log */
- if(ap->discoveries == NULL)
- irnet_get_discovery_log(ap);
-
- /* Check if we have more item to dump */
- if(ap->disco_index < ap->disco_number)
- {
- /* Write an event */
- snprintf(event, buf_size,
- "Found %08x (%s) behind %08x {hints %02X-%02X}\n",
- ap->discoveries[ap->disco_index].daddr,
- ap->discoveries[ap->disco_index].info,
- ap->discoveries[ap->disco_index].saddr,
- ap->discoveries[ap->disco_index].hints[0],
- ap->discoveries[ap->disco_index].hints[1]);
- DEBUG(CTRL_INFO, "Writing discovery %d : %s\n",
- ap->disco_index, ap->discoveries[ap->disco_index].info);
-
- /* We have an event */
- done_event = 1;
- /* Next discovery */
- ap->disco_index++;
- }
-
- /* Check if we have done the last item */
- if(ap->disco_index >= ap->disco_number)
- {
- /* No more items : remove the log and signal termination */
- DEBUG(CTRL_INFO, "Cleaning up log (0x%p)\n",
- ap->discoveries);
- if(ap->discoveries != NULL)
- {
- /* Cleanup our copy of the discovery log */
- kfree(ap->discoveries);
- ap->discoveries = NULL;
- }
- ap->disco_number = -1;
- }
-
- return done_event;
-}
-#endif /* INITIAL_DISCOVERY */
-
-/*------------------------------------------------------------------*/
-/*
- * Read is used to get IrNET events
- */
-static inline ssize_t
-irnet_ctrl_read(irnet_socket * ap,
- struct file * file,
- char __user * buf,
- size_t count)
-{
- DECLARE_WAITQUEUE(wait, current);
- char event[75];
- ssize_t ret = 0;
-
- DENTER(CTRL_TRACE, "(ap=0x%p, count=%zd)\n", ap, count);
-
-#ifdef INITIAL_DISCOVERY
- /* Check if we have read the log */
- if (irnet_read_discovery_log(ap, event, sizeof(event)))
- {
- count = min(strlen(event), count);
- if (copy_to_user(buf, event, count))
- {
- DERROR(CTRL_ERROR, "Invalid user space pointer.\n");
- return -EFAULT;
- }
-
- DEXIT(CTRL_TRACE, "\n");
- return count;
- }
-#endif /* INITIAL_DISCOVERY */
-
- /* Put ourselves on the wait queue to be woken up */
- add_wait_queue(&irnet_events.rwait, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
- for(;;)
- {
- /* If there is unread events */
- ret = 0;
- if(ap->event_index != irnet_events.index)
- break;
- ret = -EAGAIN;
- if(file->f_flags & O_NONBLOCK)
- break;
- ret = -ERESTARTSYS;
- if(signal_pending(current))
- break;
- /* Yield and wait to be woken up */
- schedule();
- }
- __set_current_state(TASK_RUNNING);
- remove_wait_queue(&irnet_events.rwait, &wait);
-
- /* Did we got it ? */
- if(ret != 0)
- {
- /* No, return the error code */
- DEXIT(CTRL_TRACE, " - ret %zd\n", ret);
- return ret;
- }
-
- /* Which event is it ? */
- switch(irnet_events.log[ap->event_index].event)
- {
- case IRNET_DISCOVER:
- snprintf(event, sizeof(event),
- "Discovered %08x (%s) behind %08x {hints %02X-%02X}\n",
- irnet_events.log[ap->event_index].daddr,
- irnet_events.log[ap->event_index].name,
- irnet_events.log[ap->event_index].saddr,
- irnet_events.log[ap->event_index].hints.byte[0],
- irnet_events.log[ap->event_index].hints.byte[1]);
- break;
- case IRNET_EXPIRE:
- snprintf(event, sizeof(event),
- "Expired %08x (%s) behind %08x {hints %02X-%02X}\n",
- irnet_events.log[ap->event_index].daddr,
- irnet_events.log[ap->event_index].name,
- irnet_events.log[ap->event_index].saddr,
- irnet_events.log[ap->event_index].hints.byte[0],
- irnet_events.log[ap->event_index].hints.byte[1]);
- break;
- case IRNET_CONNECT_TO:
- snprintf(event, sizeof(event), "Connected to %08x (%s) on ppp%d\n",
- irnet_events.log[ap->event_index].daddr,
- irnet_events.log[ap->event_index].name,
- irnet_events.log[ap->event_index].unit);
- break;
- case IRNET_CONNECT_FROM:
- snprintf(event, sizeof(event), "Connection from %08x (%s) on ppp%d\n",
- irnet_events.log[ap->event_index].daddr,
- irnet_events.log[ap->event_index].name,
- irnet_events.log[ap->event_index].unit);
- break;
- case IRNET_REQUEST_FROM:
- snprintf(event, sizeof(event), "Request from %08x (%s) behind %08x\n",
- irnet_events.log[ap->event_index].daddr,
- irnet_events.log[ap->event_index].name,
- irnet_events.log[ap->event_index].saddr);
- break;
- case IRNET_NOANSWER_FROM:
- snprintf(event, sizeof(event), "No-answer from %08x (%s) on ppp%d\n",
- irnet_events.log[ap->event_index].daddr,
- irnet_events.log[ap->event_index].name,
- irnet_events.log[ap->event_index].unit);
- break;
- case IRNET_BLOCKED_LINK:
- snprintf(event, sizeof(event), "Blocked link with %08x (%s) on ppp%d\n",
- irnet_events.log[ap->event_index].daddr,
- irnet_events.log[ap->event_index].name,
- irnet_events.log[ap->event_index].unit);
- break;
- case IRNET_DISCONNECT_FROM:
- snprintf(event, sizeof(event), "Disconnection from %08x (%s) on ppp%d\n",
- irnet_events.log[ap->event_index].daddr,
- irnet_events.log[ap->event_index].name,
- irnet_events.log[ap->event_index].unit);
- break;
- case IRNET_DISCONNECT_TO:
- snprintf(event, sizeof(event), "Disconnected to %08x (%s)\n",
- irnet_events.log[ap->event_index].daddr,
- irnet_events.log[ap->event_index].name);
- break;
- default:
- snprintf(event, sizeof(event), "Bug\n");
- }
- /* Increment our event index */
- ap->event_index = (ap->event_index + 1) % IRNET_MAX_EVENTS;
-
- DEBUG(CTRL_INFO, "Event is :%s", event);
-
- count = min(strlen(event), count);
- if (copy_to_user(buf, event, count))
- {
- DERROR(CTRL_ERROR, "Invalid user space pointer.\n");
- return -EFAULT;
- }
-
- DEXIT(CTRL_TRACE, "\n");
- return count;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Poll : called when someone do a select on /dev/irnet.
- * Just check if there are new events...
- */
-static inline __poll_t
-irnet_ctrl_poll(irnet_socket * ap,
- struct file * file,
- poll_table * wait)
-{
- __poll_t mask;
-
- DENTER(CTRL_TRACE, "(ap=0x%p)\n", ap);
-
- poll_wait(file, &irnet_events.rwait, wait);
- mask = EPOLLOUT | EPOLLWRNORM;
- /* If there is unread events */
- if(ap->event_index != irnet_events.index)
- mask |= EPOLLIN | EPOLLRDNORM;
-#ifdef INITIAL_DISCOVERY
- if(ap->disco_number != -1)
- {
- /* Test if it's the first time and therefore we need to get the log */
- if(ap->discoveries == NULL)
- irnet_get_discovery_log(ap);
- /* Recheck */
- if(ap->disco_number != -1)
- mask |= EPOLLIN | EPOLLRDNORM;
- }
-#endif /* INITIAL_DISCOVERY */
-
- DEXIT(CTRL_TRACE, " - mask=0x%X\n", mask);
- return mask;
-}
-
-
-/*********************** FILESYSTEM CALLBACKS ***********************/
-/*
- * Implement the usual open, read, write functions that will be called
- * by the file system when some action is performed on /dev/irnet.
- * Most of those actions will in fact be performed by "pppd" or
- * the control channel, we just act as a redirector...
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Open : when somebody open /dev/irnet
- * We basically create a new instance of irnet and initialise it.
- */
-static int
-dev_irnet_open(struct inode * inode,
- struct file * file)
-{
- struct irnet_socket * ap;
- int err;
-
- DENTER(FS_TRACE, "(file=0x%p)\n", file);
-
-#ifdef SECURE_DEVIRNET
- /* This could (should?) be enforced by the permissions on /dev/irnet. */
- if(!capable(CAP_NET_ADMIN))
- return -EPERM;
-#endif /* SECURE_DEVIRNET */
-
- /* Allocate a private structure for this IrNET instance */
- ap = kzalloc(sizeof(*ap), GFP_KERNEL);
- DABORT(ap == NULL, -ENOMEM, FS_ERROR, "Can't allocate struct irnet...\n");
-
- /* initialize the irnet structure */
- ap->file = file;
-
- /* PPP channel setup */
- ap->ppp_open = 0;
- ap->chan.private = ap;
- ap->chan.ops = &irnet_ppp_ops;
- ap->chan.mtu = (2048 - TTP_MAX_HEADER - 2 - PPP_HDRLEN);
- ap->chan.hdrlen = 2 + TTP_MAX_HEADER; /* for A/C + Max IrDA hdr */
- /* PPP parameters */
- ap->mru = (2048 - TTP_MAX_HEADER - 2 - PPP_HDRLEN);
- ap->xaccm[0] = ~0U;
- ap->xaccm[3] = 0x60000000U;
- ap->raccm = ~0U;
-
- /* Setup the IrDA part... */
- err = irda_irnet_create(ap);
- if(err)
- {
- DERROR(FS_ERROR, "Can't setup IrDA link...\n");
- kfree(ap);
-
- return err;
- }
-
- /* For the control channel */
- ap->event_index = irnet_events.index; /* Cancel all past events */
-
- mutex_init(&ap->lock);
-
- /* Put our stuff where we will be able to find it later */
- file->private_data = ap;
-
- DEXIT(FS_TRACE, " - ap=0x%p\n", ap);
-
- return 0;
-}
-
-
-/*------------------------------------------------------------------*/
-/*
- * Close : when somebody close /dev/irnet
- * Destroy the instance of /dev/irnet
- */
-static int
-dev_irnet_close(struct inode * inode,
- struct file * file)
-{
- irnet_socket * ap = file->private_data;
-
- DENTER(FS_TRACE, "(file=0x%p, ap=0x%p)\n",
- file, ap);
- DABORT(ap == NULL, 0, FS_ERROR, "ap is NULL !!!\n");
-
- /* Detach ourselves */
- file->private_data = NULL;
-
- /* Close IrDA stuff */
- irda_irnet_destroy(ap);
-
- /* Disconnect from the generic PPP layer if not already done */
- if(ap->ppp_open)
- {
- DERROR(FS_ERROR, "Channel still registered - deregistering !\n");
- ap->ppp_open = 0;
- ppp_unregister_channel(&ap->chan);
- }
-
- kfree(ap);
-
- DEXIT(FS_TRACE, "\n");
- return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Write does nothing.
- * (we receive packet from ppp_generic through ppp_irnet_send())
- */
-static ssize_t
-dev_irnet_write(struct file * file,
- const char __user *buf,
- size_t count,
- loff_t * ppos)
-{
- irnet_socket * ap = file->private_data;
-
- DPASS(FS_TRACE, "(file=0x%p, ap=0x%p, count=%zd)\n",
- file, ap, count);
- DABORT(ap == NULL, -ENXIO, FS_ERROR, "ap is NULL !!!\n");
-
- /* If we are connected to ppp_generic, let it handle the job */
- if(ap->ppp_open)
- return -EAGAIN;
- else
- return irnet_ctrl_write(ap, buf, count);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Read doesn't do much either.
- * (pppd poll us, but ultimately reads through /dev/ppp)
- */
-static ssize_t
-dev_irnet_read(struct file * file,
- char __user * buf,
- size_t count,
- loff_t * ppos)
-{
- irnet_socket * ap = file->private_data;
-
- DPASS(FS_TRACE, "(file=0x%p, ap=0x%p, count=%zd)\n",
- file, ap, count);
- DABORT(ap == NULL, -ENXIO, FS_ERROR, "ap is NULL !!!\n");
-
- /* If we are connected to ppp_generic, let it handle the job */
- if(ap->ppp_open)
- return -EAGAIN;
- else
- return irnet_ctrl_read(ap, file, buf, count);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Poll : called when someone do a select on /dev/irnet
- */
-static __poll_t
-dev_irnet_poll(struct file * file,
- poll_table * wait)
-{
- irnet_socket * ap = file->private_data;
- __poll_t mask;
-
- DENTER(FS_TRACE, "(file=0x%p, ap=0x%p)\n",
- file, ap);
-
- mask = EPOLLOUT | EPOLLWRNORM;
- DABORT(ap == NULL, mask, FS_ERROR, "ap is NULL !!!\n");
-
- /* If we are connected to ppp_generic, let it handle the job */
- if(!ap->ppp_open)
- mask |= irnet_ctrl_poll(ap, file, wait);
-
- DEXIT(FS_TRACE, " - mask=0x%X\n", mask);
- return mask;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * IOCtl : Called when someone does some ioctls on /dev/irnet
- * This is the way pppd configure us and control us while the PPP
- * instance is active.
- */
-static long
-dev_irnet_ioctl(
- struct file * file,
- unsigned int cmd,
- unsigned long arg)
-{
- irnet_socket * ap = file->private_data;
- int err;
- int val;
- void __user *argp = (void __user *)arg;
-
- DENTER(FS_TRACE, "(file=0x%p, ap=0x%p, cmd=0x%X)\n",
- file, ap, cmd);
-
- /* Basic checks... */
- DASSERT(ap != NULL, -ENXIO, PPP_ERROR, "ap is NULL...\n");
-#ifdef SECURE_DEVIRNET
- if(!capable(CAP_NET_ADMIN))
- return -EPERM;
-#endif /* SECURE_DEVIRNET */
-
- err = -EFAULT;
- switch(cmd)
- {
- /* Set discipline (should be N_SYNC_PPP or N_TTY) */
- case TIOCSETD:
- if(get_user(val, (int __user *)argp))
- break;
- if((val == N_SYNC_PPP) || (val == N_PPP))
- {
- DEBUG(FS_INFO, "Entering PPP discipline.\n");
- /* PPP channel setup (ap->chan in configured in dev_irnet_open())*/
- if (mutex_lock_interruptible(&ap->lock))
- return -EINTR;
-
- err = ppp_register_channel(&ap->chan);
- if(err == 0)
- {
- /* Our ppp side is active */
- ap->ppp_open = 1;
-
- DEBUG(FS_INFO, "Trying to establish a connection.\n");
- /* Setup the IrDA link now - may fail... */
- irda_irnet_connect(ap);
- }
- else
- DERROR(FS_ERROR, "Can't setup PPP channel...\n");
-
- mutex_unlock(&ap->lock);
- }
- else
- {
- /* In theory, should be N_TTY */
- DEBUG(FS_INFO, "Exiting PPP discipline.\n");
- /* Disconnect from the generic PPP layer */
- if (mutex_lock_interruptible(&ap->lock))
- return -EINTR;
-
- if(ap->ppp_open)
- {
- ap->ppp_open = 0;
- ppp_unregister_channel(&ap->chan);
- }
- else
- DERROR(FS_ERROR, "Channel not registered !\n");
- err = 0;
-
- mutex_unlock(&ap->lock);
- }
- break;
-
- /* Query PPP channel and unit number */
- case PPPIOCGCHAN:
- if (mutex_lock_interruptible(&ap->lock))
- return -EINTR;
-
- if(ap->ppp_open && !put_user(ppp_channel_index(&ap->chan),
- (int __user *)argp))
- err = 0;
-
- mutex_unlock(&ap->lock);
- break;
- case PPPIOCGUNIT:
- if (mutex_lock_interruptible(&ap->lock))
- return -EINTR;
-
- if(ap->ppp_open && !put_user(ppp_unit_number(&ap->chan),
- (int __user *)argp))
- err = 0;
-
- mutex_unlock(&ap->lock);
- break;
-
- /* All these ioctls can be passed both directly and from ppp_generic,
- * so we just deal with them in one place...
- */
- case PPPIOCGFLAGS:
- case PPPIOCSFLAGS:
- case PPPIOCGASYNCMAP:
- case PPPIOCSASYNCMAP:
- case PPPIOCGRASYNCMAP:
- case PPPIOCSRASYNCMAP:
- case PPPIOCGXASYNCMAP:
- case PPPIOCSXASYNCMAP:
- case PPPIOCGMRU:
- case PPPIOCSMRU:
- DEBUG(FS_INFO, "Standard PPP ioctl.\n");
- if(!capable(CAP_NET_ADMIN))
- err = -EPERM;
- else {
- if (mutex_lock_interruptible(&ap->lock))
- return -EINTR;
-
- err = ppp_irnet_ioctl(&ap->chan, cmd, arg);
-
- mutex_unlock(&ap->lock);
- }
- break;
-
- /* TTY IOCTLs : Pretend that we are a tty, to keep pppd happy */
- /* Get termios */
- case TCGETS:
- DEBUG(FS_INFO, "Get termios.\n");
- if (mutex_lock_interruptible(&ap->lock))
- return -EINTR;
-
-#ifndef TCGETS2
- if(!kernel_termios_to_user_termios((struct termios __user *)argp, &ap->termios))
- err = 0;
-#else
- if(kernel_termios_to_user_termios_1((struct termios __user *)argp, &ap->termios))
- err = 0;
-#endif
-
- mutex_unlock(&ap->lock);
- break;
- /* Set termios */
- case TCSETSF:
- DEBUG(FS_INFO, "Set termios.\n");
- if (mutex_lock_interruptible(&ap->lock))
- return -EINTR;
-
-#ifndef TCGETS2
- if(!user_termios_to_kernel_termios(&ap->termios, (struct termios __user *)argp))
- err = 0;
-#else
- if(!user_termios_to_kernel_termios_1(&ap->termios, (struct termios __user *)argp))
- err = 0;
-#endif
-
- mutex_unlock(&ap->lock);
- break;
-
- /* Set DTR/RTS */
- case TIOCMBIS:
- case TIOCMBIC:
- /* Set exclusive/non-exclusive mode */
- case TIOCEXCL:
- case TIOCNXCL:
- DEBUG(FS_INFO, "TTY compatibility.\n");
- err = 0;
- break;
-
- case TCGETA:
- DEBUG(FS_INFO, "TCGETA\n");
- break;
-
- case TCFLSH:
- DEBUG(FS_INFO, "TCFLSH\n");
- /* Note : this will flush buffers in PPP, so it *must* be done
- * We should also worry that we don't accept junk here and that
- * we get rid of our own buffers */
-#ifdef FLUSH_TO_PPP
- if (mutex_lock_interruptible(&ap->lock))
- return -EINTR;
- ppp_output_wakeup(&ap->chan);
- mutex_unlock(&ap->lock);
-#endif /* FLUSH_TO_PPP */
- err = 0;
- break;
-
- case FIONREAD:
- DEBUG(FS_INFO, "FIONREAD\n");
- val = 0;
- if(put_user(val, (int __user *)argp))
- break;
- err = 0;
- break;
-
- default:
- DERROR(FS_ERROR, "Unsupported ioctl (0x%X)\n", cmd);
- err = -ENOTTY;
- }
-
- DEXIT(FS_TRACE, " - err = 0x%X\n", err);
- return err;
-}
-
-/************************** PPP CALLBACKS **************************/
-/*
- * This are the functions that the generic PPP driver in the kernel
- * will call to communicate to us.
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Prepare the ppp frame for transmission over the IrDA socket.
- * We make sure that the header space is enough, and we change ppp header
- * according to flags passed by pppd.
- * This is not a callback, but just a helper function used in ppp_irnet_send()
- */
-static inline struct sk_buff *
-irnet_prepare_skb(irnet_socket * ap,
- struct sk_buff * skb)
-{
- unsigned char * data;
- int proto; /* PPP protocol */
- int islcp; /* Protocol == LCP */
- int needaddr; /* Need PPP address */
-
- DENTER(PPP_TRACE, "(ap=0x%p, skb=0x%p)\n",
- ap, skb);
-
- /* Extract PPP protocol from the frame */
- data = skb->data;
- proto = (data[0] << 8) + data[1];
-
- /* LCP packets with codes between 1 (configure-request)
- * and 7 (code-reject) must be sent as though no options
- * have been negotiated. */
- islcp = (proto == PPP_LCP) && (1 <= data[2]) && (data[2] <= 7);
-
- /* compress protocol field if option enabled */
- if((data[0] == 0) && (ap->flags & SC_COMP_PROT) && (!islcp))
- skb_pull(skb,1);
-
- /* Check if we need address/control fields */
- needaddr = 2*((ap->flags & SC_COMP_AC) == 0 || islcp);
-
- /* Is the skb headroom large enough to contain all IrDA-headers? */
- if((skb_headroom(skb) < (ap->max_header_size + needaddr)) ||
- (skb_shared(skb)))
- {
- struct sk_buff * new_skb;
-
- DEBUG(PPP_INFO, "Reallocating skb\n");
-
- /* Create a new skb */
- new_skb = skb_realloc_headroom(skb, ap->max_header_size + needaddr);
-
- /* We have to free the original skb anyway */
- dev_kfree_skb(skb);
-
- /* Did the realloc succeed ? */
- DABORT(new_skb == NULL, NULL, PPP_ERROR, "Could not realloc skb\n");
-
- /* Use the new skb instead */
- skb = new_skb;
- }
-
- /* prepend address/control fields if necessary */
- if(needaddr)
- {
- skb_push(skb, 2);
- skb->data[0] = PPP_ALLSTATIONS;
- skb->data[1] = PPP_UI;
- }
-
- DEXIT(PPP_TRACE, "\n");
-
- return skb;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Send a packet to the peer over the IrTTP connection.
- * Returns 1 iff the packet was accepted.
- * Returns 0 iff packet was not consumed.
- * If the packet was not accepted, we will call ppp_output_wakeup
- * at some later time to reactivate flow control in ppp_generic.
- */
-static int
-ppp_irnet_send(struct ppp_channel * chan,
- struct sk_buff * skb)
-{
- irnet_socket * self = (struct irnet_socket *) chan->private;
- int ret;
-
- DENTER(PPP_TRACE, "(channel=0x%p, ap/self=0x%p)\n",
- chan, self);
-
- /* Check if things are somewhat valid... */
- DASSERT(self != NULL, 0, PPP_ERROR, "Self is NULL !!!\n");
-
- /* Check if we are connected */
- if(!(test_bit(0, &self->ttp_open)))
- {
-#ifdef CONNECT_IN_SEND
- /* Let's try to connect one more time... */
- /* Note : we won't be connected after this call, but we should be
- * ready for next packet... */
- /* If we are already connecting, this will fail */
- irda_irnet_connect(self);
-#endif /* CONNECT_IN_SEND */
-
- DEBUG(PPP_INFO, "IrTTP not ready ! (%ld-%ld)\n",
- self->ttp_open, self->ttp_connect);
-
- /* Note : we can either drop the packet or block the packet.
- *
- * Blocking the packet allow us a better connection time,
- * because by calling ppp_output_wakeup() we can have
- * ppp_generic resending the LCP request immediately to us,
- * rather than waiting for one of pppd periodic transmission of
- * LCP request.
- *
- * On the other hand, if we block all packet, all those periodic
- * transmissions of pppd accumulate in ppp_generic, creating a
- * backlog of LCP request. When we eventually connect later on,
- * we have to transmit all this backlog before we can connect
- * proper (if we don't timeout before).
- *
- * The current strategy is as follow :
- * While we are attempting to connect, we block packets to get
- * a better connection time.
- * If we fail to connect, we drain the queue and start dropping packets
- */
-#ifdef BLOCK_WHEN_CONNECT
- /* If we are attempting to connect */
- if(test_bit(0, &self->ttp_connect))
- {
- /* Blocking packet, ppp_generic will retry later */
- return 0;
- }
-#endif /* BLOCK_WHEN_CONNECT */
-
- /* Dropping packet, pppd will retry later */
- dev_kfree_skb(skb);
- return 1;
- }
-
- /* Check if the queue can accept any packet, otherwise block */
- if(self->tx_flow != FLOW_START)
- DRETURN(0, PPP_INFO, "IrTTP queue full (%d skbs)...\n",
- skb_queue_len(&self->tsap->tx_queue));
-
- /* Prepare ppp frame for transmission */
- skb = irnet_prepare_skb(self, skb);
- DABORT(skb == NULL, 1, PPP_ERROR, "Prepare skb for Tx failed.\n");
-
- /* Send the packet to IrTTP */
- ret = irttp_data_request(self->tsap, skb);
- if(ret < 0)
- {
- /*
- * > IrTTPs tx queue is full, so we just have to
- * > drop the frame! You might think that we should
- * > just return -1 and don't deallocate the frame,
- * > but that is dangerous since it's possible that
- * > we have replaced the original skb with a new
- * > one with larger headroom, and that would really
- * > confuse do_dev_queue_xmit() in dev.c! I have
- * > tried :-) DB
- * Correction : we verify the flow control above (self->tx_flow),
- * so we come here only if IrTTP doesn't like the packet (empty,
- * too large, IrTTP not connected). In those rare cases, it's ok
- * to drop it, we don't want to see it here again...
- * Jean II
- */
- DERROR(PPP_ERROR, "IrTTP doesn't like this packet !!! (0x%X)\n", ret);
- /* irttp_data_request already free the packet */
- }
-
- DEXIT(PPP_TRACE, "\n");
- return 1; /* Packet has been consumed */
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Take care of the ioctls that ppp_generic doesn't want to deal with...
- * Note : we are also called from dev_irnet_ioctl().
- */
-static int
-ppp_irnet_ioctl(struct ppp_channel * chan,
- unsigned int cmd,
- unsigned long arg)
-{
- irnet_socket * ap = (struct irnet_socket *) chan->private;
- int err;
- int val;
- u32 accm[8];
- void __user *argp = (void __user *)arg;
-
- DENTER(PPP_TRACE, "(channel=0x%p, ap=0x%p, cmd=0x%X)\n",
- chan, ap, cmd);
-
- /* Basic checks... */
- DASSERT(ap != NULL, -ENXIO, PPP_ERROR, "ap is NULL...\n");
-
- err = -EFAULT;
- switch(cmd)
- {
- /* PPP flags */
- case PPPIOCGFLAGS:
- val = ap->flags | ap->rbits;
- if(put_user(val, (int __user *) argp))
- break;
- err = 0;
- break;
- case PPPIOCSFLAGS:
- if(get_user(val, (int __user *) argp))
- break;
- ap->flags = val & ~SC_RCV_BITS;
- ap->rbits = val & SC_RCV_BITS;
- err = 0;
- break;
-
- /* Async map stuff - all dummy to please pppd */
- case PPPIOCGASYNCMAP:
- if(put_user(ap->xaccm[0], (u32 __user *) argp))
- break;
- err = 0;
- break;
- case PPPIOCSASYNCMAP:
- if(get_user(ap->xaccm[0], (u32 __user *) argp))
- break;
- err = 0;
- break;
- case PPPIOCGRASYNCMAP:
- if(put_user(ap->raccm, (u32 __user *) argp))
- break;
- err = 0;
- break;
- case PPPIOCSRASYNCMAP:
- if(get_user(ap->raccm, (u32 __user *) argp))
- break;
- err = 0;
- break;
- case PPPIOCGXASYNCMAP:
- if(copy_to_user(argp, ap->xaccm, sizeof(ap->xaccm)))
- break;
- err = 0;
- break;
- case PPPIOCSXASYNCMAP:
- if(copy_from_user(accm, argp, sizeof(accm)))
- break;
- accm[2] &= ~0x40000000U; /* can't escape 0x5e */
- accm[3] |= 0x60000000U; /* must escape 0x7d, 0x7e */
- memcpy(ap->xaccm, accm, sizeof(ap->xaccm));
- err = 0;
- break;
-
- /* Max PPP frame size */
- case PPPIOCGMRU:
- if(put_user(ap->mru, (int __user *) argp))
- break;
- err = 0;
- break;
- case PPPIOCSMRU:
- if(get_user(val, (int __user *) argp))
- break;
- if(val < PPP_MRU)
- val = PPP_MRU;
- ap->mru = val;
- err = 0;
- break;
-
- default:
- DEBUG(PPP_INFO, "Unsupported ioctl (0x%X)\n", cmd);
- err = -ENOIOCTLCMD;
- }
-
- DEXIT(PPP_TRACE, " - err = 0x%X\n", err);
- return err;
-}
-
-/************************** INITIALISATION **************************/
-/*
- * Module initialisation and all that jazz...
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Hook our device callbacks in the filesystem, to connect our code
- * to /dev/irnet
- */
-static inline int __init
-ppp_irnet_init(void)
-{
- int err = 0;
-
- DENTER(MODULE_TRACE, "()\n");
-
- /* Allocate ourselves as a minor in the misc range */
- err = misc_register(&irnet_misc_device);
-
- DEXIT(MODULE_TRACE, "\n");
- return err;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Cleanup at exit...
- */
-static inline void __exit
-ppp_irnet_cleanup(void)
-{
- DENTER(MODULE_TRACE, "()\n");
-
- /* De-allocate /dev/irnet minor in misc range */
- misc_deregister(&irnet_misc_device);
-
- DEXIT(MODULE_TRACE, "\n");
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Module main entry point
- */
-static int __init
-irnet_init(void)
-{
- int err;
-
- /* Initialise both parts... */
- err = irda_irnet_init();
- if(!err)
- err = ppp_irnet_init();
- return err;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Module exit
- */
-static void __exit
-irnet_cleanup(void)
-{
- irda_irnet_cleanup();
- ppp_irnet_cleanup();
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Module magic
- */
-module_init(irnet_init);
-module_exit(irnet_cleanup);
-MODULE_AUTHOR("Jean Tourrilhes <jt@hpl.hp.com>");
-MODULE_DESCRIPTION("IrNET : Synchronous PPP over IrDA");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_CHARDEV(10, 187);
diff --git a/drivers/staging/irda/net/irnet/irnet_ppp.h b/drivers/staging/irda/net/irnet/irnet_ppp.h
deleted file mode 100644
index e6d5aa2a8aac..000000000000
--- a/drivers/staging/irda/net/irnet/irnet_ppp.h
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * IrNET protocol module : Synchronous PPP over an IrDA socket.
- *
- * Jean II - HPL `00 - <jt@hpl.hp.com>
- *
- * This file contains all definitions and declarations necessary for the
- * PPP part of the IrNET module.
- * This file is a private header, so other modules don't want to know
- * what's in there...
- */
-
-#ifndef IRNET_PPP_H
-#define IRNET_PPP_H
-
-/***************************** INCLUDES *****************************/
-
-#include "irnet.h" /* Module global include */
-#include <linux/miscdevice.h>
-
-/************************ CONSTANTS & MACROS ************************/
-
-/* IrNET control channel stuff */
-#define IRNET_MAX_COMMAND 256 /* Max length of a command line */
-
-/* PPP hardcore stuff */
-
-/* Bits in rbits (PPP flags in irnet struct) */
-#define SC_RCV_BITS (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP)
-
-/* Bit numbers in busy */
-#define XMIT_BUSY 0
-#define RECV_BUSY 1
-#define XMIT_WAKEUP 2
-#define XMIT_FULL 3
-
-/* Queue management */
-#define PPPSYNC_MAX_RQLEN 32 /* arbitrary */
-
-/****************************** TYPES ******************************/
-
-
-/**************************** PROTOTYPES ****************************/
-
-/* ----------------------- CONTROL CHANNEL ----------------------- */
-static inline ssize_t
- irnet_ctrl_write(irnet_socket *,
- const char *,
- size_t);
-static inline ssize_t
- irnet_ctrl_read(irnet_socket *,
- struct file *,
- char *,
- size_t);
-static inline unsigned int
- irnet_ctrl_poll(irnet_socket *,
- struct file *,
- poll_table *);
-/* ----------------------- CHARACTER DEVICE ----------------------- */
-static int
- dev_irnet_open(struct inode *, /* fs callback : open */
- struct file *),
- dev_irnet_close(struct inode *,
- struct file *);
-static ssize_t
- dev_irnet_write(struct file *,
- const char __user *,
- size_t,
- loff_t *),
- dev_irnet_read(struct file *,
- char __user *,
- size_t,
- loff_t *);
-static __poll_t
- dev_irnet_poll(struct file *,
- poll_table *);
-static long
- dev_irnet_ioctl(struct file *,
- unsigned int,
- unsigned long);
-/* ------------------------ PPP INTERFACE ------------------------ */
-static inline struct sk_buff *
- irnet_prepare_skb(irnet_socket *,
- struct sk_buff *);
-static int
- ppp_irnet_send(struct ppp_channel *,
- struct sk_buff *);
-static int
- ppp_irnet_ioctl(struct ppp_channel *,
- unsigned int,
- unsigned long);
-
-/**************************** VARIABLES ****************************/
-
-/* Filesystem callbacks (to call us) */
-static const struct file_operations irnet_device_fops =
-{
- .owner = THIS_MODULE,
- .read = dev_irnet_read,
- .write = dev_irnet_write,
- .poll = dev_irnet_poll,
- .unlocked_ioctl = dev_irnet_ioctl,
- .open = dev_irnet_open,
- .release = dev_irnet_close,
- .llseek = noop_llseek,
- /* Also : llseek, readdir, mmap, flush, fsync, fasync, lock, readv, writev */
-};
-
-/* Structure so that the misc major (drivers/char/misc.c) take care of us... */
-static struct miscdevice irnet_misc_device =
-{
- .minor = IRNET_MINOR,
- .name = "irnet",
- .fops = &irnet_device_fops
-};
-
-#endif /* IRNET_PPP_H */
diff --git a/drivers/staging/irda/net/irnetlink.c b/drivers/staging/irda/net/irnetlink.c
deleted file mode 100644
index 7fc340e574cf..000000000000
--- a/drivers/staging/irda/net/irnetlink.c
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * IrDA netlink layer, for stack configuration.
- *
- * Copyright (c) 2007 Samuel Ortiz <samuel@sortiz.org>
- *
- * Partly based on the 802.11 nelink implementation
- * (see net/wireless/nl80211.c) which is:
- * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * 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.
- *
- */
-
-#include <linux/socket.h>
-#include <linux/irda.h>
-#include <linux/gfp.h>
-#include <net/net_namespace.h>
-#include <net/sock.h>
-#include <net/irda/irda.h>
-#include <net/irda/irlap.h>
-#include <net/genetlink.h>
-
-
-
-static struct genl_family irda_nl_family;
-
-static struct net_device * ifname_to_netdev(struct net *net, struct genl_info *info)
-{
- char * ifname;
-
- if (!info->attrs[IRDA_NL_ATTR_IFNAME])
- return NULL;
-
- ifname = nla_data(info->attrs[IRDA_NL_ATTR_IFNAME]);
-
- pr_debug("%s(): Looking for %s\n", __func__, ifname);
-
- return dev_get_by_name(net, ifname);
-}
-
-static int irda_nl_set_mode(struct sk_buff *skb, struct genl_info *info)
-{
- struct net_device * dev;
- struct irlap_cb * irlap;
- u32 mode;
-
- if (!info->attrs[IRDA_NL_ATTR_MODE])
- return -EINVAL;
-
- mode = nla_get_u32(info->attrs[IRDA_NL_ATTR_MODE]);
-
- pr_debug("%s(): Switching to mode: %d\n", __func__, mode);
-
- dev = ifname_to_netdev(&init_net, info);
- if (!dev)
- return -ENODEV;
-
- irlap = (struct irlap_cb *)dev->atalk_ptr;
- if (!irlap) {
- dev_put(dev);
- return -ENODEV;
- }
-
- irlap->mode = mode;
-
- dev_put(dev);
-
- return 0;
-}
-
-static int irda_nl_get_mode(struct sk_buff *skb, struct genl_info *info)
-{
- struct net_device * dev;
- struct irlap_cb * irlap;
- struct sk_buff *msg;
- void *hdr;
- int ret = -ENOBUFS;
-
- dev = ifname_to_netdev(&init_net, info);
- if (!dev)
- return -ENODEV;
-
- msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
- if (!msg) {
- dev_put(dev);
- return -ENOMEM;
- }
-
- irlap = (struct irlap_cb *)dev->atalk_ptr;
- if (!irlap) {
- ret = -ENODEV;
- goto err_out;
- }
-
- hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq,
- &irda_nl_family, 0, IRDA_NL_CMD_GET_MODE);
- if (hdr == NULL) {
- ret = -EMSGSIZE;
- goto err_out;
- }
-
- if(nla_put_string(msg, IRDA_NL_ATTR_IFNAME,
- dev->name))
- goto err_out;
-
- if(nla_put_u32(msg, IRDA_NL_ATTR_MODE, irlap->mode))
- goto err_out;
-
- genlmsg_end(msg, hdr);
-
- return genlmsg_reply(msg, info);
-
- err_out:
- nlmsg_free(msg);
- dev_put(dev);
-
- return ret;
-}
-
-static const struct nla_policy irda_nl_policy[IRDA_NL_ATTR_MAX + 1] = {
- [IRDA_NL_ATTR_IFNAME] = { .type = NLA_NUL_STRING,
- .len = IFNAMSIZ-1 },
- [IRDA_NL_ATTR_MODE] = { .type = NLA_U32 },
-};
-
-static const struct genl_ops irda_nl_ops[] = {
- {
- .cmd = IRDA_NL_CMD_SET_MODE,
- .doit = irda_nl_set_mode,
- .policy = irda_nl_policy,
- .flags = GENL_ADMIN_PERM,
- },
- {
- .cmd = IRDA_NL_CMD_GET_MODE,
- .doit = irda_nl_get_mode,
- .policy = irda_nl_policy,
- /* can be retrieved by unprivileged users */
- },
-
-};
-
-static struct genl_family irda_nl_family __ro_after_init = {
- .name = IRDA_NL_NAME,
- .hdrsize = 0,
- .version = IRDA_NL_VERSION,
- .maxattr = IRDA_NL_CMD_MAX,
- .module = THIS_MODULE,
- .ops = irda_nl_ops,
- .n_ops = ARRAY_SIZE(irda_nl_ops),
-};
-
-int __init irda_nl_register(void)
-{
- return genl_register_family(&irda_nl_family);
-}
-
-void irda_nl_unregister(void)
-{
- genl_unregister_family(&irda_nl_family);
-}
diff --git a/drivers/staging/irda/net/irproc.c b/drivers/staging/irda/net/irproc.c
deleted file mode 100644
index 77cfdde9d82f..000000000000
--- a/drivers/staging/irda/net/irproc.c
+++ /dev/null
@@ -1,96 +0,0 @@
-/*********************************************************************
- *
- * Filename: irproc.c
- * Version: 1.0
- * Description: Various entries in the /proc file system
- * Status: Experimental.
- * Author: Thomas Davis, <ratbert@radiks.net>
- * Created at: Sat Feb 21 21:33:24 1998
- * Modified at: Sun Nov 14 08:54:54 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-1999, Dag Brattli <dagb@cs.uit.no>
- * Copyright (c) 1998, Thomas Davis, <ratbert@radiks.net>,
- * All Rights Reserved.
- *
- * 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.
- *
- * I, Thomas Davis, provide no warranty for any of this software.
- * This material is provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <net/net_namespace.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irlap.h>
-#include <net/irda/irlmp.h>
-
-extern const struct file_operations discovery_seq_fops;
-extern const struct file_operations irlap_seq_fops;
-extern const struct file_operations irlmp_seq_fops;
-extern const struct file_operations irttp_seq_fops;
-extern const struct file_operations irias_seq_fops;
-
-struct irda_entry {
- const char *name;
- const struct file_operations *fops;
-};
-
-struct proc_dir_entry *proc_irda;
-EXPORT_SYMBOL(proc_irda);
-
-static const struct irda_entry irda_dirs[] = {
- {"discovery", &discovery_seq_fops},
- {"irttp", &irttp_seq_fops},
- {"irlmp", &irlmp_seq_fops},
- {"irlap", &irlap_seq_fops},
- {"irias", &irias_seq_fops},
-};
-
-/*
- * Function irda_proc_register (void)
- *
- * Register irda entry in /proc file system
- *
- */
-void __init irda_proc_register(void)
-{
- int i;
-
- proc_irda = proc_mkdir("irda", init_net.proc_net);
- if (proc_irda == NULL)
- return;
-
- for (i = 0; i < ARRAY_SIZE(irda_dirs); i++)
- (void) proc_create(irda_dirs[i].name, 0, proc_irda,
- irda_dirs[i].fops);
-}
-
-/*
- * Function irda_proc_unregister (void)
- *
- * Unregister irda entry in /proc file system
- *
- */
-void irda_proc_unregister(void)
-{
- int i;
-
- if (proc_irda) {
- for (i=0; i<ARRAY_SIZE(irda_dirs); i++)
- remove_proc_entry(irda_dirs[i].name, proc_irda);
-
- remove_proc_entry("irda", init_net.proc_net);
- proc_irda = NULL;
- }
-}
-
-
diff --git a/drivers/staging/irda/net/irqueue.c b/drivers/staging/irda/net/irqueue.c
deleted file mode 100644
index 14291cbc4097..000000000000
--- a/drivers/staging/irda/net/irqueue.c
+++ /dev/null
@@ -1,912 +0,0 @@
-/*********************************************************************
- *
- * Filename: irqueue.c
- * Version: 0.3
- * Description: General queue implementation
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Tue Jun 9 13:29:31 1998
- * Modified at: Sun Dec 12 13:48:22 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- * Modified at: Thu Jan 4 14:29:10 CET 2001
- * Modified by: Marc Zyngier <mzyngier@freesurf.fr>
- *
- * Copyright (C) 1998-1999, Aage Kvalnes <aage@cs.uit.no>
- * Copyright (C) 1998, Dag Brattli,
- * All Rights Reserved.
- *
- * This code is taken from the Vortex Operating System written by Aage
- * Kvalnes. Aage has agreed that this code can use the GPL licence,
- * although he does not use that licence in his own code.
- *
- * This copyright does however _not_ include the ELF hash() function
- * which I currently don't know which licence or copyright it
- * has. Please inform me if you know.
- *
- * 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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-/*
- * NOTE :
- * There are various problems with this package :
- * o the hash function for ints is pathetic (but could be changed)
- * o locking is sometime suspicious (especially during enumeration)
- * o most users have only a few elements (== overhead)
- * o most users never use search, so don't benefit from hashing
- * Problem already fixed :
- * o not 64 bit compliant (most users do hashv = (int) self)
- * o hashbin_remove() is broken => use hashbin_remove_this()
- * I think most users would be better served by a simple linked list
- * (like include/linux/list.h) with a global spinlock per list.
- * Jean II
- */
-
-/*
- * Notes on the concurrent access to hashbin and other SMP issues
- * -------------------------------------------------------------
- * Hashbins are very often in the IrDA stack a global repository of
- * information, and therefore used in a very asynchronous manner following
- * various events (driver calls, timers, user calls...).
- * Therefore, very often it is highly important to consider the
- * management of concurrent access to the hashbin and how to guarantee the
- * consistency of the operations on it.
- *
- * First, we need to define the objective of locking :
- * 1) Protect user data (content pointed by the hashbin)
- * 2) Protect hashbin structure itself (linked list in each bin)
- *
- * OLD LOCKING
- * -----------
- *
- * The previous locking strategy, either HB_LOCAL or HB_GLOBAL were
- * both inadequate in *both* aspect.
- * o HB_GLOBAL was using a spinlock for each bin (local locking).
- * o HB_LOCAL was disabling irq on *all* CPUs, so use a single
- * global semaphore.
- * The problems were :
- * A) Global irq disabling is no longer supported by the kernel
- * B) No protection for the hashbin struct global data
- * o hashbin_delete()
- * o hb_current
- * C) No protection for user data in some cases
- *
- * A) HB_LOCAL use global irq disabling, so doesn't work on kernel
- * 2.5.X. Even when it is supported (kernel 2.4.X and earlier), its
- * performance is not satisfactory on SMP setups. Most hashbins were
- * HB_LOCAL, so (A) definitely need fixing.
- * B) HB_LOCAL could be modified to fix (B). However, because HB_GLOBAL
- * lock only the individual bins, it will never be able to lock the
- * global data, so can't do (B).
- * C) Some functions return pointer to data that is still in the
- * hashbin :
- * o hashbin_find()
- * o hashbin_get_first()
- * o hashbin_get_next()
- * As the data is still in the hashbin, it may be changed or free'd
- * while the caller is examinimg the data. In those case, locking can't
- * be done within the hashbin, but must include use of the data within
- * the caller.
- * The caller can easily do this with HB_LOCAL (just disable irqs).
- * However, this is impossible with HB_GLOBAL because the caller has no
- * way to know the proper bin, so don't know which spinlock to use.
- *
- * Quick summary : can no longer use HB_LOCAL, and HB_GLOBAL is
- * fundamentally broken and will never work.
- *
- * NEW LOCKING
- * -----------
- *
- * To fix those problems, I've introduce a few changes in the
- * hashbin locking :
- * 1) New HB_LOCK scheme
- * 2) hashbin->hb_spinlock
- * 3) New hashbin usage policy
- *
- * HB_LOCK :
- * -------
- * HB_LOCK is a locking scheme intermediate between the old HB_LOCAL
- * and HB_GLOBAL. It uses a single spinlock to protect the whole content
- * of the hashbin. As it is a single spinlock, it can protect the global
- * data of the hashbin and not only the bins themselves.
- * HB_LOCK can only protect some of the hashbin calls, so it only lock
- * call that can be made 100% safe and leave other call unprotected.
- * HB_LOCK in theory is slower than HB_GLOBAL, but as the hashbin
- * content is always small contention is not high, so it doesn't matter
- * much. HB_LOCK is probably faster than HB_LOCAL.
- *
- * hashbin->hb_spinlock :
- * --------------------
- * The spinlock that HB_LOCK uses is available for caller, so that
- * the caller can protect unprotected calls (see below).
- * If the caller want to do entirely its own locking (HB_NOLOCK), he
- * can do so and may use safely this spinlock.
- * Locking is done like this :
- * spin_lock_irqsave(&hashbin->hb_spinlock, flags);
- * Releasing the lock :
- * spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
- *
- * Safe & Protected calls :
- * ----------------------
- * The following calls are safe or protected via HB_LOCK :
- * o hashbin_new() -> safe
- * o hashbin_delete()
- * o hashbin_insert()
- * o hashbin_remove_first()
- * o hashbin_remove()
- * o hashbin_remove_this()
- * o HASHBIN_GET_SIZE() -> atomic
- *
- * The following calls only protect the hashbin itself :
- * o hashbin_lock_find()
- * o hashbin_find_next()
- *
- * Unprotected calls :
- * -----------------
- * The following calls need to be protected by the caller :
- * o hashbin_find()
- * o hashbin_get_first()
- * o hashbin_get_next()
- *
- * Locking Policy :
- * --------------
- * If the hashbin is used only in a single thread of execution
- * (explicitly or implicitely), you can use HB_NOLOCK
- * If the calling module already provide concurrent access protection,
- * you may use HB_NOLOCK.
- *
- * In all other cases, you need to use HB_LOCK and lock the hashbin
- * every time before calling one of the unprotected calls. You also must
- * use the pointer returned by the unprotected call within the locked
- * region.
- *
- * Extra care for enumeration :
- * --------------------------
- * hashbin_get_first() and hashbin_get_next() use the hashbin to
- * store the current position, in hb_current.
- * As long as the hashbin remains locked, this is safe. If you unlock
- * the hashbin, the current position may change if anybody else modify
- * or enumerate the hashbin.
- * Summary : do the full enumeration while locked.
- *
- * Alternatively, you may use hashbin_find_next(). But, this will
- * be slower, is more complex to use and doesn't protect the hashbin
- * content. So, care is needed here as well.
- *
- * Other issues :
- * ------------
- * I believe that we are overdoing it by using spin_lock_irqsave()
- * and we should use only spin_lock_bh() or similar. But, I don't have
- * the balls to try it out.
- * Don't believe that because hashbin are now (somewhat) SMP safe
- * that the rest of the code is. Higher layers tend to be safest,
- * but LAP and LMP would need some serious dedicated love.
- *
- * Jean II
- */
-#include <linux/module.h>
-#include <linux/slab.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irqueue.h>
-
-/************************ QUEUE SUBROUTINES ************************/
-
-/*
- * Hashbin
- */
-#define GET_HASHBIN(x) ( x & HASHBIN_MASK )
-
-/*
- * Function hash (name)
- *
- * This function hash the input string 'name' using the ELF hash
- * function for strings.
- */
-static __u32 hash( const char* name)
-{
- __u32 h = 0;
- __u32 g;
-
- while(*name) {
- h = (h<<4) + *name++;
- g = h & 0xf0000000;
- if (g)
- h ^=g>>24;
- h &=~g;
- }
- return h;
-}
-
-/*
- * Function enqueue_first (queue, proc)
- *
- * Insert item first in queue.
- *
- */
-static void enqueue_first(irda_queue_t **queue, irda_queue_t* element)
-{
-
- /*
- * Check if queue is empty.
- */
- if ( *queue == NULL ) {
- /*
- * Queue is empty. Insert one element into the queue.
- */
- element->q_next = element->q_prev = *queue = element;
-
- } else {
- /*
- * Queue is not empty. Insert element into front of queue.
- */
- element->q_next = (*queue);
- (*queue)->q_prev->q_next = element;
- element->q_prev = (*queue)->q_prev;
- (*queue)->q_prev = element;
- (*queue) = element;
- }
-}
-
-
-/*
- * Function dequeue (queue)
- *
- * Remove first entry in queue
- *
- */
-static irda_queue_t *dequeue_first(irda_queue_t **queue)
-{
- irda_queue_t *ret;
-
- pr_debug("dequeue_first()\n");
-
- /*
- * Set return value
- */
- ret = *queue;
-
- if ( *queue == NULL ) {
- /*
- * Queue was empty.
- */
- } else if ( (*queue)->q_next == *queue ) {
- /*
- * Queue only contained a single element. It will now be
- * empty.
- */
- *queue = NULL;
- } else {
- /*
- * Queue contained several element. Remove the first one.
- */
- (*queue)->q_prev->q_next = (*queue)->q_next;
- (*queue)->q_next->q_prev = (*queue)->q_prev;
- *queue = (*queue)->q_next;
- }
-
- /*
- * Return the removed entry (or NULL of queue was empty).
- */
- return ret;
-}
-
-/*
- * Function dequeue_general (queue, element)
- *
- *
- */
-static irda_queue_t *dequeue_general(irda_queue_t **queue, irda_queue_t* element)
-{
- irda_queue_t *ret;
-
- pr_debug("dequeue_general()\n");
-
- /*
- * Set return value
- */
- ret = *queue;
-
- if ( *queue == NULL ) {
- /*
- * Queue was empty.
- */
- } else if ( (*queue)->q_next == *queue ) {
- /*
- * Queue only contained a single element. It will now be
- * empty.
- */
- *queue = NULL;
-
- } else {
- /*
- * Remove specific element.
- */
- element->q_prev->q_next = element->q_next;
- element->q_next->q_prev = element->q_prev;
- if ( (*queue) == element)
- (*queue) = element->q_next;
- }
-
- /*
- * Return the removed entry (or NULL of queue was empty).
- */
- return ret;
-}
-
-/************************ HASHBIN MANAGEMENT ************************/
-
-/*
- * Function hashbin_create ( type, name )
- *
- * Create hashbin!
- *
- */
-hashbin_t *hashbin_new(int type)
-{
- hashbin_t* hashbin;
-
- /*
- * Allocate new hashbin
- */
- hashbin = kzalloc(sizeof(*hashbin), GFP_ATOMIC);
- if (!hashbin)
- return NULL;
-
- /*
- * Initialize structure
- */
- hashbin->hb_type = type;
- hashbin->magic = HB_MAGIC;
- //hashbin->hb_current = NULL;
-
- /* Make sure all spinlock's are unlocked */
- if ( hashbin->hb_type & HB_LOCK ) {
- spin_lock_init(&hashbin->hb_spinlock);
- }
-
- return hashbin;
-}
-EXPORT_SYMBOL(hashbin_new);
-
-
-/*
- * Function hashbin_delete (hashbin, free_func)
- *
- * Destroy hashbin, the free_func can be a user supplied special routine
- * for deallocating this structure if it's complex. If not the user can
- * just supply kfree, which should take care of the job.
- */
-int hashbin_delete( hashbin_t* hashbin, FREE_FUNC free_func)
-{
- irda_queue_t* queue;
- unsigned long flags = 0;
- int i;
-
- IRDA_ASSERT(hashbin != NULL, return -1;);
- IRDA_ASSERT(hashbin->magic == HB_MAGIC, return -1;);
-
- /* Synchronize */
- if (hashbin->hb_type & HB_LOCK)
- spin_lock_irqsave(&hashbin->hb_spinlock, flags);
-
- /*
- * Free the entries in the hashbin, TODO: use hashbin_clear when
- * it has been shown to work
- */
- for (i = 0; i < HASHBIN_SIZE; i ++ ) {
- while (1) {
- queue = dequeue_first((irda_queue_t**) &hashbin->hb_queue[i]);
-
- if (!queue)
- break;
-
- if (free_func) {
- if (hashbin->hb_type & HB_LOCK)
- spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
- free_func(queue);
- if (hashbin->hb_type & HB_LOCK)
- spin_lock_irqsave(&hashbin->hb_spinlock, flags);
- }
- }
- }
-
- /* Cleanup local data */
- hashbin->hb_current = NULL;
- hashbin->magic = ~HB_MAGIC;
-
- /* Release lock */
- if (hashbin->hb_type & HB_LOCK)
- spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
-
- /*
- * Free the hashbin structure
- */
- kfree(hashbin);
-
- return 0;
-}
-EXPORT_SYMBOL(hashbin_delete);
-
-/********************* HASHBIN LIST OPERATIONS *********************/
-
-/*
- * Function hashbin_insert (hashbin, entry, name)
- *
- * Insert an entry into the hashbin
- *
- */
-void hashbin_insert(hashbin_t* hashbin, irda_queue_t* entry, long hashv,
- const char* name)
-{
- unsigned long flags = 0;
- int bin;
-
- IRDA_ASSERT( hashbin != NULL, return;);
- IRDA_ASSERT( hashbin->magic == HB_MAGIC, return;);
-
- /*
- * Locate hashbin
- */
- if ( name )
- hashv = hash( name );
- bin = GET_HASHBIN( hashv );
-
- /* Synchronize */
- if ( hashbin->hb_type & HB_LOCK ) {
- spin_lock_irqsave(&hashbin->hb_spinlock, flags);
- } /* Default is no-lock */
-
- /*
- * Store name and key
- */
- entry->q_hash = hashv;
- if ( name )
- strlcpy( entry->q_name, name, sizeof(entry->q_name));
-
- /*
- * Insert new entry first
- */
- enqueue_first( (irda_queue_t**) &hashbin->hb_queue[ bin ],
- entry);
- hashbin->hb_size++;
-
- /* Release lock */
- if ( hashbin->hb_type & HB_LOCK ) {
- spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
- } /* Default is no-lock */
-}
-EXPORT_SYMBOL(hashbin_insert);
-
-/*
- * Function hashbin_remove_first (hashbin)
- *
- * Remove first entry of the hashbin
- *
- * Note : this function no longer use hashbin_remove(), but does things
- * similar to hashbin_remove_this(), so can be considered safe.
- * Jean II
- */
-void *hashbin_remove_first( hashbin_t *hashbin)
-{
- unsigned long flags = 0;
- irda_queue_t *entry = NULL;
-
- /* Synchronize */
- if ( hashbin->hb_type & HB_LOCK ) {
- spin_lock_irqsave(&hashbin->hb_spinlock, flags);
- } /* Default is no-lock */
-
- entry = hashbin_get_first( hashbin);
- if ( entry != NULL) {
- int bin;
- long hashv;
- /*
- * Locate hashbin
- */
- hashv = entry->q_hash;
- bin = GET_HASHBIN( hashv );
-
- /*
- * Dequeue the entry...
- */
- dequeue_general( (irda_queue_t**) &hashbin->hb_queue[ bin ],
- entry);
- hashbin->hb_size--;
- entry->q_next = NULL;
- entry->q_prev = NULL;
-
- /*
- * Check if this item is the currently selected item, and in
- * that case we must reset hb_current
- */
- if ( entry == hashbin->hb_current)
- hashbin->hb_current = NULL;
- }
-
- /* Release lock */
- if ( hashbin->hb_type & HB_LOCK ) {
- spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
- } /* Default is no-lock */
-
- return entry;
-}
-
-
-/*
- * Function hashbin_remove (hashbin, hashv, name)
- *
- * Remove entry with the given name
- *
- * The use of this function is highly discouraged, because the whole
- * concept behind hashbin_remove() is broken. In many cases, it's not
- * possible to guarantee the unicity of the index (either hashv or name),
- * leading to removing the WRONG entry.
- * The only simple safe use is :
- * hashbin_remove(hasbin, (int) self, NULL);
- * In other case, you must think hard to guarantee unicity of the index.
- * Jean II
- */
-void* hashbin_remove( hashbin_t* hashbin, long hashv, const char* name)
-{
- int bin, found = FALSE;
- unsigned long flags = 0;
- irda_queue_t* entry;
-
- IRDA_ASSERT( hashbin != NULL, return NULL;);
- IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
-
- /*
- * Locate hashbin
- */
- if ( name )
- hashv = hash( name );
- bin = GET_HASHBIN( hashv );
-
- /* Synchronize */
- if ( hashbin->hb_type & HB_LOCK ) {
- spin_lock_irqsave(&hashbin->hb_spinlock, flags);
- } /* Default is no-lock */
-
- /*
- * Search for entry
- */
- entry = hashbin->hb_queue[ bin ];
- if ( entry ) {
- do {
- /*
- * Check for key
- */
- if ( entry->q_hash == hashv ) {
- /*
- * Name compare too?
- */
- if ( name ) {
- if ( strcmp( entry->q_name, name) == 0)
- {
- found = TRUE;
- break;
- }
- } else {
- found = TRUE;
- break;
- }
- }
- entry = entry->q_next;
- } while ( entry != hashbin->hb_queue[ bin ] );
- }
-
- /*
- * If entry was found, dequeue it
- */
- if ( found ) {
- dequeue_general( (irda_queue_t**) &hashbin->hb_queue[ bin ],
- entry);
- hashbin->hb_size--;
-
- /*
- * Check if this item is the currently selected item, and in
- * that case we must reset hb_current
- */
- if ( entry == hashbin->hb_current)
- hashbin->hb_current = NULL;
- }
-
- /* Release lock */
- if ( hashbin->hb_type & HB_LOCK ) {
- spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
- } /* Default is no-lock */
-
-
- /* Return */
- if ( found )
- return entry;
- else
- return NULL;
-
-}
-EXPORT_SYMBOL(hashbin_remove);
-
-/*
- * Function hashbin_remove_this (hashbin, entry)
- *
- * Remove entry with the given name
- *
- * In some cases, the user of hashbin can't guarantee the unicity
- * of either the hashv or name.
- * In those cases, using the above function is guaranteed to cause troubles,
- * so we use this one instead...
- * And by the way, it's also faster, because we skip the search phase ;-)
- */
-void* hashbin_remove_this( hashbin_t* hashbin, irda_queue_t* entry)
-{
- unsigned long flags = 0;
- int bin;
- long hashv;
-
- IRDA_ASSERT( hashbin != NULL, return NULL;);
- IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
- IRDA_ASSERT( entry != NULL, return NULL;);
-
- /* Synchronize */
- if ( hashbin->hb_type & HB_LOCK ) {
- spin_lock_irqsave(&hashbin->hb_spinlock, flags);
- } /* Default is no-lock */
-
- /* Check if valid and not already removed... */
- if((entry->q_next == NULL) || (entry->q_prev == NULL)) {
- entry = NULL;
- goto out;
- }
-
- /*
- * Locate hashbin
- */
- hashv = entry->q_hash;
- bin = GET_HASHBIN( hashv );
-
- /*
- * Dequeue the entry...
- */
- dequeue_general( (irda_queue_t**) &hashbin->hb_queue[ bin ],
- entry);
- hashbin->hb_size--;
- entry->q_next = NULL;
- entry->q_prev = NULL;
-
- /*
- * Check if this item is the currently selected item, and in
- * that case we must reset hb_current
- */
- if ( entry == hashbin->hb_current)
- hashbin->hb_current = NULL;
-out:
- /* Release lock */
- if ( hashbin->hb_type & HB_LOCK ) {
- spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
- } /* Default is no-lock */
-
- return entry;
-}
-EXPORT_SYMBOL(hashbin_remove_this);
-
-/*********************** HASHBIN ENUMERATION ***********************/
-
-/*
- * Function hashbin_common_find (hashbin, hashv, name)
- *
- * Find item with the given hashv or name
- *
- */
-void* hashbin_find( hashbin_t* hashbin, long hashv, const char* name )
-{
- int bin;
- irda_queue_t* entry;
-
- pr_debug("hashbin_find()\n");
-
- IRDA_ASSERT( hashbin != NULL, return NULL;);
- IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
-
- /*
- * Locate hashbin
- */
- if ( name )
- hashv = hash( name );
- bin = GET_HASHBIN( hashv );
-
- /*
- * Search for entry
- */
- entry = hashbin->hb_queue[ bin];
- if ( entry ) {
- do {
- /*
- * Check for key
- */
- if ( entry->q_hash == hashv ) {
- /*
- * Name compare too?
- */
- if ( name ) {
- if ( strcmp( entry->q_name, name ) == 0 ) {
- return entry;
- }
- } else {
- return entry;
- }
- }
- entry = entry->q_next;
- } while ( entry != hashbin->hb_queue[ bin ] );
- }
-
- return NULL;
-}
-EXPORT_SYMBOL(hashbin_find);
-
-/*
- * Function hashbin_lock_find (hashbin, hashv, name)
- *
- * Find item with the given hashv or name
- *
- * Same, but with spinlock protection...
- * I call it safe, but it's only safe with respect to the hashbin, not its
- * content. - Jean II
- */
-void* hashbin_lock_find( hashbin_t* hashbin, long hashv, const char* name )
-{
- unsigned long flags = 0;
- irda_queue_t* entry;
-
- /* Synchronize */
- spin_lock_irqsave(&hashbin->hb_spinlock, flags);
-
- /*
- * Search for entry
- */
- entry = hashbin_find(hashbin, hashv, name);
-
- /* Release lock */
- spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
-
- return entry;
-}
-EXPORT_SYMBOL(hashbin_lock_find);
-
-/*
- * Function hashbin_find (hashbin, hashv, name, pnext)
- *
- * Find an item with the given hashv or name, and its successor
- *
- * This function allow to do concurrent enumerations without the
- * need to lock over the whole session, because the caller keep the
- * context of the search. On the other hand, it might fail and return
- * NULL if the entry is removed. - Jean II
- */
-void* hashbin_find_next( hashbin_t* hashbin, long hashv, const char* name,
- void ** pnext)
-{
- unsigned long flags = 0;
- irda_queue_t* entry;
-
- /* Synchronize */
- spin_lock_irqsave(&hashbin->hb_spinlock, flags);
-
- /*
- * Search for current entry
- * This allow to check if the current item is still in the
- * hashbin or has been removed.
- */
- entry = hashbin_find(hashbin, hashv, name);
-
- /*
- * Trick hashbin_get_next() to return what we want
- */
- if(entry) {
- hashbin->hb_current = entry;
- *pnext = hashbin_get_next( hashbin );
- } else
- *pnext = NULL;
-
- /* Release lock */
- spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
-
- return entry;
-}
-
-/*
- * Function hashbin_get_first (hashbin)
- *
- * Get a pointer to first element in hashbin, this function must be
- * called before any calls to hashbin_get_next()!
- *
- */
-irda_queue_t *hashbin_get_first( hashbin_t* hashbin)
-{
- irda_queue_t *entry;
- int i;
-
- IRDA_ASSERT( hashbin != NULL, return NULL;);
- IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
-
- if ( hashbin == NULL)
- return NULL;
-
- for ( i = 0; i < HASHBIN_SIZE; i ++ ) {
- entry = hashbin->hb_queue[ i];
- if ( entry) {
- hashbin->hb_current = entry;
- return entry;
- }
- }
- /*
- * Did not find any item in hashbin
- */
- return NULL;
-}
-EXPORT_SYMBOL(hashbin_get_first);
-
-/*
- * Function hashbin_get_next (hashbin)
- *
- * Get next item in hashbin. A series of hashbin_get_next() calls must
- * be started by a call to hashbin_get_first(). The function returns
- * NULL when all items have been traversed
- *
- * The context of the search is stored within the hashbin, so you must
- * protect yourself from concurrent enumerations. - Jean II
- */
-irda_queue_t *hashbin_get_next( hashbin_t *hashbin)
-{
- irda_queue_t* entry;
- int bin;
- int i;
-
- IRDA_ASSERT( hashbin != NULL, return NULL;);
- IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
-
- if ( hashbin->hb_current == NULL) {
- IRDA_ASSERT( hashbin->hb_current != NULL, return NULL;);
- return NULL;
- }
- entry = hashbin->hb_current->q_next;
- bin = GET_HASHBIN( entry->q_hash);
-
- /*
- * Make sure that we are not back at the beginning of the queue
- * again
- */
- if ( entry != hashbin->hb_queue[ bin ]) {
- hashbin->hb_current = entry;
-
- return entry;
- }
-
- /*
- * Check that this is not the last queue in hashbin
- */
- if ( bin >= HASHBIN_SIZE)
- return NULL;
-
- /*
- * Move to next queue in hashbin
- */
- bin++;
- for ( i = bin; i < HASHBIN_SIZE; i++ ) {
- entry = hashbin->hb_queue[ i];
- if ( entry) {
- hashbin->hb_current = entry;
-
- return entry;
- }
- }
- return NULL;
-}
-EXPORT_SYMBOL(hashbin_get_next);
diff --git a/drivers/staging/irda/net/irsysctl.c b/drivers/staging/irda/net/irsysctl.c
deleted file mode 100644
index 873da5e7d428..000000000000
--- a/drivers/staging/irda/net/irsysctl.c
+++ /dev/null
@@ -1,258 +0,0 @@
-/*********************************************************************
- *
- * Filename: irsysctl.c
- * Version: 1.0
- * Description: Sysctl interface for IrDA
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sun May 24 22:12:06 1998
- * Modified at: Fri Jun 4 02:50:15 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1997, 1999 Dag Brattli, All Rights Reserved.
- * Copyright (c) 2000-2001 Jean Tourrilhes <jt@hpl.hp.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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/mm.h>
-#include <linux/ctype.h>
-#include <linux/sysctl.h>
-#include <linux/init.h>
-
-#include <net/irda/irda.h> /* irda_debug */
-#include <net/irda/irlmp.h>
-#include <net/irda/timer.h>
-#include <net/irda/irias_object.h>
-
-extern int sysctl_discovery;
-extern int sysctl_discovery_slots;
-extern int sysctl_discovery_timeout;
-extern int sysctl_slot_timeout;
-extern int sysctl_fast_poll_increase;
-extern char sysctl_devname[];
-extern int sysctl_max_baud_rate;
-extern unsigned int sysctl_min_tx_turn_time;
-extern unsigned int sysctl_max_tx_data_size;
-extern unsigned int sysctl_max_tx_window;
-extern int sysctl_max_noreply_time;
-extern int sysctl_warn_noreply_time;
-extern int sysctl_lap_keepalive_time;
-
-extern struct irlmp_cb *irlmp;
-
-/* this is needed for the proc_dointvec_minmax - Jean II */
-static int max_discovery_slots = 16; /* ??? */
-static int min_discovery_slots = 1;
-/* IrLAP 6.13.2 says 25ms to 10+70ms - allow higher since some devices
- * seems to require it. (from Dag's comment) */
-static int max_slot_timeout = 160;
-static int min_slot_timeout = 20;
-static int max_max_baud_rate = 16000000; /* See qos.c - IrLAP spec */
-static int min_max_baud_rate = 2400;
-static int max_min_tx_turn_time = 10000; /* See qos.c - IrLAP spec */
-static int min_min_tx_turn_time;
-static int max_max_tx_data_size = 2048; /* See qos.c - IrLAP spec */
-static int min_max_tx_data_size = 64;
-static int max_max_tx_window = 7; /* See qos.c - IrLAP spec */
-static int min_max_tx_window = 1;
-static int max_max_noreply_time = 40; /* See qos.c - IrLAP spec */
-static int min_max_noreply_time = 3;
-static int max_warn_noreply_time = 3; /* 3s == standard */
-static int min_warn_noreply_time = 1; /* 1s == min WD_TIMER */
-static int max_lap_keepalive_time = 10000; /* 10s */
-static int min_lap_keepalive_time = 100; /* 100us */
-/* For other sysctl, I've no idea of the range. Maybe Dag could help
- * us on that - Jean II */
-
-static int do_devname(struct ctl_table *table, int write,
- void __user *buffer, size_t *lenp, loff_t *ppos)
-{
- int ret;
-
- ret = proc_dostring(table, write, buffer, lenp, ppos);
- if (ret == 0 && write) {
- struct ias_value *val;
-
- val = irias_new_string_value(sysctl_devname);
- if (val)
- irias_object_change_attribute("Device", "DeviceName", val);
- }
- return ret;
-}
-
-
-static int do_discovery(struct ctl_table *table, int write,
- void __user *buffer, size_t *lenp, loff_t *ppos)
-{
- int ret;
-
- ret = proc_dointvec(table, write, buffer, lenp, ppos);
- if (ret)
- return ret;
-
- if (irlmp == NULL)
- return -ENODEV;
-
- if (sysctl_discovery)
- irlmp_start_discovery_timer(irlmp, sysctl_discovery_timeout*HZ);
- else
- del_timer_sync(&irlmp->discovery_timer);
-
- return ret;
-}
-
-/* One file */
-static struct ctl_table irda_table[] = {
- {
- .procname = "discovery",
- .data = &sysctl_discovery,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = do_discovery,
- },
- {
- .procname = "devname",
- .data = sysctl_devname,
- .maxlen = 65,
- .mode = 0644,
- .proc_handler = do_devname,
- },
-#ifdef CONFIG_IRDA_FAST_RR
- {
- .procname = "fast_poll_increase",
- .data = &sysctl_fast_poll_increase,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec
- },
-#endif
- {
- .procname = "discovery_slots",
- .data = &sysctl_discovery_slots,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = &min_discovery_slots,
- .extra2 = &max_discovery_slots
- },
- {
- .procname = "discovery_timeout",
- .data = &sysctl_discovery_timeout,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec
- },
- {
- .procname = "slot_timeout",
- .data = &sysctl_slot_timeout,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = &min_slot_timeout,
- .extra2 = &max_slot_timeout
- },
- {
- .procname = "max_baud_rate",
- .data = &sysctl_max_baud_rate,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = &min_max_baud_rate,
- .extra2 = &max_max_baud_rate
- },
- {
- .procname = "min_tx_turn_time",
- .data = &sysctl_min_tx_turn_time,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = &min_min_tx_turn_time,
- .extra2 = &max_min_tx_turn_time
- },
- {
- .procname = "max_tx_data_size",
- .data = &sysctl_max_tx_data_size,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = &min_max_tx_data_size,
- .extra2 = &max_max_tx_data_size
- },
- {
- .procname = "max_tx_window",
- .data = &sysctl_max_tx_window,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = &min_max_tx_window,
- .extra2 = &max_max_tx_window
- },
- {
- .procname = "max_noreply_time",
- .data = &sysctl_max_noreply_time,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = &min_max_noreply_time,
- .extra2 = &max_max_noreply_time
- },
- {
- .procname = "warn_noreply_time",
- .data = &sysctl_warn_noreply_time,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = &min_warn_noreply_time,
- .extra2 = &max_warn_noreply_time
- },
- {
- .procname = "lap_keepalive_time",
- .data = &sysctl_lap_keepalive_time,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = &min_lap_keepalive_time,
- .extra2 = &max_lap_keepalive_time
- },
- { }
-};
-
-static struct ctl_table_header *irda_table_header;
-
-/*
- * Function irda_sysctl_register (void)
- *
- * Register our sysctl interface
- *
- */
-int __init irda_sysctl_register(void)
-{
- irda_table_header = register_net_sysctl(&init_net, "net/irda", irda_table);
- if (!irda_table_header)
- return -ENOMEM;
-
- return 0;
-}
-
-/*
- * Function irda_sysctl_unregister (void)
- *
- * Unregister our sysctl interface
- *
- */
-void irda_sysctl_unregister(void)
-{
- unregister_net_sysctl_table(irda_table_header);
-}
-
-
-
diff --git a/drivers/staging/irda/net/irttp.c b/drivers/staging/irda/net/irttp.c
deleted file mode 100644
index 741a94f39b4e..000000000000
--- a/drivers/staging/irda/net/irttp.c
+++ /dev/null
@@ -1,1886 +0,0 @@
-/*********************************************************************
- *
- * Filename: irttp.c
- * Version: 1.2
- * Description: Tiny Transport Protocol (TTP) implementation
- * Status: Stable
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sun Aug 31 20:14:31 1997
- * Modified at: Wed Jan 5 11:31:27 2000
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>,
- * All Rights Reserved.
- * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/skbuff.h>
-#include <linux/init.h>
-#include <linux/fs.h>
-#include <linux/seq_file.h>
-#include <linux/slab.h>
-#include <linux/export.h>
-
-#include <asm/byteorder.h>
-#include <asm/unaligned.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irlap.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/parameters.h>
-#include <net/irda/irttp.h>
-
-static struct irttp_cb *irttp;
-
-static void __irttp_close_tsap(struct tsap_cb *self);
-
-static int irttp_data_indication(void *instance, void *sap,
- struct sk_buff *skb);
-static int irttp_udata_indication(void *instance, void *sap,
- struct sk_buff *skb);
-static void irttp_disconnect_indication(void *instance, void *sap,
- LM_REASON reason, struct sk_buff *);
-static void irttp_connect_indication(void *instance, void *sap,
- struct qos_info *qos, __u32 max_sdu_size,
- __u8 header_size, struct sk_buff *skb);
-static void irttp_connect_confirm(void *instance, void *sap,
- struct qos_info *qos, __u32 max_sdu_size,
- __u8 header_size, struct sk_buff *skb);
-static void irttp_run_tx_queue(struct tsap_cb *self);
-static void irttp_run_rx_queue(struct tsap_cb *self);
-
-static void irttp_flush_queues(struct tsap_cb *self);
-static void irttp_fragment_skb(struct tsap_cb *self, struct sk_buff *skb);
-static struct sk_buff *irttp_reassemble_skb(struct tsap_cb *self);
-static int irttp_param_max_sdu_size(void *instance, irda_param_t *param,
- int get);
-
-static void irttp_flow_indication(void *instance, void *sap, LOCAL_FLOW flow);
-static void irttp_status_indication(void *instance,
- LINK_STATUS link, LOCK_STATUS lock);
-
-/* Information for parsing parameters in IrTTP */
-static const pi_minor_info_t pi_minor_call_table[] = {
- { NULL, 0 }, /* 0x00 */
- { irttp_param_max_sdu_size, PV_INTEGER | PV_BIG_ENDIAN } /* 0x01 */
-};
-static const pi_major_info_t pi_major_call_table[] = {
- { pi_minor_call_table, 2 }
-};
-static pi_param_info_t param_info = { pi_major_call_table, 1, 0x0f, 4 };
-
-/************************ GLOBAL PROCEDURES ************************/
-
-/*
- * Function irttp_init (void)
- *
- * Initialize the IrTTP layer. Called by module initialization code
- *
- */
-int __init irttp_init(void)
-{
- irttp = kzalloc(sizeof(struct irttp_cb), GFP_KERNEL);
- if (irttp == NULL)
- return -ENOMEM;
-
- irttp->magic = TTP_MAGIC;
-
- irttp->tsaps = hashbin_new(HB_LOCK);
- if (!irttp->tsaps) {
- net_err_ratelimited("%s: can't allocate IrTTP hashbin!\n",
- __func__);
- kfree(irttp);
- return -ENOMEM;
- }
-
- return 0;
-}
-
-/*
- * Function irttp_cleanup (void)
- *
- * Called by module destruction/cleanup code
- *
- */
-void irttp_cleanup(void)
-{
- /* Check for main structure */
- IRDA_ASSERT(irttp->magic == TTP_MAGIC, return;);
-
- /*
- * Delete hashbin and close all TSAP instances in it
- */
- hashbin_delete(irttp->tsaps, (FREE_FUNC) __irttp_close_tsap);
-
- irttp->magic = 0;
-
- /* De-allocate main structure */
- kfree(irttp);
-
- irttp = NULL;
-}
-
-/*************************** SUBROUTINES ***************************/
-
-/*
- * Function irttp_start_todo_timer (self, timeout)
- *
- * Start todo timer.
- *
- * Made it more effient and unsensitive to race conditions - Jean II
- */
-static inline void irttp_start_todo_timer(struct tsap_cb *self, int timeout)
-{
- /* Set new value for timer */
- mod_timer(&self->todo_timer, jiffies + timeout);
-}
-
-/*
- * Function irttp_todo_expired (data)
- *
- * Todo timer has expired!
- *
- * One of the restriction of the timer is that it is run only on the timer
- * interrupt which run every 10ms. This mean that even if you set the timer
- * with a delay of 0, it may take up to 10ms before it's run.
- * So, to minimise latency and keep cache fresh, we try to avoid using
- * it as much as possible.
- * Note : we can't use tasklets, because they can't be asynchronously
- * killed (need user context), and we can't guarantee that here...
- * Jean II
- */
-static void irttp_todo_expired(struct timer_list *t)
-{
- struct tsap_cb *self = from_timer(self, t, todo_timer);
-
- /* Check that we still exist */
- if (!self || self->magic != TTP_TSAP_MAGIC)
- return;
-
- pr_debug("%s(instance=%p)\n", __func__, self);
-
- /* Try to make some progress, especially on Tx side - Jean II */
- irttp_run_rx_queue(self);
- irttp_run_tx_queue(self);
-
- /* Check if time for disconnect */
- if (test_bit(0, &self->disconnect_pend)) {
- /* Check if it's possible to disconnect yet */
- if (skb_queue_empty(&self->tx_queue)) {
- /* Make sure disconnect is not pending anymore */
- clear_bit(0, &self->disconnect_pend); /* FALSE */
-
- /* Note : self->disconnect_skb may be NULL */
- irttp_disconnect_request(self, self->disconnect_skb,
- P_NORMAL);
- self->disconnect_skb = NULL;
- } else {
- /* Try again later */
- irttp_start_todo_timer(self, HZ/10);
-
- /* No reason to try and close now */
- return;
- }
- }
-
- /* Check if it's closing time */
- if (self->close_pend)
- /* Finish cleanup */
- irttp_close_tsap(self);
-}
-
-/*
- * Function irttp_flush_queues (self)
- *
- * Flushes (removes all frames) in transitt-buffer (tx_list)
- */
-static void irttp_flush_queues(struct tsap_cb *self)
-{
- struct sk_buff *skb;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
-
- /* Deallocate frames waiting to be sent */
- while ((skb = skb_dequeue(&self->tx_queue)) != NULL)
- dev_kfree_skb(skb);
-
- /* Deallocate received frames */
- while ((skb = skb_dequeue(&self->rx_queue)) != NULL)
- dev_kfree_skb(skb);
-
- /* Deallocate received fragments */
- while ((skb = skb_dequeue(&self->rx_fragments)) != NULL)
- dev_kfree_skb(skb);
-}
-
-/*
- * Function irttp_reassemble (self)
- *
- * Makes a new (continuous) skb of all the fragments in the fragment
- * queue
- *
- */
-static struct sk_buff *irttp_reassemble_skb(struct tsap_cb *self)
-{
- struct sk_buff *skb, *frag;
- int n = 0; /* Fragment index */
-
- IRDA_ASSERT(self != NULL, return NULL;);
- IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return NULL;);
-
- pr_debug("%s(), self->rx_sdu_size=%d\n", __func__,
- self->rx_sdu_size);
-
- skb = dev_alloc_skb(TTP_HEADER + self->rx_sdu_size);
- if (!skb)
- return NULL;
-
- /*
- * Need to reserve space for TTP header in case this skb needs to
- * be requeued in case delivery failes
- */
- skb_reserve(skb, TTP_HEADER);
- skb_put(skb, self->rx_sdu_size);
-
- /*
- * Copy all fragments to a new buffer
- */
- while ((frag = skb_dequeue(&self->rx_fragments)) != NULL) {
- skb_copy_to_linear_data_offset(skb, n, frag->data, frag->len);
- n += frag->len;
-
- dev_kfree_skb(frag);
- }
-
- pr_debug("%s(), frame len=%d, rx_sdu_size=%d, rx_max_sdu_size=%d\n",
- __func__, n, self->rx_sdu_size, self->rx_max_sdu_size);
- /* Note : irttp_run_rx_queue() calculate self->rx_sdu_size
- * by summing the size of all fragments, so we should always
- * have n == self->rx_sdu_size, except in cases where we
- * droped the last fragment (when self->rx_sdu_size exceed
- * self->rx_max_sdu_size), where n < self->rx_sdu_size.
- * Jean II */
- IRDA_ASSERT(n <= self->rx_sdu_size, n = self->rx_sdu_size;);
-
- /* Set the new length */
- skb_trim(skb, n);
-
- self->rx_sdu_size = 0;
-
- return skb;
-}
-
-/*
- * Function irttp_fragment_skb (skb)
- *
- * Fragments a frame and queues all the fragments for transmission
- *
- */
-static inline void irttp_fragment_skb(struct tsap_cb *self,
- struct sk_buff *skb)
-{
- struct sk_buff *frag;
- __u8 *frame;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
- IRDA_ASSERT(skb != NULL, return;);
-
- /*
- * Split frame into a number of segments
- */
- while (skb->len > self->max_seg_size) {
- pr_debug("%s(), fragmenting ...\n", __func__);
-
- /* Make new segment */
- frag = alloc_skb(self->max_seg_size+self->max_header_size,
- GFP_ATOMIC);
- if (!frag)
- return;
-
- skb_reserve(frag, self->max_header_size);
-
- /* Copy data from the original skb into this fragment. */
- skb_copy_from_linear_data(skb, skb_put(frag, self->max_seg_size),
- self->max_seg_size);
-
- /* Insert TTP header, with the more bit set */
- frame = skb_push(frag, TTP_HEADER);
- frame[0] = TTP_MORE;
-
- /* Hide the copied data from the original skb */
- skb_pull(skb, self->max_seg_size);
-
- /* Queue fragment */
- skb_queue_tail(&self->tx_queue, frag);
- }
- /* Queue what is left of the original skb */
- pr_debug("%s(), queuing last segment\n", __func__);
-
- frame = skb_push(skb, TTP_HEADER);
- frame[0] = 0x00; /* Clear more bit */
-
- /* Queue fragment */
- skb_queue_tail(&self->tx_queue, skb);
-}
-
-/*
- * Function irttp_param_max_sdu_size (self, param)
- *
- * Handle the MaxSduSize parameter in the connect frames, this function
- * will be called both when this parameter needs to be inserted into, and
- * extracted from the connect frames
- */
-static int irttp_param_max_sdu_size(void *instance, irda_param_t *param,
- int get)
-{
- struct tsap_cb *self;
-
- self = instance;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
-
- if (get)
- param->pv.i = self->tx_max_sdu_size;
- else
- self->tx_max_sdu_size = param->pv.i;
-
- pr_debug("%s(), MaxSduSize=%d\n", __func__, param->pv.i);
-
- return 0;
-}
-
-/*************************** CLIENT CALLS ***************************/
-/************************** LMP CALLBACKS **************************/
-/* Everything is happily mixed up. Waiting for next clean up - Jean II */
-
-/*
- * Initialization, that has to be done on new tsap
- * instance allocation and on duplication
- */
-static void irttp_init_tsap(struct tsap_cb *tsap)
-{
- spin_lock_init(&tsap->lock);
- timer_setup(&tsap->todo_timer, irttp_todo_expired, 0);
-
- skb_queue_head_init(&tsap->rx_queue);
- skb_queue_head_init(&tsap->tx_queue);
- skb_queue_head_init(&tsap->rx_fragments);
-}
-
-/*
- * Function irttp_open_tsap (stsap, notify)
- *
- * Create TSAP connection endpoint,
- */
-struct tsap_cb *irttp_open_tsap(__u8 stsap_sel, int credit, notify_t *notify)
-{
- struct tsap_cb *self;
- struct lsap_cb *lsap;
- notify_t ttp_notify;
-
- IRDA_ASSERT(irttp->magic == TTP_MAGIC, return NULL;);
-
- /* The IrLMP spec (IrLMP 1.1 p10) says that we have the right to
- * use only 0x01-0x6F. Of course, we can use LSAP_ANY as well.
- * JeanII */
- if ((stsap_sel != LSAP_ANY) &&
- ((stsap_sel < 0x01) || (stsap_sel >= 0x70))) {
- pr_debug("%s(), invalid tsap!\n", __func__);
- return NULL;
- }
-
- self = kzalloc(sizeof(struct tsap_cb), GFP_ATOMIC);
- if (self == NULL)
- return NULL;
-
- /* Initialize internal objects */
- irttp_init_tsap(self);
-
- /* Initialize callbacks for IrLMP to use */
- irda_notify_init(&ttp_notify);
- ttp_notify.connect_confirm = irttp_connect_confirm;
- ttp_notify.connect_indication = irttp_connect_indication;
- ttp_notify.disconnect_indication = irttp_disconnect_indication;
- ttp_notify.data_indication = irttp_data_indication;
- ttp_notify.udata_indication = irttp_udata_indication;
- ttp_notify.flow_indication = irttp_flow_indication;
- if (notify->status_indication != NULL)
- ttp_notify.status_indication = irttp_status_indication;
- ttp_notify.instance = self;
- strncpy(ttp_notify.name, notify->name, NOTIFY_MAX_NAME);
-
- self->magic = TTP_TSAP_MAGIC;
- self->connected = FALSE;
-
- /*
- * Create LSAP at IrLMP layer
- */
- lsap = irlmp_open_lsap(stsap_sel, &ttp_notify, 0);
- if (lsap == NULL) {
- pr_debug("%s: unable to allocate LSAP!!\n", __func__);
- __irttp_close_tsap(self);
- return NULL;
- }
-
- /*
- * If user specified LSAP_ANY as source TSAP selector, then IrLMP
- * will replace it with whatever source selector which is free, so
- * the stsap_sel we have might not be valid anymore
- */
- self->stsap_sel = lsap->slsap_sel;
- pr_debug("%s(), stsap_sel=%02x\n", __func__, self->stsap_sel);
-
- self->notify = *notify;
- self->lsap = lsap;
-
- hashbin_insert(irttp->tsaps, (irda_queue_t *) self, (long) self, NULL);
-
- if (credit > TTP_RX_MAX_CREDIT)
- self->initial_credit = TTP_RX_MAX_CREDIT;
- else
- self->initial_credit = credit;
-
- return self;
-}
-EXPORT_SYMBOL(irttp_open_tsap);
-
-/*
- * Function irttp_close (handle)
- *
- * Remove an instance of a TSAP. This function should only deal with the
- * deallocation of the TSAP, and resetting of the TSAPs values;
- *
- */
-static void __irttp_close_tsap(struct tsap_cb *self)
-{
- /* First make sure we're connected. */
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
-
- irttp_flush_queues(self);
-
- del_timer(&self->todo_timer);
-
- /* This one won't be cleaned up if we are disconnect_pend + close_pend
- * and we receive a disconnect_indication */
- if (self->disconnect_skb)
- dev_kfree_skb(self->disconnect_skb);
-
- self->connected = FALSE;
- self->magic = ~TTP_TSAP_MAGIC;
-
- kfree(self);
-}
-
-/*
- * Function irttp_close (self)
- *
- * Remove TSAP from list of all TSAPs and then deallocate all resources
- * associated with this TSAP
- *
- * Note : because we *free* the tsap structure, it is the responsibility
- * of the caller to make sure we are called only once and to deal with
- * possible race conditions. - Jean II
- */
-int irttp_close_tsap(struct tsap_cb *self)
-{
- struct tsap_cb *tsap;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
-
- /* Make sure tsap has been disconnected */
- if (self->connected) {
- /* Check if disconnect is not pending */
- if (!test_bit(0, &self->disconnect_pend)) {
- net_warn_ratelimited("%s: TSAP still connected!\n",
- __func__);
- irttp_disconnect_request(self, NULL, P_NORMAL);
- }
- self->close_pend = TRUE;
- irttp_start_todo_timer(self, HZ/10);
-
- return 0; /* Will be back! */
- }
-
- tsap = hashbin_remove(irttp->tsaps, (long) self, NULL);
-
- IRDA_ASSERT(tsap == self, return -1;);
-
- /* Close corresponding LSAP */
- if (self->lsap) {
- irlmp_close_lsap(self->lsap);
- self->lsap = NULL;
- }
-
- __irttp_close_tsap(self);
-
- return 0;
-}
-EXPORT_SYMBOL(irttp_close_tsap);
-
-/*
- * Function irttp_udata_request (self, skb)
- *
- * Send unreliable data on this TSAP
- *
- */
-int irttp_udata_request(struct tsap_cb *self, struct sk_buff *skb)
-{
- int ret;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
- IRDA_ASSERT(skb != NULL, return -1;);
-
- /* Take shortcut on zero byte packets */
- if (skb->len == 0) {
- ret = 0;
- goto err;
- }
-
- /* Check that nothing bad happens */
- if (!self->connected) {
- net_warn_ratelimited("%s(), Not connected\n", __func__);
- ret = -ENOTCONN;
- goto err;
- }
-
- if (skb->len > self->max_seg_size) {
- net_err_ratelimited("%s(), UData is too large for IrLAP!\n",
- __func__);
- ret = -EMSGSIZE;
- goto err;
- }
-
- irlmp_udata_request(self->lsap, skb);
- self->stats.tx_packets++;
-
- return 0;
-
-err:
- dev_kfree_skb(skb);
- return ret;
-}
-EXPORT_SYMBOL(irttp_udata_request);
-
-
-/*
- * Function irttp_data_request (handle, skb)
- *
- * Queue frame for transmission. If SAR is enabled, fragement the frame
- * and queue the fragments for transmission
- */
-int irttp_data_request(struct tsap_cb *self, struct sk_buff *skb)
-{
- __u8 *frame;
- int ret;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
- IRDA_ASSERT(skb != NULL, return -1;);
-
- pr_debug("%s() : queue len = %d\n", __func__,
- skb_queue_len(&self->tx_queue));
-
- /* Take shortcut on zero byte packets */
- if (skb->len == 0) {
- ret = 0;
- goto err;
- }
-
- /* Check that nothing bad happens */
- if (!self->connected) {
- net_warn_ratelimited("%s: Not connected\n", __func__);
- ret = -ENOTCONN;
- goto err;
- }
-
- /*
- * Check if SAR is disabled, and the frame is larger than what fits
- * inside an IrLAP frame
- */
- if ((self->tx_max_sdu_size == 0) && (skb->len > self->max_seg_size)) {
- net_err_ratelimited("%s: SAR disabled, and data is too large for IrLAP!\n",
- __func__);
- ret = -EMSGSIZE;
- goto err;
- }
-
- /*
- * Check if SAR is enabled, and the frame is larger than the
- * TxMaxSduSize
- */
- if ((self->tx_max_sdu_size != 0) &&
- (self->tx_max_sdu_size != TTP_SAR_UNBOUND) &&
- (skb->len > self->tx_max_sdu_size)) {
- net_err_ratelimited("%s: SAR enabled, but data is larger than TxMaxSduSize!\n",
- __func__);
- ret = -EMSGSIZE;
- goto err;
- }
- /*
- * Check if transmit queue is full
- */
- if (skb_queue_len(&self->tx_queue) >= TTP_TX_MAX_QUEUE) {
- /*
- * Give it a chance to empty itself
- */
- irttp_run_tx_queue(self);
-
- /* Drop packet. This error code should trigger the caller
- * to resend the data in the client code - Jean II */
- ret = -ENOBUFS;
- goto err;
- }
-
- /* Queue frame, or queue frame segments */
- if ((self->tx_max_sdu_size == 0) || (skb->len < self->max_seg_size)) {
- /* Queue frame */
- IRDA_ASSERT(skb_headroom(skb) >= TTP_HEADER, return -1;);
- frame = skb_push(skb, TTP_HEADER);
- frame[0] = 0x00; /* Clear more bit */
-
- skb_queue_tail(&self->tx_queue, skb);
- } else {
- /*
- * Fragment the frame, this function will also queue the
- * fragments, we don't care about the fact the transmit
- * queue may be overfilled by all the segments for a little
- * while
- */
- irttp_fragment_skb(self, skb);
- }
-
- /* Check if we can accept more data from client */
- if ((!self->tx_sdu_busy) &&
- (skb_queue_len(&self->tx_queue) > TTP_TX_HIGH_THRESHOLD)) {
- /* Tx queue filling up, so stop client. */
- if (self->notify.flow_indication) {
- self->notify.flow_indication(self->notify.instance,
- self, FLOW_STOP);
- }
- /* self->tx_sdu_busy is the state of the client.
- * Update state after notifying client to avoid
- * race condition with irttp_flow_indication().
- * If the queue empty itself after our test but before
- * we set the flag, we will fix ourselves below in
- * irttp_run_tx_queue().
- * Jean II */
- self->tx_sdu_busy = TRUE;
- }
-
- /* Try to make some progress */
- irttp_run_tx_queue(self);
-
- return 0;
-
-err:
- dev_kfree_skb(skb);
- return ret;
-}
-EXPORT_SYMBOL(irttp_data_request);
-
-/*
- * Function irttp_run_tx_queue (self)
- *
- * Transmit packets queued for transmission (if possible)
- *
- */
-static void irttp_run_tx_queue(struct tsap_cb *self)
-{
- struct sk_buff *skb;
- unsigned long flags;
- int n;
-
- pr_debug("%s() : send_credit = %d, queue_len = %d\n",
- __func__,
- self->send_credit, skb_queue_len(&self->tx_queue));
-
- /* Get exclusive access to the tx queue, otherwise don't touch it */
- if (irda_lock(&self->tx_queue_lock) == FALSE)
- return;
-
- /* Try to send out frames as long as we have credits
- * and as long as LAP is not full. If LAP is full, it will
- * poll us through irttp_flow_indication() - Jean II */
- while ((self->send_credit > 0) &&
- (!irlmp_lap_tx_queue_full(self->lsap)) &&
- (skb = skb_dequeue(&self->tx_queue))) {
- /*
- * Since we can transmit and receive frames concurrently,
- * the code below is a critical region and we must assure that
- * nobody messes with the credits while we update them.
- */
- spin_lock_irqsave(&self->lock, flags);
-
- n = self->avail_credit;
- self->avail_credit = 0;
-
- /* Only room for 127 credits in frame */
- if (n > 127) {
- self->avail_credit = n-127;
- n = 127;
- }
- self->remote_credit += n;
- self->send_credit--;
-
- spin_unlock_irqrestore(&self->lock, flags);
-
- /*
- * More bit must be set by the data_request() or fragment()
- * functions
- */
- skb->data[0] |= (n & 0x7f);
-
- /* Detach from socket.
- * The current skb has a reference to the socket that sent
- * it (skb->sk). When we pass it to IrLMP, the skb will be
- * stored in in IrLAP (self->wx_list). When we are within
- * IrLAP, we lose the notion of socket, so we should not
- * have a reference to a socket. So, we drop it here.
- *
- * Why does it matter ?
- * When the skb is freed (kfree_skb), if it is associated
- * with a socket, it release buffer space on the socket
- * (through sock_wfree() and sock_def_write_space()).
- * If the socket no longer exist, we may crash. Hard.
- * When we close a socket, we make sure that associated packets
- * in IrTTP are freed. However, we have no way to cancel
- * the packet that we have passed to IrLAP. So, if a packet
- * remains in IrLAP (retry on the link or else) after we
- * close the socket, we are dead !
- * Jean II */
- if (skb->sk != NULL) {
- /* IrSOCK application, IrOBEX, ... */
- skb_orphan(skb);
- }
- /* IrCOMM over IrTTP, IrLAN, ... */
-
- /* Pass the skb to IrLMP - done */
- irlmp_data_request(self->lsap, skb);
- self->stats.tx_packets++;
- }
-
- /* Check if we can accept more frames from client.
- * We don't want to wait until the todo timer to do that, and we
- * can't use tasklets (grr...), so we are obliged to give control
- * to client. That's ok, this test will be true not too often
- * (max once per LAP window) and we are called from places
- * where we can spend a bit of time doing stuff. - Jean II */
- if ((self->tx_sdu_busy) &&
- (skb_queue_len(&self->tx_queue) < TTP_TX_LOW_THRESHOLD) &&
- (!self->close_pend)) {
- if (self->notify.flow_indication)
- self->notify.flow_indication(self->notify.instance,
- self, FLOW_START);
-
- /* self->tx_sdu_busy is the state of the client.
- * We don't really have a race here, but it's always safer
- * to update our state after the client - Jean II */
- self->tx_sdu_busy = FALSE;
- }
-
- /* Reset lock */
- self->tx_queue_lock = 0;
-}
-
-/*
- * Function irttp_give_credit (self)
- *
- * Send a dataless flowdata TTP-PDU and give available credit to peer
- * TSAP
- */
-static inline void irttp_give_credit(struct tsap_cb *self)
-{
- struct sk_buff *tx_skb = NULL;
- unsigned long flags;
- int n;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
-
- pr_debug("%s() send=%d,avail=%d,remote=%d\n",
- __func__,
- self->send_credit, self->avail_credit, self->remote_credit);
-
- /* Give credit to peer */
- tx_skb = alloc_skb(TTP_MAX_HEADER, GFP_ATOMIC);
- if (!tx_skb)
- return;
-
- /* Reserve space for LMP, and LAP header */
- skb_reserve(tx_skb, LMP_MAX_HEADER);
-
- /*
- * Since we can transmit and receive frames concurrently,
- * the code below is a critical region and we must assure that
- * nobody messes with the credits while we update them.
- */
- spin_lock_irqsave(&self->lock, flags);
-
- n = self->avail_credit;
- self->avail_credit = 0;
-
- /* Only space for 127 credits in frame */
- if (n > 127) {
- self->avail_credit = n - 127;
- n = 127;
- }
- self->remote_credit += n;
-
- spin_unlock_irqrestore(&self->lock, flags);
-
- skb_put(tx_skb, 1);
- tx_skb->data[0] = (__u8) (n & 0x7f);
-
- irlmp_data_request(self->lsap, tx_skb);
- self->stats.tx_packets++;
-}
-
-/*
- * Function irttp_udata_indication (instance, sap, skb)
- *
- * Received some unit-data (unreliable)
- *
- */
-static int irttp_udata_indication(void *instance, void *sap,
- struct sk_buff *skb)
-{
- struct tsap_cb *self;
- int err;
-
- self = instance;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
- IRDA_ASSERT(skb != NULL, return -1;);
-
- self->stats.rx_packets++;
-
- /* Just pass data to layer above */
- if (self->notify.udata_indication) {
- err = self->notify.udata_indication(self->notify.instance,
- self, skb);
- /* Same comment as in irttp_do_data_indication() */
- if (!err)
- return 0;
- }
- /* Either no handler, or handler returns an error */
- dev_kfree_skb(skb);
-
- return 0;
-}
-
-/*
- * Function irttp_data_indication (instance, sap, skb)
- *
- * Receive segment from IrLMP.
- *
- */
-static int irttp_data_indication(void *instance, void *sap,
- struct sk_buff *skb)
-{
- struct tsap_cb *self;
- unsigned long flags;
- int n;
-
- self = instance;
-
- n = skb->data[0] & 0x7f; /* Extract the credits */
-
- self->stats.rx_packets++;
-
- /* Deal with inbound credit
- * Since we can transmit and receive frames concurrently,
- * the code below is a critical region and we must assure that
- * nobody messes with the credits while we update them.
- */
- spin_lock_irqsave(&self->lock, flags);
- self->send_credit += n;
- if (skb->len > 1)
- self->remote_credit--;
- spin_unlock_irqrestore(&self->lock, flags);
-
- /*
- * Data or dataless packet? Dataless frames contains only the
- * TTP_HEADER.
- */
- if (skb->len > 1) {
- /*
- * We don't remove the TTP header, since we must preserve the
- * more bit, so the defragment routing knows what to do
- */
- skb_queue_tail(&self->rx_queue, skb);
- } else {
- /* Dataless flowdata TTP-PDU */
- dev_kfree_skb(skb);
- }
-
-
- /* Push data to the higher layer.
- * We do it synchronously because running the todo timer for each
- * receive packet would be too much overhead and latency.
- * By passing control to the higher layer, we run the risk that
- * it may take time or grab a lock. Most often, the higher layer
- * will only put packet in a queue.
- * Anyway, packets are only dripping through the IrDA, so we can
- * have time before the next packet.
- * Further, we are run from NET_BH, so the worse that can happen is
- * us missing the optimal time to send back the PF bit in LAP.
- * Jean II */
- irttp_run_rx_queue(self);
-
- /* We now give credits to peer in irttp_run_rx_queue().
- * We need to send credit *NOW*, otherwise we are going
- * to miss the next Tx window. The todo timer may take
- * a while before it's run... - Jean II */
-
- /*
- * If the peer device has given us some credits and we didn't have
- * anyone from before, then we need to shedule the tx queue.
- * We need to do that because our Tx have stopped (so we may not
- * get any LAP flow indication) and the user may be stopped as
- * well. - Jean II
- */
- if (self->send_credit == n) {
- /* Restart pushing stuff to LAP */
- irttp_run_tx_queue(self);
- /* Note : we don't want to schedule the todo timer
- * because it has horrible latency. No tasklets
- * because the tasklet API is broken. - Jean II */
- }
-
- return 0;
-}
-
-/*
- * Function irttp_status_indication (self, reason)
- *
- * Status_indication, just pass to the higher layer...
- *
- */
-static void irttp_status_indication(void *instance,
- LINK_STATUS link, LOCK_STATUS lock)
-{
- struct tsap_cb *self;
-
- self = instance;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
-
- /* Check if client has already closed the TSAP and gone away */
- if (self->close_pend)
- return;
-
- /*
- * Inform service user if he has requested it
- */
- if (self->notify.status_indication != NULL)
- self->notify.status_indication(self->notify.instance,
- link, lock);
- else
- pr_debug("%s(), no handler\n", __func__);
-}
-
-/*
- * Function irttp_flow_indication (self, reason)
- *
- * Flow_indication : IrLAP tells us to send more data.
- *
- */
-static void irttp_flow_indication(void *instance, void *sap, LOCAL_FLOW flow)
-{
- struct tsap_cb *self;
-
- self = instance;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
-
- pr_debug("%s(instance=%p)\n", __func__, self);
-
- /* We are "polled" directly from LAP, and the LAP want to fill
- * its Tx window. We want to do our best to send it data, so that
- * we maximise the window. On the other hand, we want to limit the
- * amount of work here so that LAP doesn't hang forever waiting
- * for packets. - Jean II */
-
- /* Try to send some packets. Currently, LAP calls us every time
- * there is one free slot, so we will send only one packet.
- * This allow the scheduler to do its round robin - Jean II */
- irttp_run_tx_queue(self);
-
- /* Note regarding the interraction with higher layer.
- * irttp_run_tx_queue() may call the client when its queue
- * start to empty, via notify.flow_indication(). Initially.
- * I wanted this to happen in a tasklet, to avoid client
- * grabbing the CPU, but we can't use tasklets safely. And timer
- * is definitely too slow.
- * This will happen only once per LAP window, and usually at
- * the third packet (unless window is smaller). LAP is still
- * doing mtt and sending first packet so it's sort of OK
- * to do that. Jean II */
-
- /* If we need to send disconnect. try to do it now */
- if (self->disconnect_pend)
- irttp_start_todo_timer(self, 0);
-}
-
-/*
- * Function irttp_flow_request (self, command)
- *
- * This function could be used by the upper layers to tell IrTTP to stop
- * delivering frames if the receive queues are starting to get full, or
- * to tell IrTTP to start delivering frames again.
- */
-void irttp_flow_request(struct tsap_cb *self, LOCAL_FLOW flow)
-{
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
-
- switch (flow) {
- case FLOW_STOP:
- pr_debug("%s(), flow stop\n", __func__);
- self->rx_sdu_busy = TRUE;
- break;
- case FLOW_START:
- pr_debug("%s(), flow start\n", __func__);
- self->rx_sdu_busy = FALSE;
-
- /* Client say he can accept more data, try to free our
- * queues ASAP - Jean II */
- irttp_run_rx_queue(self);
-
- break;
- default:
- pr_debug("%s(), Unknown flow command!\n", __func__);
- }
-}
-EXPORT_SYMBOL(irttp_flow_request);
-
-/*
- * Function irttp_connect_request (self, dtsap_sel, daddr, qos)
- *
- * Try to connect to remote destination TSAP selector
- *
- */
-int irttp_connect_request(struct tsap_cb *self, __u8 dtsap_sel,
- __u32 saddr, __u32 daddr,
- struct qos_info *qos, __u32 max_sdu_size,
- struct sk_buff *userdata)
-{
- struct sk_buff *tx_skb;
- __u8 *frame;
- __u8 n;
-
- pr_debug("%s(), max_sdu_size=%d\n", __func__, max_sdu_size);
-
- IRDA_ASSERT(self != NULL, return -EBADR;);
- IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -EBADR;);
-
- if (self->connected) {
- if (userdata)
- dev_kfree_skb(userdata);
- return -EISCONN;
- }
-
- /* Any userdata supplied? */
- if (userdata == NULL) {
- tx_skb = alloc_skb(TTP_MAX_HEADER + TTP_SAR_HEADER,
- GFP_ATOMIC);
- if (!tx_skb)
- return -ENOMEM;
-
- /* Reserve space for MUX_CONTROL and LAP header */
- skb_reserve(tx_skb, TTP_MAX_HEADER + TTP_SAR_HEADER);
- } else {
- tx_skb = userdata;
- /*
- * Check that the client has reserved enough space for
- * headers
- */
- IRDA_ASSERT(skb_headroom(userdata) >= TTP_MAX_HEADER,
- { dev_kfree_skb(userdata); return -1; });
- }
-
- /* Initialize connection parameters */
- self->connected = FALSE;
- self->avail_credit = 0;
- self->rx_max_sdu_size = max_sdu_size;
- self->rx_sdu_size = 0;
- self->rx_sdu_busy = FALSE;
- self->dtsap_sel = dtsap_sel;
-
- n = self->initial_credit;
-
- self->remote_credit = 0;
- self->send_credit = 0;
-
- /*
- * Give away max 127 credits for now
- */
- if (n > 127) {
- self->avail_credit = n - 127;
- n = 127;
- }
-
- self->remote_credit = n;
-
- /* SAR enabled? */
- if (max_sdu_size > 0) {
- IRDA_ASSERT(skb_headroom(tx_skb) >= (TTP_MAX_HEADER + TTP_SAR_HEADER),
- { dev_kfree_skb(tx_skb); return -1; });
-
- /* Insert SAR parameters */
- frame = skb_push(tx_skb, TTP_HEADER + TTP_SAR_HEADER);
-
- frame[0] = TTP_PARAMETERS | n;
- frame[1] = 0x04; /* Length */
- frame[2] = 0x01; /* MaxSduSize */
- frame[3] = 0x02; /* Value length */
-
- put_unaligned(cpu_to_be16((__u16) max_sdu_size),
- (__be16 *)(frame+4));
- } else {
- /* Insert plain TTP header */
- frame = skb_push(tx_skb, TTP_HEADER);
-
- /* Insert initial credit in frame */
- frame[0] = n & 0x7f;
- }
-
- /* Connect with IrLMP. No QoS parameters for now */
- return irlmp_connect_request(self->lsap, dtsap_sel, saddr, daddr, qos,
- tx_skb);
-}
-EXPORT_SYMBOL(irttp_connect_request);
-
-/*
- * Function irttp_connect_confirm (handle, qos, skb)
- *
- * Service user confirms TSAP connection with peer.
- *
- */
-static void irttp_connect_confirm(void *instance, void *sap,
- struct qos_info *qos, __u32 max_seg_size,
- __u8 max_header_size, struct sk_buff *skb)
-{
- struct tsap_cb *self;
- int parameters;
- int ret;
- __u8 plen;
- __u8 n;
-
- self = instance;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
- IRDA_ASSERT(skb != NULL, return;);
-
- self->max_seg_size = max_seg_size - TTP_HEADER;
- self->max_header_size = max_header_size + TTP_HEADER;
-
- /*
- * Check if we have got some QoS parameters back! This should be the
- * negotiated QoS for the link.
- */
- if (qos) {
- pr_debug("IrTTP, Negotiated BAUD_RATE: %02x\n",
- qos->baud_rate.bits);
- pr_debug("IrTTP, Negotiated BAUD_RATE: %d bps.\n",
- qos->baud_rate.value);
- }
-
- n = skb->data[0] & 0x7f;
-
- pr_debug("%s(), Initial send_credit=%d\n", __func__, n);
-
- self->send_credit = n;
- self->tx_max_sdu_size = 0;
- self->connected = TRUE;
-
- parameters = skb->data[0] & 0x80;
-
- IRDA_ASSERT(skb->len >= TTP_HEADER, return;);
- skb_pull(skb, TTP_HEADER);
-
- if (parameters) {
- plen = skb->data[0];
-
- ret = irda_param_extract_all(self, skb->data+1,
- IRDA_MIN(skb->len-1, plen),
- &param_info);
-
- /* Any errors in the parameter list? */
- if (ret < 0) {
- net_warn_ratelimited("%s: error extracting parameters\n",
- __func__);
- dev_kfree_skb(skb);
-
- /* Do not accept this connection attempt */
- return;
- }
- /* Remove parameters */
- skb_pull(skb, IRDA_MIN(skb->len, plen+1));
- }
-
- pr_debug("%s() send=%d,avail=%d,remote=%d\n", __func__,
- self->send_credit, self->avail_credit, self->remote_credit);
-
- pr_debug("%s(), MaxSduSize=%d\n", __func__,
- self->tx_max_sdu_size);
-
- if (self->notify.connect_confirm) {
- self->notify.connect_confirm(self->notify.instance, self, qos,
- self->tx_max_sdu_size,
- self->max_header_size, skb);
- } else
- dev_kfree_skb(skb);
-}
-
-/*
- * Function irttp_connect_indication (handle, skb)
- *
- * Some other device is connecting to this TSAP
- *
- */
-static void irttp_connect_indication(void *instance, void *sap,
- struct qos_info *qos, __u32 max_seg_size, __u8 max_header_size,
- struct sk_buff *skb)
-{
- struct tsap_cb *self;
- struct lsap_cb *lsap;
- int parameters;
- int ret;
- __u8 plen;
- __u8 n;
-
- self = instance;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
- IRDA_ASSERT(skb != NULL, return;);
-
- lsap = sap;
-
- self->max_seg_size = max_seg_size - TTP_HEADER;
- self->max_header_size = max_header_size+TTP_HEADER;
-
- pr_debug("%s(), TSAP sel=%02x\n", __func__, self->stsap_sel);
-
- /* Need to update dtsap_sel if its equal to LSAP_ANY */
- self->dtsap_sel = lsap->dlsap_sel;
-
- n = skb->data[0] & 0x7f;
-
- self->send_credit = n;
- self->tx_max_sdu_size = 0;
-
- parameters = skb->data[0] & 0x80;
-
- IRDA_ASSERT(skb->len >= TTP_HEADER, return;);
- skb_pull(skb, TTP_HEADER);
-
- if (parameters) {
- plen = skb->data[0];
-
- ret = irda_param_extract_all(self, skb->data+1,
- IRDA_MIN(skb->len-1, plen),
- &param_info);
-
- /* Any errors in the parameter list? */
- if (ret < 0) {
- net_warn_ratelimited("%s: error extracting parameters\n",
- __func__);
- dev_kfree_skb(skb);
-
- /* Do not accept this connection attempt */
- return;
- }
-
- /* Remove parameters */
- skb_pull(skb, IRDA_MIN(skb->len, plen+1));
- }
-
- if (self->notify.connect_indication) {
- self->notify.connect_indication(self->notify.instance, self,
- qos, self->tx_max_sdu_size,
- self->max_header_size, skb);
- } else
- dev_kfree_skb(skb);
-}
-
-/*
- * Function irttp_connect_response (handle, userdata)
- *
- * Service user is accepting the connection, just pass it down to
- * IrLMP!
- *
- */
-int irttp_connect_response(struct tsap_cb *self, __u32 max_sdu_size,
- struct sk_buff *userdata)
-{
- struct sk_buff *tx_skb;
- __u8 *frame;
- int ret;
- __u8 n;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
-
- pr_debug("%s(), Source TSAP selector=%02x\n", __func__,
- self->stsap_sel);
-
- /* Any userdata supplied? */
- if (userdata == NULL) {
- tx_skb = alloc_skb(TTP_MAX_HEADER + TTP_SAR_HEADER,
- GFP_ATOMIC);
- if (!tx_skb)
- return -ENOMEM;
-
- /* Reserve space for MUX_CONTROL and LAP header */
- skb_reserve(tx_skb, TTP_MAX_HEADER + TTP_SAR_HEADER);
- } else {
- tx_skb = userdata;
- /*
- * Check that the client has reserved enough space for
- * headers
- */
- IRDA_ASSERT(skb_headroom(userdata) >= TTP_MAX_HEADER,
- { dev_kfree_skb(userdata); return -1; });
- }
-
- self->avail_credit = 0;
- self->remote_credit = 0;
- self->rx_max_sdu_size = max_sdu_size;
- self->rx_sdu_size = 0;
- self->rx_sdu_busy = FALSE;
-
- n = self->initial_credit;
-
- /* Frame has only space for max 127 credits (7 bits) */
- if (n > 127) {
- self->avail_credit = n - 127;
- n = 127;
- }
-
- self->remote_credit = n;
- self->connected = TRUE;
-
- /* SAR enabled? */
- if (max_sdu_size > 0) {
- IRDA_ASSERT(skb_headroom(tx_skb) >= (TTP_MAX_HEADER + TTP_SAR_HEADER),
- { dev_kfree_skb(tx_skb); return -1; });
-
- /* Insert TTP header with SAR parameters */
- frame = skb_push(tx_skb, TTP_HEADER + TTP_SAR_HEADER);
-
- frame[0] = TTP_PARAMETERS | n;
- frame[1] = 0x04; /* Length */
-
- /* irda_param_insert(self, IRTTP_MAX_SDU_SIZE, frame+1, */
-/* TTP_SAR_HEADER, &param_info) */
-
- frame[2] = 0x01; /* MaxSduSize */
- frame[3] = 0x02; /* Value length */
-
- put_unaligned(cpu_to_be16((__u16) max_sdu_size),
- (__be16 *)(frame+4));
- } else {
- /* Insert TTP header */
- frame = skb_push(tx_skb, TTP_HEADER);
-
- frame[0] = n & 0x7f;
- }
-
- ret = irlmp_connect_response(self->lsap, tx_skb);
-
- return ret;
-}
-EXPORT_SYMBOL(irttp_connect_response);
-
-/*
- * Function irttp_dup (self, instance)
- *
- * Duplicate TSAP, can be used by servers to confirm a connection on a
- * new TSAP so it can keep listening on the old one.
- */
-struct tsap_cb *irttp_dup(struct tsap_cb *orig, void *instance)
-{
- struct tsap_cb *new;
- unsigned long flags;
-
- /* Protect our access to the old tsap instance */
- spin_lock_irqsave(&irttp->tsaps->hb_spinlock, flags);
-
- /* Find the old instance */
- if (!hashbin_find(irttp->tsaps, (long) orig, NULL)) {
- pr_debug("%s(), unable to find TSAP\n", __func__);
- spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags);
- return NULL;
- }
-
- /* Allocate a new instance */
- new = kmemdup(orig, sizeof(struct tsap_cb), GFP_ATOMIC);
- if (!new) {
- pr_debug("%s(), unable to kmalloc\n", __func__);
- spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags);
- return NULL;
- }
- spin_lock_init(&new->lock);
-
- /* We don't need the old instance any more */
- spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags);
-
- /* Try to dup the LSAP (may fail if we were too slow) */
- new->lsap = irlmp_dup(orig->lsap, new);
- if (!new->lsap) {
- pr_debug("%s(), dup failed!\n", __func__);
- kfree(new);
- return NULL;
- }
-
- /* Not everything should be copied */
- new->notify.instance = instance;
-
- /* Initialize internal objects */
- irttp_init_tsap(new);
-
- /* This is locked */
- hashbin_insert(irttp->tsaps, (irda_queue_t *) new, (long) new, NULL);
-
- return new;
-}
-EXPORT_SYMBOL(irttp_dup);
-
-/*
- * Function irttp_disconnect_request (self)
- *
- * Close this connection please! If priority is high, the queued data
- * segments, if any, will be deallocated first
- *
- */
-int irttp_disconnect_request(struct tsap_cb *self, struct sk_buff *userdata,
- int priority)
-{
- int ret;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
-
- /* Already disconnected? */
- if (!self->connected) {
- pr_debug("%s(), already disconnected!\n", __func__);
- if (userdata)
- dev_kfree_skb(userdata);
- return -1;
- }
-
- /* Disconnect already pending ?
- * We need to use an atomic operation to prevent reentry. This
- * function may be called from various context, like user, timer
- * for following a disconnect_indication() (i.e. net_bh).
- * Jean II */
- if (test_and_set_bit(0, &self->disconnect_pend)) {
- pr_debug("%s(), disconnect already pending\n",
- __func__);
- if (userdata)
- dev_kfree_skb(userdata);
-
- /* Try to make some progress */
- irttp_run_tx_queue(self);
- return -1;
- }
-
- /*
- * Check if there is still data segments in the transmit queue
- */
- if (!skb_queue_empty(&self->tx_queue)) {
- if (priority == P_HIGH) {
- /*
- * No need to send the queued data, if we are
- * disconnecting right now since the data will
- * not have any usable connection to be sent on
- */
- pr_debug("%s(): High priority!!()\n", __func__);
- irttp_flush_queues(self);
- } else if (priority == P_NORMAL) {
- /*
- * Must delay disconnect until after all data segments
- * have been sent and the tx_queue is empty
- */
- /* We'll reuse this one later for the disconnect */
- self->disconnect_skb = userdata; /* May be NULL */
-
- irttp_run_tx_queue(self);
-
- irttp_start_todo_timer(self, HZ/10);
- return -1;
- }
- }
- /* Note : we don't need to check if self->rx_queue is full and the
- * state of self->rx_sdu_busy because the disconnect response will
- * be sent at the LMP level (so even if the peer has its Tx queue
- * full of data). - Jean II */
-
- pr_debug("%s(), Disconnecting ...\n", __func__);
- self->connected = FALSE;
-
- if (!userdata) {
- struct sk_buff *tx_skb;
- tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
- if (!tx_skb)
- return -ENOMEM;
-
- /*
- * Reserve space for MUX and LAP header
- */
- skb_reserve(tx_skb, LMP_MAX_HEADER);
-
- userdata = tx_skb;
- }
- ret = irlmp_disconnect_request(self->lsap, userdata);
-
- /* The disconnect is no longer pending */
- clear_bit(0, &self->disconnect_pend); /* FALSE */
-
- return ret;
-}
-EXPORT_SYMBOL(irttp_disconnect_request);
-
-/*
- * Function irttp_disconnect_indication (self, reason)
- *
- * Disconnect indication, TSAP disconnected by peer?
- *
- */
-static void irttp_disconnect_indication(void *instance, void *sap,
- LM_REASON reason, struct sk_buff *skb)
-{
- struct tsap_cb *self;
-
- self = instance;
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
-
- /* Prevent higher layer to send more data */
- self->connected = FALSE;
-
- /* Check if client has already tried to close the TSAP */
- if (self->close_pend) {
- /* In this case, the higher layer is probably gone. Don't
- * bother it and clean up the remains - Jean II */
- if (skb)
- dev_kfree_skb(skb);
- irttp_close_tsap(self);
- return;
- }
-
- /* If we are here, we assume that is the higher layer is still
- * waiting for the disconnect notification and able to process it,
- * even if he tried to disconnect. Otherwise, it would have already
- * attempted to close the tsap and self->close_pend would be TRUE.
- * Jean II */
-
- /* No need to notify the client if has already tried to disconnect */
- if (self->notify.disconnect_indication)
- self->notify.disconnect_indication(self->notify.instance, self,
- reason, skb);
- else
- if (skb)
- dev_kfree_skb(skb);
-}
-
-/*
- * Function irttp_do_data_indication (self, skb)
- *
- * Try to deliver reassembled skb to layer above, and requeue it if that
- * for some reason should fail. We mark rx sdu as busy to apply back
- * pressure is necessary.
- */
-static void irttp_do_data_indication(struct tsap_cb *self, struct sk_buff *skb)
-{
- int err;
-
- /* Check if client has already closed the TSAP and gone away */
- if (self->close_pend) {
- dev_kfree_skb(skb);
- return;
- }
-
- err = self->notify.data_indication(self->notify.instance, self, skb);
-
- /* Usually the layer above will notify that it's input queue is
- * starting to get filled by using the flow request, but this may
- * be difficult, so it can instead just refuse to eat it and just
- * give an error back
- */
- if (err) {
- pr_debug("%s() requeueing skb!\n", __func__);
-
- /* Make sure we take a break */
- self->rx_sdu_busy = TRUE;
-
- /* Need to push the header in again */
- skb_push(skb, TTP_HEADER);
- skb->data[0] = 0x00; /* Make sure MORE bit is cleared */
-
- /* Put skb back on queue */
- skb_queue_head(&self->rx_queue, skb);
- }
-}
-
-/*
- * Function irttp_run_rx_queue (self)
- *
- * Check if we have any frames to be transmitted, or if we have any
- * available credit to give away.
- */
-static void irttp_run_rx_queue(struct tsap_cb *self)
-{
- struct sk_buff *skb;
- int more = 0;
-
- pr_debug("%s() send=%d,avail=%d,remote=%d\n", __func__,
- self->send_credit, self->avail_credit, self->remote_credit);
-
- /* Get exclusive access to the rx queue, otherwise don't touch it */
- if (irda_lock(&self->rx_queue_lock) == FALSE)
- return;
-
- /*
- * Reassemble all frames in receive queue and deliver them
- */
- while (!self->rx_sdu_busy && (skb = skb_dequeue(&self->rx_queue))) {
- /* This bit will tell us if it's the last fragment or not */
- more = skb->data[0] & 0x80;
-
- /* Remove TTP header */
- skb_pull(skb, TTP_HEADER);
-
- /* Add the length of the remaining data */
- self->rx_sdu_size += skb->len;
-
- /*
- * If SAR is disabled, or user has requested no reassembly
- * of received fragments then we just deliver them
- * immediately. This can be requested by clients that
- * implements byte streams without any message boundaries
- */
- if (self->rx_max_sdu_size == TTP_SAR_DISABLE) {
- irttp_do_data_indication(self, skb);
- self->rx_sdu_size = 0;
-
- continue;
- }
-
- /* Check if this is a fragment, and not the last fragment */
- if (more) {
- /*
- * Queue the fragment if we still are within the
- * limits of the maximum size of the rx_sdu
- */
- if (self->rx_sdu_size <= self->rx_max_sdu_size) {
- pr_debug("%s(), queueing frag\n",
- __func__);
- skb_queue_tail(&self->rx_fragments, skb);
- } else {
- /* Free the part of the SDU that is too big */
- dev_kfree_skb(skb);
- }
- continue;
- }
- /*
- * This is the last fragment, so time to reassemble!
- */
- if ((self->rx_sdu_size <= self->rx_max_sdu_size) ||
- (self->rx_max_sdu_size == TTP_SAR_UNBOUND)) {
- /*
- * A little optimizing. Only queue the fragment if
- * there are other fragments. Since if this is the
- * last and only fragment, there is no need to
- * reassemble :-)
- */
- if (!skb_queue_empty(&self->rx_fragments)) {
- skb_queue_tail(&self->rx_fragments,
- skb);
-
- skb = irttp_reassemble_skb(self);
- }
-
- /* Now we can deliver the reassembled skb */
- irttp_do_data_indication(self, skb);
- } else {
- pr_debug("%s(), Truncated frame\n", __func__);
-
- /* Free the part of the SDU that is too big */
- dev_kfree_skb(skb);
-
- /* Deliver only the valid but truncated part of SDU */
- skb = irttp_reassemble_skb(self);
-
- irttp_do_data_indication(self, skb);
- }
- self->rx_sdu_size = 0;
- }
-
- /*
- * It's not trivial to keep track of how many credits are available
- * by incrementing at each packet, because delivery may fail
- * (irttp_do_data_indication() may requeue the frame) and because
- * we need to take care of fragmentation.
- * We want the other side to send up to initial_credit packets.
- * We have some frames in our queues, and we have already allowed it
- * to send remote_credit.
- * No need to spinlock, write is atomic and self correcting...
- * Jean II
- */
- self->avail_credit = (self->initial_credit -
- (self->remote_credit +
- skb_queue_len(&self->rx_queue) +
- skb_queue_len(&self->rx_fragments)));
-
- /* Do we have too much credits to send to peer ? */
- if ((self->remote_credit <= TTP_RX_MIN_CREDIT) &&
- (self->avail_credit > 0)) {
- /* Send explicit credit frame */
- irttp_give_credit(self);
- /* Note : do *NOT* check if tx_queue is non-empty, that
- * will produce deadlocks. I repeat : send a credit frame
- * even if we have something to send in our Tx queue.
- * If we have credits, it means that our Tx queue is blocked.
- *
- * Let's suppose the peer can't keep up with our Tx. He will
- * flow control us by not sending us any credits, and we
- * will stop Tx and start accumulating credits here.
- * Up to the point where the peer will stop its Tx queue,
- * for lack of credits.
- * Let's assume the peer application is single threaded.
- * It will block on Tx and never consume any Rx buffer.
- * Deadlock. Guaranteed. - Jean II
- */
- }
-
- /* Reset lock */
- self->rx_queue_lock = 0;
-}
-
-#ifdef CONFIG_PROC_FS
-struct irttp_iter_state {
- int id;
-};
-
-static void *irttp_seq_start(struct seq_file *seq, loff_t *pos)
-{
- struct irttp_iter_state *iter = seq->private;
- struct tsap_cb *self;
-
- /* Protect our access to the tsap list */
- spin_lock_irq(&irttp->tsaps->hb_spinlock);
- iter->id = 0;
-
- for (self = (struct tsap_cb *) hashbin_get_first(irttp->tsaps);
- self != NULL;
- self = (struct tsap_cb *) hashbin_get_next(irttp->tsaps)) {
- if (iter->id == *pos)
- break;
- ++iter->id;
- }
-
- return self;
-}
-
-static void *irttp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
-{
- struct irttp_iter_state *iter = seq->private;
-
- ++*pos;
- ++iter->id;
- return (void *) hashbin_get_next(irttp->tsaps);
-}
-
-static void irttp_seq_stop(struct seq_file *seq, void *v)
-{
- spin_unlock_irq(&irttp->tsaps->hb_spinlock);
-}
-
-static int irttp_seq_show(struct seq_file *seq, void *v)
-{
- const struct irttp_iter_state *iter = seq->private;
- const struct tsap_cb *self = v;
-
- seq_printf(seq, "TSAP %d, ", iter->id);
- seq_printf(seq, "stsap_sel: %02x, ",
- self->stsap_sel);
- seq_printf(seq, "dtsap_sel: %02x\n",
- self->dtsap_sel);
- seq_printf(seq, " connected: %s, ",
- self->connected ? "TRUE" : "FALSE");
- seq_printf(seq, "avail credit: %d, ",
- self->avail_credit);
- seq_printf(seq, "remote credit: %d, ",
- self->remote_credit);
- seq_printf(seq, "send credit: %d\n",
- self->send_credit);
- seq_printf(seq, " tx packets: %lu, ",
- self->stats.tx_packets);
- seq_printf(seq, "rx packets: %lu, ",
- self->stats.rx_packets);
- seq_printf(seq, "tx_queue len: %u ",
- skb_queue_len(&self->tx_queue));
- seq_printf(seq, "rx_queue len: %u\n",
- skb_queue_len(&self->rx_queue));
- seq_printf(seq, " tx_sdu_busy: %s, ",
- self->tx_sdu_busy ? "TRUE" : "FALSE");
- seq_printf(seq, "rx_sdu_busy: %s\n",
- self->rx_sdu_busy ? "TRUE" : "FALSE");
- seq_printf(seq, " max_seg_size: %u, ",
- self->max_seg_size);
- seq_printf(seq, "tx_max_sdu_size: %u, ",
- self->tx_max_sdu_size);
- seq_printf(seq, "rx_max_sdu_size: %u\n",
- self->rx_max_sdu_size);
-
- seq_printf(seq, " Used by (%s)\n\n",
- self->notify.name);
- return 0;
-}
-
-static const struct seq_operations irttp_seq_ops = {
- .start = irttp_seq_start,
- .next = irttp_seq_next,
- .stop = irttp_seq_stop,
- .show = irttp_seq_show,
-};
-
-static int irttp_seq_open(struct inode *inode, struct file *file)
-{
- return seq_open_private(file, &irttp_seq_ops,
- sizeof(struct irttp_iter_state));
-}
-
-const struct file_operations irttp_seq_fops = {
- .owner = THIS_MODULE,
- .open = irttp_seq_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = seq_release_private,
-};
-
-#endif /* PROC_FS */
diff --git a/drivers/staging/irda/net/parameters.c b/drivers/staging/irda/net/parameters.c
deleted file mode 100644
index 16ce32ffe004..000000000000
--- a/drivers/staging/irda/net/parameters.c
+++ /dev/null
@@ -1,584 +0,0 @@
-/*********************************************************************
- *
- * Filename: parameters.c
- * Version: 1.0
- * Description: A more general way to handle (pi,pl,pv) parameters
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Mon Jun 7 10:25:11 1999
- * Modified at: Sun Jan 30 14:08:39 2000
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/types.h>
-#include <linux/module.h>
-
-#include <asm/unaligned.h>
-#include <asm/byteorder.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/parameters.h>
-
-static int irda_extract_integer(void *self, __u8 *buf, int len, __u8 pi,
- PV_TYPE type, PI_HANDLER func);
-static int irda_extract_string(void *self, __u8 *buf, int len, __u8 pi,
- PV_TYPE type, PI_HANDLER func);
-static int irda_extract_octseq(void *self, __u8 *buf, int len, __u8 pi,
- PV_TYPE type, PI_HANDLER func);
-static int irda_extract_no_value(void *self, __u8 *buf, int len, __u8 pi,
- PV_TYPE type, PI_HANDLER func);
-
-static int irda_insert_integer(void *self, __u8 *buf, int len, __u8 pi,
- PV_TYPE type, PI_HANDLER func);
-static int irda_insert_no_value(void *self, __u8 *buf, int len, __u8 pi,
- PV_TYPE type, PI_HANDLER func);
-
-static int irda_param_unpack(__u8 *buf, char *fmt, ...);
-
-/* Parameter value call table. Must match PV_TYPE */
-static const PV_HANDLER pv_extract_table[] = {
- irda_extract_integer, /* Handler for any length integers */
- irda_extract_integer, /* Handler for 8 bits integers */
- irda_extract_integer, /* Handler for 16 bits integers */
- irda_extract_string, /* Handler for strings */
- irda_extract_integer, /* Handler for 32 bits integers */
- irda_extract_octseq, /* Handler for octet sequences */
- irda_extract_no_value /* Handler for no value parameters */
-};
-
-static const PV_HANDLER pv_insert_table[] = {
- irda_insert_integer, /* Handler for any length integers */
- irda_insert_integer, /* Handler for 8 bits integers */
- irda_insert_integer, /* Handler for 16 bits integers */
- NULL, /* Handler for strings */
- irda_insert_integer, /* Handler for 32 bits integers */
- NULL, /* Handler for octet sequences */
- irda_insert_no_value /* Handler for no value parameters */
-};
-
-/*
- * Function irda_insert_no_value (self, buf, len, pi, type, func)
- */
-static int irda_insert_no_value(void *self, __u8 *buf, int len, __u8 pi,
- PV_TYPE type, PI_HANDLER func)
-{
- irda_param_t p;
- int ret;
-
- p.pi = pi;
- p.pl = 0;
-
- /* Call handler for this parameter */
- ret = (*func)(self, &p, PV_GET);
-
- /* Extract values anyway, since handler may need them */
- irda_param_pack(buf, "bb", p.pi, p.pl);
-
- if (ret < 0)
- return ret;
-
- return 2; /* Inserted pl+2 bytes */
-}
-
-/*
- * Function irda_extract_no_value (self, buf, len, type, func)
- *
- * Extracts a parameter without a pv field (pl=0)
- *
- */
-static int irda_extract_no_value(void *self, __u8 *buf, int len, __u8 pi,
- PV_TYPE type, PI_HANDLER func)
-{
- irda_param_t p;
- int ret;
-
- /* Extract values anyway, since handler may need them */
- irda_param_unpack(buf, "bb", &p.pi, &p.pl);
-
- /* Call handler for this parameter */
- ret = (*func)(self, &p, PV_PUT);
-
- if (ret < 0)
- return ret;
-
- return 2; /* Extracted pl+2 bytes */
-}
-
-/*
- * Function irda_insert_integer (self, buf, len, pi, type, func)
- */
-static int irda_insert_integer(void *self, __u8 *buf, int len, __u8 pi,
- PV_TYPE type, PI_HANDLER func)
-{
- irda_param_t p;
- int n = 0;
- int err;
-
- p.pi = pi; /* In case handler needs to know */
- p.pl = type & PV_MASK; /* The integer type codes the length as well */
- p.pv.i = 0; /* Clear value */
-
- /* Call handler for this parameter */
- err = (*func)(self, &p, PV_GET);
- if (err < 0)
- return err;
-
- /*
- * If parameter length is still 0, then (1) this is an any length
- * integer, and (2) the handler function does not care which length
- * we choose to use, so we pick the one the gives the fewest bytes.
- */
- if (p.pl == 0) {
- if (p.pv.i < 0xff) {
- pr_debug("%s(), using 1 byte\n", __func__);
- p.pl = 1;
- } else if (p.pv.i < 0xffff) {
- pr_debug("%s(), using 2 bytes\n", __func__);
- p.pl = 2;
- } else {
- pr_debug("%s(), using 4 bytes\n", __func__);
- p.pl = 4; /* Default length */
- }
- }
- /* Check if buffer is long enough for insertion */
- if (len < (2+p.pl)) {
- net_warn_ratelimited("%s: buffer too short for insertion!\n",
- __func__);
- return -1;
- }
- pr_debug("%s(), pi=%#x, pl=%d, pi=%d\n", __func__,
- p.pi, p.pl, p.pv.i);
- switch (p.pl) {
- case 1:
- n += irda_param_pack(buf, "bbb", p.pi, p.pl, (__u8) p.pv.i);
- break;
- case 2:
- if (type & PV_BIG_ENDIAN)
- p.pv.i = cpu_to_be16((__u16) p.pv.i);
- else
- p.pv.i = cpu_to_le16((__u16) p.pv.i);
- n += irda_param_pack(buf, "bbs", p.pi, p.pl, (__u16) p.pv.i);
- break;
- case 4:
- if (type & PV_BIG_ENDIAN)
- cpu_to_be32s(&p.pv.i);
- else
- cpu_to_le32s(&p.pv.i);
- n += irda_param_pack(buf, "bbi", p.pi, p.pl, p.pv.i);
-
- break;
- default:
- net_warn_ratelimited("%s: length %d not supported\n",
- __func__, p.pl);
- /* Skip parameter */
- return -1;
- }
-
- return p.pl+2; /* Inserted pl+2 bytes */
-}
-
-/*
- * Function irda_extract integer (self, buf, len, pi, type, func)
- *
- * Extract a possibly variable length integer from buffer, and call
- * handler for processing of the parameter
- */
-static int irda_extract_integer(void *self, __u8 *buf, int len, __u8 pi,
- PV_TYPE type, PI_HANDLER func)
-{
- irda_param_t p;
- int n = 0;
- int extract_len; /* Real length we extract */
- int err;
-
- p.pi = pi; /* In case handler needs to know */
- p.pl = buf[1]; /* Extract length of value */
- p.pv.i = 0; /* Clear value */
- extract_len = p.pl; /* Default : extract all */
-
- /* Check if buffer is long enough for parsing */
- if (len < (2+p.pl)) {
- net_warn_ratelimited("%s: buffer too short for parsing! Need %d bytes, but len is only %d\n",
- __func__, p.pl, len);
- return -1;
- }
-
- /*
- * Check that the integer length is what we expect it to be. If the
- * handler want a 16 bits integer then a 32 bits is not good enough
- * PV_INTEGER means that the handler is flexible.
- */
- if (((type & PV_MASK) != PV_INTEGER) && ((type & PV_MASK) != p.pl)) {
- net_err_ratelimited("%s: invalid parameter length! Expected %d bytes, but value had %d bytes!\n",
- __func__, type & PV_MASK, p.pl);
-
- /* Most parameters are bit/byte fields or little endian,
- * so it's ok to only extract a subset of it (the subset
- * that the handler expect). This is necessary, as some
- * broken implementations seems to add extra undefined bits.
- * If the parameter is shorter than we expect or is big
- * endian, we can't play those tricks. Jean II */
- if((p.pl < (type & PV_MASK)) || (type & PV_BIG_ENDIAN)) {
- /* Skip parameter */
- return p.pl+2;
- } else {
- /* Extract subset of it, fallthrough */
- extract_len = type & PV_MASK;
- }
- }
-
-
- switch (extract_len) {
- case 1:
- n += irda_param_unpack(buf+2, "b", &p.pv.i);
- break;
- case 2:
- n += irda_param_unpack(buf+2, "s", &p.pv.i);
- if (type & PV_BIG_ENDIAN)
- p.pv.i = be16_to_cpu((__u16) p.pv.i);
- else
- p.pv.i = le16_to_cpu((__u16) p.pv.i);
- break;
- case 4:
- n += irda_param_unpack(buf+2, "i", &p.pv.i);
- if (type & PV_BIG_ENDIAN)
- be32_to_cpus(&p.pv.i);
- else
- le32_to_cpus(&p.pv.i);
- break;
- default:
- net_warn_ratelimited("%s: length %d not supported\n",
- __func__, p.pl);
-
- /* Skip parameter */
- return p.pl+2;
- }
-
- pr_debug("%s(), pi=%#x, pl=%d, pi=%d\n", __func__,
- p.pi, p.pl, p.pv.i);
- /* Call handler for this parameter */
- err = (*func)(self, &p, PV_PUT);
- if (err < 0)
- return err;
-
- return p.pl+2; /* Extracted pl+2 bytes */
-}
-
-/*
- * Function irda_extract_string (self, buf, len, type, func)
- */
-static int irda_extract_string(void *self, __u8 *buf, int len, __u8 pi,
- PV_TYPE type, PI_HANDLER func)
-{
- char str[33];
- irda_param_t p;
- int err;
-
- p.pi = pi; /* In case handler needs to know */
- p.pl = buf[1]; /* Extract length of value */
- if (p.pl > 32)
- p.pl = 32;
-
- pr_debug("%s(), pi=%#x, pl=%d\n", __func__,
- p.pi, p.pl);
-
- /* Check if buffer is long enough for parsing */
- if (len < (2+p.pl)) {
- net_warn_ratelimited("%s: buffer too short for parsing! Need %d bytes, but len is only %d\n",
- __func__, p.pl, len);
- return -1;
- }
-
- /* Should be safe to copy string like this since we have already
- * checked that the buffer is long enough */
- strncpy(str, buf+2, p.pl);
-
- pr_debug("%s(), str=0x%02x 0x%02x\n",
- __func__, (__u8)str[0], (__u8)str[1]);
-
- /* Null terminate string */
- str[p.pl] = '\0';
-
- p.pv.c = str; /* Handler will need to take a copy */
-
- /* Call handler for this parameter */
- err = (*func)(self, &p, PV_PUT);
- if (err < 0)
- return err;
-
- return p.pl+2; /* Extracted pl+2 bytes */
-}
-
-/*
- * Function irda_extract_octseq (self, buf, len, type, func)
- */
-static int irda_extract_octseq(void *self, __u8 *buf, int len, __u8 pi,
- PV_TYPE type, PI_HANDLER func)
-{
- irda_param_t p;
-
- p.pi = pi; /* In case handler needs to know */
- p.pl = buf[1]; /* Extract length of value */
-
- /* Check if buffer is long enough for parsing */
- if (len < (2+p.pl)) {
- net_warn_ratelimited("%s: buffer too short for parsing! Need %d bytes, but len is only %d\n",
- __func__, p.pl, len);
- return -1;
- }
-
- pr_debug("%s(), not impl\n", __func__);
-
- return p.pl+2; /* Extracted pl+2 bytes */
-}
-
-/*
- * Function irda_param_pack (skb, fmt, ...)
- *
- * Format:
- * 'i' = 32 bits integer
- * 's' = string
- *
- */
-int irda_param_pack(__u8 *buf, char *fmt, ...)
-{
- irda_pv_t arg;
- va_list args;
- char *p;
- int n = 0;
-
- va_start(args, fmt);
-
- for (p = fmt; *p != '\0'; p++) {
- switch (*p) {
- case 'b': /* 8 bits unsigned byte */
- buf[n++] = (__u8)va_arg(args, int);
- break;
- case 's': /* 16 bits unsigned short */
- arg.i = (__u16)va_arg(args, int);
- put_unaligned((__u16)arg.i, (__u16 *)(buf+n)); n+=2;
- break;
- case 'i': /* 32 bits unsigned integer */
- arg.i = va_arg(args, __u32);
- put_unaligned(arg.i, (__u32 *)(buf+n)); n+=4;
- break;
-#if 0
- case 'c': /* \0 terminated string */
- arg.c = va_arg(args, char *);
- strcpy(buf+n, arg.c);
- n += strlen(arg.c) + 1;
- break;
-#endif
- default:
- va_end(args);
- return -1;
- }
- }
- va_end(args);
-
- return 0;
-}
-EXPORT_SYMBOL(irda_param_pack);
-
-/*
- * Function irda_param_unpack (skb, fmt, ...)
- */
-static int irda_param_unpack(__u8 *buf, char *fmt, ...)
-{
- irda_pv_t arg;
- va_list args;
- char *p;
- int n = 0;
-
- va_start(args, fmt);
-
- for (p = fmt; *p != '\0'; p++) {
- switch (*p) {
- case 'b': /* 8 bits byte */
- arg.ip = va_arg(args, __u32 *);
- *arg.ip = buf[n++];
- break;
- case 's': /* 16 bits short */
- arg.ip = va_arg(args, __u32 *);
- *arg.ip = get_unaligned((__u16 *)(buf+n)); n+=2;
- break;
- case 'i': /* 32 bits unsigned integer */
- arg.ip = va_arg(args, __u32 *);
- *arg.ip = get_unaligned((__u32 *)(buf+n)); n+=4;
- break;
-#if 0
- case 'c': /* \0 terminated string */
- arg.c = va_arg(args, char *);
- strcpy(arg.c, buf+n);
- n += strlen(arg.c) + 1;
- break;
-#endif
- default:
- va_end(args);
- return -1;
- }
-
- }
- va_end(args);
-
- return 0;
-}
-
-/*
- * Function irda_param_insert (self, pi, buf, len, info)
- *
- * Insert the specified parameter (pi) into buffer. Returns number of
- * bytes inserted
- */
-int irda_param_insert(void *self, __u8 pi, __u8 *buf, int len,
- pi_param_info_t *info)
-{
- const pi_minor_info_t *pi_minor_info;
- __u8 pi_minor;
- __u8 pi_major;
- int type;
- int ret = -1;
- int n = 0;
-
- IRDA_ASSERT(buf != NULL, return ret;);
- IRDA_ASSERT(info != NULL, return ret;);
-
- pi_minor = pi & info->pi_mask;
- pi_major = pi >> info->pi_major_offset;
-
- /* Check if the identifier value (pi) is valid */
- if ((pi_major > info->len-1) ||
- (pi_minor > info->tables[pi_major].len-1))
- {
- pr_debug("%s(), no handler for parameter=0x%02x\n",
- __func__, pi);
-
- /* Skip this parameter */
- return -1;
- }
-
- /* Lookup the info on how to parse this parameter */
- pi_minor_info = &info->tables[pi_major].pi_minor_call_table[pi_minor];
-
- /* Find expected data type for this parameter identifier (pi)*/
- type = pi_minor_info->type;
-
- /* Check if handler has been implemented */
- if (!pi_minor_info->func) {
- net_info_ratelimited("%s: no handler for pi=%#x\n",
- __func__, pi);
- /* Skip this parameter */
- return -1;
- }
-
- /* Insert parameter value */
- ret = (*pv_insert_table[type & PV_MASK])(self, buf+n, len, pi, type,
- pi_minor_info->func);
- return ret;
-}
-EXPORT_SYMBOL(irda_param_insert);
-
-/*
- * Function irda_param_extract (self, buf, len, info)
- *
- * Parse all parameters. If len is correct, then everything should be
- * safe. Returns the number of bytes that was parsed
- *
- */
-static int irda_param_extract(void *self, __u8 *buf, int len,
- pi_param_info_t *info)
-{
- const pi_minor_info_t *pi_minor_info;
- __u8 pi_minor;
- __u8 pi_major;
- int type;
- int ret = -1;
- int n = 0;
-
- IRDA_ASSERT(buf != NULL, return ret;);
- IRDA_ASSERT(info != NULL, return ret;);
-
- pi_minor = buf[n] & info->pi_mask;
- pi_major = buf[n] >> info->pi_major_offset;
-
- /* Check if the identifier value (pi) is valid */
- if ((pi_major > info->len-1) ||
- (pi_minor > info->tables[pi_major].len-1))
- {
- pr_debug("%s(), no handler for parameter=0x%02x\n",
- __func__, buf[0]);
-
- /* Skip this parameter */
- return 2 + buf[n + 1]; /* Continue */
- }
-
- /* Lookup the info on how to parse this parameter */
- pi_minor_info = &info->tables[pi_major].pi_minor_call_table[pi_minor];
-
- /* Find expected data type for this parameter identifier (pi)*/
- type = pi_minor_info->type;
-
- pr_debug("%s(), pi=[%d,%d], type=%d\n", __func__,
- pi_major, pi_minor, type);
-
- /* Check if handler has been implemented */
- if (!pi_minor_info->func) {
- net_info_ratelimited("%s: no handler for pi=%#x\n",
- __func__, buf[n]);
- /* Skip this parameter */
- return 2 + buf[n + 1]; /* Continue */
- }
-
- /* Parse parameter value */
- ret = (*pv_extract_table[type & PV_MASK])(self, buf+n, len, buf[n],
- type, pi_minor_info->func);
- return ret;
-}
-
-/*
- * Function irda_param_extract_all (self, buf, len, info)
- *
- * Parse all parameters. If len is correct, then everything should be
- * safe. Returns the number of bytes that was parsed
- *
- */
-int irda_param_extract_all(void *self, __u8 *buf, int len,
- pi_param_info_t *info)
-{
- int ret = -1;
- int n = 0;
-
- IRDA_ASSERT(buf != NULL, return ret;);
- IRDA_ASSERT(info != NULL, return ret;);
-
- /*
- * Parse all parameters. Each parameter must be at least two bytes
- * long or else there is no point in trying to parse it
- */
- while (len > 2) {
- ret = irda_param_extract(self, buf+n, len, info);
- if (ret < 0)
- return ret;
-
- n += ret;
- len -= ret;
- }
- return n;
-}
-EXPORT_SYMBOL(irda_param_extract_all);
diff --git a/drivers/staging/irda/net/qos.c b/drivers/staging/irda/net/qos.c
deleted file mode 100644
index 25ba8509ad3e..000000000000
--- a/drivers/staging/irda/net/qos.c
+++ /dev/null
@@ -1,771 +0,0 @@
-/*********************************************************************
- *
- * Filename: qos.c
- * Version: 1.0
- * Description: IrLAP QoS parameter negotiation
- * Status: Stable
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Tue Sep 9 00:00:26 1997
- * Modified at: Sun Jan 30 14:29:16 2000
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>,
- * All Rights Reserved.
- * Copyright (c) 2000-2001 Jean Tourrilhes <jt@hpl.hp.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, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/export.h>
-
-#include <asm/byteorder.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/parameters.h>
-#include <net/irda/qos.h>
-#include <net/irda/irlap.h>
-#include <net/irda/irlap_frame.h>
-
-/*
- * Maximum values of the baud rate we negotiate with the other end.
- * Most often, you don't have to change that, because Linux-IrDA will
- * use the maximum offered by the link layer, which usually works fine.
- * In some very rare cases, you may want to limit it to lower speeds...
- */
-int sysctl_max_baud_rate = 16000000;
-/*
- * Maximum value of the lap disconnect timer we negotiate with the other end.
- * Most often, the value below represent the best compromise, but some user
- * may want to keep the LAP alive longer or shorter in case of link failure.
- * Remember that the threshold time (early warning) is fixed to 3s...
- */
-int sysctl_max_noreply_time = 12;
-/*
- * Minimum turn time to be applied before transmitting to the peer.
- * Nonzero values (usec) are used as lower limit to the per-connection
- * mtt value which was announced by the other end during negotiation.
- * Might be helpful if the peer device provides too short mtt.
- * Default is 10us which means using the unmodified value given by the
- * peer except if it's 0 (0 is likely a bug in the other stack).
- */
-unsigned int sysctl_min_tx_turn_time = 10;
-/*
- * Maximum data size to be used in transmission in payload of LAP frame.
- * There is a bit of confusion in the IrDA spec :
- * The LAP spec defines the payload of a LAP frame (I field) to be
- * 2048 bytes max (IrLAP 1.1, chapt 6.6.5, p40).
- * On the other hand, the PHY mention frames of 2048 bytes max (IrPHY
- * 1.2, chapt 5.3.2.1, p41). But, this number includes the LAP header
- * (2 bytes), and CRC (32 bits at 4 Mb/s). So, for the I field (LAP
- * payload), that's only 2042 bytes. Oups !
- * My nsc-ircc hardware has troubles receiving 2048 bytes frames at 4 Mb/s,
- * so adjust to 2042... I don't know if this bug applies only for 2048
- * bytes frames or all negotiated frame sizes, but you can use the sysctl
- * to play with this value anyway.
- * Jean II */
-unsigned int sysctl_max_tx_data_size = 2042;
-/*
- * Maximum transmit window, i.e. number of LAP frames between turn-around.
- * This allow to override what the peer told us. Some peers are buggy and
- * don't always support what they tell us.
- * Jean II */
-unsigned int sysctl_max_tx_window = 7;
-
-static int irlap_param_baud_rate(void *instance, irda_param_t *param, int get);
-static int irlap_param_link_disconnect(void *instance, irda_param_t *parm,
- int get);
-static int irlap_param_max_turn_time(void *instance, irda_param_t *param,
- int get);
-static int irlap_param_data_size(void *instance, irda_param_t *param, int get);
-static int irlap_param_window_size(void *instance, irda_param_t *param,
- int get);
-static int irlap_param_additional_bofs(void *instance, irda_param_t *parm,
- int get);
-static int irlap_param_min_turn_time(void *instance, irda_param_t *param,
- int get);
-
-#ifndef CONFIG_IRDA_DYNAMIC_WINDOW
-static __u32 irlap_requested_line_capacity(struct qos_info *qos);
-#endif
-
-static __u32 min_turn_times[] = { 10000, 5000, 1000, 500, 100, 50, 10, 0 }; /* us */
-static __u32 baud_rates[] = { 2400, 9600, 19200, 38400, 57600, 115200, 576000,
- 1152000, 4000000, 16000000 }; /* bps */
-static __u32 data_sizes[] = { 64, 128, 256, 512, 1024, 2048 }; /* bytes */
-static __u32 add_bofs[] = { 48, 24, 12, 5, 3, 2, 1, 0 }; /* bytes */
-static __u32 max_turn_times[] = { 500, 250, 100, 50 }; /* ms */
-static __u32 link_disc_times[] = { 3, 8, 12, 16, 20, 25, 30, 40 }; /* secs */
-
-static __u32 max_line_capacities[10][4] = {
- /* 500 ms 250 ms 100 ms 50 ms (max turn time) */
- { 100, 0, 0, 0 }, /* 2400 bps */
- { 400, 0, 0, 0 }, /* 9600 bps */
- { 800, 0, 0, 0 }, /* 19200 bps */
- { 1600, 0, 0, 0 }, /* 38400 bps */
- { 2360, 0, 0, 0 }, /* 57600 bps */
- { 4800, 2400, 960, 480 }, /* 115200 bps */
- { 28800, 11520, 5760, 2880 }, /* 576000 bps */
- { 57600, 28800, 11520, 5760 }, /* 1152000 bps */
- { 200000, 100000, 40000, 20000 }, /* 4000000 bps */
- { 800000, 400000, 160000, 80000 }, /* 16000000 bps */
-};
-
-static const pi_minor_info_t pi_minor_call_table_type_0[] = {
- { NULL, 0 },
-/* 01 */{ irlap_param_baud_rate, PV_INTEGER | PV_LITTLE_ENDIAN },
- { NULL, 0 },
- { NULL, 0 },
- { NULL, 0 },
- { NULL, 0 },
- { NULL, 0 },
- { NULL, 0 },
-/* 08 */{ irlap_param_link_disconnect, PV_INT_8_BITS }
-};
-
-static const pi_minor_info_t pi_minor_call_table_type_1[] = {
- { NULL, 0 },
- { NULL, 0 },
-/* 82 */{ irlap_param_max_turn_time, PV_INT_8_BITS },
-/* 83 */{ irlap_param_data_size, PV_INT_8_BITS },
-/* 84 */{ irlap_param_window_size, PV_INT_8_BITS },
-/* 85 */{ irlap_param_additional_bofs, PV_INT_8_BITS },
-/* 86 */{ irlap_param_min_turn_time, PV_INT_8_BITS },
-};
-
-static const pi_major_info_t pi_major_call_table[] = {
- { pi_minor_call_table_type_0, 9 },
- { pi_minor_call_table_type_1, 7 },
-};
-
-static pi_param_info_t irlap_param_info = { pi_major_call_table, 2, 0x7f, 7 };
-
-/* ---------------------- LOCAL SUBROUTINES ---------------------- */
-/* Note : we start with a bunch of local subroutines.
- * As the compiler is "one pass", this is the only way to get them to
- * inline properly...
- * Jean II
- */
-/*
- * Function value_index (value, array, size)
- *
- * Returns the index to the value in the specified array
- */
-static inline int value_index(__u32 value, __u32 *array, int size)
-{
- int i;
-
- for (i=0; i < size; i++)
- if (array[i] == value)
- break;
- return i;
-}
-
-/*
- * Function index_value (index, array)
- *
- * Returns value to index in array, easy!
- *
- */
-static inline __u32 index_value(int index, __u32 *array)
-{
- return array[index];
-}
-
-/*
- * Function msb_index (word)
- *
- * Returns index to most significant bit (MSB) in word
- *
- */
-static int msb_index (__u16 word)
-{
- __u16 msb = 0x8000;
- int index = 15; /* Current MSB */
-
- /* Check for buggy peers.
- * Note : there is a small probability that it could be us, but I
- * would expect driver authors to catch that pretty early and be
- * able to check precisely what's going on. If a end user sees this,
- * it's very likely the peer. - Jean II */
- if (word == 0) {
- net_warn_ratelimited("%s(), Detected buggy peer, adjust null PV to 0x1!\n",
- __func__);
- /* The only safe choice (we don't know the array size) */
- word = 0x1;
- }
-
- while (msb) {
- if (word & msb)
- break; /* Found it! */
- msb >>=1;
- index--;
- }
- return index;
-}
-
-/*
- * Function value_lower_bits (value, array)
- *
- * Returns a bit field marking all possibility lower than value.
- */
-static inline int value_lower_bits(__u32 value, __u32 *array, int size, __u16 *field)
-{
- int i;
- __u16 mask = 0x1;
- __u16 result = 0x0;
-
- for (i=0; i < size; i++) {
- /* Add the current value to the bit field, shift mask */
- result |= mask;
- mask <<= 1;
- /* Finished ? */
- if (array[i] >= value)
- break;
- }
- /* Send back a valid index */
- if(i >= size)
- i = size - 1; /* Last item */
- *field = result;
- return i;
-}
-
-/*
- * Function value_highest_bit (value, array)
- *
- * Returns a bit field marking the highest possibility lower than value.
- */
-static inline int value_highest_bit(__u32 value, __u32 *array, int size, __u16 *field)
-{
- int i;
- __u16 mask = 0x1;
- __u16 result = 0x0;
-
- for (i=0; i < size; i++) {
- /* Finished ? */
- if (array[i] <= value)
- break;
- /* Shift mask */
- mask <<= 1;
- }
- /* Set the current value to the bit field */
- result |= mask;
- /* Send back a valid index */
- if(i >= size)
- i = size - 1; /* Last item */
- *field = result;
- return i;
-}
-
-/* -------------------------- MAIN CALLS -------------------------- */
-
-/*
- * Function irda_qos_compute_intersection (qos, new)
- *
- * Compute the intersection of the old QoS capabilities with new ones
- *
- */
-void irda_qos_compute_intersection(struct qos_info *qos, struct qos_info *new)
-{
- IRDA_ASSERT(qos != NULL, return;);
- IRDA_ASSERT(new != NULL, return;);
-
- /* Apply */
- qos->baud_rate.bits &= new->baud_rate.bits;
- qos->window_size.bits &= new->window_size.bits;
- qos->min_turn_time.bits &= new->min_turn_time.bits;
- qos->max_turn_time.bits &= new->max_turn_time.bits;
- qos->data_size.bits &= new->data_size.bits;
- qos->link_disc_time.bits &= new->link_disc_time.bits;
- qos->additional_bofs.bits &= new->additional_bofs.bits;
-
- irda_qos_bits_to_value(qos);
-}
-
-/*
- * Function irda_init_max_qos_capabilies (qos)
- *
- * The purpose of this function is for layers and drivers to be able to
- * set the maximum QoS possible and then "and in" their own limitations
- *
- */
-void irda_init_max_qos_capabilies(struct qos_info *qos)
-{
- int i;
- /*
- * These are the maximum supported values as specified on pages
- * 39-43 in IrLAP
- */
-
- /* Use sysctl to set some configurable values... */
- /* Set configured max speed */
- i = value_lower_bits(sysctl_max_baud_rate, baud_rates, 10,
- &qos->baud_rate.bits);
- sysctl_max_baud_rate = index_value(i, baud_rates);
-
- /* Set configured max disc time */
- i = value_lower_bits(sysctl_max_noreply_time, link_disc_times, 8,
- &qos->link_disc_time.bits);
- sysctl_max_noreply_time = index_value(i, link_disc_times);
-
- /* LSB is first byte, MSB is second byte */
- qos->baud_rate.bits &= 0x03ff;
-
- qos->window_size.bits = 0x7f;
- qos->min_turn_time.bits = 0xff;
- qos->max_turn_time.bits = 0x0f;
- qos->data_size.bits = 0x3f;
- qos->link_disc_time.bits &= 0xff;
- qos->additional_bofs.bits = 0xff;
-}
-EXPORT_SYMBOL(irda_init_max_qos_capabilies);
-
-/*
- * Function irlap_adjust_qos_settings (qos)
- *
- * Adjust QoS settings in case some values are not possible to use because
- * of other settings
- */
-static void irlap_adjust_qos_settings(struct qos_info *qos)
-{
- __u32 line_capacity;
- int index;
-
- /*
- * Make sure the mintt is sensible.
- * Main culprit : Ericsson T39. - Jean II
- */
- if (sysctl_min_tx_turn_time > qos->min_turn_time.value) {
- int i;
-
- net_warn_ratelimited("%s(), Detected buggy peer, adjust mtt to %dus!\n",
- __func__, sysctl_min_tx_turn_time);
-
- /* We don't really need bits, but easier this way */
- i = value_highest_bit(sysctl_min_tx_turn_time, min_turn_times,
- 8, &qos->min_turn_time.bits);
- sysctl_min_tx_turn_time = index_value(i, min_turn_times);
- qos->min_turn_time.value = sysctl_min_tx_turn_time;
- }
-
- /*
- * Not allowed to use a max turn time less than 500 ms if the baudrate
- * is less than 115200
- */
- if ((qos->baud_rate.value < 115200) &&
- (qos->max_turn_time.value < 500))
- {
- pr_debug("%s(), adjusting max turn time from %d to 500 ms\n",
- __func__, qos->max_turn_time.value);
- qos->max_turn_time.value = 500;
- }
-
- /*
- * The data size must be adjusted according to the baud rate and max
- * turn time
- */
- index = value_index(qos->data_size.value, data_sizes, 6);
- line_capacity = irlap_max_line_capacity(qos->baud_rate.value,
- qos->max_turn_time.value);
-
-#ifdef CONFIG_IRDA_DYNAMIC_WINDOW
- while ((qos->data_size.value > line_capacity) && (index > 0)) {
- qos->data_size.value = data_sizes[index--];
- pr_debug("%s(), reducing data size to %d\n",
- __func__, qos->data_size.value);
- }
-#else /* Use method described in section 6.6.11 of IrLAP */
- while (irlap_requested_line_capacity(qos) > line_capacity) {
- IRDA_ASSERT(index != 0, return;);
-
- /* Must be able to send at least one frame */
- if (qos->window_size.value > 1) {
- qos->window_size.value--;
- pr_debug("%s(), reducing window size to %d\n",
- __func__, qos->window_size.value);
- } else if (index > 1) {
- qos->data_size.value = data_sizes[index--];
- pr_debug("%s(), reducing data size to %d\n",
- __func__, qos->data_size.value);
- } else {
- net_warn_ratelimited("%s(), nothing more we can do!\n",
- __func__);
- }
- }
-#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */
- /*
- * Fix tx data size according to user limits - Jean II
- */
- if (qos->data_size.value > sysctl_max_tx_data_size)
- /* Allow non discrete adjustement to avoid losing capacity */
- qos->data_size.value = sysctl_max_tx_data_size;
- /*
- * Override Tx window if user request it. - Jean II
- */
- if (qos->window_size.value > sysctl_max_tx_window)
- qos->window_size.value = sysctl_max_tx_window;
-}
-
-/*
- * Function irlap_negotiate (qos_device, qos_session, skb)
- *
- * Negotiate QoS values, not really that much negotiation :-)
- * We just set the QoS capabilities for the peer station
- *
- */
-int irlap_qos_negotiate(struct irlap_cb *self, struct sk_buff *skb)
-{
- int ret;
-
- ret = irda_param_extract_all(self, skb->data, skb->len,
- &irlap_param_info);
-
- /* Convert the negotiated bits to values */
- irda_qos_bits_to_value(&self->qos_tx);
- irda_qos_bits_to_value(&self->qos_rx);
-
- irlap_adjust_qos_settings(&self->qos_tx);
-
- pr_debug("Setting BAUD_RATE to %d bps.\n",
- self->qos_tx.baud_rate.value);
- pr_debug("Setting DATA_SIZE to %d bytes\n",
- self->qos_tx.data_size.value);
- pr_debug("Setting WINDOW_SIZE to %d\n",
- self->qos_tx.window_size.value);
- pr_debug("Setting XBOFS to %d\n",
- self->qos_tx.additional_bofs.value);
- pr_debug("Setting MAX_TURN_TIME to %d ms.\n",
- self->qos_tx.max_turn_time.value);
- pr_debug("Setting MIN_TURN_TIME to %d usecs.\n",
- self->qos_tx.min_turn_time.value);
- pr_debug("Setting LINK_DISC to %d secs.\n",
- self->qos_tx.link_disc_time.value);
- return ret;
-}
-
-/*
- * Function irlap_insert_negotiation_params (qos, fp)
- *
- * Insert QoS negotiaion pararameters into frame
- *
- */
-int irlap_insert_qos_negotiation_params(struct irlap_cb *self,
- struct sk_buff *skb)
-{
- int ret;
-
- /* Insert data rate */
- ret = irda_param_insert(self, PI_BAUD_RATE, skb_tail_pointer(skb),
- skb_tailroom(skb), &irlap_param_info);
- if (ret < 0)
- return ret;
- skb_put(skb, ret);
-
- /* Insert max turnaround time */
- ret = irda_param_insert(self, PI_MAX_TURN_TIME, skb_tail_pointer(skb),
- skb_tailroom(skb), &irlap_param_info);
- if (ret < 0)
- return ret;
- skb_put(skb, ret);
-
- /* Insert data size */
- ret = irda_param_insert(self, PI_DATA_SIZE, skb_tail_pointer(skb),
- skb_tailroom(skb), &irlap_param_info);
- if (ret < 0)
- return ret;
- skb_put(skb, ret);
-
- /* Insert window size */
- ret = irda_param_insert(self, PI_WINDOW_SIZE, skb_tail_pointer(skb),
- skb_tailroom(skb), &irlap_param_info);
- if (ret < 0)
- return ret;
- skb_put(skb, ret);
-
- /* Insert additional BOFs */
- ret = irda_param_insert(self, PI_ADD_BOFS, skb_tail_pointer(skb),
- skb_tailroom(skb), &irlap_param_info);
- if (ret < 0)
- return ret;
- skb_put(skb, ret);
-
- /* Insert minimum turnaround time */
- ret = irda_param_insert(self, PI_MIN_TURN_TIME, skb_tail_pointer(skb),
- skb_tailroom(skb), &irlap_param_info);
- if (ret < 0)
- return ret;
- skb_put(skb, ret);
-
- /* Insert link disconnect/threshold time */
- ret = irda_param_insert(self, PI_LINK_DISC, skb_tail_pointer(skb),
- skb_tailroom(skb), &irlap_param_info);
- if (ret < 0)
- return ret;
- skb_put(skb, ret);
-
- return 0;
-}
-
-/*
- * Function irlap_param_baud_rate (instance, param, get)
- *
- * Negotiate data-rate
- *
- */
-static int irlap_param_baud_rate(void *instance, irda_param_t *param, int get)
-{
- __u16 final;
-
- struct irlap_cb *self = (struct irlap_cb *) instance;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
- if (get) {
- param->pv.i = self->qos_rx.baud_rate.bits;
- pr_debug("%s(), baud rate = 0x%02x\n",
- __func__, param->pv.i);
- } else {
- /*
- * Stations must agree on baud rate, so calculate
- * intersection
- */
- pr_debug("Requested BAUD_RATE: 0x%04x\n", (__u16)param->pv.i);
- final = (__u16) param->pv.i & self->qos_rx.baud_rate.bits;
-
- pr_debug("Final BAUD_RATE: 0x%04x\n", final);
- self->qos_tx.baud_rate.bits = final;
- self->qos_rx.baud_rate.bits = final;
- }
-
- return 0;
-}
-
-/*
- * Function irlap_param_link_disconnect (instance, param, get)
- *
- * Negotiate link disconnect/threshold time.
- *
- */
-static int irlap_param_link_disconnect(void *instance, irda_param_t *param,
- int get)
-{
- __u16 final;
-
- struct irlap_cb *self = (struct irlap_cb *) instance;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
- if (get)
- param->pv.i = self->qos_rx.link_disc_time.bits;
- else {
- /*
- * Stations must agree on link disconnect/threshold
- * time.
- */
- pr_debug("LINK_DISC: %02x\n", (__u8)param->pv.i);
- final = (__u8) param->pv.i & self->qos_rx.link_disc_time.bits;
-
- pr_debug("Final LINK_DISC: %02x\n", final);
- self->qos_tx.link_disc_time.bits = final;
- self->qos_rx.link_disc_time.bits = final;
- }
- return 0;
-}
-
-/*
- * Function irlap_param_max_turn_time (instance, param, get)
- *
- * Negotiate the maximum turnaround time. This is a type 1 parameter and
- * will be negotiated independently for each station
- *
- */
-static int irlap_param_max_turn_time(void *instance, irda_param_t *param,
- int get)
-{
- struct irlap_cb *self = (struct irlap_cb *) instance;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
- if (get)
- param->pv.i = self->qos_rx.max_turn_time.bits;
- else
- self->qos_tx.max_turn_time.bits = (__u8) param->pv.i;
-
- return 0;
-}
-
-/*
- * Function irlap_param_data_size (instance, param, get)
- *
- * Negotiate the data size. This is a type 1 parameter and
- * will be negotiated independently for each station
- *
- */
-static int irlap_param_data_size(void *instance, irda_param_t *param, int get)
-{
- struct irlap_cb *self = (struct irlap_cb *) instance;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
- if (get)
- param->pv.i = self->qos_rx.data_size.bits;
- else
- self->qos_tx.data_size.bits = (__u8) param->pv.i;
-
- return 0;
-}
-
-/*
- * Function irlap_param_window_size (instance, param, get)
- *
- * Negotiate the window size. This is a type 1 parameter and
- * will be negotiated independently for each station
- *
- */
-static int irlap_param_window_size(void *instance, irda_param_t *param,
- int get)
-{
- struct irlap_cb *self = (struct irlap_cb *) instance;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
- if (get)
- param->pv.i = self->qos_rx.window_size.bits;
- else
- self->qos_tx.window_size.bits = (__u8) param->pv.i;
-
- return 0;
-}
-
-/*
- * Function irlap_param_additional_bofs (instance, param, get)
- *
- * Negotiate additional BOF characters. This is a type 1 parameter and
- * will be negotiated independently for each station.
- */
-static int irlap_param_additional_bofs(void *instance, irda_param_t *param, int get)
-{
- struct irlap_cb *self = (struct irlap_cb *) instance;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
- if (get)
- param->pv.i = self->qos_rx.additional_bofs.bits;
- else
- self->qos_tx.additional_bofs.bits = (__u8) param->pv.i;
-
- return 0;
-}
-
-/*
- * Function irlap_param_min_turn_time (instance, param, get)
- *
- * Negotiate the minimum turn around time. This is a type 1 parameter and
- * will be negotiated independently for each station
- */
-static int irlap_param_min_turn_time(void *instance, irda_param_t *param,
- int get)
-{
- struct irlap_cb *self = (struct irlap_cb *) instance;
-
- IRDA_ASSERT(self != NULL, return -1;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
- if (get)
- param->pv.i = self->qos_rx.min_turn_time.bits;
- else
- self->qos_tx.min_turn_time.bits = (__u8) param->pv.i;
-
- return 0;
-}
-
-/*
- * Function irlap_max_line_capacity (speed, max_turn_time, min_turn_time)
- *
- * Calculate the maximum line capacity
- *
- */
-__u32 irlap_max_line_capacity(__u32 speed, __u32 max_turn_time)
-{
- __u32 line_capacity;
- int i,j;
-
- pr_debug("%s(), speed=%d, max_turn_time=%d\n",
- __func__, speed, max_turn_time);
-
- i = value_index(speed, baud_rates, 10);
- j = value_index(max_turn_time, max_turn_times, 4);
-
- IRDA_ASSERT(((i >=0) && (i <10)), return 0;);
- IRDA_ASSERT(((j >=0) && (j <4)), return 0;);
-
- line_capacity = max_line_capacities[i][j];
-
- pr_debug("%s(), line capacity=%d bytes\n",
- __func__, line_capacity);
-
- return line_capacity;
-}
-
-#ifndef CONFIG_IRDA_DYNAMIC_WINDOW
-static __u32 irlap_requested_line_capacity(struct qos_info *qos)
-{
- __u32 line_capacity;
-
- line_capacity = qos->window_size.value *
- (qos->data_size.value + 6 + qos->additional_bofs.value) +
- irlap_min_turn_time_in_bytes(qos->baud_rate.value,
- qos->min_turn_time.value);
-
- pr_debug("%s(), requested line capacity=%d\n",
- __func__, line_capacity);
-
- return line_capacity;
-}
-#endif
-
-void irda_qos_bits_to_value(struct qos_info *qos)
-{
- int index;
-
- IRDA_ASSERT(qos != NULL, return;);
-
- index = msb_index(qos->baud_rate.bits);
- qos->baud_rate.value = baud_rates[index];
-
- index = msb_index(qos->data_size.bits);
- qos->data_size.value = data_sizes[index];
-
- index = msb_index(qos->window_size.bits);
- qos->window_size.value = index+1;
-
- index = msb_index(qos->min_turn_time.bits);
- qos->min_turn_time.value = min_turn_times[index];
-
- index = msb_index(qos->max_turn_time.bits);
- qos->max_turn_time.value = max_turn_times[index];
-
- index = msb_index(qos->link_disc_time.bits);
- qos->link_disc_time.value = link_disc_times[index];
-
- index = msb_index(qos->additional_bofs.bits);
- qos->additional_bofs.value = add_bofs[index];
-}
-EXPORT_SYMBOL(irda_qos_bits_to_value);
diff --git a/drivers/staging/irda/net/timer.c b/drivers/staging/irda/net/timer.c
deleted file mode 100644
index cf00c0d848aa..000000000000
--- a/drivers/staging/irda/net/timer.c
+++ /dev/null
@@ -1,231 +0,0 @@
-/*********************************************************************
- *
- * Filename: timer.c
- * Version:
- * Description:
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sat Aug 16 00:59:29 1997
- * Modified at: Wed Dec 8 12:50:34 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1997, 1999 Dag Brattli <dagb@cs.uit.no>,
- * All Rights Reserved.
- * Copyright (c) 2000-2002 Jean Tourrilhes <jt@hpl.hp.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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/delay.h>
-
-#include <net/irda/timer.h>
-#include <net/irda/irda.h>
-#include <net/irda/irda_device.h>
-#include <net/irda/irlap.h>
-#include <net/irda/irlmp.h>
-
-extern int sysctl_slot_timeout;
-
-static void irlap_slot_timer_expired(struct timer_list *t);
-static void irlap_query_timer_expired(struct timer_list *t);
-static void irlap_final_timer_expired(struct timer_list *t);
-static void irlap_wd_timer_expired(struct timer_list *t);
-static void irlap_backoff_timer_expired(struct timer_list *t);
-static void irlap_media_busy_expired(struct timer_list *t);
-
-void irlap_start_slot_timer(struct irlap_cb *self, int timeout)
-{
- irda_start_timer(&self->slot_timer, timeout,
- irlap_slot_timer_expired);
-}
-
-void irlap_start_query_timer(struct irlap_cb *self, int S, int s)
-{
- int timeout;
-
- /* Calculate when the peer discovery should end. Normally, we
- * get the end-of-discovery frame, so this is just in case
- * we miss it.
- * Basically, we multiply the number of remaining slots by our
- * slot time, plus add some extra time to properly receive the last
- * discovery packet (which is longer due to extra discovery info),
- * to avoid messing with for incoming connections requests and
- * to accommodate devices that perform discovery slower than us.
- * Jean II */
- timeout = msecs_to_jiffies(sysctl_slot_timeout) * (S - s)
- + XIDEXTRA_TIMEOUT + SMALLBUSY_TIMEOUT;
-
- /* Set or re-set the timer. We reset the timer for each received
- * discovery query, which allow us to automatically adjust to
- * the speed of the peer discovery (faster or slower). Jean II */
- irda_start_timer(&self->query_timer, timeout,
- irlap_query_timer_expired);
-}
-
-void irlap_start_final_timer(struct irlap_cb *self, int timeout)
-{
- irda_start_timer(&self->final_timer, timeout,
- irlap_final_timer_expired);
-}
-
-void irlap_start_wd_timer(struct irlap_cb *self, int timeout)
-{
- irda_start_timer(&self->wd_timer, timeout,
- irlap_wd_timer_expired);
-}
-
-void irlap_start_backoff_timer(struct irlap_cb *self, int timeout)
-{
- irda_start_timer(&self->backoff_timer, timeout,
- irlap_backoff_timer_expired);
-}
-
-void irlap_start_mbusy_timer(struct irlap_cb *self, int timeout)
-{
- irda_start_timer(&self->media_busy_timer, timeout,
- irlap_media_busy_expired);
-}
-
-void irlap_stop_mbusy_timer(struct irlap_cb *self)
-{
- /* If timer is activated, kill it! */
- del_timer(&self->media_busy_timer);
-
- /* If we are in NDM, there is a bunch of events in LAP that
- * that be pending due to the media_busy condition, such as
- * CONNECT_REQUEST and SEND_UI_FRAME. If we don't generate
- * an event, they will wait forever...
- * Jean II */
- if (self->state == LAP_NDM)
- irlap_do_event(self, MEDIA_BUSY_TIMER_EXPIRED, NULL, NULL);
-}
-
-void irlmp_start_watchdog_timer(struct lsap_cb *self, int timeout)
-{
- irda_start_timer(&self->watchdog_timer, timeout,
- irlmp_watchdog_timer_expired);
-}
-
-void irlmp_start_discovery_timer(struct irlmp_cb *self, int timeout)
-{
- irda_start_timer(&self->discovery_timer, timeout,
- irlmp_discovery_timer_expired);
-}
-
-void irlmp_start_idle_timer(struct lap_cb *self, int timeout)
-{
- irda_start_timer(&self->idle_timer, timeout,
- irlmp_idle_timer_expired);
-}
-
-void irlmp_stop_idle_timer(struct lap_cb *self)
-{
- /* If timer is activated, kill it! */
- del_timer(&self->idle_timer);
-}
-
-/*
- * Function irlap_slot_timer_expired (data)
- *
- * IrLAP slot timer has expired
- *
- */
-static void irlap_slot_timer_expired(struct timer_list *t)
-{
- struct irlap_cb *self = from_timer(self, t, slot_timer);
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
- irlap_do_event(self, SLOT_TIMER_EXPIRED, NULL, NULL);
-}
-
-/*
- * Function irlap_query_timer_expired (data)
- *
- * IrLAP query timer has expired
- *
- */
-static void irlap_query_timer_expired(struct timer_list *t)
-{
- struct irlap_cb *self = from_timer(self, t, query_timer);
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
- irlap_do_event(self, QUERY_TIMER_EXPIRED, NULL, NULL);
-}
-
-/*
- * Function irda_final_timer_expired (data)
- *
- *
- *
- */
-static void irlap_final_timer_expired(struct timer_list *t)
-{
- struct irlap_cb *self = from_timer(self, t, final_timer);
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
- irlap_do_event(self, FINAL_TIMER_EXPIRED, NULL, NULL);
-}
-
-/*
- * Function irda_wd_timer_expired (data)
- *
- *
- *
- */
-static void irlap_wd_timer_expired(struct timer_list *t)
-{
- struct irlap_cb *self = from_timer(self, t, wd_timer);
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
- irlap_do_event(self, WD_TIMER_EXPIRED, NULL, NULL);
-}
-
-/*
- * Function irda_backoff_timer_expired (data)
- *
- *
- *
- */
-static void irlap_backoff_timer_expired(struct timer_list *t)
-{
- struct irlap_cb *self = from_timer(self, t, backoff_timer);
-
- IRDA_ASSERT(self != NULL, return;);
- IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
- irlap_do_event(self, BACKOFF_TIMER_EXPIRED, NULL, NULL);
-}
-
-
-/*
- * Function irtty_media_busy_expired (data)
- *
- *
- */
-static void irlap_media_busy_expired(struct timer_list *t)
-{
- struct irlap_cb *self = from_timer(self, t, media_busy_timer);
-
- IRDA_ASSERT(self != NULL, return;);
-
- irda_device_set_media_busy(self->netdev, FALSE);
- /* Note : the LAP event will be send in irlap_stop_mbusy_timer(),
- * to catch other cases where the flag is cleared (for example
- * after a discovery) - Jean II */
-}
diff --git a/drivers/staging/irda/net/wrapper.c b/drivers/staging/irda/net/wrapper.c
deleted file mode 100644
index 40a0f993bf13..000000000000
--- a/drivers/staging/irda/net/wrapper.c
+++ /dev/null
@@ -1,492 +0,0 @@
-/*********************************************************************
- *
- * Filename: wrapper.c
- * Version: 1.2
- * Description: IrDA SIR async wrapper layer
- * Status: Stable
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Mon Aug 4 20:40:53 1997
- * Modified at: Fri Jan 28 13:21:09 2000
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- * Modified at: Fri May 28 3:11 CST 1999
- * Modified by: Horst von Brand <vonbrand@sleipnir.valparaiso.cl>
- *
- * Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>,
- * All Rights Reserved.
- * Copyright (c) 2000-2002 Jean Tourrilhes <jt@hpl.hp.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.
- *
- * Neither Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/skbuff.h>
-#include <linux/string.h>
-#include <linux/module.h>
-#include <asm/byteorder.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/wrapper.h>
-#include <net/irda/crc.h>
-#include <net/irda/irlap.h>
-#include <net/irda/irlap_frame.h>
-#include <net/irda/irda_device.h>
-
-/************************** FRAME WRAPPING **************************/
-/*
- * Unwrap and unstuff SIR frames
- *
- * Note : at FIR and MIR, HDLC framing is used and usually handled
- * by the controller, so we come here only for SIR... Jean II
- */
-
-/*
- * Function stuff_byte (byte, buf)
- *
- * Byte stuff one single byte and put the result in buffer pointed to by
- * buf. The buffer must at all times be able to have two bytes inserted.
- *
- * This is in a tight loop, better inline it, so need to be prior to callers.
- * (2000 bytes on P6 200MHz, non-inlined ~370us, inline ~170us) - Jean II
- */
-static inline int stuff_byte(__u8 byte, __u8 *buf)
-{
- switch (byte) {
- case BOF: /* FALLTHROUGH */
- case EOF: /* FALLTHROUGH */
- case CE:
- /* Insert transparently coded */
- buf[0] = CE; /* Send link escape */
- buf[1] = byte^IRDA_TRANS; /* Complement bit 5 */
- return 2;
- /* break; */
- default:
- /* Non-special value, no transparency required */
- buf[0] = byte;
- return 1;
- /* break; */
- }
-}
-
-/*
- * Function async_wrap (skb, *tx_buff, buffsize)
- *
- * Makes a new buffer with wrapping and stuffing, should check that
- * we don't get tx buffer overflow.
- */
-int async_wrap_skb(struct sk_buff *skb, __u8 *tx_buff, int buffsize)
-{
- struct irda_skb_cb *cb = (struct irda_skb_cb *) skb->cb;
- int xbofs;
- int i;
- int n;
- union {
- __u16 value;
- __u8 bytes[2];
- } fcs;
-
- /* Initialize variables */
- fcs.value = INIT_FCS;
- n = 0;
-
- /*
- * Send XBOF's for required min. turn time and for the negotiated
- * additional XBOFS
- */
-
- if (cb->magic != LAP_MAGIC) {
- /*
- * This will happen for all frames sent from user-space.
- * Nothing to worry about, but we set the default number of
- * BOF's
- */
- pr_debug("%s(), wrong magic in skb!\n", __func__);
- xbofs = 10;
- } else
- xbofs = cb->xbofs + cb->xbofs_delay;
-
- pr_debug("%s(), xbofs=%d\n", __func__, xbofs);
-
- /* Check that we never use more than 115 + 48 xbofs */
- if (xbofs > 163) {
- pr_debug("%s(), too many xbofs (%d)\n", __func__,
- xbofs);
- xbofs = 163;
- }
-
- memset(tx_buff + n, XBOF, xbofs);
- n += xbofs;
-
- /* Start of packet character BOF */
- tx_buff[n++] = BOF;
-
- /* Insert frame and calc CRC */
- for (i=0; i < skb->len; i++) {
- /*
- * Check for the possibility of tx buffer overflow. We use
- * bufsize-5 since the maximum number of bytes that can be
- * transmitted after this point is 5.
- */
- if(n >= (buffsize-5)) {
- net_err_ratelimited("%s(), tx buffer overflow (n=%d)\n",
- __func__, n);
- return n;
- }
-
- n += stuff_byte(skb->data[i], tx_buff+n);
- fcs.value = irda_fcs(fcs.value, skb->data[i]);
- }
-
- /* Insert CRC in little endian format (LSB first) */
- fcs.value = ~fcs.value;
-#ifdef __LITTLE_ENDIAN
- n += stuff_byte(fcs.bytes[0], tx_buff+n);
- n += stuff_byte(fcs.bytes[1], tx_buff+n);
-#else /* ifdef __BIG_ENDIAN */
- n += stuff_byte(fcs.bytes[1], tx_buff+n);
- n += stuff_byte(fcs.bytes[0], tx_buff+n);
-#endif
- tx_buff[n++] = EOF;
-
- return n;
-}
-EXPORT_SYMBOL(async_wrap_skb);
-
-/************************* FRAME UNWRAPPING *************************/
-/*
- * Unwrap and unstuff SIR frames
- *
- * Complete rewrite by Jean II :
- * More inline, faster, more compact, more logical. Jean II
- * (16 bytes on P6 200MHz, old 5 to 7 us, new 4 to 6 us)
- * (24 bytes on P6 200MHz, old 9 to 10 us, new 7 to 8 us)
- * (for reference, 115200 b/s is 1 byte every 69 us)
- * And reduce wrapper.o by ~900B in the process ;-)
- *
- * Then, we have the addition of ZeroCopy, which is optional
- * (i.e. the driver must initiate it) and improve final processing.
- * (2005 B frame + EOF on P6 200MHz, without 30 to 50 us, with 10 to 25 us)
- *
- * Note : at FIR and MIR, HDLC framing is used and usually handled
- * by the controller, so we come here only for SIR... Jean II
- */
-
-/*
- * We can also choose where we want to do the CRC calculation. We can
- * do it "inline", as we receive the bytes, or "postponed", when
- * receiving the End-Of-Frame.
- * (16 bytes on P6 200MHz, inlined 4 to 6 us, postponed 4 to 5 us)
- * (24 bytes on P6 200MHz, inlined 7 to 8 us, postponed 5 to 7 us)
- * With ZeroCopy :
- * (2005 B frame on P6 200MHz, inlined 10 to 25 us, postponed 140 to 180 us)
- * Without ZeroCopy :
- * (2005 B frame on P6 200MHz, inlined 30 to 50 us, postponed 150 to 180 us)
- * (Note : numbers taken with irq disabled)
- *
- * From those numbers, it's not clear which is the best strategy, because
- * we end up running through a lot of data one way or another (i.e. cache
- * misses). I personally prefer to avoid the huge latency spike of the
- * "postponed" solution, because it come just at the time when we have
- * lot's of protocol processing to do and it will hurt our ability to
- * reach low link turnaround times... Jean II
- */
-//#define POSTPONE_RX_CRC
-
-/*
- * Function async_bump (buf, len, stats)
- *
- * Got a frame, make a copy of it, and pass it up the stack! We can try
- * to inline it since it's only called from state_inside_frame
- */
-static inline void
-async_bump(struct net_device *dev,
- struct net_device_stats *stats,
- iobuff_t *rx_buff)
-{
- struct sk_buff *newskb;
- struct sk_buff *dataskb;
- int docopy;
-
- /* Check if we need to copy the data to a new skb or not.
- * If the driver doesn't use ZeroCopy Rx, we have to do it.
- * With ZeroCopy Rx, the rx_buff already point to a valid
- * skb. But, if the frame is small, it is more efficient to
- * copy it to save memory (copy will be fast anyway - that's
- * called Rx-copy-break). Jean II */
- docopy = ((rx_buff->skb == NULL) ||
- (rx_buff->len < IRDA_RX_COPY_THRESHOLD));
-
- /* Allocate a new skb */
- newskb = dev_alloc_skb(docopy ? rx_buff->len + 1 : rx_buff->truesize);
- if (!newskb) {
- stats->rx_dropped++;
- /* We could deliver the current skb if doing ZeroCopy Rx,
- * but this would stall the Rx path. Better drop the
- * packet... Jean II */
- return;
- }
-
- /* Align IP header to 20 bytes (i.e. increase skb->data)
- * Note this is only useful with IrLAN, as PPP has a variable
- * header size (2 or 1 bytes) - Jean II */
- skb_reserve(newskb, 1);
-
- if(docopy) {
- /* Copy data without CRC (length already checked) */
- skb_copy_to_linear_data(newskb, rx_buff->data,
- rx_buff->len - 2);
- /* Deliver this skb */
- dataskb = newskb;
- } else {
- /* We are using ZeroCopy. Deliver old skb */
- dataskb = rx_buff->skb;
- /* And hook the new skb to the rx_buff */
- rx_buff->skb = newskb;
- rx_buff->head = newskb->data; /* NOT newskb->head */
- //printk(KERN_DEBUG "ZeroCopy : len = %d, dataskb = %p, newskb = %p\n", rx_buff->len, dataskb, newskb);
- }
-
- /* Set proper length on skb (without CRC) */
- skb_put(dataskb, rx_buff->len - 2);
-
- /* Feed it to IrLAP layer */
- dataskb->dev = dev;
- skb_reset_mac_header(dataskb);
- dataskb->protocol = htons(ETH_P_IRDA);
-
- netif_rx(dataskb);
-
- stats->rx_packets++;
- stats->rx_bytes += rx_buff->len;
-
- /* Clean up rx_buff (redundant with async_unwrap_bof() ???) */
- rx_buff->data = rx_buff->head;
- rx_buff->len = 0;
-}
-
-/*
- * Function async_unwrap_bof(dev, byte)
- *
- * Handle Beginning Of Frame character received within a frame
- *
- */
-static inline void
-async_unwrap_bof(struct net_device *dev,
- struct net_device_stats *stats,
- iobuff_t *rx_buff, __u8 byte)
-{
- switch(rx_buff->state) {
- case LINK_ESCAPE:
- case INSIDE_FRAME:
- /* Not supposed to happen, the previous frame is not
- * finished - Jean II */
- pr_debug("%s(), Discarding incomplete frame\n",
- __func__);
- stats->rx_errors++;
- stats->rx_missed_errors++;
- irda_device_set_media_busy(dev, TRUE);
- break;
-
- case OUTSIDE_FRAME:
- case BEGIN_FRAME:
- default:
- /* We may receive multiple BOF at the start of frame */
- break;
- }
-
- /* Now receiving frame */
- rx_buff->state = BEGIN_FRAME;
- rx_buff->in_frame = TRUE;
-
- /* Time to initialize receive buffer */
- rx_buff->data = rx_buff->head;
- rx_buff->len = 0;
- rx_buff->fcs = INIT_FCS;
-}
-
-/*
- * Function async_unwrap_eof(dev, byte)
- *
- * Handle End Of Frame character received within a frame
- *
- */
-static inline void
-async_unwrap_eof(struct net_device *dev,
- struct net_device_stats *stats,
- iobuff_t *rx_buff, __u8 byte)
-{
-#ifdef POSTPONE_RX_CRC
- int i;
-#endif
-
- switch(rx_buff->state) {
- case OUTSIDE_FRAME:
- /* Probably missed the BOF */
- stats->rx_errors++;
- stats->rx_missed_errors++;
- irda_device_set_media_busy(dev, TRUE);
- break;
-
- case BEGIN_FRAME:
- case LINK_ESCAPE:
- case INSIDE_FRAME:
- default:
- /* Note : in the case of BEGIN_FRAME and LINK_ESCAPE,
- * the fcs will most likely not match and generate an
- * error, as expected - Jean II */
- rx_buff->state = OUTSIDE_FRAME;
- rx_buff->in_frame = FALSE;
-
-#ifdef POSTPONE_RX_CRC
- /* If we haven't done the CRC as we receive bytes, we
- * must do it now... Jean II */
- for(i = 0; i < rx_buff->len; i++)
- rx_buff->fcs = irda_fcs(rx_buff->fcs,
- rx_buff->data[i]);
-#endif
-
- /* Test FCS and signal success if the frame is good */
- if (rx_buff->fcs == GOOD_FCS) {
- /* Deliver frame */
- async_bump(dev, stats, rx_buff);
- break;
- } else {
- /* Wrong CRC, discard frame! */
- irda_device_set_media_busy(dev, TRUE);
-
- pr_debug("%s(), crc error\n", __func__);
- stats->rx_errors++;
- stats->rx_crc_errors++;
- }
- break;
- }
-}
-
-/*
- * Function async_unwrap_ce(dev, byte)
- *
- * Handle Character Escape character received within a frame
- *
- */
-static inline void
-async_unwrap_ce(struct net_device *dev,
- struct net_device_stats *stats,
- iobuff_t *rx_buff, __u8 byte)
-{
- switch(rx_buff->state) {
- case OUTSIDE_FRAME:
- /* Activate carrier sense */
- irda_device_set_media_busy(dev, TRUE);
- break;
-
- case LINK_ESCAPE:
- net_warn_ratelimited("%s: state not defined\n", __func__);
- break;
-
- case BEGIN_FRAME:
- case INSIDE_FRAME:
- default:
- /* Stuffed byte coming */
- rx_buff->state = LINK_ESCAPE;
- break;
- }
-}
-
-/*
- * Function async_unwrap_other(dev, byte)
- *
- * Handle other characters received within a frame
- *
- */
-static inline void
-async_unwrap_other(struct net_device *dev,
- struct net_device_stats *stats,
- iobuff_t *rx_buff, __u8 byte)
-{
- switch(rx_buff->state) {
- /* This is on the critical path, case are ordered by
- * probability (most frequent first) - Jean II */
- case INSIDE_FRAME:
- /* Must be the next byte of the frame */
- if (rx_buff->len < rx_buff->truesize) {
- rx_buff->data[rx_buff->len++] = byte;
-#ifndef POSTPONE_RX_CRC
- rx_buff->fcs = irda_fcs(rx_buff->fcs, byte);
-#endif
- } else {
- pr_debug("%s(), Rx buffer overflow, aborting\n",
- __func__);
- rx_buff->state = OUTSIDE_FRAME;
- }
- break;
-
- case LINK_ESCAPE:
- /*
- * Stuffed char, complement bit 5 of byte
- * following CE, IrLAP p.114
- */
- byte ^= IRDA_TRANS;
- if (rx_buff->len < rx_buff->truesize) {
- rx_buff->data[rx_buff->len++] = byte;
-#ifndef POSTPONE_RX_CRC
- rx_buff->fcs = irda_fcs(rx_buff->fcs, byte);
-#endif
- rx_buff->state = INSIDE_FRAME;
- } else {
- pr_debug("%s(), Rx buffer overflow, aborting\n",
- __func__);
- rx_buff->state = OUTSIDE_FRAME;
- }
- break;
-
- case OUTSIDE_FRAME:
- /* Activate carrier sense */
- if(byte != XBOF)
- irda_device_set_media_busy(dev, TRUE);
- break;
-
- case BEGIN_FRAME:
- default:
- rx_buff->data[rx_buff->len++] = byte;
-#ifndef POSTPONE_RX_CRC
- rx_buff->fcs = irda_fcs(rx_buff->fcs, byte);
-#endif
- rx_buff->state = INSIDE_FRAME;
- break;
- }
-}
-
-/*
- * Function async_unwrap_char (dev, rx_buff, byte)
- *
- * Parse and de-stuff frame received from the IrDA-port
- *
- * This is the main entry point for SIR drivers.
- */
-void async_unwrap_char(struct net_device *dev,
- struct net_device_stats *stats,
- iobuff_t *rx_buff, __u8 byte)
-{
- switch(byte) {
- case CE:
- async_unwrap_ce(dev, stats, rx_buff, byte);
- break;
- case BOF:
- async_unwrap_bof(dev, stats, rx_buff, byte);
- break;
- case EOF:
- async_unwrap_eof(dev, stats, rx_buff, byte);
- break;
- default:
- async_unwrap_other(dev, stats, rx_buff, byte);
- break;
- }
-}
-EXPORT_SYMBOL(async_unwrap_char);
-
diff --git a/drivers/staging/ks7010/Makefile b/drivers/staging/ks7010/Makefile
index 69fcf8d655c7..07dc16cc86f5 100644
--- a/drivers/staging/ks7010/Makefile
+++ b/drivers/staging/ks7010/Makefile
@@ -1,4 +1,3 @@
obj-$(CONFIG_KS7010) += ks7010.o
-ccflags-y += -DKS_WLAN_DEBUG=0
ks7010-y := michael_mic.o ks_hostif.o ks_wlan_net.o ks7010_sdio.o
diff --git a/drivers/staging/ks7010/eap_packet.h b/drivers/staging/ks7010/eap_packet.h
index dca2a142e834..58c2a3dafca2 100644
--- a/drivers/staging/ks7010/eap_packet.h
+++ b/drivers/staging/ks7010/eap_packet.h
@@ -3,12 +3,8 @@
#define EAP_PACKET_H
#include <linux/compiler.h>
-
-#define WBIT(n) (1 << (n))
-
-#ifndef ETH_ALEN
-#define ETH_ALEN 6
-#endif
+#include <linux/bitops.h>
+#include <uapi/linux/if_ether.h>
#define ETHER_HDR_SIZE 20
@@ -20,9 +16,6 @@ struct ether_hdr {
unsigned char h_command;
unsigned char h_vendor_id[3];
__be16 h_proto; /* packet type ID field */
-#define ETHER_PROTOCOL_TYPE_EAP 0x888e
-#define ETHER_PROTOCOL_TYPE_IP 0x0800
-#define ETHER_PROTOCOL_TYPE_ARP 0x0806
/* followed by length octets of data */
} __packed;
@@ -104,23 +97,23 @@ struct wpa_eapol_key {
/* followed by key_data_length bytes of key_data */
} __packed;
-#define WPA_KEY_INFO_TYPE_MASK (WBIT(0) | WBIT(1) | WBIT(2))
-#define WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 WBIT(0)
-#define WPA_KEY_INFO_TYPE_HMAC_SHA1_AES WBIT(1)
-#define WPA_KEY_INFO_KEY_TYPE WBIT(3) /* 1 = Pairwise, 0 = Group key */
+#define WPA_KEY_INFO_TYPE_MASK GENMASK(2, 0)
+#define WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 BIT(0)
+#define WPA_KEY_INFO_TYPE_HMAC_SHA1_AES BIT(1)
+#define WPA_KEY_INFO_KEY_TYPE BIT(3) /* 1 = Pairwise, 0 = Group key */
/* bit4..5 is used in WPA, but is reserved in IEEE 802.11i/RSN */
-#define WPA_KEY_INFO_KEY_INDEX_MASK (WBIT(4) | WBIT(5))
+#define WPA_KEY_INFO_KEY_INDEX_MASK GENMASK(5, 4)
#define WPA_KEY_INFO_KEY_INDEX_SHIFT 4
-#define WPA_KEY_INFO_INSTALL WBIT(6) /* pairwise */
-#define WPA_KEY_INFO_TXRX WBIT(6) /* group */
-#define WPA_KEY_INFO_ACK WBIT(7)
-#define WPA_KEY_INFO_MIC WBIT(8)
-#define WPA_KEY_INFO_SECURE WBIT(9)
-#define WPA_KEY_INFO_ERROR WBIT(10)
-#define WPA_KEY_INFO_REQUEST WBIT(11)
-#define WPA_KEY_INFO_ENCR_KEY_DATA WBIT(12) /* IEEE 802.11i/RSN only */
-
-#define WPA_CAPABILITY_PREAUTH WBIT(0)
+#define WPA_KEY_INFO_INSTALL BIT(6) /* pairwise */
+#define WPA_KEY_INFO_TXRX BIT(6) /* group */
+#define WPA_KEY_INFO_ACK BIT(7)
+#define WPA_KEY_INFO_MIC BIT(8)
+#define WPA_KEY_INFO_SECURE BIT(9)
+#define WPA_KEY_INFO_ERROR BIT(10)
+#define WPA_KEY_INFO_REQUEST BIT(11)
+#define WPA_KEY_INFO_ENCR_KEY_DATA BIT(12) /* IEEE 802.11i/RSN only */
+
+#define WPA_CAPABILITY_PREAUTH BIT(0)
#define GENERIC_INFO_ELEM 0xdd
#define RSN_INFO_ELEM 0x30
diff --git a/drivers/staging/ks7010/ks7010_sdio.c b/drivers/staging/ks7010/ks7010_sdio.c
index 8cfdff198334..b8f55a11ee1c 100644
--- a/drivers/staging/ks7010/ks7010_sdio.c
+++ b/drivers/staging/ks7010/ks7010_sdio.c
@@ -32,19 +32,39 @@ static const struct sdio_device_id ks7010_sdio_ids[] = {
};
MODULE_DEVICE_TABLE(sdio, ks7010_sdio_ids);
-#define inc_txqhead(priv) \
- (priv->tx_dev.qhead = (priv->tx_dev.qhead + 1) % TX_DEVICE_BUFF_SIZE)
-#define inc_txqtail(priv) \
- (priv->tx_dev.qtail = (priv->tx_dev.qtail + 1) % TX_DEVICE_BUFF_SIZE)
-#define cnt_txqbody(priv) \
- (((priv->tx_dev.qtail + TX_DEVICE_BUFF_SIZE) - (priv->tx_dev.qhead)) % TX_DEVICE_BUFF_SIZE)
-
-#define inc_rxqhead(priv) \
- (priv->rx_dev.qhead = (priv->rx_dev.qhead + 1) % RX_DEVICE_BUFF_SIZE)
-#define inc_rxqtail(priv) \
- (priv->rx_dev.qtail = (priv->rx_dev.qtail + 1) % RX_DEVICE_BUFF_SIZE)
-#define cnt_rxqbody(priv) \
- (((priv->rx_dev.qtail + RX_DEVICE_BUFF_SIZE) - (priv->rx_dev.qhead)) % RX_DEVICE_BUFF_SIZE)
+static inline void inc_txqhead(struct ks_wlan_private *priv)
+{
+ priv->tx_dev.qhead = (priv->tx_dev.qhead + 1) % TX_DEVICE_BUFF_SIZE;
+}
+
+static inline void inc_txqtail(struct ks_wlan_private *priv)
+{
+ priv->tx_dev.qtail = (priv->tx_dev.qtail + 1) % TX_DEVICE_BUFF_SIZE;
+}
+
+static inline unsigned int cnt_txqbody(struct ks_wlan_private *priv)
+{
+ unsigned int tx_cnt = priv->tx_dev.qtail - priv->tx_dev.qhead;
+
+ return (tx_cnt + TX_DEVICE_BUFF_SIZE) % TX_DEVICE_BUFF_SIZE;
+}
+
+static inline void inc_rxqhead(struct ks_wlan_private *priv)
+{
+ priv->rx_dev.qhead = (priv->rx_dev.qhead + 1) % RX_DEVICE_BUFF_SIZE;
+}
+
+static inline void inc_rxqtail(struct ks_wlan_private *priv)
+{
+ priv->rx_dev.qtail = (priv->rx_dev.qtail + 1) % RX_DEVICE_BUFF_SIZE;
+}
+
+static inline unsigned int cnt_rxqbody(struct ks_wlan_private *priv)
+{
+ unsigned int rx_cnt = priv->rx_dev.qtail - priv->rx_dev.qhead;
+
+ return (rx_cnt + RX_DEVICE_BUFF_SIZE) % RX_DEVICE_BUFF_SIZE;
+}
/* Read single byte from device address into byte (CMD52) */
static int ks7010_sdio_readb(struct ks_wlan_private *priv, unsigned int address,
@@ -92,22 +112,17 @@ static void ks_wlan_hw_sleep_doze_request(struct ks_wlan_private *priv)
{
int ret;
- DPRINTK(4, "\n");
-
/* clear request */
atomic_set(&priv->sleepstatus.doze_request, 0);
if (atomic_read(&priv->sleepstatus.status) == 0) {
ret = ks7010_sdio_writeb(priv, GCR_B, GCR_B_DOZE);
if (ret) {
- DPRINTK(1, " error : GCR_B\n");
+ netdev_err(priv->net_dev, " error : GCR_B\n");
goto set_sleep_mode;
}
- DPRINTK(3, "sleep_mode=SLP_SLEEP\n");
atomic_set(&priv->sleepstatus.status, 1);
priv->last_doze = jiffies;
- } else {
- DPRINTK(1, "sleep_mode=%d\n", priv->sleep_mode);
}
set_sleep_mode:
@@ -118,23 +133,18 @@ static void ks_wlan_hw_sleep_wakeup_request(struct ks_wlan_private *priv)
{
int ret;
- DPRINTK(4, "\n");
-
/* clear request */
atomic_set(&priv->sleepstatus.wakeup_request, 0);
if (atomic_read(&priv->sleepstatus.status) == 1) {
ret = ks7010_sdio_writeb(priv, WAKEUP, WAKEUP_REQ);
if (ret) {
- DPRINTK(1, " error : WAKEUP\n");
+ netdev_err(priv->net_dev, " error : WAKEUP\n");
goto set_sleep_mode;
}
- DPRINTK(4, "wake up : WAKEUP\n");
atomic_set(&priv->sleepstatus.status, 0);
priv->last_wakeup = jiffies;
++priv->wakeup_count;
- } else {
- DPRINTK(1, "sleep_mode=%d\n", priv->sleep_mode);
}
set_sleep_mode:
@@ -145,19 +155,13 @@ void ks_wlan_hw_wakeup_request(struct ks_wlan_private *priv)
{
int ret;
- DPRINTK(4, "\n");
if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) {
ret = ks7010_sdio_writeb(priv, WAKEUP, WAKEUP_REQ);
if (ret)
- DPRINTK(1, " error : WAKEUP\n");
- else
- DPRINTK(4, "wake up : WAKEUP\n");
+ netdev_err(priv->net_dev, " error : WAKEUP\n");
priv->last_wakeup = jiffies;
++priv->wakeup_count;
- } else {
- DPRINTK(1, "psstatus=%d\n",
- atomic_read(&priv->psstatus.status));
}
}
@@ -181,11 +185,11 @@ static void _ks_wlan_hw_power_save(struct ks_wlan_private *priv)
if (atomic_read(&priv->psstatus.status) == PS_SNOOZE)
return;
- DPRINTK(5, "\npsstatus.status=%d\npsstatus.confirm_wait=%d\npsstatus.snooze_guard=%d\ncnt_txqbody=%d\n",
- atomic_read(&priv->psstatus.status),
- atomic_read(&priv->psstatus.confirm_wait),
- atomic_read(&priv->psstatus.snooze_guard),
- cnt_txqbody(priv));
+ netdev_dbg(priv->net_dev, "\npsstatus.status=%d\npsstatus.confirm_wait=%d\npsstatus.snooze_guard=%d\ncnt_txqbody=%d\n",
+ atomic_read(&priv->psstatus.status),
+ atomic_read(&priv->psstatus.confirm_wait),
+ atomic_read(&priv->psstatus.snooze_guard),
+ cnt_txqbody(priv));
if (atomic_read(&priv->psstatus.confirm_wait) ||
atomic_read(&priv->psstatus.snooze_guard) ||
@@ -196,7 +200,7 @@ static void _ks_wlan_hw_power_save(struct ks_wlan_private *priv)
ret = ks7010_sdio_readb(priv, INT_PENDING, &byte);
if (ret) {
- DPRINTK(1, " error : INT_PENDING\n");
+ netdev_err(priv->net_dev, " error : INT_PENDING\n");
goto queue_delayed_work;
}
if (byte)
@@ -204,11 +208,10 @@ static void _ks_wlan_hw_power_save(struct ks_wlan_private *priv)
ret = ks7010_sdio_writeb(priv, GCR_B, GCR_B_DOZE);
if (ret) {
- DPRINTK(1, " error : GCR_B\n");
+ netdev_err(priv->net_dev, " error : GCR_B\n");
goto queue_delayed_work;
}
atomic_set(&priv->psstatus.status, PS_SNOOZE);
- DPRINTK(3, "psstatus.status=PS_SNOOZE\n");
return;
@@ -237,7 +240,7 @@ static int enqueue_txdev(struct ks_wlan_private *priv, unsigned char *p,
}
if ((TX_DEVICE_BUFF_SIZE - 1) <= cnt_txqbody(priv)) {
- DPRINTK(1, "tx buffer overflow\n");
+ netdev_err(priv->net_dev, "tx buffer overflow\n");
ret = -EOVERFLOW;
goto err_complete;
}
@@ -268,22 +271,21 @@ static int write_to_device(struct ks_wlan_private *priv, unsigned char *buffer,
hdr = (struct hostif_hdr *)buffer;
- DPRINTK(4, "size=%d\n", hdr->size);
if (le16_to_cpu(hdr->event) < HIF_DATA_REQ ||
le16_to_cpu(hdr->event) > HIF_REQ_MAX) {
- DPRINTK(1, "unknown event=%04X\n", hdr->event);
+ netdev_err(priv->net_dev, "unknown event=%04X\n", hdr->event);
return 0;
}
ret = ks7010_sdio_write(priv, DATA_WINDOW, buffer, size);
if (ret) {
- DPRINTK(1, " write error : retval=%d\n", ret);
+ netdev_err(priv->net_dev, " write error : retval=%d\n", ret);
return ret;
}
ret = ks7010_sdio_writeb(priv, WRITE_STATUS, REG_STATUS_BUSY);
if (ret) {
- DPRINTK(1, " error : WRITE_STATUS\n");
+ netdev_err(priv->net_dev, " error : WRITE_STATUS\n");
return ret;
}
@@ -295,7 +297,6 @@ static void tx_device_task(struct ks_wlan_private *priv)
struct tx_device_buffer *sp;
int ret;
- DPRINTK(4, "\n");
if (cnt_txqbody(priv) <= 0 ||
atomic_read(&priv->psstatus.status) == PS_SNOOZE)
return;
@@ -304,7 +305,7 @@ static void tx_device_task(struct ks_wlan_private *priv)
if (priv->dev_state >= DEVICE_STATE_BOOT) {
ret = write_to_device(priv, sp->sendp, sp->size);
if (ret) {
- DPRINTK(1, "write_to_device error !!(%d)\n", ret);
+ netdev_err(priv->net_dev, "write_to_device error !!(%d)\n", ret);
queue_delayed_work(priv->wq, &priv->rw_dwork, 1);
return;
}
@@ -330,7 +331,7 @@ int ks_wlan_hw_tx(struct ks_wlan_private *priv, void *p, unsigned long size,
if (le16_to_cpu(hdr->event) < HIF_DATA_REQ ||
le16_to_cpu(hdr->event) > HIF_REQ_MAX) {
- DPRINTK(1, "unknown event=%04X\n", hdr->event);
+ netdev_err(priv->net_dev, "unknown event=%04X\n", hdr->event);
return 0;
}
@@ -338,7 +339,6 @@ int ks_wlan_hw_tx(struct ks_wlan_private *priv, void *p, unsigned long size,
priv->hostt.buff[priv->hostt.qtail] = le16_to_cpu(hdr->event);
priv->hostt.qtail = (priv->hostt.qtail + 1) % SME_EVENT_BUFF_SIZE;
- DPRINTK(4, "event=%04X\n", hdr->event);
spin_lock(&priv->tx_dev.tx_dev_lock);
result = enqueue_txdev(priv, p, size, complete_handler, skb);
spin_unlock(&priv->tx_dev.tx_dev_lock);
@@ -354,8 +354,6 @@ static void rx_event_task(unsigned long dev)
struct ks_wlan_private *priv = (struct ks_wlan_private *)dev;
struct rx_device_buffer *rp;
- DPRINTK(4, "\n");
-
if (cnt_rxqbody(priv) > 0 && priv->dev_state >= DEVICE_STATE_BOOT) {
rp = &priv->rx_dev.rx_dev_buff[priv->rx_dev.qhead];
hostif_receive(priv, rp->data, rp->size);
@@ -373,11 +371,9 @@ static void ks_wlan_hw_rx(struct ks_wlan_private *priv, uint16_t size)
struct hostif_hdr *hdr;
unsigned short event = 0;
- DPRINTK(4, "\n");
-
/* receive data */
if (cnt_rxqbody(priv) >= (RX_DEVICE_BUFF_SIZE - 1)) {
- DPRINTK(1, "rx buffer overflow\n");
+ netdev_err(priv->net_dev, "rx buffer overflow\n");
return;
}
rx_buffer = &priv->rx_dev.rx_dev_buff[priv->rx_dev.qtail];
@@ -389,15 +385,14 @@ static void ks_wlan_hw_rx(struct ks_wlan_private *priv, uint16_t size)
/* length check */
if (size > 2046 || size == 0) {
-#ifdef KS_WLAN_DEBUG
- if (KS_WLAN_DEBUG > 5)
- print_hex_dump_bytes("INVALID DATA dump: ",
- DUMP_PREFIX_OFFSET,
- rx_buffer->data, 32);
+#ifdef DEBUG
+ print_hex_dump_bytes("INVALID DATA dump: ",
+ DUMP_PREFIX_OFFSET,
+ rx_buffer->data, 32);
#endif
ret = ks7010_sdio_writeb(priv, READ_STATUS, REG_STATUS_IDLE);
if (ret)
- DPRINTK(1, " error : READ_STATUS\n");
+ netdev_err(priv->net_dev, " error : READ_STATUS\n");
/* length check fail */
return;
@@ -410,11 +405,11 @@ static void ks_wlan_hw_rx(struct ks_wlan_private *priv, uint16_t size)
ret = ks7010_sdio_writeb(priv, READ_STATUS, REG_STATUS_IDLE);
if (ret)
- DPRINTK(1, " error : READ_STATUS\n");
+ netdev_err(priv->net_dev, " error : READ_STATUS\n");
if (atomic_read(&priv->psstatus.confirm_wait)) {
if (IS_HIF_CONF(event)) {
- DPRINTK(4, "IS_HIF_CONF true !!\n");
+ netdev_dbg(priv->net_dev, "IS_HIF_CONF true !!\n");
atomic_dec(&priv->psstatus.confirm_wait);
}
}
@@ -430,18 +425,16 @@ static void ks7010_rw_function(struct work_struct *work)
priv = container_of(work, struct ks_wlan_private, rw_dwork.work);
- DPRINTK(4, "\n");
-
/* wait after DOZE */
if (time_after(priv->last_doze + ((30 * HZ) / 1000), jiffies)) {
- DPRINTK(4, "wait after DOZE\n");
+ netdev_dbg(priv->net_dev, "wait after DOZE\n");
queue_delayed_work(priv->wq, &priv->rw_dwork, 1);
return;
}
/* wait after WAKEUP */
while (time_after(priv->last_wakeup + ((30 * HZ) / 1000), jiffies)) {
- DPRINTK(4, "wait after WAKEUP\n");
+ netdev_dbg(priv->net_dev, "wait after WAKEUP\n");
dev_info(&priv->ks_sdio_card->func->dev,
"wake: %lu %lu\n",
priv->last_wakeup + (30 * HZ) / 1000,
@@ -474,11 +467,10 @@ static void ks7010_rw_function(struct work_struct *work)
/* read (WriteStatus/ReadDataSize FN1:00_0014) */
ret = ks7010_sdio_readb(priv, WSTATUS_RSIZE, &byte);
if (ret) {
- DPRINTK(1, " error : WSTATUS_RSIZE psstatus=%d\n",
- atomic_read(&priv->psstatus.status));
+ netdev_err(priv->net_dev, " error : WSTATUS_RSIZE psstatus=%d\n",
+ atomic_read(&priv->psstatus.status));
goto release_host;
}
- DPRINTK(4, "WSTATUS_RSIZE=%02X\n", byte);
if (byte & RSIZE_MASK) { /* Read schedule */
ks_wlan_hw_rx(priv, (uint16_t)((byte & RSIZE_MASK) << 4));
@@ -501,17 +493,15 @@ static void ks_sdio_interrupt(struct sdio_func *func)
card = sdio_get_drvdata(func);
priv = card->priv;
- DPRINTK(4, "\n");
if (priv->dev_state < DEVICE_STATE_BOOT)
goto queue_delayed_work;
ret = ks7010_sdio_readb(priv, INT_PENDING, &status);
if (ret) {
- DPRINTK(1, "error : INT_PENDING\n");
+ netdev_err(priv->net_dev, "error : INT_PENDING\n");
goto queue_delayed_work;
}
- DPRINTK(4, "INT_PENDING=%02X\n", status);
/* schedule task for interrupt status */
/* bit7 -> Write General Communication B register */
@@ -522,7 +512,7 @@ static void ks_sdio_interrupt(struct sdio_func *func)
atomic_read(&priv->psstatus.status) == PS_SNOOZE) {
ret = ks7010_sdio_readb(priv, GCR_B, &byte);
if (ret) {
- DPRINTK(1, " error : GCR_B\n");
+ netdev_err(priv->net_dev, " error : GCR_B\n");
goto queue_delayed_work;
}
if (byte == GCR_B_ACTIVE) {
@@ -538,10 +528,9 @@ static void ks_sdio_interrupt(struct sdio_func *func)
/* read (WriteStatus/ReadDataSize FN1:00_0014) */
ret = ks7010_sdio_readb(priv, WSTATUS_RSIZE, &byte);
if (ret) {
- DPRINTK(1, " error : WSTATUS_RSIZE\n");
+ netdev_err(priv->net_dev, " error : WSTATUS_RSIZE\n");
goto queue_delayed_work;
}
- DPRINTK(4, "WSTATUS_RSIZE=%02X\n", byte);
rsize = byte & RSIZE_MASK;
if (rsize != 0) /* Read schedule */
ks_wlan_hw_rx(priv, (uint16_t)(rsize << 4));
@@ -638,7 +627,7 @@ static int ks7010_sdio_data_compare(struct ks_wlan_private *priv, u32 address,
if (memcmp(data, read_buf, size) != 0) {
ret = -EIO;
- DPRINTK(0, "data compare error (%d)\n", ret);
+ netdev_err(priv->net_dev, "data compare error (%d)\n", ret);
goto err_free_read_buf;
}
@@ -669,7 +658,7 @@ static int ks7010_upload_firmware(struct ks_sdio_card *card)
/* Firmware running ? */
ret = ks7010_sdio_readb(priv, GCR_A, &byte);
if (byte == GCR_A_RUN) {
- DPRINTK(0, "MAC firmware running ...\n");
+ netdev_dbg(priv->net_dev, "MAC firmware running ...\n");
goto release_host_and_free;
}
@@ -689,7 +678,6 @@ static int ks7010_upload_firmware(struct ks_sdio_card *card)
size = length;
length = 0;
}
- DPRINTK(4, "size = %d\n", size);
if (size == 0)
break;
memcpy(rom_buf, fw_entry->data + n, size);
@@ -715,8 +703,6 @@ static int ks7010_upload_firmware(struct ks_sdio_card *card)
if (ret)
goto release_firmware;
- DPRINTK(4, " REMAP Request : GCR_A\n");
-
/* Firmware running check */
for (n = 0; n < 50; ++n) {
mdelay(10); /* wait_ms(10); */
@@ -727,9 +713,8 @@ static int ks7010_upload_firmware(struct ks_sdio_card *card)
if (byte == GCR_A_RUN)
break;
}
- DPRINTK(4, "firmware wakeup (%d)!!!!\n", n);
if ((50) <= n) {
- DPRINTK(1, "firmware can't start\n");
+ netdev_err(priv->net_dev, "firmware can't start\n");
ret = -EIO;
goto release_firmware;
}
@@ -747,20 +732,14 @@ static int ks7010_upload_firmware(struct ks_sdio_card *card)
static void ks7010_card_init(struct ks_wlan_private *priv)
{
- DPRINTK(5, "\ncard_init_task()\n");
-
init_completion(&priv->confirm_wait);
- DPRINTK(5, "init_completion()\n");
-
/* get mac address & firmware version */
hostif_sme_enqueue(priv, SME_START);
- DPRINTK(5, "hostif_sme_enqueu()\n");
-
if (!wait_for_completion_interruptible_timeout
(&priv->confirm_wait, 5 * HZ)) {
- DPRINTK(1, "wait time out!! SME_START\n");
+ netdev_dbg(priv->net_dev, "wait time out!! SME_START\n");
}
if (priv->mac_address_valid && priv->version_size != 0)
@@ -787,14 +766,12 @@ static void ks7010_card_init(struct ks_wlan_private *priv)
if (!wait_for_completion_interruptible_timeout
(&priv->confirm_wait, 5 * HZ)) {
- DPRINTK(1, "wait time out!! wireless parameter set\n");
+ netdev_dbg(priv->net_dev, "wait time out!! wireless parameter set\n");
}
if (priv->dev_state >= DEVICE_STATE_PREINIT) {
- DPRINTK(1, "DEVICE READY!!\n");
+ netdev_dbg(priv->net_dev, "DEVICE READY!!\n");
priv->dev_state = DEVICE_STATE_READY;
- } else {
- DPRINTK(1, "dev_state=%d\n", priv->dev_state);
}
}
@@ -846,11 +823,10 @@ static int ks7010_sdio_probe(struct sdio_func *func,
sdio_claim_host(func);
ret = sdio_set_block_size(func, KS7010_IO_BLOCK_SIZE);
- DPRINTK(5, "multi_block=%d sdio_set_block_size()=%d %d\n",
+ dev_dbg(&card->func->dev, "multi_block=%d sdio_set_block_size()=%d %d\n",
func->card->cccr.multi_block, func->cur_blksize, ret);
ret = sdio_enable_func(func);
- DPRINTK(5, "sdio_enable_func() %d\n", ret);
if (ret)
goto err_free_card;
@@ -871,7 +847,7 @@ static int ks7010_sdio_probe(struct sdio_func *func,
sdio_set_drvdata(func, card);
- DPRINTK(5, "class = 0x%X, vendor = 0x%X, device = 0x%X\n",
+ dev_dbg(&card->func->dev, "class = 0x%X, vendor = 0x%X, device = 0x%X\n",
func->class, func->vendor, func->device);
/* private memory allocate */
@@ -916,9 +892,9 @@ static int ks7010_sdio_probe(struct sdio_func *func,
ret = ks7010_upload_firmware(card);
if (ret) {
- dev_err(&card->func->dev,
- "ks7010: firmware load failed !! return code = %d\n",
- ret);
+ netdev_err(priv->net_dev,
+ "ks7010: firmware load failed !! return code = %d\n",
+ ret);
goto err_free_netdev;
}
@@ -928,7 +904,7 @@ static int ks7010_sdio_probe(struct sdio_func *func,
ret = ks7010_sdio_writeb(priv, INT_PENDING, 0xff);
sdio_release_host(func);
if (ret)
- DPRINTK(1, " error : INT_PENDING\n");
+ netdev_err(priv->net_dev, " error : INT_PENDING\n");
/* enable ks7010sdio interrupt */
byte = (INT_GCR_B | INT_READ_STATUS | INT_WRITE_STATUS);
@@ -936,14 +912,13 @@ static int ks7010_sdio_probe(struct sdio_func *func,
ret = ks7010_sdio_writeb(priv, INT_ENABLE, byte);
sdio_release_host(func);
if (ret)
- DPRINTK(1, " err : INT_ENABLE\n");
+ netdev_err(priv->net_dev, " err : INT_ENABLE\n");
- DPRINTK(4, " enable Interrupt : INT_ENABLE=%02X\n", byte);
priv->dev_state = DEVICE_STATE_BOOT;
priv->wq = create_workqueue("wq");
if (!priv->wq) {
- DPRINTK(1, "create_workqueue failed !!\n");
+ netdev_err(priv->net_dev, "create_workqueue failed !!\n");
goto err_free_netdev;
}
@@ -983,7 +958,7 @@ static int send_stop_request(struct sdio_func *func)
pp = kzalloc(hif_align_size(sizeof(*pp)), GFP_KERNEL);
if (!pp) {
- DPRINTK(3, "allocate memory failed..\n");
+ netdev_err(card->priv->net_dev, "allocate memory failed..\n");
return -ENOMEM;
}
@@ -1011,35 +986,28 @@ static void ks7010_sdio_remove(struct sdio_func *func)
if (!card)
return;
- DPRINTK(1, "priv = card->priv\n");
priv = card->priv;
if (priv) {
struct net_device *netdev = priv->net_dev;
ks_wlan_net_stop(netdev);
- DPRINTK(1, "ks_wlan_net_stop\n");
/* interrupt disable */
sdio_claim_host(func);
sdio_writeb(func, 0, INT_ENABLE, &ret);
sdio_writeb(func, 0xff, INT_PENDING, &ret);
sdio_release_host(func);
- DPRINTK(1, "interrupt disable\n");
ret = send_stop_request(func);
if (ret) /* memory allocation failure */
return;
- DPRINTK(1, "STOP Req\n");
-
if (priv->wq) {
flush_workqueue(priv->wq);
destroy_workqueue(priv->wq);
}
- DPRINTK(1, "destroy_workqueue(priv->wq);\n");
hostif_exit(priv);
- DPRINTK(1, "hostif_exit\n");
unregister_netdev(netdev);
@@ -1050,17 +1018,10 @@ static void ks7010_sdio_remove(struct sdio_func *func)
sdio_claim_host(func);
sdio_release_irq(func);
- DPRINTK(1, "sdio_release_irq()\n");
sdio_disable_func(func);
- DPRINTK(1, "sdio_disable_func()\n");
sdio_release_host(func);
-
sdio_set_drvdata(func, NULL);
-
kfree(card);
- DPRINTK(1, "kfree()\n");
-
- DPRINTK(5, " Bye !!\n");
}
static struct sdio_driver ks7010_sdio_driver = {
diff --git a/drivers/staging/ks7010/ks_hostif.c b/drivers/staging/ks7010/ks_hostif.c
index 975dbbb3abd0..676961cf4103 100644
--- a/drivers/staging/ks7010/ks_hostif.c
+++ b/drivers/staging/ks7010/ks_hostif.c
@@ -21,18 +21,26 @@
/* Include Wireless Extension definition and check version */
#include <net/iw_handler.h> /* New driver API */
-/* macro */
-#define inc_smeqhead(priv) \
- (priv->sme_i.qhead = (priv->sme_i.qhead + 1) % SME_EVENT_BUFF_SIZE)
-#define inc_smeqtail(priv) \
- (priv->sme_i.qtail = (priv->sme_i.qtail + 1) % SME_EVENT_BUFF_SIZE)
-#define cnt_smeqbody(priv) \
- (((priv->sme_i.qtail + SME_EVENT_BUFF_SIZE) - (priv->sme_i.qhead)) % SME_EVENT_BUFF_SIZE)
+static inline void inc_smeqhead(struct ks_wlan_private *priv)
+{
+ priv->sme_i.qhead = (priv->sme_i.qhead + 1) % SME_EVENT_BUFF_SIZE;
+}
+
+static inline void inc_smeqtail(struct ks_wlan_private *priv)
+{
+ priv->sme_i.qtail = (priv->sme_i.qtail + 1) % SME_EVENT_BUFF_SIZE;
+}
+
+static inline unsigned int cnt_smeqbody(struct ks_wlan_private *priv)
+{
+ unsigned int sme_cnt = priv->sme_i.qtail - priv->sme_i.qhead;
+
+ return (sme_cnt + SME_EVENT_BUFF_SIZE) % SME_EVENT_BUFF_SIZE;
+}
#define KS_WLAN_MEM_FLAG (GFP_ATOMIC)
-static
-inline u8 get_BYTE(struct ks_wlan_private *priv)
+static inline u8 get_byte(struct ks_wlan_private *priv)
{
u8 data;
@@ -42,25 +50,23 @@ inline u8 get_BYTE(struct ks_wlan_private *priv)
return data;
}
-static
-inline u16 get_WORD(struct ks_wlan_private *priv)
+static inline u16 get_word(struct ks_wlan_private *priv)
{
u16 data;
- data = (get_BYTE(priv) & 0xff);
- data |= ((get_BYTE(priv) << 8) & 0xff00);
+ data = (get_byte(priv) & 0xff);
+ data |= ((get_byte(priv) << 8) & 0xff00);
return data;
}
-static
-inline u32 get_DWORD(struct ks_wlan_private *priv)
+static inline u32 get_dword(struct ks_wlan_private *priv)
{
u32 data;
- data = (get_BYTE(priv) & 0xff);
- data |= ((get_BYTE(priv) << 8) & 0x0000ff00);
- data |= ((get_BYTE(priv) << 16) & 0x00ff0000);
- data |= ((get_BYTE(priv) << 24) & 0xff000000);
+ data = (get_byte(priv) & 0xff);
+ data |= ((get_byte(priv) << 8) & 0x0000ff00);
+ data |= ((get_byte(priv) << 16) & 0x00ff0000);
+ data |= ((get_byte(priv) << 24) & 0xff000000);
return data;
}
@@ -79,26 +85,20 @@ static void ks_wlan_hw_wakeup_task(struct work_struct *work)
&priv->psstatus.wakeup_wait,
msecs_to_jiffies(20));
if (time_left <= 0) {
- DPRINTK(1, "wake up timeout or interrupted !!!\n");
+ netdev_dbg(priv->net_dev, "wake up timeout or interrupted !!!\n");
schedule_work(&priv->wakeup_work);
return;
}
- } else {
- DPRINTK(1, "ps_status=%d\n", ps_status);
}
/* power save */
- if (atomic_read(&priv->sme_task.count) > 0) {
- DPRINTK(4, "sme task enable.\n");
+ if (atomic_read(&priv->sme_task.count) > 0)
tasklet_enable(&priv->sme_task);
- }
}
static
int ks_wlan_do_power_save(struct ks_wlan_private *priv)
{
- DPRINTK(4, "psstatus.status=%d\n", atomic_read(&priv->psstatus.status));
-
if (is_connect_status(priv->connect_status))
hostif_sme_enqueue(priv, SME_POW_MNGMT_REQUEST);
else
@@ -113,7 +113,6 @@ int get_current_ap(struct ks_wlan_private *priv, struct link_ap_info_t *ap_info)
union iwreq_data wrqu;
struct net_device *netdev = priv->net_dev;
- DPRINTK(3, "\n");
ap = &priv->current_ap;
if (is_disconnect_status(priv->connect_status)) {
@@ -186,34 +185,43 @@ int get_current_ap(struct ks_wlan_private *priv, struct link_ap_info_t *ap_info)
if (is_connect_status(priv->connect_status)) {
memcpy(wrqu.ap_addr.sa_data,
priv->current_ap.bssid, ETH_ALEN);
- DPRINTK(3,
- "IWEVENT: connect bssid=%pM\n", wrqu.ap_addr.sa_data);
+ netdev_dbg(priv->net_dev,
+ "IWEVENT: connect bssid=%pM\n", wrqu.ap_addr.sa_data);
wireless_send_event(netdev, SIOCGIWAP, &wrqu, NULL);
}
- DPRINTK(4, "\n Link AP\n");
- DPRINTK(4, " bssid=%02X:%02X:%02X:%02X:%02X:%02X\n"
+ netdev_dbg(priv->net_dev, " Link AP\n");
+ netdev_dbg(priv->net_dev, " bssid=%02X:%02X:%02X:%02X:%02X:%02X\n"
" essid=%s\n"
" rate_set=%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X\n"
" channel=%d\n"
" rssi=%d\n"
" sq=%d\n"
" capability=%04X\n",
- ap->bssid[0], ap->bssid[1], ap->bssid[2],
- ap->bssid[3], ap->bssid[4], ap->bssid[5],
- &(ap->ssid.body[0]),
- ap->rate_set.body[0], ap->rate_set.body[1],
- ap->rate_set.body[2], ap->rate_set.body[3],
- ap->rate_set.body[4], ap->rate_set.body[5],
- ap->rate_set.body[6], ap->rate_set.body[7],
- ap->channel, ap->rssi, ap->sq, ap->capability);
- DPRINTK(4, "\n Link AP\n rsn.mode=%d\n rsn.size=%d\n",
- ap_info->rsn_mode, ap_info->rsn.size);
- DPRINTK(4, "\n ext_rate_set_size=%d\n rate_set_size=%d\n",
- ap_info->ext_rate_set.size, ap_info->rate_set.size);
+ ap->bssid[0], ap->bssid[1], ap->bssid[2],
+ ap->bssid[3], ap->bssid[4], ap->bssid[5],
+ &(ap->ssid.body[0]),
+ ap->rate_set.body[0], ap->rate_set.body[1],
+ ap->rate_set.body[2], ap->rate_set.body[3],
+ ap->rate_set.body[4], ap->rate_set.body[5],
+ ap->rate_set.body[6], ap->rate_set.body[7],
+ ap->channel, ap->rssi, ap->sq, ap->capability);
+ netdev_dbg(priv->net_dev, " Link AP\n rsn.mode=%d\n rsn.size=%d\n",
+ ap_info->rsn_mode, ap_info->rsn.size);
+ netdev_dbg(priv->net_dev, " ext_rate_set_size=%d\n rate_set_size=%d\n",
+ ap_info->ext_rate_set.size, ap_info->rate_set.size);
return 0;
}
+static u8 read_ie(unsigned char *bp, u8 max, u8 *body)
+{
+ u8 size = (*(bp + 1) <= max) ? *(bp + 1) : max;
+
+ memcpy(body, bp + 2, size);
+ return size;
+}
+
+
static
int get_ap_information(struct ks_wlan_private *priv, struct ap_info_t *ap_info,
struct local_ap_t *ap)
@@ -221,7 +229,6 @@ int get_ap_information(struct ks_wlan_private *priv, struct ap_info_t *ap_info,
unsigned char *bp;
int bsize, offset;
- DPRINTK(3, "\n");
memset(ap, 0, sizeof(struct local_ap_t));
/* bssid */
@@ -242,28 +249,19 @@ int get_ap_information(struct ks_wlan_private *priv, struct ap_info_t *ap_info,
offset = 0;
while (bsize > offset) {
- /* DPRINTK(4, "Element ID=%d\n",*bp); */
- switch (*bp) {
- case 0: /* ssid */
- if (*(bp + 1) <= SSID_MAX_SIZE) {
- ap->ssid.size = *(bp + 1);
- } else {
- DPRINTK(1, "size over :: ssid size=%d\n",
- *(bp + 1));
- ap->ssid.size = SSID_MAX_SIZE;
- }
- memcpy(ap->ssid.body, bp + 2, ap->ssid.size);
+ switch (*bp) { /* Information Element ID */
+ case WLAN_EID_SSID:
+ ap->ssid.size = read_ie(bp, IEEE80211_MAX_SSID_LEN,
+ ap->ssid.body);
break;
- case 1: /* rate */
- case 50: /* ext rate */
+ case WLAN_EID_SUPP_RATES:
+ case WLAN_EID_EXT_SUPP_RATES:
if ((*(bp + 1) + ap->rate_set.size) <=
RATE_SET_MAX_SIZE) {
memcpy(&ap->rate_set.body[ap->rate_set.size],
bp + 2, *(bp + 1));
ap->rate_set.size += *(bp + 1);
} else {
- DPRINTK(1, "size over :: rate size=%d\n",
- (*(bp + 1) + ap->rate_set.size));
memcpy(&ap->rate_set.body[ap->rate_set.size],
bp + 2,
RATE_SET_MAX_SIZE - ap->rate_set.size);
@@ -271,47 +269,34 @@ int get_ap_information(struct ks_wlan_private *priv, struct ap_info_t *ap_info,
(RATE_SET_MAX_SIZE - ap->rate_set.size);
}
break;
- case 3: /* DS parameter */
+ case WLAN_EID_DS_PARAMS:
break;
- case 48: /* RSN(WPA2) */
+ case WLAN_EID_RSN:
ap->rsn_ie.id = *bp;
- if (*(bp + 1) <= RSN_IE_BODY_MAX) {
- ap->rsn_ie.size = *(bp + 1);
- } else {
- DPRINTK(1, "size over :: rsn size=%d\n",
- *(bp + 1));
- ap->rsn_ie.size = RSN_IE_BODY_MAX;
- }
- memcpy(ap->rsn_ie.body, bp + 2, ap->rsn_ie.size);
+ ap->rsn_ie.size = read_ie(bp, RSN_IE_BODY_MAX,
+ ap->rsn_ie.body);
break;
- case 221: /* WPA */
- if (memcmp(bp + 2, "\x00\x50\xf2\x01", 4) == 0) { /* WPA OUI check */
+ case WLAN_EID_VENDOR_SPECIFIC: /* WPA */
+ /* WPA OUI check */
+ if (memcmp(bp + 2, CIPHER_ID_WPA_WEP40, 4) == 0) {
ap->wpa_ie.id = *bp;
- if (*(bp + 1) <= RSN_IE_BODY_MAX) {
- ap->wpa_ie.size = *(bp + 1);
- } else {
- DPRINTK(1,
- "size over :: wpa size=%d\n",
- *(bp + 1));
- ap->wpa_ie.size = RSN_IE_BODY_MAX;
- }
- memcpy(ap->wpa_ie.body, bp + 2,
- ap->wpa_ie.size);
+ ap->wpa_ie.size = read_ie(bp, RSN_IE_BODY_MAX,
+ ap->wpa_ie.body);
}
break;
- case 2: /* FH parameter */
- case 4: /* CF parameter */
- case 5: /* TIM */
- case 6: /* IBSS parameter */
- case 7: /* Country */
- case 42: /* ERP information */
- case 47: /* Reserve ID 47 Broadcom AP */
+ case WLAN_EID_FH_PARAMS:
+ case WLAN_EID_CF_PARAMS:
+ case WLAN_EID_TIM:
+ case WLAN_EID_IBSS_PARAMS:
+ case WLAN_EID_COUNTRY:
+ case WLAN_EID_ERP_INFO:
break;
default:
- DPRINTK(4, "unknown Element ID=%d\n", *bp);
+ netdev_err(priv->net_dev, "unknown Element ID=%d\n", *bp);
break;
}
+
offset += 2; /* id & size field */
offset += *(bp + 1); /* +size offset */
bp += (*(bp + 1) + 2); /* pointer update */
@@ -339,7 +324,7 @@ int hostif_data_indication_wpa(struct ks_wlan_private *priv,
eth_proto = ntohs(eth_hdr->h_proto);
if (eth_hdr->h_dest_snap != eth_hdr->h_source_snap) {
- DPRINTK(1, "invalid data format\n");
+ netdev_err(priv->net_dev, "invalid data format\n");
priv->nstats.rx_errors++;
return -EINVAL;
}
@@ -350,20 +335,20 @@ int hostif_data_indication_wpa(struct ks_wlan_private *priv,
(auth_type == TYPE_GMK2 &&
priv->wpa.group_suite == IW_AUTH_CIPHER_TKIP)) &&
key->key_len) {
- DPRINTK(4, "TKIP: protocol=%04X: size=%u\n",
- eth_proto, priv->rx_size);
+ netdev_dbg(priv->net_dev, "TKIP: protocol=%04X: size=%u\n",
+ eth_proto, priv->rx_size);
/* MIC save */
memcpy(&recv_mic[0], (priv->rxp) + ((priv->rx_size) - 8), 8);
priv->rx_size = priv->rx_size - 8;
if (auth_type > 0 && auth_type < 4) { /* auth_type check */
- MichaelMICFunction(&michael_mic,
- (uint8_t *)key->rx_mic_key,
- (uint8_t *)priv->rxp,
- (int)priv->rx_size,
- (uint8_t)0, /* priority */
- (uint8_t *)michael_mic.Result);
+ michael_mic_function(&michael_mic,
+ (uint8_t *)key->rx_mic_key,
+ (uint8_t *)priv->rxp,
+ (int)priv->rx_size,
+ (uint8_t)0, /* priority */
+ (uint8_t *)michael_mic.result);
}
- if (memcmp(michael_mic.Result, recv_mic, 8) != 0) {
+ if (memcmp(michael_mic.result, recv_mic, 8) != 0) {
now = jiffies;
mic_failure = &priv->wpa.mic_failure;
/* MIC FAILURE */
@@ -371,7 +356,7 @@ int hostif_data_indication_wpa(struct ks_wlan_private *priv,
(now - mic_failure->last_failure_time) / HZ >= 60) {
mic_failure->failure = 0;
}
- DPRINTK(4, "MIC FAILURE\n");
+ netdev_err(priv->net_dev, "MIC FAILURE\n");
if (mic_failure->failure == 0) {
mic_failure->failure = 1;
mic_failure->counter = 0;
@@ -392,7 +377,6 @@ int hostif_data_indication_wpa(struct ks_wlan_private *priv,
eth_hdr->h_source);
memset(&wrqu, 0, sizeof(wrqu));
wrqu.data.length = strlen(buf);
- DPRINTK(4, "IWEVENT:MICHAELMICFAILURE\n");
wireless_send_event(priv->net_dev, IWEVCUSTOM, &wrqu,
buf);
return -EINVAL;
@@ -414,30 +398,26 @@ void hostif_data_indication(struct ks_wlan_private *priv)
size_t size;
int ret;
- DPRINTK(3, "\n");
-
/* min length check */
if (priv->rx_size <= ETH_HLEN) {
- DPRINTK(3, "rx_size = %d\n", priv->rx_size);
priv->nstats.rx_errors++;
return;
}
- auth_type = get_WORD(priv); /* AuthType */
- get_WORD(priv); /* Reserve Area */
+ auth_type = get_word(priv); /* AuthType */
+ get_word(priv); /* Reserve Area */
eth_hdr = (struct ether_hdr *)(priv->rxp);
eth_proto = ntohs(eth_hdr->h_proto);
- DPRINTK(3, "ether protocol = %04X\n", eth_proto);
/* source address check */
if (memcmp(&priv->eth_addr[0], eth_hdr->h_source, ETH_ALEN) == 0) {
- DPRINTK(1, "invalid : source is own mac address !!\n");
- DPRINTK(1,
- "eth_hdrernet->h_dest=%02X:%02X:%02X:%02X:%02X:%02X\n",
- eth_hdr->h_source[0], eth_hdr->h_source[1],
- eth_hdr->h_source[2], eth_hdr->h_source[3],
- eth_hdr->h_source[4], eth_hdr->h_source[5]);
+ netdev_err(priv->net_dev, "invalid : source is own mac address !!\n");
+ netdev_err(priv->net_dev,
+ "eth_hdrernet->h_dest=%02X:%02X:%02X:%02X:%02X:%02X\n",
+ eth_hdr->h_source[0], eth_hdr->h_source[1],
+ eth_hdr->h_source[2], eth_hdr->h_source[3],
+ eth_hdr->h_source[4], eth_hdr->h_source[5]);
priv->nstats.rx_errors++;
return;
}
@@ -463,7 +443,8 @@ void hostif_data_indication(struct ks_wlan_private *priv)
priv->nstats.rx_dropped++;
return;
}
- DPRINTK(4, "SNAP, rx_ind_size = %d\n", rx_ind_size);
+ netdev_dbg(priv->net_dev, "SNAP, rx_ind_size = %d\n",
+ rx_ind_size);
size = ETH_ALEN * 2;
skb_put_data(skb, priv->rxp, size);
@@ -482,20 +463,24 @@ void hostif_data_indication(struct ks_wlan_private *priv)
priv->nstats.rx_dropped++;
return;
}
- DPRINTK(3, "NETBEUI/NetBIOS rx_ind_size=%d\n", rx_ind_size);
+ netdev_dbg(priv->net_dev, "NETBEUI/NetBIOS rx_ind_size=%d\n",
+ rx_ind_size);
- skb_put_data(skb, priv->rxp, 12); /* 8802/FDDI MAC copy */
+ /* 8802/FDDI MAC copy */
+ skb_put_data(skb, priv->rxp, 12);
- temp[0] = (((rx_ind_size - 12) >> 8) & 0xff); /* NETBEUI size add */
+ /* NETBEUI size add */
+ temp[0] = (((rx_ind_size - 12) >> 8) & 0xff);
temp[1] = ((rx_ind_size - 12) & 0xff);
skb_put_data(skb, temp, 2);
- skb_put_data(skb, priv->rxp + 12, rx_ind_size - 14); /* copy after Type */
+ /* copy after Type */
+ skb_put_data(skb, priv->rxp + 12, rx_ind_size - 14);
aa1x_hdr = (struct ieee802_1x_hdr *)(priv->rxp + 14);
break;
default: /* other rx data */
- DPRINTK(2, "invalid data format\n");
+ netdev_err(priv->net_dev, "invalid data format\n");
priv->nstats.rx_errors++;
return;
}
@@ -521,27 +506,24 @@ void hostif_mib_get_confirm(struct ks_wlan_private *priv)
u16 mib_val_size;
u16 mib_val_type;
- DPRINTK(3, "\n");
-
- mib_status = get_DWORD(priv); /* MIB status */
- mib_attribute = get_DWORD(priv); /* MIB atttibute */
- mib_val_size = get_WORD(priv); /* MIB value size */
- mib_val_type = get_WORD(priv); /* MIB value type */
+ mib_status = get_dword(priv); /* MIB status */
+ mib_attribute = get_dword(priv); /* MIB atttibute */
+ mib_val_size = get_word(priv); /* MIB value size */
+ mib_val_type = get_word(priv); /* MIB value type */
if (mib_status) {
/* in case of error */
- DPRINTK(1, "attribute=%08X, status=%08X\n", mib_attribute,
- mib_status);
+ netdev_err(priv->net_dev, "attribute=%08X, status=%08X\n",
+ mib_attribute, mib_status);
return;
}
switch (mib_attribute) {
case DOT11_MAC_ADDRESS:
/* MAC address */
- DPRINTK(3, " mib_attribute=DOT11_MAC_ADDRESS\n");
hostif_sme_enqueue(priv, SME_GET_MAC_ADDRESS);
memcpy(priv->eth_addr, priv->rxp, ETH_ALEN);
- priv->mac_address_valid = 1;
+ priv->mac_address_valid = true;
dev->dev_addr[0] = priv->eth_addr[0];
dev->dev_addr[1] = priv->eth_addr[1];
dev->dev_addr[2] = priv->eth_addr[2];
@@ -554,7 +536,6 @@ void hostif_mib_get_confirm(struct ks_wlan_private *priv)
break;
case DOT11_PRODUCT_VERSION:
/* firmware version */
- DPRINTK(3, " mib_attribute=DOT11_PRODUCT_VERSION\n");
priv->version_size = priv->rx_size;
memcpy(priv->firmware_version, priv->rxp, priv->rx_size);
priv->firmware_version[priv->rx_size] = '\0';
@@ -566,14 +547,12 @@ void hostif_mib_get_confirm(struct ks_wlan_private *priv)
break;
case LOCAL_GAIN:
memcpy(&priv->gain, priv->rxp, sizeof(priv->gain));
- DPRINTK(3, "tx_mode=%d, rx_mode=%d, tx_gain=%d, rx_gain=%d\n",
- priv->gain.tx_mode, priv->gain.rx_mode,
- priv->gain.tx_gain, priv->gain.rx_gain);
+ netdev_dbg(priv->net_dev, "tx_mode=%d, rx_mode=%d, tx_gain=%d, rx_gain=%d\n",
+ priv->gain.tx_mode, priv->gain.rx_mode,
+ priv->gain.tx_gain, priv->gain.rx_gain);
break;
case LOCAL_EEPROM_SUM:
memcpy(&priv->eeprom_sum, priv->rxp, sizeof(priv->eeprom_sum));
- DPRINTK(1, "eeprom_sum.type=%x, eeprom_sum.result=%x\n",
- priv->eeprom_sum.type, priv->eeprom_sum.result);
if (priv->eeprom_sum.type == 0) {
priv->eeprom_checksum = EEPROM_CHECKSUM_NONE;
} else if (priv->eeprom_sum.type == 1) {
@@ -588,7 +567,8 @@ void hostif_mib_get_confirm(struct ks_wlan_private *priv)
}
break;
default:
- DPRINTK(1, "mib_attribute=%08x\n", (unsigned int)mib_attribute);
+ netdev_err(priv->net_dev, "mib_attribute=%08x\n",
+ (unsigned int)mib_attribute);
break;
}
}
@@ -599,15 +579,13 @@ void hostif_mib_set_confirm(struct ks_wlan_private *priv)
u32 mib_status; /* +04 MIB Status */
u32 mib_attribute; /* +08 MIB attribute */
- DPRINTK(3, "\n");
-
- mib_status = get_DWORD(priv); /* MIB Status */
- mib_attribute = get_DWORD(priv); /* MIB attribute */
+ mib_status = get_dword(priv); /* MIB Status */
+ mib_attribute = get_dword(priv); /* MIB attribute */
if (mib_status) {
/* in case of error */
- DPRINTK(1, "error :: attribute=%08X, status=%08X\n",
- mib_attribute, mib_status);
+ netdev_err(priv->net_dev, "error :: attribute=%08X, status=%08X\n",
+ mib_attribute, mib_status);
}
switch (mib_attribute) {
@@ -622,32 +600,24 @@ void hostif_mib_set_confirm(struct ks_wlan_private *priv)
hostif_sme_enqueue(priv, SME_WEP_INDEX_CONFIRM);
break;
case DOT11_WEP_DEFAULT_KEY_VALUE1:
- DPRINTK(2, "DOT11_WEP_DEFAULT_KEY_VALUE1:mib_status=%d\n",
- (int)mib_status);
if (priv->wpa.rsn_enabled)
hostif_sme_enqueue(priv, SME_SET_PMK_TSC);
else
hostif_sme_enqueue(priv, SME_WEP_KEY1_CONFIRM);
break;
case DOT11_WEP_DEFAULT_KEY_VALUE2:
- DPRINTK(2, "DOT11_WEP_DEFAULT_KEY_VALUE2:mib_status=%d\n",
- (int)mib_status);
if (priv->wpa.rsn_enabled)
hostif_sme_enqueue(priv, SME_SET_GMK1_TSC);
else
hostif_sme_enqueue(priv, SME_WEP_KEY2_CONFIRM);
break;
case DOT11_WEP_DEFAULT_KEY_VALUE3:
- DPRINTK(2, "DOT11_WEP_DEFAULT_KEY_VALUE3:mib_status=%d\n",
- (int)mib_status);
if (priv->wpa.rsn_enabled)
hostif_sme_enqueue(priv, SME_SET_GMK2_TSC);
else
hostif_sme_enqueue(priv, SME_WEP_KEY3_CONFIRM);
break;
case DOT11_WEP_DEFAULT_KEY_VALUE4:
- DPRINTK(2, "DOT11_WEP_DEFAULT_KEY_VALUE4:mib_status=%d\n",
- (int)mib_status);
if (!priv->wpa.rsn_enabled)
hostif_sme_enqueue(priv, SME_WEP_KEY4_CONFIRM);
break;
@@ -656,8 +626,6 @@ void hostif_mib_set_confirm(struct ks_wlan_private *priv)
hostif_sme_enqueue(priv, SME_WEP_FLAG_CONFIRM);
break;
case DOT11_RSN_ENABLED:
- DPRINTK(2, "DOT11_RSN_ENABLED:mib_status=%d\n",
- (int)mib_status);
hostif_sme_enqueue(priv, SME_RSN_ENABLED_CONFIRM);
break;
case LOCAL_RSN_MODE:
@@ -670,53 +638,38 @@ void hostif_mib_set_confirm(struct ks_wlan_private *priv)
hostif_sme_enqueue(priv, SME_MULTICAST_CONFIRM);
break;
case LOCAL_CURRENTADDRESS:
- priv->mac_address_valid = 1;
+ priv->mac_address_valid = true;
break;
case DOT11_RSN_CONFIG_MULTICAST_CIPHER:
- DPRINTK(2, "DOT11_RSN_CONFIG_MULTICAST_CIPHER:mib_status=%d\n",
- (int)mib_status);
hostif_sme_enqueue(priv, SME_RSN_MCAST_CONFIRM);
break;
case DOT11_RSN_CONFIG_UNICAST_CIPHER:
- DPRINTK(2, "DOT11_RSN_CONFIG_UNICAST_CIPHER:mib_status=%d\n",
- (int)mib_status);
hostif_sme_enqueue(priv, SME_RSN_UCAST_CONFIRM);
break;
case DOT11_RSN_CONFIG_AUTH_SUITE:
- DPRINTK(2, "DOT11_RSN_CONFIG_AUTH_SUITE:mib_status=%d\n",
- (int)mib_status);
hostif_sme_enqueue(priv, SME_RSN_AUTH_CONFIRM);
break;
case DOT11_PMK_TSC:
- DPRINTK(2, "DOT11_PMK_TSC:mib_status=%d\n", (int)mib_status);
break;
case DOT11_GMK1_TSC:
- DPRINTK(2, "DOT11_GMK1_TSC:mib_status=%d\n", (int)mib_status);
if (atomic_read(&priv->psstatus.snooze_guard))
atomic_set(&priv->psstatus.snooze_guard, 0);
break;
case DOT11_GMK2_TSC:
- DPRINTK(2, "DOT11_GMK2_TSC:mib_status=%d\n", (int)mib_status);
if (atomic_read(&priv->psstatus.snooze_guard))
atomic_set(&priv->psstatus.snooze_guard, 0);
break;
case LOCAL_PMK:
- DPRINTK(2, "LOCAL_PMK:mib_status=%d\n", (int)mib_status);
break;
case LOCAL_GAIN:
- DPRINTK(2, "LOCAL_GAIN:mib_status=%d\n", (int)mib_status);
break;
#ifdef WPS
case LOCAL_WPS_ENABLE:
- DPRINTK(2, "LOCAL_WPS_ENABLE:mib_status=%d\n", (int)mib_status);
break;
case LOCAL_WPS_PROBE_REQ:
- DPRINTK(2, "LOCAL_WPS_PROBE_REQ:mib_status=%d\n",
- (int)mib_status);
break;
#endif /* WPS */
case LOCAL_REGION:
- DPRINTK(2, "LOCAL_REGION:mib_status=%d\n", (int)mib_status);
default:
break;
}
@@ -725,8 +678,6 @@ void hostif_mib_set_confirm(struct ks_wlan_private *priv)
static
void hostif_power_mgmt_confirm(struct ks_wlan_private *priv)
{
- DPRINTK(3, "\n");
-
if (priv->reg.power_mgmt > POWER_MGMT_ACTIVE &&
priv->reg.operation_mode == MODE_INFRASTRUCTURE) {
atomic_set(&priv->psstatus.confirm_wait, 0);
@@ -740,8 +691,6 @@ void hostif_power_mgmt_confirm(struct ks_wlan_private *priv)
static
void hostif_sleep_confirm(struct ks_wlan_private *priv)
{
- DPRINTK(3, "\n");
-
atomic_set(&priv->sleepstatus.doze_request, 1);
queue_delayed_work(priv->wq, &priv->rw_dwork, 1);
}
@@ -757,11 +706,10 @@ void hostif_start_confirm(struct ks_wlan_private *priv)
wrqu.ap_addr.sa_family = ARPHRD_ETHER;
if (is_connect_status(priv->connect_status)) {
eth_zero_addr(wrqu.ap_addr.sa_data);
- DPRINTK(3, "IWEVENT: disconnect\n");
wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
}
#endif
- DPRINTK(3, " scan_ind_count=%d\n", priv->scan_ind_count);
+ netdev_dbg(priv->net_dev, " scan_ind_count=%d\n", priv->scan_ind_count);
hostif_sme_enqueue(priv, SME_START_CONFIRM);
}
@@ -774,27 +722,23 @@ void hostif_connect_indication(struct ks_wlan_private *priv)
struct net_device *netdev = priv->net_dev;
union iwreq_data wrqu0;
- connect_code = get_WORD(priv);
+ connect_code = get_word(priv);
switch (connect_code) {
case RESULT_CONNECT: /* connect */
- DPRINTK(3, "connect :: scan_ind_count=%d\n",
- priv->scan_ind_count);
if (!(priv->connect_status & FORCE_DISCONNECT))
netif_carrier_on(netdev);
tmp = FORCE_DISCONNECT & priv->connect_status;
priv->connect_status = tmp + CONNECT_STATUS;
break;
- case RESULT_DISCONNECT: /* disconnect */
- DPRINTK(3, "disconnect :: scan_ind_count=%d\n",
- priv->scan_ind_count);
+ case RESULT_DISCONNECT: /* disconnect */
netif_carrier_off(netdev);
tmp = FORCE_DISCONNECT & priv->connect_status;
priv->connect_status = tmp + DISCONNECT_STATUS;
break;
default:
- DPRINTK(1, "unknown connect_code=%d :: scan_ind_count=%d\n",
- connect_code, priv->scan_ind_count);
+ netdev_dbg(priv->net_dev, "unknown connect_code=%d :: scan_ind_count=%d\n",
+ connect_code, priv->scan_ind_count);
netif_carrier_off(netdev);
tmp = FORCE_DISCONNECT & priv->connect_status;
priv->connect_status = tmp + DISCONNECT_STATUS;
@@ -816,9 +760,8 @@ void hostif_connect_indication(struct ks_wlan_private *priv)
if (is_disconnect_status(priv->connect_status) &&
is_connect_status(old_status)) {
eth_zero_addr(wrqu0.ap_addr.sa_data);
- DPRINTK(3, "IWEVENT: disconnect\n");
- DPRINTK(3, "disconnect :: scan_ind_count=%d\n",
- priv->scan_ind_count);
+ netdev_dbg(priv->net_dev, "disconnect :: scan_ind_count=%d\n",
+ priv->scan_ind_count);
wireless_send_event(netdev, SIOCGIWAP, &wrqu0, NULL);
}
priv->scan_ind_count = 0;
@@ -830,16 +773,18 @@ void hostif_scan_indication(struct ks_wlan_private *priv)
int i;
struct ap_info_t *ap_info;
- DPRINTK(3, "scan_ind_count = %d\n", priv->scan_ind_count);
+ netdev_dbg(priv->net_dev, "scan_ind_count = %d\n", priv->scan_ind_count);
ap_info = (struct ap_info_t *)(priv->rxp);
if (priv->scan_ind_count) {
- for (i = 0; i < priv->aplist.size; i++) { /* bssid check */
- if (memcmp(ap_info->bssid,
- priv->aplist.ap[i].bssid, ETH_ALEN) != 0)
+ /* bssid check */
+ for (i = 0; i < priv->aplist.size; i++) {
+ u8 *bssid = priv->aplist.ap[i].bssid;
+
+ if (ether_addr_equal(ap_info->bssid, bssid))
continue;
- if (ap_info->frame_type == FRAME_TYPE_PROBE_RESP)
+ if (ap_info->frame_type == IEEE80211_STYPE_PROBE_RESP)
get_ap_information(priv, ap_info,
&priv->aplist.ap[i]);
return;
@@ -847,14 +792,14 @@ void hostif_scan_indication(struct ks_wlan_private *priv)
}
priv->scan_ind_count++;
if (priv->scan_ind_count < LOCAL_APLIST_MAX + 1) {
- DPRINTK(4, " scan_ind_count=%d :: aplist.size=%d\n",
+ netdev_dbg(priv->net_dev, " scan_ind_count=%d :: aplist.size=%d\n",
priv->scan_ind_count, priv->aplist.size);
get_ap_information(priv, (struct ap_info_t *)(priv->rxp),
&(priv->aplist.ap[priv->scan_ind_count - 1]));
priv->aplist.size = priv->scan_ind_count;
} else {
- DPRINTK(4, " count over :: scan_ind_count=%d\n",
- priv->scan_ind_count);
+ netdev_dbg(priv->net_dev, " count over :: scan_ind_count=%d\n",
+ priv->scan_ind_count);
}
}
@@ -866,7 +811,6 @@ void hostif_stop_confirm(struct ks_wlan_private *priv)
struct net_device *netdev = priv->net_dev;
union iwreq_data wrqu0;
- DPRINTK(3, "\n");
if (priv->dev_state == DEVICE_STATE_SLEEP)
priv->dev_state = DEVICE_STATE_READY;
@@ -883,10 +827,7 @@ void hostif_stop_confirm(struct ks_wlan_private *priv)
if (is_disconnect_status(priv->connect_status) &&
is_connect_status(old_status)) {
eth_zero_addr(wrqu0.ap_addr.sa_data);
- DPRINTK(3, "IWEVENT: disconnect\n");
netdev_info(netdev, "IWEVENT: disconnect\n");
- DPRINTK(3, "disconnect :: scan_ind_count=%d\n",
- priv->scan_ind_count);
wireless_send_event(netdev, SIOCGIWAP, &wrqu0, NULL);
}
priv->scan_ind_count = 0;
@@ -898,7 +839,6 @@ void hostif_stop_confirm(struct ks_wlan_private *priv)
static
void hostif_ps_adhoc_set_confirm(struct ks_wlan_private *priv)
{
- DPRINTK(3, "\n");
priv->infra_status = 0; /* infrastructure mode cancel */
hostif_sme_enqueue(priv, SME_MODE_SET_CONFIRM);
}
@@ -908,9 +848,7 @@ void hostif_infrastructure_set_confirm(struct ks_wlan_private *priv)
{
u16 result_code;
- DPRINTK(3, "\n");
- result_code = get_WORD(priv);
- DPRINTK(3, "result code = %d\n", result_code);
+ result_code = get_word(priv);
priv->infra_status = 1; /* infrastructure mode set */
hostif_sme_enqueue(priv, SME_MODE_SET_CONFIRM);
}
@@ -918,7 +856,6 @@ void hostif_infrastructure_set_confirm(struct ks_wlan_private *priv)
static
void hostif_adhoc_set_confirm(struct ks_wlan_private *priv)
{
- DPRINTK(3, "\n");
priv->infra_status = 1; /* infrastructure mode set */
hostif_sme_enqueue(priv, SME_MODE_SET_CONFIRM);
}
@@ -937,7 +874,6 @@ void hostif_associate_indication(struct ks_wlan_private *priv)
static const char associnfo_leader0[] = "ASSOCINFO(ReqIEs=";
static const char associnfo_leader1[] = " RespIEs=";
- DPRINTK(3, "\n");
assoc_req = (struct association_request_t *)(priv->rxp);
assoc_resp = (struct association_response_t *)(assoc_req + 1);
pb = (unsigned char *)(assoc_resp + 1);
@@ -963,7 +899,6 @@ void hostif_associate_indication(struct ks_wlan_private *priv)
pbuf += sprintf(pbuf, ")");
wrqu.data.length += 1;
- DPRINTK(3, "IWEVENT:ASSOCINFO\n");
wireless_send_event(priv->net_dev, IWEVCUSTOM, &wrqu, buf);
}
@@ -974,16 +909,15 @@ void hostif_bss_scan_confirm(struct ks_wlan_private *priv)
struct net_device *dev = priv->net_dev;
union iwreq_data wrqu;
- result_code = get_DWORD(priv);
- DPRINTK(2, "result=%d :: scan_ind_count=%d\n", result_code,
- priv->scan_ind_count);
+ result_code = get_dword(priv);
+ netdev_dbg(priv->net_dev, "result=%d :: scan_ind_count=%d\n", result_code,
+ priv->scan_ind_count);
priv->sme_i.sme_flag &= ~SME_AP_SCAN;
hostif_sme_enqueue(priv, SME_BSS_SCAN_CONFIRM);
wrqu.data.length = 0;
wrqu.data.flags = 0;
- DPRINTK(3, "IWEVENT: SCAN CONFIRM\n");
wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
priv->scan_ind_count = 0;
}
@@ -997,32 +931,32 @@ void hostif_phy_information_confirm(struct ks_wlan_private *priv)
unsigned int transmitted_frame_count, received_fragment_count;
unsigned int failed_count, fcs_error_count;
- DPRINTK(3, "\n");
- rssi = get_BYTE(priv);
- signal = get_BYTE(priv);
- noise = get_BYTE(priv);
- link_speed = get_BYTE(priv);
- transmitted_frame_count = get_DWORD(priv);
- received_fragment_count = get_DWORD(priv);
- failed_count = get_DWORD(priv);
- fcs_error_count = get_DWORD(priv);
-
- DPRINTK(4, "phyinfo confirm rssi=%d signal=%d\n", rssi, signal);
+ rssi = get_byte(priv);
+ signal = get_byte(priv);
+ noise = get_byte(priv);
+ link_speed = get_byte(priv);
+ transmitted_frame_count = get_dword(priv);
+ received_fragment_count = get_dword(priv);
+ failed_count = get_dword(priv);
+ fcs_error_count = get_dword(priv);
+
+ netdev_dbg(priv->net_dev, "phyinfo confirm rssi=%d signal=%d\n",
+ rssi, signal);
priv->current_rate = (link_speed & RATE_MASK);
wstats->qual.qual = signal;
wstats->qual.level = 256 - rssi;
wstats->qual.noise = 0; /* invalid noise value */
wstats->qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
- DPRINTK(3, "\n rssi=%u\n"
+ netdev_dbg(priv->net_dev, "\n rssi=%u\n"
" signal=%u\n"
" link_speed=%ux500Kbps\n"
" transmitted_frame_count=%u\n"
" received_fragment_count=%u\n"
" failed_count=%u\n"
" fcs_error_count=%u\n",
- rssi, signal, link_speed, transmitted_frame_count,
- received_fragment_count, failed_count, fcs_error_count);
+ rssi, signal, link_speed, transmitted_frame_count,
+ received_fragment_count, failed_count, fcs_error_count);
/* wake_up_interruptible_all(&priv->confirm_wait); */
complete(&priv->confirm_wait);
}
@@ -1030,7 +964,7 @@ void hostif_phy_information_confirm(struct ks_wlan_private *priv)
static
void hostif_mic_failure_confirm(struct ks_wlan_private *priv)
{
- DPRINTK(3, "mic_failure=%u\n", priv->wpa.mic_failure.failure);
+ netdev_dbg(priv->net_dev, "mic_failure=%u\n", priv->wpa.mic_failure.failure);
hostif_sme_enqueue(priv, SME_MIC_FAILURE_CONFIRM);
}
@@ -1039,8 +973,7 @@ void hostif_event_check(struct ks_wlan_private *priv)
{
unsigned short event;
- DPRINTK(4, "\n");
- event = get_WORD(priv); /* get event */
+ event = get_word(priv); /* get event */
switch (event) {
case HIF_DATA_IND:
hostif_data_indication(priv);
@@ -1095,7 +1028,6 @@ void hostif_event_check(struct ks_wlan_private *priv)
break;
case HIF_AP_SET_CONF:
default:
- //DPRINTK(1, "undefined event[%04X]\n", event);
netdev_err(priv->net_dev, "undefined event[%04X]\n", event);
/* wake_up_all(&priv->confirm_wait); */
complete(&priv->confirm_wait);
@@ -1143,7 +1075,7 @@ int hostif_data_request(struct ks_wlan_private *priv, struct sk_buff *skb)
skb_len = skb->len;
if (skb_len > ETH_FRAME_LEN) {
- DPRINTK(1, "bad length skb_len=%d\n", skb_len);
+ netdev_err(priv->net_dev, "bad length skb_len=%d\n", skb_len);
ret = -EOVERFLOW;
goto err_kfree_skb;
}
@@ -1151,7 +1083,6 @@ int hostif_data_request(struct ks_wlan_private *priv, struct sk_buff *skb)
if (is_disconnect_status(priv->connect_status) ||
(priv->connect_status & FORCE_DISCONNECT) ||
priv->wpa.mic_failure.stop) {
- DPRINTK(3, " DISCONNECT\n");
if (netif_queue_stopped(priv->net_dev))
netif_wake_queue(priv->net_dev);
if (skb)
@@ -1160,8 +1091,8 @@ int hostif_data_request(struct ks_wlan_private *priv, struct sk_buff *skb)
return 0;
}
- /* for PowerSave */
- if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) { /* power save wakeup */
+ /* power save wakeup */
+ if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) {
if (!netif_queue_stopped(priv->net_dev))
netif_stop_queue(priv->net_dev);
}
@@ -1181,8 +1112,8 @@ int hostif_data_request(struct ks_wlan_private *priv, struct sk_buff *skb)
/* skb check */
eth = (struct ethhdr *)skb->data;
if (memcmp(&priv->eth_addr[0], eth->h_source, ETH_ALEN) != 0) {
- DPRINTK(1, "invalid mac address !!\n");
- DPRINTK(1, "ethernet->h_source=%pM\n", eth->h_source);
+ netdev_err(priv->net_dev, "invalid mac address !!\n");
+ netdev_err(priv->net_dev, "ethernet->h_source=%pM\n", eth->h_source);
ret = -ENXIO;
goto err_kfree;
}
@@ -1197,7 +1128,7 @@ int hostif_data_request(struct ks_wlan_private *priv, struct sk_buff *skb)
/* EtherType/Length check */
if (*(buffer + 1) + (*buffer << 8) > 1500) {
/* ProtocolEAP = *(buffer+1) + (*buffer << 8); */
- /* DPRINTK(2, "Send [SNAP]Type %x\n",ProtocolEAP); */
+ /* netdev_dbg(priv->net_dev, "Send [SNAP]Type %x\n",ProtocolEAP); */
/* SAP/CTL/OUI(6 byte) add */
*p++ = 0xAA; /* DSAP */
*p++ = 0xAA; /* SSAP */
@@ -1207,7 +1138,6 @@ int hostif_data_request(struct ks_wlan_private *priv, struct sk_buff *skb)
*p++ = 0x00; /* OUI ("000000") */
skb_len += 6;
} else {
- DPRINTK(4, "DIX\n");
/* Length(2 byte) delete */
buffer += 2;
length -= 2;
@@ -1224,7 +1154,7 @@ int hostif_data_request(struct ks_wlan_private *priv, struct sk_buff *skb)
eth_proto = ntohs(eth_hdr->h_proto);
/* for MIC FAILURE REPORT check */
- if (eth_proto == ETHER_PROTOCOL_TYPE_EAP &&
+ if (eth_proto == ETH_P_PAE &&
priv->wpa.mic_failure.failure > 0) {
aa1x_hdr = (struct ieee802_1x_hdr *)(eth_hdr + 1);
if (aa1x_hdr->type == IEEE802_1X_TYPE_EAPOL_KEY) {
@@ -1234,20 +1164,21 @@ int hostif_data_request(struct ks_wlan_private *priv, struct sk_buff *skb)
}
if (priv->wpa.rsn_enabled && priv->wpa.key[0].key_len) {
- if (eth_proto == ETHER_PROTOCOL_TYPE_EAP &&
+ /* no encryption */
+ if (eth_proto == ETH_P_PAE &&
priv->wpa.key[1].key_len == 0 &&
priv->wpa.key[2].key_len == 0 &&
priv->wpa.key[3].key_len == 0) {
- pp->auth_type = cpu_to_le16((uint16_t)TYPE_AUTH); /* no encryption */
+ pp->auth_type = cpu_to_le16((uint16_t)TYPE_AUTH);
} else {
if (priv->wpa.pairwise_suite == IW_AUTH_CIPHER_TKIP) {
- MichaelMICFunction(&michael_mic,
- (uint8_t *)priv->wpa.key[0].tx_mic_key,
- (uint8_t *)&pp->data[0],
- (int)skb_len,
- (uint8_t)0, /* priority */
- (uint8_t *)michael_mic.Result);
- memcpy(p, michael_mic.Result, 8);
+ michael_mic_function(&michael_mic,
+ (uint8_t *)priv->wpa.key[0].tx_mic_key,
+ (uint8_t *)&pp->data[0],
+ (int)skb_len,
+ (uint8_t)0, /* priority */
+ (uint8_t *)michael_mic.result);
+ memcpy(p, michael_mic.result, 8);
length += 8;
skb_len += 8;
p += 8;
@@ -1261,7 +1192,7 @@ int hostif_data_request(struct ks_wlan_private *priv, struct sk_buff *skb)
}
}
} else {
- if (eth_proto == ETHER_PROTOCOL_TYPE_EAP)
+ if (eth_proto == ETH_P_PAE)
pp->auth_type = cpu_to_le16((uint16_t)TYPE_AUTH);
else
pp->auth_type = cpu_to_le16((uint16_t)TYPE_DATA);
@@ -1278,11 +1209,11 @@ int hostif_data_request(struct ks_wlan_private *priv, struct sk_buff *skb)
send_packet_complete, skb);
/* MIC FAILURE REPORT check */
- if (eth_proto == ETHER_PROTOCOL_TYPE_EAP &&
+ if (eth_proto == ETH_P_PAE &&
priv->wpa.mic_failure.failure > 0) {
if (keyinfo & WPA_KEY_INFO_ERROR &&
keyinfo & WPA_KEY_INFO_REQUEST) {
- DPRINTK(3, " MIC ERROR Report SET : %04X\n", keyinfo);
+ netdev_err(priv->net_dev, " MIC ERROR Report SET : %04X\n", keyinfo);
hostif_sme_enqueue(priv, SME_MIC_FAILURE_REQUEST);
}
if (priv->wpa.mic_failure.failure == 2)
@@ -1299,11 +1230,11 @@ err_kfree_skb:
return ret;
}
-#define ps_confirm_wait_inc(priv) \
- do { \
- if (atomic_read(&priv->psstatus.status) > PS_ACTIVE_SET) \
- atomic_inc(&priv->psstatus.confirm_wait); \
- } while (0)
+static inline void ps_confirm_wait_inc(struct ks_wlan_private *priv)
+{
+ if (atomic_read(&priv->psstatus.status) > PS_ACTIVE_SET)
+ atomic_inc(&priv->psstatus.confirm_wait);
+}
static
void hostif_mib_get_request(struct ks_wlan_private *priv,
@@ -1311,8 +1242,6 @@ void hostif_mib_get_request(struct ks_wlan_private *priv,
{
struct hostif_mib_get_request_t *pp;
- DPRINTK(3, "\n");
-
pp = hostif_generic_request(sizeof(*pp), HIF_MIB_GET_REQ);
if (!pp)
return;
@@ -1331,12 +1260,8 @@ void hostif_mib_set_request(struct ks_wlan_private *priv,
{
struct hostif_mib_set_request_t *pp;
- DPRINTK(3, "\n");
-
- if (priv->dev_state < DEVICE_STATE_BOOT) {
- DPRINTK(3, "DeviceRemove\n");
+ if (priv->dev_state < DEVICE_STATE_BOOT)
return;
- }
pp = hostif_generic_request(sizeof(*pp), HIF_MIB_SET_REQ);
if (!pp)
@@ -1357,8 +1282,6 @@ void hostif_start_request(struct ks_wlan_private *priv, unsigned char mode)
{
struct hostif_start_request_t *pp;
- DPRINTK(3, "\n");
-
pp = hostif_generic_request(sizeof(*pp), HIF_START_REQ);
if (!pp)
return;
@@ -1373,137 +1296,63 @@ void hostif_start_request(struct ks_wlan_private *priv, unsigned char mode)
priv->scan_ind_count = 0;
}
-static
-void hostif_ps_adhoc_set_request(struct ks_wlan_private *priv)
+static __le16 ks_wlan_cap(struct ks_wlan_private *priv)
{
- struct hostif_ps_adhoc_set_request_t *pp;
- u16 capability;
+ u16 capability = 0x0000;
- DPRINTK(3, "\n");
+ if (priv->reg.preamble == SHORT_PREAMBLE)
+ capability |= WLAN_CAPABILITY_SHORT_PREAMBLE;
- pp = hostif_generic_request(sizeof(*pp), HIF_PS_ADH_SET_REQ);
- if (!pp)
- return;
+ capability &= ~(WLAN_CAPABILITY_PBCC); /* pbcc not support */
- pp->phy_type = cpu_to_le16((uint16_t)(priv->reg.phy_type));
- pp->cts_mode = cpu_to_le16((uint16_t)(priv->reg.cts_mode));
- pp->scan_type = cpu_to_le16((uint16_t)(priv->reg.scan_type));
- pp->channel = cpu_to_le16((uint16_t)(priv->reg.channel));
- pp->rate_set.size = priv->reg.rate_set.size;
- memcpy(&pp->rate_set.body[0], &priv->reg.rate_set.body[0],
- priv->reg.rate_set.size);
-
- capability = 0x0000;
- if (priv->reg.preamble == SHORT_PREAMBLE) {
- /* short preamble */
- capability |= BSS_CAP_SHORT_PREAMBLE;
- }
- capability &= ~(BSS_CAP_PBCC); /* pbcc not support */
if (priv->reg.phy_type != D_11B_ONLY_MODE) {
- capability |= BSS_CAP_SHORT_SLOT_TIME; /* ShortSlotTime support */
- capability &= ~(BSS_CAP_DSSS_OFDM); /* DSSS OFDM */
+ capability |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
+ capability &= ~(WLAN_CAPABILITY_DSSS_OFDM);
}
- pp->capability = cpu_to_le16((uint16_t)capability);
- /* send to device request */
- ps_confirm_wait_inc(priv);
- ks_wlan_hw_tx(priv, pp, hif_align_size(sizeof(*pp)), NULL, NULL);
+ return cpu_to_le16((uint16_t)capability);
}
-static
-void hostif_infrastructure_set_request(struct ks_wlan_private *priv)
+static void init_request(struct ks_wlan_private *priv, struct hostif_request_t *req)
{
- struct hostif_infrastructure_set_request_t *pp;
- u16 capability;
+ req->phy_type = cpu_to_le16((uint16_t)(priv->reg.phy_type));
+ req->cts_mode = cpu_to_le16((uint16_t)(priv->reg.cts_mode));
+ req->scan_type = cpu_to_le16((uint16_t)(priv->reg.scan_type));
+ req->rate_set.size = priv->reg.rate_set.size;
+ req->capability = ks_wlan_cap(priv);
+ memcpy(&req->rate_set.body[0], &priv->reg.rate_set.body[0],
+ priv->reg.rate_set.size);
+}
- DPRINTK(3, "ssid.size=%d\n", priv->reg.ssid.size);
+static
+void hostif_ps_adhoc_set_request(struct ks_wlan_private *priv)
+{
+ struct hostif_ps_adhoc_set_request_t *pp;
- pp = hostif_generic_request(sizeof(*pp), HIF_INFRA_SET_REQ);
+ pp = hostif_generic_request(sizeof(*pp), HIF_PS_ADH_SET_REQ);
if (!pp)
return;
- pp->phy_type = cpu_to_le16((uint16_t)(priv->reg.phy_type));
- pp->cts_mode = cpu_to_le16((uint16_t)(priv->reg.cts_mode));
- pp->scan_type = cpu_to_le16((uint16_t)(priv->reg.scan_type));
-
- pp->rate_set.size = priv->reg.rate_set.size;
- memcpy(&pp->rate_set.body[0], &priv->reg.rate_set.body[0],
- priv->reg.rate_set.size);
- pp->ssid.size = priv->reg.ssid.size;
- memcpy(&pp->ssid.body[0], &priv->reg.ssid.body[0], priv->reg.ssid.size);
-
- capability = 0x0000;
- if (priv->reg.preamble == SHORT_PREAMBLE) {
- /* short preamble */
- capability |= BSS_CAP_SHORT_PREAMBLE;
- }
- capability &= ~(BSS_CAP_PBCC); /* pbcc not support */
- if (priv->reg.phy_type != D_11B_ONLY_MODE) {
- capability |= BSS_CAP_SHORT_SLOT_TIME; /* ShortSlotTime support */
- capability &= ~(BSS_CAP_DSSS_OFDM); /* DSSS OFDM not support */
- }
- pp->capability = cpu_to_le16((uint16_t)capability);
- pp->beacon_lost_count =
- cpu_to_le16((uint16_t)(priv->reg.beacon_lost_count));
- pp->auth_type = cpu_to_le16((uint16_t)(priv->reg.authenticate_type));
-
- pp->channel_list.body[0] = 1;
- pp->channel_list.body[1] = 8;
- pp->channel_list.body[2] = 2;
- pp->channel_list.body[3] = 9;
- pp->channel_list.body[4] = 3;
- pp->channel_list.body[5] = 10;
- pp->channel_list.body[6] = 4;
- pp->channel_list.body[7] = 11;
- pp->channel_list.body[8] = 5;
- pp->channel_list.body[9] = 12;
- pp->channel_list.body[10] = 6;
- pp->channel_list.body[11] = 13;
- pp->channel_list.body[12] = 7;
- if (priv->reg.phy_type == D_11G_ONLY_MODE) {
- pp->channel_list.size = 13;
- } else {
- pp->channel_list.body[13] = 14;
- pp->channel_list.size = 14;
- }
+ init_request(priv, &pp->request);
+ pp->channel = cpu_to_le16((uint16_t)(priv->reg.channel));
/* send to device request */
ps_confirm_wait_inc(priv);
ks_wlan_hw_tx(priv, pp, hif_align_size(sizeof(*pp)), NULL, NULL);
}
-static void hostif_infrastructure_set2_request(struct ks_wlan_private *priv)
+static
+void hostif_infrastructure_set_request(struct ks_wlan_private *priv, int event)
{
- struct hostif_infrastructure_set2_request_t *pp;
- u16 capability;
-
- DPRINTK(2, "ssid.size=%d\n", priv->reg.ssid.size);
+ struct hostif_infrastructure_set_request_t *pp;
- pp = hostif_generic_request(sizeof(*pp), HIF_INFRA_SET2_REQ);
+ pp = hostif_generic_request(sizeof(*pp), event);
if (!pp)
return;
- pp->phy_type = cpu_to_le16((uint16_t)(priv->reg.phy_type));
- pp->cts_mode = cpu_to_le16((uint16_t)(priv->reg.cts_mode));
- pp->scan_type = cpu_to_le16((uint16_t)(priv->reg.scan_type));
-
- pp->rate_set.size = priv->reg.rate_set.size;
- memcpy(&pp->rate_set.body[0], &priv->reg.rate_set.body[0],
- priv->reg.rate_set.size);
+ init_request(priv, &pp->request);
pp->ssid.size = priv->reg.ssid.size;
memcpy(&pp->ssid.body[0], &priv->reg.ssid.body[0], priv->reg.ssid.size);
-
- capability = 0x0000;
- if (priv->reg.preamble == SHORT_PREAMBLE) {
- /* short preamble */
- capability |= BSS_CAP_SHORT_PREAMBLE;
- }
- capability &= ~(BSS_CAP_PBCC); /* pbcc not support */
- if (priv->reg.phy_type != D_11B_ONLY_MODE) {
- capability |= BSS_CAP_SHORT_SLOT_TIME; /* ShortSlotTime support */
- capability &= ~(BSS_CAP_DSSS_OFDM); /* DSSS OFDM not support */
- }
- pp->capability = cpu_to_le16((uint16_t)capability);
pp->beacon_lost_count =
cpu_to_le16((uint16_t)(priv->reg.beacon_lost_count));
pp->auth_type = cpu_to_le16((uint16_t)(priv->reg.authenticate_type));
@@ -1528,8 +1377,6 @@ static void hostif_infrastructure_set2_request(struct ks_wlan_private *priv)
pp->channel_list.size = 14;
}
- memcpy(pp->bssid, priv->reg.bssid, ETH_ALEN);
-
/* send to device request */
ps_confirm_wait_inc(priv);
ks_wlan_hw_tx(priv, pp, hif_align_size(sizeof(*pp)), NULL, NULL);
@@ -1539,36 +1386,16 @@ static
void hostif_adhoc_set_request(struct ks_wlan_private *priv)
{
struct hostif_adhoc_set_request_t *pp;
- u16 capability;
-
- DPRINTK(3, "\n");
pp = hostif_generic_request(sizeof(*pp), HIF_ADH_SET_REQ);
if (!pp)
return;
- pp->phy_type = cpu_to_le16((uint16_t)(priv->reg.phy_type));
- pp->cts_mode = cpu_to_le16((uint16_t)(priv->reg.cts_mode));
- pp->scan_type = cpu_to_le16((uint16_t)(priv->reg.scan_type));
+ init_request(priv, &pp->request);
pp->channel = cpu_to_le16((uint16_t)(priv->reg.channel));
- pp->rate_set.size = priv->reg.rate_set.size;
- memcpy(&pp->rate_set.body[0], &priv->reg.rate_set.body[0],
- priv->reg.rate_set.size);
pp->ssid.size = priv->reg.ssid.size;
memcpy(&pp->ssid.body[0], &priv->reg.ssid.body[0], priv->reg.ssid.size);
- capability = 0x0000;
- if (priv->reg.preamble == SHORT_PREAMBLE) {
- /* short preamble */
- capability |= BSS_CAP_SHORT_PREAMBLE;
- }
- capability &= ~(BSS_CAP_PBCC); /* pbcc not support */
- if (priv->reg.phy_type != D_11B_ONLY_MODE) {
- capability |= BSS_CAP_SHORT_SLOT_TIME; /* ShortSlotTime support */
- capability &= ~(BSS_CAP_DSSS_OFDM); /* DSSS OFDM not support */
- }
- pp->capability = cpu_to_le16((uint16_t)capability);
-
/* send to device request */
ps_confirm_wait_inc(priv);
ks_wlan_hw_tx(priv, pp, hif_align_size(sizeof(*pp)), NULL, NULL);
@@ -1578,35 +1405,15 @@ static
void hostif_adhoc_set2_request(struct ks_wlan_private *priv)
{
struct hostif_adhoc_set2_request_t *pp;
- u16 capability;
-
- DPRINTK(3, "\n");
pp = hostif_generic_request(sizeof(*pp), HIF_ADH_SET_REQ);
if (!pp)
return;
- pp->phy_type = cpu_to_le16((uint16_t)(priv->reg.phy_type));
- pp->cts_mode = cpu_to_le16((uint16_t)(priv->reg.cts_mode));
- pp->scan_type = cpu_to_le16((uint16_t)(priv->reg.scan_type));
- pp->rate_set.size = priv->reg.rate_set.size;
- memcpy(&pp->rate_set.body[0], &priv->reg.rate_set.body[0],
- priv->reg.rate_set.size);
+ init_request(priv, &pp->request);
pp->ssid.size = priv->reg.ssid.size;
memcpy(&pp->ssid.body[0], &priv->reg.ssid.body[0], priv->reg.ssid.size);
- capability = 0x0000;
- if (priv->reg.preamble == SHORT_PREAMBLE) {
- /* short preamble */
- capability |= BSS_CAP_SHORT_PREAMBLE;
- }
- capability &= ~(BSS_CAP_PBCC); /* pbcc not support */
- if (priv->reg.phy_type != D_11B_ONLY_MODE) {
- capability |= BSS_CAP_SHORT_SLOT_TIME; /* ShortSlotTime support */
- capability &= ~(BSS_CAP_DSSS_OFDM); /* DSSS OFDM not support */
- }
- pp->capability = cpu_to_le16((uint16_t)capability);
-
pp->channel_list.body[0] = priv->reg.channel;
pp->channel_list.size = 1;
memcpy(pp->bssid, priv->reg.bssid, ETH_ALEN);
@@ -1621,8 +1428,6 @@ void hostif_stop_request(struct ks_wlan_private *priv)
{
struct hostif_stop_request_t *pp;
- DPRINTK(3, "\n");
-
pp = hostif_generic_request(sizeof(*pp), HIF_STOP_REQ);
if (!pp)
return;
@@ -1637,8 +1442,6 @@ void hostif_phy_information_request(struct ks_wlan_private *priv)
{
struct hostif_phy_information_request_t *pp;
- DPRINTK(3, "\n");
-
pp = hostif_generic_request(sizeof(*pp), HIF_PHY_INFO_REQ);
if (!pp)
return;
@@ -1663,9 +1466,6 @@ void hostif_power_mgmt_request(struct ks_wlan_private *priv,
{
struct hostif_power_mgmt_request_t *pp;
- DPRINTK(3, "mode=%lu wake_up=%lu receive_dtims=%lu\n", mode, wake_up,
- receive_dtims);
-
pp = hostif_generic_request(sizeof(*pp), HIF_POWER_MGMT_REQ);
if (!pp)
return;
@@ -1685,8 +1485,6 @@ void hostif_sleep_request(struct ks_wlan_private *priv,
{
struct hostif_sleep_request_t *pp;
- DPRINTK(3, "mode=%lu\n", (long)mode);
-
if (mode == SLP_SLEEP) {
pp = hostif_generic_request(sizeof(*pp), HIF_SLEEP_REQ);
if (!pp)
@@ -1700,7 +1498,7 @@ void hostif_sleep_request(struct ks_wlan_private *priv,
atomic_set(&priv->sleepstatus.wakeup_request, 1);
queue_delayed_work(priv->wq, &priv->rw_dwork, 1);
} else {
- DPRINTK(3, "invalid mode %ld\n", (long)mode);
+ netdev_err(priv->net_dev, "invalid mode %ld\n", (long)mode);
return;
}
}
@@ -1712,8 +1510,6 @@ void hostif_bss_scan_request(struct ks_wlan_private *priv,
{
struct hostif_bss_scan_request_t *pp;
- DPRINTK(2, "\n");
-
pp = hostif_generic_request(sizeof(*pp), HIF_SCAN_REQ);
if (!pp)
return;
@@ -1764,8 +1560,6 @@ void hostif_mic_failure_request(struct ks_wlan_private *priv,
{
struct hostif_mic_failure_request_t *pp;
- DPRINTK(3, "count=%d :: timer=%d\n", failure_count, timer);
-
pp = hostif_generic_request(sizeof(*pp), HIF_MIC_FAILURE_REQ);
if (!pp)
return;
@@ -1805,14 +1599,12 @@ static void devio_rec_ind(struct ks_wlan_private *priv, unsigned char *p,
void hostif_receive(struct ks_wlan_private *priv, unsigned char *p,
unsigned int size)
{
- DPRINTK(4, "\n");
-
devio_rec_ind(priv, p, size);
priv->rxp = p;
priv->rx_size = size;
- if (get_WORD(priv) == priv->rx_size) { /* length check !! */
+ if (get_word(priv) == priv->rx_size) { /* length check !! */
hostif_event_check(priv); /* event check */
}
}
@@ -2137,11 +1929,11 @@ void hostif_sme_mode_setup(struct ks_wlan_private *priv)
case MODE_INFRASTRUCTURE:
/* Infrastructure mode */
if (!is_valid_ether_addr((u8 *)priv->reg.bssid)) {
- hostif_infrastructure_set_request(priv);
+ hostif_infrastructure_set_request(priv, HIF_INFRA_SET_REQ);
} else {
- hostif_infrastructure_set2_request(priv);
- DPRINTK(2,
- "Infra bssid = %pM\n", priv->reg.bssid);
+ hostif_infrastructure_set_request(priv, HIF_INFRA_SET2_REQ);
+ netdev_dbg(priv->net_dev,
+ "Infra bssid = %pM\n", priv->reg.bssid);
}
break;
case MODE_ADHOC:
@@ -2150,8 +1942,8 @@ void hostif_sme_mode_setup(struct ks_wlan_private *priv)
hostif_adhoc_set_request(priv);
} else {
hostif_adhoc_set2_request(priv);
- DPRINTK(2,
- "Adhoc bssid = %pM\n", priv->reg.bssid);
+ netdev_dbg(priv->net_dev,
+ "Adhoc bssid = %pM\n", priv->reg.bssid);
}
break;
default:
@@ -2169,8 +1961,6 @@ void hostif_sme_multicast_set(struct ks_wlan_private *priv)
__le32 filter_type;
int i = 0;
- DPRINTK(3, "\n");
-
spin_lock(&priv->multicast_spin);
memset(set_address, 0, NIC_MAX_MCAST_LIST * ETH_ALEN);
@@ -2220,7 +2010,6 @@ void hostif_sme_power_mgmt_set(struct ks_wlan_private *priv)
{
unsigned long mode, wake_up, receive_dtims;
- DPRINTK(3, "\n");
switch (priv->reg.power_mgmt) {
case POWER_MGMT_ACTIVE:
mode = POWER_ACTIVE;
@@ -2261,7 +2050,6 @@ void hostif_sme_power_mgmt_set(struct ks_wlan_private *priv)
static
void hostif_sme_sleep_set(struct ks_wlan_private *priv)
{
- DPRINTK(3, "\n");
switch (priv->sleep_mode) {
case SLP_SLEEP:
hostif_sleep_request(priv, priv->sleep_mode);
@@ -2345,7 +2133,6 @@ void hostif_sme_set_pmksa(struct ks_wlan_private *priv)
struct pmk_t *pmk;
int i;
- DPRINTK(4, "pmklist.size=%d\n", priv->pmklist.size);
i = 0;
list_for_each_entry(pmk, &priv->pmklist.head, list) {
if (i < PMK_LIST_MAX) {
@@ -2369,7 +2156,6 @@ void hostif_sme_execute(struct ks_wlan_private *priv, int event)
{
__le32 val;
- DPRINTK(3, "event=%d\n", event);
switch (event) {
case SME_START:
if (priv->dev_state == DEVICE_STATE_BOOT)
@@ -2403,8 +2189,8 @@ void hostif_sme_execute(struct ks_wlan_private *priv, int event)
priv->wpa.mic_failure.failure - 1,
priv->wpa.mic_failure.counter);
} else {
- DPRINTK(4, "SME_MIC_FAILURE_REQUEST: failure count=%u error?\n",
- priv->wpa.mic_failure.failure);
+ netdev_err(priv->net_dev, "SME_MIC_FAILURE_REQUEST: failure count=%u error?\n",
+ priv->wpa.mic_failure.failure);
}
break;
case SME_MIC_FAILURE_CONFIRM:
@@ -2544,8 +2330,6 @@ void hostif_sme_task(unsigned long dev)
{
struct ks_wlan_private *priv = (struct ks_wlan_private *)dev;
- DPRINTK(3, "\n");
-
if (priv->dev_state < DEVICE_STATE_BOOT)
return;
@@ -2561,16 +2345,10 @@ void hostif_sme_task(unsigned long dev)
/* send to Station Management Entity module */
void hostif_sme_enqueue(struct ks_wlan_private *priv, unsigned short event)
{
- DPRINTK(3, "\n");
-
/* enqueue sme event */
if (cnt_smeqbody(priv) < (SME_EVENT_BUFF_SIZE - 1)) {
priv->sme_i.event_buff[priv->sme_i.qtail] = event;
inc_smeqtail(priv);
-#ifdef KS_WLAN_DEBUG
- if (priv->sme_i.max_event_count < cnt_smeqbody(priv))
- priv->sme_i.max_event_count = cnt_smeqbody(priv);
-#endif /* KS_WLAN_DEBUG */
} else {
/* in case of buffer overflow */
netdev_err(priv->net_dev, "sme queue buffer overflow\n");
@@ -2579,55 +2357,79 @@ void hostif_sme_enqueue(struct ks_wlan_private *priv, unsigned short event)
tasklet_schedule(&priv->sme_task);
}
-int hostif_init(struct ks_wlan_private *priv)
+static inline void hostif_aplist_init(struct ks_wlan_private *priv)
{
- int i;
-
- DPRINTK(3, "\n");
-
+ size_t size = LOCAL_APLIST_MAX * sizeof(struct local_ap_t);
priv->aplist.size = 0;
- for (i = 0; i < LOCAL_APLIST_MAX; i++)
- memset(&priv->aplist.ap[i], 0, sizeof(struct local_ap_t));
+ memset(&priv->aplist.ap[0], 0, size);
+}
+
+static inline void hostif_status_init(struct ks_wlan_private *priv)
+{
priv->infra_status = 0;
priv->current_rate = 4;
priv->connect_status = DISCONNECT_STATUS;
+}
- spin_lock_init(&priv->multicast_spin);
+static inline void hostif_sme_init(struct ks_wlan_private *priv)
+{
+ priv->sme_i.sme_status = SME_IDLE;
+ priv->sme_i.qhead = 0;
+ priv->sme_i.qtail = 0;
+ spin_lock_init(&priv->sme_i.sme_spin);
+ priv->sme_i.sme_flag = 0;
+ tasklet_init(&priv->sme_task, hostif_sme_task, (unsigned long)priv);
+}
- spin_lock_init(&priv->dev_read_lock);
- init_waitqueue_head(&priv->devread_wait);
- priv->dev_count = 0;
- atomic_set(&priv->event_count, 0);
- atomic_set(&priv->rec_count, 0);
+static inline void hostif_wpa_init(struct ks_wlan_private *priv)
+{
+ memset(&priv->wpa, 0, sizeof(priv->wpa));
+ priv->wpa.rsn_enabled = 0;
+ priv->wpa.mic_failure.failure = 0;
+ priv->wpa.mic_failure.last_failure_time = 0;
+ priv->wpa.mic_failure.stop = 0;
+}
- /* for power save */
+static inline void hostif_power_save_init(struct ks_wlan_private *priv)
+{
atomic_set(&priv->psstatus.status, PS_NONE);
atomic_set(&priv->psstatus.confirm_wait, 0);
atomic_set(&priv->psstatus.snooze_guard, 0);
init_completion(&priv->psstatus.wakeup_wait);
INIT_WORK(&priv->wakeup_work, ks_wlan_hw_wakeup_task);
+}
+
+static inline void hostif_pmklist_init(struct ks_wlan_private *priv)
+{
+ int i;
- /* WPA */
- memset(&priv->wpa, 0, sizeof(priv->wpa));
- priv->wpa.rsn_enabled = 0;
- priv->wpa.mic_failure.failure = 0;
- priv->wpa.mic_failure.last_failure_time = 0;
- priv->wpa.mic_failure.stop = 0;
memset(&priv->pmklist, 0, sizeof(priv->pmklist));
INIT_LIST_HEAD(&priv->pmklist.head);
for (i = 0; i < PMK_LIST_MAX; i++)
INIT_LIST_HEAD(&priv->pmklist.pmk[i].list);
+}
- priv->sme_i.sme_status = SME_IDLE;
- priv->sme_i.qhead = 0;
- priv->sme_i.qtail = 0;
-#ifdef KS_WLAN_DEBUG
- priv->sme_i.max_event_count = 0;
-#endif
- spin_lock_init(&priv->sme_i.sme_spin);
- priv->sme_i.sme_flag = 0;
+static inline void hostif_counters_init(struct ks_wlan_private *priv)
+{
+ priv->dev_count = 0;
+ atomic_set(&priv->event_count, 0);
+ atomic_set(&priv->rec_count, 0);
+}
- tasklet_init(&priv->sme_task, hostif_sme_task, (unsigned long)priv);
+int hostif_init(struct ks_wlan_private *priv)
+{
+ hostif_aplist_init(priv);
+ hostif_status_init(priv);
+
+ spin_lock_init(&priv->multicast_spin);
+ spin_lock_init(&priv->dev_read_lock);
+ init_waitqueue_head(&priv->devread_wait);
+
+ hostif_counters_init(priv);
+ hostif_power_save_init(priv);
+ hostif_wpa_init(priv);
+ hostif_pmklist_init(priv);
+ hostif_sme_init(priv);
return 0;
}
diff --git a/drivers/staging/ks7010/ks_hostif.h b/drivers/staging/ks7010/ks_hostif.h
index 5bae8d468e23..2f918b11b337 100644
--- a/drivers/staging/ks7010/ks_hostif.h
+++ b/drivers/staging/ks7010/ks_hostif.h
@@ -13,6 +13,7 @@
#define _KS_HOSTIF_H_
#include <linux/compiler.h>
+#include <linux/ieee80211.h>
/*
* HOST-MAC I/F events
@@ -58,7 +59,7 @@
/*
* HOST-MAC I/F data structure
- * Byte alignmet Little Endian
+ * Byte alignment Little Endian
*/
struct hostif_hdr {
@@ -224,10 +225,9 @@ struct hostif_start_confirm_t {
__le16 result_code;
} __packed;
-#define SSID_MAX_SIZE 32
struct ssid_t {
u8 size;
- u8 body[SSID_MAX_SIZE];
+ u8 body[IEEE80211_MAX_SSID_LEN];
u8 ssid_pad;
} __packed;
@@ -284,20 +284,8 @@ struct ap_info_t {
u8 pad0; /* +09 */
__le16 beacon_period; /* +10 */
__le16 capability; /* +12 */
-#define BSS_CAP_ESS BIT(0)
-#define BSS_CAP_IBSS BIT(1)
-#define BSS_CAP_CF_POLABLE BIT(2)
-#define BSS_CAP_CF_POLL_REQ BIT(3)
-#define BSS_CAP_PRIVACY BIT(4)
-#define BSS_CAP_SHORT_PREAMBLE BIT(5)
-#define BSS_CAP_PBCC BIT(6)
-#define BSS_CAP_CHANNEL_AGILITY BIT(7)
-#define BSS_CAP_SHORT_SLOT_TIME BIT(10)
-#define BSS_CAP_DSSS_OFDM BIT(13)
u8 frame_type; /* +14 */
u8 ch_info; /* +15 */
-#define FRAME_TYPE_BEACON 0x80
-#define FRAME_TYPE_PROBE_RESP 0x50
__le16 body_size; /* +16 */
u8 body[1024]; /* +18 */
/* +1032 */
@@ -347,6 +335,22 @@ struct hostif_stop_confirm_t {
__le16 result_code;
} __packed;
+#define D_11B_ONLY_MODE 0
+#define D_11G_ONLY_MODE 1
+#define D_11BG_COMPATIBLE_MODE 2
+#define D_11A_ONLY_MODE 3
+
+#define CTS_MODE_FALSE 0
+#define CTS_MODE_TRUE 1
+
+struct hostif_request_t {
+ __le16 phy_type;
+ __le16 cts_mode;
+ __le16 scan_type;
+ __le16 capability;
+ struct rate_set16_t rate_set;
+} __packed;
+
/**
* struct hostif_ps_adhoc_set_request_t - pseudo adhoc mode
* @capability: bit5 : preamble
@@ -356,18 +360,8 @@ struct hostif_stop_confirm_t {
*/
struct hostif_ps_adhoc_set_request_t {
struct hostif_hdr header;
- __le16 phy_type;
-#define D_11B_ONLY_MODE 0
-#define D_11G_ONLY_MODE 1
-#define D_11BG_COMPATIBLE_MODE 2
-#define D_11A_ONLY_MODE 3
- __le16 cts_mode;
-#define CTS_MODE_FALSE 0
-#define CTS_MODE_TRUE 1
+ struct hostif_request_t request;
__le16 channel;
- struct rate_set16_t rate_set;
- __le16 capability;
- __le16 scan_type;
} __packed;
struct hostif_ps_adhoc_set_confirm_t {
@@ -375,48 +369,23 @@ struct hostif_ps_adhoc_set_confirm_t {
__le16 result_code;
} __packed;
-/**
- * struct hostif_infrastructure_set_request_t
- * @capability: bit5 : preamble
- * bit6 : pbcc - Not supported always 0
- * bit10 : ShortSlotTime
- * bit13 : DSSS-OFDM - Not supported always 0
- */
-struct hostif_infrastructure_set_request_t {
- struct hostif_hdr header;
- __le16 phy_type;
- __le16 cts_mode;
- struct rate_set16_t rate_set;
- struct ssid_t ssid;
- __le16 capability;
- __le16 beacon_lost_count;
- __le16 auth_type;
#define AUTH_TYPE_OPEN_SYSTEM 0
#define AUTH_TYPE_SHARED_KEY 1
- struct channel_list_t channel_list;
- __le16 scan_type;
-} __packed;
/**
- * struct hostif_infrastructure_set2_request_t
+ * struct hostif_infrastructure_set_request_t
* @capability: bit5 : preamble
* bit6 : pbcc - Not supported always 0
* bit10 : ShortSlotTime
* bit13 : DSSS-OFDM - Not supported always 0
*/
-struct hostif_infrastructure_set2_request_t {
+struct hostif_infrastructure_set_request_t {
struct hostif_hdr header;
- __le16 phy_type;
- __le16 cts_mode;
- struct rate_set16_t rate_set;
+ struct hostif_request_t request;
struct ssid_t ssid;
- __le16 capability;
__le16 beacon_lost_count;
__le16 auth_type;
-#define AUTH_TYPE_OPEN_SYSTEM 0
-#define AUTH_TYPE_SHARED_KEY 1
struct channel_list_t channel_list;
- __le16 scan_type;
u8 bssid[ETH_ALEN];
} __packed;
@@ -434,13 +403,9 @@ struct hostif_infrastructure_set_confirm_t {
*/
struct hostif_adhoc_set_request_t {
struct hostif_hdr header;
- __le16 phy_type;
- __le16 cts_mode;
- __le16 channel;
- struct rate_set16_t rate_set;
+ struct hostif_request_t request;
struct ssid_t ssid;
- __le16 capability;
- __le16 scan_type;
+ __le16 channel;
} __packed;
/**
@@ -452,13 +417,9 @@ struct hostif_adhoc_set_request_t {
*/
struct hostif_adhoc_set2_request_t {
struct hostif_hdr header;
- __le16 phy_type;
- __le16 cts_mode;
+ struct hostif_request_t request;
__le16 reserved;
- struct rate_set16_t rate_set;
struct ssid_t ssid;
- __le16 capability;
- __le16 scan_type;
struct channel_list_t channel_list;
u8 bssid[ETH_ALEN];
} __packed;
@@ -475,8 +436,6 @@ struct last_associate_t {
struct association_request_t {
u8 type;
-#define FRAME_TYPE_ASSOC_REQ 0x00
-#define FRAME_TYPE_REASSOC_REQ 0x20
u8 pad;
__le16 capability;
__le16 listen_interval;
@@ -486,8 +445,6 @@ struct association_request_t {
struct association_response_t {
u8 type;
-#define FRAME_TYPE_ASSOC_RESP 0x10
-#define FRAME_TYPE_REASSOC_RESP 0x30
u8 pad;
__le16 capability;
__le16 status;
diff --git a/drivers/staging/ks7010/ks_wlan.h b/drivers/staging/ks7010/ks_wlan.h
index 3767079be00d..1b7036c32d1c 100644
--- a/drivers/staging/ks7010/ks_wlan.h
+++ b/drivers/staging/ks7010/ks_wlan.h
@@ -34,16 +34,6 @@
#include "ks7010_sdio.h"
-#ifdef KS_WLAN_DEBUG
-#define DPRINTK(n, fmt, args...) \
- do { \
- if (KS_WLAN_DEBUG > (n)) \
- pr_notice("%s: "fmt, __func__, ## args); \
- } while (0)
-#else
-#define DPRINTK(n, fmt, args...)
-#endif
-
struct ks_wlan_parameter {
u8 operation_mode; /* Operation Mode */
u8 channel; /* Channel */
@@ -64,7 +54,7 @@ struct ks_wlan_parameter {
#define BEACON_LOST_COUNT_MAX 65535
u32 beacon_lost_count; /* Beacon Lost Count */
u32 rts; /* RTS Threashold */
- u32 fragment; /* Fragmentation Threashold */
+ u32 fragment; /* Fragmentation Threshold */
u32 privacy_invoked;
u32 wep_index;
struct {
@@ -97,7 +87,7 @@ enum {
#define SME_WEP_VAL2 BIT(6)
#define SME_WEP_VAL3 BIT(7)
#define SME_WEP_VAL4 BIT(8)
-#define SME_WEP_VAL_MASK (SME_WEP_VAL1 | SME_WEP_VAL2 | SME_WEP_VAL3 | SME_WEP_VAL4)
+#define SME_WEP_VAL_MASK GENMASK(8, 5)
#define SME_RSN BIT(9)
#define SME_RSN_MULTICAST BIT(10)
#define SME_RSN_UNICAST BIT(11)
@@ -202,10 +192,6 @@ struct sme_info {
int event_buff[SME_EVENT_BUFF_SIZE];
unsigned int qhead;
unsigned int qtail;
-#ifdef KS_WLAN_DEBUG
- /* for debug */
- unsigned int max_event_count;
-#endif
spinlock_t sme_spin;
unsigned long sme_flag;
};
@@ -420,7 +406,6 @@ struct ks_wlan_private {
struct tasklet_struct rx_bh_task;
struct net_device *net_dev;
- int reg_net; /* register_netdev */
struct net_device_stats nstats;
struct iw_statistics wstats;
@@ -470,7 +455,7 @@ struct ks_wlan_private {
unsigned char firmware_version[128 + 1];
int version_size;
- int mac_address_valid; /* Mac Address Status */
+ bool mac_address_valid; /* Mac Address Status */
int dev_state;
diff --git a/drivers/staging/ks7010/ks_wlan_ioctl.h b/drivers/staging/ks7010/ks_wlan_ioctl.h
index 28b381c24b42..121e7cb808a2 100644
--- a/drivers/staging/ks7010/ks_wlan_ioctl.h
+++ b/drivers/staging/ks7010/ks_wlan_ioctl.h
@@ -58,7 +58,6 @@
#include "ks_wlan.h"
#include <linux/netdevice.h>
-int ks_wlan_read_config_file(struct ks_wlan_private *priv);
int ks_wlan_setup_parameter(struct ks_wlan_private *priv,
unsigned int commit_flag);
diff --git a/drivers/staging/ks7010/ks_wlan_net.c b/drivers/staging/ks7010/ks_wlan_net.c
index e48c55769c94..9078e13b0d4a 100644
--- a/drivers/staging/ks7010/ks_wlan_net.c
+++ b/drivers/staging/ks7010/ks_wlan_net.c
@@ -86,7 +86,7 @@ int ks_wlan_update_phy_information(struct ks_wlan_private *priv)
{
struct iw_statistics *wstats = &priv->wstats;
- DPRINTK(4, "in_interrupt = %ld\n", in_interrupt());
+ netdev_dbg(priv->net_dev, "in_interrupt = %ld\n", in_interrupt());
if (priv->dev_state < DEVICE_STATE_READY)
return -EBUSY; /* not finished initialize */
@@ -103,7 +103,7 @@ int ks_wlan_update_phy_information(struct ks_wlan_private *priv)
/* interruptible_sleep_on_timeout(&priv->confirm_wait, HZ/2); */
if (!wait_for_completion_interruptible_timeout
(&priv->confirm_wait, HZ / 2)) {
- DPRINTK(1, "wait time out!!\n");
+ netdev_dbg(priv->net_dev, "wait time out!!\n");
}
atomic_inc(&update_phyinfo);
@@ -116,15 +116,13 @@ int ks_wlan_update_phy_information(struct ks_wlan_private *priv)
static
void ks_wlan_update_phyinfo_timeout(struct timer_list *unused)
{
- DPRINTK(4, "in_interrupt = %ld\n", in_interrupt());
+ pr_debug("in_interrupt = %ld\n", in_interrupt());
atomic_set(&update_phyinfo, 0);
}
int ks_wlan_setup_parameter(struct ks_wlan_private *priv,
unsigned int commit_flag)
{
- DPRINTK(2, "\n");
-
hostif_sme_enqueue(priv, SME_STOP_REQUEST);
if (commit_flag & SME_RTS)
@@ -268,8 +266,6 @@ static int ks_wlan_set_essid(struct net_device *dev,
struct ks_wlan_private *priv = netdev_priv(dev);
size_t len;
- DPRINTK(2, " %d\n", dwrq->flags);
-
if (priv->sleep_mode == SLP_SLEEP)
return -EPERM;
@@ -280,7 +276,6 @@ static int ks_wlan_set_essid(struct net_device *dev,
memset(priv->reg.ssid.body, 0, sizeof(priv->reg.ssid.body));
priv->reg.ssid.size = 0;
} else {
-#if 1
len = dwrq->length;
/* iwconfig uses nul termination in SSID.. */
if (len > 0 && extra[len - 1] == '\0')
@@ -290,28 +285,14 @@ static int ks_wlan_set_essid(struct net_device *dev,
if (len > IW_ESSID_MAX_SIZE)
return -EINVAL;
-#else
- /* Check the size of the string */
- if (dwrq->length > IW_ESSID_MAX_SIZE + 1)
- return -E2BIG;
-
-#endif
-
/* Set the SSID */
memset(priv->reg.ssid.body, 0, sizeof(priv->reg.ssid.body));
-
-#if 1
memcpy(priv->reg.ssid.body, extra, len);
priv->reg.ssid.size = len;
-#else
- memcpy(priv->reg.ssid.body, extra, dwrq->length);
- priv->reg.ssid.size = dwrq->length;
-#endif
}
/* Write it to the card */
priv->need_commit |= SME_MODE_SET;
-// return -EINPROGRESS; /* Call commit handler */
ks_wlan_setup_parameter(priv, priv->need_commit);
priv->need_commit = 0;
return 0;
@@ -352,8 +333,6 @@ static int ks_wlan_set_wap(struct net_device *dev, struct iw_request_info *info,
{
struct ks_wlan_private *priv = netdev_priv(dev);
- DPRINTK(2, "\n");
-
if (priv->sleep_mode == SLP_SLEEP)
return -EPERM;
@@ -370,7 +349,7 @@ static int ks_wlan_set_wap(struct net_device *dev, struct iw_request_info *info,
return -EOPNOTSUPP;
}
- DPRINTK(2, "bssid = %pM\n", priv->reg.bssid);
+ netdev_dbg(dev, "bssid = %pM\n", priv->reg.bssid);
/* Write it to the card */
if (priv->need_commit) {
@@ -683,8 +662,8 @@ static int ks_wlan_get_rate(struct net_device *dev,
{
struct ks_wlan_private *priv = netdev_priv(dev);
- DPRINTK(2, "in_interrupt = %ld update_phyinfo = %d\n",
- in_interrupt(), atomic_read(&update_phyinfo));
+ netdev_dbg(dev, "in_interrupt = %ld update_phyinfo = %d\n",
+ in_interrupt(), atomic_read(&update_phyinfo));
if (priv->sleep_mode == SLP_SLEEP)
return -EPERM;
@@ -785,8 +764,6 @@ static int ks_wlan_set_mode(struct net_device *dev,
{
struct ks_wlan_private *priv = netdev_priv(dev);
- DPRINTK(2, "mode=%d\n", *uwrq);
-
if (priv->sleep_mode == SLP_SLEEP)
return -EPERM;
@@ -848,8 +825,6 @@ static int ks_wlan_set_encode(struct net_device *dev,
int current_index = priv->reg.wep_index;
int i;
- DPRINTK(2, "flags=%04X\n", dwrq->flags);
-
if (priv->sleep_mode == SLP_SLEEP)
return -EPERM;
@@ -986,7 +961,6 @@ static int ks_wlan_get_encode(struct net_device *dev,
/* dwrq->flags |= IW_ENCODE_NOKEY; */
}
dwrq->flags |= index + 1;
- DPRINTK(2, "encoding flag = 0x%04X\n", dwrq->flags);
/* Copy the key to the user buffer */
if ((index >= 0) && (index < 4))
dwrq->length = priv->reg.wep_key[index].size;
@@ -1058,8 +1032,6 @@ static int ks_wlan_get_range(struct net_device *dev,
struct iw_range *range = (struct iw_range *)extra;
int i, k;
- DPRINTK(2, "\n");
-
if (priv->sleep_mode == SLP_SLEEP)
return -EPERM;
@@ -1300,8 +1272,6 @@ static int ks_wlan_set_scan(struct net_device *dev,
struct ks_wlan_private *priv = netdev_priv(dev);
struct iw_scan_req *req = NULL;
- DPRINTK(2, "\n");
-
if (priv->sleep_mode == SLP_SLEEP)
return -EPERM;
@@ -1366,8 +1336,8 @@ static inline char *ks_wlan_translate_scan(struct net_device *dev,
/* Add mode */
iwe.cmd = SIOCGIWMODE;
capabilities = ap->capability;
- if (capabilities & (BSS_CAP_ESS | BSS_CAP_IBSS)) {
- if (capabilities & BSS_CAP_ESS)
+ if (capabilities & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
+ if (capabilities & WLAN_CAPABILITY_ESS)
iwe.u.mode = IW_MODE_INFRA;
else
iwe.u.mode = IW_MODE_ADHOC;
@@ -1396,7 +1366,7 @@ static inline char *ks_wlan_translate_scan(struct net_device *dev,
/* Add encryption capability */
iwe.cmd = SIOCGIWENCODE;
- if (capabilities & BSS_CAP_PRIVACY)
+ if (capabilities & WLAN_CAPABILITY_PRIVACY)
iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
else
iwe.u.data.flags = IW_ENCODE_DISABLED;
@@ -1450,7 +1420,7 @@ static inline char *ks_wlan_translate_scan(struct net_device *dev,
pbuf += sprintf(pbuf, "%02x", ap->rsn_ie.body[i]);
iwe.u.data.length += (ap->rsn_ie.size) * 2;
- DPRINTK(4, "ap->rsn.size=%d\n", ap->rsn_ie.size);
+ netdev_dbg(dev, "ap->rsn.size=%d\n", ap->rsn_ie.size);
current_ev =
iwe_stream_add_point(info, current_ev, end_buf, &iwe,
@@ -1472,8 +1442,8 @@ static inline char *ks_wlan_translate_scan(struct net_device *dev,
pbuf += sprintf(pbuf, "%02x", ap->wpa_ie.body[i]);
iwe.u.data.length += (ap->wpa_ie.size) * 2;
- DPRINTK(4, "ap->rsn.size=%d\n", ap->wpa_ie.size);
- DPRINTK(4, "iwe.u.data.length=%d\n", iwe.u.data.length);
+ netdev_dbg(dev, "ap->rsn.size=%d\n", ap->wpa_ie.size);
+ netdev_dbg(dev, "iwe.u.data.length=%d\n", iwe.u.data.length);
current_ev =
iwe_stream_add_point(info, current_ev, end_buf, &iwe,
@@ -1494,21 +1464,16 @@ static int ks_wlan_get_scan(struct net_device *dev,
int i;
char *current_ev = extra;
- DPRINTK(2, "\n");
-
if (priv->sleep_mode == SLP_SLEEP)
return -EPERM;
/* for SLEEP MODE */
- if (priv->sme_i.sme_flag & SME_AP_SCAN) {
- DPRINTK(2, "flag AP_SCAN\n");
+ if (priv->sme_i.sme_flag & SME_AP_SCAN)
return -EAGAIN;
- }
if (priv->aplist.size == 0) {
/* Client error, no scan results...
* The caller need to restart the scan.
*/
- DPRINTK(2, "aplist 0\n");
return -ENODATA;
}
@@ -1552,8 +1517,6 @@ static int ks_wlan_set_genie(struct net_device *dev,
{
struct ks_wlan_private *priv = netdev_priv(dev);
- DPRINTK(2, "\n");
-
if (priv->sleep_mode == SLP_SLEEP)
return -EPERM;
/* for SLEEP MODE */
@@ -1569,8 +1532,6 @@ static int ks_wlan_set_auth_mode(struct net_device *dev,
int index = (vwrq->flags & IW_AUTH_INDEX);
int value = vwrq->value;
- DPRINTK(2, "index=%d:value=%08X\n", index, value);
-
if (priv->sleep_mode == SLP_SLEEP)
return -EPERM;
/* for SLEEP MODE */
@@ -1702,8 +1663,6 @@ static int ks_wlan_get_auth_mode(struct net_device *dev,
struct ks_wlan_private *priv = netdev_priv(dev);
int index = (vwrq->flags & IW_AUTH_INDEX);
- DPRINTK(2, "index=%d\n", index);
-
if (priv->sleep_mode == SLP_SLEEP)
return -EPERM;
@@ -1753,9 +1712,6 @@ static int ks_wlan_set_encode_ext(struct net_device *dev,
if (!enc)
return -EINVAL;
- DPRINTK(2, "flags=%04X:: ext_flags=%08X\n", dwrq->flags,
- enc->ext_flags);
-
if (priv->sleep_mode == SLP_SLEEP)
return -EPERM;
@@ -1865,8 +1821,6 @@ static int ks_wlan_set_pmksa(struct net_device *dev,
struct pmk_t *pmk;
struct list_head *ptr;
- DPRINTK(2, "\n");
-
if (priv->sleep_mode == SLP_SLEEP)
return -EPERM;
@@ -1875,7 +1829,6 @@ static int ks_wlan_set_pmksa(struct net_device *dev,
return -EINVAL;
pmksa = (struct iw_pmksa *)extra;
- DPRINTK(2, "cmd=%d\n", pmksa->cmd);
switch (pmksa->cmd) {
case IW_PMKSA_ADD:
@@ -1987,8 +1940,6 @@ static int ks_wlan_set_stop_request(struct net_device *dev,
{
struct ks_wlan_private *priv = netdev_priv(dev);
- DPRINTK(2, "\n");
-
if (priv->sleep_mode == SLP_SLEEP)
return -EPERM;
@@ -2009,8 +1960,6 @@ static int ks_wlan_set_mlme(struct net_device *dev,
struct iw_mlme *mlme = (struct iw_mlme *)extra;
__u32 mode;
- DPRINTK(2, ":%d :%d\n", mlme->cmd, mlme->reason_code);
-
if (priv->sleep_mode == SLP_SLEEP)
return -EPERM;
@@ -2269,8 +2218,6 @@ static int ks_wlan_set_sleep_mode(struct net_device *dev,
{
struct ks_wlan_private *priv = netdev_priv(dev);
- DPRINTK(2, "\n");
-
if (*uwrq == SLP_SLEEP) {
priv->sleep_mode = *uwrq;
netdev_info(dev, "SET_SLEEP_MODE %d\n", priv->sleep_mode);
@@ -2296,7 +2243,6 @@ static int ks_wlan_get_sleep_mode(struct net_device *dev,
{
struct ks_wlan_private *priv = netdev_priv(dev);
- DPRINTK(2, "GET_SLEEP_MODE %d\n", priv->sleep_mode);
*uwrq = priv->sleep_mode;
return 0;
@@ -2310,8 +2256,6 @@ static int ks_wlan_set_wps_enable(struct net_device *dev,
{
struct ks_wlan_private *priv = netdev_priv(dev);
- DPRINTK(2, "\n");
-
if (priv->sleep_mode == SLP_SLEEP)
return -EPERM;
/* for SLEEP MODE */
@@ -2331,8 +2275,6 @@ static int ks_wlan_get_wps_enable(struct net_device *dev,
{
struct ks_wlan_private *priv = netdev_priv(dev);
- DPRINTK(2, "\n");
-
if (priv->sleep_mode == SLP_SLEEP)
return -EPERM;
/* for SLEEP MODE */
@@ -2350,12 +2292,8 @@ static int ks_wlan_set_wps_probe_req(struct net_device *dev,
unsigned char len;
struct ks_wlan_private *priv = netdev_priv(dev);
- DPRINTK(2, "\n");
-
if (priv->sleep_mode == SLP_SLEEP)
return -EPERM;
- /* for SLEEP MODE */
- DPRINTK(2, "dwrq->length=%d\n", dwrq->length);
/* length check */
if (p[1] + 2 != dwrq->length || dwrq->length > 256)
@@ -2367,10 +2305,10 @@ static int ks_wlan_set_wps_probe_req(struct net_device *dev,
memcpy(priv->wps.ie, &len, sizeof(len));
p = memcpy(priv->wps.ie + 1, p, len);
- DPRINTK(2, "%d(%#x): %02X %02X %02X %02X ... %02X %02X %02X\n",
- priv->wps.ielen, priv->wps.ielen, p[0], p[1], p[2], p[3],
- p[priv->wps.ielen - 3], p[priv->wps.ielen - 2],
- p[priv->wps.ielen - 1]);
+ netdev_dbg(dev, "%d(%#x): %02X %02X %02X %02X ... %02X %02X %02X\n",
+ priv->wps.ielen, priv->wps.ielen, p[0], p[1], p[2], p[3],
+ p[priv->wps.ielen - 3], p[priv->wps.ielen - 2],
+ p[priv->wps.ielen - 1]);
hostif_sme_enqueue(priv, SME_WPS_PROBE_REQUEST);
@@ -2789,7 +2727,6 @@ static int ks_wlan_netdev_ioctl(struct net_device *dev, struct ifreq *rq,
ret = -EOPNOTSUPP;
}
- DPRINTK(5, "return=%d\n", ret);
return ret;
}
@@ -2815,7 +2752,7 @@ int ks_wlan_set_mac_address(struct net_device *dev, void *addr)
memcpy(dev->dev_addr, mac_addr->sa_data, dev->addr_len);
memcpy(priv->eth_addr, mac_addr->sa_data, ETH_ALEN);
- priv->mac_address_valid = 0;
+ priv->mac_address_valid = false;
hostif_sme_enqueue(priv, SME_MACADDRESS_SET_REQUEST);
netdev_info(dev, "ks_wlan: MAC ADDRESS = %pM\n", priv->eth_addr);
return 0;
@@ -2826,8 +2763,8 @@ void ks_wlan_tx_timeout(struct net_device *dev)
{
struct ks_wlan_private *priv = netdev_priv(dev);
- DPRINTK(1, "head(%d) tail(%d)!!\n", priv->tx_dev.qhead,
- priv->tx_dev.qtail);
+ netdev_dbg(dev, "head(%d) tail(%d)!!\n", priv->tx_dev.qhead,
+ priv->tx_dev.qtail);
if (!netif_queue_stopped(dev))
netif_stop_queue(dev);
priv->nstats.tx_errors++;
@@ -2840,7 +2777,7 @@ int ks_wlan_start_xmit(struct sk_buff *skb, struct net_device *dev)
struct ks_wlan_private *priv = netdev_priv(dev);
int ret;
- DPRINTK(3, "in_interrupt()=%ld\n", in_interrupt());
+ netdev_dbg(dev, "in_interrupt()=%ld\n", in_interrupt());
if (!skb) {
netdev_err(dev, "ks_wlan: skb == NULL!!!\n");
@@ -2858,15 +2795,13 @@ int ks_wlan_start_xmit(struct sk_buff *skb, struct net_device *dev)
netif_trans_update(dev);
if (ret)
- DPRINTK(4, "hostif_data_request error: =%d\n", ret);
+ netdev_err(dev, "hostif_data_request error: =%d\n", ret);
return 0;
}
void send_packet_complete(struct ks_wlan_private *priv, struct sk_buff *skb)
{
- DPRINTK(3, "\n");
-
priv->nstats.tx_packets++;
if (netif_queue_stopped(priv->net_dev))
@@ -2887,7 +2822,6 @@ void ks_wlan_set_multicast_list(struct net_device *dev)
{
struct ks_wlan_private *priv = netdev_priv(dev);
- DPRINTK(4, "\n");
if (priv->dev_state < DEVICE_STATE_READY)
return; /* not finished initialize */
hostif_sme_enqueue(priv, SME_MULTICAST_REQUEST);
@@ -2914,9 +2848,6 @@ int ks_wlan_close(struct net_device *dev)
{
netif_stop_queue(dev);
- DPRINTK(4, "%s: Shutting down ethercard, status was 0x%4.4x.\n",
- dev->name, 0x00);
-
return 0;
}
@@ -2944,7 +2875,7 @@ int ks_wlan_net_start(struct net_device *dev)
/* int rc; */
priv = netdev_priv(dev);
- priv->mac_address_valid = 0;
+ priv->mac_address_valid = false;
priv->need_commit = 0;
priv->device_open_status = 1;
@@ -2966,7 +2897,7 @@ int ks_wlan_net_start(struct net_device *dev)
/* The ks_wlan-specific entries in the device structure. */
dev->netdev_ops = &ks_wlan_netdev_ops;
- dev->wireless_handlers = (struct iw_handler_def *)&ks_wlan_handler_def;
+ dev->wireless_handlers = &ks_wlan_handler_def;
dev->watchdog_timeo = TX_TIMEOUT;
netif_carrier_off(dev);
diff --git a/drivers/staging/ks7010/michael_mic.c b/drivers/staging/ks7010/michael_mic.c
index 80497ef5b1aa..292eae29c552 100644
--- a/drivers/staging/ks7010/michael_mic.c
+++ b/drivers/staging/ks7010/michael_mic.c
@@ -11,123 +11,107 @@
#include <linux/types.h>
#include <linux/string.h>
+#include <linux/bitops.h>
+#include <asm/unaligned.h>
#include "michael_mic.h"
-// Rotation functions on 32 bit values
-#define ROL32(A, n) (((A) << (n)) | (((A) >> (32 - (n))) & ((1UL << (n)) - 1)))
-#define ROR32(A, n) ROL32((A), 32 - (n))
-// Convert from Byte[] to UInt32 in a portable way
-#define getUInt32(A, B) ((uint32_t)(A[B + 0] << 0) \
- + (A[B + 1] << 8) + (A[B + 2] << 16) + (A[B + 3] << 24))
-
-// Convert from UInt32 to Byte[] in a portable way
-#define putUInt32(A, B, C) \
-do { \
- A[B + 0] = (uint8_t)(C & 0xff); \
- A[B + 1] = (uint8_t)((C >> 8) & 0xff); \
- A[B + 2] = (uint8_t)((C >> 16) & 0xff); \
- A[B + 3] = (uint8_t)((C >> 24) & 0xff); \
-} while (0)
// Reset the state to the empty message.
-#define MichaelClear(A) \
-do { \
- A->L = A->K0; \
- A->R = A->K1; \
- A->nBytesInM = 0; \
-} while (0)
-
-static
-void MichaelInitializeFunction(struct michael_mic_t *Mic, uint8_t *key)
+static inline void michael_clear(struct michael_mic_t *mic)
+{
+ mic->l = mic->k0;
+ mic->r = mic->k1;
+ mic->m_bytes = 0;
+}
+
+static void michael_init(struct michael_mic_t *mic, u8 *key)
{
// Set the key
- Mic->K0 = getUInt32(key, 0);
- Mic->K1 = getUInt32(key, 4);
+ mic->k0 = get_unaligned_le32(key);
+ mic->k1 = get_unaligned_le32(key + 4);
//clear();
- MichaelClear(Mic);
+ michael_clear(mic);
+}
+
+static inline void michael_block(struct michael_mic_t *mic)
+{
+ mic->r ^= rol32(mic->l, 17);
+ mic->l += mic->r;
+ mic->r ^= ((mic->l & 0xff00ff00) >> 8) |
+ ((mic->l & 0x00ff00ff) << 8);
+ mic->l += mic->r;
+ mic->r ^= rol32(mic->l, 3); \
+ mic->l += mic->r;
+ mic->r ^= ror32(mic->l, 2); \
+ mic->l += mic->r;
}
-#define MichaelBlockFunction(L, R) \
-do { \
- R ^= ROL32(L, 17); \
- L += R; \
- R ^= ((L & 0xff00ff00) >> 8) | ((L & 0x00ff00ff) << 8); \
- L += R; \
- R ^= ROL32(L, 3); \
- L += R; \
- R ^= ROR32(L, 2); \
- L += R; \
-} while (0)
-
-static
-void MichaelAppend(struct michael_mic_t *Mic, uint8_t *src, int nBytes)
+static void michael_append(struct michael_mic_t *mic, u8 *src, int bytes)
{
int addlen;
- if (Mic->nBytesInM) {
- addlen = 4 - Mic->nBytesInM;
- if (addlen > nBytes)
- addlen = nBytes;
- memcpy(&Mic->M[Mic->nBytesInM], src, addlen);
- Mic->nBytesInM += addlen;
+ if (mic->m_bytes) {
+ addlen = 4 - mic->m_bytes;
+ if (addlen > bytes)
+ addlen = bytes;
+ memcpy(&mic->m[mic->m_bytes], src, addlen);
+ mic->m_bytes += addlen;
src += addlen;
- nBytes -= addlen;
+ bytes -= addlen;
- if (Mic->nBytesInM < 4)
+ if (mic->m_bytes < 4)
return;
- Mic->L ^= getUInt32(Mic->M, 0);
- MichaelBlockFunction(Mic->L, Mic->R);
- Mic->nBytesInM = 0;
+ mic->l ^= get_unaligned_le32(mic->m);
+ michael_block(mic);
+ mic->m_bytes = 0;
}
- while (nBytes >= 4) {
- Mic->L ^= getUInt32(src, 0);
- MichaelBlockFunction(Mic->L, Mic->R);
+ while (bytes >= 4) {
+ mic->l ^= get_unaligned_le32(src);
+ michael_block(mic);
src += 4;
- nBytes -= 4;
+ bytes -= 4;
}
- if (nBytes > 0) {
- Mic->nBytesInM = nBytes;
- memcpy(Mic->M, src, nBytes);
+ if (bytes > 0) {
+ mic->m_bytes = bytes;
+ memcpy(mic->m, src, bytes);
}
}
-static
-void MichaelGetMIC(struct michael_mic_t *Mic, uint8_t *dst)
+static void michael_get_mic(struct michael_mic_t *mic, u8 *dst)
{
- u8 *data = Mic->M;
+ u8 *data = mic->m;
- switch (Mic->nBytesInM) {
+ switch (mic->m_bytes) {
case 0:
- Mic->L ^= 0x5a;
+ mic->l ^= 0x5a;
break;
case 1:
- Mic->L ^= data[0] | 0x5a00;
+ mic->l ^= data[0] | 0x5a00;
break;
case 2:
- Mic->L ^= data[0] | (data[1] << 8) | 0x5a0000;
+ mic->l ^= data[0] | (data[1] << 8) | 0x5a0000;
break;
case 3:
- Mic->L ^= data[0] | (data[1] << 8) | (data[2] << 16) |
+ mic->l ^= data[0] | (data[1] << 8) | (data[2] << 16) |
0x5a000000;
break;
}
- MichaelBlockFunction(Mic->L, Mic->R);
- MichaelBlockFunction(Mic->L, Mic->R);
+ michael_block(mic);
+ michael_block(mic);
// The appendByte function has already computed the result.
- putUInt32(dst, 0, Mic->L);
- putUInt32(dst, 4, Mic->R);
+ put_unaligned_le32(mic->l, dst);
+ put_unaligned_le32(mic->r, dst + 4);
// Reset to the empty message.
- MichaelClear(Mic);
+ michael_clear(mic);
}
-void MichaelMICFunction(struct michael_mic_t *Mic, u8 *Key,
- u8 *Data, int Len, u8 priority,
- u8 *Result)
+void michael_mic_function(struct michael_mic_t *mic, u8 *key,
+ u8 *data, int len, u8 priority, u8 *result)
{
u8 pad_data[4] = { priority, 0, 0, 0 };
// Compute the MIC value
@@ -140,9 +124,9 @@ void MichaelMICFunction(struct michael_mic_t *Mic, u8 *Key,
* |DA|SA|Priority|0 |Data|M0|M1|M2|M3|M4|M5|M6|M7|
* +--+--+--------+--+----+--+--+--+--+--+--+--+--+
*/
- MichaelInitializeFunction(Mic, Key);
- MichaelAppend(Mic, (uint8_t *)Data, 12); /* |DA|SA| */
- MichaelAppend(Mic, pad_data, 4); /* |Priority|0|0|0| */
- MichaelAppend(Mic, (uint8_t *)(Data + 12), Len - 12); /* |Data| */
- MichaelGetMIC(Mic, Result);
+ michael_init(mic, key);
+ michael_append(mic, data, 12); /* |DA|SA| */
+ michael_append(mic, pad_data, 4); /* |Priority|0|0|0| */
+ michael_append(mic, data + 12, len - 12); /* |Data| */
+ michael_get_mic(mic, result);
}
diff --git a/drivers/staging/ks7010/michael_mic.h b/drivers/staging/ks7010/michael_mic.h
index 758e429446f1..894a8d4121a4 100644
--- a/drivers/staging/ks7010/michael_mic.h
+++ b/drivers/staging/ks7010/michael_mic.h
@@ -11,15 +11,14 @@
/* MichaelMIC routine define */
struct michael_mic_t {
- u32 K0; // Key
- u32 K1; // Key
- u32 L; // Current state
- u32 R; // Current state
- u8 M[4]; // Message accumulator (single word)
- int nBytesInM; // # bytes in M
- u8 Result[8];
+ u32 k0; // Key
+ u32 k1; // Key
+ u32 l; // Current state
+ u32 r; // Current state
+ u8 m[4]; // Message accumulator (single word)
+ int m_bytes; // # bytes in M
+ u8 result[8];
};
-void MichaelMICFunction(struct michael_mic_t *Mic, u8 *Key,
- u8 *Data, int Len, u8 priority,
- u8 *Result);
+void michael_mic_function(struct michael_mic_t *mic, u8 *key,
+ u8 *data, int len, u8 priority, u8 *result);
diff --git a/drivers/staging/lustre/TODO b/drivers/staging/lustre/TODO
index f194417d0af7..94446487748a 100644
--- a/drivers/staging/lustre/TODO
+++ b/drivers/staging/lustre/TODO
@@ -1,12 +1,302 @@
-* Possible remaining coding style fix.
-* Remove deadcode.
-* Separate client/server functionality. Functions only used by server can be
- removed from client.
-* Clean up libcfs layer. Ideally we can remove include/linux/libcfs entirely.
-* Clean up CLIO layer. Lustre client readahead/writeback control needs to better
- suit kernel providings.
-* Add documents in Documentation.
-* Other minor misc cleanups...
+Currently all the work directed toward the lustre upstream client is tracked
+at the following link:
+
+https://jira.hpdd.intel.com/browse/LU-9679
+
+Under this ticket you will see the following work items that need to be
+addressed:
+
+******************************************************************************
+* libcfs cleanup
+*
+* https://jira.hpdd.intel.com/browse/LU-9859
+*
+* Track all the cleanups and simplification of the libcfs module. Remove
+* functions the kernel provides. Possible intergrate some of the functionality
+* into the kernel proper.
+*
+******************************************************************************
+
+https://jira.hpdd.intel.com/browse/LU-100086
+
+LNET_MINOR conflicts with USERIO_MINOR
+
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-8130
+
+Fix and simplify libcfs hash handling
+
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-8703
+
+The current way we handle SMP is wrong. Platforms like ARM and KNL can have
+core and NUMA setups with things like NUMA nodes with no cores. We need to
+handle such cases. This work also greatly simplified the lustre SMP code.
+
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-9019
+
+Replace libcfs time API with standard kernel APIs. Also migrate away from
+jiffies. We found jiffies can vary on nodes which can lead to corner cases
+that can break the file system due to nodes having inconsistent behavior.
+So move to time64_t and ktime_t as much as possible.
+
+******************************************************************************
+* Proper IB support for ko2iblnd
+******************************************************************************
+https://jira.hpdd.intel.com/browse/LU-9179
+
+Poor performance for the ko2iblnd driver. This is related to many of the
+patches below that are missing from the linux client.
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-9886
+
+Crash in upstream kiblnd_handle_early_rxs()
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-10394 / LU-10526 / LU-10089
+
+Default to default to using MEM_REG
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-10459
+
+throttle tx based on queue depth
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-9943
+
+correct WR fast reg accounting
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-10291
+
+remove concurrent_sends tunable
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-10213
+
+calculate qp max_send_wrs properly
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-9810
+
+use less CQ entries for each connection
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-10129 / LU-9180
+
+rework map_on_demand behavior
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-10129
+
+query device capabilities
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-10015
+
+fix race at kiblnd_connect_peer
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-9983
+
+allow for discontiguous fragments
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-9500
+
+Don't Page Align remote_addr with FastReg
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-9448
+
+handle empty CPTs
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-9507
+
+Don't Assert On Reconnect with MultiQP
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-9472
+
+Fix FastReg map/unmap for MLX5
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-9425
+
+Turn on 2 sges by default
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-8943
+
+Enable Multiple OPA Endpoints between Nodes
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-5718
+
+multiple sges for work request
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-9094
+
+kill timedout txs from ibp_tx_queue
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-9094
+
+reconnect peer for REJ_INVALID_SERVICE_ID
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-8752
+
+Stop MLX5 triggering a dump_cqe
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-8874
+
+Move ko2iblnd to latest RDMA changes
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-8875 / LU-8874
+
+Change to new RDMA done callback mechanism
+
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-9164 / LU-8874
+
+Incorporate RDMA map/unamp API's into ko2iblnd
+
+******************************************************************************
+* sysfs/debugfs fixes
+*
+* https://jira.hpdd.intel.com/browse/LU-8066
+*
+* The original migration to sysfs was done in haste without properly working
+* utilities to test the changes. This covers the work to restore the proper
+* behavior. Huge project to make this right.
+*
+******************************************************************************
+
+https://jira.hpdd.intel.com/browse/LU-9431
+
+The function class_process_proc_param was used for our mass updates of proc
+tunables. It didn't work with sysfs and it was just ugly so it was removed.
+In the process the ability to mass update thousands of clients was lost. This
+work restores this in a sane way.
+
+------------------------------------------------------------------------------
+https://jira.hpdd.intel.com/browse/LU-9091
+
+One the major request of users is the ability to pass in parameters into a
+sysfs file in various different units. For example we can set max_pages_per_rpc
+but this can vary on platforms due to different platform sizes. So you can
+set this like max_pages_per_rpc=16MiB. The original code to handle this written
+before the string helpers were created so the code doesn't follow that format
+but it would be easy to move to. Currently the string helpers does the reverse
+of what we need, changing bytes to string. We need to change a string to bytes.
+
+******************************************************************************
+* Proper user land to kernel space interface for Lustre
+*
+* https://jira.hpdd.intel.com/browse/LU-9680
+*
+******************************************************************************
+
+https://jira.hpdd.intel.com/browse/LU-8915
+
+Don't use linux list structure as user land arguments for lnet selftest.
+This code is pretty poor quality and really needs to be reworked.
+
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-8834
+
+The lustre ioctl LL_IOC_FUTIMES_3 is very generic. Need to either work with
+other file systems with similar functionality and make a common syscall
+interface or rework our server code to automagically do it for us.
+
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-6202
+
+Cleanup up ioctl handling. We have many obsolete ioctls. Also the way we do
+ioctls can be changed over to netlink. This also has the benefit of working
+better with HPC systems that do IO forwarding. Such systems don't like ioctls
+very well.
+
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-9667
+
+More cleanups by making our utilities use sysfs instead of ioctls for LNet.
+Also it has been requested to move the remaining ioctls to the netlink API.
+
+******************************************************************************
+* Misc
+******************************************************************************
+
+------------------------------------------------------------------------------
+https://jira.hpdd.intel.com/browse/LU-9855
+
+Clean up obdclass preprocessor code. One of the major eye sores is the various
+pointer redirections and macros used by the obdclass. This makes the code very
+difficult to understand. It was requested by the Al Viro to clean this up before
+we leave staging.
+
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-9633
+
+Migrate to sphinx kernel-doc style comments. Add documents in Documentation.
+
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-6142
+
+Possible remaining coding style fix. Remove deadcode. Enforce kernel code
+style. Other minor misc cleanups...
+
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-8837
+
+Separate client/server functionality. Functions only used by server can be
+removed from client. Most of this has been done but we need a inspect of the
+code to make sure.
+
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-8964
+
+Lustre client readahead/writeback control needs to better suit kernel providings.
+Currently its being explored. We could end up replacing the CLIO read ahead
+abstract with the kernel proper version.
+
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-9862
+
+Patch that landed for LU-7890 leads to static checker errors
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-9868
+
+dcache/namei fixes for lustre
+------------------------------------------------------------------------------
+
+https://jira.hpdd.intel.com/browse/LU-10467
+
+use standard linux wait_events macros work by Neil Brown
+
+------------------------------------------------------------------------------
Please send any patches to Greg Kroah-Hartman <greg@kroah.com>, Andreas Dilger
-<andreas.dilger@intel.com>, and Oleg Drokin <oleg.drokin@intel.com>.
+<andreas.dilger@intel.com>, James Simmons <jsimmons@infradead.org> and
+Oleg Drokin <oleg.drokin@intel.com>.
diff --git a/drivers/staging/lustre/include/linux/libcfs/curproc.h b/drivers/staging/lustre/include/linux/libcfs/curproc.h
index 3cb3f086148e..4702956805a6 100644
--- a/drivers/staging/lustre/include/linux/libcfs/curproc.h
+++ b/drivers/staging/lustre/include/linux/libcfs/curproc.h
@@ -56,30 +56,21 @@
typedef u32 cfs_cap_t;
-#define CFS_CAP_CHOWN 0
-#define CFS_CAP_DAC_OVERRIDE 1
-#define CFS_CAP_DAC_READ_SEARCH 2
-#define CFS_CAP_FOWNER 3
-#define CFS_CAP_FSETID 4
-#define CFS_CAP_LINUX_IMMUTABLE 9
-#define CFS_CAP_SYS_ADMIN 21
-#define CFS_CAP_SYS_BOOT 23
-#define CFS_CAP_SYS_RESOURCE 24
+#define CFS_CAP_FS_MASK (BIT(CAP_CHOWN) | \
+ BIT(CAP_DAC_OVERRIDE) | \
+ BIT(CAP_DAC_READ_SEARCH) | \
+ BIT(CAP_FOWNER) | \
+ BIT(CAP_FSETID) | \
+ BIT(CAP_LINUX_IMMUTABLE) | \
+ BIT(CAP_SYS_ADMIN) | \
+ BIT(CAP_SYS_BOOT) | \
+ BIT(CAP_SYS_RESOURCE))
-#define CFS_CAP_FS_MASK (BIT(CFS_CAP_CHOWN) | \
- BIT(CFS_CAP_DAC_OVERRIDE) | \
- BIT(CFS_CAP_DAC_READ_SEARCH) | \
- BIT(CFS_CAP_FOWNER) | \
- BIT(CFS_CAP_FSETID) | \
- BIT(CFS_CAP_LINUX_IMMUTABLE) | \
- BIT(CFS_CAP_SYS_ADMIN) | \
- BIT(CFS_CAP_SYS_BOOT) | \
- BIT(CFS_CAP_SYS_RESOURCE))
-
-void cfs_cap_raise(cfs_cap_t cap);
-void cfs_cap_lower(cfs_cap_t cap);
-int cfs_cap_raised(cfs_cap_t cap);
-cfs_cap_t cfs_curproc_cap_pack(void);
+static inline cfs_cap_t cfs_curproc_cap_pack(void)
+{
+ /* cfs_cap_t is only the first word of kernel_cap_t */
+ return (cfs_cap_t)(current_cap().cap[0]);
+}
/* __LIBCFS_CURPROC_H__ */
#endif
diff --git a/drivers/staging/lustre/include/linux/libcfs/libcfs.h b/drivers/staging/lustre/include/linux/libcfs/libcfs.h
index ca3472cc952f..392793582956 100644
--- a/drivers/staging/lustre/include/linux/libcfs/libcfs.h
+++ b/drivers/staging/lustre/include/linux/libcfs/libcfs.h
@@ -64,14 +64,21 @@
#define LNET_ACCEPTOR_MIN_RESERVED_PORT 512
#define LNET_ACCEPTOR_MAX_RESERVED_PORT 1023
-/*
- * Defined by platform
- */
-sigset_t cfs_block_allsigs(void);
-sigset_t cfs_block_sigs(unsigned long sigs);
-sigset_t cfs_block_sigsinv(unsigned long sigs);
-void cfs_restore_sigs(sigset_t sigset);
-void cfs_clear_sigpending(void);
+/* Block all signals except for the @sigs */
+static inline void cfs_block_sigsinv(unsigned long sigs, sigset_t *old)
+{
+ sigset_t new;
+
+ siginitsetinv(&new, sigs);
+ sigorsets(&new, &current->blocked, &new);
+ sigprocmask(SIG_BLOCK, &new, old);
+}
+
+static inline void
+cfs_restore_sigs(sigset_t *old)
+{
+ sigprocmask(SIG_SETMASK, old, NULL);
+}
struct libcfs_ioctl_handler {
struct list_head item;
@@ -105,10 +112,6 @@ static inline void *__container_of(void *ptr, unsigned long shift)
#define _LIBCFS_H
-void *libcfs_kvzalloc(size_t size, gfp_t flags);
-void *libcfs_kvzalloc_cpt(struct cfs_cpt_table *cptab, int cpt, size_t size,
- gfp_t flags);
-
extern struct miscdevice libcfs_dev;
/**
* The path of debug log dump upcall script.
diff --git a/drivers/staging/lustre/include/linux/libcfs/libcfs_crypto.h b/drivers/staging/lustre/include/linux/libcfs/libcfs_crypto.h
index e5c156e9d907..3a72117140ed 100644
--- a/drivers/staging/lustre/include/linux/libcfs/libcfs_crypto.h
+++ b/drivers/staging/lustre/include/linux/libcfs/libcfs_crypto.h
@@ -189,18 +189,15 @@ int cfs_crypto_hash_digest(enum cfs_crypto_hash_alg hash_alg,
unsigned char *key, unsigned int key_len,
unsigned char *hash, unsigned int *hash_len);
-/* cfs crypto hash descriptor */
-struct cfs_crypto_hash_desc;
-
-struct cfs_crypto_hash_desc *
+struct ahash_request *
cfs_crypto_hash_init(enum cfs_crypto_hash_alg hash_alg,
unsigned char *key, unsigned int key_len);
-int cfs_crypto_hash_update_page(struct cfs_crypto_hash_desc *desc,
+int cfs_crypto_hash_update_page(struct ahash_request *desc,
struct page *page, unsigned int offset,
unsigned int len);
-int cfs_crypto_hash_update(struct cfs_crypto_hash_desc *desc, const void *buf,
+int cfs_crypto_hash_update(struct ahash_request *desc, const void *buf,
unsigned int buf_len);
-int cfs_crypto_hash_final(struct cfs_crypto_hash_desc *desc,
+int cfs_crypto_hash_final(struct ahash_request *desc,
unsigned char *hash, unsigned int *hash_len);
int cfs_crypto_register(void);
void cfs_crypto_unregister(void);
diff --git a/drivers/staging/lustre/include/linux/libcfs/libcfs_debug.h b/drivers/staging/lustre/include/linux/libcfs/libcfs_debug.h
index 1b98f0953afb..9290a19429e7 100644
--- a/drivers/staging/lustre/include/linux/libcfs/libcfs_debug.h
+++ b/drivers/staging/lustre/include/linux/libcfs/libcfs_debug.h
@@ -66,8 +66,8 @@ extern unsigned int libcfs_panic_on_lbug;
# define DEBUG_SUBSYSTEM S_UNDEFINED
#endif
-#define CDEBUG_DEFAULT_MAX_DELAY (cfs_time_seconds(600)) /* jiffies */
-#define CDEBUG_DEFAULT_MIN_DELAY ((cfs_time_seconds(1) + 1) / 2) /* jiffies */
+#define CDEBUG_DEFAULT_MAX_DELAY (600 * HZ) /* jiffies */
+#define CDEBUG_DEFAULT_MIN_DELAY ((HZ + 1) / 2) /* jiffies */
#define CDEBUG_DEFAULT_BACKOFF 2
struct cfs_debug_limit_state {
unsigned long cdls_next;
diff --git a/drivers/staging/lustre/include/linux/libcfs/libcfs_time.h b/drivers/staging/lustre/include/linux/libcfs/libcfs_time.h
index 9699646decb9..c4f25be78268 100644
--- a/drivers/staging/lustre/include/linux/libcfs/libcfs_time.h
+++ b/drivers/staging/lustre/include/linux/libcfs/libcfs_time.h
@@ -62,7 +62,7 @@ static inline int cfs_time_aftereq(unsigned long t1, unsigned long t2)
static inline unsigned long cfs_time_shift(int seconds)
{
- return cfs_time_add(cfs_time_current(), cfs_time_seconds(seconds));
+ return cfs_time_add(cfs_time_current(), seconds * HZ);
}
/*
diff --git a/drivers/staging/lustre/include/linux/libcfs/linux/linux-time.h b/drivers/staging/lustre/include/linux/libcfs/linux/linux-time.h
index aece13698eb4..805cb326af86 100644
--- a/drivers/staging/lustre/include/linux/libcfs/linux/linux-time.h
+++ b/drivers/staging/lustre/include/linux/libcfs/linux/linux-time.h
@@ -65,11 +65,6 @@ static inline unsigned long cfs_time_current(void)
return jiffies;
}
-static inline long cfs_time_seconds(int seconds)
-{
- return ((long)seconds) * msecs_to_jiffies(MSEC_PER_SEC);
-}
-
static inline long cfs_duration_sec(long d)
{
return d / msecs_to_jiffies(MSEC_PER_SEC);
@@ -85,7 +80,7 @@ static inline u64 cfs_time_add_64(u64 t, u64 d)
static inline u64 cfs_time_shift_64(int seconds)
{
return cfs_time_add_64(cfs_time_current_64(),
- cfs_time_seconds(seconds));
+ seconds * HZ);
}
static inline int cfs_time_before_64(u64 t1, u64 t2)
diff --git a/drivers/staging/lustre/include/linux/lnet/api.h b/drivers/staging/lustre/include/linux/lnet/api.h
index 31fcd33171b4..dae2e4f0056c 100644
--- a/drivers/staging/lustre/include/linux/lnet/api.h
+++ b/drivers/staging/lustre/include/linux/lnet/api.h
@@ -169,6 +169,7 @@ int LNetEQFree(struct lnet_handle_eq eventq_in);
int LNetEQPoll(struct lnet_handle_eq *eventqs_in,
int neq_in,
int timeout_ms,
+ int interruptible,
struct lnet_event *event_out,
int *which_eq_out);
/** @} lnet_eq */
diff --git a/drivers/staging/lustre/lnet/Kconfig b/drivers/staging/lustre/lnet/Kconfig
index 6bcb53d0c6f4..ad049e6f24e4 100644
--- a/drivers/staging/lustre/lnet/Kconfig
+++ b/drivers/staging/lustre/lnet/Kconfig
@@ -1,6 +1,6 @@
config LNET
tristate "Lustre networking subsystem (LNet)"
- depends on INET && m
+ depends on INET
help
The Lustre network layer, also known as LNet, is a networking abstaction
level API that was initially created to allow Lustre Filesystem to utilize
diff --git a/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.c b/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.c
index ec84edfda271..7ae2955c4db6 100644
--- a/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.c
+++ b/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.c
@@ -1211,7 +1211,7 @@ static struct kib_hca_dev *kiblnd_current_hdev(struct kib_dev *dev)
CDEBUG(D_NET, "%s: Wait for failover\n",
dev->ibd_ifname);
set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(cfs_time_seconds(1) / 100);
+ schedule_timeout(HZ / 100);
read_lock_irqsave(&kiblnd_data.kib_global_lock, flags);
}
@@ -1921,7 +1921,7 @@ struct list_head *kiblnd_pool_alloc_node(struct kib_poolset *ps)
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(interval);
- if (interval < cfs_time_seconds(1))
+ if (interval < HZ)
interval *= 2;
goto again;
@@ -2541,7 +2541,7 @@ static void kiblnd_base_shutdown(void)
"Waiting for %d threads to terminate\n",
atomic_read(&kiblnd_data.kib_nthreads));
set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(cfs_time_seconds(1));
+ schedule_timeout(HZ);
}
/* fall through */
@@ -2592,7 +2592,7 @@ static void kiblnd_shutdown(struct lnet_ni *ni)
libcfs_nid2str(ni->ni_nid),
atomic_read(&net->ibn_npeers));
set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(cfs_time_seconds(1));
+ schedule_timeout(HZ);
}
kiblnd_net_fini_pools(net);
diff --git a/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd_cb.c b/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd_cb.c
index b3e7f28eb978..6690a6cd4e34 100644
--- a/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd_cb.c
+++ b/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd_cb.c
@@ -3288,8 +3288,6 @@ kiblnd_connd(void *arg)
int peer_index = 0;
unsigned long deadline = jiffies;
- cfs_block_allsigs();
-
init_waitqueue_entry(&wait, current);
kiblnd_data.kib_connd = current;
@@ -3542,8 +3540,6 @@ kiblnd_scheduler(void *arg)
int busy_loops = 0;
int rc;
- cfs_block_allsigs();
-
init_waitqueue_entry(&wait, current);
sched = kiblnd_data.kib_scheds[KIB_THREAD_CPT(id)];
@@ -3676,8 +3672,6 @@ kiblnd_failover_thread(void *arg)
LASSERT(*kiblnd_tunables.kib_dev_failover);
- cfs_block_allsigs();
-
init_waitqueue_entry(&wait, current);
write_lock_irqsave(glock, flags);
@@ -3728,8 +3722,8 @@ kiblnd_failover_thread(void *arg)
add_wait_queue(&kiblnd_data.kib_failover_waitq, &wait);
write_unlock_irqrestore(glock, flags);
- rc = schedule_timeout(long_sleep ? cfs_time_seconds(10) :
- cfs_time_seconds(1));
+ rc = schedule_timeout(long_sleep ? 10 * HZ :
+ HZ);
remove_wait_queue(&kiblnd_data.kib_failover_waitq, &wait);
write_lock_irqsave(glock, flags);
diff --git a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd.c b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd.c
index ff292216290d..7086678e1c3e 100644
--- a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd.c
+++ b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd.c
@@ -1677,7 +1677,7 @@ ksocknal_destroy_conn(struct ksock_conn *conn)
switch (conn->ksnc_rx_state) {
case SOCKNAL_RX_LNET_PAYLOAD:
last_rcv = conn->ksnc_rx_deadline -
- cfs_time_seconds(*ksocknal_tunables.ksnd_timeout);
+ *ksocknal_tunables.ksnd_timeout * HZ;
CERROR("Completing partial receive from %s[%d], ip %pI4h:%d, with error, wanted: %zd, left: %d, last alive is %ld secs ago\n",
libcfs_id2str(conn->ksnc_peer->ksnp_id), conn->ksnc_type,
&conn->ksnc_ipaddr, conn->ksnc_port,
@@ -2356,7 +2356,7 @@ ksocknal_base_shutdown(void)
ksocknal_data.ksnd_nthreads);
read_unlock(&ksocknal_data.ksnd_global_lock);
set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(cfs_time_seconds(1));
+ schedule_timeout(HZ);
read_lock(&ksocknal_data.ksnd_global_lock);
}
read_unlock(&ksocknal_data.ksnd_global_lock);
@@ -2599,7 +2599,7 @@ ksocknal_shutdown(struct lnet_ni *ni)
"waiting for %d peers to disconnect\n",
net->ksnn_npeers);
set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(cfs_time_seconds(1));
+ schedule_timeout(HZ);
ksocknal_debug_peerhash(ni);
diff --git a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd.h b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd.h
index d50ebdf863fa..570f54ed57b1 100644
--- a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd.h
+++ b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd.h
@@ -304,15 +304,6 @@ struct ksock_tx { /* transmit packet */
/* network zero copy callback descriptor embedded in struct ksock_tx */
-/*
- * space for the rx frag descriptors; we either read a single contiguous
- * header, or up to LNET_MAX_IOV frags of payload of either type.
- */
-union ksock_rxiovspace {
- struct kvec iov[LNET_MAX_IOV];
- struct bio_vec kiov[LNET_MAX_IOV];
-};
-
#define SOCKNAL_RX_KSM_HEADER 1 /* reading ksock message header */
#define SOCKNAL_RX_LNET_HEADER 2 /* reading lnet message header */
#define SOCKNAL_RX_PARSE 3 /* Calling lnet_parse() */
@@ -359,7 +350,7 @@ struct ksock_conn {
__u8 ksnc_rx_state; /* what is being read */
int ksnc_rx_nob_left; /* # bytes to next hdr/body */
struct iov_iter ksnc_rx_to; /* copy destination */
- union ksock_rxiovspace ksnc_rx_iov_space; /* space for frag descriptors */
+ struct kvec ksnc_rx_iov_space[LNET_MAX_IOV]; /* space for frag descriptors */
__u32 ksnc_rx_csum; /* partial checksum for incoming
* data
*/
diff --git a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_cb.c b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_cb.c
index 11fd3a36424f..036fecbcede8 100644
--- a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_cb.c
+++ b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_cb.c
@@ -189,7 +189,7 @@ ksocknal_transmit(struct ksock_conn *conn, struct ksock_tx *tx)
if (ksocknal_data.ksnd_stall_tx) {
set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(cfs_time_seconds(ksocknal_data.ksnd_stall_tx));
+ schedule_timeout(ksocknal_data.ksnd_stall_tx * HZ);
}
LASSERT(tx->tx_resid);
@@ -294,7 +294,7 @@ ksocknal_receive(struct ksock_conn *conn)
if (ksocknal_data.ksnd_stall_rx) {
set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(cfs_time_seconds(ksocknal_data.ksnd_stall_rx));
+ schedule_timeout(ksocknal_data.ksnd_stall_rx * HZ);
}
rc = ksocknal_connsock_addref(conn);
@@ -986,7 +986,7 @@ int
ksocknal_new_packet(struct ksock_conn *conn, int nob_to_skip)
{
static char ksocknal_slop_buffer[4096];
- struct kvec *kvec = (struct kvec *)&conn->ksnc_rx_iov_space;
+ struct kvec *kvec = conn->ksnc_rx_iov_space;
int nob;
unsigned int niov;
@@ -1059,7 +1059,7 @@ ksocknal_new_packet(struct ksock_conn *conn, int nob_to_skip)
static int
ksocknal_process_receive(struct ksock_conn *conn)
{
- struct kvec *kvec = (struct kvec *)&conn->ksnc_rx_iov_space;
+ struct kvec *kvec = conn->ksnc_rx_iov_space;
struct lnet_hdr *lhdr;
struct lnet_process_id *id;
int rc;
@@ -1324,8 +1324,6 @@ int ksocknal_scheduler(void *arg)
info = ksocknal_data.ksnd_sched_info[KSOCK_THREAD_CPT(id)];
sched = &info->ksi_scheds[KSOCK_THREAD_SID(id)];
- cfs_block_allsigs();
-
rc = cfs_cpt_bind(lnet_cpt_table(), info->ksi_cpt);
if (rc) {
CWARN("Can't set CPU partition affinity to %d: %d\n",
@@ -1780,7 +1778,7 @@ ksocknal_connect(struct ksock_route *route)
int rc = 0;
deadline = cfs_time_add(cfs_time_current(),
- cfs_time_seconds(*ksocknal_tunables.ksnd_timeout));
+ *ksocknal_tunables.ksnd_timeout * HZ);
write_lock_bh(&ksocknal_data.ksnd_global_lock);
@@ -1878,7 +1876,7 @@ ksocknal_connect(struct ksock_route *route)
* so min_reconnectms should be good heuristic
*/
route->ksnr_retry_interval =
- cfs_time_seconds(*ksocknal_tunables.ksnd_min_reconnectms) / 1000;
+ *ksocknal_tunables.ksnd_min_reconnectms * HZ / 1000;
route->ksnr_timeout = cfs_time_add(cfs_time_current(),
route->ksnr_retry_interval);
}
@@ -1899,10 +1897,10 @@ ksocknal_connect(struct ksock_route *route)
route->ksnr_retry_interval *= 2;
route->ksnr_retry_interval =
max(route->ksnr_retry_interval,
- cfs_time_seconds(*ksocknal_tunables.ksnd_min_reconnectms) / 1000);
+ (long)*ksocknal_tunables.ksnd_min_reconnectms * HZ / 1000);
route->ksnr_retry_interval =
min(route->ksnr_retry_interval,
- cfs_time_seconds(*ksocknal_tunables.ksnd_max_reconnectms) / 1000);
+ (long)*ksocknal_tunables.ksnd_max_reconnectms * HZ / 1000);
LASSERT(route->ksnr_retry_interval);
route->ksnr_timeout = cfs_time_add(cfs_time_current(),
@@ -1972,7 +1970,7 @@ ksocknal_connd_check_start(time64_t sec, long *timeout)
if (sec - ksocknal_data.ksnd_connd_failed_stamp <= 1) {
/* may run out of resource, retry later */
- *timeout = cfs_time_seconds(1);
+ *timeout = HZ;
return 0;
}
@@ -2031,8 +2029,8 @@ ksocknal_connd_check_stop(time64_t sec, long *timeout)
val = (int)(ksocknal_data.ksnd_connd_starting_stamp +
SOCKNAL_CONND_TIMEOUT - sec);
- *timeout = (val > 0) ? cfs_time_seconds(val) :
- cfs_time_seconds(SOCKNAL_CONND_TIMEOUT);
+ *timeout = (val > 0) ? val * HZ :
+ SOCKNAL_CONND_TIMEOUT * HZ;
if (val > 0)
return 0;
@@ -2078,8 +2076,6 @@ ksocknal_connd(void *arg)
int nloops = 0;
int cons_retry = 0;
- cfs_block_allsigs();
-
init_waitqueue_entry(&wait, current);
spin_lock_bh(connd_lock);
@@ -2307,7 +2303,7 @@ ksocknal_send_keepalive_locked(struct ksock_peer *peer)
if (*ksocknal_tunables.ksnd_keepalive <= 0 ||
time_before(cfs_time_current(),
cfs_time_add(peer->ksnp_last_alive,
- cfs_time_seconds(*ksocknal_tunables.ksnd_keepalive))))
+ *ksocknal_tunables.ksnd_keepalive * HZ)))
return 0;
if (time_before(cfs_time_current(), peer->ksnp_send_keepalive))
@@ -2472,8 +2468,6 @@ ksocknal_reaper(void *arg)
int peer_index = 0;
unsigned long deadline = cfs_time_current();
- cfs_block_allsigs();
-
INIT_LIST_HEAD(&enomem_conns);
init_waitqueue_entry(&wait, current);
@@ -2563,7 +2557,7 @@ ksocknal_reaper(void *arg)
ksocknal_data.ksnd_peer_hash_size;
}
- deadline = cfs_time_add(deadline, cfs_time_seconds(p));
+ deadline = cfs_time_add(deadline, p * HZ);
}
if (nenomem_conns) {
diff --git a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib.c b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib.c
index cb28dd2baf2f..7941cfa526bc 100644
--- a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib.c
+++ b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib.c
@@ -189,7 +189,7 @@ ksocknal_lib_recv(struct ksock_conn *conn)
if (!(conn->ksnc_rx_to.type & ITER_BVEC) &&
conn->ksnc_proto != &ksocknal_protocol_v2x)
return rc;
-
+
/* accumulate checksum */
conn->ksnc_msg.ksm_csum = 0;
iov_iter_for_each_range(&conn->ksnc_rx_to, rc, lustre_csum, conn);
diff --git a/drivers/staging/lustre/lnet/libcfs/Makefile b/drivers/staging/lustre/lnet/libcfs/Makefile
index 730f2c675047..b7dc7ac11cc5 100644
--- a/drivers/staging/lustre/lnet/libcfs/Makefile
+++ b/drivers/staging/lustre/lnet/libcfs/Makefile
@@ -5,12 +5,10 @@ subdir-ccflags-y += -I$(srctree)/drivers/staging/lustre/lustre/include
obj-$(CONFIG_LNET) += libcfs.o
libcfs-linux-objs := linux-tracefile.o linux-debug.o
-libcfs-linux-objs += linux-prim.o linux-cpu.o
-libcfs-linux-objs += linux-curproc.o
+libcfs-linux-objs += linux-cpu.o
libcfs-linux-objs += linux-module.o
libcfs-linux-objs += linux-crypto.o
libcfs-linux-objs += linux-crypto-adler.o
-libcfs-linux-objs += linux-mem.o
libcfs-linux-objs := $(addprefix linux/,$(libcfs-linux-objs))
diff --git a/drivers/staging/lustre/lnet/libcfs/debug.c b/drivers/staging/lustre/lnet/libcfs/debug.c
index 551c45bf4108..1371224a8cb9 100644
--- a/drivers/staging/lustre/lnet/libcfs/debug.c
+++ b/drivers/staging/lustre/lnet/libcfs/debug.c
@@ -113,7 +113,7 @@ static int param_set_delay_minmax(const char *val,
if (rc)
return -EINVAL;
- d = cfs_time_seconds(sec) / 100;
+ d = sec * HZ / 100;
if (d < min || d > max)
return -EINVAL;
@@ -440,7 +440,7 @@ int libcfs_debug_clear_buffer(void)
return 0;
}
-/* Debug markers, although printed by S_LNET should not be be marked as such. */
+/* Debug markers, although printed by S_LNET should not be marked as such. */
#undef DEBUG_SUBSYSTEM
#define DEBUG_SUBSYSTEM S_UNDEFINED
int libcfs_debug_mark_buffer(const char *text)
diff --git a/drivers/staging/lustre/lnet/libcfs/fail.c b/drivers/staging/lustre/lnet/libcfs/fail.c
index 39439b303d65..d3f1e866c6a7 100644
--- a/drivers/staging/lustre/lnet/libcfs/fail.c
+++ b/drivers/staging/lustre/lnet/libcfs/fail.c
@@ -134,7 +134,7 @@ int __cfs_fail_timeout_set(u32 id, u32 value, int ms, int set)
CERROR("cfs_fail_timeout id %x sleeping for %dms\n",
id, ms);
set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(cfs_time_seconds(ms) / 1000);
+ schedule_timeout(ms * HZ / 1000);
CERROR("cfs_fail_timeout id %x awake\n", id);
}
return ret;
diff --git a/drivers/staging/lustre/lnet/libcfs/linux/linux-cpu.c b/drivers/staging/lustre/lnet/libcfs/linux/linux-cpu.c
index c07165e0ad95..388521e4e354 100644
--- a/drivers/staging/lustre/lnet/libcfs/linux/linux-cpu.c
+++ b/drivers/staging/lustre/lnet/libcfs/linux/linux-cpu.c
@@ -743,7 +743,7 @@ cfs_cpt_table_create(int ncpt)
goto failed;
}
- if (!zalloc_cpumask_var(&mask, GFP_NOFS)){
+ if (!zalloc_cpumask_var(&mask, GFP_NOFS)) {
CERROR("Failed to allocate scratch cpumask\n");
goto failed;
}
diff --git a/drivers/staging/lustre/lnet/libcfs/linux/linux-crypto.c b/drivers/staging/lustre/lnet/libcfs/linux/linux-crypto.c
index 80072b2a443c..b55006264155 100644
--- a/drivers/staging/lustre/lnet/libcfs/linux/linux-crypto.c
+++ b/drivers/staging/lustre/lnet/libcfs/linux/linux-crypto.c
@@ -42,7 +42,7 @@ static int cfs_crypto_hash_speeds[CFS_HASH_ALG_MAX];
/**
* Initialize the state descriptor for the specified hash algorithm.
*
- * An internal routine to allocate the hash-specific state in \a hdesc for
+ * An internal routine to allocate the hash-specific state in \a req for
* use with cfs_crypto_hash_digest() to compute the hash of a single message,
* though possibly in multiple chunks. The descriptor internal state should
* be freed with cfs_crypto_hash_final().
@@ -50,7 +50,7 @@ static int cfs_crypto_hash_speeds[CFS_HASH_ALG_MAX];
* \param[in] hash_alg hash algorithm id (CFS_HASH_ALG_*)
* \param[out] type pointer to the hash description in hash_types[]
* array
- * \param[in,out] hdesc hash state descriptor to be initialized
+ * \param[in,out] req hash state descriptor to be initialized
* \param[in] key initial hash value/state, NULL to use default
* value
* \param[in] key_len length of \a key
@@ -194,7 +194,7 @@ EXPORT_SYMBOL(cfs_crypto_hash_digest);
* \retval pointer to descriptor of hash instance
* \retval ERR_PTR(errno) in case of error
*/
-struct cfs_crypto_hash_desc *
+struct ahash_request *
cfs_crypto_hash_init(enum cfs_crypto_hash_alg hash_alg,
unsigned char *key, unsigned int key_len)
{
@@ -206,14 +206,14 @@ cfs_crypto_hash_init(enum cfs_crypto_hash_alg hash_alg,
if (err)
return ERR_PTR(err);
- return (struct cfs_crypto_hash_desc *)req;
+ return req;
}
EXPORT_SYMBOL(cfs_crypto_hash_init);
/**
* Update hash digest computed on data within the given \a page
*
- * \param[in] hdesc hash state descriptor
+ * \param[in] hreq hash state descriptor
* \param[in] page data page on which to compute the hash
* \param[in] offset offset within \a page at which to start hash
* \param[in] len length of data on which to compute hash
@@ -221,11 +221,10 @@ EXPORT_SYMBOL(cfs_crypto_hash_init);
* \retval 0 for success
* \retval negative errno on failure
*/
-int cfs_crypto_hash_update_page(struct cfs_crypto_hash_desc *hdesc,
+int cfs_crypto_hash_update_page(struct ahash_request *req,
struct page *page, unsigned int offset,
unsigned int len)
{
- struct ahash_request *req = (void *)hdesc;
struct scatterlist sl;
sg_init_table(&sl, 1);
@@ -239,17 +238,16 @@ EXPORT_SYMBOL(cfs_crypto_hash_update_page);
/**
* Update hash digest computed on the specified data
*
- * \param[in] hdesc hash state descriptor
+ * \param[in] req hash state descriptor
* \param[in] buf data buffer on which to compute the hash
* \param[in] buf_len length of \buf on which to compute hash
*
* \retval 0 for success
* \retval negative errno on failure
*/
-int cfs_crypto_hash_update(struct cfs_crypto_hash_desc *hdesc,
+int cfs_crypto_hash_update(struct ahash_request *req,
const void *buf, unsigned int buf_len)
{
- struct ahash_request *req = (void *)hdesc;
struct scatterlist sl;
sg_init_one(&sl, buf, buf_len);
@@ -262,20 +260,19 @@ EXPORT_SYMBOL(cfs_crypto_hash_update);
/**
* Finish hash calculation, copy hash digest to buffer, clean up hash descriptor
*
- * \param[in] hdesc hash descriptor
+ * \param[in] req hash descriptor
* \param[out] hash pointer to hash buffer to store hash digest
- * \param[in,out] hash_len pointer to hash buffer size, if \a hdesc = NULL
- * only free \a hdesc instead of computing the hash
+ * \param[in,out] hash_len pointer to hash buffer size, if \a req = NULL
+ * only free \a req instead of computing the hash
*
* \retval 0 for success
* \retval -EOVERFLOW if hash_len is too small for the hash digest
* \retval negative errno for other errors from lower layers
*/
-int cfs_crypto_hash_final(struct cfs_crypto_hash_desc *hdesc,
+int cfs_crypto_hash_final(struct ahash_request *req,
unsigned char *hash, unsigned int *hash_len)
{
int err;
- struct ahash_request *req = (void *)hdesc;
int size = crypto_ahash_digestsize(crypto_ahash_reqtfm(req));
if (!hash || !hash_len) {
@@ -331,7 +328,7 @@ static void cfs_crypto_performance_test(enum cfs_crypto_hash_alg hash_alg)
for (start = jiffies, end = start + msecs_to_jiffies(MSEC_PER_SEC),
bcount = 0; time_before(jiffies, end); bcount++) {
- struct cfs_crypto_hash_desc *hdesc;
+ struct ahash_request *hdesc;
int i;
hdesc = cfs_crypto_hash_init(hash_alg, NULL, 0);
diff --git a/drivers/staging/lustre/lnet/libcfs/linux/linux-curproc.c b/drivers/staging/lustre/lnet/libcfs/linux/linux-curproc.c
deleted file mode 100644
index 1d8949f1a4fa..000000000000
--- a/drivers/staging/lustre/lnet/libcfs/linux/linux-curproc.c
+++ /dev/null
@@ -1,108 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * GPL HEADER START
- *
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 only,
- * as published by the Free Software Foundation.
- *
- * 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 version 2 for more details (a copy is included
- * in the LICENSE file that accompanied this code).
- *
- * You should have received a copy of the GNU General Public License
- * version 2 along with this program; If not, see
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * GPL HEADER END
- */
-/*
- * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
- * Use is subject to license terms.
- *
- * Copyright (c) 2011, 2015, Intel Corporation.
- */
-/*
- * This file is part of Lustre, http://www.lustre.org/
- * Lustre is a trademark of Sun Microsystems, Inc.
- *
- * libcfs/libcfs/linux/linux-curproc.c
- *
- * Lustre curproc API implementation for Linux kernel
- *
- * Author: Nikita Danilov <nikita@clusterfs.com>
- */
-
-#include <linux/sched.h>
-#include <linux/fs_struct.h>
-
-#include <linux/compat.h>
-#include <linux/thread_info.h>
-
-#define DEBUG_SUBSYSTEM S_LNET
-
-#include <linux/libcfs/libcfs.h>
-
-/*
- * Implementation of cfs_curproc API (see portals/include/libcfs/curproc.h)
- * for Linux kernel.
- */
-
-void cfs_cap_raise(cfs_cap_t cap)
-{
- struct cred *cred;
-
- cred = prepare_creds();
- if (cred) {
- cap_raise(cred->cap_effective, cap);
- commit_creds(cred);
- }
-}
-EXPORT_SYMBOL(cfs_cap_raise);
-
-void cfs_cap_lower(cfs_cap_t cap)
-{
- struct cred *cred;
-
- cred = prepare_creds();
- if (cred) {
- cap_lower(cred->cap_effective, cap);
- commit_creds(cred);
- }
-}
-EXPORT_SYMBOL(cfs_cap_lower);
-
-int cfs_cap_raised(cfs_cap_t cap)
-{
- return cap_raised(current_cap(), cap);
-}
-EXPORT_SYMBOL(cfs_cap_raised);
-
-static void cfs_kernel_cap_pack(kernel_cap_t kcap, cfs_cap_t *cap)
-{
- /* XXX lost high byte */
- *cap = kcap.cap[0];
-}
-
-cfs_cap_t cfs_curproc_cap_pack(void)
-{
- cfs_cap_t cap;
-
- cfs_kernel_cap_pack(current_cap(), &cap);
- return cap;
-}
-EXPORT_SYMBOL(cfs_curproc_cap_pack);
-
-/*
- * Local variables:
- * c-indentation-style: "K&R"
- * c-basic-offset: 8
- * tab-width: 8
- * fill-column: 80
- * scroll-step: 1
- * End:
- */
diff --git a/drivers/staging/lustre/lnet/libcfs/linux/linux-mem.c b/drivers/staging/lustre/lnet/libcfs/linux/linux-mem.c
deleted file mode 100644
index 963df0ef4afb..000000000000
--- a/drivers/staging/lustre/lnet/libcfs/linux/linux-mem.c
+++ /dev/null
@@ -1,51 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 only,
- * as published by the Free Software Foundation.
- *
- * 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 version 2 for more details (a copy is included
- * in the LICENSE file that accompanied this code).
- *
- * You should have received a copy of the GNU General Public License
- * version 2 along with this program; If not, see
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- */
-/*
- * This file creates a memory allocation primitive for Lustre, that
- * allows to fallback to vmalloc allocations should regular kernel allocations
- * fail due to size or system memory fragmentation.
- *
- * Author: Oleg Drokin <green@linuxhacker.ru>
- *
- */
-/*
- * This file is part of Lustre, http://www.lustre.org/
- * Lustre is a trademark of Seagate Technology.
- */
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-
-#include <linux/libcfs/libcfs.h>
-
-void *libcfs_kvzalloc(size_t size, gfp_t flags)
-{
- void *ret;
-
- ret = kzalloc(size, flags | __GFP_NOWARN);
- if (!ret)
- ret = __vmalloc(size, flags | __GFP_ZERO, PAGE_KERNEL);
- return ret;
-}
-EXPORT_SYMBOL(libcfs_kvzalloc);
-
-void *libcfs_kvzalloc_cpt(struct cfs_cpt_table *cptab, int cpt, size_t size,
- gfp_t flags)
-{
- return kvzalloc_node(size, flags, cfs_cpt_spread_node(cptab, cpt));
-}
-EXPORT_SYMBOL(libcfs_kvzalloc_cpt);
diff --git a/drivers/staging/lustre/lnet/libcfs/linux/linux-prim.c b/drivers/staging/lustre/lnet/libcfs/linux/linux-prim.c
deleted file mode 100644
index 6f92ea272186..000000000000
--- a/drivers/staging/lustre/lnet/libcfs/linux/linux-prim.c
+++ /dev/null
@@ -1,113 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * GPL HEADER START
- *
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 only,
- * as published by the Free Software Foundation.
- *
- * 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 version 2 for more details (a copy is included
- * in the LICENSE file that accompanied this code).
- *
- * You should have received a copy of the GNU General Public License
- * version 2 along with this program; If not, see
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * GPL HEADER END
- */
-/*
- * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
- * Use is subject to license terms.
- *
- * Copyright (c) 2011, 2012, Intel Corporation.
- */
-/*
- * This file is part of Lustre, http://www.lustre.org/
- * Lustre is a trademark of Sun Microsystems, Inc.
- */
-
-#define DEBUG_SUBSYSTEM S_LNET
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/fs_struct.h>
-#include <linux/sched/signal.h>
-
-#include <linux/libcfs/libcfs.h>
-
-#if defined(CONFIG_KGDB)
-#include <linux/kgdb.h>
-#endif
-
-sigset_t
-cfs_block_allsigs(void)
-{
- unsigned long flags;
- sigset_t old;
-
- spin_lock_irqsave(&current->sighand->siglock, flags);
- old = current->blocked;
- sigfillset(&current->blocked);
- recalc_sigpending();
- spin_unlock_irqrestore(&current->sighand->siglock, flags);
-
- return old;
-}
-EXPORT_SYMBOL(cfs_block_allsigs);
-
-sigset_t cfs_block_sigs(unsigned long sigs)
-{
- unsigned long flags;
- sigset_t old;
-
- spin_lock_irqsave(&current->sighand->siglock, flags);
- old = current->blocked;
- sigaddsetmask(&current->blocked, sigs);
- recalc_sigpending();
- spin_unlock_irqrestore(&current->sighand->siglock, flags);
- return old;
-}
-EXPORT_SYMBOL(cfs_block_sigs);
-
-/* Block all signals except for the @sigs */
-sigset_t cfs_block_sigsinv(unsigned long sigs)
-{
- unsigned long flags;
- sigset_t old;
-
- spin_lock_irqsave(&current->sighand->siglock, flags);
- old = current->blocked;
- sigaddsetmask(&current->blocked, ~sigs);
- recalc_sigpending();
- spin_unlock_irqrestore(&current->sighand->siglock, flags);
-
- return old;
-}
-EXPORT_SYMBOL(cfs_block_sigsinv);
-
-void
-cfs_restore_sigs(sigset_t old)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&current->sighand->siglock, flags);
- current->blocked = old;
- recalc_sigpending();
- spin_unlock_irqrestore(&current->sighand->siglock, flags);
-}
-EXPORT_SYMBOL(cfs_restore_sigs);
-
-void
-cfs_clear_sigpending(void)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&current->sighand->siglock, flags);
- clear_tsk_thread_flag(current, TIF_SIGPENDING);
- spin_unlock_irqrestore(&current->sighand->siglock, flags);
-}
-EXPORT_SYMBOL(cfs_clear_sigpending);
diff --git a/drivers/staging/lustre/lnet/libcfs/tracefile.c b/drivers/staging/lustre/lnet/libcfs/tracefile.c
index 57913aae1d88..4affca750bc5 100644
--- a/drivers/staging/lustre/lnet/libcfs/tracefile.c
+++ b/drivers/staging/lustre/lnet/libcfs/tracefile.c
@@ -441,7 +441,7 @@ console:
if (cfs_time_after(cfs_time_current(),
cdls->cdls_next + libcfs_console_max_delay +
- cfs_time_seconds(10))) {
+ 10 * HZ)) {
/* last timeout was a long time ago */
cdls->cdls_delay /= libcfs_console_backoff * 4;
} else {
@@ -1071,7 +1071,7 @@ end_loop:
init_waitqueue_entry(&__wait, current);
add_wait_queue(&tctl->tctl_waitq, &__wait);
set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(cfs_time_seconds(1));
+ schedule_timeout(HZ);
remove_wait_queue(&tctl->tctl_waitq, &__wait);
}
complete(&tctl->tctl_stop);
diff --git a/drivers/staging/lustre/lnet/lnet/acceptor.c b/drivers/staging/lustre/lnet/lnet/acceptor.c
index ee85cab6f437..5648f17eddc0 100644
--- a/drivers/staging/lustre/lnet/lnet/acceptor.c
+++ b/drivers/staging/lustre/lnet/lnet/acceptor.c
@@ -240,7 +240,7 @@ lnet_accept(struct socket *sock, __u32 magic)
return -EPROTO;
}
- if (magic == le32_to_cpu(LNET_PROTO_TCP_MAGIC))
+ if (lnet_accept_magic(magic, LNET_PROTO_TCP_MAGIC))
str = "'old' socknal/tcpnal";
else
str = "unrecognised";
@@ -335,8 +335,6 @@ lnet_acceptor(void *arg)
LASSERT(!lnet_acceptor_state.pta_sock);
- cfs_block_allsigs();
-
rc = lnet_sock_listen(&lnet_acceptor_state.pta_sock, 0, accept_port,
accept_backlog);
if (rc) {
@@ -365,7 +363,7 @@ lnet_acceptor(void *arg)
if (rc != -EAGAIN) {
CWARN("Accept error %d: pausing...\n", rc);
set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(cfs_time_seconds(1));
+ schedule_timeout(HZ);
}
continue;
}
diff --git a/drivers/staging/lustre/lnet/lnet/api-ni.c b/drivers/staging/lustre/lnet/lnet/api-ni.c
index 2c7abad57104..90266be0132d 100644
--- a/drivers/staging/lustre/lnet/lnet/api-ni.c
+++ b/drivers/staging/lustre/lnet/lnet/api-ni.c
@@ -961,19 +961,15 @@ static void
lnet_ping_md_unlink(struct lnet_ping_info *pinfo,
struct lnet_handle_md *md_handle)
{
- sigset_t blocked = cfs_block_allsigs();
-
LNetMDUnlink(*md_handle);
LNetInvalidateMDHandle(md_handle);
/* NB md could be busy; this just starts the unlink */
while (pinfo->pi_features != LNET_PING_FEAT_INVAL) {
CDEBUG(D_NET, "Still waiting for ping MD to unlink\n");
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(cfs_time_seconds(1));
+ set_current_state(TASK_NOLOAD);
+ schedule_timeout(HZ);
}
-
- cfs_restore_sigs(blocked);
}
static void
@@ -1109,7 +1105,7 @@ lnet_clear_zombies_nis_locked(void)
libcfs_nid2str(ni->ni_nid));
}
set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(cfs_time_seconds(1));
+ schedule_timeout(HZ);
lnet_net_lock(LNET_LOCK_EX);
continue;
}
@@ -1218,6 +1214,7 @@ lnet_startup_lndni(struct lnet_ni *ni, struct lnet_ioctl_config_data *conf)
struct lnet_lnd *lnd;
struct lnet_tx_queue *tq;
int i;
+ u32 seed;
lnd_type = LNET_NETTYP(LNET_NIDNET(ni->ni_nid));
@@ -1356,6 +1353,12 @@ lnet_startup_lndni(struct lnet_ni *ni, struct lnet_ioctl_config_data *conf)
tq->tq_credits = lnet_ni_tq_credits(ni);
}
+ /* Nodes with small feet have little entropy. The NID for this
+ * node gives the most entropy in the low bits.
+ */
+ seed = LNET_NIDADDR(ni->ni_nid);
+ add_device_randomness(&seed, sizeof(seed));
+
CDEBUG(D_LNI, "Added LNI %s [%d/%d/%d/%d]\n",
libcfs_nid2str(ni->ni_nid), ni->ni_peertxcredits,
lnet_ni_tq_credits(ni) * LNET_CPT_NUMBER,
@@ -2141,7 +2144,6 @@ static int lnet_ping(struct lnet_process_id id, int timeout_ms,
int nob;
int rc;
int rc2;
- sigset_t blocked;
infosz = offsetof(struct lnet_ping_info, pi_ni[n_ids]);
@@ -2197,13 +2199,9 @@ static int lnet_ping(struct lnet_process_id id, int timeout_ms,
do {
/* MUST block for unlink to complete */
- if (unlinked)
- blocked = cfs_block_allsigs();
-
- rc2 = LNetEQPoll(&eqh, 1, timeout_ms, &event, &which);
- if (unlinked)
- cfs_restore_sigs(blocked);
+ rc2 = LNetEQPoll(&eqh, 1, timeout_ms, !unlinked,
+ &event, &which);
CDEBUG(D_NET, "poll %d(%d %d)%s\n", rc2,
(rc2 <= 0) ? -1 : event.type,
diff --git a/drivers/staging/lustre/lnet/lnet/lib-eq.c b/drivers/staging/lustre/lnet/lnet/lib-eq.c
index a173b69e2f92..ea53b5cb3f72 100644
--- a/drivers/staging/lustre/lnet/lnet/lib-eq.c
+++ b/drivers/staging/lustre/lnet/lnet/lib-eq.c
@@ -308,7 +308,7 @@ lnet_eq_dequeue_event(struct lnet_eq *eq, struct lnet_event *ev)
*/
static int
-lnet_eq_wait_locked(int *timeout_ms)
+lnet_eq_wait_locked(int *timeout_ms, long state)
__must_hold(&the_lnet.ln_eq_wait_lock)
{
int tms = *timeout_ms;
@@ -320,7 +320,7 @@ __must_hold(&the_lnet.ln_eq_wait_lock)
return -ENXIO; /* don't want to wait and no new event */
init_waitqueue_entry(&wl, current);
- set_current_state(TASK_INTERRUPTIBLE);
+ set_current_state(state);
add_wait_queue(&the_lnet.ln_eq_waitq, &wl);
lnet_eq_wait_unlock();
@@ -359,6 +359,7 @@ __must_hold(&the_lnet.ln_eq_wait_lock)
* \param timeout_ms Time in milliseconds to wait for an event to occur on
* one of the EQs. The constant LNET_TIME_FOREVER can be used to indicate an
* infinite timeout.
+ * \param interruptible, if true, use TASK_INTERRUPTIBLE, else TASK_NOLOAD
* \param event,which On successful return (1 or -EOVERFLOW), \a event will
* hold the next event in the EQs, and \a which will contain the index of the
* EQ from which the event was taken.
@@ -372,6 +373,7 @@ __must_hold(&the_lnet.ln_eq_wait_lock)
*/
int
LNetEQPoll(struct lnet_handle_eq *eventqs, int neq, int timeout_ms,
+ int interruptible,
struct lnet_event *event, int *which)
{
int wait = 1;
@@ -412,7 +414,9 @@ LNetEQPoll(struct lnet_handle_eq *eventqs, int neq, int timeout_ms,
* 0 : don't want to wait anymore, but might have new event
* so need to call dequeue again
*/
- wait = lnet_eq_wait_locked(&timeout_ms);
+ wait = lnet_eq_wait_locked(&timeout_ms,
+ interruptible ? TASK_INTERRUPTIBLE
+ : TASK_NOLOAD);
if (wait < 0) /* no new event */
break;
}
diff --git a/drivers/staging/lustre/lnet/lnet/lib-move.c b/drivers/staging/lustre/lnet/lnet/lib-move.c
index c673037dbce4..ed43b3f4b114 100644
--- a/drivers/staging/lustre/lnet/lnet/lib-move.c
+++ b/drivers/staging/lustre/lnet/lnet/lib-move.c
@@ -524,7 +524,7 @@ lnet_peer_is_alive(struct lnet_peer *lp, unsigned long now)
return 0;
deadline = cfs_time_add(lp->lp_last_alive,
- cfs_time_seconds(lp->lp_ni->ni_peertimeout));
+ lp->lp_ni->ni_peertimeout * HZ);
alive = cfs_time_after(deadline, now);
/* Update obsolete lp_alive except for routers assumed to be dead
@@ -562,7 +562,7 @@ lnet_peer_alive_locked(struct lnet_peer *lp)
unsigned long next_query =
cfs_time_add(lp->lp_last_query,
- cfs_time_seconds(lnet_queryinterval));
+ lnet_queryinterval * HZ);
if (time_before(now, next_query)) {
if (lp->lp_alive)
diff --git a/drivers/staging/lustre/lnet/lnet/lib-ptl.c b/drivers/staging/lustre/lnet/lnet/lib-ptl.c
index 471f2f6c86f4..fc47379c5938 100644
--- a/drivers/staging/lustre/lnet/lnet/lib-ptl.c
+++ b/drivers/staging/lustre/lnet/lnet/lib-ptl.c
@@ -841,6 +841,7 @@ lnet_portals_destroy(void)
cfs_array_free(the_lnet.ln_portals);
the_lnet.ln_portals = NULL;
+ the_lnet.ln_nportals = 0;
}
int
@@ -851,12 +852,12 @@ lnet_portals_create(void)
size = offsetof(struct lnet_portal, ptl_mt_maps[LNET_CPT_NUMBER]);
- the_lnet.ln_nportals = MAX_PORTALS;
- the_lnet.ln_portals = cfs_array_alloc(the_lnet.ln_nportals, size);
+ the_lnet.ln_portals = cfs_array_alloc(MAX_PORTALS, size);
if (!the_lnet.ln_portals) {
CERROR("Failed to allocate portals table\n");
return -ENOMEM;
}
+ the_lnet.ln_nportals = MAX_PORTALS;
for (i = 0; i < the_lnet.ln_nportals; i++) {
if (lnet_ptl_setup(the_lnet.ln_portals[i], i)) {
diff --git a/drivers/staging/lustre/lnet/lnet/lib-socket.c b/drivers/staging/lustre/lnet/lnet/lib-socket.c
index ce93806eefca..1bee667802b0 100644
--- a/drivers/staging/lustre/lnet/lnet/lib-socket.c
+++ b/drivers/staging/lustre/lnet/lnet/lib-socket.c
@@ -448,14 +448,13 @@ int
lnet_sock_getaddr(struct socket *sock, bool remote, __u32 *ip, int *port)
{
struct sockaddr_in sin;
- int len = sizeof(sin);
int rc;
if (remote)
- rc = kernel_getpeername(sock, (struct sockaddr *)&sin, &len);
+ rc = kernel_getpeername(sock, (struct sockaddr *)&sin);
else
- rc = kernel_getsockname(sock, (struct sockaddr *)&sin, &len);
- if (rc) {
+ rc = kernel_getsockname(sock, (struct sockaddr *)&sin);
+ if (rc < 0) {
CERROR("Error %d getting sock %s IP/port\n",
rc, remote ? "peer" : "local");
return rc;
diff --git a/drivers/staging/lustre/lnet/lnet/net_fault.c b/drivers/staging/lustre/lnet/lnet/net_fault.c
index e3468cef273b..a63b7941d435 100644
--- a/drivers/staging/lustre/lnet/lnet/net_fault.c
+++ b/drivers/staging/lustre/lnet/lnet/net_fault.c
@@ -315,9 +315,8 @@ drop_rule_match(struct lnet_drop_rule *rule, lnet_nid_t src,
rule->dr_time_base = now;
rule->dr_drop_time = rule->dr_time_base +
- cfs_time_seconds(
- prandom_u32_max(attr->u.drop.da_interval));
- rule->dr_time_base += cfs_time_seconds(attr->u.drop.da_interval);
+ prandom_u32_max(attr->u.drop.da_interval) * HZ;
+ rule->dr_time_base += attr->u.drop.da_interval * HZ;
CDEBUG(D_NET, "Drop Rule %s->%s: next drop : %lu\n",
libcfs_nid2str(attr->fa_src),
@@ -440,8 +439,7 @@ static struct delay_daemon_data delay_dd;
static unsigned long
round_timeout(unsigned long timeout)
{
- return cfs_time_seconds((unsigned int)
- cfs_duration_sec(cfs_time_sub(timeout, 0)) + 1);
+ return (unsigned int)rounddown(timeout, HZ) + HZ;
}
static void
@@ -483,10 +481,8 @@ delay_rule_match(struct lnet_delay_rule *rule, lnet_nid_t src,
rule->dl_time_base = now;
rule->dl_delay_time = rule->dl_time_base +
- cfs_time_seconds(
- prandom_u32_max(
- attr->u.delay.la_interval));
- rule->dl_time_base += cfs_time_seconds(attr->u.delay.la_interval);
+ prandom_u32_max(attr->u.delay.la_interval) * HZ;
+ rule->dl_time_base += attr->u.delay.la_interval * HZ;
CDEBUG(D_NET, "Delay Rule %s->%s: next delay : %lu\n",
libcfs_nid2str(attr->fa_src),
diff --git a/drivers/staging/lustre/lnet/lnet/peer.c b/drivers/staging/lustre/lnet/lnet/peer.c
index 3e157c10fec4..3d4caa609c83 100644
--- a/drivers/staging/lustre/lnet/lnet/peer.c
+++ b/drivers/staging/lustre/lnet/lnet/peer.c
@@ -137,7 +137,7 @@ lnet_peer_table_deathrow_wait_locked(struct lnet_peer_table *ptable,
ptable->pt_zombies);
}
set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(cfs_time_seconds(1) >> 1);
+ schedule_timeout(HZ >> 1);
lnet_net_lock(cpt_locked);
}
}
diff --git a/drivers/staging/lustre/lnet/lnet/router.c b/drivers/staging/lustre/lnet/lnet/router.c
index 6504761ca598..a3c3f4959f46 100644
--- a/drivers/staging/lustre/lnet/lnet/router.c
+++ b/drivers/staging/lustre/lnet/lnet/router.c
@@ -808,7 +808,7 @@ lnet_wait_known_routerstate(void)
return;
set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(cfs_time_seconds(1));
+ schedule_timeout(HZ);
}
}
@@ -1011,7 +1011,7 @@ lnet_ping_router_locked(struct lnet_peer *rtr)
if (secs && !rtr->lp_ping_notsent &&
cfs_time_after(now, cfs_time_add(rtr->lp_ping_timestamp,
- cfs_time_seconds(secs)))) {
+ secs * HZ))) {
int rc;
struct lnet_process_id id;
struct lnet_handle_md mdh;
@@ -1185,7 +1185,7 @@ lnet_prune_rc_data(int wait_unlink)
CDEBUG(((i & (-i)) == i) ? D_WARNING : D_NET,
"Waiting for rc buffers to unlink\n");
set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(cfs_time_seconds(1) / 4);
+ schedule_timeout(HZ / 4);
lnet_net_lock(LNET_LOCK_EX);
}
@@ -1226,8 +1226,6 @@ lnet_router_checker(void *arg)
struct lnet_peer *rtr;
struct list_head *entry;
- cfs_block_allsigs();
-
while (the_lnet.ln_rc_state == LNET_RC_STATE_RUNNING) {
__u64 version;
int cpt;
@@ -1282,7 +1280,7 @@ rescan:
else
wait_event_interruptible_timeout(the_lnet.ln_rc_waitq,
false,
- cfs_time_seconds(1));
+ HZ);
}
lnet_prune_rc_data(1); /* wait for UNLINK */
diff --git a/drivers/staging/lustre/lnet/selftest/conctl.c b/drivers/staging/lustre/lnet/selftest/conctl.c
index 34ba440b3c02..a2d8092bdeb7 100644
--- a/drivers/staging/lustre/lnet/selftest/conctl.c
+++ b/drivers/staging/lustre/lnet/selftest/conctl.c
@@ -648,14 +648,10 @@ static int lst_test_add_ioctl(struct lstio_test_args *args)
return -EINVAL;
if (args->lstio_tes_param) {
- param = kmalloc(args->lstio_tes_param_len, GFP_KERNEL);
- if (!param)
- goto out;
- if (copy_from_user(param, args->lstio_tes_param,
- args->lstio_tes_param_len)) {
- rc = -EFAULT;
- goto out;
- }
+ param = memdup_user(args->lstio_tes_param,
+ args->lstio_tes_param_len);
+ if (IS_ERR(param))
+ return PTR_ERR(param);
}
rc = -EFAULT;
@@ -674,7 +670,7 @@ static int lst_test_add_ioctl(struct lstio_test_args *args)
args->lstio_tes_param_len,
&ret, args->lstio_tes_resultp);
- if (ret)
+ if (!rc && ret)
rc = (copy_to_user(args->lstio_tes_retp, &ret,
sizeof(ret))) ? -EFAULT : 0;
out:
diff --git a/drivers/staging/lustre/lnet/selftest/conrpc.c b/drivers/staging/lustre/lnet/selftest/conrpc.c
index 7aa515c34594..6dcc966b293b 100644
--- a/drivers/staging/lustre/lnet/selftest/conrpc.c
+++ b/drivers/staging/lustre/lnet/selftest/conrpc.c
@@ -359,7 +359,7 @@ lstcon_rpc_trans_postwait(struct lstcon_rpc_trans *trans, int timeout)
rc = wait_event_interruptible_timeout(trans->tas_waitq,
lstcon_rpc_trans_check(trans),
- cfs_time_seconds(timeout));
+ timeout * HZ);
rc = (rc > 0) ? 0 : ((rc < 0) ? -EINTR : -ETIMEDOUT);
mutex_lock(&console_session.ses_mutex);
@@ -1350,7 +1350,7 @@ lstcon_rpc_cleanup_wait(void)
CWARN("Session is shutting down, waiting for termination of transactions\n");
set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(cfs_time_seconds(1));
+ schedule_timeout(HZ);
mutex_lock(&console_session.ses_mutex);
}
diff --git a/drivers/staging/lustre/lnet/selftest/framework.c b/drivers/staging/lustre/lnet/selftest/framework.c
index c7697f66f663..0ca1e3a780ca 100644
--- a/drivers/staging/lustre/lnet/selftest/framework.c
+++ b/drivers/staging/lustre/lnet/selftest/framework.c
@@ -187,7 +187,7 @@ sfw_del_session_timer(void)
return 0;
}
- return EBUSY; /* racing with sfw_session_expired() */
+ return -EBUSY; /* racing with sfw_session_expired() */
}
static void
diff --git a/drivers/staging/lustre/lnet/selftest/rpc.c b/drivers/staging/lustre/lnet/selftest/rpc.c
index f8198ad1046e..9613b0a77007 100644
--- a/drivers/staging/lustre/lnet/selftest/rpc.c
+++ b/drivers/staging/lustre/lnet/selftest/rpc.c
@@ -1604,7 +1604,7 @@ srpc_startup(void)
/* 1 second pause to avoid timestamp reuse */
set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(cfs_time_seconds(1));
+ schedule_timeout(HZ);
srpc_data.rpc_matchbits = ((__u64)ktime_get_real_seconds()) << 48;
srpc_data.rpc_state = SRPC_STATE_NONE;
diff --git a/drivers/staging/lustre/lnet/selftest/selftest.h b/drivers/staging/lustre/lnet/selftest/selftest.h
index ad04534f000c..05466b85e1c0 100644
--- a/drivers/staging/lustre/lnet/selftest/selftest.h
+++ b/drivers/staging/lustre/lnet/selftest/selftest.h
@@ -575,7 +575,7 @@ swi_state2str(int state)
#define selftest_wait_events() \
do { \
set_current_state(TASK_UNINTERRUPTIBLE); \
- schedule_timeout(cfs_time_seconds(1) / 10); \
+ schedule_timeout(HZ / 10); \
} while (0)
#define lst_wait_until(cond, lock, fmt, ...) \
diff --git a/drivers/staging/lustre/lnet/selftest/timer.c b/drivers/staging/lustre/lnet/selftest/timer.c
index ab125a8524c5..1b2c5fc81358 100644
--- a/drivers/staging/lustre/lnet/selftest/timer.c
+++ b/drivers/staging/lustre/lnet/selftest/timer.c
@@ -170,14 +170,12 @@ stt_timer_main(void *arg)
{
int rc = 0;
- cfs_block_allsigs();
-
while (!stt_data.stt_shuttingdown) {
stt_check_timers(&stt_data.stt_prev_slot);
rc = wait_event_timeout(stt_data.stt_waitq,
stt_data.stt_shuttingdown,
- cfs_time_seconds(STTIMER_SLOTTIME));
+ STTIMER_SLOTTIME * HZ);
}
spin_lock(&stt_data.stt_lock);
diff --git a/drivers/staging/lustre/lustre/Kconfig b/drivers/staging/lustre/lustre/Kconfig
index 90d826946c6a..ccb78a945995 100644
--- a/drivers/staging/lustre/lustre/Kconfig
+++ b/drivers/staging/lustre/lustre/Kconfig
@@ -1,6 +1,5 @@
config LUSTRE_FS
tristate "Lustre file system client support"
- depends on m && !MIPS && !XTENSA && !SUPERH
depends on LNET
select CRYPTO
select CRYPTO_CRC32
diff --git a/drivers/staging/lustre/lustre/fid/fid_request.c b/drivers/staging/lustre/lustre/fid/fid_request.c
index 009c2367f74e..030680f37c79 100644
--- a/drivers/staging/lustre/lustre/fid/fid_request.c
+++ b/drivers/staging/lustre/lustre/fid/fid_request.c
@@ -118,22 +118,22 @@ static int seq_client_rpc(struct lu_client_seq *seq,
goto out_req;
out = req_capsule_server_get(&req->rq_pill, &RMF_SEQ_RANGE);
- *output = *out;
- if (!lu_seq_range_is_sane(output)) {
+ if (!lu_seq_range_is_sane(out)) {
CERROR("%s: Invalid range received from server: "
- DRANGE "\n", seq->lcs_name, PRANGE(output));
+ DRANGE "\n", seq->lcs_name, PRANGE(out));
rc = -EINVAL;
goto out_req;
}
- if (lu_seq_range_is_exhausted(output)) {
+ if (lu_seq_range_is_exhausted(out)) {
CERROR("%s: Range received from server is exhausted: "
- DRANGE "]\n", seq->lcs_name, PRANGE(output));
+ DRANGE "]\n", seq->lcs_name, PRANGE(out));
rc = -EINVAL;
goto out_req;
}
+ *output = *out;
CDEBUG_LIMIT(debug_mask, "%s: Allocated %s-sequence " DRANGE "]\n",
seq->lcs_name, opcname, PRANGE(output));
@@ -174,6 +174,7 @@ static int seq_client_alloc_seq(const struct lu_env *env,
if (rc) {
CERROR("%s: Can't allocate new meta-sequence, rc %d\n",
seq->lcs_name, rc);
+ *seqnr = U64_MAX;
return rc;
}
CDEBUG(D_INFO, "%s: New range - " DRANGE "\n",
@@ -192,71 +193,49 @@ static int seq_client_alloc_seq(const struct lu_env *env,
return rc;
}
-static int seq_fid_alloc_prep(struct lu_client_seq *seq,
- wait_queue_entry_t *link)
-{
- if (seq->lcs_update) {
- add_wait_queue(&seq->lcs_waitq, link);
- set_current_state(TASK_UNINTERRUPTIBLE);
- mutex_unlock(&seq->lcs_mutex);
-
- schedule();
-
- mutex_lock(&seq->lcs_mutex);
- remove_wait_queue(&seq->lcs_waitq, link);
- set_current_state(TASK_RUNNING);
- return -EAGAIN;
- }
- ++seq->lcs_update;
- mutex_unlock(&seq->lcs_mutex);
- return 0;
-}
-
-static void seq_fid_alloc_fini(struct lu_client_seq *seq)
-{
- LASSERT(seq->lcs_update == 1);
- mutex_lock(&seq->lcs_mutex);
- --seq->lcs_update;
- wake_up(&seq->lcs_waitq);
-}
-
/* Allocate new fid on passed client @seq and save it to @fid. */
int seq_client_alloc_fid(const struct lu_env *env,
struct lu_client_seq *seq, struct lu_fid *fid)
{
- wait_queue_entry_t link;
int rc;
LASSERT(seq);
LASSERT(fid);
- init_waitqueue_entry(&link, current);
- mutex_lock(&seq->lcs_mutex);
+ spin_lock(&seq->lcs_lock);
if (OBD_FAIL_CHECK(OBD_FAIL_SEQ_EXHAUST))
seq->lcs_fid.f_oid = seq->lcs_width;
- while (1) {
+ wait_event_cmd(seq->lcs_waitq,
+ (!fid_is_zero(&seq->lcs_fid) &&
+ fid_oid(&seq->lcs_fid) < seq->lcs_width) ||
+ !seq->lcs_update,
+ spin_unlock(&seq->lcs_lock),
+ spin_lock(&seq->lcs_lock));
+
+ if (!fid_is_zero(&seq->lcs_fid) &&
+ fid_oid(&seq->lcs_fid) < seq->lcs_width) {
+ /* Just bump last allocated fid and return to caller. */
+ seq->lcs_fid.f_oid += 1;
+ rc = 0;
+ } else {
u64 seqnr;
- if (!fid_is_zero(&seq->lcs_fid) &&
- fid_oid(&seq->lcs_fid) < seq->lcs_width) {
- /* Just bump last allocated fid and return to caller. */
- seq->lcs_fid.f_oid += 1;
- rc = 0;
- break;
- }
-
- rc = seq_fid_alloc_prep(seq, &link);
- if (rc)
- continue;
+ LASSERT(seq->lcs_update == 0);
+ seq->lcs_update = 1;
+ spin_unlock(&seq->lcs_lock);
rc = seq_client_alloc_seq(env, seq, &seqnr);
+
+ spin_lock(&seq->lcs_lock);
+ seq->lcs_update = 0;
+ wake_up(&seq->lcs_waitq);
+
if (rc) {
CERROR("%s: Can't allocate new sequence, rc %d\n",
seq->lcs_name, rc);
- seq_fid_alloc_fini(seq);
- mutex_unlock(&seq->lcs_mutex);
+ spin_unlock(&seq->lcs_lock);
return rc;
}
@@ -272,13 +251,10 @@ int seq_client_alloc_fid(const struct lu_env *env,
* to setup FLD for it.
*/
rc = 1;
-
- seq_fid_alloc_fini(seq);
- break;
}
*fid = seq->lcs_fid;
- mutex_unlock(&seq->lcs_mutex);
+ spin_unlock(&seq->lcs_lock);
CDEBUG(D_INFO,
"%s: Allocated FID " DFID "\n", seq->lcs_name, PFID(fid));
@@ -292,23 +268,14 @@ EXPORT_SYMBOL(seq_client_alloc_fid);
*/
void seq_client_flush(struct lu_client_seq *seq)
{
- wait_queue_entry_t link;
LASSERT(seq);
- init_waitqueue_entry(&link, current);
- mutex_lock(&seq->lcs_mutex);
-
- while (seq->lcs_update) {
- add_wait_queue(&seq->lcs_waitq, &link);
- set_current_state(TASK_UNINTERRUPTIBLE);
- mutex_unlock(&seq->lcs_mutex);
+ spin_lock(&seq->lcs_lock);
- schedule();
-
- mutex_lock(&seq->lcs_mutex);
- remove_wait_queue(&seq->lcs_waitq, &link);
- set_current_state(TASK_RUNNING);
- }
+ wait_event_cmd(seq->lcs_waitq,
+ !seq->lcs_update,
+ spin_unlock(&seq->lcs_lock),
+ spin_lock(&seq->lcs_lock));
fid_zero(&seq->lcs_fid);
/**
@@ -319,7 +286,7 @@ void seq_client_flush(struct lu_client_seq *seq)
seq->lcs_space.lsr_index = -1;
lu_seq_range_init(&seq->lcs_space);
- mutex_unlock(&seq->lcs_mutex);
+ spin_unlock(&seq->lcs_lock);
}
EXPORT_SYMBOL(seq_client_flush);
@@ -382,7 +349,7 @@ static int seq_client_init(struct lu_client_seq *seq,
seq->lcs_type = type;
- mutex_init(&seq->lcs_mutex);
+ spin_lock_init(&seq->lcs_lock);
if (type == LUSTRE_SEQ_METADATA)
seq->lcs_width = LUSTRE_METADATA_SEQ_MAX_WIDTH;
else
diff --git a/drivers/staging/lustre/lustre/fid/lproc_fid.c b/drivers/staging/lustre/lustre/fid/lproc_fid.c
index 083419f77697..a1e5bf9f36ec 100644
--- a/drivers/staging/lustre/lustre/fid/lproc_fid.c
+++ b/drivers/staging/lustre/lustre/fid/lproc_fid.c
@@ -98,33 +98,43 @@ ldebugfs_fid_space_seq_write(struct file *file,
size_t count, loff_t *off)
{
struct lu_client_seq *seq;
+ struct lu_seq_range range;
int rc;
seq = ((struct seq_file *)file->private_data)->private;
- mutex_lock(&seq->lcs_mutex);
- rc = ldebugfs_fid_write_common(buffer, count, &seq->lcs_space);
+ rc = ldebugfs_fid_write_common(buffer, count, &range);
- if (rc == 0) {
+ spin_lock(&seq->lcs_lock);
+ if (seq->lcs_update)
+ /* An RPC call is active to update lcs_space */
+ rc = -EBUSY;
+ if (rc > 0)
+ seq->lcs_space = range;
+ spin_unlock(&seq->lcs_lock);
+
+ if (rc > 0) {
CDEBUG(D_INFO, "%s: Space: " DRANGE "\n",
- seq->lcs_name, PRANGE(&seq->lcs_space));
+ seq->lcs_name, PRANGE(&range));
}
- mutex_unlock(&seq->lcs_mutex);
-
- return count;
+ return rc;
}
static int
ldebugfs_fid_space_seq_show(struct seq_file *m, void *unused)
{
struct lu_client_seq *seq = (struct lu_client_seq *)m->private;
+ int rc = 0;
- mutex_lock(&seq->lcs_mutex);
- seq_printf(m, "[%#llx - %#llx]:%x:%s\n", PRANGE(&seq->lcs_space));
- mutex_unlock(&seq->lcs_mutex);
+ spin_lock(&seq->lcs_lock);
+ if (seq->lcs_update)
+ rc = -EBUSY;
+ else
+ seq_printf(m, "[%#llx - %#llx]:%x:%s\n", PRANGE(&seq->lcs_space));
+ spin_unlock(&seq->lcs_lock);
- return 0;
+ return rc;
}
static ssize_t
@@ -142,7 +152,7 @@ ldebugfs_fid_width_seq_write(struct file *file,
if (rc)
return rc;
- mutex_lock(&seq->lcs_mutex);
+ spin_lock(&seq->lcs_lock);
if (seq->lcs_type == LUSTRE_SEQ_DATA)
max = LUSTRE_DATA_SEQ_MAX_WIDTH;
else
@@ -155,7 +165,7 @@ ldebugfs_fid_width_seq_write(struct file *file,
seq->lcs_width);
}
- mutex_unlock(&seq->lcs_mutex);
+ spin_unlock(&seq->lcs_lock);
return count;
}
@@ -165,9 +175,9 @@ ldebugfs_fid_width_seq_show(struct seq_file *m, void *unused)
{
struct lu_client_seq *seq = (struct lu_client_seq *)m->private;
- mutex_lock(&seq->lcs_mutex);
+ spin_lock(&seq->lcs_lock);
seq_printf(m, "%llu\n", seq->lcs_width);
- mutex_unlock(&seq->lcs_mutex);
+ spin_unlock(&seq->lcs_lock);
return 0;
}
@@ -177,9 +187,9 @@ ldebugfs_fid_fid_seq_show(struct seq_file *m, void *unused)
{
struct lu_client_seq *seq = (struct lu_client_seq *)m->private;
- mutex_lock(&seq->lcs_mutex);
+ spin_lock(&seq->lcs_lock);
seq_printf(m, DFID "\n", PFID(&seq->lcs_fid));
- mutex_unlock(&seq->lcs_mutex);
+ spin_unlock(&seq->lcs_lock);
return 0;
}
diff --git a/drivers/staging/lustre/lustre/fld/fld_cache.c b/drivers/staging/lustre/lustre/fld/fld_cache.c
index ecf8b9e1ed5c..2d61ca4e51cf 100644
--- a/drivers/staging/lustre/lustre/fld/fld_cache.c
+++ b/drivers/staging/lustre/lustre/fld/fld_cache.c
@@ -263,7 +263,7 @@ static void fld_cache_punch_hole(struct fld_cache *cache,
fldt = kzalloc(sizeof(*fldt), GFP_ATOMIC);
if (!fldt) {
kfree(f_new);
- /* overlap is not allowed, so dont mess up list. */
+ /* overlap is not allowed, so don't mess up list. */
return;
}
/* break f_curr RANGE into three RANGES:
diff --git a/drivers/staging/lustre/lustre/include/cl_object.h b/drivers/staging/lustre/lustre/include/cl_object.h
index 90419dca2e1e..341a145c3331 100644
--- a/drivers/staging/lustre/lustre/include/cl_object.h
+++ b/drivers/staging/lustre/lustre/include/cl_object.h
@@ -1833,7 +1833,7 @@ struct cl_io {
*/
ci_verify_layout:1,
/**
- * file is released, restore has to to be triggered by vvp layer
+ * file is released, restore has to be triggered by vvp layer
*/
ci_restore_needed:1,
/**
diff --git a/drivers/staging/lustre/lustre/include/lprocfs_status.h b/drivers/staging/lustre/lustre/include/lprocfs_status.h
index 835a729dd8d0..426e8f3c9809 100644
--- a/drivers/staging/lustre/lustre/include/lprocfs_status.h
+++ b/drivers/staging/lustre/lustre/include/lprocfs_status.h
@@ -114,7 +114,7 @@ struct rename_stats {
* LPROCFS_CNTR_AVGMINMAX indicates a multi-valued counter samples,
* (i.e. counter can be incremented by more than "1"). When specified,
* the counter maintains min, max and sum in addition to a simple
- * invocation count. This allows averages to be be computed.
+ * invocation count. This allows averages to be computed.
* If not specified, the counter is an increment-by-1 counter.
* min, max, sum, etc. are not maintained.
*
diff --git a/drivers/staging/lustre/lustre/include/lu_object.h b/drivers/staging/lustre/lustre/include/lu_object.h
index 34e35fbff978..35c7b582f36d 100644
--- a/drivers/staging/lustre/lustre/include/lu_object.h
+++ b/drivers/staging/lustre/lustre/include/lu_object.h
@@ -1328,13 +1328,6 @@ struct lu_kmem_descr {
int lu_kmem_init(struct lu_kmem_descr *caches);
void lu_kmem_fini(struct lu_kmem_descr *caches);
-void lu_buf_free(struct lu_buf *buf);
-void lu_buf_alloc(struct lu_buf *buf, size_t size);
-void lu_buf_realloc(struct lu_buf *buf, size_t size);
-
-int lu_buf_check_and_grow(struct lu_buf *buf, size_t len);
-struct lu_buf *lu_buf_check_and_alloc(struct lu_buf *buf, size_t len);
-
extern __u32 lu_context_tags_default;
extern __u32 lu_session_tags_default;
diff --git a/drivers/staging/lustre/lustre/include/lustre_dlm.h b/drivers/staging/lustre/lustre/include/lustre_dlm.h
index e0b17052b2ea..239aa2b1268f 100644
--- a/drivers/staging/lustre/lustre/include/lustre_dlm.h
+++ b/drivers/staging/lustre/lustre/include/lustre_dlm.h
@@ -60,7 +60,7 @@ struct obd_device;
#define OBD_LDLM_DEVICENAME "ldlm"
#define LDLM_DEFAULT_LRU_SIZE (100 * num_online_cpus())
-#define LDLM_DEFAULT_MAX_ALIVE (cfs_time_seconds(3900)) /* 65 min */
+#define LDLM_DEFAULT_MAX_ALIVE (65 * 60 * HZ) /* 65 min */
#define LDLM_DEFAULT_PARALLEL_AST_LIMIT 1024
/**
diff --git a/drivers/staging/lustre/lustre/include/lustre_export.h b/drivers/staging/lustre/lustre/include/lustre_export.h
index 66ac9dc7302a..40cd168ed2ea 100644
--- a/drivers/staging/lustre/lustre/include/lustre_export.h
+++ b/drivers/staging/lustre/lustre/include/lustre_export.h
@@ -87,6 +87,8 @@ struct obd_export {
struct obd_uuid exp_client_uuid;
/** To link all exports on an obd device */
struct list_head exp_obd_chain;
+ /** work_struct for destruction of export */
+ struct work_struct exp_zombie_work;
struct hlist_node exp_uuid_hash; /** uuid-export hash*/
/** Obd device of this export */
struct obd_device *exp_obd;
diff --git a/drivers/staging/lustre/lustre/include/lustre_fid.h b/drivers/staging/lustre/lustre/include/lustre_fid.h
index d19c7a27ee48..094ad282de2c 100644
--- a/drivers/staging/lustre/lustre/include/lustre_fid.h
+++ b/drivers/staging/lustre/lustre/include/lustre_fid.h
@@ -324,7 +324,7 @@ enum lu_mgr_type {
struct lu_client_seq {
/* Sequence-controller export. */
struct obd_export *lcs_exp;
- struct mutex lcs_mutex;
+ spinlock_t lcs_lock;
/*
* Range of allowed for allocation sequences. When using lu_client_seq on
diff --git a/drivers/staging/lustre/lustre/include/lustre_import.h b/drivers/staging/lustre/lustre/include/lustre_import.h
index ea158e0630e2..1731048f1ff2 100644
--- a/drivers/staging/lustre/lustre/include/lustre_import.h
+++ b/drivers/staging/lustre/lustre/include/lustre_import.h
@@ -162,8 +162,8 @@ struct obd_import {
struct ptlrpc_client *imp_client;
/** List element for linking into pinger chain */
struct list_head imp_pinger_chain;
- /** List element for linking into chain for destruction */
- struct list_head imp_zombie_chain;
+ /** work struct for destruction of import */
+ struct work_struct imp_zombie_work;
/**
* Lists of requests that are retained for replay, waiting for a reply,
diff --git a/drivers/staging/lustre/lustre/include/lustre_lib.h b/drivers/staging/lustre/lustre/include/lustre_lib.h
index ca1dce15337e..0053eafc1c10 100644
--- a/drivers/staging/lustre/lustre/include/lustre_lib.h
+++ b/drivers/staging/lustre/lustre/include/lustre_lib.h
@@ -76,281 +76,49 @@ int do_set_info_async(struct obd_import *imp,
void target_send_reply(struct ptlrpc_request *req, int rc, int fail_id);
-/*
- * l_wait_event is a flexible sleeping function, permitting simple caller
- * configuration of interrupt and timeout sensitivity along with actions to
- * be performed in the event of either exception.
- *
- * The first form of usage looks like this:
- *
- * struct l_wait_info lwi = LWI_TIMEOUT_INTR(timeout, timeout_handler,
- * intr_handler, callback_data);
- * rc = l_wait_event(waitq, condition, &lwi);
- *
- * l_wait_event() makes the current process wait on 'waitq' until 'condition'
- * is TRUE or a "killable" signal (SIGTERM, SIKGILL, SIGINT) is pending. It
- * returns 0 to signify 'condition' is TRUE, but if a signal wakes it before
- * 'condition' becomes true, it optionally calls the specified 'intr_handler'
- * if not NULL, and returns -EINTR.
- *
- * If a non-zero timeout is specified, signals are ignored until the timeout
- * has expired. At this time, if 'timeout_handler' is not NULL it is called.
- * If it returns FALSE l_wait_event() continues to wait as described above with
- * signals enabled. Otherwise it returns -ETIMEDOUT.
- *
- * LWI_INTR(intr_handler, callback_data) is shorthand for
- * LWI_TIMEOUT_INTR(0, NULL, intr_handler, callback_data)
- *
- * The second form of usage looks like this:
- *
- * struct l_wait_info lwi = LWI_TIMEOUT(timeout, timeout_handler);
- * rc = l_wait_event(waitq, condition, &lwi);
- *
- * This form is the same as the first except that it COMPLETELY IGNORES
- * SIGNALS. The caller must therefore beware that if 'timeout' is zero, or if
- * 'timeout_handler' is not NULL and returns FALSE, then the ONLY thing that
- * can unblock the current process is 'condition' becoming TRUE.
- *
- * Another form of usage is:
- * struct l_wait_info lwi = LWI_TIMEOUT_INTERVAL(timeout, interval,
- * timeout_handler);
- * rc = l_wait_event(waitq, condition, &lwi);
- * This is the same as previous case, but condition is checked once every
- * 'interval' jiffies (if non-zero).
- *
- * Subtle synchronization point: this macro does *not* necessary takes
- * wait-queue spin-lock before returning, and, hence, following idiom is safe
- * ONLY when caller provides some external locking:
- *
- * Thread1 Thread2
- *
- * l_wait_event(&obj->wq, ....); (1)
- *
- * wake_up(&obj->wq): (2)
- * spin_lock(&q->lock); (2.1)
- * __wake_up_common(q, ...); (2.2)
- * spin_unlock(&q->lock, flags); (2.3)
- *
- * kfree(obj); (3)
- *
- * As l_wait_event() may "short-cut" execution and return without taking
- * wait-queue spin-lock, some additional synchronization is necessary to
- * guarantee that step (3) can begin only after (2.3) finishes.
- *
- * XXX nikita: some ptlrpc daemon threads have races of that sort.
- *
- */
-static inline int back_to_sleep(void *arg)
-{
- return 0;
-}
-
-#define LWI_ON_SIGNAL_NOOP ((void (*)(void *))(-1))
-
-struct l_wait_info {
- long lwi_timeout;
- long lwi_interval;
- int lwi_allow_intr;
- int (*lwi_on_timeout)(void *);
- void (*lwi_on_signal)(void *);
- void *lwi_cb_data;
-};
-
-/* NB: LWI_TIMEOUT ignores signals completely */
-#define LWI_TIMEOUT(time, cb, data) \
-((struct l_wait_info) { \
- .lwi_timeout = time, \
- .lwi_on_timeout = cb, \
- .lwi_cb_data = data, \
- .lwi_interval = 0, \
- .lwi_allow_intr = 0 \
-})
-
-#define LWI_TIMEOUT_INTERVAL(time, interval, cb, data) \
-((struct l_wait_info) { \
- .lwi_timeout = time, \
- .lwi_on_timeout = cb, \
- .lwi_cb_data = data, \
- .lwi_interval = interval, \
- .lwi_allow_intr = 0 \
-})
-
-#define LWI_TIMEOUT_INTR(time, time_cb, sig_cb, data) \
-((struct l_wait_info) { \
- .lwi_timeout = time, \
- .lwi_on_timeout = time_cb, \
- .lwi_on_signal = sig_cb, \
- .lwi_cb_data = data, \
- .lwi_interval = 0, \
- .lwi_allow_intr = 0 \
-})
-
-#define LWI_TIMEOUT_INTR_ALL(time, time_cb, sig_cb, data) \
-((struct l_wait_info) { \
- .lwi_timeout = time, \
- .lwi_on_timeout = time_cb, \
- .lwi_on_signal = sig_cb, \
- .lwi_cb_data = data, \
- .lwi_interval = 0, \
- .lwi_allow_intr = 1 \
-})
-
-#define LWI_INTR(cb, data) LWI_TIMEOUT_INTR(0, NULL, cb, data)
-
#define LUSTRE_FATAL_SIGS (sigmask(SIGKILL) | sigmask(SIGINT) | \
sigmask(SIGTERM) | sigmask(SIGQUIT) | \
sigmask(SIGALRM))
-
-/**
- * wait_queue_entry_t of Linux (version < 2.6.34) is a FIFO list for exclusively
- * waiting threads, which is not always desirable because all threads will
- * be waken up again and again, even user only needs a few of them to be
- * active most time. This is not good for performance because cache can
- * be polluted by different threads.
- *
- * LIFO list can resolve this problem because we always wakeup the most
- * recent active thread by default.
- *
- * NB: please don't call non-exclusive & exclusive wait on the same
- * waitq if add_wait_queue_exclusive_head is used.
- */
-#define add_wait_queue_exclusive_head(waitq, link) \
-{ \
- unsigned long flags; \
- \
- spin_lock_irqsave(&((waitq)->lock), flags); \
- __add_wait_queue_exclusive(waitq, link); \
- spin_unlock_irqrestore(&((waitq)->lock), flags); \
+static inline int l_fatal_signal_pending(struct task_struct *p)
+{
+ return signal_pending(p) && sigtestsetmask(&p->pending.signal, LUSTRE_FATAL_SIGS);
}
-/*
- * wait for @condition to become true, but no longer than timeout, specified
- * by @info.
- */
-#define __l_wait_event(wq, condition, info, ret, l_add_wait) \
-do { \
- wait_queue_entry_t __wait; \
- long __timeout = info->lwi_timeout; \
- sigset_t __blocked; \
- int __allow_intr = info->lwi_allow_intr; \
- \
- ret = 0; \
- if (condition) \
- break; \
- \
- init_waitqueue_entry(&__wait, current); \
- l_add_wait(&wq, &__wait); \
- \
- /* Block all signals (just the non-fatal ones if no timeout). */ \
- if (info->lwi_on_signal && (__timeout == 0 || __allow_intr)) \
- __blocked = cfs_block_sigsinv(LUSTRE_FATAL_SIGS); \
- else \
- __blocked = cfs_block_sigsinv(0); \
- \
- for (;;) { \
- if (condition) \
- break; \
- \
- set_current_state(TASK_INTERRUPTIBLE); \
- \
- if (__timeout == 0) { \
- schedule(); \
- } else { \
- long interval = info->lwi_interval ? \
- min_t(long, \
- info->lwi_interval, __timeout) : \
- __timeout; \
- long remaining = schedule_timeout(interval);\
- __timeout = cfs_time_sub(__timeout, \
- cfs_time_sub(interval, remaining));\
- if (__timeout == 0) { \
- if (!info->lwi_on_timeout || \
- info->lwi_on_timeout(info->lwi_cb_data)) { \
- ret = -ETIMEDOUT; \
- break; \
- } \
- /* Take signals after the timeout expires. */ \
- if (info->lwi_on_signal) \
- (void)cfs_block_sigsinv(LUSTRE_FATAL_SIGS);\
- } \
- } \
- \
- set_current_state(TASK_RUNNING); \
- \
- if (condition) \
- break; \
- if (signal_pending(current)) { \
- if (info->lwi_on_signal && \
- (__timeout == 0 || __allow_intr)) { \
- if (info->lwi_on_signal != LWI_ON_SIGNAL_NOOP) \
- info->lwi_on_signal(info->lwi_cb_data);\
- ret = -EINTR; \
- break; \
- } \
- /* We have to do this here because some signals */ \
- /* are not blockable - ie from strace(1). */ \
- /* In these cases we want to schedule_timeout() */ \
- /* again, because we don't want that to return */ \
- /* -EINTR when the RPC actually succeeded. */ \
- /* the recalc_sigpending() below will deliver the */ \
- /* signal properly. */ \
- cfs_clear_sigpending(); \
- } \
- } \
- \
- cfs_restore_sigs(__blocked); \
- \
- remove_wait_queue(&wq, &__wait); \
-} while (0)
-
-#define l_wait_event(wq, condition, info) \
-({ \
- int __ret; \
- struct l_wait_info *__info = (info); \
- \
- __l_wait_event(wq, condition, __info, \
- __ret, add_wait_queue); \
- __ret; \
-})
+/** @} lib */
-#define l_wait_event_exclusive(wq, condition, info) \
-({ \
- int __ret; \
- struct l_wait_info *__info = (info); \
- \
- __l_wait_event(wq, condition, __info, \
- __ret, add_wait_queue_exclusive); \
- __ret; \
-})
-#define l_wait_event_exclusive_head(wq, condition, info) \
-({ \
- int __ret; \
- struct l_wait_info *__info = (info); \
- \
- __l_wait_event(wq, condition, __info, \
- __ret, add_wait_queue_exclusive_head); \
- __ret; \
-})
-#define l_wait_condition(wq, condition) \
-({ \
- struct l_wait_info lwi = { 0 }; \
- l_wait_event(wq, condition, &lwi); \
+/* l_wait_event_abortable() is a bit like wait_event_killable()
+ * except there is a fixed set of signals which will abort:
+ * LUSTRE_FATAL_SIGS
+ */
+#define l_wait_event_abortable(wq, condition) \
+({ \
+ sigset_t __old_blocked; \
+ int __ret = 0; \
+ cfs_block_sigsinv(LUSTRE_FATAL_SIGS, &__old_blocked); \
+ __ret = wait_event_interruptible(wq, condition); \
+ cfs_restore_sigs(&__old_blocked); \
+ __ret; \
})
-#define l_wait_condition_exclusive(wq, condition) \
-({ \
- struct l_wait_info lwi = { 0 }; \
- l_wait_event_exclusive(wq, condition, &lwi); \
+#define l_wait_event_abortable_timeout(wq, condition, timeout) \
+({ \
+ sigset_t __old_blocked; \
+ int __ret = 0; \
+ cfs_block_sigsinv(LUSTRE_FATAL_SIGS, &__old_blocked); \
+ __ret = wait_event_interruptible_timeout(wq, condition, timeout);\
+ cfs_restore_sigs(&__old_blocked); \
+ __ret; \
})
-#define l_wait_condition_exclusive_head(wq, condition) \
-({ \
- struct l_wait_info lwi = { 0 }; \
- l_wait_event_exclusive_head(wq, condition, &lwi); \
+#define l_wait_event_abortable_exclusive(wq, condition) \
+({ \
+ sigset_t __old_blocked; \
+ int __ret = 0; \
+ cfs_block_sigsinv(LUSTRE_FATAL_SIGS, &__old_blocked); \
+ __ret = wait_event_interruptible_exclusive(wq, condition); \
+ cfs_restore_sigs(&__old_blocked); \
+ __ret; \
})
-
-/** @} lib */
-
#endif /* _LUSTRE_LIB_H */
diff --git a/drivers/staging/lustre/lustre/include/lustre_lmv.h b/drivers/staging/lustre/lustre/include/lustre_lmv.h
index f4298e5f7543..080ec1f8e19f 100644
--- a/drivers/staging/lustre/lustre/include/lustre_lmv.h
+++ b/drivers/staging/lustre/lustre/include/lustre_lmv.h
@@ -63,7 +63,7 @@ lsm_md_eq(const struct lmv_stripe_md *lsm1, const struct lmv_stripe_md *lsm2)
lsm1->lsm_md_master_mdt_index != lsm2->lsm_md_master_mdt_index ||
lsm1->lsm_md_hash_type != lsm2->lsm_md_hash_type ||
lsm1->lsm_md_layout_version != lsm2->lsm_md_layout_version ||
- !strcmp(lsm1->lsm_md_pool_name, lsm2->lsm_md_pool_name))
+ strcmp(lsm1->lsm_md_pool_name, lsm2->lsm_md_pool_name) != 0)
return false;
for (idx = 0; idx < lsm1->lsm_md_stripe_count; idx++) {
diff --git a/drivers/staging/lustre/lustre/include/lustre_mdc.h b/drivers/staging/lustre/lustre/include/lustre_mdc.h
index 007e1ec3f0f4..a9c9992a2502 100644
--- a/drivers/staging/lustre/lustre/include/lustre_mdc.h
+++ b/drivers/staging/lustre/lustre/include/lustre_mdc.h
@@ -124,7 +124,7 @@ static inline void mdc_get_rpc_lock(struct mdc_rpc_lock *lck,
*/
while (unlikely(lck->rpcl_it == MDC_FAKE_RPCL_IT)) {
mutex_unlock(&lck->rpcl_mutex);
- schedule_timeout(cfs_time_seconds(1) / 4);
+ schedule_timeout(HZ / 4);
goto again;
}
diff --git a/drivers/staging/lustre/lustre/include/lustre_net.h b/drivers/staging/lustre/lustre/include/lustre_net.h
index 3ff5de4770e8..d35ae0cda8d2 100644
--- a/drivers/staging/lustre/lustre/include/lustre_net.h
+++ b/drivers/staging/lustre/lustre/include/lustre_net.h
@@ -1259,8 +1259,6 @@ enum {
SVC_STOPPING = 1 << 1,
SVC_STARTING = 1 << 2,
SVC_RUNNING = 1 << 3,
- SVC_EVENT = 1 << 4,
- SVC_SIGNAL = 1 << 5,
};
#define PTLRPC_THR_NAME_LEN 32
@@ -1303,11 +1301,6 @@ struct ptlrpc_thread {
char t_name[PTLRPC_THR_NAME_LEN];
};
-static inline int thread_is_init(struct ptlrpc_thread *thread)
-{
- return thread->t_flags == 0;
-}
-
static inline int thread_is_stopped(struct ptlrpc_thread *thread)
{
return !!(thread->t_flags & SVC_STOPPED);
@@ -1328,16 +1321,6 @@ static inline int thread_is_running(struct ptlrpc_thread *thread)
return !!(thread->t_flags & SVC_RUNNING);
}
-static inline int thread_is_event(struct ptlrpc_thread *thread)
-{
- return !!(thread->t_flags & SVC_EVENT);
-}
-
-static inline int thread_is_signal(struct ptlrpc_thread *thread)
-{
- return !!(thread->t_flags & SVC_SIGNAL);
-}
-
static inline void thread_clear_flags(struct ptlrpc_thread *thread, __u32 flags)
{
thread->t_flags &= ~flags;
@@ -1821,6 +1804,9 @@ int ptlrpc_register_rqbd(struct ptlrpc_request_buffer_desc *rqbd);
*/
void ptlrpc_request_committed(struct ptlrpc_request *req, int force);
+int ptlrpc_inc_ref(void);
+void ptlrpc_dec_ref(void);
+
void ptlrpc_init_client(int req_portal, int rep_portal, char *name,
struct ptlrpc_client *);
struct ptlrpc_connection *ptlrpc_uuid_to_connection(struct obd_uuid *uuid);
@@ -2268,7 +2254,7 @@ static inline int ptlrpc_send_limit_expired(struct ptlrpc_request *req)
{
if (req->rq_delay_limit != 0 &&
time_before(cfs_time_add(req->rq_queued_time,
- cfs_time_seconds(req->rq_delay_limit)),
+ req->rq_delay_limit * HZ),
cfs_time_current())) {
return 1;
}
diff --git a/drivers/staging/lustre/lustre/include/lustre_sec.h b/drivers/staging/lustre/lustre/include/lustre_sec.h
index 64b6fd4fed8f..c5cb07acd0da 100644
--- a/drivers/staging/lustre/lustre/include/lustre_sec.h
+++ b/drivers/staging/lustre/lustre/include/lustre_sec.h
@@ -1058,9 +1058,6 @@ int sptlrpc_current_user_desc_size(void);
int sptlrpc_pack_user_desc(struct lustre_msg *msg, int offset);
int sptlrpc_unpack_user_desc(struct lustre_msg *req, int offset, int swabbed);
-#define CFS_CAP_CHOWN_MASK (1 << CFS_CAP_CHOWN)
-#define CFS_CAP_SYS_RESOURCE_MASK (1 << CFS_CAP_SYS_RESOURCE)
-
enum {
LUSTRE_SEC_NONE = 0,
LUSTRE_SEC_REMOTE = 1,
diff --git a/drivers/staging/lustre/lustre/include/obd.h b/drivers/staging/lustre/lustre/include/obd.h
index 4368f4e9f208..f1233ca7d337 100644
--- a/drivers/staging/lustre/lustre/include/obd.h
+++ b/drivers/staging/lustre/lustre/include/obd.h
@@ -191,7 +191,7 @@ struct client_obd {
struct sptlrpc_flavor cl_flvr_mgc; /* fixed flavor of mgc->mgs */
/* the grant values are protected by loi_list_lock below */
- unsigned long cl_dirty_pages; /* all _dirty_ in pahges */
+ unsigned long cl_dirty_pages; /* all _dirty_ in pages */
unsigned long cl_dirty_max_pages; /* allowed w/o rpc */
unsigned long cl_dirty_transit; /* dirty synchronous */
unsigned long cl_avail_grant; /* bytes of credit for ost */
diff --git a/drivers/staging/lustre/lustre/include/obd_class.h b/drivers/staging/lustre/lustre/include/obd_class.h
index 531e8ddfa9e5..f24dd74ffa09 100644
--- a/drivers/staging/lustre/lustre/include/obd_class.h
+++ b/drivers/staging/lustre/lustre/include/obd_class.h
@@ -294,10 +294,10 @@ struct obdo;
void obdo_to_ioobj(const struct obdo *oa, struct obd_ioobj *ioobj);
-#define OBT(dev) (dev)->obd_type
-#define OBP(dev, op) (dev)->obd_type->typ_dt_ops->op
-#define MDP(dev, op) (dev)->obd_type->typ_md_ops->op
-#define CTXTP(ctxt, op) (ctxt)->loc_logops->lop_##op
+#define OBT(dev) ((dev)->obd_type)
+#define OBP(dev, op) ((dev)->obd_type->typ_dt_ops->op)
+#define MDP(dev, op) ((dev)->obd_type->typ_md_ops->op)
+#define CTXTP(ctxt, op) ((ctxt)->loc_logops->lop_##op)
/*
* Ensure obd_setup: used for cleanup which must be called
diff --git a/drivers/staging/lustre/lustre/include/obd_support.h b/drivers/staging/lustre/lustre/include/obd_support.h
index 3f4fe290f6ea..8595091b8b86 100644
--- a/drivers/staging/lustre/lustre/include/obd_support.h
+++ b/drivers/staging/lustre/lustre/include/obd_support.h
@@ -516,7 +516,7 @@ extern char obd_jobid_var[];
#define POISON_PTR(ptr) ((void)0)
#else
#define POISON(ptr, c, s) memset(ptr, c, s)
-#define POISON_PTR(ptr) (ptr) = (void *)0xdeadbeef
+#define POISON_PTR(ptr) ((ptr) = (void *)0xdeadbeef)
#endif
#ifdef POISON_BULK
diff --git a/drivers/staging/lustre/lustre/ldlm/ldlm_flock.c b/drivers/staging/lustre/lustre/ldlm/ldlm_flock.c
index 657ab95091a0..411b540b96d9 100644
--- a/drivers/staging/lustre/lustre/ldlm/ldlm_flock.c
+++ b/drivers/staging/lustre/lustre/ldlm/ldlm_flock.c
@@ -310,24 +310,6 @@ reprocess:
return LDLM_ITER_CONTINUE;
}
-struct ldlm_flock_wait_data {
- struct ldlm_lock *fwd_lock;
-};
-
-static void
-ldlm_flock_interrupted_wait(void *data)
-{
- struct ldlm_lock *lock;
-
- lock = ((struct ldlm_flock_wait_data *)data)->fwd_lock;
-
- lock_res_and_lock(lock);
-
- /* client side - set flag to prevent lock from being put on LRU list */
- ldlm_set_cbpending(lock);
- unlock_res_and_lock(lock);
-}
-
/**
* Flock completion callback function.
*
@@ -342,8 +324,6 @@ int
ldlm_flock_completion_ast(struct ldlm_lock *lock, __u64 flags, void *data)
{
struct file_lock *getlk = lock->l_ast_data;
- struct ldlm_flock_wait_data fwd;
- struct l_wait_info lwi;
int rc = 0;
OBD_FAIL_TIMEOUT(OBD_FAIL_LDLM_CP_CB_WAIT2, 4);
@@ -372,13 +352,17 @@ ldlm_flock_completion_ast(struct ldlm_lock *lock, __u64 flags, void *data)
LDLM_DEBUG(lock,
"client-side enqueue returned a blocked lock, sleeping");
- fwd.fwd_lock = lock;
- lwi = LWI_TIMEOUT_INTR(0, NULL, ldlm_flock_interrupted_wait, &fwd);
/* Go to sleep until the lock is granted. */
- rc = l_wait_event(lock->l_waitq, is_granted_or_cancelled(lock), &lwi);
+ rc = l_wait_event_abortable(lock->l_waitq, is_granted_or_cancelled(lock));
if (rc) {
+ lock_res_and_lock(lock);
+
+ /* client side - set flag to prevent lock from being put on LRU list */
+ ldlm_set_cbpending(lock);
+ unlock_res_and_lock(lock);
+
LDLM_DEBUG(lock, "client-side enqueue waking up: failed (%d)",
rc);
return rc;
diff --git a/drivers/staging/lustre/lustre/ldlm/ldlm_lock.c b/drivers/staging/lustre/lustre/ldlm/ldlm_lock.c
index 7cbc6a06afec..95bea351d21d 100644
--- a/drivers/staging/lustre/lustre/ldlm/ldlm_lock.c
+++ b/drivers/staging/lustre/lustre/ldlm/ldlm_lock.c
@@ -1349,7 +1349,6 @@ out:
if ((flags & LDLM_FL_LVB_READY) && !ldlm_is_lvb_ready(lock)) {
__u64 wait_flags = LDLM_FL_LVB_READY |
LDLM_FL_DESTROYED | LDLM_FL_FAIL_NOTIFIED;
- struct l_wait_info lwi;
if (lock->l_completion_ast) {
int err = lock->l_completion_ast(lock,
@@ -1366,13 +1365,10 @@ out:
}
}
- lwi = LWI_TIMEOUT_INTR(cfs_time_seconds(obd_timeout),
- NULL, LWI_ON_SIGNAL_NOOP, NULL);
-
/* XXX FIXME see comment on CAN_MATCH in lustre_dlm.h */
- l_wait_event(lock->l_waitq,
- lock->l_flags & wait_flags,
- &lwi);
+ wait_event_idle_timeout(lock->l_waitq,
+ lock->l_flags & wait_flags,
+ obd_timeout * HZ);
if (!ldlm_is_lvb_ready(lock)) {
if (flags & LDLM_FL_TEST_LOCK)
LDLM_LOCK_RELEASE(lock);
@@ -1913,14 +1909,12 @@ void ldlm_cancel_callback(struct ldlm_lock *lock)
ldlm_set_bl_done(lock);
wake_up_all(&lock->l_waitq);
} else if (!ldlm_is_bl_done(lock)) {
- struct l_wait_info lwi = { 0 };
-
/*
* The lock is guaranteed to have been canceled once
* returning from this function.
*/
unlock_res_and_lock(lock);
- l_wait_event(lock->l_waitq, is_bl_done(lock), &lwi);
+ wait_event_idle(lock->l_waitq, is_bl_done(lock));
lock_res_and_lock(lock);
}
}
diff --git a/drivers/staging/lustre/lustre/ldlm/ldlm_lockd.c b/drivers/staging/lustre/lustre/ldlm/ldlm_lockd.c
index 5f6e7c933b81..c772c68e5a49 100644
--- a/drivers/staging/lustre/lustre/ldlm/ldlm_lockd.c
+++ b/drivers/staging/lustre/lustre/ldlm/ldlm_lockd.c
@@ -163,7 +163,7 @@ static void ldlm_handle_cp_callback(struct ptlrpc_request *req,
LDLM_DEBUG(lock, "client completion callback handler START");
if (OBD_FAIL_CHECK(OBD_FAIL_LDLM_CANCEL_BL_CB_RACE)) {
- int to = cfs_time_seconds(1);
+ int to = HZ;
while (to > 0) {
set_current_state(TASK_INTERRUPTIBLE);
@@ -327,7 +327,7 @@ static void ldlm_handle_gl_callback(struct ptlrpc_request *req,
!lock->l_readers && !lock->l_writers &&
cfs_time_after(cfs_time_current(),
cfs_time_add(lock->l_last_used,
- cfs_time_seconds(10)))) {
+ 10 * HZ))) {
unlock_res_and_lock(lock);
if (ldlm_bl_to_thread_lock(ns, NULL, lock))
ldlm_handle_bl_callback(ns, NULL, lock);
@@ -833,17 +833,15 @@ static int ldlm_bl_thread_main(void *arg)
/* cannot use bltd after this, it is only on caller's stack */
while (1) {
- struct l_wait_info lwi = { 0 };
struct ldlm_bl_work_item *blwi = NULL;
struct obd_export *exp = NULL;
int rc;
rc = ldlm_bl_get_work(blp, &blwi, &exp);
if (!rc)
- l_wait_event_exclusive(blp->blp_waitq,
- ldlm_bl_get_work(blp, &blwi,
- &exp),
- &lwi);
+ wait_event_idle_exclusive(blp->blp_waitq,
+ ldlm_bl_get_work(blp, &blwi,
+ &exp));
atomic_inc(&blp->blp_busy_threads);
if (ldlm_bl_thread_need_create(blp, blwi))
@@ -871,6 +869,10 @@ int ldlm_get_ref(void)
{
int rc = 0;
+ rc = ptlrpc_inc_ref();
+ if (rc)
+ return rc;
+
mutex_lock(&ldlm_ref_mutex);
if (++ldlm_refcount == 1) {
rc = ldlm_setup();
@@ -879,14 +881,18 @@ int ldlm_get_ref(void)
}
mutex_unlock(&ldlm_ref_mutex);
+ if (rc)
+ ptlrpc_dec_ref();
+
return rc;
}
void ldlm_put_ref(void)
{
+ int rc = 0;
mutex_lock(&ldlm_ref_mutex);
if (ldlm_refcount == 1) {
- int rc = ldlm_cleanup();
+ rc = ldlm_cleanup();
if (rc)
CERROR("ldlm_cleanup failed: %d\n", rc);
@@ -896,6 +902,8 @@ void ldlm_put_ref(void)
ldlm_refcount--;
}
mutex_unlock(&ldlm_ref_mutex);
+ if (!rc)
+ ptlrpc_dec_ref();
}
static ssize_t cancel_unused_locks_before_replay_show(struct kobject *kobj,
diff --git a/drivers/staging/lustre/lustre/ldlm/ldlm_pool.c b/drivers/staging/lustre/lustre/ldlm/ldlm_pool.c
index 8563bd32befa..53b8f33e54b5 100644
--- a/drivers/staging/lustre/lustre/ldlm/ldlm_pool.c
+++ b/drivers/staging/lustre/lustre/ldlm/ldlm_pool.c
@@ -784,9 +784,6 @@ static int ldlm_pool_granted(struct ldlm_pool *pl)
return atomic_read(&pl->pl_granted);
}
-static struct ptlrpc_thread *ldlm_pools_thread;
-static struct completion ldlm_pools_comp;
-
/*
* count locks from all namespaces (if possible). Returns number of
* cached locks.
@@ -899,8 +896,12 @@ static unsigned long ldlm_pools_cli_scan(struct shrinker *s,
sc->gfp_mask);
}
-static int ldlm_pools_recalc(enum ldlm_side client)
+static void ldlm_pools_recalc(struct work_struct *ws);
+static DECLARE_DELAYED_WORK(ldlm_recalc_pools, ldlm_pools_recalc);
+
+static void ldlm_pools_recalc(struct work_struct *ws)
{
+ enum ldlm_side client = LDLM_NAMESPACE_CLIENT;
struct ldlm_namespace *ns;
struct ldlm_namespace *ns_old = NULL;
/* seconds of sleep if no active namespaces */
@@ -982,97 +983,19 @@ static int ldlm_pools_recalc(enum ldlm_side client)
/* Wake up the blocking threads from time to time. */
ldlm_bl_thread_wakeup();
- return time;
-}
-
-static int ldlm_pools_thread_main(void *arg)
-{
- struct ptlrpc_thread *thread = (struct ptlrpc_thread *)arg;
- int c_time;
-
- thread_set_flags(thread, SVC_RUNNING);
- wake_up(&thread->t_ctl_waitq);
-
- CDEBUG(D_DLMTRACE, "%s: pool thread starting, process %d\n",
- "ldlm_poold", current_pid());
-
- while (1) {
- struct l_wait_info lwi;
-
- /*
- * Recal all pools on this tick.
- */
- c_time = ldlm_pools_recalc(LDLM_NAMESPACE_CLIENT);
-
- /*
- * Wait until the next check time, or until we're
- * stopped.
- */
- lwi = LWI_TIMEOUT(cfs_time_seconds(c_time),
- NULL, NULL);
- l_wait_event(thread->t_ctl_waitq,
- thread_is_stopping(thread) ||
- thread_is_event(thread),
- &lwi);
-
- if (thread_test_and_clear_flags(thread, SVC_STOPPING))
- break;
- thread_test_and_clear_flags(thread, SVC_EVENT);
- }
-
- thread_set_flags(thread, SVC_STOPPED);
- wake_up(&thread->t_ctl_waitq);
-
- CDEBUG(D_DLMTRACE, "%s: pool thread exiting, process %d\n",
- "ldlm_poold", current_pid());
-
- complete_and_exit(&ldlm_pools_comp, 0);
+ schedule_delayed_work(&ldlm_recalc_pools, time * HZ);
}
static int ldlm_pools_thread_start(void)
{
- struct l_wait_info lwi = { 0 };
- struct task_struct *task;
-
- if (ldlm_pools_thread)
- return -EALREADY;
-
- ldlm_pools_thread = kzalloc(sizeof(*ldlm_pools_thread), GFP_NOFS);
- if (!ldlm_pools_thread)
- return -ENOMEM;
-
- init_completion(&ldlm_pools_comp);
- init_waitqueue_head(&ldlm_pools_thread->t_ctl_waitq);
+ schedule_delayed_work(&ldlm_recalc_pools, 0);
- task = kthread_run(ldlm_pools_thread_main, ldlm_pools_thread,
- "ldlm_poold");
- if (IS_ERR(task)) {
- CERROR("Can't start pool thread, error %ld\n", PTR_ERR(task));
- kfree(ldlm_pools_thread);
- ldlm_pools_thread = NULL;
- return PTR_ERR(task);
- }
- l_wait_event(ldlm_pools_thread->t_ctl_waitq,
- thread_is_running(ldlm_pools_thread), &lwi);
return 0;
}
static void ldlm_pools_thread_stop(void)
{
- if (!ldlm_pools_thread)
- return;
-
- thread_set_flags(ldlm_pools_thread, SVC_STOPPING);
- wake_up(&ldlm_pools_thread->t_ctl_waitq);
-
- /*
- * Make sure that pools thread is finished before freeing @thread.
- * This fixes possible race and oops due to accessing freed memory
- * in pools thread.
- */
- wait_for_completion(&ldlm_pools_comp);
- kfree(ldlm_pools_thread);
- ldlm_pools_thread = NULL;
+ cancel_delayed_work_sync(&ldlm_recalc_pools);
}
static struct shrinker ldlm_pools_cli_shrinker = {
@@ -1086,20 +1009,15 @@ int ldlm_pools_init(void)
int rc;
rc = ldlm_pools_thread_start();
- if (rc)
- return rc;
-
- rc = register_shrinker(&ldlm_pools_cli_shrinker);
- if (rc)
- ldlm_pools_thread_stop();
+ if (!rc)
+ rc = register_shrinker(&ldlm_pools_cli_shrinker);
return rc;
}
void ldlm_pools_fini(void)
{
- if (ldlm_pools_thread)
- unregister_shrinker(&ldlm_pools_cli_shrinker);
+ unregister_shrinker(&ldlm_pools_cli_shrinker);
ldlm_pools_thread_stop();
}
diff --git a/drivers/staging/lustre/lustre/ldlm/ldlm_request.c b/drivers/staging/lustre/lustre/ldlm/ldlm_request.c
index 6aa37463db46..c3c9186b74ce 100644
--- a/drivers/staging/lustre/lustre/ldlm/ldlm_request.c
+++ b/drivers/staging/lustre/lustre/ldlm/ldlm_request.c
@@ -72,15 +72,6 @@ MODULE_PARM_DESC(ldlm_enqueue_min, "lock enqueue timeout minimum");
/* in client side, whether the cached locks will be canceled before replay */
unsigned int ldlm_cancel_unused_locks_before_replay = 1;
-static void interrupted_completion_wait(void *data)
-{
-}
-
-struct lock_wait_data {
- struct ldlm_lock *lwd_lock;
- __u32 lwd_conn_cnt;
-};
-
struct ldlm_async_args {
struct lustre_handle lock_handle;
};
@@ -112,10 +103,8 @@ static int ldlm_request_bufsize(int count, int type)
return sizeof(struct ldlm_request) + avail;
}
-static int ldlm_expired_completion_wait(void *data)
+static void ldlm_expired_completion_wait(struct ldlm_lock *lock, __u32 conn_cnt)
{
- struct lock_wait_data *lwd = data;
- struct ldlm_lock *lock = lwd->lwd_lock;
struct obd_import *imp;
struct obd_device *obd;
@@ -135,19 +124,17 @@ static int ldlm_expired_completion_wait(void *data)
if (last_dump == 0)
libcfs_debug_dumplog();
}
- return 0;
+ return;
}
obd = lock->l_conn_export->exp_obd;
imp = obd->u.cli.cl_import;
- ptlrpc_fail_import(imp, lwd->lwd_conn_cnt);
+ ptlrpc_fail_import(imp, conn_cnt);
LDLM_ERROR(lock,
"lock timed out (enqueued at %lld, %llds ago), entering recovery for %s@%s",
(s64)lock->l_last_activity,
(s64)(ktime_get_real_seconds() - lock->l_last_activity),
obd2cli_tgt(obd), imp->imp_connection->c_remote_uuid.uuid);
-
- return 0;
}
/**
@@ -251,11 +238,10 @@ EXPORT_SYMBOL(ldlm_completion_ast_async);
int ldlm_completion_ast(struct ldlm_lock *lock, __u64 flags, void *data)
{
/* XXX ALLOCATE - 160 bytes */
- struct lock_wait_data lwd;
struct obd_device *obd;
struct obd_import *imp = NULL;
- struct l_wait_info lwi;
__u32 timeout;
+ __u32 conn_cnt = 0;
int rc = 0;
if (flags == LDLM_FL_WAIT_NOREPROC) {
@@ -281,32 +267,33 @@ noreproc:
timeout = ldlm_cp_timeout(lock);
- lwd.lwd_lock = lock;
lock->l_last_activity = ktime_get_real_seconds();
- if (ldlm_is_no_timeout(lock)) {
- LDLM_DEBUG(lock, "waiting indefinitely because of NO_TIMEOUT");
- lwi = LWI_INTR(interrupted_completion_wait, &lwd);
- } else {
- lwi = LWI_TIMEOUT_INTR(cfs_time_seconds(timeout),
- ldlm_expired_completion_wait,
- interrupted_completion_wait, &lwd);
- }
-
if (imp) {
spin_lock(&imp->imp_lock);
- lwd.lwd_conn_cnt = imp->imp_conn_cnt;
+ conn_cnt = imp->imp_conn_cnt;
spin_unlock(&imp->imp_lock);
}
-
if (OBD_FAIL_CHECK_RESET(OBD_FAIL_LDLM_INTR_CP_AST,
OBD_FAIL_LDLM_CP_BL_RACE | OBD_FAIL_ONCE)) {
ldlm_set_fail_loc(lock);
rc = -EINTR;
} else {
- /* Go to sleep until the lock is granted or cancelled. */
- rc = l_wait_event(lock->l_waitq,
- is_granted_or_cancelled(lock), &lwi);
+ /* Go to sleep until the lock is granted or canceled. */
+ if (!ldlm_is_no_timeout(lock)) {
+ /* Wait uninterruptible for a while first */
+ rc = wait_event_idle_timeout(lock->l_waitq,
+ is_granted_or_cancelled(lock),
+ timeout * HZ);
+ if (rc == 0)
+ ldlm_expired_completion_wait(lock, conn_cnt);
+ }
+ /* Now wait abortable */
+ if (rc == 0)
+ rc = l_wait_event_abortable(lock->l_waitq,
+ is_granted_or_cancelled(lock));
+ else
+ rc = 0;
}
if (rc) {
diff --git a/drivers/staging/lustre/lustre/ldlm/ldlm_resource.c b/drivers/staging/lustre/lustre/ldlm/ldlm_resource.c
index 9958533cc227..4c44603ab6f9 100644
--- a/drivers/staging/lustre/lustre/ldlm/ldlm_resource.c
+++ b/drivers/staging/lustre/lustre/ldlm/ldlm_resource.c
@@ -799,7 +799,7 @@ static void cleanup_resource(struct ldlm_resource *res, struct list_head *q,
LDLM_DEBUG(lock, "setting FL_LOCAL_ONLY");
if (lock->l_flags & LDLM_FL_FAIL_LOC) {
set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(cfs_time_seconds(4));
+ schedule_timeout(4 * HZ);
set_current_state(TASK_RUNNING);
}
if (lock->l_completion_ast)
@@ -879,7 +879,6 @@ static int __ldlm_namespace_free(struct ldlm_namespace *ns, int force)
ldlm_namespace_cleanup(ns, force ? LDLM_FL_LOCAL_ONLY : 0);
if (atomic_read(&ns->ns_bref) > 0) {
- struct l_wait_info lwi = LWI_INTR(LWI_ON_SIGNAL_NOOP, NULL);
int rc;
CDEBUG(D_DLMTRACE,
@@ -887,11 +886,12 @@ static int __ldlm_namespace_free(struct ldlm_namespace *ns, int force)
ldlm_ns_name(ns), atomic_read(&ns->ns_bref));
force_wait:
if (force)
- lwi = LWI_TIMEOUT(msecs_to_jiffies(obd_timeout *
- MSEC_PER_SEC) / 4, NULL, NULL);
-
- rc = l_wait_event(ns->ns_waitq,
- atomic_read(&ns->ns_bref) == 0, &lwi);
+ rc = wait_event_idle_timeout(ns->ns_waitq,
+ atomic_read(&ns->ns_bref) == 0,
+ obd_timeout * HZ / 4) ? 0 : -ETIMEDOUT;
+ else
+ rc = l_wait_event_abortable(ns->ns_waitq,
+ atomic_read(&ns->ns_bref) == 0);
/* Forced cleanups should be able to reclaim all references,
* so it's safe to wait forever... we can't leak locks...
diff --git a/drivers/staging/lustre/lustre/llite/dcache.c b/drivers/staging/lustre/lustre/llite/dcache.c
index 549369739d80..11b82c639bfe 100644
--- a/drivers/staging/lustre/lustre/llite/dcache.c
+++ b/drivers/staging/lustre/lustre/llite/dcache.c
@@ -74,6 +74,12 @@ static void ll_release(struct dentry *de)
* an AST before calling d_revalidate_it(). The dentry still exists (marked
* INVALID) so d_lookup() matches it, but we have no lock on it (so
* lock_match() fails) and we spin around real_lookup().
+ *
+ * This race doesn't apply to lookups in d_alloc_parallel(), and for
+ * those we want to ensure that only one dentry with a given name is
+ * in ll_lookup_nd() at a time. So allow invalid dentries to match
+ * while d_in_lookup(). We will be called again when the lookup
+ * completes, and can give a different answer then.
*/
static int ll_dcompare(const struct dentry *dentry,
unsigned int len, const char *str,
@@ -90,7 +96,11 @@ static int ll_dcompare(const struct dentry *dentry,
d_count(dentry));
/* mountpoint is always valid */
- if (d_mountpoint((struct dentry *)dentry))
+ if (d_mountpoint(dentry))
+ return 0;
+
+ /* ensure exclusion against parallel lookup of the same name */
+ if (d_in_lookup((struct dentry *)dentry))
return 0;
if (d_lustre_invalid(dentry))
@@ -111,7 +121,7 @@ static int ll_ddelete(const struct dentry *de)
LASSERT(de);
CDEBUG(D_DENTRY, "%s dentry %pd (%p, parent %p, inode %p) %s%s\n",
- d_lustre_invalid((struct dentry *)de) ? "deleting" : "keeping",
+ d_lustre_invalid(de) ? "deleting" : "keeping",
de, de, de->d_parent, d_inode(de),
d_unhashed(de) ? "" : "hashed,",
list_empty(&de->d_subdirs) ? "" : "subdirs");
@@ -119,7 +129,7 @@ static int ll_ddelete(const struct dentry *de)
/* kernel >= 2.6.38 last refcount is decreased after this function. */
LASSERT(d_count(de) == 1);
- if (d_lustre_invalid((struct dentry *)de))
+ if (d_lustre_invalid(de))
return 1;
return 0;
}
diff --git a/drivers/staging/lustre/lustre/llite/dir.c b/drivers/staging/lustre/lustre/llite/dir.c
index 99b0b77c75f5..d10d27268323 100644
--- a/drivers/staging/lustre/lustre/llite/dir.c
+++ b/drivers/staging/lustre/lustre/llite/dir.c
@@ -885,7 +885,7 @@ static int quotactl_ioctl(struct ll_sb_info *sbi, struct if_quotactl *qctl)
switch (cmd) {
case Q_SETQUOTA:
case Q_SETINFO:
- if (!capable(CFS_CAP_SYS_ADMIN))
+ if (!capable(CAP_SYS_ADMIN))
return -EPERM;
break;
case Q_GETQUOTA:
@@ -893,7 +893,7 @@ static int quotactl_ioctl(struct ll_sb_info *sbi, struct if_quotactl *qctl)
!uid_eq(current_euid(), make_kuid(&init_user_ns, id))) ||
(type == GRPQUOTA &&
!in_egroup_p(make_kgid(&init_user_ns, id)))) &&
- !capable(CFS_CAP_SYS_ADMIN))
+ !capable(CAP_SYS_ADMIN))
return -EPERM;
break;
case Q_GETINFO:
@@ -1452,7 +1452,7 @@ out_quotactl:
}
case OBD_IOC_CHANGELOG_SEND:
case OBD_IOC_CHANGELOG_CLEAR:
- if (!capable(CFS_CAP_SYS_ADMIN))
+ if (!capable(CAP_SYS_ADMIN))
return -EPERM;
rc = copy_and_ioctl(cmd, sbi->ll_md_exp, (void __user *)arg,
@@ -1497,7 +1497,7 @@ out_quotactl:
if (totalsize >= MDS_MAXREQSIZE / 3)
return -E2BIG;
- hur = libcfs_kvzalloc(totalsize, GFP_NOFS);
+ hur = kzalloc(totalsize, GFP_NOFS);
if (!hur)
return -ENOMEM;
@@ -1556,7 +1556,7 @@ out_quotactl:
return rc;
}
case LL_IOC_HSM_CT_START:
- if (!capable(CFS_CAP_SYS_ADMIN))
+ if (!capable(CAP_SYS_ADMIN))
return -EPERM;
rc = copy_and_ioctl(cmd, sbi->ll_md_exp, (void __user *)arg,
diff --git a/drivers/staging/lustre/lustre/llite/file.c b/drivers/staging/lustre/lustre/llite/file.c
index 938b859b6650..ca5faea13b7e 100644
--- a/drivers/staging/lustre/lustre/llite/file.c
+++ b/drivers/staging/lustre/lustre/llite/file.c
@@ -1315,10 +1315,10 @@ static int ll_lov_setea(struct inode *inode, struct file *file,
sizeof(struct lov_user_ost_data);
int rc;
- if (!capable(CFS_CAP_SYS_ADMIN))
+ if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- lump = libcfs_kvzalloc(lum_size, GFP_NOFS);
+ lump = kzalloc(lum_size, GFP_NOFS);
if (!lump)
return -ENOMEM;
@@ -1570,7 +1570,7 @@ int ll_fid2path(struct inode *inode, void __user *arg)
size_t outsize;
int rc;
- if (!capable(CFS_CAP_DAC_READ_SEARCH) &&
+ if (!capable(CAP_DAC_READ_SEARCH) &&
!(ll_i2sbi(inode)->ll_flags & LL_SBI_USER_FID2PATH))
return -EPERM;
@@ -1840,7 +1840,7 @@ int ll_hsm_state_set(struct inode *inode, struct hsm_state_set *hss)
* NOT defined in HSM_USER_MASK.
*/
if (((hss->hss_setmask | hss->hss_clearmask) & ~HSM_USER_MASK) &&
- !capable(CFS_CAP_SYS_ADMIN))
+ !capable(CAP_SYS_ADMIN))
return -EPERM;
/* Detect out-of range archive id */
@@ -2998,7 +2998,7 @@ static int ll_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
num_bytes = sizeof(*fiemap) + (extent_count *
sizeof(struct fiemap_extent));
- fiemap = libcfs_kvzalloc(num_bytes, GFP_NOFS);
+ fiemap = kvzalloc(num_bytes, GFP_KERNEL);
if (!fiemap)
return -ENOMEM;
@@ -3361,7 +3361,7 @@ static int ll_layout_fetch(struct inode *inode, struct ldlm_lock *lock)
goto out;
}
- lvbdata = libcfs_kvzalloc(lmmsize, GFP_NOFS);
+ lvbdata = kvzalloc(lmmsize, GFP_NOFS);
if (!lvbdata) {
rc = -ENOMEM;
goto out;
diff --git a/drivers/staging/lustre/lustre/llite/glimpse.c b/drivers/staging/lustre/lustre/llite/glimpse.c
index c43ac574274c..3075358f3f08 100644
--- a/drivers/staging/lustre/lustre/llite/glimpse.c
+++ b/drivers/staging/lustre/lustre/llite/glimpse.c
@@ -69,7 +69,7 @@ blkcnt_t dirty_cnt(struct inode *inode)
void *results[1];
if (inode->i_mapping)
- cnt += radix_tree_gang_lookup_tag(&inode->i_mapping->page_tree,
+ cnt += radix_tree_gang_lookup_tag(&inode->i_mapping->i_pages,
results, 0, 1,
PAGECACHE_TAG_DIRTY);
if (cnt == 0 && atomic_read(&vob->vob_mmap_cnt) > 0)
diff --git a/drivers/staging/lustre/lustre/llite/llite_internal.h b/drivers/staging/lustre/lustre/llite/llite_internal.h
index f68c2e88f12b..d46bcf71b273 100644
--- a/drivers/staging/lustre/lustre/llite/llite_internal.h
+++ b/drivers/staging/lustre/lustre/llite/llite_internal.h
@@ -1070,8 +1070,8 @@ struct ll_statahead_info {
sai_agl_valid:1,/* AGL is valid for the dir */
sai_in_readpage:1;/* statahead in readdir() */
wait_queue_head_t sai_waitq; /* stat-ahead wait queue */
- struct ptlrpc_thread sai_thread; /* stat-ahead thread */
- struct ptlrpc_thread sai_agl_thread; /* AGL thread */
+ struct task_struct *sai_task; /* stat-ahead thread */
+ struct task_struct *sai_agl_task; /* AGL thread */
struct list_head sai_interim_entries; /* entries which got async
* stat reply, but not
* instantiated
diff --git a/drivers/staging/lustre/lustre/llite/llite_lib.c b/drivers/staging/lustre/lustre/llite/llite_lib.c
index 6735a6f006d2..e7500c53fafc 100644
--- a/drivers/staging/lustre/lustre/llite/llite_lib.c
+++ b/drivers/staging/lustre/lustre/llite/llite_lib.c
@@ -879,9 +879,15 @@ int ll_fill_super(struct super_block *sb)
CDEBUG(D_VFSTRACE, "VFS Op: sb %p\n", sb);
+ err = ptlrpc_inc_ref();
+ if (err)
+ return err;
+
cfg = kzalloc(sizeof(*cfg), GFP_NOFS);
- if (!cfg)
- return -ENOMEM;
+ if (!cfg) {
+ err = -ENOMEM;
+ goto out_put;
+ }
try_module_get(THIS_MODULE);
@@ -891,7 +897,8 @@ int ll_fill_super(struct super_block *sb)
if (!sbi) {
module_put(THIS_MODULE);
kfree(cfg);
- return -ENOMEM;
+ err = -ENOMEM;
+ goto out_put;
}
err = ll_options(lsi->lsi_lmd->lmd_opts, &sbi->ll_flags);
@@ -958,6 +965,9 @@ out_free:
LCONSOLE_WARN("Mounted %s\n", profilenm);
kfree(cfg);
+out_put:
+ if (err)
+ ptlrpc_dec_ref();
return err;
} /* ll_fill_super */
@@ -986,16 +996,12 @@ void ll_put_super(struct super_block *sb)
}
/* Wait for unstable pages to be committed to stable storage */
- if (!force) {
- struct l_wait_info lwi = LWI_INTR(LWI_ON_SIGNAL_NOOP, NULL);
-
- rc = l_wait_event(sbi->ll_cache->ccc_unstable_waitq,
- !atomic_long_read(&sbi->ll_cache->ccc_unstable_nr),
- &lwi);
- }
+ if (!force)
+ rc = l_wait_event_abortable(sbi->ll_cache->ccc_unstable_waitq,
+ !atomic_long_read(&sbi->ll_cache->ccc_unstable_nr));
ccc_count = atomic_long_read(&sbi->ll_cache->ccc_unstable_nr);
- if (!force && rc != -EINTR)
+ if (!force && rc != -ERESTARTSYS)
LASSERTF(!ccc_count, "count: %li\n", ccc_count);
/* We need to set force before the lov_disconnect in
@@ -1032,6 +1038,8 @@ void ll_put_super(struct super_block *sb)
cl_env_cache_purge(~0);
module_put(THIS_MODULE);
+
+ ptlrpc_dec_ref();
} /* client_put_super */
struct inode *ll_inode_from_resource_lock(struct ldlm_lock *lock)
@@ -1197,13 +1205,12 @@ static int ll_update_lsm_md(struct inode *inode, struct lustre_md *md)
lmv_free_memmd(lli->lli_lsm_md);
lli->lli_lsm_md = NULL;
return 0;
- } else {
- /*
- * The lustre_md from req does not include stripeEA,
- * see ll_md_setattr
- */
- return 0;
}
+ /*
+ * The lustre_md from req does not include stripeEA,
+ * see ll_md_setattr
+ */
+ return 0;
}
/* set the directory layout */
@@ -1454,7 +1461,7 @@ int ll_setattr_raw(struct dentry *dentry, struct iattr *attr, bool hsm_import)
/* POSIX: check before ATTR_*TIME_SET set (from setattr_prepare) */
if (attr->ia_valid & TIMES_SET_FLAGS) {
if ((!uid_eq(current_fsuid(), inode->i_uid)) &&
- !capable(CFS_CAP_FOWNER))
+ !capable(CAP_FOWNER))
return -EPERM;
}
@@ -1988,8 +1995,7 @@ void ll_umount_begin(struct super_block *sb)
struct ll_sb_info *sbi = ll_s2sbi(sb);
struct obd_device *obd;
struct obd_ioctl_data *ioc_data;
- wait_queue_head_t waitq;
- struct l_wait_info lwi;
+ int cnt = 0;
CDEBUG(D_VFSTRACE, "VFS Op: superblock %p count %d active %d\n", sb,
sb->s_count, atomic_read(&sb->s_active));
@@ -2025,10 +2031,10 @@ void ll_umount_begin(struct super_block *sb)
* and then continue. For now, we just periodically checking for vfs
* to decrement mnt_cnt and hope to finish it within 10sec.
*/
- init_waitqueue_head(&waitq);
- lwi = LWI_TIMEOUT_INTERVAL(cfs_time_seconds(10),
- cfs_time_seconds(1), NULL, NULL);
- l_wait_event(waitq, may_umount(sbi->ll_mnt.mnt), &lwi);
+ while (cnt < 10 && !may_umount(sbi->ll_mnt.mnt)) {
+ schedule_timeout_uninterruptible(HZ);
+ cnt++;
+ }
schedule();
}
@@ -2143,7 +2149,7 @@ int ll_prep_inode(struct inode **inode, struct ptlrpc_request *req,
md.posix_acl = NULL;
}
#endif
- rc = -ENOMEM;
+ rc = PTR_ERR(*inode);
CERROR("new_inode -fatal: rc %d\n", rc);
goto out;
}
@@ -2602,7 +2608,7 @@ int ll_getparent(struct file *file, struct getparent __user *arg)
u32 linkno;
int rc;
- if (!capable(CFS_CAP_DAC_READ_SEARCH) &&
+ if (!capable(CAP_DAC_READ_SEARCH) &&
!(ll_i2sbi(inode)->ll_flags & LL_SBI_USER_FID2PATH))
return -EPERM;
@@ -2653,7 +2659,7 @@ int ll_getparent(struct file *file, struct getparent __user *arg)
}
lb_free:
- lu_buf_free(&buf);
+ kvfree(buf.lb_buf);
ldata_free:
kfree(ldata);
return rc;
diff --git a/drivers/staging/lustre/lustre/llite/llite_mmap.c b/drivers/staging/lustre/lustre/llite/llite_mmap.c
index c0533bd6f352..214b07554e62 100644
--- a/drivers/staging/lustre/lustre/llite/llite_mmap.c
+++ b/drivers/staging/lustre/lustre/llite/llite_mmap.c
@@ -177,14 +177,14 @@ static int ll_page_mkwrite0(struct vm_area_struct *vma, struct page *vmpage,
vio->u.fault.ft_vma = vma;
vio->u.fault.ft_vmpage = vmpage;
- set = cfs_block_sigsinv(sigmask(SIGKILL) | sigmask(SIGTERM));
+ cfs_block_sigsinv(sigmask(SIGKILL) | sigmask(SIGTERM), &set);
inode = vvp_object_inode(io->ci_obj);
lli = ll_i2info(inode);
result = cl_io_loop(env, io);
- cfs_restore_sigs(set);
+ cfs_restore_sigs(&set);
if (result == 0) {
struct inode *inode = file_inode(vma->vm_file);
@@ -334,7 +334,7 @@ static int ll_fault(struct vm_fault *vmf)
* so that it can be killed by admin but not cause segfault by
* other signals.
*/
- set = cfs_block_sigsinv(sigmask(SIGKILL) | sigmask(SIGTERM));
+ cfs_block_sigsinv(sigmask(SIGKILL) | sigmask(SIGTERM), &set);
restart:
result = ll_fault0(vmf->vma, vmf);
@@ -360,7 +360,7 @@ restart:
result = VM_FAULT_LOCKED;
}
- cfs_restore_sigs(set);
+ cfs_restore_sigs(&set);
return result;
}
diff --git a/drivers/staging/lustre/lustre/llite/namei.c b/drivers/staging/lustre/lustre/llite/namei.c
index a2687f46a16d..6c9ec462eb41 100644
--- a/drivers/staging/lustre/lustre/llite/namei.c
+++ b/drivers/staging/lustre/lustre/llite/namei.c
@@ -380,52 +380,45 @@ void ll_i2gids(__u32 *suppgids, struct inode *i1, struct inode *i2)
}
/*
- * try to reuse three types of dentry:
- * 1. unhashed alias, this one is unhashed by d_invalidate (but it may be valid
- * by concurrent .revalidate).
- * 2. INVALID alias (common case for no valid ldlm lock held, but this flag may
- * be cleared by others calling d_lustre_revalidate).
- * 3. DISCONNECTED alias.
+ * Try to reuse unhashed or invalidated dentries.
+ * This is very similar to d_exact_alias(), and any changes in one should be
+ * considered for inclusion in the other. The differences are that we don't
+ * need an unhashed alias, and we don't want d_compare to be used for
+ * comparison.
*/
static struct dentry *ll_find_alias(struct inode *inode, struct dentry *dentry)
{
- struct dentry *alias, *discon_alias, *invalid_alias;
+ struct dentry *alias;
if (hlist_empty(&inode->i_dentry))
return NULL;
- discon_alias = NULL;
- invalid_alias = NULL;
-
spin_lock(&inode->i_lock);
hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) {
LASSERT(alias != dentry);
+ /*
+ * Don't need alias->d_lock here, because aliases with
+ * d_parent == entry->d_parent are not subject to name or
+ * parent changes, because the parent inode i_mutex is held.
+ */
- spin_lock(&alias->d_lock);
- if ((alias->d_flags & DCACHE_DISCONNECTED) &&
- S_ISDIR(inode->i_mode))
- /* LASSERT(last_discon == NULL); LU-405, bz 20055 */
- discon_alias = alias;
- else if (alias->d_parent == dentry->d_parent &&
- alias->d_name.hash == dentry->d_name.hash &&
- alias->d_name.len == dentry->d_name.len &&
- memcmp(alias->d_name.name, dentry->d_name.name,
- dentry->d_name.len) == 0)
- invalid_alias = alias;
- spin_unlock(&alias->d_lock);
-
- if (invalid_alias)
- break;
- }
- alias = invalid_alias ?: discon_alias ?: NULL;
- if (alias) {
+ if (alias->d_parent != dentry->d_parent)
+ continue;
+ if (alias->d_name.hash != dentry->d_name.hash)
+ continue;
+ if (alias->d_name.len != dentry->d_name.len ||
+ memcmp(alias->d_name.name, dentry->d_name.name,
+ dentry->d_name.len) != 0)
+ continue;
spin_lock(&alias->d_lock);
dget_dlock(alias);
spin_unlock(&alias->d_lock);
+ spin_unlock(&inode->i_lock);
+ return alias;
}
spin_unlock(&inode->i_lock);
- return alias;
+ return NULL;
}
/*
@@ -434,7 +427,7 @@ static struct dentry *ll_find_alias(struct inode *inode, struct dentry *dentry)
*/
struct dentry *ll_splice_alias(struct inode *inode, struct dentry *de)
{
- if (inode) {
+ if (inode && !S_ISDIR(inode->i_mode)) {
struct dentry *new = ll_find_alias(inode, de);
if (new) {
@@ -445,8 +438,13 @@ struct dentry *ll_splice_alias(struct inode *inode, struct dentry *de)
new, d_inode(new), d_count(new), new->d_flags);
return new;
}
+ d_add(de, inode);
+ } else {
+ struct dentry *new = d_splice_alias(inode, de);
+
+ if (new)
+ de = new;
}
- d_add(de, inode);
CDEBUG(D_DENTRY, "Add dentry %p inode %p refc %d flags %#x\n",
de, d_inode(de), d_count(de), de->d_flags);
return de;
diff --git a/drivers/staging/lustre/lustre/llite/statahead.c b/drivers/staging/lustre/lustre/llite/statahead.c
index 90c7324575e4..155ce3cf6f60 100644
--- a/drivers/staging/lustre/lustre/llite/statahead.c
+++ b/drivers/staging/lustre/lustre/llite/statahead.c
@@ -267,7 +267,7 @@ sa_kill(struct ll_statahead_info *sai, struct sa_entry *entry)
/* called by scanner after use, sa_entry will be killed */
static void
-sa_put(struct ll_statahead_info *sai, struct sa_entry *entry)
+sa_put(struct ll_statahead_info *sai, struct sa_entry *entry, struct ll_inode_info *lli)
{
struct sa_entry *tmp, *next;
@@ -295,7 +295,11 @@ sa_put(struct ll_statahead_info *sai, struct sa_entry *entry)
sa_kill(sai, tmp);
}
- wake_up(&sai->sai_thread.t_ctl_waitq);
+ spin_lock(&lli->lli_sa_lock);
+ if (sai->sai_task)
+ wake_up_process(sai->sai_task);
+ spin_unlock(&lli->lli_sa_lock);
+
}
/*
@@ -383,7 +387,7 @@ static void ll_agl_add(struct ll_statahead_info *sai,
}
if (added > 0)
- wake_up(&sai->sai_agl_thread.t_ctl_waitq);
+ wake_up_process(sai->sai_agl_task);
}
/* allocate sai */
@@ -403,8 +407,6 @@ static struct ll_statahead_info *ll_sai_alloc(struct dentry *dentry)
sai->sai_max = LL_SA_RPC_MIN;
sai->sai_index = 1;
init_waitqueue_head(&sai->sai_waitq);
- init_waitqueue_head(&sai->sai_thread.t_ctl_waitq);
- init_waitqueue_head(&sai->sai_agl_thread.t_ctl_waitq);
INIT_LIST_HEAD(&sai->sai_interim_entries);
INIT_LIST_HEAD(&sai->sai_entries);
@@ -466,8 +468,8 @@ static void ll_sai_put(struct ll_statahead_info *sai)
lli->lli_sai = NULL;
spin_unlock(&lli->lli_sa_lock);
- LASSERT(thread_is_stopped(&sai->sai_thread));
- LASSERT(thread_is_stopped(&sai->sai_agl_thread));
+ LASSERT(sai->sai_task == NULL);
+ LASSERT(sai->sai_agl_task == NULL);
LASSERT(sai->sai_sent == sai->sai_replied);
LASSERT(!sa_has_callback(sai));
@@ -647,7 +649,6 @@ static int ll_statahead_interpret(struct ptlrpc_request *req,
struct ll_inode_info *lli = ll_i2info(dir);
struct ll_statahead_info *sai = lli->lli_sai;
struct sa_entry *entry = (struct sa_entry *)minfo->mi_cbdata;
- wait_queue_head_t *waitq = NULL;
__u64 handle = 0;
if (it_disposition(it, DISP_LOOKUP_NEG))
@@ -658,7 +659,6 @@ static int ll_statahead_interpret(struct ptlrpc_request *req,
* sai should be always valid, no need to refcount
*/
LASSERT(sai);
- LASSERT(!thread_is_stopped(&sai->sai_thread));
LASSERT(entry);
CDEBUG(D_READA, "sa_entry %.*s rc %d\n",
@@ -682,8 +682,9 @@ static int ll_statahead_interpret(struct ptlrpc_request *req,
spin_lock(&lli->lli_sa_lock);
if (rc) {
if (__sa_make_ready(sai, entry, rc))
- waitq = &sai->sai_waitq;
+ wake_up(&sai->sai_waitq);
} else {
+ int first = 0;
entry->se_minfo = minfo;
entry->se_req = ptlrpc_request_addref(req);
/*
@@ -694,14 +695,15 @@ static int ll_statahead_interpret(struct ptlrpc_request *req,
*/
entry->se_handle = handle;
if (!sa_has_callback(sai))
- waitq = &sai->sai_thread.t_ctl_waitq;
+ first = 1;
list_add_tail(&entry->se_list, &sai->sai_interim_entries);
+
+ if (first && sai->sai_task)
+ wake_up_process(sai->sai_task);
}
sai->sai_replied++;
- if (waitq)
- wake_up(waitq);
spin_unlock(&lli->lli_sa_lock);
return rc;
@@ -861,37 +863,13 @@ static int ll_agl_thread(void *arg)
struct inode *dir = d_inode(parent);
struct ll_inode_info *plli = ll_i2info(dir);
struct ll_inode_info *clli;
- struct ll_sb_info *sbi = ll_i2sbi(dir);
- struct ll_statahead_info *sai;
- struct ptlrpc_thread *thread;
- struct l_wait_info lwi = { 0 };
+ /* We already own this reference, so it is safe to take it without a lock. */
+ struct ll_statahead_info *sai = plli->lli_sai;
- sai = ll_sai_get(dir);
- thread = &sai->sai_agl_thread;
- thread->t_pid = current_pid();
CDEBUG(D_READA, "agl thread started: sai %p, parent %pd\n",
sai, parent);
- atomic_inc(&sbi->ll_agl_total);
- spin_lock(&plli->lli_agl_lock);
- sai->sai_agl_valid = 1;
- if (thread_is_init(thread))
- /* If someone else has changed the thread state
- * (e.g. already changed to SVC_STOPPING), we can't just
- * blindly overwrite that setting.
- */
- thread_set_flags(thread, SVC_RUNNING);
- spin_unlock(&plli->lli_agl_lock);
- wake_up(&thread->t_ctl_waitq);
-
- while (1) {
- l_wait_event(thread->t_ctl_waitq,
- !list_empty(&sai->sai_agls) ||
- !thread_is_running(thread),
- &lwi);
-
- if (!thread_is_running(thread))
- break;
+ while (!kthread_should_stop()) {
spin_lock(&plli->lli_agl_lock);
/* The statahead thread maybe help to process AGL entries,
@@ -906,6 +884,12 @@ static int ll_agl_thread(void *arg)
} else {
spin_unlock(&plli->lli_agl_lock);
}
+
+ set_current_state(TASK_IDLE);
+ if (list_empty(&sai->sai_agls) &&
+ !kthread_should_stop())
+ schedule();
+ __set_current_state(TASK_RUNNING);
}
spin_lock(&plli->lli_agl_lock);
@@ -919,20 +903,16 @@ static int ll_agl_thread(void *arg)
iput(&clli->lli_vfs_inode);
spin_lock(&plli->lli_agl_lock);
}
- thread_set_flags(thread, SVC_STOPPED);
spin_unlock(&plli->lli_agl_lock);
- wake_up(&thread->t_ctl_waitq);
- ll_sai_put(sai);
CDEBUG(D_READA, "agl thread stopped: sai %p, parent %pd\n",
sai, parent);
+ ll_sai_put(sai);
return 0;
}
/* start agl thread */
static void ll_start_agl(struct dentry *parent, struct ll_statahead_info *sai)
{
- struct ptlrpc_thread *thread = &sai->sai_agl_thread;
- struct l_wait_info lwi = { 0 };
struct ll_inode_info *plli;
struct task_struct *task;
@@ -940,17 +920,22 @@ static void ll_start_agl(struct dentry *parent, struct ll_statahead_info *sai)
sai, parent);
plli = ll_i2info(d_inode(parent));
- task = kthread_run(ll_agl_thread, parent, "ll_agl_%u",
- plli->lli_opendir_pid);
+ task = kthread_create(ll_agl_thread, parent, "ll_agl_%u",
+ plli->lli_opendir_pid);
if (IS_ERR(task)) {
CERROR("can't start ll_agl thread, rc: %ld\n", PTR_ERR(task));
- thread_set_flags(thread, SVC_STOPPED);
return;
}
- l_wait_event(thread->t_ctl_waitq,
- thread_is_running(thread) || thread_is_stopped(thread),
- &lwi);
+ sai->sai_agl_task = task;
+ atomic_inc(&ll_i2sbi(d_inode(parent))->ll_agl_total);
+ spin_lock(&plli->lli_agl_lock);
+ sai->sai_agl_valid = 1;
+ spin_unlock(&plli->lli_agl_lock);
+ /* Get an extra reference that the thread holds */
+ ll_sai_get(d_inode(parent));
+
+ wake_up_process(task);
}
/* statahead thread main function */
@@ -960,20 +945,13 @@ static int ll_statahead_thread(void *arg)
struct inode *dir = d_inode(parent);
struct ll_inode_info *lli = ll_i2info(dir);
struct ll_sb_info *sbi = ll_i2sbi(dir);
- struct ll_statahead_info *sai;
- struct ptlrpc_thread *sa_thread;
- struct ptlrpc_thread *agl_thread;
+ struct ll_statahead_info *sai = lli->lli_sai;
struct page *page = NULL;
__u64 pos = 0;
int first = 0;
int rc = 0;
struct md_op_data *op_data;
- struct l_wait_info lwi = { 0 };
- sai = ll_sai_get(dir);
- sa_thread = &sai->sai_thread;
- agl_thread = &sai->sai_agl_thread;
- sa_thread->t_pid = current_pid();
CDEBUG(D_READA, "statahead thread starting: sai %p, parent %pd\n",
sai, parent);
@@ -986,21 +964,7 @@ static int ll_statahead_thread(void *arg)
op_data->op_max_pages = ll_i2sbi(dir)->ll_md_brw_pages;
- if (sbi->ll_flags & LL_SBI_AGL_ENABLED)
- ll_start_agl(parent, sai);
-
- atomic_inc(&sbi->ll_sa_total);
- spin_lock(&lli->lli_sa_lock);
- if (thread_is_init(sa_thread))
- /* If someone else has changed the thread state
- * (e.g. already changed to SVC_STOPPING), we can't just
- * blindly overwrite that setting.
- */
- thread_set_flags(sa_thread, SVC_RUNNING);
- spin_unlock(&lli->lli_sa_lock);
- wake_up(&sa_thread->t_ctl_waitq);
-
- while (pos != MDS_DIR_END_OFF && thread_is_running(sa_thread)) {
+ while (pos != MDS_DIR_END_OFF && sai->sai_task) {
struct lu_dirpage *dp;
struct lu_dirent *ent;
@@ -1017,7 +981,7 @@ static int ll_statahead_thread(void *arg)
dp = page_address(page);
for (ent = lu_dirent_start(dp);
- ent && thread_is_running(sa_thread) && !sa_low_hit(sai);
+ ent && sai->sai_task && !sa_low_hit(sai);
ent = lu_dirent_next(ent)) {
struct lu_fid fid;
__u64 hash;
@@ -1067,14 +1031,7 @@ static int ll_statahead_thread(void *arg)
fid_le_to_cpu(&fid, &ent->lde_fid);
- /* wait for spare statahead window */
do {
- l_wait_event(sa_thread->t_ctl_waitq,
- !sa_sent_full(sai) ||
- sa_has_callback(sai) ||
- !list_empty(&sai->sai_agls) ||
- !thread_is_running(sa_thread),
- &lwi);
sa_handle_callback(sai);
spin_lock(&lli->lli_agl_lock);
@@ -1094,8 +1051,16 @@ static int ll_statahead_thread(void *arg)
spin_lock(&lli->lli_agl_lock);
}
spin_unlock(&lli->lli_agl_lock);
- } while (sa_sent_full(sai) &&
- thread_is_running(sa_thread));
+
+ set_current_state(TASK_IDLE);
+ if (sa_sent_full(sai) &&
+ !sa_has_callback(sai) &&
+ agl_list_empty(sai) &&
+ sai->sai_task)
+ /* wait for spare statahead window */
+ schedule();
+ __set_current_state(TASK_RUNNING);
+ } while (sa_sent_full(sai) && sai->sai_task);
sa_statahead(parent, name, namelen, &fid);
}
@@ -1118,7 +1083,7 @@ static int ll_statahead_thread(void *arg)
if (rc < 0) {
spin_lock(&lli->lli_sa_lock);
- thread_set_flags(sa_thread, SVC_STOPPING);
+ sai->sai_task = NULL;
lli->lli_sa_enabled = 0;
spin_unlock(&lli->lli_sa_lock);
}
@@ -1127,59 +1092,46 @@ static int ll_statahead_thread(void *arg)
* statahead is finished, but statahead entries need to be cached, wait
* for file release to stop me.
*/
- while (thread_is_running(sa_thread)) {
- l_wait_event(sa_thread->t_ctl_waitq,
- sa_has_callback(sai) ||
- !agl_list_empty(sai) ||
- !thread_is_running(sa_thread),
- &lwi);
-
+ while (sai->sai_task) {
sa_handle_callback(sai);
+
+ set_current_state(TASK_IDLE);
+ if (!sa_has_callback(sai) &&
+ sai->sai_task)
+ schedule();
+ __set_current_state(TASK_RUNNING);
}
out:
- if (sai->sai_agl_valid) {
- spin_lock(&lli->lli_agl_lock);
- thread_set_flags(agl_thread, SVC_STOPPING);
- spin_unlock(&lli->lli_agl_lock);
- wake_up(&agl_thread->t_ctl_waitq);
+ if (sai->sai_agl_task) {
+ kthread_stop(sai->sai_agl_task);
CDEBUG(D_READA, "stop agl thread: sai %p pid %u\n",
- sai, (unsigned int)agl_thread->t_pid);
- l_wait_event(agl_thread->t_ctl_waitq,
- thread_is_stopped(agl_thread),
- &lwi);
- } else {
- /* Set agl_thread flags anyway. */
- thread_set_flags(agl_thread, SVC_STOPPED);
+ sai, (unsigned int)sai->sai_agl_task->pid);
+ sai->sai_agl_task = NULL;
}
-
/*
* wait for inflight statahead RPCs to finish, and then we can free sai
* safely because statahead RPC will access sai data
*/
while (sai->sai_sent != sai->sai_replied) {
/* in case we're not woken up, timeout wait */
- lwi = LWI_TIMEOUT(msecs_to_jiffies(MSEC_PER_SEC >> 3),
- NULL, NULL);
- l_wait_event(sa_thread->t_ctl_waitq,
- sai->sai_sent == sai->sai_replied, &lwi);
+ schedule_timeout_idle(HZ>>3);
}
/* release resources held by statahead RPCs */
sa_handle_callback(sai);
- spin_lock(&lli->lli_sa_lock);
- thread_set_flags(sa_thread, SVC_STOPPED);
- spin_unlock(&lli->lli_sa_lock);
-
CDEBUG(D_READA, "statahead thread stopped: sai %p, parent %pd\n",
sai, parent);
+ spin_lock(&lli->lli_sa_lock);
+ sai->sai_task = NULL;
+ spin_unlock(&lli->lli_sa_lock);
+
wake_up(&sai->sai_waitq);
- wake_up(&sa_thread->t_ctl_waitq);
ll_sai_put(sai);
- return rc;
+ do_exit(rc);
}
/* authorize opened dir handle @key to statahead */
@@ -1221,13 +1173,13 @@ void ll_deauthorize_statahead(struct inode *dir, void *key)
lli->lli_opendir_pid = 0;
lli->lli_sa_enabled = 0;
sai = lli->lli_sai;
- if (sai && thread_is_running(&sai->sai_thread)) {
+ if (sai && sai->sai_task) {
/*
* statahead thread may not quit yet because it needs to cache
* entries, now it's time to tell it to quit.
*/
- thread_set_flags(&sai->sai_thread, SVC_STOPPING);
- wake_up(&sai->sai_thread.t_ctl_waitq);
+ wake_up_process(sai->sai_task);
+ sai->sai_task = NULL;
}
spin_unlock(&lli->lli_sa_lock);
}
@@ -1382,7 +1334,6 @@ static int revalidate_statahead_dentry(struct inode *dir,
{
struct ll_inode_info *lli = ll_i2info(dir);
struct sa_entry *entry = NULL;
- struct l_wait_info lwi = { 0 };
struct ll_dentry_data *ldd;
int rc = 0;
@@ -1432,10 +1383,8 @@ static int revalidate_statahead_dentry(struct inode *dir,
spin_lock(&lli->lli_sa_lock);
sai->sai_index_wait = entry->se_index;
spin_unlock(&lli->lli_sa_lock);
- lwi = LWI_TIMEOUT_INTR(cfs_time_seconds(30), NULL,
- LWI_ON_SIGNAL_NOOP, NULL);
- rc = l_wait_event(sai->sai_waitq, sa_ready(entry), &lwi);
- if (rc < 0) {
+ if (0 == wait_event_idle_timeout(sai->sai_waitq,
+ sa_ready(entry), 30 * HZ)) {
/*
* entry may not be ready, so it may be used by inflight
* statahead RPC, don't free it.
@@ -1500,7 +1449,7 @@ out_unplug:
*/
ldd = ll_d2d(*dentryp);
ldd->lld_sa_generation = lli->lli_sa_generation;
- sa_put(sai, entry);
+ sa_put(sai, entry, lli);
return rc;
}
@@ -1520,8 +1469,6 @@ static int start_statahead_thread(struct inode *dir, struct dentry *dentry)
{
struct ll_inode_info *lli = ll_i2info(dir);
struct ll_statahead_info *sai = NULL;
- struct l_wait_info lwi = { 0 };
- struct ptlrpc_thread *thread;
struct task_struct *task;
struct dentry *parent = dentry->d_parent;
int rc;
@@ -1561,19 +1508,21 @@ static int start_statahead_thread(struct inode *dir, struct dentry *dentry)
CDEBUG(D_READA, "start statahead thread: [pid %d] [parent %pd]\n",
current_pid(), parent);
- task = kthread_run(ll_statahead_thread, parent, "ll_sa_%u",
- lli->lli_opendir_pid);
- thread = &sai->sai_thread;
+ task = kthread_create(ll_statahead_thread, parent, "ll_sa_%u",
+ lli->lli_opendir_pid);
if (IS_ERR(task)) {
rc = PTR_ERR(task);
CERROR("can't start ll_sa thread, rc : %d\n", rc);
goto out;
}
- l_wait_event(thread->t_ctl_waitq,
- thread_is_running(thread) || thread_is_stopped(thread),
- &lwi);
- ll_sai_put(sai);
+ if (ll_i2sbi(parent->d_inode)->ll_flags & LL_SBI_AGL_ENABLED)
+ ll_start_agl(parent, sai);
+
+ atomic_inc(&ll_i2sbi(parent->d_inode)->ll_sa_total);
+ sai->sai_task = task;
+
+ wake_up_process(task);
/*
* We don't stat-ahead for the first dirent since we are already in
diff --git a/drivers/staging/lustre/lustre/llite/super25.c b/drivers/staging/lustre/lustre/llite/super25.c
index 9b0bb3541a84..861e7a60f408 100644
--- a/drivers/staging/lustre/lustre/llite/super25.c
+++ b/drivers/staging/lustre/lustre/llite/super25.c
@@ -85,8 +85,7 @@ MODULE_ALIAS_FS("lustre");
static int __init lustre_init(void)
{
- struct lnet_process_id lnet_id;
- int i, rc;
+ int rc;
BUILD_BUG_ON(sizeof(LUSTRE_VOLATILE_HDR) !=
LUSTRE_VOLATILE_HDR_LEN + 1);
@@ -125,20 +124,6 @@ static int __init lustre_init(void)
goto out_debugfs;
}
- /* Nodes with small feet have little entropy. The NID for this
- * node gives the most entropy in the low bits
- */
- for (i = 0;; i++) {
- u32 seed;
-
- if (LNetGetId(i, &lnet_id) == -ENOENT)
- break;
- if (LNET_NETTYP(LNET_NIDNET(lnet_id.nid)) != LOLND) {
- seed = LNET_NIDADDR(lnet_id.nid);
- add_device_randomness(&seed, sizeof(seed));
- }
- }
-
rc = vvp_global_init();
if (rc != 0)
goto out_sysfs;
diff --git a/drivers/staging/lustre/lustre/llite/xattr.c b/drivers/staging/lustre/lustre/llite/xattr.c
index 532384c91447..2d78432963dc 100644
--- a/drivers/staging/lustre/lustre/llite/xattr.c
+++ b/drivers/staging/lustre/lustre/llite/xattr.c
@@ -75,7 +75,7 @@ static int xattr_type_filter(struct ll_sb_info *sbi,
return -EOPNOTSUPP;
if (handler->flags == XATTR_TRUSTED_T &&
- !capable(CFS_CAP_SYS_ADMIN))
+ !capable(CAP_SYS_ADMIN))
return -EPERM;
return 0;
@@ -87,10 +87,10 @@ ll_xattr_set_common(const struct xattr_handler *handler,
const char *name, const void *value, size_t size,
int flags)
{
- char fullname[strlen(handler->prefix) + strlen(name) + 1];
struct ll_sb_info *sbi = ll_i2sbi(inode);
struct ptlrpc_request *req = NULL;
const char *pv = value;
+ char *fullname;
__u64 valid;
int rc;
@@ -141,10 +141,13 @@ ll_xattr_set_common(const struct xattr_handler *handler,
return -EPERM;
}
- sprintf(fullname, "%s%s\n", handler->prefix, name);
+ fullname = kasprintf(GFP_KERNEL, "%s%s\n", handler->prefix, name);
+ if (!fullname)
+ return -ENOMEM;
rc = md_setxattr(sbi->ll_md_exp, ll_inode2fid(inode),
valid, fullname, pv, size, 0, flags,
ll_i2suppgid(inode), &req);
+ kfree(fullname);
if (rc) {
if (rc == -EOPNOTSUPP && handler->flags == XATTR_USER_T) {
LCONSOLE_INFO("Disabling user_xattr feature because it is not supported on the server\n");
@@ -364,11 +367,11 @@ static int ll_xattr_get_common(const struct xattr_handler *handler,
struct dentry *dentry, struct inode *inode,
const char *name, void *buffer, size_t size)
{
- char fullname[strlen(handler->prefix) + strlen(name) + 1];
struct ll_sb_info *sbi = ll_i2sbi(inode);
#ifdef CONFIG_FS_POSIX_ACL
struct ll_inode_info *lli = ll_i2info(inode);
#endif
+ char *fullname;
int rc;
CDEBUG(D_VFSTRACE, "VFS Op:inode=" DFID "(%p)\n",
@@ -411,9 +414,13 @@ static int ll_xattr_get_common(const struct xattr_handler *handler,
if (handler->flags == XATTR_ACL_DEFAULT_T && !S_ISDIR(inode->i_mode))
return -ENODATA;
#endif
- sprintf(fullname, "%s%s\n", handler->prefix, name);
- return ll_xattr_list(inode, fullname, handler->flags, buffer, size,
- OBD_MD_FLXATTR);
+ fullname = kasprintf(GFP_KERNEL, "%s%s\n", handler->prefix, name);
+ if (!fullname)
+ return -ENOMEM;
+ rc = ll_xattr_list(inode, fullname, handler->flags, buffer, size,
+ OBD_MD_FLXATTR);
+ kfree(fullname);
+ return rc;
}
static ssize_t ll_getxattr_lov(struct inode *inode, void *buf, size_t buf_size)
diff --git a/drivers/staging/lustre/lustre/lmv/lmv_obd.c b/drivers/staging/lustre/lustre/lmv/lmv_obd.c
index c2c57f65431e..e8a9b9902c37 100644
--- a/drivers/staging/lustre/lustre/lmv/lmv_obd.c
+++ b/drivers/staging/lustre/lustre/lmv/lmv_obd.c
@@ -1035,7 +1035,7 @@ static int lmv_iocontrol(unsigned int cmd, struct obd_export *exp,
reqlen = offsetof(typeof(*hur),
hur_user_item[nr])
+ hur->hur_request.hr_data_len;
- req = libcfs_kvzalloc(reqlen, GFP_NOFS);
+ req = kvzalloc(reqlen, GFP_NOFS);
if (!req)
return -ENOMEM;
@@ -2695,7 +2695,7 @@ static int lmv_unpackmd(struct obd_export *exp, struct lmv_stripe_md **lsmp,
if (lsm && !lmm) {
int i;
- for (i = 1; i < lsm->lsm_md_stripe_count; i++) {
+ for (i = 0; i < lsm->lsm_md_stripe_count; i++) {
/*
* For migrating inode, the master stripe and master
* object will be the same, so do not need iput, see
@@ -2733,7 +2733,7 @@ static int lmv_unpackmd(struct obd_export *exp, struct lmv_stripe_md **lsmp,
lsm_size = lmv_stripe_md_size(0);
if (!lsm) {
- lsm = libcfs_kvzalloc(lsm_size, GFP_NOFS);
+ lsm = kvzalloc(lsm_size, GFP_NOFS);
if (!lsm)
return -ENOMEM;
allocated = true;
diff --git a/drivers/staging/lustre/lustre/lov/lov_ea.c b/drivers/staging/lustre/lustre/lov/lov_ea.c
index d563dd73343a..c56a971745e8 100644
--- a/drivers/staging/lustre/lustre/lov/lov_ea.c
+++ b/drivers/staging/lustre/lustre/lov/lov_ea.c
@@ -89,7 +89,7 @@ struct lov_stripe_md *lsm_alloc_plain(u16 stripe_count)
oinfo_ptrs_size = sizeof(struct lov_oinfo *) * stripe_count;
lsm_size = sizeof(*lsm) + oinfo_ptrs_size;
- lsm = libcfs_kvzalloc(lsm_size, GFP_NOFS);
+ lsm = kvzalloc(lsm_size, GFP_NOFS);
if (!lsm)
return NULL;
diff --git a/drivers/staging/lustre/lustre/lov/lov_io.c b/drivers/staging/lustre/lustre/lov/lov_io.c
index c5f5d1b106dc..b823f8a21856 100644
--- a/drivers/staging/lustre/lustre/lov/lov_io.c
+++ b/drivers/staging/lustre/lustre/lov/lov_io.c
@@ -243,7 +243,7 @@ static int lov_io_subio_init(const struct lu_env *env, struct lov_io *lio,
* when writing a page. -jay
*/
lio->lis_subs =
- libcfs_kvzalloc(lsm->lsm_stripe_count *
+ kvzalloc(lsm->lsm_stripe_count *
sizeof(lio->lis_subs[0]),
GFP_NOFS);
if (lio->lis_subs) {
@@ -483,7 +483,7 @@ lov_io_data_version_end(const struct lu_env *env, const struct cl_io_slice *ios)
struct lov_io_sub *sub;
list_for_each_entry(sub, &lio->lis_active, sub_linkage) {
- lov_io_end_wrapper(env, sub->sub_io);
+ lov_io_end_wrapper(sub->sub_env, sub->sub_io);
parent->u.ci_data_version.dv_data_version +=
sub->sub_io->u.ci_data_version.dv_data_version;
diff --git a/drivers/staging/lustre/lustre/lov/lov_lock.c b/drivers/staging/lustre/lustre/lov/lov_lock.c
index 2fcdeb707ff9..b0292100bf26 100644
--- a/drivers/staging/lustre/lustre/lov/lov_lock.c
+++ b/drivers/staging/lustre/lustre/lov/lov_lock.c
@@ -145,7 +145,7 @@ static struct lov_lock *lov_lock_sub_init(const struct lu_env *env,
nr++;
}
LASSERT(nr > 0);
- lovlck = libcfs_kvzalloc(offsetof(struct lov_lock, lls_sub[nr]),
+ lovlck = kvzalloc(offsetof(struct lov_lock, lls_sub[nr]),
GFP_NOFS);
if (!lovlck)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/staging/lustre/lustre/lov/lov_object.c b/drivers/staging/lustre/lustre/lov/lov_object.c
index 897cf2cd4a24..f7c69680cb7d 100644
--- a/drivers/staging/lustre/lustre/lov/lov_object.c
+++ b/drivers/staging/lustre/lustre/lov/lov_object.c
@@ -242,7 +242,7 @@ static int lov_init_raid0(const struct lu_env *env, struct lov_device *dev,
r0->lo_nr = lsm->lsm_stripe_count;
LASSERT(r0->lo_nr <= lov_targets_nr(dev));
- r0->lo_sub = libcfs_kvzalloc(r0->lo_nr * sizeof(r0->lo_sub[0]),
+ r0->lo_sub = kvzalloc(r0->lo_nr * sizeof(r0->lo_sub[0]),
GFP_NOFS);
if (r0->lo_sub) {
int psz = 0;
@@ -723,15 +723,13 @@ static void lov_conf_unlock(struct lov_object *lov)
static int lov_layout_wait(const struct lu_env *env, struct lov_object *lov)
{
- struct l_wait_info lwi = { 0 };
-
while (atomic_read(&lov->lo_active_ios) > 0) {
CDEBUG(D_INODE, "file:" DFID " wait for active IO, now: %d.\n",
PFID(lu_object_fid(lov2lu(lov))),
atomic_read(&lov->lo_active_ios));
- l_wait_event(lov->lo_waitq,
- atomic_read(&lov->lo_active_ios) == 0, &lwi);
+ wait_event_idle(lov->lo_waitq,
+ atomic_read(&lov->lo_active_ios) == 0);
}
return 0;
}
@@ -1175,7 +1173,8 @@ static int fiemap_for_stripe(const struct lu_env *env, struct cl_object *obj,
/* If this is a continuation FIEMAP call and we are on
* starting stripe then lun_start needs to be set to
- * end_offset */
+ * end_offset
+ */
if (fs->fs_end_offset != 0 && stripeno == fs->fs_start_stripe)
lun_start = fs->fs_end_offset;
@@ -1200,7 +1199,8 @@ static int fiemap_for_stripe(const struct lu_env *env, struct cl_object *obj,
if (IS_ERR(subobj))
return PTR_ERR(subobj);
/* If the output buffer is very large and the objects have many
- * extents we may need to loop on a single OST repeatedly */
+ * extents we may need to loop on a single OST repeatedly
+ */
do {
if (fiemap->fm_extent_count > 0) {
/* Don't get too many extents. */
@@ -1250,7 +1250,8 @@ inactive_tgt:
ost_done = true;
fs->fs_device_done = true;
/* If last stripe has hold at the end,
- * we need to return */
+ * we need to return
+ */
if (stripeno == fs->fs_last_stripe) {
fiemap->fm_mapped_extents = 0;
fs->fs_finish = true;
@@ -1284,7 +1285,8 @@ inactive_tgt:
}
/* Clear the EXTENT_LAST flag which can be present on
- * the last extent */
+ * the last extent
+ */
if (fm_ext[ext_count - 1].fe_flags & FIEMAP_EXTENT_LAST)
fm_ext[ext_count - 1].fe_flags &= ~FIEMAP_EXTENT_LAST;
if (lov_stripe_size(lsm, fm_ext[ext_count - 1].fe_logical +
@@ -1377,7 +1379,7 @@ static int lov_object_fiemap(const struct lu_env *env, struct cl_object *obj,
if (fiemap_count_to_size(fiemap->fm_extent_count) < buffer_size)
buffer_size = fiemap_count_to_size(fiemap->fm_extent_count);
- fm_local = libcfs_kvzalloc(buffer_size, GFP_NOFS);
+ fm_local = kvzalloc(buffer_size, GFP_NOFS);
if (!fm_local) {
rc = -ENOMEM;
goto out;
diff --git a/drivers/staging/lustre/lustre/lov/lov_pack.c b/drivers/staging/lustre/lustre/lov/lov_pack.c
index e5b11c4085a9..b1060d02a164 100644
--- a/drivers/staging/lustre/lustre/lov/lov_pack.c
+++ b/drivers/staging/lustre/lustre/lov/lov_pack.c
@@ -333,7 +333,7 @@ int lov_getstripe(struct lov_object *obj, struct lov_stripe_md *lsm,
lmmk_size = lov_mds_md_size(stripe_count, lsm->lsm_magic);
- lmmk = libcfs_kvzalloc(lmmk_size, GFP_NOFS);
+ lmmk = kvzalloc(lmmk_size, GFP_NOFS);
if (!lmmk) {
rc = -ENOMEM;
goto out;
diff --git a/drivers/staging/lustre/lustre/lov/lov_request.c b/drivers/staging/lustre/lustre/lov/lov_request.c
index cfa1d7f92b0f..051450d67524 100644
--- a/drivers/staging/lustre/lustre/lov/lov_request.c
+++ b/drivers/staging/lustre/lustre/lov/lov_request.c
@@ -99,8 +99,7 @@ static int lov_check_set(struct lov_obd *lov, int idx)
*/
static int lov_check_and_wait_active(struct lov_obd *lov, int ost_idx)
{
- wait_queue_head_t waitq;
- struct l_wait_info lwi;
+ int cnt = 0;
struct lov_tgt_desc *tgt;
int rc = 0;
@@ -125,11 +124,10 @@ static int lov_check_and_wait_active(struct lov_obd *lov, int ost_idx)
mutex_unlock(&lov->lov_lock);
- init_waitqueue_head(&waitq);
- lwi = LWI_TIMEOUT_INTERVAL(cfs_time_seconds(obd_timeout),
- cfs_time_seconds(1), NULL, NULL);
-
- rc = l_wait_event(waitq, lov_check_set(lov, ost_idx), &lwi);
+ while (cnt < obd_timeout && !lov_check_set(lov, ost_idx)) {
+ schedule_timeout_uninterruptible(HZ);
+ cnt++;
+ }
if (tgt->ltd_active)
return 1;
diff --git a/drivers/staging/lustre/lustre/mdc/mdc_locks.c b/drivers/staging/lustre/lustre/mdc/mdc_locks.c
index 3114907ac5ff..695ef44532cf 100644
--- a/drivers/staging/lustre/lustre/mdc/mdc_locks.c
+++ b/drivers/staging/lustre/lustre/mdc/mdc_locks.c
@@ -660,7 +660,7 @@ static int mdc_finish_enqueue(struct obd_export *exp,
LDLM_DEBUG(lock, "layout lock returned by: %s, lvb_len: %d",
ldlm_it2str(it->it_op), lvb_len);
- lmm = libcfs_kvzalloc(lvb_len, GFP_NOFS);
+ lmm = kvzalloc(lvb_len, GFP_NOFS);
if (!lmm) {
LDLM_LOCK_PUT(lock);
return -ENOMEM;
diff --git a/drivers/staging/lustre/lustre/mdc/mdc_request.c b/drivers/staging/lustre/lustre/mdc/mdc_request.c
index 03e55bca4ada..8ee7b4d273b2 100644
--- a/drivers/staging/lustre/lustre/mdc/mdc_request.c
+++ b/drivers/staging/lustre/lustre/mdc/mdc_request.c
@@ -838,7 +838,6 @@ static int mdc_getpage(struct obd_export *exp, const struct lu_fid *fid,
struct ptlrpc_bulk_desc *desc;
struct ptlrpc_request *req;
wait_queue_head_t waitq;
- struct l_wait_info lwi;
int resends = 0;
int rc;
int i;
@@ -888,9 +887,7 @@ restart_bulk:
exp->exp_obd->obd_name, -EIO);
return -EIO;
}
- lwi = LWI_TIMEOUT_INTR(cfs_time_seconds(resends), NULL, NULL,
- NULL);
- l_wait_event(waitq, 0, &lwi);
+ wait_event_idle_timeout(waitq, 0, resends * HZ);
goto restart_bulk;
}
@@ -937,14 +934,14 @@ static struct page *mdc_page_locate(struct address_space *mapping, __u64 *hash,
struct page *page;
int found;
- spin_lock_irq(&mapping->tree_lock);
- found = radix_tree_gang_lookup(&mapping->page_tree,
+ xa_lock_irq(&mapping->i_pages);
+ found = radix_tree_gang_lookup(&mapping->i_pages,
(void **)&page, offset, 1);
if (found > 0 && !radix_tree_exceptional_entry(page)) {
struct lu_dirpage *dp;
get_page(page);
- spin_unlock_irq(&mapping->tree_lock);
+ xa_unlock_irq(&mapping->i_pages);
/*
* In contrast to find_lock_page() we are sure that directory
* page cannot be truncated (while DLM lock is held) and,
@@ -992,7 +989,7 @@ static struct page *mdc_page_locate(struct address_space *mapping, __u64 *hash,
page = ERR_PTR(-EIO);
}
} else {
- spin_unlock_irq(&mapping->tree_lock);
+ xa_unlock_irq(&mapping->i_pages);
page = NULL;
}
return page;
@@ -1058,13 +1055,14 @@ static void mdc_adjust_dirpages(struct page **pages, int cfs_pgs, int lu_pgs)
__u64 hash_end = le64_to_cpu(dp->ldp_hash_end);
__u32 flags = le32_to_cpu(dp->ldp_flags);
struct lu_dirpage *first = dp;
- struct lu_dirent *end_dirent = NULL;
- struct lu_dirent *ent;
while (--lu_pgs > 0) {
- ent = lu_dirent_start(dp);
- for (end_dirent = ent; ent;
- end_dirent = ent, ent = lu_dirent_next(ent));
+ struct lu_dirent *end_dirent = NULL;
+ struct lu_dirent *ent;
+
+ for (ent = lu_dirent_start(dp); ent;
+ ent = lu_dirent_next(ent))
+ end_dirent = ent;
/* Advance dp to next lu_dirpage. */
dp = (struct lu_dirpage *)((char *)dp + LU_PAGE_SIZE);
diff --git a/drivers/staging/lustre/lustre/mgc/mgc_request.c b/drivers/staging/lustre/lustre/mgc/mgc_request.c
index 79ff85feab64..c61cd23a96df 100644
--- a/drivers/staging/lustre/lustre/mgc/mgc_request.c
+++ b/drivers/staging/lustre/lustre/mgc/mgc_request.c
@@ -535,7 +535,6 @@ static int mgc_requeue_thread(void *data)
spin_lock(&config_list_lock);
rq_state |= RQ_RUNNING;
while (!(rq_state & RQ_STOP)) {
- struct l_wait_info lwi;
struct config_llog_data *cld, *cld_prev;
int rand = prandom_u32_max(MGC_TIMEOUT_RAND_CENTISEC);
int to;
@@ -556,9 +555,9 @@ static int mgc_requeue_thread(void *data)
to = msecs_to_jiffies(MGC_TIMEOUT_MIN_SECONDS * MSEC_PER_SEC);
/* rand is centi-seconds */
to += msecs_to_jiffies(rand * MSEC_PER_SEC / 100);
- lwi = LWI_TIMEOUT(to, NULL, NULL);
- l_wait_event(rq_waitq, rq_state & (RQ_STOP | RQ_PRECLEANUP),
- &lwi);
+ wait_event_idle_timeout(rq_waitq,
+ rq_state & (RQ_STOP | RQ_PRECLEANUP),
+ to);
/*
* iterate & processing through the list. for each cld, process
@@ -601,9 +600,7 @@ static int mgc_requeue_thread(void *data)
config_log_put(cld_prev);
/* Wait a bit to see if anyone else needs a requeue */
- lwi = (struct l_wait_info) { 0 };
- l_wait_event(rq_waitq, rq_state & (RQ_NOW | RQ_STOP),
- &lwi);
+ wait_event_idle(rq_waitq, rq_state & (RQ_NOW | RQ_STOP));
spin_lock(&config_list_lock);
}
@@ -1630,9 +1627,7 @@ restart:
if (rcl == -ESHUTDOWN &&
atomic_read(&mgc->u.cli.cl_mgc_refcount) > 0 && !retry) {
- int secs = cfs_time_seconds(obd_timeout);
struct obd_import *imp;
- struct l_wait_info lwi;
mutex_unlock(&cld->cld_lock);
imp = class_exp2cliimp(mgc->u.cli.cl_mgc_mgsexp);
@@ -1647,9 +1642,9 @@ restart:
*/
ptlrpc_pinger_force(imp);
- lwi = LWI_TIMEOUT(secs, NULL, NULL);
- l_wait_event(imp->imp_recovery_waitq,
- !mgc_import_in_recovery(imp), &lwi);
+ wait_event_idle_timeout(imp->imp_recovery_waitq,
+ !mgc_import_in_recovery(imp),
+ obd_timeout * HZ);
if (imp->imp_state == LUSTRE_IMP_FULL) {
retry = true;
diff --git a/drivers/staging/lustre/lustre/obdclass/cl_io.c b/drivers/staging/lustre/lustre/obdclass/cl_io.c
index 6ec5218a18c1..ab84e011b560 100644
--- a/drivers/staging/lustre/lustre/obdclass/cl_io.c
+++ b/drivers/staging/lustre/lustre/obdclass/cl_io.c
@@ -1097,23 +1097,24 @@ EXPORT_SYMBOL(cl_sync_io_init);
int cl_sync_io_wait(const struct lu_env *env, struct cl_sync_io *anchor,
long timeout)
{
- struct l_wait_info lwi = LWI_TIMEOUT_INTR(cfs_time_seconds(timeout),
- NULL, NULL, NULL);
- int rc;
+ int rc = 1;
LASSERT(timeout >= 0);
- rc = l_wait_event(anchor->csi_waitq,
- atomic_read(&anchor->csi_sync_nr) == 0,
- &lwi);
- if (rc < 0) {
+ if (timeout == 0)
+ wait_event_idle(anchor->csi_waitq,
+ atomic_read(&anchor->csi_sync_nr) == 0);
+ else
+ rc = wait_event_idle_timeout(anchor->csi_waitq,
+ atomic_read(&anchor->csi_sync_nr) == 0,
+ timeout * HZ);
+ if (rc == 0) {
+ rc = -ETIMEDOUT;
CERROR("IO failed: %d, still wait for %d remaining entries\n",
rc, atomic_read(&anchor->csi_sync_nr));
- lwi = (struct l_wait_info) { 0 };
- (void)l_wait_event(anchor->csi_waitq,
- atomic_read(&anchor->csi_sync_nr) == 0,
- &lwi);
+ wait_event_idle(anchor->csi_waitq,
+ atomic_read(&anchor->csi_sync_nr) == 0);
} else {
rc = anchor->csi_sync_rc;
}
diff --git a/drivers/staging/lustre/lustre/obdclass/cl_lock.c b/drivers/staging/lustre/lustre/obdclass/cl_lock.c
index 3b683b774fef..9ca29a26a38b 100644
--- a/drivers/staging/lustre/lustre/obdclass/cl_lock.c
+++ b/drivers/staging/lustre/lustre/obdclass/cl_lock.c
@@ -224,7 +224,7 @@ EXPORT_SYMBOL(cl_lock_release);
const char *cl_lock_mode_name(const enum cl_lock_mode mode)
{
- static const char *names[] = {
+ static const char * const names[] = {
[CLM_READ] = "R",
[CLM_WRITE] = "W",
[CLM_GROUP] = "G"
diff --git a/drivers/staging/lustre/lustre/obdclass/cl_object.c b/drivers/staging/lustre/lustre/obdclass/cl_object.c
index 7b18d775b001..7809f6ae1809 100644
--- a/drivers/staging/lustre/lustre/obdclass/cl_object.c
+++ b/drivers/staging/lustre/lustre/obdclass/cl_object.c
@@ -495,7 +495,7 @@ static struct cache_stats cl_env_stats = {
int cl_site_stats_print(const struct cl_site *site, struct seq_file *m)
{
size_t i;
- static const char *pstate[] = {
+ static const char * const pstate[] = {
[CPS_CACHED] = "c",
[CPS_OWNED] = "o",
[CPS_PAGEOUT] = "w",
diff --git a/drivers/staging/lustre/lustre/obdclass/genops.c b/drivers/staging/lustre/lustre/obdclass/genops.c
index b1d6ba4a3190..63ccbabb4c5a 100644
--- a/drivers/staging/lustre/lustre/obdclass/genops.c
+++ b/drivers/staging/lustre/lustre/obdclass/genops.c
@@ -48,10 +48,7 @@ struct kmem_cache *obdo_cachep;
EXPORT_SYMBOL(obdo_cachep);
static struct kmem_cache *import_cachep;
-static struct list_head obd_zombie_imports;
-static struct list_head obd_zombie_exports;
-static spinlock_t obd_zombie_impexp_lock;
-static void obd_zombie_impexp_notify(void);
+static struct workqueue_struct *zombie_wq;
static void obd_zombie_export_add(struct obd_export *exp);
static void obd_zombie_import_add(struct obd_import *imp);
@@ -701,6 +698,13 @@ void class_export_put(struct obd_export *exp)
}
EXPORT_SYMBOL(class_export_put);
+static void obd_zombie_exp_cull(struct work_struct *ws)
+{
+ struct obd_export *export = container_of(ws, struct obd_export, exp_zombie_work);
+
+ class_export_destroy(export);
+}
+
/* Creates a new export, adds it to the hash table, and returns a
* pointer to it. The refcount is 2: one for the hash reference, and
* one for the pointer returned by this function.
@@ -741,6 +745,7 @@ struct obd_export *class_new_export(struct obd_device *obd,
INIT_HLIST_NODE(&export->exp_uuid_hash);
spin_lock_init(&export->exp_bl_list_lock);
INIT_LIST_HEAD(&export->exp_bl_list);
+ INIT_WORK(&export->exp_zombie_work, obd_zombie_exp_cull);
export->exp_sp_peer = LUSTRE_SP_ANY;
export->exp_flvr.sf_rpc = SPTLRPC_FLVR_INVALID;
@@ -862,7 +867,6 @@ EXPORT_SYMBOL(class_import_get);
void class_import_put(struct obd_import *imp)
{
- LASSERT(list_empty(&imp->imp_zombie_chain));
LASSERT_ATOMIC_GT_LT(&imp->imp_refcount, 0, LI_POISON);
CDEBUG(D_INFO, "import %p refcount=%d obd=%s\n", imp,
@@ -894,6 +898,13 @@ static void init_imp_at(struct imp_at *at)
}
}
+static void obd_zombie_imp_cull(struct work_struct *ws)
+{
+ struct obd_import *import = container_of(ws, struct obd_import, imp_zombie_work);
+
+ class_import_destroy(import);
+}
+
struct obd_import *class_new_import(struct obd_device *obd)
{
struct obd_import *imp;
@@ -903,7 +914,6 @@ struct obd_import *class_new_import(struct obd_device *obd)
return NULL;
INIT_LIST_HEAD(&imp->imp_pinger_chain);
- INIT_LIST_HEAD(&imp->imp_zombie_chain);
INIT_LIST_HEAD(&imp->imp_replay_list);
INIT_LIST_HEAD(&imp->imp_sending_list);
INIT_LIST_HEAD(&imp->imp_delayed_list);
@@ -917,6 +927,7 @@ struct obd_import *class_new_import(struct obd_device *obd)
imp->imp_obd = class_incref(obd, "import", imp);
mutex_init(&imp->imp_sec_mutex);
init_waitqueue_head(&imp->imp_recovery_waitq);
+ INIT_WORK(&imp->imp_zombie_work, obd_zombie_imp_cull);
atomic_set(&imp->imp_refcount, 2);
atomic_set(&imp->imp_unregistering, 0);
@@ -1098,81 +1109,6 @@ EXPORT_SYMBOL(class_fail_export);
void (*class_export_dump_hook)(struct obd_export *) = NULL;
#endif
-/* Total amount of zombies to be destroyed */
-static int zombies_count;
-
-/**
- * kill zombie imports and exports
- */
-static void obd_zombie_impexp_cull(void)
-{
- struct obd_import *import;
- struct obd_export *export;
-
- do {
- spin_lock(&obd_zombie_impexp_lock);
-
- import = NULL;
- if (!list_empty(&obd_zombie_imports)) {
- import = list_entry(obd_zombie_imports.next,
- struct obd_import,
- imp_zombie_chain);
- list_del_init(&import->imp_zombie_chain);
- }
-
- export = NULL;
- if (!list_empty(&obd_zombie_exports)) {
- export = list_entry(obd_zombie_exports.next,
- struct obd_export,
- exp_obd_chain);
- list_del_init(&export->exp_obd_chain);
- }
-
- spin_unlock(&obd_zombie_impexp_lock);
-
- if (import) {
- class_import_destroy(import);
- spin_lock(&obd_zombie_impexp_lock);
- zombies_count--;
- spin_unlock(&obd_zombie_impexp_lock);
- }
-
- if (export) {
- class_export_destroy(export);
- spin_lock(&obd_zombie_impexp_lock);
- zombies_count--;
- spin_unlock(&obd_zombie_impexp_lock);
- }
-
- cond_resched();
- } while (import || export);
-}
-
-static struct completion obd_zombie_start;
-static struct completion obd_zombie_stop;
-static unsigned long obd_zombie_flags;
-static wait_queue_head_t obd_zombie_waitq;
-static pid_t obd_zombie_pid;
-
-enum {
- OBD_ZOMBIE_STOP = 0x0001,
-};
-
-/**
- * check for work for kill zombie import/export thread.
- */
-static int obd_zombie_impexp_check(void *arg)
-{
- int rc;
-
- spin_lock(&obd_zombie_impexp_lock);
- rc = (zombies_count == 0) &&
- !test_bit(OBD_ZOMBIE_STOP, &obd_zombie_flags);
- spin_unlock(&obd_zombie_impexp_lock);
-
- return rc;
-}
-
/**
* Add export to the obd_zombie thread and notify it.
*/
@@ -1182,12 +1118,7 @@ static void obd_zombie_export_add(struct obd_export *exp)
LASSERT(!list_empty(&exp->exp_obd_chain));
list_del_init(&exp->exp_obd_chain);
spin_unlock(&exp->exp_obd->obd_dev_lock);
- spin_lock(&obd_zombie_impexp_lock);
- zombies_count++;
- list_add(&exp->exp_obd_chain, &obd_zombie_exports);
- spin_unlock(&obd_zombie_impexp_lock);
-
- obd_zombie_impexp_notify();
+ queue_work(zombie_wq, &exp->exp_zombie_work);
}
/**
@@ -1196,40 +1127,7 @@ static void obd_zombie_export_add(struct obd_export *exp)
static void obd_zombie_import_add(struct obd_import *imp)
{
LASSERT(!imp->imp_sec);
- spin_lock(&obd_zombie_impexp_lock);
- LASSERT(list_empty(&imp->imp_zombie_chain));
- zombies_count++;
- list_add(&imp->imp_zombie_chain, &obd_zombie_imports);
- spin_unlock(&obd_zombie_impexp_lock);
-
- obd_zombie_impexp_notify();
-}
-
-/**
- * notify import/export destroy thread about new zombie.
- */
-static void obd_zombie_impexp_notify(void)
-{
- /*
- * Make sure obd_zombie_impexp_thread get this notification.
- * It is possible this signal only get by obd_zombie_barrier, and
- * barrier gulps this notification and sleeps away and hangs ensues
- */
- wake_up_all(&obd_zombie_waitq);
-}
-
-/**
- * check whether obd_zombie is idle
- */
-static int obd_zombie_is_idle(void)
-{
- int rc;
-
- LASSERT(!test_bit(OBD_ZOMBIE_STOP, &obd_zombie_flags));
- spin_lock(&obd_zombie_impexp_lock);
- rc = (zombies_count == 0);
- spin_unlock(&obd_zombie_impexp_lock);
- return rc;
+ queue_work(zombie_wq, &imp->imp_zombie_work);
}
/**
@@ -1237,64 +1135,19 @@ static int obd_zombie_is_idle(void)
*/
void obd_zombie_barrier(void)
{
- struct l_wait_info lwi = { 0 };
-
- if (obd_zombie_pid == current_pid())
- /* don't wait for myself */
- return;
- l_wait_event(obd_zombie_waitq, obd_zombie_is_idle(), &lwi);
+ flush_workqueue(zombie_wq);
}
EXPORT_SYMBOL(obd_zombie_barrier);
/**
- * destroy zombie export/import thread.
- */
-static int obd_zombie_impexp_thread(void *unused)
-{
- unshare_fs_struct();
- complete(&obd_zombie_start);
-
- obd_zombie_pid = current_pid();
-
- while (!test_bit(OBD_ZOMBIE_STOP, &obd_zombie_flags)) {
- struct l_wait_info lwi = { 0 };
-
- l_wait_event(obd_zombie_waitq,
- !obd_zombie_impexp_check(NULL), &lwi);
- obd_zombie_impexp_cull();
-
- /*
- * Notify obd_zombie_barrier callers that queues
- * may be empty.
- */
- wake_up(&obd_zombie_waitq);
- }
-
- complete(&obd_zombie_stop);
-
- return 0;
-}
-
-/**
* start destroy zombie import/export thread
*/
int obd_zombie_impexp_init(void)
{
- struct task_struct *task;
-
- INIT_LIST_HEAD(&obd_zombie_imports);
- INIT_LIST_HEAD(&obd_zombie_exports);
- spin_lock_init(&obd_zombie_impexp_lock);
- init_completion(&obd_zombie_start);
- init_completion(&obd_zombie_stop);
- init_waitqueue_head(&obd_zombie_waitq);
- obd_zombie_pid = 0;
-
- task = kthread_run(obd_zombie_impexp_thread, NULL, "obd_zombid");
- if (IS_ERR(task))
- return PTR_ERR(task);
+ zombie_wq = alloc_workqueue("obd_zombid", 0, 0);
+ if (!zombie_wq)
+ return -ENOMEM;
- wait_for_completion(&obd_zombie_start);
return 0;
}
@@ -1303,9 +1156,7 @@ int obd_zombie_impexp_init(void)
*/
void obd_zombie_impexp_stop(void)
{
- set_bit(OBD_ZOMBIE_STOP, &obd_zombie_flags);
- obd_zombie_impexp_notify();
- wait_for_completion(&obd_zombie_stop);
+ destroy_workqueue(zombie_wq);
}
struct obd_request_slot_waiter {
@@ -1336,7 +1187,6 @@ static bool obd_request_slot_avail(struct client_obd *cli,
int obd_get_request_slot(struct client_obd *cli)
{
struct obd_request_slot_waiter orsw;
- struct l_wait_info lwi;
int rc;
spin_lock(&cli->cl_loi_list_lock);
@@ -1351,11 +1201,9 @@ int obd_get_request_slot(struct client_obd *cli)
orsw.orsw_signaled = false;
spin_unlock(&cli->cl_loi_list_lock);
- lwi = LWI_INTR(LWI_ON_SIGNAL_NOOP, NULL);
- rc = l_wait_event(orsw.orsw_waitq,
- obd_request_slot_avail(cli, &orsw) ||
- orsw.orsw_signaled,
- &lwi);
+ rc = l_wait_event_abortable(orsw.orsw_waitq,
+ obd_request_slot_avail(cli, &orsw) ||
+ orsw.orsw_signaled);
/*
* Here, we must take the lock to avoid the on-stack 'orsw' to be
@@ -1593,7 +1441,6 @@ static inline bool obd_mod_rpc_slot_avail(struct client_obd *cli,
u16 obd_get_mod_rpc_slot(struct client_obd *cli, __u32 opc,
struct lookup_intent *it)
{
- struct l_wait_info lwi = LWI_INTR(NULL, NULL);
bool close_req = false;
u16 i, max;
@@ -1631,8 +1478,8 @@ u16 obd_get_mod_rpc_slot(struct client_obd *cli, __u32 opc,
CDEBUG(D_RPCTRACE, "%s: sleeping for a modify RPC slot opc %u, max %hu\n",
cli->cl_import->imp_obd->obd_name, opc, max);
- l_wait_event(cli->cl_mod_rpcs_waitq,
- obd_mod_rpc_slot_avail(cli, close_req), &lwi);
+ wait_event_idle(cli->cl_mod_rpcs_waitq,
+ obd_mod_rpc_slot_avail(cli, close_req));
} while (true);
}
EXPORT_SYMBOL(obd_get_mod_rpc_slot);
diff --git a/drivers/staging/lustre/lustre/obdclass/linkea.c b/drivers/staging/lustre/lustre/obdclass/linkea.c
index fe1638b0916e..74c99ee216bb 100644
--- a/drivers/staging/lustre/lustre/obdclass/linkea.c
+++ b/drivers/staging/lustre/lustre/obdclass/linkea.c
@@ -33,9 +33,11 @@
int linkea_data_new(struct linkea_data *ldata, struct lu_buf *buf)
{
- ldata->ld_buf = lu_buf_check_and_alloc(buf, PAGE_SIZE);
- if (!ldata->ld_buf->lb_buf)
+ buf->lb_buf = kzalloc(PAGE_SIZE, GFP_NOFS);
+ if (!buf->lb_buf)
return -ENOMEM;
+ buf->lb_len = PAGE_SIZE;
+ ldata->ld_buf = buf;
ldata->ld_leh = ldata->ld_buf->lb_buf;
ldata->ld_leh->leh_magic = LINK_EA_MAGIC;
ldata->ld_leh->leh_len = sizeof(struct link_ea_header);
@@ -158,11 +160,15 @@ int linkea_add_buf(struct linkea_data *ldata, const struct lu_name *lname,
}
if (leh->leh_len + reclen > ldata->ld_buf->lb_len) {
- if (lu_buf_check_and_grow(ldata->ld_buf,
- leh->leh_len + reclen) < 0)
+ /* Note: this never happens as MAX_LINKEA_SIZE is 4096, while
+ * the initial allocation is PAGE_SIZE.
+ */
+ void *b = krealloc(ldata->ld_buf->lb_buf, leh->leh_len + reclen, GFP_NOFS);
+ if (!b)
return -ENOMEM;
- leh = ldata->ld_leh = ldata->ld_buf->lb_buf;
+ ldata->ld_buf->lb_len = leh->leh_len + reclen;
+ leh = ldata->ld_leh = ldata->ld_buf->lb_buf = b;
}
ldata->ld_lee = ldata->ld_buf->lb_buf + leh->leh_len;
diff --git a/drivers/staging/lustre/lustre/obdclass/linux/linux-module.c b/drivers/staging/lustre/lustre/obdclass/linux/linux-module.c
index 57951237def2..7bceee7f121e 100644
--- a/drivers/staging/lustre/lustre/obdclass/linux/linux-module.c
+++ b/drivers/staging/lustre/lustre/obdclass/linux/linux-module.c
@@ -180,7 +180,7 @@ int obd_ioctl_getdata(char **buf, int *len, void __user *arg)
* obdfilter-survey is an example, which relies on ioctl. So we'd
* better avoid vmalloc on ioctl path. LU-66
*/
- *buf = libcfs_kvzalloc(hdr.ioc_len, GFP_NOFS);
+ *buf = kvzalloc(hdr.ioc_len, GFP_KERNEL);
if (!*buf) {
CERROR("Cannot allocate control buffer of len %d\n",
hdr.ioc_len);
@@ -251,7 +251,7 @@ static long obd_class_ioctl(struct file *filp, unsigned int cmd,
int err = 0;
/* Allow non-root access for OBD_IOC_PING_TARGET - used by lfs check */
- if (!capable(CFS_CAP_SYS_ADMIN) && (cmd != OBD_IOC_PING_TARGET))
+ if (!capable(CAP_SYS_ADMIN) && (cmd != OBD_IOC_PING_TARGET))
return err = -EACCES;
if ((cmd & 0xffffff00) == ((int)'T') << 8) /* ignore all tty ioctls */
return err = -ENOTTY;
diff --git a/drivers/staging/lustre/lustre/obdclass/llog.c b/drivers/staging/lustre/lustre/obdclass/llog.c
index cd051e31233e..693e1129f1f9 100644
--- a/drivers/staging/lustre/lustre/obdclass/llog.c
+++ b/drivers/staging/lustre/lustre/obdclass/llog.c
@@ -155,7 +155,7 @@ int llog_init_handle(const struct lu_env *env, struct llog_handle *handle,
LASSERT(!handle->lgh_hdr);
LASSERT(chunk_size >= LLOG_MIN_CHUNK_SIZE);
- llh = libcfs_kvzalloc(sizeof(*llh), GFP_NOFS);
+ llh = kvzalloc(sizeof(*llh), GFP_KERNEL);
if (!llh)
return -ENOMEM;
handle->lgh_hdr = llh;
@@ -240,7 +240,7 @@ static int llog_process_thread(void *arg)
/* expect chunk_size to be power of two */
LASSERT(is_power_of_2(chunk_size));
- buf = libcfs_kvzalloc(chunk_size, GFP_NOFS);
+ buf = kvzalloc(chunk_size, GFP_NOFS);
if (!buf) {
lpi->lpi_rc = -ENOMEM;
return 0;
@@ -466,7 +466,7 @@ int llog_open(const struct lu_env *env, struct llog_ctxt *ctxt,
struct llog_handle **lgh, struct llog_logid *logid,
char *name, enum llog_open_param open_param)
{
- int raised;
+ const struct cred *old_cred = NULL;
int rc;
LASSERT(ctxt);
@@ -483,12 +483,18 @@ int llog_open(const struct lu_env *env, struct llog_ctxt *ctxt,
(*lgh)->lgh_ctxt = ctxt;
(*lgh)->lgh_logops = ctxt->loc_logops;
- raised = cfs_cap_raised(CFS_CAP_SYS_RESOURCE);
- if (!raised)
- cfs_cap_raise(CFS_CAP_SYS_RESOURCE);
+ if (cap_raised(current_cap(), CAP_SYS_RESOURCE)) {
+ struct cred *cred = prepare_creds();
+
+ if (cred) {
+ cap_raise(cred->cap_effective, CAP_SYS_RESOURCE);
+ old_cred = override_creds(cred);
+ }
+ }
rc = ctxt->loc_logops->lop_open(env, *lgh, logid, name, open_param);
- if (!raised)
- cfs_cap_lower(CFS_CAP_SYS_RESOURCE);
+ if (old_cred)
+ revert_creds(old_cred);
+
if (rc) {
llog_free_handle(*lgh);
*lgh = NULL;
diff --git a/drivers/staging/lustre/lustre/obdclass/llog_obd.c b/drivers/staging/lustre/lustre/obdclass/llog_obd.c
index 28bbaa2136ac..26aea114a29b 100644
--- a/drivers/staging/lustre/lustre/obdclass/llog_obd.c
+++ b/drivers/staging/lustre/lustre/obdclass/llog_obd.c
@@ -104,7 +104,6 @@ EXPORT_SYMBOL(__llog_ctxt_put);
int llog_cleanup(const struct lu_env *env, struct llog_ctxt *ctxt)
{
- struct l_wait_info lwi = LWI_INTR(LWI_ON_SIGNAL_NOOP, NULL);
struct obd_llog_group *olg;
int rc, idx;
@@ -129,8 +128,8 @@ int llog_cleanup(const struct lu_env *env, struct llog_ctxt *ctxt)
CERROR("Error %d while cleaning up ctxt %p\n",
rc, ctxt);
- l_wait_event(olg->olg_waitq,
- llog_group_ctxt_null(olg, idx), &lwi);
+ l_wait_event_abortable(olg->olg_waitq,
+ llog_group_ctxt_null(olg, idx));
return rc;
}
diff --git a/drivers/staging/lustre/lustre/obdclass/lprocfs_status.c b/drivers/staging/lustre/lustre/obdclass/lprocfs_status.c
index e1f4ef2bddd4..2ed350527398 100644
--- a/drivers/staging/lustre/lustre/obdclass/lprocfs_status.c
+++ b/drivers/staging/lustre/lustre/obdclass/lprocfs_status.c
@@ -1467,7 +1467,7 @@ int lprocfs_write_frac_u64_helper(const char __user *buffer,
{
char kernbuf[22], *end, *pbuf;
__u64 whole, frac = 0, units;
- unsigned frac_d = 1;
+ unsigned int frac_d = 1;
int sign = 1;
if (count > (sizeof(kernbuf) - 1))
@@ -1585,7 +1585,7 @@ int ldebugfs_seq_create(struct dentry *parent, const char *name,
struct dentry *entry;
/* Disallow secretly (un)writable entries. */
- LASSERT((seq_fops->write == NULL) == ((mode & 0222) == 0));
+ LASSERT((!seq_fops->write) == ((mode & 0222) == 0));
entry = debugfs_create_file(name, mode, parent, data, seq_fops);
if (IS_ERR_OR_NULL(entry))
diff --git a/drivers/staging/lustre/lustre/obdclass/lu_object.c b/drivers/staging/lustre/lustre/obdclass/lu_object.c
index 2719abbff85f..3ae16e8501c2 100644
--- a/drivers/staging/lustre/lustre/obdclass/lu_object.c
+++ b/drivers/staging/lustre/lustre/obdclass/lu_object.c
@@ -1380,12 +1380,8 @@ static void key_fini(struct lu_context *ctx, int index)
lu_ref_del(&key->lct_reference, "ctx", ctx);
atomic_dec(&key->lct_used);
- if ((ctx->lc_tags & LCT_NOREF) == 0) {
-#ifdef CONFIG_MODULE_UNLOAD
- LINVRNT(module_refcount(key->lct_owner) > 0);
-#endif
+ if ((ctx->lc_tags & LCT_NOREF) == 0)
module_put(key->lct_owner);
- }
ctx->lc_value[index] = NULL;
}
}
@@ -1411,7 +1407,7 @@ void lu_context_key_degister(struct lu_context_key *key)
while (atomic_read(&key->lct_used) > 1) {
spin_unlock(&lu_keys_guard);
CDEBUG(D_INFO, "%s: \"%s\" %p, %d\n",
- __func__, key->lct_owner ? key->lct_owner->name : "",
+ __func__, module_name(key->lct_owner),
key, atomic_read(&key->lct_used));
schedule();
spin_lock(&lu_keys_guard);
@@ -1551,7 +1547,7 @@ void lu_context_key_quiesce(struct lu_context_key *key)
spin_unlock(&lu_keys_guard);
CDEBUG(D_INFO, "%s: \"%s\" %p, %d (%d)\n",
__func__,
- key->lct_owner ? key->lct_owner->name : "",
+ module_name(key->lct_owner),
key, atomic_read(&key->lct_used),
atomic_read(&lu_key_initing_cnt));
schedule();
@@ -1619,7 +1615,6 @@ static int keys_fill(struct lu_context *ctx)
LINVRNT(key->lct_init);
LINVRNT(key->lct_index == i);
- LASSERT(key->lct_owner);
if (!(ctx->lc_tags & LCT_NOREF) &&
!try_module_get(key->lct_owner)) {
/* module is unloading, skip this key */
@@ -1797,10 +1792,10 @@ int lu_env_refill(struct lu_env *env)
EXPORT_SYMBOL(lu_env_refill);
struct lu_site_stats {
- unsigned lss_populated;
- unsigned lss_max_search;
- unsigned lss_total;
- unsigned lss_busy;
+ unsigned int lss_populated;
+ unsigned int lss_max_search;
+ unsigned int lss_total;
+ unsigned int lss_busy;
};
static void lu_site_stats_get(struct cfs_hash *hs,
@@ -2061,73 +2056,3 @@ void lu_kmem_fini(struct lu_kmem_descr *caches)
}
}
EXPORT_SYMBOL(lu_kmem_fini);
-
-void lu_buf_free(struct lu_buf *buf)
-{
- LASSERT(buf);
- if (buf->lb_buf) {
- LASSERT(buf->lb_len > 0);
- kvfree(buf->lb_buf);
- buf->lb_buf = NULL;
- buf->lb_len = 0;
- }
-}
-EXPORT_SYMBOL(lu_buf_free);
-
-void lu_buf_alloc(struct lu_buf *buf, size_t size)
-{
- LASSERT(buf);
- LASSERT(!buf->lb_buf);
- LASSERT(!buf->lb_len);
- buf->lb_buf = libcfs_kvzalloc(size, GFP_NOFS);
- if (likely(buf->lb_buf))
- buf->lb_len = size;
-}
-EXPORT_SYMBOL(lu_buf_alloc);
-
-void lu_buf_realloc(struct lu_buf *buf, size_t size)
-{
- lu_buf_free(buf);
- lu_buf_alloc(buf, size);
-}
-EXPORT_SYMBOL(lu_buf_realloc);
-
-struct lu_buf *lu_buf_check_and_alloc(struct lu_buf *buf, size_t len)
-{
- if (!buf->lb_buf && !buf->lb_len)
- lu_buf_alloc(buf, len);
-
- if ((len > buf->lb_len) && buf->lb_buf)
- lu_buf_realloc(buf, len);
-
- return buf;
-}
-EXPORT_SYMBOL(lu_buf_check_and_alloc);
-
-/**
- * Increase the size of the \a buf.
- * preserves old data in buffer
- * old buffer remains unchanged on error
- * \retval 0 or -ENOMEM
- */
-int lu_buf_check_and_grow(struct lu_buf *buf, size_t len)
-{
- char *ptr;
-
- if (len <= buf->lb_len)
- return 0;
-
- ptr = libcfs_kvzalloc(len, GFP_NOFS);
- if (!ptr)
- return -ENOMEM;
-
- /* Free the old buf */
- if (buf->lb_buf) {
- memcpy(ptr, buf->lb_buf, buf->lb_len);
- kvfree(buf->lb_buf);
- }
-
- buf->lb_buf = ptr;
- buf->lb_len = len;
- return 0;
-}
diff --git a/drivers/staging/lustre/lustre/obdclass/lustre_handles.c b/drivers/staging/lustre/lustre/obdclass/lustre_handles.c
index 2d6da2431a09..f53b1a3c342e 100644
--- a/drivers/staging/lustre/lustre/obdclass/lustre_handles.c
+++ b/drivers/staging/lustre/lustre/obdclass/lustre_handles.c
@@ -184,8 +184,8 @@ int class_handle_init(void)
LASSERT(!handle_hash);
- handle_hash = libcfs_kvzalloc(sizeof(*bucket) * HANDLE_HASH_SIZE,
- GFP_NOFS);
+ handle_hash = kvzalloc(sizeof(*bucket) * HANDLE_HASH_SIZE,
+ GFP_KERNEL);
if (!handle_hash)
return -ENOMEM;
diff --git a/drivers/staging/lustre/lustre/obdclass/obd_config.c b/drivers/staging/lustre/lustre/obdclass/obd_config.c
index 997c0f9aafb5..277576b586db 100644
--- a/drivers/staging/lustre/lustre/obdclass/obd_config.c
+++ b/drivers/staging/lustre/lustre/obdclass/obd_config.c
@@ -455,7 +455,7 @@ static int class_cleanup(struct obd_device *obd, struct lustre_cfg *lcfg)
spin_unlock(&obd->obd_dev_lock);
while (obd->obd_conn_inprogress > 0)
- yield();
+ cond_resched();
smp_rmb();
if (lcfg->lcfg_bufcount >= 2 && LUSTRE_CFG_BUFLEN(lcfg, 1) > 0) {
diff --git a/drivers/staging/lustre/lustre/obdclass/obd_mount.c b/drivers/staging/lustre/lustre/obdclass/obd_mount.c
index acc1ea773c9c..f5e8214ac37b 100644
--- a/drivers/staging/lustre/lustre/obdclass/obd_mount.c
+++ b/drivers/staging/lustre/lustre/obdclass/obd_mount.c
@@ -243,7 +243,7 @@ int lustre_start_mgc(struct super_block *sb)
libcfs_nid2str_r(nid, nidstr, sizeof(nidstr));
mgcname = kasprintf(GFP_NOFS,
"%s%s", LUSTRE_MGC_OBDNAME, nidstr);
- niduuid = kasprintf(GFP_NOFS, "%s_%x", mgcname, i);
+ niduuid = kasprintf(GFP_NOFS, "%s_%x", mgcname, 0);
if (!mgcname || !niduuid) {
rc = -ENOMEM;
goto out_free;
diff --git a/drivers/staging/lustre/lustre/obdecho/echo_client.c b/drivers/staging/lustre/lustre/obdecho/echo_client.c
index b9c1dc7e61b0..99a76db51ae0 100644
--- a/drivers/staging/lustre/lustre/obdecho/echo_client.c
+++ b/drivers/staging/lustre/lustre/obdecho/echo_client.c
@@ -752,7 +752,7 @@ static struct lu_device *echo_device_free(const struct lu_env *env,
spin_unlock(&ec->ec_lock);
CERROR("echo_client still has objects at cleanup time, wait for 1 second\n");
set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(cfs_time_seconds(1));
+ schedule_timeout(HZ);
lu_site_purge(env, ed->ed_site, -1);
spin_lock(&ec->ec_lock);
}
@@ -1502,7 +1502,7 @@ echo_client_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
switch (cmd) {
case OBD_IOC_CREATE: /* may create echo object */
- if (!capable(CFS_CAP_SYS_ADMIN)) {
+ if (!capable(CAP_SYS_ADMIN)) {
rc = -EPERM;
goto out;
}
@@ -1511,7 +1511,7 @@ echo_client_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
goto out;
case OBD_IOC_DESTROY:
- if (!capable(CFS_CAP_SYS_ADMIN)) {
+ if (!capable(CAP_SYS_ADMIN)) {
rc = -EPERM;
goto out;
}
@@ -1534,7 +1534,7 @@ echo_client_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
goto out;
case OBD_IOC_SETATTR:
- if (!capable(CFS_CAP_SYS_ADMIN)) {
+ if (!capable(CAP_SYS_ADMIN)) {
rc = -EPERM;
goto out;
}
@@ -1547,7 +1547,7 @@ echo_client_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
goto out;
case OBD_IOC_BRW_WRITE:
- if (!capable(CFS_CAP_SYS_ADMIN)) {
+ if (!capable(CAP_SYS_ADMIN)) {
rc = -EPERM;
goto out;
}
diff --git a/drivers/staging/lustre/lustre/osc/osc_cache.c b/drivers/staging/lustre/lustre/osc/osc_cache.c
index 5767ac2a7d16..459503727ce3 100644
--- a/drivers/staging/lustre/lustre/osc/osc_cache.c
+++ b/drivers/staging/lustre/lustre/osc/osc_cache.c
@@ -934,8 +934,6 @@ static int osc_extent_wait(const struct lu_env *env, struct osc_extent *ext,
enum osc_extent_state state)
{
struct osc_object *obj = ext->oe_obj;
- struct l_wait_info lwi = LWI_TIMEOUT_INTR(cfs_time_seconds(600), NULL,
- LWI_ON_SIGNAL_NOOP, NULL);
int rc = 0;
osc_object_lock(obj);
@@ -958,18 +956,19 @@ static int osc_extent_wait(const struct lu_env *env, struct osc_extent *ext,
osc_extent_release(env, ext);
/* wait for the extent until its state becomes @state */
- rc = l_wait_event(ext->oe_waitq, extent_wait_cb(ext, state), &lwi);
- if (rc == -ETIMEDOUT) {
+ rc = wait_event_idle_timeout(ext->oe_waitq,
+ extent_wait_cb(ext, state), 600 * HZ);
+ if (rc == 0) {
OSC_EXTENT_DUMP(D_ERROR, ext,
"%s: wait ext to %u timedout, recovery in progress?\n",
cli_name(osc_cli(obj)), state);
- lwi = LWI_INTR(NULL, NULL);
- rc = l_wait_event(ext->oe_waitq, extent_wait_cb(ext, state),
- &lwi);
+ wait_event_idle(ext->oe_waitq, extent_wait_cb(ext, state));
}
- if (rc == 0 && ext->oe_rc < 0)
+ if (ext->oe_rc < 0)
rc = ext->oe_rc;
+ else
+ rc = 0;
return rc;
}
@@ -1530,7 +1529,7 @@ static int osc_enter_cache_try(struct client_obd *cli,
if (rc < 0)
return 0;
- if (cli->cl_dirty_pages <= cli->cl_dirty_max_pages &&
+ if (cli->cl_dirty_pages < cli->cl_dirty_max_pages &&
atomic_long_read(&obd_dirty_pages) + 1 <= obd_max_dirty_pages) {
osc_consume_write_grant(cli, &oap->oap_brw_page);
if (transient) {
@@ -1569,12 +1568,9 @@ static int osc_enter_cache(const struct lu_env *env, struct client_obd *cli,
struct osc_object *osc = oap->oap_obj;
struct lov_oinfo *loi = osc->oo_oinfo;
struct osc_cache_waiter ocw;
- struct l_wait_info lwi;
+ unsigned long timeout = (AT_OFF ? obd_timeout : at_max) * HZ;
int rc = -EDQUOT;
- lwi = LWI_TIMEOUT_INTR(cfs_time_seconds(AT_OFF ? obd_timeout : at_max),
- NULL, LWI_ON_SIGNAL_NOOP, NULL);
-
OSC_DUMP_GRANT(D_CACHE, cli, "need:%d\n", bytes);
spin_lock(&cli->cl_loi_list_lock);
@@ -1617,13 +1613,15 @@ static int osc_enter_cache(const struct lu_env *env, struct client_obd *cli,
CDEBUG(D_CACHE, "%s: sleeping for cache space @ %p for %p\n",
cli_name(cli), &ocw, oap);
- rc = l_wait_event(ocw.ocw_waitq, ocw_granted(cli, &ocw), &lwi);
+ rc = wait_event_idle_timeout(ocw.ocw_waitq,
+ ocw_granted(cli, &ocw), timeout);
spin_lock(&cli->cl_loi_list_lock);
- if (rc < 0) {
- /* l_wait_event is interrupted by signal, or timed out */
+ if (rc == 0) {
+ /* wait_event is interrupted by signal, or timed out */
list_del_init(&ocw.ocw_entry);
+ rc = -ETIMEDOUT;
break;
}
LASSERT(list_empty(&ocw.ocw_entry));
@@ -2347,7 +2345,7 @@ int osc_prep_async_page(struct osc_object *osc, struct osc_page *ops,
oap->oap_obj_off = offset;
LASSERT(!(offset & ~PAGE_MASK));
- if (capable(CFS_CAP_SYS_RESOURCE))
+ if (capable(CAP_SYS_RESOURCE))
oap->oap_brw_flags = OBD_BRW_NOQUOTA;
INIT_LIST_HEAD(&oap->oap_pending_item);
@@ -2386,7 +2384,7 @@ int osc_queue_async_io(const struct lu_env *env, struct cl_io *io,
/* Set the OBD_BRW_SRVLOCK before the page is queued. */
brw_flags |= ops->ops_srvlock ? OBD_BRW_SRVLOCK : 0;
- if (capable(CFS_CAP_SYS_RESOURCE)) {
+ if (capable(CAP_SYS_RESOURCE)) {
brw_flags |= OBD_BRW_NOQUOTA;
cmd |= OBD_BRW_NOQUOTA;
}
diff --git a/drivers/staging/lustre/lustre/osc/osc_object.c b/drivers/staging/lustre/lustre/osc/osc_object.c
index f82c87a77550..6baa8e2e00c9 100644
--- a/drivers/staging/lustre/lustre/osc/osc_object.c
+++ b/drivers/staging/lustre/lustre/osc/osc_object.c
@@ -328,7 +328,7 @@ int osc_object_is_contended(struct osc_object *obj)
* ll_file_is_contended.
*/
retry_time = cfs_time_add(obj->oo_contention_time,
- cfs_time_seconds(osc_contention_time));
+ osc_contention_time * HZ);
if (cfs_time_after(cur_time, retry_time)) {
osc_object_clear_contended(obj);
return 0;
@@ -454,12 +454,10 @@ struct lu_object *osc_object_alloc(const struct lu_env *env,
int osc_object_invalidate(const struct lu_env *env, struct osc_object *osc)
{
- struct l_wait_info lwi = { 0 };
-
CDEBUG(D_INODE, "Invalidate osc object: %p, # of active IOs: %d\n",
osc, atomic_read(&osc->oo_nr_ios));
- l_wait_event(osc->oo_io_waitq, !atomic_read(&osc->oo_nr_ios), &lwi);
+ wait_event_idle(osc->oo_io_waitq, !atomic_read(&osc->oo_nr_ios));
/* Discard all dirty pages of this object. */
osc_cache_truncate_start(env, osc, 0, NULL);
diff --git a/drivers/staging/lustre/lustre/osc/osc_page.c b/drivers/staging/lustre/lustre/osc/osc_page.c
index 20094b6309f9..01a930dbbf64 100644
--- a/drivers/staging/lustre/lustre/osc/osc_page.c
+++ b/drivers/staging/lustre/lustre/osc/osc_page.c
@@ -307,7 +307,7 @@ void osc_page_submit(const struct lu_env *env, struct osc_page *opg,
oap->oap_count = opg->ops_to - opg->ops_from;
oap->oap_brw_flags = brw_flags | OBD_BRW_SYNC;
- if (capable(CFS_CAP_SYS_RESOURCE)) {
+ if (capable(CAP_SYS_RESOURCE)) {
oap->oap_brw_flags |= OBD_BRW_NOQUOTA;
oap->oap_cmd |= OBD_BRW_NOQUOTA;
}
@@ -759,7 +759,6 @@ out:
static int osc_lru_alloc(const struct lu_env *env, struct client_obd *cli,
struct osc_page *opg)
{
- struct l_wait_info lwi = LWI_INTR(LWI_ON_SIGNAL_NOOP, NULL);
struct osc_io *oio = osc_env_io(env);
int rc = 0;
@@ -782,9 +781,8 @@ static int osc_lru_alloc(const struct lu_env *env, struct client_obd *cli,
cond_resched();
- rc = l_wait_event(osc_lru_waitq,
- atomic_long_read(cli->cl_lru_left) > 0,
- &lwi);
+ rc = l_wait_event_abortable(osc_lru_waitq,
+ atomic_long_read(cli->cl_lru_left) > 0);
if (rc < 0)
break;
diff --git a/drivers/staging/lustre/lustre/osc/osc_request.c b/drivers/staging/lustre/lustre/osc/osc_request.c
index 45b1ebf33363..1c2bbbf5d864 100644
--- a/drivers/staging/lustre/lustre/osc/osc_request.c
+++ b/drivers/staging/lustre/lustre/osc/osc_request.c
@@ -552,14 +552,12 @@ static int osc_destroy(const struct lu_env *env, struct obd_export *exp,
req->rq_interpret_reply = osc_destroy_interpret;
if (!osc_can_send_destroy(cli)) {
- struct l_wait_info lwi = LWI_INTR(LWI_ON_SIGNAL_NOOP, NULL);
-
/*
* Wait until the number of on-going destroy RPCs drops
* under max_rpc_in_flight
*/
- l_wait_event_exclusive(cli->cl_destroy_waitq,
- osc_can_send_destroy(cli), &lwi);
+ l_wait_event_abortable_exclusive(cli->cl_destroy_waitq,
+ osc_can_send_destroy(cli));
}
/* Do not wait for response */
@@ -933,7 +931,7 @@ static u32 osc_checksum_bulk(int nob, u32 pg_count,
{
__u32 cksum;
int i = 0;
- struct cfs_crypto_hash_desc *hdesc;
+ struct ahash_request *hdesc;
unsigned int bufsize;
unsigned char cfs_alg = cksum_obd2cfs(cksum_type);
diff --git a/drivers/staging/lustre/lustre/ptlrpc/client.c b/drivers/staging/lustre/lustre/ptlrpc/client.c
index bac4b2304bad..ca096fadb9c0 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/client.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/client.c
@@ -504,19 +504,16 @@ void ptlrpc_request_cache_free(struct ptlrpc_request *req)
*/
void ptlrpc_free_rq_pool(struct ptlrpc_request_pool *pool)
{
- struct list_head *l, *tmp;
struct ptlrpc_request *req;
- spin_lock(&pool->prp_lock);
- list_for_each_safe(l, tmp, &pool->prp_req_list) {
- req = list_entry(l, struct ptlrpc_request, rq_list);
+ while ((req = list_first_entry_or_null(&pool->prp_req_list,
+ struct ptlrpc_request, rq_list))) {
list_del(&req->rq_list);
LASSERT(req->rq_reqbuf);
LASSERT(req->rq_reqbuf_len == pool->prp_rq_size);
kvfree(req->rq_reqbuf);
ptlrpc_request_cache_free(req);
}
- spin_unlock(&pool->prp_lock);
kfree(pool);
}
EXPORT_SYMBOL(ptlrpc_free_rq_pool);
@@ -544,10 +541,10 @@ int ptlrpc_add_rqs_to_pool(struct ptlrpc_request_pool *pool, int num_rq)
struct lustre_msg *msg;
spin_unlock(&pool->prp_lock);
- req = ptlrpc_request_cache_alloc(GFP_NOFS);
+ req = ptlrpc_request_cache_alloc(GFP_KERNEL);
if (!req)
return i;
- msg = libcfs_kvzalloc(size, GFP_NOFS);
+ msg = kvzalloc(size, GFP_KERNEL);
if (!msg) {
ptlrpc_request_cache_free(req);
return i;
@@ -656,16 +653,13 @@ static void __ptlrpc_free_req_to_pool(struct ptlrpc_request *request)
void ptlrpc_add_unreplied(struct ptlrpc_request *req)
{
struct obd_import *imp = req->rq_import;
- struct list_head *tmp;
struct ptlrpc_request *iter;
assert_spin_locked(&imp->imp_lock);
LASSERT(list_empty(&req->rq_unreplied_list));
/* unreplied list is sorted by xid in ascending order */
- list_for_each_prev(tmp, &imp->imp_unreplied_list) {
- iter = list_entry(tmp, struct ptlrpc_request,
- rq_unreplied_list);
+ list_for_each_entry_reverse(iter, &imp->imp_unreplied_list, rq_unreplied_list) {
LASSERT(req->rq_xid != iter->rq_xid);
if (req->rq_xid < iter->rq_xid)
@@ -766,7 +760,7 @@ int ptlrpc_request_bufs_pack(struct ptlrpc_request *request,
* fail_loc
*/
set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(cfs_time_seconds(2));
+ schedule_timeout(2 * HZ);
set_current_state(TASK_RUNNING);
}
}
@@ -1001,18 +995,14 @@ struct ptlrpc_request_set *ptlrpc_prep_fcset(int max, set_producer_func func,
*/
void ptlrpc_set_destroy(struct ptlrpc_request_set *set)
{
- struct list_head *tmp;
- struct list_head *next;
+ struct ptlrpc_request *req;
int expected_phase;
int n = 0;
/* Requests on the set should either all be completed, or all be new */
expected_phase = (atomic_read(&set->set_remaining) == 0) ?
RQ_PHASE_COMPLETE : RQ_PHASE_NEW;
- list_for_each(tmp, &set->set_requests) {
- struct ptlrpc_request *req =
- list_entry(tmp, struct ptlrpc_request, rq_set_chain);
-
+ list_for_each_entry(req, &set->set_requests, rq_set_chain) {
LASSERT(req->rq_phase == expected_phase);
n++;
}
@@ -1021,9 +1011,9 @@ void ptlrpc_set_destroy(struct ptlrpc_request_set *set)
atomic_read(&set->set_remaining) == n, "%d / %d\n",
atomic_read(&set->set_remaining), n);
- list_for_each_safe(tmp, next, &set->set_requests) {
- struct ptlrpc_request *req =
- list_entry(tmp, struct ptlrpc_request, rq_set_chain);
+ while ((req = list_first_entry_or_null(&set->set_requests,
+ struct ptlrpc_request,
+ rq_set_chain))) {
list_del_init(&req->rq_set_chain);
LASSERT(req->rq_phase == expected_phase);
@@ -1588,7 +1578,8 @@ static int ptlrpc_send_new_req(struct ptlrpc_request *req)
spin_lock(&imp->imp_lock);
if (!list_empty(&req->rq_list)) {
list_del_init(&req->rq_list);
- atomic_dec(&req->rq_import->imp_inflight);
+ if (atomic_dec_and_test(&req->rq_import->imp_inflight))
+ wake_up_all(&req->rq_import->imp_recovery_waitq);
}
spin_unlock(&imp->imp_lock);
ptlrpc_rqphase_move(req, RQ_PHASE_NEW);
@@ -1639,7 +1630,7 @@ static inline int ptlrpc_set_producer(struct ptlrpc_request_set *set)
*/
int ptlrpc_check_set(const struct lu_env *env, struct ptlrpc_request_set *set)
{
- struct list_head *tmp, *next;
+ struct ptlrpc_request *req, *next;
struct list_head comp_reqs;
int force_timer_recalc = 0;
@@ -1647,9 +1638,7 @@ int ptlrpc_check_set(const struct lu_env *env, struct ptlrpc_request_set *set)
return 1;
INIT_LIST_HEAD(&comp_reqs);
- list_for_each_safe(tmp, next, &set->set_requests) {
- struct ptlrpc_request *req =
- list_entry(tmp, struct ptlrpc_request, rq_set_chain);
+ list_for_each_entry_safe(req, next, &set->set_requests, rq_set_chain) {
struct obd_import *imp = req->rq_import;
int unregistered = 0;
int rc = 0;
@@ -1773,7 +1762,7 @@ int ptlrpc_check_set(const struct lu_env *env, struct ptlrpc_request_set *set)
}
/*
- * ptlrpc_set_wait->l_wait_event sets lwi_allow_intr
+ * ptlrpc_set_wait allow signal to abort the timeout
* so it sets rq_intr regardless of individual rpc
* timeouts. The synchronous IO waiting path sets
* rq_intr irrespective of whether ptlrpcd
@@ -2121,19 +2110,15 @@ int ptlrpc_expire_one_request(struct ptlrpc_request *req, int async_unlink)
/**
* Time out all uncompleted requests in request set pointed by \a data
- * Callback used when waiting on sets with l_wait_event.
- * Always returns 1.
+ * Called when wait_event_idle_timeout times out.
*/
-int ptlrpc_expired_set(void *data)
+void ptlrpc_expired_set(struct ptlrpc_request_set *set)
{
- struct ptlrpc_request_set *set = data;
- struct list_head *tmp;
+ struct ptlrpc_request *req;
time64_t now = ktime_get_real_seconds();
/* A timeout expired. See which reqs it applies to... */
- list_for_each(tmp, &set->set_requests) {
- struct ptlrpc_request *req =
- list_entry(tmp, struct ptlrpc_request, rq_set_chain);
+ list_for_each_entry(req, &set->set_requests, rq_set_chain) {
/* don't expire request waiting for context */
if (req->rq_wait_ctx)
@@ -2155,13 +2140,6 @@ int ptlrpc_expired_set(void *data)
*/
ptlrpc_expire_one_request(req, 1);
}
-
- /*
- * When waiting for a whole set, we always break out of the
- * sleep so we can recalculate the timeout, or enable interrupts
- * if everyone's timed out.
- */
- return 1;
}
/**
@@ -2177,18 +2155,14 @@ EXPORT_SYMBOL(ptlrpc_mark_interrupted);
/**
* Interrupts (sets interrupted flag) all uncompleted requests in
- * a set \a data. Callback for l_wait_event for interruptible waits.
+ * a set \a data. Called when l_wait_event_abortable_timeout receives signal.
*/
-static void ptlrpc_interrupted_set(void *data)
+static void ptlrpc_interrupted_set(struct ptlrpc_request_set *set)
{
- struct ptlrpc_request_set *set = data;
- struct list_head *tmp;
-
+ struct ptlrpc_request *req;
CDEBUG(D_RPCTRACE, "INTERRUPTED SET %p\n", set);
- list_for_each(tmp, &set->set_requests) {
- struct ptlrpc_request *req =
- list_entry(tmp, struct ptlrpc_request, rq_set_chain);
+ list_for_each_entry(req, &set->set_requests, rq_set_chain) {
if (req->rq_phase != RQ_PHASE_RPC &&
req->rq_phase != RQ_PHASE_UNREG_RPC)
@@ -2203,14 +2177,12 @@ static void ptlrpc_interrupted_set(void *data)
*/
int ptlrpc_set_next_timeout(struct ptlrpc_request_set *set)
{
- struct list_head *tmp;
time64_t now = ktime_get_real_seconds();
int timeout = 0;
struct ptlrpc_request *req;
time64_t deadline;
- list_for_each(tmp, &set->set_requests) {
- req = list_entry(tmp, struct ptlrpc_request, rq_set_chain);
+ list_for_each_entry(req, &set->set_requests, rq_set_chain) {
/* Request in-flight? */
if (!(((req->rq_phase == RQ_PHASE_RPC) && !req->rq_waiting) ||
@@ -2249,17 +2221,13 @@ int ptlrpc_set_next_timeout(struct ptlrpc_request_set *set)
*/
int ptlrpc_set_wait(struct ptlrpc_request_set *set)
{
- struct list_head *tmp;
struct ptlrpc_request *req;
- struct l_wait_info lwi;
int rc, timeout;
if (set->set_producer)
(void)ptlrpc_set_producer(set);
else
- list_for_each(tmp, &set->set_requests) {
- req = list_entry(tmp, struct ptlrpc_request,
- rq_set_chain);
+ list_for_each_entry(req, &set->set_requests, rq_set_chain) {
if (req->rq_phase == RQ_PHASE_NEW)
(void)ptlrpc_send_new_req(req);
}
@@ -2277,46 +2245,47 @@ int ptlrpc_set_wait(struct ptlrpc_request_set *set)
CDEBUG(D_RPCTRACE, "set %p going to sleep for %d seconds\n",
set, timeout);
- if (timeout == 0 && !signal_pending(current))
+ if (timeout == 0 && !signal_pending(current)) {
/*
* No requests are in-flight (ether timed out
* or delayed), so we can allow interrupts.
* We still want to block for a limited time,
* so we allow interrupts during the timeout.
*/
- lwi = LWI_TIMEOUT_INTR_ALL(cfs_time_seconds(1),
- ptlrpc_expired_set,
- ptlrpc_interrupted_set, set);
- else
+ rc = l_wait_event_abortable_timeout(set->set_waitq,
+ ptlrpc_check_set(NULL, set),
+ HZ);
+ if (rc == 0) {
+ rc = -ETIMEDOUT;
+ ptlrpc_expired_set(set);
+ } else if (rc < 0) {
+ rc = -EINTR;
+ ptlrpc_interrupted_set(set);
+ } else
+ rc = 0;
+ } else {
/*
* At least one request is in flight, so no
* interrupts are allowed. Wait until all
* complete, or an in-flight req times out.
*/
- lwi = LWI_TIMEOUT(cfs_time_seconds(timeout ? timeout : 1),
- ptlrpc_expired_set, set);
-
- rc = l_wait_event(set->set_waitq, ptlrpc_check_set(NULL, set), &lwi);
-
- /*
- * LU-769 - if we ignored the signal because it was already
- * pending when we started, we need to handle it now or we risk
- * it being ignored forever
- */
- if (rc == -ETIMEDOUT && !lwi.lwi_allow_intr &&
- signal_pending(current)) {
- sigset_t blocked_sigs =
- cfs_block_sigsinv(LUSTRE_FATAL_SIGS);
-
- /*
- * In fact we only interrupt for the "fatal" signals
- * like SIGINT or SIGKILL. We still ignore less
- * important signals since ptlrpc set is not easily
- * reentrant from userspace again
- */
- if (signal_pending(current))
- ptlrpc_interrupted_set(set);
- cfs_restore_sigs(blocked_sigs);
+ rc = wait_event_idle_timeout(set->set_waitq,
+ ptlrpc_check_set(NULL, set),
+ (timeout ? timeout : 1) * HZ);
+ if (rc == 0) {
+ ptlrpc_expired_set(set);
+ rc = -ETIMEDOUT;
+ /*
+ * LU-769 - if we ignored the signal
+ * because it was already pending when
+ * we started, we need to handle it
+ * now or we risk it being ignored
+ * forever
+ */
+ if (l_fatal_signal_pending(current))
+ ptlrpc_interrupted_set(set);
+ } else
+ rc = 0;
}
LASSERT(rc == 0 || rc == -EINTR || rc == -ETIMEDOUT);
@@ -2331,9 +2300,7 @@ int ptlrpc_set_wait(struct ptlrpc_request_set *set)
* the error cases -eeb.
*/
if (rc == 0 && atomic_read(&set->set_remaining) == 0) {
- list_for_each(tmp, &set->set_requests) {
- req = list_entry(tmp, struct ptlrpc_request,
- rq_set_chain);
+ list_for_each_entry(req, &set->set_requests, rq_set_chain) {
spin_lock(&req->rq_lock);
req->rq_invalid_rqset = 1;
spin_unlock(&req->rq_lock);
@@ -2344,9 +2311,7 @@ int ptlrpc_set_wait(struct ptlrpc_request_set *set)
LASSERT(atomic_read(&set->set_remaining) == 0);
rc = set->set_rc; /* rq_status of already freed requests if any */
- list_for_each(tmp, &set->set_requests) {
- req = list_entry(tmp, struct ptlrpc_request, rq_set_chain);
-
+ list_for_each_entry(req, &set->set_requests, rq_set_chain) {
LASSERT(req->rq_phase == RQ_PHASE_COMPLETE);
if (req->rq_status != 0)
rc = req->rq_status;
@@ -2495,7 +2460,6 @@ static int ptlrpc_unregister_reply(struct ptlrpc_request *request, int async)
{
int rc;
wait_queue_head_t *wq;
- struct l_wait_info lwi;
/* Might sleep. */
LASSERT(!in_interrupt());
@@ -2524,7 +2488,7 @@ static int ptlrpc_unregister_reply(struct ptlrpc_request *request, int async)
return 0;
/*
- * We have to l_wait_event() whatever the result, to give liblustre
+ * We have to wait_event_idle_timeout() whatever the result, to give liblustre
* a chance to run reply_in_callback(), and to make sure we've
* unlinked before returning a req to the pool.
*/
@@ -2538,16 +2502,17 @@ static int ptlrpc_unregister_reply(struct ptlrpc_request *request, int async)
* Network access will complete in finite time but the HUGE
* timeout lets us CWARN for visibility of sluggish NALs
*/
- lwi = LWI_TIMEOUT_INTERVAL(cfs_time_seconds(LONG_UNLINK),
- cfs_time_seconds(1), NULL, NULL);
- rc = l_wait_event(*wq, !ptlrpc_client_recv_or_unlink(request),
- &lwi);
- if (rc == 0) {
+ int cnt = 0;
+ while (cnt < LONG_UNLINK &&
+ (rc = wait_event_idle_timeout(*wq,
+ !ptlrpc_client_recv_or_unlink(request),
+ HZ)) == 0)
+ cnt += 1;
+ if (rc > 0) {
ptlrpc_rqphase_move(request, request->rq_next_phase);
return 1;
}
- LASSERT(rc == -ETIMEDOUT);
DEBUG_REQ(D_WARNING, request,
"Unexpectedly long timeout receiving_reply=%d req_ulinked=%d reply_unlinked=%d",
request->rq_receiving_reply,
@@ -2725,8 +2690,7 @@ EXPORT_SYMBOL(ptlrpc_request_addref);
void ptlrpc_retain_replayable_request(struct ptlrpc_request *req,
struct obd_import *imp)
{
- struct list_head *tmp;
-
+ struct ptlrpc_request *iter;
assert_spin_locked(&imp->imp_lock);
if (req->rq_transno == 0) {
@@ -2753,10 +2717,7 @@ void ptlrpc_retain_replayable_request(struct ptlrpc_request *req,
LASSERT(imp->imp_replayable);
/* Balanced in ptlrpc_free_committed, usually. */
ptlrpc_request_addref(req);
- list_for_each_prev(tmp, &imp->imp_replay_list) {
- struct ptlrpc_request *iter =
- list_entry(tmp, struct ptlrpc_request, rq_replay_list);
-
+ list_for_each_entry_reverse(iter, &imp->imp_replay_list, rq_replay_list) {
/*
* We may have duplicate transnos if we create and then
* open a file, or for closes retained if to match creating
@@ -2964,7 +2925,7 @@ int ptlrpc_replay_req(struct ptlrpc_request *req)
*/
void ptlrpc_abort_inflight(struct obd_import *imp)
{
- struct list_head *tmp, *n;
+ struct ptlrpc_request *req, *n;
/*
* Make sure that no new requests get processed for this import.
@@ -2978,10 +2939,7 @@ void ptlrpc_abort_inflight(struct obd_import *imp)
* locked? Also, how do we know if the requests on the list are
* being freed at this time?
*/
- list_for_each_safe(tmp, n, &imp->imp_sending_list) {
- struct ptlrpc_request *req =
- list_entry(tmp, struct ptlrpc_request, rq_list);
-
+ list_for_each_entry_safe(req, n, &imp->imp_sending_list, rq_list) {
DEBUG_REQ(D_RPCTRACE, req, "inflight");
spin_lock(&req->rq_lock);
@@ -2993,10 +2951,7 @@ void ptlrpc_abort_inflight(struct obd_import *imp)
spin_unlock(&req->rq_lock);
}
- list_for_each_safe(tmp, n, &imp->imp_delayed_list) {
- struct ptlrpc_request *req =
- list_entry(tmp, struct ptlrpc_request, rq_list);
-
+ list_for_each_entry_safe(req, n, &imp->imp_delayed_list, rq_list) {
DEBUG_REQ(D_RPCTRACE, req, "aborting waiting req");
spin_lock(&req->rq_lock);
@@ -3023,12 +2978,9 @@ void ptlrpc_abort_inflight(struct obd_import *imp)
*/
void ptlrpc_abort_set(struct ptlrpc_request_set *set)
{
- struct list_head *tmp, *pos;
-
- list_for_each_safe(pos, tmp, &set->set_requests) {
- struct ptlrpc_request *req =
- list_entry(pos, struct ptlrpc_request, rq_set_chain);
+ struct ptlrpc_request *req, *tmp;
+ list_for_each_entry_safe(req, tmp, &set->set_requests, rq_set_chain) {
spin_lock(&req->rq_lock);
if (req->rq_phase != RQ_PHASE_RPC) {
spin_unlock(&req->rq_lock);
diff --git a/drivers/staging/lustre/lustre/ptlrpc/events.c b/drivers/staging/lustre/lustre/ptlrpc/events.c
index 811b7ab3a582..130bacc2c891 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/events.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/events.c
@@ -490,8 +490,6 @@ int ptlrpc_uuid_to_peer(struct obd_uuid *uuid,
static void ptlrpc_ni_fini(void)
{
- wait_queue_head_t waitq;
- struct l_wait_info lwi;
int rc;
int retries;
@@ -515,10 +513,7 @@ static void ptlrpc_ni_fini(void)
if (retries != 0)
CWARN("Event queue still busy\n");
- /* Wait for a bit */
- init_waitqueue_head(&waitq);
- lwi = LWI_TIMEOUT(cfs_time_seconds(2), NULL, NULL);
- l_wait_event(waitq, 0, &lwi);
+ schedule_timeout_uninterruptible(2 * HZ);
break;
}
}
diff --git a/drivers/staging/lustre/lustre/ptlrpc/import.c b/drivers/staging/lustre/lustre/ptlrpc/import.c
index 5b0f65536c29..a2c4fc3488b1 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/import.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/import.c
@@ -242,15 +242,13 @@ ptlrpc_inflight_deadline(struct ptlrpc_request *req, time64_t now)
static unsigned int ptlrpc_inflight_timeout(struct obd_import *imp)
{
time64_t now = ktime_get_real_seconds();
- struct list_head *tmp, *n;
- struct ptlrpc_request *req;
+ struct ptlrpc_request *req, *n;
unsigned int timeout = 0;
spin_lock(&imp->imp_lock);
- list_for_each_safe(tmp, n, &imp->imp_sending_list) {
- req = list_entry(tmp, struct ptlrpc_request, rq_list);
+ list_for_each_entry_safe(req, n, &imp->imp_sending_list, rq_list)
timeout = max(ptlrpc_inflight_deadline(req, now), timeout);
- }
+
spin_unlock(&imp->imp_lock);
return timeout;
}
@@ -263,9 +261,7 @@ static unsigned int ptlrpc_inflight_timeout(struct obd_import *imp)
*/
void ptlrpc_invalidate_import(struct obd_import *imp)
{
- struct list_head *tmp, *n;
- struct ptlrpc_request *req;
- struct l_wait_info lwi;
+ struct ptlrpc_request *req, *n;
unsigned int timeout;
int rc;
@@ -306,19 +302,15 @@ void ptlrpc_invalidate_import(struct obd_import *imp)
* callbacks. Cap it at obd_timeout -- these should all
* have been locally cancelled by ptlrpc_abort_inflight.
*/
- lwi = LWI_TIMEOUT_INTERVAL(
- cfs_timeout_cap(cfs_time_seconds(timeout)),
- (timeout > 1) ? cfs_time_seconds(1) :
- cfs_time_seconds(1) / 2,
- NULL, NULL);
- rc = l_wait_event(imp->imp_recovery_waitq,
- (atomic_read(&imp->imp_inflight) == 0),
- &lwi);
- if (rc) {
+ rc = wait_event_idle_timeout(imp->imp_recovery_waitq,
+ atomic_read(&imp->imp_inflight) == 0,
+ obd_timeout * HZ);
+
+ if (rc == 0) {
const char *cli_tgt = obd2cli_tgt(imp->imp_obd);
- CERROR("%s: rc = %d waiting for callback (%d != 0)\n",
- cli_tgt, rc,
+ CERROR("%s: timeout waiting for callback (%d != 0)\n",
+ cli_tgt,
atomic_read(&imp->imp_inflight));
spin_lock(&imp->imp_lock);
@@ -341,19 +333,13 @@ void ptlrpc_invalidate_import(struct obd_import *imp)
*/
rc = 0;
} else {
- list_for_each_safe(tmp, n,
- &imp->imp_sending_list) {
- req = list_entry(tmp,
- struct ptlrpc_request,
- rq_list);
+ list_for_each_entry_safe(req, n,
+ &imp->imp_sending_list, rq_list) {
DEBUG_REQ(D_ERROR, req,
"still on sending list");
}
- list_for_each_safe(tmp, n,
- &imp->imp_delayed_list) {
- req = list_entry(tmp,
- struct ptlrpc_request,
- rq_list);
+ list_for_each_entry_safe(req, n,
+ &imp->imp_delayed_list, rq_list) {
DEBUG_REQ(D_ERROR, req,
"still on delayed list");
}
@@ -365,7 +351,7 @@ void ptlrpc_invalidate_import(struct obd_import *imp)
}
spin_unlock(&imp->imp_lock);
}
- } while (rc != 0);
+ } while (rc == 0);
/*
* Let's additionally check that no new rpcs added to import in
@@ -430,21 +416,19 @@ void ptlrpc_fail_import(struct obd_import *imp, __u32 conn_cnt)
int ptlrpc_reconnect_import(struct obd_import *imp)
{
- struct l_wait_info lwi;
- int secs = cfs_time_seconds(obd_timeout);
int rc;
ptlrpc_pinger_force(imp);
CDEBUG(D_HA, "%s: recovery started, waiting %u seconds\n",
- obd2cli_tgt(imp->imp_obd), secs);
+ obd2cli_tgt(imp->imp_obd), obd_timeout);
- lwi = LWI_TIMEOUT(secs, NULL, NULL);
- rc = l_wait_event(imp->imp_recovery_waitq,
- !ptlrpc_import_in_recovery(imp), &lwi);
+ rc = wait_event_idle_timeout(imp->imp_recovery_waitq,
+ !ptlrpc_import_in_recovery(imp),
+ obd_timeout * HZ);
CDEBUG(D_HA, "%s: recovery finished s:%s\n", obd2cli_tgt(imp->imp_obd),
ptlrpc_import_state_name(imp->imp_state));
- return rc;
+ return rc == 0 ? -ETIMEDOUT : 0;
}
EXPORT_SYMBOL(ptlrpc_reconnect_import);
@@ -564,14 +548,13 @@ static int import_select_connection(struct obd_import *imp)
static int ptlrpc_first_transno(struct obd_import *imp, __u64 *transno)
{
struct ptlrpc_request *req;
- struct list_head *tmp;
/* The requests in committed_list always have smaller transnos than
* the requests in replay_list
*/
if (!list_empty(&imp->imp_committed_list)) {
- tmp = imp->imp_committed_list.next;
- req = list_entry(tmp, struct ptlrpc_request, rq_replay_list);
+ req = list_first_entry(&imp->imp_committed_list,
+ struct ptlrpc_request, rq_replay_list);
*transno = req->rq_transno;
if (req->rq_transno == 0) {
DEBUG_REQ(D_ERROR, req,
@@ -581,8 +564,8 @@ static int ptlrpc_first_transno(struct obd_import *imp, __u64 *transno)
return 1;
}
if (!list_empty(&imp->imp_replay_list)) {
- tmp = imp->imp_replay_list.next;
- req = list_entry(tmp, struct ptlrpc_request, rq_replay_list);
+ req = list_first_entry(&imp->imp_replay_list,
+ struct ptlrpc_request, rq_replay_list);
*transno = req->rq_transno;
if (req->rq_transno == 0) {
DEBUG_REQ(D_ERROR, req, "zero transno in replay_list");
@@ -1503,25 +1486,25 @@ int ptlrpc_disconnect_import(struct obd_import *imp, int noclose)
}
if (ptlrpc_import_in_recovery(imp)) {
- struct l_wait_info lwi;
long timeout;
if (AT_OFF) {
if (imp->imp_server_timeout)
- timeout = cfs_time_seconds(obd_timeout / 2);
+ timeout = obd_timeout * HZ / 2;
else
- timeout = cfs_time_seconds(obd_timeout);
+ timeout = obd_timeout * HZ;
} else {
int idx = import_at_get_index(imp,
imp->imp_client->cli_request_portal);
- timeout = cfs_time_seconds(
- at_get(&imp->imp_at.iat_service_estimate[idx]));
+ timeout = at_get(&imp->imp_at.iat_service_estimate[idx]) * HZ;
}
- lwi = LWI_TIMEOUT_INTR(cfs_timeout_cap(timeout),
- back_to_sleep, LWI_ON_SIGNAL_NOOP, NULL);
- rc = l_wait_event(imp->imp_recovery_waitq,
- !ptlrpc_import_in_recovery(imp), &lwi);
+ if (wait_event_idle_timeout(imp->imp_recovery_waitq,
+ !ptlrpc_import_in_recovery(imp),
+ cfs_timeout_cap(timeout)) == 0)
+ l_wait_event_abortable(
+ imp->imp_recovery_waitq,
+ !ptlrpc_import_in_recovery(imp));
}
spin_lock(&imp->imp_lock);
diff --git a/drivers/staging/lustre/lustre/ptlrpc/layout.c b/drivers/staging/lustre/lustre/ptlrpc/layout.c
index 18769d335751..2855f38c8190 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/layout.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/layout.c
@@ -1555,7 +1555,7 @@ struct req_format RQF_OST_GET_INFO_FIEMAP =
EXPORT_SYMBOL(RQF_OST_GET_INFO_FIEMAP);
/* Convenience macro */
-#define FMT_FIELD(fmt, i, j) (fmt)->rf_fields[(i)].d[(j)]
+#define FMT_FIELD(fmt, i, j) ((fmt)->rf_fields[(i)].d[(j)])
/**
* Initializes the capsule abstraction by computing and setting the \a rf_idx
diff --git a/drivers/staging/lustre/lustre/ptlrpc/niobuf.c b/drivers/staging/lustre/lustre/ptlrpc/niobuf.c
index 047d712e850c..86883abaad2c 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/niobuf.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/niobuf.c
@@ -229,7 +229,6 @@ int ptlrpc_unregister_bulk(struct ptlrpc_request *req, int async)
{
struct ptlrpc_bulk_desc *desc = req->rq_bulk;
wait_queue_head_t *wq;
- struct l_wait_info lwi;
int rc;
LASSERT(!in_interrupt()); /* might sleep */
@@ -246,7 +245,7 @@ int ptlrpc_unregister_bulk(struct ptlrpc_request *req, int async)
/* the unlink ensures the callback happens ASAP and is the last
* one. If it fails, it must be because completion just happened,
- * but we must still l_wait_event() in this case to give liblustre
+ * but we must still wait_event() in this case to give liblustre
* a chance to run client_bulk_callback()
*/
mdunlink_iterate_helper(desc->bd_mds, desc->bd_md_max_brw);
@@ -270,15 +269,17 @@ int ptlrpc_unregister_bulk(struct ptlrpc_request *req, int async)
/* Network access will complete in finite time but the HUGE
* timeout lets us CWARN for visibility of sluggish LNDs
*/
- lwi = LWI_TIMEOUT_INTERVAL(cfs_time_seconds(LONG_UNLINK),
- cfs_time_seconds(1), NULL, NULL);
- rc = l_wait_event(*wq, !ptlrpc_client_bulk_active(req), &lwi);
- if (rc == 0) {
+ int cnt = 0;
+ while (cnt < LONG_UNLINK &&
+ (rc = wait_event_idle_timeout(*wq,
+ !ptlrpc_client_bulk_active(req),
+ HZ)) == 0)
+ cnt += 1;
+ if (rc > 0) {
ptlrpc_rqphase_move(req, req->rq_next_phase);
return 1;
}
- LASSERT(rc == -ETIMEDOUT);
DEBUG_REQ(D_WARNING, req, "Unexpectedly long timeout: desc %p",
desc);
}
diff --git a/drivers/staging/lustre/lustre/ptlrpc/pack_generic.c b/drivers/staging/lustre/lustre/ptlrpc/pack_generic.c
index a64e125df95f..f73463ac401f 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/pack_generic.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/pack_generic.c
@@ -260,17 +260,16 @@ lustre_get_emerg_rs(struct ptlrpc_service_part *svcpt)
/* See if we have anything in a pool, and wait if nothing */
while (list_empty(&svcpt->scp_rep_idle)) {
- struct l_wait_info lwi;
int rc;
spin_unlock(&svcpt->scp_rep_lock);
/* If we cannot get anything for some long time, we better
* bail out instead of waiting infinitely
*/
- lwi = LWI_TIMEOUT(cfs_time_seconds(10), NULL, NULL);
- rc = l_wait_event(svcpt->scp_rep_waitq,
- !list_empty(&svcpt->scp_rep_idle), &lwi);
- if (rc != 0)
+ rc = wait_event_idle_timeout(svcpt->scp_rep_waitq,
+ !list_empty(&svcpt->scp_rep_idle),
+ 10 * HZ);
+ if (rc == 0)
goto out;
spin_lock(&svcpt->scp_rep_lock);
}
diff --git a/drivers/staging/lustre/lustre/ptlrpc/pinger.c b/drivers/staging/lustre/lustre/ptlrpc/pinger.c
index fe6b47bfe8be..0775b7a048bb 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/pinger.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/pinger.c
@@ -141,7 +141,7 @@ static long pinger_check_timeout(unsigned long time)
}
mutex_unlock(&pinger_mutex);
- return cfs_time_sub(cfs_time_add(time, cfs_time_seconds(timeout)),
+ return cfs_time_sub(cfs_time_add(time, timeout * HZ),
cfs_time_current());
}
@@ -217,37 +217,29 @@ static void ptlrpc_pinger_process_import(struct obd_import *imp,
}
}
-static int ptlrpc_pinger_main(void *arg)
-{
- struct ptlrpc_thread *thread = arg;
-
- /* Record that the thread is running */
- thread_set_flags(thread, SVC_RUNNING);
- wake_up(&thread->t_ctl_waitq);
+static struct workqueue_struct *pinger_wq;
+static void ptlrpc_pinger_main(struct work_struct *ws);
+static DECLARE_DELAYED_WORK(ping_work, ptlrpc_pinger_main);
- /* And now, loop forever, pinging as needed. */
- while (1) {
- unsigned long this_ping = cfs_time_current();
- struct l_wait_info lwi;
- long time_to_next_wake;
- struct timeout_item *item;
- struct list_head *iter;
+static void ptlrpc_pinger_main(struct work_struct *ws)
+{
+ unsigned long this_ping = cfs_time_current();
+ long time_to_next_wake;
+ struct timeout_item *item;
+ struct obd_import *imp;
+ do {
mutex_lock(&pinger_mutex);
list_for_each_entry(item, &timeout_list, ti_chain) {
item->ti_cb(item, item->ti_cb_data);
}
- list_for_each(iter, &pinger_imports) {
- struct obd_import *imp =
- list_entry(iter, struct obd_import,
- imp_pinger_chain);
-
+ list_for_each_entry(imp, &pinger_imports, imp_pinger_chain) {
ptlrpc_pinger_process_import(imp, this_ping);
/* obd_timeout might have changed */
if (imp->imp_pingable && imp->imp_next_ping &&
cfs_time_after(imp->imp_next_ping,
cfs_time_add(this_ping,
- cfs_time_seconds(PING_INTERVAL))))
+ PING_INTERVAL * HZ)))
ptlrpc_update_next_ping(imp, 0);
}
mutex_unlock(&pinger_mutex);
@@ -264,55 +256,25 @@ static int ptlrpc_pinger_main(void *arg)
CDEBUG(D_INFO, "next wakeup in " CFS_DURATION_T " (%ld)\n",
time_to_next_wake,
cfs_time_add(this_ping,
- cfs_time_seconds(PING_INTERVAL)));
- if (time_to_next_wake > 0) {
- lwi = LWI_TIMEOUT(max_t(long, time_to_next_wake,
- cfs_time_seconds(1)),
- NULL, NULL);
- l_wait_event(thread->t_ctl_waitq,
- thread_is_stopping(thread) ||
- thread_is_event(thread),
- &lwi);
- if (thread_test_and_clear_flags(thread, SVC_STOPPING))
- break;
- /* woken after adding import to reset timer */
- thread_test_and_clear_flags(thread, SVC_EVENT);
- }
- }
-
- thread_set_flags(thread, SVC_STOPPED);
- wake_up(&thread->t_ctl_waitq);
+ PING_INTERVAL * HZ));
+ } while (time_to_next_wake <= 0);
- CDEBUG(D_NET, "pinger thread exiting, process %d\n", current_pid());
- return 0;
+ queue_delayed_work(pinger_wq, &ping_work,
+ round_jiffies_up_relative(time_to_next_wake));
}
-static struct ptlrpc_thread pinger_thread;
-
int ptlrpc_start_pinger(void)
{
- struct l_wait_info lwi = { 0 };
- struct task_struct *task;
- int rc;
-
- if (!thread_is_init(&pinger_thread) &&
- !thread_is_stopped(&pinger_thread))
+ if (pinger_wq)
return -EALREADY;
- init_waitqueue_head(&pinger_thread.t_ctl_waitq);
-
- strcpy(pinger_thread.t_name, "ll_ping");
-
- task = kthread_run(ptlrpc_pinger_main, &pinger_thread,
- pinger_thread.t_name);
- if (IS_ERR(task)) {
- rc = PTR_ERR(task);
- CERROR("cannot start pinger thread: rc = %d\n", rc);
- return rc;
+ pinger_wq = alloc_workqueue("ptlrpc_pinger", WQ_MEM_RECLAIM, 1);
+ if (!pinger_wq) {
+ CERROR("cannot start pinger workqueue\n");
+ return -ENOMEM;
}
- l_wait_event(pinger_thread.t_ctl_waitq,
- thread_is_running(&pinger_thread), &lwi);
+ queue_delayed_work(pinger_wq, &ping_work, 0);
return 0;
}
@@ -320,19 +282,15 @@ static int ptlrpc_pinger_remove_timeouts(void);
int ptlrpc_stop_pinger(void)
{
- struct l_wait_info lwi = { 0 };
int rc = 0;
- if (thread_is_init(&pinger_thread) ||
- thread_is_stopped(&pinger_thread))
+ if (!pinger_wq)
return -EALREADY;
ptlrpc_pinger_remove_timeouts();
- thread_set_flags(&pinger_thread, SVC_STOPPING);
- wake_up(&pinger_thread.t_ctl_waitq);
-
- l_wait_event(pinger_thread.t_ctl_waitq,
- thread_is_stopped(&pinger_thread), &lwi);
+ cancel_delayed_work_sync(&ping_work);
+ destroy_workqueue(pinger_wq);
+ pinger_wq = NULL;
return rc;
}
@@ -515,6 +473,5 @@ static int ptlrpc_pinger_remove_timeouts(void)
void ptlrpc_pinger_wake_up(void)
{
- thread_add_flags(&pinger_thread, SVC_EVENT);
- wake_up(&pinger_thread.t_ctl_waitq);
+ mod_delayed_work(pinger_wq, &ping_work, 0);
}
diff --git a/drivers/staging/lustre/lustre/ptlrpc/ptlrpc_internal.h b/drivers/staging/lustre/lustre/ptlrpc/ptlrpc_internal.h
index f9decbd1459d..b7a8d7537a66 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/ptlrpc_internal.h
+++ b/drivers/staging/lustre/lustre/ptlrpc/ptlrpc_internal.h
@@ -68,7 +68,7 @@ void ptlrpc_request_cache_free(struct ptlrpc_request *req);
void ptlrpc_init_xid(void);
void ptlrpc_set_add_new_req(struct ptlrpcd_ctl *pc,
struct ptlrpc_request *req);
-int ptlrpc_expired_set(void *data);
+void ptlrpc_expired_set(struct ptlrpc_request_set *set);
int ptlrpc_set_next_timeout(struct ptlrpc_request_set *set);
void ptlrpc_resend_req(struct ptlrpc_request *request);
void ptlrpc_set_bulk_mbits(struct ptlrpc_request *req);
diff --git a/drivers/staging/lustre/lustre/ptlrpc/ptlrpc_module.c b/drivers/staging/lustre/lustre/ptlrpc/ptlrpc_module.c
index 131fc6d9646e..38923418669f 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/ptlrpc_module.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/ptlrpc_module.c
@@ -45,6 +45,42 @@ extern spinlock_t ptlrpc_last_xid_lock;
extern spinlock_t ptlrpc_rs_debug_lock;
#endif
+DEFINE_MUTEX(ptlrpc_startup);
+static int ptlrpc_active = 0;
+
+int ptlrpc_inc_ref(void)
+{
+ int rc = 0;
+
+ mutex_lock(&ptlrpc_startup);
+ if (ptlrpc_active++ == 0) {
+ ptlrpc_put_connection_superhack = ptlrpc_connection_put;
+
+ rc = ptlrpc_init_portals();
+ if (!rc) {
+ rc= ptlrpc_start_pinger();
+ if (rc)
+ ptlrpc_exit_portals();
+ }
+ if (rc)
+ ptlrpc_active--;
+ }
+ mutex_unlock(&ptlrpc_startup);
+ return rc;
+}
+EXPORT_SYMBOL(ptlrpc_inc_ref);
+
+void ptlrpc_dec_ref(void)
+{
+ mutex_lock(&ptlrpc_startup);
+ if (--ptlrpc_active == 0) {
+ ptlrpc_stop_pinger();
+ ptlrpc_exit_portals();
+ }
+ mutex_unlock(&ptlrpc_startup);
+}
+EXPORT_SYMBOL(ptlrpc_dec_ref);
+
static int __init ptlrpc_init(void)
{
int rc, cleanup_phase = 0;
@@ -71,24 +107,12 @@ static int __init ptlrpc_init(void)
if (rc)
goto cleanup;
- cleanup_phase = 2;
- rc = ptlrpc_init_portals();
- if (rc)
- goto cleanup;
-
cleanup_phase = 3;
rc = ptlrpc_connection_init();
if (rc)
goto cleanup;
- cleanup_phase = 4;
- ptlrpc_put_connection_superhack = ptlrpc_connection_put;
-
- rc = ptlrpc_start_pinger();
- if (rc)
- goto cleanup;
-
cleanup_phase = 5;
rc = ldlm_init();
if (rc)
@@ -122,15 +146,9 @@ cleanup:
ldlm_exit();
/* Fall through */
case 5:
- ptlrpc_stop_pinger();
- /* Fall through */
- case 4:
ptlrpc_connection_fini();
/* Fall through */
case 3:
- ptlrpc_exit_portals();
- /* Fall through */
- case 2:
ptlrpc_request_cache_fini();
/* Fall through */
case 1:
@@ -150,8 +168,6 @@ static void __exit ptlrpc_exit(void)
ptlrpc_nrs_fini();
sptlrpc_fini();
ldlm_exit();
- ptlrpc_stop_pinger();
- ptlrpc_exit_portals();
ptlrpc_request_cache_fini();
ptlrpc_hr_fini();
ptlrpc_connection_fini();
diff --git a/drivers/staging/lustre/lustre/ptlrpc/ptlrpcd.c b/drivers/staging/lustre/lustre/ptlrpc/ptlrpcd.c
index 8b865294d933..c0fa13942bd8 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/ptlrpcd.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/ptlrpcd.c
@@ -197,17 +197,14 @@ ptlrpcd_select_pc(struct ptlrpc_request *req)
static int ptlrpcd_steal_rqset(struct ptlrpc_request_set *des,
struct ptlrpc_request_set *src)
{
- struct list_head *tmp, *pos;
- struct ptlrpc_request *req;
+ struct ptlrpc_request *req, *tmp;
int rc = 0;
spin_lock(&src->set_new_req_lock);
if (likely(!list_empty(&src->set_new_requests))) {
- list_for_each_safe(pos, tmp, &src->set_new_requests) {
- req = list_entry(pos, struct ptlrpc_request,
- rq_set_chain);
+ list_for_each_entry_safe(req, tmp, &src->set_new_requests, rq_set_chain)
req->rq_set = des;
- }
+
list_splice_init(&src->set_new_requests, &des->set_requests);
rc = atomic_read(&src->set_new_count);
atomic_add(rc, &des->set_remaining);
@@ -230,12 +227,13 @@ void ptlrpcd_add_req(struct ptlrpc_request *req)
spin_lock(&req->rq_lock);
if (req->rq_invalid_rqset) {
- struct l_wait_info lwi = LWI_TIMEOUT(cfs_time_seconds(5),
- back_to_sleep, NULL);
-
req->rq_invalid_rqset = 0;
spin_unlock(&req->rq_lock);
- l_wait_event(req->rq_set_waitq, !req->rq_set, &lwi);
+ if (wait_event_idle_timeout(req->rq_set_waitq,
+ !req->rq_set,
+ 5 * HZ) == 0)
+ wait_event_idle(req->rq_set_waitq,
+ !req->rq_set);
} else if (req->rq_set) {
/* If we have a valid "rq_set", just reuse it to avoid double
* linked.
@@ -272,8 +270,7 @@ static inline void ptlrpc_reqset_get(struct ptlrpc_request_set *set)
*/
static int ptlrpcd_check(struct lu_env *env, struct ptlrpcd_ctl *pc)
{
- struct list_head *tmp, *pos;
- struct ptlrpc_request *req;
+ struct ptlrpc_request *req, *tmp;
struct ptlrpc_request_set *set = pc->pc_set;
int rc = 0;
int rc2;
@@ -319,8 +316,7 @@ static int ptlrpcd_check(struct lu_env *env, struct ptlrpcd_ctl *pc)
/* NB: ptlrpc_check_set has already moved completed request at the
* head of seq::set_requests
*/
- list_for_each_safe(pos, tmp, &set->set_requests) {
- req = list_entry(pos, struct ptlrpc_request, rq_set_chain);
+ list_for_each_entry_safe(req, tmp, &set->set_requests, rq_set_chain) {
if (req->rq_phase != RQ_PHASE_COMPLETE)
break;
@@ -434,16 +430,17 @@ static int ptlrpcd(void *arg)
* new_req_list and ptlrpcd_check() moves them into the set.
*/
do {
- struct l_wait_info lwi;
int timeout;
timeout = ptlrpc_set_next_timeout(set);
- lwi = LWI_TIMEOUT(cfs_time_seconds(timeout ? timeout : 1),
- ptlrpc_expired_set, set);
lu_context_enter(&env.le_ctx);
lu_context_enter(env.le_ses);
- l_wait_event(set->set_waitq, ptlrpcd_check(&env, pc), &lwi);
+ if (wait_event_idle_timeout(set->set_waitq,
+ ptlrpcd_check(&env, pc),
+ (timeout ? timeout : 1) * HZ) == 0)
+ ptlrpc_expired_set(set);
+
lu_context_exit(&env.le_ctx);
lu_context_exit(env.le_ses);
diff --git a/drivers/staging/lustre/lustre/ptlrpc/recover.c b/drivers/staging/lustre/lustre/ptlrpc/recover.c
index e4d3f23e9f3a..2ea0a7ff87dd 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/recover.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/recover.c
@@ -66,8 +66,7 @@ void ptlrpc_initiate_recovery(struct obd_import *imp)
int ptlrpc_replay_next(struct obd_import *imp, int *inflight)
{
int rc = 0;
- struct list_head *tmp, *pos;
- struct ptlrpc_request *req = NULL;
+ struct ptlrpc_request *req = NULL, *pos;
__u64 last_transno;
*inflight = 0;
@@ -86,8 +85,8 @@ int ptlrpc_replay_next(struct obd_import *imp, int *inflight)
/* Replay all the committed open requests on committed_list first */
if (!list_empty(&imp->imp_committed_list)) {
- tmp = imp->imp_committed_list.prev;
- req = list_entry(tmp, struct ptlrpc_request, rq_replay_list);
+ req = list_last_entry(&imp->imp_committed_list,
+ struct ptlrpc_request, rq_replay_list);
/* The last request on committed_list hasn't been replayed */
if (req->rq_transno > last_transno) {
@@ -119,13 +118,13 @@ int ptlrpc_replay_next(struct obd_import *imp, int *inflight)
* the imp_replay_list
*/
if (!req) {
- list_for_each_safe(tmp, pos, &imp->imp_replay_list) {
- req = list_entry(tmp, struct ptlrpc_request,
- rq_replay_list);
-
- if (req->rq_transno > last_transno)
+ struct ptlrpc_request *tmp;
+ list_for_each_entry_safe(tmp, pos, &imp->imp_replay_list,
+ rq_replay_list) {
+ if (tmp->rq_transno > last_transno) {
+ req = tmp;
break;
- req = NULL;
+ }
}
}
@@ -211,13 +210,10 @@ int ptlrpc_resend(struct obd_import *imp)
*/
void ptlrpc_wake_delayed(struct obd_import *imp)
{
- struct list_head *tmp, *pos;
- struct ptlrpc_request *req;
+ struct ptlrpc_request *req, *pos;
spin_lock(&imp->imp_lock);
- list_for_each_safe(tmp, pos, &imp->imp_delayed_list) {
- req = list_entry(tmp, struct ptlrpc_request, rq_list);
-
+ list_for_each_entry_safe(req, pos, &imp->imp_delayed_list, rq_list) {
DEBUG_REQ(D_HA, req, "waking (set %p):", req->rq_set);
ptlrpc_client_wake_req(req);
}
@@ -346,17 +342,15 @@ int ptlrpc_recover_import(struct obd_import *imp, char *new_uuid, int async)
goto out;
if (!async) {
- struct l_wait_info lwi;
- int secs = cfs_time_seconds(obd_timeout);
-
CDEBUG(D_HA, "%s: recovery started, waiting %u seconds\n",
- obd2cli_tgt(imp->imp_obd), secs);
+ obd2cli_tgt(imp->imp_obd), obd_timeout);
- lwi = LWI_TIMEOUT(secs, NULL, NULL);
- rc = l_wait_event(imp->imp_recovery_waitq,
- !ptlrpc_import_in_recovery(imp), &lwi);
+ rc = wait_event_idle_timeout(imp->imp_recovery_waitq,
+ !ptlrpc_import_in_recovery(imp),
+ obd_timeout * HZ);
CDEBUG(D_HA, "%s: recovery finished\n",
obd2cli_tgt(imp->imp_obd));
+ rc = rc ? 0 : -ETIMEDOUT;
}
out:
diff --git a/drivers/staging/lustre/lustre/ptlrpc/sec.c b/drivers/staging/lustre/lustre/ptlrpc/sec.c
index 617e004d00f8..3cb1e075f077 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/sec.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/sec.c
@@ -339,11 +339,9 @@ static int import_sec_validate_get(struct obd_import *imp,
}
*sec = sptlrpc_import_sec_ref(imp);
- /* Only output an error when the import is still active */
if (!*sec) {
- if (list_empty(&imp->imp_zombie_chain))
- CERROR("import %p (%s) with no sec\n",
- imp, ptlrpc_import_state_name(imp->imp_state));
+ CERROR("import %p (%s) with no sec\n",
+ imp, ptlrpc_import_state_name(imp->imp_state));
return -EACCES;
}
@@ -442,7 +440,7 @@ int sptlrpc_req_ctx_switch(struct ptlrpc_request *req,
/* save request message */
reqmsg_size = req->rq_reqlen;
if (reqmsg_size != 0) {
- reqmsg = libcfs_kvzalloc(reqmsg_size, GFP_NOFS);
+ reqmsg = kvzalloc(reqmsg_size, GFP_NOFS);
if (!reqmsg)
return -ENOMEM;
memcpy(reqmsg, req->rq_reqmsg, reqmsg_size);
@@ -554,9 +552,8 @@ int ctx_check_refresh(struct ptlrpc_cli_ctx *ctx)
}
static
-int ctx_refresh_timeout(void *data)
+int ctx_refresh_timeout(struct ptlrpc_request *req)
{
- struct ptlrpc_request *req = data;
int rc;
/* conn_cnt is needed in expire_one_request */
@@ -575,10 +572,8 @@ int ctx_refresh_timeout(void *data)
}
static
-void ctx_refresh_interrupt(void *data)
+void ctx_refresh_interrupt(struct ptlrpc_request *req)
{
- struct ptlrpc_request *req = data;
-
spin_lock(&req->rq_lock);
req->rq_intr = 1;
spin_unlock(&req->rq_lock);
@@ -611,7 +606,6 @@ int sptlrpc_req_refresh_ctx(struct ptlrpc_request *req, long timeout)
{
struct ptlrpc_cli_ctx *ctx = req->rq_cli_ctx;
struct ptlrpc_sec *sec;
- struct l_wait_info lwi;
int rc;
LASSERT(ctx);
@@ -743,10 +737,28 @@ again:
req->rq_restart = 0;
spin_unlock(&req->rq_lock);
- lwi = LWI_TIMEOUT_INTR(msecs_to_jiffies(timeout * MSEC_PER_SEC),
- ctx_refresh_timeout, ctx_refresh_interrupt,
- req);
- rc = l_wait_event(req->rq_reply_waitq, ctx_check_refresh(ctx), &lwi);
+ rc = wait_event_idle_timeout(req->rq_reply_waitq,
+ ctx_check_refresh(ctx),
+ timeout * HZ);
+ if (rc == 0 && ctx_refresh_timeout(req) == 0) {
+ /* Keep waiting, but enable some signals */
+ rc = l_wait_event_abortable(req->rq_reply_waitq,
+ ctx_check_refresh(ctx));
+ if (rc == 0)
+ rc = 1;
+ }
+
+ if (rc > 0)
+ /* condition is true */
+ rc = 0;
+ else if (rc == 0)
+ /* Timed out */
+ rc = -ETIMEDOUT;
+ else {
+ /* Aborted by signal */
+ rc = -EINTR;
+ ctx_refresh_interrupt(req);
+ }
/*
* following cases could lead us here:
@@ -1075,7 +1087,7 @@ int sptlrpc_cli_unwrap_early_reply(struct ptlrpc_request *req,
early_size = req->rq_nob_received;
early_bufsz = size_roundup_power2(early_size);
- early_buf = libcfs_kvzalloc(early_bufsz, GFP_NOFS);
+ early_buf = kvzalloc(early_bufsz, GFP_NOFS);
if (!early_buf) {
rc = -ENOMEM;
goto err_req;
diff --git a/drivers/staging/lustre/lustre/ptlrpc/sec_bulk.c b/drivers/staging/lustre/lustre/ptlrpc/sec_bulk.c
index 134ee727e8b7..625b9520d78f 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/sec_bulk.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/sec_bulk.c
@@ -375,9 +375,9 @@ static inline void enc_pools_alloc(void)
{
LASSERT(page_pools.epp_max_pools);
page_pools.epp_pools =
- libcfs_kvzalloc(page_pools.epp_max_pools *
+ kvzalloc(page_pools.epp_max_pools *
sizeof(*page_pools.epp_pools),
- GFP_NOFS);
+ GFP_KERNEL);
}
static inline void enc_pools_free(void)
@@ -530,7 +530,7 @@ EXPORT_SYMBOL(bulk_sec_desc_unpack);
int sptlrpc_get_bulk_checksum(struct ptlrpc_bulk_desc *desc, __u8 alg,
void *buf, int buflen)
{
- struct cfs_crypto_hash_desc *hdesc;
+ struct ahash_request *hdesc;
int hashsize;
unsigned int bufsize;
int i, err;
diff --git a/drivers/staging/lustre/lustre/ptlrpc/sec_gc.c b/drivers/staging/lustre/lustre/ptlrpc/sec_gc.c
index 8d1e0edfcede..2c8bad7b7877 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/sec_gc.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/sec_gc.c
@@ -55,7 +55,6 @@ static spinlock_t sec_gc_list_lock;
static LIST_HEAD(sec_gc_ctx_list);
static spinlock_t sec_gc_ctx_list_lock;
-static struct ptlrpc_thread sec_gc_thread;
static atomic_t sec_gc_wait_del = ATOMIC_INIT(0);
void sptlrpc_gc_add_sec(struct ptlrpc_sec *sec)
@@ -139,95 +138,53 @@ static void sec_do_gc(struct ptlrpc_sec *sec)
sec->ps_gc_next = ktime_get_real_seconds() + sec->ps_gc_interval;
}
-static int sec_gc_main(void *arg)
-{
- struct ptlrpc_thread *thread = arg;
- struct l_wait_info lwi;
-
- unshare_fs_struct();
+static void sec_gc_main(struct work_struct *ws);
+static DECLARE_DELAYED_WORK(sec_gc_work, sec_gc_main);
- /* Record that the thread is running */
- thread_set_flags(thread, SVC_RUNNING);
- wake_up(&thread->t_ctl_waitq);
-
- while (1) {
- struct ptlrpc_sec *sec;
+static void sec_gc_main(struct work_struct *ws)
+{
+ struct ptlrpc_sec *sec;
- thread_clear_flags(thread, SVC_SIGNAL);
- sec_process_ctx_list();
+ sec_process_ctx_list();
again:
- /* go through sec list do gc.
- * FIXME here we iterate through the whole list each time which
- * is not optimal. we perhaps want to use balanced binary tree
- * to trace each sec as order of expiry time.
- * another issue here is we wakeup as fixed interval instead of
- * according to each sec's expiry time
+ /* go through sec list do gc.
+ * FIXME here we iterate through the whole list each time which
+ * is not optimal. we perhaps want to use balanced binary tree
+ * to trace each sec as order of expiry time.
+ * another issue here is we wakeup as fixed interval instead of
+ * according to each sec's expiry time
+ */
+ mutex_lock(&sec_gc_mutex);
+ list_for_each_entry(sec, &sec_gc_list, ps_gc_list) {
+ /* if someone is waiting to be deleted, let it
+ * proceed as soon as possible.
*/
- mutex_lock(&sec_gc_mutex);
- list_for_each_entry(sec, &sec_gc_list, ps_gc_list) {
- /* if someone is waiting to be deleted, let it
- * proceed as soon as possible.
- */
- if (atomic_read(&sec_gc_wait_del)) {
- CDEBUG(D_SEC, "deletion pending, start over\n");
- mutex_unlock(&sec_gc_mutex);
- goto again;
- }
-
- sec_do_gc(sec);
+ if (atomic_read(&sec_gc_wait_del)) {
+ CDEBUG(D_SEC, "deletion pending, start over\n");
+ mutex_unlock(&sec_gc_mutex);
+ goto again;
}
- mutex_unlock(&sec_gc_mutex);
-
- /* check ctx list again before sleep */
- sec_process_ctx_list();
- lwi = LWI_TIMEOUT(msecs_to_jiffies(SEC_GC_INTERVAL * MSEC_PER_SEC),
- NULL, NULL);
- l_wait_event(thread->t_ctl_waitq,
- thread_is_stopping(thread) ||
- thread_is_signal(thread),
- &lwi);
-
- if (thread_test_and_clear_flags(thread, SVC_STOPPING))
- break;
+ sec_do_gc(sec);
}
+ mutex_unlock(&sec_gc_mutex);
- thread_set_flags(thread, SVC_STOPPED);
- wake_up(&thread->t_ctl_waitq);
- return 0;
+ /* check ctx list again before sleep */
+ sec_process_ctx_list();
+ schedule_delayed_work(&sec_gc_work, SEC_GC_INTERVAL * HZ);
}
int sptlrpc_gc_init(void)
{
- struct l_wait_info lwi = { 0 };
- struct task_struct *task;
-
mutex_init(&sec_gc_mutex);
spin_lock_init(&sec_gc_list_lock);
spin_lock_init(&sec_gc_ctx_list_lock);
- /* initialize thread control */
- memset(&sec_gc_thread, 0, sizeof(sec_gc_thread));
- init_waitqueue_head(&sec_gc_thread.t_ctl_waitq);
-
- task = kthread_run(sec_gc_main, &sec_gc_thread, "sptlrpc_gc");
- if (IS_ERR(task)) {
- CERROR("can't start gc thread: %ld\n", PTR_ERR(task));
- return PTR_ERR(task);
- }
-
- l_wait_event(sec_gc_thread.t_ctl_waitq,
- thread_is_running(&sec_gc_thread), &lwi);
+ schedule_delayed_work(&sec_gc_work, 0);
return 0;
}
void sptlrpc_gc_fini(void)
{
- struct l_wait_info lwi = { 0 };
-
- thread_set_flags(&sec_gc_thread, SVC_STOPPING);
- wake_up(&sec_gc_thread.t_ctl_waitq);
-
- l_wait_event(sec_gc_thread.t_ctl_waitq,
- thread_is_stopped(&sec_gc_thread), &lwi);
+ cancel_delayed_work_sync(&sec_gc_work);
}
diff --git a/drivers/staging/lustre/lustre/ptlrpc/sec_null.c b/drivers/staging/lustre/lustre/ptlrpc/sec_null.c
index 80cea0b24693..ecc387d1b9b4 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/sec_null.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/sec_null.c
@@ -158,7 +158,7 @@ int null_alloc_reqbuf(struct ptlrpc_sec *sec,
int alloc_size = size_roundup_power2(msgsize);
LASSERT(!req->rq_pool);
- req->rq_reqbuf = libcfs_kvzalloc(alloc_size, GFP_NOFS);
+ req->rq_reqbuf = kvzalloc(alloc_size, GFP_NOFS);
if (!req->rq_reqbuf)
return -ENOMEM;
@@ -201,7 +201,7 @@ int null_alloc_repbuf(struct ptlrpc_sec *sec,
msgsize = size_roundup_power2(msgsize);
- req->rq_repbuf = libcfs_kvzalloc(msgsize, GFP_NOFS);
+ req->rq_repbuf = kvzalloc(msgsize, GFP_NOFS);
if (!req->rq_repbuf)
return -ENOMEM;
@@ -246,7 +246,7 @@ int null_enlarge_reqbuf(struct ptlrpc_sec *sec,
if (req->rq_reqbuf_len < newmsg_size) {
alloc_size = size_roundup_power2(newmsg_size);
- newbuf = libcfs_kvzalloc(alloc_size, GFP_NOFS);
+ newbuf = kvzalloc(alloc_size, GFP_NOFS);
if (!newbuf)
return -ENOMEM;
@@ -317,7 +317,7 @@ int null_alloc_rs(struct ptlrpc_request *req, int msgsize)
/* pre-allocated */
LASSERT(rs->rs_size >= rs_size);
} else {
- rs = libcfs_kvzalloc(rs_size, GFP_NOFS);
+ rs = kvzalloc(rs_size, GFP_NOFS);
if (!rs)
return -ENOMEM;
diff --git a/drivers/staging/lustre/lustre/ptlrpc/sec_plain.c b/drivers/staging/lustre/lustre/ptlrpc/sec_plain.c
index 44e34056515b..ec3d9af76b17 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/sec_plain.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/sec_plain.c
@@ -562,7 +562,7 @@ int plain_alloc_reqbuf(struct ptlrpc_sec *sec,
LASSERT(!req->rq_pool);
alloc_len = size_roundup_power2(alloc_len);
- req->rq_reqbuf = libcfs_kvzalloc(alloc_len, GFP_NOFS);
+ req->rq_reqbuf = kvzalloc(alloc_len, GFP_NOFS);
if (!req->rq_reqbuf)
return -ENOMEM;
@@ -620,7 +620,7 @@ int plain_alloc_repbuf(struct ptlrpc_sec *sec,
alloc_len = size_roundup_power2(alloc_len);
- req->rq_repbuf = libcfs_kvzalloc(alloc_len, GFP_NOFS);
+ req->rq_repbuf = kvzalloc(alloc_len, GFP_NOFS);
if (!req->rq_repbuf)
return -ENOMEM;
@@ -671,7 +671,7 @@ int plain_enlarge_reqbuf(struct ptlrpc_sec *sec,
if (req->rq_reqbuf_len < newbuf_size) {
newbuf_size = size_roundup_power2(newbuf_size);
- newbuf = libcfs_kvzalloc(newbuf_size, GFP_NOFS);
+ newbuf = kvzalloc(newbuf_size, GFP_NOFS);
if (!newbuf)
return -ENOMEM;
@@ -808,7 +808,7 @@ int plain_alloc_rs(struct ptlrpc_request *req, int msgsize)
/* pre-allocated */
LASSERT(rs->rs_size >= rs_size);
} else {
- rs = libcfs_kvzalloc(rs_size, GFP_NOFS);
+ rs = kvzalloc(rs_size, GFP_NOFS);
if (!rs)
return -ENOMEM;
diff --git a/drivers/staging/lustre/lustre/ptlrpc/service.c b/drivers/staging/lustre/lustre/ptlrpc/service.c
index 63be6e7273f3..f37364e00dfe 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/service.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/service.c
@@ -83,10 +83,10 @@ ptlrpc_alloc_rqbd(struct ptlrpc_service_part *svcpt)
rqbd->rqbd_cbid.cbid_fn = request_in_callback;
rqbd->rqbd_cbid.cbid_arg = rqbd;
INIT_LIST_HEAD(&rqbd->rqbd_reqs);
- rqbd->rqbd_buffer = libcfs_kvzalloc_cpt(svc->srv_cptable,
- svcpt->scp_cpt,
- svc->srv_buf_size,
- GFP_KERNEL);
+ rqbd->rqbd_buffer = kvzalloc_node(svc->srv_buf_size, GFP_KERNEL,
+ cfs_cpt_spread_node(svc->srv_cptable,
+ svcpt->scp_cpt));
+
if (!rqbd->rqbd_buffer) {
kfree(rqbd);
return NULL;
@@ -726,8 +726,6 @@ static void ptlrpc_server_drop_request(struct ptlrpc_request *req)
struct ptlrpc_service_part *svcpt = rqbd->rqbd_svcpt;
struct ptlrpc_service *svc = svcpt->scp_service;
int refcount;
- struct list_head *tmp;
- struct list_head *nxt;
if (!atomic_dec_and_test(&req->rq_refcount))
return;
@@ -776,9 +774,7 @@ static void ptlrpc_server_drop_request(struct ptlrpc_request *req)
/* remove rqbd's reqs from svc's req history while
* I've got the service lock
*/
- list_for_each(tmp, &rqbd->rqbd_reqs) {
- req = list_entry(tmp, struct ptlrpc_request,
- rq_list);
+ list_for_each_entry(req, &rqbd->rqbd_reqs, rq_list) {
/* Track the highest culled req seq */
if (req->rq_history_seq >
svcpt->scp_hist_seq_culled) {
@@ -790,10 +786,9 @@ static void ptlrpc_server_drop_request(struct ptlrpc_request *req)
spin_unlock(&svcpt->scp_lock);
- list_for_each_safe(tmp, nxt, &rqbd->rqbd_reqs) {
- req = list_entry(rqbd->rqbd_reqs.next,
- struct ptlrpc_request,
- rq_list);
+ while ((req = list_first_entry_or_null(
+ &rqbd->rqbd_reqs,
+ struct ptlrpc_request, rq_list))) {
list_del(&req->rq_list);
ptlrpc_server_free_request(req);
}
@@ -1068,7 +1063,7 @@ static int ptlrpc_at_send_early_reply(struct ptlrpc_request *req)
reqcopy = ptlrpc_request_cache_alloc(GFP_NOFS);
if (!reqcopy)
return -ENOMEM;
- reqmsg = libcfs_kvzalloc(req->rq_reqlen, GFP_NOFS);
+ reqmsg = kvzalloc(req->rq_reqlen, GFP_NOFS);
if (!reqmsg) {
rc = -ENOMEM;
goto out_free;
@@ -1897,15 +1892,6 @@ ptlrpc_check_rqbd_pool(struct ptlrpc_service_part *svcpt)
}
}
-static int
-ptlrpc_retry_rqbds(void *arg)
-{
- struct ptlrpc_service_part *svcpt = arg;
-
- svcpt->scp_rqbd_timeout = 0;
- return -ETIMEDOUT;
-}
-
static inline int
ptlrpc_threads_enough(struct ptlrpc_service_part *svcpt)
{
@@ -1968,13 +1954,17 @@ ptlrpc_server_request_incoming(struct ptlrpc_service_part *svcpt)
return !list_empty(&svcpt->scp_req_incoming);
}
+/* We perfer lifo queuing, but kernel doesn't provide that yet. */
+#ifndef wait_event_idle_exclusive_lifo
+#define wait_event_idle_exclusive_lifo wait_event_idle_exclusive
+#define wait_event_idle_exclusive_lifo_timeout wait_event_idle_exclusive_timeout
+#endif
+
static __attribute__((__noinline__)) int
ptlrpc_wait_event(struct ptlrpc_service_part *svcpt,
struct ptlrpc_thread *thread)
{
/* Don't exit while there are replies to be handled */
- struct l_wait_info lwi = LWI_TIMEOUT(svcpt->scp_rqbd_timeout,
- ptlrpc_retry_rqbds, svcpt);
/* XXX: Add this back when libcfs watchdog is merged upstream
lc_watchdog_disable(thread->t_watchdog);
@@ -1982,13 +1972,25 @@ ptlrpc_wait_event(struct ptlrpc_service_part *svcpt,
cond_resched();
- l_wait_event_exclusive_head(svcpt->scp_waitq,
- ptlrpc_thread_stopping(thread) ||
- ptlrpc_server_request_incoming(svcpt) ||
- ptlrpc_server_request_pending(svcpt,
- false) ||
- ptlrpc_rqbd_pending(svcpt) ||
- ptlrpc_at_check(svcpt), &lwi);
+ if (svcpt->scp_rqbd_timeout == 0)
+ wait_event_idle_exclusive_lifo(
+ svcpt->scp_waitq,
+ ptlrpc_thread_stopping(thread) ||
+ ptlrpc_server_request_incoming(svcpt) ||
+ ptlrpc_server_request_pending(svcpt,
+ false) ||
+ ptlrpc_rqbd_pending(svcpt) ||
+ ptlrpc_at_check(svcpt));
+ else if (0 == wait_event_idle_exclusive_lifo_timeout(
+ svcpt->scp_waitq,
+ ptlrpc_thread_stopping(thread) ||
+ ptlrpc_server_request_incoming(svcpt) ||
+ ptlrpc_server_request_pending(svcpt,
+ false) ||
+ ptlrpc_rqbd_pending(svcpt) ||
+ ptlrpc_at_check(svcpt),
+ svcpt->scp_rqbd_timeout))
+ svcpt->scp_rqbd_timeout = 0;
if (ptlrpc_thread_stopping(thread))
return -EINTR;
@@ -2044,7 +2046,7 @@ static int ptlrpc_main(void *arg)
goto out;
}
- env = kzalloc(sizeof(*env), GFP_NOFS);
+ env = kzalloc(sizeof(*env), GFP_KERNEL);
if (!env) {
rc = -ENOMEM;
goto out_srv_fini;
@@ -2070,7 +2072,7 @@ static int ptlrpc_main(void *arg)
}
/* Alloc reply state structure for this one */
- rs = libcfs_kvzalloc(svc->srv_max_reply_size, GFP_NOFS);
+ rs = kvzalloc(svc->srv_max_reply_size, GFP_KERNEL);
if (!rs) {
rc = -ENOMEM;
goto out_srv_fini;
@@ -2149,7 +2151,7 @@ static int ptlrpc_main(void *arg)
* Wait for a timeout (unless something else
* happens) before I try again
*/
- svcpt->scp_rqbd_timeout = cfs_time_seconds(1) / 10;
+ svcpt->scp_rqbd_timeout = HZ / 10;
CDEBUG(D_RPCTRACE, "Posted buffers: %d\n",
svcpt->scp_nrqbds_posted);
}
@@ -2233,7 +2235,7 @@ static int ptlrpc_hr_main(void *arg)
wake_up(&ptlrpc_hr.hr_waitq);
while (!ptlrpc_hr.hr_stopping) {
- l_wait_condition(hrt->hrt_waitq, hrt_dont_sleep(hrt, &replies));
+ wait_event_idle(hrt->hrt_waitq, hrt_dont_sleep(hrt, &replies));
while (!list_empty(&replies)) {
struct ptlrpc_reply_state *rs;
@@ -2312,7 +2314,6 @@ static int ptlrpc_start_hr_threads(void)
static void ptlrpc_svcpt_stop_threads(struct ptlrpc_service_part *svcpt)
{
- struct l_wait_info lwi = { 0 };
struct ptlrpc_thread *thread;
LIST_HEAD(zombie);
@@ -2341,8 +2342,8 @@ static void ptlrpc_svcpt_stop_threads(struct ptlrpc_service_part *svcpt)
CDEBUG(D_INFO, "waiting for stopping-thread %s #%u\n",
svcpt->scp_service->srv_thread_name, thread->t_id);
- l_wait_event(thread->t_ctl_waitq,
- thread_is_stopped(thread), &lwi);
+ wait_event_idle(thread->t_ctl_waitq,
+ thread_is_stopped(thread));
spin_lock(&svcpt->scp_lock);
}
@@ -2403,7 +2404,6 @@ int ptlrpc_start_threads(struct ptlrpc_service *svc)
int ptlrpc_start_thread(struct ptlrpc_service_part *svcpt, int wait)
{
- struct l_wait_info lwi = { 0 };
struct ptlrpc_thread *thread;
struct ptlrpc_service *svc;
struct task_struct *task;
@@ -2499,9 +2499,8 @@ int ptlrpc_start_thread(struct ptlrpc_service_part *svcpt, int wait)
if (!wait)
return 0;
- l_wait_event(thread->t_ctl_waitq,
- thread_is_running(thread) || thread_is_stopped(thread),
- &lwi);
+ wait_event_idle(thread->t_ctl_waitq,
+ thread_is_running(thread) || thread_is_stopped(thread));
rc = thread_is_stopped(thread) ? thread->t_id : 0;
return rc;
@@ -2591,13 +2590,12 @@ static void ptlrpc_wait_replies(struct ptlrpc_service_part *svcpt)
{
while (1) {
int rc;
- struct l_wait_info lwi = LWI_TIMEOUT(cfs_time_seconds(10),
- NULL, NULL);
- rc = l_wait_event(svcpt->scp_waitq,
- atomic_read(&svcpt->scp_nreps_difficult) == 0,
- &lwi);
- if (rc == 0)
+ rc = wait_event_idle_timeout(
+ svcpt->scp_waitq,
+ atomic_read(&svcpt->scp_nreps_difficult) == 0,
+ 10 * HZ);
+ if (rc > 0)
break;
CWARN("Unexpectedly long timeout %s %p\n",
svcpt->scp_service->srv_name, svcpt->scp_service);
@@ -2622,7 +2620,7 @@ ptlrpc_service_unlink_rqbd(struct ptlrpc_service *svc)
{
struct ptlrpc_service_part *svcpt;
struct ptlrpc_request_buffer_desc *rqbd;
- struct l_wait_info lwi;
+ int cnt;
int rc;
int i;
@@ -2662,12 +2660,13 @@ ptlrpc_service_unlink_rqbd(struct ptlrpc_service *svc)
* the HUGE timeout lets us CWARN for visibility
* of sluggish LNDs
*/
- lwi = LWI_TIMEOUT_INTERVAL(
- cfs_time_seconds(LONG_UNLINK),
- cfs_time_seconds(1), NULL, NULL);
- rc = l_wait_event(svcpt->scp_waitq,
- svcpt->scp_nrqbds_posted == 0, &lwi);
- if (rc == -ETIMEDOUT) {
+ cnt = 0;
+ while (cnt < LONG_UNLINK &&
+ (rc = wait_event_idle_timeout(svcpt->scp_waitq,
+ svcpt->scp_nrqbds_posted == 0,
+ HZ)) == 0)
+ cnt++;
+ if (rc == 0) {
CWARN("Service %s waiting for request buffers\n",
svcpt->scp_service->srv_name);
}
diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig
index e68e1d343d53..4c495a10025c 100644
--- a/drivers/staging/media/Kconfig
+++ b/drivers/staging/media/Kconfig
@@ -23,12 +23,14 @@ source "drivers/staging/media/atomisp/Kconfig"
source "drivers/staging/media/bcm2048/Kconfig"
-source "drivers/staging/media/cxd2099/Kconfig"
-
source "drivers/staging/media/davinci_vpfe/Kconfig"
source "drivers/staging/media/imx/Kconfig"
+source "drivers/staging/media/imx074/Kconfig"
+
+source "drivers/staging/media/mt9t031/Kconfig"
+
source "drivers/staging/media/omap4iss/Kconfig"
source "drivers/staging/media/tegra-vde/Kconfig"
diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile
index 59a47f69884f..61a5765cb98f 100644
--- a/drivers/staging/media/Makefile
+++ b/drivers/staging/media/Makefile
@@ -1,7 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_I2C_BCM2048) += bcm2048/
-obj-$(CONFIG_DVB_CXD2099) += cxd2099/
obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/
+obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074/
+obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031/
obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/
obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/
obj-$(CONFIG_INTEL_ATOMISP) += atomisp/
diff --git a/drivers/staging/media/atomisp/i2c/Kconfig b/drivers/staging/media/atomisp/i2c/Kconfig
index db054d3c7ed6..f7f7177b9b37 100644
--- a/drivers/staging/media/atomisp/i2c/Kconfig
+++ b/drivers/staging/media/atomisp/i2c/Kconfig
@@ -28,18 +28,6 @@ config VIDEO_ATOMISP_GC2235
It currently only works with the atomisp driver.
-config VIDEO_ATOMISP_OV8858
- tristate "Omnivision ov8858 sensor support"
- depends on ACPI
- depends on I2C && VIDEO_V4L2 && VIDEO_ATOMISP
- ---help---
- This is a Video4Linux2 sensor-level driver for the Omnivision
- ov8858 RAW sensor.
-
- OV8858 is a 8M raw sensor.
-
- It currently only works with the atomisp driver.
-
config VIDEO_ATOMISP_MSRLIST_HELPER
tristate "Helper library to load, parse and apply large register lists."
depends on I2C
diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c
index 61b7598469eb..512fa87fa11b 100644
--- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c
+++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c
@@ -619,7 +619,7 @@ static const struct v4l2_ctrl_ops ctrl_ops = {
.g_volatile_ctrl = gc0310_g_volatile_ctrl
};
-struct v4l2_ctrl_config gc0310_controls[] = {
+static const struct v4l2_ctrl_config gc0310_controls[] = {
{
.ops = &ctrl_ops,
.id = V4L2_CID_EXPOSURE_ABSOLUTE,
@@ -1204,57 +1204,6 @@ fail_power_off:
return ret;
}
-static int gc0310_g_parm(struct v4l2_subdev *sd,
- struct v4l2_streamparm *param)
-{
- struct gc0310_device *dev = to_gc0310_sensor(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!param)
- return -EINVAL;
-
- if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- dev_err(&client->dev, "unsupported buffer type.\n");
- return -EINVAL;
- }
-
- memset(param, 0, sizeof(*param));
- param->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- if (dev->fmt_idx >= 0 && dev->fmt_idx < N_RES) {
- param->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
- param->parm.capture.timeperframe.numerator = 1;
- param->parm.capture.capturemode = dev->run_mode;
- param->parm.capture.timeperframe.denominator =
- gc0310_res[dev->fmt_idx].fps;
- }
- return 0;
-}
-
-static int gc0310_s_parm(struct v4l2_subdev *sd,
- struct v4l2_streamparm *param)
-{
- struct gc0310_device *dev = to_gc0310_sensor(sd);
- dev->run_mode = param->parm.capture.capturemode;
-
- mutex_lock(&dev->input_lock);
- switch (dev->run_mode) {
- case CI_MODE_VIDEO:
- gc0310_res = gc0310_res_video;
- N_RES = N_RES_VIDEO;
- break;
- case CI_MODE_STILL_CAPTURE:
- gc0310_res = gc0310_res_still;
- N_RES = N_RES_STILL;
- break;
- default:
- gc0310_res = gc0310_res_preview;
- N_RES = N_RES_PREVIEW;
- }
- mutex_unlock(&dev->input_lock);
- return 0;
-}
-
static int gc0310_g_frame_interval(struct v4l2_subdev *sd,
struct v4l2_subdev_frame_interval *interval)
{
@@ -1313,8 +1262,6 @@ static const struct v4l2_subdev_sensor_ops gc0310_sensor_ops = {
static const struct v4l2_subdev_video_ops gc0310_video_ops = {
.s_stream = gc0310_s_stream,
- .g_parm = gc0310_g_parm,
- .s_parm = gc0310_s_parm,
.g_frame_interval = gc0310_g_frame_interval,
};
diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c
index d8de46da64ae..93f9c618f3d8 100644
--- a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c
+++ b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c
@@ -944,57 +944,6 @@ fail_power_off:
return ret;
}
-static int gc2235_g_parm(struct v4l2_subdev *sd,
- struct v4l2_streamparm *param)
-{
- struct gc2235_device *dev = to_gc2235_sensor(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!param)
- return -EINVAL;
-
- if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- dev_err(&client->dev, "unsupported buffer type.\n");
- return -EINVAL;
- }
-
- memset(param, 0, sizeof(*param));
- param->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- if (dev->fmt_idx >= 0 && dev->fmt_idx < N_RES) {
- param->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
- param->parm.capture.timeperframe.numerator = 1;
- param->parm.capture.capturemode = dev->run_mode;
- param->parm.capture.timeperframe.denominator =
- gc2235_res[dev->fmt_idx].fps;
- }
- return 0;
-}
-
-static int gc2235_s_parm(struct v4l2_subdev *sd,
- struct v4l2_streamparm *param)
-{
- struct gc2235_device *dev = to_gc2235_sensor(sd);
- dev->run_mode = param->parm.capture.capturemode;
-
- mutex_lock(&dev->input_lock);
- switch (dev->run_mode) {
- case CI_MODE_VIDEO:
- gc2235_res = gc2235_res_video;
- N_RES = N_RES_VIDEO;
- break;
- case CI_MODE_STILL_CAPTURE:
- gc2235_res = gc2235_res_still;
- N_RES = N_RES_STILL;
- break;
- default:
- gc2235_res = gc2235_res_preview;
- N_RES = N_RES_PREVIEW;
- }
- mutex_unlock(&dev->input_lock);
- return 0;
-}
-
static int gc2235_g_frame_interval(struct v4l2_subdev *sd,
struct v4l2_subdev_frame_interval *interval)
{
@@ -1052,8 +1001,6 @@ static const struct v4l2_subdev_sensor_ops gc2235_sensor_ops = {
static const struct v4l2_subdev_video_ops gc2235_video_ops = {
.s_stream = gc2235_s_stream,
- .g_parm = gc2235_g_parm,
- .s_parm = gc2235_s_parm,
.g_frame_interval = gc2235_g_frame_interval,
};
diff --git a/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c b/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c
index df253a557c76..44db9f9f1fc5 100644
--- a/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c
+++ b/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c
@@ -107,7 +107,7 @@ mt9m114_write_reg(struct i2c_client *client, u16 data_length, u16 reg, u32 val)
int num_msg;
struct i2c_msg msg;
unsigned char data[6] = {0};
- u16 *wreg;
+ __be16 *wreg;
int retry = 0;
if (!client->adapter) {
@@ -130,18 +130,20 @@ again:
msg.buf = data;
/* high byte goes out first */
- wreg = (u16 *)data;
+ wreg = (void *)data;
*wreg = cpu_to_be16(reg);
if (data_length == MISENSOR_8BIT) {
data[2] = (u8)(val);
} else if (data_length == MISENSOR_16BIT) {
- u16 *wdata = (u16 *)&data[2];
- *wdata = be16_to_cpu((u16)val);
+ u16 *wdata = (void *)&data[2];
+
+ *wdata = be16_to_cpu(*(__be16 *)&data[2]);
} else {
/* MISENSOR_32BIT */
- u32 *wdata = (u32 *)&data[2];
- *wdata = be32_to_cpu(val);
+ u32 *wdata = (void *)&data[2];
+
+ *wdata = be32_to_cpu(*(__be32 *)&data[2]);
}
num_msg = i2c_transfer(client->adapter, &msg, 1);
@@ -245,6 +247,7 @@ static int __mt9m114_flush_reg_array(struct i2c_client *client,
const int num_msg = 1;
int ret;
int retry = 0;
+ __be16 *data16 = (void *)&ctrl->buffer.addr;
if (ctrl->index == 0)
return 0;
@@ -253,7 +256,7 @@ again:
msg.addr = client->addr;
msg.flags = 0;
msg.len = 2 + ctrl->index;
- ctrl->buffer.addr = cpu_to_be16(ctrl->buffer.addr);
+ *data16 = cpu_to_be16(ctrl->buffer.addr);
msg.buf = (u8 *)&ctrl->buffer;
ret = i2c_transfer(client->adapter, &msg, num_msg);
@@ -282,8 +285,8 @@ static int __mt9m114_buf_reg_array(struct i2c_client *client,
struct mt9m114_write_ctrl *ctrl,
const struct misensor_reg *next)
{
- u16 *data16;
- u32 *data32;
+ __be16 *data16;
+ __be32 *data32;
int err;
/* Insufficient buffer? Let's flush and get more free space. */
@@ -298,11 +301,11 @@ static int __mt9m114_buf_reg_array(struct i2c_client *client,
ctrl->buffer.data[ctrl->index] = (u8)next->val;
break;
case MISENSOR_16BIT:
- data16 = (u16 *)&ctrl->buffer.data[ctrl->index];
+ data16 = (__be16 *)&ctrl->buffer.data[ctrl->index];
*data16 = cpu_to_be16((u16)next->val);
break;
case MISENSOR_32BIT:
- data32 = (u32 *)&ctrl->buffer.data[ctrl->index];
+ data32 = (__be32 *)&ctrl->buffer.data[ctrl->index];
*data32 = cpu_to_be32(next->val);
break;
default:
@@ -1684,11 +1687,6 @@ static int mt9m114_t_vflip(struct v4l2_subdev *sd, int value)
return !!err;
}
-static int mt9m114_s_parm(struct v4l2_subdev *sd,
- struct v4l2_streamparm *param)
-{
- return 0;
-}
static int mt9m114_g_frame_interval(struct v4l2_subdev *sd,
struct v4l2_subdev_frame_interval *interval)
@@ -1781,7 +1779,6 @@ static int mt9m114_g_skip_frames(struct v4l2_subdev *sd, u32 *frames)
}
static const struct v4l2_subdev_video_ops mt9m114_video_ops = {
- .s_parm = mt9m114_s_parm,
.s_stream = mt9m114_s_stream,
.g_frame_interval = mt9m114_g_frame_interval,
};
diff --git a/drivers/staging/media/atomisp/i2c/atomisp-ov2680.c b/drivers/staging/media/atomisp/i2c/atomisp-ov2680.c
index 84f8d33ce2d1..c0849299d592 100644
--- a/drivers/staging/media/atomisp/i2c/atomisp-ov2680.c
+++ b/drivers/staging/media/atomisp/i2c/atomisp-ov2680.c
@@ -94,9 +94,9 @@ static int ov2680_read_reg(struct i2c_client *client,
if (data_length == OV2680_8BIT)
*val = (u8)data[0];
else if (data_length == OV2680_16BIT)
- *val = be16_to_cpu(*(u16 *)&data[0]);
+ *val = be16_to_cpu(*(__be16 *)&data[0]);
else
- *val = be32_to_cpu(*(u32 *)&data[0]);
+ *val = be32_to_cpu(*(__be32 *)&data[0]);
//dev_dbg(&client->dev, "++++i2c read adr%x = %x\n", reg,*val);
return 0;
}
@@ -121,7 +121,7 @@ static int ov2680_write_reg(struct i2c_client *client, u16 data_length,
{
int ret;
unsigned char data[4] = {0};
- u16 *wreg = (u16 *)data;
+ __be16 *wreg = (void *)data;
const u16 len = data_length + sizeof(u16); /* 16-bit address + data */
if (data_length != OV2680_8BIT && data_length != OV2680_16BIT) {
@@ -137,7 +137,8 @@ static int ov2680_write_reg(struct i2c_client *client, u16 data_length,
data[2] = (u8)(val);
} else {
/* OV2680_16BIT */
- u16 *wdata = (u16 *)&data[2];
+ __be16 *wdata = (void *)&data[2];
+
*wdata = cpu_to_be16(val);
}
@@ -169,12 +170,13 @@ static int __ov2680_flush_reg_array(struct i2c_client *client,
struct ov2680_write_ctrl *ctrl)
{
u16 size;
+ __be16 *data16 = (void *)&ctrl->buffer.addr;
if (ctrl->index == 0)
return 0;
size = sizeof(u16) + ctrl->index; /* 16-bit address + data */
- ctrl->buffer.addr = cpu_to_be16(ctrl->buffer.addr);
+ *data16 = cpu_to_be16(ctrl->buffer.addr);
ctrl->index = 0;
return ov2680_i2c_write(client, size, (u8 *)&ctrl->buffer);
@@ -185,7 +187,7 @@ static int __ov2680_buf_reg_array(struct i2c_client *client,
const struct ov2680_reg *next)
{
int size;
- u16 *data16;
+ __be16 *data16;
switch (next->type) {
case OV2680_8BIT:
@@ -194,7 +196,7 @@ static int __ov2680_buf_reg_array(struct i2c_client *client,
break;
case OV2680_16BIT:
size = 2;
- data16 = (u16 *)&ctrl->buffer.data[ctrl->index];
+ data16 = (void *)&ctrl->buffer.data[ctrl->index];
*data16 = cpu_to_be16((u16)next->val);
break;
default:
@@ -722,7 +724,7 @@ static const struct v4l2_ctrl_ops ctrl_ops = {
.g_volatile_ctrl = ov2680_g_volatile_ctrl
};
-struct v4l2_ctrl_config ov2680_controls[] = {
+static const struct v4l2_ctrl_config ov2680_controls[] = {
{
.ops = &ctrl_ops,
.id = V4L2_CID_EXPOSURE_ABSOLUTE,
@@ -1280,60 +1282,6 @@ fail_power_off:
return ret;
}
-static int ov2680_g_parm(struct v4l2_subdev *sd,
- struct v4l2_streamparm *param)
-{
- struct ov2680_device *dev = to_ov2680_sensor(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!param)
- return -EINVAL;
-
- if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- dev_err(&client->dev, "unsupported buffer type.\n");
- return -EINVAL;
- }
-
- memset(param, 0, sizeof(*param));
- param->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- if (dev->fmt_idx >= 0 && dev->fmt_idx < N_RES) {
- param->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
- param->parm.capture.timeperframe.numerator = 1;
- param->parm.capture.capturemode = dev->run_mode;
- param->parm.capture.timeperframe.denominator =
- ov2680_res[dev->fmt_idx].fps;
- }
- return 0;
-}
-
-static int ov2680_s_parm(struct v4l2_subdev *sd,
- struct v4l2_streamparm *param)
-{
- struct ov2680_device *dev = to_ov2680_sensor(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- dev->run_mode = param->parm.capture.capturemode;
-
- v4l2_info(client, "\n%s:run_mode :%x\n", __func__, dev->run_mode);
-
- mutex_lock(&dev->input_lock);
- switch (dev->run_mode) {
- case CI_MODE_VIDEO:
- ov2680_res = ov2680_res_video;
- N_RES = N_RES_VIDEO;
- break;
- case CI_MODE_STILL_CAPTURE:
- ov2680_res = ov2680_res_still;
- N_RES = N_RES_STILL;
- break;
- default:
- ov2680_res = ov2680_res_preview;
- N_RES = N_RES_PREVIEW;
- }
- mutex_unlock(&dev->input_lock);
- return 0;
-}
-
static int ov2680_g_frame_interval(struct v4l2_subdev *sd,
struct v4l2_subdev_frame_interval *interval)
{
@@ -1387,8 +1335,6 @@ static int ov2680_g_skip_frames(struct v4l2_subdev *sd, u32 *frames)
static const struct v4l2_subdev_video_ops ov2680_video_ops = {
.s_stream = ov2680_s_stream,
- .g_parm = ov2680_g_parm,
- .s_parm = ov2680_s_parm,
.g_frame_interval = ov2680_g_frame_interval,
};
diff --git a/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c b/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c
index 2b6ae0faf972..a362eebd882f 100644
--- a/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c
+++ b/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c
@@ -85,9 +85,9 @@ static int ov2722_read_reg(struct i2c_client *client,
if (data_length == OV2722_8BIT)
*val = (u8)data[0];
else if (data_length == OV2722_16BIT)
- *val = be16_to_cpu(*(u16 *)&data[0]);
+ *val = be16_to_cpu(*(__be16 *)&data[0]);
else
- *val = be32_to_cpu(*(u32 *)&data[0]);
+ *val = be32_to_cpu(*(__be32 *)&data[0]);
return 0;
}
@@ -112,7 +112,7 @@ static int ov2722_write_reg(struct i2c_client *client, u16 data_length,
{
int ret;
unsigned char data[4] = {0};
- u16 *wreg = (u16 *)data;
+ __be16 *wreg = (__be16 *)data;
const u16 len = data_length + sizeof(u16); /* 16-bit address + data */
if (data_length != OV2722_8BIT && data_length != OV2722_16BIT) {
@@ -128,7 +128,8 @@ static int ov2722_write_reg(struct i2c_client *client, u16 data_length,
data[2] = (u8)(val);
} else {
/* OV2722_16BIT */
- u16 *wdata = (u16 *)&data[2];
+ __be16 *wdata = (__be16 *)&data[2];
+
*wdata = cpu_to_be16(val);
}
@@ -160,12 +161,13 @@ static int __ov2722_flush_reg_array(struct i2c_client *client,
struct ov2722_write_ctrl *ctrl)
{
u16 size;
+ __be16 *data16 = (void *)&ctrl->buffer.addr;
if (ctrl->index == 0)
return 0;
size = sizeof(u16) + ctrl->index; /* 16-bit address + data */
- ctrl->buffer.addr = cpu_to_be16(ctrl->buffer.addr);
+ *data16 = cpu_to_be16(ctrl->buffer.addr);
ctrl->index = 0;
return ov2722_i2c_write(client, size, (u8 *)&ctrl->buffer);
@@ -176,7 +178,7 @@ static int __ov2722_buf_reg_array(struct i2c_client *client,
const struct ov2722_reg *next)
{
int size;
- u16 *data16;
+ __be16 *data16;
switch (next->type) {
case OV2722_8BIT:
@@ -185,7 +187,7 @@ static int __ov2722_buf_reg_array(struct i2c_client *client,
break;
case OV2722_16BIT:
size = 2;
- data16 = (u16 *)&ctrl->buffer.data[ctrl->index];
+ data16 = (void *)&ctrl->buffer.data[ctrl->index];
*data16 = cpu_to_be16((u16)next->val);
break;
default:
@@ -569,7 +571,7 @@ static const struct v4l2_ctrl_ops ctrl_ops = {
.g_volatile_ctrl = ov2722_g_volatile_ctrl
};
-struct v4l2_ctrl_config ov2722_controls[] = {
+static const struct v4l2_ctrl_config ov2722_controls[] = {
{
.ops = &ctrl_ops,
.id = V4L2_CID_EXPOSURE_ABSOLUTE,
@@ -1083,57 +1085,6 @@ fail_power_off:
return ret;
}
-static int ov2722_g_parm(struct v4l2_subdev *sd,
- struct v4l2_streamparm *param)
-{
- struct ov2722_device *dev = to_ov2722_sensor(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!param)
- return -EINVAL;
-
- if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- dev_err(&client->dev, "unsupported buffer type.\n");
- return -EINVAL;
- }
-
- memset(param, 0, sizeof(*param));
- param->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- if (dev->fmt_idx >= 0 && dev->fmt_idx < N_RES) {
- param->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
- param->parm.capture.timeperframe.numerator = 1;
- param->parm.capture.capturemode = dev->run_mode;
- param->parm.capture.timeperframe.denominator =
- ov2722_res[dev->fmt_idx].fps;
- }
- return 0;
-}
-
-static int ov2722_s_parm(struct v4l2_subdev *sd,
- struct v4l2_streamparm *param)
-{
- struct ov2722_device *dev = to_ov2722_sensor(sd);
- dev->run_mode = param->parm.capture.capturemode;
-
- mutex_lock(&dev->input_lock);
- switch (dev->run_mode) {
- case CI_MODE_VIDEO:
- ov2722_res = ov2722_res_video;
- N_RES = N_RES_VIDEO;
- break;
- case CI_MODE_STILL_CAPTURE:
- ov2722_res = ov2722_res_still;
- N_RES = N_RES_STILL;
- break;
- default:
- ov2722_res = ov2722_res_preview;
- N_RES = N_RES_PREVIEW;
- }
- mutex_unlock(&dev->input_lock);
- return 0;
-}
-
static int ov2722_g_frame_interval(struct v4l2_subdev *sd,
struct v4l2_subdev_frame_interval *interval)
{
@@ -1192,8 +1143,6 @@ static const struct v4l2_subdev_sensor_ops ov2722_sensor_ops = {
static const struct v4l2_subdev_video_ops ov2722_video_ops = {
.s_stream = ov2722_s_stream,
- .g_parm = ov2722_g_parm,
- .s_parm = ov2722_s_parm,
.g_frame_interval = ov2722_g_frame_interval,
};
diff --git a/drivers/staging/media/atomisp/i2c/gc0310.h b/drivers/staging/media/atomisp/i2c/gc0310.h
index c422d0398fc7..70c252c5163c 100644
--- a/drivers/staging/media/atomisp/i2c/gc0310.h
+++ b/drivers/staging/media/atomisp/i2c/gc0310.h
@@ -150,7 +150,6 @@ struct gc0310_device {
struct camera_sensor_platform_data *platform_data;
int vt_pix_clk_freq_mhz;
int fmt_idx;
- int run_mode;
u8 res;
u8 type;
};
@@ -378,8 +377,7 @@ static struct gc0310_reg const gc0310_VGA_30fps[] = {
{GC0310_TOK_TERM, 0, 0},
};
-
-struct gc0310_resolution gc0310_res_preview[] = {
+static struct gc0310_resolution gc0310_res_preview[] = {
{
.desc = "gc0310_VGA_30fps",
.width = 656, // 648,
@@ -400,48 +398,6 @@ struct gc0310_resolution gc0310_res_preview[] = {
};
#define N_RES_PREVIEW (ARRAY_SIZE(gc0310_res_preview))
-struct gc0310_resolution gc0310_res_still[] = {
- {
- .desc = "gc0310_VGA_30fps",
- .width = 656, // 648,
- .height = 496, // 488,
- .fps = 30,
- //.pix_clk_freq = 73,
- .used = 0,
-#if 0
- .pixels_per_line = 0x0314,
- .lines_per_frame = 0x0213,
-#endif
- .bin_factor_x = 1,
- .bin_factor_y = 1,
- .bin_mode = 0,
- .skip_frames = 2,
- .regs = gc0310_VGA_30fps,
- },
-};
-#define N_RES_STILL (ARRAY_SIZE(gc0310_res_still))
-
-struct gc0310_resolution gc0310_res_video[] = {
- {
- .desc = "gc0310_VGA_30fps",
- .width = 656, // 648,
- .height = 496, // 488,
- .fps = 30,
- //.pix_clk_freq = 73,
- .used = 0,
-#if 0
- .pixels_per_line = 0x0314,
- .lines_per_frame = 0x0213,
-#endif
- .bin_factor_x = 1,
- .bin_factor_y = 1,
- .bin_mode = 0,
- .skip_frames = 2,
- .regs = gc0310_VGA_30fps,
- },
-};
-#define N_RES_VIDEO (ARRAY_SIZE(gc0310_res_video))
-
static struct gc0310_resolution *gc0310_res = gc0310_res_preview;
static unsigned long N_RES = N_RES_PREVIEW;
#endif
diff --git a/drivers/staging/media/atomisp/i2c/gc2235.h b/drivers/staging/media/atomisp/i2c/gc2235.h
index 3c30a05c3991..0e805bcfa4d8 100644
--- a/drivers/staging/media/atomisp/i2c/gc2235.h
+++ b/drivers/staging/media/atomisp/i2c/gc2235.h
@@ -156,7 +156,6 @@ struct gc2235_device {
struct camera_sensor_platform_data *platform_data;
int vt_pix_clk_freq_mhz;
int fmt_idx;
- int run_mode;
u8 res;
u8 type;
};
@@ -575,6 +574,11 @@ static struct gc2235_resolution gc2235_res_preview[] = {
};
#define N_RES_PREVIEW (ARRAY_SIZE(gc2235_res_preview))
+/*
+ * Disable non-preview configurations until the configuration selection is
+ * improved.
+ */
+#if 0
static struct gc2235_resolution gc2235_res_still[] = {
{
.desc = "gc2235_1600_900_30fps",
@@ -659,6 +663,7 @@ static struct gc2235_resolution gc2235_res_video[] = {
};
#define N_RES_VIDEO (ARRAY_SIZE(gc2235_res_video))
+#endif
static struct gc2235_resolution *gc2235_res = gc2235_res_preview;
static unsigned long N_RES = N_RES_PREVIEW;
diff --git a/drivers/staging/media/atomisp/i2c/ov2680.h b/drivers/staging/media/atomisp/i2c/ov2680.h
index 03f75dd80f87..cb38e6e79409 100644
--- a/drivers/staging/media/atomisp/i2c/ov2680.h
+++ b/drivers/staging/media/atomisp/i2c/ov2680.h
@@ -850,74 +850,6 @@ struct ov2680_format {
};
#define N_RES_PREVIEW (ARRAY_SIZE(ov2680_res_preview))
-static struct ov2680_resolution ov2680_res_still[] = {
- {
- .desc = "ov2680_1616x1216_30fps",
- .width = 1616,
- .height = 1216,
- .pix_clk_freq = 66,
- .fps = 30,
- .used = 0,
- .pixels_per_line = 1698,//1704,
- .lines_per_frame = 1294,
- .bin_factor_x = 0,
- .bin_factor_y = 0,
- .bin_mode = 0,
- .skip_frames = 3,
- .regs = ov2680_1616x1216_30fps,
- },
- {
- .desc = "ov2680_1616x916_30fps",
- .width = 1616,
- .height = 916,
- .fps = 30,
- .pix_clk_freq = 66,
- .used = 0,
- .pixels_per_line = 1698,//1704,
- .lines_per_frame = 1294,
- .bin_factor_x = 0,
- .bin_factor_y = 0,
- .bin_mode = 0,
- .skip_frames = 3,
- .regs = ov2680_1616x916_30fps,
- },
-};
-#define N_RES_STILL (ARRAY_SIZE(ov2680_res_still))
-
-static struct ov2680_resolution ov2680_res_video[] = {
- {
- .desc = "ov2680_1616x1216_30fps",
- .width = 1616,
- .height = 1216,
- .pix_clk_freq = 66,
- .fps = 30,
- .used = 0,
- .pixels_per_line = 1698,//1704,
- .lines_per_frame = 1294,
- .bin_factor_x = 0,
- .bin_factor_y = 0,
- .bin_mode = 0,
- .skip_frames = 3,
- .regs = ov2680_1616x1216_30fps,
- },
- {
- .desc = "ov2680_720p_30fps",
- .width = 1616,
- .height = 916,
- .fps = 30,
- .pix_clk_freq = 66,
- .used = 0,
- .pixels_per_line = 1698,//1704,
- .lines_per_frame = 1294,
- .bin_factor_x = 0,
- .bin_factor_y = 0,
- .bin_mode = 0,
- .skip_frames = 3,
- .regs = ov2680_1616x916_30fps,
- },
-};
-#define N_RES_VIDEO (ARRAY_SIZE(ov2680_res_video))
-
static struct ov2680_resolution *ov2680_res = ov2680_res_preview;
static unsigned long N_RES = N_RES_PREVIEW;
diff --git a/drivers/staging/media/atomisp/i2c/ov2722.h b/drivers/staging/media/atomisp/i2c/ov2722.h
index d8a973d71699..757b37613ccc 100644
--- a/drivers/staging/media/atomisp/i2c/ov2722.h
+++ b/drivers/staging/media/atomisp/i2c/ov2722.h
@@ -1096,7 +1096,7 @@ static struct ov2722_reg const ov2722_720p_30fps[] = {
{OV2722_TOK_TERM, 0, 0},
};
-struct ov2722_resolution ov2722_res_preview[] = {
+static struct ov2722_resolution ov2722_res_preview[] = {
{
.desc = "ov2722_1632_1092_30fps",
.width = 1632,
@@ -1148,6 +1148,11 @@ struct ov2722_resolution ov2722_res_preview[] = {
};
#define N_RES_PREVIEW (ARRAY_SIZE(ov2722_res_preview))
+/*
+ * Disable non-preview configurations until the configuration selection is
+ * improved.
+ */
+#if 0
struct ov2722_resolution ov2722_res_still[] = {
{
.desc = "ov2722_480P_30fps",
@@ -1250,6 +1255,7 @@ struct ov2722_resolution ov2722_res_video[] = {
},
};
#define N_RES_VIDEO (ARRAY_SIZE(ov2722_res_video))
+#endif
static struct ov2722_resolution *ov2722_res = ov2722_res_preview;
static unsigned long N_RES = N_RES_PREVIEW;
diff --git a/drivers/staging/media/atomisp/i2c/ov5693/atomisp-ov5693.c b/drivers/staging/media/atomisp/i2c/ov5693/atomisp-ov5693.c
index 40d01bf4bf28..714297c36b3e 100644
--- a/drivers/staging/media/atomisp/i2c/ov5693/atomisp-ov5693.c
+++ b/drivers/staging/media/atomisp/i2c/ov5693/atomisp-ov5693.c
@@ -173,9 +173,9 @@ static int ov5693_read_reg(struct i2c_client *client,
if (data_length == OV5693_8BIT)
*val = (u8)data[0];
else if (data_length == OV5693_16BIT)
- *val = be16_to_cpu(*(u16 *)&data[0]);
+ *val = be16_to_cpu(*(__be16 *)&data[0]);
else
- *val = be32_to_cpu(*(u32 *)&data[0]);
+ *val = be32_to_cpu(*(__be32 *)&data[0]);
return 0;
}
@@ -200,13 +200,13 @@ static int vcm_dw_i2c_write(struct i2c_client *client, u16 data)
struct i2c_msg msg;
const int num_msg = 1;
int ret;
- u16 val;
+ __be16 val;
val = cpu_to_be16(data);
msg.addr = VCM_ADDR;
msg.flags = 0;
msg.len = OV5693_16BIT;
- msg.buf = (u8 *)&val;
+ msg.buf = (void *)&val;
ret = i2c_transfer(client->adapter, &msg, 1);
@@ -263,7 +263,7 @@ static int ov5693_write_reg(struct i2c_client *client, u16 data_length,
{
int ret;
unsigned char data[4] = {0};
- u16 *wreg = (u16 *)data;
+ __be16 *wreg = (void *)data;
const u16 len = data_length + sizeof(u16); /* 16-bit address + data */
if (data_length != OV5693_8BIT && data_length != OV5693_16BIT) {
@@ -279,7 +279,8 @@ static int ov5693_write_reg(struct i2c_client *client, u16 data_length,
data[2] = (u8)(val);
} else {
/* OV5693_16BIT */
- u16 *wdata = (u16 *)&data[2];
+ __be16 *wdata = (void *)&data[2];
+
*wdata = cpu_to_be16(val);
}
@@ -311,15 +312,17 @@ static int __ov5693_flush_reg_array(struct i2c_client *client,
struct ov5693_write_ctrl *ctrl)
{
u16 size;
+ __be16 *reg = (void *)&ctrl->buffer.addr;
if (ctrl->index == 0)
return 0;
size = sizeof(u16) + ctrl->index; /* 16-bit address + data */
- ctrl->buffer.addr = cpu_to_be16(ctrl->buffer.addr);
+
+ *reg = cpu_to_be16(ctrl->buffer.addr);
ctrl->index = 0;
- return ov5693_i2c_write(client, size, (u8 *)&ctrl->buffer);
+ return ov5693_i2c_write(client, size, (u8 *)reg);
}
static int __ov5693_buf_reg_array(struct i2c_client *client,
@@ -327,7 +330,7 @@ static int __ov5693_buf_reg_array(struct i2c_client *client,
const struct ov5693_reg *next)
{
int size;
- u16 *data16;
+ __be16 *data16;
switch (next->type) {
case OV5693_8BIT:
@@ -336,7 +339,8 @@ static int __ov5693_buf_reg_array(struct i2c_client *client,
break;
case OV5693_16BIT:
size = 2;
- data16 = (u16 *)&ctrl->buffer.data[ctrl->index];
+
+ data16 = (void *)&ctrl->buffer.data[ctrl->index];
*data16 = cpu_to_be16((u16)next->val);
break;
default:
@@ -951,7 +955,7 @@ static int ad5823_t_focus_vcm(struct v4l2_subdev *sd, u16 val)
return ret;
}
-int ad5823_t_focus_abs(struct v4l2_subdev *sd, s32 value)
+static int ad5823_t_focus_abs(struct v4l2_subdev *sd, s32 value)
{
value = min(value, AD5823_MAX_FOCUS_POS);
return ad5823_t_focus_vcm(sd, value);
@@ -1132,7 +1136,7 @@ static const struct v4l2_ctrl_ops ctrl_ops = {
.g_volatile_ctrl = ov5693_g_volatile_ctrl
};
-struct v4l2_ctrl_config ov5693_controls[] = {
+static const struct v4l2_ctrl_config ov5693_controls[] = {
{
.ops = &ctrl_ops,
.id = V4L2_CID_EXPOSURE_ABSOLUTE,
@@ -1805,58 +1809,6 @@ fail_power_off:
return ret;
}
-static int ov5693_g_parm(struct v4l2_subdev *sd,
- struct v4l2_streamparm *param)
-{
- struct ov5693_device *dev = to_ov5693_sensor(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!param)
- return -EINVAL;
-
- if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- dev_err(&client->dev, "unsupported buffer type.\n");
- return -EINVAL;
- }
-
- memset(param, 0, sizeof(*param));
- param->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- if (dev->fmt_idx >= 0 && dev->fmt_idx < N_RES) {
- param->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
- param->parm.capture.timeperframe.numerator = 1;
- param->parm.capture.capturemode = dev->run_mode;
- param->parm.capture.timeperframe.denominator =
- ov5693_res[dev->fmt_idx].fps;
- }
- return 0;
-}
-
-static int ov5693_s_parm(struct v4l2_subdev *sd,
- struct v4l2_streamparm *param)
-{
- struct ov5693_device *dev = to_ov5693_sensor(sd);
-
- dev->run_mode = param->parm.capture.capturemode;
-
- mutex_lock(&dev->input_lock);
- switch (dev->run_mode) {
- case CI_MODE_VIDEO:
- ov5693_res = ov5693_res_video;
- N_RES = N_RES_VIDEO;
- break;
- case CI_MODE_STILL_CAPTURE:
- ov5693_res = ov5693_res_still;
- N_RES = N_RES_STILL;
- break;
- default:
- ov5693_res = ov5693_res_preview;
- N_RES = N_RES_PREVIEW;
- }
- mutex_unlock(&dev->input_lock);
- return 0;
-}
-
static int ov5693_g_frame_interval(struct v4l2_subdev *sd,
struct v4l2_subdev_frame_interval *interval)
{
@@ -1899,8 +1851,6 @@ static int ov5693_enum_frame_size(struct v4l2_subdev *sd,
static const struct v4l2_subdev_video_ops ov5693_video_ops = {
.s_stream = ov5693_s_stream,
- .g_parm = ov5693_g_parm,
- .s_parm = ov5693_s_parm,
.g_frame_interval = ov5693_g_frame_interval,
};
@@ -1947,7 +1897,7 @@ static int ov5693_probe(struct i2c_client *client)
struct ov5693_device *dev;
int i2c;
int ret = 0;
- void *pdata = client->dev.platform_data;
+ void *pdata;
unsigned int i;
/*
diff --git a/drivers/staging/media/atomisp/i2c/ov5693/ov5693.h b/drivers/staging/media/atomisp/i2c/ov5693/ov5693.h
index 68cfcb4a6c3c..9058a82455a6 100644
--- a/drivers/staging/media/atomisp/i2c/ov5693/ov5693.h
+++ b/drivers/staging/media/atomisp/i2c/ov5693/ov5693.h
@@ -1087,7 +1087,7 @@ static struct ov5693_reg const ov5693_2576x1936_30fps[] = {
{OV5693_TOK_TERM, 0, 0}
};
-struct ov5693_resolution ov5693_res_preview[] = {
+static struct ov5693_resolution ov5693_res_preview[] = {
{
.desc = "ov5693_736x496_30fps",
.width = 736,
@@ -1147,6 +1147,11 @@ struct ov5693_resolution ov5693_res_preview[] = {
};
#define N_RES_PREVIEW (ARRAY_SIZE(ov5693_res_preview))
+/*
+ * Disable non-preview configurations until the configuration selection is
+ * improved.
+ */
+#if 0
struct ov5693_resolution ov5693_res_still[] = {
{
.desc = "ov5693_736x496_30fps",
@@ -1364,6 +1369,7 @@ struct ov5693_resolution ov5693_res_video[] = {
},
};
#define N_RES_VIDEO (ARRAY_SIZE(ov5693_res_video))
+#endif
static struct ov5693_resolution *ov5693_res = ov5693_res_preview;
static unsigned long N_RES = N_RES_PREVIEW;
diff --git a/drivers/staging/media/atomisp/i2c/ov8858.c b/drivers/staging/media/atomisp/i2c/ov8858.c
deleted file mode 100644
index 3cf8c710ac65..000000000000
--- a/drivers/staging/media/atomisp/i2c/ov8858.c
+++ /dev/null
@@ -1,2169 +0,0 @@
-/*
- * Support for OmniVision ov8858 camera sensor.
- *
- * Copyright (c) 2014 Intel Corporation. All Rights Reserved.
- *
- * 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.
- *
- * 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.
- *
- *
- */
-
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <media/v4l2-device.h>
-#include <linux/acpi.h>
-#include "../include/linux/atomisp_gmin_platform.h"
-#ifdef CONFIG_PLATFORM_BTNS
-#include "ov8858_btns.h"
-#else
-#include "ov8858.h"
-#endif
-static int ov8858_i2c_read(struct i2c_client *client, u16 len, u16 addr,
- u8 *buf)
-{
- struct i2c_msg msg[2];
- u8 address[2];
- int err;
-
- if (!client->adapter) {
- dev_err(&client->dev, "%s error, no adapter\n", __func__);
- return -ENODEV;
- }
-
- dev_dbg(&client->dev, "%s: len = %d, addr = 0x%04x\n",
- __func__, len, addr);
-
- memset(msg, 0, sizeof(msg));
-
- address[0] = (addr >> 8) & 0xff;
- address[1] = addr & 0xff;
-
- msg[0].addr = client->addr;
- msg[0].flags = 0;
- msg[0].len = I2C_MSG_LENGTH;
- msg[0].buf = address;
-
- msg[1].addr = client->addr;
- msg[1].len = len;
- msg[1].flags = I2C_M_RD;
- msg[1].buf = buf;
-
- err = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
- if (err != 2) {
- if (err >= 0)
- err = -EIO;
- goto error;
- }
-
- return 0;
-error:
- dev_err(&client->dev, "reading from address 0x%x error %d", addr, err);
- return err;
-}
-
-static int ov8858_read_reg(struct i2c_client *client, u16 type, u16 reg,
- u16 *val)
-{
- u8 data[OV8858_SHORT_MAX];
- int err;
-
- dev_dbg(&client->dev, "%s: type = %d, reg = 0x%04x\n",
- __func__, type, reg);
-
- /* read only 8 and 16 bit values */
- if (type != OV8858_8BIT && type != OV8858_16BIT) {
- dev_err(&client->dev, "%s error, invalid data length\n",
- __func__);
- return -EINVAL;
- }
-
- memset(data, 0, sizeof(data));
-
- err = ov8858_i2c_read(client, type, reg, data);
- if (err)
- goto error;
-
- /* high byte comes first */
- if (type == OV8858_8BIT)
- *val = (u8)data[0];
- else
- *val = data[0] << 8 | data[1];
-
- dev_dbg(&client->dev, "%s: val = 0x%04x\n", __func__, *val);
-
- return 0;
-
-error:
- dev_err(&client->dev, "read from offset 0x%x error %d", reg, err);
- return err;
-}
-
-static int ov8858_i2c_write(struct i2c_client *client, u16 len, u8 *data)
-{
- struct i2c_msg msg;
- const int num_msg = 1;
- int ret;
-
- msg.addr = client->addr;
- msg.flags = 0;
- msg.len = len;
- msg.buf = data;
-
- ret = i2c_transfer(client->adapter, &msg, 1);
-
- return ret == num_msg ? 0 : -EIO;
-}
-
-static int
-ov8858_write_reg(struct i2c_client *client, u16 data_length, u16 reg, u16 val)
-{
- int ret;
- unsigned char data[4] = {0};
- u16 *wreg;
- const u16 len = data_length + sizeof(u16); /* 16-bit address + data */
-
- dev_dbg(&client->dev,
- "%s: data_length = %d, reg = 0x%04x, val = 0x%04x\n",
- __func__, data_length, reg, val);
-
- if (!client->adapter) {
- dev_err(&client->dev, "%s error, no adapter\n", __func__);
- return -ENODEV;
- }
-
- if (data_length != OV8858_8BIT && data_length != OV8858_16BIT) {
- dev_err(&client->dev, "%s error, invalid length\n", __func__);
- return -EINVAL;
- }
-
- /* high byte goes out first */
- wreg = (u16 *)data;
- *wreg = cpu_to_be16(reg);
-
- if (data_length == OV8858_8BIT) {
- data[2] = (u8)(val);
- } else {
- /* OV8858_16BIT */
- u16 *wdata = (u16 *)&data[2];
- *wdata = be16_to_cpu(val);
- }
-
- ret = ov8858_i2c_write(client, len, data);
- if (ret)
- dev_err(&client->dev,
- "write error: wrote 0x%x to offset 0x%x error %d",
- val, reg, ret);
-
- return ret;
-}
-
-/*
- * ov8858_write_reg_array - Initializes a list of registers
- * @client: i2c driver client structure
- * @reglist: list of registers to be written
- *
- * This function initializes a list of registers. When consecutive addresses
- * are found in a row on the list, this function creates a buffer and sends
- * consecutive data in a single i2c_transfer().
- *
- * __ov8858_flush_reg_array(), __ov8858_buf_reg_array() and
- * __ov8858_write_reg_is_consecutive() are internal functions to
- * ov8858_write_reg_array() and should be not used anywhere else.
- *
- */
-static int __ov8858_flush_reg_array(struct i2c_client *client,
- struct ov8858_write_ctrl *ctrl)
-{
- u16 size;
- if (ctrl->index == 0)
- return 0;
-
- size = sizeof(u16) + ctrl->index; /* 16-bit address + data */
- ctrl->buffer.addr = cpu_to_be16(ctrl->buffer.addr);
- ctrl->index = 0;
-
- return ov8858_i2c_write(client, size, (u8 *)&ctrl->buffer);
-}
-
-static int __ov8858_buf_reg_array(struct i2c_client *client,
- struct ov8858_write_ctrl *ctrl,
- const struct ov8858_reg *next)
-{
- int size;
- u16 *data16;
-
- switch (next->type) {
- case OV8858_8BIT:
- size = 1;
- ctrl->buffer.data[ctrl->index] = (u8)next->val;
- break;
- case OV8858_16BIT:
- size = 2;
- data16 = (u16 *)&ctrl->buffer.data[ctrl->index];
- *data16 = cpu_to_be16((u16)next->val);
- break;
- default:
- return -EINVAL;
- }
-
- /* When first item is added, we need to store its starting address */
- if (ctrl->index == 0)
- ctrl->buffer.addr = next->sreg;
-
- ctrl->index += size;
-
- /*
- * Buffer cannot guarantee free space for u32? Better flush it to avoid
- * possible lack of memory for next item.
- */
- if (ctrl->index + sizeof(u16) >= OV8858_MAX_WRITE_BUF_SIZE)
- __ov8858_flush_reg_array(client, ctrl);
-
- return 0;
-}
-
-static int
-__ov8858_write_reg_is_consecutive(struct i2c_client *client,
- struct ov8858_write_ctrl *ctrl,
- const struct ov8858_reg *next)
-{
- if (ctrl->index == 0)
- return 1;
-
- return ctrl->buffer.addr + ctrl->index == next->sreg;
-}
-
-static int ov8858_write_reg_array(struct i2c_client *client,
- const struct ov8858_reg *reglist)
-{
- const struct ov8858_reg *next = reglist;
- struct ov8858_write_ctrl ctrl;
- int err;
-
- ctrl.index = 0;
- for (; next->type != OV8858_TOK_TERM; next++) {
- switch (next->type & OV8858_TOK_MASK) {
- case OV8858_TOK_DELAY:
- err = __ov8858_flush_reg_array(client, &ctrl);
- if (err)
- return err;
- msleep(next->val);
- break;
-
- default:
- /*
- * If next address is not consecutive, data needs to be
- * flushed before proceeding
- */
- if (!__ov8858_write_reg_is_consecutive(client,
- &ctrl, next)) {
- err = __ov8858_flush_reg_array(client, &ctrl);
- if (err)
- return err;
- }
- err = __ov8858_buf_reg_array(client, &ctrl, next);
- if (err) {
- dev_err(&client->dev, "%s: write error\n",
- __func__);
- return err;
- }
- break;
- }
- }
-
- return __ov8858_flush_reg_array(client, &ctrl);
-}
-
-static int __ov8858_min_fps_diff(int fps,
- const struct ov8858_fps_setting *fps_list)
-{
- int diff = INT_MAX;
- int i;
-
- if (fps == 0)
- return 0;
-
- for (i = 0; i < MAX_FPS_OPTIONS_SUPPORTED; i++) {
- if (!fps_list[i].fps)
- break;
- if (abs(fps_list[i].fps - fps) < diff)
- diff = abs(fps_list[i].fps - fps);
- }
-
- return diff;
-}
-
-static int __ov8858_nearest_fps_index(int fps,
- const struct ov8858_fps_setting *fps_list)
-{
- int fps_index = 0;
- int i;
-
- for (i = 0; i < MAX_FPS_OPTIONS_SUPPORTED; i++) {
- if (!fps_list[i].fps)
- break;
- if (abs(fps_list[i].fps - fps)
- < abs(fps_list[fps_index].fps - fps))
- fps_index = i;
- }
- return fps_index;
-}
-
-static int __ov8858_update_frame_timing(struct v4l2_subdev *sd,
- u16 *hts, u16 *vts)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret;
-
-
- dev_dbg(&client->dev, "%s OV8858_TIMING_HTS=0x%04x\n",
- __func__, *hts);
-
- /* HTS = pixel_per_line / 2 */
- ret = ov8858_write_reg(client, OV8858_16BIT,
- OV8858_TIMING_HTS, *hts >> 1);
- if (ret)
- return ret;
- dev_dbg(&client->dev, "%s OV8858_TIMING_VTS=0x%04x\n",
- __func__, *vts);
-
- return ov8858_write_reg(client, OV8858_16BIT, OV8858_TIMING_VTS, *vts);
-}
-
-static int __ov8858_set_exposure(struct v4l2_subdev *sd, int exposure, int gain,
- int dig_gain, u16 *hts, u16 *vts)
-{
- struct ov8858_device *dev = to_ov8858_sensor(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int exp_val, ret;
- dev_dbg(&client->dev, "%s, exposure = %d, gain=%d, dig_gain=%d\n",
- __func__, exposure, gain, dig_gain);
-
- if (dev->limit_exposure_flag) {
- if (exposure > *vts - OV8858_INTEGRATION_TIME_MARGIN)
- exposure = *vts - OV8858_INTEGRATION_TIME_MARGIN;
- } else {
- if (*vts < exposure + OV8858_INTEGRATION_TIME_MARGIN)
- *vts = (u16) exposure + OV8858_INTEGRATION_TIME_MARGIN;
- }
-
- ret = __ov8858_update_frame_timing(sd, hts, vts);
- if (ret)
- return ret;
-
- /* For ov8858, the low 4 bits are fraction bits and must be kept 0 */
- exp_val = exposure << 4;
- ret = ov8858_write_reg(client, OV8858_8BIT,
- OV8858_LONG_EXPO+2, exp_val & 0xFF);
- if (ret)
- return ret;
-
- ret = ov8858_write_reg(client, OV8858_8BIT,
- OV8858_LONG_EXPO+1, (exp_val >> 8) & 0xFF);
- if (ret)
- return ret;
-
- ret = ov8858_write_reg(client, OV8858_8BIT,
- OV8858_LONG_EXPO, (exp_val >> 16) & 0x0F);
- if (ret)
- return ret;
-
- /* Digital gain : to all MWB channel gains */
- if (dig_gain) {
- ret = ov8858_write_reg(client, OV8858_16BIT,
- OV8858_MWB_RED_GAIN_H, dig_gain);
- if (ret)
- return ret;
-
- ret = ov8858_write_reg(client, OV8858_16BIT,
- OV8858_MWB_GREEN_GAIN_H, dig_gain);
- if (ret)
- return ret;
-
- ret = ov8858_write_reg(client, OV8858_16BIT,
- OV8858_MWB_BLUE_GAIN_H, dig_gain);
- if (ret)
- return ret;
- }
-
- ret = ov8858_write_reg(client, OV8858_16BIT, OV8858_LONG_GAIN,
- gain & 0x07ff);
- if (ret)
- return ret;
-
- dev->gain = gain;
- dev->exposure = exposure;
- dev->digital_gain = dig_gain;
-
- return 0;
-}
-
-static int ov8858_set_exposure(struct v4l2_subdev *sd, int exposure, int gain,
- int dig_gain)
-{
- struct ov8858_device *dev = to_ov8858_sensor(sd);
- const struct ov8858_resolution *res;
- u16 hts, vts;
- int ret;
-
- mutex_lock(&dev->input_lock);
-
- /* Validate exposure: cannot exceed 16bit value */
- exposure = clamp_t(int, exposure, 0, OV8858_MAX_EXPOSURE_VALUE);
-
- /* Validate gain: must not exceed maximum 8bit value */
- gain = clamp_t(int, gain, 0, OV8858_MAX_GAIN_VALUE);
-
- /* Validate digital gain: must not exceed 12 bit value*/
- dig_gain = clamp_t(int, dig_gain, 0, OV8858_MWB_GAIN_MAX);
-
- res = &dev->curr_res_table[dev->fmt_idx];
- /*
- * Vendor: HTS reg value is half the total pixel line
- */
- hts = res->fps_options[dev->fps_index].pixels_per_line;
- vts = res->fps_options[dev->fps_index].lines_per_frame;
-
- ret = __ov8858_set_exposure(sd, exposure, gain, dig_gain, &hts, &vts);
-
- mutex_unlock(&dev->input_lock);
-
- return ret;
-}
-
-/*
- When exposure gain value set to sensor, the sensor changed value.
- So we need the function to get real value
- */
-static int ov8858_g_update_exposure(struct v4l2_subdev *sd,
- struct atomisp_update_exposure *exposure)
-{
- struct ov8858_device *dev = to_ov8858_sensor(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int gain = exposure->gain;
-
- dev_dbg(&client->dev, "%s: gain: %d, digi_gain: %d\n", __func__,
- exposure->gain, exposure->digi_gain);
- exposure->update_digi_gain = dev->digital_gain;
- /* This real gain value fetching function is provided by vendor */
- exposure->update_gain = (((gain & 0x700) >> 8) + 1) * (gain & 0xFF);
-
- return 0;
-}
-
-static int ov8858_s_exposure(struct v4l2_subdev *sd,
- struct atomisp_exposure *exposure)
-{
- return ov8858_set_exposure(sd, exposure->integration_time[0],
- exposure->gain[0], exposure->gain[1]);
-}
-
-static int ov8858_priv_int_data_init(struct v4l2_subdev *sd)
-{
- struct ov8858_device *dev = to_ov8858_sensor(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- u32 size = OV8858_OTP_END_ADDR - OV8858_OTP_START_ADDR + 1;
- int r;
- u16 isp_ctrl2 = 0;
-
- if (!dev->otp_data) {
- dev->otp_data = devm_kzalloc(&client->dev, size, GFP_KERNEL);
- if (!dev->otp_data) {
- r = -ENOMEM;
- goto error3;
- }
-
- /* Streaming has to be on */
- r = ov8858_write_reg(client, OV8858_8BIT, OV8858_STREAM_MODE,
- 0x01);
- if (r)
- goto error2;
-
- /* Turn off Dead Pixel Correction */
- r = ov8858_read_reg(client, OV8858_8BIT,
- OV8858_OTP_ISP_CTRL2, &isp_ctrl2);
- if (r)
- goto error1;
-
- r = ov8858_write_reg(client, OV8858_8BIT, OV8858_OTP_ISP_CTRL2,
- isp_ctrl2 & ~OV8858_OTP_DPC_ENABLE);
- if (r)
- goto error1;
-
- /* Enable partial OTP read mode */
- r = ov8858_write_reg(client, OV8858_8BIT, OV8858_OTP_MODE_CTRL,
- OV8858_OTP_MODE_PROGRAM_DISABLE |
- OV8858_OTP_MODE_MANUAL);
- if (r)
- goto error1;
-
- /* Set address range of OTP memory to read */
- r = ov8858_write_reg(client, OV8858_16BIT,
- OV8858_OTP_START_ADDR_REG,
- OV8858_OTP_START_ADDR);
- if (r)
- goto error1;
-
- r = ov8858_write_reg(client, OV8858_16BIT,
- OV8858_OTP_END_ADDR_REG,
- OV8858_OTP_END_ADDR);
- if (r)
- goto error1;
-
- /* Load the OTP data into the OTP buffer */
- r = ov8858_write_reg(client, OV8858_8BIT, OV8858_OTP_LOAD_CTRL,
- OV8858_OTP_LOAD_ENABLE);
- if (r)
- goto error1;
-
- /* Wait for the data to load into the buffer */
- usleep_range(5000, 5500);
-
- /* Read the OTP data from the buffer */
- r = ov8858_i2c_read(client, size, OV8858_OTP_START_ADDR,
- dev->otp_data);
- if (r)
- goto error1;
-
- /* Turn on Dead Pixel Correction */
- r = ov8858_write_reg(client, OV8858_8BIT, OV8858_OTP_ISP_CTRL2,
- isp_ctrl2 | OV8858_OTP_DPC_ENABLE);
- if (r)
- goto error1;
-
- /* Stop streaming */
- r = ov8858_write_reg(client, 1, OV8858_STREAM_MODE, 0x00);
- if (r) {
- dev_err(&client->dev, "%s: cannot turn off streaming\n",
- __func__);
- goto error1;
- }
- }
-
-
- return 0;
-
-error1:
- /* Turn on Dead Pixel Correction and set streaming off */
- ov8858_write_reg(client, OV8858_8BIT, OV8858_OTP_ISP_CTRL2,
- isp_ctrl2 | OV8858_OTP_DPC_ENABLE);
- ov8858_write_reg(client, 1, OV8858_STREAM_MODE, 0x00);
-error2:
- devm_kfree(&client->dev, dev->otp_data);
- dev->otp_data = NULL;
-error3:
- dev_err(&client->dev, "%s: OTP reading failed\n", __func__);
- return r;
-}
-
-static int ov8858_g_priv_int_data(struct v4l2_subdev *sd,
- struct v4l2_private_int_data *priv)
-{
- struct ov8858_device *dev = to_ov8858_sensor(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- u32 size = OV8858_OTP_END_ADDR - OV8858_OTP_START_ADDR + 1;
- int r;
-
- mutex_lock(&dev->input_lock);
-
- if (!dev->otp_data) {
- dev_err(&client->dev, "%s: otp data is NULL\n", __func__);
- mutex_unlock(&dev->input_lock);
- return -EFAULT;
- }
-
- if (copy_to_user(priv->data, dev->otp_data,
- min_t(__u32, priv->size, size))) {
- r = -EFAULT;
- dev_err(&client->dev, "%s: OTP reading failed\n", __func__);
- mutex_unlock(&dev->input_lock);
- return r;
- }
-
- priv->size = size;
- mutex_unlock(&dev->input_lock);
-
- return 0;
-}
-
-static int __ov8858_init(struct v4l2_subdev *sd)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov8858_device *dev = to_ov8858_sensor(sd);
- int ret;
- dev_dbg(&client->dev, "%s\n", __func__);
-
- /* Sets the default FPS */
- dev->fps_index = 0;
-
- /* Set default exposure values (initially start values) */
- dev->exposure = 256;
- dev->gain = 16;
- dev->digital_gain = 1024;
- dev->limit_exposure_flag = false;
-
- dev_dbg(&client->dev, "%s: Writing basic settings to ov8858\n",
- __func__);
- ret = ov8858_write_reg_array(client, ov8858_BasicSettings);
- if (ret)
- return ret;
-
- return ov8858_priv_int_data_init(sd);
-}
-
-static int ov8858_init(struct v4l2_subdev *sd, u32 val)
-{
- struct ov8858_device *dev = to_ov8858_sensor(sd);
- int ret;
-
- mutex_lock(&dev->input_lock);
- ret = __ov8858_init(sd);
- mutex_unlock(&dev->input_lock);
-
- return ret;
-}
-
-static void ov8858_uninit(struct v4l2_subdev *sd)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov8858_device *dev = to_ov8858_sensor(sd);
- struct v4l2_ctrl *ctrl;
- dev_dbg(&client->dev, "%s:\n", __func__);
-
- dev->exposure = 0;
- dev->gain = 0;
- dev->digital_gain = 0;
- dev->limit_exposure_flag = false;
- mutex_unlock(&dev->input_lock);
- ctrl = v4l2_ctrl_find(sd->ctrl_handler,
- V4L2_CID_EXPOSURE_AUTO_PRIORITY);
- if (ctrl)
- v4l2_ctrl_s_ctrl(ctrl, V4L2_EXPOSURE_AUTO);
- mutex_lock(&dev->input_lock);
-}
-
-static int ov8858_g_comp_delay(struct v4l2_subdev *sd, unsigned int *usec)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov8858_device *dev = to_ov8858_sensor(sd);
- int ret = 0, exposure;
- u16 vts, data;
-
- if (dev->exposure == 0) {
- ret = ov8858_read_reg(client, OV8858_16BIT,
- OV8858_LONG_EXPO + 1, &data);
- if (ret)
- return ret;
- exposure = data;
- exposure >>= 4;
- } else {
- exposure = dev->exposure;
- }
-
- ret = ov8858_read_reg(client, OV8858_16BIT, OV8858_TIMING_VTS, &vts);
- if (ret || vts == 0)
- vts = OV8858_DEPTH_VTS_CONST;
-
- *usec = (exposure * 33333 / vts);
- if (*usec > OV8858_DEPTH_COMP_CONST)
- *usec = *usec - OV8858_DEPTH_COMP_CONST;
- else
- *usec = OV8858_DEPTH_COMP_CONST;
-
- return 0;
-}
-
-static long ov8858_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- switch (cmd) {
- case ATOMISP_IOC_S_EXPOSURE:
- return ov8858_s_exposure(sd, (struct atomisp_exposure *)arg);
- case ATOMISP_IOC_G_SENSOR_PRIV_INT_DATA:
- return ov8858_g_priv_int_data(sd, arg);
- case ATOMISP_IOC_G_DEPTH_SYNC_COMP:
- return ov8858_g_comp_delay(sd, (unsigned int *)arg);
- case ATOMISP_IOC_G_UPDATE_EXPOSURE:
- return ov8858_g_update_exposure(sd,
- (struct atomisp_update_exposure *)arg);
- default:
- dev_dbg(&client->dev, "Unhandled command 0x%X\n", cmd);
- return -EINVAL;
- }
-}
-
-static int __power_ctrl(struct v4l2_subdev *sd, bool flag)
-{
- int ret = 0;
- struct ov8858_device *dev = to_ov8858_sensor(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!dev || !dev->platform_data)
- return -ENODEV;
-
- if (dev->platform_data->v1p2_ctrl) {
- ret = dev->platform_data->v1p2_ctrl(sd, flag);
- if (ret) {
- dev_err(&client->dev,
- "failed to power %s 1.2v power rail\n",
- flag ? "up" : "down");
- return ret;
- }
- }
-
- if (dev->platform_data->v2p8_ctrl) {
- ret = dev->platform_data->v2p8_ctrl(sd, flag);
- if (ret) {
- dev_err(&client->dev,
- "failed to power %s 2.8v power rail\n",
- flag ? "up" : "down");
- return ret;
- }
- }
-
- if (dev->platform_data->v1p8_ctrl) {
- ret = dev->platform_data->v1p8_ctrl(sd, flag);
- if (ret) {
- dev_err(&client->dev,
- "failed to power %s 1.8v power rail\n",
- flag ? "up" : "down");
- if (dev->platform_data->v2p8_ctrl)
- dev->platform_data->v2p8_ctrl(sd, 0);
- return ret;
- }
- }
-
- if (flag)
- msleep(20); /* Wait for power lines to stabilize */
- return ret;
-}
-
-static int __gpio_ctrl(struct v4l2_subdev *sd, bool flag)
-{
- struct i2c_client *client;
- struct ov8858_device *dev;
-
- if (!sd)
- return -EINVAL;
-
- client = v4l2_get_subdevdata(sd);
- dev = to_ov8858_sensor(sd);
-
- if (!client || !dev || !dev->platform_data)
- return -ENODEV;
-
- if (dev->platform_data->gpio0_ctrl)
- return dev->platform_data->gpio0_ctrl(sd, flag);
-
- dev_err(&client->dev, "failed to find platform gpio callback\n");
-
- return -EINVAL;
-}
-
-static int power_up(struct v4l2_subdev *sd)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov8858_device *dev = to_ov8858_sensor(sd);
- int ret;
- dev_dbg(&client->dev, "%s\n", __func__);
-
- /* Enable power */
- ret = __power_ctrl(sd, 1);
- if (ret) {
- dev_err(&client->dev, "power rail on failed %d.\n", ret);
- goto fail_power;
- }
-
- /* Enable clock */
- ret = dev->platform_data->flisclk_ctrl(sd, 1);
- if (ret) {
- dev_err(&client->dev, "flisclk on failed %d\n", ret);
- goto fail_clk;
- }
-
- /* Release reset */
- ret = __gpio_ctrl(sd, 1);
- if (ret) {
- dev_err(&client->dev, "gpio on failed %d\n", ret);
- goto fail_gpio;
- }
-
- /* Minumum delay is 8192 clock cycles before first i2c transaction,
- * which is 1.37 ms at the lowest allowed clock rate 6 MHz */
- usleep_range(2000, 2500);
- return 0;
-
-fail_gpio:
- dev->platform_data->flisclk_ctrl(sd, 0);
-fail_clk:
- __power_ctrl(sd, 0);
-fail_power:
- dev_err(&client->dev, "Sensor power-up failed\n");
-
- return ret;
-}
-
-static int power_down(struct v4l2_subdev *sd)
-{
- struct ov8858_device *dev = to_ov8858_sensor(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret;
- dev_dbg(&client->dev, "%s\n", __func__);
-
- ret = dev->platform_data->flisclk_ctrl(sd, 0);
- if (ret)
- dev_err(&client->dev, "flisclk off failed\n");
-
- ret = __gpio_ctrl(sd, 0);
- if (ret)
- dev_err(&client->dev, "gpio off failed\n");
-
- ret = __power_ctrl(sd, 0);
- if (ret)
- dev_err(&client->dev, "power rail off failed.\n");
-
- return ret;
-}
-
-static int __ov8858_s_power(struct v4l2_subdev *sd, int on)
-{
- struct ov8858_device *dev = to_ov8858_sensor(sd);
- int ret, r = 0;
-
- if (on == 0) {
- ov8858_uninit(sd);
- if (dev->vcm_driver && dev->vcm_driver->power_down)
- r = dev->vcm_driver->power_down(sd);
- ret = power_down(sd);
- if (r != 0 && ret == 0)
- ret = r;
- } else {
- ret = power_up(sd);
- if (ret)
- power_down(sd);
- if (dev->vcm_driver && dev->vcm_driver->power_up) {
- ret = dev->vcm_driver->power_up(sd);
- if (ret) {
- power_down(sd);
- return ret;
- }
- }
- return __ov8858_init(sd);
- }
-
- return ret;
-}
-
-static int ov8858_s_power(struct v4l2_subdev *sd, int on)
-{
- int ret;
- struct ov8858_device *dev = to_ov8858_sensor(sd);
-
- mutex_lock(&dev->input_lock);
- ret = __ov8858_s_power(sd, on);
- mutex_unlock(&dev->input_lock);
-
- /*
- * FIXME: Compatibility with old behaviour: return to preview
- * when the device is power cycled.
- */
- if (!ret && on)
- v4l2_ctrl_s_ctrl(dev->run_mode, ATOMISP_RUN_MODE_PREVIEW);
-
- return ret;
-}
-
-/*
- * Return value of the specified register, first try getting it from
- * the register list and if not found, get from the sensor via i2c.
- */
-static int ov8858_get_register(struct v4l2_subdev *sd, int reg, int type,
- const struct ov8858_reg *reglist)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- const struct ov8858_reg *next;
- u16 val;
-
- /* Try if the values are in the register list */
- for (next = reglist; next->type != OV8858_TOK_TERM; next++) {
- if (next->sreg == reg) {
- if (type == OV8858_8BIT)
- return next->val;
-
- if (type == OV8858_16BIT &&
- next[1].type != OV8858_TOK_TERM)
- return next[0].val << 8 | next[1].val;
- }
- }
-
- /* If not, read from sensor */
- if (ov8858_read_reg(client, type, reg, &val)) {
- dev_err(&client->dev, "failed to read register 0x%08x\n", reg);
- return -EIO;
- }
-
- return val;
-}
-
-static inline int ov8858_get_register_16bit(struct v4l2_subdev *sd, int reg,
- const struct ov8858_reg *reglist)
-{
- return ov8858_get_register(sd, reg, OV8858_16BIT, reglist);
-}
-
-static inline int ov8858_get_register_8bit(struct v4l2_subdev *sd, int reg,
- const struct ov8858_reg *reglist)
-{
- return ov8858_get_register(sd, reg, OV8858_8BIT, reglist);
-}
-
-static int __ov8858_get_pll1_values(struct v4l2_subdev *sd,
- int *value,
- const struct ov8858_reg *reglist)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- unsigned int prediv_idx;
- unsigned int multiplier;
- unsigned int sys_prediv;
- unsigned int prediv_coef[] = {2, 3, 4, 5, 6, 8, 12, 16};
- int ret;
-
- ret = ov8858_get_register_8bit(sd, OV8858_PLL1_PREDIV0, reglist);
- if (ret < 0)
- return ret;
-
- if (ret & OV8858_PLL1_PREDIV0_MASK)
- *value /= 2;
-
- ret = ov8858_get_register_8bit(sd, OV8858_PLL1_PREDIV, reglist);
-
- if (ret < 0)
- return ret;
-
- prediv_idx = ret & OV8858_PLL1_PREDIV_MASK;
- *value = *value * 2 / prediv_coef[prediv_idx];
-
- ret = ov8858_get_register_16bit(sd, OV8858_PLL1_MULTIPLIER, reglist);
- if (ret < 0)
- return ret;
-
- multiplier = ret;
- *value *= multiplier & OV8858_PLL1_MULTIPLIER_MASK;
- ret = ov8858_get_register_8bit(sd, OV8858_PLL1_SYS_PRE_DIV, reglist);
-
- if (ret < 0)
- return ret;
-
- sys_prediv = ret & OV8858_PLL1_SYS_PRE_DIV_MASK;
- *value /= (sys_prediv + 3);
- ret = ov8858_get_register_8bit(sd, OV8858_PLL1_SYS_DIVIDER, reglist);
-
- if (ret < 0)
- return ret;
-
- if (ret & OV8858_PLL1_SYS_DIVIDER_MASK)
- *value /= 2;
-
- dev_dbg(&client->dev, "%s: *value: %d\n", __func__, *value);
-
- return 0;
-}
-
-static int __ov8858_get_pll2a_values(struct v4l2_subdev *sd, int *value,
- const struct ov8858_reg *reglist)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- unsigned int prediv_idx;
- unsigned int multiplier;
- unsigned int prediv_coef[] = {2, 3, 4, 5, 6, 8, 12, 16};
- int ret;
-
- ret = ov8858_get_register_8bit(sd, OV8858_PLL2_PREDIV0, reglist);
- if (ret < 0)
- return ret;
-
- if (ret & OV8858_PLL2_PREDIV0_MASK)
- *value /= 2;
-
- ret = ov8858_get_register_8bit(sd, OV8858_PLL2_PREDIV, reglist);
- if (ret < 0)
- return ret;
-
- prediv_idx = (ret & OV8858_PLL2_PREDIV_MASK);
- *value = *value * 2 / prediv_coef[prediv_idx];
-
- ret = ov8858_get_register_16bit(sd, OV8858_PLL2_MULTIPLIER, reglist);
- if (ret < 0)
- return ret;
-
- multiplier = ret;
- *value *= multiplier & OV8858_PLL2_MULTIPLIER_MASK;
- dev_dbg(&client->dev, "%s: *value: %d\n", __func__, *value);
-
- return 0;
-}
-static int __ov8858_get_pll2b_values(struct v4l2_subdev *sd, int *value,
- const struct ov8858_reg *reglist)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- unsigned int dac_divider;
- int ret;
-
- ret = ov8858_get_register_8bit(sd, OV8858_PLL2_DAC_DIVIDER, reglist);
- if (ret < 0)
- return ret;
-
- dac_divider = (ret & OV8858_PLL2_DAC_DIVIDER_MASK) + 1;
- *value /= dac_divider;
-
- dev_dbg(&client->dev, "%s: *value: %d\n", __func__, *value);
-
- return 0;
-}
-static int __ov8858_get_pll2c_values(struct v4l2_subdev *sd, int *value,
- const struct ov8858_reg *reglist)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- unsigned int sys_pre_div;
- unsigned int sys_divider_idx;
- unsigned int sys_divider_coef[] = {2, 3, 4, 5, 6, 7, 8, 10};
- int ret;
-
- ret = ov8858_get_register_8bit(sd, OV8858_PLL2_SYS_PRE_DIV, reglist);
- if (ret < 0)
- return ret;
-
- sys_pre_div = (ret & OV8858_PLL2_SYS_PRE_DIV_MASK) + 1;
- *value /= sys_pre_div;
-
- ret = ov8858_get_register_8bit(sd, OV8858_PLL2_SYS_DIVIDER, reglist);
- if (ret < 0)
- return ret;
-
- sys_divider_idx = ret & OV8858_PLL2_SYS_DIVIDER_MASK;
- *value *= 2 / sys_divider_coef[sys_divider_idx];
-
- dev_dbg(&client->dev, "%s: *value: %d\n", __func__, *value);
-
- return 0;
-}
-
-static int ov8858_get_intg_factor(struct v4l2_subdev *sd,
- struct camera_mipi_info *info,
- const struct ov8858_reg *reglist)
-{
- const unsigned int ext_clk = 19200000; /* Hz */
- struct atomisp_sensor_mode_data *m = &info->data;
- struct ov8858_device *dev = to_ov8858_sensor(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct device *d = &client->dev;
- const struct ov8858_resolution *res =
- &dev->curr_res_table[dev->fmt_idx];
- unsigned int pll_sclksel1;
- unsigned int pll_sclksel2;
- unsigned int sys_pre_div;
- unsigned int sclk_pdiv;
- unsigned int sclk = ext_clk;
- u16 hts;
- int ret;
-
- memset(&info->data, 0, sizeof(info->data));
-
- ret = ov8858_get_register_8bit(sd, OV8858_PLL_SCLKSEL1, reglist);
- if (ret < 0)
- return ret;
-
- dev_dbg(d, "%s: OV8858_PLL_SCLKSEL1: 0x%02x\n", __func__, ret);
- pll_sclksel1 = ret & OV8858_PLL_SCLKSEL1_MASK;
-
- ret = ov8858_get_register_8bit(sd, OV8858_PLL_SCLKSEL2, reglist);
- if (ret < 0)
- return ret;
-
- dev_dbg(d, "%s: OV8858_PLL_SCLKSEL2: 0x%02x\n", __func__, ret);
- pll_sclksel2 = ret & OV8858_PLL_SCLKSEL2_MASK;
-
- if (pll_sclksel2) {
- ret = __ov8858_get_pll2a_values(sd, &sclk, reglist);
- if (ret < 0)
- return ret;
- ret = __ov8858_get_pll2b_values(sd, &sclk, reglist);
- if (ret < 0)
- return ret;
- } else if (pll_sclksel1) {
- ret = __ov8858_get_pll2a_values(sd, &sclk, reglist);
- if (ret < 0)
- return ret;
- ret = __ov8858_get_pll2c_values(sd, &sclk, reglist);
- if (ret < 0)
- return ret;
- } else {
- ret = __ov8858_get_pll1_values(sd, &sclk, reglist);
- if (ret < 0)
- return ret;
- }
-
- ret = ov8858_get_register_8bit(sd, OV8858_SRB_HOST_INPUT_DIS, reglist);
- if (ret < 0)
- return ret;
-
- dev_dbg(d, "%s: OV8858_SRB_HOST_INPUT_DIS: 0x%02x\n", __func__, ret);
-
- sys_pre_div = ret & OV8858_SYS_PRE_DIV_MASK;
- sys_pre_div >>= OV8858_SYS_PRE_DIV_OFFSET;
-
- if (sys_pre_div == 1)
- sclk /= 2;
- else if (sys_pre_div == 2)
- sclk /= 4;
-
- sclk_pdiv = ret & OV8858_SCLK_PDIV_MASK;
- sclk_pdiv >>= OV8858_SCLK_PDIV_OFFSET;
-
- if (sclk_pdiv > 1)
- sclk /= sclk_pdiv;
-
- dev_dbg(d, "%s: sclk: %d\n", __func__, sclk);
-
- dev->vt_pix_clk_freq_mhz = sclk;
- m->vt_pix_clk_freq_mhz = sclk;
-
- /* HTS and VTS */
- m->frame_length_lines =
- res->fps_options[dev->fps_index].lines_per_frame;
- m->line_length_pck = res->fps_options[dev->fps_index].pixels_per_line;
-
- m->coarse_integration_time_min = 0;
- m->coarse_integration_time_max_margin = OV8858_INTEGRATION_TIME_MARGIN;
- ret = ov8858_read_reg(client, OV8858_16BIT, OV8858_TIMING_HTS, &hts);
- if (ret < 0)
- return ret;
- m->hts = hts;
- dev_dbg(&client->dev, "%s: get HTS %d\n", __func__, hts);
-
- /* OV Sensor do not use fine integration time. */
- m->fine_integration_time_min = 0;
- m->fine_integration_time_max_margin = 0;
-
- /*
- * read_mode indicate whether binning is used for calculating
- * the correct exposure value from the user side. So adapt the
- * read mode values accordingly.
- */
- m->read_mode = res->bin_factor_x ?
- OV8858_READ_MODE_BINNING_ON : OV8858_READ_MODE_BINNING_OFF;
-
- ret = ov8858_get_register_8bit(sd, OV8858_H_INC_ODD, res->regs);
- if (ret < 0)
- return ret;
- m->binning_factor_x = (ret + 1) / 2;
-
- ret = ov8858_get_register_8bit(sd, OV8858_V_INC_ODD, res->regs);
- if (ret < 0)
- return ret;
- m->binning_factor_y = (ret + 1) / 2;
-
- /* Get the cropping and output resolution to ISP for this mode. */
- ret = ov8858_get_register_16bit(sd, OV8858_HORIZONTAL_START_H,
- res->regs);
- if (ret < 0)
- return ret;
-
- m->crop_horizontal_start = ret;
-
- ret = ov8858_get_register_16bit(sd, OV8858_VERTICAL_START_H, res->regs);
- if (ret < 0)
- return ret;
-
- m->crop_vertical_start = ret;
-
- ret = ov8858_get_register_16bit(sd, OV8858_HORIZONTAL_END_H, res->regs);
- if (ret < 0)
- return ret;
-
- m->crop_horizontal_end = ret;
-
- ret = ov8858_get_register_16bit(sd, OV8858_VERTICAL_END_H, res->regs);
- if (ret < 0)
- return ret;
-
- m->crop_vertical_end = ret;
-
- ret = ov8858_get_register_16bit(sd, OV8858_HORIZONTAL_OUTPUT_SIZE_H,
- res->regs);
- if (ret < 0)
- return ret;
-
- m->output_width = ret;
-
- ret = ov8858_get_register_16bit(sd, OV8858_VERTICAL_OUTPUT_SIZE_H,
- res->regs);
- if (ret < 0)
- return ret;
-
- m->output_height = ret;
-
- return 0;
-}
-
-/*
- * distance - calculate the distance
- * @res: resolution
- * @w: width
- * @h: height
- *
- * Get the gap between res_w/res_h and w/h.
- * distance = (res_w/res_h - w/h) / (w/h) * 8192
- * res->width/height smaller than w/h wouldn't be considered.
- * The gap of ratio larger than 1/8 wouldn't be considered.
- * Returns the value of gap or -1 if fail.
- */
-#define LARGEST_ALLOWED_RATIO_MISMATCH 1024
-static int distance(struct ov8858_resolution const *res, const u32 w,
- const u32 h)
-{
- int ratio;
- int distance;
-
- if (w == 0 || h == 0 ||
- res->width < w || res->height < h)
- return -1;
-
- ratio = res->width << 13;
- ratio /= w;
- ratio *= h;
- ratio /= res->height;
-
- distance = abs(ratio - 8192);
-
- if (distance > LARGEST_ALLOWED_RATIO_MISMATCH)
- return -1;
- return distance;
-}
-
-/*
- * Returns the nearest higher resolution index.
- * @w: width
- * @h: height
- * matching is done based on enveloping resolution and
- * aspect ratio. If the aspect ratio cannot be matched
- * to any index, -1 is returned.
- */
-static int nearest_resolution_index(struct v4l2_subdev *sd, int w, int h)
-{
- int i;
- int idx = -1;
- int dist;
- int fps_diff;
- int min_fps_diff = INT_MAX;
- int min_dist = INT_MAX;
- int min_res_w = INT_MAX;
- const struct ov8858_resolution *tmp_res = NULL;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov8858_device *dev = to_ov8858_sensor(sd);
- dev_dbg(&client->dev, "%s: w=%d, h=%d\n", __func__, w, h);
-
- for (i = 0; i < dev->entries_curr_table; i++) {
- tmp_res = &dev->curr_res_table[i];
- dist = distance(tmp_res, w, h);
- dev_dbg(&client->dev,
- "%s[%d]: %dx%d distance=%d\n", tmp_res->desc,
- i, tmp_res->width, tmp_res->height, dist);
- if (dist == -1)
- continue;
- if (dist < min_dist) {
- min_dist = dist;
- min_res_w = tmp_res->width;
- min_fps_diff = __ov8858_min_fps_diff(dev->fps,
- tmp_res->fps_options);
- idx = i;
- }
- if (dist == min_dist) {
- fps_diff = __ov8858_min_fps_diff(dev->fps,
- tmp_res->fps_options);
- if (fps_diff < min_fps_diff) {
- min_fps_diff = fps_diff;
- idx = i;
- }
- if (tmp_res->width < min_res_w) {
- min_res_w = tmp_res->width;
- idx = i;
- }
- }
- }
-
- return idx;
-}
-
-static int ov8858_set_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *format)
-{
- struct v4l2_mbus_framefmt *fmt = &format->format;
- struct ov8858_device *dev = to_ov8858_sensor(sd);
- struct camera_mipi_info *ov8858_info = NULL;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- const struct ov8858_resolution *res;
- int ret;
- int idx;
- if (format->pad)
- return -EINVAL;
- if (!fmt)
- return -EINVAL;
-
- ov8858_info = v4l2_get_subdev_hostdata(sd);
- if (ov8858_info == NULL)
- return -EINVAL;
-
- mutex_lock(&dev->input_lock);
-
- if ((fmt->width > OV8858_RES_WIDTH_MAX) ||
- (fmt->height > OV8858_RES_HEIGHT_MAX)) {
- fmt->width = OV8858_RES_WIDTH_MAX;
- fmt->height = OV8858_RES_HEIGHT_MAX;
- } else {
- idx = nearest_resolution_index(sd, fmt->width, fmt->height);
-
- /*
- * nearest_resolution_index() doesn't return smaller
- * resolutions. If it fails, it means the requested resolution
- * is higher than we can support. Fallback to highest possible
- * resolution in this case.
- */
- if (idx == -1)
- idx = dev->entries_curr_table - 1;
-
- fmt->width = dev->curr_res_table[idx].width;
- fmt->height = dev->curr_res_table[idx].height;
- }
-
- fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
- if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
- cfg->try_fmt = *fmt;
- mutex_unlock(&dev->input_lock);
- return 0;
- }
-
- dev->fmt_idx = nearest_resolution_index(sd, fmt->width, fmt->height);
- if (dev->fmt_idx == -1) {
- ret = -EINVAL;
- goto out;
- }
- res = &dev->curr_res_table[dev->fmt_idx];
- dev_dbg(&client->dev, "%s: selected width = %d, height = %d\n",
- __func__, res->width, res->height);
-
- /* Adjust the FPS selection based on the resolution selected */
- dev->fps_index = __ov8858_nearest_fps_index(dev->fps, res->fps_options);
- dev->fps = res->fps_options[dev->fps_index].fps;
- dev->regs = res->fps_options[dev->fps_index].regs;
- if (!dev->regs)
- dev->regs = res->regs;
-
- ret = ov8858_write_reg_array(client, dev->regs);
- if (ret)
- goto out;
-
- dev->pixels_per_line = res->fps_options[dev->fps_index].pixels_per_line;
- dev->lines_per_frame = res->fps_options[dev->fps_index].lines_per_frame;
-
- /* ov8858 only support RGB RAW10 output */
- ov8858_info->metadata_width = res->width * 10 / 8;
- ov8858_info->metadata_height = 2;
- ov8858_info->metadata_format = ATOMISP_INPUT_FORMAT_EMBEDDED;
-
- /* Set the initial exposure */
- ret = __ov8858_set_exposure(sd, dev->exposure, dev->gain,
- dev->digital_gain, &dev->pixels_per_line,
- &dev->lines_per_frame);
- if (ret)
- goto out;
-
- ret = ov8858_get_intg_factor(sd, ov8858_info, dev->regs);
-
-out:
- mutex_unlock(&dev->input_lock);
-
- return ret;
-}
-
-static int ov8858_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *format)
-{
- struct v4l2_mbus_framefmt *fmt = &format->format;
- struct ov8858_device *dev = to_ov8858_sensor(sd);
-
- if (format->pad)
- return -EINVAL;
- if (!fmt)
- return -EINVAL;
-
- mutex_lock(&dev->input_lock);
- fmt->width = dev->curr_res_table[dev->fmt_idx].width;
- fmt->height = dev->curr_res_table[dev->fmt_idx].height;
- fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
- mutex_unlock(&dev->input_lock);
-
- return 0;
-}
-
-static int ov8858_detect(struct i2c_client *client, u16 *id)
-{
- struct i2c_adapter *adapter = client->adapter;
- u16 id_hi = 0;
- u16 id_low = 0;
- int ret;
-
- /* i2c check */
- if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
- return -ENODEV;
-
- dev_dbg(&client->dev, "%s: I2C functionality ok\n", __func__);
- ret = ov8858_read_reg(client, OV8858_8BIT, OV8858_CHIP_ID_HIGH, &id_hi);
- if (ret)
- return ret;
- dev_dbg(&client->dev, "%s: id_high = 0x%04x\n", __func__, id_hi);
- ret = ov8858_read_reg(client, OV8858_8BIT, OV8858_CHIP_ID_LOW, &id_low);
- if (ret)
- return ret;
- dev_dbg(&client->dev, "%s: id_low = 0x%04x\n", __func__, id_low);
- *id = (id_hi << 8) | id_low;
-
- dev_dbg(&client->dev, "%s: chip_id = 0x%04x\n", __func__, *id);
-
- dev_info(&client->dev, "%s: chip_id = 0x%04x\n", __func__, *id);
- if (*id != OV8858_CHIP_ID)
- return -ENODEV;
-
- /* Stream off now. */
- return ov8858_write_reg(client, OV8858_8BIT, OV8858_STREAM_MODE, 0);
-}
-
-static void __ov8858_print_timing(struct v4l2_subdev *sd)
-{
- struct ov8858_device *dev = to_ov8858_sensor(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- u16 width = dev->curr_res_table[dev->fmt_idx].width;
- u16 height = dev->curr_res_table[dev->fmt_idx].height;
-
- dev_dbg(&client->dev, "Dump ov8858 timing in stream on:\n");
- dev_dbg(&client->dev, "width: %d:\n", width);
- dev_dbg(&client->dev, "height: %d:\n", height);
- dev_dbg(&client->dev, "pixels_per_line: %d:\n", dev->pixels_per_line);
- dev_dbg(&client->dev, "line per frame: %d:\n", dev->lines_per_frame);
- dev_dbg(&client->dev, "pix freq: %d:\n", dev->vt_pix_clk_freq_mhz);
- /* updated formula: pixels_per_line = 2 * HTS */
- /* updated formula: fps = SCLK / (VTS * HTS) */
- dev_dbg(&client->dev, "init fps: %d:\n", dev->vt_pix_clk_freq_mhz /
- (dev->pixels_per_line / 2) / dev->lines_per_frame);
- dev_dbg(&client->dev, "HBlank: %d nS:\n",
- 1000 * (dev->pixels_per_line - width) /
- (dev->vt_pix_clk_freq_mhz / 1000000));
- dev_dbg(&client->dev, "VBlank: %d uS:\n",
- (dev->lines_per_frame - height) * dev->pixels_per_line /
- (dev->vt_pix_clk_freq_mhz / 1000000));
-}
-
-/*
- * ov8858 stream on/off
- */
-static int ov8858_s_stream(struct v4l2_subdev *sd, int enable)
-{
- struct ov8858_device *dev = to_ov8858_sensor(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret;
- u16 val;
- dev_dbg(&client->dev, "%s: enable = %d\n", __func__, enable);
-
- /* Set orientation */
- ret = ov8858_read_reg(client, OV8858_8BIT, OV8858_FORMAT2, &val);
- if (ret)
- return ret;
-
- ret = ov8858_write_reg(client, OV8858_8BIT, OV8858_FORMAT2,
- dev->hflip ? val | OV8858_FLIP_ENABLE :
- val & ~OV8858_FLIP_ENABLE);
- if (ret)
- return ret;
-
- ret = ov8858_read_reg(client, OV8858_8BIT, OV8858_FORMAT1, &val);
- if (ret)
- return ret;
-
- ret = ov8858_write_reg(client, OV8858_8BIT, OV8858_FORMAT1,
- dev->vflip ? val | OV8858_FLIP_ENABLE :
- val & ~OV8858_FLIP_ENABLE);
- if (ret)
- return ret;
-
- mutex_lock(&dev->input_lock);
- if (enable) {
- __ov8858_print_timing(sd);
- ret = ov8858_write_reg_array(client, ov8858_streaming);
- if (ret != 0) {
- dev_err(&client->dev, "write_reg_array err\n");
- goto out;
- }
- dev->streaming = 1;
- } else {
- ret = ov8858_write_reg_array(client, ov8858_soft_standby);
- if (ret != 0) {
- dev_err(&client->dev, "write_reg_array err\n");
- goto out;
- }
- dev->streaming = 0;
- dev->fps_index = 0;
- dev->fps = 0;
- }
-out:
- mutex_unlock(&dev->input_lock);
- return ret;
-}
-
-static int __update_ov8858_device_settings(struct ov8858_device *dev,
- u16 sensor_id)
-{
- if (sensor_id == OV8858_CHIP_ID)
-#ifdef CONFIG_PLATFORM_BTNS
- dev->vcm_driver = &ov8858_vcms[OV8858_ID_DEFAULT];
-#else
- dev->vcm_driver = &ov8858_vcms[OV8858_SUNNY];
-#endif
- else
- return -ENODEV;
-
- if (dev->vcm_driver && dev->vcm_driver->init)
- return dev->vcm_driver->init(&dev->sd);
-
- return 0;
-}
-
-static int ov8858_s_config(struct v4l2_subdev *sd,
- int irq, void *pdata)
-{
- struct ov8858_device *dev = to_ov8858_sensor(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- u16 sensor_id;
- int ret;
-
- if (pdata == NULL)
- return -ENODEV;
-
- dev->platform_data = pdata;
-
- mutex_lock(&dev->input_lock);
-
- ret = __ov8858_s_power(sd, 1);
- if (ret) {
- dev_err(&client->dev, "power-up error %d!\n", ret);
- mutex_unlock(&dev->input_lock);
- return ret;
- }
-
- ret = dev->platform_data->csi_cfg(sd, 1);
- if (ret)
- goto fail_csi_cfg;
-
- /* config & detect sensor */
- ret = ov8858_detect(client, &sensor_id);
- if (ret) {
- dev_err(&client->dev, "detect error %d!\n", ret);
- goto fail_detect;
- }
-
- dev->sensor_id = sensor_id;
-
- /* power off sensor */
- ret = __ov8858_s_power(sd, 0);
- if (ret) {
- dev->platform_data->csi_cfg(sd, 0);
- dev_err(&client->dev, "__ov8858_s_power-down error %d!\n", ret);
- goto fail_update;
- }
-
- /* Resolution settings depend on sensor type and platform */
- ret = __update_ov8858_device_settings(dev, dev->sensor_id);
- if (ret) {
- dev->platform_data->csi_cfg(sd, 0);
- dev_err(&client->dev, "__update_ov8858_device_settings error %d!\n", ret);
- goto fail_update;
- }
-
- mutex_unlock(&dev->input_lock);
- return ret;
-
-fail_detect:
- dev->platform_data->csi_cfg(sd, 0);
-fail_csi_cfg:
- __ov8858_s_power(sd, 0);
-fail_update:
- mutex_unlock(&dev->input_lock);
- dev_err(&client->dev, "sensor power-gating failed\n");
- return ret;
-}
-
-static int
-ov8858_enum_mbus_code(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- if (code->index)
- return -EINVAL;
- code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
-
- return 0;
-}
-
-static int
-ov8858_enum_frame_size(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_frame_size_enum *fse)
-{
- int index = fse->index;
- struct ov8858_device *dev = to_ov8858_sensor(sd);
-
- mutex_lock(&dev->input_lock);
- if (index >= dev->entries_curr_table) {
- mutex_unlock(&dev->input_lock);
- return -EINVAL;
- }
-
- fse->min_width = dev->curr_res_table[index].width;
- fse->min_height = dev->curr_res_table[index].height;
- fse->max_width = dev->curr_res_table[index].width;
- fse->max_height = dev->curr_res_table[index].height;
- mutex_unlock(&dev->input_lock);
-
- return 0;
-}
-
-static int ov8858_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct ov8858_device *dev = container_of(
- ctrl->handler, struct ov8858_device, ctrl_handler);
- struct i2c_client *client = v4l2_get_subdevdata(&dev->sd);
-
- /* input_lock is taken by the control framework, so it
- * doesn't need to be taken here.
- */
-
- switch (ctrl->id) {
- case V4L2_CID_RUN_MODE:
- switch (ctrl->val) {
- case ATOMISP_RUN_MODE_VIDEO:
- dev->curr_res_table = ov8858_res_video;
- dev->entries_curr_table = ARRAY_SIZE(ov8858_res_video);
- break;
- case ATOMISP_RUN_MODE_STILL_CAPTURE:
- dev->curr_res_table = ov8858_res_still;
- dev->entries_curr_table = ARRAY_SIZE(ov8858_res_still);
- break;
- default:
- dev->curr_res_table = ov8858_res_preview;
- dev->entries_curr_table =
- ARRAY_SIZE(ov8858_res_preview);
- }
-
- dev->fmt_idx = 0;
- dev->fps_index = 0;
-
- return 0;
- case V4L2_CID_FOCUS_ABSOLUTE:
- if (dev->vcm_driver && dev->vcm_driver->t_focus_abs)
- return dev->vcm_driver->t_focus_abs(&dev->sd,
- ctrl->val);
- return 0;
- case V4L2_CID_EXPOSURE_AUTO_PRIORITY:
- if (ctrl->val == V4L2_EXPOSURE_AUTO)
- dev->limit_exposure_flag = false;
- else if (ctrl->val == V4L2_EXPOSURE_APERTURE_PRIORITY)
- dev->limit_exposure_flag = true;
- return 0;
- case V4L2_CID_HFLIP:
- dev->hflip = ctrl->val;
- return 0;
- case V4L2_CID_VFLIP:
- dev->vflip = ctrl->val;
- return 0;
- default:
- dev_err(&client->dev, "%s: Error: Invalid ctrl: 0x%X\n",
- __func__, ctrl->id);
- return -EINVAL;
- }
-}
-
-static int ov8858_g_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct ov8858_device *dev = container_of(
- ctrl->handler, struct ov8858_device, ctrl_handler);
- struct i2c_client *client = v4l2_get_subdevdata(&dev->sd);
- int r_odd, r_even;
- int i = dev->fmt_idx;
-
- switch (ctrl->id) {
- case V4L2_CID_FOCUS_STATUS:
- if (dev->vcm_driver && dev->vcm_driver->q_focus_status)
- return dev->vcm_driver->q_focus_status(&dev->sd,
- &(ctrl->val));
- return 0;
- case V4L2_CID_BIN_FACTOR_HORZ:
- r_odd = ov8858_get_register_8bit(&dev->sd, OV8858_H_INC_ODD,
- dev->curr_res_table[i].regs);
- if (r_odd < 0)
- return r_odd;
- r_even = ov8858_get_register_8bit(&dev->sd, OV8858_H_INC_EVEN,
- dev->curr_res_table[i].regs);
- if (r_even < 0)
- return r_even;
- ctrl->val = fls(r_odd + (r_even)) - 2;
- return 0;
-
- case V4L2_CID_BIN_FACTOR_VERT:
- r_odd = ov8858_get_register_8bit(&dev->sd, OV8858_V_INC_ODD,
- dev->curr_res_table[i].regs);
- if (r_odd < 0)
- return r_odd;
- r_even = ov8858_get_register_8bit(&dev->sd, OV8858_V_INC_EVEN,
- dev->curr_res_table[i].regs);
- if (r_even < 0)
- return r_even;
- ctrl->val = fls(r_odd + (r_even)) - 2;
- return 0;
- case V4L2_CID_HFLIP:
- ctrl->val = dev->hflip;
- break;
- case V4L2_CID_VFLIP:
- ctrl->val = dev->vflip;
- break;
- case V4L2_CID_EXPOSURE_ABSOLUTE:
- ctrl->val = dev->exposure;
- break;
- default:
- dev_warn(&client->dev,
- "%s: Error: Invalid ctrl: 0x%X\n", __func__, ctrl->id);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int
-ov8858_g_frame_interval(struct v4l2_subdev *sd,
- struct v4l2_subdev_frame_interval *interval)
-{
- struct ov8858_device *dev = to_ov8858_sensor(sd);
- const struct ov8858_resolution *res =
- &dev->curr_res_table[dev->fmt_idx];
-
- mutex_lock(&dev->input_lock);
- interval->interval.denominator = res->fps_options[dev->fps_index].fps;
- interval->interval.numerator = 1;
- mutex_unlock(&dev->input_lock);
- return 0;
-}
-
-static int __ov8858_s_frame_interval(struct v4l2_subdev *sd,
- struct v4l2_subdev_frame_interval *interval)
-{
- struct ov8858_device *dev = to_ov8858_sensor(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- const struct ov8858_resolution *res =
- &dev->curr_res_table[dev->fmt_idx];
- struct camera_mipi_info *info = NULL;
- unsigned int fps_index;
- int ret = 0;
- int fps;
-
- info = v4l2_get_subdev_hostdata(sd);
- if (info == NULL)
- return -EINVAL;
-
- if (!interval->interval.numerator)
- interval->interval.numerator = 1;
-
- fps = interval->interval.denominator / interval->interval.numerator;
-
- /* No need to proceed further if we are not streaming */
- if (!dev->streaming) {
- /* Save the new FPS and use it while selecting setting */
- dev->fps = fps;
- return 0;
- }
-
- /* Ignore if we are already using the required FPS. */
- if (fps == res->fps_options[dev->fps_index].fps)
- return 0;
-
- fps_index = __ov8858_nearest_fps_index(fps, res->fps_options);
-
- if (res->fps_options[fps_index].regs &&
- res->fps_options[fps_index].regs != dev->regs) {
- dev_err(&client->dev,
- "Sensor is streaming, can't apply new configuration\n");
- return -EBUSY;
- }
-
- dev->fps_index = fps_index;
- dev->fps = res->fps_options[dev->fps_index].fps;
-
- /* Update the new frametimings based on FPS */
- dev->pixels_per_line =
- res->fps_options[dev->fps_index].pixels_per_line;
- dev->lines_per_frame =
- res->fps_options[dev->fps_index].lines_per_frame;
-
- /* update frametiming. Conside the curren exposure/gain as well */
- ret = __ov8858_update_frame_timing(sd,
- &dev->pixels_per_line, &dev->lines_per_frame);
- if (ret)
- return ret;
-
- /* Update the new values so that user side knows the current settings */
- ret = ov8858_get_intg_factor(sd, info, dev->regs);
- if (ret)
- return ret;
-
- interval->interval.denominator = res->fps_options[dev->fps_index].fps;
- interval->interval.numerator = 1;
- __ov8858_print_timing(sd);
-
- return ret;
-}
-
-static int ov8858_s_frame_interval(struct v4l2_subdev *sd,
- struct v4l2_subdev_frame_interval *interval)
-{
- struct ov8858_device *dev = to_ov8858_sensor(sd);
- int ret;
-
- mutex_lock(&dev->input_lock);
- ret = __ov8858_s_frame_interval(sd, interval);
- mutex_unlock(&dev->input_lock);
-
- return ret;
-}
-
-static int ov8858_g_skip_frames(struct v4l2_subdev *sd, u32 *frames)
-{
- struct ov8858_device *dev = to_ov8858_sensor(sd);
-
- mutex_lock(&dev->input_lock);
- *frames = dev->curr_res_table[dev->fmt_idx].skip_frames;
- mutex_unlock(&dev->input_lock);
-
- return 0;
-}
-
-static const struct v4l2_subdev_sensor_ops ov8858_sensor_ops = {
- .g_skip_frames = ov8858_g_skip_frames,
-};
-
-static const struct v4l2_ctrl_ops ctrl_ops = {
- .s_ctrl = ov8858_s_ctrl,
- .g_volatile_ctrl = ov8858_g_ctrl,
-};
-
-static const struct v4l2_subdev_video_ops ov8858_video_ops = {
- .s_stream = ov8858_s_stream,
- .g_frame_interval = ov8858_g_frame_interval,
- .s_frame_interval = ov8858_s_frame_interval,
-};
-
-static const struct v4l2_subdev_core_ops ov8858_core_ops = {
- .s_power = ov8858_s_power,
- .ioctl = ov8858_ioctl,
- .init = ov8858_init,
-};
-
-static const struct v4l2_subdev_pad_ops ov8858_pad_ops = {
- .enum_mbus_code = ov8858_enum_mbus_code,
- .enum_frame_size = ov8858_enum_frame_size,
- .get_fmt = ov8858_get_fmt,
- .set_fmt = ov8858_set_fmt,
-};
-
-static const struct v4l2_subdev_ops ov8858_ops = {
- .core = &ov8858_core_ops,
- .video = &ov8858_video_ops,
- .pad = &ov8858_pad_ops,
- .sensor = &ov8858_sensor_ops,
-};
-
-static const struct media_entity_operations ov_entity_ops = {
- .link_setup = NULL,
-};
-
-static int ov8858_remove(struct i2c_client *client)
-{
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
- struct ov8858_device *dev = to_ov8858_sensor(sd);
-
- media_entity_cleanup(&dev->sd.entity);
- v4l2_ctrl_handler_free(&dev->ctrl_handler);
- dev->platform_data->csi_cfg(sd, 0);
- v4l2_device_unregister_subdev(sd);
- kfree(dev);
-
- return 0;
-}
-
-static const char * const ctrl_run_mode_menu[] = {
- NULL,
- "Video",
- "Still capture",
- "Continuous capture",
- "Preview",
-};
-
-static const struct v4l2_ctrl_config ctrl_run_mode = {
- .ops = &ctrl_ops,
- .id = V4L2_CID_RUN_MODE,
- .name = "run mode",
- .type = V4L2_CTRL_TYPE_MENU,
- .min = 1,
- .def = 4,
- .max = 4,
- .qmenu = ctrl_run_mode_menu,
-};
-
-static const struct v4l2_ctrl_config ctrls[] = {
- {
- .ops = &ctrl_ops,
- .id = V4L2_CID_VFLIP,
- .name = "Vertical flip",
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .min = false,
- .max = true,
- .step = 1,
- }, {
- .ops = &ctrl_ops,
- .id = V4L2_CID_HFLIP,
- .name = "Horizontal flip",
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .min = false,
- .max = true,
- .step = 1,
- }, {
- .ops = &ctrl_ops,
- .id = V4L2_CID_EXPOSURE_ABSOLUTE,
- .name = "Absolute exposure",
- .type = V4L2_CTRL_TYPE_INTEGER,
- .max = 0xffff,
- .min = 0x0,
- .step = 1,
- .def = 0x00,
- .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE,
- }, {
- .ops = &ctrl_ops,
- .id = V4L2_CID_FOCUS_ABSOLUTE,
- .name = "Focus absolute",
- .type = V4L2_CTRL_TYPE_INTEGER,
- .step = 1,
- .max = OV8858_MAX_FOCUS_POS,
- }, {
- /* This one is junk: see the spec for proper use of this CID. */
- .ops = &ctrl_ops,
- .id = V4L2_CID_FOCUS_STATUS,
- .name = "Focus status",
- .type = V4L2_CTRL_TYPE_INTEGER,
- .step = 1,
- .max = 100,
- .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE,
- }, {
- /* This is crap. For compatibility use only. */
- .ops = &ctrl_ops,
- .id = V4L2_CID_FOCAL_ABSOLUTE,
- .name = "Focal lenght",
- .type = V4L2_CTRL_TYPE_INTEGER,
- .min = (OV8858_FOCAL_LENGTH_NUM << 16) |
- OV8858_FOCAL_LENGTH_DEM,
- .max = (OV8858_FOCAL_LENGTH_NUM << 16) |
- OV8858_FOCAL_LENGTH_DEM,
- .step = 1,
- .def = (OV8858_FOCAL_LENGTH_NUM << 16) |
- OV8858_FOCAL_LENGTH_DEM,
- .flags = V4L2_CTRL_FLAG_READ_ONLY,
- }, {
- /* This one is crap, too. For compatibility use only. */
- .ops = &ctrl_ops,
- .id = V4L2_CID_FNUMBER_ABSOLUTE,
- .name = "F-number",
- .type = V4L2_CTRL_TYPE_INTEGER,
- .min = (OV8858_F_NUMBER_DEFAULT_NUM << 16) |
- OV8858_F_NUMBER_DEM,
- .max = (OV8858_F_NUMBER_DEFAULT_NUM << 16) |
- OV8858_F_NUMBER_DEM,
- .step = 1,
- .def = (OV8858_F_NUMBER_DEFAULT_NUM << 16) |
- OV8858_F_NUMBER_DEM,
- .flags = V4L2_CTRL_FLAG_READ_ONLY,
- }, {
- /*
- * The most utter crap. _Never_ use this, even for
- * compatibility reasons!
- */
- .ops = &ctrl_ops,
- .id = V4L2_CID_FNUMBER_RANGE,
- .name = "F-number range",
- .type = V4L2_CTRL_TYPE_INTEGER,
- .min = (OV8858_F_NUMBER_DEFAULT_NUM << 24) |
- (OV8858_F_NUMBER_DEM << 16) |
- (OV8858_F_NUMBER_DEFAULT_NUM << 8) |
- OV8858_F_NUMBER_DEM,
- .max = (OV8858_F_NUMBER_DEFAULT_NUM << 24) |
- (OV8858_F_NUMBER_DEM << 16) |
- (OV8858_F_NUMBER_DEFAULT_NUM << 8) |
- OV8858_F_NUMBER_DEM,
- .step = 1,
- .def = (OV8858_F_NUMBER_DEFAULT_NUM << 24) |
- (OV8858_F_NUMBER_DEM << 16) |
- (OV8858_F_NUMBER_DEFAULT_NUM << 8) |
- OV8858_F_NUMBER_DEM,
- .flags = V4L2_CTRL_FLAG_READ_ONLY,
- }, {
- .ops = &ctrl_ops,
- .id = V4L2_CID_BIN_FACTOR_HORZ,
- .name = "Horizontal binning factor",
- .type = V4L2_CTRL_TYPE_INTEGER,
- .max = OV8858_BIN_FACTOR_MAX,
- .step = 1,
- .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE,
- }, {
- .ops = &ctrl_ops,
- .id = V4L2_CID_BIN_FACTOR_VERT,
- .name = "Vertical binning factor",
- .type = V4L2_CTRL_TYPE_INTEGER,
- .max = OV8858_BIN_FACTOR_MAX,
- .step = 1,
- .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE,
- }, {
- .ops = &ctrl_ops,
- .id = V4L2_CID_EXPOSURE_AUTO_PRIORITY,
- .name = "Exposure auto priority",
- .type = V4L2_CTRL_TYPE_INTEGER,
- .min = V4L2_EXPOSURE_AUTO,
- .max = V4L2_EXPOSURE_APERTURE_PRIORITY,
- .step = 1,
- }
-};
-
-static int ov8858_probe(struct i2c_client *client)
-{
- struct ov8858_device *dev;
- unsigned int i;
- int ret = 0;
- struct camera_sensor_platform_data *pdata;
-
- dev_dbg(&client->dev, "%s:\n", __func__);
-
- /* allocate sensor device & init sub device */
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev)
- return -ENOMEM;
-
- mutex_init(&dev->input_lock);
-
- dev->fmt_idx = 0;
- dev->sensor_id = OV_ID_DEFAULT;
- dev->vcm_driver = &ov8858_vcms[OV8858_ID_DEFAULT];
-
- v4l2_i2c_subdev_init(&(dev->sd), client, &ov8858_ops);
-
- pdata = gmin_camera_platform_data(&dev->sd,
- ATOMISP_INPUT_FORMAT_RAW_10,
- atomisp_bayer_order_bggr);
- if (!pdata) {
- dev_err(&client->dev,
- "%s: failed to get acpi platform data\n",
- __func__);
- goto out_free;
- }
- ret = ov8858_s_config(&dev->sd, client->irq, pdata);
- if (ret) {
- dev_err(&client->dev,
- "%s: failed to set config\n", __func__);
- goto out_free;
- }
- ret = atomisp_register_i2c_module(&dev->sd, pdata, RAW_CAMERA);
- if (ret) {
- dev_err(&client->dev,
- "%s: failed to register subdev\n", __func__);
- goto out_free;
- }
-
- /*
- * sd->name is updated with sensor driver name by the v4l2.
- * change it to sensor name in this case.
- */
- snprintf(dev->sd.name, sizeof(dev->sd.name), "%s%x %d-%04x",
- OV_SUBDEV_PREFIX, dev->sensor_id,
- i2c_adapter_id(client->adapter), client->addr);
-
- dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
- dev->pad.flags = MEDIA_PAD_FL_SOURCE;
- dev->format.code = MEDIA_BUS_FMT_SBGGR10_1X10;
- dev->sd.entity.ops = &ov_entity_ops;
- dev->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
-
- ret = v4l2_ctrl_handler_init(&dev->ctrl_handler, ARRAY_SIZE(ctrls) + 1);
- if (ret) {
- ov8858_remove(client);
- return ret;
- }
-
- dev->run_mode = v4l2_ctrl_new_custom(&dev->ctrl_handler,
- &ctrl_run_mode, NULL);
-
- for (i = 0; i < ARRAY_SIZE(ctrls); i++)
- v4l2_ctrl_new_custom(&dev->ctrl_handler, &ctrls[i], NULL);
-
- if (dev->ctrl_handler.error) {
- ov8858_remove(client);
- return dev->ctrl_handler.error;
- }
-
- /* Use same lock for controls as for everything else. */
- dev->ctrl_handler.lock = &dev->input_lock;
- dev->sd.ctrl_handler = &dev->ctrl_handler;
- v4l2_ctrl_handler_setup(&dev->ctrl_handler);
-
- ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad);
- if (ret) {
- ov8858_remove(client);
- return ret;
- }
-
- return 0;
-
-out_free:
- v4l2_device_unregister_subdev(&dev->sd);
- kfree(dev);
- return ret;
-}
-
-static const struct acpi_device_id ov8858_acpi_match[] = {
- {"INT3477"},
- {},
-};
-MODULE_DEVICE_TABLE(acpi, ov8858_acpi_match);
-
-static struct i2c_driver ov8858_driver = {
- .driver = {
- .name = "ov8858",
- .acpi_match_table = ov8858_acpi_match,
- },
- .probe_new = ov8858_probe,
- .remove = ov8858_remove,
-};
-module_i2c_driver(ov8858_driver);
-
-MODULE_DESCRIPTION("A low-level driver for Omnivision OV8858 sensors");
-MODULE_LICENSE("GPL");
diff --git a/drivers/staging/media/atomisp/i2c/ov8858.h b/drivers/staging/media/atomisp/i2c/ov8858.h
deleted file mode 100644
index 6c89568bb44e..000000000000
--- a/drivers/staging/media/atomisp/i2c/ov8858.h
+++ /dev/null
@@ -1,1474 +0,0 @@
-/*
- * Support for the Omnivision OV8858 camera sensor.
- *
- * Copyright (c) 2014 Intel Corporation. All Rights Reserved.
- *
- * 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.
- *
- * 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.
- *
- *
- */
-
-#ifndef __OV8858_H__
-#define __OV8858_H__
-#include "../include/linux/atomisp_platform.h"
-#include <media/v4l2-ctrls.h>
-
-#define I2C_MSG_LENGTH 0x2
-
-/*
- * This should be added into include/linux/videodev2.h
- * NOTE: This is most likely not used anywhere.
- */
-#define V4L2_IDENT_OV8858 V4L2_IDENT_UNKNOWN
-
-/*
- * Indexes for VCM driver lists
- */
-#define OV8858_ID_DEFAULT 0
-#define OV8858_SUNNY 1
-
-#define OV8858_OTP_START_ADDR 0x7010
-#define OV8858_OTP_END_ADDR 0x7186
-
-/*
- * ov8858 System control registers
- */
-
-#define OV8858_OTP_LOAD_CTRL 0x3D81
-#define OV8858_OTP_MODE_CTRL 0x3D84
-#define OV8858_OTP_START_ADDR_REG 0x3D88
-#define OV8858_OTP_END_ADDR_REG 0x3D8A
-#define OV8858_OTP_ISP_CTRL2 0x5002
-
-#define OV8858_OTP_MODE_MANUAL BIT(6)
-#define OV8858_OTP_MODE_PROGRAM_DISABLE BIT(7)
-#define OV8858_OTP_LOAD_ENABLE BIT(0)
-#define OV8858_OTP_DPC_ENABLE BIT(3)
-
-#define OV8858_PLL1_PREDIV0 0x030A
-#define OV8858_PLL1_PREDIV 0x0300
-#define OV8858_PLL1_MULTIPLIER 0x0301
-#define OV8858_PLL1_SYS_PRE_DIV 0x0305
-#define OV8858_PLL1_SYS_DIVIDER 0x0306
-
-#define OV8858_PLL1_PREDIV0_MASK BIT(0)
-#define OV8858_PLL1_PREDIV_MASK (BIT(0) | BIT(1) | BIT(2))
-#define OV8858_PLL1_MULTIPLIER_MASK 0x01FF
-#define OV8858_PLL1_SYS_PRE_DIV_MASK (BIT(0) | BIT(1))
-#define OV8858_PLL1_SYS_DIVIDER_MASK BIT(0)
-
-#define OV8858_PLL2_PREDIV0 0x0312
-#define OV8858_PLL2_PREDIV 0x030B
-#define OV8858_PLL2_MULTIPLIER 0x030C
-#define OV8858_PLL2_DAC_DIVIDER 0x0312
-#define OV8858_PLL2_SYS_PRE_DIV 0x030F
-#define OV8858_PLL2_SYS_DIVIDER 0x030E
-
-#define OV8858_PLL2_PREDIV0_MASK BIT(4)
-#define OV8858_PLL2_PREDIV_MASK (BIT(0) | BIT(1) | BIT(2))
-#define OV8858_PLL2_MULTIPLIER_MASK 0x01FF
-#define OV8858_PLL2_DAC_DIVIDER_MASK (BIT(0) | BIT(1) | BIT(2) | BIT(3))
-#define OV8858_PLL2_SYS_PRE_DIV_MASK (BIT(0) | BIT(1) | BIT(2) | BIT(3))
-#define OV8858_PLL2_SYS_DIVIDER_MASK (BIT(0) | BIT(1) | BIT(2))
-
-#define OV8858_PLL_SCLKSEL1 0x3032
-#define OV8858_PLL_SCLKSEL2 0x3033
-#define OV8858_SRB_HOST_INPUT_DIS 0x3106
-
-#define OV8858_PLL_SCLKSEL1_MASK BIT(7)
-#define OV8858_PLL_SCLKSEL2_MASK BIT(1)
-
-#define OV8858_SYS_PRE_DIV_OFFSET 2
-#define OV8858_SYS_PRE_DIV_MASK (BIT(2) | BIT(3))
-#define OV8858_SCLK_PDIV_OFFSET 4
-#define OV8858_SCLK_PDIV_MASK (BIT(4) | BIT(5) | BIT(6) | BIT(7))
-
-#define OV8858_TIMING_HTS 0x380C
-#define OV8858_TIMING_VTS 0x380E
-
-#define OV8858_HORIZONTAL_START_H 0x3800
-#define OV8858_VERTICAL_START_H 0x3802
-#define OV8858_HORIZONTAL_END_H 0x3804
-#define OV8858_VERTICAL_END_H 0x3806
-#define OV8858_HORIZONTAL_OUTPUT_SIZE_H 0x3808
-#define OV8858_VERTICAL_OUTPUT_SIZE_H 0x380A
-
-#define OV8858_GROUP_ACCESS 0x3208
-#define OV8858_GROUP_ZERO 0x00
-#define OV8858_GROUP_ACCESS_HOLD_START 0x00
-#define OV8858_GROUP_ACCESS_HOLD_END 0x10
-#define OV8858_GROUP_ACCESS_DELAY_LAUNCH 0xA0
-#define OV8858_GROUP_ACCESS_QUICK_LAUNCH 0xE0
-
-#define OV_SUBDEV_PREFIX "ov"
-#define OV_ID_DEFAULT 0x0000
-#define OV8858_CHIP_ID 0x8858
-
-#define OV8858_LONG_EXPO 0x3500
-#define OV8858_LONG_GAIN 0x3508
-#define OV8858_LONG_DIGI_GAIN 0x350A
-#define OV8858_SHORT_GAIN 0x350C
-#define OV8858_SHORT_DIGI_GAIN 0x350E
-
-#define OV8858_FORMAT1 0x3820
-#define OV8858_FORMAT2 0x3821
-
-#define OV8858_FLIP_ENABLE 0x06
-
-#define OV8858_MWB_RED_GAIN_H 0x5032
-#define OV8858_MWB_GREEN_GAIN_H 0x5034
-#define OV8858_MWB_BLUE_GAIN_H 0x5036
-#define OV8858_MWB_GAIN_MAX 0x0FFF
-
-#define OV8858_CHIP_ID_HIGH 0x300B
-#define OV8858_CHIP_ID_LOW 0x300C
-#define OV8858_STREAM_MODE 0x0100
-
-#define OV8858_FOCAL_LENGTH_NUM 294 /* 2.94mm */
-#define OV8858_FOCAL_LENGTH_DEM 100
-#define OV8858_F_NUMBER_DEFAULT_NUM 24 /* 2.4 */
-#define OV8858_F_NUMBER_DEM 10
-
-#define OV8858_H_INC_ODD 0x3814
-#define OV8858_H_INC_EVEN 0x3815
-#define OV8858_V_INC_ODD 0x382A
-#define OV8858_V_INC_EVEN 0x382B
-
-#define OV8858_READ_MODE_BINNING_ON 0x0400 /* ToDo: Check this */
-#define OV8858_READ_MODE_BINNING_OFF 0x00 /* ToDo: Check this */
-#define OV8858_BIN_FACTOR_MAX 2
-#define OV8858_INTEGRATION_TIME_MARGIN 14
-
-#define OV8858_MAX_VTS_VALUE 0xFFFF
-#define OV8858_MAX_EXPOSURE_VALUE \
- (OV8858_MAX_VTS_VALUE - OV8858_INTEGRATION_TIME_MARGIN)
-#define OV8858_MAX_GAIN_VALUE 0x07FF
-
-#define OV8858_MAX_FOCUS_POS 1023
-
-#define OV8858_TEST_PATTERN_REG 0x5E00
-
-struct ov8858_vcm {
- int (*power_up)(struct v4l2_subdev *sd);
- int (*power_down)(struct v4l2_subdev *sd);
- int (*init)(struct v4l2_subdev *sd);
- int (*t_focus_abs)(struct v4l2_subdev *sd, s32 value);
- int (*t_focus_rel)(struct v4l2_subdev *sd, s32 value);
- int (*q_focus_status)(struct v4l2_subdev *sd, s32 *value);
- int (*q_focus_abs)(struct v4l2_subdev *sd, s32 *value);
- int (*t_vcm_slew)(struct v4l2_subdev *sd, s32 value);
- int (*t_vcm_timing)(struct v4l2_subdev *sd, s32 value);
-};
-
-/*
- * Defines for register writes and register array processing
- * */
-#define OV8858_BYTE_MAX 32
-#define OV8858_SHORT_MAX 16
-#define OV8858_TOK_MASK 0xFFF0
-
-#define MAX_FPS_OPTIONS_SUPPORTED 3
-
-#define OV8858_DEPTH_COMP_CONST 2200
-#define OV8858_DEPTH_VTS_CONST 2573
-
-enum ov8858_tok_type {
- OV8858_8BIT = 0x0001,
- OV8858_16BIT = 0x0002,
- OV8858_TOK_TERM = 0xF000, /* terminating token for reg list */
- OV8858_TOK_DELAY = 0xFE00 /* delay token for reg list */
-};
-
-/*
- * If register address or register width is not 32 bit width,
- * user needs to convert it manually
- */
-struct s_register_setting {
- u32 reg;
- u32 val;
-};
-
-/**
- * struct ov8858_reg - MI sensor register format
- * @type: type of the register
- * @reg: 16-bit offset to register
- * @val: 8/16/32-bit register value
- *
- * Define a structure for sensor register initialization values
- */
-struct ov8858_reg {
- enum ov8858_tok_type type;
- u16 sreg;
- u32 val; /* @set value for read/mod/write, @mask */
-};
-
-struct ov8858_fps_setting {
- int fps;
- unsigned short pixels_per_line;
- unsigned short lines_per_frame;
- const struct ov8858_reg *regs; /* regs that the fps setting needs */
-};
-
-struct ov8858_resolution {
- u8 *desc;
- const struct ov8858_reg *regs;
- int res;
- int width;
- int height;
- bool used;
- u8 bin_factor_x;
- u8 bin_factor_y;
- unsigned short skip_frames;
- const struct ov8858_fps_setting fps_options[MAX_FPS_OPTIONS_SUPPORTED];
-};
-
-/*
- * ov8858 device structure
- * */
-struct ov8858_device {
- struct v4l2_subdev sd;
- struct media_pad pad;
- struct v4l2_mbus_framefmt format;
-
- struct camera_sensor_platform_data *platform_data;
- struct mutex input_lock; /* serialize sensor's ioctl */
- int fmt_idx;
- int streaming;
- int vt_pix_clk_freq_mhz;
- int fps_index;
- u16 sensor_id; /* Sensor id from registers */
- u16 i2c_id; /* Sensor id from i2c_device_id */
- int exposure;
- int gain;
- u16 digital_gain;
- u16 pixels_per_line;
- u16 lines_per_frame;
- u8 fps;
- u8 *otp_data;
- /* Prevent the framerate from being lowered in low light scenes. */
- int limit_exposure_flag;
- bool hflip;
- bool vflip;
-
- const struct ov8858_reg *regs;
- struct ov8858_vcm *vcm_driver;
- const struct ov8858_resolution *curr_res_table;
- unsigned long entries_curr_table;
-
- struct v4l2_ctrl_handler ctrl_handler;
- struct v4l2_ctrl *run_mode;
-};
-
-#define to_ov8858_sensor(x) container_of(x, struct ov8858_device, sd)
-
-#define OV8858_MAX_WRITE_BUF_SIZE 32
-struct ov8858_write_buffer {
- u16 addr;
- u8 data[OV8858_MAX_WRITE_BUF_SIZE];
-};
-
-struct ov8858_write_ctrl {
- int index;
- struct ov8858_write_buffer buffer;
-};
-
-static const struct ov8858_reg ov8858_soft_standby[] = {
- {OV8858_8BIT, 0x0100, 0x00},
- {OV8858_TOK_TERM, 0, 0}
-};
-
-static const struct ov8858_reg ov8858_streaming[] = {
- {OV8858_8BIT, 0x0100, 0x01},
- {OV8858_TOK_TERM, 0, 0}
-};
-
-static const struct ov8858_reg ov8858_param_hold[] = {
- {OV8858_8BIT, OV8858_GROUP_ACCESS,
- OV8858_GROUP_ZERO | OV8858_GROUP_ACCESS_HOLD_START},
- {OV8858_TOK_TERM, 0, 0}
-};
-
-static const struct ov8858_reg ov8858_param_update[] = {
- {OV8858_8BIT, OV8858_GROUP_ACCESS,
- OV8858_GROUP_ZERO | OV8858_GROUP_ACCESS_HOLD_END},
- {OV8858_8BIT, OV8858_GROUP_ACCESS,
- OV8858_GROUP_ZERO | OV8858_GROUP_ACCESS_DELAY_LAUNCH},
- {OV8858_TOK_TERM, 0, 0}
-};
-
-extern int dw9718_vcm_power_up(struct v4l2_subdev *sd);
-extern int dw9718_vcm_power_down(struct v4l2_subdev *sd);
-extern int dw9718_vcm_init(struct v4l2_subdev *sd);
-extern int dw9718_t_focus_abs(struct v4l2_subdev *sd, s32 value);
-extern int dw9718_t_focus_rel(struct v4l2_subdev *sd, s32 value);
-extern int dw9718_q_focus_status(struct v4l2_subdev *sd, s32 *value);
-extern int dw9718_q_focus_abs(struct v4l2_subdev *sd, s32 *value);
-extern int dw9718_t_vcm_slew(struct v4l2_subdev *sd, s32 value);
-extern int dw9718_t_vcm_timing(struct v4l2_subdev *sd, s32 value);
-
-extern int vcm_power_up(struct v4l2_subdev *sd);
-extern int vcm_power_down(struct v4l2_subdev *sd);
-
-static struct ov8858_vcm ov8858_vcms[] = {
- [OV8858_SUNNY] = {
- .power_up = dw9718_vcm_power_up,
- .power_down = dw9718_vcm_power_down,
- .init = dw9718_vcm_init,
- .t_focus_abs = dw9718_t_focus_abs,
- .t_focus_rel = dw9718_t_focus_rel,
- .q_focus_status = dw9718_q_focus_status,
- .q_focus_abs = dw9718_q_focus_abs,
- .t_vcm_slew = dw9718_t_vcm_slew,
- .t_vcm_timing = dw9718_t_vcm_timing,
- },
- [OV8858_ID_DEFAULT] = {
- .power_up = NULL,
- .power_down = NULL,
- },
-};
-
-
-#define OV8858_RES_WIDTH_MAX 3280
-#define OV8858_RES_HEIGHT_MAX 2464
-
-static struct ov8858_reg ov8858_BasicSettings[] = {
- {OV8858_8BIT, 0x0103, 0x01}, /* software_reset */
- {OV8858_8BIT, 0x0100, 0x00}, /* software_standby */
- /* PLL settings */
- {OV8858_8BIT, 0x0300, 0x05}, /* pll1_pre_div = /4 */
- {OV8858_8BIT, 0x0302, 0xAF}, /* pll1_multiplier = 175 */
- {OV8858_8BIT, 0x0303, 0x00}, /* pll1_divm = /(1 + 0) */
- {OV8858_8BIT, 0x0304, 0x03}, /* pll1_div_mipi = /8 */
- {OV8858_8BIT, 0x030B, 0x02}, /* pll2_pre_div = /2 */
- {OV8858_8BIT, 0x030D, 0x4E}, /* pll2_r_divp = 78 */
- {OV8858_8BIT, 0x030E, 0x00}, /* pll2_r_divs = /1 */
- {OV8858_8BIT, 0x030F, 0x04}, /* pll2_r_divsp = /(1 + 4) */
- /* pll2_pre_div0 = /1, pll2_r_divdac = /(1 + 1) */
- {OV8858_8BIT, 0x0312, 0x01},
- {OV8858_8BIT, 0x031E, 0x0C}, /* pll1_no_lat = 1, mipi_bitsel_man = 0 */
-
- /* PAD OEN2, VSYNC out enable=0x80, disable=0x00 */
- {OV8858_8BIT, 0x3002, 0x80},
- /* PAD OUT2, VSYNC pulse direction low-to-high = 1 */
- {OV8858_8BIT, 0x3007, 0x01},
- /* PAD SEL2, VSYNC out value = 0 */
- {OV8858_8BIT, 0x300D, 0x00},
- /* PAD OUT2, VSYNC out select = 0 */
- {OV8858_8BIT, 0x3010, 0x00},
-
- /* Npump clock div = /2, Ppump clock div = /4 */
- {OV8858_8BIT, 0x3015, 0x01},
- /*
- * mipi_lane_mode = 1+3, mipi_lvds_sel = 1 = MIPI enable,
- * r_phy_pd_mipi_man = 0, lane_dis_option = 0
- */
- {OV8858_8BIT, 0x3018, 0x72},
- /* Clock switch output = normal, pclk_div = /1 */
- {OV8858_8BIT, 0x3020, 0x93},
- /*
- * lvds_mode_o = 0, clock lane disable when pd_mipi = 0,
- * pd_mipi enable when rst_sync = 1
- */
- {OV8858_8BIT, 0x3022, 0x01},
- {OV8858_8BIT, 0x3031, 0x0A}, /* mipi_bit_sel = 10 */
- {OV8858_8BIT, 0x3034, 0x00}, /* Unknown */
- /* sclk_div = /1, sclk_pre_div = /1, chip debug = 1 */
- {OV8858_8BIT, 0x3106, 0x01},
-
- {OV8858_8BIT, 0x3305, 0xF1}, /* Unknown */
- {OV8858_8BIT, 0x3307, 0x04}, /* Unknown */
- {OV8858_8BIT, 0x3308, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x3309, 0x28}, /* Unknown */
- {OV8858_8BIT, 0x330A, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x330B, 0x20}, /* Unknown */
- {OV8858_8BIT, 0x330C, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x330D, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x330E, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x330F, 0x40}, /* Unknown */
-
- {OV8858_8BIT, 0x3500, 0x00}, /* long exposure = 0x9A20 */
- {OV8858_8BIT, 0x3501, 0x9A}, /* long exposure = 0x9A20 */
- {OV8858_8BIT, 0x3502, 0x20}, /* long exposure = 0x9A20 */
- /*
- * Digital fraction gain delay option = Delay 1 frame,
- * Gain change delay option = Delay 1 frame,
- * Gain delay option = Delay 1 frame,
- * Gain manual as sensor gain = Input gain as real gain format,
- * Exposure delay option (must be 0 = Delay 1 frame,
- * Exposure change delay option (must be 0) = Delay 1 frame
- */
- {OV8858_8BIT, 0x3503, 0x00},
- {OV8858_8BIT, 0x3505, 0x80}, /* gain conversation option */
- /*
- * [10:7] are integer gain, [6:0] are fraction gain. For example:
- * 0x80 is 1x gain, 0x100 is 2x gain, 0x1C0 is 3.5x gain
- */
- {OV8858_8BIT, 0x3508, 0x02}, /* long gain = 0x0200 */
- {OV8858_8BIT, 0x3509, 0x00}, /* long gain = 0x0200 */
- {OV8858_8BIT, 0x350C, 0x00}, /* short gain = 0x0080 */
- {OV8858_8BIT, 0x350D, 0x80}, /* short gain = 0x0080 */
- {OV8858_8BIT, 0x3510, 0x00}, /* short exposure = 0x000200 */
- {OV8858_8BIT, 0x3511, 0x02}, /* short exposure = 0x000200 */
- {OV8858_8BIT, 0x3512, 0x00}, /* short exposure = 0x000200 */
-
- {OV8858_8BIT, 0x3600, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x3601, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x3602, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x3603, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x3604, 0x22}, /* Unknown */
- {OV8858_8BIT, 0x3605, 0x30}, /* Unknown */
- {OV8858_8BIT, 0x3606, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x3607, 0x20}, /* Unknown */
- {OV8858_8BIT, 0x3608, 0x11}, /* Unknown */
- {OV8858_8BIT, 0x3609, 0x28}, /* Unknown */
- {OV8858_8BIT, 0x360A, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x360B, 0x06}, /* Unknown */
- {OV8858_8BIT, 0x360C, 0xDC}, /* Unknown */
- {OV8858_8BIT, 0x360D, 0x40}, /* Unknown */
- {OV8858_8BIT, 0x360E, 0x0C}, /* Unknown */
- {OV8858_8BIT, 0x360F, 0x20}, /* Unknown */
- {OV8858_8BIT, 0x3610, 0x07}, /* Unknown */
- {OV8858_8BIT, 0x3611, 0x20}, /* Unknown */
- {OV8858_8BIT, 0x3612, 0x88}, /* Unknown */
- {OV8858_8BIT, 0x3613, 0x80}, /* Unknown */
- {OV8858_8BIT, 0x3614, 0x58}, /* Unknown */
- {OV8858_8BIT, 0x3615, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x3616, 0x4A}, /* Unknown */
- {OV8858_8BIT, 0x3617, 0x90}, /* Unknown */
- {OV8858_8BIT, 0x3618, 0x56}, /* Unknown */
- {OV8858_8BIT, 0x3619, 0x70}, /* Unknown */
- {OV8858_8BIT, 0x361A, 0x99}, /* Unknown */
- {OV8858_8BIT, 0x361B, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x361C, 0x07}, /* Unknown */
- {OV8858_8BIT, 0x361D, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x361E, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x361F, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x3633, 0x0C}, /* Unknown */
- {OV8858_8BIT, 0x3634, 0x0C}, /* Unknown */
- {OV8858_8BIT, 0x3635, 0x0C}, /* Unknown */
- {OV8858_8BIT, 0x3636, 0x0C}, /* Unknown */
- {OV8858_8BIT, 0x3638, 0xFF}, /* Unknown */
- {OV8858_8BIT, 0x3645, 0x13}, /* Unknown */
- {OV8858_8BIT, 0x3646, 0x83}, /* Unknown */
- {OV8858_8BIT, 0x364A, 0x07}, /* Unknown */
-
- {OV8858_8BIT, 0x3700, 0x30}, /* Unknown */
- {OV8858_8BIT, 0x3701, 0x18}, /* Unknown */
- {OV8858_8BIT, 0x3702, 0x50}, /* Unknown */
- {OV8858_8BIT, 0x3703, 0x32}, /* Unknown */
- {OV8858_8BIT, 0x3704, 0x28}, /* Unknown */
- {OV8858_8BIT, 0x3705, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x3706, 0x6A}, /* Unknown */
- {OV8858_8BIT, 0x3707, 0x08}, /* Unknown */
- {OV8858_8BIT, 0x3708, 0x48}, /* Unknown */
- {OV8858_8BIT, 0x3709, 0x66}, /* Unknown */
- {OV8858_8BIT, 0x370A, 0x01}, /* Unknown */
- {OV8858_8BIT, 0x370B, 0x6A}, /* Unknown */
- {OV8858_8BIT, 0x370C, 0x07}, /* Unknown */
- {OV8858_8BIT, 0x3712, 0x44}, /* Unknown */
- {OV8858_8BIT, 0x3714, 0x24}, /* Unknown */
- {OV8858_8BIT, 0x3718, 0x14}, /* Unknown */
- {OV8858_8BIT, 0x3719, 0x31}, /* Unknown */
- {OV8858_8BIT, 0x371E, 0x31}, /* Unknown */
- {OV8858_8BIT, 0x371F, 0x7F}, /* Unknown */
- {OV8858_8BIT, 0x3720, 0x0A}, /* Unknown */
- {OV8858_8BIT, 0x3721, 0x0A}, /* Unknown */
- {OV8858_8BIT, 0x3724, 0x0C}, /* Unknown */
- {OV8858_8BIT, 0x3725, 0x02}, /* Unknown */
- {OV8858_8BIT, 0x3726, 0x0C}, /* Unknown */
- {OV8858_8BIT, 0x3728, 0x0A}, /* Unknown */
- {OV8858_8BIT, 0x3729, 0x03}, /* Unknown */
- {OV8858_8BIT, 0x372A, 0x06}, /* Unknown */
- {OV8858_8BIT, 0x372B, 0xA6}, /* Unknown */
- {OV8858_8BIT, 0x372C, 0xA6}, /* Unknown */
- {OV8858_8BIT, 0x372D, 0xA6}, /* Unknown */
- {OV8858_8BIT, 0x372E, 0x0C}, /* Unknown */
- {OV8858_8BIT, 0x372F, 0x20}, /* Unknown */
- {OV8858_8BIT, 0x3730, 0x02}, /* Unknown */
- {OV8858_8BIT, 0x3731, 0x0C}, /* Unknown */
- {OV8858_8BIT, 0x3732, 0x28}, /* Unknown */
- {OV8858_8BIT, 0x3733, 0x10}, /* Unknown */
- {OV8858_8BIT, 0x3734, 0x40}, /* Unknown */
- {OV8858_8BIT, 0x3736, 0x30}, /* Unknown */
- {OV8858_8BIT, 0x373A, 0x0A}, /* Unknown */
- {OV8858_8BIT, 0x373B, 0x0B}, /* Unknown */
- {OV8858_8BIT, 0x373C, 0x14}, /* Unknown */
- {OV8858_8BIT, 0x373E, 0x06}, /* Unknown */
- {OV8858_8BIT, 0x3755, 0x10}, /* Unknown */
- {OV8858_8BIT, 0x3758, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x3759, 0x4C}, /* Unknown */
- {OV8858_8BIT, 0x375A, 0x0C}, /* Unknown */
- {OV8858_8BIT, 0x375B, 0x26}, /* Unknown */
- {OV8858_8BIT, 0x375C, 0x20}, /* Unknown */
- {OV8858_8BIT, 0x375D, 0x04}, /* Unknown */
- {OV8858_8BIT, 0x375E, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x375F, 0x28}, /* Unknown */
- {OV8858_8BIT, 0x3760, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x3761, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x3762, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x3763, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x3766, 0xFF}, /* Unknown */
- {OV8858_8BIT, 0x3768, 0x22}, /* Unknown */
- {OV8858_8BIT, 0x3769, 0x44}, /* Unknown */
- {OV8858_8BIT, 0x376A, 0x44}, /* Unknown */
- {OV8858_8BIT, 0x376B, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x376F, 0x01}, /* Unknown */
- {OV8858_8BIT, 0x3772, 0x46}, /* Unknown */
- {OV8858_8BIT, 0x3773, 0x04}, /* Unknown */
- {OV8858_8BIT, 0x3774, 0x2C}, /* Unknown */
- {OV8858_8BIT, 0x3775, 0x13}, /* Unknown */
- {OV8858_8BIT, 0x3776, 0x08}, /* Unknown */
- {OV8858_8BIT, 0x3777, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x3778, 0x16}, /* Unknown */
- {OV8858_8BIT, 0x37A0, 0x88}, /* Unknown */
- {OV8858_8BIT, 0x37A1, 0x7A}, /* Unknown */
- {OV8858_8BIT, 0x37A2, 0x7A}, /* Unknown */
- {OV8858_8BIT, 0x37A3, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x37A4, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x37A5, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x37A6, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x37A7, 0x88}, /* Unknown */
- {OV8858_8BIT, 0x37A8, 0x98}, /* Unknown */
- {OV8858_8BIT, 0x37A9, 0x98}, /* Unknown */
- {OV8858_8BIT, 0x37AA, 0x88}, /* Unknown */
- {OV8858_8BIT, 0x37AB, 0x5C}, /* Unknown */
- {OV8858_8BIT, 0x37AC, 0x5C}, /* Unknown */
- {OV8858_8BIT, 0x37AD, 0x55}, /* Unknown */
- {OV8858_8BIT, 0x37AE, 0x19}, /* Unknown */
- {OV8858_8BIT, 0x37AF, 0x19}, /* Unknown */
- {OV8858_8BIT, 0x37B0, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x37B1, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x37B2, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x37B3, 0x84}, /* Unknown */
- {OV8858_8BIT, 0x37B4, 0x84}, /* Unknown */
- {OV8858_8BIT, 0x37B5, 0x66}, /* Unknown */
- {OV8858_8BIT, 0x37B6, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x37B7, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x37B8, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x37B9, 0xFF}, /* Unknown */
-
- {OV8858_8BIT, 0x3800, 0x00}, /* h_crop_start high */
- {OV8858_8BIT, 0x3801, 0x0C}, /* h_crop_start low */
- {OV8858_8BIT, 0x3802, 0x00}, /* v_crop_start high */
- {OV8858_8BIT, 0x3803, 0x0C}, /* v_crop_start low */
- {OV8858_8BIT, 0x3804, 0x0C}, /* h_crop_end high */
- {OV8858_8BIT, 0x3805, 0xD3}, /* h_crop_end low */
- {OV8858_8BIT, 0x3806, 0x09}, /* v_crop_end high */
- {OV8858_8BIT, 0x3807, 0xA3}, /* v_crop_end low */
- {OV8858_8BIT, 0x3808, 0x0C}, /* h_output_size high */
- {OV8858_8BIT, 0x3809, 0xC0}, /* h_output_size low */
- {OV8858_8BIT, 0x380A, 0x09}, /* v_output_size high */
- {OV8858_8BIT, 0x380B, 0x90}, /* v_output_size low */
- {OV8858_8BIT, 0x380C, 0x07}, /* horizontal timing size high */
- {OV8858_8BIT, 0x380D, 0x94}, /* horizontal timing size low */
- {OV8858_8BIT, 0x380E, 0x0A}, /* vertical timing size high */
- {OV8858_8BIT, 0x380F, 0x0D}, /* vertical timing size low */
- {OV8858_8BIT, 0x3810, 0x00}, /* h_win offset high */
- {OV8858_8BIT, 0x3811, 0x04}, /* h_win offset low */
- {OV8858_8BIT, 0x3812, 0x00}, /* v_win offset high */
- {OV8858_8BIT, 0x3813, 0x02}, /* v_win offset low */
- {OV8858_8BIT, 0x3814, 0x01}, /* h_odd_inc */
- {OV8858_8BIT, 0x3815, 0x01}, /* h_even_inc */
- {OV8858_8BIT, 0x3820, 0x00}, /* format1 */
- {OV8858_8BIT, 0x3821, 0x40}, /* format2 */
- {OV8858_8BIT, 0x382A, 0x01}, /* v_odd_inc */
- {OV8858_8BIT, 0x382B, 0x01}, /* v_even_inc */
-
- {OV8858_8BIT, 0x3830, 0x06}, /* Unknown */
- {OV8858_8BIT, 0x3836, 0x01}, /* Unknown */
- {OV8858_8BIT, 0x3837, 0x18}, /* Unknown */
- {OV8858_8BIT, 0x3841, 0xFF}, /* AUTO_SIZE_CTRL */
- {OV8858_8BIT, 0x3846, 0x48}, /* Unknown */
-
- {OV8858_8BIT, 0x3D85, 0x14}, /* OTP_REG85 */
- {OV8858_8BIT, 0x3D8C, 0x73}, /* OTP_SETTING_STT_ADDRESS */
- {OV8858_8BIT, 0x3D8D, 0xDE}, /* OTP_SETTING_STT_ADDRESS */
- {OV8858_8BIT, 0x3F08, 0x10}, /* PSRAM control register */
- {OV8858_8BIT, 0x3F0A, 0x80}, /* PSRAM control register */
-
- {OV8858_8BIT, 0x4000, 0xF1}, /* BLC CTRL00 = default */
- {OV8858_8BIT, 0x4001, 0x00}, /* BLC CTRL01 */
- {OV8858_8BIT, 0x4002, 0x27}, /* BLC offset = 0x27 */
- {OV8858_8BIT, 0x4005, 0x10}, /* BLC target = 0x0010 */
- {OV8858_8BIT, 0x4009, 0x81}, /* BLC CTRL09 */
- {OV8858_8BIT, 0x400B, 0x0C}, /* BLC CTRL0B = default */
- {OV8858_8BIT, 0x401B, 0x00}, /* Zero line R coeff. = 0x0000 */
- {OV8858_8BIT, 0x401D, 0x00}, /* Zero line T coeff. = 0x0000 */
- {OV8858_8BIT, 0x401F, 0x00}, /* BLC CTRL1F */
- {OV8858_8BIT, 0x4020, 0x00}, /* Anchor left start = 0x0004 */
- {OV8858_8BIT, 0x4021, 0x04}, /* Anchor left start = 0x0004 */
- {OV8858_8BIT, 0x4022, 0x0B}, /* Anchor left end = 0x0BC3 */
- {OV8858_8BIT, 0x4023, 0xC3}, /* Anchor left end = 0x0BC3 */
- {OV8858_8BIT, 0x4024, 0x0C}, /* Anchor right start = 0x0C36 */
- {OV8858_8BIT, 0x4025, 0x36}, /* Anchor right start = 0x0C36 */
- {OV8858_8BIT, 0x4026, 0x0C}, /* Anchor right end = 0x0C37 */
- {OV8858_8BIT, 0x4027, 0x37}, /* Anchor right end = 0x0C37 */
- {OV8858_8BIT, 0x4028, 0x00}, /* Top zero line start = 0 */
- {OV8858_8BIT, 0x4029, 0x02}, /* Top zero line number = 2 */
- {OV8858_8BIT, 0x402A, 0x04}, /* Top black line start = 4 */
- {OV8858_8BIT, 0x402B, 0x08}, /* Top black line number = 8 */
- {OV8858_8BIT, 0x402C, 0x02}, /* Bottom zero start line = 2 */
- {OV8858_8BIT, 0x402D, 0x02}, /* Bottom zero line number = 2 */
- {OV8858_8BIT, 0x402E, 0x0C}, /* Bottom black line start = 12 */
- {OV8858_8BIT, 0x402F, 0x02}, /* Bottom black line number = 2 */
-
- {OV8858_8BIT, 0x4034, 0x3F}, /* Unknown */
- {OV8858_8BIT, 0x403D, 0x04}, /* BLC CTRL3D */
- {OV8858_8BIT, 0x4300, 0xFF}, /* clip_max[11:4] = 0xFFF */
- {OV8858_8BIT, 0x4301, 0x00}, /* clip_min[11:4] = 0 */
- {OV8858_8BIT, 0x4302, 0x0F}, /* clip_min/max[3:0] */
- {OV8858_8BIT, 0x4307, 0x01}, /* Unknown */
- {OV8858_8BIT, 0x4316, 0x00}, /* CTRL16 = default */
- {OV8858_8BIT, 0x4503, 0x18}, /* Unknown */
- {OV8858_8BIT, 0x4500, 0x38}, /* Unknown */
- {OV8858_8BIT, 0x4600, 0x01}, /* Unknown */
- {OV8858_8BIT, 0x4601, 0x97}, /* Unknown */
- /* wkup_dly = Mark1 wakeup delay/2^10 = 0x25 */
- {OV8858_8BIT, 0x4808, 0x25},
- {OV8858_8BIT, 0x4816, 0x52}, /* Embedded data type*/
- {OV8858_8BIT, 0x481F, 0x32}, /* clk_prepare_min = 0x32 */
- {OV8858_8BIT, 0x4825, 0x3A}, /* lpx_p_min = 0x3A */
- {OV8858_8BIT, 0x4826, 0x40}, /* hs_prepare_min = 0x40 */
- {OV8858_8BIT, 0x4837, 0x14}, /* pclk_period = 0x14 */
- {OV8858_8BIT, 0x4850, 0x10}, /* LANE SEL01 */
- {OV8858_8BIT, 0x4851, 0x32}, /* LANE SEL02 */
-
- {OV8858_8BIT, 0x4B00, 0x2A}, /* Unknown */
- {OV8858_8BIT, 0x4B0D, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x4D00, 0x04}, /* TPM_CTRL_REG */
- {OV8858_8BIT, 0x4D01, 0x18}, /* TPM_CTRL_REG */
- {OV8858_8BIT, 0x4D02, 0xC3}, /* TPM_CTRL_REG */
- {OV8858_8BIT, 0x4D03, 0xFF}, /* TPM_CTRL_REG */
- {OV8858_8BIT, 0x4D04, 0xFF}, /* TPM_CTRL_REG */
- {OV8858_8BIT, 0x4D05, 0xFF}, /* TPM_CTRL_REG */
-
- /*
- * Lens correction (LENC) function enable = 0
- * Slave sensor AWB Gain function enable = 1
- * Slave sensor AWB Statistics function enable = 1
- * Master sensor AWB Gain function enable = 1
- * Master sensor AWB Statistics function enable = 1
- * Black DPC function enable = 1
- * White DPC function enable =1
- */
- {OV8858_8BIT, 0x5000, 0x7E},
- {OV8858_8BIT, 0x5001, 0x01}, /* BLC function enable = 1 */
- /*
- * Horizontal scale function enable = 0
- * WBMATCH bypass mode = Select slave sensor's gain
- * WBMATCH function enable = 0
- * Master MWB gain support RGBC = 0
- * OTP_DPC function enable = 1
- * Manual mode of VarioPixel function enable = 0
- * Manual enable of VarioPixel function enable = 0
- * Use VSYNC to latch ISP modules's function enable signals = 0
- */
- {OV8858_8BIT, 0x5002, 0x08},
- /*
- * Bypass all ISP modules after BLC module = 0
- * DPC_DBC buffer control enable = 1
- * WBMATCH VSYNC selection = Select master sensor's VSYNC fall
- * Select master AWB gain to embed line = AWB gain before manual mode
- * Enable BLC's input flip_i signal = 0
- */
- {OV8858_8BIT, 0x5003, 0x20},
- {OV8858_8BIT, 0x5041, 0x1D}, /* ISP CTRL41 - embedded data=on */
- {OV8858_8BIT, 0x5046, 0x12}, /* ISP CTRL46 = default */
- /*
- * Tail enable = 1
- * Saturate cross cluster enable = 1
- * Remove cross cluster enable = 1
- * Enable to remove connected defect pixels in same channel = 1
- * Enable to remove connected defect pixels in different channel = 1
- * Smooth enable, use average G for recovery = 1
- * Black/white sensor mode enable = 0
- * Manual mode enable = 0
- */
- {OV8858_8BIT, 0x5780, 0xFC},
- {OV8858_8BIT, 0x5784, 0x0C}, /* DPC CTRL04 */
- {OV8858_8BIT, 0x5787, 0x40}, /* DPC CTRL07 */
- {OV8858_8BIT, 0x5788, 0x08}, /* DPC CTRL08 */
- {OV8858_8BIT, 0x578A, 0x02}, /* DPC CTRL0A */
- {OV8858_8BIT, 0x578B, 0x01}, /* DPC CTRL0B */
- {OV8858_8BIT, 0x578C, 0x01}, /* DPC CTRL0C */
- {OV8858_8BIT, 0x578E, 0x02}, /* DPC CTRL0E */
- {OV8858_8BIT, 0x578F, 0x01}, /* DPC CTRL0F */
- {OV8858_8BIT, 0x5790, 0x01}, /* DPC CTRL10 */
- {OV8858_8BIT, 0x5901, 0x00}, /* VAP CTRL01 = default */
- /* WINC CTRL08 = embedded data in 1st line*/
- {OV8858_8BIT, 0x5A08, 0x00},
- {OV8858_8BIT, 0x5B00, 0x02}, /* OTP CTRL00 */
- {OV8858_8BIT, 0x5B01, 0x10}, /* OTP CTRL01 */
- {OV8858_8BIT, 0x5B02, 0x03}, /* OTP CTRL02 */
- {OV8858_8BIT, 0x5B03, 0xCF}, /* OTP CTRL03 */
- {OV8858_8BIT, 0x5B05, 0x6C}, /* OTP CTRL05 = default */
- {OV8858_8BIT, 0x5E00, 0x00}, /* PRE CTRL00 = default */
- {OV8858_8BIT, 0x5E01, 0x41}, /* PRE_CTRL01 = default */
-
- {OV8858_TOK_TERM, 0, 0}
-};
-
-/*****************************STILL********************************/
-
-static const struct ov8858_reg ov8858_8M[] = {
- {OV8858_8BIT, 0x0100, 0x00}, /* software_standby */
- {OV8858_8BIT, 0x3778, 0x16}, /* Unknown */
- {OV8858_8BIT, 0x3800, 0x00}, /* h_crop_start high */
- {OV8858_8BIT, 0x3801, 0x0C}, /* h_crop_start low 12 */
- {OV8858_8BIT, 0x3802, 0x00}, /* v_crop_start high */
- {OV8858_8BIT, 0x3803, 0x0C}, /* v_crop_start low */
- {OV8858_8BIT, 0x3804, 0x0C}, /* h_crop_end high */
- {OV8858_8BIT, 0x3805, 0xD3}, /* h_crop_end low 3283 */
- {OV8858_8BIT, 0x3806, 0x09}, /* v_crop_end high */
- {OV8858_8BIT, 0x3807, 0xA3}, /* v_crop_end low */
- {OV8858_8BIT, 0x3808, 0x0C}, /* h_output_size high 3280 x 2464 */
- {OV8858_8BIT, 0x3809, 0xD0}, /* h_output_size low */
- {OV8858_8BIT, 0x380A, 0x09}, /* v_output_size high */
- {OV8858_8BIT, 0x380B, 0xa0}, /* v_output_size low */
- {OV8858_8BIT, 0x380C, 0x07}, /* horizontal timing size high */
- {OV8858_8BIT, 0x380D, 0x94}, /* horizontal timing size low */
- {OV8858_8BIT, 0x380E, 0x0A}, /* vertical timing size high */
- {OV8858_8BIT, 0x380F, 0x0D}, /* vertical timing size low */
- {OV8858_8BIT, 0x3814, 0x01}, /* h_odd_inc */
- {OV8858_8BIT, 0x3815, 0x01}, /* h_even_inc */
- {OV8858_8BIT, 0x3820, 0x00}, /* format1 */
- {OV8858_8BIT, 0x3821, 0x40}, /* format2 */
- {OV8858_8BIT, 0x382A, 0x01}, /* v_odd_inc */
- {OV8858_8BIT, 0x382B, 0x01}, /* v_even_inc */
- {OV8858_8BIT, 0x3830, 0x06}, /* Unknown */
- {OV8858_8BIT, 0x3836, 0x01}, /* Unknown */
- {OV8858_8BIT, 0x3D85, 0x14}, /* OTP_REG85 */
- {OV8858_8BIT, 0x3F08, 0x10}, /* PSRAM control register */
- {OV8858_8BIT, 0x4000, 0xF1}, /* BLC CTRL00 = default */
- {OV8858_8BIT, 0x4001, 0x00}, /* BLC CTRL01 */
- {OV8858_8BIT, 0x4002, 0x27}, /* BLC offset = 0x27 */
- {OV8858_8BIT, 0x4005, 0x10}, /* BLC target = 0x0010 */
- {OV8858_8BIT, 0x4009, 0x81}, /* BLC CTRL09 */
- {OV8858_8BIT, 0x400B, 0x0C}, /* BLC CTRL0B = default */
- {OV8858_8BIT, 0x401B, 0x00}, /* Zero line R coeff. = 0x0000 */
- {OV8858_8BIT, 0x401D, 0x00}, /* Zero line T coeff. = 0x0000 */
- {OV8858_8BIT, 0x401F, 0x00}, /* BLC CTRL1F */
- {OV8858_8BIT, 0x4020, 0x00}, /* Anchor left start = 0x0004 */
- {OV8858_8BIT, 0x4021, 0x04}, /* Anchor left start = 0x0004 */
- {OV8858_8BIT, 0x4022, 0x0B}, /* Anchor left end = 0x0BC3 */
- {OV8858_8BIT, 0x4023, 0xC3}, /* Anchor left end = 0x0BC3 */
- {OV8858_8BIT, 0x4024, 0x0C}, /* Anchor right start = 0x0C36 */
- {OV8858_8BIT, 0x4025, 0x36}, /* Anchor right start = 0x0C36 */
- {OV8858_8BIT, 0x4026, 0x0C}, /* Anchor right end = 0x0C37 */
- {OV8858_8BIT, 0x4027, 0x37}, /* Anchor right end = 0x0C37 */
- {OV8858_8BIT, 0x4028, 0x00}, /* Top zero line start = 0 */
- {OV8858_8BIT, 0x4029, 0x02}, /* Top zero line number = 2 */
- {OV8858_8BIT, 0x402A, 0x04}, /* Top black line start = 4 */
- {OV8858_8BIT, 0x402B, 0x08}, /* Top black line number = 8 */
- {OV8858_8BIT, 0x402C, 0x02}, /* Bottom zero start line = 2 */
- {OV8858_8BIT, 0x402D, 0x02}, /* Bottom zero line number = 2 */
- {OV8858_8BIT, 0x402E, 0x0C}, /* Bottom black line start = 12 */
- {OV8858_8BIT, 0x402F, 0x02}, /* Bottom black line number = 2 */
- {OV8858_8BIT, 0x4034, 0x3F}, /* Unknown */
- {OV8858_8BIT, 0x403D, 0x04}, /* BLC CTRL3D */
- {OV8858_8BIT, 0x4600, 0x01}, /* Unknown */
- {OV8858_8BIT, 0x4601, 0x97}, /* Unknown */
- {OV8858_8BIT, 0x4837, 0x14}, /* pclk_period = 0x14 */
- {OV8858_TOK_TERM, 0, 0}
-};
-
-static const struct ov8858_reg ov8858_3276x1848[] = {
- {OV8858_8BIT, 0x0100, 0x00}, /* software_standby */
- {OV8858_8BIT, 0x3778, 0x16}, /* Unknown */
- {OV8858_8BIT, 0x3800, 0x00}, /* h_crop_start high */
- {OV8858_8BIT, 0x3801, 0x10}, /* h_crop_start low 0c->10*/
- {OV8858_8BIT, 0x3802, 0x01}, /* v_crop_start high */
- {OV8858_8BIT, 0x3803, 0x42}, /* v_crop_start low 3e->42*/
- {OV8858_8BIT, 0x3804, 0x0C}, /* h_crop_end high */
- {OV8858_8BIT, 0x3805, 0xD3}, /* h_crop_end low */
- {OV8858_8BIT, 0x3806, 0x08}, /* v_crop_end high */
- {OV8858_8BIT, 0x3807, 0x71}, /* v_crop_end low */
- {OV8858_8BIT, 0x3808, 0x0C}, /* h_output_size high 3276 x 1848 */
- {OV8858_8BIT, 0x3809, 0xCC}, /* h_output_size low d0->cc*/
- {OV8858_8BIT, 0x380A, 0x07}, /* v_output_size high */
- {OV8858_8BIT, 0x380B, 0x38}, /* v_output_size low 3c->38*/
- {OV8858_8BIT, 0x380C, 0x07}, /* horizontal timing size high */
- {OV8858_8BIT, 0x380D, 0x94}, /* horizontal timing size low */
- {OV8858_8BIT, 0x380E, 0x0A}, /* vertical timing size high */
- {OV8858_8BIT, 0x380F, 0x0D}, /* vertical timing size low */
- {OV8858_8BIT, 0x3814, 0x01}, /* h_odd_inc */
- {OV8858_8BIT, 0x3815, 0x01}, /* h_even_inc */
- {OV8858_8BIT, 0x3820, 0x00}, /* format1 */
- {OV8858_8BIT, 0x3821, 0x40}, /* format2 */
- {OV8858_8BIT, 0x382A, 0x01}, /* v_odd_inc */
- {OV8858_8BIT, 0x382B, 0x01}, /* v_even_inc */
- {OV8858_8BIT, 0x3830, 0x06}, /* Unknown */
- {OV8858_8BIT, 0x3836, 0x01}, /* Unknown */
- {OV8858_8BIT, 0x3D85, 0x14}, /* OTP_REG85 */
- {OV8858_8BIT, 0x3F08, 0x10}, /* PSRAM control register */
- {OV8858_8BIT, 0x4000, 0xF1}, /* BLC CTRL00 = default */
- {OV8858_8BIT, 0x4001, 0x00}, /* BLC CTRL01 */
- {OV8858_8BIT, 0x4002, 0x27}, /* BLC offset = 0x27 */
- {OV8858_8BIT, 0x4005, 0x10}, /* BLC target = 0x0010 */
- {OV8858_8BIT, 0x4009, 0x81}, /* BLC CTRL09 */
- {OV8858_8BIT, 0x400B, 0x0C}, /* BLC CTRL0B = default */
- {OV8858_8BIT, 0x401B, 0x00}, /* Zero line R coeff. = 0x0000 */
- {OV8858_8BIT, 0x401D, 0x00}, /* Zero line T coeff. = 0x0000 */
- {OV8858_8BIT, 0x401F, 0x00}, /* BLC CTRL1F */
- {OV8858_8BIT, 0x4020, 0x00}, /* Anchor left start = 0x0004 */
- {OV8858_8BIT, 0x4021, 0x04}, /* Anchor left start = 0x0004 */
- {OV8858_8BIT, 0x4022, 0x0B}, /* Anchor left end = 0x0BC3 */
- {OV8858_8BIT, 0x4023, 0xC3}, /* Anchor left end = 0x0BC3 */
- {OV8858_8BIT, 0x4024, 0x0C}, /* Anchor right start = 0x0C36 */
- {OV8858_8BIT, 0x4025, 0x36}, /* Anchor right start = 0x0C36 */
- {OV8858_8BIT, 0x4026, 0x0C}, /* Anchor right end = 0x0C37 */
- {OV8858_8BIT, 0x4027, 0x37}, /* Anchor right end = 0x0C37 */
- {OV8858_8BIT, 0x4028, 0x00}, /* Top zero line start = 0 */
- {OV8858_8BIT, 0x4029, 0x02}, /* Top zero line number = 2 */
- {OV8858_8BIT, 0x402A, 0x04}, /* Top black line start = 4 */
- {OV8858_8BIT, 0x402B, 0x08}, /* Top black line number = 8 */
- {OV8858_8BIT, 0x402C, 0x02}, /* Bottom zero start line = 2 */
- {OV8858_8BIT, 0x402D, 0x02}, /* Bottom zero line number = 2 */
- {OV8858_8BIT, 0x402E, 0x0C}, /* Bottom black line start = 12 */
- {OV8858_8BIT, 0x402F, 0x02}, /* Bottom black line number = 2 */
- {OV8858_8BIT, 0x4034, 0x3F}, /* Unknown */
- {OV8858_8BIT, 0x403D, 0x04}, /* BLC CTRL3D */
- {OV8858_8BIT, 0x4600, 0x01}, /* Unknown */
- {OV8858_8BIT, 0x4601, 0x97}, /* Unknown */
- {OV8858_8BIT, 0x4837, 0x14}, /* pclk_period = 0x14 */
- {OV8858_TOK_TERM, 0, 0}
-};
-
-static const struct ov8858_reg ov8858_6M[] = {
- {OV8858_8BIT, 0x0100, 0x00}, /* software_standby */
- {OV8858_8BIT, 0x3778, 0x16}, /* Unknown */
- {OV8858_8BIT, 0x3800, 0x00}, /* h_crop_start high */
- {OV8858_8BIT, 0x3801, 0x0C}, /* h_crop_start low */
- {OV8858_8BIT, 0x3802, 0x01}, /* v_crop_start high */
- {OV8858_8BIT, 0x3803, 0x3E}, /* v_crop_start low */
- {OV8858_8BIT, 0x3804, 0x0C}, /* h_crop_end high */
- {OV8858_8BIT, 0x3805, 0xD3}, /* h_crop_end low */
- {OV8858_8BIT, 0x3806, 0x08}, /* v_crop_end high */
- {OV8858_8BIT, 0x3807, 0x71}, /* v_crop_end low */
- {OV8858_8BIT, 0x3808, 0x0C}, /* h_output_size high 3280 x 1852 */
- {OV8858_8BIT, 0x3809, 0xD0}, /* h_output_size low */
- {OV8858_8BIT, 0x380A, 0x07}, /* v_output_size high */
- {OV8858_8BIT, 0x380B, 0x3C}, /* v_output_size low */
- {OV8858_8BIT, 0x380C, 0x07}, /* horizontal timing size high */
- {OV8858_8BIT, 0x380D, 0x94}, /* horizontal timing size low */
- {OV8858_8BIT, 0x380E, 0x0A}, /* vertical timing size high */
- {OV8858_8BIT, 0x380F, 0x0D}, /* vertical timing size low */
- {OV8858_8BIT, 0x3814, 0x01}, /* h_odd_inc */
- {OV8858_8BIT, 0x3815, 0x01}, /* h_even_inc */
- {OV8858_8BIT, 0x3820, 0x00}, /* format1 */
- {OV8858_8BIT, 0x3821, 0x40}, /* format2 */
- {OV8858_8BIT, 0x382A, 0x01}, /* v_odd_inc */
- {OV8858_8BIT, 0x382B, 0x01}, /* v_even_inc */
- {OV8858_8BIT, 0x3830, 0x06}, /* Unknown */
- {OV8858_8BIT, 0x3836, 0x01}, /* Unknown */
- {OV8858_8BIT, 0x3D85, 0x14}, /* OTP_REG85 */
- {OV8858_8BIT, 0x3F08, 0x10}, /* PSRAM control register */
- {OV8858_8BIT, 0x4000, 0xF1}, /* BLC CTRL00 = default */
- {OV8858_8BIT, 0x4001, 0x00}, /* BLC CTRL01 */
- {OV8858_8BIT, 0x4002, 0x27}, /* BLC offset = 0x27 */
- {OV8858_8BIT, 0x4005, 0x10}, /* BLC target = 0x0010 */
- {OV8858_8BIT, 0x4009, 0x81}, /* BLC CTRL09 */
- {OV8858_8BIT, 0x400B, 0x0C}, /* BLC CTRL0B = default */
- {OV8858_8BIT, 0x401B, 0x00}, /* Zero line R coeff. = 0x0000 */
- {OV8858_8BIT, 0x401D, 0x00}, /* Zero line T coeff. = 0x0000 */
- {OV8858_8BIT, 0x401F, 0x00}, /* BLC CTRL1F */
- {OV8858_8BIT, 0x4020, 0x00}, /* Anchor left start = 0x0004 */
- {OV8858_8BIT, 0x4021, 0x04}, /* Anchor left start = 0x0004 */
- {OV8858_8BIT, 0x4022, 0x0B}, /* Anchor left end = 0x0BC3 */
- {OV8858_8BIT, 0x4023, 0xC3}, /* Anchor left end = 0x0BC3 */
- {OV8858_8BIT, 0x4024, 0x0C}, /* Anchor right start = 0x0C36 */
- {OV8858_8BIT, 0x4025, 0x36}, /* Anchor right start = 0x0C36 */
- {OV8858_8BIT, 0x4026, 0x0C}, /* Anchor right end = 0x0C37 */
- {OV8858_8BIT, 0x4027, 0x37}, /* Anchor right end = 0x0C37 */
- {OV8858_8BIT, 0x4028, 0x00}, /* Top zero line start = 0 */
- {OV8858_8BIT, 0x4029, 0x02}, /* Top zero line number = 2 */
- {OV8858_8BIT, 0x402A, 0x04}, /* Top black line start = 4 */
- {OV8858_8BIT, 0x402B, 0x08}, /* Top black line number = 8 */
- {OV8858_8BIT, 0x402C, 0x02}, /* Bottom zero start line = 2 */
- {OV8858_8BIT, 0x402D, 0x02}, /* Bottom zero line number = 2 */
- {OV8858_8BIT, 0x402E, 0x0C}, /* Bottom black line start = 12 */
- {OV8858_8BIT, 0x402F, 0x02}, /* Bottom black line number = 2 */
- {OV8858_8BIT, 0x4034, 0x3F}, /* Unknown */
- {OV8858_8BIT, 0x403D, 0x04}, /* BLC CTRL3D */
- {OV8858_8BIT, 0x4600, 0x01}, /* Unknown */
- {OV8858_8BIT, 0x4601, 0x97}, /* Unknown */
- {OV8858_8BIT, 0x4837, 0x14}, /* pclk_period = 0x14 */
- {OV8858_TOK_TERM, 0, 0}
-};
-
-static const struct ov8858_reg ov8858_1080P_60[] = {
- {OV8858_8BIT, 0x0100, 0x00}, /* software_standby */
- {OV8858_8BIT, 0x3778, 0x17}, /* Unknown */
- {OV8858_8BIT, 0x3800, 0x02}, /* h_crop_start high */
- {OV8858_8BIT, 0x3801, 0x26}, /* h_crop_start low */
- {OV8858_8BIT, 0x3802, 0x02}, /* v_crop_start high */
- {OV8858_8BIT, 0x3803, 0x8C}, /* v_crop_start low */
- {OV8858_8BIT, 0x3804, 0x0A}, /* h_crop_end high */
- {OV8858_8BIT, 0x3805, 0x9D}, /* h_crop_end low */
- {OV8858_8BIT, 0x3806, 0x07}, /* v_crop_end high */
- {OV8858_8BIT, 0x3807, 0x0A}, /* v_crop_end low */
- {OV8858_8BIT, 0x3808, 0x07}, /* h_output_size high*/
- {OV8858_8BIT, 0x3809, 0x90}, /* h_output_size low */
- {OV8858_8BIT, 0x380A, 0x04}, /* v_output_size high */
- {OV8858_8BIT, 0x380B, 0x48}, /* v_output_size low */
- {OV8858_8BIT, 0x380C, 0x07}, /* horizontal timing size high */
- {OV8858_8BIT, 0x380D, 0x94}, /* horizontal timing size low */
- {OV8858_8BIT, 0x380E, 0x04}, /* vertical timing size high */
- {OV8858_8BIT, 0x380F, 0xEC}, /* vertical timing size low */
- {OV8858_8BIT, 0x3814, 0x01}, /* h_odd_inc */
- {OV8858_8BIT, 0x3815, 0x01}, /* h_even_inc */
- {OV8858_8BIT, 0x3820, 0x00}, /* format1 */
- {OV8858_8BIT, 0x3821, 0x40}, /* format2 */
- {OV8858_8BIT, 0x382A, 0x01}, /* v_odd_inc */
- {OV8858_8BIT, 0x382B, 0x01}, /* v_even_inc */
- {OV8858_8BIT, 0x3830, 0x06}, /* Unknown */
- {OV8858_8BIT, 0x3836, 0x01}, /* Unknown */
- {OV8858_8BIT, 0x3D85, 0x14}, /* OTP_REG85 */
- {OV8858_8BIT, 0x3F08, 0x10}, /* PSRAM control register */
- {OV8858_8BIT, 0x4000, 0xF1}, /* BLC CTRL00 = default */
- {OV8858_8BIT, 0x4001, 0x00}, /* BLC CTRL01 */
- {OV8858_8BIT, 0x4002, 0x27}, /* BLC offset = 0x27 */
- {OV8858_8BIT, 0x4005, 0x10}, /* BLC target = 0x0010 */
- {OV8858_8BIT, 0x4009, 0x81}, /* BLC CTRL09 */
- {OV8858_8BIT, 0x400B, 0x0C}, /* BLC CTRL0B = default */
- {OV8858_8BIT, 0x401B, 0x00}, /* Zero line R coeff. = 0x0000 */
- {OV8858_8BIT, 0x401D, 0x00}, /* Zero line T coeff. = 0x0000 */
- {OV8858_8BIT, 0x401F, 0x00}, /* BLC CTRL1F */
- {OV8858_8BIT, 0x4020, 0x00}, /* Anchor left start = 0x0004 */
- {OV8858_8BIT, 0x4021, 0x04}, /* Anchor left start = 0x0004 */
- {OV8858_8BIT, 0x4022, 0x07}, /* Anchor left end = 0x072D */
- {OV8858_8BIT, 0x4023, 0x2D}, /* Anchor left end = 0x072D */
- {OV8858_8BIT, 0x4024, 0x07}, /* Anchor right start = 0x079E */
- {OV8858_8BIT, 0x4025, 0x9E}, /* Anchor right start = 0x079E */
- {OV8858_8BIT, 0x4026, 0x07}, /* Anchor right end = 0x079F */
- {OV8858_8BIT, 0x4027, 0x9F}, /* Anchor right end = 0x079F */
- {OV8858_8BIT, 0x4028, 0x00}, /* Top zero line start = 0 */
- {OV8858_8BIT, 0x4029, 0x02}, /* Top zero line number = 2 */
- {OV8858_8BIT, 0x402A, 0x04}, /* Top black line start = 4 */
- {OV8858_8BIT, 0x402B, 0x08}, /* Top black line number = 8 */
- {OV8858_8BIT, 0x402C, 0x02}, /* Bottom zero start line = 2 */
- {OV8858_8BIT, 0x402D, 0x02}, /* Bottom zero line number = 2 */
- {OV8858_8BIT, 0x402E, 0x0C}, /* Bottom black line start = 12 */
- {OV8858_8BIT, 0x402F, 0x02}, /* Bottom black line number = 2 */
- {OV8858_8BIT, 0x4034, 0x3F}, /* Unknown */
- {OV8858_8BIT, 0x403D, 0x04}, /* BLC CTRL3D */
- {OV8858_8BIT, 0x4600, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x4601, 0xef}, /* Unknown */
- {OV8858_8BIT, 0x4837, 0x16}, /* pclk_period = 0x16 */
- {OV8858_TOK_TERM, 0, 0}
-};
-
-static const struct ov8858_reg ov8858_1080P_30[] = {
- {OV8858_8BIT, 0x0100, 0x00}, /* software_standby */
- {OV8858_8BIT, 0x3778, 0x17}, /* Unknown */
- {OV8858_8BIT, 0x3800, 0x02}, /* h_crop_start high */
- {OV8858_8BIT, 0x3801, 0x26}, /* h_crop_start low */
- {OV8858_8BIT, 0x3802, 0x02}, /* v_crop_start high */
- {OV8858_8BIT, 0x3803, 0x8C}, /* v_crop_start low */
- {OV8858_8BIT, 0x3804, 0x0A}, /* h_crop_end high */
- {OV8858_8BIT, 0x3805, 0x9D}, /* h_crop_end low */
- {OV8858_8BIT, 0x3806, 0x07}, /* v_crop_end high */
- {OV8858_8BIT, 0x3807, 0x0A}, /* v_crop_end low */
- {OV8858_8BIT, 0x3808, 0x07}, /* h_output_size high*/
- {OV8858_8BIT, 0x3809, 0x90}, /* h_output_size low */
- {OV8858_8BIT, 0x380A, 0x04}, /* v_output_size high */
- {OV8858_8BIT, 0x380B, 0x48}, /* v_output_size low */
- {OV8858_8BIT, 0x380C, 0x07}, /* horizontal timing size high */
- {OV8858_8BIT, 0x380D, 0x94}, /* horizontal timing size low */
- {OV8858_8BIT, 0x380E, 0x0A}, /* vertical timing size high */
- {OV8858_8BIT, 0x380F, 0x0D}, /* vertical timing size low */
- {OV8858_8BIT, 0x3814, 0x01}, /* h_odd_inc */
- {OV8858_8BIT, 0x3815, 0x01}, /* h_even_inc */
- {OV8858_8BIT, 0x3820, 0x00}, /* format1 */
- {OV8858_8BIT, 0x3821, 0x40}, /* format2 */
- {OV8858_8BIT, 0x382A, 0x01}, /* v_odd_inc */
- {OV8858_8BIT, 0x382B, 0x01}, /* v_even_inc */
- {OV8858_8BIT, 0x3830, 0x06}, /* Unknown */
- {OV8858_8BIT, 0x3836, 0x01}, /* Unknown */
- {OV8858_8BIT, 0x3D85, 0x14}, /* OTP_REG85 */
- {OV8858_8BIT, 0x3F08, 0x10}, /* PSRAM control register */
- {OV8858_8BIT, 0x4000, 0xF1}, /* BLC CTRL00 = default */
- {OV8858_8BIT, 0x4001, 0x00}, /* BLC CTRL01 */
- {OV8858_8BIT, 0x4002, 0x27}, /* BLC offset = 0x27 */
- {OV8858_8BIT, 0x4005, 0x10}, /* BLC target = 0x0010 */
- {OV8858_8BIT, 0x4009, 0x81}, /* BLC CTRL09 */
- {OV8858_8BIT, 0x400B, 0x0C}, /* BLC CTRL0B = default */
- {OV8858_8BIT, 0x401B, 0x00}, /* Zero line R coeff. = 0x0000 */
- {OV8858_8BIT, 0x401D, 0x00}, /* Zero line T coeff. = 0x0000 */
- {OV8858_8BIT, 0x401F, 0x00}, /* BLC CTRL1F */
- {OV8858_8BIT, 0x4020, 0x00}, /* Anchor left start = 0x0004 */
- {OV8858_8BIT, 0x4021, 0x04}, /* Anchor left start = 0x0004 */
- {OV8858_8BIT, 0x4022, 0x07}, /* Anchor left end = 0x072D */
- {OV8858_8BIT, 0x4023, 0x2D}, /* Anchor left end = 0x072D */
- {OV8858_8BIT, 0x4024, 0x07}, /* Anchor right start = 0x079E */
- {OV8858_8BIT, 0x4025, 0x9E}, /* Anchor right start = 0x079E */
- {OV8858_8BIT, 0x4026, 0x07}, /* Anchor right end = 0x079F */
- {OV8858_8BIT, 0x4027, 0x9F}, /* Anchor right end = 0x079F */
- {OV8858_8BIT, 0x4028, 0x00}, /* Top zero line start = 0 */
- {OV8858_8BIT, 0x4029, 0x02}, /* Top zero line number = 2 */
- {OV8858_8BIT, 0x402A, 0x04}, /* Top black line start = 4 */
- {OV8858_8BIT, 0x402B, 0x08}, /* Top black line number = 8 */
- {OV8858_8BIT, 0x402C, 0x02}, /* Bottom zero start line = 2 */
- {OV8858_8BIT, 0x402D, 0x02}, /* Bottom zero line number = 2 */
- {OV8858_8BIT, 0x402E, 0x0C}, /* Bottom black line start = 12 */
- {OV8858_8BIT, 0x402F, 0x02}, /* Bottom black line number = 2 */
- {OV8858_8BIT, 0x4034, 0x3F}, /* Unknown */
- {OV8858_8BIT, 0x403D, 0x04}, /* BLC CTRL3D */
- {OV8858_8BIT, 0x4600, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x4601, 0xef}, /* Unknown */
- {OV8858_8BIT, 0x4837, 0x16}, /* pclk_period = 0x16 */
- {OV8858_TOK_TERM, 0, 0}
-};
-
-static const struct ov8858_reg ov8858_1640x1232[] = {
- {OV8858_8BIT, 0x0100, 0x00}, /* software_standby */
- {OV8858_8BIT, 0x3778, 0x16}, /* Unknown */
- {OV8858_8BIT, 0x3800, 0x00}, /* h_crop_start high */
- {OV8858_8BIT, 0x3801, 0x0C}, /* h_crop_start low 12 */
- {OV8858_8BIT, 0x3802, 0x00}, /* v_crop_start high */
- {OV8858_8BIT, 0x3803, 0x0C}, /* v_crop_start low */
- {OV8858_8BIT, 0x3804, 0x0C}, /* h_crop_end high 3283 */
- {OV8858_8BIT, 0x3805, 0xD3}, /* h_crop_end low */
- {OV8858_8BIT, 0x3806, 0x09}, /* v_crop_end high */
- {OV8858_8BIT, 0x3807, 0xA3}, /* v_crop_end low */
- {OV8858_8BIT, 0x3808, 0x06}, /* h_output_size high 1640 x 1232 */
- {OV8858_8BIT, 0x3809, 0x68}, /* h_output_size low */
- {OV8858_8BIT, 0x380A, 0x04}, /* v_output_size high */
- {OV8858_8BIT, 0x380B, 0xD0}, /* v_output_size low */
- {OV8858_8BIT, 0x380C, 0x07}, /* horizontal timing size high */
- {OV8858_8BIT, 0x380D, 0x94}, /* horizontal timing size low */
- {OV8858_8BIT, 0x380E, 0x09}, /* vertical timing size high */
- {OV8858_8BIT, 0x380F, 0xAA}, /* vertical timing size low */
- {OV8858_8BIT, 0x3814, 0x03}, /* h_odd_inc */
- {OV8858_8BIT, 0x3815, 0x01}, /* h_even_inc */
- {OV8858_8BIT, 0x3820, 0x00}, /* format1 */
- {OV8858_8BIT, 0x3821, 0x67}, /* format2 */
- {OV8858_8BIT, 0x382A, 0x03}, /* v_odd_inc */
- {OV8858_8BIT, 0x382B, 0x01}, /* v_even_inc */
- {OV8858_8BIT, 0x3830, 0x08}, /* Unknown */
- {OV8858_8BIT, 0x3836, 0x02}, /* Unknown */
- {OV8858_8BIT, 0x3D85, 0x16}, /* OTP_REG85 */
- {OV8858_8BIT, 0x3F08, 0x08}, /* PSRAM control register */
- {OV8858_8BIT, 0x4000, 0xF1}, /* BLC CTRL00 = default */
- {OV8858_8BIT, 0x4001, 0x10}, /* BLC CTRL01 */
- {OV8858_8BIT, 0x4002, 0x27}, /* BLC offset = 0x27 */
- {OV8858_8BIT, 0x4005, 0x10}, /* BLC target = 0x0010 */
- {OV8858_8BIT, 0x4009, 0x81}, /* BLC CTRL09 */
- {OV8858_8BIT, 0x400B, 0x0C}, /* BLC CTRL0B = default */
- {OV8858_8BIT, 0x401B, 0x00}, /* Zero line R coeff. = 0x0000 */
- {OV8858_8BIT, 0x401D, 0x00}, /* Zero line T coeff. = 0x0000 */
- {OV8858_8BIT, 0x401F, 0x00}, /* BLC CTRL1F */
- {OV8858_8BIT, 0x4020, 0x00}, /* Anchor left start = 0x0004 */
- {OV8858_8BIT, 0x4021, 0x04}, /* Anchor left start = 0x0004 */
- {OV8858_8BIT, 0x4022, 0x04}, /* Anchor left end = 0x04B9 */
- {OV8858_8BIT, 0x4023, 0xB9}, /* Anchor left end = 0x04B9 */
- {OV8858_8BIT, 0x4024, 0x05}, /* Anchor right start = 0x052A */
- {OV8858_8BIT, 0x4025, 0x2A}, /* Anchor right start = 0x052A */
- {OV8858_8BIT, 0x4026, 0x05}, /* Anchor right end = 0x052B */
- {OV8858_8BIT, 0x4027, 0x2B}, /* Anchor right end = 0x052B */
- {OV8858_8BIT, 0x4028, 0x00}, /* Top zero line start = 0 */
- {OV8858_8BIT, 0x4029, 0x02}, /* Top zero line number = 2 */
- {OV8858_8BIT, 0x402A, 0x04}, /* Top black line start = 4 */
- {OV8858_8BIT, 0x402B, 0x04}, /* Top black line number = 8 */
- {OV8858_8BIT, 0x402C, 0x02}, /* Bottom zero start line = 2 */
- {OV8858_8BIT, 0x402D, 0x02}, /* Bottom zero line number = 2 */
- {OV8858_8BIT, 0x402E, 0x08}, /* Bottom black line start = 8 */
- {OV8858_8BIT, 0x402F, 0x02}, /* Bottom black line number = 2 */
- {OV8858_8BIT, 0x4034, 0x3F}, /* Unknown */
- {OV8858_8BIT, 0x403D, 0x04}, /* BLC CTRL3D */
- {OV8858_8BIT, 0x4600, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x4601, 0xCB}, /* Unknown */
- {OV8858_8BIT, 0x4837, 0x14}, /* pclk_period = 0x14 */
- {OV8858_TOK_TERM, 0, 0}
-};
-
-static const struct ov8858_reg ov8858_1640x1096[] = {
- {OV8858_8BIT, 0x0100, 0x00}, /* software_standby */
- {OV8858_8BIT, 0x3778, 0x16}, /* Unknown */
- {OV8858_8BIT, 0x3800, 0x00}, /* h_crop_start high */
- {OV8858_8BIT, 0x3801, 0x0C}, /* h_crop_start low 12 */
- {OV8858_8BIT, 0x3802, 0x00}, /* v_crop_start high */
- {OV8858_8BIT, 0x3803, 0x0C}, /* v_crop_start low */
- {OV8858_8BIT, 0x3804, 0x0C}, /* h_crop_end high 3283 */
- {OV8858_8BIT, 0x3805, 0xD3}, /* h_crop_end low */
- {OV8858_8BIT, 0x3806, 0x09}, /* v_crop_end high */
- {OV8858_8BIT, 0x3807, 0xA3}, /* v_crop_end low */
- {OV8858_8BIT, 0x3808, 0x06}, /* h_output_size high 1640 x 1096 */
- {OV8858_8BIT, 0x3809, 0x68}, /* h_output_size low */
- {OV8858_8BIT, 0x380A, 0x04}, /* v_output_size high */
- {OV8858_8BIT, 0x380B, 0x48}, /* v_output_size low */
- {OV8858_8BIT, 0x380C, 0x07}, /* horizontal timing size high */
- {OV8858_8BIT, 0x380D, 0x94}, /* horizontal timing size low */
- {OV8858_8BIT, 0x380E, 0x09}, /* vertical timing size high */
- {OV8858_8BIT, 0x380F, 0xAA}, /* vertical timing size low */
- {OV8858_8BIT, 0x3814, 0x03}, /* h_odd_inc */
- {OV8858_8BIT, 0x3815, 0x01}, /* h_even_inc */
- {OV8858_8BIT, 0x3820, 0x00}, /* format1 */
- {OV8858_8BIT, 0x3821, 0x67}, /* format2 */
- {OV8858_8BIT, 0x382A, 0x03}, /* v_odd_inc */
- {OV8858_8BIT, 0x382B, 0x01}, /* v_even_inc */
- {OV8858_8BIT, 0x3830, 0x08}, /* Unknown */
- {OV8858_8BIT, 0x3836, 0x02}, /* Unknown */
- {OV8858_8BIT, 0x3D85, 0x16}, /* OTP_REG85 */
- {OV8858_8BIT, 0x3F08, 0x08}, /* PSRAM control register */
- {OV8858_8BIT, 0x4000, 0xF1}, /* BLC CTRL00 = default */
- {OV8858_8BIT, 0x4001, 0x10}, /* BLC CTRL01 */
- {OV8858_8BIT, 0x4002, 0x27}, /* BLC offset = 0x27 */
- {OV8858_8BIT, 0x4005, 0x10}, /* BLC target = 0x0010 */
- {OV8858_8BIT, 0x4009, 0x81}, /* BLC CTRL09 */
- {OV8858_8BIT, 0x400B, 0x0C}, /* BLC CTRL0B = default */
- {OV8858_8BIT, 0x401B, 0x00}, /* Zero line R coeff. = 0x0000 */
- {OV8858_8BIT, 0x401D, 0x00}, /* Zero line T coeff. = 0x0000 */
- {OV8858_8BIT, 0x401F, 0x00}, /* BLC CTRL1F */
- {OV8858_8BIT, 0x4020, 0x00}, /* Anchor left start = 0x0004 */
- {OV8858_8BIT, 0x4021, 0x04}, /* Anchor left start = 0x0004 */
- {OV8858_8BIT, 0x4022, 0x04}, /* Anchor left end = 0x04B9 */
- {OV8858_8BIT, 0x4023, 0xB9}, /* Anchor left end = 0x04B9 */
- {OV8858_8BIT, 0x4024, 0x05}, /* Anchor right start = 0x052A */
- {OV8858_8BIT, 0x4025, 0x2A}, /* Anchor right start = 0x052A */
- {OV8858_8BIT, 0x4026, 0x05}, /* Anchor right end = 0x052B */
- {OV8858_8BIT, 0x4027, 0x2B}, /* Anchor right end = 0x052B */
- {OV8858_8BIT, 0x4028, 0x00}, /* Top zero line start = 0 */
- {OV8858_8BIT, 0x4029, 0x02}, /* Top zero line number = 2 */
- {OV8858_8BIT, 0x402A, 0x04}, /* Top black line start = 4 */
- {OV8858_8BIT, 0x402B, 0x04}, /* Top black line number = 8 */
- {OV8858_8BIT, 0x402C, 0x02}, /* Bottom zero start line = 2 */
- {OV8858_8BIT, 0x402D, 0x02}, /* Bottom zero line number = 2 */
- {OV8858_8BIT, 0x402E, 0x08}, /* Bottom black line start = 8 */
- {OV8858_8BIT, 0x402F, 0x02}, /* Bottom black line number = 2 */
- {OV8858_8BIT, 0x4034, 0x3F}, /* Unknown */
- {OV8858_8BIT, 0x403D, 0x04}, /* BLC CTRL3D */
- {OV8858_8BIT, 0x4600, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x4601, 0xCB}, /* Unknown */
- {OV8858_8BIT, 0x4837, 0x14}, /* pclk_period = 0x14 */
- {OV8858_TOK_TERM, 0, 0}
-};
-
-
-static const struct ov8858_reg ov8858_1640x926[] = {
- {OV8858_8BIT, 0x0100, 0x00}, /* software_standby */
- {OV8858_8BIT, 0x3778, 0x16}, /* Unknown */
- {OV8858_8BIT, 0x3800, 0x00}, /* h_crop_start high */
- {OV8858_8BIT, 0x3801, 0x0C}, /* h_crop_start low */
- {OV8858_8BIT, 0x3802, 0x00}, /* v_crop_start high */
- {OV8858_8BIT, 0x3803, 0x0C}, /* v_crop_start low */
- {OV8858_8BIT, 0x3804, 0x0C}, /* h_crop_end high */
- {OV8858_8BIT, 0x3805, 0xD3}, /* h_crop_end low */
- {OV8858_8BIT, 0x3806, 0x09}, /* v_crop_end high */
- {OV8858_8BIT, 0x3807, 0xA3}, /* v_crop_end low */
- {OV8858_8BIT, 0x3808, 0x06}, /* h_output_size high 1640 x 926 */
- {OV8858_8BIT, 0x3809, 0x68}, /* h_output_size low */
- {OV8858_8BIT, 0x380A, 0x03}, /* v_output_size high */
- {OV8858_8BIT, 0x380B, 0x9E}, /* v_output_size low */
- {OV8858_8BIT, 0x380C, 0x07}, /* horizontal timing size high */
- {OV8858_8BIT, 0x380D, 0x94}, /* horizontal timing size low */
- {OV8858_8BIT, 0x380E, 0x09}, /* vertical timing size high */
- {OV8858_8BIT, 0x380F, 0xAA}, /* vertical timing size low */
- {OV8858_8BIT, 0x3814, 0x03}, /* h_odd_inc */
- {OV8858_8BIT, 0x3815, 0x01}, /* h_even_inc */
- {OV8858_8BIT, 0x3820, 0x00}, /* format1 */
- {OV8858_8BIT, 0x3821, 0x67}, /* format2 */
- {OV8858_8BIT, 0x382A, 0x03}, /* v_odd_inc */
- {OV8858_8BIT, 0x382B, 0x01}, /* v_even_inc */
- {OV8858_8BIT, 0x3830, 0x08}, /* Unknown */
- {OV8858_8BIT, 0x3836, 0x02}, /* Unknown */
- {OV8858_8BIT, 0x3D85, 0x16}, /* OTP_REG85 */
- {OV8858_8BIT, 0x3F08, 0x08}, /* PSRAM control register */
- {OV8858_8BIT, 0x4000, 0xF1}, /* BLC CTRL00 = default */
- {OV8858_8BIT, 0x4001, 0x10}, /* BLC CTRL01 */
- {OV8858_8BIT, 0x4002, 0x27}, /* BLC offset = 0x27 */
- {OV8858_8BIT, 0x4005, 0x10}, /* BLC target = 0x0010 */
- {OV8858_8BIT, 0x4009, 0x81}, /* BLC CTRL09 */
- {OV8858_8BIT, 0x400B, 0x0C}, /* BLC CTRL0B = default */
- {OV8858_8BIT, 0x401B, 0x00}, /* Zero line R coeff. = 0x0000 */
- {OV8858_8BIT, 0x401D, 0x00}, /* Zero line T coeff. = 0x0000 */
- {OV8858_8BIT, 0x401F, 0x00}, /* BLC CTRL1F */
- {OV8858_8BIT, 0x4020, 0x00}, /* Anchor left start = 0x0004 */
- {OV8858_8BIT, 0x4021, 0x04}, /* Anchor left start = 0x0004 */
- {OV8858_8BIT, 0x4022, 0x04}, /* Anchor left end = 0x04B9 */
- {OV8858_8BIT, 0x4023, 0xB9}, /* Anchor left end = 0x04B9 */
- {OV8858_8BIT, 0x4024, 0x05}, /* Anchor right start = 0x052A */
- {OV8858_8BIT, 0x4025, 0x2A}, /* Anchor right start = 0x052A */
- {OV8858_8BIT, 0x4026, 0x05}, /* Anchor right end = 0x052B */
- {OV8858_8BIT, 0x4027, 0x2B}, /* Anchor right end = 0x052B */
- {OV8858_8BIT, 0x4028, 0x00}, /* Top zero line start = 0 */
- {OV8858_8BIT, 0x4029, 0x02}, /* Top zero line number = 2 */
- {OV8858_8BIT, 0x402A, 0x04}, /* Top black line start = 4 */
- {OV8858_8BIT, 0x402B, 0x04}, /* Top black line number = 8 */
- {OV8858_8BIT, 0x402C, 0x02}, /* Bottom zero start line = 2 */
- {OV8858_8BIT, 0x402D, 0x02}, /* Bottom zero line number = 2 */
- {OV8858_8BIT, 0x402E, 0x08}, /* Bottom black line start = 8 */
- {OV8858_8BIT, 0x402F, 0x02}, /* Bottom black line number = 2 */
- {OV8858_8BIT, 0x4034, 0x3F}, /* Unknown */
- {OV8858_8BIT, 0x403D, 0x04}, /* BLC CTRL3D */
- {OV8858_8BIT, 0x4600, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x4601, 0xCB}, /* Unknown */
- {OV8858_8BIT, 0x4837, 0x14}, /* pclk_period = 0x14 */
- {OV8858_TOK_TERM, 0, 0}
-};
-
-static struct ov8858_resolution ov8858_res_preview[] = {
- {
- .desc = "ov8858_1640x926_PREVIEW",
- .width = 1640,
- .height = 926,
- .used = 0,
- .regs = ov8858_1640x926,
- .bin_factor_x = 0,
- .bin_factor_y = 0,
- .skip_frames = 0,
- .fps_options = {
- {
- .fps = 30,
- .pixels_per_line = 3880,
- .lines_per_frame = 2573,
- },
- {
- }
- },
- },
- {
- .desc = "ov8858_1640x1232_PREVIEW",
- .width = 1640,
- .height = 1232,
- .used = 0,
- .regs = ov8858_1640x1232,
- .bin_factor_x = 0,
- .bin_factor_y = 0,
- .skip_frames = 0,
- .fps_options = {
- {
- .fps = 30,
- .pixels_per_line = 3880,
- .lines_per_frame = 2573,
- },
- {
- }
- },
- },
- {
- .desc = "ov8858_3276x1848_PREVIEW",
- .width = 3276,
- .height = 1848,
- .used = 0,
- .regs = ov8858_3276x1848,
- .bin_factor_x = 0,
- .bin_factor_y = 0,
- .skip_frames = 0,
- .fps_options = {
- {
- .fps = 30,
- .pixels_per_line = 3880,
- .lines_per_frame = 2573,
- },
- {
- }
- },
- },
- {
- .desc = "ov8858_8M_PREVIEW",
- .width = 3280,
- .height = 2464,
- .used = 0,
- .regs = ov8858_8M,
- .bin_factor_x = 0,
- .bin_factor_y = 0,
- .skip_frames = 0,
- .fps_options = {
- {
- .fps = 30,
- .pixels_per_line = 3880,
- .lines_per_frame = 2573,
- },
- {
- }
- },
- },
-};
-
-static struct ov8858_resolution ov8858_res_still[] = {
- {
- .desc = "ov8858_1640x1232_STILL",
- .width = 1640,
- .height = 1232,
- .used = 0,
- .regs = ov8858_1640x1232,
- .bin_factor_x = 0,
- .bin_factor_y = 0,
- .skip_frames = 0,
- .fps_options = {
- {
- .fps = 30,
- .pixels_per_line = 3880,
- .lines_per_frame = 2573,
- },
- {
- }
- },
- },
- {
- .desc = "ov8858_1640x926_STILL",
- .width = 1640,
- .height = 926,
- .used = 0,
- .regs = ov8858_1640x926,
- .bin_factor_x = 0,
- .bin_factor_y = 0,
- .skip_frames = 1,
- .fps_options = {
- {
- .fps = 30,
- .pixels_per_line = 3880,
- .lines_per_frame = 2573,
- },
- {
- }
- },
- },
- {
- .desc = "ov8858_3276X1848_STILL",
- .width = 3276,
- .height = 1848,
- .used = 0,
- .regs = ov8858_3276x1848,
- .bin_factor_x = 0,
- .bin_factor_y = 0,
- .skip_frames = 1,
- .fps_options = {
- {
- .fps = 30,
- .pixels_per_line = 3880,
- .lines_per_frame = 2573,
- },
- {
- }
- },
- },
- {
- .desc = "ov8858_8M_STILL",
- .width = 3280,
- .height = 2464,
- .used = 0,
- .regs = ov8858_8M,
- .bin_factor_x = 0,
- .bin_factor_y = 0,
- .skip_frames = 1,
- .fps_options = {
- {
- /* Pixel clock: 149.76MHZ */
- .fps = 10,
- .pixels_per_line = 3880,
- .lines_per_frame = 3859,
- },
- {
- }
- },
- },
-};
-
-static struct ov8858_resolution ov8858_res_video[] = {
- {
- .desc = "ov8858_1640x926_VIDEO",
- .width = 1640,
- .height = 926,
- .used = 0,
- .regs = ov8858_1640x926,
- .bin_factor_x = 0,
- .bin_factor_y = 0,
- .skip_frames = 1,
- .fps_options = {
- {
- .fps = 30,
- .pixels_per_line = 3880,
- .lines_per_frame = 2573,
- },
- {
- }
- },
- },
- {
- .desc = "ov8858_1640x1232_VIDEO",
- .width = 1640,
- .height = 1232,
- .used = 0,
- .regs = ov8858_1640x1232,
- .bin_factor_x = 0,
- .bin_factor_y = 0,
- .skip_frames = 1,
- .fps_options = {
- {
- .fps = 30,
- .pixels_per_line = 3880,
- .lines_per_frame = 2573,
- },
- {
- }
- },
- },
- {
- .desc = "ov8858_1640x1096_VIDEO",
- .width = 1640,
- .height = 1096,
- .used = 0,
- .regs = ov8858_1640x1096,
- .bin_factor_x = 0,
- .bin_factor_y = 0,
- .skip_frames = 1,
- .fps_options = {
- {
- .fps = 30,
- .pixels_per_line = 3880,
- .lines_per_frame = 2573,
- },
- {
- }
- },
- },
- {
- .desc = "ov8858_6M_VIDEO",
- .width = 3280,
- .height = 1852,
- .used = 0,
- .regs = ov8858_6M,
- .bin_factor_x = 0,
- .bin_factor_y = 0,
- .skip_frames = 1,
- .fps_options = {
- {
- .fps = 30,
- .pixels_per_line = 3880,
- .lines_per_frame = 2573,
- },
- {
- }
- },
- },
- {
- .desc = "ov8858_8M_VIDEO",
- .width = 3280,
- .height = 2464,
- .used = 0,
- .regs = ov8858_8M,
- .bin_factor_x = 0,
- .bin_factor_y = 0,
- .skip_frames = 1,
- .fps_options = {
- {
- .fps = 30,
- .pixels_per_line = 3880,
- .lines_per_frame = 2573,
- },
- {
- }
- },
- },
-};
-
-#endif /* __OV8858_H__ */
diff --git a/drivers/staging/media/atomisp/i2c/ov8858_btns.h b/drivers/staging/media/atomisp/i2c/ov8858_btns.h
deleted file mode 100644
index f81851306832..000000000000
--- a/drivers/staging/media/atomisp/i2c/ov8858_btns.h
+++ /dev/null
@@ -1,1276 +0,0 @@
-/*
- * Support for the Omnivision OV8858 camera sensor.
- *
- * Copyright (c) 2014 Intel Corporation. All Rights Reserved.
- *
- * 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.
- *
- * 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.
- *
- *
- */
-
-#ifndef __OV8858_H__
-#define __OV8858_H__
-#include "../include/linux/atomisp_platform.h"
-#include <media/v4l2-ctrls.h>
-
-#define I2C_MSG_LENGTH 0x2
-
-/*
- * This should be added into include/linux/videodev2.h
- * NOTE: This is most likely not used anywhere.
- */
-#define V4L2_IDENT_OV8858 V4L2_IDENT_UNKNOWN
-
-/*
- * Indexes for VCM driver lists
- */
-#define OV8858_ID_DEFAULT 0
-#define OV8858_SUNNY 1
-
-#define OV8858_OTP_START_ADDR 0x7010
-#define OV8858_OTP_END_ADDR 0x7186
-
-/*
- * ov8858 System control registers
- */
-
-#define OV8858_OTP_LOAD_CTRL 0x3D81
-#define OV8858_OTP_MODE_CTRL 0x3D84
-#define OV8858_OTP_START_ADDR_REG 0x3D88
-#define OV8858_OTP_END_ADDR_REG 0x3D8A
-#define OV8858_OTP_ISP_CTRL2 0x5002
-
-#define OV8858_OTP_MODE_MANUAL BIT(6)
-#define OV8858_OTP_MODE_PROGRAM_DISABLE BIT(7)
-#define OV8858_OTP_LOAD_ENABLE BIT(0)
-#define OV8858_OTP_DPC_ENABLE BIT(3)
-
-#define OV8858_PLL1_PREDIV0 0x030A
-#define OV8858_PLL1_PREDIV 0x0300
-#define OV8858_PLL1_MULTIPLIER 0x0301
-#define OV8858_PLL1_SYS_PRE_DIV 0x0305
-#define OV8858_PLL1_SYS_DIVIDER 0x0306
-
-#define OV8858_PLL1_PREDIV0_MASK BIT(0)
-#define OV8858_PLL1_PREDIV_MASK (BIT(0) | BIT(1) | BIT(2))
-#define OV8858_PLL1_MULTIPLIER_MASK 0x01FF
-#define OV8858_PLL1_SYS_PRE_DIV_MASK (BIT(0) | BIT(1))
-#define OV8858_PLL1_SYS_DIVIDER_MASK BIT(0)
-
-#define OV8858_PLL2_PREDIV0 0x0312
-#define OV8858_PLL2_PREDIV 0x030B
-#define OV8858_PLL2_MULTIPLIER 0x030C
-#define OV8858_PLL2_DAC_DIVIDER 0x0312
-#define OV8858_PLL2_SYS_PRE_DIV 0x030F
-#define OV8858_PLL2_SYS_DIVIDER 0x030E
-
-#define OV8858_PLL2_PREDIV0_MASK BIT(4)
-#define OV8858_PLL2_PREDIV_MASK (BIT(0) | BIT(1) | BIT(2))
-#define OV8858_PLL2_MULTIPLIER_MASK 0x01FF
-#define OV8858_PLL2_DAC_DIVIDER_MASK (BIT(0) | BIT(1) | BIT(2) | BIT(3))
-#define OV8858_PLL2_SYS_PRE_DIV_MASK (BIT(0) | BIT(1) | BIT(2) | BIT(3))
-#define OV8858_PLL2_SYS_DIVIDER_MASK (BIT(0) | BIT(1) | BIT(2))
-
-#define OV8858_PLL_SCLKSEL1 0x3032
-#define OV8858_PLL_SCLKSEL2 0x3033
-#define OV8858_SRB_HOST_INPUT_DIS 0x3106
-
-#define OV8858_PLL_SCLKSEL1_MASK BIT(7)
-#define OV8858_PLL_SCLKSEL2_MASK BIT(1)
-
-#define OV8858_SYS_PRE_DIV_OFFSET 2
-#define OV8858_SYS_PRE_DIV_MASK (BIT(2) | BIT(3))
-#define OV8858_SCLK_PDIV_OFFSET 4
-#define OV8858_SCLK_PDIV_MASK (BIT(4) | BIT(5) | BIT(6) | BIT(7))
-
-#define OV8858_TIMING_HTS 0x380C
-#define OV8858_TIMING_VTS 0x380E
-
-#define OV8858_HORIZONTAL_START_H 0x3800
-#define OV8858_VERTICAL_START_H 0x3802
-#define OV8858_HORIZONTAL_END_H 0x3804
-#define OV8858_VERTICAL_END_H 0x3806
-#define OV8858_HORIZONTAL_OUTPUT_SIZE_H 0x3808
-#define OV8858_VERTICAL_OUTPUT_SIZE_H 0x380A
-
-#define OV8858_GROUP_ACCESS 0x3208
-#define OV8858_GROUP_ZERO 0x00
-#define OV8858_GROUP_ACCESS_HOLD_START 0x00
-#define OV8858_GROUP_ACCESS_HOLD_END 0x10
-#define OV8858_GROUP_ACCESS_DELAY_LAUNCH 0xA0
-#define OV8858_GROUP_ACCESS_QUICK_LAUNCH 0xE0
-
-#define OV_SUBDEV_PREFIX "ov"
-#define OV_ID_DEFAULT 0x0000
-#define OV8858_CHIP_ID 0x8858
-
-#define OV8858_LONG_EXPO 0x3500
-#define OV8858_LONG_GAIN 0x3508
-#define OV8858_LONG_DIGI_GAIN 0x350A
-#define OV8858_SHORT_GAIN 0x350C
-#define OV8858_SHORT_DIGI_GAIN 0x350E
-
-#define OV8858_FORMAT1 0x3820
-#define OV8858_FORMAT2 0x3821
-
-#define OV8858_FLIP_ENABLE 0x06
-
-#define OV8858_MWB_RED_GAIN_H 0x5032
-#define OV8858_MWB_GREEN_GAIN_H 0x5034
-#define OV8858_MWB_BLUE_GAIN_H 0x5036
-#define OV8858_MWB_GAIN_MAX 0x0FFF
-
-#define OV8858_CHIP_ID_HIGH 0x300B
-#define OV8858_CHIP_ID_LOW 0x300C
-#define OV8858_STREAM_MODE 0x0100
-
-#define OV8858_FOCAL_LENGTH_NUM 294 /* 2.94mm */
-#define OV8858_FOCAL_LENGTH_DEM 100
-#define OV8858_F_NUMBER_DEFAULT_NUM 24 /* 2.4 */
-#define OV8858_F_NUMBER_DEM 10
-
-#define OV8858_H_INC_ODD 0x3814
-#define OV8858_H_INC_EVEN 0x3815
-#define OV8858_V_INC_ODD 0x382A
-#define OV8858_V_INC_EVEN 0x382B
-
-#define OV8858_READ_MODE_BINNING_ON 0x0400 /* ToDo: Check this */
-#define OV8858_READ_MODE_BINNING_OFF 0x00 /* ToDo: Check this */
-#define OV8858_BIN_FACTOR_MAX 2
-#define OV8858_INTEGRATION_TIME_MARGIN 14
-
-#define OV8858_MAX_VTS_VALUE 0xFFFF
-#define OV8858_MAX_EXPOSURE_VALUE \
- (OV8858_MAX_VTS_VALUE - OV8858_INTEGRATION_TIME_MARGIN)
-#define OV8858_MAX_GAIN_VALUE 0x07FF
-
-#define OV8858_MAX_FOCUS_POS 1023
-
-#define OV8858_TEST_PATTERN_REG 0x5E00
-
-struct ov8858_vcm {
- int (*power_up)(struct v4l2_subdev *sd);
- int (*power_down)(struct v4l2_subdev *sd);
- int (*init)(struct v4l2_subdev *sd);
- int (*t_focus_abs)(struct v4l2_subdev *sd, s32 value);
- int (*t_focus_rel)(struct v4l2_subdev *sd, s32 value);
- int (*q_focus_status)(struct v4l2_subdev *sd, s32 *value);
- int (*q_focus_abs)(struct v4l2_subdev *sd, s32 *value);
- int (*t_vcm_slew)(struct v4l2_subdev *sd, s32 value);
- int (*t_vcm_timing)(struct v4l2_subdev *sd, s32 value);
-};
-
-/*
- * Defines for register writes and register array processing
- * */
-#define OV8858_BYTE_MAX 32
-#define OV8858_SHORT_MAX 16
-#define OV8858_TOK_MASK 0xFFF0
-
-#define MAX_FPS_OPTIONS_SUPPORTED 3
-
-#define OV8858_DEPTH_COMP_CONST 2200
-#define OV8858_DEPTH_VTS_CONST 2573
-
-enum ov8858_tok_type {
- OV8858_8BIT = 0x0001,
- OV8858_16BIT = 0x0002,
- OV8858_TOK_TERM = 0xF000, /* terminating token for reg list */
- OV8858_TOK_DELAY = 0xFE00 /* delay token for reg list */
-};
-
-/*
- * If register address or register width is not 32 bit width,
- * user needs to convert it manually
- */
-struct s_register_setting {
- u32 reg;
- u32 val;
-};
-
-/**
- * struct ov8858_reg - MI sensor register format
- * @type: type of the register
- * @reg: 16-bit offset to register
- * @val: 8/16/32-bit register value
- *
- * Define a structure for sensor register initialization values
- */
-struct ov8858_reg {
- enum ov8858_tok_type type;
- u16 sreg;
- u32 val; /* @set value for read/mod/write, @mask */
-};
-
-struct ov8858_fps_setting {
- int fps;
- unsigned short pixels_per_line;
- unsigned short lines_per_frame;
- const struct ov8858_reg *regs; /* regs that the fps setting needs */
-};
-
-struct ov8858_resolution {
- u8 *desc;
- const struct ov8858_reg *regs;
- int res;
- int width;
- int height;
- bool used;
- u8 bin_factor_x;
- u8 bin_factor_y;
- unsigned short skip_frames;
- const struct ov8858_fps_setting fps_options[MAX_FPS_OPTIONS_SUPPORTED];
-};
-
-/*
- * ov8858 device structure
- * */
-struct ov8858_device {
- struct v4l2_subdev sd;
- struct media_pad pad;
- struct v4l2_mbus_framefmt format;
-
- struct camera_sensor_platform_data *platform_data;
- struct mutex input_lock; /* serialize sensor's ioctl */
- int fmt_idx;
- int streaming;
- int vt_pix_clk_freq_mhz;
- int fps_index;
- u16 sensor_id; /* Sensor id from registers */
- u16 i2c_id; /* Sensor id from i2c_device_id */
- int exposure;
- int gain;
- u16 digital_gain;
- u16 pixels_per_line;
- u16 lines_per_frame;
- u8 fps;
- u8 *otp_data;
- /* Prevent the framerate from being lowered in low light scenes. */
- int limit_exposure_flag;
- bool hflip;
- bool vflip;
-
- const struct ov8858_reg *regs;
- struct ov8858_vcm *vcm_driver;
- const struct ov8858_resolution *curr_res_table;
- unsigned long entries_curr_table;
-
- struct v4l2_ctrl_handler ctrl_handler;
- struct v4l2_ctrl *run_mode;
-};
-
-#define to_ov8858_sensor(x) container_of(x, struct ov8858_device, sd)
-
-#define OV8858_MAX_WRITE_BUF_SIZE 32
-struct ov8858_write_buffer {
- u16 addr;
- u8 data[OV8858_MAX_WRITE_BUF_SIZE];
-};
-
-struct ov8858_write_ctrl {
- int index;
- struct ov8858_write_buffer buffer;
-};
-
-static const struct ov8858_reg ov8858_soft_standby[] = {
- {OV8858_8BIT, 0x0100, 0x00},
- {OV8858_TOK_TERM, 0, 0}
-};
-
-static const struct ov8858_reg ov8858_streaming[] = {
- {OV8858_8BIT, 0x0100, 0x01},
- {OV8858_TOK_TERM, 0, 0}
-};
-
-static const struct ov8858_reg ov8858_param_hold[] = {
- {OV8858_8BIT, OV8858_GROUP_ACCESS,
- OV8858_GROUP_ZERO | OV8858_GROUP_ACCESS_HOLD_START},
- {OV8858_TOK_TERM, 0, 0}
-};
-
-static const struct ov8858_reg ov8858_param_update[] = {
- {OV8858_8BIT, OV8858_GROUP_ACCESS,
- OV8858_GROUP_ZERO | OV8858_GROUP_ACCESS_HOLD_END},
- {OV8858_8BIT, OV8858_GROUP_ACCESS,
- OV8858_GROUP_ZERO | OV8858_GROUP_ACCESS_DELAY_LAUNCH},
- {OV8858_TOK_TERM, 0, 0}
-};
-
-extern int dw9718_vcm_power_up(struct v4l2_subdev *sd);
-extern int dw9718_vcm_power_down(struct v4l2_subdev *sd);
-extern int dw9718_vcm_init(struct v4l2_subdev *sd);
-extern int dw9718_t_focus_abs(struct v4l2_subdev *sd, s32 value);
-extern int dw9718_t_focus_rel(struct v4l2_subdev *sd, s32 value);
-extern int dw9718_q_focus_status(struct v4l2_subdev *sd, s32 *value);
-extern int dw9718_q_focus_abs(struct v4l2_subdev *sd, s32 *value);
-extern int dw9718_t_vcm_slew(struct v4l2_subdev *sd, s32 value);
-extern int dw9718_t_vcm_timing(struct v4l2_subdev *sd, s32 value);
-
-extern int vcm_power_up(struct v4l2_subdev *sd);
-extern int vcm_power_down(struct v4l2_subdev *sd);
-
-static struct ov8858_vcm ov8858_vcms[] = {
- [OV8858_SUNNY] = {
- .power_up = dw9718_vcm_power_up,
- .power_down = dw9718_vcm_power_down,
- .init = dw9718_vcm_init,
- .t_focus_abs = dw9718_t_focus_abs,
- .t_focus_rel = dw9718_t_focus_rel,
- .q_focus_status = dw9718_q_focus_status,
- .q_focus_abs = dw9718_q_focus_abs,
- .t_vcm_slew = dw9718_t_vcm_slew,
- .t_vcm_timing = dw9718_t_vcm_timing,
- },
- [OV8858_ID_DEFAULT] = {
- .power_up = NULL,
- .power_down = NULL,
- },
-};
-
-
-#define OV8858_RES_WIDTH_MAX 3280
-#define OV8858_RES_HEIGHT_MAX 2464
-
-static struct ov8858_reg ov8858_BasicSettings[] = {
- {OV8858_8BIT, 0x0103, 0x01}, /* software_reset */
- {OV8858_8BIT, 0x0100, 0x00}, /* software_standby */
- /* PLL settings */
- {OV8858_8BIT, 0x0300, 0x05}, /* pll1_pre_div = /4 */
- {OV8858_8BIT, 0x0302, 0xAF}, /* pll1_multiplier = 175 */
- {OV8858_8BIT, 0x0303, 0x00}, /* pll1_divm = /(1 + 0) */
- {OV8858_8BIT, 0x0304, 0x03}, /* pll1_div_mipi = /8 */
- {OV8858_8BIT, 0x030B, 0x02}, /* pll2_pre_div = /2 */
- {OV8858_8BIT, 0x030D, 0x4E}, /* pll2_r_divp = 78 */
- {OV8858_8BIT, 0x030E, 0x00}, /* pll2_r_divs = /1 */
- {OV8858_8BIT, 0x030F, 0x04}, /* pll2_r_divsp = /(1 + 4) */
- /* pll2_pre_div0 = /1, pll2_r_divdac = /(1 + 1) */
- {OV8858_8BIT, 0x0312, 0x01},
- {OV8858_8BIT, 0x031E, 0x0C}, /* pll1_no_lat = 1, mipi_bitsel_man = 0 */
-
- /* PAD OEN2, VSYNC out enable=0x80, disable=0x00 */
- {OV8858_8BIT, 0x3002, 0x80},
- /* PAD OUT2, VSYNC pulse direction low-to-high = 1 */
- {OV8858_8BIT, 0x3007, 0x01},
- /* PAD SEL2, VSYNC out value = 0 */
- {OV8858_8BIT, 0x300D, 0x00},
- /* PAD OUT2, VSYNC out select = 0 */
- {OV8858_8BIT, 0x3010, 0x00},
-
- /* Npump clock div = /2, Ppump clock div = /4 */
- {OV8858_8BIT, 0x3015, 0x01},
- /*
- * mipi_lane_mode = 1+3, mipi_lvds_sel = 1 = MIPI enable,
- * r_phy_pd_mipi_man = 0, lane_dis_option = 0
- */
- {OV8858_8BIT, 0x3018, 0x72},
- /* Clock switch output = normal, pclk_div = /1 */
- {OV8858_8BIT, 0x3020, 0x93},
- /*
- * lvds_mode_o = 0, clock lane disable when pd_mipi = 0,
- * pd_mipi enable when rst_sync = 1
- */
- {OV8858_8BIT, 0x3022, 0x01},
- {OV8858_8BIT, 0x3031, 0x0A}, /* mipi_bit_sel = 10 */
- {OV8858_8BIT, 0x3034, 0x00}, /* Unknown */
- /* sclk_div = /1, sclk_pre_div = /1, chip debug = 1 */
- {OV8858_8BIT, 0x3106, 0x01},
-
- {OV8858_8BIT, 0x3305, 0xF1}, /* Unknown */
- {OV8858_8BIT, 0x3307, 0x04}, /* Unknown */
- {OV8858_8BIT, 0x3308, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x3309, 0x28}, /* Unknown */
- {OV8858_8BIT, 0x330A, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x330B, 0x20}, /* Unknown */
- {OV8858_8BIT, 0x330C, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x330D, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x330E, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x330F, 0x40}, /* Unknown */
-
- {OV8858_8BIT, 0x3500, 0x00}, /* long exposure = 0x9A20 */
- {OV8858_8BIT, 0x3501, 0x9A}, /* long exposure = 0x9A20 */
- {OV8858_8BIT, 0x3502, 0x20}, /* long exposure = 0x9A20 */
- /*
- * Digital fraction gain delay option = Delay 1 frame,
- * Gain change delay option = Delay 1 frame,
- * Gain delay option = Delay 1 frame,
- * Gain manual as sensor gain = Input gain as real gain format,
- * Exposure delay option (must be 0 = Delay 1 frame,
- * Exposure change delay option (must be 0) = Delay 1 frame
- */
- {OV8858_8BIT, 0x3503, 0x00},
- {OV8858_8BIT, 0x3505, 0x80}, /* gain conversation option */
- /*
- * [10:7] are integer gain, [6:0] are fraction gain. For example:
- * 0x80 is 1x gain, 0x100 is 2x gain, 0x1C0 is 3.5x gain
- */
- {OV8858_8BIT, 0x3508, 0x02}, /* long gain = 0x0200 */
- {OV8858_8BIT, 0x3509, 0x00}, /* long gain = 0x0200 */
- {OV8858_8BIT, 0x350C, 0x00}, /* short gain = 0x0080 */
- {OV8858_8BIT, 0x350D, 0x80}, /* short gain = 0x0080 */
- {OV8858_8BIT, 0x3510, 0x00}, /* short exposure = 0x000200 */
- {OV8858_8BIT, 0x3511, 0x02}, /* short exposure = 0x000200 */
- {OV8858_8BIT, 0x3512, 0x00}, /* short exposure = 0x000200 */
-
- {OV8858_8BIT, 0x3600, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x3601, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x3602, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x3603, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x3604, 0x22}, /* Unknown */
- {OV8858_8BIT, 0x3605, 0x30}, /* Unknown */
- {OV8858_8BIT, 0x3606, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x3607, 0x20}, /* Unknown */
- {OV8858_8BIT, 0x3608, 0x11}, /* Unknown */
- {OV8858_8BIT, 0x3609, 0x28}, /* Unknown */
- {OV8858_8BIT, 0x360A, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x360B, 0x06}, /* Unknown */
- {OV8858_8BIT, 0x360C, 0xDC}, /* Unknown */
- {OV8858_8BIT, 0x360D, 0x40}, /* Unknown */
- {OV8858_8BIT, 0x360E, 0x0C}, /* Unknown */
- {OV8858_8BIT, 0x360F, 0x20}, /* Unknown */
- {OV8858_8BIT, 0x3610, 0x07}, /* Unknown */
- {OV8858_8BIT, 0x3611, 0x20}, /* Unknown */
- {OV8858_8BIT, 0x3612, 0x88}, /* Unknown */
- {OV8858_8BIT, 0x3613, 0x80}, /* Unknown */
- {OV8858_8BIT, 0x3614, 0x58}, /* Unknown */
- {OV8858_8BIT, 0x3615, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x3616, 0x4A}, /* Unknown */
- {OV8858_8BIT, 0x3617, 0x90}, /* Unknown */
- {OV8858_8BIT, 0x3618, 0x56}, /* Unknown */
- {OV8858_8BIT, 0x3619, 0x70}, /* Unknown */
- {OV8858_8BIT, 0x361A, 0x99}, /* Unknown */
- {OV8858_8BIT, 0x361B, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x361C, 0x07}, /* Unknown */
- {OV8858_8BIT, 0x361D, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x361E, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x361F, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x3633, 0x0C}, /* Unknown */
- {OV8858_8BIT, 0x3634, 0x0C}, /* Unknown */
- {OV8858_8BIT, 0x3635, 0x0C}, /* Unknown */
- {OV8858_8BIT, 0x3636, 0x0C}, /* Unknown */
- {OV8858_8BIT, 0x3638, 0xFF}, /* Unknown */
- {OV8858_8BIT, 0x3645, 0x13}, /* Unknown */
- {OV8858_8BIT, 0x3646, 0x83}, /* Unknown */
- {OV8858_8BIT, 0x364A, 0x07}, /* Unknown */
-
- {OV8858_8BIT, 0x3700, 0x30}, /* Unknown */
- {OV8858_8BIT, 0x3701, 0x18}, /* Unknown */
- {OV8858_8BIT, 0x3702, 0x50}, /* Unknown */
- {OV8858_8BIT, 0x3703, 0x32}, /* Unknown */
- {OV8858_8BIT, 0x3704, 0x28}, /* Unknown */
- {OV8858_8BIT, 0x3705, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x3706, 0x6A}, /* Unknown */
- {OV8858_8BIT, 0x3707, 0x08}, /* Unknown */
- {OV8858_8BIT, 0x3708, 0x48}, /* Unknown */
- {OV8858_8BIT, 0x3709, 0x66}, /* Unknown */
- {OV8858_8BIT, 0x370A, 0x01}, /* Unknown */
- {OV8858_8BIT, 0x370B, 0x6A}, /* Unknown */
- {OV8858_8BIT, 0x370C, 0x07}, /* Unknown */
- {OV8858_8BIT, 0x3712, 0x44}, /* Unknown */
- {OV8858_8BIT, 0x3714, 0x24}, /* Unknown */
- {OV8858_8BIT, 0x3718, 0x14}, /* Unknown */
- {OV8858_8BIT, 0x3719, 0x31}, /* Unknown */
- {OV8858_8BIT, 0x371E, 0x31}, /* Unknown */
- {OV8858_8BIT, 0x371F, 0x7F}, /* Unknown */
- {OV8858_8BIT, 0x3720, 0x0A}, /* Unknown */
- {OV8858_8BIT, 0x3721, 0x0A}, /* Unknown */
- {OV8858_8BIT, 0x3724, 0x0C}, /* Unknown */
- {OV8858_8BIT, 0x3725, 0x02}, /* Unknown */
- {OV8858_8BIT, 0x3726, 0x0C}, /* Unknown */
- {OV8858_8BIT, 0x3728, 0x0A}, /* Unknown */
- {OV8858_8BIT, 0x3729, 0x03}, /* Unknown */
- {OV8858_8BIT, 0x372A, 0x06}, /* Unknown */
- {OV8858_8BIT, 0x372B, 0xA6}, /* Unknown */
- {OV8858_8BIT, 0x372C, 0xA6}, /* Unknown */
- {OV8858_8BIT, 0x372D, 0xA6}, /* Unknown */
- {OV8858_8BIT, 0x372E, 0x0C}, /* Unknown */
- {OV8858_8BIT, 0x372F, 0x20}, /* Unknown */
- {OV8858_8BIT, 0x3730, 0x02}, /* Unknown */
- {OV8858_8BIT, 0x3731, 0x0C}, /* Unknown */
- {OV8858_8BIT, 0x3732, 0x28}, /* Unknown */
- {OV8858_8BIT, 0x3733, 0x10}, /* Unknown */
- {OV8858_8BIT, 0x3734, 0x40}, /* Unknown */
- {OV8858_8BIT, 0x3736, 0x30}, /* Unknown */
- {OV8858_8BIT, 0x373A, 0x0A}, /* Unknown */
- {OV8858_8BIT, 0x373B, 0x0B}, /* Unknown */
- {OV8858_8BIT, 0x373C, 0x14}, /* Unknown */
- {OV8858_8BIT, 0x373E, 0x06}, /* Unknown */
- {OV8858_8BIT, 0x3755, 0x10}, /* Unknown */
- {OV8858_8BIT, 0x3758, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x3759, 0x4C}, /* Unknown */
- {OV8858_8BIT, 0x375A, 0x0C}, /* Unknown */
- {OV8858_8BIT, 0x375B, 0x26}, /* Unknown */
- {OV8858_8BIT, 0x375C, 0x20}, /* Unknown */
- {OV8858_8BIT, 0x375D, 0x04}, /* Unknown */
- {OV8858_8BIT, 0x375E, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x375F, 0x28}, /* Unknown */
- {OV8858_8BIT, 0x3760, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x3761, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x3762, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x3763, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x3766, 0xFF}, /* Unknown */
- {OV8858_8BIT, 0x3768, 0x22}, /* Unknown */
- {OV8858_8BIT, 0x3769, 0x44}, /* Unknown */
- {OV8858_8BIT, 0x376A, 0x44}, /* Unknown */
- {OV8858_8BIT, 0x376B, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x376F, 0x01}, /* Unknown */
- {OV8858_8BIT, 0x3772, 0x46}, /* Unknown */
- {OV8858_8BIT, 0x3773, 0x04}, /* Unknown */
- {OV8858_8BIT, 0x3774, 0x2C}, /* Unknown */
- {OV8858_8BIT, 0x3775, 0x13}, /* Unknown */
- {OV8858_8BIT, 0x3776, 0x08}, /* Unknown */
- {OV8858_8BIT, 0x3777, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x3778, 0x16}, /* Unknown */
- {OV8858_8BIT, 0x37A0, 0x88}, /* Unknown */
- {OV8858_8BIT, 0x37A1, 0x7A}, /* Unknown */
- {OV8858_8BIT, 0x37A2, 0x7A}, /* Unknown */
- {OV8858_8BIT, 0x37A3, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x37A4, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x37A5, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x37A6, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x37A7, 0x88}, /* Unknown */
- {OV8858_8BIT, 0x37A8, 0x98}, /* Unknown */
- {OV8858_8BIT, 0x37A9, 0x98}, /* Unknown */
- {OV8858_8BIT, 0x37AA, 0x88}, /* Unknown */
- {OV8858_8BIT, 0x37AB, 0x5C}, /* Unknown */
- {OV8858_8BIT, 0x37AC, 0x5C}, /* Unknown */
- {OV8858_8BIT, 0x37AD, 0x55}, /* Unknown */
- {OV8858_8BIT, 0x37AE, 0x19}, /* Unknown */
- {OV8858_8BIT, 0x37AF, 0x19}, /* Unknown */
- {OV8858_8BIT, 0x37B0, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x37B1, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x37B2, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x37B3, 0x84}, /* Unknown */
- {OV8858_8BIT, 0x37B4, 0x84}, /* Unknown */
- {OV8858_8BIT, 0x37B5, 0x66}, /* Unknown */
- {OV8858_8BIT, 0x37B6, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x37B7, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x37B8, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x37B9, 0xFF}, /* Unknown */
-
- {OV8858_8BIT, 0x3800, 0x00}, /* h_crop_start high */
- {OV8858_8BIT, 0x3801, 0x0C}, /* h_crop_start low */
- {OV8858_8BIT, 0x3802, 0x00}, /* v_crop_start high */
- {OV8858_8BIT, 0x3803, 0x0C}, /* v_crop_start low */
- {OV8858_8BIT, 0x3804, 0x0C}, /* h_crop_end high */
- {OV8858_8BIT, 0x3805, 0xD3}, /* h_crop_end low */
- {OV8858_8BIT, 0x3806, 0x09}, /* v_crop_end high */
- {OV8858_8BIT, 0x3807, 0xA3}, /* v_crop_end low */
- {OV8858_8BIT, 0x3808, 0x0C}, /* h_output_size high */
- {OV8858_8BIT, 0x3809, 0xC0}, /* h_output_size low */
- {OV8858_8BIT, 0x380A, 0x09}, /* v_output_size high */
- {OV8858_8BIT, 0x380B, 0x90}, /* v_output_size low */
- {OV8858_8BIT, 0x380C, 0x07}, /* horizontal timing size high */
- {OV8858_8BIT, 0x380D, 0x94}, /* horizontal timing size low */
- {OV8858_8BIT, 0x380E, 0x0A}, /* vertical timing size high */
- {OV8858_8BIT, 0x380F, 0x0D}, /* vertical timing size low */
- {OV8858_8BIT, 0x3810, 0x00}, /* h_win offset high */
- {OV8858_8BIT, 0x3811, 0x04}, /* h_win offset low */
- {OV8858_8BIT, 0x3812, 0x00}, /* v_win offset high */
- {OV8858_8BIT, 0x3813, 0x02}, /* v_win offset low */
- {OV8858_8BIT, 0x3814, 0x01}, /* h_odd_inc */
- {OV8858_8BIT, 0x3815, 0x01}, /* h_even_inc */
- {OV8858_8BIT, 0x3820, 0x00}, /* format1 */
- {OV8858_8BIT, 0x3821, 0x40}, /* format2 */
- {OV8858_8BIT, 0x382A, 0x01}, /* v_odd_inc */
- {OV8858_8BIT, 0x382B, 0x01}, /* v_even_inc */
-
- {OV8858_8BIT, 0x3830, 0x06}, /* Unknown */
- {OV8858_8BIT, 0x3836, 0x01}, /* Unknown */
- {OV8858_8BIT, 0x3837, 0x18}, /* Unknown */
- {OV8858_8BIT, 0x3841, 0xFF}, /* AUTO_SIZE_CTRL */
- {OV8858_8BIT, 0x3846, 0x48}, /* Unknown */
-
- {OV8858_8BIT, 0x3D85, 0x14}, /* OTP_REG85 */
- {OV8858_8BIT, 0x3D8C, 0x73}, /* OTP_SETTING_STT_ADDRESS */
- {OV8858_8BIT, 0x3D8D, 0xDE}, /* OTP_SETTING_STT_ADDRESS */
- {OV8858_8BIT, 0x3F08, 0x10}, /* PSRAM control register */
- {OV8858_8BIT, 0x3F0A, 0x80}, /* PSRAM control register */
-
- {OV8858_8BIT, 0x4000, 0xF1}, /* BLC CTRL00 = default */
- {OV8858_8BIT, 0x4001, 0x00}, /* BLC CTRL01 */
- {OV8858_8BIT, 0x4002, 0x27}, /* BLC offset = 0x27 */
- {OV8858_8BIT, 0x4005, 0x10}, /* BLC target = 0x0010 */
- {OV8858_8BIT, 0x4009, 0x81}, /* BLC CTRL09 */
- {OV8858_8BIT, 0x400B, 0x0C}, /* BLC CTRL0B = default */
- {OV8858_8BIT, 0x400A, 0x01},
- {OV8858_8BIT, 0x4011, 0x20}, /* BLC CTRL11 = 0x20 */
- {OV8858_8BIT, 0x401B, 0x00}, /* Zero line R coeff. = 0x0000 */
- {OV8858_8BIT, 0x401D, 0x00}, /* Zero line T coeff. = 0x0000 */
- {OV8858_8BIT, 0x401F, 0x00}, /* BLC CTRL1F */
- {OV8858_8BIT, 0x4020, 0x00}, /* Anchor left start = 0x0004 */
- {OV8858_8BIT, 0x4021, 0x04}, /* Anchor left start = 0x0004 */
- {OV8858_8BIT, 0x4022, 0x0C}, /* Anchor left end = 0x0C60 */
- {OV8858_8BIT, 0x4023, 0x60}, /* Anchor left end = 0x0C60 */
- {OV8858_8BIT, 0x4024, 0x0F}, /* Anchor right start = 0x0F36 */
- {OV8858_8BIT, 0x4025, 0x36}, /* Anchor right start = 0x0F36 */
- {OV8858_8BIT, 0x4026, 0x0F}, /* Anchor right end = 0x0F37 */
- {OV8858_8BIT, 0x4027, 0x37}, /* Anchor right end = 0x0F37 */
- {OV8858_8BIT, 0x4028, 0x00}, /* Top zero line start = 0 */
- {OV8858_8BIT, 0x4029, 0x04}, /* Top zero line number = 4 */
- {OV8858_8BIT, 0x402A, 0x04}, /* Top black line start = 4 */
- {OV8858_8BIT, 0x402B, 0x08}, /* Top black line number = 8 */
- {OV8858_8BIT, 0x402C, 0x00}, /* Bottom zero start line = 0 */
- {OV8858_8BIT, 0x402D, 0x02}, /* Bottom zero line number = 2 */
- {OV8858_8BIT, 0x402E, 0x04}, /* Bottom black line start = 4 */
- {OV8858_8BIT, 0x402F, 0x08}, /* Bottom black line number = 8 */
-
- {OV8858_8BIT, 0x4034, 0x3F}, /* Unknown */
- {OV8858_8BIT, 0x403D, 0x04}, /* BLC CTRL3D */
- {OV8858_8BIT, 0x4300, 0xFF}, /* clip_max[11:4] = 0xFFF */
- {OV8858_8BIT, 0x4301, 0x00}, /* clip_min[11:4] = 0 */
- {OV8858_8BIT, 0x4302, 0x0F}, /* clip_min/max[3:0] */
- {OV8858_8BIT, 0x4307, 0x01}, /* Unknown */
- {OV8858_8BIT, 0x4316, 0x00}, /* CTRL16 = default */
- {OV8858_8BIT, 0x4503, 0x18}, /* Unknown */
- {OV8858_8BIT, 0x4500, 0x38}, /* Unknown */
- {OV8858_8BIT, 0x4600, 0x01}, /* Unknown */
- {OV8858_8BIT, 0x4601, 0x97}, /* Unknown */
- /* wkup_dly = Mark1 wakeup delay/2^10 = 0x25 */
- {OV8858_8BIT, 0x4808, 0x25},
- {OV8858_8BIT, 0x4816, 0x52}, /* Embedded data type*/
- {OV8858_8BIT, 0x481F, 0x32}, /* clk_prepare_min = 0x32 */
- {OV8858_8BIT, 0x4825, 0x3A}, /* lpx_p_min = 0x3A */
- {OV8858_8BIT, 0x4826, 0x40}, /* hs_prepare_min = 0x40 */
- {OV8858_8BIT, 0x4837, 0x14}, /* pclk_period = 0x14 */
- {OV8858_8BIT, 0x4850, 0x10}, /* LANE SEL01 */
- {OV8858_8BIT, 0x4851, 0x32}, /* LANE SEL02 */
-
- {OV8858_8BIT, 0x4B00, 0x2A}, /* Unknown */
- {OV8858_8BIT, 0x4B0D, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x4D00, 0x04}, /* TPM_CTRL_REG */
- {OV8858_8BIT, 0x4D01, 0x18}, /* TPM_CTRL_REG */
- {OV8858_8BIT, 0x4D02, 0xC3}, /* TPM_CTRL_REG */
- {OV8858_8BIT, 0x4D03, 0xFF}, /* TPM_CTRL_REG */
- {OV8858_8BIT, 0x4D04, 0xFF}, /* TPM_CTRL_REG */
- {OV8858_8BIT, 0x4D05, 0xFF}, /* TPM_CTRL_REG */
-
- /*
- * Lens correction (LENC) function enable = 0
- * Slave sensor AWB Gain function enable = 1
- * Slave sensor AWB Statistics function enable = 1
- * Master sensor AWB Gain function enable = 1
- * Master sensor AWB Statistics function enable = 1
- * Black DPC function enable = 1
- * White DPC function enable =1
- */
- {OV8858_8BIT, 0x5000, 0x7E},
- {OV8858_8BIT, 0x5001, 0x01}, /* BLC function enable = 1 */
- /*
- * Horizontal scale function enable = 0
- * WBMATCH bypass mode = Select slave sensor's gain
- * WBMATCH function enable = 0
- * Master MWB gain support RGBC = 0
- * OTP_DPC function enable = 1
- * Manual mode of VarioPixel function enable = 0
- * Manual enable of VarioPixel function enable = 0
- * Use VSYNC to latch ISP modules's function enable signals = 0
- */
- {OV8858_8BIT, 0x5002, 0x08},
- /*
- * Bypass all ISP modules after BLC module = 0
- * DPC_DBC buffer control enable = 1
- * WBMATCH VSYNC selection = Select master sensor's VSYNC fall
- * Select master AWB gain to embed line = AWB gain before manual mode
- * Enable BLC's input flip_i signal = 0
- */
- {OV8858_8BIT, 0x5003, 0x20},
- {OV8858_8BIT, 0x5041, 0x1D}, /* ISP CTRL41 - embedded data=on */
- {OV8858_8BIT, 0x5046, 0x12}, /* ISP CTRL46 = default */
- /*
- * Tail enable = 1
- * Saturate cross cluster enable = 1
- * Remove cross cluster enable = 1
- * Enable to remove connected defect pixels in same channel = 1
- * Enable to remove connected defect pixels in different channel = 1
- * Smooth enable, use average G for recovery = 1
- * Black/white sensor mode enable = 0
- * Manual mode enable = 0
- */
- {OV8858_8BIT, 0x5780, 0xFC},
- {OV8858_8BIT, 0x5784, 0x0C}, /* DPC CTRL04 */
- {OV8858_8BIT, 0x5787, 0x40}, /* DPC CTRL07 */
- {OV8858_8BIT, 0x5788, 0x08}, /* DPC CTRL08 */
- {OV8858_8BIT, 0x578A, 0x02}, /* DPC CTRL0A */
- {OV8858_8BIT, 0x578B, 0x01}, /* DPC CTRL0B */
- {OV8858_8BIT, 0x578C, 0x01}, /* DPC CTRL0C */
- {OV8858_8BIT, 0x578E, 0x02}, /* DPC CTRL0E */
- {OV8858_8BIT, 0x578F, 0x01}, /* DPC CTRL0F */
- {OV8858_8BIT, 0x5790, 0x01}, /* DPC CTRL10 */
- {OV8858_8BIT, 0x5901, 0x00}, /* VAP CTRL01 = default */
- /* WINC CTRL08 = embedded data in 1st line*/
- {OV8858_8BIT, 0x5A08, 0x00},
- {OV8858_8BIT, 0x5B00, 0x02}, /* OTP CTRL00 */
- {OV8858_8BIT, 0x5B01, 0x10}, /* OTP CTRL01 */
- {OV8858_8BIT, 0x5B02, 0x03}, /* OTP CTRL02 */
- {OV8858_8BIT, 0x5B03, 0xCF}, /* OTP CTRL03 */
- {OV8858_8BIT, 0x5B05, 0x6C}, /* OTP CTRL05 = default */
- {OV8858_8BIT, 0x5E00, 0x00}, /* PRE CTRL00 = default */
- {OV8858_8BIT, 0x5E01, 0x41}, /* PRE_CTRL01 = default */
-
- {OV8858_TOK_TERM, 0, 0}
-};
-
-/*****************************STILL********************************/
-
-static const struct ov8858_reg ov8858_8M[] = {
- {OV8858_8BIT, 0x0100, 0x00}, /* software_standby */
- {OV8858_8BIT, 0x3778, 0x16}, /* Unknown */
- {OV8858_8BIT, 0x3800, 0x00}, /* h_crop_start high */
- {OV8858_8BIT, 0x3801, 0x0C}, /* h_crop_start low 12 */
- {OV8858_8BIT, 0x3802, 0x00}, /* v_crop_start high */
- {OV8858_8BIT, 0x3803, 0x0C}, /* v_crop_start low */
- {OV8858_8BIT, 0x3804, 0x0C}, /* h_crop_end high */
- {OV8858_8BIT, 0x3805, 0xD3}, /* h_crop_end low 3283 */
- {OV8858_8BIT, 0x3806, 0x09}, /* v_crop_end high */
- {OV8858_8BIT, 0x3807, 0xA3}, /* v_crop_end low */
- {OV8858_8BIT, 0x3808, 0x0C}, /* h_output_size high 3280 x 2464 */
- {OV8858_8BIT, 0x3809, 0xD0}, /* h_output_size low */
- {OV8858_8BIT, 0x380A, 0x09}, /* v_output_size high */
- {OV8858_8BIT, 0x380B, 0xa0}, /* v_output_size low */
- {OV8858_8BIT, 0x380C, 0x07}, /* horizontal timing size high */
- {OV8858_8BIT, 0x380D, 0x94}, /* horizontal timing size low */
- {OV8858_8BIT, 0x380E, 0x0A}, /* vertical timing size high */
- {OV8858_8BIT, 0x380F, 0x0D}, /* vertical timing size low */
- {OV8858_8BIT, 0x3814, 0x01}, /* h_odd_inc */
- {OV8858_8BIT, 0x3815, 0x01}, /* h_even_inc */
- {OV8858_8BIT, 0x3820, 0x00}, /* format1 */
- {OV8858_8BIT, 0x3821, 0x40}, /* format2 */
- {OV8858_8BIT, 0x382A, 0x01}, /* v_odd_inc */
- {OV8858_8BIT, 0x382B, 0x01}, /* v_even_inc */
- {OV8858_8BIT, 0x3830, 0x06}, /* Unknown */
- {OV8858_8BIT, 0x3836, 0x01}, /* Unknown */
- {OV8858_8BIT, 0x3D85, 0x14}, /* OTP_REG85 */
- {OV8858_8BIT, 0x3F08, 0x10}, /* PSRAM control register */
- {OV8858_8BIT, 0x4034, 0x3F}, /* Unknown */
- {OV8858_8BIT, 0x403D, 0x04}, /* BLC CTRL3D */
- {OV8858_8BIT, 0x4600, 0x01}, /* Unknown */
- {OV8858_8BIT, 0x4601, 0x97}, /* Unknown */
- {OV8858_8BIT, 0x4837, 0x14}, /* pclk_period = 0x14 */
- {OV8858_TOK_TERM, 0, 0}
-};
-
-static const struct ov8858_reg ov8858_3276x1848[] = {
- {OV8858_8BIT, 0x0100, 0x00}, /* software_standby */
- {OV8858_8BIT, 0x3778, 0x16}, /* Unknown */
- {OV8858_8BIT, 0x3800, 0x00}, /* h_crop_start high */
- {OV8858_8BIT, 0x3801, 0x10}, /* h_crop_start low 0c->10*/
- {OV8858_8BIT, 0x3802, 0x01}, /* v_crop_start high */
- {OV8858_8BIT, 0x3803, 0x42}, /* v_crop_start low 3e->42*/
- {OV8858_8BIT, 0x3804, 0x0C}, /* h_crop_end high */
- {OV8858_8BIT, 0x3805, 0xD3}, /* h_crop_end low */
- {OV8858_8BIT, 0x3806, 0x08}, /* v_crop_end high */
- {OV8858_8BIT, 0x3807, 0x71}, /* v_crop_end low */
- {OV8858_8BIT, 0x3808, 0x0C}, /* h_output_size high 3276 x 1848 */
- {OV8858_8BIT, 0x3809, 0xCC}, /* h_output_size low d0->cc*/
- {OV8858_8BIT, 0x380A, 0x07}, /* v_output_size high */
- {OV8858_8BIT, 0x380B, 0x38}, /* v_output_size low 3c->38*/
- {OV8858_8BIT, 0x380C, 0x07}, /* horizontal timing size high */
- {OV8858_8BIT, 0x380D, 0x94}, /* horizontal timing size low */
- {OV8858_8BIT, 0x380E, 0x0A}, /* vertical timing size high */
- {OV8858_8BIT, 0x380F, 0x0D}, /* vertical timing size low */
- {OV8858_8BIT, 0x3814, 0x01}, /* h_odd_inc */
- {OV8858_8BIT, 0x3815, 0x01}, /* h_even_inc */
- {OV8858_8BIT, 0x3820, 0x00}, /* format1 */
- {OV8858_8BIT, 0x3821, 0x40}, /* format2 */
- {OV8858_8BIT, 0x382A, 0x01}, /* v_odd_inc */
- {OV8858_8BIT, 0x382B, 0x01}, /* v_even_inc */
- {OV8858_8BIT, 0x3830, 0x06}, /* Unknown */
- {OV8858_8BIT, 0x3836, 0x01}, /* Unknown */
- {OV8858_8BIT, 0x3D85, 0x14}, /* OTP_REG85 */
- {OV8858_8BIT, 0x3F08, 0x10}, /* PSRAM control register */
- {OV8858_8BIT, 0x4034, 0x3F}, /* Unknown */
- {OV8858_8BIT, 0x403D, 0x04}, /* BLC CTRL3D */
- {OV8858_8BIT, 0x4600, 0x01}, /* Unknown */
- {OV8858_8BIT, 0x4601, 0x97}, /* Unknown */
- {OV8858_8BIT, 0x4837, 0x14}, /* pclk_period = 0x14 */
- {OV8858_TOK_TERM, 0, 0}
-};
-
-static const struct ov8858_reg ov8858_6M[] = {
- {OV8858_8BIT, 0x0100, 0x00}, /* software_standby */
- {OV8858_8BIT, 0x3778, 0x16}, /* Unknown */
- {OV8858_8BIT, 0x3800, 0x00}, /* h_crop_start high */
- {OV8858_8BIT, 0x3801, 0x0C}, /* h_crop_start low */
- {OV8858_8BIT, 0x3802, 0x01}, /* v_crop_start high */
- {OV8858_8BIT, 0x3803, 0x3E}, /* v_crop_start low */
- {OV8858_8BIT, 0x3804, 0x0C}, /* h_crop_end high */
- {OV8858_8BIT, 0x3805, 0xD3}, /* h_crop_end low */
- {OV8858_8BIT, 0x3806, 0x08}, /* v_crop_end high */
- {OV8858_8BIT, 0x3807, 0x71}, /* v_crop_end low */
- {OV8858_8BIT, 0x3808, 0x0C}, /* h_output_size high 3280 x 1852 */
- {OV8858_8BIT, 0x3809, 0xD0}, /* h_output_size low */
- {OV8858_8BIT, 0x380A, 0x07}, /* v_output_size high */
- {OV8858_8BIT, 0x380B, 0x3C}, /* v_output_size low */
- {OV8858_8BIT, 0x380C, 0x07}, /* horizontal timing size high */
- {OV8858_8BIT, 0x380D, 0x94}, /* horizontal timing size low */
- {OV8858_8BIT, 0x380E, 0x0A}, /* vertical timing size high */
- {OV8858_8BIT, 0x380F, 0x0D}, /* vertical timing size low */
- {OV8858_8BIT, 0x3814, 0x01}, /* h_odd_inc */
- {OV8858_8BIT, 0x3815, 0x01}, /* h_even_inc */
- {OV8858_8BIT, 0x3820, 0x00}, /* format1 */
- {OV8858_8BIT, 0x3821, 0x40}, /* format2 */
- {OV8858_8BIT, 0x382A, 0x01}, /* v_odd_inc */
- {OV8858_8BIT, 0x382B, 0x01}, /* v_even_inc */
- {OV8858_8BIT, 0x3830, 0x06}, /* Unknown */
- {OV8858_8BIT, 0x3836, 0x01}, /* Unknown */
- {OV8858_8BIT, 0x3D85, 0x14}, /* OTP_REG85 */
- {OV8858_8BIT, 0x3F08, 0x10}, /* PSRAM control register */
- {OV8858_8BIT, 0x4034, 0x3F}, /* Unknown */
- {OV8858_8BIT, 0x403D, 0x04}, /* BLC CTRL3D */
- {OV8858_8BIT, 0x4600, 0x01}, /* Unknown */
- {OV8858_8BIT, 0x4601, 0x97}, /* Unknown */
- {OV8858_8BIT, 0x4837, 0x14}, /* pclk_period = 0x14 */
- {OV8858_TOK_TERM, 0, 0}
-};
-
-static const struct ov8858_reg ov8858_1080P_60[] = {
- {OV8858_8BIT, 0x0100, 0x00}, /* software_standby */
- {OV8858_8BIT, 0x3778, 0x17}, /* Unknown */
- {OV8858_8BIT, 0x3800, 0x02}, /* h_crop_start high */
- {OV8858_8BIT, 0x3801, 0x26}, /* h_crop_start low */
- {OV8858_8BIT, 0x3802, 0x02}, /* v_crop_start high */
- {OV8858_8BIT, 0x3803, 0x8C}, /* v_crop_start low */
- {OV8858_8BIT, 0x3804, 0x0A}, /* h_crop_end high */
- {OV8858_8BIT, 0x3805, 0x9D}, /* h_crop_end low */
- {OV8858_8BIT, 0x3806, 0x07}, /* v_crop_end high */
- {OV8858_8BIT, 0x3807, 0x0A}, /* v_crop_end low */
- {OV8858_8BIT, 0x3808, 0x07}, /* h_output_size high*/
- {OV8858_8BIT, 0x3809, 0x90}, /* h_output_size low */
- {OV8858_8BIT, 0x380A, 0x04}, /* v_output_size high */
- {OV8858_8BIT, 0x380B, 0x48}, /* v_output_size low */
- {OV8858_8BIT, 0x380C, 0x07}, /* horizontal timing size high */
- {OV8858_8BIT, 0x380D, 0x94}, /* horizontal timing size low */
- {OV8858_8BIT, 0x380E, 0x04}, /* vertical timing size high */
- {OV8858_8BIT, 0x380F, 0xEC}, /* vertical timing size low */
- {OV8858_8BIT, 0x3814, 0x01}, /* h_odd_inc */
- {OV8858_8BIT, 0x3815, 0x01}, /* h_even_inc */
- {OV8858_8BIT, 0x3820, 0x00}, /* format1 */
- {OV8858_8BIT, 0x3821, 0x40}, /* format2 */
- {OV8858_8BIT, 0x382A, 0x01}, /* v_odd_inc */
- {OV8858_8BIT, 0x382B, 0x01}, /* v_even_inc */
- {OV8858_8BIT, 0x3830, 0x06}, /* Unknown */
- {OV8858_8BIT, 0x3836, 0x01}, /* Unknown */
- {OV8858_8BIT, 0x3D85, 0x14}, /* OTP_REG85 */
- {OV8858_8BIT, 0x3F08, 0x10}, /* PSRAM control register */
- {OV8858_8BIT, 0x4034, 0x3F}, /* Unknown */
- {OV8858_8BIT, 0x403D, 0x04}, /* BLC CTRL3D */
- {OV8858_8BIT, 0x4600, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x4601, 0xef}, /* Unknown */
- {OV8858_8BIT, 0x4837, 0x16}, /* pclk_period = 0x16 */
- {OV8858_TOK_TERM, 0, 0}
-};
-
-static const struct ov8858_reg ov8858_1080P_30[] = {
- {OV8858_8BIT, 0x0100, 0x00}, /* software_standby */
- {OV8858_8BIT, 0x3778, 0x17}, /* Unknown */
- {OV8858_8BIT, 0x3800, 0x02}, /* h_crop_start high */
- {OV8858_8BIT, 0x3801, 0x26}, /* h_crop_start low */
- {OV8858_8BIT, 0x3802, 0x02}, /* v_crop_start high */
- {OV8858_8BIT, 0x3803, 0x8C}, /* v_crop_start low */
- {OV8858_8BIT, 0x3804, 0x0A}, /* h_crop_end high */
- {OV8858_8BIT, 0x3805, 0x9D}, /* h_crop_end low */
- {OV8858_8BIT, 0x3806, 0x07}, /* v_crop_end high */
- {OV8858_8BIT, 0x3807, 0x0A}, /* v_crop_end low */
- {OV8858_8BIT, 0x3808, 0x07}, /* h_output_size high*/
- {OV8858_8BIT, 0x3809, 0x90}, /* h_output_size low */
- {OV8858_8BIT, 0x380A, 0x04}, /* v_output_size high */
- {OV8858_8BIT, 0x380B, 0x48}, /* v_output_size low */
- {OV8858_8BIT, 0x380C, 0x07}, /* horizontal timing size high */
- {OV8858_8BIT, 0x380D, 0x94}, /* horizontal timing size low */
- {OV8858_8BIT, 0x380E, 0x0A}, /* vertical timing size high */
- {OV8858_8BIT, 0x380F, 0x0D}, /* vertical timing size low */
- {OV8858_8BIT, 0x3814, 0x01}, /* h_odd_inc */
- {OV8858_8BIT, 0x3815, 0x01}, /* h_even_inc */
- {OV8858_8BIT, 0x3820, 0x00}, /* format1 */
- {OV8858_8BIT, 0x3821, 0x40}, /* format2 */
- {OV8858_8BIT, 0x382A, 0x01}, /* v_odd_inc */
- {OV8858_8BIT, 0x382B, 0x01}, /* v_even_inc */
- {OV8858_8BIT, 0x3830, 0x06}, /* Unknown */
- {OV8858_8BIT, 0x3836, 0x01}, /* Unknown */
- {OV8858_8BIT, 0x3D85, 0x14}, /* OTP_REG85 */
- {OV8858_8BIT, 0x3F08, 0x10}, /* PSRAM control register */
- {OV8858_8BIT, 0x4034, 0x3F}, /* Unknown */
- {OV8858_8BIT, 0x403D, 0x04}, /* BLC CTRL3D */
- {OV8858_8BIT, 0x4600, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x4601, 0xef}, /* Unknown */
- {OV8858_8BIT, 0x4837, 0x16}, /* pclk_period = 0x16 */
- {OV8858_TOK_TERM, 0, 0}
-};
-
-static const struct ov8858_reg ov8858_1640x1232[] = {
- {OV8858_8BIT, 0x0100, 0x00}, /* software_standby */
- {OV8858_8BIT, 0x3778, 0x16}, /* Unknown */
- {OV8858_8BIT, 0x3800, 0x00}, /* h_crop_start high */
- {OV8858_8BIT, 0x3801, 0x0C}, /* h_crop_start low 12 */
- {OV8858_8BIT, 0x3802, 0x00}, /* v_crop_start high */
- {OV8858_8BIT, 0x3803, 0x0C}, /* v_crop_start low */
- {OV8858_8BIT, 0x3804, 0x0C}, /* h_crop_end high 3283 */
- {OV8858_8BIT, 0x3805, 0xD3}, /* h_crop_end low */
- {OV8858_8BIT, 0x3806, 0x09}, /* v_crop_end high */
- {OV8858_8BIT, 0x3807, 0xA3}, /* v_crop_end low */
- {OV8858_8BIT, 0x3808, 0x06}, /* h_output_size high 1640 x 1232 */
- {OV8858_8BIT, 0x3809, 0x68}, /* h_output_size low */
- {OV8858_8BIT, 0x380A, 0x04}, /* v_output_size high */
- {OV8858_8BIT, 0x380B, 0xD0}, /* v_output_size low */
- {OV8858_8BIT, 0x380C, 0x07}, /* horizontal timing size high */
- {OV8858_8BIT, 0x380D, 0x94}, /* horizontal timing size low */
- {OV8858_8BIT, 0x380E, 0x09}, /* vertical timing size high */
- {OV8858_8BIT, 0x380F, 0xAA}, /* vertical timing size low */
- {OV8858_8BIT, 0x3814, 0x03}, /* h_odd_inc */
- {OV8858_8BIT, 0x3815, 0x01}, /* h_even_inc */
- {OV8858_8BIT, 0x3820, 0x00}, /* format1 */
- {OV8858_8BIT, 0x3821, 0x67}, /* format2 */
- {OV8858_8BIT, 0x382A, 0x03}, /* v_odd_inc */
- {OV8858_8BIT, 0x382B, 0x01}, /* v_even_inc */
- {OV8858_8BIT, 0x3830, 0x08}, /* Unknown */
- {OV8858_8BIT, 0x3836, 0x02}, /* Unknown */
- {OV8858_8BIT, 0x3D85, 0x16}, /* OTP_REG85 */
- {OV8858_8BIT, 0x3F08, 0x08}, /* PSRAM control register */
- {OV8858_8BIT, 0x4034, 0x3F}, /* Unknown */
- {OV8858_8BIT, 0x403D, 0x04}, /* BLC CTRL3D */
- {OV8858_8BIT, 0x4600, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x4601, 0xCB}, /* Unknown */
- {OV8858_8BIT, 0x4837, 0x14}, /* pclk_period = 0x14 */
- {OV8858_TOK_TERM, 0, 0}
-};
-
-static const struct ov8858_reg ov8858_1640x1096[] = {
- {OV8858_8BIT, 0x0100, 0x00}, /* software_standby */
- {OV8858_8BIT, 0x3778, 0x16}, /* Unknown */
- {OV8858_8BIT, 0x3800, 0x00}, /* h_crop_start high */
- {OV8858_8BIT, 0x3801, 0x0C}, /* h_crop_start low 12 */
- {OV8858_8BIT, 0x3802, 0x00}, /* v_crop_start high */
- {OV8858_8BIT, 0x3803, 0x0C}, /* v_crop_start low */
- {OV8858_8BIT, 0x3804, 0x0C}, /* h_crop_end high 3283 */
- {OV8858_8BIT, 0x3805, 0xD3}, /* h_crop_end low */
- {OV8858_8BIT, 0x3806, 0x09}, /* v_crop_end high */
- {OV8858_8BIT, 0x3807, 0xA3}, /* v_crop_end low */
- {OV8858_8BIT, 0x3808, 0x06}, /* h_output_size high 1640 x 1096 */
- {OV8858_8BIT, 0x3809, 0x68}, /* h_output_size low */
- {OV8858_8BIT, 0x380A, 0x04}, /* v_output_size high */
- {OV8858_8BIT, 0x380B, 0x48}, /* v_output_size low */
- {OV8858_8BIT, 0x380C, 0x07}, /* horizontal timing size high */
- {OV8858_8BIT, 0x380D, 0x94}, /* horizontal timing size low */
- {OV8858_8BIT, 0x380E, 0x09}, /* vertical timing size high */
- {OV8858_8BIT, 0x380F, 0xAA}, /* vertical timing size low */
- {OV8858_8BIT, 0x3814, 0x03}, /* h_odd_inc */
- {OV8858_8BIT, 0x3815, 0x01}, /* h_even_inc */
- {OV8858_8BIT, 0x3820, 0x00}, /* format1 */
- {OV8858_8BIT, 0x3821, 0x67}, /* format2 */
- {OV8858_8BIT, 0x382A, 0x03}, /* v_odd_inc */
- {OV8858_8BIT, 0x382B, 0x01}, /* v_even_inc */
- {OV8858_8BIT, 0x3830, 0x08}, /* Unknown */
- {OV8858_8BIT, 0x3836, 0x02}, /* Unknown */
- {OV8858_8BIT, 0x3D85, 0x16}, /* OTP_REG85 */
- {OV8858_8BIT, 0x3F08, 0x08}, /* PSRAM control register */
- {OV8858_8BIT, 0x4034, 0x3F}, /* Unknown */
- {OV8858_8BIT, 0x403D, 0x04}, /* BLC CTRL3D */
- {OV8858_8BIT, 0x4600, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x4601, 0xCB}, /* Unknown */
- {OV8858_8BIT, 0x4837, 0x14}, /* pclk_period = 0x14 */
- {OV8858_TOK_TERM, 0, 0}
-};
-
-
-static const struct ov8858_reg ov8858_1640x926[] = {
- {OV8858_8BIT, 0x0100, 0x00}, /* software_standby */
- {OV8858_8BIT, 0x3778, 0x16}, /* Unknown */
- {OV8858_8BIT, 0x3800, 0x00}, /* h_crop_start high */
- {OV8858_8BIT, 0x3801, 0x0C}, /* h_crop_start low */
- {OV8858_8BIT, 0x3802, 0x00}, /* v_crop_start high */
- {OV8858_8BIT, 0x3803, 0x0C}, /* v_crop_start low */
- {OV8858_8BIT, 0x3804, 0x0C}, /* h_crop_end high */
- {OV8858_8BIT, 0x3805, 0xD3}, /* h_crop_end low */
- {OV8858_8BIT, 0x3806, 0x09}, /* v_crop_end high */
- {OV8858_8BIT, 0x3807, 0xA3}, /* v_crop_end low */
- {OV8858_8BIT, 0x3808, 0x06}, /* h_output_size high 1640 x 926 */
- {OV8858_8BIT, 0x3809, 0x68}, /* h_output_size low */
- {OV8858_8BIT, 0x380A, 0x03}, /* v_output_size high */
- {OV8858_8BIT, 0x380B, 0x9E}, /* v_output_size low */
- {OV8858_8BIT, 0x380C, 0x07}, /* horizontal timing size high */
- {OV8858_8BIT, 0x380D, 0x94}, /* horizontal timing size low */
- {OV8858_8BIT, 0x380E, 0x09}, /* vertical timing size high */
- {OV8858_8BIT, 0x380F, 0xAA}, /* vertical timing size low */
- {OV8858_8BIT, 0x3814, 0x03}, /* h_odd_inc */
- {OV8858_8BIT, 0x3815, 0x01}, /* h_even_inc */
- {OV8858_8BIT, 0x3820, 0x00}, /* format1 */
- {OV8858_8BIT, 0x3821, 0x67}, /* format2 */
- {OV8858_8BIT, 0x382A, 0x03}, /* v_odd_inc */
- {OV8858_8BIT, 0x382B, 0x01}, /* v_even_inc */
- {OV8858_8BIT, 0x3830, 0x08}, /* Unknown */
- {OV8858_8BIT, 0x3836, 0x02}, /* Unknown */
- {OV8858_8BIT, 0x3D85, 0x16}, /* OTP_REG85 */
- {OV8858_8BIT, 0x3F08, 0x08}, /* PSRAM control register */
- {OV8858_8BIT, 0x4034, 0x3F}, /* Unknown */
- {OV8858_8BIT, 0x403D, 0x04}, /* BLC CTRL3D */
- {OV8858_8BIT, 0x4600, 0x00}, /* Unknown */
- {OV8858_8BIT, 0x4601, 0xCB}, /* Unknown */
- {OV8858_8BIT, 0x4837, 0x14}, /* pclk_period = 0x14 */
- {OV8858_TOK_TERM, 0, 0}
-};
-
-static struct ov8858_resolution ov8858_res_preview[] = {
- {
- .desc = "ov8858_1640x926_PREVIEW",
- .width = 1640,
- .height = 926,
- .used = 0,
- .regs = ov8858_1640x926,
- .bin_factor_x = 0,
- .bin_factor_y = 0,
- .skip_frames = 0,
- .fps_options = {
- {
- .fps = 30,
- .pixels_per_line = 3880,
- .lines_per_frame = 2573,
- },
- {
- }
- },
- },
- {
- .desc = "ov8858_1640x1232_PREVIEW",
- .width = 1640,
- .height = 1232,
- .used = 0,
- .regs = ov8858_1640x1232,
- .bin_factor_x = 0,
- .bin_factor_y = 0,
- .skip_frames = 0,
- .fps_options = {
- {
- .fps = 30,
- .pixels_per_line = 3880,
- .lines_per_frame = 2573,
- },
- {
- }
- },
- },
- {
- .desc = "ov8858_1936x1096_PREVIEW",
- .width = 1936,
- .height = 1096,
- .used = 0,
- .regs = ov8858_1080P_30,
- .bin_factor_x = 0,
- .bin_factor_y = 0,
- .skip_frames = 0,
- .fps_options = {
- {
- .fps = 30,
- .pixels_per_line = 3880,
- .lines_per_frame = 2573,
- },
- {
- }
- },
- },
- {
- .desc = "ov8858_3276x1848_PREVIEW",
- .width = 3276,
- .height = 1848,
- .used = 0,
- .regs = ov8858_3276x1848,
- .bin_factor_x = 0,
- .bin_factor_y = 0,
- .skip_frames = 0,
- .fps_options = {
- {
- .fps = 30,
- .pixels_per_line = 3880,
- .lines_per_frame = 2573,
- },
- {
- }
- },
- },
- {
- .desc = "ov8858_8M_PREVIEW",
- .width = 3280,
- .height = 2464,
- .used = 0,
- .regs = ov8858_8M,
- .bin_factor_x = 0,
- .bin_factor_y = 0,
- .skip_frames = 0,
- .fps_options = {
- {
- .fps = 30,
- .pixels_per_line = 3880,
- .lines_per_frame = 2573,
- },
- {
- }
- },
- },
-};
-
-static struct ov8858_resolution ov8858_res_still[] = {
- {
- .desc = "ov8858_1640x1232_STILL",
- .width = 1640,
- .height = 1232,
- .used = 0,
- .regs = ov8858_1640x1232,
- .bin_factor_x = 0,
- .bin_factor_y = 0,
- .skip_frames = 0,
- .fps_options = {
- {
- .fps = 30,
- .pixels_per_line = 3880,
- .lines_per_frame = 2573,
- },
- {
- }
- },
- },
- {
- .desc = "ov8858_1640x926_STILL",
- .width = 1640,
- .height = 926,
- .used = 0,
- .regs = ov8858_1640x926,
- .bin_factor_x = 0,
- .bin_factor_y = 0,
- .skip_frames = 1,
- .fps_options = {
- {
- .fps = 30,
- .pixels_per_line = 3880,
- .lines_per_frame = 2573,
- },
- {
- }
- },
- },
- {
- .desc = "ov8858_3276X1848_STILL",
- .width = 3276,
- .height = 1848,
- .used = 0,
- .regs = ov8858_3276x1848,
- .bin_factor_x = 0,
- .bin_factor_y = 0,
- .skip_frames = 1,
- .fps_options = {
- {
- .fps = 30,
- .pixels_per_line = 3880,
- .lines_per_frame = 2573,
- },
- {
- }
- },
- },
- {
- .desc = "ov8858_8M_STILL",
- .width = 3280,
- .height = 2464,
- .used = 0,
- .regs = ov8858_8M,
- .bin_factor_x = 0,
- .bin_factor_y = 0,
- .skip_frames = 1,
- .fps_options = {
- {
- /* Pixel clock: 149.76MHZ */
- .fps = 10,
- .pixels_per_line = 3880,
- .lines_per_frame = 3859,
- },
- {
- }
- },
- },
-};
-
-static struct ov8858_resolution ov8858_res_video[] = {
- {
- .desc = "ov8858_1640x926_VIDEO",
- .width = 1640,
- .height = 926,
- .used = 0,
- .regs = ov8858_1640x926,
- .bin_factor_x = 0,
- .bin_factor_y = 0,
- .skip_frames = 1,
- .fps_options = {
- {
- .fps = 30,
- .pixels_per_line = 3880,
- .lines_per_frame = 2573,
- },
- {
- }
- },
- },
- {
- .desc = "ov8858_1640x1232_VIDEO",
- .width = 1640,
- .height = 1232,
- .used = 0,
- .regs = ov8858_1640x1232,
- .bin_factor_x = 0,
- .bin_factor_y = 0,
- .skip_frames = 1,
- .fps_options = {
- {
- .fps = 30,
- .pixels_per_line = 3880,
- .lines_per_frame = 2573,
- },
- {
- }
- },
- },
- {
- .desc = "ov8858_1640x1096_VIDEO",
- .width = 1640,
- .height = 1096,
- .used = 0,
- .regs = ov8858_1640x1096,
- .bin_factor_x = 0,
- .bin_factor_y = 0,
- .skip_frames = 1,
- .fps_options = {
- {
- .fps = 30,
- .pixels_per_line = 3880,
- .lines_per_frame = 2573,
- },
- {
- }
- },
- },
- {
- .desc = "ov8858_1080P_30_VIDEO",
- .width = 1936,
- .height = 1096,
- .used = 0,
- .regs = ov8858_1080P_30,
- .bin_factor_x = 0,
- .bin_factor_y = 0,
- .skip_frames = 1,
- .fps_options = {
- {
- .fps = 30,
- .pixels_per_line = 3880,
- .lines_per_frame = 2573,
- },
- {
- }
- },
- },
-};
-
-#endif /* __OV8858_H__ */
diff --git a/drivers/staging/media/atomisp/include/linux/atomisp_platform.h b/drivers/staging/media/atomisp/include/linux/atomisp_platform.h
index e0f0c379e7ce..aa5e294e7b7d 100644
--- a/drivers/staging/media/atomisp/include/linux/atomisp_platform.h
+++ b/drivers/staging/media/atomisp/include/linux/atomisp_platform.h
@@ -104,6 +104,10 @@ enum atomisp_input_format {
ATOMISP_INPUT_FORMAT_USER_DEF8, /* User defined 8-bit data type 8 */
};
+#define N_ATOMISP_INPUT_FORMAT (ATOMISP_INPUT_FORMAT_USER_DEF8 + 1)
+
+
+
enum intel_v4l2_subdev_type {
RAW_CAMERA = 1,
SOC_CAMERA = 2,
diff --git a/drivers/staging/media/atomisp/include/linux/vlv2_plat_clock.h b/drivers/staging/media/atomisp/include/linux/vlv2_plat_clock.h
deleted file mode 100644
index ed709bdd6498..000000000000
--- a/drivers/staging/media/atomisp/include/linux/vlv2_plat_clock.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * vlv2_plat_clock.h
- *
- * Copyright (C) 2013 Intel Corp
- * Author: Asutosh Pathak <asutosh.pathak@intel.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 of the License.
- *
- * 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.
- */
-
-#ifndef __VLV2_PLAT_CLOCK_H
-#define __VLV2_PLAT_CLOCK_H
-
-int vlv2_plat_set_clock_freq(int clock_num, int freq_type);
-int vlv2_plat_get_clock_freq(int clock_num);
-
-int vlv2_plat_configure_clock(int clock_num, u32 conf);
-int vlv2_plat_get_clock_status(int clock_num);
-
-#endif /* __VLV2_PLAT_CLOCK_H */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/Makefile b/drivers/staging/media/atomisp/pci/atomisp2/Makefile
index ac3805345f20..7fead5fc9a7d 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/Makefile
+++ b/drivers/staging/media/atomisp/pci/atomisp2/Makefile
@@ -59,17 +59,14 @@ atomisp-objs += \
css2400/isp/kernels/bnr/bnr_1.0/ia_css_bnr.host.o \
css2400/isp/kernels/bnr/bnr2_2/ia_css_bnr2_2.host.o \
css2400/isp/kernels/dpc2/ia_css_dpc2.host.o \
- css2400/isp/kernels/dpc2/ia_css_dpc2_default.host.o \
css2400/isp/kernels/fc/fc_1.0/ia_css_formats.host.o \
css2400/isp/kernels/ctc/ctc_1.0/ia_css_ctc.host.o \
css2400/isp/kernels/ctc/ctc_1.0/ia_css_ctc_table.host.o \
css2400/isp/kernels/ctc/ctc2/ia_css_ctc2.host.o \
css2400/isp/kernels/ctc/ctc1_5/ia_css_ctc1_5.host.o \
css2400/isp/kernels/bh/bh_2/ia_css_bh.host.o \
- css2400/isp/kernels/bnlm/ia_css_bnlm_default.host.o \
css2400/isp/kernels/bnlm/ia_css_bnlm.host.o \
css2400/isp/kernels/tdf/tdf_1.0/ia_css_tdf.host.o \
- css2400/isp/kernels/tdf/tdf_1.0/ia_css_tdf_default.host.o \
css2400/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.o \
css2400/isp/kernels/anr/anr_1.0/ia_css_anr.host.o \
css2400/isp/kernels/anr/anr_2/ia_css_anr2_table.host.o \
@@ -96,7 +93,6 @@ atomisp-objs += \
css2400/isp/kernels/ob/ob2/ia_css_ob2.host.o \
css2400/isp/kernels/iterator/iterator_1.0/ia_css_iterator.host.o \
css2400/isp/kernels/wb/wb_1.0/ia_css_wb.host.o \
- css2400/isp/kernels/eed1_8/ia_css_eed1_8_default.host.o \
css2400/isp/kernels/eed1_8/ia_css_eed1_8.host.o \
css2400/isp/kernels/sc/sc_1.0/ia_css_sc.host.o \
css2400/isp/kernels/ipu2_io_ls/bayer_io_ls/ia_css_bayer_io.host.o \
@@ -215,7 +211,6 @@ INCLUDES += \
-I$(atomisp)/css2400/isp/kernels/aa/aa_2/ \
-I$(atomisp)/css2400/isp/kernels/anr/anr_1.0/ \
-I$(atomisp)/css2400/isp/kernels/anr/anr_2/ \
- -I$(atomisp)/css2400/isp/kernels/bayer_ls/bayer_ls_1.0/ \
-I$(atomisp)/css2400/isp/kernels/bh/bh_2/ \
-I$(atomisp)/css2400/isp/kernels/bnlm/ \
-I$(atomisp)/css2400/isp/kernels/bnr/ \
@@ -258,14 +253,10 @@ INCLUDES += \
-I$(atomisp)/css2400/isp/kernels/io_ls/ \
-I$(atomisp)/css2400/isp/kernels/io_ls/bayer_io_ls/ \
-I$(atomisp)/css2400/isp/kernels/io_ls/common/ \
- -I$(atomisp)/css2400/isp/kernels/io_ls/plane_io_ls/ \
- -I$(atomisp)/css2400/isp/kernels/io_ls/yuv420_io_ls/ \
-I$(atomisp)/css2400/isp/kernels/io_ls/yuv444_io_ls/ \
-I$(atomisp)/css2400/isp/kernels/ipu2_io_ls/ \
-I$(atomisp)/css2400/isp/kernels/ipu2_io_ls/bayer_io_ls/ \
-I$(atomisp)/css2400/isp/kernels/ipu2_io_ls/common/ \
- -I$(atomisp)/css2400/isp/kernels/ipu2_io_ls/plane_io_ls/ \
- -I$(atomisp)/css2400/isp/kernels/ipu2_io_ls/yuv420_io_ls/ \
-I$(atomisp)/css2400/isp/kernels/ipu2_io_ls/yuv444_io_ls/ \
-I$(atomisp)/css2400/isp/kernels/iterator/ \
-I$(atomisp)/css2400/isp/kernels/iterator/iterator_1.0/ \
@@ -289,9 +280,6 @@ INCLUDES += \
-I$(atomisp)/css2400/isp/kernels/ref/ref_1.0/ \
-I$(atomisp)/css2400/isp/kernels/s3a/ \
-I$(atomisp)/css2400/isp/kernels/s3a/s3a_1.0/ \
- -I$(atomisp)/css2400/isp/kernels/s3a_stat_ls/ \
- -I$(atomisp)/css2400/isp/kernels/scale/ \
- -I$(atomisp)/css2400/isp/kernels/scale/scale_1.0/ \
-I$(atomisp)/css2400/isp/kernels/sc/ \
-I$(atomisp)/css2400/isp/kernels/sc/sc_1.0/ \
-I$(atomisp)/css2400/isp/kernels/sdis/ \
@@ -315,8 +303,6 @@ INCLUDES += \
-I$(atomisp)/css2400/isp/kernels/ynr/ \
-I$(atomisp)/css2400/isp/kernels/ynr/ynr_1.0/ \
-I$(atomisp)/css2400/isp/kernels/ynr/ynr_2/ \
- -I$(atomisp)/css2400/isp/kernels/yuv_ls \
- -I$(atomisp)/css2400/isp/kernels/yuv_ls/yuv_ls_1.0/ \
-I$(atomisp)/css2400/isp/modes/interface/ \
-I$(atomisp)/css2400/runtime/binary/interface/ \
-I$(atomisp)/css2400/runtime/bufq/interface/ \
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_cmd.c
index debf0e3853ff..fa6ea506f8b1 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_cmd.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_cmd.c
@@ -437,7 +437,7 @@ static void atomisp_reset_event(struct atomisp_sub_device *asd)
}
-static void print_csi_rx_errors(enum ia_css_csi2_port port,
+static void print_csi_rx_errors(enum mipi_port_id port,
struct atomisp_device *isp)
{
u32 infos = 0;
@@ -481,7 +481,7 @@ static void clear_irq_reg(struct atomisp_device *isp)
}
static struct atomisp_sub_device *
-__get_asd_from_port(struct atomisp_device *isp, mipi_port_ID_t port)
+__get_asd_from_port(struct atomisp_device *isp, enum mipi_port_id port)
{
int i;
@@ -515,7 +515,7 @@ irqreturn_t atomisp_isr(int irq, void *dev)
spin_lock_irqsave(&isp->lock, flags);
if (isp->sw_contex.power_state != ATOM_ISP_POWER_UP ||
- isp->css_initialized == false) {
+ !isp->css_initialized) {
spin_unlock_irqrestore(&isp->lock, flags);
return IRQ_HANDLED;
}
@@ -570,9 +570,9 @@ irqreturn_t atomisp_isr(int irq, void *dev)
(irq_infos & CSS_IRQ_INFO_IF_ERROR)) {
/* handle mipi receiver error */
u32 rx_infos;
- enum ia_css_csi2_port port;
+ enum mipi_port_id port;
- for (port = IA_CSS_CSI2_PORT0; port <= IA_CSS_CSI2_PORT2;
+ for (port = MIPI_PORT0_ID; port <= MIPI_PORT2_ID;
port++) {
print_csi_rx_errors(port, isp);
atomisp_css_rx_get_irq_info(port, &rx_infos);
@@ -1850,7 +1850,7 @@ irqreturn_t atomisp_isr_thread(int irq, void *isp_ptr)
bool frame_done_found[MAX_STREAM_NUM] = {0};
bool css_pipe_done[MAX_STREAM_NUM] = {0};
unsigned int i;
- struct atomisp_sub_device *asd = &isp->asd[0];
+ struct atomisp_sub_device *asd;
dev_dbg(isp->dev, ">%s\n", __func__);
@@ -2091,7 +2091,7 @@ int atomisp_set_sensor_runmode(struct atomisp_sub_device *asd,
struct atomisp_device *isp = asd->isp;
struct v4l2_ctrl *c;
struct v4l2_streamparm p = {0};
- int ret;
+ int ret = 0;
int modes[] = { CI_MODE_NONE,
CI_MODE_VIDEO,
CI_MODE_STILL_CAPTURE,
@@ -2105,13 +2105,8 @@ int atomisp_set_sensor_runmode(struct atomisp_sub_device *asd,
c = v4l2_ctrl_find(isp->inputs[asd->input_curr].camera->ctrl_handler,
V4L2_CID_RUN_MODE);
- if (c) {
+ if (c)
ret = v4l2_ctrl_s_ctrl(c, runmode->mode);
- } else {
- p.parm.capture.capturemode = modes[runmode->mode];
- ret = v4l2_subdev_call(isp->inputs[asd->input_curr].camera,
- video, s_parm, &p);
- }
mutex_unlock(asd->ctrl_handler.lock);
return ret;
@@ -4608,7 +4603,7 @@ int atomisp_fixed_pattern(struct atomisp_sub_device *asd, int flag,
}
if (*value == 0) {
- asd->params.fpn_en = 0;
+ asd->params.fpn_en = false;
return 0;
}
@@ -5033,7 +5028,7 @@ atomisp_try_fmt_file(struct atomisp_device *isp, struct v4l2_format *f)
return 0;
}
-mipi_port_ID_t __get_mipi_port(struct atomisp_device *isp,
+enum mipi_port_id __get_mipi_port(struct atomisp_device *isp,
enum atomisp_camera_port port)
{
switch (port) {
@@ -5167,22 +5162,22 @@ static int __enable_continuous_mode(struct atomisp_sub_device *asd,
return atomisp_update_run_mode(asd);
}
-int configure_pp_input_nop(struct atomisp_sub_device *asd,
- unsigned int width, unsigned int height)
+static int configure_pp_input_nop(struct atomisp_sub_device *asd,
+ unsigned int width, unsigned int height)
{
return 0;
}
-int configure_output_nop(struct atomisp_sub_device *asd,
- unsigned int width, unsigned int height,
- unsigned int min_width,
- enum atomisp_css_frame_format sh_fmt)
+static int configure_output_nop(struct atomisp_sub_device *asd,
+ unsigned int width, unsigned int height,
+ unsigned int min_width,
+ enum atomisp_css_frame_format sh_fmt)
{
return 0;
}
-int get_frame_info_nop(struct atomisp_sub_device *asd,
- struct atomisp_css_frame_info *finfo)
+static int get_frame_info_nop(struct atomisp_sub_device *asd,
+ struct atomisp_css_frame_info *finfo)
{
return 0;
}
@@ -5529,7 +5524,7 @@ static void atomisp_get_dis_envelop(struct atomisp_sub_device *asd,
/* if subdev type is SOC camera,we do not need to set DVS */
if (isp->inputs[asd->input_curr].type == SOC_CAMERA)
- asd->params.video_dis_en = 0;
+ asd->params.video_dis_en = false;
if (asd->params.video_dis_en &&
asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) {
@@ -5629,7 +5624,7 @@ static int atomisp_set_fmt_to_snr(struct video_device *vdev,
ffmt = req_ffmt;
dev_warn(isp->dev,
"can not enable video dis due to sensor limitation.");
- asd->params.video_dis_en = 0;
+ asd->params.video_dis_en = false;
}
}
dev_dbg(isp->dev, "sensor width: %d, height: %d\n",
@@ -5654,7 +5649,7 @@ static int atomisp_set_fmt_to_snr(struct video_device *vdev,
(ffmt->width < req_ffmt->width || ffmt->height < req_ffmt->height)) {
dev_warn(isp->dev,
"can not enable video dis due to sensor limitation.");
- asd->params.video_dis_en = 0;
+ asd->params.video_dis_en = false;
}
atomisp_subdev_set_ffmt(&asd->subdev, fh.pad,
@@ -6157,7 +6152,7 @@ int atomisp_set_shading_table(struct atomisp_sub_device *asd,
if (!user_shading_table->enable) {
atomisp_css_set_shading_table(asd, NULL);
- asd->params.sc_en = 0;
+ asd->params.sc_en = false;
return 0;
}
@@ -6195,7 +6190,7 @@ int atomisp_set_shading_table(struct atomisp_sub_device *asd,
free_table = asd->params.css_param.shading_table;
asd->params.css_param.shading_table = shading_table;
atomisp_css_set_shading_table(asd, shading_table);
- asd->params.sc_en = 1;
+ asd->params.sc_en = true;
out:
if (free_table != NULL)
@@ -6632,7 +6627,7 @@ int atomisp_inject_a_fake_event(struct atomisp_sub_device *asd, int *event)
return 0;
}
-int atomisp_get_pipe_id(struct atomisp_video_pipe *pipe)
+static int atomisp_get_pipe_id(struct atomisp_video_pipe *pipe)
{
struct atomisp_sub_device *asd = pipe->asd;
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_cmd.h b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_cmd.h
index bdc73862fb79..79d493dba403 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_cmd.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_cmd.h
@@ -389,7 +389,7 @@ int atomisp_source_pad_to_stream_id(struct atomisp_sub_device *asd,
*/
void atomisp_eof_event(struct atomisp_sub_device *asd, uint8_t exp_id);
-mipi_port_ID_t __get_mipi_port(struct atomisp_device *isp,
+enum mipi_port_id __get_mipi_port(struct atomisp_device *isp,
enum atomisp_camera_port port);
bool atomisp_is_vf_pipe(struct atomisp_video_pipe *pipe);
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_compat.h b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_compat.h
index 3ef850cd25bd..6c829d0a1e4c 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_compat.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_compat.h
@@ -148,10 +148,10 @@ void atomisp_css_init_struct(struct atomisp_sub_device *asd);
int atomisp_css_irq_translate(struct atomisp_device *isp,
unsigned int *infos);
-void atomisp_css_rx_get_irq_info(enum ia_css_csi2_port port,
+void atomisp_css_rx_get_irq_info(enum mipi_port_id port,
unsigned int *infos);
-void atomisp_css_rx_clear_irq_info(enum ia_css_csi2_port port,
+void atomisp_css_rx_clear_irq_info(enum mipi_port_id port,
unsigned int infos);
int atomisp_css_irq_enable(struct atomisp_device *isp,
@@ -182,8 +182,6 @@ void atomisp_css_mmu_invalidate_cache(void);
void atomisp_css_mmu_invalidate_tlb(void);
-void atomisp_css_mmu_set_page_table_base_index(unsigned long base_index);
-
int atomisp_css_start(struct atomisp_sub_device *asd,
enum atomisp_css_pipe_id pipe_id, bool in_reset);
@@ -255,7 +253,7 @@ void atomisp_css_isys_set_valid(struct atomisp_sub_device *asd,
void atomisp_css_isys_set_format(struct atomisp_sub_device *asd,
enum atomisp_input_stream_id stream_id,
- enum atomisp_css_stream_format format,
+ enum atomisp_input_format format,
int isys_stream);
int atomisp_css_set_default_isys_config(struct atomisp_sub_device *asd,
@@ -264,18 +262,18 @@ int atomisp_css_set_default_isys_config(struct atomisp_sub_device *asd,
int atomisp_css_isys_two_stream_cfg(struct atomisp_sub_device *asd,
enum atomisp_input_stream_id stream_id,
- enum atomisp_css_stream_format input_format);
+ enum atomisp_input_format input_format);
void atomisp_css_isys_two_stream_cfg_update_stream1(
struct atomisp_sub_device *asd,
enum atomisp_input_stream_id stream_id,
- enum atomisp_css_stream_format input_format,
+ enum atomisp_input_format input_format,
unsigned int width, unsigned int height);
void atomisp_css_isys_two_stream_cfg_update_stream2(
struct atomisp_sub_device *asd,
enum atomisp_input_stream_id stream_id,
- enum atomisp_css_stream_format input_format,
+ enum atomisp_input_format input_format,
unsigned int width, unsigned int height);
int atomisp_css_input_set_resolution(struct atomisp_sub_device *asd,
@@ -292,7 +290,7 @@ void atomisp_css_input_set_bayer_order(struct atomisp_sub_device *asd,
void atomisp_css_input_set_format(struct atomisp_sub_device *asd,
enum atomisp_input_stream_id stream_id,
- enum atomisp_css_stream_format format);
+ enum atomisp_input_format format);
int atomisp_css_input_set_effective_resolution(
struct atomisp_sub_device *asd,
@@ -334,11 +332,11 @@ void atomisp_css_enable_cvf(struct atomisp_sub_device *asd,
bool enable);
int atomisp_css_input_configure_port(struct atomisp_sub_device *asd,
- mipi_port_ID_t port,
+ enum mipi_port_id port,
unsigned int num_lanes,
unsigned int timeout,
unsigned int mipi_freq,
- enum atomisp_css_stream_format metadata_format,
+ enum atomisp_input_format metadata_format,
unsigned int metadata_width,
unsigned int metadata_height);
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_compat_css20.c b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_compat_css20.c
index b7f9da014641..f668c68dc33a 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_compat_css20.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_compat_css20.c
@@ -88,7 +88,7 @@ unsigned int atomisp_css_debug_get_dtrace_level(void)
return ia_css_debug_trace_level;
}
-void atomisp_css2_hw_store_8(hrt_address addr, uint8_t data)
+static void atomisp_css2_hw_store_8(hrt_address addr, uint8_t data)
{
unsigned long flags;
@@ -126,7 +126,7 @@ static uint8_t atomisp_css2_hw_load_8(hrt_address addr)
return ret;
}
-uint16_t atomisp_css2_hw_load_16(hrt_address addr)
+static uint16_t atomisp_css2_hw_load_16(hrt_address addr)
{
unsigned long flags;
uint16_t ret;
@@ -136,7 +136,8 @@ uint16_t atomisp_css2_hw_load_16(hrt_address addr)
spin_unlock_irqrestore(&mmio_lock, flags);
return ret;
}
-uint32_t atomisp_css2_hw_load_32(hrt_address addr)
+
+static uint32_t atomisp_css2_hw_load_32(hrt_address addr)
{
unsigned long flags;
uint32_t ret;
@@ -1019,7 +1020,7 @@ int atomisp_css_irq_translate(struct atomisp_device *isp,
return 0;
}
-void atomisp_css_rx_get_irq_info(enum ia_css_csi2_port port,
+void atomisp_css_rx_get_irq_info(enum mipi_port_id port,
unsigned int *infos)
{
#ifndef ISP2401_NEW_INPUT_SYSTEM
@@ -1029,7 +1030,7 @@ void atomisp_css_rx_get_irq_info(enum ia_css_csi2_port port,
#endif
}
-void atomisp_css_rx_clear_irq_info(enum ia_css_csi2_port port,
+void atomisp_css_rx_clear_irq_info(enum mipi_port_id port,
unsigned int infos)
{
#ifndef ISP2401_NEW_INPUT_SYSTEM
@@ -1159,31 +1160,6 @@ void atomisp_css_mmu_invalidate_tlb(void)
ia_css_mmu_invalidate_cache();
}
-void atomisp_css_mmu_set_page_table_base_index(unsigned long base_index)
-{
-}
-
-/*
- * Check whether currently running MIPI buffer size fulfill
- * the requirement of the stream to be run
- */
-bool __need_realloc_mipi_buffer(struct atomisp_device *isp)
-{
- unsigned int i;
-
- for (i = 0; i < isp->num_of_streams; i++) {
- struct atomisp_sub_device *asd = &isp->asd[i];
-
- if (asd->streaming !=
- ATOMISP_DEVICE_STREAMING_ENABLED)
- continue;
- if (asd->mipi_frame_size < isp->mipi_frame_size)
- return true;
- }
-
- return false;
-}
-
int atomisp_css_start(struct atomisp_sub_device *asd,
enum atomisp_css_pipe_id pipe_id, bool in_reset)
{
@@ -1808,7 +1784,7 @@ void atomisp_css_isys_set_valid(struct atomisp_sub_device *asd,
void atomisp_css_isys_set_format(struct atomisp_sub_device *asd,
enum atomisp_input_stream_id stream_id,
- enum atomisp_css_stream_format format,
+ enum atomisp_input_format format,
int isys_stream)
{
@@ -1820,7 +1796,7 @@ void atomisp_css_isys_set_format(struct atomisp_sub_device *asd,
void atomisp_css_input_set_format(struct atomisp_sub_device *asd,
enum atomisp_input_stream_id stream_id,
- enum atomisp_css_stream_format format)
+ enum atomisp_input_format format)
{
struct ia_css_stream_config *s_config =
@@ -1859,7 +1835,7 @@ int atomisp_css_set_default_isys_config(struct atomisp_sub_device *asd,
int atomisp_css_isys_two_stream_cfg(struct atomisp_sub_device *asd,
enum atomisp_input_stream_id stream_id,
- enum atomisp_css_stream_format input_format)
+ enum atomisp_input_format input_format)
{
struct ia_css_stream_config *s_config =
&asd->stream_env[stream_id].stream_config;
@@ -1873,9 +1849,9 @@ int atomisp_css_isys_two_stream_cfg(struct atomisp_sub_device *asd,
s_config->isys_config[IA_CSS_STREAM_ISYS_STREAM_1].linked_isys_stream_id
= IA_CSS_STREAM_ISYS_STREAM_0;
s_config->isys_config[IA_CSS_STREAM_ISYS_STREAM_0].format =
- IA_CSS_STREAM_FORMAT_USER_DEF1;
+ ATOMISP_INPUT_FORMAT_USER_DEF1;
s_config->isys_config[IA_CSS_STREAM_ISYS_STREAM_1].format =
- IA_CSS_STREAM_FORMAT_USER_DEF2;
+ ATOMISP_INPUT_FORMAT_USER_DEF2;
s_config->isys_config[IA_CSS_STREAM_ISYS_STREAM_1].valid = true;
return 0;
}
@@ -1883,7 +1859,7 @@ int atomisp_css_isys_two_stream_cfg(struct atomisp_sub_device *asd,
void atomisp_css_isys_two_stream_cfg_update_stream1(
struct atomisp_sub_device *asd,
enum atomisp_input_stream_id stream_id,
- enum atomisp_css_stream_format input_format,
+ enum atomisp_input_format input_format,
unsigned int width, unsigned int height)
{
struct ia_css_stream_config *s_config =
@@ -1901,7 +1877,7 @@ void atomisp_css_isys_two_stream_cfg_update_stream1(
void atomisp_css_isys_two_stream_cfg_update_stream2(
struct atomisp_sub_device *asd,
enum atomisp_input_stream_id stream_id,
- enum atomisp_css_stream_format input_format,
+ enum atomisp_input_format input_format,
unsigned int width, unsigned int height)
{
struct ia_css_stream_config *s_config =
@@ -2142,11 +2118,11 @@ void atomisp_css_enable_cvf(struct atomisp_sub_device *asd,
int atomisp_css_input_configure_port(
struct atomisp_sub_device *asd,
- mipi_port_ID_t port,
+ enum mipi_port_id port,
unsigned int num_lanes,
unsigned int timeout,
unsigned int mipi_freq,
- enum atomisp_css_stream_format metadata_format,
+ enum atomisp_input_format metadata_format,
unsigned int metadata_width,
unsigned int metadata_height)
{
@@ -2890,8 +2866,8 @@ stream_err:
return -EINVAL;
}
-unsigned int atomisp_get_pipe_index(struct atomisp_sub_device *asd,
- uint16_t source_pad)
+static unsigned int atomisp_get_pipe_index(struct atomisp_sub_device *asd,
+ uint16_t source_pad)
{
struct atomisp_device *isp = asd->isp;
/*
@@ -4502,7 +4478,7 @@ int atomisp_css_isr_thread(struct atomisp_device *isp,
{
enum atomisp_input_stream_id stream_id = 0;
struct atomisp_css_event current_event;
- struct atomisp_sub_device *asd = &isp->asd[0];
+ struct atomisp_sub_device *asd;
#ifndef ISP2401
bool reset_wdt_timer[MAX_STREAM_NUM] = {false};
#endif
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_compat_css20.h b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_compat_css20.h
index b03711668eda..a06c5b6e8027 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_compat_css20.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_compat_css20.h
@@ -37,7 +37,6 @@
#define atomisp_css_irq_info ia_css_irq_info
#define atomisp_css_isp_config ia_css_isp_config
#define atomisp_css_bayer_order ia_css_bayer_order
-#define atomisp_css_stream_format ia_css_stream_format
#define atomisp_css_capture_mode ia_css_capture_mode
#define atomisp_css_input_mode ia_css_input_mode
#define atomisp_css_frame ia_css_frame
@@ -117,7 +116,7 @@
*/
#define CSS_ID(val) (IA_ ## val)
#define CSS_EVENT(val) (IA_CSS_EVENT_TYPE_ ## val)
-#define CSS_FORMAT(val) (IA_CSS_STREAM_FORMAT_ ## val)
+#define CSS_FORMAT(val) (ATOMISP_INPUT_FORMAT_ ## val)
#define CSS_EVENT_PORT_EOF CSS_EVENT(PORT_EOF)
#define CSS_EVENT_FRAME_TAGGED CSS_EVENT(FRAME_TAGGED)
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_drvfs.c b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_drvfs.c
index ceedb82b6beb..a815c768bda9 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_drvfs.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_drvfs.c
@@ -22,6 +22,7 @@
#include "atomisp_compat.h"
#include "atomisp_internal.h"
#include "atomisp_ioctl.h"
+#include "atomisp_drvfs.h"
#include "hmm/hmm.h"
/*
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_file.c b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_file.c
index 377ec2a9fa6d..c6d96987561d 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_file.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_file.c
@@ -77,20 +77,6 @@ static int file_input_s_stream(struct v4l2_subdev *sd, int enable)
return 0;
}
-static int file_input_g_parm(struct v4l2_subdev *sd,
- struct v4l2_streamparm *param)
-{
- /*to fake*/
- return 0;
-}
-
-static int file_input_s_parm(struct v4l2_subdev *sd,
- struct v4l2_streamparm *param)
-{
- /*to fake*/
- return 0;
-}
-
static int file_input_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *format)
@@ -166,8 +152,6 @@ static int file_input_enum_frame_ival(struct v4l2_subdev *sd,
static const struct v4l2_subdev_video_ops file_input_video_ops = {
.s_stream = file_input_s_stream,
- .g_parm = file_input_g_parm,
- .s_parm = file_input_s_parm,
};
static const struct v4l2_subdev_core_ops file_input_core_ops = {
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_fops.c b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_fops.c
index 4f9f9dca5e6a..709137f25700 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_fops.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_fops.c
@@ -689,7 +689,7 @@ static void atomisp_dev_init_struct(struct atomisp_device *isp)
{
unsigned int i;
- isp->sw_contex.file_input = 0;
+ isp->sw_contex.file_input = false;
isp->need_gfx_throttle = true;
isp->isp_fatal_error = false;
isp->mipi_frame_size = 0;
@@ -708,12 +708,12 @@ static void atomisp_subdev_init_struct(struct atomisp_sub_device *asd)
v4l2_ctrl_s_ctrl(asd->run_mode, ATOMISP_RUN_MODE_STILL_CAPTURE);
memset(&asd->params.css_param, 0, sizeof(asd->params.css_param));
asd->params.color_effect = V4L2_COLORFX_NONE;
- asd->params.bad_pixel_en = 1;
- asd->params.gdc_cac_en = 0;
- asd->params.video_dis_en = 0;
- asd->params.sc_en = 0;
- asd->params.fpn_en = 0;
- asd->params.xnr_en = 0;
+ asd->params.bad_pixel_en = true;
+ asd->params.gdc_cac_en = false;
+ asd->params.video_dis_en = false;
+ asd->params.sc_en = false;
+ asd->params.fpn_en = false;
+ asd->params.xnr_en = false;
asd->params.false_color = 0;
asd->params.online_process = 1;
asd->params.yuv_ds_en = 0;
@@ -1279,7 +1279,10 @@ const struct v4l2_file_operations atomisp_fops = {
.mmap = atomisp_mmap,
.unlocked_ioctl = video_ioctl2,
#ifdef CONFIG_COMPAT
+ /*
+ * There are problems with this code. Disable this for now.
.compat_ioctl32 = atomisp_compat_ioctl32,
+ */
#endif
.poll = atomisp_poll,
};
@@ -1291,7 +1294,10 @@ const struct v4l2_file_operations atomisp_file_fops = {
.mmap = atomisp_file_mmap,
.unlocked_ioctl = video_ioctl2,
#ifdef CONFIG_COMPAT
+ /*
+ * There are problems with this code. Disable this for now.
.compat_ioctl32 = atomisp_compat_ioctl32,
+ */
#endif
.poll = atomisp_poll,
};
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_ioctl.c b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_ioctl.c
index 5c84dd63778e..61bd550dafb9 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_ioctl.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_ioctl.c
@@ -1607,10 +1607,12 @@ int atomisp_stream_on_master_slave_sensor(struct atomisp_device *isp,
/* FIXME! */
#ifndef ISP2401
-void __wdt_on_master_slave_sensor(struct atomisp_device *isp, unsigned int wdt_duration)
+static void __wdt_on_master_slave_sensor(struct atomisp_device *isp,
+ unsigned int wdt_duration)
#else
-void __wdt_on_master_slave_sensor(struct atomisp_video_pipe *pipe,
- unsigned int wdt_duration, bool enable)
+static void __wdt_on_master_slave_sensor(struct atomisp_video_pipe *pipe,
+ unsigned int wdt_duration,
+ bool enable)
#endif
{
#ifndef ISP2401
@@ -2731,7 +2733,7 @@ static int atomisp_s_parm_file(struct file *file, void *fh,
}
rt_mutex_lock(&isp->mutex);
- isp->sw_contex.file_input = 1;
+ isp->sw_contex.file_input = true;
rt_mutex_unlock(&isp->mutex);
return 0;
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_subdev.c b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_subdev.c
index f3e18d627b0a..49a9973b4289 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_subdev.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_subdev.c
@@ -42,17 +42,17 @@ const struct atomisp_in_fmt_conv atomisp_in_fmt_conv[] = {
{ MEDIA_BUS_FMT_SGBRG12_1X12, 12, 12, ATOMISP_INPUT_FORMAT_RAW_12, CSS_BAYER_ORDER_GBRG, CSS_FORMAT_RAW_12 },
{ MEDIA_BUS_FMT_SGRBG12_1X12, 12, 12, ATOMISP_INPUT_FORMAT_RAW_12, CSS_BAYER_ORDER_GRBG, CSS_FORMAT_RAW_12 },
{ MEDIA_BUS_FMT_SRGGB12_1X12, 12, 12, ATOMISP_INPUT_FORMAT_RAW_12, CSS_BAYER_ORDER_RGGB, CSS_FORMAT_RAW_12 },
- { MEDIA_BUS_FMT_UYVY8_1X16, 8, 8, ATOMISP_INPUT_FORMAT_YUV422_8, 0, IA_CSS_STREAM_FORMAT_YUV422_8 },
- { MEDIA_BUS_FMT_YUYV8_1X16, 8, 8, ATOMISP_INPUT_FORMAT_YUV422_8, 0, IA_CSS_STREAM_FORMAT_YUV422_8 },
- { MEDIA_BUS_FMT_JPEG_1X8, 8, 8, CSS_FRAME_FORMAT_BINARY_8, 0, IA_CSS_STREAM_FORMAT_BINARY_8 },
+ { MEDIA_BUS_FMT_UYVY8_1X16, 8, 8, ATOMISP_INPUT_FORMAT_YUV422_8, 0, ATOMISP_INPUT_FORMAT_YUV422_8 },
+ { MEDIA_BUS_FMT_YUYV8_1X16, 8, 8, ATOMISP_INPUT_FORMAT_YUV422_8, 0, ATOMISP_INPUT_FORMAT_YUV422_8 },
+ { MEDIA_BUS_FMT_JPEG_1X8, 8, 8, CSS_FRAME_FORMAT_BINARY_8, 0, ATOMISP_INPUT_FORMAT_BINARY_8 },
{ V4L2_MBUS_FMT_CUSTOM_NV12, 12, 12, CSS_FRAME_FORMAT_NV12, 0, CSS_FRAME_FORMAT_NV12 },
{ V4L2_MBUS_FMT_CUSTOM_NV21, 12, 12, CSS_FRAME_FORMAT_NV21, 0, CSS_FRAME_FORMAT_NV21 },
- { V4L2_MBUS_FMT_CUSTOM_YUV420, 12, 12, ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY, 0, IA_CSS_STREAM_FORMAT_YUV420_8_LEGACY },
+ { V4L2_MBUS_FMT_CUSTOM_YUV420, 12, 12, ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY, 0, ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY },
#if 0
- { V4L2_MBUS_FMT_CUSTOM_M10MO_RAW, 8, 8, CSS_FRAME_FORMAT_BINARY_8, 0, IA_CSS_STREAM_FORMAT_BINARY_8 },
+ { V4L2_MBUS_FMT_CUSTOM_M10MO_RAW, 8, 8, CSS_FRAME_FORMAT_BINARY_8, 0, ATOMISP_INPUT_FORMAT_BINARY_8 },
#endif
/* no valid V4L2 MBUS code for metadata format, so leave it 0. */
- { 0, 0, 0, ATOMISP_INPUT_FORMAT_EMBEDDED, 0, IA_CSS_STREAM_FORMAT_EMBEDDED },
+ { 0, 0, 0, ATOMISP_INPUT_FORMAT_EMBEDDED, 0, ATOMISP_INPUT_FORMAT_EMBEDDED },
{}
};
@@ -101,7 +101,7 @@ const struct atomisp_in_fmt_conv *atomisp_find_in_fmt_conv(u32 code)
}
const struct atomisp_in_fmt_conv *atomisp_find_in_fmt_conv_by_atomisp_in_fmt(
- enum atomisp_css_stream_format atomisp_in_fmt)
+ enum atomisp_input_format atomisp_in_fmt)
{
int i;
@@ -819,12 +819,6 @@ static int __atomisp_update_run_mode(struct atomisp_sub_device *asd)
struct atomisp_device *isp = asd->isp;
struct v4l2_ctrl *ctrl = asd->run_mode;
struct v4l2_ctrl *c;
- struct v4l2_streamparm p = {0};
- int modes[] = { CI_MODE_NONE,
- CI_MODE_VIDEO,
- CI_MODE_STILL_CAPTURE,
- CI_MODE_CONTINUOUS,
- CI_MODE_PREVIEW };
s32 mode;
if (ctrl->val != ATOMISP_RUN_MODE_VIDEO &&
@@ -840,11 +834,7 @@ static int __atomisp_update_run_mode(struct atomisp_sub_device *asd)
if (c)
return v4l2_ctrl_s_ctrl(c, mode);
- /* Fall back to obsolete s_parm */
- p.parm.capture.capturemode = modes[mode];
-
- return v4l2_subdev_call(
- isp->inputs[asd->input_curr].camera, video, s_parm, &p);
+ return 0;
}
int atomisp_update_run_mode(struct atomisp_sub_device *asd)
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_subdev.h b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_subdev.h
index c3eba675da06..59ff8723c182 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_subdev.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_subdev.h
@@ -58,9 +58,9 @@ struct atomisp_in_fmt_conv {
u32 code;
uint8_t bpp; /* bits per pixel */
uint8_t depth; /* uncompressed */
- enum atomisp_css_stream_format atomisp_in_fmt;
+ enum atomisp_input_format atomisp_in_fmt;
enum atomisp_css_bayer_order bayer_order;
- enum ia_css_stream_format css_stream_fmt;
+ enum atomisp_input_format css_stream_fmt;
};
struct atomisp_sub_device;
@@ -424,10 +424,10 @@ bool atomisp_subdev_is_compressed(u32 code);
const struct atomisp_in_fmt_conv *atomisp_find_in_fmt_conv(u32 code);
#ifndef ISP2401
const struct atomisp_in_fmt_conv *atomisp_find_in_fmt_conv_by_atomisp_in_fmt(
- enum atomisp_css_stream_format atomisp_in_fmt);
+ enum atomisp_input_format atomisp_in_fmt);
#else
const struct atomisp_in_fmt_conv
- *atomisp_find_in_fmt_conv_by_atomisp_in_fmt(enum atomisp_css_stream_format
+ *atomisp_find_in_fmt_conv_by_atomisp_in_fmt(enum atomisp_input_format
atomisp_in_fmt);
#endif
const struct atomisp_in_fmt_conv *atomisp_find_in_fmt_conv_compressed(u32 code);
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_tpg.c b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_tpg.c
index b71cc7bcdbab..adc900272f6f 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_tpg.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_tpg.c
@@ -27,18 +27,6 @@ static int tpg_s_stream(struct v4l2_subdev *sd, int enable)
return 0;
}
-static int tpg_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *param)
-{
- /*to fake*/
- return 0;
-}
-
-static int tpg_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *param)
-{
- /*to fake*/
- return 0;
-}
-
static int tpg_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *format)
@@ -101,8 +89,6 @@ static int tpg_enum_frame_ival(struct v4l2_subdev *sd,
static const struct v4l2_subdev_video_ops tpg_video_ops = {
.s_stream = tpg_s_stream,
- .g_parm = tpg_g_parm,
- .s_parm = tpg_s_parm,
};
static const struct v4l2_subdev_core_ops tpg_core_ops = {
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_v4l2.c
index 548e00e7d67b..ba20344ec560 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_v4l2.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_v4l2.c
@@ -1137,7 +1137,6 @@ static int init_atomisp_wdts(struct atomisp_device *isp)
for (i = 0; i < isp->num_of_streams; i++) {
struct atomisp_sub_device *asd = &isp->asd[i];
- asd = &isp->asd[i];
#ifndef ISP2401
timer_setup(&asd->wdt, atomisp_wdt, 0);
#else
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/camera/util/interface/ia_css_util.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/camera/util/interface/ia_css_util.h
index a8c27676a38b..5ab48f346790 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/camera/util/interface/ia_css_util.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/camera/util/interface/ia_css_util.h
@@ -116,7 +116,7 @@ extern bool ia_css_util_resolution_is_even(
*
*/
extern unsigned int ia_css_util_input_format_bpp(
- enum ia_css_stream_format stream_format,
+ enum atomisp_input_format stream_format,
bool two_ppc);
/* @brief check if input format it raw
@@ -126,7 +126,7 @@ extern unsigned int ia_css_util_input_format_bpp(
*
*/
extern bool ia_css_util_is_input_format_raw(
- enum ia_css_stream_format stream_format);
+ enum atomisp_input_format stream_format);
/* @brief check if input format it yuv
*
@@ -135,7 +135,7 @@ extern bool ia_css_util_is_input_format_raw(
*
*/
extern bool ia_css_util_is_input_format_yuv(
- enum ia_css_stream_format stream_format);
+ enum atomisp_input_format stream_format);
#endif /* __IA_CSS_UTIL_H__ */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/camera/util/src/util.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/camera/util/src/util.c
index 54193789a809..91e586112332 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/camera/util/src/util.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/camera/util/src/util.c
@@ -52,55 +52,55 @@ enum ia_css_err ia_css_convert_errno(
/* MW: Table look-up ??? */
unsigned int ia_css_util_input_format_bpp(
- enum ia_css_stream_format format,
+ enum atomisp_input_format format,
bool two_ppc)
{
unsigned int rval = 0;
switch (format) {
- case IA_CSS_STREAM_FORMAT_YUV420_8_LEGACY:
- case IA_CSS_STREAM_FORMAT_YUV420_8:
- case IA_CSS_STREAM_FORMAT_YUV422_8:
- case IA_CSS_STREAM_FORMAT_RGB_888:
- case IA_CSS_STREAM_FORMAT_RAW_8:
- case IA_CSS_STREAM_FORMAT_BINARY_8:
- case IA_CSS_STREAM_FORMAT_EMBEDDED:
+ case ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY:
+ case ATOMISP_INPUT_FORMAT_YUV420_8:
+ case ATOMISP_INPUT_FORMAT_YUV422_8:
+ case ATOMISP_INPUT_FORMAT_RGB_888:
+ case ATOMISP_INPUT_FORMAT_RAW_8:
+ case ATOMISP_INPUT_FORMAT_BINARY_8:
+ case ATOMISP_INPUT_FORMAT_EMBEDDED:
rval = 8;
break;
- case IA_CSS_STREAM_FORMAT_YUV420_10:
- case IA_CSS_STREAM_FORMAT_YUV422_10:
- case IA_CSS_STREAM_FORMAT_RAW_10:
+ case ATOMISP_INPUT_FORMAT_YUV420_10:
+ case ATOMISP_INPUT_FORMAT_YUV422_10:
+ case ATOMISP_INPUT_FORMAT_RAW_10:
rval = 10;
break;
- case IA_CSS_STREAM_FORMAT_YUV420_16:
- case IA_CSS_STREAM_FORMAT_YUV422_16:
+ case ATOMISP_INPUT_FORMAT_YUV420_16:
+ case ATOMISP_INPUT_FORMAT_YUV422_16:
rval = 16;
break;
- case IA_CSS_STREAM_FORMAT_RGB_444:
+ case ATOMISP_INPUT_FORMAT_RGB_444:
rval = 4;
break;
- case IA_CSS_STREAM_FORMAT_RGB_555:
+ case ATOMISP_INPUT_FORMAT_RGB_555:
rval = 5;
break;
- case IA_CSS_STREAM_FORMAT_RGB_565:
+ case ATOMISP_INPUT_FORMAT_RGB_565:
rval = 65;
break;
- case IA_CSS_STREAM_FORMAT_RGB_666:
- case IA_CSS_STREAM_FORMAT_RAW_6:
+ case ATOMISP_INPUT_FORMAT_RGB_666:
+ case ATOMISP_INPUT_FORMAT_RAW_6:
rval = 6;
break;
- case IA_CSS_STREAM_FORMAT_RAW_7:
+ case ATOMISP_INPUT_FORMAT_RAW_7:
rval = 7;
break;
- case IA_CSS_STREAM_FORMAT_RAW_12:
+ case ATOMISP_INPUT_FORMAT_RAW_12:
rval = 12;
break;
- case IA_CSS_STREAM_FORMAT_RAW_14:
+ case ATOMISP_INPUT_FORMAT_RAW_14:
if (two_ppc)
rval = 14;
else
rval = 12;
break;
- case IA_CSS_STREAM_FORMAT_RAW_16:
+ case ATOMISP_INPUT_FORMAT_RAW_16:
if (two_ppc)
rval = 16;
else
@@ -175,28 +175,28 @@ bool ia_css_util_resolution_is_even(const struct ia_css_resolution resolution)
}
#endif
-bool ia_css_util_is_input_format_raw(enum ia_css_stream_format format)
+bool ia_css_util_is_input_format_raw(enum atomisp_input_format format)
{
- return ((format == IA_CSS_STREAM_FORMAT_RAW_6) ||
- (format == IA_CSS_STREAM_FORMAT_RAW_7) ||
- (format == IA_CSS_STREAM_FORMAT_RAW_8) ||
- (format == IA_CSS_STREAM_FORMAT_RAW_10) ||
- (format == IA_CSS_STREAM_FORMAT_RAW_12));
+ return ((format == ATOMISP_INPUT_FORMAT_RAW_6) ||
+ (format == ATOMISP_INPUT_FORMAT_RAW_7) ||
+ (format == ATOMISP_INPUT_FORMAT_RAW_8) ||
+ (format == ATOMISP_INPUT_FORMAT_RAW_10) ||
+ (format == ATOMISP_INPUT_FORMAT_RAW_12));
/* raw_14 and raw_16 are not supported as input formats to the ISP.
* They can only be copied to a frame in memory using the
* copy binary.
*/
}
-bool ia_css_util_is_input_format_yuv(enum ia_css_stream_format format)
+bool ia_css_util_is_input_format_yuv(enum atomisp_input_format format)
{
- return format == IA_CSS_STREAM_FORMAT_YUV420_8_LEGACY ||
- format == IA_CSS_STREAM_FORMAT_YUV420_8 ||
- format == IA_CSS_STREAM_FORMAT_YUV420_10 ||
- format == IA_CSS_STREAM_FORMAT_YUV420_16 ||
- format == IA_CSS_STREAM_FORMAT_YUV422_8 ||
- format == IA_CSS_STREAM_FORMAT_YUV422_10 ||
- format == IA_CSS_STREAM_FORMAT_YUV422_16;
+ return format == ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY ||
+ format == ATOMISP_INPUT_FORMAT_YUV420_8 ||
+ format == ATOMISP_INPUT_FORMAT_YUV420_10 ||
+ format == ATOMISP_INPUT_FORMAT_YUV420_16 ||
+ format == ATOMISP_INPUT_FORMAT_YUV422_8 ||
+ format == ATOMISP_INPUT_FORMAT_YUV422_10 ||
+ format == ATOMISP_INPUT_FORMAT_YUV422_16;
}
enum ia_css_err ia_css_util_check_input(
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/css_2400_system/hrt/gp_regs_defs.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/css_2400_system/hrt/gp_regs_defs.h
deleted file mode 100644
index 34e734f6648e..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/css_2400_system/hrt/gp_regs_defs.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef _gp_regs_defs_h
-#define _gp_regs_defs_h
-
-#define _HRT_GP_REGS_IS_FWD_REG_IDX 0
-
-#define _HRT_GP_REGS_REG_ALIGN 4
-
-#endif /* _gp_regs_defs_h */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/css_2400_system/hrt/sp_hrt.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/css_2400_system/hrt/sp_hrt.h
deleted file mode 100644
index 7ee4deba519a..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/css_2400_system/hrt/sp_hrt.h
+++ /dev/null
@@ -1,24 +0,0 @@
-#ifndef ISP2401
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef _sp_hrt_h_
-#define _sp_hrt_h_
-
-#define hrt_sp_dmem(cell) HRT_PROC_TYPE_PROP(cell, _dmem)
-
-#define hrt_sp_dmem_master_port_address(cell) hrt_mem_master_port_address(cell, hrt_sp_dmem(cell))
-
-#endif /* _sp_hrt_h_ */
-#endif
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/css_2401_csi2p_system/hrt/gp_regs_defs.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/css_2401_csi2p_system/hrt/gp_regs_defs.h
deleted file mode 100644
index 34e734f6648e..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/css_2401_csi2p_system/hrt/gp_regs_defs.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef _gp_regs_defs_h
-#define _gp_regs_defs_h
-
-#define _HRT_GP_REGS_IS_FWD_REG_IDX 0
-
-#define _HRT_GP_REGS_REG_ALIGN 4
-
-#endif /* _gp_regs_defs_h */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/css_2401_csi2p_system/hrt/sp_hrt.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/css_2401_csi2p_system/hrt/sp_hrt.h
deleted file mode 100644
index 7ee4deba519a..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/css_2401_csi2p_system/hrt/sp_hrt.h
+++ /dev/null
@@ -1,24 +0,0 @@
-#ifndef ISP2401
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef _sp_hrt_h_
-#define _sp_hrt_h_
-
-#define hrt_sp_dmem(cell) HRT_PROC_TYPE_PROP(cell, _dmem)
-
-#define hrt_sp_dmem_master_port_address(cell) hrt_mem_master_port_address(cell, hrt_sp_dmem(cell))
-
-#endif /* _sp_hrt_h_ */
-#endif
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/css_2401_csi2p_system/system_global.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/css_2401_csi2p_system/system_global.h
index d2e3a2deea2e..7907f0ff6d6c 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/css_2401_csi2p_system/system_global.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/css_2401_csi2p_system/system_global.h
@@ -284,12 +284,12 @@ typedef enum {
N_RX_ID
} rx_ID_t;
-typedef enum {
+enum mipi_port_id {
MIPI_PORT0_ID = 0,
MIPI_PORT1_ID,
MIPI_PORT2_ID,
N_MIPI_PORT_ID
-} mipi_port_ID_t;
+};
#define N_RX_CHANNEL_ID 4
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/css_2401_system/hrt/gp_regs_defs.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/css_2401_system/hrt/gp_regs_defs.h
deleted file mode 100644
index 34e734f6648e..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/css_2401_system/hrt/gp_regs_defs.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef _gp_regs_defs_h
-#define _gp_regs_defs_h
-
-#define _HRT_GP_REGS_IS_FWD_REG_IDX 0
-
-#define _HRT_GP_REGS_REG_ALIGN 4
-
-#endif /* _gp_regs_defs_h */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/css_2401_system/hrt/sp_hrt.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/css_2401_system/hrt/sp_hrt.h
deleted file mode 100644
index 7ee4deba519a..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/css_2401_system/hrt/sp_hrt.h
+++ /dev/null
@@ -1,24 +0,0 @@
-#ifndef ISP2401
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef _sp_hrt_h_
-#define _sp_hrt_h_
-
-#define hrt_sp_dmem(cell) HRT_PROC_TYPE_PROP(cell, _dmem)
-
-#define hrt_sp_dmem_master_port_address(cell) hrt_mem_master_port_address(cell, hrt_sp_dmem(cell))
-
-#endif /* _sp_hrt_h_ */
-#endif
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/css_api_version.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/css_api_version.h
deleted file mode 100644
index efcd6e1679e8..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/css_api_version.h
+++ /dev/null
@@ -1,673 +0,0 @@
-/*
-#ifndef ISP2401
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-#else
-Support for Intel Camera Imaging ISP subsystem.
-Copyright (c) 2010 - 2015, Intel Corporation.
-#endif
-
-#ifdef ISP2401
-This program is free software; you can redistribute it and/or modify it
-under the terms and conditions 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.
-*/
-#endif
-#ifndef __CSS_API_VERSION_H
-#define __CSS_API_VERSION_H
-
-/* @file
- * CSS API version file. This file contains the version number of the CSS-API.
- *
- * This file is generated from a set of input files describing the CSS-API
- * changes. Don't edit this file directly.
- */
-
-
-/**
-
-The version string has four dot-separated numbers, read left to right:
- The first two are the API version, and should not be changed.
- The third number is incremented by a CSS firmware developer when the
- API change is not backwards compatible.
- The fourth number is incremented by the a CSS firmware developer for
- every API change.
- It should be zeroed when the third number changes.
-
-*/
-
-#ifndef ISP2401
-#define CSS_API_VERSION_STRING "2.1.15.3"
-#else
-#define CSS_API_VERSION_STRING "2.1.20.9"
-#endif
-
-/*
-Change log
-
-v2.0.1.0, initial version:
-- added API versioning
-
-v2.0.1.1, activate CSS-API versioning:
-- added description of major and minor version numbers
-
-v2.0.1.2, modified struct ia_css_frame_info:
-- added new member ia_css_crop_info
-
-v2.0.1.3, added IA_CSS_ERR_NOT_SUPPORTED
-
-v2.1.0.0
-- moved version number to 2.1.0.0
-- created new files for refactoring the code
-
-v2.1.1.0, modified struct ia_css_pipe_config and struct ia_css_pipe_info and struct ia_css_pipe:
-- use array to handle multiple output ports
-
-v2.1.1.1
-- added api to lock/unlock of RAW Buffers to Support HALv3 Feature
-
-v2.1.1.2, modified struct ia_css_stream_config:
-- to support multiple isys streams in one virtual channel, keep the old one for backward compatibility
-
-v2.1.2.0, modify ia_css_stream_config:
-- add isys_config and input_config to support multiple isys stream within one virtual channel
-
-v2.1.2.1, add IA_CSS_STREAM_FORMAT_NUM
-- add IA_CSS_STREAM_FORMAT_NUM definition to reflect the number of ia_css_stream_format enums
-
-v2.1.2.2, modified enum ia_css_stream_format
-- Add 16bit YUV formats to ia_css_stream_format enum:
-- IA_CSS_STREAM_FORMAT_YUV420_16 (directly after IA_CSS_STREAM_FORMAT_YUV420_10)
-- IA_CSS_STREAM_FORMAT_YUV422_16 (directly after IA_CSS_STREAM_FORMAT_YUV422_10)
-
-v2.1.2.3
-- added api to enable/disable digital zoom for capture pipe.
-
-v2.1.2.4, change CSS API to generate the shading table which should be directly sent to ISP:
-- keep the old CSS API (which uses the conversion of the shading table in CSS) for backward compatibility
-
-v2.1.2.5
-- Added SP frame time measurement (in ticks) and result is sent on a new member
-- in ia_css_buffer.h.
-
-v2.1.2.6, add function ia_css_check_firmware_version()
-- the function ia_css_check_firmware_version() returns true when the firmware version matches and returns false otherwise.
-
-v2.1.2.7
-- rename dynamic_data_index to dynamic_queue_id in struct ia_css_frame.
-- update IA_CSS_PIPE_MODE_NUM
-
-v2.1.2.8
-- added flag for video full range
-
-v2.1.2.9
-- add public parameters for xnr3 kernel
-
-v2.1.2.10
-- add new interface to enable output mirroring
-
-v2.1.2.11, MIPI buffers optimization
-- modified struct ia_css_mipi_buffer_config, added number of MIPI buffers needed for the stream
-- backwards compatible, need another patch to remove legacy function and code
-
-v2.1.2.12
-- create consolidated firmware package for 2400, 2401, csi2p, bxtpoc
-
-v2.1.3.0
-- rename ia_css_output_config.enable_mirror
-- add new interface to enable vertical output flipping
-
-v2.1.3.1
-- deprecated ia_css_rx_get_irq_info and ia_css_rx_clear_irq_info because both are hardcoded to work on CSI port 1.
-- added new functions ia_css_rx_port_get_irq_info and ia_css_rx_port_clear_irq_info, both have a port ID as extra argument.
-
-v2.1.3.2
-- reverted v2.1.3.0 change
-
-v2.1.3.3
-- Added isys event queue.
-- Renamed ia_css_dequeue_event to ia_css_dequeue_psys_event
-- Made ia_css_dequeue_event deprecated
-
-v2.1.3.4
-- added new interface to support ACC extension QoS feature.
-- added IA_CSS_EVENT_TYPE_ACC_STAGE_COMPLETE.
-
-v2.1.3.5
-- added tiled frame format IA_CSS_FRAME_FORMAT_NV12_TILEY
-
-v2.1.3.6
-- added functions ia_css_host_data_allocate and ia_css_host_data_free
-
-v2.1.4.0, default pipe config change
-- disable enable_dz param by default
-
-v2.1.5.0
-- removed mix_range field from yuvp1_y_ee_nr_frng_public_config
-
-v2.1.5.1, exposure IDs per stream
-- added MIN/MAX exposure ID macros
-- made exposure ID sequence per-stream instead of global (across all streams)
-
-#ifdef ISP2401
-v2.1.5.1, Add parameters to mmgr routines via a macro.
-- Replaced mmgr funtions with macros to add caller func name + line #.
-- This is done to help debug memory access issues, allocation issues, etc.
-
-#endif
-v2.1.6.0, Interface for vertical output flip
-- add new interface to enable vertical output flipping
-- rename ia_css_output_config.enable_mirror
-
-#ifndef ISP2401
-v2.1.6.1, Effective res on pipe
-#else
-v2.1.6.2 (2 changes parallel), Effective res on pipe
-#endif
-- Added input_effective_res to struct ia_css_pipe_config in ia_css_pipe_public.h.
-
-#ifndef ISP2401
-v2.1.6.2, CSS-API version file generated from individual changes
-#else
-v2.1.6.3 (2 changes parallel), CSS-API version file generated from individual changes
-#endif
-- Avoid merge-conflicts by generating version file from individual CSS-API changes.
-- Parallel CSS-API changes can map to the same version number after this change.
-- Version numbers for a change could increase due to parallel changes being merged.
-- The version number would not decrease for a change.
-
-#ifndef ISP2401
-v2.1.6.5 (2 changes parallel), Add SP FW error event
-#else
-v2.1.6.6 (4 changes parallel), Add SP FW error event
-#endif
-- Added FW error event. This gets raised when the SP FW runs into an
-- error situation from which it cannot recover.
-
-#ifndef ISP2401
-v2.1.6.5 (2 changes parallel), expose bnr FF enable bits in bnr public API
-#else
-v2.1.6.6 (4 changes parallel), expose bnr FF enable bits in bnr public API
-#endif
-- Added ff enable bits to bnr_public_config_dn_detect_ctrl_config_t struct
-
-#ifndef ISP2401
-v2.1.6.5 (2 changes parallel), ISP configuration per pipe
-#else
-v2.1.6.6 (4 changes parallel), ISP configuration per pipe
-#endif
-- Added ISP configuration per pipe support: p_isp_config field in
-- struct ia_css_pipe_config and ia_css_pipe_set_isp_config_on_pipe
-- and ia_css_pipe_set_isp_config functions
-
-#ifndef ISP2401
-v2.1.7.0, removed css_version.h
-#else
-v2.1.7.0 (2 changes parallel), removed css_version.h
-#endif
-- Removed css_version.h that was used for versioning in manual (non-CI) releases.
-
-#ifndef ISP2401
-v2.1.7.1, Add helpers (get and set) for ISP cfg per pipe
-#else
-v2.1.7.2 (2 changes parallel), Add helpers (get and set) for ISP cfg per pipe
-#endif
-- Add helpers (get and set) for ISP configuration per pipe
-
-#ifndef ISP2401
-v2.1.7.2, Add feature to lock all RAW buffers
-#else
-v2.1.7.3 (2 changes parallel), Add feature to lock all RAW buffers
-#endif
-- This API change adds a boolean flag (lock_all) in the stream_config struct.
-- If this flag is set to true, then all frames will be locked if locking is
-- enabled. By default this flag is set to false.
-- When this flag is false, then only buffers that are sent to the preview pipe
-- will be locked. If continuous viewfinder is disabled, the flag should be set
-- to true.
-
-#ifndef ISP2401
-v2.1.8.0 (2 changes parallel), Various changes to support ACC configuration per pipe
-#else
-v2.1.8.0 (4 changes parallel), Various changes to support ACC configuration per pipe
-#endif
-- Add ia_css_pipe_get_isp_config()
-- Remove ia_css_pipe_set_isp_config_on_pipe (duplicated
-- by ia_css_pipe_set_isp_config)
-- Add isp configuration as parameter for
-- ia_css_pipe_set_isp_config
-- Remove ia_css_pipe_isp_config_set()
-- Remove ia_css_pipe_isp_config_get()
-
-#ifndef ISP2401
-v2.1.8.2 (2 changes parallel), Added member num_invalid_frames to ia_css_pipe_info structure.
-#else
-v2.1.8.3 (4 changes parallel), Added member num_invalid_frames to ia_css_pipe_info structure.
-#endif
-- Added member num_invalid_frames to ia_css_pipe_info structure.
-- This helps the driver make sure that the first valid output
-- frame goes into the first user-supplied output buffer.
-
-#ifndef ISP2401
-v2.1.8.4 (2 changes parallel), ISYS EOF timestamp for output buffers
-#else
-v2.1.8.5 (4 changes parallel), ISYS EOF timestamp for output buffers
-#endif
-- driver gets EOF timer to every out frame . ia_css_buffer modified to accomodate same.
-
-#ifndef ISP2401
-v2.1.8.4 (4 changes parallel), display_config
-#else
-v2.1.8.5 (6 changes parallel), display_config
-#endif
-- Added formats- and output config parameters for configuration of the (optional) display output.
-
-#ifndef ISP2401
-v2.1.8.4 (2 changes parallel), Adding zoom region parameters to CSS API
-#else
-v2.1.8.5 (4 changes parallel), Adding zoom region parameters to CSS API
-#endif
-- Adding ia_css_point and ia_css_region structures to css-api.
-- Adding zoom_region(type ia_css_region) parameter to ia_css_dz_config structure.
-- By using this user can do the zoom based on zoom region and
-- the center of the zoom region is not restricted at the center of the input frame.
-
-#ifndef ISP2401
-v2.1.8.6 (1 changes parallel), Add new ia_css_fw_warning type
-#else
-v2.1.8.7 (3 changes parallel), Add new ia_css_fw_warning type
-#endif
-- Add IA_CSS_FW_WARNING_TAG_EXP_ID_FAILED enum to ia_css_fw_warning type
-- Extend sp_warning() with exp_id parameter
-
-#ifndef ISP2401
-v2.1.8.6 (1 changes parallel), Add includes in GC, GC2 kernel interface files
-#else
-v2.1.8.7 (3 changes parallel), Add includes in GC, GC2 kernel interface files
-#endif
-- add ia_css_ctc_types.h includes in ia_css_gc_types.h and ia_css_gc2_types.h. Needed to get ia_css_vamem_type.
-
-#ifndef ISP2401
-v2.1.9.0 (1 changes parallel), Introduce sp assert event.
-#else
-v2.1.9.0 (3 changes parallel), Introduce sp assert event.
-#endif
-- Add IA_CSS_EVENT_TYPE_FW_ASSERT. The FW sends the event in case an assert goes off.
-
-#ifndef ISP2401
-v2.1.9.1 (1 changes parallel), Exclude driver part from ia_css_buffer.h as it is also used by SP
-#else
-v2.1.9.2 (3 changes parallel), Exclude driver part from ia_css_buffer.h as it is also used by SP
-#endif
-- Excluded driver part of the interface from SP/ISP code
-- Driver I/F is not affected
-
-#ifndef ISP2401
-v2.1.9.2, added IA_CSS_EVENT_TYPE_TIMER
-#else
-v2.1.9.3 (2 changes parallel), added IA_CSS_EVENT_TYPE_TIMER
-#endif
-- Added a new event called IA_CSS_EVENT_TYPE_TIMER
-
-#ifndef ISP2401
-v2.1.10.0 (4 changes parallel), Add a flag "enable_dpc" to "struct ia_css_pipe_config"
-#else
-v2.1.10.0 (6 changes parallel), Add a flag "enable_dpc" to "struct ia_css_pipe_config"
-#endif
-- Add a flag "enable_dpc" to "struct ia_css_pipe_config"
-
-#ifndef ISP2401
-v2.1.10.6 (6 changes parallel), change the pipe version type from integer to enum
-#else
-v2.1.10.8 (9 changes parallel), change the pipe version type from integer to enum
-#endif
-- add new enum to enumerate ISP pipe version
-- change the pipe version type in pipe_config from integer to enum
-
-#ifndef ISP2401
-v2.1.13.0 (8 changes parallel), Stop Support for Skycam B0
-#else
-v2.1.14.0 (12 changes parallel), Stop Support for Skycam B0
-#endif
-- Remove a few pre-processor defines for Skycam B0/C0 as support
-
-#ifndef ISP2401
-v2.1.14.0 (24 changes parallel), change the pipe version type from integer to enum
-#else
-v2.1.15.0 (28 changes parallel), change the pipe version type from integer to enum
-#endif
-- remove the temporary workaround for backward compatability
-
-#ifndef ISP2401
-v2.1.14.0 (13 changes parallel), expose_gamma_enable_option
-#else
-v2.1.15.0 (17 changes parallel), expose_gamma_enable_option
-#endif
-- added enable param to gamma_corr_public_config
-- added documentation to rgbpp_public.h
-
-#ifndef ISP2401
-v2.1.14.0 (12 changes parallel), Remove deprecated FW_ERROR event.
-#else
-v2.1.15.0 (16 changes parallel), Remove deprecated FW_ERROR event.
-#endif
-- Remove code for deprecated FW_ERROR event.
-
-#ifndef ISP2401
-v2.1.14.3 (5 changes parallel), fix IEFD's puclic API types
-#else
-v2.1.15.5 (8 changes parallel), fix IEFD's puclic API types
-#endif
-- fix IEFD public API members types: rad_cu6_x1,rad_cu_unsharp_x1 & unsharp_amount
-
-#ifndef ISP2401
-v2.1.14.3 (5 changes parallel), Add IA_CSS_FW_WARNING_FRAME_PARAM_MISMATCH
-#else
-v2.1.15.5 (8 changes parallel), Add IA_CSS_FW_WARNING_FRAME_PARAM_MISMATCH
-#endif
-- Add IA_CSS_FW_WARNING_FRAME_PARAM_MISMATCH enum to ia_css_fw_warning type
-
-#ifndef ISP2401
-v2.1.14.4 (5 changes parallel), new API getter functions for gdc in buffer information
-#else
-v2.1.15.8 (11 changes parallel), add_flag_to_disable_continous_viewfinder
-- add a new flag in stream_config to disable continuous viewfinder
-- in ZSL use case.
-
-v2.1.16.0 (8 changes parallel), revert ia_css_skc_dvs_statistics field size change
-- Reverted field size change, change was not ready for driver yet.
-
-v2.1.17.0 (7 changes parallel), change CSS API to fix the shading correction off-center issue
-- update the ia_css_shading_info structure in ia_css_types.h
-
-v2.1.17.0 (32 changes parallel), add_flag_to_disable_continous_viewfinder_part2
-- remove the old interfaces
-
-v2.1.17.4 (8 changes parallel), Added public interface for setting the scaler LUT.
-- Added the public struct to output system and modified the global config struct.
-
-v2.1.17.5 (7 changes parallel), Add parameters for new TNR3 component
-- Add new parameters for new TNR3 component
-
-v2.1.17.6 (9 changes parallel), Update skycam DPC_MAX_NUMBER_OF_DP
-- Automated tooling requires an API change request
-- This change changes the implementation of #define DPC_MAX_NUMBER_OF_DP
-- it now returns a different number
-
-v2.1.17.6 (8 changes parallel), Return an error when both DPC and BDS are enabled in a pipe config
-- Return an error when both DPC and BDS are enabled in a pipe config
-
-v2.1.17.6 (9 changes parallel), new API getter functions for gdc in buffer information
-#endif
-- ia_css_pipe_get_dvs_filter() added
-- ia_css_pipe_get_gdc_in_buffer_info() added
-
-#ifndef ISP2401
-v2.1.14.5 (8 changes parallel), Update CNR2 ineffective values
-#else
-v2.1.17.7 (12 changes parallel), Update CNR2 ineffective values
-#endif
-- Fixed Incorrect ineffective values listed in ia_css_cnr_config
-- Correct Ineffective value is 8191
-
-#ifndef ISP2401
-v2.1.14.5 (8 changes parallel), af_roi_api
-#else
-v2.1.17.7 (12 changes parallel), af_roi_api
-#endif
-- added a new function to set AF ROI ia_css_set_af_roi
-- added a new struct ia_css_s3a_roi_offset
-
-#ifndef ISP2401
-v2.1.14.5 (8 changes parallel), remove x_y_end_from_ae_and_awb
-#else
-v2.1.17.7 (12 changes parallel), Enlarge AF AWB_FR stats buffers
-- Enlarge AF and AWB_FR stats buffers to support max grid width per stripe as oppose to per frame
-
-v2.1.17.7 (12 changes parallel), remove x_y_end_from_ae_and_awb
-#endif
-- added a flag to prepare removal of x_end and y_end from ae grid public config
-- added a flag to prepare removal of x_end and y_end from awb grid public config
-
-#ifndef ISP2401
-v2.1.14.5 (4 changes parallel), Added public interface for setting the scaler LUT.
-- Added the public struct to output system and modified the global config struct.
-#else
-v2.1.17.8 (5 changes parallel)
-- added input_yuv , input_raw to ia_css_binary_info.enable
-- struct, these attributes were always there but not saved
-- in the binary_info struct
-#endif
-
-#ifndef ISP2401
-v2.1.14.6 (8 changes parallel), add_flag_to_disable_continous_viewfinder
-- add a new flag in stream_config to disable continuous viewfinder
-- in ZSL use case.
-#else
-v2.1.17.9 (6 changes parallel), cleanup_awb_ae_rgb_integration_flags
-- this change only cleans up an approved api CR see wikis below
-#endif
-
-#ifndef ISP2401
-v2.1.14.6 (8 changes parallel), Enlarge AF AWB_FR stats buffers
-- Enlarge AF and AWB_FR stats buffers to support max grid width per stripe as oppose to per frame
-#else
-v2.1.17.10 (6 changes parallel), output_system_input_resolution
-- adedd gdc_output_system_in_resolution to pipe config struct
-#endif
-
-#ifndef ISP2401
-v2.1.14.8 (6 changes parallel), pipe config option for vf output bci mode downscaling
-#else
-v2.1.17.10 (5 changes parallel), Per pipe DPC configuration is added to ia_css_isp_parameters
-- Per pipe DPC configuration is added to ia_css_isp_parameters
-
-v2.1.17.10 (10 changes parallel), pipe config option for vf output bci mode downscaling
-#endif
-- vf downscaling using yuv_scale binary.
-
-#ifndef ISP2401
-v2.1.14.10 (7 changes parallel), Add scale mode GDC V2 LUT to CSS API
-#else
-v2.1.17.12 (11 changes parallel), Add scale mode GDC V2 LUT to CSS API
-#endif
-- Allow client to set global LUT for gdc v2 (First step in this change. See wiki page for more details)
-
-#ifndef ISP2401
-v2.1.14.10 (8 changes parallel), Include added to type-support.h.
-#else
-v2.1.17.12 (12 changes parallel), Include added to type-support.h.
-#endif
-- Include of hive/cell_support.h was added to type-support.h, in order to
-- have access to define HAVE_STDINT.
-
-#ifndef ISP2401
-v2.1.14.11 (7 changes parallel), Pipe configuration to enable BLI mode downscaling for
-#else
-v2.1.17.13 (11 changes parallel), Pipe configuration to enable BLI mode downscaling for
-#endif
-- BLI mode downscaling for capture post-processing
-
-#ifndef ISP2401
-v2.1.14.14 (9 changes parallel), Fix copyright headers (no functional change)
-#else
-v2.1.17.15 (8 changes parallel), Add copyright headers to css files
-- Add copyright headers to css API files
-
-v2.1.17.15 (8 changes parallel), add copyright header to include files
-- add copyright header to include files
-
-v2.1.17.15 (8 changes parallel), add copyright header to isp files
-- add copyright header to isp files
-
-v2.1.17.15 (8 changes parallel), add copyright header to refactored code
-- add copyright header to refactored code
-- (base, camera, runtime directories)
-
-v2.1.17.16 (13 changes parallel), Fix copyright headers (no functional change)
-#endif
-- No functional change; only fixes copyright headers
-
-#ifndef ISP2401
-v2.1.14.14 (6 changes parallel), Remove continuous mode special case handling in ia_css_pipe_set_isp_config
-#else
-v2.1.17.16 (10 changes parallel), Remove continuous mode special case handling in ia_css_pipe_set_isp_config
-#endif
-- For continuous mode isp_config was being send to all pipes,
-- even though API ia_css_pipe_set_isp_config is for single pipe
-- Removed incorrect case
-
-#ifndef ISP2401
-v2.1.14.14 (6 changes parallel), DVS statistics grid produced by accelerator
-#else
-v2.1.17.16 (5 changes parallel), Added documentation to formats_config header file
-- Added description of ranges for full-range flag
-
-v2.1.17.16 (10 changes parallel), DVS statistics grid produced by accelerator
-#endif
-- Add DVS statistics produced by accelerator grid to pipe info
-- Add ia_css_pipe_has_dvs_stats function
-
-#ifndef ISP2401
-v2.1.14.15 (7 changes parallel), cont_remove_x_y_end_from_ae_and_awb
-#else
-v2.1.17.17 (5 changes parallel), Provide the CSS interface to select the luma only binaries
-- Add a flag "enable_luma_only" to "struct ia_css_pipe_config"
-
-v2.1.17.17 (11 changes parallel), cont_remove_x_y_end_from_ae_and_awb
-#endif
-- this patch doesn't introduce any new api change, it only fixes a recent
-- api merged change (#31938) , in order to have success CI i had to upload an api change request
-
-#ifndef ISP2401
-v2.1.14.17 (6 changes parallel), Add XNR3 blending strength to kernel interface
-- Added a blending strength field to the XNR3 kernel interface to add
-- support for blending.
-#else
-v2.1.17.17 (10 changes parallel), GAC state dump for debug
-- added ia_css_dump_gac_state function
-
-v2.1.17.18 (23 changes parallel), output_format_nv12_16
-- added new output fromat nv12_16
-#endif
-
-#ifndef ISP2401
-v2.1.14.18 (22 changes parallel), eliminate two_pixels_per_clock field
-#else
-v2.1.17.18 (4 changes parallel), Remove author details from SKC src code
-- remove author details from skc src code
-
-v2.1.17.19 (26 changes parallel), eliminate two_pixels_per_clock field
-#endif
-- remove obsolete field two_pixels_per_clock
-
-#ifndef ISP2401
-v2.1.14.19 (3 changes parallel), Fix copyright headers (no functional change)
-#else
-v2.1.17.20 (7 changes parallel), Fix copyright headers (no functional change)
-#endif
-- No functional change; only fixes copyright headers
-
-#ifndef ISP2401
-v2.1.14.21 (3 changes parallel), ia_css_skc_dvs_statistics field size change
-- ia_css_skc_dvs_statistics field size change
-#else
-v2.1.17.20 (11 changes parallel), Add XNR3 blending strength to kernel interface
-- Added a blending strength field to the XNR3 kernel interface to add
-- support for blending.
-#endif
-
-#ifndef ISP2401
-v2.1.15.0 (3 changes parallel), revert ia_css_skc_dvs_statistics field size change
-- Reverted field size change, change was not ready for driver yet.
-#else
-v2.1.17.21 (24 changes parallel), Add N_CSS_PRBS_IDS and N_CSS_TPG_IDS
-- Add N_CSS_PRBS_IDS to reflect the number of ia_css_prbs_id enum
-- Add N_CSS_TPG_IDS to reflect the number of ia_css_tpg_id enum
-#endif
-
-#ifndef ISP2401
-v2.1.15.2 (3 changes parallel), Return an error when both DPC and BDS are enabled in a pipe config
-- Return an error when both DPC and BDS are enabled in a pipe config
-#else
-v2.1.17.23 (8 changes parallel), ia_css_skc_dvs_statistics field size change
-- ia_css_skc_dvs_statistics field size change
-#endif
-
-#ifndef ISP2401
-v2.1.15.3 (2 changes parallel), Update skycam DPC_MAX_NUMBER_OF_DP
-- Automated tooling requires an API change request
-- This change changes the implementation of #define DPC_MAX_NUMBER_OF_DP
-- it now returns a different number
-#else
-v2.1.19.0 (6 changes parallel)
-- Added code to calculate input_res using the Windows specification of binning
-#endif
-
-#ifndef ISP2401
-v2.1.15.3 (18 changes parallel), output_format_nv12_16
-- added new output fromat nv12_16
-#else
-v2.1.20.0 (7 changes parallel), Add interface to select TNR enabled binaries
-- Add a bool "enable_tnr" to "struct ia_css_pipe_config"
-
-v2.1.20.0 (6 changes parallel), OSYS & GDC Debug dump function addition
-- add GDC state dump function
-- add OSYS state dump function
-
-v2.1.20.4 (7 changes parallel), Add ref_buf_select parameter for TNR3 to kernel interface
-- Added a ref_buf_select parameter to the TNR3 kernel interface to add
-- support for multiple reference buffers.
-
-v2.1.20.4 (6 changes parallel), DVS MAX grid dimensions to cover maximal resolution
-- rename DVS_TABLE_HEIGHT/WIDTH to MAX_DVS_COORDS_TABLE_HEIGHT/WIDTH
-- modify value of the above macros to cover max resolution
-
-v2.1.20.5 (54 changes parallel), add input feeder calculations getter
-- add input_feeder_config public struct
-- add get_input_feeder_config getter
-
-v2.1.20.5 (4 changes parallel), Enable runtime updating mapped args for QoS extension pipe
-- added ia_css_pipe_update_qos_ext_mapped_arg()
-
-v2.1.20.7 (77 changes parallel), Add parameters to CPU routines via a macro.
-- Replaced CPU memory allocation functions with macros to add caller func name + line number.
-- This is done to help debug memory access issues, allocation issues, etc.
-- Changed API: only ia_css_env.h
-
-v2.1.20.7 (2 changes parallel), Frame format override
-- Added a function call to the pipe interface for overriding
-- the frame format as set in the pipe.
-- This is an optional interface that can be used under
-- some strict conditions.
-
-v2.1.20.7 (2 changes parallel), Output_system_in_res Information
-- Output_system_in_res_info field added to pipe_info struct
-
-v2.1.20.8, Temprarily disable memory debug features for SVOS.
-- Temporary commented out the additions to allow SKC testing till root cause found
-- Changed files ia_css_env.h and sh_css.c.
-
-v2.1.20.9, Enable ISP 2.7 naming
-- Add IA_CSS_PIPE_VERSION_2_7 to enum ia_css_pipe_version
-- Add #define SH_CSS_ISP_PIPE_VERSION_2_7 4
-#endif
-
-*/
-
-#endif /*__CSS_API_VERSION_H*/
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/debug.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/debug.c
index c412810887b3..dcb9a3127cfe 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/debug.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/debug.c
@@ -29,7 +29,7 @@
hrt_address debug_buffer_address = (hrt_address)-1;
hrt_vaddress debug_buffer_ddr_address = (hrt_vaddress)-1;
/* The local copy */
-debug_data_t debug_data;
+static debug_data_t debug_data;
debug_data_t *debug_data_ptr = &debug_data;
void debug_buffer_init(const hrt_address addr)
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/gp_timer.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/gp_timer.c
index bcfd443f5202..b6b1344786b1 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/gp_timer.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/gp_timer.c
@@ -29,7 +29,7 @@ gp_timer_reg_load(uint32_t reg);
static void
gp_timer_reg_store(uint32_t reg, uint32_t value);
-uint32_t
+static uint32_t
gp_timer_reg_load(uint32_t reg)
{
return ia_css_device_load_uint32(
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/hive_isp_css_ddr_hrt_modified.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/hive_isp_css_ddr_hrt_modified.h
deleted file mode 100644
index 39785aa21459..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/hive_isp_css_ddr_hrt_modified.h
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2010-2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef _hive_isp_css_ddr_hrt_modified_h_
-#define _hive_isp_css_ddr_hrt_modified_h_
-
-#include <hmm_64/hmm.h>
-
-/* This function reads an image from DDR and stores it in the img_buf array
- that has been allocated by the caller.
- The specifics of how the pixels are stored into DDR by the DMA are taken
- into account (bits padded to a width of 256, depending on the number of
- elements per ddr word).
- The DMA specific parameters give to this function (elems_per_xword and sign_extend)
- should correspond to those given to the DMA engine.
- The address is a virtual address which will be translated to a physical address before
- data is loaded from or stored to that address.
-
- The return value is 0 in case of success and 1 in case of failure.
- */
-unsigned int
-hrt_isp_css_read_image_from_ddr(
- unsigned short *img_buf,
- unsigned int width,
- unsigned int height,
- unsigned int elems_per_xword,
- unsigned int sign_extend,
- hmm_ptr virt_addr);
-
-/* This function writes an image to DDR, keeping the same aspects into account as the read_image function
- above. */
-unsigned int
-hrt_isp_css_write_image_to_ddr(
- const unsigned short *img_buf,
- unsigned int width,
- unsigned int height,
- unsigned int elems_per_xword,
- unsigned int sign_extend,
- hmm_ptr virt_addr);
-
-/* return the size in bytes of an image (frame or plane). */
-unsigned int
-hrt_isp_css_sizeof_image_in_ddr(
- unsigned int width,
- unsigned int height,
- unsigned int bits_per_element);
-
-unsigned int
-hrt_isp_css_stride_of_image_in_ddr(
- unsigned int width,
- unsigned int bits_per_element);
-
-hmm_ptr
-hrt_isp_css_alloc_image_in_ddr(
- unsigned int width,
- unsigned int height,
- unsigned int elems_per_xword);
-
-hmm_ptr
-hrt_isp_css_calloc_image_in_ddr(
- unsigned int width,
- unsigned int height,
- unsigned int elems_per_xword);
-
-#ifndef HIVE_ISP_NO_GDC
-#include "gdc_v2_defs.h"
-
-hmm_ptr
-hrt_isp_css_alloc_gdc_lut_in_ddr(void);
-
-void
-hrt_isp_css_write_gdc_lut_to_ddr(
- short values[4][HRT_GDC_N],
- hmm_ptr virt_addr);
-#endif
-
-#ifdef _HIVE_ISP_CSS_FPGA_SYSTEM
-hmm_ptr
-hrt_isp_css_alloc_image_for_display(
- unsigned int width,
- unsigned int height,
- unsigned int elems_per_xword);
-
-hmm_ptr
-hrt_isp_css_calloc_image_for_display(
- unsigned int width,
- unsigned int height,
- unsigned int elems_per_xword);
-#endif
-
-/* New set of functions, these do not require the elems_per_xword, but use bits_per_element instead,
- this way the user does not need to know about the width of a DDR word. */
-unsigned int
-hrt_isp_css_read_unsigned(
- unsigned short *target,
- unsigned int width,
- unsigned int height,
- unsigned int source_bits_per_element,
- hmm_ptr source);
-
-unsigned int
-hrt_isp_css_read_signed(
- short *target,
- unsigned int width,
- unsigned int height,
- unsigned int source_bits_per_element,
- hmm_ptr source);
-
-unsigned int
-hrt_isp_css_write_unsigned(
- const unsigned short *source,
- unsigned int width,
- unsigned int height,
- unsigned int target_bits_per_element,
- hmm_ptr target);
-
-unsigned int
-hrt_isp_css_write_signed(
- const short *source,
- unsigned int width,
- unsigned int height,
- unsigned int target_bits_per_element,
- hmm_ptr target);
-
-hmm_ptr
-hrt_isp_css_alloc(
- unsigned int width,
- unsigned int height,
- unsigned int bits_per_element);
-
-hmm_ptr
-hrt_isp_css_calloc(
- unsigned int width,
- unsigned int height,
- unsigned int bits_per_element);
-
-#endif /* _hive_isp_css_ddr_hrt_modified_h_ */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/hive_isp_css_hrt_modified.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/hive_isp_css_hrt_modified.h
deleted file mode 100644
index 342553d10e08..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/hive_isp_css_hrt_modified.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2010-2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef _hive_isp_css_hrt_h
-#define _hive_isp_css_hrt_h
-
-#include "system_types.h"
-
-#include "hive_isp_css_host_ids_hrt.h"
-#include "hive_isp_css_defs.h"
-
-#ifdef HRT_ISP_CSS_CUSTOM_HOST
-#ifndef HRT_USE_VIR_ADDRS
-#define HRT_USE_VIR_ADDRS
-#endif
-/*#include "hive_isp_css_custom_host_hrt.h"*/
-#endif
-
-#include <gpio_block.h>
-#include <gp_regs.h>
-#include <gp_timer_hrt.h>
- #include <css_receiver_2400_hrt.h>
-// #include <isp2400_mamoiada_params.h>
-// #include <isp2400_support.h>
- /* insert idle signal clearing and setting around hrt_main */
- #if !defined(HRT_HW) || defined(HRT_ISP_CSS_INSERT_IDLE_SIGNAL)
- #define hrt_main _hrt_isp_css_main
- #endif
- #ifdef _HIVE_ISP_CSS_SPECMAN_SYSTEM
- #include "hive_isp_css_2400_specman_system.h"
- #else
-#if defined(IS_ISP_2400_MAMOIADA_SYSTEM)
- #include "hive_isp_css_2400_system.h"
-#elif defined(IS_ISP_2401_MAMOIADA_SYSTEM)
- #include "hive_isp_css_2401_system.h"
-#else
-#error "hive_isp_css_hrt_modified.h: SYSTEM must be one of {2400_MAMOIADA_SYSTEM, 2401_MAMOIADA_SYSTEM}"
-#endif
- #endif
-#include <sp_hrt.h>
-#include <input_system_hrt.h>
-#include <input_selector_hrt.h>
-#include <sig_monitor_hrt.h>
-
-#include "hive_isp_css_sdram_wakeup_hrt.h"
-#include "hive_isp_css_idle_signal_hrt.h"
-#include "hive_isp_css_sp_hrt.h"
-#include "hive_isp_css_isp_hrt.h"
-#include "hive_isp_css_streaming_to_mipi_hrt.h"
-#include "hive_isp_css_testbench_hrt.h"
-#include "hive_isp_css_streaming_monitors_hrt.h"
-#include "hive_isp_css_gp_regs_hrt.h"
-#if defined(IS_ISP_2400_MAMOIADA_SYSTEM)
-#include "hive_isp_css_irq_hrt.h"
-#elif defined(IS_ISP_2401_MAMOIADA_SYSTEM)
-#include "hive_isp_css_2401_irq_hrt.h"
-#else
-#error "hive_isp_css_hrt_modified.h: SYSTEM must be one of {2400_MAMOIADA_SYSTEM, 2401_MAMOIADA_SYSTEM}"
-#endif
-
-#include "hive_isp_css_stream_switch_hrt.h"
-
-#include "hive_isp_css_ddr_hrt_modified.h"
-#include "hive_isp_css_dma_set_hrt.h"
-
-#define HIVE_ISP_CSS_NO_STREAM_SWITCH 1
-
-#endif /* _hive_isp_css_hrt_h */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/input_formatter.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/input_formatter.c
index a8997e45738e..0e1ca995fb06 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/input_formatter.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/input_formatter.c
@@ -45,8 +45,9 @@ const uint8_t HIVE_IF_SWITCH_CODE[N_INPUT_FORMATTER_ID] = {
HIVE_INPUT_SWITCH_SELECT_STR_TO_MEM};
/* MW Should be part of system_global.h, where we have the main enumeration */
-const bool HIVE_IF_BIN_COPY[N_INPUT_FORMATTER_ID] = {
- false, false, false, true};
+static const bool HIVE_IF_BIN_COPY[N_INPUT_FORMATTER_ID] = {
+ false, false, false, true
+};
void input_formatter_rst(
const input_formatter_ID_t ID)
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/input_system.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/input_system.c
index bd6821e436b2..2515e162828f 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/input_system.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/input_system.c
@@ -29,7 +29,7 @@
#define ZERO (0x0)
#define ONE (1U)
-const ib_buffer_t IB_BUFFER_NULL = {0 ,0, 0 };
+static const ib_buffer_t IB_BUFFER_NULL = {0 ,0, 0 };
static input_system_error_t input_system_configure_channel(
const channel_cfg_t channel);
@@ -98,7 +98,7 @@ static inline void ctrl_unit_get_state(
static inline void mipi_port_get_state(
const rx_ID_t ID,
- const mipi_port_ID_t port_ID,
+ const enum mipi_port_id port_ID,
mipi_port_state_t *state);
static inline void rx_channel_get_state(
@@ -180,7 +180,7 @@ void receiver_get_state(
const rx_ID_t ID,
receiver_state_t *state)
{
- mipi_port_ID_t port_id;
+ enum mipi_port_id port_id;
unsigned int ch_id;
assert(ID < N_RX_ID);
@@ -209,7 +209,7 @@ void receiver_get_state(
state->raw16 = (uint16_t)receiver_reg_load(ID,
_HRT_CSS_RECEIVER_RAW16_REG_IDX);
- for (port_id = (mipi_port_ID_t)0; port_id < N_MIPI_PORT_ID; port_id++) {
+ for (port_id = (enum mipi_port_id)0; port_id < N_MIPI_PORT_ID; port_id++) {
mipi_port_get_state(ID, port_id,
&(state->mipi_port_state[port_id]));
}
@@ -305,7 +305,7 @@ void receiver_set_compression(
void receiver_port_enable(
const rx_ID_t ID,
- const mipi_port_ID_t port_ID,
+ const enum mipi_port_id port_ID,
const bool cnd)
{
hrt_data reg = receiver_port_reg_load(ID, port_ID,
@@ -324,7 +324,7 @@ void receiver_port_enable(
bool is_receiver_port_enabled(
const rx_ID_t ID,
- const mipi_port_ID_t port_ID)
+ const enum mipi_port_id port_ID)
{
hrt_data reg = receiver_port_reg_load(ID, port_ID,
_HRT_CSS_RECEIVER_DEVICE_READY_REG_IDX);
@@ -333,7 +333,7 @@ bool is_receiver_port_enabled(
void receiver_irq_enable(
const rx_ID_t ID,
- const mipi_port_ID_t port_ID,
+ const enum mipi_port_id port_ID,
const rx_irq_info_t irq_info)
{
receiver_port_reg_store(ID,
@@ -343,7 +343,7 @@ void receiver_irq_enable(
rx_irq_info_t receiver_get_irq_info(
const rx_ID_t ID,
- const mipi_port_ID_t port_ID)
+ const enum mipi_port_id port_ID)
{
return receiver_port_reg_load(ID,
port_ID, _HRT_CSS_RECEIVER_IRQ_STATUS_REG_IDX);
@@ -351,7 +351,7 @@ rx_irq_info_t receiver_get_irq_info(
void receiver_irq_clear(
const rx_ID_t ID,
- const mipi_port_ID_t port_ID,
+ const enum mipi_port_id port_ID,
const rx_irq_info_t irq_info)
{
receiver_port_reg_store(ID,
@@ -556,7 +556,7 @@ static inline void ctrl_unit_get_state(
static inline void mipi_port_get_state(
const rx_ID_t ID,
- const mipi_port_ID_t port_ID,
+ const enum mipi_port_id port_ID,
mipi_port_state_t *state)
{
int i;
@@ -644,12 +644,12 @@ static inline void rx_channel_get_state(
}
// MW: "2400" in the name is not good, but this is to avoid a naming conflict
-input_system_cfg2400_t config;
+static input_system_cfg2400_t config;
static void receiver_rst(
const rx_ID_t ID)
{
- mipi_port_ID_t port_id;
+ enum mipi_port_id port_id;
assert(ID < N_RX_ID);
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/input_system_local.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/input_system_local.h
index 3e8bd00082dc..bf9230fd08f2 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/input_system_local.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/input_system_local.h
@@ -353,7 +353,7 @@ typedef struct rx_cfg_s rx_cfg_t;
*/
struct rx_cfg_s {
rx_mode_t mode; /* The HW config */
- mipi_port_ID_t port; /* The port ID to apply the control on */
+ enum mipi_port_id port; /* The port ID to apply the control on */
unsigned int timeout;
unsigned int initcount;
unsigned int synccount;
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/input_system_private.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/input_system_private.h
index 118185eb86e9..48876bb08b70 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/input_system_private.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/host/input_system_private.h
@@ -63,7 +63,7 @@ STORAGE_CLASS_INPUT_SYSTEM_C hrt_data receiver_reg_load(
STORAGE_CLASS_INPUT_SYSTEM_C void receiver_port_reg_store(
const rx_ID_t ID,
- const mipi_port_ID_t port_ID,
+ const enum mipi_port_id port_ID,
const hrt_address reg,
const hrt_data value)
{
@@ -77,7 +77,7 @@ STORAGE_CLASS_INPUT_SYSTEM_C void receiver_port_reg_store(
STORAGE_CLASS_INPUT_SYSTEM_C hrt_data receiver_port_reg_load(
const rx_ID_t ID,
- const mipi_port_ID_t port_ID,
+ const enum mipi_port_id port_ID,
const hrt_address reg)
{
assert(ID < N_RX_ID);
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/input_formatter_global.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/input_formatter_global.h
index 5654d911db65..7558f4964313 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/input_formatter_global.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/input_formatter_global.h
@@ -107,22 +107,6 @@ struct input_formatter_cfg_s {
uint32_t block_no_reqs;
};
-#define DEFAULT_IF_CONFIG \
-{ \
- 0, /* start_line */\
- 0, /* start_column */\
- 0, /* left_padding */\
- 0, /* cropped_height */\
- 0, /* cropped_width */\
- 0, /* deinterleaving */\
- 0, /*.buf_vecs */\
- 0, /* buf_start_index */\
- 0, /* buf_increment */\
- 0, /* buf_eol_offset */\
- false, /* is_yuv420_format */\
- false /* block_no_reqs */\
-}
-
extern const hrt_address HIVE_IF_SRST_ADDRESS[N_INPUT_FORMATTER_ID];
extern const hrt_data HIVE_IF_SRST_MASK[N_INPUT_FORMATTER_ID];
extern const uint8_t HIVE_IF_SWITCH_CODE[N_INPUT_FORMATTER_ID];
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/resource_global.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/resource_global.h
deleted file mode 100644
index 01c915c033a9..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/resource_global.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __RESOURCE_GLOBAL_H_INCLUDED__
-#define __RESOURCE_GLOBAL_H_INCLUDED__
-
-#define IS_RESOURCE_VERSION_1
-
-typedef enum {
- DMA_CHANNEL_RESOURCE_TYPE,
- IRQ_CHANNEL_RESOURCE_TYPE,
- MEM_SECTION_RESOURCE_TYPE,
- N_RESOURCE_TYPE
-} resource_type_ID_t;
-
-typedef enum {
- PERMANENT_RESOURCE_RESERVATION,
- PERSISTENT_RESOURCE_RESERVATION,
- DEDICTATED_RESOURCE_RESERVATION,
- SHARED_RESOURCE_RESERVATION,
- N_RESOURCE_RESERVATION
-} resource_reservation_t;
-
-#endif /* __RESOURCE_GLOBAL_H_INCLUDED__ */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/system_global.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/system_global.h
index d803efd7400a..6f63962a54e8 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/system_global.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/system_global.h
@@ -266,12 +266,12 @@ typedef enum {
N_RX_ID
} rx_ID_t;
-typedef enum {
+enum mipi_port_id {
MIPI_PORT0_ID = 0,
MIPI_PORT1_ID,
MIPI_PORT2_ID,
N_MIPI_PORT_ID
-} mipi_port_ID_t;
+};
#define N_RX_CHANNEL_ID 4
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/xmem_global.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/xmem_global.h
deleted file mode 100644
index 1d3a43abe55d..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_common/xmem_global.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __XMEM_GLOBAL_H_INCLUDED__
-#define __XMEM_GLOBAL_H_INCLUDED__
-
-#include "isp.h"
-
-#endif /* __XMEM_GLOBAL_H_INCLUDED__ */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/bamem.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/bamem.h
deleted file mode 100644
index 6928965cf513..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/bamem.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __BAMEM_H_INCLUDED__
-#define __BAMEM_H_INCLUDED__
-
-/*
- * This file is included on every cell {SP,ISP,host} and on every system
- * that uses the BAMEM device. It defines the API to DLI bridge
- *
- * System and cell specific interfaces and inline code are included
- * conditionally through Makefile path settings.
- *
- * - . system and cell agnostic interfaces, constants and identifiers
- * - public: system agnostic, cell specific interfaces
- * - private: system dependent, cell specific interfaces & inline implementations
- * - global: system specific constants and identifiers
- * - local: system and cell specific constants and identifiers
- */
-
-
-#include "system_local.h"
-#include "bamem_local.h"
-
-#ifndef __INLINE_BAMEM__
-#define STORAGE_CLASS_BAMEM_H extern
-#define STORAGE_CLASS_BAMEM_C
-#include "bamem_public.h"
-#else /* __INLINE_BAMEM__ */
-#define STORAGE_CLASS_BAMEM_H static inline
-#define STORAGE_CLASS_BAMEM_C static inline
-#include "bamem_private.h"
-#endif /* __INLINE_BAMEM__ */
-
-#endif /* __BAMEM_H_INCLUDED__ */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/bbb_config.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/bbb_config.h
deleted file mode 100644
index 18bc5ef3d0bf..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/bbb_config.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __BBB_CONFIG_H_INCLUDED__
-#define __BBB_CONFIG_H_INCLUDED__
-/* This header contains BBB defines common to ISP and host */
-
-#define BFA_MAX_KWAY (49)
-#define BFA_RW_LUT_SIZE (7)
-
-#define SAD3x3_IN_SHIFT (2) /* input right shift value for SAD3x3 */
-#define SAD3x3_OUT_SHIFT (2) /* output right shift value for SAD3x3 */
-
-/* XCU and BMA related defines shared between host and ISP
- * also need to be moved here */
-#endif /* __BBB_CONFIG_H_INCLUDED__ */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/cpu_mem_support.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/cpu_mem_support.h
deleted file mode 100644
index 6d014fafb713..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/cpu_mem_support.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __CPU_MEM_SUPPORT_H_INCLUDED__
-#define __CPU_MEM_SUPPORT_H_INCLUDED__
-
-#if defined (__KERNEL__)
-#include <linux/string.h> /* memset */
-#else
-#include <string.h> /* memset */
-#endif
-
-#include "sh_css_internal.h" /* sh_css_malloc and sh_css_free */
-
-static inline void*
-ia_css_cpu_mem_alloc(unsigned int size)
-{
- return sh_css_malloc(size);
-}
-
-static inline void*
-ia_css_cpu_mem_copy(void* dst, const void* src, unsigned int size)
-{
- if(!src || !dst)
- return NULL;
-
- return memcpy(dst, src, size);
-}
-
-static inline void*
-ia_css_cpu_mem_set_zero(void* dst, unsigned int size)
-{
- if(!dst)
- return NULL;
-
- return memset(dst, 0, size);
-}
-
-static inline void
-ia_css_cpu_mem_free(void* ptr)
-{
- if(!ptr)
- return;
-
- sh_css_free(ptr);
-}
-
-#endif /* __CPU_MEM_SUPPORT_H_INCLUDED__ */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/input_system_public.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/input_system_public.h
index 1596757fe9ef..6e37ff0fe0f9 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/input_system_public.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/input_system_public.h
@@ -83,7 +83,7 @@ extern void receiver_set_compression(
*/
extern void receiver_port_enable(
const rx_ID_t ID,
- const mipi_port_ID_t port_ID,
+ const enum mipi_port_id port_ID,
const bool cnd);
/*! Flag if PORT[port_ID] of RECEIVER[ID] is enabled
@@ -95,7 +95,7 @@ extern void receiver_port_enable(
*/
extern bool is_receiver_port_enabled(
const rx_ID_t ID,
- const mipi_port_ID_t port_ID);
+ const enum mipi_port_id port_ID);
/*! Enable the IRQ channels of PORT[port_ID] of RECEIVER[ID]
@@ -107,7 +107,7 @@ extern bool is_receiver_port_enabled(
*/
extern void receiver_irq_enable(
const rx_ID_t ID,
- const mipi_port_ID_t port_ID,
+ const enum mipi_port_id port_ID,
const rx_irq_info_t irq_info);
/*! Return the IRQ status of PORT[port_ID] of RECEIVER[ID]
@@ -119,7 +119,7 @@ extern void receiver_irq_enable(
*/
extern rx_irq_info_t receiver_get_irq_info(
const rx_ID_t ID,
- const mipi_port_ID_t port_ID);
+ const enum mipi_port_id port_ID);
/*! Clear the IRQ status of PORT[port_ID] of RECEIVER[ID]
@@ -131,7 +131,7 @@ extern rx_irq_info_t receiver_get_irq_info(
*/
extern void receiver_irq_clear(
const rx_ID_t ID,
- const mipi_port_ID_t port_ID,
+ const enum mipi_port_id port_ID,
const rx_irq_info_t irq_info);
/*! Write to a control register of INPUT_SYSTEM[ID]
@@ -195,7 +195,7 @@ STORAGE_CLASS_INPUT_SYSTEM_H hrt_data receiver_reg_load(
*/
STORAGE_CLASS_INPUT_SYSTEM_H void receiver_port_reg_store(
const rx_ID_t ID,
- const mipi_port_ID_t port_ID,
+ const enum mipi_port_id port_ID,
const hrt_address reg,
const hrt_data value);
@@ -210,7 +210,7 @@ STORAGE_CLASS_INPUT_SYSTEM_H void receiver_port_reg_store(
*/
STORAGE_CLASS_INPUT_SYSTEM_H hrt_data receiver_port_reg_load(
const rx_ID_t ID,
- const mipi_port_ID_t port_ID,
+ const enum mipi_port_id port_ID,
const hrt_address reg);
/*! Write to a control register of SUB_SYSTEM[sub_ID] of INPUT_SYSTEM[ID]
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp2400_config.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp2400_config.h
deleted file mode 100644
index ab3391716c82..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp2400_config.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __ISP2400_CONFIG_H_INCLUDED__
-#define __ISP2400_CONFIG_H_INCLUDED__
-
-#define NUM_BITS 14
-#define NUM_SLICE_ELEMS 4
-#define ROUNDMODE ROUND_NEAREST_EVEN
-#define MAX_SHIFT_1W (NUM_BITS-1) /* Max number of bits a 1w input can be shifted */
-#define MAX_SHIFT_2W (2*NUM_BITS-1) /* Max number of bits a 2w input can be shifted */
-
-#endif /* __ISP2400_CONFIG_H_INCLUDED__ */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp2500_config.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp2500_config.h
deleted file mode 100644
index 4fae856f5a23..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp2500_config.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __ISP2500_CONFIG_H_INCLUDED__
-#define __ISP2500_CONFIG_H_INCLUDED__
-
-#define NUM_BITS 12
-#define NUM_SLICE_ELEMS 4
-#define ROUNDMODE ROUND_NEAREST_EVEN
-#define MAX_SHIFT_1W (NUM_BITS-1) /* Max number of bits a 1w input can be shifted */
-#define MAX_SHIFT_2W (2*NUM_BITS-1) /* Max number of bits a 2w input can be shifted */
-
-
-#define HAS_div_unit
-
-#define HAS_vec_sub
-
-#endif /* __ISP2500_CONFIG_H_INCLUDED__ */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp2600_config.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp2600_config.h
deleted file mode 100644
index 6086be8cb0d3..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp2600_config.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __ISP2600_CONFIG_H_INCLUDED__
-#define __ISP2600_CONFIG_H_INCLUDED__
-
-
-#define NUM_BITS 16
-
-
-#define NUM_SLICE_ELEMS 8
-#define ROUNDMODE ROUND_NEAREST_EVEN
-#define MAX_SHIFT_1W (NUM_BITS-1) /* Max number of bits a 1w input can be shifted */
-#define MAX_SHIFT_2W (2*NUM_BITS-1) /* Max number of bits a 2w input can be shifted */
-#define ISP_NWAY 32 /* Number of elements in a vector in ISP 2600 */
-
-#define HAS_div_unit
-#define HAS_1w_sqrt_u_unit
-#define HAS_2w_sqrt_u_unit
-
-#define HAS_vec_sub
-
-#endif /* __ISP2600_CONFIG_H_INCLUDED__ */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp2601_config.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp2601_config.h
deleted file mode 100644
index beceefa24ca0..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp2601_config.h
+++ /dev/null
@@ -1,70 +0,0 @@
-#ifndef ISP2401
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __ISP2601_CONFIG_H_INCLUDED__
-#define __ISP2601_CONFIG_H_INCLUDED__
-
-#define NUM_BITS 16
-#define ISP_VEC_ELEMBITS NUM_BITS
-#define ISP_NWAY 32
-#define NUM_SLICE_ELEMS 4
-#define ROUNDMODE ROUND_NEAREST_EVEN
-#define MAX_SHIFT_1W (NUM_BITS-1) /* Max number of bits a 1w input can be shifted */
-#define MAX_SHIFT_2W (2*NUM_BITS-1) /* Max number of bits a 2w input can be shifted */
-
-#define HAS_div_unit
-#define HAS_bfa_unit
-#define HAS_1w_sqrt_u_unit
-#define HAS_2w_sqrt_u_unit
-
-#define HAS_vec_sub
-
-/* Bit widths and element widths defined in HW implementation of BFA */
-#define BFA_THRESHOLD_BIT_CNT (8)
-#define BFA_THRESHOLD_MASK ((1<<BFA_THRESHOLD_BIT_CNT)-1)
-#define BFA_SW_BIT_CNT (7)
-#define BFA_SW_MASK ((1<<BFA_SW_BIT_CNT)-1)
-
-#define BFA_RW_BIT_CNT (7)
-#define BFA_RW_MASK ((1<<BFA_RW_BIT_CNT)-1)
-#define BFA_RW_SLOPE_BIT_POS (8)
-#define BFA_RW_SLOPE_BIT_SHIFT (5)
-
-#define BFA_RW_IDX_BIT_CNT (3)
-#define BFA_RW_FRAC_BIT_CNT (5)
-#define BFA_RW_LUT0_FRAC_START_BIT (0)
-#define BFA_RW_LUT0_FRAC_END_BIT (BFA_RW_LUT0_FRAC_START_BIT+BFA_RW_FRAC_BIT_CNT-1) /* 4 */
-#define BFA_RW_LUT1_FRAC_START_BIT (2)
-#define BFA_RW_LUT1_FRAC_END_BIT (BFA_RW_LUT1_FRAC_START_BIT+BFA_RW_FRAC_BIT_CNT-1) /* 6 */
-/* LUT IDX end bit computation, start+idx_bit_cnt-2, one -1 comes as we count
- * bits from 0, another -1 comes as we use 2 lut table, so idx_bit_cnt is one
- * bit more */
-#define BFA_RW_LUT0_IDX_START_BIT (BFA_RW_LUT0_FRAC_END_BIT+1) /* 5 */
-#define BFA_RW_LUT0_IDX_END_BIT (BFA_RW_LUT0_IDX_START_BIT+BFA_RW_IDX_BIT_CNT-2) /* 6 */
-#define BFA_RW_LUT1_IDX_START_BIT (BFA_RW_LUT1_FRAC_END_BIT + 1) /* 7 */
-#define BFA_RW_LUT1_IDX_END_BIT (BFA_RW_LUT1_IDX_START_BIT+BFA_RW_IDX_BIT_CNT-2) /* 8 */
-#define BFA_RW_LUT_THRESHOLD (1<<(BFA_RW_LUT1_IDX_END_BIT-1)) /* 0x80 : next bit after lut1 end is set */
-#define BFA_RW_LUT1_IDX_OFFSET ((1<<(BFA_RW_IDX_BIT_CNT-1))-1) /* 3 */
-
-#define BFA_CP_MASK (0xFFFFFF80)
-#define BFA_SUBABS_SHIFT (6)
-#define BFA_SUBABS_BIT_CNT (8)
-#define BFA_SUBABS_MAX ((1<<BFA_SUBABS_BIT_CNT)-1)
-#define BFA_SUBABSSAT_BIT_CNT (9)
-#define BFA_SUBABSSAT_MAX ((1<<BFA_SUBABSSAT_BIT_CNT)-1)
-#define BFA_WEIGHT_SHIFT (6)
-
-#endif /* __ISP2601_CONFIG_H_INCLUDED__ */
-#endif
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp_config.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp_config.h
deleted file mode 100644
index 80506f2419a8..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp_config.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __ISP_CONFIG_H_INCLUDED__
-#define __ISP_CONFIG_H_INCLUDED__
-
-#if defined(ISP2400) || defined(ISP2401)
-#include "isp2400_config.h"
-#else
-#error "Please define a core {ISP2400, ISP2401}"
-#endif
-
-#endif /* __ISP_CONFIG_H_INCLUDED__ */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp_op1w.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp_op1w.h
deleted file mode 100644
index 0d978e5911c0..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp_op1w.h
+++ /dev/null
@@ -1,844 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __ISP_OP1W_H_INCLUDED__
-#define __ISP_OP1W_H_INCLUDED__
-
-/*
- * This file is part of the Multi-precision vector operations exstension package.
- */
-
-/*
- * Single-precision vector operations
- */
-
-/*
- * Prerequisites:
- *
- */
-
-#ifdef INLINE_ISP_OP1W
-#define STORAGE_CLASS_ISP_OP1W_FUNC_H static inline
-#define STORAGE_CLASS_ISP_OP1W_DATA_H static inline_DATA
-#else /* INLINE_ISP_OP1W */
-#define STORAGE_CLASS_ISP_OP1W_FUNC_H extern
-#define STORAGE_CLASS_ISP_OP1W_DATA_H extern_DATA
-#endif /* INLINE_ISP_OP1W */
-
-/*
- * Single-precision data type specification
- */
-
-#include "isp_op1w_types.h"
-#include "isp_op2w_types.h" // for doubling operations.
-
-/*
- * Single-precision prototype specification
- */
-
-/* Arithmetic */
-
-/* @brief bitwise AND
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return bitwise and of both input arguments
- *
- * This function will calculate the bitwise and.
- * result = _a & _b
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_and(
- const tvector1w _a,
- const tvector1w _b);
-
-/* @brief bitwise OR
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return bitwise or of both input arguments
- *
- * This function will calculate the bitwise or.
- * result = _a | _b
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_or(
- const tvector1w _a,
- const tvector1w _b);
-
-/* @brief bitwise XOR
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return bitwise xor of both input arguments
- *
- * This function will calculate the bitwise xor.
- * result = _a ^ _b
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_xor(
- const tvector1w _a,
- const tvector1w _b);
-
-/* @brief bitwise inverse
- *
- * @param[in] _a first argument
- *
- * @return bitwise inverse of both input arguments
- *
- * This function will calculate the bitwise inverse.
- * result = ~_a
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_inv(
- const tvector1w _a);
-
-/* Additive */
-
-/* @brief addition
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return sum of both input arguments
- *
- * This function will calculate the sum of the input arguments.
- * in case of overflow it will wrap around.
- * result = _a + _b
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_add(
- const tvector1w _a,
- const tvector1w _b);
-
-/* @brief subtraction
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return _b subtracted from _a.
- *
- * This function will subtract _b from _a.
- * in case of overflow it will wrap around.
- * result = _a - _b
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_sub(
- const tvector1w _a,
- const tvector1w _b);
-
-/* @brief saturated addition
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return saturated sum of both input arguments
- *
- * This function will calculate the sum of the input arguments.
- * in case of overflow it will saturate.
- * result = CLIP(_a + _b, MIN_RANGE, MAX_RANGE);
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_addsat(
- const tvector1w _a,
- const tvector1w _b);
-
-/* @brief saturated subtraction
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return saturated subtraction of both input arguments
- *
- * This function will subtract _b from _a.
- * in case of overflow it will saturate.
- * result = CLIP(_a - _b, MIN_RANGE, MAX_RANGE);
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_subsat(
- const tvector1w _a,
- const tvector1w _b);
-
-#ifdef ISP2401
-/* @brief Unsigned saturated subtraction
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return saturated subtraction of both input arguments
- *
- * This function will subtract _b from _a.
- * in case of overflow it will saturate.
- * result = CLIP(_a - _b, 0, MAX_RANGE);
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w_unsigned OP_1w_subsat_u(
- const tvector1w_unsigned _a,
- const tvector1w_unsigned _b);
-
-#endif
-/* @brief subtraction with shift right and rounding
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return (a - b) >> 1
- *
- * This function subtracts _b from _a and right shifts
- * the result by 1 bit with rounding.
- * No overflow can occur.
- * result = (_a - _b) >> 1
- *
- * Note: This function will be deprecated due to
- * the naming confusion and it will be replaced
- * by "OP_1w_subhalfrnd".
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_subasr1(
- const tvector1w _a,
- const tvector1w _b);
-
-/* @brief Subtraction with shift right and rounding
- *
- * @param[in] _a first operand
- * @param[in] _b second operand
- *
- * @return (_a - _b) >> 1
- *
- * This function subtracts _b from _a and right shifts
- * the result by 1 bit with rounding.
- * No overflow can occur.
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_subhalfrnd(
- const tvector1w _a,
- const tvector1w _b);
-
-/* @brief Subtraction with shift right and no rounding
- *
- * @param[in] _a first operand
- * @param[in] _b second operand
- *
- * @return (_a - _b) >> 1
- *
- * This function subtracts _b from _a and right shifts
- * the result by 1 bit without rounding (i.e. truncation).
- * No overflow can occur.
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_subhalf(
- const tvector1w _a,
- const tvector1w _b);
-
-
-/* @brief saturated absolute value
- *
- * @param[in] _a input
- *
- * @return saturated absolute value of the input
- *
- * This function will calculate the saturated absolute value of the input.
- * in case of overflow it will saturate.
- * if (_a > 0) return _a;<br>
- * else return CLIP(-_a, MIN_RANGE, MAX_RANGE);<br>
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_abs(
- const tvector1w _a);
-
-/* @brief saturated absolute difference
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return sat(abs(a-b));
- *
- * This function will calculate the saturated absolute value
- * of the saturated difference of both inputs.
- * result = sat(abs(sat(_a - _b)));
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_subabssat(
- const tvector1w _a,
- const tvector1w _b);
-
-/* Multiplicative */
-
-/* @brief doubling multiply
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return product of _a and _b
- *
- * This function will calculate the product
- * of the input arguments and returns a double
- * precision result.
- * No overflow can occur.
- * result = _a * _b;
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector2w OP_1w_muld(
- const tvector1w _a,
- const tvector1w _b);
-
-/* @brief integer multiply
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return product of _a and _b
- *
- * This function will calculate the product
- * of the input arguments and returns the LSB
- * aligned single precision result.
- * In case of overflow it will wrap around.
- * result = _a * _b;
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_mul(
- const tvector1w _a,
- const tvector1w _b);
-
-/* @brief fractional saturating multiply
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return saturated product of _a and _b
- *
- * This function will calculate the fixed point
- * product of the input arguments
- * and returns a single precision result.
- * In case of overflow it will saturate.
- * FP_UNITY * FP_UNITY => FP_UNITY.
- * result = CLIP(_a * _b >> (NUM_BITS-1), MIN_RANGE, MAX_RANGE);
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_qmul(
- const tvector1w _a,
- const tvector1w _b);
-
-/* @brief fractional saturating multiply with rounding
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return product of _a and _b
- *
- * This function will calculate the fixed point
- * product of the input arguments
- * and returns a single precision result.
- * FP_UNITY * FP_UNITY => FP_UNITY.
- * Depending on the rounding mode of the core
- * it will round to nearest or to nearest even.
- * result = CLIP(_a * _b >> (NUM_BITS-1), MIN_RANGE, MAX_RANGE);
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_qrmul(
- const tvector1w _a,
- const tvector1w _b);
-
-/* Comparative */
-
-/* @brief equal
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return _a == _b
- *
- * This function will return true if both inputs
- * are equal, and false if not equal.
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tflags OP_1w_eq(
- const tvector1w _a,
- const tvector1w _b);
-
-/* @brief not equal
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return _a != _b
- *
- * This function will return false if both inputs
- * are equal, and true if not equal.
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tflags OP_1w_ne(
- const tvector1w _a,
- const tvector1w _b);
-
-/* @brief less or equal
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return _a <= _b
- *
- * This function will return true if _a is smaller
- * or equal than _b.
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tflags OP_1w_le(
- const tvector1w _a,
- const tvector1w _b);
-
-/* @brief less then
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return _a < _b
- *
- * This function will return true if _a is smaller
- * than _b.
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tflags OP_1w_lt(
- const tvector1w _a,
- const tvector1w _b);
-
-/* @brief greater or equal
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return _a >= _b
- *
- * This function will return true if _a is greater
- * or equal than _b.
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tflags OP_1w_ge(
- const tvector1w _a,
- const tvector1w _b);
-
-/* @brief greater than
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return _a > _b
- *
- * This function will return true if _a is greater
- * than _b.
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tflags OP_1w_gt(
- const tvector1w _a,
- const tvector1w _b);
-
-/* Shift */
-
-/* @brief aritmetic shift right
- *
- * @param[in] _a input
- * @param[in] _b shift amount
- *
- * @return _a >> _b
- *
- * This function will shift _a with _b bits to the right,
- * preserving the sign bit.
- * It asserts 0 <= _b <= MAX_SHIFT_1W.
- *
- * The operation count for this function assumes that
- * the shift amount is a cloned scalar input.
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_asr(
- const tvector1w _a,
- const tvector1w _b);
-
-/* @brief aritmetic shift right with rounding
- *
- * @param[in] _a input
- * @param[in] _b shift amount
- *
- * @return _a >> _b
- *
- * If _b < NUM_BITS, this function will shift _a with _b bits to the right,
- * preserving the sign bit, and depending on the rounding mode of the core
- * it will round to nearest or to nearest even.
- * If _b >= NUM_BITS, this function will return 0.
- * It asserts 0 <= _b <= MAX_SHIFT_1W.
- * The operation count for this function assumes that
- * the shift amount is a cloned scalar input.
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_asrrnd(
- const tvector1w _a,
- const tvector1w _b);
-
-/* @brief saturating arithmetic shift left
- *
- * @param[in] _a input
- * @param[in] _b shift amount
- *
- * @return _a << _b
- *
- * If _b < MAX_BITDEPTH, this function will shift _a with _b bits to the left,
- * saturating at MIN_RANGE/MAX_RANGE in case of overflow.
- * If _b >= MAX_BITDEPTH, this function will return MIN_RANGE if _a < 0,
- * MAX_RANGE if _a > 0, 0 if _a == 0.
- * (with MAX_BITDEPTH=64)
- * It asserts 0 <= _b <= MAX_SHIFT_1W.
- * The operation count for this function assumes that
- * the shift amount is a cloned scalar input.
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_asl(
- const tvector1w _a,
- const tvector1w _b);
-
-/* @brief saturating aritmetic shift left
- *
- * @param[in] _a input
- * @param[in] _b shift amount
- *
- * @return _a << _b
- *
- * This function is identical to OP_1w_asl( )
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_aslsat(
- const tvector1w _a,
- const tvector1w _b);
-
-/* @brief logical shift left
- *
- * @param[in] _a input
- * @param[in] _b shift amount
- *
- * @return _a << _b
- *
- * This function will shift _a with _b bits to the left.
- * It will insert zeroes on the right.
- * It asserts 0 <= _b <= MAX_SHIFT_1W.
- * The operation count for this function assumes that
- * the shift amount is a cloned scalar input.
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_lsl(
- const tvector1w _a,
- const tvector1w _b);
-
-/* @brief logical shift right
- *
- * @param[in] _a input
- * @param[in] _b shift amount
- *
- * @return _a >> _b
- *
- * This function will shift _a with _b bits to the right.
- * It will insert zeroes on the left.
- * It asserts 0 <= _b <= MAX_SHIFT_1W.
- * The operation count for this function assumes that
- * the shift amount is a cloned scalar input.
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_lsr(
- const tvector1w _a,
- const tvector1w _b);
-
-#ifdef ISP2401
-/* @brief bidirectional saturating arithmetic shift
- *
- * @param[in] _a input
- * @param[in] _b shift amount
- *
- * @return _a << |_b| if _b is positive
- * _a >> |_b| if _b is negative
- *
- * If _b > 0, this function will shift _a with _b bits to the left,
- * saturating at MIN_RANGE/MAX_RANGE in case of overflow.
- * if _b < 0, this function will shift _a with _b bits to the right.
- * It asserts -MAX_SHIFT_1W <= _b <= MAX_SHIFT_1W.
- * If _b = 0, it returns _a.
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_ashift_sat(
- const tvector1w _a,
- const tvector1w _b);
-
-/* @brief bidirectional non-saturating arithmetic shift
- *
- * @param[in] _a input
- * @param[in] _b shift amount
- *
- * @return _a << |_b| if _b is positive
- * _a >> |_b| if _b is negative
- *
- * If _b > 0, this function will shift _a with _b bits to the left,
- * no saturation is performed in case of overflow.
- * if _b < 0, this function will shift _a with _b bits to the right.
- * It asserts -MAX_SHIFT_1W <= _b <= MAX_SHIFT_1W.
- * If _b = 0, it returns _a.
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_ashift(
- const tvector1w _a,
- const tvector1w _b);
-
-
-/* @brief bidirectional logical shift
- *
- * @param[in] _a input
- * @param[in] _b shift amount
- *
- * @return _a << |_b| if _b is positive
- * _a >> |_b| if _b is negative
- *
- * This function will shift _a with _b bits to the left if _b is positive.
- * This function will shift _a with _b bits to the right if _b is negative.
- * It asserts -MAX_SHIFT_1W <= _b <= MAX_SHIFT_1W.
- * It inserts zeros on the left or right depending on the shift direction:
- * right or left.
- * The operation count for this function assumes that
- * the shift amount is a cloned scalar input.
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_lshift(
- const tvector1w _a,
- const tvector1w _b);
-
-#endif
-/* Cast */
-
-/* @brief Cast from int to 1w
- *
- * @param[in] _a input
- *
- * @return _a
- *
- * This function casts the input from integer type to
- * single precision. It asserts there is no overflow.
- *
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_int_cast_to_1w(
- const int _a);
-
-/* @brief Cast from 1w to int
- *
- * @param[in] _a input
- *
- * @return _a
- *
- * This function casts the input from single precision type to
- * integer, preserving value and sign.
- *
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H int OP_1w_cast_to_int(
- const tvector1w _a);
-
-/* @brief Cast from 1w to 2w
- *
- * @param[in] _a input
- *
- * @return _a
- *
- * This function casts the input from single precision type to
- * double precision, preserving value and sign.
- *
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector2w OP_1w_cast_to_2w(
- const tvector1w _a);
-
-/* @brief Cast from 2w to 1w
- *
- * @param[in] _a input
- *
- * @return _a
- *
- * This function casts the input from double precision type to
- * single precision. In case of overflow it will wrap around.
- *
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_2w_cast_to_1w(
- const tvector2w _a);
-
-
-/* @brief Cast from 2w to 1w with saturation
- *
- * @param[in] _a input
- *
- * @return _a
- *
- * This function casts the input from double precision type to
- * single precision after saturating it to the range of single
- * precision.
- *
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_2w_sat_cast_to_1w(
- const tvector2w _a);
-
-/* clipping */
-
-/* @brief Clip asymmetrical
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return _a clipped between ~_b and b
- *
- * This function will clip the first argument between
- * (-_b - 1) and _b.
- * It asserts _b >= 0.
- *
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_clip_asym(
- const tvector1w _a,
- const tvector1w _b);
-
-/* @brief Clip zero
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return _a clipped beteween 0 and _b
- *
- * This function will clip the first argument between
- * zero and _b.
- * It asserts _b >= 0.
- *
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_clipz(
- const tvector1w _a,
- const tvector1w _b);
-
-/* division */
-
-/* @brief Truncated division
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return trunc( _a / _b )
- *
- * This function will divide the first argument by
- * the second argument, with rounding toward 0.
- * If _b == 0 and _a < 0, the function will return MIN_RANGE.
- * If _b == 0 and _a == 0, the function will return 0.
- * If _b == 0 and _a > 0, the function will return MAX_RANGE.
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_div(
- const tvector1w _a,
- const tvector1w _b);
-
-/* @brief Fractional saturating divide
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return _a / _b
- *
- * This function will perform fixed point division of
- * the first argument by the second argument, with rounding toward 0.
- * In case of overflow it will saturate.
- * If _b == 0 and _a < 0, the function will return MIN_RANGE.
- * If _b == 0 and _a == 0, the function will return 0.
- * If _b == 0 and _a > 0, the function will return MAX_RANGE.
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_qdiv(
- const tvector1w _a,
- const tvector1w _b);
-
-/* @brief Modulo
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return _a % _b
- *
- * This function will return the remainder r = _a - _b * trunc( _a / _b ),
- * Note that the sign of the remainder is always equal to the sign of _a.
- * If _b == 0 the function will return _a.
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_mod(
- const tvector1w _a,
- const tvector1w _b);
-
-/* @brief Unsigned integer Square root
- *
- * @param[in] _a input
- *
- * @return Integer square root of _a
- *
- * This function will calculate the Integer square root of _a
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w_unsigned OP_1w_sqrt_u(
- const tvector1w_unsigned _a);
-
-/* Miscellaneous */
-
-/* @brief Multiplexer
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- * @param[in] _c condition
- *
- * @return _c ? _a : _b
- *
- * This function will return _a if the condition _c
- * is true and _b otherwise.
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_mux(
- const tvector1w _a,
- const tvector1w _b,
- const tflags _c);
-
-/* @brief Average without rounding
- *
- * @param[in] _a first operand
- * @param[in] _b second operand
- *
- * @return (_a + _b) >> 1
- *
- * This function will add _a and _b, and right shift
- * the result by one without rounding. No overflow
- * will occur because addition is performed in the
- * proper precision.
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_avg(
- const tvector1w _a,
- const tvector1w _b);
-
-/* @brief Average with rounding
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return (_a + _b) >> 1
- *
- * This function will add _a and _b at full precision,
- * and right shift with rounding the result with 1 bit.
- * Depending on the rounding mode of the core
- * it will round to nearest or to nearest even.
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_avgrnd(
- const tvector1w _a,
- const tvector1w _b);
-
-/* @brief Minimum
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return (_a < _b) ? _a : _b;
- *
- * This function will return the smallest of both
- * input arguments.
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_min(
- const tvector1w _a,
- const tvector1w _b);
-
-/* @brief Maximum
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return (_a > _b) ? _a : _b;
- *
- * This function will return the largest of both
- * input arguments.
- */
-STORAGE_CLASS_ISP_OP1W_FUNC_H tvector1w OP_1w_max(
- const tvector1w _a,
- const tvector1w _b);
-
-#ifndef INLINE_ISP_OP1W
-#define STORAGE_CLASS_ISP_OP1W_FUNC_C
-#define STORAGE_CLASS_ISP_OP1W_DATA_C const
-#else /* INLINE_ISP_OP1W */
-#define STORAGE_CLASS_ISP_OP1W_FUNC_C STORAGE_CLASS_ISP_OP1W_FUNC_H
-#define STORAGE_CLASS_ISP_OP1W_DATA_C STORAGE_CLASS_ISP_OP1W_DATA_H
-#include "isp_op1w.c"
-#define ISP_OP1W_INLINED
-#endif /* INLINE_ISP_OP1W */
-
-#endif /* __ISP_OP1W_H_INCLUDED__ */
-
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp_op1w_types.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp_op1w_types.h
deleted file mode 100644
index c81e587509a1..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp_op1w_types.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __ISP_OP1W_TYPES_H_INCLUDED__
-#define __ISP_OP1W_TYPES_H_INCLUDED__
-
-/*
- * This file is part of the Multi-precision vector operations exstension package.
- */
-
-/*
- * Single-precision vector operations
- */
-
-/*
- * Prerequisites:
- *
- */
-
-#include "mpmath.h"
-
-/*
- * Single-precision data type specification
- */
-
-
-typedef mpsdata_t tvector1w;
-typedef mpsdata_t tscalar1w;
-typedef spsdata_t tflags;
-typedef mpudata_t tvector1w_unsigned;
-typedef mpsdata_t tscalar1w_weight;
-typedef mpsdata_t tvector1w_signed_positive;
-typedef mpsdata_t tvector1w_weight;
-#ifdef ISP2401
-typedef bool tscalar_bool;
-#endif
-
-typedef struct {
- tvector1w d;
- tflags f;
-} tvector1w_tflags1w;
-
-#endif /* __ISP_OP1W_TYPES_H_INCLUDED__ */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp_op2w.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp_op2w.h
deleted file mode 100644
index 7575d260b837..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp_op2w.h
+++ /dev/null
@@ -1,674 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __ISP_OP2W_H_INCLUDED__
-#define __ISP_OP2W_H_INCLUDED__
-
-/*
- * This file is part of the Multi-precision vector operations exstension package.
- */
-
-/*
- * Double-precision vector operations
- */
-
-/*
- * Prerequisites:
- *
- */
-
-#ifdef INLINE_ISP_OP2W
-#define STORAGE_CLASS_ISP_OP2W_FUNC_H static inline
-#define STORAGE_CLASS_ISP_OP2W_DATA_H static inline_DATA
-#else /* INLINE_ISP_OP2W */
-#define STORAGE_CLASS_ISP_OP2W_FUNC_H extern
-#define STORAGE_CLASS_ISP_OP2W_DATA_H extern_DATA
-#endif /* INLINE_ISP_OP2W */
-
-/*
- * Double-precision data type specification
- */
-
-#include "isp_op2w_types.h"
-
-/*
- * Double-precision prototype specification
- */
-
-/* Arithmetic */
-
-/* @brief bitwise AND
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return bitwise and of both input arguments
- *
- * This function will calculate the bitwise and.
- * result = _a & _b
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_and(
- const tvector2w _a,
- const tvector2w _b);
-
-/* @brief bitwise OR
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return bitwise or of both input arguments
- *
- * This function will calculate the bitwise or.
- * result = _a | _b
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_or(
- const tvector2w _a,
- const tvector2w _b);
-
-/* @brief bitwise XOR
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return bitwise xor of both input arguments
- *
- * This function will calculate the bitwise xor.
- * result = _a ^ _b
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_xor(
- const tvector2w _a,
- const tvector2w _b);
-
-/* @brief bitwise inverse
- *
- * @param[in] _a first argument
- *
- * @return bitwise inverse of both input arguments
- *
- * This function will calculate the bitwise inverse.
- * result = ~_a
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_inv(
- const tvector2w _a);
-
-/* Additive */
-
-/* @brief addition
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return sum of both input arguments
- *
- * This function will calculate the sum of the input arguments.
- * in case of overflow it will wrap around.
- * result = _a + _b
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_add(
- const tvector2w _a,
- const tvector2w _b);
-
-/* @brief subtraction
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return _b subtracted from _a.
- *
- * This function will subtract _b from _a.
- * in case of overflow it will wrap around.
- * result = _a - _b
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_sub(
- const tvector2w _a,
- const tvector2w _b);
-
-/* @brief saturated addition
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return saturated sum of both input arguments
- *
- * This function will calculate the sum of the input arguments.
- * in case of overflow it will saturate
- * result = CLIP(_a + _b, MIN_RANGE, MAX_RANGE);
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_addsat(
- const tvector2w _a,
- const tvector2w _b);
-
-/* @brief saturated subtraction
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return saturated subtraction of both input arguments
- *
- * This function will subtract _b from _a.
- * in case of overflow it will saturate
- * result = CLIP(_a - _b, MIN_RANGE, MAX_RANGE);
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_subsat(
- const tvector2w _a,
- const tvector2w _b);
-
-/* @brief subtraction with shift right and rounding
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return (a - b) >> 1
- *
- * This function subtracts _b from _a and right shifts
- * the result by 1 bit with rounding.
- * No overflow can occur.
- * result = (_a - _b) >> 1
- *
- * Note: This function will be deprecated due to
- * the naming confusion and it will be replaced
- * by "OP_2w_subhalfrnd".
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_subasr1(
- const tvector2w _a,
- const tvector2w _b);
-
-/* @brief Subtraction with shift right and rounding
- *
- * @param[in] _a first operand
- * @param[in] _b second operand
- *
- * @return (_a - _b) >> 1
- *
- * This function subtracts _b from _a and right shifts
- * the result by 1 bit with rounding.
- * No overflow can occur.
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_subhalfrnd(
- const tvector2w _a,
- const tvector2w _b);
-
-/* @brief Subtraction with shift right and no rounding
- *
- * @param[in] _a first operand
- * @param[in] _b second operand
- *
- * @return (_a - _b) >> 1
- *
- * This function subtracts _b from _a and right shifts
- * the result by 1 bit without rounding (i.e. truncation).
- * No overflow can occur.
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_subhalf(
- const tvector2w _a,
- const tvector2w _b);
-
-/* @brief saturated absolute value
- *
- * @param[in] _a input
- *
- * @return saturated absolute value of the input
- *
- * This function will calculate the saturated absolute value of the input.
- * In case of overflow it will saturate.
- * if (_a > 0) return _a;<br>
- * else return CLIP(-_a, MIN_RANGE, MAX_RANGE);<br>
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_abs(
- const tvector2w _a);
-
-/* @brief saturated absolute difference
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return sat(abs(sat(a-b)));
- *
- * This function will calculate the saturated absolute value
- * of the saturated difference of both inputs.
- * result = sat(abs(sat(_a - _b)));
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_subabssat(
- const tvector2w _a,
- const tvector2w _b);
-
-/* Multiplicative */
-
-/* @brief integer multiply
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return product of _a and _b
- *
- * This function will calculate the product
- * of the input arguments and returns the LSB
- * aligned double precision result.
- * In case of overflow it will wrap around.
- * result = _a * _b;
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_mul(
- const tvector2w _a,
- const tvector2w _b);
-
-/* @brief fractional saturating multiply
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return saturated product of _a and _b
- *
- * This function will calculate the fixed point
- * product of the input arguments
- * and returns a double precision result.
- * In case of overflow it will saturate.
- * result =((_a * _b) << 1) >> (2*NUM_BITS);
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_qmul(
- const tvector2w _a,
- const tvector2w _b);
-
-/* @brief fractional saturating multiply with rounding
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return product of _a and _b
- *
- * This function will calculate the fixed point
- * product of the input arguments
- * and returns a double precision result.
- * Depending on the rounding mode of the core
- * it will round to nearest or to nearest even.
- * In case of overflow it will saturate.
- * result = ((_a * _b) << 1) >> (2*NUM_BITS);
- */
-
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_qrmul(
- const tvector2w _a,
- const tvector2w _b);
-
-/* Comparative */
-
-/* @brief equal
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return _a == _b
- *
- * This function will return true if both inputs
- * are equal, and false if not equal.
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tflags OP_2w_eq(
- const tvector2w _a,
- const tvector2w _b);
-
-/* @brief not equal
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return _a != _b
- *
- * This function will return false if both inputs
- * are equal, and true if not equal.
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tflags OP_2w_ne(
- const tvector2w _a,
- const tvector2w _b);
-
-/* @brief less or equal
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return _a <= _b
- *
- * This function will return true if _a is smaller
- * or equal than _b.
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tflags OP_2w_le(
- const tvector2w _a,
- const tvector2w _b);
-
-/* @brief less then
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return _a < _b
- *
- * This function will return true if _a is smaller
- * than _b.
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tflags OP_2w_lt(
- const tvector2w _a,
- const tvector2w _b);
-
-/* @brief greater or equal
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return _a >= _b
- *
- * This function will return true if _a is greater
- * or equal than _b.
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tflags OP_2w_ge(
- const tvector2w _a,
- const tvector2w _b);
-
-/* @brief greater than
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return _a > _b
- *
- * This function will return true if _a is greater
- * than _b.
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tflags OP_2w_gt(
- const tvector2w _a,
- const tvector2w _b);
-
-/* Shift */
-
-/* @brief aritmetic shift right
- *
- * @param[in] _a input
- * @param[in] _b shift amount
- *
- * @return _a >> _b
- *
- * This function will shift _a with _b bits to the right,
- * preserving the sign bit.
- * It asserts 0 <= _b <= MAX_SHIFT_2W.
- * The operation count for this function assumes that
- * the shift amount is a cloned scalar input.
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_asr(
- const tvector2w _a,
- const tvector2w _b);
-
-/* @brief aritmetic shift right with rounding
- *
- * @param[in] _a input
- * @param[in] _b shift amount
- *
- * @return _a >> _b
- *
- * If _b < 2*NUM_BITS, this function will shift _a with _b bits to the right,
- * preserving the sign bit, and depending on the rounding mode of the core
- * it will round to nearest or to nearest even.
- * If _b >= 2*NUM_BITS, this function will return 0.
- * It asserts 0 <= _b <= MAX_SHIFT_2W.
- * The operation count for this function assumes that
- * the shift amount is a cloned scalar input.
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_asrrnd(
- const tvector2w _a,
- const tvector2w _b);
-
-/* @brief saturating aritmetic shift left
- *
- * @param[in] _a input
- * @param[in] _b shift amount
- *
- * @return _a << _b
- *
- * If _b < MAX_BITDEPTH, this function will shift _a with _b bits to the left,
- * saturating at MIN_RANGE/MAX_RANGE in case of overflow.
- * If _b >= MAX_BITDEPTH, this function will return MIN_RANGE if _a < 0,
- * MAX_RANGE if _a > 0, 0 if _a == 0.
- * (with MAX_BITDEPTH=64)
- * It asserts 0 <= _b <= MAX_SHIFT_2W.
- * The operation count for this function assumes that
- * the shift amount is a cloned scalar input.
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_asl(
- const tvector2w _a,
- const tvector2w _b);
-
-/* @brief saturating aritmetic shift left
- *
- * @param[in] _a input
- * @param[in] _b shift amount
- *
- * @return _a << _b
- *
- * This function is identical to OP_2w_asl( )
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_aslsat(
- const tvector2w _a,
- const tvector2w _b);
-
-/* @brief logical shift left
- *
- * @param[in] _a input
- * @param[in] _b shift amount
- *
- * @return _a << _b
- *
- * This function will shift _a with _b bits to the left.
- * It will insert zeroes on the right.
- * It asserts 0 <= _b <= MAX_SHIFT_2W.
- * The operation count for this function assumes that
- * the shift amount is a cloned scalar input.
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_lsl(
- const tvector2w _a,
- const tvector2w _b);
-
-/* @brief logical shift right
- *
- * @param[in] _a input
- * @param[in] _b shift amount
- *
- * @return _a >> _b
- *
- * This function will shift _a with _b bits to the right.
- * It will insert zeroes on the left.
- * It asserts 0 <= _b <= MAX_SHIFT_2W.
- * The operation count for this function assumes that
- * the shift amount is a cloned scalar input.
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_lsr(
- const tvector2w _a,
- const tvector2w _b);
-
-/* clipping */
-
-/* @brief Clip asymmetrical
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return _a clipped between ~_b and b
- *
- * This function will clip the first argument between
- * (-_b - 1) and _b.
- * It asserts _b >= 0.
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_clip_asym(
- const tvector2w _a,
- const tvector2w _b);
-
-/* @brief Clip zero
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return _a clipped beteween 0 and _b
- *
- * This function will clip the first argument between
- * zero and _b.
- * It asserts _b >= 0.
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_clipz(
- const tvector2w _a,
- const tvector2w _b);
-
-/* division */
-
-/* @brief Truncated division
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return trunc( _a / _b )
- *
- * This function will divide the first argument by
- * the second argument, with rounding toward 0.
- * If _b == 0 and _a < 0, the function will return MIN_RANGE.
- * If _b == 0 and _a == 0, the function will return 0.
- * If _b == 0 and _a > 0, the function will return MAX_RANGE.
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_div(
- const tvector2w _a,
- const tvector2w _b);
-
-/* @brief Saturating truncated division
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return CLIP( trunc( _a / _b ), MIN_RANGE1w, MAX_RANGE1w )
- *
- * This function will divide the first argument by
- * the second argument, with rounding toward 0, and
- * saturate the result to the range of single precision.
- * If _b == 0 and _a < 0, the function will return MIN_RANGE.
- * If _b == 0 and _a == 0, the function will return 0.
- * If _b == 0 and _a > 0, the function will return MAX_RANGE.
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector1w OP_2w_divh(
- const tvector2w _a,
- const tvector1w _b);
-
-/* @brief Modulo
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return n/a
- *
- * This function has not yet been implemented.
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_mod(
- const tvector2w _a,
- const tvector2w _b);
-
-/* @brief Unsigned Integer Square root
- *
- * @param[in] _a input
- *
- * @return square root of _a
- *
- * This function will calculate the unsigned integer square root of _a
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector1w_unsigned OP_2w_sqrt_u(
- const tvector2w_unsigned _a);
-
-/* Miscellaneous */
-
-/* @brief Multiplexer
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- * @param[in] _c condition
- *
- * @return _c ? _a : _b
- *
- * This function will return _a if the condition _c
- * is true and _b otherwise.
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_mux(
- const tvector2w _a,
- const tvector2w _b,
- const tflags _c);
-
-/* @brief Average without rounding
- *
- * @param[in] _a first operand
- * @param[in] _b second operand
- *
- * @return (_a + _b) >> 1
- *
- * This function will add _a and _b, and right shift
- * the result by one without rounding. No overflow
- * will occur because addition is performed in the
- * proper precision.
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_avg(
- const tvector2w _a,
- const tvector2w _b);
-
-/* @brief Average with rounding
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return (_a + _b) >> 1
- *
- * This function will add _a and _b at full precision,
- * and right shift with rounding the result with 1 bit.
- * Depending on the rounding mode of the core
- * it will round to nearest or to nearest even.
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_avgrnd(
- const tvector2w _a,
- const tvector2w _b);
-
-/* @brief Minimum
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return (_a < _b) ? _a : _b;
- *
- * This function will return the smallest of both
- * input arguments.
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_min(
- const tvector2w _a,
- const tvector2w _b);
-
-/* @brief Maximum
- *
- * @param[in] _a first argument
- * @param[in] _b second argument
- *
- * @return (_a > _b) ? _a : _b;
- *
- * This function will return the largest of both
- * input arguments.
- */
-STORAGE_CLASS_ISP_OP2W_FUNC_H tvector2w OP_2w_max(
- const tvector2w _a,
- const tvector2w _b);
-
-#ifndef INLINE_ISP_OP2W
-#define STORAGE_CLASS_ISP_OP2W_FUNC_C
-#define STORAGE_CLASS_ISP_OP2W_DATA_C const
-#else /* INLINE_ISP_OP2W */
-#define STORAGE_CLASS_ISP_OP2W_FUNC_C STORAGE_CLASS_ISP_OP2W_FUNC_H
-#define STORAGE_CLASS_ISP_OP2W_DATA_C STORAGE_CLASS_ISP_OP2W_DATA_H
-#include "isp_op2w.c"
-#define ISP_OP2W_INLINED
-#endif /* INLINE_ISP_OP2W */
-
-#endif /* __ISP_OP2W_H_INCLUDED__ */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp_op2w_types.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp_op2w_types.h
deleted file mode 100644
index 7e86083a8a33..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp_op2w_types.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __ISP_OP2W_TYPES_H_INCLUDED__
-#define __ISP_OP2W_TYPES_H_INCLUDED__
-
-/*
- * This file is part of the Multi-precision vector operations exstension package.
- */
-
-/*
- * Double-precision vector operations
- */
-
-/*
- * Prerequisites:
- *
- */
-#include "mpmath.h"
-#include "isp_op1w_types.h"
-
-/*
- * Single-precision data type specification
- */
-
-
-typedef mpsdata_t tvector2w;
-typedef mpsdata_t tscalar2w;
-typedef mpsdata_t tvector2w_signed_positive;
-typedef mpudata_t tvector2w_unsigned;
-
-
-typedef struct {
- tvector2w d;
- tflags f;
-} tvector2w_tflags;
-
-#endif /* __ISP_OP2W_TYPES_H_INCLUDED__ */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp_op_count.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp_op_count.h
deleted file mode 100644
index 8e7b48d026b0..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/isp_op_count.h
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __ISP_OP_COUNT_H_INCLUDED__
-#define __ISP_OP_COUNT_H_INCLUDED__
-
-#include <stdio.h>
-
-typedef struct {
- long long bbb_cnt; /* number of bbb */
- int bbb_op; /* operations per bbb */
- long long total_cnt; /* bbb_cnt * bbb_op */
-} bbb_stat_t;
-
-typedef enum {
- bbb_func_OP_1w_and,
- bbb_func_OP_1w_or,
- bbb_func_OP_1w_xor,
- bbb_func_OP_1w_inv,
- bbb_func_OP_1w_add,
- bbb_func_OP_1w_sub,
- bbb_func_OP_1w_addsat,
- bbb_func_OP_1w_subsat,
- bbb_func_OP_1w_subasr1,
- bbb_func_OP_1w_subhalf,
- bbb_func_OP_1w_subhalfrnd,
- bbb_func_OP_1w_abs,
- bbb_func_OP_1w_subabssat,
-#ifdef ISP2401
- bbb_func_OP_1w_subsat_u,
-#endif
- bbb_func_OP_1w_muld,
- bbb_func_OP_1w_mul,
- bbb_func_OP_1w_qmul,
- bbb_func_OP_1w_qrmul,
- bbb_func_OP_1w_eq,
- bbb_func_OP_1w_ne,
- bbb_func_OP_1w_le,
- bbb_func_OP_1w_lt,
- bbb_func_OP_1w_ge,
- bbb_func_OP_1w_gt,
- bbb_func_OP_1w_asr,
- bbb_func_OP_1w_asrrnd,
- bbb_func_OP_1w_asl,
- bbb_func_OP_1w_aslsat,
- bbb_func_OP_1w_lsl,
- bbb_func_OP_1w_lsr,
-#ifdef ISP2401
- bbb_func_OP_1w_ashift,
- bbb_func_OP_1w_lshift,
-#endif
- bbb_func_OP_int_cast_to_1w ,
- bbb_func_OP_1w_cast_to_int ,
- bbb_func_OP_1w_cast_to_2w ,
- bbb_func_OP_2w_cast_to_1w ,
- bbb_func_OP_2w_sat_cast_to_1w ,
- bbb_func_OP_1w_clip_asym,
- bbb_func_OP_1w_clipz,
- bbb_func_OP_1w_div,
- bbb_func_OP_1w_qdiv,
- bbb_func_OP_1w_mod,
- bbb_func_OP_1w_sqrt_u,
- bbb_func_OP_1w_mux,
- bbb_func_OP_1w_avg,
- bbb_func_OP_1w_avgrnd,
- bbb_func_OP_1w_min,
- bbb_func_OP_1w_max,
- bbb_func_OP_2w_and,
- bbb_func_OP_2w_or,
- bbb_func_OP_2w_xor,
- bbb_func_OP_2w_inv,
- bbb_func_OP_2w_add,
- bbb_func_OP_2w_sub,
- bbb_func_OP_2w_addsat,
- bbb_func_OP_2w_subsat,
- bbb_func_OP_2w_subasr1,
- bbb_func_OP_2w_subhalf,
- bbb_func_OP_2w_subhalfrnd,
- bbb_func_OP_2w_abs,
- bbb_func_OP_2w_subabssat,
- bbb_func_OP_2w_mul,
- bbb_func_OP_2w_qmul,
- bbb_func_OP_2w_qrmul,
- bbb_func_OP_2w_eq,
- bbb_func_OP_2w_ne,
- bbb_func_OP_2w_le,
- bbb_func_OP_2w_lt,
- bbb_func_OP_2w_ge,
- bbb_func_OP_2w_gt,
- bbb_func_OP_2w_asr,
- bbb_func_OP_2w_asrrnd,
- bbb_func_OP_2w_asl,
- bbb_func_OP_2w_aslsat,
- bbb_func_OP_2w_lsl,
- bbb_func_OP_2w_lsr,
- bbb_func_OP_2w_clip_asym,
- bbb_func_OP_2w_clipz,
- bbb_func_OP_2w_div,
- bbb_func_OP_2w_divh,
- bbb_func_OP_2w_mod,
- bbb_func_OP_2w_sqrt_u,
- bbb_func_OP_2w_mux,
- bbb_func_OP_2w_avg,
- bbb_func_OP_2w_avgrnd,
- bbb_func_OP_2w_min,
- bbb_func_OP_2w_max,
- bbb_func_OP_1w_mul_realigning,
-#ifdef ISP2401
- bbb_func_OP_1w_imax32,
- bbb_func_OP_1w_imaxidx32,
- bbb_func_OP_1w_cond_add,
-#endif
-
- bbb_func_num_functions
-} bbb_functions_t;
-
-typedef enum {
- core_func_OP_and,
- core_func_OP_or,
- core_func_OP_xor,
- core_func_OP_inv,
- core_func_OP_add,
- core_func_OP_sub,
- core_func_OP_addsat,
- core_func_OP_subsat,
- core_func_OP_subasr1,
- core_func_OP_abs,
- core_func_OP_subabssat,
-#ifdef ISP2401
- core_func_OP_subsat_u,
-#endif
- core_func_OP_muld,
- core_func_OP_mul,
- core_func_OP_qrmul,
- core_func_OP_eq,
- core_func_OP_ne,
- core_func_OP_le,
- core_func_OP_lt,
- core_func_OP_ge,
- core_func_OP_gt,
- core_func_OP_asr,
- core_func_OP_asl,
- core_func_OP_asrrnd,
- core_func_OP_lsl,
- core_func_OP_lslsat,
- core_func_OP_lsr,
- core_func_OP_lsrrnd,
- core_func_OP_clip_asym,
- core_func_OP_clipz,
- core_func_OP_div,
- core_func_OP_mod,
- core_func_OP_sqrt,
- core_func_OP_mux,
- core_func_OP_avgrnd,
- core_func_OP_min,
- core_func_OP_max,
-
- core_func_num_functions
-
-} core_functions_t;
-
-/* inc_bbb_count() can be used for building blocks that are implemented with one operation
- inc_bbb_count_ext() will be used in case the operation count is not known or greater than one.
-
- For some operations there is a difference in operation count for the cloned version and the
- not cloned version. this difference is not vissible on the reference code side.
- We could add a min and max operation count for those operations, and keep track of those counts
- separately. That way in the report the impact can be seen. */
-
-#ifdef DISABLE_OPCNT
-#define inc_bbb_count(func)
-#define inc_bbb_count_ext(func, cnt)
-#define enable_bbb_count()
-#define disable_bbb_count()
-#else
-#define inc_bbb_count(func) _inc_bbb_count(func)
-#define inc_bbb_count_ext(func, cnt) _inc_bbb_count_ext(func, cnt)
-#define enable_bbb_count() _enable_bbb_count()
-#define disable_bbb_count() _disable_bbb_count()
-#endif
-
-void
-inc_core_count_n(
- core_functions_t func,
- unsigned n);
-
-void
-_enable_bbb_count(void);
-
-void
-_disable_bbb_count(void);
-
-void
-_inc_bbb_count(
- bbb_functions_t func);
-
-void
-_inc_bbb_count_ext(
- bbb_functions_t func,
- int op_count);
-
-void
-bbb_func_reset_count(void);
-
-void
-bbb_func_print_totals(
- FILE * fp,
- unsigned non_zero_only);
-
-void
-core_func_print_totals(
- FILE* fp,
- unsigned non_zero_only);
-
-#endif
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/osys_public.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/osys_public.h
deleted file mode 100644
index 8695e3c01fa6..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/osys_public.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __OSYS_PUBLIC_H_INCLUDED__
-#define __OSYS_PUBLIC_H_INCLUDED__
-
-#include "system_types.h"
-
-#endif /* __OSYS_PUBLIC_H_INCLUDED__ */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/pipeline_public.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/pipeline_public.h
deleted file mode 100644
index 32cea582b4c4..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/pipeline_public.h
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __PIPELINE_PUBLIC_H_INCLUDED__
-#define __PIPELINE_PUBLIC_H_INCLUDED__
-
-#endif /* __PIPELINE_PUBLIC_H_INCLUDED__ */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/ref_vector_func.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/ref_vector_func.h
deleted file mode 100644
index c1638c06407d..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/ref_vector_func.h
+++ /dev/null
@@ -1,1221 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef _REF_VECTOR_FUNC_H_INCLUDED_
-#define _REF_VECTOR_FUNC_H_INCLUDED_
-
-
-#ifdef INLINE_VECTOR_FUNC
-#define STORAGE_CLASS_REF_VECTOR_FUNC_H static inline
-#define STORAGE_CLASS_REF_VECTOR_DATA_H static inline_DATA
-#else /* INLINE_VECTOR_FUNC */
-#define STORAGE_CLASS_REF_VECTOR_FUNC_H extern
-#define STORAGE_CLASS_REF_VECTOR_DATA_H extern_DATA
-#endif /* INLINE_VECTOR_FUNC */
-
-
-#include "ref_vector_func_types.h"
-
-/* @brief Doubling multiply accumulate with saturation
- *
- * @param[in] acc accumulator
- * @param[in] a multiply input
- * @param[in] b multiply input
- *
- * @return acc + (a*b)
- *
- * This function will do a doubling multiply ont
- * inputs a and b, and will add the result to acc.
- * in case of an overflow of acc, it will saturate.
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector2w OP_1w_maccd_sat(
- tvector2w acc,
- tvector1w a,
- tvector1w b );
-
-/* @brief Doubling multiply accumulate
- *
- * @param[in] acc accumulator
- * @param[in] a multiply input
- * @param[in] b multiply input
- *
- * @return acc + (a*b)
- *
- * This function will do a doubling multiply ont
- * inputs a and b, and will add the result to acc.
- * in case of overflow it will not saturate but wrap around.
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector2w OP_1w_maccd(
- tvector2w acc,
- tvector1w a,
- tvector1w b );
-
-/* @brief Re-aligning multiply
- *
- * @param[in] a multiply input
- * @param[in] b multiply input
- * @param[in] shift shift amount
- *
- * @return (a*b)>>shift
- *
- * This function will multiply a with b, followed by a right
- * shift with rounding. the result is saturated and casted
- * to single precision.
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w OP_1w_mul_realigning(
- tvector1w a,
- tvector1w b,
- tscalar1w shift );
-
-/* @brief Leading bit index
- *
- * @param[in] a input
- *
- * @return index of the leading bit of each element
- *
- * This function finds the index of leading one (set) bit of the
- * input. The index starts with 0 for the LSB and can go upto
- * ISP_VEC_ELEMBITS-1 for the MSB. For an input equal to zero,
- * the returned index is -1.
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w OP_1w_lod(
- tvector1w a);
-
-/* @brief Config Unit Input Processing
- *
- * @param[in] a input
- * @param[in] input_scale input scaling factor
- * @param[in] input_offset input offset factor
- *
- * @return scaled & offset added input clamped to MAXVALUE
- *
- * As part of input processing for piecewise linear estimation config unit,
- * this function will perform scaling followed by adding offset and
- * then clamping to the MAX InputValue
- * It asserts -MAX_SHIFT_1W <= input_scale <= MAX_SHIFT_1W, and
- * -MAX_SHIFT_1W <= input_offset <= MAX_SHIFT_1W
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w OP_1w_input_scaling_offset_clamping(
- tvector1w a,
- tscalar1w_5bit_signed input_scale,
- tscalar1w_5bit_signed input_offset);
-
-/* @brief Config Unit Output Processing
- *
- * @param[in] a output
- * @param[in] output_scale output scaling factor
- *
- * @return scaled & clamped output value
- *
- * As part of output processing for piecewise linear estimation config unit,
- * This function will perform scaling and then clamping to output
- * MAX value.
- * It asserts -MAX_SHIFT_1W <= output_scale <= MAX_SHIFT_1W
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w OP_1w_output_scaling_clamping(
- tvector1w a,
- tscalar1w_5bit_signed output_scale);
-
-/* @brief Config Unit Piecewiselinear estimation
- *
- * @param[in] a input
- * @param[in] config_points config parameter structure
- *
- * @return piecewise linear estimated output
- *
- * Given a set of N points {(x1,y1),()x2,y2), ....,(xn,yn)}, to find
- * the functional value at an arbitrary point around the input set,
- * this function will perform input processing followed by piecewise
- * linear estimation and then output processing to yield the final value.
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w OP_1w_piecewise_estimation(
- tvector1w a,
- ref_config_points config_points);
-
-/* @brief Fast Config Unit
- *
- * @param[in] x input
- * @param[in] init_vectors LUT data structure
- *
- * @return piecewise linear estimated output
- * This block gets an input x and a set of input configuration points stored in a look-up
- * table of 32 elements. First, the x input is clipped to be within the range [x1, xn+1].
- * Then, it computes the interval in which the input lies. Finally, the output is computed
- * by performing linear interpolation based on the interval properties (i.e. x_prev, slope,
- * and offset). This block assumes that the points are equally spaced and that the interval
- * size is a power of 2.
- **/
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w OP_1w_XCU(
- tvector1w x,
- xcu_ref_init_vectors init_vectors);
-
-
-/* @brief LXCU
- *
- * @param[in] x input
- * @param[in] init_vectors LUT data structure
- *
- * @return logarithmic piecewise linear estimated output.
- * This block gets an input x and a set of input configuration points stored in a look-up
- * table of 32 elements. It computes the interval in which the input lies.
- * Then output is computed by performing linear interpolation based on the interval
- * properties (i.e. x_prev, slope, * and offset).
- * This BBB assumes spacing x-coordinates of "init vectors" increase exponentially as
- * shown below.
- * interval size : 2^0 2^1 2^2 2^3
- * x-coordinates: x0<--->x1<---->x2<---->x3<---->
- **/
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w OP_1w_LXCU(
- tvector1w x,
- xcu_ref_init_vectors init_vectors);
-
-/* @brief Coring
- *
- * @param[in] coring_vec Amount of coring based on brightness level
- * @param[in] filt_input Vector of input pixels on which Coring is applied
- * @param[in] m_CnrCoring0 Coring Level0
- *
- * @return vector of filtered pixels after coring is applied
- *
- * This function will perform adaptive coring based on brightness level to
- * remove noise
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w coring(
- tvector1w coring_vec,
- tvector1w filt_input,
- tscalar1w m_CnrCoring0 );
-
-/* @brief Normalised FIR with coefficients [3,4,1]
- *
- * @param[in] m 1x3 matrix with pixels
- *
- * @return filtered output
- *
- * This function will calculate the
- * Normalised FIR with coefficients [3,4,1],
- *-5dB at Fs/2, -90 degree phase shift (quarter pixel)
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w fir1x3m_5dB_m90_nrm (
- const s_1w_1x3_matrix m);
-
-/* @brief Normalised FIR with coefficients [1,4,3]
- *
- * @param[in] m 1x3 matrix with pixels
- *
- * @return filtered output
- *
- * This function will calculate the
- * Normalised FIR with coefficients [1,4,3],
- *-5dB at Fs/2, +90 degree phase shift (quarter pixel)
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w fir1x3m_5dB_p90_nrm (
- const s_1w_1x3_matrix m);
-
-/* @brief Normalised FIR with coefficients [1,2,1]
- *
- * @param[in] m 1x3 matrix with pixels
- *
- * @return filtered output
- *
- * This function will calculate the
- * Normalised FIR with coefficients [1,2,1], -6dB at Fs/2
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w fir1x3m_6dB_nrm (
- const s_1w_1x3_matrix m);
-
-/* @brief Normalised FIR with coefficients [13,16,3]
- *
- * @param[in] m 1x3 matrix with pixels
- *
- * @return filtered output
- *
- * This function will calculate the
- * Normalised FIR with coefficients [13,16,3],
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w fir1x3m_6dB_nrm_ph0 (
- const s_1w_1x3_matrix m);
-
-/* @brief Normalised FIR with coefficients [9,16,7]
- *
- * @param[in] m 1x3 matrix with pixels
- *
- * @return filtered output
- *
- * This function will calculate the
- * Normalised FIR with coefficients [9,16,7],
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w fir1x3m_6dB_nrm_ph1 (
- const s_1w_1x3_matrix m);
-
-/* @brief Normalised FIR with coefficients [5,16,11]
- *
- * @param[in] m 1x3 matrix with pixels
- *
- * @return filtered output
- *
- * This function will calculate the
- * Normalised FIR with coefficients [5,16,11],
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w fir1x3m_6dB_nrm_ph2 (
- const s_1w_1x3_matrix m);
-
-/* @brief Normalised FIR with coefficients [1,16,15]
- *
- * @param[in] m 1x3 matrix with pixels
- *
- * @return filtered output
- *
- * This function will calculate the
- * Normalised FIR with coefficients [1,16,15],
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w fir1x3m_6dB_nrm_ph3 (
- const s_1w_1x3_matrix m);
-
-/* @brief Normalised FIR with programable phase shift
- *
- * @param[in] m 1x3 matrix with pixels
- * @param[in] coeff phase shift
- *
- * @return filtered output
- *
- * This function will calculate the
- * Normalised FIR with coefficients [8-coeff,16,8+coeff],
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w fir1x3m_6dB_nrm_calc_coeff (
- const s_1w_1x3_matrix m, tscalar1w_3bit coeff);
-
-/* @brief 3 tap FIR with coefficients [1,1,1]
- *
- * @param[in] m 1x3 matrix with pixels
- *
- * @return filtered output
- *
- * This function will calculate the
- * FIR with coefficients [1,1,1], -9dB at Fs/2 normalized with factor 1/2
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w fir1x3m_9dB_nrm (
- const s_1w_1x3_matrix m);
-
-#ifdef ISP2401
-/* @brief symmetric 3 tap FIR acts as LPF or BSF
- *
- * @param[in] m 1x3 matrix with pixels
- * @param[in] k filter coefficient shift
- * @param[in] bsf_flag 1 for BSF and 0 for LPF
- *
- * @return filtered output
- *
- * This function performs variable coefficient symmetric 3 tap filter which can
- * be either used as Low Pass Filter or Band Stop Filter.
- * Symmetric 3tap tap filter with DC gain 1 has filter coefficients [a, 1-2a, a]
- * For LPF 'a' can be approximated as (1 - 2^(-k))/4, k = 0, 1, 2, ...
- * and filter output can be approximated as:
- * out_LPF = ((v00 + v02) - ((v00 + v02) >> k) + (2 * (v01 + (v01 >> k)))) >> 2
- * For BSF 'a' can be approximated as (1 + 2^(-k))/4, k = 0, 1, 2, ...
- * and filter output can be approximated as:
- * out_BSF = ((v00 + v02) + ((v00 + v02) >> k) + (2 * (v01 - (v01 >> k)))) >> 2
- * For a given filter coefficient shift 'k' and bsf_flag this function
- * behaves either as LPF or BSF.
- * All computation is done using 1w arithmetic and implementation does not use
- * any multiplication.
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w
-sym_fir1x3m_lpf_bsf(s_1w_1x3_matrix m,
- tscalar1w k,
- tscalar_bool bsf_flag);
-#endif
-
-/* @brief Normalised 2D FIR with coefficients [1;2;1] * [1,2,1]
- *
- * @param[in] m 3x3 matrix with pixels
- *
- * @return filtered output
- *
- * This function will calculate the
- * Normalised FIR with coefficients [1;2;1] * [1,2,1]
- * Unity gain filter through repeated scaling and rounding
- * - 6 rotate operations per output
- * - 8 vector operations per output
- * _______
- * 14 total operations
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w fir3x3m_6dB_nrm (
- const s_1w_3x3_matrix m);
-
-/* @brief Normalised 2D FIR with coefficients [1;1;1] * [1,1,1]
- *
- * @param[in] m 3x3 matrix with pixels
- *
- * @return filtered output
- *
- * This function will calculate the
- * Normalised FIR with coefficients [1;1;1] * [1,1,1]
- *
- * (near) Unity gain filter through repeated scaling and rounding
- * - 6 rotate operations per output
- * - 8 vector operations per output
- * _______
- * 14 operations
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w fir3x3m_9dB_nrm (
- const s_1w_3x3_matrix m);
-
-/* @brief Normalised dual output 2D FIR with coefficients [1;2;1] * [1,2,1]
- *
- * @param[in] m 4x3 matrix with pixels
- *
- * @return two filtered outputs (2x1 matrix)
- *
- * This function will calculate the
- * Normalised FIR with coefficients [1;2;1] * [1,2,1]
- * and produce two outputs (vertical)
- * Unity gain filter through repeated scaling and rounding
- * compute two outputs per call to re-use common intermediates
- * - 4 rotate operations per output
- * - 6 vector operations per output (alternative possible, but in this
- * form it's not obvious to re-use variables)
- * _______
- * 10 total operations
- */
- STORAGE_CLASS_REF_VECTOR_FUNC_H s_1w_2x1_matrix fir3x3m_6dB_out2x1_nrm (
- const s_1w_4x3_matrix m);
-
-/* @brief Normalised dual output 2D FIR with coefficients [1;1;1] * [1,1,1]
- *
- * @param[in] m 4x3 matrix with pixels
- *
- * @return two filtered outputs (2x1 matrix)
- *
- * This function will calculate the
- * Normalised FIR with coefficients [1;1;1] * [1,1,1]
- * and produce two outputs (vertical)
- * (near) Unity gain filter through repeated scaling and rounding
- * compute two outputs per call to re-use common intermediates
- * - 4 rotate operations per output
- * - 7 vector operations per output (alternative possible, but in this
- * form it's not obvious to re-use variables)
- * _______
- * 11 total operations
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H s_1w_2x1_matrix fir3x3m_9dB_out2x1_nrm (
- const s_1w_4x3_matrix m);
-
-/* @brief Normalised 2D FIR 5x5
- *
- * @param[in] m 5x5 matrix with pixels
- *
- * @return filtered output
- *
- * This function will calculate the
- * Normalised FIR with coefficients [1;1;1] * [1;2;1] * [1,2,1] * [1,1,1]
- * and produce a filtered output
- * (near) Unity gain filter through repeated scaling and rounding
- * - 20 rotate operations per output
- * - 28 vector operations per output
- * _______
- * 48 total operations
-*/
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w fir5x5m_15dB_nrm (
- const s_1w_5x5_matrix m);
-
-/* @brief Normalised FIR 1x5
- *
- * @param[in] m 1x5 matrix with pixels
- *
- * @return filtered output
- *
- * This function will calculate the
- * Normalised FIR with coefficients [1,2,1] * [1,1,1] = [1,4,6,4,1]
- * and produce a filtered output
- * (near) Unity gain filter through repeated scaling and rounding
- * - 4 rotate operations per output
- * - 5 vector operations per output
- * _______
- * 9 total operations
-*/
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w fir1x5m_12dB_nrm (
- const s_1w_1x5_matrix m);
-
-/* @brief Normalised 2D FIR 5x5
- *
- * @param[in] m 5x5 matrix with pixels
- *
- * @return filtered output
- *
- * This function will calculate the
- * Normalised FIR with coefficients [1;2;1] * [1;2;1] * [1,2,1] * [1,2,1]
- * and produce a filtered output
- * (near) Unity gain filter through repeated scaling and rounding
- * - 20 rotate operations per output
- * - 30 vector operations per output
- * _______
- * 50 total operations
-*/
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w fir5x5m_12dB_nrm (
- const s_1w_5x5_matrix m);
-
-/* @brief Approximate averaging FIR 1x5
- *
- * @param[in] m 1x5 matrix with pixels
- *
- * @return filtered output
- *
- * This function will produce filtered output by
- * applying the filter coefficients (1/8) * [1,1,1,1,1]
- * _______
- * 5 vector operations
-*/
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w fir1x5m_box (
- s_1w_1x5_matrix m);
-
-/* @brief Approximate averaging FIR 1x9
- *
- * @param[in] m 1x9 matrix with pixels
- *
- * @return filtered output
- *
- * This function will produce filtered output by
- * applying the filter coefficients (1/16) * [1,1,1,1,1,1,1,1,1]
- * _______
- * 9 vector operations
-*/
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w fir1x9m_box (
- s_1w_1x9_matrix m);
-
-/* @brief Approximate averaging FIR 1x11
- *
- * @param[in] m 1x11 matrix with pixels
- *
- * @return filtered output
- *
- * This function will produce filtered output by
- * applying the filter coefficients (1/16) * [1,1,1,1,1,1,1,1,1,1,1]
- * _______
- * 12 vector operations
-*/
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w fir1x11m_box (
- s_1w_1x11_matrix m);
-
-/* @brief Symmetric 7 tap filter with normalization
- *
- * @param[in] in 1x7 matrix with pixels
- * @param[in] coeff 1x4 matrix with coefficients
- * @param[in] out_shift output pixel shift value for normalization
- *
- * @return symmetric 7 tap filter output
- *
- * This function performs symmetric 7 tap filter over input pixels.
- * Filter sum is normalized by shifting out_shift bits.
- * Filter sum: p0*c3 + p1*c2 + p2*c1 + p3*c0 + p4*c1 + p5*c2 + p6*c3
- * is implemented as: (p0 + p6)*c3 + (p1 + p5)*c2 + (p2 + p4)*c1 + p3*c0 to
- * reduce multiplication.
- * Input pixels should to be scaled, otherwise overflow is possible during
- * addition
-*/
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w
-fir1x7m_sym_nrm(s_1w_1x7_matrix in,
- s_1w_1x4_matrix coeff,
- tvector1w out_shift);
-
-/* @brief Symmetric 7 tap filter with normalization at input side
- *
- * @param[in] in 1x7 matrix with pixels
- * @param[in] coeff 1x4 matrix with coefficients
- *
- * @return symmetric 7 tap filter output
- *
- * This function performs symmetric 7 tap filter over input pixels.
- * Filter sum: p0*c3 + p1*c2 + p2*c1 + p3*c0 + p4*c1 + p5*c2 + p6*c3
- * = (p0 + p6)*c3 + (p1 + p5)*c2 + (p2 + p4)*c1 + p3*c0
- * Input pixels and coefficients are in Qn format, where n =
- * ISP_VEC_ELEMBITS - 1 (ie Q15 for Broxton)
- * To avoid double precision arithmetic input pixel sum and final sum is
- * implemented using avgrnd and coefficient multiplication using qrmul.
- * Final result is in Qm format where m = ISP_VEC_ELEMBITS - 2 (ie Q14 for
- * Broxton)
-*/
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w
-fir1x7m_sym_innrm_approx(s_1w_1x7_matrix in,
- s_1w_1x4_matrix coeff);
-
-/* @brief Symmetric 7 tap filter with normalization at output side
- *
- * @param[in] in 1x7 matrix with pixels
- * @param[in] coeff 1x4 matrix with coefficients
- *
- * @return symmetric 7 tap filter output
- *
- * This function performs symmetric 7 tap filter over input pixels.
- * Filter sum: p0*c3 + p1*c2 + p2*c1 + p3*c0 + p4*c1 + p5*c2 + p6*c3
- * = (p0 + p6)*c3 + (p1 + p5)*c2 + (p2 + p4)*c1 + p3*c0
- * Input pixels are in Qn and coefficients are in Qm format, where n =
- * ISP_VEC_ELEMBITS - 2 and m = ISP_VEC_ELEMBITS - 1 (ie Q14 and Q15
- * respectively for Broxton)
- * To avoid double precision arithmetic input pixel sum and final sum is
- * implemented using addsat and coefficient multiplication using qrmul.
- * Final sum is left shifted by 2 and saturated to produce result is Qm format
- * (ie Q15 for Broxton)
-*/
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w
-fir1x7m_sym_outnrm_approx(s_1w_1x7_matrix in,
- s_1w_1x4_matrix coeff);
-
-/* @brief 4 tap filter with normalization
- *
- * @param[in] in 1x4 matrix with pixels
- * @param[in] coeff 1x4 matrix with coefficients
- * @param[in] out_shift output pixel shift value for normalization
- *
- * @return 4 tap filter output
- *
- * This function performs 4 tap filter over input pixels.
- * Filter sum is normalized by shifting out_shift bits.
- * Filter sum: p0*c0 + p1*c1 + p2*c2 + p3*c3
-*/
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w
-fir1x4m_nrm(s_1w_1x4_matrix in,
- s_1w_1x4_matrix coeff,
- tvector1w out_shift);
-
-/* @brief 4 tap filter with normalization for half pixel interpolation
- *
- * @param[in] in 1x4 matrix with pixels
- *
- * @return 4 tap filter output with filter tap [-1 9 9 -1]/16
- *
- * This function performs 4 tap filter over input pixels.
- * Filter sum: -p0 + 9*p1 + 9*p2 - p3
- * This filter implementation is completely free from multiplication and double
- * precision arithmetic.
- * Typical usage of this filter is to half pixel interpolation of Bezier
- * surface
- * */
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w
-fir1x4m_bicubic_bezier_half(s_1w_1x4_matrix in);
-
-/* @brief 4 tap filter with normalization for quarter pixel interpolation
- *
- * @param[in] in 1x4 matrix with pixels
- * @param[in] coeff 1x4 matrix with coefficients
- *
- * @return 4 tap filter output
- *
- * This function performs 4 tap filter over input pixels.
- * Filter sum: p0*c0 + p1*c1 + p2*c2 + p3*c3
- * To avoid double precision arithmetic we implemented multiplication using
- * qrmul and addition using avgrnd. Coefficients( c0 to c3) formats are assumed
- * to be: Qm, Qn, Qo, Qm, where m = n + 2 and o = n + 1.
- * Typical usage of this filter is to quarter pixel interpolation of Bezier
- * surface with filter coefficients:[-9 111 29 -3]/128. For which coefficient
- * values should be: [-9216/2^17 28416/2^15 1484/2^16 -3072/2^17] for
- * ISP_VEC_ELEMBITS = 16.
-*/
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w
-fir1x4m_bicubic_bezier_quarter(s_1w_1x4_matrix in,
- s_1w_1x4_matrix coeff);
-
-
-/* @brief Symmetric 3 tap filter with normalization
- *
- * @param[in] in 1x3 matrix with pixels
- * @param[in] coeff 1x2 matrix with coefficients
- * @param[in] out_shift output pixel shift value for normalization
- *
- * @return symmetric 3 tap filter output
- *
- * This function performs symmetric 3 tap filter input pixels.
- * Filter sum is normalized by shifting out_shift bits.
- * Filter sum: p0*c1 + p1*c0 + p2*c1
- * is implemented as: (p0 + p2)*c1 + p1*c0 to reduce multiplication.
- * Input pixels should to be scaled, otherwise overflow is possible during
- * addition
-*/
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w
-fir1x3m_sym_nrm(s_1w_1x3_matrix in,
- s_1w_1x2_matrix coeff,
- tvector1w out_shift);
-
-/* @brief Symmetric 3 tap filter with normalization
- *
- * @param[in] in 1x3 matrix with pixels
- * @param[in] coeff 1x2 matrix with coefficients
- *
- * @return symmetric 3 tap filter output
- *
- * This function performs symmetric 3 tap filter over input pixels.
- * Filter sum: p0*c1 + p1*c0 + p2*c1 = (p0 + p2)*c1 + p1*c0
- * Input pixels are in Qn and coefficient c0 is in Qm and c1 is in Qn format,
- * where n = ISP_VEC_ELEMBITS - 1 and m = ISP_VEC_ELEMBITS - 2 ( ie Q15 and Q14
- * respectively for Broxton)
- * To avoid double precision arithmetic input pixel sum is implemented using
- * avgrnd, coefficient multiplication using qrmul and final sum using addsat
- * Final sum is Qm format (ie Q14 for Broxton)
-*/
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w
-fir1x3m_sym_nrm_approx(s_1w_1x3_matrix in,
- s_1w_1x2_matrix coeff);
-
-/* @brief Mean of 1x3 matrix
- *
- * @param[in] m 1x3 matrix with pixels
- *
- * @return mean of 1x3 matrix
- *
- * This function calculates the mean of 1x3 pixels,
- * with a factor of 4/3.
-*/
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w mean1x3m(
- s_1w_1x3_matrix m);
-
-/* @brief Mean of 3x3 matrix
- *
- * @param[in] m 3x3 matrix with pixels
- *
- * @return mean of 3x3 matrix
- *
- * This function calculates the mean of 3x3 pixels,
- * with a factor of 16/9.
-*/
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w mean3x3m(
- s_1w_3x3_matrix m);
-
-/* @brief Mean of 1x4 matrix
- *
- * @param[in] m 1x4 matrix with pixels
- *
- * @return mean of 1x4 matrix
- *
- * This function calculates the mean of 1x4 pixels
-*/
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w mean1x4m(
- s_1w_1x4_matrix m);
-
-/* @brief Mean of 4x4 matrix
- *
- * @param[in] m 4x4 matrix with pixels
- *
- * @return mean of 4x4 matrix
- *
- * This function calculates the mean of 4x4 matrix with pixels
-*/
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w mean4x4m(
- s_1w_4x4_matrix m);
-
-/* @brief Mean of 2x3 matrix
- *
- * @param[in] m 2x3 matrix with pixels
- *
- * @return mean of 2x3 matrix
- *
- * This function calculates the mean of 2x3 matrix with pixels
- * with a factor of 8/6.
-*/
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w mean2x3m(
- s_1w_2x3_matrix m);
-
-/* @brief Mean of 1x5 matrix
- *
- * @param[in] m 1x5 matrix with pixels
- *
- * @return mean of 1x5 matrix
- *
- * This function calculates the mean of 1x5 matrix with pixels
- * with a factor of 8/5.
-*/
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w mean1x5m(s_1w_1x5_matrix m);
-
-/* @brief Mean of 1x6 matrix
- *
- * @param[in] m 1x6 matrix with pixels
- *
- * @return mean of 1x6 matrix
- *
- * This function calculates the mean of 1x6 matrix with pixels
- * with a factor of 8/6.
-*/
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w mean1x6m(
- s_1w_1x6_matrix m);
-
-/* @brief Mean of 5x5 matrix
- *
- * @param[in] m 5x5 matrix with pixels
- *
- * @return mean of 5x5 matrix
- *
- * This function calculates the mean of 5x5 matrix with pixels
- * with a factor of 32/25.
-*/
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w mean5x5m(
- s_1w_5x5_matrix m);
-
-/* @brief Mean of 6x6 matrix
- *
- * @param[in] m 6x6 matrix with pixels
- *
- * @return mean of 6x6 matrix
- *
- * This function calculates the mean of 6x6 matrix with pixels
- * with a factor of 64/36.
-*/
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w mean6x6m(
- s_1w_6x6_matrix m);
-
-/* @brief Minimum of 4x4 matrix
- *
- * @param[in] m 4x4 matrix with pixels
- *
- * @return minimum of 4x4 matrix
- *
- * This function calculates the minimum of
- * 4x4 matrix with pixels.
-*/
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w min4x4m(
- s_1w_4x4_matrix m);
-
-/* @brief Maximum of 4x4 matrix
- *
- * @param[in] m 4x4 matrix with pixels
- *
- * @return maximum of 4x4 matrix
- *
- * This function calculates the maximum of
- * 4x4 matrix with pixels.
-*/
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w max4x4m(
- s_1w_4x4_matrix m);
-
-/* @brief SAD between two 3x3 matrices
- *
- * @param[in] a 3x3 matrix with pixels
- *
- * @param[in] b 3x3 matrix with pixels
- *
- * @return 3x3 matrix SAD
- *
- * This function calculates the sum of absolute difference between two matrices.
- * Both input pixels and SAD are normalized by a factor of SAD3x3_IN_SHIFT and
- * SAD3x3_OUT_SHIFT respectively.
- * Computed SAD is 1/(2 ^ (SAD3x3_IN_SHIFT + SAD3x3_OUT_SHIFT)) ie 1/16 factor
- * of original SAD and it's more precise than sad3x3m()
-*/
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w sad3x3m_precise(
- s_1w_3x3_matrix a,
- s_1w_3x3_matrix b);
-
-/* @brief SAD between two 3x3 matrices
- *
- * @param[in] a 3x3 matrix with pixels
- *
- * @param[in] b 3x3 matrix with pixels
- *
- * @return 3x3 matrix SAD
- *
- * This function calculates the sum of absolute difference between two matrices.
- * This version saves cycles by avoiding input normalization and wide vector
- * operation during sum computation
- * Input pixel differences are computed by absolute of rounded, halved
- * subtraction. Normalized sum is computed by rounded averages.
- * Computed SAD is (1/2)*(1/16) = 1/32 factor of original SAD. Factor 1/2 comes
- * from input halving operation and factor 1/16 comes from mean operation
-*/
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w sad3x3m(
- s_1w_3x3_matrix a,
- s_1w_3x3_matrix b);
-
-/* @brief SAD between two 5x5 matrices
- *
- * @param[in] a 5x5 matrix with pixels
- *
- * @param[in] b 5x5 matrix with pixels
- *
- * @return 5x5 matrix SAD
- *
- * Computed SAD is = 1/32 factor of original SAD.
-*/
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w sad5x5m(
- s_1w_5x5_matrix a,
- s_1w_5x5_matrix b);
-
-/* @brief Absolute gradient between two sets of 1x5 matrices
- *
- * @param[in] m0 first set of 1x5 matrix with pixels
- * @param[in] m1 second set of 1x5 matrix with pixels
- *
- * @return absolute gradient between two 1x5 matrices
- *
- * This function computes mean of two input 1x5 matrices and returns
- * absolute difference between two mean values.
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w
-absgrad1x5m(s_1w_1x5_matrix m0, s_1w_1x5_matrix m1);
-
-/* @brief Bi-linear Interpolation optimized(approximate)
- *
- * @param[in] a input0
- * @param[in] b input1
- * @param[in] c cloned weight factor
- *
- * @return (a-b)*c + b
- *
- * This function will do bi-linear Interpolation on
- * inputs a and b using constant weight factor c
- *
- * Inputs a,b are assumed in S1.15 format
- * Weight factor has to be in range [0,1] and is assumed to be in S2.14 format
- *
- * The bilinear interpolation equation is (a*c) + b*(1-c),
- * But this is implemented as (a-b)*c + b for optimization
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w OP_1w_bilinear_interpol_approx_c(
- tvector1w a,
- tvector1w b,
- tscalar1w_weight c);
-
-/* @brief Bi-linear Interpolation optimized(approximate)
- *
- * @param[in] a input0
- * @param[in] b input1
- * @param[in] c weight factor
- *
- * @return (a-b)*c + b
- *
- * This function will do bi-linear Interpolation on
- * inputs a and b using weight factor c
- *
- * Inputs a,b are assumed in S1.15 format
- * Weight factor has to be in range [0,1] and is assumed to be in S2.14 format
- *
- * The bilinear interpolation equation is (a*c) + b*(1-c),
- * But this is implemented as (a-b)*c + b for optimization
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w OP_1w_bilinear_interpol_approx(
- tvector1w a,
- tvector1w b,
- tvector1w_weight c);
-
-/* @brief Bi-linear Interpolation
- *
- * @param[in] a input0
- * @param[in] b input1
- * @param[in] c weight factor
- *
- * @return (a*c) + b*(1-c)
- *
- * This function will do bi-linear Interpolation on
- * inputs a and b using weight factor c
- *
- * Inputs a,b are assumed in S1.15 format
- * Weight factor has to be in range [0,1] and is assumed to be in S2.14 format
- *
- * The bilinear interpolation equation is (a*c) + b*(1-c),
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w OP_1w_bilinear_interpol(
- tvector1w a,
- tvector1w b,
- tscalar1w_weight c);
-
-/* @brief Generic Block Matching Algorithm
- * @param[in] search_window pointer to input search window of 16x16 pixels
- * @param[in] ref_block pointer to input reference block of 8x8 pixels, where N<=M
- * @param[in] output pointer to output sads
- * @param[in] search_sz search size for SAD computation
- * @param[in] ref_sz block size
- * @param[in] pixel_shift pixel shift to search the data
- * @param[in] search_block_sz search window block size
- * @param[in] shift shift value, with which the output is shifted right
- *
- * @return 0 when the computation is successful.
-
- * * This function compares the reference block with a block of size NxN in the search
- * window. Sum of absolute differences for each pixel in the reference block and the
- * corresponding pixel in the search block. Whole search window os traversed with the
- * reference block with the given pixel shift.
- *
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H int generic_block_matching_algorithm(
- tscalar1w **search_window,
- tscalar1w **ref_block,
- tscalar1w *output,
- int search_sz,
- int ref_sz,
- int pixel_shift,
- int search_block_sz,
- tscalar1w_4bit_bma_shift shift);
-
-#ifndef ISP2401
-/* @brief OP_1w_asp_bma_16_1_32way
-#else
-/* @brief OP_1w_asp_bma_16_1_32way_nomask
-#endif
- *
- * @param[in] search_area input search window of 16x16 pixels
- * @param[in] input_block input reference block of 8x8 pixels, where N<=M
- * @param[in] shift shift value, with which the output is shifted right
- *
- * @return 81 SADs for all the search blocks.
-
- * This function compares the reference block with a block of size 8x8 pixels in the
- * search window of 16x16 pixels. Sum of absolute differences for each pixel in the
- * reference block and the corresponding pixel in the search block is calculated.
- * Whole search window is traversed with the reference block with the pixel shift of 1
- * pixels. The output is right shifted with the given shift value. The shift value is
- * a 4 bit value.
- *
- */
-
-#ifndef ISP2401
-STORAGE_CLASS_REF_VECTOR_FUNC_H bma_output_16_1 OP_1w_asp_bma_16_1_32way(
-#else
-STORAGE_CLASS_REF_VECTOR_FUNC_H bma_output_16_1 OP_1w_asp_bma_16_1_32way_nomask(
-#endif
- bma_16x16_search_window search_area,
- ref_block_8x8 input_block,
- tscalar1w_4bit_bma_shift shift);
-
-#ifndef ISP2401
-/* @brief OP_1w_asp_bma_16_2_32way
-#else
-/* @brief OP_1w_asp_bma_16_2_32way_nomask
-#endif
- *
- * @param[in] search_area input search window of 16x16 pixels
- * @param[in] input_block input reference block of 8x8 pixels, where N<=M
- * @param[in] shift shift value, with which the output is shifted right
- *
- * @return 25 SADs for all the search blocks.
- * This function compares the reference block with a block of size 8x8 in the search
- * window of 16x61. Sum of absolute differences for each pixel in the reference block
- * and the corresponding pixel in the search block is computed. Whole search window is
- * traversed with the reference block with the given pixel shift of 2 pixels. The output
- * is right shifted with the given shift value. The shift value is a 4 bit value.
- *
- */
-
-#ifndef ISP2401
-STORAGE_CLASS_REF_VECTOR_FUNC_H bma_output_16_2 OP_1w_asp_bma_16_2_32way(
-#else
-STORAGE_CLASS_REF_VECTOR_FUNC_H bma_output_16_2 OP_1w_asp_bma_16_2_32way_nomask(
-#endif
- bma_16x16_search_window search_area,
- ref_block_8x8 input_block,
- tscalar1w_4bit_bma_shift shift);
-#ifndef ISP2401
-/* @brief OP_1w_asp_bma_14_1_32way
-#else
-/* @brief OP_1w_asp_bma_14_1_32way_nomask
-#endif
- *
- * @param[in] search_area input search block of 16x16 pixels with search window of 14x14 pixels
- * @param[in] input_block input reference block of 8x8 pixels, where N<=M
- * @param[in] shift shift value, with which the output is shifted right
- *
- * @return 49 SADs for all the search blocks.
- * This function compares the reference block with a block of size 8x8 in the search
- * window of 14x14. Sum of absolute differences for each pixel in the reference block
- * and the corresponding pixel in the search block. Whole search window is traversed
- * with the reference block with 2 pixel shift. The output is right shifted with the
- * given shift value. The shift value is a 4 bit value. Input is always a 16x16 block
- * but the search window is 14x14, with last 2 pixels of row and column are not used
- * for computation.
- *
- */
-
-#ifndef ISP2401
-STORAGE_CLASS_REF_VECTOR_FUNC_H bma_output_14_1 OP_1w_asp_bma_14_1_32way(
-#else
-STORAGE_CLASS_REF_VECTOR_FUNC_H bma_output_14_1 OP_1w_asp_bma_14_1_32way_nomask(
-#endif
- bma_16x16_search_window search_area,
- ref_block_8x8 input_block,
- tscalar1w_4bit_bma_shift shift);
-
-#ifndef ISP2401
-/* @brief OP_1w_asp_bma_14_2_32way
-#else
-/* @brief OP_1w_asp_bma_14_2_32way_nomask
-#endif
- *
- * @param[in] search_area input search block of 16x16 pixels with search window of 14x14 pixels
- * @param[in] input_block input reference block of 8x8 pixels, where N<=M
- * @param[in] shift shift value, with which the output is shifted right
- *
- * @return 16 SADs for all the search blocks.
- * This function compares the reference block with a block of size 8x8 in the search
- * window of 14x14. Sum of absolute differences for each pixel in the reference block
- * and the corresponding pixel in the search block. Whole search window is traversed
- * with the reference block with 2 pixels shift. The output is right shifted with the
- * given shift value. The shift value is a 4 bit value.
- *
- */
-
-#ifndef ISP2401
-STORAGE_CLASS_REF_VECTOR_FUNC_H bma_output_14_2 OP_1w_asp_bma_14_2_32way(
-#else
-STORAGE_CLASS_REF_VECTOR_FUNC_H bma_output_14_2 OP_1w_asp_bma_14_2_32way_nomask(
-#endif
- bma_16x16_search_window search_area,
- ref_block_8x8 input_block,
- tscalar1w_4bit_bma_shift shift);
-
-#ifdef ISP2401
-/* @brief multiplex addition and passing
- *
- * @param[in] _a first pixel
- * @param[in] _b second pixel
- * @param[in] _c condition flag
- *
- * @return (_a + _b) if condition flag is true
- * _a if condition flag is false
- *
- * This function does multiplex addition depending on the input condition flag
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H tvector1w OP_1w_cond_add(
- tvector1w _a,
- tvector1w _b,
- tflags _c);
-
-#endif
-#ifdef HAS_bfa_unit
-/* @brief OP_1w_single_bfa_7x7
- *
- * @param[in] weights - spatial and range weight lut
- * @param[in] threshold - threshold plane, for range weight scaling
- * @param[in] central_pix - central pixel plane
- * @param[in] src_plane - src pixel plane
- *
- * @return Bilateral filter output
- *
- * This function implements, 7x7 single bilateral filter.
- * Output = {sum(pixel * weight), sum(weight)}
- * Where sum is summation over 7x7 block set.
- * weight = spatial weight * range weight
- * spatial weights are loaded from spatial_weight_lut depending on src pixel
- * position in the 7x7 block
- * range weights are computed by table look up from range_weight_lut depending
- * on scaled absolute difference between src and central pixels.
- * threshold is used as scaling factor. range_weight_lut consists of
- * BFA_RW_LUT_SIZE numbers of LUT entries to model any distribution function.
- * Piecewise linear approximation technique is used to compute range weight
- * It computes absolute difference between central pixel and 61 src pixels.
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H bfa_7x7_output OP_1w_single_bfa_7x7(
- bfa_weights weights,
- tvector1w threshold,
- tvector1w central_pix,
- s_1w_7x7_matrix src_plane);
-
-/* @brief OP_1w_joint_bfa_7x7
- *
- * @param[in] weights - spatial and range weight lut
- * @param[in] threshold0 - 1st threshold plane, for range weight scaling
- * @param[in] central_pix0 - 1st central pixel plane
- * @param[in] src0_plane - 1st pixel plane
- * @param[in] threshold1 - 2nd threshold plane, for range weight scaling
- * @param[in] central_pix1 - 2nd central pixel plane
- * @param[in] src1_plane - 2nd pixel plane
- *
- * @return Joint bilateral filter output
- *
- * This function implements, 7x7 joint bilateral filter.
- * Output = {sum(pixel * weight), sum(weight)}
- * Where sum is summation over 7x7 block set.
- * weight = spatial weight * range weight
- * spatial weights are loaded from spatial_weight_lut depending on src pixel
- * position in the 7x7 block
- * range weights are computed by table look up from range_weight_lut depending
- * on sum of scaled absolute difference between central pixel and two src pixel
- * planes. threshold is used as scaling factor. range_weight_lut consists of
- * BFA_RW_LUT_SIZE numbers of LUT entries to model any distribution function.
- * Piecewise linear approximation technique is used to compute range weight
- * It computes absolute difference between central pixel and 61 src pixels.
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H bfa_7x7_output OP_1w_joint_bfa_7x7(
- bfa_weights weights,
- tvector1w threshold0,
- tvector1w central_pix0,
- s_1w_7x7_matrix src0_plane,
- tvector1w threshold1,
- tvector1w central_pix1,
- s_1w_7x7_matrix src1_plane);
-
-/* @brief bbb_bfa_gen_spatial_weight_lut
- *
- * @param[in] in - 7x7 matrix of spatial weights
- * @param[in] out - generated LUT
- *
- * @return None
- *
- * This function implements, creates spatial weight look up table used
- * for bilaterl filter instruction.
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H void bbb_bfa_gen_spatial_weight_lut(
- s_1w_7x7_matrix in,
- tvector1w out[BFA_MAX_KWAY]);
-
-/* @brief bbb_bfa_gen_range_weight_lut
- *
- * @param[in] in - input range weight,
- * @param[in] out - generated LUT
- *
- * @return None
- *
- * This function implements, creates range weight look up table used
- * for bilaterl filter instruction.
- * 8 unsigned 7b weights are represented in 7 16bits LUT
- * LUT formation is done as follows:
- * higher 8 bit: Point(N) = Point(N+1) - Point(N)
- * lower 8 bit: Point(N) = Point(N)
- * Weight function can be any monotonic decreasing function for x >= 0
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H void bbb_bfa_gen_range_weight_lut(
- tvector1w in[BFA_RW_LUT_SIZE+1],
- tvector1w out[BFA_RW_LUT_SIZE]);
-#endif
-
-#ifdef ISP2401
-/* @brief OP_1w_imax32
- *
- * @param[in] src - structure that holds an array of 32 elements.
- *
- * @return maximum element among input array.
- *
- *This function gets maximum element from an array of 32 elements.
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H int OP_1w_imax32(
- imax32_ref_in_vector src);
-
-/* @brief OP_1w_imaxidx32
- *
- * @param[in] src - structure that holds a vector of elements.
- *
- * @return index of first element with maximum value among array.
- *
- * This function gets index of first element with maximum value
- * from 32 elements.
- */
-STORAGE_CLASS_REF_VECTOR_FUNC_H int OP_1w_imaxidx32(
- imax32_ref_in_vector src);
-
-#endif
-#ifndef INLINE_VECTOR_FUNC
-#define STORAGE_CLASS_REF_VECTOR_FUNC_C
-#define STORAGE_CLASS_REF_VECTOR_DATA_C const
-#else /* INLINE_VECTOR_FUNC */
-#define STORAGE_CLASS_REF_VECTOR_FUNC_C STORAGE_CLASS_REF_VECTOR_FUNC_H
-#define STORAGE_CLASS_REF_VECTOR_DATA_C STORAGE_CLASS_REF_VECTOR_DATA_H
-#include "ref_vector_func.c"
-#define VECTOR_FUNC_INLINED
-#endif /* INLINE_VECTOR_FUNC */
-
-#endif /*_REF_VECTOR_FUNC_H_INCLUDED_*/
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/ref_vector_func_types.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/ref_vector_func_types.h
deleted file mode 100644
index 4dd05eba852e..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/host/ref_vector_func_types.h
+++ /dev/null
@@ -1,385 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __REF_VECTOR_FUNC_TYPES_H_INCLUDED__
-#define __REF_VECTOR_FUNC_TYPES_H_INCLUDED__
-
-
-/*
- * Prerequisites:
- *
- */
-#include "mpmath.h"
-#include "bbb_config.h"
-#include "isp_op1w_types.h"
-#include "isp_op2w_types.h"
-
-/* Defines for the Config Unit */
-#define MAX_CONFIG_POINTS 5
-#define INPUT_OFFSET_FACTOR 10
-#define INPUT_SCALE_FACTOR 10
-#define OUTPUT_SCALE_FACTOR 10
-#define SLOPE_A_RESOLUTION 10
-#define CONFIG_UNIT_LUT_SIZE_32 32 /*XCU works for ISP_NWAY = 32 */
-#define LXCU_LUT_SIZE 16
-#ifdef ISP2401
-#define IMAX32_ELEM_SIZE 32
-#endif
-
-#define ONE_IN_Q14 (1<<(NUM_BITS-2))
-#define Q29_TO_Q15_SHIFT_VAL (NUM_BITS-2)
-#define Q28_TO_Q15_SHIFT_VAL (NUM_BITS-3)
-#define MAX_ELEM(width_in_bits) ((1<<(width_in_bits))-1)
-
-/* Block matching algorithm related data */
-/* NUM_OF_SADS = ((SEARCH_AREA_HEIGHT - REF_BLOCK_HEIGHT)/PIXEL_SHIFT + 1)* \
- ((SEARCH_AREA_WIDTH - REF_BLOCK_WIDTH)/PIXEL_SHIFT + 1) */
-
-#define SADS(sw_h,sw_w, ref_h, ref_w, p_sh) (((sw_h - ref_h)/p_sh + 1)*((sw_w - ref_w)/p_sh + 1))
-#define SADS_16x16_1 SADS(16, 16, 8, 8, 1)
-#define SADS_16x16_2 SADS(16, 16, 8, 8, 2)
-#define SADS_14x14_1 SADS(14, 14, 8, 8, 1)
-#define SADS_14x14_2 SADS(14, 14, 8, 8, 2)
-
-#define BMA_OUTPUT_MATRIX_DIM(sw_h, ref_h, p_sh) ((sw_h - ref_h)/p_sh + 1)
-#define BMA_OUT_16x16_2_32 BMA_OUTPUT_MATRIX_DIM(16, 8, 2)
-#define BMA_OUT_14x14_2_32 BMA_OUTPUT_MATRIX_DIM(14, 8, 2)
-#define BMA_OUT_16x16_1_32 BMA_OUTPUT_MATRIX_DIM(16, 8, 1)
-#define BMA_OUT_14x14_1_32 BMA_OUTPUT_MATRIX_DIM(14, 8, 1)
-#define BMA_SEARCH_BLOCK_SZ_16 16
-#define BMA_REF_BLOCK_SZ_8 8
-#define PIXEL_SHIFT_2 2
-#define PIXEL_SHIFT_1 1
-#define BMA_SEARCH_WIN_SZ_16 16
-#define BMA_SEARCH_WIN_SZ_14 14
-
-
-/*
- * Struct type specification
- */
-
-typedef unsigned short tscalar1w_3bit; /* tscalar1w in interval [0, 2^3) */
-typedef short tscalar1w_5bit_signed; /* tscalar1w in interval [-2^(5-1), 2^(5-1)) */
-typedef unsigned short tscalar1w_5bit; /* tscalar1w in interval [0, 2^5) */
-typedef short tscalar1w_range1wbit; /* tscalar1w in interval [-NUM_BITS, NUM_BITS] */
-typedef short tscalar1w_unsigned_range1wbit; /* tscalar1w in interval [0, NUM_BITS] */
-typedef unsigned short tvector_8bit; /* 8 bit positive number */
-typedef unsigned short tvector_5bit;
-typedef unsigned short tvector_4bit;
-typedef unsigned short tscalar1w_16bit;
-typedef unsigned short tscalar1w_4bit_bma_shift;
-
-typedef struct {
- tvector1w v0 ;
- tvector1w v1 ;
-} s_1w_2x1_matrix;
-
-#define S_1W_2X1_MATRIX_DEFAULT ((s_1w_2x1_matrix)\
- { 0, 0 })
-
-typedef struct {
- tvector1w v00;
- tvector1w v01;
-} s_1w_1x2_matrix;
-
-#define S_1W_1X2_MATRIX_DEFAULT ((s_1w_1x2_matrix)\
- { 0, 0 })
-
-typedef struct {
- tvector1w v00 ;
- tvector1w v01 ;
- tvector1w v02 ;
-} s_1w_1x3_matrix;
-
-#define S_1W_1X3_MATRIX_DEFAULT ((s_1w_1x3_matrix)\
- { 0, 0, 0, })
-
-typedef struct {
- tvector1w v00; tvector1w v01; tvector1w v02;
- tvector1w v10; tvector1w v11; tvector1w v12;
-} s_1w_2x3_matrix;
-
-#define S_1W_2X3_MATRIX_DEFAULT ((s_1w_2x3_matrix)\
- { 0, 0, 0, \
- 0, 0, 0 })
-
-typedef struct {
- tvector1w v00 ; tvector1w v01 ; tvector1w v02 ;
- tvector1w v10 ; tvector1w v11 ; tvector1w v12 ;
- tvector1w v20 ; tvector1w v21 ; tvector1w v22 ;
-} s_1w_3x3_matrix;
-
-#define S_1W_3X3_MATRIX_DEFAULT ((s_1w_3x3_matrix)\
- { 0, 0, 0, \
- 0, 0, 0, \
- 0, 0, 0 })
-
-typedef struct {
- tvector1w v00 ; tvector1w v01 ; tvector1w v02 ;
- tvector1w v10 ; tvector1w v11 ; tvector1w v12 ;
- tvector1w v20 ; tvector1w v21 ; tvector1w v22 ;
- tvector1w v30 ; tvector1w v31 ; tvector1w v32 ;
-} s_1w_4x3_matrix;
-
-#define S_1W_4X3_MATRIX_DEFAULT ((s_1w_4x3_matrix)\
- { 0, 0, 0, \
- 0, 0, 0, \
- 0, 0, 0, \
- 0, 0, 0 })
-
-typedef struct {
- tvector1w v00 ;
- tvector1w v01 ;
- tvector1w v02 ;
- tvector1w v03 ;
- tvector1w v04 ;
-} s_1w_1x5_matrix;
-
-#define S_1W_1X5_MATRIX_DEFAULT ((s_1w_1x5_matrix)\
- { 0, 0, 0, 0, 0 })
-
-typedef struct {
- tvector1w v00 ; tvector1w v01 ; tvector1w v02 ; tvector1w v03 ; tvector1w v04 ;
- tvector1w v10 ; tvector1w v11 ; tvector1w v12 ; tvector1w v13 ; tvector1w v14 ;
- tvector1w v20 ; tvector1w v21 ; tvector1w v22 ; tvector1w v23 ; tvector1w v24 ;
- tvector1w v30 ; tvector1w v31 ; tvector1w v32 ; tvector1w v33 ; tvector1w v34 ;
- tvector1w v40 ; tvector1w v41 ; tvector1w v42 ; tvector1w v43 ; tvector1w v44 ;
-} s_1w_5x5_matrix;
-
-#define S_1W_5X5_MATRIX_DEFAULT ((s_1w_5x5_matrix)\
- { 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0 })
-#ifndef ISP2401
-
-#else
-
-#endif
-typedef struct {
- tvector1w v00;
- tvector1w v01;
- tvector1w v02;
- tvector1w v03;
- tvector1w v04;
- tvector1w v05;
- tvector1w v06;
-} s_1w_1x7_matrix;
-
-#define S_1W_1X7_MATRIX_DEFAULT ((s_1w_1x7_matrix)\
- { 0, 0, 0, 0, 0, 0, 0 })
-
-typedef struct {
- tvector1w v00;
- tvector1w v01;
- tvector1w v02;
- tvector1w v03;
- tvector1w v04;
- tvector1w v05;
- tvector1w v06;
- tvector1w v07;
- tvector1w v08;
-} s_1w_1x9_matrix;
-
-#define S_1W_1X9_MATRIX_DEFAULT ((s_1w_1x9_matrix)\
- { 0, 0, 0, 0, 0, 0, 0, 0, 0 })
-
-typedef struct {
- tvector1w v00;
- tvector1w v01;
- tvector1w v02;
- tvector1w v03;
-} s_1w_1x4_matrix;
-
-#define S_1W_1X4_MATRIX ((s_1w_1x4_matrix)\
- { 0, 0, 0, 0 })
-
-typedef struct {
- tvector1w v00; tvector1w v01; tvector1w v02; tvector1w v03;
- tvector1w v10; tvector1w v11; tvector1w v12; tvector1w v13;
- tvector1w v20; tvector1w v21; tvector1w v22; tvector1w v23;
- tvector1w v30; tvector1w v31; tvector1w v32; tvector1w v33;
-} s_1w_4x4_matrix;
-
-#define S_1W_4X4_MATRIX_DEFAULT ((s_1w_4x4_matrix)\
- { 0, 0, 0, 0, \
- 0, 0, 0, 0, \
- 0, 0, 0, 0, \
- 0, 0, 0, 0 })
-
-typedef struct {
- tvector1w v00;
- tvector1w v01;
- tvector1w v02;
- tvector1w v03;
- tvector1w v04;
- tvector1w v05;
-} s_1w_1x6_matrix;
-
-#define S_1W_1X6_MATRIX_DEFAULT ((s_1w_1x6_matrix)\
- { 0, 0, 0, 0, 0, 0 })
-
-typedef struct {
- tvector1w v00; tvector1w v01; tvector1w v02; tvector1w v03; tvector1w v04; tvector1w v05;
- tvector1w v10; tvector1w v11; tvector1w v12; tvector1w v13; tvector1w v14; tvector1w v15;
- tvector1w v20; tvector1w v21; tvector1w v22; tvector1w v23; tvector1w v24; tvector1w v25;
- tvector1w v30; tvector1w v31; tvector1w v32; tvector1w v33; tvector1w v34; tvector1w v35;
- tvector1w v40; tvector1w v41; tvector1w v42; tvector1w v43; tvector1w v44; tvector1w v45;
- tvector1w v50; tvector1w v51; tvector1w v52; tvector1w v53; tvector1w v54; tvector1w v55;
-} s_1w_6x6_matrix;
-
-#define S_1W_6X6_MATRIX_DEFAULT ((s_1w_6x6_matrix)\
- { 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0 })
-
-typedef struct {
- tvector1w v00; tvector1w v01; tvector1w v02; tvector1w v03; tvector1w v04;
- tvector1w v05; tvector1w v06; tvector1w v07; tvector1w v08;
- tvector1w v10; tvector1w v11; tvector1w v12; tvector1w v13; tvector1w v14;
- tvector1w v15; tvector1w v16; tvector1w v17; tvector1w v18;
- tvector1w v20; tvector1w v21; tvector1w v22; tvector1w v23; tvector1w v24;
- tvector1w v25; tvector1w v26; tvector1w v27; tvector1w v28;
- tvector1w v30; tvector1w v31; tvector1w v32; tvector1w v33; tvector1w v34;
- tvector1w v35; tvector1w v36; tvector1w v37; tvector1w v38;
- tvector1w v40; tvector1w v41; tvector1w v42; tvector1w v43; tvector1w v44;
- tvector1w v45; tvector1w v46; tvector1w v47; tvector1w v48;
- tvector1w v50; tvector1w v51; tvector1w v52; tvector1w v53; tvector1w v54;
- tvector1w v55; tvector1w v56; tvector1w v57; tvector1w v58;
- tvector1w v60; tvector1w v61; tvector1w v62; tvector1w v63; tvector1w v64;
- tvector1w v65; tvector1w v66; tvector1w v67; tvector1w v68;
- tvector1w v70; tvector1w v71; tvector1w v72; tvector1w v73; tvector1w v74;
- tvector1w v75; tvector1w v76; tvector1w v77; tvector1w v78;
- tvector1w v80; tvector1w v81; tvector1w v82; tvector1w v83; tvector1w v84;
- tvector1w v85; tvector1w v86; tvector1w v87; tvector1w v88;
-} s_1w_9x9_matrix;
-
-#define S_1W_9X9_MATRIX_DEFAULT ((s_1w_9x9_matrix)\
- { 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0 })
-
-typedef struct {
- tvector1w v00; tvector1w v01; tvector1w v02; tvector1w v03; tvector1w v04;
- tvector1w v05; tvector1w v06;
- tvector1w v10; tvector1w v11; tvector1w v12; tvector1w v13; tvector1w v14;
- tvector1w v15; tvector1w v16;
- tvector1w v20; tvector1w v21; tvector1w v22; tvector1w v23; tvector1w v24;
- tvector1w v25; tvector1w v26;
- tvector1w v30; tvector1w v31; tvector1w v32; tvector1w v33; tvector1w v34;
- tvector1w v35; tvector1w v36;
- tvector1w v40; tvector1w v41; tvector1w v42; tvector1w v43; tvector1w v44;
- tvector1w v45; tvector1w v46;
- tvector1w v50; tvector1w v51; tvector1w v52; tvector1w v53; tvector1w v54;
- tvector1w v55; tvector1w v56;
- tvector1w v60; tvector1w v61; tvector1w v62; tvector1w v63; tvector1w v64;
- tvector1w v65; tvector1w v66;
-} s_1w_7x7_matrix;
-
-#define S_1W_7X7_MATRIX_DEFAULT ((s_1w_7x7_matrix)\
- { 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0 })
-
-typedef struct {
- tvector1w v0_0;
- tvector1w v0_1;
- tvector1w v0_2;
- tvector1w v0_3;
- tvector1w v0_4;
- tvector1w v0_5;
- tvector1w v0_6;
- tvector1w v0_7;
- tvector1w v0_8;
- tvector1w v0_9;
- tvector1w v0_10;
-} s_1w_1x11_matrix;
-
-#define S_1W_1X11_MATRIX_DEFAULT ((s_1w_1x11_matrix)\
- { 0, 0, 0, 0, 0, 0, 0, 0, 0 })
-
-typedef struct {
- tvector1w x_cord[MAX_CONFIG_POINTS];
- tvector1w slope[MAX_CONFIG_POINTS-1];
- tvector1w y_offset[MAX_CONFIG_POINTS-1];
-} ref_config_points;
-
-typedef struct {
- tscalar1w_range1wbit slope_vec[CONFIG_UNIT_LUT_SIZE_32];
- tscalar1w_range1wbit offset_vec[CONFIG_UNIT_LUT_SIZE_32];
- tscalar1w_16bit x_cord_vec[CONFIG_UNIT_LUT_SIZE_32];
- tscalar1w_16bit x_cord_max;
- tscalar1w_5bit exponent;
- tscalar1w_5bit slope_resolution;
-} xcu_ref_init_vectors;
-
-typedef struct {
-#ifdef ISP2401
- tvector1w elem[IMAX32_ELEM_SIZE];
-} imax32_ref_in_vector;
-
-typedef struct {
-#endif
- tscalar1w search[BMA_SEARCH_BLOCK_SZ_16][BMA_SEARCH_BLOCK_SZ_16];
-} bma_16x16_search_window;
-
-typedef struct {
- tscalar1w ref[BMA_REF_BLOCK_SZ_8][BMA_REF_BLOCK_SZ_8];
-} ref_block_8x8;
-
-typedef struct {
- tscalar1w sads[SADS_16x16_1];
-} bma_output_16_1;
-
-typedef struct {
- tscalar1w sads[SADS_16x16_2];
-} bma_output_16_2;
-
-typedef struct {
- tscalar1w sads[SADS_14x14_2];
-} bma_output_14_2;
-
-typedef struct {
- tscalar1w sads[SADS_14x14_1];
-} bma_output_14_1;
-
-typedef struct {
- tvector1w spatial_weight_lut[BFA_MAX_KWAY]; /* spatial weight LUT */
- /* range weight LUT, (BFA_RW_LUT_SIZE + 1) numbers of LUT values are compressed in BFA_RW_LUT_SIZE buffer.
- * range_weight_lut[k] = packed(drop[k], range_weight[k])
- * where, drop[k] = range_weight[k+1] - range_weight[k]
- * pack(msb, lsb): two 8bits numbers packed in one 16bits number */
- tvector1w range_weight_lut[BFA_RW_LUT_SIZE];
-} bfa_weights;
-
-/* Return type for BFA BBBs */
-typedef struct {
- tvector2w sop; /* weighted sum of pixels */
- tvector1w sow; /* sum of weights */
-} bfa_7x7_output;
-#endif /* __REF_VECTOR_FUNC_TYPES_H_INCLUDED__ */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/mpmath.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/mpmath.h
deleted file mode 100644
index cd938375e02e..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/mpmath.h
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __MPMATH_H_INCLUDED__
-#define __MPMATH_H_INCLUDED__
-
-
-#ifdef INLINE_MPMATH
-#define STORAGE_CLASS_MPMATH_FUNC_H static inline
-#define STORAGE_CLASS_MPMATH_DATA_H static inline_DATA
-#else /* INLINE_MPMATH */
-#define STORAGE_CLASS_MPMATH_FUNC_H extern
-#define STORAGE_CLASS_MPMATH_DATA_H extern_DATA
-#endif /* INLINE_MPMATH */
-
-#include <type_support.h>
-
-/*
- * Implementation limits
- */
-#define MIN_BITDEPTH 1
-#define MAX_BITDEPTH 64
-
-#define ROUND_NEAREST_EVEN 0
-#define ROUND_NEAREST 1
-
-/*
- * The MP types
- *
- * "vector lane data" is scalar. With "scalar data" for limited range shift and address values
- */
-typedef unsigned long long mpudata_t; /* Type of reference MP scalar / vector lane data; unsigned */
-typedef long long mpsdata_t; /* Type of reference MP scalar / vector lane data; signed */
-typedef unsigned short spudata_t; /* Type of reference SP scalar / vector lane data; unsigned */
-typedef short spsdata_t; /* Type of reference SP scalar / vector lane data; signed */
-typedef unsigned short bitdepth_t;
-
-typedef enum {
- mp_zero_ID,
- mp_one_ID,
- mp_mone_ID,
- mp_smin_ID,
- mp_smax_ID,
- mp_umin_ID,
- mp_umax_ID,
- N_mp_const_ID
-} mp_const_ID_t;
-
-#ifdef ISP2401
-/* _isValidMpudata is for internal use by mpmath and bbb's.
- * isValidMpudata is for external use by functions on top.
- */
-#ifndef ENABLE_VALID_MP_DATA_CHECK
-#define _isValidMpsdata(data,bitdepth) (1)
-#define _isValidMpudata(data,bitdepth) (1)
-#else
-#define _isValidMpsdata(data,bitdepth) isValidMpsdata(data,bitdepth)
-#define _isValidMpudata(data,bitdepth) isValidMpsdata(data,bitdepth)
-
-#endif
-#endif
-STORAGE_CLASS_MPMATH_FUNC_H bool isValidMpsdata(
- const mpsdata_t data,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H bool isValidMpudata(
- const mpudata_t data,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_castd (
- const mpsdata_t in0,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_casth (
- const mpsdata_t in0,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_scasth (
- const mpsdata_t in0,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_qcastd (
- const mpsdata_t in0,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_qcasth (
- const mpsdata_t in0,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_qrcasth (
- const mpsdata_t in0,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_abs (
- const mpsdata_t in0,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_limit (
- const mpsdata_t bnd_low,
- const mpsdata_t in0,
- const mpsdata_t bnd_high,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_max (
- const mpsdata_t in0,
- const mpsdata_t in1,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_min (
- const mpsdata_t in0,
- const mpsdata_t in1,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_mux (
- const spudata_t sel,
- const mpsdata_t in0,
- const mpsdata_t in1,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_rmux (
- const spudata_t sel,
- const mpsdata_t in0,
- const mpsdata_t in1,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_add (
- const mpsdata_t in0,
- const mpsdata_t in1,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_sadd (
- const mpsdata_t in0,
- const mpsdata_t in1,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_sub (
- const mpsdata_t in0,
- const mpsdata_t in1,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_ssub (
- const mpsdata_t in0,
- const mpsdata_t in1,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_addasr1 (
- const mpsdata_t in0,
- const mpsdata_t in1,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_subasr1 (
- const mpsdata_t in0,
- const mpsdata_t in1,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_lsr (
- const mpsdata_t in0,
- const spsdata_t shft,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_asr (
- const mpsdata_t in0,
- const spsdata_t shft,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_rasr (
- const mpsdata_t in0,
- const spsdata_t shft,
- const bitdepth_t bitdepth);
-
-/* "mp_rasr_u()" is implemented by "mp_rasr()" */
-STORAGE_CLASS_MPMATH_FUNC_H mpudata_t mp_rasr_u (
- const mpudata_t in0,
- const spsdata_t shft,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_lsl (
- const mpsdata_t in0,
- const spsdata_t shft,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_asl (
- const mpsdata_t in0,
- const spsdata_t shft,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_muld (
- const mpsdata_t in0,
- const mpsdata_t in1,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_mul (
- const mpsdata_t in0,
- const mpsdata_t in1,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_qmul (
- const mpsdata_t in0,
- const mpsdata_t in1,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_qrmul (
- const mpsdata_t in0,
- const mpsdata_t in1,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_qdiv (
- const mpsdata_t in0,
- const mpsdata_t in1,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_qdivh (
- const mpsdata_t in0,
- const mpsdata_t in1,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_div (
- const mpsdata_t in0,
- const mpsdata_t in1,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_divh (
- const mpsdata_t in0,
- const mpsdata_t in1,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_and (
- const mpsdata_t in0,
- const mpsdata_t in1,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_compl (
- const mpsdata_t in0,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_or (
- const mpsdata_t in0,
- const mpsdata_t in1,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_xor (
- const mpsdata_t in0,
- const mpsdata_t in1,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H spudata_t mp_isEQ (
- const mpsdata_t in0,
- const mpsdata_t in1,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H spudata_t mp_isNE (
- const mpsdata_t in0,
- const mpsdata_t in1,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H spudata_t mp_isGT (
- const mpsdata_t in0,
- const mpsdata_t in1,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H spudata_t mp_isGE (
- const mpsdata_t in0,
- const mpsdata_t in1,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H spudata_t mp_isLT (
- const mpsdata_t in0,
- const mpsdata_t in1,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H spudata_t mp_isLE (
- const mpsdata_t in0,
- const mpsdata_t in1,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H spudata_t mp_isEQZ (
- const mpsdata_t in0,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H spudata_t mp_isNEZ (
- const mpsdata_t in0,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H spudata_t mp_isGTZ (
- const mpsdata_t in0,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H spudata_t mp_isGEZ (
- const mpsdata_t in0,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H spudata_t mp_isLTZ (
- const mpsdata_t in0,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H spudata_t mp_isLEZ (
- const mpsdata_t in0,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpsdata_t mp_const (
- const mp_const_ID_t ID,
- const bitdepth_t bitdepth);
-
-STORAGE_CLASS_MPMATH_FUNC_H mpudata_t mp_sqrt_u(
- const mpudata_t in0,
- const bitdepth_t bitdepth);
-
-#ifndef INLINE_MPMATH
-#define STORAGE_CLASS_MPMATH_FUNC_C
-#define STORAGE_CLASS_MPMATH_DATA_C const
-#else /* INLINE_MPMATH */
-#define STORAGE_CLASS_MPMATH_FUNC_C STORAGE_CLASS_MPMATH_FUNC_H
-#define STORAGE_CLASS_MPMATH_DATA_C STORAGE_CLASS_MPMATH_DATA_H
-#include "mpmath.c"
-#define MPMATH_INLINED
-#endif /* INLINE_MPMATH */
-
-#endif /* __MPMATH_H_INCLUDED__ */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/osys.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/osys.h
deleted file mode 100644
index a607242c5f1a..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/osys.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __OSYS_H_INCLUDED__
-#define __OSYS_H_INCLUDED__
-
-/*
- * This file is included on every cell {SP,ISP,host} and on every system
- * that uses the OSYS device. It defines the API to DLI bridge
- *
- * System and cell specific interfaces and inline code are included
- * conditionally through Makefile path settings.
- *
- * - . system and cell agnostic interfaces, constants and identifiers
- * - public: system agnostic, cell specific interfaces
- * - private: system dependent, cell specific interfaces & inline implementations
- * - global: system specific constants and identifiers
- * - local: system and cell specific constants and identifiers
- *
- */
-
-
-#include "system_local.h"
-#include "osys_local.h"
-
-#ifndef __INLINE_OSYS__
-#define STORAGE_CLASS_OSYS_H extern
-#define STORAGE_CLASS_OSYS_C
-#include "osys_public.h"
-#else /* __INLINE_OSYS__ */
-#define STORAGE_CLASS_OSYS_H static inline
-#define STORAGE_CLASS_OSYS_C static inline
-#include "osys_private.h"
-#endif /* __INLINE_OSYS__ */
-
-#endif /* __OSYS_H_INCLUDED__ */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/stream_buffer.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/stream_buffer.h
deleted file mode 100644
index 53d535e4f2ae..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/stream_buffer.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __STREAM_BUFFER_H_INCLUDED__
-#define __STREAM_BUFFER_H_INCLUDED__
-
-/*
- * This file is included on every cell {SP,ISP,host} and on every system
- * that uses the DMA device. It defines the API to DLI bridge
- *
- * System and cell specific interfaces and inline code are included
- * conditionally through Makefile path settings.
- *
- * - . system and cell agnostic interfaces, constants and identifiers
- * - public: system agnostic, cell specific interfaces
- * - private: system dependent, cell specific interfaces & inline implementations
- * - global: system specific constants and identifiers
- * - local: system and cell specific constants and identifiers
- *
- */
-
-
-#include "system_local.h"
-#include "stream_buffer_local.h"
-
-#ifndef __INLINE_STREAM_BUFFER__
-#define STORAGE_CLASS_STREAM_BUFFER_H extern
-#define STORAGE_CLASS_STREAM_BUFFER_C
-#include "stream_buffer_public.h"
-#else /* __INLINE_STREAM_BUFFER__ */
-#define STORAGE_CLASS_STREAM_BUFFER_H static inline
-#define STORAGE_CLASS_STREAM_BUFFER_C static inline
-#include "stream_buffer_private.h"
-#endif /* __INLINE_STREAM_BUFFER__ */
-
-#endif /* __STREAM_BUFFER_H_INCLUDED__ */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/vector_func.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/vector_func.h
deleted file mode 100644
index 5368b9062897..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/vector_func.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __VECTOR_FUNC_H_INCLUDED__
-#define __VECTOR_FUNC_H_INCLUDED__
-
-
-/* TODO: Later filters will be moved to types directory,
- * and we should only include matrix_MxN types */
-#include "filters/filters_1.0/filter_2x2.h"
-#include "filters/filters_1.0/filter_3x3.h"
-#include "filters/filters_1.0/filter_4x4.h"
-#include "filters/filters_1.0/filter_5x5.h"
-
-#include "vector_func_local.h"
-
-#ifndef __INLINE_VECTOR_FUNC__
-#define STORAGE_CLASS_VECTOR_FUNC_H extern
-#define STORAGE_CLASS_VECTOR_FUNC_C
-#include "vector_func_public.h"
-#else /* __INLINE_VECTOR_FUNC__ */
-#define STORAGE_CLASS_VECTOR_FUNC_H static inline
-#define STORAGE_CLASS_VECTOR_FUNC_C static inline
-#include "vector_func_private.h"
-#endif /* __INLINE_VECTOR_FUNC__ */
-
-#endif /* __VECTOR_FUNC_H_INCLUDED__ */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/vector_ops.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/vector_ops.h
deleted file mode 100644
index 4923f2d5518b..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/vector_ops.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __VECTOR_OPS_H_INCLUDED__
-#define __VECTOR_OPS_H_INCLUDED__
-
-
-#include "vector_ops_local.h"
-
-#ifndef __INLINE_VECTOR_OPS__
-#define STORAGE_CLASS_VECTOR_OPS_H extern
-#define STORAGE_CLASS_VECTOR_OPS_C
-#include "vector_ops_public.h"
-#else /* __INLINE_VECTOR_OPS__ */
-#define STORAGE_CLASS_VECTOR_OPS_H static inline
-#define STORAGE_CLASS_VECTOR_OPS_C static inline
-#include "vector_ops_private.h"
-#endif /* __INLINE_VECTOR_OPS__ */
-
-#endif /* __VECTOR_OPS_H_INCLUDED__ */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/xmem.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/xmem.h
deleted file mode 100644
index 13083fe55141..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_include/xmem.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __XMEM_H_INCLUDED__
-#define __XMEM_H_INCLUDED__
-
-/*
- * This file is included on every cell {SP,ISP,host} and on every system
- * that uses the XMEM device. It defines the API to DLI bridge
- *
- * System and cell specific interfaces and inline code are included
- * conditionally through Makefile path settings.
- *
- * - . system and cell agnostic interfaces, constants and identifiers
- * - public: system agnostic, cell specific interfaces
- * - private: system dependent, cell specific interfaces & inline implementations
- * - global: system specific constants and identifiers
- * - local: system and cell specific constants and identifiers
- */
-
-
-#include "system_local.h"
-#include "xmem_local.h"
-
-#ifndef __INLINE_XMEM__
-#define STORAGE_CLASS_XMEM_H extern
-#define STORAGE_CLASS_XMEM_C
-#include "xmem_public.h"
-#else /* __INLINE_XMEM__ */
-#define STORAGE_CLASS_XMEM_H static inline
-#define STORAGE_CLASS_XMEM_C static inline
-#include "xmem_private.h"
-#endif /* __INLINE_XMEM__ */
-
-#endif /* __XMEM_H_INCLUDED__ */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_shared/socket_global.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_shared/socket_global.h
deleted file mode 100644
index 2b7025e90250..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_shared/socket_global.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __SOCKET_GLOBAL_H_INCLUDED__
-#define __SOCKET_GLOBAL_H_INCLUDED__
-
-#include "stream_buffer.h"
-
-/* define the socket port direction */
-typedef enum {
- SOCKET_PORT_DIRECTION_NULL,
- SOCKET_PORT_DIRECTION_IN,
- SOCKET_PORT_DIRECTION_OUT
-} socket_port_direction_t;
-
-/* pointer to the port's callout function */
-typedef void (*socket_port_callout_fp)(void);
-typedef struct socket_port_s socket_port_t;
-typedef struct socket_s socket_t;
-
-/* data structure of the socket port */
-struct socket_port_s {
- unsigned channel; /* the port entity */
- socket_port_direction_t direction; /* the port direction */
- socket_port_callout_fp callout; /* the port callout function */
-
- socket_t *socket; /* point to the socket */
-
- struct {
- unsigned data;
- } buf; /* the buffer at the port */
-};
-
-/* data structure of the socket */
-struct socket_s {
- socket_port_t *in; /* the in-direction port */
- socket_port_t *out; /* the out-direction port */
- stream_buffer_t buf; /* the buffer between in-ports and out-ports */
-};
-
-#endif /* __SOCKET_GLOBAL_H_INCLUDED__ */
-
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_shared/stream_buffer_global.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_shared/stream_buffer_global.h
deleted file mode 100644
index b9664b9608dc..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/hive_isp_css_shared/stream_buffer_global.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __STREAM_BUFFER_GLOBAL_H_INCLUDED__
-#define __STREAM_BUFFER_GLOBAL_H_INCLUDED__
-
-typedef struct stream_buffer_s stream_buffer_t;
-struct stream_buffer_s {
- unsigned base;
- unsigned limit;
- unsigned top;
-};
-
-#endif /* __STREAM_BUFFER_GLOBAL_H_INCLUDED__ */
-
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_frame_public.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_frame_public.h
index ba7a076c3afa..0beb7347a4f3 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_frame_public.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_frame_public.h
@@ -121,15 +121,9 @@ struct ia_css_frame_info {
};
#define IA_CSS_BINARY_DEFAULT_FRAME_INFO \
-{ \
- {0, /* width */ \
- 0}, /* height */ \
- 0, /* padded_width */ \
- IA_CSS_FRAME_FORMAT_NUM, /* format */ \
- 0, /* raw_bit_depth */ \
- IA_CSS_BAYER_ORDER_NUM, /* raw_bayer_order */ \
- {0, /*start col */ \
- 0}, /*start line */ \
+(struct ia_css_frame_info) { \
+ .format = IA_CSS_FRAME_FORMAT_NUM, \
+ .raw_bayer_order = IA_CSS_BAYER_ORDER_NUM, \
}
/**
@@ -190,18 +184,11 @@ struct ia_css_frame {
};
#define DEFAULT_FRAME \
-{ \
- IA_CSS_BINARY_DEFAULT_FRAME_INFO, /* info */ \
- 0, /* data */ \
- 0, /* data_bytes */ \
- SH_CSS_INVALID_QUEUE_ID, /* dynamic_data_index */ \
- IA_CSS_BUFFER_TYPE_INVALID, /* buf_type */ \
- IA_CSS_FRAME_FLASH_STATE_NONE, /* flash_state */ \
- 0, /* exp_id */ \
- 0, /* isp_config_id */ \
- false, /* valid */ \
- false, /* contiguous */ \
- { 0 } /* planes */ \
+(struct ia_css_frame) { \
+ .info = IA_CSS_BINARY_DEFAULT_FRAME_INFO, \
+ .dynamic_queue_id = SH_CSS_INVALID_QUEUE_ID, \
+ .buf_type = IA_CSS_BUFFER_TYPE_INVALID, \
+ .flash_state = IA_CSS_FRAME_FLASH_STATE_NONE, \
}
/* @brief Fill a frame with zeros
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_input_port.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_input_port.h
index f415570a3da9..ad9ca5449369 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_input_port.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_input_port.h
@@ -12,6 +12,9 @@
* more details.
*/
+/* For MIPI_PORT0_ID to MIPI_PORT2_ID */
+#include "system_global.h"
+
#ifndef __IA_CSS_INPUT_PORT_H
#define __IA_CSS_INPUT_PORT_H
@@ -19,21 +22,12 @@
* This file contains information about the possible input ports for CSS
*/
-/* Enumeration of the physical input ports on the CSS hardware.
- * There are 3 MIPI CSI-2 ports.
- */
-enum ia_css_csi2_port {
- IA_CSS_CSI2_PORT0, /* Implicitly map to MIPI_PORT0_ID */
- IA_CSS_CSI2_PORT1, /* Implicitly map to MIPI_PORT1_ID */
- IA_CSS_CSI2_PORT2 /* Implicitly map to MIPI_PORT2_ID */
-};
-
/* Backward compatible for CSS API 2.0 only
* TO BE REMOVED when all drivers move to CSS API 2.1
*/
-#define IA_CSS_CSI2_PORT_4LANE IA_CSS_CSI2_PORT0
-#define IA_CSS_CSI2_PORT_1LANE IA_CSS_CSI2_PORT1
-#define IA_CSS_CSI2_PORT_2LANE IA_CSS_CSI2_PORT2
+#define IA_CSS_CSI2_PORT_4LANE MIPI_PORT0_ID
+#define IA_CSS_CSI2_PORT_1LANE MIPI_PORT1_ID
+#define IA_CSS_CSI2_PORT_2LANE MIPI_PORT2_ID
/* The CSI2 interface supports 2 types of compression or can
* be run without compression.
@@ -56,7 +50,7 @@ struct ia_css_csi2_compression {
/* Input port structure.
*/
struct ia_css_input_port {
- enum ia_css_csi2_port port; /** Physical CSI-2 port */
+ enum mipi_port_id port; /** Physical CSI-2 port */
unsigned int num_lanes; /** Number of lanes used (4-lane port only) */
unsigned int timeout; /** Timeout value */
unsigned int rxcount; /** Register value, should include all lanes */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_irq.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_irq.h
index 10ef61178bb2..c8840138899a 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_irq.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_irq.h
@@ -186,7 +186,7 @@ ia_css_rx_get_irq_info(unsigned int *irq_bits);
* that occurred.
*/
void
-ia_css_rx_port_get_irq_info(enum ia_css_csi2_port port, unsigned int *irq_bits);
+ia_css_rx_port_get_irq_info(enum mipi_port_id port, unsigned int *irq_bits);
/* @brief Clear CSI receiver error info.
*
@@ -218,7 +218,7 @@ ia_css_rx_clear_irq_info(unsigned int irq_bits);
* error bits get overwritten.
*/
void
-ia_css_rx_port_clear_irq_info(enum ia_css_csi2_port port, unsigned int irq_bits);
+ia_css_rx_port_clear_irq_info(enum mipi_port_id port, unsigned int irq_bits);
/* @brief Enable or disable specific interrupts.
*
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_metadata.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_metadata.h
index 8b674c98224c..ed0b6ab371da 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_metadata.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_metadata.h
@@ -27,8 +27,8 @@
* to process sensor metadata.
*/
struct ia_css_metadata_config {
- enum ia_css_stream_format data_type; /** Data type of CSI-2 embedded
- data. The default value is IA_CSS_STREAM_FORMAT_EMBEDDED. For
+ enum atomisp_input_format data_type; /** Data type of CSI-2 embedded
+ data. The default value is ATOMISP_INPUT_FORMAT_EMBEDDED. For
certain sensors, user can choose non-default data type for embedded
data. */
struct ia_css_resolution resolution; /** Resolution */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_mipi.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_mipi.h
index f9c9cd76be97..367b2aafa5e8 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_mipi.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_mipi.h
@@ -55,7 +55,7 @@ ia_css_mipi_frame_specify(const unsigned int size_mem_words,
*
*/
enum ia_css_err
-ia_css_mipi_frame_enable_check_on_size(const enum ia_css_csi2_port port,
+ia_css_mipi_frame_enable_check_on_size(const enum mipi_port_id port,
const unsigned int size_mem_words);
#endif
@@ -74,7 +74,7 @@ ia_css_mipi_frame_enable_check_on_size(const enum ia_css_csi2_port port,
enum ia_css_err
ia_css_mipi_frame_calculate_size(const unsigned int width,
const unsigned int height,
- const enum ia_css_stream_format format,
+ const enum atomisp_input_format format,
const bool hasSOLandEOL,
const unsigned int embedded_data_size_words,
unsigned int *size_mem_words);
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_pipe.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_pipe.h
index d0c0e6b92025..f6870fa7a18c 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_pipe.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_pipe.h
@@ -33,22 +33,17 @@ struct ia_css_preview_settings {
/* 2401 only for these two - do we in fact use them for anything real */
struct ia_css_frame *delay_frames[MAX_NUM_DELAY_FRAMES];
struct ia_css_frame *tnr_frames[NUM_TNR_FRAMES];
-
+
struct ia_css_pipe *copy_pipe;
struct ia_css_pipe *capture_pipe;
struct ia_css_pipe *acc_pipe;
};
#define IA_CSS_DEFAULT_PREVIEW_SETTINGS \
-{ \
- IA_CSS_BINARY_DEFAULT_SETTINGS, /* copy_binary */\
- IA_CSS_BINARY_DEFAULT_SETTINGS, /* preview_binary */\
- IA_CSS_BINARY_DEFAULT_SETTINGS, /* vf_pp_binary */\
- { NULL }, /* dvs_frames */ \
- { NULL }, /* tnr_frames */ \
- NULL, /* copy_pipe */\
- NULL, /* capture_pipe */\
- NULL, /* acc_pipe */\
+(struct ia_css_preview_settings) { \
+ .copy_binary = IA_CSS_BINARY_DEFAULT_SETTINGS, \
+ .preview_binary = IA_CSS_BINARY_DEFAULT_SETTINGS, \
+ .vf_pp_binary = IA_CSS_BINARY_DEFAULT_SETTINGS, \
}
struct ia_css_capture_settings {
@@ -70,20 +65,15 @@ struct ia_css_capture_settings {
};
#define IA_CSS_DEFAULT_CAPTURE_SETTINGS \
-{ \
- IA_CSS_BINARY_DEFAULT_SETTINGS, /* copy_binary */\
- {IA_CSS_BINARY_DEFAULT_SETTINGS}, /* primary_binary */\
- 0, /* num_primary_stage */\
- IA_CSS_BINARY_DEFAULT_SETTINGS, /* pre_isp_binary */\
- IA_CSS_BINARY_DEFAULT_SETTINGS, /* anr_gdc_binary */\
- IA_CSS_BINARY_DEFAULT_SETTINGS, /* post_isp_binary */\
- IA_CSS_BINARY_DEFAULT_SETTINGS, /* capture_pp_binary */\
- IA_CSS_BINARY_DEFAULT_SETTINGS, /* vf_pp_binary */\
- IA_CSS_BINARY_DEFAULT_SETTINGS, /* capture_ldc_binary */\
- NULL, /* yuv_scaler_binary */ \
- { NULL }, /* delay_frames[ref_frames] */ \
- NULL, /* is_output_stage */ \
- 0, /* num_yuv_scaler */ \
+(struct ia_css_capture_settings) { \
+ .copy_binary = IA_CSS_BINARY_DEFAULT_SETTINGS, \
+ .primary_binary = {IA_CSS_BINARY_DEFAULT_SETTINGS}, \
+ .pre_isp_binary = IA_CSS_BINARY_DEFAULT_SETTINGS, \
+ .anr_gdc_binary = IA_CSS_BINARY_DEFAULT_SETTINGS, \
+ .post_isp_binary = IA_CSS_BINARY_DEFAULT_SETTINGS, \
+ .capture_pp_binary = IA_CSS_BINARY_DEFAULT_SETTINGS, \
+ .vf_pp_binary = IA_CSS_BINARY_DEFAULT_SETTINGS, \
+ .capture_ldc_binary = IA_CSS_BINARY_DEFAULT_SETTINGS, \
}
struct ia_css_video_settings {
@@ -105,18 +95,10 @@ struct ia_css_video_settings {
};
#define IA_CSS_DEFAULT_VIDEO_SETTINGS \
-{ \
- IA_CSS_BINARY_DEFAULT_SETTINGS, /* copy_binary */ \
- IA_CSS_BINARY_DEFAULT_SETTINGS, /* video_binary */ \
- IA_CSS_BINARY_DEFAULT_SETTINGS, /* vf_pp_binary */ \
- NULL, /* yuv_scaler_binary */ \
- { NULL }, /* delay_frames */ \
- { NULL }, /* tnr_frames */ \
- NULL, /* vf_pp_in_frame */ \
- NULL, /* copy_pipe */ \
- NULL, /* capture_pipe */ \
- NULL, /* is_output_stage */ \
- 0, /* num_yuv_scaler */ \
+(struct ia_css_video_settings) { \
+ .copy_binary = IA_CSS_BINARY_DEFAULT_SETTINGS, \
+ .video_binary = IA_CSS_BINARY_DEFAULT_SETTINGS, \
+ .vf_pp_binary = IA_CSS_BINARY_DEFAULT_SETTINGS, \
}
struct ia_css_yuvpp_settings {
@@ -130,14 +112,8 @@ struct ia_css_yuvpp_settings {
};
#define IA_CSS_DEFAULT_YUVPP_SETTINGS \
-{ \
- IA_CSS_BINARY_DEFAULT_SETTINGS, /* copy_binary */ \
- NULL, /* yuv_scaler_binary */ \
- NULL, /* vf_pp_binary */ \
- NULL, /* is_output_stage */ \
- 0, /* num_yuv_scaler */ \
- 0, /* num_vf_pp */ \
- 0, /* num_output */ \
+(struct ia_css_yuvpp_settings) { \
+ .copy_binary = IA_CSS_BINARY_DEFAULT_SETTINGS, \
}
struct osys_object;
@@ -185,35 +161,26 @@ struct ia_css_pipe {
};
#define IA_CSS_DEFAULT_PIPE \
-{ \
- false, /* stop_requested */ \
- DEFAULT_PIPE_CONFIG, /* config */ \
- DEFAULT_PIPE_EXTRA_CONFIG, /* extra_config */ \
- DEFAULT_PIPE_INFO, /* info */ \
- IA_CSS_PIPE_ID_ACC, /* mode (pipe_id) */ \
- NULL, /* shading_table */ \
- DEFAULT_PIPELINE, /* pipeline */ \
- {IA_CSS_BINARY_DEFAULT_FRAME_INFO}, /* output_info */ \
- IA_CSS_BINARY_DEFAULT_FRAME_INFO, /* bds_output_info */ \
- {IA_CSS_BINARY_DEFAULT_FRAME_INFO}, /* vf_output_info */ \
- IA_CSS_BINARY_DEFAULT_FRAME_INFO, /* out_yuv_ds_input_info */ \
- IA_CSS_BINARY_DEFAULT_FRAME_INFO, /* vf_yuv_ds_input_info */ \
- NULL, /* output_stage */ \
- NULL, /* vf_stage */ \
- SH_CSS_BDS_FACTOR_1_00, /* required_bds_factor */ \
- 1, /* dvs_frame_delay */ \
- 0, /* num_invalid_frames */ \
- {true}, /* enable_viewfinder */ \
- NULL, /* stream */ \
- DEFAULT_FRAME, /* in_frame_struct */ \
- DEFAULT_FRAME, /* out_frame_struct */ \
- DEFAULT_FRAME, /* vf_frame_struct */ \
- { NULL }, /* continuous_frames */ \
- { NULL }, /* cont_md_buffers */ \
- { IA_CSS_DEFAULT_PREVIEW_SETTINGS }, /* pipe_settings */ \
- 0, /* scaler_pp_lut */ \
- NULL, /* osys object */ \
- PIPE_ENTRY_EMPTY_TOKEN, /* pipe_num */\
+(struct ia_css_pipe) { \
+ .config = DEFAULT_PIPE_CONFIG, \
+ .info = DEFAULT_PIPE_INFO, \
+ .mode = IA_CSS_PIPE_ID_ACC, /* (pipe_id) */ \
+ .pipeline = DEFAULT_PIPELINE, \
+ .output_info = {IA_CSS_BINARY_DEFAULT_FRAME_INFO}, \
+ .bds_output_info = IA_CSS_BINARY_DEFAULT_FRAME_INFO, \
+ .vf_output_info = {IA_CSS_BINARY_DEFAULT_FRAME_INFO}, \
+ .out_yuv_ds_input_info = IA_CSS_BINARY_DEFAULT_FRAME_INFO, \
+ .vf_yuv_ds_input_info = IA_CSS_BINARY_DEFAULT_FRAME_INFO, \
+ .required_bds_factor = SH_CSS_BDS_FACTOR_1_00, \
+ .dvs_frame_delay = 1, \
+ .enable_viewfinder = {true}, \
+ .in_frame_struct = DEFAULT_FRAME, \
+ .out_frame_struct = DEFAULT_FRAME, \
+ .vf_frame_struct = DEFAULT_FRAME, \
+ .pipe_settings = { \
+ .preview = IA_CSS_DEFAULT_PREVIEW_SETTINGS \
+ }, \
+ .pipe_num = PIPE_ENTRY_EMPTY_TOKEN, \
}
void ia_css_pipe_map_queue(struct ia_css_pipe *pipe, bool map);
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_pipe_public.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_pipe_public.h
index df0aad9a6ab9..11225d5ac442 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_pipe_public.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_pipe_public.h
@@ -152,82 +152,20 @@ struct ia_css_pipe_config {
};
-#ifdef ISP2401
-/**
- * Default origin of internal frame positioned on shading table.
- */
-#define IA_CSS_PIPE_DEFAULT_INTERNAL_FRAME_ORIGIN_BQS_ON_SCTBL \
-{ \
- 0, /* x [bqs] */ \
- 0 /* y [bqs] */ \
-}
-
-/**
- * Default settings for newly created pipe configurations.
- */
-#define DEFAULT_PIPE_CONFIG \
-{ \
- IA_CSS_PIPE_MODE_PREVIEW, /* mode */ \
- 1, /* isp_pipe_version */ \
- { 0, 0 }, /* pipe_effective_input_res */ \
- { 0, 0 }, /* bayer_ds_out_res */ \
- { 0, 0 }, /* vf_pp_in_res */ \
- { 0, 0 }, /* capt_pp_in_res */ \
- { 0, 0 }, /* output_system_in_res */ \
- { 0, 0 }, /* dvs_crop_out_res */ \
- {IA_CSS_BINARY_DEFAULT_FRAME_INFO}, /* output_info */ \
- {IA_CSS_BINARY_DEFAULT_FRAME_INFO}, /* vf_output_info */ \
- NULL, /* acc_extension */ \
- NULL, /* acc_stages */ \
- 0, /* num_acc_stages */ \
- DEFAULT_CAPTURE_CONFIG, /* default_capture_config */ \
- { 0, 0 }, /* dvs_envelope */ \
- IA_CSS_FRAME_DELAY_1, /* dvs_frame_delay */ \
- -1, /* acc_num_execs */ \
- false, /* enable_dz */ \
- false, /* enable_dpc */ \
- false, /* enable_vfpp_bci */ \
- false, /* enable_luma_only */ \
- false, /* enable_tnr */ \
- NULL, /* p_isp_config */\
- { 0, 0 }, /* gdc_in_buffer_res */ \
- { 0, 0 }, /* gdc_in_buffer_offset */ \
- IA_CSS_PIPE_DEFAULT_INTERNAL_FRAME_ORIGIN_BQS_ON_SCTBL /* internal_frame_origin_bqs_on_sctbl */ \
-}
-
-#else
-
/**
* Default settings for newly created pipe configurations.
*/
#define DEFAULT_PIPE_CONFIG \
-{ \
- IA_CSS_PIPE_MODE_PREVIEW, /* mode */ \
- 1, /* isp_pipe_version */ \
- { 0, 0 }, /* pipe_effective_input_res */ \
- { 0, 0 }, /* bayer_ds_out_res */ \
- { 0, 0 }, /* vf_pp_in_res */ \
- { 0, 0 }, /* capt_pp_in_res */ \
- { 0, 0 }, /* dvs_crop_out_res */ \
- {IA_CSS_BINARY_DEFAULT_FRAME_INFO}, /* output_info */ \
- {IA_CSS_BINARY_DEFAULT_FRAME_INFO}, /* vf_output_info */ \
- NULL, /* acc_extension */ \
- NULL, /* acc_stages */ \
- 0, /* num_acc_stages */ \
- DEFAULT_CAPTURE_CONFIG, /* default_capture_config */ \
- { 0, 0 }, /* dvs_envelope */ \
- IA_CSS_FRAME_DELAY_1, /* dvs_frame_delay */ \
- -1, /* acc_num_execs */ \
- false, /* enable_dz */ \
- false, /* enable_dpc */ \
- false, /* enable_vfpp_bci */ \
- NULL, /* p_isp_config */\
- { 0, 0 }, /* gdc_in_buffer_res */ \
- { 0, 0 } /* gdc_in_buffer_offset */ \
+(struct ia_css_pipe_config) { \
+ .mode = IA_CSS_PIPE_MODE_PREVIEW, \
+ .isp_pipe_version = 1, \
+ .output_info = {IA_CSS_BINARY_DEFAULT_FRAME_INFO}, \
+ .vf_output_info = {IA_CSS_BINARY_DEFAULT_FRAME_INFO}, \
+ .default_capture_config = DEFAULT_CAPTURE_CONFIG, \
+ .dvs_frame_delay = IA_CSS_FRAME_DELAY_1, \
+ .acc_num_execs = -1, \
}
-#endif
-
/* Pipe info, this struct describes properties of a pipe after it's stream has
* been created.
* ~~~** DO NOT ADD NEW FIELD **~~~ This structure will be deprecated.
@@ -272,33 +210,15 @@ struct ia_css_pipe_info {
/**
* Defaults for ia_css_pipe_info structs.
*/
-#ifdef ISP2401
-
-#define DEFAULT_PIPE_INFO \
-{ \
- {IA_CSS_BINARY_DEFAULT_FRAME_INFO}, /* output_info */ \
- {IA_CSS_BINARY_DEFAULT_FRAME_INFO}, /* vf_output_info */ \
- IA_CSS_BINARY_DEFAULT_FRAME_INFO, /* raw_output_info */ \
- { 0, 0}, /* output system in res */ \
- DEFAULT_SHADING_INFO, /* shading_info */ \
- DEFAULT_GRID_INFO, /* grid_info */ \
- 0 /* num_invalid_frames */ \
-}
-
-#else
-
#define DEFAULT_PIPE_INFO \
-{ \
- {IA_CSS_BINARY_DEFAULT_FRAME_INFO}, /* output_info */ \
- {IA_CSS_BINARY_DEFAULT_FRAME_INFO}, /* vf_output_info */ \
- IA_CSS_BINARY_DEFAULT_FRAME_INFO, /* raw_output_info */ \
- DEFAULT_SHADING_INFO, /* shading_info */ \
- DEFAULT_GRID_INFO, /* grid_info */ \
- 0 /* num_invalid_frames */ \
+(struct ia_css_pipe_info) { \
+ .output_info = {IA_CSS_BINARY_DEFAULT_FRAME_INFO}, \
+ .vf_output_info = {IA_CSS_BINARY_DEFAULT_FRAME_INFO}, \
+ .raw_output_info = IA_CSS_BINARY_DEFAULT_FRAME_INFO, \
+ .shading_info = DEFAULT_SHADING_INFO, \
+ .grid_info = DEFAULT_GRID_INFO, \
}
-#endif
-
/* @brief Load default pipe configuration
* @param[out] pipe_config The pipe configuration.
* @return None
@@ -402,7 +322,7 @@ ia_css_pipe_set_isp_config(struct ia_css_pipe *pipe,
exception holds for IA_CSS_EVENT_TYPE_PORT_EOF, for this event an IRQ is always
raised.
Note that events are still queued and the Host can poll for them. The
- or_mask and and_mask may be be active at the same time\n
+ or_mask and and_mask may be active at the same time\n
\n
Default values, for all pipe id's, after ia_css_init:\n
or_mask = IA_CSS_EVENT_TYPE_ALL\n
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_stream_format.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_stream_format.h
index f7e9020a86e1..f97b9eb2b19c 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_stream_format.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_stream_format.h
@@ -20,75 +20,10 @@
*/
#include <type_support.h> /* bool */
-
-/* The ISP streaming input interface supports the following formats.
- * These match the corresponding MIPI formats.
- */
-enum ia_css_stream_format {
- IA_CSS_STREAM_FORMAT_YUV420_8_LEGACY, /** 8 bits per subpixel */
- IA_CSS_STREAM_FORMAT_YUV420_8, /** 8 bits per subpixel */
- IA_CSS_STREAM_FORMAT_YUV420_10, /** 10 bits per subpixel */
- IA_CSS_STREAM_FORMAT_YUV420_16, /** 16 bits per subpixel */
- IA_CSS_STREAM_FORMAT_YUV422_8, /** UYVY..UYVY, 8 bits per subpixel */
- IA_CSS_STREAM_FORMAT_YUV422_10, /** UYVY..UYVY, 10 bits per subpixel */
- IA_CSS_STREAM_FORMAT_YUV422_16, /** UYVY..UYVY, 16 bits per subpixel */
- IA_CSS_STREAM_FORMAT_RGB_444, /** BGR..BGR, 4 bits per subpixel */
- IA_CSS_STREAM_FORMAT_RGB_555, /** BGR..BGR, 5 bits per subpixel */
- IA_CSS_STREAM_FORMAT_RGB_565, /** BGR..BGR, 5 bits B and R, 6 bits G */
- IA_CSS_STREAM_FORMAT_RGB_666, /** BGR..BGR, 6 bits per subpixel */
- IA_CSS_STREAM_FORMAT_RGB_888, /** BGR..BGR, 8 bits per subpixel */
- IA_CSS_STREAM_FORMAT_RAW_6, /** RAW data, 6 bits per pixel */
- IA_CSS_STREAM_FORMAT_RAW_7, /** RAW data, 7 bits per pixel */
- IA_CSS_STREAM_FORMAT_RAW_8, /** RAW data, 8 bits per pixel */
- IA_CSS_STREAM_FORMAT_RAW_10, /** RAW data, 10 bits per pixel */
- IA_CSS_STREAM_FORMAT_RAW_12, /** RAW data, 12 bits per pixel */
- IA_CSS_STREAM_FORMAT_RAW_14, /** RAW data, 14 bits per pixel */
- IA_CSS_STREAM_FORMAT_RAW_16, /** RAW data, 16 bits per pixel, which is
- not specified in CSI-MIPI standard*/
- IA_CSS_STREAM_FORMAT_BINARY_8, /** Binary byte stream, which is target at
- JPEG. */
-
- /* CSI2-MIPI specific format: Generic short packet data. It is used to
- * keep the timing information for the opening/closing of shutters,
- * triggering of flashes and etc.
- */
- IA_CSS_STREAM_FORMAT_GENERIC_SHORT1, /** Generic Short Packet Code 1 */
- IA_CSS_STREAM_FORMAT_GENERIC_SHORT2, /** Generic Short Packet Code 2 */
- IA_CSS_STREAM_FORMAT_GENERIC_SHORT3, /** Generic Short Packet Code 3 */
- IA_CSS_STREAM_FORMAT_GENERIC_SHORT4, /** Generic Short Packet Code 4 */
- IA_CSS_STREAM_FORMAT_GENERIC_SHORT5, /** Generic Short Packet Code 5 */
- IA_CSS_STREAM_FORMAT_GENERIC_SHORT6, /** Generic Short Packet Code 6 */
- IA_CSS_STREAM_FORMAT_GENERIC_SHORT7, /** Generic Short Packet Code 7 */
- IA_CSS_STREAM_FORMAT_GENERIC_SHORT8, /** Generic Short Packet Code 8 */
-
- /* CSI2-MIPI specific format: YUV data.
- */
- IA_CSS_STREAM_FORMAT_YUV420_8_SHIFT, /** YUV420 8-bit (Chroma Shifted Pixel Sampling) */
- IA_CSS_STREAM_FORMAT_YUV420_10_SHIFT, /** YUV420 8-bit (Chroma Shifted Pixel Sampling) */
-
- /* CSI2-MIPI specific format: Generic long packet data
- */
- IA_CSS_STREAM_FORMAT_EMBEDDED, /** Embedded 8-bit non Image Data */
-
- /* CSI2-MIPI specific format: User defined byte-based data. For example,
- * the data transmitter (e.g. the SoC sensor) can keep the JPEG data as
- * the User Defined Data Type 4 and the MPEG data as the
- * User Defined Data Type 7.
- */
- IA_CSS_STREAM_FORMAT_USER_DEF1, /** User defined 8-bit data type 1 */
- IA_CSS_STREAM_FORMAT_USER_DEF2, /** User defined 8-bit data type 2 */
- IA_CSS_STREAM_FORMAT_USER_DEF3, /** User defined 8-bit data type 3 */
- IA_CSS_STREAM_FORMAT_USER_DEF4, /** User defined 8-bit data type 4 */
- IA_CSS_STREAM_FORMAT_USER_DEF5, /** User defined 8-bit data type 5 */
- IA_CSS_STREAM_FORMAT_USER_DEF6, /** User defined 8-bit data type 6 */
- IA_CSS_STREAM_FORMAT_USER_DEF7, /** User defined 8-bit data type 7 */
- IA_CSS_STREAM_FORMAT_USER_DEF8, /** User defined 8-bit data type 8 */
-};
-
-#define IA_CSS_STREAM_FORMAT_NUM IA_CSS_STREAM_FORMAT_USER_DEF8
+#include "../../../include/linux/atomisp_platform.h"
unsigned int ia_css_util_input_format_bpp(
- enum ia_css_stream_format format,
+ enum atomisp_input_format format,
bool two_ppc);
-#endif /* __IA_CSS_STREAM_FORMAT_H */
+#endif /* __ATOMISP_INPUT_FORMAT_H */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_stream_public.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_stream_public.h
index ca3203357ff5..ddefad330db7 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_stream_public.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_stream_public.h
@@ -62,7 +62,7 @@ enum {
*/
struct ia_css_stream_isys_stream_config {
struct ia_css_resolution input_res; /** Resolution of input data */
- enum ia_css_stream_format format; /** Format of input stream. This data
+ enum atomisp_input_format format; /** Format of input stream. This data
format will be mapped to MIPI data
type internally. */
int linked_isys_stream_id; /** default value is -1, other value means
@@ -77,7 +77,7 @@ struct ia_css_stream_input_config {
Used for CSS 2400/1 System and deprecated for other
systems (replaced by input_effective_res in
ia_css_pipe_config) */
- enum ia_css_stream_format format; /** Format of input stream. This data
+ enum atomisp_input_format format; /** Format of input stream. This data
format will be mapped to MIPI data
type internally. */
enum ia_css_bayer_order bayer_order; /** Bayer order for RAW streams */
@@ -257,7 +257,7 @@ ia_css_stream_unload(struct ia_css_stream *stream);
*
* This function will return the stream format.
*/
-enum ia_css_stream_format
+enum atomisp_input_format
ia_css_stream_get_format(const struct ia_css_stream *stream);
/* @brief Check if the stream is configured for 2 pixels per clock
@@ -453,7 +453,7 @@ ia_css_stream_send_input_line(const struct ia_css_stream *stream,
*/
void
ia_css_stream_send_input_embedded_line(const struct ia_css_stream *stream,
- enum ia_css_stream_format format,
+ enum atomisp_input_format format,
const unsigned short *data,
unsigned int width);
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_types.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_types.h
index 725b90072cfe..259ab3f074ba 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_types.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/ia_css_types.h
@@ -370,52 +370,20 @@ struct ia_css_shading_info {
} info;
};
-#ifndef ISP2401
-
-/* Default Shading Correction information of Shading Correction Type 1. */
-#define DEFAULT_SHADING_INFO_TYPE_1 \
-{ \
- IA_CSS_SHADING_CORRECTION_TYPE_1, /* type */ \
- { /* info */ \
- { \
- 0, /* enable */ \
- 0, /* num_hor_grids */ \
- 0, /* num_ver_grids */ \
- 0, /* bqs_per_grid_cell */ \
- 1, /* bayer_scale_hor_ratio_in */ \
- 1, /* bayer_scale_hor_ratio_out */ \
- 1, /* bayer_scale_ver_ratio_in */ \
- 1, /* bayer_scale_ver_ratio_out */ \
- 0, /* sc_bayer_origin_x_bqs_on_shading_table */ \
- 0 /* sc_bayer_origin_y_bqs_on_shading_table */ \
- } \
- } \
-}
-
-#else
-
/* Default Shading Correction information of Shading Correction Type 1. */
#define DEFAULT_SHADING_INFO_TYPE_1 \
-{ \
- IA_CSS_SHADING_CORRECTION_TYPE_1, /* type */ \
- { /* info */ \
- { \
- 0, /* num_hor_grids */ \
- 0, /* num_ver_grids */ \
- 0, /* bqs_per_grid_cell */ \
- 1, /* bayer_scale_hor_ratio_in */ \
- 1, /* bayer_scale_hor_ratio_out */ \
- 1, /* bayer_scale_ver_ratio_in */ \
- 1, /* bayer_scale_ver_ratio_out */ \
- {0, 0}, /* isp_input_sensor_data_res_bqs */ \
- {0, 0}, /* sensor_data_res_bqs */ \
- {0, 0} /* sensor_data_origin_bqs_on_sctbl */ \
+(struct ia_css_shading_info) { \
+ .type = IA_CSS_SHADING_CORRECTION_TYPE_1, \
+ .info = { \
+ .type_1 = { \
+ .bayer_scale_hor_ratio_in = 1, \
+ .bayer_scale_hor_ratio_out = 1, \
+ .bayer_scale_ver_ratio_in = 1, \
+ .bayer_scale_ver_ratio_out = 1, \
} \
} \
}
-#endif
-
/* Default Shading Correction information. */
#define DEFAULT_SHADING_INFO DEFAULT_SHADING_INFO_TYPE_1
@@ -438,12 +406,9 @@ struct ia_css_grid_info {
/* defaults for ia_css_grid_info structs */
#define DEFAULT_GRID_INFO \
-{ \
- 0, /* isp_in_width */ \
- 0, /* isp_in_height */ \
- DEFAULT_3A_GRID_INFO, /* s3a_grid */ \
- DEFAULT_DVS_GRID_INFO, /* dvs_grid */ \
- IA_CSS_VAMEM_TYPE_1 /* vamem_type */ \
+(struct ia_css_grid_info) { \
+ .dvs_grid = DEFAULT_DVS_GRID_INFO, \
+ .vamem_type = IA_CSS_VAMEM_TYPE_1 \
}
/* Morphing table, used for geometric distortion and chromatic abberration
@@ -534,11 +499,8 @@ struct ia_css_capture_config {
/* default settings for ia_css_capture_config structs */
#define DEFAULT_CAPTURE_CONFIG \
-{ \
- IA_CSS_CAPTURE_MODE_PRIMARY, /* mode (capture) */ \
- false, /* enable_xnr */ \
- false, /* enable_raw_output */ \
- false /* enable_capture_pp_bli */ \
+(struct ia_css_capture_config) { \
+ .mode = IA_CSS_CAPTURE_MODE_PRIMARY, \
}
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/aa/aa_2/ia_css_aa2_state.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/aa/aa_2/ia_css_aa2_state.h
deleted file mode 100644
index cc404018b112..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/aa/aa_2/ia_css_aa2_state.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __IA_CSS_AA2_STATE_H
-#define __IA_CSS_AA2_STATE_H
-
-#include "type_support.h"
-#include "vmem.h" /* for VMEM_ARRAY*/
-
-/* Denotes the maximum number of pixels per line that can be processed:
-* MAX_AA_VECTORS_PER_LINE = maximum_line_width / ISP_NWAY */
-#ifndef MAX_AA_VECTORS_PER_LINE
-#error Please define MAX_AA_VECTORS_PER_LINE.
-#endif
-
-/* This uses 2 history lines for both y, u and v*/
-#define AA_STATE_Y_BUFFER_HEIGHT 2
-#define AA_STATE_UV_BUFFER_HEIGHT 2
-#define AA_STATE_Y_BUFFER_WIDTH MAX_AA_VECTORS_PER_LINE
-/* The number of u and v elements is half y due to yuv420 downsampling. */
-#define AA_STATE_UV_BUFFER_WIDTH (AA_STATE_Y_BUFFER_WIDTH/2)
-
-
-struct ia_css_isp_aa_vmem_state {
- VMEM_ARRAY(y[AA_STATE_Y_BUFFER_HEIGHT], AA_STATE_Y_BUFFER_WIDTH*ISP_NWAY);
- VMEM_ARRAY(u[AA_STATE_UV_BUFFER_HEIGHT], AA_STATE_UV_BUFFER_WIDTH*ISP_NWAY);
- VMEM_ARRAY(v[AA_STATE_UV_BUFFER_HEIGHT], AA_STATE_UV_BUFFER_WIDTH*ISP_NWAY);
-};
-
-#endif /* __IA_CSS_AA2_STATE_H */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/bayer_ls/bayer_ls_1.0/ia_css_bayer_load_param.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/bayer_ls/bayer_ls_1.0/ia_css_bayer_load_param.h
deleted file mode 100644
index 8e1f300bcd39..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/bayer_ls/bayer_ls_1.0/ia_css_bayer_load_param.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __IA_CSS_BAYER_LOAD_PARAM_H
-#define __IA_CSS_BAYER_LOAD_PARAM_H
-
-#include "ia_css_bayer_ls_param.h"
-
-#endif /* __IA_CSS_BAYER_LOAD_PARAM_H */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/bayer_ls/bayer_ls_1.0/ia_css_bayer_ls_param.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/bayer_ls/bayer_ls_1.0/ia_css_bayer_ls_param.h
deleted file mode 100644
index a0d355454aa3..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/bayer_ls/bayer_ls_1.0/ia_css_bayer_ls_param.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __IA_CSS_BAYER_LS_PARAM_H
-#define __IA_CSS_BAYER_LS_PARAM_H
-
-#include "type_support.h"
-#ifndef ISP2401
-
-#define NUM_BAYER_LS 2
-#define BAYER_IDX_GR 0
-#define BAYER_IDX_R 1
-#define BAYER_IDX_B 2
-#define BAYER_IDX_GB 3
-#define BAYER_QUAD_WIDTH 2
-#define BAYER_QUAD_HEIGHT 2
-#define NOF_BAYER_VECTORS 4
-
-/* bayer load/store */
-struct sh_css_isp_bayer_ls_isp_config {
- uint32_t base_address[NUM_BAYER_LS];
- uint32_t width[NUM_BAYER_LS];
- uint32_t height[NUM_BAYER_LS];
- uint32_t stride[NUM_BAYER_LS];
-};
-
-#else
-#include "../../io_ls/common/ia_css_common_io_types.h"
-#endif
-
-#endif /* __IA_CSS_BAYER_LS_PARAM_H */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/bayer_ls/bayer_ls_1.0/ia_css_bayer_store_param.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/bayer_ls/bayer_ls_1.0/ia_css_bayer_store_param.h
deleted file mode 100644
index f330be80efa6..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/bayer_ls/bayer_ls_1.0/ia_css_bayer_store_param.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __IA_CSS_BAYER_STORE_PARAM_H
-#define __IA_CSS_BAYER_STORE_PARAM_H
-
-#include "ia_css_bayer_ls_param.h"
-
-
-#endif /* __IA_CSS_BAYER_STORE_PARAM_H */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/bnlm/ia_css_bnlm.host.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/bnlm/ia_css_bnlm.host.h
index b99c0644ab38..675f6e539b3f 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/bnlm/ia_css_bnlm.host.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/bnlm/ia_css_bnlm.host.h
@@ -17,7 +17,6 @@
#include "ia_css_bnlm_types.h"
#include "ia_css_bnlm_param.h"
-#include "ia_css_bnlm_default.host.h"
void
ia_css_bnlm_vmem_encode(
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/bnlm/ia_css_bnlm_default.host.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/bnlm/ia_css_bnlm_default.host.c
deleted file mode 100644
index e2eb88c0f123..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/bnlm/ia_css_bnlm_default.host.c
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#include "ia_css_bnlm_types.h"
-
-const struct ia_css_bnlm_config default_bnlm_config = {
-
- .rad_enable = true,
- .rad_x_origin = 0,
- .rad_y_origin = 0,
- .avg_min_th = 127,
- .max_min_th = 2047,
-
- .exp_coeff_a = 6048,
- .exp_coeff_b = 7828,
- .exp_coeff_c = 0,
- .exp_exponent = 3,
-
- .nl_th = {2252, 2251, 2250},
- .match_quality_max_idx = {2, 3, 3, 1},
-
- .mu_root_lut_thr = {
- 26, 56, 128, 216, 462, 626, 932, 1108, 1480, 1564, 1824, 1896, 2368, 3428, 4560},
- .mu_root_lut_val = {
- 384, 320, 320, 264, 248, 240, 224, 192, 192, 160, 160, 160, 136, 130, 96, 80},
- .sad_norm_lut_thr = {
- 236, 328, 470, 774, 964, 1486, 2294, 3244, 4844, 6524, 6524, 6524, 6524, 6524, 6524},
- .sad_norm_lut_val = {
- 8064, 7680, 7168, 6144, 5120, 3840, 2560, 2304, 1984, 1792, 1792, 1792, 1792, 1792, 1792, 1792},
- .sig_detail_lut_thr = {
- 2936, 3354, 3943, 4896, 5230, 5682, 5996, 7299, 7299, 7299, 7299, 7299, 7299, 7299, 7299},
- .sig_detail_lut_val = {
- 8191, 7680, 7168, 6144, 5120, 4608, 4224, 4032, 4032, 4032, 4032, 4032, 4032, 4032, 4032, 4032},
- .sig_rad_lut_thr = {
- 18, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20},
- .sig_rad_lut_val = {
- 2560, 7168, 8188, 8188, 8188, 8188, 8188, 8188, 8188, 8188, 8188, 8188, 8188, 8188, 8188, 8188},
- .rad_pow_lut_thr = {
- 0, 7013, 7013, 7013, 7013, 7013, 7013, 7013, 7013, 7013, 7013, 7013, 7013, 7013, 7013},
- .rad_pow_lut_val = {
- 8191, 8191, 8191, 8191, 8191, 8191, 8191, 8191, 8191, 8191, 8191, 8191, 8191, 8191, 8191, 8191},
- .nl_0_lut_thr = {
- 1072, 7000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000},
- .nl_0_lut_val = {
- 2560, 3072, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120},
- .nl_1_lut_thr = {
- 624, 3224, 3392, 7424, 7424, 7424, 7424, 7424, 7424, 7424, 7424, 7424, 7424, 7424, 7424},
- .nl_1_lut_val = {
- 3584, 4608, 5120, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144},
- .nl_2_lut_thr = {
- 745, 2896, 3720, 6535, 7696, 8040, 8040, 8040, 8040, 8040, 8040, 8040, 8040, 8040, 8040},
- .nl_2_lut_val = {
- 3584, 4608, 6144, 7168, 7936, 8191, 8191, 8191, 8191, 8191, 8191, 8191, 8191, 8191, 8191, 8191},
- .nl_3_lut_thr = {
- 4848, 4984, 5872, 6000, 6517, 6960, 7944, 8088, 8161, 8161, 8161, 8161, 8161, 8161, 8161},
- .nl_3_lut_val = {
- 3072, 4104, 4608, 5120, 6144, 7168, 7680, 8128, 8191, 8191, 8191, 8191, 8191, 8191, 8191, 8191},
-
-};
-
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/bnlm/ia_css_bnlm_default.host.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/bnlm/ia_css_bnlm_default.host.h
deleted file mode 100644
index f18c8070abba..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/bnlm/ia_css_bnlm_default.host.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __IA_CSS_BNLM_DEFAULT_HOST_H
-#define __IA_CSS_BNLM_DEFAULT_HOST_H
-
-#include "ia_css_bnlm_types.h"
-extern const struct ia_css_bnlm_config default_bnlm_config;
-
-#endif /* __IA_CSS_BNLM_DEFAULT_HOST_H */
-
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/bnlm/ia_css_bnlm_state.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/bnlm/ia_css_bnlm_state.h
deleted file mode 100644
index 79cce0e40e82..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/bnlm/ia_css_bnlm_state.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __IA_CSS_BNLM_STATE_H
-#define __IA_CSS_BNLM_STATE_H
-
-
-#include "type_support.h"
-#include "vmem.h" /* for VMEM_ARRAY*/
-#include "bnlm.isp.h"
-
-struct bnlm_vmem_state {
- /* State buffers required for BNLM */
- VMEM_ARRAY(buf[BNLM_STATE_BUF_HEIGHT], BNLM_STATE_BUF_WIDTH*ISP_NWAY);
-};
-
-
-
-#endif /* __IA_CSS_BNLM_STATE_H */
-
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/cnr/cnr_1.0/ia_css_cnr_state.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/cnr/cnr_1.0/ia_css_cnr_state.h
deleted file mode 100644
index 795fba76bb20..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/cnr/cnr_1.0/ia_css_cnr_state.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __IA_CSS_CNR_STATE_H
-#define __IA_CSS_CNR_STATE_H
-
-#include "type_support.h"
-
-#include "vmem.h"
-
-typedef struct
-{
- VMEM_ARRAY(u, ISP_NWAY);
- VMEM_ARRAY(v, ISP_NWAY);
-} s_cnr_buf;
-
-/* CNR (color noise reduction) */
-struct sh_css_isp_cnr_vmem_state {
- s_cnr_buf cnr_buf[2][MAX_VECTORS_PER_BUF_LINE/2];
-};
-
-#endif /* __IA_CSS_CNR_STATE_H */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/cnr/cnr_2/ia_css_cnr_state.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/cnr/cnr_2/ia_css_cnr_state.h
deleted file mode 100644
index e533e2fa8cd5..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/cnr/cnr_2/ia_css_cnr_state.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __IA_CSS_CNR2_STATE_H
-#define __IA_CSS_CNR2_STATE_H
-
-#include "type_support.h"
-#include "vmem.h"
-
-typedef struct
-{
- VMEM_ARRAY(y, (MAX_VECTORS_PER_BUF_LINE/2)*ISP_NWAY);
- VMEM_ARRAY(u, (MAX_VECTORS_PER_BUF_LINE/2)*ISP_NWAY);
- VMEM_ARRAY(v, (MAX_VECTORS_PER_BUF_LINE/2)*ISP_NWAY);
-} s_cnr_buf;
-
-/* CNR (color noise reduction) */
-struct sh_css_isp_cnr_vmem_state {
- s_cnr_buf cnr_buf;
-};
-
-#endif /* __IA_CSS_CNR2_STATE_H */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/dp/dp_1.0/ia_css_dp_state.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/dp/dp_1.0/ia_css_dp_state.h
deleted file mode 100644
index f832b3697908..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/dp/dp_1.0/ia_css_dp_state.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __IA_CSS_DP_STATE_H
-#define __IA_CSS_DP_STATE_H
-
-#include "type_support.h"
-
-#include "vmem.h"
-#ifndef ISP2401
-#if NEED_BDS_OTHER_THAN_1_00
-#else
-#if ENABLE_FIXED_BAYER_DS
-#endif
-#define MAX_VECTORS_PER_DP_LINE MAX_VECTORS_PER_BUF_INPUT_LINE
-#else
-#define MAX_VECTORS_PER_DP_LINE MAX_VECTORS_PER_BUF_LINE
-#endif
-
-/* DP (Defect Pixel Correction) */
-struct sh_css_isp_dp_vmem_state {
- VMEM_ARRAY(dp_buf[4], MAX_VECTORS_PER_DP_LINE*ISP_NWAY);
-};
-
-#endif /* __IA_CSS_DP_STATE_H */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/dpc2/ia_css_dpc2.host.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/dpc2/ia_css_dpc2.host.h
index 641564b4af8e..38d10a5237c6 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/dpc2/ia_css_dpc2.host.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/dpc2/ia_css_dpc2.host.h
@@ -17,7 +17,6 @@
#include "ia_css_dpc2_types.h"
#include "ia_css_dpc2_param.h"
-#include "ia_css_dpc2_default.host.h"
void
ia_css_dpc2_encode(
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/dpc2/ia_css_dpc2_default.host.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/dpc2/ia_css_dpc2_default.host.c
deleted file mode 100644
index c102601cc635..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/dpc2/ia_css_dpc2_default.host.c
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#include "ia_css_dpc2_types.h"
-
-const struct ia_css_dpc2_config default_dpc2_config = {
- .metric1 = 1638,
- .metric2 = 128,
- .metric3 = 1638,
- .wb_gain_gr = 512,
- .wb_gain_r = 512,
- .wb_gain_b = 512,
- .wb_gain_gb = 512
-};
-
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/dpc2/ia_css_dpc2_default.host.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/dpc2/ia_css_dpc2_default.host.h
deleted file mode 100644
index a1527ce3eddc..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/dpc2/ia_css_dpc2_default.host.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __IA_CSS_DPC2_DEFAULT_HOST_H
-#define __IA_CSS_DPC2_DEFAULT_HOST_H
-
-#include "ia_css_dpc2_types.h"
-
-extern const struct ia_css_dpc2_config default_dpc2_config;
-
-#endif /* __IA_CSS_DPC2_DEFAULT_HOST_H */
-
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/dpc2/ia_css_dpc2_state.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/dpc2/ia_css_dpc2_state.h
deleted file mode 100644
index cbf1e81e83a6..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/dpc2/ia_css_dpc2_state.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __IA_CSS_DPC2_STATE_H
-#define __IA_CSS_DPC2_STATE_H
-
-#include "type_support.h"
-#include "vmem.h" /* for VMEM_ARRAY*/
-
-#include "ia_css_dpc2_param.h"
-
-struct sh_css_isp_dpc2_vmem_state {
- VMEM_ARRAY(dpc2_input_lines[DPC2_STATE_INPUT_BUFFER_HEIGHT], DPC2_STATE_INPUT_BUFFER_WIDTH*ISP_NWAY);
- VMEM_ARRAY(dpc2_local_deviations[DPC2_STATE_LOCAL_DEVIATION_BUFFER_HEIGHT], DPC2_STATE_LOCAL_DEVIATION_BUFFER_WIDTH*ISP_NWAY);
- VMEM_ARRAY(dpc2_second_min[DPC2_STATE_SECOND_MINMAX_BUFFER_HEIGHT], DPC2_STATE_SECOND_MINMAX_BUFFER_WIDTH*ISP_NWAY);
- VMEM_ARRAY(dpc2_second_max[DPC2_STATE_SECOND_MINMAX_BUFFER_HEIGHT], DPC2_STATE_SECOND_MINMAX_BUFFER_WIDTH*ISP_NWAY);
-};
-
-#endif /* __IA_CSS_DPC2_STATE_H */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/eed1_8/ia_css_eed1_8.host.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/eed1_8/ia_css_eed1_8.host.h
index 355ff13273b0..fff932c1364e 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/eed1_8/ia_css_eed1_8.host.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/eed1_8/ia_css_eed1_8.host.h
@@ -17,7 +17,6 @@
#include "ia_css_eed1_8_types.h"
#include "ia_css_eed1_8_param.h"
-#include "ia_css_eed1_8_default.host.h"
void
ia_css_eed1_8_vmem_encode(
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/eed1_8/ia_css_eed1_8_default.host.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/eed1_8/ia_css_eed1_8_default.host.c
deleted file mode 100644
index 3622719dafa5..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/eed1_8/ia_css_eed1_8_default.host.c
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#include "ia_css_eed1_8_types.h"
-
-/* The default values for the kernel parameters are based on
- * ISP261 CSS API public parameter list_all.xlsx from 12-09-2014
- * The parameter list is available on the ISP261 sharepoint
- */
-
-/* Default kernel parameters. */
-const struct ia_css_eed1_8_config default_eed1_8_config = {
- .rbzp_strength = 5489,
- .fcstrength = 6554,
- .fcthres_0 = 0,
- .fcthres_1 = 0,
- .fc_sat_coef = 8191,
- .fc_coring_prm = 128,
- .aerel_thres0 = 0,
- .aerel_gain0 = 8191,
- .aerel_thres1 = 16,
- .aerel_gain1 = 20,
- .derel_thres0 = 1229,
- .derel_gain0 = 1,
- .derel_thres1 = 819,
- .derel_gain1 = 1,
- .coring_pos0 = 0,
- .coring_pos1 = 0,
- .coring_neg0 = 0,
- .coring_neg1 = 0,
- .gain_exp = 2,
- .gain_pos0 = 6144,
- .gain_pos1 = 2048,
- .gain_neg0 = 2048,
- .gain_neg1 = 6144,
- .pos_margin0 = 1475,
- .pos_margin1 = 1475,
- .neg_margin0 = 1475,
- .neg_margin1 = 1475,
- .dew_enhance_seg_x = {
- 0,
- 64,
- 272,
- 688,
- 1376,
- 2400,
- 3840,
- 5744,
- 8191
- },
- .dew_enhance_seg_y = {
- 0,
- 144,
- 480,
- 1040,
- 1852,
- 2945,
- 4357,
- 6094,
- 8191
- },
- .dew_enhance_seg_slope = {
- 4608,
- 3308,
- 2757,
- 2417,
- 2186,
- 8033,
- 7473,
- 7020
- },
- .dew_enhance_seg_exp = {
- 2,
- 2,
- 2,
- 2,
- 2,
- 0,
- 0,
- 0
- },
- .dedgew_max = 6144
-};
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/eed1_8/ia_css_eed1_8_default.host.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/eed1_8/ia_css_eed1_8_default.host.h
deleted file mode 100644
index 782f739ca8b5..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/eed1_8/ia_css_eed1_8_default.host.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __IA_CSS_EED1_8_DEFAULT_HOST_H
-#define __IA_CSS_EED1_8_DEFAULT_HOST_H
-
-#include "ia_css_eed1_8_types.h"
-
-extern const struct ia_css_eed1_8_config default_eed1_8_config;
-
-#endif /* __IA_CSS_EED1_8_DEFAULT_HOST_H */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/eed1_8/ia_css_eed1_8_state.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/eed1_8/ia_css_eed1_8_state.h
deleted file mode 100644
index 47e451b15044..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/eed1_8/ia_css_eed1_8_state.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __IA_CSS_EED1_8_STATE_H
-#define __IA_CSS_EED1_8_STATE_H
-
-#include "type_support.h"
-#include "vmem.h" /* for VMEM_ARRAY*/
-
-#include "ia_css_eed1_8_param.h"
-
-struct eed1_8_vmem_state {
- VMEM_ARRAY(eed1_8_input_lines[EED1_8_STATE_INPUT_BUFFER_HEIGHT], EED1_8_STATE_INPUT_BUFFER_WIDTH*ISP_NWAY);
- VMEM_ARRAY(eed1_8_LD_H[EED1_8_STATE_LD_H_HEIGHT], EED1_8_STATE_LD_H_WIDTH*ISP_NWAY);
- VMEM_ARRAY(eed1_8_LD_V[EED1_8_STATE_LD_V_HEIGHT], EED1_8_STATE_LD_V_WIDTH*ISP_NWAY);
- VMEM_ARRAY(eed1_8_D_Hr[EED1_8_STATE_D_HR_HEIGHT], EED1_8_STATE_D_HR_WIDTH*ISP_NWAY);
- VMEM_ARRAY(eed1_8_D_Hb[EED1_8_STATE_D_HB_HEIGHT], EED1_8_STATE_D_HB_WIDTH*ISP_NWAY);
- VMEM_ARRAY(eed1_8_D_Vr[EED1_8_STATE_D_VR_HEIGHT], EED1_8_STATE_D_VR_WIDTH*ISP_NWAY);
- VMEM_ARRAY(eed1_8_D_Vb[EED1_8_STATE_D_VB_HEIGHT], EED1_8_STATE_D_VB_WIDTH*ISP_NWAY);
- VMEM_ARRAY(eed1_8_rb_zipped[EED1_8_STATE_RB_ZIPPED_HEIGHT], EED1_8_STATE_RB_ZIPPED_WIDTH*ISP_NWAY);
-#if EED1_8_FC_ENABLE_MEDIAN
- VMEM_ARRAY(eed1_8_Yc[EED1_8_STATE_YC_HEIGHT], EED1_8_STATE_YC_WIDTH*ISP_NWAY);
- VMEM_ARRAY(eed1_8_Cg[EED1_8_STATE_CG_HEIGHT], EED1_8_STATE_CG_WIDTH*ISP_NWAY);
- VMEM_ARRAY(eed1_8_Co[EED1_8_STATE_CO_HEIGHT], EED1_8_STATE_CO_WIDTH*ISP_NWAY);
- VMEM_ARRAY(eed1_8_AbsK[EED1_8_STATE_ABSK_HEIGHT], EED1_8_STATE_ABSK_WIDTH*ISP_NWAY);
-#endif
-};
-
-#endif /* __IA_CSS_EED1_8_STATE_H */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/io_ls/plane_io_ls/ia_css_plane_io_param.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/io_ls/plane_io_ls/ia_css_plane_io_param.h
deleted file mode 100644
index 213ef3b385aa..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/io_ls/plane_io_ls/ia_css_plane_io_param.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef ISP2401
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __IA_CSS_PLANE_IO_PARAM_H
-#define __IA_CSS_PLANE_IO_PARAM_H
-
-#include "../common/ia_css_common_io_param.h"
-
-#endif /* __IA_CSS_PLANE_IO_PARAM_H */
-#endif
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/io_ls/plane_io_ls/ia_css_plane_io_types.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/io_ls/plane_io_ls/ia_css_plane_io_types.h
deleted file mode 100644
index d635741505e2..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/io_ls/plane_io_ls/ia_css_plane_io_types.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#ifndef ISP2401
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __IA_CSS_PLANE_IO_TYPES_H
-#define __IA_CSS_PLANE_IO_TYPES_H
-
-#include "../common/ia_css_common_io_types.h"
-
-#define PLANE_IO_LS_NUM_PLANES 3
-
-struct ia_css_plane_io_config {
- struct ia_css_common_io_config get_plane_io_config[PLANE_IO_LS_NUM_PLANES];
- struct ia_css_common_io_config put_plane_io_config[PLANE_IO_LS_NUM_PLANES];
-};
-
-#endif /* __IA_CSS_PLANE_IO_TYPES_H */
-
-#endif
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/io_ls/yuv420_io_ls/ia_css_yuv420_io_param.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/io_ls/yuv420_io_ls/ia_css_yuv420_io_param.h
deleted file mode 100644
index 52450a9a55a1..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/io_ls/yuv420_io_ls/ia_css_yuv420_io_param.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef ISP2401
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __IA_CSS_YUV420_IO_PARAM
-#define __IA_CSS_YUV420_IO_PARAM
-
-#include "../common/ia_css_common_io_param.h"
-
-#endif
-#endif
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/io_ls/yuv420_io_ls/ia_css_yuv420_io_types.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/io_ls/yuv420_io_ls/ia_css_yuv420_io_types.h
deleted file mode 100644
index 99ec1143b214..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/io_ls/yuv420_io_ls/ia_css_yuv420_io_types.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef ISP2401
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __IA_CSS_YUV420_IO_TYPES
-#define __IA_CSS_YUV420_IO_TYPES
-
-#include "../common/ia_css_common_io_types.h"
-
-#endif
-#endif
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/ipu2_io_ls/plane_io_ls/ia_css_plane_io_param.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/ipu2_io_ls/plane_io_ls/ia_css_plane_io_param.h
deleted file mode 100644
index 881b7e5236dc..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/ipu2_io_ls/plane_io_ls/ia_css_plane_io_param.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifdef ISP2401
-/**
-Support for Intel Camera Imaging ISP subsystem.
-Copyright (c) 2010 - 2015, Intel Corporation.
-
-This program is free software; you can redistribute it and/or modify it
-under the terms and conditions 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.
-*/
-
-#ifndef __IA_CSS_PLANE_IO_PARAM_H
-#define __IA_CSS_PLANE_IO_PARAM_H
-
-#include "../common/ia_css_common_io_param.h"
-
-#endif /* __IA_CSS_PLANE_IO_PARAM_H */
-#endif
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/ipu2_io_ls/plane_io_ls/ia_css_plane_io_types.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/ipu2_io_ls/plane_io_ls/ia_css_plane_io_types.h
deleted file mode 100644
index f4b9e8de3d8e..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/ipu2_io_ls/plane_io_ls/ia_css_plane_io_types.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#ifdef ISP2401
-/**
-Support for Intel Camera Imaging ISP subsystem.
-Copyright (c) 2010 - 2015, Intel Corporation.
-
-This program is free software; you can redistribute it and/or modify it
-under the terms and conditions 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.
-*/
-
-#ifndef __IA_CSS_PLANE_IO_TYPES_H
-#define __IA_CSS_PLANE_IO_TYPES_H
-
-#include "../common/ia_css_common_io_types.h"
-
-#define PLANE_IO_LS_NUM_PLANES 3
-
-struct ia_css_plane_io_config {
- struct ia_css_common_io_config get_plane_io_config[PLANE_IO_LS_NUM_PLANES];
- struct ia_css_common_io_config put_plane_io_config[PLANE_IO_LS_NUM_PLANES];
-};
-
-#endif /* __IA_CSS_PLANE_IO_TYPES_H */
-
-#endif
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/ipu2_io_ls/yuv420_io_ls/ia_css_yuv420_io_param.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/ipu2_io_ls/yuv420_io_ls/ia_css_yuv420_io_param.h
deleted file mode 100644
index 86184b545fed..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/ipu2_io_ls/yuv420_io_ls/ia_css_yuv420_io_param.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifdef ISP2401
-/**
-Support for Intel Camera Imaging ISP subsystem.
-Copyright (c) 2010 - 2015, Intel Corporation.
-
-This program is free software; you can redistribute it and/or modify it
-under the terms and conditions 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.
-*/
-
-#ifndef __IA_CSS_YUV420_IO_PARAM
-#define __IA_CSS_YUV420_IO_PARAM
-
-#include "../common/ia_css_common_io_param.h"
-
-#endif
-#endif
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/ipu2_io_ls/yuv420_io_ls/ia_css_yuv420_io_types.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/ipu2_io_ls/yuv420_io_ls/ia_css_yuv420_io_types.h
deleted file mode 100644
index ad750f530013..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/ipu2_io_ls/yuv420_io_ls/ia_css_yuv420_io_types.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifdef ISP2401
-/**
-Support for Intel Camera Imaging ISP subsystem.
-Copyright (c) 2010 - 2015, Intel Corporation.
-
-This program is free software; you can redistribute it and/or modify it
-under the terms and conditions 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.
-*/
-
-#ifndef __IA_CSS_YUV420_IO_TYPES
-#define __IA_CSS_YUV420_IO_TYPES
-
-#include "../common/ia_css_common_io_types.h"
-
-#endif
-#endif
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/norm/norm_1.0/ia_css_norm_types.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/norm/norm_1.0/ia_css_norm_types.h
deleted file mode 100644
index 5581bddf9f9b..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/norm/norm_1.0/ia_css_norm_types.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __IA_CSS_NORM_TYPES_H
-#define __IA_CSS_NORM_TYPES_H
-
-
-#endif /* __IA_CSS_NORM_TYPES_H */
-
-
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/output/output_1.0/ia_css_output.host.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/output/output_1.0/ia_css_output.host.c
index 8fdf47c9310c..9efe5e5e4e06 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/output/output_1.0/ia_css_output.host.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/output/output_1.0/ia_css_output.host.c
@@ -60,7 +60,7 @@ ia_css_output_config(
(void)size;
ia_css_dma_configure_from_info(&to->port_b, from->info);
to->width_a_over_b = elems_a / to->port_b.elems;
- to->height = from->info->res.height;
+ to->height = from->info ? from->info->res.height : 0;
to->enable = from->info != NULL;
ia_css_frame_info_to_frame_sp_info(&to->info, from->info);
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/raw/raw_1.0/ia_css_raw.host.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/raw/raw_1.0/ia_css_raw.host.c
index 68a27f0cfba0..fa9ce0fedf23 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/raw/raw_1.0/ia_css_raw.host.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/raw/raw_1.0/ia_css_raw.host.c
@@ -37,34 +37,34 @@ sh_css_elems_bytes_from_info (unsigned raw_bit_depth)
/* MW: These areMIPI / ISYS properties, not camera function properties */
static enum sh_stream_format
-css2isp_stream_format(enum ia_css_stream_format from)
+css2isp_stream_format(enum atomisp_input_format from)
{
switch (from) {
- case IA_CSS_STREAM_FORMAT_YUV420_8_LEGACY:
+ case ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY:
return sh_stream_format_yuv420_legacy;
- case IA_CSS_STREAM_FORMAT_YUV420_8:
- case IA_CSS_STREAM_FORMAT_YUV420_10:
- case IA_CSS_STREAM_FORMAT_YUV420_16:
+ case ATOMISP_INPUT_FORMAT_YUV420_8:
+ case ATOMISP_INPUT_FORMAT_YUV420_10:
+ case ATOMISP_INPUT_FORMAT_YUV420_16:
return sh_stream_format_yuv420;
- case IA_CSS_STREAM_FORMAT_YUV422_8:
- case IA_CSS_STREAM_FORMAT_YUV422_10:
- case IA_CSS_STREAM_FORMAT_YUV422_16:
+ case ATOMISP_INPUT_FORMAT_YUV422_8:
+ case ATOMISP_INPUT_FORMAT_YUV422_10:
+ case ATOMISP_INPUT_FORMAT_YUV422_16:
return sh_stream_format_yuv422;
- case IA_CSS_STREAM_FORMAT_RGB_444:
- case IA_CSS_STREAM_FORMAT_RGB_555:
- case IA_CSS_STREAM_FORMAT_RGB_565:
- case IA_CSS_STREAM_FORMAT_RGB_666:
- case IA_CSS_STREAM_FORMAT_RGB_888:
+ case ATOMISP_INPUT_FORMAT_RGB_444:
+ case ATOMISP_INPUT_FORMAT_RGB_555:
+ case ATOMISP_INPUT_FORMAT_RGB_565:
+ case ATOMISP_INPUT_FORMAT_RGB_666:
+ case ATOMISP_INPUT_FORMAT_RGB_888:
return sh_stream_format_rgb;
- case IA_CSS_STREAM_FORMAT_RAW_6:
- case IA_CSS_STREAM_FORMAT_RAW_7:
- case IA_CSS_STREAM_FORMAT_RAW_8:
- case IA_CSS_STREAM_FORMAT_RAW_10:
- case IA_CSS_STREAM_FORMAT_RAW_12:
- case IA_CSS_STREAM_FORMAT_RAW_14:
- case IA_CSS_STREAM_FORMAT_RAW_16:
+ case ATOMISP_INPUT_FORMAT_RAW_6:
+ case ATOMISP_INPUT_FORMAT_RAW_7:
+ case ATOMISP_INPUT_FORMAT_RAW_8:
+ case ATOMISP_INPUT_FORMAT_RAW_10:
+ case ATOMISP_INPUT_FORMAT_RAW_12:
+ case ATOMISP_INPUT_FORMAT_RAW_14:
+ case ATOMISP_INPUT_FORMAT_RAW_16:
return sh_stream_format_raw;
- case IA_CSS_STREAM_FORMAT_BINARY_8:
+ case ATOMISP_INPUT_FORMAT_BINARY_8:
default:
return sh_stream_format_raw;
}
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/raw/raw_1.0/ia_css_raw_types.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/raw/raw_1.0/ia_css_raw_types.h
index 5c0b8febd79a..ae868eb5e10f 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/raw/raw_1.0/ia_css_raw_types.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/raw/raw_1.0/ia_css_raw_types.h
@@ -28,7 +28,7 @@ struct ia_css_raw_configuration {
const struct ia_css_frame_info *in_info;
const struct ia_css_frame_info *internal_info;
bool two_ppc;
- enum ia_css_stream_format stream_format;
+ enum atomisp_input_format stream_format;
bool deinterleaved;
uint8_t enable_left_padding;
};
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/s3a/s3a_1.0/ia_css_s3a_types.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/s3a/s3a_1.0/ia_css_s3a_types.h
index 8d674d2c6427..63e70669f085 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/s3a/s3a_1.0/ia_css_s3a_types.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/s3a/s3a_1.0/ia_css_s3a_types.h
@@ -28,7 +28,7 @@
/* 3A configuration. This configures the 3A statistics collection
* module.
*/
-
+
/* 3A statistics grid
*
* ISP block: S3A1 (3A Support for 3A ver.1 (Histogram is not used for AE))
@@ -54,7 +54,7 @@ struct ia_css_3a_grid_info {
uint32_t awb_fr_enable; /** awb_fr enabled in binary,
0:disabled, 1:enabled */
struct awb_fr_public_grid_config awb_fr_grd_info;/** see description in awb_fr_public.h*/
-
+
uint32_t elem_bit_depth; /** TODO:Taken from BYT - need input from AIQ
if needed for SKC
Bit depth of element used
@@ -98,52 +98,6 @@ struct ia_css_3a_grid_info {
};
-#if defined(SYSTEM_css_skycam_c0_system)
-#if defined USE_NEW_AE_STRUCT || defined USE_NEW_AWB_STRUCT
-#define DEFAULT_3A_GRID_INFO \
-{ \
- 0, /* ae_enable */ \
- {0,0,0,0,0,0,0}, /* AE: width,height,b_width,b_height,x_start,y_start*/ \
- 0, /* awb_enable */ \
- {0,0,0,0,0,0}, /* AWB: width,height,b_width,b_height,x_start,y_start*/ \
- 0, /* af_enable */ \
- {0,0,0,0,0,0,0}, /* AF: width,height,b_width,b_height,x_start,y_start,ff_en*/ \
- 0, /* awb_fr_enable */ \
- {0,0,0,0,0,0,0}, /* AWB_FR: width,height,b_width,b_height,x_start,y_start,ff_en*/ \
- 0, /* elem_bit_depth */ \
-}
-#else
-#define DEFAULT_3A_GRID_INFO \
-{ \
- 0, /* ae_enable */ \
- {0,0,0,0,0,0,0,0,0}, /* AE: width,height,b_width,b_height,x_start,y_start,x_end,y_end*/ \
- 0, /* awb_enable */ \
- {0,0,0,0,0,0,0,0}, /* AWB: width,height,b_width,b_height,x_start,y_start,x_end,y_end*/ \
- 0, /* af_enable */ \
- {0,0,0,0,0,0,0}, /* AF: width,height,b_width,b_height,x_start,y_start,ff_en*/ \
- 0, /* awb_fr_enable */ \
- {0,0,0,0,0,0,0}, /* AWB_FR: width,height,b_width,b_height,x_start,y_start,ff_en*/ \
- 0, /* elem_bit_depth */ \
-}
-#endif /* USE_NEW_AE_STRUCT || defined USE_NEW_AWB_STRUCT */
-
-#else
-#define DEFAULT_3A_GRID_INFO \
-{ \
- 0, /* enable */ \
- 0, /* use_dmem */ \
- 0, /* has_histogram */ \
- 0, /* width */ \
- 0, /* height */ \
- 0, /* aligned_width */ \
- 0, /* aligned_height */ \
- 0, /* bqs_per_grid_cell */ \
- 0, /* deci_factor_log2 */ \
- 0, /* elem_bit_depth */ \
-}
-
-#endif
-
/* This struct should be split into 3, for AE, AWB and AF.
* However, that will require driver/ 3A lib modifications.
*/
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/s3a_stat_ls/ia_css_s3a_stat_ls_param.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/s3a_stat_ls/ia_css_s3a_stat_ls_param.h
deleted file mode 100644
index 9aa019539f47..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/s3a_stat_ls/ia_css_s3a_stat_ls_param.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __IA_CSS_S3A_STAT_LS_PARAM_H
-#define __IA_CSS_S3A_STAT_LS_PARAM_H
-
-#include "type_support.h"
-#ifdef ISP2401
-#include "../../io_ls/common/ia_css_common_io_types.h"
-#endif
-
-#define NUM_S3A_LS 1
-
-/* s3a statistics store */
-#ifdef ISP2401
-struct ia_css_s3a_stat_ls_configuration {
- uint32_t s3a_grid_size_log2;
-};
-
-#endif
-struct sh_css_isp_s3a_stat_ls_isp_config {
-#ifndef ISP2401
- uint32_t base_address[NUM_S3A_LS];
- uint32_t width[NUM_S3A_LS];
- uint32_t height[NUM_S3A_LS];
- uint32_t stride[NUM_S3A_LS];
-#endif
- uint32_t s3a_grid_size_log2[NUM_S3A_LS];
-};
-
-#ifndef ISP2401
-
-#endif
-#endif /* __IA_CSS_S3A_STAT_LS_PARAM_H */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/s3a_stat_ls/ia_css_s3a_stat_store_param.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/s3a_stat_ls/ia_css_s3a_stat_store_param.h
deleted file mode 100644
index 676b42d364e8..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/s3a_stat_ls/ia_css_s3a_stat_store_param.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __IA_CSS_S3A_STAT_STORE_PARAM_H
-#define __IA_CSS_S3A_STAT_STORE_PARAM_H
-
-#include "ia_css_s3a_stat_ls_param.h"
-
-
-#endif /* __IA_CSS_S3A_STAT_STORE_PARAM_H */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/scale/scale_1.0/ia_css_scale_param.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/scale/scale_1.0/ia_css_scale_param.h
deleted file mode 100644
index fd19f008ff91..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/scale/scale_1.0/ia_css_scale_param.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef _IA_CSS_SCALE_PARAM_H
-#define _IA_CSS_SCALE_PARAM_H
-
-#include "uds/uds_1.0/ia_css_uds_param.h"
-
-#endif /* _IA_CSS_SCALE_PARAM_H */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/common/ia_css_sdis_common_types.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/common/ia_css_sdis_common_types.h
index 031983c357e4..381e5730d405 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/common/ia_css_sdis_common_types.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/common/ia_css_sdis_common_types.h
@@ -41,20 +41,6 @@ struct ia_css_sdis_info {
uint32_t deci_factor_log2;
};
-#define IA_CSS_DEFAULT_SDIS_INFO \
- { \
- { { 0, 0 }, /* dim */ \
- { 0, 0 }, /* pad */ \
- }, /* grid */ \
- { { 0, 0 }, /* dim */ \
- { 0, 0 }, /* pad */ \
- }, /* coef */ \
- { { 0, 0 }, /* dim */ \
- { 0, 0 }, /* pad */ \
- }, /* proj */ \
- 0, /* dis_deci_factor_log2 */ \
- }
-
/* DVS statistics grid
*
* ISP block: SDVS1 (DIS/DVS Support for DIS/DVS ver.1 (2-axes))
@@ -209,16 +195,17 @@ struct ia_css_dvs_stat_grid_info {
/* DVS statistics generated by accelerator default grid info
*/
-#define DEFAULT_DVS_GRID_INFO { \
-{ \
- { 0, 0, 0}, /* GBL CFG reg: kappa, match_shifrt, binning mode*/ \
- {{{0, 0, 0, 0}, {0, 0, 0}, {0, 0} }, \
- {{0, 0, 0, 0}, {0, 0, 0}, {0, 0} }, \
- {{0, 0, 0, 0}, {0, 0, 0}, {0, 0} } }, \
- {{0, 0, 0, 0}, {4, 0, 0, 0}, {0, 0, 0, 0} } } \
+#define DEFAULT_DVS_GRID_INFO \
+(union ia_css_dvs_grid_u) { \
+ .dvs_stat_grid_info = (struct ia_css_dvs_stat_grid_info) { \
+ .fe_roi_cfg = { \
+ [1] = (struct dvs_stat_public_dvs_level_fe_roi_cfg) { \
+ .x_start = 4 \
+ } \
+ } \
+ } \
}
-
/* Union that holds all types of DVS statistics grid info in
* CSS format
* */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/common/ia_css_sdis_param.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/common/ia_css_sdis_param.h
deleted file mode 100644
index 586cc4315c1f..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/common/ia_css_sdis_param.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __IA_CSS_SDIS_PARAM_COMMON_H
-#define __IA_CSS_SDIS_PARAM_COMMON_H
-
-
-#include "sdis/common/ia_css_sdis_common.host.h"
-
-#endif /* __IA_CSS_SDIS_PARAM_COMMON_H */
-
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c
index 9478c12abe89..0fdd696bf654 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c
@@ -169,8 +169,7 @@ ia_css_sdis_init_info(
unsigned enabled)
{
if (!enabled) {
- struct ia_css_sdis_info default_dis = IA_CSS_DEFAULT_SDIS_INFO;
- *dis = default_dis;
+ *dis = (struct ia_css_sdis_info) { };
return;
}
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_1.0/ia_css_sdis_param.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_1.0/ia_css_sdis_param.h
deleted file mode 100644
index 2dd8696802d0..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_1.0/ia_css_sdis_param.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __IA_CSS_SDIS_PARAM_H
-#define __IA_CSS_SDIS_PARAM_H
-
-#include "sdis.isp.h"
-
-#endif /* __IA_CSS_SDIS_PARAM_H */
-
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_2/ia_css_sdis_param.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_2/ia_css_sdis_param.h
deleted file mode 100644
index cea352e45713..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/sdis/sdis_2/ia_css_sdis_param.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __IA_CSS_SDIS2_PARAM_H
-#define __IA_CSS_SDIS2_PARAM_H
-
-#include "sdis.isp.h"
-
-#endif /* __IA_CSS_SDIS2_PARAM_H */
-
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/tdf/tdf_1.0/ia_css_tdf.host.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/tdf/tdf_1.0/ia_css_tdf.host.c
index e775af51c0c0..78a113bfe8f1 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/tdf/tdf_1.0/ia_css_tdf.host.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/tdf/tdf_1.0/ia_css_tdf.host.c
@@ -15,7 +15,7 @@
#include "ia_css_debug.h"
#include "ia_css_tdf.host.h"
-const int16_t g_pyramid[8][8] = {
+static const int16_t g_pyramid[8][8] = {
{128, 384, 640, 896, 896, 640, 384, 128},
{384, 1152, 1920, 2688, 2688, 1920, 1152, 384},
{640, 1920, 3200, 4480, 4480, 3200, 1920, 640},
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/tdf/tdf_1.0/ia_css_tdf.host.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/tdf/tdf_1.0/ia_css_tdf.host.h
index 1b3e759e41a3..bd628a18e839 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/tdf/tdf_1.0/ia_css_tdf.host.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/tdf/tdf_1.0/ia_css_tdf.host.h
@@ -17,7 +17,6 @@
#include "ia_css_tdf_types.h"
#include "ia_css_tdf_param.h"
-#include "ia_css_tdf_default.host.h"
void
ia_css_tdf_vmem_encode(
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/tdf/tdf_1.0/ia_css_tdf_default.host.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/tdf/tdf_1.0/ia_css_tdf_default.host.c
deleted file mode 100644
index 9bb42daf070d..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/tdf/tdf_1.0/ia_css_tdf_default.host.c
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#include "ia_css_tdf_types.h"
-
-const struct ia_css_tdf_config default_tdf_config = {
- .thres_flat_table = {0},
- .thres_detail_table = {0},
- .epsilon_0 = 4095,
- .epsilon_1 = 5733,
- .eps_scale_text = 409,
- .eps_scale_edge = 3686,
- .sepa_flat = 1294,
- .sepa_edge = 4095,
- .blend_flat = 819,
- .blend_text = 819,
- .blend_edge = 8191,
- .shading_gain = 1024,
- .shading_base_gain = 8191,
- .local_y_gain = 0,
- .local_y_base_gain = 2047,
- .rad_x_origin = 0,
- .rad_y_origin = 0
-};
-
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/tdf/tdf_1.0/ia_css_tdf_default.host.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/tdf/tdf_1.0/ia_css_tdf_default.host.h
deleted file mode 100644
index cd8fb70e5a87..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/tdf/tdf_1.0/ia_css_tdf_default.host.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __IA_CSS_TDF_DEFAULT_HOST_H
-#define __IA_CSS_TDF_DEFAULT_HOST_H
-
-#include "ia_css_tdf_types.h"
-
-extern const struct ia_css_tdf_config default_tdf_config;
-
-#endif /* __IA_CSS_TDF_DEFAULT_HOST_H */
-
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/vf/vf_1.0/ia_css_vf.host.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/vf/vf_1.0/ia_css_vf.host.c
index 5610833ed595..c2076e412410 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/vf/vf_1.0/ia_css_vf.host.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/vf/vf_1.0/ia_css_vf.host.c
@@ -130,11 +130,11 @@ ia_css_vf_configure(
err = configure_kernel(info, out_info, vf_info, downscale_log2, &config);
configure_dma(&config, vf_info);
- if (binary) {
- if (vf_info)
- vf_info->raw_bit_depth = info->dma.vfdec_bits_per_pixel;
- ia_css_configure_vf (binary, &config);
- }
+
+ if (vf_info)
+ vf_info->raw_bit_depth = info->dma.vfdec_bits_per_pixel;
+ ia_css_configure_vf (binary, &config);
+
return IA_CSS_SUCCESS;
}
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/xnr/xnr_3.0/ia_css_xnr3_wrapper_param.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/xnr/xnr_3.0/ia_css_xnr3_wrapper_param.h
deleted file mode 100644
index 1a98555fd5d9..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/xnr/xnr_3.0/ia_css_xnr3_wrapper_param.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __IA_CSS_XNR3_WRAPPER_PARAM_H
-#define __IA_CSS_XNR3_WRAPPER_PARAM_H
-
-#include "ia_css_xnr3_param.h"
-
-#endif
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/yuv_ls/yuv_ls_1.0/ia_css_yuv_load_param.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/yuv_ls/yuv_ls_1.0/ia_css_yuv_load_param.h
deleted file mode 100644
index 400c6790cbf5..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/yuv_ls/yuv_ls_1.0/ia_css_yuv_load_param.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __IA_CSS_YUV_LOAD_PARAM_H
-#define __IA_CSS_YUV_LOAD_PARAM_H
-
-#include "ia_css_yuv_ls_param.h"
-
-#endif /* __IA_CSS_YUV_LOAD_PARAM_H */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/yuv_ls/yuv_ls_1.0/ia_css_yuv_ls_param.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/yuv_ls/yuv_ls_1.0/ia_css_yuv_ls_param.h
deleted file mode 100644
index c9ff0cb2493a..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/yuv_ls/yuv_ls_1.0/ia_css_yuv_ls_param.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __IA_CSS_YUV_LS_PARAM_H
-#define __IA_CSS_YUV_LS_PARAM_H
-
-#include "type_support.h"
-#ifndef ISP2401
-
-/* The number of load/store kernels in a pipeline can be greater than one.
- * A kernel can consume more than one input or can produce more
- * than one output.
- */
-#define NUM_YUV_LS 2
-
-/* YUV load/store */
-struct sh_css_isp_yuv_ls_isp_config {
- unsigned base_address[NUM_YUV_LS];
- unsigned width[NUM_YUV_LS];
- unsigned height[NUM_YUV_LS];
- unsigned stride[NUM_YUV_LS];
-};
-
-#else
-#include "../../io_ls/common/ia_css_common_io_types.h"
-#endif
-
-#endif /* __IA_CSS_YUV_LS_PARAM_H */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/yuv_ls/yuv_ls_1.0/ia_css_yuv_store_param.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/yuv_ls/yuv_ls_1.0/ia_css_yuv_store_param.h
deleted file mode 100644
index 69c474ea1ffd..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/kernels/yuv_ls/yuv_ls_1.0/ia_css_yuv_store_param.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-
-#ifndef __IA_CSS_YUV_STORE_PARAM_H
-#define __IA_CSS_YUV_STORE_PARAM_H
-
-#include "ia_css_yuv_ls_param.h"
-
-
-#endif /* __IA_CSS_YUV_STORE_PARAM_H */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/modes/interface/isp_exprs.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/modes/interface/isp_exprs.h
deleted file mode 100644
index e625ba62cc15..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/isp/modes/interface/isp_exprs.h
+++ /dev/null
@@ -1,286 +0,0 @@
-#ifndef ISP2401
-/*
- * Support for Intel Camera Imaging ISP subsystem.
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
- */
-#else
-/**
-Support for Intel Camera Imaging ISP subsystem.
-Copyright (c) 2010 - 2015, Intel Corporation.
-
-This program is free software; you can redistribute it and/or modify it
-under the terms and conditions 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.
-*/
-#endif
-
-#ifndef _COMMON_ISP_EXPRS_H_
-#define _COMMON_ISP_EXPRS_H_
-
-/* Binary independent pre-processor expressions */
-
-#include "sh_css_defs.h"
-#include "isp_const.h"
-
-#ifdef __HOST
-#error "isp_exprs.h: Do not include on HOST, contains ISP specific defines"
-#endif
-
-#ifndef __ISP
-#if defined(MODE)
-#define MODE aap
-#error "isp_exprs.h: is mode independent, but MODE is set"
-#endif
-#if defined(VARIABLE_RESOLUTION)
-#define VARIABLE_RESOLUTION noot
-#error "isp_exprs.h: is mode independent, but VARIABLE_RESOLUTION is set"
-#endif
-#if defined(DECI_FACTOR_LOG2)
-#define DECI_FACTOR_LOG2 mies
-#error "isp_exprs.h: is mode independent, but DECI_FACTOR_LOG2 is set"
-#endif
-#endif
-
-#define LOG_VECTOR_STEP _ISP_LOG_VECTOR_STEP(MODE)
-/* should be even and multiple of vf downscaling */
-#define ISP_OUTPUT_CHUNK_LOG_FACTOR (MAX_VF_LOG_DOWNSCALE<=1 ? LOG_VECTOR_STEP : \
- umax(VF_LOG_DOWNSCALE, LOG_VECTOR_STEP))
-
-#define CEIL_DIV_CHUNKS(n,c) ((c) == 1 ? (n) \
- : CEIL_SHIFT(CEIL_DIV((n), (c)), ISP_OUTPUT_CHUNK_LOG_FACTOR)<<ISP_OUTPUT_CHUNK_LOG_FACTOR)
-
-
-#define ISP_VARIABLE_INPUT (ISP_INPUT == IA_CSS_BINARY_INPUT_VARIABLE)
-
-/* Binary independent versions, see isp_defs.h for binary dependent ones */
-#ifndef __ISP
-#define IMAGEFORMAT_IS_RAW(fmt) ((fmt) == IA_CSS_FRAME_FORMAT_RAW)
-
-#define IMAGEFORMAT_IS_RAW_INTERLEAVED(fmt) ((fmt) == IA_CSS_FRAME_FORMAT_RAW)
-
-#define IMAGEFORMAT_IS_RGB(fmt) ((fmt) == IA_CSS_FRAME_FORMAT_RGBA888 || (fmt) == IA_CSS_FRAME_FORMAT_PLANAR_RGB888 || \
- (fmt) == IA_CSS_FRAME_FORMAT_RGB565)
-
-#define IMAGEFORMAT_IS_RGB_INTERLEAVED(fmt) ((fmt) == IA_CSS_FRAME_FORMAT_RGBA888 || (fmt) == IA_CSS_FRAME_FORMAT_RGB565)
-
-#define IMAGEFORMAT_UV_INTERLEAVED(fmt) ((fmt) == IA_CSS_FRAME_FORMAT_NV11 || \
- (fmt) == IA_CSS_FRAME_FORMAT_NV12 || (fmt) == IA_CSS_FRAME_FORMAT_NV21 || \
- (fmt) == IA_CSS_FRAME_FORMAT_NV16 || (fmt) == IA_CSS_FRAME_FORMAT_NV61 || \
- (fmt) == IA_CSS_FRAME_FORMAT_UYVY || (fmt) == IA_CSS_FRAME_FORMAT_YUYV || \
- (fmt) == IA_CSS_FRAME_FORMAT_NV12_16 || (fmt) == IA_CSS_FRAME_FORMAT_NV12_TILEY)
-
-#define IMAGEFORMAT_YUV_INTERLEAVED(fmt) ((fmt) == IA_CSS_FRAME_FORMAT_UYVY || (fmt) == IA_CSS_FRAME_FORMAT_YUYV)
-
-#define IMAGEFORMAT_INTERLEAVED(fmt) (IMAGEFORMAT_UV_INTERLEAVED(fmt) || IMAGEFORMAT_IS_RGB_INTERLEAVED(fmt))
-
-#define IMAGEFORMAT_SUB_SAMPL_420(fmt) ((fmt) == IA_CSS_FRAME_FORMAT_YUV420 || (fmt) == IA_CSS_FRAME_FORMAT_YV12 || \
- (fmt) == IA_CSS_FRAME_FORMAT_NV12 || (fmt) == IA_CSS_FRAME_FORMAT_NV21 || \
- (fmt) == IA_CSS_FRAME_FORMAT_NV12_16 || (fmt) == IA_CSS_FRAME_FORMAT_NV12TILEY)
-
-#define IMAGEFORMAT_SUB_SAMPL_422(fmt) ((fmt) == IA_CSS_FRAME_FORMAT_YUV422 || (fmt) == IA_CSS_FRAME_FORMAT_YV16 || \
- (fmt) == IA_CSS_FRAME_FORMAT_NV16 || (fmt) == IA_CSS_FRAME_FORMAT_NV61)
-
-#define IMAGEFORMAT_SUB_SAMPL_444(fmt) ((fmt) == IA_CSS_FRAME_FORMAT_YUV444)
-
-#define IMAGEFORMAT_UV_SWAPPED(fmt) ((fmt) == IA_CSS_FRAME_FORMAT_NV21 || (fmt) == IA_CSS_FRAME_FORMAT_NV61)
-
-#define IMAGEFORMAT_IS_RGBA(fmt) ((fmt) == IA_CSS_FRAME_FORMAT_RGBA888)
-
-#define IMAGEFORMAT_IS_NV11(fmt) ((fmt) == IA_CSS_FRAME_FORMAT_NV11)
-
-#define IMAGEFORMAT_IS_16BIT(fmt) ((fmt) == IA_CSS_FRAME_FORMAT_YUV420_16 || (fmt) == IA_CSS_FRAME_FORMAT_NV12_16 || (fmt) == IA_CSS_FRAME_FORMAT_YUV422_16)
-
-#endif
-
-
-/******** GDCAC settings *******/
-#define GDCAC_BPP ISP_VEC_ELEMBITS /* We use 14 bits per pixel component for the GDCAC mode */
-#define GDC_INPUT_BLOCK_WIDTH 2 /* Two vectors are needed */
-#define GDC_OUTPUT_BLOCK_WIDTH 1 /* One vector is produced */
-
-#if ISP_VEC_NELEMS == 16
-/* For 16*16 output block, the distortion fits in 13.312 lines __ALWAYS__ */
-#define GDC_INPUT_BLOCK_HEIGHT 14
-#elif ISP_VEC_NELEMS == 64
-/* For 64*64 output block, the distortion fits in 47. lines __ALWAYS__ */
-#define GDC_INPUT_BLOCK_HEIGHT 48
-#endif
-/*******************************/
-
-
-#define ENABLE_HUP ((isp_input_width - isp_envelope_width) < isp_output_width)
-#define ENABLE_VUP ((isp_input_height - isp_envelope_height) < isp_output_height)
-
-#define ISP_INPUT_WIDTH (ENABLE_DS | ENABLE_HUP ? isp_input_width : ISP_INTERNAL_WIDTH)
-#define ISP_INPUT_HEIGHT (ENABLE_DS | ENABLE_VUP ? isp_input_height : isp_internal_height)
-
-#define DECI_FACTOR_LOG2 (ISP_FIXED_S3A_DECI_LOG ? ISP_FIXED_S3A_DECI_LOG : isp_deci_log_factor)
-
-#define ISP_S3ATBL_WIDTH \
- _ISP_S3ATBL_ISP_WIDTH(_ISP_S3A_ELEMS_ISP_WIDTH((ENABLE_HUP ? ISP_INTERNAL_WIDTH : ISP_INPUT_WIDTH), ISP_LEFT_CROPPING), \
- DECI_FACTOR_LOG2)
-#define S3ATBL_WIDTH_BYTES (sizeof(struct ia_css_3a_output) * ISP_S3ATBL_WIDTH)
-#define S3ATBL_WIDTH_SHORTS (S3ATBL_WIDTH_BYTES / sizeof(short))
-
-/* should be even?? */
-#define ISP_UV_OUTPUT_CHUNK_VECS CEIL_DIV(ISP_OUTPUT_CHUNK_VECS, 2)
-
-
-#if defined(__ISP) || defined(INIT_VARS)
-
-#define ISP_USE_IF (ISP_INPUT == IA_CSS_BINARY_INPUT_MEMORY ? 0 : \
- ISP_INPUT == IA_CSS_BINARY_INPUT_SENSOR ? 1 : \
- isp_online)
-
-#define ISP_DVS_ENVELOPE_WIDTH 0
-#define ISP_DVS_ENVELOPE_HEIGHT 0
-
-#define _ISP_INPUT_WIDTH_VECS _ISP_VECS(ISP_INPUT_WIDTH)
-
-#if !defined(__ISP) || (VARIABLE_RESOLUTION && !__HOST)
-#define ISP_INPUT_WIDTH_VECS isp_vectors_per_input_line
-#else
-#define ISP_INPUT_WIDTH_VECS _ISP_INPUT_WIDTH_VECS
-#endif
-
-#if !defined(__ISP) || VARIABLE_RESOLUTION
-#define ISP_INTERNAL_WIDTH_VECS isp_vectors_per_line
-#else
-#define ISP_INTERNAL_WIDTH_VECS _ISP_INTERNAL_WIDTH_VECS
-#endif
-
-#define _ISP_INTERNAL_HEIGHT __ISP_INTERNAL_HEIGHT(isp_output_height, ISP_TOP_CROPPING, ISP_DVS_ENVELOPE_HEIGHT)
-
-#define ISP_INTERNAL_HEIGHT isp_internal_height
-
-#define _ISP_INTERNAL_WIDTH __ISP_INTERNAL_WIDTH(ISP_OUTPUT_WIDTH, ISP_DVS_ENVELOPE_WIDTH, \
- ISP_LEFT_CROPPING, MODE, ISP_C_SUBSAMPLING, \
- OUTPUT_NUM_CHUNKS, ISP_PIPELINING)
-
-#define ISP_UV_INTERNAL_WIDTH (ISP_INTERNAL_WIDTH / 2)
-#define ISP_UV_INTERNAL_HEIGHT (ISP_INTERNAL_HEIGHT / 2)
-
-#define _ISP_INTERNAL_WIDTH_VECS (_ISP_INTERNAL_WIDTH / ISP_VEC_NELEMS)
-#define _ISP_UV_INTERNAL_WIDTH_VECS CEIL_DIV(ISP_UV_INTERNAL_WIDTH, ISP_VEC_NELEMS)
-
-#define ISP_VF_OUTPUT_WIDTH _ISP_VF_OUTPUT_WIDTH(ISP_VF_OUTPUT_WIDTH_VECS)
-#define ISP_VF_OUTPUT_HEIGHT _ISP_VF_OUTPUT_HEIGHT(isp_output_height, VF_LOG_DOWNSCALE)
-
-#if defined (__ISP) && !VARIABLE_RESOLUTION
-#define ISP_INTERNAL_WIDTH _ISP_INTERNAL_WIDTH
-#define ISP_VF_OUTPUT_WIDTH_VECS _ISP_VF_OUTPUT_WIDTH_VECS
-#else
-#define ISP_INTERNAL_WIDTH (VARIABLE_RESOLUTION ? isp_internal_width : _ISP_INTERNAL_WIDTH)
-#define ISP_VF_OUTPUT_WIDTH_VECS (VARIABLE_RESOLUTION ? isp_vf_output_width_vecs : _ISP_VF_OUTPUT_WIDTH_VECS)
-#endif
-
-#if defined(__ISP) && !VARIABLE_RESOLUTION
-#define ISP_OUTPUT_WIDTH ISP_MAX_OUTPUT_WIDTH
-#define VF_LOG_DOWNSCALE MAX_VF_LOG_DOWNSCALE
-#else
-#define ISP_OUTPUT_WIDTH isp_output_width
-#define VF_LOG_DOWNSCALE isp_vf_downscale_bits
-#endif
-
-#if !defined(__ISP) || VARIABLE_RESOLUTION
-#define _ISP_MAX_VF_OUTPUT_WIDTH __ISP_MAX_VF_OUTPUT_WIDTH(2*SH_CSS_MAX_VF_WIDTH, ISP_LEFT_CROPPING)
-#elif defined(MODE) && MODE == IA_CSS_BINARY_MODE_PRIMARY && ISP_OUTPUT_WIDTH > 3328
-/* Because of vmem issues, should be fixed later */
-#define _ISP_MAX_VF_OUTPUT_WIDTH (SH_CSS_MAX_VF_WIDTH - 2*ISP_VEC_NELEMS + (ISP_LEFT_CROPPING ? 2 * ISP_VEC_NELEMS : 0))
-#else
-#define _ISP_MAX_VF_OUTPUT_WIDTH (ISP_VF_OUTPUT_WIDTH + (ISP_LEFT_CROPPING ? (2 >> VF_LOG_DOWNSCALE) * ISP_VEC_NELEMS : 0))
-#endif
-
-#define ISP_MAX_VF_OUTPUT_VECS CEIL_DIV(_ISP_MAX_VF_OUTPUT_WIDTH, ISP_VEC_NELEMS)
-
-
-
-#define ISP_MIN_STRIPE_WIDTH (ISP_PIPELINING * (1<<_ISP_LOG_VECTOR_STEP(MODE)))
-
-/******* STRIPING-RELATED MACROS *******/
-#define NO_STRIPING (ISP_NUM_STRIPES == 1)
-
-#define ISP_OUTPUT_CHUNK_VECS \
- (NO_STRIPING ? CEIL_DIV_CHUNKS(ISP_OUTPUT_VECS_EXTRA_CROP, OUTPUT_NUM_CHUNKS) \
- : ISP_IO_STRIPE_WIDTH_VECS(ISP_OUTPUT_VECS_EXTRA_CROP, ISP_LEFT_PADDING_VECS, ISP_NUM_STRIPES, ISP_MIN_STRIPE_WIDTH) )
-
-#define VECTORS_PER_LINE \
- (NO_STRIPING ? ISP_INTERNAL_WIDTH_VECS \
- : ISP_IO_STRIPE_WIDTH_VECS(ISP_INTERNAL_WIDTH_VECS, ISP_LEFT_PADDING_VECS, ISP_NUM_STRIPES, ISP_MIN_STRIPE_WIDTH) )
-
-#define VECTORS_PER_INPUT_LINE \
- (NO_STRIPING ? ISP_INPUT_WIDTH_VECS \
- : ISP_IO_STRIPE_WIDTH_VECS(ISP_INPUT_WIDTH_VECS, ISP_LEFT_PADDING_VECS, ISP_NUM_STRIPES, ISP_MIN_STRIPE_WIDTH)+_ISP_EXTRA_PADDING_VECS)
-
-
-#define ISP_MAX_VF_OUTPUT_STRIPE_VECS \
- (NO_STRIPING ? ISP_MAX_VF_OUTPUT_VECS \
- : CEIL_MUL(CEIL_DIV(ISP_MAX_VF_OUTPUT_VECS, ISP_NUM_STRIPES), 2))
-#define _ISP_VF_OUTPUT_WIDTH_VECS \
- (NO_STRIPING ? __ISP_VF_OUTPUT_WIDTH_VECS(ISP_OUTPUT_WIDTH, VF_LOG_DOWNSCALE) \
- : __ISP_VF_OUTPUT_WIDTH_VECS(CEIL_DIV(ISP_OUTPUT_WIDTH, ISP_NUM_STRIPES), VF_LOG_DOWNSCALE))
-
-#define ISP_IO_STRIPE_WIDTH_VECS(width, padding, num_stripes, min_stripe) \
- MAX(CEIL_MUL(padding + CEIL_DIV(width-padding, num_stripes) \
- , 2) \
- , min_stripe)
-////////// INPUT & INTERNAL
-/* should be even */
-#define INPUT_NUM_CHUNKS OUTPUT_NUM_CHUNKS
-
-#define INPUT_VECTORS_PER_CHUNK CEIL_DIV_CHUNKS(VECTORS_PER_INPUT_LINE, INPUT_NUM_CHUNKS)
-
-/* only for ISP code, will be removed: */
-#define VECTORS_PER_FULL_LINE ISP_INTERNAL_WIDTH_VECS
-#define VECTORS_PER_INPUT_FULL_LINE ISP_INPUT_WIDTH_VECS
-
-////////// OUTPUT
-/* should at least even and also multiple of vf scaling */
-#define ISP_OUTPUT_VECS_EXTRA_CROP CEIL_DIV(ISP_OUTPUT_WIDTH_EXTRA_CROP, ISP_VEC_NELEMS)
-
-/* Output is decoupled from input */
-#define ISP_OUTPUT_WIDTH_EXTRA_CROP CEIL_MUL(CEIL_MUL((ENABLE_DVS_ENVELOPE ? ISP_OUTPUT_WIDTH : ISP_INTERNAL_WIDTH), 2*ISP_VEC_NELEMS), \
- ISP_C_SUBSAMPLING * OUTPUT_NUM_CHUNKS * HIVE_ISP_DDR_WORD_BYTES)
-
-#define ISP_MAX_VF_OUTPUT_CHUNK_VECS \
- (NO_CHUNKING ? ISP_MAX_VF_OUTPUT_STRIPE_VECS \
- : 2*CEIL_DIV(ISP_MAX_VF_OUTPUT_STRIPE_VECS, 2*OUTPUT_NUM_CHUNKS))
-
-#define OUTPUT_VECTORS_PER_CHUNK CEIL_DIV_CHUNKS(VECTORS_PER_LINE,OUTPUT_NUM_CHUNKS)
-
-/* should be even?? */
-#define OUTPUT_C_VECTORS_PER_CHUNK CEIL_DIV(OUTPUT_VECTORS_PER_CHUNK, 2)
-
-#ifndef ISP2401
-/**** SCTBL defs *******/
-#define ISP_SCTBL_HEIGHT \
- _ISP_SCTBL_HEIGHT(ISP_INPUT_HEIGHT, DECI_FACTOR_LOG2)
-
-#endif
-/**** UDS defs *********/
-#define UDS_DMACH_STRIDE_B_IN_Y (( ISP_INTERNAL_WIDTH /BITS8_ELEMENTS_PER_XMEM_ADDR)*HIVE_ISP_DDR_WORD_BYTES)
-#define UDS_DMACH_STRIDE_B_IN_C (((ISP_INTERNAL_WIDTH/2)/BITS8_ELEMENTS_PER_XMEM_ADDR)*HIVE_ISP_DDR_WORD_BYTES)
-
-#else /* defined(__ISP) || defined(INIT_VARS) */
-
-#define ISP_INTERNAL_WIDTH isp_internal_width
-#define ISP_INTERNAL_HEIGHT isp_internal_height
-
-#endif /* defined(__ISP) || defined(INIT_VARS) */
-
-#endif /* _COMMON_ISP_EXPRS_H_ */
-
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/binary/interface/ia_css_binary.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/binary/interface/ia_css_binary.h
index 5a58abe2b233..b62c4d321a4e 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/binary/interface/ia_css_binary.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/binary/interface/ia_css_binary.h
@@ -93,17 +93,6 @@ struct ia_css_cas_binary_descr {
bool *is_output_stage;
};
-#define IA_CSS_DEFAULT_CAS_BINARY_DESCR \
-{ \
- 0, \
- 0, \
- NULL, \
- NULL, \
- NULL, \
- NULL, \
- NULL, \
-}
-
struct ia_css_binary_descr {
int mode;
bool online;
@@ -124,7 +113,7 @@ struct ia_css_binary_descr {
#endif
bool enable_capture_pp_bli;
struct ia_css_resolution dvs_env;
- enum ia_css_stream_format stream_format;
+ enum atomisp_input_format stream_format;
struct ia_css_frame_info *in_info; /* the info of the input-frame with the
ISP required resolution. */
struct ia_css_frame_info *bds_out_info;
@@ -137,7 +126,7 @@ struct ia_css_binary_descr {
struct ia_css_binary {
const struct ia_css_binary_xinfo *info;
- enum ia_css_stream_format input_format;
+ enum atomisp_input_format input_format;
struct ia_css_frame_info in_frame_info;
struct ia_css_frame_info internal_frame_info;
struct ia_css_frame_info out_frame_info[IA_CSS_BINARY_MAX_OUTPUT_PORTS];
@@ -171,80 +160,15 @@ struct ia_css_binary {
struct ia_css_isp_param_css_segments css_params;
};
-#ifdef ISP2401
-
#define IA_CSS_BINARY_DEFAULT_SETTINGS \
-{ \
- NULL, \
- IA_CSS_STREAM_FORMAT_YUV420_8_LEGACY, \
- IA_CSS_BINARY_DEFAULT_FRAME_INFO, \
- IA_CSS_BINARY_DEFAULT_FRAME_INFO, \
- {IA_CSS_BINARY_DEFAULT_FRAME_INFO}, \
- { 0, 0},/* effective_in_frame_res */ \
- IA_CSS_BINARY_DEFAULT_FRAME_INFO, \
- 0, /* input_buf_vectors */ \
- 0, /* deci_factor_log2 */ \
- 0, /* vf_downscale_log2 */ \
- 0, /* s3atbl_width */ \
- 0, /* s3atbl_height */ \
- 0, /* s3atbl_isp_width */ \
- 0, /* s3atbl_isp_height */ \
- 0, /* morph_tbl_width */ \
- 0, /* morph_tbl_aligned_width */ \
- 0, /* morph_tbl_height */ \
- 0, /* sctbl_width_per_color */ \
- 0, /* sctbl_aligned_width_per_color */ \
- 0, /* sctbl_height */ \
- 0, /* sctbl_legacy_width_per_color */ \
- 0, /* sctbl_legacy_height */ \
- IA_CSS_DEFAULT_SDIS_INFO, /* dis */ \
- { 0, 0},/* dvs_envelope_info */ \
- false, /* online */ \
- 0, /* uds_xc */ \
- 0, /* uds_yc */ \
- 0, /* left_padding */ \
- DEFAULT_BINARY_METRICS, /* metrics */ \
- IA_CSS_DEFAULT_ISP_MEM_PARAMS, /* mem_params */ \
- IA_CSS_DEFAULT_ISP_CSS_PARAMS, /* css_params */ \
+(struct ia_css_binary) { \
+ .input_format = ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY, \
+ .in_frame_info = IA_CSS_BINARY_DEFAULT_FRAME_INFO, \
+ .internal_frame_info = IA_CSS_BINARY_DEFAULT_FRAME_INFO, \
+ .out_frame_info = {IA_CSS_BINARY_DEFAULT_FRAME_INFO}, \
+ .vf_frame_info = IA_CSS_BINARY_DEFAULT_FRAME_INFO, \
}
-#else
-
-#define IA_CSS_BINARY_DEFAULT_SETTINGS \
-{ \
- NULL, \
- IA_CSS_STREAM_FORMAT_YUV420_8_LEGACY, \
- IA_CSS_BINARY_DEFAULT_FRAME_INFO, \
- IA_CSS_BINARY_DEFAULT_FRAME_INFO, \
- {IA_CSS_BINARY_DEFAULT_FRAME_INFO}, \
- { 0, 0},/* effective_in_frame_res */ \
- IA_CSS_BINARY_DEFAULT_FRAME_INFO, \
- 0, /* input_buf_vectors */ \
- 0, /* deci_factor_log2 */ \
- 0, /* vf_downscale_log2 */ \
- 0, /* s3atbl_width */ \
- 0, /* s3atbl_height */ \
- 0, /* s3atbl_isp_width */ \
- 0, /* s3atbl_isp_height */ \
- 0, /* morph_tbl_width */ \
- 0, /* morph_tbl_aligned_width */ \
- 0, /* morph_tbl_height */ \
- 0, /* sctbl_width_per_color */ \
- 0, /* sctbl_aligned_width_per_color */ \
- 0, /* sctbl_height */ \
- IA_CSS_DEFAULT_SDIS_INFO, /* dis */ \
- { 0, 0},/* dvs_envelope_info */ \
- false, /* online */ \
- 0, /* uds_xc */ \
- 0, /* uds_yc */ \
- 0, /* left_padding */ \
- DEFAULT_BINARY_METRICS, /* metrics */ \
- IA_CSS_DEFAULT_ISP_MEM_PARAMS, /* mem_params */ \
- IA_CSS_DEFAULT_ISP_CSS_PARAMS, /* css_params */ \
-}
-
-#endif
-
enum ia_css_err
ia_css_binary_init_infos(void);
@@ -255,7 +179,7 @@ enum ia_css_err
ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo,
bool online,
bool two_ppc,
- enum ia_css_stream_format stream_format,
+ enum atomisp_input_format stream_format,
const struct ia_css_frame_info *in_info,
const struct ia_css_frame_info *bds_out_info,
const struct ia_css_frame_info *out_info[],
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/binary/src/binary.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/binary/src/binary.c
index 295e07049393..0cd6e1da43cf 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/binary/src/binary.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/binary/src/binary.c
@@ -490,7 +490,6 @@ ia_css_binary_get_shading_info_type_1(const struct ia_css_binary *binary, /* [in
struct sh_css_shading_table_bayer_origin_compute_results res;
#else
struct sh_css_binary_sc_requirements scr;
- struct ia_css_shading_info default_shading_info_type_1 = DEFAULT_SHADING_INFO_TYPE_1;
#endif
#ifndef ISP2401
@@ -542,7 +541,7 @@ ia_css_binary_get_shading_info_type_1(const struct ia_css_binary *binary, /* [in
&res);
if (err != IA_CSS_SUCCESS)
#else
- *shading_info = default_shading_info_type_1;
+ *shading_info = DEFAULT_SHADING_INFO_TYPE_1;
err = sh_css_binary_get_sc_requirements(binary, required_bds_factor, stream_config, &scr);
if (err != IA_CSS_SUCCESS) {
@@ -862,7 +861,7 @@ binary_supports_output_format(const struct ia_css_binary_xinfo *info,
#ifdef ISP2401
static bool
binary_supports_input_format(const struct ia_css_binary_xinfo *info,
- enum ia_css_stream_format format)
+ enum atomisp_input_format format)
{
assert(info != NULL);
@@ -1089,7 +1088,7 @@ enum ia_css_err
ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo,
bool online,
bool two_ppc,
- enum ia_css_stream_format stream_format,
+ enum atomisp_input_format stream_format,
const struct ia_css_frame_info *in_info, /* can be NULL */
const struct ia_css_frame_info *bds_out_info, /* can be NULL */
const struct ia_css_frame_info *out_info[], /* can be NULL */
@@ -1383,7 +1382,7 @@ ia_css_binary_find(struct ia_css_binary_descr *descr,
int mode;
bool online;
bool two_ppc;
- enum ia_css_stream_format stream_format;
+ enum atomisp_input_format stream_format;
const struct ia_css_frame_info *req_in_info,
*req_bds_out_info,
*req_out_info[IA_CSS_BINARY_MAX_OUTPUT_PORTS],
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/bufq/src/bufq.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/bufq/src/bufq.c
index e50d9f2e2609..ffbcdd80d934 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/bufq/src/bufq.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/bufq/src/bufq.c
@@ -90,12 +90,11 @@ struct sh_css_queues {
#endif
-struct sh_css_queues css_queues;
-
-
/*******************************************************
*** Static variables
********************************************************/
+static struct sh_css_queues css_queues;
+
static int buffer_type_to_queue_id_map[SH_CSS_MAX_SP_THREADS][IA_CSS_NUM_DYNAMIC_BUFFER_TYPE];
static bool queue_availability[SH_CSS_MAX_SP_THREADS][SH_CSS_MAX_NUM_QUEUES];
@@ -207,7 +206,7 @@ static void map_buffer_type_to_queue_id(
}
for (i = SH_CSS_QUEUE_C_ID; i < SH_CSS_MAX_NUM_QUEUES; i++) {
- if (queue_availability[thread_id][i] == true) {
+ if (queue_availability[thread_id][i]) {
queue_availability[thread_id][i] = false;
buffer_type_to_queue_id_map[thread_id][buf_type] = i;
break;
@@ -266,7 +265,7 @@ static ia_css_queue_t *bufq_get_qhandle(
case sh_css_sp2host_isys_event_queue:
q = &css_queues.sp2host_isys_event_queue_handle;
break;
-#endif
+#endif
case sh_css_host2sp_tag_cmd_queue:
q = &css_queues.host2sp_tag_cmd_queue_handle;
break;
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/src/ia_css_debug.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/src/ia_css_debug.c
index f22d73b56bc6..4607a76dc78a 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/src/ia_css_debug.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/debug/src/ia_css_debug.c
@@ -110,9 +110,6 @@
/* Global variable to store the dtrace verbosity level */
unsigned int ia_css_debug_trace_level = IA_CSS_DEBUG_WARNING;
-/* Assumes that IA_CSS_STREAM_FORMAT_BINARY_8 is last */
-#define N_IA_CSS_STREAM_FORMAT (IA_CSS_STREAM_FORMAT_BINARY_8+1)
-
#define DPG_START "ia_css_debug_pipe_graph_dump_start "
#define DPG_END " ia_css_debug_pipe_graph_dump_end\n"
@@ -141,8 +138,8 @@ static struct pipe_graph_class {
int width;
int eff_height;
int eff_width;
- enum ia_css_stream_format stream_format;
-} pg_inst = {true, 0, 0, 0, 0, N_IA_CSS_STREAM_FORMAT};
+ enum atomisp_input_format stream_format;
+} pg_inst = {true, 0, 0, 0, 0, N_ATOMISP_INPUT_FORMAT};
static const char * const queue_id_to_str[] = {
/* [SH_CSS_QUEUE_A_ID] =*/ "queue_A",
@@ -261,86 +258,86 @@ unsigned int ia_css_debug_get_dtrace_level(void)
return ia_css_debug_trace_level;
}
-static const char *debug_stream_format2str(const enum ia_css_stream_format stream_format)
+static const char *debug_stream_format2str(const enum atomisp_input_format stream_format)
{
switch (stream_format) {
- case IA_CSS_STREAM_FORMAT_YUV420_8_LEGACY:
+ case ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY:
return "yuv420-8-legacy";
- case IA_CSS_STREAM_FORMAT_YUV420_8:
+ case ATOMISP_INPUT_FORMAT_YUV420_8:
return "yuv420-8";
- case IA_CSS_STREAM_FORMAT_YUV420_10:
+ case ATOMISP_INPUT_FORMAT_YUV420_10:
return "yuv420-10";
- case IA_CSS_STREAM_FORMAT_YUV420_16:
+ case ATOMISP_INPUT_FORMAT_YUV420_16:
return "yuv420-16";
- case IA_CSS_STREAM_FORMAT_YUV422_8:
+ case ATOMISP_INPUT_FORMAT_YUV422_8:
return "yuv422-8";
- case IA_CSS_STREAM_FORMAT_YUV422_10:
+ case ATOMISP_INPUT_FORMAT_YUV422_10:
return "yuv422-10";
- case IA_CSS_STREAM_FORMAT_YUV422_16:
+ case ATOMISP_INPUT_FORMAT_YUV422_16:
return "yuv422-16";
- case IA_CSS_STREAM_FORMAT_RGB_444:
+ case ATOMISP_INPUT_FORMAT_RGB_444:
return "rgb444";
- case IA_CSS_STREAM_FORMAT_RGB_555:
+ case ATOMISP_INPUT_FORMAT_RGB_555:
return "rgb555";
- case IA_CSS_STREAM_FORMAT_RGB_565:
+ case ATOMISP_INPUT_FORMAT_RGB_565:
return "rgb565";
- case IA_CSS_STREAM_FORMAT_RGB_666:
+ case ATOMISP_INPUT_FORMAT_RGB_666:
return "rgb666";
- case IA_CSS_STREAM_FORMAT_RGB_888:
+ case ATOMISP_INPUT_FORMAT_RGB_888:
return "rgb888";
- case IA_CSS_STREAM_FORMAT_RAW_6:
+ case ATOMISP_INPUT_FORMAT_RAW_6:
return "raw6";
- case IA_CSS_STREAM_FORMAT_RAW_7:
+ case ATOMISP_INPUT_FORMAT_RAW_7:
return "raw7";
- case IA_CSS_STREAM_FORMAT_RAW_8:
+ case ATOMISP_INPUT_FORMAT_RAW_8:
return "raw8";
- case IA_CSS_STREAM_FORMAT_RAW_10:
+ case ATOMISP_INPUT_FORMAT_RAW_10:
return "raw10";
- case IA_CSS_STREAM_FORMAT_RAW_12:
+ case ATOMISP_INPUT_FORMAT_RAW_12:
return "raw12";
- case IA_CSS_STREAM_FORMAT_RAW_14:
+ case ATOMISP_INPUT_FORMAT_RAW_14:
return "raw14";
- case IA_CSS_STREAM_FORMAT_RAW_16:
+ case ATOMISP_INPUT_FORMAT_RAW_16:
return "raw16";
- case IA_CSS_STREAM_FORMAT_BINARY_8:
+ case ATOMISP_INPUT_FORMAT_BINARY_8:
return "binary8";
- case IA_CSS_STREAM_FORMAT_GENERIC_SHORT1:
+ case ATOMISP_INPUT_FORMAT_GENERIC_SHORT1:
return "generic-short1";
- case IA_CSS_STREAM_FORMAT_GENERIC_SHORT2:
+ case ATOMISP_INPUT_FORMAT_GENERIC_SHORT2:
return "generic-short2";
- case IA_CSS_STREAM_FORMAT_GENERIC_SHORT3:
+ case ATOMISP_INPUT_FORMAT_GENERIC_SHORT3:
return "generic-short3";
- case IA_CSS_STREAM_FORMAT_GENERIC_SHORT4:
+ case ATOMISP_INPUT_FORMAT_GENERIC_SHORT4:
return "generic-short4";
- case IA_CSS_STREAM_FORMAT_GENERIC_SHORT5:
+ case ATOMISP_INPUT_FORMAT_GENERIC_SHORT5:
return "generic-short5";
- case IA_CSS_STREAM_FORMAT_GENERIC_SHORT6:
+ case ATOMISP_INPUT_FORMAT_GENERIC_SHORT6:
return "generic-short6";
- case IA_CSS_STREAM_FORMAT_GENERIC_SHORT7:
+ case ATOMISP_INPUT_FORMAT_GENERIC_SHORT7:
return "generic-short7";
- case IA_CSS_STREAM_FORMAT_GENERIC_SHORT8:
+ case ATOMISP_INPUT_FORMAT_GENERIC_SHORT8:
return "generic-short8";
- case IA_CSS_STREAM_FORMAT_YUV420_8_SHIFT:
+ case ATOMISP_INPUT_FORMAT_YUV420_8_SHIFT:
return "yuv420-8-shift";
- case IA_CSS_STREAM_FORMAT_YUV420_10_SHIFT:
+ case ATOMISP_INPUT_FORMAT_YUV420_10_SHIFT:
return "yuv420-10-shift";
- case IA_CSS_STREAM_FORMAT_EMBEDDED:
+ case ATOMISP_INPUT_FORMAT_EMBEDDED:
return "embedded-8";
- case IA_CSS_STREAM_FORMAT_USER_DEF1:
+ case ATOMISP_INPUT_FORMAT_USER_DEF1:
return "user-def-8-type-1";
- case IA_CSS_STREAM_FORMAT_USER_DEF2:
+ case ATOMISP_INPUT_FORMAT_USER_DEF2:
return "user-def-8-type-2";
- case IA_CSS_STREAM_FORMAT_USER_DEF3:
+ case ATOMISP_INPUT_FORMAT_USER_DEF3:
return "user-def-8-type-3";
- case IA_CSS_STREAM_FORMAT_USER_DEF4:
+ case ATOMISP_INPUT_FORMAT_USER_DEF4:
return "user-def-8-type-4";
- case IA_CSS_STREAM_FORMAT_USER_DEF5:
+ case ATOMISP_INPUT_FORMAT_USER_DEF5:
return "user-def-8-type-5";
- case IA_CSS_STREAM_FORMAT_USER_DEF6:
+ case ATOMISP_INPUT_FORMAT_USER_DEF6:
return "user-def-8-type-6";
- case IA_CSS_STREAM_FORMAT_USER_DEF7:
+ case ATOMISP_INPUT_FORMAT_USER_DEF7:
return "user-def-8-type-7";
- case IA_CSS_STREAM_FORMAT_USER_DEF8:
+ case ATOMISP_INPUT_FORMAT_USER_DEF8:
return "user-def-8-type-8";
default:
@@ -2679,9 +2676,9 @@ ia_css_debug_pipe_graph_dump_frame(
}
dtrace_dot(
"node [shape = box, "
- "fixedsize=true, width=2, height=0.7]; \"0x%08lx\" "
+ "fixedsize=true, width=2, height=0.7]; \"%p\" "
"[label = \"%s\\n%d(%d) x %d, %dbpp\\n%s\"];",
- HOST_ADDRESS(frame),
+ frame,
debug_frame_format2str(frame->info.format),
frame->info.res.width,
frame->info.padded_width,
@@ -2691,16 +2688,16 @@ ia_css_debug_pipe_graph_dump_frame(
if (in_frame) {
dtrace_dot(
- "\"0x%08lx\"->\"%s(pipe%d)\" "
+ "\"%p\"->\"%s(pipe%d)\" "
"[label = %s_frame];",
- HOST_ADDRESS(frame),
+ frame,
blob_name, id, frame_name);
} else {
dtrace_dot(
- "\"%s(pipe%d)\"->\"0x%08lx\" "
+ "\"%s(pipe%d)\"->\"%p\" "
"[label = %s_frame];",
blob_name, id,
- HOST_ADDRESS(frame),
+ frame,
frame_name);
}
}
@@ -2730,7 +2727,7 @@ void ia_css_debug_pipe_graph_dump_epilogue(void)
}
- if (pg_inst.stream_format != N_IA_CSS_STREAM_FORMAT) {
+ if (pg_inst.stream_format != N_ATOMISP_INPUT_FORMAT) {
/* An input stream format has been set so assume we have
* an input system and sensor
*/
@@ -2770,7 +2767,7 @@ void ia_css_debug_pipe_graph_dump_epilogue(void)
pg_inst.height = 0;
pg_inst.eff_width = 0;
pg_inst.eff_height = 0;
- pg_inst.stream_format = N_IA_CSS_STREAM_FORMAT;
+ pg_inst.stream_format = N_ATOMISP_INPUT_FORMAT;
}
void
@@ -2858,13 +2855,7 @@ ia_css_debug_pipe_graph_dump_stage(
if (l && enable_info[l-1] == ',')
enable_info[--l] = '\0';
- if (l <= ENABLE_LINE_MAX_LENGTH) {
- /* It fits on one line, copy string and init */
- /* other helper strings with empty string */
- strcpy_s(enable_info,
- sizeof(enable_info),
- ei);
- } else {
+ if (l > ENABLE_LINE_MAX_LENGTH) {
/* Too big for one line, find last comma */
p = ENABLE_LINE_MAX_LENGTH;
while (ei[p] != ',')
@@ -3017,9 +3008,9 @@ ia_css_debug_pipe_graph_dump_sp_raw_copy(
snprintf(ring_buffer, sizeof(ring_buffer),
"node [shape = box, "
- "fixedsize=true, width=2, height=0.7]; \"0x%08lx\" "
+ "fixedsize=true, width=2, height=0.7]; \"%p\" "
"[label = \"%s\\n%d(%d) x %d\\nRingbuffer\"];",
- HOST_ADDRESS(out_frame),
+ out_frame,
debug_frame_format2str(out_frame->info.format),
out_frame->info.res.width,
out_frame->info.padded_width,
@@ -3028,9 +3019,9 @@ ia_css_debug_pipe_graph_dump_sp_raw_copy(
dtrace_dot(ring_buffer);
dtrace_dot(
- "\"%s(pipe%d)\"->\"0x%08lx\" "
+ "\"%s(pipe%d)\"->\"%p\" "
"[label = out_frame];",
- "sp_raw_copy", 1, HOST_ADDRESS(out_frame));
+ "sp_raw_copy", 1, out_frame);
snprintf(dot_id_input_bin, sizeof(dot_id_input_bin), "%s(pipe%d)", "sp_raw_copy", 1);
}
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/frame/src/frame.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/frame/src/frame.c
index 5faa89ad8a23..7562beadabef 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/frame/src/frame.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/frame/src/frame.c
@@ -196,7 +196,7 @@ enum ia_css_err ia_css_frame_map(struct ia_css_frame **frame,
attribute, context);
if (me->data == mmgr_NULL)
err = IA_CSS_ERR_INVALID_ARGUMENTS;
- };
+ }
if (err != IA_CSS_SUCCESS) {
sh_css_free(me);
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/ifmtr/src/ifmtr.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/ifmtr/src/ifmtr.c
index adefa57820a4..1bed027435fd 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/ifmtr/src/ifmtr.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/ifmtr/src/ifmtr.c
@@ -112,13 +112,13 @@ enum ia_css_err ia_css_ifmtr_configure(struct ia_css_stream_config *config,
width_b_factor = 1, start_column_b,
left_padding = 0;
input_formatter_cfg_t if_a_config, if_b_config;
- enum ia_css_stream_format input_format;
+ enum atomisp_input_format input_format;
enum ia_css_err err = IA_CSS_SUCCESS;
uint8_t if_config_index;
/* Determine which input formatter config set is targeted. */
/* Index is equal to the CSI-2 port used. */
- enum ia_css_csi2_port port;
+ enum mipi_port_id port;
if (binary) {
cropped_height = binary->in_frame_info.res.height;
@@ -141,7 +141,7 @@ enum ia_css_err ia_css_ifmtr_configure(struct ia_css_stream_config *config,
if (config->mode == IA_CSS_INPUT_MODE_SENSOR
|| config->mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR) {
port = config->source.port.port;
- if_config_index = (uint8_t) (port - IA_CSS_CSI2_PORT0);
+ if_config_index = (uint8_t) (port - MIPI_PORT0_ID);
} else if (config->mode == IA_CSS_INPUT_MODE_MEMORY) {
if_config_index = SH_CSS_IF_CONFIG_NOT_NEEDED;
} else {
@@ -189,7 +189,7 @@ enum ia_css_err ia_css_ifmtr_configure(struct ia_css_stream_config *config,
bits_per_pixel = input_formatter_get_alignment(INPUT_FORMATTER0_ID)
* 8 / ISP_VEC_NELEMS;
switch (input_format) {
- case IA_CSS_STREAM_FORMAT_YUV420_8_LEGACY:
+ case ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY:
if (two_ppc) {
vmem_increment = 1;
deinterleaving = 1;
@@ -219,9 +219,9 @@ enum ia_css_err ia_css_ifmtr_configure(struct ia_css_stream_config *config,
start_column = start_column * deinterleaving / 2;
}
break;
- case IA_CSS_STREAM_FORMAT_YUV420_8:
- case IA_CSS_STREAM_FORMAT_YUV420_10:
- case IA_CSS_STREAM_FORMAT_YUV420_16:
+ case ATOMISP_INPUT_FORMAT_YUV420_8:
+ case ATOMISP_INPUT_FORMAT_YUV420_10:
+ case ATOMISP_INPUT_FORMAT_YUV420_16:
if (two_ppc) {
vmem_increment = 1;
deinterleaving = 1;
@@ -246,9 +246,9 @@ enum ia_css_err ia_css_ifmtr_configure(struct ia_css_stream_config *config,
start_column *= deinterleaving;
}
break;
- case IA_CSS_STREAM_FORMAT_YUV422_8:
- case IA_CSS_STREAM_FORMAT_YUV422_10:
- case IA_CSS_STREAM_FORMAT_YUV422_16:
+ case ATOMISP_INPUT_FORMAT_YUV422_8:
+ case ATOMISP_INPUT_FORMAT_YUV422_10:
+ case ATOMISP_INPUT_FORMAT_YUV422_16:
if (two_ppc) {
vmem_increment = 1;
deinterleaving = 1;
@@ -267,11 +267,11 @@ enum ia_css_err ia_css_ifmtr_configure(struct ia_css_stream_config *config,
start_column *= deinterleaving;
}
break;
- case IA_CSS_STREAM_FORMAT_RGB_444:
- case IA_CSS_STREAM_FORMAT_RGB_555:
- case IA_CSS_STREAM_FORMAT_RGB_565:
- case IA_CSS_STREAM_FORMAT_RGB_666:
- case IA_CSS_STREAM_FORMAT_RGB_888:
+ case ATOMISP_INPUT_FORMAT_RGB_444:
+ case ATOMISP_INPUT_FORMAT_RGB_555:
+ case ATOMISP_INPUT_FORMAT_RGB_565:
+ case ATOMISP_INPUT_FORMAT_RGB_666:
+ case ATOMISP_INPUT_FORMAT_RGB_888:
num_vectors *= 2;
if (two_ppc) {
deinterleaving = 2; /* BR in if_a, G in if_b */
@@ -293,11 +293,11 @@ enum ia_css_err ia_css_ifmtr_configure(struct ia_css_stream_config *config,
num_vectors = num_vectors / 2 * deinterleaving;
buf_offset_b = buffer_width / 2 / ISP_VEC_NELEMS;
break;
- case IA_CSS_STREAM_FORMAT_RAW_6:
- case IA_CSS_STREAM_FORMAT_RAW_7:
- case IA_CSS_STREAM_FORMAT_RAW_8:
- case IA_CSS_STREAM_FORMAT_RAW_10:
- case IA_CSS_STREAM_FORMAT_RAW_12:
+ case ATOMISP_INPUT_FORMAT_RAW_6:
+ case ATOMISP_INPUT_FORMAT_RAW_7:
+ case ATOMISP_INPUT_FORMAT_RAW_8:
+ case ATOMISP_INPUT_FORMAT_RAW_10:
+ case ATOMISP_INPUT_FORMAT_RAW_12:
if (two_ppc) {
int crop_col = (start_column % 2) == 1;
vmem_increment = 2;
@@ -332,8 +332,8 @@ enum ia_css_err ia_css_ifmtr_configure(struct ia_css_stream_config *config,
vectors_per_line = CEIL_DIV(cropped_width, ISP_VEC_NELEMS);
vectors_per_line = CEIL_MUL(vectors_per_line, deinterleaving);
break;
- case IA_CSS_STREAM_FORMAT_RAW_14:
- case IA_CSS_STREAM_FORMAT_RAW_16:
+ case ATOMISP_INPUT_FORMAT_RAW_14:
+ case ATOMISP_INPUT_FORMAT_RAW_16:
if (two_ppc) {
num_vectors *= 2;
vmem_increment = 1;
@@ -350,26 +350,26 @@ enum ia_css_err ia_css_ifmtr_configure(struct ia_css_stream_config *config,
}
buffer_height *= 2;
break;
- case IA_CSS_STREAM_FORMAT_BINARY_8:
- case IA_CSS_STREAM_FORMAT_GENERIC_SHORT1:
- case IA_CSS_STREAM_FORMAT_GENERIC_SHORT2:
- case IA_CSS_STREAM_FORMAT_GENERIC_SHORT3:
- case IA_CSS_STREAM_FORMAT_GENERIC_SHORT4:
- case IA_CSS_STREAM_FORMAT_GENERIC_SHORT5:
- case IA_CSS_STREAM_FORMAT_GENERIC_SHORT6:
- case IA_CSS_STREAM_FORMAT_GENERIC_SHORT7:
- case IA_CSS_STREAM_FORMAT_GENERIC_SHORT8:
- case IA_CSS_STREAM_FORMAT_YUV420_8_SHIFT:
- case IA_CSS_STREAM_FORMAT_YUV420_10_SHIFT:
- case IA_CSS_STREAM_FORMAT_EMBEDDED:
- case IA_CSS_STREAM_FORMAT_USER_DEF1:
- case IA_CSS_STREAM_FORMAT_USER_DEF2:
- case IA_CSS_STREAM_FORMAT_USER_DEF3:
- case IA_CSS_STREAM_FORMAT_USER_DEF4:
- case IA_CSS_STREAM_FORMAT_USER_DEF5:
- case IA_CSS_STREAM_FORMAT_USER_DEF6:
- case IA_CSS_STREAM_FORMAT_USER_DEF7:
- case IA_CSS_STREAM_FORMAT_USER_DEF8:
+ case ATOMISP_INPUT_FORMAT_BINARY_8:
+ case ATOMISP_INPUT_FORMAT_GENERIC_SHORT1:
+ case ATOMISP_INPUT_FORMAT_GENERIC_SHORT2:
+ case ATOMISP_INPUT_FORMAT_GENERIC_SHORT3:
+ case ATOMISP_INPUT_FORMAT_GENERIC_SHORT4:
+ case ATOMISP_INPUT_FORMAT_GENERIC_SHORT5:
+ case ATOMISP_INPUT_FORMAT_GENERIC_SHORT6:
+ case ATOMISP_INPUT_FORMAT_GENERIC_SHORT7:
+ case ATOMISP_INPUT_FORMAT_GENERIC_SHORT8:
+ case ATOMISP_INPUT_FORMAT_YUV420_8_SHIFT:
+ case ATOMISP_INPUT_FORMAT_YUV420_10_SHIFT:
+ case ATOMISP_INPUT_FORMAT_EMBEDDED:
+ case ATOMISP_INPUT_FORMAT_USER_DEF1:
+ case ATOMISP_INPUT_FORMAT_USER_DEF2:
+ case ATOMISP_INPUT_FORMAT_USER_DEF3:
+ case ATOMISP_INPUT_FORMAT_USER_DEF4:
+ case ATOMISP_INPUT_FORMAT_USER_DEF5:
+ case ATOMISP_INPUT_FORMAT_USER_DEF6:
+ case ATOMISP_INPUT_FORMAT_USER_DEF7:
+ case ATOMISP_INPUT_FORMAT_USER_DEF8:
break;
}
if (width_a == 0)
@@ -420,9 +420,9 @@ enum ia_css_err ia_css_ifmtr_configure(struct ia_css_stream_config *config,
if_a_config.buf_eol_offset =
buffer_width * bits_per_pixel / 8 - line_width;
if_a_config.is_yuv420_format =
- (input_format == IA_CSS_STREAM_FORMAT_YUV420_8)
- || (input_format == IA_CSS_STREAM_FORMAT_YUV420_10)
- || (input_format == IA_CSS_STREAM_FORMAT_YUV420_16);
+ (input_format == ATOMISP_INPUT_FORMAT_YUV420_8)
+ || (input_format == ATOMISP_INPUT_FORMAT_YUV420_10)
+ || (input_format == ATOMISP_INPUT_FORMAT_YUV420_16);
if_a_config.block_no_reqs = (config->mode != IA_CSS_INPUT_MODE_SENSOR);
if (two_ppc) {
@@ -449,9 +449,9 @@ enum ia_css_err ia_css_ifmtr_configure(struct ia_css_stream_config *config,
if_b_config.buf_eol_offset =
buffer_width * bits_per_pixel / 8 - line_width;
if_b_config.is_yuv420_format =
- input_format == IA_CSS_STREAM_FORMAT_YUV420_8
- || input_format == IA_CSS_STREAM_FORMAT_YUV420_10
- || input_format == IA_CSS_STREAM_FORMAT_YUV420_16;
+ input_format == ATOMISP_INPUT_FORMAT_YUV420_8
+ || input_format == ATOMISP_INPUT_FORMAT_YUV420_10
+ || input_format == ATOMISP_INPUT_FORMAT_YUV420_16;
if_b_config.block_no_reqs =
(config->mode != IA_CSS_INPUT_MODE_SENSOR);
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/inputfifo/interface/ia_css_inputfifo.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/inputfifo/interface/ia_css_inputfifo.h
index 47d0f7e53f47..545f9e2da59e 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/inputfifo/interface/ia_css_inputfifo.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/inputfifo/interface/ia_css_inputfifo.h
@@ -42,12 +42,12 @@ void ia_css_inputfifo_send_input_frame(
unsigned int width,
unsigned int height,
unsigned int ch_id,
- enum ia_css_stream_format input_format,
+ enum atomisp_input_format input_format,
bool two_ppc);
void ia_css_inputfifo_start_frame(
unsigned int ch_id,
- enum ia_css_stream_format input_format,
+ enum atomisp_input_format input_format,
bool two_ppc);
void ia_css_inputfifo_send_line(
@@ -59,7 +59,7 @@ void ia_css_inputfifo_send_line(
void ia_css_inputfifo_send_embedded_line(
unsigned int ch_id,
- enum ia_css_stream_format data_type,
+ enum atomisp_input_format data_type,
const unsigned short *data,
unsigned int width);
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/inputfifo/src/inputfifo.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/inputfifo/src/inputfifo.c
index 8dc74927e9a2..24ca4aaf8df1 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/inputfifo/src/inputfifo.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/inputfifo/src/inputfifo.c
@@ -86,7 +86,7 @@ static unsigned int inputfifo_curr_ch_id, inputfifo_curr_fmt_type;
#endif
struct inputfifo_instance {
unsigned int ch_id;
- enum ia_css_stream_format input_format;
+ enum atomisp_input_format input_format;
bool two_ppc;
bool streaming;
unsigned int hblank_cycles;
@@ -466,21 +466,21 @@ static void inputfifo_send_frame(
static enum inputfifo_mipi_data_type inputfifo_determine_type(
- enum ia_css_stream_format input_format)
+ enum atomisp_input_format input_format)
{
enum inputfifo_mipi_data_type type;
type = inputfifo_mipi_data_type_regular;
- if (input_format == IA_CSS_STREAM_FORMAT_YUV420_8_LEGACY) {
+ if (input_format == ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY) {
type =
inputfifo_mipi_data_type_yuv420_legacy;
- } else if (input_format == IA_CSS_STREAM_FORMAT_YUV420_8 ||
- input_format == IA_CSS_STREAM_FORMAT_YUV420_10 ||
- input_format == IA_CSS_STREAM_FORMAT_YUV420_16) {
+ } else if (input_format == ATOMISP_INPUT_FORMAT_YUV420_8 ||
+ input_format == ATOMISP_INPUT_FORMAT_YUV420_10 ||
+ input_format == ATOMISP_INPUT_FORMAT_YUV420_16) {
type =
inputfifo_mipi_data_type_yuv420;
- } else if (input_format >= IA_CSS_STREAM_FORMAT_RGB_444 &&
- input_format <= IA_CSS_STREAM_FORMAT_RGB_888) {
+ } else if (input_format >= ATOMISP_INPUT_FORMAT_RGB_444 &&
+ input_format <= ATOMISP_INPUT_FORMAT_RGB_888) {
type =
inputfifo_mipi_data_type_rgb;
}
@@ -500,7 +500,7 @@ void ia_css_inputfifo_send_input_frame(
unsigned int width,
unsigned int height,
unsigned int ch_id,
- enum ia_css_stream_format input_format,
+ enum atomisp_input_format input_format,
bool two_ppc)
{
unsigned int fmt_type, hblank_cycles, marker_cycles;
@@ -524,7 +524,7 @@ void ia_css_inputfifo_send_input_frame(
void ia_css_inputfifo_start_frame(
unsigned int ch_id,
- enum ia_css_stream_format input_format,
+ enum atomisp_input_format input_format,
bool two_ppc)
{
struct inputfifo_instance *s2mi;
@@ -574,7 +574,7 @@ void ia_css_inputfifo_send_line(
void ia_css_inputfifo_send_embedded_line(
unsigned int ch_id,
- enum ia_css_stream_format data_type,
+ enum atomisp_input_format data_type,
const unsigned short *data,
unsigned int width)
{
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isp_param/interface/ia_css_isp_param_types.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isp_param/interface/ia_css_isp_param_types.h
index fa3f09347b22..9d111793bb65 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isp_param/interface/ia_css_isp_param_types.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isp_param/interface/ia_css_isp_param_types.h
@@ -94,14 +94,5 @@ union ia_css_all_memory_offsets {
} array[IA_CSS_NUM_PARAM_CLASSES];
};
-#define IA_CSS_DEFAULT_ISP_MEM_PARAMS \
- { { { { NULL, 0 } } } }
-
-#define IA_CSS_DEFAULT_ISP_CSS_PARAMS \
- { { { { 0, 0 } } } }
-
-#define IA_CSS_DEFAULT_ISP_ISP_PARAMS \
- { { { { 0, 0 } } } }
-
#endif /* _IA_CSS_ISP_PARAM_TYPES_H_ */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isys/interface/ia_css_isys.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isys/interface/ia_css_isys.h
index 4cf2defe9ef0..8c005db9766e 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isys/interface/ia_css_isys.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isys/interface/ia_css_isys.h
@@ -50,8 +50,8 @@ typedef input_system_cfg_t ia_css_isys_descr_t;
#if defined(USE_INPUT_SYSTEM_VERSION_2) || defined(USE_INPUT_SYSTEM_VERSION_2401)
input_system_error_t ia_css_isys_init(void);
void ia_css_isys_uninit(void);
-mipi_port_ID_t ia_css_isys_port_to_mipi_port(
- enum ia_css_csi2_port api_port);
+enum mipi_port_id ia_css_isys_port_to_mipi_port(
+ enum mipi_port_id api_port);
#endif
#if defined(USE_INPUT_SYSTEM_VERSION_2401)
@@ -68,7 +68,7 @@ mipi_port_ID_t ia_css_isys_port_to_mipi_port(
* there is already a stream registered with the same handle
*/
enum ia_css_err ia_css_isys_csi_rx_register_stream(
- enum ia_css_csi2_port port,
+ enum mipi_port_id port,
uint32_t isys_stream_id);
/**
@@ -83,14 +83,14 @@ enum ia_css_err ia_css_isys_csi_rx_register_stream(
* there is no stream registered with that handle
*/
enum ia_css_err ia_css_isys_csi_rx_unregister_stream(
- enum ia_css_csi2_port port,
+ enum mipi_port_id port,
uint32_t isys_stream_id);
enum ia_css_err ia_css_isys_convert_compressed_format(
struct ia_css_csi2_compression *comp,
struct input_system_cfg_s *cfg);
unsigned int ia_css_csi2_calculate_input_system_alignment(
- enum ia_css_stream_format fmt_type);
+ enum atomisp_input_format fmt_type);
#endif
#if !defined(USE_INPUT_SYSTEM_VERSION_2401)
@@ -101,12 +101,12 @@ void ia_css_isys_rx_configure(
void ia_css_isys_rx_disable(void);
-void ia_css_isys_rx_enable_all_interrupts(mipi_port_ID_t port);
+void ia_css_isys_rx_enable_all_interrupts(enum mipi_port_id port);
-unsigned int ia_css_isys_rx_get_interrupt_reg(mipi_port_ID_t port);
-void ia_css_isys_rx_get_irq_info(mipi_port_ID_t port,
+unsigned int ia_css_isys_rx_get_interrupt_reg(enum mipi_port_id port);
+void ia_css_isys_rx_get_irq_info(enum mipi_port_id port,
unsigned int *irq_infos);
-void ia_css_isys_rx_clear_irq_info(mipi_port_ID_t port,
+void ia_css_isys_rx_clear_irq_info(enum mipi_port_id port,
unsigned int irq_infos);
unsigned int ia_css_isys_rx_translate_irq_infos(unsigned int bits);
@@ -124,7 +124,7 @@ unsigned int ia_css_isys_rx_translate_irq_infos(unsigned int bits);
* format type must be sumitted correctly by the application.
*/
enum ia_css_err ia_css_isys_convert_stream_format_to_mipi_format(
- enum ia_css_stream_format input_format,
+ enum atomisp_input_format input_format,
mipi_predictor_t compression,
unsigned int *fmt_type);
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isys/src/csi_rx_rmgr.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isys/src/csi_rx_rmgr.c
index 3b04dc51335a..a914ce5532ec 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isys/src/csi_rx_rmgr.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isys/src/csi_rx_rmgr.c
@@ -141,7 +141,7 @@ void ia_css_isys_csi_rx_lut_rmgr_release(
}
enum ia_css_err ia_css_isys_csi_rx_register_stream(
- enum ia_css_csi2_port port,
+ enum mipi_port_id port,
uint32_t isys_stream_id)
{
enum ia_css_err retval = IA_CSS_ERR_INTERNAL_ERROR;
@@ -160,7 +160,7 @@ enum ia_css_err ia_css_isys_csi_rx_register_stream(
}
enum ia_css_err ia_css_isys_csi_rx_unregister_stream(
- enum ia_css_csi2_port port,
+ enum mipi_port_id port,
uint32_t isys_stream_id)
{
enum ia_css_err retval = IA_CSS_ERR_INTERNAL_ERROR;
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isys/src/isys_init.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isys/src/isys_init.c
index 4122084fd237..2ae5e59d5e31 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isys/src/isys_init.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isys/src/isys_init.c
@@ -105,8 +105,6 @@ input_system_error_t ia_css_isys_init(void)
#elif defined(USE_INPUT_SYSTEM_VERSION_2401)
input_system_error_t ia_css_isys_init(void)
{
- input_system_error_t error = INPUT_SYSTEM_ERR_NO_ERROR;
-
ia_css_isys_csi_rx_lut_rmgr_init();
ia_css_isys_ibuf_rmgr_init();
ia_css_isys_dma_channel_rmgr_init();
@@ -120,7 +118,7 @@ input_system_error_t ia_css_isys_init(void)
isys_irqc_status_enable(ISYS_IRQ1_ID);
isys_irqc_status_enable(ISYS_IRQ2_ID);
- return error;
+ return INPUT_SYSTEM_ERR_NO_ERROR;
}
#endif
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isys/src/rx.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isys/src/rx.c
index 70f6cb5e5918..425bd3cc3f34 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isys/src/rx.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isys/src/rx.c
@@ -36,7 +36,7 @@ more details.
#include "sh_css_internal.h"
#if !defined(USE_INPUT_SYSTEM_VERSION_2401)
-void ia_css_isys_rx_enable_all_interrupts(mipi_port_ID_t port)
+void ia_css_isys_rx_enable_all_interrupts(enum mipi_port_id port)
{
hrt_data bits = receiver_port_reg_load(RX0_ID,
port,
@@ -80,22 +80,22 @@ void ia_css_isys_rx_enable_all_interrupts(mipi_port_ID_t port)
* initializers in Windows. Without that there is no easy way to guarantee
* that the array values would be in the correct order.
* */
-mipi_port_ID_t ia_css_isys_port_to_mipi_port(enum ia_css_csi2_port api_port)
+enum mipi_port_id ia_css_isys_port_to_mipi_port(enum mipi_port_id api_port)
{
/* In this module the validity of the inptu variable should
* have been checked already, so we do not check for erroneous
* values. */
- mipi_port_ID_t port = MIPI_PORT0_ID;
+ enum mipi_port_id port = MIPI_PORT0_ID;
- if (api_port == IA_CSS_CSI2_PORT1)
+ if (api_port == MIPI_PORT1_ID)
port = MIPI_PORT1_ID;
- else if (api_port == IA_CSS_CSI2_PORT2)
+ else if (api_port == MIPI_PORT2_ID)
port = MIPI_PORT2_ID;
return port;
}
-unsigned int ia_css_isys_rx_get_interrupt_reg(mipi_port_ID_t port)
+unsigned int ia_css_isys_rx_get_interrupt_reg(enum mipi_port_id port)
{
return receiver_port_reg_load(RX0_ID,
port,
@@ -104,17 +104,17 @@ unsigned int ia_css_isys_rx_get_interrupt_reg(mipi_port_ID_t port)
void ia_css_rx_get_irq_info(unsigned int *irq_infos)
{
- ia_css_rx_port_get_irq_info(IA_CSS_CSI2_PORT1, irq_infos);
+ ia_css_rx_port_get_irq_info(MIPI_PORT1_ID, irq_infos);
}
-void ia_css_rx_port_get_irq_info(enum ia_css_csi2_port api_port,
+void ia_css_rx_port_get_irq_info(enum mipi_port_id api_port,
unsigned int *irq_infos)
{
- mipi_port_ID_t port = ia_css_isys_port_to_mipi_port(api_port);
+ enum mipi_port_id port = ia_css_isys_port_to_mipi_port(api_port);
ia_css_isys_rx_get_irq_info(port, irq_infos);
}
-void ia_css_isys_rx_get_irq_info(mipi_port_ID_t port,
+void ia_css_isys_rx_get_irq_info(enum mipi_port_id port,
unsigned int *irq_infos)
{
unsigned int bits;
@@ -169,16 +169,16 @@ unsigned int ia_css_isys_rx_translate_irq_infos(unsigned int bits)
void ia_css_rx_clear_irq_info(unsigned int irq_infos)
{
- ia_css_rx_port_clear_irq_info(IA_CSS_CSI2_PORT1, irq_infos);
+ ia_css_rx_port_clear_irq_info(MIPI_PORT1_ID, irq_infos);
}
-void ia_css_rx_port_clear_irq_info(enum ia_css_csi2_port api_port, unsigned int irq_infos)
+void ia_css_rx_port_clear_irq_info(enum mipi_port_id api_port, unsigned int irq_infos)
{
- mipi_port_ID_t port = ia_css_isys_port_to_mipi_port(api_port);
+ enum mipi_port_id port = ia_css_isys_port_to_mipi_port(api_port);
ia_css_isys_rx_clear_irq_info(port, irq_infos);
}
-void ia_css_isys_rx_clear_irq_info(mipi_port_ID_t port, unsigned int irq_infos)
+void ia_css_isys_rx_clear_irq_info(enum mipi_port_id port, unsigned int irq_infos)
{
hrt_data bits = receiver_port_reg_load(RX0_ID,
port,
@@ -229,7 +229,7 @@ void ia_css_isys_rx_clear_irq_info(mipi_port_ID_t port, unsigned int irq_infos)
#endif /* #if !defined(USE_INPUT_SYSTEM_VERSION_2401) */
enum ia_css_err ia_css_isys_convert_stream_format_to_mipi_format(
- enum ia_css_stream_format input_format,
+ enum atomisp_input_format input_format,
mipi_predictor_t compression,
unsigned int *fmt_type)
{
@@ -244,25 +244,25 @@ enum ia_css_err ia_css_isys_convert_stream_format_to_mipi_format(
*/
if (compression != MIPI_PREDICTOR_NONE) {
switch (input_format) {
- case IA_CSS_STREAM_FORMAT_RAW_6:
+ case ATOMISP_INPUT_FORMAT_RAW_6:
*fmt_type = 6;
break;
- case IA_CSS_STREAM_FORMAT_RAW_7:
+ case ATOMISP_INPUT_FORMAT_RAW_7:
*fmt_type = 7;
break;
- case IA_CSS_STREAM_FORMAT_RAW_8:
+ case ATOMISP_INPUT_FORMAT_RAW_8:
*fmt_type = 8;
break;
- case IA_CSS_STREAM_FORMAT_RAW_10:
+ case ATOMISP_INPUT_FORMAT_RAW_10:
*fmt_type = 10;
break;
- case IA_CSS_STREAM_FORMAT_RAW_12:
+ case ATOMISP_INPUT_FORMAT_RAW_12:
*fmt_type = 12;
break;
- case IA_CSS_STREAM_FORMAT_RAW_14:
+ case ATOMISP_INPUT_FORMAT_RAW_14:
*fmt_type = 14;
break;
- case IA_CSS_STREAM_FORMAT_RAW_16:
+ case ATOMISP_INPUT_FORMAT_RAW_16:
*fmt_type = 16;
break;
default:
@@ -277,96 +277,96 @@ enum ia_css_err ia_css_isys_convert_stream_format_to_mipi_format(
* MW: For some reason the mapping is not 1-to-1
*/
switch (input_format) {
- case IA_CSS_STREAM_FORMAT_RGB_888:
+ case ATOMISP_INPUT_FORMAT_RGB_888:
*fmt_type = MIPI_FORMAT_RGB888;
break;
- case IA_CSS_STREAM_FORMAT_RGB_555:
+ case ATOMISP_INPUT_FORMAT_RGB_555:
*fmt_type = MIPI_FORMAT_RGB555;
break;
- case IA_CSS_STREAM_FORMAT_RGB_444:
+ case ATOMISP_INPUT_FORMAT_RGB_444:
*fmt_type = MIPI_FORMAT_RGB444;
break;
- case IA_CSS_STREAM_FORMAT_RGB_565:
+ case ATOMISP_INPUT_FORMAT_RGB_565:
*fmt_type = MIPI_FORMAT_RGB565;
break;
- case IA_CSS_STREAM_FORMAT_RGB_666:
+ case ATOMISP_INPUT_FORMAT_RGB_666:
*fmt_type = MIPI_FORMAT_RGB666;
break;
- case IA_CSS_STREAM_FORMAT_RAW_8:
+ case ATOMISP_INPUT_FORMAT_RAW_8:
*fmt_type = MIPI_FORMAT_RAW8;
break;
- case IA_CSS_STREAM_FORMAT_RAW_10:
+ case ATOMISP_INPUT_FORMAT_RAW_10:
*fmt_type = MIPI_FORMAT_RAW10;
break;
- case IA_CSS_STREAM_FORMAT_RAW_6:
+ case ATOMISP_INPUT_FORMAT_RAW_6:
*fmt_type = MIPI_FORMAT_RAW6;
break;
- case IA_CSS_STREAM_FORMAT_RAW_7:
+ case ATOMISP_INPUT_FORMAT_RAW_7:
*fmt_type = MIPI_FORMAT_RAW7;
break;
- case IA_CSS_STREAM_FORMAT_RAW_12:
+ case ATOMISP_INPUT_FORMAT_RAW_12:
*fmt_type = MIPI_FORMAT_RAW12;
break;
- case IA_CSS_STREAM_FORMAT_RAW_14:
+ case ATOMISP_INPUT_FORMAT_RAW_14:
*fmt_type = MIPI_FORMAT_RAW14;
break;
- case IA_CSS_STREAM_FORMAT_YUV420_8:
+ case ATOMISP_INPUT_FORMAT_YUV420_8:
*fmt_type = MIPI_FORMAT_YUV420_8;
break;
- case IA_CSS_STREAM_FORMAT_YUV420_10:
+ case ATOMISP_INPUT_FORMAT_YUV420_10:
*fmt_type = MIPI_FORMAT_YUV420_10;
break;
- case IA_CSS_STREAM_FORMAT_YUV422_8:
+ case ATOMISP_INPUT_FORMAT_YUV422_8:
*fmt_type = MIPI_FORMAT_YUV422_8;
break;
- case IA_CSS_STREAM_FORMAT_YUV422_10:
+ case ATOMISP_INPUT_FORMAT_YUV422_10:
*fmt_type = MIPI_FORMAT_YUV422_10;
break;
- case IA_CSS_STREAM_FORMAT_YUV420_8_LEGACY:
+ case ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY:
*fmt_type = MIPI_FORMAT_YUV420_8_LEGACY;
break;
- case IA_CSS_STREAM_FORMAT_EMBEDDED:
+ case ATOMISP_INPUT_FORMAT_EMBEDDED:
*fmt_type = MIPI_FORMAT_EMBEDDED;
break;
#ifndef USE_INPUT_SYSTEM_VERSION_2401
- case IA_CSS_STREAM_FORMAT_RAW_16:
+ case ATOMISP_INPUT_FORMAT_RAW_16:
/* This is not specified by Arasan, so we use
* 17 for now.
*/
*fmt_type = MIPI_FORMAT_RAW16;
break;
- case IA_CSS_STREAM_FORMAT_BINARY_8:
+ case ATOMISP_INPUT_FORMAT_BINARY_8:
*fmt_type = MIPI_FORMAT_BINARY_8;
break;
#else
- case IA_CSS_STREAM_FORMAT_USER_DEF1:
+ case ATOMISP_INPUT_FORMAT_USER_DEF1:
*fmt_type = MIPI_FORMAT_CUSTOM0;
break;
- case IA_CSS_STREAM_FORMAT_USER_DEF2:
+ case ATOMISP_INPUT_FORMAT_USER_DEF2:
*fmt_type = MIPI_FORMAT_CUSTOM1;
break;
- case IA_CSS_STREAM_FORMAT_USER_DEF3:
+ case ATOMISP_INPUT_FORMAT_USER_DEF3:
*fmt_type = MIPI_FORMAT_CUSTOM2;
break;
- case IA_CSS_STREAM_FORMAT_USER_DEF4:
+ case ATOMISP_INPUT_FORMAT_USER_DEF4:
*fmt_type = MIPI_FORMAT_CUSTOM3;
break;
- case IA_CSS_STREAM_FORMAT_USER_DEF5:
+ case ATOMISP_INPUT_FORMAT_USER_DEF5:
*fmt_type = MIPI_FORMAT_CUSTOM4;
break;
- case IA_CSS_STREAM_FORMAT_USER_DEF6:
+ case ATOMISP_INPUT_FORMAT_USER_DEF6:
*fmt_type = MIPI_FORMAT_CUSTOM5;
break;
- case IA_CSS_STREAM_FORMAT_USER_DEF7:
+ case ATOMISP_INPUT_FORMAT_USER_DEF7:
*fmt_type = MIPI_FORMAT_CUSTOM6;
break;
- case IA_CSS_STREAM_FORMAT_USER_DEF8:
+ case ATOMISP_INPUT_FORMAT_USER_DEF8:
*fmt_type = MIPI_FORMAT_CUSTOM7;
break;
#endif
- case IA_CSS_STREAM_FORMAT_YUV420_16:
- case IA_CSS_STREAM_FORMAT_YUV422_16:
+ case ATOMISP_INPUT_FORMAT_YUV420_16:
+ case ATOMISP_INPUT_FORMAT_YUV422_16:
default:
return IA_CSS_ERR_INTERNAL_ERROR;
}
@@ -448,34 +448,34 @@ enum ia_css_err ia_css_isys_convert_compressed_format(
}
unsigned int ia_css_csi2_calculate_input_system_alignment(
- enum ia_css_stream_format fmt_type)
+ enum atomisp_input_format fmt_type)
{
unsigned int memory_alignment_in_bytes = HIVE_ISP_DDR_WORD_BYTES;
switch (fmt_type) {
- case IA_CSS_STREAM_FORMAT_RAW_6:
- case IA_CSS_STREAM_FORMAT_RAW_7:
- case IA_CSS_STREAM_FORMAT_RAW_8:
- case IA_CSS_STREAM_FORMAT_RAW_10:
- case IA_CSS_STREAM_FORMAT_RAW_12:
- case IA_CSS_STREAM_FORMAT_RAW_14:
+ case ATOMISP_INPUT_FORMAT_RAW_6:
+ case ATOMISP_INPUT_FORMAT_RAW_7:
+ case ATOMISP_INPUT_FORMAT_RAW_8:
+ case ATOMISP_INPUT_FORMAT_RAW_10:
+ case ATOMISP_INPUT_FORMAT_RAW_12:
+ case ATOMISP_INPUT_FORMAT_RAW_14:
memory_alignment_in_bytes = 2 * ISP_VEC_NELEMS;
break;
- case IA_CSS_STREAM_FORMAT_YUV420_8:
- case IA_CSS_STREAM_FORMAT_YUV422_8:
- case IA_CSS_STREAM_FORMAT_USER_DEF1:
- case IA_CSS_STREAM_FORMAT_USER_DEF2:
- case IA_CSS_STREAM_FORMAT_USER_DEF3:
- case IA_CSS_STREAM_FORMAT_USER_DEF4:
- case IA_CSS_STREAM_FORMAT_USER_DEF5:
- case IA_CSS_STREAM_FORMAT_USER_DEF6:
- case IA_CSS_STREAM_FORMAT_USER_DEF7:
- case IA_CSS_STREAM_FORMAT_USER_DEF8:
+ case ATOMISP_INPUT_FORMAT_YUV420_8:
+ case ATOMISP_INPUT_FORMAT_YUV422_8:
+ case ATOMISP_INPUT_FORMAT_USER_DEF1:
+ case ATOMISP_INPUT_FORMAT_USER_DEF2:
+ case ATOMISP_INPUT_FORMAT_USER_DEF3:
+ case ATOMISP_INPUT_FORMAT_USER_DEF4:
+ case ATOMISP_INPUT_FORMAT_USER_DEF5:
+ case ATOMISP_INPUT_FORMAT_USER_DEF6:
+ case ATOMISP_INPUT_FORMAT_USER_DEF7:
+ case ATOMISP_INPUT_FORMAT_USER_DEF8:
/* Planar YUV formats need to have all planes aligned, this means
* double the alignment for the Y plane if the horizontal decimation is 2. */
memory_alignment_in_bytes = 2 * HIVE_ISP_DDR_WORD_BYTES;
break;
- case IA_CSS_STREAM_FORMAT_EMBEDDED:
+ case ATOMISP_INPUT_FORMAT_EMBEDDED:
default:
memory_alignment_in_bytes = HIVE_ISP_DDR_WORD_BYTES;
break;
@@ -492,7 +492,7 @@ void ia_css_isys_rx_configure(const rx_cfg_t *config,
#if defined(HAS_RX_VERSION_2)
bool port_enabled[N_MIPI_PORT_ID];
bool any_port_enabled = false;
- mipi_port_ID_t port;
+ enum mipi_port_id port;
if ((config == NULL)
|| (config->mode >= N_RX_MODE)
@@ -500,7 +500,7 @@ void ia_css_isys_rx_configure(const rx_cfg_t *config,
assert(0);
return;
}
- for (port = (mipi_port_ID_t) 0; port < N_MIPI_PORT_ID; port++) {
+ for (port = (enum mipi_port_id) 0; port < N_MIPI_PORT_ID; port++) {
if (is_receiver_port_enabled(RX0_ID, port))
any_port_enabled = true;
}
@@ -595,8 +595,8 @@ void ia_css_isys_rx_configure(const rx_cfg_t *config,
void ia_css_isys_rx_disable(void)
{
- mipi_port_ID_t port;
- for (port = (mipi_port_ID_t) 0; port < N_MIPI_PORT_ID; port++) {
+ enum mipi_port_id port;
+ for (port = (enum mipi_port_id) 0; port < N_MIPI_PORT_ID; port++) {
receiver_port_reg_store(RX0_ID, port,
_HRT_CSS_RECEIVER_DEVICE_READY_REG_IDX,
false);
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isys/src/virtual_isys.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isys/src/virtual_isys.c
index 90922a7acefd..2484949453b7 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isys/src/virtual_isys.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/isys/src/virtual_isys.c
@@ -331,7 +331,7 @@ static bool create_input_system_channel(
break;
}
- if (rc == false)
+ if (!rc)
return false;
if (!acquire_sid(me->stream2mmio_id, &(me->stream2mmio_sid_id))) {
@@ -474,7 +474,7 @@ static bool calculate_input_system_channel_cfg(
rc = calculate_stream2mmio_cfg(isys_cfg, metadata,
&(channel_cfg->stream2mmio_cfg));
- if (rc == false)
+ if (!rc)
return false;
rc = calculate_ibuf_ctrl_cfg(
@@ -482,7 +482,7 @@ static bool calculate_input_system_channel_cfg(
input_port,
isys_cfg,
&(channel_cfg->ibuf_ctrl_cfg));
- if (rc == false)
+ if (!rc)
return false;
if (metadata)
channel_cfg->ibuf_ctrl_cfg.stores_per_frame = isys_cfg->metadata.lines_per_frame;
@@ -491,7 +491,7 @@ static bool calculate_input_system_channel_cfg(
channel,
isys_cfg,
&(channel_cfg->dma_cfg));
- if (rc == false)
+ if (!rc)
return false;
rc = calculate_isys2401_dma_port_cfg(
@@ -499,7 +499,7 @@ static bool calculate_input_system_channel_cfg(
false,
metadata,
&(channel_cfg->dma_src_port_cfg));
- if (rc == false)
+ if (!rc)
return false;
rc = calculate_isys2401_dma_port_cfg(
@@ -507,7 +507,7 @@ static bool calculate_input_system_channel_cfg(
isys_cfg->raw_packed,
metadata,
&(channel_cfg->dma_dest_port_cfg));
- if (rc == false)
+ if (!rc)
return false;
return true;
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/pipeline/interface/ia_css_pipeline.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/pipeline/interface/ia_css_pipeline.h
index e64936e2d46e..85ed7db0af55 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/pipeline/interface/ia_css_pipeline.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/pipeline/interface/ia_css_pipeline.h
@@ -74,21 +74,15 @@ struct ia_css_pipeline {
};
#define DEFAULT_PIPELINE \
-{ \
- IA_CSS_PIPE_ID_PREVIEW, /* pipe_id */ \
- 0, /* pipe_num */ \
- false, /* stop_requested */ \
- NULL, /* stages */ \
- NULL, /* current_stage */ \
- 0, /* num_stages */ \
- DEFAULT_FRAME, /* in_frame */ \
- {DEFAULT_FRAME}, /* out_frame */ \
- {DEFAULT_FRAME}, /* vf_frame */ \
- IA_CSS_FRAME_DELAY_1, /* frame_delay */ \
- 0, /* inout_port_config */ \
- -1, /* num_execs */ \
- true, /* acquire_isp_each_stage */\
- QOS_INVALID /* pipe_qos_config */\
+(struct ia_css_pipeline) { \
+ .pipe_id = IA_CSS_PIPE_ID_PREVIEW, \
+ .in_frame = DEFAULT_FRAME, \
+ .out_frame = {DEFAULT_FRAME}, \
+ .vf_frame = {DEFAULT_FRAME}, \
+ .dvs_frame_delay = IA_CSS_FRAME_DELAY_1, \
+ .num_execs = -1, \
+ .acquire_isp_each_stage = true, \
+ .pipe_qos_config = QOS_INVALID \
}
/* Stage descriptor used to create a new stage in the pipeline */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/pipeline/src/pipeline.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/pipeline/src/pipeline.c
index 8f93d29d1c51..4746620ca212 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/pipeline/src/pipeline.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/pipeline/src/pipeline.c
@@ -161,9 +161,9 @@ void ia_css_pipeline_start(enum ia_css_pipe_id pipe_id,
#endif
#if !defined(HAS_NO_INPUT_SYSTEM)
#ifndef ISP2401
- , (mipi_port_ID_t) 0
+ , (enum mipi_port_id) 0
#else
- (mipi_port_ID_t) 0,
+ (enum mipi_port_id) 0,
#endif
#endif
#ifndef ISP2401
@@ -574,7 +574,7 @@ static void pipeline_map_num_to_sp_thread(unsigned int pipe_num)
But the below is more descriptive.
*/
- assert(found_sp_thread != false);
+ assert(found_sp_thread);
}
static void pipeline_unmap_num_to_sp_thread(unsigned int pipe_num)
@@ -692,17 +692,16 @@ static void pipeline_init_defaults(
unsigned int pipe_num,
unsigned int dvs_frame_delay)
{
- struct ia_css_frame init_frame = DEFAULT_FRAME;
unsigned int i;
pipeline->pipe_id = pipe_id;
pipeline->stages = NULL;
pipeline->stop_requested = false;
pipeline->current_stage = NULL;
- pipeline->in_frame = init_frame;
+ pipeline->in_frame = DEFAULT_FRAME;
for (i = 0; i < IA_CSS_PIPE_MAX_OUTPUT_STAGE; i++) {
- pipeline->out_frame[i] = init_frame;
- pipeline->vf_frame[i] = init_frame;
+ pipeline->out_frame[i] = DEFAULT_FRAME;
+ pipeline->vf_frame[i] = DEFAULT_FRAME;
}
pipeline->num_execs = -1;
pipeline->acquire_isp_each_stage = true;
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/rmgr/src/rmgr_vbuf.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/rmgr/src/rmgr_vbuf.c
index 54239ac9d7c9..a4d8a48f95ba 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/rmgr/src/rmgr_vbuf.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/runtime/rmgr/src/rmgr_vbuf.c
@@ -24,12 +24,12 @@
* @brief VBUF resource handles
*/
#define NUM_HANDLES 1000
-struct ia_css_rmgr_vbuf_handle handle_table[NUM_HANDLES];
+static struct ia_css_rmgr_vbuf_handle handle_table[NUM_HANDLES];
/*
* @brief VBUF resource pool - refpool
*/
-struct ia_css_rmgr_vbuf_pool refpool = {
+static struct ia_css_rmgr_vbuf_pool refpool = {
false, /* copy_on_write */
false, /* recycle */
0, /* size */
@@ -40,7 +40,7 @@ struct ia_css_rmgr_vbuf_pool refpool = {
/*
* @brief VBUF resource pool - writepool
*/
-struct ia_css_rmgr_vbuf_pool writepool = {
+static struct ia_css_rmgr_vbuf_pool writepool = {
true, /* copy_on_write */
false, /* recycle */
0, /* size */
@@ -51,7 +51,7 @@ struct ia_css_rmgr_vbuf_pool writepool = {
/*
* @brief VBUF resource pool - hmmbufferpool
*/
-struct ia_css_rmgr_vbuf_pool hmmbufferpool = {
+static struct ia_css_rmgr_vbuf_pool hmmbufferpool = {
true, /* copy_on_write */
true, /* recycle */
32, /* size */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c
index 322bb3de6098..c771e4b910f3 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css.c
@@ -112,8 +112,6 @@ static int thread_alive;
#define STATS_ENABLED(stage) (stage && stage->binary && stage->binary->info && \
(stage->binary->info->sp.enable.s3a || stage->binary->info->sp.enable.dis))
-#define DEFAULT_PLANES { {0, 0, 0, 0} }
-
struct sh_css my_css;
int (*sh_css_printf) (const char *fmt, va_list args) = NULL;
@@ -194,7 +192,7 @@ sh_css_pipe_start(struct ia_css_stream *stream);
* @param[in] stream Point to the target "ia_css_stream" instance.
*
* @return
- * - IA_CSS_SUCCESS, if the "stop" requests have been sucessfully sent out.
+ * - IA_CSS_SUCCESS, if the "stop" requests have been successfully sent out.
* - CSS error code, otherwise.
*
*
@@ -464,46 +462,46 @@ verify_copy_out_frame_format(struct ia_css_pipe *pipe)
assert(pipe->stream != NULL);
switch (pipe->stream->config.input_config.format) {
- case IA_CSS_STREAM_FORMAT_YUV420_8_LEGACY:
- case IA_CSS_STREAM_FORMAT_YUV420_8:
+ case ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY:
+ case ATOMISP_INPUT_FORMAT_YUV420_8:
for (i=0; i<ARRAY_SIZE(yuv420_copy_formats) && !found; i++)
found = (out_fmt == yuv420_copy_formats[i]);
break;
- case IA_CSS_STREAM_FORMAT_YUV420_10:
- case IA_CSS_STREAM_FORMAT_YUV420_16:
+ case ATOMISP_INPUT_FORMAT_YUV420_10:
+ case ATOMISP_INPUT_FORMAT_YUV420_16:
found = (out_fmt == IA_CSS_FRAME_FORMAT_YUV420_16);
break;
- case IA_CSS_STREAM_FORMAT_YUV422_8:
+ case ATOMISP_INPUT_FORMAT_YUV422_8:
for (i=0; i<ARRAY_SIZE(yuv422_copy_formats) && !found; i++)
found = (out_fmt == yuv422_copy_formats[i]);
break;
- case IA_CSS_STREAM_FORMAT_YUV422_10:
- case IA_CSS_STREAM_FORMAT_YUV422_16:
+ case ATOMISP_INPUT_FORMAT_YUV422_10:
+ case ATOMISP_INPUT_FORMAT_YUV422_16:
found = (out_fmt == IA_CSS_FRAME_FORMAT_YUV422_16 ||
out_fmt == IA_CSS_FRAME_FORMAT_YUV420_16);
break;
- case IA_CSS_STREAM_FORMAT_RGB_444:
- case IA_CSS_STREAM_FORMAT_RGB_555:
- case IA_CSS_STREAM_FORMAT_RGB_565:
+ case ATOMISP_INPUT_FORMAT_RGB_444:
+ case ATOMISP_INPUT_FORMAT_RGB_555:
+ case ATOMISP_INPUT_FORMAT_RGB_565:
found = (out_fmt == IA_CSS_FRAME_FORMAT_RGBA888 ||
out_fmt == IA_CSS_FRAME_FORMAT_RGB565);
break;
- case IA_CSS_STREAM_FORMAT_RGB_666:
- case IA_CSS_STREAM_FORMAT_RGB_888:
+ case ATOMISP_INPUT_FORMAT_RGB_666:
+ case ATOMISP_INPUT_FORMAT_RGB_888:
found = (out_fmt == IA_CSS_FRAME_FORMAT_RGBA888 ||
out_fmt == IA_CSS_FRAME_FORMAT_YUV420);
break;
- case IA_CSS_STREAM_FORMAT_RAW_6:
- case IA_CSS_STREAM_FORMAT_RAW_7:
- case IA_CSS_STREAM_FORMAT_RAW_8:
- case IA_CSS_STREAM_FORMAT_RAW_10:
- case IA_CSS_STREAM_FORMAT_RAW_12:
- case IA_CSS_STREAM_FORMAT_RAW_14:
- case IA_CSS_STREAM_FORMAT_RAW_16:
+ case ATOMISP_INPUT_FORMAT_RAW_6:
+ case ATOMISP_INPUT_FORMAT_RAW_7:
+ case ATOMISP_INPUT_FORMAT_RAW_8:
+ case ATOMISP_INPUT_FORMAT_RAW_10:
+ case ATOMISP_INPUT_FORMAT_RAW_12:
+ case ATOMISP_INPUT_FORMAT_RAW_14:
+ case ATOMISP_INPUT_FORMAT_RAW_16:
found = (out_fmt == IA_CSS_FRAME_FORMAT_RAW) ||
(out_fmt == IA_CSS_FRAME_FORMAT_RAW_PACKED);
break;
- case IA_CSS_STREAM_FORMAT_BINARY_8:
+ case ATOMISP_INPUT_FORMAT_BINARY_8:
found = (out_fmt == IA_CSS_FRAME_FORMAT_BINARY_8);
break;
default:
@@ -588,13 +586,13 @@ sh_css_config_input_network(struct ia_css_stream *stream)
}
#elif !defined(HAS_NO_INPUT_SYSTEM) && defined(USE_INPUT_SYSTEM_VERSION_2401)
static unsigned int csi2_protocol_calculate_max_subpixels_per_line(
- enum ia_css_stream_format format,
+ enum atomisp_input_format format,
unsigned int pixels_per_line)
{
unsigned int rval;
switch (format) {
- case IA_CSS_STREAM_FORMAT_YUV420_8_LEGACY:
+ case ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY:
/*
* The frame format layout is shown below.
*
@@ -613,9 +611,9 @@ static unsigned int csi2_protocol_calculate_max_subpixels_per_line(
*/
rval = pixels_per_line * 2;
break;
- case IA_CSS_STREAM_FORMAT_YUV420_8:
- case IA_CSS_STREAM_FORMAT_YUV420_10:
- case IA_CSS_STREAM_FORMAT_YUV420_16:
+ case ATOMISP_INPUT_FORMAT_YUV420_8:
+ case ATOMISP_INPUT_FORMAT_YUV420_10:
+ case ATOMISP_INPUT_FORMAT_YUV420_16:
/*
* The frame format layout is shown below.
*
@@ -632,9 +630,9 @@ static unsigned int csi2_protocol_calculate_max_subpixels_per_line(
*/
rval = pixels_per_line * 2;
break;
- case IA_CSS_STREAM_FORMAT_YUV422_8:
- case IA_CSS_STREAM_FORMAT_YUV422_10:
- case IA_CSS_STREAM_FORMAT_YUV422_16:
+ case ATOMISP_INPUT_FORMAT_YUV422_8:
+ case ATOMISP_INPUT_FORMAT_YUV422_10:
+ case ATOMISP_INPUT_FORMAT_YUV422_16:
/*
* The frame format layout is shown below.
*
@@ -651,11 +649,11 @@ static unsigned int csi2_protocol_calculate_max_subpixels_per_line(
*/
rval = pixels_per_line * 2;
break;
- case IA_CSS_STREAM_FORMAT_RGB_444:
- case IA_CSS_STREAM_FORMAT_RGB_555:
- case IA_CSS_STREAM_FORMAT_RGB_565:
- case IA_CSS_STREAM_FORMAT_RGB_666:
- case IA_CSS_STREAM_FORMAT_RGB_888:
+ case ATOMISP_INPUT_FORMAT_RGB_444:
+ case ATOMISP_INPUT_FORMAT_RGB_555:
+ case ATOMISP_INPUT_FORMAT_RGB_565:
+ case ATOMISP_INPUT_FORMAT_RGB_666:
+ case ATOMISP_INPUT_FORMAT_RGB_888:
/*
* The frame format layout is shown below.
*
@@ -672,22 +670,22 @@ static unsigned int csi2_protocol_calculate_max_subpixels_per_line(
*/
rval = pixels_per_line * 4;
break;
- case IA_CSS_STREAM_FORMAT_RAW_6:
- case IA_CSS_STREAM_FORMAT_RAW_7:
- case IA_CSS_STREAM_FORMAT_RAW_8:
- case IA_CSS_STREAM_FORMAT_RAW_10:
- case IA_CSS_STREAM_FORMAT_RAW_12:
- case IA_CSS_STREAM_FORMAT_RAW_14:
- case IA_CSS_STREAM_FORMAT_RAW_16:
- case IA_CSS_STREAM_FORMAT_BINARY_8:
- case IA_CSS_STREAM_FORMAT_USER_DEF1:
- case IA_CSS_STREAM_FORMAT_USER_DEF2:
- case IA_CSS_STREAM_FORMAT_USER_DEF3:
- case IA_CSS_STREAM_FORMAT_USER_DEF4:
- case IA_CSS_STREAM_FORMAT_USER_DEF5:
- case IA_CSS_STREAM_FORMAT_USER_DEF6:
- case IA_CSS_STREAM_FORMAT_USER_DEF7:
- case IA_CSS_STREAM_FORMAT_USER_DEF8:
+ case ATOMISP_INPUT_FORMAT_RAW_6:
+ case ATOMISP_INPUT_FORMAT_RAW_7:
+ case ATOMISP_INPUT_FORMAT_RAW_8:
+ case ATOMISP_INPUT_FORMAT_RAW_10:
+ case ATOMISP_INPUT_FORMAT_RAW_12:
+ case ATOMISP_INPUT_FORMAT_RAW_14:
+ case ATOMISP_INPUT_FORMAT_RAW_16:
+ case ATOMISP_INPUT_FORMAT_BINARY_8:
+ case ATOMISP_INPUT_FORMAT_USER_DEF1:
+ case ATOMISP_INPUT_FORMAT_USER_DEF2:
+ case ATOMISP_INPUT_FORMAT_USER_DEF3:
+ case ATOMISP_INPUT_FORMAT_USER_DEF4:
+ case ATOMISP_INPUT_FORMAT_USER_DEF5:
+ case ATOMISP_INPUT_FORMAT_USER_DEF6:
+ case ATOMISP_INPUT_FORMAT_USER_DEF7:
+ case ATOMISP_INPUT_FORMAT_USER_DEF8:
/*
* The frame format layout is shown below.
*
@@ -744,11 +742,11 @@ static bool sh_css_translate_stream_cfg_to_input_system_input_port_id(
break;
case IA_CSS_INPUT_MODE_BUFFERED_SENSOR:
- if (stream_cfg->source.port.port == IA_CSS_CSI2_PORT0) {
+ if (stream_cfg->source.port.port == MIPI_PORT0_ID) {
isys_stream_descr->input_port_id = INPUT_SYSTEM_CSI_PORT0_ID;
- } else if (stream_cfg->source.port.port == IA_CSS_CSI2_PORT1) {
+ } else if (stream_cfg->source.port.port == MIPI_PORT1_ID) {
isys_stream_descr->input_port_id = INPUT_SYSTEM_CSI_PORT1_ID;
- } else if (stream_cfg->source.port.port == IA_CSS_CSI2_PORT2) {
+ } else if (stream_cfg->source.port.port == MIPI_PORT2_ID) {
isys_stream_descr->input_port_id = INPUT_SYSTEM_CSI_PORT2_ID;
}
@@ -929,7 +927,7 @@ static bool sh_css_translate_stream_cfg_to_input_system_input_port_resolution(
unsigned int max_subpixels_per_line;
unsigned int lines_per_frame;
unsigned int align_req_in_bytes;
- enum ia_css_stream_format fmt_type;
+ enum atomisp_input_format fmt_type;
fmt_type = stream_cfg->isys_config[isys_stream_idx].format;
if ((stream_cfg->mode == IA_CSS_INPUT_MODE_SENSOR ||
@@ -938,11 +936,11 @@ static bool sh_css_translate_stream_cfg_to_input_system_input_port_resolution(
if (stream_cfg->source.port.compression.uncompressed_bits_per_pixel ==
UNCOMPRESSED_BITS_PER_PIXEL_10) {
- fmt_type = IA_CSS_STREAM_FORMAT_RAW_10;
+ fmt_type = ATOMISP_INPUT_FORMAT_RAW_10;
}
else if (stream_cfg->source.port.compression.uncompressed_bits_per_pixel ==
UNCOMPRESSED_BITS_PER_PIXEL_12) {
- fmt_type = IA_CSS_STREAM_FORMAT_RAW_12;
+ fmt_type = ATOMISP_INPUT_FORMAT_RAW_12;
}
else
return false;
@@ -1054,7 +1052,7 @@ sh_css_config_input_network(struct ia_css_stream *stream)
if (stream->last_pipe->config.mode == IA_CSS_PIPE_MODE_CAPTURE) {
/*
* We need to poll the ISYS HW in capture_indication itself
- * for "non-continous" capture usecase for getting accurate
+ * for "non-continuous" capture usecase for getting accurate
* isys frame capture timestamps.
* This is because the capturepipe propcessing takes longer
* to execute than the input system frame capture.
@@ -1084,7 +1082,7 @@ sh_css_config_input_network(struct ia_css_stream *stream)
/* get the SP thread id */
rc = ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(pipe), &sp_thread_id);
- if (rc != true)
+ if (!rc)
return IA_CSS_ERR_INTERNAL_ERROR;
/* get the target input terminal */
sp_pipeline_input_terminal = &(sh_css_sp_group.pipe_io[sp_thread_id].input);
@@ -1110,7 +1108,7 @@ sh_css_config_input_network(struct ia_css_stream *stream)
&(isys_stream_descr));
}
- if (rc != true)
+ if (!rc)
return IA_CSS_ERR_INTERNAL_ERROR;
isys_stream_id = ia_css_isys_generate_stream_id(sp_thread_id, i);
@@ -1120,7 +1118,7 @@ sh_css_config_input_network(struct ia_css_stream *stream)
&(isys_stream_descr),
&(sp_pipeline_input_terminal->context.virtual_input_system_stream[i]),
isys_stream_id);
- if (rc != true)
+ if (!rc)
return IA_CSS_ERR_INTERNAL_ERROR;
/* calculate the configuration of the virtual Input System (2401) */
@@ -1128,7 +1126,7 @@ sh_css_config_input_network(struct ia_css_stream *stream)
&(sp_pipeline_input_terminal->context.virtual_input_system_stream[i]),
&(isys_stream_descr),
&(sp_pipeline_input_terminal->ctrl.virtual_input_system_stream_cfg[i]));
- if (rc != true) {
+ if (!rc) {
ia_css_isys_stream_destroy(&(sp_pipeline_input_terminal->context.virtual_input_system_stream[i]));
return IA_CSS_ERR_INTERNAL_ERROR;
}
@@ -1197,7 +1195,7 @@ static inline struct ia_css_pipe *stream_get_target_pipe(
static enum ia_css_err stream_csi_rx_helper(
struct ia_css_stream *stream,
- enum ia_css_err (*func)(enum ia_css_csi2_port, uint32_t))
+ enum ia_css_err (*func)(enum mipi_port_id, uint32_t))
{
enum ia_css_err retval = IA_CSS_ERR_INTERNAL_ERROR;
uint32_t sp_thread_id, stream_id;
@@ -1393,7 +1391,7 @@ start_copy_on_sp(struct ia_css_pipe *pipe,
ia_css_isys_rx_disable();
#endif
- if (pipe->stream->config.input_config.format != IA_CSS_STREAM_FORMAT_BINARY_8)
+ if (pipe->stream->config.input_config.format != ATOMISP_INPUT_FORMAT_BINARY_8)
return IA_CSS_ERR_INTERNAL_ERROR;
sh_css_sp_start_binary_copy(ia_css_pipe_get_pipe_num(pipe), out_frame, pipe->stream->config.pixels_per_clock == 2);
@@ -1456,7 +1454,7 @@ static void start_pipe(
&me->stream->info.metadata_info
#if !defined(HAS_NO_INPUT_SYSTEM)
,(input_mode==IA_CSS_INPUT_MODE_MEMORY) ?
- (mipi_port_ID_t)0 :
+ (enum mipi_port_id)0 :
me->stream->config.source.port.port
#endif
#ifdef ISP2401
@@ -1499,7 +1497,7 @@ static void
enable_interrupts(enum ia_css_irq_type irq_type)
{
#ifdef USE_INPUT_SYSTEM_VERSION_2
- mipi_port_ID_t port;
+ enum mipi_port_id port;
#endif
bool enable_pulse = irq_type != IA_CSS_IRQ_TYPE_EDGE;
IA_CSS_ENTER_PRIVATE("");
@@ -2291,25 +2289,19 @@ init_pipe_defaults(enum ia_css_pipe_mode mode,
struct ia_css_pipe *pipe,
bool copy_pipe)
{
- static struct ia_css_pipe default_pipe = IA_CSS_DEFAULT_PIPE;
- static struct ia_css_preview_settings prev = IA_CSS_DEFAULT_PREVIEW_SETTINGS;
- static struct ia_css_capture_settings capt = IA_CSS_DEFAULT_CAPTURE_SETTINGS;
- static struct ia_css_video_settings video = IA_CSS_DEFAULT_VIDEO_SETTINGS;
- static struct ia_css_yuvpp_settings yuvpp = IA_CSS_DEFAULT_YUVPP_SETTINGS;
-
if (pipe == NULL) {
IA_CSS_ERROR("NULL pipe parameter");
return IA_CSS_ERR_INVALID_ARGUMENTS;
}
/* Initialize pipe to pre-defined defaults */
- *pipe = default_pipe;
+ *pipe = IA_CSS_DEFAULT_PIPE;
/* TODO: JB should not be needed, but temporary backward reference */
switch (mode) {
case IA_CSS_PIPE_MODE_PREVIEW:
pipe->mode = IA_CSS_PIPE_ID_PREVIEW;
- pipe->pipe_settings.preview = prev;
+ pipe->pipe_settings.preview = IA_CSS_DEFAULT_PREVIEW_SETTINGS;
break;
case IA_CSS_PIPE_MODE_CAPTURE:
if (copy_pipe) {
@@ -2317,11 +2309,11 @@ init_pipe_defaults(enum ia_css_pipe_mode mode,
} else {
pipe->mode = IA_CSS_PIPE_ID_CAPTURE;
}
- pipe->pipe_settings.capture = capt;
+ pipe->pipe_settings.capture = IA_CSS_DEFAULT_CAPTURE_SETTINGS;
break;
case IA_CSS_PIPE_MODE_VIDEO:
pipe->mode = IA_CSS_PIPE_ID_VIDEO;
- pipe->pipe_settings.video = video;
+ pipe->pipe_settings.video = IA_CSS_DEFAULT_VIDEO_SETTINGS;
break;
case IA_CSS_PIPE_MODE_ACC:
pipe->mode = IA_CSS_PIPE_ID_ACC;
@@ -2331,7 +2323,7 @@ init_pipe_defaults(enum ia_css_pipe_mode mode,
break;
case IA_CSS_PIPE_MODE_YUVPP:
pipe->mode = IA_CSS_PIPE_ID_YUVPP;
- pipe->pipe_settings.yuvpp = yuvpp;
+ pipe->pipe_settings.yuvpp = IA_CSS_DEFAULT_YUVPP_SETTINGS;
break;
default:
return IA_CSS_ERR_INVALID_ARGUMENTS;
@@ -2570,7 +2562,7 @@ ia_css_uninit(void)
ifmtr_set_if_blocking_mode_reset = true;
#endif
- if (fw_explicitly_loaded == false) {
+ if (!fw_explicitly_loaded) {
ia_css_unload_firmware();
}
ia_css_spctrl_unload_fw(SP0_ID);
@@ -3657,7 +3649,7 @@ static enum ia_css_err create_host_video_pipeline(struct ia_css_pipe *pipe)
in_frame = me->stages->args.out_frame[0];
} else if (pipe->stream->config.continuous) {
#ifdef USE_INPUT_SYSTEM_VERSION_2401
- /* When continous is enabled, configure in_frame with the
+ /* When continuous is enabled, configure in_frame with the
* last pipe, which is the copy pipe.
*/
in_frame = pipe->stream->last_pipe->continuous_frames[0];
@@ -3854,7 +3846,7 @@ create_host_preview_pipeline(struct ia_css_pipe *pipe)
* - Direct Sensor Mode Online Preview
* - Buffered Sensor Mode Online Preview
* - Direct Sensor Mode Continuous Preview
- * - Buffered Sensor Mode Continous Preview
+ * - Buffered Sensor Mode Continuous Preview
*/
sensor = (pipe->stream->config.mode == IA_CSS_INPUT_MODE_SENSOR);
buffered_sensor = (pipe->stream->config.mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR);
@@ -4082,9 +4074,9 @@ preview_start(struct ia_css_pipe *pipe)
#endif
#if !defined(HAS_NO_INPUT_SYSTEM)
#ifndef ISP2401
- , (mipi_port_ID_t)0
+ , (enum mipi_port_id)0
#else
- (mipi_port_ID_t)0,
+ (enum mipi_port_id)0,
#endif
#endif
#ifndef ISP2401
@@ -4114,9 +4106,9 @@ preview_start(struct ia_css_pipe *pipe)
#endif
#if !defined(HAS_NO_INPUT_SYSTEM)
#ifndef ISP2401
- , (mipi_port_ID_t) 0
+ , (enum mipi_port_id) 0
#else
- (mipi_port_ID_t) 0,
+ (enum mipi_port_id) 0,
#endif
#endif
#ifndef ISP2401
@@ -4681,7 +4673,7 @@ ia_css_dequeue_psys_event(struct ia_css_event *event)
event->type = convert_event_sp_to_host_domain[payload[0]];
/* Some sane default values since not all events use all fields. */
event->pipe = NULL;
- event->port = IA_CSS_CSI2_PORT0;
+ event->port = MIPI_PORT0_ID;
event->exp_id = 0;
event->fw_warning = IA_CSS_FW_WARNING_NONE;
event->fw_handle = 0;
@@ -4715,7 +4707,7 @@ ia_css_dequeue_psys_event(struct ia_css_event *event)
event->timer_subcode = payload[2];
}
/* It's a non timer event. So clear first half of the timer event data.
- * If the second part of the TIMER event is not recieved, we discard
+ * If the second part of the TIMER event is not received, we discard
* the first half of the timer data and process the non timer event without
* affecting the flow. So the non timer event falls through
* the code. */
@@ -4727,7 +4719,7 @@ ia_css_dequeue_psys_event(struct ia_css_event *event)
}
}
if (event->type == IA_CSS_EVENT_TYPE_PORT_EOF) {
- event->port = (enum ia_css_csi2_port)payload[1];
+ event->port = (enum mipi_port_id)payload[1];
event->exp_id = payload[3];
} else if (event->type == IA_CSS_EVENT_TYPE_FW_WARNING) {
event->fw_warning = (enum ia_css_fw_warning)payload[1];
@@ -5588,8 +5580,7 @@ static enum ia_css_err load_video_binaries(struct ia_css_pipe *pipe)
/* we build up the pipeline starting at the end */
/* YUV post-processing if needed */
if (need_scaler) {
- struct ia_css_cas_binary_descr cas_scaler_descr
- = IA_CSS_DEFAULT_CAS_BINARY_DESCR;
+ struct ia_css_cas_binary_descr cas_scaler_descr = { };
/* NV12 is the common format that is supported by both */
/* yuv_scaler and the video_xx_isp2_min binaries. */
@@ -5958,9 +5949,9 @@ static enum ia_css_err video_start(struct ia_css_pipe *pipe)
#endif
#if !defined(HAS_NO_INPUT_SYSTEM)
#ifndef ISP2401
- , (mipi_port_ID_t)0
+ , (enum mipi_port_id)0
#else
- (mipi_port_ID_t)0,
+ (enum mipi_port_id)0,
#endif
#endif
#ifndef ISP2401
@@ -6244,8 +6235,8 @@ static enum ia_css_err load_primary_binaries(
pipe_out_info->res);
if (need_extra_yuv_scaler) {
- struct ia_css_cas_binary_descr cas_scaler_descr
- = IA_CSS_DEFAULT_CAS_BINARY_DESCR;
+ struct ia_css_cas_binary_descr cas_scaler_descr = { };
+
err = ia_css_pipe_create_cas_scaler_desc_single_output(
&capt_pp_out_info,
pipe_out_info,
@@ -6793,7 +6784,7 @@ static bool copy_on_sp(struct ia_css_pipe *pipe)
rval &= (pipe->config.default_capture_config.mode == IA_CSS_CAPTURE_MODE_RAW);
- rval &= ((pipe->stream->config.input_config.format == IA_CSS_STREAM_FORMAT_BINARY_8) ||
+ rval &= ((pipe->stream->config.input_config.format == ATOMISP_INPUT_FORMAT_BINARY_8) ||
(pipe->config.mode == IA_CSS_PIPE_MODE_COPY));
return rval;
@@ -6826,7 +6817,7 @@ static enum ia_css_err load_capture_binaries(
return err;
}
if (copy_on_sp(pipe) &&
- pipe->stream->config.input_config.format == IA_CSS_STREAM_FORMAT_BINARY_8) {
+ pipe->stream->config.input_config.format == ATOMISP_INPUT_FORMAT_BINARY_8) {
ia_css_frame_info_init(
&pipe->output_info[0],
JPEG_BYTES,
@@ -6924,7 +6915,7 @@ need_yuv_scaler_stage(const struct ia_css_pipe *pipe)
/* TODO: make generic function */
need_format_conversion =
- ((pipe->stream->config.input_config.format == IA_CSS_STREAM_FORMAT_YUV420_8_LEGACY) &&
+ ((pipe->stream->config.input_config.format == ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY) &&
(pipe->output_info[0].format != IA_CSS_FRAME_FORMAT_CSI_MIPI_LEGACY_YUV420_8));
in_res = pipe->config.input_effective_res;
@@ -6958,7 +6949,7 @@ static enum ia_css_err ia_css_pipe_create_cas_scaler_desc_single_output(
unsigned int i;
unsigned int hor_ds_factor = 0, ver_ds_factor = 0;
enum ia_css_err err = IA_CSS_SUCCESS;
- struct ia_css_frame_info tmp_in_info = IA_CSS_BINARY_DEFAULT_FRAME_INFO;
+ struct ia_css_frame_info tmp_in_info;
unsigned max_scale_factor_per_stage = MAX_PREFERRED_YUV_DS_PER_STEP;
@@ -7232,7 +7223,7 @@ load_yuvpp_binaries(struct ia_css_pipe *pipe)
struct ia_css_frame_info *vf_pp_in_info[IA_CSS_PIPE_MAX_OUTPUT_STAGE];
struct ia_css_yuvpp_settings *mycs;
struct ia_css_binary *next_binary;
- struct ia_css_cas_binary_descr cas_scaler_descr = IA_CSS_DEFAULT_CAS_BINARY_DESCR;
+ struct ia_css_cas_binary_descr cas_scaler_descr = { };
unsigned int i, j;
bool need_isp_copy_binary = false;
@@ -7313,7 +7304,7 @@ load_yuvpp_binaries(struct ia_css_pipe *pipe)
/*
* NOTES
* - Why does the "yuvpp" pipe needs "isp_copy_binary" (i.e. ISP Copy) when
- * its input is "IA_CSS_STREAM_FORMAT_YUV422_8"?
+ * its input is "ATOMISP_INPUT_FORMAT_YUV422_8"?
*
* In most use cases, the first stage in the "yuvpp" pipe is the "yuv_scale_
* binary". However, the "yuv_scale_binary" does NOT support the input-frame
@@ -7328,7 +7319,7 @@ load_yuvpp_binaries(struct ia_css_pipe *pipe)
* "yuv_scale_binary".
*/
need_isp_copy_binary =
- (pipe->stream->config.input_config.format == IA_CSS_STREAM_FORMAT_YUV422_8);
+ (pipe->stream->config.input_config.format == ATOMISP_INPUT_FORMAT_YUV422_8);
#else /* !USE_INPUT_SYSTEM_VERSION_2401 */
need_isp_copy_binary = true;
#endif /* USE_INPUT_SYSTEM_VERSION_2401 */
@@ -7610,7 +7601,7 @@ create_host_yuvpp_pipeline(struct ia_css_pipe *pipe)
* except for the following:
* - Direct Sensor Mode Online Capture
* - Direct Sensor Mode Continuous Capture
- * - Buffered Sensor Mode Continous Capture
+ * - Buffered Sensor Mode Continuous Capture
*/
sensor = pipe->stream->config.mode == IA_CSS_INPUT_MODE_SENSOR;
buffered_sensor = pipe->stream->config.mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR;
@@ -7636,11 +7627,11 @@ create_host_yuvpp_pipeline(struct ia_css_pipe *pipe)
* Bayer-Quad RAW.
*/
int in_frame_format;
- if (pipe->stream->config.input_config.format == IA_CSS_STREAM_FORMAT_YUV420_8_LEGACY) {
+ if (pipe->stream->config.input_config.format == ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY) {
in_frame_format = IA_CSS_FRAME_FORMAT_CSI_MIPI_LEGACY_YUV420_8;
- } else if (pipe->stream->config.input_config.format == IA_CSS_STREAM_FORMAT_YUV422_8) {
+ } else if (pipe->stream->config.input_config.format == ATOMISP_INPUT_FORMAT_YUV422_8) {
/*
- * When the sensor output frame format is "IA_CSS_STREAM_FORMAT_YUV422_8",
+ * When the sensor output frame format is "ATOMISP_INPUT_FORMAT_YUV422_8",
* the "isp_copy_var" binary is selected as the first stage in the yuvpp
* pipe.
*
@@ -7748,7 +7739,7 @@ create_host_yuvpp_pipeline(struct ia_css_pipe *pipe)
for (i = 0, j = 0; i < num_stage; i++) {
assert(j < num_output_stage);
- if (pipe->pipe_settings.yuvpp.is_output_stage[i] == true) {
+ if (pipe->pipe_settings.yuvpp.is_output_stage[i]) {
tmp_out_frame = out_frame[j];
tmp_vf_frame = vf_frame[j];
} else {
@@ -7767,7 +7758,7 @@ create_host_yuvpp_pipeline(struct ia_css_pipe *pipe)
}
/* we use output port 1 as internal output port */
tmp_in_frame = yuv_scaler_stage->args.out_frame[1];
- if (pipe->pipe_settings.yuvpp.is_output_stage[i] == true) {
+ if (pipe->pipe_settings.yuvpp.is_output_stage[i]) {
if (tmp_vf_frame && (tmp_vf_frame->info.res.width != 0)) {
in_frame = yuv_scaler_stage->args.out_vf_frame;
err = add_vf_pp_stage(pipe, in_frame, tmp_vf_frame, &vf_pp_binary[j],
@@ -7821,7 +7812,7 @@ create_host_copy_pipeline(struct ia_css_pipe *pipe,
out_frame->flash_state = IA_CSS_FRAME_FLASH_STATE_NONE;
if (copy_on_sp(pipe) &&
- pipe->stream->config.input_config.format == IA_CSS_STREAM_FORMAT_BINARY_8) {
+ pipe->stream->config.input_config.format == ATOMISP_INPUT_FORMAT_BINARY_8) {
ia_css_frame_info_init(
&out_frame->info,
JPEG_BYTES,
@@ -7950,7 +7941,7 @@ create_host_regular_capture_pipeline(struct ia_css_pipe *pipe)
* - Direct Sensor Mode Online Capture
* - Direct Sensor Mode Online Capture
* - Direct Sensor Mode Continuous Capture
- * - Buffered Sensor Mode Continous Capture
+ * - Buffered Sensor Mode Continuous Capture
*/
sensor = (pipe->stream->config.mode == IA_CSS_INPUT_MODE_SENSOR);
buffered_sensor = (pipe->stream->config.mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR);
@@ -8053,7 +8044,6 @@ create_host_regular_capture_pipeline(struct ia_css_pipe *pipe)
}
if (mode == IA_CSS_CAPTURE_MODE_PRIMARY) {
- unsigned int frm;
struct ia_css_frame *local_in_frame = NULL;
struct ia_css_frame *local_out_frame = NULL;
@@ -8091,7 +8081,6 @@ create_host_regular_capture_pipeline(struct ia_css_pipe *pipe)
return err;
}
}
- (void)frm;
/* If we use copy iso primary,
the input must be yuv iso raw */
current_stage->args.copy_vf =
@@ -8330,8 +8319,6 @@ sh_css_pipe_get_output_frame_info(struct ia_css_pipe *pipe,
struct ia_css_frame_info *info,
unsigned int idx)
{
- enum ia_css_err err = IA_CSS_SUCCESS;
-
assert(pipe != NULL);
assert(info != NULL);
@@ -8340,7 +8327,7 @@ sh_css_pipe_get_output_frame_info(struct ia_css_pipe *pipe,
*info = pipe->output_info[idx];
if (copy_on_sp(pipe) &&
- pipe->stream->config.input_config.format == IA_CSS_STREAM_FORMAT_BINARY_8) {
+ pipe->stream->config.input_config.format == ATOMISP_INPUT_FORMAT_BINARY_8) {
ia_css_frame_info_init(
info,
JPEG_BYTES,
@@ -8356,7 +8343,7 @@ sh_css_pipe_get_output_frame_info(struct ia_css_pipe *pipe,
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
"sh_css_pipe_get_output_frame_info() leave:\n");
- return err;
+ return IA_CSS_SUCCESS;
}
#if !defined(HAS_NO_INPUT_SYSTEM)
@@ -8401,7 +8388,7 @@ ia_css_stream_send_input_line(const struct ia_css_stream *stream,
void
ia_css_stream_send_input_embedded_line(const struct ia_css_stream *stream,
- enum ia_css_stream_format format,
+ enum atomisp_input_format format,
const unsigned short *data,
unsigned int width)
{
@@ -8827,10 +8814,8 @@ sh_css_init_host_sp_control_vars(void)
*/
void ia_css_pipe_config_defaults(struct ia_css_pipe_config *pipe_config)
{
- struct ia_css_pipe_config def_config = DEFAULT_PIPE_CONFIG;
-
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "ia_css_pipe_config_defaults()\n");
- *pipe_config = def_config;
+ *pipe_config = DEFAULT_PIPE_CONFIG;
}
void
@@ -8915,7 +8900,7 @@ ia_css_pipe_create(const struct ia_css_pipe_config *config,
err = ia_css_pipe_create_extra(config, NULL, pipe);
if(err == IA_CSS_SUCCESS) {
- IA_CSS_LOG("pipe created successfuly = %p", *pipe);
+ IA_CSS_LOG("pipe created successfully = %p", *pipe);
}
IA_CSS_LEAVE_ERR_PRIVATE(err);
@@ -9187,7 +9172,7 @@ ia_css_stream_configure_rx(struct ia_css_stream *stream)
else if (config->num_lanes != 0)
return IA_CSS_ERR_INVALID_ARGUMENTS;
- if (config->port > IA_CSS_CSI2_PORT2)
+ if (config->port > MIPI_PORT2_ID)
return IA_CSS_ERR_INVALID_ARGUMENTS;
stream->csi_rx_config.port =
ia_css_isys_port_to_mipi_port(config->port);
@@ -9374,7 +9359,7 @@ ia_css_stream_create(const struct ia_css_stream_config *stream_config,
#if defined(USE_INPUT_SYSTEM_VERSION_2)
/* We don't support metadata for JPEG stream, since they both use str2mem */
- if (stream_config->input_config.format == IA_CSS_STREAM_FORMAT_BINARY_8 &&
+ if (stream_config->input_config.format == ATOMISP_INPUT_FORMAT_BINARY_8 &&
stream_config->metadata_config.resolution.height > 0) {
err = IA_CSS_ERR_INVALID_ARGUMENTS;
IA_CSS_LEAVE_ERR(err);
@@ -10153,7 +10138,7 @@ ia_css_temp_pipe_to_pipe_id(const struct ia_css_pipe *pipe, enum ia_css_pipe_id
return IA_CSS_SUCCESS;
}
-enum ia_css_stream_format
+enum atomisp_input_format
ia_css_stream_get_format(const struct ia_css_stream *stream)
{
return stream->config.input_config.format;
@@ -10229,8 +10214,6 @@ ia_css_stream_get_3a_binary(const struct ia_css_stream *stream)
enum ia_css_err
ia_css_stream_set_output_padded_width(struct ia_css_stream *stream, unsigned int output_padded_width)
{
- enum ia_css_err err = IA_CSS_SUCCESS;
-
struct ia_css_pipe *pipe;
assert(stream != NULL);
@@ -10243,7 +10226,7 @@ ia_css_stream_set_output_padded_width(struct ia_css_stream *stream, unsigned int
pipe->config.output_info[IA_CSS_PIPE_OUTPUT_STAGE_0].padded_width = output_padded_width;
pipe->output_info[IA_CSS_PIPE_OUTPUT_STAGE_0].padded_width = output_padded_width;
- return err;
+ return IA_CSS_SUCCESS;
}
static struct ia_css_binary *
@@ -10745,7 +10728,7 @@ ia_css_pipe_set_qos_ext_state(struct ia_css_pipe *pipe, uint32_t fw_handle, bool
(uint8_t) IA_CSS_PSYS_SW_EVENT_STAGE_ENABLE_DISABLE,
(uint8_t) thread_id,
(uint8_t) stage->stage_num,
- (enable == true) ? 1 : 0);
+ enable ? 1 : 0);
if (err == IA_CSS_SUCCESS) {
if(enable)
SH_CSS_QOS_STAGE_ENABLE(&(sh_css_sp_group.pipe[thread_id]),stage->stage_num);
@@ -11070,7 +11053,7 @@ static struct sh_css_hmm_buffer_record
buffer_record = &hmm_buffer_record[0];
for (i = 0; i < MAX_HMM_BUFFER_NUM; i++) {
- if (buffer_record->in_use == false) {
+ if (!buffer_record->in_use) {
buffer_record->in_use = true;
buffer_record->type = type;
buffer_record->h_vbuf = h_vbuf;
@@ -11094,7 +11077,7 @@ static struct sh_css_hmm_buffer_record
buffer_record = &hmm_buffer_record[0];
for (i = 0; i < MAX_HMM_BUFFER_NUM; i++) {
- if ((buffer_record->in_use == true) &&
+ if ((buffer_record->in_use) &&
(buffer_record->type == type) &&
(buffer_record->h_vbuf != NULL) &&
(buffer_record->h_vbuf->vptr == ddr_buffer_addr)) {
@@ -11104,7 +11087,7 @@ static struct sh_css_hmm_buffer_record
buffer_record++;
}
- if (found_record == true)
+ if (found_record)
return buffer_record;
else
return NULL;
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_legacy.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_legacy.h
index 4bcc35d219f8..4fd25ba2cd0d 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_legacy.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_legacy.h
@@ -52,17 +52,6 @@ struct ia_css_pipe_extra_config {
bool disable_vf_pp;
};
-#define DEFAULT_PIPE_EXTRA_CONFIG \
-{ \
- false, /* enable_raw_binning */ \
- false, /* enable_yuv_ds */ \
- false, /* enable_high_speed */ \
- false, /* enable_dvs_6axis */ \
- false, /* enable_reduced_pipe */ \
- false, /* enable_fractional_ds */ \
- false, /* disable_vf_pp */ \
-}
-
enum ia_css_err
ia_css_pipe_create_extra(const struct ia_css_pipe_config *config,
const struct ia_css_pipe_extra_config *extra_config,
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_metrics.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_metrics.h
index 40840ea318ab..2ef9238d95ad 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_metrics.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_metrics.h
@@ -24,16 +24,6 @@ struct sh_css_pc_histogram {
unsigned *msink;
};
-#if !defined(__USE_DESIGNATED_INITIALISERS__)
-#define DEFAULT_PC_HISTOGRAM \
-{ \
- 0, \
- NULL, \
- NULL, \
- NULL \
-}
-#endif
-
struct sh_css_binary_metrics {
unsigned mode;
unsigned id;
@@ -42,17 +32,6 @@ struct sh_css_binary_metrics {
struct sh_css_binary_metrics *next;
};
-#if !defined(__USE_DESIGNATED_INITIALISERS__)
-#define DEFAULT_BINARY_METRICS \
-{ \
- 0, \
- 0, \
- DEFAULT_PC_HISTOGRAM, \
- DEFAULT_PC_HISTOGRAM, \
- NULL \
-}
-#endif
-
struct ia_css_frame_metrics {
unsigned num_frames;
};
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_mipi.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_mipi.c
index 883474e90c81..a6a00024bae8 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_mipi.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_mipi.c
@@ -104,7 +104,7 @@ static bool ia_css_mipi_is_source_port_valid(struct ia_css_pipe *pipe,
enum ia_css_err
ia_css_mipi_frame_calculate_size(const unsigned int width,
const unsigned int height,
- const enum ia_css_stream_format format,
+ const enum atomisp_input_format format,
const bool hasSOLandEOL,
const unsigned int embedded_data_size_words,
unsigned int *size_mem_words)
@@ -136,16 +136,16 @@ ia_css_mipi_frame_calculate_size(const unsigned int width,
width_padded, height, format, hasSOLandEOL, embedded_data_size_words);
switch (format) {
- case IA_CSS_STREAM_FORMAT_RAW_6: /* 4p, 3B, 24bits */
+ case ATOMISP_INPUT_FORMAT_RAW_6: /* 4p, 3B, 24bits */
bits_per_pixel = 6; break;
- case IA_CSS_STREAM_FORMAT_RAW_7: /* 8p, 7B, 56bits */
+ case ATOMISP_INPUT_FORMAT_RAW_7: /* 8p, 7B, 56bits */
bits_per_pixel = 7; break;
- case IA_CSS_STREAM_FORMAT_RAW_8: /* 1p, 1B, 8bits */
- case IA_CSS_STREAM_FORMAT_BINARY_8: /* 8bits, TODO: check. */
- case IA_CSS_STREAM_FORMAT_YUV420_8: /* odd 2p, 2B, 16bits, even 2p, 4B, 32bits */
+ case ATOMISP_INPUT_FORMAT_RAW_8: /* 1p, 1B, 8bits */
+ case ATOMISP_INPUT_FORMAT_BINARY_8: /* 8bits, TODO: check. */
+ case ATOMISP_INPUT_FORMAT_YUV420_8: /* odd 2p, 2B, 16bits, even 2p, 4B, 32bits */
bits_per_pixel = 8; break;
- case IA_CSS_STREAM_FORMAT_YUV420_10: /* odd 4p, 5B, 40bits, even 4p, 10B, 80bits */
- case IA_CSS_STREAM_FORMAT_RAW_10: /* 4p, 5B, 40bits */
+ case ATOMISP_INPUT_FORMAT_YUV420_10: /* odd 4p, 5B, 40bits, even 4p, 10B, 80bits */
+ case ATOMISP_INPUT_FORMAT_RAW_10: /* 4p, 5B, 40bits */
#if !defined(HAS_NO_PACKED_RAW_PIXELS)
/* The changes will be reverted as soon as RAW
* Buffers are deployed by the 2401 Input System
@@ -156,26 +156,26 @@ ia_css_mipi_frame_calculate_size(const unsigned int width,
bits_per_pixel = 16;
#endif
break;
- case IA_CSS_STREAM_FORMAT_YUV420_8_LEGACY: /* 2p, 3B, 24bits */
- case IA_CSS_STREAM_FORMAT_RAW_12: /* 2p, 3B, 24bits */
+ case ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY: /* 2p, 3B, 24bits */
+ case ATOMISP_INPUT_FORMAT_RAW_12: /* 2p, 3B, 24bits */
bits_per_pixel = 12; break;
- case IA_CSS_STREAM_FORMAT_RAW_14: /* 4p, 7B, 56bits */
+ case ATOMISP_INPUT_FORMAT_RAW_14: /* 4p, 7B, 56bits */
bits_per_pixel = 14; break;
- case IA_CSS_STREAM_FORMAT_RGB_444: /* 1p, 2B, 16bits */
- case IA_CSS_STREAM_FORMAT_RGB_555: /* 1p, 2B, 16bits */
- case IA_CSS_STREAM_FORMAT_RGB_565: /* 1p, 2B, 16bits */
- case IA_CSS_STREAM_FORMAT_YUV422_8: /* 2p, 4B, 32bits */
+ case ATOMISP_INPUT_FORMAT_RGB_444: /* 1p, 2B, 16bits */
+ case ATOMISP_INPUT_FORMAT_RGB_555: /* 1p, 2B, 16bits */
+ case ATOMISP_INPUT_FORMAT_RGB_565: /* 1p, 2B, 16bits */
+ case ATOMISP_INPUT_FORMAT_YUV422_8: /* 2p, 4B, 32bits */
bits_per_pixel = 16; break;
- case IA_CSS_STREAM_FORMAT_RGB_666: /* 4p, 9B, 72bits */
+ case ATOMISP_INPUT_FORMAT_RGB_666: /* 4p, 9B, 72bits */
bits_per_pixel = 18; break;
- case IA_CSS_STREAM_FORMAT_YUV422_10: /* 2p, 5B, 40bits */
+ case ATOMISP_INPUT_FORMAT_YUV422_10: /* 2p, 5B, 40bits */
bits_per_pixel = 20; break;
- case IA_CSS_STREAM_FORMAT_RGB_888: /* 1p, 3B, 24bits */
+ case ATOMISP_INPUT_FORMAT_RGB_888: /* 1p, 3B, 24bits */
bits_per_pixel = 24; break;
- case IA_CSS_STREAM_FORMAT_YUV420_16: /* Not supported */
- case IA_CSS_STREAM_FORMAT_YUV422_16: /* Not supported */
- case IA_CSS_STREAM_FORMAT_RAW_16: /* TODO: not specified in MIPI SPEC, check */
+ case ATOMISP_INPUT_FORMAT_YUV420_16: /* Not supported */
+ case ATOMISP_INPUT_FORMAT_YUV422_16: /* Not supported */
+ case ATOMISP_INPUT_FORMAT_RAW_16: /* TODO: not specified in MIPI SPEC, check */
default:
return IA_CSS_ERR_INVALID_ARGUMENTS;
}
@@ -183,9 +183,9 @@ ia_css_mipi_frame_calculate_size(const unsigned int width,
odd_line_bytes = (width_padded * bits_per_pixel + 7) >> 3; /* ceil ( bits per line / 8) */
/* Even lines for YUV420 formats are double in bits_per_pixel. */
- if (format == IA_CSS_STREAM_FORMAT_YUV420_8
- || format == IA_CSS_STREAM_FORMAT_YUV420_10
- || format == IA_CSS_STREAM_FORMAT_YUV420_16) {
+ if (format == ATOMISP_INPUT_FORMAT_YUV420_8
+ || format == ATOMISP_INPUT_FORMAT_YUV420_10
+ || format == ATOMISP_INPUT_FORMAT_YUV420_16) {
even_line_bytes = (width_padded * 2 * bits_per_pixel + 7) >> 3; /* ceil ( bits per line / 8) */
} else {
even_line_bytes = odd_line_bytes;
@@ -239,7 +239,7 @@ ia_css_mipi_frame_calculate_size(const unsigned int width,
#if !defined(HAS_NO_INPUT_SYSTEM) && defined(USE_INPUT_SYSTEM_VERSION_2)
enum ia_css_err
-ia_css_mipi_frame_enable_check_on_size(const enum ia_css_csi2_port port,
+ia_css_mipi_frame_enable_check_on_size(const enum mipi_port_id port,
const unsigned int size_mem_words)
{
uint32_t idx;
@@ -285,7 +285,7 @@ calculate_mipi_buff_size(
#else
unsigned int width;
unsigned int height;
- enum ia_css_stream_format format;
+ enum atomisp_input_format format;
bool pack_raw_pixels;
unsigned int width_padded;
@@ -348,15 +348,15 @@ calculate_mipi_buff_size(
bits_per_pixel = sh_css_stream_format_2_bits_per_subpixel(format);
bits_per_pixel =
- (format == IA_CSS_STREAM_FORMAT_RAW_10 && pack_raw_pixels) ? bits_per_pixel : 16;
+ (format == ATOMISP_INPUT_FORMAT_RAW_10 && pack_raw_pixels) ? bits_per_pixel : 16;
if (bits_per_pixel == 0)
return IA_CSS_ERR_INTERNAL_ERROR;
odd_line_bytes = (width_padded * bits_per_pixel + 7) >> 3; /* ceil ( bits per line / 8) */
/* Even lines for YUV420 formats are double in bits_per_pixel. */
- if (format == IA_CSS_STREAM_FORMAT_YUV420_8
- || format == IA_CSS_STREAM_FORMAT_YUV420_10) {
+ if (format == ATOMISP_INPUT_FORMAT_YUV420_8
+ || format == ATOMISP_INPUT_FORMAT_YUV420_10) {
even_line_bytes = (width_padded * 2 * bits_per_pixel + 7) >> 3; /* ceil ( bits per line / 8) */
} else {
even_line_bytes = odd_line_bytes;
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_params.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_params.c
index fbb36112fe3c..43529b1605c3 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_params.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_params.c
@@ -110,7 +110,7 @@
#define FPNTBL_BYTES(binary) \
(sizeof(char) * (binary)->in_frame_info.res.height * \
(binary)->in_frame_info.padded_width)
-
+
#ifndef ISP2401
#define SCTBL_BYTES(binary) \
@@ -1741,7 +1741,7 @@ ia_css_process_zoom_and_motion(
out_infos[0] = &args->out_frame[0]->info;
info = &stage->firmware->info.isp;
ia_css_binary_fill_info(info, false, false,
- IA_CSS_STREAM_FORMAT_RAW_10,
+ ATOMISP_INPUT_FORMAT_RAW_10,
args->in_frame ? &args->in_frame->info : NULL,
NULL,
out_infos,
@@ -2891,8 +2891,8 @@ ia_css_metadata_free_multiple(unsigned int num_bufs, struct ia_css_metadata **bu
}
}
-unsigned g_param_buffer_dequeue_count = 0;
-unsigned g_param_buffer_enqueue_count = 0;
+static unsigned g_param_buffer_dequeue_count = 0;
+static unsigned g_param_buffer_enqueue_count = 0;
enum ia_css_err
ia_css_stream_isp_parameters_init(struct ia_css_stream *stream)
@@ -3805,7 +3805,6 @@ sh_css_param_update_isp_params(struct ia_css_pipe *curr_pipe,
enum sh_css_queue_id queue_id;
- (void)stage;
pipe = curr_pipe->stream->pipes[i];
pipeline = ia_css_pipe_get_pipeline(pipe);
pipe_num = ia_css_pipe_get_pipe_num(pipe);
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_sp.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_sp.c
index 6fc00fc402b1..85263725540d 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_sp.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_sp.c
@@ -71,7 +71,7 @@
struct sh_css_sp_group sh_css_sp_group;
struct sh_css_sp_stage sh_css_sp_stage;
struct sh_css_isp_stage sh_css_isp_stage;
-struct sh_css_sp_output sh_css_sp_output;
+static struct sh_css_sp_output sh_css_sp_output;
static struct sh_css_sp_per_frame_data per_frame_data;
/* true if SP supports frame loop and host2sp_commands */
@@ -117,9 +117,9 @@ copy_isp_stage_to_sp_stage(void)
*/
sh_css_sp_stage.enable.sdis = sh_css_isp_stage.binary_info.enable.dis;
sh_css_sp_stage.enable.s3a = sh_css_isp_stage.binary_info.enable.s3a;
-#ifdef ISP2401
+#ifdef ISP2401
sh_css_sp_stage.enable.lace_stats = sh_css_isp_stage.binary_info.enable.lace_stats;
-#endif
+#endif
}
void
@@ -754,7 +754,7 @@ sh_css_sp_write_frame_pointers(const struct sh_css_binary_args *args)
static void
sh_css_sp_init_group(bool two_ppc,
- enum ia_css_stream_format input_format,
+ enum atomisp_input_format input_format,
bool no_isp_sync,
uint8_t if_config_index)
{
@@ -817,7 +817,6 @@ configure_isp_from_args(
bool two_ppc,
bool deinterleaved)
{
- enum ia_css_err err = IA_CSS_SUCCESS;
#ifdef ISP2401
struct ia_css_pipe *pipe = find_pipe_by_num(pipeline->pipe_num);
const struct ia_css_resolution *res;
@@ -841,7 +840,7 @@ configure_isp_from_args(
ia_css_ref_configure(binary, (const struct ia_css_frame **)args->delay_frames, pipeline->dvs_frame_delay);
ia_css_tnr_configure(binary, (const struct ia_css_frame **)args->tnr_frames);
ia_css_bayer_io_config(binary, args);
- return err;
+ return IA_CSS_SUCCESS;
}
static void
@@ -1118,7 +1117,7 @@ sp_init_stage(struct ia_css_pipeline_stage *stage,
out_infos[0] = &args->out_frame[0]->info;
info = &firmware->info.isp;
ia_css_binary_fill_info(info, false, false,
- IA_CSS_STREAM_FORMAT_RAW_10,
+ ATOMISP_INPUT_FORMAT_RAW_10,
args->in_frame ? &args->in_frame->info : NULL,
NULL,
out_infos,
@@ -1197,7 +1196,7 @@ sh_css_sp_init_pipeline(struct ia_css_pipeline *me,
const struct ia_css_metadata_config *md_config,
const struct ia_css_metadata_info *md_info,
#if !defined(HAS_NO_INPUT_SYSTEM)
- const mipi_port_ID_t port_id
+ const enum mipi_port_id port_id
#endif
#ifdef ISP2401
,
@@ -1442,8 +1441,6 @@ sh_css_update_host2sp_offline_frame(
unsigned int HIVE_ADDR_host_sp_com;
unsigned int offset;
- (void)HIVE_ADDR_host_sp_com; /* Suppres warnings in CRUN */
-
assert(frame_num < NUM_CONTINUOUS_FRAMES);
/* Write new frame data into SP DMEM */
@@ -1473,8 +1470,6 @@ sh_css_update_host2sp_mipi_frame(
unsigned int HIVE_ADDR_host_sp_com;
unsigned int offset;
- (void)HIVE_ADDR_host_sp_com; /* Suppres warnings in CRUN */
-
/* MIPI buffers are dedicated to port, so now there are more of them. */
assert(frame_num < (N_CSI_PORTS * NUM_MIPI_FRAMES_PER_STREAM));
@@ -1500,8 +1495,6 @@ sh_css_update_host2sp_mipi_metadata(
unsigned int HIVE_ADDR_host_sp_com;
unsigned int o;
- (void)HIVE_ADDR_host_sp_com; /* Suppres warnings in CRUN */
-
/* MIPI buffers are dedicated to port, so now there are more of them. */
assert(frame_num < (N_CSI_PORTS * NUM_MIPI_FRAMES_PER_STREAM));
@@ -1520,8 +1513,6 @@ sh_css_update_host2sp_num_mipi_frames(unsigned num_frames)
unsigned int HIVE_ADDR_host_sp_com;
unsigned int offset;
- (void)HIVE_ADDR_host_sp_com; /* Suppres warnings in CRUN */
-
/* Write new frame data into SP DMEM */
HIVE_ADDR_host_sp_com = sh_css_sp_fw.info.sp.host_sp_com;
offset = (unsigned int)offsetof(struct host_sp_communication, host2sp_num_mipi_frames)
@@ -1539,8 +1530,6 @@ sh_css_update_host2sp_cont_num_raw_frames(unsigned num_frames, bool set_avail)
unsigned int extra_num_frames, avail_num_frames;
unsigned int offset, offset_extra;
- (void)HIVE_ADDR_host_sp_com; /* Suppres warnings in CRUN */
-
/* Write new frame data into SP DMEM */
fw = &sh_css_sp_fw;
HIVE_ADDR_host_sp_com = fw->info.sp.host_sp_com;
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_sp.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_sp.h
index 98444a3cc3e4..3c41e997de79 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_sp.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_sp.h
@@ -64,7 +64,7 @@ sh_css_sp_init_pipeline(struct ia_css_pipeline *me,
const struct ia_css_metadata_config *md_config,
const struct ia_css_metadata_info *md_info,
#if !defined(HAS_NO_INPUT_SYSTEM)
- const mipi_port_ID_t port_id
+ const enum mipi_port_id port_id
#endif
#ifdef ISP2401
,
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_stream_format.c b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_stream_format.c
index 52d0a6471597..77f135e7dc3c 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_stream_format.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_stream_format.c
@@ -16,55 +16,55 @@
#include <ia_css_stream_format.h>
unsigned int sh_css_stream_format_2_bits_per_subpixel(
- enum ia_css_stream_format format)
+ enum atomisp_input_format format)
{
unsigned int rval;
switch (format) {
- case IA_CSS_STREAM_FORMAT_RGB_444:
+ case ATOMISP_INPUT_FORMAT_RGB_444:
rval = 4;
break;
- case IA_CSS_STREAM_FORMAT_RGB_555:
+ case ATOMISP_INPUT_FORMAT_RGB_555:
rval = 5;
break;
- case IA_CSS_STREAM_FORMAT_RGB_565:
- case IA_CSS_STREAM_FORMAT_RGB_666:
- case IA_CSS_STREAM_FORMAT_RAW_6:
+ case ATOMISP_INPUT_FORMAT_RGB_565:
+ case ATOMISP_INPUT_FORMAT_RGB_666:
+ case ATOMISP_INPUT_FORMAT_RAW_6:
rval = 6;
break;
- case IA_CSS_STREAM_FORMAT_RAW_7:
+ case ATOMISP_INPUT_FORMAT_RAW_7:
rval = 7;
break;
- case IA_CSS_STREAM_FORMAT_YUV420_8_LEGACY:
- case IA_CSS_STREAM_FORMAT_YUV420_8:
- case IA_CSS_STREAM_FORMAT_YUV422_8:
- case IA_CSS_STREAM_FORMAT_RGB_888:
- case IA_CSS_STREAM_FORMAT_RAW_8:
- case IA_CSS_STREAM_FORMAT_BINARY_8:
- case IA_CSS_STREAM_FORMAT_USER_DEF1:
- case IA_CSS_STREAM_FORMAT_USER_DEF2:
- case IA_CSS_STREAM_FORMAT_USER_DEF3:
- case IA_CSS_STREAM_FORMAT_USER_DEF4:
- case IA_CSS_STREAM_FORMAT_USER_DEF5:
- case IA_CSS_STREAM_FORMAT_USER_DEF6:
- case IA_CSS_STREAM_FORMAT_USER_DEF7:
- case IA_CSS_STREAM_FORMAT_USER_DEF8:
+ case ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY:
+ case ATOMISP_INPUT_FORMAT_YUV420_8:
+ case ATOMISP_INPUT_FORMAT_YUV422_8:
+ case ATOMISP_INPUT_FORMAT_RGB_888:
+ case ATOMISP_INPUT_FORMAT_RAW_8:
+ case ATOMISP_INPUT_FORMAT_BINARY_8:
+ case ATOMISP_INPUT_FORMAT_USER_DEF1:
+ case ATOMISP_INPUT_FORMAT_USER_DEF2:
+ case ATOMISP_INPUT_FORMAT_USER_DEF3:
+ case ATOMISP_INPUT_FORMAT_USER_DEF4:
+ case ATOMISP_INPUT_FORMAT_USER_DEF5:
+ case ATOMISP_INPUT_FORMAT_USER_DEF6:
+ case ATOMISP_INPUT_FORMAT_USER_DEF7:
+ case ATOMISP_INPUT_FORMAT_USER_DEF8:
rval = 8;
break;
- case IA_CSS_STREAM_FORMAT_YUV420_10:
- case IA_CSS_STREAM_FORMAT_YUV422_10:
- case IA_CSS_STREAM_FORMAT_RAW_10:
+ case ATOMISP_INPUT_FORMAT_YUV420_10:
+ case ATOMISP_INPUT_FORMAT_YUV422_10:
+ case ATOMISP_INPUT_FORMAT_RAW_10:
rval = 10;
break;
- case IA_CSS_STREAM_FORMAT_RAW_12:
+ case ATOMISP_INPUT_FORMAT_RAW_12:
rval = 12;
break;
- case IA_CSS_STREAM_FORMAT_RAW_14:
+ case ATOMISP_INPUT_FORMAT_RAW_14:
rval = 14;
break;
- case IA_CSS_STREAM_FORMAT_RAW_16:
- case IA_CSS_STREAM_FORMAT_YUV420_16:
- case IA_CSS_STREAM_FORMAT_YUV422_16:
+ case ATOMISP_INPUT_FORMAT_RAW_16:
+ case ATOMISP_INPUT_FORMAT_YUV420_16:
+ case ATOMISP_INPUT_FORMAT_YUV422_16:
rval = 16;
break;
default:
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_stream_format.h b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_stream_format.h
index aab2b6207051..b699f538e0dd 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_stream_format.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_stream_format.h
@@ -18,6 +18,6 @@
#include <ia_css_stream_format.h>
unsigned int sh_css_stream_format_2_bits_per_subpixel(
- enum ia_css_stream_format format);
+ enum atomisp_input_format format);
#endif /* __SH_CSS_STREAM_FORMAT_H */
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/include/hmm/hmm_bo_dev.h b/drivers/staging/media/atomisp/pci/atomisp2/include/hmm/hmm_bo_dev.h
deleted file mode 100644
index 9e51a657ece4..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/include/hmm/hmm_bo_dev.h
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Support for Medifield PNW Camera Imaging ISP subsystem.
- *
- * Copyright (c) 2010 Intel Corporation. All Rights Reserved.
- *
- * Copyright (c) 2010 Silicon Hive www.siliconhive.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.
- *
- * 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.
- *
- *
- */
-
-#ifndef __HMM_BO_DEV_H__
-#define __HMM_BO_DEV_H__
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/list.h>
-#include <linux/spinlock.h>
-#include <linux/mutex.h>
-#include "mmu/isp_mmu.h"
-#include "hmm/hmm_common.h"
-#include "hmm/hmm_vm.h"
-#include "ia_css_types.h"
-
-#define check_bodev_null_return(bdev, exp) \
- check_null_return(bdev, exp, \
- "NULL hmm_bo_device.\n")
-
-#define check_bodev_null_return_void(bdev) \
- check_null_return_void(bdev, \
- "NULL hmm_bo_device.\n")
-
-#define HMM_BO_DEVICE_INITED 0x1
-
-#define HMM_BO_CACHE_SIZE 2
-
-
-struct hmm_buffer_object;
-
-struct hmm_bo_device {
- /* isp_mmu provides lock itself */
- struct isp_mmu mmu;
-
- /* hmm_vm provides lock itself */
- struct hmm_vm vaddr_space;
-
- struct list_head free_bo_list;
- struct list_head active_bo_list;
-
- /* list lock is used to protect both of the buffer object lists */
- spinlock_t list_lock;
-#ifdef CONFIG_ION
- struct ion_client *iclient;
-#endif
- int flag;
-};
-
-int hmm_bo_device_init(struct hmm_bo_device *bdev,
- struct isp_mmu_client *mmu_driver,
- unsigned int vaddr_start, unsigned int size);
-
-/*
- * clean up all hmm_bo_device related things.
- */
-void hmm_bo_device_exit(struct hmm_bo_device *bdev);
-
-/*
- * whether the bo device is inited or not.
- */
-int hmm_bo_device_inited(struct hmm_bo_device *bdev);
-
-/*
- * find the buffer object with virtual address vaddr.
- * return NULL if no such buffer object found.
- */
-struct hmm_buffer_object *hmm_bo_device_search_start(
- struct hmm_bo_device *bdev, ia_css_ptr vaddr);
-
-/*
- * find the buffer object with virtual address vaddr.
- * return NULL if no such buffer object found.
- */
-struct hmm_buffer_object *hmm_bo_device_search_in_range(
- struct hmm_bo_device *bdev, ia_css_ptr vaddr);
-
-/*
- * find the buffer object with kernel virtual address vaddr.
- * return NULL if no such buffer object found.
- */
-struct hmm_buffer_object *hmm_bo_device_search_vmap_start(
- struct hmm_bo_device *bdev, const void *vaddr);
-
-/*
- * find a buffer object with pgnr pages from free_bo_list and
- * activate it (remove from free_bo_list and add to
- * active_bo_list)
- *
- * return NULL if no such buffer object found.
- */
-struct hmm_buffer_object *hmm_bo_device_get_bo(
- struct hmm_bo_device *bdev, unsigned int pgnr);
-
-/*
- * destroy all buffer objects in the free_bo_list.
- */
-void hmm_bo_device_destroy_free_bo_list(struct hmm_bo_device *bdev);
-/*
- * destroy buffer object with start virtual address vaddr.
- */
-void hmm_bo_device_destroy_free_bo_addr(struct hmm_bo_device *bdev,
- ia_css_ptr vaddr);
-/*
- * destroy all buffer objects with pgnr pages.
- */
-void hmm_bo_device_destroy_free_bo_size(struct hmm_bo_device *bdev,
- unsigned int pgnr);
-
-#endif
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/include/mmu/isp_mmu.h b/drivers/staging/media/atomisp/pci/atomisp2/include/mmu/isp_mmu.h
index 560014add005..4b2d94a37ea1 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/include/mmu/isp_mmu.h
+++ b/drivers/staging/media/atomisp/pci/atomisp2/include/mmu/isp_mmu.h
@@ -80,12 +80,10 @@ struct isp_mmu_client {
unsigned int null_pte;
/*
- * set/get page directory base address (physical address).
+ * get page directory base address (physical address).
*
* must be provided.
*/
- int (*set_pd_base) (struct isp_mmu *mmu,
- phys_addr_t pd_base);
unsigned int (*get_pd_base) (struct isp_mmu *mmu, phys_addr_t pd_base);
/*
* callback to flush tlb.
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/include/mmu/sh_mmu.h b/drivers/staging/media/atomisp/pci/atomisp2/include/mmu/sh_mmu.h
deleted file mode 100644
index 031c0398bf65..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp2/include/mmu/sh_mmu.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Support for Medifield PNW Camera Imaging ISP subsystem.
- *
- * Copyright (c) 2010 Intel Corporation. All Rights Reserved.
- *
- * Copyright (c) 2010 Silicon Hive www.siliconhive.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.
- *
- * 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.
- *
- *
- */
-#ifndef SH_MMU_H_
-#define SH_MMU_H_
-
-
-#include <sh_css.h>
-
-#include "mmu/isp_mmu.h"
-
-
-/*
- * include SH header file here
- */
-
-/*
- * set page directory base address (physical address).
- *
- * must be provided.
- */
-static int sh_set_pd_base(struct isp_mmu *mmu,
- unsigned int phys)
-{
- sh_css_mmu_set_page_table_base_address((void *)phys);
- return 0;
-}
-
-/*
- * callback to flush tlb.
- *
- * tlb_flush_range will at least flush TLBs containing
- * address mapping from addr to addr + size.
- *
- * tlb_flush_all will flush all TLBs.
- *
- * tlb_flush_all is must be provided. if tlb_flush_range is
- * not valid, it will set to tlb_flush_all by default.
- */
-static void sh_tlb_flush(struct isp_mmu *mmu)
-{
- sh_css_mmu_invalidate_cache();
-}
-
-static struct isp_mmu_driver sh_mmu_driver = {
- .name = "Silicon Hive ISP3000 MMU",
- .pte_valid_mask = 0x1,
- .set_pd_base = sh_set_pd_base,
- .tlb_flush_all = sh_tlb_flush,
-};
-
-#define ISP_VM_START 0x0
-#define ISP_VM_SIZE (1 << 30) /* 1G address space */
-#define ISP_PTR_NULL NULL
-
-#endif /* SH_MMU_H_ */
-
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/mmu/isp_mmu.c b/drivers/staging/media/atomisp/pci/atomisp2/mmu/isp_mmu.c
index e36c2a33b41a..198f29f4a324 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/mmu/isp_mmu.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/mmu/isp_mmu.c
@@ -344,13 +344,6 @@ static int mmu_map(struct isp_mmu *mmu, unsigned int isp_virt,
/*
* setup L1 page table physical addr to MMU
*/
- ret = mmu->driver->set_pd_base(mmu, l1_pt);
- if (ret) {
- dev_err(atomisp_dev,
- "set page directory base address fail.\n");
- mutex_unlock(&mmu->pt_mutex);
- return ret;
- }
mmu->base_address = l1_pt;
mmu->l1_pte = isp_pgaddr_to_pte_valid(mmu, l1_pt);
memset(mmu->l2_pgt_refcount, 0, sizeof(int) * ISP_L1PT_PTES);
@@ -450,7 +443,7 @@ static void mmu_l1_unmap(struct isp_mmu *mmu, phys_addr_t l1_pt,
ptr = end;
}
/*
- * use the same L2 page next time, so we dont
+ * use the same L2 page next time, so we don't
* need to invalidate and free this PT.
*/
/* atomisp_set_pte(l1_pt, idx, NULL_PTE); */
@@ -531,10 +524,8 @@ int isp_mmu_init(struct isp_mmu *mmu, struct isp_mmu_client *driver)
mmu->driver = driver;
- if (!driver->set_pd_base || !driver->tlb_flush_all) {
- dev_err(atomisp_dev,
- "set_pd_base or tlb_flush_all operation "
- "not provided.\n");
+ if (!driver->tlb_flush_all) {
+ dev_err(atomisp_dev, "tlb_flush_all operation not provided.\n");
return -EINVAL;
}
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/mmu/sh_mmu_mrfld.c b/drivers/staging/media/atomisp/pci/atomisp2/mmu/sh_mmu_mrfld.c
index c59bcc982966..c0212564b7c8 100644
--- a/drivers/staging/media/atomisp/pci/atomisp2/mmu/sh_mmu_mrfld.c
+++ b/drivers/staging/media/atomisp/pci/atomisp2/mmu/sh_mmu_mrfld.c
@@ -18,6 +18,7 @@
*/
#include "type_support.h"
#include "mmu/isp_mmu.h"
+#include "mmu/sh_mmu_mrfld.h"
#include "memory_access/memory_access.h"
#include "atomisp_compat.h"
@@ -40,20 +41,6 @@ static phys_addr_t sh_pte_to_phys(struct isp_mmu *mmu,
return (phys_addr_t)((pte & ~mask) << ISP_PAGE_OFFSET);
}
-/*
- * set page directory base address (physical address).
- *
- * must be provided.
- */
-static int sh_set_pd_base(struct isp_mmu *mmu,
- phys_addr_t phys)
-{
- unsigned int pte = sh_phys_to_pte(mmu, phys);
- /*mmgr_set_base_address(HOST_ADDRESS(pte));*/
- atomisp_css_mmu_set_page_table_base_index(HOST_ADDRESS(pte));
- return 0;
-}
-
static unsigned int sh_get_pd_base(struct isp_mmu *mmu,
phys_addr_t phys)
{
@@ -81,7 +68,6 @@ struct isp_mmu_client sh_mmu_mrfld = {
.name = "Silicon Hive ISP3000 MMU",
.pte_valid_mask = MERR_VALID_PTE_MASK,
.null_pte = ~MERR_VALID_PTE_MASK,
- .set_pd_base = sh_set_pd_base,
.get_pd_base = sh_get_pd_base,
.tlb_flush_all = sh_tlb_flush,
.phys_to_pte = sh_phys_to_pte,
diff --git a/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c b/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c
index d8b7183db252..3283c1b05d6a 100644
--- a/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c
+++ b/drivers/staging/media/atomisp/platform/intel-mid/atomisp_gmin_platform.c
@@ -441,7 +441,7 @@ static int gmin_v1p2_ctrl(struct v4l2_subdev *subdev, int on)
{
struct gmin_subdev *gs = find_gmin_subdev(subdev);
- if (gs && gs->v1p2_on == on)
+ if (!gs || gs->v1p2_on == on)
return 0;
gs->v1p2_on = on;
@@ -475,7 +475,7 @@ static int gmin_v1p8_ctrl(struct v4l2_subdev *subdev, int on)
}
}
- if (gs && gs->v1p8_on == on)
+ if (!gs || gs->v1p8_on == on)
return 0;
gs->v1p8_on = on;
@@ -511,7 +511,7 @@ static int gmin_v2p8_ctrl(struct v4l2_subdev *subdev, int on)
}
}
- if (gs && gs->v2p8_on == on)
+ if (!gs || gs->v2p8_on == on)
return 0;
gs->v2p8_on = on;
@@ -693,9 +693,11 @@ static int gmin_get_config_var(struct device *dev, const char *var,
for (i = 0; i < sizeof(var8) && var8[i]; i++)
var16[i] = var8[i];
+#ifdef CONFIG_64BIT
/* To avoid owerflows when calling the efivar API */
if (*out_len > ULONG_MAX)
return -EINVAL;
+#endif
/* Not sure this API usage is kosher; efivar_entry_get()'s
* implementation simply uses VariableName and VendorGuid from
diff --git a/drivers/staging/media/cxd2099/Kconfig b/drivers/staging/media/cxd2099/Kconfig
deleted file mode 100644
index b48aefddc84c..000000000000
--- a/drivers/staging/media/cxd2099/Kconfig
+++ /dev/null
@@ -1,12 +0,0 @@
-config DVB_CXD2099
- tristate "CXD2099AR Common Interface driver"
- depends on DVB_CORE && PCI && I2C
- ---help---
- Support for the CI module found on cards based on
- - Micronas ngene PCIe bridge: cineS2 etc.
- - Digital Devices PCIe bridge: Octopus series
-
- For now, data is passed through '/dev/dvb/adapterX/sec0':
- - Encrypted data must be written to 'sec0'.
- - Decrypted data can be read from 'sec0'.
- - Setup the CAM using device 'ca0'.
diff --git a/drivers/staging/media/cxd2099/Makefile b/drivers/staging/media/cxd2099/Makefile
deleted file mode 100644
index 30432c9aabc4..000000000000
--- a/drivers/staging/media/cxd2099/Makefile
+++ /dev/null
@@ -1,4 +0,0 @@
-obj-$(CONFIG_DVB_CXD2099) += cxd2099.o
-
-ccflags-y += -Idrivers/media/dvb-frontends/
-ccflags-y += -Idrivers/media/tuners/
diff --git a/drivers/staging/media/cxd2099/TODO b/drivers/staging/media/cxd2099/TODO
deleted file mode 100644
index 375bb6f8ee2c..000000000000
--- a/drivers/staging/media/cxd2099/TODO
+++ /dev/null
@@ -1,12 +0,0 @@
-For now, data is passed through '/dev/dvb/adapterX/sec0':
- - Encrypted data must be written to 'sec0'.
- - Decrypted data can be read from 'sec0'.
- - Setup the CAM using device 'ca0'.
-
-But this is wrong. There are some discussions about the proper way for
-doing it, as seen at:
- http://www.mail-archive.com/linux-media@vger.kernel.org/msg22196.html
-
-While there's no proper fix for it, the driver should be kept in staging.
-
-Patches should be submitted to: linux-media@vger.kernel.org.
diff --git a/drivers/staging/media/cxd2099/cxd2099.c b/drivers/staging/media/cxd2099/cxd2099.c
deleted file mode 100644
index dc9cbd8f2104..000000000000
--- a/drivers/staging/media/cxd2099/cxd2099.c
+++ /dev/null
@@ -1,705 +0,0 @@
-/*
- * cxd2099.c: Driver for the CXD2099AR Common Interface Controller
- *
- * Copyright (C) 2010-2013 Digital Devices GmbH
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 only, as published by the Free Software Foundation.
- *
- * 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.
- */
-
-#include <linux/slab.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/i2c.h>
-#include <linux/wait.h>
-#include <linux/delay.h>
-#include <linux/mutex.h>
-#include <linux/io.h>
-
-#include "cxd2099.h"
-
-static int buffermode;
-module_param(buffermode, int, 0444);
-MODULE_PARM_DESC(buffermode, "Enable CXD2099AR buffer mode (default: disabled)");
-
-static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount);
-
-struct cxd {
- struct dvb_ca_en50221 en;
-
- struct i2c_adapter *i2c;
- struct cxd2099_cfg cfg;
-
- u8 regs[0x23];
- u8 lastaddress;
- u8 clk_reg_f;
- u8 clk_reg_b;
- int mode;
- int ready;
- int dr;
- int write_busy;
- int slot_stat;
-
- u8 amem[1024];
- int amem_read;
-
- int cammode;
- struct mutex lock; /* device access lock */
-
- u8 rbuf[1028];
- u8 wbuf[1028];
-};
-
-static int i2c_write_reg(struct i2c_adapter *adapter, u8 adr,
- u8 reg, u8 data)
-{
- u8 m[2] = {reg, data};
- struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m, .len = 2};
-
- if (i2c_transfer(adapter, &msg, 1) != 1) {
- dev_err(&adapter->dev,
- "Failed to write to I2C register %02x@%02x!\n",
- reg, adr);
- return -1;
- }
- return 0;
-}
-
-static int i2c_write(struct i2c_adapter *adapter, u8 adr,
- u8 *data, u16 len)
-{
- struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = data, .len = len};
-
- if (i2c_transfer(adapter, &msg, 1) != 1) {
- dev_err(&adapter->dev, "Failed to write to I2C!\n");
- return -1;
- }
- return 0;
-}
-
-static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr,
- u8 reg, u8 *val)
-{
- struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
- .buf = &reg, .len = 1},
- {.addr = adr, .flags = I2C_M_RD,
- .buf = val, .len = 1} };
-
- if (i2c_transfer(adapter, msgs, 2) != 2) {
- dev_err(&adapter->dev, "error in %s()\n", __func__);
- return -1;
- }
- return 0;
-}
-
-static int i2c_read(struct i2c_adapter *adapter, u8 adr,
- u8 reg, u8 *data, u16 n)
-{
- struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
- .buf = &reg, .len = 1},
- {.addr = adr, .flags = I2C_M_RD,
- .buf = data, .len = n} };
-
- if (i2c_transfer(adapter, msgs, 2) != 2) {
- dev_err(&adapter->dev, "error in %s()\n", __func__);
- return -1;
- }
- return 0;
-}
-
-static int read_block(struct cxd *ci, u8 adr, u8 *data, u16 n)
-{
- int status = 0;
-
- if (ci->lastaddress != adr)
- status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, adr);
- if (!status) {
- ci->lastaddress = adr;
-
- while (n) {
- int len = n;
-
- if (ci->cfg.max_i2c && len > ci->cfg.max_i2c)
- len = ci->cfg.max_i2c;
- status = i2c_read(ci->i2c, ci->cfg.adr, 1, data, len);
- if (status)
- return status;
- data += len;
- n -= len;
- }
- }
- return status;
-}
-
-static int read_reg(struct cxd *ci, u8 reg, u8 *val)
-{
- return read_block(ci, reg, val, 1);
-}
-
-static int read_pccard(struct cxd *ci, u16 address, u8 *data, u8 n)
-{
- int status;
- u8 addr[3] = {2, address & 0xff, address >> 8};
-
- status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
- if (!status)
- status = i2c_read(ci->i2c, ci->cfg.adr, 3, data, n);
- return status;
-}
-
-static int write_pccard(struct cxd *ci, u16 address, u8 *data, u8 n)
-{
- int status;
- u8 addr[3] = {2, address & 0xff, address >> 8};
-
- status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
- if (!status) {
- u8 buf[256] = {3};
-
- memcpy(buf + 1, data, n);
- status = i2c_write(ci->i2c, ci->cfg.adr, buf, n + 1);
- }
- return status;
-}
-
-static int read_io(struct cxd *ci, u16 address, u8 *val)
-{
- int status;
- u8 addr[3] = {2, address & 0xff, address >> 8};
-
- status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
- if (!status)
- status = i2c_read(ci->i2c, ci->cfg.adr, 3, val, 1);
- return status;
-}
-
-static int write_io(struct cxd *ci, u16 address, u8 val)
-{
- int status;
- u8 addr[3] = {2, address & 0xff, address >> 8};
- u8 buf[2] = {3, val};
-
- status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
- if (!status)
- status = i2c_write(ci->i2c, ci->cfg.adr, buf, 2);
- return status;
-}
-
-static int write_regm(struct cxd *ci, u8 reg, u8 val, u8 mask)
-{
- int status = 0;
-
- if (ci->lastaddress != reg)
- status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, reg);
- if (!status && reg >= 6 && reg <= 8 && mask != 0xff)
- status = i2c_read_reg(ci->i2c, ci->cfg.adr, 1, &ci->regs[reg]);
- ci->lastaddress = reg;
- ci->regs[reg] = (ci->regs[reg] & (~mask)) | val;
- if (!status)
- status = i2c_write_reg(ci->i2c, ci->cfg.adr, 1, ci->regs[reg]);
- if (reg == 0x20)
- ci->regs[reg] &= 0x7f;
- return status;
-}
-
-static int write_reg(struct cxd *ci, u8 reg, u8 val)
-{
- return write_regm(ci, reg, val, 0xff);
-}
-
-static int write_block(struct cxd *ci, u8 adr, u8 *data, u16 n)
-{
- int status = 0;
- u8 *buf = ci->wbuf;
-
- if (ci->lastaddress != adr)
- status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, adr);
- if (status)
- return status;
-
- ci->lastaddress = adr;
- buf[0] = 1;
- while (n) {
- int len = n;
-
- if (ci->cfg.max_i2c && (len + 1 > ci->cfg.max_i2c))
- len = ci->cfg.max_i2c - 1;
- memcpy(buf + 1, data, len);
- status = i2c_write(ci->i2c, ci->cfg.adr, buf, len + 1);
- if (status)
- return status;
- n -= len;
- data += len;
- }
- return status;
-}
-
-static void set_mode(struct cxd *ci, int mode)
-{
- if (mode == ci->mode)
- return;
-
- switch (mode) {
- case 0x00: /* IO mem */
- write_regm(ci, 0x06, 0x00, 0x07);
- break;
- case 0x01: /* ATT mem */
- write_regm(ci, 0x06, 0x02, 0x07);
- break;
- default:
- break;
- }
- ci->mode = mode;
-}
-
-static void cam_mode(struct cxd *ci, int mode)
-{
- u8 dummy;
-
- if (mode == ci->cammode)
- return;
-
- switch (mode) {
- case 0x00:
- write_regm(ci, 0x20, 0x80, 0x80);
- break;
- case 0x01:
- if (!ci->en.read_data)
- return;
- ci->write_busy = 0;
- dev_info(&ci->i2c->dev, "enable cam buffer mode\n");
- write_reg(ci, 0x0d, 0x00);
- write_reg(ci, 0x0e, 0x01);
- write_regm(ci, 0x08, 0x40, 0x40);
- read_reg(ci, 0x12, &dummy);
- write_regm(ci, 0x08, 0x80, 0x80);
- break;
- default:
- break;
- }
- ci->cammode = mode;
-}
-
-static int init(struct cxd *ci)
-{
- int status;
-
- mutex_lock(&ci->lock);
- ci->mode = -1;
- do {
- status = write_reg(ci, 0x00, 0x00);
- if (status < 0)
- break;
- status = write_reg(ci, 0x01, 0x00);
- if (status < 0)
- break;
- status = write_reg(ci, 0x02, 0x10);
- if (status < 0)
- break;
- status = write_reg(ci, 0x03, 0x00);
- if (status < 0)
- break;
- status = write_reg(ci, 0x05, 0xFF);
- if (status < 0)
- break;
- status = write_reg(ci, 0x06, 0x1F);
- if (status < 0)
- break;
- status = write_reg(ci, 0x07, 0x1F);
- if (status < 0)
- break;
- status = write_reg(ci, 0x08, 0x28);
- if (status < 0)
- break;
- status = write_reg(ci, 0x14, 0x20);
- if (status < 0)
- break;
-
- /* TOSTRT = 8, Mode B (gated clock), falling Edge,
- * Serial, POL=HIGH, MSB
- */
- status = write_reg(ci, 0x0A, 0xA7);
- if (status < 0)
- break;
-
- status = write_reg(ci, 0x0B, 0x33);
- if (status < 0)
- break;
- status = write_reg(ci, 0x0C, 0x33);
- if (status < 0)
- break;
-
- status = write_regm(ci, 0x14, 0x00, 0x0F);
- if (status < 0)
- break;
- status = write_reg(ci, 0x15, ci->clk_reg_b);
- if (status < 0)
- break;
- status = write_regm(ci, 0x16, 0x00, 0x0F);
- if (status < 0)
- break;
- status = write_reg(ci, 0x17, ci->clk_reg_f);
- if (status < 0)
- break;
-
- if (ci->cfg.clock_mode == 2) {
- /* bitrate*2^13/ 72000 */
- u32 reg = ((ci->cfg.bitrate << 13) + 71999) / 72000;
-
- if (ci->cfg.polarity) {
- status = write_reg(ci, 0x09, 0x6f);
- if (status < 0)
- break;
- } else {
- status = write_reg(ci, 0x09, 0x6d);
- if (status < 0)
- break;
- }
- status = write_reg(ci, 0x20, 0x08);
- if (status < 0)
- break;
- status = write_reg(ci, 0x21, (reg >> 8) & 0xff);
- if (status < 0)
- break;
- status = write_reg(ci, 0x22, reg & 0xff);
- if (status < 0)
- break;
- } else if (ci->cfg.clock_mode == 1) {
- if (ci->cfg.polarity) {
- status = write_reg(ci, 0x09, 0x6f); /* D */
- if (status < 0)
- break;
- } else {
- status = write_reg(ci, 0x09, 0x6d);
- if (status < 0)
- break;
- }
- status = write_reg(ci, 0x20, 0x68);
- if (status < 0)
- break;
- status = write_reg(ci, 0x21, 0x00);
- if (status < 0)
- break;
- status = write_reg(ci, 0x22, 0x02);
- if (status < 0)
- break;
- } else {
- if (ci->cfg.polarity) {
- status = write_reg(ci, 0x09, 0x4f); /* C */
- if (status < 0)
- break;
- } else {
- status = write_reg(ci, 0x09, 0x4d);
- if (status < 0)
- break;
- }
- status = write_reg(ci, 0x20, 0x28);
- if (status < 0)
- break;
- status = write_reg(ci, 0x21, 0x00);
- if (status < 0)
- break;
- status = write_reg(ci, 0x22, 0x07);
- if (status < 0)
- break;
- }
-
- status = write_regm(ci, 0x20, 0x80, 0x80);
- if (status < 0)
- break;
- status = write_regm(ci, 0x03, 0x02, 0x02);
- if (status < 0)
- break;
- status = write_reg(ci, 0x01, 0x04);
- if (status < 0)
- break;
- status = write_reg(ci, 0x00, 0x31);
- if (status < 0)
- break;
-
- /* Put TS in bypass */
- status = write_regm(ci, 0x09, 0x08, 0x08);
- if (status < 0)
- break;
- ci->cammode = -1;
- cam_mode(ci, 0);
- } while (0);
- mutex_unlock(&ci->lock);
-
- return 0;
-}
-
-static int read_attribute_mem(struct dvb_ca_en50221 *ca,
- int slot, int address)
-{
- struct cxd *ci = ca->data;
- u8 val;
-
- mutex_lock(&ci->lock);
- set_mode(ci, 1);
- read_pccard(ci, address, &val, 1);
- mutex_unlock(&ci->lock);
- return val;
-}
-
-static int write_attribute_mem(struct dvb_ca_en50221 *ca, int slot,
- int address, u8 value)
-{
- struct cxd *ci = ca->data;
-
- mutex_lock(&ci->lock);
- set_mode(ci, 1);
- write_pccard(ci, address, &value, 1);
- mutex_unlock(&ci->lock);
- return 0;
-}
-
-static int read_cam_control(struct dvb_ca_en50221 *ca,
- int slot, u8 address)
-{
- struct cxd *ci = ca->data;
- u8 val;
-
- mutex_lock(&ci->lock);
- set_mode(ci, 0);
- read_io(ci, address, &val);
- mutex_unlock(&ci->lock);
- return val;
-}
-
-static int write_cam_control(struct dvb_ca_en50221 *ca, int slot,
- u8 address, u8 value)
-{
- struct cxd *ci = ca->data;
-
- mutex_lock(&ci->lock);
- set_mode(ci, 0);
- write_io(ci, address, value);
- mutex_unlock(&ci->lock);
- return 0;
-}
-
-static int slot_reset(struct dvb_ca_en50221 *ca, int slot)
-{
- struct cxd *ci = ca->data;
-
- if (ci->cammode)
- read_data(ca, slot, ci->rbuf, 0);
-
- mutex_lock(&ci->lock);
- cam_mode(ci, 0);
- write_reg(ci, 0x00, 0x21);
- write_reg(ci, 0x06, 0x1F);
- write_reg(ci, 0x00, 0x31);
- write_regm(ci, 0x20, 0x80, 0x80);
- write_reg(ci, 0x03, 0x02);
- ci->ready = 0;
- ci->mode = -1;
- {
- int i;
-
- for (i = 0; i < 100; i++) {
- usleep_range(10000, 11000);
- if (ci->ready)
- break;
- }
- }
- mutex_unlock(&ci->lock);
- return 0;
-}
-
-static int slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
-{
- struct cxd *ci = ca->data;
-
- dev_dbg(&ci->i2c->dev, "%s\n", __func__);
- if (ci->cammode)
- read_data(ca, slot, ci->rbuf, 0);
- mutex_lock(&ci->lock);
- write_reg(ci, 0x00, 0x21);
- write_reg(ci, 0x06, 0x1F);
- msleep(300);
-
- write_regm(ci, 0x09, 0x08, 0x08);
- write_regm(ci, 0x20, 0x80, 0x80); /* Reset CAM Mode */
- write_regm(ci, 0x06, 0x07, 0x07); /* Clear IO Mode */
-
- ci->mode = -1;
- ci->write_busy = 0;
- mutex_unlock(&ci->lock);
- return 0;
-}
-
-static int slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
-{
- struct cxd *ci = ca->data;
-
- mutex_lock(&ci->lock);
- write_regm(ci, 0x09, 0x00, 0x08);
- set_mode(ci, 0);
- cam_mode(ci, 1);
- mutex_unlock(&ci->lock);
- return 0;
-}
-
-static int campoll(struct cxd *ci)
-{
- u8 istat;
-
- read_reg(ci, 0x04, &istat);
- if (!istat)
- return 0;
- write_reg(ci, 0x05, istat);
-
- if (istat & 0x40)
- ci->dr = 1;
- if (istat & 0x20)
- ci->write_busy = 0;
-
- if (istat & 2) {
- u8 slotstat;
-
- read_reg(ci, 0x01, &slotstat);
- if (!(2 & slotstat)) {
- if (!ci->slot_stat) {
- ci->slot_stat |=
- DVB_CA_EN50221_POLL_CAM_PRESENT;
- write_regm(ci, 0x03, 0x08, 0x08);
- }
-
- } else {
- if (ci->slot_stat) {
- ci->slot_stat = 0;
- write_regm(ci, 0x03, 0x00, 0x08);
- dev_info(&ci->i2c->dev, "NO CAM\n");
- ci->ready = 0;
- }
- }
- if ((istat & 8) &&
- ci->slot_stat == DVB_CA_EN50221_POLL_CAM_PRESENT) {
- ci->ready = 1;
- ci->slot_stat |= DVB_CA_EN50221_POLL_CAM_READY;
- }
- }
- return 0;
-}
-
-static int poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open)
-{
- struct cxd *ci = ca->data;
- u8 slotstat;
-
- mutex_lock(&ci->lock);
- campoll(ci);
- read_reg(ci, 0x01, &slotstat);
- mutex_unlock(&ci->lock);
-
- return ci->slot_stat;
-}
-
-static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount)
-{
- struct cxd *ci = ca->data;
- u8 msb, lsb;
- u16 len;
-
- mutex_lock(&ci->lock);
- campoll(ci);
- mutex_unlock(&ci->lock);
-
- if (!ci->dr)
- return 0;
-
- mutex_lock(&ci->lock);
- read_reg(ci, 0x0f, &msb);
- read_reg(ci, 0x10, &lsb);
- len = ((u16)msb << 8) | lsb;
- if (len > ecount || len < 2) {
- /* read it anyway or cxd may hang */
- read_block(ci, 0x12, ci->rbuf, len);
- mutex_unlock(&ci->lock);
- return -EIO;
- }
- read_block(ci, 0x12, ebuf, len);
- ci->dr = 0;
- mutex_unlock(&ci->lock);
- return len;
-}
-
-static int write_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount)
-{
- struct cxd *ci = ca->data;
-
- if (ci->write_busy)
- return -EAGAIN;
- mutex_lock(&ci->lock);
- write_reg(ci, 0x0d, ecount >> 8);
- write_reg(ci, 0x0e, ecount & 0xff);
- write_block(ci, 0x11, ebuf, ecount);
- ci->write_busy = 1;
- mutex_unlock(&ci->lock);
- return ecount;
-}
-
-static struct dvb_ca_en50221 en_templ = {
- .read_attribute_mem = read_attribute_mem,
- .write_attribute_mem = write_attribute_mem,
- .read_cam_control = read_cam_control,
- .write_cam_control = write_cam_control,
- .slot_reset = slot_reset,
- .slot_shutdown = slot_shutdown,
- .slot_ts_enable = slot_ts_enable,
- .poll_slot_status = poll_slot_status,
- .read_data = read_data,
- .write_data = write_data,
-};
-
-struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg,
- void *priv,
- struct i2c_adapter *i2c)
-{
- struct cxd *ci;
- u8 val;
-
- if (i2c_read_reg(i2c, cfg->adr, 0, &val) < 0) {
- dev_info(&i2c->dev, "No CXD2099AR detected at 0x%02x\n",
- cfg->adr);
- return NULL;
- }
-
- ci = kzalloc(sizeof(*ci), GFP_KERNEL);
- if (!ci)
- return NULL;
-
- mutex_init(&ci->lock);
- ci->cfg = *cfg;
- ci->i2c = i2c;
- ci->lastaddress = 0xff;
- ci->clk_reg_b = 0x4a;
- ci->clk_reg_f = 0x1b;
-
- ci->en = en_templ;
- ci->en.data = ci;
- init(ci);
- dev_info(&i2c->dev, "Attached CXD2099AR at 0x%02x\n", ci->cfg.adr);
-
- if (!buffermode) {
- ci->en.read_data = NULL;
- ci->en.write_data = NULL;
- } else {
- dev_info(&i2c->dev, "Using CXD2099AR buffer mode");
- }
-
- return &ci->en;
-}
-EXPORT_SYMBOL(cxd2099_attach);
-
-MODULE_DESCRIPTION("CXD2099AR Common Interface controller driver");
-MODULE_AUTHOR("Ralph Metzler");
-MODULE_LICENSE("GPL");
diff --git a/drivers/staging/media/cxd2099/cxd2099.h b/drivers/staging/media/cxd2099/cxd2099.h
deleted file mode 100644
index 253e3155a6df..000000000000
--- a/drivers/staging/media/cxd2099/cxd2099.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * cxd2099.h: Driver for the CXD2099AR Common Interface Controller
- *
- * Copyright (C) 2010-2011 Digital Devices GmbH
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 only, as published by the Free Software Foundation.
- *
- * 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.
- */
-
-#ifndef _CXD2099_H_
-#define _CXD2099_H_
-
-#include <media/dvb_ca_en50221.h>
-
-struct cxd2099_cfg {
- u32 bitrate;
- u8 adr;
- u8 polarity;
- u8 clock_mode;
-
- u32 max_i2c;
-};
-
-#if defined(CONFIG_DVB_CXD2099) || \
- (defined(CONFIG_DVB_CXD2099_MODULE) && defined(MODULE))
-struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg,
- void *priv, struct i2c_adapter *i2c);
-#else
-
-static inline struct
-dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg, void *priv,
- struct i2c_adapter *i2c)
-{
- dev_warn(&i2c->dev, "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif
-
-#endif
diff --git a/drivers/staging/media/davinci_vpfe/dm365_resizer.c b/drivers/staging/media/davinci_vpfe/dm365_resizer.c
index 857b0e847c5e..1ee216d71d42 100644
--- a/drivers/staging/media/davinci_vpfe/dm365_resizer.c
+++ b/drivers/staging/media/davinci_vpfe/dm365_resizer.c
@@ -480,7 +480,7 @@ resizer_configure_common_in_params(struct vpfe_resizer_device *resizer)
return 0;
}
static int
-resizer_configure_in_continious_mode(struct vpfe_resizer_device *resizer)
+resizer_configure_in_continuous_mode(struct vpfe_resizer_device *resizer)
{
struct device *dev = resizer->crop_resizer.subdev.v4l2_dev->dev;
struct resizer_params *param = &resizer->config;
@@ -1242,7 +1242,7 @@ static int resizer_do_hw_setup(struct vpfe_resizer_device *resizer)
ipipeif_source == IPIPEIF_OUTPUT_RESIZER)
ret = resizer_configure_in_single_shot_mode(resizer);
else
- ret = resizer_configure_in_continious_mode(resizer);
+ ret = resizer_configure_in_continuous_mode(resizer);
if (ret)
return ret;
ret = config_rsz_hw(resizer, param);
diff --git a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c
index bffe2153b910..634d38c4bea1 100644
--- a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c
+++ b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c
@@ -444,7 +444,7 @@ static int vpfe_register_entities(struct vpfe_device *vpfe_dev)
for (i = 0; i < vpfe_dev->num_ext_subdevs; i++)
/*
* if entity has no pads (ex: amplifier),
- * cant establish link
+ * can't establish link
*/
if (vpfe_dev->sd[i]->entity.num_pads) {
ret = media_create_pad_link(&vpfe_dev->sd[i]->entity,
diff --git a/drivers/staging/media/imx/Kconfig b/drivers/staging/media/imx/Kconfig
index 2be921cd0d55..bfc17de56b17 100644
--- a/drivers/staging/media/imx/Kconfig
+++ b/drivers/staging/media/imx/Kconfig
@@ -1,7 +1,9 @@
config VIDEO_IMX_MEDIA
tristate "i.MX5/6 V4L2 media core driver"
- depends on MEDIA_CONTROLLER && VIDEO_V4L2 && ARCH_MXC && IMX_IPUV3_CORE
+ depends on ARCH_MXC || COMPILE_TEST
+ depends on MEDIA_CONTROLLER && VIDEO_V4L2 && IMX_IPUV3_CORE
depends on VIDEO_V4L2_SUBDEV_API
+ depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_FWNODE
---help---
diff --git a/drivers/staging/media/imx/imx-ic-prp.c b/drivers/staging/media/imx/imx-ic-prp.c
index c6d7e80932ad..98923fc844ce 100644
--- a/drivers/staging/media/imx/imx-ic-prp.c
+++ b/drivers/staging/media/imx/imx-ic-prp.c
@@ -462,6 +462,7 @@ static int prp_registered(struct v4l2_subdev *sd)
}
static const struct v4l2_subdev_pad_ops prp_pad_ops = {
+ .init_cfg = imx_media_init_cfg,
.enum_mbus_code = prp_enum_mbus_code,
.get_fmt = prp_get_fmt,
.set_fmt = prp_set_fmt,
diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c
index 143038c6c403..ae453fd422f0 100644
--- a/drivers/staging/media/imx/imx-ic-prpencvf.c
+++ b/drivers/staging/media/imx/imx-ic-prpencvf.c
@@ -923,7 +923,7 @@ static int prp_enum_frame_size(struct v4l2_subdev *sd,
struct v4l2_subdev_frame_size_enum *fse)
{
struct prp_priv *priv = sd_to_priv(sd);
- struct v4l2_subdev_format format = {0};
+ struct v4l2_subdev_format format = {};
const struct imx_media_pixfmt *cc;
int ret = 0;
@@ -1253,6 +1253,7 @@ static void prp_unregistered(struct v4l2_subdev *sd)
}
static const struct v4l2_subdev_pad_ops prp_pad_ops = {
+ .init_cfg = imx_media_init_cfg,
.enum_mbus_code = prp_enum_mbus_code,
.enum_frame_size = prp_enum_frame_size,
.get_fmt = prp_get_fmt,
diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c
index 576bdc7e9c42..0ccabe04b0e1 100644
--- a/drivers/staging/media/imx/imx-media-capture.c
+++ b/drivers/staging/media/imx/imx-media-capture.c
@@ -141,7 +141,8 @@ static int capture_enum_frameintervals(struct file *file, void *fh,
fie.code = cc->codes[0];
- ret = v4l2_subdev_call(priv->src_sd, pad, enum_frame_interval, NULL, &fie);
+ ret = v4l2_subdev_call(priv->src_sd, pad, enum_frame_interval,
+ NULL, &fie);
if (ret)
return ret;
diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c
index eb7be5093a9d..16cab40156ca 100644
--- a/drivers/staging/media/imx/imx-media-csi.c
+++ b/drivers/staging/media/imx/imx-media-csi.c
@@ -400,6 +400,7 @@ static int csi_idmac_setup_channel(struct csi_priv *priv)
case V4L2_PIX_FMT_SGBRG8:
case V4L2_PIX_FMT_SGRBG8:
case V4L2_PIX_FMT_SRGGB8:
+ case V4L2_PIX_FMT_GREY:
burst_size = 16;
passthrough = true;
passthrough_bits = 8;
@@ -668,11 +669,10 @@ static int csi_setup(struct csi_priv *priv)
static int csi_start(struct csi_priv *priv)
{
- struct v4l2_fract *output_fi, *input_fi;
+ struct v4l2_fract *output_fi;
int ret;
output_fi = &priv->frame_interval[priv->active_output_pad];
- input_fi = &priv->frame_interval[CSI_SINK_PAD];
if (priv->dest == IPU_CSI_DEST_IDMAC) {
ret = csi_idmac_start(priv);
@@ -1005,7 +1005,7 @@ static int csi_link_validate(struct v4l2_subdev *sd,
struct v4l2_subdev_format *sink_fmt)
{
struct csi_priv *priv = v4l2_get_subdevdata(sd);
- struct v4l2_fwnode_endpoint upstream_ep;
+ struct v4l2_fwnode_endpoint upstream_ep = {};
const struct imx_media_pixfmt *incc;
bool is_csi2;
int ret;
@@ -1715,6 +1715,7 @@ static const struct v4l2_subdev_video_ops csi_video_ops = {
};
static const struct v4l2_subdev_pad_ops csi_pad_ops = {
+ .init_cfg = imx_media_init_cfg,
.enum_mbus_code = csi_enum_mbus_code,
.enum_frame_size = csi_enum_frame_size,
.enum_frame_interval = csi_enum_frame_interval,
@@ -1797,6 +1798,13 @@ static int imx_csi_probe(struct platform_device *pdev)
*/
priv->dev->of_node = pdata->of_node;
pinctrl = devm_pinctrl_get_select_default(priv->dev);
+ if (IS_ERR(pinctrl)) {
+ ret = PTR_ERR(priv->vdev);
+ dev_dbg(priv->dev,
+ "devm_pinctrl_get_select_default() failed: %d\n", ret);
+ if (ret != -ENODEV)
+ goto free;
+ }
ret = v4l2_async_register_subdev(&priv->sd);
if (ret)
diff --git a/drivers/staging/media/imx/imx-media-internal-sd.c b/drivers/staging/media/imx/imx-media-internal-sd.c
index 70833fe503b5..daf66c2d69ab 100644
--- a/drivers/staging/media/imx/imx-media-internal-sd.c
+++ b/drivers/staging/media/imx/imx-media-internal-sd.c
@@ -271,7 +271,7 @@ static int add_internal_subdev(struct imx_media_dev *imxmd,
int ipu_id)
{
struct imx_media_internal_sd_platformdata pdata;
- struct platform_device_info pdevinfo = {0};
+ struct platform_device_info pdevinfo = {};
struct platform_device *pdev;
pdata.grp_id = isd->id->grp_id;
diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c
index 13dafa77a2eb..fab98fc0d6a0 100644
--- a/drivers/staging/media/imx/imx-media-utils.c
+++ b/drivers/staging/media/imx/imx-media-utils.c
@@ -93,7 +93,7 @@ static const struct imx_media_pixfmt rgb_formats[] = {
.bpp = 32,
.ipufmt = true,
},
- /*** raw bayer formats start here ***/
+ /*** raw bayer and grayscale formats start here ***/
{
.fourcc = V4L2_PIX_FMT_SBGGR8,
.codes = {MEDIA_BUS_FMT_SBGGR8_1X8},
@@ -162,6 +162,12 @@ static const struct imx_media_pixfmt rgb_formats[] = {
.cs = IPUV3_COLORSPACE_RGB,
.bpp = 16,
.bayer = true,
+ }, {
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .codes = {MEDIA_BUS_FMT_Y8_1X8},
+ .cs = IPUV3_COLORSPACE_RGB,
+ .bpp = 8,
+ .bayer = true,
},
/***
* non-mbus RGB formats start here. NOTE! when adding non-mbus
@@ -219,58 +225,63 @@ static void init_mbus_colorimetry(struct v4l2_mbus_framefmt *mbus,
mbus->ycbcr_enc);
}
+static const
+struct imx_media_pixfmt *__find_format(u32 fourcc,
+ u32 code,
+ bool allow_non_mbus,
+ bool allow_bayer,
+ const struct imx_media_pixfmt *array,
+ u32 array_size)
+{
+ const struct imx_media_pixfmt *fmt;
+ int i, j;
+
+ for (i = 0; i < array_size; i++) {
+ fmt = &array[i];
+
+ if ((!allow_non_mbus && !fmt->codes[0]) ||
+ (!allow_bayer && fmt->bayer))
+ continue;
+
+ if (fourcc && fmt->fourcc == fourcc)
+ return fmt;
+
+ if (!code)
+ continue;
+
+ for (j = 0; fmt->codes[j]; j++) {
+ if (code == fmt->codes[j])
+ return fmt;
+ }
+ }
+ return NULL;
+}
+
static const struct imx_media_pixfmt *find_format(u32 fourcc,
u32 code,
enum codespace_sel cs_sel,
bool allow_non_mbus,
bool allow_bayer)
{
- const struct imx_media_pixfmt *array, *fmt, *ret = NULL;
- u32 array_size;
- int i, j;
+ const struct imx_media_pixfmt *ret;
switch (cs_sel) {
case CS_SEL_YUV:
- array_size = NUM_YUV_FORMATS;
- array = yuv_formats;
- break;
+ return __find_format(fourcc, code, allow_non_mbus, allow_bayer,
+ yuv_formats, NUM_YUV_FORMATS);
case CS_SEL_RGB:
- array_size = NUM_RGB_FORMATS;
- array = rgb_formats;
- break;
+ return __find_format(fourcc, code, allow_non_mbus, allow_bayer,
+ rgb_formats, NUM_RGB_FORMATS);
case CS_SEL_ANY:
- array_size = NUM_YUV_FORMATS + NUM_RGB_FORMATS;
- array = yuv_formats;
- break;
+ ret = __find_format(fourcc, code, allow_non_mbus, allow_bayer,
+ yuv_formats, NUM_YUV_FORMATS);
+ if (ret)
+ return ret;
+ return __find_format(fourcc, code, allow_non_mbus, allow_bayer,
+ rgb_formats, NUM_RGB_FORMATS);
default:
return NULL;
}
-
- for (i = 0; i < array_size; i++) {
- if (cs_sel == CS_SEL_ANY && i >= NUM_YUV_FORMATS)
- fmt = &rgb_formats[i - NUM_YUV_FORMATS];
- else
- fmt = &array[i];
-
- if ((!allow_non_mbus && fmt->codes[0] == 0) ||
- (!allow_bayer && fmt->bayer))
- continue;
-
- if (fourcc && fmt->fourcc == fourcc) {
- ret = fmt;
- goto out;
- }
-
- for (j = 0; code && fmt->codes[j]; j++) {
- if (code == fmt->codes[j]) {
- ret = fmt;
- goto out;
- }
- }
- }
-
-out:
- return ret;
}
static int enum_format(u32 *fourcc, u32 *code, u32 index,
@@ -465,6 +476,35 @@ int imx_media_init_mbus_fmt(struct v4l2_mbus_framefmt *mbus,
EXPORT_SYMBOL_GPL(imx_media_init_mbus_fmt);
/*
+ * Initializes the TRY format to the ACTIVE format on all pads
+ * of a subdev. Can be used as the .init_cfg pad operation.
+ */
+int imx_media_init_cfg(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg)
+{
+ struct v4l2_mbus_framefmt *mf_try;
+ struct v4l2_subdev_format format;
+ unsigned int pad;
+ int ret;
+
+ for (pad = 0; pad < sd->entity.num_pads; pad++) {
+ memset(&format, 0, sizeof(format));
+
+ format.pad = pad;
+ format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &format);
+ if (ret)
+ continue;
+
+ mf_try = v4l2_subdev_get_try_format(sd, cfg, pad);
+ *mf_try = format.format;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(imx_media_init_cfg);
+
+/*
* Check whether the field and colorimetry parameters in tryfmt are
* uninitialized, and if so fill them with the values from fmt,
* or if tryfmt->colorspace has been initialized, all the default
diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c
index 433474d58e3e..482250d47e7c 100644
--- a/drivers/staging/media/imx/imx-media-vdic.c
+++ b/drivers/staging/media/imx/imx-media-vdic.c
@@ -177,7 +177,7 @@ static int vdic_get_ipu_resources(struct vdic_priv *priv)
priv->vdi_in_ch = ch;
ch = ipu_idmac_get(priv->ipu, IPUV3_CHANNEL_MEM_VDI_NEXT);
- if (IS_ERR(priv->vdi_in_ch_n)) {
+ if (IS_ERR(ch)) {
err_chan = IPUV3_CHANNEL_MEM_VDI_NEXT;
ret = PTR_ERR(ch);
goto out_err_chan;
@@ -909,6 +909,7 @@ static void vdic_unregistered(struct v4l2_subdev *sd)
}
static const struct v4l2_subdev_pad_ops vdic_pad_ops = {
+ .init_cfg = imx_media_init_cfg,
.enum_mbus_code = vdic_enum_mbus_code,
.get_fmt = vdic_get_fmt,
.set_fmt = vdic_set_fmt,
diff --git a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h
index 2fd6dfdf37d6..e945e0ed6dd6 100644
--- a/drivers/staging/media/imx/imx-media.h
+++ b/drivers/staging/media/imx/imx-media.h
@@ -172,6 +172,8 @@ int imx_media_enum_ipu_format(u32 *code, u32 index, enum codespace_sel cs_sel);
int imx_media_init_mbus_fmt(struct v4l2_mbus_framefmt *mbus,
u32 width, u32 height, u32 code, u32 field,
const struct imx_media_pixfmt **cc);
+int imx_media_init_cfg(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg);
void imx_media_fill_default_mbus_fields(struct v4l2_mbus_framefmt *tryfmt,
struct v4l2_mbus_framefmt *fmt,
bool ic_route);
diff --git a/drivers/staging/media/imx/imx6-mipi-csi2.c b/drivers/staging/media/imx/imx6-mipi-csi2.c
index 477d191c568b..ceeeb3069a02 100644
--- a/drivers/staging/media/imx/imx6-mipi-csi2.c
+++ b/drivers/staging/media/imx/imx6-mipi-csi2.c
@@ -447,6 +447,16 @@ out:
return ret;
}
+static struct v4l2_mbus_framefmt *
+__csi2_get_fmt(struct csi2_dev *csi2, struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(&csi2->sd, cfg, pad);
+ else
+ return &csi2->format_mbus;
+}
+
static int csi2_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *sdformat)
@@ -456,11 +466,7 @@ static int csi2_get_fmt(struct v4l2_subdev *sd,
mutex_lock(&csi2->lock);
- if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY)
- fmt = v4l2_subdev_get_try_format(&csi2->sd, cfg,
- sdformat->pad);
- else
- fmt = &csi2->format_mbus;
+ fmt = __csi2_get_fmt(csi2, cfg, sdformat->pad, sdformat->which);
sdformat->format = *fmt;
@@ -474,6 +480,7 @@ static int csi2_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_format *sdformat)
{
struct csi2_dev *csi2 = sd_to_dev(sd);
+ struct v4l2_mbus_framefmt *fmt;
int ret = 0;
if (sdformat->pad >= CSI2_NUM_PADS)
@@ -490,10 +497,9 @@ static int csi2_set_fmt(struct v4l2_subdev *sd,
if (sdformat->pad != CSI2_SINK_PAD)
sdformat->format = csi2->format_mbus;
- if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY)
- cfg->try_fmt = sdformat->format;
- else
- csi2->format_mbus = sdformat->format;
+ fmt = __csi2_get_fmt(csi2, cfg, sdformat->pad, sdformat->which);
+
+ *fmt = sdformat->format;
out:
mutex_unlock(&csi2->lock);
return ret;
@@ -531,6 +537,7 @@ static const struct v4l2_subdev_video_ops csi2_video_ops = {
};
static const struct v4l2_subdev_pad_ops csi2_pad_ops = {
+ .init_cfg = imx_media_init_cfg,
.get_fmt = csi2_get_fmt,
.set_fmt = csi2_set_fmt,
};
diff --git a/drivers/staging/media/imx074/Kconfig b/drivers/staging/media/imx074/Kconfig
new file mode 100644
index 000000000000..229cbeea580b
--- /dev/null
+++ b/drivers/staging/media/imx074/Kconfig
@@ -0,0 +1,5 @@
+config SOC_CAMERA_IMX074
+ tristate "imx074 support (DEPRECATED)"
+ depends on SOC_CAMERA && I2C
+ help
+ This driver supports IMX074 cameras from Sony
diff --git a/drivers/staging/media/imx074/Makefile b/drivers/staging/media/imx074/Makefile
new file mode 100644
index 000000000000..7d183574aa84
--- /dev/null
+++ b/drivers/staging/media/imx074/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o
diff --git a/drivers/staging/media/imx074/TODO b/drivers/staging/media/imx074/TODO
new file mode 100644
index 000000000000..15580a4f950c
--- /dev/null
+++ b/drivers/staging/media/imx074/TODO
@@ -0,0 +1,5 @@
+This sensor driver needs to be converted to a regular
+v4l2 subdev driver. The soc_camera framework is deprecated and
+will be removed in the future. Unless someone does this work this
+sensor driver will be deleted when the soc_camera framework is
+deleted.
diff --git a/drivers/media/i2c/soc_camera/imx074.c b/drivers/staging/media/imx074/imx074.c
index 77f1e0243d6e..77f1e0243d6e 100644
--- a/drivers/media/i2c/soc_camera/imx074.c
+++ b/drivers/staging/media/imx074/imx074.c
diff --git a/drivers/staging/media/mt9t031/Kconfig b/drivers/staging/media/mt9t031/Kconfig
new file mode 100644
index 000000000000..f48e06a03cdb
--- /dev/null
+++ b/drivers/staging/media/mt9t031/Kconfig
@@ -0,0 +1,11 @@
+config SOC_CAMERA_IMX074
+ tristate "imx074 support (DEPRECATED)"
+ depends on SOC_CAMERA && I2C
+ help
+ This driver supports IMX074 cameras from Sony
+
+config SOC_CAMERA_MT9T031
+ tristate "mt9t031 support (DEPRECATED)"
+ depends on SOC_CAMERA && I2C
+ help
+ This driver supports MT9T031 cameras from Micron.
diff --git a/drivers/staging/media/mt9t031/Makefile b/drivers/staging/media/mt9t031/Makefile
new file mode 100644
index 000000000000..bfd24c442b33
--- /dev/null
+++ b/drivers/staging/media/mt9t031/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o
diff --git a/drivers/staging/media/mt9t031/TODO b/drivers/staging/media/mt9t031/TODO
new file mode 100644
index 000000000000..15580a4f950c
--- /dev/null
+++ b/drivers/staging/media/mt9t031/TODO
@@ -0,0 +1,5 @@
+This sensor driver needs to be converted to a regular
+v4l2 subdev driver. The soc_camera framework is deprecated and
+will be removed in the future. Unless someone does this work this
+sensor driver will be deleted when the soc_camera framework is
+deleted.
diff --git a/drivers/media/i2c/soc_camera/mt9t031.c b/drivers/staging/media/mt9t031/mt9t031.c
index 4802d30e47de..4802d30e47de 100644
--- a/drivers/media/i2c/soc_camera/mt9t031.c
+++ b/drivers/staging/media/mt9t031/mt9t031.c
diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c
index 3dda8d81bf0b..8f2833526f7f 100644
--- a/drivers/staging/most/core.c
+++ b/drivers/staging/most/core.c
@@ -583,6 +583,7 @@ static ssize_t components_show(struct device_driver *drv, char *buf)
}
return offs;
}
+
/**
* split_string - parses buf and extracts ':' separated substrings.
*
@@ -915,7 +916,6 @@ static void arm_mbo(struct mbo *mbo)
unsigned long flags;
struct most_channel *c;
- BUG_ON((!mbo) || (!mbo->context));
c = mbo->context;
if (c->is_poisoned) {
@@ -1018,8 +1018,6 @@ static void most_write_completion(struct mbo *mbo)
{
struct most_channel *c;
- BUG_ON((!mbo) || (!mbo->context));
-
c = mbo->context;
if (mbo->status == MBO_E_INVAL)
pr_info("WARN: Tx MBO status: invalid\n");
@@ -1202,7 +1200,6 @@ int most_start_channel(struct most_interface *iface, int id,
num_buffer = arm_mbo_chain(c, c->cfg.direction,
most_write_completion);
if (unlikely(!num_buffer)) {
- pr_info("failed to allocate memory\n");
ret = -ENOMEM;
goto error;
}
@@ -1381,7 +1378,6 @@ int most_register_interface(struct most_interface *iface)
iface->p = kzalloc(sizeof(*iface->p), GFP_KERNEL);
if (!iface->p) {
- pr_info("Failed to allocate interface instance\n");
ida_simple_remove(&mdev_id, id);
return -ENOMEM;
}
@@ -1474,7 +1470,8 @@ void most_deregister_interface(struct most_interface *iface)
int i;
struct most_channel *c;
- pr_info("deregistering device %s (%s)\n", dev_name(&iface->dev), iface->description);
+ pr_info("deregistering device %s (%s)\n", dev_name(&iface->dev),
+ iface->description);
for (i = 0; i < iface->num_channels; i++) {
c = iface->p->channel[i];
if (c->pipe0.comp)
diff --git a/drivers/staging/most/core.h b/drivers/staging/most/core.h
index 74a29163b68a..884bd71fafce 100644
--- a/drivers/staging/most/core.h
+++ b/drivers/staging/most/core.h
@@ -184,7 +184,7 @@ struct mbo {
u16 buffer_length;
u16 processed_length;
enum mbo_status_flags status;
- void (*complete)(struct mbo *);
+ void (*complete)(struct mbo *mbo);
};
/**
diff --git a/drivers/staging/mt29f_spinand/mt29f_spinand.c b/drivers/staging/mt29f_spinand/mt29f_spinand.c
index 264ad362d858..448478451c4c 100644
--- a/drivers/staging/mt29f_spinand/mt29f_spinand.c
+++ b/drivers/staging/mt29f_spinand/mt29f_spinand.c
@@ -316,6 +316,7 @@ static int spinand_read_page_to_cache(struct spi_device *spi_nand, u16 page_id)
row = page_id;
cmd.cmd = CMD_READ;
cmd.n_addr = 3;
+ cmd.addr[0] = (u8)((row & 0xff0000) >> 16);
cmd.addr[1] = (u8)((row & 0xff00) >> 8);
cmd.addr[2] = (u8)(row & 0x00ff);
@@ -464,6 +465,7 @@ static int spinand_program_execute(struct spi_device *spi_nand, u16 page_id)
row = page_id;
cmd.cmd = CMD_PROG_PAGE_EXC;
cmd.n_addr = 3;
+ cmd.addr[0] = (u8)((row & 0xff0000) >> 16);
cmd.addr[1] = (u8)((row & 0xff00) >> 8);
cmd.addr[2] = (u8)(row & 0x00ff);
@@ -579,6 +581,7 @@ static int spinand_erase_block_erase(struct spi_device *spi_nand, u16 block_id)
row = block_id;
cmd.cmd = CMD_ERASE_BLK;
cmd.n_addr = 3;
+ cmd.addr[0] = (u8)((row & 0xff0000) >> 16);
cmd.addr[1] = (u8)((row & 0xff00) >> 8);
cmd.addr[2] = (u8)(row & 0x00ff);
@@ -918,8 +921,8 @@ static int spinand_probe(struct spi_device *spi_nand)
chip->waitfunc = spinand_wait;
chip->options |= NAND_CACHEPRG;
chip->select_chip = spinand_select_chip;
- chip->onfi_set_features = nand_onfi_get_set_features_notsupp;
- chip->onfi_get_features = nand_onfi_get_set_features_notsupp;
+ chip->set_features = nand_get_set_features_notsupp;
+ chip->get_features = nand_get_set_features_notsupp;
mtd = nand_to_mtd(chip);
diff --git a/drivers/staging/mt7621-dma/Kconfig b/drivers/staging/mt7621-dma/Kconfig
new file mode 100644
index 000000000000..2423c40099d1
--- /dev/null
+++ b/drivers/staging/mt7621-dma/Kconfig
@@ -0,0 +1,12 @@
+config DMA_RALINK
+ tristate "RALINK DMA support"
+ depends on RALINK && !SOC_RT288X
+ select DMA_ENGINE
+ select DMA_VIRTUAL_CHANNELS
+
+config MTK_HSDMA
+ tristate "MTK HSDMA support"
+ depends on RALINK && SOC_MT7621
+ select DMA_ENGINE
+ select DMA_VIRTUAL_CHANNELS
+
diff --git a/drivers/staging/mt7621-dma/Makefile b/drivers/staging/mt7621-dma/Makefile
new file mode 100644
index 000000000000..d3152d45cf45
--- /dev/null
+++ b/drivers/staging/mt7621-dma/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_DMA_RALINK) += ralink-gdma.o
+obj-$(CONFIG_MTK_HSDMA) += mtk-hsdma.o
+
+ccflags-y += -I$(srctree)/drivers/dma
diff --git a/drivers/staging/mt7621-dma/TODO b/drivers/staging/mt7621-dma/TODO
new file mode 100644
index 000000000000..fdbc5002c32a
--- /dev/null
+++ b/drivers/staging/mt7621-dma/TODO
@@ -0,0 +1,5 @@
+
+- general code review and clean up
+- ensure device-tree requirements are documented
+
+Cc: NeilBrown <neil@brown.name>
diff --git a/drivers/staging/mt7621-dma/mtk-hsdma.c b/drivers/staging/mt7621-dma/mtk-hsdma.c
new file mode 100644
index 000000000000..df6ebf41bdea
--- /dev/null
+++ b/drivers/staging/mt7621-dma/mtk-hsdma.c
@@ -0,0 +1,768 @@
+/*
+ * Copyright (C) 2015, Michael Lee <igvtee@gmail.com>
+ * MTK HSDMA support
+ *
+ * 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/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/irq.h>
+#include <linux/of_dma.h>
+#include <linux/reset.h>
+#include <linux/of_device.h>
+
+#include "virt-dma.h"
+
+#define HSDMA_BASE_OFFSET 0x800
+
+#define HSDMA_REG_TX_BASE 0x00
+#define HSDMA_REG_TX_CNT 0x04
+#define HSDMA_REG_TX_CTX 0x08
+#define HSDMA_REG_TX_DTX 0x0c
+#define HSDMA_REG_RX_BASE 0x100
+#define HSDMA_REG_RX_CNT 0x104
+#define HSDMA_REG_RX_CRX 0x108
+#define HSDMA_REG_RX_DRX 0x10c
+#define HSDMA_REG_INFO 0x200
+#define HSDMA_REG_GLO_CFG 0x204
+#define HSDMA_REG_RST_CFG 0x208
+#define HSDMA_REG_DELAY_INT 0x20c
+#define HSDMA_REG_FREEQ_THRES 0x210
+#define HSDMA_REG_INT_STATUS 0x220
+#define HSDMA_REG_INT_MASK 0x228
+#define HSDMA_REG_SCH_Q01 0x280
+#define HSDMA_REG_SCH_Q23 0x284
+
+#define HSDMA_DESCS_MAX 0xfff
+#define HSDMA_DESCS_NUM 8
+#define HSDMA_DESCS_MASK (HSDMA_DESCS_NUM - 1)
+#define HSDMA_NEXT_DESC(x) (((x) + 1) & HSDMA_DESCS_MASK)
+
+/* HSDMA_REG_INFO */
+#define HSDMA_INFO_INDEX_MASK 0xf
+#define HSDMA_INFO_INDEX_SHIFT 24
+#define HSDMA_INFO_BASE_MASK 0xff
+#define HSDMA_INFO_BASE_SHIFT 16
+#define HSDMA_INFO_RX_MASK 0xff
+#define HSDMA_INFO_RX_SHIFT 8
+#define HSDMA_INFO_TX_MASK 0xff
+#define HSDMA_INFO_TX_SHIFT 0
+
+/* HSDMA_REG_GLO_CFG */
+#define HSDMA_GLO_TX_2B_OFFSET BIT(31)
+#define HSDMA_GLO_CLK_GATE BIT(30)
+#define HSDMA_GLO_BYTE_SWAP BIT(29)
+#define HSDMA_GLO_MULTI_DMA BIT(10)
+#define HSDMA_GLO_TWO_BUF BIT(9)
+#define HSDMA_GLO_32B_DESC BIT(8)
+#define HSDMA_GLO_BIG_ENDIAN BIT(7)
+#define HSDMA_GLO_TX_DONE BIT(6)
+#define HSDMA_GLO_BT_MASK 0x3
+#define HSDMA_GLO_BT_SHIFT 4
+#define HSDMA_GLO_RX_BUSY BIT(3)
+#define HSDMA_GLO_RX_DMA BIT(2)
+#define HSDMA_GLO_TX_BUSY BIT(1)
+#define HSDMA_GLO_TX_DMA BIT(0)
+
+#define HSDMA_BT_SIZE_16BYTES (0 << HSDMA_GLO_BT_SHIFT)
+#define HSDMA_BT_SIZE_32BYTES (1 << HSDMA_GLO_BT_SHIFT)
+#define HSDMA_BT_SIZE_64BYTES (2 << HSDMA_GLO_BT_SHIFT)
+#define HSDMA_BT_SIZE_128BYTES (3 << HSDMA_GLO_BT_SHIFT)
+
+#define HSDMA_GLO_DEFAULT (HSDMA_GLO_MULTI_DMA | \
+ HSDMA_GLO_RX_DMA | HSDMA_GLO_TX_DMA | HSDMA_BT_SIZE_32BYTES)
+
+/* HSDMA_REG_RST_CFG */
+#define HSDMA_RST_RX_SHIFT 16
+#define HSDMA_RST_TX_SHIFT 0
+
+/* HSDMA_REG_DELAY_INT */
+#define HSDMA_DELAY_INT_EN BIT(15)
+#define HSDMA_DELAY_PEND_OFFSET 8
+#define HSDMA_DELAY_TIME_OFFSET 0
+#define HSDMA_DELAY_TX_OFFSET 16
+#define HSDMA_DELAY_RX_OFFSET 0
+
+#define HSDMA_DELAY_INIT(x) (HSDMA_DELAY_INT_EN | \
+ ((x) << HSDMA_DELAY_PEND_OFFSET))
+#define HSDMA_DELAY(x) ((HSDMA_DELAY_INIT(x) << \
+ HSDMA_DELAY_TX_OFFSET) | HSDMA_DELAY_INIT(x))
+
+/* HSDMA_REG_INT_STATUS */
+#define HSDMA_INT_DELAY_RX_COH BIT(31)
+#define HSDMA_INT_DELAY_RX_INT BIT(30)
+#define HSDMA_INT_DELAY_TX_COH BIT(29)
+#define HSDMA_INT_DELAY_TX_INT BIT(28)
+#define HSDMA_INT_RX_MASK 0x3
+#define HSDMA_INT_RX_SHIFT 16
+#define HSDMA_INT_RX_Q0 BIT(16)
+#define HSDMA_INT_TX_MASK 0xf
+#define HSDMA_INT_TX_SHIFT 0
+#define HSDMA_INT_TX_Q0 BIT(0)
+
+/* tx/rx dma desc flags */
+#define HSDMA_PLEN_MASK 0x3fff
+#define HSDMA_DESC_DONE BIT(31)
+#define HSDMA_DESC_LS0 BIT(30)
+#define HSDMA_DESC_PLEN0(_x) (((_x) & HSDMA_PLEN_MASK) << 16)
+#define HSDMA_DESC_TAG BIT(15)
+#define HSDMA_DESC_LS1 BIT(14)
+#define HSDMA_DESC_PLEN1(_x) ((_x) & HSDMA_PLEN_MASK)
+
+/* align 4 bytes */
+#define HSDMA_ALIGN_SIZE 3
+/* align size 128bytes */
+#define HSDMA_MAX_PLEN 0x3f80
+
+struct hsdma_desc {
+ u32 addr0;
+ u32 flags;
+ u32 addr1;
+ u32 unused;
+};
+
+struct mtk_hsdma_sg {
+ dma_addr_t src_addr;
+ dma_addr_t dst_addr;
+ u32 len;
+};
+
+struct mtk_hsdma_desc {
+ struct virt_dma_desc vdesc;
+ unsigned int num_sgs;
+ struct mtk_hsdma_sg sg[1];
+};
+
+struct mtk_hsdma_chan {
+ struct virt_dma_chan vchan;
+ unsigned int id;
+ dma_addr_t desc_addr;
+ int tx_idx;
+ int rx_idx;
+ struct hsdma_desc *tx_ring;
+ struct hsdma_desc *rx_ring;
+ struct mtk_hsdma_desc *desc;
+ unsigned int next_sg;
+};
+
+struct mtk_hsdam_engine {
+ struct dma_device ddev;
+ struct device_dma_parameters dma_parms;
+ void __iomem *base;
+ struct tasklet_struct task;
+ volatile unsigned long chan_issued;
+
+ struct mtk_hsdma_chan chan[1];
+};
+
+static inline struct mtk_hsdam_engine *mtk_hsdma_chan_get_dev(
+ struct mtk_hsdma_chan *chan)
+{
+ return container_of(chan->vchan.chan.device, struct mtk_hsdam_engine,
+ ddev);
+}
+
+static inline struct mtk_hsdma_chan *to_mtk_hsdma_chan(struct dma_chan *c)
+{
+ return container_of(c, struct mtk_hsdma_chan, vchan.chan);
+}
+
+static inline struct mtk_hsdma_desc *to_mtk_hsdma_desc(
+ struct virt_dma_desc *vdesc)
+{
+ return container_of(vdesc, struct mtk_hsdma_desc, vdesc);
+}
+
+static inline u32 mtk_hsdma_read(struct mtk_hsdam_engine *hsdma, u32 reg)
+{
+ return readl(hsdma->base + reg);
+}
+
+static inline void mtk_hsdma_write(struct mtk_hsdam_engine *hsdma,
+ unsigned reg, u32 val)
+{
+ writel(val, hsdma->base + reg);
+}
+
+static void mtk_hsdma_reset_chan(struct mtk_hsdam_engine *hsdma,
+ struct mtk_hsdma_chan *chan)
+{
+ chan->tx_idx = 0;
+ chan->rx_idx = HSDMA_DESCS_NUM - 1;
+
+ mtk_hsdma_write(hsdma, HSDMA_REG_TX_CTX, chan->tx_idx);
+ mtk_hsdma_write(hsdma, HSDMA_REG_RX_CRX, chan->rx_idx);
+
+ mtk_hsdma_write(hsdma, HSDMA_REG_RST_CFG,
+ 0x1 << (chan->id + HSDMA_RST_TX_SHIFT));
+ mtk_hsdma_write(hsdma, HSDMA_REG_RST_CFG,
+ 0x1 << (chan->id + HSDMA_RST_RX_SHIFT));
+}
+
+static void hsdma_dump_reg(struct mtk_hsdam_engine *hsdma)
+{
+ dev_dbg(hsdma->ddev.dev, "tbase %08x, tcnt %08x, " \
+ "tctx %08x, tdtx: %08x, rbase %08x, " \
+ "rcnt %08x, rctx %08x, rdtx %08x\n",
+ mtk_hsdma_read(hsdma, HSDMA_REG_TX_BASE),
+ mtk_hsdma_read(hsdma, HSDMA_REG_TX_CNT),
+ mtk_hsdma_read(hsdma, HSDMA_REG_TX_CTX),
+ mtk_hsdma_read(hsdma, HSDMA_REG_TX_DTX),
+ mtk_hsdma_read(hsdma, HSDMA_REG_RX_BASE),
+ mtk_hsdma_read(hsdma, HSDMA_REG_RX_CNT),
+ mtk_hsdma_read(hsdma, HSDMA_REG_RX_CRX),
+ mtk_hsdma_read(hsdma, HSDMA_REG_RX_DRX));
+
+ dev_dbg(hsdma->ddev.dev, "info %08x, glo %08x, delay %08x, " \
+ "intr_stat %08x, intr_mask %08x\n",
+ mtk_hsdma_read(hsdma, HSDMA_REG_INFO),
+ mtk_hsdma_read(hsdma, HSDMA_REG_GLO_CFG),
+ mtk_hsdma_read(hsdma, HSDMA_REG_DELAY_INT),
+ mtk_hsdma_read(hsdma, HSDMA_REG_INT_STATUS),
+ mtk_hsdma_read(hsdma, HSDMA_REG_INT_MASK));
+}
+
+static void hsdma_dump_desc(struct mtk_hsdam_engine *hsdma,
+ struct mtk_hsdma_chan *chan)
+{
+ struct hsdma_desc *tx_desc;
+ struct hsdma_desc *rx_desc;
+ int i;
+
+ dev_dbg(hsdma->ddev.dev, "tx idx: %d, rx idx: %d\n",
+ chan->tx_idx, chan->rx_idx);
+
+ for (i = 0; i < HSDMA_DESCS_NUM; i++) {
+ tx_desc = &chan->tx_ring[i];
+ rx_desc = &chan->rx_ring[i];
+
+ dev_dbg(hsdma->ddev.dev, "%d tx addr0: %08x, flags %08x, " \
+ "tx addr1: %08x, rx addr0 %08x, flags %08x\n",
+ i, tx_desc->addr0, tx_desc->flags, \
+ tx_desc->addr1, rx_desc->addr0, rx_desc->flags);
+ }
+}
+
+static void mtk_hsdma_reset(struct mtk_hsdam_engine *hsdma,
+ struct mtk_hsdma_chan *chan)
+{
+ int i;
+
+ /* disable dma */
+ mtk_hsdma_write(hsdma, HSDMA_REG_GLO_CFG, 0);
+
+ /* disable intr */
+ mtk_hsdma_write(hsdma, HSDMA_REG_INT_MASK, 0);
+
+ /* init desc value */
+ for (i = 0; i < HSDMA_DESCS_NUM; i++) {
+ chan->tx_ring[i].addr0 = 0;
+ chan->tx_ring[i].flags = HSDMA_DESC_LS0 |
+ HSDMA_DESC_DONE;
+ }
+ for (i = 0; i < HSDMA_DESCS_NUM; i++) {
+ chan->rx_ring[i].addr0 = 0;
+ chan->rx_ring[i].flags = 0;
+ }
+
+ /* reset */
+ mtk_hsdma_reset_chan(hsdma, chan);
+
+ /* enable intr */
+ mtk_hsdma_write(hsdma, HSDMA_REG_INT_MASK, HSDMA_INT_RX_Q0);
+
+ /* enable dma */
+ mtk_hsdma_write(hsdma, HSDMA_REG_GLO_CFG, HSDMA_GLO_DEFAULT);
+}
+
+static int mtk_hsdma_terminate_all(struct dma_chan *c)
+{
+ struct mtk_hsdma_chan *chan = to_mtk_hsdma_chan(c);
+ struct mtk_hsdam_engine *hsdma = mtk_hsdma_chan_get_dev(chan);
+ unsigned long timeout;
+ LIST_HEAD(head);
+
+ spin_lock_bh(&chan->vchan.lock);
+ chan->desc = NULL;
+ clear_bit(chan->id, &hsdma->chan_issued);
+ vchan_get_all_descriptors(&chan->vchan, &head);
+ spin_unlock_bh(&chan->vchan.lock);
+
+ vchan_dma_desc_free_list(&chan->vchan, &head);
+
+ /* wait dma transfer complete */
+ timeout = jiffies + msecs_to_jiffies(2000);
+ while (mtk_hsdma_read(hsdma, HSDMA_REG_GLO_CFG) &
+ (HSDMA_GLO_RX_BUSY | HSDMA_GLO_TX_BUSY)) {
+ if (time_after_eq(jiffies, timeout)) {
+ hsdma_dump_desc(hsdma, chan);
+ mtk_hsdma_reset(hsdma, chan);
+ dev_err(hsdma->ddev.dev, "timeout, reset it\n");
+ break;
+ }
+ cpu_relax();
+ }
+
+ return 0;
+}
+
+static int mtk_hsdma_start_transfer(struct mtk_hsdam_engine *hsdma,
+ struct mtk_hsdma_chan *chan)
+{
+ dma_addr_t src, dst;
+ size_t len, tlen;
+ struct hsdma_desc *tx_desc, *rx_desc;
+ struct mtk_hsdma_sg *sg;
+ unsigned int i;
+ int rx_idx;
+
+ sg = &chan->desc->sg[0];
+ len = sg->len;
+ chan->desc->num_sgs = DIV_ROUND_UP(len, HSDMA_MAX_PLEN);
+
+ /* tx desc */
+ src = sg->src_addr;
+ for (i = 0; i < chan->desc->num_sgs; i++) {
+ if (len > HSDMA_MAX_PLEN)
+ tlen = HSDMA_MAX_PLEN;
+ else
+ tlen = len;
+
+ if (i & 0x1) {
+ tx_desc->addr1 = src;
+ tx_desc->flags |= HSDMA_DESC_PLEN1(tlen);
+ } else {
+ tx_desc = &chan->tx_ring[chan->tx_idx];
+ tx_desc->addr0 = src;
+ tx_desc->flags = HSDMA_DESC_PLEN0(tlen);
+
+ /* update index */
+ chan->tx_idx = HSDMA_NEXT_DESC(chan->tx_idx);
+ }
+
+ src += tlen;
+ len -= tlen;
+ }
+ if (i & 0x1)
+ tx_desc->flags |= HSDMA_DESC_LS0;
+ else
+ tx_desc->flags |= HSDMA_DESC_LS1;
+
+ /* rx desc */
+ rx_idx = HSDMA_NEXT_DESC(chan->rx_idx);
+ len = sg->len;
+ dst = sg->dst_addr;
+ for (i = 0; i < chan->desc->num_sgs; i++) {
+ rx_desc = &chan->rx_ring[rx_idx];
+ if (len > HSDMA_MAX_PLEN)
+ tlen = HSDMA_MAX_PLEN;
+ else
+ tlen = len;
+
+ rx_desc->addr0 = dst;
+ rx_desc->flags = HSDMA_DESC_PLEN0(tlen);
+
+ dst += tlen;
+ len -= tlen;
+
+ /* update index */
+ rx_idx = HSDMA_NEXT_DESC(rx_idx);
+ }
+
+ /* make sure desc and index all up to date */
+ wmb();
+ mtk_hsdma_write(hsdma, HSDMA_REG_TX_CTX, chan->tx_idx);
+
+ return 0;
+}
+
+static int gdma_next_desc(struct mtk_hsdma_chan *chan)
+{
+ struct virt_dma_desc *vdesc;
+
+ vdesc = vchan_next_desc(&chan->vchan);
+ if (!vdesc) {
+ chan->desc = NULL;
+ return 0;
+ }
+ chan->desc = to_mtk_hsdma_desc(vdesc);
+ chan->next_sg = 0;
+
+ return 1;
+}
+
+static void mtk_hsdma_chan_done(struct mtk_hsdam_engine *hsdma,
+ struct mtk_hsdma_chan *chan)
+{
+ struct mtk_hsdma_desc *desc;
+ int chan_issued;
+
+ chan_issued = 0;
+ spin_lock_bh(&chan->vchan.lock);
+ desc = chan->desc;
+ if (likely(desc)) {
+ if (chan->next_sg == desc->num_sgs) {
+ list_del(&desc->vdesc.node);
+ vchan_cookie_complete(&desc->vdesc);
+ chan_issued = gdma_next_desc(chan);
+ }
+ } else
+ dev_dbg(hsdma->ddev.dev, "no desc to complete\n");
+
+ if (chan_issued)
+ set_bit(chan->id, &hsdma->chan_issued);
+ spin_unlock_bh(&chan->vchan.lock);
+}
+
+static irqreturn_t mtk_hsdma_irq(int irq, void *devid)
+{
+ struct mtk_hsdam_engine *hsdma = devid;
+ u32 status;
+
+ status = mtk_hsdma_read(hsdma, HSDMA_REG_INT_STATUS);
+ if (unlikely(!status))
+ return IRQ_NONE;
+
+ if (likely(status & HSDMA_INT_RX_Q0))
+ tasklet_schedule(&hsdma->task);
+ else
+ dev_dbg(hsdma->ddev.dev, "unhandle irq status %08x\n",
+ status);
+ /* clean intr bits */
+ mtk_hsdma_write(hsdma, HSDMA_REG_INT_STATUS, status);
+
+ return IRQ_HANDLED;
+}
+
+static void mtk_hsdma_issue_pending(struct dma_chan *c)
+{
+ struct mtk_hsdma_chan *chan = to_mtk_hsdma_chan(c);
+ struct mtk_hsdam_engine *hsdma = mtk_hsdma_chan_get_dev(chan);
+
+ spin_lock_bh(&chan->vchan.lock);
+ if (vchan_issue_pending(&chan->vchan) && !chan->desc) {
+ if (gdma_next_desc(chan)) {
+ set_bit(chan->id, &hsdma->chan_issued);
+ tasklet_schedule(&hsdma->task);
+ } else
+ dev_dbg(hsdma->ddev.dev, "no desc to issue\n");
+ }
+ spin_unlock_bh(&chan->vchan.lock);
+}
+
+static struct dma_async_tx_descriptor *mtk_hsdma_prep_dma_memcpy(
+ struct dma_chan *c, dma_addr_t dest, dma_addr_t src,
+ size_t len, unsigned long flags)
+{
+ struct mtk_hsdma_chan *chan = to_mtk_hsdma_chan(c);
+ struct mtk_hsdma_desc *desc;
+
+ if (len <= 0)
+ return NULL;
+
+ desc = kzalloc(sizeof(struct mtk_hsdma_desc), GFP_ATOMIC);
+ if (!desc) {
+ dev_err(c->device->dev, "alloc memcpy decs error\n");
+ return NULL;
+ }
+
+ desc->sg[0].src_addr = src;
+ desc->sg[0].dst_addr = dest;
+ desc->sg[0].len = len;
+
+ return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
+}
+
+static enum dma_status mtk_hsdma_tx_status(struct dma_chan *c,
+ dma_cookie_t cookie,
+ struct dma_tx_state *state)
+{
+ return dma_cookie_status(c, cookie, state);
+}
+
+static void mtk_hsdma_free_chan_resources(struct dma_chan *c)
+{
+ vchan_free_chan_resources(to_virt_chan(c));
+}
+
+static void mtk_hsdma_desc_free(struct virt_dma_desc *vdesc)
+{
+ kfree(container_of(vdesc, struct mtk_hsdma_desc, vdesc));
+}
+
+static void mtk_hsdma_tx(struct mtk_hsdam_engine *hsdma)
+{
+ struct mtk_hsdma_chan *chan;
+
+ if (test_and_clear_bit(0, &hsdma->chan_issued)) {
+ chan = &hsdma->chan[0];
+ if (chan->desc)
+ mtk_hsdma_start_transfer(hsdma, chan);
+ else
+ dev_dbg(hsdma->ddev.dev, "chan 0 no desc to issue\n");
+ }
+}
+
+static void mtk_hsdma_rx(struct mtk_hsdam_engine *hsdma)
+{
+ struct mtk_hsdma_chan *chan;
+ int next_idx, drx_idx, cnt;
+
+ chan = &hsdma->chan[0];
+ next_idx = HSDMA_NEXT_DESC(chan->rx_idx);
+ drx_idx = mtk_hsdma_read(hsdma, HSDMA_REG_RX_DRX);
+
+ cnt = (drx_idx - next_idx) & HSDMA_DESCS_MASK;
+ if (!cnt)
+ return;
+
+ chan->next_sg += cnt;
+ chan->rx_idx = (chan->rx_idx + cnt) & HSDMA_DESCS_MASK;
+
+ /* update rx crx */
+ wmb();
+ mtk_hsdma_write(hsdma, HSDMA_REG_RX_CRX, chan->rx_idx);
+
+ mtk_hsdma_chan_done(hsdma, chan);
+}
+
+static void mtk_hsdma_tasklet(unsigned long arg)
+{
+ struct mtk_hsdam_engine *hsdma = (struct mtk_hsdam_engine *)arg;
+
+ mtk_hsdma_rx(hsdma);
+ mtk_hsdma_tx(hsdma);
+}
+
+static int mtk_hsdam_alloc_desc(struct mtk_hsdam_engine *hsdma,
+ struct mtk_hsdma_chan *chan)
+{
+ int i;
+
+ chan->tx_ring = dma_alloc_coherent(hsdma->ddev.dev,
+ 2 * HSDMA_DESCS_NUM * sizeof(*chan->tx_ring),
+ &chan->desc_addr, GFP_ATOMIC | __GFP_ZERO);
+ if (!chan->tx_ring)
+ goto no_mem;
+
+ chan->rx_ring = &chan->tx_ring[HSDMA_DESCS_NUM];
+
+ /* init tx ring value */
+ for (i = 0; i < HSDMA_DESCS_NUM; i++)
+ chan->tx_ring[i].flags = HSDMA_DESC_LS0 | HSDMA_DESC_DONE;
+
+ return 0;
+no_mem:
+ return -ENOMEM;
+}
+
+static void mtk_hsdam_free_desc(struct mtk_hsdam_engine *hsdma,
+ struct mtk_hsdma_chan *chan)
+{
+ if (chan->tx_ring) {
+ dma_free_coherent(hsdma->ddev.dev,
+ 2 * HSDMA_DESCS_NUM * sizeof(*chan->tx_ring),
+ chan->tx_ring, chan->desc_addr);
+ chan->tx_ring = NULL;
+ chan->rx_ring = NULL;
+ }
+}
+
+static int mtk_hsdma_init(struct mtk_hsdam_engine *hsdma)
+{
+ struct mtk_hsdma_chan *chan;
+ int ret;
+ u32 reg;
+
+ /* init desc */
+ chan = &hsdma->chan[0];
+ ret = mtk_hsdam_alloc_desc(hsdma, chan);
+ if (ret)
+ return ret;
+
+ /* tx */
+ mtk_hsdma_write(hsdma, HSDMA_REG_TX_BASE, chan->desc_addr);
+ mtk_hsdma_write(hsdma, HSDMA_REG_TX_CNT, HSDMA_DESCS_NUM);
+ /* rx */
+ mtk_hsdma_write(hsdma, HSDMA_REG_RX_BASE, chan->desc_addr +
+ (sizeof(struct hsdma_desc) * HSDMA_DESCS_NUM));
+ mtk_hsdma_write(hsdma, HSDMA_REG_RX_CNT, HSDMA_DESCS_NUM);
+ /* reset */
+ mtk_hsdma_reset_chan(hsdma, chan);
+
+ /* enable rx intr */
+ mtk_hsdma_write(hsdma, HSDMA_REG_INT_MASK, HSDMA_INT_RX_Q0);
+
+ /* enable dma */
+ mtk_hsdma_write(hsdma, HSDMA_REG_GLO_CFG, HSDMA_GLO_DEFAULT);
+
+ /* hardware info */
+ reg = mtk_hsdma_read(hsdma, HSDMA_REG_INFO);
+ dev_info(hsdma->ddev.dev, "rx: %d, tx: %d\n",
+ (reg >> HSDMA_INFO_RX_SHIFT) & HSDMA_INFO_RX_MASK,
+ (reg >> HSDMA_INFO_TX_SHIFT) & HSDMA_INFO_TX_MASK);
+
+ hsdma_dump_reg(hsdma);
+
+ return ret;
+}
+
+static void mtk_hsdma_uninit(struct mtk_hsdam_engine *hsdma)
+{
+ struct mtk_hsdma_chan *chan;
+
+ /* disable dma */
+ mtk_hsdma_write(hsdma, HSDMA_REG_GLO_CFG, 0);
+
+ /* disable intr */
+ mtk_hsdma_write(hsdma, HSDMA_REG_INT_MASK, 0);
+
+ /* free desc */
+ chan = &hsdma->chan[0];
+ mtk_hsdam_free_desc(hsdma, chan);
+
+ /* tx */
+ mtk_hsdma_write(hsdma, HSDMA_REG_TX_BASE, 0);
+ mtk_hsdma_write(hsdma, HSDMA_REG_TX_CNT, 0);
+ /* rx */
+ mtk_hsdma_write(hsdma, HSDMA_REG_RX_BASE, 0);
+ mtk_hsdma_write(hsdma, HSDMA_REG_RX_CNT, 0);
+ /* reset */
+ mtk_hsdma_reset_chan(hsdma, chan);
+}
+
+static const struct of_device_id mtk_hsdma_of_match[] = {
+ { .compatible = "mediatek,mt7621-hsdma" },
+ { },
+};
+
+static int mtk_hsdma_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct mtk_hsdma_chan *chan;
+ struct mtk_hsdam_engine *hsdma;
+ struct dma_device *dd;
+ struct resource *res;
+ int ret;
+ int irq;
+ void __iomem *base;
+
+ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ match = of_match_device(mtk_hsdma_of_match, &pdev->dev);
+ if (!match)
+ return -EINVAL;
+
+ hsdma = devm_kzalloc(&pdev->dev, sizeof(*hsdma), GFP_KERNEL);
+ if (!hsdma) {
+ dev_err(&pdev->dev, "alloc dma device failed\n");
+ return -EINVAL;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+ hsdma->base = base + HSDMA_BASE_OFFSET;
+ tasklet_init(&hsdma->task, mtk_hsdma_tasklet, (unsigned long)hsdma);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "failed to get irq\n");
+ return -EINVAL;
+ }
+ ret = devm_request_irq(&pdev->dev, irq, mtk_hsdma_irq,
+ 0, dev_name(&pdev->dev), hsdma);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request irq\n");
+ return ret;
+ }
+
+ device_reset(&pdev->dev);
+
+ dd = &hsdma->ddev;
+ dma_cap_set(DMA_MEMCPY, dd->cap_mask);
+ dd->copy_align = HSDMA_ALIGN_SIZE;
+ dd->device_free_chan_resources = mtk_hsdma_free_chan_resources;
+ dd->device_prep_dma_memcpy = mtk_hsdma_prep_dma_memcpy;
+ dd->device_terminate_all = mtk_hsdma_terminate_all;
+ dd->device_tx_status = mtk_hsdma_tx_status;
+ dd->device_issue_pending = mtk_hsdma_issue_pending;
+ dd->dev = &pdev->dev;
+ dd->dev->dma_parms = &hsdma->dma_parms;
+ dma_set_max_seg_size(dd->dev, HSDMA_MAX_PLEN);
+ INIT_LIST_HEAD(&dd->channels);
+
+ chan = &hsdma->chan[0];
+ chan->id = 0;
+ chan->vchan.desc_free = mtk_hsdma_desc_free;
+ vchan_init(&chan->vchan, dd);
+
+ /* init hardware */
+ ret = mtk_hsdma_init(hsdma);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to alloc ring descs\n");
+ return ret;
+ }
+
+ ret = dma_async_device_register(dd);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register dma device\n");
+ return ret;
+ }
+
+ ret = of_dma_controller_register(pdev->dev.of_node,
+ of_dma_xlate_by_chan_id, hsdma);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register of dma controller\n");
+ goto err_unregister;
+ }
+
+ platform_set_drvdata(pdev, hsdma);
+
+ return 0;
+
+err_unregister:
+ dma_async_device_unregister(dd);
+ return ret;
+}
+
+static int mtk_hsdma_remove(struct platform_device *pdev)
+{
+ struct mtk_hsdam_engine *hsdma = platform_get_drvdata(pdev);
+
+ mtk_hsdma_uninit(hsdma);
+
+ of_dma_controller_free(pdev->dev.of_node);
+ dma_async_device_unregister(&hsdma->ddev);
+
+ return 0;
+}
+
+static struct platform_driver mtk_hsdma_driver = {
+ .probe = mtk_hsdma_probe,
+ .remove = mtk_hsdma_remove,
+ .driver = {
+ .name = "hsdma-mt7621",
+ .of_match_table = mtk_hsdma_of_match,
+ },
+};
+module_platform_driver(mtk_hsdma_driver);
+
+MODULE_AUTHOR("Michael Lee <igvtee@gmail.com>");
+MODULE_DESCRIPTION("MTK HSDMA driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/mt7621-dma/ralink-gdma.c b/drivers/staging/mt7621-dma/ralink-gdma.c
new file mode 100644
index 000000000000..6d9fe175ea52
--- /dev/null
+++ b/drivers/staging/mt7621-dma/ralink-gdma.c
@@ -0,0 +1,930 @@
+/*
+ * Copyright (C) 2013, Lars-Peter Clausen <lars@metafoo.de>
+ * GDMA4740 DMAC support
+ *
+ * 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/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/irq.h>
+#include <linux/of_dma.h>
+#include <linux/reset.h>
+#include <linux/of_device.h>
+
+#include "virt-dma.h"
+
+#define GDMA_REG_SRC_ADDR(x) (0x00 + (x) * 0x10)
+#define GDMA_REG_DST_ADDR(x) (0x04 + (x) * 0x10)
+
+#define GDMA_REG_CTRL0(x) (0x08 + (x) * 0x10)
+#define GDMA_REG_CTRL0_TX_MASK 0xffff
+#define GDMA_REG_CTRL0_TX_SHIFT 16
+#define GDMA_REG_CTRL0_CURR_MASK 0xff
+#define GDMA_REG_CTRL0_CURR_SHIFT 8
+#define GDMA_REG_CTRL0_SRC_ADDR_FIXED BIT(7)
+#define GDMA_REG_CTRL0_DST_ADDR_FIXED BIT(6)
+#define GDMA_REG_CTRL0_BURST_MASK 0x7
+#define GDMA_REG_CTRL0_BURST_SHIFT 3
+#define GDMA_REG_CTRL0_DONE_INT BIT(2)
+#define GDMA_REG_CTRL0_ENABLE BIT(1)
+#define GDMA_REG_CTRL0_SW_MODE BIT(0)
+
+#define GDMA_REG_CTRL1(x) (0x0c + (x) * 0x10)
+#define GDMA_REG_CTRL1_SEG_MASK 0xf
+#define GDMA_REG_CTRL1_SEG_SHIFT 22
+#define GDMA_REG_CTRL1_REQ_MASK 0x3f
+#define GDMA_REG_CTRL1_SRC_REQ_SHIFT 16
+#define GDMA_REG_CTRL1_DST_REQ_SHIFT 8
+#define GDMA_REG_CTRL1_CONTINOUS BIT(14)
+#define GDMA_REG_CTRL1_NEXT_MASK 0x1f
+#define GDMA_REG_CTRL1_NEXT_SHIFT 3
+#define GDMA_REG_CTRL1_COHERENT BIT(2)
+#define GDMA_REG_CTRL1_FAIL BIT(1)
+#define GDMA_REG_CTRL1_MASK BIT(0)
+
+#define GDMA_REG_UNMASK_INT 0x200
+#define GDMA_REG_DONE_INT 0x204
+
+#define GDMA_REG_GCT 0x220
+#define GDMA_REG_GCT_CHAN_MASK 0x3
+#define GDMA_REG_GCT_CHAN_SHIFT 3
+#define GDMA_REG_GCT_VER_MASK 0x3
+#define GDMA_REG_GCT_VER_SHIFT 1
+#define GDMA_REG_GCT_ARBIT_RR BIT(0)
+
+#define GDMA_REG_REQSTS 0x2a0
+#define GDMA_REG_ACKSTS 0x2a4
+#define GDMA_REG_FINSTS 0x2a8
+
+/* for RT305X gdma registers */
+#define GDMA_RT305X_CTRL0_REQ_MASK 0xf
+#define GDMA_RT305X_CTRL0_SRC_REQ_SHIFT 12
+#define GDMA_RT305X_CTRL0_DST_REQ_SHIFT 8
+
+#define GDMA_RT305X_CTRL1_FAIL BIT(4)
+#define GDMA_RT305X_CTRL1_NEXT_MASK 0x7
+#define GDMA_RT305X_CTRL1_NEXT_SHIFT 1
+
+#define GDMA_RT305X_STATUS_INT 0x80
+#define GDMA_RT305X_STATUS_SIGNAL 0x84
+#define GDMA_RT305X_GCT 0x88
+
+/* for MT7621 gdma registers */
+#define GDMA_REG_PERF_START(x) (0x230 + (x) * 0x8)
+#define GDMA_REG_PERF_END(x) (0x234 + (x) * 0x8)
+
+enum gdma_dma_transfer_size {
+ GDMA_TRANSFER_SIZE_4BYTE = 0,
+ GDMA_TRANSFER_SIZE_8BYTE = 1,
+ GDMA_TRANSFER_SIZE_16BYTE = 2,
+ GDMA_TRANSFER_SIZE_32BYTE = 3,
+ GDMA_TRANSFER_SIZE_64BYTE = 4,
+};
+
+struct gdma_dma_sg {
+ dma_addr_t src_addr;
+ dma_addr_t dst_addr;
+ u32 len;
+};
+
+struct gdma_dma_desc {
+ struct virt_dma_desc vdesc;
+
+ enum dma_transfer_direction direction;
+ bool cyclic;
+
+ u32 residue;
+ unsigned int num_sgs;
+ struct gdma_dma_sg sg[];
+};
+
+struct gdma_dmaengine_chan {
+ struct virt_dma_chan vchan;
+ unsigned int id;
+ unsigned int slave_id;
+
+ dma_addr_t fifo_addr;
+ enum gdma_dma_transfer_size burst_size;
+
+ struct gdma_dma_desc *desc;
+ unsigned int next_sg;
+};
+
+struct gdma_dma_dev {
+ struct dma_device ddev;
+ struct device_dma_parameters dma_parms;
+ struct gdma_data *data;
+ void __iomem *base;
+ struct tasklet_struct task;
+ volatile unsigned long chan_issued;
+ atomic_t cnt;
+
+ struct gdma_dmaengine_chan chan[];
+};
+
+struct gdma_data {
+ int chancnt;
+ u32 done_int_reg;
+ void (*init)(struct gdma_dma_dev *dma_dev);
+ int (*start_transfer)(struct gdma_dmaengine_chan *chan);
+};
+
+static struct gdma_dma_dev *gdma_dma_chan_get_dev(
+ struct gdma_dmaengine_chan *chan)
+{
+ return container_of(chan->vchan.chan.device, struct gdma_dma_dev,
+ ddev);
+}
+
+static struct gdma_dmaengine_chan *to_gdma_dma_chan(struct dma_chan *c)
+{
+ return container_of(c, struct gdma_dmaengine_chan, vchan.chan);
+}
+
+static struct gdma_dma_desc *to_gdma_dma_desc(struct virt_dma_desc *vdesc)
+{
+ return container_of(vdesc, struct gdma_dma_desc, vdesc);
+}
+
+static inline uint32_t gdma_dma_read(struct gdma_dma_dev *dma_dev,
+ unsigned int reg)
+{
+ return readl(dma_dev->base + reg);
+}
+
+static inline void gdma_dma_write(struct gdma_dma_dev *dma_dev,
+ unsigned reg, uint32_t val)
+{
+ writel(val, dma_dev->base + reg);
+}
+
+static struct gdma_dma_desc *gdma_dma_alloc_desc(unsigned int num_sgs)
+{
+ return kzalloc(sizeof(struct gdma_dma_desc) +
+ sizeof(struct gdma_dma_sg) * num_sgs, GFP_ATOMIC);
+}
+
+static enum gdma_dma_transfer_size gdma_dma_maxburst(u32 maxburst)
+{
+ if (maxburst < 2)
+ return GDMA_TRANSFER_SIZE_4BYTE;
+ else if (maxburst < 4)
+ return GDMA_TRANSFER_SIZE_8BYTE;
+ else if (maxburst < 8)
+ return GDMA_TRANSFER_SIZE_16BYTE;
+ else if (maxburst < 16)
+ return GDMA_TRANSFER_SIZE_32BYTE;
+ else
+ return GDMA_TRANSFER_SIZE_64BYTE;
+}
+
+static int gdma_dma_config(struct dma_chan *c,
+ struct dma_slave_config *config)
+{
+ struct gdma_dmaengine_chan *chan = to_gdma_dma_chan(c);
+ struct gdma_dma_dev *dma_dev = gdma_dma_chan_get_dev(chan);
+
+ if (config->device_fc) {
+ dev_err(dma_dev->ddev.dev, "not support flow controller\n");
+ return -EINVAL;
+ }
+
+ switch (config->direction) {
+ case DMA_MEM_TO_DEV:
+ if (config->dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) {
+ dev_err(dma_dev->ddev.dev, "only support 4 byte buswidth\n");
+ return -EINVAL;
+ }
+ chan->slave_id = config->slave_id;
+ chan->fifo_addr = config->dst_addr;
+ chan->burst_size = gdma_dma_maxburst(config->dst_maxburst);
+ break;
+ case DMA_DEV_TO_MEM:
+ if (config->src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) {
+ dev_err(dma_dev->ddev.dev, "only support 4 byte buswidth\n");
+ return -EINVAL;
+ }
+ chan->slave_id = config->slave_id;
+ chan->fifo_addr = config->src_addr;
+ chan->burst_size = gdma_dma_maxburst(config->src_maxburst);
+ break;
+ default:
+ dev_err(dma_dev->ddev.dev, "direction type %d error\n",
+ config->direction);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int gdma_dma_terminate_all(struct dma_chan *c)
+{
+ struct gdma_dmaengine_chan *chan = to_gdma_dma_chan(c);
+ struct gdma_dma_dev *dma_dev = gdma_dma_chan_get_dev(chan);
+ unsigned long flags, timeout;
+ LIST_HEAD(head);
+ int i = 0;
+
+ spin_lock_irqsave(&chan->vchan.lock, flags);
+ chan->desc = NULL;
+ clear_bit(chan->id, &dma_dev->chan_issued);
+ vchan_get_all_descriptors(&chan->vchan, &head);
+ spin_unlock_irqrestore(&chan->vchan.lock, flags);
+
+ vchan_dma_desc_free_list(&chan->vchan, &head);
+
+ /* wait dma transfer complete */
+ timeout = jiffies + msecs_to_jiffies(5000);
+ while (gdma_dma_read(dma_dev, GDMA_REG_CTRL0(chan->id)) &
+ GDMA_REG_CTRL0_ENABLE) {
+ if (time_after_eq(jiffies, timeout)) {
+ dev_err(dma_dev->ddev.dev, "chan %d wait timeout\n",
+ chan->id);
+ /* restore to init value */
+ gdma_dma_write(dma_dev, GDMA_REG_CTRL0(chan->id), 0);
+ break;
+ }
+ cpu_relax();
+ i++;
+ }
+
+ if (i)
+ dev_dbg(dma_dev->ddev.dev, "terminate chan %d loops %d\n",
+ chan->id, i);
+
+ return 0;
+}
+
+static void rt305x_dump_reg(struct gdma_dma_dev *dma_dev, int id)
+{
+ dev_dbg(dma_dev->ddev.dev, "chan %d, src %08x, dst %08x, ctr0 %08x, " \
+ "ctr1 %08x, intr %08x, signal %08x\n", id,
+ gdma_dma_read(dma_dev, GDMA_REG_SRC_ADDR(id)),
+ gdma_dma_read(dma_dev, GDMA_REG_DST_ADDR(id)),
+ gdma_dma_read(dma_dev, GDMA_REG_CTRL0(id)),
+ gdma_dma_read(dma_dev, GDMA_REG_CTRL1(id)),
+ gdma_dma_read(dma_dev, GDMA_RT305X_STATUS_INT),
+ gdma_dma_read(dma_dev, GDMA_RT305X_STATUS_SIGNAL));
+}
+
+static int rt305x_gdma_start_transfer(struct gdma_dmaengine_chan *chan)
+{
+ struct gdma_dma_dev *dma_dev = gdma_dma_chan_get_dev(chan);
+ dma_addr_t src_addr, dst_addr;
+ struct gdma_dma_sg *sg;
+ uint32_t ctrl0, ctrl1;
+
+ /* verify chan is already stopped */
+ ctrl0 = gdma_dma_read(dma_dev, GDMA_REG_CTRL0(chan->id));
+ if (unlikely(ctrl0 & GDMA_REG_CTRL0_ENABLE)) {
+ dev_err(dma_dev->ddev.dev, "chan %d is start(%08x).\n",
+ chan->id, ctrl0);
+ rt305x_dump_reg(dma_dev, chan->id);
+ return -EINVAL;
+ }
+
+ sg = &chan->desc->sg[chan->next_sg];
+ if (chan->desc->direction == DMA_MEM_TO_DEV) {
+ src_addr = sg->src_addr;
+ dst_addr = chan->fifo_addr;
+ ctrl0 = GDMA_REG_CTRL0_DST_ADDR_FIXED | \
+ (8 << GDMA_RT305X_CTRL0_SRC_REQ_SHIFT) | \
+ (chan->slave_id << GDMA_RT305X_CTRL0_DST_REQ_SHIFT);
+ } else if (chan->desc->direction == DMA_DEV_TO_MEM) {
+ src_addr = chan->fifo_addr;
+ dst_addr = sg->dst_addr;
+ ctrl0 = GDMA_REG_CTRL0_SRC_ADDR_FIXED | \
+ (chan->slave_id << GDMA_RT305X_CTRL0_SRC_REQ_SHIFT) | \
+ (8 << GDMA_RT305X_CTRL0_DST_REQ_SHIFT);
+ } else if (chan->desc->direction == DMA_MEM_TO_MEM) {
+ /*
+ * TODO: memcpy function have bugs. sometime it will copy
+ * more 8 bytes data when using dmatest verify.
+ */
+ src_addr = sg->src_addr;
+ dst_addr = sg->dst_addr;
+ ctrl0 = GDMA_REG_CTRL0_SW_MODE | \
+ (8 << GDMA_REG_CTRL1_SRC_REQ_SHIFT) | \
+ (8 << GDMA_REG_CTRL1_DST_REQ_SHIFT);
+ } else {
+ dev_err(dma_dev->ddev.dev, "direction type %d error\n",
+ chan->desc->direction);
+ return -EINVAL;
+ }
+
+ ctrl0 |= (sg->len << GDMA_REG_CTRL0_TX_SHIFT) | \
+ (chan->burst_size << GDMA_REG_CTRL0_BURST_SHIFT) | \
+ GDMA_REG_CTRL0_DONE_INT | GDMA_REG_CTRL0_ENABLE;
+ ctrl1 = chan->id << GDMA_REG_CTRL1_NEXT_SHIFT;
+
+ chan->next_sg++;
+ gdma_dma_write(dma_dev, GDMA_REG_SRC_ADDR(chan->id), src_addr);
+ gdma_dma_write(dma_dev, GDMA_REG_DST_ADDR(chan->id), dst_addr);
+ gdma_dma_write(dma_dev, GDMA_REG_CTRL1(chan->id), ctrl1);
+
+ /* make sure next_sg is update */
+ wmb();
+ gdma_dma_write(dma_dev, GDMA_REG_CTRL0(chan->id), ctrl0);
+
+ return 0;
+}
+
+static void rt3883_dump_reg(struct gdma_dma_dev *dma_dev, int id)
+{
+ dev_dbg(dma_dev->ddev.dev, "chan %d, src %08x, dst %08x, ctr0 %08x, " \
+ "ctr1 %08x, unmask %08x, done %08x, " \
+ "req %08x, ack %08x, fin %08x\n", id,
+ gdma_dma_read(dma_dev, GDMA_REG_SRC_ADDR(id)),
+ gdma_dma_read(dma_dev, GDMA_REG_DST_ADDR(id)),
+ gdma_dma_read(dma_dev, GDMA_REG_CTRL0(id)),
+ gdma_dma_read(dma_dev, GDMA_REG_CTRL1(id)),
+ gdma_dma_read(dma_dev, GDMA_REG_UNMASK_INT),
+ gdma_dma_read(dma_dev, GDMA_REG_DONE_INT),
+ gdma_dma_read(dma_dev, GDMA_REG_REQSTS),
+ gdma_dma_read(dma_dev, GDMA_REG_ACKSTS),
+ gdma_dma_read(dma_dev, GDMA_REG_FINSTS));
+}
+
+static int rt3883_gdma_start_transfer(struct gdma_dmaengine_chan *chan)
+{
+ struct gdma_dma_dev *dma_dev = gdma_dma_chan_get_dev(chan);
+ dma_addr_t src_addr, dst_addr;
+ struct gdma_dma_sg *sg;
+ uint32_t ctrl0, ctrl1;
+
+ /* verify chan is already stopped */
+ ctrl0 = gdma_dma_read(dma_dev, GDMA_REG_CTRL0(chan->id));
+ if (unlikely(ctrl0 & GDMA_REG_CTRL0_ENABLE)) {
+ dev_err(dma_dev->ddev.dev, "chan %d is start(%08x).\n",
+ chan->id, ctrl0);
+ rt3883_dump_reg(dma_dev, chan->id);
+ return -EINVAL;
+ }
+
+ sg = &chan->desc->sg[chan->next_sg];
+ if (chan->desc->direction == DMA_MEM_TO_DEV) {
+ src_addr = sg->src_addr;
+ dst_addr = chan->fifo_addr;
+ ctrl0 = GDMA_REG_CTRL0_DST_ADDR_FIXED;
+ ctrl1 = (32 << GDMA_REG_CTRL1_SRC_REQ_SHIFT) | \
+ (chan->slave_id << GDMA_REG_CTRL1_DST_REQ_SHIFT);
+ } else if (chan->desc->direction == DMA_DEV_TO_MEM) {
+ src_addr = chan->fifo_addr;
+ dst_addr = sg->dst_addr;
+ ctrl0 = GDMA_REG_CTRL0_SRC_ADDR_FIXED;
+ ctrl1 = (chan->slave_id << GDMA_REG_CTRL1_SRC_REQ_SHIFT) | \
+ (32 << GDMA_REG_CTRL1_DST_REQ_SHIFT) | \
+ GDMA_REG_CTRL1_COHERENT;
+ } else if (chan->desc->direction == DMA_MEM_TO_MEM) {
+ src_addr = sg->src_addr;
+ dst_addr = sg->dst_addr;
+ ctrl0 = GDMA_REG_CTRL0_SW_MODE;
+ ctrl1 = (32 << GDMA_REG_CTRL1_SRC_REQ_SHIFT) | \
+ (32 << GDMA_REG_CTRL1_DST_REQ_SHIFT) | \
+ GDMA_REG_CTRL1_COHERENT;
+ } else {
+ dev_err(dma_dev->ddev.dev, "direction type %d error\n",
+ chan->desc->direction);
+ return -EINVAL;
+ }
+
+ ctrl0 |= (sg->len << GDMA_REG_CTRL0_TX_SHIFT) | \
+ (chan->burst_size << GDMA_REG_CTRL0_BURST_SHIFT) | \
+ GDMA_REG_CTRL0_DONE_INT | GDMA_REG_CTRL0_ENABLE;
+ ctrl1 |= chan->id << GDMA_REG_CTRL1_NEXT_SHIFT;
+
+ chan->next_sg++;
+ gdma_dma_write(dma_dev, GDMA_REG_SRC_ADDR(chan->id), src_addr);
+ gdma_dma_write(dma_dev, GDMA_REG_DST_ADDR(chan->id), dst_addr);
+ gdma_dma_write(dma_dev, GDMA_REG_CTRL1(chan->id), ctrl1);
+
+ /* make sure next_sg is update */
+ wmb();
+ gdma_dma_write(dma_dev, GDMA_REG_CTRL0(chan->id), ctrl0);
+
+ return 0;
+}
+
+static inline int gdma_start_transfer(struct gdma_dma_dev *dma_dev,
+ struct gdma_dmaengine_chan *chan)
+{
+ return dma_dev->data->start_transfer(chan);
+}
+
+static int gdma_next_desc(struct gdma_dmaengine_chan *chan)
+{
+ struct virt_dma_desc *vdesc;
+
+ vdesc = vchan_next_desc(&chan->vchan);
+ if (!vdesc) {
+ chan->desc = NULL;
+ return 0;
+ }
+ chan->desc = to_gdma_dma_desc(vdesc);
+ chan->next_sg = 0;
+
+ return 1;
+}
+
+static void gdma_dma_chan_irq(struct gdma_dma_dev *dma_dev,
+ struct gdma_dmaengine_chan *chan)
+{
+ struct gdma_dma_desc *desc;
+ unsigned long flags;
+ int chan_issued;
+
+ chan_issued = 0;
+ spin_lock_irqsave(&chan->vchan.lock, flags);
+ desc = chan->desc;
+ if (desc) {
+ if (desc->cyclic) {
+ vchan_cyclic_callback(&desc->vdesc);
+ if (chan->next_sg == desc->num_sgs)
+ chan->next_sg = 0;
+ chan_issued = 1;
+ } else {
+ desc->residue -= desc->sg[chan->next_sg - 1].len;
+ if (chan->next_sg == desc->num_sgs) {
+ list_del(&desc->vdesc.node);
+ vchan_cookie_complete(&desc->vdesc);
+ chan_issued = gdma_next_desc(chan);
+ } else
+ chan_issued = 1;
+ }
+ } else
+ dev_dbg(dma_dev->ddev.dev, "chan %d no desc to complete\n",
+ chan->id);
+ if (chan_issued)
+ set_bit(chan->id, &dma_dev->chan_issued);
+ spin_unlock_irqrestore(&chan->vchan.lock, flags);
+}
+
+static irqreturn_t gdma_dma_irq(int irq, void *devid)
+{
+ struct gdma_dma_dev *dma_dev = devid;
+ u32 done, done_reg;
+ unsigned int i;
+
+ done_reg = dma_dev->data->done_int_reg;
+ done = gdma_dma_read(dma_dev, done_reg);
+ if (unlikely(!done))
+ return IRQ_NONE;
+
+ /* clean done bits */
+ gdma_dma_write(dma_dev, done_reg, done);
+
+ i = 0;
+ while (done) {
+ if (done & 0x1) {
+ gdma_dma_chan_irq(dma_dev, &dma_dev->chan[i]);
+ atomic_dec(&dma_dev->cnt);
+ }
+ done >>= 1;
+ i++;
+ }
+
+ /* start only have work to do */
+ if (dma_dev->chan_issued)
+ tasklet_schedule(&dma_dev->task);
+
+ return IRQ_HANDLED;
+}
+
+static void gdma_dma_issue_pending(struct dma_chan *c)
+{
+ struct gdma_dmaengine_chan *chan = to_gdma_dma_chan(c);
+ struct gdma_dma_dev *dma_dev = gdma_dma_chan_get_dev(chan);
+ unsigned long flags;
+
+ spin_lock_irqsave(&chan->vchan.lock, flags);
+ if (vchan_issue_pending(&chan->vchan) && !chan->desc) {
+ if (gdma_next_desc(chan)) {
+ set_bit(chan->id, &dma_dev->chan_issued);
+ tasklet_schedule(&dma_dev->task);
+ } else
+ dev_dbg(dma_dev->ddev.dev, "chan %d no desc to issue\n",
+ chan->id);
+ }
+ spin_unlock_irqrestore(&chan->vchan.lock, flags);
+}
+
+static struct dma_async_tx_descriptor *gdma_dma_prep_slave_sg(
+ struct dma_chan *c, struct scatterlist *sgl,
+ unsigned int sg_len, enum dma_transfer_direction direction,
+ unsigned long flags, void *context)
+{
+ struct gdma_dmaengine_chan *chan = to_gdma_dma_chan(c);
+ struct gdma_dma_desc *desc;
+ struct scatterlist *sg;
+ unsigned int i;
+
+ desc = gdma_dma_alloc_desc(sg_len);
+ if (!desc) {
+ dev_err(c->device->dev, "alloc sg decs error\n");
+ return NULL;
+ }
+ desc->residue = 0;
+
+ for_each_sg(sgl, sg, sg_len, i) {
+ if (direction == DMA_MEM_TO_DEV)
+ desc->sg[i].src_addr = sg_dma_address(sg);
+ else if (direction == DMA_DEV_TO_MEM)
+ desc->sg[i].dst_addr = sg_dma_address(sg);
+ else {
+ dev_err(c->device->dev, "direction type %d error\n",
+ direction);
+ goto free_desc;
+ }
+
+ if (unlikely(sg_dma_len(sg) > GDMA_REG_CTRL0_TX_MASK)) {
+ dev_err(c->device->dev, "sg len too large %d\n",
+ sg_dma_len(sg));
+ goto free_desc;
+ }
+ desc->sg[i].len = sg_dma_len(sg);
+ desc->residue += sg_dma_len(sg);
+ }
+
+ desc->num_sgs = sg_len;
+ desc->direction = direction;
+ desc->cyclic = false;
+
+ return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
+
+free_desc:
+ kfree(desc);
+ return NULL;
+}
+
+static struct dma_async_tx_descriptor *gdma_dma_prep_dma_memcpy(
+ struct dma_chan *c, dma_addr_t dest, dma_addr_t src,
+ size_t len, unsigned long flags)
+{
+ struct gdma_dmaengine_chan *chan = to_gdma_dma_chan(c);
+ struct gdma_dma_desc *desc;
+ unsigned int num_periods, i;
+ size_t xfer_count;
+
+ if (len <= 0)
+ return NULL;
+
+ chan->burst_size = gdma_dma_maxburst(len >> 2);
+
+ xfer_count = GDMA_REG_CTRL0_TX_MASK;
+ num_periods = DIV_ROUND_UP(len, xfer_count);
+
+ desc = gdma_dma_alloc_desc(num_periods);
+ if (!desc) {
+ dev_err(c->device->dev, "alloc memcpy decs error\n");
+ return NULL;
+ }
+ desc->residue = len;
+
+ for (i = 0; i < num_periods; i++) {
+ desc->sg[i].src_addr = src;
+ desc->sg[i].dst_addr = dest;
+ if (len > xfer_count)
+ desc->sg[i].len = xfer_count;
+ else
+ desc->sg[i].len = len;
+ src += desc->sg[i].len;
+ dest += desc->sg[i].len;
+ len -= desc->sg[i].len;
+ }
+
+ desc->num_sgs = num_periods;
+ desc->direction = DMA_MEM_TO_MEM;
+ desc->cyclic = false;
+
+ return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
+}
+
+static struct dma_async_tx_descriptor *gdma_dma_prep_dma_cyclic(
+ struct dma_chan *c, dma_addr_t buf_addr, size_t buf_len,
+ size_t period_len, enum dma_transfer_direction direction,
+ unsigned long flags)
+{
+ struct gdma_dmaengine_chan *chan = to_gdma_dma_chan(c);
+ struct gdma_dma_desc *desc;
+ unsigned int num_periods, i;
+
+ if (buf_len % period_len)
+ return NULL;
+
+ if (period_len > GDMA_REG_CTRL0_TX_MASK) {
+ dev_err(c->device->dev, "cyclic len too large %d\n",
+ period_len);
+ return NULL;
+ }
+
+ num_periods = buf_len / period_len;
+ desc = gdma_dma_alloc_desc(num_periods);
+ if (!desc) {
+ dev_err(c->device->dev, "alloc cyclic decs error\n");
+ return NULL;
+ }
+ desc->residue = buf_len;
+
+ for (i = 0; i < num_periods; i++) {
+ if (direction == DMA_MEM_TO_DEV)
+ desc->sg[i].src_addr = buf_addr;
+ else if (direction == DMA_DEV_TO_MEM)
+ desc->sg[i].dst_addr = buf_addr;
+ else {
+ dev_err(c->device->dev, "direction type %d error\n",
+ direction);
+ goto free_desc;
+ }
+ desc->sg[i].len = period_len;
+ buf_addr += period_len;
+ }
+
+ desc->num_sgs = num_periods;
+ desc->direction = direction;
+ desc->cyclic = true;
+
+ return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
+
+free_desc:
+ kfree(desc);
+ return NULL;
+}
+
+static enum dma_status gdma_dma_tx_status(struct dma_chan *c,
+ dma_cookie_t cookie,
+ struct dma_tx_state *state)
+{
+ struct gdma_dmaengine_chan *chan = to_gdma_dma_chan(c);
+ struct virt_dma_desc *vdesc;
+ enum dma_status status;
+ unsigned long flags;
+ struct gdma_dma_desc *desc;
+
+ status = dma_cookie_status(c, cookie, state);
+ if (status == DMA_COMPLETE || !state)
+ return status;
+
+ spin_lock_irqsave(&chan->vchan.lock, flags);
+ desc = chan->desc;
+ if (desc && (cookie == desc->vdesc.tx.cookie)) {
+ /*
+ * We never update edesc->residue in the cyclic case, so we
+ * can tell the remaining room to the end of the circular
+ * buffer.
+ */
+ if (desc->cyclic)
+ state->residue = desc->residue -
+ ((chan->next_sg - 1) * desc->sg[0].len);
+ else
+ state->residue = desc->residue;
+ } else {
+ vdesc = vchan_find_desc(&chan->vchan, cookie);
+ if (vdesc)
+ state->residue = to_gdma_dma_desc(vdesc)->residue;
+ }
+ spin_unlock_irqrestore(&chan->vchan.lock, flags);
+
+ dev_dbg(c->device->dev, "tx residue %d bytes\n", state->residue);
+
+ return status;
+}
+
+static void gdma_dma_free_chan_resources(struct dma_chan *c)
+{
+ vchan_free_chan_resources(to_virt_chan(c));
+}
+
+static void gdma_dma_desc_free(struct virt_dma_desc *vdesc)
+{
+ kfree(container_of(vdesc, struct gdma_dma_desc, vdesc));
+}
+
+static void gdma_dma_tasklet(unsigned long arg)
+{
+ struct gdma_dma_dev *dma_dev = (struct gdma_dma_dev *)arg;
+ struct gdma_dmaengine_chan *chan;
+ static unsigned int last_chan;
+ unsigned int i, chan_mask;
+
+ /* record last chan to round robin all chans */
+ i = last_chan;
+ chan_mask = dma_dev->data->chancnt - 1;
+ do {
+ /*
+ * on mt7621. when verify with dmatest with all
+ * channel is enable. we need to limit only two
+ * channel is working at the same time. otherwise the
+ * data will have problem.
+ */
+ if (atomic_read(&dma_dev->cnt) >= 2) {
+ last_chan = i;
+ break;
+ }
+
+ if (test_and_clear_bit(i, &dma_dev->chan_issued)) {
+ chan = &dma_dev->chan[i];
+ if (chan->desc) {
+ atomic_inc(&dma_dev->cnt);
+ gdma_start_transfer(dma_dev, chan);
+ } else
+ dev_dbg(dma_dev->ddev.dev, "chan %d no desc to issue\n", chan->id);
+
+ if (!dma_dev->chan_issued)
+ break;
+ }
+
+ i = (i + 1) & chan_mask;
+ } while (i != last_chan);
+}
+
+static void rt305x_gdma_init(struct gdma_dma_dev *dma_dev)
+{
+ uint32_t gct;
+
+ /* all chans round robin */
+ gdma_dma_write(dma_dev, GDMA_RT305X_GCT, GDMA_REG_GCT_ARBIT_RR);
+
+ gct = gdma_dma_read(dma_dev, GDMA_RT305X_GCT);
+ dev_info(dma_dev->ddev.dev, "revision: %d, channels: %d\n",
+ (gct >> GDMA_REG_GCT_VER_SHIFT) & GDMA_REG_GCT_VER_MASK,
+ 8 << ((gct >> GDMA_REG_GCT_CHAN_SHIFT) &
+ GDMA_REG_GCT_CHAN_MASK));
+}
+
+static void rt3883_gdma_init(struct gdma_dma_dev *dma_dev)
+{
+ uint32_t gct;
+
+ /* all chans round robin */
+ gdma_dma_write(dma_dev, GDMA_REG_GCT, GDMA_REG_GCT_ARBIT_RR);
+
+ gct = gdma_dma_read(dma_dev, GDMA_REG_GCT);
+ dev_info(dma_dev->ddev.dev, "revision: %d, channels: %d\n",
+ (gct >> GDMA_REG_GCT_VER_SHIFT) & GDMA_REG_GCT_VER_MASK,
+ 8 << ((gct >> GDMA_REG_GCT_CHAN_SHIFT) &
+ GDMA_REG_GCT_CHAN_MASK));
+}
+
+static struct gdma_data rt305x_gdma_data = {
+ .chancnt = 8,
+ .done_int_reg = GDMA_RT305X_STATUS_INT,
+ .init = rt305x_gdma_init,
+ .start_transfer = rt305x_gdma_start_transfer,
+};
+
+static struct gdma_data rt3883_gdma_data = {
+ .chancnt = 16,
+ .done_int_reg = GDMA_REG_DONE_INT,
+ .init = rt3883_gdma_init,
+ .start_transfer = rt3883_gdma_start_transfer,
+};
+
+static const struct of_device_id gdma_of_match_table[] = {
+ { .compatible = "ralink,rt305x-gdma", .data = &rt305x_gdma_data },
+ { .compatible = "ralink,rt3883-gdma", .data = &rt3883_gdma_data },
+ { },
+};
+
+static int gdma_dma_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct gdma_dmaengine_chan *chan;
+ struct gdma_dma_dev *dma_dev;
+ struct dma_device *dd;
+ unsigned int i;
+ struct resource *res;
+ int ret;
+ int irq;
+ void __iomem *base;
+ struct gdma_data *data;
+
+ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ match = of_match_device(gdma_of_match_table, &pdev->dev);
+ if (!match)
+ return -EINVAL;
+ data = (struct gdma_data *) match->data;
+
+ dma_dev = devm_kzalloc(&pdev->dev, sizeof(*dma_dev) +
+ (sizeof(struct gdma_dmaengine_chan) * data->chancnt),
+ GFP_KERNEL);
+ if (!dma_dev) {
+ dev_err(&pdev->dev, "alloc dma device failed\n");
+ return -EINVAL;
+ }
+ dma_dev->data = data;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+ dma_dev->base = base;
+ tasklet_init(&dma_dev->task, gdma_dma_tasklet, (unsigned long)dma_dev);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "failed to get irq\n");
+ return -EINVAL;
+ }
+ ret = devm_request_irq(&pdev->dev, irq, gdma_dma_irq,
+ 0, dev_name(&pdev->dev), dma_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request irq\n");
+ return ret;
+ }
+
+ device_reset(&pdev->dev);
+
+ dd = &dma_dev->ddev;
+ dma_cap_set(DMA_MEMCPY, dd->cap_mask);
+ dma_cap_set(DMA_SLAVE, dd->cap_mask);
+ dma_cap_set(DMA_CYCLIC, dd->cap_mask);
+ dd->device_free_chan_resources = gdma_dma_free_chan_resources;
+ dd->device_prep_dma_memcpy = gdma_dma_prep_dma_memcpy;
+ dd->device_prep_slave_sg = gdma_dma_prep_slave_sg;
+ dd->device_prep_dma_cyclic = gdma_dma_prep_dma_cyclic;
+ dd->device_config = gdma_dma_config;
+ dd->device_terminate_all = gdma_dma_terminate_all;
+ dd->device_tx_status = gdma_dma_tx_status;
+ dd->device_issue_pending = gdma_dma_issue_pending;
+
+ dd->src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
+ dd->dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
+ dd->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
+ dd->residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT;
+
+ dd->dev = &pdev->dev;
+ dd->dev->dma_parms = &dma_dev->dma_parms;
+ dma_set_max_seg_size(dd->dev, GDMA_REG_CTRL0_TX_MASK);
+ INIT_LIST_HEAD(&dd->channels);
+
+ for (i = 0; i < data->chancnt; i++) {
+ chan = &dma_dev->chan[i];
+ chan->id = i;
+ chan->vchan.desc_free = gdma_dma_desc_free;
+ vchan_init(&chan->vchan, dd);
+ }
+
+ /* init hardware */
+ data->init(dma_dev);
+
+ ret = dma_async_device_register(dd);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register dma device\n");
+ return ret;
+ }
+
+ ret = of_dma_controller_register(pdev->dev.of_node,
+ of_dma_xlate_by_chan_id, dma_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register of dma controller\n");
+ goto err_unregister;
+ }
+
+ platform_set_drvdata(pdev, dma_dev);
+
+ return 0;
+
+err_unregister:
+ dma_async_device_unregister(dd);
+ return ret;
+}
+
+static int gdma_dma_remove(struct platform_device *pdev)
+{
+ struct gdma_dma_dev *dma_dev = platform_get_drvdata(pdev);
+
+ tasklet_kill(&dma_dev->task);
+ of_dma_controller_free(pdev->dev.of_node);
+ dma_async_device_unregister(&dma_dev->ddev);
+
+ return 0;
+}
+
+static struct platform_driver gdma_dma_driver = {
+ .probe = gdma_dma_probe,
+ .remove = gdma_dma_remove,
+ .driver = {
+ .name = "gdma-rt2880",
+ .of_match_table = gdma_of_match_table,
+ },
+};
+module_platform_driver(gdma_dma_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Ralink/MTK DMA driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/mt7621-dts/Kconfig b/drivers/staging/mt7621-dts/Kconfig
new file mode 100644
index 000000000000..94a9e16c0b92
--- /dev/null
+++ b/drivers/staging/mt7621-dts/Kconfig
@@ -0,0 +1,5 @@
+config DTB_GNUBEE1
+ bool "GnuBee1 NAS"
+ depends on SOC_MT7621 && DTB_RT_NONE
+ select BUILTIN_DTB
+
diff --git a/drivers/staging/mt7621-dts/Makefile b/drivers/staging/mt7621-dts/Makefile
new file mode 100644
index 000000000000..195eba4a5c65
--- /dev/null
+++ b/drivers/staging/mt7621-dts/Makefile
@@ -0,0 +1,3 @@
+dtb-$(CONFIG_DTB_GNUBEE1) += gbpc1.dtb
+
+obj-y += $(patsubst %.dtb, %.dtb.o, $(dtb-y))
diff --git a/drivers/staging/mt7621-dts/TODO b/drivers/staging/mt7621-dts/TODO
new file mode 100644
index 000000000000..15803132c1ea
--- /dev/null
+++ b/drivers/staging/mt7621-dts/TODO
@@ -0,0 +1,5 @@
+
+- ensure all usage matches code
+- ensure all features used are documented
+
+Cc: NeilBrown <neil@brown.name> \ No newline at end of file
diff --git a/drivers/staging/mt7621-dts/gbpc1.dts b/drivers/staging/mt7621-dts/gbpc1.dts
new file mode 100644
index 000000000000..515c7cbdd15e
--- /dev/null
+++ b/drivers/staging/mt7621-dts/gbpc1.dts
@@ -0,0 +1,143 @@
+/dts-v1/;
+
+#include "mt7621.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
+
+/ {
+ compatible = "gnubee,gb-pc1", "mediatek,mt7621-soc";
+ model = "GB-PC1";
+
+ memory@0 {
+ device_type = "memory";
+ reg = <0x0 0x1c000000>, <0x20000000 0x4000000>;
+ };
+
+ chosen {
+ bootargs = "console=ttyS0,57600";
+ };
+
+ palmbus: palmbus@1E000000 {
+ i2c@900 {
+ status = "okay";
+ };
+ };
+
+ gpio-keys-polled {
+ compatible = "gpio-keys-polled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ poll-interval = <20>;
+
+ reset {
+ label = "reset";
+ gpios = <&gpio0 18 GPIO_ACTIVE_HIGH>;
+ linux,code = <KEY_RESTART>;
+ };
+ };
+
+ gpio-leds {
+ compatible = "gpio-leds";
+
+ system {
+ label = "gb-pc1:green:system";
+ gpios = <&gpio0 6 GPIO_ACTIVE_LOW>;
+ };
+
+ status {
+ label = "gb-pc1:green:status";
+ gpios = <&gpio0 8 GPIO_ACTIVE_LOW>;
+ };
+
+ lan1 {
+ label = "gb-pc1:green:lan1";
+ gpios = <&gpio0 24 GPIO_ACTIVE_LOW>;
+ };
+
+ lan2 {
+ label = "gb-pc1:green:lan2";
+ gpios = <&gpio0 25 GPIO_ACTIVE_LOW>;
+ };
+ };
+};
+
+&sdhci {
+ status = "okay";
+};
+
+&spi0 {
+ status = "okay";
+
+ m25p80@0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "jedec,spi-nor";
+ reg = <0>;
+ spi-max-frequency = <10000000>;
+ m25p,chunked-io = <32>;
+
+ partition@0 {
+ label = "u-boot";
+ reg = <0x0 0x30000>;
+ read-only;
+ };
+
+ partition@30000 {
+ label = "u-boot-env";
+ reg = <0x30000 0x10000>;
+ read-only;
+ };
+
+ factory: partition@40000 {
+ label = "factory";
+ reg = <0x40000 0x10000>;
+ read-only;
+ };
+
+ partition@50000 {
+ label = "firmware";
+ reg = <0x50000 0xFB0000>;
+ };
+
+ };
+};
+
+&sysclock {
+ compatible = "fixed-clock";
+ clock-frequency = <90000000>;
+};
+
+&cpuclock {
+ compatible = "fixed-clock";
+ clock-frequency = <900000000>;
+};
+
+&pcie {
+ status = "okay";
+};
+
+&ethernet {
+ //mtd-mac-address = <&factory 0xe000>;
+ gmac1: mac@0 {
+ compatible = "mediatek,eth-mac";
+ reg = <0>;
+ phy-handle = <&phy1>;
+ };
+
+ mdio-bus {
+ phy1: ethernet-phy@1 {
+ reg = <1>;
+ phy-mode = "rgmii";
+ };
+ };
+};
+
+&pinctrl {
+ state_default: pinctrl0 {
+ gpio {
+ ralink,group = "wdt", "rgmii2", "uart3";
+ ralink,function = "gpio";
+ };
+ };
+};
diff --git a/drivers/staging/mt7621-dts/mt7621.dtsi b/drivers/staging/mt7621-dts/mt7621.dtsi
new file mode 100644
index 000000000000..ebcaa8b1fc81
--- /dev/null
+++ b/drivers/staging/mt7621-dts/mt7621.dtsi
@@ -0,0 +1,471 @@
+#include <dt-bindings/interrupt-controller/mips-gic.h>
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "mediatek,mt7621-soc";
+
+ cpus {
+ cpu@0 {
+ compatible = "mips,mips1004Kc";
+ };
+
+ cpu@1 {
+ compatible = "mips,mips1004Kc";
+ };
+ };
+
+ cpuintc: cpuintc@0 {
+ #address-cells = <0>;
+ #interrupt-cells = <1>;
+ interrupt-controller;
+ compatible = "mti,cpu-interrupt-controller";
+ };
+
+ aliases {
+ serial0 = &uartlite;
+ };
+
+ cpuclock: cpuclock@0 {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+
+ /* FIXME: there should be way to detect this */
+ clock-frequency = <880000000>;
+ };
+
+ sysclock: sysclock@0 {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+
+ /* FIXME: there should be way to detect this */
+ clock-frequency = <50000000>;
+ };
+
+ palmbus: palmbus@1E000000 {
+ compatible = "palmbus";
+ reg = <0x1E000000 0x100000>;
+ ranges = <0x0 0x1E000000 0x0FFFFF>;
+
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ sysc: sysc@0 {
+ compatible = "mtk,mt7621-sysc";
+ reg = <0x0 0x100>;
+ };
+
+ wdt: wdt@100 {
+ compatible = "mtk,mt7621-wdt";
+ reg = <0x100 0x100>;
+ };
+
+ gpio@600 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ compatible = "mtk,mt7621-gpio";
+ reg = <0x600 0x100>;
+
+ gpio0: bank@0 {
+ reg = <0>;
+ compatible = "mtk,mt7621-gpio-bank";
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ gpio1: bank@1 {
+ reg = <1>;
+ compatible = "mtk,mt7621-gpio-bank";
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ gpio2: bank@2 {
+ reg = <2>;
+ compatible = "mtk,mt7621-gpio-bank";
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+ };
+
+ i2c: i2c@900 {
+ compatible = "mediatek,mt7621-i2c";
+ reg = <0x900 0x100>;
+
+ clocks = <&sysclock>;
+
+ resets = <&rstctrl 16>;
+ reset-names = "i2c";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ status = "disabled";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c_pins>;
+ };
+
+ i2s: i2s@a00 {
+ compatible = "mediatek,mt7621-i2s";
+ reg = <0xa00 0x100>;
+
+ clocks = <&sysclock>;
+
+ resets = <&rstctrl 17>;
+ reset-names = "i2s";
+
+ interrupt-parent = <&gic>;
+ interrupts = <GIC_SHARED 16 IRQ_TYPE_LEVEL_HIGH>;
+
+ txdma-req = <2>;
+ rxdma-req = <3>;
+
+ dmas = <&gdma 4>,
+ <&gdma 6>;
+ dma-names = "tx", "rx";
+
+ status = "disabled";
+ };
+
+ memc: memc@5000 {
+ compatible = "mtk,mt7621-memc";
+ reg = <0x300 0x100>;
+ };
+
+ cpc: cpc@1fbf0000 {
+ compatible = "mtk,mt7621-cpc";
+ reg = <0x1fbf0000 0x8000>;
+ };
+
+ mc: mc@1fbf8000 {
+ compatible = "mtk,mt7621-mc";
+ reg = <0x1fbf8000 0x8000>;
+ };
+
+ uartlite: uartlite@c00 {
+ compatible = "ns16550a";
+ reg = <0xc00 0x100>;
+
+ clocks = <&sysclock>;
+ clock-frequency = <50000000>;
+
+ interrupt-parent = <&gic>;
+ interrupts = <GIC_SHARED 26 IRQ_TYPE_LEVEL_HIGH>;
+
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ no-loopback-test;
+ };
+
+ spi0: spi@b00 {
+ status = "disabled";
+
+ compatible = "ralink,mt7621-spi";
+ reg = <0xb00 0x100>;
+
+ clocks = <&sysclock>;
+
+ resets = <&rstctrl 18>;
+ reset-names = "spi";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&spi_pins>;
+ };
+
+ gdma: gdma@2800 {
+ compatible = "ralink,rt3883-gdma";
+ reg = <0x2800 0x800>;
+
+ resets = <&rstctrl 14>;
+ reset-names = "dma";
+
+ interrupt-parent = <&gic>;
+ interrupts = <0 13 4>;
+
+ #dma-cells = <1>;
+ #dma-channels = <16>;
+ #dma-requests = <16>;
+
+ status = "disabled";
+ };
+
+ hsdma: hsdma@7000 {
+ compatible = "mediatek,mt7621-hsdma";
+ reg = <0x7000 0x1000>;
+
+ resets = <&rstctrl 5>;
+ reset-names = "hsdma";
+
+ interrupt-parent = <&gic>;
+ interrupts = <0 11 4>;
+
+ #dma-cells = <1>;
+ #dma-channels = <1>;
+ #dma-requests = <1>;
+
+ status = "disabled";
+ };
+ };
+
+ pinctrl: pinctrl {
+ compatible = "ralink,rt2880-pinmux";
+ pinctrl-names = "default";
+ pinctrl-0 = <&state_default>;
+
+ state_default: pinctrl0 {
+ };
+
+ i2c_pins: i2c {
+ i2c {
+ ralink,group = "i2c";
+ ralink,function = "i2c";
+ };
+ };
+
+ spi_pins: spi {
+ spi {
+ ralink,group = "spi";
+ ralink,function = "spi";
+ };
+ };
+
+ uart1_pins: uart1 {
+ uart1 {
+ ralink,group = "uart1";
+ ralink,function = "uart1";
+ };
+ };
+
+ uart2_pins: uart2 {
+ uart2 {
+ ralink,group = "uart2";
+ ralink,function = "uart2";
+ };
+ };
+
+ uart3_pins: uart3 {
+ uart3 {
+ ralink,group = "uart3";
+ ralink,function = "uart3";
+ };
+ };
+
+ rgmii1_pins: rgmii1 {
+ rgmii1 {
+ ralink,group = "rgmii1";
+ ralink,function = "rgmii1";
+ };
+ };
+
+ rgmii2_pins: rgmii2 {
+ rgmii2 {
+ ralink,group = "rgmii2";
+ ralink,function = "rgmii2";
+ };
+ };
+
+ mdio_pins: mdio {
+ mdio {
+ ralink,group = "mdio";
+ ralink,function = "mdio";
+ };
+ };
+
+ pcie_pins: pcie {
+ pcie {
+ ralink,group = "pcie";
+ ralink,function = "pcie rst";
+ };
+ };
+
+ nand_pins: nand {
+ spi-nand {
+ ralink,group = "spi";
+ ralink,function = "nand1";
+ };
+
+ sdhci-nand {
+ ralink,group = "sdhci";
+ ralink,function = "nand2";
+ };
+ };
+
+ sdhci_pins: sdhci {
+ sdhci {
+ ralink,group = "sdhci";
+ ralink,function = "sdhci";
+ };
+ };
+ };
+
+ rstctrl: rstctrl {
+ compatible = "ralink,rt2880-reset";
+ #reset-cells = <1>;
+ };
+
+ clkctrl: clkctrl {
+ compatible = "ralink,rt2880-clock";
+ #clock-cells = <1>;
+ };
+
+ sdhci: sdhci@1E130000 {
+ status = "disabled";
+
+ compatible = "ralink,mt7620-sdhci";
+ reg = <0x1E130000 0x4000>;
+
+ interrupt-parent = <&gic>;
+ interrupts = <GIC_SHARED 20 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ xhci: xhci@1E1C0000 {
+ status = "okay";
+
+ compatible = "mediatek,mt8173-xhci";
+ reg = <0x1e1c0000 0x1000
+ 0x1e1d0700 0x0100>;
+ reg-names = "mac", "ippc";
+
+ clocks = <&sysclock>;
+ clock-names = "sys_ck";
+
+ interrupt-parent = <&gic>;
+ interrupts = <GIC_SHARED 22 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ gic: interrupt-controller@1fbc0000 {
+ compatible = "mti,gic";
+ reg = <0x1fbc0000 0x2000>;
+
+ interrupt-controller;
+ #interrupt-cells = <3>;
+
+ mti,reserved-cpu-vectors = <7>;
+
+ timer {
+ compatible = "mti,gic-timer";
+ interrupts = <GIC_LOCAL 1 IRQ_TYPE_NONE>;
+ clocks = <&cpuclock>;
+ };
+ };
+
+ nand: nand@1e003000 {
+ status = "disabled";
+
+ compatible = "mtk,mt7621-nand";
+ bank-width = <2>;
+ reg = <0x1e003000 0x800
+ 0x1e003800 0x800>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ };
+
+ ethsys: syscon@1e000000 {
+ compatible = "mediatek,mt7621-ethsys",
+ "syscon";
+ reg = <0x1e000000 0x1000>;
+ #clock-cells = <1>;
+ };
+
+ ethernet: ethernet@1e100000 {
+ compatible = "mediatek,mt7621-eth";
+ reg = <0x1e100000 0x10000>;
+
+ clocks = <&sysclock>;
+ clock-names = "ethif";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ resets = <&rstctrl 6 &rstctrl 23>;
+ reset-names = "fe", "eth";
+
+ interrupt-parent = <&gic>;
+ interrupts = <GIC_SHARED 3 IRQ_TYPE_LEVEL_HIGH>;
+
+ mediatek,ethsys = <&ethsys>;
+
+ mediatek,switch = <&gsw>;
+
+ mdio-bus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ phy1f: ethernet-phy@1f {
+ reg = <0x1f>;
+ phy-mode = "rgmii";
+ };
+ };
+ };
+
+ gsw: gsw@1e110000 {
+ compatible = "mediatek,mt7621-gsw";
+ reg = <0x1e110000 0x8000>;
+ interrupt-parent = <&gic>;
+ interrupts = <GIC_SHARED 23 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ pcie: pcie@1e140000 {
+ compatible = "mediatek,mt7621-pci";
+ reg = <0x1e140000 0x100
+ 0x1e142000 0x100>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&pcie_pins>;
+
+ device_type = "pci";
+
+ bus-range = <0 255>;
+ ranges = <
+ 0x02000000 0 0x00000000 0x60000000 0 0x10000000 /* pci memory */
+ 0x01000000 0 0x00000000 0x1e160000 0 0x00010000 /* io space */
+ >;
+
+ interrupt-parent = <&gic>;
+ interrupts = <GIC_SHARED 4 IRQ_TYPE_LEVEL_HIGH
+ GIC_SHARED 24 IRQ_TYPE_LEVEL_HIGH
+ GIC_SHARED 25 IRQ_TYPE_LEVEL_HIGH>;
+
+ status = "disabled";
+
+ resets = <&rstctrl 24 &rstctrl 25 &rstctrl 26>;
+ reset-names = "pcie0", "pcie1", "pcie2";
+ clocks = <&clkctrl 24 &clkctrl 25 &clkctrl 26>;
+ clock-names = "pcie0", "pcie1", "pcie2";
+
+ pcie0 {
+ reg = <0x0000 0 0 0 0>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ device_type = "pci";
+ };
+
+ pcie1 {
+ reg = <0x0800 0 0 0 0>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ device_type = "pci";
+ };
+
+ pcie2 {
+ reg = <0x1000 0 0 0 0>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ device_type = "pci";
+ };
+ };
+};
diff --git a/drivers/staging/mt7621-eth/Documentation/devicetree/bindings/net/mediatek-net-gsw.txt b/drivers/staging/mt7621-eth/Documentation/devicetree/bindings/net/mediatek-net-gsw.txt
new file mode 100644
index 000000000000..596b38552697
--- /dev/null
+++ b/drivers/staging/mt7621-eth/Documentation/devicetree/bindings/net/mediatek-net-gsw.txt
@@ -0,0 +1,48 @@
+Mediatek Gigabit Switch
+=======================
+
+The mediatek gigabit switch can be found on Mediatek SoCs.
+
+Required properties:
+- compatible: Should be "mediatek,mt7620-gsw", "mediatek,mt7621-gsw",
+ "mediatek,mt7623-gsw"
+- reg: Address and length of the register set for the device
+- interrupts: Should contain the gigabit switches interrupt
+
+
+Additional required properties for ARM based SoCs:
+- mediatek,reset-pin: phandle describing the reset GPIO
+- clocks: the clocks used by the switch
+- clock-names: the names of the clocks listed in the clocks property
+ these should be "trgpll", "esw", "gp2", "gp1"
+- mt7530-supply: the phandle of the regulator used to power the switch
+- mediatek,pctl-regmap: phandle to the port control regmap. this is used to
+ setup the drive current
+
+
+Optional properties:
+- interrupt-parent: Should be the phandle for the interrupt controller
+ that services interrupts for this device
+
+Example:
+
+gsw: switch@1b100000 {
+ compatible = "mediatek,mt7623-gsw";
+ reg = <0 0x1b110000 0 0x300000>;
+
+ interrupt-parent = <&pio>;
+ interrupts = <168 IRQ_TYPE_EDGE_RISING>;
+
+ clocks = <&apmixedsys CLK_APMIXED_TRGPLL>,
+ <&ethsys CLK_ETHSYS_ESW>,
+ <&ethsys CLK_ETHSYS_GP2>,
+ <&ethsys CLK_ETHSYS_GP1>;
+ clock-names = "trgpll", "esw", "gp2", "gp1";
+
+ mt7530-supply = <&mt6323_vpa_reg>;
+
+ mediatek,pctl-regmap = <&syscfg_pctl_a>;
+ mediatek,reset-pin = <&pio 15 0>;
+
+ status = "okay";
+};
diff --git a/drivers/staging/mt7621-eth/Kconfig b/drivers/staging/mt7621-eth/Kconfig
new file mode 100644
index 000000000000..44ea86c7a96c
--- /dev/null
+++ b/drivers/staging/mt7621-eth/Kconfig
@@ -0,0 +1,39 @@
+config NET_VENDOR_MEDIATEK_STAGING
+ bool "MediaTek ethernet driver - staging version"
+ depends on RALINK
+ ---help---
+ If you have an MT7621 Mediatek SoC with ethernet, say Y.
+
+if NET_VENDOR_MEDIATEK_STAGING
+choice
+ prompt "MAC type"
+
+config NET_MEDIATEK_MT7621
+ bool "MT7621"
+ depends on MIPS && SOC_MT7621
+
+endchoice
+
+config NET_MEDIATEK_SOC_STAGING
+ tristate "MediaTek SoC Gigabit Ethernet support"
+ depends on NET_VENDOR_MEDIATEK_STAGING
+ select PHYLIB
+ ---help---
+ This driver supports the gigabit ethernet MACs in the
+ MediaTek SoC family.
+
+config NET_MEDIATEK_MDIO
+ def_bool NET_MEDIATEK_SOC_STAGING
+ depends on NET_MEDIATEK_MT7621
+ select PHYLIB
+
+config NET_MEDIATEK_MDIO_MT7620
+ def_bool NET_MEDIATEK_SOC_STAGING
+ depends on NET_MEDIATEK_MT7621
+ select NET_MEDIATEK_MDIO
+
+config NET_MEDIATEK_GSW_MT7621
+ def_tristate NET_MEDIATEK_SOC_STAGING
+ depends on NET_MEDIATEK_MT7621
+
+endif #NET_VENDOR_MEDIATEK_STAGING
diff --git a/drivers/staging/mt7621-eth/Makefile b/drivers/staging/mt7621-eth/Makefile
new file mode 100644
index 000000000000..018bcc3596b3
--- /dev/null
+++ b/drivers/staging/mt7621-eth/Makefile
@@ -0,0 +1,14 @@
+#
+# Makefile for the Ralink SoCs built-in ethernet macs
+#
+
+mtk-eth-soc-y += mtk_eth_soc.o ethtool.o
+
+mtk-eth-soc-$(CONFIG_NET_MEDIATEK_MDIO) += mdio.o
+mtk-eth-soc-$(CONFIG_NET_MEDIATEK_MDIO_MT7620) += mdio_mt7620.o
+
+mtk-eth-soc-$(CONFIG_NET_MEDIATEK_MT7621) += soc_mt7621.o
+
+obj-$(CONFIG_NET_MEDIATEK_GSW_MT7621) += gsw_mt7621.o
+
+obj-$(CONFIG_NET_MEDIATEK_SOC_STAGING) += mtk-eth-soc.o
diff --git a/drivers/staging/mt7621-eth/TODO b/drivers/staging/mt7621-eth/TODO
new file mode 100644
index 000000000000..f9e47d4b4cd4
--- /dev/null
+++ b/drivers/staging/mt7621-eth/TODO
@@ -0,0 +1,13 @@
+
+- verify devicetree documentation is consistent with code
+- fix ethtool - currently doesn't return valid data.
+- general code review and clean up
+- add support for second MAC on mt7621
+- convert gsw code to use switchdev interfaces
+- md7620_mmi_write etc should probably be wrapped
+ in a regmap abstraction.
+- Get soc_mt7621 to work with QDMA TX if possible.
+- Ensure phys are correctly configured when a cable
+ is plugged in.
+
+Cc: NeilBrown <neil@brown.name>
diff --git a/drivers/staging/mt7621-eth/ethtool.c b/drivers/staging/mt7621-eth/ethtool.c
new file mode 100644
index 000000000000..38ba0c040aba
--- /dev/null
+++ b/drivers/staging/mt7621-eth/ethtool.c
@@ -0,0 +1,225 @@
+/* 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 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
+ */
+
+#include "mtk_eth_soc.h"
+
+static const char mtk_gdma_str[][ETH_GSTRING_LEN] = {
+#define _FE(x...) # x,
+MTK_STAT_REG_DECLARE
+#undef _FE
+};
+
+static int mtk_get_link_ksettings(struct net_device *dev,
+ struct ethtool_link_ksettings *cmd)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ int err;
+
+ if (!mac->phy_dev)
+ return -ENODEV;
+
+ if (mac->phy_flags == MTK_PHY_FLAG_ATTACH) {
+ err = phy_read_status(mac->phy_dev);
+ if (err)
+ return -ENODEV;
+ }
+
+ phy_ethtool_ksettings_get(mac->phy_dev, cmd);
+ return 0;
+}
+
+static int mtk_set_link_ksettings(struct net_device *dev,
+ const struct ethtool_link_ksettings *cmd)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+
+ if (!mac->phy_dev)
+ return -ENODEV;
+
+ if (cmd->base.phy_address != mac->phy_dev->mdio.addr) {
+ if (mac->hw->phy->phy_node[cmd->base.phy_address]) {
+ mac->phy_dev = mac->hw->phy->phy[cmd->base.phy_address];
+ mac->phy_flags = MTK_PHY_FLAG_PORT;
+ } else if (mac->hw->mii_bus) {
+ mac->phy_dev = mdiobus_get_phy(mac->hw->mii_bus, cmd->base.phy_address);
+ if (!mac->phy_dev)
+ return -ENODEV;
+ mac->phy_flags = MTK_PHY_FLAG_ATTACH;
+ } else {
+ return -ENODEV;
+ }
+ }
+
+ return phy_ethtool_ksettings_set(mac->phy_dev, cmd);
+
+}
+
+static void mtk_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_soc_data *soc = mac->hw->soc;
+
+ strlcpy(info->driver, mac->hw->dev->driver->name, sizeof(info->driver));
+ strlcpy(info->bus_info, dev_name(mac->hw->dev), sizeof(info->bus_info));
+
+ if (soc->reg_table[MTK_REG_MTK_COUNTER_BASE])
+ info->n_stats = ARRAY_SIZE(mtk_gdma_str);
+}
+
+static u32 mtk_get_msglevel(struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+
+ return mac->hw->msg_enable;
+}
+
+static void mtk_set_msglevel(struct net_device *dev, u32 value)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+
+ mac->hw->msg_enable = value;
+}
+
+static int mtk_nway_reset(struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+
+ if (!mac->phy_dev)
+ return -EOPNOTSUPP;
+
+ return genphy_restart_aneg(mac->phy_dev);
+}
+
+static u32 mtk_get_link(struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ int err;
+
+ if (!mac->phy_dev)
+ goto out_get_link;
+
+ if (mac->phy_flags == MTK_PHY_FLAG_ATTACH) {
+ err = genphy_update_link(mac->phy_dev);
+ if (err)
+ goto out_get_link;
+ }
+
+ return mac->phy_dev->link;
+
+out_get_link:
+ return ethtool_op_get_link(dev);
+}
+
+static int mtk_set_ringparam(struct net_device *dev,
+ struct ethtool_ringparam *ring)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+
+ if ((ring->tx_pending < 2) ||
+ (ring->rx_pending < 2) ||
+ (ring->rx_pending > mac->hw->soc->dma_ring_size) ||
+ (ring->tx_pending > mac->hw->soc->dma_ring_size))
+ return -EINVAL;
+
+ dev->netdev_ops->ndo_stop(dev);
+
+ mac->hw->tx_ring.tx_ring_size = BIT(fls(ring->tx_pending) - 1);
+ mac->hw->rx_ring[0].rx_ring_size = BIT(fls(ring->rx_pending) - 1);
+
+ return dev->netdev_ops->ndo_open(dev);
+}
+
+static void mtk_get_ringparam(struct net_device *dev,
+ struct ethtool_ringparam *ring)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+
+ ring->rx_max_pending = mac->hw->soc->dma_ring_size;
+ ring->tx_max_pending = mac->hw->soc->dma_ring_size;
+ ring->rx_pending = mac->hw->rx_ring[0].rx_ring_size;
+ ring->tx_pending = mac->hw->tx_ring.tx_ring_size;
+}
+
+static void mtk_get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+ switch (stringset) {
+ case ETH_SS_STATS:
+ memcpy(data, *mtk_gdma_str, sizeof(mtk_gdma_str));
+ break;
+ }
+}
+
+static int mtk_get_sset_count(struct net_device *dev, int sset)
+{
+ switch (sset) {
+ case ETH_SS_STATS:
+ return ARRAY_SIZE(mtk_gdma_str);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static void mtk_get_ethtool_stats(struct net_device *dev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_hw_stats *hwstats = mac->hw_stats;
+ u64 *data_src, *data_dst;
+ unsigned int start;
+ int i;
+
+ if (netif_running(dev) && netif_device_present(dev)) {
+ if (spin_trylock(&hwstats->stats_lock)) {
+ mtk_stats_update_mac(mac);
+ spin_unlock(&hwstats->stats_lock);
+ }
+ }
+
+ do {
+ data_src = &hwstats->tx_bytes;
+ data_dst = data;
+ start = u64_stats_fetch_begin_irq(&hwstats->syncp);
+
+ for (i = 0; i < ARRAY_SIZE(mtk_gdma_str); i++)
+ *data_dst++ = *data_src++;
+
+ } while (u64_stats_fetch_retry_irq(&hwstats->syncp, start));
+}
+
+static struct ethtool_ops mtk_ethtool_ops = {
+ .get_link_ksettings = mtk_get_link_ksettings,
+ .set_link_ksettings = mtk_set_link_ksettings,
+ .get_drvinfo = mtk_get_drvinfo,
+ .get_msglevel = mtk_get_msglevel,
+ .set_msglevel = mtk_set_msglevel,
+ .nway_reset = mtk_nway_reset,
+ .get_link = mtk_get_link,
+ .set_ringparam = mtk_set_ringparam,
+ .get_ringparam = mtk_get_ringparam,
+};
+
+void mtk_set_ethtool_ops(struct net_device *netdev)
+{
+ struct mtk_mac *mac = netdev_priv(netdev);
+ struct mtk_soc_data *soc = mac->hw->soc;
+
+ if (soc->reg_table[MTK_REG_MTK_COUNTER_BASE]) {
+ mtk_ethtool_ops.get_strings = mtk_get_strings;
+ mtk_ethtool_ops.get_sset_count = mtk_get_sset_count;
+ mtk_ethtool_ops.get_ethtool_stats = mtk_get_ethtool_stats;
+ }
+
+ netdev->ethtool_ops = &mtk_ethtool_ops;
+}
diff --git a/drivers/staging/mt7621-eth/ethtool.h b/drivers/staging/mt7621-eth/ethtool.h
new file mode 100644
index 000000000000..40b4cf011660
--- /dev/null
+++ b/drivers/staging/mt7621-eth/ethtool.h
@@ -0,0 +1,22 @@
+/* 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 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
+ */
+
+#ifndef MTK_ETHTOOL_H
+#define MTK_ETHTOOL_H
+
+#include <linux/ethtool.h>
+
+void mtk_set_ethtool_ops(struct net_device *netdev);
+
+#endif /* MTK_ETHTOOL_H */
diff --git a/drivers/staging/mt7621-eth/gsw_mt7620.h b/drivers/staging/mt7621-eth/gsw_mt7620.h
new file mode 100644
index 000000000000..1766939e2101
--- /dev/null
+++ b/drivers/staging/mt7621-eth/gsw_mt7620.h
@@ -0,0 +1,277 @@
+/* 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 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
+ */
+
+#ifndef _RALINK_GSW_MT7620_H__
+#define _RALINK_GSW_MT7620_H__
+
+#define GSW_REG_PHY_TIMEOUT (5 * HZ)
+
+#define MT7620_GSW_REG_PIAC 0x0004
+
+#define GSW_NUM_VLANS 16
+#define GSW_NUM_VIDS 4096
+#define GSW_NUM_PORTS 7
+#define GSW_PORT6 6
+
+#define GSW_MDIO_ACCESS BIT(31)
+#define GSW_MDIO_READ BIT(19)
+#define GSW_MDIO_WRITE BIT(18)
+#define GSW_MDIO_START BIT(16)
+#define GSW_MDIO_ADDR_SHIFT 20
+#define GSW_MDIO_REG_SHIFT 25
+
+#define GSW_REG_PORT_PMCR(x) (0x3000 + (x * 0x100))
+#define GSW_REG_PORT_STATUS(x) (0x3008 + (x * 0x100))
+#define GSW_REG_SMACCR0 0x3fE4
+#define GSW_REG_SMACCR1 0x3fE8
+#define GSW_REG_CKGCR 0x3ff0
+
+#define GSW_REG_IMR 0x7008
+#define GSW_REG_ISR 0x700c
+#define GSW_REG_GPC1 0x7014
+
+#define SYSC_REG_CHIP_REV_ID 0x0c
+#define SYSC_REG_CFG 0x10
+#define SYSC_REG_CFG1 0x14
+#define RST_CTRL_MCM BIT(2)
+#define SYSC_PAD_RGMII2_MDIO 0x58
+#define SYSC_GPIO_MODE 0x60
+
+#define PORT_IRQ_ST_CHG 0x7f
+
+#define MT7621_ESW_PHY_POLLING 0x0000
+#define MT7620_ESW_PHY_POLLING 0x7000
+
+#define PMCR_IPG BIT(18)
+#define PMCR_MAC_MODE BIT(16)
+#define PMCR_FORCE BIT(15)
+#define PMCR_TX_EN BIT(14)
+#define PMCR_RX_EN BIT(13)
+#define PMCR_BACKOFF BIT(9)
+#define PMCR_BACKPRES BIT(8)
+#define PMCR_RX_FC BIT(5)
+#define PMCR_TX_FC BIT(4)
+#define PMCR_SPEED(_x) (_x << 2)
+#define PMCR_DUPLEX BIT(1)
+#define PMCR_LINK BIT(0)
+
+#define PHY_AN_EN BIT(31)
+#define PHY_PRE_EN BIT(30)
+#define PMY_MDC_CONF(_x) ((_x & 0x3f) << 24)
+
+/* ethernet subsystem config register */
+#define ETHSYS_SYSCFG0 0x14
+/* ethernet subsystem clock register */
+#define ETHSYS_CLKCFG0 0x2c
+#define ETHSYS_TRGMII_CLK_SEL362_5 BIT(11)
+
+/* p5 RGMII wrapper TX clock control register */
+#define MT7530_P5RGMIITXCR 0x7b04
+/* p5 RGMII wrapper RX clock control register */
+#define MT7530_P5RGMIIRXCR 0x7b00
+/* TRGMII TDX ODT registers */
+#define MT7530_TRGMII_TD0_ODT 0x7a54
+#define MT7530_TRGMII_TD1_ODT 0x7a5c
+#define MT7530_TRGMII_TD2_ODT 0x7a64
+#define MT7530_TRGMII_TD3_ODT 0x7a6c
+#define MT7530_TRGMII_TD4_ODT 0x7a74
+#define MT7530_TRGMII_TD5_ODT 0x7a7c
+/* TRGMII TCK ctrl register */
+#define MT7530_TRGMII_TCK_CTRL 0x7a78
+/* TRGMII Tx ctrl register */
+#define MT7530_TRGMII_TXCTRL 0x7a40
+/* port 6 extended control register */
+#define MT7530_P6ECR 0x7830
+/* IO driver control register */
+#define MT7530_IO_DRV_CR 0x7810
+/* top signal control register */
+#define MT7530_TOP_SIG_CTRL 0x7808
+/* modified hwtrap register */
+#define MT7530_MHWTRAP 0x7804
+/* hwtrap status register */
+#define MT7530_HWTRAP 0x7800
+/* status interrupt register */
+#define MT7530_SYS_INT_STS 0x700c
+/* system nterrupt register */
+#define MT7530_SYS_INT_EN 0x7008
+/* system control register */
+#define MT7530_SYS_CTRL 0x7000
+/* port MAC status register */
+#define MT7530_PMSR_P(x) (0x3008 + (x * 0x100))
+/* port MAC control register */
+#define MT7530_PMCR_P(x) (0x3000 + (x * 0x100))
+
+#define MT7621_XTAL_SHIFT 6
+#define MT7621_XTAL_MASK 0x7
+#define MT7621_XTAL_25 6
+#define MT7621_XTAL_40 3
+#define MT7621_MDIO_DRV_MASK (3 << 4)
+#define MT7621_GE1_MODE_MASK (3 << 12)
+
+#define TRGMII_TXCTRL_TXC_INV BIT(30)
+#define P6ECR_INTF_MODE_RGMII BIT(1)
+#define P5RGMIIRXCR_C_ALIGN BIT(8)
+#define P5RGMIIRXCR_DELAY_2 BIT(1)
+#define P5RGMIITXCR_DELAY_2 (BIT(8) | BIT(2))
+
+/* TOP_SIG_CTRL bits */
+#define TOP_SIG_CTRL_NORMAL (BIT(17) | BIT(16))
+
+/* MHWTRAP bits */
+#define MHWTRAP_MANUAL BIT(16)
+#define MHWTRAP_P5_MAC_SEL BIT(13)
+#define MHWTRAP_P6_DIS BIT(8)
+#define MHWTRAP_P5_RGMII_MODE BIT(7)
+#define MHWTRAP_P5_DIS BIT(6)
+#define MHWTRAP_PHY_ACCESS BIT(5)
+
+/* HWTRAP bits */
+#define HWTRAP_XTAL_SHIFT 9
+#define HWTRAP_XTAL_MASK 0x3
+
+/* SYS_CTRL bits */
+#define SYS_CTRL_SW_RST BIT(1)
+#define SYS_CTRL_REG_RST BIT(0)
+
+/* PMCR bits */
+#define PMCR_IFG_XMIT_96 BIT(18)
+#define PMCR_MAC_MODE BIT(16)
+#define PMCR_FORCE_MODE BIT(15)
+#define PMCR_TX_EN BIT(14)
+#define PMCR_RX_EN BIT(13)
+#define PMCR_BACK_PRES_EN BIT(9)
+#define PMCR_BACKOFF_EN BIT(8)
+#define PMCR_TX_FC_EN BIT(5)
+#define PMCR_RX_FC_EN BIT(4)
+#define PMCR_FORCE_SPEED_1000 BIT(3)
+#define PMCR_FORCE_FDX BIT(1)
+#define PMCR_FORCE_LNK BIT(0)
+#define PMCR_FIXED_LINK (PMCR_IFG_XMIT_96 | PMCR_MAC_MODE | \
+ PMCR_FORCE_MODE | PMCR_TX_EN | PMCR_RX_EN | \
+ PMCR_BACK_PRES_EN | PMCR_BACKOFF_EN | \
+ PMCR_FORCE_SPEED_1000 | PMCR_FORCE_FDX | \
+ PMCR_FORCE_LNK)
+
+#define PMCR_FIXED_LINK_FC (PMCR_FIXED_LINK | \
+ PMCR_TX_FC_EN | PMCR_RX_FC_EN)
+
+/* TRGMII control registers */
+#define GSW_INTF_MODE 0x390
+#define GSW_TRGMII_TD0_ODT 0x354
+#define GSW_TRGMII_TD1_ODT 0x35c
+#define GSW_TRGMII_TD2_ODT 0x364
+#define GSW_TRGMII_TD3_ODT 0x36c
+#define GSW_TRGMII_TXCTL_ODT 0x374
+#define GSW_TRGMII_TCK_ODT 0x37c
+#define GSW_TRGMII_RCK_CTRL 0x300
+
+#define INTF_MODE_TRGMII BIT(1)
+#define TRGMII_RCK_CTRL_RX_RST BIT(31)
+
+/* Mac control registers */
+#define MTK_MAC_P2_MCR 0x200
+#define MTK_MAC_P1_MCR 0x100
+
+#define MAC_MCR_MAX_RX_2K BIT(29)
+#define MAC_MCR_IPG_CFG (BIT(18) | BIT(16))
+#define MAC_MCR_FORCE_MODE BIT(15)
+#define MAC_MCR_TX_EN BIT(14)
+#define MAC_MCR_RX_EN BIT(13)
+#define MAC_MCR_BACKOFF_EN BIT(9)
+#define MAC_MCR_BACKPR_EN BIT(8)
+#define MAC_MCR_FORCE_RX_FC BIT(5)
+#define MAC_MCR_FORCE_TX_FC BIT(4)
+#define MAC_MCR_SPEED_1000 BIT(3)
+#define MAC_MCR_FORCE_DPX BIT(1)
+#define MAC_MCR_FORCE_LINK BIT(0)
+#define MAC_MCR_FIXED_LINK (MAC_MCR_MAX_RX_2K | MAC_MCR_IPG_CFG | \
+ MAC_MCR_FORCE_MODE | MAC_MCR_TX_EN | \
+ MAC_MCR_RX_EN | MAC_MCR_BACKOFF_EN | \
+ MAC_MCR_BACKPR_EN | MAC_MCR_FORCE_RX_FC | \
+ MAC_MCR_FORCE_TX_FC | MAC_MCR_SPEED_1000 | \
+ MAC_MCR_FORCE_DPX | MAC_MCR_FORCE_LINK)
+#define MAC_MCR_FIXED_LINK_FC (MAC_MCR_MAX_RX_2K | MAC_MCR_IPG_CFG | \
+ MAC_MCR_FIXED_LINK)
+
+/* possible XTAL speed */
+#define MT7623_XTAL_40 0
+#define MT7623_XTAL_20 1
+#define MT7623_XTAL_25 3
+
+/* GPIO port control registers */
+#define GPIO_OD33_CTRL8 0x4c0
+#define GPIO_BIAS_CTRL 0xed0
+#define GPIO_DRV_SEL10 0xf00
+
+/* on MT7620 the functio of port 4 can be software configured */
+enum {
+ PORT4_EPHY = 0,
+ PORT4_EXT,
+};
+
+/* struct mt7620_gsw - the structure that holds the SoC specific data
+ * @dev: The Device struct
+ * @base: The base address
+ * @piac_offset: The PIAC base may change depending on SoC
+ * @irq: The IRQ we are using
+ * @port4: The port4 mode on MT7620
+ * @autopoll: Is MDIO autopolling enabled
+ * @ethsys: The ethsys register map
+ * @pctl: The pin control register map
+ * @clk_gsw: The switch clock
+ * @clk_gp1: The gmac1 clock
+ * @clk_gp2: The gmac2 clock
+ * @clk_trgpll: The trgmii pll clock
+ */
+struct mt7620_gsw {
+ struct device *dev;
+ void __iomem *base;
+ u32 piac_offset;
+ int irq;
+ int port4;
+ unsigned long int autopoll;
+
+ struct regmap *ethsys;
+ struct regmap *pctl;
+
+ struct clk *clk_gsw;
+ struct clk *clk_gp1;
+ struct clk *clk_gp2;
+ struct clk *clk_trgpll;
+};
+
+/* switch register I/O wrappers */
+void mtk_switch_w32(struct mt7620_gsw *gsw, u32 val, unsigned reg);
+u32 mtk_switch_r32(struct mt7620_gsw *gsw, unsigned reg);
+
+/* the callback used by the driver core to bringup the switch */
+int mtk_gsw_init(struct mtk_eth *eth);
+
+/* MDIO access wrappers */
+int mt7620_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val);
+int mt7620_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg);
+void mt7620_mdio_link_adjust(struct mtk_eth *eth, int port);
+int mt7620_has_carrier(struct mtk_eth *eth);
+void mt7620_print_link_state(struct mtk_eth *eth, int port, int link,
+ int speed, int duplex);
+void mt7530_mdio_w32(struct mt7620_gsw *gsw, u32 reg, u32 val);
+u32 mt7530_mdio_r32(struct mt7620_gsw *gsw, u32 reg);
+void mt7530_mdio_m32(struct mt7620_gsw *gsw, u32 mask, u32 set, u32 reg);
+
+u32 _mt7620_mii_write(struct mt7620_gsw *gsw, u32 phy_addr,
+ u32 phy_register, u32 write_data);
+u32 _mt7620_mii_read(struct mt7620_gsw *gsw, int phy_addr, int phy_reg);
+void mt7620_handle_carrier(struct mtk_eth *eth);
+
+#endif
diff --git a/drivers/staging/mt7621-eth/gsw_mt7621.c b/drivers/staging/mt7621-eth/gsw_mt7621.c
new file mode 100644
index 000000000000..ce8d7d7577c7
--- /dev/null
+++ b/drivers/staging/mt7621-eth/gsw_mt7621.c
@@ -0,0 +1,298 @@
+/* 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 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+
+#include <ralink_regs.h>
+
+#include "mtk_eth_soc.h"
+#include "gsw_mt7620.h"
+
+void mtk_switch_w32(struct mt7620_gsw *gsw, u32 val, unsigned reg)
+{
+ iowrite32(val, gsw->base + reg);
+}
+EXPORT_SYMBOL_GPL(mtk_switch_w32);
+
+u32 mtk_switch_r32(struct mt7620_gsw *gsw, unsigned reg)
+{
+ return ioread32(gsw->base + reg);
+}
+EXPORT_SYMBOL_GPL(mtk_switch_r32);
+
+static irqreturn_t gsw_interrupt_mt7621(int irq, void *_eth)
+{
+ struct mtk_eth *eth = (struct mtk_eth *)_eth;
+ struct mt7620_gsw *gsw = (struct mt7620_gsw *)eth->sw_priv;
+ u32 reg, i;
+
+ reg = mt7530_mdio_r32(gsw, MT7530_SYS_INT_STS);
+
+ for (i = 0; i < 5; i++) {
+ unsigned int link;
+
+ if ((reg & BIT(i)) == 0)
+ continue;
+
+ link = mt7530_mdio_r32(gsw, MT7530_PMSR_P(i)) & 0x1;
+
+ if (link == eth->link[i])
+ continue;
+
+ eth->link[i] = link;
+ if (link)
+ netdev_info(*eth->netdev,
+ "port %d link up\n", i);
+ else
+ netdev_info(*eth->netdev,
+ "port %d link down\n", i);
+ }
+
+ mt7530_mdio_w32(gsw, MT7530_SYS_INT_STS, 0x1f);
+
+ return IRQ_HANDLED;
+}
+
+static void mt7621_hw_init(struct mtk_eth *eth, struct mt7620_gsw *gsw,
+ struct device_node *np)
+{
+ u32 i;
+ u32 val;
+
+ /* hardware reset the switch */
+ mtk_reset(eth, RST_CTRL_MCM);
+ mdelay(10);
+
+ /* reduce RGMII2 PAD driving strength */
+ rt_sysc_m32(MT7621_MDIO_DRV_MASK, 0, SYSC_PAD_RGMII2_MDIO);
+
+ /* gpio mux - RGMII1=Normal mode */
+ rt_sysc_m32(BIT(14), 0, SYSC_GPIO_MODE);
+
+ /* set GMAC1 RGMII mode */
+ rt_sysc_m32(MT7621_GE1_MODE_MASK, 0, SYSC_REG_CFG1);
+
+ /* enable MDIO to control MT7530 */
+ rt_sysc_m32(3 << 12, 0, SYSC_GPIO_MODE);
+
+ /* turn off all PHYs */
+ for (i = 0; i <= 4; i++) {
+ val = _mt7620_mii_read(gsw, i, 0x0);
+ val |= BIT(11);
+ _mt7620_mii_write(gsw, i, 0x0, val);
+ }
+
+ /* reset the switch */
+ mt7530_mdio_w32(gsw, MT7530_SYS_CTRL,
+ SYS_CTRL_SW_RST | SYS_CTRL_REG_RST);
+ usleep_range(10, 20);
+
+ if ((rt_sysc_r32(SYSC_REG_CHIP_REV_ID) & 0xFFFF) == 0x0101) {
+ /* GE1, Force 1000M/FD, FC ON, MAX_RX_LENGTH 1536 */
+ mtk_switch_w32(gsw, MAC_MCR_FIXED_LINK, MTK_MAC_P2_MCR);
+ mt7530_mdio_w32(gsw, MT7530_PMCR_P(6), PMCR_FIXED_LINK);
+ } else {
+ /* GE1, Force 1000M/FD, FC ON, MAX_RX_LENGTH 1536 */
+ mtk_switch_w32(gsw, MAC_MCR_FIXED_LINK_FC, MTK_MAC_P1_MCR);
+ mt7530_mdio_w32(gsw, MT7530_PMCR_P(6), PMCR_FIXED_LINK_FC);
+ }
+
+ /* GE2, Link down */
+ mtk_switch_w32(gsw, MAC_MCR_FORCE_MODE, MTK_MAC_P2_MCR);
+
+ /* Enable Port 6, P5 as GMAC5, P5 disable */
+ val = mt7530_mdio_r32(gsw, MT7530_MHWTRAP);
+ /* Enable Port 6 */
+ val &= ~MHWTRAP_P6_DIS;
+ /* Disable Port 5 */
+ val |= MHWTRAP_P5_DIS;
+ /* manual override of HW-Trap */
+ val |= MHWTRAP_MANUAL;
+ mt7530_mdio_w32(gsw, MT7530_MHWTRAP, val);
+
+ val = rt_sysc_r32(SYSC_REG_CFG);
+ val = (val >> MT7621_XTAL_SHIFT) & MT7621_XTAL_MASK;
+ if (val < MT7621_XTAL_25 && val >= MT7621_XTAL_40) {
+ /* 40Mhz */
+
+ /* disable MT7530 core clock */
+ _mt7620_mii_write(gsw, 0, 13, 0x1f);
+ _mt7620_mii_write(gsw, 0, 14, 0x410);
+ _mt7620_mii_write(gsw, 0, 13, 0x401f);
+ _mt7620_mii_write(gsw, 0, 14, 0x0);
+
+ /* disable MT7530 PLL */
+ _mt7620_mii_write(gsw, 0, 13, 0x1f);
+ _mt7620_mii_write(gsw, 0, 14, 0x40d);
+ _mt7620_mii_write(gsw, 0, 13, 0x401f);
+ _mt7620_mii_write(gsw, 0, 14, 0x2020);
+
+ /* for MT7530 core clock = 500Mhz */
+ _mt7620_mii_write(gsw, 0, 13, 0x1f);
+ _mt7620_mii_write(gsw, 0, 14, 0x40e);
+ _mt7620_mii_write(gsw, 0, 13, 0x401f);
+ _mt7620_mii_write(gsw, 0, 14, 0x119);
+
+ /* enable MT7530 PLL */
+ _mt7620_mii_write(gsw, 0, 13, 0x1f);
+ _mt7620_mii_write(gsw, 0, 14, 0x40d);
+ _mt7620_mii_write(gsw, 0, 13, 0x401f);
+ _mt7620_mii_write(gsw, 0, 14, 0x2820);
+
+ usleep_range(20, 40);
+
+ /* enable MT7530 core clock */
+ _mt7620_mii_write(gsw, 0, 13, 0x1f);
+ _mt7620_mii_write(gsw, 0, 14, 0x410);
+ _mt7620_mii_write(gsw, 0, 13, 0x401f);
+ }
+
+ /* RGMII */
+ _mt7620_mii_write(gsw, 0, 14, 0x1);
+
+ /* set MT7530 central align */
+ mt7530_mdio_m32(gsw, BIT(0), P6ECR_INTF_MODE_RGMII, MT7530_P6ECR);
+ mt7530_mdio_m32(gsw, TRGMII_TXCTRL_TXC_INV, 0,
+ MT7530_TRGMII_TXCTRL);
+ mt7530_mdio_w32(gsw, MT7530_TRGMII_TCK_CTRL, 0x855);
+
+ /* delay setting for 10/1000M */
+ mt7530_mdio_w32(gsw, MT7530_P5RGMIIRXCR,
+ P5RGMIIRXCR_C_ALIGN | P5RGMIIRXCR_DELAY_2);
+ mt7530_mdio_w32(gsw, MT7530_P5RGMIITXCR, 0x14);
+
+ /* lower Tx Driving*/
+ mt7530_mdio_w32(gsw, MT7530_TRGMII_TD0_ODT, 0x44);
+ mt7530_mdio_w32(gsw, MT7530_TRGMII_TD1_ODT, 0x44);
+ mt7530_mdio_w32(gsw, MT7530_TRGMII_TD2_ODT, 0x44);
+ mt7530_mdio_w32(gsw, MT7530_TRGMII_TD3_ODT, 0x44);
+ mt7530_mdio_w32(gsw, MT7530_TRGMII_TD4_ODT, 0x44);
+ mt7530_mdio_w32(gsw, MT7530_TRGMII_TD5_ODT, 0x44);
+
+ /* turn on all PHYs */
+ for (i = 0; i <= 4; i++) {
+ val = _mt7620_mii_read(gsw, i, 0);
+ val &= ~BIT(11);
+ _mt7620_mii_write(gsw, i, 0, val);
+ }
+
+#define MT7530_NUM_PORTS 8
+#define REG_ESW_PORT_PCR(x) (0x2004 | ((x) << 8))
+#define REG_ESW_PORT_PVC(x) (0x2010 | ((x) << 8))
+#define REG_ESW_PORT_PPBV1(x) (0x2014 | ((x) << 8))
+#define MT7530_CPU_PORT 6
+
+ /* This is copied from mt7530_apply_config in libreCMC driver */
+ {
+ int i;
+ for (i = 0; i < MT7530_NUM_PORTS; i++)
+ mt7530_mdio_w32(gsw, REG_ESW_PORT_PCR(i), 0x00400000);
+
+ mt7530_mdio_w32(gsw, REG_ESW_PORT_PCR(MT7530_CPU_PORT), 0x00ff0000);
+
+ for (i = 0; i < MT7530_NUM_PORTS; i++)
+ mt7530_mdio_w32(gsw, REG_ESW_PORT_PVC(i), 0x810000c0);
+
+ }
+
+ /* enable irq */
+ mt7530_mdio_m32(gsw, 0, 3 << 16, MT7530_TOP_SIG_CTRL);
+ mt7530_mdio_w32(gsw, MT7530_SYS_INT_EN, 0x1f);
+
+}
+
+static const struct of_device_id mediatek_gsw_match[] = {
+ { .compatible = "mediatek,mt7621-gsw" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mediatek_gsw_match);
+
+int mtk_gsw_init(struct mtk_eth *eth)
+{
+ struct device_node *np = eth->switch_np;
+ struct platform_device *pdev = of_find_device_by_node(np);
+ struct mt7620_gsw *gsw;
+
+ if (!pdev)
+ return -ENODEV;
+
+ if (!of_device_is_compatible(np, mediatek_gsw_match->compatible))
+ return -EINVAL;
+
+ gsw = platform_get_drvdata(pdev);
+ eth->sw_priv = gsw;
+
+ if (!gsw->irq)
+ return -EINVAL;
+
+ request_irq(gsw->irq, gsw_interrupt_mt7621, 0,
+ "gsw", eth);
+ disable_irq(gsw->irq);
+
+ mt7621_hw_init(eth, gsw, np);
+
+ enable_irq(gsw->irq);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_gsw_init);
+
+static int mt7621_gsw_probe(struct platform_device *pdev)
+{
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct mt7620_gsw *gsw;
+
+ gsw = devm_kzalloc(&pdev->dev, sizeof(struct mt7620_gsw), GFP_KERNEL);
+ if (!gsw)
+ return -ENOMEM;
+
+ gsw->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(gsw->base))
+ return PTR_ERR(gsw->base);
+
+ gsw->dev = &pdev->dev;
+ gsw->irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
+
+ platform_set_drvdata(pdev, gsw);
+
+ return 0;
+}
+
+static int mt7621_gsw_remove(struct platform_device *pdev)
+{
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver gsw_driver = {
+ .probe = mt7621_gsw_probe,
+ .remove = mt7621_gsw_remove,
+ .driver = {
+ .name = "mt7621-gsw",
+ .owner = THIS_MODULE,
+ .of_match_table = mediatek_gsw_match,
+ },
+};
+
+module_platform_driver(gsw_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
+MODULE_DESCRIPTION("GBit switch driver for Mediatek MT7621 SoC");
diff --git a/drivers/staging/mt7621-eth/mdio.c b/drivers/staging/mt7621-eth/mdio.c
new file mode 100644
index 000000000000..9d713078ef90
--- /dev/null
+++ b/drivers/staging/mt7621-eth/mdio.c
@@ -0,0 +1,271 @@
+/* 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 of the License
+ *
+ * Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/phy.h>
+#include <linux/of_net.h>
+#include <linux/of_mdio.h>
+
+#include "mtk_eth_soc.h"
+#include "mdio.h"
+
+static int mtk_mdio_reset(struct mii_bus *bus)
+{
+ /* TODO */
+ return 0;
+}
+
+static void mtk_phy_link_adjust(struct net_device *dev)
+{
+ struct mtk_eth *eth = netdev_priv(dev);
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&eth->phy->lock, flags);
+ for (i = 0; i < 8; i++) {
+ if (eth->phy->phy_node[i]) {
+ struct phy_device *phydev = eth->phy->phy[i];
+ int status_change = 0;
+
+ if (phydev->link)
+ if (eth->phy->duplex[i] != phydev->duplex ||
+ eth->phy->speed[i] != phydev->speed)
+ status_change = 1;
+
+ if (phydev->link != eth->link[i])
+ status_change = 1;
+
+ switch (phydev->speed) {
+ case SPEED_1000:
+ case SPEED_100:
+ case SPEED_10:
+ eth->link[i] = phydev->link;
+ eth->phy->duplex[i] = phydev->duplex;
+ eth->phy->speed[i] = phydev->speed;
+
+ if (status_change &&
+ eth->soc->mdio_adjust_link)
+ eth->soc->mdio_adjust_link(eth, i);
+ break;
+ }
+ }
+ }
+}
+
+int mtk_connect_phy_node(struct mtk_eth *eth, struct mtk_mac *mac,
+ struct device_node *phy_node)
+{
+ const __be32 *_port = NULL;
+ struct phy_device *phydev;
+ int phy_mode, port;
+
+ _port = of_get_property(phy_node, "reg", NULL);
+
+ if (!_port || (be32_to_cpu(*_port) >= 0x20)) {
+ pr_err("%s: invalid port id\n", phy_node->name);
+ return -EINVAL;
+ }
+ port = be32_to_cpu(*_port);
+ phy_mode = of_get_phy_mode(phy_node);
+ if (phy_mode < 0) {
+ dev_err(eth->dev, "incorrect phy-mode %d\n", phy_mode);
+ eth->phy->phy_node[port] = NULL;
+ return -EINVAL;
+ }
+
+ phydev = of_phy_connect(eth->netdev[mac->id], phy_node,
+ mtk_phy_link_adjust, 0, phy_mode);
+ if (!phydev) {
+ dev_err(eth->dev, "could not connect to PHY\n");
+ eth->phy->phy_node[port] = NULL;
+ return -ENODEV;
+ }
+
+ phydev->supported &= PHY_GBIT_FEATURES;
+ phydev->advertising = phydev->supported;
+
+ dev_info(eth->dev,
+ "connected port %d to PHY at %s [uid=%08x, driver=%s]\n",
+ port, phydev_name(phydev), phydev->phy_id,
+ phydev->drv->name);
+
+ eth->phy->phy[port] = phydev;
+ eth->link[port] = 0;
+
+ return 0;
+}
+
+static void phy_init(struct mtk_eth *eth, struct mtk_mac *mac,
+ struct phy_device *phy)
+{
+ phy_attach(eth->netdev[mac->id], phydev_name(phy),
+ PHY_INTERFACE_MODE_MII);
+
+ phy->autoneg = AUTONEG_ENABLE;
+ phy->speed = 0;
+ phy->duplex = 0;
+ phy->supported &= PHY_BASIC_FEATURES;
+ phy->advertising = phy->supported | ADVERTISED_Autoneg;
+
+ phy_start_aneg(phy);
+}
+
+static int mtk_phy_connect(struct mtk_mac *mac)
+{
+ struct mtk_eth *eth = mac->hw;
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ if (eth->phy->phy_node[i]) {
+ if (!mac->phy_dev) {
+ mac->phy_dev = eth->phy->phy[i];
+ mac->phy_flags = MTK_PHY_FLAG_PORT;
+ }
+ } else if (eth->mii_bus) {
+ struct phy_device *phy;
+ phy = mdiobus_get_phy(eth->mii_bus, i);
+ if (phy) {
+ phy_init(eth, mac, phy);
+ if (!mac->phy_dev) {
+ mac->phy_dev = phy;
+ mac->phy_flags = MTK_PHY_FLAG_ATTACH;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void mtk_phy_disconnect(struct mtk_mac *mac)
+{
+ struct mtk_eth *eth = mac->hw;
+ unsigned long flags;
+ int i;
+
+ for (i = 0; i < 8; i++)
+ if (eth->phy->phy_fixed[i]) {
+ spin_lock_irqsave(&eth->phy->lock, flags);
+ eth->link[i] = 0;
+ if (eth->soc->mdio_adjust_link)
+ eth->soc->mdio_adjust_link(eth, i);
+ spin_unlock_irqrestore(&eth->phy->lock, flags);
+ } else if (eth->phy->phy[i]) {
+ phy_disconnect(eth->phy->phy[i]);
+ } else if (eth->mii_bus) {
+ struct phy_device *phy = mdiobus_get_phy(eth->mii_bus, i);
+ if (phy)
+ phy_detach(phy);
+ }
+}
+
+static void mtk_phy_start(struct mtk_mac *mac)
+{
+ struct mtk_eth *eth = mac->hw;
+ unsigned long flags;
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ if (eth->phy->phy_fixed[i]) {
+ spin_lock_irqsave(&eth->phy->lock, flags);
+ eth->link[i] = 1;
+ if (eth->soc->mdio_adjust_link)
+ eth->soc->mdio_adjust_link(eth, i);
+ spin_unlock_irqrestore(&eth->phy->lock, flags);
+ } else if (eth->phy->phy[i]) {
+ phy_start(eth->phy->phy[i]);
+ }
+ }
+}
+
+static void mtk_phy_stop(struct mtk_mac *mac)
+{
+ struct mtk_eth *eth = mac->hw;
+ unsigned long flags;
+ int i;
+
+ for (i = 0; i < 8; i++)
+ if (eth->phy->phy_fixed[i]) {
+ spin_lock_irqsave(&eth->phy->lock, flags);
+ eth->link[i] = 0;
+ if (eth->soc->mdio_adjust_link)
+ eth->soc->mdio_adjust_link(eth, i);
+ spin_unlock_irqrestore(&eth->phy->lock, flags);
+ } else if (eth->phy->phy[i]) {
+ phy_stop(eth->phy->phy[i]);
+ }
+}
+
+static struct mtk_phy phy_ralink = {
+ .connect = mtk_phy_connect,
+ .disconnect = mtk_phy_disconnect,
+ .start = mtk_phy_start,
+ .stop = mtk_phy_stop,
+};
+
+int mtk_mdio_init(struct mtk_eth *eth)
+{
+ struct device_node *mii_np;
+ int err;
+
+ if (!eth->soc->mdio_read || !eth->soc->mdio_write)
+ return 0;
+
+ spin_lock_init(&phy_ralink.lock);
+ eth->phy = &phy_ralink;
+
+ mii_np = of_get_child_by_name(eth->dev->of_node, "mdio-bus");
+ if (!mii_np) {
+ dev_err(eth->dev, "no %s child node found", "mdio-bus");
+ return -ENODEV;
+ }
+
+ if (!of_device_is_available(mii_np)) {
+ err = 0;
+ goto err_put_node;
+ }
+
+ eth->mii_bus = mdiobus_alloc();
+ if (!eth->mii_bus) {
+ err = -ENOMEM;
+ goto err_put_node;
+ }
+
+ eth->mii_bus->name = "mdio";
+ eth->mii_bus->read = eth->soc->mdio_read;
+ eth->mii_bus->write = eth->soc->mdio_write;
+ eth->mii_bus->reset = mtk_mdio_reset;
+ eth->mii_bus->priv = eth;
+ eth->mii_bus->parent = eth->dev;
+
+ snprintf(eth->mii_bus->id, MII_BUS_ID_SIZE, "%s", mii_np->name);
+ err = of_mdiobus_register(eth->mii_bus, mii_np);
+ if (err)
+ goto err_free_bus;
+
+ return 0;
+
+err_free_bus:
+ kfree(eth->mii_bus);
+err_put_node:
+ of_node_put(mii_np);
+ eth->mii_bus = NULL;
+ return err;
+}
+
+void mtk_mdio_cleanup(struct mtk_eth *eth)
+{
+ if (!eth->mii_bus)
+ return;
+
+ mdiobus_unregister(eth->mii_bus);
+ of_node_put(eth->mii_bus->dev.of_node);
+ kfree(eth->mii_bus);
+}
diff --git a/drivers/staging/mt7621-eth/mdio.h b/drivers/staging/mt7621-eth/mdio.h
new file mode 100644
index 000000000000..b14e23842a01
--- /dev/null
+++ b/drivers/staging/mt7621-eth/mdio.h
@@ -0,0 +1,27 @@
+/* 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 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
+ */
+
+#ifndef _RALINK_MDIO_H__
+#define _RALINK_MDIO_H__
+
+#ifdef CONFIG_NET_MEDIATEK_MDIO
+int mtk_mdio_init(struct mtk_eth *eth);
+void mtk_mdio_cleanup(struct mtk_eth *eth);
+int mtk_connect_phy_node(struct mtk_eth *eth, struct mtk_mac *mac,
+ struct device_node *phy_node);
+#else
+static inline int mtk_mdio_init(struct mtk_eth *eth) { return 0; }
+static inline void mtk_mdio_cleanup(struct mtk_eth *eth) {}
+#endif
+#endif
diff --git a/drivers/staging/mt7621-eth/mdio_mt7620.c b/drivers/staging/mt7621-eth/mdio_mt7620.c
new file mode 100644
index 000000000000..ced605c2914e
--- /dev/null
+++ b/drivers/staging/mt7621-eth/mdio_mt7620.c
@@ -0,0 +1,173 @@
+/* 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 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+#include "mtk_eth_soc.h"
+#include "gsw_mt7620.h"
+#include "mdio.h"
+
+static int mt7620_mii_busy_wait(struct mt7620_gsw *gsw)
+{
+ unsigned long t_start = jiffies;
+
+ while (1) {
+ if (!(mtk_switch_r32(gsw,
+ gsw->piac_offset + MT7620_GSW_REG_PIAC) &
+ GSW_MDIO_ACCESS))
+ return 0;
+ if (time_after(jiffies, t_start + GSW_REG_PHY_TIMEOUT))
+ break;
+ }
+
+ dev_err(gsw->dev, "mdio: MDIO timeout\n");
+ return -1;
+}
+
+u32 _mt7620_mii_write(struct mt7620_gsw *gsw, u32 phy_addr,
+ u32 phy_register, u32 write_data)
+{
+ if (mt7620_mii_busy_wait(gsw))
+ return -1;
+
+ write_data &= 0xffff;
+
+ mtk_switch_w32(gsw, GSW_MDIO_ACCESS | GSW_MDIO_START | GSW_MDIO_WRITE |
+ (phy_register << GSW_MDIO_REG_SHIFT) |
+ (phy_addr << GSW_MDIO_ADDR_SHIFT) | write_data,
+ MT7620_GSW_REG_PIAC);
+
+ if (mt7620_mii_busy_wait(gsw))
+ return -1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(_mt7620_mii_write);
+
+u32 _mt7620_mii_read(struct mt7620_gsw *gsw, int phy_addr, int phy_reg)
+{
+ u32 d;
+
+ if (mt7620_mii_busy_wait(gsw))
+ return 0xffff;
+
+ mtk_switch_w32(gsw, GSW_MDIO_ACCESS | GSW_MDIO_START | GSW_MDIO_READ |
+ (phy_reg << GSW_MDIO_REG_SHIFT) |
+ (phy_addr << GSW_MDIO_ADDR_SHIFT),
+ MT7620_GSW_REG_PIAC);
+
+ if (mt7620_mii_busy_wait(gsw))
+ return 0xffff;
+
+ d = mtk_switch_r32(gsw, MT7620_GSW_REG_PIAC) & 0xffff;
+
+ return d;
+}
+EXPORT_SYMBOL_GPL(_mt7620_mii_read);
+
+int mt7620_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val)
+{
+ struct mtk_eth *eth = bus->priv;
+ struct mt7620_gsw *gsw = (struct mt7620_gsw *)eth->sw_priv;
+
+ return _mt7620_mii_write(gsw, phy_addr, phy_reg, val);
+}
+
+int mt7620_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg)
+{
+ struct mtk_eth *eth = bus->priv;
+ struct mt7620_gsw *gsw = (struct mt7620_gsw *)eth->sw_priv;
+
+ return _mt7620_mii_read(gsw, phy_addr, phy_reg);
+}
+
+void mt7530_mdio_w32(struct mt7620_gsw *gsw, u32 reg, u32 val)
+{
+ _mt7620_mii_write(gsw, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
+ _mt7620_mii_write(gsw, 0x1f, (reg >> 2) & 0xf, val & 0xffff);
+ _mt7620_mii_write(gsw, 0x1f, 0x10, val >> 16);
+}
+EXPORT_SYMBOL_GPL(mt7530_mdio_w32);
+
+u32 mt7530_mdio_r32(struct mt7620_gsw *gsw, u32 reg)
+{
+ u16 high, low;
+
+ _mt7620_mii_write(gsw, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
+ low = _mt7620_mii_read(gsw, 0x1f, (reg >> 2) & 0xf);
+ high = _mt7620_mii_read(gsw, 0x1f, 0x10);
+
+ return (high << 16) | (low & 0xffff);
+}
+EXPORT_SYMBOL_GPL(mt7530_mdio_r32);
+
+void mt7530_mdio_m32(struct mt7620_gsw *gsw, u32 mask, u32 set, u32 reg)
+{
+ u32 val = mt7530_mdio_r32(gsw, reg);
+
+ val &= ~mask;
+ val |= set;
+ mt7530_mdio_w32(gsw, reg, val);
+}
+EXPORT_SYMBOL_GPL(mt7530_mdio_m32);
+
+static unsigned char *mtk_speed_str(int speed)
+{
+ switch (speed) {
+ case 2:
+ case SPEED_1000:
+ return "1000";
+ case 1:
+ case SPEED_100:
+ return "100";
+ case 0:
+ case SPEED_10:
+ return "10";
+ }
+
+ return "? ";
+}
+
+int mt7620_has_carrier(struct mtk_eth *eth)
+{
+ struct mt7620_gsw *gsw = (struct mt7620_gsw *)eth->sw_priv;
+ int i;
+
+ for (i = 0; i < GSW_PORT6; i++)
+ if (mt7530_mdio_r32(gsw, GSW_REG_PORT_STATUS(i)) & 0x1)
+ return 1;
+ return 0;
+}
+
+void mt7620_print_link_state(struct mtk_eth *eth, int port, int link,
+ int speed, int duplex)
+{
+ struct mt7620_gsw *gsw = eth->sw_priv;
+
+ if (link)
+ dev_info(gsw->dev, "port %d link up (%sMbps/%s duplex)\n",
+ port, mtk_speed_str(speed),
+ (duplex) ? "Full" : "Half");
+ else
+ dev_info(gsw->dev, "port %d link down\n", port);
+}
+
+void mt7620_mdio_link_adjust(struct mtk_eth *eth, int port)
+{
+ mt7620_print_link_state(eth, port, eth->link[port],
+ eth->phy->speed[port],
+ (eth->phy->duplex[port] == DUPLEX_FULL));
+}
diff --git a/drivers/staging/mt7621-eth/mtk_eth_soc.c b/drivers/staging/mt7621-eth/mtk_eth_soc.c
new file mode 100644
index 000000000000..cbc7339843a5
--- /dev/null
+++ b/drivers/staging/mt7621-eth/mtk_eth_soc.c
@@ -0,0 +1,2178 @@
+/* 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 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/clk.h>
+#include <linux/of_net.h>
+#include <linux/of_mdio.h>
+#include <linux/if_vlan.h>
+#include <linux/reset.h>
+#include <linux/tcp.h>
+#include <linux/io.h>
+#include <linux/bug.h>
+#include <linux/regmap.h>
+
+#include "mtk_eth_soc.h"
+#include "mdio.h"
+#include "ethtool.h"
+
+#define MAX_RX_LENGTH 1536
+#define MTK_RX_ETH_HLEN (VLAN_ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN)
+#define MTK_RX_HLEN (NET_SKB_PAD + MTK_RX_ETH_HLEN + NET_IP_ALIGN)
+#define DMA_DUMMY_DESC 0xffffffff
+#define MTK_DEFAULT_MSG_ENABLE \
+ (NETIF_MSG_DRV | \
+ NETIF_MSG_PROBE | \
+ NETIF_MSG_LINK | \
+ NETIF_MSG_TIMER | \
+ NETIF_MSG_IFDOWN | \
+ NETIF_MSG_IFUP | \
+ NETIF_MSG_RX_ERR | \
+ NETIF_MSG_TX_ERR)
+
+#define TX_DMA_DESP2_DEF (TX_DMA_LS0 | TX_DMA_DONE)
+#define NEXT_TX_DESP_IDX(X) (((X) + 1) & (ring->tx_ring_size - 1))
+#define NEXT_RX_DESP_IDX(X) (((X) + 1) & (ring->rx_ring_size - 1))
+
+#define SYSC_REG_RSTCTRL 0x34
+
+static int mtk_msg_level = -1;
+module_param_named(msg_level, mtk_msg_level, int, 0);
+MODULE_PARM_DESC(msg_level, "Message level (-1=defaults,0=none,...,16=all)");
+
+static const u16 mtk_reg_table_default[MTK_REG_COUNT] = {
+ [MTK_REG_PDMA_GLO_CFG] = MTK_PDMA_GLO_CFG,
+ [MTK_REG_PDMA_RST_CFG] = MTK_PDMA_RST_CFG,
+ [MTK_REG_DLY_INT_CFG] = MTK_DLY_INT_CFG,
+ [MTK_REG_TX_BASE_PTR0] = MTK_TX_BASE_PTR0,
+ [MTK_REG_TX_MAX_CNT0] = MTK_TX_MAX_CNT0,
+ [MTK_REG_TX_CTX_IDX0] = MTK_TX_CTX_IDX0,
+ [MTK_REG_TX_DTX_IDX0] = MTK_TX_DTX_IDX0,
+ [MTK_REG_RX_BASE_PTR0] = MTK_RX_BASE_PTR0,
+ [MTK_REG_RX_MAX_CNT0] = MTK_RX_MAX_CNT0,
+ [MTK_REG_RX_CALC_IDX0] = MTK_RX_CALC_IDX0,
+ [MTK_REG_RX_DRX_IDX0] = MTK_RX_DRX_IDX0,
+ [MTK_REG_MTK_INT_ENABLE] = MTK_INT_ENABLE,
+ [MTK_REG_MTK_INT_STATUS] = MTK_INT_STATUS,
+ [MTK_REG_MTK_DMA_VID_BASE] = MTK_DMA_VID0,
+ [MTK_REG_MTK_COUNTER_BASE] = MTK_GDMA1_TX_GBCNT,
+ [MTK_REG_MTK_RST_GL] = MTK_RST_GL,
+};
+
+static const u16 *mtk_reg_table = mtk_reg_table_default;
+
+void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg)
+{
+ __raw_writel(val, eth->base + reg);
+}
+
+u32 mtk_r32(struct mtk_eth *eth, unsigned reg)
+{
+ return __raw_readl(eth->base + reg);
+}
+
+static void mtk_reg_w32(struct mtk_eth *eth, u32 val, enum mtk_reg reg)
+{
+ mtk_w32(eth, val, mtk_reg_table[reg]);
+}
+
+static u32 mtk_reg_r32(struct mtk_eth *eth, enum mtk_reg reg)
+{
+ return mtk_r32(eth, mtk_reg_table[reg]);
+}
+
+/* these bits are also exposed via the reset-controller API. however the switch
+ * and FE need to be brought out of reset in the exakt same moemtn and the
+ * reset-controller api does not provide this feature yet. Do the reset manually
+ * until we fixed the reset-controller api to be able to do this
+ */
+void mtk_reset(struct mtk_eth *eth, u32 reset_bits)
+{
+ u32 val;
+
+ regmap_read(eth->ethsys, SYSC_REG_RSTCTRL, &val);
+ val |= reset_bits;
+ regmap_write(eth->ethsys, SYSC_REG_RSTCTRL, val);
+ usleep_range(10, 20);
+ val &= ~reset_bits;
+ regmap_write(eth->ethsys, SYSC_REG_RSTCTRL, val);
+ usleep_range(10, 20);
+}
+EXPORT_SYMBOL(mtk_reset);
+
+static inline void mtk_irq_ack(struct mtk_eth *eth, u32 mask)
+{
+ if (eth->soc->dma_type & MTK_PDMA)
+ mtk_reg_w32(eth, mask, MTK_REG_MTK_INT_STATUS);
+ if (eth->soc->dma_type & MTK_QDMA)
+ mtk_w32(eth, mask, MTK_QMTK_INT_STATUS);
+}
+
+static inline u32 mtk_irq_pending(struct mtk_eth *eth)
+{
+ u32 status = 0;
+
+ if (eth->soc->dma_type & MTK_PDMA)
+ status |= mtk_reg_r32(eth, MTK_REG_MTK_INT_STATUS);
+ if (eth->soc->dma_type & MTK_QDMA)
+ status |= mtk_r32(eth, MTK_QMTK_INT_STATUS);
+
+ return status;
+}
+
+static void mtk_irq_ack_status(struct mtk_eth *eth, u32 mask)
+{
+ u32 status_reg = MTK_REG_MTK_INT_STATUS;
+
+ if (mtk_reg_table[MTK_REG_MTK_INT_STATUS2])
+ status_reg = MTK_REG_MTK_INT_STATUS2;
+
+ mtk_reg_w32(eth, mask, status_reg);
+}
+
+static u32 mtk_irq_pending_status(struct mtk_eth *eth)
+{
+ u32 status_reg = MTK_REG_MTK_INT_STATUS;
+
+ if (mtk_reg_table[MTK_REG_MTK_INT_STATUS2])
+ status_reg = MTK_REG_MTK_INT_STATUS2;
+
+ return mtk_reg_r32(eth, status_reg);
+}
+
+static inline void mtk_irq_disable(struct mtk_eth *eth, u32 mask)
+{
+ u32 val;
+
+ if (eth->soc->dma_type & MTK_PDMA) {
+ val = mtk_reg_r32(eth, MTK_REG_MTK_INT_ENABLE);
+ mtk_reg_w32(eth, val & ~mask, MTK_REG_MTK_INT_ENABLE);
+ /* flush write */
+ mtk_reg_r32(eth, MTK_REG_MTK_INT_ENABLE);
+ }
+ if (eth->soc->dma_type & MTK_QDMA) {
+ val = mtk_r32(eth, MTK_QMTK_INT_ENABLE);
+ mtk_w32(eth, val & ~mask, MTK_QMTK_INT_ENABLE);
+ /* flush write */
+ mtk_r32(eth, MTK_QMTK_INT_ENABLE);
+ }
+}
+
+static inline void mtk_irq_enable(struct mtk_eth *eth, u32 mask)
+{
+ u32 val;
+
+ if (eth->soc->dma_type & MTK_PDMA) {
+ val = mtk_reg_r32(eth, MTK_REG_MTK_INT_ENABLE);
+ mtk_reg_w32(eth, val | mask, MTK_REG_MTK_INT_ENABLE);
+ /* flush write */
+ mtk_reg_r32(eth, MTK_REG_MTK_INT_ENABLE);
+ }
+ if (eth->soc->dma_type & MTK_QDMA) {
+ val = mtk_r32(eth, MTK_QMTK_INT_ENABLE);
+ mtk_w32(eth, val | mask, MTK_QMTK_INT_ENABLE);
+ /* flush write */
+ mtk_r32(eth, MTK_QMTK_INT_ENABLE);
+ }
+}
+
+static inline u32 mtk_irq_enabled(struct mtk_eth *eth)
+{
+ u32 enabled = 0;
+
+ if (eth->soc->dma_type & MTK_PDMA)
+ enabled |= mtk_reg_r32(eth, MTK_REG_MTK_INT_ENABLE);
+ if (eth->soc->dma_type & MTK_QDMA)
+ enabled |= mtk_r32(eth, MTK_QMTK_INT_ENABLE);
+
+ return enabled;
+}
+
+static inline void mtk_hw_set_macaddr(struct mtk_mac *mac,
+ unsigned char *macaddr)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mac->hw->page_lock, flags);
+ mtk_w32(mac->hw, (macaddr[0] << 8) | macaddr[1], MTK_GDMA1_MAC_ADRH);
+ mtk_w32(mac->hw, (macaddr[2] << 24) | (macaddr[3] << 16) |
+ (macaddr[4] << 8) | macaddr[5],
+ MTK_GDMA1_MAC_ADRL);
+ spin_unlock_irqrestore(&mac->hw->page_lock, flags);
+}
+
+static int mtk_set_mac_address(struct net_device *dev, void *p)
+{
+ int ret = eth_mac_addr(dev, p);
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+
+ if (ret)
+ return ret;
+
+ if (eth->soc->set_mac)
+ eth->soc->set_mac(mac, dev->dev_addr);
+ else
+ mtk_hw_set_macaddr(mac, p);
+
+ return 0;
+}
+
+static inline int mtk_max_frag_size(int mtu)
+{
+ /* make sure buf_size will be at least MAX_RX_LENGTH */
+ if (mtu + MTK_RX_ETH_HLEN < MAX_RX_LENGTH)
+ mtu = MAX_RX_LENGTH - MTK_RX_ETH_HLEN;
+
+ return SKB_DATA_ALIGN(MTK_RX_HLEN + mtu) +
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+}
+
+static inline int mtk_max_buf_size(int frag_size)
+{
+ int buf_size = frag_size - NET_SKB_PAD - NET_IP_ALIGN -
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+
+ WARN_ON(buf_size < MAX_RX_LENGTH);
+
+ return buf_size;
+}
+
+static inline void mtk_get_rxd(struct mtk_rx_dma *rxd,
+ struct mtk_rx_dma *dma_rxd)
+{
+ rxd->rxd1 = READ_ONCE(dma_rxd->rxd1);
+ rxd->rxd2 = READ_ONCE(dma_rxd->rxd2);
+ rxd->rxd3 = READ_ONCE(dma_rxd->rxd3);
+ rxd->rxd4 = READ_ONCE(dma_rxd->rxd4);
+}
+
+static inline void mtk_set_txd_pdma(struct mtk_tx_dma *txd,
+ struct mtk_tx_dma *dma_txd)
+{
+ WRITE_ONCE(dma_txd->txd1, txd->txd1);
+ WRITE_ONCE(dma_txd->txd3, txd->txd3);
+ WRITE_ONCE(dma_txd->txd4, txd->txd4);
+ /* clean dma done flag last */
+ WRITE_ONCE(dma_txd->txd2, txd->txd2);
+}
+
+static void mtk_clean_rx(struct mtk_eth *eth, struct mtk_rx_ring *ring)
+{
+ int i;
+
+ if (ring->rx_data && ring->rx_dma) {
+ for (i = 0; i < ring->rx_ring_size; i++) {
+ if (!ring->rx_data[i])
+ continue;
+ if (!ring->rx_dma[i].rxd1)
+ continue;
+ dma_unmap_single(eth->dev,
+ ring->rx_dma[i].rxd1,
+ ring->rx_buf_size,
+ DMA_FROM_DEVICE);
+ skb_free_frag(ring->rx_data[i]);
+ }
+ kfree(ring->rx_data);
+ ring->rx_data = NULL;
+ }
+
+ if (ring->rx_dma) {
+ dma_free_coherent(eth->dev,
+ ring->rx_ring_size * sizeof(*ring->rx_dma),
+ ring->rx_dma,
+ ring->rx_phys);
+ ring->rx_dma = NULL;
+ }
+}
+
+static int mtk_dma_rx_alloc(struct mtk_eth *eth, struct mtk_rx_ring *ring)
+{
+ int i, pad = 0;
+
+ ring->frag_size = mtk_max_frag_size(ETH_DATA_LEN);
+ ring->rx_buf_size = mtk_max_buf_size(ring->frag_size);
+ ring->rx_ring_size = eth->soc->dma_ring_size;
+ ring->rx_data = kcalloc(ring->rx_ring_size, sizeof(*ring->rx_data),
+ GFP_KERNEL);
+ if (!ring->rx_data)
+ goto no_rx_mem;
+
+ for (i = 0; i < ring->rx_ring_size; i++) {
+ ring->rx_data[i] = netdev_alloc_frag(ring->frag_size);
+ if (!ring->rx_data[i])
+ goto no_rx_mem;
+ }
+
+ ring->rx_dma = dma_alloc_coherent(eth->dev,
+ ring->rx_ring_size * sizeof(*ring->rx_dma),
+ &ring->rx_phys,
+ GFP_ATOMIC | __GFP_ZERO);
+ if (!ring->rx_dma)
+ goto no_rx_mem;
+
+ if (!eth->soc->rx_2b_offset)
+ pad = NET_IP_ALIGN;
+
+ for (i = 0; i < ring->rx_ring_size; i++) {
+ dma_addr_t dma_addr = dma_map_single(eth->dev,
+ ring->rx_data[i] + NET_SKB_PAD + pad,
+ ring->rx_buf_size,
+ DMA_FROM_DEVICE);
+ if (unlikely(dma_mapping_error(eth->dev, dma_addr)))
+ goto no_rx_mem;
+ ring->rx_dma[i].rxd1 = (unsigned int)dma_addr;
+
+ if (eth->soc->rx_sg_dma)
+ ring->rx_dma[i].rxd2 = RX_DMA_PLEN0(ring->rx_buf_size);
+ else
+ ring->rx_dma[i].rxd2 = RX_DMA_LSO;
+ }
+ ring->rx_calc_idx = ring->rx_ring_size - 1;
+ /* make sure that all changes to the dma ring are flushed before we
+ * continue
+ */
+ wmb();
+
+ return 0;
+
+no_rx_mem:
+ return -ENOMEM;
+}
+
+static void mtk_txd_unmap(struct device *dev, struct mtk_tx_buf *tx_buf)
+{
+ if (tx_buf->flags & MTK_TX_FLAGS_SINGLE0) {
+ dma_unmap_single(dev,
+ dma_unmap_addr(tx_buf, dma_addr0),
+ dma_unmap_len(tx_buf, dma_len0),
+ DMA_TO_DEVICE);
+ } else if (tx_buf->flags & MTK_TX_FLAGS_PAGE0) {
+ dma_unmap_page(dev,
+ dma_unmap_addr(tx_buf, dma_addr0),
+ dma_unmap_len(tx_buf, dma_len0),
+ DMA_TO_DEVICE);
+ }
+ if (tx_buf->flags & MTK_TX_FLAGS_PAGE1)
+ dma_unmap_page(dev,
+ dma_unmap_addr(tx_buf, dma_addr1),
+ dma_unmap_len(tx_buf, dma_len1),
+ DMA_TO_DEVICE);
+
+ tx_buf->flags = 0;
+ if (tx_buf->skb && (tx_buf->skb != (struct sk_buff *)DMA_DUMMY_DESC))
+ dev_kfree_skb_any(tx_buf->skb);
+ tx_buf->skb = NULL;
+}
+
+static void mtk_pdma_tx_clean(struct mtk_eth *eth)
+{
+ struct mtk_tx_ring *ring = &eth->tx_ring;
+ int i;
+
+ if (ring->tx_buf) {
+ for (i = 0; i < ring->tx_ring_size; i++)
+ mtk_txd_unmap(eth->dev, &ring->tx_buf[i]);
+ kfree(ring->tx_buf);
+ ring->tx_buf = NULL;
+ }
+
+ if (ring->tx_dma) {
+ dma_free_coherent(eth->dev,
+ ring->tx_ring_size * sizeof(*ring->tx_dma),
+ ring->tx_dma,
+ ring->tx_phys);
+ ring->tx_dma = NULL;
+ }
+}
+
+static void mtk_qdma_tx_clean(struct mtk_eth *eth)
+{
+ struct mtk_tx_ring *ring = &eth->tx_ring;
+ int i;
+
+ if (ring->tx_buf) {
+ for (i = 0; i < ring->tx_ring_size; i++)
+ mtk_txd_unmap(eth->dev, &ring->tx_buf[i]);
+ kfree(ring->tx_buf);
+ ring->tx_buf = NULL;
+ }
+
+ if (ring->tx_dma) {
+ dma_free_coherent(eth->dev,
+ ring->tx_ring_size * sizeof(*ring->tx_dma),
+ ring->tx_dma,
+ ring->tx_phys);
+ ring->tx_dma = NULL;
+ }
+}
+
+void mtk_stats_update_mac(struct mtk_mac *mac)
+{
+ struct mtk_hw_stats *hw_stats = mac->hw_stats;
+ unsigned int base = mtk_reg_table[MTK_REG_MTK_COUNTER_BASE];
+ u64 stats;
+
+ base += hw_stats->reg_offset;
+
+ u64_stats_update_begin(&hw_stats->syncp);
+
+ if (mac->hw->soc->new_stats) {
+ hw_stats->rx_bytes += mtk_r32(mac->hw, base);
+ stats = mtk_r32(mac->hw, base + 0x04);
+ if (stats)
+ hw_stats->rx_bytes += (stats << 32);
+ hw_stats->rx_packets += mtk_r32(mac->hw, base + 0x08);
+ hw_stats->rx_overflow += mtk_r32(mac->hw, base + 0x10);
+ hw_stats->rx_fcs_errors += mtk_r32(mac->hw, base + 0x14);
+ hw_stats->rx_short_errors += mtk_r32(mac->hw, base + 0x18);
+ hw_stats->rx_long_errors += mtk_r32(mac->hw, base + 0x1c);
+ hw_stats->rx_checksum_errors += mtk_r32(mac->hw, base + 0x20);
+ hw_stats->rx_flow_control_packets +=
+ mtk_r32(mac->hw, base + 0x24);
+ hw_stats->tx_skip += mtk_r32(mac->hw, base + 0x28);
+ hw_stats->tx_collisions += mtk_r32(mac->hw, base + 0x2c);
+ hw_stats->tx_bytes += mtk_r32(mac->hw, base + 0x30);
+ stats = mtk_r32(mac->hw, base + 0x34);
+ if (stats)
+ hw_stats->tx_bytes += (stats << 32);
+ hw_stats->tx_packets += mtk_r32(mac->hw, base + 0x38);
+ } else {
+ hw_stats->tx_bytes += mtk_r32(mac->hw, base);
+ hw_stats->tx_packets += mtk_r32(mac->hw, base + 0x04);
+ hw_stats->tx_skip += mtk_r32(mac->hw, base + 0x08);
+ hw_stats->tx_collisions += mtk_r32(mac->hw, base + 0x0c);
+ hw_stats->rx_bytes += mtk_r32(mac->hw, base + 0x20);
+ hw_stats->rx_packets += mtk_r32(mac->hw, base + 0x24);
+ hw_stats->rx_overflow += mtk_r32(mac->hw, base + 0x28);
+ hw_stats->rx_fcs_errors += mtk_r32(mac->hw, base + 0x2c);
+ hw_stats->rx_short_errors += mtk_r32(mac->hw, base + 0x30);
+ hw_stats->rx_long_errors += mtk_r32(mac->hw, base + 0x34);
+ hw_stats->rx_checksum_errors += mtk_r32(mac->hw, base + 0x38);
+ hw_stats->rx_flow_control_packets +=
+ mtk_r32(mac->hw, base + 0x3c);
+ }
+
+ u64_stats_update_end(&hw_stats->syncp);
+}
+
+static void mtk_get_stats64(struct net_device *dev,
+ struct rtnl_link_stats64 *storage)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_hw_stats *hw_stats = mac->hw_stats;
+ unsigned int base = mtk_reg_table[MTK_REG_MTK_COUNTER_BASE];
+ unsigned int start;
+
+ if (!base) {
+ netdev_stats_to_stats64(storage, &dev->stats);
+ return;
+ }
+
+ if (netif_running(dev) && netif_device_present(dev)) {
+ if (spin_trylock(&hw_stats->stats_lock)) {
+ mtk_stats_update_mac(mac);
+ spin_unlock(&hw_stats->stats_lock);
+ }
+ }
+
+ do {
+ start = u64_stats_fetch_begin_irq(&hw_stats->syncp);
+ storage->rx_packets = hw_stats->rx_packets;
+ storage->tx_packets = hw_stats->tx_packets;
+ storage->rx_bytes = hw_stats->rx_bytes;
+ storage->tx_bytes = hw_stats->tx_bytes;
+ storage->collisions = hw_stats->tx_collisions;
+ storage->rx_length_errors = hw_stats->rx_short_errors +
+ hw_stats->rx_long_errors;
+ storage->rx_over_errors = hw_stats->rx_overflow;
+ storage->rx_crc_errors = hw_stats->rx_fcs_errors;
+ storage->rx_errors = hw_stats->rx_checksum_errors;
+ storage->tx_aborted_errors = hw_stats->tx_skip;
+ } while (u64_stats_fetch_retry_irq(&hw_stats->syncp, start));
+
+ storage->tx_errors = dev->stats.tx_errors;
+ storage->rx_dropped = dev->stats.rx_dropped;
+ storage->tx_dropped = dev->stats.tx_dropped;
+}
+
+static int mtk_vlan_rx_add_vid(struct net_device *dev,
+ __be16 proto, u16 vid)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+ u32 idx = (vid & 0xf);
+ u32 vlan_cfg;
+
+ if (!((mtk_reg_table[MTK_REG_MTK_DMA_VID_BASE]) &&
+ (dev->features & NETIF_F_HW_VLAN_CTAG_TX)))
+ return 0;
+
+ if (test_bit(idx, &eth->vlan_map)) {
+ netdev_warn(dev, "disable tx vlan offload\n");
+ dev->wanted_features &= ~NETIF_F_HW_VLAN_CTAG_TX;
+ netdev_update_features(dev);
+ } else {
+ vlan_cfg = mtk_r32(eth,
+ mtk_reg_table[MTK_REG_MTK_DMA_VID_BASE] +
+ ((idx >> 1) << 2));
+ if (idx & 0x1) {
+ vlan_cfg &= 0xffff;
+ vlan_cfg |= (vid << 16);
+ } else {
+ vlan_cfg &= 0xffff0000;
+ vlan_cfg |= vid;
+ }
+ mtk_w32(eth,
+ vlan_cfg, mtk_reg_table[MTK_REG_MTK_DMA_VID_BASE] +
+ ((idx >> 1) << 2));
+ set_bit(idx, &eth->vlan_map);
+ }
+
+ return 0;
+}
+
+static int mtk_vlan_rx_kill_vid(struct net_device *dev,
+ __be16 proto, u16 vid)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+ u32 idx = (vid & 0xf);
+
+ if (!((mtk_reg_table[MTK_REG_MTK_DMA_VID_BASE]) &&
+ (dev->features & NETIF_F_HW_VLAN_CTAG_TX)))
+ return 0;
+
+ clear_bit(idx, &eth->vlan_map);
+
+ return 0;
+}
+
+static inline u32 mtk_pdma_empty_txd(struct mtk_tx_ring *ring)
+{
+ barrier();
+ return (u32)(ring->tx_ring_size -
+ ((ring->tx_next_idx - ring->tx_free_idx) &
+ (ring->tx_ring_size - 1)));
+}
+
+static int mtk_skb_padto(struct sk_buff *skb, struct mtk_eth *eth)
+{
+ unsigned int len;
+ int ret;
+
+ if (unlikely(skb->len >= VLAN_ETH_ZLEN))
+ return 0;
+
+ if (eth->soc->padding_64b && !eth->soc->padding_bug)
+ return 0;
+
+ if (skb_vlan_tag_present(skb))
+ len = ETH_ZLEN;
+ else if (skb->protocol == cpu_to_be16(ETH_P_8021Q))
+ len = VLAN_ETH_ZLEN;
+ else if (!eth->soc->padding_64b)
+ len = ETH_ZLEN;
+ else
+ return 0;
+
+ if (skb->len >= len)
+ return 0;
+
+ ret = skb_pad(skb, len - skb->len);
+ if (ret < 0)
+ return ret;
+ skb->len = len;
+ skb_set_tail_pointer(skb, len);
+
+ return ret;
+}
+
+static int mtk_pdma_tx_map(struct sk_buff *skb, struct net_device *dev,
+ int tx_num, struct mtk_tx_ring *ring, bool gso)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+ struct skb_frag_struct *frag;
+ struct mtk_tx_dma txd, *ptxd;
+ struct mtk_tx_buf *tx_buf;
+ int i, j, k, frag_size, frag_map_size, offset;
+ dma_addr_t mapped_addr;
+ unsigned int nr_frags;
+ u32 def_txd4;
+
+ if (mtk_skb_padto(skb, eth)) {
+ netif_warn(eth, tx_err, dev, "tx padding failed!\n");
+ return -1;
+ }
+
+ tx_buf = &ring->tx_buf[ring->tx_next_idx];
+ memset(tx_buf, 0, sizeof(*tx_buf));
+ memset(&txd, 0, sizeof(txd));
+ nr_frags = skb_shinfo(skb)->nr_frags;
+
+ /* init tx descriptor */
+ def_txd4 = eth->soc->txd4;
+ txd.txd4 = def_txd4;
+
+ if (eth->soc->mac_count > 1)
+ txd.txd4 |= (mac->id + 1) << TX_DMA_FPORT_SHIFT;
+
+ if (gso)
+ txd.txd4 |= TX_DMA_TSO;
+
+ /* TX Checksum offload */
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ txd.txd4 |= TX_DMA_CHKSUM;
+
+ /* VLAN header offload */
+ if (skb_vlan_tag_present(skb)) {
+ u16 tag = skb_vlan_tag_get(skb);
+
+ txd.txd4 |= TX_DMA_INS_VLAN |
+ ((tag >> VLAN_PRIO_SHIFT) << 4) |
+ (tag & 0xF);
+ }
+
+ mapped_addr = dma_map_single(&dev->dev, skb->data,
+ skb_headlen(skb), DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(&dev->dev, mapped_addr)))
+ return -1;
+
+ txd.txd1 = mapped_addr;
+ txd.txd2 = TX_DMA_PLEN0(skb_headlen(skb));
+
+ tx_buf->flags |= MTK_TX_FLAGS_SINGLE0;
+ dma_unmap_addr_set(tx_buf, dma_addr0, mapped_addr);
+ dma_unmap_len_set(tx_buf, dma_len0, skb_headlen(skb));
+
+ /* TX SG offload */
+ j = ring->tx_next_idx;
+ k = 0;
+ for (i = 0; i < nr_frags; i++) {
+ offset = 0;
+ frag = &skb_shinfo(skb)->frags[i];
+ frag_size = skb_frag_size(frag);
+
+ while (frag_size > 0) {
+ frag_map_size = min(frag_size, TX_DMA_BUF_LEN);
+ mapped_addr = skb_frag_dma_map(&dev->dev, frag, offset,
+ frag_map_size,
+ DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(&dev->dev, mapped_addr)))
+ goto err_dma;
+
+ if (k & 0x1) {
+ j = NEXT_TX_DESP_IDX(j);
+ txd.txd1 = mapped_addr;
+ txd.txd2 = TX_DMA_PLEN0(frag_map_size);
+ txd.txd4 = def_txd4;
+
+ tx_buf = &ring->tx_buf[j];
+ memset(tx_buf, 0, sizeof(*tx_buf));
+
+ tx_buf->flags |= MTK_TX_FLAGS_PAGE0;
+ dma_unmap_addr_set(tx_buf, dma_addr0,
+ mapped_addr);
+ dma_unmap_len_set(tx_buf, dma_len0,
+ frag_map_size);
+ } else {
+ txd.txd3 = mapped_addr;
+ txd.txd2 |= TX_DMA_PLEN1(frag_map_size);
+
+ tx_buf->skb = (struct sk_buff *)DMA_DUMMY_DESC;
+ tx_buf->flags |= MTK_TX_FLAGS_PAGE1;
+ dma_unmap_addr_set(tx_buf, dma_addr1,
+ mapped_addr);
+ dma_unmap_len_set(tx_buf, dma_len1,
+ frag_map_size);
+
+ if (!((i == (nr_frags - 1)) &&
+ (frag_map_size == frag_size))) {
+ mtk_set_txd_pdma(&txd,
+ &ring->tx_dma[j]);
+ memset(&txd, 0, sizeof(txd));
+ }
+ }
+ frag_size -= frag_map_size;
+ offset += frag_map_size;
+ k++;
+ }
+ }
+
+ /* set last segment */
+ if (k & 0x1)
+ txd.txd2 |= TX_DMA_LS1;
+ else
+ txd.txd2 |= TX_DMA_LS0;
+ mtk_set_txd_pdma(&txd, &ring->tx_dma[j]);
+
+ /* store skb to cleanup */
+ tx_buf->skb = skb;
+
+ netdev_sent_queue(dev, skb->len);
+ skb_tx_timestamp(skb);
+
+ ring->tx_next_idx = NEXT_TX_DESP_IDX(j);
+ /* make sure that all changes to the dma ring are flushed before we
+ * continue
+ */
+ wmb();
+ atomic_set(&ring->tx_free_count, mtk_pdma_empty_txd(ring));
+
+ if (netif_xmit_stopped(netdev_get_tx_queue(dev, 0)) || !skb->xmit_more)
+ mtk_reg_w32(eth, ring->tx_next_idx, MTK_REG_TX_CTX_IDX0);
+
+ return 0;
+
+err_dma:
+ j = ring->tx_next_idx;
+ for (i = 0; i < tx_num; i++) {
+ ptxd = &ring->tx_dma[j];
+ tx_buf = &ring->tx_buf[j];
+
+ /* unmap dma */
+ mtk_txd_unmap(&dev->dev, tx_buf);
+
+ ptxd->txd2 = TX_DMA_DESP2_DEF;
+ j = NEXT_TX_DESP_IDX(j);
+ }
+ /* make sure that all changes to the dma ring are flushed before we
+ * continue
+ */
+ wmb();
+ return -1;
+}
+
+/* the qdma core needs scratch memory to be setup */
+static int mtk_init_fq_dma(struct mtk_eth *eth)
+{
+ unsigned int phy_ring_head, phy_ring_tail;
+ int cnt = eth->soc->dma_ring_size;
+ dma_addr_t dma_addr;
+ int i;
+
+ eth->scratch_ring = dma_alloc_coherent(eth->dev,
+ cnt * sizeof(struct mtk_tx_dma),
+ &phy_ring_head,
+ GFP_ATOMIC | __GFP_ZERO);
+ if (unlikely(!eth->scratch_ring))
+ return -ENOMEM;
+
+ eth->scratch_head = kcalloc(cnt, QDMA_PAGE_SIZE,
+ GFP_KERNEL);
+ dma_addr = dma_map_single(eth->dev,
+ eth->scratch_head, cnt * QDMA_PAGE_SIZE,
+ DMA_FROM_DEVICE);
+ if (unlikely(dma_mapping_error(eth->dev, dma_addr)))
+ return -ENOMEM;
+
+ memset(eth->scratch_ring, 0x0, sizeof(struct mtk_tx_dma) * cnt);
+ phy_ring_tail = phy_ring_head + (sizeof(struct mtk_tx_dma) * (cnt - 1));
+
+ for (i = 0; i < cnt; i++) {
+ eth->scratch_ring[i].txd1 = (dma_addr + (i * QDMA_PAGE_SIZE));
+ if (i < cnt - 1)
+ eth->scratch_ring[i].txd2 = (phy_ring_head +
+ ((i + 1) * sizeof(struct mtk_tx_dma)));
+ eth->scratch_ring[i].txd3 = TX_QDMA_SDL(QDMA_PAGE_SIZE);
+ }
+
+ mtk_w32(eth, phy_ring_head, MTK_QDMA_FQ_HEAD);
+ mtk_w32(eth, phy_ring_tail, MTK_QDMA_FQ_TAIL);
+ mtk_w32(eth, (cnt << 16) | cnt, MTK_QDMA_FQ_CNT);
+ mtk_w32(eth, QDMA_PAGE_SIZE << 16, MTK_QDMA_FQ_BLEN);
+
+ return 0;
+}
+
+static void *mtk_qdma_phys_to_virt(struct mtk_tx_ring *ring, u32 desc)
+{
+ void *ret = ring->tx_dma;
+
+ return ret + (desc - ring->tx_phys);
+}
+
+static struct mtk_tx_dma *mtk_tx_next_qdma(struct mtk_tx_ring *ring,
+ struct mtk_tx_dma *txd)
+{
+ return mtk_qdma_phys_to_virt(ring, txd->txd2);
+}
+
+static struct mtk_tx_buf *mtk_desc_to_tx_buf(struct mtk_tx_ring *ring,
+ struct mtk_tx_dma *txd)
+{
+ int idx = txd - ring->tx_dma;
+
+ return &ring->tx_buf[idx];
+}
+
+static int mtk_qdma_tx_map(struct sk_buff *skb, struct net_device *dev,
+ int tx_num, struct mtk_tx_ring *ring, bool gso)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+ struct mtk_tx_dma *itxd, *txd;
+ struct mtk_tx_buf *tx_buf;
+ dma_addr_t mapped_addr;
+ unsigned int nr_frags;
+ int i, n_desc = 1;
+ u32 txd4 = eth->soc->txd4;
+
+ itxd = ring->tx_next_free;
+ if (itxd == ring->tx_last_free)
+ return -ENOMEM;
+
+ if (eth->soc->mac_count > 1)
+ txd4 |= (mac->id + 1) << TX_DMA_FPORT_SHIFT;
+
+ tx_buf = mtk_desc_to_tx_buf(ring, itxd);
+ memset(tx_buf, 0, sizeof(*tx_buf));
+
+ if (gso)
+ txd4 |= TX_DMA_TSO;
+
+ /* TX Checksum offload */
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ txd4 |= TX_DMA_CHKSUM;
+
+ /* VLAN header offload */
+ if (skb_vlan_tag_present(skb))
+ txd4 |= TX_DMA_INS_VLAN_MT7621 | skb_vlan_tag_get(skb);
+
+ mapped_addr = dma_map_single(&dev->dev, skb->data,
+ skb_headlen(skb), DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(&dev->dev, mapped_addr)))
+ return -ENOMEM;
+
+ WRITE_ONCE(itxd->txd1, mapped_addr);
+ tx_buf->flags |= MTK_TX_FLAGS_SINGLE0;
+ dma_unmap_addr_set(tx_buf, dma_addr0, mapped_addr);
+ dma_unmap_len_set(tx_buf, dma_len0, skb_headlen(skb));
+
+ /* TX SG offload */
+ txd = itxd;
+ nr_frags = skb_shinfo(skb)->nr_frags;
+ for (i = 0; i < nr_frags; i++) {
+ struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i];
+ unsigned int offset = 0;
+ int frag_size = skb_frag_size(frag);
+
+ while (frag_size) {
+ bool last_frag = false;
+ unsigned int frag_map_size;
+
+ txd = mtk_tx_next_qdma(ring, txd);
+ if (txd == ring->tx_last_free)
+ goto err_dma;
+
+ n_desc++;
+ frag_map_size = min(frag_size, TX_DMA_BUF_LEN);
+ mapped_addr = skb_frag_dma_map(&dev->dev, frag, offset,
+ frag_map_size,
+ DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(&dev->dev, mapped_addr)))
+ goto err_dma;
+
+ if (i == nr_frags - 1 &&
+ (frag_size - frag_map_size) == 0)
+ last_frag = true;
+
+ WRITE_ONCE(txd->txd1, mapped_addr);
+ WRITE_ONCE(txd->txd3, (QDMA_TX_SWC |
+ TX_DMA_PLEN0(frag_map_size) |
+ last_frag * TX_DMA_LS0) |
+ mac->id);
+ WRITE_ONCE(txd->txd4, 0);
+
+ tx_buf->skb = (struct sk_buff *)DMA_DUMMY_DESC;
+ tx_buf = mtk_desc_to_tx_buf(ring, txd);
+ memset(tx_buf, 0, sizeof(*tx_buf));
+
+ tx_buf->flags |= MTK_TX_FLAGS_PAGE0;
+ dma_unmap_addr_set(tx_buf, dma_addr0, mapped_addr);
+ dma_unmap_len_set(tx_buf, dma_len0, frag_map_size);
+ frag_size -= frag_map_size;
+ offset += frag_map_size;
+ }
+ }
+
+ /* store skb to cleanup */
+ tx_buf->skb = skb;
+
+ WRITE_ONCE(itxd->txd4, txd4);
+ WRITE_ONCE(itxd->txd3, (QDMA_TX_SWC | TX_DMA_PLEN0(skb_headlen(skb)) |
+ (!nr_frags * TX_DMA_LS0)));
+
+ netdev_sent_queue(dev, skb->len);
+ skb_tx_timestamp(skb);
+
+ ring->tx_next_free = mtk_tx_next_qdma(ring, txd);
+ atomic_sub(n_desc, &ring->tx_free_count);
+
+ /* make sure that all changes to the dma ring are flushed before we
+ * continue
+ */
+ wmb();
+
+ if (netif_xmit_stopped(netdev_get_tx_queue(dev, 0)) || !skb->xmit_more)
+ mtk_w32(eth, txd->txd2, MTK_QTX_CTX_PTR);
+
+ return 0;
+
+err_dma:
+ do {
+ tx_buf = mtk_desc_to_tx_buf(ring, txd);
+
+ /* unmap dma */
+ mtk_txd_unmap(&dev->dev, tx_buf);
+
+ itxd->txd3 = TX_DMA_DESP2_DEF;
+ itxd = mtk_tx_next_qdma(ring, itxd);
+ } while (itxd != txd);
+
+ return -ENOMEM;
+}
+
+static inline int mtk_cal_txd_req(struct sk_buff *skb)
+{
+ int i, nfrags;
+ struct skb_frag_struct *frag;
+
+ nfrags = 1;
+ if (skb_is_gso(skb)) {
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ frag = &skb_shinfo(skb)->frags[i];
+ nfrags += DIV_ROUND_UP(frag->size, TX_DMA_BUF_LEN);
+ }
+ } else {
+ nfrags += skb_shinfo(skb)->nr_frags;
+ }
+
+ return DIV_ROUND_UP(nfrags, 2);
+}
+
+static int mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+ struct mtk_tx_ring *ring = &eth->tx_ring;
+ struct net_device_stats *stats = &dev->stats;
+ int tx_num;
+ int len = skb->len;
+ bool gso = false;
+
+ tx_num = mtk_cal_txd_req(skb);
+ if (unlikely(atomic_read(&ring->tx_free_count) <= tx_num)) {
+ netif_stop_queue(dev);
+ netif_err(eth, tx_queued, dev,
+ "Tx Ring full when queue awake!\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ /* TSO: fill MSS info in tcp checksum field */
+ if (skb_is_gso(skb)) {
+ if (skb_cow_head(skb, 0)) {
+ netif_warn(eth, tx_err, dev,
+ "GSO expand head fail.\n");
+ goto drop;
+ }
+
+ if (skb_shinfo(skb)->gso_type &
+ (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)) {
+ gso = true;
+ tcp_hdr(skb)->check = htons(skb_shinfo(skb)->gso_size);
+ }
+ }
+
+ if (ring->tx_map(skb, dev, tx_num, ring, gso) < 0)
+ goto drop;
+
+ stats->tx_packets++;
+ stats->tx_bytes += len;
+
+ if (unlikely(atomic_read(&ring->tx_free_count) <= ring->tx_thresh)) {
+ netif_stop_queue(dev);
+ smp_mb();
+ if (unlikely(atomic_read(&ring->tx_free_count) >
+ ring->tx_thresh))
+ netif_wake_queue(dev);
+ }
+
+ return NETDEV_TX_OK;
+
+drop:
+ stats->tx_dropped++;
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+}
+
+static int mtk_poll_rx(struct napi_struct *napi, int budget,
+ struct mtk_eth *eth, u32 rx_intr)
+{
+ struct mtk_soc_data *soc = eth->soc;
+ struct mtk_rx_ring *ring = &eth->rx_ring[0];
+ int idx = ring->rx_calc_idx;
+ u32 checksum_bit;
+ struct sk_buff *skb;
+ u8 *data, *new_data;
+ struct mtk_rx_dma *rxd, trxd;
+ int done = 0, pad;
+
+ if (eth->soc->hw_features & NETIF_F_RXCSUM)
+ checksum_bit = soc->checksum_bit;
+ else
+ checksum_bit = 0;
+
+ if (eth->soc->rx_2b_offset)
+ pad = 0;
+ else
+ pad = NET_IP_ALIGN;
+
+ while (done < budget) {
+ struct net_device *netdev;
+ unsigned int pktlen;
+ dma_addr_t dma_addr;
+ int mac = 0;
+
+ idx = NEXT_RX_DESP_IDX(idx);
+ rxd = &ring->rx_dma[idx];
+ data = ring->rx_data[idx];
+
+ mtk_get_rxd(&trxd, rxd);
+ if (!(trxd.rxd2 & RX_DMA_DONE))
+ break;
+
+ /* find out which mac the packet come from. values start at 1 */
+ if (eth->soc->mac_count > 1) {
+ mac = (trxd.rxd4 >> RX_DMA_FPORT_SHIFT) &
+ RX_DMA_FPORT_MASK;
+ mac--;
+ if (mac < 0 || mac >= eth->soc->mac_count)
+ goto release_desc;
+ }
+
+ netdev = eth->netdev[mac];
+
+ /* alloc new buffer */
+ new_data = napi_alloc_frag(ring->frag_size);
+ if (unlikely(!new_data || !netdev)) {
+ netdev->stats.rx_dropped++;
+ goto release_desc;
+ }
+ dma_addr = dma_map_single(&netdev->dev,
+ new_data + NET_SKB_PAD + pad,
+ ring->rx_buf_size,
+ DMA_FROM_DEVICE);
+ if (unlikely(dma_mapping_error(&netdev->dev, dma_addr))) {
+ skb_free_frag(new_data);
+ goto release_desc;
+ }
+
+ /* receive data */
+ skb = build_skb(data, ring->frag_size);
+ if (unlikely(!skb)) {
+ put_page(virt_to_head_page(new_data));
+ goto release_desc;
+ }
+ skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
+
+ dma_unmap_single(&netdev->dev, trxd.rxd1,
+ ring->rx_buf_size, DMA_FROM_DEVICE);
+ pktlen = RX_DMA_GET_PLEN0(trxd.rxd2);
+ skb->dev = netdev;
+ skb_put(skb, pktlen);
+ if (trxd.rxd4 & checksum_bit)
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ else
+ skb_checksum_none_assert(skb);
+ skb->protocol = eth_type_trans(skb, netdev);
+
+ netdev->stats.rx_packets++;
+ netdev->stats.rx_bytes += pktlen;
+
+ if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX &&
+ RX_DMA_VID(trxd.rxd3))
+ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
+ RX_DMA_VID(trxd.rxd3));
+ napi_gro_receive(napi, skb);
+
+ ring->rx_data[idx] = new_data;
+ rxd->rxd1 = (unsigned int)dma_addr;
+
+release_desc:
+ if (eth->soc->rx_sg_dma)
+ rxd->rxd2 = RX_DMA_PLEN0(ring->rx_buf_size);
+ else
+ rxd->rxd2 = RX_DMA_LSO;
+
+ ring->rx_calc_idx = idx;
+ /* make sure that all changes to the dma ring are flushed before
+ * we continue
+ */
+ wmb();
+ if (eth->soc->dma_type == MTK_QDMA)
+ mtk_w32(eth, ring->rx_calc_idx, MTK_QRX_CRX_IDX0);
+ else
+ mtk_reg_w32(eth, ring->rx_calc_idx,
+ MTK_REG_RX_CALC_IDX0);
+ done++;
+ }
+
+ if (done < budget)
+ mtk_irq_ack(eth, rx_intr);
+
+ return done;
+}
+
+static int mtk_pdma_tx_poll(struct mtk_eth *eth, int budget, bool *tx_again)
+{
+ struct sk_buff *skb;
+ struct mtk_tx_buf *tx_buf;
+ int done = 0;
+ u32 idx, hwidx;
+ struct mtk_tx_ring *ring = &eth->tx_ring;
+ unsigned int bytes = 0;
+
+ idx = ring->tx_free_idx;
+ hwidx = mtk_reg_r32(eth, MTK_REG_TX_DTX_IDX0);
+
+ while ((idx != hwidx) && budget) {
+ tx_buf = &ring->tx_buf[idx];
+ skb = tx_buf->skb;
+
+ if (!skb)
+ break;
+
+ if (skb != (struct sk_buff *)DMA_DUMMY_DESC) {
+ bytes += skb->len;
+ done++;
+ budget--;
+ }
+ mtk_txd_unmap(eth->dev, tx_buf);
+ idx = NEXT_TX_DESP_IDX(idx);
+ }
+ ring->tx_free_idx = idx;
+ atomic_set(&ring->tx_free_count, mtk_pdma_empty_txd(ring));
+
+ /* read hw index again make sure no new tx packet */
+ if (idx != hwidx || idx != mtk_reg_r32(eth, MTK_REG_TX_DTX_IDX0))
+ *tx_again = 1;
+
+ if (done)
+ netdev_completed_queue(*eth->netdev, done, bytes);
+
+ return done;
+}
+
+static int mtk_qdma_tx_poll(struct mtk_eth *eth, int budget, bool *tx_again)
+{
+ struct mtk_tx_ring *ring = &eth->tx_ring;
+ struct mtk_tx_dma *desc;
+ struct sk_buff *skb;
+ struct mtk_tx_buf *tx_buf;
+ int total = 0, done[MTK_MAX_DEVS];
+ unsigned int bytes[MTK_MAX_DEVS];
+ u32 cpu, dma;
+ static int condition;
+ int i;
+
+ memset(done, 0, sizeof(done));
+ memset(bytes, 0, sizeof(bytes));
+
+ cpu = mtk_r32(eth, MTK_QTX_CRX_PTR);
+ dma = mtk_r32(eth, MTK_QTX_DRX_PTR);
+
+ desc = mtk_qdma_phys_to_virt(ring, cpu);
+
+ while ((cpu != dma) && budget) {
+ u32 next_cpu = desc->txd2;
+ int mac;
+
+ desc = mtk_tx_next_qdma(ring, desc);
+ if ((desc->txd3 & QDMA_TX_OWNER_CPU) == 0)
+ break;
+
+ mac = (desc->txd4 >> TX_DMA_FPORT_SHIFT) &
+ TX_DMA_FPORT_MASK;
+ mac--;
+
+ tx_buf = mtk_desc_to_tx_buf(ring, desc);
+ skb = tx_buf->skb;
+ if (!skb) {
+ condition = 1;
+ break;
+ }
+
+ if (skb != (struct sk_buff *)DMA_DUMMY_DESC) {
+ bytes[mac] += skb->len;
+ done[mac]++;
+ budget--;
+ }
+ mtk_txd_unmap(eth->dev, tx_buf);
+
+ ring->tx_last_free->txd2 = next_cpu;
+ ring->tx_last_free = desc;
+ atomic_inc(&ring->tx_free_count);
+
+ cpu = next_cpu;
+ }
+
+ mtk_w32(eth, cpu, MTK_QTX_CRX_PTR);
+
+ /* read hw index again make sure no new tx packet */
+ if (cpu != dma || cpu != mtk_r32(eth, MTK_QTX_DRX_PTR))
+ *tx_again = true;
+
+ for (i = 0; i < eth->soc->mac_count; i++) {
+ if (!done[i])
+ continue;
+ netdev_completed_queue(eth->netdev[i], done[i], bytes[i]);
+ total += done[i];
+ }
+
+ return total;
+}
+
+static int mtk_poll_tx(struct mtk_eth *eth, int budget, u32 tx_intr,
+ bool *tx_again)
+{
+ struct mtk_tx_ring *ring = &eth->tx_ring;
+ struct net_device *netdev = eth->netdev[0];
+ int done;
+
+ done = eth->tx_ring.tx_poll(eth, budget, tx_again);
+ if (!*tx_again)
+ mtk_irq_ack(eth, tx_intr);
+
+ if (!done)
+ return 0;
+
+ smp_mb();
+ if (unlikely(!netif_queue_stopped(netdev)))
+ return done;
+
+ if (atomic_read(&ring->tx_free_count) > ring->tx_thresh)
+ netif_wake_queue(netdev);
+
+ return done;
+}
+
+static void mtk_stats_update(struct mtk_eth *eth)
+{
+ int i;
+
+ for (i = 0; i < eth->soc->mac_count; i++) {
+ if (!eth->mac[i] || !eth->mac[i]->hw_stats)
+ continue;
+ if (spin_trylock(&eth->mac[i]->hw_stats->stats_lock)) {
+ mtk_stats_update_mac(eth->mac[i]);
+ spin_unlock(&eth->mac[i]->hw_stats->stats_lock);
+ }
+ }
+}
+
+static int mtk_poll(struct napi_struct *napi, int budget)
+{
+ struct mtk_eth *eth = container_of(napi, struct mtk_eth, rx_napi);
+ u32 status, mtk_status, mask, tx_intr, rx_intr, status_intr;
+ int tx_done, rx_done;
+ bool tx_again = false;
+
+ status = mtk_irq_pending(eth);
+ mtk_status = mtk_irq_pending_status(eth);
+ tx_intr = eth->soc->tx_int;
+ rx_intr = eth->soc->rx_int;
+ status_intr = eth->soc->status_int;
+ tx_done = 0;
+ rx_done = 0;
+ tx_again = 0;
+
+ if (status & tx_intr)
+ tx_done = mtk_poll_tx(eth, budget, tx_intr, &tx_again);
+
+ if (status & rx_intr)
+ rx_done = mtk_poll_rx(napi, budget, eth, rx_intr);
+
+ if (unlikely(mtk_status & status_intr)) {
+ mtk_stats_update(eth);
+ mtk_irq_ack_status(eth, status_intr);
+ }
+
+ if (unlikely(netif_msg_intr(eth))) {
+ mask = mtk_irq_enabled(eth);
+ netdev_info(eth->netdev[0],
+ "done tx %d, rx %d, intr 0x%08x/0x%x\n",
+ tx_done, rx_done, status, mask);
+ }
+
+ if (tx_again || rx_done == budget)
+ return budget;
+
+ status = mtk_irq_pending(eth);
+ if (status & (tx_intr | rx_intr))
+ return budget;
+
+ napi_complete(napi);
+ mtk_irq_enable(eth, tx_intr | rx_intr);
+
+ return rx_done;
+}
+
+static int mtk_pdma_tx_alloc(struct mtk_eth *eth)
+{
+ int i;
+ struct mtk_tx_ring *ring = &eth->tx_ring;
+
+ ring->tx_ring_size = eth->soc->dma_ring_size;
+ ring->tx_free_idx = 0;
+ ring->tx_next_idx = 0;
+ ring->tx_thresh = max((unsigned long)ring->tx_ring_size >> 2,
+ MAX_SKB_FRAGS);
+
+ ring->tx_buf = kcalloc(ring->tx_ring_size, sizeof(*ring->tx_buf),
+ GFP_KERNEL);
+ if (!ring->tx_buf)
+ goto no_tx_mem;
+
+ ring->tx_dma = dma_alloc_coherent(eth->dev,
+ ring->tx_ring_size * sizeof(*ring->tx_dma),
+ &ring->tx_phys,
+ GFP_ATOMIC | __GFP_ZERO);
+ if (!ring->tx_dma)
+ goto no_tx_mem;
+
+ for (i = 0; i < ring->tx_ring_size; i++) {
+ ring->tx_dma[i].txd2 = TX_DMA_DESP2_DEF;
+ ring->tx_dma[i].txd4 = eth->soc->txd4;
+ }
+
+ atomic_set(&ring->tx_free_count, mtk_pdma_empty_txd(ring));
+ ring->tx_map = mtk_pdma_tx_map;
+ ring->tx_poll = mtk_pdma_tx_poll;
+ ring->tx_clean = mtk_pdma_tx_clean;
+
+ /* make sure that all changes to the dma ring are flushed before we
+ * continue
+ */
+ wmb();
+
+ mtk_reg_w32(eth, ring->tx_phys, MTK_REG_TX_BASE_PTR0);
+ mtk_reg_w32(eth, ring->tx_ring_size, MTK_REG_TX_MAX_CNT0);
+ mtk_reg_w32(eth, 0, MTK_REG_TX_CTX_IDX0);
+ mtk_reg_w32(eth, MTK_PST_DTX_IDX0, MTK_REG_PDMA_RST_CFG);
+
+ return 0;
+
+no_tx_mem:
+ return -ENOMEM;
+}
+
+static int mtk_qdma_tx_alloc_tx(struct mtk_eth *eth)
+{
+ struct mtk_tx_ring *ring = &eth->tx_ring;
+ int i, sz = sizeof(*ring->tx_dma);
+
+ ring->tx_ring_size = eth->soc->dma_ring_size;
+ ring->tx_buf = kcalloc(ring->tx_ring_size, sizeof(*ring->tx_buf),
+ GFP_KERNEL);
+ if (!ring->tx_buf)
+ goto no_tx_mem;
+
+ ring->tx_dma = dma_alloc_coherent(eth->dev,
+ ring->tx_ring_size * sz,
+ &ring->tx_phys,
+ GFP_ATOMIC | __GFP_ZERO);
+ if (!ring->tx_dma)
+ goto no_tx_mem;
+
+ memset(ring->tx_dma, 0, ring->tx_ring_size * sz);
+ for (i = 0; i < ring->tx_ring_size; i++) {
+ int next = (i + 1) % ring->tx_ring_size;
+ u32 next_ptr = ring->tx_phys + next * sz;
+
+ ring->tx_dma[i].txd2 = next_ptr;
+ ring->tx_dma[i].txd3 = TX_DMA_DESP2_DEF;
+ }
+
+ atomic_set(&ring->tx_free_count, ring->tx_ring_size - 2);
+ ring->tx_next_free = &ring->tx_dma[0];
+ ring->tx_last_free = &ring->tx_dma[ring->tx_ring_size - 2];
+ ring->tx_thresh = max((unsigned long)ring->tx_ring_size >> 2,
+ MAX_SKB_FRAGS);
+
+ ring->tx_map = mtk_qdma_tx_map;
+ ring->tx_poll = mtk_qdma_tx_poll;
+ ring->tx_clean = mtk_qdma_tx_clean;
+
+ /* make sure that all changes to the dma ring are flushed before we
+ * continue
+ */
+ wmb();
+
+ mtk_w32(eth, ring->tx_phys, MTK_QTX_CTX_PTR);
+ mtk_w32(eth, ring->tx_phys, MTK_QTX_DTX_PTR);
+ mtk_w32(eth,
+ ring->tx_phys + ((ring->tx_ring_size - 1) * sz),
+ MTK_QTX_CRX_PTR);
+ mtk_w32(eth,
+ ring->tx_phys + ((ring->tx_ring_size - 1) * sz),
+ MTK_QTX_DRX_PTR);
+
+ return 0;
+
+no_tx_mem:
+ return -ENOMEM;
+}
+
+static int mtk_qdma_init(struct mtk_eth *eth, int ring)
+{
+ int err;
+
+ err = mtk_init_fq_dma(eth);
+ if (err)
+ return err;
+
+ err = mtk_qdma_tx_alloc_tx(eth);
+ if (err)
+ return err;
+
+ err = mtk_dma_rx_alloc(eth, &eth->rx_ring[ring]);
+ if (err)
+ return err;
+
+ mtk_w32(eth, eth->rx_ring[ring].rx_phys, MTK_QRX_BASE_PTR0);
+ mtk_w32(eth, eth->rx_ring[ring].rx_ring_size, MTK_QRX_MAX_CNT0);
+ mtk_w32(eth, eth->rx_ring[ring].rx_calc_idx, MTK_QRX_CRX_IDX0);
+ mtk_w32(eth, MTK_PST_DRX_IDX0, MTK_QDMA_RST_IDX);
+ mtk_w32(eth, (QDMA_RES_THRES << 8) | QDMA_RES_THRES, MTK_QTX_CFG(0));
+
+ /* Enable random early drop and set drop threshold automatically */
+ mtk_w32(eth, 0x174444, MTK_QDMA_FC_THRES);
+ mtk_w32(eth, 0x0, MTK_QDMA_HRED2);
+
+ return 0;
+}
+
+static int mtk_pdma_qdma_init(struct mtk_eth *eth)
+{
+ int err = mtk_qdma_init(eth, 1);
+
+ if (err)
+ return err;
+
+ err = mtk_dma_rx_alloc(eth, &eth->rx_ring[0]);
+ if (err)
+ return err;
+
+ mtk_reg_w32(eth, eth->rx_ring[0].rx_phys, MTK_REG_RX_BASE_PTR0);
+ mtk_reg_w32(eth, eth->rx_ring[0].rx_ring_size, MTK_REG_RX_MAX_CNT0);
+ mtk_reg_w32(eth, eth->rx_ring[0].rx_calc_idx, MTK_REG_RX_CALC_IDX0);
+ mtk_reg_w32(eth, MTK_PST_DRX_IDX0, MTK_REG_PDMA_RST_CFG);
+
+ return 0;
+}
+
+static int mtk_pdma_init(struct mtk_eth *eth)
+{
+ struct mtk_rx_ring *ring = &eth->rx_ring[0];
+ int err;
+
+ err = mtk_pdma_tx_alloc(eth);
+ if (err)
+ return err;
+
+ err = mtk_dma_rx_alloc(eth, ring);
+ if (err)
+ return err;
+
+ mtk_reg_w32(eth, ring->rx_phys, MTK_REG_RX_BASE_PTR0);
+ mtk_reg_w32(eth, ring->rx_ring_size, MTK_REG_RX_MAX_CNT0);
+ mtk_reg_w32(eth, ring->rx_calc_idx, MTK_REG_RX_CALC_IDX0);
+ mtk_reg_w32(eth, MTK_PST_DRX_IDX0, MTK_REG_PDMA_RST_CFG);
+
+ return 0;
+}
+
+static void mtk_dma_free(struct mtk_eth *eth)
+{
+ int i;
+
+ for (i = 0; i < eth->soc->mac_count; i++)
+ if (eth->netdev[i])
+ netdev_reset_queue(eth->netdev[i]);
+ eth->tx_ring.tx_clean(eth);
+ mtk_clean_rx(eth, &eth->rx_ring[0]);
+ mtk_clean_rx(eth, &eth->rx_ring[1]);
+ kfree(eth->scratch_head);
+}
+
+static void mtk_tx_timeout(struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+ struct mtk_tx_ring *ring = &eth->tx_ring;
+
+ eth->netdev[mac->id]->stats.tx_errors++;
+ netif_err(eth, tx_err, dev,
+ "transmit timed out\n");
+ if (eth->soc->dma_type & MTK_PDMA) {
+ netif_info(eth, drv, dev, "pdma_cfg:%08x\n",
+ mtk_reg_r32(eth, MTK_REG_PDMA_GLO_CFG));
+ netif_info(eth, drv, dev, "tx_ring=%d, "
+ "base=%08x, max=%u, ctx=%u, dtx=%u, fdx=%hu, next=%hu\n",
+ 0, mtk_reg_r32(eth, MTK_REG_TX_BASE_PTR0),
+ mtk_reg_r32(eth, MTK_REG_TX_MAX_CNT0),
+ mtk_reg_r32(eth, MTK_REG_TX_CTX_IDX0),
+ mtk_reg_r32(eth, MTK_REG_TX_DTX_IDX0),
+ ring->tx_free_idx,
+ ring->tx_next_idx);
+ }
+ if (eth->soc->dma_type & MTK_QDMA) {
+ netif_info(eth, drv, dev, "qdma_cfg:%08x\n",
+ mtk_r32(eth, MTK_QDMA_GLO_CFG));
+ netif_info(eth, drv, dev, "tx_ring=%d, "
+ "ctx=%08x, dtx=%08x, crx=%08x, drx=%08x, free=%hu\n",
+ 0, mtk_r32(eth, MTK_QTX_CTX_PTR),
+ mtk_r32(eth, MTK_QTX_DTX_PTR),
+ mtk_r32(eth, MTK_QTX_CRX_PTR),
+ mtk_r32(eth, MTK_QTX_DRX_PTR),
+ atomic_read(&ring->tx_free_count));
+ }
+ netif_info(eth, drv, dev,
+ "rx_ring=%d, base=%08x, max=%u, calc=%u, drx=%u\n",
+ 0, mtk_reg_r32(eth, MTK_REG_RX_BASE_PTR0),
+ mtk_reg_r32(eth, MTK_REG_RX_MAX_CNT0),
+ mtk_reg_r32(eth, MTK_REG_RX_CALC_IDX0),
+ mtk_reg_r32(eth, MTK_REG_RX_DRX_IDX0));
+
+ schedule_work(&mac->pending_work);
+}
+
+static irqreturn_t mtk_handle_irq(int irq, void *_eth)
+{
+ struct mtk_eth *eth = _eth;
+ u32 status, int_mask;
+
+ status = mtk_irq_pending(eth);
+ if (unlikely(!status))
+ return IRQ_NONE;
+
+ int_mask = (eth->soc->rx_int | eth->soc->tx_int);
+ if (likely(status & int_mask)) {
+ if (likely(napi_schedule_prep(&eth->rx_napi)))
+ __napi_schedule(&eth->rx_napi);
+ } else {
+ mtk_irq_ack(eth, status);
+ }
+ mtk_irq_disable(eth, int_mask);
+
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void mtk_poll_controller(struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+ u32 int_mask = eth->soc->tx_int | eth->soc->rx_int;
+
+ mtk_irq_disable(eth, int_mask);
+ mtk_handle_irq(dev->irq, dev);
+ mtk_irq_enable(eth, int_mask);
+}
+#endif
+
+int mtk_set_clock_cycle(struct mtk_eth *eth)
+{
+ unsigned long sysclk = eth->sysclk;
+
+ sysclk /= MTK_US_CYC_CNT_DIVISOR;
+ sysclk <<= MTK_US_CYC_CNT_SHIFT;
+
+ mtk_w32(eth, (mtk_r32(eth, MTK_GLO_CFG) &
+ ~(MTK_US_CYC_CNT_MASK << MTK_US_CYC_CNT_SHIFT)) |
+ sysclk,
+ MTK_GLO_CFG);
+ return 0;
+}
+
+void mtk_fwd_config(struct mtk_eth *eth)
+{
+ u32 fwd_cfg;
+
+ fwd_cfg = mtk_r32(eth, MTK_GDMA1_FWD_CFG);
+
+ /* disable jumbo frame */
+ if (eth->soc->jumbo_frame)
+ fwd_cfg &= ~MTK_GDM1_JMB_EN;
+
+ /* set unicast/multicast/broadcast frame to cpu */
+ fwd_cfg &= ~0xffff;
+
+ mtk_w32(eth, fwd_cfg, MTK_GDMA1_FWD_CFG);
+}
+
+void mtk_csum_config(struct mtk_eth *eth)
+{
+ if (eth->soc->hw_features & NETIF_F_RXCSUM)
+ mtk_w32(eth, mtk_r32(eth, MTK_GDMA1_FWD_CFG) |
+ (MTK_GDM1_ICS_EN | MTK_GDM1_TCS_EN | MTK_GDM1_UCS_EN),
+ MTK_GDMA1_FWD_CFG);
+ else
+ mtk_w32(eth, mtk_r32(eth, MTK_GDMA1_FWD_CFG) &
+ ~(MTK_GDM1_ICS_EN | MTK_GDM1_TCS_EN | MTK_GDM1_UCS_EN),
+ MTK_GDMA1_FWD_CFG);
+ if (eth->soc->hw_features & NETIF_F_IP_CSUM)
+ mtk_w32(eth, mtk_r32(eth, MTK_CDMA_CSG_CFG) |
+ (MTK_ICS_GEN_EN | MTK_TCS_GEN_EN | MTK_UCS_GEN_EN),
+ MTK_CDMA_CSG_CFG);
+ else
+ mtk_w32(eth, mtk_r32(eth, MTK_CDMA_CSG_CFG) &
+ ~(MTK_ICS_GEN_EN | MTK_TCS_GEN_EN | MTK_UCS_GEN_EN),
+ MTK_CDMA_CSG_CFG);
+}
+
+static int mtk_start_dma(struct mtk_eth *eth)
+{
+ unsigned long flags;
+ u32 val;
+ int err;
+
+ if (eth->soc->dma_type == MTK_PDMA)
+ err = mtk_pdma_init(eth);
+ else if (eth->soc->dma_type == MTK_QDMA)
+ err = mtk_qdma_init(eth, 0);
+ else
+ err = mtk_pdma_qdma_init(eth);
+ if (err) {
+ mtk_dma_free(eth);
+ return err;
+ }
+
+ spin_lock_irqsave(&eth->page_lock, flags);
+
+ val = MTK_TX_WB_DDONE | MTK_RX_DMA_EN | MTK_TX_DMA_EN;
+ if (eth->soc->rx_2b_offset)
+ val |= MTK_RX_2B_OFFSET;
+ val |= eth->soc->pdma_glo_cfg;
+
+ if (eth->soc->dma_type & MTK_PDMA)
+ mtk_reg_w32(eth, val, MTK_REG_PDMA_GLO_CFG);
+
+ if (eth->soc->dma_type & MTK_QDMA)
+ mtk_w32(eth, val, MTK_QDMA_GLO_CFG);
+
+ spin_unlock_irqrestore(&eth->page_lock, flags);
+
+ return 0;
+}
+
+static int mtk_open(struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+
+ if (!atomic_read(&eth->dma_refcnt)) {
+ int err = mtk_start_dma(eth);
+
+ if (err)
+ return err;
+
+ napi_enable(&eth->rx_napi);
+ mtk_irq_enable(eth, eth->soc->tx_int | eth->soc->rx_int);
+ }
+ atomic_inc(&eth->dma_refcnt);
+
+ if (eth->phy)
+ eth->phy->start(mac);
+
+ if (eth->soc->has_carrier && eth->soc->has_carrier(eth))
+ netif_carrier_on(dev);
+
+ netif_start_queue(dev);
+ eth->soc->fwd_config(eth);
+
+ return 0;
+}
+
+static void mtk_stop_dma(struct mtk_eth *eth, u32 glo_cfg)
+{
+ unsigned long flags;
+ u32 val;
+ int i;
+
+ /* stop the dma enfine */
+ spin_lock_irqsave(&eth->page_lock, flags);
+ val = mtk_r32(eth, glo_cfg);
+ mtk_w32(eth, val & ~(MTK_TX_WB_DDONE | MTK_RX_DMA_EN | MTK_TX_DMA_EN),
+ glo_cfg);
+ spin_unlock_irqrestore(&eth->page_lock, flags);
+
+ /* wait for dma stop */
+ for (i = 0; i < 10; i++) {
+ val = mtk_r32(eth, glo_cfg);
+ if (val & (MTK_TX_DMA_BUSY | MTK_RX_DMA_BUSY)) {
+ msleep(20);
+ continue;
+ }
+ break;
+ }
+}
+
+static int mtk_stop(struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+
+ netif_tx_disable(dev);
+ if (eth->phy)
+ eth->phy->stop(mac);
+
+ if (!atomic_dec_and_test(&eth->dma_refcnt))
+ return 0;
+
+ mtk_irq_disable(eth, eth->soc->tx_int | eth->soc->rx_int);
+ napi_disable(&eth->rx_napi);
+
+ if (eth->soc->dma_type & MTK_PDMA)
+ mtk_stop_dma(eth, mtk_reg_table[MTK_REG_PDMA_GLO_CFG]);
+
+ if (eth->soc->dma_type & MTK_QDMA)
+ mtk_stop_dma(eth, MTK_QDMA_GLO_CFG);
+
+ mtk_dma_free(eth);
+
+ return 0;
+}
+
+static int __init mtk_init_hw(struct mtk_eth *eth)
+{
+ int i, err;
+
+ eth->soc->reset_fe(eth);
+
+ if (eth->soc->switch_init)
+ if (eth->soc->switch_init(eth)) {
+ dev_err(eth->dev, "failed to initialize switch core\n");
+ return -ENODEV;
+ }
+
+ err = devm_request_irq(eth->dev, eth->irq, mtk_handle_irq, 0,
+ dev_name(eth->dev), eth);
+ if (err)
+ return err;
+
+ err = mtk_mdio_init(eth);
+ if (err)
+ return err;
+
+ /* disable delay and normal interrupt */
+ mtk_reg_w32(eth, 0, MTK_REG_DLY_INT_CFG);
+ if (eth->soc->dma_type & MTK_QDMA)
+ mtk_w32(eth, 0, MTK_QDMA_DELAY_INT);
+ mtk_irq_disable(eth, eth->soc->tx_int | eth->soc->rx_int);
+
+ /* frame engine will push VLAN tag regarding to VIDX field in Tx desc */
+ if (mtk_reg_table[MTK_REG_MTK_DMA_VID_BASE])
+ for (i = 0; i < 16; i += 2)
+ mtk_w32(eth, ((i + 1) << 16) + i,
+ mtk_reg_table[MTK_REG_MTK_DMA_VID_BASE] +
+ (i * 2));
+
+ if (eth->soc->fwd_config(eth))
+ dev_err(eth->dev, "unable to get clock\n");
+
+ if (mtk_reg_table[MTK_REG_MTK_RST_GL]) {
+ mtk_reg_w32(eth, 1, MTK_REG_MTK_RST_GL);
+ mtk_reg_w32(eth, 0, MTK_REG_MTK_RST_GL);
+ }
+
+ return 0;
+}
+
+static int __init mtk_init(struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+ struct device_node *port;
+ const char *mac_addr;
+ int err;
+
+ mac_addr = of_get_mac_address(mac->of_node);
+ if (mac_addr)
+ ether_addr_copy(dev->dev_addr, mac_addr);
+
+ /* If the mac address is invalid, use random mac address */
+ if (!is_valid_ether_addr(dev->dev_addr)) {
+ random_ether_addr(dev->dev_addr);
+ dev_err(eth->dev, "generated random MAC address %pM\n",
+ dev->dev_addr);
+ dev->addr_assign_type = NET_ADDR_RANDOM;
+ }
+ mac->hw->soc->set_mac(mac, dev->dev_addr);
+
+ if (eth->soc->port_init)
+ for_each_child_of_node(mac->of_node, port)
+ if (of_device_is_compatible(port,
+ "mediatek,eth-port") &&
+ of_device_is_available(port))
+ eth->soc->port_init(eth, mac, port);
+
+ if (eth->phy) {
+ err = eth->phy->connect(mac);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static void mtk_uninit(struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+
+ if (eth->phy)
+ eth->phy->disconnect(mac);
+ mtk_mdio_cleanup(eth);
+
+ mtk_irq_disable(eth, ~0);
+ free_irq(dev->irq, dev);
+}
+
+static int mtk_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+
+ if (!mac->phy_dev)
+ return -ENODEV;
+
+ switch (cmd) {
+ case SIOCGMIIPHY:
+ case SIOCGMIIREG:
+ case SIOCSMIIREG:
+ return phy_mii_ioctl(mac->phy_dev, ifr, cmd);
+ default:
+ break;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static int mtk_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+ int frag_size, old_mtu;
+ u32 fwd_cfg;
+
+ if (!eth->soc->jumbo_frame)
+ return eth_change_mtu(dev, new_mtu);
+
+ frag_size = mtk_max_frag_size(new_mtu);
+ if (new_mtu < 68 || frag_size > PAGE_SIZE)
+ return -EINVAL;
+
+ old_mtu = dev->mtu;
+ dev->mtu = new_mtu;
+
+ /* return early if the buffer sizes will not change */
+ if (old_mtu <= ETH_DATA_LEN && new_mtu <= ETH_DATA_LEN)
+ return 0;
+ if (old_mtu > ETH_DATA_LEN && new_mtu > ETH_DATA_LEN)
+ return 0;
+
+ if (new_mtu <= ETH_DATA_LEN)
+ eth->rx_ring[0].frag_size = mtk_max_frag_size(ETH_DATA_LEN);
+ else
+ eth->rx_ring[0].frag_size = PAGE_SIZE;
+ eth->rx_ring[0].rx_buf_size =
+ mtk_max_buf_size(eth->rx_ring[0].frag_size);
+
+ if (!netif_running(dev))
+ return 0;
+
+ mtk_stop(dev);
+ fwd_cfg = mtk_r32(eth, MTK_GDMA1_FWD_CFG);
+ if (new_mtu <= ETH_DATA_LEN) {
+ fwd_cfg &= ~MTK_GDM1_JMB_EN;
+ } else {
+ fwd_cfg &= ~(MTK_GDM1_JMB_LEN_MASK << MTK_GDM1_JMB_LEN_SHIFT);
+ fwd_cfg |= (DIV_ROUND_UP(frag_size, 1024) <<
+ MTK_GDM1_JMB_LEN_SHIFT) | MTK_GDM1_JMB_EN;
+ }
+ mtk_w32(eth, fwd_cfg, MTK_GDMA1_FWD_CFG);
+
+ return mtk_open(dev);
+}
+
+static void mtk_pending_work(struct work_struct *work)
+{
+ struct mtk_mac *mac = container_of(work, struct mtk_mac, pending_work);
+ struct mtk_eth *eth = mac->hw;
+ struct net_device *dev = eth->netdev[mac->id];
+ int err;
+
+ rtnl_lock();
+ mtk_stop(dev);
+
+ err = mtk_open(dev);
+ if (err) {
+ netif_alert(eth, ifup, dev,
+ "Driver up/down cycle failed, closing device.\n");
+ dev_close(dev);
+ }
+ rtnl_unlock();
+}
+
+static int mtk_cleanup(struct mtk_eth *eth)
+{
+ int i;
+
+ for (i = 0; i < eth->soc->mac_count; i++) {
+ struct mtk_mac *mac = netdev_priv(eth->netdev[i]);
+
+ if (!eth->netdev[i])
+ continue;
+
+ unregister_netdev(eth->netdev[i]);
+ free_netdev(eth->netdev[i]);
+ cancel_work_sync(&mac->pending_work);
+ }
+
+ return 0;
+}
+
+static const struct net_device_ops mtk_netdev_ops = {
+ .ndo_init = mtk_init,
+ .ndo_uninit = mtk_uninit,
+ .ndo_open = mtk_open,
+ .ndo_stop = mtk_stop,
+ .ndo_start_xmit = mtk_start_xmit,
+ .ndo_set_mac_address = mtk_set_mac_address,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_do_ioctl = mtk_do_ioctl,
+ .ndo_change_mtu = mtk_change_mtu,
+ .ndo_tx_timeout = mtk_tx_timeout,
+ .ndo_get_stats64 = mtk_get_stats64,
+ .ndo_vlan_rx_add_vid = mtk_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = mtk_vlan_rx_kill_vid,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = mtk_poll_controller,
+#endif
+};
+
+static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
+{
+ struct mtk_mac *mac;
+ const __be32 *_id = of_get_property(np, "reg", NULL);
+ int id, err;
+
+ if (!_id) {
+ dev_err(eth->dev, "missing mac id\n");
+ return -EINVAL;
+ }
+ id = be32_to_cpup(_id);
+ if (id >= eth->soc->mac_count || eth->netdev[id]) {
+ dev_err(eth->dev, "%d is not a valid mac id\n", id);
+ return -EINVAL;
+ }
+
+ eth->netdev[id] = alloc_etherdev(sizeof(*mac));
+ if (!eth->netdev[id]) {
+ dev_err(eth->dev, "alloc_etherdev failed\n");
+ return -ENOMEM;
+ }
+ mac = netdev_priv(eth->netdev[id]);
+ eth->mac[id] = mac;
+ mac->id = id;
+ mac->hw = eth;
+ mac->of_node = np;
+ INIT_WORK(&mac->pending_work, mtk_pending_work);
+
+ if (mtk_reg_table[MTK_REG_MTK_COUNTER_BASE]) {
+ mac->hw_stats = devm_kzalloc(eth->dev,
+ sizeof(*mac->hw_stats),
+ GFP_KERNEL);
+ if (!mac->hw_stats)
+ return -ENOMEM;
+ spin_lock_init(&mac->hw_stats->stats_lock);
+ mac->hw_stats->reg_offset = id * MTK_STAT_OFFSET;
+ }
+
+ SET_NETDEV_DEV(eth->netdev[id], eth->dev);
+ eth->netdev[id]->netdev_ops = &mtk_netdev_ops;
+ eth->netdev[id]->base_addr = (unsigned long)eth->base;
+
+ if (eth->soc->init_data)
+ eth->soc->init_data(eth->soc, eth->netdev[id]);
+
+ eth->netdev[id]->vlan_features = eth->soc->hw_features &
+ ~(NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX);
+ eth->netdev[id]->features |= eth->soc->hw_features;
+
+ if (mtk_reg_table[MTK_REG_MTK_DMA_VID_BASE])
+ eth->netdev[id]->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
+
+ mtk_set_ethtool_ops(eth->netdev[id]);
+
+ err = register_netdev(eth->netdev[id]);
+ if (err) {
+ dev_err(eth->dev, "error bringing up device\n");
+ return err;
+ }
+ eth->netdev[id]->irq = eth->irq;
+ netif_info(eth, probe, eth->netdev[id],
+ "mediatek frame engine at 0x%08lx, irq %d\n",
+ eth->netdev[id]->base_addr, eth->netdev[id]->irq);
+
+ return 0;
+}
+
+static int mtk_probe(struct platform_device *pdev)
+{
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ const struct of_device_id *match;
+ struct device_node *mac_np;
+ struct mtk_soc_data *soc;
+ struct mtk_eth *eth;
+ struct clk *sysclk;
+ int err;
+
+ pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+ pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
+
+ device_reset(&pdev->dev);
+
+ match = of_match_device(of_mtk_match, &pdev->dev);
+ soc = (struct mtk_soc_data *)match->data;
+
+ if (soc->reg_table)
+ mtk_reg_table = soc->reg_table;
+
+ eth = devm_kzalloc(&pdev->dev, sizeof(*eth), GFP_KERNEL);
+ if (!eth)
+ return -ENOMEM;
+
+ eth->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(eth->base))
+ return PTR_ERR(eth->base);
+
+ spin_lock_init(&eth->page_lock);
+
+ eth->ethsys = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+ "mediatek,ethsys");
+ if (IS_ERR(eth->ethsys))
+ return PTR_ERR(eth->ethsys);
+
+ eth->irq = platform_get_irq(pdev, 0);
+ if (eth->irq < 0) {
+ dev_err(&pdev->dev, "no IRQ resource found\n");
+ return -ENXIO;
+ }
+
+ sysclk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(sysclk)) {
+ dev_err(&pdev->dev,
+ "the clock is not defined in the devicetree\n");
+ return -ENXIO;
+ }
+ eth->sysclk = clk_get_rate(sysclk);
+
+ eth->switch_np = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,switch", 0);
+ if (soc->has_switch && !eth->switch_np) {
+ dev_err(&pdev->dev, "failed to read switch phandle\n");
+ return -ENODEV;
+ }
+
+ eth->dev = &pdev->dev;
+ eth->soc = soc;
+ eth->msg_enable = netif_msg_init(mtk_msg_level, MTK_DEFAULT_MSG_ENABLE);
+
+ err = mtk_init_hw(eth);
+ if (err)
+ return err;
+
+ if (eth->soc->mac_count > 1) {
+ for_each_child_of_node(pdev->dev.of_node, mac_np) {
+ if (!of_device_is_compatible(mac_np,
+ "mediatek,eth-mac"))
+ continue;
+
+ if (!of_device_is_available(mac_np))
+ continue;
+
+ err = mtk_add_mac(eth, mac_np);
+ if (err)
+ goto err_free_dev;
+ }
+
+ init_dummy_netdev(&eth->dummy_dev);
+ netif_napi_add(&eth->dummy_dev, &eth->rx_napi, mtk_poll,
+ soc->napi_weight);
+ } else {
+ err = mtk_add_mac(eth, pdev->dev.of_node);
+ if (err)
+ goto err_free_dev;
+ netif_napi_add(eth->netdev[0], &eth->rx_napi, mtk_poll,
+ soc->napi_weight);
+ }
+
+ platform_set_drvdata(pdev, eth);
+
+ return 0;
+
+err_free_dev:
+ mtk_cleanup(eth);
+ return err;
+}
+
+static int mtk_remove(struct platform_device *pdev)
+{
+ struct mtk_eth *eth = platform_get_drvdata(pdev);
+
+ netif_napi_del(&eth->rx_napi);
+ mtk_cleanup(eth);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver mtk_driver = {
+ .probe = mtk_probe,
+ .remove = mtk_remove,
+ .driver = {
+ .name = "mtk_soc_eth",
+ .owner = THIS_MODULE,
+ .of_match_table = of_mtk_match,
+ },
+};
+
+module_platform_driver(mtk_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
+MODULE_DESCRIPTION("Ethernet driver for MediaTek SoC");
diff --git a/drivers/staging/mt7621-eth/mtk_eth_soc.h b/drivers/staging/mt7621-eth/mtk_eth_soc.h
new file mode 100644
index 000000000000..443f88d8af65
--- /dev/null
+++ b/drivers/staging/mt7621-eth/mtk_eth_soc.h
@@ -0,0 +1,721 @@
+/* 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 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
+ */
+
+#ifndef MTK_ETH_H
+#define MTK_ETH_H
+
+#include <linux/mii.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/dma-mapping.h>
+#include <linux/phy.h>
+#include <linux/ethtool.h>
+#include <linux/version.h>
+#include <linux/atomic.h>
+
+/* these registers have different offsets depending on the SoC. we use a lookup
+ * table for these
+ */
+enum mtk_reg {
+ MTK_REG_PDMA_GLO_CFG = 0,
+ MTK_REG_PDMA_RST_CFG,
+ MTK_REG_DLY_INT_CFG,
+ MTK_REG_TX_BASE_PTR0,
+ MTK_REG_TX_MAX_CNT0,
+ MTK_REG_TX_CTX_IDX0,
+ MTK_REG_TX_DTX_IDX0,
+ MTK_REG_RX_BASE_PTR0,
+ MTK_REG_RX_MAX_CNT0,
+ MTK_REG_RX_CALC_IDX0,
+ MTK_REG_RX_DRX_IDX0,
+ MTK_REG_MTK_INT_ENABLE,
+ MTK_REG_MTK_INT_STATUS,
+ MTK_REG_MTK_DMA_VID_BASE,
+ MTK_REG_MTK_COUNTER_BASE,
+ MTK_REG_MTK_RST_GL,
+ MTK_REG_MTK_INT_STATUS2,
+ MTK_REG_COUNT
+};
+
+/* delayed interrupt bits */
+#define MTK_DELAY_EN_INT 0x80
+#define MTK_DELAY_MAX_INT 0x04
+#define MTK_DELAY_MAX_TOUT 0x04
+#define MTK_DELAY_TIME 20
+#define MTK_DELAY_CHAN (((MTK_DELAY_EN_INT | MTK_DELAY_MAX_INT) << 8) \
+ | MTK_DELAY_MAX_TOUT)
+#define MTK_DELAY_INIT ((MTK_DELAY_CHAN << 16) | MTK_DELAY_CHAN)
+#define MTK_PSE_FQFC_CFG_INIT 0x80504000
+#define MTK_PSE_FQFC_CFG_256Q 0xff908000
+
+/* interrupt bits */
+#define MTK_CNT_PPE_AF BIT(31)
+#define MTK_CNT_GDM_AF BIT(29)
+#define MTK_PSE_P2_FC BIT(26)
+#define MTK_PSE_BUF_DROP BIT(24)
+#define MTK_GDM_OTHER_DROP BIT(23)
+#define MTK_PSE_P1_FC BIT(22)
+#define MTK_PSE_P0_FC BIT(21)
+#define MTK_PSE_FQ_EMPTY BIT(20)
+#define MTK_GE1_STA_CHG BIT(18)
+#define MTK_TX_COHERENT BIT(17)
+#define MTK_RX_COHERENT BIT(16)
+#define MTK_TX_DONE_INT3 BIT(11)
+#define MTK_TX_DONE_INT2 BIT(10)
+#define MTK_TX_DONE_INT1 BIT(9)
+#define MTK_TX_DONE_INT0 BIT(8)
+#define MTK_RX_DONE_INT0 BIT(2)
+#define MTK_TX_DLY_INT BIT(1)
+#define MTK_RX_DLY_INT BIT(0)
+
+#define MTK_RX_DONE_INT MTK_RX_DONE_INT0
+#define MTK_TX_DONE_INT (MTK_TX_DONE_INT0 | MTK_TX_DONE_INT1 | \
+ MTK_TX_DONE_INT2 | MTK_TX_DONE_INT3)
+
+#define RT5350_RX_DLY_INT BIT(30)
+#define RT5350_TX_DLY_INT BIT(28)
+#define RT5350_RX_DONE_INT1 BIT(17)
+#define RT5350_RX_DONE_INT0 BIT(16)
+#define RT5350_TX_DONE_INT3 BIT(3)
+#define RT5350_TX_DONE_INT2 BIT(2)
+#define RT5350_TX_DONE_INT1 BIT(1)
+#define RT5350_TX_DONE_INT0 BIT(0)
+
+#define RT5350_RX_DONE_INT (RT5350_RX_DONE_INT0 | RT5350_RX_DONE_INT1)
+#define RT5350_TX_DONE_INT (RT5350_TX_DONE_INT0 | RT5350_TX_DONE_INT1 | \
+ RT5350_TX_DONE_INT2 | RT5350_TX_DONE_INT3)
+
+/* registers */
+#define MTK_GDMA_OFFSET 0x0020
+#define MTK_PSE_OFFSET 0x0040
+#define MTK_GDMA2_OFFSET 0x0060
+#define MTK_CDMA_OFFSET 0x0080
+#define MTK_DMA_VID0 0x00a8
+#define MTK_PDMA_OFFSET 0x0100
+#define MTK_PPE_OFFSET 0x0200
+#define MTK_CMTABLE_OFFSET 0x0400
+#define MTK_POLICYTABLE_OFFSET 0x1000
+
+#define MT7621_GDMA_OFFSET 0x0500
+#define MT7620_GDMA_OFFSET 0x0600
+
+#define RT5350_PDMA_OFFSET 0x0800
+#define RT5350_SDM_OFFSET 0x0c00
+
+#define MTK_MDIO_ACCESS 0x00
+#define MTK_MDIO_CFG 0x04
+#define MTK_GLO_CFG 0x08
+#define MTK_RST_GL 0x0C
+#define MTK_INT_STATUS 0x10
+#define MTK_INT_ENABLE 0x14
+#define MTK_MDIO_CFG2 0x18
+#define MTK_FOC_TS_T 0x1C
+
+#define MTK_GDMA1_FWD_CFG (MTK_GDMA_OFFSET + 0x00)
+#define MTK_GDMA1_SCH_CFG (MTK_GDMA_OFFSET + 0x04)
+#define MTK_GDMA1_SHPR_CFG (MTK_GDMA_OFFSET + 0x08)
+#define MTK_GDMA1_MAC_ADRL (MTK_GDMA_OFFSET + 0x0C)
+#define MTK_GDMA1_MAC_ADRH (MTK_GDMA_OFFSET + 0x10)
+
+#define MTK_GDMA2_FWD_CFG (MTK_GDMA2_OFFSET + 0x00)
+#define MTK_GDMA2_SCH_CFG (MTK_GDMA2_OFFSET + 0x04)
+#define MTK_GDMA2_SHPR_CFG (MTK_GDMA2_OFFSET + 0x08)
+#define MTK_GDMA2_MAC_ADRL (MTK_GDMA2_OFFSET + 0x0C)
+#define MTK_GDMA2_MAC_ADRH (MTK_GDMA2_OFFSET + 0x10)
+
+#define MTK_PSE_FQ_CFG (MTK_PSE_OFFSET + 0x00)
+#define MTK_CDMA_FC_CFG (MTK_PSE_OFFSET + 0x04)
+#define MTK_GDMA1_FC_CFG (MTK_PSE_OFFSET + 0x08)
+#define MTK_GDMA2_FC_CFG (MTK_PSE_OFFSET + 0x0C)
+
+#define MTK_CDMA_CSG_CFG (MTK_CDMA_OFFSET + 0x00)
+#define MTK_CDMA_SCH_CFG (MTK_CDMA_OFFSET + 0x04)
+
+#define MT7621_GDMA_FWD_CFG(x) (MT7621_GDMA_OFFSET + (x * 0x1000))
+
+/* FIXME this might be different for different SOCs */
+#define MT7620_GDMA1_FWD_CFG (MT7621_GDMA_OFFSET + 0x00)
+
+#define RT5350_TX_BASE_PTR0 (RT5350_PDMA_OFFSET + 0x00)
+#define RT5350_TX_MAX_CNT0 (RT5350_PDMA_OFFSET + 0x04)
+#define RT5350_TX_CTX_IDX0 (RT5350_PDMA_OFFSET + 0x08)
+#define RT5350_TX_DTX_IDX0 (RT5350_PDMA_OFFSET + 0x0C)
+#define RT5350_TX_BASE_PTR1 (RT5350_PDMA_OFFSET + 0x10)
+#define RT5350_TX_MAX_CNT1 (RT5350_PDMA_OFFSET + 0x14)
+#define RT5350_TX_CTX_IDX1 (RT5350_PDMA_OFFSET + 0x18)
+#define RT5350_TX_DTX_IDX1 (RT5350_PDMA_OFFSET + 0x1C)
+#define RT5350_TX_BASE_PTR2 (RT5350_PDMA_OFFSET + 0x20)
+#define RT5350_TX_MAX_CNT2 (RT5350_PDMA_OFFSET + 0x24)
+#define RT5350_TX_CTX_IDX2 (RT5350_PDMA_OFFSET + 0x28)
+#define RT5350_TX_DTX_IDX2 (RT5350_PDMA_OFFSET + 0x2C)
+#define RT5350_TX_BASE_PTR3 (RT5350_PDMA_OFFSET + 0x30)
+#define RT5350_TX_MAX_CNT3 (RT5350_PDMA_OFFSET + 0x34)
+#define RT5350_TX_CTX_IDX3 (RT5350_PDMA_OFFSET + 0x38)
+#define RT5350_TX_DTX_IDX3 (RT5350_PDMA_OFFSET + 0x3C)
+#define RT5350_RX_BASE_PTR0 (RT5350_PDMA_OFFSET + 0x100)
+#define RT5350_RX_MAX_CNT0 (RT5350_PDMA_OFFSET + 0x104)
+#define RT5350_RX_CALC_IDX0 (RT5350_PDMA_OFFSET + 0x108)
+#define RT5350_RX_DRX_IDX0 (RT5350_PDMA_OFFSET + 0x10C)
+#define RT5350_RX_BASE_PTR1 (RT5350_PDMA_OFFSET + 0x110)
+#define RT5350_RX_MAX_CNT1 (RT5350_PDMA_OFFSET + 0x114)
+#define RT5350_RX_CALC_IDX1 (RT5350_PDMA_OFFSET + 0x118)
+#define RT5350_RX_DRX_IDX1 (RT5350_PDMA_OFFSET + 0x11C)
+#define RT5350_PDMA_GLO_CFG (RT5350_PDMA_OFFSET + 0x204)
+#define RT5350_PDMA_RST_CFG (RT5350_PDMA_OFFSET + 0x208)
+#define RT5350_DLY_INT_CFG (RT5350_PDMA_OFFSET + 0x20c)
+#define RT5350_MTK_INT_STATUS (RT5350_PDMA_OFFSET + 0x220)
+#define RT5350_MTK_INT_ENABLE (RT5350_PDMA_OFFSET + 0x228)
+#define RT5350_PDMA_SCH_CFG (RT5350_PDMA_OFFSET + 0x280)
+
+#define MTK_PDMA_GLO_CFG (MTK_PDMA_OFFSET + 0x00)
+#define MTK_PDMA_RST_CFG (MTK_PDMA_OFFSET + 0x04)
+#define MTK_PDMA_SCH_CFG (MTK_PDMA_OFFSET + 0x08)
+#define MTK_DLY_INT_CFG (MTK_PDMA_OFFSET + 0x0C)
+#define MTK_TX_BASE_PTR0 (MTK_PDMA_OFFSET + 0x10)
+#define MTK_TX_MAX_CNT0 (MTK_PDMA_OFFSET + 0x14)
+#define MTK_TX_CTX_IDX0 (MTK_PDMA_OFFSET + 0x18)
+#define MTK_TX_DTX_IDX0 (MTK_PDMA_OFFSET + 0x1C)
+#define MTK_TX_BASE_PTR1 (MTK_PDMA_OFFSET + 0x20)
+#define MTK_TX_MAX_CNT1 (MTK_PDMA_OFFSET + 0x24)
+#define MTK_TX_CTX_IDX1 (MTK_PDMA_OFFSET + 0x28)
+#define MTK_TX_DTX_IDX1 (MTK_PDMA_OFFSET + 0x2C)
+#define MTK_RX_BASE_PTR0 (MTK_PDMA_OFFSET + 0x30)
+#define MTK_RX_MAX_CNT0 (MTK_PDMA_OFFSET + 0x34)
+#define MTK_RX_CALC_IDX0 (MTK_PDMA_OFFSET + 0x38)
+#define MTK_RX_DRX_IDX0 (MTK_PDMA_OFFSET + 0x3C)
+#define MTK_TX_BASE_PTR2 (MTK_PDMA_OFFSET + 0x40)
+#define MTK_TX_MAX_CNT2 (MTK_PDMA_OFFSET + 0x44)
+#define MTK_TX_CTX_IDX2 (MTK_PDMA_OFFSET + 0x48)
+#define MTK_TX_DTX_IDX2 (MTK_PDMA_OFFSET + 0x4C)
+#define MTK_TX_BASE_PTR3 (MTK_PDMA_OFFSET + 0x50)
+#define MTK_TX_MAX_CNT3 (MTK_PDMA_OFFSET + 0x54)
+#define MTK_TX_CTX_IDX3 (MTK_PDMA_OFFSET + 0x58)
+#define MTK_TX_DTX_IDX3 (MTK_PDMA_OFFSET + 0x5C)
+#define MTK_RX_BASE_PTR1 (MTK_PDMA_OFFSET + 0x60)
+#define MTK_RX_MAX_CNT1 (MTK_PDMA_OFFSET + 0x64)
+#define MTK_RX_CALC_IDX1 (MTK_PDMA_OFFSET + 0x68)
+#define MTK_RX_DRX_IDX1 (MTK_PDMA_OFFSET + 0x6C)
+
+/* Switch DMA configuration */
+#define RT5350_SDM_CFG (RT5350_SDM_OFFSET + 0x00)
+#define RT5350_SDM_RRING (RT5350_SDM_OFFSET + 0x04)
+#define RT5350_SDM_TRING (RT5350_SDM_OFFSET + 0x08)
+#define RT5350_SDM_MAC_ADRL (RT5350_SDM_OFFSET + 0x0C)
+#define RT5350_SDM_MAC_ADRH (RT5350_SDM_OFFSET + 0x10)
+#define RT5350_SDM_TPCNT (RT5350_SDM_OFFSET + 0x100)
+#define RT5350_SDM_TBCNT (RT5350_SDM_OFFSET + 0x104)
+#define RT5350_SDM_RPCNT (RT5350_SDM_OFFSET + 0x108)
+#define RT5350_SDM_RBCNT (RT5350_SDM_OFFSET + 0x10C)
+#define RT5350_SDM_CS_ERR (RT5350_SDM_OFFSET + 0x110)
+
+#define RT5350_SDM_ICS_EN BIT(16)
+#define RT5350_SDM_TCS_EN BIT(17)
+#define RT5350_SDM_UCS_EN BIT(18)
+
+/* QDMA registers */
+#define MTK_QTX_CFG(x) (0x1800 + (x * 0x10))
+#define MTK_QTX_SCH(x) (0x1804 + (x * 0x10))
+#define MTK_QRX_BASE_PTR0 0x1900
+#define MTK_QRX_MAX_CNT0 0x1904
+#define MTK_QRX_CRX_IDX0 0x1908
+#define MTK_QRX_DRX_IDX0 0x190C
+#define MTK_QDMA_GLO_CFG 0x1A04
+#define MTK_QDMA_RST_IDX 0x1A08
+#define MTK_QDMA_DELAY_INT 0x1A0C
+#define MTK_QDMA_FC_THRES 0x1A10
+#define MTK_QMTK_INT_STATUS 0x1A18
+#define MTK_QMTK_INT_ENABLE 0x1A1C
+#define MTK_QDMA_HRED2 0x1A44
+
+#define MTK_QTX_CTX_PTR 0x1B00
+#define MTK_QTX_DTX_PTR 0x1B04
+
+#define MTK_QTX_CRX_PTR 0x1B10
+#define MTK_QTX_DRX_PTR 0x1B14
+
+#define MTK_QDMA_FQ_HEAD 0x1B20
+#define MTK_QDMA_FQ_TAIL 0x1B24
+#define MTK_QDMA_FQ_CNT 0x1B28
+#define MTK_QDMA_FQ_BLEN 0x1B2C
+
+#define QDMA_PAGE_SIZE 2048
+#define QDMA_TX_OWNER_CPU BIT(31)
+#define QDMA_TX_SWC BIT(14)
+#define TX_QDMA_SDL(_x) (((_x) & 0x3fff) << 16)
+#define QDMA_RES_THRES 4
+
+/* MDIO_CFG register bits */
+#define MTK_MDIO_CFG_AUTO_POLL_EN BIT(29)
+#define MTK_MDIO_CFG_GP1_BP_EN BIT(16)
+#define MTK_MDIO_CFG_GP1_FRC_EN BIT(15)
+#define MTK_MDIO_CFG_GP1_SPEED_10 (0 << 13)
+#define MTK_MDIO_CFG_GP1_SPEED_100 (1 << 13)
+#define MTK_MDIO_CFG_GP1_SPEED_1000 (2 << 13)
+#define MTK_MDIO_CFG_GP1_DUPLEX BIT(12)
+#define MTK_MDIO_CFG_GP1_FC_TX BIT(11)
+#define MTK_MDIO_CFG_GP1_FC_RX BIT(10)
+#define MTK_MDIO_CFG_GP1_LNK_DWN BIT(9)
+#define MTK_MDIO_CFG_GP1_AN_FAIL BIT(8)
+#define MTK_MDIO_CFG_MDC_CLK_DIV_1 (0 << 6)
+#define MTK_MDIO_CFG_MDC_CLK_DIV_2 (1 << 6)
+#define MTK_MDIO_CFG_MDC_CLK_DIV_4 (2 << 6)
+#define MTK_MDIO_CFG_MDC_CLK_DIV_8 (3 << 6)
+#define MTK_MDIO_CFG_TURBO_MII_FREQ BIT(5)
+#define MTK_MDIO_CFG_TURBO_MII_MODE BIT(4)
+#define MTK_MDIO_CFG_RX_CLK_SKEW_0 (0 << 2)
+#define MTK_MDIO_CFG_RX_CLK_SKEW_200 (1 << 2)
+#define MTK_MDIO_CFG_RX_CLK_SKEW_400 (2 << 2)
+#define MTK_MDIO_CFG_RX_CLK_SKEW_INV (3 << 2)
+#define MTK_MDIO_CFG_TX_CLK_SKEW_0 0
+#define MTK_MDIO_CFG_TX_CLK_SKEW_200 1
+#define MTK_MDIO_CFG_TX_CLK_SKEW_400 2
+#define MTK_MDIO_CFG_TX_CLK_SKEW_INV 3
+
+/* uni-cast port */
+#define MTK_GDM1_JMB_LEN_MASK 0xf
+#define MTK_GDM1_JMB_LEN_SHIFT 28
+#define MTK_GDM1_ICS_EN BIT(22)
+#define MTK_GDM1_TCS_EN BIT(21)
+#define MTK_GDM1_UCS_EN BIT(20)
+#define MTK_GDM1_JMB_EN BIT(19)
+#define MTK_GDM1_STRPCRC BIT(16)
+#define MTK_GDM1_UFRC_P_CPU (0 << 12)
+#define MTK_GDM1_UFRC_P_GDMA1 (1 << 12)
+#define MTK_GDM1_UFRC_P_PPE (6 << 12)
+
+/* checksums */
+#define MTK_ICS_GEN_EN BIT(2)
+#define MTK_UCS_GEN_EN BIT(1)
+#define MTK_TCS_GEN_EN BIT(0)
+
+/* dma mode */
+#define MTK_PDMA BIT(0)
+#define MTK_QDMA BIT(1)
+#define MTK_PDMA_RX_QDMA_TX (MTK_PDMA | MTK_QDMA)
+
+/* dma ring */
+#define MTK_PST_DRX_IDX0 BIT(16)
+#define MTK_PST_DTX_IDX3 BIT(3)
+#define MTK_PST_DTX_IDX2 BIT(2)
+#define MTK_PST_DTX_IDX1 BIT(1)
+#define MTK_PST_DTX_IDX0 BIT(0)
+
+#define MTK_RX_2B_OFFSET BIT(31)
+#define MTK_TX_WB_DDONE BIT(6)
+#define MTK_RX_DMA_BUSY BIT(3)
+#define MTK_TX_DMA_BUSY BIT(1)
+#define MTK_RX_DMA_EN BIT(2)
+#define MTK_TX_DMA_EN BIT(0)
+
+#define MTK_PDMA_SIZE_4DWORDS (0 << 4)
+#define MTK_PDMA_SIZE_8DWORDS (1 << 4)
+#define MTK_PDMA_SIZE_16DWORDS (2 << 4)
+
+#define MTK_US_CYC_CNT_MASK 0xff
+#define MTK_US_CYC_CNT_SHIFT 0x8
+#define MTK_US_CYC_CNT_DIVISOR 1000000
+
+/* PDMA descriptor rxd2 */
+#define RX_DMA_DONE BIT(31)
+#define RX_DMA_LSO BIT(30)
+#define RX_DMA_PLEN0(_x) (((_x) & 0x3fff) << 16)
+#define RX_DMA_GET_PLEN0(_x) (((_x) >> 16) & 0x3fff)
+#define RX_DMA_TAG BIT(15)
+
+/* PDMA descriptor rxd3 */
+#define RX_DMA_TPID(_x) (((_x) >> 16) & 0xffff)
+#define RX_DMA_VID(_x) ((_x) & 0xfff)
+
+/* PDMA descriptor rxd4 */
+#define RX_DMA_L4VALID BIT(30)
+#define RX_DMA_FPORT_SHIFT 19
+#define RX_DMA_FPORT_MASK 0x7
+
+struct mtk_rx_dma {
+ unsigned int rxd1;
+ unsigned int rxd2;
+ unsigned int rxd3;
+ unsigned int rxd4;
+} __packed __aligned(4);
+
+/* PDMA tx descriptor bits */
+#define TX_DMA_BUF_LEN 0x3fff
+#define TX_DMA_PLEN0_MASK (TX_DMA_BUF_LEN << 16)
+#define TX_DMA_PLEN0(_x) (((_x) & TX_DMA_BUF_LEN) << 16)
+#define TX_DMA_PLEN1(_x) ((_x) & TX_DMA_BUF_LEN)
+#define TX_DMA_GET_PLEN0(_x) (((_x) >> 16) & TX_DMA_BUF_LEN)
+#define TX_DMA_GET_PLEN1(_x) ((_x) & TX_DMA_BUF_LEN)
+#define TX_DMA_LS1 BIT(14)
+#define TX_DMA_LS0 BIT(30)
+#define TX_DMA_DONE BIT(31)
+#define TX_DMA_FPORT_SHIFT 25
+#define TX_DMA_FPORT_MASK 0x7
+#define TX_DMA_INS_VLAN_MT7621 BIT(16)
+#define TX_DMA_INS_VLAN BIT(7)
+#define TX_DMA_INS_PPPOE BIT(12)
+#define TX_DMA_TAG BIT(15)
+#define TX_DMA_TAG_MASK BIT(15)
+#define TX_DMA_QN(_x) ((_x) << 16)
+#define TX_DMA_PN(_x) ((_x) << 24)
+#define TX_DMA_QN_MASK TX_DMA_QN(0x7)
+#define TX_DMA_PN_MASK TX_DMA_PN(0x7)
+#define TX_DMA_UDF BIT(20)
+#define TX_DMA_CHKSUM (0x7 << 29)
+#define TX_DMA_TSO BIT(28)
+#define TX_DMA_DESP4_DEF (TX_DMA_QN(3) | TX_DMA_PN(1))
+
+/* frame engine counters */
+#define MTK_PPE_AC_BCNT0 (MTK_CMTABLE_OFFSET + 0x00)
+#define MTK_GDMA1_TX_GBCNT (MTK_CMTABLE_OFFSET + 0x300)
+#define MTK_GDMA2_TX_GBCNT (MTK_GDMA1_TX_GBCNT + 0x40)
+
+/* phy device flags */
+#define MTK_PHY_FLAG_PORT BIT(0)
+#define MTK_PHY_FLAG_ATTACH BIT(1)
+
+struct mtk_tx_dma {
+ unsigned int txd1;
+ unsigned int txd2;
+ unsigned int txd3;
+ unsigned int txd4;
+} __packed __aligned(4);
+
+struct mtk_eth;
+struct mtk_mac;
+
+/* manage the attached phys */
+struct mtk_phy {
+ spinlock_t lock;
+
+ struct phy_device *phy[8];
+ struct device_node *phy_node[8];
+ const __be32 *phy_fixed[8];
+ int duplex[8];
+ int speed[8];
+ int tx_fc[8];
+ int rx_fc[8];
+ int (*connect)(struct mtk_mac *mac);
+ void (*disconnect)(struct mtk_mac *mac);
+ void (*start)(struct mtk_mac *mac);
+ void (*stop)(struct mtk_mac *mac);
+};
+
+/* struct mtk_soc_data - the structure that holds the SoC specific data
+ * @reg_table: Some of the legacy registers changed their location
+ * over time. Their offsets are stored in this table
+ *
+ * @init_data: Some features depend on the silicon revision. This
+ * callback allows runtime modification of the content of
+ * this struct
+ * @reset_fe: This callback is used to trigger the reset of the frame
+ * engine
+ * @set_mac: This callback is used to set the unicast mac address
+ * filter
+ * @fwd_config: This callback is used to setup the forward config
+ * register of the MAC
+ * @switch_init: This callback is used to bring up the switch core
+ * @port_init: Some SoCs have ports that can be router to a switch port
+ * or an external PHY. This callback is used to setup these
+ * ports.
+ * @has_carrier: This callback allows driver to check if there is a cable
+ * attached.
+ * @mdio_init: This callbck is used to setup the MDIO bus if one is
+ * present
+ * @mdio_cleanup: This callback is used to cleanup the MDIO state.
+ * @mdio_write: This callback is used to write data to the MDIO bus.
+ * @mdio_read: This callback is used to write data to the MDIO bus.
+ * @mdio_adjust_link: This callback is used to apply the PHY settings.
+ * @piac_offset: the PIAC register has a different different base offset
+ * @hw_features: feature set depends on the SoC type
+ * @dma_ring_size: allow GBit SoCs to set bigger rings than FE SoCs
+ * @napi_weight: allow GBit SoCs to set bigger napi weight than FE SoCs
+ * @dma_type: SoCs is PDMA, QDMA or a mix of the 2
+ * @pdma_glo_cfg: the default DMA configuration
+ * @rx_int: the TX interrupt bits used by the SoC
+ * @tx_int: the TX interrupt bits used by the SoC
+ * @status_int: the Status interrupt bits used by the SoC
+ * @checksum_bit: the bits used to turn on HW checksumming
+ * @txd4: default value of the TXD4 descriptor
+ * @mac_count: the number of MACs that the SoC has
+ * @new_stats: there is a old and new way to read hardware stats
+ * registers
+ * @jumbo_frame: does the SoC support jumbo frames ?
+ * @rx_2b_offset: tell the rx dma to offset the data by 2 bytes
+ * @rx_sg_dma: scatter gather support
+ * @padding_64b enable 64 bit padding
+ * @padding_bug: rt2880 has a padding bug
+ * @has_switch: does the SoC have a built-in switch
+ *
+ * Although all of the supported SoCs share the same basic functionality, there
+ * are several SoC specific functions and features that we need to support. This
+ * struct holds the SoC specific data so that the common core can figure out
+ * how to setup and use these differences.
+ */
+struct mtk_soc_data {
+ const u16 *reg_table;
+
+ void (*init_data)(struct mtk_soc_data *data, struct net_device *netdev);
+ void (*reset_fe)(struct mtk_eth *eth);
+ void (*set_mac)(struct mtk_mac *mac, unsigned char *macaddr);
+ int (*fwd_config)(struct mtk_eth *eth);
+ int (*switch_init)(struct mtk_eth *eth);
+ void (*port_init)(struct mtk_eth *eth, struct mtk_mac *mac,
+ struct device_node *port);
+ int (*has_carrier)(struct mtk_eth *eth);
+ int (*mdio_init)(struct mtk_eth *eth);
+ void (*mdio_cleanup)(struct mtk_eth *eth);
+ int (*mdio_write)(struct mii_bus *bus, int phy_addr, int phy_reg,
+ u16 val);
+ int (*mdio_read)(struct mii_bus *bus, int phy_addr, int phy_reg);
+ void (*mdio_adjust_link)(struct mtk_eth *eth, int port);
+ u32 piac_offset;
+ netdev_features_t hw_features;
+ u32 dma_ring_size;
+ u32 napi_weight;
+ u32 dma_type;
+ u32 pdma_glo_cfg;
+ u32 rx_int;
+ u32 tx_int;
+ u32 status_int;
+ u32 checksum_bit;
+ u32 txd4;
+ u32 mac_count;
+
+ u32 new_stats:1;
+ u32 jumbo_frame:1;
+ u32 rx_2b_offset:1;
+ u32 rx_sg_dma:1;
+ u32 padding_64b:1;
+ u32 padding_bug:1;
+ u32 has_switch:1;
+};
+
+/* ugly macro hack to make sure hw_stats and ethtool strings are consistent */
+#define MTK_STAT_OFFSET 0x40
+#define MTK_STAT_REG_DECLARE \
+ _FE(tx_bytes) \
+ _FE(tx_packets) \
+ _FE(tx_skip) \
+ _FE(tx_collisions) \
+ _FE(rx_bytes) \
+ _FE(rx_packets) \
+ _FE(rx_overflow) \
+ _FE(rx_fcs_errors) \
+ _FE(rx_short_errors) \
+ _FE(rx_long_errors) \
+ _FE(rx_checksum_errors) \
+ _FE(rx_flow_control_packets)
+
+/* struct mtk_hw_stats - the structure that holds the traffic statistics.
+ * @stats_lock: make sure that stats operations are atomic
+ * @reg_offset: the status register offset of the SoC
+ * @syncp: the refcount
+ *
+ * All of the supported SoCs have hardware counters for traffic statstics.
+ * Whenever the status IRQ triggers we can read the latest stats from these
+ * counters and store them in this struct.
+ */
+struct mtk_hw_stats {
+ spinlock_t stats_lock;
+ u32 reg_offset;
+ struct u64_stats_sync syncp;
+
+#define _FE(x) u64 x;
+ MTK_STAT_REG_DECLARE
+#undef _FE
+};
+
+/* PDMA descriptor can point at 1-2 segments. This enum allows us to track how
+ * memory was allocated so that it can be freed properly
+ */
+enum mtk_tx_flags {
+ MTK_TX_FLAGS_SINGLE0 = 0x01,
+ MTK_TX_FLAGS_PAGE0 = 0x02,
+ MTK_TX_FLAGS_PAGE1 = 0x04,
+};
+
+/* struct mtk_tx_buf - This struct holds the pointers to the memory pointed at
+ * by the TX descriptor s
+ * @skb: The SKB pointer of the packet being sent
+ * @dma_addr0: The base addr of the first segment
+ * @dma_len0: The length of the first segment
+ * @dma_addr1: The base addr of the second segment
+ * @dma_len1: The length of the second segment
+ */
+struct mtk_tx_buf {
+ struct sk_buff *skb;
+ u32 flags;
+ DEFINE_DMA_UNMAP_ADDR(dma_addr0);
+ DEFINE_DMA_UNMAP_LEN(dma_len0);
+ DEFINE_DMA_UNMAP_ADDR(dma_addr1);
+ DEFINE_DMA_UNMAP_LEN(dma_len1);
+};
+
+/* struct mtk_tx_ring - This struct holds info describing a TX ring
+ * @tx_dma: The descriptor ring
+ * @tx_buf: The memory pointed at by the ring
+ * @tx_phys: The physical addr of tx_buf
+ * @tx_next_free: Pointer to the next free descriptor
+ * @tx_last_free: Pointer to the last free descriptor
+ * @tx_thresh: The threshold of minimum amount of free descriptors
+ * @tx_map: Callback to map a new packet into the ring
+ * @tx_poll: Callback for the housekeeping function
+ * @tx_clean: Callback for the cleanup function
+ * @tx_ring_size: How many descriptors are in the ring
+ * @tx_free_idx: The index of th next free descriptor
+ * @tx_next_idx: QDMA uses a linked list. This element points to the next
+ * free descriptor in the list
+ * @tx_free_count: QDMA uses a linked list. Track how many free descriptors
+ * are present
+ */
+struct mtk_tx_ring {
+ struct mtk_tx_dma *tx_dma;
+ struct mtk_tx_buf *tx_buf;
+ dma_addr_t tx_phys;
+ struct mtk_tx_dma *tx_next_free;
+ struct mtk_tx_dma *tx_last_free;
+ u16 tx_thresh;
+ int (*tx_map)(struct sk_buff *skb, struct net_device *dev, int tx_num,
+ struct mtk_tx_ring *ring, bool gso);
+ int (*tx_poll)(struct mtk_eth *eth, int budget, bool *tx_again);
+ void (*tx_clean)(struct mtk_eth *eth);
+
+ /* PDMA only */
+ u16 tx_ring_size;
+ u16 tx_free_idx;
+
+ /* QDMA only */
+ u16 tx_next_idx;
+ atomic_t tx_free_count;
+};
+
+/* struct mtk_rx_ring - This struct holds info describing a RX ring
+ * @rx_dma: The descriptor ring
+ * @rx_data: The memory pointed at by the ring
+ * @trx_phys: The physical addr of rx_buf
+ * @rx_ring_size: How many descriptors are in the ring
+ * @rx_buf_size: The size of each packet buffer
+ * @rx_calc_idx: The current head of ring
+ */
+struct mtk_rx_ring {
+ struct mtk_rx_dma *rx_dma;
+ u8 **rx_data;
+ dma_addr_t rx_phys;
+ u16 rx_ring_size;
+ u16 frag_size;
+ u16 rx_buf_size;
+ u16 rx_calc_idx;
+};
+
+/* currently no SoC has more than 2 macs */
+#define MTK_MAX_DEVS 2
+
+/* struct mtk_eth - This is the main datasructure for holding the state
+ * of the driver
+ * @dev: The device pointer
+ * @base: The mapped register i/o base
+ * @page_lock: Make sure that register operations are atomic
+ * @soc: pointer to our SoC specific data
+ * @dummy_dev: we run 2 netdevs on 1 physical DMA ring and need a
+ * dummy for NAPI to work
+ * @netdev: The netdev instances
+ * @mac: Each netdev is linked to a physical MAC
+ * @switch_np: The phandle for the switch
+ * @irq: The IRQ that we are using
+ * @msg_enable: Ethtool msg level
+ * @ysclk: The sysclk rate - neeed for calibration
+ * @ethsys: The register map pointing at the range used to setup
+ * MII modes
+ * @dma_refcnt: track how many netdevs are using the DMA engine
+ * @tx_ring: Pointer to the memore holding info about the TX ring
+ * @rx_ring: Pointer to the memore holding info about the RX ring
+ * @rx_napi: The NAPI struct
+ * @scratch_ring: Newer SoCs need memory for a second HW managed TX ring
+ * @scratch_head: The scratch memory that scratch_ring points to.
+ * @phy: Info about the attached PHYs
+ * @mii_bus: If there is a bus we need to create an instance for it
+ * @link: Track if the ports have a physical link
+ * @sw_priv: Pointer to the switches private data
+ * @vlan_map: RX VID tracking
+ */
+
+struct mtk_eth {
+ struct device *dev;
+ void __iomem *base;
+ spinlock_t page_lock;
+ struct mtk_soc_data *soc;
+ struct net_device dummy_dev;
+ struct net_device *netdev[MTK_MAX_DEVS];
+ struct mtk_mac *mac[MTK_MAX_DEVS];
+ struct device_node *switch_np;
+ int irq;
+ u32 msg_enable;
+ unsigned long sysclk;
+ struct regmap *ethsys;
+ atomic_t dma_refcnt;
+ struct mtk_tx_ring tx_ring;
+ struct mtk_rx_ring rx_ring[2];
+ struct napi_struct rx_napi;
+ struct mtk_tx_dma *scratch_ring;
+ void *scratch_head;
+ struct mtk_phy *phy;
+ struct mii_bus *mii_bus;
+ int link[8];
+ void *sw_priv;
+ unsigned long vlan_map;
+};
+
+/* struct mtk_mac - the structure that holds the info about the MACs of the
+ * SoC
+ * @id: The number of the MAC
+ * @of_node: Our devicetree node
+ * @hw: Backpointer to our main datastruture
+ * @hw_stats: Packet statistics counter
+ * @phy_dev: The attached PHY if available
+ * @phy_flags: The PHYs flags
+ * @pending_work: The workqueue used to reset the dma ring
+ */
+struct mtk_mac {
+ int id;
+ struct device_node *of_node;
+ struct mtk_eth *hw;
+ struct mtk_hw_stats *hw_stats;
+ struct phy_device *phy_dev;
+ u32 phy_flags;
+ struct work_struct pending_work;
+};
+
+/* the struct describing the SoC. these are declared in the soc_xyz.c files */
+extern const struct of_device_id of_mtk_match[];
+
+/* read the hardware status register */
+void mtk_stats_update_mac(struct mtk_mac *mac);
+
+/* default checksum setup handler */
+void mtk_reset(struct mtk_eth *eth, u32 reset_bits);
+
+/* register i/o wrappers */
+void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg);
+u32 mtk_r32(struct mtk_eth *eth, unsigned reg);
+
+/* default clock calibration handler */
+int mtk_set_clock_cycle(struct mtk_eth *eth);
+
+/* default checksum setup handler */
+void mtk_csum_config(struct mtk_eth *eth);
+
+/* default forward config handler */
+void mtk_fwd_config(struct mtk_eth *eth);
+
+#endif /* MTK_ETH_H */
diff --git a/drivers/staging/mt7621-eth/soc_mt7621.c b/drivers/staging/mt7621-eth/soc_mt7621.c
new file mode 100644
index 000000000000..743c0eed89b6
--- /dev/null
+++ b/drivers/staging/mt7621-eth/soc_mt7621.c
@@ -0,0 +1,160 @@
+/* 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 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/if_vlan.h>
+#include <linux/of_net.h>
+
+#include <asm/mach-ralink/ralink_regs.h>
+
+#include "mtk_eth_soc.h"
+#include "gsw_mt7620.h"
+#include "mdio.h"
+
+#define MT7620_CDMA_CSG_CFG 0x400
+#define MT7621_CDMP_IG_CTRL (MT7620_CDMA_CSG_CFG + 0x00)
+#define MT7621_CDMP_EG_CTRL (MT7620_CDMA_CSG_CFG + 0x04)
+#define MT7621_RESET_FE BIT(6)
+#define MT7621_L4_VALID BIT(24)
+
+#define MT7621_TX_DMA_UDF BIT(19)
+
+#define CDMA_ICS_EN BIT(2)
+#define CDMA_UCS_EN BIT(1)
+#define CDMA_TCS_EN BIT(0)
+
+#define GDMA_ICS_EN BIT(22)
+#define GDMA_TCS_EN BIT(21)
+#define GDMA_UCS_EN BIT(20)
+
+/* frame engine counters */
+#define MT7621_REG_MIB_OFFSET 0x2000
+#define MT7621_PPE_AC_BCNT0 (MT7621_REG_MIB_OFFSET + 0x00)
+#define MT7621_GDM1_TX_GBCNT (MT7621_REG_MIB_OFFSET + 0x400)
+#define MT7621_GDM2_TX_GBCNT (MT7621_GDM1_TX_GBCNT + 0x40)
+
+#define GSW_REG_GDMA1_MAC_ADRL 0x508
+#define GSW_REG_GDMA1_MAC_ADRH 0x50C
+#define GSW_REG_GDMA2_MAC_ADRL 0x1508
+#define GSW_REG_GDMA2_MAC_ADRH 0x150C
+
+
+#define MT7621_MTK_RST_GL 0x04
+#define MT7620_MTK_INT_STATUS2 0x08
+
+/* MTK_INT_STATUS reg on mt7620 define CNT_GDM1_AF at BIT(29)
+ * but after test it should be BIT(13).
+ */
+#define MT7621_MTK_GDM1_AF BIT(28)
+#define MT7621_MTK_GDM2_AF BIT(29)
+
+static const u16 mt7621_reg_table[MTK_REG_COUNT] = {
+ [MTK_REG_PDMA_GLO_CFG] = RT5350_PDMA_GLO_CFG,
+ [MTK_REG_PDMA_RST_CFG] = RT5350_PDMA_RST_CFG,
+ [MTK_REG_DLY_INT_CFG] = RT5350_DLY_INT_CFG,
+ [MTK_REG_TX_BASE_PTR0] = RT5350_TX_BASE_PTR0,
+ [MTK_REG_TX_MAX_CNT0] = RT5350_TX_MAX_CNT0,
+ [MTK_REG_TX_CTX_IDX0] = RT5350_TX_CTX_IDX0,
+ [MTK_REG_TX_DTX_IDX0] = RT5350_TX_DTX_IDX0,
+ [MTK_REG_RX_BASE_PTR0] = RT5350_RX_BASE_PTR0,
+ [MTK_REG_RX_MAX_CNT0] = RT5350_RX_MAX_CNT0,
+ [MTK_REG_RX_CALC_IDX0] = RT5350_RX_CALC_IDX0,
+ [MTK_REG_RX_DRX_IDX0] = RT5350_RX_DRX_IDX0,
+ [MTK_REG_MTK_INT_ENABLE] = RT5350_MTK_INT_ENABLE,
+ [MTK_REG_MTK_INT_STATUS] = RT5350_MTK_INT_STATUS,
+ [MTK_REG_MTK_DMA_VID_BASE] = 0,
+ [MTK_REG_MTK_COUNTER_BASE] = MT7621_GDM1_TX_GBCNT,
+ [MTK_REG_MTK_RST_GL] = MT7621_MTK_RST_GL,
+ [MTK_REG_MTK_INT_STATUS2] = MT7620_MTK_INT_STATUS2,
+};
+
+static void mt7621_mtk_reset(struct mtk_eth *eth)
+{
+ mtk_reset(eth, MT7621_RESET_FE);
+}
+
+static int mt7621_fwd_config(struct mtk_eth *eth)
+{
+ /* Setup GMAC1 only, there is no support for GMAC2 yet */
+ mtk_w32(eth, mtk_r32(eth, MT7620_GDMA1_FWD_CFG) & ~0xffff,
+ MT7620_GDMA1_FWD_CFG);
+
+ /* Enable RX checksum */
+ mtk_w32(eth, mtk_r32(eth, MT7620_GDMA1_FWD_CFG) | (GDMA_ICS_EN |
+ GDMA_TCS_EN | GDMA_UCS_EN),
+ MT7620_GDMA1_FWD_CFG);
+
+ /* Enable RX VLan Offloading */
+ mtk_w32(eth, 0, MT7621_CDMP_EG_CTRL);
+
+ return 0;
+}
+
+static void mt7621_set_mac(struct mtk_mac *mac, unsigned char *hwaddr)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mac->hw->page_lock, flags);
+ if (mac->id == 0) {
+ mtk_w32(mac->hw, (hwaddr[0] << 8) | hwaddr[1], GSW_REG_GDMA1_MAC_ADRH);
+ mtk_w32(mac->hw, (hwaddr[2] << 24) | (hwaddr[3] << 16) |
+ (hwaddr[4] << 8) | hwaddr[5],
+ GSW_REG_GDMA1_MAC_ADRL);
+ }
+ if (mac->id == 1) {
+ mtk_w32(mac->hw, (hwaddr[0] << 8) | hwaddr[1], GSW_REG_GDMA2_MAC_ADRH);
+ mtk_w32(mac->hw, (hwaddr[2] << 24) | (hwaddr[3] << 16) |
+ (hwaddr[4] << 8) | hwaddr[5],
+ GSW_REG_GDMA2_MAC_ADRL);
+ }
+ spin_unlock_irqrestore(&mac->hw->page_lock, flags);
+}
+
+static struct mtk_soc_data mt7621_data = {
+ .hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
+ NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX |
+ NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6 |
+ NETIF_F_IPV6_CSUM,
+ .dma_type = MTK_PDMA,
+ .dma_ring_size = 256,
+ .napi_weight = 64,
+ .new_stats = 1,
+ .padding_64b = 1,
+ .rx_2b_offset = 1,
+ .rx_sg_dma = 1,
+ .has_switch = 1,
+ .mac_count = 2,
+ .reset_fe = mt7621_mtk_reset,
+ .set_mac = mt7621_set_mac,
+ .fwd_config = mt7621_fwd_config,
+ .switch_init = mtk_gsw_init,
+ .reg_table = mt7621_reg_table,
+ .pdma_glo_cfg = MTK_PDMA_SIZE_16DWORDS,
+ .rx_int = RT5350_RX_DONE_INT,
+ .tx_int = RT5350_TX_DONE_INT,
+ .status_int = MT7621_MTK_GDM1_AF | MT7621_MTK_GDM2_AF,
+ .checksum_bit = MT7621_L4_VALID,
+ .has_carrier = mt7620_has_carrier,
+ .mdio_read = mt7620_mdio_read,
+ .mdio_write = mt7620_mdio_write,
+ .mdio_adjust_link = mt7620_mdio_link_adjust,
+};
+
+const struct of_device_id of_mtk_match[] = {
+ { .compatible = "mediatek,mt7621-eth", .data = &mt7621_data },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, of_mtk_match);
diff --git a/drivers/staging/mt7621-gpio/Kconfig b/drivers/staging/mt7621-gpio/Kconfig
new file mode 100644
index 000000000000..c741ec3f4e50
--- /dev/null
+++ b/drivers/staging/mt7621-gpio/Kconfig
@@ -0,0 +1,6 @@
+config GPIO_MT7621
+ bool "Mediatek GPIO Support"
+ depends on SOC_MT7620 || SOC_MT7621
+ select ARCH_REQUIRE_GPIOLIB
+ help
+ Say yes here to support the Mediatek SoC GPIO device
diff --git a/drivers/staging/mt7621-gpio/Makefile b/drivers/staging/mt7621-gpio/Makefile
new file mode 100644
index 000000000000..e269ab1b8717
--- /dev/null
+++ b/drivers/staging/mt7621-gpio/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_GPIO_MT7621) += gpio-mt7621.o
+
+ccflags-y += -I$(srctree)/$(src)/include
diff --git a/drivers/staging/mt7621-gpio/TODO b/drivers/staging/mt7621-gpio/TODO
new file mode 100644
index 000000000000..71439054e2e4
--- /dev/null
+++ b/drivers/staging/mt7621-gpio/TODO
@@ -0,0 +1,5 @@
+
+- general code review and clean up
+- ensure device-tree requirements are documented
+
+Cc: NeilBrown <neil@brown.name>
diff --git a/drivers/staging/mt7621-gpio/gpio-mt7621.c b/drivers/staging/mt7621-gpio/gpio-mt7621.c
new file mode 100644
index 000000000000..51235687ddb6
--- /dev/null
+++ b/drivers/staging/mt7621-gpio/gpio-mt7621.c
@@ -0,0 +1,352 @@
+/*
+ * 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.
+ *
+ * Copyright (C) 2009-2011 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ */
+
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/spinlock.h>
+#include <linux/irqdomain.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#define MTK_MAX_BANK 3
+#define MTK_BANK_WIDTH 32
+
+enum mediatek_gpio_reg {
+ GPIO_REG_CTRL = 0,
+ GPIO_REG_POL,
+ GPIO_REG_DATA,
+ GPIO_REG_DSET,
+ GPIO_REG_DCLR,
+ GPIO_REG_REDGE,
+ GPIO_REG_FEDGE,
+ GPIO_REG_HLVL,
+ GPIO_REG_LLVL,
+ GPIO_REG_STAT,
+ GPIO_REG_EDGE,
+};
+
+static void __iomem *mediatek_gpio_membase;
+static int mediatek_gpio_irq;
+static struct irq_domain *mediatek_gpio_irq_domain;
+
+static struct mtk_gc {
+ struct gpio_chip chip;
+ spinlock_t lock;
+ int bank;
+ u32 rising;
+ u32 falling;
+} *gc_map[MTK_MAX_BANK];
+
+static inline struct mtk_gc
+*to_mediatek_gpio(struct gpio_chip *chip)
+{
+ struct mtk_gc *mgc;
+
+ mgc = container_of(chip, struct mtk_gc, chip);
+
+ return mgc;
+}
+
+static inline void
+mtk_gpio_w32(struct mtk_gc *rg, u8 reg, u32 val)
+{
+ iowrite32(val, mediatek_gpio_membase + (reg * 0x10) + (rg->bank * 0x4));
+}
+
+static inline u32
+mtk_gpio_r32(struct mtk_gc *rg, u8 reg)
+{
+ return ioread32(mediatek_gpio_membase + (reg * 0x10) + (rg->bank * 0x4));
+}
+
+static void
+mediatek_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct mtk_gc *rg = to_mediatek_gpio(chip);
+
+ mtk_gpio_w32(rg, (value) ? GPIO_REG_DSET : GPIO_REG_DCLR, BIT(offset));
+}
+
+static int
+mediatek_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct mtk_gc *rg = to_mediatek_gpio(chip);
+
+ return !!(mtk_gpio_r32(rg, GPIO_REG_DATA) & BIT(offset));
+}
+
+static int
+mediatek_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct mtk_gc *rg = to_mediatek_gpio(chip);
+ unsigned long flags;
+ u32 t;
+
+ spin_lock_irqsave(&rg->lock, flags);
+ t = mtk_gpio_r32(rg, GPIO_REG_CTRL);
+ t &= ~BIT(offset);
+ mtk_gpio_w32(rg, GPIO_REG_CTRL, t);
+ spin_unlock_irqrestore(&rg->lock, flags);
+
+ return 0;
+}
+
+static int
+mediatek_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct mtk_gc *rg = to_mediatek_gpio(chip);
+ unsigned long flags;
+ u32 t;
+
+ spin_lock_irqsave(&rg->lock, flags);
+ t = mtk_gpio_r32(rg, GPIO_REG_CTRL);
+ t |= BIT(offset);
+ mtk_gpio_w32(rg, GPIO_REG_CTRL, t);
+ mediatek_gpio_set(chip, offset, value);
+ spin_unlock_irqrestore(&rg->lock, flags);
+
+ return 0;
+}
+
+static int
+mediatek_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
+{
+ struct mtk_gc *rg = to_mediatek_gpio(chip);
+ unsigned long flags;
+ u32 t;
+
+ spin_lock_irqsave(&rg->lock, flags);
+ t = mtk_gpio_r32(rg, GPIO_REG_CTRL);
+ spin_unlock_irqrestore(&rg->lock, flags);
+
+ if (t & BIT(offset))
+ return 0;
+
+ return 1;
+}
+
+static int
+mediatek_gpio_to_irq(struct gpio_chip *chip, unsigned pin)
+{
+ struct mtk_gc *rg = to_mediatek_gpio(chip);
+
+ return irq_create_mapping(mediatek_gpio_irq_domain, pin + (rg->bank * MTK_BANK_WIDTH));
+}
+
+static int
+mediatek_gpio_bank_probe(struct platform_device *pdev, struct device_node *bank)
+{
+ const __be32 *id = of_get_property(bank, "reg", NULL);
+ struct mtk_gc *rg = devm_kzalloc(&pdev->dev,
+ sizeof(struct mtk_gc), GFP_KERNEL);
+
+ if (!rg || !id || be32_to_cpu(*id) > MTK_MAX_BANK)
+ return -ENOMEM;
+
+ gc_map[be32_to_cpu(*id)] = rg;
+
+ memset(rg, 0, sizeof(struct mtk_gc));
+
+ spin_lock_init(&rg->lock);
+
+ rg->chip.parent = &pdev->dev;
+ rg->chip.label = dev_name(&pdev->dev);
+ rg->chip.of_node = bank;
+ rg->chip.base = MTK_BANK_WIDTH * be32_to_cpu(*id);
+ rg->chip.ngpio = MTK_BANK_WIDTH;
+ rg->chip.direction_input = mediatek_gpio_direction_input;
+ rg->chip.direction_output = mediatek_gpio_direction_output;
+ rg->chip.get_direction = mediatek_gpio_get_direction;
+ rg->chip.get = mediatek_gpio_get;
+ rg->chip.set = mediatek_gpio_set;
+ if (mediatek_gpio_irq_domain)
+ rg->chip.to_irq = mediatek_gpio_to_irq;
+ rg->bank = be32_to_cpu(*id);
+
+ /* set polarity to low for all gpios */
+ mtk_gpio_w32(rg, GPIO_REG_POL, 0);
+
+ dev_info(&pdev->dev, "registering %d gpios\n", rg->chip.ngpio);
+
+ return gpiochip_add(&rg->chip);
+}
+
+static void
+mediatek_gpio_irq_handler(struct irq_desc *desc)
+{
+ int i;
+
+ for (i = 0; i < MTK_MAX_BANK; i++) {
+ struct mtk_gc *rg = gc_map[i];
+ unsigned long pending;
+ int bit;
+
+ if (!rg)
+ continue;
+
+ pending = mtk_gpio_r32(rg, GPIO_REG_STAT);
+
+ for_each_set_bit(bit, &pending, MTK_BANK_WIDTH) {
+ u32 map = irq_find_mapping(mediatek_gpio_irq_domain, (MTK_BANK_WIDTH * i) + bit);
+
+ generic_handle_irq(map);
+ mtk_gpio_w32(rg, GPIO_REG_STAT, BIT(bit));
+ }
+ }
+}
+
+static void
+mediatek_gpio_irq_unmask(struct irq_data *d)
+{
+ int pin = d->hwirq;
+ int bank = pin / 32;
+ struct mtk_gc *rg = gc_map[bank];
+ unsigned long flags;
+ u32 rise, fall;
+
+ if (!rg)
+ return;
+
+ rise = mtk_gpio_r32(rg, GPIO_REG_REDGE);
+ fall = mtk_gpio_r32(rg, GPIO_REG_FEDGE);
+
+ spin_lock_irqsave(&rg->lock, flags);
+ mtk_gpio_w32(rg, GPIO_REG_REDGE, rise | (BIT(d->hwirq) & rg->rising));
+ mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall | (BIT(d->hwirq) & rg->falling));
+ spin_unlock_irqrestore(&rg->lock, flags);
+}
+
+static void
+mediatek_gpio_irq_mask(struct irq_data *d)
+{
+ int pin = d->hwirq;
+ int bank = pin / 32;
+ struct mtk_gc *rg = gc_map[bank];
+ unsigned long flags;
+ u32 rise, fall;
+
+ if (!rg)
+ return;
+
+ rise = mtk_gpio_r32(rg, GPIO_REG_REDGE);
+ fall = mtk_gpio_r32(rg, GPIO_REG_FEDGE);
+
+ spin_lock_irqsave(&rg->lock, flags);
+ mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall & ~BIT(d->hwirq));
+ mtk_gpio_w32(rg, GPIO_REG_REDGE, rise & ~BIT(d->hwirq));
+ spin_unlock_irqrestore(&rg->lock, flags);
+}
+
+static int
+mediatek_gpio_irq_type(struct irq_data *d, unsigned int type)
+{
+ int pin = d->hwirq;
+ int bank = pin / 32;
+ struct mtk_gc *rg = gc_map[bank];
+ u32 mask = BIT(d->hwirq);
+
+ if (!rg)
+ return -1;
+
+ if (type == IRQ_TYPE_PROBE) {
+ if ((rg->rising | rg->falling) & mask)
+ return 0;
+
+ type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
+ }
+
+ if (type & IRQ_TYPE_EDGE_RISING)
+ rg->rising |= mask;
+ else
+ rg->rising &= ~mask;
+
+ if (type & IRQ_TYPE_EDGE_FALLING)
+ rg->falling |= mask;
+ else
+ rg->falling &= ~mask;
+
+ return 0;
+}
+
+static struct irq_chip mediatek_gpio_irq_chip = {
+ .name = "GPIO",
+ .irq_unmask = mediatek_gpio_irq_unmask,
+ .irq_mask = mediatek_gpio_irq_mask,
+ .irq_mask_ack = mediatek_gpio_irq_mask,
+ .irq_set_type = mediatek_gpio_irq_type,
+};
+
+static int
+mediatek_gpio_gpio_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
+{
+ irq_set_chip_and_handler(irq, &mediatek_gpio_irq_chip, handle_level_irq);
+ irq_set_handler_data(irq, d);
+
+ return 0;
+}
+
+static const struct irq_domain_ops irq_domain_ops = {
+ .xlate = irq_domain_xlate_onecell,
+ .map = mediatek_gpio_gpio_map,
+};
+
+static int
+mediatek_gpio_probe(struct platform_device *pdev)
+{
+ struct device_node *bank, *np = pdev->dev.of_node;
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ mediatek_gpio_membase = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(mediatek_gpio_membase))
+ return PTR_ERR(mediatek_gpio_membase);
+
+ mediatek_gpio_irq = irq_of_parse_and_map(np, 0);
+ if (mediatek_gpio_irq) {
+ mediatek_gpio_irq_domain = irq_domain_add_linear(np,
+ MTK_MAX_BANK * MTK_BANK_WIDTH,
+ &irq_domain_ops, NULL);
+ if (!mediatek_gpio_irq_domain)
+ dev_err(&pdev->dev, "irq_domain_add_linear failed\n");
+ }
+
+ for_each_child_of_node(np, bank)
+ if (of_device_is_compatible(bank, "mtk,mt7621-gpio-bank"))
+ mediatek_gpio_bank_probe(pdev, bank);
+
+ if (mediatek_gpio_irq_domain)
+ irq_set_chained_handler(mediatek_gpio_irq, mediatek_gpio_irq_handler);
+
+ return 0;
+}
+
+static const struct of_device_id mediatek_gpio_match[] = {
+ { .compatible = "mtk,mt7621-gpio" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mediatek_gpio_match);
+
+static struct platform_driver mediatek_gpio_driver = {
+ .probe = mediatek_gpio_probe,
+ .driver = {
+ .name = "mt7621_gpio",
+ .of_match_table = mediatek_gpio_match,
+ },
+};
+
+static int __init
+mediatek_gpio_init(void)
+{
+ return platform_driver_register(&mediatek_gpio_driver);
+}
+
+subsys_initcall(mediatek_gpio_init);
diff --git a/drivers/staging/mt7621-mmc/Kconfig b/drivers/staging/mt7621-mmc/Kconfig
new file mode 100644
index 000000000000..c6dfe8c637dc
--- /dev/null
+++ b/drivers/staging/mt7621-mmc/Kconfig
@@ -0,0 +1,16 @@
+config MTK_MMC
+ tristate "MTK SD/MMC"
+ depends on !MTD_NAND_RALINK && MMC
+
+config MTK_AEE_KDUMP
+ bool "MTK AEE KDUMP"
+ depends on MTK_MMC
+
+config MTK_MMC_CD_POLL
+ bool "Card Detect with Polling"
+ depends on MTK_MMC
+
+config MTK_MMC_EMMC_8BIT
+ bool "eMMC 8-bit support"
+ depends on MTK_MMC && RALINK_MT7628
+
diff --git a/drivers/staging/mt7621-mmc/Makefile b/drivers/staging/mt7621-mmc/Makefile
new file mode 100644
index 000000000000..caead0b54703
--- /dev/null
+++ b/drivers/staging/mt7621-mmc/Makefile
@@ -0,0 +1,42 @@
+# Copyright Statement:
+#
+# This software/firmware and related documentation ("MediaTek Software") are
+# protected under relevant copyright laws. The information contained herein
+# is confidential and proprietary to MediaTek Inc. and/or its licensors.
+# Without the prior written permission of MediaTek inc. and/or its licensors,
+# any reproduction, modification, use or disclosure of MediaTek Software,
+# and information contained herein, in whole or in part, shall be strictly prohibited.
+#
+# MediaTek Inc. (C) 2010. All rights reserved.
+#
+# BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+# THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+# RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
+# AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+# NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
+# SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
+# SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
+# THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
+# THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
+# CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
+# SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+# STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
+# CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
+# AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
+# OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
+# MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+#
+# The following software/firmware and/or related documentation ("MediaTek Software")
+# have been modified by MediaTek Inc. All revisions are subject to any receiver's
+# applicable license agreements with MediaTek Inc.
+
+obj-$(CONFIG_MTK_MMC) += mtk_sd.o
+mtk_sd-objs := sd.o dbg.o
+ifeq ($(CONFIG_MTK_AEE_KDUMP),y)
+EXTRA_CFLAGS += -DMT6575_SD_DEBUG
+endif
+
+clean:
+ @rm -f *.o modules.order .*.cmd
diff --git a/drivers/staging/mt7621-mmc/TODO b/drivers/staging/mt7621-mmc/TODO
new file mode 100644
index 000000000000..febb32d37e07
--- /dev/null
+++ b/drivers/staging/mt7621-mmc/TODO
@@ -0,0 +1,8 @@
+
+- general code review and clean up
+- ensure device-tree requirements are documented
+- should probably be merged with drivers/mmc/host/mtk-sd.c
+- possibly fix to work with highmem pages so a bounce buffer isn't
+ needed.
+
+Cc: NeilBrown <neil@brown.name>
diff --git a/drivers/staging/mt7621-mmc/board.h b/drivers/staging/mt7621-mmc/board.h
new file mode 100644
index 000000000000..33bfc7b9597a
--- /dev/null
+++ b/drivers/staging/mt7621-mmc/board.h
@@ -0,0 +1,137 @@
+/* Copyright Statement:
+ *
+ * This software/firmware and related documentation ("MediaTek Software") are
+ * protected under relevant copyright laws. The information contained herein
+ * is confidential and proprietary to MediaTek Inc. and/or its licensors.
+ * Without the prior written permission of MediaTek inc. and/or its licensors,
+ * any reproduction, modification, use or disclosure of MediaTek Software,
+ * and information contained herein, in whole or in part, shall be strictly prohibited.
+ */
+/* MediaTek Inc. (C) 2010. All rights reserved.
+ *
+ * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+ * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+ * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
+ * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+ * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
+ * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
+ * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
+ * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
+ * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
+ * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
+ * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+ * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
+ * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
+ * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
+ * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
+ * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+ *
+ * The following software/firmware and/or related documentation ("MediaTek Software")
+ * have been modified by MediaTek Inc. All revisions are subject to any receiver's
+ * applicable license agreements with MediaTek Inc.
+ */
+
+#ifndef __ARCH_ARM_MACH_BOARD_H
+#define __ARCH_ARM_MACH_BOARD_H
+
+#include <generated/autoconf.h>
+#include <linux/pm.h>
+/* --- chhung */
+// #include <mach/mt6575.h>
+// #include <board-custom.h>
+/* end of chhung */
+
+typedef void (*sdio_irq_handler_t)(void*); /* external irq handler */
+typedef void (*pm_callback_t)(pm_message_t state, void *data);
+
+#define MSDC_CD_PIN_EN (1 << 0) /* card detection pin is wired */
+#define MSDC_WP_PIN_EN (1 << 1) /* write protection pin is wired */
+#define MSDC_RST_PIN_EN (1 << 2) /* emmc reset pin is wired */
+#define MSDC_SDIO_IRQ (1 << 3) /* use internal sdio irq (bus) */
+#define MSDC_EXT_SDIO_IRQ (1 << 4) /* use external sdio irq */
+#define MSDC_REMOVABLE (1 << 5) /* removable slot */
+#define MSDC_SYS_SUSPEND (1 << 6) /* suspended by system */
+#define MSDC_HIGHSPEED (1 << 7) /* high-speed mode support */
+#define MSDC_UHS1 (1 << 8) /* uhs-1 mode support */
+#define MSDC_DDR (1 << 9) /* ddr mode support */
+
+
+#define MSDC_SMPL_RISING (0)
+#define MSDC_SMPL_FALLING (1)
+
+#define MSDC_CMD_PIN (0)
+#define MSDC_DAT_PIN (1)
+#define MSDC_CD_PIN (2)
+#define MSDC_WP_PIN (3)
+#define MSDC_RST_PIN (4)
+
+enum {
+ MSDC_CLKSRC_48MHZ = 0,
+// MSDC_CLKSRC_26MHZ = 0,
+// MSDC_CLKSRC_197MHZ = 1,
+// MSDC_CLKSRC_208MHZ = 2
+};
+
+struct msdc_hw {
+ unsigned char clk_src; /* host clock source */
+ unsigned char cmd_edge; /* command latch edge */
+ unsigned char data_edge; /* data latch edge */
+ unsigned char clk_drv; /* clock pad driving */
+ unsigned char cmd_drv; /* command pad driving */
+ unsigned char dat_drv; /* data pad driving */
+ unsigned long flags; /* hardware capability flags */
+ unsigned long data_pins; /* data pins */
+ unsigned long data_offset; /* data address offset */
+
+ /* config gpio pull mode */
+ void (*config_gpio_pin)(int type, int pull);
+
+ /* external power control for card */
+ void (*ext_power_on)(void);
+ void (*ext_power_off)(void);
+
+ /* external sdio irq operations */
+ void (*request_sdio_eirq)(sdio_irq_handler_t sdio_irq_handler, void *data);
+ void (*enable_sdio_eirq)(void);
+ void (*disable_sdio_eirq)(void);
+
+ /* external cd irq operations */
+ void (*request_cd_eirq)(sdio_irq_handler_t cd_irq_handler, void *data);
+ void (*enable_cd_eirq)(void);
+ void (*disable_cd_eirq)(void);
+ int (*get_cd_status)(void);
+
+ /* power management callback for external module */
+ void (*register_pm)(pm_callback_t pm_cb, void *data);
+};
+
+extern struct msdc_hw msdc0_hw;
+extern struct msdc_hw msdc1_hw;
+extern struct msdc_hw msdc2_hw;
+extern struct msdc_hw msdc3_hw;
+
+/*GPS driver*/
+#define GPS_FLAG_FORCE_OFF 0x0001
+struct mt3326_gps_hardware {
+ int (*ext_power_on)(int);
+ int (*ext_power_off)(int);
+};
+extern struct mt3326_gps_hardware mt3326_gps_hw;
+
+/* NAND driver */
+struct mt6575_nand_host_hw {
+ unsigned int nfi_bus_width; /* NFI_BUS_WIDTH */
+ unsigned int nfi_access_timing; /* NFI_ACCESS_TIMING */
+ unsigned int nfi_cs_num; /* NFI_CS_NUM */
+ unsigned int nand_sec_size; /* NAND_SECTOR_SIZE */
+ unsigned int nand_sec_shift; /* NAND_SECTOR_SHIFT */
+ unsigned int nand_ecc_size;
+ unsigned int nand_ecc_bytes;
+ unsigned int nand_ecc_mode;
+};
+extern struct mt6575_nand_host_hw mt6575_nand_hw;
+
+#endif /* __ARCH_ARM_MACH_BOARD_H */
+
diff --git a/drivers/staging/mt7621-mmc/dbg.c b/drivers/staging/mt7621-mmc/dbg.c
new file mode 100644
index 000000000000..4dc115b53993
--- /dev/null
+++ b/drivers/staging/mt7621-mmc/dbg.c
@@ -0,0 +1,347 @@
+/* Copyright Statement:
+ *
+ * This software/firmware and related documentation ("MediaTek Software") are
+ * protected under relevant copyright laws. The information contained herein
+ * is confidential and proprietary to MediaTek Inc. and/or its licensors.
+ * Without the prior written permission of MediaTek inc. and/or its licensors,
+ * any reproduction, modification, use or disclosure of MediaTek Software,
+ * and information contained herein, in whole or in part, shall be strictly prohibited.
+ *
+ * MediaTek Inc. (C) 2010. All rights reserved.
+ *
+ * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+ * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+ * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
+ * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+ * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
+ * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
+ * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
+ * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
+ * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
+ * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
+ * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+ * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
+ * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
+ * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
+ * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
+ * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+ *
+ * The following software/firmware and/or related documentation ("MediaTek Software")
+ * have been modified by MediaTek Inc. All revisions are subject to any receiver's
+ * applicable license agreements with MediaTek Inc.
+ */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+// #include <mach/mt6575_gpt.h> /* --- by chhung */
+#include "dbg.h"
+#include "mt6575_sd.h"
+#include <linux/seq_file.h>
+
+static char cmd_buf[256];
+
+/* for debug zone */
+unsigned int sd_debug_zone[4]={
+ 0,
+ 0,
+ 0,
+ 0
+};
+
+/* mode select */
+u32 dma_size[4]={
+ 512,
+ 512,
+ 512,
+ 512
+};
+msdc_mode drv_mode[4]={
+ MODE_SIZE_DEP, /* using DMA or not depend on the size */
+ MODE_SIZE_DEP,
+ MODE_SIZE_DEP,
+ MODE_SIZE_DEP
+};
+
+#if defined (MT6575_SD_DEBUG)
+/* for driver profile */
+#define TICKS_ONE_MS (13000)
+u32 gpt_enable = 0;
+u32 sdio_pro_enable = 0; /* make sure gpt is enabled */
+u32 sdio_pro_time = 0; /* no more than 30s */
+struct sdio_profile sdio_perfomance = {0};
+
+#if 0 /* --- chhung */
+void msdc_init_gpt(void)
+{
+ GPT_CONFIG config;
+
+ config.num = GPT6;
+ config.mode = GPT_FREE_RUN;
+ config.clkSrc = GPT_CLK_SRC_SYS;
+ config.clkDiv = GPT_CLK_DIV_1; /* 13MHz GPT6 */
+
+ if (GPT_Config(config) == FALSE )
+ return;
+
+ GPT_Start(GPT6);
+}
+#endif /* end of --- */
+
+u32 msdc_time_calc(u32 old_L32, u32 old_H32, u32 new_L32, u32 new_H32)
+{
+ u32 ret = 0;
+
+ if (new_H32 == old_H32) {
+ ret = new_L32 - old_L32;
+ } else if(new_H32 == (old_H32 + 1)) {
+ if (new_L32 > old_L32) {
+ printk("msdc old_L<0x%x> new_L<0x%x>\n", old_L32, new_L32);
+ }
+ ret = (0xffffffff - old_L32);
+ ret += new_L32;
+ } else {
+ printk("msdc old_H<0x%x> new_H<0x%x>\n", old_H32, new_H32);
+ }
+
+ return ret;
+}
+
+void msdc_sdio_profile(struct sdio_profile* result)
+{
+ struct cmd_profile* cmd;
+ u32 i;
+
+ printk("sdio === performance dump ===\n");
+ printk("sdio === total execute tick<%d> time<%dms> Tx<%dB> Rx<%dB>\n",
+ result->total_tc, result->total_tc / TICKS_ONE_MS,
+ result->total_tx_bytes, result->total_rx_bytes);
+
+ /* CMD52 Dump */
+ cmd = &result->cmd52_rx;
+ printk("sdio === CMD52 Rx <%d>times tick<%d> Max<%d> Min<%d> Aver<%d>\n", cmd->count, cmd->tot_tc,
+ cmd->max_tc, cmd->min_tc, cmd->tot_tc/cmd->count);
+ cmd = &result->cmd52_tx;
+ printk("sdio === CMD52 Tx <%d>times tick<%d> Max<%d> Min<%d> Aver<%d>\n", cmd->count, cmd->tot_tc,
+ cmd->max_tc, cmd->min_tc, cmd->tot_tc/cmd->count);
+
+ /* CMD53 Rx bytes + block mode */
+ for (i=0; i<512; i++) {
+ cmd = &result->cmd53_rx_byte[i];
+ if (cmd->count) {
+ printk("sdio<%6d><%3dB>_Rx_<%9d><%9d><%6d><%6d>_<%9dB><%2dM>\n", cmd->count, i, cmd->tot_tc,
+ cmd->max_tc, cmd->min_tc, cmd->tot_tc/cmd->count,
+ cmd->tot_bytes, (cmd->tot_bytes/10)*13 / (cmd->tot_tc/10));
+ }
+ }
+ for (i=0; i<100; i++) {
+ cmd = &result->cmd53_rx_blk[i];
+ if (cmd->count) {
+ printk("sdio<%6d><%3d>B_Rx_<%9d><%9d><%6d><%6d>_<%9dB><%2dM>\n", cmd->count, i, cmd->tot_tc,
+ cmd->max_tc, cmd->min_tc, cmd->tot_tc/cmd->count,
+ cmd->tot_bytes, (cmd->tot_bytes/10)*13 / (cmd->tot_tc/10));
+ }
+ }
+
+ /* CMD53 Tx bytes + block mode */
+ for (i=0; i<512; i++) {
+ cmd = &result->cmd53_tx_byte[i];
+ if (cmd->count) {
+ printk("sdio<%6d><%3dB>_Tx_<%9d><%9d><%6d><%6d>_<%9dB><%2dM>\n", cmd->count, i, cmd->tot_tc,
+ cmd->max_tc, cmd->min_tc, cmd->tot_tc/cmd->count,
+ cmd->tot_bytes, (cmd->tot_bytes/10)*13 / (cmd->tot_tc/10));
+ }
+ }
+ for (i=0; i<100; i++) {
+ cmd = &result->cmd53_tx_blk[i];
+ if (cmd->count) {
+ printk("sdio<%6d><%3d>B_Tx_<%9d><%9d><%6d><%6d>_<%9dB><%2dM>\n", cmd->count, i, cmd->tot_tc,
+ cmd->max_tc, cmd->min_tc, cmd->tot_tc/cmd->count,
+ cmd->tot_bytes, (cmd->tot_bytes/10)*13 / (cmd->tot_tc/10));
+ }
+ }
+
+ printk("sdio === performance dump done ===\n");
+}
+
+//========= sdio command table ===========
+void msdc_performance(u32 opcode, u32 sizes, u32 bRx, u32 ticks)
+{
+ struct sdio_profile* result = &sdio_perfomance;
+ struct cmd_profile* cmd;
+ u32 block;
+
+ if (sdio_pro_enable == 0) {
+ return;
+ }
+
+ if (opcode == 52) {
+ cmd = bRx ? &result->cmd52_rx : &result->cmd52_tx;
+ } else if (opcode == 53) {
+ if (sizes < 512) {
+ cmd = bRx ? &result->cmd53_rx_byte[sizes] : &result->cmd53_tx_byte[sizes];
+ } else {
+ block = sizes / 512;
+ if (block >= 99) {
+ printk("cmd53 error blocks\n");
+ while(1);
+ }
+ cmd = bRx ? &result->cmd53_rx_blk[block] : &result->cmd53_tx_blk[block];
+ }
+ } else {
+ return;
+ }
+
+ /* update the members */
+ if (ticks > cmd->max_tc){
+ cmd->max_tc = ticks;
+ }
+ if (cmd->min_tc == 0 || ticks < cmd->min_tc) {
+ cmd->min_tc = ticks;
+ }
+ cmd->tot_tc += ticks;
+ cmd->tot_bytes += sizes;
+ cmd->count ++;
+
+ if (bRx) {
+ result->total_rx_bytes += sizes;
+ } else {
+ result->total_tx_bytes += sizes;
+ }
+ result->total_tc += ticks;
+
+ /* dump when total_tc > 30s */
+ if (result->total_tc >= sdio_pro_time * TICKS_ONE_MS * 1000) {
+ msdc_sdio_profile(result);
+ memset(result, 0 , sizeof(struct sdio_profile));
+ }
+}
+
+//========== driver proc interface ===========
+static int msdc_debug_proc_read(struct seq_file *s, void *p)
+{
+ seq_printf(s, "\n=========================================\n");
+ seq_printf(s, "Index<0> + Id + Zone\n");
+ seq_printf(s, "-> PWR<9> WRN<8> | FIO<7> OPS<6> FUN<5> CFG<4> | INT<3> RSP<2> CMD<1> DMA<0>\n");
+ seq_printf(s, "-> echo 0 3 0x3ff >msdc_bebug -> host[3] debug zone set to 0x3ff\n");
+ seq_printf(s, "-> MSDC[0] Zone: 0x%.8x\n", sd_debug_zone[0]);
+ seq_printf(s, "-> MSDC[1] Zone: 0x%.8x\n", sd_debug_zone[1]);
+ seq_printf(s, "-> MSDC[2] Zone: 0x%.8x\n", sd_debug_zone[2]);
+ seq_printf(s, "-> MSDC[3] Zone: 0x%.8x\n", sd_debug_zone[3]);
+
+ seq_printf(s, "Index<1> + ID:4|Mode:4 + DMA_SIZE\n");
+ seq_printf(s, "-> 0)PIO 1)DMA 2)SIZE\n");
+ seq_printf(s, "-> echo 1 22 0x200 >msdc_bebug -> host[2] size mode, dma when >= 512\n");
+ seq_printf(s, "-> MSDC[0] mode<%d> size<%d>\n", drv_mode[0], dma_size[0]);
+ seq_printf(s, "-> MSDC[1] mode<%d> size<%d>\n", drv_mode[1], dma_size[1]);
+ seq_printf(s, "-> MSDC[2] mode<%d> size<%d>\n", drv_mode[2], dma_size[2]);
+ seq_printf(s, "-> MSDC[3] mode<%d> size<%d>\n", drv_mode[3], dma_size[3]);
+
+ seq_printf(s, "Index<3> + SDIO_PROFILE + TIME\n");
+ seq_printf(s, "-> echo 3 1 0x1E >msdc_bebug -> enable sdio_profile, 30s\n");
+ seq_printf(s, "-> SDIO_PROFILE<%d> TIME<%ds>\n", sdio_pro_enable, sdio_pro_time);
+ seq_printf(s, "=========================================\n\n");
+
+ return 0;
+}
+
+static ssize_t msdc_debug_proc_write(struct file *file,
+ const char __user *buf, size_t count, loff_t *data)
+{
+ int ret;
+
+ int cmd, p1, p2;
+ int id, zone;
+ int mode, size;
+
+ if (count == 0)return -1;
+ if(count > 255)count = 255;
+
+ ret = copy_from_user(cmd_buf, buf, count);
+ if (ret < 0)return -1;
+
+ cmd_buf[count] = '\0';
+ printk("msdc Write %s\n", cmd_buf);
+
+ sscanf(cmd_buf, "%x %x %x", &cmd, &p1, &p2);
+
+ if(cmd == SD_TOOL_ZONE) {
+ id = p1; zone = p2; zone &= 0x3ff;
+ printk("msdc host_id<%d> zone<0x%.8x>\n", id, zone);
+ if(id >=0 && id<=3){
+ sd_debug_zone[id] = zone;
+ }
+ else if(id == 4){
+ sd_debug_zone[0] = sd_debug_zone[1] = zone;
+ sd_debug_zone[2] = sd_debug_zone[3] = zone;
+ }
+ else{
+ printk("msdc host_id error when set debug zone\n");
+ }
+ } else if (cmd == SD_TOOL_DMA_SIZE) {
+ id = p1>>4; mode = (p1&0xf); size = p2;
+ if(id >=0 && id<=3){
+ drv_mode[id] = mode;
+ dma_size[id] = p2;
+ }
+ else if(id == 4){
+ drv_mode[0] = drv_mode[1] = mode;
+ drv_mode[2] = drv_mode[3] = mode;
+ dma_size[0] = dma_size[1] = p2;
+ dma_size[2] = dma_size[3] = p2;
+ }
+ else{
+ printk("msdc host_id error when select mode\n");
+ }
+ } else if (cmd == SD_TOOL_SDIO_PROFILE) {
+ if (p1 == 1) { /* enable profile */
+ if (gpt_enable == 0) {
+ // msdc_init_gpt(); /* --- by chhung */
+ gpt_enable = 1;
+ }
+ sdio_pro_enable = 1;
+ if (p2 == 0) p2 = 1; if (p2 >= 30) p2 = 30;
+ sdio_pro_time = p2 ;
+ } else if (p1 == 0) {
+ /* todo */
+ sdio_pro_enable = 0;
+ }
+ }
+
+ return count;
+}
+
+static int msdc_debug_show(struct inode *inode, struct file *file)
+{
+ return single_open(file, msdc_debug_proc_read, NULL);
+}
+
+static const struct file_operations msdc_debug_fops = {
+ .owner = THIS_MODULE,
+ .open = msdc_debug_show,
+ .read = seq_read,
+ .write = msdc_debug_proc_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+int msdc_debug_proc_init(void)
+{
+ struct proc_dir_entry *de = proc_create("msdc_debug", 0667, NULL, &msdc_debug_fops);
+
+ if (!de || IS_ERR(de))
+ printk("!! Create MSDC debug PROC fail !!\n");
+
+ return 0 ;
+}
+EXPORT_SYMBOL_GPL(msdc_debug_proc_init);
+#endif
diff --git a/drivers/staging/mt7621-mmc/dbg.h b/drivers/staging/mt7621-mmc/dbg.h
new file mode 100644
index 000000000000..e58c4312933e
--- /dev/null
+++ b/drivers/staging/mt7621-mmc/dbg.h
@@ -0,0 +1,156 @@
+/* Copyright Statement:
+ *
+ * This software/firmware and related documentation ("MediaTek Software") are
+ * protected under relevant copyright laws. The information contained herein
+ * is confidential and proprietary to MediaTek Inc. and/or its licensors.
+ * Without the prior written permission of MediaTek inc. and/or its licensors,
+ * any reproduction, modification, use or disclosure of MediaTek Software,
+ * and information contained herein, in whole or in part, shall be strictly prohibited.
+ *
+ * MediaTek Inc. (C) 2010. All rights reserved.
+ *
+ * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+ * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+ * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
+ * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+ * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
+ * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
+ * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
+ * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
+ * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
+ * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
+ * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+ * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
+ * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
+ * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
+ * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
+ * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+ *
+ * The following software/firmware and/or related documentation ("MediaTek Software")
+ * have been modified by MediaTek Inc. All revisions are subject to any receiver's
+ * applicable license agreements with MediaTek Inc.
+ */
+#ifndef __MT_MSDC_DEUBG__
+#define __MT_MSDC_DEUBG__
+
+//==========================
+extern u32 sdio_pro_enable;
+/* for a type command, e.g. CMD53, 2 blocks */
+struct cmd_profile {
+ u32 max_tc; /* Max tick count */
+ u32 min_tc;
+ u32 tot_tc; /* total tick count */
+ u32 tot_bytes;
+ u32 count; /* the counts of the command */
+};
+
+/* dump when total_tc and total_bytes */
+struct sdio_profile {
+ u32 total_tc; /* total tick count of CMD52 and CMD53 */
+ u32 total_tx_bytes; /* total bytes of CMD53 Tx */
+ u32 total_rx_bytes; /* total bytes of CMD53 Rx */
+
+ /*CMD52*/
+ struct cmd_profile cmd52_tx;
+ struct cmd_profile cmd52_rx;
+
+ /*CMD53 in byte unit */
+ struct cmd_profile cmd53_tx_byte[512];
+ struct cmd_profile cmd53_rx_byte[512];
+
+ /*CMD53 in block unit */
+ struct cmd_profile cmd53_tx_blk[100];
+ struct cmd_profile cmd53_rx_blk[100];
+};
+
+//==========================
+typedef enum {
+ SD_TOOL_ZONE = 0,
+ SD_TOOL_DMA_SIZE = 1,
+ SD_TOOL_PM_ENABLE = 2,
+ SD_TOOL_SDIO_PROFILE = 3,
+} msdc_dbg;
+
+typedef enum {
+ MODE_PIO = 0,
+ MODE_DMA = 1,
+ MODE_SIZE_DEP = 2,
+} msdc_mode;
+extern msdc_mode drv_mode[4];
+extern u32 dma_size[4];
+
+/* Debug message event */
+#define DBG_EVT_NONE (0) /* No event */
+#define DBG_EVT_DMA (1 << 0) /* DMA related event */
+#define DBG_EVT_CMD (1 << 1) /* MSDC CMD related event */
+#define DBG_EVT_RSP (1 << 2) /* MSDC CMD RSP related event */
+#define DBG_EVT_INT (1 << 3) /* MSDC INT event */
+#define DBG_EVT_CFG (1 << 4) /* MSDC CFG event */
+#define DBG_EVT_FUC (1 << 5) /* Function event */
+#define DBG_EVT_OPS (1 << 6) /* Read/Write operation event */
+#define DBG_EVT_FIO (1 << 7) /* FIFO operation event */
+#define DBG_EVT_WRN (1 << 8) /* Warning event */
+#define DBG_EVT_PWR (1 << 9) /* Power event */
+#define DBG_EVT_ALL (0xffffffff)
+
+#define DBG_EVT_MASK (DBG_EVT_ALL)
+
+extern unsigned int sd_debug_zone[4];
+#define TAG "msdc"
+#if 0 /* +++ chhung */
+#define BUG_ON(x) \
+do { \
+ if (x) { \
+ printk("[BUG] %s LINE:%d FILE:%s\n", #x, __LINE__, __FILE__); \
+ while(1); \
+ } \
+}while(0)
+#endif /* end of +++ */
+
+#define N_MSG(evt, fmt, args...)
+/*
+do { \
+ if ((DBG_EVT_##evt) & sd_debug_zone[host->id]) { \
+ printk(KERN_ERR TAG"%d -> "fmt" <- %s() : L<%d> PID<%s><0x%x>\n", \
+ host->id, ##args , __FUNCTION__, __LINE__, current->comm, current->pid); \
+ } \
+} while(0)
+*/
+
+#define ERR_MSG(fmt, args...) \
+do { \
+ printk(KERN_ERR TAG"%d -> "fmt" <- %s() : L<%d> PID<%s><0x%x>\n", \
+ host->id, ##args , __FUNCTION__, __LINE__, current->comm, current->pid); \
+} while(0);
+
+#if 1
+//defined CONFIG_MTK_MMC_CD_POLL
+#define INIT_MSG(fmt, args...)
+#define IRQ_MSG(fmt, args...)
+#else
+#define INIT_MSG(fmt, args...) \
+do { \
+ printk(KERN_ERR TAG"%d -> "fmt" <- %s() : L<%d> PID<%s><0x%x>\n", \
+ host->id, ##args , __FUNCTION__, __LINE__, current->comm, current->pid); \
+} while(0);
+
+/* PID in ISR in not corrent */
+#define IRQ_MSG(fmt, args...) \
+do { \
+ printk(KERN_ERR TAG"%d -> "fmt" <- %s() : L<%d>\n", \
+ host->id, ##args , __FUNCTION__, __LINE__); \
+} while(0);
+#endif
+
+int msdc_debug_proc_init(void);
+
+#if 0 /* --- chhung */
+void msdc_init_gpt(void);
+extern void GPT_GetCounter64(UINT32 *cntL32, UINT32 *cntH32);
+#endif /* end of --- */
+u32 msdc_time_calc(u32 old_L32, u32 old_H32, u32 new_L32, u32 new_H32);
+void msdc_performance(u32 opcode, u32 sizes, u32 bRx, u32 ticks);
+
+#endif
diff --git a/drivers/staging/mt7621-mmc/mt6575_sd.h b/drivers/staging/mt7621-mmc/mt6575_sd.h
new file mode 100644
index 000000000000..e90c4f1d1df7
--- /dev/null
+++ b/drivers/staging/mt7621-mmc/mt6575_sd.h
@@ -0,0 +1,1001 @@
+/* Copyright Statement:
+ *
+ * This software/firmware and related documentation ("MediaTek Software") are
+ * protected under relevant copyright laws. The information contained herein
+ * is confidential and proprietary to MediaTek Inc. and/or its licensors.
+ * Without the prior written permission of MediaTek inc. and/or its licensors,
+ * any reproduction, modification, use or disclosure of MediaTek Software,
+ * and information contained herein, in whole or in part, shall be strictly prohibited.
+ */
+/* MediaTek Inc. (C) 2010. All rights reserved.
+ *
+ * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+ * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+ * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
+ * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+ * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
+ * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
+ * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
+ * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
+ * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
+ * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
+ * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+ * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
+ * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
+ * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
+ * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
+ * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+ *
+ * The following software/firmware and/or related documentation ("MediaTek Software")
+ * have been modified by MediaTek Inc. All revisions are subject to any receiver's
+ * applicable license agreements with MediaTek Inc.
+ */
+
+#ifndef MT6575_SD_H
+#define MT6575_SD_H
+
+#include <linux/bitops.h>
+#include <linux/mmc/host.h>
+
+// #include <mach/mt6575_reg_base.h> /* --- by chhung */
+
+/*--------------------------------------------------------------------------*/
+/* Common Macro */
+/*--------------------------------------------------------------------------*/
+#define REG_ADDR(x) ((volatile u32*)(base + OFFSET_##x))
+
+/*--------------------------------------------------------------------------*/
+/* Common Definition */
+/*--------------------------------------------------------------------------*/
+#define MSDC_FIFO_SZ (128)
+#define MSDC_FIFO_THD (64) // (128)
+#define MSDC_NUM (4)
+
+#define MSDC_MS (0)
+#define MSDC_SDMMC (1)
+
+#define MSDC_MODE_UNKNOWN (0)
+#define MSDC_MODE_PIO (1)
+#define MSDC_MODE_DMA_BASIC (2)
+#define MSDC_MODE_DMA_DESC (3)
+#define MSDC_MODE_DMA_ENHANCED (4)
+#define MSDC_MODE_MMC_STREAM (5)
+
+#define MSDC_BUS_1BITS (0)
+#define MSDC_BUS_4BITS (1)
+#define MSDC_BUS_8BITS (2)
+
+#define MSDC_BRUST_8B (3)
+#define MSDC_BRUST_16B (4)
+#define MSDC_BRUST_32B (5)
+#define MSDC_BRUST_64B (6)
+
+#define MSDC_PIN_PULL_NONE (0)
+#define MSDC_PIN_PULL_DOWN (1)
+#define MSDC_PIN_PULL_UP (2)
+#define MSDC_PIN_KEEP (3)
+
+#define MSDC_MAX_SCLK (48000000) /* +/- by chhung */
+#define MSDC_MIN_SCLK (260000)
+
+#define MSDC_AUTOCMD12 (0x0001)
+#define MSDC_AUTOCMD23 (0x0002)
+#define MSDC_AUTOCMD19 (0x0003)
+
+#define MSDC_EMMC_BOOTMODE0 (0) /* Pull low CMD mode */
+#define MSDC_EMMC_BOOTMODE1 (1) /* Reset CMD mode */
+
+enum {
+ RESP_NONE = 0,
+ RESP_R1,
+ RESP_R2,
+ RESP_R3,
+ RESP_R4,
+ RESP_R5,
+ RESP_R6,
+ RESP_R7,
+ RESP_R1B
+};
+
+/*--------------------------------------------------------------------------*/
+/* Register Offset */
+/*--------------------------------------------------------------------------*/
+#define OFFSET_MSDC_CFG (0x0)
+#define OFFSET_MSDC_IOCON (0x04)
+#define OFFSET_MSDC_PS (0x08)
+#define OFFSET_MSDC_INT (0x0c)
+#define OFFSET_MSDC_INTEN (0x10)
+#define OFFSET_MSDC_FIFOCS (0x14)
+#define OFFSET_MSDC_TXDATA (0x18)
+#define OFFSET_MSDC_RXDATA (0x1c)
+#define OFFSET_SDC_CFG (0x30)
+#define OFFSET_SDC_CMD (0x34)
+#define OFFSET_SDC_ARG (0x38)
+#define OFFSET_SDC_STS (0x3c)
+#define OFFSET_SDC_RESP0 (0x40)
+#define OFFSET_SDC_RESP1 (0x44)
+#define OFFSET_SDC_RESP2 (0x48)
+#define OFFSET_SDC_RESP3 (0x4c)
+#define OFFSET_SDC_BLK_NUM (0x50)
+#define OFFSET_SDC_CSTS (0x58)
+#define OFFSET_SDC_CSTS_EN (0x5c)
+#define OFFSET_SDC_DCRC_STS (0x60)
+#define OFFSET_EMMC_CFG0 (0x70)
+#define OFFSET_EMMC_CFG1 (0x74)
+#define OFFSET_EMMC_STS (0x78)
+#define OFFSET_EMMC_IOCON (0x7c)
+#define OFFSET_SDC_ACMD_RESP (0x80)
+#define OFFSET_SDC_ACMD19_TRG (0x84)
+#define OFFSET_SDC_ACMD19_STS (0x88)
+#define OFFSET_MSDC_DMA_SA (0x90)
+#define OFFSET_MSDC_DMA_CA (0x94)
+#define OFFSET_MSDC_DMA_CTRL (0x98)
+#define OFFSET_MSDC_DMA_CFG (0x9c)
+#define OFFSET_MSDC_DBG_SEL (0xa0)
+#define OFFSET_MSDC_DBG_OUT (0xa4)
+#define OFFSET_MSDC_PATCH_BIT (0xb0)
+#define OFFSET_MSDC_PATCH_BIT1 (0xb4)
+#define OFFSET_MSDC_PAD_CTL0 (0xe0)
+#define OFFSET_MSDC_PAD_CTL1 (0xe4)
+#define OFFSET_MSDC_PAD_CTL2 (0xe8)
+#define OFFSET_MSDC_PAD_TUNE (0xec)
+#define OFFSET_MSDC_DAT_RDDLY0 (0xf0)
+#define OFFSET_MSDC_DAT_RDDLY1 (0xf4)
+#define OFFSET_MSDC_HW_DBG (0xf8)
+#define OFFSET_MSDC_VERSION (0x100)
+#define OFFSET_MSDC_ECO_VER (0x104)
+
+/*--------------------------------------------------------------------------*/
+/* Register Address */
+/*--------------------------------------------------------------------------*/
+
+/* common register */
+#define MSDC_CFG REG_ADDR(MSDC_CFG)
+#define MSDC_IOCON REG_ADDR(MSDC_IOCON)
+#define MSDC_PS REG_ADDR(MSDC_PS)
+#define MSDC_INT REG_ADDR(MSDC_INT)
+#define MSDC_INTEN REG_ADDR(MSDC_INTEN)
+#define MSDC_FIFOCS REG_ADDR(MSDC_FIFOCS)
+#define MSDC_TXDATA REG_ADDR(MSDC_TXDATA)
+#define MSDC_RXDATA REG_ADDR(MSDC_RXDATA)
+#define MSDC_PATCH_BIT0 REG_ADDR(MSDC_PATCH_BIT)
+
+/* sdmmc register */
+#define SDC_CFG REG_ADDR(SDC_CFG)
+#define SDC_CMD REG_ADDR(SDC_CMD)
+#define SDC_ARG REG_ADDR(SDC_ARG)
+#define SDC_STS REG_ADDR(SDC_STS)
+#define SDC_RESP0 REG_ADDR(SDC_RESP0)
+#define SDC_RESP1 REG_ADDR(SDC_RESP1)
+#define SDC_RESP2 REG_ADDR(SDC_RESP2)
+#define SDC_RESP3 REG_ADDR(SDC_RESP3)
+#define SDC_BLK_NUM REG_ADDR(SDC_BLK_NUM)
+#define SDC_CSTS REG_ADDR(SDC_CSTS)
+#define SDC_CSTS_EN REG_ADDR(SDC_CSTS_EN)
+#define SDC_DCRC_STS REG_ADDR(SDC_DCRC_STS)
+
+/* emmc register*/
+#define EMMC_CFG0 REG_ADDR(EMMC_CFG0)
+#define EMMC_CFG1 REG_ADDR(EMMC_CFG1)
+#define EMMC_STS REG_ADDR(EMMC_STS)
+#define EMMC_IOCON REG_ADDR(EMMC_IOCON)
+
+/* auto command register */
+#define SDC_ACMD_RESP REG_ADDR(SDC_ACMD_RESP)
+#define SDC_ACMD19_TRG REG_ADDR(SDC_ACMD19_TRG)
+#define SDC_ACMD19_STS REG_ADDR(SDC_ACMD19_STS)
+
+/* dma register */
+#define MSDC_DMA_SA REG_ADDR(MSDC_DMA_SA)
+#define MSDC_DMA_CA REG_ADDR(MSDC_DMA_CA)
+#define MSDC_DMA_CTRL REG_ADDR(MSDC_DMA_CTRL)
+#define MSDC_DMA_CFG REG_ADDR(MSDC_DMA_CFG)
+
+/* pad ctrl register */
+#define MSDC_PAD_CTL0 REG_ADDR(MSDC_PAD_CTL0)
+#define MSDC_PAD_CTL1 REG_ADDR(MSDC_PAD_CTL1)
+#define MSDC_PAD_CTL2 REG_ADDR(MSDC_PAD_CTL2)
+
+/* data read delay */
+#define MSDC_DAT_RDDLY0 REG_ADDR(MSDC_DAT_RDDLY0)
+#define MSDC_DAT_RDDLY1 REG_ADDR(MSDC_DAT_RDDLY1)
+
+/* debug register */
+#define MSDC_DBG_SEL REG_ADDR(MSDC_DBG_SEL)
+#define MSDC_DBG_OUT REG_ADDR(MSDC_DBG_OUT)
+
+/* misc register */
+#define MSDC_PATCH_BIT REG_ADDR(MSDC_PATCH_BIT)
+#define MSDC_PATCH_BIT1 REG_ADDR(MSDC_PATCH_BIT1)
+#define MSDC_PAD_TUNE REG_ADDR(MSDC_PAD_TUNE)
+#define MSDC_HW_DBG REG_ADDR(MSDC_HW_DBG)
+#define MSDC_VERSION REG_ADDR(MSDC_VERSION)
+#define MSDC_ECO_VER REG_ADDR(MSDC_ECO_VER) /* ECO Version */
+
+/*--------------------------------------------------------------------------*/
+/* Register Mask */
+/*--------------------------------------------------------------------------*/
+
+/* MSDC_CFG mask */
+#define MSDC_CFG_MODE (0x1 << 0) /* RW */
+#define MSDC_CFG_CKPDN (0x1 << 1) /* RW */
+#define MSDC_CFG_RST (0x1 << 2) /* RW */
+#define MSDC_CFG_PIO (0x1 << 3) /* RW */
+#define MSDC_CFG_CKDRVEN (0x1 << 4) /* RW */
+#define MSDC_CFG_BV18SDT (0x1 << 5) /* RW */
+#define MSDC_CFG_BV18PSS (0x1 << 6) /* R */
+#define MSDC_CFG_CKSTB (0x1 << 7) /* R */
+#define MSDC_CFG_CKDIV (0xff << 8) /* RW */
+#define MSDC_CFG_CKMOD (0x3 << 16) /* RW */
+
+/* MSDC_IOCON mask */
+#define MSDC_IOCON_SDR104CKS (0x1 << 0) /* RW */
+#define MSDC_IOCON_RSPL (0x1 << 1) /* RW */
+#define MSDC_IOCON_DSPL (0x1 << 2) /* RW */
+#define MSDC_IOCON_DDLSEL (0x1 << 3) /* RW */
+#define MSDC_IOCON_DDR50CKD (0x1 << 4) /* RW */
+#define MSDC_IOCON_DSPLSEL (0x1 << 5) /* RW */
+#define MSDC_IOCON_D0SPL (0x1 << 16) /* RW */
+#define MSDC_IOCON_D1SPL (0x1 << 17) /* RW */
+#define MSDC_IOCON_D2SPL (0x1 << 18) /* RW */
+#define MSDC_IOCON_D3SPL (0x1 << 19) /* RW */
+#define MSDC_IOCON_D4SPL (0x1 << 20) /* RW */
+#define MSDC_IOCON_D5SPL (0x1 << 21) /* RW */
+#define MSDC_IOCON_D6SPL (0x1 << 22) /* RW */
+#define MSDC_IOCON_D7SPL (0x1 << 23) /* RW */
+#define MSDC_IOCON_RISCSZ (0x3 << 24) /* RW */
+
+/* MSDC_PS mask */
+#define MSDC_PS_CDEN (0x1 << 0) /* RW */
+#define MSDC_PS_CDSTS (0x1 << 1) /* R */
+#define MSDC_PS_CDDEBOUNCE (0xf << 12) /* RW */
+#define MSDC_PS_DAT (0xff << 16) /* R */
+#define MSDC_PS_CMD (0x1 << 24) /* R */
+#define MSDC_PS_WP (0x1UL<< 31) /* R */
+
+/* MSDC_INT mask */
+#define MSDC_INT_MMCIRQ (0x1 << 0) /* W1C */
+#define MSDC_INT_CDSC (0x1 << 1) /* W1C */
+#define MSDC_INT_ACMDRDY (0x1 << 3) /* W1C */
+#define MSDC_INT_ACMDTMO (0x1 << 4) /* W1C */
+#define MSDC_INT_ACMDCRCERR (0x1 << 5) /* W1C */
+#define MSDC_INT_DMAQ_EMPTY (0x1 << 6) /* W1C */
+#define MSDC_INT_SDIOIRQ (0x1 << 7) /* W1C */
+#define MSDC_INT_CMDRDY (0x1 << 8) /* W1C */
+#define MSDC_INT_CMDTMO (0x1 << 9) /* W1C */
+#define MSDC_INT_RSPCRCERR (0x1 << 10) /* W1C */
+#define MSDC_INT_CSTA (0x1 << 11) /* R */
+#define MSDC_INT_XFER_COMPL (0x1 << 12) /* W1C */
+#define MSDC_INT_DXFER_DONE (0x1 << 13) /* W1C */
+#define MSDC_INT_DATTMO (0x1 << 14) /* W1C */
+#define MSDC_INT_DATCRCERR (0x1 << 15) /* W1C */
+#define MSDC_INT_ACMD19_DONE (0x1 << 16) /* W1C */
+
+/* MSDC_INTEN mask */
+#define MSDC_INTEN_MMCIRQ (0x1 << 0) /* RW */
+#define MSDC_INTEN_CDSC (0x1 << 1) /* RW */
+#define MSDC_INTEN_ACMDRDY (0x1 << 3) /* RW */
+#define MSDC_INTEN_ACMDTMO (0x1 << 4) /* RW */
+#define MSDC_INTEN_ACMDCRCERR (0x1 << 5) /* RW */
+#define MSDC_INTEN_DMAQ_EMPTY (0x1 << 6) /* RW */
+#define MSDC_INTEN_SDIOIRQ (0x1 << 7) /* RW */
+#define MSDC_INTEN_CMDRDY (0x1 << 8) /* RW */
+#define MSDC_INTEN_CMDTMO (0x1 << 9) /* RW */
+#define MSDC_INTEN_RSPCRCERR (0x1 << 10) /* RW */
+#define MSDC_INTEN_CSTA (0x1 << 11) /* RW */
+#define MSDC_INTEN_XFER_COMPL (0x1 << 12) /* RW */
+#define MSDC_INTEN_DXFER_DONE (0x1 << 13) /* RW */
+#define MSDC_INTEN_DATTMO (0x1 << 14) /* RW */
+#define MSDC_INTEN_DATCRCERR (0x1 << 15) /* RW */
+#define MSDC_INTEN_ACMD19_DONE (0x1 << 16) /* RW */
+
+/* MSDC_FIFOCS mask */
+#define MSDC_FIFOCS_RXCNT (0xff << 0) /* R */
+#define MSDC_FIFOCS_TXCNT (0xff << 16) /* R */
+#define MSDC_FIFOCS_CLR (0x1UL<< 31) /* RW */
+
+/* SDC_CFG mask */
+#define SDC_CFG_SDIOINTWKUP (0x1 << 0) /* RW */
+#define SDC_CFG_INSWKUP (0x1 << 1) /* RW */
+#define SDC_CFG_BUSWIDTH (0x3 << 16) /* RW */
+#define SDC_CFG_SDIO (0x1 << 19) /* RW */
+#define SDC_CFG_SDIOIDE (0x1 << 20) /* RW */
+#define SDC_CFG_INTATGAP (0x1 << 21) /* RW */
+#define SDC_CFG_DTOC (0xffUL << 24) /* RW */
+
+/* SDC_CMD mask */
+#define SDC_CMD_OPC (0x3f << 0) /* RW */
+#define SDC_CMD_BRK (0x1 << 6) /* RW */
+#define SDC_CMD_RSPTYP (0x7 << 7) /* RW */
+#define SDC_CMD_DTYP (0x3 << 11) /* RW */
+#define SDC_CMD_DTYP (0x3 << 11) /* RW */
+#define SDC_CMD_RW (0x1 << 13) /* RW */
+#define SDC_CMD_STOP (0x1 << 14) /* RW */
+#define SDC_CMD_GOIRQ (0x1 << 15) /* RW */
+#define SDC_CMD_BLKLEN (0xfff<< 16) /* RW */
+#define SDC_CMD_AUTOCMD (0x3 << 28) /* RW */
+#define SDC_CMD_VOLSWTH (0x1 << 30) /* RW */
+
+/* SDC_STS mask */
+#define SDC_STS_SDCBUSY (0x1 << 0) /* RW */
+#define SDC_STS_CMDBUSY (0x1 << 1) /* RW */
+#define SDC_STS_SWR_COMPL (0x1 << 31) /* RW */
+
+/* SDC_DCRC_STS mask */
+#define SDC_DCRC_STS_NEG (0xf << 8) /* RO */
+#define SDC_DCRC_STS_POS (0xff << 0) /* RO */
+
+/* EMMC_CFG0 mask */
+#define EMMC_CFG0_BOOTSTART (0x1 << 0) /* W */
+#define EMMC_CFG0_BOOTSTOP (0x1 << 1) /* W */
+#define EMMC_CFG0_BOOTMODE (0x1 << 2) /* RW */
+#define EMMC_CFG0_BOOTACKDIS (0x1 << 3) /* RW */
+#define EMMC_CFG0_BOOTWDLY (0x7 << 12) /* RW */
+#define EMMC_CFG0_BOOTSUPP (0x1 << 15) /* RW */
+
+/* EMMC_CFG1 mask */
+#define EMMC_CFG1_BOOTDATTMC (0xfffff << 0) /* RW */
+#define EMMC_CFG1_BOOTACKTMC (0xfffUL << 20) /* RW */
+
+/* EMMC_STS mask */
+#define EMMC_STS_BOOTCRCERR (0x1 << 0) /* W1C */
+#define EMMC_STS_BOOTACKERR (0x1 << 1) /* W1C */
+#define EMMC_STS_BOOTDATTMO (0x1 << 2) /* W1C */
+#define EMMC_STS_BOOTACKTMO (0x1 << 3) /* W1C */
+#define EMMC_STS_BOOTUPSTATE (0x1 << 4) /* R */
+#define EMMC_STS_BOOTACKRCV (0x1 << 5) /* W1C */
+#define EMMC_STS_BOOTDATRCV (0x1 << 6) /* R */
+
+/* EMMC_IOCON mask */
+#define EMMC_IOCON_BOOTRST (0x1 << 0) /* RW */
+
+/* SDC_ACMD19_TRG mask */
+#define SDC_ACMD19_TRG_TUNESEL (0xf << 0) /* RW */
+
+/* MSDC_DMA_CTRL mask */
+#define MSDC_DMA_CTRL_START (0x1 << 0) /* W */
+#define MSDC_DMA_CTRL_STOP (0x1 << 1) /* W */
+#define MSDC_DMA_CTRL_RESUME (0x1 << 2) /* W */
+#define MSDC_DMA_CTRL_MODE (0x1 << 8) /* RW */
+#define MSDC_DMA_CTRL_LASTBUF (0x1 << 10) /* RW */
+#define MSDC_DMA_CTRL_BRUSTSZ (0x7 << 12) /* RW */
+#define MSDC_DMA_CTRL_XFERSZ (0xffffUL << 16)/* RW */
+
+/* MSDC_DMA_CFG mask */
+#define MSDC_DMA_CFG_STS (0x1 << 0) /* R */
+#define MSDC_DMA_CFG_DECSEN (0x1 << 1) /* RW */
+#define MSDC_DMA_CFG_BDCSERR (0x1 << 4) /* R */
+#define MSDC_DMA_CFG_GPDCSERR (0x1 << 5) /* R */
+
+/* MSDC_PATCH_BIT mask */
+#define MSDC_PATCH_BIT_WFLSMODE (0x1 << 0) /* RW */
+#define MSDC_PATCH_BIT_ODDSUPP (0x1 << 1) /* RW */
+#define MSDC_PATCH_BIT_CKGEN_CK (0x1 << 6) /* E2: Fixed to 1 */
+#define MSDC_PATCH_BIT_IODSSEL (0x1 << 16) /* RW */
+#define MSDC_PATCH_BIT_IOINTSEL (0x1 << 17) /* RW */
+#define MSDC_PATCH_BIT_BUSYDLY (0xf << 18) /* RW */
+#define MSDC_PATCH_BIT_WDOD (0xf << 22) /* RW */
+#define MSDC_PATCH_BIT_IDRTSEL (0x1 << 26) /* RW */
+#define MSDC_PATCH_BIT_CMDFSEL (0x1 << 27) /* RW */
+#define MSDC_PATCH_BIT_INTDLSEL (0x1 << 28) /* RW */
+#define MSDC_PATCH_BIT_SPCPUSH (0x1 << 29) /* RW */
+#define MSDC_PATCH_BIT_DECRCTMO (0x1 << 30) /* RW */
+
+/* MSDC_PATCH_BIT1 mask */
+#define MSDC_PATCH_BIT1_WRDAT_CRCS (0x7 << 3)
+#define MSDC_PATCH_BIT1_CMD_RSP (0x7 << 0)
+
+/* MSDC_PAD_CTL0 mask */
+#define MSDC_PAD_CTL0_CLKDRVN (0x7 << 0) /* RW */
+#define MSDC_PAD_CTL0_CLKDRVP (0x7 << 4) /* RW */
+#define MSDC_PAD_CTL0_CLKSR (0x1 << 8) /* RW */
+#define MSDC_PAD_CTL0_CLKPD (0x1 << 16) /* RW */
+#define MSDC_PAD_CTL0_CLKPU (0x1 << 17) /* RW */
+#define MSDC_PAD_CTL0_CLKSMT (0x1 << 18) /* RW */
+#define MSDC_PAD_CTL0_CLKIES (0x1 << 19) /* RW */
+#define MSDC_PAD_CTL0_CLKTDSEL (0xf << 20) /* RW */
+#define MSDC_PAD_CTL0_CLKRDSEL (0xffUL<< 24) /* RW */
+
+/* MSDC_PAD_CTL1 mask */
+#define MSDC_PAD_CTL1_CMDDRVN (0x7 << 0) /* RW */
+#define MSDC_PAD_CTL1_CMDDRVP (0x7 << 4) /* RW */
+#define MSDC_PAD_CTL1_CMDSR (0x1 << 8) /* RW */
+#define MSDC_PAD_CTL1_CMDPD (0x1 << 16) /* RW */
+#define MSDC_PAD_CTL1_CMDPU (0x1 << 17) /* RW */
+#define MSDC_PAD_CTL1_CMDSMT (0x1 << 18) /* RW */
+#define MSDC_PAD_CTL1_CMDIES (0x1 << 19) /* RW */
+#define MSDC_PAD_CTL1_CMDTDSEL (0xf << 20) /* RW */
+#define MSDC_PAD_CTL1_CMDRDSEL (0xffUL<< 24) /* RW */
+
+/* MSDC_PAD_CTL2 mask */
+#define MSDC_PAD_CTL2_DATDRVN (0x7 << 0) /* RW */
+#define MSDC_PAD_CTL2_DATDRVP (0x7 << 4) /* RW */
+#define MSDC_PAD_CTL2_DATSR (0x1 << 8) /* RW */
+#define MSDC_PAD_CTL2_DATPD (0x1 << 16) /* RW */
+#define MSDC_PAD_CTL2_DATPU (0x1 << 17) /* RW */
+#define MSDC_PAD_CTL2_DATIES (0x1 << 19) /* RW */
+#define MSDC_PAD_CTL2_DATSMT (0x1 << 18) /* RW */
+#define MSDC_PAD_CTL2_DATTDSEL (0xf << 20) /* RW */
+#define MSDC_PAD_CTL2_DATRDSEL (0xffUL<< 24) /* RW */
+
+/* MSDC_PAD_TUNE mask */
+#define MSDC_PAD_TUNE_DATWRDLY (0x1F << 0) /* RW */
+#define MSDC_PAD_TUNE_DATRRDLY (0x1F << 8) /* RW */
+#define MSDC_PAD_TUNE_CMDRDLY (0x1F << 16) /* RW */
+#define MSDC_PAD_TUNE_CMDRRDLY (0x1FUL << 22) /* RW */
+#define MSDC_PAD_TUNE_CLKTXDLY (0x1FUL << 27) /* RW */
+
+/* MSDC_DAT_RDDLY0/1 mask */
+#define MSDC_DAT_RDDLY0_D0 (0x1F << 0) /* RW */
+#define MSDC_DAT_RDDLY0_D1 (0x1F << 8) /* RW */
+#define MSDC_DAT_RDDLY0_D2 (0x1F << 16) /* RW */
+#define MSDC_DAT_RDDLY0_D3 (0x1F << 24) /* RW */
+
+#define MSDC_DAT_RDDLY1_D4 (0x1F << 0) /* RW */
+#define MSDC_DAT_RDDLY1_D5 (0x1F << 8) /* RW */
+#define MSDC_DAT_RDDLY1_D6 (0x1F << 16) /* RW */
+#define MSDC_DAT_RDDLY1_D7 (0x1F << 24) /* RW */
+
+#define MSDC_CKGEN_MSDC_DLY_SEL (0x1F<<10)
+#define MSDC_INT_DAT_LATCH_CK_SEL (0x7<<7)
+#define MSDC_CKGEN_MSDC_CK_SEL (0x1<<6)
+#define CARD_READY_FOR_DATA (1<<8)
+#define CARD_CURRENT_STATE(x) ((x&0x00001E00)>>9)
+
+/*--------------------------------------------------------------------------*/
+/* Descriptor Structure */
+/*--------------------------------------------------------------------------*/
+typedef struct {
+ u32 hwo:1; /* could be changed by hw */
+ u32 bdp:1;
+ u32 rsv0:6;
+ u32 chksum:8;
+ u32 intr:1;
+ u32 rsv1:15;
+ void *next;
+ void *ptr;
+ u32 buflen:16;
+ u32 extlen:8;
+ u32 rsv2:8;
+ u32 arg;
+ u32 blknum;
+ u32 cmd;
+} gpd_t;
+
+typedef struct {
+ u32 eol:1;
+ u32 rsv0:7;
+ u32 chksum:8;
+ u32 rsv1:1;
+ u32 blkpad:1;
+ u32 dwpad:1;
+ u32 rsv2:13;
+ void *next;
+ void *ptr;
+ u32 buflen:16;
+ u32 rsv3:16;
+} bd_t;
+
+/*--------------------------------------------------------------------------*/
+/* Register Debugging Structure */
+/*--------------------------------------------------------------------------*/
+
+typedef struct {
+ u32 msdc:1;
+ u32 ckpwn:1;
+ u32 rst:1;
+ u32 pio:1;
+ u32 ckdrven:1;
+ u32 start18v:1;
+ u32 pass18v:1;
+ u32 ckstb:1;
+ u32 ckdiv:8;
+ u32 ckmod:2;
+ u32 pad:14;
+} msdc_cfg_reg;
+typedef struct {
+ u32 sdr104cksel:1;
+ u32 rsmpl:1;
+ u32 dsmpl:1;
+ u32 ddlysel:1;
+ u32 ddr50ckd:1;
+ u32 dsplsel:1;
+ u32 pad1:10;
+ u32 d0spl:1;
+ u32 d1spl:1;
+ u32 d2spl:1;
+ u32 d3spl:1;
+ u32 d4spl:1;
+ u32 d5spl:1;
+ u32 d6spl:1;
+ u32 d7spl:1;
+ u32 riscsz:1;
+ u32 pad2:7;
+} msdc_iocon_reg;
+typedef struct {
+ u32 cden:1;
+ u32 cdsts:1;
+ u32 pad1:10;
+ u32 cddebounce:4;
+ u32 dat:8;
+ u32 cmd:1;
+ u32 pad2:6;
+ u32 wp:1;
+} msdc_ps_reg;
+typedef struct {
+ u32 mmcirq:1;
+ u32 cdsc:1;
+ u32 pad1:1;
+ u32 atocmdrdy:1;
+ u32 atocmdtmo:1;
+ u32 atocmdcrc:1;
+ u32 dmaqempty:1;
+ u32 sdioirq:1;
+ u32 cmdrdy:1;
+ u32 cmdtmo:1;
+ u32 rspcrc:1;
+ u32 csta:1;
+ u32 xfercomp:1;
+ u32 dxferdone:1;
+ u32 dattmo:1;
+ u32 datcrc:1;
+ u32 atocmd19done:1;
+ u32 pad2:15;
+} msdc_int_reg;
+typedef struct {
+ u32 mmcirq:1;
+ u32 cdsc:1;
+ u32 pad1:1;
+ u32 atocmdrdy:1;
+ u32 atocmdtmo:1;
+ u32 atocmdcrc:1;
+ u32 dmaqempty:1;
+ u32 sdioirq:1;
+ u32 cmdrdy:1;
+ u32 cmdtmo:1;
+ u32 rspcrc:1;
+ u32 csta:1;
+ u32 xfercomp:1;
+ u32 dxferdone:1;
+ u32 dattmo:1;
+ u32 datcrc:1;
+ u32 atocmd19done:1;
+ u32 pad2:15;
+} msdc_inten_reg;
+typedef struct {
+ u32 rxcnt:8;
+ u32 pad1:8;
+ u32 txcnt:8;
+ u32 pad2:7;
+ u32 clr:1;
+} msdc_fifocs_reg;
+typedef struct {
+ u32 val;
+} msdc_txdat_reg;
+typedef struct {
+ u32 val;
+} msdc_rxdat_reg;
+typedef struct {
+ u32 sdiowkup:1;
+ u32 inswkup:1;
+ u32 pad1:14;
+ u32 buswidth:2;
+ u32 pad2:1;
+ u32 sdio:1;
+ u32 sdioide:1;
+ u32 intblkgap:1;
+ u32 pad4:2;
+ u32 dtoc:8;
+} sdc_cfg_reg;
+typedef struct {
+ u32 cmd:6;
+ u32 brk:1;
+ u32 rsptyp:3;
+ u32 pad1:1;
+ u32 dtype:2;
+ u32 rw:1;
+ u32 stop:1;
+ u32 goirq:1;
+ u32 blklen:12;
+ u32 atocmd:2;
+ u32 volswth:1;
+ u32 pad2:1;
+} sdc_cmd_reg;
+typedef struct {
+ u32 arg;
+} sdc_arg_reg;
+typedef struct {
+ u32 sdcbusy:1;
+ u32 cmdbusy:1;
+ u32 pad:29;
+ u32 swrcmpl:1;
+} sdc_sts_reg;
+typedef struct {
+ u32 val;
+} sdc_resp0_reg;
+typedef struct {
+ u32 val;
+} sdc_resp1_reg;
+typedef struct {
+ u32 val;
+} sdc_resp2_reg;
+typedef struct {
+ u32 val;
+} sdc_resp3_reg;
+typedef struct {
+ u32 num;
+} sdc_blknum_reg;
+typedef struct {
+ u32 sts;
+} sdc_csts_reg;
+typedef struct {
+ u32 sts;
+} sdc_cstsen_reg;
+typedef struct {
+ u32 datcrcsts:8;
+ u32 ddrcrcsts:4;
+ u32 pad:20;
+} sdc_datcrcsts_reg;
+typedef struct {
+ u32 bootstart:1;
+ u32 bootstop:1;
+ u32 bootmode:1;
+ u32 pad1:9;
+ u32 bootwaidly:3;
+ u32 bootsupp:1;
+ u32 pad2:16;
+} emmc_cfg0_reg;
+typedef struct {
+ u32 bootcrctmc:16;
+ u32 pad:4;
+ u32 bootacktmc:12;
+} emmc_cfg1_reg;
+typedef struct {
+ u32 bootcrcerr:1;
+ u32 bootackerr:1;
+ u32 bootdattmo:1;
+ u32 bootacktmo:1;
+ u32 bootupstate:1;
+ u32 bootackrcv:1;
+ u32 bootdatrcv:1;
+ u32 pad:25;
+} emmc_sts_reg;
+typedef struct {
+ u32 bootrst:1;
+ u32 pad:31;
+} emmc_iocon_reg;
+typedef struct {
+ u32 val;
+} msdc_acmd_resp_reg;
+typedef struct {
+ u32 tunesel:4;
+ u32 pad:28;
+} msdc_acmd19_trg_reg;
+typedef struct {
+ u32 val;
+} msdc_acmd19_sts_reg;
+typedef struct {
+ u32 addr;
+} msdc_dma_sa_reg;
+typedef struct {
+ u32 addr;
+} msdc_dma_ca_reg;
+typedef struct {
+ u32 start:1;
+ u32 stop:1;
+ u32 resume:1;
+ u32 pad1:5;
+ u32 mode:1;
+ u32 pad2:1;
+ u32 lastbuf:1;
+ u32 pad3:1;
+ u32 brustsz:3;
+ u32 pad4:1;
+ u32 xfersz:16;
+} msdc_dma_ctrl_reg;
+typedef struct {
+ u32 status:1;
+ u32 decsen:1;
+ u32 pad1:2;
+ u32 bdcsen:1;
+ u32 gpdcsen:1;
+ u32 pad2:26;
+} msdc_dma_cfg_reg;
+typedef struct {
+ u32 sel:16;
+ u32 pad2:16;
+} msdc_dbg_sel_reg;
+typedef struct {
+ u32 val;
+} msdc_dbg_out_reg;
+typedef struct {
+ u32 clkdrvn:3;
+ u32 rsv0:1;
+ u32 clkdrvp:3;
+ u32 rsv1:1;
+ u32 clksr:1;
+ u32 rsv2:7;
+ u32 clkpd:1;
+ u32 clkpu:1;
+ u32 clksmt:1;
+ u32 clkies:1;
+ u32 clktdsel:4;
+ u32 clkrdsel:8;
+} msdc_pad_ctl0_reg;
+typedef struct {
+ u32 cmddrvn:3;
+ u32 rsv0:1;
+ u32 cmddrvp:3;
+ u32 rsv1:1;
+ u32 cmdsr:1;
+ u32 rsv2:7;
+ u32 cmdpd:1;
+ u32 cmdpu:1;
+ u32 cmdsmt:1;
+ u32 cmdies:1;
+ u32 cmdtdsel:4;
+ u32 cmdrdsel:8;
+} msdc_pad_ctl1_reg;
+typedef struct {
+ u32 datdrvn:3;
+ u32 rsv0:1;
+ u32 datdrvp:3;
+ u32 rsv1:1;
+ u32 datsr:1;
+ u32 rsv2:7;
+ u32 datpd:1;
+ u32 datpu:1;
+ u32 datsmt:1;
+ u32 daties:1;
+ u32 dattdsel:4;
+ u32 datrdsel:8;
+} msdc_pad_ctl2_reg;
+typedef struct {
+ u32 wrrxdly:3;
+ u32 pad1:5;
+ u32 rdrxdly:8;
+ u32 pad2:16;
+} msdc_pad_tune_reg;
+typedef struct {
+ u32 dat0:5;
+ u32 rsv0:3;
+ u32 dat1:5;
+ u32 rsv1:3;
+ u32 dat2:5;
+ u32 rsv2:3;
+ u32 dat3:5;
+ u32 rsv3:3;
+} msdc_dat_rddly0;
+typedef struct {
+ u32 dat4:5;
+ u32 rsv4:3;
+ u32 dat5:5;
+ u32 rsv5:3;
+ u32 dat6:5;
+ u32 rsv6:3;
+ u32 dat7:5;
+ u32 rsv7:3;
+} msdc_dat_rddly1;
+typedef struct {
+ u32 dbg0sel:8;
+ u32 dbg1sel:6;
+ u32 pad1:2;
+ u32 dbg2sel:6;
+ u32 pad2:2;
+ u32 dbg3sel:6;
+ u32 pad3:2;
+} msdc_hw_dbg_reg;
+typedef struct {
+ u32 val;
+} msdc_version_reg;
+typedef struct {
+ u32 val;
+} msdc_eco_ver_reg;
+
+struct msdc_regs {
+ msdc_cfg_reg msdc_cfg; /* base+0x00h */
+ msdc_iocon_reg msdc_iocon; /* base+0x04h */
+ msdc_ps_reg msdc_ps; /* base+0x08h */
+ msdc_int_reg msdc_int; /* base+0x0ch */
+ msdc_inten_reg msdc_inten; /* base+0x10h */
+ msdc_fifocs_reg msdc_fifocs; /* base+0x14h */
+ msdc_txdat_reg msdc_txdat; /* base+0x18h */
+ msdc_rxdat_reg msdc_rxdat; /* base+0x1ch */
+ u32 rsv1[4];
+ sdc_cfg_reg sdc_cfg; /* base+0x30h */
+ sdc_cmd_reg sdc_cmd; /* base+0x34h */
+ sdc_arg_reg sdc_arg; /* base+0x38h */
+ sdc_sts_reg sdc_sts; /* base+0x3ch */
+ sdc_resp0_reg sdc_resp0; /* base+0x40h */
+ sdc_resp1_reg sdc_resp1; /* base+0x44h */
+ sdc_resp2_reg sdc_resp2; /* base+0x48h */
+ sdc_resp3_reg sdc_resp3; /* base+0x4ch */
+ sdc_blknum_reg sdc_blknum; /* base+0x50h */
+ u32 rsv2[1];
+ sdc_csts_reg sdc_csts; /* base+0x58h */
+ sdc_cstsen_reg sdc_cstsen; /* base+0x5ch */
+ sdc_datcrcsts_reg sdc_dcrcsta; /* base+0x60h */
+ u32 rsv3[3];
+ emmc_cfg0_reg emmc_cfg0; /* base+0x70h */
+ emmc_cfg1_reg emmc_cfg1; /* base+0x74h */
+ emmc_sts_reg emmc_sts; /* base+0x78h */
+ emmc_iocon_reg emmc_iocon; /* base+0x7ch */
+ msdc_acmd_resp_reg acmd_resp; /* base+0x80h */
+ msdc_acmd19_trg_reg acmd19_trg; /* base+0x84h */
+ msdc_acmd19_sts_reg acmd19_sts; /* base+0x88h */
+ u32 rsv4[1];
+ msdc_dma_sa_reg dma_sa; /* base+0x90h */
+ msdc_dma_ca_reg dma_ca; /* base+0x94h */
+ msdc_dma_ctrl_reg dma_ctrl; /* base+0x98h */
+ msdc_dma_cfg_reg dma_cfg; /* base+0x9ch */
+ msdc_dbg_sel_reg dbg_sel; /* base+0xa0h */
+ msdc_dbg_out_reg dbg_out; /* base+0xa4h */
+ u32 rsv5[2];
+ u32 patch0; /* base+0xb0h */
+ u32 patch1; /* base+0xb4h */
+ u32 rsv6[10];
+ msdc_pad_ctl0_reg pad_ctl0; /* base+0xe0h */
+ msdc_pad_ctl1_reg pad_ctl1; /* base+0xe4h */
+ msdc_pad_ctl2_reg pad_ctl2; /* base+0xe8h */
+ msdc_pad_tune_reg pad_tune; /* base+0xech */
+ msdc_dat_rddly0 dat_rddly0; /* base+0xf0h */
+ msdc_dat_rddly1 dat_rddly1; /* base+0xf4h */
+ msdc_hw_dbg_reg hw_dbg; /* base+0xf8h */
+ u32 rsv7[1];
+ msdc_version_reg version; /* base+0x100h */
+ msdc_eco_ver_reg eco_ver; /* base+0x104h */
+};
+
+struct scatterlist_ex {
+ u32 cmd;
+ u32 arg;
+ u32 sglen;
+ struct scatterlist *sg;
+};
+
+#define DMA_FLAG_NONE (0x00000000)
+#define DMA_FLAG_EN_CHKSUM (0x00000001)
+#define DMA_FLAG_PAD_BLOCK (0x00000002)
+#define DMA_FLAG_PAD_DWORD (0x00000004)
+
+struct msdc_dma {
+ u32 flags; /* flags */
+ u32 xfersz; /* xfer size in bytes */
+ u32 sglen; /* size of scatter list */
+ u32 blklen; /* block size */
+ struct scatterlist *sg; /* I/O scatter list */
+ struct scatterlist_ex *esg; /* extended I/O scatter list */
+ u8 mode; /* dma mode */
+ u8 burstsz; /* burst size */
+ u8 intr; /* dma done interrupt */
+ u8 padding; /* padding */
+ u32 cmd; /* enhanced mode command */
+ u32 arg; /* enhanced mode arg */
+ u32 rsp; /* enhanced mode command response */
+ u32 autorsp; /* auto command response */
+
+ gpd_t *gpd; /* pointer to gpd array */
+ bd_t *bd; /* pointer to bd array */
+ dma_addr_t gpd_addr; /* the physical address of gpd array */
+ dma_addr_t bd_addr; /* the physical address of bd array */
+ u32 used_gpd; /* the number of used gpd elements */
+ u32 used_bd; /* the number of used bd elements */
+};
+
+struct msdc_host
+{
+ struct msdc_hw *hw;
+
+ struct mmc_host *mmc; /* mmc structure */
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+ struct mmc_request *mrq;
+ int cmd_rsp;
+ int cmd_rsp_done;
+ int cmd_r1b_done;
+
+ int error;
+ spinlock_t lock; /* mutex */
+ struct semaphore sem;
+
+ u32 blksz; /* host block size */
+ u32 base; /* host base address */
+ int id; /* host id */
+ int pwr_ref; /* core power reference count */
+
+ u32 xfer_size; /* total transferred size */
+
+ struct msdc_dma dma; /* dma channel */
+ u32 dma_addr; /* dma transfer address */
+ u32 dma_left_size; /* dma transfer left size */
+ u32 dma_xfer_size; /* dma transfer size in bytes */
+ int dma_xfer; /* dma transfer mode */
+
+ u32 timeout_ns; /* data timeout ns */
+ u32 timeout_clks; /* data timeout clks */
+
+ atomic_t abort; /* abort transfer */
+
+ int irq; /* host interrupt */
+
+ struct tasklet_struct card_tasklet;
+#if 0
+ struct work_struct card_workqueue;
+#else
+ struct delayed_work card_delaywork;
+#endif
+
+ struct completion cmd_done;
+ struct completion xfer_done;
+ struct pm_message pm_state;
+
+ u32 mclk; /* mmc subsystem clock */
+ u32 hclk; /* host clock speed */
+ u32 sclk; /* SD/MS clock speed */
+ u8 core_clkon; /* Host core clock on ? */
+ u8 card_clkon; /* Card clock on ? */
+ u8 core_power; /* core power */
+ u8 power_mode; /* host power mode */
+ u8 card_inserted; /* card inserted ? */
+ u8 suspend; /* host suspended ? */
+ u8 reserved;
+ u8 app_cmd; /* for app command */
+ u32 app_cmd_arg;
+ u64 starttime;
+};
+
+static inline unsigned int uffs(unsigned int x)
+{
+ unsigned int r = 1;
+
+ if (!x)
+ return 0;
+ if (!(x & 0xffff)) {
+ x >>= 16;
+ r += 16;
+ }
+ if (!(x & 0xff)) {
+ x >>= 8;
+ r += 8;
+ }
+ if (!(x & 0xf)) {
+ x >>= 4;
+ r += 4;
+ }
+ if (!(x & 3)) {
+ x >>= 2;
+ r += 2;
+ }
+ if (!(x & 1)) {
+ x >>= 1;
+ r += 1;
+ }
+ return r;
+}
+#define sdr_read8(reg) __raw_readb(reg)
+#define sdr_read16(reg) __raw_readw(reg)
+#define sdr_read32(reg) __raw_readl(reg)
+#define sdr_write8(reg,val) __raw_writeb(val,reg)
+#define sdr_write16(reg,val) __raw_writew(val,reg)
+#define sdr_write32(reg,val) __raw_writel(val,reg)
+
+#define sdr_set_bits(reg,bs) ((*(volatile u32*)(reg)) |= (u32)(bs))
+#define sdr_clr_bits(reg,bs) ((*(volatile u32*)(reg)) &= ~((u32)(bs)))
+
+#define sdr_set_field(reg,field,val) \
+ do { \
+ volatile unsigned int tv = sdr_read32(reg); \
+ tv &= ~(field); \
+ tv |= ((val) << (uffs((unsigned int)field) - 1)); \
+ sdr_write32(reg,tv); \
+ } while(0)
+#define sdr_get_field(reg,field,val) \
+ do { \
+ volatile unsigned int tv = sdr_read32(reg); \
+ val = ((tv & (field)) >> (uffs((unsigned int)field) - 1)); \
+ } while(0)
+
+#endif
+
diff --git a/drivers/staging/mt7621-mmc/sd.c b/drivers/staging/mt7621-mmc/sd.c
new file mode 100644
index 000000000000..a1d0173eba56
--- /dev/null
+++ b/drivers/staging/mt7621-mmc/sd.c
@@ -0,0 +1,3074 @@
+/* Copyright Statement:
+ *
+ * This software/firmware and related documentation ("MediaTek Software") are
+ * protected under relevant copyright laws. The information contained herein
+ * is confidential and proprietary to MediaTek Inc. and/or its licensors.
+ * Without the prior written permission of MediaTek inc. and/or its licensors,
+ * any reproduction, modification, use or disclosure of MediaTek Software,
+ * and information contained herein, in whole or in part, shall be strictly prohibited.
+ *
+ * MediaTek Inc. (C) 2010. All rights reserved.
+ *
+ * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+ * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+ * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
+ * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+ * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
+ * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
+ * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
+ * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
+ * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
+ * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
+ * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+ * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
+ * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
+ * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
+ * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
+ * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+ *
+ * The following software/firmware and/or related documentation ("MediaTek Software")
+ * have been modified by MediaTek Inc. All revisions are subject to any receiver's
+ * applicable license agreements with MediaTek Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/blkdev.h>
+#include <linux/slab.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/core.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/sdio.h>
+#include <linux/dma-mapping.h>
+
+/* +++ by chhung */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/pm.h>
+#include <linux/of.h>
+
+#define MSDC_SMPL_FALLING (1)
+#define MSDC_CD_PIN_EN (1 << 0) /* card detection pin is wired */
+#define MSDC_WP_PIN_EN (1 << 1) /* write protection pin is wired */
+#define MSDC_REMOVABLE (1 << 5) /* removable slot */
+#define MSDC_SYS_SUSPEND (1 << 6) /* suspended by system */
+#define MSDC_HIGHSPEED (1 << 7)
+
+//#define IRQ_SDC 14 //MT7620 /*FIXME*/
+#ifdef CONFIG_SOC_MT7621
+#define RALINK_SYSCTL_BASE 0xbe000000
+#define RALINK_MSDC_BASE 0xbe130000
+#else
+#define RALINK_SYSCTL_BASE 0xb0000000
+#define RALINK_MSDC_BASE 0xb0130000
+#endif
+#define IRQ_SDC 22 /*FIXME*/
+
+#include <asm/dma.h>
+/* end of +++ */
+
+
+#include <asm/mach-ralink/ralink_regs.h>
+
+#if 0 /* --- by chhung */
+#include <mach/board.h>
+#include <mach/mt6575_devs.h>
+#include <mach/mt6575_typedefs.h>
+#include <mach/mt6575_clock_manager.h>
+#include <mach/mt6575_pm_ldo.h>
+//#include <mach/mt6575_pll.h>
+//#include <mach/mt6575_gpio.h>
+//#include <mach/mt6575_gpt_sw.h>
+#include <asm/tcm.h>
+// #include <mach/mt6575_gpt.h>
+#endif /* end of --- */
+
+#include "mt6575_sd.h"
+#include "dbg.h"
+
+/* +++ by chhung */
+#include "board.h"
+/* end of +++ */
+
+#if 0 /* --- by chhung */
+#define isb() __asm__ __volatile__ ("" : : : "memory")
+#define dsb() __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 4" \
+ : : "r" (0) : "memory")
+#define dmb() __asm__ __volatile__ ("" : : : "memory")
+#endif /* end of --- */
+
+#define DRV_NAME "mtk-sd"
+
+#define HOST_MAX_NUM (1) /* +/- by chhung */
+
+#if defined (CONFIG_SOC_MT7620)
+#define HOST_MAX_MCLK (48000000) /* +/- by chhung */
+#elif defined (CONFIG_SOC_MT7621)
+#define HOST_MAX_MCLK (50000000) /* +/- by chhung */
+#endif
+#define HOST_MIN_MCLK (260000)
+
+#define HOST_MAX_BLKSZ (2048)
+
+#define MSDC_OCR_AVAIL (MMC_VDD_28_29 | MMC_VDD_29_30 | MMC_VDD_30_31 | MMC_VDD_31_32 | MMC_VDD_32_33)
+
+#define GPIO_PULL_DOWN (0)
+#define GPIO_PULL_UP (1)
+
+#if 0 /* --- by chhung */
+#define MSDC_CLKSRC_REG (0xf100000C)
+#define PDN_REG (0xF1000010)
+#endif /* end of --- */
+
+#define DEFAULT_DEBOUNCE (8) /* 8 cycles */
+#define DEFAULT_DTOC (40) /* data timeout counter. 65536x40 sclk. */
+
+#define CMD_TIMEOUT (HZ/10) /* 100ms */
+#define DAT_TIMEOUT (HZ/2 * 5) /* 500ms x5 */
+
+#define MAX_DMA_CNT (64 * 1024 - 512) /* a single transaction for WIFI may be 50K*/
+
+#define MAX_GPD_NUM (1 + 1) /* one null gpd */
+#define MAX_BD_NUM (1024)
+#define MAX_BD_PER_GPD (MAX_BD_NUM)
+
+#define MAX_HW_SGMTS (MAX_BD_NUM)
+#define MAX_PHY_SGMTS (MAX_BD_NUM)
+#define MAX_SGMT_SZ (MAX_DMA_CNT)
+#define MAX_REQ_SZ (MAX_SGMT_SZ * 8)
+
+#ifdef MT6575_SD_DEBUG
+static struct msdc_regs *msdc_reg[HOST_MAX_NUM];
+#endif
+
+static int mtk_sw_poll;
+
+static int cd_active_low = 1;
+
+//=================================
+#define PERI_MSDC0_PDN (15)
+//#define PERI_MSDC1_PDN (16)
+//#define PERI_MSDC2_PDN (17)
+//#define PERI_MSDC3_PDN (18)
+
+struct msdc_host *msdc_6575_host[] = {NULL,NULL,NULL,NULL};
+#if 0 /* --- by chhung */
+/* gate means clock power down */
+static int g_clk_gate = 0;
+#define msdc_gate_clock(id) \
+ do { \
+ g_clk_gate &= ~(1 << ((id) + PERI_MSDC0_PDN)); \
+ } while(0)
+/* not like power down register. 1 means clock on. */
+#define msdc_ungate_clock(id) \
+ do { \
+ g_clk_gate |= 1 << ((id) + PERI_MSDC0_PDN); \
+ } while(0)
+
+// do we need sync object or not
+void msdc_clk_status(int * status)
+{
+ *status = g_clk_gate;
+}
+#endif /* end of --- */
+
+/* +++ by chhung */
+struct msdc_hw msdc0_hw = {
+ .clk_src = 0,
+ .cmd_edge = MSDC_SMPL_FALLING,
+ .data_edge = MSDC_SMPL_FALLING,
+ .clk_drv = 4,
+ .cmd_drv = 4,
+ .dat_drv = 4,
+ .data_pins = 4,
+ .data_offset = 0,
+ .flags = MSDC_SYS_SUSPEND | MSDC_CD_PIN_EN | MSDC_REMOVABLE | MSDC_HIGHSPEED,
+// .flags = MSDC_SYS_SUSPEND | MSDC_WP_PIN_EN | MSDC_CD_PIN_EN | MSDC_REMOVABLE,
+};
+
+static struct resource mtk_sd_resources[] = {
+ [0] = {
+ .start = RALINK_MSDC_BASE,
+ .end = RALINK_MSDC_BASE+0x3fff,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_SDC, /*FIXME*/
+ .end = IRQ_SDC, /*FIXME*/
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device mtk_sd_device = {
+ .name = "mtk-sd",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(mtk_sd_resources),
+ .resource = mtk_sd_resources,
+};
+/* end of +++ */
+
+static int msdc_rsp[] = {
+ 0, /* RESP_NONE */
+ 1, /* RESP_R1 */
+ 2, /* RESP_R2 */
+ 3, /* RESP_R3 */
+ 4, /* RESP_R4 */
+ 1, /* RESP_R5 */
+ 1, /* RESP_R6 */
+ 1, /* RESP_R7 */
+ 7, /* RESP_R1b */
+};
+
+/* For Inhanced DMA */
+#define msdc_init_gpd_ex(gpd,extlen,cmd,arg,blknum) \
+ do { \
+ ((gpd_t*)gpd)->extlen = extlen; \
+ ((gpd_t*)gpd)->cmd = cmd; \
+ ((gpd_t*)gpd)->arg = arg; \
+ ((gpd_t*)gpd)->blknum = blknum; \
+ }while(0)
+
+#define msdc_init_bd(bd, blkpad, dwpad, dptr, dlen) \
+ do { \
+ BUG_ON(dlen > 0xFFFFUL); \
+ ((bd_t*)bd)->blkpad = blkpad; \
+ ((bd_t*)bd)->dwpad = dwpad; \
+ ((bd_t*)bd)->ptr = (void*)dptr; \
+ ((bd_t*)bd)->buflen = dlen; \
+ }while(0)
+
+#define msdc_txfifocnt() ((sdr_read32(MSDC_FIFOCS) & MSDC_FIFOCS_TXCNT) >> 16)
+#define msdc_rxfifocnt() ((sdr_read32(MSDC_FIFOCS) & MSDC_FIFOCS_RXCNT) >> 0)
+#define msdc_fifo_write32(v) sdr_write32(MSDC_TXDATA, (v))
+#define msdc_fifo_write8(v) sdr_write8(MSDC_TXDATA, (v))
+#define msdc_fifo_read32() sdr_read32(MSDC_RXDATA)
+#define msdc_fifo_read8() sdr_read8(MSDC_RXDATA)
+
+
+#define msdc_dma_on() sdr_clr_bits(MSDC_CFG, MSDC_CFG_PIO)
+#define msdc_dma_off() sdr_set_bits(MSDC_CFG, MSDC_CFG_PIO)
+
+#define msdc_retry(expr,retry,cnt) \
+ do { \
+ int backup = cnt; \
+ while (retry) { \
+ if (!(expr)) break; \
+ if (cnt-- == 0) { \
+ retry--; mdelay(1); cnt = backup; \
+ } \
+ } \
+ WARN_ON(retry == 0); \
+ } while(0)
+
+#if 0 /* --- by chhung */
+#define msdc_reset() \
+ do { \
+ int retry = 3, cnt = 1000; \
+ sdr_set_bits(MSDC_CFG, MSDC_CFG_RST); \
+ dsb(); \
+ msdc_retry(sdr_read32(MSDC_CFG) & MSDC_CFG_RST, retry, cnt); \
+ } while(0)
+#else
+#define msdc_reset() \
+ do { \
+ int retry = 3, cnt = 1000; \
+ sdr_set_bits(MSDC_CFG, MSDC_CFG_RST); \
+ msdc_retry(sdr_read32(MSDC_CFG) & MSDC_CFG_RST, retry, cnt); \
+ } while(0)
+#endif /* end of +/- */
+
+#define msdc_clr_int() \
+ do { \
+ volatile u32 val = sdr_read32(MSDC_INT); \
+ sdr_write32(MSDC_INT, val); \
+ } while(0)
+
+#define msdc_clr_fifo() \
+ do { \
+ int retry = 3, cnt = 1000; \
+ sdr_set_bits(MSDC_FIFOCS, MSDC_FIFOCS_CLR); \
+ msdc_retry(sdr_read32(MSDC_FIFOCS) & MSDC_FIFOCS_CLR, retry, cnt); \
+ } while(0)
+
+#define msdc_irq_save(val) \
+ do { \
+ val = sdr_read32(MSDC_INTEN); \
+ sdr_clr_bits(MSDC_INTEN, val); \
+ } while(0)
+
+#define msdc_irq_restore(val) \
+ do { \
+ sdr_set_bits(MSDC_INTEN, val); \
+ } while(0)
+
+/* clock source for host: global */
+#if defined (CONFIG_SOC_MT7620)
+static u32 hclks[] = {48000000}; /* +/- by chhung */
+#elif defined (CONFIG_SOC_MT7621)
+static u32 hclks[] = {50000000}; /* +/- by chhung */
+#endif
+
+//============================================
+// the power for msdc host controller: global
+// always keep the VMC on.
+//============================================
+#define msdc_vcore_on(host) \
+ do { \
+ INIT_MSG("[+]VMC ref. count<%d>", ++host->pwr_ref); \
+ (void)hwPowerOn(MT65XX_POWER_LDO_VMC, VOL_3300, "SD"); \
+ } while (0)
+#define msdc_vcore_off(host) \
+ do { \
+ INIT_MSG("[-]VMC ref. count<%d>", --host->pwr_ref); \
+ (void)hwPowerDown(MT65XX_POWER_LDO_VMC, "SD"); \
+ } while (0)
+
+//====================================
+// the vdd output for card: global
+// always keep the VMCH on.
+//====================================
+#define msdc_vdd_on(host) \
+ do { \
+ (void)hwPowerOn(MT65XX_POWER_LDO_VMCH, VOL_3300, "SD"); \
+ } while (0)
+#define msdc_vdd_off(host) \
+ do { \
+ (void)hwPowerDown(MT65XX_POWER_LDO_VMCH, "SD"); \
+ } while (0)
+
+#define sdc_is_busy() (sdr_read32(SDC_STS) & SDC_STS_SDCBUSY)
+#define sdc_is_cmd_busy() (sdr_read32(SDC_STS) & SDC_STS_CMDBUSY)
+
+#define sdc_send_cmd(cmd,arg) \
+ do { \
+ sdr_write32(SDC_ARG, (arg)); \
+ sdr_write32(SDC_CMD, (cmd)); \
+ } while(0)
+
+// can modify to read h/w register.
+//#define is_card_present(h) ((sdr_read32(MSDC_PS) & MSDC_PS_CDSTS) ? 0 : 1);
+#define is_card_present(h) (((struct msdc_host*)(h))->card_inserted)
+
+/* +++ by chhung */
+#ifndef __ASSEMBLY__
+#define PHYSADDR(a) (((unsigned long)(a)) & 0x1fffffff)
+#else
+#define PHYSADDR(a) ((a) & 0x1fffffff)
+#endif
+/* end of +++ */
+static unsigned int msdc_do_command(struct msdc_host *host,
+ struct mmc_command *cmd,
+ int tune,
+ unsigned long timeout);
+
+static int msdc_tune_cmdrsp(struct msdc_host*host,struct mmc_command *cmd);
+
+#ifdef MT6575_SD_DEBUG
+static void msdc_dump_card_status(struct msdc_host *host, u32 status)
+{
+/* N_MSG is currently a no-op */
+#if 0
+ static char *state[] = {
+ "Idle", /* 0 */
+ "Ready", /* 1 */
+ "Ident", /* 2 */
+ "Stby", /* 3 */
+ "Tran", /* 4 */
+ "Data", /* 5 */
+ "Rcv", /* 6 */
+ "Prg", /* 7 */
+ "Dis", /* 8 */
+ "Reserved", /* 9 */
+ "Reserved", /* 10 */
+ "Reserved", /* 11 */
+ "Reserved", /* 12 */
+ "Reserved", /* 13 */
+ "Reserved", /* 14 */
+ "I/O mode", /* 15 */
+ };
+#endif
+ if (status & R1_OUT_OF_RANGE)
+ N_MSG(RSP, "[CARD_STATUS] Out of Range");
+ if (status & R1_ADDRESS_ERROR)
+ N_MSG(RSP, "[CARD_STATUS] Address Error");
+ if (status & R1_BLOCK_LEN_ERROR)
+ N_MSG(RSP, "[CARD_STATUS] Block Len Error");
+ if (status & R1_ERASE_SEQ_ERROR)
+ N_MSG(RSP, "[CARD_STATUS] Erase Seq Error");
+ if (status & R1_ERASE_PARAM)
+ N_MSG(RSP, "[CARD_STATUS] Erase Param");
+ if (status & R1_WP_VIOLATION)
+ N_MSG(RSP, "[CARD_STATUS] WP Violation");
+ if (status & R1_CARD_IS_LOCKED)
+ N_MSG(RSP, "[CARD_STATUS] Card is Locked");
+ if (status & R1_LOCK_UNLOCK_FAILED)
+ N_MSG(RSP, "[CARD_STATUS] Lock/Unlock Failed");
+ if (status & R1_COM_CRC_ERROR)
+ N_MSG(RSP, "[CARD_STATUS] Command CRC Error");
+ if (status & R1_ILLEGAL_COMMAND)
+ N_MSG(RSP, "[CARD_STATUS] Illegal Command");
+ if (status & R1_CARD_ECC_FAILED)
+ N_MSG(RSP, "[CARD_STATUS] Card ECC Failed");
+ if (status & R1_CC_ERROR)
+ N_MSG(RSP, "[CARD_STATUS] CC Error");
+ if (status & R1_ERROR)
+ N_MSG(RSP, "[CARD_STATUS] Error");
+ if (status & R1_UNDERRUN)
+ N_MSG(RSP, "[CARD_STATUS] Underrun");
+ if (status & R1_OVERRUN)
+ N_MSG(RSP, "[CARD_STATUS] Overrun");
+ if (status & R1_CID_CSD_OVERWRITE)
+ N_MSG(RSP, "[CARD_STATUS] CID/CSD Overwrite");
+ if (status & R1_WP_ERASE_SKIP)
+ N_MSG(RSP, "[CARD_STATUS] WP Eraser Skip");
+ if (status & R1_CARD_ECC_DISABLED)
+ N_MSG(RSP, "[CARD_STATUS] Card ECC Disabled");
+ if (status & R1_ERASE_RESET)
+ N_MSG(RSP, "[CARD_STATUS] Erase Reset");
+ if (status & R1_READY_FOR_DATA)
+ N_MSG(RSP, "[CARD_STATUS] Ready for Data");
+ if (status & R1_SWITCH_ERROR)
+ N_MSG(RSP, "[CARD_STATUS] Switch error");
+ if (status & R1_APP_CMD)
+ N_MSG(RSP, "[CARD_STATUS] App Command");
+
+ N_MSG(RSP, "[CARD_STATUS] '%s' State", state[R1_CURRENT_STATE(status)]);
+}
+
+static void msdc_dump_ocr_reg(struct msdc_host *host, u32 resp)
+{
+ if (resp & (1 << 7))
+ N_MSG(RSP, "[OCR] Low Voltage Range");
+ if (resp & (1 << 15))
+ N_MSG(RSP, "[OCR] 2.7-2.8 volt");
+ if (resp & (1 << 16))
+ N_MSG(RSP, "[OCR] 2.8-2.9 volt");
+ if (resp & (1 << 17))
+ N_MSG(RSP, "[OCR] 2.9-3.0 volt");
+ if (resp & (1 << 18))
+ N_MSG(RSP, "[OCR] 3.0-3.1 volt");
+ if (resp & (1 << 19))
+ N_MSG(RSP, "[OCR] 3.1-3.2 volt");
+ if (resp & (1 << 20))
+ N_MSG(RSP, "[OCR] 3.2-3.3 volt");
+ if (resp & (1 << 21))
+ N_MSG(RSP, "[OCR] 3.3-3.4 volt");
+ if (resp & (1 << 22))
+ N_MSG(RSP, "[OCR] 3.4-3.5 volt");
+ if (resp & (1 << 23))
+ N_MSG(RSP, "[OCR] 3.5-3.6 volt");
+ if (resp & (1 << 24))
+ N_MSG(RSP, "[OCR] Switching to 1.8V Accepted (S18A)");
+ if (resp & (1 << 30))
+ N_MSG(RSP, "[OCR] Card Capacity Status (CCS)");
+ if (resp & (1 << 31))
+ N_MSG(RSP, "[OCR] Card Power Up Status (Idle)");
+ else
+ N_MSG(RSP, "[OCR] Card Power Up Status (Busy)");
+}
+
+static void msdc_dump_rca_resp(struct msdc_host *host, u32 resp)
+{
+ u32 status = (((resp >> 15) & 0x1) << 23) |
+ (((resp >> 14) & 0x1) << 22) |
+ (((resp >> 13) & 0x1) << 19) |
+ (resp & 0x1fff);
+
+ N_MSG(RSP, "[RCA] 0x%.4x", resp >> 16);
+ msdc_dump_card_status(host, status);
+}
+
+static void msdc_dump_io_resp(struct msdc_host *host, u32 resp)
+{
+ u32 flags = (resp >> 8) & 0xFF;
+#if 0
+ char *state[] = {"DIS", "CMD", "TRN", "RFU"};
+#endif
+ if (flags & (1 << 7))
+ N_MSG(RSP, "[IO] COM_CRC_ERR");
+ if (flags & (1 << 6))
+ N_MSG(RSP, "[IO] Illgal command");
+ if (flags & (1 << 3))
+ N_MSG(RSP, "[IO] Error");
+ if (flags & (1 << 2))
+ N_MSG(RSP, "[IO] RFU");
+ if (flags & (1 << 1))
+ N_MSG(RSP, "[IO] Function number error");
+ if (flags & (1 << 0))
+ N_MSG(RSP, "[IO] Out of range");
+
+ N_MSG(RSP, "[IO] State: %s, Data:0x%x", state[(resp >> 12) & 0x3], resp & 0xFF);
+}
+#endif
+
+static void msdc_set_timeout(struct msdc_host *host, u32 ns, u32 clks)
+{
+ u32 base = host->base;
+ u32 timeout, clk_ns;
+
+ host->timeout_ns = ns;
+ host->timeout_clks = clks;
+
+ clk_ns = 1000000000UL / host->sclk;
+ timeout = ns / clk_ns + clks;
+ timeout = timeout >> 16; /* in 65536 sclk cycle unit */
+ timeout = timeout > 1 ? timeout - 1 : 0;
+ timeout = timeout > 255 ? 255 : timeout;
+
+ sdr_set_field(SDC_CFG, SDC_CFG_DTOC, timeout);
+
+ N_MSG(OPS, "Set read data timeout: %dns %dclks -> %d x 65536 cycles",
+ ns, clks, timeout + 1);
+}
+
+/* msdc_eirq_sdio() will be called when EIRQ(for WIFI) */
+static void msdc_eirq_sdio(void *data)
+{
+ struct msdc_host *host = (struct msdc_host *)data;
+
+ N_MSG(INT, "SDIO EINT");
+
+ mmc_signal_sdio_irq(host->mmc);
+}
+
+/* msdc_eirq_cd will not be used! We not using EINT for card detection. */
+static void msdc_eirq_cd(void *data)
+{
+ struct msdc_host *host = (struct msdc_host *)data;
+
+ N_MSG(INT, "CD EINT");
+
+#if 0
+ tasklet_hi_schedule(&host->card_tasklet);
+#else
+ schedule_delayed_work(&host->card_delaywork, HZ);
+#endif
+}
+
+#if 0
+static void msdc_tasklet_card(unsigned long arg)
+{
+ struct msdc_host *host = (struct msdc_host *)arg;
+#else
+static void msdc_tasklet_card(struct work_struct *work)
+{
+ struct msdc_host *host = (struct msdc_host *)container_of(work,
+ struct msdc_host, card_delaywork.work);
+#endif
+ struct msdc_hw *hw = host->hw;
+ u32 base = host->base;
+ u32 inserted;
+ u32 status = 0;
+ //u32 change = 0;
+
+ spin_lock(&host->lock);
+
+ if (hw->get_cd_status) { // NULL
+ inserted = hw->get_cd_status();
+ } else {
+ status = sdr_read32(MSDC_PS);
+ if (cd_active_low)
+ inserted = (status & MSDC_PS_CDSTS) ? 0 : 1;
+ else
+ inserted = (status & MSDC_PS_CDSTS) ? 1 : 0;
+ }
+
+#if 0
+ change = host->card_inserted ^ inserted;
+ host->card_inserted = inserted;
+
+ if (change && !host->suspend) {
+ if (inserted) {
+ host->mmc->f_max = HOST_MAX_MCLK; // work around
+ }
+ mmc_detect_change(host->mmc, msecs_to_jiffies(20));
+ }
+#else /* Make sure: handle the last interrupt */
+ host->card_inserted = inserted;
+
+ if (!host->suspend) {
+ host->mmc->f_max = HOST_MAX_MCLK;
+ mmc_detect_change(host->mmc, msecs_to_jiffies(20));
+ }
+
+ IRQ_MSG("card found<%s>", inserted ? "inserted" : "removed");
+#endif
+
+ spin_unlock(&host->lock);
+}
+
+#if 0 /* --- by chhung */
+/* For E2 only */
+static u8 clk_src_bit[4] = {
+ 0, 3, 5, 7
+};
+
+static void msdc_select_clksrc(struct msdc_host* host, unsigned char clksrc)
+{
+ u32 val;
+ u32 base = host->base;
+
+ BUG_ON(clksrc > 3);
+ INIT_MSG("set clock source to <%d>", clksrc);
+
+ val = sdr_read32(MSDC_CLKSRC_REG);
+ if (sdr_read32(MSDC_ECO_VER) >= 4) {
+ val &= ~(0x3 << clk_src_bit[host->id]);
+ val |= clksrc << clk_src_bit[host->id];
+ } else {
+ val &= ~0x3; val |= clksrc;
+ }
+ sdr_write32(MSDC_CLKSRC_REG, val);
+
+ host->hclk = hclks[clksrc];
+ host->hw->clk_src = clksrc;
+}
+#endif /* end of --- */
+
+static void msdc_set_mclk(struct msdc_host *host, int ddr, unsigned int hz)
+{
+ //struct msdc_hw *hw = host->hw;
+ u32 base = host->base;
+ u32 mode;
+ u32 flags;
+ u32 div;
+ u32 sclk;
+ u32 hclk = host->hclk;
+ //u8 clksrc = hw->clk_src;
+
+ if (!hz) { // set mmc system clock to 0 ?
+ //ERR_MSG("set mclk to 0!!!");
+ msdc_reset();
+ return;
+ }
+
+ msdc_irq_save(flags);
+
+#if defined (CONFIG_MT7621_FPGA) || defined (CONFIG_MT7628_FPGA)
+ mode = 0x0; /* use divisor */
+ if (hz >= (hclk >> 1)) {
+ div = 0; /* mean div = 1/2 */
+ sclk = hclk >> 1; /* sclk = clk / 2 */
+ } else {
+ div = (hclk + ((hz << 2) - 1)) / (hz << 2);
+ sclk = (hclk >> 2) / div;
+ }
+#else
+ if (ddr) {
+ mode = 0x2; /* ddr mode and use divisor */
+ if (hz >= (hclk >> 2)) {
+ div = 1; /* mean div = 1/4 */
+ sclk = hclk >> 2; /* sclk = clk / 4 */
+ } else {
+ div = (hclk + ((hz << 2) - 1)) / (hz << 2);
+ sclk = (hclk >> 2) / div;
+ }
+ } else if (hz >= hclk) { /* bug fix */
+ mode = 0x1; /* no divisor and divisor is ignored */
+ div = 0;
+ sclk = hclk;
+ } else {
+ mode = 0x0; /* use divisor */
+ if (hz >= (hclk >> 1)) {
+ div = 0; /* mean div = 1/2 */
+ sclk = hclk >> 1; /* sclk = clk / 2 */
+ } else {
+ div = (hclk + ((hz << 2) - 1)) / (hz << 2);
+ sclk = (hclk >> 2) / div;
+ }
+ }
+#endif
+ /* set clock mode and divisor */
+ sdr_set_field(MSDC_CFG, MSDC_CFG_CKMOD, mode);
+ sdr_set_field(MSDC_CFG, MSDC_CFG_CKDIV, div);
+
+ /* wait clock stable */
+ while (!(sdr_read32(MSDC_CFG) & MSDC_CFG_CKSTB));
+
+ host->sclk = sclk;
+ host->mclk = hz;
+ msdc_set_timeout(host, host->timeout_ns, host->timeout_clks); // need?
+
+ INIT_MSG("================");
+ INIT_MSG("!!! Set<%dKHz> Source<%dKHz> -> sclk<%dKHz>", hz/1000, hclk/1000, sclk/1000);
+ INIT_MSG("================");
+
+ msdc_irq_restore(flags);
+}
+
+/* Fix me. when need to abort */
+static void msdc_abort_data(struct msdc_host *host)
+{
+ u32 base = host->base;
+ struct mmc_command *stop = host->mrq->stop;
+
+ ERR_MSG("Need to Abort. dma<%d>", host->dma_xfer);
+
+ msdc_reset();
+ msdc_clr_fifo();
+ msdc_clr_int();
+
+ // need to check FIFO count 0 ?
+
+ if (stop) { /* try to stop, but may not success */
+ ERR_MSG("stop when abort CMD<%d>", stop->opcode);
+ (void)msdc_do_command(host, stop, 0, CMD_TIMEOUT);
+ }
+
+ //if (host->mclk >= 25000000) {
+ // msdc_set_mclk(host, 0, host->mclk >> 1);
+ //}
+}
+
+#if 0 /* --- by chhung */
+static void msdc_pin_config(struct msdc_host *host, int mode)
+{
+ struct msdc_hw *hw = host->hw;
+ u32 base = host->base;
+ int pull = (mode == MSDC_PIN_PULL_UP) ? GPIO_PULL_UP : GPIO_PULL_DOWN;
+
+ /* Config WP pin */
+ if (hw->flags & MSDC_WP_PIN_EN) {
+ if (hw->config_gpio_pin) /* NULL */
+ hw->config_gpio_pin(MSDC_WP_PIN, pull);
+ }
+
+ switch (mode) {
+ case MSDC_PIN_PULL_UP:
+ //sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKPU, 1); /* Check & FIXME */
+ //sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKPD, 0); /* Check & FIXME */
+ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDPU, 1);
+ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDPD, 0);
+ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATPU, 1);
+ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATPD, 0);
+ break;
+ case MSDC_PIN_PULL_DOWN:
+ //sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKPU, 0); /* Check & FIXME */
+ //sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKPD, 1); /* Check & FIXME */
+ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDPU, 0);
+ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDPD, 1);
+ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATPU, 0);
+ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATPD, 1);
+ break;
+ case MSDC_PIN_PULL_NONE:
+ default:
+ //sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKPU, 0); /* Check & FIXME */
+ //sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKPD, 0); /* Check & FIXME */
+ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDPU, 0);
+ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDPD, 0);
+ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATPU, 0);
+ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATPD, 0);
+ break;
+ }
+
+ N_MSG(CFG, "Pins mode(%d), down(%d), up(%d)",
+ mode, MSDC_PIN_PULL_DOWN, MSDC_PIN_PULL_UP);
+}
+
+void msdc_pin_reset(struct msdc_host *host, int mode)
+{
+ struct msdc_hw *hw = (struct msdc_hw *)host->hw;
+ u32 base = host->base;
+ int pull = (mode == MSDC_PIN_PULL_UP) ? GPIO_PULL_UP : GPIO_PULL_DOWN;
+
+ /* Config reset pin */
+ if (hw->flags & MSDC_RST_PIN_EN) {
+ if (hw->config_gpio_pin) /* NULL */
+ hw->config_gpio_pin(MSDC_RST_PIN, pull);
+
+ if (mode == MSDC_PIN_PULL_UP) {
+ sdr_clr_bits(EMMC_IOCON, EMMC_IOCON_BOOTRST);
+ } else {
+ sdr_set_bits(EMMC_IOCON, EMMC_IOCON_BOOTRST);
+ }
+ }
+}
+
+static void msdc_core_power(struct msdc_host *host, int on)
+{
+ N_MSG(CFG, "Turn %s %s power (copower: %d -> %d)",
+ on ? "on" : "off", "core", host->core_power, on);
+
+ if (on && host->core_power == 0) {
+ msdc_vcore_on(host);
+ host->core_power = 1;
+ msleep(1);
+ } else if (!on && host->core_power == 1) {
+ msdc_vcore_off(host);
+ host->core_power = 0;
+ msleep(1);
+ }
+}
+
+static void msdc_host_power(struct msdc_host *host, int on)
+{
+ N_MSG(CFG, "Turn %s %s power ", on ? "on" : "off", "host");
+
+ if (on) {
+ //msdc_core_power(host, 1); // need do card detection.
+ msdc_pin_reset(host, MSDC_PIN_PULL_UP);
+ } else {
+ msdc_pin_reset(host, MSDC_PIN_PULL_DOWN);
+ //msdc_core_power(host, 0);
+ }
+}
+
+static void msdc_card_power(struct msdc_host *host, int on)
+{
+ N_MSG(CFG, "Turn %s %s power ", on ? "on" : "off", "card");
+
+ if (on) {
+ msdc_pin_config(host, MSDC_PIN_PULL_UP);
+ if (host->hw->ext_power_on) {
+ host->hw->ext_power_on();
+ } else {
+ //msdc_vdd_on(host); // need todo card detection.
+ }
+ msleep(1);
+ } else {
+ if (host->hw->ext_power_off) {
+ host->hw->ext_power_off();
+ } else {
+ //msdc_vdd_off(host);
+ }
+ msdc_pin_config(host, MSDC_PIN_PULL_DOWN);
+ msleep(1);
+ }
+}
+
+static void msdc_set_power_mode(struct msdc_host *host, u8 mode)
+{
+ N_MSG(CFG, "Set power mode(%d)", mode);
+
+ if (host->power_mode == MMC_POWER_OFF && mode != MMC_POWER_OFF) {
+ msdc_host_power(host, 1);
+ msdc_card_power(host, 1);
+ } else if (host->power_mode != MMC_POWER_OFF && mode == MMC_POWER_OFF) {
+ msdc_card_power(host, 0);
+ msdc_host_power(host, 0);
+ }
+ host->power_mode = mode;
+}
+#endif /* end of --- */
+
+#ifdef CONFIG_PM
+/*
+ register as callback function of WIFI(combo_sdio_register_pm) .
+ can called by msdc_drv_suspend/resume too.
+*/
+static void msdc_pm(pm_message_t state, void *data)
+{
+ struct msdc_host *host = (struct msdc_host *)data;
+ int evt = state.event;
+
+ if (evt == PM_EVENT_USER_RESUME || evt == PM_EVENT_USER_SUSPEND) {
+ INIT_MSG("USR_%s: suspend<%d> power<%d>",
+ evt == PM_EVENT_USER_RESUME ? "EVENT_USER_RESUME" : "EVENT_USER_SUSPEND",
+ host->suspend, host->power_mode);
+ }
+
+ if (evt == PM_EVENT_SUSPEND || evt == PM_EVENT_USER_SUSPEND) {
+ if (host->suspend) /* already suspend */ /* default 0*/
+ return;
+
+ /* for memory card. already power off by mmc */
+ if (evt == PM_EVENT_SUSPEND && host->power_mode == MMC_POWER_OFF)
+ return;
+
+ host->suspend = 1;
+ host->pm_state = state; /* default PMSG_RESUME */
+
+ INIT_MSG("%s Suspend", evt == PM_EVENT_SUSPEND ? "PM" : "USR");
+ if(host->hw->flags & MSDC_SYS_SUSPEND) /* set for card */
+ (void)mmc_suspend_host(host->mmc);
+ else {
+ // host->mmc->pm_flags |= MMC_PM_IGNORE_PM_NOTIFY; /* just for double confirm */ /* --- by chhung */
+ mmc_remove_host(host->mmc);
+ }
+ } else if (evt == PM_EVENT_RESUME || evt == PM_EVENT_USER_RESUME) {
+ if (!host->suspend){
+ //ERR_MSG("warning: already resume");
+ return;
+ }
+
+ /* No PM resume when USR suspend */
+ if (evt == PM_EVENT_RESUME && host->pm_state.event == PM_EVENT_USER_SUSPEND) {
+ ERR_MSG("PM Resume when in USR Suspend"); /* won't happen. */
+ return;
+ }
+
+ host->suspend = 0;
+ host->pm_state = state;
+
+ INIT_MSG("%s Resume", evt == PM_EVENT_RESUME ? "PM" : "USR");
+ if(host->hw->flags & MSDC_SYS_SUSPEND) { /* will not set for WIFI */
+ (void)mmc_resume_host(host->mmc);
+ }
+ else {
+ // host->mmc->pm_flags |= MMC_PM_IGNORE_PM_NOTIFY; /* --- by chhung */
+ mmc_add_host(host->mmc);
+ }
+ }
+}
+#endif
+
+/*--------------------------------------------------------------------------*/
+/* mmc_host_ops members */
+/*--------------------------------------------------------------------------*/
+static unsigned int msdc_command_start(struct msdc_host *host,
+ struct mmc_command *cmd,
+ int tune, /* not used */
+ unsigned long timeout)
+{
+ u32 base = host->base;
+ u32 opcode = cmd->opcode;
+ u32 rawcmd;
+ u32 wints = MSDC_INT_CMDRDY | MSDC_INT_RSPCRCERR | MSDC_INT_CMDTMO |
+ MSDC_INT_ACMDRDY | MSDC_INT_ACMDCRCERR | MSDC_INT_ACMDTMO |
+ MSDC_INT_ACMD19_DONE;
+
+ u32 resp;
+ unsigned long tmo;
+
+ /* Protocol layer does not provide response type, but our hardware needs
+ * to know exact type, not just size!
+ */
+ if (opcode == MMC_SEND_OP_COND || opcode == SD_APP_OP_COND)
+ resp = RESP_R3;
+ else if (opcode == MMC_SET_RELATIVE_ADDR || opcode == SD_SEND_RELATIVE_ADDR)
+ resp = (mmc_cmd_type(cmd) == MMC_CMD_BCR) ? RESP_R6 : RESP_R1;
+ else if (opcode == MMC_FAST_IO)
+ resp = RESP_R4;
+ else if (opcode == MMC_GO_IRQ_STATE)
+ resp = RESP_R5;
+ else if (opcode == MMC_SELECT_CARD)
+ resp = (cmd->arg != 0) ? RESP_R1B : RESP_NONE;
+ else if (opcode == SD_IO_RW_DIRECT || opcode == SD_IO_RW_EXTENDED)
+ resp = RESP_R1; /* SDIO workaround. */
+ else if (opcode == SD_SEND_IF_COND && (mmc_cmd_type(cmd) == MMC_CMD_BCR))
+ resp = RESP_R1;
+ else {
+ switch (mmc_resp_type(cmd)) {
+ case MMC_RSP_R1:
+ resp = RESP_R1;
+ break;
+ case MMC_RSP_R1B:
+ resp = RESP_R1B;
+ break;
+ case MMC_RSP_R2:
+ resp = RESP_R2;
+ break;
+ case MMC_RSP_R3:
+ resp = RESP_R3;
+ break;
+ case MMC_RSP_NONE:
+ default:
+ resp = RESP_NONE;
+ break;
+ }
+ }
+
+ cmd->error = 0;
+ /* rawcmd :
+ * vol_swt << 30 | auto_cmd << 28 | blklen << 16 | go_irq << 15 |
+ * stop << 14 | rw << 13 | dtype << 11 | rsptyp << 7 | brk << 6 | opcode
+ */
+ rawcmd = opcode | msdc_rsp[resp] << 7 | host->blksz << 16;
+
+ if (opcode == MMC_READ_MULTIPLE_BLOCK) {
+ rawcmd |= (2 << 11);
+ } else if (opcode == MMC_READ_SINGLE_BLOCK) {
+ rawcmd |= (1 << 11);
+ } else if (opcode == MMC_WRITE_MULTIPLE_BLOCK) {
+ rawcmd |= ((2 << 11) | (1 << 13));
+ } else if (opcode == MMC_WRITE_BLOCK) {
+ rawcmd |= ((1 << 11) | (1 << 13));
+ } else if (opcode == SD_IO_RW_EXTENDED) {
+ if (cmd->data->flags & MMC_DATA_WRITE)
+ rawcmd |= (1 << 13);
+ if (cmd->data->blocks > 1)
+ rawcmd |= (2 << 11);
+ else
+ rawcmd |= (1 << 11);
+ } else if (opcode == SD_IO_RW_DIRECT && cmd->flags == (unsigned int)-1) {
+ rawcmd |= (1 << 14);
+ } else if ((opcode == SD_APP_SEND_SCR) ||
+ (opcode == SD_APP_SEND_NUM_WR_BLKS) ||
+ (opcode == SD_SWITCH && (mmc_cmd_type(cmd) == MMC_CMD_ADTC)) ||
+ (opcode == SD_APP_SD_STATUS && (mmc_cmd_type(cmd) == MMC_CMD_ADTC)) ||
+ (opcode == MMC_SEND_EXT_CSD && (mmc_cmd_type(cmd) == MMC_CMD_ADTC))) {
+ rawcmd |= (1 << 11);
+ } else if (opcode == MMC_STOP_TRANSMISSION) {
+ rawcmd |= (1 << 14);
+ rawcmd &= ~(0x0FFF << 16);
+ }
+
+ N_MSG(CMD, "CMD<%d><0x%.8x> Arg<0x%.8x>", opcode , rawcmd, cmd->arg);
+
+ tmo = jiffies + timeout;
+
+ if (opcode == MMC_SEND_STATUS) {
+ for (;;) {
+ if (!sdc_is_cmd_busy())
+ break;
+
+ if (time_after(jiffies, tmo)) {
+ ERR_MSG("XXX cmd_busy timeout: before CMD<%d>", opcode);
+ cmd->error = (unsigned int)-ETIMEDOUT;
+ msdc_reset();
+ goto end;
+ }
+ }
+ }else {
+ for (;;) {
+ if (!sdc_is_busy())
+ break;
+ if (time_after(jiffies, tmo)) {
+ ERR_MSG("XXX sdc_busy timeout: before CMD<%d>", opcode);
+ cmd->error = (unsigned int)-ETIMEDOUT;
+ msdc_reset();
+ goto end;
+ }
+ }
+ }
+
+ //BUG_ON(in_interrupt());
+ host->cmd = cmd;
+ host->cmd_rsp = resp;
+
+ init_completion(&host->cmd_done);
+
+ sdr_set_bits(MSDC_INTEN, wints);
+ sdc_send_cmd(rawcmd, cmd->arg);
+
+end:
+ return cmd->error;
+}
+
+static unsigned int msdc_command_resp(struct msdc_host *host,
+ struct mmc_command *cmd,
+ int tune,
+ unsigned long timeout)
+{
+ u32 base = host->base;
+ u32 opcode = cmd->opcode;
+ //u32 rawcmd;
+ u32 resp;
+ u32 wints = MSDC_INT_CMDRDY | MSDC_INT_RSPCRCERR | MSDC_INT_CMDTMO |
+ MSDC_INT_ACMDRDY | MSDC_INT_ACMDCRCERR | MSDC_INT_ACMDTMO |
+ MSDC_INT_ACMD19_DONE;
+
+ resp = host->cmd_rsp;
+
+ BUG_ON(in_interrupt());
+ //init_completion(&host->cmd_done);
+ //sdr_set_bits(MSDC_INTEN, wints);
+
+ spin_unlock(&host->lock);
+ if(!wait_for_completion_timeout(&host->cmd_done, 10*timeout)){
+ ERR_MSG("XXX CMD<%d> wait_for_completion timeout ARG<0x%.8x>", opcode, cmd->arg);
+ cmd->error = (unsigned int)-ETIMEDOUT;
+ msdc_reset();
+ }
+ spin_lock(&host->lock);
+
+ sdr_clr_bits(MSDC_INTEN, wints);
+ host->cmd = NULL;
+
+//end:
+#ifdef MT6575_SD_DEBUG
+ switch (resp) {
+ case RESP_NONE:
+ N_MSG(RSP, "CMD_RSP(%d): %d RSP(%d)", opcode, cmd->error, resp);
+ break;
+ case RESP_R2:
+ N_MSG(RSP, "CMD_RSP(%d): %d RSP(%d)= %.8x %.8x %.8x %.8x",
+ opcode, cmd->error, resp, cmd->resp[0], cmd->resp[1],
+ cmd->resp[2], cmd->resp[3]);
+ break;
+ default: /* Response types 1, 3, 4, 5, 6, 7(1b) */
+ N_MSG(RSP, "CMD_RSP(%d): %d RSP(%d)= 0x%.8x",
+ opcode, cmd->error, resp, cmd->resp[0]);
+ if (cmd->error == 0) {
+ switch (resp) {
+ case RESP_R1:
+ case RESP_R1B:
+ msdc_dump_card_status(host, cmd->resp[0]);
+ break;
+ case RESP_R3:
+ msdc_dump_ocr_reg(host, cmd->resp[0]);
+ break;
+ case RESP_R5:
+ msdc_dump_io_resp(host, cmd->resp[0]);
+ break;
+ case RESP_R6:
+ msdc_dump_rca_resp(host, cmd->resp[0]);
+ break;
+ }
+ }
+ break;
+ }
+#endif
+
+ /* do we need to save card's RCA when SD_SEND_RELATIVE_ADDR */
+
+ if (!tune) {
+ return cmd->error;
+ }
+
+ /* memory card CRC */
+ if(host->hw->flags & MSDC_REMOVABLE && cmd->error == (unsigned int)(-EIO) ) {
+ if (sdr_read32(SDC_CMD) & 0x1800) { /* check if has data phase */
+ msdc_abort_data(host);
+ } else {
+ /* do basic: reset*/
+ msdc_reset();
+ msdc_clr_fifo();
+ msdc_clr_int();
+ }
+ cmd->error = msdc_tune_cmdrsp(host,cmd);
+ }
+
+ // check DAT0
+ /* if (resp == RESP_R1B) {
+ while ((sdr_read32(MSDC_PS) & 0x10000) != 0x10000);
+ } */
+ /* CMD12 Error Handle */
+
+ return cmd->error;
+}
+
+static unsigned int msdc_do_command(struct msdc_host *host,
+ struct mmc_command *cmd,
+ int tune,
+ unsigned long timeout)
+{
+ if (msdc_command_start(host, cmd, tune, timeout))
+ goto end;
+
+ if (msdc_command_resp(host, cmd, tune, timeout))
+ goto end;
+
+end:
+
+ N_MSG(CMD, " return<%d> resp<0x%.8x>", cmd->error, cmd->resp[0]);
+ return cmd->error;
+}
+
+/* The abort condition when PIO read/write
+ tmo:
+*/
+static int msdc_pio_abort(struct msdc_host *host, struct mmc_data *data, unsigned long tmo)
+{
+ int ret = 0;
+ u32 base = host->base;
+
+ if (atomic_read(&host->abort)) {
+ ret = 1;
+ }
+
+ if (time_after(jiffies, tmo)) {
+ data->error = (unsigned int)-ETIMEDOUT;
+ ERR_MSG("XXX PIO Data Timeout: CMD<%d>", host->mrq->cmd->opcode);
+ ret = 1;
+ }
+
+ if(ret) {
+ msdc_reset();
+ msdc_clr_fifo();
+ msdc_clr_int();
+ ERR_MSG("msdc pio find abort");
+ }
+ return ret;
+}
+
+/*
+ Need to add a timeout, or WDT timeout, system reboot.
+*/
+// pio mode data read/write
+static int msdc_pio_read(struct msdc_host *host, struct mmc_data *data)
+{
+ struct scatterlist *sg = data->sg;
+ u32 base = host->base;
+ u32 num = data->sg_len;
+ u32 *ptr;
+ u8 *u8ptr;
+ u32 left = 0;
+ u32 count, size = 0;
+ u32 wints = MSDC_INTEN_DATTMO | MSDC_INTEN_DATCRCERR ;
+ unsigned long tmo = jiffies + DAT_TIMEOUT;
+
+ sdr_set_bits(MSDC_INTEN, wints);
+ while (num) {
+ left = sg_dma_len(sg);
+ ptr = sg_virt(sg);
+ while (left) {
+ if ((left >= MSDC_FIFO_THD) && (msdc_rxfifocnt() >= MSDC_FIFO_THD)) {
+ count = MSDC_FIFO_THD >> 2;
+ do {
+ *ptr++ = msdc_fifo_read32();
+ } while (--count);
+ left -= MSDC_FIFO_THD;
+ } else if ((left < MSDC_FIFO_THD) && msdc_rxfifocnt() >= left) {
+ while (left > 3) {
+ *ptr++ = msdc_fifo_read32();
+ left -= 4;
+ }
+
+ u8ptr = (u8 *)ptr;
+ while(left) {
+ * u8ptr++ = msdc_fifo_read8();
+ left--;
+ }
+ }
+
+ if (msdc_pio_abort(host, data, tmo)) {
+ goto end;
+ }
+ }
+ size += sg_dma_len(sg);
+ sg = sg_next(sg); num--;
+ }
+end:
+ data->bytes_xfered += size;
+ N_MSG(FIO, " PIO Read<%d>bytes", size);
+
+ sdr_clr_bits(MSDC_INTEN, wints);
+ if(data->error) ERR_MSG("read pio data->error<%d> left<%d> size<%d>", data->error, left, size);
+ return data->error;
+}
+
+/* please make sure won't using PIO when size >= 512
+ which means, memory card block read/write won't using pio
+ then don't need to handle the CMD12 when data error.
+*/
+static int msdc_pio_write(struct msdc_host* host, struct mmc_data *data)
+{
+ u32 base = host->base;
+ struct scatterlist *sg = data->sg;
+ u32 num = data->sg_len;
+ u32 *ptr;
+ u8 *u8ptr;
+ u32 left;
+ u32 count, size = 0;
+ u32 wints = MSDC_INTEN_DATTMO | MSDC_INTEN_DATCRCERR ;
+ unsigned long tmo = jiffies + DAT_TIMEOUT;
+
+ sdr_set_bits(MSDC_INTEN, wints);
+ while (num) {
+ left = sg_dma_len(sg);
+ ptr = sg_virt(sg);
+
+ while (left) {
+ if (left >= MSDC_FIFO_SZ && msdc_txfifocnt() == 0) {
+ count = MSDC_FIFO_SZ >> 2;
+ do {
+ msdc_fifo_write32(*ptr); ptr++;
+ } while (--count);
+ left -= MSDC_FIFO_SZ;
+ } else if (left < MSDC_FIFO_SZ && msdc_txfifocnt() == 0) {
+ while (left > 3) {
+ msdc_fifo_write32(*ptr); ptr++;
+ left -= 4;
+ }
+
+ u8ptr = (u8*)ptr;
+ while(left){
+ msdc_fifo_write8(*u8ptr); u8ptr++;
+ left--;
+ }
+ }
+
+ if (msdc_pio_abort(host, data, tmo)) {
+ goto end;
+ }
+ }
+ size += sg_dma_len(sg);
+ sg = sg_next(sg); num--;
+ }
+end:
+ data->bytes_xfered += size;
+ N_MSG(FIO, " PIO Write<%d>bytes", size);
+ if(data->error) ERR_MSG("write pio data->error<%d>", data->error);
+
+ sdr_clr_bits(MSDC_INTEN, wints);
+ return data->error;
+}
+
+#if 0 /* --- by chhung */
+// DMA resume / start / stop
+static void msdc_dma_resume(struct msdc_host *host)
+{
+ u32 base = host->base;
+
+ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_RESUME, 1);
+
+ N_MSG(DMA, "DMA resume");
+}
+#endif /* end of --- */
+
+static void msdc_dma_start(struct msdc_host *host)
+{
+ u32 base = host->base;
+ u32 wints = MSDC_INTEN_XFER_COMPL | MSDC_INTEN_DATTMO | MSDC_INTEN_DATCRCERR ;
+
+ sdr_set_bits(MSDC_INTEN, wints);
+ //dsb(); /* --- by chhung */
+ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_START, 1);
+
+ N_MSG(DMA, "DMA start");
+}
+
+static void msdc_dma_stop(struct msdc_host *host)
+{
+ u32 base = host->base;
+ //u32 retries=500;
+ u32 wints = MSDC_INTEN_XFER_COMPL | MSDC_INTEN_DATTMO | MSDC_INTEN_DATCRCERR ;
+
+ N_MSG(DMA, "DMA status: 0x%.8x",sdr_read32(MSDC_DMA_CFG));
+ //while (sdr_read32(MSDC_DMA_CFG) & MSDC_DMA_CFG_STS);
+
+ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_STOP, 1);
+ while (sdr_read32(MSDC_DMA_CFG) & MSDC_DMA_CFG_STS);
+
+ //dsb(); /* --- by chhung */
+ sdr_clr_bits(MSDC_INTEN, wints); /* Not just xfer_comp */
+
+ N_MSG(DMA, "DMA stop");
+}
+
+#if 0 /* --- by chhung */
+/* dump a gpd list */
+static void msdc_dma_dump(struct msdc_host *host, struct msdc_dma *dma)
+{
+ gpd_t *gpd = dma->gpd;
+ bd_t *bd = dma->bd;
+ bd_t *ptr;
+ int i = 0;
+ int p_to_v;
+
+ if (dma->mode != MSDC_MODE_DMA_DESC) {
+ return;
+ }
+
+ ERR_MSG("try to dump gpd and bd");
+
+ /* dump gpd */
+ ERR_MSG(".gpd<0x%.8x> gpd_phy<0x%.8x>", (int)gpd, (int)dma->gpd_addr);
+ ERR_MSG("...hwo <%d>", gpd->hwo );
+ ERR_MSG("...bdp <%d>", gpd->bdp );
+ ERR_MSG("...chksum<0x%.8x>", gpd->chksum );
+ //ERR_MSG("...intr <0x%.8x>", gpd->intr );
+ ERR_MSG("...next <0x%.8x>", (int)gpd->next );
+ ERR_MSG("...ptr <0x%.8x>", (int)gpd->ptr );
+ ERR_MSG("...buflen<0x%.8x>", gpd->buflen );
+ //ERR_MSG("...extlen<0x%.8x>", gpd->extlen );
+ //ERR_MSG("...arg <0x%.8x>", gpd->arg );
+ //ERR_MSG("...blknum<0x%.8x>", gpd->blknum );
+ //ERR_MSG("...cmd <0x%.8x>", gpd->cmd );
+
+ /* dump bd */
+ ERR_MSG(".bd<0x%.8x> bd_phy<0x%.8x> gpd_ptr<0x%.8x>", (int)bd, (int)dma->bd_addr, (int)gpd->ptr);
+ ptr = bd;
+ p_to_v = ((u32)bd - (u32)dma->bd_addr);
+ while (1) {
+ ERR_MSG(".bd[%d]", i); i++;
+ ERR_MSG("...eol <%d>", ptr->eol );
+ ERR_MSG("...chksum<0x%.8x>", ptr->chksum );
+ //ERR_MSG("...blkpad<0x%.8x>", ptr->blkpad );
+ //ERR_MSG("...dwpad <0x%.8x>", ptr->dwpad );
+ ERR_MSG("...next <0x%.8x>", (int)ptr->next );
+ ERR_MSG("...ptr <0x%.8x>", (int)ptr->ptr );
+ ERR_MSG("...buflen<0x%.8x>", (int)ptr->buflen );
+
+ if (ptr->eol == 1) {
+ break;
+ }
+
+ /* find the next bd, virtual address of ptr->next */
+ /* don't need to enable when use malloc */
+ //BUG_ON( (ptr->next + p_to_v)!=(ptr+1) );
+ //ERR_MSG(".next bd<0x%.8x><0x%.8x>", (ptr->next + p_to_v), (ptr+1));
+ ptr++;
+ }
+
+ ERR_MSG("dump gpd and bd finished");
+}
+#endif /* end of --- */
+
+/* calc checksum */
+static u8 msdc_dma_calcs(u8 *buf, u32 len)
+{
+ u32 i, sum = 0;
+ for (i = 0; i < len; i++) {
+ sum += buf[i];
+ }
+ return 0xFF - (u8)sum;
+}
+
+/* gpd bd setup + dma registers */
+static int msdc_dma_config(struct msdc_host *host, struct msdc_dma *dma)
+{
+ u32 base = host->base;
+ u32 sglen = dma->sglen;
+ //u32 i, j, num, bdlen, arg, xfersz;
+ u32 j, num, bdlen;
+ u8 blkpad, dwpad, chksum;
+ struct scatterlist *sg = dma->sg;
+ gpd_t *gpd;
+ bd_t *bd;
+
+ switch (dma->mode) {
+ case MSDC_MODE_DMA_BASIC:
+ BUG_ON(dma->xfersz > 65535);
+ BUG_ON(dma->sglen != 1);
+ sdr_write32(MSDC_DMA_SA, PHYSADDR(sg_dma_address(sg)));
+ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_LASTBUF, 1);
+//#if defined (CONFIG_RALINK_MT7620)
+ if (ralink_soc == MT762X_SOC_MT7620A)
+ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_XFERSZ, sg_dma_len(sg));
+//#elif defined (CONFIG_RALINK_MT7621) || defined (CONFIG_RALINK_MT7628)
+ else
+ sdr_write32((volatile u32*)(RALINK_MSDC_BASE+0xa8), sg_dma_len(sg));
+//#endif
+ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_BRUSTSZ, dma->burstsz);
+ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_MODE, 0);
+ break;
+ case MSDC_MODE_DMA_DESC:
+ blkpad = (dma->flags & DMA_FLAG_PAD_BLOCK) ? 1 : 0;
+ dwpad = (dma->flags & DMA_FLAG_PAD_DWORD) ? 1 : 0;
+ chksum = (dma->flags & DMA_FLAG_EN_CHKSUM) ? 1 : 0;
+
+ /* calculate the required number of gpd */
+ num = (sglen + MAX_BD_PER_GPD - 1) / MAX_BD_PER_GPD;
+ BUG_ON(num !=1 );
+
+ gpd = dma->gpd;
+ bd = dma->bd;
+ bdlen = sglen;
+
+ /* modify gpd*/
+ //gpd->intr = 0;
+ gpd->hwo = 1; /* hw will clear it */
+ gpd->bdp = 1;
+ gpd->chksum = 0; /* need to clear first. */
+ gpd->chksum = (chksum ? msdc_dma_calcs((u8 *)gpd, 16) : 0);
+
+ /* modify bd*/
+ for (j = 0; j < bdlen; j++) {
+ msdc_init_bd(&bd[j], blkpad, dwpad, sg_dma_address(sg), sg_dma_len(sg));
+ if(j == bdlen - 1) {
+ bd[j].eol = 1; /* the last bd */
+ } else {
+ bd[j].eol = 0;
+ }
+ bd[j].chksum = 0; /* checksume need to clear first */
+ bd[j].chksum = (chksum ? msdc_dma_calcs((u8 *)(&bd[j]), 16) : 0);
+ sg++;
+ }
+
+ dma->used_gpd += 2;
+ dma->used_bd += bdlen;
+
+ sdr_set_field(MSDC_DMA_CFG, MSDC_DMA_CFG_DECSEN, chksum);
+ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_BRUSTSZ, dma->burstsz);
+ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_MODE, 1);
+
+ sdr_write32(MSDC_DMA_SA, PHYSADDR((u32)dma->gpd_addr));
+ break;
+
+ default:
+ break;
+ }
+
+ N_MSG(DMA, "DMA_CTRL = 0x%x", sdr_read32(MSDC_DMA_CTRL));
+ N_MSG(DMA, "DMA_CFG = 0x%x", sdr_read32(MSDC_DMA_CFG));
+ N_MSG(DMA, "DMA_SA = 0x%x", sdr_read32(MSDC_DMA_SA));
+
+ return 0;
+}
+
+static void msdc_dma_setup(struct msdc_host *host, struct msdc_dma *dma,
+ struct scatterlist *sg, unsigned int sglen)
+{
+ BUG_ON(sglen > MAX_BD_NUM); /* not support currently */
+
+ dma->sg = sg;
+ dma->flags = DMA_FLAG_EN_CHKSUM;
+ //dma->flags = DMA_FLAG_NONE; /* CHECKME */
+ dma->sglen = sglen;
+ dma->xfersz = host->xfer_size;
+ dma->burstsz = MSDC_BRUST_64B;
+
+ if (sglen == 1 && sg_dma_len(sg) <= MAX_DMA_CNT)
+ dma->mode = MSDC_MODE_DMA_BASIC;
+ else
+ dma->mode = MSDC_MODE_DMA_DESC;
+
+ N_MSG(DMA, "DMA mode<%d> sglen<%d> xfersz<%d>", dma->mode, dma->sglen, dma->xfersz);
+
+ msdc_dma_config(host, dma);
+
+ /*if (dma->mode == MSDC_MODE_DMA_DESC) {
+ //msdc_dma_dump(host, dma);
+ } */
+}
+
+/* set block number before send command */
+static void msdc_set_blknum(struct msdc_host *host, u32 blknum)
+{
+ u32 base = host->base;
+
+ sdr_write32(SDC_BLK_NUM, blknum);
+}
+
+static int msdc_do_request(struct mmc_host*mmc, struct mmc_request*mrq)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+ u32 base = host->base;
+ //u32 intsts = 0;
+ unsigned int left=0;
+ int dma = 0, read = 1, dir = DMA_FROM_DEVICE, send_type=0;
+
+ #define SND_DAT 0
+ #define SND_CMD 1
+
+ BUG_ON(mmc == NULL);
+ BUG_ON(mrq == NULL);
+
+ host->error = 0;
+ atomic_set(&host->abort, 0);
+
+ cmd = mrq->cmd;
+ data = mrq->cmd->data;
+
+#if 0 /* --- by chhung */
+ //if(host->id ==1){
+ N_MSG(OPS, "enable clock!");
+ msdc_ungate_clock(host->id);
+ //}
+#endif /* end of --- */
+
+ if (!data) {
+ send_type=SND_CMD;
+ if (msdc_do_command(host, cmd, 1, CMD_TIMEOUT) != 0) {
+ goto done;
+ }
+ } else {
+ BUG_ON(data->blksz > HOST_MAX_BLKSZ);
+ send_type=SND_DAT;
+
+ data->error = 0;
+ read = data->flags & MMC_DATA_READ ? 1 : 0;
+ host->data = data;
+ host->xfer_size = data->blocks * data->blksz;
+ host->blksz = data->blksz;
+
+ /* deside the transfer mode */
+ if (drv_mode[host->id] == MODE_PIO) {
+ host->dma_xfer = dma = 0;
+ } else if (drv_mode[host->id] == MODE_DMA) {
+ host->dma_xfer = dma = 1;
+ } else if (drv_mode[host->id] == MODE_SIZE_DEP) {
+ host->dma_xfer = dma = ((host->xfer_size >= dma_size[host->id]) ? 1 : 0);
+ }
+
+ if (read) {
+ if ((host->timeout_ns != data->timeout_ns) ||
+ (host->timeout_clks != data->timeout_clks)) {
+ msdc_set_timeout(host, data->timeout_ns, data->timeout_clks);
+ }
+ }
+
+ msdc_set_blknum(host, data->blocks);
+ //msdc_clr_fifo(); /* no need */
+
+ if (dma) {
+ msdc_dma_on(); /* enable DMA mode first!! */
+ init_completion(&host->xfer_done);
+
+ /* start the command first*/
+ if (msdc_command_start(host, cmd, 1, CMD_TIMEOUT) != 0)
+ goto done;
+
+ dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+ (void)dma_map_sg(mmc_dev(mmc), data->sg, data->sg_len, dir);
+ msdc_dma_setup(host, &host->dma, data->sg, data->sg_len);
+
+ /* then wait command done */
+ if (msdc_command_resp(host, cmd, 1, CMD_TIMEOUT) != 0)
+ goto done;
+
+ /* for read, the data coming too fast, then CRC error
+ start DMA no business with CRC. */
+ //init_completion(&host->xfer_done);
+ msdc_dma_start(host);
+
+ spin_unlock(&host->lock);
+ if(!wait_for_completion_timeout(&host->xfer_done, DAT_TIMEOUT)){
+ ERR_MSG("XXX CMD<%d> wait xfer_done<%d> timeout!!", cmd->opcode, data->blocks * data->blksz);
+ ERR_MSG(" DMA_SA = 0x%x", sdr_read32(MSDC_DMA_SA));
+ ERR_MSG(" DMA_CA = 0x%x", sdr_read32(MSDC_DMA_CA));
+ ERR_MSG(" DMA_CTRL = 0x%x", sdr_read32(MSDC_DMA_CTRL));
+ ERR_MSG(" DMA_CFG = 0x%x", sdr_read32(MSDC_DMA_CFG));
+ data->error = (unsigned int)-ETIMEDOUT;
+
+ msdc_reset();
+ msdc_clr_fifo();
+ msdc_clr_int();
+ }
+ spin_lock(&host->lock);
+ msdc_dma_stop(host);
+ } else {
+ /* Firstly: send command */
+ if (msdc_do_command(host, cmd, 1, CMD_TIMEOUT) != 0) {
+ goto done;
+ }
+
+ /* Secondly: pio data phase */
+ if (read) {
+ if (msdc_pio_read(host, data)){
+ goto done;
+ }
+ } else {
+ if (msdc_pio_write(host, data)) {
+ goto done;
+ }
+ }
+
+ /* For write case: make sure contents in fifo flushed to device */
+ if (!read) {
+ while (1) {
+ left=msdc_txfifocnt();
+ if (left == 0) {
+ break;
+ }
+ if (msdc_pio_abort(host, data, jiffies + DAT_TIMEOUT)) {
+ break;
+ /* Fix me: what about if data error, when stop ? how to? */
+ }
+ }
+ } else {
+ /* Fix me: read case: need to check CRC error */
+ }
+
+ /* For write case: SDCBUSY and Xfer_Comp will assert when DAT0 not busy.
+ For read case : SDCBUSY and Xfer_Comp will assert when last byte read out from FIFO.
+ */
+
+ /* try not to wait xfer_comp interrupt.
+ the next command will check SDC_BUSY.
+ SDC_BUSY means xfer_comp assert
+ */
+
+ } // PIO mode
+
+ /* Last: stop transfer */
+ if (data->stop){
+ if (msdc_do_command(host, data->stop, 0, CMD_TIMEOUT) != 0) {
+ goto done;
+ }
+ }
+ }
+
+done:
+ if (data != NULL) {
+ host->data = NULL;
+ host->dma_xfer = 0;
+ if (dma != 0) {
+ msdc_dma_off();
+ host->dma.used_bd = 0;
+ host->dma.used_gpd = 0;
+ dma_unmap_sg(mmc_dev(mmc), data->sg, data->sg_len, dir);
+ }
+ host->blksz = 0;
+
+#if 0 // don't stop twice!
+ if(host->hw->flags & MSDC_REMOVABLE && data->error) {
+ msdc_abort_data(host);
+ /* reset in IRQ, stop command has issued. -> No need */
+ }
+#endif
+
+ N_MSG(OPS, "CMD<%d> data<%s %s> blksz<%d> block<%d> error<%d>",cmd->opcode, (dma? "dma":"pio"),
+ (read ? "read ":"write") ,data->blksz, data->blocks, data->error);
+ }
+
+#if 0 /* --- by chhung */
+#if 1
+ //if(host->id==1) {
+ if(send_type==SND_CMD) {
+ if(cmd->opcode == MMC_SEND_STATUS) {
+ if((cmd->resp[0] & CARD_READY_FOR_DATA) ||(CARD_CURRENT_STATE(cmd->resp[0]) != 7)){
+ N_MSG(OPS,"disable clock, CMD13 IDLE");
+ msdc_gate_clock(host->id);
+ }
+ } else {
+ N_MSG(OPS,"disable clock, CMD<%d>", cmd->opcode);
+ msdc_gate_clock(host->id);
+ }
+ } else {
+ if(read) {
+ N_MSG(OPS,"disable clock!!! Read CMD<%d>",cmd->opcode);
+ msdc_gate_clock(host->id);
+ }
+ }
+ //}
+#else
+ msdc_gate_clock(host->id);
+#endif
+#endif /* end of --- */
+
+ if (mrq->cmd->error) host->error = 0x001;
+ if (mrq->data && mrq->data->error) host->error |= 0x010;
+ if (mrq->stop && mrq->stop->error) host->error |= 0x100;
+
+ //if (host->error) ERR_MSG("host->error<%d>", host->error);
+
+ return host->error;
+}
+
+static int msdc_app_cmd(struct mmc_host *mmc, struct msdc_host *host)
+{
+ struct mmc_command cmd;
+ struct mmc_request mrq;
+ u32 err;
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ cmd.opcode = MMC_APP_CMD;
+#if 0 /* bug: we meet mmc->card is null when ACMD6 */
+ cmd.arg = mmc->card->rca << 16;
+#else
+ cmd.arg = host->app_cmd_arg;
+#endif
+ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
+
+ memset(&mrq, 0, sizeof(struct mmc_request));
+ mrq.cmd = &cmd; cmd.mrq = &mrq;
+ cmd.data = NULL;
+
+ err = msdc_do_command(host, &cmd, 0, CMD_TIMEOUT);
+ return err;
+}
+
+static int msdc_tune_cmdrsp(struct msdc_host*host, struct mmc_command *cmd)
+{
+ int result = -1;
+ u32 base = host->base;
+ u32 rsmpl, cur_rsmpl, orig_rsmpl;
+ u32 rrdly, cur_rrdly = 0xffffffff, orig_rrdly;
+ u32 skip = 1;
+
+ /* ==== don't support 3.0 now ====
+ 1: R_SMPL[1]
+ 2: PAD_CMD_RESP_RXDLY[26:22]
+ ==========================*/
+
+ // save the previous tune result
+ sdr_get_field(MSDC_IOCON, MSDC_IOCON_RSPL, orig_rsmpl);
+ sdr_get_field(MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRRDLY, orig_rrdly);
+
+ rrdly = 0;
+ do {
+ for (rsmpl = 0; rsmpl < 2; rsmpl++) {
+ /* Lv1: R_SMPL[1] */
+ cur_rsmpl = (orig_rsmpl + rsmpl) % 2;
+ if (skip == 1) {
+ skip = 0;
+ continue;
+ }
+ sdr_set_field(MSDC_IOCON, MSDC_IOCON_RSPL, cur_rsmpl);
+
+ if (host->app_cmd) {
+ result = msdc_app_cmd(host->mmc, host);
+ if (result) {
+ ERR_MSG("TUNE_CMD app_cmd<%d> failed: RESP_RXDLY<%d>,R_SMPL<%d>",
+ host->mrq->cmd->opcode, cur_rrdly, cur_rsmpl);
+ continue;
+ }
+ }
+ result = msdc_do_command(host, cmd, 0, CMD_TIMEOUT); // not tune.
+ ERR_MSG("TUNE_CMD<%d> %s PAD_CMD_RESP_RXDLY[26:22]<%d> R_SMPL[1]<%d>", cmd->opcode,
+ (result == 0) ? "PASS" : "FAIL", cur_rrdly, cur_rsmpl);
+
+ if (result == 0) {
+ return 0;
+ }
+ if (result != (unsigned int)(-EIO)) {
+ ERR_MSG("TUNE_CMD<%d> Error<%d> not -EIO", cmd->opcode, result);
+ return result;
+ }
+
+ /* should be EIO */
+ if (sdr_read32(SDC_CMD) & 0x1800) { /* check if has data phase */
+ msdc_abort_data(host);
+ }
+ }
+
+ /* Lv2: PAD_CMD_RESP_RXDLY[26:22] */
+ cur_rrdly = (orig_rrdly + rrdly + 1) % 32;
+ sdr_set_field(MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRRDLY, cur_rrdly);
+ }while (++rrdly < 32);
+
+ return result;
+}
+
+/* Support SD2.0 Only */
+static int msdc_tune_bread(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ u32 base = host->base;
+ u32 ddr=0;
+ u32 dcrc=0;
+ u32 rxdly, cur_rxdly0, cur_rxdly1;
+ u32 dsmpl, cur_dsmpl, orig_dsmpl;
+ u32 cur_dat0, cur_dat1, cur_dat2, cur_dat3;
+ u32 cur_dat4, cur_dat5, cur_dat6, cur_dat7;
+ u32 orig_dat0, orig_dat1, orig_dat2, orig_dat3;
+ u32 orig_dat4, orig_dat5, orig_dat6, orig_dat7;
+ int result = -1;
+ u32 skip = 1;
+
+ sdr_get_field(MSDC_IOCON, MSDC_IOCON_DSPL, orig_dsmpl);
+
+ /* Tune Method 2. */
+ sdr_set_field(MSDC_IOCON, MSDC_IOCON_DDLSEL, 1);
+
+ rxdly = 0;
+ do {
+ for (dsmpl = 0; dsmpl < 2; dsmpl++) {
+ cur_dsmpl = (orig_dsmpl + dsmpl) % 2;
+ if (skip == 1) {
+ skip = 0;
+ continue;
+ }
+ sdr_set_field(MSDC_IOCON, MSDC_IOCON_DSPL, cur_dsmpl);
+
+ if (host->app_cmd) {
+ result = msdc_app_cmd(host->mmc, host);
+ if (result) {
+ ERR_MSG("TUNE_BREAD app_cmd<%d> failed", host->mrq->cmd->opcode);
+ continue;
+ }
+ }
+ result = msdc_do_request(mmc,mrq);
+
+ sdr_get_field(SDC_DCRC_STS, SDC_DCRC_STS_POS|SDC_DCRC_STS_NEG, dcrc); /* RO */
+ if (!ddr) dcrc &= ~SDC_DCRC_STS_NEG;
+ ERR_MSG("TUNE_BREAD<%s> dcrc<0x%x> DATRDDLY0/1<0x%x><0x%x> dsmpl<0x%x>",
+ (result == 0 && dcrc == 0) ? "PASS" : "FAIL", dcrc,
+ sdr_read32(MSDC_DAT_RDDLY0), sdr_read32(MSDC_DAT_RDDLY1), cur_dsmpl);
+
+ /* Fix me: result is 0, but dcrc is still exist */
+ if (result == 0 && dcrc == 0) {
+ goto done;
+ } else {
+ /* there is a case: command timeout, and data phase not processed */
+ if (mrq->data->error != 0 && mrq->data->error != (unsigned int)(-EIO)) {
+ ERR_MSG("TUNE_READ: result<0x%x> cmd_error<%d> data_error<%d>",
+ result, mrq->cmd->error, mrq->data->error);
+ goto done;
+ }
+ }
+ }
+
+ cur_rxdly0 = sdr_read32(MSDC_DAT_RDDLY0);
+ cur_rxdly1 = sdr_read32(MSDC_DAT_RDDLY1);
+
+ /* E1 ECO. YD: Reverse */
+ if (sdr_read32(MSDC_ECO_VER) >= 4) {
+ orig_dat0 = (cur_rxdly0 >> 24) & 0x1F;
+ orig_dat1 = (cur_rxdly0 >> 16) & 0x1F;
+ orig_dat2 = (cur_rxdly0 >> 8) & 0x1F;
+ orig_dat3 = (cur_rxdly0 >> 0) & 0x1F;
+ orig_dat4 = (cur_rxdly1 >> 24) & 0x1F;
+ orig_dat5 = (cur_rxdly1 >> 16) & 0x1F;
+ orig_dat6 = (cur_rxdly1 >> 8) & 0x1F;
+ orig_dat7 = (cur_rxdly1 >> 0) & 0x1F;
+ } else {
+ orig_dat0 = (cur_rxdly0 >> 0) & 0x1F;
+ orig_dat1 = (cur_rxdly0 >> 8) & 0x1F;
+ orig_dat2 = (cur_rxdly0 >> 16) & 0x1F;
+ orig_dat3 = (cur_rxdly0 >> 24) & 0x1F;
+ orig_dat4 = (cur_rxdly1 >> 0) & 0x1F;
+ orig_dat5 = (cur_rxdly1 >> 8) & 0x1F;
+ orig_dat6 = (cur_rxdly1 >> 16) & 0x1F;
+ orig_dat7 = (cur_rxdly1 >> 24) & 0x1F;
+ }
+
+ if (ddr) {
+ cur_dat0 = (dcrc & (1 << 0) || dcrc & (1 << 8)) ? ((orig_dat0 + 1) % 32) : orig_dat0;
+ cur_dat1 = (dcrc & (1 << 1) || dcrc & (1 << 9)) ? ((orig_dat1 + 1) % 32) : orig_dat1;
+ cur_dat2 = (dcrc & (1 << 2) || dcrc & (1 << 10)) ? ((orig_dat2 + 1) % 32) : orig_dat2;
+ cur_dat3 = (dcrc & (1 << 3) || dcrc & (1 << 11)) ? ((orig_dat3 + 1) % 32) : orig_dat3;
+ } else {
+ cur_dat0 = (dcrc & (1 << 0)) ? ((orig_dat0 + 1) % 32) : orig_dat0;
+ cur_dat1 = (dcrc & (1 << 1)) ? ((orig_dat1 + 1) % 32) : orig_dat1;
+ cur_dat2 = (dcrc & (1 << 2)) ? ((orig_dat2 + 1) % 32) : orig_dat2;
+ cur_dat3 = (dcrc & (1 << 3)) ? ((orig_dat3 + 1) % 32) : orig_dat3;
+ }
+ cur_dat4 = (dcrc & (1 << 4)) ? ((orig_dat4 + 1) % 32) : orig_dat4;
+ cur_dat5 = (dcrc & (1 << 5)) ? ((orig_dat5 + 1) % 32) : orig_dat5;
+ cur_dat6 = (dcrc & (1 << 6)) ? ((orig_dat6 + 1) % 32) : orig_dat6;
+ cur_dat7 = (dcrc & (1 << 7)) ? ((orig_dat7 + 1) % 32) : orig_dat7;
+
+ cur_rxdly0 = (cur_dat0 << 24) | (cur_dat1 << 16) | (cur_dat2 << 8) | (cur_dat3 << 0);
+ cur_rxdly1 = (cur_dat4 << 24) | (cur_dat5 << 16) | (cur_dat6 << 8) | (cur_dat7 << 0);
+
+ sdr_write32(MSDC_DAT_RDDLY0, cur_rxdly0);
+ sdr_write32(MSDC_DAT_RDDLY1, cur_rxdly1);
+
+ } while (++rxdly < 32);
+
+done:
+ return result;
+}
+
+static int msdc_tune_bwrite(struct mmc_host *mmc,struct mmc_request *mrq)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ u32 base = host->base;
+
+ u32 wrrdly, cur_wrrdly = 0xffffffff, orig_wrrdly;
+ u32 dsmpl, cur_dsmpl, orig_dsmpl;
+ u32 rxdly, cur_rxdly0;
+ u32 orig_dat0, orig_dat1, orig_dat2, orig_dat3;
+ u32 cur_dat0, cur_dat1, cur_dat2, cur_dat3;
+ int result = -1;
+ u32 skip = 1;
+
+ // MSDC_IOCON_DDR50CKD need to check. [Fix me]
+
+ sdr_get_field(MSDC_PAD_TUNE, MSDC_PAD_TUNE_DATWRDLY, orig_wrrdly);
+ sdr_get_field(MSDC_IOCON, MSDC_IOCON_DSPL, orig_dsmpl );
+
+ /* Tune Method 2. just DAT0 */
+ sdr_set_field(MSDC_IOCON, MSDC_IOCON_DDLSEL, 1);
+ cur_rxdly0 = sdr_read32(MSDC_DAT_RDDLY0);
+
+ /* E1 ECO. YD: Reverse */
+ if (sdr_read32(MSDC_ECO_VER) >= 4) {
+ orig_dat0 = (cur_rxdly0 >> 24) & 0x1F;
+ orig_dat1 = (cur_rxdly0 >> 16) & 0x1F;
+ orig_dat2 = (cur_rxdly0 >> 8) & 0x1F;
+ orig_dat3 = (cur_rxdly0 >> 0) & 0x1F;
+ } else {
+ orig_dat0 = (cur_rxdly0 >> 0) & 0x1F;
+ orig_dat1 = (cur_rxdly0 >> 8) & 0x1F;
+ orig_dat2 = (cur_rxdly0 >> 16) & 0x1F;
+ orig_dat3 = (cur_rxdly0 >> 24) & 0x1F;
+ }
+
+ rxdly = 0;
+ do {
+ wrrdly = 0;
+ do {
+ for (dsmpl = 0; dsmpl < 2; dsmpl++) {
+ cur_dsmpl = (orig_dsmpl + dsmpl) % 2;
+ if (skip == 1) {
+ skip = 0;
+ continue;
+ }
+ sdr_set_field(MSDC_IOCON, MSDC_IOCON_DSPL, cur_dsmpl);
+
+ if (host->app_cmd) {
+ result = msdc_app_cmd(host->mmc, host);
+ if (result) {
+ ERR_MSG("TUNE_BWRITE app_cmd<%d> failed", host->mrq->cmd->opcode);
+ continue;
+ }
+ }
+ result = msdc_do_request(mmc,mrq);
+
+ ERR_MSG("TUNE_BWRITE<%s> DSPL<%d> DATWRDLY<%d> MSDC_DAT_RDDLY0<0x%x>",
+ result == 0 ? "PASS" : "FAIL",
+ cur_dsmpl, cur_wrrdly, cur_rxdly0);
+
+ if (result == 0) {
+ goto done;
+ }
+ else {
+ /* there is a case: command timeout, and data phase not processed */
+ if (mrq->data->error != (unsigned int)(-EIO)) {
+ ERR_MSG("TUNE_READ: result<0x%x> cmd_error<%d> data_error<%d>",
+ result, mrq->cmd->error, mrq->data->error);
+ goto done;
+ }
+ }
+ }
+ cur_wrrdly = (orig_wrrdly + wrrdly + 1) % 32;
+ sdr_set_field(MSDC_PAD_TUNE, MSDC_PAD_TUNE_DATWRDLY, cur_wrrdly);
+ } while (++wrrdly < 32);
+
+ cur_dat0 = (orig_dat0 + rxdly) % 32; /* only adjust bit-1 for crc */
+ cur_dat1 = orig_dat1;
+ cur_dat2 = orig_dat2;
+ cur_dat3 = orig_dat3;
+
+ cur_rxdly0 = (cur_dat0 << 24) | (cur_dat1 << 16) | (cur_dat2 << 8) | (cur_dat3 << 0);
+ sdr_write32(MSDC_DAT_RDDLY0, cur_rxdly0);
+ } while (++rxdly < 32);
+
+done:
+ return result;
+}
+
+static int msdc_get_card_status(struct mmc_host *mmc, struct msdc_host *host, u32 *status)
+{
+ struct mmc_command cmd;
+ struct mmc_request mrq;
+ u32 err;
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ cmd.opcode = MMC_SEND_STATUS;
+ if (mmc->card) {
+ cmd.arg = mmc->card->rca << 16;
+ } else {
+ ERR_MSG("cmd13 mmc card is null");
+ cmd.arg = host->app_cmd_arg;
+ }
+ cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
+
+ memset(&mrq, 0, sizeof(struct mmc_request));
+ mrq.cmd = &cmd; cmd.mrq = &mrq;
+ cmd.data = NULL;
+
+ err = msdc_do_command(host, &cmd, 1, CMD_TIMEOUT);
+
+ if (status) {
+ *status = cmd.resp[0];
+ }
+
+ return err;
+}
+
+static int msdc_check_busy(struct mmc_host *mmc, struct msdc_host *host)
+{
+ u32 err = 0;
+ u32 status = 0;
+
+ do {
+ err = msdc_get_card_status(mmc, host, &status);
+ if (err) return err;
+ /* need cmd12? */
+ ERR_MSG("cmd<13> resp<0x%x>", status);
+ } while (R1_CURRENT_STATE(status) == 7);
+
+ return err;
+}
+
+/* failed when msdc_do_request */
+static int msdc_tune_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+ //u32 base = host->base;
+ int ret=0, read;
+
+ cmd = mrq->cmd;
+ data = mrq->cmd->data;
+
+ read = data->flags & MMC_DATA_READ ? 1 : 0;
+
+ if (read) {
+ if (data->error == (unsigned int)(-EIO)) {
+ ret = msdc_tune_bread(mmc,mrq);
+ }
+ } else {
+ ret = msdc_check_busy(mmc, host);
+ if (ret){
+ ERR_MSG("XXX cmd13 wait program done failed");
+ return ret;
+ }
+ /* CRC and TO */
+ /* Fix me: don't care card status? */
+ ret = msdc_tune_bwrite(mmc,mrq);
+ }
+
+ return ret;
+}
+
+/* ops.request */
+static void msdc_ops_request(struct mmc_host *mmc,struct mmc_request *mrq)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+
+ //=== for sdio profile ===
+#if 0 /* --- by chhung */
+ u32 old_H32, old_L32, new_H32, new_L32;
+ u32 ticks = 0, opcode = 0, sizes = 0, bRx = 0;
+#endif /* end of --- */
+
+ if(host->mrq){
+ ERR_MSG("XXX host->mrq<0x%.8x>", (int)host->mrq);
+ BUG();
+ }
+
+ if (!is_card_present(host) || host->power_mode == MMC_POWER_OFF) {
+ ERR_MSG("cmd<%d> card<%d> power<%d>", mrq->cmd->opcode, is_card_present(host), host->power_mode);
+ mrq->cmd->error = (unsigned int)-ENOMEDIUM;
+
+#if 1
+ mrq->done(mrq); // call done directly.
+#else
+ mrq->cmd->retries = 0; // please don't retry.
+ mmc_request_done(mmc, mrq);
+#endif
+
+ return;
+ }
+
+ /* start to process */
+ spin_lock(&host->lock);
+#if 0 /* --- by chhung */
+ if (sdio_pro_enable) { //=== for sdio profile ===
+ if (mrq->cmd->opcode == 52 || mrq->cmd->opcode == 53) {
+ GPT_GetCounter64(&old_L32, &old_H32);
+ }
+ }
+#endif /* end of --- */
+
+ host->mrq = mrq;
+
+ if (msdc_do_request(mmc,mrq)) {
+ if(host->hw->flags & MSDC_REMOVABLE && ralink_soc == MT762X_SOC_MT7621AT && mrq->data && mrq->data->error) {
+ msdc_tune_request(mmc,mrq);
+ }
+ }
+
+ /* ==== when request done, check if app_cmd ==== */
+ if (mrq->cmd->opcode == MMC_APP_CMD) {
+ host->app_cmd = 1;
+ host->app_cmd_arg = mrq->cmd->arg; /* save the RCA */
+ } else {
+ host->app_cmd = 0;
+ //host->app_cmd_arg = 0;
+ }
+
+ host->mrq = NULL;
+
+#if 0 /* --- by chhung */
+ //=== for sdio profile ===
+ if (sdio_pro_enable) {
+ if (mrq->cmd->opcode == 52 || mrq->cmd->opcode == 53) {
+ GPT_GetCounter64(&new_L32, &new_H32);
+ ticks = msdc_time_calc(old_L32, old_H32, new_L32, new_H32);
+
+ opcode = mrq->cmd->opcode;
+ if (mrq->cmd->data) {
+ sizes = mrq->cmd->data->blocks * mrq->cmd->data->blksz;
+ bRx = mrq->cmd->data->flags & MMC_DATA_READ ? 1 : 0 ;
+ } else {
+ bRx = mrq->cmd->arg & 0x80000000 ? 1 : 0;
+ }
+
+ if (!mrq->cmd->error) {
+ msdc_performance(opcode, sizes, bRx, ticks);
+ }
+ }
+ }
+#endif /* end of --- */
+ spin_unlock(&host->lock);
+
+ mmc_request_done(mmc, mrq);
+
+ return;
+}
+
+/* called by ops.set_ios */
+static void msdc_set_buswidth(struct msdc_host *host, u32 width)
+{
+ u32 base = host->base;
+ u32 val = sdr_read32(SDC_CFG);
+
+ val &= ~SDC_CFG_BUSWIDTH;
+
+ switch (width) {
+ default:
+ case MMC_BUS_WIDTH_1:
+ width = 1;
+ val |= (MSDC_BUS_1BITS << 16);
+ break;
+ case MMC_BUS_WIDTH_4:
+ val |= (MSDC_BUS_4BITS << 16);
+ break;
+ case MMC_BUS_WIDTH_8:
+ val |= (MSDC_BUS_8BITS << 16);
+ break;
+ }
+
+ sdr_write32(SDC_CFG, val);
+
+ N_MSG(CFG, "Bus Width = %d", width);
+}
+
+/* ops.set_ios */
+static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ struct msdc_hw *hw=host->hw;
+ u32 base = host->base;
+ u32 ddr = 0;
+
+#ifdef MT6575_SD_DEBUG
+ static char *vdd[] = {
+ "1.50v", "1.55v", "1.60v", "1.65v", "1.70v", "1.80v", "1.90v",
+ "2.00v", "2.10v", "2.20v", "2.30v", "2.40v", "2.50v", "2.60v",
+ "2.70v", "2.80v", "2.90v", "3.00v", "3.10v", "3.20v", "3.30v",
+ "3.40v", "3.50v", "3.60v"
+ };
+ static char *power_mode[] = {
+ "OFF", "UP", "ON"
+ };
+ static char *bus_mode[] = {
+ "UNKNOWN", "OPENDRAIN", "PUSHPULL"
+ };
+ static char *timing[] = {
+ "LEGACY", "MMC_HS", "SD_HS"
+ };
+
+ printk("SET_IOS: CLK(%dkHz), BUS(%s), BW(%u), PWR(%s), VDD(%s), TIMING(%s)",
+ ios->clock / 1000, bus_mode[ios->bus_mode],
+ (ios->bus_width == MMC_BUS_WIDTH_4) ? 4 : 1,
+ power_mode[ios->power_mode], vdd[ios->vdd], timing[ios->timing]);
+#endif
+
+ msdc_set_buswidth(host, ios->bus_width);
+
+ /* Power control ??? */
+ switch (ios->power_mode) {
+ case MMC_POWER_OFF:
+ case MMC_POWER_UP:
+ // msdc_set_power_mode(host, ios->power_mode); /* --- by chhung */
+ break;
+ case MMC_POWER_ON:
+ host->power_mode = MMC_POWER_ON;
+ break;
+ default:
+ break;
+ }
+
+ /* Clock control */
+ if (host->mclk != ios->clock) {
+ if(ios->clock > 25000000) {
+ //if (!(host->hw->flags & MSDC_REMOVABLE)) {
+ INIT_MSG("SD data latch edge<%d>", hw->data_edge);
+ sdr_set_field(MSDC_IOCON, MSDC_IOCON_RSPL, hw->cmd_edge);
+ sdr_set_field(MSDC_IOCON, MSDC_IOCON_DSPL, hw->data_edge);
+ //} /* for tuning debug */
+ } else { /* default value */
+ sdr_write32(MSDC_IOCON, 0x00000000);
+ // sdr_write32(MSDC_DAT_RDDLY0, 0x00000000);
+ sdr_write32(MSDC_DAT_RDDLY0, 0x10101010); // for MT7620 E2 and afterward
+ sdr_write32(MSDC_DAT_RDDLY1, 0x00000000);
+ // sdr_write32(MSDC_PAD_TUNE, 0x00000000);
+ sdr_write32(MSDC_PAD_TUNE, 0x84101010); // for MT7620 E2 and afterward
+ }
+ msdc_set_mclk(host, ddr, ios->clock);
+ }
+}
+
+/* ops.get_ro */
+static int msdc_ops_get_ro(struct mmc_host *mmc)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ u32 base = host->base;
+ unsigned long flags;
+ int ro = 0;
+
+ if (host->hw->flags & MSDC_WP_PIN_EN) { /* set for card */
+ spin_lock_irqsave(&host->lock, flags);
+ ro = (sdr_read32(MSDC_PS) >> 31);
+ spin_unlock_irqrestore(&host->lock, flags);
+ }
+ return ro;
+}
+
+/* ops.get_cd */
+static int msdc_ops_get_cd(struct mmc_host *mmc)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ u32 base = host->base;
+ unsigned long flags;
+ int present = 1;
+
+ /* for sdio, MSDC_REMOVABLE not set, always return 1 */
+ if (!(host->hw->flags & MSDC_REMOVABLE)) {
+ /* For sdio, read H/W always get<1>, but may timeout some times */
+#if 1
+ host->card_inserted = 1;
+ return 1;
+#else
+ host->card_inserted = (host->pm_state.event == PM_EVENT_USER_RESUME) ? 1 : 0;
+ INIT_MSG("sdio ops_get_cd<%d>", host->card_inserted);
+ return host->card_inserted;
+#endif
+ }
+
+ /* MSDC_CD_PIN_EN set for card */
+ if (host->hw->flags & MSDC_CD_PIN_EN) {
+ spin_lock_irqsave(&host->lock, flags);
+#if 0
+ present = host->card_inserted; /* why not read from H/W: Fix me*/
+#else
+ // CD
+ if (cd_active_low)
+ present = (sdr_read32(MSDC_PS) & MSDC_PS_CDSTS) ? 0 : 1;
+ else
+ present = (sdr_read32(MSDC_PS) & MSDC_PS_CDSTS) ? 1 : 0;
+ host->card_inserted = present;
+#endif
+ spin_unlock_irqrestore(&host->lock, flags);
+ } else {
+ present = 0; /* TODO? Check DAT3 pins for card detection */
+ }
+
+ INIT_MSG("ops_get_cd return<%d>", present);
+ return present;
+}
+
+/* ops.enable_sdio_irq */
+static void msdc_ops_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ struct msdc_hw *hw = host->hw;
+ u32 base = host->base;
+ u32 tmp;
+
+ if (hw->flags & MSDC_EXT_SDIO_IRQ) { /* yes for sdio */
+ if (enable) {
+ hw->enable_sdio_eirq(); /* combo_sdio_enable_eirq */
+ } else {
+ hw->disable_sdio_eirq(); /* combo_sdio_disable_eirq */
+ }
+ } else {
+ ERR_MSG("XXX "); /* so never enter here */
+ tmp = sdr_read32(SDC_CFG);
+ /* FIXME. Need to interrupt gap detection */
+ if (enable) {
+ tmp |= (SDC_CFG_SDIOIDE | SDC_CFG_SDIOINTWKUP);
+ } else {
+ tmp &= ~(SDC_CFG_SDIOIDE | SDC_CFG_SDIOINTWKUP);
+ }
+ sdr_write32(SDC_CFG, tmp);
+ }
+}
+
+static struct mmc_host_ops mt_msdc_ops = {
+ .request = msdc_ops_request,
+ .set_ios = msdc_ops_set_ios,
+ .get_ro = msdc_ops_get_ro,
+ .get_cd = msdc_ops_get_cd,
+ .enable_sdio_irq = msdc_ops_enable_sdio_irq,
+};
+
+/*--------------------------------------------------------------------------*/
+/* interrupt handler */
+/*--------------------------------------------------------------------------*/
+static irqreturn_t msdc_irq(int irq, void *dev_id)
+{
+ struct msdc_host *host = (struct msdc_host *)dev_id;
+ struct mmc_data *data = host->data;
+ struct mmc_command *cmd = host->cmd;
+ u32 base = host->base;
+
+ u32 cmdsts = MSDC_INT_RSPCRCERR | MSDC_INT_CMDTMO | MSDC_INT_CMDRDY |
+ MSDC_INT_ACMDCRCERR | MSDC_INT_ACMDTMO | MSDC_INT_ACMDRDY |
+ MSDC_INT_ACMD19_DONE;
+ u32 datsts = MSDC_INT_DATCRCERR |MSDC_INT_DATTMO;
+
+ u32 intsts = sdr_read32(MSDC_INT);
+ u32 inten = sdr_read32(MSDC_INTEN); inten &= intsts;
+
+ sdr_write32(MSDC_INT, intsts); /* clear interrupts */
+ /* MSG will cause fatal error */
+
+ /* card change interrupt */
+ if (intsts & MSDC_INT_CDSC){
+ if (mtk_sw_poll)
+ return IRQ_HANDLED;
+ IRQ_MSG("MSDC_INT_CDSC irq<0x%.8x>", intsts);
+#if 0 /* ---/+++ by chhung: fix slot mechanical bounce issue */
+ tasklet_hi_schedule(&host->card_tasklet);
+#else
+ schedule_delayed_work(&host->card_delaywork, HZ);
+#endif
+ /* tuning when plug card ? */
+ }
+
+ /* sdio interrupt */
+ if (intsts & MSDC_INT_SDIOIRQ){
+ IRQ_MSG("XXX MSDC_INT_SDIOIRQ"); /* seems not sdio irq */
+ //mmc_signal_sdio_irq(host->mmc);
+ }
+
+ /* transfer complete interrupt */
+ if (data != NULL) {
+ if (inten & MSDC_INT_XFER_COMPL) {
+ data->bytes_xfered = host->dma.xfersz;
+ complete(&host->xfer_done);
+ }
+
+ if (intsts & datsts) {
+ /* do basic reset, or stop command will sdc_busy */
+ msdc_reset();
+ msdc_clr_fifo();
+ msdc_clr_int();
+ atomic_set(&host->abort, 1); /* For PIO mode exit */
+
+ if (intsts & MSDC_INT_DATTMO){
+ IRQ_MSG("XXX CMD<%d> MSDC_INT_DATTMO", host->mrq->cmd->opcode);
+ data->error = (unsigned int)-ETIMEDOUT;
+ }
+ else if (intsts & MSDC_INT_DATCRCERR){
+ IRQ_MSG("XXX CMD<%d> MSDC_INT_DATCRCERR, SDC_DCRC_STS<0x%x>", host->mrq->cmd->opcode, sdr_read32(SDC_DCRC_STS));
+ data->error = (unsigned int)-EIO;
+ }
+
+ //if(sdr_read32(MSDC_INTEN) & MSDC_INT_XFER_COMPL) {
+ if (host->dma_xfer) {
+ complete(&host->xfer_done); /* Read CRC come fast, XFER_COMPL not enabled */
+ } /* PIO mode can't do complete, because not init */
+ }
+ }
+
+ /* command interrupts */
+ if ((cmd != NULL) && (intsts & cmdsts)) {
+ if ((intsts & MSDC_INT_CMDRDY) || (intsts & MSDC_INT_ACMDRDY) ||
+ (intsts & MSDC_INT_ACMD19_DONE)) {
+ u32 *rsp = &cmd->resp[0];
+
+ switch (host->cmd_rsp) {
+ case RESP_NONE:
+ break;
+ case RESP_R2:
+ *rsp++ = sdr_read32(SDC_RESP3); *rsp++ = sdr_read32(SDC_RESP2);
+ *rsp++ = sdr_read32(SDC_RESP1); *rsp++ = sdr_read32(SDC_RESP0);
+ break;
+ default: /* Response types 1, 3, 4, 5, 6, 7(1b) */
+ if ((intsts & MSDC_INT_ACMDRDY) || (intsts & MSDC_INT_ACMD19_DONE)) {
+ *rsp = sdr_read32(SDC_ACMD_RESP);
+ } else {
+ *rsp = sdr_read32(SDC_RESP0);
+ }
+ break;
+ }
+ } else if ((intsts & MSDC_INT_RSPCRCERR) || (intsts & MSDC_INT_ACMDCRCERR)) {
+ if(intsts & MSDC_INT_ACMDCRCERR){
+ IRQ_MSG("XXX CMD<%d> MSDC_INT_ACMDCRCERR",cmd->opcode);
+ }
+ else {
+ IRQ_MSG("XXX CMD<%d> MSDC_INT_RSPCRCERR",cmd->opcode);
+ }
+ cmd->error = (unsigned int)-EIO;
+ } else if ((intsts & MSDC_INT_CMDTMO) || (intsts & MSDC_INT_ACMDTMO)) {
+ if(intsts & MSDC_INT_ACMDTMO){
+ IRQ_MSG("XXX CMD<%d> MSDC_INT_ACMDTMO",cmd->opcode);
+ }
+ else {
+ IRQ_MSG("XXX CMD<%d> MSDC_INT_CMDTMO",cmd->opcode);
+ }
+ cmd->error = (unsigned int)-ETIMEDOUT;
+ msdc_reset();
+ msdc_clr_fifo();
+ msdc_clr_int();
+ }
+ complete(&host->cmd_done);
+ }
+
+ /* mmc irq interrupts */
+ if (intsts & MSDC_INT_MMCIRQ) {
+ printk(KERN_INFO "msdc[%d] MMCIRQ: SDC_CSTS=0x%.8x\r\n", host->id, sdr_read32(SDC_CSTS));
+ }
+
+#ifdef MT6575_SD_DEBUG
+ {
+/* msdc_int_reg *int_reg = (msdc_int_reg*)&intsts;*/
+ N_MSG(INT, "IRQ_EVT(0x%x): MMCIRQ(%d) CDSC(%d), ACRDY(%d), ACTMO(%d), ACCRE(%d) AC19DN(%d)",
+ intsts,
+ int_reg->mmcirq,
+ int_reg->cdsc,
+ int_reg->atocmdrdy,
+ int_reg->atocmdtmo,
+ int_reg->atocmdcrc,
+ int_reg->atocmd19done);
+ N_MSG(INT, "IRQ_EVT(0x%x): SDIO(%d) CMDRDY(%d), CMDTMO(%d), RSPCRC(%d), CSTA(%d)",
+ intsts,
+ int_reg->sdioirq,
+ int_reg->cmdrdy,
+ int_reg->cmdtmo,
+ int_reg->rspcrc,
+ int_reg->csta);
+ N_MSG(INT, "IRQ_EVT(0x%x): XFCMP(%d) DXDONE(%d), DATTMO(%d), DATCRC(%d), DMAEMP(%d)",
+ intsts,
+ int_reg->xfercomp,
+ int_reg->dxferdone,
+ int_reg->dattmo,
+ int_reg->datcrc,
+ int_reg->dmaqempty);
+
+ }
+#endif
+
+ return IRQ_HANDLED;
+}
+
+/*--------------------------------------------------------------------------*/
+/* platform_driver members */
+/*--------------------------------------------------------------------------*/
+/* called by msdc_drv_probe/remove */
+static void msdc_enable_cd_irq(struct msdc_host *host, int enable)
+{
+ struct msdc_hw *hw = host->hw;
+ u32 base = host->base;
+
+ /* for sdio, not set */
+ if ((hw->flags & MSDC_CD_PIN_EN) == 0) {
+ /* Pull down card detection pin since it is not avaiable */
+ /*
+ if (hw->config_gpio_pin)
+ hw->config_gpio_pin(MSDC_CD_PIN, GPIO_PULL_DOWN);
+ */
+ sdr_clr_bits(MSDC_PS, MSDC_PS_CDEN);
+ sdr_clr_bits(MSDC_INTEN, MSDC_INTEN_CDSC);
+ sdr_clr_bits(SDC_CFG, SDC_CFG_INSWKUP);
+ return;
+ }
+
+ N_MSG(CFG, "CD IRQ Eanable(%d)", enable);
+
+ if (enable) {
+ if (hw->enable_cd_eirq) { /* not set, never enter */
+ hw->enable_cd_eirq();
+ } else {
+ /* card detection circuit relies on the core power so that the core power
+ * shouldn't be turned off. Here adds a reference count to keep
+ * the core power alive.
+ */
+ //msdc_vcore_on(host); //did in msdc_init_hw()
+
+ if (hw->config_gpio_pin) /* NULL */
+ hw->config_gpio_pin(MSDC_CD_PIN, GPIO_PULL_UP);
+
+ sdr_set_field(MSDC_PS, MSDC_PS_CDDEBOUNCE, DEFAULT_DEBOUNCE);
+ sdr_set_bits(MSDC_PS, MSDC_PS_CDEN);
+ sdr_set_bits(MSDC_INTEN, MSDC_INTEN_CDSC);
+ sdr_set_bits(SDC_CFG, SDC_CFG_INSWKUP); /* not in document! Fix me */
+ }
+ } else {
+ if (hw->disable_cd_eirq) {
+ hw->disable_cd_eirq();
+ } else {
+ if (hw->config_gpio_pin) /* NULL */
+ hw->config_gpio_pin(MSDC_CD_PIN, GPIO_PULL_DOWN);
+
+ sdr_clr_bits(SDC_CFG, SDC_CFG_INSWKUP);
+ sdr_clr_bits(MSDC_PS, MSDC_PS_CDEN);
+ sdr_clr_bits(MSDC_INTEN, MSDC_INTEN_CDSC);
+
+ /* Here decreases a reference count to core power since card
+ * detection circuit is shutdown.
+ */
+ //msdc_vcore_off(host);
+ }
+ }
+}
+
+/* called by msdc_drv_probe */
+static void msdc_init_hw(struct msdc_host *host)
+{
+ u32 base = host->base;
+ struct msdc_hw *hw = host->hw;
+
+#ifdef MT6575_SD_DEBUG
+ msdc_reg[host->id] = (struct msdc_regs *)host->base;
+#endif
+
+ /* Power on */
+#if 0 /* --- by chhung */
+ msdc_vcore_on(host);
+ msdc_pin_reset(host, MSDC_PIN_PULL_UP);
+ msdc_select_clksrc(host, hw->clk_src);
+ enable_clock(PERI_MSDC0_PDN + host->id, "SD");
+ msdc_vdd_on(host);
+#endif /* end of --- */
+ /* Configure to MMC/SD mode */
+ sdr_set_field(MSDC_CFG, MSDC_CFG_MODE, MSDC_SDMMC);
+
+ /* Reset */
+ msdc_reset();
+ msdc_clr_fifo();
+
+ /* Disable card detection */
+ sdr_clr_bits(MSDC_PS, MSDC_PS_CDEN);
+
+ /* Disable and clear all interrupts */
+ sdr_clr_bits(MSDC_INTEN, sdr_read32(MSDC_INTEN));
+ sdr_write32(MSDC_INT, sdr_read32(MSDC_INT));
+
+#if 1
+ /* reset tuning parameter */
+ sdr_write32(MSDC_PAD_CTL0, 0x00090000);
+ sdr_write32(MSDC_PAD_CTL1, 0x000A0000);
+ sdr_write32(MSDC_PAD_CTL2, 0x000A0000);
+ // sdr_write32(MSDC_PAD_TUNE, 0x00000000);
+ sdr_write32(MSDC_PAD_TUNE, 0x84101010); // for MT7620 E2 and afterward
+ // sdr_write32(MSDC_DAT_RDDLY0, 0x00000000);
+ sdr_write32(MSDC_DAT_RDDLY0, 0x10101010); // for MT7620 E2 and afterward
+ sdr_write32(MSDC_DAT_RDDLY1, 0x00000000);
+ sdr_write32(MSDC_IOCON, 0x00000000);
+#if 0 // use MT7620 default value: 0x403c004f
+ sdr_write32(MSDC_PATCH_BIT0, 0x003C000F); /* bit0 modified: Rx Data Clock Source: 1 -> 2.0*/
+#endif
+
+ if (sdr_read32(MSDC_ECO_VER) >= 4) {
+ if (host->id == 1) {
+ sdr_set_field(MSDC_PATCH_BIT1, MSDC_PATCH_BIT1_WRDAT_CRCS, 1);
+ sdr_set_field(MSDC_PATCH_BIT1, MSDC_PATCH_BIT1_CMD_RSP, 1);
+
+ /* internal clock: latch read data */
+ sdr_set_bits(MSDC_PATCH_BIT0, MSDC_PATCH_BIT_CKGEN_CK);
+ }
+ }
+#endif
+
+ /* for safety, should clear SDC_CFG.SDIO_INT_DET_EN & set SDC_CFG.SDIO in
+ pre-loader,uboot,kernel drivers. and SDC_CFG.SDIO_INT_DET_EN will be only
+ set when kernel driver wants to use SDIO bus interrupt */
+ /* Configure to enable SDIO mode. it's must otherwise sdio cmd5 failed */
+ sdr_set_bits(SDC_CFG, SDC_CFG_SDIO);
+
+ /* disable detect SDIO device interupt function */
+ sdr_clr_bits(SDC_CFG, SDC_CFG_SDIOIDE);
+
+ /* eneable SMT for glitch filter */
+ sdr_set_bits(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKSMT);
+ sdr_set_bits(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDSMT);
+ sdr_set_bits(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATSMT);
+
+#if 1
+ /* set clk, cmd, dat pad driving */
+ sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKDRVN, hw->clk_drv);
+ sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKDRVP, hw->clk_drv);
+ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDDRVN, hw->cmd_drv);
+ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDDRVP, hw->cmd_drv);
+ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATDRVN, hw->dat_drv);
+ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATDRVP, hw->dat_drv);
+#else
+ sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKDRVN, 0);
+ sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKDRVP, 0);
+ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDDRVN, 0);
+ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDDRVP, 0);
+ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATDRVN, 0);
+ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATDRVP, 0);
+#endif
+
+ /* set sampling edge */
+
+ /* write crc timeout detection */
+ sdr_set_field(MSDC_PATCH_BIT0, 1 << 30, 1);
+
+ /* Configure to default data timeout */
+ sdr_set_field(SDC_CFG, SDC_CFG_DTOC, DEFAULT_DTOC);
+
+ msdc_set_buswidth(host, MMC_BUS_WIDTH_1);
+
+ N_MSG(FUC, "init hardware done!");
+}
+
+/* called by msdc_drv_remove */
+static void msdc_deinit_hw(struct msdc_host *host)
+{
+ u32 base = host->base;
+
+ /* Disable and clear all interrupts */
+ sdr_clr_bits(MSDC_INTEN, sdr_read32(MSDC_INTEN));
+ sdr_write32(MSDC_INT, sdr_read32(MSDC_INT));
+
+ /* Disable card detection */
+ msdc_enable_cd_irq(host, 0);
+ // msdc_set_power_mode(host, MMC_POWER_OFF); /* make sure power down */ /* --- by chhung */
+}
+
+/* init gpd and bd list in msdc_drv_probe */
+static void msdc_init_gpd_bd(struct msdc_host *host, struct msdc_dma *dma)
+{
+ gpd_t *gpd = dma->gpd;
+ bd_t *bd = dma->bd;
+ bd_t *ptr, *prev;
+
+ /* we just support one gpd */
+ int bdlen = MAX_BD_PER_GPD;
+
+ /* init the 2 gpd */
+ memset(gpd, 0, sizeof(gpd_t) * 2);
+ //gpd->next = (void *)virt_to_phys(gpd + 1); /* pointer to a null gpd, bug! kmalloc <-> virt_to_phys */
+ //gpd->next = (dma->gpd_addr + 1); /* bug */
+ gpd->next = (void *)((u32)dma->gpd_addr + sizeof(gpd_t));
+
+ //gpd->intr = 0;
+ gpd->bdp = 1; /* hwo, cs, bd pointer */
+ //gpd->ptr = (void*)virt_to_phys(bd);
+ gpd->ptr = (void *)dma->bd_addr; /* physical address */
+
+ memset(bd, 0, sizeof(bd_t) * bdlen);
+ ptr = bd + bdlen - 1;
+ //ptr->eol = 1; /* 0 or 1 [Fix me]*/
+ //ptr->next = 0;
+
+ while (ptr != bd) {
+ prev = ptr - 1;
+ prev->next = (void *)(dma->bd_addr + sizeof(bd_t) *(ptr - bd));
+ ptr = prev;
+ }
+}
+
+static int msdc_drv_probe(struct platform_device *pdev)
+{
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ __iomem void *base;
+ struct mmc_host *mmc;
+ struct resource *mem;
+ struct msdc_host *host;
+ struct msdc_hw *hw;
+ int ret, irq;
+
+ pdev->dev.platform_data = &msdc0_hw;
+
+ if (of_property_read_bool(pdev->dev.of_node, "mtk,wp-en"))
+ msdc0_hw.flags |= MSDC_WP_PIN_EN;
+
+ /* Allocate MMC host for this device */
+ mmc = mmc_alloc_host(sizeof(struct msdc_host), &pdev->dev);
+ if (!mmc) return -ENOMEM;
+
+ hw = (struct msdc_hw*)pdev->dev.platform_data;
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+
+ //BUG_ON((!hw) || (!mem) || (irq < 0)); /* --- by chhung */
+
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ /* Set host parameters to mmc */
+ mmc->ops = &mt_msdc_ops;
+ mmc->f_min = HOST_MIN_MCLK;
+ mmc->f_max = HOST_MAX_MCLK;
+ mmc->ocr_avail = MSDC_OCR_AVAIL;
+
+ /* For sd card: MSDC_SYS_SUSPEND | MSDC_WP_PIN_EN | MSDC_CD_PIN_EN | MSDC_REMOVABLE | MSDC_HIGHSPEED,
+ For sdio : MSDC_EXT_SDIO_IRQ | MSDC_HIGHSPEED */
+ if (hw->flags & MSDC_HIGHSPEED) {
+ mmc->caps = MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
+ }
+ if (hw->data_pins == 4) { /* current data_pins are all 4*/
+ mmc->caps |= MMC_CAP_4_BIT_DATA;
+ } else if (hw->data_pins == 8) {
+ mmc->caps |= MMC_CAP_8_BIT_DATA;
+ }
+ if ((hw->flags & MSDC_SDIO_IRQ) || (hw->flags & MSDC_EXT_SDIO_IRQ))
+ mmc->caps |= MMC_CAP_SDIO_IRQ; /* yes for sdio */
+
+ cd_active_low = !of_property_read_bool(pdev->dev.of_node, "mediatek,cd-high");
+ mtk_sw_poll = of_property_read_bool(pdev->dev.of_node, "mediatek,cd-poll");
+
+ if (mtk_sw_poll)
+ mmc->caps |= MMC_CAP_NEEDS_POLL;
+
+ /* MMC core transfer sizes tunable parameters */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3,10,0)
+ mmc->max_segs = MAX_HW_SGMTS;
+#else
+ mmc->max_hw_segs = MAX_HW_SGMTS;
+ mmc->max_phys_segs = MAX_PHY_SGMTS;
+#endif
+ mmc->max_seg_size = MAX_SGMT_SZ;
+ mmc->max_blk_size = HOST_MAX_BLKSZ;
+ mmc->max_req_size = MAX_REQ_SZ;
+ mmc->max_blk_count = mmc->max_req_size;
+
+ host = mmc_priv(mmc);
+ host->hw = hw;
+ host->mmc = mmc;
+ host->id = pdev->id;
+ if (host->id < 0 || host->id >= 4)
+ host->id = 0;
+ host->error = 0;
+ host->irq = irq;
+ host->base = (unsigned long) base;
+ host->mclk = 0; /* mclk: the request clock of mmc sub-system */
+ host->hclk = hclks[hw->clk_src]; /* hclk: clock of clock source to msdc controller */
+ host->sclk = 0; /* sclk: the really clock after divition */
+ host->pm_state = PMSG_RESUME;
+ host->suspend = 0;
+ host->core_clkon = 0;
+ host->card_clkon = 0;
+ host->core_power = 0;
+ host->power_mode = MMC_POWER_OFF;
+// host->card_inserted = hw->flags & MSDC_REMOVABLE ? 0 : 1;
+ host->timeout_ns = 0;
+ host->timeout_clks = DEFAULT_DTOC * 65536;
+
+ host->mrq = NULL;
+ //init_MUTEX(&host->sem); /* we don't need to support multiple threads access */
+
+ host->dma.used_gpd = 0;
+ host->dma.used_bd = 0;
+ mmc_dev(mmc)->dma_mask = NULL;
+
+ /* using dma_alloc_coherent*/ /* todo: using 1, for all 4 slots */
+ host->dma.gpd = dma_alloc_coherent(NULL, MAX_GPD_NUM * sizeof(gpd_t), &host->dma.gpd_addr, GFP_KERNEL);
+ host->dma.bd = dma_alloc_coherent(NULL, MAX_BD_NUM * sizeof(bd_t), &host->dma.bd_addr, GFP_KERNEL);
+ BUG_ON((!host->dma.gpd) || (!host->dma.bd));
+ msdc_init_gpd_bd(host, &host->dma);
+ /*for emmc*/
+ msdc_6575_host[pdev->id] = host;
+
+#if 0
+ tasklet_init(&host->card_tasklet, msdc_tasklet_card, (ulong)host);
+#else
+ INIT_DELAYED_WORK(&host->card_delaywork, msdc_tasklet_card);
+#endif
+ spin_lock_init(&host->lock);
+ msdc_init_hw(host);
+
+ if (ralink_soc == MT762X_SOC_MT7621AT)
+ ret = request_irq((unsigned int)irq, msdc_irq, 0, dev_name(&pdev->dev), host);
+ else
+ ret = request_irq((unsigned int)irq, msdc_irq, IRQF_TRIGGER_LOW, dev_name(&pdev->dev), host);
+
+ if (ret) goto release;
+ // mt65xx_irq_unmask(irq); /* --- by chhung */
+
+ if (hw->flags & MSDC_CD_PIN_EN) { /* not set for sdio */
+ if (hw->request_cd_eirq) { /* not set for MT6575 */
+ hw->request_cd_eirq(msdc_eirq_cd, (void*)host); /* msdc_eirq_cd will not be used! */
+ }
+ }
+
+ if (hw->request_sdio_eirq) /* set to combo_sdio_request_eirq() for WIFI */
+ hw->request_sdio_eirq(msdc_eirq_sdio, (void*)host); /* msdc_eirq_sdio() will be called when EIRQ */
+
+ if (hw->register_pm) {/* yes for sdio */
+#ifdef CONFIG_PM
+ hw->register_pm(msdc_pm, (void*)host); /* combo_sdio_register_pm() */
+#endif
+ if(hw->flags & MSDC_SYS_SUSPEND) { /* will not set for WIFI */
+ ERR_MSG("MSDC_SYS_SUSPEND and register_pm both set");
+ }
+ //mmc->pm_flags |= MMC_PM_IGNORE_PM_NOTIFY; /* pm not controlled by system but by client. */ /* --- by chhung */
+ }
+
+ platform_set_drvdata(pdev, mmc);
+
+ ret = mmc_add_host(mmc);
+ if (ret) goto free_irq;
+
+ /* Config card detection pin and enable interrupts */
+ if (hw->flags & MSDC_CD_PIN_EN) { /* set for card */
+ msdc_enable_cd_irq(host, 1);
+ } else {
+ msdc_enable_cd_irq(host, 0);
+ }
+
+ return 0;
+
+free_irq:
+ free_irq(irq, host);
+release:
+ platform_set_drvdata(pdev, NULL);
+ msdc_deinit_hw(host);
+
+#if 0
+ tasklet_kill(&host->card_tasklet);
+#else
+ cancel_delayed_work_sync(&host->card_delaywork);
+#endif
+
+ if (mem)
+ release_mem_region(mem->start, mem->end - mem->start + 1);
+
+ mmc_free_host(mmc);
+
+ return ret;
+}
+
+/* 4 device share one driver, using "drvdata" to show difference */
+static int msdc_drv_remove(struct platform_device *pdev)
+{
+ struct mmc_host *mmc;
+ struct msdc_host *host;
+ struct resource *mem;
+
+ mmc = platform_get_drvdata(pdev);
+ BUG_ON(!mmc);
+
+ host = mmc_priv(mmc);
+ BUG_ON(!host);
+
+ ERR_MSG("removed !!!");
+
+ platform_set_drvdata(pdev, NULL);
+ mmc_remove_host(host->mmc);
+ msdc_deinit_hw(host);
+
+#if 0
+ tasklet_kill(&host->card_tasklet);
+#else
+ cancel_delayed_work_sync(&host->card_delaywork);
+#endif
+ free_irq(host->irq, host);
+
+ dma_free_coherent(NULL, MAX_GPD_NUM * sizeof(gpd_t), host->dma.gpd, host->dma.gpd_addr);
+ dma_free_coherent(NULL, MAX_BD_NUM * sizeof(bd_t), host->dma.bd, host->dma.bd_addr);
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (mem)
+ release_mem_region(mem->start, mem->end - mem->start + 1);
+
+ mmc_free_host(host->mmc);
+
+ return 0;
+}
+
+/* Fix me: Power Flow */
+#ifdef CONFIG_PM
+static int msdc_drv_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ int ret = 0;
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+ struct msdc_host *host = mmc_priv(mmc);
+
+ if (mmc && state.event == PM_EVENT_SUSPEND && (host->hw->flags & MSDC_SYS_SUSPEND)) { /* will set for card */
+ msdc_pm(state, (void*)host);
+ }
+
+ return ret;
+}
+
+static int msdc_drv_resume(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+ struct msdc_host *host = mmc_priv(mmc);
+ struct pm_message state;
+
+ state.event = PM_EVENT_RESUME;
+ if (mmc && (host->hw->flags & MSDC_SYS_SUSPEND)) {/* will set for card */
+ msdc_pm(state, (void*)host);
+ }
+
+ /* This mean WIFI not controller by PM */
+
+ return ret;
+}
+#endif
+
+static const struct of_device_id mt7620_sdhci_match[] = {
+ { .compatible = "ralink,mt7620-sdhci" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt7620_sdhci_match);
+
+static struct platform_driver mt_msdc_driver = {
+ .probe = msdc_drv_probe,
+ .remove = msdc_drv_remove,
+#ifdef CONFIG_PM
+ .suspend = msdc_drv_suspend,
+ .resume = msdc_drv_resume,
+#endif
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = mt7620_sdhci_match,
+ },
+};
+
+/*--------------------------------------------------------------------------*/
+/* module init/exit */
+/*--------------------------------------------------------------------------*/
+static int __init mt_msdc_init(void)
+{
+ int ret;
+/* +++ by chhung */
+ u32 reg;
+
+#if defined (CONFIG_MTD_ANY_RALINK)
+ extern int ra_check_flash_type(void);
+ if(ra_check_flash_type() == 2) { /* NAND */
+ printk("%s: !!!!! SDXC Module Initialize Fail !!!!!", __func__);
+ return 0;
+ }
+#endif
+ printk("MTK MSDC device init.\n");
+ mtk_sd_device.dev.platform_data = &msdc0_hw;
+if (ralink_soc == MT762X_SOC_MT7620A || ralink_soc == MT762X_SOC_MT7621AT) {
+//#if defined (CONFIG_RALINK_MT7620) || defined (CONFIG_RALINK_MT7621)
+ reg = sdr_read32((volatile u32*)(RALINK_SYSCTL_BASE + 0x60)) & ~(0x3<<18);
+//#if defined (CONFIG_RALINK_MT7620)
+ if (ralink_soc == MT762X_SOC_MT7620A)
+ reg |= 0x1<<18;
+//#endif
+} else {
+//#elif defined (CONFIG_RALINK_MT7628)
+ /* TODO: maybe omitted when RAether already toggle AGPIO_CFG */
+ reg = sdr_read32((volatile u32*)(RALINK_SYSCTL_BASE + 0x3c));
+ reg |= 0x1e << 16;
+ sdr_write32((volatile u32*)(RALINK_SYSCTL_BASE + 0x3c), reg);
+
+ reg = sdr_read32((volatile u32*)(RALINK_SYSCTL_BASE + 0x60)) & ~(0x3<<10);
+#if defined (CONFIG_MTK_MMC_EMMC_8BIT)
+ reg |= 0x3<<26 | 0x3<<28 | 0x3<<30;
+ msdc0_hw.data_pins = 8,
+#endif
+//#endif
+}
+ sdr_write32((volatile u32*)(RALINK_SYSCTL_BASE + 0x60), reg);
+ //platform_device_register(&mtk_sd_device);
+/* end of +++ */
+
+ ret = platform_driver_register(&mt_msdc_driver);
+ if (ret) {
+ printk(KERN_ERR DRV_NAME ": Can't register driver");
+ return ret;
+ }
+ printk(KERN_INFO DRV_NAME ": MediaTek MT6575 MSDC Driver\n");
+
+#if defined (MT6575_SD_DEBUG)
+ msdc_debug_proc_init();
+#endif
+ return 0;
+}
+
+static void __exit mt_msdc_exit(void)
+{
+// platform_device_unregister(&mtk_sd_device);
+ platform_driver_unregister(&mt_msdc_driver);
+}
+
+module_init(mt_msdc_init);
+module_exit(mt_msdc_exit);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("MediaTek MT6575 SD/MMC Card Driver");
+MODULE_AUTHOR("Infinity Chen <infinity.chen@mediatek.com>");
+
+EXPORT_SYMBOL(msdc_6575_host);
diff --git a/drivers/staging/mt7621-pci/Makefile b/drivers/staging/mt7621-pci/Makefile
new file mode 100644
index 000000000000..607b84bedcc3
--- /dev/null
+++ b/drivers/staging/mt7621-pci/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SOC_MT7621) += pci-mt7621.o
diff --git a/drivers/staging/mt7621-pci/TODO b/drivers/staging/mt7621-pci/TODO
new file mode 100644
index 000000000000..cf30f629b9fd
--- /dev/null
+++ b/drivers/staging/mt7621-pci/TODO
@@ -0,0 +1,12 @@
+
+- general code review and cleanup
+- can this be converted to not require PCI_DRIVERS_LEGACY ??
+ The irq returned by pcibios_map_irq is a "hwirq" (hardware irq number)
+ and pci_assign_irq() assigns this directly to dev->irq, which
+ expects a "virq" (virtual irq number). These numbers are different
+ on MIPS. There is a gross hack to make it work on one
+ specific platform, so it can be tested.
+- Should this be merged with arch/mips/pci/pci-mt7620.c ??
+- ensure device-tree requirements are documented
+
+Cc: NeilBrown <neil@brown.name>
diff --git a/drivers/staging/mt7621-pci/pci-mt7621.c b/drivers/staging/mt7621-pci/pci-mt7621.c
new file mode 100644
index 000000000000..1fa41eb8a87f
--- /dev/null
+++ b/drivers/staging/mt7621-pci/pci-mt7621.c
@@ -0,0 +1,840 @@
+/**************************************************************************
+ *
+ * BRIEF MODULE DESCRIPTION
+ * PCI init for Ralink RT2880 solution
+ *
+ * Copyright 2007 Ralink Inc. (bruce_chang@ralinktech.com.tw)
+ *
+ * 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.
+ *
+ *
+ **************************************************************************
+ * May 2007 Bruce Chang
+ * Initial Release
+ *
+ * May 2009 Bruce Chang
+ * support RT2880/RT3883 PCIe
+ *
+ * May 2011 Bruce Chang
+ * support RT6855/MT7620 PCIe
+ *
+ **************************************************************************
+ */
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <asm/pci.h>
+#include <asm/io.h>
+#include <asm/mips-cm.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_pci.h>
+#include <linux/platform_device.h>
+
+#include <ralink_regs.h>
+
+extern void pcie_phy_init(void);
+extern void chk_phy_pll(void);
+
+/*
+ * These functions and structures provide the BIOS scan and mapping of the PCI
+ * devices.
+ */
+
+#define CONFIG_PCIE_PORT0
+#define CONFIG_PCIE_PORT1
+#define CONFIG_PCIE_PORT2
+#define RALINK_PCIE0_CLK_EN (1<<24)
+#define RALINK_PCIE1_CLK_EN (1<<25)
+#define RALINK_PCIE2_CLK_EN (1<<26)
+
+#define RALINK_PCI_CONFIG_ADDR 0x20
+#define RALINK_PCI_CONFIG_DATA_VIRTUAL_REG 0x24
+#define SURFBOARDINT_PCIE0 11 /* PCIE0 */
+#define RALINK_INT_PCIE0 SURFBOARDINT_PCIE0
+#define RALINK_INT_PCIE1 SURFBOARDINT_PCIE1
+#define RALINK_INT_PCIE2 SURFBOARDINT_PCIE2
+#define SURFBOARDINT_PCIE1 31 /* PCIE1 */
+#define SURFBOARDINT_PCIE2 32 /* PCIE2 */
+#define RALINK_PCI_MEMBASE *(volatile u32 *)(RALINK_PCI_BASE + 0x0028)
+#define RALINK_PCI_IOBASE *(volatile u32 *)(RALINK_PCI_BASE + 0x002C)
+#define RALINK_PCIE0_RST (1<<24)
+#define RALINK_PCIE1_RST (1<<25)
+#define RALINK_PCIE2_RST (1<<26)
+#define RALINK_SYSCTL_BASE 0xBE000000
+
+#define RALINK_PCI_PCICFG_ADDR *(volatile u32 *)(RALINK_PCI_BASE + 0x0000)
+#define RALINK_PCI_PCIMSK_ADDR *(volatile u32 *)(RALINK_PCI_BASE + 0x000C)
+#define RALINK_PCI_BASE 0xBE140000
+
+#define RALINK_PCIEPHY_P0P1_CTL_OFFSET (RALINK_PCI_BASE + 0x9000)
+#define RT6855_PCIE0_OFFSET 0x2000
+#define RT6855_PCIE1_OFFSET 0x3000
+#define RT6855_PCIE2_OFFSET 0x4000
+
+#define RALINK_PCI0_BAR0SETUP_ADDR *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE0_OFFSET + 0x0010)
+#define RALINK_PCI0_IMBASEBAR0_ADDR *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE0_OFFSET + 0x0018)
+#define RALINK_PCI0_ID *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE0_OFFSET + 0x0030)
+#define RALINK_PCI0_CLASS *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE0_OFFSET + 0x0034)
+#define RALINK_PCI0_SUBID *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE0_OFFSET + 0x0038)
+#define RALINK_PCI0_STATUS *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE0_OFFSET + 0x0050)
+#define RALINK_PCI0_DERR *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE0_OFFSET + 0x0060)
+#define RALINK_PCI0_ECRC *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE0_OFFSET + 0x0064)
+
+#define RALINK_PCI1_BAR0SETUP_ADDR *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE1_OFFSET + 0x0010)
+#define RALINK_PCI1_IMBASEBAR0_ADDR *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE1_OFFSET + 0x0018)
+#define RALINK_PCI1_ID *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE1_OFFSET + 0x0030)
+#define RALINK_PCI1_CLASS *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE1_OFFSET + 0x0034)
+#define RALINK_PCI1_SUBID *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE1_OFFSET + 0x0038)
+#define RALINK_PCI1_STATUS *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE1_OFFSET + 0x0050)
+#define RALINK_PCI1_DERR *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE1_OFFSET + 0x0060)
+#define RALINK_PCI1_ECRC *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE1_OFFSET + 0x0064)
+
+#define RALINK_PCI2_BAR0SETUP_ADDR *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE2_OFFSET + 0x0010)
+#define RALINK_PCI2_IMBASEBAR0_ADDR *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE2_OFFSET + 0x0018)
+#define RALINK_PCI2_ID *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE2_OFFSET + 0x0030)
+#define RALINK_PCI2_CLASS *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE2_OFFSET + 0x0034)
+#define RALINK_PCI2_SUBID *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE2_OFFSET + 0x0038)
+#define RALINK_PCI2_STATUS *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE2_OFFSET + 0x0050)
+#define RALINK_PCI2_DERR *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE2_OFFSET + 0x0060)
+#define RALINK_PCI2_ECRC *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE2_OFFSET + 0x0064)
+
+#define RALINK_PCIEPHY_P0P1_CTL_OFFSET (RALINK_PCI_BASE + 0x9000)
+#define RALINK_PCIEPHY_P2_CTL_OFFSET (RALINK_PCI_BASE + 0xA000)
+
+
+#define MV_WRITE(ofs, data) \
+ *(volatile u32 *)(RALINK_PCI_BASE+(ofs)) = cpu_to_le32(data)
+#define MV_READ(ofs, data) \
+ *(data) = le32_to_cpu(*(volatile u32 *)(RALINK_PCI_BASE+(ofs)))
+#define MV_READ_DATA(ofs) \
+ le32_to_cpu(*(volatile u32 *)(RALINK_PCI_BASE+(ofs)))
+
+#define MV_WRITE_16(ofs, data) \
+ *(volatile u16 *)(RALINK_PCI_BASE+(ofs)) = cpu_to_le16(data)
+#define MV_READ_16(ofs, data) \
+ *(data) = le16_to_cpu(*(volatile u16 *)(RALINK_PCI_BASE+(ofs)))
+
+#define MV_WRITE_8(ofs, data) \
+ *(volatile u8 *)(RALINK_PCI_BASE+(ofs)) = data
+#define MV_READ_8(ofs, data) \
+ *(data) = *(volatile u8 *)(RALINK_PCI_BASE+(ofs))
+
+
+
+#define RALINK_PCI_MM_MAP_BASE 0x60000000
+#define RALINK_PCI_IO_MAP_BASE 0x1e160000
+
+#define RALINK_SYSTEM_CONTROL_BASE 0xbe000000
+#define GPIO_PERST
+#define ASSERT_SYSRST_PCIE(val) do { \
+ if (*(unsigned int *)(0xbe00000c) == 0x00030101) \
+ RALINK_RSTCTRL |= val; \
+ else \
+ RALINK_RSTCTRL &= ~val; \
+ } while(0)
+#define DEASSERT_SYSRST_PCIE(val) do { \
+ if (*(unsigned int *)(0xbe00000c) == 0x00030101) \
+ RALINK_RSTCTRL &= ~val; \
+ else \
+ RALINK_RSTCTRL |= val; \
+ } while(0)
+#define RALINK_SYSCFG1 *(unsigned int *)(RALINK_SYSTEM_CONTROL_BASE + 0x14)
+#define RALINK_CLKCFG1 *(unsigned int *)(RALINK_SYSTEM_CONTROL_BASE + 0x30)
+#define RALINK_RSTCTRL *(unsigned int *)(RALINK_SYSTEM_CONTROL_BASE + 0x34)
+#define RALINK_GPIOMODE *(unsigned int *)(RALINK_SYSTEM_CONTROL_BASE + 0x60)
+#define RALINK_PCIE_CLK_GEN *(unsigned int *)(RALINK_SYSTEM_CONTROL_BASE + 0x7c)
+#define RALINK_PCIE_CLK_GEN1 *(unsigned int *)(RALINK_SYSTEM_CONTROL_BASE + 0x80)
+#define PPLL_CFG1 *(unsigned int *)(RALINK_SYSTEM_CONTROL_BASE + 0x9c)
+#define PPLL_DRV *(unsigned int *)(RALINK_SYSTEM_CONTROL_BASE + 0xa0)
+//RALINK_SYSCFG1 bit
+#define RALINK_PCI_HOST_MODE_EN (1<<7)
+#define RALINK_PCIE_RC_MODE_EN (1<<8)
+//RALINK_RSTCTRL bit
+#define RALINK_PCIE_RST (1<<23)
+#define RALINK_PCI_RST (1<<24)
+//RALINK_CLKCFG1 bit
+#define RALINK_PCI_CLK_EN (1<<19)
+#define RALINK_PCIE_CLK_EN (1<<21)
+//RALINK_GPIOMODE bit
+#define PCI_SLOTx2 (1<<11)
+#define PCI_SLOTx1 (2<<11)
+//MTK PCIE PLL bit
+#define PDRV_SW_SET (1<<31)
+#define LC_CKDRVPD_ (1<<19)
+
+#define MEMORY_BASE 0x0
+static int pcie_link_status = 0;
+
+#define PCI_ACCESS_READ_1 0
+#define PCI_ACCESS_READ_2 1
+#define PCI_ACCESS_READ_4 2
+#define PCI_ACCESS_WRITE_1 3
+#define PCI_ACCESS_WRITE_2 4
+#define PCI_ACCESS_WRITE_4 5
+
+static int config_access(unsigned char access_type, struct pci_bus *bus,
+ unsigned int devfn, unsigned int where, u32 * data)
+{
+ unsigned int slot = PCI_SLOT(devfn);
+ u8 func = PCI_FUNC(devfn);
+ uint32_t address_reg, data_reg;
+ unsigned int address;
+
+ address_reg = RALINK_PCI_CONFIG_ADDR;
+ data_reg = RALINK_PCI_CONFIG_DATA_VIRTUAL_REG;
+
+ address = (((where&0xF00)>>8)<<24) |(bus->number << 16) | (slot << 11) | (func << 8) | (where & 0xfc) | 0x80000000;
+ MV_WRITE(address_reg, address);
+
+ switch(access_type) {
+ case PCI_ACCESS_WRITE_1:
+ MV_WRITE_8(data_reg+(where&0x3), *data);
+ break;
+ case PCI_ACCESS_WRITE_2:
+ MV_WRITE_16(data_reg+(where&0x3), *data);
+ break;
+ case PCI_ACCESS_WRITE_4:
+ MV_WRITE(data_reg, *data);
+ break;
+ case PCI_ACCESS_READ_1:
+ MV_READ_8( data_reg+(where&0x3), data);
+ break;
+ case PCI_ACCESS_READ_2:
+ MV_READ_16(data_reg+(where&0x3), data);
+ break;
+ case PCI_ACCESS_READ_4:
+ MV_READ(data_reg, data);
+ break;
+ default:
+ printk("no specify access type\n");
+ break;
+ }
+ return 0;
+}
+
+static int
+read_config_byte(struct pci_bus *bus, unsigned int devfn, int where, u8 * val)
+{
+ return config_access(PCI_ACCESS_READ_1, bus, devfn, (unsigned int)where, (u32 *)val);
+}
+
+static int
+read_config_word(struct pci_bus *bus, unsigned int devfn, int where, u16 * val)
+{
+ return config_access(PCI_ACCESS_READ_2, bus, devfn, (unsigned int)where, (u32 *)val);
+}
+
+static int
+read_config_dword(struct pci_bus *bus, unsigned int devfn, int where, u32 * val)
+{
+ return config_access(PCI_ACCESS_READ_4, bus, devfn, (unsigned int)where, (u32 *)val);
+}
+
+static int
+write_config_byte(struct pci_bus *bus, unsigned int devfn, int where, u8 val)
+{
+ if (config_access(PCI_ACCESS_WRITE_1, bus, devfn, (unsigned int)where, (u32 *)&val))
+ return -1;
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int
+write_config_word(struct pci_bus *bus, unsigned int devfn, int where, u16 val)
+{
+ if (config_access(PCI_ACCESS_WRITE_2, bus, devfn, where, (u32 *)&val))
+ return -1;
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int
+write_config_dword(struct pci_bus *bus, unsigned int devfn, int where, u32 val)
+{
+ if (config_access(PCI_ACCESS_WRITE_4, bus, devfn, where, &val))
+ return -1;
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+
+static int
+pci_config_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 * val)
+{
+ switch (size) {
+ case 1:
+ return read_config_byte(bus, devfn, where, (u8 *) val);
+ case 2:
+ return read_config_word(bus, devfn, where, (u16 *) val);
+ default:
+ return read_config_dword(bus, devfn, where, val);
+ }
+}
+
+static int
+pci_config_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val)
+{
+ switch (size) {
+ case 1:
+ return write_config_byte(bus, devfn, where, (u8) val);
+ case 2:
+ return write_config_word(bus, devfn, where, (u16) val);
+ default:
+ return write_config_dword(bus, devfn, where, val);
+ }
+}
+
+struct pci_ops mt7621_pci_ops= {
+ .read = pci_config_read,
+ .write = pci_config_write,
+};
+
+static struct resource mt7621_res_pci_mem1 = {
+ .name = "PCI MEM1",
+ .start = RALINK_PCI_MM_MAP_BASE,
+ .end = (u32)((RALINK_PCI_MM_MAP_BASE + (unsigned char *)0x0fffffff)),
+ .flags = IORESOURCE_MEM,
+};
+static struct resource mt7621_res_pci_io1 = {
+ .name = "PCI I/O1",
+ .start = RALINK_PCI_IO_MAP_BASE,
+ .end = (u32)((RALINK_PCI_IO_MAP_BASE + (unsigned char *)0x0ffff)),
+ .flags = IORESOURCE_IO,
+};
+
+static struct pci_controller mt7621_controller = {
+ .pci_ops = &mt7621_pci_ops,
+ .mem_resource = &mt7621_res_pci_mem1,
+ .io_resource = &mt7621_res_pci_io1,
+ .mem_offset = 0x00000000UL,
+ .io_offset = 0x00000000UL,
+ .io_map_base = 0xa0000000,
+};
+
+static void
+read_config(unsigned long bus, unsigned long dev, unsigned long func, unsigned long reg, unsigned long *val)
+{
+ unsigned int address_reg, data_reg, address;
+
+ address_reg = RALINK_PCI_CONFIG_ADDR;
+ data_reg = RALINK_PCI_CONFIG_DATA_VIRTUAL_REG;
+ address = (((reg & 0xF00)>>8)<<24) | (bus << 16) | (dev << 11) | (func << 8) | (reg & 0xfc) | 0x80000000 ;
+ MV_WRITE(address_reg, address);
+ MV_READ(data_reg, val);
+ return;
+}
+
+static void
+write_config(unsigned long bus, unsigned long dev, unsigned long func, unsigned long reg, unsigned long val)
+{
+ unsigned int address_reg, data_reg, address;
+
+ address_reg = RALINK_PCI_CONFIG_ADDR;
+ data_reg = RALINK_PCI_CONFIG_DATA_VIRTUAL_REG;
+ address = (((reg & 0xF00)>>8)<<24) | (bus << 16) | (dev << 11) | (func << 8) | (reg & 0xfc) | 0x80000000 ;
+ MV_WRITE(address_reg, address);
+ MV_WRITE(data_reg, val);
+ return;
+}
+
+
+int
+pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+ u16 cmd;
+ u32 val;
+ int irq = 0;
+
+ if ((dev->bus->number == 0) && (slot == 0)) {
+ write_config(0, 0, 0, PCI_BASE_ADDRESS_0, MEMORY_BASE);
+ read_config(0, 0, 0, PCI_BASE_ADDRESS_0, (unsigned long *)&val);
+ printk("BAR0 at slot 0 = %x\n", val);
+ printk("bus=0x%x, slot = 0x%x\n",dev->bus->number, slot);
+ } else if((dev->bus->number == 0) && (slot == 0x1)) {
+ write_config(0, 1, 0, PCI_BASE_ADDRESS_0, MEMORY_BASE);
+ read_config(0, 1, 0, PCI_BASE_ADDRESS_0, (unsigned long *)&val);
+ printk("BAR0 at slot 1 = %x\n", val);
+ printk("bus=0x%x, slot = 0x%x\n",dev->bus->number, slot);
+ } else if((dev->bus->number == 0) && (slot == 0x2)) {
+ write_config(0, 2, 0, PCI_BASE_ADDRESS_0, MEMORY_BASE);
+ read_config(0, 2, 0, PCI_BASE_ADDRESS_0, (unsigned long *)&val);
+ printk("BAR0 at slot 2 = %x\n", val);
+ printk("bus=0x%x, slot = 0x%x\n",dev->bus->number, slot);
+ } else if ((dev->bus->number == 1) && (slot == 0x0)) {
+ switch (pcie_link_status) {
+ case 2:
+ case 6:
+ irq = RALINK_INT_PCIE1;
+ break;
+ case 4:
+ irq = RALINK_INT_PCIE2;
+ break;
+ default:
+ irq = RALINK_INT_PCIE0;
+ }
+ printk("bus=0x%x, slot = 0x%x, irq=0x%x\n",dev->bus->number, slot, dev->irq);
+ } else if ((dev->bus->number == 2) && (slot == 0x0)) {
+ switch (pcie_link_status) {
+ case 5:
+ case 6:
+ irq = RALINK_INT_PCIE2;
+ break;
+ default:
+ irq = RALINK_INT_PCIE1;
+ }
+ printk("bus=0x%x, slot = 0x%x, irq=0x%x\n",dev->bus->number, slot, dev->irq);
+ } else if ((dev->bus->number == 2) && (slot == 0x1)) {
+ switch (pcie_link_status) {
+ case 5:
+ case 6:
+ irq = RALINK_INT_PCIE2;
+ break;
+ default:
+ irq = RALINK_INT_PCIE1;
+ }
+ printk("bus=0x%x, slot = 0x%x, irq=0x%x\n",dev->bus->number, slot, dev->irq);
+ } else if ((dev->bus->number ==3) && (slot == 0x0)) {
+ irq = RALINK_INT_PCIE2;
+ printk("bus=0x%x, slot = 0x%x, irq=0x%x\n",dev->bus->number, slot, dev->irq);
+ } else if ((dev->bus->number ==3) && (slot == 0x1)) {
+ irq = RALINK_INT_PCIE2;
+ printk("bus=0x%x, slot = 0x%x, irq=0x%x\n",dev->bus->number, slot, dev->irq);
+ } else if ((dev->bus->number ==3) && (slot == 0x2)) {
+ irq = RALINK_INT_PCIE2;
+ printk("bus=0x%x, slot = 0x%x, irq=0x%x\n",dev->bus->number, slot, dev->irq);
+ } else {
+ printk("bus=0x%x, slot = 0x%x\n",dev->bus->number, slot);
+ return 0;
+ }
+
+ pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 0x14); //configure cache line size 0x14
+ pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0xFF); //configure latency timer 0x10
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ cmd = cmd | PCI_COMMAND_MASTER | PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
+ pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
+#ifdef CONFIG_DTB_GNUBEE1
+ /*
+ * 'irq' here is a hwirq, but a virq is needed. Until we know how and where
+ * to convert one to the other, we have this hack for the GNUBEE1
+ */
+ return irq == 11 ? 22 : irq;
+#else
+ return irq;
+#endif
+}
+
+void
+set_pcie_phy(u32 *addr, int start_b, int bits, int val)
+{
+// printk("0x%p:", addr);
+// printk(" %x", *addr);
+ *(unsigned int *)(addr) &= ~(((1<<bits) - 1)<<start_b);
+ *(unsigned int *)(addr) |= val << start_b;
+// printk(" -> %x\n", *addr);
+}
+
+void
+bypass_pipe_rst(void)
+{
+#if defined (CONFIG_PCIE_PORT0)
+ /* PCIe Port 0 */
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x02c), 12, 1, 0x01); // rg_pe1_pipe_rst_b
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x02c), 4, 1, 0x01); // rg_pe1_pipe_cmd_frc[4]
+#endif
+#if defined (CONFIG_PCIE_PORT1)
+ /* PCIe Port 1 */
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x12c), 12, 1, 0x01); // rg_pe1_pipe_rst_b
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x12c), 4, 1, 0x01); // rg_pe1_pipe_cmd_frc[4]
+#endif
+#if defined (CONFIG_PCIE_PORT2)
+ /* PCIe Port 2 */
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x02c), 12, 1, 0x01); // rg_pe1_pipe_rst_b
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x02c), 4, 1, 0x01); // rg_pe1_pipe_cmd_frc[4]
+#endif
+}
+
+void
+set_phy_for_ssc(void)
+{
+ unsigned long reg = (*(volatile u32 *)(RALINK_SYSCTL_BASE + 0x10));
+
+ reg = (reg >> 6) & 0x7;
+#if defined (CONFIG_PCIE_PORT0) || defined (CONFIG_PCIE_PORT1)
+ /* Set PCIe Port0 & Port1 PHY to disable SSC */
+ /* Debug Xtal Type */
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x400), 8, 1, 0x01); // rg_pe1_frc_h_xtal_type
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x400), 9, 2, 0x00); // rg_pe1_h_xtal_type
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x000), 4, 1, 0x01); // rg_pe1_frc_phy_en //Force Port 0 enable control
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x100), 4, 1, 0x01); // rg_pe1_frc_phy_en //Force Port 1 enable control
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x000), 5, 1, 0x00); // rg_pe1_phy_en //Port 0 disable
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x100), 5, 1, 0x00); // rg_pe1_phy_en //Port 1 disable
+ if(reg <= 5 && reg >= 3) { // 40MHz Xtal
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x490), 6, 2, 0x01); // RG_PE1_H_PLL_PREDIV //Pre-divider ratio (for host mode)
+ printk("***** Xtal 40MHz *****\n");
+ } else { // 25MHz | 20MHz Xtal
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x490), 6, 2, 0x00); // RG_PE1_H_PLL_PREDIV //Pre-divider ratio (for host mode)
+ if (reg >= 6) {
+ printk("***** Xtal 25MHz *****\n");
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x4bc), 4, 2, 0x01); // RG_PE1_H_PLL_FBKSEL //Feedback clock select
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x49c), 0,31, 0x18000000); // RG_PE1_H_LCDDS_PCW_NCPO //DDS NCPO PCW (for host mode)
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x4a4), 0,16, 0x18d); // RG_PE1_H_LCDDS_SSC_PRD //DDS SSC dither period control
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x4a8), 0,12, 0x4a); // RG_PE1_H_LCDDS_SSC_DELTA //DDS SSC dither amplitude control
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x4a8), 16,12, 0x4a); // RG_PE1_H_LCDDS_SSC_DELTA1 //DDS SSC dither amplitude control for initial
+ } else {
+ printk("***** Xtal 20MHz *****\n");
+ }
+ }
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x4a0), 5, 1, 0x01); // RG_PE1_LCDDS_CLK_PH_INV //DDS clock inversion
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x490), 22, 2, 0x02); // RG_PE1_H_PLL_BC
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x490), 18, 4, 0x06); // RG_PE1_H_PLL_BP
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x490), 12, 4, 0x02); // RG_PE1_H_PLL_IR
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x490), 8, 4, 0x01); // RG_PE1_H_PLL_IC
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x4ac), 16, 3, 0x00); // RG_PE1_H_PLL_BR
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x490), 1, 3, 0x02); // RG_PE1_PLL_DIVEN
+ if(reg <= 5 && reg >= 3) { // 40MHz Xtal
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x414), 6, 2, 0x01); // rg_pe1_mstckdiv //value of da_pe1_mstckdiv when force mode enable
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x414), 5, 1, 0x01); // rg_pe1_frc_mstckdiv //force mode enable of da_pe1_mstckdiv
+ }
+ /* Enable PHY and disable force mode */
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x000), 5, 1, 0x01); // rg_pe1_phy_en //Port 0 enable
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x100), 5, 1, 0x01); // rg_pe1_phy_en //Port 1 enable
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x000), 4, 1, 0x00); // rg_pe1_frc_phy_en //Force Port 0 disable control
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x100), 4, 1, 0x00); // rg_pe1_frc_phy_en //Force Port 1 disable control
+#endif
+#if defined (CONFIG_PCIE_PORT2)
+ /* Set PCIe Port2 PHY to disable SSC */
+ /* Debug Xtal Type */
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x400), 8, 1, 0x01); // rg_pe1_frc_h_xtal_type
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x400), 9, 2, 0x00); // rg_pe1_h_xtal_type
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x000), 4, 1, 0x01); // rg_pe1_frc_phy_en //Force Port 0 enable control
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x000), 5, 1, 0x00); // rg_pe1_phy_en //Port 0 disable
+ if(reg <= 5 && reg >= 3) { // 40MHz Xtal
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x490), 6, 2, 0x01); // RG_PE1_H_PLL_PREDIV //Pre-divider ratio (for host mode)
+ } else { // 25MHz | 20MHz Xtal
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x490), 6, 2, 0x00); // RG_PE1_H_PLL_PREDIV //Pre-divider ratio (for host mode)
+ if (reg >= 6) { // 25MHz Xtal
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x4bc), 4, 2, 0x01); // RG_PE1_H_PLL_FBKSEL //Feedback clock select
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x49c), 0,31, 0x18000000); // RG_PE1_H_LCDDS_PCW_NCPO //DDS NCPO PCW (for host mode)
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x4a4), 0,16, 0x18d); // RG_PE1_H_LCDDS_SSC_PRD //DDS SSC dither period control
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x4a8), 0,12, 0x4a); // RG_PE1_H_LCDDS_SSC_DELTA //DDS SSC dither amplitude control
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x4a8), 16,12, 0x4a); // RG_PE1_H_LCDDS_SSC_DELTA1 //DDS SSC dither amplitude control for initial
+ }
+ }
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x4a0), 5, 1, 0x01); // RG_PE1_LCDDS_CLK_PH_INV //DDS clock inversion
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x490), 22, 2, 0x02); // RG_PE1_H_PLL_BC
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x490), 18, 4, 0x06); // RG_PE1_H_PLL_BP
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x490), 12, 4, 0x02); // RG_PE1_H_PLL_IR
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x490), 8, 4, 0x01); // RG_PE1_H_PLL_IC
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x4ac), 16, 3, 0x00); // RG_PE1_H_PLL_BR
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x490), 1, 3, 0x02); // RG_PE1_PLL_DIVEN
+ if(reg <= 5 && reg >= 3) { // 40MHz Xtal
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x414), 6, 2, 0x01); // rg_pe1_mstckdiv //value of da_pe1_mstckdiv when force mode enable
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x414), 5, 1, 0x01); // rg_pe1_frc_mstckdiv //force mode enable of da_pe1_mstckdiv
+ }
+ /* Enable PHY and disable force mode */
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x000), 5, 1, 0x01); // rg_pe1_phy_en //Port 0 enable
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x000), 4, 1, 0x00); // rg_pe1_frc_phy_en //Force Port 0 disable control
+#endif
+}
+
+void setup_cm_memory_region(struct resource *mem_resource)
+{
+ resource_size_t mask;
+ if (mips_cps_numiocu(0)) {
+ /* FIXME: hardware doesn't accept mask values with 1s after
+ 0s (e.g. 0xffef), so it would be great to warn if that's
+ about to happen */
+ mask = ~(mem_resource->end - mem_resource->start);
+
+ write_gcr_reg1_base(mem_resource->start);
+ write_gcr_reg1_mask(mask | CM_GCR_REGn_MASK_CMTGT_IOCU0);
+ printk("PCI coherence region base: 0x%08llx, mask/settings: 0x%08llx\n",
+ (unsigned long long)read_gcr_reg1_base(),
+ (unsigned long long)read_gcr_reg1_mask());
+ }
+}
+
+static int mt7621_pci_probe(struct platform_device *pdev)
+{
+ unsigned long val = 0;
+
+ iomem_resource.start = 0;
+ iomem_resource.end= ~0;
+ ioport_resource.start= 0;
+ ioport_resource.end = ~0;
+
+#if defined (CONFIG_PCIE_PORT0)
+ val = RALINK_PCIE0_RST;
+#endif
+#if defined (CONFIG_PCIE_PORT1)
+ val |= RALINK_PCIE1_RST;
+#endif
+#if defined (CONFIG_PCIE_PORT2)
+ val |= RALINK_PCIE2_RST;
+#endif
+ ASSERT_SYSRST_PCIE(RALINK_PCIE0_RST | RALINK_PCIE1_RST | RALINK_PCIE2_RST);
+ printk("pull PCIe RST: RALINK_RSTCTRL = %x\n", RALINK_RSTCTRL);
+#if defined GPIO_PERST /* add GPIO control instead of PERST_N */ /*chhung*/
+ *(unsigned int *)(0xbe000060) &= ~(0x3<<10 | 0x3<<3);
+ *(unsigned int *)(0xbe000060) |= 0x1<<10 | 0x1<<3;
+ mdelay(100);
+ *(unsigned int *)(0xbe000600) |= 0x1<<19 | 0x1<<8 | 0x1<<7; // use GPIO19/GPIO8/GPIO7 (PERST_N/UART_RXD3/UART_TXD3)
+ mdelay(100);
+ *(unsigned int *)(0xbe000620) &= ~(0x1<<19 | 0x1<<8 | 0x1<<7); // clear DATA
+
+ mdelay(100);
+#else
+ *(unsigned int *)(0xbe000060) &= ~0x00000c00;
+#endif
+#if defined (CONFIG_PCIE_PORT0)
+ val = RALINK_PCIE0_RST;
+#endif
+#if defined (CONFIG_PCIE_PORT1)
+ val |= RALINK_PCIE1_RST;
+#endif
+#if defined (CONFIG_PCIE_PORT2)
+ val |= RALINK_PCIE2_RST;
+#endif
+ DEASSERT_SYSRST_PCIE(val);
+ printk("release PCIe RST: RALINK_RSTCTRL = %x\n", RALINK_RSTCTRL);
+
+ if ((*(unsigned int *)(0xbe00000c)&0xFFFF) == 0x0101) // MT7621 E2
+ bypass_pipe_rst();
+ set_phy_for_ssc();
+ printk("release PCIe RST: RALINK_RSTCTRL = %x\n", RALINK_RSTCTRL);
+
+#if defined (CONFIG_PCIE_PORT0)
+ read_config(0, 0, 0, 0x70c, &val);
+ printk("Port 0 N_FTS = %x\n", (unsigned int)val);
+#endif
+#if defined (CONFIG_PCIE_PORT1)
+ read_config(0, 1, 0, 0x70c, &val);
+ printk("Port 1 N_FTS = %x\n", (unsigned int)val);
+#endif
+#if defined (CONFIG_PCIE_PORT2)
+ read_config(0, 2, 0, 0x70c, &val);
+ printk("Port 2 N_FTS = %x\n", (unsigned int)val);
+#endif
+
+ RALINK_RSTCTRL = (RALINK_RSTCTRL | RALINK_PCIE_RST);
+ RALINK_SYSCFG1 &= ~(0x30);
+ RALINK_SYSCFG1 |= (2<<4);
+ RALINK_PCIE_CLK_GEN &= 0x7fffffff;
+ RALINK_PCIE_CLK_GEN1 &= 0x80ffffff;
+ RALINK_PCIE_CLK_GEN1 |= 0xa << 24;
+ RALINK_PCIE_CLK_GEN |= 0x80000000;
+ mdelay(50);
+ RALINK_RSTCTRL = (RALINK_RSTCTRL & ~RALINK_PCIE_RST);
+
+
+#if defined GPIO_PERST /* add GPIO control instead of PERST_N */ /*chhung*/
+ *(unsigned int *)(0xbe000620) |= 0x1<<19 | 0x1<<8 | 0x1<<7; // set DATA
+ mdelay(100);
+#else
+ RALINK_PCI_PCICFG_ADDR &= ~(1<<1); //de-assert PERST
+#endif
+ mdelay(500);
+
+
+ mdelay(500);
+#if defined (CONFIG_PCIE_PORT0)
+ if(( RALINK_PCI0_STATUS & 0x1) == 0)
+ {
+ printk("PCIE0 no card, disable it(RST&CLK)\n");
+ ASSERT_SYSRST_PCIE(RALINK_PCIE0_RST);
+ RALINK_CLKCFG1 = (RALINK_CLKCFG1 & ~RALINK_PCIE0_CLK_EN);
+ pcie_link_status &= ~(1<<0);
+ } else {
+ pcie_link_status |= 1<<0;
+ RALINK_PCI_PCIMSK_ADDR |= (1<<20); // enable pcie1 interrupt
+ }
+#endif
+#if defined (CONFIG_PCIE_PORT1)
+ if(( RALINK_PCI1_STATUS & 0x1) == 0)
+ {
+ printk("PCIE1 no card, disable it(RST&CLK)\n");
+ ASSERT_SYSRST_PCIE(RALINK_PCIE1_RST);
+ RALINK_CLKCFG1 = (RALINK_CLKCFG1 & ~RALINK_PCIE1_CLK_EN);
+ pcie_link_status &= ~(1<<1);
+ } else {
+ pcie_link_status |= 1<<1;
+ RALINK_PCI_PCIMSK_ADDR |= (1<<21); // enable pcie1 interrupt
+ }
+#endif
+#if defined (CONFIG_PCIE_PORT2)
+ if (( RALINK_PCI2_STATUS & 0x1) == 0) {
+ printk("PCIE2 no card, disable it(RST&CLK)\n");
+ ASSERT_SYSRST_PCIE(RALINK_PCIE2_RST);
+ RALINK_CLKCFG1 = (RALINK_CLKCFG1 & ~RALINK_PCIE2_CLK_EN);
+ pcie_link_status &= ~(1<<2);
+ } else {
+ pcie_link_status |= 1<<2;
+ RALINK_PCI_PCIMSK_ADDR |= (1<<22); // enable pcie2 interrupt
+ }
+#endif
+ if (pcie_link_status == 0)
+ return 0;
+
+/*
+pcie(2/1/0) link status pcie2_num pcie1_num pcie0_num
+3'b000 x x x
+3'b001 x x 0
+3'b010 x 0 x
+3'b011 x 1 0
+3'b100 0 x x
+3'b101 1 x 0
+3'b110 1 0 x
+3'b111 2 1 0
+*/
+ switch(pcie_link_status) {
+ case 2:
+ RALINK_PCI_PCICFG_ADDR &= ~0x00ff0000;
+ RALINK_PCI_PCICFG_ADDR |= 0x1 << 16; //port0
+ RALINK_PCI_PCICFG_ADDR |= 0x0 << 20; //port1
+ break;
+ case 4:
+ RALINK_PCI_PCICFG_ADDR &= ~0x0fff0000;
+ RALINK_PCI_PCICFG_ADDR |= 0x1 << 16; //port0
+ RALINK_PCI_PCICFG_ADDR |= 0x2 << 20; //port1
+ RALINK_PCI_PCICFG_ADDR |= 0x0 << 24; //port2
+ break;
+ case 5:
+ RALINK_PCI_PCICFG_ADDR &= ~0x0fff0000;
+ RALINK_PCI_PCICFG_ADDR |= 0x0 << 16; //port0
+ RALINK_PCI_PCICFG_ADDR |= 0x2 << 20; //port1
+ RALINK_PCI_PCICFG_ADDR |= 0x1 << 24; //port2
+ break;
+ case 6:
+ RALINK_PCI_PCICFG_ADDR &= ~0x0fff0000;
+ RALINK_PCI_PCICFG_ADDR |= 0x2 << 16; //port0
+ RALINK_PCI_PCICFG_ADDR |= 0x0 << 20; //port1
+ RALINK_PCI_PCICFG_ADDR |= 0x1 << 24; //port2
+ break;
+ }
+ printk(" -> %x\n", RALINK_PCI_PCICFG_ADDR);
+ //printk(" RALINK_PCI_ARBCTL = %x\n", RALINK_PCI_ARBCTL);
+
+/*
+ ioport_resource.start = mt7621_res_pci_io1.start;
+ ioport_resource.end = mt7621_res_pci_io1.end;
+*/
+
+ RALINK_PCI_MEMBASE = 0xffffffff; //RALINK_PCI_MM_MAP_BASE;
+ RALINK_PCI_IOBASE = RALINK_PCI_IO_MAP_BASE;
+
+#if defined (CONFIG_PCIE_PORT0)
+ //PCIe0
+ if((pcie_link_status & 0x1) != 0) {
+ RALINK_PCI0_BAR0SETUP_ADDR = 0x7FFF0001; //open 7FFF:2G; ENABLE
+ RALINK_PCI0_IMBASEBAR0_ADDR = MEMORY_BASE;
+ RALINK_PCI0_CLASS = 0x06040001;
+ printk("PCIE0 enabled\n");
+ }
+#endif
+#if defined (CONFIG_PCIE_PORT1)
+ //PCIe1
+ if ((pcie_link_status & 0x2) != 0) {
+ RALINK_PCI1_BAR0SETUP_ADDR = 0x7FFF0001; //open 7FFF:2G; ENABLE
+ RALINK_PCI1_IMBASEBAR0_ADDR = MEMORY_BASE;
+ RALINK_PCI1_CLASS = 0x06040001;
+ printk("PCIE1 enabled\n");
+ }
+#endif
+#if defined (CONFIG_PCIE_PORT2)
+ //PCIe2
+ if ((pcie_link_status & 0x4) != 0) {
+ RALINK_PCI2_BAR0SETUP_ADDR = 0x7FFF0001; //open 7FFF:2G; ENABLE
+ RALINK_PCI2_IMBASEBAR0_ADDR = MEMORY_BASE;
+ RALINK_PCI2_CLASS = 0x06040001;
+ printk("PCIE2 enabled\n");
+ }
+#endif
+
+
+ switch(pcie_link_status) {
+ case 7:
+ read_config(0, 2, 0, 0x4, &val);
+ write_config(0, 2, 0, 0x4, val|0x4);
+ // write_config(0, 1, 0, 0x4, val|0x7);
+ read_config(0, 2, 0, 0x70c, &val);
+ val &= ~(0xff)<<8;
+ val |= 0x50<<8;
+ write_config(0, 2, 0, 0x70c, val);
+ case 3:
+ case 5:
+ case 6:
+ read_config(0, 1, 0, 0x4, &val);
+ write_config(0, 1, 0, 0x4, val|0x4);
+ // write_config(0, 1, 0, 0x4, val|0x7);
+ read_config(0, 1, 0, 0x70c, &val);
+ val &= ~(0xff)<<8;
+ val |= 0x50<<8;
+ write_config(0, 1, 0, 0x70c, val);
+ default:
+ read_config(0, 0, 0, 0x4, &val);
+ write_config(0, 0, 0, 0x4, val|0x4); //bus master enable
+ // write_config(0, 0, 0, 0x4, val|0x7); //bus master enable
+ read_config(0, 0, 0, 0x70c, &val);
+ val &= ~(0xff)<<8;
+ val |= 0x50<<8;
+ write_config(0, 0, 0, 0x70c, val);
+ }
+
+ pci_load_of_ranges(&mt7621_controller, pdev->dev.of_node);
+ setup_cm_memory_region(mt7621_controller.mem_resource);
+ register_pci_controller(&mt7621_controller);
+ return 0;
+
+}
+
+int pcibios_plat_dev_init(struct pci_dev *dev)
+{
+ return 0;
+}
+
+static const struct of_device_id mt7621_pci_ids[] = {
+ { .compatible = "mediatek,mt7621-pci" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt7621_pci_ids);
+
+static struct platform_driver mt7621_pci_driver = {
+ .probe = mt7621_pci_probe,
+ .driver = {
+ .name = "mt7621-pci",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(mt7621_pci_ids),
+ },
+};
+
+static int __init mt7621_pci_init(void)
+{
+ return platform_driver_register(&mt7621_pci_driver);
+}
+
+arch_initcall(mt7621_pci_init);
diff --git a/drivers/staging/mt7621-pinctrl/Kconfig b/drivers/staging/mt7621-pinctrl/Kconfig
new file mode 100644
index 000000000000..37cf9c3273be
--- /dev/null
+++ b/drivers/staging/mt7621-pinctrl/Kconfig
@@ -0,0 +1,4 @@
+config PINCTRL_RT2880
+ bool "RT2800 pinctrl driver for RALINK/Mediatek SOCs"
+ depends on RALINK
+ select PINMUX
diff --git a/drivers/staging/mt7621-pinctrl/Makefile b/drivers/staging/mt7621-pinctrl/Makefile
new file mode 100644
index 000000000000..856102137a1e
--- /dev/null
+++ b/drivers/staging/mt7621-pinctrl/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_PINCTRL_RT2880) += pinctrl-rt2880.o
+
+ccflags-y += -I$(srctree)/drivers/pinctrl
diff --git a/drivers/staging/mt7621-pinctrl/TODO b/drivers/staging/mt7621-pinctrl/TODO
new file mode 100644
index 000000000000..b2c235a16d5c
--- /dev/null
+++ b/drivers/staging/mt7621-pinctrl/TODO
@@ -0,0 +1,6 @@
+
+- general code review and cleanup
+- should probably be always selected by 'config RALINK'
+- ensure device-tree requirements are documented
+
+Cc: NeilBrown <neil@brown.name>
diff --git a/drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c b/drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c
new file mode 100644
index 000000000000..3d2d1c2a006f
--- /dev/null
+++ b/drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c
@@ -0,0 +1,472 @@
+/*
+ * linux/drivers/pinctrl/pinctrl-rt2880.c
+ *
+ * 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
+ * publishhed by the Free Software Foundation.
+ *
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/machine.h>
+
+#include <asm/mach-ralink/ralink_regs.h>
+#include <asm/mach-ralink/pinmux.h>
+#include <asm/mach-ralink/mt7620.h>
+
+#include "core.h"
+
+#define SYSC_REG_GPIO_MODE 0x60
+#define SYSC_REG_GPIO_MODE2 0x64
+
+struct rt2880_priv {
+ struct device *dev;
+
+ struct pinctrl_pin_desc *pads;
+ struct pinctrl_desc *desc;
+
+ struct rt2880_pmx_func **func;
+ int func_count;
+
+ struct rt2880_pmx_group *groups;
+ const char **group_names;
+ int group_count;
+
+ uint8_t *gpio;
+ int max_pins;
+};
+
+static int rt2880_get_group_count(struct pinctrl_dev *pctrldev)
+{
+ struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
+
+ return p->group_count;
+}
+
+static const char *rt2880_get_group_name(struct pinctrl_dev *pctrldev,
+ unsigned group)
+{
+ struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
+
+ if (group >= p->group_count)
+ return NULL;
+
+ return p->group_names[group];
+}
+
+static int rt2880_get_group_pins(struct pinctrl_dev *pctrldev,
+ unsigned group,
+ const unsigned **pins,
+ unsigned *num_pins)
+{
+ struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
+
+ if (group >= p->group_count)
+ return -EINVAL;
+
+ *pins = p->groups[group].func[0].pins;
+ *num_pins = p->groups[group].func[0].pin_count;
+
+ return 0;
+}
+
+static void rt2880_pinctrl_dt_free_map(struct pinctrl_dev *pctrldev,
+ struct pinctrl_map *map, unsigned num_maps)
+{
+ int i;
+
+ for (i = 0; i < num_maps; i++)
+ if (map[i].type == PIN_MAP_TYPE_CONFIGS_PIN ||
+ map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP)
+ kfree(map[i].data.configs.configs);
+ kfree(map);
+}
+
+static void rt2880_pinctrl_pin_dbg_show(struct pinctrl_dev *pctrldev,
+ struct seq_file *s,
+ unsigned offset)
+{
+ seq_printf(s, "ralink pio");
+}
+
+static void rt2880_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctrldev,
+ struct device_node *np,
+ struct pinctrl_map **map)
+{
+ const char *function;
+ int func = of_property_read_string(np, "ralink,function", &function);
+ int grps = of_property_count_strings(np, "ralink,group");
+ int i;
+
+ if (func || !grps)
+ return;
+
+ for (i = 0; i < grps; i++) {
+ const char *group;
+
+ of_property_read_string_index(np, "ralink,group", i, &group);
+
+ (*map)->type = PIN_MAP_TYPE_MUX_GROUP;
+ (*map)->name = function;
+ (*map)->data.mux.group = group;
+ (*map)->data.mux.function = function;
+ (*map)++;
+ }
+}
+
+static int rt2880_pinctrl_dt_node_to_map(struct pinctrl_dev *pctrldev,
+ struct device_node *np_config,
+ struct pinctrl_map **map,
+ unsigned *num_maps)
+{
+ int max_maps = 0;
+ struct pinctrl_map *tmp;
+ struct device_node *np;
+
+ for_each_child_of_node(np_config, np) {
+ int ret = of_property_count_strings(np, "ralink,group");
+
+ if (ret >= 0)
+ max_maps += ret;
+ }
+
+ if (!max_maps)
+ return max_maps;
+
+ *map = kzalloc(max_maps * sizeof(struct pinctrl_map), GFP_KERNEL);
+ if (!*map)
+ return -ENOMEM;
+
+ tmp = *map;
+
+ for_each_child_of_node(np_config, np)
+ rt2880_pinctrl_dt_subnode_to_map(pctrldev, np, &tmp);
+ *num_maps = max_maps;
+
+ return 0;
+}
+
+static const struct pinctrl_ops rt2880_pctrl_ops = {
+ .get_groups_count = rt2880_get_group_count,
+ .get_group_name = rt2880_get_group_name,
+ .get_group_pins = rt2880_get_group_pins,
+ .pin_dbg_show = rt2880_pinctrl_pin_dbg_show,
+ .dt_node_to_map = rt2880_pinctrl_dt_node_to_map,
+ .dt_free_map = rt2880_pinctrl_dt_free_map,
+};
+
+static int rt2880_pmx_func_count(struct pinctrl_dev *pctrldev)
+{
+ struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
+
+ return p->func_count;
+}
+
+static const char *rt2880_pmx_func_name(struct pinctrl_dev *pctrldev,
+ unsigned func)
+{
+ struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
+
+ return p->func[func]->name;
+}
+
+static int rt2880_pmx_group_get_groups(struct pinctrl_dev *pctrldev,
+ unsigned func,
+ const char * const **groups,
+ unsigned * const num_groups)
+{
+ struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
+
+ if (p->func[func]->group_count == 1)
+ *groups = &p->group_names[p->func[func]->groups[0]];
+ else
+ *groups = p->group_names;
+
+ *num_groups = p->func[func]->group_count;
+
+ return 0;
+}
+
+static int rt2880_pmx_group_enable(struct pinctrl_dev *pctrldev,
+ unsigned func,
+ unsigned group)
+{
+ struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
+ u32 mode = 0;
+ u32 reg = SYSC_REG_GPIO_MODE;
+ int i;
+ int shift;
+
+ /* dont allow double use */
+ if (p->groups[group].enabled) {
+ dev_err(p->dev, "%s is already enabled\n", p->groups[group].name);
+ return -EBUSY;
+ }
+
+ p->groups[group].enabled = 1;
+ p->func[func]->enabled = 1;
+
+ shift = p->groups[group].shift;
+ if (shift >= 32) {
+ shift -= 32;
+ reg = SYSC_REG_GPIO_MODE2;
+ }
+ mode = rt_sysc_r32(reg);
+ mode &= ~(p->groups[group].mask << shift);
+
+ /* mark the pins as gpio */
+ for (i = 0; i < p->groups[group].func[0].pin_count; i++)
+ p->gpio[p->groups[group].func[0].pins[i]] = 1;
+
+ /* function 0 is gpio and needs special handling */
+ if (func == 0) {
+ mode |= p->groups[group].gpio << shift;
+ } else {
+ for (i = 0; i < p->func[func]->pin_count; i++)
+ p->gpio[p->func[func]->pins[i]] = 0;
+ mode |= p->func[func]->value << shift;
+ }
+ rt_sysc_w32(mode, reg);
+
+ return 0;
+}
+
+static int rt2880_pmx_group_gpio_request_enable(struct pinctrl_dev *pctrldev,
+ struct pinctrl_gpio_range *range,
+ unsigned pin)
+{
+ struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
+
+ if (!p->gpio[pin]) {
+ dev_err(p->dev, "pin %d is not set to gpio mux\n", pin);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct pinmux_ops rt2880_pmx_group_ops = {
+ .get_functions_count = rt2880_pmx_func_count,
+ .get_function_name = rt2880_pmx_func_name,
+ .get_function_groups = rt2880_pmx_group_get_groups,
+ .set_mux = rt2880_pmx_group_enable,
+ .gpio_request_enable = rt2880_pmx_group_gpio_request_enable,
+};
+
+static struct pinctrl_desc rt2880_pctrl_desc = {
+ .owner = THIS_MODULE,
+ .name = "rt2880-pinmux",
+ .pctlops = &rt2880_pctrl_ops,
+ .pmxops = &rt2880_pmx_group_ops,
+};
+
+static struct rt2880_pmx_func gpio_func = {
+ .name = "gpio",
+};
+
+static int rt2880_pinmux_index(struct rt2880_priv *p)
+{
+ struct rt2880_pmx_func **f;
+ struct rt2880_pmx_group *mux = p->groups;
+ int i, j, c = 0;
+
+ /* count the mux functions */
+ while (mux->name) {
+ p->group_count++;
+ mux++;
+ }
+
+ /* allocate the group names array needed by the gpio function */
+ p->group_names = devm_kzalloc(p->dev, sizeof(char *) * p->group_count, GFP_KERNEL);
+ if (!p->group_names)
+ return -1;
+
+ for (i = 0; i < p->group_count; i++) {
+ p->group_names[i] = p->groups[i].name;
+ p->func_count += p->groups[i].func_count;
+ }
+
+ /* we have a dummy function[0] for gpio */
+ p->func_count++;
+
+ /* allocate our function and group mapping index buffers */
+ f = p->func = devm_kzalloc(p->dev, sizeof(struct rt2880_pmx_func) * p->func_count, GFP_KERNEL);
+ gpio_func.groups = devm_kzalloc(p->dev, sizeof(int) * p->group_count, GFP_KERNEL);
+ if (!f || !gpio_func.groups)
+ return -1;
+
+ /* add a backpointer to the function so it knows its group */
+ gpio_func.group_count = p->group_count;
+ for (i = 0; i < gpio_func.group_count; i++)
+ gpio_func.groups[i] = i;
+
+ f[c] = &gpio_func;
+ c++;
+
+ /* add remaining functions */
+ for (i = 0; i < p->group_count; i++) {
+ for (j = 0; j < p->groups[i].func_count; j++) {
+ f[c] = &p->groups[i].func[j];
+ f[c]->groups = devm_kzalloc(p->dev, sizeof(int), GFP_KERNEL);
+ f[c]->groups[0] = i;
+ f[c]->group_count = 1;
+ c++;
+ }
+ }
+ return 0;
+}
+
+static int rt2880_pinmux_pins(struct rt2880_priv *p)
+{
+ int i, j;
+
+ /* loop over the functions and initialize the pins array. also work out the highest pin used */
+ for (i = 0; i < p->func_count; i++) {
+ int pin;
+
+ if (!p->func[i]->pin_count)
+ continue;
+
+ p->func[i]->pins = devm_kzalloc(p->dev, sizeof(int) * p->func[i]->pin_count, GFP_KERNEL);
+ for (j = 0; j < p->func[i]->pin_count; j++)
+ p->func[i]->pins[j] = p->func[i]->pin_first + j;
+
+ pin = p->func[i]->pin_first + p->func[i]->pin_count;
+ if (pin > p->max_pins)
+ p->max_pins = pin;
+ }
+
+ /* the buffer that tells us which pins are gpio */
+ p->gpio = devm_kzalloc(p->dev,sizeof(uint8_t) * p->max_pins,
+ GFP_KERNEL);
+ /* the pads needed to tell pinctrl about our pins */
+ p->pads = devm_kzalloc(p->dev,
+ sizeof(struct pinctrl_pin_desc) * p->max_pins,
+ GFP_KERNEL);
+ if (!p->pads || !p->gpio ) {
+ dev_err(p->dev, "Failed to allocate gpio data\n");
+ return -ENOMEM;
+ }
+
+ memset(p->gpio, 1, sizeof(uint8_t) * p->max_pins);
+ for (i = 0; i < p->func_count; i++) {
+ if (!p->func[i]->pin_count)
+ continue;
+
+ for (j = 0; j < p->func[i]->pin_count; j++)
+ p->gpio[p->func[i]->pins[j]] = 0;
+ }
+
+ /* pin 0 is always a gpio */
+ p->gpio[0] = 1;
+
+ /* set the pads */
+ for (i = 0; i < p->max_pins; i++) {
+ /* strlen("ioXY") + 1 = 5 */
+ char *name = devm_kzalloc(p->dev, 5, GFP_KERNEL);
+
+ if (!name) {
+ dev_err(p->dev, "Failed to allocate pad name\n");
+ return -ENOMEM;
+ }
+ snprintf(name, 5, "io%d", i);
+ p->pads[i].number = i;
+ p->pads[i].name = name;
+ }
+ p->desc->pins = p->pads;
+ p->desc->npins = p->max_pins;
+
+ return 0;
+}
+
+static int rt2880_pinmux_probe(struct platform_device *pdev)
+{
+ struct rt2880_priv *p;
+ struct pinctrl_dev *dev;
+ struct device_node *np;
+
+ if (!rt2880_pinmux_data)
+ return -ENOSYS;
+
+ /* setup the private data */
+ p = devm_kzalloc(&pdev->dev, sizeof(struct rt2880_priv), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ p->dev = &pdev->dev;
+ p->desc = &rt2880_pctrl_desc;
+ p->groups = rt2880_pinmux_data;
+ platform_set_drvdata(pdev, p);
+
+ /* init the device */
+ if (rt2880_pinmux_index(p)) {
+ dev_err(&pdev->dev, "failed to load index\n");
+ return -EINVAL;
+ }
+ if (rt2880_pinmux_pins(p)) {
+ dev_err(&pdev->dev, "failed to load pins\n");
+ return -EINVAL;
+ }
+ dev = pinctrl_register(p->desc, &pdev->dev, p);
+ if (IS_ERR(dev))
+ return PTR_ERR(dev);
+
+ /* finalize by adding gpio ranges for enables gpio controllers */
+ for_each_compatible_node(np, NULL, "ralink,rt2880-gpio") {
+ const __be32 *ngpio, *gpiobase;
+ struct pinctrl_gpio_range *range;
+ char *name;
+
+ if (!of_device_is_available(np))
+ continue;
+
+ ngpio = of_get_property(np, "ralink,num-gpios", NULL);
+ gpiobase = of_get_property(np, "ralink,gpio-base", NULL);
+ if (!ngpio || !gpiobase) {
+ dev_err(&pdev->dev, "failed to load chip info\n");
+ return -EINVAL;
+ }
+
+ range = devm_kzalloc(p->dev, sizeof(struct pinctrl_gpio_range) + 4, GFP_KERNEL);
+ range->name = name = (char *) &range[1];
+ sprintf(name, "pio");
+ range->npins = __be32_to_cpu(*ngpio);
+ range->base = __be32_to_cpu(*gpiobase);
+ range->pin_base = range->base;
+ pinctrl_add_gpio_range(dev, range);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id rt2880_pinmux_match[] = {
+ { .compatible = "ralink,rt2880-pinmux" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rt2880_pinmux_match);
+
+static struct platform_driver rt2880_pinmux_driver = {
+ .probe = rt2880_pinmux_probe,
+ .driver = {
+ .name = "rt2880-pinmux",
+ .owner = THIS_MODULE,
+ .of_match_table = rt2880_pinmux_match,
+ },
+};
+
+int __init rt2880_pinmux_init(void)
+{
+ return platform_driver_register(&rt2880_pinmux_driver);
+}
+
+core_initcall_sync(rt2880_pinmux_init);
diff --git a/drivers/staging/mt7621-spi/Kconfig b/drivers/staging/mt7621-spi/Kconfig
new file mode 100644
index 000000000000..0b90f4cfa426
--- /dev/null
+++ b/drivers/staging/mt7621-spi/Kconfig
@@ -0,0 +1,6 @@
+config SPI_MT7621
+ tristate "MediaTek MT7621 SPI Controller"
+ depends on RALINK
+ help
+ This selects a driver for the MediaTek MT7621 SPI Controller.
+
diff --git a/drivers/staging/mt7621-spi/Makefile b/drivers/staging/mt7621-spi/Makefile
new file mode 100644
index 000000000000..3be508f63bac
--- /dev/null
+++ b/drivers/staging/mt7621-spi/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SPI_MT7621) += spi-mt7621.o
diff --git a/drivers/staging/mt7621-spi/TODO b/drivers/staging/mt7621-spi/TODO
new file mode 100644
index 000000000000..fdbc5002c32a
--- /dev/null
+++ b/drivers/staging/mt7621-spi/TODO
@@ -0,0 +1,5 @@
+
+- general code review and clean up
+- ensure device-tree requirements are documented
+
+Cc: NeilBrown <neil@brown.name>
diff --git a/drivers/staging/mt7621-spi/spi-mt7621.c b/drivers/staging/mt7621-spi/spi-mt7621.c
new file mode 100644
index 000000000000..d95e0b32f1f0
--- /dev/null
+++ b/drivers/staging/mt7621-spi/spi-mt7621.c
@@ -0,0 +1,489 @@
+/*
+ * spi-mt7621.c -- MediaTek MT7621 SPI controller driver
+ *
+ * Copyright (C) 2011 Sergiy <piratfm@gmail.com>
+ * Copyright (C) 2011-2013 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2014-2015 Felix Fietkau <nbd@nbd.name>
+ *
+ * Some parts are based on spi-orion.c:
+ * Author: Shadi Ammouri <shadi@marvell.com>
+ * Copyright (C) 2007-2008 Marvell Ltd.
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/reset.h>
+#include <linux/spi/spi.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/swab.h>
+
+#include <ralink_regs.h>
+
+#define SPI_BPW_MASK(bits) BIT((bits) - 1)
+
+#define DRIVER_NAME "spi-mt7621"
+/* in usec */
+#define RALINK_SPI_WAIT_MAX_LOOP 2000
+
+/* SPISTAT register bit field */
+#define SPISTAT_BUSY BIT(0)
+
+#define MT7621_SPI_TRANS 0x00
+#define SPITRANS_BUSY BIT(16)
+
+#define MT7621_SPI_OPCODE 0x04
+#define MT7621_SPI_DATA0 0x08
+#define MT7621_SPI_DATA4 0x18
+#define SPI_CTL_TX_RX_CNT_MASK 0xff
+#define SPI_CTL_START BIT(8)
+
+#define MT7621_SPI_POLAR 0x38
+#define MT7621_SPI_MASTER 0x28
+#define MT7621_SPI_MOREBUF 0x2c
+#define MT7621_SPI_SPACE 0x3c
+
+#define MT7621_CPHA BIT(5)
+#define MT7621_CPOL BIT(4)
+#define MT7621_LSB_FIRST BIT(3)
+
+#define RT2880_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | SPI_CS_HIGH)
+
+struct mt7621_spi;
+
+struct mt7621_spi {
+ struct spi_master *master;
+ void __iomem *base;
+ unsigned int sys_freq;
+ unsigned int speed;
+ struct clk *clk;
+ spinlock_t lock;
+
+ struct mt7621_spi_ops *ops;
+};
+
+static inline struct mt7621_spi *spidev_to_mt7621_spi(struct spi_device *spi)
+{
+ return spi_master_get_devdata(spi->master);
+}
+
+static inline u32 mt7621_spi_read(struct mt7621_spi *rs, u32 reg)
+{
+ return ioread32(rs->base + reg);
+}
+
+static inline void mt7621_spi_write(struct mt7621_spi *rs, u32 reg, u32 val)
+{
+ iowrite32(val, rs->base + reg);
+}
+
+static void mt7621_spi_reset(struct mt7621_spi *rs, int duplex)
+{
+ u32 master = mt7621_spi_read(rs, MT7621_SPI_MASTER);
+
+ master |= 7 << 29;
+ master |= 1 << 2;
+ if (duplex)
+ master |= 1 << 10;
+ else
+ master &= ~(1 << 10);
+
+ mt7621_spi_write(rs, MT7621_SPI_MASTER, master);
+}
+
+static void mt7621_spi_set_cs(struct spi_device *spi, int enable)
+{
+ struct mt7621_spi *rs = spidev_to_mt7621_spi(spi);
+ int cs = spi->chip_select;
+ u32 polar = 0;
+
+ mt7621_spi_reset(rs, cs);
+ if (enable)
+ polar = BIT(cs);
+ mt7621_spi_write(rs, MT7621_SPI_POLAR, polar);
+}
+
+static int mt7621_spi_prepare(struct spi_device *spi, unsigned int speed)
+{
+ struct mt7621_spi *rs = spidev_to_mt7621_spi(spi);
+ u32 rate;
+ u32 reg;
+
+ dev_dbg(&spi->dev, "speed:%u\n", speed);
+
+ rate = DIV_ROUND_UP(rs->sys_freq, speed);
+ dev_dbg(&spi->dev, "rate-1:%u\n", rate);
+
+ if (rate > 4097)
+ return -EINVAL;
+
+ if (rate < 2)
+ rate = 2;
+
+ reg = mt7621_spi_read(rs, MT7621_SPI_MASTER);
+ reg &= ~(0xfff << 16);
+ reg |= (rate - 2) << 16;
+ rs->speed = speed;
+
+ reg &= ~MT7621_LSB_FIRST;
+ if (spi->mode & SPI_LSB_FIRST)
+ reg |= MT7621_LSB_FIRST;
+
+ reg &= ~(MT7621_CPHA | MT7621_CPOL);
+ switch(spi->mode & (SPI_CPOL | SPI_CPHA)) {
+ case SPI_MODE_0:
+ break;
+ case SPI_MODE_1:
+ reg |= MT7621_CPHA;
+ break;
+ case SPI_MODE_2:
+ reg |= MT7621_CPOL;
+ break;
+ case SPI_MODE_3:
+ reg |= MT7621_CPOL | MT7621_CPHA;
+ break;
+ }
+ mt7621_spi_write(rs, MT7621_SPI_MASTER, reg);
+
+ return 0;
+}
+
+static inline int mt7621_spi_wait_till_ready(struct spi_device *spi)
+{
+ struct mt7621_spi *rs = spidev_to_mt7621_spi(spi);
+ int i;
+
+ for (i = 0; i < RALINK_SPI_WAIT_MAX_LOOP; i++) {
+ u32 status;
+
+ status = mt7621_spi_read(rs, MT7621_SPI_TRANS);
+ if ((status & SPITRANS_BUSY) == 0) {
+ return 0;
+ }
+ cpu_relax();
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int mt7621_spi_transfer_half_duplex(struct spi_master *master,
+ struct spi_message *m)
+{
+ struct mt7621_spi *rs = spi_master_get_devdata(master);
+ struct spi_device *spi = m->spi;
+ unsigned int speed = spi->max_speed_hz;
+ struct spi_transfer *t = NULL;
+ int status = 0;
+ int i, len = 0;
+ int rx_len = 0;
+ u32 data[9] = { 0 };
+ u32 val;
+
+ mt7621_spi_wait_till_ready(spi);
+
+ list_for_each_entry(t, &m->transfers, transfer_list) {
+ const u8 *buf = t->tx_buf;
+
+ if (t->rx_buf)
+ rx_len += t->len;
+
+ if (!buf)
+ continue;
+
+ if (t->speed_hz < speed)
+ speed = t->speed_hz;
+
+ if (WARN_ON(len + t->len > 36)) {
+ status = -EIO;
+ goto msg_done;
+ }
+
+ for (i = 0; i < t->len; i++, len++)
+ data[len / 4] |= buf[i] << (8 * (len & 3));
+ }
+
+ if (WARN_ON(rx_len > 32)) {
+ status = -EIO;
+ goto msg_done;
+ }
+
+ if (mt7621_spi_prepare(spi, speed)) {
+ status = -EIO;
+ goto msg_done;
+ }
+ data[0] = swab32(data[0]);
+ if (len < 4)
+ data[0] >>= (4 - len) * 8;
+
+ for (i = 0; i < len; i += 4)
+ mt7621_spi_write(rs, MT7621_SPI_OPCODE + i, data[i / 4]);
+
+ val = (min_t(int, len, 4) * 8) << 24;
+ if (len > 4)
+ val |= (len - 4) * 8;
+ val |= (rx_len * 8) << 12;
+ mt7621_spi_write(rs, MT7621_SPI_MOREBUF, val);
+
+ mt7621_spi_set_cs(spi, 1);
+
+ val = mt7621_spi_read(rs, MT7621_SPI_TRANS);
+ val |= SPI_CTL_START;
+ mt7621_spi_write(rs, MT7621_SPI_TRANS, val);
+
+ mt7621_spi_wait_till_ready(spi);
+
+ mt7621_spi_set_cs(spi, 0);
+
+ for (i = 0; i < rx_len; i += 4)
+ data[i / 4] = mt7621_spi_read(rs, MT7621_SPI_DATA0 + i);
+
+ m->actual_length = len + rx_len;
+
+ len = 0;
+ list_for_each_entry(t, &m->transfers, transfer_list) {
+ u8 *buf = t->rx_buf;
+
+ if (!buf)
+ continue;
+
+ for (i = 0; i < t->len; i++, len++)
+ buf[i] = data[len / 4] >> (8 * (len & 3));
+ }
+
+msg_done:
+ m->status = status;
+ spi_finalize_current_message(master);
+
+ return 0;
+}
+
+static int mt7621_spi_transfer_full_duplex(struct spi_master *master,
+ struct spi_message *m)
+{
+ struct mt7621_spi *rs = spi_master_get_devdata(master);
+ struct spi_device *spi = m->spi;
+ unsigned int speed = spi->max_speed_hz;
+ struct spi_transfer *t = NULL;
+ int status = 0;
+ int i, len = 0;
+ int rx_len = 0;
+ u32 data[9] = { 0 };
+ u32 val = 0;
+
+ mt7621_spi_wait_till_ready(spi);
+
+ list_for_each_entry(t, &m->transfers, transfer_list) {
+ const u8 *buf = t->tx_buf;
+
+ if (t->rx_buf)
+ rx_len += t->len;
+
+ if (!buf)
+ continue;
+
+ if (WARN_ON(len + t->len > 16)) {
+ status = -EIO;
+ goto msg_done;
+ }
+
+ for (i = 0; i < t->len; i++, len++)
+ data[len / 4] |= buf[i] << (8 * (len & 3));
+ if (speed > t->speed_hz)
+ speed = t->speed_hz;
+ }
+
+ if (WARN_ON(rx_len > 16)) {
+ status = -EIO;
+ goto msg_done;
+ }
+
+ if (mt7621_spi_prepare(spi, speed)) {
+ status = -EIO;
+ goto msg_done;
+ }
+
+ for (i = 0; i < len; i += 4)
+ mt7621_spi_write(rs, MT7621_SPI_DATA0 + i, data[i / 4]);
+
+ val |= len * 8;
+ val |= (rx_len * 8) << 12;
+ mt7621_spi_write(rs, MT7621_SPI_MOREBUF, val);
+
+ mt7621_spi_set_cs(spi, 1);
+
+ val = mt7621_spi_read(rs, MT7621_SPI_TRANS);
+ val |= SPI_CTL_START;
+ mt7621_spi_write(rs, MT7621_SPI_TRANS, val);
+
+ mt7621_spi_wait_till_ready(spi);
+
+ mt7621_spi_set_cs(spi, 0);
+
+ for (i = 0; i < rx_len; i += 4)
+ data[i / 4] = mt7621_spi_read(rs, MT7621_SPI_DATA4 + i);
+
+ m->actual_length = rx_len;
+
+ len = 0;
+ list_for_each_entry(t, &m->transfers, transfer_list) {
+ u8 *buf = t->rx_buf;
+
+ if (!buf)
+ continue;
+
+ for (i = 0; i < t->len; i++, len++)
+ buf[i] = data[len / 4] >> (8 * (len & 3));
+ }
+
+msg_done:
+ m->status = status;
+ spi_finalize_current_message(master);
+
+ return 0;
+}
+
+static int mt7621_spi_transfer_one_message(struct spi_master *master,
+ struct spi_message *m)
+{
+ struct spi_device *spi = m->spi;
+ int cs = spi->chip_select;
+
+ if (cs)
+ return mt7621_spi_transfer_full_duplex(master, m);
+ return mt7621_spi_transfer_half_duplex(master, m);
+}
+
+static int mt7621_spi_setup(struct spi_device *spi)
+{
+ struct mt7621_spi *rs = spidev_to_mt7621_spi(spi);
+
+ if ((spi->max_speed_hz == 0) ||
+ (spi->max_speed_hz > (rs->sys_freq / 2)))
+ spi->max_speed_hz = (rs->sys_freq / 2);
+
+ if (spi->max_speed_hz < (rs->sys_freq / 4097)) {
+ dev_err(&spi->dev, "setup: requested speed is too low %d Hz\n",
+ spi->max_speed_hz);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id mt7621_spi_match[] = {
+ { .compatible = "ralink,mt7621-spi" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt7621_spi_match);
+
+static size_t max_transfer_size(struct spi_device *spi)
+{
+ return 32;
+}
+
+static int mt7621_spi_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct spi_master *master;
+ struct mt7621_spi *rs;
+ unsigned long flags;
+ void __iomem *base;
+ struct resource *r;
+ int status = 0;
+ struct clk *clk;
+ struct mt7621_spi_ops *ops;
+
+ match = of_match_device(mt7621_spi_match, &pdev->dev);
+ if (!match)
+ return -EINVAL;
+ ops = (struct mt7621_spi_ops *)match->data;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "unable to get SYS clock, err=%d\n",
+ status);
+ return PTR_ERR(clk);
+ }
+
+ status = clk_prepare_enable(clk);
+ if (status)
+ return status;
+
+ master = spi_alloc_master(&pdev->dev, sizeof(*rs));
+ if (master == NULL) {
+ dev_info(&pdev->dev, "master allocation failed\n");
+ return -ENOMEM;
+ }
+
+ master->mode_bits = RT2880_SPI_MODE_BITS;
+
+ master->setup = mt7621_spi_setup;
+ master->transfer_one_message = mt7621_spi_transfer_one_message;
+ master->bits_per_word_mask = SPI_BPW_MASK(8);
+ master->dev.of_node = pdev->dev.of_node;
+ master->num_chipselect = 2;
+ master->max_transfer_size = max_transfer_size;
+
+ dev_set_drvdata(&pdev->dev, master);
+
+ rs = spi_master_get_devdata(master);
+ rs->base = base;
+ rs->clk = clk;
+ rs->master = master;
+ rs->sys_freq = clk_get_rate(rs->clk);
+ rs->ops = ops;
+ dev_info(&pdev->dev, "sys_freq: %u\n", rs->sys_freq);
+ spin_lock_irqsave(&rs->lock, flags);
+
+ device_reset(&pdev->dev);
+
+ mt7621_spi_reset(rs, 0);
+
+ return spi_register_master(master);
+}
+
+static int mt7621_spi_remove(struct platform_device *pdev)
+{
+ struct spi_master *master;
+ struct mt7621_spi *rs;
+
+ master = dev_get_drvdata(&pdev->dev);
+ rs = spi_master_get_devdata(master);
+
+ clk_disable(rs->clk);
+ spi_unregister_master(master);
+
+ return 0;
+}
+
+MODULE_ALIAS("platform:" DRIVER_NAME);
+
+static struct platform_driver mt7621_spi_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = mt7621_spi_match,
+ },
+ .probe = mt7621_spi_probe,
+ .remove = mt7621_spi_remove,
+};
+
+module_platform_driver(mt7621_spi_driver);
+
+MODULE_DESCRIPTION("MT7621 SPI driver");
+MODULE_AUTHOR("Felix Fietkau <nbd@nbd.name>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/netlogic/xlr_net.c b/drivers/staging/netlogic/xlr_net.c
index 30532d8c310b..e461168313bf 100644
--- a/drivers/staging/netlogic/xlr_net.c
+++ b/drivers/staging/netlogic/xlr_net.c
@@ -993,8 +993,7 @@ static int xlr_net_probe(struct platform_device *pdev)
/*
* Allocate our adapter data structure and attach it to the device.
*/
- adapter = (struct xlr_adapter *)
- devm_kzalloc(&pdev->dev, sizeof(*adapter), GFP_KERNEL);
+ adapter = devm_kzalloc(&pdev->dev, sizeof(*adapter), GFP_KERNEL);
if (!adapter)
return -ENOMEM;
diff --git a/drivers/staging/pi433/Documentation/pi433.txt b/drivers/staging/pi433/Documentation/pi433.txt
index 7d9dc2244848..d0b7000faafc 100644
--- a/drivers/staging/pi433/Documentation/pi433.txt
+++ b/drivers/staging/pi433/Documentation/pi433.txt
@@ -92,7 +92,7 @@ rf params:
shaping0_3 - gauss filter with BT 0.3 (FSK only)
shapingBR - filter cut off at BR (OOK only)
shaping2BR - filter cut off at 2*BR (OOK only)
- paRamp (FSK only)
+ pa_ramp (FSK only)
ramp3400 - amp ramps up in 3.4ms
ramp2000 - amp ramps up in 2.0ms
ramp1000 - amp ramps up in 1ms
@@ -180,7 +180,7 @@ rf params:
threshold value for the signal strength on the receiver input.
If this value is exceeded, a reception cycle starts
Allowed values: 0...255
- thresholdDecrement
+ threshold_decrement
in order to adapt to different levels of singnal strength, over
time the receiver gets more and more sensitive. This value
determs, how fast the sensitivity increases.
@@ -192,11 +192,11 @@ rf params:
step_4_0db - increase in 4 db steps
step_5_0db - increase in 5 db steps
step_6_0db - increase in 6 db steps
- antennaImpedance
+ antenna_impedance
sets the electrical adoption of the antenna
- fiftyOhm - for antennas with an impedance of 50Ohm
- twohundretOhm - for antennas with an impedance of 200Ohm
- lnaGain
+ fifty_ohm - for antennas with an impedance of 50Ohm
+ two_hundred_ohm - for antennas with an impedance of 200Ohm
+ lna_gain
sets the gain of the low noise amp
automatic - lna gain is determined by an agc
max - lna gain is set to maximum
@@ -215,9 +215,9 @@ rf params:
Allowd values: 0...7
dagc;
operation mode of the digital automatic gain control
- normalMode
+ normal_mode
improve
- improve4LowModulationIndex
+ improve_for_low_modulation_index
packet format:
enable_sync
@@ -232,12 +232,12 @@ rf params:
amount of bytes that were requested by the read request.
Attention: should be used in combination with sync, only
enable_address_filtering;
- filteringOff - no address filtering will take place
- nodeAddress - all telegrams, not matching the node
- address will be internally discarded
- nodeOrBroadcastAddress - all telegrams, neither matching the
- node, nor the broadcast address will
- be internally discarded
+ filtering_off - no address filtering will take place
+ node_address - all telegrams, not matching the node
+ address will be internally discarded
+ node_or_broadcast_address - all telegrams, neither matching the
+ node, nor the broadcast address will
+ be internally discarded
Attention: Sync option must be enabled in order to use this feature
enable_crc
optionOn - a crc will be calculated over the payload of
diff --git a/drivers/staging/pi433/pi433_if.c b/drivers/staging/pi433/pi433_if.c
index edcd7e798f99..d1e0ddbc79ce 100644
--- a/drivers/staging/pi433/pi433_if.c
+++ b/drivers/staging/pi433/pi433_if.c
@@ -187,10 +187,12 @@ rf69_set_rx_cfg(struct pi433_device *dev, struct pi433_rx_cfg *rx_cfg)
ret = rf69_set_ook_threshold_dec(dev->spi, rx_cfg->threshold_decrement);
if (ret < 0)
return ret;
- ret = rf69_set_bandwidth(dev->spi, rx_cfg->bw_mantisse, rx_cfg->bw_exponent);
+ ret = rf69_set_bandwidth(dev->spi, rx_cfg->bw_mantisse,
+ rx_cfg->bw_exponent);
if (ret < 0)
return ret;
- ret = rf69_set_bandwidth_during_afc(dev->spi, rx_cfg->bw_mantisse, rx_cfg->bw_exponent);
+ ret = rf69_set_bandwidth_during_afc(dev->spi, rx_cfg->bw_mantisse,
+ rx_cfg->bw_exponent);
if (ret < 0)
return ret;
ret = rf69_set_dagc(dev->spi, rx_cfg->dagc);
@@ -206,7 +208,8 @@ rf69_set_rx_cfg(struct pi433_device *dev, struct pi433_rx_cfg *rx_cfg)
if (ret < 0)
return ret;
- ret = rf69_set_fifo_fill_condition(dev->spi, afterSyncInterrupt);
+ ret = rf69_set_fifo_fill_condition(dev->spi,
+ after_sync_interrupt);
if (ret < 0)
return ret;
} else {
@@ -219,15 +222,16 @@ rf69_set_rx_cfg(struct pi433_device *dev, struct pi433_rx_cfg *rx_cfg)
return ret;
}
if (rx_cfg->enable_length_byte == OPTION_ON) {
- ret = rf69_set_packet_format(dev->spi, packetLengthVar);
+ ret = rf69_set_packet_format(dev->spi, packet_length_var);
if (ret < 0)
return ret;
} else {
- ret = rf69_set_packet_format(dev->spi, packetLengthFix);
+ ret = rf69_set_packet_format(dev->spi, packet_length_fix);
if (ret < 0)
return ret;
}
- ret = rf69_set_adressFiltering(dev->spi, rx_cfg->enable_address_filtering);
+ ret = rf69_set_address_filtering(dev->spi,
+ rx_cfg->enable_address_filtering);
if (ret < 0)
return ret;
@@ -253,7 +257,7 @@ rf69_set_rx_cfg(struct pi433_device *dev, struct pi433_rx_cfg *rx_cfg)
payload_length = rx_cfg->fixed_message_length;
if (rx_cfg->enable_length_byte == OPTION_ON)
payload_length++;
- if (rx_cfg->enable_address_filtering != filteringOff)
+ if (rx_cfg->enable_address_filtering != filtering_off)
payload_length++;
ret = rf69_set_payload_length(dev->spi, payload_length);
if (ret < 0)
@@ -270,11 +274,12 @@ rf69_set_rx_cfg(struct pi433_device *dev, struct pi433_rx_cfg *rx_cfg)
if (ret < 0)
return ret;
}
- if (rx_cfg->enable_address_filtering != filteringOff) {
+ if (rx_cfg->enable_address_filtering != filtering_off) {
ret = rf69_set_node_address(dev->spi, rx_cfg->node_address);
if (ret < 0)
return ret;
- ret = rf69_set_broadcast_address(dev->spi, rx_cfg->broadcast_address);
+ ret = rf69_set_broadcast_address(dev->spi,
+ rx_cfg->broadcast_address);
if (ret < 0)
return ret;
}
@@ -311,7 +316,8 @@ rf69_set_tx_cfg(struct pi433_device *dev, struct pi433_tx_cfg *tx_cfg)
/* packet format enable */
if (tx_cfg->enable_preamble == OPTION_ON) {
- ret = rf69_set_preamble_length(dev->spi, tx_cfg->preamble_length);
+ ret = rf69_set_preamble_length(dev->spi,
+ tx_cfg->preamble_length);
if (ret < 0)
return ret;
} else {
@@ -331,11 +337,11 @@ rf69_set_tx_cfg(struct pi433_device *dev, struct pi433_tx_cfg *tx_cfg)
}
if (tx_cfg->enable_length_byte == OPTION_ON) {
- ret = rf69_set_packet_format(dev->spi, packetLengthVar);
+ ret = rf69_set_packet_format(dev->spi, packet_length_var);
if (ret < 0)
return ret;
} else {
- ret = rf69_set_packet_format(dev->spi, packetLengthFix);
+ ret = rf69_set_packet_format(dev->spi, packet_length_fix);
if (ret < 0)
return ret;
}
@@ -437,7 +443,7 @@ pi433_receive(void *data)
return retval;
/* now check RSSI, if low wait for getting high (RSSI interrupt) */
- while (!rf69_get_flag(dev->spi, rssiExceededThreshold)) {
+ while (!rf69_get_flag(dev->spi, rssi_exceeded_threshold)) {
/* allow tx to interrupt us while waiting for high RSSI */
dev->interrupt_rx_allowed = true;
wake_up_interruptible(&dev->tx_wait_queue);
@@ -446,7 +452,7 @@ pi433_receive(void *data)
dev_dbg(dev->dev, "rx: going to wait for high RSSI level");
retval = wait_event_interruptible(dev->rx_wait_queue,
rf69_get_flag(dev->spi,
- rssiExceededThreshold));
+ rssi_exceeded_threshold));
if (retval) /* wait was interrupted */
goto abort;
dev->interrupt_rx_allowed = false;
@@ -470,10 +476,12 @@ pi433_receive(void *data)
goto abort;
}
bytes_total = dev->rx_cfg.fixed_message_length;
- dev_dbg(dev->dev, "rx: msg len set to %d by fixed length", bytes_total);
+ dev_dbg(dev->dev, "rx: msg len set to %d by fixed length",
+ bytes_total);
} else {
bytes_total = dev->rx_buffer_size;
- dev_dbg(dev->dev, "rx: msg len set to %d as requested by read", bytes_total);
+ dev_dbg(dev->dev, "rx: msg len set to %d as requested by read",
+ bytes_total);
}
/* length byte enabled? */
@@ -489,11 +497,12 @@ pi433_receive(void *data)
goto abort;
}
dev->free_in_fifo++;
- dev_dbg(dev->dev, "rx: msg len reset to %d due to length byte", bytes_total);
+ dev_dbg(dev->dev, "rx: msg len reset to %d due to length byte",
+ bytes_total);
}
/* address byte enabled? */
- if (dev->rx_cfg.enable_address_filtering != filteringOff) {
+ if (dev->rx_cfg.enable_address_filtering != filtering_off) {
u8 dummy;
bytes_total--;
@@ -519,7 +528,8 @@ pi433_receive(void *data)
/* need to drop bytes or acquire? */
if (dev->rx_bytes_to_drop > dev->rx_bytes_dropped)
- bytes_to_read = dev->rx_bytes_to_drop - dev->rx_bytes_dropped;
+ bytes_to_read = dev->rx_bytes_to_drop -
+ dev->rx_bytes_dropped;
else
bytes_to_read = bytes_total - dev->rx_position;
@@ -620,8 +630,10 @@ pi433_tx_thread(void *data)
device->buffer[position++] = tx_cfg.address_byte;
/* finally get message data from fifo */
- retval = kfifo_out(&device->tx_fifo, &device->buffer[position], sizeof(device->buffer) - position);
- dev_dbg(device->dev, "read %d message byte(s) from fifo queue.", retval);
+ retval = kfifo_out(&device->tx_fifo, &device->buffer[position],
+ sizeof(device->buffer) - position);
+ dev_dbg(device->dev,
+ "read %d message byte(s) from fifo queue.", retval);
mutex_unlock(&device->tx_fifo_lock);
/* if rx is active, we need to interrupt the waiting for
@@ -699,12 +711,13 @@ pi433_tx_thread(void *data)
while ((repetitions > 0) && (size > position)) {
if ((size - position) > device->free_in_fifo) {
/* msg to big for fifo - take a part */
- int temp = device->free_in_fifo;
+ int write_size = device->free_in_fifo;
+
device->free_in_fifo = 0;
rf69_write_fifo(spi,
&device->buffer[position],
- temp);
- position += temp;
+ write_size);
+ position += write_size;
} else {
/* msg fits into fifo - take all */
device->free_in_fifo -= size;
@@ -724,7 +737,8 @@ pi433_tx_thread(void *data)
}
/* we are done. Wait for packet to get sent */
- dev_dbg(device->dev, "thread: wait for packet to get sent/fifo to be empty");
+ dev_dbg(device->dev,
+ "thread: wait for packet to get sent/fifo to be empty");
wait_event_interruptible(device->fifo_wait_queue,
device->free_in_fifo == FIFO_SIZE ||
kthread_should_stop());
@@ -819,7 +833,8 @@ pi433_write(struct file *filp, const char __user *buf,
* - message
*/
mutex_lock(&device->tx_fifo_lock);
- retval = kfifo_in(&device->tx_fifo, &instance->tx_cfg, sizeof(instance->tx_cfg));
+ retval = kfifo_in(&device->tx_fifo, &instance->tx_cfg,
+ sizeof(instance->tx_cfg));
if (retval != sizeof(instance->tx_cfg))
goto abort;
@@ -983,7 +998,7 @@ static int pi433_release(struct inode *inode, struct file *filp)
/*-------------------------------------------------------------------------*/
-static int setup_GPIOs(struct pi433_device *device)
+static int setup_gpio(struct pi433_device *device)
{
char name[5];
int retval;
@@ -996,10 +1011,12 @@ static int setup_GPIOs(struct pi433_device *device)
for (i = 0; i < NUM_DIO; i++) {
/* "construct" name and get the gpio descriptor */
snprintf(name, sizeof(name), "DIO%d", i);
- device->gpiod[i] = gpiod_get(&device->spi->dev, name, 0 /*GPIOD_IN*/);
+ device->gpiod[i] = gpiod_get(&device->spi->dev, name,
+ 0 /*GPIOD_IN*/);
if (device->gpiod[i] == ERR_PTR(-ENOENT)) {
- dev_dbg(&device->spi->dev, "Could not find entry for %s. Ignoring.", name);
+ dev_dbg(&device->spi->dev,
+ "Could not find entry for %s. Ignoring.", name);
continue;
}
@@ -1043,7 +1060,7 @@ static int setup_GPIOs(struct pi433_device *device)
return 0;
}
-static void free_GPIOs(struct pi433_device *device)
+static void free_gpio(struct pi433_device *device)
{
int i;
@@ -1158,7 +1175,7 @@ static int pi433_probe(struct spi_device *spi)
mutex_init(&device->rx_lock);
/* setup GPIO (including irq_handler) for the different DIOs */
- retval = setup_GPIOs(device);
+ retval = setup_gpio(device);
if (retval) {
dev_dbg(&spi->dev, "setup of GPIOs failed");
goto GPIO_failed;
@@ -1183,7 +1200,7 @@ static int pi433_probe(struct spi_device *spi)
retval = rf69_set_output_power_level(spi, 13);
if (retval < 0)
goto minor_failed;
- retval = rf69_set_antenna_impedance(spi, fiftyOhm);
+ retval = rf69_set_antenna_impedance(spi, fifty_ohm);
if (retval < 0)
goto minor_failed;
@@ -1245,7 +1262,7 @@ send_thread_failed:
device_create_failed:
pi433_free_minor(device);
minor_failed:
- free_GPIOs(device);
+ free_gpio(device);
GPIO_failed:
kfree(device);
@@ -1257,7 +1274,7 @@ static int pi433_remove(struct spi_device *spi)
struct pi433_device *device = spi_get_drvdata(spi);
/* free GPIOs */
- free_GPIOs(device);
+ free_gpio(device);
/* make sure ops on existing fds can abort cleanly */
device->spi = NULL;
@@ -1315,7 +1332,7 @@ static int __init pi433_init(void)
* that will key udev/mdev to add/remove /dev nodes. Last, register
* Last, register the driver which manages those device numbers.
*/
- status = alloc_chrdev_region(&pi433_dev, 0 /*firstminor*/, N_PI433_MINORS /*count*/, "pi433" /*name*/);
+ status = alloc_chrdev_region(&pi433_dev, 0, N_PI433_MINORS, "pi433");
if (status < 0)
return status;
diff --git a/drivers/staging/pi433/pi433_if.h b/drivers/staging/pi433/pi433_if.h
index 7314f69af198..b6e214c29ddf 100644
--- a/drivers/staging/pi433/pi433_if.h
+++ b/drivers/staging/pi433/pi433_if.h
@@ -67,9 +67,9 @@ struct pi433_tx_cfg {
enum modulation modulation;
enum mod_shaping mod_shaping;
- enum paRamp pa_ramp;
+ enum pa_ramp pa_ramp;
- enum txStartCondition tx_start_condition;
+ enum tx_start_condition tx_start_condition;
__u16 repetitions;
@@ -115,9 +115,9 @@ struct pi433_rx_cfg {
enum modulation modulation;
__u8 rssi_threshold;
- enum thresholdDecrement threshold_decrement;
- enum antennaImpedance antenna_impedance;
- enum lnaGain lna_gain;
+ enum threshold_decrement threshold_decrement;
+ enum antenna_impedance antenna_impedance;
+ enum lna_gain lna_gain;
enum mantisse bw_mantisse; /* normal: 0x50 */
__u8 bw_exponent; /* during AFC: 0x8b */
enum dagc dagc;
@@ -125,7 +125,7 @@ struct pi433_rx_cfg {
/* packet format */
enum option_on_off enable_sync;
enum option_on_off enable_length_byte; /* should be used in combination with sync, only */
- enum addressFiltering enable_address_filtering; /* operational with sync, only */
+ enum address_filtering enable_address_filtering; /* operational with sync, only */
enum option_on_off enable_crc; /* only operational, if sync on and fixed length or length byte is used */
__u8 sync_length;
diff --git a/drivers/staging/pi433/rf69.c b/drivers/staging/pi433/rf69.c
index 7ccdff6ae213..5b0554823263 100644
--- a/drivers/staging/pi433/rf69.c
+++ b/drivers/staging/pi433/rf69.c
@@ -151,11 +151,11 @@ int rf69_set_modulation(struct spi_device *spi, enum modulation modulation)
static enum modulation rf69_get_modulation(struct spi_device *spi)
{
- u8 currentValue;
+ u8 modulation_reg;
- currentValue = rf69_read_reg(spi, REG_DATAMODUL);
+ modulation_reg = rf69_read_reg(spi, REG_DATAMODUL);
- switch (currentValue & MASK_DATAMODUL_MODULATION_TYPE) {
+ switch (modulation_reg & MASK_DATAMODUL_MODULATION_TYPE) {
case DATAMODUL_MODULATION_TYPE_OOK:
return OOK;
case DATAMODUL_MODULATION_TYPE_FSK:
@@ -201,26 +201,26 @@ int rf69_set_modulation_shaping(struct spi_device *spi,
}
}
-int rf69_set_bit_rate(struct spi_device *spi, u16 bitRate)
+int rf69_set_bit_rate(struct spi_device *spi, u16 bit_rate)
{
int retval;
- u32 bitRate_min;
- u32 bitRate_reg;
+ u32 bit_rate_min;
+ u32 bit_rate_reg;
u8 msb;
u8 lsb;
// check input value
- bitRate_min = F_OSC / 8388608; // 8388608 = 2^23;
- if (bitRate < bitRate_min) {
+ bit_rate_min = F_OSC / 8388608; // 8388608 = 2^23;
+ if (bit_rate < bit_rate_min) {
dev_dbg(&spi->dev, "setBitRate: illegal input param");
return -EINVAL;
}
// calculate reg settings
- bitRate_reg = (F_OSC / bitRate);
+ bit_rate_reg = (F_OSC / bit_rate);
- msb = (bitRate_reg & 0xff00) >> 8;
- lsb = (bitRate_reg & 0xff);
+ msb = (bit_rate_reg & 0xff00) >> 8;
+ lsb = (bit_rate_reg & 0xff);
// transmit to RF 69
retval = rf69_write_reg(spi, REG_BITRATE_MSB, msb);
@@ -330,24 +330,24 @@ int rf69_disable_amplifier(struct spi_device *spi, u8 amplifier_mask)
return rf69_clear_bit(spi, REG_PALEVEL, amplifier_mask);
}
-int rf69_set_output_power_level(struct spi_device *spi, u8 powerLevel)
+int rf69_set_output_power_level(struct spi_device *spi, u8 power_level)
{
// TODO: Dependency to PA0,1,2 setting
- powerLevel += 18;
+ power_level += 18;
// check input value
- if (powerLevel > 0x1f) {
+ if (power_level > 0x1f) {
dev_dbg(&spi->dev, "set: illegal input param");
return -EINVAL;
}
// write value
- return rf69_read_mod_write(spi, REG_PALEVEL, MASK_PALEVEL_OUTPUT_POWER, powerLevel);
+ return rf69_read_mod_write(spi, REG_PALEVEL, MASK_PALEVEL_OUTPUT_POWER, power_level);
}
-int rf69_set_pa_ramp(struct spi_device *spi, enum paRamp paRamp)
+int rf69_set_pa_ramp(struct spi_device *spi, enum pa_ramp pa_ramp)
{
- switch (paRamp) {
+ switch (pa_ramp) {
case ramp3400:
return rf69_write_reg(spi, REG_PARAMP, PARAMP_3400);
case ramp2000:
@@ -386,12 +386,12 @@ int rf69_set_pa_ramp(struct spi_device *spi, enum paRamp paRamp)
}
}
-int rf69_set_antenna_impedance(struct spi_device *spi, enum antennaImpedance antennaImpedance)
+int rf69_set_antenna_impedance(struct spi_device *spi, enum antenna_impedance antenna_impedance)
{
- switch (antennaImpedance) {
- case fiftyOhm:
+ switch (antenna_impedance) {
+ case fifty_ohm:
return rf69_clear_bit(spi, REG_LNA, MASK_LNA_ZIN);
- case twohundretOhm:
+ case two_hundred_ohm:
return rf69_set_bit(spi, REG_LNA, MASK_LNA_ZIN);
default:
dev_dbg(&spi->dev, "set: illegal input param");
@@ -399,9 +399,9 @@ int rf69_set_antenna_impedance(struct spi_device *spi, enum antennaImpedance ant
}
}
-int rf69_set_lna_gain(struct spi_device *spi, enum lnaGain lnaGain)
+int rf69_set_lna_gain(struct spi_device *spi, enum lna_gain lna_gain)
{
- switch (lnaGain) {
+ switch (lna_gain) {
case automatic:
return rf69_read_mod_write(spi, REG_LNA, MASK_LNA_GAIN, LNA_GAIN_AUTO);
case max:
@@ -425,7 +425,7 @@ int rf69_set_lna_gain(struct spi_device *spi, enum lnaGain lnaGain)
static int rf69_set_bandwidth_intern(struct spi_device *spi, u8 reg,
enum mantisse mantisse, u8 exponent)
{
- u8 newValue;
+ u8 bandwidth;
// check value for mantisse and exponent
if (exponent > 7) {
@@ -441,29 +441,29 @@ static int rf69_set_bandwidth_intern(struct spi_device *spi, u8 reg,
}
// read old value
- newValue = rf69_read_reg(spi, reg);
+ bandwidth = rf69_read_reg(spi, reg);
// "delete" mantisse and exponent = just keep the DCC setting
- newValue = newValue & MASK_BW_DCC_FREQ;
+ bandwidth = bandwidth & MASK_BW_DCC_FREQ;
// add new mantisse
switch (mantisse) {
case mantisse16:
- newValue = newValue | BW_MANT_16;
+ bandwidth = bandwidth | BW_MANT_16;
break;
case mantisse20:
- newValue = newValue | BW_MANT_20;
+ bandwidth = bandwidth | BW_MANT_20;
break;
case mantisse24:
- newValue = newValue | BW_MANT_24;
+ bandwidth = bandwidth | BW_MANT_24;
break;
}
// add new exponent
- newValue = newValue | exponent;
+ bandwidth = bandwidth | exponent;
// write back
- return rf69_write_reg(spi, reg, newValue);
+ return rf69_write_reg(spi, reg, bandwidth);
}
int rf69_set_bandwidth(struct spi_device *spi, enum mantisse mantisse, u8 exponent)
@@ -476,9 +476,9 @@ int rf69_set_bandwidth_during_afc(struct spi_device *spi, enum mantisse mantisse
return rf69_set_bandwidth_intern(spi, REG_AFCBW, mantisse, exponent);
}
-int rf69_set_ook_threshold_dec(struct spi_device *spi, enum thresholdDecrement thresholdDecrement)
+int rf69_set_ook_threshold_dec(struct spi_device *spi, enum threshold_decrement threshold_decrement)
{
- switch (thresholdDecrement) {
+ switch (threshold_decrement) {
case dec_every8th:
return rf69_read_mod_write(spi, REG_OOKPEAK, MASK_OOKPEAK_THRESDEC, OOKPEAK_THRESHDEC_EVERY_8TH);
case dec_every4th:
@@ -501,31 +501,31 @@ int rf69_set_ook_threshold_dec(struct spi_device *spi, enum thresholdDecrement t
}
}
-int rf69_set_dio_mapping(struct spi_device *spi, u8 DIONumber, u8 value)
+int rf69_set_dio_mapping(struct spi_device *spi, u8 dio_number, u8 value)
{
u8 mask;
u8 shift;
- u8 regaddr;
- u8 regValue;
+ u8 dio_addr;
+ u8 dio_value;
- switch (DIONumber) {
+ switch (dio_number) {
case 0:
- mask = MASK_DIO0; shift = SHIFT_DIO0; regaddr = REG_DIOMAPPING1;
+ mask = MASK_DIO0; shift = SHIFT_DIO0; dio_addr = REG_DIOMAPPING1;
break;
case 1:
- mask = MASK_DIO1; shift = SHIFT_DIO1; regaddr = REG_DIOMAPPING1;
+ mask = MASK_DIO1; shift = SHIFT_DIO1; dio_addr = REG_DIOMAPPING1;
break;
case 2:
- mask = MASK_DIO2; shift = SHIFT_DIO2; regaddr = REG_DIOMAPPING1;
+ mask = MASK_DIO2; shift = SHIFT_DIO2; dio_addr = REG_DIOMAPPING1;
break;
case 3:
- mask = MASK_DIO3; shift = SHIFT_DIO3; regaddr = REG_DIOMAPPING1;
+ mask = MASK_DIO3; shift = SHIFT_DIO3; dio_addr = REG_DIOMAPPING1;
break;
case 4:
- mask = MASK_DIO4; shift = SHIFT_DIO4; regaddr = REG_DIOMAPPING2;
+ mask = MASK_DIO4; shift = SHIFT_DIO4; dio_addr = REG_DIOMAPPING2;
break;
case 5:
- mask = MASK_DIO5; shift = SHIFT_DIO5; regaddr = REG_DIOMAPPING2;
+ mask = MASK_DIO5; shift = SHIFT_DIO5; dio_addr = REG_DIOMAPPING2;
break;
default:
dev_dbg(&spi->dev, "set: illegal input param");
@@ -533,33 +533,33 @@ int rf69_set_dio_mapping(struct spi_device *spi, u8 DIONumber, u8 value)
}
// read reg
- regValue = rf69_read_reg(spi, regaddr);
+ dio_value = rf69_read_reg(spi, dio_addr);
// delete old value
- regValue = regValue & ~mask;
+ dio_value = dio_value & ~mask;
// add new value
- regValue = regValue | value << shift;
+ dio_value = dio_value | value << shift;
// write back
- return rf69_write_reg(spi, regaddr, regValue);
+ return rf69_write_reg(spi, dio_addr, dio_value);
}
bool rf69_get_flag(struct spi_device *spi, enum flag flag)
{
switch (flag) {
- case modeSwitchCompleted:
+ case mode_switch_completed:
return (rf69_read_reg(spi, REG_IRQFLAGS1) & MASK_IRQFLAGS1_MODE_READY);
- case readyToReceive:
+ case ready_to_receive:
return (rf69_read_reg(spi, REG_IRQFLAGS1) & MASK_IRQFLAGS1_RX_READY);
- case readyToSend:
+ case ready_to_send:
return (rf69_read_reg(spi, REG_IRQFLAGS1) & MASK_IRQFLAGS1_TX_READY);
- case pllLocked:
+ case pll_locked:
return (rf69_read_reg(spi, REG_IRQFLAGS1) & MASK_IRQFLAGS1_PLL_LOCK);
- case rssiExceededThreshold:
+ case rssi_exceeded_threshold:
return (rf69_read_reg(spi, REG_IRQFLAGS1) & MASK_IRQFLAGS1_RSSI);
case timeout:
return (rf69_read_reg(spi, REG_IRQFLAGS1) & MASK_IRQFLAGS1_TIMEOUT);
case automode:
return (rf69_read_reg(spi, REG_IRQFLAGS1) & MASK_IRQFLAGS1_AUTOMODE);
- case syncAddressMatch:
+ case sync_address_match:
return (rf69_read_reg(spi, REG_IRQFLAGS1) & MASK_IRQFLAGS1_SYNC_ADDRESS_MATCH);
case fifo_full:
return (rf69_read_reg(spi, REG_IRQFLAGS2) & MASK_IRQFLAGS2_FIFO_FULL);
@@ -571,13 +571,13 @@ bool rf69_get_flag(struct spi_device *spi, enum flag flag)
return (rf69_read_reg(spi, REG_IRQFLAGS2) & MASK_IRQFLAGS2_FIFO_LEVEL);
case fifo_overrun:
return (rf69_read_reg(spi, REG_IRQFLAGS2) & MASK_IRQFLAGS2_FIFO_OVERRUN);
- case packetSent:
+ case packet_sent:
return (rf69_read_reg(spi, REG_IRQFLAGS2) & MASK_IRQFLAGS2_PACKET_SENT);
case payload_ready:
return (rf69_read_reg(spi, REG_IRQFLAGS2) & MASK_IRQFLAGS2_PAYLOAD_READY);
- case crcOk:
+ case crc_ok:
return (rf69_read_reg(spi, REG_IRQFLAGS2) & MASK_IRQFLAGS2_CRC_OK);
- case batteryLow:
+ case battery_low:
return (rf69_read_reg(spi, REG_IRQFLAGS2) & MASK_IRQFLAGS2_LOW_BAT);
default: return false;
}
@@ -590,7 +590,7 @@ int rf69_set_rssi_threshold(struct spi_device *spi, u8 threshold)
return rf69_write_reg(spi, REG_RSSITHRESH, threshold);
}
-int rf69_set_preamble_length(struct spi_device *spi, u16 preambleLength)
+int rf69_set_preamble_length(struct spi_device *spi, u16 preamble_length)
{
int retval;
u8 msb, lsb;
@@ -598,8 +598,8 @@ int rf69_set_preamble_length(struct spi_device *spi, u16 preambleLength)
/* no value check needed - u16 exactly matches register size */
/* calculate reg settings */
- msb = (preambleLength & 0xff00) >> 8;
- lsb = (preambleLength & 0xff);
+ msb = (preamble_length & 0xff00) >> 8;
+ lsb = (preamble_length & 0xff);
/* transmit to chip */
retval = rf69_write_reg(spi, REG_PREAMBLE_MSB, msb);
@@ -625,7 +625,7 @@ int rf69_set_fifo_fill_condition(struct spi_device *spi, enum fifo_fill_conditio
switch (fifo_fill_condition) {
case always:
return rf69_set_bit(spi, REG_SYNC_CONFIG, MASK_SYNC_CONFIG_FIFO_FILL_CONDITION);
- case afterSyncInterrupt:
+ case after_sync_interrupt:
return rf69_clear_bit(spi, REG_SYNC_CONFIG, MASK_SYNC_CONFIG_FIFO_FILL_CONDITION);
default:
dev_dbg(&spi->dev, "set: illegal input param");
@@ -633,40 +633,40 @@ int rf69_set_fifo_fill_condition(struct spi_device *spi, enum fifo_fill_conditio
}
}
-int rf69_set_sync_size(struct spi_device *spi, u8 syncSize)
+int rf69_set_sync_size(struct spi_device *spi, u8 sync_size)
{
// check input value
- if (syncSize > 0x07) {
+ if (sync_size > 0x07) {
dev_dbg(&spi->dev, "set: illegal input param");
return -EINVAL;
}
// write value
- return rf69_read_mod_write(spi, REG_SYNC_CONFIG, MASK_SYNC_CONFIG_SYNC_SIZE, (syncSize << 3));
+ return rf69_read_mod_write(spi, REG_SYNC_CONFIG, MASK_SYNC_CONFIG_SYNC_SIZE, (sync_size << 3));
}
-int rf69_set_sync_values(struct spi_device *spi, u8 syncValues[8])
+int rf69_set_sync_values(struct spi_device *spi, u8 sync_values[8])
{
int retval = 0;
- retval += rf69_write_reg(spi, REG_SYNCVALUE1, syncValues[0]);
- retval += rf69_write_reg(spi, REG_SYNCVALUE2, syncValues[1]);
- retval += rf69_write_reg(spi, REG_SYNCVALUE3, syncValues[2]);
- retval += rf69_write_reg(spi, REG_SYNCVALUE4, syncValues[3]);
- retval += rf69_write_reg(spi, REG_SYNCVALUE5, syncValues[4]);
- retval += rf69_write_reg(spi, REG_SYNCVALUE6, syncValues[5]);
- retval += rf69_write_reg(spi, REG_SYNCVALUE7, syncValues[6]);
- retval += rf69_write_reg(spi, REG_SYNCVALUE8, syncValues[7]);
+ retval += rf69_write_reg(spi, REG_SYNCVALUE1, sync_values[0]);
+ retval += rf69_write_reg(spi, REG_SYNCVALUE2, sync_values[1]);
+ retval += rf69_write_reg(spi, REG_SYNCVALUE3, sync_values[2]);
+ retval += rf69_write_reg(spi, REG_SYNCVALUE4, sync_values[3]);
+ retval += rf69_write_reg(spi, REG_SYNCVALUE5, sync_values[4]);
+ retval += rf69_write_reg(spi, REG_SYNCVALUE6, sync_values[5]);
+ retval += rf69_write_reg(spi, REG_SYNCVALUE7, sync_values[6]);
+ retval += rf69_write_reg(spi, REG_SYNCVALUE8, sync_values[7]);
return retval;
}
-int rf69_set_packet_format(struct spi_device *spi, enum packetFormat packetFormat)
+int rf69_set_packet_format(struct spi_device *spi, enum packet_format packet_format)
{
- switch (packetFormat) {
- case packetLengthVar:
+ switch (packet_format) {
+ case packet_length_var:
return rf69_set_bit(spi, REG_PACKETCONFIG1, MASK_PACKETCONFIG1_PAKET_FORMAT_VARIABLE);
- case packetLengthFix:
+ case packet_length_fix:
return rf69_clear_bit(spi, REG_PACKETCONFIG1, MASK_PACKETCONFIG1_PAKET_FORMAT_VARIABLE);
default:
dev_dbg(&spi->dev, "set: illegal input param");
@@ -684,14 +684,14 @@ int rf69_disable_crc(struct spi_device *spi)
return rf69_clear_bit(spi, REG_PACKETCONFIG1, MASK_PACKETCONFIG1_CRC_ON);
}
-int rf69_set_adressFiltering(struct spi_device *spi, enum addressFiltering addressFiltering)
+int rf69_set_address_filtering(struct spi_device *spi, enum address_filtering address_filtering)
{
- switch (addressFiltering) {
- case filteringOff:
+ switch (address_filtering) {
+ case filtering_off:
return rf69_read_mod_write(spi, REG_PACKETCONFIG1, MASK_PACKETCONFIG1_ADDRESSFILTERING, PACKETCONFIG1_ADDRESSFILTERING_OFF);
- case nodeAddress:
+ case node_address:
return rf69_read_mod_write(spi, REG_PACKETCONFIG1, MASK_PACKETCONFIG1_ADDRESSFILTERING, PACKETCONFIG1_ADDRESSFILTERING_NODE);
- case nodeOrBroadcastAddress:
+ case node_or_broadcast_address:
return rf69_read_mod_write(spi, REG_PACKETCONFIG1, MASK_PACKETCONFIG1_ADDRESSFILTERING, PACKETCONFIG1_ADDRESSFILTERING_NODEBROADCAST);
default:
dev_dbg(&spi->dev, "set: illegal input param");
@@ -704,19 +704,19 @@ int rf69_set_payload_length(struct spi_device *spi, u8 payload_length)
return rf69_write_reg(spi, REG_PAYLOAD_LENGTH, payload_length);
}
-int rf69_set_node_address(struct spi_device *spi, u8 nodeAddress)
+int rf69_set_node_address(struct spi_device *spi, u8 node_address)
{
- return rf69_write_reg(spi, REG_NODEADRS, nodeAddress);
+ return rf69_write_reg(spi, REG_NODEADRS, node_address);
}
-int rf69_set_broadcast_address(struct spi_device *spi, u8 broadcastAddress)
+int rf69_set_broadcast_address(struct spi_device *spi, u8 broadcast_address)
{
- return rf69_write_reg(spi, REG_BROADCASTADRS, broadcastAddress);
+ return rf69_write_reg(spi, REG_BROADCASTADRS, broadcast_address);
}
-int rf69_set_tx_start_condition(struct spi_device *spi, enum txStartCondition txStartCondition)
+int rf69_set_tx_start_condition(struct spi_device *spi, enum tx_start_condition tx_start_condition)
{
- switch (txStartCondition) {
+ switch (tx_start_condition) {
case fifo_level:
return rf69_clear_bit(spi, REG_FIFO_THRESH, MASK_FIFO_THRESH_TXSTART);
case fifo_not_empty:
@@ -751,11 +751,11 @@ int rf69_set_fifo_threshold(struct spi_device *spi, u8 threshold)
int rf69_set_dagc(struct spi_device *spi, enum dagc dagc)
{
switch (dagc) {
- case normalMode:
+ case normal_mode:
return rf69_write_reg(spi, REG_TESTDAGC, DAGC_NORMAL);
case improve:
return rf69_write_reg(spi, REG_TESTDAGC, DAGC_IMPROVED_LOWBETA0);
- case improve4LowModulationIndex:
+ case improve_for_low_modulation_index:
return rf69_write_reg(spi, REG_TESTDAGC, DAGC_IMPROVED_LOWBETA1);
default:
dev_dbg(&spi->dev, "set: illegal input param");
diff --git a/drivers/staging/pi433/rf69.h b/drivers/staging/pi433/rf69.h
index 09d221b8b6df..b9f6850e3316 100644
--- a/drivers/staging/pi433/rf69.h
+++ b/drivers/staging/pi433/rf69.h
@@ -29,35 +29,35 @@ int rf69_set_mode(struct spi_device *spi, enum mode mode);
int rf69_set_data_mode(struct spi_device *spi, u8 data_mode);
int rf69_set_modulation(struct spi_device *spi, enum modulation modulation);
int rf69_set_modulation_shaping(struct spi_device *spi, enum mod_shaping mod_shaping);
-int rf69_set_bit_rate(struct spi_device *spi, u16 bitRate);
+int rf69_set_bit_rate(struct spi_device *spi, u16 bit_rate);
int rf69_set_deviation(struct spi_device *spi, u32 deviation);
int rf69_set_frequency(struct spi_device *spi, u32 frequency);
int rf69_enable_amplifier(struct spi_device *spi, u8 amplifier_mask);
int rf69_disable_amplifier(struct spi_device *spi, u8 amplifier_mask);
-int rf69_set_output_power_level(struct spi_device *spi, u8 powerLevel);
-int rf69_set_pa_ramp(struct spi_device *spi, enum paRamp paRamp);
-int rf69_set_antenna_impedance(struct spi_device *spi, enum antennaImpedance antennaImpedance);
-int rf69_set_lna_gain(struct spi_device *spi, enum lnaGain lnaGain);
+int rf69_set_output_power_level(struct spi_device *spi, u8 power_level);
+int rf69_set_pa_ramp(struct spi_device *spi, enum pa_ramp pa_ramp);
+int rf69_set_antenna_impedance(struct spi_device *spi, enum antenna_impedance antenna_impedance);
+int rf69_set_lna_gain(struct spi_device *spi, enum lna_gain lna_gain);
int rf69_set_bandwidth(struct spi_device *spi, enum mantisse mantisse, u8 exponent);
int rf69_set_bandwidth_during_afc(struct spi_device *spi, enum mantisse mantisse, u8 exponent);
-int rf69_set_ook_threshold_dec(struct spi_device *spi, enum thresholdDecrement thresholdDecrement);
-int rf69_set_dio_mapping(struct spi_device *spi, u8 DIONumber, u8 value);
+int rf69_set_ook_threshold_dec(struct spi_device *spi, enum threshold_decrement threshold_decrement);
+int rf69_set_dio_mapping(struct spi_device *spi, u8 dio_number, u8 value);
bool rf69_get_flag(struct spi_device *spi, enum flag flag);
int rf69_set_rssi_threshold(struct spi_device *spi, u8 threshold);
-int rf69_set_preamble_length(struct spi_device *spi, u16 preambleLength);
+int rf69_set_preamble_length(struct spi_device *spi, u16 preamble_length);
int rf69_enable_sync(struct spi_device *spi);
int rf69_disable_sync(struct spi_device *spi);
int rf69_set_fifo_fill_condition(struct spi_device *spi, enum fifo_fill_condition fifo_fill_condition);
int rf69_set_sync_size(struct spi_device *spi, u8 sync_size);
-int rf69_set_sync_values(struct spi_device *spi, u8 syncValues[8]);
-int rf69_set_packet_format(struct spi_device *spi, enum packetFormat packetFormat);
+int rf69_set_sync_values(struct spi_device *spi, u8 sync_values[8]);
+int rf69_set_packet_format(struct spi_device *spi, enum packet_format packet_format);
int rf69_enable_crc(struct spi_device *spi);
int rf69_disable_crc(struct spi_device *spi);
-int rf69_set_adressFiltering(struct spi_device *spi, enum addressFiltering addressFiltering);
+int rf69_set_address_filtering(struct spi_device *spi, enum address_filtering address_filtering);
int rf69_set_payload_length(struct spi_device *spi, u8 payload_length);
-int rf69_set_node_address(struct spi_device *spi, u8 nodeAddress);
-int rf69_set_broadcast_address(struct spi_device *spi, u8 broadcastAddress);
-int rf69_set_tx_start_condition(struct spi_device *spi, enum txStartCondition txStartCondition);
+int rf69_set_node_address(struct spi_device *spi, u8 node_address);
+int rf69_set_broadcast_address(struct spi_device *spi, u8 broadcast_address);
+int rf69_set_tx_start_condition(struct spi_device *spi, enum tx_start_condition tx_start_condition);
int rf69_set_fifo_threshold(struct spi_device *spi, u8 threshold);
int rf69_set_dagc(struct spi_device *spi, enum dagc dagc);
diff --git a/drivers/staging/pi433/rf69_enum.h b/drivers/staging/pi433/rf69_enum.h
index 03440cfa957c..493bd0025453 100644
--- a/drivers/staging/pi433/rf69_enum.h
+++ b/drivers/staging/pi433/rf69_enum.h
@@ -41,7 +41,7 @@ enum mod_shaping {
SHAPING_2BR
};
-enum paRamp {
+enum pa_ramp {
ramp3400,
ramp2000,
ramp1000,
@@ -60,12 +60,12 @@ enum paRamp {
ramp10
};
-enum antennaImpedance {
- fiftyOhm,
- twohundretOhm
+enum antenna_impedance {
+ fifty_ohm,
+ two_hundred_ohm
};
-enum lnaGain {
+enum lna_gain {
automatic,
max,
max_minus_6,
@@ -82,7 +82,7 @@ enum mantisse {
mantisse24
};
-enum thresholdDecrement {
+enum threshold_decrement {
dec_every8th,
dec_every4th,
dec_every2nd,
@@ -94,50 +94,50 @@ enum thresholdDecrement {
};
enum flag {
- modeSwitchCompleted,
- readyToReceive,
- readyToSend,
- pllLocked,
- rssiExceededThreshold,
+ mode_switch_completed,
+ ready_to_receive,
+ ready_to_send,
+ pll_locked,
+ rssi_exceeded_threshold,
timeout,
automode,
- syncAddressMatch,
+ sync_address_match,
fifo_full,
// fifo_not_empty, collision with next enum; replaced by following enum...
fifo_empty,
fifo_level_below_threshold,
fifo_overrun,
- packetSent,
+ packet_sent,
payload_ready,
- crcOk,
- batteryLow
+ crc_ok,
+ battery_low
};
enum fifo_fill_condition {
- afterSyncInterrupt,
+ after_sync_interrupt,
always
};
-enum packetFormat {
- packetLengthFix,
- packetLengthVar
+enum packet_format {
+ packet_length_fix,
+ packet_length_var
};
-enum txStartCondition {
+enum tx_start_condition {
fifo_level,
fifo_not_empty
};
-enum addressFiltering {
- filteringOff,
- nodeAddress,
- nodeOrBroadcastAddress
+enum address_filtering {
+ filtering_off,
+ node_address,
+ node_or_broadcast_address
};
enum dagc {
- normalMode,
+ normal_mode,
improve,
- improve4LowModulationIndex
+ improve_for_low_modulation_index
};
#endif
diff --git a/drivers/staging/rtl8188eu/Kconfig b/drivers/staging/rtl8188eu/Kconfig
index cb836c59d564..673fdce25530 100644
--- a/drivers/staging/rtl8188eu/Kconfig
+++ b/drivers/staging/rtl8188eu/Kconfig
@@ -4,6 +4,10 @@ config R8188EU
depends on m
select WIRELESS_EXT
select WEXT_PRIV
+ select LIB80211
+ select LIB80211_CRYPT_WEP
+ select LIB80211_CRYPT_CCMP
+ select LIB80211_CRYPT_TKIP
---help---
This option adds the Realtek RTL8188EU USB device such as TP-Link TL-WN725N.
If built as a module, it will be called r8188eu.
diff --git a/drivers/staging/rtl8188eu/core/rtw_ieee80211.c b/drivers/staging/rtl8188eu/core/rtw_ieee80211.c
index bb867a987c2b..0b0fdccc7278 100644
--- a/drivers/staging/rtl8188eu/core/rtw_ieee80211.c
+++ b/drivers/staging/rtl8188eu/core/rtw_ieee80211.c
@@ -577,7 +577,7 @@ u8 rtw_is_wps_ie(u8 *ie_ptr, uint *wps_ielen)
u8 match = false;
u8 eid, wps_oui[4] = {0x0, 0x50, 0xf2, 0x04};
- if (ie_ptr == NULL)
+ if (!ie_ptr)
return match;
eid = ie_ptr[0];
@@ -926,7 +926,7 @@ void rtw_macaddr_cfg(u8 *mac_addr)
{
u8 mac[ETH_ALEN];
- if (mac_addr == NULL)
+ if (!mac_addr)
return;
if (rtw_initmac && mac_pton(rtw_initmac, mac)) {
@@ -952,7 +952,7 @@ void rtw_macaddr_cfg(u8 *mac_addr)
DBG_88E("MAC Address from efuse error, assign default one !!!\n");
}
- DBG_88E("rtw_macaddr_cfg MAC Address = %pM\n", (mac_addr));
+ DBG_88E("%s MAC Address = %pM\n", __func__, (mac_addr));
}
static int rtw_get_cipher_info(struct wlan_network *pnetwork)
@@ -965,7 +965,7 @@ static int rtw_get_cipher_info(struct wlan_network *pnetwork)
pbuf = rtw_get_wpa_ie(&pnetwork->network.IEs[12], &wpa_ielen, pnetwork->network.IELength - 12);
if (pbuf && (wpa_ielen > 0)) {
- RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_get_cipher_info: wpa_ielen: %d", wpa_ielen));
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("%s: wpa_ielen: %d", __func__, wpa_ielen));
if (_SUCCESS == rtw_parse_wpa_ie(pbuf, wpa_ielen + 2, &group_cipher, &pairwise_cipher, &is8021x)) {
pnetwork->BcnInfo.pairwise_cipher = pairwise_cipher;
pnetwork->BcnInfo.group_cipher = group_cipher;
@@ -1014,10 +1014,10 @@ void rtw_get_bcn_info(struct wlan_network *pnetwork)
pnetwork->BcnInfo.encryp_protocol = ENCRYP_PROTOCOL_OPENSYS;
}
rtw_get_sec_ie(pnetwork->network.IEs, pnetwork->network.IELength, NULL, &rsn_len, NULL, &wpa_len);
- RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_get_bcn_info: ssid =%s\n", pnetwork->network.Ssid.Ssid));
- RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_get_bcn_info: wpa_len =%d rsn_len =%d\n", wpa_len, rsn_len));
- RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_get_bcn_info: ssid =%s\n", pnetwork->network.Ssid.Ssid));
- RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_get_bcn_info: wpa_len =%d rsn_len =%d\n", wpa_len, rsn_len));
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("%s: ssid =%s\n", __func__, pnetwork->network.Ssid.Ssid));
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("%s: wpa_len =%d rsn_len =%d\n", __func__, wpa_len, rsn_len));
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("%s: ssid =%s\n", __func__, pnetwork->network.Ssid.Ssid));
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("%s: wpa_len =%d rsn_len =%d\n", __func__, wpa_len, rsn_len));
if (rsn_len > 0) {
pnetwork->BcnInfo.encryp_protocol = ENCRYP_PROTOCOL_WPA2;
diff --git a/drivers/staging/rtl8188eu/core/rtw_ioctl_set.c b/drivers/staging/rtl8188eu/core/rtw_ioctl_set.c
index 767928a2cbb4..2183c613e61b 100644
--- a/drivers/staging/rtl8188eu/core/rtw_ioctl_set.c
+++ b/drivers/staging/rtl8188eu/core/rtw_ioctl_set.c
@@ -207,7 +207,7 @@ release_mlme_lock:
exit:
RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_err_,
- ("rtw_set_802_11_bssid: status=%d\n", status));
+ ("%s: status=%d\n", __func__, status));
return status;
@@ -316,7 +316,7 @@ release_mlme_lock:
exit:
RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_err_,
- ("-rtw_set_802_11_ssid: status =%d\n", status));
+ ("-%s: status =%d\n", __func__, status));
return status;
}
@@ -418,22 +418,22 @@ u8 rtw_set_802_11_bssid_list_scan(struct adapter *padapter, struct ndis_802_11_s
u8 res = true;
- RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_err_, ("+rtw_set_802_11_bssid_list_scan(), fw_state =%x\n", get_fwstate(pmlmepriv)));
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_err_, ("+%s(), fw_state =%x\n", __func__, get_fwstate(pmlmepriv)));
- if (padapter == NULL) {
+ if (!padapter) {
res = false;
goto exit;
}
if (!padapter->hw_init_completed) {
res = false;
- RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_err_, ("\n === rtw_set_802_11_bssid_list_scan:hw_init_completed == false ===\n"));
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_err_, ("\n === %s:hw_init_completed == false ===\n", __func__));
goto exit;
}
if ((check_fwstate(pmlmepriv, _FW_UNDER_SURVEY|_FW_UNDER_LINKING)) ||
(pmlmepriv->LinkDetectInfo.bBusyTraffic)) {
/* Scan or linking is in progress, do nothing. */
- RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_err_, ("rtw_set_802_11_bssid_list_scan fail since fw_state = %x\n", get_fwstate(pmlmepriv)));
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_err_, ("%s fail since fw_state = %x\n", __func__, get_fwstate(pmlmepriv)));
res = true;
if (check_fwstate(pmlmepriv,
@@ -473,7 +473,7 @@ u8 rtw_set_802_11_authentication_mode(struct adapter *padapter, enum ndis_802_11
psecuritypriv->ndisauthtype = authmode;
RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_,
- ("rtw_set_802_11_authentication_mode:psecuritypriv->ndisauthtype=%d",
+ ("%s:psecuritypriv->ndisauthtype=%d", __func__,
psecuritypriv->ndisauthtype));
if (psecuritypriv->ndisauthtype > 3)
diff --git a/drivers/staging/rtl8188eu/core/rtw_mlme.c b/drivers/staging/rtl8188eu/core/rtw_mlme.c
index 1cd49e292804..8d49e3047201 100644
--- a/drivers/staging/rtl8188eu/core/rtw_mlme.c
+++ b/drivers/staging/rtl8188eu/core/rtw_mlme.c
@@ -790,7 +790,7 @@ void rtw_indicate_connect(struct adapter *padapter)
{
struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
- RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("+rtw_indicate_connect\n"));
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("+%s\n", __func__));
pmlmepriv->to_join = false;
@@ -806,7 +806,7 @@ void rtw_indicate_connect(struct adapter *padapter)
rtw_set_scan_deny(padapter, 3000);
- RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("-rtw_indicate_connect: fw_state=0x%08x\n", get_fwstate(pmlmepriv)));
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("-%s: fw_state=0x%08x\n", __func__, get_fwstate(pmlmepriv)));
}
/*
@@ -897,7 +897,7 @@ static struct sta_info *rtw_joinbss_update_stainfo(struct adapter *padapter, str
* Commented by Albert 2012/07/21
* When doing the WPS, the wps_ie_len won't equal to 0
* And the Wi-Fi driver shouldn't allow the data
- * packet to be tramsmitted.
+ * packet to be transmitted.
*/
if (padapter->securitypriv.wps_ie_len != 0) {
psta->ieee8021x_blocked = true;
@@ -1371,7 +1371,7 @@ void _rtw_join_timeout_handler (struct timer_list *t)
}
/*
- * rtw_scan_timeout_handler - Timeout/Faliure handler for CMD SiteSurvey
+ * rtw_scan_timeout_handler - Timeout/Failure handler for CMD SiteSurvey
* @adapter: pointer to struct adapter structure
*/
void rtw_scan_timeout_handler (struct timer_list *t)
@@ -1756,7 +1756,7 @@ int rtw_restruct_sec_ie(struct adapter *adapter, u8 *in_ie, u8 *out_ie, uint in_
uint ndissecuritytype = psecuritypriv->ndisencryptstatus;
RT_TRACE(_module_rtl871x_mlme_c_, _drv_notice_,
- ("+rtw_restruct_sec_ie: ndisauthmode=%d ndissecuritytype=%d\n",
+ ("+%s: ndisauthmode=%d ndissecuritytype=%d\n", __func__,
ndisauthmode, ndissecuritytype));
/* copy fixed ie only */
@@ -1983,7 +1983,7 @@ void rtw_update_ht_cap(struct adapter *padapter, u8 *pie, uint ie_len)
if ((!pmlmeinfo->HT_info_enable) || (!pmlmeinfo->HT_caps_enable))
return;
- DBG_88E("+rtw_update_ht_cap()\n");
+ DBG_88E("+%s()\n", __func__);
/* maybe needs check if ap supports rx ampdu. */
if ((!phtpriv->ampdu_enable) && (pregistrypriv->ampdu_enable == 1)) {
@@ -2057,7 +2057,7 @@ void rtw_issue_addbareq_cmd(struct adapter *padapter, struct xmit_frame *pxmitfr
issued |= (phtpriv->candidate_tid_bitmap >> priority) & 0x1;
if (issued == 0) {
- DBG_88E("rtw_issue_addbareq_cmd, p=%d\n", priority);
+ DBG_88E("%s, p=%d\n", __func__, priority);
psta->htpriv.candidate_tid_bitmap |= BIT((u8)priority);
rtw_addbareq_cmd(padapter, (u8)priority, pattrib->ra);
}
diff --git a/drivers/staging/rtl8188eu/core/rtw_mlme_ext.c b/drivers/staging/rtl8188eu/core/rtw_mlme_ext.c
index bcb6919bb7d5..19266cf1edbd 100644
--- a/drivers/staging/rtl8188eu/core/rtw_mlme_ext.c
+++ b/drivers/staging/rtl8188eu/core/rtw_mlme_ext.c
@@ -163,13 +163,13 @@ struct xmit_frame *alloc_mgtxmitframe(struct xmit_priv *pxmitpriv)
struct xmit_buf *pxmitbuf;
pmgntframe = rtw_alloc_xmitframe(pxmitpriv);
- if (pmgntframe == NULL) {
+ if (!pmgntframe) {
DBG_88E("%s, alloc xmitframe fail\n", __func__);
return NULL;
}
pxmitbuf = rtw_alloc_xmitbuf_ext(pxmitpriv);
- if (pxmitbuf == NULL) {
+ if (!pxmitbuf) {
DBG_88E("%s, alloc xmitbuf fail\n", __func__);
rtw_free_xmitframe(pxmitpriv, pmgntframe);
return NULL;
@@ -332,7 +332,7 @@ static void issue_beacon(struct adapter *padapter, int timeout_ms)
u8 bc_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
pmgntframe = alloc_mgtxmitframe(pxmitpriv);
- if (pmgntframe == NULL) {
+ if (!pmgntframe) {
DBG_88E("%s, alloc mgnt frame fail\n", __func__);
return;
}
@@ -478,7 +478,7 @@ static void issue_probersp(struct adapter *padapter, unsigned char *da)
unsigned int rate_len;
pmgntframe = alloc_mgtxmitframe(pxmitpriv);
- if (pmgntframe == NULL) {
+ if (!pmgntframe) {
DBG_88E("%s, alloc mgnt frame fail\n", __func__);
return;
}
@@ -623,10 +623,10 @@ static int issue_probereq(struct adapter *padapter,
int bssrate_len = 0;
u8 bc_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
- RT_TRACE(_module_rtl871x_mlme_c_, _drv_notice_, ("+issue_probereq\n"));
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_notice_, ("+%s\n", __func__));
pmgntframe = alloc_mgtxmitframe(pxmitpriv);
- if (pmgntframe == NULL)
+ if (!pmgntframe)
goto exit;
/* update attribute */
@@ -912,7 +912,7 @@ static void issue_asocrsp(struct adapter *padapter, unsigned short status,
DBG_88E("%s\n", __func__);
pmgntframe = alloc_mgtxmitframe(pxmitpriv);
- if (pmgntframe == NULL)
+ if (!pmgntframe)
return;
/* update attribute */
@@ -1039,7 +1039,7 @@ static void issue_assocreq(struct adapter *padapter)
struct wlan_bssid_ex *pnetwork = &(pmlmeinfo->network);
pmgntframe = alloc_mgtxmitframe(pxmitpriv);
- if (pmgntframe == NULL)
+ if (!pmgntframe)
goto exit;
/* update attribute */
@@ -1133,7 +1133,7 @@ static void issue_assocreq(struct adapter *padapter)
/* RSN */
p = rtw_get_ie((pmlmeinfo->network.IEs + sizeof(struct ndis_802_11_fixed_ie)), _RSN_IE_2_, &ie_len, (pmlmeinfo->network.IELength - sizeof(struct ndis_802_11_fixed_ie)));
- if (p != NULL)
+ if (p)
pframe = rtw_set_ie(pframe, _RSN_IE_2_, ie_len, (p + 2), &(pattrib->pktlen));
/* HT caps */
@@ -1221,7 +1221,7 @@ static int _issue_nulldata(struct adapter *padapter, unsigned char *da,
pnetwork = &(pmlmeinfo->network);
pmgntframe = alloc_mgtxmitframe(pxmitpriv);
- if (pmgntframe == NULL)
+ if (!pmgntframe)
goto exit;
/* update attribute */
@@ -1338,7 +1338,7 @@ static int _issue_qos_nulldata(struct adapter *padapter, unsigned char *da,
DBG_88E("%s\n", __func__);
pmgntframe = alloc_mgtxmitframe(pxmitpriv);
- if (pmgntframe == NULL)
+ if (!pmgntframe)
goto exit;
/* update attribute */
@@ -1584,7 +1584,7 @@ static void issue_action_BA(struct adapter *padapter, unsigned char *raddr,
DBG_88E("%s, category=%d, action=%d, status=%d\n", __func__, category, action, status);
pmgntframe = alloc_mgtxmitframe(pxmitpriv);
- if (pmgntframe == NULL)
+ if (!pmgntframe)
return;
/* update attribute */
@@ -1632,7 +1632,7 @@ static void issue_action_BA(struct adapter *padapter, unsigned char *raddr,
&pattrib->pktlen);
psta = rtw_get_stainfo(pstapriv, raddr);
- if (psta != NULL) {
+ if (psta) {
start_seq = (psta->sta_xmitpriv.txseq_tid[status & 0x07]&0xfff) + 1;
DBG_88E("BA_starting_seqctrl=%d for TID=%d\n", start_seq, status & 0x07);
@@ -1743,7 +1743,7 @@ static void issue_action_BSSCoexistPacket(struct adapter *padapter)
action = ACT_PUBLIC_BSSCOEXIST;
pmgntframe = alloc_mgtxmitframe(pxmitpriv);
- if (pmgntframe == NULL)
+ if (!pmgntframe)
return;
/* update attribute */
@@ -2091,7 +2091,7 @@ static u8 collect_bss_info(struct adapter *padapter,
/* checking SSID */
p = rtw_get_ie(bssid->IEs + ie_offset, _SSID_IE_, &len, bssid->IELength - ie_offset);
- if (p == NULL) {
+ if (!p) {
DBG_88E("marc: cannot find SSID for survey event\n");
return _FAIL;
}
@@ -2122,7 +2122,7 @@ static u8 collect_bss_info(struct adapter *padapter,
}
p = rtw_get_ie(bssid->IEs + ie_offset, _EXT_SUPPORTEDRATES_IE_, &len, bssid->IELength - ie_offset);
- if (p != NULL) {
+ if (p) {
if (len > (NDIS_802_11_LENGTH_RATES_EX-i)) {
DBG_88E("%s()-%d: IE too long (%d) for survey event\n", __func__, __LINE__, len);
return _FAIL;
@@ -2253,7 +2253,7 @@ static void start_create_ibss(struct adapter *padapter)
pmlmeinfo->state |= WIFI_FW_ASSOC_SUCCESS;
}
} else {
- DBG_88E("start_create_ibss, invalid cap:%x\n", caps);
+ DBG_88E("%s, invalid cap:%x\n", __func__, caps);
return;
}
}
@@ -2568,7 +2568,7 @@ static unsigned int OnProbeReq(struct adapter *padapter,
len - WLAN_HDR_A3_LEN - _PROBEREQ_IE_OFFSET_);
/* check (wildcard) SSID */
- if (p != NULL) {
+ if (p) {
if ((ielen != 0 && memcmp((void *)(p+2), (void *)cur->Ssid.Ssid, cur->Ssid.SsidLength)) ||
(ielen == 0 && pmlmeinfo->hidden_ssid_mode))
return _SUCCESS;
@@ -2705,7 +2705,7 @@ static unsigned int OnAuth(struct adapter *padapter,
if ((pmlmeinfo->state&0x03) != WIFI_FW_AP_STATE)
return _FAIL;
- DBG_88E("+OnAuth\n");
+ DBG_88E("+%s\n", __func__);
sa = GetAddr2Ptr(pframe);
@@ -2735,11 +2735,11 @@ static unsigned int OnAuth(struct adapter *padapter,
}
pstat = rtw_get_stainfo(pstapriv, sa);
- if (pstat == NULL) {
+ if (!pstat) {
/* allocate a new one */
DBG_88E("going to alloc stainfo for sa=%pM\n", sa);
pstat = rtw_alloc_stainfo(pstapriv, sa);
- if (pstat == NULL) {
+ if (!pstat) {
DBG_88E(" Exceed the upper limit of supported clients...\n");
status = _STATS_UNABLE_HANDLE_STA_;
goto auth_fail;
@@ -2973,7 +2973,7 @@ static unsigned int OnAssocReq(struct adapter *padapter,
}
pstat = rtw_get_stainfo(pstapriv, GetAddr2Ptr(pframe));
- if (pstat == NULL) {
+ if (!pstat) {
status = _RSON_CLS2_;
goto asoc_class2_error;
}
@@ -3014,7 +3014,7 @@ static unsigned int OnAssocReq(struct adapter *padapter,
/* checking SSID */
p = rtw_get_ie(pframe + WLAN_HDR_A3_LEN + ie_offset, _SSID_IE_, &ie_len,
pkt_len - WLAN_HDR_A3_LEN - ie_offset);
- if (p == NULL)
+ if (!p)
status = _STATS_FAILURE_;
if (ie_len == 0) { /* broadcast ssid, however it is not allowed in assocreq */
@@ -3122,7 +3122,7 @@ static unsigned int OnAssocReq(struct adapter *padapter,
goto OnAssocReqFail;
pstat->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS);
- if (wpa_ie == NULL) {
+ if (!wpa_ie) {
if (elems.wps_ie) {
DBG_88E("STA included WPS IE in "
"(Re)Association Request - assume WPS is "
@@ -3653,7 +3653,7 @@ static unsigned int OnAction_back(struct adapter *padapter,
addr = GetAddr2Ptr(pframe);
psta = rtw_get_stainfo(pstapriv, addr);
- if (psta == NULL)
+ if (!psta)
return _SUCCESS;
frame_body = (unsigned char *)(pframe + sizeof(struct ieee80211_hdr_3addr));
@@ -4144,13 +4144,13 @@ void mgt_dispatcher(struct adapter *padapter, struct recv_frame *precv_frame)
struct sta_info *psta = rtw_get_stainfo(&padapter->stapriv, GetAddr2Ptr(pframe));
RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
- ("+mgt_dispatcher: type(0x%x) subtype(0x%x)\n",
+ ("+%s: type(0x%x) subtype(0x%x)\n", __func__,
(unsigned int)GetFrameType(pframe),
(unsigned int)GetFrameSubType(pframe)));
if (GetFrameType(pframe) != WIFI_MGT_TYPE) {
RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
- ("mgt_dispatcher: type(0x%x) error!\n",
+ ("%s: type(0x%x) error!\n", __func__,
(unsigned int)GetFrameType(pframe)));
return;
}
@@ -4170,7 +4170,7 @@ void mgt_dispatcher(struct adapter *padapter, struct recv_frame *precv_frame)
}
ptable += index;
- if (psta != NULL) {
+ if (psta) {
if (GetRetry(pframe)) {
if (precv_frame->attrib.seq_num ==
psta->RxMgmtFrameSeqNum) {
@@ -4355,7 +4355,7 @@ void report_join_res(struct adapter *padapter, int res)
pjoinbss_evt->network.join_res = res;
pjoinbss_evt->network.aid = res;
- DBG_88E("report_join_res(%d)\n", res);
+ DBG_88E("%s(%d)\n", __func__, res);
rtw_joinbss_event_prehandle(padapter, (u8 *)&pjoinbss_evt->network);
@@ -4415,7 +4415,7 @@ void report_del_sta_event(struct adapter *padapter, unsigned char *MacAddr,
pdel_sta_evt->mac_id = mac_id;
- DBG_88E("report_del_sta_event: delete STA, mac_id =%d\n", mac_id);
+ DBG_88E("%s: delete STA, mac_id =%d\n", __func__, mac_id);
rtw_enqueue_cmd(pcmdpriv, pcmd_obj);
}
@@ -4460,7 +4460,7 @@ void report_add_sta_event(struct adapter *padapter, unsigned char *MacAddr,
ether_addr_copy((unsigned char *)(&(padd_sta_evt->macaddr)), MacAddr);
padd_sta_evt->cam_id = cam_idx;
- DBG_88E("report_add_sta_event: add STA\n");
+ DBG_88E("%s: add STA\n", __func__);
rtw_enqueue_cmd(pcmdpriv, pcmd_obj);
}
@@ -4702,7 +4702,7 @@ void linked_status_chk(struct adapter *padapter)
rx_chk_limit = 4;
psta = rtw_get_stainfo(pstapriv, pmlmeinfo->network.MacAddress);
- if (psta != NULL) {
+ if (psta) {
bool is_p2p_enable = false;
if (!chk_ap_is_alive(padapter, psta))
@@ -4782,7 +4782,7 @@ void linked_status_chk(struct adapter *padapter)
if (pmlmeinfo->FW_sta_info[i].status == 1) {
psta = pmlmeinfo->FW_sta_info[i].psta;
- if (psta == NULL)
+ if (!psta)
continue;
if (pmlmeinfo->FW_sta_info[i].rx_pkt == sta_rx_pkts(psta)) {
if (pmlmeinfo->FW_sta_info[i].retry < 3) {
@@ -4852,7 +4852,7 @@ void link_timer_hdl(struct timer_list *t)
struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
if (pmlmeinfo->state & WIFI_FW_AUTH_NULL) {
- DBG_88E("link_timer_hdl:no beacon while connecting\n");
+ DBG_88E("%s:no beacon while connecting\n", __func__);
pmlmeinfo->state = WIFI_FW_NULL_STATE;
report_join_res(padapter, -3);
} else if (pmlmeinfo->state & WIFI_FW_AUTH_STATE) {
@@ -4863,7 +4863,7 @@ void link_timer_hdl(struct timer_list *t)
return;
}
- DBG_88E("link_timer_hdl: auth timeout and try again\n");
+ DBG_88E("%s: auth timeout and try again\n", __func__);
pmlmeinfo->auth_seq = 1;
issue_auth(padapter, NULL, 0);
set_link_timer(pmlmeext, REAUTH_TO);
@@ -4875,7 +4875,7 @@ void link_timer_hdl(struct timer_list *t)
return;
}
- DBG_88E("link_timer_hdl: assoc timeout and try again\n");
+ DBG_88E("%s: assoc timeout and try again\n", __func__);
issue_assocreq(padapter);
set_link_timer(pmlmeext, REASSOC_TO);
}
diff --git a/drivers/staging/rtl8188eu/core/rtw_recv.c b/drivers/staging/rtl8188eu/core/rtw_recv.c
index 6506a1587df0..05936a45eb93 100644
--- a/drivers/staging/rtl8188eu/core/rtw_recv.c
+++ b/drivers/staging/rtl8188eu/core/rtw_recv.c
@@ -23,6 +23,7 @@
#include <mon.h>
#include <wifi.h>
#include <linux/vmalloc.h>
+#include <net/lib80211.h>
#define ETHERNET_HEADER_SIZE 14 /* Ethernet Header Length */
#define LLC_HEADER_SIZE 6 /* LLC Header Length */
@@ -220,31 +221,20 @@ u32 rtw_free_uc_swdec_pending_queue(struct adapter *adapter)
static int recvframe_chkmic(struct adapter *adapter,
struct recv_frame *precvframe)
{
- int i, res = _SUCCESS;
- u32 datalen;
- u8 miccode[8];
- u8 bmic_err = false, brpt_micerror = true;
- u8 *pframe, *payload, *pframemic;
- u8 *mickey;
- struct sta_info *stainfo;
- struct rx_pkt_attrib *prxattrib = &precvframe->attrib;
- struct security_priv *psecuritypriv = &adapter->securitypriv;
-
- struct mlme_ext_priv *pmlmeext = &adapter->mlmeextpriv;
- struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
-
- stainfo = rtw_get_stainfo(&adapter->stapriv, &prxattrib->ta[0]);
+ int res = _SUCCESS;
+ struct rx_pkt_attrib *prxattrib = &precvframe->attrib;
+ struct sta_info *stainfo = rtw_get_stainfo(&adapter->stapriv, prxattrib->ta);
if (prxattrib->encrypt == _TKIP_) {
- RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
- ("\n %s: prxattrib->encrypt==_TKIP_\n", __func__));
- RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
- ("\n %s: da=0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x\n",
- __func__, prxattrib->ra[0], prxattrib->ra[1], prxattrib->ra[2],
- prxattrib->ra[3], prxattrib->ra[4], prxattrib->ra[5]));
-
- /* calculate mic code */
- if (stainfo != NULL) {
+ if (stainfo) {
+ int key_idx;
+ const int iv_len = 8, icv_len = 4, key_length = 32;
+ struct sk_buff *skb = precvframe->pkt;
+ u8 key[32], iv[8], icv[4], *pframe = skb->data;
+ void *crypto_private = NULL;
+ struct lib80211_crypto_ops *crypto_ops = try_then_request_module(lib80211_get_crypto_ops("TKIP"), "lib80211_crypt_tkip");
+ struct security_priv *psecuritypriv = &adapter->securitypriv;
+
if (IS_MCAST(prxattrib->ra)) {
if (!psecuritypriv) {
res = _FAIL;
@@ -253,115 +243,58 @@ static int recvframe_chkmic(struct adapter *adapter,
DBG_88E("\n %s: didn't install group key!!!!!!!!!!\n", __func__);
goto exit;
}
- mickey = &psecuritypriv->dot118021XGrprxmickey[prxattrib->key_index].skey[0];
-
- RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
- ("\n %s: bcmc key\n", __func__));
+ key_idx = prxattrib->key_index;
+ memcpy(key, psecuritypriv->dot118021XGrpKey[key_idx].skey, 16);
+ memcpy(key + 16, psecuritypriv->dot118021XGrprxmickey[key_idx].skey, 16);
} else {
- mickey = &stainfo->dot11tkiprxmickey.skey[0];
- RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
- ("\n %s: unicast key\n", __func__));
+ key_idx = 0;
+ memcpy(key, stainfo->dot118021x_UncstKey.skey, 16);
+ memcpy(key + 16, stainfo->dot11tkiprxmickey.skey, 16);
}
- /* icv_len included the mic code */
- datalen = precvframe->pkt->len-prxattrib->hdrlen -
- prxattrib->iv_len-prxattrib->icv_len-8;
- pframe = precvframe->pkt->data;
- payload = pframe+prxattrib->hdrlen+prxattrib->iv_len;
-
- RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("\n prxattrib->iv_len=%d prxattrib->icv_len=%d\n", prxattrib->iv_len, prxattrib->icv_len));
- rtw_seccalctkipmic(mickey, pframe, payload, datalen, &miccode[0],
- (unsigned char)prxattrib->priority); /* care the length of the data */
+ if (!crypto_ops) {
+ res = _FAIL;
+ goto exit_lib80211_tkip;
+ }
- pframemic = payload+datalen;
+ memcpy(iv, pframe + prxattrib->hdrlen, iv_len);
+ memcpy(icv, pframe + skb->len - icv_len, icv_len);
+ memmove(pframe + iv_len, pframe, prxattrib->hdrlen);
- bmic_err = false;
+ skb_pull(skb, iv_len);
+ skb_trim(skb, skb->len - icv_len);
- for (i = 0; i < 8; i++) {
- if (miccode[i] != *(pframemic+i)) {
- RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
- ("%s: miccode[%d](%02x)!=*(pframemic+%d)(%02x) ",
- __func__, i, miccode[i], i, *(pframemic + i)));
- bmic_err = true;
- }
+ crypto_private = crypto_ops->init(key_idx);
+ if (!crypto_private) {
+ res = _FAIL;
+ goto exit_lib80211_tkip;
+ }
+ if (crypto_ops->set_key(key, key_length, NULL, crypto_private) < 0) {
+ res = _FAIL;
+ goto exit_lib80211_tkip;
+ }
+ if (crypto_ops->decrypt_msdu(skb, key_idx, prxattrib->hdrlen, crypto_private)) {
+ res = _FAIL;
+ goto exit_lib80211_tkip;
}
- if (bmic_err) {
- RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
- ("\n *(pframemic-8)-*(pframemic-1)=0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x\n",
- *(pframemic-8), *(pframemic-7), *(pframemic-6),
- *(pframemic-5), *(pframemic-4), *(pframemic-3),
- *(pframemic-2), *(pframemic-1)));
- RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
- ("\n *(pframemic-16)-*(pframemic-9)=0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x\n",
- *(pframemic-16), *(pframemic-15), *(pframemic-14),
- *(pframemic-13), *(pframemic-12), *(pframemic-11),
- *(pframemic-10), *(pframemic-9)));
- {
- uint i;
+ memmove(pframe, pframe + iv_len, prxattrib->hdrlen);
+ skb_push(skb, iv_len);
+ skb_put(skb, icv_len);
- RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
- ("\n ======demp packet (len=%d)======\n",
- precvframe->pkt->len));
- for (i = 0; i < precvframe->pkt->len; i += 8) {
- RT_TRACE(_module_rtl871x_recv_c_,
- _drv_err_,
- ("0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x",
- *(precvframe->pkt->data+i),
- *(precvframe->pkt->data+i+1),
- *(precvframe->pkt->data+i+2),
- *(precvframe->pkt->data+i+3),
- *(precvframe->pkt->data+i+4),
- *(precvframe->pkt->data+i+5),
- *(precvframe->pkt->data+i+6),
- *(precvframe->pkt->data+i+7)));
- }
- RT_TRACE(_module_rtl871x_recv_c_,
- _drv_err_,
- ("\n ====== demp packet end [len=%d]======\n",
- precvframe->pkt->len));
- RT_TRACE(_module_rtl871x_recv_c_,
- _drv_err_,
- ("\n hrdlen=%d,\n",
- prxattrib->hdrlen));
- }
+ memcpy(pframe + prxattrib->hdrlen, iv, iv_len);
+ memcpy(pframe + skb->len - icv_len, icv, icv_len);
- RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
- ("ra=0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x psecuritypriv->binstallGrpkey=%d ",
- prxattrib->ra[0], prxattrib->ra[1], prxattrib->ra[2],
- prxattrib->ra[3], prxattrib->ra[4], prxattrib->ra[5], psecuritypriv->binstallGrpkey));
-
- /* double check key_index for some timing issue , */
- /* cannot compare with psecuritypriv->dot118021XGrpKeyid also cause timing issue */
- if ((IS_MCAST(prxattrib->ra) == true) && (prxattrib->key_index != pmlmeinfo->key_index))
- brpt_micerror = false;
-
- if ((prxattrib->bdecrypted) && (brpt_micerror)) {
- rtw_handle_tkip_mic_err(adapter, (u8)IS_MCAST(prxattrib->ra));
- RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, (" mic error :prxattrib->bdecrypted=%d ", prxattrib->bdecrypted));
- DBG_88E(" mic error :prxattrib->bdecrypted=%d\n", prxattrib->bdecrypted);
- } else {
- RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, (" mic error :prxattrib->bdecrypted=%d ", prxattrib->bdecrypted));
- DBG_88E(" mic error :prxattrib->bdecrypted=%d\n", prxattrib->bdecrypted);
- }
- res = _FAIL;
- } else {
- /* mic checked ok */
- if ((!psecuritypriv->bcheck_grpkey) && (IS_MCAST(prxattrib->ra))) {
- psecuritypriv->bcheck_grpkey = true;
- RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("psecuritypriv->bcheck_grpkey = true"));
- }
- }
+exit_lib80211_tkip:
+ if (crypto_ops && crypto_private)
+ crypto_ops->deinit(crypto_private);
} else {
RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
("%s: rtw_get_stainfo==NULL!!!\n", __func__));
}
-
- skb_trim(precvframe->pkt, precvframe->pkt->len - 8);
}
exit:
-
return res;
}
@@ -404,7 +337,7 @@ static struct recv_frame *decryptor(struct adapter *padapter,
switch (prxattrib->encrypt) {
case _WEP40_:
case _WEP104_:
- rtw_wep_decrypt(padapter, (u8 *)precv_frame);
+ res = rtw_wep_decrypt(padapter, (u8 *)precv_frame);
break;
case _TKIP_:
res = rtw_tkip_decrypt(padapter, (u8 *)precv_frame);
@@ -454,7 +387,7 @@ static struct recv_frame *portctrl(struct adapter *adapter,
prtnframe = NULL;
- RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("########portctrl:adapter->securitypriv.dot11AuthAlgrthm=%d\n", adapter->securitypriv.dot11AuthAlgrthm));
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("########%s:adapter->securitypriv.dot11AuthAlgrthm=%d\n", __func__, adapter->securitypriv.dot11AuthAlgrthm));
if (auth_alg == 2) {
/* get ether_type */
@@ -465,7 +398,7 @@ static struct recv_frame *portctrl(struct adapter *adapter,
if ((psta != NULL) && (psta->ieee8021x_blocked)) {
/* blocked */
/* only accept EAPOL frame */
- RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("########portctrl:psta->ieee8021x_blocked==1\n"));
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("########%s:psta->ieee8021x_blocked==1\n", __func__));
if (ether_type == eapol_type) {
prtnframe = precv_frame;
@@ -477,23 +410,23 @@ static struct recv_frame *portctrl(struct adapter *adapter,
} else {
/* allowed */
/* check decryption status, and decrypt the frame if needed */
- RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("########portctrl:psta->ieee8021x_blocked==0\n"));
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("########%s:psta->ieee8021x_blocked==0\n", __func__));
RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
- ("portctrl:precv_frame->hdr.attrib.privacy=%x\n",
- precv_frame->attrib.privacy));
+ ("%s:precv_frame->hdr.attrib.privacy=%x\n",
+ __func__, precv_frame->attrib.privacy));
if (pattrib->bdecrypted == 0)
- RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("portctrl:prxstat->decrypted=%x\n", pattrib->bdecrypted));
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("%s:prxstat->decrypted=%x\n", __func__, pattrib->bdecrypted));
prtnframe = precv_frame;
/* check is the EAPOL frame or not (Rekey) */
if (ether_type == eapol_type) {
- RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_, ("########portctrl:ether_type==0x888e\n"));
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_, ("########%s:ether_type==0x888e\n", __func__));
/* check Rekey */
prtnframe = precv_frame;
} else {
- RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("########portctrl:ether_type=0x%04x\n", ether_type));
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("########%s:ether_type=0x%04x\n", __func__, ether_type));
}
}
} else {
@@ -512,14 +445,14 @@ static int recv_decache(struct recv_frame *precv_frame, u8 bretry,
(precv_frame->attrib.frag_num & 0xf);
if (tid > 15) {
- RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_, ("recv_decache, (tid>15)! seq_ctrl=0x%x, tid=0x%x\n", seq_ctrl, tid));
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_, ("%s, (tid>15)! seq_ctrl=0x%x, tid=0x%x\n", __func__, seq_ctrl, tid));
return _FAIL;
}
if (1) {/* if (bretry) */
if (seq_ctrl == prxcache->tid_rxseq[tid]) {
- RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_, ("recv_decache, seq_ctrl=0x%x, tid=0x%x, tid_rxseq=0x%x\n", seq_ctrl, tid, prxcache->tid_rxseq[tid]));
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_, ("%s, seq_ctrl=0x%x, tid=0x%x, tid_rxseq=0x%x\n", __func__, seq_ctrl, tid, prxcache->tid_rxseq[tid]));
return _FAIL;
}
@@ -718,7 +651,7 @@ int sta2sta_data_frame(struct adapter *adapter, struct recv_frame *precv_frame,
*psta = rtw_get_stainfo(pstapriv, sta_addr); /* get ap_info */
if (*psta == NULL) {
- RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("can't get psta under sta2sta_data_frame ; drop pkt\n"));
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("can't get psta under %s ; drop pkt\n", __func__));
ret = _FAIL;
goto exit;
}
@@ -754,7 +687,7 @@ static int ap2sta_data_frame(
/* da should be for me */
if ((memcmp(myhwaddr, pattrib->dst, ETH_ALEN)) && (!bmcast)) {
RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
- (" ap2sta_data_frame: compare DA fail; DA=%pM\n", (pattrib->dst)));
+ (" %s: compare DA fail; DA=%pM\n", __func__, (pattrib->dst)));
ret = _FAIL;
goto exit;
}
@@ -764,7 +697,7 @@ static int ap2sta_data_frame(
!memcmp(mybssid, "\x0\x0\x0\x0\x0\x0", ETH_ALEN) ||
(memcmp(pattrib->bssid, mybssid, ETH_ALEN))) {
RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
- (" ap2sta_data_frame: compare BSSID fail ; BSSID=%pM\n", (pattrib->bssid)));
+ (" %s: compare BSSID fail ; BSSID=%pM\n", __func__, (pattrib->bssid)));
RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("mybssid=%pM\n", (mybssid)));
if (!bmcast) {
@@ -1009,10 +942,10 @@ static int validate_recv_mgnt_frame(struct adapter *padapter,
{
struct sta_info *psta;
- RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("+validate_recv_mgnt_frame\n"));
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("+%s\n", __func__));
precv_frame = recvframe_chk_defrag(padapter, precv_frame);
- if (precv_frame == NULL) {
+ if (!precv_frame) {
RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_,
("%s: fragment packet\n", __func__));
return _SUCCESS;
@@ -1060,7 +993,7 @@ static int validate_recv_data_frame(struct adapter *adapter,
psa = get_sa(ptr);
pbssid = get_hdr_bssid(ptr);
- if (pbssid == NULL) {
+ if (!pbssid) {
ret = _FAIL;
goto exit;
}
@@ -1102,7 +1035,7 @@ static int validate_recv_data_frame(struct adapter *adapter,
else if (ret == RTW_RX_HANDLED)
goto exit;
- if (psta == NULL) {
+ if (!psta) {
RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, (" after to_fr_ds_chk; psta==NULL\n"));
ret = _FAIL;
goto exit;
@@ -1141,7 +1074,7 @@ static int validate_recv_data_frame(struct adapter *adapter,
}
if (pattrib->privacy) {
- RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("validate_recv_data_frame:pattrib->privacy=%x\n", pattrib->privacy));
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("%s:pattrib->privacy=%x\n", __func__, pattrib->privacy));
RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("\n ^^^^^^^^^^^IS_MCAST(pattrib->ra(0x%02x))=%d^^^^^^^^^^^^^^^6\n", pattrib->ra[0], IS_MCAST(pattrib->ra)));
GET_ENCRY_ALGO(psecuritypriv, psta, pattrib->encrypt, IS_MCAST(pattrib->ra));
@@ -1436,7 +1369,7 @@ struct recv_frame *recvframe_chk_defrag(struct adapter *padapter,
psta_addr = pfhdr->attrib.ta;
psta = rtw_get_stainfo(pstapriv, psta_addr);
- if (psta == NULL) {
+ if (!psta) {
u8 type = GetFrameType(pfhdr->pkt->data);
if (type != WIFI_DATA_TYPE) {
@@ -1455,7 +1388,7 @@ struct recv_frame *recvframe_chk_defrag(struct adapter *padapter,
if (ismfrag == 1) {
/* 0~(n-1) fragment frame */
/* enqueue to defraf_g */
- if (pdefrag_q != NULL) {
+ if (pdefrag_q) {
if (fragnum == 0) {
/* the first fragment */
if (!list_empty(&pdefrag_q->queue))
@@ -1482,7 +1415,7 @@ struct recv_frame *recvframe_chk_defrag(struct adapter *padapter,
if ((ismfrag == 0) && (fragnum != 0)) {
/* the last fragment frame */
/* enqueue the last fragment */
- if (pdefrag_q != NULL) {
+ if (pdefrag_q) {
phead = get_list_head(pdefrag_q);
list_add_tail(&pfhdr->list, phead);
@@ -1714,8 +1647,8 @@ static int recv_indicatepkts_in_order(struct adapter *padapter, struct recv_reor
if (!SN_LESS(preorder_ctrl->indicate_seq, pattrib->seq_num)) {
RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_,
- ("recv_indicatepkts_in_order: indicate=%d seq=%d amsdu=%d\n",
- preorder_ctrl->indicate_seq, pattrib->seq_num, pattrib->amsdu));
+ ("%s: indicate=%d seq=%d amsdu=%d\n",
+ __func__, preorder_ctrl->indicate_seq, pattrib->seq_num, pattrib->amsdu));
plist = plist->next;
list_del_init(&(prframe->list));
@@ -1762,7 +1695,7 @@ static int recv_indicatepkt_reorder(struct adapter *padapter,
(pattrib->ack_policy != 0)) {
if ((!padapter->bDriverStopped) &&
(!padapter->bSurpriseRemoved)) {
- RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_, ("@@@@ recv_indicatepkt_reorder -recv_func recv_indicatepkt\n"));
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_, ("@@@@ %s -recv_func recv_indicatepkt\n", __func__));
rtw_recv_indicatepkt(padapter, prframe);
return _SUCCESS;
@@ -1792,7 +1725,7 @@ static int recv_indicatepkt_reorder(struct adapter *padapter,
spin_lock_bh(&ppending_recvframe_queue->lock);
RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_,
- ("recv_indicatepkt_reorder: indicate=%d seq=%d\n",
+ ("%s: indicate=%d seq=%d\n", __func__,
preorder_ctrl->indicate_seq, pattrib->seq_num));
/* s2. check if winstart_b(indicate_seq) needs to been updated */
@@ -1884,10 +1817,10 @@ static int process_recv_indicatepkts(struct adapter *padapter,
if ((!padapter->bDriverStopped) &&
(!padapter->bSurpriseRemoved)) {
/* indicate this recv_frame */
- RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_, ("@@@@ process_recv_indicatepkts- recv_func recv_indicatepkt\n"));
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_, ("@@@@ %s- recv_func recv_indicatepkt\n", __func__));
rtw_recv_indicatepkt(padapter, prframe);
} else {
- RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_, ("@@@@ process_recv_indicatepkts- recv_func free_indicatepkt\n"));
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_, ("@@@@ %s- recv_func free_indicatepkt\n", __func__));
RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_, ("recv_func:bDriverStopped(%d) OR bSurpriseRemoved(%d)", padapter->bDriverStopped, padapter->bSurpriseRemoved));
retval = _FAIL;
@@ -1928,20 +1861,20 @@ static int recv_func_posthandle(struct adapter *padapter,
LedControl8188eu(padapter, LED_CTL_RX);
prframe = decryptor(padapter, prframe);
- if (prframe == NULL) {
+ if (!prframe) {
RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("decryptor: drop pkt\n"));
ret = _FAIL;
goto _recv_data_drop;
}
prframe = recvframe_chk_defrag(padapter, prframe);
- if (prframe == NULL) {
+ if (!prframe) {
RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("recvframe_chk_defrag: drop pkt\n"));
goto _recv_data_drop;
}
prframe = portctrl(padapter, prframe);
- if (prframe == NULL) {
+ if (!prframe) {
RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("portctrl: drop pkt\n"));
ret = _FAIL;
goto _recv_data_drop;
@@ -2012,7 +1945,7 @@ s32 rtw_recv_entry(struct recv_frame *precvframe)
ret = recv_func(padapter, precvframe);
if (ret == _FAIL) {
- RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("rtw_recv_entry: recv_func return fail!!!\n"));
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("%s: recv_func return fail!!!\n", __func__));
goto _recv_entry_drop;
}
diff --git a/drivers/staging/rtl8188eu/core/rtw_security.c b/drivers/staging/rtl8188eu/core/rtw_security.c
index 5b1ef229df2a..bfe0b217e679 100644
--- a/drivers/staging/rtl8188eu/core/rtw_security.c
+++ b/drivers/staging/rtl8188eu/core/rtw_security.c
@@ -18,6 +18,7 @@
#include <drv_types.h>
#include <wifi.h>
#include <osdep_intf.h>
+#include <net/lib80211.h>
/* WEP related ===== */
@@ -195,48 +196,57 @@ void rtw_wep_encrypt(struct adapter *padapter, u8 *pxmitframe)
}
-void rtw_wep_decrypt(struct adapter *padapter, u8 *precvframe)
+int rtw_wep_decrypt(struct adapter *padapter, u8 *precvframe)
{
- /* exclude ICV */
- u8 crc[4];
- struct arc4context mycontext;
- int length;
- u32 keylength;
- u8 *pframe, *payload, *iv, wepkey[16];
- u8 keyindex;
struct rx_pkt_attrib *prxattrib = &(((struct recv_frame *)precvframe)->attrib);
- struct security_priv *psecuritypriv = &padapter->securitypriv;
+ if ((prxattrib->encrypt == _WEP40_) || (prxattrib->encrypt == _WEP104_)) {
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct sk_buff *skb = ((struct recv_frame *)precvframe)->pkt;
+ u8 *pframe = skb->data;
+ void *crypto_private = NULL;
+ int status = _SUCCESS;
+ const int keyindex = prxattrib->key_index;
+ struct lib80211_crypto_ops *crypto_ops = try_then_request_module(lib80211_get_crypto_ops("WEP"), "lib80211_crypt_wep");
+ char iv[4], icv[4];
+
+ if (!crypto_ops) {
+ status = _FAIL;
+ goto exit;
+ }
- pframe = (unsigned char *)((struct recv_frame *)precvframe)->pkt->data;
+ memcpy(iv, pframe + prxattrib->hdrlen, 4);
+ memcpy(icv, pframe + skb->len - 4, 4);
- /* start to decrypt recvframe */
- if ((prxattrib->encrypt == _WEP40_) || (prxattrib->encrypt == _WEP104_)) {
- iv = pframe+prxattrib->hdrlen;
- keyindex = prxattrib->key_index;
- keylength = psecuritypriv->dot11DefKeylen[keyindex];
- memcpy(&wepkey[0], iv, 3);
- memcpy(&wepkey[3], &psecuritypriv->dot11DefKey[keyindex].skey[0], keylength);
- length = ((struct recv_frame *)precvframe)->pkt->len-prxattrib->hdrlen-prxattrib->iv_len;
-
- payload = pframe+prxattrib->iv_len+prxattrib->hdrlen;
-
- /* decrypt payload include icv */
- arcfour_init(&mycontext, wepkey, 3+keylength);
- arcfour_encrypt(&mycontext, payload, payload, length);
-
- /* calculate icv and compare the icv */
- *((__le32 *)crc) = getcrc32(payload, length - 4);
-
- if (crc[3] != payload[length-1] ||
- crc[2] != payload[length-2] ||
- crc[1] != payload[length-3] ||
- crc[0] != payload[length-4]) {
- RT_TRACE(_module_rtl871x_security_c_, _drv_err_,
- ("rtw_wep_decrypt:icv error crc (%4ph)!=payload (%4ph)\n",
- &crc, &payload[length-4]));
+ crypto_private = crypto_ops->init(keyindex);
+ if (!crypto_private) {
+ status = _FAIL;
+ goto exit;
}
+ if (crypto_ops->set_key(psecuritypriv->dot11DefKey[keyindex].skey,
+ psecuritypriv->dot11DefKeylen[keyindex], NULL, crypto_private) < 0) {
+ status = _FAIL;
+ goto exit;
+ }
+ if (crypto_ops->decrypt_mpdu(skb, prxattrib->hdrlen, crypto_private)) {
+ status = _FAIL;
+ goto exit;
+ }
+
+ memmove(pframe, pframe + 4, prxattrib->hdrlen);
+ skb_push(skb, 4);
+ skb_put(skb, 4);
+
+ memcpy(pframe + prxattrib->hdrlen, iv, 4);
+ memcpy(pframe + skb->len - 4, icv, 4);
+
+exit:
+ if (crypto_ops && crypto_private)
+ crypto_ops->deinit(crypto_private);
+ return status;
}
+
+ return _FAIL;
}
/* 3 ===== TKIP related ===== */
@@ -593,7 +603,7 @@ u32 rtw_tkip_encrypt(struct adapter *padapter, u8 *pxmitframe)
stainfo = rtw_get_stainfo(&padapter->stapriv, &pattrib->ra[0]);
if (stainfo != NULL) {
- RT_TRACE(_module_rtl871x_security_c_, _drv_err_, ("rtw_tkip_encrypt: stainfo!= NULL!!!\n"));
+ RT_TRACE(_module_rtl871x_security_c_, _drv_err_, ("%s: stainfo!= NULL!!!\n", __func__));
if (IS_MCAST(pattrib->ra))
prwskey = psecuritypriv->dot118021XGrpKey[psecuritypriv->dot118021XGrpKeyid].skey;
@@ -633,78 +643,78 @@ u32 rtw_tkip_encrypt(struct adapter *padapter, u8 *pxmitframe)
}
}
} else {
- RT_TRACE(_module_rtl871x_security_c_, _drv_err_, ("rtw_tkip_encrypt: stainfo==NULL!!!\n"));
+ RT_TRACE(_module_rtl871x_security_c_, _drv_err_, ("%s: stainfo==NULL!!!\n", __func__));
res = _FAIL;
}
}
return res;
}
-/* The hlen isn't include the IV */
u32 rtw_tkip_decrypt(struct adapter *padapter, u8 *precvframe)
-{ /* exclude ICV */
- u16 pnl;
- u32 pnh;
- u8 rc4key[16];
- u8 ttkey[16];
- u8 crc[4];
- struct arc4context mycontext;
- int length;
-
- u8 *pframe, *payload, *iv, *prwskey;
- union pn48 dot11txpn;
- struct sta_info *stainfo;
- struct rx_pkt_attrib *prxattrib = &((struct recv_frame *)precvframe)->attrib;
- struct security_priv *psecuritypriv = &padapter->securitypriv;
- u32 res = _SUCCESS;
-
-
- pframe = (unsigned char *)((struct recv_frame *)precvframe)->pkt->data;
+{
+ struct rx_pkt_attrib *prxattrib = &((struct recv_frame *)precvframe)->attrib;
+ u32 res = _SUCCESS;
/* 4 start to decrypt recvframe */
if (prxattrib->encrypt == _TKIP_) {
- stainfo = rtw_get_stainfo(&padapter->stapriv, &prxattrib->ta[0]);
- if (stainfo != NULL) {
+ struct sta_info *stainfo = rtw_get_stainfo(&padapter->stapriv, prxattrib->ta);
+
+ if (stainfo) {
+ int key_idx;
+ const int iv_len = 8, icv_len = 4, key_length = 32;
+ void *crypto_private = NULL;
+ struct sk_buff *skb = ((struct recv_frame *)precvframe)->pkt;
+ u8 key[32], iv[8], icv[4], *pframe = skb->data;
+ struct lib80211_crypto_ops *crypto_ops = try_then_request_module(lib80211_get_crypto_ops("TKIP"), "lib80211_crypt_tkip");
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+
if (IS_MCAST(prxattrib->ra)) {
if (!psecuritypriv->binstallGrpkey) {
res = _FAIL;
DBG_88E("%s:rx bc/mc packets, but didn't install group key!!!!!!!!!!\n", __func__);
goto exit;
}
- prwskey = psecuritypriv->dot118021XGrpKey[prxattrib->key_index].skey;
+ key_idx = prxattrib->key_index;
+ memcpy(key, psecuritypriv->dot118021XGrpKey[key_idx].skey, 16);
+ memcpy(key + 16, psecuritypriv->dot118021XGrprxmickey[key_idx].skey, 16);
} else {
- RT_TRACE(_module_rtl871x_security_c_, _drv_err_, ("rtw_tkip_decrypt: stainfo!= NULL!!!\n"));
- prwskey = &stainfo->dot118021x_UncstKey.skey[0];
+ key_idx = 0;
+ memcpy(key, stainfo->dot118021x_UncstKey.skey, 16);
+ memcpy(key + 16, stainfo->dot11tkiprxmickey.skey, 16);
}
- iv = pframe+prxattrib->hdrlen;
- payload = pframe+prxattrib->iv_len+prxattrib->hdrlen;
- length = ((struct recv_frame *)precvframe)->pkt->len-prxattrib->hdrlen-prxattrib->iv_len;
-
- GET_TKIP_PN(iv, dot11txpn);
-
- pnl = (u16)(dot11txpn.val);
- pnh = (u32)(dot11txpn.val>>16);
+ if (!crypto_ops) {
+ res = _FAIL;
+ goto exit_lib80211_tkip;
+ }
- phase1((u16 *)&ttkey[0], prwskey, &prxattrib->ta[0], pnh);
- phase2(&rc4key[0], prwskey, (unsigned short *)&ttkey[0], pnl);
+ memcpy(iv, pframe + prxattrib->hdrlen, iv_len);
+ memcpy(icv, pframe + skb->len - icv_len, icv_len);
- /* 4 decrypt payload include icv */
+ crypto_private = crypto_ops->init(key_idx);
+ if (!crypto_private) {
+ res = _FAIL;
+ goto exit_lib80211_tkip;
+ }
+ if (crypto_ops->set_key(key, key_length, NULL, crypto_private) < 0) {
+ res = _FAIL;
+ goto exit_lib80211_tkip;
+ }
+ if (crypto_ops->decrypt_mpdu(skb, prxattrib->hdrlen, crypto_private)) {
+ res = _FAIL;
+ goto exit_lib80211_tkip;
+ }
- arcfour_init(&mycontext, rc4key, 16);
- arcfour_encrypt(&mycontext, payload, payload, length);
+ memmove(pframe, pframe + iv_len, prxattrib->hdrlen);
+ skb_push(skb, iv_len);
+ skb_put(skb, icv_len);
- *((__le32 *)crc) = getcrc32(payload, length-4);
+ memcpy(pframe + prxattrib->hdrlen, iv, iv_len);
+ memcpy(pframe + skb->len - icv_len, icv, icv_len);
- if (crc[3] != payload[length-1] ||
- crc[2] != payload[length-2] ||
- crc[1] != payload[length-3] ||
- crc[0] != payload[length-4]) {
- RT_TRACE(_module_rtl871x_security_c_, _drv_err_,
- ("rtw_wep_decrypt:icv error crc (%4ph)!=payload (%4ph)\n",
- &crc, &payload[length-4]));
- res = _FAIL;
- }
+exit_lib80211_tkip:
+ if (crypto_ops && crypto_private)
+ crypto_ops->deinit(crypto_private);
} else {
RT_TRACE(_module_rtl871x_security_c_, _drv_err_, ("rtw_tkip_decrypt: stainfo==NULL!!!\n"));
res = _FAIL;
@@ -1235,8 +1245,8 @@ u32 rtw_aes_encrypt(struct adapter *padapter, u8 *pxmitframe)
else
stainfo = rtw_get_stainfo(&padapter->stapriv, &pattrib->ra[0]);
- if (stainfo != NULL) {
- RT_TRACE(_module_rtl871x_security_c_, _drv_err_, ("rtw_aes_encrypt: stainfo!= NULL!!!\n"));
+ if (stainfo) {
+ RT_TRACE(_module_rtl871x_security_c_, _drv_err_, ("%s: stainfo!= NULL!!!\n", __func__));
if (IS_MCAST(pattrib->ra))
prwskey = psecuritypriv->dot118021XGrpKey[psecuritypriv->dot118021XGrpKeyid].skey;
@@ -1256,7 +1266,7 @@ u32 rtw_aes_encrypt(struct adapter *padapter, u8 *pxmitframe)
}
}
} else{
- RT_TRACE(_module_rtl871x_security_c_, _drv_err_, ("rtw_aes_encrypt: stainfo==NULL!!!\n"));
+ RT_TRACE(_module_rtl871x_security_c_, _drv_err_, ("%s: stainfo==NULL!!!\n", __func__));
res = _FAIL;
}
}
@@ -1265,217 +1275,24 @@ u32 rtw_aes_encrypt(struct adapter *padapter, u8 *pxmitframe)
return res;
}
-static int aes_decipher(u8 *key, uint hdrlen,
- u8 *pframe, uint plen)
+u32 rtw_aes_decrypt(struct adapter *padapter, u8 *precvframe)
{
- static u8 message[MAX_MSG_SIZE];
- uint qc_exists, a4_exists, i, j, payload_remainder,
- num_blocks, payload_index;
- int res = _SUCCESS;
-
- u8 pn_vector[6];
- u8 mic_iv[16];
- u8 mic_header1[16];
- u8 mic_header2[16];
- u8 ctr_preload[16];
-
- /* Intermediate Buffers */
- u8 chain_buffer[16];
- u8 aes_out[16];
- u8 padded_buffer[16];
- u8 mic[8];
-
-/* uint offset = 0; */
- uint frtype = GetFrameType(pframe);
- uint frsubtype = GetFrameSubType(pframe);
- frsubtype >>= 4;
-
- memset(mic_iv, 0, 16);
- memset(mic_header1, 0, 16);
- memset(mic_header2, 0, 16);
- memset(ctr_preload, 0, 16);
- memset(chain_buffer, 0, 16);
- memset(aes_out, 0, 16);
- memset(padded_buffer, 0, 16);
-
- /* start to decrypt the payload */
-
- num_blocks = (plen-8) / 16; /* plen including llc, payload_length and mic) */
-
- payload_remainder = (plen-8) % 16;
-
- pn_vector[0] = pframe[hdrlen];
- pn_vector[1] = pframe[hdrlen+1];
- pn_vector[2] = pframe[hdrlen+4];
- pn_vector[3] = pframe[hdrlen+5];
- pn_vector[4] = pframe[hdrlen+6];
- pn_vector[5] = pframe[hdrlen+7];
-
- if ((hdrlen == WLAN_HDR_A3_LEN) || (hdrlen == WLAN_HDR_A3_QOS_LEN))
- a4_exists = 0;
- else
- a4_exists = 1;
-
- if ((frtype == WIFI_DATA_CFACK) || (frtype == WIFI_DATA_CFPOLL) ||
- (frtype == WIFI_DATA_CFACKPOLL)) {
- qc_exists = 1;
- if (hdrlen != WLAN_HDR_A3_QOS_LEN)
- hdrlen += 2;
- } else if ((frsubtype == 0x08) || (frsubtype == 0x09) ||
- (frsubtype == 0x0a) || (frsubtype == 0x0b)) {
- if (hdrlen != WLAN_HDR_A3_QOS_LEN)
- hdrlen += 2;
- qc_exists = 1;
- } else {
- qc_exists = 0;
- }
-
- /* now, decrypt pframe with hdrlen offset and plen long */
-
- payload_index = hdrlen + 8; /* 8 is for extiv */
-
- for (i = 0; i < num_blocks; i++) {
- construct_ctr_preload(ctr_preload, a4_exists, qc_exists, pframe, pn_vector, i+1);
-
- aes128k128d(key, ctr_preload, aes_out);
- bitwise_xor(aes_out, &pframe[payload_index], chain_buffer);
-
- for (j = 0; j < 16; j++)
- pframe[payload_index++] = chain_buffer[j];
- }
-
- if (payload_remainder > 0) { /* If there is a short final block, then pad it,*/
- /* encrypt it and copy the unpadded part back */
- construct_ctr_preload(ctr_preload, a4_exists, qc_exists, pframe, pn_vector, num_blocks+1);
-
- for (j = 0; j < 16; j++)
- padded_buffer[j] = 0x00;
- for (j = 0; j < payload_remainder; j++)
- padded_buffer[j] = pframe[payload_index+j];
- aes128k128d(key, ctr_preload, aes_out);
- bitwise_xor(aes_out, padded_buffer, chain_buffer);
- for (j = 0; j < payload_remainder; j++)
- pframe[payload_index++] = chain_buffer[j];
- }
-
- /* start to calculate the mic */
- if ((hdrlen+plen+8) <= MAX_MSG_SIZE)
- memcpy(message, pframe, (hdrlen + plen+8)); /* 8 is for ext iv len */
-
- pn_vector[0] = pframe[hdrlen];
- pn_vector[1] = pframe[hdrlen+1];
- pn_vector[2] = pframe[hdrlen+4];
- pn_vector[3] = pframe[hdrlen+5];
- pn_vector[4] = pframe[hdrlen+6];
- pn_vector[5] = pframe[hdrlen+7];
- construct_mic_iv(mic_iv, qc_exists, a4_exists, message, plen-8, pn_vector);
-
- construct_mic_header1(mic_header1, hdrlen, message);
- construct_mic_header2(mic_header2, message, a4_exists, qc_exists);
-
- payload_remainder = (plen-8) % 16;
- num_blocks = (plen-8) / 16;
-
- /* Find start of payload */
- payload_index = hdrlen + 8;
-
- /* Calculate MIC */
- aes128k128d(key, mic_iv, aes_out);
- bitwise_xor(aes_out, mic_header1, chain_buffer);
- aes128k128d(key, chain_buffer, aes_out);
- bitwise_xor(aes_out, mic_header2, chain_buffer);
- aes128k128d(key, chain_buffer, aes_out);
-
- for (i = 0; i < num_blocks; i++) {
- bitwise_xor(aes_out, &message[payload_index], chain_buffer);
-
- payload_index += 16;
- aes128k128d(key, chain_buffer, aes_out);
- }
-
- /* Add on the final payload block if it needs padding */
- if (payload_remainder > 0) {
- for (j = 0; j < 16; j++)
- padded_buffer[j] = 0x00;
- for (j = 0; j < payload_remainder; j++)
- padded_buffer[j] = message[payload_index++];
- bitwise_xor(aes_out, padded_buffer, chain_buffer);
- aes128k128d(key, chain_buffer, aes_out);
- }
-
- for (j = 0 ; j < 8; j++)
- mic[j] = aes_out[j];
-
- /* Insert MIC into payload */
- for (j = 0; j < 8; j++)
- message[payload_index+j] = mic[j];
-
- payload_index = hdrlen + 8;
- for (i = 0; i < num_blocks; i++) {
- construct_ctr_preload(ctr_preload, a4_exists, qc_exists, message, pn_vector, i+1);
- aes128k128d(key, ctr_preload, aes_out);
- bitwise_xor(aes_out, &message[payload_index], chain_buffer);
- for (j = 0; j < 16; j++)
- message[payload_index++] = chain_buffer[j];
- }
-
- if (payload_remainder > 0) { /* If there is a short final block, then pad it,*/
- /* encrypt it and copy the unpadded part back */
- construct_ctr_preload(ctr_preload, a4_exists, qc_exists, message, pn_vector, num_blocks+1);
-
- for (j = 0; j < 16; j++)
- padded_buffer[j] = 0x00;
- for (j = 0; j < payload_remainder; j++)
- padded_buffer[j] = message[payload_index+j];
- aes128k128d(key, ctr_preload, aes_out);
- bitwise_xor(aes_out, padded_buffer, chain_buffer);
- for (j = 0; j < payload_remainder; j++)
- message[payload_index++] = chain_buffer[j];
- }
-
- /* Encrypt the MIC */
- construct_ctr_preload(ctr_preload, a4_exists, qc_exists, message, pn_vector, 0);
-
- for (j = 0; j < 16; j++)
- padded_buffer[j] = 0x00;
- for (j = 0; j < 8; j++)
- padded_buffer[j] = message[j+hdrlen+8+plen-8];
-
- aes128k128d(key, ctr_preload, aes_out);
- bitwise_xor(aes_out, padded_buffer, chain_buffer);
- for (j = 0; j < 8; j++)
- message[payload_index++] = chain_buffer[j];
-
- /* compare the mic */
- for (i = 0; i < 8; i++) {
- if (pframe[hdrlen+8+plen-8+i] != message[hdrlen+8+plen-8+i]) {
- RT_TRACE(_module_rtl871x_security_c_, _drv_err_,
- ("aes_decipher:mic check error mic[%d]: pframe(%x)!=message(%x)\n",
- i, pframe[hdrlen+8+plen-8+i], message[hdrlen+8+plen-8+i]));
- DBG_88E("aes_decipher:mic check error mic[%d]: pframe(%x)!=message(%x)\n",
- i, pframe[hdrlen+8+plen-8+i], message[hdrlen+8+plen-8+i]);
- res = _FAIL;
- }
- }
- return res;
-}
-
-u32 rtw_aes_decrypt(struct adapter *padapter, u8 *precvframe)
-{ /* exclude ICV */
- /* Intermediate Buffers */
- int length;
- u8 *pframe, *prwskey; /* *payload,*iv */
- struct sta_info *stainfo;
- struct rx_pkt_attrib *prxattrib = &((struct recv_frame *)precvframe)->attrib;
- struct security_priv *psecuritypriv = &padapter->securitypriv;
- u32 res = _SUCCESS;
+ struct rx_pkt_attrib *prxattrib = &((struct recv_frame *)precvframe)->attrib;
+ u32 res = _SUCCESS;
- pframe = (unsigned char *)((struct recv_frame *)precvframe)->pkt->data;
/* 4 start to encrypt each fragment */
if (prxattrib->encrypt == _AES_) {
- stainfo = rtw_get_stainfo(&padapter->stapriv, &prxattrib->ta[0]);
+ struct sta_info *stainfo = rtw_get_stainfo(&padapter->stapriv, &prxattrib->ta[0]);
+
if (stainfo != NULL) {
- RT_TRACE(_module_rtl871x_security_c_, _drv_err_, ("rtw_aes_decrypt: stainfo!= NULL!!!\n"));
+ int key_idx;
+ const int key_length = 16, iv_len = 8, icv_len = 8;
+ struct sk_buff *skb = ((struct recv_frame *)precvframe)->pkt;
+ void *crypto_private = NULL;
+ u8 *key, *pframe = skb->data;
+ struct lib80211_crypto_ops *crypto_ops = try_then_request_module(lib80211_get_crypto_ops("CCMP"), "lib80211_crypt_ccmp");
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ char iv[8], icv[8];
if (IS_MCAST(prxattrib->ra)) {
/* in concurrent we should use sw descrypt in group key, so we remove this message */
@@ -1484,18 +1301,45 @@ u32 rtw_aes_decrypt(struct adapter *padapter, u8 *precvframe)
DBG_88E("%s:rx bc/mc packets, but didn't install group key!!!!!!!!!!\n", __func__);
goto exit;
}
- prwskey = psecuritypriv->dot118021XGrpKey[prxattrib->key_index].skey;
- if (psecuritypriv->dot118021XGrpKeyid != prxattrib->key_index) {
- DBG_88E("not match packet_index=%d, install_index=%d\n",
- prxattrib->key_index, psecuritypriv->dot118021XGrpKeyid);
- res = _FAIL;
- goto exit;
- }
+ key_idx = psecuritypriv->dot118021XGrpKeyid;
+ key = psecuritypriv->dot118021XGrpKey[key_idx].skey;
} else {
- prwskey = &stainfo->dot118021x_UncstKey.skey[0];
+ key_idx = 0;
+ key = stainfo->dot118021x_UncstKey.skey;
+ }
+
+ if (!crypto_ops) {
+ res = _FAIL;
+ goto exit_lib80211_ccmp;
}
- length = ((struct recv_frame *)precvframe)->pkt->len-prxattrib->hdrlen-prxattrib->iv_len;
- res = aes_decipher(prwskey, prxattrib->hdrlen, pframe, length);
+
+ memcpy(iv, pframe + prxattrib->hdrlen, iv_len);
+ memcpy(icv, pframe + skb->len - icv_len, icv_len);
+
+ crypto_private = crypto_ops->init(key_idx);
+ if (!crypto_private) {
+ res = _FAIL;
+ goto exit_lib80211_ccmp;
+ }
+ if (crypto_ops->set_key(key, key_length, NULL, crypto_private) < 0) {
+ res = _FAIL;
+ goto exit_lib80211_ccmp;
+ }
+ if (crypto_ops->decrypt_mpdu(skb, prxattrib->hdrlen, crypto_private)) {
+ res = _FAIL;
+ goto exit_lib80211_ccmp;
+ }
+
+ memmove(pframe, pframe + iv_len, prxattrib->hdrlen);
+ skb_push(skb, iv_len);
+ skb_put(skb, icv_len);
+
+ memcpy(pframe + prxattrib->hdrlen, iv, iv_len);
+ memcpy(pframe + skb->len - icv_len, icv, icv_len);
+
+exit_lib80211_ccmp:
+ if (crypto_ops && crypto_private)
+ crypto_ops->deinit(crypto_private);
} else {
RT_TRACE(_module_rtl871x_security_c_, _drv_err_, ("rtw_aes_encrypt: stainfo==NULL!!!\n"));
res = _FAIL;
diff --git a/drivers/staging/rtl8188eu/core/rtw_sta_mgt.c b/drivers/staging/rtl8188eu/core/rtw_sta_mgt.c
index 2fd2a9e2416e..f42aa4e0ddb8 100644
--- a/drivers/staging/rtl8188eu/core/rtw_sta_mgt.c
+++ b/drivers/staging/rtl8188eu/core/rtw_sta_mgt.c
@@ -194,9 +194,9 @@ struct sta_info *rtw_alloc_stainfo(struct sta_priv *pstapriv, u8 *hwaddr)
_rtw_init_stainfo(psta);
memcpy(psta->hwaddr, hwaddr, ETH_ALEN);
index = wifi_mac_hash(hwaddr);
- RT_TRACE(_module_rtl871x_sta_mgt_c_, _drv_info_, ("rtw_alloc_stainfo: index=%x", index));
+ RT_TRACE(_module_rtl871x_sta_mgt_c_, _drv_info_, ("%s: index=%x", __func__, index));
if (index >= NUM_STA) {
- RT_TRACE(_module_rtl871x_sta_mgt_c_, _drv_err_, ("ERROR => rtw_alloc_stainfo: index >= NUM_STA"));
+ RT_TRACE(_module_rtl871x_sta_mgt_c_, _drv_err_, ("ERROR => %s: index >= NUM_STA", __func__));
psta = NULL;
goto exit;
}
diff --git a/drivers/staging/rtl8188eu/core/rtw_wlan_util.c b/drivers/staging/rtl8188eu/core/rtw_wlan_util.c
index 2db8a5d11c0d..9a130cbf6def 100644
--- a/drivers/staging/rtl8188eu/core/rtw_wlan_util.c
+++ b/drivers/staging/rtl8188eu/core/rtw_wlan_util.c
@@ -715,7 +715,7 @@ void HT_caps_handler(struct adapter *padapter, struct ndis_802_11_var_ie *pIE)
struct ht_priv *phtpriv = &pmlmepriv->htpriv;
u8 *HT_cap = (u8 *)(&pmlmeinfo->HT_caps);
- if (pIE == NULL)
+ if (!pIE)
return;
if (!phtpriv->ht_option)
@@ -755,7 +755,7 @@ void HT_info_handler(struct adapter *padapter, struct ndis_802_11_var_ie *pIE)
struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
struct ht_priv *phtpriv = &pmlmepriv->htpriv;
- if (pIE == NULL)
+ if (!pIE)
return;
if (!phtpriv->ht_option)
@@ -1534,7 +1534,7 @@ int update_sta_support_rate(struct adapter *padapter, u8 *pvar_ie, uint var_ie_l
struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
pIE = (struct ndis_802_11_var_ie *)rtw_get_ie(pvar_ie, _SUPPORTEDRATES_IE_, &ie_len, var_ie_len);
- if (pIE == NULL)
+ if (!pIE)
return _FAIL;
if (ie_len > NDIS_802_11_LENGTH_RATES_EX)
return _FAIL;
diff --git a/drivers/staging/rtl8188eu/core/rtw_xmit.c b/drivers/staging/rtl8188eu/core/rtw_xmit.c
index e8d9858f2942..3c034486346b 100644
--- a/drivers/staging/rtl8188eu/core/rtw_xmit.c
+++ b/drivers/staging/rtl8188eu/core/rtw_xmit.c
@@ -387,34 +387,27 @@ u8 qos_acm(u8 acm_mask, u8 priority)
return change_priority;
}
-static void set_qos(struct pkt_file *ppktfile, struct pkt_attrib *pattrib)
+static void set_qos(struct sk_buff *skb, struct pkt_attrib *pattrib)
{
- struct ethhdr etherhdr;
- struct iphdr ip_hdr;
- s32 user_prio = 0;
-
- _rtw_open_pktfile(ppktfile->pkt, ppktfile);
- _rtw_pktfile_read(ppktfile, (unsigned char *)&etherhdr, ETH_HLEN);
-
- /* get user_prio from IP hdr */
if (pattrib->ether_type == 0x0800) {
- _rtw_pktfile_read(ppktfile, (u8 *)&ip_hdr, sizeof(ip_hdr));
-/* user_prio = (ntohs(ip_hdr.tos) >> 5) & 0x3; */
- user_prio = ip_hdr.tos >> 5;
+ struct iphdr ip_hdr;
+
+ skb_copy_bits(skb, ETH_HLEN, &ip_hdr, sizeof(ip_hdr));
+ pattrib->priority = ip_hdr.tos >> 5;
} else if (pattrib->ether_type == ETH_P_PAE) {
/* "When priority processing of data frames is supported, */
/* a STA's SME should send EAPOL-Key frames at the highest priority." */
- user_prio = 7;
+ pattrib->priority = 7;
+ } else {
+ pattrib->priority = 0;
}
- pattrib->priority = user_prio;
pattrib->hdrlen = WLAN_HDR_A3_QOS_LEN;
pattrib->subtype = WIFI_QOS_DATA_TYPE;
}
static s32 update_attrib(struct adapter *padapter, struct sk_buff *pkt, struct pkt_attrib *pattrib)
{
- struct pkt_file pktfile;
struct sta_info *psta = NULL;
struct ethhdr etherhdr;
@@ -425,9 +418,7 @@ static s32 update_attrib(struct adapter *padapter, struct sk_buff *pkt, struct p
struct qos_priv *pqospriv = &pmlmepriv->qospriv;
int res = _SUCCESS;
-
- _rtw_open_pktfile(pkt, &pktfile);
- _rtw_pktfile_read(&pktfile, (u8 *)&etherhdr, ETH_HLEN);
+ skb_copy_bits(pkt, 0, &etherhdr, ETH_HLEN);
pattrib->ether_type = ntohs(etherhdr.h_proto);
@@ -448,22 +439,23 @@ static s32 update_attrib(struct adapter *padapter, struct sk_buff *pkt, struct p
memcpy(pattrib->ta, get_bssid(pmlmepriv), ETH_ALEN);
}
- pattrib->pktlen = pktfile.pkt_len;
+ pattrib->pktlen = pkt->len - ETH_HLEN;
if (pattrib->ether_type == ETH_P_IP) {
/* The following is for DHCP and ARP packet, we use cck1M to tx these packets and let LPS awake some time */
/* to prevent DHCP protocol fail */
u8 tmp[24];
- _rtw_pktfile_read(&pktfile, &tmp[0], 24);
+ skb_copy_bits(pkt, ETH_HLEN, tmp, 24);
+
pattrib->dhcp_pkt = 0;
- if (pktfile.pkt_len > 282) {/* MINIMUM_DHCP_PACKET_SIZE) { */
+ if (pkt->len > ETH_HLEN + 24 + 282) {/* MINIMUM_DHCP_PACKET_SIZE) { */
if (pattrib->ether_type == ETH_P_IP) {/* IP header */
if (((tmp[21] == 68) && (tmp[23] == 67)) ||
((tmp[21] == 67) && (tmp[23] == 68))) {
/* 68 : UDP BOOTP client */
/* 67 : UDP BOOTP server */
- RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("====================== update_attrib: get DHCP Packet\n"));
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("====================== %s: get DHCP Packet\n", __func__));
/* Use low rate to send DHCP packet. */
pattrib->dhcp_pkt = 1;
}
@@ -487,7 +479,7 @@ static s32 update_attrib(struct adapter *padapter, struct sk_buff *pkt, struct p
psta = rtw_get_bcmc_stainfo(padapter);
} else {
psta = rtw_get_stainfo(pstapriv, pattrib->ra);
- if (psta == NULL) { /* if we cannot get psta => drrp the pkt */
+ if (!psta) { /* if we cannot get psta => drrp the pkt */
RT_TRACE(_module_rtl871x_xmit_c_, _drv_alert_, ("\nupdate_attrib => get sta_info fail, ra: %pM\n", (pattrib->ra)));
res = _FAIL;
goto exit;
@@ -516,10 +508,10 @@ static s32 update_attrib(struct adapter *padapter, struct sk_buff *pkt, struct p
if (check_fwstate(pmlmepriv, WIFI_AP_STATE|WIFI_ADHOC_STATE|WIFI_ADHOC_MASTER_STATE)) {
if (psta->qos_option)
- set_qos(&pktfile, pattrib);
+ set_qos(pkt, pattrib);
} else {
if (pqospriv->qos_option) {
- set_qos(&pktfile, pattrib);
+ set_qos(pkt, pattrib);
if (pmlmepriv->acm_mask != 0)
pattrib->priority = qos_acm(pmlmepriv->acm_mask, pattrib->priority);
@@ -587,16 +579,16 @@ static s32 update_attrib(struct adapter *padapter, struct sk_buff *pkt, struct p
}
RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_,
- ("update_attrib: encrypt=%d\n", pattrib->encrypt));
+ ("%s: encrypt=%d\n", __func__, pattrib->encrypt));
if (pattrib->encrypt && !psecuritypriv->hw_decrypted) {
pattrib->bswenc = true;
RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_,
- ("update_attrib: encrypt=%d bswenc = true\n",
+ ("%s: encrypt=%d bswenc = true\n", __func__,
pattrib->encrypt));
} else {
pattrib->bswenc = false;
- RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, ("update_attrib: bswenc = false\n"));
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, ("%s: bswenc = false\n", __func__));
}
update_attrib_phy_info(pattrib, psta);
@@ -630,7 +622,7 @@ static s32 xmitframe_addmic(struct adapter *padapter, struct xmit_frame *pxmitfr
if (pattrib->encrypt == _TKIP_) {/* if (psecuritypriv->dot11PrivacyAlgrthm == _TKIP_PRIVACY_) */
/* encode mic code */
- if (stainfo != NULL) {
+ if (stainfo) {
u8 null_key[16] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0};
@@ -698,11 +690,11 @@ static s32 xmitframe_addmic(struct adapter *padapter, struct xmit_frame *pxmitfr
}
}
rtw_secgetmic(&micdata, &mic[0]);
- RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("xmitframe_addmic: before add mic code!!!\n"));
- RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("xmitframe_addmic: pattrib->last_txcmdsz=%d!!!\n", pattrib->last_txcmdsz));
- RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("xmitframe_addmic: mic[0]=0x%.2x , mic[1]=0x%.2x , mic[2]= 0x%.2x, mic[3]=0x%.2x\n\
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("%s: before add mic code!!!\n", __func__));
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("%s: pattrib->last_txcmdsz=%d!!!\n", __func__, pattrib->last_txcmdsz));
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("%s: mic[0]=0x%.2x , mic[1]=0x%.2x , mic[2]= 0x%.2x, mic[3]=0x%.2x\n\
mic[4]= 0x%.2x , mic[5]= 0x%.2x , mic[6]= 0x%.2x , mic[7]= 0x%.2x !!!!\n",
- mic[0], mic[1], mic[2], mic[3], mic[4], mic[5], mic[6], mic[7]));
+ __func__, mic[0], mic[1], mic[2], mic[3], mic[4], mic[5], mic[6], mic[7]));
/* add mic code and add the mic code length in last_txcmdsz */
memcpy(payload, &mic[0], 8);
@@ -718,7 +710,7 @@ static s32 xmitframe_addmic(struct adapter *padapter, struct xmit_frame *pxmitfr
*(payload + curfragnum + 4), *(payload + curfragnum + 5),
*(payload + curfragnum + 6), *(payload + curfragnum + 7)));
} else {
- RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("xmitframe_addmic: rtw_get_stainfo==NULL!!!\n"));
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("%s: rtw_get_stainfo==NULL!!!\n", __func__));
}
}
@@ -732,7 +724,7 @@ static s32 xmitframe_swencrypt(struct adapter *padapter, struct xmit_frame *pxmi
if (pattrib->bswenc) {
- RT_TRACE(_module_rtl871x_xmit_c_, _drv_alert_, ("### xmitframe_swencrypt\n"));
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_alert_, ("### %s\n", __func__));
switch (pattrib->encrypt) {
case _WEP40_:
case _WEP104_:
@@ -951,7 +943,6 @@ This sub-routine will perform all the following:
*/
s32 rtw_xmitframe_coalesce(struct adapter *padapter, struct sk_buff *pkt, struct xmit_frame *pxmitframe)
{
- struct pkt_file pktfile;
s32 frg_inx, frg_len, mpdu_len, llc_sz, mem_sz;
size_t addr;
u8 *pframe, *mem_start;
@@ -962,11 +953,11 @@ s32 rtw_xmitframe_coalesce(struct adapter *padapter, struct sk_buff *pkt, struct
u8 *pbuf_start;
s32 bmcst = IS_MCAST(pattrib->ra);
s32 res = _SUCCESS;
-
+ size_t remainder = pkt->len - ETH_HLEN;
psta = rtw_get_stainfo(&padapter->stapriv, pattrib->ra);
- if (psta == NULL)
+ if (!psta)
return _FAIL;
if (pxmitframe->buf_addr == NULL) {
@@ -981,15 +972,12 @@ s32 rtw_xmitframe_coalesce(struct adapter *padapter, struct sk_buff *pkt, struct
mem_start = pbuf_start + hw_hdr_offset;
if (rtw_make_wlanhdr(padapter, mem_start, pattrib) == _FAIL) {
- RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("rtw_xmitframe_coalesce: rtw_make_wlanhdr fail; drop pkt\n"));
- DBG_88E("rtw_xmitframe_coalesce: rtw_make_wlanhdr fail; drop pkt\n");
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("%s: rtw_make_wlanhdr fail; drop pkt\n", __func__));
+ DBG_88E("%s: rtw_make_wlanhdr fail; drop pkt\n", __func__);
res = _FAIL;
goto exit;
}
- _rtw_open_pktfile(pkt, &pktfile);
- _rtw_pktfile_read(&pktfile, NULL, ETH_HLEN);
-
frg_inx = 0;
frg_len = pxmitpriv->frag_len - 4;/* 2346-4 = 2342 */
@@ -1029,8 +1017,8 @@ s32 rtw_xmitframe_coalesce(struct adapter *padapter, struct sk_buff *pkt, struct
memcpy(pframe, pattrib->iv, pattrib->iv_len);
RT_TRACE(_module_rtl871x_xmit_c_, _drv_notice_,
- ("rtw_xmitframe_coalesce: keyid=%d pattrib->iv[3]=%.2x pframe=%.2x %.2x %.2x %.2x\n",
- padapter->securitypriv.dot11PrivacyKeyIndex, pattrib->iv[3], *pframe, *(pframe+1), *(pframe+2), *(pframe+3)));
+ ("%s: keyid=%d pattrib->iv[3]=%.2x pframe=%.2x %.2x %.2x %.2x\n",
+ __func__, padapter->securitypriv.dot11PrivacyKeyIndex, pattrib->iv[3], *pframe, *(pframe+1), *(pframe+2), *(pframe+3)));
pframe += pattrib->iv_len;
@@ -1046,12 +1034,9 @@ s32 rtw_xmitframe_coalesce(struct adapter *padapter, struct sk_buff *pkt, struct
if ((pattrib->icv_len > 0) && (pattrib->bswenc))
mpdu_len -= pattrib->icv_len;
- if (bmcst) {
- /* don't do fragment to broadcat/multicast packets */
- mem_sz = _rtw_pktfile_read(&pktfile, pframe, pattrib->pktlen);
- } else {
- mem_sz = _rtw_pktfile_read(&pktfile, pframe, mpdu_len);
- }
+ mem_sz = min_t(size_t, bmcst ? pattrib->pktlen : mpdu_len, remainder);
+ skb_copy_bits(pkt, pkt->len - remainder, pframe, mem_sz);
+ remainder -= mem_sz;
pframe += mem_sz;
@@ -1062,7 +1047,7 @@ s32 rtw_xmitframe_coalesce(struct adapter *padapter, struct sk_buff *pkt, struct
frg_inx++;
- if (bmcst || pktfile.pkt_len == 0) {
+ if (bmcst || remainder == 0) {
pattrib->nr_frags = frg_inx;
pattrib->last_txcmdsz = pattrib->hdrlen + pattrib->iv_len + ((pattrib->nr_frags == 1) ? llc_sz : 0) +
@@ -1154,7 +1139,7 @@ void rtw_update_protection(struct adapter *padapter, u8 *ie, uint ie_len)
case AUTO_VCS:
default:
perp = rtw_get_ie(ie, _ERPINFO_IE_, &erp_len, ie_len);
- if (perp == NULL) {
+ if (!perp) {
pxmitpriv->vcs = NONE_VCS;
} else {
protection = (*(perp + 2)) & BIT(1);
@@ -1222,7 +1207,7 @@ s32 rtw_free_xmitbuf_ext(struct xmit_priv *pxmitpriv, struct xmit_buf *pxmitbuf)
struct __queue *pfree_queue = &pxmitpriv->free_xmit_extbuf_queue;
- if (pxmitbuf == NULL)
+ if (!pxmitbuf)
return _FAIL;
spin_lock_irqsave(&pfree_queue->lock, irql);
@@ -1268,7 +1253,7 @@ s32 rtw_free_xmitbuf(struct xmit_priv *pxmitpriv, struct xmit_buf *pxmitbuf)
unsigned long irql;
struct __queue *pfree_xmitbuf_queue = &pxmitpriv->free_xmitbuf_queue;
- if (pxmitbuf == NULL)
+ if (!pxmitbuf)
return _FAIL;
if (pxmitbuf->sctx) {
@@ -1359,8 +1344,8 @@ s32 rtw_free_xmitframe(struct xmit_priv *pxmitpriv, struct xmit_frame *pxmitfram
struct sk_buff *pndis_pkt = NULL;
- if (pxmitframe == NULL) {
- RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("====== rtw_free_xmitframe():pxmitframe == NULL!!!!!!!!!!\n"));
+ if (!pxmitframe) {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("====== %s:pxmitframe == NULL!!!!!!!!!!\n", __func__));
goto exit;
}
@@ -1376,7 +1361,7 @@ s32 rtw_free_xmitframe(struct xmit_priv *pxmitpriv, struct xmit_frame *pxmitfram
list_add_tail(&pxmitframe->list, get_list_head(pfree_xmit_queue));
pxmitpriv->free_xmitframe_cnt++;
- RT_TRACE(_module_rtl871x_xmit_c_, _drv_debug_, ("rtw_free_xmitframe():free_xmitframe_cnt=%d\n", pxmitpriv->free_xmitframe_cnt));
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_debug_, ("%s:free_xmitframe_cnt=%d\n", __func__, pxmitpriv->free_xmitframe_cnt));
spin_unlock_bh(&pfree_xmit_queue->lock);
@@ -1415,7 +1400,7 @@ s32 rtw_xmitframe_enqueue(struct adapter *padapter, struct xmit_frame *pxmitfram
{
if (rtw_xmit_classifier(padapter, pxmitframe) == _FAIL) {
RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_,
- ("rtw_xmitframe_enqueue: drop xmit pkt for classifier fail\n"));
+ ("%s: drop xmit pkt for classifier fail\n", __func__));
/* pxmitframe->pkt = NULL; */
return _FAIL;
}
@@ -1505,26 +1490,26 @@ struct tx_servq *rtw_get_sta_pending(struct adapter *padapter, struct sta_info *
case 2:
ptxservq = &psta->sta_xmitpriv.bk_q;
*(ac) = 3;
- RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, ("rtw_get_sta_pending : BK\n"));
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, ("%s : BK\n", __func__));
break;
case 4:
case 5:
ptxservq = &psta->sta_xmitpriv.vi_q;
*(ac) = 1;
- RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, ("rtw_get_sta_pending : VI\n"));
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, ("%s : VI\n", __func__));
break;
case 6:
case 7:
ptxservq = &psta->sta_xmitpriv.vo_q;
*(ac) = 0;
- RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, ("rtw_get_sta_pending : VO\n"));
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, ("%s : VO\n", __func__));
break;
case 0:
case 3:
default:
ptxservq = &psta->sta_xmitpriv.be_q;
*(ac) = 2;
- RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, ("rtw_get_sta_pending : BE\n"));
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, ("%s : BE\n", __func__));
break;
}
@@ -1552,10 +1537,10 @@ s32 rtw_xmit_classifier(struct adapter *padapter, struct xmit_frame *pxmitframe)
else
psta = rtw_get_stainfo(pstapriv, pattrib->ra);
- if (psta == NULL) {
+ if (!psta) {
res = _FAIL;
- DBG_88E("rtw_xmit_classifier: psta == NULL\n");
- RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("rtw_xmit_classifier: psta == NULL\n"));
+ DBG_88E("%s: psta == NULL\n", __func__);
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("%s: psta == NULL\n", __func__));
goto exit;
}
@@ -1660,8 +1645,8 @@ s32 rtw_xmit(struct adapter *padapter, struct sk_buff **ppkt)
s32 res;
pxmitframe = rtw_alloc_xmitframe(pxmitpriv);
- if (pxmitframe == NULL) {
- RT_TRACE(_module_xmit_osdep_c_, _drv_err_, ("rtw_xmit: no more pxmitframe\n"));
+ if (!pxmitframe) {
+ RT_TRACE(_module_xmit_osdep_c_, _drv_err_, ("%s: no more pxmitframe\n", __func__));
DBG_88E("DBG_TX_DROP_FRAME %s no more pxmitframe\n", __func__);
return -1;
}
@@ -1669,7 +1654,7 @@ s32 rtw_xmit(struct adapter *padapter, struct sk_buff **ppkt)
res = update_attrib(padapter, *ppkt, &pxmitframe->attrib);
if (res == _FAIL) {
- RT_TRACE(_module_xmit_osdep_c_, _drv_err_, ("rtw_xmit: update attrib fail\n"));
+ RT_TRACE(_module_xmit_osdep_c_, _drv_err_, ("%s: update attrib fail\n", __func__));
rtw_free_xmitframe(pxmitpriv, pxmitframe);
return -1;
}
@@ -1713,7 +1698,7 @@ int xmitframe_enqueue_for_sleeping_sta(struct adapter *padapter, struct xmit_fra
else
psta = rtw_get_stainfo(pstapriv, pattrib->ra);
- if (psta == NULL)
+ if (!psta)
return ret;
if (pattrib->triggered == 1) {
diff --git a/drivers/staging/rtl8188eu/hal/fw.c b/drivers/staging/rtl8188eu/hal/fw.c
index 03d091bad13a..6b67b38a6a9f 100644
--- a/drivers/staging/rtl8188eu/hal/fw.c
+++ b/drivers/staging/rtl8188eu/hal/fw.c
@@ -30,7 +30,7 @@
#include "rtl8188e_hal.h"
#include <linux/firmware.h>
-#include <linux/kmemleak.h>
+#include <linux/slab.h>
static void _rtl88e_enable_fw_download(struct adapter *adapt, bool enable)
{
diff --git a/drivers/staging/rtl8188eu/hal/rtl8188e_cmd.c b/drivers/staging/rtl8188eu/hal/rtl8188e_cmd.c
index 9b7ba9bffb0d..8d242adae4b3 100644
--- a/drivers/staging/rtl8188eu/hal/rtl8188e_cmd.c
+++ b/drivers/staging/rtl8188eu/hal/rtl8188e_cmd.c
@@ -36,7 +36,7 @@ static u8 _is_fw_read_cmd_down(struct adapter *adapt, u8 msgbox_num)
do {
valid = usb_read8(adapt, REG_HMETFR) & BIT(msgbox_num);
- if (0 == valid)
+ if (valid == 0)
read_down = true;
} while ((!read_down) && (retry_cnts--));
@@ -67,7 +67,8 @@ static s32 FillH2CCmd_88E(struct adapter *adapt, u8 ElementID, u32 CmdLen, u8 *p
if (!adapt->bFWReady) {
- DBG_88E("FillH2CCmd_88E(): return H2C cmd because fw is not ready\n");
+ DBG_88E("%s(): return H2C cmd because fw is not ready\n",
+ __func__);
return ret;
}
diff --git a/drivers/staging/rtl8188eu/hal/rtl8188e_dm.c b/drivers/staging/rtl8188eu/hal/rtl8188e_dm.c
index d04b7fbb71e1..ff227c8b98ca 100644
--- a/drivers/staging/rtl8188eu/hal/rtl8188e_dm.c
+++ b/drivers/staging/rtl8188eu/hal/rtl8188e_dm.c
@@ -182,7 +182,7 @@ void rtw_hal_dm_init(struct adapter *Adapter)
/* Compare RSSI for deciding antenna */
void rtw_hal_antdiv_rssi_compared(struct adapter *Adapter, struct wlan_bssid_ex *dst, struct wlan_bssid_ex *src)
{
- if (0 != Adapter->HalData->AntDivCfg) {
+ if (Adapter->HalData->AntDivCfg != 0) {
/* select optimum_antenna for before linked =>For antenna diversity */
if (dst->Rssi >= src->Rssi) {/* keep org parameter */
src->Rssi = dst->Rssi;
diff --git a/drivers/staging/rtl8188eu/hal/rtl8188e_hal_init.c b/drivers/staging/rtl8188eu/hal/rtl8188e_hal_init.c
index 3673f573ac3d..54ede4baa0c9 100644
--- a/drivers/staging/rtl8188eu/hal/rtl8188e_hal_init.c
+++ b/drivers/staging/rtl8188eu/hal/rtl8188e_hal_init.c
@@ -221,13 +221,13 @@ s32 InitLLTTable(struct adapter *padapter, u8 txpktbuf_bndy)
} else {
for (i = 0; i < (txpktbuf_bndy - 1); i++) {
status = _LLTWrite(padapter, i, i + 1);
- if (_SUCCESS != status)
+ if (status != _SUCCESS)
return status;
}
/* end of list */
status = _LLTWrite(padapter, (txpktbuf_bndy - 1), 0xFF);
- if (_SUCCESS != status)
+ if (status != _SUCCESS)
return status;
/* Make the other pages as ring buffer */
@@ -235,13 +235,13 @@ s32 InitLLTTable(struct adapter *padapter, u8 txpktbuf_bndy)
/* Otherwise used as local loopback buffer. */
for (i = txpktbuf_bndy; i < Last_Entry_Of_TxPktBuf; i++) {
status = _LLTWrite(padapter, i, (i + 1));
- if (_SUCCESS != status)
+ if (status != _SUCCESS)
return status;
}
/* Let last entry point to the start entry of ring buffer */
status = _LLTWrite(padapter, Last_Entry_Of_TxPktBuf, txpktbuf_bndy);
- if (_SUCCESS != status) {
+ if (status != _SUCCESS) {
return status;
}
}
diff --git a/drivers/staging/rtl8188eu/hal/rtl8188eu_xmit.c b/drivers/staging/rtl8188eu/hal/rtl8188eu_xmit.c
index a9912b60eb59..4f0f512f303c 100644
--- a/drivers/staging/rtl8188eu/hal/rtl8188eu_xmit.c
+++ b/drivers/staging/rtl8188eu/hal/rtl8188eu_xmit.c
@@ -552,7 +552,7 @@ s32 rtl8188eu_xmitframe_complete(struct adapter *adapt, struct xmit_priv *pxmitp
pbuf = round_up(pbuf_tail, 8);
pfirstframe->agg_num++;
- if (MAX_TX_AGG_PACKET_NUMBER == pfirstframe->agg_num)
+ if (pfirstframe->agg_num == MAX_TX_AGG_PACKET_NUMBER)
break;
if (pbuf < bulkptr) {
diff --git a/drivers/staging/rtl8188eu/hal/usb_halinit.c b/drivers/staging/rtl8188eu/hal/usb_halinit.c
index 17967c944946..c3bb183aba38 100644
--- a/drivers/staging/rtl8188eu/hal/usb_halinit.c
+++ b/drivers/staging/rtl8188eu/hal/usb_halinit.c
@@ -1516,8 +1516,8 @@ void rtw_hal_set_hwreg(struct adapter *Adapter, u8 variable, u8 *val)
case HW_VAR_CAM_WRITE:
{
u32 cmd;
-
u32 *cam_val = (u32 *)val;
+
usb_write32(Adapter, WCAMI, cam_val[0]);
cmd = CAM_POLLINIG | CAM_WRITE | cam_val[1];
diff --git a/drivers/staging/rtl8188eu/include/rtw_security.h b/drivers/staging/rtl8188eu/include/rtw_security.h
index a0c6cf706218..b1883ca852af 100644
--- a/drivers/staging/rtl8188eu/include/rtw_security.h
+++ b/drivers/staging/rtl8188eu/include/rtw_security.h
@@ -308,6 +308,6 @@ u32 rtw_tkip_encrypt(struct adapter *padapter, u8 *pxmitframe);
void rtw_wep_encrypt(struct adapter *padapter, u8 *pxmitframe);
u32 rtw_aes_decrypt(struct adapter *padapter, u8 *precvframe);
u32 rtw_tkip_decrypt(struct adapter *padapter, u8 *precvframe);
-void rtw_wep_decrypt(struct adapter *padapter, u8 *precvframe);
+int rtw_wep_decrypt(struct adapter *padapter, u8 *precvframe);
#endif /* __RTL871X_SECURITY_H_ */
diff --git a/drivers/staging/rtl8188eu/include/xmit_osdep.h b/drivers/staging/rtl8188eu/include/xmit_osdep.h
index 959ef4b3066c..00ebad88f0d1 100644
--- a/drivers/staging/rtl8188eu/include/xmit_osdep.h
+++ b/drivers/staging/rtl8188eu/include/xmit_osdep.h
@@ -18,15 +18,6 @@
#include <osdep_service.h>
#include <drv_types.h>
-struct pkt_file {
- struct sk_buff *pkt;
- size_t pkt_len; /* the remainder length of the open_file */
- unsigned char *cur_buffer;
- u8 *buf_start;
- u8 *cur_addr;
- size_t buf_len;
-};
-
#define NR_XMITFRAME 256
struct xmit_priv;
@@ -43,10 +34,6 @@ int rtw_os_xmit_resource_alloc(struct adapter *padapter,
struct xmit_buf *pxmitbuf, u32 alloc_sz);
void rtw_os_xmit_resource_free(struct xmit_buf *pxmitbuf);
-uint rtw_remainder_len(struct pkt_file *pfile);
-void _rtw_open_pktfile(struct sk_buff *pkt, struct pkt_file *pfile);
-uint _rtw_pktfile_read(struct pkt_file *pfile, u8 *rmem, uint rlen);
-
void rtw_os_pkt_complete(struct adapter *padapter, struct sk_buff *pkt);
void rtw_os_xmit_complete(struct adapter *padapter,
struct xmit_frame *pxframe);
diff --git a/drivers/staging/rtl8188eu/os_dep/usb_intf.c b/drivers/staging/rtl8188eu/os_dep/usb_intf.c
index 32c7225a831e..127ecf896fc9 100644
--- a/drivers/staging/rtl8188eu/os_dep/usb_intf.c
+++ b/drivers/staging/rtl8188eu/os_dep/usb_intf.c
@@ -333,7 +333,7 @@ static struct adapter *rtw_usb_if1_init(struct dvobj_priv *dvobj,
struct net_device *pmondev;
int status = _FAIL;
- padapter = (struct adapter *)vzalloc(sizeof(*padapter));
+ padapter = vzalloc(sizeof(*padapter));
if (padapter == NULL)
goto exit;
padapter->dvobj = dvobj;
diff --git a/drivers/staging/rtl8188eu/os_dep/xmit_linux.c b/drivers/staging/rtl8188eu/os_dep/xmit_linux.c
index 8bf8248e4ac7..8ac9567c954d 100644
--- a/drivers/staging/rtl8188eu/os_dep/xmit_linux.c
+++ b/drivers/staging/rtl8188eu/os_dep/xmit_linux.c
@@ -22,43 +22,6 @@
#include <xmit_osdep.h>
#include <osdep_intf.h>
-uint rtw_remainder_len(struct pkt_file *pfile)
-{
- return pfile->buf_len - ((size_t)(pfile->cur_addr) -
- (size_t)(pfile->buf_start));
-}
-
-void _rtw_open_pktfile(struct sk_buff *pktptr, struct pkt_file *pfile)
-{
-
- pfile->pkt = pktptr;
- pfile->cur_addr = pktptr->data;
- pfile->buf_start = pktptr->data;
- pfile->pkt_len = pktptr->len;
- pfile->buf_len = pktptr->len;
-
- pfile->cur_buffer = pfile->buf_start;
-
-}
-
-uint _rtw_pktfile_read(struct pkt_file *pfile, u8 *rmem, uint rlen)
-{
- uint len = 0;
-
-
- len = rtw_remainder_len(pfile);
- len = min(rlen, len);
-
- if (rmem)
- skb_copy_bits(pfile->pkt, pfile->buf_len-pfile->pkt_len, rmem, len);
-
- pfile->cur_addr += len;
- pfile->pkt_len -= len;
-
-
- return len;
-}
-
int rtw_os_xmit_resource_alloc(struct adapter *padapter, struct xmit_buf *pxmitbuf, u32 alloc_sz)
{
int i;
diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_wx.c b/drivers/staging/rtl8192e/rtl8192e/rtl_wx.c
index f802f60281f8..843e874b8a06 100644
--- a/drivers/staging/rtl8192e/rtl8192e/rtl_wx.c
+++ b/drivers/staging/rtl8192e/rtl8192e/rtl_wx.c
@@ -36,7 +36,6 @@ static int _rtl92e_wx_get_freq(struct net_device *dev,
return rtllib_wx_get_freq(priv->rtllib, a, wrqu, b);
}
-
static int _rtl92e_wx_get_mode(struct net_device *dev,
struct iw_request_info *a,
union iwreq_data *wrqu, char *b)
@@ -149,7 +148,6 @@ static int _rtl92e_wx_set_rawtx(struct net_device *dev,
mutex_unlock(&priv->wx_mutex);
return ret;
-
}
static int _rtl92e_wx_force_reset(struct net_device *dev,
@@ -165,7 +163,6 @@ static int _rtl92e_wx_force_reset(struct net_device *dev,
priv->force_reset = *extra;
mutex_unlock(&priv->wx_mutex);
return 0;
-
}
static int _rtl92e_wx_adapter_power_status(struct net_device *dev,
@@ -174,7 +171,7 @@ static int _rtl92e_wx_adapter_power_status(struct net_device *dev,
{
struct r8192_priv *priv = rtllib_priv(dev);
struct rt_pwr_save_ctrl *pPSC = (struct rt_pwr_save_ctrl *)
- (&(priv->rtllib->PowerSaveControl));
+ (&priv->rtllib->PowerSaveControl);
struct rtllib_device *ieee = priv->rtllib;
mutex_lock(&priv->wx_mutex);
@@ -205,7 +202,7 @@ static int _rtl92e_wx_set_lps_awake_interval(struct net_device *dev,
{
struct r8192_priv *priv = rtllib_priv(dev);
struct rt_pwr_save_ctrl *pPSC = (struct rt_pwr_save_ctrl *)
- (&(priv->rtllib->PowerSaveControl));
+ (&priv->rtllib->PowerSaveControl);
mutex_lock(&priv->wx_mutex);
@@ -231,7 +228,6 @@ static int _rtl92e_wx_set_force_lps(struct net_device *dev,
priv->force_lps = *extra;
mutex_unlock(&priv->wx_mutex);
return 0;
-
}
static int _rtl92e_wx_set_debug(struct net_device *dev,
@@ -247,7 +243,7 @@ static int _rtl92e_wx_set_debug(struct net_device *dev,
netdev_info(dev, "=====>%s(), *extra:%x, debugflag:%x\n", __func__,
*extra, rt_global_debug_component);
if (c > 0)
- rt_global_debug_component |= (1<<c);
+ rt_global_debug_component |= (1 << c);
else
rt_global_debug_component &= BIT31;
return 0;
@@ -356,7 +352,7 @@ static int _rtl92e_wx_get_range(struct net_device *dev,
range->min_pmp = 0;
range->max_pmp = 5000000;
range->min_pmt = 0;
- range->max_pmt = 65535*1000;
+ range->max_pmt = 65535 * 1000;
range->pmp_flags = IW_POWER_PERIOD;
range->pmt_flags = IW_POWER_TIMEOUT;
range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R;
@@ -364,7 +360,7 @@ static int _rtl92e_wx_get_range(struct net_device *dev,
range->we_version_source = 18;
for (i = 0, val = 0; i < 14; i++) {
- if ((priv->rtllib->active_channel_map)[i+1]) {
+ if ((priv->rtllib->active_channel_map)[i + 1]) {
range->freq[val].i = i + 1;
range->freq[val].m = rtllib_wlan_frequencies[i] *
100000;
@@ -377,8 +373,8 @@ static int _rtl92e_wx_get_range(struct net_device *dev,
}
range->num_frequency = val;
range->num_channels = val;
- range->enc_capa = IW_ENC_CAPA_WPA|IW_ENC_CAPA_WPA2|
- IW_ENC_CAPA_CIPHER_TKIP|IW_ENC_CAPA_CIPHER_CCMP;
+ range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
+ IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
range->scan_capa = IW_SCAN_CAPA_ESSID | IW_SCAN_CAPA_TYPE;
/* Event capability (kernel + driver) */
@@ -475,12 +471,10 @@ static int _rtl92e_wx_set_scan(struct net_device *dev,
return ret;
}
-
static int _rtl92e_wx_get_scan(struct net_device *dev,
struct iw_request_info *a,
union iwreq_data *wrqu, char *b)
{
-
int ret;
struct r8192_priv *priv = rtllib_priv(dev);
@@ -490,7 +484,6 @@ static int _rtl92e_wx_get_scan(struct net_device *dev,
if (priv->bHwRadioOff)
return 0;
-
mutex_lock(&priv->wx_mutex);
ret = rtllib_wx_get_scan(priv->rtllib, a, wrqu, b);
@@ -552,7 +545,6 @@ static int _rtl92e_wx_set_nick(struct net_device *dev,
memcpy(priv->nick, extra, wrqu->data.length);
mutex_unlock(&priv->wx_mutex);
return 0;
-
}
static int _rtl92e_wx_get_nick(struct net_device *dev,
@@ -596,7 +588,6 @@ static int _rtl92e_wx_get_name(struct net_device *dev,
return rtllib_wx_get_name(priv->rtllib, info, wrqu, extra);
}
-
static int _rtl92e_wx_set_frag(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
@@ -619,7 +610,6 @@ static int _rtl92e_wx_set_frag(struct net_device *dev,
return 0;
}
-
static int _rtl92e_wx_get_frag(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
@@ -633,7 +623,6 @@ static int _rtl92e_wx_get_frag(struct net_device *dev,
return 0;
}
-
static int _rtl92e_wx_set_wap(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *awrq, char *extra)
@@ -651,10 +640,8 @@ static int _rtl92e_wx_set_wap(struct net_device *dev,
mutex_unlock(&priv->wx_mutex);
return ret;
-
}
-
static int _rtl92e_wx_get_wap(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
@@ -664,7 +651,6 @@ static int _rtl92e_wx_get_wap(struct net_device *dev,
return rtllib_wx_get_wap(priv->rtllib, info, wrqu, extra);
}
-
static int _rtl92e_wx_get_enc(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *key)
@@ -707,7 +693,6 @@ static int _rtl92e_wx_set_enc(struct net_device *dev,
ret = rtllib_wx_set_encode(priv->rtllib, info, wrqu, key);
mutex_unlock(&priv->wx_mutex);
-
if (wrqu->encoding.flags & IW_ENCODE_DISABLED) {
ieee->pairwise_key_type = ieee->group_key_type = KEY_TYPE_NA;
rtl92e_cam_reset(dev);
@@ -716,9 +701,8 @@ static int _rtl92e_wx_set_enc(struct net_device *dev,
goto end_hw_sec;
}
if (wrqu->encoding.length != 0) {
-
for (i = 0; i < 4; i++) {
- hwkey[i] |= key[4*i+0]&mask;
+ hwkey[i] |= key[4 * i + 0] & mask;
if (i == 1 && (4 * i + 1) == wrqu->encoding.length)
mask = 0x00;
if (i == 3 && (4 * i + 1) == wrqu->encoding.length)
@@ -786,8 +770,6 @@ static int _rtl92e_wx_set_scan_type(struct net_device *dev,
return 1;
}
-
-
#define R8192_MAX_RETRY 255
static int _rtl92e_wx_set_retry(struct net_device *dev,
struct iw_request_info *info,
@@ -833,7 +815,6 @@ static int _rtl92e_wx_get_retry(struct net_device *dev,
{
struct r8192_priv *priv = rtllib_priv(dev);
-
wrqu->retry.disabled = 0; /* can't be disabled */
if ((wrqu->retry.flags & IW_RETRY_TYPE) ==
@@ -862,12 +843,10 @@ static int _rtl92e_wx_get_sens(struct net_device *dev,
return 0;
}
-
static int _rtl92e_wx_set_sens(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
-
struct r8192_priv *priv = rtllib_priv(dev);
short err = 0;
@@ -963,15 +942,12 @@ static int _rtl92e_wx_set_encode_ext(struct net_device *dev,
rtl92e_set_swcam(dev, 4, idx, alg,
(u8 *)ieee->ap_mac_addr, 0, key, 0);
}
-
-
}
end_hw_sec:
priv->rtllib->wx_set_enc = 0;
mutex_unlock(&priv->wx_mutex);
return ret;
-
}
static int _rtl92e_wx_set_auth(struct net_device *dev,
@@ -986,7 +962,7 @@ static int _rtl92e_wx_set_auth(struct net_device *dev,
return 0;
mutex_lock(&priv->wx_mutex);
- ret = rtllib_wx_set_auth(priv->rtllib, info, &(data->param), extra);
+ ret = rtllib_wx_set_auth(priv->rtllib, info, &data->param, extra);
mutex_unlock(&priv->wx_mutex);
return ret;
}
@@ -995,7 +971,6 @@ static int _rtl92e_wx_set_mlme(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
-
int ret = 0;
struct r8192_priv *priv = rtllib_priv(dev);
@@ -1089,7 +1064,6 @@ static int _rtl92e_wx_set_promisc_mode(struct net_device *dev,
return 0;
}
-
static int _rtl92e_wx_get_promisc_mode(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
@@ -1109,7 +1083,6 @@ static int _rtl92e_wx_get_promisc_mode(struct net_device *dev,
return 0;
}
-
#define IW_IOCTL(x) ((x) - SIOCSIWCOMMIT)
static iw_handler r8192_wx_handlers[] = {
[IW_IOCTL(SIOCGIWNAME)] = _rtl92e_wx_get_name,
@@ -1166,15 +1139,15 @@ static const struct iw_priv_args r8192_private_args[] = {
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "forcereset"
}, {
SIOCIWFIRSTPRIV + 0x6,
- IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED|1, IW_PRIV_TYPE_NONE,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, IW_PRIV_TYPE_NONE,
"set_power"
}, {
SIOCIWFIRSTPRIV + 0xa,
- IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED|1, IW_PRIV_TYPE_NONE,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, IW_PRIV_TYPE_NONE,
"lps_interv"
}, {
SIOCIWFIRSTPRIV + 0xb,
- IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED|1, IW_PRIV_TYPE_NONE,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, IW_PRIV_TYPE_NONE,
"lps_force"
}, {
SIOCIWFIRSTPRIV + 0x16,
diff --git a/drivers/staging/rtl8192e/rtl819x_BAProc.c b/drivers/staging/rtl8192e/rtl819x_BAProc.c
index eb6d841f7c45..c466a5e7e3bd 100644
--- a/drivers/staging/rtl8192e/rtl819x_BAProc.c
+++ b/drivers/staging/rtl8192e/rtl819x_BAProc.c
@@ -76,7 +76,7 @@ static struct sk_buff *rtllib_ADDBA(struct rtllib_device *ieee, u8 *Dst,
u16 StatusCode, u8 type)
{
struct sk_buff *skb = NULL;
- struct rtllib_hdr_3addr *BAReq = NULL;
+ struct rtllib_hdr_3addr *BAReq = NULL;
u8 *tag = NULL;
u16 len = ieee->tx_headroom + 9;
diff --git a/drivers/staging/rtl8192e/rtllib_rx.c b/drivers/staging/rtl8192e/rtllib_rx.c
index 30f72d220af1..fa580ce1cf43 100644
--- a/drivers/staging/rtl8192e/rtllib_rx.c
+++ b/drivers/staging/rtl8192e/rtllib_rx.c
@@ -2644,8 +2644,8 @@ static inline void rtllib_process_probe_response(
(network->ssid_len ? 1 : 0))) {
update_network(ieee, &ieee->current_network, network);
if ((ieee->current_network.mode == IEEE_N_24G ||
- ieee->current_network.mode == IEEE_G)
- && ieee->current_network.berp_info_valid) {
+ ieee->current_network.mode == IEEE_G) &&
+ ieee->current_network.berp_info_valid) {
if (ieee->current_network.erp_value & ERP_UseProtection)
ieee->current_network.buseprotection = true;
else
diff --git a/drivers/staging/rtl8192e/rtllib_wx.c b/drivers/staging/rtl8192e/rtllib_wx.c
index 03fbff067fa4..74d4d2df3eb3 100644
--- a/drivers/staging/rtl8192e/rtllib_wx.c
+++ b/drivers/staging/rtl8192e/rtllib_wx.c
@@ -371,8 +371,7 @@ int rtllib_wx_set_encode(struct rtllib_device *ieee,
struct lib80211_crypt_data *new_crypt;
/* take WEP into use */
- new_crypt = kzalloc(sizeof(struct lib80211_crypt_data),
- GFP_KERNEL);
+ new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
if (new_crypt == NULL)
return -ENOMEM;
new_crypt->ops = lib80211_get_crypto_ops("R-WEP");
diff --git a/drivers/staging/rtl8192u/ieee80211/ieee80211_crypt_ccmp.c b/drivers/staging/rtl8192u/ieee80211/ieee80211_crypt_ccmp.c
index e6648f7723ce..a4b40422e5bd 100644
--- a/drivers/staging/rtl8192u/ieee80211/ieee80211_crypt_ccmp.c
+++ b/drivers/staging/rtl8192u/ieee80211/ieee80211_crypt_ccmp.c
@@ -73,7 +73,7 @@ static void *ieee80211_ccmp_init(int key_idx)
priv->tfm = (void *)crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(priv->tfm)) {
- printk(KERN_DEBUG "ieee80211_crypt_ccmp: could not allocate crypto API aes\n");
+ pr_debug("ieee80211_crypt_ccmp: could not allocate crypto API aes\n");
priv->tfm = NULL;
goto fail;
}
@@ -276,22 +276,22 @@ static int ieee80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
keyidx = pos[3];
if (!(keyidx & (1 << 5))) {
if (net_ratelimit()) {
- printk(KERN_DEBUG "CCMP: received packet without ExtIV flag from %pM\n",
- hdr->addr2);
+ netdev_dbg(skb->dev, "CCMP: received packet without ExtIV flag from %pM\n",
+ hdr->addr2);
}
key->dot11RSNAStatsCCMPFormatErrors++;
return -2;
}
keyidx >>= 6;
if (key->key_idx != keyidx) {
- printk(KERN_DEBUG "CCMP: RX tkey->key_idx=%d frame keyidx=%d priv=%p\n",
- key->key_idx, keyidx, priv);
+ netdev_dbg(skb->dev, "CCMP: RX tkey->key_idx=%d frame keyidx=%d priv=%p\n",
+ key->key_idx, keyidx, priv);
return -6;
}
if (!key->key_set) {
if (net_ratelimit()) {
- printk(KERN_DEBUG "CCMP: received packet from %pM with keyid=%d that does not have a configured key\n",
- hdr->addr2, keyidx);
+ netdev_dbg(skb->dev, "CCMP: received packet from %pM with keyid=%d that does not have a configured key\n",
+ hdr->addr2, keyidx);
}
return -3;
}
@@ -306,8 +306,8 @@ static int ieee80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
if (memcmp(pn, key->rx_pn, CCMP_PN_LEN) <= 0) {
if (net_ratelimit()) {
- printk(KERN_DEBUG "CCMP: replay detected: STA=%pM previous PN %pm received PN %pm\n",
- hdr->addr2, key->rx_pn, pn);
+ netdev_dbg(skb->dev, "CCMP: replay detected: STA=%pM previous PN %pm received PN %pm\n",
+ hdr->addr2, key->rx_pn, pn);
}
key->dot11RSNAStatsCCMPReplays++;
return -4;
@@ -341,8 +341,8 @@ static int ieee80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
if (memcmp(mic, a, CCMP_MIC_LEN) != 0) {
if (net_ratelimit()) {
- printk(KERN_DEBUG "CCMP: decrypt failed: STA=%pM\n",
- hdr->addr2);
+ netdev_dbg(skb->dev, "CCMP: decrypt failed: STA=%pM\n",
+ hdr->addr2);
}
key->dot11RSNAStatsCCMPDecryptErrors++;
return -5;
diff --git a/drivers/staging/rtl8192u/ieee80211/rtl819x_BAProc.c b/drivers/staging/rtl8192u/ieee80211/rtl819x_BAProc.c
index 21b55fd5b717..86c73570e88a 100644
--- a/drivers/staging/rtl8192u/ieee80211/rtl819x_BAProc.c
+++ b/drivers/staging/rtl8192u/ieee80211/rtl819x_BAProc.c
@@ -195,7 +195,9 @@ static struct sk_buff *ieee80211_DELBA(
u16 len = 6 + ieee->tx_headroom;
if (net_ratelimit())
- IEEE80211_DEBUG(IEEE80211_DL_TRACE | IEEE80211_DL_BA, "========>%s(), ReasonCode(%d) sentd to:%pM\n", __func__, ReasonCode, dst);
+ IEEE80211_DEBUG(IEEE80211_DL_TRACE | IEEE80211_DL_BA,
+ "========>%s(), ReasonCode(%d) sentd to:%pM\n",
+ __func__, ReasonCode, dst);
memset(&DelbaParamSet, 0, 2);
@@ -233,7 +235,8 @@ static struct sk_buff *ieee80211_DELBA(
IEEE80211_DEBUG_DATA(IEEE80211_DL_DATA|IEEE80211_DL_BA, skb->data, skb->len);
if (net_ratelimit())
- IEEE80211_DEBUG(IEEE80211_DL_TRACE | IEEE80211_DL_BA, "<=====%s()\n", __func__);
+ IEEE80211_DEBUG(IEEE80211_DL_TRACE | IEEE80211_DL_BA,
+ "<=====%s()\n", __func__);
return skb;
}
diff --git a/drivers/staging/rtl8192u/r8192U_core.c b/drivers/staging/rtl8192u/r8192U_core.c
index 3c300f7b6a62..d607c59761cf 100644
--- a/drivers/staging/rtl8192u/r8192U_core.c
+++ b/drivers/staging/rtl8192u/r8192U_core.c
@@ -1706,6 +1706,8 @@ static short rtl8192_usb_initendpoints(struct net_device *dev)
priv->rx_urb[16] = usb_alloc_urb(0, GFP_KERNEL);
priv->oldaddr = kmalloc(16, GFP_KERNEL);
+ if (!priv->oldaddr)
+ return -ENOMEM;
oldaddr = priv->oldaddr;
align = ((long)oldaddr) & 3;
if (align) {
diff --git a/drivers/staging/rtl8712/drv_types.h b/drivers/staging/rtl8712/drv_types.h
index ae79047ac6dc..ede99e96984f 100644
--- a/drivers/staging/rtl8712/drv_types.h
+++ b/drivers/staging/rtl8712/drv_types.h
@@ -161,7 +161,7 @@ struct _adapter {
u8 EepromAddressSize;
u8 hw_init_completed;
struct task_struct *cmdThread;
- pid_t evtThread;
+ pid_t evtThread;
struct task_struct *xmitThread;
pid_t recvThread;
uint (*dvobj_init)(struct _adapter *adapter);
diff --git a/drivers/staging/rtl8712/ieee80211.c b/drivers/staging/rtl8712/ieee80211.c
index 987270395635..7a4c00e49a88 100644
--- a/drivers/staging/rtl8712/ieee80211.c
+++ b/drivers/staging/rtl8712/ieee80211.c
@@ -107,7 +107,7 @@ u8 *r8712_set_ie(u8 *pbuf, sint index, uint len, u8 *source, uint *frlen)
* index: the information element id index, limit is the limit for search
* ---------------------------------------------------------------------------
*/
-u8 *r8712_get_ie(u8 *pbuf, sint index, sint *len, sint limit)
+u8 *r8712_get_ie(u8 *pbuf, sint index, uint *len, sint limit)
{
sint tmp, i;
u8 *p;
@@ -166,7 +166,8 @@ static uint r8712_get_rateset_len(u8 *rateset)
int r8712_generate_ie(struct registry_priv *pregistrypriv)
{
- int sz = 0, rate_len;
+ int rate_len;
+ uint sz = 0;
struct wlan_bssid_ex *pdev_network = &pregistrypriv->dev_network;
u8 *ie = pdev_network->IEs;
u16 beaconPeriod = (u16)pdev_network->Configuration.BeaconPeriod;
@@ -211,9 +212,9 @@ int r8712_generate_ie(struct registry_priv *pregistrypriv)
return sz;
}
-unsigned char *r8712_get_wpa_ie(unsigned char *pie, int *wpa_ie_len, int limit)
+unsigned char *r8712_get_wpa_ie(unsigned char *pie, uint *wpa_ie_len, int limit)
{
- int len;
+ u32 len;
u16 val16;
unsigned char wpa_oui_type[] = {0x00, 0x50, 0xf2, 0x01};
u8 *pbuf = pie;
@@ -245,7 +246,7 @@ check_next_ie:
return NULL;
}
-unsigned char *r8712_get_wpa2_ie(unsigned char *pie, int *rsn_ie_len, int limit)
+unsigned char *r8712_get_wpa2_ie(unsigned char *pie, uint *rsn_ie_len, int limit)
{
return r8712_get_ie(pie, _WPA2_IE_ID_, rsn_ie_len, limit);
}
diff --git a/drivers/staging/rtl8712/ieee80211.h b/drivers/staging/rtl8712/ieee80211.h
index 68fd65e80906..d605dfd02200 100644
--- a/drivers/staging/rtl8712/ieee80211.h
+++ b/drivers/staging/rtl8712/ieee80211.h
@@ -738,9 +738,9 @@ static inline int ieee80211_get_hdrlen(u16 fc)
struct registry_priv;
u8 *r8712_set_ie(u8 *pbuf, sint index, uint len, u8 *source, uint *frlen);
-u8 *r8712_get_ie(u8 *pbuf, sint index, sint *len, sint limit);
-unsigned char *r8712_get_wpa_ie(unsigned char *pie, int *rsn_ie_len, int limit);
-unsigned char *r8712_get_wpa2_ie(unsigned char *pie, int *rsn_ie_len,
+u8 *r8712_get_ie(u8 *pbuf, sint index, uint *len, sint limit);
+unsigned char *r8712_get_wpa_ie(unsigned char *pie, uint *rsn_ie_len, int limit);
+unsigned char *r8712_get_wpa2_ie(unsigned char *pie, uint *rsn_ie_len,
int limit);
int r8712_parse_wpa_ie(u8 *wpa_ie, int wpa_ie_len, int *group_cipher,
int *pairwise_cipher);
diff --git a/drivers/staging/rtl8712/mlme_linux.c b/drivers/staging/rtl8712/mlme_linux.c
index 3c7c4a4faeb2..baaa52f04560 100644
--- a/drivers/staging/rtl8712/mlme_linux.c
+++ b/drivers/staging/rtl8712/mlme_linux.c
@@ -36,7 +36,7 @@ static void sitesurvey_ctrl_handler(struct timer_list *t)
{
struct _adapter *adapter =
from_timer(adapter, t,
- mlmepriv.sitesurveyctrl.sitesurvey_ctrl_timer);
+ mlmepriv.sitesurveyctrl.sitesurvey_ctrl_timer);
_r8712_sitesurvey_ctrl_handler(adapter);
mod_timer(&adapter->mlmepriv.sitesurveyctrl.sitesurvey_ctrl_timer,
diff --git a/drivers/staging/rtl8712/os_intfs.c b/drivers/staging/rtl8712/os_intfs.c
index e7df5d7986fc..ff4e451c10f9 100644
--- a/drivers/staging/rtl8712/os_intfs.c
+++ b/drivers/staging/rtl8712/os_intfs.c
@@ -230,7 +230,7 @@ struct net_device *r8712_init_netdev(void)
static u32 start_drv_threads(struct _adapter *padapter)
{
padapter->cmdThread = kthread_run(r8712_cmd_thread, padapter, "%s",
- padapter->pnetdev->name);
+ padapter->pnetdev->name);
if (IS_ERR(padapter->cmdThread))
return _FAIL;
return _SUCCESS;
@@ -347,7 +347,6 @@ u8 r8712_free_drv_sw(struct _adapter *padapter)
return _SUCCESS;
}
-
static void enable_video_mode(struct _adapter *padapter, int cbw40_value)
{
/* bit 8:
diff --git a/drivers/staging/rtl8712/recv_linux.c b/drivers/staging/rtl8712/recv_linux.c
index 986a55bb9877..8cf4286f6318 100644
--- a/drivers/staging/rtl8712/recv_linux.c
+++ b/drivers/staging/rtl8712/recv_linux.c
@@ -111,8 +111,8 @@ void r8712_recv_indicatepkt(struct _adapter *padapter,
_pkt *skb;
struct rx_pkt_attrib *pattrib = &precv_frame->u.hdr.attrib;
- precvpriv = &(padapter->recvpriv);
- pfree_recv_queue = &(precvpriv->free_recv_queue);
+ precvpriv = &padapter->recvpriv;
+ pfree_recv_queue = &precvpriv->free_recv_queue;
skb = precv_frame->u.hdr.pkt;
if (!skb)
goto _recv_indicatepkt_drop;
diff --git a/drivers/staging/rtl8712/rtl8712_bitdef.h b/drivers/staging/rtl8712/rtl8712_bitdef.h
index bff57a8eef3c..dee35fe2587a 100644
--- a/drivers/staging/rtl8712/rtl8712_bitdef.h
+++ b/drivers/staging/rtl8712/rtl8712_bitdef.h
@@ -18,7 +18,6 @@
*
******************************************************************************/
-
#ifndef __RTL8712_BITDEF_H__
#define __RTL8712_BITDEF_H__
diff --git a/drivers/staging/rtl8712/rtl8712_cmd.c b/drivers/staging/rtl8712/rtl8712_cmd.c
index 9c8e0c50a804..b1dfe9f46619 100644
--- a/drivers/staging/rtl8712/rtl8712_cmd.c
+++ b/drivers/staging/rtl8712/rtl8712_cmd.c
@@ -320,7 +320,7 @@ int r8712_cmd_thread(void *context)
struct tx_desc *pdesc;
void (*pcmd_callback)(struct _adapter *dev, struct cmd_obj *pcmd);
struct _adapter *padapter = context;
- struct cmd_priv *pcmdpriv = &(padapter->cmdpriv);
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
struct completion *cmd_queue_comp =
&pcmdpriv->cmd_queue_comp;
struct mutex *pwctrl_lock = &padapter->pwrctrlpriv.mutex_lock;
@@ -334,7 +334,7 @@ int r8712_cmd_thread(void *context)
if (r8712_register_cmd_alive(padapter) != _SUCCESS)
continue;
_next:
- pcmd = r8712_dequeue_cmd(&(pcmdpriv->cmd_queue));
+ pcmd = r8712_dequeue_cmd(&pcmdpriv->cmd_queue);
if (!(pcmd)) {
r8712_unregister_cmd_alive(padapter);
continue;
@@ -419,7 +419,7 @@ _next:
}
/* free all cmd_obj resources */
do {
- pcmd = r8712_dequeue_cmd(&(pcmdpriv->cmd_queue));
+ pcmd = r8712_dequeue_cmd(&pcmdpriv->cmd_queue);
if (!pcmd)
break;
r8712_free_cmd_obj(pcmd);
@@ -433,7 +433,7 @@ void r8712_event_handle(struct _adapter *padapter, __le32 *peventbuf)
u8 evt_code, evt_seq;
u16 evt_sz;
void (*event_callback)(struct _adapter *dev, u8 *pbuf);
- struct evt_priv *pevt_priv = &(padapter->evtpriv);
+ struct evt_priv *pevt_priv = &padapter->evtpriv;
if (!peventbuf)
goto _abort_event_;
diff --git a/drivers/staging/rtl8712/rtl8712_cmd.h b/drivers/staging/rtl8712/rtl8712_cmd.h
index 67e9e910aef9..9181bb6b04c3 100644
--- a/drivers/staging/rtl8712/rtl8712_cmd.h
+++ b/drivers/staging/rtl8712/rtl8712_cmd.h
@@ -145,8 +145,8 @@ enum rtl8712_h2c_cmd {
#define _SetBBReg_CMD_ _Write_BBREG_CMD_
#define _GetRFReg_CMD_ _Read_RFREG_CMD_
#define _SetRFReg_CMD_ _Write_RFREG_CMD_
-#define _DRV_INT_CMD_ (MAX_H2CCMD+1)
-#define _SetRFIntFs_CMD_ (MAX_H2CCMD+2)
+#define _DRV_INT_CMD_ (MAX_H2CCMD + 1)
+#define _SetRFIntFs_CMD_ (MAX_H2CCMD + 2)
#ifdef _RTL8712_CMD_C_
static struct _cmd_callback cmd_callback[] = {
diff --git a/drivers/staging/rtl8712/rtl8712_cmdctrl_bitdef.h b/drivers/staging/rtl8712/rtl8712_cmdctrl_bitdef.h
index b7dda903001f..4b8985d50098 100644
--- a/drivers/staging/rtl8712/rtl8712_cmdctrl_bitdef.h
+++ b/drivers/staging/rtl8712/rtl8712_cmdctrl_bitdef.h
@@ -63,7 +63,7 @@
#define _IMEM_CHK_RPT BIT(1)
#define _IMEM_CODE_DONE BIT(0)
-#define _TXDMA_INIT_VALUE (_IMEM_CHK_RPT|_EMEM_CHK_RPT)
+#define _TXDMA_INIT_VALUE (_IMEM_CHK_RPT | _EMEM_CHK_RPT)
/*RCR*/
#define _ENMBID BIT(27)
diff --git a/drivers/staging/rtl8712/rtl8712_cmdctrl_regdef.h b/drivers/staging/rtl8712/rtl8712_cmdctrl_regdef.h
index 9374f1c48853..8df42a70399f 100644
--- a/drivers/staging/rtl8712/rtl8712_cmdctrl_regdef.h
+++ b/drivers/staging/rtl8712/rtl8712_cmdctrl_regdef.h
@@ -20,7 +20,6 @@
#ifndef __RTL8712_CMDCTRL_REGDEF_H__
#define __RTL8712_CMDCTRL_REGDEF_H__
-
#define CR (RTL8712_CMDCTRL_ + 0x0000)
#define TXPAUSE (RTL8712_CMDCTRL_ + 0x0002)
#define TCR (RTL8712_CMDCTRL_ + 0x0004)
@@ -29,6 +28,5 @@
#define SYSF_CFG (RTL8712_CMDCTRL_ + 0x000D)
#define MBIDCTRL (RTL8712_CMDCTRL_ + 0x000E)
-
#endif /* __RTL8712_CMDCTRL_REGDEF_H__ */
diff --git a/drivers/staging/rtl8712/rtl8712_debugctrl_bitdef.h b/drivers/staging/rtl8712/rtl8712_debugctrl_bitdef.h
index 8bd483795ca4..4b3436795cb1 100644
--- a/drivers/staging/rtl8712/rtl8712_debugctrl_bitdef.h
+++ b/drivers/staging/rtl8712/rtl8712_debugctrl_bitdef.h
@@ -51,5 +51,4 @@
/*FDLOCKFLAG1*/
#define _LOCKFLAG1_MSK 0x03
-
#endif /* __RTL8712_DEBUGCTRL_BITDEF_H__ */
diff --git a/drivers/staging/rtl8712/rtl8712_debugctrl_regdef.h b/drivers/staging/rtl8712/rtl8712_debugctrl_regdef.h
index 43630bb068f5..d7c964d436a1 100644
--- a/drivers/staging/rtl8712/rtl8712_debugctrl_regdef.h
+++ b/drivers/staging/rtl8712/rtl8712_debugctrl_regdef.h
@@ -41,7 +41,5 @@
#define TRXPKTBUF_DBG_CTRL (RTL8712_DEBUGCTRL_ + 0x38)
#define DPLL_MON (RTL8712_DEBUGCTRL_ + 0x3A)
-
-
#endif /* __RTL8712_DEBUGCTRL_REGDEF_H__ */
diff --git a/drivers/staging/rtl8712/rtl8712_fifoctrl_bitdef.h b/drivers/staging/rtl8712/rtl8712_fifoctrl_bitdef.h
index c564dc862d9d..bd8240476d71 100644
--- a/drivers/staging/rtl8712/rtl8712_fifoctrl_bitdef.h
+++ b/drivers/staging/rtl8712/rtl8712_fifoctrl_bitdef.h
@@ -140,6 +140,5 @@
/*TXFF_PG_NUM*/
#define _TXFF_PG_NUM_MSK 0x0FFF
-
#endif /* __RTL8712_FIFOCTRL_BITDEF_H__ */
diff --git a/drivers/staging/rtl8712/rtl8712_fifoctrl_regdef.h b/drivers/staging/rtl8712/rtl8712_fifoctrl_regdef.h
index 29b89c45c70c..6d527380fd29 100644
--- a/drivers/staging/rtl8712/rtl8712_fifoctrl_regdef.h
+++ b/drivers/staging/rtl8712/rtl8712_fifoctrl_regdef.h
@@ -71,6 +71,4 @@
#define TXQ_PGADD (RTL8712_FIFOCTRL_ + 0xB3)
#define TXFF_PG_NUM (RTL8712_FIFOCTRL_ + 0xB4)
-
-
#endif /* __RTL8712_FIFOCTRL_REGDEF_H__ */
diff --git a/drivers/staging/rtl8712/rtl8712_gp_bitdef.h b/drivers/staging/rtl8712/rtl8712_gp_bitdef.h
index 44c906097530..66c35c990983 100644
--- a/drivers/staging/rtl8712/rtl8712_gp_bitdef.h
+++ b/drivers/staging/rtl8712/rtl8712_gp_bitdef.h
@@ -70,7 +70,7 @@
#define GPIOSEL_PHYDBG 1 /* PHYDBG*/
#define GPIOSEL_BT 2 /* BT_coex*/
#define GPIOSEL_WLANDBG 3 /* WLANDBG*/
-#define GPIOSEL_GPIO_MASK (~(BIT(0)|BIT(1)))
+#define GPIOSEL_GPIO_MASK (~(BIT(0) | BIT(1)))
/* HW Radio OFF switch (GPIO BIT) */
#define HAL_8192S_HW_GPIO_OFF_BIT BIT(3)
#define HAL_8192S_HW_GPIO_OFF_MASK 0xF7
diff --git a/drivers/staging/rtl8712/rtl8712_gp_regdef.h b/drivers/staging/rtl8712/rtl8712_gp_regdef.h
index 8fc68f6a2c79..a0379360d0a3 100644
--- a/drivers/staging/rtl8712/rtl8712_gp_regdef.h
+++ b/drivers/staging/rtl8712/rtl8712_gp_regdef.h
@@ -37,6 +37,5 @@
#define PHY_REG_RPT (RTL8712_GP_ + 0x13)
#define PHY_REG_DATA (RTL8712_GP_ + 0x14)
-
#endif /*__RTL8712_GP_REGDEF_H__ */
diff --git a/drivers/staging/rtl8712/rtl8712_interrupt_bitdef.h b/drivers/staging/rtl8712/rtl8712_interrupt_bitdef.h
index 49598c314f09..2a561d2862e0 100644
--- a/drivers/staging/rtl8712/rtl8712_interrupt_bitdef.h
+++ b/drivers/staging/rtl8712/rtl8712_interrupt_bitdef.h
@@ -53,6 +53,5 @@
#define _VODOK BIT(1)
#define _RXOK BIT(0)
-
#endif /*__RTL8712_INTERRUPT_BITDEF_H__*/
diff --git a/drivers/staging/rtl8712/rtl8712_led.c b/drivers/staging/rtl8712/rtl8712_led.c
index 455fba721135..e9077347837e 100644
--- a/drivers/staging/rtl8712/rtl8712_led.c
+++ b/drivers/staging/rtl8712/rtl8712_led.c
@@ -181,11 +181,11 @@ static void SwLedOff(struct _adapter *padapter, struct LED_871x *pLed)
*/
void r8712_InitSwLeds(struct _adapter *padapter)
{
- struct led_priv *pledpriv = &(padapter->ledpriv);
+ struct led_priv *pledpriv = &padapter->ledpriv;
pledpriv->LedControlHandler = LedControl871x;
- InitLed871x(padapter, &(pledpriv->SwLed0), LED_PIN_LED0);
- InitLed871x(padapter, &(pledpriv->SwLed1), LED_PIN_LED1);
+ InitLed871x(padapter, &pledpriv->SwLed0, LED_PIN_LED0);
+ InitLed871x(padapter, &pledpriv->SwLed1, LED_PIN_LED1);
}
/* Description:
@@ -193,10 +193,10 @@ void r8712_InitSwLeds(struct _adapter *padapter)
*/
void r8712_DeInitSwLeds(struct _adapter *padapter)
{
- struct led_priv *ledpriv = &(padapter->ledpriv);
+ struct led_priv *ledpriv = &padapter->ledpriv;
- DeInitLed871x(&(ledpriv->SwLed0));
- DeInitLed871x(&(ledpriv->SwLed1));
+ DeInitLed871x(&ledpriv->SwLed0);
+ DeInitLed871x(&ledpriv->SwLed1);
}
/* Description:
@@ -206,7 +206,7 @@ void r8712_DeInitSwLeds(struct _adapter *padapter)
static void SwLedBlink(struct LED_871x *pLed)
{
struct _adapter *padapter = pLed->padapter;
- struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
u8 bStopBlinking = false;
/* Change LED according to BlinkingLedState specified. */
@@ -281,14 +281,14 @@ static void SwLedBlink(struct LED_871x *pLed)
static void SwLedBlink1(struct LED_871x *pLed)
{
struct _adapter *padapter = pLed->padapter;
- struct led_priv *ledpriv = &(padapter->ledpriv);
- struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
- struct eeprom_priv *peeprompriv = &(padapter->eeprompriv);
- struct LED_871x *pLed1 = &(ledpriv->SwLed1);
+ struct led_priv *ledpriv = &padapter->ledpriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct eeprom_priv *peeprompriv = &padapter->eeprompriv;
+ struct LED_871x *pLed1 = &ledpriv->SwLed1;
u8 bStopBlinking = false;
if (peeprompriv->CustomerID == RT_CID_819x_CAMEO)
- pLed = &(ledpriv->SwLed1);
+ pLed = &ledpriv->SwLed1;
/* Change LED according to BlinkingLedState specified. */
if (pLed->BlinkingLedState == LED_STATE_ON)
SwLedOn(padapter, pLed);
@@ -499,7 +499,7 @@ static void SwLedBlink2(struct LED_871x *pLed)
static void SwLedBlink3(struct LED_871x *pLed)
{
struct _adapter *padapter = pLed->padapter;
- struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
u8 bStopBlinking = false;
/* Change LED according to BlinkingLedState specified. */
@@ -593,8 +593,8 @@ static void SwLedBlink3(struct LED_871x *pLed)
static void SwLedBlink4(struct LED_871x *pLed)
{
struct _adapter *padapter = pLed->padapter;
- struct led_priv *ledpriv = &(padapter->ledpriv);
- struct LED_871x *pLed1 = &(ledpriv->SwLed1);
+ struct led_priv *ledpriv = &padapter->ledpriv;
+ struct LED_871x *pLed1 = &ledpriv->SwLed1;
u8 bStopBlinking = false;
/* Change LED according to BlinkingLedState specified. */
@@ -844,7 +844,7 @@ static void BlinkWorkItemCallback(struct work_struct *work)
{
struct LED_871x *pLed = container_of(work, struct LED_871x,
BlinkWorkItem);
- struct led_priv *ledpriv = &(pLed->padapter->ledpriv);
+ struct led_priv *ledpriv = &pLed->padapter->ledpriv;
switch (ledpriv->LedStrategy) {
case SW_LED_MODE0:
@@ -886,13 +886,13 @@ static void BlinkWorkItemCallback(struct work_struct *work)
static void SwLedControlMode1(struct _adapter *padapter,
enum LED_CTL_MODE LedAction)
{
- struct led_priv *ledpriv = &(padapter->ledpriv);
- struct LED_871x *pLed = &(ledpriv->SwLed0);
- struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
- struct sitesurvey_ctrl *psitesurveyctrl = &(pmlmepriv->sitesurveyctrl);
+ struct led_priv *ledpriv = &padapter->ledpriv;
+ struct LED_871x *pLed = &ledpriv->SwLed0;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct sitesurvey_ctrl *psitesurveyctrl = &pmlmepriv->sitesurveyctrl;
if (padapter->eeprompriv.CustomerID == RT_CID_819x_CAMEO)
- pLed = &(ledpriv->SwLed1);
+ pLed = &ledpriv->SwLed1;
switch (LedAction) {
case LED_CTL_START_TO_LINK:
case LED_CTL_NO_LINK:
@@ -1106,9 +1106,9 @@ static void SwLedControlMode1(struct _adapter *padapter,
static void SwLedControlMode2(struct _adapter *padapter,
enum LED_CTL_MODE LedAction)
{
- struct led_priv *ledpriv = &(padapter->ledpriv);
+ struct led_priv *ledpriv = &padapter->ledpriv;
struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
- struct LED_871x *pLed = &(ledpriv->SwLed0);
+ struct LED_871x *pLed = &ledpriv->SwLed0;
switch (LedAction) {
case LED_CTL_SITE_SURVEY:
@@ -1239,9 +1239,9 @@ static void SwLedControlMode2(struct _adapter *padapter,
static void SwLedControlMode3(struct _adapter *padapter,
enum LED_CTL_MODE LedAction)
{
- struct led_priv *ledpriv = &(padapter->ledpriv);
+ struct led_priv *ledpriv = &padapter->ledpriv;
struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
- struct LED_871x *pLed = &(ledpriv->SwLed0);
+ struct LED_871x *pLed = &ledpriv->SwLed0;
switch (LedAction) {
case LED_CTL_SITE_SURVEY:
@@ -1383,10 +1383,10 @@ static void SwLedControlMode3(struct _adapter *padapter,
static void SwLedControlMode4(struct _adapter *padapter,
enum LED_CTL_MODE LedAction)
{
- struct led_priv *ledpriv = &(padapter->ledpriv);
+ struct led_priv *ledpriv = &padapter->ledpriv;
struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
- struct LED_871x *pLed = &(ledpriv->SwLed0);
- struct LED_871x *pLed1 = &(ledpriv->SwLed1);
+ struct LED_871x *pLed = &ledpriv->SwLed0;
+ struct LED_871x *pLed1 = &ledpriv->SwLed1;
switch (LedAction) {
case LED_CTL_START_TO_LINK:
@@ -1650,12 +1650,12 @@ static void SwLedControlMode4(struct _adapter *padapter,
static void SwLedControlMode5(struct _adapter *padapter,
enum LED_CTL_MODE LedAction)
{
- struct led_priv *ledpriv = &(padapter->ledpriv);
+ struct led_priv *ledpriv = &padapter->ledpriv;
struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
- struct LED_871x *pLed = &(ledpriv->SwLed0);
+ struct LED_871x *pLed = &ledpriv->SwLed0;
if (padapter->eeprompriv.CustomerID == RT_CID_819x_CAMEO)
- pLed = &(ledpriv->SwLed1);
+ pLed = &ledpriv->SwLed1;
switch (LedAction) {
case LED_CTL_POWER_ON:
@@ -1723,9 +1723,9 @@ static void SwLedControlMode5(struct _adapter *padapter,
static void SwLedControlMode6(struct _adapter *padapter,
enum LED_CTL_MODE LedAction)
{
- struct led_priv *ledpriv = &(padapter->ledpriv);
+ struct led_priv *ledpriv = &padapter->ledpriv;
struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
- struct LED_871x *pLed = &(ledpriv->SwLed0);
+ struct LED_871x *pLed = &ledpriv->SwLed0;
switch (LedAction) {
case LED_CTL_POWER_ON:
@@ -1737,7 +1737,7 @@ static void SwLedControlMode6(struct _adapter *padapter,
pLed->CurrLedState = LED_STATE_ON;
pLed->BlinkingLedState = LED_STATE_ON;
pLed->bLedBlinkInProgress = false;
- mod_timer(&(pLed->BlinkTimer), jiffies + msecs_to_jiffies(0));
+ mod_timer(&pLed->BlinkTimer, jiffies + msecs_to_jiffies(0));
break;
case LED_CTL_TX:
case LED_CTL_RX:
@@ -1807,7 +1807,7 @@ static void SwLedControlMode6(struct _adapter *padapter,
*/
void LedControl871x(struct _adapter *padapter, enum LED_CTL_MODE LedAction)
{
- struct led_priv *ledpriv = &(padapter->ledpriv);
+ struct led_priv *ledpriv = &padapter->ledpriv;
if (!ledpriv->bRegUseLed)
return;
diff --git a/drivers/staging/rtl8712/rtl871x_mlme.c b/drivers/staging/rtl8712/rtl871x_mlme.c
index 78245080e328..ac547ddd72d1 100644
--- a/drivers/staging/rtl8712/rtl871x_mlme.c
+++ b/drivers/staging/rtl8712/rtl871x_mlme.c
@@ -1725,7 +1725,8 @@ unsigned int r8712_restructure_ht_ie(struct _adapter *padapter, u8 *in_ie,
static void update_ht_cap(struct _adapter *padapter, u8 *pie, uint ie_len)
{
u8 *p, max_ampdu_sz;
- int i, len;
+ int i;
+ uint len;
struct sta_info *bmc_sta, *psta;
struct ieee80211_ht_cap *pht_capie;
struct recv_reorder_ctrl *preorder_ctrl;
diff --git a/drivers/staging/rtl8712/rtl871x_xmit.c b/drivers/staging/rtl8712/rtl871x_xmit.c
index eda2aee02ff8..a8ae14ce6613 100644
--- a/drivers/staging/rtl8712/rtl871x_xmit.c
+++ b/drivers/staging/rtl8712/rtl871x_xmit.c
@@ -717,7 +717,7 @@ void r8712_update_protection(struct _adapter *padapter, u8 *ie, uint ie_len)
{
uint protection;
u8 *perp;
- sint erp_len;
+ uint erp_len;
struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
struct registry_priv *pregistrypriv = &padapter->registrypriv;
diff --git a/drivers/staging/rtl8723bs/core/rtw_cmd.c b/drivers/staging/rtl8723bs/core/rtw_cmd.c
index af0a9e0a00df..9e132f943687 100644
--- a/drivers/staging/rtl8723bs/core/rtw_cmd.c
+++ b/drivers/staging/rtl8723bs/core/rtw_cmd.c
@@ -1742,7 +1742,7 @@ exit:
return res;
}
-u32 g_wait_hiq_empty = 0;
+u32 g_wait_hiq_empty;
static void rtw_chk_hi_queue_hdl(struct adapter *padapter)
{
diff --git a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
index 9167900b5f7d..74750dbce379 100644
--- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
+++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
@@ -1380,8 +1380,8 @@ int rtw_action_frame_parse(const u8 *frame, u32 frame_len, u8 *category, u8 *act
fc = le16_to_cpu(((struct ieee80211_hdr_3addr *)frame)->frame_control);
- if ((fc & (RTW_IEEE80211_FCTL_FTYPE|RTW_IEEE80211_FCTL_STYPE))
- != (RTW_IEEE80211_FTYPE_MGMT|RTW_IEEE80211_STYPE_ACTION)
+ if ((fc & (IEEE80211_FCTL_FTYPE|IEEE80211_FCTL_STYPE))
+ != (IEEE80211_FTYPE_MGMT|IEEE80211_STYPE_ACTION)
) {
return false;
}
diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme.c b/drivers/staging/rtl8723bs/core/rtw_mlme.c
index fe739eb2cf7d..c13e514a655a 100644
--- a/drivers/staging/rtl8723bs/core/rtw_mlme.c
+++ b/drivers/staging/rtl8723bs/core/rtw_mlme.c
@@ -14,6 +14,7 @@
******************************************************************************/
#define _RTW_MLME_C_
+#include <linux/etherdevice.h>
#include <drv_types.h>
#include <rtw_debug.h>
#include <linux/jiffies.h>
@@ -2109,7 +2110,7 @@ int rtw_select_roaming_candidate(struct mlme_priv *mlme)
mlme->roam_network = candidate;
if (!memcmp(candidate->network.MacAddress, mlme->roam_tgt_addr, ETH_ALEN))
- memset(mlme->roam_tgt_addr, 0, ETH_ALEN);
+ eth_zero_addr(mlme->roam_tgt_addr);
}
ret = _SUCCESS;
diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c
index 7d7756e40bcb..589fab24bb25 100644
--- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c
+++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c
@@ -1219,7 +1219,7 @@ unsigned int OnAssocReq(struct adapter *padapter, union recv_frame *precv_frame)
}
- if (pkt_len < IEEE80211_3ADDR_LEN + ie_offset) {
+ if (pkt_len < sizeof(struct ieee80211_hdr_3addr) + ie_offset) {
DBG_871X("handle_assoc(reassoc =%d) - too short payload (len =%lu)"
"\n", reassoc, (unsigned long)pkt_len);
return _FAIL;
@@ -1236,8 +1236,8 @@ unsigned int OnAssocReq(struct adapter *padapter, union recv_frame *precv_frame)
/* listen_interval = le16_to_cpu(*(unsigned short *)(pframe + WLAN_HDR_A3_LEN+2)); */
listen_interval = RTW_GET_LE16(pframe + WLAN_HDR_A3_LEN+2);
- left = pkt_len - (IEEE80211_3ADDR_LEN + ie_offset);
- pos = pframe + (IEEE80211_3ADDR_LEN + ie_offset);
+ left = pkt_len - (sizeof(struct ieee80211_hdr_3addr) + ie_offset);
+ pos = pframe + (sizeof(struct ieee80211_hdr_3addr) + ie_offset);
DBG_871X("%s\n", __func__);
@@ -4263,7 +4263,7 @@ unsigned int send_beacon(struct adapter *padapter)
issue_beacon(padapter, 100);
issue++;
do {
- yield();
+ cond_resched();
rtw_hal_get_hwreg(padapter, HW_VAR_BCN_VALID, (u8 *)(&bxmitok));
poll++;
} while ((poll%10) != 0 && false == bxmitok && !padapter->bSurpriseRemoved && !padapter->bDriverStopped);
diff --git a/drivers/staging/rtl8723bs/core/rtw_pwrctrl.c b/drivers/staging/rtl8723bs/core/rtw_pwrctrl.c
index 4a6af72013fa..85f7769ecc2d 100644
--- a/drivers/staging/rtl8723bs/core/rtw_pwrctrl.c
+++ b/drivers/staging/rtl8723bs/core/rtw_pwrctrl.c
@@ -701,7 +701,7 @@ void LPS_Leave_check(
bReady = false;
start_time = jiffies;
- yield();
+ cond_resched();
while (1) {
down(&pwrpriv->lock);
diff --git a/drivers/staging/rtl8723bs/core/rtw_recv.c b/drivers/staging/rtl8723bs/core/rtw_recv.c
index 9c7c3be0553a..86f995b8a88b 100644
--- a/drivers/staging/rtl8723bs/core/rtw_recv.c
+++ b/drivers/staging/rtl8723bs/core/rtw_recv.c
@@ -1785,7 +1785,7 @@ static union recv_frame *recvframe_defrag(struct adapter *adapter,
pfhdr->attrib.icv_len = pnfhdr->attrib.icv_len;
plist = get_next(plist);
- };
+ }
/* free the defrag_q queue and return the prframe */
rtw_free_recvframe_queue(defrag_q, pfree_recv_queue);
diff --git a/drivers/staging/rtl8723bs/hal/hal_com.c b/drivers/staging/rtl8723bs/hal/hal_com.c
index dec887a5b338..1cef1d77977c 100644
--- a/drivers/staging/rtl8723bs/hal/hal_com.c
+++ b/drivers/staging/rtl8723bs/hal/hal_com.c
@@ -1680,18 +1680,18 @@ void rtw_store_phy_info(struct adapter *padapter, union recv_frame *prframe)
struct hal_com_data *pHalData = GET_HAL_DATA(padapter);
struct rx_pkt_attrib *pattrib = &prframe->u.hdr.attrib;
- PODM_PHY_INFO_T pPhyInfo = (PODM_PHY_INFO_T)(&pattrib->phy_info);
+ struct odm_phy_info *pPhyInfo = (PODM_PHY_INFO_T)(&pattrib->phy_info);
struct rx_raw_rssi *psample_pkt_rssi = &padapter->recvpriv.raw_rssi_info;
psample_pkt_rssi->data_rate = pattrib->data_rate;
isCCKrate = pattrib->data_rate <= DESC_RATE11M;
- psample_pkt_rssi->pwdball = pPhyInfo->RxPWDBAll;
- psample_pkt_rssi->pwr_all = pPhyInfo->RecvSignalPower;
+ psample_pkt_rssi->pwdball = pPhyInfo->rx_pwd_ba11;
+ psample_pkt_rssi->pwr_all = pPhyInfo->recv_signal_power;
for (rf_path = 0; rf_path < pHalData->NumTotalRFPath; rf_path++) {
- psample_pkt_rssi->mimo_singal_strength[rf_path] = pPhyInfo->RxMIMOSignalStrength[rf_path];
- psample_pkt_rssi->mimo_singal_quality[rf_path] = pPhyInfo->RxMIMOSignalQuality[rf_path];
+ psample_pkt_rssi->mimo_singal_strength[rf_path] = pPhyInfo->rx_mimo_signal_strength[rf_path];
+ psample_pkt_rssi->mimo_singal_quality[rf_path] = pPhyInfo->rx_mimo_signal_quality[rf_path];
if (!isCCKrate) {
psample_pkt_rssi->ofdm_pwr[rf_path] = pPhyInfo->RxPwr[rf_path];
psample_pkt_rssi->ofdm_snr[rf_path] = pPhyInfo->RxSNR[rf_path];
diff --git a/drivers/staging/rtl8723bs/hal/odm.h b/drivers/staging/rtl8723bs/hal/odm.h
index 87a76bafecb3..1037b88e8f08 100644
--- a/drivers/staging/rtl8723bs/hal/odm.h
+++ b/drivers/staging/rtl8723bs/hal/odm.h
@@ -17,7 +17,6 @@
#ifndef __HALDMOUTSRC_H__
#define __HALDMOUTSRC_H__
-
#include "odm_EdcaTurboCheck.h"
#include "odm_DIG.h"
#include "odm_PathDiv.h"
@@ -32,7 +31,6 @@
#define TRAFFIC_HIGH 1
#define NONE 0
-
/* 3 Tx Power Tracking */
/* 3 ============================================================ */
#define DPK_DELTA_MAPPING_NUM 13
@@ -81,7 +79,6 @@
#define AUX_ANT 2 /* AntB or Ant Aux */
#define MAX_ANT 3 /* 3 for AP using */
-
/* Antenna Diversity Type */
#define SW_ANTDIV 0
#define HW_ANTDIV 1
@@ -200,7 +197,6 @@ typedef struct _ODM_RATE_ADAPTIVE {
} ODM_RATE_ADAPTIVE, *PODM_RATE_ADAPTIVE;
-
#define IQK_MAC_REG_NUM 4
#define IQK_ADDA_REG_NUM 16
#define IQK_BB_REG_NUM_MAX 10
@@ -229,49 +225,49 @@ typedef struct _ODM_RATE_ADAPTIVE {
#define MAX_PATH_NUM_8814A 4
#define MAX_PATH_NUM_8822B 2
-
#define IQK_THRESHOLD 8
#define DPK_THRESHOLD 4
-typedef struct _ODM_Phy_Status_Info_ {
- /* */
- /* Be care, if you want to add any element please insert between */
- /* RxPWDBAll & SignalStrength. */
- /* */
- u8 RxPWDBAll;
-
- u8 SignalQuality; /* in 0-100 index. */
- s8 RxMIMOSignalQuality[4]; /* per-path's EVM */
- u8 RxMIMOEVMdbm[4]; /* per-path's EVM dbm */
+struct odm_phy_info {
+ /*
+ * Be care, if you want to add any element, please insert it between
+ * rx_pwd_ball and signal_strength.
+ */
+ u8 rx_pwd_ba11;
- u8 RxMIMOSignalStrength[4];/* in 0~100 index */
+ u8 signal_quality; /* in 0-100 index. */
+ s8 rx_mimo_signal_quality[4]; /* per-path's EVM */
+ u8 rx_mimo_evm_dbm[4]; /* per-path's EVM dbm */
- u16 Cfo_short[4]; /* per-path's Cfo_short */
- u16 Cfo_tail[4]; /* per-path's Cfo_tail */
+ u8 rx_mimo_signal_strength[4]; /* in 0~100 index */
- s8 RxPower; /* in dBm Translate from PWdB */
- s8 RecvSignalPower; /* Real power in dBm for this packet, no beautification and aggregation. Keep this raw info to be used for the other procedures. */
- u8 BTRxRSSIPercentage;
- u8 SignalStrength; /* in 0-100 index. */
+ u16 cfo_short[4]; /* per-path's Cfo_short */
+ u16 cfo_tail[4]; /* per-path's Cfo_tail */
- s8 RxPwr[4]; /* per-path's pwdb */
+ s8 rx_power; /* in dBm Translate from PWdB */
- u8 RxSNR[4]; /* per-path's SNR */
- u8 BandWidth;
- u8 btCoexPwrAdjust;
-} ODM_PHY_INFO_T, *PODM_PHY_INFO_T;
+ /*
+ * Real power in dBm for this packet, no beautification and
+ * aggregation. Keep this raw info to be used for the other procedures.
+ */
+ s8 recv_signal_power;
+ u8 bt_rx_rssi_percentage;
+ u8 signal_strength; /* in 0-100 index. */
+ s8 rx_pwr[4]; /* per-path's pwdb */
-typedef struct _ODM_Per_Pkt_Info_ {
- /* u8 Rate; */
- u8 DataRate;
- u8 StationID;
- bool bPacketMatchBSSID;
- bool bPacketToSelf;
- bool bPacketBeacon;
- bool bToSelf;
-} ODM_PACKET_INFO_T, *PODM_PACKET_INFO_T;
+ u8 rx_snr[4]; /* per-path's SNR */
+ u8 band_width;
+ u8 bt_coex_pwr_adjust;
+};
+struct odm_packet_info {
+ u8 data_rate;
+ u8 station_id;
+ bool bssid_match;
+ bool to_self;
+ bool is_beacon;
+};
typedef struct _ODM_Phy_Dbg_Info_ {
/* ODM Write, debug info */
@@ -285,12 +281,10 @@ typedef struct _ODM_Phy_Dbg_Info_ {
} ODM_PHY_DBG_INFO_T;
-
typedef struct _ODM_Mac_Status_Info_ {
u8 test;
} ODM_MAC_INFO;
-
typedef enum tag_Dynamic_ODM_Support_Ability_Type {
/* BB Team */
ODM_DIG = 0x00000001,
@@ -369,7 +363,6 @@ typedef enum _ODM_Common_Info_Definition {
ODM_CMNINFO_SMART_CONCURRENT,
/* HOOK BEFORE REG INIT----------- */
-
/* Dynamic value: */
/* POINTER REFERENCE----------- */
ODM_CMNINFO_MAC_PHY_MODE, /* ODM_MAC_PHY_MODE_E */
@@ -427,8 +420,6 @@ typedef enum _ODM_Common_Info_Definition {
ODM_CMNINFO_MAC_STATUS,
ODM_CMNINFO_MAX,
-
-
} ODM_CMNINFO_E;
/* 2011/10/20 MH Define ODM support ability. ODM_CMNINFO_ABILITY */
@@ -509,7 +500,6 @@ typedef enum tag_ODM_RF_Path_Bit_Definition {
ODM_RF_RX_D = BIT7,
} ODM_RF_PATH_E;
-
typedef enum tag_ODM_RF_Type_Definition {
ODM_1T1R = 0,
ODM_1T2R = 1,
@@ -521,7 +511,6 @@ typedef enum tag_ODM_RF_Type_Definition {
ODM_4T4R = 7,
} ODM_RF_TYPE_E;
-
/* */
/* ODM Dynamic common info value definition */
/* */
@@ -538,7 +527,6 @@ typedef enum tag_ODM_MAC_PHY_Mode_Definition {
ODM_DMDP = 2,
} ODM_MAC_PHY_MODE_E;
-
typedef enum tag_BT_Coexist_Definition {
ODM_BT_BUSY = 1,
ODM_BT_ON = 2,
@@ -607,7 +595,6 @@ typedef enum tag_Bandwidth_Definition {
ODM_BW10M = 4,
} ODM_BW_E;
-
/* ODM_CMNINFO_BOARD_TYPE */
/* For non-AC-series IC , ODM_BOARD_5G_EXT_PA and ODM_BOARD_5G_EXT_LNA are ignored */
/* For AC-series IC, external PA & LNA can be indivisuallly added on 2.4G and/or 5G */
@@ -661,7 +648,6 @@ typedef enum tag_CCA_Path {
ODM_CCA_1R_B = 2,
} ODM_CCA_PATH_E;
-
typedef struct _ODM_RA_Info_ {
u8 RateID;
u32 RateMask;
@@ -700,7 +686,6 @@ typedef struct _IQK_MATRIX_REGS_SETTING {
bool bBWIqkResultSaved[3];
} IQK_MATRIX_REGS_SETTING, *PIQK_MATRIX_REGS_SETTING;
-
/* Remove PATHDIV_PARA struct to odm_PathDiv.h */
typedef struct ODM_RF_Calibration_Structure {
@@ -736,7 +721,6 @@ typedef struct ODM_RF_Calibration_Structure {
u8 bRfPiEnable;
u32 TXPowerTrackingCallbackCnt; /* cosa add for debug */
-
/* Tx power Tracking ------------------------- */
u8 bCCKinCH14;
u8 CCK_index;
@@ -791,7 +775,6 @@ typedef struct ODM_RF_Calibration_Structure {
u32 TxIQC_8723B[2][3][2]; /* { {S1: 0xc94, 0xc80, 0xc4c} , {S0: 0xc9c, 0xc88, 0xc4c}} */
u32 RxIQC_8723B[2][2][2]; /* { {S1: 0xc14, 0xca0} , {S0: 0xc14, 0xca0}} */
-
/* for APK */
u32 APKoutput[2][2]; /* path A/B; output1_1a/output1_2a */
u8 bAPKdone;
@@ -839,7 +822,6 @@ typedef struct _FAST_ANTENNA_TRAINNING_ {
u32 OFDM_counter_main;
u32 OFDM_counter_aux;
-
u32 CCK_CtrlFrame_Cnt_main;
u32 CCK_CtrlFrame_Cnt_aux;
u32 OFDM_CtrlFrame_Cnt_main;
@@ -875,13 +857,11 @@ typedef struct _ODM_PATH_DIVERSITY_ {
u32 PathB_Cnt[ODM_ASSOCIATE_ENTRY_NUM];
} PATHDIV_T, *pPATHDIV_T;
-
typedef enum _BASEBAND_CONFIG_PHY_REG_PG_VALUE_TYPE{
PHY_REG_PG_RELATIVE_VALUE = 0,
PHY_REG_PG_EXACT_VALUE = 1
} PHY_REG_PG_TYPE;
-
/* */
/* Antenna detection information from single tone mechanism, added by Roger, 2012.11.27. */
/* */
@@ -933,7 +913,6 @@ typedef struct DM_Out_Source_Dynamic_Mechanism_Structure {
/* bool bSlaveOfDMSP; */
/* REMOVED COMMON INFO---------- */
-
/* 1 COMMON INFORMATION */
/* */
@@ -1105,7 +1084,6 @@ typedef struct DM_Out_Source_Dynamic_Mechanism_Structure {
u8 Adaptivity_IGI_upper;
u8 NHM_cnt_0;
-
ODM_NOISE_MONITOR noise_level;/* ODM_MAX_CHANNEL_NUM]; */
/* */
/* 2 Define STA info. */
@@ -1367,7 +1345,6 @@ typedef enum tag_SW_Antenna_Switch_Definition {
Antenna_MAX = 3,
} DM_SWAS_E;
-
/* Maximal number of antenna detection mechanism needs to perform, added by Roger, 2011.12.28. */
#define MAX_ANTENNA_DETECTION_CNT 10
@@ -1400,7 +1377,6 @@ extern u32 TxScalingTable_Jaguar[TXSCALE_TABLE_SIZE];
void ODM_SetAntenna(PDM_ODM_T pDM_Odm, u8 Antenna);
-
/* Remove BB power saving by Yuchen */
#define dm_CheckTXPowerTracking ODM_TXPowerTrackingCheck
@@ -1417,7 +1393,7 @@ bool ODM_RAStateCheck(
void ODM_SwAntDivChkPerPktRssi(
PDM_ODM_T pDM_Odm,
u8 StationID,
- PODM_PHY_INFO_T pPhyInfo
+ struct odm_phy_info *pPhyInfo
);
u32 ODM_Get_Rate_Bitmap(
diff --git a/drivers/staging/rtl8723bs/hal/odm_CfoTracking.c b/drivers/staging/rtl8723bs/hal/odm_CfoTracking.c
index 71853e6f7106..178aaab3f76c 100644
--- a/drivers/staging/rtl8723bs/hal/odm_CfoTracking.c
+++ b/drivers/staging/rtl8723bs/hal/odm_CfoTracking.c
@@ -316,14 +316,14 @@ void ODM_CfoTracking(void *pDM_VOID)
void ODM_ParsingCFO(void *pDM_VOID, void *pPktinfo_VOID, s8 *pcfotail)
{
PDM_ODM_T pDM_Odm = (PDM_ODM_T)pDM_VOID;
- PODM_PACKET_INFO_T pPktinfo = (PODM_PACKET_INFO_T)pPktinfo_VOID;
+ struct odm_packet_info *pPktinfo = (struct odm_packet_info *)pPktinfo_VOID;
PCFO_TRACKING pCfoTrack = &pDM_Odm->DM_CfoTrack;
u8 i;
if (!(pDM_Odm->SupportAbility & ODM_BB_CFO_TRACKING))
return;
- if (pPktinfo->StationID != 0) {
+ if (pPktinfo->station_id != 0) {
/* 3 Update CFO report for path-A & path-B */
/* Only paht-A and path-B have CFO tail and short CFO */
for (i = ODM_RF_PATH_A; i <= ODM_RF_PATH_B; i++)
diff --git a/drivers/staging/rtl8723bs/hal/odm_HWConfig.c b/drivers/staging/rtl8723bs/hal/odm_HWConfig.c
index 8dd6da8a4e26..9e161f080c57 100644
--- a/drivers/staging/rtl8723bs/hal/odm_HWConfig.c
+++ b/drivers/staging/rtl8723bs/hal/odm_HWConfig.c
@@ -91,9 +91,9 @@ static u8 odm_EVMdbToPercentage(s8 Value)
static void odm_RxPhyStatus92CSeries_Parsing(
PDM_ODM_T pDM_Odm,
- PODM_PHY_INFO_T pPhyInfo,
+ struct odm_phy_info *pPhyInfo,
u8 *pPhyStatus,
- PODM_PACKET_INFO_T pPktinfo
+ struct odm_packet_info *pPktinfo
)
{
u8 i, Max_spatial_stream;
@@ -106,9 +106,9 @@ static void odm_RxPhyStatus92CSeries_Parsing(
u8 LNA_idx, VGA_idx;
PPHY_STATUS_RPT_8192CD_T pPhyStaRpt = (PPHY_STATUS_RPT_8192CD_T)pPhyStatus;
- isCCKrate = pPktinfo->DataRate <= DESC_RATE11M;
- pPhyInfo->RxMIMOSignalQuality[ODM_RF_PATH_A] = -1;
- pPhyInfo->RxMIMOSignalQuality[ODM_RF_PATH_B] = -1;
+ isCCKrate = pPktinfo->data_rate <= DESC_RATE11M;
+ pPhyInfo->rx_mimo_signal_quality[ODM_RF_PATH_A] = -1;
+ pPhyInfo->rx_mimo_signal_quality[ODM_RF_PATH_B] = -1;
if (isCCKrate) {
@@ -137,9 +137,9 @@ static void odm_RxPhyStatus92CSeries_Parsing(
if (PWDB_ALL > 100)
PWDB_ALL = 100;
- pPhyInfo->RxPWDBAll = PWDB_ALL;
- pPhyInfo->BTRxRSSIPercentage = PWDB_ALL;
- pPhyInfo->RecvSignalPower = rx_pwr_all;
+ pPhyInfo->rx_pwd_ba11 = PWDB_ALL;
+ pPhyInfo->bt_rx_rssi_percentage = PWDB_ALL;
+ pPhyInfo->recv_signal_power = rx_pwr_all;
/* */
/* (3) Get Signal Quality (EVM) */
/* */
@@ -147,7 +147,7 @@ static void odm_RxPhyStatus92CSeries_Parsing(
{
u8 SQ, SQ_rpt;
- if (pPhyInfo->RxPWDBAll > 40 && !pDM_Odm->bInHctTest)
+ if (pPhyInfo->rx_pwd_ba11 > 40 && !pDM_Odm->bInHctTest)
SQ = 100;
else {
SQ_rpt = pPhyStaRpt->cck_sig_qual_ofdm_pwdb_all;
@@ -162,9 +162,9 @@ static void odm_RxPhyStatus92CSeries_Parsing(
}
/* DbgPrint("cck SQ = %d\n", SQ); */
- pPhyInfo->SignalQuality = SQ;
- pPhyInfo->RxMIMOSignalQuality[ODM_RF_PATH_A] = SQ;
- pPhyInfo->RxMIMOSignalQuality[ODM_RF_PATH_B] = -1;
+ pPhyInfo->signal_quality = SQ;
+ pPhyInfo->rx_mimo_signal_quality[ODM_RF_PATH_A] = SQ;
+ pPhyInfo->rx_mimo_signal_quality[ODM_RF_PATH_B] = -1;
}
} else { /* is OFDM rate */
pDM_Odm->PhyDbgInfo.NumQryPhyStatusOFDM++;
@@ -183,17 +183,17 @@ static void odm_RxPhyStatus92CSeries_Parsing(
rx_pwr[i] = ((pPhyStaRpt->path_agc[i].gain&0x3F)*2) - 110;
- pPhyInfo->RxPwr[i] = rx_pwr[i];
+ pPhyInfo->rx_pwr[i] = rx_pwr[i];
/* Translate DBM to percentage. */
RSSI = odm_QueryRxPwrPercentage(rx_pwr[i]);
total_rssi += RSSI;
/* RT_DISP(FRX, RX_PHY_SS, ("RF-%d RXPWR =%x RSSI =%d\n", i, rx_pwr[i], RSSI)); */
- pPhyInfo->RxMIMOSignalStrength[i] = (u8) RSSI;
+ pPhyInfo->rx_mimo_signal_strength[i] = (u8) RSSI;
/* Get Rx snr value in DB */
- pPhyInfo->RxSNR[i] = pDM_Odm->PhyDbgInfo.RxSNRdB[i] = (s32)(pPhyStaRpt->path_rxsnr[i]/2);
+ pPhyInfo->rx_snr[i] = pDM_Odm->PhyDbgInfo.RxSNRdB[i] = (s32)(pPhyStaRpt->path_rxsnr[i]/2);
}
@@ -205,17 +205,17 @@ static void odm_RxPhyStatus92CSeries_Parsing(
PWDB_ALL_BT = PWDB_ALL = odm_QueryRxPwrPercentage(rx_pwr_all);
/* RT_DISP(FRX, RX_PHY_SS, ("PWDB_ALL =%d\n", PWDB_ALL)); */
- pPhyInfo->RxPWDBAll = PWDB_ALL;
- /* ODM_RT_TRACE(pDM_Odm, ODM_COMP_RSSI_MONITOR, ODM_DBG_LOUD, ("ODM OFDM RSSI =%d\n", pPhyInfo->RxPWDBAll)); */
- pPhyInfo->BTRxRSSIPercentage = PWDB_ALL_BT;
- pPhyInfo->RxPower = rx_pwr_all;
- pPhyInfo->RecvSignalPower = rx_pwr_all;
+ pPhyInfo->rx_pwd_ba11 = PWDB_ALL;
+ /* ODM_RT_TRACE(pDM_Odm, ODM_COMP_RSSI_MONITOR, ODM_DBG_LOUD, ("ODM OFDM RSSI =%d\n", pPhyInfo->rx_pwd_ba11)); */
+ pPhyInfo->bt_rx_rssi_percentage = PWDB_ALL_BT;
+ pPhyInfo->rx_power = rx_pwr_all;
+ pPhyInfo->recv_signal_power = rx_pwr_all;
{/* pMgntInfo->CustomerID != RT_CID_819x_Lenovo */
/* */
/* (3)EVM of HT rate */
/* */
- if (pPktinfo->DataRate >= DESC_RATEMCS8 && pPktinfo->DataRate <= DESC_RATEMCS15)
+ if (pPktinfo->data_rate >= DESC_RATEMCS8 && pPktinfo->data_rate <= DESC_RATEMCS15)
Max_spatial_stream = 2; /* both spatial stream make sense */
else
Max_spatial_stream = 1; /* only spatial stream 1 makes sense */
@@ -232,9 +232,9 @@ static void odm_RxPhyStatus92CSeries_Parsing(
/* if (pPktinfo->bPacketMatchBSSID) */
{
if (i == ODM_RF_PATH_A) /* Fill value in RFD, Get the first spatial stream only */
- pPhyInfo->SignalQuality = (u8)(EVM & 0xff);
+ pPhyInfo->signal_quality = (u8)(EVM & 0xff);
- pPhyInfo->RxMIMOSignalQuality[i] = (u8)(EVM & 0xff);
+ pPhyInfo->rx_mimo_signal_quality[i] = (u8)(EVM & 0xff);
}
}
}
@@ -249,25 +249,25 @@ static void odm_RxPhyStatus92CSeries_Parsing(
#ifdef CONFIG_SKIP_SIGNAL_SCALE_MAPPING
pPhyInfo->SignalStrength = (u8)PWDB_ALL;
#else
- pPhyInfo->SignalStrength = (u8)(odm_SignalScaleMapping(pDM_Odm, PWDB_ALL));/* PWDB_ALL; */
+ pPhyInfo->signal_strength = (u8)(odm_SignalScaleMapping(pDM_Odm, PWDB_ALL));/* PWDB_ALL; */
#endif
} else {
if (rf_rx_num != 0) {
#ifdef CONFIG_SKIP_SIGNAL_SCALE_MAPPING
total_rssi /= rf_rx_num;
- pPhyInfo->SignalStrength = (u8)total_rssi;
+ pPhyInfo->signal_strength = (u8)total_rssi;
#else
- pPhyInfo->SignalStrength = (u8)(odm_SignalScaleMapping(pDM_Odm, total_rssi /= rf_rx_num));
+ pPhyInfo->signal_strength = (u8)(odm_SignalScaleMapping(pDM_Odm, total_rssi /= rf_rx_num));
#endif
}
}
- /* DbgPrint("isCCKrate = %d, pPhyInfo->RxPWDBAll = %d, pPhyStaRpt->cck_agc_rpt_ofdm_cfosho_a = 0x%x\n", */
- /* isCCKrate, pPhyInfo->RxPWDBAll, pPhyStaRpt->cck_agc_rpt_ofdm_cfosho_a); */
+ /* DbgPrint("isCCKrate = %d, pPhyInfo->rx_pwd_ba11 = %d, pPhyStaRpt->cck_agc_rpt_ofdm_cfosho_a = 0x%x\n", */
+ /* isCCKrate, pPhyInfo->rx_pwd_ba11, pPhyStaRpt->cck_agc_rpt_ofdm_cfosho_a); */
}
static void odm_Process_RSSIForDM(
- PDM_ODM_T pDM_Odm, PODM_PHY_INFO_T pPhyInfo, PODM_PACKET_INFO_T pPktinfo
+ PDM_ODM_T pDM_Odm, struct odm_phy_info *pPhyInfo, struct odm_packet_info *pPktinfo
)
{
@@ -279,22 +279,22 @@ static void odm_Process_RSSIForDM(
PSTA_INFO_T pEntry;
- if (pPktinfo->StationID == 0xFF)
+ if (pPktinfo->station_id == 0xFF)
return;
- pEntry = pDM_Odm->pODM_StaInfo[pPktinfo->StationID];
+ pEntry = pDM_Odm->pODM_StaInfo[pPktinfo->station_id];
if (!IS_STA_VALID(pEntry))
return;
- if ((!pPktinfo->bPacketMatchBSSID))
+ if ((!pPktinfo->bssid_match))
return;
- if (pPktinfo->bPacketBeacon)
+ if (pPktinfo->is_beacon)
pDM_Odm->PhyDbgInfo.NumQryBeaconPkt++;
- isCCKrate = ((pPktinfo->DataRate <= DESC_RATE11M)) ? true : false;
- pDM_Odm->RxRate = pPktinfo->DataRate;
+ isCCKrate = ((pPktinfo->data_rate <= DESC_RATE11M)) ? true : false;
+ pDM_Odm->RxRate = pPktinfo->data_rate;
/* Statistic for antenna/path diversity------------------ */
if (pDM_Odm->SupportAbility & ODM_BB_ANT_DIV) {
@@ -307,28 +307,28 @@ static void odm_Process_RSSIForDM(
UndecoratedSmoothedOFDM = pEntry->rssi_stat.UndecoratedSmoothedOFDM;
UndecoratedSmoothedPWDB = pEntry->rssi_stat.UndecoratedSmoothedPWDB;
- if (pPktinfo->bPacketToSelf || pPktinfo->bPacketBeacon) {
+ if (pPktinfo->to_self || pPktinfo->is_beacon) {
if (!isCCKrate) { /* ofdm rate */
- if (pPhyInfo->RxMIMOSignalStrength[ODM_RF_PATH_B] == 0) {
- RSSI_Ave = pPhyInfo->RxMIMOSignalStrength[ODM_RF_PATH_A];
- pDM_Odm->RSSI_A = pPhyInfo->RxMIMOSignalStrength[ODM_RF_PATH_A];
+ if (pPhyInfo->rx_mimo_signal_strength[ODM_RF_PATH_B] == 0) {
+ RSSI_Ave = pPhyInfo->rx_mimo_signal_strength[ODM_RF_PATH_A];
+ pDM_Odm->RSSI_A = pPhyInfo->rx_mimo_signal_strength[ODM_RF_PATH_A];
pDM_Odm->RSSI_B = 0;
} else {
- /* DbgPrint("pRfd->Status.RxMIMOSignalStrength[0] = %d, pRfd->Status.RxMIMOSignalStrength[1] = %d\n", */
- /* pRfd->Status.RxMIMOSignalStrength[0], pRfd->Status.RxMIMOSignalStrength[1]); */
- pDM_Odm->RSSI_A = pPhyInfo->RxMIMOSignalStrength[ODM_RF_PATH_A];
- pDM_Odm->RSSI_B = pPhyInfo->RxMIMOSignalStrength[ODM_RF_PATH_B];
+ /* DbgPrint("pRfd->Status.rx_mimo_signal_strength[0] = %d, pRfd->Status.rx_mimo_signal_strength[1] = %d\n", */
+ /* pRfd->Status.rx_mimo_signal_strength[0], pRfd->Status.rx_mimo_signal_strength[1]); */
+ pDM_Odm->RSSI_A = pPhyInfo->rx_mimo_signal_strength[ODM_RF_PATH_A];
+ pDM_Odm->RSSI_B = pPhyInfo->rx_mimo_signal_strength[ODM_RF_PATH_B];
if (
- pPhyInfo->RxMIMOSignalStrength[ODM_RF_PATH_A] >
- pPhyInfo->RxMIMOSignalStrength[ODM_RF_PATH_B]
+ pPhyInfo->rx_mimo_signal_strength[ODM_RF_PATH_A] >
+ pPhyInfo->rx_mimo_signal_strength[ODM_RF_PATH_B]
) {
- RSSI_max = pPhyInfo->RxMIMOSignalStrength[ODM_RF_PATH_A];
- RSSI_min = pPhyInfo->RxMIMOSignalStrength[ODM_RF_PATH_B];
+ RSSI_max = pPhyInfo->rx_mimo_signal_strength[ODM_RF_PATH_A];
+ RSSI_min = pPhyInfo->rx_mimo_signal_strength[ODM_RF_PATH_B];
} else {
- RSSI_max = pPhyInfo->RxMIMOSignalStrength[ODM_RF_PATH_B];
- RSSI_min = pPhyInfo->RxMIMOSignalStrength[ODM_RF_PATH_A];
+ RSSI_max = pPhyInfo->rx_mimo_signal_strength[ODM_RF_PATH_B];
+ RSSI_min = pPhyInfo->rx_mimo_signal_strength[ODM_RF_PATH_A];
}
if ((RSSI_max-RSSI_min) < 3)
@@ -343,9 +343,9 @@ static void odm_Process_RSSIForDM(
/* 1 Process OFDM RSSI */
if (UndecoratedSmoothedOFDM <= 0) /* initialize */
- UndecoratedSmoothedOFDM = pPhyInfo->RxPWDBAll;
+ UndecoratedSmoothedOFDM = pPhyInfo->rx_pwd_ba11;
else {
- if (pPhyInfo->RxPWDBAll > (u32)UndecoratedSmoothedOFDM) {
+ if (pPhyInfo->rx_pwd_ba11 > (u32)UndecoratedSmoothedOFDM) {
UndecoratedSmoothedOFDM =
((UndecoratedSmoothedOFDM*(Rx_Smooth_Factor-1)) +
RSSI_Ave)/Rx_Smooth_Factor;
@@ -360,23 +360,23 @@ static void odm_Process_RSSIForDM(
pEntry->rssi_stat.PacketMap = (pEntry->rssi_stat.PacketMap<<1) | BIT0;
} else {
- RSSI_Ave = pPhyInfo->RxPWDBAll;
- pDM_Odm->RSSI_A = (u8) pPhyInfo->RxPWDBAll;
+ RSSI_Ave = pPhyInfo->rx_pwd_ba11;
+ pDM_Odm->RSSI_A = (u8) pPhyInfo->rx_pwd_ba11;
pDM_Odm->RSSI_B = 0;
/* 1 Process CCK RSSI */
if (UndecoratedSmoothedCCK <= 0) /* initialize */
- UndecoratedSmoothedCCK = pPhyInfo->RxPWDBAll;
+ UndecoratedSmoothedCCK = pPhyInfo->rx_pwd_ba11;
else {
- if (pPhyInfo->RxPWDBAll > (u32)UndecoratedSmoothedCCK) {
+ if (pPhyInfo->rx_pwd_ba11 > (u32)UndecoratedSmoothedCCK) {
UndecoratedSmoothedCCK =
((UndecoratedSmoothedCCK*(Rx_Smooth_Factor-1)) +
- pPhyInfo->RxPWDBAll)/Rx_Smooth_Factor;
+ pPhyInfo->rx_pwd_ba11)/Rx_Smooth_Factor;
UndecoratedSmoothedCCK = UndecoratedSmoothedCCK + 1;
} else {
UndecoratedSmoothedCCK =
((UndecoratedSmoothedCCK*(Rx_Smooth_Factor-1)) +
- pPhyInfo->RxPWDBAll)/Rx_Smooth_Factor;
+ pPhyInfo->rx_pwd_ba11)/Rx_Smooth_Factor;
}
}
pEntry->rssi_stat.PacketMap = pEntry->rssi_stat.PacketMap<<1;
@@ -422,9 +422,9 @@ static void odm_Process_RSSIForDM(
/* */
static void ODM_PhyStatusQuery_92CSeries(
PDM_ODM_T pDM_Odm,
- PODM_PHY_INFO_T pPhyInfo,
+ struct odm_phy_info *pPhyInfo,
u8 *pPhyStatus,
- PODM_PACKET_INFO_T pPktinfo
+ struct odm_packet_info *pPktinfo
)
{
@@ -436,9 +436,9 @@ static void ODM_PhyStatusQuery_92CSeries(
void ODM_PhyStatusQuery(
PDM_ODM_T pDM_Odm,
- PODM_PHY_INFO_T pPhyInfo,
+ struct odm_phy_info *pPhyInfo,
u8 *pPhyStatus,
- PODM_PACKET_INFO_T pPktinfo
+ struct odm_packet_info *pPktinfo
)
{
diff --git a/drivers/staging/rtl8723bs/hal/odm_HWConfig.h b/drivers/staging/rtl8723bs/hal/odm_HWConfig.h
index f029922d12f0..fdb4f8579ff9 100644
--- a/drivers/staging/rtl8723bs/hal/odm_HWConfig.h
+++ b/drivers/staging/rtl8723bs/hal/odm_HWConfig.h
@@ -131,9 +131,9 @@ typedef struct _Phy_Status_Rpt_8812 {
void ODM_PhyStatusQuery(
PDM_ODM_T pDM_Odm,
- PODM_PHY_INFO_T pPhyInfo,
+ struct odm_phy_info *pPhyInfo,
u8 *pPhyStatus,
- PODM_PACKET_INFO_T pPktinfo
+ struct odm_packet_info *pPktinfo
);
HAL_STATUS ODM_ConfigRFWithTxPwrTrackHeaderFile(PDM_ODM_T pDM_Odm);
diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c
index d6cef9e8378d..0ce9b47d644d 100644
--- a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c
+++ b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c
@@ -433,13 +433,12 @@ s32 rtl8723b_FirmwareDownload(struct adapter *padapter, bool bUsedWoWLANFw)
goto exit;
}
- pFirmware->szFwBuffer = kzalloc(fw->size, GFP_KERNEL);
+ pFirmware->szFwBuffer = kmemdup(fw->data, fw->size, GFP_KERNEL);
if (!pFirmware->szFwBuffer) {
rtStatus = _FAIL;
goto exit;
}
- memcpy(pFirmware->szFwBuffer, fw->data, fw->size);
pFirmware->ulFwLength = fw->size;
release_firmware(fw);
if (pFirmware->ulFwLength > FW_8723B_SIZE) {
diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_rf6052.c b/drivers/staging/rtl8723bs/hal/rtl8723b_rf6052.c
index a71b552eca9a..f6aeb2630398 100644
--- a/drivers/staging/rtl8723bs/hal/rtl8723b_rf6052.c
+++ b/drivers/staging/rtl8723bs/hal/rtl8723b_rf6052.c
@@ -163,7 +163,7 @@ static int phy_RF6052_Config_ParaFile(struct adapter *Adapter)
break;
}
- /*----Restore RFENV control type----*/;
+ /*----Restore RFENV control type----*/
switch (eRFPath) {
case RF_PATH_A:
case RF_PATH_C:
diff --git a/drivers/staging/rtl8723bs/hal/rtl8723bs_recv.c b/drivers/staging/rtl8723bs/hal/rtl8723bs_recv.c
index d9a4567ca721..5d5cd4d01156 100644
--- a/drivers/staging/rtl8723bs/hal/rtl8723bs_recv.c
+++ b/drivers/staging/rtl8723bs/hal/rtl8723bs_recv.c
@@ -18,7 +18,6 @@
#include <rtw_debug.h>
#include <rtl8723b_hal.h>
-
static s32 initrecvbuf(struct recv_buf *precvbuf, struct adapter *padapter)
{
INIT_LIST_HEAD(&precvbuf->list);
@@ -29,9 +28,9 @@ static s32 initrecvbuf(struct recv_buf *precvbuf, struct adapter *padapter)
return _SUCCESS;
}
-static void update_recvframe_attrib(
- struct adapter *padapter, union recv_frame *precvframe, struct recv_stat *prxstat
-)
+static void update_recvframe_attrib(struct adapter *padapter,
+ union recv_frame *precvframe,
+ struct recv_stat *prxstat)
{
struct rx_pkt_attrib *pattrib;
struct recv_stat report;
@@ -48,7 +47,7 @@ static void update_recvframe_attrib(
memset(pattrib, 0, sizeof(struct rx_pkt_attrib));
/* update rx report to recv_frame attribute */
- pattrib->pkt_rpt_type = prxreport->c2h_ind?C2H_PACKET:NORMAL_RX;
+ pattrib->pkt_rpt_type = prxreport->c2h_ind ? C2H_PACKET : NORMAL_RX;
/* DBG_871X("%s: pkt_rpt_type =%d\n", __func__, pattrib->pkt_rpt_type); */
if (pattrib->pkt_rpt_type == NORMAL_RX) {
@@ -75,8 +74,9 @@ static void update_recvframe_attrib(
pattrib->mdata = (u8)prxreport->md;
pattrib->data_rate = (u8)prxreport->rx_rate;
- } else
+ } else {
pattrib->pkt_len = (u16)prxreport->pktlen;
+ }
}
/*
@@ -84,75 +84,92 @@ static void update_recvframe_attrib(
*Before calling this function,
*precvframe->u.hdr.rx_data should be ready!
*/
-static void update_recvframe_phyinfo(
- union recv_frame *precvframe, struct phy_stat *pphy_status
-)
+static void update_recvframe_phyinfo(union recv_frame *precvframe,
+ struct phy_stat *pphy_status)
{
struct adapter *padapter = precvframe->u.hdr.adapter;
struct rx_pkt_attrib *pattrib = &precvframe->u.hdr.attrib;
- struct hal_com_data *pHalData = GET_HAL_DATA(padapter);
- PODM_PHY_INFO_T pPHYInfo = (PODM_PHY_INFO_T)(&pattrib->phy_info);
+ struct hal_com_data *p_hal_data = GET_HAL_DATA(padapter);
+ struct odm_phy_info *p_phy_info =
+ (struct odm_phy_info *)(&pattrib->phy_info);
u8 *wlanhdr;
- ODM_PACKET_INFO_T pkt_info;
+ u8 *my_bssid;
+ u8 *rx_bssid;
+ u8 *rx_ra;
+ u8 *my_hwaddr;
u8 *sa = NULL;
+
+ struct odm_packet_info pkt_info = {
+ .data_rate = 0x00,
+ .station_id = 0x00,
+ .bssid_match = false,
+ .to_self = false,
+ .is_beacon = false,
+ };
+
/* _irqL irqL; */
struct sta_priv *pstapriv;
struct sta_info *psta;
- pkt_info.bPacketMatchBSSID = false;
- pkt_info.bPacketToSelf = false;
- pkt_info.bPacketBeacon = false;
-
-
wlanhdr = get_recvframe_data(precvframe);
+ my_bssid = get_bssid(&padapter->mlmepriv);
+ rx_bssid = get_hdr_bssid(wlanhdr);
+ pkt_info.bssid_match = ((!IsFrameTypeCtrl(wlanhdr)) &&
+ !pattrib->icv_err && !pattrib->crc_err &&
+ !ether_addr_equal(rx_bssid, my_bssid));
- pkt_info.bPacketMatchBSSID = ((!IsFrameTypeCtrl(wlanhdr)) &&
- !pattrib->icv_err && !pattrib->crc_err &&
- !memcmp(get_hdr_bssid(wlanhdr), get_bssid(&padapter->mlmepriv), ETH_ALEN));
+ rx_ra = get_ra(wlanhdr);
+ my_hwaddr = myid(&padapter->eeprompriv);
+ pkt_info.to_self = pkt_info.bssid_match &&
+ !ether_addr_equal(rx_ra, my_hwaddr);
- pkt_info.bPacketToSelf = pkt_info.bPacketMatchBSSID && (!memcmp(get_ra(wlanhdr), myid(&padapter->eeprompriv), ETH_ALEN));
- pkt_info.bPacketBeacon = pkt_info.bPacketMatchBSSID && (GetFrameSubType(wlanhdr) == WIFI_BEACON);
+ pkt_info.is_beacon = pkt_info.bssid_match &&
+ (GetFrameSubType(wlanhdr) == WIFI_BEACON);
sa = get_ta(wlanhdr);
- pkt_info.StationID = 0xFF;
+ pkt_info.station_id = 0xFF;
pstapriv = &padapter->stapriv;
psta = rtw_get_stainfo(pstapriv, sa);
if (psta) {
- pkt_info.StationID = psta->mac_id;
- /* DBG_8192C("%s ==> StationID(%d)\n", __func__, pkt_info.StationID); */
+ pkt_info.station_id = psta->mac_id;
+ /* DBG_8192C("%s ==> StationID(%d)\n",
+ * __func__, pkt_info.station_id); */
}
- pkt_info.DataRate = pattrib->data_rate;
+ pkt_info.data_rate = pattrib->data_rate;
/* rtl8723b_query_rx_phy_status(precvframe, pphy_status); */
- /* spin_lock_bh(&pHalData->odm_stainfo_lock); */
- ODM_PhyStatusQuery(&pHalData->odmpriv, pPHYInfo, (u8 *)pphy_status, &(pkt_info));
+ /* spin_lock_bh(&p_hal_data->odm_stainfo_lock); */
+ ODM_PhyStatusQuery(&p_hal_data->odmpriv, p_phy_info,
+ (u8 *)pphy_status, &(pkt_info));
if (psta)
psta->rssi = pattrib->phy_info.RecvSignalPower;
- /* spin_unlock_bh(&pHalData->odm_stainfo_lock); */
+ /* spin_unlock_bh(&p_hal_data->odm_stainfo_lock); */
precvframe->u.hdr.psta = NULL;
if (
- pkt_info.bPacketMatchBSSID &&
+ pkt_info.bssid_match &&
(check_fwstate(&padapter->mlmepriv, WIFI_AP_STATE) == true)
) {
if (psta) {
precvframe->u.hdr.psta = psta;
rtl8723b_process_phy_info(padapter, precvframe);
}
- } else if (pkt_info.bPacketToSelf || pkt_info.bPacketBeacon) {
- if (check_fwstate(&padapter->mlmepriv, WIFI_ADHOC_STATE|WIFI_ADHOC_MASTER_STATE) == true)
+ } else if (pkt_info.to_self || pkt_info.is_beacon) {
+ u32 adhoc_state = WIFI_ADHOC_STATE | WIFI_ADHOC_MASTER_STATE;
+ if (check_fwstate(&padapter->mlmepriv, adhoc_state))
if (psta)
precvframe->u.hdr.psta = psta;
rtl8723b_process_phy_info(padapter, precvframe);
}
}
-static void rtl8723bs_c2h_packet_handler(struct adapter *padapter, u8 *pbuf, u16 length)
+static void rtl8723bs_c2h_packet_handler(struct adapter *padapter,
+ u8 *pbuf, u16 length)
{
- u8 *tmpBuf = NULL;
+ u8 *tmp = NULL;
u8 res = false;
if (length == 0)
@@ -160,85 +177,127 @@ static void rtl8723bs_c2h_packet_handler(struct adapter *padapter, u8 *pbuf, u16
/* DBG_871X("+%s() length =%d\n", __func__, length); */
- tmpBuf = rtw_zmalloc(length);
- if (tmpBuf == NULL)
+ tmp = rtw_zmalloc(length);
+ if (tmp == NULL)
return;
- memcpy(tmpBuf, pbuf, length);
+ memcpy(tmp, pbuf, length);
- res = rtw_c2h_packet_wk_cmd(padapter, tmpBuf, length);
+ res = rtw_c2h_packet_wk_cmd(padapter, tmp, length);
if (res == false)
- kfree(tmpBuf);
+ kfree(tmp);
/* DBG_871X("-%s res(%d)\n", __func__, res); */
return;
}
+static inline union recv_frame *try_alloc_recvframe(struct recv_priv *precvpriv,
+ struct recv_buf *precvbuf)
+{
+ union recv_frame *precvframe;
+
+ precvframe = rtw_alloc_recvframe(&precvpriv->free_recv_queue);
+ if (!precvframe) {
+ DBG_8192C("%s: no enough recv frame!\n", __func__);
+ rtw_enqueue_recvbuf_to_head(precvbuf,
+ &precvpriv->recv_buf_pending_queue);
+
+ /* The case of can't allocte recvframe should be temporary, */
+ /* schedule again and hope recvframe is available next time. */
+ tasklet_schedule(&precvpriv->recv_tasklet);
+ }
+
+ return precvframe;
+}
+
+static inline bool rx_crc_err(struct recv_priv *precvpriv,
+ struct hal_com_data *p_hal_data,
+ struct rx_pkt_attrib *pattrib,
+ union recv_frame *precvframe)
+{
+ /* fix Hardware RX data error, drop whole recv_buffer */
+ if ((!(p_hal_data->ReceiveConfig & RCR_ACRC32)) && pattrib->crc_err) {
+ DBG_8192C("%s()-%d: RX Warning! rx CRC ERROR !!\n",
+ __func__, __LINE__);
+ rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
+ return true;
+ }
+
+ return false;
+}
+
+static inline bool pkt_exceeds_tail(struct recv_priv *precvpriv,
+ u8 *end, u8 *tail,
+ union recv_frame *precvframe)
+{
+ if (end > tail) {
+ DBG_8192C("%s()-%d: : next pkt len(%p,%d) exceed ptail(%p)!\n",
+ __func__, __LINE__, ptr, pkt_offset, precvbuf->ptail);
+ rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
+ return true;
+ }
+
+ return false;
+}
+
static void rtl8723bs_recv_tasklet(void *priv)
{
struct adapter *padapter;
- struct hal_com_data *pHalData;
+ struct hal_com_data *p_hal_data;
struct recv_priv *precvpriv;
struct recv_buf *precvbuf;
union recv_frame *precvframe;
struct rx_pkt_attrib *pattrib;
+ struct __queue *recv_buf_queue;
u8 *ptr;
u32 pkt_offset, skb_len, alloc_sz;
_pkt *pkt_copy = NULL;
u8 shift_sz = 0, rx_report_sz = 0;
-
padapter = priv;
- pHalData = GET_HAL_DATA(padapter);
+ p_hal_data = GET_HAL_DATA(padapter);
precvpriv = &padapter->recvpriv;
+ recv_buf_queue = &precvpriv->recv_buf_pending_queue;
do {
- precvbuf = rtw_dequeue_recvbuf(&precvpriv->recv_buf_pending_queue);
- if (NULL == precvbuf)
+ precvbuf = rtw_dequeue_recvbuf(recv_buf_queue);
+ if (!precvbuf)
break;
ptr = precvbuf->pdata;
while (ptr < precvbuf->ptail) {
- precvframe = rtw_alloc_recvframe(&precvpriv->free_recv_queue);
- if (precvframe == NULL) {
- DBG_8192C("%s: no enough recv frame!\n", __func__);
- rtw_enqueue_recvbuf_to_head(precvbuf, &precvpriv->recv_buf_pending_queue);
-
- /* The case of can't allocte recvframe should be temporary, */
- /* schedule again and hope recvframe is available next time. */
- tasklet_schedule(&precvpriv->recv_tasklet);
+ precvframe = try_alloc_recvframe(precvpriv, precvbuf);
+ if(!precvframe)
return;
- }
/* rx desc parsing */
- update_recvframe_attrib(padapter, precvframe, (struct recv_stat *)ptr);
+ update_recvframe_attrib(padapter, precvframe,
+ (struct recv_stat *)ptr);
pattrib = &precvframe->u.hdr.attrib;
- /* fix Hardware RX data error, drop whole recv_buffer */
- if ((!(pHalData->ReceiveConfig & RCR_ACRC32)) && pattrib->crc_err) {
- DBG_8192C("%s()-%d: RX Warning! rx CRC ERROR !!\n", __func__, __LINE__);
- rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
+ if(rx_crc_err(precvpriv, p_hal_data,
+ pattrib, precvframe))
break;
- }
rx_report_sz = RXDESC_SIZE + pattrib->drvinfo_sz;
- pkt_offset = rx_report_sz + pattrib->shift_sz + pattrib->pkt_len;
+ pkt_offset = rx_report_sz +
+ pattrib->shift_sz +
+ pattrib->pkt_len;
- if ((ptr + pkt_offset) > precvbuf->ptail) {
- DBG_8192C("%s()-%d: : next pkt len(%p,%d) exceed ptail(%p)!\n", __func__, __LINE__, ptr, pkt_offset, precvbuf->ptail);
- rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
+ if(pkt_exceeds_tail(precvpriv, ptr + pkt_offset,
+ precvbuf->ptail, precvframe))
break;
- }
if ((pattrib->crc_err) || (pattrib->icv_err)) {
- {
- DBG_8192C("%s: crc_err =%d icv_err =%d, skip!\n", __func__, pattrib->crc_err, pattrib->icv_err);
- }
- rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
+ DBG_8192C("%s: crc_err =%d icv_err =%d, skip!\n",
+ __func__, pattrib->crc_err,
+ pattrib->icv_err);
+ rtw_free_recvframe(precvframe,
+ &precvpriv->free_recv_queue);
} else {
/* Modified by Albert 20101213 */
/* For 8 bytes IP header alignment. */
@@ -289,7 +348,7 @@ static void rtl8723bs_recv_tasklet(void *priv)
skb_reset_tail_pointer(pkt_clone);
precvframe->u.hdr.rx_head = precvframe->u.hdr.rx_data = precvframe->u.hdr.rx_tail
= pkt_clone->data;
- precvframe->u.hdr.rx_end = pkt_clone->data + skb_len;
+ precvframe->u.hdr.rx_end = pkt_clone->data + skb_len;
} else {
DBG_8192C("%s: rtw_skb_clone fail\n", __func__);
rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
@@ -300,14 +359,14 @@ static void rtl8723bs_recv_tasklet(void *priv)
recvframe_put(precvframe, skb_len);
/* recvframe_pull(precvframe, drvinfo_sz + RXDESC_SIZE); */
- if (pHalData->ReceiveConfig & RCR_APPFCS)
+ if (p_hal_data->ReceiveConfig & RCR_APPFCS)
recvframe_pull_tail(precvframe, IEEE80211_FCS_LEN);
/* move to drv info position */
ptr += RXDESC_SIZE;
/* update drv info */
- if (pHalData->ReceiveConfig & RCR_APP_BA_SSN) {
+ if (p_hal_data->ReceiveConfig & RCR_APP_BA_SSN) {
/* rtl8723s_update_bassn(padapter, pdrvinfo); */
ptr += 4;
}
@@ -337,7 +396,6 @@ static void rtl8723bs_recv_tasklet(void *priv)
rtl8723bs_c2h_packet_handler(padapter, precvframe->u.hdr.rx_data, pattrib->pkt_len);
rtw_free_recvframe(precvframe, &precvpriv->free_recv_queue);
-
}
}
@@ -350,7 +408,6 @@ static void rtl8723bs_recv_tasklet(void *priv)
rtw_enqueue_recvbuf(precvbuf, &precvpriv->free_recv_buf_queue);
} while (1);
-
}
/*
@@ -366,7 +423,6 @@ s32 rtl8723bs_init_recv_priv(struct adapter *padapter)
struct recv_priv *precvpriv;
struct recv_buf *precvbuf;
-
res = _SUCCESS;
precvpriv = &padapter->recvpriv;
@@ -463,7 +519,6 @@ void rtl8723bs_free_recv_priv(struct adapter *padapter)
struct recv_priv *precvpriv;
struct recv_buf *precvbuf;
-
precvpriv = &padapter->recvpriv;
/* 3 1. kill tasklet */
diff --git a/drivers/staging/rtl8723bs/include/drv_types.h b/drivers/staging/rtl8723bs/include/drv_types.h
index 32129ac8e169..16b81b1a3f33 100644
--- a/drivers/staging/rtl8723bs/include/drv_types.h
+++ b/drivers/staging/rtl8723bs/include/drv_types.h
@@ -692,9 +692,9 @@ int rtw_suspend_wow(struct adapter *padapter);
int rtw_resume_process_wow(struct adapter *padapter);
#endif
-__inline static u8 *myid(struct eeprom_priv *peepriv)
+static inline u8 *myid(struct eeprom_priv *peepriv)
{
- return (peepriv->mac_addr);
+ return peepriv->mac_addr;
}
/* HCI Related header file */
diff --git a/drivers/staging/rtl8723bs/include/ieee80211.h b/drivers/staging/rtl8723bs/include/ieee80211.h
index 73ce63770c3c..c8e5251c2760 100644
--- a/drivers/staging/rtl8723bs/include/ieee80211.h
+++ b/drivers/staging/rtl8723bs/include/ieee80211.h
@@ -84,10 +84,6 @@ enum {
#define IEEE_PARAM_IEEE_802_1X 6
#define IEEE_PARAM_WPAX_SELECT 7
-#define AUTH_ALG_OPEN_SYSTEM 0x1
-#define AUTH_ALG_SHARED_KEY 0x2
-#define AUTH_ALG_LEAP 0x00000004
-
#define IEEE_MLME_STA_DEAUTH 1
#define IEEE_MLME_STA_DISASSOC 2
@@ -276,20 +272,6 @@ struct sta_data{
u64 tx_drops;
};
-#define IEEE80211_DATA_LEN 2304
-/* Maximum size for the MA-UNITDATA primitive, 802.11 standard section
- 6.2.1.1.2.
-
- The figure in section 7.1.2 suggests a body size of up to 2312
- bytes is allowed, which is a bit confusing, I suspect this
- represents the 2304 bytes of real data, plus a possible 8 bytes of
- WEP IV and ICV. (this interpretation suggested by Ramiro Barreiro) */
-
-
-#define IEEE80211_HLEN 30
-#define IEEE80211_FRAME_LEN (IEEE80211_DATA_LEN + IEEE80211_HLEN)
-
-
/* this is stolen from ipw2200 driver */
#define IEEE_IBSS_MAC_HASH_SIZE 31
@@ -317,75 +299,11 @@ enum eap_type {
EAPOL_ENCAP_ASF_ALERT
};
-#define IEEE80211_3ADDR_LEN 24
-#define IEEE80211_4ADDR_LEN 30
#define IEEE80211_FCS_LEN 4
#define MIN_FRAG_THRESHOLD 256U
#define MAX_FRAG_THRESHOLD 2346U
-/* Frame control field constants */
-#define RTW_IEEE80211_FCTL_VERS 0x0003
-#define RTW_IEEE80211_FCTL_FTYPE 0x000c
-#define RTW_IEEE80211_FCTL_STYPE 0x00f0
-#define RTW_IEEE80211_FCTL_TODS 0x0100
-#define RTW_IEEE80211_FCTL_FROMDS 0x0200
-#define RTW_IEEE80211_FCTL_MOREFRAGS 0x0400
-#define RTW_IEEE80211_FCTL_RETRY 0x0800
-#define RTW_IEEE80211_FCTL_PM 0x1000
-#define RTW_IEEE80211_FCTL_MOREDATA 0x2000
-#define RTW_IEEE80211_FCTL_PROTECTED 0x4000
-#define RTW_IEEE80211_FCTL_ORDER 0x8000
-#define RTW_IEEE80211_FCTL_CTL_EXT 0x0f00
-
-#define RTW_IEEE80211_FTYPE_MGMT 0x0000
-#define RTW_IEEE80211_FTYPE_CTL 0x0004
-#define RTW_IEEE80211_FTYPE_DATA 0x0008
-#define RTW_IEEE80211_FTYPE_EXT 0x000c
-
-/* management */
-#define RTW_IEEE80211_STYPE_ASSOC_REQ 0x0000
-#define RTW_IEEE80211_STYPE_ASSOC_RESP 0x0010
-#define RTW_IEEE80211_STYPE_REASSOC_REQ 0x0020
-#define RTW_IEEE80211_STYPE_REASSOC_RESP 0x0030
-#define RTW_IEEE80211_STYPE_PROBE_REQ 0x0040
-#define RTW_IEEE80211_STYPE_PROBE_RESP 0x0050
-#define RTW_IEEE80211_STYPE_BEACON 0x0080
-#define RTW_IEEE80211_STYPE_ATIM 0x0090
-#define RTW_IEEE80211_STYPE_DISASSOC 0x00A0
-#define RTW_IEEE80211_STYPE_AUTH 0x00B0
-#define RTW_IEEE80211_STYPE_DEAUTH 0x00C0
-#define RTW_IEEE80211_STYPE_ACTION 0x00D0
-
-/* control */
-#define RTW_IEEE80211_STYPE_CTL_EXT 0x0060
-#define RTW_IEEE80211_STYPE_BACK_REQ 0x0080
-#define RTW_IEEE80211_STYPE_BACK 0x0090
-#define RTW_IEEE80211_STYPE_PSPOLL 0x00A0
-#define RTW_IEEE80211_STYPE_RTS 0x00B0
-#define RTW_IEEE80211_STYPE_CTS 0x00C0
-#define RTW_IEEE80211_STYPE_ACK 0x00D0
-#define RTW_IEEE80211_STYPE_CFEND 0x00E0
-#define RTW_IEEE80211_STYPE_CFENDACK 0x00F0
-
-/* data */
-#define RTW_IEEE80211_STYPE_DATA 0x0000
-#define RTW_IEEE80211_STYPE_DATA_CFACK 0x0010
-#define RTW_IEEE80211_STYPE_DATA_CFPOLL 0x0020
-#define RTW_IEEE80211_STYPE_DATA_CFACKPOLL 0x0030
-#define RTW_IEEE80211_STYPE_NULLFUNC 0x0040
-#define RTW_IEEE80211_STYPE_CFACK 0x0050
-#define RTW_IEEE80211_STYPE_CFPOLL 0x0060
-#define RTW_IEEE80211_STYPE_CFACKPOLL 0x0070
-#define RTW_IEEE80211_STYPE_QOS_DATA 0x0080
-#define RTW_IEEE80211_STYPE_QOS_DATA_CFACK 0x0090
-#define RTW_IEEE80211_STYPE_QOS_DATA_CFPOLL 0x00A0
-#define RTW_IEEE80211_STYPE_QOS_DATA_CFACKPOLL 0x00B0
-#define RTW_IEEE80211_STYPE_QOS_NULLFUNC 0x00C0
-#define RTW_IEEE80211_STYPE_QOS_CFACK 0x00D0
-#define RTW_IEEE80211_STYPE_QOS_CFPOLL 0x00E0
-#define RTW_IEEE80211_STYPE_QOS_CFACKPOLL 0x00F0
-
/* sequence control field */
#define RTW_IEEE80211_SCTL_FRAG 0x000F
#define RTW_IEEE80211_SCTL_SEQ 0xFFF0
@@ -426,8 +344,8 @@ struct ieee80211_snap_hdr {
#define SNAP_SIZE sizeof(struct ieee80211_snap_hdr)
-#define WLAN_FC_GET_TYPE(fc) ((fc) & RTW_IEEE80211_FCTL_FTYPE)
-#define WLAN_FC_GET_STYPE(fc) ((fc) & RTW_IEEE80211_FCTL_STYPE)
+#define WLAN_FC_GET_TYPE(fc) ((fc) & IEEE80211_FCTL_FTYPE)
+#define WLAN_FC_GET_STYPE(fc) ((fc) & IEEE80211_FCTL_STYPE)
#define WLAN_QC_GET_TID(qc) ((qc) & 0x0f)
@@ -435,91 +353,23 @@ struct ieee80211_snap_hdr {
#define WLAN_GET_SEQ_SEQ(seq) ((seq) & RTW_IEEE80211_SCTL_SEQ)
/* Authentication algorithms */
-#define WLAN_AUTH_OPEN 0
-#define WLAN_AUTH_SHARED_KEY 1
-
-#define WLAN_AUTH_CHALLENGE_LEN 128
-
#define WLAN_CAPABILITY_BSS (1<<0)
-#define WLAN_CAPABILITY_IBSS (1<<1)
-#define WLAN_CAPABILITY_CF_POLLABLE (1<<2)
-#define WLAN_CAPABILITY_CF_POLL_REQUEST (1<<3)
-#define WLAN_CAPABILITY_PRIVACY (1<<4)
-#define WLAN_CAPABILITY_SHORT_PREAMBLE (1<<5)
-#define WLAN_CAPABILITY_PBCC (1<<6)
-#define WLAN_CAPABILITY_CHANNEL_AGILITY (1<<7)
#define WLAN_CAPABILITY_SHORT_SLOT (1<<10)
-/* Status codes */
-#define WLAN_STATUS_SUCCESS 0
-#define WLAN_STATUS_UNSPECIFIED_FAILURE 1
-#define WLAN_STATUS_CAPS_UNSUPPORTED 10
-#define WLAN_STATUS_REASSOC_NO_ASSOC 11
-#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12
-#define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13
-#define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14
-#define WLAN_STATUS_CHALLENGE_FAIL 15
-#define WLAN_STATUS_AUTH_TIMEOUT 16
-#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17
-#define WLAN_STATUS_ASSOC_DENIED_RATES 18
/* 802.11b */
#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19
-#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20
-#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21
/* Reason codes */
-#define WLAN_REASON_UNSPECIFIED 1
-#define WLAN_REASON_PREV_AUTH_NOT_VALID 2
-#define WLAN_REASON_DEAUTH_LEAVING 3
-#define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4
-#define WLAN_REASON_DISASSOC_AP_BUSY 5
-#define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6
-#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7
-#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8
-#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9
#define WLAN_REASON_ACTIVE_ROAM 65533
#define WLAN_REASON_JOIN_WRONG_CHANNEL 65534
#define WLAN_REASON_EXPIRATION_CHK 65535
-/* Information Element IDs */
-#define WLAN_EID_SSID 0
-#define WLAN_EID_SUPP_RATES 1
-#define WLAN_EID_FH_PARAMS 2
-#define WLAN_EID_DS_PARAMS 3
-#define WLAN_EID_CF_PARAMS 4
-#define WLAN_EID_TIM 5
-#define WLAN_EID_IBSS_PARAMS 6
-#define WLAN_EID_CHALLENGE 16
-/* EIDs defined by IEEE 802.11h - START */
-#define WLAN_EID_PWR_CONSTRAINT 32
-#define WLAN_EID_PWR_CAPABILITY 33
-#define WLAN_EID_TPC_REQUEST 34
-#define WLAN_EID_TPC_REPORT 35
-#define WLAN_EID_SUPPORTED_CHANNELS 36
-#define WLAN_EID_CHANNEL_SWITCH 37
-#define WLAN_EID_MEASURE_REQUEST 38
-#define WLAN_EID_MEASURE_REPORT 39
-#define WLAN_EID_QUITE 40
-#define WLAN_EID_IBSS_DFS 41
/* EIDs defined by IEEE 802.11h - END */
-#define WLAN_EID_ERP_INFO 42
#define WLAN_EID_HT_CAP 45
-#define WLAN_EID_RSN 48
-#define WLAN_EID_EXT_SUPP_RATES 50
-#define WLAN_EID_MOBILITY_DOMAIN 54
-#define WLAN_EID_FAST_BSS_TRANSITION 55
-#define WLAN_EID_TIMEOUT_INTERVAL 56
-#define WLAN_EID_RIC_DATA 57
-#define WLAN_EID_HT_OPERATION 61
-#define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62
#define WLAN_EID_20_40_BSS_COEXISTENCE 72
#define WLAN_EID_20_40_BSS_INTOLERANT 73
#define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74
-#define WLAN_EID_MMIE 76
-#define WLAN_EID_VENDOR_SPECIFIC 221
#define WLAN_EID_GENERIC (WLAN_EID_VENDOR_SPECIFIC)
-#define WLAN_EID_VHT_CAPABILITY 191
-#define WLAN_EID_VHT_OPERATION 192
#define WLAN_EID_VHT_OP_MODE_NOTIFY 199
#define IEEE80211_MGMT_HDR_LEN 24
diff --git a/drivers/staging/rtl8723bs/include/rtw_mlme.h b/drivers/staging/rtl8723bs/include/rtw_mlme.h
index 00b3d92c9f51..2e4f12b54929 100644
--- a/drivers/staging/rtl8723bs/include/rtw_mlme.h
+++ b/drivers/staging/rtl8723bs/include/rtw_mlme.h
@@ -299,7 +299,7 @@ struct wifidirect_info{
struct tdls_ss_record{ /* signal strength record */
u8 macaddr[ETH_ALEN];
- u8 RxPWDBAll;
+ u8 rx_pwd_ba11;
u8 is_tdls_sta; /* true: direct link sta, false: else */
};
diff --git a/drivers/staging/rtl8723bs/include/rtw_recv.h b/drivers/staging/rtl8723bs/include/rtw_recv.h
index 71039ca79e4b..d4986f5685c5 100644
--- a/drivers/staging/rtl8723bs/include/rtw_recv.h
+++ b/drivers/staging/rtl8723bs/include/rtw_recv.h
@@ -99,20 +99,20 @@ struct signal_stat {
};
struct phy_info {
- u8 RxPWDBAll;
+ u8 rx_pwd_ba11;
u8 SignalQuality; /* in 0-100 index. */
- s8 RxMIMOSignalQuality[4]; /* per-path's EVM */
+ s8 rx_mimo_signal_quality[4]; /* per-path's EVM */
u8 RxMIMOEVMdbm[4]; /* per-path's EVM dbm */
- u8 RxMIMOSignalStrength[4];/* in 0~100 index */
+ u8 rx_mimo_signal_strength[4];/* in 0~100 index */
u16 Cfo_short[4]; /* per-path's Cfo_short */
u16 Cfo_tail[4]; /* per-path's Cfo_tail */
s8 RxPower; /* in dBm Translate from PWdB */
s8 RecvSignalPower;/* Real power in dBm for this packet, no beautification and aggregation. Keep this raw info to be used for the other procedures. */
- u8 BTRxRSSIPercentage;
+ u8 bt_rx_rssi_percentage;
u8 SignalStrength; /* in 0-100 index. */
s8 RxPwr[4]; /* per-path's pwdb */
@@ -187,7 +187,7 @@ struct rx_pkt_attrib {
u8 signal_qual;
s8 rx_mimo_signal_qual[2];
u8 signal_strength;
- u32 RxPWDBAll;
+ u32 rx_pwd_ba11;
s32 RecvSignalPower;
*/
struct phy_info phy_info;
diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c
index 51d48de24a24..46bc2e512557 100644
--- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c
+++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c
@@ -14,6 +14,7 @@
******************************************************************************/
#define _IOCTL_CFG80211_C_
+#include <linux/etherdevice.h>
#include <drv_types.h>
#include <rtw_debug.h>
#include <linux/jiffies.h>
@@ -2391,7 +2392,7 @@ static int cfg80211_rtw_del_pmksa(struct wiphy *wiphy,
{
if (!memcmp(psecuritypriv->PMKIDList[index].Bssid, (u8 *)pmksa->bssid, ETH_ALEN))
{ /* BSSID is matched, the same AP => Remove this PMKID information and reset it. */
- memset(psecuritypriv->PMKIDList[index].Bssid, 0x00, ETH_ALEN);
+ eth_zero_addr(psecuritypriv->PMKIDList[index].Bssid);
memset(psecuritypriv->PMKIDList[index].PMKID, 0x00, WLAN_PMKID_LEN);
psecuritypriv->PMKIDList[index].bUsed = false;
bMatched = true;
@@ -2516,7 +2517,7 @@ static int rtw_cfg80211_monitor_if_xmit_entry(struct sk_buff *skb, struct net_de
dot11_hdr = (struct ieee80211_hdr *)skb->data;
frame_control = le16_to_cpu(dot11_hdr->frame_control);
/* Check if the QoS bit is set */
- if ((frame_control & RTW_IEEE80211_FCTL_FTYPE) == RTW_IEEE80211_FTYPE_DATA) {
+ if ((frame_control & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) {
/* Check if this ia a Wireless Distribution System (WDS) frame
* which has 4 MAC addresses
*/
@@ -2544,8 +2545,8 @@ static int rtw_cfg80211_monitor_if_xmit_entry(struct sk_buff *skb, struct net_de
return ret;
}
- else if ((frame_control & (RTW_IEEE80211_FCTL_FTYPE|RTW_IEEE80211_FCTL_STYPE))
- == (RTW_IEEE80211_FTYPE_MGMT|RTW_IEEE80211_STYPE_ACTION)
+ else if ((frame_control & (IEEE80211_FCTL_FTYPE|IEEE80211_FCTL_STYPE))
+ == (IEEE80211_FTYPE_MGMT|IEEE80211_STYPE_ACTION)
)
{
/* only for action frames */
@@ -2606,7 +2607,7 @@ static int rtw_cfg80211_monitor_if_xmit_entry(struct sk_buff *skb, struct net_de
}
else
{
- DBG_8192C("frame_control = 0x%x\n", frame_control & (RTW_IEEE80211_FCTL_FTYPE|RTW_IEEE80211_FCTL_STYPE));
+ DBG_8192C("frame_control = 0x%x\n", frame_control & (IEEE80211_FCTL_FTYPE|IEEE80211_FCTL_STYPE));
}
@@ -3255,7 +3256,7 @@ static int cfg80211_rtw_sched_scan_start(struct wiphy *wiphy,
struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
- u8 ret;
+ int ret;
if (padapter->bup == false) {
DBG_871X("%s: net device is down.\n", __func__);
diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_linux.c b/drivers/staging/rtl8723bs/os_dep/ioctl_linux.c
index cc18d0ad7d7b..b26533983864 100644
--- a/drivers/staging/rtl8723bs/os_dep/ioctl_linux.c
+++ b/drivers/staging/rtl8723bs/os_dep/ioctl_linux.c
@@ -14,6 +14,7 @@
******************************************************************************/
#define _IOCTL_LINUX_C_
+#include <linux/etherdevice.h>
#include <drv_types.h>
#include <rtw_debug.h>
#include <rtw_mp.h>
@@ -124,7 +125,7 @@ void rtw_indicate_wx_disassoc_event(struct adapter *padapter)
memset(&wrqu, 0, sizeof(union iwreq_data));
wrqu.ap_addr.sa_family = ARPHRD_ETHER;
- memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
+ eth_zero_addr(wrqu.ap_addr.sa_data);
}
/*
@@ -475,26 +476,26 @@ static int wpa_set_auth_algs(struct net_device *dev, u32 value)
struct adapter *padapter = (struct adapter *) rtw_netdev_priv(dev);
int ret = 0;
- if ((value & AUTH_ALG_SHARED_KEY) && (value & AUTH_ALG_OPEN_SYSTEM)) {
- DBG_871X("wpa_set_auth_algs, AUTH_ALG_SHARED_KEY and AUTH_ALG_OPEN_SYSTEM [value:0x%x]\n", value);
+ if ((value & WLAN_AUTH_SHARED_KEY) && (value & WLAN_AUTH_OPEN)) {
+ DBG_871X("wpa_set_auth_algs, WLAN_AUTH_SHARED_KEY and WLAN_AUTH_OPEN [value:0x%x]\n", value);
padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled;
padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeAutoSwitch;
padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_Auto;
- } else if (value & AUTH_ALG_SHARED_KEY) {
- DBG_871X("wpa_set_auth_algs, AUTH_ALG_SHARED_KEY [value:0x%x]\n", value);
+ } else if (value & WLAN_AUTH_SHARED_KEY) {
+ DBG_871X("wpa_set_auth_algs, WLAN_AUTH_SHARED_KEY [value:0x%x]\n", value);
padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled;
padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeShared;
padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_Shared;
- } else if (value & AUTH_ALG_OPEN_SYSTEM) {
- DBG_871X("wpa_set_auth_algs, AUTH_ALG_OPEN_SYSTEM\n");
+ } else if (value & WLAN_AUTH_OPEN) {
+ DBG_871X("wpa_set_auth_algs, WLAN_AUTH_OPEN\n");
/* padapter->securitypriv.ndisencryptstatus = Ndis802_11EncryptionDisabled; */
if (padapter->securitypriv.ndisauthtype < Ndis802_11AuthModeWPAPSK) {
padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeOpen;
padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_Open;
}
- } else if (value & AUTH_ALG_LEAP) {
- DBG_871X("wpa_set_auth_algs, AUTH_ALG_LEAP\n");
+ } else if (value & WLAN_AUTH_LEAP) {
+ DBG_871X("wpa_set_auth_algs, WLAN_AUTH_LEAP\n");
} else {
DBG_871X("wpa_set_auth_algs, error!\n");
ret = -EINVAL;
@@ -1080,7 +1081,7 @@ static int rtw_wx_set_pmkid(struct net_device *dev,
for (j = 0 ; j<NUM_PMKID_CACHE; j++) {
if (!memcmp(psecuritypriv->PMKIDList[j].Bssid, strIssueBssid, ETH_ALEN)) {
/* BSSID is matched, the same AP => Remove this PMKID information and reset it. */
- memset(psecuritypriv->PMKIDList[ j ].Bssid, 0x00, ETH_ALEN);
+ eth_zero_addr(psecuritypriv->PMKIDList[j].Bssid);
psecuritypriv->PMKIDList[ j ].bUsed = false;
break;
}
@@ -1294,7 +1295,7 @@ static int rtw_wx_get_wap(struct net_device *dev,
wrqu->ap_addr.sa_family = ARPHRD_ETHER;
- memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN);
+ eth_zero_addr(wrqu->ap_addr.sa_data);
RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_wx_get_wap\n"));
@@ -1303,7 +1304,7 @@ static int rtw_wx_get_wap(struct net_device *dev,
((check_fwstate(pmlmepriv, WIFI_AP_STATE)) == true)) {
memcpy(wrqu->ap_addr.sa_data, pcur_bss->MacAddress, ETH_ALEN);
} else {
- memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN);
+ eth_zero_addr(wrqu->ap_addr.sa_data);
}
return 0;
@@ -2130,75 +2131,65 @@ static int rtw_wx_set_gen_ie(struct net_device *dev,
}
static int rtw_wx_set_auth(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
{
struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
struct iw_param *param = (struct iw_param*)&(wrqu->param);
int ret = 0;
switch (param->flags & IW_AUTH_INDEX) {
-
case IW_AUTH_WPA_VERSION:
break;
case IW_AUTH_CIPHER_PAIRWISE:
-
break;
case IW_AUTH_CIPHER_GROUP:
-
break;
case IW_AUTH_KEY_MGMT:
/*
* ??? does not use these parameters
*/
break;
-
case IW_AUTH_TKIP_COUNTERMEASURES:
- {
- if (param->value) {
- /* wpa_supplicant is enabling the tkip countermeasure. */
+ /* wpa_supplicant is setting the tkip countermeasure. */
+ if (param->value) /* enabling */
padapter->securitypriv.btkip_countermeasure = true;
- } else {
- /* wpa_supplicant is disabling the tkip countermeasure. */
+ else /* disabling */
padapter->securitypriv.btkip_countermeasure = false;
- }
break;
- }
case IW_AUTH_DROP_UNENCRYPTED:
- {
- /* HACK:
- *
- * wpa_supplicant calls set_wpa_enabled when the driver
- * is loaded and unloaded, regardless of if WPA is being
- * used. No other calls are made which can be used to
- * determine if encryption will be used or not prior to
- * association being expected. If encryption is not being
- * used, drop_unencrypted is set to false, else true -- we
- * can use this to determine if the CAP_PRIVACY_ON bit should
- * be set.
- */
-
- if (padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption1Enabled) {
- break;/* it means init value, or using wep, ndisencryptstatus = Ndis802_11Encryption1Enabled, */
- /* then it needn't reset it; */
- }
-
- if (param->value) {
- padapter->securitypriv.ndisencryptstatus = Ndis802_11EncryptionDisabled;
- padapter->securitypriv.dot11PrivacyAlgrthm = _NO_PRIVACY_;
- padapter->securitypriv.dot118021XGrpPrivacy = _NO_PRIVACY_;
- padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_Open; /* open system */
- padapter->securitypriv.ndisauthtype =Ndis802_11AuthModeOpen;
- }
+ /* HACK:
+ *
+ * wpa_supplicant calls set_wpa_enabled when the driver
+ * is loaded and unloaded, regardless of if WPA is being
+ * used. No other calls are made which can be used to
+ * determine if encryption will be used or not prior to
+ * association being expected. If encryption is not being
+ * used, drop_unencrypted is set to false, else true -- we
+ * can use this to determine if the CAP_PRIVACY_ON bit should
+ * be set.
+ */
+ /*
+ * This means init value, or using wep, ndisencryptstatus =
+ * Ndis802_11Encryption1Enabled, then it needn't reset it;
+ */
+ if (padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption1Enabled)
break;
+
+ if (param->value) {
+ padapter->securitypriv.ndisencryptstatus = Ndis802_11EncryptionDisabled;
+ padapter->securitypriv.dot11PrivacyAlgrthm = _NO_PRIVACY_;
+ padapter->securitypriv.dot118021XGrpPrivacy = _NO_PRIVACY_;
+ padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_Open; /* open system */
+ padapter->securitypriv.ndisauthtype =Ndis802_11AuthModeOpen;
}
+ break;
case IW_AUTH_80211_AUTH_ALG:
-
/*
* It's the starting point of a link layer connection using wpa_supplicant
- */
+ */
if (check_fwstate(&padapter->mlmepriv, _FW_LINKED)) {
LeaveAllPowerSaveMode(padapter);
rtw_disassoc_cmd(padapter, 500, false);
@@ -2207,11 +2198,8 @@ static int rtw_wx_set_auth(struct net_device *dev,
rtw_free_assoc_resources(padapter, 1);
}
-
ret = wpa_set_auth_algs(dev, (u32)param->value);
-
break;
-
case IW_AUTH_WPA_ENABLED:
break;
case IW_AUTH_RX_UNENCRYPTED_EAPOL:
@@ -2221,6 +2209,7 @@ static int rtw_wx_set_auth(struct net_device *dev,
default:
return -EOPNOTSUPP;
}
+
return ret;
}
diff --git a/drivers/staging/rtl8723bs/os_dep/recv_linux.c b/drivers/staging/rtl8723bs/os_dep/recv_linux.c
index e804b430931c..b43e24c3a23a 100644
--- a/drivers/staging/rtl8723bs/os_dep/recv_linux.c
+++ b/drivers/staging/rtl8723bs/os_dep/recv_linux.c
@@ -43,6 +43,7 @@ void rtw_os_recv_resource_free(struct recv_priv *precvpriv)
{
sint i;
union recv_frame *precvframe;
+
precvframe = (union recv_frame*) precvpriv->precv_frame_buf;
for (i = 0; i < NR_RECVFRAME; i++)
@@ -181,11 +182,11 @@ void rtw_os_recv_indicate_pkt(struct adapter *padapter, _pkt *pkt, struct rx_pkt
pkt->dev = padapter->pnetdev;
#ifdef CONFIG_TCP_CSUM_OFFLOAD_RX
- if ((pattrib->tcpchk_valid == 1) && (pattrib->tcp_chkrpt == 1)) {
+ if ((pattrib->tcpchk_valid == 1) && (pattrib->tcp_chkrpt == 1))
pkt->ip_summed = CHECKSUM_UNNECESSARY;
- } else {
+ else
pkt->ip_summed = CHECKSUM_NONE;
- }
+
#else /* !CONFIG_TCP_CSUM_OFFLOAD_RX */
pkt->ip_summed = CHECKSUM_NONE;
#endif /* CONFIG_TCP_CSUM_OFFLOAD_RX */
diff --git a/drivers/staging/rtl8723bs/os_dep/rtw_proc.c b/drivers/staging/rtl8723bs/os_dep/rtw_proc.c
index 9a885e626d1c..49c8684dc25b 100644
--- a/drivers/staging/rtl8723bs/os_dep/rtw_proc.c
+++ b/drivers/staging/rtl8723bs/os_dep/rtw_proc.c
@@ -19,7 +19,7 @@
#ifdef PROC_DEBUG
-static struct proc_dir_entry *rtw_proc = NULL;
+static struct proc_dir_entry *rtw_proc;
#define RTW_PROC_NAME "rtl8723bs"
@@ -142,7 +142,7 @@ int rtw_drv_proc_init(void)
goto exit;
}
- for (i = 0;i<drv_proc_hdls_num;i++) {
+ for (i = 0; i < drv_proc_hdls_num; i++) {
entry = rtw_proc_create_entry(drv_proc_hdls[i].name, rtw_proc, &rtw_drv_proc_fops, (void *)i);
if (!entry) {
rtw_warn_on(1);
@@ -163,7 +163,7 @@ void rtw_drv_proc_deinit(void)
if (rtw_proc == NULL)
return;
- for (i = 0;i<drv_proc_hdls_num;i++)
+ for (i = 0; i < drv_proc_hdls_num; i++)
remove_proc_entry(drv_proc_hdls[i].name, rtw_proc);
remove_proc_entry(RTW_PROC_NAME, get_proc_net);
@@ -341,7 +341,7 @@ static int proc_get_cam_cache(struct seq_file *m, void *v)
/* "MK", "GK", "MFB", "valid" */
);
- for (i = 0;i<32;i++) {
+ for (i = 0; i < 32; i++) {
if (dvobj->cam_cache[i].ctrl != 0)
DBG_871X_SEL_NL(m, "%2u 0x%04x "MAC_FMT" "KEY_FMT" %3u %-7s"
/* %2u %2u 0x%02x %5u" */
@@ -663,7 +663,7 @@ static struct proc_dir_entry *rtw_odm_proc_init(struct net_device *dev)
adapter->dir_odm = dir_odm;
- for (i = 0;i<odm_proc_hdls_num;i++) {
+ for (i = 0; i < odm_proc_hdls_num; i++) {
entry = rtw_proc_create_entry(odm_proc_hdls[i].name, dir_odm, &rtw_odm_proc_fops, (void *)i);
if (!entry) {
rtw_warn_on(1);
@@ -687,7 +687,7 @@ static void rtw_odm_proc_deinit(struct adapter *adapter)
return;
}
- for (i = 0;i<odm_proc_hdls_num;i++)
+ for (i = 0; i < odm_proc_hdls_num; i++)
remove_proc_entry(odm_proc_hdls[i].name, dir_odm);
remove_proc_entry("odm", adapter->dir_dev);
@@ -721,7 +721,7 @@ struct proc_dir_entry *rtw_adapter_proc_init(struct net_device *dev)
adapter->dir_dev = dir_dev;
- for (i = 0;i<adapter_proc_hdls_num;i++) {
+ for (i = 0; i < adapter_proc_hdls_num; i++) {
entry = rtw_proc_create_entry(adapter_proc_hdls[i].name, dir_dev, &rtw_adapter_proc_fops, (void *)i);
if (!entry) {
rtw_warn_on(1);
@@ -749,7 +749,7 @@ void rtw_adapter_proc_deinit(struct net_device *dev)
return;
}
- for (i = 0;i<adapter_proc_hdls_num;i++)
+ for (i = 0; i < adapter_proc_hdls_num; i++)
remove_proc_entry(adapter_proc_hdls[i].name, dir_dev);
rtw_odm_proc_deinit(adapter);
@@ -773,7 +773,7 @@ void rtw_adapter_proc_replace(struct net_device *dev)
return;
}
- for (i = 0;i<adapter_proc_hdls_num;i++)
+ for (i = 0; i < adapter_proc_hdls_num; i++)
remove_proc_entry(adapter_proc_hdls[i].name, dir_dev);
rtw_odm_proc_deinit(adapter);
diff --git a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c
index 943324877707..99c407ba0874 100644
--- a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c
+++ b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c
@@ -337,7 +337,7 @@ static struct adapter *rtw_sdio_if1_init(struct dvobj_priv *dvobj, const struct
struct adapter *padapter = NULL;
PSDIO_DATA psdio = &dvobj->intf_data;
- padapter = (struct adapter *)vzalloc(sizeof(*padapter));
+ padapter = vzalloc(sizeof(*padapter));
if (padapter == NULL) {
goto exit;
}
diff --git a/drivers/staging/rtl8723bs/os_dep/xmit_linux.c b/drivers/staging/rtl8723bs/os_dep/xmit_linux.c
index f29e110f9bdb..21e1b811f997 100644
--- a/drivers/staging/rtl8723bs/os_dep/xmit_linux.c
+++ b/drivers/staging/rtl8723bs/os_dep/xmit_linux.c
@@ -64,9 +64,7 @@ int rtw_os_xmit_resource_alloc(struct adapter *padapter, struct xmit_buf *pxmitb
if (alloc_sz > 0) {
pxmitbuf->pallocated_buf = rtw_zmalloc(alloc_sz);
if (pxmitbuf->pallocated_buf == NULL)
- {
return _FAIL;
- }
pxmitbuf->pbuf = (u8 *)N_BYTE_ALIGMENT((SIZE_PTR)(pxmitbuf->pallocated_buf), XMITBUF_ALIGN_SZ);
}
@@ -90,10 +88,8 @@ void rtw_os_pkt_complete(struct adapter *padapter, _pkt *pkt)
queue = skb_get_queue_mapping(pkt);
if (padapter->registrypriv.wifi_spec) {
if (__netif_subqueue_stopped(padapter->pnetdev, queue) &&
- (pxmitpriv->hwxmits[queue].accnt < WMM_XMIT_THRESHOLD))
- {
+ (pxmitpriv->hwxmits[queue].accnt < WMM_XMIT_THRESHOLD))
netif_wake_subqueue(padapter->pnetdev, queue);
- }
} else {
if (__netif_subqueue_stopped(padapter->pnetdev, queue))
netif_wake_subqueue(padapter->pnetdev, queue);
@@ -177,18 +173,15 @@ static int rtw_mlcst2unicst(struct adapter *padapter, struct sk_buff *skb)
for (i = 0; i < chk_alive_num; i++) {
psta = rtw_get_stainfo_by_offset(pstapriv, chk_alive_list[i]);
- if (!(psta->state & _FW_LINKED))
- {
+ if (!(psta->state & _FW_LINKED)) {
DBG_COUNTER(padapter->tx_logs.os_tx_m2u_ignore_fw_linked);
continue;
}
/* avoid come from STA1 and send back STA1 */
- if (!memcmp(psta->hwaddr, &skb->data[6], 6)
- || !memcmp(psta->hwaddr, null_addr, 6)
- || !memcmp(psta->hwaddr, bc_addr, 6)
- )
- {
+ if (!memcmp(psta->hwaddr, &skb->data[6], 6) ||
+ !memcmp(psta->hwaddr, null_addr, 6) ||
+ !memcmp(psta->hwaddr, bc_addr, 6)) {
DBG_COUNTER(padapter->tx_logs.os_tx_m2u_ignore_self);
continue;
}
@@ -248,14 +241,11 @@ int _rtw_xmit_entry(_pkt *pkt, _nic_hdl pnetdev)
|| is_broadcast_mac_addr(pkt->data)
#endif
)
- && (padapter->registrypriv.wifi_spec == 0)
- )
- {
+ && padapter->registrypriv.wifi_spec == 0) {
if (pxmitpriv->free_xmitframe_cnt > (NR_XMITFRAME/4)) {
res = rtw_mlcst2unicst(padapter, pkt);
- if (res == true) {
+ if (res == true)
goto exit;
- }
} else {
/* DBG_871X("Stop M2U(%d, %d)! ", pxmitpriv->free_xmitframe_cnt, pxmitpriv->free_xmitbuf_cnt); */
/* DBG_871X("!m2u); */
diff --git a/drivers/staging/rtlwifi/base.c b/drivers/staging/rtlwifi/base.c
index c947def37d31..eea00035a735 100644
--- a/drivers/staging/rtlwifi/base.c
+++ b/drivers/staging/rtlwifi/base.c
@@ -710,20 +710,20 @@ u8 rtl_mrate_idx_to_arfr_id(
ret = RATEID_IDX_BGN_40M_1SS;
else
ret = RATEID_IDX_BGN_40M_2SS;
- ; break;
+ break;
case RATR_INX_WIRELESS_N:
case RATR_INX_WIRELESS_NG:
if (rtlphy->rf_type == RF_1T1R)
ret = RATEID_IDX_GN_N1SS;
else
ret = RATEID_IDX_GN_N2SS;
- ; break;
+ break;
case RATR_INX_WIRELESS_NB:
if (rtlphy->rf_type == RF_1T1R)
ret = RATEID_IDX_BGN_20M_1SS_BN;
else
ret = RATEID_IDX_BGN_20M_2SS_BN;
- ; break;
+ break;
case RATR_INX_WIRELESS_GB:
ret = RATEID_IDX_BG;
break;
diff --git a/drivers/staging/rtlwifi/btcoexist/halbtc8822b2ant.c b/drivers/staging/rtlwifi/btcoexist/halbtc8822b2ant.c
index ffff5b062672..5b826403ed66 100644
--- a/drivers/staging/rtlwifi/btcoexist/halbtc8822b2ant.c
+++ b/drivers/staging/rtlwifi/btcoexist/halbtc8822b2ant.c
@@ -2885,12 +2885,8 @@ static void halbtc8822b2ant_action_a2dp(struct btc_coexist *btcoexist)
NORMAL_EXEC, 5);
} else {
- if (wifi_turbo)
- halbtc8822b2ant_coex_table_with_type(
- btcoexist, NORMAL_EXEC, 10);
- else
- halbtc8822b2ant_coex_table_with_type(
- btcoexist, NORMAL_EXEC, 10);
+ halbtc8822b2ant_coex_table_with_type(btcoexist,
+ NORMAL_EXEC, 10);
halbtc8822b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true,
109);
diff --git a/drivers/staging/rtlwifi/pci.c b/drivers/staging/rtlwifi/pci.c
index 70a64a5f564a..d56810eabde7 100644
--- a/drivers/staging/rtlwifi/pci.c
+++ b/drivers/staging/rtlwifi/pci.c
@@ -31,7 +31,6 @@
#include "efuse.h"
#include <linux/interrupt.h>
#include <linux/export.h>
-#include <linux/kmemleak.h>
#include <linux/module.h>
MODULE_AUTHOR("lizhaoming <chaoming_li@realsil.com.cn>");
diff --git a/drivers/staging/rtlwifi/phydm/phydm_features.h b/drivers/staging/rtlwifi/phydm/phydm_features.h
index 37f6f0cd7235..a12361c6a1a0 100644
--- a/drivers/staging/rtlwifi/phydm/phydm_features.h
+++ b/drivers/staging/rtlwifi/phydm/phydm_features.h
@@ -24,7 +24,7 @@
*****************************************************************************/
#ifndef __PHYDM_FEATURES_H__
-#define __PHYDM_FEATURES
+#define __PHYDM_FEATURES_H__
/*phydm debyg report & tools*/
diff --git a/drivers/staging/rtlwifi/phydm/phydm_kfree.h b/drivers/staging/rtlwifi/phydm/phydm_kfree.h
index 1ee60059afc1..fa1627e3662d 100644
--- a/drivers/staging/rtlwifi/phydm/phydm_kfree.h
+++ b/drivers/staging/rtlwifi/phydm/phydm_kfree.h
@@ -24,7 +24,7 @@
*****************************************************************************/
#ifndef __PHYDMKFREE_H__
-#define __PHYDKFREE_H__
+#define __PHYDMKFREE_H__
#define KFREE_VERSION "1.0"
diff --git a/drivers/staging/rtlwifi/phydm/phydm_rainfo.c b/drivers/staging/rtlwifi/phydm/phydm_rainfo.c
index 8c08c76d4eda..e10a91aeebee 100644
--- a/drivers/staging/rtlwifi/phydm/phydm_rainfo.c
+++ b/drivers/staging/rtlwifi/phydm/phydm_rainfo.c
@@ -142,7 +142,6 @@ void odm_c2h_ra_para_report_handler(void *dm_void, u8 *cmd_buf, u8 cmd_len)
"SGI_support =", cmd_buf[7]);
ODM_RT_TRACE(dm, ODM_FW_DEBUG_TRACE, "%5s %d\n",
"Rate_ID =", cmd_buf[8]);
- ;
}
ODM_RT_TRACE(dm, ODM_FW_DEBUG_TRACE,
"-------------------------------\n");
diff --git a/drivers/staging/rtlwifi/phydm/rtl8822b/phydm_iqk_8822b.c b/drivers/staging/rtlwifi/phydm/rtl8822b/phydm_iqk_8822b.c
index d320311213cc..e2c72af16150 100644
--- a/drivers/staging/rtlwifi/phydm/rtl8822b/phydm_iqk_8822b.c
+++ b/drivers/staging/rtlwifi/phydm/rtl8822b/phydm_iqk_8822b.c
@@ -1311,7 +1311,7 @@ static void _phy_iq_calibrate_8822b(struct phy_dm_struct *dm, bool reset)
iqk_info->kcount = 0;
ODM_RT_TRACE(dm, ODM_COMP_CALIBRATION, "[IQK]delay 50ms!!!\n");
ODM_delay_ms(50);
- };
+ }
_iqk_backup_iqk_8822b(dm, 1);
_iqk_fill_iqk_report_8822b(dm, 0);
diff --git a/drivers/staging/rtlwifi/rtl8822be/phy.c b/drivers/staging/rtlwifi/rtl8822be/phy.c
index ef37ae98c803..6697aee9317f 100644
--- a/drivers/staging/rtlwifi/rtl8822be/phy.c
+++ b/drivers/staging/rtlwifi/rtl8822be/phy.c
@@ -1251,7 +1251,7 @@ static void _rtl8822be_get_rate_values_of_tx_power_by_rate(
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
"Invalid reg_addr 0x%x in %s()\n", reg_addr, __func__);
break;
- };
+ }
}
void rtl8822be_store_tx_power_by_rate(struct ieee80211_hw *hw, u32 band,
diff --git a/drivers/staging/rtlwifi/wifi.h b/drivers/staging/rtlwifi/wifi.h
index ca0243fa2e66..a23bb1719e35 100644
--- a/drivers/staging/rtlwifi/wifi.h
+++ b/drivers/staging/rtlwifi/wifi.h
@@ -2379,16 +2379,17 @@ struct rtl_hal_usbint_cfg {
u32 rx_max_size;
/* op - rx */
- void (*usb_rx_hdl)(struct ieee80211_hw *, struct sk_buff *);
- void (*usb_rx_segregate_hdl)(struct ieee80211_hw *, struct sk_buff *,
- struct sk_buff_head *);
+ void (*usb_rx_hdl)(struct ieee80211_hw *hw, struct sk_buff *skb);
+ void (*usb_rx_segregate_hdl)(struct ieee80211_hw *hw,
+ struct sk_buff *skb,
+ struct sk_buff_head *skbh);
/* tx */
- void (*usb_tx_cleanup)(struct ieee80211_hw *, struct sk_buff *);
- int (*usb_tx_post_hdl)(struct ieee80211_hw *, struct urb *,
- struct sk_buff *);
- struct sk_buff *(*usb_tx_aggregate_hdl)(struct ieee80211_hw *,
- struct sk_buff_head *);
+ void (*usb_tx_cleanup)(struct ieee80211_hw *hw, struct sk_buff *skb);
+ int (*usb_tx_post_hdl)(struct ieee80211_hw *hw, struct urb *urb,
+ struct sk_buff *skb);
+ struct sk_buff *(*usb_tx_aggregate_hdl)(struct ieee80211_hw *hw,
+ struct sk_buff_head *skbh);
/* endpoint mapping */
int (*usb_endpoint_mapping)(struct ieee80211_hw *hw);
@@ -2693,12 +2694,12 @@ struct rtl_btc_ops {
};
struct rtl_halmac_ops {
- int (*halmac_init_adapter)(struct rtl_priv *);
- int (*halmac_deinit_adapter)(struct rtl_priv *);
- int (*halmac_init_hal)(struct rtl_priv *);
- int (*halmac_deinit_hal)(struct rtl_priv *);
- int (*halmac_poweron)(struct rtl_priv *);
- int (*halmac_poweroff)(struct rtl_priv *);
+ int (*halmac_init_adapter)(struct rtl_priv *rtlpriv);
+ int (*halmac_deinit_adapter)(struct rtl_priv *rtlpriv);
+ int (*halmac_init_hal)(struct rtl_priv *rtlpriv);
+ int (*halmac_deinit_hal)(struct rtl_priv *rtlpriv);
+ int (*halmac_poweron)(struct rtl_priv *rtlpriv);
+ int (*halmac_poweroff)(struct rtl_priv *rtlpriv);
int (*halmac_phy_power_switch)(struct rtl_priv *rtlpriv, u8 enable);
int (*halmac_set_mac_address)(struct rtl_priv *rtlpriv, u8 hwport,
diff --git a/drivers/staging/rts5208/rtsx_chip.h b/drivers/staging/rts5208/rtsx_chip.h
index 4f6e3c1c4621..8a8cd5d3cf7e 100644
--- a/drivers/staging/rts5208/rtsx_chip.h
+++ b/drivers/staging/rts5208/rtsx_chip.h
@@ -339,13 +339,13 @@ struct sense_data_t {
#define CHK_BIT(data, idx) ((data) & (1 << (idx)))
/* SG descriptor */
-#define SG_INT 0x04
-#define SG_END 0x02
-#define SG_VALID 0x01
+#define RTSX_SG_INT 0x04
+#define RTSX_SG_END 0x02
+#define RTSX_SG_VALID 0x01
-#define SG_NO_OP 0x00
-#define SG_TRANS_DATA (0x02 << 4)
-#define SG_LINK_DESC (0x03 << 4)
+#define RTSX_SG_NO_OP 0x00
+#define RTSX_SG_TRANS_DATA (0x02 << 4)
+#define RTSX_SG_LINK_DESC (0x03 << 4)
struct rtsx_chip;
diff --git a/drivers/staging/rts5208/rtsx_transport.c b/drivers/staging/rts5208/rtsx_transport.c
index 8b57e17ee6d3..716cce2bd7f0 100644
--- a/drivers/staging/rts5208/rtsx_transport.c
+++ b/drivers/staging/rts5208/rtsx_transport.c
@@ -308,7 +308,7 @@ static inline void rtsx_add_sg_tbl(
do {
if (len > 0x80000) {
temp_len = 0x80000;
- temp_opt = option & (~SG_END);
+ temp_opt = option & (~RTSX_SG_END);
} else {
temp_len = len;
temp_opt = option;
@@ -407,9 +407,9 @@ static int rtsx_transfer_sglist_adma_partial(struct rtsx_chip *chip, u8 card,
*index = *index + 1;
}
if ((i == (sg_cnt - 1)) || !resid)
- option = SG_VALID | SG_END | SG_TRANS_DATA;
+ option = RTSX_SG_VALID | RTSX_SG_END | RTSX_SG_TRANS_DATA;
else
- option = SG_VALID | SG_TRANS_DATA;
+ option = RTSX_SG_VALID | RTSX_SG_TRANS_DATA;
rtsx_add_sg_tbl(chip, (u32)addr, (u32)len, option);
@@ -555,9 +555,9 @@ static int rtsx_transfer_sglist_adma(struct rtsx_chip *chip, u8 card,
(unsigned int)addr, len);
if (j == (sg_cnt - 1))
- option = SG_VALID | SG_END | SG_TRANS_DATA;
+ option = RTSX_SG_VALID | RTSX_SG_END | RTSX_SG_TRANS_DATA;
else
- option = SG_VALID | SG_TRANS_DATA;
+ option = RTSX_SG_VALID | RTSX_SG_TRANS_DATA;
rtsx_add_sg_tbl(chip, (u32)addr, (u32)len, option);
diff --git a/drivers/staging/sm750fb/ddk750_chip.c b/drivers/staging/sm750fb/ddk750_chip.c
index 313b99104398..4c1f00f551da 100644
--- a/drivers/staging/sm750fb/ddk750_chip.c
+++ b/drivers/staging/sm750fb/ddk750_chip.c
@@ -8,9 +8,9 @@
#define MHz(x) ((x) * 1000000)
-static logical_chip_type_t chip;
+static enum logical_chip_type chip;
-logical_chip_type_t sm750_get_chip_type(void)
+enum logical_chip_type sm750_get_chip_type(void)
{
return chip;
}
diff --git a/drivers/staging/sm750fb/ddk750_chip.h b/drivers/staging/sm750fb/ddk750_chip.h
index aee82fcaf669..c72aac21b675 100644
--- a/drivers/staging/sm750fb/ddk750_chip.h
+++ b/drivers/staging/sm750fb/ddk750_chip.h
@@ -24,25 +24,23 @@ static inline void poke32(u32 addr, u32 data)
}
/* This is all the chips recognized by this library */
-typedef enum _logical_chip_type_t {
+enum logical_chip_type {
SM_UNKNOWN,
SM718,
SM750,
SM750LE,
-}
-logical_chip_type_t;
+};
-typedef enum _clock_type_t {
+enum clock_type {
MXCLK_PLL,
PRIMARY_PLL,
SECONDARY_PLL,
VGA0_PLL,
VGA1_PLL,
-}
-clock_type_t;
+};
struct pll_value {
- clock_type_t clockType;
+ enum clock_type clockType;
unsigned long inputFreq; /* Input clock frequency to the PLL */
/* Use this when clockType = PANEL_PLL */
@@ -94,7 +92,7 @@ struct initchip_param {
/* More initialization parameter can be added if needed */
};
-logical_chip_type_t sm750_get_chip_type(void);
+enum logical_chip_type sm750_get_chip_type(void);
void sm750_set_chip_type(unsigned short devId, u8 revId);
unsigned int sm750_calc_pll_value(unsigned int request, struct pll_value *pll);
unsigned int sm750_format_pll_reg(struct pll_value *pPLL);
diff --git a/drivers/staging/sm750fb/ddk750_display.c b/drivers/staging/sm750fb/ddk750_display.c
index c6fd90191530..1273e7d18925 100644
--- a/drivers/staging/sm750fb/ddk750_display.c
+++ b/drivers/staging/sm750fb/ddk750_display.c
@@ -111,7 +111,7 @@ static void swPanelPowerSequence(int disp, int delay)
primary_wait_vertical_sync(delay);
}
-void ddk750_setLogicalDispOut(disp_output_t output)
+void ddk750_setLogicalDispOut(enum disp_output output)
{
unsigned int reg;
diff --git a/drivers/staging/sm750fb/ddk750_display.h b/drivers/staging/sm750fb/ddk750_display.h
index 523bbf33521c..7fd101d98199 100644
--- a/drivers/staging/sm750fb/ddk750_display.h
+++ b/drivers/staging/sm750fb/ddk750_display.h
@@ -89,7 +89,7 @@
* LCD1 means panel path TFT1 & panel path DVI (so enable DAC)
* CRT means crt path DSUB
*/
-typedef enum _disp_output_t {
+enum disp_output {
do_LCD1_PRI = PNL_2_PRI | PRI_TP_ON | PNL_SEQ_ON | DAC_ON,
do_LCD1_SEC = PNL_2_SEC | SEC_TP_ON | PNL_SEQ_ON | DAC_ON,
do_LCD2_PRI = CRT_2_PRI | PRI_TP_ON | DUAL_TFT_ON,
@@ -100,9 +100,8 @@ typedef enum _disp_output_t {
*/
do_CRT_PRI = CRT_2_PRI | PRI_TP_ON | DPMS_ON | DAC_ON,
do_CRT_SEC = CRT_2_SEC | SEC_TP_ON | DPMS_ON | DAC_ON,
-}
-disp_output_t;
+};
-void ddk750_setLogicalDispOut(disp_output_t output);
+void ddk750_setLogicalDispOut(enum disp_output output);
#endif
diff --git a/drivers/staging/sm750fb/ddk750_mode.c b/drivers/staging/sm750fb/ddk750_mode.c
index 2cdd87b78e58..7e22d093b091 100644
--- a/drivers/staging/sm750fb/ddk750_mode.c
+++ b/drivers/staging/sm750fb/ddk750_mode.c
@@ -206,7 +206,7 @@ static int programModeRegisters(struct mode_parameter *pModeParam,
return ret;
}
-int ddk750_setModeTiming(struct mode_parameter *parm, clock_type_t clock)
+int ddk750_setModeTiming(struct mode_parameter *parm, enum clock_type clock)
{
struct pll_value pll;
unsigned int uiActualPixelClk;
diff --git a/drivers/staging/sm750fb/ddk750_mode.h b/drivers/staging/sm750fb/ddk750_mode.h
index 259a9d6a4eb2..2df78a0937b2 100644
--- a/drivers/staging/sm750fb/ddk750_mode.h
+++ b/drivers/staging/sm750fb/ddk750_mode.h
@@ -33,5 +33,5 @@ struct mode_parameter {
enum spolarity clock_phase_polarity;
};
-int ddk750_setModeTiming(struct mode_parameter *parm, clock_type_t clock);
+int ddk750_setModeTiming(struct mode_parameter *parm, enum clock_type clock);
#endif
diff --git a/drivers/staging/sm750fb/sm750_hw.c b/drivers/staging/sm750fb/sm750_hw.c
index a8c79864ee4b..edeae9d06883 100644
--- a/drivers/staging/sm750fb/sm750_hw.c
+++ b/drivers/staging/sm750fb/sm750_hw.c
@@ -185,7 +185,7 @@ int hw_sm750_output_setMode(struct lynxfb_output *output,
struct fb_fix_screeninfo *fix)
{
int ret;
- disp_output_t disp_set;
+ enum disp_output disp_set;
int channel;
ret = 0;
@@ -254,7 +254,7 @@ int hw_sm750_crtc_setMode(struct lynxfb_crtc *crtc,
int ret, fmt;
u32 reg;
struct mode_parameter modparm;
- clock_type_t clock;
+ enum clock_type clock;
struct sm750_dev *sm750_dev;
struct lynxfb_par *par;
diff --git a/drivers/staging/speakup/Kconfig b/drivers/staging/speakup/Kconfig
index 7e8037e230b8..efd6f4560d3e 100644
--- a/drivers/staging/speakup/Kconfig
+++ b/drivers/staging/speakup/Kconfig
@@ -1,7 +1,7 @@
menu "Speakup console speech"
config SPEAKUP
- depends on VT && !MN10300
+ depends on VT
tristate "Speakup core"
---help---
This is the Speakup screen reader. Think of it as a
diff --git a/drivers/staging/speakup/main.c b/drivers/staging/speakup/main.c
index cf1259059776..af30b7099bed 100644
--- a/drivers/staging/speakup/main.c
+++ b/drivers/staging/speakup/main.c
@@ -417,7 +417,7 @@ static void announce_edge(struct vc_data *vc, int msg_id)
bleep(spk_y);
if ((spk_bleeps & 2) && (msg_id < edge_quiet))
synth_printf("%s\n",
- spk_msg_get(MSG_EDGE_MSGS_START + msg_id - 1));
+ spk_msg_get(MSG_EDGE_MSGS_START + msg_id - 1));
}
static void speak_char(u16 ch)
@@ -449,8 +449,9 @@ static void speak_char(u16 ch)
if (*cp == '^') {
cp++;
synth_printf(" %s%s ", spk_msg_get(MSG_CTRL), cp);
- } else
+ } else {
synth_printf(" %s ", cp);
+ }
}
}
@@ -561,7 +562,7 @@ static u_long get_word(struct vc_data *vc)
get_char(vc, (u_short *)&tmp_pos + 1, &temp) > SPACE) {
tmp_pos += 2;
tmpx++;
- } else
+ } else {
while (tmpx > 0) {
ch = get_char(vc, (u_short *)tmp_pos - 1, &temp);
if ((ch == SPACE || ch == 0 ||
@@ -571,6 +572,7 @@ static u_long get_word(struct vc_data *vc)
tmp_pos -= 2;
tmpx--;
}
+ }
attr_ch = get_char(vc, (u_short *)tmp_pos, &spk_attr);
buf[cnt++] = attr_ch;
while (tmpx < vc->vc_cols - 1) {
diff --git a/drivers/staging/speakup/speakup_decpc.c b/drivers/staging/speakup/speakup_decpc.c
index 303f393d3f2f..6649309e0342 100644
--- a/drivers/staging/speakup/speakup_decpc.c
+++ b/drivers/staging/speakup/speakup_decpc.c
@@ -349,7 +349,7 @@ static int testkernel(void)
return 0;
else if (dt_stat == 0x0dec)
pr_warn("dec_pc at 0x%x, software not loaded\n",
- speakup_info.port_tts);
+ speakup_info.port_tts);
status = -3;
oops: synth_release_region(speakup_info.port_tts, SYNTH_IO_EXTENT);
speakup_info.port_tts = 0;
@@ -412,11 +412,11 @@ static void do_catch_up(struct spk_synth *synth)
if (!in_escape)
dt_sendchar(PROCSPEECH);
spin_lock_irqsave(&speakup_info.spinlock,
- flags);
+ flags);
jiffy_delta_val = jiffy_delta->u.n.value;
delay_time_val = delay_time->u.n.value;
spin_unlock_irqrestore(&speakup_info.spinlock,
- flags);
+ flags);
schedule_timeout(msecs_to_jiffies
(delay_time_val));
jiff_max = jiffies + jiffy_delta_val;
diff --git a/drivers/staging/speakup/speakup_dectlk.c b/drivers/staging/speakup/speakup_dectlk.c
index 2ea22a2eb5f9..a144f28ee1a8 100644
--- a/drivers/staging/speakup/speakup_dectlk.c
+++ b/drivers/staging/speakup/speakup_dectlk.c
@@ -262,11 +262,11 @@ static void do_catch_up(struct spk_synth *synth)
if (!in_escape)
synth->io_ops->synth_out(synth, PROCSPEECH);
spin_lock_irqsave(&speakup_info.spinlock,
- flags);
+ flags);
jiffy_delta_val = jiffy_delta->u.n.value;
delay_time_val = delay_time->u.n.value;
spin_unlock_irqrestore(&speakup_info.spinlock,
- flags);
+ flags);
schedule_timeout(msecs_to_jiffies
(delay_time_val));
jiff_max = jiffies + jiffy_delta_val;
diff --git a/drivers/staging/speakup/speakup_dtlk.c b/drivers/staging/speakup/speakup_dtlk.c
index f8cb83c9b82e..dbebed0eeeec 100644
--- a/drivers/staging/speakup/speakup_dtlk.c
+++ b/drivers/staging/speakup/speakup_dtlk.c
@@ -266,7 +266,7 @@ static char synth_read_tts(void)
outb_p(ch, speakup_info.port_tts);
while (synth_readable())
cpu_relax();
- return (char) ch;
+ return (char)ch;
}
/* interrogate the DoubleTalk PC and return its settings */
@@ -287,7 +287,7 @@ static struct synth_settings *synth_interrogate(struct spk_synth *synth)
}
t = buf;
/* serial number is little endian */
- status.serial_number = t[0] + t[1]*256;
+ status.serial_number = t[0] + t[1] * 256;
t += 2;
for (i = 0; *t != '\r'; t++) {
status.rom_version[i] = *t;
@@ -323,29 +323,29 @@ static int synth_probe(struct spk_synth *synth)
if (port_forced) {
speakup_info.port_tts = port_forced;
pr_info("probe forced to %x by kernel command line\n",
- speakup_info.port_tts);
+ speakup_info.port_tts);
if ((port_forced & 0xf) != 0xf)
pr_info("warning: port base should probably end with f\n");
- if (synth_request_region(speakup_info.port_tts-1,
- SYNTH_IO_EXTENT)) {
+ if (synth_request_region(speakup_info.port_tts - 1,
+ SYNTH_IO_EXTENT)) {
pr_warn("sorry, port already reserved\n");
return -EBUSY;
}
- port_val = inw(speakup_info.port_tts-1);
- synth_lpc = speakup_info.port_tts-1;
+ port_val = inw(speakup_info.port_tts - 1);
+ synth_lpc = speakup_info.port_tts - 1;
} else {
for (i = 0; synth_portlist[i]; i++) {
if (synth_request_region(synth_portlist[i],
- SYNTH_IO_EXTENT))
+ SYNTH_IO_EXTENT))
continue;
port_val = inw(synth_portlist[i]) & 0xfbff;
if (port_val == 0x107f) {
synth_lpc = synth_portlist[i];
- speakup_info.port_tts = synth_lpc+1;
+ speakup_info.port_tts = synth_lpc + 1;
break;
}
synth_release_region(synth_portlist[i],
- SYNTH_IO_EXTENT);
+ SYNTH_IO_EXTENT);
}
}
port_val &= 0xfbff;
@@ -359,7 +359,7 @@ static int synth_probe(struct spk_synth *synth)
cpu_relax(); /* wait until it's ready */
sp = synth_interrogate(synth);
pr_info("%s: %03x-%03x, ROM ver %s, s/n %u, driver: %s\n",
- synth->long_name, synth_lpc, synth_lpc+SYNTH_IO_EXTENT - 1,
+ synth->long_name, synth_lpc, synth_lpc + SYNTH_IO_EXTENT - 1,
sp->rom_version, sp->serial_number, synth->version);
synth->alive = 1;
return 0;
@@ -369,7 +369,8 @@ static void dtlk_release(void)
{
spk_stop_serial_interrupt();
if (speakup_info.port_tts)
- synth_release_region(speakup_info.port_tts-1, SYNTH_IO_EXTENT);
+ synth_release_region(speakup_info.port_tts - 1,
+ SYNTH_IO_EXTENT);
speakup_info.port_tts = 0;
}
diff --git a/drivers/staging/speakup/speakup_dummy.c b/drivers/staging/speakup/speakup_dummy.c
index a30d60450bd5..aa0c900f79f2 100644
--- a/drivers/staging/speakup/speakup_dummy.c
+++ b/drivers/staging/speakup/speakup_dummy.c
@@ -94,7 +94,7 @@ static struct spk_synth synth_dummy = {
.probe = spk_ttyio_synth_probe,
.release = spk_ttyio_release,
.synth_immediate = spk_ttyio_synth_immediate,
- .catch_up = spk_do_catch_up,
+ .catch_up = spk_do_catch_up_unicode,
.flush = spk_synth_flush,
.is_alive = spk_synth_is_alive_restart,
.synth_adjust = NULL,
diff --git a/drivers/staging/speakup/speakup_keypc.c b/drivers/staging/speakup/speakup_keypc.c
index de76183932e1..3901734982a4 100644
--- a/drivers/staging/speakup/speakup_keypc.c
+++ b/drivers/staging/speakup/speakup_keypc.c
@@ -260,7 +260,7 @@ static int synth_probe(struct spk_synth *synth)
if (port_forced) {
synth_port = port_forced;
pr_info("probe forced to %x by kernel command line\n",
- synth_port);
+ synth_port);
if (synth_request_region(synth_port-1, SYNTH_IO_EXTENT)) {
pr_warn("sorry, port already reserved\n");
return -EBUSY;
@@ -269,7 +269,7 @@ static int synth_probe(struct spk_synth *synth)
} else {
for (i = 0; synth_portlist[i]; i++) {
if (synth_request_region(synth_portlist[i],
- SYNTH_IO_EXTENT)) {
+ SYNTH_IO_EXTENT)) {
pr_warn
("request_region: failed with 0x%x, %d\n",
synth_portlist[i], SYNTH_IO_EXTENT);
diff --git a/drivers/staging/speakup/spk_priv.h b/drivers/staging/speakup/spk_priv.h
index 00430437eb4c..7b3a16e1fa23 100644
--- a/drivers/staging/speakup/spk_priv.h
+++ b/drivers/staging/speakup/spk_priv.h
@@ -57,6 +57,7 @@ int spk_ttyio_synth_probe(struct spk_synth *synth);
const char *spk_serial_synth_immediate(struct spk_synth *synth, const char *buff);
const char *spk_ttyio_synth_immediate(struct spk_synth *synth, const char *buff);
void spk_do_catch_up(struct spk_synth *synth);
+void spk_do_catch_up_unicode(struct spk_synth *synth);
void spk_synth_flush(struct spk_synth *synth);
unsigned char spk_synth_get_index(struct spk_synth *synth);
int spk_synth_is_alive_nop(struct spk_synth *synth);
diff --git a/drivers/staging/speakup/spk_ttyio.c b/drivers/staging/speakup/spk_ttyio.c
index 5aa3ffa3772d..eac63aab8162 100644
--- a/drivers/staging/speakup/spk_ttyio.c
+++ b/drivers/staging/speakup/spk_ttyio.c
@@ -71,7 +71,7 @@ static void spk_ttyio_ldisc_close(struct tty_struct *tty)
}
static int spk_ttyio_receive_buf2(struct tty_struct *tty,
- const unsigned char *cp, char *fp, int count)
+ const unsigned char *cp, char *fp, int count)
{
struct spk_ldisc_data *ldisc_data = tty->disc_data;
@@ -110,6 +110,7 @@ static struct tty_ldisc_ops spk_ttyio_ldisc_ops = {
};
static int spk_ttyio_out(struct spk_synth *in_synth, const char ch);
+static int spk_ttyio_out_unicode(struct spk_synth *in_synth, u16 ch);
static void spk_ttyio_send_xchar(char ch);
static void spk_ttyio_tiocmset(unsigned int set, unsigned int clear);
static unsigned char spk_ttyio_in(void);
@@ -118,6 +119,7 @@ static void spk_ttyio_flush_buffer(void);
struct spk_io_ops spk_ttyio_ops = {
.synth_out = spk_ttyio_out,
+ .synth_out_unicode = spk_ttyio_out_unicode,
.send_xchar = spk_ttyio_send_xchar,
.tiocmset = spk_ttyio_tiocmset,
.synth_in = spk_ttyio_in,
@@ -221,6 +223,23 @@ static int spk_ttyio_out(struct spk_synth *in_synth, const char ch)
return 0;
}
+static int spk_ttyio_out_unicode(struct spk_synth *in_synth, u16 ch)
+{
+ int ret;
+
+ if (ch < 0x80)
+ ret = spk_ttyio_out(in_synth, ch);
+ else if (ch < 0x800) {
+ ret = spk_ttyio_out(in_synth, 0xc0 | (ch >> 6));
+ ret &= spk_ttyio_out(in_synth, 0x80 | (ch & 0x3f));
+ } else {
+ ret = spk_ttyio_out(in_synth, 0xe0 | (ch >> 12));
+ ret &= spk_ttyio_out(in_synth, 0x80 | ((ch >> 6) & 0x3f));
+ ret &= spk_ttyio_out(in_synth, 0x80 | (ch & 0x3f));
+ }
+ return ret;
+}
+
static int check_tty(struct tty_struct *tty)
{
if (!tty) {
diff --git a/drivers/staging/speakup/spk_types.h b/drivers/staging/speakup/spk_types.h
index c50de6035a9a..4203bed90b4f 100644
--- a/drivers/staging/speakup/spk_types.h
+++ b/drivers/staging/speakup/spk_types.h
@@ -151,6 +151,7 @@ struct spk_synth;
struct spk_io_ops {
int (*synth_out)(struct spk_synth *synth, const char ch);
+ int (*synth_out_unicode)(struct spk_synth *synth, u16 ch);
void (*send_xchar)(char ch);
void (*tiocmset)(unsigned int set, unsigned int clear);
unsigned char (*synth_in)(void);
diff --git a/drivers/staging/speakup/synth.c b/drivers/staging/speakup/synth.c
index c06e6a810999..7deeb7061018 100644
--- a/drivers/staging/speakup/synth.c
+++ b/drivers/staging/speakup/synth.c
@@ -52,9 +52,9 @@ static int do_synth_init(struct spk_synth *in_synth);
* For devices that have a "full" notification mechanism, the driver can
* adapt the loop the way they prefer.
*/
-void spk_do_catch_up(struct spk_synth *synth)
+static void _spk_do_catch_up(struct spk_synth *synth, int unicode)
{
- u_char ch;
+ u16 ch;
unsigned long flags;
unsigned long jiff_max;
struct var_t *delay_time;
@@ -63,6 +63,7 @@ void spk_do_catch_up(struct spk_synth *synth)
int jiffy_delta_val;
int delay_time_val;
int full_time_val;
+ int ret;
jiffy_delta = spk_get_var(JIFFY);
full_time = spk_get_var(FULL);
@@ -81,7 +82,8 @@ void spk_do_catch_up(struct spk_synth *synth)
synth->flush(synth);
continue;
}
- synth_buffer_skip_nonlatin1();
+ if (!unicode)
+ synth_buffer_skip_nonlatin1();
if (synth_buffer_empty()) {
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
break;
@@ -92,7 +94,11 @@ void spk_do_catch_up(struct spk_synth *synth)
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
if (ch == '\n')
ch = synth->procspeech;
- if (!synth->io_ops->synth_out(synth, ch)) {
+ if (unicode)
+ ret = synth->io_ops->synth_out_unicode(synth, ch);
+ else
+ ret = synth->io_ops->synth_out(synth, ch);
+ if (!ret) {
schedule_timeout(msecs_to_jiffies(full_time_val));
continue;
}
@@ -117,8 +123,19 @@ void spk_do_catch_up(struct spk_synth *synth)
}
synth->io_ops->synth_out(synth, synth->procspeech);
}
+
+void spk_do_catch_up(struct spk_synth *synth)
+{
+ _spk_do_catch_up(synth, 0);
+}
EXPORT_SYMBOL_GPL(spk_do_catch_up);
+void spk_do_catch_up_unicode(struct spk_synth *synth)
+{
+ _spk_do_catch_up(synth, 1);
+}
+EXPORT_SYMBOL_GPL(spk_do_catch_up_unicode);
+
void spk_synth_flush(struct spk_synth *synth)
{
synth->io_ops->flush_buffer();
diff --git a/drivers/staging/typec/tcpci.c b/drivers/staging/typec/tcpci.c
index 9bd4412356c9..076d97eaff6f 100644
--- a/drivers/staging/typec/tcpci.c
+++ b/drivers/staging/typec/tcpci.c
@@ -21,7 +21,6 @@
struct tcpci {
struct device *dev;
- struct i2c_client *client;
struct tcpm_port *port;
@@ -30,6 +29,12 @@ struct tcpci {
bool controls_vbus;
struct tcpc_dev tcpc;
+ struct tcpci_data *data;
+};
+
+struct tcpci_chip {
+ struct tcpci *tcpci;
+ struct tcpci_data data;
};
static inline struct tcpci *tcpc_to_tcpci(struct tcpc_dev *tcpc)
@@ -37,8 +42,7 @@ static inline struct tcpci *tcpc_to_tcpci(struct tcpc_dev *tcpc)
return container_of(tcpc, struct tcpci, tcpc);
}
-static int tcpci_read16(struct tcpci *tcpci, unsigned int reg,
- u16 *val)
+static int tcpci_read16(struct tcpci *tcpci, unsigned int reg, u16 *val)
{
return regmap_raw_read(tcpci->regmap, reg, val, sizeof(u16));
}
@@ -98,9 +102,17 @@ static int tcpci_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc)
static int tcpci_start_drp_toggling(struct tcpc_dev *tcpc,
enum typec_cc_status cc)
{
+ int ret;
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
unsigned int reg = TCPC_ROLE_CTRL_DRP;
+ /* Handle vendor drp toggling */
+ if (tcpci->data->start_drp_toggling) {
+ ret = tcpci->data->start_drp_toggling(tcpci, tcpci->data, cc);
+ if (ret < 0)
+ return ret;
+ }
+
switch (cc) {
default:
case TYPEC_CC_RP_DEF:
@@ -117,7 +129,17 @@ static int tcpci_start_drp_toggling(struct tcpc_dev *tcpc,
break;
}
- return regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg);
+ if (cc == TYPEC_CC_RD)
+ reg |= (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) |
+ (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT);
+ else
+ reg |= (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) |
+ (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT);
+ ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg);
+ if (ret < 0)
+ return ret;
+ return regmap_write(tcpci->regmap, TCPC_COMMAND,
+ TCPC_CMD_LOOK4CONNECTION);
}
static enum typec_cc_status tcpci_to_typec_cc(unsigned int cc, bool sink)
@@ -178,6 +200,13 @@ static int tcpci_set_vconn(struct tcpc_dev *tcpc, bool enable)
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
int ret;
+ /* Handle vendor set vconn */
+ if (tcpci->data->set_vconn) {
+ ret = tcpci->data->set_vconn(tcpci, tcpci->data, enable);
+ if (ret < 0)
+ return ret;
+ }
+
ret = regmap_write(tcpci->regmap, TCPC_POWER_CTRL,
enable ? TCPC_POWER_CTRL_VCONN_ENABLE : 0);
if (ret < 0)
@@ -323,6 +352,13 @@ static int tcpci_init(struct tcpc_dev *tcpc)
if (time_after(jiffies, timeout))
return -ETIMEDOUT;
+ /* Handle vendor init */
+ if (tcpci->data->init) {
+ ret = tcpci->data->init(tcpci, tcpci->data);
+ if (ret < 0)
+ return ret;
+ }
+
/* Clear all events */
ret = tcpci_write16(tcpci, TCPC_ALERT, 0xffff);
if (ret < 0)
@@ -344,9 +380,8 @@ static int tcpci_init(struct tcpc_dev *tcpc)
return tcpci_write16(tcpci, TCPC_ALERT_MASK, reg);
}
-static irqreturn_t tcpci_irq(int irq, void *dev_id)
+irqreturn_t tcpci_irq(struct tcpci *tcpci)
{
- struct tcpci *tcpci = dev_id;
u16 status;
tcpci_read16(tcpci, TCPC_ALERT, &status);
@@ -412,6 +447,14 @@ static irqreturn_t tcpci_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
+EXPORT_SYMBOL_GPL(tcpci_irq);
+
+static irqreturn_t _tcpci_irq(int irq, void *dev_id)
+{
+ struct tcpci_chip *chip = dev_id;
+
+ return tcpci_irq(chip->tcpci);
+}
static const struct regmap_config tcpci_regmap_config = {
.reg_bits = 8,
@@ -435,22 +478,18 @@ static int tcpci_parse_config(struct tcpci *tcpci)
return 0;
}
-static int tcpci_probe(struct i2c_client *client,
- const struct i2c_device_id *i2c_id)
+struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data)
{
struct tcpci *tcpci;
int err;
- tcpci = devm_kzalloc(&client->dev, sizeof(*tcpci), GFP_KERNEL);
+ tcpci = devm_kzalloc(dev, sizeof(*tcpci), GFP_KERNEL);
if (!tcpci)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
- tcpci->client = client;
- tcpci->dev = &client->dev;
- i2c_set_clientdata(client, tcpci);
- tcpci->regmap = devm_regmap_init_i2c(client, &tcpci_regmap_config);
- if (IS_ERR(tcpci->regmap))
- return PTR_ERR(tcpci->regmap);
+ tcpci->dev = dev;
+ tcpci->data = data;
+ tcpci->regmap = data->regmap;
tcpci->tcpc.init = tcpci_init;
tcpci->tcpc.get_vbus = tcpci_get_vbus;
@@ -467,27 +506,63 @@ static int tcpci_probe(struct i2c_client *client,
err = tcpci_parse_config(tcpci);
if (err < 0)
- return err;
+ return ERR_PTR(err);
- /* Disable chip interrupts */
- tcpci_write16(tcpci, TCPC_ALERT_MASK, 0);
+ tcpci->port = tcpm_register_port(tcpci->dev, &tcpci->tcpc);
+ if (PTR_ERR_OR_ZERO(tcpci->port))
+ return ERR_CAST(tcpci->port);
- err = devm_request_threaded_irq(tcpci->dev, client->irq, NULL,
- tcpci_irq,
+ return tcpci;
+}
+EXPORT_SYMBOL_GPL(tcpci_register_port);
+
+void tcpci_unregister_port(struct tcpci *tcpci)
+{
+ tcpm_unregister_port(tcpci->port);
+}
+EXPORT_SYMBOL_GPL(tcpci_unregister_port);
+
+static int tcpci_probe(struct i2c_client *client,
+ const struct i2c_device_id *i2c_id)
+{
+ struct tcpci_chip *chip;
+ int err;
+ u16 val = 0;
+
+ chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->data.regmap = devm_regmap_init_i2c(client, &tcpci_regmap_config);
+ if (IS_ERR(chip->data.regmap))
+ return PTR_ERR(chip->data.regmap);
+
+ /* Disable chip interrupts before requesting irq */
+ err = regmap_raw_write(chip->data.regmap, TCPC_ALERT_MASK, &val,
+ sizeof(u16));
+ if (err < 0)
+ return err;
+
+ err = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+ _tcpci_irq,
IRQF_ONESHOT | IRQF_TRIGGER_LOW,
- dev_name(tcpci->dev), tcpci);
+ dev_name(&client->dev), chip);
if (err < 0)
return err;
- tcpci->port = tcpm_register_port(tcpci->dev, &tcpci->tcpc);
- return PTR_ERR_OR_ZERO(tcpci->port);
+ chip->tcpci = tcpci_register_port(&client->dev, &chip->data);
+ if (PTR_ERR_OR_ZERO(chip->tcpci))
+ return PTR_ERR(chip->tcpci);
+
+ i2c_set_clientdata(client, chip);
+ return 0;
}
static int tcpci_remove(struct i2c_client *client)
{
- struct tcpci *tcpci = i2c_get_clientdata(client);
+ struct tcpci_chip *chip = i2c_get_clientdata(client);
- tcpm_unregister_port(tcpci->port);
+ tcpci_unregister_port(chip->tcpci);
return 0;
}
diff --git a/drivers/staging/typec/tcpci.h b/drivers/staging/typec/tcpci.h
index fdfb06cc3b86..34c865f0dcf6 100644
--- a/drivers/staging/typec/tcpci.h
+++ b/drivers/staging/typec/tcpci.h
@@ -121,4 +121,18 @@
#define TCPC_VBUS_VOLTAGE_ALARM_HI_CFG 0x76
#define TCPC_VBUS_VOLTAGE_ALARM_LO_CFG 0x78
+struct tcpci;
+struct tcpci_data {
+ struct regmap *regmap;
+ int (*init)(struct tcpci *tcpci, struct tcpci_data *data);
+ int (*set_vconn)(struct tcpci *tcpci, struct tcpci_data *data,
+ bool enable);
+ int (*start_drp_toggling)(struct tcpci *tcpci, struct tcpci_data *data,
+ enum typec_cc_status cc);
+};
+
+struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data);
+void tcpci_unregister_port(struct tcpci *tcpci);
+irqreturn_t tcpci_irq(struct tcpci *tcpci);
+
#endif /* __LINUX_USB_TCPCI_H */
diff --git a/drivers/staging/unisys/visorinput/Kconfig b/drivers/staging/unisys/visorinput/Kconfig
index 655cd62433de..a3817e0f7e5c 100644
--- a/drivers/staging/unisys/visorinput/Kconfig
+++ b/drivers/staging/unisys/visorinput/Kconfig
@@ -4,7 +4,7 @@
config UNISYS_VISORINPUT
tristate "Unisys visorinput driver"
- depends on UNISYSSPAR && UNISYS_VISORBUS && FB && INPUT
+ depends on UNISYSSPAR && UNISYS_VISORBUS && INPUT
---help---
The Unisys s-Par visorinput driver provides a virtualized system
console (keyboard and mouse) that is accessible through the
diff --git a/drivers/staging/unisys/visorinput/Makefile b/drivers/staging/unisys/visorinput/Makefile
index beedca7f0e09..6e4bfa059a1f 100644
--- a/drivers/staging/unisys/visorinput/Makefile
+++ b/drivers/staging/unisys/visorinput/Makefile
@@ -4,4 +4,3 @@
obj-$(CONFIG_UNISYS_VISORINPUT) += visorinput.o
-ccflags-y += -Idrivers/staging/unisys/include
diff --git a/drivers/staging/unisys/visorinput/ultrainputreport.h b/drivers/staging/unisys/visorinput/ultrainputreport.h
deleted file mode 100644
index 67dac430ce0c..000000000000
--- a/drivers/staging/unisys/visorinput/ultrainputreport.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright (C) 2010 - 2015 UNISYS CORPORATION
- * All rights reserved.
- */
-
-#ifndef __SPAR_ULTRAINPUTREPORT_H__
-#define __SPAR_ULTRAINPUTREPORT_H__
-
-#include <linux/types.h>
-
-/* These defines identify mouse and keyboard activity which is specified by the
- * firmware to the host using the cmsimpleinput protocol. @ingroup coretypes
- */
- /* only motion; arg1=x, arg2=y */
-#define INPUTACTION_XY_MOTION 1
-/* arg1: 1=left,2=center,3=right */
-#define INPUTACTION_MOUSE_BUTTON_DOWN 2
-/* arg1: 1=left,2=center,3=right */
-#define INPUTACTION_MOUSE_BUTTON_UP 3
-/* arg1: 1=left,2=center,3=right */
-#define INPUTACTION_MOUSE_BUTTON_CLICK 4
-/* arg1: 1=left,2=center 3=right */
-#define INPUTACTION_MOUSE_BUTTON_DCLICK 5
-/* arg1: wheel rotation away from user */
-#define INPUTACTION_WHEEL_ROTATE_AWAY 6
-/* arg1: wheel rotation toward user */
-#define INPUTACTION_WHEEL_ROTATE_TOWARD 7
-/* arg1: scancode, as follows: If arg1 <= 0xff, it's a 1-byte scancode and arg1
- * is that scancode. If arg1 > 0xff, it's a 2-byte scanecode, with the 1st
- * byte in the low 8 bits, and the 2nd byte in the high 8 bits.
- * E.g., the right ALT key would appear as x'38e0'.
- */
-#define INPUTACTION_KEY_DOWN 64
-/* arg1: scancode (in same format as inputaction_keyDown) */
-#define INPUTACTION_KEY_UP 65
-/* arg1: scancode (in same format as inputaction_keyDown); MUST refer to one of
- * the locking keys, like capslock, numlock, or scrolllock.
- * arg2: 1 iff locking key should be in the LOCKED position (e.g., light is ON)
- */
-#define INPUTACTION_SET_LOCKING_KEY_STATE 66
-/* arg1: scancode (in same format as inputaction_keyDown */
-#define INPUTACTION_KEY_DOWN_UP 67
-
-struct visor_inputactivity {
- u16 action;
- u16 arg1;
- u16 arg2;
- u16 arg3;
-} __packed;
-
-struct visor_inputreport {
- u64 seq_no;
- struct visor_inputactivity activity;
-} __packed;
-
-#endif
diff --git a/drivers/staging/unisys/visorinput/visorinput.c b/drivers/staging/unisys/visorinput/visorinput.c
index d8048e48658f..9693fb559052 100644
--- a/drivers/staging/unisys/visorinput/visorinput.c
+++ b/drivers/staging/unisys/visorinput/visorinput.c
@@ -18,7 +18,36 @@
#include <linux/uuid.h>
#include <linux/visorbus.h>
-#include "ultrainputreport.h"
+/* These defines identify mouse and keyboard activity which is specified by the
+ * firmware to the host using the cmsimpleinput protocol. @ingroup coretypes
+ */
+/* only motion; arg1=x, arg2=y */
+#define INPUTACTION_XY_MOTION 1
+
+/* arg1: 1=left,2=center,3=right */
+#define INPUTACTION_MOUSE_BUTTON_DOWN 2
+#define INPUTACTION_MOUSE_BUTTON_UP 3
+#define INPUTACTION_MOUSE_BUTTON_CLICK 4
+#define INPUTACTION_MOUSE_BUTTON_DCLICK 5
+
+/* arg1: wheel rotation away from/toward user */
+#define INPUTACTION_WHEEL_ROTATE_AWAY 6
+#define INPUTACTION_WHEEL_ROTATE_TOWARD 7
+
+/* arg1: scancode, as follows: If arg1 <= 0xff, it's a 1-byte scancode and arg1
+ * is that scancode. If arg1 > 0xff, it's a 2-byte scanecode, with the 1st
+ * byte in the low 8 bits, and the 2nd byte in the high 8 bits.
+ * E.g., the right ALT key would appear as x'38e0'.
+ */
+#define INPUTACTION_KEY_DOWN 64
+#define INPUTACTION_KEY_UP 65
+#define INPUTACTION_KEY_DOWN_UP 67
+
+/* arg1: scancode (in same format as inputaction_keyDown); MUST refer to one of
+ * the locking keys, like capslock, numlock, or scrolllock.
+ * arg2: 1 iff locking key should be in the LOCKED position (e.g., light is ON)
+ */
+#define INPUTACTION_SET_LOCKING_KEY_STATE 66
/* Keyboard channel {c73416d0-b0b8-44af-b304-9d2ae99f1b3d} */
#define VISOR_KEYBOARD_CHANNEL_GUID \
@@ -32,19 +61,45 @@
0x81, 0xc3, 0x61, 0xab, 0xcd, 0xbd, 0xbd, 0x87)
#define VISOR_MOUSE_CHANNEL_GUID_STR "addf07d4-94a9-46e2-81c3-61abcdbdbd87"
-#define PIXELS_ACROSS_DEFAULT 800
-#define PIXELS_DOWN_DEFAULT 600
+#define PIXELS_ACROSS_DEFAULT 1024
+#define PIXELS_DOWN_DEFAULT 768
#define KEYCODE_TABLE_BYTES 256
-enum visorinput_device_type {
+struct visor_inputactivity {
+ u16 action;
+ u16 arg1;
+ u16 arg2;
+ u16 arg3;
+} __packed;
+
+struct visor_inputreport {
+ u64 seq_no;
+ struct visor_inputactivity activity;
+} __packed;
+
+/* header of keyboard/mouse channels */
+struct visor_input_channel_data {
+ u32 n_input_reports;
+ union {
+ struct {
+ u16 x_res;
+ u16 y_res;
+ } mouse;
+ struct {
+ u32 flags;
+ } keyboard;
+ };
+} __packed;
+
+enum visorinput_dev_type {
visorinput_keyboard,
visorinput_mouse,
};
/*
- * This is the private data that we store for each device.
- * A pointer to this struct is maintained via
- * dev_get_drvdata() / dev_set_drvdata() for each struct device.
+ * This is the private data that we store for each device. A pointer to this
+ * struct is maintained via dev_get_drvdata() / dev_set_drvdata() for each
+ * struct device.
*/
struct visorinput_devdata {
struct visor_device *dev;
@@ -215,10 +270,9 @@ static int visorinput_open(struct input_dev *visorinput_dev)
dev_dbg(&visorinput_dev->dev, "%s opened\n", __func__);
/*
- * If we're not paused, really enable interrupts.
- * Regardless of whether we are paused, set a flag indicating
- * interrupts should be enabled so when we resume, interrupts
- * will really be enabled.
+ * If we're not paused, really enable interrupts. Regardless of whether
+ * we are paused, set a flag indicating interrupts should be enabled so
+ * when we resume, interrupts will really be enabled.
*/
mutex_lock(&devdata->lock_visor_dev);
devdata->interrupts_enabled = true;
@@ -244,10 +298,9 @@ static void visorinput_close(struct input_dev *visorinput_dev)
dev_dbg(&visorinput_dev->dev, "%s closed\n", __func__);
/*
- * If we're not paused, really disable interrupts.
- * Regardless of whether we are paused, set a flag indicating
- * interrupts should be disabled so when we resume we will
- * not re-enable them.
+ * If we're not paused, really disable interrupts. Regardless of
+ * whether we are paused, set a flag indicating interrupts should be
+ * disabled so when we resume we will not re-enable them.
*/
mutex_lock(&devdata->lock_visor_dev);
devdata->interrupts_enabled = false;
@@ -260,9 +313,9 @@ out_unlock:
}
/*
- * setup_client_keyboard() initializes and returns a Linux input node that
- * we can use to deliver keyboard inputs to Linux. We of course do this when
- * we see keyboard inputs coming in on a keyboard channel.
+ * setup_client_keyboard() initializes and returns a Linux input node that we
+ * can use to deliver keyboard inputs to Linux. We of course do this when we
+ * see keyboard inputs coming in on a keyboard channel.
*/
static struct input_dev *setup_client_keyboard(void *devdata,
unsigned char *keycode_table)
@@ -306,10 +359,9 @@ static struct input_dev *setup_client_keyboard(void *devdata,
return visorinput_dev;
}
-static struct input_dev *setup_client_mouse(void *devdata)
+static struct input_dev *setup_client_mouse(void *devdata, unsigned int xres,
+ unsigned int yres)
{
- int xres, yres;
- struct fb_info *fb0;
struct input_dev *visorinput_dev = input_allocate_device();
if (!visorinput_dev)
@@ -327,14 +379,10 @@ static struct input_dev *setup_client_mouse(void *devdata)
set_bit(BTN_RIGHT, visorinput_dev->keybit);
set_bit(BTN_MIDDLE, visorinput_dev->keybit);
- if (registered_fb[0]) {
- fb0 = registered_fb[0];
- xres = fb0->var.xres_virtual;
- yres = fb0->var.yres_virtual;
- } else {
+ if (xres == 0)
xres = PIXELS_ACROSS_DEFAULT;
+ if (yres == 0)
yres = PIXELS_DOWN_DEFAULT;
- }
input_set_abs_params(visorinput_dev, ABS_X, 0, xres, 0, 0);
input_set_abs_params(visorinput_dev, ABS_Y, 0, yres, 0, 0);
@@ -347,14 +395,15 @@ static struct input_dev *setup_client_mouse(void *devdata)
return visorinput_dev;
}
-static struct visorinput_devdata *devdata_create(
- struct visor_device *dev,
- enum visorinput_device_type devtype)
+static struct visorinput_devdata *devdata_create(struct visor_device *dev,
+ enum visorinput_dev_type dtype)
{
struct visorinput_devdata *devdata = NULL;
unsigned int extra_bytes = 0;
+ unsigned int size, xres, yres, err;
+ struct visor_input_channel_data data;
- if (devtype == visorinput_keyboard)
+ if (dtype == visorinput_keyboard)
/* allocate room for devdata->keycode_table, filled in below */
extra_bytes = KEYCODE_TABLE_BYTES * 2;
devdata = kzalloc(sizeof(*devdata) + extra_bytes, GFP_KERNEL);
@@ -373,11 +422,11 @@ static struct visorinput_devdata *devdata_create(
devdata->paused = true;
/*
- * This is an input device in a client guest partition,
- * so we need to create whatever input nodes are necessary to
- * deliver our inputs to the guest OS.
+ * This is an input device in a client guest partition, so we need to
+ * create whatever input nodes are necessary to deliver our inputs to
+ * the guest OS.
*/
- switch (devtype) {
+ switch (dtype) {
case visorinput_keyboard:
devdata->keycode_table_bytes = extra_bytes;
memcpy(devdata->keycode_table, visorkbd_keycode,
@@ -390,7 +439,15 @@ static struct visorinput_devdata *devdata_create(
goto cleanups_register;
break;
case visorinput_mouse:
- devdata->visorinput_dev = setup_client_mouse(devdata);
+ size = sizeof(struct visor_input_channel_data);
+ err = visorbus_read_channel(dev, sizeof(struct channel_header),
+ &data, size);
+ if (err)
+ goto cleanups_register;
+ xres = data.mouse.x_res;
+ yres = data.mouse.y_res;
+ devdata->visorinput_dev = setup_client_mouse(devdata, xres,
+ yres);
if (!devdata->visorinput_dev)
goto cleanups_register;
break;
@@ -404,10 +461,9 @@ static struct visorinput_devdata *devdata_create(
/*
* Device struct is completely set up now, with the exception of
- * visorinput_dev being registered.
- * We need to unlock before we register the device, because this
- * can cause an on-stack call of visorinput_open(), which would
- * deadlock if we had the lock.
+ * visorinput_dev being registered. We need to unlock before we
+ * register the device, because this can cause an on-stack call of
+ * visorinput_open(), which would deadlock if we had the lock.
*/
if (input_register_device(devdata->visorinput_dev)) {
input_free_device(devdata->visorinput_dev);
@@ -416,9 +472,9 @@ static struct visorinput_devdata *devdata_create(
mutex_lock(&devdata->lock_visor_dev);
/*
- * Establish calls to visorinput_channel_interrupt() if that is
- * the desired state that we've kept track of in interrupts_enabled
- * while the device was being created.
+ * Establish calls to visorinput_channel_interrupt() if that is the
+ * desired state that we've kept track of in interrupts_enabled while
+ * the device was being created.
*/
devdata->paused = false;
if (devdata->interrupts_enabled)
@@ -437,17 +493,17 @@ err_kfree_devdata:
static int visorinput_probe(struct visor_device *dev)
{
const guid_t *guid;
- enum visorinput_device_type devtype;
+ enum visorinput_dev_type dtype;
guid = visorchannel_get_guid(dev->visorchannel);
if (guid_equal(guid, &visor_mouse_channel_guid))
- devtype = visorinput_mouse;
+ dtype = visorinput_mouse;
else if (guid_equal(guid, &visor_keyboard_channel_guid))
- devtype = visorinput_keyboard;
+ dtype = visorinput_keyboard;
else
return -ENODEV;
visorbus_disable_channel_interrupts(dev);
- if (!devdata_create(dev, devtype))
+ if (!devdata_create(dev, dtype))
return -ENOMEM;
return 0;
}
@@ -469,8 +525,8 @@ static void visorinput_remove(struct visor_device *dev)
visorbus_disable_channel_interrupts(dev);
/*
- * due to above, at this time no thread of execution will be
- * in visorinput_channel_interrupt()
+ * due to above, at this time no thread of execution will be in
+ * visorinput_channel_interrupt()
*/
dev_set_drvdata(&dev->device, NULL);
@@ -513,9 +569,8 @@ static void handle_locking_key(struct input_dev *visorinput_dev, int keycode,
}
/*
- * <scancode> is either a 1-byte scancode, or an extended 16-bit scancode
- * with 0xE0 in the low byte and the extended scancode value in the next
- * higher byte.
+ * <scancode> is either a 1-byte scancode, or an extended 16-bit scancode with
+ * 0xE0 in the low byte and the extended scancode value in the next higher byte.
*/
static int scancode_to_keycode(int scancode)
{
@@ -656,8 +711,8 @@ static int visorinput_pause(struct visor_device *dev,
visorbus_disable_channel_interrupts(dev);
/*
- * due to above, at this time no thread of execution will be
- * in visorinput_channel_interrupt()
+ * due to above, at this time no thread of execution will be in
+ * visorinput_channel_interrupt()
*/
devdata->paused = true;
complete_func(dev, 0);
@@ -687,9 +742,9 @@ static int visorinput_resume(struct visor_device *dev,
complete_func(dev, 0);
/*
- * Re-establish calls to visorinput_channel_interrupt() if that is
- * the desired state that we've kept track of in interrupts_enabled
- * while the device was paused.
+ * Re-establish calls to visorinput_channel_interrupt() if that is the
+ * desired state that we've kept track of in interrupts_enabled while
+ * the device was paused.
*/
if (devdata->interrupts_enabled)
visorbus_enable_channel_interrupts(dev);
diff --git a/drivers/staging/vc04_services/Makefile b/drivers/staging/vc04_services/Makefile
index 1ecb261e04ae..fb26b826e640 100644
--- a/drivers/staging/vc04_services/Makefile
+++ b/drivers/staging/vc04_services/Makefile
@@ -4,7 +4,6 @@ obj-$(CONFIG_BCM2835_VCHIQ) += vchiq.o
vchiq-objs := \
interface/vchiq_arm/vchiq_core.o \
interface/vchiq_arm/vchiq_arm.o \
- interface/vchiq_arm/vchiq_kern_lib.o \
interface/vchiq_arm/vchiq_2835_arm.o \
interface/vchiq_arm/vchiq_debugfs.o \
interface/vchiq_arm/vchiq_shim.o \
diff --git a/drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c b/drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c
index 5f7551fbf5cf..8359cf881bef 100644
--- a/drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c
+++ b/drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c
@@ -209,6 +209,7 @@ static int snd_bcm2835_playback_close(struct snd_pcm_substream *substream)
*/
if (alsa_stream->running) {
int err;
+
err = bcm2835_audio_stop(alsa_stream);
alsa_stream->running = 0;
if (err)
@@ -278,7 +279,8 @@ static int snd_bcm2835_pcm_prepare(struct snd_pcm_substream *substream)
/* notify the vchiq that it should enter spdif passthrough mode by
* setting channels=0 (see
- * https://github.com/raspberrypi/linux/issues/528) */
+ * https://github.com/raspberrypi/linux/issues/528)
+ */
if (chip->spdif_status & IEC958_AES0_NONAUDIO)
channels = 0;
else
@@ -412,7 +414,7 @@ static int snd_bcm2835_pcm_lib_ioctl(struct snd_pcm_substream *substream,
int ret = snd_pcm_lib_ioctl(substream, cmd, arg);
audio_info(" .. substream=%p, cmd=%d, arg=%p (%x) ret=%d\n", substream,
- cmd, arg, arg ? *(unsigned *) arg : 0, ret);
+ cmd, arg, arg ? *(unsigned int *)arg : 0, ret);
return ret;
}
diff --git a/drivers/staging/vc04_services/bcm2835-audio/bcm2835.c b/drivers/staging/vc04_services/bcm2835-audio/bcm2835.c
index 045d577fe4f8..0ed21dd08170 100644
--- a/drivers/staging/vc04_services/bcm2835-audio/bcm2835.c
+++ b/drivers/staging/vc04_services/bcm2835-audio/bcm2835.c
@@ -25,6 +25,10 @@ MODULE_PARM_DESC(enable_compat_alsa,
static void snd_devm_unregister_child(struct device *dev, void *res)
{
struct device *childdev = *(struct device **)res;
+ struct bcm2835_chip *chip = dev_get_drvdata(childdev);
+ struct snd_card *card = chip->card;
+
+ snd_card_free(card);
device_unregister(childdev);
}
@@ -50,6 +54,13 @@ static int snd_devm_add_child(struct device *dev, struct device *child)
return 0;
}
+static void snd_bcm2835_release(struct device *dev)
+{
+ struct bcm2835_chip *chip = dev_get_drvdata(dev);
+
+ kfree(chip);
+}
+
static struct device *
snd_create_device(struct device *parent,
struct device_driver *driver,
@@ -65,6 +76,7 @@ snd_create_device(struct device *parent,
device_initialize(device);
device->parent = parent;
device->driver = driver;
+ device->release = snd_bcm2835_release;
dev_set_name(device, "%s", name);
@@ -75,18 +87,19 @@ snd_create_device(struct device *parent,
return device;
}
-static int snd_bcm2835_free(struct bcm2835_chip *chip)
-{
- kfree(chip);
- return 0;
-}
-
/* component-destructor
* (see "Management of Cards and Components")
*/
static int snd_bcm2835_dev_free(struct snd_device *device)
{
- return snd_bcm2835_free(device->device_data);
+ struct bcm2835_chip *chip = device->device_data;
+ struct snd_card *card = chip->card;
+
+ /* TODO: free pcm, ctl */
+
+ snd_device_free(card, chip);
+
+ return 0;
}
/* chip-specific constructor
@@ -111,7 +124,7 @@ static int snd_bcm2835_create(struct snd_card *card,
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
if (err) {
- snd_bcm2835_free(chip);
+ kfree(chip);
return err;
}
@@ -119,31 +132,14 @@ static int snd_bcm2835_create(struct snd_card *card,
return 0;
}
-static void snd_devm_card_free(struct device *dev, void *res)
+static struct snd_card *snd_bcm2835_card_new(struct device *dev)
{
- struct snd_card *snd_card = *(struct snd_card **)res;
-
- snd_card_free(snd_card);
-}
-
-static struct snd_card *snd_devm_card_new(struct device *dev)
-{
- struct snd_card **dr;
struct snd_card *card;
int ret;
- dr = devres_alloc(snd_devm_card_free, sizeof(*dr), GFP_KERNEL);
- if (!dr)
- return ERR_PTR(-ENOMEM);
-
ret = snd_card_new(dev, -1, NULL, THIS_MODULE, 0, &card);
- if (ret) {
- devres_free(dr);
+ if (ret)
return ERR_PTR(ret);
- }
-
- *dr = card;
- devres_add(dev, dr);
return card;
}
@@ -260,7 +256,7 @@ static int snd_add_child_device(struct device *device,
return PTR_ERR(child);
}
- card = snd_devm_card_new(child);
+ card = snd_bcm2835_card_new(child);
if (IS_ERR(card)) {
dev_err(child, "Failed to create card");
return PTR_ERR(card);
@@ -302,7 +298,7 @@ static int snd_add_child_device(struct device *device,
return err;
}
- dev_set_drvdata(child, card);
+ dev_set_drvdata(child, chip);
dev_info(child, "card created with %d channels\n", numchans);
return 0;
diff --git a/drivers/staging/vc04_services/bcm2835-camera/controls.c b/drivers/staging/vc04_services/bcm2835-camera/controls.c
index 0736214e1422..cff7b1e07153 100644
--- a/drivers/staging/vc04_services/bcm2835-camera/controls.c
+++ b/drivers/staging/vc04_services/bcm2835-camera/controls.c
@@ -1270,6 +1270,7 @@ int bm2835_mmal_init_controls(struct bm2835_mmal_dev *dev,
* mismatches.
*/
int i;
+
mask = 1 << V4L2_SCENE_MODE_NONE;
for (i = 0;
i < ARRAY_SIZE(scene_configs);
diff --git a/drivers/staging/vc04_services/interface/vchi/TODO b/drivers/staging/vc04_services/interface/vchi/TODO
index df93154b1aa6..86708c7c8ec3 100644
--- a/drivers/staging/vc04_services/interface/vchi/TODO
+++ b/drivers/staging/vc04_services/interface/vchi/TODO
@@ -1,9 +1,4 @@
-1) Write a DT binding doc and get the corresponding DT node merged to
- bcm2835.
-
-This will let the driver probe when enabled.
-
-2) Import drivers using VCHI.
+1) Import drivers using VCHI.
VCHI is just a tool to let drivers talk to the firmware. Here are
some of the ones we want:
@@ -14,11 +9,6 @@ some of the ones we want:
requests to the firmware, which are transmitted across VCHIQ. vcdbg
is really useful for debugging firmware interactions.
- - bcm2835-camera (https://github.com/raspberrypi/linux/tree/rpi-4.4.y/drivers/media/platform/bcm2835)
-
- This driver will let us get images from the camera using the MMAL
- protocol over VCHI.
-
- VCSM (https://github.com/raspberrypi/linux/tree/rpi-4.4.y/drivers/char/broadcom/vc_sm)
This driver is used for talking about regions of VC memory across
@@ -26,10 +16,41 @@ some of the ones we want:
to manage these buffers as dmabufs so that we can zero-copy import
camera images into vc4 for rendering/display.
-3) Garbage-collect unused code
+2) Garbage-collect unused code
One of the reasons this driver wasn't upstreamed previously was that
there's a lot code that got built that's probably unnecessary these
days. Once we have the set of VCHI-using drivers we want in tree, we
should be able to do a sweep of the code to see what's left that's
unused.
+
+3) Make driver more portable
+
+Building this driver with arm/multi_v7_defconfig or arm64/defconfig
+leads to data corruption during the following command:
+
+ vchiq_test -f 1
+
+This should be fixed.
+
+4) Fix kernel module support
+
+Even the VPU firmware doesn't support a VCHI re-connect, the driver
+should properly handle a module unload. This also includes that all
+resouces must be freed (kthreads, debugfs entries, ...) and global
+variables avoided.
+
+5) Fix stack hog
+
+Running make checkstack shows that vchiq_dump_service_use_state() has
+an extensive stack usage. Maybe other functions are also affected.
+
+6) Cleanup logging mechanism
+
+The driver should probably be using the standard kernel logging mechanisms
+such as dev_info, dev_dbg, and friends.
+
+7) Documentation
+
+A short top-down description of this driver's architecture (function of
+kthreads, userspace, limitations) could be very helpful for reviewers.
diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
index b59ef14890aa..afdd3e944f3f 100644
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
@@ -77,7 +77,17 @@ struct vchiq_pagelist_info {
};
static void __iomem *g_regs;
-static unsigned int g_cache_line_size = sizeof(CACHE_LINE_SIZE);
+/* This value is the size of the L2 cache lines as understood by the
+ * VPU firmware, which determines the required alignment of the
+ * offsets/sizes in pagelists.
+ *
+ * Modern VPU firmware looks for a DT "cache-line-size" property in
+ * the VCHIQ node and will overwrite it with the actual L2 cache size,
+ * which the kernel must then respect. That property was rejected
+ * upstream, so we have to use the VPU firmware's compatibility value
+ * of 32.
+ */
+static unsigned int g_cache_line_size = 32;
static unsigned int g_fragments_size;
static char *g_fragments_base;
static char *g_free_fragments;
@@ -117,14 +127,6 @@ int vchiq_platform_init(struct platform_device *pdev, VCHIQ_STATE_T *state)
if (err < 0)
return err;
- err = of_property_read_u32(dev->of_node, "cache-line-size",
- &g_cache_line_size);
-
- if (err) {
- dev_err(dev, "Missing cache-line-size property\n");
- return -ENODEV;
- }
-
g_fragments_size = 2 * g_cache_line_size;
/* Allocate space for the channels in coherent memory */
diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
index c2c440009cac..24d456b0a6f0 100644
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
@@ -193,6 +193,355 @@ static const char *const ioctl_names[] = {
vchiq_static_assert(ARRAY_SIZE(ioctl_names) ==
(VCHIQ_IOC_MAX + 1));
+static VCHIQ_STATUS_T
+vchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data,
+ unsigned int size, VCHIQ_BULK_DIR_T dir);
+
+#define VCHIQ_INIT_RETRIES 10
+VCHIQ_STATUS_T vchiq_initialise(VCHIQ_INSTANCE_T *instance_out)
+{
+ VCHIQ_STATUS_T status = VCHIQ_ERROR;
+ VCHIQ_STATE_T *state;
+ VCHIQ_INSTANCE_T instance = NULL;
+ int i;
+
+ vchiq_log_trace(vchiq_core_log_level, "%s called", __func__);
+
+ /* VideoCore may not be ready due to boot up timing.
+ * It may never be ready if kernel and firmware are mismatched,so don't
+ * block forever.
+ */
+ for (i = 0; i < VCHIQ_INIT_RETRIES; i++) {
+ state = vchiq_get_state();
+ if (state)
+ break;
+ udelay(500);
+ }
+ if (i == VCHIQ_INIT_RETRIES) {
+ vchiq_log_error(vchiq_core_log_level,
+ "%s: videocore not initialized\n", __func__);
+ goto failed;
+ } else if (i > 0) {
+ vchiq_log_warning(vchiq_core_log_level,
+ "%s: videocore initialized after %d retries\n",
+ __func__, i);
+ }
+
+ instance = kzalloc(sizeof(*instance), GFP_KERNEL);
+ if (!instance) {
+ vchiq_log_error(vchiq_core_log_level,
+ "%s: error allocating vchiq instance\n", __func__);
+ goto failed;
+ }
+
+ instance->connected = 0;
+ instance->state = state;
+ mutex_init(&instance->bulk_waiter_list_mutex);
+ INIT_LIST_HEAD(&instance->bulk_waiter_list);
+
+ *instance_out = instance;
+
+ status = VCHIQ_SUCCESS;
+
+failed:
+ vchiq_log_trace(vchiq_core_log_level,
+ "%s(%p): returning %d", __func__, instance, status);
+
+ return status;
+}
+EXPORT_SYMBOL(vchiq_initialise);
+
+VCHIQ_STATUS_T vchiq_shutdown(VCHIQ_INSTANCE_T instance)
+{
+ VCHIQ_STATUS_T status;
+ VCHIQ_STATE_T *state = instance->state;
+
+ vchiq_log_trace(vchiq_core_log_level,
+ "%s(%p) called", __func__, instance);
+
+ if (mutex_lock_killable(&state->mutex) != 0)
+ return VCHIQ_RETRY;
+
+ /* Remove all services */
+ status = vchiq_shutdown_internal(state, instance);
+
+ mutex_unlock(&state->mutex);
+
+ vchiq_log_trace(vchiq_core_log_level,
+ "%s(%p): returning %d", __func__, instance, status);
+
+ if (status == VCHIQ_SUCCESS) {
+ struct list_head *pos, *next;
+
+ list_for_each_safe(pos, next,
+ &instance->bulk_waiter_list) {
+ struct bulk_waiter_node *waiter;
+
+ waiter = list_entry(pos,
+ struct bulk_waiter_node,
+ list);
+ list_del(pos);
+ vchiq_log_info(vchiq_arm_log_level,
+ "bulk_waiter - cleaned up %pK for pid %d",
+ waiter, waiter->pid);
+ kfree(waiter);
+ }
+ kfree(instance);
+ }
+
+ return status;
+}
+EXPORT_SYMBOL(vchiq_shutdown);
+
+static int vchiq_is_connected(VCHIQ_INSTANCE_T instance)
+{
+ return instance->connected;
+}
+
+VCHIQ_STATUS_T vchiq_connect(VCHIQ_INSTANCE_T instance)
+{
+ VCHIQ_STATUS_T status;
+ VCHIQ_STATE_T *state = instance->state;
+
+ vchiq_log_trace(vchiq_core_log_level,
+ "%s(%p) called", __func__, instance);
+
+ if (mutex_lock_killable(&state->mutex) != 0) {
+ vchiq_log_trace(vchiq_core_log_level,
+ "%s: call to mutex_lock failed", __func__);
+ status = VCHIQ_RETRY;
+ goto failed;
+ }
+ status = vchiq_connect_internal(state, instance);
+
+ if (status == VCHIQ_SUCCESS)
+ instance->connected = 1;
+
+ mutex_unlock(&state->mutex);
+
+failed:
+ vchiq_log_trace(vchiq_core_log_level,
+ "%s(%p): returning %d", __func__, instance, status);
+
+ return status;
+}
+EXPORT_SYMBOL(vchiq_connect);
+
+VCHIQ_STATUS_T vchiq_add_service(
+ VCHIQ_INSTANCE_T instance,
+ const VCHIQ_SERVICE_PARAMS_T *params,
+ VCHIQ_SERVICE_HANDLE_T *phandle)
+{
+ VCHIQ_STATUS_T status;
+ VCHIQ_STATE_T *state = instance->state;
+ VCHIQ_SERVICE_T *service = NULL;
+ int srvstate;
+
+ vchiq_log_trace(vchiq_core_log_level,
+ "%s(%p) called", __func__, instance);
+
+ *phandle = VCHIQ_SERVICE_HANDLE_INVALID;
+
+ srvstate = vchiq_is_connected(instance)
+ ? VCHIQ_SRVSTATE_LISTENING
+ : VCHIQ_SRVSTATE_HIDDEN;
+
+ service = vchiq_add_service_internal(
+ state,
+ params,
+ srvstate,
+ instance,
+ NULL);
+
+ if (service) {
+ *phandle = service->handle;
+ status = VCHIQ_SUCCESS;
+ } else
+ status = VCHIQ_ERROR;
+
+ vchiq_log_trace(vchiq_core_log_level,
+ "%s(%p): returning %d", __func__, instance, status);
+
+ return status;
+}
+EXPORT_SYMBOL(vchiq_add_service);
+
+VCHIQ_STATUS_T vchiq_open_service(
+ VCHIQ_INSTANCE_T instance,
+ const VCHIQ_SERVICE_PARAMS_T *params,
+ VCHIQ_SERVICE_HANDLE_T *phandle)
+{
+ VCHIQ_STATUS_T status = VCHIQ_ERROR;
+ VCHIQ_STATE_T *state = instance->state;
+ VCHIQ_SERVICE_T *service = NULL;
+
+ vchiq_log_trace(vchiq_core_log_level,
+ "%s(%p) called", __func__, instance);
+
+ *phandle = VCHIQ_SERVICE_HANDLE_INVALID;
+
+ if (!vchiq_is_connected(instance))
+ goto failed;
+
+ service = vchiq_add_service_internal(state,
+ params,
+ VCHIQ_SRVSTATE_OPENING,
+ instance,
+ NULL);
+
+ if (service) {
+ *phandle = service->handle;
+ status = vchiq_open_service_internal(service, current->pid);
+ if (status != VCHIQ_SUCCESS) {
+ vchiq_remove_service(service->handle);
+ *phandle = VCHIQ_SERVICE_HANDLE_INVALID;
+ }
+ }
+
+failed:
+ vchiq_log_trace(vchiq_core_log_level,
+ "%s(%p): returning %d", __func__, instance, status);
+
+ return status;
+}
+EXPORT_SYMBOL(vchiq_open_service);
+
+VCHIQ_STATUS_T
+vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, const void *data,
+ unsigned int size, void *userdata, VCHIQ_BULK_MODE_T mode)
+{
+ VCHIQ_STATUS_T status;
+
+ switch (mode) {
+ case VCHIQ_BULK_MODE_NOCALLBACK:
+ case VCHIQ_BULK_MODE_CALLBACK:
+ status = vchiq_bulk_transfer(handle,
+ VCHI_MEM_HANDLE_INVALID, (void *)data, size, userdata,
+ mode, VCHIQ_BULK_TRANSMIT);
+ break;
+ case VCHIQ_BULK_MODE_BLOCKING:
+ status = vchiq_blocking_bulk_transfer(handle,
+ (void *)data, size, VCHIQ_BULK_TRANSMIT);
+ break;
+ default:
+ return VCHIQ_ERROR;
+ }
+
+ return status;
+}
+EXPORT_SYMBOL(vchiq_bulk_transmit);
+
+VCHIQ_STATUS_T
+vchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data,
+ unsigned int size, void *userdata, VCHIQ_BULK_MODE_T mode)
+{
+ VCHIQ_STATUS_T status;
+
+ switch (mode) {
+ case VCHIQ_BULK_MODE_NOCALLBACK:
+ case VCHIQ_BULK_MODE_CALLBACK:
+ status = vchiq_bulk_transfer(handle,
+ VCHI_MEM_HANDLE_INVALID, data, size, userdata,
+ mode, VCHIQ_BULK_RECEIVE);
+ break;
+ case VCHIQ_BULK_MODE_BLOCKING:
+ status = vchiq_blocking_bulk_transfer(handle,
+ (void *)data, size, VCHIQ_BULK_RECEIVE);
+ break;
+ default:
+ return VCHIQ_ERROR;
+ }
+
+ return status;
+}
+EXPORT_SYMBOL(vchiq_bulk_receive);
+
+static VCHIQ_STATUS_T
+vchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data,
+ unsigned int size, VCHIQ_BULK_DIR_T dir)
+{
+ VCHIQ_INSTANCE_T instance;
+ VCHIQ_SERVICE_T *service;
+ VCHIQ_STATUS_T status;
+ struct bulk_waiter_node *waiter = NULL;
+ struct list_head *pos;
+
+ service = find_service_by_handle(handle);
+ if (!service)
+ return VCHIQ_ERROR;
+
+ instance = service->instance;
+
+ unlock_service(service);
+
+ mutex_lock(&instance->bulk_waiter_list_mutex);
+ list_for_each(pos, &instance->bulk_waiter_list) {
+ if (list_entry(pos, struct bulk_waiter_node,
+ list)->pid == current->pid) {
+ waiter = list_entry(pos,
+ struct bulk_waiter_node,
+ list);
+ list_del(pos);
+ break;
+ }
+ }
+ mutex_unlock(&instance->bulk_waiter_list_mutex);
+
+ if (waiter) {
+ VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk;
+
+ if (bulk) {
+ /* This thread has an outstanding bulk transfer. */
+ if ((bulk->data != data) ||
+ (bulk->size != size)) {
+ /* This is not a retry of the previous one.
+ * Cancel the signal when the transfer
+ * completes.
+ */
+ spin_lock(&bulk_waiter_spinlock);
+ bulk->userdata = NULL;
+ spin_unlock(&bulk_waiter_spinlock);
+ }
+ }
+ }
+
+ if (!waiter) {
+ waiter = kzalloc(sizeof(struct bulk_waiter_node), GFP_KERNEL);
+ if (!waiter) {
+ vchiq_log_error(vchiq_core_log_level,
+ "%s - out of memory", __func__);
+ return VCHIQ_ERROR;
+ }
+ }
+
+ status = vchiq_bulk_transfer(handle, VCHI_MEM_HANDLE_INVALID,
+ data, size, &waiter->bulk_waiter, VCHIQ_BULK_MODE_BLOCKING,
+ dir);
+ if ((status != VCHIQ_RETRY) || fatal_signal_pending(current) ||
+ !waiter->bulk_waiter.bulk) {
+ VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk;
+
+ if (bulk) {
+ /* Cancel the signal when the transfer
+ * completes.
+ */
+ spin_lock(&bulk_waiter_spinlock);
+ bulk->userdata = NULL;
+ spin_unlock(&bulk_waiter_spinlock);
+ }
+ kfree(waiter);
+ } else {
+ waiter->pid = current->pid;
+ mutex_lock(&instance->bulk_waiter_list_mutex);
+ list_add(&waiter->list, &instance->bulk_waiter_list);
+ mutex_unlock(&instance->bulk_waiter_list_mutex);
+ vchiq_log_info(vchiq_arm_log_level,
+ "saved bulk_waiter %pK for pid %d",
+ waiter, current->pid);
+ }
+
+ return status;
+}
/****************************************************************************
*
* add_completion
@@ -3225,7 +3574,8 @@ static int vchiq_probe(struct platform_device *pdev)
struct rpi_firmware *fw;
int err;
- fw_node = of_parse_phandle(pdev->dev.of_node, "firmware", 0);
+ fw_node = of_find_compatible_node(NULL, NULL,
+ "raspberrypi,bcm2835-firmware");
if (!fw_node) {
dev_err(&pdev->dev, "Missing firmware node\n");
return -ENOENT;
diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_build_info.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_build_info.h
deleted file mode 100644
index df645813bdae..000000000000
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_build_info.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/**
- * Copyright (c) 2010-2012 Broadcom. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The names of the above-listed copyright holders may not be used
- * to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * ALTERNATIVELY, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2, as published by the Free
- * Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR
- * CONTRIBUTORS 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.
- */
-
-const char *vchiq_get_build_hostname(void);
-const char *vchiq_get_build_version(void);
-const char *vchiq_get_build_time(void);
-const char *vchiq_get_build_date(void);
diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_if.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_if.h
index 0e270852900d..e4109a83e628 100644
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_if.h
+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_if.h
@@ -149,16 +149,6 @@ vchiq_queue_message(VCHIQ_SERVICE_HANDLE_T handle,
size_t size);
extern void vchiq_release_message(VCHIQ_SERVICE_HANDLE_T service,
VCHIQ_HEADER_T *header);
-extern VCHIQ_STATUS_T vchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T service,
- const void *data, unsigned int size, void *userdata);
-extern VCHIQ_STATUS_T vchiq_queue_bulk_receive(VCHIQ_SERVICE_HANDLE_T service,
- void *data, unsigned int size, void *userdata);
-extern VCHIQ_STATUS_T vchiq_queue_bulk_transmit_handle(
- VCHIQ_SERVICE_HANDLE_T service, VCHI_MEM_HANDLE_T handle,
- const void *offset, unsigned int size, void *userdata);
-extern VCHIQ_STATUS_T vchiq_queue_bulk_receive_handle(
- VCHIQ_SERVICE_HANDLE_T service, VCHI_MEM_HANDLE_T handle,
- void *offset, unsigned int size, void *userdata);
extern VCHIQ_STATUS_T vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T service,
const void *data, unsigned int size, void *userdata,
VCHIQ_BULK_MODE_T mode);
diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c
deleted file mode 100644
index 43c89a08bda9..000000000000
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c
+++ /dev/null
@@ -1,431 +0,0 @@
-/**
- * Copyright (c) 2010-2012 Broadcom. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The names of the above-listed copyright holders may not be used
- * to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * ALTERNATIVELY, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2, as published by the Free
- * Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR
- * CONTRIBUTORS 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.
- */
-
-/* ---- Include Files ---------------------------------------------------- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-
-#include "vchiq_core.h"
-#include "vchiq_arm.h"
-#include "vchiq_killable.h"
-
-/* ---- Public Variables ------------------------------------------------- */
-
-/* ---- Private Constants and Types -------------------------------------- */
-
-struct bulk_waiter_node {
- struct bulk_waiter bulk_waiter;
- int pid;
- struct list_head list;
-};
-
-struct vchiq_instance_struct {
- VCHIQ_STATE_T *state;
-
- int connected;
-
- struct list_head bulk_waiter_list;
- struct mutex bulk_waiter_list_mutex;
-};
-
-static VCHIQ_STATUS_T
-vchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data,
- unsigned int size, VCHIQ_BULK_DIR_T dir);
-
-#define VCHIQ_INIT_RETRIES 10
-VCHIQ_STATUS_T vchiq_initialise(VCHIQ_INSTANCE_T *instance_out)
-{
- VCHIQ_STATUS_T status = VCHIQ_ERROR;
- VCHIQ_STATE_T *state;
- VCHIQ_INSTANCE_T instance = NULL;
- int i;
-
- vchiq_log_trace(vchiq_core_log_level, "%s called", __func__);
-
- /* VideoCore may not be ready due to boot up timing.
- * It may never be ready if kernel and firmware are mismatched,so don't
- * block forever.
- */
- for (i = 0; i < VCHIQ_INIT_RETRIES; i++) {
- state = vchiq_get_state();
- if (state)
- break;
- udelay(500);
- }
- if (i == VCHIQ_INIT_RETRIES) {
- vchiq_log_error(vchiq_core_log_level,
- "%s: videocore not initialized\n", __func__);
- goto failed;
- } else if (i > 0) {
- vchiq_log_warning(vchiq_core_log_level,
- "%s: videocore initialized after %d retries\n",
- __func__, i);
- }
-
- instance = kzalloc(sizeof(*instance), GFP_KERNEL);
- if (!instance) {
- vchiq_log_error(vchiq_core_log_level,
- "%s: error allocating vchiq instance\n", __func__);
- goto failed;
- }
-
- instance->connected = 0;
- instance->state = state;
- mutex_init(&instance->bulk_waiter_list_mutex);
- INIT_LIST_HEAD(&instance->bulk_waiter_list);
-
- *instance_out = instance;
-
- status = VCHIQ_SUCCESS;
-
-failed:
- vchiq_log_trace(vchiq_core_log_level,
- "%s(%p): returning %d", __func__, instance, status);
-
- return status;
-}
-EXPORT_SYMBOL(vchiq_initialise);
-
-VCHIQ_STATUS_T vchiq_shutdown(VCHIQ_INSTANCE_T instance)
-{
- VCHIQ_STATUS_T status;
- VCHIQ_STATE_T *state = instance->state;
-
- vchiq_log_trace(vchiq_core_log_level,
- "%s(%p) called", __func__, instance);
-
- if (mutex_lock_killable(&state->mutex) != 0)
- return VCHIQ_RETRY;
-
- /* Remove all services */
- status = vchiq_shutdown_internal(state, instance);
-
- mutex_unlock(&state->mutex);
-
- vchiq_log_trace(vchiq_core_log_level,
- "%s(%p): returning %d", __func__, instance, status);
-
- if (status == VCHIQ_SUCCESS) {
- struct list_head *pos, *next;
-
- list_for_each_safe(pos, next,
- &instance->bulk_waiter_list) {
- struct bulk_waiter_node *waiter;
-
- waiter = list_entry(pos,
- struct bulk_waiter_node,
- list);
- list_del(pos);
- vchiq_log_info(vchiq_arm_log_level,
- "bulk_waiter - cleaned up %pK for pid %d",
- waiter, waiter->pid);
- kfree(waiter);
- }
- kfree(instance);
- }
-
- return status;
-}
-EXPORT_SYMBOL(vchiq_shutdown);
-
-static int vchiq_is_connected(VCHIQ_INSTANCE_T instance)
-{
- return instance->connected;
-}
-
-VCHIQ_STATUS_T vchiq_connect(VCHIQ_INSTANCE_T instance)
-{
- VCHIQ_STATUS_T status;
- VCHIQ_STATE_T *state = instance->state;
-
- vchiq_log_trace(vchiq_core_log_level,
- "%s(%p) called", __func__, instance);
-
- if (mutex_lock_killable(&state->mutex) != 0) {
- vchiq_log_trace(vchiq_core_log_level,
- "%s: call to mutex_lock failed", __func__);
- status = VCHIQ_RETRY;
- goto failed;
- }
- status = vchiq_connect_internal(state, instance);
-
- if (status == VCHIQ_SUCCESS)
- instance->connected = 1;
-
- mutex_unlock(&state->mutex);
-
-failed:
- vchiq_log_trace(vchiq_core_log_level,
- "%s(%p): returning %d", __func__, instance, status);
-
- return status;
-}
-EXPORT_SYMBOL(vchiq_connect);
-
-VCHIQ_STATUS_T vchiq_add_service(
- VCHIQ_INSTANCE_T instance,
- const VCHIQ_SERVICE_PARAMS_T *params,
- VCHIQ_SERVICE_HANDLE_T *phandle)
-{
- VCHIQ_STATUS_T status;
- VCHIQ_STATE_T *state = instance->state;
- VCHIQ_SERVICE_T *service = NULL;
- int srvstate;
-
- vchiq_log_trace(vchiq_core_log_level,
- "%s(%p) called", __func__, instance);
-
- *phandle = VCHIQ_SERVICE_HANDLE_INVALID;
-
- srvstate = vchiq_is_connected(instance)
- ? VCHIQ_SRVSTATE_LISTENING
- : VCHIQ_SRVSTATE_HIDDEN;
-
- service = vchiq_add_service_internal(
- state,
- params,
- srvstate,
- instance,
- NULL);
-
- if (service) {
- *phandle = service->handle;
- status = VCHIQ_SUCCESS;
- } else
- status = VCHIQ_ERROR;
-
- vchiq_log_trace(vchiq_core_log_level,
- "%s(%p): returning %d", __func__, instance, status);
-
- return status;
-}
-EXPORT_SYMBOL(vchiq_add_service);
-
-VCHIQ_STATUS_T vchiq_open_service(
- VCHIQ_INSTANCE_T instance,
- const VCHIQ_SERVICE_PARAMS_T *params,
- VCHIQ_SERVICE_HANDLE_T *phandle)
-{
- VCHIQ_STATUS_T status = VCHIQ_ERROR;
- VCHIQ_STATE_T *state = instance->state;
- VCHIQ_SERVICE_T *service = NULL;
-
- vchiq_log_trace(vchiq_core_log_level,
- "%s(%p) called", __func__, instance);
-
- *phandle = VCHIQ_SERVICE_HANDLE_INVALID;
-
- if (!vchiq_is_connected(instance))
- goto failed;
-
- service = vchiq_add_service_internal(state,
- params,
- VCHIQ_SRVSTATE_OPENING,
- instance,
- NULL);
-
- if (service) {
- *phandle = service->handle;
- status = vchiq_open_service_internal(service, current->pid);
- if (status != VCHIQ_SUCCESS) {
- vchiq_remove_service(service->handle);
- *phandle = VCHIQ_SERVICE_HANDLE_INVALID;
- }
- }
-
-failed:
- vchiq_log_trace(vchiq_core_log_level,
- "%s(%p): returning %d", __func__, instance, status);
-
- return status;
-}
-EXPORT_SYMBOL(vchiq_open_service);
-
-VCHIQ_STATUS_T
-vchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle,
- const void *data, unsigned int size, void *userdata)
-{
- return vchiq_bulk_transfer(handle,
- VCHI_MEM_HANDLE_INVALID, (void *)data, size, userdata,
- VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_TRANSMIT);
-}
-EXPORT_SYMBOL(vchiq_queue_bulk_transmit);
-
-VCHIQ_STATUS_T
-vchiq_queue_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data,
- unsigned int size, void *userdata)
-{
- return vchiq_bulk_transfer(handle,
- VCHI_MEM_HANDLE_INVALID, data, size, userdata,
- VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_RECEIVE);
-}
-EXPORT_SYMBOL(vchiq_queue_bulk_receive);
-
-VCHIQ_STATUS_T
-vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, const void *data,
- unsigned int size, void *userdata, VCHIQ_BULK_MODE_T mode)
-{
- VCHIQ_STATUS_T status;
-
- switch (mode) {
- case VCHIQ_BULK_MODE_NOCALLBACK:
- case VCHIQ_BULK_MODE_CALLBACK:
- status = vchiq_bulk_transfer(handle,
- VCHI_MEM_HANDLE_INVALID, (void *)data, size, userdata,
- mode, VCHIQ_BULK_TRANSMIT);
- break;
- case VCHIQ_BULK_MODE_BLOCKING:
- status = vchiq_blocking_bulk_transfer(handle,
- (void *)data, size, VCHIQ_BULK_TRANSMIT);
- break;
- default:
- return VCHIQ_ERROR;
- }
-
- return status;
-}
-EXPORT_SYMBOL(vchiq_bulk_transmit);
-
-VCHIQ_STATUS_T
-vchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data,
- unsigned int size, void *userdata, VCHIQ_BULK_MODE_T mode)
-{
- VCHIQ_STATUS_T status;
-
- switch (mode) {
- case VCHIQ_BULK_MODE_NOCALLBACK:
- case VCHIQ_BULK_MODE_CALLBACK:
- status = vchiq_bulk_transfer(handle,
- VCHI_MEM_HANDLE_INVALID, data, size, userdata,
- mode, VCHIQ_BULK_RECEIVE);
- break;
- case VCHIQ_BULK_MODE_BLOCKING:
- status = vchiq_blocking_bulk_transfer(handle,
- (void *)data, size, VCHIQ_BULK_RECEIVE);
- break;
- default:
- return VCHIQ_ERROR;
- }
-
- return status;
-}
-EXPORT_SYMBOL(vchiq_bulk_receive);
-
-static VCHIQ_STATUS_T
-vchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data,
- unsigned int size, VCHIQ_BULK_DIR_T dir)
-{
- VCHIQ_INSTANCE_T instance;
- VCHIQ_SERVICE_T *service;
- VCHIQ_STATUS_T status;
- struct bulk_waiter_node *waiter = NULL;
- struct list_head *pos;
-
- service = find_service_by_handle(handle);
- if (!service)
- return VCHIQ_ERROR;
-
- instance = service->instance;
-
- unlock_service(service);
-
- mutex_lock(&instance->bulk_waiter_list_mutex);
- list_for_each(pos, &instance->bulk_waiter_list) {
- if (list_entry(pos, struct bulk_waiter_node,
- list)->pid == current->pid) {
- waiter = list_entry(pos,
- struct bulk_waiter_node,
- list);
- list_del(pos);
- break;
- }
- }
- mutex_unlock(&instance->bulk_waiter_list_mutex);
-
- if (waiter) {
- VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk;
-
- if (bulk) {
- /* This thread has an outstanding bulk transfer. */
- if ((bulk->data != data) ||
- (bulk->size != size)) {
- /* This is not a retry of the previous one.
- * Cancel the signal when the transfer
- * completes.
- */
- spin_lock(&bulk_waiter_spinlock);
- bulk->userdata = NULL;
- spin_unlock(&bulk_waiter_spinlock);
- }
- }
- }
-
- if (!waiter) {
- waiter = kzalloc(sizeof(struct bulk_waiter_node), GFP_KERNEL);
- if (!waiter) {
- vchiq_log_error(vchiq_core_log_level,
- "%s - out of memory", __func__);
- return VCHIQ_ERROR;
- }
- }
-
- status = vchiq_bulk_transfer(handle, VCHI_MEM_HANDLE_INVALID,
- data, size, &waiter->bulk_waiter, VCHIQ_BULK_MODE_BLOCKING,
- dir);
- if ((status != VCHIQ_RETRY) || fatal_signal_pending(current) ||
- !waiter->bulk_waiter.bulk) {
- VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk;
-
- if (bulk) {
- /* Cancel the signal when the transfer
- * completes.
- */
- spin_lock(&bulk_waiter_spinlock);
- bulk->userdata = NULL;
- spin_unlock(&bulk_waiter_spinlock);
- }
- kfree(waiter);
- } else {
- waiter->pid = current->pid;
- mutex_lock(&instance->bulk_waiter_list_mutex);
- list_add(&waiter->list, &instance->bulk_waiter_list);
- mutex_unlock(&instance->bulk_waiter_list_mutex);
- vchiq_log_info(vchiq_arm_log_level,
- "saved bulk_waiter %pK for pid %d",
- waiter, current->pid);
- }
-
- return status;
-}
diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_memdrv.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_memdrv.h
deleted file mode 100644
index c233b866725b..000000000000
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_memdrv.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/**
- * Copyright (c) 2010-2012 Broadcom. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The names of the above-listed copyright holders may not be used
- * to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * ALTERNATIVELY, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2, as published by the Free
- * Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR
- * CONTRIBUTORS 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.
- */
-
-#ifndef VCHIQ_MEMDRV_H
-#define VCHIQ_MEMDRV_H
-
-/* ---- Include Files ----------------------------------------------------- */
-
-#include <linux/kernel.h>
-#include "vchiq_if.h"
-
-/* ---- Constants and Types ---------------------------------------------- */
-
-/* ---- Variable Externs ------------------------------------------------- */
-
-/* ---- Function Prototypes ---------------------------------------------- */
-
-VCHIQ_STATUS_T vchiq_memdrv_initialise(void);
-
-VCHIQ_STATUS_T vchiq_userdrv_create_instance(
- const VCHIQ_PLATFORM_DATA_T * platform_data);
-
-VCHIQ_STATUS_T vchiq_userdrv_suspend(
- const VCHIQ_PLATFORM_DATA_T * platform_data);
-
-VCHIQ_STATUS_T vchiq_userdrv_resume(
- const VCHIQ_PLATFORM_DATA_T * platform_data);
-
-#endif
diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_pagelist.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_pagelist.h
index 926c247fd9d3..bec411061554 100644
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_pagelist.h
+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_pagelist.h
@@ -34,7 +34,6 @@
#ifndef VCHIQ_PAGELIST_H
#define VCHIQ_PAGELIST_H
-#define CACHE_LINE_SIZE 32
#define PAGELIST_WRITE 0
#define PAGELIST_READ 1
#define PAGELIST_READ_WITH_FRAGMENTS 2
@@ -49,9 +48,4 @@ typedef struct pagelist_struct {
*/
} PAGELIST_T;
-typedef struct fragments_struct {
- char headbuf[CACHE_LINE_SIZE];
- char tailbuf[CACHE_LINE_SIZE];
-} FRAGMENTS_T;
-
#endif /* VCHIQ_PAGELIST_H */
diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_version.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_version.c
deleted file mode 100644
index 994b81798134..000000000000
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_version.c
+++ /dev/null
@@ -1,59 +0,0 @@
-/**
- * Copyright (c) 2010-2012 Broadcom. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The names of the above-listed copyright holders may not be used
- * to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * ALTERNATIVELY, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2, as published by the Free
- * Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR
- * CONTRIBUTORS 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.
- */
-#include "vchiq_build_info.h"
-#include <linux/broadcom/vc_debug_sym.h>
-
-VC_DEBUG_DECLARE_STRING_VAR(vchiq_build_hostname, "dc4-arm-01");
-VC_DEBUG_DECLARE_STRING_VAR(vchiq_build_version, "9245b4c35b99b3870e1f7dc598c5692b3c66a6f0 (tainted)");
-VC_DEBUG_DECLARE_STRING_VAR(vchiq_build_time, __TIME__);
-VC_DEBUG_DECLARE_STRING_VAR(vchiq_build_date, __DATE__);
-
-const char *vchiq_get_build_hostname(void)
-{
- return vchiq_build_hostname;
-}
-
-const char *vchiq_get_build_version(void)
-{
- return vchiq_build_version;
-}
-
-const char *vchiq_get_build_date(void)
-{
- return vchiq_build_date;
-}
-
-const char *vchiq_get_build_time(void)
-{
- return vchiq_build_time;
-}
diff --git a/drivers/staging/vme/devices/vme_user.c b/drivers/staging/vme/devices/vme_user.c
index 3242dee8246f..6a33aaa1a49f 100644
--- a/drivers/staging/vme/devices/vme_user.c
+++ b/drivers/staging/vme/devices/vme_user.c
@@ -558,7 +558,7 @@ static int vme_user_probe(struct vme_dev *vdev)
vme_user_cdev->owner = THIS_MODULE;
err = cdev_add(vme_user_cdev, MKDEV(VME_MAJOR, 0), VME_DEVS);
if (err)
- goto err_char;
+ goto err_class;
/* Request slave resources and allocate buffers (128kB wide) */
for (i = SLAVE_MINOR; i < (SLAVE_MAX + 1); i++) {
@@ -618,7 +618,7 @@ static int vme_user_probe(struct vme_dev *vdev)
if (IS_ERR(vme_user_sysfs_class)) {
dev_err(&vdev->dev, "Error creating vme_user class.\n");
err = PTR_ERR(vme_user_sysfs_class);
- goto err_class;
+ goto err_master;
}
/* Add sysfs Entries */
diff --git a/drivers/staging/vt6655/baseband.h b/drivers/staging/vt6655/baseband.h
index b8ee33dcb352..30d9e9d20a39 100644
--- a/drivers/staging/vt6655/baseband.h
+++ b/drivers/staging/vt6655/baseband.h
@@ -46,13 +46,10 @@
#define TOP_RATE_2M 0x00200000
#define TOP_RATE_1M 0x00100000
-unsigned int
-BBuGetFrameTime(
- unsigned char byPreambleType,
- unsigned char byPktType,
- unsigned int cbFrameLength,
- unsigned short wRate
-);
+unsigned int BBuGetFrameTime(unsigned char byPreambleType,
+ unsigned char byPktType,
+ unsigned int cbFrameLength,
+ unsigned short wRate);
void vnt_get_phy_field(struct vnt_private *priv, u32 frame_length,
u16 tx_rate, u8 pkt_type, struct vnt_phy_field *phy);
diff --git a/drivers/staging/vt6655/card.h b/drivers/staging/vt6655/card.h
index 487039a64587..5884fd56153e 100644
--- a/drivers/staging/vt6655/card.h
+++ b/drivers/staging/vt6655/card.h
@@ -39,19 +39,6 @@
#define CB_MAX_CHANNEL_5G 42
#define CB_MAX_CHANNEL (CB_MAX_CHANNEL_24G + CB_MAX_CHANNEL_5G)
-typedef enum _CARD_PKT_TYPE {
- PKT_TYPE_802_11_BCN,
- PKT_TYPE_802_11_MNG,
- PKT_TYPE_802_11_DATA,
- PKT_TYPE_802_11_ALL
-} CARD_PKT_TYPE, *PCARD_PKT_TYPE;
-
-typedef enum _CARD_STATUS_TYPE {
- CARD_STATUS_MEDIA_CONNECT,
- CARD_STATUS_MEDIA_DISCONNECT,
- CARD_STATUS_PMKID
-} CARD_STATUS_TYPE, *PCARD_STATUS_TYPE;
-
struct vnt_private;
void CARDvSetRSPINF(struct vnt_private *priv, u8 bb_type);
diff --git a/drivers/staging/vt6655/device_cfg.h b/drivers/staging/vt6655/device_cfg.h
index 73f904b51b96..6b41c74f7c2a 100644
--- a/drivers/staging/vt6655/device_cfg.h
+++ b/drivers/staging/vt6655/device_cfg.h
@@ -16,13 +16,6 @@
#include <linux/types.h>
-typedef
-struct _version {
- unsigned char major;
- unsigned char minor;
- unsigned char build;
-} version_t, *pversion_t;
-
#define VID_TABLE_SIZE 64
#define MCAST_TABLE_SIZE 64
#define MCAM_SIZE 32
@@ -52,8 +45,4 @@ struct _version {
#define PKT_BUF_SZ 2390
-typedef enum _chip_type {
- VT3253 = 1
-} CHIP_TYPE, *PCHIP_TYPE;
-
#endif
diff --git a/drivers/staging/vt6655/device_main.c b/drivers/staging/vt6655/device_main.c
index 0dc902022a91..fbc4bc68144c 100644
--- a/drivers/staging/vt6655/device_main.c
+++ b/drivers/staging/vt6655/device_main.c
@@ -543,7 +543,7 @@ static void device_init_rd0_ring(struct vnt_private *priv)
if (!device_alloc_rx_buf(priv, desc))
dev_err(&priv->pcid->dev, "can not alloc rx bufs\n");
- desc->next = &(priv->aRD0Ring[(i + 1) % priv->opts.rx_descs0]);
+ desc->next = &priv->aRD0Ring[(i + 1) % priv->opts.rx_descs0];
desc->next_desc = cpu_to_le32(curr + sizeof(struct vnt_rx_desc));
}
@@ -567,7 +567,7 @@ static void device_init_rd1_ring(struct vnt_private *priv)
if (!device_alloc_rx_buf(priv, desc))
dev_err(&priv->pcid->dev, "can not alloc rx bufs\n");
- desc->next = &(priv->aRD1Ring[(i+1) % priv->opts.rx_descs1]);
+ desc->next = &priv->aRD1Ring[(i+1) % priv->opts.rx_descs1];
desc->next_desc = cpu_to_le32(curr + sizeof(struct vnt_rx_desc));
}
@@ -581,7 +581,7 @@ static void device_free_rd0_ring(struct vnt_private *priv)
int i;
for (i = 0; i < priv->opts.rx_descs0; i++) {
- struct vnt_rx_desc *desc = &(priv->aRD0Ring[i]);
+ struct vnt_rx_desc *desc = &priv->aRD0Ring[i];
struct vnt_rd_info *rd_info = desc->rd_info;
dma_unmap_single(&priv->pcid->dev, rd_info->skb_dma,
diff --git a/drivers/staging/vt6655/rxtx.c b/drivers/staging/vt6655/rxtx.c
index 9aa4d5262aaa..9c4a5325afc7 100644
--- a/drivers/staging/vt6655/rxtx.c
+++ b/drivers/staging/vt6655/rxtx.c
@@ -320,7 +320,6 @@ s_uGetDataDuration(
uNextPktTime = s_uGetTxRsvTime(pDevice, byPktType, cbLastFragmentSize, wFB_Opt1[FB_RATE0][wRate-RATE_18M], bNeedAck);
else
uNextPktTime = s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, wFB_Opt1[FB_RATE0][wRate-RATE_18M], bNeedAck);
-
}
if (bNeedAck) {
@@ -490,11 +489,9 @@ s_uFillDataHead(
bool is_pspoll
)
{
-
if (!pTxDataHead)
return 0;
-
if (byPktType == PK_TYPE_11GB || byPktType == PK_TYPE_11GA) {
if (byFBOption == AUTO_FB_NONE) {
struct vnt_tx_datahead_g *buf = pTxDataHead;
@@ -618,7 +615,6 @@ s_uFillDataHead(
return 0;
}
-
static
void
s_vFillRTSHead(
@@ -966,7 +962,7 @@ s_vGenerateTxParameter(
return;
if (byPktType == PK_TYPE_11GB || byPktType == PK_TYPE_11GA) {
- if (pvRTS != NULL) { /* RTS_need */
+ if (pvRTS) { /* RTS_need */
/* Fill RsvTime */
struct vnt_rrv_time_rts *buf = pvRrvTime;
@@ -988,7 +984,7 @@ s_vGenerateTxParameter(
s_vFillCTSHead(pDevice, uDMAIdx, byPktType, pvCTS, cbFrameSize, bNeedACK, bDisCRC, wCurrentRate, byFBOption);
}
} else if (byPktType == PK_TYPE_11A) {
- if (pvRTS != NULL) {/* RTS_need, non PCF mode */
+ if (pvRTS) {/* RTS_need, non PCF mode */
struct vnt_rrv_time_ab *buf = pvRrvTime;
buf->rts_rrv_time = s_uGetRTSCTSRsvTime(pDevice, 2, byPktType, cbFrameSize, wCurrentRate);
@@ -1002,7 +998,7 @@ s_vGenerateTxParameter(
buf->rrv_time = vnt_rxtx_rsvtime_le16(pDevice, PK_TYPE_11A, cbFrameSize, wCurrentRate, bNeedACK);
}
} else if (byPktType == PK_TYPE_11B) {
- if (pvRTS != NULL) {/* RTS_need, non PCF mode */
+ if (pvRTS) {/* RTS_need, non PCF mode */
struct vnt_rrv_time_ab *buf = pvRrvTime;
buf->rts_rrv_time = s_uGetRTSCTSRsvTime(pDevice, 0, byPktType, cbFrameSize, wCurrentRate);
@@ -1080,7 +1076,6 @@ s_cbFillTxBufHead(struct vnt_private *pDevice, unsigned char byPktType,
else if (fifo_ctl & FIFOCTL_AUTO_FB_1)
byFBOption = AUTO_FB_1;
-
/* Set RrvTime/RTS/CTS Buffer */
wTxBufSize = sizeof(struct vnt_tx_fifo_head);
if (byPktType == PK_TYPE_11GB || byPktType == PK_TYPE_11GA) {/* 802.11g packet */
@@ -1389,7 +1384,6 @@ int vnt_generate_fifo_header(struct vnt_private *priv, u32 dma_idx,
else if (priv->byAutoFBCtrl == AUTO_FB_1)
tx_buffer_head->fifo_ctl |=
cpu_to_le16(FIFOCTL_AUTO_FB_1);
-
}
tx_buffer_head->frag_ctl |= cpu_to_le16(FRAGCTL_NONFRAG);
diff --git a/drivers/staging/vt6656/usbpipe.c b/drivers/staging/vt6656/usbpipe.c
index 273176386a51..5bbc56f8779e 100644
--- a/drivers/staging/vt6656/usbpipe.c
+++ b/drivers/staging/vt6656/usbpipe.c
@@ -194,9 +194,6 @@ static void vnt_submit_rx_urb_complete(struct urb *urb)
if (vnt_rx_data(priv, rcb, urb->actual_length)) {
rcb->skb = dev_alloc_skb(priv->rx_buf_sz);
if (!rcb->skb) {
- dev_dbg(&priv->usb->dev,
- "Failed to re-alloc rx skb\n");
-
rcb->in_use = false;
return;
}
diff --git a/drivers/staging/wilc1000/coreconfigurator.c b/drivers/staging/wilc1000/coreconfigurator.c
index e98fc8e93011..db66b1cc80b3 100644
--- a/drivers/staging/wilc1000/coreconfigurator.c
+++ b/drivers/staging/wilc1000/coreconfigurator.c
@@ -148,19 +148,19 @@ static inline u8 get_from_ds(u8 *header)
return ((header[1] & 0x02) >> 1);
}
-static inline void get_address1(u8 *pu8msa, u8 *addr)
+static inline void get_address1(u8 *msa, u8 *addr)
{
- memcpy(addr, pu8msa + 4, 6);
+ memcpy(addr, msa + 4, 6);
}
-static inline void get_address2(u8 *pu8msa, u8 *addr)
+static inline void get_address2(u8 *msa, u8 *addr)
{
- memcpy(addr, pu8msa + 10, 6);
+ memcpy(addr, msa + 10, 6);
}
-static inline void get_address3(u8 *pu8msa, u8 *addr)
+static inline void get_address3(u8 *msa, u8 *addr)
{
- memcpy(addr, pu8msa + 16, 6);
+ memcpy(addr, msa + 16, 6);
}
static inline void get_BSSID(u8 *data, u8 *bssid)
@@ -238,30 +238,30 @@ static inline u16 get_asoc_id(u8 *data)
return asoc_id;
}
-static u8 *get_tim_elm(u8 *pu8msa, u16 rx_len, u16 tag_param_offset)
+static u8 *get_tim_elm(u8 *msa, u16 rx_len, u16 tag_param_offset)
{
u16 index;
index = tag_param_offset;
while (index < (rx_len - FCS_LEN)) {
- if (pu8msa[index] == ITIM)
- return &pu8msa[index];
- index += (IE_HDR_LEN + pu8msa[index + 1]);
+ if (msa[index] == ITIM)
+ return &msa[index];
+ index += (IE_HDR_LEN + msa[index + 1]);
}
return NULL;
}
-static u8 get_current_channel_802_11n(u8 *pu8msa, u16 rx_len)
+static u8 get_current_channel_802_11n(u8 *msa, u16 rx_len)
{
u16 index;
index = TAG_PARAM_OFFSET;
while (index < (rx_len - FCS_LEN)) {
- if (pu8msa[index] == IDSPARMS)
- return pu8msa[index + 2];
- index += pu8msa[index + 1] + IE_HDR_LEN;
+ if (msa[index] == IDSPARMS)
+ return msa[index + 2];
+ index += msa[index + 1] + IE_HDR_LEN;
}
return 0;
@@ -320,8 +320,8 @@ s32 wilc_parse_network_info(u8 *msg_buffer,
get_ssid(msa, network_info->ssid, &network_info->ssid_len);
get_BSSID(msa, network_info->bssid);
- network_info->ch = get_current_channel_802_11n(msa,
- rx_len + FCS_LEN);
+ network_info->ch = get_current_channel_802_11n(msa, rx_len
+ + FCS_LEN);
index = MAC_HDR_LEN + TIME_STAMP_LEN;
diff --git a/drivers/staging/wilc1000/host_interface.c b/drivers/staging/wilc1000/host_interface.c
index 358354b3a2b4..885f5fcead77 100644
--- a/drivers/staging/wilc1000/host_interface.c
+++ b/drivers/staging/wilc1000/host_interface.c
@@ -254,20 +254,13 @@ static u32 inactive_time;
static u8 del_beacon;
static u32 clients_count;
-static u8 *join_req;
-static u8 *info_element;
-static u8 mode_11i;
-static u8 auth_type;
-static u32 join_req_size;
-static u32 info_element_size;
-static struct wilc_vif *join_req_vif;
#define REAL_JOIN_REQ 0
#define FLUSHED_JOIN_REQ 1
#define FLUSHED_BYTE_POS 79
-static void *host_int_ParseJoinBssParam(struct network_info *ptstrNetworkInfo);
+static void *host_int_parse_join_bss_param(struct network_info *info);
static int host_int_get_ipaddress(struct wilc_vif *vif, u8 *ip_addr, u8 idx);
-static s32 Handle_ScanDone(struct wilc_vif *vif, enum scan_event enuEvent);
+static s32 handle_scan_done(struct wilc_vif *vif, enum scan_event evt);
static void host_if_work(struct work_struct *work);
/*!
@@ -469,8 +462,7 @@ static void handle_get_mac_address(struct wilc_vif *vif,
complete(&hif_wait_response);
}
-static void handle_cfg_param(struct wilc_vif *vif,
- struct cfg_param_attr *cfg_param_attr)
+static void handle_cfg_param(struct wilc_vif *vif, struct cfg_param_attr *param)
{
int ret = 0;
struct wid wid_list[32];
@@ -479,12 +471,12 @@ static void handle_cfg_param(struct wilc_vif *vif,
mutex_lock(&hif_drv->cfg_values_lock);
- if (cfg_param_attr->flag & BSS_TYPE) {
- u8 bss_type = cfg_param_attr->bss_type;
+ if (param->flag & BSS_TYPE) {
+ u8 bss_type = param->bss_type;
if (bss_type < 6) {
wid_list[i].id = WID_BSS_TYPE;
- wid_list[i].val = (s8 *)&bss_type;
+ wid_list[i].val = (s8 *)&param->bss_type;
wid_list[i].type = WID_CHAR;
wid_list[i].size = sizeof(char);
hif_drv->cfg_values.bss_type = bss_type;
@@ -494,228 +486,244 @@ static void handle_cfg_param(struct wilc_vif *vif,
}
i++;
}
- if (cfg_param_attr->flag & AUTH_TYPE) {
- if (cfg_param_attr->auth_type == 1 ||
- cfg_param_attr->auth_type == 2 ||
- cfg_param_attr->auth_type == 5) {
+ if (param->flag & AUTH_TYPE) {
+ u8 auth_type = param->auth_type;
+
+ if (auth_type == 1 || auth_type == 2 || auth_type == 5) {
wid_list[i].id = WID_AUTH_TYPE;
- wid_list[i].val = (s8 *)&cfg_param_attr->auth_type;
+ wid_list[i].val = (s8 *)&param->auth_type;
wid_list[i].type = WID_CHAR;
wid_list[i].size = sizeof(char);
- hif_drv->cfg_values.auth_type = (u8)cfg_param_attr->auth_type;
+ hif_drv->cfg_values.auth_type = auth_type;
} else {
netdev_err(vif->ndev, "Impossible value\n");
goto unlock;
}
i++;
}
- if (cfg_param_attr->flag & AUTHEN_TIMEOUT) {
- if (cfg_param_attr->auth_timeout > 0 &&
- cfg_param_attr->auth_timeout < 65536) {
+ if (param->flag & AUTHEN_TIMEOUT) {
+ if (param->auth_timeout > 0) {
wid_list[i].id = WID_AUTH_TIMEOUT;
- wid_list[i].val = (s8 *)&cfg_param_attr->auth_timeout;
+ wid_list[i].val = (s8 *)&param->auth_timeout;
wid_list[i].type = WID_SHORT;
wid_list[i].size = sizeof(u16);
- hif_drv->cfg_values.auth_timeout = cfg_param_attr->auth_timeout;
+ hif_drv->cfg_values.auth_timeout = param->auth_timeout;
} else {
netdev_err(vif->ndev, "Range(1 ~ 65535) over\n");
goto unlock;
}
i++;
}
- if (cfg_param_attr->flag & POWER_MANAGEMENT) {
- if (cfg_param_attr->power_mgmt_mode < 5) {
+ if (param->flag & POWER_MANAGEMENT) {
+ u8 pm_mode = param->power_mgmt_mode;
+
+ if (pm_mode < 5) {
wid_list[i].id = WID_POWER_MANAGEMENT;
- wid_list[i].val = (s8 *)&cfg_param_attr->power_mgmt_mode;
+ wid_list[i].val = (s8 *)&param->power_mgmt_mode;
wid_list[i].type = WID_CHAR;
wid_list[i].size = sizeof(char);
- hif_drv->cfg_values.power_mgmt_mode = (u8)cfg_param_attr->power_mgmt_mode;
+ hif_drv->cfg_values.power_mgmt_mode = pm_mode;
} else {
netdev_err(vif->ndev, "Invalid power mode\n");
goto unlock;
}
i++;
}
- if (cfg_param_attr->flag & RETRY_SHORT) {
- if (cfg_param_attr->short_retry_limit > 0 &&
- cfg_param_attr->short_retry_limit < 256) {
+ if (param->flag & RETRY_SHORT) {
+ u16 retry_limit = param->short_retry_limit;
+
+ if (retry_limit > 0 && retry_limit < 256) {
wid_list[i].id = WID_SHORT_RETRY_LIMIT;
- wid_list[i].val = (s8 *)&cfg_param_attr->short_retry_limit;
+ wid_list[i].val = (s8 *)&param->short_retry_limit;
wid_list[i].type = WID_SHORT;
wid_list[i].size = sizeof(u16);
- hif_drv->cfg_values.short_retry_limit = cfg_param_attr->short_retry_limit;
+ hif_drv->cfg_values.short_retry_limit = retry_limit;
} else {
netdev_err(vif->ndev, "Range(1~256) over\n");
goto unlock;
}
i++;
}
- if (cfg_param_attr->flag & RETRY_LONG) {
- if (cfg_param_attr->long_retry_limit > 0 &&
- cfg_param_attr->long_retry_limit < 256) {
+ if (param->flag & RETRY_LONG) {
+ u16 limit = param->long_retry_limit;
+
+ if (limit > 0 && limit < 256) {
wid_list[i].id = WID_LONG_RETRY_LIMIT;
- wid_list[i].val = (s8 *)&cfg_param_attr->long_retry_limit;
+ wid_list[i].val = (s8 *)&param->long_retry_limit;
wid_list[i].type = WID_SHORT;
wid_list[i].size = sizeof(u16);
- hif_drv->cfg_values.long_retry_limit = cfg_param_attr->long_retry_limit;
+ hif_drv->cfg_values.long_retry_limit = limit;
} else {
netdev_err(vif->ndev, "Range(1~256) over\n");
goto unlock;
}
i++;
}
- if (cfg_param_attr->flag & FRAG_THRESHOLD) {
- if (cfg_param_attr->frag_threshold > 255 &&
- cfg_param_attr->frag_threshold < 7937) {
+ if (param->flag & FRAG_THRESHOLD) {
+ u16 frag_th = param->frag_threshold;
+
+ if (frag_th > 255 && frag_th < 7937) {
wid_list[i].id = WID_FRAG_THRESHOLD;
- wid_list[i].val = (s8 *)&cfg_param_attr->frag_threshold;
+ wid_list[i].val = (s8 *)&param->frag_threshold;
wid_list[i].type = WID_SHORT;
wid_list[i].size = sizeof(u16);
- hif_drv->cfg_values.frag_threshold = cfg_param_attr->frag_threshold;
+ hif_drv->cfg_values.frag_threshold = frag_th;
} else {
netdev_err(vif->ndev, "Threshold Range fail\n");
goto unlock;
}
i++;
}
- if (cfg_param_attr->flag & RTS_THRESHOLD) {
- if (cfg_param_attr->rts_threshold > 255 &&
- cfg_param_attr->rts_threshold < 65536) {
+ if (param->flag & RTS_THRESHOLD) {
+ u16 rts_th = param->rts_threshold;
+
+ if (rts_th > 255) {
wid_list[i].id = WID_RTS_THRESHOLD;
- wid_list[i].val = (s8 *)&cfg_param_attr->rts_threshold;
+ wid_list[i].val = (s8 *)&param->rts_threshold;
wid_list[i].type = WID_SHORT;
wid_list[i].size = sizeof(u16);
- hif_drv->cfg_values.rts_threshold = cfg_param_attr->rts_threshold;
+ hif_drv->cfg_values.rts_threshold = rts_th;
} else {
netdev_err(vif->ndev, "Threshold Range fail\n");
goto unlock;
}
i++;
}
- if (cfg_param_attr->flag & PREAMBLE) {
- if (cfg_param_attr->preamble_type < 3) {
+ if (param->flag & PREAMBLE) {
+ u16 preamble_type = param->preamble_type;
+
+ if (param->preamble_type < 3) {
wid_list[i].id = WID_PREAMBLE;
- wid_list[i].val = (s8 *)&cfg_param_attr->preamble_type;
+ wid_list[i].val = (s8 *)&param->preamble_type;
wid_list[i].type = WID_CHAR;
wid_list[i].size = sizeof(char);
- hif_drv->cfg_values.preamble_type = cfg_param_attr->preamble_type;
+ hif_drv->cfg_values.preamble_type = preamble_type;
} else {
netdev_err(vif->ndev, "Preamle Range(0~2) over\n");
goto unlock;
}
i++;
}
- if (cfg_param_attr->flag & SHORT_SLOT_ALLOWED) {
- if (cfg_param_attr->short_slot_allowed < 2) {
+ if (param->flag & SHORT_SLOT_ALLOWED) {
+ u8 slot_allowed = param->short_slot_allowed;
+
+ if (slot_allowed < 2) {
wid_list[i].id = WID_SHORT_SLOT_ALLOWED;
- wid_list[i].val = (s8 *)&cfg_param_attr->short_slot_allowed;
+ wid_list[i].val = (s8 *)&param->short_slot_allowed;
wid_list[i].type = WID_CHAR;
wid_list[i].size = sizeof(char);
- hif_drv->cfg_values.short_slot_allowed = (u8)cfg_param_attr->short_slot_allowed;
+ hif_drv->cfg_values.short_slot_allowed = slot_allowed;
} else {
netdev_err(vif->ndev, "Short slot(2) over\n");
goto unlock;
}
i++;
}
- if (cfg_param_attr->flag & TXOP_PROT_DISABLE) {
- if (cfg_param_attr->txop_prot_disabled < 2) {
+ if (param->flag & TXOP_PROT_DISABLE) {
+ u8 prot_disabled = param->txop_prot_disabled;
+
+ if (param->txop_prot_disabled < 2) {
wid_list[i].id = WID_11N_TXOP_PROT_DISABLE;
- wid_list[i].val = (s8 *)&cfg_param_attr->txop_prot_disabled;
+ wid_list[i].val = (s8 *)&param->txop_prot_disabled;
wid_list[i].type = WID_CHAR;
wid_list[i].size = sizeof(char);
- hif_drv->cfg_values.txop_prot_disabled = (u8)cfg_param_attr->txop_prot_disabled;
+ hif_drv->cfg_values.txop_prot_disabled = prot_disabled;
} else {
netdev_err(vif->ndev, "TXOP prot disable\n");
goto unlock;
}
i++;
}
- if (cfg_param_attr->flag & BEACON_INTERVAL) {
- if (cfg_param_attr->beacon_interval > 0 &&
- cfg_param_attr->beacon_interval < 65536) {
+ if (param->flag & BEACON_INTERVAL) {
+ u16 beacon_interval = param->beacon_interval;
+
+ if (beacon_interval > 0) {
wid_list[i].id = WID_BEACON_INTERVAL;
- wid_list[i].val = (s8 *)&cfg_param_attr->beacon_interval;
+ wid_list[i].val = (s8 *)&param->beacon_interval;
wid_list[i].type = WID_SHORT;
wid_list[i].size = sizeof(u16);
- hif_drv->cfg_values.beacon_interval = cfg_param_attr->beacon_interval;
+ hif_drv->cfg_values.beacon_interval = beacon_interval;
} else {
netdev_err(vif->ndev, "Beacon interval(1~65535)fail\n");
goto unlock;
}
i++;
}
- if (cfg_param_attr->flag & DTIM_PERIOD) {
- if (cfg_param_attr->dtim_period > 0 &&
- cfg_param_attr->dtim_period < 256) {
+ if (param->flag & DTIM_PERIOD) {
+ if (param->dtim_period > 0 && param->dtim_period < 256) {
wid_list[i].id = WID_DTIM_PERIOD;
- wid_list[i].val = (s8 *)&cfg_param_attr->dtim_period;
+ wid_list[i].val = (s8 *)&param->dtim_period;
wid_list[i].type = WID_CHAR;
wid_list[i].size = sizeof(char);
- hif_drv->cfg_values.dtim_period = cfg_param_attr->dtim_period;
+ hif_drv->cfg_values.dtim_period = param->dtim_period;
} else {
netdev_err(vif->ndev, "DTIM range(1~255) fail\n");
goto unlock;
}
i++;
}
- if (cfg_param_attr->flag & SITE_SURVEY) {
- if (cfg_param_attr->site_survey_enabled < 3) {
+ if (param->flag & SITE_SURVEY) {
+ enum SITESURVEY enabled = param->site_survey_enabled;
+
+ if (enabled < 3) {
wid_list[i].id = WID_SITE_SURVEY;
- wid_list[i].val = (s8 *)&cfg_param_attr->site_survey_enabled;
+ wid_list[i].val = (s8 *)&param->site_survey_enabled;
wid_list[i].type = WID_CHAR;
wid_list[i].size = sizeof(char);
- hif_drv->cfg_values.site_survey_enabled = (u8)cfg_param_attr->site_survey_enabled;
+ hif_drv->cfg_values.site_survey_enabled = enabled;
} else {
netdev_err(vif->ndev, "Site survey disable\n");
goto unlock;
}
i++;
}
- if (cfg_param_attr->flag & SITE_SURVEY_SCAN_TIME) {
- if (cfg_param_attr->site_survey_scan_time > 0 &&
- cfg_param_attr->site_survey_scan_time < 65536) {
+ if (param->flag & SITE_SURVEY_SCAN_TIME) {
+ u16 scan_time = param->site_survey_scan_time;
+
+ if (scan_time > 0) {
wid_list[i].id = WID_SITE_SURVEY_SCAN_TIME;
- wid_list[i].val = (s8 *)&cfg_param_attr->site_survey_scan_time;
+ wid_list[i].val = (s8 *)&param->site_survey_scan_time;
wid_list[i].type = WID_SHORT;
wid_list[i].size = sizeof(u16);
- hif_drv->cfg_values.site_survey_scan_time = cfg_param_attr->site_survey_scan_time;
+ hif_drv->cfg_values.site_survey_scan_time = scan_time;
} else {
netdev_err(vif->ndev, "Site scan time(1~65535) over\n");
goto unlock;
}
i++;
}
- if (cfg_param_attr->flag & ACTIVE_SCANTIME) {
- if (cfg_param_attr->active_scan_time > 0 &&
- cfg_param_attr->active_scan_time < 65536) {
+ if (param->flag & ACTIVE_SCANTIME) {
+ u16 active_scan_time = param->active_scan_time;
+
+ if (active_scan_time > 0) {
wid_list[i].id = WID_ACTIVE_SCAN_TIME;
- wid_list[i].val = (s8 *)&cfg_param_attr->active_scan_time;
+ wid_list[i].val = (s8 *)&param->active_scan_time;
wid_list[i].type = WID_SHORT;
wid_list[i].size = sizeof(u16);
- hif_drv->cfg_values.active_scan_time = cfg_param_attr->active_scan_time;
+ hif_drv->cfg_values.active_scan_time = active_scan_time;
} else {
netdev_err(vif->ndev, "Active time(1~65535) over\n");
goto unlock;
}
i++;
}
- if (cfg_param_attr->flag & PASSIVE_SCANTIME) {
- if (cfg_param_attr->passive_scan_time > 0 &&
- cfg_param_attr->passive_scan_time < 65536) {
+ if (param->flag & PASSIVE_SCANTIME) {
+ u16 time = param->passive_scan_time;
+
+ if (time > 0) {
wid_list[i].id = WID_PASSIVE_SCAN_TIME;
- wid_list[i].val = (s8 *)&cfg_param_attr->passive_scan_time;
+ wid_list[i].val = (s8 *)&param->passive_scan_time;
wid_list[i].type = WID_SHORT;
wid_list[i].size = sizeof(u16);
- hif_drv->cfg_values.passive_scan_time = cfg_param_attr->passive_scan_time;
+ hif_drv->cfg_values.passive_scan_time = time;
} else {
netdev_err(vif->ndev, "Passive time(1~65535) over\n");
goto unlock;
}
i++;
}
- if (cfg_param_attr->flag & CURRENT_TX_RATE) {
- enum CURRENT_TXRATE curr_tx_rate = cfg_param_attr->curr_tx_rate;
+ if (param->flag & CURRENT_TX_RATE) {
+ enum CURRENT_TXRATE curr_tx_rate = param->curr_tx_rate;
if (curr_tx_rate == AUTORATE || curr_tx_rate == MBPS_1 ||
curr_tx_rate == MBPS_2 || curr_tx_rate == MBPS_5_5 ||
@@ -754,8 +762,9 @@ static s32 handle_scan(struct wilc_vif *vif, struct scan_attr *scan_info)
u32 i;
u8 *buffer;
u8 valuesize = 0;
- u8 *pu8HdnNtwrksWidVal = NULL;
+ u8 *hdn_ntwk_wid_val = NULL;
struct host_if_drv *hif_drv = vif->hif_drv;
+ struct hidden_network *hidden_net = &scan_info->hidden_network;
hif_drv->usr_scan_req.scan_result = scan_info->result;
hif_drv->usr_scan_req.arg = scan_info->arg;
@@ -764,13 +773,13 @@ static s32 handle_scan(struct wilc_vif *vif, struct scan_attr *scan_info)
hif_drv->hif_state < HOST_IF_CONNECTED) {
netdev_err(vif->ndev, "Already scan\n");
result = -EBUSY;
- goto ERRORHANDLER;
+ goto error;
}
if (wilc_optaining_ip || wilc_connecting) {
netdev_err(vif->ndev, "Don't do obss scan\n");
result = -EBUSY;
- goto ERRORHANDLER;
+ goto error;
}
hif_drv->usr_scan_req.rcvd_ch_cnt = 0;
@@ -778,19 +787,20 @@ static s32 handle_scan(struct wilc_vif *vif, struct scan_attr *scan_info)
wid_list[index].id = (u16)WID_SSID_PROBE_REQ;
wid_list[index].type = WID_STR;
- for (i = 0; i < scan_info->hidden_network.n_ssids; i++)
- valuesize += ((scan_info->hidden_network.net_info[i].ssid_len) + 1);
- pu8HdnNtwrksWidVal = kmalloc(valuesize + 1, GFP_KERNEL);
- wid_list[index].val = pu8HdnNtwrksWidVal;
+ for (i = 0; i < hidden_net->n_ssids; i++)
+ valuesize += ((hidden_net->net_info[i].ssid_len) + 1);
+ hdn_ntwk_wid_val = kmalloc(valuesize + 1, GFP_KERNEL);
+ wid_list[index].val = hdn_ntwk_wid_val;
if (wid_list[index].val) {
buffer = wid_list[index].val;
- *buffer++ = scan_info->hidden_network.n_ssids;
+ *buffer++ = hidden_net->n_ssids;
- for (i = 0; i < scan_info->hidden_network.n_ssids; i++) {
- *buffer++ = scan_info->hidden_network.net_info[i].ssid_len;
- memcpy(buffer, scan_info->hidden_network.net_info[i].ssid, scan_info->hidden_network.net_info[i].ssid_len);
- buffer += scan_info->hidden_network.net_info[i].ssid_len;
+ for (i = 0; i < hidden_net->n_ssids; i++) {
+ *buffer++ = hidden_net->net_info[i].ssid_len;
+ memcpy(buffer, hidden_net->net_info[i].ssid,
+ hidden_net->net_info[i].ssid_len);
+ buffer += hidden_net->net_info[i].ssid_len;
}
wid_list[index].size = (s32)(valuesize + 1);
@@ -818,7 +828,7 @@ static s32 handle_scan(struct wilc_vif *vif, struct scan_attr *scan_info)
for (i = 0; i < scan_info->ch_list_len; i++) {
if (scan_info->ch_freq_list[i] > 0)
- scan_info->ch_freq_list[i] = scan_info->ch_freq_list[i] - 1;
+ scan_info->ch_freq_list[i] -= 1;
}
}
@@ -844,10 +854,10 @@ static s32 handle_scan(struct wilc_vif *vif, struct scan_attr *scan_info)
if (result)
netdev_err(vif->ndev, "Failed to send scan parameters\n");
-ERRORHANDLER:
+error:
if (result) {
del_timer(&hif_drv->scan_timer);
- Handle_ScanDone(vif, SCAN_EVENT_ABORTED);
+ handle_scan_done(vif, SCAN_EVENT_ABORTED);
}
kfree(scan_info->ch_freq_list);
@@ -858,24 +868,24 @@ ERRORHANDLER:
kfree(scan_info->hidden_network.net_info);
scan_info->hidden_network.net_info = NULL;
- kfree(pu8HdnNtwrksWidVal);
+ kfree(hdn_ntwk_wid_val);
return result;
}
-static s32 Handle_ScanDone(struct wilc_vif *vif,
- enum scan_event enuEvent)
+static s32 handle_scan_done(struct wilc_vif *vif, enum scan_event evt)
{
s32 result = 0;
- u8 u8abort_running_scan;
+ u8 abort_running_scan;
struct wid wid;
struct host_if_drv *hif_drv = vif->hif_drv;
+ struct user_scan_req *scan_req;
- if (enuEvent == SCAN_EVENT_ABORTED) {
- u8abort_running_scan = 1;
+ if (evt == SCAN_EVENT_ABORTED) {
+ abort_running_scan = 1;
wid.id = (u16)WID_ABORT_RUNNING_SCAN;
wid.type = WID_CHAR;
- wid.val = (s8 *)&u8abort_running_scan;
+ wid.val = (s8 *)&abort_running_scan;
wid.size = sizeof(char);
result = wilc_send_config_pkt(vif, SET_CFG, &wid, 1,
@@ -892,285 +902,280 @@ static s32 Handle_ScanDone(struct wilc_vif *vif,
return result;
}
- if (hif_drv->usr_scan_req.scan_result) {
- hif_drv->usr_scan_req.scan_result(enuEvent, NULL,
- hif_drv->usr_scan_req.arg, NULL);
- hif_drv->usr_scan_req.scan_result = NULL;
+ scan_req = &hif_drv->usr_scan_req;
+ if (scan_req->scan_result) {
+ scan_req->scan_result(evt, NULL, scan_req->arg, NULL);
+ scan_req->scan_result = NULL;
}
return result;
}
u8 wilc_connected_ssid[6] = {0};
-static s32 Handle_Connect(struct wilc_vif *vif,
- struct connect_attr *pstrHostIFconnectAttr)
+static s32 handle_connect(struct wilc_vif *vif,
+ struct connect_attr *conn_attr)
{
s32 result = 0;
- struct wid strWIDList[8];
- u32 u32WidsCount = 0, dummyval = 0;
- u8 *pu8CurrByte = NULL;
- struct join_bss_param *ptstrJoinBssParam;
+ struct wid wid_list[8];
+ u32 wid_cnt = 0, dummyval = 0;
+ u8 *cur_byte = NULL;
+ struct join_bss_param *bss_param;
struct host_if_drv *hif_drv = vif->hif_drv;
- if (memcmp(pstrHostIFconnectAttr->bssid, wilc_connected_ssid, ETH_ALEN) == 0) {
+ if (memcmp(conn_attr->bssid, wilc_connected_ssid, ETH_ALEN) == 0) {
result = 0;
netdev_err(vif->ndev, "Discard connect request\n");
return result;
}
- ptstrJoinBssParam = pstrHostIFconnectAttr->params;
- if (!ptstrJoinBssParam) {
+ bss_param = conn_attr->params;
+ if (!bss_param) {
netdev_err(vif->ndev, "Required BSSID not found\n");
result = -ENOENT;
- goto ERRORHANDLER;
+ goto error;
}
- if (pstrHostIFconnectAttr->bssid) {
- hif_drv->usr_conn_req.bssid = kmalloc(6, GFP_KERNEL);
- memcpy(hif_drv->usr_conn_req.bssid, pstrHostIFconnectAttr->bssid, 6);
+ if (conn_attr->bssid) {
+ hif_drv->usr_conn_req.bssid = kmemdup(conn_attr->bssid, 6,
+ GFP_KERNEL);
+ if (!hif_drv->usr_conn_req.bssid) {
+ result = -ENOMEM;
+ goto error;
+ }
}
- hif_drv->usr_conn_req.ssid_len = pstrHostIFconnectAttr->ssid_len;
- if (pstrHostIFconnectAttr->ssid) {
- hif_drv->usr_conn_req.ssid = kmalloc(pstrHostIFconnectAttr->ssid_len + 1, GFP_KERNEL);
+ hif_drv->usr_conn_req.ssid_len = conn_attr->ssid_len;
+ if (conn_attr->ssid) {
+ hif_drv->usr_conn_req.ssid = kmalloc(conn_attr->ssid_len + 1,
+ GFP_KERNEL);
+ if (!hif_drv->usr_conn_req.ssid) {
+ result = -ENOMEM;
+ goto error;
+ }
memcpy(hif_drv->usr_conn_req.ssid,
- pstrHostIFconnectAttr->ssid,
- pstrHostIFconnectAttr->ssid_len);
- hif_drv->usr_conn_req.ssid[pstrHostIFconnectAttr->ssid_len] = '\0';
- }
-
- hif_drv->usr_conn_req.ies_len = pstrHostIFconnectAttr->ies_len;
- if (pstrHostIFconnectAttr->ies) {
- hif_drv->usr_conn_req.ies = kmalloc(pstrHostIFconnectAttr->ies_len, GFP_KERNEL);
- memcpy(hif_drv->usr_conn_req.ies,
- pstrHostIFconnectAttr->ies,
- pstrHostIFconnectAttr->ies_len);
- }
-
- hif_drv->usr_conn_req.security = pstrHostIFconnectAttr->security;
- hif_drv->usr_conn_req.auth_type = pstrHostIFconnectAttr->auth_type;
- hif_drv->usr_conn_req.conn_result = pstrHostIFconnectAttr->result;
- hif_drv->usr_conn_req.arg = pstrHostIFconnectAttr->arg;
-
- strWIDList[u32WidsCount].id = WID_SUCCESS_FRAME_COUNT;
- strWIDList[u32WidsCount].type = WID_INT;
- strWIDList[u32WidsCount].size = sizeof(u32);
- strWIDList[u32WidsCount].val = (s8 *)(&(dummyval));
- u32WidsCount++;
-
- strWIDList[u32WidsCount].id = WID_RECEIVED_FRAGMENT_COUNT;
- strWIDList[u32WidsCount].type = WID_INT;
- strWIDList[u32WidsCount].size = sizeof(u32);
- strWIDList[u32WidsCount].val = (s8 *)(&(dummyval));
- u32WidsCount++;
-
- strWIDList[u32WidsCount].id = WID_FAILED_COUNT;
- strWIDList[u32WidsCount].type = WID_INT;
- strWIDList[u32WidsCount].size = sizeof(u32);
- strWIDList[u32WidsCount].val = (s8 *)(&(dummyval));
- u32WidsCount++;
+ conn_attr->ssid,
+ conn_attr->ssid_len);
+ hif_drv->usr_conn_req.ssid[conn_attr->ssid_len] = '\0';
+ }
- {
- strWIDList[u32WidsCount].id = WID_INFO_ELEMENT_ASSOCIATE;
- strWIDList[u32WidsCount].type = WID_BIN_DATA;
- strWIDList[u32WidsCount].val = hif_drv->usr_conn_req.ies;
- strWIDList[u32WidsCount].size = hif_drv->usr_conn_req.ies_len;
- u32WidsCount++;
-
- if (memcmp("DIRECT-", pstrHostIFconnectAttr->ssid, 7)) {
- info_element_size = hif_drv->usr_conn_req.ies_len;
- info_element = kmalloc(info_element_size, GFP_KERNEL);
- memcpy(info_element, hif_drv->usr_conn_req.ies,
- info_element_size);
+ hif_drv->usr_conn_req.ies_len = conn_attr->ies_len;
+ if (conn_attr->ies) {
+ hif_drv->usr_conn_req.ies = kmemdup(conn_attr->ies,
+ conn_attr->ies_len,
+ GFP_KERNEL);
+ if (!hif_drv->usr_conn_req.ies) {
+ result = -ENOMEM;
+ goto error;
}
}
- strWIDList[u32WidsCount].id = (u16)WID_11I_MODE;
- strWIDList[u32WidsCount].type = WID_CHAR;
- strWIDList[u32WidsCount].size = sizeof(char);
- strWIDList[u32WidsCount].val = (s8 *)&hif_drv->usr_conn_req.security;
- u32WidsCount++;
-
- if (memcmp("DIRECT-", pstrHostIFconnectAttr->ssid, 7))
- mode_11i = hif_drv->usr_conn_req.security;
- strWIDList[u32WidsCount].id = (u16)WID_AUTH_TYPE;
- strWIDList[u32WidsCount].type = WID_CHAR;
- strWIDList[u32WidsCount].size = sizeof(char);
- strWIDList[u32WidsCount].val = (s8 *)&hif_drv->usr_conn_req.auth_type;
- u32WidsCount++;
-
- if (memcmp("DIRECT-", pstrHostIFconnectAttr->ssid, 7))
- auth_type = (u8)hif_drv->usr_conn_req.auth_type;
-
- strWIDList[u32WidsCount].id = (u16)WID_JOIN_REQ_EXTENDED;
- strWIDList[u32WidsCount].type = WID_STR;
- strWIDList[u32WidsCount].size = 112;
- strWIDList[u32WidsCount].val = kmalloc(strWIDList[u32WidsCount].size, GFP_KERNEL);
-
- if (memcmp("DIRECT-", pstrHostIFconnectAttr->ssid, 7)) {
- join_req_size = strWIDList[u32WidsCount].size;
- join_req = kmalloc(join_req_size, GFP_KERNEL);
- }
- if (!strWIDList[u32WidsCount].val) {
+ hif_drv->usr_conn_req.security = conn_attr->security;
+ hif_drv->usr_conn_req.auth_type = conn_attr->auth_type;
+ hif_drv->usr_conn_req.conn_result = conn_attr->result;
+ hif_drv->usr_conn_req.arg = conn_attr->arg;
+
+ wid_list[wid_cnt].id = WID_SUCCESS_FRAME_COUNT;
+ wid_list[wid_cnt].type = WID_INT;
+ wid_list[wid_cnt].size = sizeof(u32);
+ wid_list[wid_cnt].val = (s8 *)(&(dummyval));
+ wid_cnt++;
+
+ wid_list[wid_cnt].id = WID_RECEIVED_FRAGMENT_COUNT;
+ wid_list[wid_cnt].type = WID_INT;
+ wid_list[wid_cnt].size = sizeof(u32);
+ wid_list[wid_cnt].val = (s8 *)(&(dummyval));
+ wid_cnt++;
+
+ wid_list[wid_cnt].id = WID_FAILED_COUNT;
+ wid_list[wid_cnt].type = WID_INT;
+ wid_list[wid_cnt].size = sizeof(u32);
+ wid_list[wid_cnt].val = (s8 *)(&(dummyval));
+ wid_cnt++;
+
+ wid_list[wid_cnt].id = WID_INFO_ELEMENT_ASSOCIATE;
+ wid_list[wid_cnt].type = WID_BIN_DATA;
+ wid_list[wid_cnt].val = hif_drv->usr_conn_req.ies;
+ wid_list[wid_cnt].size = hif_drv->usr_conn_req.ies_len;
+ wid_cnt++;
+
+ wid_list[wid_cnt].id = (u16)WID_11I_MODE;
+ wid_list[wid_cnt].type = WID_CHAR;
+ wid_list[wid_cnt].size = sizeof(char);
+ wid_list[wid_cnt].val = (s8 *)&hif_drv->usr_conn_req.security;
+ wid_cnt++;
+
+ wid_list[wid_cnt].id = (u16)WID_AUTH_TYPE;
+ wid_list[wid_cnt].type = WID_CHAR;
+ wid_list[wid_cnt].size = sizeof(char);
+ wid_list[wid_cnt].val = (s8 *)&hif_drv->usr_conn_req.auth_type;
+ wid_cnt++;
+
+ wid_list[wid_cnt].id = (u16)WID_JOIN_REQ_EXTENDED;
+ wid_list[wid_cnt].type = WID_STR;
+ wid_list[wid_cnt].size = 112;
+ wid_list[wid_cnt].val = kmalloc(wid_list[wid_cnt].size, GFP_KERNEL);
+
+ if (!wid_list[wid_cnt].val) {
result = -EFAULT;
- goto ERRORHANDLER;
+ goto error;
}
- pu8CurrByte = strWIDList[u32WidsCount].val;
+ cur_byte = wid_list[wid_cnt].val;
- if (pstrHostIFconnectAttr->ssid) {
- memcpy(pu8CurrByte, pstrHostIFconnectAttr->ssid, pstrHostIFconnectAttr->ssid_len);
- pu8CurrByte[pstrHostIFconnectAttr->ssid_len] = '\0';
+ if (conn_attr->ssid) {
+ memcpy(cur_byte, conn_attr->ssid, conn_attr->ssid_len);
+ cur_byte[conn_attr->ssid_len] = '\0';
}
- pu8CurrByte += MAX_SSID_LEN;
- *(pu8CurrByte++) = INFRASTRUCTURE;
+ cur_byte += MAX_SSID_LEN;
+ *(cur_byte++) = INFRASTRUCTURE;
- if (pstrHostIFconnectAttr->ch >= 1 && pstrHostIFconnectAttr->ch <= 14) {
- *(pu8CurrByte++) = pstrHostIFconnectAttr->ch;
+ if (conn_attr->ch >= 1 && conn_attr->ch <= 14) {
+ *(cur_byte++) = conn_attr->ch;
} else {
netdev_err(vif->ndev, "Channel out of range\n");
- *(pu8CurrByte++) = 0xFF;
+ *(cur_byte++) = 0xFF;
}
- *(pu8CurrByte++) = (ptstrJoinBssParam->cap_info) & 0xFF;
- *(pu8CurrByte++) = ((ptstrJoinBssParam->cap_info) >> 8) & 0xFF;
+ *(cur_byte++) = (bss_param->cap_info) & 0xFF;
+ *(cur_byte++) = ((bss_param->cap_info) >> 8) & 0xFF;
- if (pstrHostIFconnectAttr->bssid)
- memcpy(pu8CurrByte, pstrHostIFconnectAttr->bssid, 6);
- pu8CurrByte += 6;
+ if (conn_attr->bssid)
+ memcpy(cur_byte, conn_attr->bssid, 6);
+ cur_byte += 6;
- if (pstrHostIFconnectAttr->bssid)
- memcpy(pu8CurrByte, pstrHostIFconnectAttr->bssid, 6);
- pu8CurrByte += 6;
+ if (conn_attr->bssid)
+ memcpy(cur_byte, conn_attr->bssid, 6);
+ cur_byte += 6;
- *(pu8CurrByte++) = (ptstrJoinBssParam->beacon_period) & 0xFF;
- *(pu8CurrByte++) = ((ptstrJoinBssParam->beacon_period) >> 8) & 0xFF;
- *(pu8CurrByte++) = ptstrJoinBssParam->dtim_period;
+ *(cur_byte++) = (bss_param->beacon_period) & 0xFF;
+ *(cur_byte++) = ((bss_param->beacon_period) >> 8) & 0xFF;
+ *(cur_byte++) = bss_param->dtim_period;
- memcpy(pu8CurrByte, ptstrJoinBssParam->supp_rates, MAX_RATES_SUPPORTED + 1);
- pu8CurrByte += (MAX_RATES_SUPPORTED + 1);
+ memcpy(cur_byte, bss_param->supp_rates, MAX_RATES_SUPPORTED + 1);
+ cur_byte += (MAX_RATES_SUPPORTED + 1);
- *(pu8CurrByte++) = ptstrJoinBssParam->wmm_cap;
- *(pu8CurrByte++) = ptstrJoinBssParam->uapsd_cap;
+ *(cur_byte++) = bss_param->wmm_cap;
+ *(cur_byte++) = bss_param->uapsd_cap;
- *(pu8CurrByte++) = ptstrJoinBssParam->ht_capable;
- hif_drv->usr_conn_req.ht_capable = ptstrJoinBssParam->ht_capable;
+ *(cur_byte++) = bss_param->ht_capable;
+ hif_drv->usr_conn_req.ht_capable = bss_param->ht_capable;
- *(pu8CurrByte++) = ptstrJoinBssParam->rsn_found;
- *(pu8CurrByte++) = ptstrJoinBssParam->rsn_grp_policy;
- *(pu8CurrByte++) = ptstrJoinBssParam->mode_802_11i;
+ *(cur_byte++) = bss_param->rsn_found;
+ *(cur_byte++) = bss_param->rsn_grp_policy;
+ *(cur_byte++) = bss_param->mode_802_11i;
- memcpy(pu8CurrByte, ptstrJoinBssParam->rsn_pcip_policy, sizeof(ptstrJoinBssParam->rsn_pcip_policy));
- pu8CurrByte += sizeof(ptstrJoinBssParam->rsn_pcip_policy);
+ memcpy(cur_byte, bss_param->rsn_pcip_policy,
+ sizeof(bss_param->rsn_pcip_policy));
+ cur_byte += sizeof(bss_param->rsn_pcip_policy);
- memcpy(pu8CurrByte, ptstrJoinBssParam->rsn_auth_policy, sizeof(ptstrJoinBssParam->rsn_auth_policy));
- pu8CurrByte += sizeof(ptstrJoinBssParam->rsn_auth_policy);
+ memcpy(cur_byte, bss_param->rsn_auth_policy,
+ sizeof(bss_param->rsn_auth_policy));
+ cur_byte += sizeof(bss_param->rsn_auth_policy);
- memcpy(pu8CurrByte, ptstrJoinBssParam->rsn_cap, sizeof(ptstrJoinBssParam->rsn_cap));
- pu8CurrByte += sizeof(ptstrJoinBssParam->rsn_cap);
+ memcpy(cur_byte, bss_param->rsn_cap, sizeof(bss_param->rsn_cap));
+ cur_byte += sizeof(bss_param->rsn_cap);
- *(pu8CurrByte++) = REAL_JOIN_REQ;
- *(pu8CurrByte++) = ptstrJoinBssParam->noa_enabled;
+ *(cur_byte++) = REAL_JOIN_REQ;
+ *(cur_byte++) = bss_param->noa_enabled;
- if (ptstrJoinBssParam->noa_enabled) {
- *(pu8CurrByte++) = (ptstrJoinBssParam->tsf) & 0xFF;
- *(pu8CurrByte++) = ((ptstrJoinBssParam->tsf) >> 8) & 0xFF;
- *(pu8CurrByte++) = ((ptstrJoinBssParam->tsf) >> 16) & 0xFF;
- *(pu8CurrByte++) = ((ptstrJoinBssParam->tsf) >> 24) & 0xFF;
+ if (bss_param->noa_enabled) {
+ *(cur_byte++) = (bss_param->tsf) & 0xFF;
+ *(cur_byte++) = ((bss_param->tsf) >> 8) & 0xFF;
+ *(cur_byte++) = ((bss_param->tsf) >> 16) & 0xFF;
+ *(cur_byte++) = ((bss_param->tsf) >> 24) & 0xFF;
- *(pu8CurrByte++) = ptstrJoinBssParam->opp_enabled;
- *(pu8CurrByte++) = ptstrJoinBssParam->idx;
+ *(cur_byte++) = bss_param->opp_enabled;
+ *(cur_byte++) = bss_param->idx;
- if (ptstrJoinBssParam->opp_enabled)
- *(pu8CurrByte++) = ptstrJoinBssParam->ct_window;
+ if (bss_param->opp_enabled)
+ *(cur_byte++) = bss_param->ct_window;
- *(pu8CurrByte++) = ptstrJoinBssParam->cnt;
+ *(cur_byte++) = bss_param->cnt;
- memcpy(pu8CurrByte, ptstrJoinBssParam->duration, sizeof(ptstrJoinBssParam->duration));
- pu8CurrByte += sizeof(ptstrJoinBssParam->duration);
+ memcpy(cur_byte, bss_param->duration,
+ sizeof(bss_param->duration));
+ cur_byte += sizeof(bss_param->duration);
- memcpy(pu8CurrByte, ptstrJoinBssParam->interval, sizeof(ptstrJoinBssParam->interval));
- pu8CurrByte += sizeof(ptstrJoinBssParam->interval);
+ memcpy(cur_byte, bss_param->interval,
+ sizeof(bss_param->interval));
+ cur_byte += sizeof(bss_param->interval);
- memcpy(pu8CurrByte, ptstrJoinBssParam->start_time, sizeof(ptstrJoinBssParam->start_time));
- pu8CurrByte += sizeof(ptstrJoinBssParam->start_time);
+ memcpy(cur_byte, bss_param->start_time,
+ sizeof(bss_param->start_time));
+ cur_byte += sizeof(bss_param->start_time);
}
- pu8CurrByte = strWIDList[u32WidsCount].val;
- u32WidsCount++;
+ cur_byte = wid_list[wid_cnt].val;
+ wid_cnt++;
- if (memcmp("DIRECT-", pstrHostIFconnectAttr->ssid, 7)) {
- memcpy(join_req, pu8CurrByte, join_req_size);
- join_req_vif = vif;
- }
-
- if (pstrHostIFconnectAttr->bssid)
+ if (conn_attr->bssid)
memcpy(wilc_connected_ssid,
- pstrHostIFconnectAttr->bssid, ETH_ALEN);
+ conn_attr->bssid, ETH_ALEN);
- result = wilc_send_config_pkt(vif, SET_CFG, strWIDList,
- u32WidsCount,
+ result = wilc_send_config_pkt(vif, SET_CFG, wid_list,
+ wid_cnt,
wilc_get_vif_idx(vif));
if (result) {
netdev_err(vif->ndev, "failed to send config packet\n");
result = -EFAULT;
- goto ERRORHANDLER;
+ goto error;
} else {
hif_drv->hif_state = HOST_IF_WAITING_CONN_RESP;
}
-ERRORHANDLER:
+error:
if (result) {
- struct connect_info strConnectInfo;
+ struct connect_info conn_info;
del_timer(&hif_drv->connect_timer);
- memset(&strConnectInfo, 0, sizeof(struct connect_info));
+ memset(&conn_info, 0, sizeof(struct connect_info));
- if (pstrHostIFconnectAttr->result) {
- if (pstrHostIFconnectAttr->bssid)
- memcpy(strConnectInfo.bssid, pstrHostIFconnectAttr->bssid, 6);
+ if (conn_attr->result) {
+ if (conn_attr->bssid)
+ memcpy(conn_info.bssid, conn_attr->bssid, 6);
- if (pstrHostIFconnectAttr->ies) {
- strConnectInfo.req_ies_len = pstrHostIFconnectAttr->ies_len;
- strConnectInfo.req_ies = kmalloc(pstrHostIFconnectAttr->ies_len, GFP_KERNEL);
- memcpy(strConnectInfo.req_ies,
- pstrHostIFconnectAttr->ies,
- pstrHostIFconnectAttr->ies_len);
+ if (conn_attr->ies) {
+ conn_info.req_ies_len = conn_attr->ies_len;
+ conn_info.req_ies = kmalloc(conn_attr->ies_len,
+ GFP_KERNEL);
+ memcpy(conn_info.req_ies,
+ conn_attr->ies,
+ conn_attr->ies_len);
}
- pstrHostIFconnectAttr->result(CONN_DISCONN_EVENT_CONN_RESP,
- &strConnectInfo,
+ conn_attr->result(CONN_DISCONN_EVENT_CONN_RESP,
+ &conn_info,
MAC_DISCONNECTED,
NULL,
- pstrHostIFconnectAttr->arg);
+ conn_attr->arg);
hif_drv->hif_state = HOST_IF_IDLE;
- kfree(strConnectInfo.req_ies);
- strConnectInfo.req_ies = NULL;
+ kfree(conn_info.req_ies);
+ conn_info.req_ies = NULL;
} else {
netdev_err(vif->ndev, "Connect callback is NULL\n");
}
}
- kfree(pstrHostIFconnectAttr->bssid);
- pstrHostIFconnectAttr->bssid = NULL;
+ kfree(conn_attr->bssid);
+ conn_attr->bssid = NULL;
- kfree(pstrHostIFconnectAttr->ssid);
- pstrHostIFconnectAttr->ssid = NULL;
+ kfree(conn_attr->ssid);
+ conn_attr->ssid = NULL;
- kfree(pstrHostIFconnectAttr->ies);
- pstrHostIFconnectAttr->ies = NULL;
+ kfree(conn_attr->ies);
+ conn_attr->ies = NULL;
- kfree(pu8CurrByte);
+ kfree(cur_byte);
return result;
}
-static s32 Handle_ConnectTimeout(struct wilc_vif *vif)
+static s32 handle_connect_timeout(struct wilc_vif *vif)
{
s32 result = 0;
- struct connect_info strConnectInfo;
+ struct connect_info info;
struct wid wid;
- u16 u16DummyReasonCode = 0;
+ u16 dummy_reason_code = 0;
struct host_if_drv *hif_drv = vif->hif_drv;
if (!hif_drv) {
@@ -1182,37 +1187,37 @@ static s32 Handle_ConnectTimeout(struct wilc_vif *vif)
scan_while_connected = false;
- memset(&strConnectInfo, 0, sizeof(struct connect_info));
+ memset(&info, 0, sizeof(struct connect_info));
if (hif_drv->usr_conn_req.conn_result) {
if (hif_drv->usr_conn_req.bssid) {
- memcpy(strConnectInfo.bssid,
+ memcpy(info.bssid,
hif_drv->usr_conn_req.bssid, 6);
}
if (hif_drv->usr_conn_req.ies) {
- strConnectInfo.req_ies_len = hif_drv->usr_conn_req.ies_len;
- strConnectInfo.req_ies = kmalloc(hif_drv->usr_conn_req.ies_len, GFP_KERNEL);
- memcpy(strConnectInfo.req_ies,
+ info.req_ies_len = hif_drv->usr_conn_req.ies_len;
+ info.req_ies = kmalloc(hif_drv->usr_conn_req.ies_len, GFP_KERNEL);
+ memcpy(info.req_ies,
hif_drv->usr_conn_req.ies,
hif_drv->usr_conn_req.ies_len);
}
hif_drv->usr_conn_req.conn_result(CONN_DISCONN_EVENT_CONN_RESP,
- &strConnectInfo,
+ &info,
MAC_DISCONNECTED,
NULL,
hif_drv->usr_conn_req.arg);
- kfree(strConnectInfo.req_ies);
- strConnectInfo.req_ies = NULL;
+ kfree(info.req_ies);
+ info.req_ies = NULL;
} else {
netdev_err(vif->ndev, "Connect callback is NULL\n");
}
wid.id = (u16)WID_DISCONNECT;
wid.type = WID_CHAR;
- wid.val = (s8 *)&u16DummyReasonCode;
+ wid.val = (s8 *)&dummy_reason_code;
wid.size = sizeof(char);
result = wilc_send_config_pkt(vif, SET_CFG, &wid, 1,
@@ -1231,382 +1236,380 @@ static s32 Handle_ConnectTimeout(struct wilc_vif *vif)
eth_zero_addr(wilc_connected_ssid);
- if (join_req && join_req_vif == vif) {
- kfree(join_req);
- join_req = NULL;
- }
-
- if (info_element && join_req_vif == vif) {
- kfree(info_element);
- info_element = NULL;
- }
-
return result;
}
-static s32 Handle_RcvdNtwrkInfo(struct wilc_vif *vif,
- struct rcvd_net_info *pstrRcvdNetworkInfo)
+static s32 handle_rcvd_ntwrk_info(struct wilc_vif *vif,
+ struct rcvd_net_info *rcvd_info)
{
u32 i;
- bool bNewNtwrkFound;
+ bool found;
s32 result = 0;
- struct network_info *pstrNetworkInfo = NULL;
- void *pJoinParams = NULL;
+ struct network_info *info = NULL;
+ void *params = NULL;
struct host_if_drv *hif_drv = vif->hif_drv;
+ struct user_scan_req *scan_req = &hif_drv->usr_scan_req;
- bNewNtwrkFound = true;
+ found = true;
- if (hif_drv->usr_scan_req.scan_result) {
- wilc_parse_network_info(pstrRcvdNetworkInfo->buffer, &pstrNetworkInfo);
- if (!pstrNetworkInfo ||
- !hif_drv->usr_scan_req.scan_result) {
- netdev_err(vif->ndev, "driver is null\n");
- result = -EINVAL;
- goto done;
- }
+ if (!scan_req->scan_result)
+ goto done;
- for (i = 0; i < hif_drv->usr_scan_req.rcvd_ch_cnt; i++) {
- if (memcmp(hif_drv->usr_scan_req.net_info[i].bssid,
- pstrNetworkInfo->bssid, 6) == 0) {
- if (pstrNetworkInfo->rssi <= hif_drv->usr_scan_req.net_info[i].rssi) {
- goto done;
- } else {
- hif_drv->usr_scan_req.net_info[i].rssi = pstrNetworkInfo->rssi;
- bNewNtwrkFound = false;
- break;
- }
+ wilc_parse_network_info(rcvd_info->buffer, &info);
+ if (!info || !scan_req->scan_result) {
+ netdev_err(vif->ndev, "driver is null\n");
+ result = -EINVAL;
+ goto done;
+ }
+
+ for (i = 0; i < scan_req->rcvd_ch_cnt; i++) {
+ if (memcmp(scan_req->net_info[i].bssid, info->bssid, 6) == 0) {
+ if (info->rssi <= scan_req->net_info[i].rssi) {
+ goto done;
+ } else {
+ scan_req->net_info[i].rssi = info->rssi;
+ found = false;
+ break;
}
}
+ }
- if (bNewNtwrkFound) {
- if (hif_drv->usr_scan_req.rcvd_ch_cnt < MAX_NUM_SCANNED_NETWORKS) {
- hif_drv->usr_scan_req.net_info[hif_drv->usr_scan_req.rcvd_ch_cnt].rssi = pstrNetworkInfo->rssi;
+ if (found) {
+ if (scan_req->rcvd_ch_cnt < MAX_NUM_SCANNED_NETWORKS) {
+ scan_req->net_info[scan_req->rcvd_ch_cnt].rssi = info->rssi;
- memcpy(hif_drv->usr_scan_req.net_info[hif_drv->usr_scan_req.rcvd_ch_cnt].bssid,
- pstrNetworkInfo->bssid, 6);
+ memcpy(scan_req->net_info[scan_req->rcvd_ch_cnt].bssid,
+ info->bssid, 6);
- hif_drv->usr_scan_req.rcvd_ch_cnt++;
+ scan_req->rcvd_ch_cnt++;
- pstrNetworkInfo->new_network = true;
- pJoinParams = host_int_ParseJoinBssParam(pstrNetworkInfo);
+ info->new_network = true;
+ params = host_int_parse_join_bss_param(info);
- hif_drv->usr_scan_req.scan_result(SCAN_EVENT_NETWORK_FOUND, pstrNetworkInfo,
- hif_drv->usr_scan_req.arg,
- pJoinParams);
- }
- } else {
- pstrNetworkInfo->new_network = false;
- hif_drv->usr_scan_req.scan_result(SCAN_EVENT_NETWORK_FOUND, pstrNetworkInfo,
- hif_drv->usr_scan_req.arg, NULL);
+ scan_req->scan_result(SCAN_EVENT_NETWORK_FOUND, info,
+ scan_req->arg, params);
}
+ } else {
+ info->new_network = false;
+ scan_req->scan_result(SCAN_EVENT_NETWORK_FOUND, info,
+ scan_req->arg, NULL);
}
done:
- kfree(pstrRcvdNetworkInfo->buffer);
- pstrRcvdNetworkInfo->buffer = NULL;
+ kfree(rcvd_info->buffer);
+ rcvd_info->buffer = NULL;
- if (pstrNetworkInfo) {
- kfree(pstrNetworkInfo->ies);
- kfree(pstrNetworkInfo);
+ if (info) {
+ kfree(info->ies);
+ kfree(info);
}
return result;
}
static s32 host_int_get_assoc_res_info(struct wilc_vif *vif,
- u8 *pu8AssocRespInfo,
- u32 u32MaxAssocRespInfoLen,
- u32 *pu32RcvdAssocRespInfoLen);
+ u8 *assoc_resp_info,
+ u32 max_assoc_resp_info_len,
+ u32 *rcvd_assoc_resp_info_len);
-static s32 Handle_RcvdGnrlAsyncInfo(struct wilc_vif *vif,
- struct rcvd_async_info *pstrRcvdGnrlAsyncInfo)
+static inline void host_int_free_user_conn_req(struct host_if_drv *hif_drv)
{
- s32 result = 0;
- u8 u8MsgType = 0;
- u8 u8MsgID = 0;
- u16 u16MsgLen = 0;
- u16 u16WidID = (u16)WID_NIL;
- u8 u8WidLen = 0;
- u8 u8MacStatus;
- u8 u8MacStatusReasonCode;
- u8 u8MacStatusAdditionalInfo;
- struct connect_info strConnectInfo;
- struct disconnect_info strDisconnectNotifInfo;
- s32 s32Err = 0;
+ hif_drv->usr_conn_req.ssid_len = 0;
+ kfree(hif_drv->usr_conn_req.ssid);
+ hif_drv->usr_conn_req.ssid = NULL;
+ kfree(hif_drv->usr_conn_req.bssid);
+ hif_drv->usr_conn_req.bssid = NULL;
+ hif_drv->usr_conn_req.ies_len = 0;
+ kfree(hif_drv->usr_conn_req.ies);
+ hif_drv->usr_conn_req.ies = NULL;
+}
+
+static inline void host_int_parse_assoc_resp_info(struct wilc_vif *vif,
+ u8 mac_status)
+{
+ struct connect_resp_info *connect_resp_info = NULL;
+ struct connect_info conn_info;
struct host_if_drv *hif_drv = vif->hif_drv;
- if (!hif_drv) {
- netdev_err(vif->ndev, "Driver handler is NULL\n");
- return -ENODEV;
- }
+ memset(&conn_info, 0, sizeof(struct connect_info));
- if (hif_drv->hif_state == HOST_IF_WAITING_CONN_RESP ||
- hif_drv->hif_state == HOST_IF_CONNECTED ||
- hif_drv->usr_scan_req.scan_result) {
- if (!pstrRcvdGnrlAsyncInfo->buffer ||
- !hif_drv->usr_conn_req.conn_result) {
- netdev_err(vif->ndev, "driver is null\n");
- return -EINVAL;
- }
+ if (mac_status == MAC_CONNECTED) {
+ u32 rcvd_assoc_resp_info_len;
- u8MsgType = pstrRcvdGnrlAsyncInfo->buffer[0];
+ memset(rcv_assoc_resp, 0, MAX_ASSOC_RESP_FRAME_SIZE);
- if ('I' != u8MsgType) {
- netdev_err(vif->ndev, "Received Message incorrect.\n");
- return -EFAULT;
- }
+ host_int_get_assoc_res_info(vif, rcv_assoc_resp,
+ MAX_ASSOC_RESP_FRAME_SIZE,
+ &rcvd_assoc_resp_info_len);
- u8MsgID = pstrRcvdGnrlAsyncInfo->buffer[1];
- u16MsgLen = MAKE_WORD16(pstrRcvdGnrlAsyncInfo->buffer[2], pstrRcvdGnrlAsyncInfo->buffer[3]);
- u16WidID = MAKE_WORD16(pstrRcvdGnrlAsyncInfo->buffer[4], pstrRcvdGnrlAsyncInfo->buffer[5]);
- u8WidLen = pstrRcvdGnrlAsyncInfo->buffer[6];
- u8MacStatus = pstrRcvdGnrlAsyncInfo->buffer[7];
- u8MacStatusReasonCode = pstrRcvdGnrlAsyncInfo->buffer[8];
- u8MacStatusAdditionalInfo = pstrRcvdGnrlAsyncInfo->buffer[9];
- if (hif_drv->hif_state == HOST_IF_WAITING_CONN_RESP) {
- u32 u32RcvdAssocRespInfoLen = 0;
- struct connect_resp_info *pstrConnectRespInfo = NULL;
-
- memset(&strConnectInfo, 0, sizeof(struct connect_info));
-
- if (u8MacStatus == MAC_CONNECTED) {
- memset(rcv_assoc_resp, 0, MAX_ASSOC_RESP_FRAME_SIZE);
-
- host_int_get_assoc_res_info(vif,
- rcv_assoc_resp,
- MAX_ASSOC_RESP_FRAME_SIZE,
- &u32RcvdAssocRespInfoLen);
-
- if (u32RcvdAssocRespInfoLen != 0) {
- s32Err = wilc_parse_assoc_resp_info(rcv_assoc_resp, u32RcvdAssocRespInfoLen,
- &pstrConnectRespInfo);
- if (s32Err) {
- netdev_err(vif->ndev, "wilc_parse_assoc_resp_info() returned error %d\n", s32Err);
- } else {
- strConnectInfo.status = pstrConnectRespInfo->status;
-
- if (strConnectInfo.status == SUCCESSFUL_STATUSCODE && pstrConnectRespInfo->ies) {
- strConnectInfo.resp_ies_len = pstrConnectRespInfo->ies_len;
- strConnectInfo.resp_ies = kmalloc(pstrConnectRespInfo->ies_len, GFP_KERNEL);
- memcpy(strConnectInfo.resp_ies, pstrConnectRespInfo->ies,
- pstrConnectRespInfo->ies_len);
- }
-
- if (pstrConnectRespInfo) {
- kfree(pstrConnectRespInfo->ies);
- kfree(pstrConnectRespInfo);
- }
- }
+ if (rcvd_assoc_resp_info_len != 0) {
+ s32 err = 0;
+
+ err = wilc_parse_assoc_resp_info(rcv_assoc_resp, rcvd_assoc_resp_info_len,
+ &connect_resp_info);
+ if (err) {
+ netdev_err(vif->ndev,
+ "wilc_parse_assoc_resp_info() returned error %d\n",
+ err);
+ } else {
+ conn_info.status = connect_resp_info->status;
+
+ if (conn_info.status == SUCCESSFUL_STATUSCODE &&
+ connect_resp_info->ies) {
+ conn_info.resp_ies = kmemdup(connect_resp_info->ies,
+ connect_resp_info->ies_len,
+ GFP_KERNEL);
+ if (conn_info.resp_ies)
+ conn_info.resp_ies_len = connect_resp_info->ies_len;
}
- }
- if (u8MacStatus == MAC_CONNECTED &&
- strConnectInfo.status != SUCCESSFUL_STATUSCODE) {
- netdev_err(vif->ndev, "Received MAC status is MAC_CONNECTED while the received status code in Asoc Resp is not SUCCESSFUL_STATUSCODE\n");
- eth_zero_addr(wilc_connected_ssid);
- } else if (u8MacStatus == MAC_DISCONNECTED) {
- netdev_err(vif->ndev, "Received MAC status is MAC_DISCONNECTED\n");
- eth_zero_addr(wilc_connected_ssid);
+ kfree(connect_resp_info->ies);
+ kfree(connect_resp_info);
}
+ }
+ }
- if (hif_drv->usr_conn_req.bssid) {
- memcpy(strConnectInfo.bssid, hif_drv->usr_conn_req.bssid, 6);
+ if (mac_status == MAC_CONNECTED &&
+ conn_info.status != SUCCESSFUL_STATUSCODE) {
+ netdev_err(vif->ndev,
+ "Received MAC status is MAC_CONNECTED while the received status code in Asoc Resp is not SUCCESSFUL_STATUSCODE\n");
+ eth_zero_addr(wilc_connected_ssid);
+ } else if (mac_status == MAC_DISCONNECTED) {
+ netdev_err(vif->ndev, "Received MAC status is MAC_DISCONNECTED\n");
+ eth_zero_addr(wilc_connected_ssid);
+ }
- if (u8MacStatus == MAC_CONNECTED &&
- strConnectInfo.status == SUCCESSFUL_STATUSCODE) {
- memcpy(hif_drv->assoc_bssid,
- hif_drv->usr_conn_req.bssid, ETH_ALEN);
- }
- }
+ if (hif_drv->usr_conn_req.bssid) {
+ memcpy(conn_info.bssid, hif_drv->usr_conn_req.bssid, 6);
- if (hif_drv->usr_conn_req.ies) {
- strConnectInfo.req_ies_len = hif_drv->usr_conn_req.ies_len;
- strConnectInfo.req_ies = kmalloc(hif_drv->usr_conn_req.ies_len, GFP_KERNEL);
- memcpy(strConnectInfo.req_ies,
- hif_drv->usr_conn_req.ies,
- hif_drv->usr_conn_req.ies_len);
- }
+ if (mac_status == MAC_CONNECTED &&
+ conn_info.status == SUCCESSFUL_STATUSCODE) {
+ memcpy(hif_drv->assoc_bssid,
+ hif_drv->usr_conn_req.bssid, ETH_ALEN);
+ }
+ }
- del_timer(&hif_drv->connect_timer);
- hif_drv->usr_conn_req.conn_result(CONN_DISCONN_EVENT_CONN_RESP,
- &strConnectInfo,
- u8MacStatus,
- NULL,
- hif_drv->usr_conn_req.arg);
+ if (hif_drv->usr_conn_req.ies) {
+ conn_info.req_ies = kmemdup(hif_drv->usr_conn_req.ies,
+ hif_drv->usr_conn_req.ies_len,
+ GFP_KERNEL);
+ if (conn_info.req_ies)
+ conn_info.req_ies_len = hif_drv->usr_conn_req.ies_len;
+ }
- if (u8MacStatus == MAC_CONNECTED &&
- strConnectInfo.status == SUCCESSFUL_STATUSCODE) {
- wilc_set_power_mgmt(vif, 0, 0);
+ del_timer(&hif_drv->connect_timer);
+ hif_drv->usr_conn_req.conn_result(CONN_DISCONN_EVENT_CONN_RESP,
+ &conn_info, mac_status, NULL,
+ hif_drv->usr_conn_req.arg);
- hif_drv->hif_state = HOST_IF_CONNECTED;
+ if (mac_status == MAC_CONNECTED &&
+ conn_info.status == SUCCESSFUL_STATUSCODE) {
+ wilc_set_power_mgmt(vif, 0, 0);
- wilc_optaining_ip = true;
- mod_timer(&wilc_during_ip_timer,
- jiffies + msecs_to_jiffies(10000));
- } else {
- hif_drv->hif_state = HOST_IF_IDLE;
- scan_while_connected = false;
- }
+ hif_drv->hif_state = HOST_IF_CONNECTED;
- kfree(strConnectInfo.resp_ies);
- strConnectInfo.resp_ies = NULL;
-
- kfree(strConnectInfo.req_ies);
- strConnectInfo.req_ies = NULL;
- hif_drv->usr_conn_req.ssid_len = 0;
- kfree(hif_drv->usr_conn_req.ssid);
- hif_drv->usr_conn_req.ssid = NULL;
- kfree(hif_drv->usr_conn_req.bssid);
- hif_drv->usr_conn_req.bssid = NULL;
- hif_drv->usr_conn_req.ies_len = 0;
- kfree(hif_drv->usr_conn_req.ies);
- hif_drv->usr_conn_req.ies = NULL;
- } else if ((u8MacStatus == MAC_DISCONNECTED) &&
- (hif_drv->hif_state == HOST_IF_CONNECTED)) {
- memset(&strDisconnectNotifInfo, 0, sizeof(struct disconnect_info));
+ wilc_optaining_ip = true;
+ mod_timer(&wilc_during_ip_timer,
+ jiffies + msecs_to_jiffies(10000));
+ } else {
+ hif_drv->hif_state = HOST_IF_IDLE;
+ scan_while_connected = false;
+ }
- if (hif_drv->usr_scan_req.scan_result) {
- del_timer(&hif_drv->scan_timer);
- Handle_ScanDone(vif, SCAN_EVENT_ABORTED);
- }
+ kfree(conn_info.resp_ies);
+ conn_info.resp_ies = NULL;
- strDisconnectNotifInfo.reason = 0;
- strDisconnectNotifInfo.ie = NULL;
- strDisconnectNotifInfo.ie_len = 0;
+ kfree(conn_info.req_ies);
+ conn_info.req_ies = NULL;
+ host_int_free_user_conn_req(hif_drv);
+}
- if (hif_drv->usr_conn_req.conn_result) {
- wilc_optaining_ip = false;
- wilc_set_power_mgmt(vif, 0, 0);
+static inline void host_int_handle_disconnect(struct wilc_vif *vif)
+{
+ struct disconnect_info disconn_info;
+ struct host_if_drv *hif_drv = vif->hif_drv;
- hif_drv->usr_conn_req.conn_result(CONN_DISCONN_EVENT_DISCONN_NOTIF,
- NULL,
- 0,
- &strDisconnectNotifInfo,
- hif_drv->usr_conn_req.arg);
- } else {
- netdev_err(vif->ndev, "Connect result NULL\n");
- }
+ memset(&disconn_info, 0, sizeof(struct disconnect_info));
- eth_zero_addr(hif_drv->assoc_bssid);
+ if (hif_drv->usr_scan_req.scan_result) {
+ del_timer(&hif_drv->scan_timer);
+ handle_scan_done(vif, SCAN_EVENT_ABORTED);
+ }
- hif_drv->usr_conn_req.ssid_len = 0;
- kfree(hif_drv->usr_conn_req.ssid);
- hif_drv->usr_conn_req.ssid = NULL;
- kfree(hif_drv->usr_conn_req.bssid);
- hif_drv->usr_conn_req.bssid = NULL;
- hif_drv->usr_conn_req.ies_len = 0;
- kfree(hif_drv->usr_conn_req.ies);
- hif_drv->usr_conn_req.ies = NULL;
+ disconn_info.reason = 0;
+ disconn_info.ie = NULL;
+ disconn_info.ie_len = 0;
- if (join_req && join_req_vif == vif) {
- kfree(join_req);
- join_req = NULL;
- }
+ if (hif_drv->usr_conn_req.conn_result) {
+ wilc_optaining_ip = false;
+ wilc_set_power_mgmt(vif, 0, 0);
- if (info_element && join_req_vif == vif) {
- kfree(info_element);
- info_element = NULL;
- }
+ hif_drv->usr_conn_req.conn_result(CONN_DISCONN_EVENT_DISCONN_NOTIF,
+ NULL, 0, &disconn_info,
+ hif_drv->usr_conn_req.arg);
+ } else {
+ netdev_err(vif->ndev, "Connect result NULL\n");
+ }
- hif_drv->hif_state = HOST_IF_IDLE;
- scan_while_connected = false;
+ eth_zero_addr(hif_drv->assoc_bssid);
+
+ host_int_free_user_conn_req(hif_drv);
+ hif_drv->hif_state = HOST_IF_IDLE;
+ scan_while_connected = false;
+}
+
+static s32 handle_rcvd_gnrl_async_info(struct wilc_vif *vif,
+ struct rcvd_async_info *rcvd_info)
+{
+ s32 result = 0;
+ u8 msg_type = 0;
+ u8 msg_id = 0;
+ u16 msg_len = 0;
+ u16 wid_id = (u16)WID_NIL;
+ u8 wid_len = 0;
+ u8 mac_status;
+ u8 mac_status_reason_code;
+ u8 mac_status_additional_info;
+ struct host_if_drv *hif_drv = vif->hif_drv;
+
+ if (!rcvd_info->buffer) {
+ netdev_err(vif->ndev, "Received buffer is NULL\n");
+ return -EINVAL;
+ }
+
+ if (!hif_drv) {
+ netdev_err(vif->ndev, "Driver handler is NULL\n");
+ kfree(rcvd_info->buffer);
+ rcvd_info->buffer = NULL;
+ return -ENODEV;
+ }
+
+ if (hif_drv->hif_state == HOST_IF_WAITING_CONN_RESP ||
+ hif_drv->hif_state == HOST_IF_CONNECTED ||
+ hif_drv->usr_scan_req.scan_result) {
+ if (!hif_drv->usr_conn_req.conn_result) {
+ netdev_err(vif->ndev, "driver is null\n");
+ kfree(rcvd_info->buffer);
+ rcvd_info->buffer = NULL;
+ return -EINVAL;
+ }
+
+ msg_type = rcvd_info->buffer[0];
+
+ if ('I' != msg_type) {
+ netdev_err(vif->ndev, "Received Message incorrect.\n");
+ kfree(rcvd_info->buffer);
+ rcvd_info->buffer = NULL;
+ return -EFAULT;
+ }
- } else if ((u8MacStatus == MAC_DISCONNECTED) &&
+ msg_id = rcvd_info->buffer[1];
+ msg_len = MAKE_WORD16(rcvd_info->buffer[2], rcvd_info->buffer[3]);
+ wid_id = MAKE_WORD16(rcvd_info->buffer[4], rcvd_info->buffer[5]);
+ wid_len = rcvd_info->buffer[6];
+ mac_status = rcvd_info->buffer[7];
+ mac_status_reason_code = rcvd_info->buffer[8];
+ mac_status_additional_info = rcvd_info->buffer[9];
+ if (hif_drv->hif_state == HOST_IF_WAITING_CONN_RESP) {
+ host_int_parse_assoc_resp_info(vif, mac_status);
+ } else if ((mac_status == MAC_DISCONNECTED) &&
+ (hif_drv->hif_state == HOST_IF_CONNECTED)) {
+ host_int_handle_disconnect(vif);
+ } else if ((mac_status == MAC_DISCONNECTED) &&
(hif_drv->usr_scan_req.scan_result)) {
del_timer(&hif_drv->scan_timer);
if (hif_drv->usr_scan_req.scan_result)
- Handle_ScanDone(vif, SCAN_EVENT_ABORTED);
+ handle_scan_done(vif, SCAN_EVENT_ABORTED);
}
}
- kfree(pstrRcvdGnrlAsyncInfo->buffer);
- pstrRcvdGnrlAsyncInfo->buffer = NULL;
+ kfree(rcvd_info->buffer);
+ rcvd_info->buffer = NULL;
return result;
}
-static int Handle_Key(struct wilc_vif *vif,
- struct key_attr *pstrHostIFkeyAttr)
+static int handle_key(struct wilc_vif *vif, struct key_attr *hif_key)
{
s32 result = 0;
struct wid wid;
- struct wid strWIDList[5];
+ struct wid wid_list[5];
u8 i;
- u8 *pu8keybuf;
+ u8 *key_buf;
s8 s8idxarray[1];
s8 ret = 0;
struct host_if_drv *hif_drv = vif->hif_drv;
- switch (pstrHostIFkeyAttr->type) {
+ switch (hif_key->type) {
case WEP:
- if (pstrHostIFkeyAttr->action & ADDKEY_AP) {
- strWIDList[0].id = (u16)WID_11I_MODE;
- strWIDList[0].type = WID_CHAR;
- strWIDList[0].size = sizeof(char);
- strWIDList[0].val = (s8 *)&pstrHostIFkeyAttr->attr.wep.mode;
+ if (hif_key->action & ADDKEY_AP) {
+ wid_list[0].id = (u16)WID_11I_MODE;
+ wid_list[0].type = WID_CHAR;
+ wid_list[0].size = sizeof(char);
+ wid_list[0].val = (s8 *)&hif_key->attr.wep.mode;
- strWIDList[1].id = WID_AUTH_TYPE;
- strWIDList[1].type = WID_CHAR;
- strWIDList[1].size = sizeof(char);
- strWIDList[1].val = (s8 *)&pstrHostIFkeyAttr->attr.wep.auth_type;
+ wid_list[1].id = WID_AUTH_TYPE;
+ wid_list[1].type = WID_CHAR;
+ wid_list[1].size = sizeof(char);
+ wid_list[1].val = (s8 *)&hif_key->attr.wep.auth_type;
- pu8keybuf = kmalloc(pstrHostIFkeyAttr->attr.wep.key_len + 2,
- GFP_KERNEL);
- if (!pu8keybuf)
+ key_buf = kmalloc(hif_key->attr.wep.key_len + 2,
+ GFP_KERNEL);
+ if (!key_buf)
return -ENOMEM;
- pu8keybuf[0] = pstrHostIFkeyAttr->attr.wep.index;
- pu8keybuf[1] = pstrHostIFkeyAttr->attr.wep.key_len;
+ key_buf[0] = hif_key->attr.wep.index;
+ key_buf[1] = hif_key->attr.wep.key_len;
- memcpy(&pu8keybuf[2], pstrHostIFkeyAttr->attr.wep.key,
- pstrHostIFkeyAttr->attr.wep.key_len);
+ memcpy(&key_buf[2], hif_key->attr.wep.key,
+ hif_key->attr.wep.key_len);
- kfree(pstrHostIFkeyAttr->attr.wep.key);
+ kfree(hif_key->attr.wep.key);
- strWIDList[2].id = (u16)WID_WEP_KEY_VALUE;
- strWIDList[2].type = WID_STR;
- strWIDList[2].size = pstrHostIFkeyAttr->attr.wep.key_len + 2;
- strWIDList[2].val = (s8 *)pu8keybuf;
+ wid_list[2].id = (u16)WID_WEP_KEY_VALUE;
+ wid_list[2].type = WID_STR;
+ wid_list[2].size = hif_key->attr.wep.key_len + 2;
+ wid_list[2].val = (s8 *)key_buf;
result = wilc_send_config_pkt(vif, SET_CFG,
- strWIDList, 3,
+ wid_list, 3,
wilc_get_vif_idx(vif));
- kfree(pu8keybuf);
- } else if (pstrHostIFkeyAttr->action & ADDKEY) {
- pu8keybuf = kmalloc(pstrHostIFkeyAttr->attr.wep.key_len + 2, GFP_KERNEL);
- if (!pu8keybuf)
+ kfree(key_buf);
+ } else if (hif_key->action & ADDKEY) {
+ key_buf = kmalloc(hif_key->attr.wep.key_len + 2, GFP_KERNEL);
+ if (!key_buf)
return -ENOMEM;
- pu8keybuf[0] = pstrHostIFkeyAttr->attr.wep.index;
- memcpy(pu8keybuf + 1, &pstrHostIFkeyAttr->attr.wep.key_len, 1);
- memcpy(pu8keybuf + 2, pstrHostIFkeyAttr->attr.wep.key,
- pstrHostIFkeyAttr->attr.wep.key_len);
- kfree(pstrHostIFkeyAttr->attr.wep.key);
+ key_buf[0] = hif_key->attr.wep.index;
+ memcpy(key_buf + 1, &hif_key->attr.wep.key_len, 1);
+ memcpy(key_buf + 2, hif_key->attr.wep.key,
+ hif_key->attr.wep.key_len);
+ kfree(hif_key->attr.wep.key);
wid.id = (u16)WID_ADD_WEP_KEY;
wid.type = WID_STR;
- wid.val = (s8 *)pu8keybuf;
- wid.size = pstrHostIFkeyAttr->attr.wep.key_len + 2;
+ wid.val = (s8 *)key_buf;
+ wid.size = hif_key->attr.wep.key_len + 2;
result = wilc_send_config_pkt(vif, SET_CFG,
&wid, 1,
wilc_get_vif_idx(vif));
- kfree(pu8keybuf);
- } else if (pstrHostIFkeyAttr->action & REMOVEKEY) {
+ kfree(key_buf);
+ } else if (hif_key->action & REMOVEKEY) {
wid.id = (u16)WID_REMOVE_WEP_KEY;
wid.type = WID_STR;
- s8idxarray[0] = (s8)pstrHostIFkeyAttr->attr.wep.index;
+ s8idxarray[0] = (s8)hif_key->attr.wep.index;
wid.val = s8idxarray;
wid.size = 1;
result = wilc_send_config_pkt(vif, SET_CFG,
&wid, 1,
wilc_get_vif_idx(vif));
- } else if (pstrHostIFkeyAttr->action & DEFAULTKEY) {
+ } else if (hif_key->action & DEFAULTKEY) {
wid.id = (u16)WID_KEY_ID;
wid.type = WID_CHAR;
- wid.val = (s8 *)&pstrHostIFkeyAttr->attr.wep.index;
+ wid.val = (s8 *)&hif_key->attr.wep.index;
wid.size = sizeof(char);
result = wilc_send_config_pkt(vif, SET_CFG,
@@ -1617,157 +1620,157 @@ static int Handle_Key(struct wilc_vif *vif,
break;
case WPA_RX_GTK:
- if (pstrHostIFkeyAttr->action & ADDKEY_AP) {
- pu8keybuf = kzalloc(RX_MIC_KEY_MSG_LEN, GFP_KERNEL);
- if (!pu8keybuf) {
+ if (hif_key->action & ADDKEY_AP) {
+ key_buf = kzalloc(RX_MIC_KEY_MSG_LEN, GFP_KERNEL);
+ if (!key_buf) {
ret = -ENOMEM;
- goto _WPARxGtk_end_case_;
+ goto out_wpa_rx_gtk;
}
- if (pstrHostIFkeyAttr->attr.wpa.seq)
- memcpy(pu8keybuf + 6, pstrHostIFkeyAttr->attr.wpa.seq, 8);
+ if (hif_key->attr.wpa.seq)
+ memcpy(key_buf + 6, hif_key->attr.wpa.seq, 8);
- memcpy(pu8keybuf + 14, &pstrHostIFkeyAttr->attr.wpa.index, 1);
- memcpy(pu8keybuf + 15, &pstrHostIFkeyAttr->attr.wpa.key_len, 1);
- memcpy(pu8keybuf + 16, pstrHostIFkeyAttr->attr.wpa.key,
- pstrHostIFkeyAttr->attr.wpa.key_len);
+ memcpy(key_buf + 14, &hif_key->attr.wpa.index, 1);
+ memcpy(key_buf + 15, &hif_key->attr.wpa.key_len, 1);
+ memcpy(key_buf + 16, hif_key->attr.wpa.key,
+ hif_key->attr.wpa.key_len);
- strWIDList[0].id = (u16)WID_11I_MODE;
- strWIDList[0].type = WID_CHAR;
- strWIDList[0].size = sizeof(char);
- strWIDList[0].val = (s8 *)&pstrHostIFkeyAttr->attr.wpa.mode;
+ wid_list[0].id = (u16)WID_11I_MODE;
+ wid_list[0].type = WID_CHAR;
+ wid_list[0].size = sizeof(char);
+ wid_list[0].val = (s8 *)&hif_key->attr.wpa.mode;
- strWIDList[1].id = (u16)WID_ADD_RX_GTK;
- strWIDList[1].type = WID_STR;
- strWIDList[1].val = (s8 *)pu8keybuf;
- strWIDList[1].size = RX_MIC_KEY_MSG_LEN;
+ wid_list[1].id = (u16)WID_ADD_RX_GTK;
+ wid_list[1].type = WID_STR;
+ wid_list[1].val = (s8 *)key_buf;
+ wid_list[1].size = RX_MIC_KEY_MSG_LEN;
result = wilc_send_config_pkt(vif, SET_CFG,
- strWIDList, 2,
+ wid_list, 2,
wilc_get_vif_idx(vif));
- kfree(pu8keybuf);
+ kfree(key_buf);
complete(&hif_drv->comp_test_key_block);
- } else if (pstrHostIFkeyAttr->action & ADDKEY) {
- pu8keybuf = kzalloc(RX_MIC_KEY_MSG_LEN, GFP_KERNEL);
- if (!pu8keybuf) {
+ } else if (hif_key->action & ADDKEY) {
+ key_buf = kzalloc(RX_MIC_KEY_MSG_LEN, GFP_KERNEL);
+ if (!key_buf) {
ret = -ENOMEM;
- goto _WPARxGtk_end_case_;
+ goto out_wpa_rx_gtk;
}
if (hif_drv->hif_state == HOST_IF_CONNECTED)
- memcpy(pu8keybuf, hif_drv->assoc_bssid, ETH_ALEN);
+ memcpy(key_buf, hif_drv->assoc_bssid, ETH_ALEN);
else
netdev_err(vif->ndev, "Couldn't handle\n");
- memcpy(pu8keybuf + 6, pstrHostIFkeyAttr->attr.wpa.seq, 8);
- memcpy(pu8keybuf + 14, &pstrHostIFkeyAttr->attr.wpa.index, 1);
- memcpy(pu8keybuf + 15, &pstrHostIFkeyAttr->attr.wpa.key_len, 1);
- memcpy(pu8keybuf + 16, pstrHostIFkeyAttr->attr.wpa.key,
- pstrHostIFkeyAttr->attr.wpa.key_len);
+ memcpy(key_buf + 6, hif_key->attr.wpa.seq, 8);
+ memcpy(key_buf + 14, &hif_key->attr.wpa.index, 1);
+ memcpy(key_buf + 15, &hif_key->attr.wpa.key_len, 1);
+ memcpy(key_buf + 16, hif_key->attr.wpa.key,
+ hif_key->attr.wpa.key_len);
wid.id = (u16)WID_ADD_RX_GTK;
wid.type = WID_STR;
- wid.val = (s8 *)pu8keybuf;
+ wid.val = (s8 *)key_buf;
wid.size = RX_MIC_KEY_MSG_LEN;
result = wilc_send_config_pkt(vif, SET_CFG,
&wid, 1,
wilc_get_vif_idx(vif));
- kfree(pu8keybuf);
+ kfree(key_buf);
complete(&hif_drv->comp_test_key_block);
}
-_WPARxGtk_end_case_:
- kfree(pstrHostIFkeyAttr->attr.wpa.key);
- kfree(pstrHostIFkeyAttr->attr.wpa.seq);
+out_wpa_rx_gtk:
+ kfree(hif_key->attr.wpa.key);
+ kfree(hif_key->attr.wpa.seq);
if (ret)
return ret;
break;
case WPA_PTK:
- if (pstrHostIFkeyAttr->action & ADDKEY_AP) {
- pu8keybuf = kmalloc(PTK_KEY_MSG_LEN + 1, GFP_KERNEL);
- if (!pu8keybuf) {
+ if (hif_key->action & ADDKEY_AP) {
+ key_buf = kmalloc(PTK_KEY_MSG_LEN + 1, GFP_KERNEL);
+ if (!key_buf) {
ret = -ENOMEM;
- goto _WPAPtk_end_case_;
+ goto out_wpa_ptk;
}
- memcpy(pu8keybuf, pstrHostIFkeyAttr->attr.wpa.mac_addr, 6);
- memcpy(pu8keybuf + 6, &pstrHostIFkeyAttr->attr.wpa.index, 1);
- memcpy(pu8keybuf + 7, &pstrHostIFkeyAttr->attr.wpa.key_len, 1);
- memcpy(pu8keybuf + 8, pstrHostIFkeyAttr->attr.wpa.key,
- pstrHostIFkeyAttr->attr.wpa.key_len);
+ memcpy(key_buf, hif_key->attr.wpa.mac_addr, 6);
+ memcpy(key_buf + 6, &hif_key->attr.wpa.index, 1);
+ memcpy(key_buf + 7, &hif_key->attr.wpa.key_len, 1);
+ memcpy(key_buf + 8, hif_key->attr.wpa.key,
+ hif_key->attr.wpa.key_len);
- strWIDList[0].id = (u16)WID_11I_MODE;
- strWIDList[0].type = WID_CHAR;
- strWIDList[0].size = sizeof(char);
- strWIDList[0].val = (s8 *)&pstrHostIFkeyAttr->attr.wpa.mode;
+ wid_list[0].id = (u16)WID_11I_MODE;
+ wid_list[0].type = WID_CHAR;
+ wid_list[0].size = sizeof(char);
+ wid_list[0].val = (s8 *)&hif_key->attr.wpa.mode;
- strWIDList[1].id = (u16)WID_ADD_PTK;
- strWIDList[1].type = WID_STR;
- strWIDList[1].val = (s8 *)pu8keybuf;
- strWIDList[1].size = PTK_KEY_MSG_LEN + 1;
+ wid_list[1].id = (u16)WID_ADD_PTK;
+ wid_list[1].type = WID_STR;
+ wid_list[1].val = (s8 *)key_buf;
+ wid_list[1].size = PTK_KEY_MSG_LEN + 1;
result = wilc_send_config_pkt(vif, SET_CFG,
- strWIDList, 2,
+ wid_list, 2,
wilc_get_vif_idx(vif));
- kfree(pu8keybuf);
+ kfree(key_buf);
complete(&hif_drv->comp_test_key_block);
- } else if (pstrHostIFkeyAttr->action & ADDKEY) {
- pu8keybuf = kmalloc(PTK_KEY_MSG_LEN, GFP_KERNEL);
- if (!pu8keybuf) {
+ } else if (hif_key->action & ADDKEY) {
+ key_buf = kmalloc(PTK_KEY_MSG_LEN, GFP_KERNEL);
+ if (!key_buf) {
netdev_err(vif->ndev, "No buffer send PTK\n");
ret = -ENOMEM;
- goto _WPAPtk_end_case_;
+ goto out_wpa_ptk;
}
- memcpy(pu8keybuf, pstrHostIFkeyAttr->attr.wpa.mac_addr, 6);
- memcpy(pu8keybuf + 6, &pstrHostIFkeyAttr->attr.wpa.key_len, 1);
- memcpy(pu8keybuf + 7, pstrHostIFkeyAttr->attr.wpa.key,
- pstrHostIFkeyAttr->attr.wpa.key_len);
+ memcpy(key_buf, hif_key->attr.wpa.mac_addr, 6);
+ memcpy(key_buf + 6, &hif_key->attr.wpa.key_len, 1);
+ memcpy(key_buf + 7, hif_key->attr.wpa.key,
+ hif_key->attr.wpa.key_len);
wid.id = (u16)WID_ADD_PTK;
wid.type = WID_STR;
- wid.val = (s8 *)pu8keybuf;
+ wid.val = (s8 *)key_buf;
wid.size = PTK_KEY_MSG_LEN;
result = wilc_send_config_pkt(vif, SET_CFG,
&wid, 1,
wilc_get_vif_idx(vif));
- kfree(pu8keybuf);
+ kfree(key_buf);
complete(&hif_drv->comp_test_key_block);
}
-_WPAPtk_end_case_:
- kfree(pstrHostIFkeyAttr->attr.wpa.key);
+out_wpa_ptk:
+ kfree(hif_key->attr.wpa.key);
if (ret)
return ret;
break;
case PMKSA:
- pu8keybuf = kmalloc((pstrHostIFkeyAttr->attr.pmkid.numpmkid * PMKSA_KEY_LEN) + 1, GFP_KERNEL);
- if (!pu8keybuf)
+ key_buf = kmalloc((hif_key->attr.pmkid.numpmkid * PMKSA_KEY_LEN) + 1, GFP_KERNEL);
+ if (!key_buf)
return -ENOMEM;
- pu8keybuf[0] = pstrHostIFkeyAttr->attr.pmkid.numpmkid;
+ key_buf[0] = hif_key->attr.pmkid.numpmkid;
- for (i = 0; i < pstrHostIFkeyAttr->attr.pmkid.numpmkid; i++) {
- memcpy(pu8keybuf + ((PMKSA_KEY_LEN * i) + 1), pstrHostIFkeyAttr->attr.pmkid.pmkidlist[i].bssid, ETH_ALEN);
- memcpy(pu8keybuf + ((PMKSA_KEY_LEN * i) + ETH_ALEN + 1), pstrHostIFkeyAttr->attr.pmkid.pmkidlist[i].pmkid, PMKID_LEN);
+ for (i = 0; i < hif_key->attr.pmkid.numpmkid; i++) {
+ memcpy(key_buf + ((PMKSA_KEY_LEN * i) + 1), hif_key->attr.pmkid.pmkidlist[i].bssid, ETH_ALEN);
+ memcpy(key_buf + ((PMKSA_KEY_LEN * i) + ETH_ALEN + 1), hif_key->attr.pmkid.pmkidlist[i].pmkid, PMKID_LEN);
}
wid.id = (u16)WID_PMKID_INFO;
wid.type = WID_STR;
- wid.val = (s8 *)pu8keybuf;
- wid.size = (pstrHostIFkeyAttr->attr.pmkid.numpmkid * PMKSA_KEY_LEN) + 1;
+ wid.val = (s8 *)key_buf;
+ wid.size = (hif_key->attr.pmkid.numpmkid * PMKSA_KEY_LEN) + 1;
result = wilc_send_config_pkt(vif, SET_CFG, &wid, 1,
wilc_get_vif_idx(vif));
- kfree(pu8keybuf);
+ kfree(key_buf);
break;
}
@@ -1777,17 +1780,19 @@ _WPAPtk_end_case_:
return result;
}
-static void Handle_Disconnect(struct wilc_vif *vif)
+static void handle_disconnect(struct wilc_vif *vif)
{
struct wid wid;
struct host_if_drv *hif_drv = vif->hif_drv;
-
+ struct disconnect_info disconn_info;
+ struct user_scan_req *scan_req;
+ struct user_conn_req *conn_req;
s32 result = 0;
- u16 u16DummyReasonCode = 0;
+ u16 dummy_reason_code = 0;
wid.id = (u16)WID_DISCONNECT;
wid.type = WID_CHAR;
- wid.val = (s8 *)&u16DummyReasonCode;
+ wid.val = (s8 *)&dummy_reason_code;
wid.size = sizeof(char);
wilc_optaining_ip = false;
@@ -1800,62 +1805,50 @@ static void Handle_Disconnect(struct wilc_vif *vif)
if (result) {
netdev_err(vif->ndev, "Failed to send dissconect\n");
- } else {
- struct disconnect_info strDisconnectNotifInfo;
-
- memset(&strDisconnectNotifInfo, 0, sizeof(struct disconnect_info));
+ goto out;
+ }
- strDisconnectNotifInfo.reason = 0;
- strDisconnectNotifInfo.ie = NULL;
- strDisconnectNotifInfo.ie_len = 0;
+ memset(&disconn_info, 0, sizeof(struct disconnect_info));
- if (hif_drv->usr_scan_req.scan_result) {
- del_timer(&hif_drv->scan_timer);
- hif_drv->usr_scan_req.scan_result(SCAN_EVENT_ABORTED,
- NULL,
- hif_drv->usr_scan_req.arg,
- NULL);
- hif_drv->usr_scan_req.scan_result = NULL;
- }
+ disconn_info.reason = 0;
+ disconn_info.ie = NULL;
+ disconn_info.ie_len = 0;
+ scan_req = &hif_drv->usr_scan_req;
+ conn_req = &hif_drv->usr_conn_req;
- if (hif_drv->usr_conn_req.conn_result) {
- if (hif_drv->hif_state == HOST_IF_WAITING_CONN_RESP)
- del_timer(&hif_drv->connect_timer);
+ if (scan_req->scan_result) {
+ del_timer(&hif_drv->scan_timer);
+ scan_req->scan_result(SCAN_EVENT_ABORTED, NULL, scan_req->arg,
+ NULL);
+ scan_req->scan_result = NULL;
+ }
- hif_drv->usr_conn_req.conn_result(CONN_DISCONN_EVENT_DISCONN_NOTIF,
- NULL,
- 0,
- &strDisconnectNotifInfo,
- hif_drv->usr_conn_req.arg);
- } else {
- netdev_err(vif->ndev, "conn_result = NULL\n");
- }
+ if (conn_req->conn_result) {
+ if (hif_drv->hif_state == HOST_IF_WAITING_CONN_RESP)
+ del_timer(&hif_drv->connect_timer);
- scan_while_connected = false;
+ conn_req->conn_result(CONN_DISCONN_EVENT_DISCONN_NOTIF, NULL,
+ 0, &disconn_info, conn_req->arg);
+ } else {
+ netdev_err(vif->ndev, "conn_result = NULL\n");
+ }
- hif_drv->hif_state = HOST_IF_IDLE;
+ scan_while_connected = false;
- eth_zero_addr(hif_drv->assoc_bssid);
+ hif_drv->hif_state = HOST_IF_IDLE;
- hif_drv->usr_conn_req.ssid_len = 0;
- kfree(hif_drv->usr_conn_req.ssid);
- hif_drv->usr_conn_req.ssid = NULL;
- kfree(hif_drv->usr_conn_req.bssid);
- hif_drv->usr_conn_req.bssid = NULL;
- hif_drv->usr_conn_req.ies_len = 0;
- kfree(hif_drv->usr_conn_req.ies);
- hif_drv->usr_conn_req.ies = NULL;
+ eth_zero_addr(hif_drv->assoc_bssid);
- if (join_req && join_req_vif == vif) {
- kfree(join_req);
- join_req = NULL;
- }
+ conn_req->ssid_len = 0;
+ kfree(conn_req->ssid);
+ conn_req->ssid = NULL;
+ kfree(conn_req->bssid);
+ conn_req->bssid = NULL;
+ conn_req->ies_len = 0;
+ kfree(conn_req->ies);
+ conn_req->ies = NULL;
- if (info_element && join_req_vif == vif) {
- kfree(info_element);
- info_element = NULL;
- }
- }
+out:
complete(&hif_drv->comp_test_disconn_block);
}
@@ -1869,7 +1862,7 @@ void wilc_resolve_disconnect_aberration(struct wilc_vif *vif)
wilc_disconnect(vif, 1);
}
-static void Handle_GetRssi(struct wilc_vif *vif)
+static void handle_get_rssi(struct wilc_vif *vif)
{
s32 result = 0;
struct wid wid;
@@ -1889,65 +1882,64 @@ static void Handle_GetRssi(struct wilc_vif *vif)
complete(&vif->hif_drv->comp_get_rssi);
}
-static s32 Handle_GetStatistics(struct wilc_vif *vif,
- struct rf_info *pstrStatistics)
+static s32 handle_get_statistics(struct wilc_vif *vif,
+ struct rf_info *stats)
{
- struct wid strWIDList[5];
- u32 u32WidsCount = 0, result = 0;
-
- strWIDList[u32WidsCount].id = WID_LINKSPEED;
- strWIDList[u32WidsCount].type = WID_CHAR;
- strWIDList[u32WidsCount].size = sizeof(char);
- strWIDList[u32WidsCount].val = (s8 *)&pstrStatistics->link_speed;
- u32WidsCount++;
-
- strWIDList[u32WidsCount].id = WID_RSSI;
- strWIDList[u32WidsCount].type = WID_CHAR;
- strWIDList[u32WidsCount].size = sizeof(char);
- strWIDList[u32WidsCount].val = (s8 *)&pstrStatistics->rssi;
- u32WidsCount++;
-
- strWIDList[u32WidsCount].id = WID_SUCCESS_FRAME_COUNT;
- strWIDList[u32WidsCount].type = WID_INT;
- strWIDList[u32WidsCount].size = sizeof(u32);
- strWIDList[u32WidsCount].val = (s8 *)&pstrStatistics->tx_cnt;
- u32WidsCount++;
-
- strWIDList[u32WidsCount].id = WID_RECEIVED_FRAGMENT_COUNT;
- strWIDList[u32WidsCount].type = WID_INT;
- strWIDList[u32WidsCount].size = sizeof(u32);
- strWIDList[u32WidsCount].val = (s8 *)&pstrStatistics->rx_cnt;
- u32WidsCount++;
-
- strWIDList[u32WidsCount].id = WID_FAILED_COUNT;
- strWIDList[u32WidsCount].type = WID_INT;
- strWIDList[u32WidsCount].size = sizeof(u32);
- strWIDList[u32WidsCount].val = (s8 *)&pstrStatistics->tx_fail_cnt;
- u32WidsCount++;
-
- result = wilc_send_config_pkt(vif, GET_CFG, strWIDList,
- u32WidsCount,
+ struct wid wid_list[5];
+ u32 wid_cnt = 0, result = 0;
+
+ wid_list[wid_cnt].id = WID_LINKSPEED;
+ wid_list[wid_cnt].type = WID_CHAR;
+ wid_list[wid_cnt].size = sizeof(char);
+ wid_list[wid_cnt].val = (s8 *)&stats->link_speed;
+ wid_cnt++;
+
+ wid_list[wid_cnt].id = WID_RSSI;
+ wid_list[wid_cnt].type = WID_CHAR;
+ wid_list[wid_cnt].size = sizeof(char);
+ wid_list[wid_cnt].val = (s8 *)&stats->rssi;
+ wid_cnt++;
+
+ wid_list[wid_cnt].id = WID_SUCCESS_FRAME_COUNT;
+ wid_list[wid_cnt].type = WID_INT;
+ wid_list[wid_cnt].size = sizeof(u32);
+ wid_list[wid_cnt].val = (s8 *)&stats->tx_cnt;
+ wid_cnt++;
+
+ wid_list[wid_cnt].id = WID_RECEIVED_FRAGMENT_COUNT;
+ wid_list[wid_cnt].type = WID_INT;
+ wid_list[wid_cnt].size = sizeof(u32);
+ wid_list[wid_cnt].val = (s8 *)&stats->rx_cnt;
+ wid_cnt++;
+
+ wid_list[wid_cnt].id = WID_FAILED_COUNT;
+ wid_list[wid_cnt].type = WID_INT;
+ wid_list[wid_cnt].size = sizeof(u32);
+ wid_list[wid_cnt].val = (s8 *)&stats->tx_fail_cnt;
+ wid_cnt++;
+
+ result = wilc_send_config_pkt(vif, GET_CFG, wid_list,
+ wid_cnt,
wilc_get_vif_idx(vif));
if (result)
netdev_err(vif->ndev, "Failed to send scan parameters\n");
- if (pstrStatistics->link_speed > TCP_ACK_FILTER_LINK_SPEED_THRESH &&
- pstrStatistics->link_speed != DEFAULT_LINK_SPEED)
+ if (stats->link_speed > TCP_ACK_FILTER_LINK_SPEED_THRESH &&
+ stats->link_speed != DEFAULT_LINK_SPEED)
wilc_enable_tcp_ack_filter(true);
- else if (pstrStatistics->link_speed != DEFAULT_LINK_SPEED)
+ else if (stats->link_speed != DEFAULT_LINK_SPEED)
wilc_enable_tcp_ack_filter(false);
- if (pstrStatistics != &vif->wilc->dummy_statistics)
+ if (stats != &vif->wilc->dummy_statistics)
complete(&hif_wait_response);
return 0;
}
-static s32 Handle_Get_InActiveTime(struct wilc_vif *vif,
- struct sta_inactive_t *strHostIfStaInactiveT)
+static s32 handle_get_inactive_time(struct wilc_vif *vif,
+ struct sta_inactive_t *hif_sta_inactive)
{
s32 result = 0;
- u8 *stamac;
struct wid wid;
struct host_if_drv *hif_drv = vif->hif_drv;
@@ -1958,11 +1950,11 @@ static s32 Handle_Get_InActiveTime(struct wilc_vif *vif,
if (!wid.val)
return -ENOMEM;
- stamac = wid.val;
- ether_addr_copy(stamac, strHostIfStaInactiveT->mac);
+ ether_addr_copy(wid.val, hif_sta_inactive->mac);
result = wilc_send_config_pkt(vif, SET_CFG, &wid, 1,
wilc_get_vif_idx(vif));
+ kfree(wid.val);
if (result) {
netdev_err(vif->ndev, "Failed to SET inactive time\n");
@@ -1987,64 +1979,63 @@ static s32 Handle_Get_InActiveTime(struct wilc_vif *vif,
return result;
}
-static void Handle_AddBeacon(struct wilc_vif *vif,
- struct beacon_attr *pstrSetBeaconParam)
+static void handle_add_beacon(struct wilc_vif *vif, struct beacon_attr *param)
{
s32 result = 0;
struct wid wid;
- u8 *pu8CurrByte;
+ u8 *cur_byte;
wid.id = (u16)WID_ADD_BEACON;
wid.type = WID_BIN;
- wid.size = pstrSetBeaconParam->head_len + pstrSetBeaconParam->tail_len + 16;
+ wid.size = param->head_len + param->tail_len + 16;
wid.val = kmalloc(wid.size, GFP_KERNEL);
if (!wid.val)
- goto ERRORHANDLER;
+ goto error;
- pu8CurrByte = wid.val;
- *pu8CurrByte++ = (pstrSetBeaconParam->interval & 0xFF);
- *pu8CurrByte++ = ((pstrSetBeaconParam->interval >> 8) & 0xFF);
- *pu8CurrByte++ = ((pstrSetBeaconParam->interval >> 16) & 0xFF);
- *pu8CurrByte++ = ((pstrSetBeaconParam->interval >> 24) & 0xFF);
+ cur_byte = wid.val;
+ *cur_byte++ = (param->interval & 0xFF);
+ *cur_byte++ = ((param->interval >> 8) & 0xFF);
+ *cur_byte++ = ((param->interval >> 16) & 0xFF);
+ *cur_byte++ = ((param->interval >> 24) & 0xFF);
- *pu8CurrByte++ = (pstrSetBeaconParam->dtim_period & 0xFF);
- *pu8CurrByte++ = ((pstrSetBeaconParam->dtim_period >> 8) & 0xFF);
- *pu8CurrByte++ = ((pstrSetBeaconParam->dtim_period >> 16) & 0xFF);
- *pu8CurrByte++ = ((pstrSetBeaconParam->dtim_period >> 24) & 0xFF);
+ *cur_byte++ = (param->dtim_period & 0xFF);
+ *cur_byte++ = ((param->dtim_period >> 8) & 0xFF);
+ *cur_byte++ = ((param->dtim_period >> 16) & 0xFF);
+ *cur_byte++ = ((param->dtim_period >> 24) & 0xFF);
- *pu8CurrByte++ = (pstrSetBeaconParam->head_len & 0xFF);
- *pu8CurrByte++ = ((pstrSetBeaconParam->head_len >> 8) & 0xFF);
- *pu8CurrByte++ = ((pstrSetBeaconParam->head_len >> 16) & 0xFF);
- *pu8CurrByte++ = ((pstrSetBeaconParam->head_len >> 24) & 0xFF);
+ *cur_byte++ = (param->head_len & 0xFF);
+ *cur_byte++ = ((param->head_len >> 8) & 0xFF);
+ *cur_byte++ = ((param->head_len >> 16) & 0xFF);
+ *cur_byte++ = ((param->head_len >> 24) & 0xFF);
- memcpy(pu8CurrByte, pstrSetBeaconParam->head, pstrSetBeaconParam->head_len);
- pu8CurrByte += pstrSetBeaconParam->head_len;
+ memcpy(cur_byte, param->head, param->head_len);
+ cur_byte += param->head_len;
- *pu8CurrByte++ = (pstrSetBeaconParam->tail_len & 0xFF);
- *pu8CurrByte++ = ((pstrSetBeaconParam->tail_len >> 8) & 0xFF);
- *pu8CurrByte++ = ((pstrSetBeaconParam->tail_len >> 16) & 0xFF);
- *pu8CurrByte++ = ((pstrSetBeaconParam->tail_len >> 24) & 0xFF);
+ *cur_byte++ = (param->tail_len & 0xFF);
+ *cur_byte++ = ((param->tail_len >> 8) & 0xFF);
+ *cur_byte++ = ((param->tail_len >> 16) & 0xFF);
+ *cur_byte++ = ((param->tail_len >> 24) & 0xFF);
- if (pstrSetBeaconParam->tail)
- memcpy(pu8CurrByte, pstrSetBeaconParam->tail, pstrSetBeaconParam->tail_len);
- pu8CurrByte += pstrSetBeaconParam->tail_len;
+ if (param->tail)
+ memcpy(cur_byte, param->tail, param->tail_len);
+ cur_byte += param->tail_len;
result = wilc_send_config_pkt(vif, SET_CFG, &wid, 1,
wilc_get_vif_idx(vif));
if (result)
netdev_err(vif->ndev, "Failed to send add beacon\n");
-ERRORHANDLER:
+error:
kfree(wid.val);
- kfree(pstrSetBeaconParam->head);
- kfree(pstrSetBeaconParam->tail);
+ kfree(param->head);
+ kfree(param->tail);
}
-static void Handle_DelBeacon(struct wilc_vif *vif)
+static void handle_del_beacon(struct wilc_vif *vif)
{
s32 result = 0;
struct wid wid;
- u8 *pu8CurrByte;
+ u8 *cur_byte;
wid.id = (u16)WID_DEL_BEACON;
wid.type = WID_CHAR;
@@ -2054,7 +2045,7 @@ static void Handle_DelBeacon(struct wilc_vif *vif)
if (!wid.val)
return;
- pu8CurrByte = wid.val;
+ cur_byte = wid.val;
result = wilc_send_config_pkt(vif, SET_CFG, &wid, 1,
wilc_get_vif_idx(vif));
@@ -2062,95 +2053,92 @@ static void Handle_DelBeacon(struct wilc_vif *vif)
netdev_err(vif->ndev, "Failed to send delete beacon\n");
}
-static u32 WILC_HostIf_PackStaParam(u8 *pu8Buffer,
- struct add_sta_param *pstrStationParam)
+static u32 wilc_hif_pack_sta_param(u8 *buff, struct add_sta_param *param)
{
- u8 *pu8CurrByte;
+ u8 *cur_byte;
- pu8CurrByte = pu8Buffer;
+ cur_byte = buff;
- memcpy(pu8CurrByte, pstrStationParam->bssid, ETH_ALEN);
- pu8CurrByte += ETH_ALEN;
+ memcpy(cur_byte, param->bssid, ETH_ALEN);
+ cur_byte += ETH_ALEN;
- *pu8CurrByte++ = pstrStationParam->aid & 0xFF;
- *pu8CurrByte++ = (pstrStationParam->aid >> 8) & 0xFF;
+ *cur_byte++ = param->aid & 0xFF;
+ *cur_byte++ = (param->aid >> 8) & 0xFF;
- *pu8CurrByte++ = pstrStationParam->rates_len;
- if (pstrStationParam->rates_len > 0)
- memcpy(pu8CurrByte, pstrStationParam->rates,
- pstrStationParam->rates_len);
- pu8CurrByte += pstrStationParam->rates_len;
+ *cur_byte++ = param->rates_len;
+ if (param->rates_len > 0)
+ memcpy(cur_byte, param->rates, param->rates_len);
+ cur_byte += param->rates_len;
- *pu8CurrByte++ = pstrStationParam->ht_supported;
- memcpy(pu8CurrByte, &pstrStationParam->ht_capa,
- sizeof(struct ieee80211_ht_cap));
- pu8CurrByte += sizeof(struct ieee80211_ht_cap);
+ *cur_byte++ = param->ht_supported;
+ memcpy(cur_byte, &param->ht_capa, sizeof(struct ieee80211_ht_cap));
+ cur_byte += sizeof(struct ieee80211_ht_cap);
- *pu8CurrByte++ = pstrStationParam->flags_mask & 0xFF;
- *pu8CurrByte++ = (pstrStationParam->flags_mask >> 8) & 0xFF;
+ *cur_byte++ = param->flags_mask & 0xFF;
+ *cur_byte++ = (param->flags_mask >> 8) & 0xFF;
- *pu8CurrByte++ = pstrStationParam->flags_set & 0xFF;
- *pu8CurrByte++ = (pstrStationParam->flags_set >> 8) & 0xFF;
+ *cur_byte++ = param->flags_set & 0xFF;
+ *cur_byte++ = (param->flags_set >> 8) & 0xFF;
- return pu8CurrByte - pu8Buffer;
+ return cur_byte - buff;
}
-static void Handle_AddStation(struct wilc_vif *vif,
- struct add_sta_param *pstrStationParam)
+static void handle_add_station(struct wilc_vif *vif,
+ struct add_sta_param *param)
{
s32 result = 0;
struct wid wid;
- u8 *pu8CurrByte;
+ u8 *cur_byte;
wid.id = (u16)WID_ADD_STA;
wid.type = WID_BIN;
- wid.size = WILC_ADD_STA_LENGTH + pstrStationParam->rates_len;
+ wid.size = WILC_ADD_STA_LENGTH + param->rates_len;
wid.val = kmalloc(wid.size, GFP_KERNEL);
if (!wid.val)
- goto ERRORHANDLER;
+ goto error;
- pu8CurrByte = wid.val;
- pu8CurrByte += WILC_HostIf_PackStaParam(pu8CurrByte, pstrStationParam);
+ cur_byte = wid.val;
+ cur_byte += wilc_hif_pack_sta_param(cur_byte, param);
result = wilc_send_config_pkt(vif, SET_CFG, &wid, 1,
wilc_get_vif_idx(vif));
if (result != 0)
netdev_err(vif->ndev, "Failed to send add station\n");
-ERRORHANDLER:
- kfree(pstrStationParam->rates);
+error:
+ kfree(param->rates);
kfree(wid.val);
}
-static void Handle_DelAllSta(struct wilc_vif *vif,
- struct del_all_sta *pstrDelAllStaParam)
+static void handle_del_all_sta(struct wilc_vif *vif,
+ struct del_all_sta *param)
{
s32 result = 0;
struct wid wid;
- u8 *pu8CurrByte;
+ u8 *curr_byte;
u8 i;
- u8 au8Zero_Buff[6] = {0};
+ u8 zero_buff[6] = {0};
wid.id = (u16)WID_DEL_ALL_STA;
wid.type = WID_STR;
- wid.size = (pstrDelAllStaParam->assoc_sta * ETH_ALEN) + 1;
+ wid.size = (param->assoc_sta * ETH_ALEN) + 1;
- wid.val = kmalloc((pstrDelAllStaParam->assoc_sta * ETH_ALEN) + 1, GFP_KERNEL);
+ wid.val = kmalloc((param->assoc_sta * ETH_ALEN) + 1, GFP_KERNEL);
if (!wid.val)
- goto ERRORHANDLER;
+ goto error;
- pu8CurrByte = wid.val;
+ curr_byte = wid.val;
- *(pu8CurrByte++) = pstrDelAllStaParam->assoc_sta;
+ *(curr_byte++) = param->assoc_sta;
for (i = 0; i < MAX_NUM_STA; i++) {
- if (memcmp(pstrDelAllStaParam->del_all_sta[i], au8Zero_Buff, ETH_ALEN))
- memcpy(pu8CurrByte, pstrDelAllStaParam->del_all_sta[i], ETH_ALEN);
+ if (memcmp(param->del_all_sta[i], zero_buff, ETH_ALEN))
+ memcpy(curr_byte, param->del_all_sta[i], ETH_ALEN);
else
continue;
- pu8CurrByte += ETH_ALEN;
+ curr_byte += ETH_ALEN;
}
result = wilc_send_config_pkt(vif, SET_CFG, &wid, 1,
@@ -2158,18 +2146,17 @@ static void Handle_DelAllSta(struct wilc_vif *vif,
if (result)
netdev_err(vif->ndev, "Failed to send add station\n");
-ERRORHANDLER:
+error:
kfree(wid.val);
complete(&hif_wait_response);
}
-static void Handle_DelStation(struct wilc_vif *vif,
- struct del_sta *pstrDelStaParam)
+static void handle_del_station(struct wilc_vif *vif, struct del_sta *param)
{
s32 result = 0;
struct wid wid;
- u8 *pu8CurrByte;
+ u8 *cur_byte;
wid.id = (u16)WID_REMOVE_STA;
wid.type = WID_BIN;
@@ -2177,107 +2164,108 @@ static void Handle_DelStation(struct wilc_vif *vif,
wid.val = kmalloc(wid.size, GFP_KERNEL);
if (!wid.val)
- goto ERRORHANDLER;
+ goto error;
- pu8CurrByte = wid.val;
+ cur_byte = wid.val;
- ether_addr_copy(pu8CurrByte, pstrDelStaParam->mac_addr);
+ ether_addr_copy(cur_byte, param->mac_addr);
result = wilc_send_config_pkt(vif, SET_CFG, &wid, 1,
wilc_get_vif_idx(vif));
if (result)
netdev_err(vif->ndev, "Failed to send add station\n");
-ERRORHANDLER:
+error:
kfree(wid.val);
}
-static void Handle_EditStation(struct wilc_vif *vif,
- struct add_sta_param *pstrStationParam)
+static void handle_edit_station(struct wilc_vif *vif,
+ struct add_sta_param *param)
{
s32 result = 0;
struct wid wid;
- u8 *pu8CurrByte;
+ u8 *cur_byte;
wid.id = (u16)WID_EDIT_STA;
wid.type = WID_BIN;
- wid.size = WILC_ADD_STA_LENGTH + pstrStationParam->rates_len;
+ wid.size = WILC_ADD_STA_LENGTH + param->rates_len;
wid.val = kmalloc(wid.size, GFP_KERNEL);
if (!wid.val)
- goto ERRORHANDLER;
+ goto error;
- pu8CurrByte = wid.val;
- pu8CurrByte += WILC_HostIf_PackStaParam(pu8CurrByte, pstrStationParam);
+ cur_byte = wid.val;
+ cur_byte += wilc_hif_pack_sta_param(cur_byte, param);
result = wilc_send_config_pkt(vif, SET_CFG, &wid, 1,
wilc_get_vif_idx(vif));
if (result)
netdev_err(vif->ndev, "Failed to send edit station\n");
-ERRORHANDLER:
- kfree(pstrStationParam->rates);
+error:
+ kfree(param->rates);
kfree(wid.val);
}
-static int Handle_RemainOnChan(struct wilc_vif *vif,
- struct remain_ch *pstrHostIfRemainOnChan)
+static int handle_remain_on_chan(struct wilc_vif *vif,
+ struct remain_ch *hif_remain_ch)
{
s32 result = 0;
- u8 u8remain_on_chan_flag;
+ u8 remain_on_chan_flag;
struct wid wid;
struct host_if_drv *hif_drv = vif->hif_drv;
if (!hif_drv->remain_on_ch_pending) {
- hif_drv->remain_on_ch.arg = pstrHostIfRemainOnChan->arg;
- hif_drv->remain_on_ch.expired = pstrHostIfRemainOnChan->expired;
- hif_drv->remain_on_ch.ready = pstrHostIfRemainOnChan->ready;
- hif_drv->remain_on_ch.ch = pstrHostIfRemainOnChan->ch;
- hif_drv->remain_on_ch.id = pstrHostIfRemainOnChan->id;
+ hif_drv->remain_on_ch.arg = hif_remain_ch->arg;
+ hif_drv->remain_on_ch.expired = hif_remain_ch->expired;
+ hif_drv->remain_on_ch.ready = hif_remain_ch->ready;
+ hif_drv->remain_on_ch.ch = hif_remain_ch->ch;
+ hif_drv->remain_on_ch.id = hif_remain_ch->id;
} else {
- pstrHostIfRemainOnChan->ch = hif_drv->remain_on_ch.ch;
+ hif_remain_ch->ch = hif_drv->remain_on_ch.ch;
}
if (hif_drv->usr_scan_req.scan_result) {
hif_drv->remain_on_ch_pending = 1;
result = -EBUSY;
- goto ERRORHANDLER;
+ goto error;
}
if (hif_drv->hif_state == HOST_IF_WAITING_CONN_RESP) {
result = -EBUSY;
- goto ERRORHANDLER;
+ goto error;
}
if (wilc_optaining_ip || wilc_connecting) {
result = -EBUSY;
- goto ERRORHANDLER;
+ goto error;
}
- u8remain_on_chan_flag = true;
+ remain_on_chan_flag = true;
wid.id = (u16)WID_REMAIN_ON_CHAN;
wid.type = WID_STR;
wid.size = 2;
wid.val = kmalloc(wid.size, GFP_KERNEL);
if (!wid.val) {
result = -ENOMEM;
- goto ERRORHANDLER;
+ goto error;
}
- wid.val[0] = u8remain_on_chan_flag;
- wid.val[1] = (s8)pstrHostIfRemainOnChan->ch;
+ wid.val[0] = remain_on_chan_flag;
+ wid.val[1] = (s8)hif_remain_ch->ch;
result = wilc_send_config_pkt(vif, SET_CFG, &wid, 1,
wilc_get_vif_idx(vif));
+ kfree(wid.val);
if (result != 0)
netdev_err(vif->ndev, "Failed to set remain on channel\n");
-ERRORHANDLER:
+error:
{
P2P_LISTEN_STATE = 1;
hif_drv->remain_on_ch_timer_vif = vif;
mod_timer(&hif_drv->remain_on_ch_timer,
jiffies +
- msecs_to_jiffies(pstrHostIfRemainOnChan->duration));
+ msecs_to_jiffies(hif_remain_ch->duration));
if (hif_drv->remain_on_ch.ready)
hif_drv->remain_on_ch.ready(hif_drv->remain_on_ch.arg);
@@ -2289,12 +2277,12 @@ ERRORHANDLER:
return result;
}
-static int Handle_RegisterFrame(struct wilc_vif *vif,
- struct reg_frame *pstrHostIfRegisterFrame)
+static int handle_register_frame(struct wilc_vif *vif,
+ struct reg_frame *hif_reg_frame)
{
s32 result = 0;
struct wid wid;
- u8 *pu8CurrByte;
+ u8 *cur_byte;
wid.id = (u16)WID_REGISTER_FRAME;
wid.type = WID_STR;
@@ -2302,16 +2290,17 @@ static int Handle_RegisterFrame(struct wilc_vif *vif,
if (!wid.val)
return -ENOMEM;
- pu8CurrByte = wid.val;
+ cur_byte = wid.val;
- *pu8CurrByte++ = pstrHostIfRegisterFrame->reg;
- *pu8CurrByte++ = pstrHostIfRegisterFrame->reg_id;
- memcpy(pu8CurrByte, &pstrHostIfRegisterFrame->frame_type, sizeof(u16));
+ *cur_byte++ = hif_reg_frame->reg;
+ *cur_byte++ = hif_reg_frame->reg_id;
+ memcpy(cur_byte, &hif_reg_frame->frame_type, sizeof(u16));
wid.size = sizeof(u16) + 2;
result = wilc_send_config_pkt(vif, SET_CFG, &wid, 1,
wilc_get_vif_idx(vif));
+ kfree(wid.val);
if (result) {
netdev_err(vif->ndev, "Failed to frame register\n");
result = -EINVAL;
@@ -2320,16 +2309,16 @@ static int Handle_RegisterFrame(struct wilc_vif *vif,
return result;
}
-static u32 Handle_ListenStateExpired(struct wilc_vif *vif,
- struct remain_ch *pstrHostIfRemainOnChan)
+static u32 handle_listen_state_expired(struct wilc_vif *vif,
+ struct remain_ch *hif_remain_ch)
{
- u8 u8remain_on_chan_flag;
+ u8 remain_on_chan_flag;
struct wid wid;
s32 result = 0;
struct host_if_drv *hif_drv = vif->hif_drv;
if (P2P_LISTEN_STATE) {
- u8remain_on_chan_flag = false;
+ remain_on_chan_flag = false;
wid.id = (u16)WID_REMAIN_ON_CHAN;
wid.type = WID_STR;
wid.size = 2;
@@ -2338,11 +2327,12 @@ static u32 Handle_ListenStateExpired(struct wilc_vif *vif,
if (!wid.val)
return -ENOMEM;
- wid.val[0] = u8remain_on_chan_flag;
+ wid.val[0] = remain_on_chan_flag;
wid.val[1] = FALSE_FRMWR_CHANNEL;
result = wilc_send_config_pkt(vif, SET_CFG, &wid, 1,
wilc_get_vif_idx(vif));
+ kfree(wid.val);
if (result != 0) {
netdev_err(vif->ndev, "Failed to set remain channel\n");
goto _done_;
@@ -2350,7 +2340,7 @@ static u32 Handle_ListenStateExpired(struct wilc_vif *vif,
if (hif_drv->remain_on_ch.expired) {
hif_drv->remain_on_ch.expired(hif_drv->remain_on_ch.arg,
- pstrHostIfRemainOnChan->id);
+ hif_remain_ch->id);
}
P2P_LISTEN_STATE = 0;
} else {
@@ -2362,7 +2352,7 @@ _done_:
return result;
}
-static void ListenTimerCB(struct timer_list *t)
+static void listen_timer_cb(struct timer_list *t)
{
struct host_if_drv *hif_drv = from_timer(hif_drv, t,
remain_on_ch_timer);
@@ -2382,21 +2372,21 @@ static void ListenTimerCB(struct timer_list *t)
netdev_err(vif->ndev, "wilc_mq_send fail\n");
}
-static void Handle_PowerManagement(struct wilc_vif *vif,
- struct power_mgmt_param *strPowerMgmtParam)
+static void handle_power_management(struct wilc_vif *vif,
+ struct power_mgmt_param *pm_param)
{
s32 result = 0;
struct wid wid;
- s8 s8PowerMode;
+ s8 power_mode;
wid.id = (u16)WID_POWER_MANAGEMENT;
- if (strPowerMgmtParam->enabled)
- s8PowerMode = MIN_FAST_PS;
+ if (pm_param->enabled)
+ power_mode = MIN_FAST_PS;
else
- s8PowerMode = NO_POWERSAVE;
+ power_mode = NO_POWERSAVE;
- wid.val = &s8PowerMode;
+ wid.val = &power_mode;
wid.size = sizeof(char);
result = wilc_send_config_pkt(vif, SET_CFG, &wid, 1,
@@ -2405,41 +2395,41 @@ static void Handle_PowerManagement(struct wilc_vif *vif,
netdev_err(vif->ndev, "Failed to send power management\n");
}
-static void Handle_SetMulticastFilter(struct wilc_vif *vif,
- struct set_multicast *strHostIfSetMulti)
+static void handle_set_mcast_filter(struct wilc_vif *vif,
+ struct set_multicast *hif_set_mc)
{
s32 result = 0;
struct wid wid;
- u8 *pu8CurrByte;
+ u8 *cur_byte;
wid.id = (u16)WID_SETUP_MULTICAST_FILTER;
wid.type = WID_BIN;
- wid.size = sizeof(struct set_multicast) + (strHostIfSetMulti->cnt * ETH_ALEN);
+ wid.size = sizeof(struct set_multicast) + (hif_set_mc->cnt * ETH_ALEN);
wid.val = kmalloc(wid.size, GFP_KERNEL);
if (!wid.val)
- goto ERRORHANDLER;
+ goto error;
- pu8CurrByte = wid.val;
- *pu8CurrByte++ = (strHostIfSetMulti->enabled & 0xFF);
- *pu8CurrByte++ = 0;
- *pu8CurrByte++ = 0;
- *pu8CurrByte++ = 0;
+ cur_byte = wid.val;
+ *cur_byte++ = (hif_set_mc->enabled & 0xFF);
+ *cur_byte++ = 0;
+ *cur_byte++ = 0;
+ *cur_byte++ = 0;
- *pu8CurrByte++ = (strHostIfSetMulti->cnt & 0xFF);
- *pu8CurrByte++ = ((strHostIfSetMulti->cnt >> 8) & 0xFF);
- *pu8CurrByte++ = ((strHostIfSetMulti->cnt >> 16) & 0xFF);
- *pu8CurrByte++ = ((strHostIfSetMulti->cnt >> 24) & 0xFF);
+ *cur_byte++ = (hif_set_mc->cnt & 0xFF);
+ *cur_byte++ = ((hif_set_mc->cnt >> 8) & 0xFF);
+ *cur_byte++ = ((hif_set_mc->cnt >> 16) & 0xFF);
+ *cur_byte++ = ((hif_set_mc->cnt >> 24) & 0xFF);
- if ((strHostIfSetMulti->cnt) > 0)
- memcpy(pu8CurrByte, wilc_multicast_mac_addr_list,
- ((strHostIfSetMulti->cnt) * ETH_ALEN));
+ if (hif_set_mc->cnt > 0)
+ memcpy(cur_byte, wilc_multicast_mac_addr_list,
+ ((hif_set_mc->cnt) * ETH_ALEN));
result = wilc_send_config_pkt(vif, SET_CFG, &wid, 1,
wilc_get_vif_idx(vif));
if (result)
netdev_err(vif->ndev, "Failed to send setup multicast\n");
-ERRORHANDLER:
+error:
kfree(wid.val);
}
@@ -2498,20 +2488,20 @@ static void host_if_work(struct work_struct *work)
break;
case HOST_IF_MSG_CONNECT:
- Handle_Connect(msg->vif, &msg->body.con_info);
+ handle_connect(msg->vif, &msg->body.con_info);
break;
case HOST_IF_MSG_RCVD_NTWRK_INFO:
- Handle_RcvdNtwrkInfo(msg->vif, &msg->body.net_info);
+ handle_rcvd_ntwrk_info(msg->vif, &msg->body.net_info);
break;
case HOST_IF_MSG_RCVD_GNRL_ASYNC_INFO:
- Handle_RcvdGnrlAsyncInfo(msg->vif,
- &msg->body.async_info);
+ handle_rcvd_gnrl_async_info(msg->vif,
+ &msg->body.async_info);
break;
case HOST_IF_MSG_KEY:
- Handle_Key(msg->vif, &msg->body.key_info);
+ handle_key(msg->vif, &msg->body.key_info);
break;
case HOST_IF_MSG_CFG_PARAMS:
@@ -2523,7 +2513,7 @@ static void host_if_work(struct work_struct *work)
break;
case HOST_IF_MSG_DISCONNECT:
- Handle_Disconnect(msg->vif);
+ handle_disconnect(msg->vif);
break;
case HOST_IF_MSG_RCVD_SCAN_COMPLETE:
@@ -2532,58 +2522,58 @@ static void host_if_work(struct work_struct *work)
if (!wilc_wlan_get_num_conn_ifcs(wilc))
wilc_chip_sleep_manually(wilc);
- Handle_ScanDone(msg->vif, SCAN_EVENT_DONE);
+ handle_scan_done(msg->vif, SCAN_EVENT_DONE);
if (msg->vif->hif_drv->remain_on_ch_pending)
- Handle_RemainOnChan(msg->vif,
- &msg->body.remain_on_ch);
+ handle_remain_on_chan(msg->vif,
+ &msg->body.remain_on_ch);
break;
case HOST_IF_MSG_GET_RSSI:
- Handle_GetRssi(msg->vif);
+ handle_get_rssi(msg->vif);
break;
case HOST_IF_MSG_GET_STATISTICS:
- Handle_GetStatistics(msg->vif,
- (struct rf_info *)msg->body.data);
+ handle_get_statistics(msg->vif,
+ (struct rf_info *)msg->body.data);
break;
case HOST_IF_MSG_ADD_BEACON:
- Handle_AddBeacon(msg->vif, &msg->body.beacon_info);
+ handle_add_beacon(msg->vif, &msg->body.beacon_info);
break;
case HOST_IF_MSG_DEL_BEACON:
- Handle_DelBeacon(msg->vif);
+ handle_del_beacon(msg->vif);
break;
case HOST_IF_MSG_ADD_STATION:
- Handle_AddStation(msg->vif, &msg->body.add_sta_info);
+ handle_add_station(msg->vif, &msg->body.add_sta_info);
break;
case HOST_IF_MSG_DEL_STATION:
- Handle_DelStation(msg->vif, &msg->body.del_sta_info);
+ handle_del_station(msg->vif, &msg->body.del_sta_info);
break;
case HOST_IF_MSG_EDIT_STATION:
- Handle_EditStation(msg->vif, &msg->body.edit_sta_info);
+ handle_edit_station(msg->vif, &msg->body.edit_sta_info);
break;
case HOST_IF_MSG_GET_INACTIVETIME:
- Handle_Get_InActiveTime(msg->vif, &msg->body.mac_info);
+ handle_get_inactive_time(msg->vif, &msg->body.mac_info);
break;
case HOST_IF_MSG_SCAN_TIMER_FIRED:
- Handle_ScanDone(msg->vif, SCAN_EVENT_ABORTED);
+ handle_scan_done(msg->vif, SCAN_EVENT_ABORTED);
break;
case HOST_IF_MSG_CONNECT_TIMER_FIRED:
- Handle_ConnectTimeout(msg->vif);
+ handle_connect_timeout(msg->vif);
break;
case HOST_IF_MSG_POWER_MGMT:
- Handle_PowerManagement(msg->vif,
- &msg->body.pwr_mgmt_info);
+ handle_power_management(msg->vif,
+ &msg->body.pwr_mgmt_info);
break;
case HOST_IF_MSG_SET_WFIDRV_HANDLER:
@@ -2610,23 +2600,23 @@ static void host_if_work(struct work_struct *work)
break;
case HOST_IF_MSG_REMAIN_ON_CHAN:
- Handle_RemainOnChan(msg->vif, &msg->body.remain_on_ch);
+ handle_remain_on_chan(msg->vif, &msg->body.remain_on_ch);
break;
case HOST_IF_MSG_REGISTER_FRAME:
- Handle_RegisterFrame(msg->vif, &msg->body.reg_frame);
+ handle_register_frame(msg->vif, &msg->body.reg_frame);
break;
case HOST_IF_MSG_LISTEN_TIMER_FIRED:
- Handle_ListenStateExpired(msg->vif, &msg->body.remain_on_ch);
+ handle_listen_state_expired(msg->vif, &msg->body.remain_on_ch);
break;
case HOST_IF_MSG_SET_MULTICAST_FILTER:
- Handle_SetMulticastFilter(msg->vif, &msg->body.multicast_info);
+ handle_set_mcast_filter(msg->vif, &msg->body.multicast_info);
break;
case HOST_IF_MSG_DEL_ALL_STA:
- Handle_DelAllSta(msg->vif, &msg->body.del_all_sta_info);
+ handle_del_all_sta(msg->vif, &msg->body.del_all_sta_info);
break;
case HOST_IF_MSG_SET_TX_POWER:
@@ -2647,7 +2637,7 @@ free_msg:
complete(&hif_thread_comp);
}
-static void TimerCB_Scan(struct timer_list *t)
+static void timer_scan_cb(struct timer_list *t)
{
struct host_if_drv *hif_drv = from_timer(hif_drv, t, scan_timer);
struct wilc_vif *vif = hif_drv->scan_timer_vif;
@@ -2660,7 +2650,7 @@ static void TimerCB_Scan(struct timer_list *t)
wilc_enqueue_cmd(&msg);
}
-static void TimerCB_Connect(struct timer_list *t)
+static void timer_connect_cb(struct timer_list *t)
{
struct host_if_drv *hif_drv = from_timer(hif_drv, t,
connect_timer);
@@ -2674,13 +2664,13 @@ static void TimerCB_Connect(struct timer_list *t)
wilc_enqueue_cmd(&msg);
}
-s32 wilc_remove_key(struct host_if_drv *hif_drv, const u8 *pu8StaAddress)
+s32 wilc_remove_key(struct host_if_drv *hif_drv, const u8 *sta_addr)
{
struct wid wid;
wid.id = (u16)WID_REMOVE_KEY;
wid.type = WID_STR;
- wid.val = (s8 *)pu8StaAddress;
+ wid.val = (s8 *)sta_addr;
wid.size = 6;
return 0;
@@ -2747,7 +2737,7 @@ int wilc_set_wep_default_keyid(struct wilc_vif *vif, u8 index)
int wilc_add_wep_key_bss_sta(struct wilc_vif *vif, const u8 *key, u8 len,
u8 index)
{
- int result = 0;
+ int result;
struct host_if_msg msg;
struct host_if_drv *hif_drv = vif->hif_drv;
@@ -2770,17 +2760,20 @@ int wilc_add_wep_key_bss_sta(struct wilc_vif *vif, const u8 *key, u8 len,
msg.body.key_info.attr.wep.index = index;
result = wilc_enqueue_cmd(&msg);
- if (result)
+ if (result) {
netdev_err(vif->ndev, "STA - WEP Key\n");
- wait_for_completion(&hif_drv->comp_test_key_block);
+ kfree(msg.body.key_info.attr.wep.key);
+ return result;
+ }
- return result;
+ wait_for_completion(&hif_drv->comp_test_key_block);
+ return 0;
}
int wilc_add_wep_key_bss_ap(struct wilc_vif *vif, const u8 *key, u8 len,
u8 index, u8 mode, enum AUTHTYPE auth_type)
{
- int result = 0;
+ int result;
struct host_if_msg msg;
struct host_if_drv *hif_drv = vif->hif_drv;
@@ -2805,20 +2798,21 @@ int wilc_add_wep_key_bss_ap(struct wilc_vif *vif, const u8 *key, u8 len,
msg.body.key_info.attr.wep.auth_type = auth_type;
result = wilc_enqueue_cmd(&msg);
-
- if (result)
+ if (result) {
netdev_err(vif->ndev, "AP - WEP Key\n");
- else
- wait_for_completion(&hif_drv->comp_test_key_block);
+ kfree(msg.body.key_info.attr.wep.key);
+ return result;
+ }
- return result;
+ wait_for_completion(&hif_drv->comp_test_key_block);
+ return 0;
}
int wilc_add_ptk(struct wilc_vif *vif, const u8 *ptk, u8 ptk_key_len,
const u8 *mac_addr, const u8 *rx_mic, const u8 *tx_mic,
u8 mode, u8 cipher_mode, u8 index)
{
- int result = 0;
+ int result;
struct host_if_msg msg;
struct host_if_drv *hif_drv = vif->hif_drv;
u8 key_len = ptk_key_len;
@@ -2850,10 +2844,12 @@ int wilc_add_ptk(struct wilc_vif *vif, const u8 *ptk, u8 ptk_key_len,
return -ENOMEM;
if (rx_mic)
- memcpy(msg.body.key_info.attr.wpa.key + 16, rx_mic, RX_MIC_KEY_LEN);
+ memcpy(msg.body.key_info.attr.wpa.key + 16, rx_mic,
+ RX_MIC_KEY_LEN);
if (tx_mic)
- memcpy(msg.body.key_info.attr.wpa.key + 24, tx_mic, TX_MIC_KEY_LEN);
+ memcpy(msg.body.key_info.attr.wpa.key + 24, tx_mic,
+ TX_MIC_KEY_LEN);
msg.body.key_info.attr.wpa.key_len = key_len;
msg.body.key_info.attr.wpa.mac_addr = mac_addr;
@@ -2861,13 +2857,14 @@ int wilc_add_ptk(struct wilc_vif *vif, const u8 *ptk, u8 ptk_key_len,
msg.vif = vif;
result = wilc_enqueue_cmd(&msg);
-
- if (result)
+ if (result) {
netdev_err(vif->ndev, "PTK Key\n");
- else
- wait_for_completion(&hif_drv->comp_test_key_block);
+ kfree(msg.body.key_info.attr.wpa.key);
+ return result;
+ }
- return result;
+ wait_for_completion(&hif_drv->comp_test_key_block);
+ return 0;
}
int wilc_add_rx_gtk(struct wilc_vif *vif, const u8 *rx_gtk, u8 gtk_key_len,
@@ -2875,7 +2872,7 @@ int wilc_add_rx_gtk(struct wilc_vif *vif, const u8 *rx_gtk, u8 gtk_key_len,
const u8 *rx_mic, const u8 *tx_mic, u8 mode,
u8 cipher_mode)
{
- int result = 0;
+ int result;
struct host_if_msg msg;
struct host_if_drv *hif_drv = vif->hif_drv;
u8 key_len = gtk_key_len;
@@ -2914,8 +2911,10 @@ int wilc_add_rx_gtk(struct wilc_vif *vif, const u8 *rx_gtk, u8 gtk_key_len,
msg.body.key_info.attr.wpa.key = kmemdup(rx_gtk,
key_len,
GFP_KERNEL);
- if (!msg.body.key_info.attr.wpa.key)
+ if (!msg.body.key_info.attr.wpa.key) {
+ kfree(msg.body.key_info.attr.wpa.seq);
return -ENOMEM;
+ }
if (rx_mic)
memcpy(msg.body.key_info.attr.wpa.key + 16, rx_mic,
@@ -2930,12 +2929,15 @@ int wilc_add_rx_gtk(struct wilc_vif *vif, const u8 *rx_gtk, u8 gtk_key_len,
msg.body.key_info.attr.wpa.seq_len = key_rsc_len;
result = wilc_enqueue_cmd(&msg);
- if (result)
+ if (result) {
netdev_err(vif->ndev, "RX GTK\n");
- else
- wait_for_completion(&hif_drv->comp_test_key_block);
+ kfree(msg.body.key_info.attr.wpa.seq);
+ kfree(msg.body.key_info.attr.wpa.key);
+ return result;
+ }
- return result;
+ wait_for_completion(&hif_drv->comp_test_key_block);
+ return 0;
}
int wilc_set_pmkid_info(struct wilc_vif *vif,
@@ -3080,27 +3082,27 @@ int wilc_disconnect(struct wilc_vif *vif, u16 reason_code)
}
static s32 host_int_get_assoc_res_info(struct wilc_vif *vif,
- u8 *pu8AssocRespInfo,
- u32 u32MaxAssocRespInfoLen,
- u32 *pu32RcvdAssocRespInfoLen)
+ u8 *assoc_resp_info,
+ u32 max_assoc_resp_info_len,
+ u32 *rcvd_assoc_resp_info_len)
{
s32 result = 0;
struct wid wid;
wid.id = (u16)WID_ASSOC_RES_INFO;
wid.type = WID_STR;
- wid.val = pu8AssocRespInfo;
- wid.size = u32MaxAssocRespInfoLen;
+ wid.val = assoc_resp_info;
+ wid.size = max_assoc_resp_info_len;
result = wilc_send_config_pkt(vif, GET_CFG, &wid, 1,
wilc_get_vif_idx(vif));
if (result) {
- *pu32RcvdAssocRespInfoLen = 0;
+ *rcvd_assoc_resp_info_len = 0;
netdev_err(vif->ndev, "Failed to send association response\n");
return -EINVAL;
}
- *pu32RcvdAssocRespInfoLen = wid.size;
+ *rcvd_assoc_resp_info_len = wid.size;
return result;
}
@@ -3165,7 +3167,7 @@ int wilc_set_operation_mode(struct wilc_vif *vif, u32 mode)
}
s32 wilc_get_inactive_time(struct wilc_vif *vif, const u8 *mac,
- u32 *pu32InactiveTime)
+ u32 *out_val)
{
s32 result = 0;
struct host_if_msg msg;
@@ -3188,7 +3190,7 @@ s32 wilc_get_inactive_time(struct wilc_vif *vif, const u8 *mac,
else
wait_for_completion(&hif_drv->comp_inactive_time);
- *pu32InactiveTime = inactive_time;
+ *out_val = inactive_time;
return result;
}
@@ -3316,7 +3318,7 @@ int wilc_hif_set_cfg(struct wilc_vif *vif,
return wilc_enqueue_cmd(&msg);
}
-static void GetPeriodicRSSI(struct timer_list *unused)
+static void get_periodic_rssi(struct timer_list *unused)
{
struct wilc_vif *vif = periodic_rssi_vif;
@@ -3381,13 +3383,13 @@ int wilc_init(struct net_device *dev, struct host_if_drv **hif_drv_handler)
}
periodic_rssi_vif = vif;
- timer_setup(&periodic_rssi, GetPeriodicRSSI, 0);
+ timer_setup(&periodic_rssi, get_periodic_rssi, 0);
mod_timer(&periodic_rssi, jiffies + msecs_to_jiffies(5000));
}
- timer_setup(&hif_drv->scan_timer, TimerCB_Scan, 0);
- timer_setup(&hif_drv->connect_timer, TimerCB_Connect, 0);
- timer_setup(&hif_drv->remain_on_ch_timer, ListenTimerCB, 0);
+ timer_setup(&hif_drv->scan_timer, timer_scan_cb, 0);
+ timer_setup(&hif_drv->connect_timer, timer_connect_cb, 0);
+ timer_setup(&hif_drv->remain_on_ch_timer, listen_timer_cb, 0);
mutex_init(&hif_drv->cfg_values_lock);
mutex_lock(&hif_drv->cfg_values_lock);
@@ -3434,7 +3436,8 @@ int wilc_deinit(struct wilc_vif *vif)
if (hif_drv->usr_scan_req.scan_result) {
hif_drv->usr_scan_req.scan_result(SCAN_EVENT_ABORTED, NULL,
- hif_drv->usr_scan_req.arg, NULL);
+ hif_drv->usr_scan_req.arg,
+ NULL);
hif_drv->usr_scan_req.scan_result = NULL;
}
@@ -3473,7 +3476,10 @@ void wilc_network_info_received(struct wilc *wilc, u8 *buffer, u32 length)
struct host_if_drv *hif_drv = NULL;
struct wilc_vif *vif;
- id = ((buffer[length - 4]) | (buffer[length - 3] << 8) | (buffer[length - 2] << 16) | (buffer[length - 1] << 24));
+ id = buffer[length - 4];
+ id |= (buffer[length - 3] << 8);
+ id |= (buffer[length - 2] << 16);
+ id |= (buffer[length - 1] << 24);
vif = wilc_get_vif_from_idx(wilc, id);
if (!vif)
return;
@@ -3490,12 +3496,15 @@ void wilc_network_info_received(struct wilc *wilc, u8 *buffer, u32 length)
msg.vif = vif;
msg.body.net_info.len = length;
- msg.body.net_info.buffer = kmalloc(length, GFP_KERNEL);
- memcpy(msg.body.net_info.buffer, buffer, length);
+ msg.body.net_info.buffer = kmemdup(buffer, length, GFP_KERNEL);
+ if (!msg.body.net_info.buffer)
+ return;
result = wilc_enqueue_cmd(&msg);
- if (result)
+ if (result) {
netdev_err(vif->ndev, "message parameters (%d)\n", result);
+ kfree(msg.body.net_info.buffer);
+ }
}
void wilc_gnrl_async_info_received(struct wilc *wilc, u8 *buffer, u32 length)
@@ -3508,7 +3517,10 @@ void wilc_gnrl_async_info_received(struct wilc *wilc, u8 *buffer, u32 length)
mutex_lock(&hif_deinit_lock);
- id = ((buffer[length - 4]) | (buffer[length - 3] << 8) | (buffer[length - 2] << 16) | (buffer[length - 1] << 24));
+ id = buffer[length - 4];
+ id |= (buffer[length - 3] << 8);
+ id |= (buffer[length - 2] << 16);
+ id |= (buffer[length - 1] << 24);
vif = wilc_get_vif_from_idx(wilc, id);
if (!vif) {
mutex_unlock(&hif_deinit_lock);
@@ -3534,12 +3546,17 @@ void wilc_gnrl_async_info_received(struct wilc *wilc, u8 *buffer, u32 length)
msg.vif = vif;
msg.body.async_info.len = length;
- msg.body.async_info.buffer = kmalloc(length, GFP_KERNEL);
- memcpy(msg.body.async_info.buffer, buffer, length);
+ msg.body.async_info.buffer = kmemdup(buffer, length, GFP_KERNEL);
+ if (!msg.body.async_info.buffer) {
+ mutex_unlock(&hif_deinit_lock);
+ return;
+ }
result = wilc_enqueue_cmd(&msg);
- if (result)
+ if (result) {
netdev_err(vif->ndev, "synchronous info (%d)\n", result);
+ kfree(msg.body.async_info.buffer);
+ }
mutex_unlock(&hif_deinit_lock);
}
@@ -3552,7 +3569,10 @@ void wilc_scan_complete_received(struct wilc *wilc, u8 *buffer, u32 length)
struct host_if_drv *hif_drv = NULL;
struct wilc_vif *vif;
- id = ((buffer[length - 4]) | (buffer[length - 3] << 8) | (buffer[length - 2] << 16) | (buffer[length - 1] << 24));
+ id = buffer[length - 4];
+ id |= buffer[length - 3] << 8;
+ id |= buffer[length - 2] << 16;
+ id |= buffer[length - 1] << 24;
vif = wilc_get_vif_from_idx(wilc, id);
if (!vif)
return;
@@ -3673,7 +3693,7 @@ int wilc_add_beacon(struct wilc_vif *vif, u32 interval, u32 dtim_period,
beacon_info->head = kmemdup(head, head_len, GFP_KERNEL);
if (!beacon_info->head) {
result = -ENOMEM;
- goto ERRORHANDLER;
+ goto error;
}
beacon_info->tail_len = tail_len;
@@ -3681,7 +3701,7 @@ int wilc_add_beacon(struct wilc_vif *vif, u32 interval, u32 dtim_period,
beacon_info->tail = kmemdup(tail, tail_len, GFP_KERNEL);
if (!beacon_info->tail) {
result = -ENOMEM;
- goto ERRORHANDLER;
+ goto error;
}
} else {
beacon_info->tail = NULL;
@@ -3691,7 +3711,7 @@ int wilc_add_beacon(struct wilc_vif *vif, u32 interval, u32 dtim_period,
if (result)
netdev_err(vif->ndev, "wilc mq send fail\n");
-ERRORHANDLER:
+error:
if (result) {
kfree(beacon_info->head);
@@ -3737,8 +3757,10 @@ int wilc_add_station(struct wilc_vif *vif, struct add_sta_param *sta_param)
}
result = wilc_enqueue_cmd(&msg);
- if (result)
+ if (result) {
netdev_err(vif->ndev, "wilc_mq_send fail\n");
+ kfree(add_sta_info->rates);
+ }
return result;
}
@@ -3780,7 +3802,8 @@ int wilc_del_allstation(struct wilc_vif *vif, u8 mac_addr[][ETH_ALEN])
for (i = 0; i < MAX_NUM_STA; i++) {
if (memcmp(mac_addr[i], zero_addr, ETH_ALEN)) {
- memcpy(del_all_sta_info->del_all_sta[i], mac_addr[i], ETH_ALEN);
+ memcpy(del_all_sta_info->del_all_sta[i], mac_addr[i],
+ ETH_ALEN);
assoc_sta++;
}
}
@@ -3820,8 +3843,10 @@ int wilc_edit_station(struct wilc_vif *vif,
}
result = wilc_enqueue_cmd(&msg);
- if (result)
+ if (result) {
netdev_err(vif->ndev, "wilc_mq_send fail\n");
+ kfree(add_sta_info->rates);
+ }
return result;
}
@@ -3870,152 +3895,152 @@ int wilc_setup_multicast_filter(struct wilc_vif *vif, bool enabled,
return result;
}
-static void *host_int_ParseJoinBssParam(struct network_info *ptstrNetworkInfo)
+static void *host_int_parse_join_bss_param(struct network_info *info)
{
- struct join_bss_param *pNewJoinBssParam = NULL;
- u8 *pu8IEs;
- u16 u16IEsLen;
+ struct join_bss_param *param = NULL;
+ u8 *ies;
+ u16 ies_len;
u16 index = 0;
- u8 suppRatesNo = 0;
- u8 extSuppRatesNo;
- u16 jumpOffset;
- u8 pcipherCount;
- u8 authCount;
- u8 pcipherTotalCount = 0;
- u8 authTotalCount = 0;
+ u8 rates_no = 0;
+ u8 ext_rates_no;
+ u16 offset;
+ u8 pcipher_cnt;
+ u8 auth_cnt;
+ u8 pcipher_total_cnt = 0;
+ u8 auth_total_cnt = 0;
u8 i, j;
- pu8IEs = ptstrNetworkInfo->ies;
- u16IEsLen = ptstrNetworkInfo->ies_len;
-
- pNewJoinBssParam = kzalloc(sizeof(*pNewJoinBssParam), GFP_KERNEL);
- if (pNewJoinBssParam) {
- pNewJoinBssParam->dtim_period = ptstrNetworkInfo->dtim_period;
- pNewJoinBssParam->beacon_period = ptstrNetworkInfo->beacon_period;
- pNewJoinBssParam->cap_info = ptstrNetworkInfo->cap_info;
- memcpy(pNewJoinBssParam->bssid, ptstrNetworkInfo->bssid, 6);
- memcpy((u8 *)pNewJoinBssParam->ssid, ptstrNetworkInfo->ssid,
- ptstrNetworkInfo->ssid_len + 1);
- pNewJoinBssParam->ssid_len = ptstrNetworkInfo->ssid_len;
- memset(pNewJoinBssParam->rsn_pcip_policy, 0xFF, 3);
- memset(pNewJoinBssParam->rsn_auth_policy, 0xFF, 3);
-
- while (index < u16IEsLen) {
- if (pu8IEs[index] == SUPP_RATES_IE) {
- suppRatesNo = pu8IEs[index + 1];
- pNewJoinBssParam->supp_rates[0] = suppRatesNo;
- index += 2;
-
- for (i = 0; i < suppRatesNo; i++)
- pNewJoinBssParam->supp_rates[i + 1] = pu8IEs[index + i];
-
- index += suppRatesNo;
- } else if (pu8IEs[index] == EXT_SUPP_RATES_IE) {
- extSuppRatesNo = pu8IEs[index + 1];
- if (extSuppRatesNo > (MAX_RATES_SUPPORTED - suppRatesNo))
- pNewJoinBssParam->supp_rates[0] = MAX_RATES_SUPPORTED;
- else
- pNewJoinBssParam->supp_rates[0] += extSuppRatesNo;
- index += 2;
- for (i = 0; i < (pNewJoinBssParam->supp_rates[0] - suppRatesNo); i++)
- pNewJoinBssParam->supp_rates[suppRatesNo + i + 1] = pu8IEs[index + i];
-
- index += extSuppRatesNo;
- } else if (pu8IEs[index] == HT_CAPABILITY_IE) {
- pNewJoinBssParam->ht_capable = true;
- index += pu8IEs[index + 1] + 2;
- } else if ((pu8IEs[index] == WMM_IE) &&
- (pu8IEs[index + 2] == 0x00) && (pu8IEs[index + 3] == 0x50) &&
- (pu8IEs[index + 4] == 0xF2) &&
- (pu8IEs[index + 5] == 0x02) &&
- ((pu8IEs[index + 6] == 0x00) || (pu8IEs[index + 6] == 0x01)) &&
- (pu8IEs[index + 7] == 0x01)) {
- pNewJoinBssParam->wmm_cap = true;
-
- if (pu8IEs[index + 8] & BIT(7))
- pNewJoinBssParam->uapsd_cap = true;
- index += pu8IEs[index + 1] + 2;
- } else if ((pu8IEs[index] == P2P_IE) &&
- (pu8IEs[index + 2] == 0x50) && (pu8IEs[index + 3] == 0x6f) &&
- (pu8IEs[index + 4] == 0x9a) &&
- (pu8IEs[index + 5] == 0x09) && (pu8IEs[index + 6] == 0x0c)) {
- u16 u16P2P_count;
-
- pNewJoinBssParam->tsf = ptstrNetworkInfo->tsf_lo;
- pNewJoinBssParam->noa_enabled = 1;
- pNewJoinBssParam->idx = pu8IEs[index + 9];
-
- if (pu8IEs[index + 10] & BIT(7)) {
- pNewJoinBssParam->opp_enabled = 1;
- pNewJoinBssParam->ct_window = pu8IEs[index + 10];
- } else {
- pNewJoinBssParam->opp_enabled = 0;
- }
+ ies = info->ies;
+ ies_len = info->ies_len;
- pNewJoinBssParam->cnt = pu8IEs[index + 11];
- u16P2P_count = index + 12;
+ param = kzalloc(sizeof(*param), GFP_KERNEL);
+ if (!param)
+ return NULL;
- memcpy(pNewJoinBssParam->duration, pu8IEs + u16P2P_count, 4);
- u16P2P_count += 4;
+ param->dtim_period = info->dtim_period;
+ param->beacon_period = info->beacon_period;
+ param->cap_info = info->cap_info;
+ memcpy(param->bssid, info->bssid, 6);
+ memcpy((u8 *)param->ssid, info->ssid, info->ssid_len + 1);
+ param->ssid_len = info->ssid_len;
+ memset(param->rsn_pcip_policy, 0xFF, 3);
+ memset(param->rsn_auth_policy, 0xFF, 3);
+
+ while (index < ies_len) {
+ if (ies[index] == SUPP_RATES_IE) {
+ rates_no = ies[index + 1];
+ param->supp_rates[0] = rates_no;
+ index += 2;
+
+ for (i = 0; i < rates_no; i++)
+ param->supp_rates[i + 1] = ies[index + i];
+
+ index += rates_no;
+ } else if (ies[index] == EXT_SUPP_RATES_IE) {
+ ext_rates_no = ies[index + 1];
+ if (ext_rates_no > (MAX_RATES_SUPPORTED - rates_no))
+ param->supp_rates[0] = MAX_RATES_SUPPORTED;
+ else
+ param->supp_rates[0] += ext_rates_no;
+ index += 2;
+ for (i = 0; i < (param->supp_rates[0] - rates_no); i++)
+ param->supp_rates[rates_no + i + 1] = ies[index + i];
+
+ index += ext_rates_no;
+ } else if (ies[index] == HT_CAPABILITY_IE) {
+ param->ht_capable = true;
+ index += ies[index + 1] + 2;
+ } else if ((ies[index] == WMM_IE) &&
+ (ies[index + 2] == 0x00) && (ies[index + 3] == 0x50) &&
+ (ies[index + 4] == 0xF2) &&
+ (ies[index + 5] == 0x02) &&
+ ((ies[index + 6] == 0x00) || (ies[index + 6] == 0x01)) &&
+ (ies[index + 7] == 0x01)) {
+ param->wmm_cap = true;
+
+ if (ies[index + 8] & BIT(7))
+ param->uapsd_cap = true;
+ index += ies[index + 1] + 2;
+ } else if ((ies[index] == P2P_IE) &&
+ (ies[index + 2] == 0x50) && (ies[index + 3] == 0x6f) &&
+ (ies[index + 4] == 0x9a) &&
+ (ies[index + 5] == 0x09) && (ies[index + 6] == 0x0c)) {
+ u16 p2p_cnt;
+
+ param->tsf = info->tsf_lo;
+ param->noa_enabled = 1;
+ param->idx = ies[index + 9];
+
+ if (ies[index + 10] & BIT(7)) {
+ param->opp_enabled = 1;
+ param->ct_window = ies[index + 10];
+ } else {
+ param->opp_enabled = 0;
+ }
- memcpy(pNewJoinBssParam->interval, pu8IEs + u16P2P_count, 4);
- u16P2P_count += 4;
+ param->cnt = ies[index + 11];
+ p2p_cnt = index + 12;
- memcpy(pNewJoinBssParam->start_time, pu8IEs + u16P2P_count, 4);
+ memcpy(param->duration, ies + p2p_cnt, 4);
+ p2p_cnt += 4;
- index += pu8IEs[index + 1] + 2;
- } else if ((pu8IEs[index] == RSN_IE) ||
- ((pu8IEs[index] == WPA_IE) && (pu8IEs[index + 2] == 0x00) &&
- (pu8IEs[index + 3] == 0x50) && (pu8IEs[index + 4] == 0xF2) &&
- (pu8IEs[index + 5] == 0x01))) {
- u16 rsnIndex = index;
+ memcpy(param->interval, ies + p2p_cnt, 4);
+ p2p_cnt += 4;
- if (pu8IEs[rsnIndex] == RSN_IE) {
- pNewJoinBssParam->mode_802_11i = 2;
- } else {
- if (pNewJoinBssParam->mode_802_11i == 0)
- pNewJoinBssParam->mode_802_11i = 1;
- rsnIndex += 4;
- }
+ memcpy(param->start_time, ies + p2p_cnt, 4);
- rsnIndex += 7;
- pNewJoinBssParam->rsn_grp_policy = pu8IEs[rsnIndex];
- rsnIndex++;
- jumpOffset = pu8IEs[rsnIndex] * 4;
- pcipherCount = (pu8IEs[rsnIndex] > 3) ? 3 : pu8IEs[rsnIndex];
- rsnIndex += 2;
+ index += ies[index + 1] + 2;
+ } else if ((ies[index] == RSN_IE) ||
+ ((ies[index] == WPA_IE) && (ies[index + 2] == 0x00) &&
+ (ies[index + 3] == 0x50) && (ies[index + 4] == 0xF2) &&
+ (ies[index + 5] == 0x01))) {
+ u16 rsn_idx = index;
- for (i = pcipherTotalCount, j = 0; i < pcipherCount + pcipherTotalCount && i < 3; i++, j++)
- pNewJoinBssParam->rsn_pcip_policy[i] = pu8IEs[rsnIndex + ((j + 1) * 4) - 1];
+ if (ies[rsn_idx] == RSN_IE) {
+ param->mode_802_11i = 2;
+ } else {
+ if (param->mode_802_11i == 0)
+ param->mode_802_11i = 1;
+ rsn_idx += 4;
+ }
- pcipherTotalCount += pcipherCount;
- rsnIndex += jumpOffset;
+ rsn_idx += 7;
+ param->rsn_grp_policy = ies[rsn_idx];
+ rsn_idx++;
+ offset = ies[rsn_idx] * 4;
+ pcipher_cnt = (ies[rsn_idx] > 3) ? 3 : ies[rsn_idx];
+ rsn_idx += 2;
- jumpOffset = pu8IEs[rsnIndex] * 4;
+ for (i = pcipher_total_cnt, j = 0; i < pcipher_cnt + pcipher_total_cnt && i < 3; i++, j++)
+ param->rsn_pcip_policy[i] = ies[rsn_idx + ((j + 1) * 4) - 1];
- authCount = (pu8IEs[rsnIndex] > 3) ? 3 : pu8IEs[rsnIndex];
- rsnIndex += 2;
+ pcipher_total_cnt += pcipher_cnt;
+ rsn_idx += offset;
- for (i = authTotalCount, j = 0; i < authTotalCount + authCount; i++, j++)
- pNewJoinBssParam->rsn_auth_policy[i] = pu8IEs[rsnIndex + ((j + 1) * 4) - 1];
+ offset = ies[rsn_idx] * 4;
- authTotalCount += authCount;
- rsnIndex += jumpOffset;
+ auth_cnt = (ies[rsn_idx] > 3) ? 3 : ies[rsn_idx];
+ rsn_idx += 2;
- if (pu8IEs[index] == RSN_IE) {
- pNewJoinBssParam->rsn_cap[0] = pu8IEs[rsnIndex];
- pNewJoinBssParam->rsn_cap[1] = pu8IEs[rsnIndex + 1];
- rsnIndex += 2;
- }
- pNewJoinBssParam->rsn_found = true;
- index += pu8IEs[index + 1] + 2;
- } else {
- index += pu8IEs[index + 1] + 2;
+ for (i = auth_total_cnt, j = 0; i < auth_total_cnt + auth_cnt; i++, j++)
+ param->rsn_auth_policy[i] = ies[rsn_idx + ((j + 1) * 4) - 1];
+
+ auth_total_cnt += auth_cnt;
+ rsn_idx += offset;
+
+ if (ies[index] == RSN_IE) {
+ param->rsn_cap[0] = ies[rsn_idx];
+ param->rsn_cap[1] = ies[rsn_idx + 1];
+ rsn_idx += 2;
}
+ param->rsn_found = true;
+ index += ies[index + 1] + 2;
+ } else {
+ index += ies[index + 1] + 2;
}
}
- return (void *)pNewJoinBssParam;
+ return (void *)param;
}
int wilc_setup_ipaddress(struct wilc_vif *vif, u8 *ip_addr, u8 idx)
diff --git a/drivers/staging/wilc1000/host_interface.h b/drivers/staging/wilc1000/host_interface.h
index aa914d69ab0d..4b60b1822e91 100644
--- a/drivers/staging/wilc1000/host_interface.h
+++ b/drivers/staging/wilc1000/host_interface.h
@@ -304,7 +304,7 @@ struct add_sta_param {
};
struct wilc_vif;
-s32 wilc_remove_key(struct host_if_drv *hWFIDrv, const u8 *pu8StaAddress);
+s32 wilc_remove_key(struct host_if_drv *hif_drv, const u8 *sta_addr);
int wilc_remove_wep_key(struct wilc_vif *vif, u8 index);
int wilc_set_wep_default_keyid(struct wilc_vif *vif, u8 index);
int wilc_add_wep_key_bss_sta(struct wilc_vif *vif, const u8 *key, u8 len,
diff --git a/drivers/staging/wilc1000/linux_mon.c b/drivers/staging/wilc1000/linux_mon.c
index 91d49c4738dc..169213f24faf 100644
--- a/drivers/staging/wilc1000/linux_mon.c
+++ b/drivers/staging/wilc1000/linux_mon.c
@@ -40,9 +40,6 @@ static u8 broadcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
#define IEEE80211_RADIOTAP_F_TX_RTS 0x0004 /* used rts/cts handshake */
#define IEEE80211_RADIOTAP_F_TX_FAIL 0x0001 /* failed due to excessive*/
-#define IS_MANAGMEMENT 0x100
-#define IS_MANAGMEMENT_CALLBACK 0x080
-#define IS_MGMT_STATUS_SUCCES 0x040
#define GET_PKT_OFFSET(a) (((a) >> 22) & 0x1ff)
void WILC_WFI_monitor_rx(u8 *buff, u32 size)
@@ -150,7 +147,7 @@ static int mon_mgmt_tx(struct net_device *dev, const u8 *buf, size_t len)
if (!mgmt_tx)
return -ENOMEM;
- mgmt_tx->buff = kmalloc(len, GFP_ATOMIC);
+ mgmt_tx->buff = kmemdup(buf, len, GFP_ATOMIC);
if (!mgmt_tx->buff) {
kfree(mgmt_tx);
return -ENOMEM;
@@ -158,7 +155,6 @@ static int mon_mgmt_tx(struct net_device *dev, const u8 *buf, size_t len)
mgmt_tx->size = len;
- memcpy(mgmt_tx->buff, buf, len);
wilc_wlan_txq_add_mgmt_pkt(dev, mgmt_tx, mgmt_tx->buff, mgmt_tx->size,
mgmt_tx_complete);
@@ -256,7 +252,7 @@ static const struct net_device_ops wilc_wfi_netdev_ops = {
* @brief WILC_WFI_init_mon_interface
* @details
* @param[in]
- * @return int : Return 0 on Success
+ * @return Pointer to net_device
* @author mdaftedar
* @date 12 JUL 2012
* @version 1.0
@@ -264,7 +260,6 @@ static const struct net_device_ops wilc_wfi_netdev_ops = {
struct net_device *WILC_WFI_init_mon_interface(const char *name,
struct net_device *real_dev)
{
- u32 ret = 0;
struct WILC_WFI_mon_priv *priv;
/*If monitor interface is already initialized, return it*/
@@ -279,8 +274,7 @@ struct net_device *WILC_WFI_init_mon_interface(const char *name,
wilc_wfi_mon->name[IFNAMSIZ - 1] = 0;
wilc_wfi_mon->netdev_ops = &wilc_wfi_netdev_ops;
- ret = register_netdevice(wilc_wfi_mon);
- if (ret) {
+ if (register_netdevice(wilc_wfi_mon)) {
netdev_err(real_dev, "register_netdevice failed\n");
return NULL;
}
diff --git a/drivers/staging/wilc1000/linux_wlan.c b/drivers/staging/wilc1000/linux_wlan.c
index d9725efe0537..38a83bd31671 100644
--- a/drivers/staging/wilc1000/linux_wlan.c
+++ b/drivers/staging/wilc1000/linux_wlan.c
@@ -628,8 +628,7 @@ void wilc1000_wlan_deinit(struct net_device *dev)
wl->hif_func->disable_interrupt(wl);
mutex_unlock(&wl->hif_cs);
}
- if (&wl->txq_event)
- complete(&wl->txq_event);
+ complete(&wl->txq_event);
wlan_deinitialize_threads(dev);
deinit_irq(dev);
@@ -677,11 +676,9 @@ static int wlan_deinit_locks(struct net_device *dev)
vif = netdev_priv(dev);
wilc = vif->wilc;
- if (&wilc->hif_cs)
- mutex_destroy(&wilc->hif_cs);
-
- if (&wilc->rxq_cs)
- mutex_destroy(&wilc->rxq_cs);
+ mutex_destroy(&wilc->hif_cs);
+ mutex_destroy(&wilc->rxq_cs);
+ mutex_destroy(&wilc->txq_add_to_head_cs);
return 0;
}
@@ -716,8 +713,7 @@ static void wlan_deinitialize_threads(struct net_device *dev)
wl->close = 1;
- if (&wl->txq_event)
- complete(&wl->txq_event);
+ complete(&wl->txq_event);
if (wl->txq_thread) {
kthread_stop(wl->txq_thread);
@@ -866,10 +862,10 @@ static int wilc_mac_open(struct net_device *ndev)
break;
}
}
- wilc_get_mac_address(vif, mac_add);
- netdev_dbg(ndev, "Mac address: %pM\n", mac_add);
- memcpy(wl->vif[i]->src_addr, mac_add, ETH_ALEN);
+ wilc_get_mac_address(vif, mac_add);
+ netdev_dbg(ndev, "Mac address: %pM\n", mac_add);
+ memcpy(wl->vif[i]->src_addr, mac_add, ETH_ALEN);
memcpy(ndev->dev_addr, wl->vif[i]->src_addr, ETH_ALEN);
if (!is_valid_ether_addr(ndev->dev_addr)) {
@@ -1154,7 +1150,7 @@ void wilc_frmw_to_linux(struct wilc *wilc, u8 *buff, u32 size, u32 pkt_offset)
}
}
-void WILC_WFI_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size)
+void wilc_wfi_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size)
{
int i = 0;
struct wilc_vif *vif;
diff --git a/drivers/staging/wilc1000/wilc_sdio.c b/drivers/staging/wilc1000/wilc_sdio.c
index bb65b374c1ce..a08899941491 100644
--- a/drivers/staging/wilc1000/wilc_sdio.c
+++ b/drivers/staging/wilc1000/wilc_sdio.c
@@ -405,7 +405,7 @@ static int sdio_write_reg(struct wilc *wilc, u32 addr, u32 data)
cmd.increment = 1;
cmd.count = 4;
cmd.buffer = (u8 *)&data;
- cmd.block_size = g_sdio.block_size; /* johnny : prevent it from setting unexpected value */
+ cmd.block_size = g_sdio.block_size;
ret = wilc_sdio_cmd53(wilc, &cmd);
if (ret) {
dev_err(&func->dev,
@@ -489,7 +489,7 @@ static int sdio_write(struct wilc *wilc, u32 addr, u8 *buf, u32 size)
cmd.count = nleft;
cmd.buffer = buf;
- cmd.block_size = block_size; /* johnny : prevent it from setting unexpected value */
+ cmd.block_size = block_size;
if (addr > 0) {
if (!sdio_set_func0_csa_address(wilc, addr))
@@ -543,7 +543,7 @@ static int sdio_read_reg(struct wilc *wilc, u32 addr, u32 *data)
cmd.count = 4;
cmd.buffer = (u8 *)data;
- cmd.block_size = g_sdio.block_size; /* johnny : prevent it from setting unexpected value */
+ cmd.block_size = g_sdio.block_size;
ret = wilc_sdio_cmd53(wilc, &cmd);
if (ret) {
dev_err(&func->dev,
@@ -629,7 +629,7 @@ static int sdio_read(struct wilc *wilc, u32 addr, u8 *buf, u32 size)
cmd.count = nleft;
cmd.buffer = buf;
- cmd.block_size = block_size; /* johnny : prevent it from setting unexpected value */
+ cmd.block_size = block_size;
if (addr > 0) {
if (!sdio_set_func0_csa_address(wilc, addr))
@@ -871,6 +871,7 @@ static int sdio_clear_int_ext(struct wilc *wilc, u32 val)
{
struct sdio_func *func = dev_to_sdio_func(wilc->dev);
int ret;
+ int vmm_ctl;
if (g_sdio.has_thrpt_enh3) {
u32 reg;
@@ -909,84 +910,82 @@ static int sdio_clear_int_ext(struct wilc *wilc, u32 val)
goto _fail_;
}
}
- } else {
- if (g_sdio.irq_gpio) {
- /* see below. has_thrpt_enh2 uses register 0xf8 to clear interrupts. */
- /* Cannot clear multiple interrupts. Must clear each interrupt individually */
- u32 flags;
-
- flags = val & (BIT(MAX_NUM_INT) - 1);
- if (flags) {
- int i;
-
- ret = 1;
- for (i = 0; i < g_sdio.nint; i++) {
- if (flags & 1) {
- struct sdio_cmd52 cmd;
-
- cmd.read_write = 1;
- cmd.function = 0;
- cmd.raw = 0;
- cmd.address = 0xf8;
- cmd.data = BIT(i);
-
- ret = wilc_sdio_cmd52(wilc, &cmd);
- if (ret) {
- dev_err(&func->dev,
- "Failed cmd52, set 0xf8 data (%d) ...\n",
- __LINE__);
- goto _fail_;
- }
+ return 1;
+ }
+ if (g_sdio.irq_gpio) {
+ /* has_thrpt_enh2 uses register 0xf8 to clear interrupts. */
+ /*
+ * Cannot clear multiple interrupts.
+ * Must clear each interrupt individually.
+ */
+ u32 flags;
+
+ flags = val & (BIT(MAX_NUM_INT) - 1);
+ if (flags) {
+ int i;
+
+ ret = 1;
+ for (i = 0; i < g_sdio.nint; i++) {
+ if (flags & 1) {
+ struct sdio_cmd52 cmd;
+
+ cmd.read_write = 1;
+ cmd.function = 0;
+ cmd.raw = 0;
+ cmd.address = 0xf8;
+ cmd.data = BIT(i);
+
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed cmd52, set 0xf8 data (%d) ...\n",
+ __LINE__);
+ goto _fail_;
}
- if (!ret)
- break;
- flags >>= 1;
}
if (!ret)
- goto _fail_;
- for (i = g_sdio.nint; i < MAX_NUM_INT; i++) {
- if (flags & 1)
- dev_err(&func->dev,
- "Unexpected interrupt cleared %d...\n",
- i);
- flags >>= 1;
- }
+ break;
+ flags >>= 1;
}
- }
-
- {
- u32 vmm_ctl;
-
- vmm_ctl = 0;
- /* select VMM table 0 */
- if ((val & SEL_VMM_TBL0) == SEL_VMM_TBL0)
- vmm_ctl |= BIT(0);
- /* select VMM table 1 */
- if ((val & SEL_VMM_TBL1) == SEL_VMM_TBL1)
- vmm_ctl |= BIT(1);
- /* enable VMM */
- if ((val & EN_VMM) == EN_VMM)
- vmm_ctl |= BIT(2);
-
- if (vmm_ctl) {
- struct sdio_cmd52 cmd;
-
- cmd.read_write = 1;
- cmd.function = 0;
- cmd.raw = 0;
- cmd.address = 0xf6;
- cmd.data = vmm_ctl;
- ret = wilc_sdio_cmd52(wilc, &cmd);
- if (ret) {
+ if (!ret)
+ goto _fail_;
+ for (i = g_sdio.nint; i < MAX_NUM_INT; i++) {
+ if (flags & 1)
dev_err(&func->dev,
- "Failed cmd52, set 0xf6 data (%d) ...\n",
- __LINE__);
- goto _fail_;
- }
+ "Unexpected interrupt cleared %d...\n",
+ i);
+ flags >>= 1;
}
}
}
+ vmm_ctl = 0;
+ /* select VMM table 0 */
+ if ((val & SEL_VMM_TBL0) == SEL_VMM_TBL0)
+ vmm_ctl |= BIT(0);
+ /* select VMM table 1 */
+ if ((val & SEL_VMM_TBL1) == SEL_VMM_TBL1)
+ vmm_ctl |= BIT(1);
+ /* enable VMM */
+ if ((val & EN_VMM) == EN_VMM)
+ vmm_ctl |= BIT(2);
+
+ if (vmm_ctl) {
+ struct sdio_cmd52 cmd;
+
+ cmd.read_write = 1;
+ cmd.function = 0;
+ cmd.raw = 0;
+ cmd.address = 0xf6;
+ cmd.data = vmm_ctl;
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed cmd52, set 0xf6 data (%d) ...\n",
+ __LINE__);
+ goto _fail_;
+ }
+ }
return 1;
_fail_:
return 0;
diff --git a/drivers/staging/wilc1000/wilc_spi.c b/drivers/staging/wilc1000/wilc_spi.c
index 8f71a6022721..6b392c946a6f 100644
--- a/drivers/staging/wilc1000/wilc_spi.c
+++ b/drivers/staging/wilc1000/wilc_spi.c
@@ -287,17 +287,19 @@ static int spi_cmd_complete(struct wilc *wilc, u8 cmd, u32 adr, u8 *b, u32 sz,
u8 rsp;
int len = 0;
int result = N_OK;
+ int retry;
+ u8 crc[2];
wb[0] = cmd;
switch (cmd) {
- case CMD_SINGLE_READ: /* single word (4 bytes) read */
+ case CMD_SINGLE_READ: /* single word (4 bytes) read */
wb[1] = (u8)(adr >> 16);
wb[2] = (u8)(adr >> 8);
wb[3] = (u8)adr;
len = 5;
break;
- case CMD_INTERNAL_READ: /* internal register read */
+ case CMD_INTERNAL_READ: /* internal register read */
wb[1] = (u8)(adr >> 8);
if (clockless == 1)
wb[1] |= BIT(7);
@@ -306,29 +308,29 @@ static int spi_cmd_complete(struct wilc *wilc, u8 cmd, u32 adr, u8 *b, u32 sz,
len = 5;
break;
- case CMD_TERMINATE: /* termination */
+ case CMD_TERMINATE:
wb[1] = 0x00;
wb[2] = 0x00;
wb[3] = 0x00;
len = 5;
break;
- case CMD_REPEAT: /* repeat */
+ case CMD_REPEAT:
wb[1] = 0x00;
wb[2] = 0x00;
wb[3] = 0x00;
len = 5;
break;
- case CMD_RESET: /* reset */
+ case CMD_RESET:
wb[1] = 0xff;
wb[2] = 0xff;
wb[3] = 0xff;
len = 5;
break;
- case CMD_DMA_WRITE: /* dma write */
- case CMD_DMA_READ: /* dma read */
+ case CMD_DMA_WRITE: /* dma write */
+ case CMD_DMA_READ: /* dma read */
wb[1] = (u8)(adr >> 16);
wb[2] = (u8)(adr >> 8);
wb[3] = (u8)adr;
@@ -337,8 +339,8 @@ static int spi_cmd_complete(struct wilc *wilc, u8 cmd, u32 adr, u8 *b, u32 sz,
len = 7;
break;
- case CMD_DMA_EXT_WRITE: /* dma extended write */
- case CMD_DMA_EXT_READ: /* dma extended read */
+ case CMD_DMA_EXT_WRITE: /* dma extended write */
+ case CMD_DMA_EXT_READ: /* dma extended read */
wb[1] = (u8)(adr >> 16);
wb[2] = (u8)(adr >> 8);
wb[3] = (u8)adr;
@@ -348,7 +350,7 @@ static int spi_cmd_complete(struct wilc *wilc, u8 cmd, u32 adr, u8 *b, u32 sz,
len = 8;
break;
- case CMD_INTERNAL_WRITE: /* internal register write */
+ case CMD_INTERNAL_WRITE: /* internal register write */
wb[1] = (u8)(adr >> 8);
if (clockless == 1)
wb[1] |= BIT(7);
@@ -360,7 +362,7 @@ static int spi_cmd_complete(struct wilc *wilc, u8 cmd, u32 adr, u8 *b, u32 sz,
len = 8;
break;
- case CMD_SINGLE_WRITE: /* single word write */
+ case CMD_SINGLE_WRITE: /* single word write */
wb[1] = (u8)(adr >> 16);
wb[2] = (u8)(adr >> 8);
wb[3] = (u8)(adr);
@@ -395,13 +397,12 @@ static int spi_cmd_complete(struct wilc *wilc, u8 cmd, u32 adr, u8 *b, u32 sz,
cmd == CMD_REPEAT) {
len2 = len + (NUM_SKIP_BYTES + NUM_RSP_BYTES + NUM_DUMMY_BYTES);
} else if (cmd == CMD_INTERNAL_READ || cmd == CMD_SINGLE_READ) {
- if (!g_spi.crc_off) {
- len2 = len + (NUM_RSP_BYTES + NUM_DATA_HDR_BYTES + NUM_DATA_BYTES
- + NUM_CRC_BYTES + NUM_DUMMY_BYTES);
- } else {
- len2 = len + (NUM_RSP_BYTES + NUM_DATA_HDR_BYTES + NUM_DATA_BYTES
- + NUM_DUMMY_BYTES);
- }
+ int tmp = NUM_RSP_BYTES + NUM_DATA_HDR_BYTES + NUM_DATA_BYTES
+ + NUM_DUMMY_BYTES;
+ if (!g_spi.crc_off)
+ len2 = len + tmp + NUM_CRC_BYTES;
+ else
+ len2 = len + tmp;
} else {
len2 = len + (NUM_RSP_BYTES + NUM_DUMMY_BYTES);
}
@@ -422,19 +423,13 @@ static int spi_cmd_complete(struct wilc *wilc, u8 cmd, u32 adr, u8 *b, u32 sz,
return N_FAIL;
}
- /**
+ /*
* Command/Control response
- **/
- if (cmd == CMD_RESET ||
- cmd == CMD_TERMINATE ||
- cmd == CMD_REPEAT) {
- rix++; /* skip 1 byte */
- }
+ */
+ if (cmd == CMD_RESET || cmd == CMD_TERMINATE || cmd == CMD_REPEAT)
+ rix++; /* skip 1 byte */
- /* do { */
rsp = rb[rix++];
- /* if(rsp == cmd) break; */
- /* } while(&rptr[1] <= &rb[len2]); */
if (rsp != cmd) {
dev_err(&spi->dev,
@@ -443,9 +438,9 @@ static int spi_cmd_complete(struct wilc *wilc, u8 cmd, u32 adr, u8 *b, u32 sz,
return N_FAIL;
}
- /**
+ /*
* State response
- **/
+ */
rsp = rb[rix++];
if (rsp != 0x00) {
dev_err(&spi->dev, "Failed cmd state response state (%02x)\n",
@@ -455,15 +450,15 @@ static int spi_cmd_complete(struct wilc *wilc, u8 cmd, u32 adr, u8 *b, u32 sz,
if (cmd == CMD_INTERNAL_READ || cmd == CMD_SINGLE_READ ||
cmd == CMD_DMA_READ || cmd == CMD_DMA_EXT_READ) {
- int retry;
- /* u16 crc1, crc2; */
- u8 crc[2];
- /**
+ /*
* Data Respnose header
- **/
+ */
retry = 100;
do {
- /* ensure there is room in buffer later to read data and crc */
+ /*
+ * ensure there is room in buffer later
+ * to read data and crc
+ */
if (rix < len2) {
rsp = rb[rix++];
} else {
@@ -479,129 +474,134 @@ static int spi_cmd_complete(struct wilc *wilc, u8 cmd, u32 adr, u8 *b, u32 sz,
"Error, data read response (%02x)\n", rsp);
return N_RESET;
}
+ }
- if (cmd == CMD_INTERNAL_READ || cmd == CMD_SINGLE_READ) {
- /**
- * Read bytes
- **/
- if ((rix + 3) < len2) {
- b[0] = rb[rix++];
- b[1] = rb[rix++];
- b[2] = rb[rix++];
- b[3] = rb[rix++];
+ if (cmd == CMD_INTERNAL_READ || cmd == CMD_SINGLE_READ) {
+ /*
+ * Read bytes
+ */
+ if ((rix + 3) < len2) {
+ b[0] = rb[rix++];
+ b[1] = rb[rix++];
+ b[2] = rb[rix++];
+ b[3] = rb[rix++];
+ } else {
+ dev_err(&spi->dev,
+ "buffer overrun when reading data.\n");
+ return N_FAIL;
+ }
+
+ if (!g_spi.crc_off) {
+ /*
+ * Read Crc
+ */
+ if ((rix + 1) < len2) {
+ crc[0] = rb[rix++];
+ crc[1] = rb[rix++];
} else {
dev_err(&spi->dev,
- "buffer overrun when reading data.\n");
+ "buffer overrun when reading crc.\n");
return N_FAIL;
}
+ }
+ } else if ((cmd == CMD_DMA_READ) || (cmd == CMD_DMA_EXT_READ)) {
+ int ix;
- if (!g_spi.crc_off) {
- /**
- * Read Crc
- **/
- if ((rix + 1) < len2) {
- crc[0] = rb[rix++];
- crc[1] = rb[rix++];
- } else {
- dev_err(&spi->dev, "buffer overrun when reading crc.\n");
- return N_FAIL;
- }
- }
- } else if ((cmd == CMD_DMA_READ) || (cmd == CMD_DMA_EXT_READ)) {
- int ix;
-
- /* some data may be read in response to dummy bytes. */
- for (ix = 0; (rix < len2) && (ix < sz); )
- b[ix++] = rb[rix++];
-
- sz -= ix;
+ /* some data may be read in response to dummy bytes. */
+ for (ix = 0; (rix < len2) && (ix < sz); )
+ b[ix++] = rb[rix++];
- if (sz > 0) {
- int nbytes;
+ sz -= ix;
- if (sz <= (DATA_PKT_SZ - ix))
- nbytes = sz;
- else
- nbytes = DATA_PKT_SZ - ix;
+ if (sz > 0) {
+ int nbytes;
- /**
- * Read bytes
- **/
- if (wilc_spi_rx(wilc, &b[ix], nbytes)) {
- dev_err(&spi->dev, "Failed data block read, bus error...\n");
- result = N_FAIL;
- goto _error_;
- }
+ if (sz <= (DATA_PKT_SZ - ix))
+ nbytes = sz;
+ else
+ nbytes = DATA_PKT_SZ - ix;
- /**
- * Read Crc
- **/
- if (!g_spi.crc_off) {
- if (wilc_spi_rx(wilc, crc, 2)) {
- dev_err(&spi->dev, "Failed data block crc read, bus error...\n");
- result = N_FAIL;
- goto _error_;
- }
- }
+ /*
+ * Read bytes
+ */
+ if (wilc_spi_rx(wilc, &b[ix], nbytes)) {
+ dev_err(&spi->dev,
+ "Failed block read, bus err\n");
+ result = N_FAIL;
+ goto _error_;
+ }
- ix += nbytes;
- sz -= nbytes;
+ /*
+ * Read Crc
+ */
+ if (!g_spi.crc_off && wilc_spi_rx(wilc, crc, 2)) {
+ dev_err(&spi->dev,
+ "Failed block crc read, bus err\n");
+ result = N_FAIL;
+ goto _error_;
}
- /* if any data in left unread, then read the rest using normal DMA code.*/
- while (sz > 0) {
- int nbytes;
-
- if (sz <= DATA_PKT_SZ)
- nbytes = sz;
- else
- nbytes = DATA_PKT_SZ;
-
- /**
- * read data response only on the next DMA cycles not
- * the first DMA since data response header is already
- * handled above for the first DMA.
- **/
- /**
- * Data Respnose header
- **/
- retry = 10;
- do {
- if (wilc_spi_rx(wilc, &rsp, 1)) {
- dev_err(&spi->dev, "Failed data response read, bus error...\n");
- result = N_FAIL;
- break;
- }
- if (((rsp >> 4) & 0xf) == 0xf)
- break;
- } while (retry--);
-
- if (result == N_FAIL)
- break;
+ ix += nbytes;
+ sz -= nbytes;
+ }
+
+ /*
+ * if any data in left unread,
+ * then read the rest using normal DMA code.
+ */
+ while (sz > 0) {
+ int nbytes;
- /**
- * Read bytes
- **/
- if (wilc_spi_rx(wilc, &b[ix], nbytes)) {
- dev_err(&spi->dev, "Failed data block read, bus error...\n");
+ if (sz <= DATA_PKT_SZ)
+ nbytes = sz;
+ else
+ nbytes = DATA_PKT_SZ;
+
+ /*
+ * read data response only on the next DMA cycles not
+ * the first DMA since data response header is already
+ * handled above for the first DMA.
+ */
+ /*
+ * Data Respnose header
+ */
+ retry = 10;
+ do {
+ if (wilc_spi_rx(wilc, &rsp, 1)) {
+ dev_err(&spi->dev,
+ "Failed resp read, bus err\n");
result = N_FAIL;
break;
}
+ if (((rsp >> 4) & 0xf) == 0xf)
+ break;
+ } while (retry--);
- /**
- * Read Crc
- **/
- if (!g_spi.crc_off) {
- if (wilc_spi_rx(wilc, crc, 2)) {
- dev_err(&spi->dev, "Failed data block crc read, bus error...\n");
- result = N_FAIL;
- break;
- }
- }
+ if (result == N_FAIL)
+ break;
- ix += nbytes;
- sz -= nbytes;
+ /*
+ * Read bytes
+ */
+ if (wilc_spi_rx(wilc, &b[ix], nbytes)) {
+ dev_err(&spi->dev,
+ "Failed block read, bus err\n");
+ result = N_FAIL;
+ break;
}
+
+ /*
+ * Read Crc
+ */
+ if (!g_spi.crc_off && wilc_spi_rx(wilc, crc, 2)) {
+ dev_err(&spi->dev,
+ "Failed block crc read, bus err\n");
+ result = N_FAIL;
+ break;
+ }
+
+ ix += nbytes;
+ sz -= nbytes;
}
}
_error_:
@@ -614,11 +614,10 @@ static int spi_data_write(struct wilc *wilc, u8 *b, u32 sz)
int ix, nbytes;
int result = 1;
u8 cmd, order, crc[2] = {0};
- /* u8 rsp; */
- /**
- * Data
- **/
+ /*
+ * Data
+ */
ix = 0;
do {
if (sz <= DATA_PKT_SZ)
@@ -626,9 +625,9 @@ static int spi_data_write(struct wilc *wilc, u8 *b, u32 sz)
else
nbytes = DATA_PKT_SZ;
- /**
- * Write command
- **/
+ /*
+ * Write command
+ */
cmd = 0xf0;
if (ix == 0) {
if (sz <= DATA_PKT_SZ)
@@ -650,9 +649,9 @@ static int spi_data_write(struct wilc *wilc, u8 *b, u32 sz)
break;
}
- /**
- * Write data
- **/
+ /*
+ * Write data
+ */
if (wilc_spi_tx(wilc, &b[ix], nbytes)) {
dev_err(&spi->dev,
"Failed data block write, bus error...\n");
@@ -660,9 +659,9 @@ static int spi_data_write(struct wilc *wilc, u8 *b, u32 sz)
break;
}
- /**
- * Write Crc
- **/
+ /*
+ * Write Crc
+ */
if (!g_spi.crc_off) {
if (wilc_spi_tx(wilc, crc, 2)) {
dev_err(&spi->dev, "Failed data block crc write, bus error...\n");
@@ -671,9 +670,9 @@ static int spi_data_write(struct wilc *wilc, u8 *b, u32 sz)
}
}
- /**
- * No need to wait for response
- **/
+ /*
+ * No need to wait for response
+ */
ix += nbytes;
sz -= nbytes;
} while (sz);
@@ -733,7 +732,7 @@ static int wilc_spi_write_reg(struct wilc *wilc, u32 addr, u32 data)
data = cpu_to_le32(data);
if (addr < 0x30) {
- /* Clockless register*/
+ /* Clockless register */
cmd = CMD_INTERNAL_WRITE;
clockless = 1;
}
@@ -751,9 +750,9 @@ static int wilc_spi_write(struct wilc *wilc, u32 addr, u8 *buf, u32 size)
int result;
u8 cmd = CMD_DMA_EXT_WRITE;
- /**
- * has to be greated than 4
- **/
+ /*
+ * has to be greated than 4
+ */
if (size <= 4)
return 0;
@@ -764,9 +763,9 @@ static int wilc_spi_write(struct wilc *wilc, u32 addr, u8 *buf, u32 size)
return 0;
}
- /**
- * Data
- **/
+ /*
+ * Data
+ */
result = spi_data_write(wilc, buf, size);
if (result != N_OK)
dev_err(&spi->dev, "Failed block data write...\n");
@@ -783,7 +782,7 @@ static int wilc_spi_read_reg(struct wilc *wilc, u32 addr, u32 *data)
if (addr < 0x30) {
/* dev_err(&spi->dev, "***** read addr %d\n\n", addr); */
- /* Clockless register*/
+ /* Clockless register */
cmd = CMD_INTERNAL_READ;
clockless = 1;
}
@@ -825,9 +824,9 @@ static int wilc_spi_read(struct wilc *wilc, u32 addr, u8 *buf, u32 size)
static int _wilc_spi_deinit(struct wilc *wilc)
{
- /**
- * TODO:
- **/
+ /*
+ * TODO:
+ */
return 1;
}
@@ -836,7 +835,6 @@ static int wilc_spi_init(struct wilc *wilc, bool resume)
struct spi_device *spi = to_spi_device(wilc->dev);
u32 reg;
u32 chipid;
-
static int isinit;
if (isinit) {
@@ -849,45 +847,53 @@ static int wilc_spi_init(struct wilc *wilc, bool resume)
memset(&g_spi, 0, sizeof(struct wilc_spi));
- /**
- * configure protocol
- **/
+ /*
+ * configure protocol
+ */
g_spi.crc_off = 0;
- /* TODO: We can remove the CRC trials if there is a definite way to reset */
+ /*
+ * TODO: We can remove the CRC trials if there is a definite
+ * way to reset
+ */
/* the SPI to it's initial value. */
if (!spi_internal_read(wilc, WILC_SPI_PROTOCOL_OFFSET, &reg)) {
- /* Read failed. Try with CRC off. This might happen when module
+ /*
+ * Read failed. Try with CRC off. This might happen when module
* is removed but chip isn't reset
*/
g_spi.crc_off = 1;
- dev_err(&spi->dev, "Failed internal read protocol with CRC on, retrying with CRC off...\n");
+ dev_err(&spi->dev,
+ "Failed read with CRC on, retrying with CRC off\n");
if (!spi_internal_read(wilc, WILC_SPI_PROTOCOL_OFFSET, &reg)) {
- /* Reaad failed with both CRC on and off, something went bad */
- dev_err(&spi->dev,
- "Failed internal read protocol...\n");
+ /*
+ * Read failed with both CRC on and off,
+ * something went bad
+ */
+ dev_err(&spi->dev, "Failed internal read protocol\n");
return 0;
}
}
- if (g_spi.crc_off == 0) {
- reg &= ~0xc; /* disable crc checking */
+ if (g_spi.crc_off == 0) {
+ reg &= ~0xc; /* disable crc checking */
reg &= ~0x70;
reg |= (0x5 << 4);
if (!spi_internal_write(wilc, WILC_SPI_PROTOCOL_OFFSET, reg)) {
- dev_err(&spi->dev, "[wilc spi %d]: Failed internal write protocol reg...\n", __LINE__);
+ dev_err(&spi->dev,
+ "[wilc spi %d]: Failed internal write reg\n",
+ __LINE__);
return 0;
}
g_spi.crc_off = 1;
}
- /**
- * make sure can read back chip id correctly
- **/
+ /*
+ * make sure can read back chip id correctly
+ */
if (!wilc_spi_read_reg(wilc, 0x1000, &chipid)) {
dev_err(&spi->dev, "Fail cmd read chip id...\n");
return 0;
}
- /* dev_err(&spi->dev, "chipid (%08x)\n", chipid); */
g_spi.has_thrpt_enh = 1;
@@ -933,45 +939,46 @@ static int wilc_spi_read_int(struct wilc *wilc, u32 *int_status)
int happened, j;
u32 unknown_mask;
u32 irq_flags;
+ int k = IRG_FLAGS_OFFSET + 5;
if (g_spi.has_thrpt_enh) {
ret = spi_internal_read(wilc, 0xe840 - WILC_SPI_REG_BASE,
int_status);
- } else {
- ret = wilc_spi_read_reg(wilc, WILC_VMM_TO_HOST_SIZE,
- &byte_cnt);
- if (!ret) {
- dev_err(&spi->dev,
- "Failed read WILC_VMM_TO_HOST_SIZE ...\n");
- goto _fail_;
- }
- tmp = (byte_cnt >> 2) & IRQ_DMA_WD_CNT_MASK;
+ return ret;
+ }
+ ret = wilc_spi_read_reg(wilc, WILC_VMM_TO_HOST_SIZE, &byte_cnt);
+ if (!ret) {
+ dev_err(&spi->dev,
+ "Failed read WILC_VMM_TO_HOST_SIZE ...\n");
+ goto _fail_;
+ }
+ tmp = (byte_cnt >> 2) & IRQ_DMA_WD_CNT_MASK;
- j = 0;
- do {
- happened = 0;
+ j = 0;
+ do {
+ happened = 0;
- wilc_spi_read_reg(wilc, 0x1a90, &irq_flags);
- tmp |= ((irq_flags >> 27) << IRG_FLAGS_OFFSET);
+ wilc_spi_read_reg(wilc, 0x1a90, &irq_flags);
+ tmp |= ((irq_flags >> 27) << IRG_FLAGS_OFFSET);
- if (g_spi.nint > 5) {
- wilc_spi_read_reg(wilc, 0x1a94,
- &irq_flags);
- tmp |= (((irq_flags >> 0) & 0x7) << (IRG_FLAGS_OFFSET + 5));
- }
+ if (g_spi.nint > 5) {
+ wilc_spi_read_reg(wilc, 0x1a94, &irq_flags);
+ tmp |= (((irq_flags >> 0) & 0x7) << k);
+ }
- unknown_mask = ~((1ul << g_spi.nint) - 1);
+ unknown_mask = ~((1ul << g_spi.nint) - 1);
- if ((tmp >> IRG_FLAGS_OFFSET) & unknown_mask) {
- dev_err(&spi->dev, "Unexpected interrupt (2): j=%d, tmp=%x, mask=%x\n", j, tmp, unknown_mask);
- happened = 1;
- }
+ if ((tmp >> IRG_FLAGS_OFFSET) & unknown_mask) {
+ dev_err(&spi->dev,
+ "Unexpected interrupt(2):j=%d,tmp=%x,mask=%x\n",
+ j, tmp, unknown_mask);
+ happened = 1;
+ }
- j++;
- } while (happened);
+ j++;
+ } while (happened);
- *int_status = tmp;
- }
+ *int_status = tmp;
_fail_:
return ret;
@@ -981,71 +988,69 @@ static int wilc_spi_clear_int_ext(struct wilc *wilc, u32 val)
{
struct spi_device *spi = to_spi_device(wilc->dev);
int ret;
+ u32 flags;
+ u32 tbl_ctl;
if (g_spi.has_thrpt_enh) {
ret = spi_internal_write(wilc, 0xe844 - WILC_SPI_REG_BASE,
val);
- } else {
- u32 flags;
-
- flags = val & (BIT(MAX_NUM_INT) - 1);
- if (flags) {
- int i;
-
- ret = 1;
- for (i = 0; i < g_spi.nint; i++) {
- /* No matter what you write 1 or 0, it will clear interrupt. */
- if (flags & 1)
- ret = wilc_spi_write_reg(wilc, 0x10c8 + i * 4, 1);
- if (!ret)
- break;
- flags >>= 1;
- }
- if (!ret) {
+ return ret;
+ }
+
+ flags = val & (BIT(MAX_NUM_INT) - 1);
+ if (flags) {
+ int i;
+
+ ret = 1;
+ for (i = 0; i < g_spi.nint; i++) {
+ /*
+ * No matter what you write 1 or 0,
+ * it will clear interrupt.
+ */
+ if (flags & 1)
+ ret = wilc_spi_write_reg(wilc,
+ 0x10c8 + i * 4, 1);
+ if (!ret)
+ break;
+ flags >>= 1;
+ }
+ if (!ret) {
+ dev_err(&spi->dev,
+ "Failed wilc_spi_write_reg, set reg %x ...\n",
+ 0x10c8 + i * 4);
+ goto _fail_;
+ }
+ for (i = g_spi.nint; i < MAX_NUM_INT; i++) {
+ if (flags & 1)
dev_err(&spi->dev,
- "Failed wilc_spi_write_reg, set reg %x ...\n",
- 0x10c8 + i * 4);
- goto _fail_;
- }
- for (i = g_spi.nint; i < MAX_NUM_INT; i++) {
- if (flags & 1)
- dev_err(&spi->dev,
- "Unexpected interrupt cleared %d...\n",
- i);
- flags >>= 1;
- }
+ "Unexpected interrupt cleared %d...\n",
+ i);
+ flags >>= 1;
}
+ }
- {
- u32 tbl_ctl;
-
- tbl_ctl = 0;
- /* select VMM table 0 */
- if ((val & SEL_VMM_TBL0) == SEL_VMM_TBL0)
- tbl_ctl |= BIT(0);
- /* select VMM table 1 */
- if ((val & SEL_VMM_TBL1) == SEL_VMM_TBL1)
- tbl_ctl |= BIT(1);
+ tbl_ctl = 0;
+ /* select VMM table 0 */
+ if ((val & SEL_VMM_TBL0) == SEL_VMM_TBL0)
+ tbl_ctl |= BIT(0);
+ /* select VMM table 1 */
+ if ((val & SEL_VMM_TBL1) == SEL_VMM_TBL1)
+ tbl_ctl |= BIT(1);
- ret = wilc_spi_write_reg(wilc, WILC_VMM_TBL_CTL,
- tbl_ctl);
- if (!ret) {
- dev_err(&spi->dev,
- "fail write reg vmm_tbl_ctl...\n");
- goto _fail_;
- }
+ ret = wilc_spi_write_reg(wilc, WILC_VMM_TBL_CTL, tbl_ctl);
+ if (!ret) {
+ dev_err(&spi->dev, "fail write reg vmm_tbl_ctl...\n");
+ goto _fail_;
+ }
- if ((val & EN_VMM) == EN_VMM) {
- /**
- * enable vmm transfer.
- **/
- ret = wilc_spi_write_reg(wilc,
- WILC_VMM_CORE_CTL, 1);
- if (!ret) {
- dev_err(&spi->dev, "fail write reg vmm_core_ctl...\n");
- goto _fail_;
- }
- }
+ if ((val & EN_VMM) == EN_VMM) {
+ /*
+ * enable vmm transfer.
+ */
+ ret = wilc_spi_write_reg(wilc, WILC_VMM_CORE_CTL, 1);
+ if (!ret) {
+ dev_err(&spi->dev, "fail write reg vmm_core_ctl...\n");
+ goto _fail_;
}
}
_fail_:
@@ -1065,9 +1070,9 @@ static int wilc_spi_sync_ext(struct wilc *wilc, int nint)
g_spi.nint = nint;
- /**
- * interrupt pin mux select
- **/
+ /*
+ * interrupt pin mux select
+ */
ret = wilc_spi_read_reg(wilc, WILC_PIN_MUX_0, &reg);
if (!ret) {
dev_err(&spi->dev, "Failed read reg (%08x)...\n",
@@ -1082,9 +1087,9 @@ static int wilc_spi_sync_ext(struct wilc *wilc, int nint)
return 0;
}
- /**
- * interrupt enable
- **/
+ /*
+ * interrupt enable
+ */
ret = wilc_spi_read_reg(wilc, WILC_INTR_ENABLE, &reg);
if (!ret) {
dev_err(&spi->dev, "Failed read reg (%08x)...\n",
diff --git a/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c b/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c
index 621810d70450..730d64f2f46a 100644
--- a/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c
+++ b/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c
@@ -85,9 +85,6 @@ static const struct wiphy_wowlan_support wowlan_support = {
#define TCP_ACK_FILTER_LINK_SPEED_THRESH 54
#define DEFAULT_LINK_SPEED 72
-#define IS_MANAGMEMENT 0x100
-#define IS_MANAGMEMENT_CALLBACK 0x080
-#define IS_MGMT_STATUS_SUCCES 0x040
#define GET_PKT_OFFSET(a) (((a) >> 22) & 0x1ff)
static struct network_info last_scanned_shadow[MAX_NUM_SCANNED_NETWORKS_SHADOW];
@@ -297,8 +294,7 @@ static void clear_duringIP(struct timer_list *unused)
wilc_optaining_ip = false;
}
-static int is_network_in_shadow(struct network_info *pstrNetworkInfo,
- void *user_void)
+static int is_network_in_shadow(struct network_info *nw_info, void *user_void)
{
int state = -1;
int i;
@@ -309,7 +305,7 @@ static int is_network_in_shadow(struct network_info *pstrNetworkInfo,
} else {
for (i = 0; i < last_scanned_cnt; i++) {
if (memcmp(last_scanned_shadow[i].bssid,
- pstrNetworkInfo->bssid, 6) == 0) {
+ nw_info->bssid, 6) == 0) {
state = i;
break;
}
@@ -318,10 +314,10 @@ static int is_network_in_shadow(struct network_info *pstrNetworkInfo,
return state;
}
-static void add_network_to_shadow(struct network_info *pstrNetworkInfo,
- void *user_void, void *pJoinParams)
+static void add_network_to_shadow(struct network_info *nw_info,
+ void *user_void, void *join_params)
{
- int ap_found = is_network_in_shadow(pstrNetworkInfo, user_void);
+ int ap_found = is_network_in_shadow(nw_info, user_void);
u32 ap_index = 0;
u8 rssi_index = 0;
@@ -335,42 +331,41 @@ static void add_network_to_shadow(struct network_info *pstrNetworkInfo,
ap_index = ap_found;
}
rssi_index = last_scanned_shadow[ap_index].rssi_history.index;
- last_scanned_shadow[ap_index].rssi_history.samples[rssi_index++] = pstrNetworkInfo->rssi;
+ last_scanned_shadow[ap_index].rssi_history.samples[rssi_index++] = nw_info->rssi;
if (rssi_index == NUM_RSSI) {
rssi_index = 0;
last_scanned_shadow[ap_index].rssi_history.full = true;
}
last_scanned_shadow[ap_index].rssi_history.index = rssi_index;
- last_scanned_shadow[ap_index].rssi = pstrNetworkInfo->rssi;
- last_scanned_shadow[ap_index].cap_info = pstrNetworkInfo->cap_info;
- last_scanned_shadow[ap_index].ssid_len = pstrNetworkInfo->ssid_len;
+ last_scanned_shadow[ap_index].rssi = nw_info->rssi;
+ last_scanned_shadow[ap_index].cap_info = nw_info->cap_info;
+ last_scanned_shadow[ap_index].ssid_len = nw_info->ssid_len;
memcpy(last_scanned_shadow[ap_index].ssid,
- pstrNetworkInfo->ssid, pstrNetworkInfo->ssid_len);
+ nw_info->ssid, nw_info->ssid_len);
memcpy(last_scanned_shadow[ap_index].bssid,
- pstrNetworkInfo->bssid, ETH_ALEN);
- last_scanned_shadow[ap_index].beacon_period = pstrNetworkInfo->beacon_period;
- last_scanned_shadow[ap_index].dtim_period = pstrNetworkInfo->dtim_period;
- last_scanned_shadow[ap_index].ch = pstrNetworkInfo->ch;
- last_scanned_shadow[ap_index].ies_len = pstrNetworkInfo->ies_len;
- last_scanned_shadow[ap_index].tsf_hi = pstrNetworkInfo->tsf_hi;
+ nw_info->bssid, ETH_ALEN);
+ last_scanned_shadow[ap_index].beacon_period = nw_info->beacon_period;
+ last_scanned_shadow[ap_index].dtim_period = nw_info->dtim_period;
+ last_scanned_shadow[ap_index].ch = nw_info->ch;
+ last_scanned_shadow[ap_index].ies_len = nw_info->ies_len;
+ last_scanned_shadow[ap_index].tsf_hi = nw_info->tsf_hi;
if (ap_found != -1)
kfree(last_scanned_shadow[ap_index].ies);
- last_scanned_shadow[ap_index].ies = kmalloc(pstrNetworkInfo->ies_len,
+ last_scanned_shadow[ap_index].ies = kmalloc(nw_info->ies_len,
GFP_KERNEL);
memcpy(last_scanned_shadow[ap_index].ies,
- pstrNetworkInfo->ies, pstrNetworkInfo->ies_len);
+ nw_info->ies, nw_info->ies_len);
last_scanned_shadow[ap_index].time_scan = jiffies;
last_scanned_shadow[ap_index].time_scan_cached = jiffies;
last_scanned_shadow[ap_index].found = 1;
if (ap_found != -1)
kfree(last_scanned_shadow[ap_index].join_params);
- last_scanned_shadow[ap_index].join_params = pJoinParams;
+ last_scanned_shadow[ap_index].join_params = join_params;
}
-static void CfgScanResult(enum scan_event scan_event,
- struct network_info *network_info,
- void *user_void,
- void *join_params)
+static void cfg_scan_result(enum scan_event scan_event,
+ struct network_info *network_info,
+ void *user_void, void *join_params)
{
struct wilc_priv *priv;
struct wiphy *wiphy;
@@ -379,91 +374,97 @@ static void CfgScanResult(enum scan_event scan_event,
struct cfg80211_bss *bss = NULL;
priv = user_void;
- if (priv->cfg_scanning) {
- if (scan_event == SCAN_EVENT_NETWORK_FOUND) {
- wiphy = priv->dev->ieee80211_ptr->wiphy;
+ if (!priv->cfg_scanning)
+ return;
+
+ if (scan_event == SCAN_EVENT_NETWORK_FOUND) {
+ wiphy = priv->dev->ieee80211_ptr->wiphy;
+
+ if (!wiphy || !network_info)
+ return;
+
+ if (wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
+ (((s32)network_info->rssi * 100) < 0 ||
+ ((s32)network_info->rssi * 100) > 100))
+ return;
+
+ s32Freq = ieee80211_channel_to_frequency((s32)network_info->ch,
+ NL80211_BAND_2GHZ);
+ channel = ieee80211_get_channel(wiphy, s32Freq);
+
+ if (!channel)
+ return;
- if (!wiphy)
+ if (network_info->new_network) {
+ if (priv->rcvd_ch_cnt >= MAX_NUM_SCANNED_NETWORKS)
return;
- if (wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
- (((s32)network_info->rssi * 100) < 0 ||
- ((s32)network_info->rssi * 100) > 100))
+ priv->rcvd_ch_cnt++;
+
+ add_network_to_shadow(network_info, priv, join_params);
+
+ if (memcmp("DIRECT-", network_info->ssid, 7))
return;
- if (network_info) {
- s32Freq = ieee80211_channel_to_frequency((s32)network_info->ch, NL80211_BAND_2GHZ);
- channel = ieee80211_get_channel(wiphy, s32Freq);
-
- if (!channel)
- return;
-
- if (network_info->new_network) {
- if (priv->rcvd_ch_cnt < MAX_NUM_SCANNED_NETWORKS) {
- priv->rcvd_ch_cnt++;
-
- add_network_to_shadow(network_info, priv, join_params);
-
- if (!(memcmp("DIRECT-", network_info->ssid, 7))) {
- bss = cfg80211_inform_bss(wiphy,
- channel,
- CFG80211_BSS_FTYPE_UNKNOWN,
- network_info->bssid,
- network_info->tsf_hi,
- network_info->cap_info,
- network_info->beacon_period,
- (const u8 *)network_info->ies,
- (size_t)network_info->ies_len,
- (s32)network_info->rssi * 100,
- GFP_KERNEL);
- cfg80211_put_bss(wiphy, bss);
- }
- }
- } else {
- u32 i;
+ bss = cfg80211_inform_bss(wiphy,
+ channel,
+ CFG80211_BSS_FTYPE_UNKNOWN,
+ network_info->bssid,
+ network_info->tsf_hi,
+ network_info->cap_info,
+ network_info->beacon_period,
+ (const u8 *)network_info->ies,
+ (size_t)network_info->ies_len,
+ (s32)network_info->rssi * 100,
+ GFP_KERNEL);
+ cfg80211_put_bss(wiphy, bss);
+ } else {
+ u32 i;
- for (i = 0; i < priv->rcvd_ch_cnt; i++) {
- if (memcmp(last_scanned_shadow[i].bssid, network_info->bssid, 6) == 0) {
- last_scanned_shadow[i].rssi = network_info->rssi;
- last_scanned_shadow[i].time_scan = jiffies;
- break;
- }
- }
- }
+ for (i = 0; i < priv->rcvd_ch_cnt; i++) {
+ if (memcmp(last_scanned_shadow[i].bssid,
+ network_info->bssid, 6) == 0)
+ break;
}
- } else if (scan_event == SCAN_EVENT_DONE) {
- refresh_scan(priv, false);
- mutex_lock(&priv->scan_req_lock);
+ if (i >= priv->rcvd_ch_cnt)
+ return;
- if (priv->scan_req) {
- struct cfg80211_scan_info info = {
- .aborted = false,
- };
+ last_scanned_shadow[i].rssi = network_info->rssi;
+ last_scanned_shadow[i].time_scan = jiffies;
+ }
+ } else if (scan_event == SCAN_EVENT_DONE) {
+ refresh_scan(priv, false);
- cfg80211_scan_done(priv->scan_req, &info);
- priv->rcvd_ch_cnt = 0;
- priv->cfg_scanning = false;
- priv->scan_req = NULL;
- }
- mutex_unlock(&priv->scan_req_lock);
- } else if (scan_event == SCAN_EVENT_ABORTED) {
- mutex_lock(&priv->scan_req_lock);
+ mutex_lock(&priv->scan_req_lock);
- if (priv->scan_req) {
- struct cfg80211_scan_info info = {
- .aborted = false,
- };
+ if (priv->scan_req) {
+ struct cfg80211_scan_info info = {
+ .aborted = false,
+ };
- update_scan_time();
- refresh_scan(priv, false);
+ cfg80211_scan_done(priv->scan_req, &info);
+ priv->rcvd_ch_cnt = 0;
+ priv->cfg_scanning = false;
+ priv->scan_req = NULL;
+ }
+ mutex_unlock(&priv->scan_req_lock);
+ } else if (scan_event == SCAN_EVENT_ABORTED) {
+ mutex_lock(&priv->scan_req_lock);
- cfg80211_scan_done(priv->scan_req, &info);
- priv->cfg_scanning = false;
- priv->scan_req = NULL;
- }
- mutex_unlock(&priv->scan_req_lock);
+ if (priv->scan_req) {
+ struct cfg80211_scan_info info = {
+ .aborted = false,
+ };
+
+ update_scan_time();
+ refresh_scan(priv, false);
+
+ cfg80211_scan_done(priv->scan_req, &info);
+ priv->cfg_scanning = false;
+ priv->scan_req = NULL;
}
+ mutex_unlock(&priv->scan_req_lock);
}
}
@@ -586,8 +587,8 @@ static int scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
struct wilc_priv *priv;
u32 i;
s32 ret = 0;
- u8 au8ScanChanList[MAX_NUM_SCANNED_NETWORKS];
- struct hidden_network strHiddenNetwork;
+ u8 scan_ch_list[MAX_NUM_SCANNED_NETWORKS];
+ struct hidden_network hidden_ntwk;
struct wilc_vif *vif;
priv = wiphy_priv(wiphy);
@@ -602,38 +603,38 @@ static int scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
priv->cfg_scanning = true;
if (request->n_channels <= MAX_NUM_SCANNED_NETWORKS) {
for (i = 0; i < request->n_channels; i++)
- au8ScanChanList[i] = (u8)ieee80211_frequency_to_channel(request->channels[i]->center_freq);
+ scan_ch_list[i] = (u8)ieee80211_frequency_to_channel(request->channels[i]->center_freq);
if (request->n_ssids >= 1) {
- strHiddenNetwork.net_info =
+ hidden_ntwk.net_info =
kmalloc_array(request->n_ssids,
sizeof(struct hidden_network),
GFP_KERNEL);
- if (!strHiddenNetwork.net_info)
+ if (!hidden_ntwk.net_info)
return -ENOMEM;
- strHiddenNetwork.n_ssids = request->n_ssids;
+ hidden_ntwk.n_ssids = request->n_ssids;
for (i = 0; i < request->n_ssids; i++) {
if (request->ssids[i].ssid_len != 0) {
- strHiddenNetwork.net_info[i].ssid = kmalloc(request->ssids[i].ssid_len, GFP_KERNEL);
- memcpy(strHiddenNetwork.net_info[i].ssid, request->ssids[i].ssid, request->ssids[i].ssid_len);
- strHiddenNetwork.net_info[i].ssid_len = request->ssids[i].ssid_len;
+ hidden_ntwk.net_info[i].ssid = kmalloc(request->ssids[i].ssid_len, GFP_KERNEL);
+ memcpy(hidden_ntwk.net_info[i].ssid, request->ssids[i].ssid, request->ssids[i].ssid_len);
+ hidden_ntwk.net_info[i].ssid_len = request->ssids[i].ssid_len;
} else {
- strHiddenNetwork.n_ssids -= 1;
+ hidden_ntwk.n_ssids -= 1;
}
}
ret = wilc_scan(vif, USER_SCAN, ACTIVE_SCAN,
- au8ScanChanList,
+ scan_ch_list,
request->n_channels,
(const u8 *)request->ie,
- request->ie_len, CfgScanResult,
- (void *)priv, &strHiddenNetwork);
+ request->ie_len, cfg_scan_result,
+ (void *)priv, &hidden_ntwk);
} else {
ret = wilc_scan(vif, USER_SCAN, ACTIVE_SCAN,
- au8ScanChanList,
+ scan_ch_list,
request->n_channels,
(const u8 *)request->ie,
- request->ie_len, CfgScanResult,
+ request->ie_len, cfg_scan_result,
(void *)priv, NULL);
}
} else {
@@ -657,7 +658,7 @@ static int connect(struct wiphy *wiphy, struct net_device *dev,
struct wilc_priv *priv;
struct host_if_drv *wfi_drv;
- struct network_info *pstrNetworkInfo = NULL;
+ struct network_info *nw_info = NULL;
struct wilc_vif *vif;
wilc_connecting = 1;
@@ -692,7 +693,7 @@ static int connect(struct wiphy *wiphy, struct net_device *dev,
}
if (sel_bssi_idx < last_scanned_cnt) {
- pstrNetworkInfo = &last_scanned_shadow[sel_bssi_idx];
+ nw_info = &last_scanned_shadow[sel_bssi_idx];
} else {
ret = -ENOENT;
wilc_connecting = 0;
@@ -775,29 +776,23 @@ static int connect(struct wiphy *wiphy, struct net_device *dev,
}
if (sme->crypto.n_akm_suites) {
- switch (sme->crypto.akm_suites[0]) {
- case WLAN_AKM_SUITE_8021X:
+ if (sme->crypto.akm_suites[0] == WLAN_AKM_SUITE_8021X)
auth_type = IEEE8021;
- break;
-
- default:
- break;
- }
}
- curr_channel = pstrNetworkInfo->ch;
+ curr_channel = nw_info->ch;
if (!wfi_drv->p2p_connect)
- wlan_channel = pstrNetworkInfo->ch;
+ wlan_channel = nw_info->ch;
- wilc_wlan_set_bssid(dev, pstrNetworkInfo->bssid, STATION_MODE);
+ wilc_wlan_set_bssid(dev, nw_info->bssid, STATION_MODE);
- ret = wilc_set_join_req(vif, pstrNetworkInfo->bssid, sme->ssid,
+ ret = wilc_set_join_req(vif, nw_info->bssid, sme->ssid,
sme->ssid_len, sme->ie, sme->ie_len,
cfg_connect_result, (void *)priv,
u8security, auth_type,
- pstrNetworkInfo->ch,
- pstrNetworkInfo->join_params);
+ nw_info->ch,
+ nw_info->join_params);
if (ret != 0) {
netdev_err(dev, "wilc_set_join_req(): Error\n");
ret = -ENOENT;
@@ -959,18 +954,14 @@ static int add_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
}
kfree(priv->wilc_ptk[key_index]->key);
-
priv->wilc_ptk[key_index]->key = kmalloc(params->key_len, GFP_KERNEL);
+ memcpy(priv->wilc_ptk[key_index]->key, params->key, params->key_len);
kfree(priv->wilc_ptk[key_index]->seq);
-
- if (params->seq_len > 0)
+ if (params->seq_len > 0) {
priv->wilc_ptk[key_index]->seq = kmalloc(params->seq_len, GFP_KERNEL);
-
- memcpy(priv->wilc_ptk[key_index]->key, params->key, params->key_len);
-
- if (params->seq_len > 0)
memcpy(priv->wilc_ptk[key_index]->seq, params->seq, params->seq_len);
+ }
priv->wilc_ptk[key_index]->cipher = params->cipher;
priv->wilc_ptk[key_index]->key_len = params->key_len;
@@ -1127,8 +1118,8 @@ static int del_key(struct wiphy *wiphy, struct net_device *netdev,
}
static int get_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
- bool pairwise,
- const u8 *mac_addr, void *cookie, void (*callback)(void *cookie, struct key_params *))
+ bool pairwise, const u8 *mac_addr, void *cookie,
+ void (*callback)(void *cookie, struct key_params *))
{
struct wilc_priv *priv;
struct key_params key_params;
@@ -1154,8 +1145,8 @@ static int get_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
return 0;
}
-static int set_default_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
- bool unicast, bool multicast)
+static int set_default_key(struct wiphy *wiphy, struct net_device *netdev,
+ u8 key_index, bool unicast, bool multicast)
{
struct wilc_priv *priv;
struct wilc_vif *vif;
@@ -1346,10 +1337,33 @@ static int flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
return 0;
}
+static inline void wilc_wfi_cfg_parse_ch_attr(u8 *buf, u8 ch_list_attr_idx,
+ u8 op_ch_attr_idx)
+{
+ int i = 0;
+ int j = 0;
+
+ if (ch_list_attr_idx) {
+ u8 limit = ch_list_attr_idx + 3 + buf[ch_list_attr_idx + 1];
+
+ for (i = ch_list_attr_idx + 3; i < limit; i++) {
+ if (buf[i] == 0x51) {
+ for (j = i + 2; j < ((i + 2) + buf[i + 1]); j++)
+ buf[j] = wlan_channel;
+ break;
+ }
+ }
+ }
+
+ if (op_ch_attr_idx) {
+ buf[op_ch_attr_idx + 6] = 0x51;
+ buf[op_ch_attr_idx + 7] = wlan_channel;
+ }
+}
+
static void wilc_wfi_cfg_parse_rx_action(u8 *buf, u32 len)
{
u32 index = 0;
- u32 i = 0, j = 0;
u8 op_channel_attr_index = 0;
u8 channel_list_attr_index = 0;
@@ -1364,28 +1378,15 @@ static void wilc_wfi_cfg_parse_rx_action(u8 *buf, u32 len)
op_channel_attr_index = index;
index += buf[index + 1] + 3;
}
- if (wlan_channel != INVALID_CHANNEL) {
- if (channel_list_attr_index) {
- for (i = channel_list_attr_index + 3; i < ((channel_list_attr_index + 3) + buf[channel_list_attr_index + 1]); i++) {
- if (buf[i] == 0x51) {
- for (j = i + 2; j < ((i + 2) + buf[i + 1]); j++)
- buf[j] = wlan_channel;
- break;
- }
- }
- }
-
- if (op_channel_attr_index) {
- buf[op_channel_attr_index + 6] = 0x51;
- buf[op_channel_attr_index + 7] = wlan_channel;
- }
- }
+ if (wlan_channel != INVALID_CHANNEL)
+ wilc_wfi_cfg_parse_ch_attr(buf, channel_list_attr_index,
+ op_channel_attr_index);
}
-static void wilc_wfi_cfg_parse_tx_action(u8 *buf, u32 len, bool oper_ch, u8 iftype)
+static void wilc_wfi_cfg_parse_tx_action(u8 *buf, u32 len, bool oper_ch,
+ u8 iftype)
{
u32 index = 0;
- u32 i = 0, j = 0;
u8 op_channel_attr_index = 0;
u8 channel_list_attr_index = 0;
@@ -1403,22 +1404,9 @@ static void wilc_wfi_cfg_parse_tx_action(u8 *buf, u32 len, bool oper_ch, u8 ifty
op_channel_attr_index = index;
index += buf[index + 1] + 3;
}
- if (wlan_channel != INVALID_CHANNEL && oper_ch) {
- if (channel_list_attr_index) {
- for (i = channel_list_attr_index + 3; i < ((channel_list_attr_index + 3) + buf[channel_list_attr_index + 1]); i++) {
- if (buf[i] == 0x51) {
- for (j = i + 2; j < ((i + 2) + buf[i + 1]); j++)
- buf[j] = wlan_channel;
- break;
- }
- }
- }
-
- if (op_channel_attr_index) {
- buf[op_channel_attr_index + 6] = 0x51;
- buf[op_channel_attr_index + 7] = wlan_channel;
- }
- }
+ if (wlan_channel != INVALID_CHANNEL && oper_ch)
+ wilc_wfi_cfg_parse_ch_attr(buf, channel_list_attr_index,
+ op_channel_attr_index);
}
void WILC_WFI_p2p_rx(struct net_device *dev, u8 *buff, u32 size)
@@ -1717,9 +1705,13 @@ static int mgmt_tx_cancel_wait(struct wiphy *wiphy,
wfi_drv->p2p_timeout = jiffies;
if (!priv->p2p_listen_state) {
+ struct wilc_wfi_p2p_listen_params *params;
+
+ params = &priv->remain_on_ch_params;
+
cfg80211_remain_on_channel_expired(priv->wdev,
- priv->remain_on_ch_params.listen_cookie,
- priv->remain_on_ch_params.listen_ch,
+ params->listen_cookie,
+ params->listen_ch,
GFP_KERNEL);
}
@@ -1813,7 +1805,8 @@ static int set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
}
static int change_virtual_intf(struct wiphy *wiphy, struct net_device *dev,
- enum nl80211_iftype type, struct vif_params *params)
+ enum nl80211_iftype type,
+ struct vif_params *params)
{
struct wilc_priv *priv;
struct wilc_vif *vif;
@@ -1837,7 +1830,8 @@ static int change_virtual_intf(struct wiphy *wiphy, struct net_device *dev,
vif->iftype = STATION_MODE;
wilc_set_operation_mode(vif, STATION_MODE);
- memset(priv->assoc_stainfo.sta_associated_bss, 0, MAX_NUM_STA * ETH_ALEN);
+ memset(priv->assoc_stainfo.sta_associated_bss, 0,
+ MAX_NUM_STA * ETH_ALEN);
wilc_enable_ps = true;
wilc_set_power_mgmt(vif, 1, 0);
@@ -2310,6 +2304,7 @@ int wilc_deinit_host_int(struct net_device *net)
op_ifcs--;
+ mutex_destroy(&priv->scan_req_lock);
ret = wilc_deinit(vif);
clear_shadow_scan();
diff --git a/drivers/staging/wilc1000/wilc_wfi_netdevice.h b/drivers/staging/wilc1000/wilc_wfi_netdevice.h
index 3337fb26c8e2..d62c4f1cddc6 100644
--- a/drivers/staging/wilc1000/wilc_wfi_netdevice.h
+++ b/drivers/staging/wilc1000/wilc_wfi_netdevice.h
@@ -230,7 +230,7 @@ void wilc_netdev_cleanup(struct wilc *wilc);
int wilc_netdev_init(struct wilc **wilc, struct device *dev, int io_type,
int gpio, const struct wilc_hif_func *ops);
void wilc1000_wlan_deinit(struct net_device *dev);
-void WILC_WFI_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size);
+void wilc_wfi_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size);
int wilc_wlan_get_firmware(struct net_device *dev);
int wilc_wlan_set_bssid(struct net_device *wilc_netdev, u8 *bssid, u8 mode);
diff --git a/drivers/staging/wilc1000/wilc_wlan.c b/drivers/staging/wilc1000/wilc_wlan.c
index acaeafc2c350..bcbb92323a0a 100644
--- a/drivers/staging/wilc1000/wilc_wlan.c
+++ b/drivers/staging/wilc1000/wilc_wlan.c
@@ -173,11 +173,13 @@ static inline int update_tcp_session(u32 index, u32 ack)
static inline int add_tcp_pending_ack(u32 ack, u32 session_index,
struct txq_entry_t *txqe)
{
- if (pending_base + pending_acks < MAX_PENDING_ACKS) {
- pending_acks_info[pending_base + pending_acks].ack_num = ack;
- pending_acks_info[pending_base + pending_acks].txqe = txqe;
- pending_acks_info[pending_base + pending_acks].session_index = session_index;
- txqe->tcp_pending_ack_idx = pending_base + pending_acks;
+ u32 i = pending_base + pending_acks;
+
+ if (i < MAX_PENDING_ACKS) {
+ pending_acks_info[i].ack_num = ack;
+ pending_acks_info[i].txqe = txqe;
+ pending_acks_info[i].session_index = session_index;
+ txqe->tcp_pending_ack_idx = i;
pending_acks++;
}
return 0;
@@ -230,8 +232,10 @@ static inline void tcp_process(struct net_device *dev, struct txq_entry_t *tqe)
(u32)tcp_hdr_ptr[11];
for (i = 0; i < tcp_session; i++) {
+ u32 j = ack_session_info[i].seq_num;
+
if (i < 2 * MAX_TCP_SESSION &&
- ack_session_info[i].seq_num == seq_no) {
+ j == seq_no) {
update_tcp_session(i, ack_no);
break;
}
@@ -258,10 +262,20 @@ static int wilc_wlan_txq_filter_dup_tcp_ack(struct net_device *dev)
spin_lock_irqsave(&wilc->txq_spinlock, wilc->txq_spinlock_flags);
for (i = pending_base; i < (pending_base + pending_acks); i++) {
- if (i >= MAX_PENDING_ACKS ||
- pending_acks_info[i].session_index >= 2 * MAX_TCP_SESSION)
+ u32 session_index;
+ u32 bigger_ack_num;
+
+ if (i >= MAX_PENDING_ACKS)
break;
- if (pending_acks_info[i].ack_num < ack_session_info[pending_acks_info[i].session_index].bigger_ack_num) {
+
+ session_index = pending_acks_info[i].session_index;
+
+ if (session_index >= 2 * MAX_TCP_SESSION)
+ break;
+
+ bigger_ack_num = ack_session_info[session_index].bigger_ack_num;
+
+ if (pending_acks_info[i].ack_num < bigger_ack_num) {
struct txq_entry_t *tqe;
tqe = pending_acks_info[i].txqe;
@@ -562,197 +576,196 @@ int wilc_wlan_handle_txq(struct net_device *dev, u32 *txq_count)
u32 vmm_table[WILC_VMM_TBL_SIZE];
struct wilc_vif *vif;
struct wilc *wilc;
+ const struct wilc_hif_func *func;
vif = netdev_priv(dev);
wilc = vif->wilc;
txb = wilc->tx_buffer;
wilc->txq_exit = 0;
+
+ if (wilc->quit)
+ goto out;
+
+ mutex_lock(&wilc->txq_add_to_head_cs);
+ wilc_wlan_txq_filter_dup_tcp_ack(dev);
+ tqe = wilc_wlan_txq_get_first(wilc);
+ i = 0;
+ sum = 0;
do {
- if (wilc->quit)
- break;
+ if (tqe && (i < (WILC_VMM_TBL_SIZE - 1))) {
+ if (tqe->type == WILC_CFG_PKT)
+ vmm_sz = ETH_CONFIG_PKT_HDR_OFFSET;
- mutex_lock(&wilc->txq_add_to_head_cs);
- wilc_wlan_txq_filter_dup_tcp_ack(dev);
- tqe = wilc_wlan_txq_get_first(wilc);
- i = 0;
- sum = 0;
- do {
- if (tqe && (i < (WILC_VMM_TBL_SIZE - 1))) {
- if (tqe->type == WILC_CFG_PKT)
- vmm_sz = ETH_CONFIG_PKT_HDR_OFFSET;
+ else if (tqe->type == WILC_NET_PKT)
+ vmm_sz = ETH_ETHERNET_HDR_OFFSET;
+
+ else
+ vmm_sz = HOST_HDR_OFFSET;
+
+ vmm_sz += tqe->buffer_size;
+
+ if (vmm_sz & 0x3)
+ vmm_sz = (vmm_sz + 4) & ~0x3;
- else if (tqe->type == WILC_NET_PKT)
- vmm_sz = ETH_ETHERNET_HDR_OFFSET;
+ if ((sum + vmm_sz) > LINUX_TX_SIZE)
+ break;
- else
- vmm_sz = HOST_HDR_OFFSET;
+ vmm_table[i] = vmm_sz / 4;
+ if (tqe->type == WILC_CFG_PKT)
+ vmm_table[i] |= BIT(10);
+ vmm_table[i] = cpu_to_le32(vmm_table[i]);
- vmm_sz += tqe->buffer_size;
+ i++;
+ sum += vmm_sz;
+ tqe = wilc_wlan_txq_get_next(wilc, tqe);
+ } else {
+ break;
+ }
+ } while (1);
+
+ if (i == 0)
+ goto out;
+ vmm_table[i] = 0x0;
+
+ acquire_bus(wilc, ACQUIRE_AND_WAKEUP);
+ counter = 0;
+ func = wilc->hif_func;
+ do {
+ ret = func->hif_read_reg(wilc, WILC_HOST_TX_CTRL, &reg);
+ if (!ret)
+ break;
- if (vmm_sz & 0x3)
- vmm_sz = (vmm_sz + 4) & ~0x3;
+ if ((reg & 0x1) == 0)
+ break;
- if ((sum + vmm_sz) > LINUX_TX_SIZE)
- break;
+ counter++;
+ if (counter > 200) {
+ counter = 0;
+ ret = func->hif_write_reg(wilc, WILC_HOST_TX_CTRL, 0);
+ break;
+ }
+ } while (!wilc->quit);
- vmm_table[i] = vmm_sz / 4;
- if (tqe->type == WILC_CFG_PKT)
- vmm_table[i] |= BIT(10);
- vmm_table[i] = cpu_to_le32(vmm_table[i]);
+ if (!ret)
+ goto out_release_bus;
- i++;
- sum += vmm_sz;
- tqe = wilc_wlan_txq_get_next(wilc, tqe);
- } else {
- break;
- }
- } while (1);
+ timeout = 200;
+ do {
+ ret = func->hif_block_tx(wilc,
+ WILC_VMM_TBL_RX_SHADOW_BASE,
+ (u8 *)vmm_table,
+ ((i + 1) * 4));
+ if (!ret)
+ break;
- if (i == 0)
+ ret = func->hif_write_reg(wilc, WILC_HOST_VMM_CTL, 0x2);
+ if (!ret)
break;
- vmm_table[i] = 0x0;
- acquire_bus(wilc, ACQUIRE_AND_WAKEUP);
- counter = 0;
do {
- ret = wilc->hif_func->hif_read_reg(wilc,
- WILC_HOST_TX_CTRL,
- &reg);
+ ret = func->hif_read_reg(wilc, WILC_HOST_VMM_CTL, &reg);
if (!ret)
break;
-
- if ((reg & 0x1) == 0)
- break;
-
- counter++;
- if (counter > 200) {
- counter = 0;
- ret = wilc->hif_func->hif_write_reg(wilc, WILC_HOST_TX_CTRL, 0);
+ if ((reg >> 2) & 0x1) {
+ entries = ((reg >> 3) & 0x3f);
break;
}
- } while (!wilc->quit);
+ release_bus(wilc, RELEASE_ALLOW_SLEEP);
+ } while (--timeout);
+ if (timeout <= 0) {
+ ret = func->hif_write_reg(wilc, WILC_HOST_VMM_CTL, 0x0);
+ break;
+ }
if (!ret)
- goto _end_;
+ break;
- timeout = 200;
- do {
- ret = wilc->hif_func->hif_block_tx(wilc, WILC_VMM_TBL_RX_SHADOW_BASE, (u8 *)vmm_table, ((i + 1) * 4));
+ if (entries == 0) {
+ ret = func->hif_read_reg(wilc, WILC_HOST_TX_CTRL, &reg);
if (!ret)
break;
-
- ret = wilc->hif_func->hif_write_reg(wilc,
- WILC_HOST_VMM_CTL,
- 0x2);
+ reg &= ~BIT(0);
+ ret = func->hif_write_reg(wilc, WILC_HOST_TX_CTRL, reg);
if (!ret)
break;
+ break;
+ }
+ break;
+ } while (1);
- do {
- ret = wilc->hif_func->hif_read_reg(wilc, WILC_HOST_VMM_CTL, &reg);
- if (!ret)
- break;
- if ((reg >> 2) & 0x1) {
- entries = ((reg >> 3) & 0x3f);
- break;
- }
- release_bus(wilc, RELEASE_ALLOW_SLEEP);
- } while (--timeout);
- if (timeout <= 0) {
- ret = wilc->hif_func->hif_write_reg(wilc, WILC_HOST_VMM_CTL, 0x0);
- break;
- }
+ if (!ret)
+ goto out_release_bus;
- if (!ret)
- break;
+ if (entries == 0) {
+ ret = WILC_TX_ERR_NO_BUF;
+ goto out_release_bus;
+ }
- if (entries == 0) {
- ret = wilc->hif_func->hif_read_reg(wilc, WILC_HOST_TX_CTRL, &reg);
- if (!ret)
- break;
- reg &= ~BIT(0);
- ret = wilc->hif_func->hif_write_reg(wilc, WILC_HOST_TX_CTRL, reg);
- if (!ret)
- break;
- break;
- }
+ release_bus(wilc, RELEASE_ALLOW_SLEEP);
+
+ offset = 0;
+ i = 0;
+ do {
+ u32 header, buffer_offset;
+ char *bssid;
+
+ tqe = wilc_wlan_txq_remove_from_head(dev);
+ if (!tqe)
break;
- } while (1);
- if (!ret)
- goto _end_;
+ if (vmm_table[i] == 0)
+ break;
- if (entries == 0) {
- ret = WILC_TX_ERR_NO_BUF;
- goto _end_;
- }
+ vmm_table[i] = cpu_to_le32(vmm_table[i]);
+ vmm_sz = (vmm_table[i] & 0x3ff);
+ vmm_sz *= 4;
+ header = (tqe->type << 31) |
+ (tqe->buffer_size << 15) |
+ vmm_sz;
+ if (tqe->type == WILC_MGMT_PKT)
+ header |= BIT(30);
+ else
+ header &= ~BIT(30);
- release_bus(wilc, RELEASE_ALLOW_SLEEP);
+ header = cpu_to_le32(header);
+ memcpy(&txb[offset], &header, 4);
+ if (tqe->type == WILC_CFG_PKT) {
+ buffer_offset = ETH_CONFIG_PKT_HDR_OFFSET;
+ } else if (tqe->type == WILC_NET_PKT) {
+ bssid = ((struct tx_complete_data *)(tqe->priv))->bssid;
- offset = 0;
- i = 0;
- do {
- tqe = wilc_wlan_txq_remove_from_head(dev);
- if (tqe && vmm_table[i] != 0) {
- u32 header, buffer_offset;
-
- vmm_table[i] = cpu_to_le32(vmm_table[i]);
- vmm_sz = (vmm_table[i] & 0x3ff);
- vmm_sz *= 4;
- header = (tqe->type << 31) |
- (tqe->buffer_size << 15) |
- vmm_sz;
- if (tqe->type == WILC_MGMT_PKT)
- header |= BIT(30);
- else
- header &= ~BIT(30);
-
- header = cpu_to_le32(header);
- memcpy(&txb[offset], &header, 4);
- if (tqe->type == WILC_CFG_PKT) {
- buffer_offset = ETH_CONFIG_PKT_HDR_OFFSET;
- } else if (tqe->type == WILC_NET_PKT) {
- char *bssid = ((struct tx_complete_data *)(tqe->priv))->bssid;
-
- buffer_offset = ETH_ETHERNET_HDR_OFFSET;
- memcpy(&txb[offset + 8], bssid, 6);
- } else {
- buffer_offset = HOST_HDR_OFFSET;
- }
+ buffer_offset = ETH_ETHERNET_HDR_OFFSET;
+ memcpy(&txb[offset + 8], bssid, 6);
+ } else {
+ buffer_offset = HOST_HDR_OFFSET;
+ }
- memcpy(&txb[offset + buffer_offset],
- tqe->buffer, tqe->buffer_size);
- offset += vmm_sz;
- i++;
- tqe->status = 1;
- if (tqe->tx_complete_func)
- tqe->tx_complete_func(tqe->priv,
- tqe->status);
- if (tqe->tcp_pending_ack_idx != NOT_TCP_ACK &&
- tqe->tcp_pending_ack_idx < MAX_PENDING_ACKS)
- pending_acks_info[tqe->tcp_pending_ack_idx].txqe = NULL;
- kfree(tqe);
- } else {
- break;
- }
- } while (--entries);
+ memcpy(&txb[offset + buffer_offset],
+ tqe->buffer, tqe->buffer_size);
+ offset += vmm_sz;
+ i++;
+ tqe->status = 1;
+ if (tqe->tx_complete_func)
+ tqe->tx_complete_func(tqe->priv, tqe->status);
+ if (tqe->tcp_pending_ack_idx != NOT_TCP_ACK &&
+ tqe->tcp_pending_ack_idx < MAX_PENDING_ACKS)
+ pending_acks_info[tqe->tcp_pending_ack_idx].txqe = NULL;
+ kfree(tqe);
+ } while (--entries);
- acquire_bus(wilc, ACQUIRE_AND_WAKEUP);
+ acquire_bus(wilc, ACQUIRE_AND_WAKEUP);
- ret = wilc->hif_func->hif_clear_int_ext(wilc, ENABLE_TX_VMM);
- if (!ret)
- goto _end_;
+ ret = func->hif_clear_int_ext(wilc, ENABLE_TX_VMM);
+ if (!ret)
+ goto out_release_bus;
- ret = wilc->hif_func->hif_block_tx_ext(wilc, 0, txb, offset);
- if (!ret)
- goto _end_;
+ ret = func->hif_block_tx_ext(wilc, 0, txb, offset);
-_end_:
+out_release_bus:
+ release_bus(wilc, RELEASE_ALLOW_SLEEP);
- release_bus(wilc, RELEASE_ALLOW_SLEEP);
- if (ret != 1)
- break;
- } while (0);
+out:
mutex_unlock(&wilc->txq_add_to_head_cs);
wilc->txq_exit = 1;
@@ -760,9 +773,70 @@ _end_:
return ret;
}
+static void wilc_wlan_handle_rx_buff(struct wilc *wilc, u8 *buffer, int size)
+{
+ int offset = 0;
+ u32 header;
+ u32 pkt_len, pkt_offset, tp_len;
+ int is_cfg_packet;
+ u8 *buff_ptr;
+
+ do {
+ buff_ptr = buffer + offset;
+ memcpy(&header, buff_ptr, 4);
+ header = cpu_to_le32(header);
+
+ is_cfg_packet = (header >> 31) & 0x1;
+ pkt_offset = (header >> 22) & 0x1ff;
+ tp_len = (header >> 11) & 0x7ff;
+ pkt_len = header & 0x7ff;
+
+ if (pkt_len == 0 || tp_len == 0)
+ break;
+
+ if (pkt_offset & IS_MANAGMEMENT) {
+ pkt_offset &= ~(IS_MANAGMEMENT |
+ IS_MANAGMEMENT_CALLBACK |
+ IS_MGMT_STATUS_SUCCES);
+ buff_ptr += HOST_HDR_OFFSET;
+ wilc_wfi_mgmt_rx(wilc, buff_ptr, pkt_len);
+ } else {
+ if (!is_cfg_packet) {
+ if (pkt_len > 0) {
+ wilc_frmw_to_linux(wilc, buff_ptr,
+ pkt_len,
+ pkt_offset);
+ }
+ } else {
+ struct wilc_cfg_rsp rsp;
+
+ buff_ptr += pkt_offset;
+
+ wilc_wlan_cfg_indicate_rx(wilc, buff_ptr,
+ pkt_len,
+ &rsp);
+ if (rsp.type == WILC_CFG_RSP) {
+ if (wilc->cfg_seq_no == rsp.seq_no)
+ complete(&wilc->cfg_event);
+ } else if (rsp.type == WILC_CFG_RSP_STATUS) {
+ wilc_mac_indicate(wilc,
+ WILC_MAC_INDICATE_STATUS);
+
+ } else if (rsp.type == WILC_CFG_RSP_SCAN) {
+ wilc_mac_indicate(wilc,
+ WILC_MAC_INDICATE_SCAN);
+ }
+ }
+ }
+ offset += tp_len;
+ if (offset >= size)
+ break;
+ } while (1);
+}
+
static void wilc_wlan_handle_rxq(struct wilc *wilc)
{
- int offset = 0, size;
+ int size;
u8 *buffer;
struct rxq_entry_t *rqe;
@@ -779,61 +853,8 @@ static void wilc_wlan_handle_rxq(struct wilc *wilc)
buffer = rqe->buffer;
size = rqe->buffer_size;
- offset = 0;
-
- do {
- u32 header;
- u32 pkt_len, pkt_offset, tp_len;
- int is_cfg_packet;
+ wilc_wlan_handle_rx_buff(wilc, buffer, size);
- memcpy(&header, &buffer[offset], 4);
- header = cpu_to_le32(header);
-
- is_cfg_packet = (header >> 31) & 0x1;
- pkt_offset = (header >> 22) & 0x1ff;
- tp_len = (header >> 11) & 0x7ff;
- pkt_len = header & 0x7ff;
-
- if (pkt_len == 0 || tp_len == 0)
- break;
-
- #define IS_MANAGMEMENT 0x100
- #define IS_MANAGMEMENT_CALLBACK 0x080
- #define IS_MGMT_STATUS_SUCCES 0x040
-
- if (pkt_offset & IS_MANAGMEMENT) {
- pkt_offset &= ~(IS_MANAGMEMENT |
- IS_MANAGMEMENT_CALLBACK |
- IS_MGMT_STATUS_SUCCES);
-
- WILC_WFI_mgmt_rx(wilc, &buffer[offset + HOST_HDR_OFFSET], pkt_len);
- } else {
- if (!is_cfg_packet) {
- if (pkt_len > 0) {
- wilc_frmw_to_linux(wilc,
- &buffer[offset],
- pkt_len,
- pkt_offset);
- }
- } else {
- struct wilc_cfg_rsp rsp;
-
- wilc_wlan_cfg_indicate_rx(wilc, &buffer[pkt_offset + offset], pkt_len, &rsp);
- if (rsp.type == WILC_CFG_RSP) {
- if (wilc->cfg_seq_no == rsp.seq_no)
- complete(&wilc->cfg_event);
- } else if (rsp.type == WILC_CFG_RSP_STATUS) {
- wilc_mac_indicate(wilc, WILC_MAC_INDICATE_STATUS);
-
- } else if (rsp.type == WILC_CFG_RSP_SCAN) {
- wilc_mac_indicate(wilc, WILC_MAC_INDICATE_SCAN);
- }
- }
- }
- offset += tp_len;
- if (offset >= size)
- break;
- } while (1);
kfree(rqe);
} while (1);
@@ -1216,27 +1237,28 @@ int wilc_wlan_cfg_set(struct wilc_vif *vif, int start, u16 wid, u8 *buffer,
offset += ret_size;
wilc->cfg_frame_offset = offset;
- if (commit) {
- netdev_dbg(vif->ndev,
- "[WILC]PACKET Commit with sequence number %d\n",
- wilc->cfg_seq_no);
- netdev_dbg(vif->ndev, "Processing cfg_set()\n");
- wilc->cfg_frame_in_use = 1;
+ if (!commit)
+ return ret_size;
- if (wilc_wlan_cfg_commit(vif, WILC_CFG_SET, drv_handler))
- ret_size = 0;
+ netdev_dbg(vif->ndev,
+ "[WILC]PACKET Commit with sequence number %d\n",
+ wilc->cfg_seq_no);
+ netdev_dbg(vif->ndev, "Processing cfg_set()\n");
+ wilc->cfg_frame_in_use = 1;
- if (!wait_for_completion_timeout(&wilc->cfg_event,
- msecs_to_jiffies(CFG_PKTS_TIMEOUT))) {
- netdev_dbg(vif->ndev, "Set Timed Out\n");
- ret_size = 0;
- }
+ if (wilc_wlan_cfg_commit(vif, WILC_CFG_SET, drv_handler))
+ ret_size = 0;
- wilc->cfg_frame_in_use = 0;
- wilc->cfg_frame_offset = 0;
- wilc->cfg_seq_no += 1;
+ if (!wait_for_completion_timeout(&wilc->cfg_event,
+ msecs_to_jiffies(CFG_PKTS_TIMEOUT))) {
+ netdev_dbg(vif->ndev, "Set Timed Out\n");
+ ret_size = 0;
}
+ wilc->cfg_frame_in_use = 0;
+ wilc->cfg_frame_offset = 0;
+ wilc->cfg_seq_no += 1;
+
return ret_size;
}
@@ -1258,21 +1280,22 @@ int wilc_wlan_cfg_get(struct wilc_vif *vif, int start, u16 wid, int commit,
offset += ret_size;
wilc->cfg_frame_offset = offset;
- if (commit) {
- wilc->cfg_frame_in_use = 1;
+ if (!commit)
+ return ret_size;
- if (wilc_wlan_cfg_commit(vif, WILC_CFG_QUERY, drv_handler))
- ret_size = 0;
+ wilc->cfg_frame_in_use = 1;
- if (!wait_for_completion_timeout(&wilc->cfg_event,
- msecs_to_jiffies(CFG_PKTS_TIMEOUT))) {
- netdev_dbg(vif->ndev, "Get Timed Out\n");
- ret_size = 0;
- }
- wilc->cfg_frame_in_use = 0;
- wilc->cfg_frame_offset = 0;
- wilc->cfg_seq_no += 1;
+ if (wilc_wlan_cfg_commit(vif, WILC_CFG_QUERY, drv_handler))
+ ret_size = 0;
+
+ if (!wait_for_completion_timeout(&wilc->cfg_event,
+ msecs_to_jiffies(CFG_PKTS_TIMEOUT))) {
+ netdev_dbg(vif->ndev, "Get Timed Out\n");
+ ret_size = 0;
}
+ wilc->cfg_frame_in_use = 0;
+ wilc->cfg_frame_offset = 0;
+ wilc->cfg_seq_no += 1;
return ret_size;
}
diff --git a/drivers/staging/wilc1000/wilc_wlan.h b/drivers/staging/wilc1000/wilc_wlan.h
index da7173105497..fa157a67b045 100644
--- a/drivers/staging/wilc1000/wilc_wlan.h
+++ b/drivers/staging/wilc1000/wilc_wlan.h
@@ -195,6 +195,11 @@
#define ENABLE_TX_VMM (SEL_VMM_TBL0 | EN_VMM)
/*time for expiring the completion of cfg packets*/
#define CFG_PKTS_TIMEOUT 2000
+
+#define IS_MANAGMEMENT 0x100
+#define IS_MANAGMEMENT_CALLBACK 0x080
+#define IS_MGMT_STATUS_SUCCES 0x040
+
/********************************************
*
* Debug Type
diff --git a/drivers/staging/wilc1000/wilc_wlan_cfg.c b/drivers/staging/wilc1000/wilc_wlan_cfg.c
index aeb5417f3587..2b44f4cc56b7 100644
--- a/drivers/staging/wilc1000/wilc_wlan_cfg.c
+++ b/drivers/staging/wilc1000/wilc_wlan_cfg.c
@@ -221,7 +221,8 @@ static int wilc_wlan_cfg_set_word(u8 *frame, u32 offset, u16 id, u32 val32)
return 8;
}
-static int wilc_wlan_cfg_set_str(u8 *frame, u32 offset, u16 id, u8 *str, u32 size)
+static int wilc_wlan_cfg_set_str(u8 *frame, u32 offset, u16 id, u8 *str,
+ u32 size)
{
u8 *buf;
@@ -483,20 +484,21 @@ int wilc_wlan_cfg_get_wid_value(u16 wid, u8 *buffer, u32 buffer_size)
} while (1);
} else if (type == CFG_STR_CMD) {
do {
- if (g_cfg_str[i].id == WID_NIL)
+ u32 id = g_cfg_str[i].id;
+
+ if (id == WID_NIL)
break;
- if (g_cfg_str[i].id == wid) {
+ if (id == wid) {
u32 size = g_cfg_str[i].str[0] |
(g_cfg_str[i].str[1] << 8);
if (buffer_size >= size) {
- if (g_cfg_str[i].id == WID_SITE_SURVEY_RESULTS) {
+ if (id == WID_SITE_SURVEY_RESULTS) {
static int toggle;
i += toggle;
toggle ^= 1;
-
}
memcpy(buffer, &g_cfg_str[i].str[2],
size);
@@ -523,9 +525,12 @@ int wilc_wlan_cfg_indicate_rx(struct wilc *wilc, u8 *frame, int size,
frame += 4;
size -= 4;
- /**
- * The valid types of response messages are 'R' (Response), 'I' (Information), and 'N' (Network Information)
- **/
+ /*
+ * The valid types of response messages are
+ * 'R' (Response),
+ * 'I' (Information), and
+ * 'N' (Network Information)
+ */
switch (msg_type) {
case 'R':
diff --git a/drivers/staging/wlan-ng/prism2mgmt.c b/drivers/staging/wlan-ng/prism2mgmt.c
index 78934e435fcf..d7de9e9c47a2 100644
--- a/drivers/staging/wlan-ng/prism2mgmt.c
+++ b/drivers/staging/wlan-ng/prism2mgmt.c
@@ -563,7 +563,7 @@ int prism2mgmt_start(struct wlandevice *wlandev, void *msgp)
/*** STATION ***/
/* Set the REQUIRED config items */
/* SSID */
- pstr = (struct p80211pstrd *)&(msg->ssid.data);
+ pstr = (struct p80211pstrd *)&msg->ssid.data;
prism2mgmt_pstr2bytestr(p2bytestr, pstr);
result = hfa384x_drvr_setconfig(hw, HFA384x_RID_CNFOWNSSID,
bytebuf, HFA384x_RID_CNFOWNSSID_LEN);
diff --git a/drivers/staging/xgifb/XGI_main_26.c b/drivers/staging/xgifb/XGI_main_26.c
index 10107de0119a..eca0b50f0df6 100644
--- a/drivers/staging/xgifb/XGI_main_26.c
+++ b/drivers/staging/xgifb/XGI_main_26.c
@@ -544,41 +544,42 @@ static u8 XGIfb_search_refresh_rate(struct xgifb_video_info *xgifb_info,
yres = XGIbios_mode[xgifb_info->mode_idx].yres;
xgifb_info->rate_idx = 0;
- while ((XGIfb_vrate[i].idx != 0) && (XGIfb_vrate[i].xres <= xres)) {
- if ((XGIfb_vrate[i].xres == xres) &&
- (XGIfb_vrate[i].yres == yres)) {
- if (XGIfb_vrate[i].refresh == rate) {
+
+ while (XGIfb_vrate[i].idx != 0 && XGIfb_vrate[i].xres <= xres) {
+ /* Skip values with xres or yres less than specified */
+ if ((XGIfb_vrate[i].yres != yres) ||
+ (XGIfb_vrate[i].xres != xres)) {
+ i++;
+ continue;
+ }
+ if (XGIfb_vrate[i].refresh == rate) {
+ xgifb_info->rate_idx = XGIfb_vrate[i].idx;
+ break;
+ } else if (XGIfb_vrate[i].refresh > rate) {
+ if (XGIfb_vrate[i].refresh - rate <= 3) {
+ pr_debug("Adjusting rate from %d up to %d\n",
+ rate, XGIfb_vrate[i].refresh);
xgifb_info->rate_idx = XGIfb_vrate[i].idx;
- break;
- } else if (XGIfb_vrate[i].refresh > rate) {
- if ((XGIfb_vrate[i].refresh - rate) <= 3) {
- pr_debug("Adjusting rate from %d up to %d\n",
- rate, XGIfb_vrate[i].refresh);
- xgifb_info->rate_idx =
- XGIfb_vrate[i].idx;
- xgifb_info->refresh_rate =
- XGIfb_vrate[i].refresh;
- } else if (((rate - XGIfb_vrate[i - 1].refresh)
- <= 2) && (XGIfb_vrate[i].idx
- != 1)) {
- pr_debug("Adjusting rate from %d down to %d\n",
- rate,
- XGIfb_vrate[i - 1].refresh);
- xgifb_info->rate_idx =
- XGIfb_vrate[i - 1].idx;
- xgifb_info->refresh_rate =
- XGIfb_vrate[i - 1].refresh;
- }
- break;
- } else if ((rate - XGIfb_vrate[i].refresh) <= 2) {
+ xgifb_info->refresh_rate =
+ XGIfb_vrate[i].refresh;
+ } else if ((XGIfb_vrate[i].idx != 1) &&
+ (rate - XGIfb_vrate[i - 1].refresh <= 2)) {
pr_debug("Adjusting rate from %d down to %d\n",
- rate, XGIfb_vrate[i].refresh);
- xgifb_info->rate_idx = XGIfb_vrate[i].idx;
- break;
+ rate, XGIfb_vrate[i - 1].refresh);
+ xgifb_info->rate_idx = XGIfb_vrate[i - 1].idx;
+ xgifb_info->refresh_rate =
+ XGIfb_vrate[i - 1].refresh;
}
+ break;
+ } else if (rate - XGIfb_vrate[i].refresh <= 2) {
+ pr_debug("Adjusting rate from %d down to %d\n",
+ rate, XGIfb_vrate[i].refresh);
+ xgifb_info->rate_idx = XGIfb_vrate[i].idx;
+ break;
}
i++;
}
+
if (xgifb_info->rate_idx > 0)
return xgifb_info->rate_idx;
pr_info("Unsupported rate %d for %dx%d\n",
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
index 9eb10d34682c..8e223799347a 100644
--- a/drivers/target/iscsi/iscsi_target.c
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -26,6 +26,7 @@
#include <linux/delay.h>
#include <linux/sched/signal.h>
#include <asm/unaligned.h>
+#include <linux/inet.h>
#include <net/ipv6.h>
#include <scsi/scsi_proto.h>
#include <scsi/iscsi_proto.h>
@@ -3291,30 +3292,6 @@ iscsit_send_task_mgt_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
return conn->conn_transport->iscsit_xmit_pdu(conn, cmd, NULL, NULL, 0);
}
-static bool iscsit_check_inaddr_any(struct iscsi_np *np)
-{
- bool ret = false;
-
- if (np->np_sockaddr.ss_family == AF_INET6) {
- const struct sockaddr_in6 sin6 = {
- .sin6_addr = IN6ADDR_ANY_INIT };
- struct sockaddr_in6 *sock_in6 =
- (struct sockaddr_in6 *)&np->np_sockaddr;
-
- if (!memcmp(sock_in6->sin6_addr.s6_addr,
- sin6.sin6_addr.s6_addr, 16))
- ret = true;
- } else {
- struct sockaddr_in * sock_in =
- (struct sockaddr_in *)&np->np_sockaddr;
-
- if (sock_in->sin_addr.s_addr == htonl(INADDR_ANY))
- ret = true;
- }
-
- return ret;
-}
-
#define SENDTARGETS_BUF_LIMIT 32768U
static int
@@ -3393,7 +3370,6 @@ iscsit_build_sendtargets_response(struct iscsi_cmd *cmd,
list_for_each_entry(tpg_np, &tpg->tpg_gnp_list,
tpg_np_list) {
struct iscsi_np *np = tpg_np->tpg_np;
- bool inaddr_any = iscsit_check_inaddr_any(np);
struct sockaddr_storage *sockaddr;
if (np->np_network_transport != network_transport)
@@ -3422,7 +3398,7 @@ iscsit_build_sendtargets_response(struct iscsi_cmd *cmd,
}
}
- if (inaddr_any)
+ if (inet_addr_is_any((struct sockaddr *)&np->np_sockaddr))
sockaddr = &conn->local_sockaddr;
else
sockaddr = &np->np_sockaddr;
diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c
index 64c5a57b92e4..99501785cdc1 100644
--- a/drivers/target/iscsi/iscsi_target_login.c
+++ b/drivers/target/iscsi/iscsi_target_login.c
@@ -1020,7 +1020,7 @@ int iscsit_accept_np(struct iscsi_np *np, struct iscsi_conn *conn)
struct socket *new_sock, *sock = np->np_socket;
struct sockaddr_in sock_in;
struct sockaddr_in6 sock_in6;
- int rc, err;
+ int rc;
rc = kernel_accept(sock, &new_sock, 0);
if (rc < 0)
@@ -1033,8 +1033,8 @@ int iscsit_accept_np(struct iscsi_np *np, struct iscsi_conn *conn)
memset(&sock_in6, 0, sizeof(struct sockaddr_in6));
rc = conn->sock->ops->getname(conn->sock,
- (struct sockaddr *)&sock_in6, &err, 1);
- if (!rc) {
+ (struct sockaddr *)&sock_in6, 1);
+ if (rc >= 0) {
if (!ipv6_addr_v4mapped(&sock_in6.sin6_addr)) {
memcpy(&conn->login_sockaddr, &sock_in6, sizeof(sock_in6));
} else {
@@ -1047,8 +1047,8 @@ int iscsit_accept_np(struct iscsi_np *np, struct iscsi_conn *conn)
}
rc = conn->sock->ops->getname(conn->sock,
- (struct sockaddr *)&sock_in6, &err, 0);
- if (!rc) {
+ (struct sockaddr *)&sock_in6, 0);
+ if (rc >= 0) {
if (!ipv6_addr_v4mapped(&sock_in6.sin6_addr)) {
memcpy(&conn->local_sockaddr, &sock_in6, sizeof(sock_in6));
} else {
@@ -1063,13 +1063,13 @@ int iscsit_accept_np(struct iscsi_np *np, struct iscsi_conn *conn)
memset(&sock_in, 0, sizeof(struct sockaddr_in));
rc = conn->sock->ops->getname(conn->sock,
- (struct sockaddr *)&sock_in, &err, 1);
- if (!rc)
+ (struct sockaddr *)&sock_in, 1);
+ if (rc >= 0)
memcpy(&conn->login_sockaddr, &sock_in, sizeof(sock_in));
rc = conn->sock->ops->getname(conn->sock,
- (struct sockaddr *)&sock_in, &err, 0);
- if (!rc)
+ (struct sockaddr *)&sock_in, 0);
+ if (rc >= 0)
memcpy(&conn->local_sockaddr, &sock_in, sizeof(sock_in));
}
diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c
index 9cd4ffe76c07..60d5b918c4ac 100644
--- a/drivers/target/loopback/tcm_loop.c
+++ b/drivers/target/loopback/tcm_loop.c
@@ -309,7 +309,7 @@ static int tcm_loop_target_reset(struct scsi_cmnd *sc)
static int tcm_loop_slave_alloc(struct scsi_device *sd)
{
- set_bit(QUEUE_FLAG_BIDI, &sd->request_queue->queue_flags);
+ blk_queue_flag_set(QUEUE_FLAG_BIDI, sd->request_queue);
return 0;
}
diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c
index 0d99b242e82e..6cb933ecc084 100644
--- a/drivers/target/target_core_pscsi.c
+++ b/drivers/target/target_core_pscsi.c
@@ -890,6 +890,7 @@ pscsi_map_sg(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents,
bytes = min(bytes, data_len);
if (!bio) {
+new_bio:
nr_vecs = min_t(int, BIO_MAX_PAGES, nr_pages);
nr_pages -= nr_vecs;
/*
@@ -931,6 +932,7 @@ pscsi_map_sg(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents,
* be allocated with pscsi_get_bio() above.
*/
bio = NULL;
+ goto new_bio;
}
data_len -= bytes;
diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index e9843c53fe31..e5fd5ed217da 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -356,6 +356,27 @@ static bool optee_msg_api_uid_is_optee_api(optee_invoke_fn *invoke_fn)
return false;
}
+static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
+{
+ union {
+ struct arm_smccc_res smccc;
+ struct optee_smc_call_get_os_revision_result result;
+ } res = {
+ .result = {
+ .build_id = 0
+ }
+ };
+
+ invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
+ &res.smccc);
+
+ if (res.result.build_id)
+ pr_info("revision %lu.%lu (%08lx)", res.result.major,
+ res.result.minor, res.result.build_id);
+ else
+ pr_info("revision %lu.%lu", res.result.major, res.result.minor);
+}
+
static bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn)
{
union {
@@ -547,6 +568,8 @@ static struct optee *optee_probe(struct device_node *np)
return ERR_PTR(-EINVAL);
}
+ optee_msg_get_os_revision(invoke_fn);
+
if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
pr_warn("api revision mismatch\n");
return ERR_PTR(-EINVAL);
diff --git a/drivers/tee/optee/optee_smc.h b/drivers/tee/optee/optee_smc.h
index 7cd327243ada..bbf0cf028c16 100644
--- a/drivers/tee/optee/optee_smc.h
+++ b/drivers/tee/optee/optee_smc.h
@@ -112,12 +112,20 @@ struct optee_smc_calls_revision_result {
* Trusted OS, not of the API.
*
* Returns revision in a0-1 in the same way as OPTEE_SMC_CALLS_REVISION
- * described above.
+ * described above. May optionally return a 32-bit build identifier in a2,
+ * with zero meaning unspecified.
*/
#define OPTEE_SMC_FUNCID_GET_OS_REVISION OPTEE_MSG_FUNCID_GET_OS_REVISION
#define OPTEE_SMC_CALL_GET_OS_REVISION \
OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_OS_REVISION)
+struct optee_smc_call_get_os_revision_result {
+ unsigned long major;
+ unsigned long minor;
+ unsigned long build_id;
+ unsigned long reserved1;
+};
+
/*
* Call with struct optee_msg_arg as argument
*
diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
index 6c4b200a4560..0124a91c8d71 100644
--- a/drivers/tee/tee_core.c
+++ b/drivers/tee/tee_core.c
@@ -693,7 +693,7 @@ struct tee_device *tee_device_alloc(const struct tee_desc *teedesc,
{
struct tee_device *teedev;
void *ret;
- int rc;
+ int rc, max_id;
int offs = 0;
if (!teedesc || !teedesc->name || !teedesc->ops ||
@@ -707,16 +707,20 @@ struct tee_device *tee_device_alloc(const struct tee_desc *teedesc,
goto err;
}
- if (teedesc->flags & TEE_DESC_PRIVILEGED)
+ max_id = TEE_NUM_DEVICES / 2;
+
+ if (teedesc->flags & TEE_DESC_PRIVILEGED) {
offs = TEE_NUM_DEVICES / 2;
+ max_id = TEE_NUM_DEVICES;
+ }
spin_lock(&driver_lock);
- teedev->id = find_next_zero_bit(dev_mask, TEE_NUM_DEVICES, offs);
- if (teedev->id < TEE_NUM_DEVICES)
+ teedev->id = find_next_zero_bit(dev_mask, max_id, offs);
+ if (teedev->id < max_id)
set_bit(teedev->id, dev_mask);
spin_unlock(&driver_lock);
- if (teedev->id >= TEE_NUM_DEVICES) {
+ if (teedev->id >= max_id) {
ret = ERR_PTR(-ENOMEM);
goto err;
}
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index b6adc54b96f1..82979880f985 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -15,6 +15,13 @@ menuconfig THERMAL
if THERMAL
+config THERMAL_STATISTICS
+ bool "Thermal state transition statistics"
+ help
+ Export thermal state transition statistics information through sysfs.
+
+ If in doubt, say N.
+
config THERMAL_EMERGENCY_POWEROFF_DELAY_MS
int "Emergency poweroff delay in milli-seconds"
depends on THERMAL
diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c
index a67781b7a0b2..ee3a215b333a 100644
--- a/drivers/thermal/imx_thermal.c
+++ b/drivers/thermal/imx_thermal.c
@@ -637,6 +637,9 @@ static int imx_thermal_probe(struct platform_device *pdev)
regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN);
regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP);
+ data->irq_enabled = true;
+ data->mode = THERMAL_DEVICE_ENABLED;
+
ret = devm_request_threaded_irq(&pdev->dev, data->irq,
imx_thermal_alarm_irq, imx_thermal_alarm_irq_thread,
0, "imx_thermal", data);
@@ -649,9 +652,6 @@ static int imx_thermal_probe(struct platform_device *pdev)
return ret;
}
- data->irq_enabled = true;
- data->mode = THERMAL_DEVICE_ENABLED;
-
return 0;
}
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index 2b1b0ba393a4..d64325e078db 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -972,8 +972,8 @@ __thermal_cooling_device_register(struct device_node *np,
cdev->ops = ops;
cdev->updated = false;
cdev->device.class = &thermal_class;
- thermal_cooling_device_setup_sysfs(cdev);
cdev->devdata = devdata;
+ thermal_cooling_device_setup_sysfs(cdev);
dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
result = device_register(&cdev->device);
if (result) {
@@ -1106,6 +1106,7 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev)
ida_simple_remove(&thermal_cdev_ida, cdev->id);
device_unregister(&cdev->device);
+ thermal_cooling_device_destroy_sysfs(cdev);
}
EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister);
diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h
index 27e3b1df7360..5e4150261500 100644
--- a/drivers/thermal/thermal_core.h
+++ b/drivers/thermal/thermal_core.h
@@ -73,6 +73,7 @@ int thermal_build_list_of_policies(char *buf);
int thermal_zone_create_device_groups(struct thermal_zone_device *, int);
void thermal_zone_destroy_device_groups(struct thermal_zone_device *);
void thermal_cooling_device_setup_sysfs(struct thermal_cooling_device *);
+void thermal_cooling_device_destroy_sysfs(struct thermal_cooling_device *cdev);
/* used only at binding time */
ssize_t
thermal_cooling_device_trip_point_show(struct device *,
@@ -84,6 +85,15 @@ ssize_t thermal_cooling_device_weight_store(struct device *,
struct device_attribute *,
const char *, size_t);
+#ifdef CONFIG_THERMAL_STATISTICS
+void thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev,
+ unsigned long new_state);
+#else
+static inline void
+thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev,
+ unsigned long new_state) {}
+#endif /* CONFIG_THERMAL_STATISTICS */
+
#ifdef CONFIG_THERMAL_GOV_STEP_WISE
int thermal_gov_step_wise_register(void);
void thermal_gov_step_wise_unregister(void);
diff --git a/drivers/thermal/thermal_helpers.c b/drivers/thermal/thermal_helpers.c
index 8cdf75adcce1..eb03d7e099bb 100644
--- a/drivers/thermal/thermal_helpers.c
+++ b/drivers/thermal/thermal_helpers.c
@@ -187,7 +187,10 @@ void thermal_cdev_update(struct thermal_cooling_device *cdev)
if (instance->target > target)
target = instance->target;
}
- cdev->ops->set_cur_state(cdev, target);
+
+ if (!cdev->ops->set_cur_state(cdev, target))
+ thermal_cooling_device_stats_update(cdev, target);
+
cdev->updated = true;
mutex_unlock(&cdev->lock);
trace_cdev_update(cdev, target);
diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c
index ba81c9080f6e..23b5e0a709b0 100644
--- a/drivers/thermal/thermal_sysfs.c
+++ b/drivers/thermal/thermal_sysfs.c
@@ -20,6 +20,7 @@
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/string.h>
+#include <linux/jiffies.h>
#include "thermal_core.h"
@@ -721,6 +722,7 @@ thermal_cooling_device_cur_state_store(struct device *dev,
result = cdev->ops->set_cur_state(cdev, state);
if (result)
return result;
+ thermal_cooling_device_stats_update(cdev, state);
return count;
}
@@ -745,14 +747,237 @@ static const struct attribute_group cooling_device_attr_group = {
static const struct attribute_group *cooling_device_attr_groups[] = {
&cooling_device_attr_group,
+ NULL, /* Space allocated for cooling_device_stats_attr_group */
NULL,
};
+#ifdef CONFIG_THERMAL_STATISTICS
+struct cooling_dev_stats {
+ spinlock_t lock;
+ unsigned int total_trans;
+ unsigned long state;
+ unsigned long max_states;
+ ktime_t last_time;
+ ktime_t *time_in_state;
+ unsigned int *trans_table;
+};
+
+static void update_time_in_state(struct cooling_dev_stats *stats)
+{
+ ktime_t now = ktime_get(), delta;
+
+ delta = ktime_sub(now, stats->last_time);
+ stats->time_in_state[stats->state] =
+ ktime_add(stats->time_in_state[stats->state], delta);
+ stats->last_time = now;
+}
+
+void thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev,
+ unsigned long new_state)
+{
+ struct cooling_dev_stats *stats = cdev->stats;
+
+ spin_lock(&stats->lock);
+
+ if (stats->state == new_state)
+ goto unlock;
+
+ update_time_in_state(stats);
+ stats->trans_table[stats->state * stats->max_states + new_state]++;
+ stats->state = new_state;
+ stats->total_trans++;
+
+unlock:
+ spin_unlock(&stats->lock);
+}
+
+static ssize_t
+thermal_cooling_device_total_trans_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct thermal_cooling_device *cdev = to_cooling_device(dev);
+ struct cooling_dev_stats *stats = cdev->stats;
+ int ret;
+
+ spin_lock(&stats->lock);
+ ret = sprintf(buf, "%u\n", stats->total_trans);
+ spin_unlock(&stats->lock);
+
+ return ret;
+}
+
+static ssize_t
+thermal_cooling_device_time_in_state_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct thermal_cooling_device *cdev = to_cooling_device(dev);
+ struct cooling_dev_stats *stats = cdev->stats;
+ ssize_t len = 0;
+ int i;
+
+ spin_lock(&stats->lock);
+ update_time_in_state(stats);
+
+ for (i = 0; i < stats->max_states; i++) {
+ len += sprintf(buf + len, "state%u\t%llu\n", i,
+ ktime_to_ms(stats->time_in_state[i]));
+ }
+ spin_unlock(&stats->lock);
+
+ return len;
+}
+
+static ssize_t
+thermal_cooling_device_reset_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct thermal_cooling_device *cdev = to_cooling_device(dev);
+ struct cooling_dev_stats *stats = cdev->stats;
+ int i, states = stats->max_states;
+
+ spin_lock(&stats->lock);
+
+ stats->total_trans = 0;
+ stats->last_time = ktime_get();
+ memset(stats->trans_table, 0,
+ states * states * sizeof(*stats->trans_table));
+
+ for (i = 0; i < stats->max_states; i++)
+ stats->time_in_state[i] = ktime_set(0, 0);
+
+ spin_unlock(&stats->lock);
+
+ return count;
+}
+
+static ssize_t
+thermal_cooling_device_trans_table_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct thermal_cooling_device *cdev = to_cooling_device(dev);
+ struct cooling_dev_stats *stats = cdev->stats;
+ ssize_t len = 0;
+ int i, j;
+
+ len += snprintf(buf + len, PAGE_SIZE - len, " From : To\n");
+ len += snprintf(buf + len, PAGE_SIZE - len, " : ");
+ for (i = 0; i < stats->max_states; i++) {
+ if (len >= PAGE_SIZE)
+ break;
+ len += snprintf(buf + len, PAGE_SIZE - len, "state%2u ", i);
+ }
+ if (len >= PAGE_SIZE)
+ return PAGE_SIZE;
+
+ len += snprintf(buf + len, PAGE_SIZE - len, "\n");
+
+ for (i = 0; i < stats->max_states; i++) {
+ if (len >= PAGE_SIZE)
+ break;
+
+ len += snprintf(buf + len, PAGE_SIZE - len, "state%2u:", i);
+
+ for (j = 0; j < stats->max_states; j++) {
+ if (len >= PAGE_SIZE)
+ break;
+ len += snprintf(buf + len, PAGE_SIZE - len, "%8u ",
+ stats->trans_table[i * stats->max_states + j]);
+ }
+ if (len >= PAGE_SIZE)
+ break;
+ len += snprintf(buf + len, PAGE_SIZE - len, "\n");
+ }
+
+ if (len >= PAGE_SIZE) {
+ pr_warn_once("Thermal transition table exceeds PAGE_SIZE. Disabling\n");
+ return -EFBIG;
+ }
+ return len;
+}
+
+static DEVICE_ATTR(total_trans, 0444, thermal_cooling_device_total_trans_show,
+ NULL);
+static DEVICE_ATTR(time_in_state_ms, 0444,
+ thermal_cooling_device_time_in_state_show, NULL);
+static DEVICE_ATTR(reset, 0200, NULL, thermal_cooling_device_reset_store);
+static DEVICE_ATTR(trans_table, 0444,
+ thermal_cooling_device_trans_table_show, NULL);
+
+static struct attribute *cooling_device_stats_attrs[] = {
+ &dev_attr_total_trans.attr,
+ &dev_attr_time_in_state_ms.attr,
+ &dev_attr_reset.attr,
+ &dev_attr_trans_table.attr,
+ NULL
+};
+
+static const struct attribute_group cooling_device_stats_attr_group = {
+ .attrs = cooling_device_stats_attrs,
+ .name = "stats"
+};
+
+static void cooling_device_stats_setup(struct thermal_cooling_device *cdev)
+{
+ struct cooling_dev_stats *stats;
+ unsigned long states;
+ int var;
+
+ if (cdev->ops->get_max_state(cdev, &states))
+ return;
+
+ states++; /* Total number of states is highest state + 1 */
+
+ var = sizeof(*stats);
+ var += sizeof(*stats->time_in_state) * states;
+ var += sizeof(*stats->trans_table) * states * states;
+
+ stats = kzalloc(var, GFP_KERNEL);
+ if (!stats)
+ return;
+
+ stats->time_in_state = (ktime_t *)(stats + 1);
+ stats->trans_table = (unsigned int *)(stats->time_in_state + states);
+ cdev->stats = stats;
+ stats->last_time = ktime_get();
+ stats->max_states = states;
+
+ spin_lock_init(&stats->lock);
+
+ /* Fill the empty slot left in cooling_device_attr_groups */
+ var = ARRAY_SIZE(cooling_device_attr_groups) - 2;
+ cooling_device_attr_groups[var] = &cooling_device_stats_attr_group;
+}
+
+static void cooling_device_stats_destroy(struct thermal_cooling_device *cdev)
+{
+ kfree(cdev->stats);
+ cdev->stats = NULL;
+}
+
+#else
+
+static inline void
+cooling_device_stats_setup(struct thermal_cooling_device *cdev) {}
+static inline void
+cooling_device_stats_destroy(struct thermal_cooling_device *cdev) {}
+
+#endif /* CONFIG_THERMAL_STATISTICS */
+
void thermal_cooling_device_setup_sysfs(struct thermal_cooling_device *cdev)
{
+ cooling_device_stats_setup(cdev);
cdev->device.groups = cooling_device_attr_groups;
}
+void thermal_cooling_device_destroy_sysfs(struct thermal_cooling_device *cdev)
+{
+ cooling_device_stats_destroy(cdev);
+}
+
/* these helper will be used only at the time of bindig */
ssize_t
thermal_cooling_device_trip_point_show(struct device *dev,
diff --git a/drivers/thunderbolt/dma_port.c b/drivers/thunderbolt/dma_port.c
index af6dde347bee..f2701194f810 100644
--- a/drivers/thunderbolt/dma_port.c
+++ b/drivers/thunderbolt/dma_port.c
@@ -170,24 +170,22 @@ static int dma_port_write(struct tb_ctl *ctl, const void *buffer, u64 route,
static int dma_find_port(struct tb_switch *sw)
{
- int port, ret;
- u32 type;
+ static const int ports[] = { 3, 5, 7 };
+ int i;
/*
- * The DMA (NHI) port is either 3 or 5 depending on the
- * controller. Try both starting from 5 which is more common.
+ * The DMA (NHI) port is either 3, 5 or 7 depending on the
+ * controller. Try all of them.
*/
- port = 5;
- ret = dma_port_read(sw->tb->ctl, &type, tb_route(sw), port, 2, 1,
- DMA_PORT_TIMEOUT);
- if (!ret && (type & 0xffffff) == TB_TYPE_NHI)
- return port;
-
- port = 3;
- ret = dma_port_read(sw->tb->ctl, &type, tb_route(sw), port, 2, 1,
- DMA_PORT_TIMEOUT);
- if (!ret && (type & 0xffffff) == TB_TYPE_NHI)
- return port;
+ for (i = 0; i < ARRAY_SIZE(ports); i++) {
+ u32 type;
+ int ret;
+
+ ret = dma_port_read(sw->tb->ctl, &type, tb_route(sw), ports[i],
+ 2, 1, DMA_PORT_TIMEOUT);
+ if (!ret && (type & 0xffffff) == TB_TYPE_NHI)
+ return ports[i];
+ }
return -ENODEV;
}
diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c
index 9b90115319ce..6281266b8ec0 100644
--- a/drivers/thunderbolt/domain.c
+++ b/drivers/thunderbolt/domain.c
@@ -117,23 +117,151 @@ static const char * const tb_security_names[] = {
[TB_SECURITY_USER] = "user",
[TB_SECURITY_SECURE] = "secure",
[TB_SECURITY_DPONLY] = "dponly",
+ [TB_SECURITY_USBONLY] = "usbonly",
};
+static ssize_t boot_acl_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct tb *tb = container_of(dev, struct tb, dev);
+ uuid_t *uuids;
+ ssize_t ret;
+ int i;
+
+ uuids = kcalloc(tb->nboot_acl, sizeof(uuid_t), GFP_KERNEL);
+ if (!uuids)
+ return -ENOMEM;
+
+ if (mutex_lock_interruptible(&tb->lock)) {
+ ret = -ERESTARTSYS;
+ goto out;
+ }
+ ret = tb->cm_ops->get_boot_acl(tb, uuids, tb->nboot_acl);
+ if (ret) {
+ mutex_unlock(&tb->lock);
+ goto out;
+ }
+ mutex_unlock(&tb->lock);
+
+ for (ret = 0, i = 0; i < tb->nboot_acl; i++) {
+ if (!uuid_is_null(&uuids[i]))
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "%pUb",
+ &uuids[i]);
+
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s",
+ i < tb->nboot_acl - 1 ? "," : "\n");
+ }
+
+out:
+ kfree(uuids);
+ return ret;
+}
+
+static ssize_t boot_acl_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct tb *tb = container_of(dev, struct tb, dev);
+ char *str, *s, *uuid_str;
+ ssize_t ret = 0;
+ uuid_t *acl;
+ int i = 0;
+
+ /*
+ * Make sure the value is not bigger than tb->nboot_acl * UUID
+ * length + commas and optional "\n". Also the smallest allowable
+ * string is tb->nboot_acl * ",".
+ */
+ if (count > (UUID_STRING_LEN + 1) * tb->nboot_acl + 1)
+ return -EINVAL;
+ if (count < tb->nboot_acl - 1)
+ return -EINVAL;
+
+ str = kstrdup(buf, GFP_KERNEL);
+ if (!str)
+ return -ENOMEM;
+
+ acl = kcalloc(tb->nboot_acl, sizeof(uuid_t), GFP_KERNEL);
+ if (!acl) {
+ ret = -ENOMEM;
+ goto err_free_str;
+ }
+
+ uuid_str = strim(str);
+ while ((s = strsep(&uuid_str, ",")) != NULL && i < tb->nboot_acl) {
+ size_t len = strlen(s);
+
+ if (len) {
+ if (len != UUID_STRING_LEN) {
+ ret = -EINVAL;
+ goto err_free_acl;
+ }
+ ret = uuid_parse(s, &acl[i]);
+ if (ret)
+ goto err_free_acl;
+ }
+
+ i++;
+ }
+
+ if (s || i < tb->nboot_acl) {
+ ret = -EINVAL;
+ goto err_free_acl;
+ }
+
+ if (mutex_lock_interruptible(&tb->lock)) {
+ ret = -ERESTARTSYS;
+ goto err_free_acl;
+ }
+ ret = tb->cm_ops->set_boot_acl(tb, acl, tb->nboot_acl);
+ mutex_unlock(&tb->lock);
+
+err_free_acl:
+ kfree(acl);
+err_free_str:
+ kfree(str);
+
+ return ret ?: count;
+}
+static DEVICE_ATTR_RW(boot_acl);
+
static ssize_t security_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct tb *tb = container_of(dev, struct tb, dev);
+ const char *name = "unknown";
- return sprintf(buf, "%s\n", tb_security_names[tb->security_level]);
+ if (tb->security_level < ARRAY_SIZE(tb_security_names))
+ name = tb_security_names[tb->security_level];
+
+ return sprintf(buf, "%s\n", name);
}
static DEVICE_ATTR_RO(security);
static struct attribute *domain_attrs[] = {
+ &dev_attr_boot_acl.attr,
&dev_attr_security.attr,
NULL,
};
+static umode_t domain_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct tb *tb = container_of(dev, struct tb, dev);
+
+ if (attr == &dev_attr_boot_acl.attr) {
+ if (tb->nboot_acl &&
+ tb->cm_ops->get_boot_acl &&
+ tb->cm_ops->set_boot_acl)
+ return attr->mode;
+ return 0;
+ }
+
+ return attr->mode;
+}
+
static struct attribute_group domain_attr_group = {
+ .is_visible = domain_attr_is_visible,
.attrs = domain_attrs,
};
diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c
index ab02d13f40b7..2d2ceda9aa26 100644
--- a/drivers/thunderbolt/icm.c
+++ b/drivers/thunderbolt/icm.c
@@ -41,7 +41,8 @@
#define PHY_PORT_CS1_LINK_STATE_MASK GENMASK(29, 26)
#define PHY_PORT_CS1_LINK_STATE_SHIFT 26
-#define ICM_TIMEOUT 5000 /* ms */
+#define ICM_TIMEOUT 5000 /* ms */
+#define ICM_APPROVE_TIMEOUT 10000 /* ms */
#define ICM_MAX_LINK 4
#define ICM_MAX_DEPTH 6
@@ -55,9 +56,11 @@
* @vnd_cap: Vendor defined capability where PCIe2CIO mailbox resides
* (only set when @upstream_port is not %NULL)
* @safe_mode: ICM is in safe mode
+ * @max_boot_acl: Maximum number of preboot ACL entries (%0 if not supported)
* @is_supported: Checks if we can support ICM on this controller
* @get_mode: Read and return the ICM firmware mode (optional)
* @get_route: Find a route string for given switch
+ * @driver_ready: Send driver ready message to ICM
* @device_connected: Handle device connected ICM message
* @device_disconnected: Handle device disconnected ICM message
* @xdomain_connected - Handle XDomain connected ICM message
@@ -67,11 +70,15 @@ struct icm {
struct mutex request_lock;
struct delayed_work rescan_work;
struct pci_dev *upstream_port;
+ size_t max_boot_acl;
int vnd_cap;
bool safe_mode;
bool (*is_supported)(struct tb *tb);
int (*get_mode)(struct tb *tb);
int (*get_route)(struct tb *tb, u8 link, u8 depth, u64 *route);
+ int (*driver_ready)(struct tb *tb,
+ enum tb_security_level *security_level,
+ size_t *nboot_acl);
void (*device_connected)(struct tb *tb,
const struct icm_pkg_header *hdr);
void (*device_disconnected)(struct tb *tb,
@@ -111,6 +118,12 @@ static inline u64 get_route(u32 route_hi, u32 route_lo)
return (u64)route_hi << 32 | route_lo;
}
+static inline u64 get_parent_route(u64 route)
+{
+ int depth = tb_route_length(route);
+ return depth ? route & ~(0xffULL << (depth - 1) * TB_ROUTE_SHIFT) : 0;
+}
+
static bool icm_match(const struct tb_cfg_request *req,
const struct ctl_pkg *pkg)
{
@@ -245,6 +258,28 @@ err_free:
return ret;
}
+static int
+icm_fr_driver_ready(struct tb *tb, enum tb_security_level *security_level,
+ size_t *nboot_acl)
+{
+ struct icm_fr_pkg_driver_ready_response reply;
+ struct icm_pkg_driver_ready request = {
+ .hdr.code = ICM_DRIVER_READY,
+ };
+ int ret;
+
+ memset(&reply, 0, sizeof(reply));
+ ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
+ 1, ICM_TIMEOUT);
+ if (ret)
+ return ret;
+
+ if (security_level)
+ *security_level = reply.security_level & ICM_FR_SLEVEL_MASK;
+
+ return 0;
+}
+
static int icm_fr_approve_switch(struct tb *tb, struct tb_switch *sw)
{
struct icm_fr_pkg_approve_device request;
@@ -260,7 +295,7 @@ static int icm_fr_approve_switch(struct tb *tb, struct tb_switch *sw)
memset(&reply, 0, sizeof(reply));
/* Use larger timeout as establishing tunnels can take some time */
ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
- 1, 10000);
+ 1, ICM_APPROVE_TIMEOUT);
if (ret)
return ret;
@@ -374,6 +409,59 @@ static int icm_fr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd)
return 0;
}
+static void add_switch(struct tb_switch *parent_sw, u64 route,
+ const uuid_t *uuid, u8 connection_id, u8 connection_key,
+ u8 link, u8 depth, enum tb_security_level security_level,
+ bool authorized, bool boot)
+{
+ struct tb_switch *sw;
+
+ sw = tb_switch_alloc(parent_sw->tb, &parent_sw->dev, route);
+ if (!sw)
+ return;
+
+ sw->uuid = kmemdup(uuid, sizeof(*uuid), GFP_KERNEL);
+ sw->connection_id = connection_id;
+ sw->connection_key = connection_key;
+ sw->link = link;
+ sw->depth = depth;
+ sw->authorized = authorized;
+ sw->security_level = security_level;
+ sw->boot = boot;
+
+ /* Link the two switches now */
+ tb_port_at(route, parent_sw)->remote = tb_upstream_port(sw);
+ tb_upstream_port(sw)->remote = tb_port_at(route, parent_sw);
+
+ if (tb_switch_add(sw)) {
+ tb_port_at(tb_route(sw), parent_sw)->remote = NULL;
+ tb_switch_put(sw);
+ return;
+ }
+}
+
+static void update_switch(struct tb_switch *parent_sw, struct tb_switch *sw,
+ u64 route, u8 connection_id, u8 connection_key,
+ u8 link, u8 depth, bool boot)
+{
+ /* Disconnect from parent */
+ tb_port_at(tb_route(sw), parent_sw)->remote = NULL;
+ /* Re-connect via updated port*/
+ tb_port_at(route, parent_sw)->remote = tb_upstream_port(sw);
+
+ /* Update with the new addressing information */
+ sw->config.route_hi = upper_32_bits(route);
+ sw->config.route_lo = lower_32_bits(route);
+ sw->connection_id = connection_id;
+ sw->connection_key = connection_key;
+ sw->link = link;
+ sw->depth = depth;
+ sw->boot = boot;
+
+ /* This switch still exists */
+ sw->is_unplugged = false;
+}
+
static void remove_switch(struct tb_switch *sw)
{
struct tb_switch *parent_sw;
@@ -383,15 +471,52 @@ static void remove_switch(struct tb_switch *sw)
tb_switch_remove(sw);
}
+static void add_xdomain(struct tb_switch *sw, u64 route,
+ const uuid_t *local_uuid, const uuid_t *remote_uuid,
+ u8 link, u8 depth)
+{
+ struct tb_xdomain *xd;
+
+ xd = tb_xdomain_alloc(sw->tb, &sw->dev, route, local_uuid, remote_uuid);
+ if (!xd)
+ return;
+
+ xd->link = link;
+ xd->depth = depth;
+
+ tb_port_at(route, sw)->xdomain = xd;
+
+ tb_xdomain_add(xd);
+}
+
+static void update_xdomain(struct tb_xdomain *xd, u64 route, u8 link)
+{
+ xd->link = link;
+ xd->route = route;
+ xd->is_unplugged = false;
+}
+
+static void remove_xdomain(struct tb_xdomain *xd)
+{
+ struct tb_switch *sw;
+
+ sw = tb_to_switch(xd->dev.parent);
+ tb_port_at(xd->route, sw)->xdomain = NULL;
+ tb_xdomain_remove(xd);
+}
+
static void
icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
{
const struct icm_fr_event_device_connected *pkg =
(const struct icm_fr_event_device_connected *)hdr;
+ enum tb_security_level security_level;
struct tb_switch *sw, *parent_sw;
struct icm *icm = tb_priv(tb);
bool authorized = false;
+ struct tb_xdomain *xd;
u8 link, depth;
+ bool boot;
u64 route;
int ret;
@@ -399,6 +524,15 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
depth = (pkg->link_info & ICM_LINK_INFO_DEPTH_MASK) >>
ICM_LINK_INFO_DEPTH_SHIFT;
authorized = pkg->link_info & ICM_LINK_INFO_APPROVED;
+ security_level = (pkg->hdr.flags & ICM_FLAGS_SLEVEL_MASK) >>
+ ICM_FLAGS_SLEVEL_SHIFT;
+ boot = pkg->link_info & ICM_LINK_INFO_BOOT;
+
+ if (pkg->link_info & ICM_LINK_INFO_REJECTED) {
+ tb_info(tb, "switch at %u.%u was rejected by ICM firmware because topology limit exceeded\n",
+ link, depth);
+ return;
+ }
ret = icm->get_route(tb, link, depth, &route);
if (ret) {
@@ -425,16 +559,8 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
*/
if (sw->depth == depth && sw_phy_port == phy_port &&
!!sw->authorized == authorized) {
- tb_port_at(tb_route(sw), parent_sw)->remote = NULL;
- tb_port_at(route, parent_sw)->remote =
- tb_upstream_port(sw);
- sw->config.route_hi = upper_32_bits(route);
- sw->config.route_lo = lower_32_bits(route);
- sw->connection_id = pkg->connection_id;
- sw->connection_key = pkg->connection_key;
- sw->link = link;
- sw->depth = depth;
- sw->is_unplugged = false;
+ update_switch(parent_sw, sw, route, pkg->connection_id,
+ pkg->connection_key, link, depth, boot);
tb_switch_put(sw);
return;
}
@@ -467,6 +593,13 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
tb_switch_put(sw);
}
+ /* Remove existing XDomain connection if found */
+ xd = tb_xdomain_find_by_link_depth(tb, link, depth);
+ if (xd) {
+ remove_xdomain(xd);
+ tb_xdomain_put(xd);
+ }
+
parent_sw = tb_switch_find_by_link_depth(tb, link, depth - 1);
if (!parent_sw) {
tb_err(tb, "failed to find parent switch for %u.%u\n",
@@ -474,30 +607,10 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
return;
}
- sw = tb_switch_alloc(tb, &parent_sw->dev, route);
- if (!sw) {
- tb_switch_put(parent_sw);
- return;
- }
-
- sw->uuid = kmemdup(&pkg->ep_uuid, sizeof(pkg->ep_uuid), GFP_KERNEL);
- sw->connection_id = pkg->connection_id;
- sw->connection_key = pkg->connection_key;
- sw->link = link;
- sw->depth = depth;
- sw->authorized = authorized;
- sw->security_level = (pkg->hdr.flags & ICM_FLAGS_SLEVEL_MASK) >>
- ICM_FLAGS_SLEVEL_SHIFT;
-
- /* Link the two switches now */
- tb_port_at(route, parent_sw)->remote = tb_upstream_port(sw);
- tb_upstream_port(sw)->remote = tb_port_at(route, parent_sw);
+ add_switch(parent_sw, route, &pkg->ep_uuid, pkg->connection_id,
+ pkg->connection_key, link, depth, security_level,
+ authorized, boot);
- ret = tb_switch_add(sw);
- if (ret) {
- tb_port_at(tb_route(sw), parent_sw)->remote = NULL;
- tb_switch_put(sw);
- }
tb_switch_put(parent_sw);
}
@@ -529,15 +642,6 @@ icm_fr_device_disconnected(struct tb *tb, const struct icm_pkg_header *hdr)
tb_switch_put(sw);
}
-static void remove_xdomain(struct tb_xdomain *xd)
-{
- struct tb_switch *sw;
-
- sw = tb_to_switch(xd->dev.parent);
- tb_port_at(xd->route, sw)->xdomain = NULL;
- tb_xdomain_remove(xd);
-}
-
static void
icm_fr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr)
{
@@ -577,9 +681,7 @@ icm_fr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr)
phy_port = phy_port_from_route(route, depth);
if (xd->depth == depth && xd_phy_port == phy_port) {
- xd->link = link;
- xd->route = route;
- xd->is_unplugged = false;
+ update_xdomain(xd, route, link);
tb_xdomain_put(xd);
return;
}
@@ -629,19 +731,8 @@ icm_fr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr)
return;
}
- xd = tb_xdomain_alloc(sw->tb, &sw->dev, route,
- &pkg->local_uuid, &pkg->remote_uuid);
- if (!xd) {
- tb_switch_put(sw);
- return;
- }
-
- xd->link = link;
- xd->depth = depth;
-
- tb_port_at(route, sw)->xdomain = xd;
-
- tb_xdomain_add(xd);
+ add_xdomain(sw, route, &pkg->local_uuid, &pkg->remote_uuid, link,
+ depth);
tb_switch_put(sw);
}
@@ -664,6 +755,351 @@ icm_fr_xdomain_disconnected(struct tb *tb, const struct icm_pkg_header *hdr)
}
}
+static int
+icm_tr_driver_ready(struct tb *tb, enum tb_security_level *security_level,
+ size_t *nboot_acl)
+{
+ struct icm_tr_pkg_driver_ready_response reply;
+ struct icm_pkg_driver_ready request = {
+ .hdr.code = ICM_DRIVER_READY,
+ };
+ int ret;
+
+ memset(&reply, 0, sizeof(reply));
+ ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
+ 1, 20000);
+ if (ret)
+ return ret;
+
+ if (security_level)
+ *security_level = reply.info & ICM_TR_INFO_SLEVEL_MASK;
+ if (nboot_acl)
+ *nboot_acl = (reply.info & ICM_TR_INFO_BOOT_ACL_MASK) >>
+ ICM_TR_INFO_BOOT_ACL_SHIFT;
+ return 0;
+}
+
+static int icm_tr_approve_switch(struct tb *tb, struct tb_switch *sw)
+{
+ struct icm_tr_pkg_approve_device request;
+ struct icm_tr_pkg_approve_device reply;
+ int ret;
+
+ memset(&request, 0, sizeof(request));
+ memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid));
+ request.hdr.code = ICM_APPROVE_DEVICE;
+ request.route_lo = sw->config.route_lo;
+ request.route_hi = sw->config.route_hi;
+ request.connection_id = sw->connection_id;
+
+ memset(&reply, 0, sizeof(reply));
+ ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
+ 1, ICM_APPROVE_TIMEOUT);
+ if (ret)
+ return ret;
+
+ if (reply.hdr.flags & ICM_FLAGS_ERROR) {
+ tb_warn(tb, "PCIe tunnel creation failed\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int icm_tr_add_switch_key(struct tb *tb, struct tb_switch *sw)
+{
+ struct icm_tr_pkg_add_device_key_response reply;
+ struct icm_tr_pkg_add_device_key request;
+ int ret;
+
+ memset(&request, 0, sizeof(request));
+ memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid));
+ request.hdr.code = ICM_ADD_DEVICE_KEY;
+ request.route_lo = sw->config.route_lo;
+ request.route_hi = sw->config.route_hi;
+ request.connection_id = sw->connection_id;
+ memcpy(request.key, sw->key, TB_SWITCH_KEY_SIZE);
+
+ memset(&reply, 0, sizeof(reply));
+ ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
+ 1, ICM_TIMEOUT);
+ if (ret)
+ return ret;
+
+ if (reply.hdr.flags & ICM_FLAGS_ERROR) {
+ tb_warn(tb, "Adding key to switch failed\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int icm_tr_challenge_switch_key(struct tb *tb, struct tb_switch *sw,
+ const u8 *challenge, u8 *response)
+{
+ struct icm_tr_pkg_challenge_device_response reply;
+ struct icm_tr_pkg_challenge_device request;
+ int ret;
+
+ memset(&request, 0, sizeof(request));
+ memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid));
+ request.hdr.code = ICM_CHALLENGE_DEVICE;
+ request.route_lo = sw->config.route_lo;
+ request.route_hi = sw->config.route_hi;
+ request.connection_id = sw->connection_id;
+ memcpy(request.challenge, challenge, TB_SWITCH_KEY_SIZE);
+
+ memset(&reply, 0, sizeof(reply));
+ ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
+ 1, ICM_TIMEOUT);
+ if (ret)
+ return ret;
+
+ if (reply.hdr.flags & ICM_FLAGS_ERROR)
+ return -EKEYREJECTED;
+ if (reply.hdr.flags & ICM_FLAGS_NO_KEY)
+ return -ENOKEY;
+
+ memcpy(response, reply.response, TB_SWITCH_KEY_SIZE);
+
+ return 0;
+}
+
+static int icm_tr_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd)
+{
+ struct icm_tr_pkg_approve_xdomain_response reply;
+ struct icm_tr_pkg_approve_xdomain request;
+ int ret;
+
+ memset(&request, 0, sizeof(request));
+ request.hdr.code = ICM_APPROVE_XDOMAIN;
+ request.route_hi = upper_32_bits(xd->route);
+ request.route_lo = lower_32_bits(xd->route);
+ request.transmit_path = xd->transmit_path;
+ request.transmit_ring = xd->transmit_ring;
+ request.receive_path = xd->receive_path;
+ request.receive_ring = xd->receive_ring;
+ memcpy(&request.remote_uuid, xd->remote_uuid, sizeof(*xd->remote_uuid));
+
+ memset(&reply, 0, sizeof(reply));
+ ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
+ 1, ICM_TIMEOUT);
+ if (ret)
+ return ret;
+
+ if (reply.hdr.flags & ICM_FLAGS_ERROR)
+ return -EIO;
+
+ return 0;
+}
+
+static int icm_tr_xdomain_tear_down(struct tb *tb, struct tb_xdomain *xd,
+ int stage)
+{
+ struct icm_tr_pkg_disconnect_xdomain_response reply;
+ struct icm_tr_pkg_disconnect_xdomain request;
+ int ret;
+
+ memset(&request, 0, sizeof(request));
+ request.hdr.code = ICM_DISCONNECT_XDOMAIN;
+ request.stage = stage;
+ request.route_hi = upper_32_bits(xd->route);
+ request.route_lo = lower_32_bits(xd->route);
+ memcpy(&request.remote_uuid, xd->remote_uuid, sizeof(*xd->remote_uuid));
+
+ memset(&reply, 0, sizeof(reply));
+ ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
+ 1, ICM_TIMEOUT);
+ if (ret)
+ return ret;
+
+ if (reply.hdr.flags & ICM_FLAGS_ERROR)
+ return -EIO;
+
+ return 0;
+}
+
+static int icm_tr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd)
+{
+ int ret;
+
+ ret = icm_tr_xdomain_tear_down(tb, xd, 1);
+ if (ret)
+ return ret;
+
+ usleep_range(10, 50);
+ return icm_tr_xdomain_tear_down(tb, xd, 2);
+}
+
+static void
+icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
+{
+ const struct icm_tr_event_device_connected *pkg =
+ (const struct icm_tr_event_device_connected *)hdr;
+ enum tb_security_level security_level;
+ struct tb_switch *sw, *parent_sw;
+ struct tb_xdomain *xd;
+ bool authorized, boot;
+ u64 route;
+
+ /*
+ * Currently we don't use the QoS information coming with the
+ * device connected message so simply just ignore that extra
+ * packet for now.
+ */
+ if (pkg->hdr.packet_id)
+ return;
+
+ /*
+ * After NVM upgrade adding root switch device fails because we
+ * initiated reset. During that time ICM might still send device
+ * connected message which we ignore here.
+ */
+ if (!tb->root_switch)
+ return;
+
+ route = get_route(pkg->route_hi, pkg->route_lo);
+ authorized = pkg->link_info & ICM_LINK_INFO_APPROVED;
+ security_level = (pkg->hdr.flags & ICM_FLAGS_SLEVEL_MASK) >>
+ ICM_FLAGS_SLEVEL_SHIFT;
+ boot = pkg->link_info & ICM_LINK_INFO_BOOT;
+
+ if (pkg->link_info & ICM_LINK_INFO_REJECTED) {
+ tb_info(tb, "switch at %llx was rejected by ICM firmware because topology limit exceeded\n",
+ route);
+ return;
+ }
+
+ sw = tb_switch_find_by_uuid(tb, &pkg->ep_uuid);
+ if (sw) {
+ /* Update the switch if it is still in the same place */
+ if (tb_route(sw) == route && !!sw->authorized == authorized) {
+ parent_sw = tb_to_switch(sw->dev.parent);
+ update_switch(parent_sw, sw, route, pkg->connection_id,
+ 0, 0, 0, boot);
+ tb_switch_put(sw);
+ return;
+ }
+
+ remove_switch(sw);
+ tb_switch_put(sw);
+ }
+
+ /* Another switch with the same address */
+ sw = tb_switch_find_by_route(tb, route);
+ if (sw) {
+ remove_switch(sw);
+ tb_switch_put(sw);
+ }
+
+ /* XDomain connection with the same address */
+ xd = tb_xdomain_find_by_route(tb, route);
+ if (xd) {
+ remove_xdomain(xd);
+ tb_xdomain_put(xd);
+ }
+
+ parent_sw = tb_switch_find_by_route(tb, get_parent_route(route));
+ if (!parent_sw) {
+ tb_err(tb, "failed to find parent switch for %llx\n", route);
+ return;
+ }
+
+ add_switch(parent_sw, route, &pkg->ep_uuid, pkg->connection_id,
+ 0, 0, 0, security_level, authorized, boot);
+
+ tb_switch_put(parent_sw);
+}
+
+static void
+icm_tr_device_disconnected(struct tb *tb, const struct icm_pkg_header *hdr)
+{
+ const struct icm_tr_event_device_disconnected *pkg =
+ (const struct icm_tr_event_device_disconnected *)hdr;
+ struct tb_switch *sw;
+ u64 route;
+
+ route = get_route(pkg->route_hi, pkg->route_lo);
+
+ sw = tb_switch_find_by_route(tb, route);
+ if (!sw) {
+ tb_warn(tb, "no switch exists at %llx, ignoring\n", route);
+ return;
+ }
+
+ remove_switch(sw);
+ tb_switch_put(sw);
+}
+
+static void
+icm_tr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr)
+{
+ const struct icm_tr_event_xdomain_connected *pkg =
+ (const struct icm_tr_event_xdomain_connected *)hdr;
+ struct tb_xdomain *xd;
+ struct tb_switch *sw;
+ u64 route;
+
+ if (!tb->root_switch)
+ return;
+
+ route = get_route(pkg->local_route_hi, pkg->local_route_lo);
+
+ xd = tb_xdomain_find_by_uuid(tb, &pkg->remote_uuid);
+ if (xd) {
+ if (xd->route == route) {
+ update_xdomain(xd, route, 0);
+ tb_xdomain_put(xd);
+ return;
+ }
+
+ remove_xdomain(xd);
+ tb_xdomain_put(xd);
+ }
+
+ /* An existing xdomain with the same address */
+ xd = tb_xdomain_find_by_route(tb, route);
+ if (xd) {
+ remove_xdomain(xd);
+ tb_xdomain_put(xd);
+ }
+
+ /*
+ * If the user disconnected a switch during suspend and
+ * connected another host to the same port, remove the switch
+ * first.
+ */
+ sw = get_switch_at_route(tb->root_switch, route);
+ if (sw)
+ remove_switch(sw);
+
+ sw = tb_switch_find_by_route(tb, get_parent_route(route));
+ if (!sw) {
+ tb_warn(tb, "no switch exists at %llx, ignoring\n", route);
+ return;
+ }
+
+ add_xdomain(sw, route, &pkg->local_uuid, &pkg->remote_uuid, 0, 0);
+ tb_switch_put(sw);
+}
+
+static void
+icm_tr_xdomain_disconnected(struct tb *tb, const struct icm_pkg_header *hdr)
+{
+ const struct icm_tr_event_xdomain_disconnected *pkg =
+ (const struct icm_tr_event_xdomain_disconnected *)hdr;
+ struct tb_xdomain *xd;
+ u64 route;
+
+ route = get_route(pkg->route_hi, pkg->route_lo);
+
+ xd = tb_xdomain_find_by_route(tb, route);
+ if (xd) {
+ remove_xdomain(xd);
+ tb_xdomain_put(xd);
+ }
+}
+
static struct pci_dev *get_upstream_port(struct pci_dev *pdev)
{
struct pci_dev *parent;
@@ -728,14 +1164,14 @@ static bool icm_ar_is_supported(struct tb *tb)
static int icm_ar_get_mode(struct tb *tb)
{
struct tb_nhi *nhi = tb->nhi;
- int retries = 5;
+ int retries = 60;
u32 val;
do {
val = ioread32(nhi->iobase + REG_FW_STS);
if (val & REG_FW_STS_NVM_AUTH_DONE)
break;
- msleep(30);
+ msleep(50);
} while (--retries);
if (!retries) {
@@ -746,6 +1182,30 @@ static int icm_ar_get_mode(struct tb *tb)
return nhi_mailbox_mode(nhi);
}
+static int
+icm_ar_driver_ready(struct tb *tb, enum tb_security_level *security_level,
+ size_t *nboot_acl)
+{
+ struct icm_ar_pkg_driver_ready_response reply;
+ struct icm_pkg_driver_ready request = {
+ .hdr.code = ICM_DRIVER_READY,
+ };
+ int ret;
+
+ memset(&reply, 0, sizeof(reply));
+ ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
+ 1, ICM_TIMEOUT);
+ if (ret)
+ return ret;
+
+ if (security_level)
+ *security_level = reply.info & ICM_AR_INFO_SLEVEL_MASK;
+ if (nboot_acl && (reply.info & ICM_AR_INFO_BOOT_ACL_SUPPORTED))
+ *nboot_acl = (reply.info & ICM_AR_INFO_BOOT_ACL_MASK) >>
+ ICM_AR_INFO_BOOT_ACL_SHIFT;
+ return 0;
+}
+
static int icm_ar_get_route(struct tb *tb, u8 link, u8 depth, u64 *route)
{
struct icm_ar_pkg_get_route_response reply;
@@ -768,6 +1228,87 @@ static int icm_ar_get_route(struct tb *tb, u8 link, u8 depth, u64 *route)
return 0;
}
+static int icm_ar_get_boot_acl(struct tb *tb, uuid_t *uuids, size_t nuuids)
+{
+ struct icm_ar_pkg_preboot_acl_response reply;
+ struct icm_ar_pkg_preboot_acl request = {
+ .hdr = { .code = ICM_PREBOOT_ACL },
+ };
+ int ret, i;
+
+ memset(&reply, 0, sizeof(reply));
+ ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
+ 1, ICM_TIMEOUT);
+ if (ret)
+ return ret;
+
+ if (reply.hdr.flags & ICM_FLAGS_ERROR)
+ return -EIO;
+
+ for (i = 0; i < nuuids; i++) {
+ u32 *uuid = (u32 *)&uuids[i];
+
+ uuid[0] = reply.acl[i].uuid_lo;
+ uuid[1] = reply.acl[i].uuid_hi;
+
+ if (uuid[0] == 0xffffffff && uuid[1] == 0xffffffff) {
+ /* Map empty entries to null UUID */
+ uuid[0] = 0;
+ uuid[1] = 0;
+ } else {
+ /* Upper two DWs are always one's */
+ uuid[2] = 0xffffffff;
+ uuid[3] = 0xffffffff;
+ }
+ }
+
+ return ret;
+}
+
+static int icm_ar_set_boot_acl(struct tb *tb, const uuid_t *uuids,
+ size_t nuuids)
+{
+ struct icm_ar_pkg_preboot_acl_response reply;
+ struct icm_ar_pkg_preboot_acl request = {
+ .hdr = {
+ .code = ICM_PREBOOT_ACL,
+ .flags = ICM_FLAGS_WRITE,
+ },
+ };
+ int ret, i;
+
+ for (i = 0; i < nuuids; i++) {
+ const u32 *uuid = (const u32 *)&uuids[i];
+
+ if (uuid_is_null(&uuids[i])) {
+ /*
+ * Map null UUID to the empty (all one) entries
+ * for ICM.
+ */
+ request.acl[i].uuid_lo = 0xffffffff;
+ request.acl[i].uuid_hi = 0xffffffff;
+ } else {
+ /* Two high DWs need to be set to all one */
+ if (uuid[2] != 0xffffffff || uuid[3] != 0xffffffff)
+ return -EINVAL;
+
+ request.acl[i].uuid_lo = uuid[0];
+ request.acl[i].uuid_hi = uuid[1];
+ }
+ }
+
+ memset(&reply, 0, sizeof(reply));
+ ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
+ 1, ICM_TIMEOUT);
+ if (ret)
+ return ret;
+
+ if (reply.hdr.flags & ICM_FLAGS_ERROR)
+ return -EIO;
+
+ return 0;
+}
+
static void icm_handle_notification(struct work_struct *work)
{
struct icm_notification *n = container_of(work, typeof(*n), work);
@@ -814,23 +1355,18 @@ static void icm_handle_event(struct tb *tb, enum tb_cfg_pkg_type type,
}
static int
-__icm_driver_ready(struct tb *tb, enum tb_security_level *security_level)
+__icm_driver_ready(struct tb *tb, enum tb_security_level *security_level,
+ size_t *nboot_acl)
{
- struct icm_pkg_driver_ready_response reply;
- struct icm_pkg_driver_ready request = {
- .hdr.code = ICM_DRIVER_READY,
- };
- unsigned int retries = 10;
+ struct icm *icm = tb_priv(tb);
+ unsigned int retries = 50;
int ret;
- memset(&reply, 0, sizeof(reply));
- ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
- 1, ICM_TIMEOUT);
- if (ret)
+ ret = icm->driver_ready(tb, security_level, nboot_acl);
+ if (ret) {
+ tb_err(tb, "failed to send driver ready to ICM\n");
return ret;
-
- if (security_level)
- *security_level = reply.security_level & 0xf;
+ }
/*
* Hold on here until the switch config space is accessible so
@@ -848,6 +1384,7 @@ __icm_driver_ready(struct tb *tb, enum tb_security_level *security_level)
msleep(50);
} while (--retries);
+ tb_err(tb, "failed to read root switch config space, giving up\n");
return -ETIMEDOUT;
}
@@ -915,6 +1452,9 @@ static int icm_firmware_reset(struct tb *tb, struct tb_nhi *nhi)
struct icm *icm = tb_priv(tb);
u32 val;
+ if (!icm->upstream_port)
+ return -ENODEV;
+
/* Put ARC to wait for CIO reset event to happen */
val = ioread32(nhi->iobase + REG_FW_STS);
val |= REG_FW_STS_CIO_RESET_REQ;
@@ -1054,6 +1594,9 @@ static int icm_firmware_init(struct tb *tb)
break;
default:
+ if (ret < 0)
+ return ret;
+
tb_err(tb, "ICM firmware is in wrong mode: %u\n", ret);
return -ENODEV;
}
@@ -1089,7 +1632,18 @@ static int icm_driver_ready(struct tb *tb)
return 0;
}
- return __icm_driver_ready(tb, &tb->security_level);
+ ret = __icm_driver_ready(tb, &tb->security_level, &tb->nboot_acl);
+ if (ret)
+ return ret;
+
+ /*
+ * Make sure the number of supported preboot ACL matches what we
+ * expect or disable the whole feature.
+ */
+ if (tb->nboot_acl > icm->max_boot_acl)
+ tb->nboot_acl = 0;
+
+ return 0;
}
static int icm_suspend(struct tb *tb)
@@ -1185,7 +1739,7 @@ static void icm_complete(struct tb *tb)
* Now all existing children should be resumed, start events
* from ICM to get updated status.
*/
- __icm_driver_ready(tb, NULL);
+ __icm_driver_ready(tb, NULL, NULL);
/*
* We do not get notifications of devices that have been
@@ -1238,7 +1792,7 @@ static int icm_disconnect_pcie_paths(struct tb *tb)
return nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_DISCONNECT_PCIE_PATHS, 0);
}
-/* Falcon Ridge and Alpine Ridge */
+/* Falcon Ridge */
static const struct tb_cm_ops icm_fr_ops = {
.driver_ready = icm_driver_ready,
.start = icm_start,
@@ -1254,6 +1808,42 @@ static const struct tb_cm_ops icm_fr_ops = {
.disconnect_xdomain_paths = icm_fr_disconnect_xdomain_paths,
};
+/* Alpine Ridge */
+static const struct tb_cm_ops icm_ar_ops = {
+ .driver_ready = icm_driver_ready,
+ .start = icm_start,
+ .stop = icm_stop,
+ .suspend = icm_suspend,
+ .complete = icm_complete,
+ .handle_event = icm_handle_event,
+ .get_boot_acl = icm_ar_get_boot_acl,
+ .set_boot_acl = icm_ar_set_boot_acl,
+ .approve_switch = icm_fr_approve_switch,
+ .add_switch_key = icm_fr_add_switch_key,
+ .challenge_switch_key = icm_fr_challenge_switch_key,
+ .disconnect_pcie_paths = icm_disconnect_pcie_paths,
+ .approve_xdomain_paths = icm_fr_approve_xdomain_paths,
+ .disconnect_xdomain_paths = icm_fr_disconnect_xdomain_paths,
+};
+
+/* Titan Ridge */
+static const struct tb_cm_ops icm_tr_ops = {
+ .driver_ready = icm_driver_ready,
+ .start = icm_start,
+ .stop = icm_stop,
+ .suspend = icm_suspend,
+ .complete = icm_complete,
+ .handle_event = icm_handle_event,
+ .get_boot_acl = icm_ar_get_boot_acl,
+ .set_boot_acl = icm_ar_set_boot_acl,
+ .approve_switch = icm_tr_approve_switch,
+ .add_switch_key = icm_tr_add_switch_key,
+ .challenge_switch_key = icm_tr_challenge_switch_key,
+ .disconnect_pcie_paths = icm_disconnect_pcie_paths,
+ .approve_xdomain_paths = icm_tr_approve_xdomain_paths,
+ .disconnect_xdomain_paths = icm_tr_disconnect_xdomain_paths,
+};
+
struct tb *icm_probe(struct tb_nhi *nhi)
{
struct icm *icm;
@@ -1272,6 +1862,7 @@ struct tb *icm_probe(struct tb_nhi *nhi)
case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI:
icm->is_supported = icm_fr_is_supported;
icm->get_route = icm_fr_get_route;
+ icm->driver_ready = icm_fr_driver_ready;
icm->device_connected = icm_fr_device_connected;
icm->device_disconnected = icm_fr_device_disconnected;
icm->xdomain_connected = icm_fr_xdomain_connected;
@@ -1284,14 +1875,29 @@ struct tb *icm_probe(struct tb_nhi *nhi)
case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_NHI:
case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_NHI:
case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_NHI:
+ icm->max_boot_acl = ICM_AR_PREBOOT_ACL_ENTRIES;
icm->is_supported = icm_ar_is_supported;
icm->get_mode = icm_ar_get_mode;
icm->get_route = icm_ar_get_route;
+ icm->driver_ready = icm_ar_driver_ready;
icm->device_connected = icm_fr_device_connected;
icm->device_disconnected = icm_fr_device_disconnected;
icm->xdomain_connected = icm_fr_xdomain_connected;
icm->xdomain_disconnected = icm_fr_xdomain_disconnected;
- tb->cm_ops = &icm_fr_ops;
+ tb->cm_ops = &icm_ar_ops;
+ break;
+
+ case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_NHI:
+ case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_NHI:
+ icm->max_boot_acl = ICM_AR_PREBOOT_ACL_ENTRIES;
+ icm->is_supported = icm_ar_is_supported;
+ icm->get_mode = icm_ar_get_mode;
+ icm->driver_ready = icm_tr_driver_ready;
+ icm->device_connected = icm_tr_device_connected;
+ icm->device_disconnected = icm_tr_device_disconnected;
+ icm->xdomain_connected = icm_tr_xdomain_connected;
+ icm->xdomain_disconnected = icm_tr_xdomain_disconnected;
+ tb->cm_ops = &icm_tr_ops;
break;
}
diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c
index f45bcbc63738..f5a33e88e676 100644
--- a/drivers/thunderbolt/nhi.c
+++ b/drivers/thunderbolt/nhi.c
@@ -1036,7 +1036,7 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
*/
tb_domain_put(tb);
nhi_shutdown(nhi);
- return -EIO;
+ return res;
}
pci_set_drvdata(pdev, tb);
@@ -1064,6 +1064,7 @@ static const struct dev_pm_ops nhi_pm_ops = {
* we just disable hotplug, the
* pci-tunnels stay alive.
*/
+ .thaw_noirq = nhi_resume_noirq,
.restore_noirq = nhi_resume_noirq,
.suspend = nhi_suspend,
.freeze = nhi_suspend,
@@ -1110,6 +1111,8 @@ static struct pci_device_id nhi_ids[] = {
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_NHI) },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_NHI) },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_USBONLY_NHI) },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_NHI) },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_NHI) },
{ 0,}
};
diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h
index 4476ab4cfd0c..1696a4560948 100644
--- a/drivers/thunderbolt/nhi.h
+++ b/drivers/thunderbolt/nhi.h
@@ -45,5 +45,10 @@ enum nhi_fw_mode nhi_mailbox_mode(struct tb_nhi *nhi);
#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_USBONLY_NHI 0x15dc
#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_USBONLY_NHI 0x15dd
#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_USBONLY_NHI 0x15de
+#define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_BRIDGE 0x15e7
+#define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_NHI 0x15e8
+#define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_BRIDGE 0x15ea
+#define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_NHI 0x15eb
+#define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_BRIDGE 0x15ef
#endif
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index da54ace4dd2f..25758671ddf4 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -716,6 +716,13 @@ static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val)
if (sw->authorized)
goto unlock;
+ /*
+ * Make sure there is no PCIe rescan ongoing when a new PCIe
+ * tunnel is created. Otherwise the PCIe rescan code might find
+ * the new tunnel too early.
+ */
+ pci_lock_rescan_remove();
+
switch (val) {
/* Approve switch */
case 1:
@@ -735,6 +742,8 @@ static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val)
break;
}
+ pci_unlock_rescan_remove();
+
if (!ret) {
sw->authorized = val;
/* Notify status change to the userspace */
@@ -766,6 +775,15 @@ static ssize_t authorized_store(struct device *dev,
}
static DEVICE_ATTR_RW(authorized);
+static ssize_t boot_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct tb_switch *sw = tb_to_switch(dev);
+
+ return sprintf(buf, "%u\n", sw->boot);
+}
+static DEVICE_ATTR_RO(boot);
+
static ssize_t device_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
@@ -942,6 +960,7 @@ static DEVICE_ATTR_RO(unique_id);
static struct attribute *switch_attrs[] = {
&dev_attr_authorized.attr,
+ &dev_attr_boot.attr,
&dev_attr_device.attr,
&dev_attr_device_name.attr,
&dev_attr_key.attr,
@@ -970,6 +989,10 @@ static umode_t switch_attr_is_visible(struct kobject *kobj,
if (sw->dma_port)
return attr->mode;
return 0;
+ } else if (attr == &dev_attr_boot.attr) {
+ if (tb_route(sw))
+ return attr->mode;
+ return 0;
}
return sw->safe_mode ? 0 : attr->mode;
@@ -1028,6 +1051,9 @@ static int tb_switch_get_generation(struct tb_switch *sw)
case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_BRIDGE:
case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_BRIDGE:
case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_BRIDGE:
+ case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_BRIDGE:
+ case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_BRIDGE:
+ case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_BRIDGE:
return 3;
default:
@@ -1470,6 +1496,7 @@ struct tb_sw_lookup {
u8 link;
u8 depth;
const uuid_t *uuid;
+ u64 route;
};
static int tb_switch_match(struct device *dev, void *data)
@@ -1485,6 +1512,11 @@ static int tb_switch_match(struct device *dev, void *data)
if (lookup->uuid)
return !memcmp(sw->uuid, lookup->uuid, sizeof(*lookup->uuid));
+ if (lookup->route) {
+ return sw->config.route_lo == lower_32_bits(lookup->route) &&
+ sw->config.route_hi == upper_32_bits(lookup->route);
+ }
+
/* Root switch is matched only by depth */
if (!lookup->depth)
return !sw->depth;
@@ -1519,7 +1551,7 @@ struct tb_switch *tb_switch_find_by_link_depth(struct tb *tb, u8 link, u8 depth)
}
/**
- * tb_switch_find_by_link_depth() - Find switch by UUID
+ * tb_switch_find_by_uuid() - Find switch by UUID
* @tb: Domain the switch belongs
* @uuid: UUID to look for
*
@@ -1542,6 +1574,33 @@ struct tb_switch *tb_switch_find_by_uuid(struct tb *tb, const uuid_t *uuid)
return NULL;
}
+/**
+ * tb_switch_find_by_route() - Find switch by route string
+ * @tb: Domain the switch belongs
+ * @route: Route string to look for
+ *
+ * Returned switch has reference count increased so the caller needs to
+ * call tb_switch_put() when done with the switch.
+ */
+struct tb_switch *tb_switch_find_by_route(struct tb *tb, u64 route)
+{
+ struct tb_sw_lookup lookup;
+ struct device *dev;
+
+ if (!route)
+ return tb_switch_get(tb->root_switch);
+
+ memset(&lookup, 0, sizeof(lookup));
+ lookup.tb = tb;
+ lookup.route = route;
+
+ dev = bus_find_device(&tb_bus_type, NULL, &lookup, tb_switch_match);
+ if (dev)
+ return tb_to_switch(dev);
+
+ return NULL;
+}
+
void tb_switch_exit(void)
{
ida_destroy(&nvm_ida);
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 895c57a0a090..9d9f0ca16bfb 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -66,6 +66,7 @@ struct tb_switch_nvm {
* @nvm: Pointer to the NVM if the switch has one (%NULL otherwise)
* @no_nvm_upgrade: Prevent NVM upgrade of this switch
* @safe_mode: The switch is in safe-mode
+ * @boot: Whether the switch was already authorized on boot or not
* @authorized: Whether the switch is authorized by user or policy
* @work: Work used to automatically authorize a switch
* @security_level: Switch supported security level
@@ -99,6 +100,7 @@ struct tb_switch {
struct tb_switch_nvm *nvm;
bool no_nvm_upgrade;
bool safe_mode;
+ bool boot;
unsigned int authorized;
struct work_struct work;
enum tb_security_level security_level;
@@ -198,6 +200,8 @@ struct tb_path {
* @suspend: Connection manager specific suspend
* @complete: Connection manager specific complete
* @handle_event: Handle thunderbolt event
+ * @get_boot_acl: Get boot ACL list
+ * @set_boot_acl: Set boot ACL list
* @approve_switch: Approve switch
* @add_switch_key: Add key to switch
* @challenge_switch_key: Challenge switch using key
@@ -215,6 +219,8 @@ struct tb_cm_ops {
void (*complete)(struct tb *tb);
void (*handle_event)(struct tb *tb, enum tb_cfg_pkg_type,
const void *buf, size_t size);
+ int (*get_boot_acl)(struct tb *tb, uuid_t *uuids, size_t nuuids);
+ int (*set_boot_acl)(struct tb *tb, const uuid_t *uuids, size_t nuuids);
int (*approve_switch)(struct tb *tb, struct tb_switch *sw);
int (*add_switch_key)(struct tb *tb, struct tb_switch *sw);
int (*challenge_switch_key)(struct tb *tb, struct tb_switch *sw,
@@ -386,6 +392,14 @@ struct tb_switch *get_switch_at_route(struct tb_switch *sw, u64 route);
struct tb_switch *tb_switch_find_by_link_depth(struct tb *tb, u8 link,
u8 depth);
struct tb_switch *tb_switch_find_by_uuid(struct tb *tb, const uuid_t *uuid);
+struct tb_switch *tb_switch_find_by_route(struct tb *tb, u64 route);
+
+static inline struct tb_switch *tb_switch_get(struct tb_switch *sw)
+{
+ if (sw)
+ get_device(&sw->dev);
+ return sw;
+}
static inline void tb_switch_put(struct tb_switch *sw)
{
diff --git a/drivers/thunderbolt/tb_msgs.h b/drivers/thunderbolt/tb_msgs.h
index b0a092baa605..bc13f8d6b804 100644
--- a/drivers/thunderbolt/tb_msgs.h
+++ b/drivers/thunderbolt/tb_msgs.h
@@ -102,6 +102,8 @@ enum icm_pkg_code {
ICM_ADD_DEVICE_KEY = 0x6,
ICM_GET_ROUTE = 0xa,
ICM_APPROVE_XDOMAIN = 0x10,
+ ICM_DISCONNECT_XDOMAIN = 0x11,
+ ICM_PREBOOT_ACL = 0x18,
};
enum icm_event_code {
@@ -122,18 +124,23 @@ struct icm_pkg_header {
#define ICM_FLAGS_NO_KEY BIT(1)
#define ICM_FLAGS_SLEVEL_SHIFT 3
#define ICM_FLAGS_SLEVEL_MASK GENMASK(4, 3)
+#define ICM_FLAGS_WRITE BIT(7)
struct icm_pkg_driver_ready {
struct icm_pkg_header hdr;
};
-struct icm_pkg_driver_ready_response {
+/* Falcon Ridge only messages */
+
+struct icm_fr_pkg_driver_ready_response {
struct icm_pkg_header hdr;
u8 romver;
u8 ramver;
u16 security_level;
};
+#define ICM_FR_SLEVEL_MASK 0xf
+
/* Falcon Ridge & Alpine Ridge common messages */
struct icm_fr_pkg_get_topology {
@@ -176,6 +183,8 @@ struct icm_fr_event_device_connected {
#define ICM_LINK_INFO_DEPTH_SHIFT 4
#define ICM_LINK_INFO_DEPTH_MASK GENMASK(7, 4)
#define ICM_LINK_INFO_APPROVED BIT(8)
+#define ICM_LINK_INFO_REJECTED BIT(9)
+#define ICM_LINK_INFO_BOOT BIT(10)
struct icm_fr_pkg_approve_device {
struct icm_pkg_header hdr;
@@ -270,6 +279,18 @@ struct icm_fr_pkg_approve_xdomain_response {
/* Alpine Ridge only messages */
+struct icm_ar_pkg_driver_ready_response {
+ struct icm_pkg_header hdr;
+ u8 romver;
+ u8 ramver;
+ u16 info;
+};
+
+#define ICM_AR_INFO_SLEVEL_MASK GENMASK(3, 0)
+#define ICM_AR_INFO_BOOT_ACL_SHIFT 7
+#define ICM_AR_INFO_BOOT_ACL_MASK GENMASK(11, 7)
+#define ICM_AR_INFO_BOOT_ACL_SUPPORTED BIT(13)
+
struct icm_ar_pkg_get_route {
struct icm_pkg_header hdr;
u16 reserved;
@@ -284,6 +305,163 @@ struct icm_ar_pkg_get_route_response {
u32 route_lo;
};
+struct icm_ar_boot_acl_entry {
+ u32 uuid_lo;
+ u32 uuid_hi;
+};
+
+#define ICM_AR_PREBOOT_ACL_ENTRIES 16
+
+struct icm_ar_pkg_preboot_acl {
+ struct icm_pkg_header hdr;
+ struct icm_ar_boot_acl_entry acl[ICM_AR_PREBOOT_ACL_ENTRIES];
+};
+
+struct icm_ar_pkg_preboot_acl_response {
+ struct icm_pkg_header hdr;
+ struct icm_ar_boot_acl_entry acl[ICM_AR_PREBOOT_ACL_ENTRIES];
+};
+
+/* Titan Ridge messages */
+
+struct icm_tr_pkg_driver_ready_response {
+ struct icm_pkg_header hdr;
+ u16 reserved1;
+ u16 info;
+ u32 nvm_version;
+ u16 device_id;
+ u16 reserved2;
+};
+
+#define ICM_TR_INFO_SLEVEL_MASK GENMASK(2, 0)
+#define ICM_TR_INFO_BOOT_ACL_SHIFT 7
+#define ICM_TR_INFO_BOOT_ACL_MASK GENMASK(12, 7)
+
+struct icm_tr_event_device_connected {
+ struct icm_pkg_header hdr;
+ uuid_t ep_uuid;
+ u32 route_hi;
+ u32 route_lo;
+ u8 connection_id;
+ u8 reserved;
+ u16 link_info;
+ u32 ep_name[55];
+};
+
+struct icm_tr_event_device_disconnected {
+ struct icm_pkg_header hdr;
+ u32 route_hi;
+ u32 route_lo;
+};
+
+struct icm_tr_event_xdomain_connected {
+ struct icm_pkg_header hdr;
+ u16 reserved;
+ u16 link_info;
+ uuid_t remote_uuid;
+ uuid_t local_uuid;
+ u32 local_route_hi;
+ u32 local_route_lo;
+ u32 remote_route_hi;
+ u32 remote_route_lo;
+};
+
+struct icm_tr_event_xdomain_disconnected {
+ struct icm_pkg_header hdr;
+ u32 route_hi;
+ u32 route_lo;
+ uuid_t remote_uuid;
+};
+
+struct icm_tr_pkg_approve_device {
+ struct icm_pkg_header hdr;
+ uuid_t ep_uuid;
+ u32 route_hi;
+ u32 route_lo;
+ u8 connection_id;
+ u8 reserved1[3];
+};
+
+struct icm_tr_pkg_add_device_key {
+ struct icm_pkg_header hdr;
+ uuid_t ep_uuid;
+ u32 route_hi;
+ u32 route_lo;
+ u8 connection_id;
+ u8 reserved[3];
+ u32 key[8];
+};
+
+struct icm_tr_pkg_challenge_device {
+ struct icm_pkg_header hdr;
+ uuid_t ep_uuid;
+ u32 route_hi;
+ u32 route_lo;
+ u8 connection_id;
+ u8 reserved[3];
+ u32 challenge[8];
+};
+
+struct icm_tr_pkg_approve_xdomain {
+ struct icm_pkg_header hdr;
+ u32 route_hi;
+ u32 route_lo;
+ uuid_t remote_uuid;
+ u16 transmit_path;
+ u16 transmit_ring;
+ u16 receive_path;
+ u16 receive_ring;
+};
+
+struct icm_tr_pkg_disconnect_xdomain {
+ struct icm_pkg_header hdr;
+ u8 stage;
+ u8 reserved[3];
+ u32 route_hi;
+ u32 route_lo;
+ uuid_t remote_uuid;
+};
+
+struct icm_tr_pkg_challenge_device_response {
+ struct icm_pkg_header hdr;
+ uuid_t ep_uuid;
+ u32 route_hi;
+ u32 route_lo;
+ u8 connection_id;
+ u8 reserved[3];
+ u32 challenge[8];
+ u32 response[8];
+};
+
+struct icm_tr_pkg_add_device_key_response {
+ struct icm_pkg_header hdr;
+ uuid_t ep_uuid;
+ u32 route_hi;
+ u32 route_lo;
+ u8 connection_id;
+ u8 reserved[3];
+};
+
+struct icm_tr_pkg_approve_xdomain_response {
+ struct icm_pkg_header hdr;
+ u32 route_hi;
+ u32 route_lo;
+ uuid_t remote_uuid;
+ u16 transmit_path;
+ u16 transmit_ring;
+ u16 receive_path;
+ u16 receive_ring;
+};
+
+struct icm_tr_pkg_disconnect_xdomain_response {
+ struct icm_pkg_header hdr;
+ u8 stage;
+ u8 reserved[3];
+ u32 route_hi;
+ u32 route_lo;
+ uuid_t remote_uuid;
+};
+
/* XDomain messages */
struct tb_xdomain_header {
diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c
index f25d88d4552b..8abb4e843085 100644
--- a/drivers/thunderbolt/xdomain.c
+++ b/drivers/thunderbolt/xdomain.c
@@ -1255,6 +1255,7 @@ struct tb_xdomain_lookup {
const uuid_t *uuid;
u8 link;
u8 depth;
+ u64 route;
};
static struct tb_xdomain *switch_find_xdomain(struct tb_switch *sw,
@@ -1275,9 +1276,13 @@ static struct tb_xdomain *switch_find_xdomain(struct tb_switch *sw,
if (lookup->uuid) {
if (uuid_equal(xd->remote_uuid, lookup->uuid))
return xd;
- } else if (lookup->link == xd->link &&
+ } else if (lookup->link &&
+ lookup->link == xd->link &&
lookup->depth == xd->depth) {
return xd;
+ } else if (lookup->route &&
+ lookup->route == xd->route) {
+ return xd;
}
} else if (port->remote) {
xd = switch_find_xdomain(port->remote->sw, lookup);
@@ -1313,12 +1318,7 @@ struct tb_xdomain *tb_xdomain_find_by_uuid(struct tb *tb, const uuid_t *uuid)
lookup.uuid = uuid;
xd = switch_find_xdomain(tb->root_switch, &lookup);
- if (xd) {
- get_device(&xd->dev);
- return xd;
- }
-
- return NULL;
+ return tb_xdomain_get(xd);
}
EXPORT_SYMBOL_GPL(tb_xdomain_find_by_uuid);
@@ -1349,13 +1349,36 @@ struct tb_xdomain *tb_xdomain_find_by_link_depth(struct tb *tb, u8 link,
lookup.depth = depth;
xd = switch_find_xdomain(tb->root_switch, &lookup);
- if (xd) {
- get_device(&xd->dev);
- return xd;
- }
+ return tb_xdomain_get(xd);
+}
- return NULL;
+/**
+ * tb_xdomain_find_by_route() - Find an XDomain by route string
+ * @tb: Domain where the XDomain belongs to
+ * @route: XDomain route string
+ *
+ * Finds XDomain by walking through the Thunderbolt topology below @tb.
+ * The returned XDomain will have its reference count increased so the
+ * caller needs to call tb_xdomain_put() when it is done with the
+ * object.
+ *
+ * This will find all XDomains including the ones that are not yet added
+ * to the bus (handshake is still in progress).
+ *
+ * The caller needs to hold @tb->lock.
+ */
+struct tb_xdomain *tb_xdomain_find_by_route(struct tb *tb, u64 route)
+{
+ struct tb_xdomain_lookup lookup;
+ struct tb_xdomain *xd;
+
+ memset(&lookup, 0, sizeof(lookup));
+ lookup.route = route;
+
+ xd = switch_find_xdomain(tb->root_switch, &lookup);
+ return tb_xdomain_get(xd);
}
+EXPORT_SYMBOL_GPL(tb_xdomain_find_by_route);
bool tb_xdomain_handle_request(struct tb *tb, enum tb_cfg_pkg_type type,
const void *buf, size_t size)
diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
index b811442c5ce6..0840d27381ea 100644
--- a/drivers/tty/Kconfig
+++ b/drivers/tty/Kconfig
@@ -11,7 +11,7 @@ if TTY
config VT
bool "Virtual terminal" if EXPERT
- depends on !S390 && !UML
+ depends on !UML
select INPUT
default y
---help---
@@ -151,19 +151,6 @@ config LEGACY_PTY_COUNT
When not in use, each legacy PTY occupies 12 bytes on 32-bit
architectures and 24 bytes on 64-bit architectures.
-config BFIN_JTAG_COMM
- tristate "Blackfin JTAG Communication"
- depends on BLACKFIN
- help
- Add support for emulating a TTY device over the Blackfin JTAG.
-
- To compile this driver as a module, choose M here: the
- module will be called bfin_jtag_comm.
-
-config BFIN_JTAG_COMM_CONSOLE
- bool "Console on Blackfin JTAG"
- depends on BFIN_JTAG_COMM=y
-
config SERIAL_NONSTANDARD
bool "Non-standard serial port support"
depends on HAS_IOMEM
@@ -402,19 +389,6 @@ config GOLDFISH_TTY_EARLY_CONSOLE
default y if GOLDFISH_TTY=y
select SERIAL_EARLYCON
-config DA_TTY
- bool "DA TTY"
- depends on METAG_DA
- select SERIAL_NONSTANDARD
- help
- This enables a TTY on a Dash channel.
-
-config DA_CONSOLE
- bool "DA Console"
- depends on DA_TTY
- help
- This enables a console on a Dash channel.
-
config MIPS_EJTAG_FDC_TTY
bool "MIPS EJTAG Fast Debug Channel TTY"
depends on MIPS_CDMM
diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile
index 8ce3a8661b31..c72cafdf32b4 100644
--- a/drivers/tty/Makefile
+++ b/drivers/tty/Makefile
@@ -20,7 +20,6 @@ obj-$(CONFIG_SERIAL_DEV_BUS) += serdev/
# tty drivers
obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o
-obj-$(CONFIG_BFIN_JTAG_COMM) += bfin_jtag_comm.o
obj-$(CONFIG_CYCLADES) += cyclades.o
obj-$(CONFIG_ISI) += isicom.o
obj-$(CONFIG_MOXA_INTELLIO) += moxa.o
@@ -32,7 +31,6 @@ obj-$(CONFIG_SYNCLINKMP) += synclinkmp.o
obj-$(CONFIG_SYNCLINK) += synclink.o
obj-$(CONFIG_PPC_EPAPR_HV_BYTECHAN) += ehv_bytechan.o
obj-$(CONFIG_GOLDFISH_TTY) += goldfish.o
-obj-$(CONFIG_DA_TTY) += metag_da.o
obj-$(CONFIG_MIPS_EJTAG_FDC_TTY) += mips_ejtag_fdc.o
obj-$(CONFIG_VCC) += vcc.o
diff --git a/drivers/tty/bfin_jtag_comm.c b/drivers/tty/bfin_jtag_comm.c
deleted file mode 100644
index c369bf27a67b..000000000000
--- a/drivers/tty/bfin_jtag_comm.c
+++ /dev/null
@@ -1,353 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * TTY over Blackfin JTAG Communication
- *
- * Copyright 2008-2009 Analog Devices Inc.
- *
- * Enter bugs at http://blackfin.uclinux.org/
- */
-
-#define DRV_NAME "bfin-jtag-comm"
-#define DEV_NAME "ttyBFJC"
-#define pr_fmt(fmt) DRV_NAME ": " fmt
-
-#include <linux/circ_buf.h>
-#include <linux/console.h>
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/kernel.h>
-#include <linux/kthread.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/tty.h>
-#include <linux/tty_driver.h>
-#include <linux/tty_flip.h>
-#include <linux/atomic.h>
-
-#define pr_init(fmt, args...) ({ static const __initconst char __fmt[] = fmt; printk(__fmt, ## args); })
-
-/* See the Debug/Emulation chapter in the HRM */
-#define EMUDOF 0x00000001 /* EMUDAT_OUT full & valid */
-#define EMUDIF 0x00000002 /* EMUDAT_IN full & valid */
-#define EMUDOOVF 0x00000004 /* EMUDAT_OUT overflow */
-#define EMUDIOVF 0x00000008 /* EMUDAT_IN overflow */
-
-static inline uint32_t bfin_write_emudat(uint32_t emudat)
-{
- __asm__ __volatile__("emudat = %0;" : : "d"(emudat));
- return emudat;
-}
-
-static inline uint32_t bfin_read_emudat(void)
-{
- uint32_t emudat;
- __asm__ __volatile__("%0 = emudat;" : "=d"(emudat));
- return emudat;
-}
-
-static inline uint32_t bfin_write_emudat_chars(char a, char b, char c, char d)
-{
- return bfin_write_emudat((a << 0) | (b << 8) | (c << 16) | (d << 24));
-}
-
-#define CIRC_SIZE 2048 /* see comment in tty_io.c:do_tty_write() */
-#define CIRC_MASK (CIRC_SIZE - 1)
-#define circ_empty(circ) ((circ)->head == (circ)->tail)
-#define circ_free(circ) CIRC_SPACE((circ)->head, (circ)->tail, CIRC_SIZE)
-#define circ_cnt(circ) CIRC_CNT((circ)->head, (circ)->tail, CIRC_SIZE)
-#define circ_byte(circ, idx) ((circ)->buf[(idx) & CIRC_MASK])
-
-static struct tty_driver *bfin_jc_driver;
-static struct task_struct *bfin_jc_kthread;
-static struct tty_port port;
-static volatile struct circ_buf bfin_jc_write_buf;
-
-static int
-bfin_jc_emudat_manager(void *arg)
-{
- uint32_t inbound_len = 0, outbound_len = 0;
-
- while (!kthread_should_stop()) {
- struct tty_struct *tty = tty_port_tty_get(&port);
- /* no one left to give data to, so sleep */
- if (tty == NULL && circ_empty(&bfin_jc_write_buf)) {
- pr_debug("waiting for readers\n");
- __set_current_state(TASK_UNINTERRUPTIBLE);
- schedule();
- continue;
- }
-
- /* no data available, so just chill */
- if (!(bfin_read_DBGSTAT() & EMUDIF) && circ_empty(&bfin_jc_write_buf)) {
- pr_debug("waiting for data (in_len = %i) (circ: %i %i)\n",
- inbound_len, bfin_jc_write_buf.tail, bfin_jc_write_buf.head);
- tty_kref_put(tty);
- if (inbound_len)
- schedule();
- else
- schedule_timeout_interruptible(HZ);
- continue;
- }
-
- /* if incoming data is ready, eat it */
- if (bfin_read_DBGSTAT() & EMUDIF) {
- uint32_t emudat = bfin_read_emudat();
- if (inbound_len == 0) {
- pr_debug("incoming length: 0x%08x\n", emudat);
- inbound_len = emudat;
- } else {
- size_t num_chars = (4 <= inbound_len ? 4 : inbound_len);
- pr_debug(" incoming data: 0x%08x (pushing %zu)\n", emudat, num_chars);
- inbound_len -= num_chars;
- tty_insert_flip_string(&port, (unsigned char *)&emudat, num_chars);
- tty_flip_buffer_push(&port);
- }
- }
-
- /* if outgoing data is ready, post it */
- if (!(bfin_read_DBGSTAT() & EMUDOF) && !circ_empty(&bfin_jc_write_buf)) {
- if (outbound_len == 0) {
- outbound_len = circ_cnt(&bfin_jc_write_buf);
- bfin_write_emudat(outbound_len);
- pr_debug("outgoing length: 0x%08x\n", outbound_len);
- } else {
- int tail = bfin_jc_write_buf.tail;
- size_t ate = (4 <= outbound_len ? 4 : outbound_len);
- uint32_t emudat =
- bfin_write_emudat_chars(
- circ_byte(&bfin_jc_write_buf, tail + 0),
- circ_byte(&bfin_jc_write_buf, tail + 1),
- circ_byte(&bfin_jc_write_buf, tail + 2),
- circ_byte(&bfin_jc_write_buf, tail + 3)
- );
- bfin_jc_write_buf.tail += ate;
- outbound_len -= ate;
- if (tty)
- tty_wakeup(tty);
- pr_debug(" outgoing data: 0x%08x (pushing %zu)\n", emudat, ate);
- }
- }
- tty_kref_put(tty);
- }
-
- __set_current_state(TASK_RUNNING);
- return 0;
-}
-
-static int
-bfin_jc_open(struct tty_struct *tty, struct file *filp)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&port.lock, flags);
- port.count++;
- spin_unlock_irqrestore(&port.lock, flags);
- tty_port_tty_set(&port, tty);
- wake_up_process(bfin_jc_kthread);
- return 0;
-}
-
-static void
-bfin_jc_close(struct tty_struct *tty, struct file *filp)
-{
- unsigned long flags;
- bool last;
-
- spin_lock_irqsave(&port.lock, flags);
- last = --port.count == 0;
- spin_unlock_irqrestore(&port.lock, flags);
- if (last)
- tty_port_tty_set(&port, NULL);
- wake_up_process(bfin_jc_kthread);
-}
-
-/* XXX: we dont handle the put_char() case where we must handle count = 1 */
-static int
-bfin_jc_circ_write(const unsigned char *buf, int count)
-{
- int i;
- count = min(count, circ_free(&bfin_jc_write_buf));
- pr_debug("going to write chunk of %i bytes\n", count);
- for (i = 0; i < count; ++i)
- circ_byte(&bfin_jc_write_buf, bfin_jc_write_buf.head + i) = buf[i];
- bfin_jc_write_buf.head += i;
- return i;
-}
-
-#ifndef CONFIG_BFIN_JTAG_COMM_CONSOLE
-# define console_lock()
-# define console_unlock()
-#endif
-static int
-bfin_jc_write(struct tty_struct *tty, const unsigned char *buf, int count)
-{
- int i;
- console_lock();
- i = bfin_jc_circ_write(buf, count);
- console_unlock();
- wake_up_process(bfin_jc_kthread);
- return i;
-}
-
-static void
-bfin_jc_flush_chars(struct tty_struct *tty)
-{
- wake_up_process(bfin_jc_kthread);
-}
-
-static int
-bfin_jc_write_room(struct tty_struct *tty)
-{
- return circ_free(&bfin_jc_write_buf);
-}
-
-static int
-bfin_jc_chars_in_buffer(struct tty_struct *tty)
-{
- return circ_cnt(&bfin_jc_write_buf);
-}
-
-static const struct tty_operations bfin_jc_ops = {
- .open = bfin_jc_open,
- .close = bfin_jc_close,
- .write = bfin_jc_write,
- /*.put_char = bfin_jc_put_char,*/
- .flush_chars = bfin_jc_flush_chars,
- .write_room = bfin_jc_write_room,
- .chars_in_buffer = bfin_jc_chars_in_buffer,
-};
-
-static int __init bfin_jc_init(void)
-{
- int ret;
-
- bfin_jc_kthread = kthread_create(bfin_jc_emudat_manager, NULL, DRV_NAME);
- if (IS_ERR(bfin_jc_kthread))
- return PTR_ERR(bfin_jc_kthread);
-
- ret = -ENOMEM;
-
- bfin_jc_write_buf.head = bfin_jc_write_buf.tail = 0;
- bfin_jc_write_buf.buf = kmalloc(CIRC_SIZE, GFP_KERNEL);
- if (!bfin_jc_write_buf.buf)
- goto err_buf;
-
- bfin_jc_driver = alloc_tty_driver(1);
- if (!bfin_jc_driver)
- goto err_driver;
-
- tty_port_init(&port);
-
- bfin_jc_driver->driver_name = DRV_NAME;
- bfin_jc_driver->name = DEV_NAME;
- bfin_jc_driver->type = TTY_DRIVER_TYPE_SERIAL;
- bfin_jc_driver->subtype = SERIAL_TYPE_NORMAL;
- bfin_jc_driver->init_termios = tty_std_termios;
- tty_set_operations(bfin_jc_driver, &bfin_jc_ops);
- tty_port_link_device(&port, bfin_jc_driver, 0);
-
- ret = tty_register_driver(bfin_jc_driver);
- if (ret)
- goto err;
-
- pr_init(KERN_INFO DRV_NAME ": initialized\n");
-
- return 0;
-
- err:
- tty_port_destroy(&port);
- put_tty_driver(bfin_jc_driver);
- err_driver:
- kfree(bfin_jc_write_buf.buf);
- err_buf:
- kthread_stop(bfin_jc_kthread);
- return ret;
-}
-module_init(bfin_jc_init);
-
-static void __exit bfin_jc_exit(void)
-{
- kthread_stop(bfin_jc_kthread);
- kfree(bfin_jc_write_buf.buf);
- tty_unregister_driver(bfin_jc_driver);
- put_tty_driver(bfin_jc_driver);
- tty_port_destroy(&port);
-}
-module_exit(bfin_jc_exit);
-
-#if defined(CONFIG_BFIN_JTAG_COMM_CONSOLE) || defined(CONFIG_EARLY_PRINTK)
-static void
-bfin_jc_straight_buffer_write(const char *buf, unsigned count)
-{
- unsigned ate = 0;
- while (bfin_read_DBGSTAT() & EMUDOF)
- continue;
- bfin_write_emudat(count);
- while (ate < count) {
- while (bfin_read_DBGSTAT() & EMUDOF)
- continue;
- bfin_write_emudat_chars(buf[ate], buf[ate+1], buf[ate+2], buf[ate+3]);
- ate += 4;
- }
-}
-#endif
-
-#ifdef CONFIG_BFIN_JTAG_COMM_CONSOLE
-static void
-bfin_jc_console_write(struct console *co, const char *buf, unsigned count)
-{
- if (bfin_jc_kthread == NULL)
- bfin_jc_straight_buffer_write(buf, count);
- else
- bfin_jc_circ_write(buf, count);
-}
-
-static struct tty_driver *
-bfin_jc_console_device(struct console *co, int *index)
-{
- *index = co->index;
- return bfin_jc_driver;
-}
-
-static struct console bfin_jc_console = {
- .name = DEV_NAME,
- .write = bfin_jc_console_write,
- .device = bfin_jc_console_device,
- .flags = CON_ANYTIME | CON_PRINTBUFFER,
- .index = -1,
-};
-
-static int __init bfin_jc_console_init(void)
-{
- register_console(&bfin_jc_console);
- return 0;
-}
-console_initcall(bfin_jc_console_init);
-#endif
-
-#ifdef CONFIG_EARLY_PRINTK
-static void __init
-bfin_jc_early_write(struct console *co, const char *buf, unsigned int count)
-{
- bfin_jc_straight_buffer_write(buf, count);
-}
-
-static struct console bfin_jc_early_console __initdata = {
- .name = "early_BFJC",
- .write = bfin_jc_early_write,
- .flags = CON_ANYTIME | CON_PRINTBUFFER,
- .index = -1,
-};
-
-struct console * __init
-bfin_jc_early_init(unsigned int port, unsigned int cflag)
-{
- return &bfin_jc_early_console;
-}
-#endif
-
-MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>");
-MODULE_DESCRIPTION("TTY over Blackfin JTAG Communication");
-MODULE_LICENSE("GPL");
diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig
index fec457edad14..4293c172e120 100644
--- a/drivers/tty/hvc/Kconfig
+++ b/drivers/tty/hvc/Kconfig
@@ -88,14 +88,15 @@ config HVC_DCC
driver. This console is used through a JTAG only on ARM. If you don't have
a JTAG then you probably don't want this option.
-config HVC_BFIN_JTAG
- bool "Blackfin JTAG console"
- depends on BLACKFIN
+config HVC_RISCV_SBI
+ bool "RISC-V SBI console support"
+ depends on RISCV
select HVC_DRIVER
help
- This console uses the Blackfin JTAG to create a console under the
- the HVC driver. If you don't have JTAG, then you probably don't
- want this option.
+ This enables support for console output via RISC-V SBI calls, which
+ is normally used only during boot to output printk.
+
+ If you don't know what do to here, say Y.
config HVCS
tristate "IBM Hypervisor Virtual Console Server support"
diff --git a/drivers/tty/hvc/Makefile b/drivers/tty/hvc/Makefile
index 0b02ec7f1dfd..98880e357941 100644
--- a/drivers/tty/hvc/Makefile
+++ b/drivers/tty/hvc/Makefile
@@ -3,12 +3,11 @@ obj-$(CONFIG_HVC_CONSOLE) += hvc_vio.o hvsi_lib.o
obj-$(CONFIG_HVC_OPAL) += hvc_opal.o hvsi_lib.o
obj-$(CONFIG_HVC_OLD_HVSI) += hvsi.o
obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o
-obj-$(CONFIG_HVC_TILE) += hvc_tile.o
obj-$(CONFIG_HVC_DCC) += hvc_dcc.o
obj-$(CONFIG_HVC_DRIVER) += hvc_console.o
obj-$(CONFIG_HVC_IRQ) += hvc_irq.o
obj-$(CONFIG_HVC_XEN) += hvc_xen.o
obj-$(CONFIG_HVC_IUCV) += hvc_iucv.o
obj-$(CONFIG_HVC_UDBG) += hvc_udbg.o
-obj-$(CONFIG_HVC_BFIN_JTAG) += hvc_bfin_jtag.o
+obj-$(CONFIG_HVC_RISCV_SBI) += hvc_riscv_sbi.o
obj-$(CONFIG_HVCS) += hvcs.o
diff --git a/drivers/tty/hvc/hvc_bfin_jtag.c b/drivers/tty/hvc/hvc_bfin_jtag.c
deleted file mode 100644
index dd7cae4c195b..000000000000
--- a/drivers/tty/hvc/hvc_bfin_jtag.c
+++ /dev/null
@@ -1,104 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Console via Blackfin JTAG Communication
- *
- * Copyright 2008-2011 Analog Devices Inc.
- *
- * Enter bugs at http://blackfin.uclinux.org/
- */
-
-#include <linux/console.h>
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/init.h>
-#include <linux/moduleparam.h>
-#include <linux/types.h>
-
-#include "hvc_console.h"
-
-/* See the Debug/Emulation chapter in the HRM */
-#define EMUDOF 0x00000001 /* EMUDAT_OUT full & valid */
-#define EMUDIF 0x00000002 /* EMUDAT_IN full & valid */
-#define EMUDOOVF 0x00000004 /* EMUDAT_OUT overflow */
-#define EMUDIOVF 0x00000008 /* EMUDAT_IN overflow */
-
-/* Helper functions to glue the register API to simple C operations */
-static inline uint32_t bfin_write_emudat(uint32_t emudat)
-{
- __asm__ __volatile__("emudat = %0;" : : "d"(emudat));
- return emudat;
-}
-
-static inline uint32_t bfin_read_emudat(void)
-{
- uint32_t emudat;
- __asm__ __volatile__("%0 = emudat;" : "=d"(emudat));
- return emudat;
-}
-
-/* Send data to the host */
-static int hvc_bfin_put_chars(uint32_t vt, const char *buf, int count)
-{
- static uint32_t outbound_len;
- uint32_t emudat;
- int ret;
-
- if (bfin_read_DBGSTAT() & EMUDOF)
- return 0;
-
- if (!outbound_len) {
- outbound_len = count;
- bfin_write_emudat(outbound_len);
- return 0;
- }
-
- ret = min(outbound_len, (uint32_t)4);
- memcpy(&emudat, buf, ret);
- bfin_write_emudat(emudat);
- outbound_len -= ret;
-
- return ret;
-}
-
-/* Receive data from the host */
-static int hvc_bfin_get_chars(uint32_t vt, char *buf, int count)
-{
- static uint32_t inbound_len;
- uint32_t emudat;
- int ret;
-
- if (!(bfin_read_DBGSTAT() & EMUDIF))
- return 0;
- emudat = bfin_read_emudat();
-
- if (!inbound_len) {
- inbound_len = emudat;
- return 0;
- }
-
- ret = min(inbound_len, (uint32_t)4);
- memcpy(buf, &emudat, ret);
- inbound_len -= ret;
-
- return ret;
-}
-
-/* Glue the HVC layers to the Blackfin layers */
-static const struct hv_ops hvc_bfin_get_put_ops = {
- .get_chars = hvc_bfin_get_chars,
- .put_chars = hvc_bfin_put_chars,
-};
-
-static int __init hvc_bfin_console_init(void)
-{
- hvc_instantiate(0, 0, &hvc_bfin_get_put_ops);
- return 0;
-}
-console_initcall(hvc_bfin_console_init);
-
-static int __init hvc_bfin_init(void)
-{
- hvc_alloc(0, 0, &hvc_bfin_get_put_ops, 128);
- return 0;
-}
-device_initcall(hvc_bfin_init);
diff --git a/drivers/tty/hvc/hvc_riscv_sbi.c b/drivers/tty/hvc/hvc_riscv_sbi.c
new file mode 100644
index 000000000000..75155bde2b88
--- /dev/null
+++ b/drivers/tty/hvc/hvc_riscv_sbi.c
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2008 David Gibson, IBM Corporation
+ * Copyright (C) 2012 Regents of the University of California
+ * Copyright (C) 2017 SiFive
+ */
+
+#include <linux/console.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+
+#include <asm/sbi.h>
+
+#include "hvc_console.h"
+
+static int hvc_sbi_tty_put(uint32_t vtermno, const char *buf, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ sbi_console_putchar(buf[i]);
+
+ return i;
+}
+
+static int hvc_sbi_tty_get(uint32_t vtermno, char *buf, int count)
+{
+ int i, c;
+
+ for (i = 0; i < count; i++) {
+ c = sbi_console_getchar();
+ if (c < 0)
+ break;
+ buf[i] = c;
+ }
+
+ return i;
+}
+
+static const struct hv_ops hvc_sbi_ops = {
+ .get_chars = hvc_sbi_tty_get,
+ .put_chars = hvc_sbi_tty_put,
+};
+
+static int __init hvc_sbi_init(void)
+{
+ return PTR_ERR_OR_ZERO(hvc_alloc(0, 0, &hvc_sbi_ops, 16));
+}
+device_initcall(hvc_sbi_init);
+
+static int __init hvc_sbi_console_init(void)
+{
+ hvc_instantiate(0, 0, &hvc_sbi_ops);
+ add_preferred_console("hvc", 0, NULL);
+
+ return 0;
+}
+console_initcall(hvc_sbi_console_init);
diff --git a/drivers/tty/hvc/hvc_tile.c b/drivers/tty/hvc/hvc_tile.c
deleted file mode 100644
index b517c0661abb..000000000000
--- a/drivers/tty/hvc/hvc_tile.c
+++ /dev/null
@@ -1,196 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright 2010 Tilera Corporation. All Rights Reserved.
- *
- * Tilera TILE Processor hypervisor console
- */
-
-#include <linux/console.h>
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/moduleparam.h>
-#include <linux/platform_device.h>
-#include <linux/types.h>
-
-#include <asm/setup.h>
-#include <arch/sim_def.h>
-
-#include <hv/hypervisor.h>
-
-#include "hvc_console.h"
-
-static int use_sim_console;
-static int __init sim_console(char *str)
-{
- use_sim_console = 1;
- return 0;
-}
-early_param("sim_console", sim_console);
-
-int tile_console_write(const char *buf, int count)
-{
- if (unlikely(use_sim_console)) {
- int i;
- for (i = 0; i < count; ++i)
- __insn_mtspr(SPR_SIM_CONTROL, SIM_CONTROL_PUTC |
- (buf[i] << _SIM_CONTROL_OPERATOR_BITS));
- __insn_mtspr(SPR_SIM_CONTROL, SIM_CONTROL_PUTC |
- (SIM_PUTC_FLUSH_BINARY <<
- _SIM_CONTROL_OPERATOR_BITS));
- return 0;
- } else {
- /* Translate 0 bytes written to EAGAIN for hvc_console_print. */
- return hv_console_write((HV_VirtAddr)buf, count) ?: -EAGAIN;
- }
-}
-
-static int hvc_tile_put_chars(uint32_t vt, const char *buf, int count)
-{
- return tile_console_write(buf, count);
-}
-
-static int hvc_tile_get_chars(uint32_t vt, char *buf, int count)
-{
- int i, c;
-
- for (i = 0; i < count; ++i) {
- c = hv_console_read_if_ready();
- if (c < 0)
- break;
- buf[i] = c;
- }
-
- return i;
-}
-
-#ifdef __tilegx__
-/*
- * IRQ based callbacks.
- */
-static int hvc_tile_notifier_add_irq(struct hvc_struct *hp, int irq)
-{
- int rc;
- int cpu = raw_smp_processor_id(); /* Choose an arbitrary cpu */
- HV_Coord coord = { .x = cpu_x(cpu), .y = cpu_y(cpu) };
-
- rc = notifier_add_irq(hp, irq);
- if (rc)
- return rc;
-
- /*
- * Request that the hypervisor start sending us interrupts.
- * If the hypervisor returns an error, we still return 0, so that
- * we can fall back to polling.
- */
- if (hv_console_set_ipi(KERNEL_PL, irq, coord) < 0)
- notifier_del_irq(hp, irq);
-
- return 0;
-}
-
-static void hvc_tile_notifier_del_irq(struct hvc_struct *hp, int irq)
-{
- HV_Coord coord = { 0, 0 };
-
- /* Tell the hypervisor to stop sending us interrupts. */
- hv_console_set_ipi(KERNEL_PL, -1, coord);
-
- notifier_del_irq(hp, irq);
-}
-
-static void hvc_tile_notifier_hangup_irq(struct hvc_struct *hp, int irq)
-{
- hvc_tile_notifier_del_irq(hp, irq);
-}
-#endif
-
-static const struct hv_ops hvc_tile_get_put_ops = {
- .get_chars = hvc_tile_get_chars,
- .put_chars = hvc_tile_put_chars,
-#ifdef __tilegx__
- .notifier_add = hvc_tile_notifier_add_irq,
- .notifier_del = hvc_tile_notifier_del_irq,
- .notifier_hangup = hvc_tile_notifier_hangup_irq,
-#endif
-};
-
-
-#ifdef __tilegx__
-static int hvc_tile_probe(struct platform_device *pdev)
-{
- struct hvc_struct *hp;
- int tile_hvc_irq;
-
- /* Create our IRQ and register it. */
- tile_hvc_irq = irq_alloc_hwirq(-1);
- if (!tile_hvc_irq)
- return -ENXIO;
-
- tile_irq_activate(tile_hvc_irq, TILE_IRQ_PERCPU);
- hp = hvc_alloc(0, tile_hvc_irq, &hvc_tile_get_put_ops, 128);
- if (IS_ERR(hp)) {
- irq_free_hwirq(tile_hvc_irq);
- return PTR_ERR(hp);
- }
- dev_set_drvdata(&pdev->dev, hp);
-
- return 0;
-}
-
-static int hvc_tile_remove(struct platform_device *pdev)
-{
- int rc;
- struct hvc_struct *hp = dev_get_drvdata(&pdev->dev);
-
- rc = hvc_remove(hp);
- if (rc == 0)
- irq_free_hwirq(hp->data);
-
- return rc;
-}
-
-static void hvc_tile_shutdown(struct platform_device *pdev)
-{
- struct hvc_struct *hp = dev_get_drvdata(&pdev->dev);
-
- hvc_tile_notifier_del_irq(hp, hp->data);
-}
-
-static struct platform_device hvc_tile_pdev = {
- .name = "hvc-tile",
- .id = 0,
-};
-
-static struct platform_driver hvc_tile_driver = {
- .probe = hvc_tile_probe,
- .remove = hvc_tile_remove,
- .shutdown = hvc_tile_shutdown,
- .driver = {
- .name = "hvc-tile",
- }
-};
-#endif
-
-static int __init hvc_tile_console_init(void)
-{
- hvc_instantiate(0, 0, &hvc_tile_get_put_ops);
- add_preferred_console("hvc", 0, NULL);
- return 0;
-}
-console_initcall(hvc_tile_console_init);
-
-static int __init hvc_tile_init(void)
-{
-#ifndef __tilegx__
- struct hvc_struct *hp;
- hp = hvc_alloc(0, 0, &hvc_tile_get_put_ops, 128);
- return PTR_ERR_OR_ZERO(hp);
-#else
- platform_device_register(&hvc_tile_pdev);
- return platform_driver_register(&hvc_tile_driver);
-#endif
-}
-device_initcall(hvc_tile_init);
diff --git a/drivers/tty/metag_da.c b/drivers/tty/metag_da.c
deleted file mode 100644
index 99eaed4b2dbc..000000000000
--- a/drivers/tty/metag_da.c
+++ /dev/null
@@ -1,665 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * dashtty.c - tty driver for Dash channels interface.
- *
- * Copyright (C) 2007,2008,2012 Imagination Technologies
- */
-
-#include <linux/atomic.h>
-#include <linux/completion.h>
-#include <linux/console.h>
-#include <linux/delay.h>
-#include <linux/export.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/kthread.h>
-#include <linux/moduleparam.h>
-#include <linux/mutex.h>
-#include <linux/sched.h>
-#include <linux/serial.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/tty.h>
-#include <linux/tty_driver.h>
-#include <linux/tty_flip.h>
-#include <linux/uaccess.h>
-
-#include <asm/da.h>
-
-/* Channel error codes */
-#define CONAOK 0
-#define CONERR 1
-#define CONBAD 2
-#define CONPRM 3
-#define CONADR 4
-#define CONCNT 5
-#define CONCBF 6
-#define CONCBE 7
-#define CONBSY 8
-
-/* Default channel for the console */
-#define CONSOLE_CHANNEL 1
-
-#define NUM_TTY_CHANNELS 6
-
-/* Auto allocate */
-#define DA_TTY_MAJOR 0
-
-/* A speedy poll rate helps the userland debug process connection response.
- * But, if you set it too high then no other userland processes get much
- * of a look in.
- */
-#define DA_TTY_POLL (HZ / 50)
-
-/*
- * A short put delay improves latency but has a high throughput overhead
- */
-#define DA_TTY_PUT_DELAY (HZ / 100)
-
-static atomic_t num_channels_need_poll = ATOMIC_INIT(0);
-
-static struct timer_list poll_timer;
-
-static struct tty_driver *channel_driver;
-
-static struct timer_list put_timer;
-static struct task_struct *dashtty_thread;
-
-/*
- * The console_poll parameter determines whether the console channel should be
- * polled for input.
- * By default the console channel isn't polled at all, in order to avoid the
- * overhead, but that means it isn't possible to have a login on /dev/console.
- */
-static bool console_poll;
-module_param(console_poll, bool, S_IRUGO);
-
-#define RX_BUF_SIZE 1024
-
-enum {
- INCHR = 1,
- OUTCHR,
- RDBUF,
- WRBUF,
- RDSTAT
-};
-
-/**
- * struct dashtty_port - Wrapper struct for dashtty tty_port.
- * @port: TTY port data
- * @rx_lock: Lock for rx_buf.
- * This protects between the poll timer and user context.
- * It's also held during read SWITCH operations.
- * @rx_buf: Read buffer
- * @xmit_lock: Lock for xmit_*, and port.xmit_buf.
- * This protects between user context and kernel thread.
- * It's also held during write SWITCH operations.
- * @xmit_cnt: Size of xmit buffer contents
- * @xmit_head: Head of xmit buffer where data is written
- * @xmit_tail: Tail of xmit buffer where data is read
- * @xmit_empty: Completion for xmit buffer being empty
- */
-struct dashtty_port {
- struct tty_port port;
- spinlock_t rx_lock;
- void *rx_buf;
- struct mutex xmit_lock;
- unsigned int xmit_cnt;
- unsigned int xmit_head;
- unsigned int xmit_tail;
- struct completion xmit_empty;
-};
-
-static struct dashtty_port dashtty_ports[NUM_TTY_CHANNELS];
-
-static atomic_t dashtty_xmit_cnt = ATOMIC_INIT(0);
-static wait_queue_head_t dashtty_waitqueue;
-
-/*
- * Low-level DA channel access routines
- */
-static int chancall(int in_bios_function, int in_channel,
- int in_arg2, void *in_arg3,
- void *in_arg4)
-{
- register int bios_function asm("D1Ar1") = in_bios_function;
- register int channel asm("D0Ar2") = in_channel;
- register int arg2 asm("D1Ar3") = in_arg2;
- register void *arg3 asm("D0Ar4") = in_arg3;
- register void *arg4 asm("D1Ar5") = in_arg4;
- register int bios_call asm("D0Ar6") = 3;
- register int result asm("D0Re0");
-
- asm volatile (
- "MSETL [A0StP++], %6,%4,%2\n\t"
- "ADD A0StP, A0StP, #8\n\t"
- "SWITCH #0x0C30208\n\t"
- "GETD %0, [A0StP+#-8]\n\t"
- "SUB A0StP, A0StP, #(4*6)+8\n\t"
- : "=d" (result) /* outs */
- : "d" (bios_function),
- "d" (channel),
- "d" (arg2),
- "d" (arg3),
- "d" (arg4),
- "d" (bios_call) /* ins */
- : "memory");
-
- return result;
-}
-
-/*
- * Attempts to fetch count bytes from channel and returns actual count.
- */
-static int fetch_data(unsigned int channel)
-{
- struct dashtty_port *dport = &dashtty_ports[channel];
- int received = 0;
-
- spin_lock_bh(&dport->rx_lock);
- /* check the port isn't being shut down */
- if (!dport->rx_buf)
- goto unlock;
- if (chancall(RDBUF, channel, RX_BUF_SIZE,
- (void *)dport->rx_buf, &received) == CONAOK) {
- if (received) {
- int space;
- unsigned char *cbuf;
-
- space = tty_prepare_flip_string(&dport->port, &cbuf,
- received);
-
- if (space <= 0)
- goto unlock;
-
- memcpy(cbuf, dport->rx_buf, space);
- tty_flip_buffer_push(&dport->port);
- }
- }
-unlock:
- spin_unlock_bh(&dport->rx_lock);
-
- return received;
-}
-
-/**
- * find_channel_to_poll() - Returns number of the next channel to poll.
- * Returns: The number of the next channel to poll, or -1 if none need
- * polling.
- */
-static int find_channel_to_poll(void)
-{
- static int last_polled_channel;
- int last = last_polled_channel;
- int chan;
- struct dashtty_port *dport;
-
- for (chan = last + 1; ; ++chan) {
- if (chan >= NUM_TTY_CHANNELS)
- chan = 0;
-
- dport = &dashtty_ports[chan];
- if (dport->rx_buf) {
- last_polled_channel = chan;
- return chan;
- }
-
- if (chan == last)
- break;
- }
- return -1;
-}
-
-/**
- * put_channel_data() - Write out a block of channel data.
- * @chan: DA channel number.
- *
- * Write a single block of data out to the debug adapter. If the circular buffer
- * is wrapped then only the first block is written.
- *
- * Returns: 1 if the remote buffer was too full to accept data.
- * 0 otherwise.
- */
-static int put_channel_data(unsigned int chan)
-{
- struct dashtty_port *dport;
- struct tty_struct *tty;
- int number_written;
- unsigned int count = 0;
-
- dport = &dashtty_ports[chan];
- mutex_lock(&dport->xmit_lock);
- if (dport->xmit_cnt) {
- count = min((unsigned int)(SERIAL_XMIT_SIZE - dport->xmit_tail),
- dport->xmit_cnt);
- chancall(WRBUF, chan, count,
- dport->port.xmit_buf + dport->xmit_tail,
- &number_written);
- dport->xmit_cnt -= number_written;
- if (!dport->xmit_cnt) {
- /* reset pointers to avoid wraps */
- dport->xmit_head = 0;
- dport->xmit_tail = 0;
- complete(&dport->xmit_empty);
- } else {
- dport->xmit_tail += number_written;
- if (dport->xmit_tail >= SERIAL_XMIT_SIZE)
- dport->xmit_tail -= SERIAL_XMIT_SIZE;
- }
- atomic_sub(number_written, &dashtty_xmit_cnt);
- }
- mutex_unlock(&dport->xmit_lock);
-
- /* if we've made more data available, wake up tty */
- if (count && number_written) {
- tty = tty_port_tty_get(&dport->port);
- if (tty) {
- tty_wakeup(tty);
- tty_kref_put(tty);
- }
- }
-
- /* did the write fail? */
- return count && !number_written;
-}
-
-/**
- * put_data() - Kernel thread to write out blocks of channel data to DA.
- * @arg: Unused.
- *
- * This kernel thread runs while @dashtty_xmit_cnt != 0, and loops over the
- * channels to write out any buffered data. If any of the channels stall due to
- * the remote buffer being full, a hold off happens to allow the debugger to
- * drain the buffer.
- */
-static int put_data(void *arg)
-{
- unsigned int chan, stall;
-
- __set_current_state(TASK_RUNNING);
- while (!kthread_should_stop()) {
- /*
- * For each channel see if there's anything to transmit in the
- * port's xmit_buf.
- */
- stall = 0;
- for (chan = 0; chan < NUM_TTY_CHANNELS; ++chan)
- stall += put_channel_data(chan);
-
- /*
- * If some of the buffers are full, hold off for a short while
- * to allow them to empty.
- */
- if (stall)
- msleep(25);
-
- wait_event_interruptible(dashtty_waitqueue,
- atomic_read(&dashtty_xmit_cnt));
- }
-
- return 0;
-}
-
-/*
- * This gets called every DA_TTY_POLL and polls the channels for data
- */
-static void dashtty_timer(struct timer_list *poll_timer)
-{
- int channel;
-
- /* If there are no ports open do nothing and don't poll again. */
- if (!atomic_read(&num_channels_need_poll))
- return;
-
- channel = find_channel_to_poll();
-
- /* Did we find a channel to poll? */
- if (channel >= 0)
- fetch_data(channel);
-
- mod_timer(poll_timer, jiffies + DA_TTY_POLL);
-}
-
-static void add_poll_timer(struct timer_list *poll_timer)
-{
- timer_setup(poll_timer, dashtty_timer, TIMER_PINNED);
- poll_timer->expires = jiffies + DA_TTY_POLL;
-
- /*
- * Always attach the timer to the boot CPU. The DA channels are per-CPU
- * so all polling should be from a single CPU.
- */
- add_timer_on(poll_timer, 0);
-}
-
-static int dashtty_port_activate(struct tty_port *port, struct tty_struct *tty)
-{
- struct dashtty_port *dport = container_of(port, struct dashtty_port,
- port);
- void *rx_buf;
-
- /* Allocate the buffer we use for writing data */
- if (tty_port_alloc_xmit_buf(port) < 0)
- goto err;
-
- /* Allocate the buffer we use for reading data */
- rx_buf = kzalloc(RX_BUF_SIZE, GFP_KERNEL);
- if (!rx_buf)
- goto err_free_xmit;
-
- spin_lock_bh(&dport->rx_lock);
- dport->rx_buf = rx_buf;
- spin_unlock_bh(&dport->rx_lock);
-
- /*
- * Don't add the poll timer if we're opening a console. This
- * avoids the overhead of polling the Dash but means it is not
- * possible to have a login on /dev/console.
- *
- */
- if (console_poll || dport != &dashtty_ports[CONSOLE_CHANNEL])
- if (atomic_inc_return(&num_channels_need_poll) == 1)
- add_poll_timer(&poll_timer);
-
- return 0;
-err_free_xmit:
- tty_port_free_xmit_buf(port);
-err:
- return -ENOMEM;
-}
-
-static void dashtty_port_shutdown(struct tty_port *port)
-{
- struct dashtty_port *dport = container_of(port, struct dashtty_port,
- port);
- void *rx_buf;
- unsigned int count;
-
- /* stop reading */
- if (console_poll || dport != &dashtty_ports[CONSOLE_CHANNEL])
- if (atomic_dec_and_test(&num_channels_need_poll))
- del_timer_sync(&poll_timer);
-
- mutex_lock(&dport->xmit_lock);
- count = dport->xmit_cnt;
- mutex_unlock(&dport->xmit_lock);
- if (count) {
- /*
- * There's still data to write out, so wake and wait for the
- * writer thread to drain the buffer.
- */
- del_timer(&put_timer);
- wake_up_interruptible(&dashtty_waitqueue);
- wait_for_completion(&dport->xmit_empty);
- }
-
- /* Null the read buffer (timer could still be running!) */
- spin_lock_bh(&dport->rx_lock);
- rx_buf = dport->rx_buf;
- dport->rx_buf = NULL;
- spin_unlock_bh(&dport->rx_lock);
- /* Free the read buffer */
- kfree(rx_buf);
-
- /* Free the write buffer */
- tty_port_free_xmit_buf(port);
-}
-
-static const struct tty_port_operations dashtty_port_ops = {
- .activate = dashtty_port_activate,
- .shutdown = dashtty_port_shutdown,
-};
-
-static int dashtty_install(struct tty_driver *driver, struct tty_struct *tty)
-{
- return tty_port_install(&dashtty_ports[tty->index].port, driver, tty);
-}
-
-static int dashtty_open(struct tty_struct *tty, struct file *filp)
-{
- return tty_port_open(tty->port, tty, filp);
-}
-
-static void dashtty_close(struct tty_struct *tty, struct file *filp)
-{
- return tty_port_close(tty->port, tty, filp);
-}
-
-static void dashtty_hangup(struct tty_struct *tty)
-{
- int channel;
- struct dashtty_port *dport;
-
- channel = tty->index;
- dport = &dashtty_ports[channel];
-
- /* drop any data in the xmit buffer */
- mutex_lock(&dport->xmit_lock);
- if (dport->xmit_cnt) {
- atomic_sub(dport->xmit_cnt, &dashtty_xmit_cnt);
- dport->xmit_cnt = 0;
- dport->xmit_head = 0;
- dport->xmit_tail = 0;
- complete(&dport->xmit_empty);
- }
- mutex_unlock(&dport->xmit_lock);
-
- tty_port_hangup(tty->port);
-}
-
-/**
- * dashtty_put_timer() - Delayed wake up of kernel thread.
- * @ignored: unused
- *
- * This timer function wakes up the kernel thread if any data exists in the
- * buffers. It is used to delay the expensive writeout until the writer has
- * stopped writing.
- */
-static void dashtty_put_timer(struct timer_list *unused)
-{
- if (atomic_read(&dashtty_xmit_cnt))
- wake_up_interruptible(&dashtty_waitqueue);
-}
-
-static int dashtty_write(struct tty_struct *tty, const unsigned char *buf,
- int total)
-{
- int channel, count, block;
- struct dashtty_port *dport;
-
- /* Determine the channel */
- channel = tty->index;
- dport = &dashtty_ports[channel];
-
- /*
- * Write to output buffer.
- *
- * The reason that we asynchronously write the buffer is because if we
- * were to write the buffer synchronously then because DA channels are
- * per-CPU the buffer would be written to the channel of whatever CPU
- * we're running on.
- *
- * What we actually want to happen is have all input and output done on
- * one CPU.
- */
- mutex_lock(&dport->xmit_lock);
- /* work out how many bytes we can write to the xmit buffer */
- total = min(total, (int)(SERIAL_XMIT_SIZE - dport->xmit_cnt));
- atomic_add(total, &dashtty_xmit_cnt);
- dport->xmit_cnt += total;
- /* write the actual bytes (may need splitting if it wraps) */
- for (count = total; count; count -= block) {
- block = min(count, (int)(SERIAL_XMIT_SIZE - dport->xmit_head));
- memcpy(dport->port.xmit_buf + dport->xmit_head, buf, block);
- dport->xmit_head += block;
- if (dport->xmit_head >= SERIAL_XMIT_SIZE)
- dport->xmit_head -= SERIAL_XMIT_SIZE;
- buf += block;
- }
- count = dport->xmit_cnt;
- /* xmit buffer no longer empty? */
- if (count)
- reinit_completion(&dport->xmit_empty);
- mutex_unlock(&dport->xmit_lock);
-
- if (total) {
- /*
- * If the buffer is full, wake up the kthread, otherwise allow
- * some more time for the buffer to fill up a bit before waking
- * it.
- */
- if (count == SERIAL_XMIT_SIZE) {
- del_timer(&put_timer);
- wake_up_interruptible(&dashtty_waitqueue);
- } else {
- mod_timer(&put_timer, jiffies + DA_TTY_PUT_DELAY);
- }
- }
- return total;
-}
-
-static int dashtty_write_room(struct tty_struct *tty)
-{
- struct dashtty_port *dport;
- int channel;
- int room;
-
- channel = tty->index;
- dport = &dashtty_ports[channel];
-
- /* report the space in the xmit buffer */
- mutex_lock(&dport->xmit_lock);
- room = SERIAL_XMIT_SIZE - dport->xmit_cnt;
- mutex_unlock(&dport->xmit_lock);
-
- return room;
-}
-
-static int dashtty_chars_in_buffer(struct tty_struct *tty)
-{
- struct dashtty_port *dport;
- int channel;
- int chars;
-
- channel = tty->index;
- dport = &dashtty_ports[channel];
-
- /* report the number of bytes in the xmit buffer */
- mutex_lock(&dport->xmit_lock);
- chars = dport->xmit_cnt;
- mutex_unlock(&dport->xmit_lock);
-
- return chars;
-}
-
-static const struct tty_operations dashtty_ops = {
- .install = dashtty_install,
- .open = dashtty_open,
- .close = dashtty_close,
- .hangup = dashtty_hangup,
- .write = dashtty_write,
- .write_room = dashtty_write_room,
- .chars_in_buffer = dashtty_chars_in_buffer,
-};
-
-static int __init dashtty_init(void)
-{
- int ret;
- int nport;
- struct dashtty_port *dport;
-
- if (!metag_da_enabled())
- return -ENODEV;
-
- channel_driver = tty_alloc_driver(NUM_TTY_CHANNELS,
- TTY_DRIVER_REAL_RAW);
- if (IS_ERR(channel_driver))
- return PTR_ERR(channel_driver);
-
- channel_driver->driver_name = "metag_da";
- channel_driver->name = "ttyDA";
- channel_driver->major = DA_TTY_MAJOR;
- channel_driver->minor_start = 0;
- channel_driver->type = TTY_DRIVER_TYPE_SERIAL;
- channel_driver->subtype = SERIAL_TYPE_NORMAL;
- channel_driver->init_termios = tty_std_termios;
- channel_driver->init_termios.c_cflag |= CLOCAL;
-
- tty_set_operations(channel_driver, &dashtty_ops);
- for (nport = 0; nport < NUM_TTY_CHANNELS; nport++) {
- dport = &dashtty_ports[nport];
- tty_port_init(&dport->port);
- dport->port.ops = &dashtty_port_ops;
- spin_lock_init(&dport->rx_lock);
- mutex_init(&dport->xmit_lock);
- /* the xmit buffer starts empty, i.e. completely written */
- init_completion(&dport->xmit_empty);
- complete(&dport->xmit_empty);
- }
-
- timer_setup(&put_timer, dashtty_put_timer, 0);
-
- init_waitqueue_head(&dashtty_waitqueue);
- dashtty_thread = kthread_create(put_data, NULL, "ttyDA");
- if (IS_ERR(dashtty_thread)) {
- pr_err("Couldn't create dashtty thread\n");
- ret = PTR_ERR(dashtty_thread);
- goto err_destroy_ports;
- }
- /*
- * Bind the writer thread to the boot CPU so it can't migrate.
- * DA channels are per-CPU and we want all channel I/O to be on a single
- * predictable CPU.
- */
- kthread_bind(dashtty_thread, 0);
- wake_up_process(dashtty_thread);
-
- ret = tty_register_driver(channel_driver);
-
- if (ret < 0) {
- pr_err("Couldn't install dashtty driver: err %d\n",
- ret);
- goto err_stop_kthread;
- }
-
- return 0;
-
-err_stop_kthread:
- kthread_stop(dashtty_thread);
-err_destroy_ports:
- for (nport = 0; nport < NUM_TTY_CHANNELS; nport++) {
- dport = &dashtty_ports[nport];
- tty_port_destroy(&dport->port);
- }
- put_tty_driver(channel_driver);
- return ret;
-}
-device_initcall(dashtty_init);
-
-#ifdef CONFIG_DA_CONSOLE
-
-static void dash_console_write(struct console *co, const char *s,
- unsigned int count)
-{
- int actually_written;
-
- chancall(WRBUF, CONSOLE_CHANNEL, count, (void *)s, &actually_written);
-}
-
-static struct tty_driver *dash_console_device(struct console *c, int *index)
-{
- *index = c->index;
- return channel_driver;
-}
-
-struct console dash_console = {
- .name = "ttyDA",
- .write = dash_console_write,
- .device = dash_console_device,
- .flags = CON_PRINTBUFFER,
- .index = 1,
-};
-
-#endif
diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
index 3b3e1f6632d7..1dbe27c9946c 100644
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -121,6 +121,9 @@ struct gsm_dlci {
struct mutex mutex;
/* Link layer */
+ int mode;
+#define DLCI_MODE_ABM 0 /* Normal Asynchronous Balanced Mode */
+#define DLCI_MODE_ADM 1 /* Asynchronous Disconnected Mode */
spinlock_t lock; /* Protects the internal state */
struct timer_list t1; /* Retransmit timer for SABM and UA */
int retries;
@@ -1364,7 +1367,13 @@ retry:
ctrl->data = data;
ctrl->len = clen;
gsm->pending_cmd = ctrl;
- gsm->cretries = gsm->n2;
+
+ /* If DLCI0 is in ADM mode skip retries, it won't respond */
+ if (gsm->dlci[0]->mode == DLCI_MODE_ADM)
+ gsm->cretries = 1;
+ else
+ gsm->cretries = gsm->n2;
+
mod_timer(&gsm->t2_timer, jiffies + gsm->t2 * HZ / 100);
gsm_control_transmit(gsm, ctrl);
spin_unlock_irqrestore(&gsm->control_lock, flags);
@@ -1472,6 +1481,7 @@ static void gsm_dlci_t1(struct timer_list *t)
if (debug & 8)
pr_info("DLCI %d opening in ADM mode.\n",
dlci->addr);
+ dlci->mode = DLCI_MODE_ADM;
gsm_dlci_open(dlci);
} else {
gsm_dlci_close(dlci);
@@ -2861,11 +2871,22 @@ static int gsmtty_modem_update(struct gsm_dlci *dlci, u8 brk)
static int gsm_carrier_raised(struct tty_port *port)
{
struct gsm_dlci *dlci = container_of(port, struct gsm_dlci, port);
+ struct gsm_mux *gsm = dlci->gsm;
+
/* Not yet open so no carrier info */
if (dlci->state != DLCI_OPEN)
return 0;
if (debug & 2)
return 1;
+
+ /*
+ * Basic mode with control channel in ADM mode may not respond
+ * to CMD_MSC at all and modem_rx is empty.
+ */
+ if (gsm->encoding == 0 && gsm->dlci[0]->mode == DLCI_MODE_ADM &&
+ !dlci->modem_rx)
+ return 1;
+
return dlci->modem_rx & TIOCM_CD;
}
diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c
index f439c72b9e3c..df93b727e984 100644
--- a/drivers/tty/serdev/core.c
+++ b/drivers/tty/serdev/core.c
@@ -350,7 +350,7 @@ static struct bus_type serdev_bus_type = {
};
/**
- * serdev_controller_alloc() - Allocate a new serdev device
+ * serdev_device_alloc() - Allocate a new serdev device
* @ctrl: associated controller
*
* Caller is responsible for either calling serdev_device_add() to add the
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index cd1b94a0f451..6fcdb90f616a 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -9,6 +9,7 @@
* LCR is written whilst busy. If it is, then a busy detect interrupt is
* raised, the LCR needs to be rewritten and the uart status register read.
*/
+#include <linux/delay.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/module.h>
@@ -119,10 +120,27 @@ static void dw8250_check_lcr(struct uart_port *p, int value)
*/
}
+/* Returns once the transmitter is empty or we run out of retries */
+static void dw8250_tx_wait_empty(struct uart_port *p, int tries)
+{
+ unsigned int lsr;
+
+ while (tries--) {
+ lsr = readb (p->membase + (UART_LSR << p->regshift));
+ if (lsr & UART_LSR_TEMT)
+ break;
+ udelay (10);
+ }
+}
+
static void dw8250_serial_out(struct uart_port *p, int offset, int value)
{
struct dw8250_data *d = p->private_data;
+ /* Allow the TX to drain before we reconfigure */
+ if (offset == UART_LCR)
+ dw8250_tx_wait_empty(p, 1000);
+
writeb(value, p->membase + (offset << p->regshift));
if (offset == UART_LCR && !d->uart_16550_compatible)
@@ -339,17 +357,11 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data)
p->serial_in = dw8250_serial_in32be;
p->serial_out = dw8250_serial_out32be;
}
- } else if (has_acpi_companion(p->dev)) {
- const struct acpi_device_id *id;
-
- id = acpi_match_device(p->dev->driver->acpi_match_table,
- p->dev);
- if (id && !strcmp(id->id, "APMC0D08")) {
- p->iotype = UPIO_MEM32;
- p->regshift = 2;
- p->serial_in = dw8250_serial_in32;
- data->uart_16550_compatible = true;
- }
+ } else if (acpi_dev_present("APMC0D08", NULL, -1)) {
+ p->iotype = UPIO_MEM32;
+ p->regshift = 2;
+ p->serial_in = dw8250_serial_in32;
+ data->uart_16550_compatible = true;
}
/* Platforms with iDMA */
diff --git a/drivers/tty/serial/8250/8250_men_mcb.c b/drivers/tty/serial/8250/8250_men_mcb.c
index 308977807994..127017cc41d9 100644
--- a/drivers/tty/serial/8250/8250_men_mcb.c
+++ b/drivers/tty/serial/8250/8250_men_mcb.c
@@ -1,12 +1,19 @@
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/io.h>
#include <linux/mcb.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
#include <linux/serial_8250.h>
#include <uapi/linux/serial_core.h>
+#define MEN_UART_ID_Z025 0x19
+#define MEN_UART_ID_Z057 0x39
+#define MEN_UART_ID_Z125 0x7d
+
+#define MEN_UART_MEM_SIZE 0x10
+
struct serial_8250_men_mcb_data {
struct uart_8250_port uart;
int line;
@@ -18,7 +25,7 @@ struct serial_8250_men_mcb_data {
* parameter in order to really set the correct baudrate, and
* do so if possible without user interaction
*/
-static u32 men_z125_lookup_uartclk(struct mcb_device *mdev)
+static u32 men_lookup_uartclk(struct mcb_device *mdev)
{
/* use default value if board is not available below */
u32 clkval = 1041666;
@@ -28,10 +35,12 @@ static u32 men_z125_lookup_uartclk(struct mcb_device *mdev)
mdev->bus->name);
if (strncmp(mdev->bus->name, "F075", 4) == 0)
clkval = 1041666;
- else if (strncmp(mdev->bus->name, "F216", 4) == 0)
+ else if (strncmp(mdev->bus->name, "F216", 4) == 0)
clkval = 1843200;
else if (strncmp(mdev->bus->name, "G215", 4) == 0)
clkval = 1843200;
+ else if (strncmp(mdev->bus->name, "F210", 4) == 0)
+ clkval = 115200;
else
dev_info(&mdev->dev,
"board not detected, using default uartclk\n");
@@ -41,62 +50,108 @@ static u32 men_z125_lookup_uartclk(struct mcb_device *mdev)
return clkval;
}
+static unsigned int get_num_ports(struct mcb_device *mdev,
+ void __iomem *membase)
+{
+ switch (mdev->id) {
+ case MEN_UART_ID_Z125:
+ return 1U;
+ case MEN_UART_ID_Z025:
+ return readb(membase) >> 4;
+ case MEN_UART_ID_Z057:
+ return 4U;
+ default:
+ dev_err(&mdev->dev, "no supported device!\n");
+ return -ENODEV;
+ }
+}
+
static int serial_8250_men_mcb_probe(struct mcb_device *mdev,
const struct mcb_device_id *id)
{
struct serial_8250_men_mcb_data *data;
struct resource *mem;
-
- data = devm_kzalloc(&mdev->dev,
- sizeof(struct serial_8250_men_mcb_data),
- GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- mcb_set_drvdata(mdev, data);
- data->uart.port.dev = mdev->dma_dev;
- spin_lock_init(&data->uart.port.lock);
-
- data->uart.port.type = PORT_16550;
- data->uart.port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE;
- data->uart.port.iotype = UPIO_MEM;
- data->uart.port.uartclk = men_z125_lookup_uartclk(mdev);
- data->uart.port.regshift = 0;
- data->uart.port.fifosize = 60;
+ unsigned int num_ports;
+ unsigned int i;
+ void __iomem *membase;
mem = mcb_get_resource(mdev, IORESOURCE_MEM);
if (mem == NULL)
return -ENXIO;
+ membase = devm_ioremap_resource(&mdev->dev, mem);
+ if (IS_ERR(membase))
+ return PTR_ERR_OR_ZERO(membase);
- data->uart.port.irq = mcb_get_irq(mdev);
+ num_ports = get_num_ports(mdev, membase);
- data->uart.port.membase = devm_ioremap_resource(&mdev->dev, mem);
- if (IS_ERR(data->uart.port.membase))
- return PTR_ERR_OR_ZERO(data->uart.port.membase);
+ dev_dbg(&mdev->dev, "found a 16z%03u with %u ports\n",
+ mdev->id, num_ports);
- data->uart.port.mapbase = (unsigned long) mem->start;
- data->uart.port.iobase = data->uart.port.mapbase;
+ if (num_ports == 0 || num_ports > 4) {
+ dev_err(&mdev->dev, "unexpected number of ports: %u\n",
+ num_ports);
+ return -ENODEV;
+ }
- /* ok, register the port */
- data->line = serial8250_register_8250_port(&data->uart);
- if (data->line < 0)
- return data->line;
+ data = devm_kcalloc(&mdev->dev, num_ports,
+ sizeof(struct serial_8250_men_mcb_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
- dev_info(&mdev->dev, "found 16Z125 UART: ttyS%d\n", data->line);
+ mcb_set_drvdata(mdev, data);
+
+ for (i = 0; i < num_ports; i++) {
+ data[i].uart.port.dev = mdev->dma_dev;
+ spin_lock_init(&data[i].uart.port.lock);
+
+ data[i].uart.port.type = PORT_16550;
+ data[i].uart.port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ
+ | UPF_FIXED_TYPE;
+ data[i].uart.port.iotype = UPIO_MEM;
+ data[i].uart.port.uartclk = men_lookup_uartclk(mdev);
+ data[i].uart.port.regshift = 0;
+ data[i].uart.port.irq = mcb_get_irq(mdev);
+ data[i].uart.port.membase = membase;
+ data[i].uart.port.fifosize = 60;
+ data[i].uart.port.mapbase = (unsigned long) mem->start
+ + i * MEN_UART_MEM_SIZE;
+ data[i].uart.port.iobase = data[i].uart.port.mapbase;
+
+ /* ok, register the port */
+ data[i].line = serial8250_register_8250_port(&data[i].uart);
+ if (data[i].line < 0) {
+ dev_err(&mdev->dev, "unable to register UART port\n");
+ return data[i].line;
+ }
+ dev_info(&mdev->dev, "found MCB UART: ttyS%d\n", data[i].line);
+ }
return 0;
}
static void serial_8250_men_mcb_remove(struct mcb_device *mdev)
{
+ unsigned int num_ports, i;
struct serial_8250_men_mcb_data *data = mcb_get_drvdata(mdev);
- if (data)
- serial8250_unregister_port(data->line);
+ if (!data)
+ return;
+
+ num_ports = get_num_ports(mdev, data[0].uart.port.membase);
+ if (num_ports < 0 || num_ports > 4) {
+ dev_err(&mdev->dev, "error retrieving number of ports!\n");
+ return;
+ }
+
+ for (i = 0; i < num_ports; i++)
+ serial8250_unregister_port(data[i].line);
}
static const struct mcb_device_id serial_8250_men_mcb_ids[] = {
- { .device = 0x7d },
+ { .device = MEN_UART_ID_Z025 },
+ { .device = MEN_UART_ID_Z057 },
+ { .device = MEN_UART_ID_Z125 },
{ }
};
MODULE_DEVICE_TABLE(mcb, serial_8250_men_mcb_ids);
@@ -113,6 +168,8 @@ static struct mcb_driver mcb_driver = {
module_mcb_driver(mcb_driver);
MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("MEN 16z125 8250 UART driver");
+MODULE_DESCRIPTION("MEN 8250 UART driver");
MODULE_AUTHOR("Michael Moese <michael.moese@men.de");
MODULE_ALIAS("mcb:16z125");
+MODULE_ALIAS("mcb:16z025");
+MODULE_ALIAS("mcb:16z057");
diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c
index 160b8906d9b9..9835b1c1cbe1 100644
--- a/drivers/tty/serial/8250/8250_of.c
+++ b/drivers/tty/serial/8250/8250_of.c
@@ -316,6 +316,7 @@ static const struct of_device_id of_platform_serial_table[] = {
{ .compatible = "mrvl,mmp-uart",
.data = (void *)PORT_XSCALE, },
{ .compatible = "ti,da830-uart", .data = (void *)PORT_DA830, },
+ { .compatible = "nuvoton,npcm750-uart", .data = (void *)PORT_NPCM, },
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(of, of_platform_serial_table);
diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index 57f6eba47f44..624b501fd253 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -114,6 +114,7 @@ struct omap8250_priv {
struct uart_8250_dma omap8250_dma;
spinlock_t rx_dma_lock;
bool rx_dma_broken;
+ bool throttled;
};
#ifdef CONFIG_SERIAL_8250_DMA
@@ -692,6 +693,7 @@ static void omap_8250_shutdown(struct uart_port *port)
static void omap_8250_throttle(struct uart_port *port)
{
+ struct omap8250_priv *priv = port->private_data;
struct uart_8250_port *up = up_to_u8250p(port);
unsigned long flags;
@@ -700,6 +702,7 @@ static void omap_8250_throttle(struct uart_port *port)
spin_lock_irqsave(&port->lock, flags);
up->ier &= ~(UART_IER_RLSI | UART_IER_RDI);
serial_out(up, UART_IER, up->ier);
+ priv->throttled = true;
spin_unlock_irqrestore(&port->lock, flags);
pm_runtime_mark_last_busy(port->dev);
@@ -738,12 +741,16 @@ static int omap_8250_rs485_config(struct uart_port *port,
static void omap_8250_unthrottle(struct uart_port *port)
{
+ struct omap8250_priv *priv = port->private_data;
struct uart_8250_port *up = up_to_u8250p(port);
unsigned long flags;
pm_runtime_get_sync(port->dev);
spin_lock_irqsave(&port->lock, flags);
+ priv->throttled = false;
+ if (up->dma)
+ up->dma->rx_dma(up);
up->ier |= UART_IER_RLSI | UART_IER_RDI;
serial_out(up, UART_IER, up->ier);
spin_unlock_irqrestore(&port->lock, flags);
@@ -788,6 +795,7 @@ unlock:
static void __dma_rx_complete(void *param)
{
struct uart_8250_port *p = param;
+ struct omap8250_priv *priv = p->port.private_data;
struct uart_8250_dma *dma = p->dma;
struct dma_tx_state state;
unsigned long flags;
@@ -805,7 +813,8 @@ static void __dma_rx_complete(void *param)
return;
}
__dma_rx_do_complete(p);
- omap_8250_rx_dma(p);
+ if (!priv->throttled)
+ omap_8250_rx_dma(p);
spin_unlock_irqrestore(&p->port.lock, flags);
}
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index a93f77ab3da0..3296a05cda2d 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -1685,9 +1685,6 @@ pci_wch_ch38x_setup(struct serial_private *priv,
#define PCI_DEVICE_ID_BROADCOM_TRUMANAGE 0x160a
#define PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800 0x818e
-#define PCI_VENDOR_ID_SUNIX 0x1fd4
-#define PCI_DEVICE_ID_SUNIX_1999 0x1999
-
#define PCIE_VENDOR_ID_WCH 0x1c00
#define PCIE_DEVICE_ID_WCH_CH382_2S1P 0x3250
#define PCIE_DEVICE_ID_WCH_CH384_4S 0x3470
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index 1328c7e70108..95833cbc4338 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -47,6 +47,10 @@
#define UART_EXAR_SLEEP 0x8b /* Sleep mode */
#define UART_EXAR_DVID 0x8d /* Device identification */
+/* Nuvoton NPCM timeout register */
+#define UART_NPCM_TOR 7
+#define UART_NPCM_TOIE BIT(7) /* Timeout Interrupt Enable */
+
/*
* Debugging.
*/
@@ -293,6 +297,15 @@ static const struct serial8250_config uart_config[] = {
UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT,
.flags = UART_CAP_FIFO,
},
+ [PORT_NPCM] = {
+ .name = "Nuvoton 16550",
+ .fifo_size = 16,
+ .tx_loadsz = 16,
+ .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 |
+ UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT,
+ .rxtrig_bytes = {1, 4, 8, 14},
+ .flags = UART_CAP_FIFO,
+ },
};
/* Uart divisor latch read */
@@ -1854,7 +1867,8 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
status = serial_port_in(port, UART_LSR);
- if (status & (UART_LSR_DR | UART_LSR_BI)) {
+ if (status & (UART_LSR_DR | UART_LSR_BI) &&
+ iir & UART_IIR_RDI) {
if (!up->dma || handle_rx_dma(up, iir))
status = serial8250_rx_chars(up, status);
}
@@ -2140,6 +2154,15 @@ int serial8250_do_startup(struct uart_port *port)
UART_DA830_PWREMU_MGMT_FREE);
}
+ if (port->type == PORT_NPCM) {
+ /*
+ * Nuvoton calls the scratch register 'UART_TOR' (timeout
+ * register). Enable it, and set TIOC (timeout interrupt
+ * comparator) to be 0x20 for correct operation.
+ */
+ serial_port_out(port, UART_NPCM_TOR, UART_NPCM_TOIE | 0x20);
+ }
+
#ifdef CONFIG_SERIAL_8250_RSA
/*
* If this is an RSA port, see if we can kick it up to the
@@ -2462,6 +2485,15 @@ static unsigned int xr17v35x_get_divisor(struct uart_8250_port *up,
return quot_16 >> 4;
}
+/* Nuvoton NPCM UARTs have a custom divisor calculation */
+static unsigned int npcm_get_divisor(struct uart_8250_port *up,
+ unsigned int baud)
+{
+ struct uart_port *port = &up->port;
+
+ return DIV_ROUND_CLOSEST(port->uartclk, 16 * baud + 2) - 2;
+}
+
static unsigned int serial8250_get_divisor(struct uart_8250_port *up,
unsigned int baud,
unsigned int *frac)
@@ -2482,6 +2514,8 @@ static unsigned int serial8250_get_divisor(struct uart_8250_port *up,
quot = 0x8002;
else if (up->port.type == PORT_XR17V35X)
quot = xr17v35x_get_divisor(up, baud, frac);
+ else if (up->port.type == PORT_NPCM)
+ quot = npcm_get_divisor(up, baud);
else
quot = uart_get_divisor(port, baud);
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index 16b1496e6105..f005eaf8bc57 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -157,11 +157,12 @@ config SERIAL_8250_CS
If unsure, say N.
config SERIAL_8250_MEN_MCB
- tristate "MEN Z125 UART device support"
+ tristate "MEN MCB UART device support"
depends on MCB && SERIAL_8250
help
This enables support for FPGA based UARTs found on many MEN
- boards. This driver enables support for the Z125 UARTs.
+ boards. This driver enables support for the 16z025, 16z057
+ and 16z125 UARTs.
To compile this driver as a module, chose M here: the
module will be called 8250_men_mcb.
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 3682fd3e960c..0f058df0b070 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -498,92 +498,6 @@ config SERIAL_SA1100_CONSOLE
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)
-config SERIAL_BFIN
- tristate "Blackfin serial port support"
- depends on BLACKFIN
- select SERIAL_CORE
- select SERIAL_BFIN_UART0 if (BF531 || BF532 || BF533 || BF561)
- help
- Add support for the built-in UARTs on the Blackfin.
-
- To compile this driver as a module, choose M here: the
- module is named bfin_uart.ko.
-
-config SERIAL_BFIN_CONSOLE
- bool "Console on Blackfin serial port"
- depends on SERIAL_BFIN=y
- select SERIAL_CORE_CONSOLE
-
-choice
- prompt "UART Mode"
- depends on SERIAL_BFIN
- default SERIAL_BFIN_DMA
- help
- This driver supports the built-in serial ports of the Blackfin family
- of CPUs
-
-config SERIAL_BFIN_DMA
- bool "DMA mode"
- depends on !DMA_UNCACHED_NONE && KGDB_SERIAL_CONSOLE=n
- help
- This driver works under DMA mode. If this option is selected, the
- blackfin simple dma driver is also enabled.
-
-config SERIAL_BFIN_PIO
- bool "PIO mode"
- help
- This driver works under PIO mode.
-
-endchoice
-
-config SERIAL_BFIN_UART0
- bool "Enable UART0"
- depends on SERIAL_BFIN
- help
- Enable UART0
-
-config BFIN_UART0_CTSRTS
- bool "Enable UART0 hardware flow control"
- depends on SERIAL_BFIN_UART0
- help
- Enable hardware flow control in the driver.
-
-config SERIAL_BFIN_UART1
- bool "Enable UART1"
- depends on SERIAL_BFIN && (!BF531 && !BF532 && !BF533 && !BF561)
- help
- Enable UART1
-
-config BFIN_UART1_CTSRTS
- bool "Enable UART1 hardware flow control"
- depends on SERIAL_BFIN_UART1
- help
- Enable hardware flow control in the driver.
-
-config SERIAL_BFIN_UART2
- bool "Enable UART2"
- depends on SERIAL_BFIN && (BF54x || BF538 || BF539)
- help
- Enable UART2
-
-config BFIN_UART2_CTSRTS
- bool "Enable UART2 hardware flow control"
- depends on SERIAL_BFIN_UART2
- help
- Enable hardware flow control in the driver.
-
-config SERIAL_BFIN_UART3
- bool "Enable UART3"
- depends on SERIAL_BFIN && (BF54x)
- help
- Enable UART3
-
-config BFIN_UART3_CTSRTS
- bool "Enable UART3 hardware flow control"
- depends on SERIAL_BFIN_UART3
- help
- Enable hardware flow control in the driver.
-
config SERIAL_IMX
tristate "IMX serial port support"
depends on HAS_DMA
@@ -991,35 +905,6 @@ config SERIAL_ICOM
This driver can also be built as a module. If so, the module
will be called icom.
-config SERIAL_M32R_SIO
- bool "M32R SIO I/F"
- depends on M32R
- default y
- select SERIAL_CORE
- help
- Say Y here if you want to use the M32R serial controller.
-
-config SERIAL_M32R_SIO_CONSOLE
- bool "use SIO console"
- depends on SERIAL_M32R_SIO=y
- select SERIAL_CORE_CONSOLE
- help
- Say Y here if you want to support a serial console.
-
- If you use an M3T-M32700UT or an OPSPUT platform,
- please say also y for SERIAL_M32R_PLDSIO.
-
-config SERIAL_M32R_PLDSIO
- bool "M32R SIO I/F on a PLD"
- depends on SERIAL_M32R_SIO=y && (PLAT_OPSPUT || PLAT_USRV || PLAT_M32700UT)
- default n
- help
- Say Y here if you want to use the M32R serial controller
- on a PLD (Programmable Logic Device).
-
- If you use an M3T-M32700UT or an OPSPUT platform,
- please say Y.
-
config SERIAL_TXX9
bool "TMPTX39XX/49XX SIO support"
depends on HAS_TXX9_SERIAL
@@ -1104,6 +989,21 @@ config SERIAL_MSM_CONSOLE
select SERIAL_CORE_CONSOLE
select SERIAL_EARLYCON
+config SERIAL_QCOM_GENI
+ tristate "QCOM on-chip GENI based serial port support"
+ depends on ARCH_QCOM || COMPILE_TEST
+ depends on QCOM_GENI_SE
+ select SERIAL_CORE
+
+config SERIAL_QCOM_GENI_CONSOLE
+ bool "QCOM GENI Serial Console support"
+ depends on SERIAL_QCOM_GENI=y
+ select SERIAL_CORE_CONSOLE
+ select SERIAL_EARLYCON
+ help
+ Serial console driver for Qualcomm Technologies Inc's GENI based
+ QUP hardware.
+
config SERIAL_VT8500
bool "VIA VT8500 on-chip serial port support"
depends on ARCH_VT8500
@@ -1114,17 +1014,6 @@ config SERIAL_VT8500_CONSOLE
depends on SERIAL_VT8500=y
select SERIAL_CORE_CONSOLE
-config SERIAL_ETRAXFS
- bool "ETRAX FS serial port support"
- depends on ETRAX_ARCH_V32 && OF
- select SERIAL_CORE
- select SERIAL_MCTRL_GPIO if GPIOLIB
-
-config SERIAL_ETRAXFS_CONSOLE
- bool "ETRAX FS serial console support"
- depends on SERIAL_ETRAXFS
- select SERIAL_CORE_CONSOLE
-
config SERIAL_NETX
tristate "NetX serial port support"
depends on ARCH_NETX
@@ -1242,69 +1131,6 @@ config SERIAL_SC16IS7XX_SPI
This is additional support to exsisting driver.
You must select at least one bus for the driver to be built.
-config SERIAL_BFIN_SPORT
- tristate "Blackfin SPORT emulate UART"
- depends on BLACKFIN
- select SERIAL_CORE
- help
- Enable SPORT emulate UART on Blackfin series.
-
- To compile this driver as a module, choose M here: the
- module will be called bfin_sport_uart.
-
-config SERIAL_BFIN_SPORT_CONSOLE
- bool "Console on Blackfin sport emulated uart"
- depends on SERIAL_BFIN_SPORT=y
- select SERIAL_CORE_CONSOLE
-
-config SERIAL_BFIN_SPORT0_UART
- bool "Enable UART over SPORT0"
- depends on SERIAL_BFIN_SPORT && !(BF542 || BF544)
- help
- Enable UART over SPORT0
-
-config SERIAL_BFIN_SPORT0_UART_CTSRTS
- bool "Enable UART over SPORT0 hardware flow control"
- depends on SERIAL_BFIN_SPORT0_UART
- help
- Enable hardware flow control in the driver.
-
-config SERIAL_BFIN_SPORT1_UART
- bool "Enable UART over SPORT1"
- depends on SERIAL_BFIN_SPORT
- help
- Enable UART over SPORT1
-
-config SERIAL_BFIN_SPORT1_UART_CTSRTS
- bool "Enable UART over SPORT1 hardware flow control"
- depends on SERIAL_BFIN_SPORT1_UART
- help
- Enable hardware flow control in the driver.
-
-config SERIAL_BFIN_SPORT2_UART
- bool "Enable UART over SPORT2"
- depends on SERIAL_BFIN_SPORT && (BF54x || BF538 || BF539)
- help
- Enable UART over SPORT2
-
-config SERIAL_BFIN_SPORT2_UART_CTSRTS
- bool "Enable UART over SPORT2 hardware flow control"
- depends on SERIAL_BFIN_SPORT2_UART
- help
- Enable hardware flow control in the driver.
-
-config SERIAL_BFIN_SPORT3_UART
- bool "Enable UART over SPORT3"
- depends on SERIAL_BFIN_SPORT && (BF54x || BF538 || BF539)
- help
- Enable UART over SPORT3
-
-config SERIAL_BFIN_SPORT3_UART_CTSRTS
- bool "Enable UART over SPORT3 hardware flow control"
- depends on SERIAL_BFIN_SPORT3_UART
- help
- Enable hardware flow control in the driver.
-
config SERIAL_TIMBERDALE
tristate "Support for timberdale UART"
select SERIAL_CORE
@@ -1519,15 +1345,6 @@ config SERIAL_EFM32_UART_CONSOLE
depends on SERIAL_EFM32_UART=y
select SERIAL_CORE_CONSOLE
-config SERIAL_TILEGX
- tristate "TILE-Gx on-chip serial port support"
- depends on TILEGX
- select TILE_GXIO_UART
- select SERIAL_CORE
- ---help---
- This device provides access to the on-chip UARTs on the TILE-Gx
- processor.
-
config SERIAL_ARC
tristate "ARC UART driver support"
select SERIAL_CORE
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 842d185d697e..daac675612df 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -29,8 +29,6 @@ obj-$(CONFIG_SERIAL_PXA_NON8250) += pxa.o
obj-$(CONFIG_SERIAL_PNX8XXX) += pnx8xxx_uart.o
obj-$(CONFIG_SERIAL_SA1100) += sa1100.o
obj-$(CONFIG_SERIAL_BCM63XX) += bcm63xx_uart.o
-obj-$(CONFIG_SERIAL_BFIN) += bfin_uart.o
-obj-$(CONFIG_SERIAL_BFIN_SPORT) += bfin_sport_uart.o
obj-$(CONFIG_SERIAL_SAMSUNG) += samsung.o
obj-$(CONFIG_SERIAL_MAX3100) += max3100.o
obj-$(CONFIG_SERIAL_MAX310X) += max310x.o
@@ -47,12 +45,9 @@ obj-$(CONFIG_SERIAL_CPM) += cpm_uart/
obj-$(CONFIG_SERIAL_IMX) += imx.o
obj-$(CONFIG_SERIAL_MPC52xx) += mpc52xx_uart.o
obj-$(CONFIG_SERIAL_ICOM) += icom.o
-obj-$(CONFIG_SERIAL_M32R_SIO) += m32r_sio.o
obj-$(CONFIG_SERIAL_MPSC) += mpsc.o
obj-$(CONFIG_SERIAL_MESON) += meson_uart.o
obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o
-obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o
-obj-$(CONFIG_SERIAL_ETRAXFS) += etraxfs-uart.o
obj-$(CONFIG_SERIAL_SCCNXP) += sccnxp.o
obj-$(CONFIG_SERIAL_SC16IS7XX_CORE) += sc16is7xx.o
obj-$(CONFIG_SERIAL_JSM) += jsm/
@@ -63,12 +58,12 @@ obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o
obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o
obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
obj-$(CONFIG_SERIAL_MSM) += msm_serial.o
+obj-$(CONFIG_SERIAL_QCOM_GENI) += qcom_geni_serial.o
obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o
obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o
obj-$(CONFIG_SERIAL_ST_ASC) += st-asc.o
-obj-$(CONFIG_SERIAL_TILEGX) += tilegx.o
obj-$(CONFIG_SERIAL_QE) += ucc_uart.o
obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o
obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o
diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c
index b88b05f8e81e..0e487ce091ac 100644
--- a/drivers/tty/serial/altera_uart.c
+++ b/drivers/tty/serial/altera_uart.c
@@ -109,6 +109,20 @@ static unsigned int altera_uart_get_mctrl(struct uart_port *port)
return sigs;
}
+static void altera_uart_update_ctrl_reg(struct altera_uart *pp)
+{
+ unsigned short imr = pp->imr;
+
+ /*
+ * If the device doesn't have an irq, ensure that the irq bits are
+ * masked out to keep the irq line inactive.
+ */
+ if (!pp->port.irq)
+ imr &= ALTERA_UART_CONTROL_TRBK_MSK | ALTERA_UART_CONTROL_RTS_MSK;
+
+ altera_uart_writel(&pp->port, imr, ALTERA_UART_CONTROL_REG);
+}
+
static void altera_uart_set_mctrl(struct uart_port *port, unsigned int sigs)
{
struct altera_uart *pp = container_of(port, struct altera_uart, port);
@@ -118,7 +132,7 @@ static void altera_uart_set_mctrl(struct uart_port *port, unsigned int sigs)
pp->imr |= ALTERA_UART_CONTROL_RTS_MSK;
else
pp->imr &= ~ALTERA_UART_CONTROL_RTS_MSK;
- altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG);
+ altera_uart_update_ctrl_reg(pp);
}
static void altera_uart_start_tx(struct uart_port *port)
@@ -126,7 +140,7 @@ static void altera_uart_start_tx(struct uart_port *port)
struct altera_uart *pp = container_of(port, struct altera_uart, port);
pp->imr |= ALTERA_UART_CONTROL_TRDY_MSK;
- altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG);
+ altera_uart_update_ctrl_reg(pp);
}
static void altera_uart_stop_tx(struct uart_port *port)
@@ -134,7 +148,7 @@ static void altera_uart_stop_tx(struct uart_port *port)
struct altera_uart *pp = container_of(port, struct altera_uart, port);
pp->imr &= ~ALTERA_UART_CONTROL_TRDY_MSK;
- altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG);
+ altera_uart_update_ctrl_reg(pp);
}
static void altera_uart_stop_rx(struct uart_port *port)
@@ -142,7 +156,7 @@ static void altera_uart_stop_rx(struct uart_port *port)
struct altera_uart *pp = container_of(port, struct altera_uart, port);
pp->imr &= ~ALTERA_UART_CONTROL_RRDY_MSK;
- altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG);
+ altera_uart_update_ctrl_reg(pp);
}
static void altera_uart_break_ctl(struct uart_port *port, int break_state)
@@ -155,7 +169,7 @@ static void altera_uart_break_ctl(struct uart_port *port, int break_state)
pp->imr |= ALTERA_UART_CONTROL_TRBK_MSK;
else
pp->imr &= ~ALTERA_UART_CONTROL_TRBK_MSK;
- altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG);
+ altera_uart_update_ctrl_reg(pp);
spin_unlock_irqrestore(&port->lock, flags);
}
@@ -262,7 +276,7 @@ static void altera_uart_tx_chars(struct altera_uart *pp)
if (xmit->head == xmit->tail) {
pp->imr &= ~ALTERA_UART_CONTROL_TRDY_MSK;
- altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG);
+ altera_uart_update_ctrl_reg(pp);
}
}
@@ -307,27 +321,27 @@ static int altera_uart_startup(struct uart_port *port)
{
struct altera_uart *pp = container_of(port, struct altera_uart, port);
unsigned long flags;
- int ret;
if (!port->irq) {
timer_setup(&pp->tmr, altera_uart_timer, 0);
mod_timer(&pp->tmr, jiffies + uart_poll_timeout(port));
- return 0;
- }
-
- ret = request_irq(port->irq, altera_uart_interrupt, 0,
- DRV_NAME, port);
- if (ret) {
- pr_err(DRV_NAME ": unable to attach Altera UART %d "
- "interrupt vector=%d\n", port->line, port->irq);
- return ret;
+ } else {
+ int ret;
+
+ ret = request_irq(port->irq, altera_uart_interrupt, 0,
+ DRV_NAME, port);
+ if (ret) {
+ pr_err(DRV_NAME ": unable to attach Altera UART %d "
+ "interrupt vector=%d\n", port->line, port->irq);
+ return ret;
+ }
}
spin_lock_irqsave(&port->lock, flags);
/* Enable RX interrupts now */
pp->imr = ALTERA_UART_CONTROL_RRDY_MSK;
- writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG);
+ altera_uart_update_ctrl_reg(pp);
spin_unlock_irqrestore(&port->lock, flags);
@@ -343,7 +357,7 @@ static void altera_uart_shutdown(struct uart_port *port)
/* Disable all interrupts now */
pp->imr = 0;
- writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG);
+ altera_uart_update_ctrl_reg(pp);
spin_unlock_irqrestore(&port->lock, flags);
@@ -432,7 +446,7 @@ static void altera_uart_console_putc(struct uart_port *port, int c)
ALTERA_UART_STATUS_TRDY_MSK))
cpu_relax();
- writel(c, port->membase + ALTERA_UART_TXDATA_REG);
+ altera_uart_writel(port, c, ALTERA_UART_TXDATA_REG);
}
static void altera_uart_console_write(struct console *co, const char *s,
@@ -502,13 +516,13 @@ static int __init altera_uart_earlycon_setup(struct earlycon_device *dev,
return -ENODEV;
/* Enable RX interrupts now */
- writel(ALTERA_UART_CONTROL_RRDY_MSK,
- port->membase + ALTERA_UART_CONTROL_REG);
+ altera_uart_writel(port, ALTERA_UART_CONTROL_RRDY_MSK,
+ ALTERA_UART_CONTROL_REG);
if (dev->baud) {
unsigned int baudclk = port->uartclk / dev->baud;
- writel(baudclk, port->membase + ALTERA_UART_DIVISOR_REG);
+ altera_uart_writel(port, baudclk, ALTERA_UART_DIVISOR_REG);
}
dev->con->write = altera_uart_earlycon_write;
diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c
index 2599f9ecccfe..d904a3a345e7 100644
--- a/drivers/tty/serial/arc_uart.c
+++ b/drivers/tty/serial/arc_uart.c
@@ -593,6 +593,11 @@ static int arc_serial_probe(struct platform_device *pdev)
if (dev_id < 0)
dev_id = 0;
+ if (dev_id >= ARRAY_SIZE(arc_uart_ports)) {
+ dev_err(&pdev->dev, "serial%d out of range\n", dev_id);
+ return -EINVAL;
+ }
+
uart = &arc_uart_ports[dev_id];
port = &uart->port;
diff --git a/drivers/tty/serial/bfin_sport_uart.c b/drivers/tty/serial/bfin_sport_uart.c
deleted file mode 100644
index 4ccca5d22f4f..000000000000
--- a/drivers/tty/serial/bfin_sport_uart.c
+++ /dev/null
@@ -1,937 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Blackfin On-Chip Sport Emulated UART Driver
- *
- * Copyright 2006-2009 Analog Devices Inc.
- *
- * Enter bugs at http://blackfin.uclinux.org/
- */
-
-/*
- * This driver and the hardware supported are in term of EE-191 of ADI.
- * http://www.analog.com/static/imported-files/application_notes/EE191.pdf
- * This application note describe how to implement a UART on a Sharc DSP,
- * but this driver is implemented on Blackfin Processor.
- * Transmit Frame Sync is not used by this driver to transfer data out.
- */
-
-/* #define DEBUG */
-
-#define DRV_NAME "bfin-sport-uart"
-#define DEVICE_NAME "ttySS"
-#define pr_fmt(fmt) DRV_NAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/ioport.h>
-#include <linux/io.h>
-#include <linux/init.h>
-#include <linux/console.h>
-#include <linux/sysrq.h>
-#include <linux/slab.h>
-#include <linux/platform_device.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/serial_core.h>
-#include <linux/gpio.h>
-
-#include <asm/bfin_sport.h>
-#include <asm/delay.h>
-#include <asm/portmux.h>
-
-#include "bfin_sport_uart.h"
-
-struct sport_uart_port {
- struct uart_port port;
- int err_irq;
- unsigned short csize;
- unsigned short rxmask;
- unsigned short txmask1;
- unsigned short txmask2;
- unsigned char stopb;
-/* unsigned char parib; */
-#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS
- int cts_pin;
- int rts_pin;
-#endif
-};
-
-static int sport_uart_tx_chars(struct sport_uart_port *up);
-static void sport_stop_tx(struct uart_port *port);
-
-static inline void tx_one_byte(struct sport_uart_port *up, unsigned int value)
-{
- pr_debug("%s value:%x, mask1=0x%x, mask2=0x%x\n", __func__, value,
- up->txmask1, up->txmask2);
-
- /* Place Start and Stop bits */
- __asm__ __volatile__ (
- "%[val] <<= 1;"
- "%[val] = %[val] & %[mask1];"
- "%[val] = %[val] | %[mask2];"
- : [val]"+d"(value)
- : [mask1]"d"(up->txmask1), [mask2]"d"(up->txmask2)
- : "ASTAT"
- );
- pr_debug("%s value:%x\n", __func__, value);
-
- SPORT_PUT_TX(up, value);
-}
-
-static inline unsigned char rx_one_byte(struct sport_uart_port *up)
-{
- unsigned int value;
- unsigned char extract;
- u32 tmp_mask1, tmp_mask2, tmp_shift, tmp;
-
- if ((up->csize + up->stopb) > 7)
- value = SPORT_GET_RX32(up);
- else
- value = SPORT_GET_RX(up);
-
- pr_debug("%s value:%x, cs=%d, mask=0x%x\n", __func__, value,
- up->csize, up->rxmask);
-
- /* Extract data */
- __asm__ __volatile__ (
- "%[extr] = 0;"
- "%[mask1] = %[rxmask];"
- "%[mask2] = 0x0200(Z);"
- "%[shift] = 0;"
- "LSETUP(.Lloop_s, .Lloop_e) LC0 = %[lc];"
- ".Lloop_s:"
- "%[tmp] = extract(%[val], %[mask1].L)(Z);"
- "%[tmp] <<= %[shift];"
- "%[extr] = %[extr] | %[tmp];"
- "%[mask1] = %[mask1] - %[mask2];"
- ".Lloop_e:"
- "%[shift] += 1;"
- : [extr]"=&d"(extract), [shift]"=&d"(tmp_shift), [tmp]"=&d"(tmp),
- [mask1]"=&d"(tmp_mask1), [mask2]"=&d"(tmp_mask2)
- : [val]"d"(value), [rxmask]"d"(up->rxmask), [lc]"a"(up->csize)
- : "ASTAT", "LB0", "LC0", "LT0"
- );
-
- pr_debug(" extract:%x\n", extract);
- return extract;
-}
-
-static int sport_uart_setup(struct sport_uart_port *up, int size, int baud_rate)
-{
- int tclkdiv, rclkdiv;
- unsigned int sclk = get_sclk();
-
- /* Set TCR1 and TCR2, TFSR is not enabled for uart */
- SPORT_PUT_TCR1(up, (LATFS | ITFS | TFSR | TLSBIT | ITCLK));
- SPORT_PUT_TCR2(up, size + 1);
- pr_debug("%s TCR1:%x, TCR2:%x\n", __func__, SPORT_GET_TCR1(up), SPORT_GET_TCR2(up));
-
- /* Set RCR1 and RCR2 */
- SPORT_PUT_RCR1(up, (RCKFE | LARFS | LRFS | RFSR | IRCLK));
- SPORT_PUT_RCR2(up, (size + 1) * 2 - 1);
- pr_debug("%s RCR1:%x, RCR2:%x\n", __func__, SPORT_GET_RCR1(up), SPORT_GET_RCR2(up));
-
- tclkdiv = sclk / (2 * baud_rate) - 1;
- /* The actual uart baud rate of devices vary between +/-2%. The sport
- * RX sample rate should be faster than the double of the worst case,
- * otherwise, wrong data are received. So, set sport RX clock to be
- * 3% faster.
- */
- rclkdiv = sclk / (2 * baud_rate * 2 * 97 / 100) - 1;
- SPORT_PUT_TCLKDIV(up, tclkdiv);
- SPORT_PUT_RCLKDIV(up, rclkdiv);
- SSYNC();
- pr_debug("%s sclk:%d, baud_rate:%d, tclkdiv:%d, rclkdiv:%d\n",
- __func__, sclk, baud_rate, tclkdiv, rclkdiv);
-
- return 0;
-}
-
-static irqreturn_t sport_uart_rx_irq(int irq, void *dev_id)
-{
- struct sport_uart_port *up = dev_id;
- struct tty_port *port = &up->port.state->port;
- unsigned int ch;
-
- spin_lock(&up->port.lock);
-
- while (SPORT_GET_STAT(up) & RXNE) {
- ch = rx_one_byte(up);
- up->port.icount.rx++;
-
- if (!uart_handle_sysrq_char(&up->port, ch))
- tty_insert_flip_char(port, ch, TTY_NORMAL);
- }
-
- spin_unlock(&up->port.lock);
-
- /* XXX this won't deadlock with lowlat? */
- tty_flip_buffer_push(port);
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t sport_uart_tx_irq(int irq, void *dev_id)
-{
- struct sport_uart_port *up = dev_id;
-
- spin_lock(&up->port.lock);
- sport_uart_tx_chars(up);
- spin_unlock(&up->port.lock);
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t sport_uart_err_irq(int irq, void *dev_id)
-{
- struct sport_uart_port *up = dev_id;
- unsigned int stat = SPORT_GET_STAT(up);
-
- spin_lock(&up->port.lock);
-
- /* Overflow in RX FIFO */
- if (stat & ROVF) {
- up->port.icount.overrun++;
- tty_insert_flip_char(&up->port.state->port, 0, TTY_OVERRUN);
- SPORT_PUT_STAT(up, ROVF); /* Clear ROVF bit */
- }
- /* These should not happen */
- if (stat & (TOVF | TUVF | RUVF)) {
- pr_err("SPORT Error:%s %s %s\n",
- (stat & TOVF) ? "TX overflow" : "",
- (stat & TUVF) ? "TX underflow" : "",
- (stat & RUVF) ? "RX underflow" : "");
- SPORT_PUT_TCR1(up, SPORT_GET_TCR1(up) & ~TSPEN);
- SPORT_PUT_RCR1(up, SPORT_GET_RCR1(up) & ~RSPEN);
- }
- SSYNC();
-
- spin_unlock(&up->port.lock);
- /* XXX we don't push the overrun bit to TTY? */
-
- return IRQ_HANDLED;
-}
-
-#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS
-static unsigned int sport_get_mctrl(struct uart_port *port)
-{
- struct sport_uart_port *up = (struct sport_uart_port *)port;
- if (up->cts_pin < 0)
- return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
-
- /* CTS PIN is negative assertive. */
- if (SPORT_UART_GET_CTS(up))
- return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
- else
- return TIOCM_DSR | TIOCM_CAR;
-}
-
-static void sport_set_mctrl(struct uart_port *port, unsigned int mctrl)
-{
- struct sport_uart_port *up = (struct sport_uart_port *)port;
- if (up->rts_pin < 0)
- return;
-
- /* RTS PIN is negative assertive. */
- if (mctrl & TIOCM_RTS)
- SPORT_UART_ENABLE_RTS(up);
- else
- SPORT_UART_DISABLE_RTS(up);
-}
-
-/*
- * Handle any change of modem status signal.
- */
-static irqreturn_t sport_mctrl_cts_int(int irq, void *dev_id)
-{
- struct sport_uart_port *up = (struct sport_uart_port *)dev_id;
- unsigned int status;
-
- status = sport_get_mctrl(&up->port);
- uart_handle_cts_change(&up->port, status & TIOCM_CTS);
-
- return IRQ_HANDLED;
-}
-#else
-static unsigned int sport_get_mctrl(struct uart_port *port)
-{
- pr_debug("%s enter\n", __func__);
- return TIOCM_CTS | TIOCM_CD | TIOCM_DSR;
-}
-
-static void sport_set_mctrl(struct uart_port *port, unsigned int mctrl)
-{
- pr_debug("%s enter\n", __func__);
-}
-#endif
-
-/* Reqeust IRQ, Setup clock */
-static int sport_startup(struct uart_port *port)
-{
- struct sport_uart_port *up = (struct sport_uart_port *)port;
- int ret;
-
- pr_debug("%s enter\n", __func__);
- ret = request_irq(up->port.irq, sport_uart_rx_irq, 0,
- "SPORT_UART_RX", up);
- if (ret) {
- dev_err(port->dev, "unable to request SPORT RX interrupt\n");
- return ret;
- }
-
- ret = request_irq(up->port.irq+1, sport_uart_tx_irq, 0,
- "SPORT_UART_TX", up);
- if (ret) {
- dev_err(port->dev, "unable to request SPORT TX interrupt\n");
- goto fail1;
- }
-
- ret = request_irq(up->err_irq, sport_uart_err_irq, 0,
- "SPORT_UART_STATUS", up);
- if (ret) {
- dev_err(port->dev, "unable to request SPORT status interrupt\n");
- goto fail2;
- }
-
-#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS
- if (up->cts_pin >= 0) {
- if (request_irq(gpio_to_irq(up->cts_pin),
- sport_mctrl_cts_int,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
- 0, "BFIN_SPORT_UART_CTS", up)) {
- up->cts_pin = -1;
- dev_info(port->dev, "Unable to attach BlackFin UART over SPORT CTS interrupt. So, disable it.\n");
- }
- }
- if (up->rts_pin >= 0) {
- if (gpio_request(up->rts_pin, DRV_NAME)) {
- dev_info(port->dev, "fail to request RTS PIN at GPIO_%d\n", up->rts_pin);
- up->rts_pin = -1;
- } else
- gpio_direction_output(up->rts_pin, 0);
- }
-#endif
-
- return 0;
- fail2:
- free_irq(up->port.irq+1, up);
- fail1:
- free_irq(up->port.irq, up);
-
- return ret;
-}
-
-/*
- * sport_uart_tx_chars
- *
- * ret 1 means need to enable sport.
- * ret 0 means do nothing.
- */
-static int sport_uart_tx_chars(struct sport_uart_port *up)
-{
- struct circ_buf *xmit = &up->port.state->xmit;
-
- if (SPORT_GET_STAT(up) & TXF)
- return 0;
-
- if (up->port.x_char) {
- tx_one_byte(up, up->port.x_char);
- up->port.icount.tx++;
- up->port.x_char = 0;
- return 1;
- }
-
- if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
- /* The waiting loop to stop SPORT TX from TX interrupt is
- * too long. This may block SPORT RX interrupts and cause
- * RX FIFO overflow. So, do stop sport TX only after the last
- * char in TX FIFO is moved into the shift register.
- */
- if (SPORT_GET_STAT(up) & TXHRE)
- sport_stop_tx(&up->port);
- return 0;
- }
-
- while(!(SPORT_GET_STAT(up) & TXF) && !uart_circ_empty(xmit)) {
- tx_one_byte(up, xmit->buf[xmit->tail]);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE -1);
- up->port.icount.tx++;
- }
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(&up->port);
-
- return 1;
-}
-
-static unsigned int sport_tx_empty(struct uart_port *port)
-{
- struct sport_uart_port *up = (struct sport_uart_port *)port;
- unsigned int stat;
-
- stat = SPORT_GET_STAT(up);
- pr_debug("%s stat:%04x\n", __func__, stat);
- if (stat & TXHRE) {
- return TIOCSER_TEMT;
- } else
- return 0;
-}
-
-static void sport_stop_tx(struct uart_port *port)
-{
- struct sport_uart_port *up = (struct sport_uart_port *)port;
-
- pr_debug("%s enter\n", __func__);
-
- if (!(SPORT_GET_TCR1(up) & TSPEN))
- return;
-
- /* Although the hold register is empty, last byte is still in shift
- * register and not sent out yet. So, put a dummy data into TX FIFO.
- * Then, sport tx stops when last byte is shift out and the dummy
- * data is moved into the shift register.
- */
- SPORT_PUT_TX(up, 0xffff);
- while (!(SPORT_GET_STAT(up) & TXHRE))
- cpu_relax();
-
- SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) & ~TSPEN));
- SSYNC();
-
- return;
-}
-
-static void sport_start_tx(struct uart_port *port)
-{
- struct sport_uart_port *up = (struct sport_uart_port *)port;
-
- pr_debug("%s enter\n", __func__);
-
- /* Write data into SPORT FIFO before enable SPROT to transmit */
- if (sport_uart_tx_chars(up)) {
- /* Enable transmit, then an interrupt will generated */
- SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) | TSPEN));
- SSYNC();
- }
-
- pr_debug("%s exit\n", __func__);
-}
-
-static void sport_stop_rx(struct uart_port *port)
-{
- struct sport_uart_port *up = (struct sport_uart_port *)port;
-
- pr_debug("%s enter\n", __func__);
- /* Disable sport to stop rx */
- SPORT_PUT_RCR1(up, (SPORT_GET_RCR1(up) & ~RSPEN));
- SSYNC();
-}
-
-static void sport_break_ctl(struct uart_port *port, int break_state)
-{
- pr_debug("%s enter\n", __func__);
-}
-
-static void sport_shutdown(struct uart_port *port)
-{
- struct sport_uart_port *up = (struct sport_uart_port *)port;
-
- dev_dbg(port->dev, "%s enter\n", __func__);
-
- /* Disable sport */
- SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) & ~TSPEN));
- SPORT_PUT_RCR1(up, (SPORT_GET_RCR1(up) & ~RSPEN));
- SSYNC();
-
- free_irq(up->port.irq, up);
- free_irq(up->port.irq+1, up);
- free_irq(up->err_irq, up);
-#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS
- if (up->cts_pin >= 0)
- free_irq(gpio_to_irq(up->cts_pin), up);
- if (up->rts_pin >= 0)
- gpio_free(up->rts_pin);
-#endif
-}
-
-static const char *sport_type(struct uart_port *port)
-{
- struct sport_uart_port *up = (struct sport_uart_port *)port;
-
- pr_debug("%s enter\n", __func__);
- return up->port.type == PORT_BFIN_SPORT ? "BFIN-SPORT-UART" : NULL;
-}
-
-static void sport_release_port(struct uart_port *port)
-{
- pr_debug("%s enter\n", __func__);
-}
-
-static int sport_request_port(struct uart_port *port)
-{
- pr_debug("%s enter\n", __func__);
- return 0;
-}
-
-static void sport_config_port(struct uart_port *port, int flags)
-{
- struct sport_uart_port *up = (struct sport_uart_port *)port;
-
- pr_debug("%s enter\n", __func__);
- up->port.type = PORT_BFIN_SPORT;
-}
-
-static int sport_verify_port(struct uart_port *port, struct serial_struct *ser)
-{
- pr_debug("%s enter\n", __func__);
- return 0;
-}
-
-static void sport_set_termios(struct uart_port *port,
- struct ktermios *termios, struct ktermios *old)
-{
- struct sport_uart_port *up = (struct sport_uart_port *)port;
- unsigned long flags;
- int i;
-
- pr_debug("%s enter, c_cflag:%08x\n", __func__, termios->c_cflag);
-
-#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS
- if (old == NULL && up->cts_pin != -1)
- termios->c_cflag |= CRTSCTS;
- else if (up->cts_pin == -1)
- termios->c_cflag &= ~CRTSCTS;
-#endif
-
- switch (termios->c_cflag & CSIZE) {
- case CS8:
- up->csize = 8;
- break;
- case CS7:
- up->csize = 7;
- break;
- case CS6:
- up->csize = 6;
- break;
- case CS5:
- up->csize = 5;
- break;
- default:
- pr_warn("requested word length not supported\n");
- break;
- }
-
- if (termios->c_cflag & CSTOPB) {
- up->stopb = 1;
- }
- if (termios->c_cflag & PARENB) {
- pr_warn("PAREN bit is not supported yet\n");
- /* up->parib = 1; */
- }
-
- spin_lock_irqsave(&up->port.lock, flags);
-
- port->read_status_mask = 0;
-
- /*
- * Characters to ignore
- */
- port->ignore_status_mask = 0;
-
- /* RX extract mask */
- up->rxmask = 0x01 | (((up->csize + up->stopb) * 2 - 1) << 0x8);
- /* TX masks, 8 bit data and 1 bit stop for example:
- * mask1 = b#0111111110
- * mask2 = b#1000000000
- */
- for (i = 0, up->txmask1 = 0; i < up->csize; i++)
- up->txmask1 |= (1<<i);
- up->txmask2 = (1<<i);
- if (up->stopb) {
- ++i;
- up->txmask2 |= (1<<i);
- }
- up->txmask1 <<= 1;
- up->txmask2 <<= 1;
- /* uart baud rate */
- port->uartclk = uart_get_baud_rate(port, termios, old, 0, get_sclk()/16);
-
- /* Disable UART */
- SPORT_PUT_TCR1(up, SPORT_GET_TCR1(up) & ~TSPEN);
- SPORT_PUT_RCR1(up, SPORT_GET_RCR1(up) & ~RSPEN);
-
- sport_uart_setup(up, up->csize + up->stopb, port->uartclk);
-
- /* driver TX line high after config, one dummy data is
- * necessary to stop sport after shift one byte
- */
- SPORT_PUT_TX(up, 0xffff);
- SPORT_PUT_TX(up, 0xffff);
- SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) | TSPEN));
- SSYNC();
- while (!(SPORT_GET_STAT(up) & TXHRE))
- cpu_relax();
- SPORT_PUT_TCR1(up, SPORT_GET_TCR1(up) & ~TSPEN);
- SSYNC();
-
- /* Port speed changed, update the per-port timeout. */
- uart_update_timeout(port, termios->c_cflag, port->uartclk);
-
- /* Enable sport rx */
- SPORT_PUT_RCR1(up, SPORT_GET_RCR1(up) | RSPEN);
- SSYNC();
-
- spin_unlock_irqrestore(&up->port.lock, flags);
-}
-
-static const struct uart_ops sport_uart_ops = {
- .tx_empty = sport_tx_empty,
- .set_mctrl = sport_set_mctrl,
- .get_mctrl = sport_get_mctrl,
- .stop_tx = sport_stop_tx,
- .start_tx = sport_start_tx,
- .stop_rx = sport_stop_rx,
- .break_ctl = sport_break_ctl,
- .startup = sport_startup,
- .shutdown = sport_shutdown,
- .set_termios = sport_set_termios,
- .type = sport_type,
- .release_port = sport_release_port,
- .request_port = sport_request_port,
- .config_port = sport_config_port,
- .verify_port = sport_verify_port,
-};
-
-#define BFIN_SPORT_UART_MAX_PORTS 4
-
-static struct sport_uart_port *bfin_sport_uart_ports[BFIN_SPORT_UART_MAX_PORTS];
-
-#ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE
-#define CLASS_BFIN_SPORT_CONSOLE "bfin-sport-console"
-
-static int __init
-sport_uart_console_setup(struct console *co, char *options)
-{
- struct sport_uart_port *up;
- int baud = 57600;
- int bits = 8;
- int parity = 'n';
-# ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS
- int flow = 'r';
-# else
- int flow = 'n';
-# endif
-
- /* Check whether an invalid uart number has been specified */
- if (co->index < 0 || co->index >= BFIN_SPORT_UART_MAX_PORTS)
- return -ENODEV;
-
- up = bfin_sport_uart_ports[co->index];
- if (!up)
- return -ENODEV;
-
- if (options)
- uart_parse_options(options, &baud, &parity, &bits, &flow);
-
- return uart_set_options(&up->port, co, baud, parity, bits, flow);
-}
-
-static void sport_uart_console_putchar(struct uart_port *port, int ch)
-{
- struct sport_uart_port *up = (struct sport_uart_port *)port;
-
- while (SPORT_GET_STAT(up) & TXF)
- barrier();
-
- tx_one_byte(up, ch);
-}
-
-/*
- * Interrupts are disabled on entering
- */
-static void
-sport_uart_console_write(struct console *co, const char *s, unsigned int count)
-{
- struct sport_uart_port *up = bfin_sport_uart_ports[co->index];
- unsigned long flags;
-
- spin_lock_irqsave(&up->port.lock, flags);
-
- if (SPORT_GET_TCR1(up) & TSPEN)
- uart_console_write(&up->port, s, count, sport_uart_console_putchar);
- else {
- /* dummy data to start sport */
- while (SPORT_GET_STAT(up) & TXF)
- barrier();
- SPORT_PUT_TX(up, 0xffff);
- /* Enable transmit, then an interrupt will generated */
- SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) | TSPEN));
- SSYNC();
-
- uart_console_write(&up->port, s, count, sport_uart_console_putchar);
-
- /* Although the hold register is empty, last byte is still in shift
- * register and not sent out yet. So, put a dummy data into TX FIFO.
- * Then, sport tx stops when last byte is shift out and the dummy
- * data is moved into the shift register.
- */
- while (SPORT_GET_STAT(up) & TXF)
- barrier();
- SPORT_PUT_TX(up, 0xffff);
- while (!(SPORT_GET_STAT(up) & TXHRE))
- barrier();
-
- /* Stop sport tx transfer */
- SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) & ~TSPEN));
- SSYNC();
- }
-
- spin_unlock_irqrestore(&up->port.lock, flags);
-}
-
-static struct uart_driver sport_uart_reg;
-
-static struct console sport_uart_console = {
- .name = DEVICE_NAME,
- .write = sport_uart_console_write,
- .device = uart_console_device,
- .setup = sport_uart_console_setup,
- .flags = CON_PRINTBUFFER,
- .index = -1,
- .data = &sport_uart_reg,
-};
-
-#define SPORT_UART_CONSOLE (&sport_uart_console)
-#else
-#define SPORT_UART_CONSOLE NULL
-#endif /* CONFIG_SERIAL_BFIN_SPORT_CONSOLE */
-
-
-static struct uart_driver sport_uart_reg = {
- .owner = THIS_MODULE,
- .driver_name = DRV_NAME,
- .dev_name = DEVICE_NAME,
- .major = 204,
- .minor = 84,
- .nr = BFIN_SPORT_UART_MAX_PORTS,
- .cons = SPORT_UART_CONSOLE,
-};
-
-#ifdef CONFIG_PM
-static int sport_uart_suspend(struct device *dev)
-{
- struct sport_uart_port *sport = dev_get_drvdata(dev);
-
- dev_dbg(dev, "%s enter\n", __func__);
- if (sport)
- uart_suspend_port(&sport_uart_reg, &sport->port);
-
- return 0;
-}
-
-static int sport_uart_resume(struct device *dev)
-{
- struct sport_uart_port *sport = dev_get_drvdata(dev);
-
- dev_dbg(dev, "%s enter\n", __func__);
- if (sport)
- uart_resume_port(&sport_uart_reg, &sport->port);
-
- return 0;
-}
-
-static const struct dev_pm_ops bfin_sport_uart_dev_pm_ops = {
- .suspend = sport_uart_suspend,
- .resume = sport_uart_resume,
-};
-#endif
-
-static int sport_uart_probe(struct platform_device *pdev)
-{
- struct resource *res;
- struct sport_uart_port *sport;
- int ret = 0;
-
- dev_dbg(&pdev->dev, "%s enter\n", __func__);
-
- if (pdev->id < 0 || pdev->id >= BFIN_SPORT_UART_MAX_PORTS) {
- dev_err(&pdev->dev, "Wrong sport uart platform device id.\n");
- return -ENOENT;
- }
-
- if (bfin_sport_uart_ports[pdev->id] == NULL) {
- bfin_sport_uart_ports[pdev->id] =
- kzalloc(sizeof(struct sport_uart_port), GFP_KERNEL);
- sport = bfin_sport_uart_ports[pdev->id];
- if (!sport) {
- dev_err(&pdev->dev,
- "Fail to malloc sport_uart_port\n");
- return -ENOMEM;
- }
-
- ret = peripheral_request_list(dev_get_platdata(&pdev->dev),
- DRV_NAME);
- if (ret) {
- dev_err(&pdev->dev,
- "Fail to request SPORT peripherals\n");
- goto out_error_free_mem;
- }
-
- spin_lock_init(&sport->port.lock);
- sport->port.fifosize = SPORT_TX_FIFO_SIZE,
- sport->port.ops = &sport_uart_ops;
- sport->port.line = pdev->id;
- sport->port.iotype = UPIO_MEM;
- sport->port.flags = UPF_BOOT_AUTOCONF;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
- dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");
- ret = -ENOENT;
- goto out_error_free_peripherals;
- }
-
- sport->port.membase = ioremap(res->start, resource_size(res));
- if (!sport->port.membase) {
- dev_err(&pdev->dev, "Cannot map sport IO\n");
- ret = -ENXIO;
- goto out_error_free_peripherals;
- }
- sport->port.mapbase = res->start;
-
- sport->port.irq = platform_get_irq(pdev, 0);
- if ((int)sport->port.irq < 0) {
- dev_err(&pdev->dev, "No sport RX/TX IRQ specified\n");
- ret = -ENOENT;
- goto out_error_unmap;
- }
-
- sport->err_irq = platform_get_irq(pdev, 1);
- if (sport->err_irq < 0) {
- dev_err(&pdev->dev, "No sport status IRQ specified\n");
- ret = -ENOENT;
- goto out_error_unmap;
- }
-#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS
- res = platform_get_resource(pdev, IORESOURCE_IO, 0);
- if (res == NULL)
- sport->cts_pin = -1;
- else
- sport->cts_pin = res->start;
-
- res = platform_get_resource(pdev, IORESOURCE_IO, 1);
- if (res == NULL)
- sport->rts_pin = -1;
- else
- sport->rts_pin = res->start;
-#endif
- }
-
-#ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE
- if (!is_early_platform_device(pdev)) {
-#endif
- sport = bfin_sport_uart_ports[pdev->id];
- sport->port.dev = &pdev->dev;
- dev_set_drvdata(&pdev->dev, sport);
- ret = uart_add_one_port(&sport_uart_reg, &sport->port);
-#ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE
- }
-#endif
- if (!ret)
- return 0;
-
- if (sport) {
-out_error_unmap:
- iounmap(sport->port.membase);
-out_error_free_peripherals:
- peripheral_free_list(dev_get_platdata(&pdev->dev));
-out_error_free_mem:
- kfree(sport);
- bfin_sport_uart_ports[pdev->id] = NULL;
- }
-
- return ret;
-}
-
-static int sport_uart_remove(struct platform_device *pdev)
-{
- struct sport_uart_port *sport = platform_get_drvdata(pdev);
-
- dev_dbg(&pdev->dev, "%s enter\n", __func__);
- dev_set_drvdata(&pdev->dev, NULL);
-
- if (sport) {
- uart_remove_one_port(&sport_uart_reg, &sport->port);
- iounmap(sport->port.membase);
- peripheral_free_list(dev_get_platdata(&pdev->dev));
- kfree(sport);
- bfin_sport_uart_ports[pdev->id] = NULL;
- }
-
- return 0;
-}
-
-static struct platform_driver sport_uart_driver = {
- .probe = sport_uart_probe,
- .remove = sport_uart_remove,
- .driver = {
- .name = DRV_NAME,
-#ifdef CONFIG_PM
- .pm = &bfin_sport_uart_dev_pm_ops,
-#endif
- },
-};
-
-#ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE
-static struct early_platform_driver early_sport_uart_driver __initdata = {
- .class_str = CLASS_BFIN_SPORT_CONSOLE,
- .pdrv = &sport_uart_driver,
- .requested_id = EARLY_PLATFORM_ID_UNSET,
-};
-
-static int __init sport_uart_rs_console_init(void)
-{
- early_platform_driver_register(&early_sport_uart_driver, DRV_NAME);
-
- early_platform_driver_probe(CLASS_BFIN_SPORT_CONSOLE,
- BFIN_SPORT_UART_MAX_PORTS, 0);
-
- register_console(&sport_uart_console);
-
- return 0;
-}
-console_initcall(sport_uart_rs_console_init);
-#endif
-
-static int __init sport_uart_init(void)
-{
- int ret;
-
- pr_info("Blackfin uart over sport driver\n");
-
- ret = uart_register_driver(&sport_uart_reg);
- if (ret) {
- pr_err("failed to register %s:%d\n",
- sport_uart_reg.driver_name, ret);
- return ret;
- }
-
- ret = platform_driver_register(&sport_uart_driver);
- if (ret) {
- pr_err("failed to register sport uart driver:%d\n", ret);
- uart_unregister_driver(&sport_uart_reg);
- }
-
- return ret;
-}
-module_init(sport_uart_init);
-
-static void __exit sport_uart_exit(void)
-{
- platform_driver_unregister(&sport_uart_driver);
- uart_unregister_driver(&sport_uart_reg);
-}
-module_exit(sport_uart_exit);
-
-MODULE_AUTHOR("Sonic Zhang, Roy Huang");
-MODULE_DESCRIPTION("Blackfin serial over SPORT driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/bfin_sport_uart.h b/drivers/tty/serial/bfin_sport_uart.h
deleted file mode 100644
index 4b12f45d6580..000000000000
--- a/drivers/tty/serial/bfin_sport_uart.h
+++ /dev/null
@@ -1,86 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Blackfin On-Chip Sport Emulated UART Driver
- *
- * Copyright 2006-2008 Analog Devices Inc.
- *
- * Enter bugs at http://blackfin.uclinux.org/
- */
-
-/*
- * This driver and the hardware supported are in term of EE-191 of ADI.
- * http://www.analog.com/static/imported-files/application_notes/EE191.pdf
- * This application note describe how to implement a UART on a Sharc DSP,
- * but this driver is implemented on Blackfin Processor.
- * Transmit Frame Sync is not used by this driver to transfer data out.
- */
-
-#ifndef _BFIN_SPORT_UART_H
-#define _BFIN_SPORT_UART_H
-
-#define OFFSET_TCR1 0x00 /* Transmit Configuration 1 Register */
-#define OFFSET_TCR2 0x04 /* Transmit Configuration 2 Register */
-#define OFFSET_TCLKDIV 0x08 /* Transmit Serial Clock Divider Register */
-#define OFFSET_TFSDIV 0x0C /* Transmit Frame Sync Divider Register */
-#define OFFSET_TX 0x10 /* Transmit Data Register */
-#define OFFSET_RX 0x18 /* Receive Data Register */
-#define OFFSET_RCR1 0x20 /* Receive Configuration 1 Register */
-#define OFFSET_RCR2 0x24 /* Receive Configuration 2 Register */
-#define OFFSET_RCLKDIV 0x28 /* Receive Serial Clock Divider Register */
-#define OFFSET_RFSDIV 0x2c /* Receive Frame Sync Divider Register */
-#define OFFSET_STAT 0x30 /* Status Register */
-
-#define SPORT_GET_TCR1(sport) bfin_read16(((sport)->port.membase + OFFSET_TCR1))
-#define SPORT_GET_TCR2(sport) bfin_read16(((sport)->port.membase + OFFSET_TCR2))
-#define SPORT_GET_TCLKDIV(sport) bfin_read16(((sport)->port.membase + OFFSET_TCLKDIV))
-#define SPORT_GET_TFSDIV(sport) bfin_read16(((sport)->port.membase + OFFSET_TFSDIV))
-#define SPORT_GET_TX(sport) bfin_read16(((sport)->port.membase + OFFSET_TX))
-#define SPORT_GET_RX(sport) bfin_read16(((sport)->port.membase + OFFSET_RX))
-/*
- * If another interrupt fires while doing a 32-bit read from RX FIFO,
- * a fake RX underflow error will be generated. So disable interrupts
- * to prevent interruption while reading the FIFO.
- */
-#define SPORT_GET_RX32(sport) \
-({ \
- unsigned int __ret; \
- unsigned long flags; \
- if (ANOMALY_05000473) \
- local_irq_save(flags); \
- __ret = bfin_read32((sport)->port.membase + OFFSET_RX); \
- if (ANOMALY_05000473) \
- local_irq_restore(flags); \
- __ret; \
-})
-#define SPORT_GET_RCR1(sport) bfin_read16(((sport)->port.membase + OFFSET_RCR1))
-#define SPORT_GET_RCR2(sport) bfin_read16(((sport)->port.membase + OFFSET_RCR2))
-#define SPORT_GET_RCLKDIV(sport) bfin_read16(((sport)->port.membase + OFFSET_RCLKDIV))
-#define SPORT_GET_RFSDIV(sport) bfin_read16(((sport)->port.membase + OFFSET_RFSDIV))
-#define SPORT_GET_STAT(sport) bfin_read16(((sport)->port.membase + OFFSET_STAT))
-
-#define SPORT_PUT_TCR1(sport, v) bfin_write16(((sport)->port.membase + OFFSET_TCR1), v)
-#define SPORT_PUT_TCR2(sport, v) bfin_write16(((sport)->port.membase + OFFSET_TCR2), v)
-#define SPORT_PUT_TCLKDIV(sport, v) bfin_write16(((sport)->port.membase + OFFSET_TCLKDIV), v)
-#define SPORT_PUT_TFSDIV(sport, v) bfin_write16(((sport)->port.membase + OFFSET_TFSDIV), v)
-#define SPORT_PUT_TX(sport, v) bfin_write16(((sport)->port.membase + OFFSET_TX), v)
-#define SPORT_PUT_RX(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RX), v)
-#define SPORT_PUT_RCR1(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RCR1), v)
-#define SPORT_PUT_RCR2(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RCR2), v)
-#define SPORT_PUT_RCLKDIV(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RCLKDIV), v)
-#define SPORT_PUT_RFSDIV(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RFSDIV), v)
-#define SPORT_PUT_STAT(sport, v) bfin_write16(((sport)->port.membase + OFFSET_STAT), v)
-
-#define SPORT_TX_FIFO_SIZE 8
-
-#define SPORT_UART_GET_CTS(x) gpio_get_value(x->cts_pin)
-#define SPORT_UART_DISABLE_RTS(x) gpio_set_value(x->rts_pin, 1)
-#define SPORT_UART_ENABLE_RTS(x) gpio_set_value(x->rts_pin, 0)
-
-#if defined(CONFIG_SERIAL_BFIN_SPORT0_UART_CTSRTS) \
- || defined(CONFIG_SERIAL_BFIN_SPORT1_UART_CTSRTS) \
- || defined(CONFIG_SERIAL_BFIN_SPORT2_UART_CTSRTS) \
- || defined(CONFIG_SERIAL_BFIN_SPORT3_UART_CTSRTS)
-# define CONFIG_SERIAL_BFIN_SPORT_CTSRTS
-#endif
-
-#endif /* _BFIN_SPORT_UART_H */
diff --git a/drivers/tty/serial/bfin_uart.c b/drivers/tty/serial/bfin_uart.c
deleted file mode 100644
index 4755fa696321..000000000000
--- a/drivers/tty/serial/bfin_uart.c
+++ /dev/null
@@ -1,1551 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Blackfin On-Chip Serial Driver
- *
- * Copyright 2006-2011 Analog Devices Inc.
- *
- * Enter bugs at http://blackfin.uclinux.org/
- */
-
-#if defined(CONFIG_SERIAL_BFIN_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
-#define SUPPORT_SYSRQ
-#endif
-
-#define DRIVER_NAME "bfin-uart"
-#define pr_fmt(fmt) DRIVER_NAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/ioport.h>
-#include <linux/gfp.h>
-#include <linux/io.h>
-#include <linux/init.h>
-#include <linux/console.h>
-#include <linux/sysrq.h>
-#include <linux/platform_device.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/serial_core.h>
-#include <linux/gpio.h>
-#include <linux/irq.h>
-#include <linux/kgdb.h>
-#include <linux/slab.h>
-#include <linux/dma-mapping.h>
-
-#include <asm/portmux.h>
-#include <asm/cacheflush.h>
-#include <asm/dma.h>
-#include <asm/bfin_serial.h>
-
-#ifdef CONFIG_SERIAL_BFIN_MODULE
-# undef CONFIG_EARLY_PRINTK
-#endif
-
-/* UART name and device definitions */
-#define BFIN_SERIAL_DEV_NAME "ttyBF"
-#define BFIN_SERIAL_MAJOR 204
-#define BFIN_SERIAL_MINOR 64
-
-static struct bfin_serial_port *bfin_serial_ports[BFIN_UART_NR_PORTS];
-
-#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \
- defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE)
-
-# ifndef CONFIG_SERIAL_BFIN_PIO
-# error KGDB only support UART in PIO mode.
-# endif
-
-static int kgdboc_port_line;
-static int kgdboc_break_enabled;
-#endif
-/*
- * Setup for console. Argument comes from the menuconfig
- */
-#define DMA_RX_XCOUNT 512
-#define DMA_RX_YCOUNT (PAGE_SIZE / DMA_RX_XCOUNT)
-
-#define DMA_RX_FLUSH_JIFFIES (HZ / 50)
-
-#ifdef CONFIG_SERIAL_BFIN_DMA
-static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart);
-#else
-static void bfin_serial_tx_chars(struct bfin_serial_port *uart);
-#endif
-
-static void bfin_serial_reset_irda(struct uart_port *port);
-
-#if defined(SERIAL_BFIN_CTSRTS) || \
- defined(SERIAL_BFIN_HARD_CTSRTS)
-static unsigned int bfin_serial_get_mctrl(struct uart_port *port)
-{
- struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
- if (uart->cts_pin < 0)
- return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
-
- /* CTS PIN is negative assertive. */
- if (UART_GET_CTS(uart))
- return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
- else
- return TIOCM_DSR | TIOCM_CAR;
-}
-
-static void bfin_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
-{
- struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
- if (uart->rts_pin < 0)
- return;
-
- /* RTS PIN is negative assertive. */
- if (mctrl & TIOCM_RTS)
- UART_ENABLE_RTS(uart);
- else
- UART_DISABLE_RTS(uart);
-}
-
-/*
- * Handle any change of modem status signal.
- */
-static irqreturn_t bfin_serial_mctrl_cts_int(int irq, void *dev_id)
-{
- struct bfin_serial_port *uart = dev_id;
- struct uart_port *uport = &uart->port;
- unsigned int status = bfin_serial_get_mctrl(uport);
-#ifdef SERIAL_BFIN_HARD_CTSRTS
-
- UART_CLEAR_SCTS(uart);
- if (uport->hw_stopped) {
- if (status) {
- uport->hw_stopped = 0;
- uart_write_wakeup(uport);
- }
- } else {
- if (!status)
- uport->hw_stopped = 1;
- }
-#else
- uart_handle_cts_change(uport, status & TIOCM_CTS);
-#endif
-
- return IRQ_HANDLED;
-}
-#else
-static unsigned int bfin_serial_get_mctrl(struct uart_port *port)
-{
- return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
-}
-
-static void bfin_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
-{
-}
-#endif
-
-/*
- * interrupts are disabled on entry
- */
-static void bfin_serial_stop_tx(struct uart_port *port)
-{
- struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
-#ifdef CONFIG_SERIAL_BFIN_DMA
- struct circ_buf *xmit = &uart->port.state->xmit;
-#endif
-
- while (!(UART_GET_LSR(uart) & TEMT))
- cpu_relax();
-
-#ifdef CONFIG_SERIAL_BFIN_DMA
- disable_dma(uart->tx_dma_channel);
- xmit->tail = (xmit->tail + uart->tx_count) & (UART_XMIT_SIZE - 1);
- uart->port.icount.tx += uart->tx_count;
- uart->tx_count = 0;
- uart->tx_done = 1;
-#else
-#if defined(CONFIG_BF54x) || defined(CONFIG_BF60x)
- /* Clear TFI bit */
- UART_PUT_LSR(uart, TFI);
-#endif
- UART_CLEAR_IER(uart, ETBEI);
-#endif
-}
-
-/*
- * port is locked and interrupts are disabled
- */
-static void bfin_serial_start_tx(struct uart_port *port)
-{
- struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
- struct tty_struct *tty = uart->port.state->port.tty;
-
- /*
- * To avoid losting RX interrupt, we reset IR function
- * before sending data.
- */
- if (tty->termios.c_line == N_IRDA)
- bfin_serial_reset_irda(port);
-
-#ifdef CONFIG_SERIAL_BFIN_DMA
- if (uart->tx_done)
- bfin_serial_dma_tx_chars(uart);
-#else
- UART_SET_IER(uart, ETBEI);
- bfin_serial_tx_chars(uart);
-#endif
-}
-
-/*
- * Interrupts are enabled
- */
-static void bfin_serial_stop_rx(struct uart_port *port)
-{
- struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
-
- UART_CLEAR_IER(uart, ERBFI);
-}
-
-#if ANOMALY_05000363 && defined(CONFIG_SERIAL_BFIN_PIO)
-# define UART_GET_ANOMALY_THRESHOLD(uart) ((uart)->anomaly_threshold)
-# define UART_SET_ANOMALY_THRESHOLD(uart, v) ((uart)->anomaly_threshold = (v))
-#else
-# define UART_GET_ANOMALY_THRESHOLD(uart) 0
-# define UART_SET_ANOMALY_THRESHOLD(uart, v)
-#endif
-
-#ifdef CONFIG_SERIAL_BFIN_PIO
-static void bfin_serial_rx_chars(struct bfin_serial_port *uart)
-{
- unsigned int status, ch, flg;
- static u64 anomaly_start;
-
- status = UART_GET_LSR(uart);
- UART_CLEAR_LSR(uart);
-
- ch = UART_GET_CHAR(uart);
- uart->port.icount.rx++;
-
-#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \
- defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE)
- if (kgdb_connected && kgdboc_port_line == uart->port.line
- && kgdboc_break_enabled)
- if (ch == 0x3) {/* Ctrl + C */
- kgdb_breakpoint();
- return;
- }
-
- if (!uart->port.state)
- return;
-#endif
- if (ANOMALY_05000363) {
- /* The BF533 (and BF561) family of processors have a nice anomaly
- * where they continuously generate characters for a "single" break.
- * We have to basically ignore this flood until the "next" valid
- * character comes across. Due to the nature of the flood, it is
- * not possible to reliably catch bytes that are sent too quickly
- * after this break. So application code talking to the Blackfin
- * which sends a break signal must allow at least 1.5 character
- * times after the end of the break for things to stabilize. This
- * timeout was picked as it must absolutely be larger than 1
- * character time +/- some percent. So 1.5 sounds good. All other
- * Blackfin families operate properly. Woo.
- */
- if (anomaly_start > 0) {
- u64 curr, nsecs, threshold_ns;
-
- if ((~ch & (~ch + 1)) & 0xff)
- goto known_good_char;
-
- curr = ktime_get_ns();
- nsecs = curr - anomaly_start;
- if (nsecs >> 32)
- goto known_good_char;
-
- threshold_ns = UART_GET_ANOMALY_THRESHOLD(uart)
- * NSEC_PER_USEC;
- if (nsecs > threshold_ns)
- goto known_good_char;
-
- if (ch)
- anomaly_start = 0;
- else
- anomaly_start = curr;
-
- return;
-
- known_good_char:
- status &= ~BI;
- anomaly_start = 0;
- }
- }
-
- if (status & BI) {
- if (ANOMALY_05000363)
- if (bfin_revid() < 5)
- anomaly_start = ktime_get_ns();
- uart->port.icount.brk++;
- if (uart_handle_break(&uart->port))
- goto ignore_char;
- status &= ~(PE | FE);
- }
- if (status & PE)
- uart->port.icount.parity++;
- if (status & OE)
- uart->port.icount.overrun++;
- if (status & FE)
- uart->port.icount.frame++;
-
- status &= uart->port.read_status_mask;
-
- if (status & BI)
- flg = TTY_BREAK;
- else if (status & PE)
- flg = TTY_PARITY;
- else if (status & FE)
- flg = TTY_FRAME;
- else
- flg = TTY_NORMAL;
-
- if (uart_handle_sysrq_char(&uart->port, ch))
- goto ignore_char;
-
- uart_insert_char(&uart->port, status, OE, ch, flg);
-
- ignore_char:
- tty_flip_buffer_push(&uart->port.state->port);
-}
-
-static void bfin_serial_tx_chars(struct bfin_serial_port *uart)
-{
- struct circ_buf *xmit = &uart->port.state->xmit;
-
- if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) {
-#if defined(CONFIG_BF54x) || defined(CONFIG_BF60x)
- /* Clear TFI bit */
- UART_PUT_LSR(uart, TFI);
-#endif
- /* Anomaly notes:
- * 05000215 - we always clear ETBEI within last UART TX
- * interrupt to end a string. It is always set
- * when start a new tx.
- */
- UART_CLEAR_IER(uart, ETBEI);
- return;
- }
-
- if (uart->port.x_char) {
- UART_PUT_CHAR(uart, uart->port.x_char);
- uart->port.icount.tx++;
- uart->port.x_char = 0;
- }
-
- while ((UART_GET_LSR(uart) & THRE) && xmit->tail != xmit->head) {
- UART_PUT_CHAR(uart, xmit->buf[xmit->tail]);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- uart->port.icount.tx++;
- }
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(&uart->port);
-}
-
-static irqreturn_t bfin_serial_rx_int(int irq, void *dev_id)
-{
- struct bfin_serial_port *uart = dev_id;
-
- while (UART_GET_LSR(uart) & DR)
- bfin_serial_rx_chars(uart);
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t bfin_serial_tx_int(int irq, void *dev_id)
-{
- struct bfin_serial_port *uart = dev_id;
-
- spin_lock(&uart->port.lock);
- if (UART_GET_LSR(uart) & THRE)
- bfin_serial_tx_chars(uart);
- spin_unlock(&uart->port.lock);
-
- return IRQ_HANDLED;
-}
-#endif
-
-#ifdef CONFIG_SERIAL_BFIN_DMA
-static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart)
-{
- struct circ_buf *xmit = &uart->port.state->xmit;
-
- uart->tx_done = 0;
-
- if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) {
- uart->tx_count = 0;
- uart->tx_done = 1;
- return;
- }
-
- if (uart->port.x_char) {
- UART_PUT_CHAR(uart, uart->port.x_char);
- uart->port.icount.tx++;
- uart->port.x_char = 0;
- }
-
- uart->tx_count = CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE);
- if (uart->tx_count > (UART_XMIT_SIZE - xmit->tail))
- uart->tx_count = UART_XMIT_SIZE - xmit->tail;
- blackfin_dcache_flush_range((unsigned long)(xmit->buf+xmit->tail),
- (unsigned long)(xmit->buf+xmit->tail+uart->tx_count));
- set_dma_config(uart->tx_dma_channel,
- set_bfin_dma_config(DIR_READ, DMA_FLOW_STOP,
- INTR_ON_BUF,
- DIMENSION_LINEAR,
- DATA_SIZE_8,
- DMA_SYNC_RESTART));
- set_dma_start_addr(uart->tx_dma_channel, (unsigned long)(xmit->buf+xmit->tail));
- set_dma_x_count(uart->tx_dma_channel, uart->tx_count);
- set_dma_x_modify(uart->tx_dma_channel, 1);
- SSYNC();
- enable_dma(uart->tx_dma_channel);
-
- UART_SET_IER(uart, ETBEI);
-}
-
-static void bfin_serial_dma_rx_chars(struct bfin_serial_port *uart)
-{
- int i, flg, status;
-
- status = UART_GET_LSR(uart);
- UART_CLEAR_LSR(uart);
-
- uart->port.icount.rx +=
- CIRC_CNT(uart->rx_dma_buf.head, uart->rx_dma_buf.tail,
- UART_XMIT_SIZE);
-
- if (status & BI) {
- uart->port.icount.brk++;
- if (uart_handle_break(&uart->port))
- goto dma_ignore_char;
- status &= ~(PE | FE);
- }
- if (status & PE)
- uart->port.icount.parity++;
- if (status & OE)
- uart->port.icount.overrun++;
- if (status & FE)
- uart->port.icount.frame++;
-
- status &= uart->port.read_status_mask;
-
- if (status & BI)
- flg = TTY_BREAK;
- else if (status & PE)
- flg = TTY_PARITY;
- else if (status & FE)
- flg = TTY_FRAME;
- else
- flg = TTY_NORMAL;
-
- for (i = uart->rx_dma_buf.tail; ; i++) {
- if (i >= UART_XMIT_SIZE)
- i = 0;
- if (i == uart->rx_dma_buf.head)
- break;
- if (!uart_handle_sysrq_char(&uart->port, uart->rx_dma_buf.buf[i]))
- uart_insert_char(&uart->port, status, OE,
- uart->rx_dma_buf.buf[i], flg);
- }
-
- dma_ignore_char:
- tty_flip_buffer_push(&uart->port.state->port);
-}
-
-void bfin_serial_rx_dma_timeout(struct timer_list *t)
-{
- struct bfin_serial_port *uart = from_timer(uart, t, rx_dma_timer);
- int x_pos, pos;
- unsigned long flags;
-
- dma_disable_irq_nosync(uart->rx_dma_channel);
- spin_lock_irqsave(&uart->rx_lock, flags);
-
- /* 2D DMA RX buffer ring is used. Because curr_y_count and
- * curr_x_count can't be read as an atomic operation,
- * curr_y_count should be read before curr_x_count. When
- * curr_x_count is read, curr_y_count may already indicate
- * next buffer line. But, the position calculated here is
- * still indicate the old line. The wrong position data may
- * be smaller than current buffer tail, which cause garbages
- * are received if it is not prohibit.
- */
- uart->rx_dma_nrows = get_dma_curr_ycount(uart->rx_dma_channel);
- x_pos = get_dma_curr_xcount(uart->rx_dma_channel);
- uart->rx_dma_nrows = DMA_RX_YCOUNT - uart->rx_dma_nrows;
- if (uart->rx_dma_nrows == DMA_RX_YCOUNT || x_pos == 0)
- uart->rx_dma_nrows = 0;
- x_pos = DMA_RX_XCOUNT - x_pos;
- if (x_pos == DMA_RX_XCOUNT)
- x_pos = 0;
-
- pos = uart->rx_dma_nrows * DMA_RX_XCOUNT + x_pos;
- /* Ignore receiving data if new position is in the same line of
- * current buffer tail and small.
- */
- if (pos > uart->rx_dma_buf.tail ||
- uart->rx_dma_nrows < (uart->rx_dma_buf.tail/DMA_RX_XCOUNT)) {
- uart->rx_dma_buf.head = pos;
- bfin_serial_dma_rx_chars(uart);
- uart->rx_dma_buf.tail = uart->rx_dma_buf.head;
- }
-
- spin_unlock_irqrestore(&uart->rx_lock, flags);
- dma_enable_irq(uart->rx_dma_channel);
-
- mod_timer(&(uart->rx_dma_timer), jiffies + DMA_RX_FLUSH_JIFFIES);
-}
-
-static irqreturn_t bfin_serial_dma_tx_int(int irq, void *dev_id)
-{
- struct bfin_serial_port *uart = dev_id;
- struct circ_buf *xmit = &uart->port.state->xmit;
-
- spin_lock(&uart->port.lock);
- if (!(get_dma_curr_irqstat(uart->tx_dma_channel)&DMA_RUN)) {
- disable_dma(uart->tx_dma_channel);
- clear_dma_irqstat(uart->tx_dma_channel);
- /* Anomaly notes:
- * 05000215 - we always clear ETBEI within last UART TX
- * interrupt to end a string. It is always set
- * when start a new tx.
- */
- UART_CLEAR_IER(uart, ETBEI);
- uart->port.icount.tx += uart->tx_count;
- if (!(xmit->tail == 0 && xmit->head == 0)) {
- xmit->tail = (xmit->tail + uart->tx_count) & (UART_XMIT_SIZE - 1);
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(&uart->port);
- }
-
- bfin_serial_dma_tx_chars(uart);
- }
-
- spin_unlock(&uart->port.lock);
- return IRQ_HANDLED;
-}
-
-static irqreturn_t bfin_serial_dma_rx_int(int irq, void *dev_id)
-{
- struct bfin_serial_port *uart = dev_id;
- unsigned int irqstat;
- int x_pos, pos;
-
- spin_lock(&uart->rx_lock);
- irqstat = get_dma_curr_irqstat(uart->rx_dma_channel);
- clear_dma_irqstat(uart->rx_dma_channel);
-
- uart->rx_dma_nrows = get_dma_curr_ycount(uart->rx_dma_channel);
- x_pos = get_dma_curr_xcount(uart->rx_dma_channel);
- uart->rx_dma_nrows = DMA_RX_YCOUNT - uart->rx_dma_nrows;
- if (uart->rx_dma_nrows == DMA_RX_YCOUNT || x_pos == 0)
- uart->rx_dma_nrows = 0;
-
- pos = uart->rx_dma_nrows * DMA_RX_XCOUNT;
- if (pos > uart->rx_dma_buf.tail ||
- uart->rx_dma_nrows < (uart->rx_dma_buf.tail/DMA_RX_XCOUNT)) {
- uart->rx_dma_buf.head = pos;
- bfin_serial_dma_rx_chars(uart);
- uart->rx_dma_buf.tail = uart->rx_dma_buf.head;
- }
-
- spin_unlock(&uart->rx_lock);
-
- return IRQ_HANDLED;
-}
-#endif
-
-/*
- * Return TIOCSER_TEMT when transmitter is not busy.
- */
-static unsigned int bfin_serial_tx_empty(struct uart_port *port)
-{
- struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
- unsigned int lsr;
-
- lsr = UART_GET_LSR(uart);
- if (lsr & TEMT)
- return TIOCSER_TEMT;
- else
- return 0;
-}
-
-static void bfin_serial_break_ctl(struct uart_port *port, int break_state)
-{
- struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
- u32 lcr = UART_GET_LCR(uart);
- if (break_state)
- lcr |= SB;
- else
- lcr &= ~SB;
- UART_PUT_LCR(uart, lcr);
- SSYNC();
-}
-
-static int bfin_serial_startup(struct uart_port *port)
-{
- struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
-
-#ifdef CONFIG_SERIAL_BFIN_DMA
- dma_addr_t dma_handle;
-
- if (request_dma(uart->rx_dma_channel, "BFIN_UART_RX") < 0) {
- printk(KERN_NOTICE "Unable to attach Blackfin UART RX DMA channel\n");
- return -EBUSY;
- }
-
- if (request_dma(uart->tx_dma_channel, "BFIN_UART_TX") < 0) {
- printk(KERN_NOTICE "Unable to attach Blackfin UART TX DMA channel\n");
- free_dma(uart->rx_dma_channel);
- return -EBUSY;
- }
-
- set_dma_callback(uart->rx_dma_channel, bfin_serial_dma_rx_int, uart);
- set_dma_callback(uart->tx_dma_channel, bfin_serial_dma_tx_int, uart);
-
- uart->rx_dma_buf.buf = (unsigned char *)dma_alloc_coherent(NULL, PAGE_SIZE, &dma_handle, GFP_DMA);
- uart->rx_dma_buf.head = 0;
- uart->rx_dma_buf.tail = 0;
- uart->rx_dma_nrows = 0;
-
- set_dma_config(uart->rx_dma_channel,
- set_bfin_dma_config(DIR_WRITE, DMA_FLOW_AUTO,
- INTR_ON_ROW, DIMENSION_2D,
- DATA_SIZE_8,
- DMA_SYNC_RESTART));
- set_dma_x_count(uart->rx_dma_channel, DMA_RX_XCOUNT);
- set_dma_x_modify(uart->rx_dma_channel, 1);
- set_dma_y_count(uart->rx_dma_channel, DMA_RX_YCOUNT);
- set_dma_y_modify(uart->rx_dma_channel, 1);
- set_dma_start_addr(uart->rx_dma_channel, (unsigned long)uart->rx_dma_buf.buf);
- enable_dma(uart->rx_dma_channel);
-
- uart->rx_dma_timer.expires = jiffies + DMA_RX_FLUSH_JIFFIES;
- add_timer(&(uart->rx_dma_timer));
-#else
-# if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \
- defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE)
- if (kgdboc_port_line == uart->port.line && kgdboc_break_enabled)
- kgdboc_break_enabled = 0;
- else {
-# endif
- if (request_irq(uart->rx_irq, bfin_serial_rx_int, 0,
- "BFIN_UART_RX", uart)) {
- printk(KERN_NOTICE "Unable to attach BlackFin UART RX interrupt\n");
- return -EBUSY;
- }
-
- if (request_irq
- (uart->tx_irq, bfin_serial_tx_int, 0,
- "BFIN_UART_TX", uart)) {
- printk(KERN_NOTICE "Unable to attach BlackFin UART TX interrupt\n");
- free_irq(uart->rx_irq, uart);
- return -EBUSY;
- }
-
-# ifdef CONFIG_BF54x
- {
- /*
- * UART2 and UART3 on BF548 share interrupt PINs and DMA
- * controllers with SPORT2 and SPORT3. UART rx and tx
- * interrupts are generated in PIO mode only when configure
- * their peripheral mapping registers properly, which means
- * request corresponding DMA channels in PIO mode as well.
- */
- unsigned uart_dma_ch_rx, uart_dma_ch_tx;
-
- switch (uart->rx_irq) {
- case IRQ_UART3_RX:
- uart_dma_ch_rx = CH_UART3_RX;
- uart_dma_ch_tx = CH_UART3_TX;
- break;
- case IRQ_UART2_RX:
- uart_dma_ch_rx = CH_UART2_RX;
- uart_dma_ch_tx = CH_UART2_TX;
- break;
- default:
- uart_dma_ch_rx = uart_dma_ch_tx = 0;
- break;
- }
-
- if (uart_dma_ch_rx &&
- request_dma(uart_dma_ch_rx, "BFIN_UART_RX") < 0) {
- printk(KERN_NOTICE"Fail to attach UART interrupt\n");
- free_irq(uart->rx_irq, uart);
- free_irq(uart->tx_irq, uart);
- return -EBUSY;
- }
- if (uart_dma_ch_tx &&
- request_dma(uart_dma_ch_tx, "BFIN_UART_TX") < 0) {
- printk(KERN_NOTICE "Fail to attach UART interrupt\n");
- free_dma(uart_dma_ch_rx);
- free_irq(uart->rx_irq, uart);
- free_irq(uart->tx_irq, uart);
- return -EBUSY;
- }
- }
-# endif
-# if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \
- defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE)
- }
-# endif
-#endif
-
-#ifdef SERIAL_BFIN_CTSRTS
- if (uart->cts_pin >= 0) {
- if (request_irq(gpio_to_irq(uart->cts_pin),
- bfin_serial_mctrl_cts_int,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
- 0, "BFIN_UART_CTS", uart)) {
- uart->cts_pin = -1;
- pr_info("Unable to attach BlackFin UART CTS interrupt. So, disable it.\n");
- }
- }
- if (uart->rts_pin >= 0) {
- if (gpio_request(uart->rts_pin, DRIVER_NAME)) {
- pr_info("fail to request RTS PIN at GPIO_%d\n", uart->rts_pin);
- uart->rts_pin = -1;
- } else
- gpio_direction_output(uart->rts_pin, 0);
- }
-#endif
-#ifdef SERIAL_BFIN_HARD_CTSRTS
- if (uart->cts_pin >= 0) {
- if (request_irq(uart->status_irq, bfin_serial_mctrl_cts_int,
- 0, "BFIN_UART_MODEM_STATUS", uart)) {
- uart->cts_pin = -1;
- dev_info(port->dev, "Unable to attach BlackFin UART Modem Status interrupt.\n");
- }
-
- /* CTS RTS PINs are negative assertive. */
- UART_PUT_MCR(uart, UART_GET_MCR(uart) | ACTS);
- UART_SET_IER(uart, EDSSI);
- }
-#endif
-
- UART_SET_IER(uart, ERBFI);
- return 0;
-}
-
-static void bfin_serial_shutdown(struct uart_port *port)
-{
- struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
-
-#ifdef CONFIG_SERIAL_BFIN_DMA
- disable_dma(uart->tx_dma_channel);
- free_dma(uart->tx_dma_channel);
- disable_dma(uart->rx_dma_channel);
- free_dma(uart->rx_dma_channel);
- del_timer(&(uart->rx_dma_timer));
- dma_free_coherent(NULL, PAGE_SIZE, uart->rx_dma_buf.buf, 0);
-#else
-#ifdef CONFIG_BF54x
- switch (uart->port.irq) {
- case IRQ_UART3_RX:
- free_dma(CH_UART3_RX);
- free_dma(CH_UART3_TX);
- break;
- case IRQ_UART2_RX:
- free_dma(CH_UART2_RX);
- free_dma(CH_UART2_TX);
- break;
- default:
- break;
- }
-#endif
- free_irq(uart->rx_irq, uart);
- free_irq(uart->tx_irq, uart);
-#endif
-
-#ifdef SERIAL_BFIN_CTSRTS
- if (uart->cts_pin >= 0)
- free_irq(gpio_to_irq(uart->cts_pin), uart);
- if (uart->rts_pin >= 0)
- gpio_free(uart->rts_pin);
-#endif
-#ifdef SERIAL_BFIN_HARD_CTSRTS
- if (uart->cts_pin >= 0)
- free_irq(uart->status_irq, uart);
-#endif
-}
-
-static void
-bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios,
- struct ktermios *old)
-{
- struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
- unsigned long flags;
- unsigned int baud, quot;
- unsigned int ier, lcr = 0;
- unsigned long timeout;
-
-#ifdef SERIAL_BFIN_CTSRTS
- if (old == NULL && uart->cts_pin != -1)
- termios->c_cflag |= CRTSCTS;
- else if (uart->cts_pin == -1)
- termios->c_cflag &= ~CRTSCTS;
-#endif
-
- switch (termios->c_cflag & CSIZE) {
- case CS8:
- lcr = WLS(8);
- break;
- case CS7:
- lcr = WLS(7);
- break;
- case CS6:
- lcr = WLS(6);
- break;
- case CS5:
- lcr = WLS(5);
- break;
- default:
- printk(KERN_ERR "%s: word length not supported\n",
- __func__);
- }
-
- /* Anomaly notes:
- * 05000231 - STOP bit is always set to 1 whatever the user is set.
- */
- if (termios->c_cflag & CSTOPB) {
- if (ANOMALY_05000231)
- printk(KERN_WARNING "STOP bits other than 1 is not "
- "supported in case of anomaly 05000231.\n");
- else
- lcr |= STB;
- }
- if (termios->c_cflag & PARENB)
- lcr |= PEN;
- if (!(termios->c_cflag & PARODD))
- lcr |= EPS;
- if (termios->c_cflag & CMSPAR)
- lcr |= STP;
-
- spin_lock_irqsave(&uart->port.lock, flags);
-
- port->read_status_mask = OE;
- if (termios->c_iflag & INPCK)
- port->read_status_mask |= (FE | PE);
- if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
- port->read_status_mask |= BI;
-
- /*
- * Characters to ignore
- */
- port->ignore_status_mask = 0;
- if (termios->c_iflag & IGNPAR)
- port->ignore_status_mask |= FE | PE;
- if (termios->c_iflag & IGNBRK) {
- port->ignore_status_mask |= BI;
- /*
- * If we're ignoring parity and break indicators,
- * ignore overruns too (for real raw support).
- */
- if (termios->c_iflag & IGNPAR)
- port->ignore_status_mask |= OE;
- }
-
- baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
- quot = uart_get_divisor(port, baud);
-
- /* If discipline is not IRDA, apply ANOMALY_05000230 */
- if (termios->c_line != N_IRDA)
- quot -= ANOMALY_05000230;
-
- UART_SET_ANOMALY_THRESHOLD(uart, USEC_PER_SEC / baud * 15);
-
- /* Wait till the transfer buffer is empty */
- timeout = jiffies + msecs_to_jiffies(10);
- while (UART_GET_GCTL(uart) & UCEN && !(UART_GET_LSR(uart) & TEMT))
- if (time_after(jiffies, timeout)) {
- dev_warn(port->dev, "timeout waiting for TX buffer empty\n");
- break;
- }
-
- /* Disable UART */
- ier = UART_GET_IER(uart);
- UART_PUT_GCTL(uart, UART_GET_GCTL(uart) & ~UCEN);
- UART_DISABLE_INTS(uart);
-
- /* Set DLAB in LCR to Access CLK */
- UART_SET_DLAB(uart);
-
- UART_PUT_CLK(uart, quot);
- SSYNC();
-
- /* Clear DLAB in LCR to Access THR RBR IER */
- UART_CLEAR_DLAB(uart);
-
- UART_PUT_LCR(uart, (UART_GET_LCR(uart) & ~LCR_MASK) | lcr);
-
- /* Enable UART */
- UART_ENABLE_INTS(uart, ier);
- UART_PUT_GCTL(uart, UART_GET_GCTL(uart) | UCEN);
-
- /* Port speed changed, update the per-port timeout. */
- uart_update_timeout(port, termios->c_cflag, baud);
-
- spin_unlock_irqrestore(&uart->port.lock, flags);
-}
-
-static const char *bfin_serial_type(struct uart_port *port)
-{
- struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
-
- return uart->port.type == PORT_BFIN ? "BFIN-UART" : NULL;
-}
-
-/*
- * Release the memory region(s) being used by 'port'.
- */
-static void bfin_serial_release_port(struct uart_port *port)
-{
-}
-
-/*
- * Request the memory region(s) being used by 'port'.
- */
-static int bfin_serial_request_port(struct uart_port *port)
-{
- return 0;
-}
-
-/*
- * Configure/autoconfigure the port.
- */
-static void bfin_serial_config_port(struct uart_port *port, int flags)
-{
- struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
-
- if (flags & UART_CONFIG_TYPE &&
- bfin_serial_request_port(&uart->port) == 0)
- uart->port.type = PORT_BFIN;
-}
-
-/*
- * Verify the new serial_struct (for TIOCSSERIAL).
- * The only change we allow are to the flags and type, and
- * even then only between PORT_BFIN and PORT_UNKNOWN
- */
-static int
-bfin_serial_verify_port(struct uart_port *port, struct serial_struct *ser)
-{
- return 0;
-}
-
-/*
- * Enable the IrDA function if tty->ldisc.num is N_IRDA.
- * In other cases, disable IrDA function.
- */
-static void bfin_serial_set_ldisc(struct uart_port *port,
- struct ktermios *termios)
-{
- struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
- unsigned int val;
-
- switch (termios->c_line) {
- case N_IRDA:
- val = UART_GET_GCTL(uart);
- val |= (UMOD_IRDA | RPOLC);
- UART_PUT_GCTL(uart, val);
- break;
- default:
- val = UART_GET_GCTL(uart);
- val &= ~(UMOD_MASK | RPOLC);
- UART_PUT_GCTL(uart, val);
- }
-}
-
-static void bfin_serial_reset_irda(struct uart_port *port)
-{
- struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
- unsigned int val;
-
- val = UART_GET_GCTL(uart);
- val &= ~(UMOD_MASK | RPOLC);
- UART_PUT_GCTL(uart, val);
- SSYNC();
- val |= (UMOD_IRDA | RPOLC);
- UART_PUT_GCTL(uart, val);
- SSYNC();
-}
-
-#ifdef CONFIG_CONSOLE_POLL
-/* Anomaly notes:
- * 05000099 - Because we only use THRE in poll_put and DR in poll_get,
- * losing other bits of UART_LSR is not a problem here.
- */
-static void bfin_serial_poll_put_char(struct uart_port *port, unsigned char chr)
-{
- struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
-
- while (!(UART_GET_LSR(uart) & THRE))
- cpu_relax();
-
- UART_CLEAR_DLAB(uart);
- UART_PUT_CHAR(uart, (unsigned char)chr);
-}
-
-static int bfin_serial_poll_get_char(struct uart_port *port)
-{
- struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
- unsigned char chr;
-
- while (!(UART_GET_LSR(uart) & DR))
- cpu_relax();
-
- UART_CLEAR_DLAB(uart);
- chr = UART_GET_CHAR(uart);
-
- return chr;
-}
-#endif
-
-static struct uart_ops bfin_serial_pops = {
- .tx_empty = bfin_serial_tx_empty,
- .set_mctrl = bfin_serial_set_mctrl,
- .get_mctrl = bfin_serial_get_mctrl,
- .stop_tx = bfin_serial_stop_tx,
- .start_tx = bfin_serial_start_tx,
- .stop_rx = bfin_serial_stop_rx,
- .break_ctl = bfin_serial_break_ctl,
- .startup = bfin_serial_startup,
- .shutdown = bfin_serial_shutdown,
- .set_termios = bfin_serial_set_termios,
- .set_ldisc = bfin_serial_set_ldisc,
- .type = bfin_serial_type,
- .release_port = bfin_serial_release_port,
- .request_port = bfin_serial_request_port,
- .config_port = bfin_serial_config_port,
- .verify_port = bfin_serial_verify_port,
-#ifdef CONFIG_CONSOLE_POLL
- .poll_put_char = bfin_serial_poll_put_char,
- .poll_get_char = bfin_serial_poll_get_char,
-#endif
-};
-
-#if defined(CONFIG_SERIAL_BFIN_CONSOLE) || defined(CONFIG_EARLY_PRINTK)
-/*
- * If the port was already initialised (eg, by a boot loader),
- * try to determine the current setup.
- */
-static void __init
-bfin_serial_console_get_options(struct bfin_serial_port *uart, int *baud,
- int *parity, int *bits)
-{
- unsigned int status;
-
- status = UART_GET_IER(uart) & (ERBFI | ETBEI);
- if (status == (ERBFI | ETBEI)) {
- /* ok, the port was enabled */
- u32 lcr, clk;
-
- lcr = UART_GET_LCR(uart);
-
- *parity = 'n';
- if (lcr & PEN) {
- if (lcr & EPS)
- *parity = 'e';
- else
- *parity = 'o';
- }
- *bits = ((lcr & WLS_MASK) >> WLS_OFFSET) + 5;
-
- /* Set DLAB in LCR to Access CLK */
- UART_SET_DLAB(uart);
-
- clk = UART_GET_CLK(uart);
-
- /* Clear DLAB in LCR to Access THR RBR IER */
- UART_CLEAR_DLAB(uart);
-
- *baud = get_sclk() / (16*clk);
- }
- pr_debug("%s:baud = %d, parity = %c, bits= %d\n", __func__, *baud, *parity, *bits);
-}
-
-static struct uart_driver bfin_serial_reg;
-
-static void bfin_serial_console_putchar(struct uart_port *port, int ch)
-{
- struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
- while (!(UART_GET_LSR(uart) & THRE))
- barrier();
- UART_PUT_CHAR(uart, ch);
-}
-
-#endif /* defined (CONFIG_SERIAL_BFIN_CONSOLE) ||
- defined (CONFIG_EARLY_PRINTK) */
-
-#ifdef CONFIG_SERIAL_BFIN_CONSOLE
-#define CLASS_BFIN_CONSOLE "bfin-console"
-/*
- * Interrupts are disabled on entering
- */
-static void
-bfin_serial_console_write(struct console *co, const char *s, unsigned int count)
-{
- struct bfin_serial_port *uart = bfin_serial_ports[co->index];
- unsigned long flags;
-
- spin_lock_irqsave(&uart->port.lock, flags);
- uart_console_write(&uart->port, s, count, bfin_serial_console_putchar);
- spin_unlock_irqrestore(&uart->port.lock, flags);
-
-}
-
-static int __init
-bfin_serial_console_setup(struct console *co, char *options)
-{
- struct bfin_serial_port *uart;
- int baud = 57600;
- int bits = 8;
- int parity = 'n';
-# if defined(SERIAL_BFIN_CTSRTS) || \
- defined(SERIAL_BFIN_HARD_CTSRTS)
- int flow = 'r';
-# else
- int flow = 'n';
-# endif
-
- /*
- * 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 < 0 || co->index >= BFIN_UART_NR_PORTS)
- return -ENODEV;
-
- uart = bfin_serial_ports[co->index];
- if (!uart)
- return -ENODEV;
-
- if (options)
- uart_parse_options(options, &baud, &parity, &bits, &flow);
- else
- bfin_serial_console_get_options(uart, &baud, &parity, &bits);
-
- return uart_set_options(&uart->port, co, baud, parity, bits, flow);
-}
-
-static struct console bfin_serial_console = {
- .name = BFIN_SERIAL_DEV_NAME,
- .write = bfin_serial_console_write,
- .device = uart_console_device,
- .setup = bfin_serial_console_setup,
- .flags = CON_PRINTBUFFER,
- .index = -1,
- .data = &bfin_serial_reg,
-};
-#define BFIN_SERIAL_CONSOLE (&bfin_serial_console)
-#else
-#define BFIN_SERIAL_CONSOLE NULL
-#endif /* CONFIG_SERIAL_BFIN_CONSOLE */
-
-#ifdef CONFIG_EARLY_PRINTK
-static struct bfin_serial_port bfin_earlyprintk_port;
-#define CLASS_BFIN_EARLYPRINTK "bfin-earlyprintk"
-
-/*
- * Interrupts are disabled on entering
- */
-static void
-bfin_earlyprintk_console_write(struct console *co, const char *s, unsigned int count)
-{
- unsigned long flags;
-
- if (bfin_earlyprintk_port.port.line != co->index)
- return;
-
- spin_lock_irqsave(&bfin_earlyprintk_port.port.lock, flags);
- uart_console_write(&bfin_earlyprintk_port.port, s, count,
- bfin_serial_console_putchar);
- spin_unlock_irqrestore(&bfin_earlyprintk_port.port.lock, flags);
-}
-
-/*
- * This should have a .setup or .early_setup in it, but then things get called
- * without the command line options, and the baud rate gets messed up - so
- * don't let the common infrastructure play with things. (see calls to setup
- * & earlysetup in ./kernel/printk.c:register_console()
- */
-static struct console bfin_early_serial_console __initdata = {
- .name = "early_BFuart",
- .write = bfin_earlyprintk_console_write,
- .device = uart_console_device,
- .flags = CON_PRINTBUFFER,
- .index = -1,
- .data = &bfin_serial_reg,
-};
-#endif
-
-static struct uart_driver bfin_serial_reg = {
- .owner = THIS_MODULE,
- .driver_name = DRIVER_NAME,
- .dev_name = BFIN_SERIAL_DEV_NAME,
- .major = BFIN_SERIAL_MAJOR,
- .minor = BFIN_SERIAL_MINOR,
- .nr = BFIN_UART_NR_PORTS,
- .cons = BFIN_SERIAL_CONSOLE,
-};
-
-static int bfin_serial_suspend(struct platform_device *pdev, pm_message_t state)
-{
- struct bfin_serial_port *uart = platform_get_drvdata(pdev);
-
- return uart_suspend_port(&bfin_serial_reg, &uart->port);
-}
-
-static int bfin_serial_resume(struct platform_device *pdev)
-{
- struct bfin_serial_port *uart = platform_get_drvdata(pdev);
-
- return uart_resume_port(&bfin_serial_reg, &uart->port);
-}
-
-static int bfin_serial_probe(struct platform_device *pdev)
-{
- struct resource *res;
- struct bfin_serial_port *uart = NULL;
- int ret = 0;
-
- if (pdev->id < 0 || pdev->id >= BFIN_UART_NR_PORTS) {
- dev_err(&pdev->dev, "Wrong bfin uart platform device id.\n");
- return -ENOENT;
- }
-
- if (bfin_serial_ports[pdev->id] == NULL) {
-
- uart = kzalloc(sizeof(*uart), GFP_KERNEL);
- if (!uart) {
- dev_err(&pdev->dev,
- "fail to malloc bfin_serial_port\n");
- return -ENOMEM;
- }
- bfin_serial_ports[pdev->id] = uart;
-
-#ifdef CONFIG_EARLY_PRINTK
- if (!(bfin_earlyprintk_port.port.membase
- && bfin_earlyprintk_port.port.line == pdev->id)) {
- /*
- * If the peripheral PINs of current port is allocated
- * in earlyprintk probe stage, don't do it again.
- */
-#endif
- ret = peripheral_request_list(
- dev_get_platdata(&pdev->dev),
- DRIVER_NAME);
- if (ret) {
- dev_err(&pdev->dev,
- "fail to request bfin serial peripherals\n");
- goto out_error_free_mem;
- }
-#ifdef CONFIG_EARLY_PRINTK
- }
-#endif
-
- spin_lock_init(&uart->port.lock);
- uart->port.uartclk = get_sclk();
- uart->port.fifosize = BFIN_UART_TX_FIFO_SIZE;
- uart->port.ops = &bfin_serial_pops;
- uart->port.line = pdev->id;
- uart->port.iotype = UPIO_MEM;
- uart->port.flags = UPF_BOOT_AUTOCONF;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
- dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");
- ret = -ENOENT;
- goto out_error_free_peripherals;
- }
-
- uart->port.membase = ioremap(res->start, resource_size(res));
- if (!uart->port.membase) {
- dev_err(&pdev->dev, "Cannot map uart IO\n");
- ret = -ENXIO;
- goto out_error_free_peripherals;
- }
- uart->port.mapbase = res->start;
-
- uart->tx_irq = platform_get_irq(pdev, 0);
- if (uart->tx_irq < 0) {
- dev_err(&pdev->dev, "No uart TX IRQ specified\n");
- ret = -ENOENT;
- goto out_error_unmap;
- }
-
- uart->rx_irq = platform_get_irq(pdev, 1);
- if (uart->rx_irq < 0) {
- dev_err(&pdev->dev, "No uart RX IRQ specified\n");
- ret = -ENOENT;
- goto out_error_unmap;
- }
- uart->port.irq = uart->rx_irq;
-
- uart->status_irq = platform_get_irq(pdev, 2);
- if (uart->status_irq < 0) {
- dev_err(&pdev->dev, "No uart status IRQ specified\n");
- ret = -ENOENT;
- goto out_error_unmap;
- }
-
-#ifdef CONFIG_SERIAL_BFIN_DMA
- spin_lock_init(&uart->rx_lock);
- uart->tx_done = 1;
- uart->tx_count = 0;
-
- res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (res == NULL) {
- dev_err(&pdev->dev, "No uart TX DMA channel specified\n");
- ret = -ENOENT;
- goto out_error_unmap;
- }
- uart->tx_dma_channel = res->start;
-
- res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
- if (res == NULL) {
- dev_err(&pdev->dev, "No uart RX DMA channel specified\n");
- ret = -ENOENT;
- goto out_error_unmap;
- }
- uart->rx_dma_channel = res->start;
-
- timer_setup(&uart->rx_dma_timer, bfin_serial_rx_dma_timeout, 0);
-#endif
-
-#if defined(SERIAL_BFIN_CTSRTS) || \
- defined(SERIAL_BFIN_HARD_CTSRTS)
- res = platform_get_resource(pdev, IORESOURCE_IO, 0);
- if (res == NULL)
- uart->cts_pin = -1;
- else
- uart->cts_pin = res->start;
-
- res = platform_get_resource(pdev, IORESOURCE_IO, 1);
- if (res == NULL)
- uart->rts_pin = -1;
- else
- uart->rts_pin = res->start;
-#endif
- }
-
-#ifdef CONFIG_SERIAL_BFIN_CONSOLE
- if (!is_early_platform_device(pdev)) {
-#endif
- uart = bfin_serial_ports[pdev->id];
- uart->port.dev = &pdev->dev;
- dev_set_drvdata(&pdev->dev, uart);
- ret = uart_add_one_port(&bfin_serial_reg, &uart->port);
-#ifdef CONFIG_SERIAL_BFIN_CONSOLE
- }
-#endif
-
- if (!ret)
- return 0;
-
- if (uart) {
-out_error_unmap:
- iounmap(uart->port.membase);
-out_error_free_peripherals:
- peripheral_free_list(dev_get_platdata(&pdev->dev));
-out_error_free_mem:
- kfree(uart);
- bfin_serial_ports[pdev->id] = NULL;
- }
-
- return ret;
-}
-
-static int bfin_serial_remove(struct platform_device *pdev)
-{
- struct bfin_serial_port *uart = platform_get_drvdata(pdev);
-
- dev_set_drvdata(&pdev->dev, NULL);
-
- if (uart) {
- uart_remove_one_port(&bfin_serial_reg, &uart->port);
- iounmap(uart->port.membase);
- peripheral_free_list(dev_get_platdata(&pdev->dev));
- kfree(uart);
- bfin_serial_ports[pdev->id] = NULL;
- }
-
- return 0;
-}
-
-static struct platform_driver bfin_serial_driver = {
- .probe = bfin_serial_probe,
- .remove = bfin_serial_remove,
- .suspend = bfin_serial_suspend,
- .resume = bfin_serial_resume,
- .driver = {
- .name = DRIVER_NAME,
- },
-};
-
-#if defined(CONFIG_SERIAL_BFIN_CONSOLE)
-static struct early_platform_driver early_bfin_serial_driver __initdata = {
- .class_str = CLASS_BFIN_CONSOLE,
- .pdrv = &bfin_serial_driver,
- .requested_id = EARLY_PLATFORM_ID_UNSET,
-};
-
-static int __init bfin_serial_rs_console_init(void)
-{
- early_platform_driver_register(&early_bfin_serial_driver, DRIVER_NAME);
-
- early_platform_driver_probe(CLASS_BFIN_CONSOLE, BFIN_UART_NR_PORTS, 0);
-
- register_console(&bfin_serial_console);
-
- return 0;
-}
-console_initcall(bfin_serial_rs_console_init);
-#endif
-
-#ifdef CONFIG_EARLY_PRINTK
-/*
- * Memory can't be allocated dynamically during earlyprink init stage.
- * So, do individual probe for earlyprink with a static uart port variable.
- */
-static int bfin_earlyprintk_probe(struct platform_device *pdev)
-{
- struct resource *res;
- int ret;
-
- if (pdev->id < 0 || pdev->id >= BFIN_UART_NR_PORTS) {
- dev_err(&pdev->dev, "Wrong earlyprintk platform device id.\n");
- return -ENOENT;
- }
-
- ret = peripheral_request_list(dev_get_platdata(&pdev->dev),
- DRIVER_NAME);
- if (ret) {
- dev_err(&pdev->dev,
- "fail to request bfin serial peripherals\n");
- return ret;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
- dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");
- ret = -ENOENT;
- goto out_error_free_peripherals;
- }
-
- bfin_earlyprintk_port.port.membase = ioremap(res->start,
- resource_size(res));
- if (!bfin_earlyprintk_port.port.membase) {
- dev_err(&pdev->dev, "Cannot map uart IO\n");
- ret = -ENXIO;
- goto out_error_free_peripherals;
- }
- bfin_earlyprintk_port.port.mapbase = res->start;
- bfin_earlyprintk_port.port.line = pdev->id;
- bfin_earlyprintk_port.port.uartclk = get_sclk();
- bfin_earlyprintk_port.port.fifosize = BFIN_UART_TX_FIFO_SIZE;
- spin_lock_init(&bfin_earlyprintk_port.port.lock);
-
- return 0;
-
-out_error_free_peripherals:
- peripheral_free_list(dev_get_platdata(&pdev->dev));
-
- return ret;
-}
-
-static struct platform_driver bfin_earlyprintk_driver = {
- .probe = bfin_earlyprintk_probe,
- .driver = {
- .name = DRIVER_NAME,
- .owner = THIS_MODULE,
- },
-};
-
-static struct early_platform_driver early_bfin_earlyprintk_driver __initdata = {
- .class_str = CLASS_BFIN_EARLYPRINTK,
- .pdrv = &bfin_earlyprintk_driver,
- .requested_id = EARLY_PLATFORM_ID_UNSET,
-};
-
-struct console __init *bfin_earlyserial_init(unsigned int port,
- unsigned int cflag)
-{
- struct ktermios t;
- char port_name[20];
-
- if (port < 0 || port >= BFIN_UART_NR_PORTS)
- return NULL;
-
- /*
- * Only probe resource of the given port in earlyprintk boot arg.
- * The expected port id should be indicated in port name string.
- */
- snprintf(port_name, 20, DRIVER_NAME ".%d", port);
- early_platform_driver_register(&early_bfin_earlyprintk_driver,
- port_name);
- early_platform_driver_probe(CLASS_BFIN_EARLYPRINTK, 1, 0);
-
- if (!bfin_earlyprintk_port.port.membase)
- return NULL;
-
-#ifdef CONFIG_SERIAL_BFIN_CONSOLE
- /*
- * If we are using early serial, don't let the normal console rewind
- * log buffer, since that causes things to be printed multiple times
- */
- bfin_serial_console.flags &= ~CON_PRINTBUFFER;
-#endif
-
- bfin_early_serial_console.index = port;
- t.c_cflag = cflag;
- t.c_iflag = 0;
- t.c_oflag = 0;
- t.c_lflag = ICANON;
- t.c_line = port;
- bfin_serial_set_termios(&bfin_earlyprintk_port.port, &t, &t);
-
- return &bfin_early_serial_console;
-}
-#endif /* CONFIG_EARLY_PRINTK */
-
-static int __init bfin_serial_init(void)
-{
- int ret;
-
- pr_info("Blackfin serial driver\n");
-
- ret = uart_register_driver(&bfin_serial_reg);
- if (ret) {
- pr_err("failed to register %s:%d\n",
- bfin_serial_reg.driver_name, ret);
- }
-
- ret = platform_driver_register(&bfin_serial_driver);
- if (ret) {
- pr_err("fail to register bfin uart\n");
- uart_unregister_driver(&bfin_serial_reg);
- }
-
- return ret;
-}
-
-static void __exit bfin_serial_exit(void)
-{
- platform_driver_unregister(&bfin_serial_driver);
- uart_unregister_driver(&bfin_serial_reg);
-}
-
-
-module_init(bfin_serial_init);
-module_exit(bfin_serial_exit);
-
-MODULE_AUTHOR("Sonic Zhang, Aubrey Li");
-MODULE_DESCRIPTION("Blackfin generic serial port driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_CHARDEV_MAJOR(BFIN_SERIAL_MAJOR);
-MODULE_ALIAS("platform:bfin-uart");
diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c
deleted file mode 100644
index c9458a033e3c..000000000000
--- a/drivers/tty/serial/crisv10.c
+++ /dev/null
@@ -1,4248 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Serial port driver for the ETRAX 100LX chip
- *
- * Copyright (C) 1998-2007 Axis Communications AB
- *
- * Many, many authors. Based once upon a time on serial.c for 16x50.
- *
- */
-
-static char *serial_version = "$Revision: 1.25 $";
-
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched/signal.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/major.h>
-#include <linux/string.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/mutex.h>
-#include <linux/bitops.h>
-#include <linux/seq_file.h>
-#include <linux/delay.h>
-#include <linux/uaccess.h>
-#include <linux/io.h>
-
-#include <asm/irq.h>
-#include <asm/dma.h>
-
-#include <arch/svinto.h>
-#include <arch/system.h>
-
-/* non-arch dependent serial structures are in linux/serial.h */
-#include <linux/serial.h>
-/* while we keep our own stuff (struct e100_serial) in a local .h file */
-#include "crisv10.h"
-#include <asm/fasttimer.h>
-#include <arch/io_interface_mux.h>
-
-#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
-#ifndef CONFIG_ETRAX_FAST_TIMER
-#error "Enable FAST_TIMER to use SERIAL_FAST_TIMER"
-#endif
-#endif
-
-#if defined(CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS) && \
- (CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS == 0)
-#error "RX_TIMEOUT_TICKS == 0 not allowed, use 1"
-#endif
-
-/*
- * All of the compatibilty code so we can compile serial.c against
- * older kernels is hidden in serial_compat.h
- */
-#if defined(LOCAL_HEADERS)
-#include "serial_compat.h"
-#endif
-
-struct tty_driver *serial_driver;
-
-/* number of characters left in xmit buffer before we ask for more */
-#define WAKEUP_CHARS 256
-
-//#define SERIAL_DEBUG_INTR
-//#define SERIAL_DEBUG_OPEN
-//#define SERIAL_DEBUG_FLOW
-//#define SERIAL_DEBUG_DATA
-//#define SERIAL_DEBUG_THROTTLE
-//#define SERIAL_DEBUG_IO /* Debug for Extra control and status pins */
-//#define SERIAL_DEBUG_LINE 0 /* What serport we want to debug */
-
-/* Enable this to use serial interrupts to handle when you
- expect the first received event on the serial port to
- be an error, break or similar. Used to be able to flash IRMA
- from eLinux */
-#define SERIAL_HANDLE_EARLY_ERRORS
-
-/* Currently 16 descriptors x 128 bytes = 2048 bytes */
-#define SERIAL_DESCR_BUF_SIZE 256
-
-#define SERIAL_PRESCALE_BASE 3125000 /* 3.125MHz */
-#define DEF_BAUD_BASE SERIAL_PRESCALE_BASE
-
-/* We don't want to load the system with massive fast timer interrupt
- * on high baudrates so limit it to 250 us (4kHz) */
-#define MIN_FLUSH_TIME_USEC 250
-
-/* Add an x here to log a lot of timer stuff */
-#define TIMERD(x)
-/* Debug details of interrupt handling */
-#define DINTR1(x) /* irq on/off, errors */
-#define DINTR2(x) /* tx and rx */
-/* Debug flip buffer stuff */
-#define DFLIP(x)
-/* Debug flow control and overview of data flow */
-#define DFLOW(x)
-#define DBAUD(x)
-#define DLOG_INT_TRIG(x)
-
-//#define DEBUG_LOG_INCLUDED
-#ifndef DEBUG_LOG_INCLUDED
-#define DEBUG_LOG(line, string, value)
-#else
-struct debug_log_info
-{
- unsigned long time;
- unsigned long timer_data;
-// int line;
- const char *string;
- int value;
-};
-#define DEBUG_LOG_SIZE 4096
-
-struct debug_log_info debug_log[DEBUG_LOG_SIZE];
-int debug_log_pos = 0;
-
-#define DEBUG_LOG(_line, _string, _value) do { \
- if ((_line) == SERIAL_DEBUG_LINE) {\
- debug_log_func(_line, _string, _value); \
- }\
-}while(0)
-
-void debug_log_func(int line, const char *string, int value)
-{
- if (debug_log_pos < DEBUG_LOG_SIZE) {
- debug_log[debug_log_pos].time = jiffies;
- debug_log[debug_log_pos].timer_data = *R_TIMER_DATA;
-// debug_log[debug_log_pos].line = line;
- debug_log[debug_log_pos].string = string;
- debug_log[debug_log_pos].value = value;
- debug_log_pos++;
- }
- /*printk(string, value);*/
-}
-#endif
-
-#ifndef CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS
-/* Default number of timer ticks before flushing rx fifo
- * When using "little data, low latency applications: use 0
- * When using "much data applications (PPP)" use ~5
- */
-#define CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS 5
-#endif
-
-unsigned long timer_data_to_ns(unsigned long timer_data);
-
-static void change_speed(struct e100_serial *info);
-static void rs_throttle(struct tty_struct * tty);
-static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
-static int rs_write(struct tty_struct *tty,
- const unsigned char *buf, int count);
-#ifdef CONFIG_ETRAX_RS485
-static int e100_write_rs485(struct tty_struct *tty,
- const unsigned char *buf, int count);
-#endif
-static int get_lsr_info(struct e100_serial *info, unsigned int *value);
-
-
-#define DEF_BAUD 115200 /* 115.2 kbit/s */
-#define DEF_RX 0x20 /* or SERIAL_CTRL_W >> 8 */
-/* Default value of tx_ctrl register: has txd(bit 7)=1 (idle) as default */
-#define DEF_TX 0x80 /* or SERIAL_CTRL_B */
-
-/* offsets from R_SERIALx_CTRL */
-
-#define REG_DATA 0
-#define REG_DATA_STATUS32 0 /* this is the 32 bit register R_SERIALx_READ */
-#define REG_TR_DATA 0
-#define REG_STATUS 1
-#define REG_TR_CTRL 1
-#define REG_REC_CTRL 2
-#define REG_BAUD 3
-#define REG_XOFF 4 /* this is a 32 bit register */
-
-/* The bitfields are the same for all serial ports */
-#define SER_RXD_MASK IO_MASK(R_SERIAL0_STATUS, rxd)
-#define SER_DATA_AVAIL_MASK IO_MASK(R_SERIAL0_STATUS, data_avail)
-#define SER_FRAMING_ERR_MASK IO_MASK(R_SERIAL0_STATUS, framing_err)
-#define SER_PAR_ERR_MASK IO_MASK(R_SERIAL0_STATUS, par_err)
-#define SER_OVERRUN_MASK IO_MASK(R_SERIAL0_STATUS, overrun)
-
-#define SER_ERROR_MASK (SER_OVERRUN_MASK | SER_PAR_ERR_MASK | SER_FRAMING_ERR_MASK)
-
-/* Values for info->errorcode */
-#define ERRCODE_SET_BREAK (TTY_BREAK)
-#define ERRCODE_INSERT 0x100
-#define ERRCODE_INSERT_BREAK (ERRCODE_INSERT | TTY_BREAK)
-
-#define FORCE_EOP(info) *R_SET_EOP = 1U << info->iseteop;
-
-/*
- * General note regarding the use of IO_* macros in this file:
- *
- * We will use the bits defined for DMA channel 6 when using various
- * IO_* macros (e.g. IO_STATE, IO_MASK, IO_EXTRACT) and _assume_ they are
- * the same for all channels (which of course they are).
- *
- * We will also use the bits defined for serial port 0 when writing commands
- * to the different ports, as these bits too are the same for all ports.
- */
-
-
-/* Mask for the irqs possibly enabled in R_IRQ_MASK1_RD etc. */
-static const unsigned long e100_ser_int_mask = 0
-#ifdef CONFIG_ETRAX_SERIAL_PORT0
-| IO_MASK(R_IRQ_MASK1_RD, ser0_data) | IO_MASK(R_IRQ_MASK1_RD, ser0_ready)
-#endif
-#ifdef CONFIG_ETRAX_SERIAL_PORT1
-| IO_MASK(R_IRQ_MASK1_RD, ser1_data) | IO_MASK(R_IRQ_MASK1_RD, ser1_ready)
-#endif
-#ifdef CONFIG_ETRAX_SERIAL_PORT2
-| IO_MASK(R_IRQ_MASK1_RD, ser2_data) | IO_MASK(R_IRQ_MASK1_RD, ser2_ready)
-#endif
-#ifdef CONFIG_ETRAX_SERIAL_PORT3
-| IO_MASK(R_IRQ_MASK1_RD, ser3_data) | IO_MASK(R_IRQ_MASK1_RD, ser3_ready)
-#endif
-;
-unsigned long r_alt_ser_baudrate_shadow = 0;
-
-/* this is the data for the four serial ports in the etrax100 */
-/* DMA2(ser2), DMA4(ser3), DMA6(ser0) or DMA8(ser1) */
-/* R_DMA_CHx_CLR_INTR, R_DMA_CHx_FIRST, R_DMA_CHx_CMD */
-
-static struct e100_serial rs_table[] = {
- { .baud = DEF_BAUD,
- .ioport = (unsigned char *)R_SERIAL0_CTRL,
- .irq = 1U << 12, /* uses DMA 6 and 7 */
- .oclrintradr = R_DMA_CH6_CLR_INTR,
- .ofirstadr = R_DMA_CH6_FIRST,
- .ocmdadr = R_DMA_CH6_CMD,
- .ostatusadr = R_DMA_CH6_STATUS,
- .iclrintradr = R_DMA_CH7_CLR_INTR,
- .ifirstadr = R_DMA_CH7_FIRST,
- .icmdadr = R_DMA_CH7_CMD,
- .idescradr = R_DMA_CH7_DESCR,
- .rx_ctrl = DEF_RX,
- .tx_ctrl = DEF_TX,
- .iseteop = 2,
- .dma_owner = dma_ser0,
- .io_if = if_serial_0,
-#ifdef CONFIG_ETRAX_SERIAL_PORT0
- .enabled = 1,
-#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT
- .dma_out_enabled = 1,
- .dma_out_nbr = SER0_TX_DMA_NBR,
- .dma_out_irq_nbr = SER0_DMA_TX_IRQ_NBR,
- .dma_out_irq_flags = 0,
- .dma_out_irq_description = "serial 0 dma tr",
-#else
- .dma_out_enabled = 0,
- .dma_out_nbr = UINT_MAX,
- .dma_out_irq_nbr = 0,
- .dma_out_irq_flags = 0,
- .dma_out_irq_description = NULL,
-#endif
-#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN
- .dma_in_enabled = 1,
- .dma_in_nbr = SER0_RX_DMA_NBR,
- .dma_in_irq_nbr = SER0_DMA_RX_IRQ_NBR,
- .dma_in_irq_flags = 0,
- .dma_in_irq_description = "serial 0 dma rec",
-#else
- .dma_in_enabled = 0,
- .dma_in_nbr = UINT_MAX,
- .dma_in_irq_nbr = 0,
- .dma_in_irq_flags = 0,
- .dma_in_irq_description = NULL,
-#endif
-#else
- .enabled = 0,
- .io_if_description = NULL,
- .dma_out_enabled = 0,
- .dma_in_enabled = 0
-#endif
-
-}, /* ttyS0 */
- { .baud = DEF_BAUD,
- .ioport = (unsigned char *)R_SERIAL1_CTRL,
- .irq = 1U << 16, /* uses DMA 8 and 9 */
- .oclrintradr = R_DMA_CH8_CLR_INTR,
- .ofirstadr = R_DMA_CH8_FIRST,
- .ocmdadr = R_DMA_CH8_CMD,
- .ostatusadr = R_DMA_CH8_STATUS,
- .iclrintradr = R_DMA_CH9_CLR_INTR,
- .ifirstadr = R_DMA_CH9_FIRST,
- .icmdadr = R_DMA_CH9_CMD,
- .idescradr = R_DMA_CH9_DESCR,
- .rx_ctrl = DEF_RX,
- .tx_ctrl = DEF_TX,
- .iseteop = 3,
- .dma_owner = dma_ser1,
- .io_if = if_serial_1,
-#ifdef CONFIG_ETRAX_SERIAL_PORT1
- .enabled = 1,
- .io_if_description = "ser1",
-#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA8_OUT
- .dma_out_enabled = 1,
- .dma_out_nbr = SER1_TX_DMA_NBR,
- .dma_out_irq_nbr = SER1_DMA_TX_IRQ_NBR,
- .dma_out_irq_flags = 0,
- .dma_out_irq_description = "serial 1 dma tr",
-#else
- .dma_out_enabled = 0,
- .dma_out_nbr = UINT_MAX,
- .dma_out_irq_nbr = 0,
- .dma_out_irq_flags = 0,
- .dma_out_irq_description = NULL,
-#endif
-#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA9_IN
- .dma_in_enabled = 1,
- .dma_in_nbr = SER1_RX_DMA_NBR,
- .dma_in_irq_nbr = SER1_DMA_RX_IRQ_NBR,
- .dma_in_irq_flags = 0,
- .dma_in_irq_description = "serial 1 dma rec",
-#else
- .dma_in_enabled = 0,
- .dma_in_enabled = 0,
- .dma_in_nbr = UINT_MAX,
- .dma_in_irq_nbr = 0,
- .dma_in_irq_flags = 0,
- .dma_in_irq_description = NULL,
-#endif
-#else
- .enabled = 0,
- .io_if_description = NULL,
- .dma_in_irq_nbr = 0,
- .dma_out_enabled = 0,
- .dma_in_enabled = 0
-#endif
-}, /* ttyS1 */
-
- { .baud = DEF_BAUD,
- .ioport = (unsigned char *)R_SERIAL2_CTRL,
- .irq = 1U << 4, /* uses DMA 2 and 3 */
- .oclrintradr = R_DMA_CH2_CLR_INTR,
- .ofirstadr = R_DMA_CH2_FIRST,
- .ocmdadr = R_DMA_CH2_CMD,
- .ostatusadr = R_DMA_CH2_STATUS,
- .iclrintradr = R_DMA_CH3_CLR_INTR,
- .ifirstadr = R_DMA_CH3_FIRST,
- .icmdadr = R_DMA_CH3_CMD,
- .idescradr = R_DMA_CH3_DESCR,
- .rx_ctrl = DEF_RX,
- .tx_ctrl = DEF_TX,
- .iseteop = 0,
- .dma_owner = dma_ser2,
- .io_if = if_serial_2,
-#ifdef CONFIG_ETRAX_SERIAL_PORT2
- .enabled = 1,
- .io_if_description = "ser2",
-#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT
- .dma_out_enabled = 1,
- .dma_out_nbr = SER2_TX_DMA_NBR,
- .dma_out_irq_nbr = SER2_DMA_TX_IRQ_NBR,
- .dma_out_irq_flags = 0,
- .dma_out_irq_description = "serial 2 dma tr",
-#else
- .dma_out_enabled = 0,
- .dma_out_nbr = UINT_MAX,
- .dma_out_irq_nbr = 0,
- .dma_out_irq_flags = 0,
- .dma_out_irq_description = NULL,
-#endif
-#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN
- .dma_in_enabled = 1,
- .dma_in_nbr = SER2_RX_DMA_NBR,
- .dma_in_irq_nbr = SER2_DMA_RX_IRQ_NBR,
- .dma_in_irq_flags = 0,
- .dma_in_irq_description = "serial 2 dma rec",
-#else
- .dma_in_enabled = 0,
- .dma_in_nbr = UINT_MAX,
- .dma_in_irq_nbr = 0,
- .dma_in_irq_flags = 0,
- .dma_in_irq_description = NULL,
-#endif
-#else
- .enabled = 0,
- .io_if_description = NULL,
- .dma_out_enabled = 0,
- .dma_in_enabled = 0
-#endif
- }, /* ttyS2 */
-
- { .baud = DEF_BAUD,
- .ioport = (unsigned char *)R_SERIAL3_CTRL,
- .irq = 1U << 8, /* uses DMA 4 and 5 */
- .oclrintradr = R_DMA_CH4_CLR_INTR,
- .ofirstadr = R_DMA_CH4_FIRST,
- .ocmdadr = R_DMA_CH4_CMD,
- .ostatusadr = R_DMA_CH4_STATUS,
- .iclrintradr = R_DMA_CH5_CLR_INTR,
- .ifirstadr = R_DMA_CH5_FIRST,
- .icmdadr = R_DMA_CH5_CMD,
- .idescradr = R_DMA_CH5_DESCR,
- .rx_ctrl = DEF_RX,
- .tx_ctrl = DEF_TX,
- .iseteop = 1,
- .dma_owner = dma_ser3,
- .io_if = if_serial_3,
-#ifdef CONFIG_ETRAX_SERIAL_PORT3
- .enabled = 1,
- .io_if_description = "ser3",
-#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA4_OUT
- .dma_out_enabled = 1,
- .dma_out_nbr = SER3_TX_DMA_NBR,
- .dma_out_irq_nbr = SER3_DMA_TX_IRQ_NBR,
- .dma_out_irq_flags = 0,
- .dma_out_irq_description = "serial 3 dma tr",
-#else
- .dma_out_enabled = 0,
- .dma_out_nbr = UINT_MAX,
- .dma_out_irq_nbr = 0,
- .dma_out_irq_flags = 0,
- .dma_out_irq_description = NULL,
-#endif
-#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA5_IN
- .dma_in_enabled = 1,
- .dma_in_nbr = SER3_RX_DMA_NBR,
- .dma_in_irq_nbr = SER3_DMA_RX_IRQ_NBR,
- .dma_in_irq_flags = 0,
- .dma_in_irq_description = "serial 3 dma rec",
-#else
- .dma_in_enabled = 0,
- .dma_in_nbr = UINT_MAX,
- .dma_in_irq_nbr = 0,
- .dma_in_irq_flags = 0,
- .dma_in_irq_description = NULL
-#endif
-#else
- .enabled = 0,
- .io_if_description = NULL,
- .dma_out_enabled = 0,
- .dma_in_enabled = 0
-#endif
- } /* ttyS3 */
-};
-
-
-#define NR_PORTS (sizeof(rs_table)/sizeof(struct e100_serial))
-
-#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
-static struct fast_timer fast_timers[NR_PORTS];
-#endif
-
-/* RS-485 */
-#if defined(CONFIG_ETRAX_RS485)
-#ifdef CONFIG_ETRAX_FAST_TIMER
-static struct fast_timer fast_timers_rs485[NR_PORTS];
-#endif
-#if defined(CONFIG_ETRAX_RS485_ON_PA)
-static int rs485_pa_bit = CONFIG_ETRAX_RS485_ON_PA_BIT;
-#endif
-#endif
-
-/* Info and macros needed for each ports extra control/status signals. */
-#define E100_STRUCT_PORT(line, pinname) \
- ((CONFIG_ETRAX_SER##line##_##pinname##_ON_PA_BIT >= 0)? \
- (R_PORT_PA_DATA): ( \
- (CONFIG_ETRAX_SER##line##_##pinname##_ON_PB_BIT >= 0)? \
- (R_PORT_PB_DATA):&dummy_ser[line]))
-
-#define E100_STRUCT_SHADOW(line, pinname) \
- ((CONFIG_ETRAX_SER##line##_##pinname##_ON_PA_BIT >= 0)? \
- (&port_pa_data_shadow): ( \
- (CONFIG_ETRAX_SER##line##_##pinname##_ON_PB_BIT >= 0)? \
- (&port_pb_data_shadow):&dummy_ser[line]))
-#define E100_STRUCT_MASK(line, pinname) \
- ((CONFIG_ETRAX_SER##line##_##pinname##_ON_PA_BIT >= 0)? \
- (1<<CONFIG_ETRAX_SER##line##_##pinname##_ON_PA_BIT): ( \
- (CONFIG_ETRAX_SER##line##_##pinname##_ON_PB_BIT >= 0)? \
- (1<<CONFIG_ETRAX_SER##line##_##pinname##_ON_PB_BIT):DUMMY_##pinname##_MASK))
-
-#define DUMMY_DTR_MASK 1
-#define DUMMY_RI_MASK 2
-#define DUMMY_DSR_MASK 4
-#define DUMMY_CD_MASK 8
-static unsigned char dummy_ser[NR_PORTS] = {0xFF, 0xFF, 0xFF,0xFF};
-
-/* If not all status pins are used or disabled, use mixed mode */
-#ifdef CONFIG_ETRAX_SERIAL_PORT0
-
-#define SER0_PA_BITSUM (CONFIG_ETRAX_SER0_DTR_ON_PA_BIT+CONFIG_ETRAX_SER0_RI_ON_PA_BIT+CONFIG_ETRAX_SER0_DSR_ON_PA_BIT+CONFIG_ETRAX_SER0_CD_ON_PA_BIT)
-
-#if SER0_PA_BITSUM != -4
-# if CONFIG_ETRAX_SER0_DTR_ON_PA_BIT == -1
-# ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-# if CONFIG_ETRAX_SER0_RI_ON_PA_BIT == -1
-# ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-# if CONFIG_ETRAX_SER0_DSR_ON_PA_BIT == -1
-# ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-# if CONFIG_ETRAX_SER0_CD_ON_PA_BIT == -1
-# ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-#endif
-
-#define SER0_PB_BITSUM (CONFIG_ETRAX_SER0_DTR_ON_PB_BIT+CONFIG_ETRAX_SER0_RI_ON_PB_BIT+CONFIG_ETRAX_SER0_DSR_ON_PB_BIT+CONFIG_ETRAX_SER0_CD_ON_PB_BIT)
-
-#if SER0_PB_BITSUM != -4
-# if CONFIG_ETRAX_SER0_DTR_ON_PB_BIT == -1
-# ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-# if CONFIG_ETRAX_SER0_RI_ON_PB_BIT == -1
-# ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-# if CONFIG_ETRAX_SER0_DSR_ON_PB_BIT == -1
-# ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-# if CONFIG_ETRAX_SER0_CD_ON_PB_BIT == -1
-# ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-#endif
-
-#endif /* PORT0 */
-
-
-#ifdef CONFIG_ETRAX_SERIAL_PORT1
-
-#define SER1_PA_BITSUM (CONFIG_ETRAX_SER1_DTR_ON_PA_BIT+CONFIG_ETRAX_SER1_RI_ON_PA_BIT+CONFIG_ETRAX_SER1_DSR_ON_PA_BIT+CONFIG_ETRAX_SER1_CD_ON_PA_BIT)
-
-#if SER1_PA_BITSUM != -4
-# if CONFIG_ETRAX_SER1_DTR_ON_PA_BIT == -1
-# ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-# if CONFIG_ETRAX_SER1_RI_ON_PA_BIT == -1
-# ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-# if CONFIG_ETRAX_SER1_DSR_ON_PA_BIT == -1
-# ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-# if CONFIG_ETRAX_SER1_CD_ON_PA_BIT == -1
-# ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-#endif
-
-#define SER1_PB_BITSUM (CONFIG_ETRAX_SER1_DTR_ON_PB_BIT+CONFIG_ETRAX_SER1_RI_ON_PB_BIT+CONFIG_ETRAX_SER1_DSR_ON_PB_BIT+CONFIG_ETRAX_SER1_CD_ON_PB_BIT)
-
-#if SER1_PB_BITSUM != -4
-# if CONFIG_ETRAX_SER1_DTR_ON_PB_BIT == -1
-# ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-# if CONFIG_ETRAX_SER1_RI_ON_PB_BIT == -1
-# ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-# if CONFIG_ETRAX_SER1_DSR_ON_PB_BIT == -1
-# ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-# if CONFIG_ETRAX_SER1_CD_ON_PB_BIT == -1
-# ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-#endif
-
-#endif /* PORT1 */
-
-#ifdef CONFIG_ETRAX_SERIAL_PORT2
-
-#define SER2_PA_BITSUM (CONFIG_ETRAX_SER2_DTR_ON_PA_BIT+CONFIG_ETRAX_SER2_RI_ON_PA_BIT+CONFIG_ETRAX_SER2_DSR_ON_PA_BIT+CONFIG_ETRAX_SER2_CD_ON_PA_BIT)
-
-#if SER2_PA_BITSUM != -4
-# if CONFIG_ETRAX_SER2_DTR_ON_PA_BIT == -1
-# ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-# if CONFIG_ETRAX_SER2_RI_ON_PA_BIT == -1
-# ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-# if CONFIG_ETRAX_SER2_DSR_ON_PA_BIT == -1
-# ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-# if CONFIG_ETRAX_SER2_CD_ON_PA_BIT == -1
-# ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-#endif
-
-#define SER2_PB_BITSUM (CONFIG_ETRAX_SER2_DTR_ON_PB_BIT+CONFIG_ETRAX_SER2_RI_ON_PB_BIT+CONFIG_ETRAX_SER2_DSR_ON_PB_BIT+CONFIG_ETRAX_SER2_CD_ON_PB_BIT)
-
-#if SER2_PB_BITSUM != -4
-# if CONFIG_ETRAX_SER2_DTR_ON_PB_BIT == -1
-# ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-# if CONFIG_ETRAX_SER2_RI_ON_PB_BIT == -1
-# ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-# if CONFIG_ETRAX_SER2_DSR_ON_PB_BIT == -1
-# ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-# if CONFIG_ETRAX_SER2_CD_ON_PB_BIT == -1
-# ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-#endif
-
-#endif /* PORT2 */
-
-#ifdef CONFIG_ETRAX_SERIAL_PORT3
-
-#define SER3_PA_BITSUM (CONFIG_ETRAX_SER3_DTR_ON_PA_BIT+CONFIG_ETRAX_SER3_RI_ON_PA_BIT+CONFIG_ETRAX_SER3_DSR_ON_PA_BIT+CONFIG_ETRAX_SER3_CD_ON_PA_BIT)
-
-#if SER3_PA_BITSUM != -4
-# if CONFIG_ETRAX_SER3_DTR_ON_PA_BIT == -1
-# ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-# if CONFIG_ETRAX_SER3_RI_ON_PA_BIT == -1
-# ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-# if CONFIG_ETRAX_SER3_DSR_ON_PA_BIT == -1
-# ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-# if CONFIG_ETRAX_SER3_CD_ON_PA_BIT == -1
-# ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-#endif
-
-#define SER3_PB_BITSUM (CONFIG_ETRAX_SER3_DTR_ON_PB_BIT+CONFIG_ETRAX_SER3_RI_ON_PB_BIT+CONFIG_ETRAX_SER3_DSR_ON_PB_BIT+CONFIG_ETRAX_SER3_CD_ON_PB_BIT)
-
-#if SER3_PB_BITSUM != -4
-# if CONFIG_ETRAX_SER3_DTR_ON_PB_BIT == -1
-# ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-# if CONFIG_ETRAX_SER3_RI_ON_PB_BIT == -1
-# ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-# if CONFIG_ETRAX_SER3_DSR_ON_PB_BIT == -1
-# ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-# if CONFIG_ETRAX_SER3_CD_ON_PB_BIT == -1
-# ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
-# define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
-# endif
-# endif
-#endif
-
-#endif /* PORT3 */
-
-
-#if defined(CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED) || \
- defined(CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED) || \
- defined(CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED) || \
- defined(CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED)
-#define ETRAX_SERX_DTR_RI_DSR_CD_MIXED
-#endif
-
-#ifdef ETRAX_SERX_DTR_RI_DSR_CD_MIXED
-/* The pins can be mixed on PA and PB */
-#define CONTROL_PINS_PORT_NOT_USED(line) \
- &dummy_ser[line], &dummy_ser[line], \
- &dummy_ser[line], &dummy_ser[line], \
- &dummy_ser[line], &dummy_ser[line], \
- &dummy_ser[line], &dummy_ser[line], \
- DUMMY_DTR_MASK, DUMMY_RI_MASK, DUMMY_DSR_MASK, DUMMY_CD_MASK
-
-
-struct control_pins
-{
- volatile unsigned char *dtr_port;
- unsigned char *dtr_shadow;
- volatile unsigned char *ri_port;
- unsigned char *ri_shadow;
- volatile unsigned char *dsr_port;
- unsigned char *dsr_shadow;
- volatile unsigned char *cd_port;
- unsigned char *cd_shadow;
-
- unsigned char dtr_mask;
- unsigned char ri_mask;
- unsigned char dsr_mask;
- unsigned char cd_mask;
-};
-
-static const struct control_pins e100_modem_pins[NR_PORTS] =
-{
- /* Ser 0 */
- {
-#ifdef CONFIG_ETRAX_SERIAL_PORT0
- E100_STRUCT_PORT(0,DTR), E100_STRUCT_SHADOW(0,DTR),
- E100_STRUCT_PORT(0,RI), E100_STRUCT_SHADOW(0,RI),
- E100_STRUCT_PORT(0,DSR), E100_STRUCT_SHADOW(0,DSR),
- E100_STRUCT_PORT(0,CD), E100_STRUCT_SHADOW(0,CD),
- E100_STRUCT_MASK(0,DTR),
- E100_STRUCT_MASK(0,RI),
- E100_STRUCT_MASK(0,DSR),
- E100_STRUCT_MASK(0,CD)
-#else
- CONTROL_PINS_PORT_NOT_USED(0)
-#endif
- },
-
- /* Ser 1 */
- {
-#ifdef CONFIG_ETRAX_SERIAL_PORT1
- E100_STRUCT_PORT(1,DTR), E100_STRUCT_SHADOW(1,DTR),
- E100_STRUCT_PORT(1,RI), E100_STRUCT_SHADOW(1,RI),
- E100_STRUCT_PORT(1,DSR), E100_STRUCT_SHADOW(1,DSR),
- E100_STRUCT_PORT(1,CD), E100_STRUCT_SHADOW(1,CD),
- E100_STRUCT_MASK(1,DTR),
- E100_STRUCT_MASK(1,RI),
- E100_STRUCT_MASK(1,DSR),
- E100_STRUCT_MASK(1,CD)
-#else
- CONTROL_PINS_PORT_NOT_USED(1)
-#endif
- },
-
- /* Ser 2 */
- {
-#ifdef CONFIG_ETRAX_SERIAL_PORT2
- E100_STRUCT_PORT(2,DTR), E100_STRUCT_SHADOW(2,DTR),
- E100_STRUCT_PORT(2,RI), E100_STRUCT_SHADOW(2,RI),
- E100_STRUCT_PORT(2,DSR), E100_STRUCT_SHADOW(2,DSR),
- E100_STRUCT_PORT(2,CD), E100_STRUCT_SHADOW(2,CD),
- E100_STRUCT_MASK(2,DTR),
- E100_STRUCT_MASK(2,RI),
- E100_STRUCT_MASK(2,DSR),
- E100_STRUCT_MASK(2,CD)
-#else
- CONTROL_PINS_PORT_NOT_USED(2)
-#endif
- },
-
- /* Ser 3 */
- {
-#ifdef CONFIG_ETRAX_SERIAL_PORT3
- E100_STRUCT_PORT(3,DTR), E100_STRUCT_SHADOW(3,DTR),
- E100_STRUCT_PORT(3,RI), E100_STRUCT_SHADOW(3,RI),
- E100_STRUCT_PORT(3,DSR), E100_STRUCT_SHADOW(3,DSR),
- E100_STRUCT_PORT(3,CD), E100_STRUCT_SHADOW(3,CD),
- E100_STRUCT_MASK(3,DTR),
- E100_STRUCT_MASK(3,RI),
- E100_STRUCT_MASK(3,DSR),
- E100_STRUCT_MASK(3,CD)
-#else
- CONTROL_PINS_PORT_NOT_USED(3)
-#endif
- }
-};
-#else /* ETRAX_SERX_DTR_RI_DSR_CD_MIXED */
-
-/* All pins are on either PA or PB for each serial port */
-#define CONTROL_PINS_PORT_NOT_USED(line) \
- &dummy_ser[line], &dummy_ser[line], \
- DUMMY_DTR_MASK, DUMMY_RI_MASK, DUMMY_DSR_MASK, DUMMY_CD_MASK
-
-
-struct control_pins
-{
- volatile unsigned char *port;
- unsigned char *shadow;
-
- unsigned char dtr_mask;
- unsigned char ri_mask;
- unsigned char dsr_mask;
- unsigned char cd_mask;
-};
-
-#define dtr_port port
-#define dtr_shadow shadow
-#define ri_port port
-#define ri_shadow shadow
-#define dsr_port port
-#define dsr_shadow shadow
-#define cd_port port
-#define cd_shadow shadow
-
-static const struct control_pins e100_modem_pins[NR_PORTS] =
-{
- /* Ser 0 */
- {
-#ifdef CONFIG_ETRAX_SERIAL_PORT0
- E100_STRUCT_PORT(0,DTR), E100_STRUCT_SHADOW(0,DTR),
- E100_STRUCT_MASK(0,DTR),
- E100_STRUCT_MASK(0,RI),
- E100_STRUCT_MASK(0,DSR),
- E100_STRUCT_MASK(0,CD)
-#else
- CONTROL_PINS_PORT_NOT_USED(0)
-#endif
- },
-
- /* Ser 1 */
- {
-#ifdef CONFIG_ETRAX_SERIAL_PORT1
- E100_STRUCT_PORT(1,DTR), E100_STRUCT_SHADOW(1,DTR),
- E100_STRUCT_MASK(1,DTR),
- E100_STRUCT_MASK(1,RI),
- E100_STRUCT_MASK(1,DSR),
- E100_STRUCT_MASK(1,CD)
-#else
- CONTROL_PINS_PORT_NOT_USED(1)
-#endif
- },
-
- /* Ser 2 */
- {
-#ifdef CONFIG_ETRAX_SERIAL_PORT2
- E100_STRUCT_PORT(2,DTR), E100_STRUCT_SHADOW(2,DTR),
- E100_STRUCT_MASK(2,DTR),
- E100_STRUCT_MASK(2,RI),
- E100_STRUCT_MASK(2,DSR),
- E100_STRUCT_MASK(2,CD)
-#else
- CONTROL_PINS_PORT_NOT_USED(2)
-#endif
- },
-
- /* Ser 3 */
- {
-#ifdef CONFIG_ETRAX_SERIAL_PORT3
- E100_STRUCT_PORT(3,DTR), E100_STRUCT_SHADOW(3,DTR),
- E100_STRUCT_MASK(3,DTR),
- E100_STRUCT_MASK(3,RI),
- E100_STRUCT_MASK(3,DSR),
- E100_STRUCT_MASK(3,CD)
-#else
- CONTROL_PINS_PORT_NOT_USED(3)
-#endif
- }
-};
-#endif /* !ETRAX_SERX_DTR_RI_DSR_CD_MIXED */
-
-#define E100_RTS_MASK 0x20
-#define E100_CTS_MASK 0x40
-
-/* All serial port signals are active low:
- * active = 0 -> 3.3V to RS-232 driver -> -12V on RS-232 level
- * inactive = 1 -> 0V to RS-232 driver -> +12V on RS-232 level
- *
- * These macros returns the pin value: 0=0V, >=1 = 3.3V on ETRAX chip
- */
-
-/* Output */
-#define E100_RTS_GET(info) ((info)->rx_ctrl & E100_RTS_MASK)
-/* Input */
-#define E100_CTS_GET(info) ((info)->ioport[REG_STATUS] & E100_CTS_MASK)
-
-/* These are typically PA or PB and 0 means 0V, 1 means 3.3V */
-/* Is an output */
-#define E100_DTR_GET(info) ((*e100_modem_pins[(info)->line].dtr_shadow) & e100_modem_pins[(info)->line].dtr_mask)
-
-/* Normally inputs */
-#define E100_RI_GET(info) ((*e100_modem_pins[(info)->line].ri_port) & e100_modem_pins[(info)->line].ri_mask)
-#define E100_CD_GET(info) ((*e100_modem_pins[(info)->line].cd_port) & e100_modem_pins[(info)->line].cd_mask)
-
-/* Input */
-#define E100_DSR_GET(info) ((*e100_modem_pins[(info)->line].dsr_port) & e100_modem_pins[(info)->line].dsr_mask)
-
-/* Calculate the chartime depending on baudrate, numbor of bits etc. */
-static void update_char_time(struct e100_serial * info)
-{
- tcflag_t cflags = info->port.tty->termios.c_cflag;
- int bits;
-
- /* calc. number of bits / data byte */
- /* databits + startbit and 1 stopbit */
- if ((cflags & CSIZE) == CS7)
- bits = 9;
- else
- bits = 10;
-
- if (cflags & CSTOPB) /* 2 stopbits ? */
- bits++;
-
- if (cflags & PARENB) /* parity bit ? */
- bits++;
-
- /* calc timeout */
- info->char_time_usec = ((bits * 1000000) / info->baud) + 1;
- info->flush_time_usec = 4*info->char_time_usec;
- if (info->flush_time_usec < MIN_FLUSH_TIME_USEC)
- info->flush_time_usec = MIN_FLUSH_TIME_USEC;
-
-}
-
-/*
- * This function maps from the Bxxxx defines in asm/termbits.h into real
- * baud rates.
- */
-
-static int
-cflag_to_baud(unsigned int cflag)
-{
- static int baud_table[] = {
- 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400,
- 4800, 9600, 19200, 38400 };
-
- static int ext_baud_table[] = {
- 0, 57600, 115200, 230400, 460800, 921600, 1843200, 6250000,
- 0, 0, 0, 0, 0, 0, 0, 0 };
-
- if (cflag & CBAUDEX)
- return ext_baud_table[(cflag & CBAUD) & ~CBAUDEX];
- else
- return baud_table[cflag & CBAUD];
-}
-
-/* and this maps to an etrax100 hardware baud constant */
-
-static unsigned char
-cflag_to_etrax_baud(unsigned int cflag)
-{
- char retval;
-
- static char baud_table[] = {
- -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, -1, 3, 4, 5, 6, 7 };
-
- static char ext_baud_table[] = {
- -1, 8, 9, 10, 11, 12, 13, 14, -1, -1, -1, -1, -1, -1, -1, -1 };
-
- if (cflag & CBAUDEX)
- retval = ext_baud_table[(cflag & CBAUD) & ~CBAUDEX];
- else
- retval = baud_table[cflag & CBAUD];
-
- if (retval < 0) {
- printk(KERN_WARNING "serdriver tried setting invalid baud rate, flags %x.\n", cflag);
- retval = 5; /* choose default 9600 instead */
- }
-
- return retval | (retval << 4); /* choose same for both TX and RX */
-}
-
-
-/* Various static support functions */
-
-/* Functions to set or clear DTR/RTS on the requested line */
-/* It is complicated by the fact that RTS is a serial port register, while
- * DTR might not be implemented in the HW at all, and if it is, it can be on
- * any general port.
- */
-
-
-static inline void
-e100_dtr(struct e100_serial *info, int set)
-{
- unsigned char mask = e100_modem_pins[info->line].dtr_mask;
-
-#ifdef SERIAL_DEBUG_IO
- printk("ser%i dtr %i mask: 0x%02X\n", info->line, set, mask);
- printk("ser%i shadow before 0x%02X get: %i\n",
- info->line, *e100_modem_pins[info->line].dtr_shadow,
- E100_DTR_GET(info));
-#endif
- /* DTR is active low */
- {
- unsigned long flags;
-
- local_irq_save(flags);
- *e100_modem_pins[info->line].dtr_shadow &= ~mask;
- *e100_modem_pins[info->line].dtr_shadow |= (set ? 0 : mask);
- *e100_modem_pins[info->line].dtr_port = *e100_modem_pins[info->line].dtr_shadow;
- local_irq_restore(flags);
- }
-
-#ifdef SERIAL_DEBUG_IO
- printk("ser%i shadow after 0x%02X get: %i\n",
- info->line, *e100_modem_pins[info->line].dtr_shadow,
- E100_DTR_GET(info));
-#endif
-}
-
-/* set = 0 means 3.3V on the pin, bitvalue: 0=active, 1=inactive
- * 0=0V , 1=3.3V
- */
-static inline void
-e100_rts(struct e100_serial *info, int set)
-{
- unsigned long flags;
- local_irq_save(flags);
- info->rx_ctrl &= ~E100_RTS_MASK;
- info->rx_ctrl |= (set ? 0 : E100_RTS_MASK); /* RTS is active low */
- info->ioport[REG_REC_CTRL] = info->rx_ctrl;
- local_irq_restore(flags);
-#ifdef SERIAL_DEBUG_IO
- printk("ser%i rts %i\n", info->line, set);
-#endif
-}
-
-
-/* If this behaves as a modem, RI and CD is an output */
-static inline void
-e100_ri_out(struct e100_serial *info, int set)
-{
- /* RI is active low */
- {
- unsigned char mask = e100_modem_pins[info->line].ri_mask;
- unsigned long flags;
-
- local_irq_save(flags);
- *e100_modem_pins[info->line].ri_shadow &= ~mask;
- *e100_modem_pins[info->line].ri_shadow |= (set ? 0 : mask);
- *e100_modem_pins[info->line].ri_port = *e100_modem_pins[info->line].ri_shadow;
- local_irq_restore(flags);
- }
-}
-static inline void
-e100_cd_out(struct e100_serial *info, int set)
-{
- /* CD is active low */
- {
- unsigned char mask = e100_modem_pins[info->line].cd_mask;
- unsigned long flags;
-
- local_irq_save(flags);
- *e100_modem_pins[info->line].cd_shadow &= ~mask;
- *e100_modem_pins[info->line].cd_shadow |= (set ? 0 : mask);
- *e100_modem_pins[info->line].cd_port = *e100_modem_pins[info->line].cd_shadow;
- local_irq_restore(flags);
- }
-}
-
-static inline void
-e100_disable_rx(struct e100_serial *info)
-{
- /* disable the receiver */
- info->ioport[REG_REC_CTRL] =
- (info->rx_ctrl &= ~IO_MASK(R_SERIAL0_REC_CTRL, rec_enable));
-}
-
-static inline void
-e100_enable_rx(struct e100_serial *info)
-{
- /* enable the receiver */
- info->ioport[REG_REC_CTRL] =
- (info->rx_ctrl |= IO_MASK(R_SERIAL0_REC_CTRL, rec_enable));
-}
-
-/* the rx DMA uses both the dma_descr and the dma_eop interrupts */
-
-static inline void
-e100_disable_rxdma_irq(struct e100_serial *info)
-{
-#ifdef SERIAL_DEBUG_INTR
- printk("rxdma_irq(%d): 0\n",info->line);
-#endif
- DINTR1(DEBUG_LOG(info->line,"IRQ disable_rxdma_irq %i\n", info->line));
- *R_IRQ_MASK2_CLR = (info->irq << 2) | (info->irq << 3);
-}
-
-static inline void
-e100_enable_rxdma_irq(struct e100_serial *info)
-{
-#ifdef SERIAL_DEBUG_INTR
- printk("rxdma_irq(%d): 1\n",info->line);
-#endif
- DINTR1(DEBUG_LOG(info->line,"IRQ enable_rxdma_irq %i\n", info->line));
- *R_IRQ_MASK2_SET = (info->irq << 2) | (info->irq << 3);
-}
-
-/* the tx DMA uses only dma_descr interrupt */
-
-static void e100_disable_txdma_irq(struct e100_serial *info)
-{
-#ifdef SERIAL_DEBUG_INTR
- printk("txdma_irq(%d): 0\n",info->line);
-#endif
- DINTR1(DEBUG_LOG(info->line,"IRQ disable_txdma_irq %i\n", info->line));
- *R_IRQ_MASK2_CLR = info->irq;
-}
-
-static void e100_enable_txdma_irq(struct e100_serial *info)
-{
-#ifdef SERIAL_DEBUG_INTR
- printk("txdma_irq(%d): 1\n",info->line);
-#endif
- DINTR1(DEBUG_LOG(info->line,"IRQ enable_txdma_irq %i\n", info->line));
- *R_IRQ_MASK2_SET = info->irq;
-}
-
-static void e100_disable_txdma_channel(struct e100_serial *info)
-{
- unsigned long flags;
-
- /* Disable output DMA channel for the serial port in question
- * ( set to something other than serialX)
- */
- local_irq_save(flags);
- DFLOW(DEBUG_LOG(info->line, "disable_txdma_channel %i\n", info->line));
- if (info->line == 0) {
- if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma6)) ==
- IO_STATE(R_GEN_CONFIG, dma6, serial0)) {
- genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma6);
- genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma6, unused);
- }
- } else if (info->line == 1) {
- if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma8)) ==
- IO_STATE(R_GEN_CONFIG, dma8, serial1)) {
- genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma8);
- genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma8, usb);
- }
- } else if (info->line == 2) {
- if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma2)) ==
- IO_STATE(R_GEN_CONFIG, dma2, serial2)) {
- genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma2);
- genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma2, par0);
- }
- } else if (info->line == 3) {
- if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma4)) ==
- IO_STATE(R_GEN_CONFIG, dma4, serial3)) {
- genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma4);
- genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma4, par1);
- }
- }
- *R_GEN_CONFIG = genconfig_shadow;
- local_irq_restore(flags);
-}
-
-
-static void e100_enable_txdma_channel(struct e100_serial *info)
-{
- unsigned long flags;
-
- local_irq_save(flags);
- DFLOW(DEBUG_LOG(info->line, "enable_txdma_channel %i\n", info->line));
- /* Enable output DMA channel for the serial port in question */
- if (info->line == 0) {
- genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma6);
- genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma6, serial0);
- } else if (info->line == 1) {
- genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma8);
- genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma8, serial1);
- } else if (info->line == 2) {
- genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma2);
- genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma2, serial2);
- } else if (info->line == 3) {
- genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma4);
- genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma4, serial3);
- }
- *R_GEN_CONFIG = genconfig_shadow;
- local_irq_restore(flags);
-}
-
-static void e100_disable_rxdma_channel(struct e100_serial *info)
-{
- unsigned long flags;
-
- /* Disable input DMA channel for the serial port in question
- * ( set to something other than serialX)
- */
- local_irq_save(flags);
- if (info->line == 0) {
- if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma7)) ==
- IO_STATE(R_GEN_CONFIG, dma7, serial0)) {
- genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma7);
- genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma7, unused);
- }
- } else if (info->line == 1) {
- if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma9)) ==
- IO_STATE(R_GEN_CONFIG, dma9, serial1)) {
- genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma9);
- genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma9, usb);
- }
- } else if (info->line == 2) {
- if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma3)) ==
- IO_STATE(R_GEN_CONFIG, dma3, serial2)) {
- genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma3);
- genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma3, par0);
- }
- } else if (info->line == 3) {
- if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma5)) ==
- IO_STATE(R_GEN_CONFIG, dma5, serial3)) {
- genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma5);
- genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma5, par1);
- }
- }
- *R_GEN_CONFIG = genconfig_shadow;
- local_irq_restore(flags);
-}
-
-
-static void e100_enable_rxdma_channel(struct e100_serial *info)
-{
- unsigned long flags;
-
- local_irq_save(flags);
- /* Enable input DMA channel for the serial port in question */
- if (info->line == 0) {
- genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma7);
- genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma7, serial0);
- } else if (info->line == 1) {
- genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma9);
- genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma9, serial1);
- } else if (info->line == 2) {
- genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma3);
- genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma3, serial2);
- } else if (info->line == 3) {
- genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma5);
- genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma5, serial3);
- }
- *R_GEN_CONFIG = genconfig_shadow;
- local_irq_restore(flags);
-}
-
-#ifdef SERIAL_HANDLE_EARLY_ERRORS
-/* in order to detect and fix errors on the first byte
- we have to use the serial interrupts as well. */
-
-static inline void
-e100_disable_serial_data_irq(struct e100_serial *info)
-{
-#ifdef SERIAL_DEBUG_INTR
- printk("ser_irq(%d): 0\n",info->line);
-#endif
- DINTR1(DEBUG_LOG(info->line,"IRQ disable data_irq %i\n", info->line));
- *R_IRQ_MASK1_CLR = (1U << (8+2*info->line));
-}
-
-static inline void
-e100_enable_serial_data_irq(struct e100_serial *info)
-{
-#ifdef SERIAL_DEBUG_INTR
- printk("ser_irq(%d): 1\n",info->line);
- printk("**** %d = %d\n",
- (8+2*info->line),
- (1U << (8+2*info->line)));
-#endif
- DINTR1(DEBUG_LOG(info->line,"IRQ enable data_irq %i\n", info->line));
- *R_IRQ_MASK1_SET = (1U << (8+2*info->line));
-}
-#endif
-
-static inline void
-e100_disable_serial_tx_ready_irq(struct e100_serial *info)
-{
-#ifdef SERIAL_DEBUG_INTR
- printk("ser_tx_irq(%d): 0\n",info->line);
-#endif
- DINTR1(DEBUG_LOG(info->line,"IRQ disable ready_irq %i\n", info->line));
- *R_IRQ_MASK1_CLR = (1U << (8+1+2*info->line));
-}
-
-static inline void
-e100_enable_serial_tx_ready_irq(struct e100_serial *info)
-{
-#ifdef SERIAL_DEBUG_INTR
- printk("ser_tx_irq(%d): 1\n",info->line);
- printk("**** %d = %d\n",
- (8+1+2*info->line),
- (1U << (8+1+2*info->line)));
-#endif
- DINTR2(DEBUG_LOG(info->line,"IRQ enable ready_irq %i\n", info->line));
- *R_IRQ_MASK1_SET = (1U << (8+1+2*info->line));
-}
-
-static inline void e100_enable_rx_irq(struct e100_serial *info)
-{
- if (info->uses_dma_in)
- e100_enable_rxdma_irq(info);
- else
- e100_enable_serial_data_irq(info);
-}
-static inline void e100_disable_rx_irq(struct e100_serial *info)
-{
- if (info->uses_dma_in)
- e100_disable_rxdma_irq(info);
- else
- e100_disable_serial_data_irq(info);
-}
-
-#if defined(CONFIG_ETRAX_RS485)
-/* Enable RS-485 mode on selected port. This is UGLY. */
-static int
-e100_enable_rs485(struct tty_struct *tty, struct serial_rs485 *r)
-{
- struct e100_serial * info = (struct e100_serial *)tty->driver_data;
-
-#if defined(CONFIG_ETRAX_RS485_ON_PA)
- *R_PORT_PA_DATA = port_pa_data_shadow |= (1 << rs485_pa_bit);
-#endif
-
- info->rs485 = *r;
-
- /* Maximum delay before RTS equal to 1000 */
- if (info->rs485.delay_rts_before_send >= 1000)
- info->rs485.delay_rts_before_send = 1000;
-
-/* printk("rts: on send = %i, after = %i, enabled = %i",
- info->rs485.rts_on_send,
- info->rs485.rts_after_sent,
- info->rs485.enabled
- );
-*/
- return 0;
-}
-
-static int
-e100_write_rs485(struct tty_struct *tty,
- const unsigned char *buf, int count)
-{
- struct e100_serial * info = (struct e100_serial *)tty->driver_data;
- int old_value = (info->rs485.flags) & SER_RS485_ENABLED;
-
- /* rs485 is always implicitly enabled if we're using the ioctl()
- * but it doesn't have to be set in the serial_rs485
- * (to be backward compatible with old apps)
- * So we store, set and restore it.
- */
- info->rs485.flags |= SER_RS485_ENABLED;
- /* rs_write now deals with RS485 if enabled */
- count = rs_write(tty, buf, count);
- if (!old_value)
- info->rs485.flags &= ~(SER_RS485_ENABLED);
- return count;
-}
-
-#ifdef CONFIG_ETRAX_FAST_TIMER
-/* Timer function to toggle RTS when using FAST_TIMER */
-static void rs485_toggle_rts_timer_function(unsigned long data)
-{
- struct e100_serial *info = (struct e100_serial *)data;
-
- fast_timers_rs485[info->line].function = NULL;
- e100_rts(info, (info->rs485.flags & SER_RS485_RTS_AFTER_SEND));
-#if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER)
- e100_enable_rx(info);
- e100_enable_rx_irq(info);
-#endif
-}
-#endif
-#endif /* CONFIG_ETRAX_RS485 */
-
-/*
- * ------------------------------------------------------------
- * rs_stop() and rs_start()
- *
- * This routines are called before setting or resetting tty->stopped.
- * They enable or disable transmitter using the XOFF registers, as necessary.
- * ------------------------------------------------------------
- */
-
-static void
-rs_stop(struct tty_struct *tty)
-{
- struct e100_serial *info = (struct e100_serial *)tty->driver_data;
- if (info) {
- unsigned long flags;
- unsigned long xoff;
-
- local_irq_save(flags);
- DFLOW(DEBUG_LOG(info->line, "XOFF rs_stop xmit %i\n",
- CIRC_CNT(info->xmit.head,
- info->xmit.tail,SERIAL_XMIT_SIZE)));
-
- xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char,
- STOP_CHAR(info->port.tty));
- xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, stop);
- if (I_IXON(tty))
- xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable);
-
- *((unsigned long *)&info->ioport[REG_XOFF]) = xoff;
- local_irq_restore(flags);
- }
-}
-
-static void
-rs_start(struct tty_struct *tty)
-{
- struct e100_serial *info = (struct e100_serial *)tty->driver_data;
- if (info) {
- unsigned long flags;
- unsigned long xoff;
-
- local_irq_save(flags);
- DFLOW(DEBUG_LOG(info->line, "XOFF rs_start xmit %i\n",
- CIRC_CNT(info->xmit.head,
- info->xmit.tail,SERIAL_XMIT_SIZE)));
- xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(tty));
- xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, enable);
- if (I_IXON(tty))
- xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable);
-
- *((unsigned long *)&info->ioport[REG_XOFF]) = xoff;
- if (!info->uses_dma_out &&
- info->xmit.head != info->xmit.tail && info->xmit.buf)
- e100_enable_serial_tx_ready_irq(info);
-
- local_irq_restore(flags);
- }
-}
-
-/*
- * ----------------------------------------------------------------------
- *
- * Here starts the interrupt handling routines. All of the following
- * subroutines are declared as inline and are folded into
- * rs_interrupt(). They were separated out for readability's sake.
- *
- * Note: rs_interrupt() is a "fast" interrupt, which means that it
- * runs with interrupts turned off. People who may want to modify
- * rs_interrupt() should try to keep the interrupt handler as fast as
- * possible. After you are done making modifications, it is not a bad
- * idea to do:
- *
- * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
- *
- * and look at the resulting assemble code in serial.s.
- *
- * - Ted Ts'o (tytso@mit.edu), 7-Mar-93
- * -----------------------------------------------------------------------
- */
-
-/*
- * This routine is used by the interrupt handler to schedule
- * processing in the software interrupt portion of the driver.
- */
-static void rs_sched_event(struct e100_serial *info, int event)
-{
- if (info->event & (1 << event))
- return;
- info->event |= 1 << event;
- schedule_work(&info->work);
-}
-
-/* The output DMA channel is free - use it to send as many chars as possible
- * NOTES:
- * We don't pay attention to info->x_char, which means if the TTY wants to
- * use XON/XOFF it will set info->x_char but we won't send any X char!
- *
- * To implement this, we'd just start a DMA send of 1 byte pointing at a
- * buffer containing the X char, and skip updating xmit. We'd also have to
- * check if the last sent char was the X char when we enter this function
- * the next time, to avoid updating xmit with the sent X value.
- */
-
-static void
-transmit_chars_dma(struct e100_serial *info)
-{
- unsigned int c, sentl;
- struct etrax_dma_descr *descr;
-
- /* acknowledge both dma_descr and dma_eop irq in R_DMA_CHx_CLR_INTR */
- *info->oclrintradr =
- IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
- IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
-
-#ifdef SERIAL_DEBUG_INTR
- if (info->line == SERIAL_DEBUG_LINE)
- printk("tc\n");
-#endif
- if (!info->tr_running) {
- /* weirdo... we shouldn't get here! */
- printk(KERN_WARNING "Achtung: transmit_chars_dma with !tr_running\n");
- return;
- }
-
- descr = &info->tr_descr;
-
- /* first get the amount of bytes sent during the last DMA transfer,
- and update xmit accordingly */
-
- /* if the stop bit was not set, all data has been sent */
- if (!(descr->status & d_stop)) {
- sentl = descr->sw_len;
- } else
- /* otherwise we find the amount of data sent here */
- sentl = descr->hw_len;
-
- DFLOW(DEBUG_LOG(info->line, "TX %i done\n", sentl));
-
- /* update stats */
- info->icount.tx += sentl;
-
- /* update xmit buffer */
- info->xmit.tail = (info->xmit.tail + sentl) & (SERIAL_XMIT_SIZE - 1);
-
- /* if there is only a few chars left in the buf, wake up the blocked
- write if any */
- if (CIRC_CNT(info->xmit.head,
- info->xmit.tail,
- SERIAL_XMIT_SIZE) < WAKEUP_CHARS)
- rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
-
- /* find out the largest amount of consecutive bytes we want to send now */
-
- c = CIRC_CNT_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
-
- /* Don't send all in one DMA transfer - divide it so we wake up
- * application before all is sent
- */
-
- if (c >= 4*WAKEUP_CHARS)
- c = c/2;
-
- if (c <= 0) {
- /* our job here is done, don't schedule any new DMA transfer */
- info->tr_running = 0;
-
-#if defined(CONFIG_ETRAX_RS485) && defined(CONFIG_ETRAX_FAST_TIMER)
- if (info->rs485.flags & SER_RS485_ENABLED) {
- /* Set a short timer to toggle RTS */
- start_one_shot_timer(&fast_timers_rs485[info->line],
- rs485_toggle_rts_timer_function,
- (unsigned long)info,
- info->char_time_usec*2,
- "RS-485");
- }
-#endif /* RS485 */
- return;
- }
-
- /* ok we can schedule a dma send of c chars starting at info->xmit.tail */
- /* set up the descriptor correctly for output */
- DFLOW(DEBUG_LOG(info->line, "TX %i\n", c));
- descr->ctrl = d_int | d_eol | d_wait; /* Wait needed for tty_wait_until_sent() */
- descr->sw_len = c;
- descr->buf = virt_to_phys(info->xmit.buf + info->xmit.tail);
- descr->status = 0;
-
- *info->ofirstadr = virt_to_phys(descr); /* write to R_DMAx_FIRST */
- *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, start);
-
- /* DMA is now running (hopefully) */
-} /* transmit_chars_dma */
-
-static void
-start_transmit(struct e100_serial *info)
-{
-#if 0
- if (info->line == SERIAL_DEBUG_LINE)
- printk("x\n");
-#endif
-
- info->tr_descr.sw_len = 0;
- info->tr_descr.hw_len = 0;
- info->tr_descr.status = 0;
- info->tr_running = 1;
- if (info->uses_dma_out)
- transmit_chars_dma(info);
- else
- e100_enable_serial_tx_ready_irq(info);
-} /* start_transmit */
-
-#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
-static int serial_fast_timer_started = 0;
-static int serial_fast_timer_expired = 0;
-static void flush_timeout_function(unsigned long data);
-#define START_FLUSH_FAST_TIMER_TIME(info, string, usec) {\
- unsigned long timer_flags; \
- local_irq_save(timer_flags); \
- if (fast_timers[info->line].function == NULL) { \
- serial_fast_timer_started++; \
- TIMERD(DEBUG_LOG(info->line, "start_timer %i ", info->line)); \
- TIMERD(DEBUG_LOG(info->line, "num started: %i\n", serial_fast_timer_started)); \
- start_one_shot_timer(&fast_timers[info->line], \
- flush_timeout_function, \
- (unsigned long)info, \
- (usec), \
- string); \
- } \
- else { \
- TIMERD(DEBUG_LOG(info->line, "timer %i already running\n", info->line)); \
- } \
- local_irq_restore(timer_flags); \
-}
-#define START_FLUSH_FAST_TIMER(info, string) START_FLUSH_FAST_TIMER_TIME(info, string, info->flush_time_usec)
-
-#else
-#define START_FLUSH_FAST_TIMER_TIME(info, string, usec)
-#define START_FLUSH_FAST_TIMER(info, string)
-#endif
-
-static struct etrax_recv_buffer *
-alloc_recv_buffer(unsigned int size)
-{
- struct etrax_recv_buffer *buffer;
-
- buffer = kmalloc(sizeof *buffer + size, GFP_ATOMIC);
- if (!buffer)
- return NULL;
-
- buffer->next = NULL;
- buffer->length = 0;
- buffer->error = TTY_NORMAL;
-
- return buffer;
-}
-
-static void
-append_recv_buffer(struct e100_serial *info, struct etrax_recv_buffer *buffer)
-{
- unsigned long flags;
-
- local_irq_save(flags);
-
- if (!info->first_recv_buffer)
- info->first_recv_buffer = buffer;
- else
- info->last_recv_buffer->next = buffer;
-
- info->last_recv_buffer = buffer;
-
- info->recv_cnt += buffer->length;
- if (info->recv_cnt > info->max_recv_cnt)
- info->max_recv_cnt = info->recv_cnt;
-
- local_irq_restore(flags);
-}
-
-static int
-add_char_and_flag(struct e100_serial *info, unsigned char data, unsigned char flag)
-{
- struct etrax_recv_buffer *buffer;
- if (info->uses_dma_in) {
- buffer = alloc_recv_buffer(4);
- if (!buffer)
- return 0;
-
- buffer->length = 1;
- buffer->error = flag;
- buffer->buffer[0] = data;
-
- append_recv_buffer(info, buffer);
-
- info->icount.rx++;
- } else {
- tty_insert_flip_char(&info->port, data, flag);
- info->icount.rx++;
- }
-
- return 1;
-}
-
-static unsigned int handle_descr_data(struct e100_serial *info,
- struct etrax_dma_descr *descr,
- unsigned int recvl)
-{
- struct etrax_recv_buffer *buffer = phys_to_virt(descr->buf) - sizeof *buffer;
-
- if (info->recv_cnt + recvl > 65536) {
- printk(KERN_WARNING
- "%s: Too much pending incoming serial data! Dropping %u bytes.\n", __func__, recvl);
- return 0;
- }
-
- buffer->length = recvl;
-
- if (info->errorcode == ERRCODE_SET_BREAK)
- buffer->error = TTY_BREAK;
- info->errorcode = 0;
-
- append_recv_buffer(info, buffer);
-
- buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE);
- if (!buffer)
- panic("%s: Failed to allocate memory for receive buffer!\n", __func__);
-
- descr->buf = virt_to_phys(buffer->buffer);
-
- return recvl;
-}
-
-static unsigned int handle_all_descr_data(struct e100_serial *info)
-{
- struct etrax_dma_descr *descr;
- unsigned int recvl;
- unsigned int ret = 0;
-
- while (1)
- {
- descr = &info->rec_descr[info->cur_rec_descr];
-
- if (descr == phys_to_virt(*info->idescradr))
- break;
-
- if (++info->cur_rec_descr == SERIAL_RECV_DESCRIPTORS)
- info->cur_rec_descr = 0;
-
- /* find out how many bytes were read */
-
- /* if the eop bit was not set, all data has been received */
- if (!(descr->status & d_eop)) {
- recvl = descr->sw_len;
- } else {
- /* otherwise we find the amount of data received here */
- recvl = descr->hw_len;
- }
-
- /* Reset the status information */
- descr->status = 0;
-
- DFLOW( DEBUG_LOG(info->line, "RX %lu\n", recvl);
- if (info->port.tty->stopped) {
- unsigned char *buf = phys_to_virt(descr->buf);
- DEBUG_LOG(info->line, "rx 0x%02X\n", buf[0]);
- DEBUG_LOG(info->line, "rx 0x%02X\n", buf[1]);
- DEBUG_LOG(info->line, "rx 0x%02X\n", buf[2]);
- }
- );
-
- /* update stats */
- info->icount.rx += recvl;
-
- ret += handle_descr_data(info, descr, recvl);
- }
-
- return ret;
-}
-
-static void receive_chars_dma(struct e100_serial *info)
-{
- struct tty_struct *tty;
- unsigned char rstat;
-
- /* Acknowledge both dma_descr and dma_eop irq in R_DMA_CHx_CLR_INTR */
- *info->iclrintradr =
- IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
- IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
-
- tty = info->port.tty;
- if (!tty) /* Something wrong... */
- return;
-
-#ifdef SERIAL_HANDLE_EARLY_ERRORS
- if (info->uses_dma_in)
- e100_enable_serial_data_irq(info);
-#endif
-
- if (info->errorcode == ERRCODE_INSERT_BREAK)
- add_char_and_flag(info, '\0', TTY_BREAK);
-
- handle_all_descr_data(info);
-
- /* Read the status register to detect errors */
- rstat = info->ioport[REG_STATUS];
- if (rstat & IO_MASK(R_SERIAL0_STATUS, xoff_detect) ) {
- DFLOW(DEBUG_LOG(info->line, "XOFF detect stat %x\n", rstat));
- }
-
- if (rstat & SER_ERROR_MASK) {
- /* If we got an error, we must reset it by reading the
- * data_in field
- */
- unsigned char data = info->ioport[REG_DATA];
-
- DEBUG_LOG(info->line, "#dERR: s d 0x%04X\n",
- ((rstat & SER_ERROR_MASK) << 8) | data);
-
- if (rstat & SER_PAR_ERR_MASK)
- add_char_and_flag(info, data, TTY_PARITY);
- else if (rstat & SER_OVERRUN_MASK)
- add_char_and_flag(info, data, TTY_OVERRUN);
- else if (rstat & SER_FRAMING_ERR_MASK)
- add_char_and_flag(info, data, TTY_FRAME);
- }
-
- START_FLUSH_FAST_TIMER(info, "receive_chars");
-
- /* Restart the receiving DMA */
- *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart);
-}
-
-static int start_recv_dma(struct e100_serial *info)
-{
- struct etrax_dma_descr *descr = info->rec_descr;
- struct etrax_recv_buffer *buffer;
- int i;
-
- /* Set up the receiving descriptors */
- for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++) {
- buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE);
- if (!buffer)
- panic("%s: Failed to allocate memory for receive buffer!\n", __func__);
-
- descr[i].ctrl = d_int;
- descr[i].buf = virt_to_phys(buffer->buffer);
- descr[i].sw_len = SERIAL_DESCR_BUF_SIZE;
- descr[i].hw_len = 0;
- descr[i].status = 0;
- descr[i].next = virt_to_phys(&descr[i+1]);
- }
-
- /* Link the last descriptor to the first */
- descr[i-1].next = virt_to_phys(&descr[0]);
-
- /* Start with the first descriptor in the list */
- info->cur_rec_descr = 0;
-
- /* Start the DMA */
- *info->ifirstadr = virt_to_phys(&descr[info->cur_rec_descr]);
- *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, start);
-
- /* Input DMA should be running now */
- return 1;
-}
-
-static void
-start_receive(struct e100_serial *info)
-{
- if (info->uses_dma_in) {
- /* reset the input dma channel to be sure it works */
-
- *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
- while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) ==
- IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
-
- start_recv_dma(info);
- }
-}
-
-
-/* the bits in the MASK2 register are laid out like this:
- DMAI_EOP DMAI_DESCR DMAO_EOP DMAO_DESCR
- where I is the input channel and O is the output channel for the port.
- info->irq is the bit number for the DMAO_DESCR so to check the others we
- shift info->irq to the left.
-*/
-
-/* dma output channel interrupt handler
- this interrupt is called from DMA2(ser2), DMA4(ser3), DMA6(ser0) or
- DMA8(ser1) when they have finished a descriptor with the intr flag set.
-*/
-
-static irqreturn_t
-tr_interrupt(int irq, void *dev_id)
-{
- struct e100_serial *info;
- unsigned long ireg;
- int i;
- int handled = 0;
-
- /* find out the line that caused this irq and get it from rs_table */
-
- ireg = *R_IRQ_MASK2_RD; /* get the active irq bits for the dma channels */
-
- for (i = 0; i < NR_PORTS; i++) {
- info = rs_table + i;
- if (!info->enabled || !info->uses_dma_out)
- continue;
- /* check for dma_descr (don't need to check for dma_eop in output dma for serial */
- if (ireg & info->irq) {
- handled = 1;
- /* we can send a new dma bunch. make it so. */
- DINTR2(DEBUG_LOG(info->line, "tr_interrupt %i\n", i));
- /* Read jiffies_usec first,
- * we want this time to be as late as possible
- */
- info->last_tx_active_usec = GET_JIFFIES_USEC();
- info->last_tx_active = jiffies;
- transmit_chars_dma(info);
- }
-
- /* FIXME: here we should really check for a change in the
- status lines and if so call status_handle(info) */
- }
- return IRQ_RETVAL(handled);
-} /* tr_interrupt */
-
-/* dma input channel interrupt handler */
-
-static irqreturn_t
-rec_interrupt(int irq, void *dev_id)
-{
- struct e100_serial *info;
- unsigned long ireg;
- int i;
- int handled = 0;
-
- /* find out the line that caused this irq and get it from rs_table */
-
- ireg = *R_IRQ_MASK2_RD; /* get the active irq bits for the dma channels */
-
- for (i = 0; i < NR_PORTS; i++) {
- info = rs_table + i;
- if (!info->enabled || !info->uses_dma_in)
- continue;
- /* check for both dma_eop and dma_descr for the input dma channel */
- if (ireg & ((info->irq << 2) | (info->irq << 3))) {
- handled = 1;
- /* we have received something */
- receive_chars_dma(info);
- }
-
- /* FIXME: here we should really check for a change in the
- status lines and if so call status_handle(info) */
- }
- return IRQ_RETVAL(handled);
-} /* rec_interrupt */
-
-static int force_eop_if_needed(struct e100_serial *info)
-{
- /* We check data_avail bit to determine if data has
- * arrived since last time
- */
- unsigned char rstat = info->ioport[REG_STATUS];
-
- /* error or datavail? */
- if (rstat & SER_ERROR_MASK) {
- /* Some error has occurred. If there has been valid data, an
- * EOP interrupt will be made automatically. If no data, the
- * normal ser_interrupt should be enabled and handle it.
- * So do nothing!
- */
- DEBUG_LOG(info->line, "timeout err: rstat 0x%03X\n",
- rstat | (info->line << 8));
- return 0;
- }
-
- if (rstat & SER_DATA_AVAIL_MASK) {
- /* Ok data, no error, count it */
- TIMERD(DEBUG_LOG(info->line, "timeout: rstat 0x%03X\n",
- rstat | (info->line << 8)));
- /* Read data to clear status flags */
- (void)info->ioport[REG_DATA];
-
- info->forced_eop = 0;
- START_FLUSH_FAST_TIMER(info, "magic");
- return 0;
- }
-
- /* hit the timeout, force an EOP for the input
- * dma channel if we haven't already
- */
- if (!info->forced_eop) {
- info->forced_eop = 1;
- TIMERD(DEBUG_LOG(info->line, "timeout EOP %i\n", info->line));
- FORCE_EOP(info);
- }
-
- return 1;
-}
-
-static void flush_to_flip_buffer(struct e100_serial *info)
-{
- struct etrax_recv_buffer *buffer;
- unsigned long flags;
-
- local_irq_save(flags);
-
- while ((buffer = info->first_recv_buffer) != NULL) {
- unsigned int count = buffer->length;
-
- tty_insert_flip_string(&info->port, buffer->buffer, count);
- info->recv_cnt -= count;
-
- if (count == buffer->length) {
- info->first_recv_buffer = buffer->next;
- kfree(buffer);
- } else {
- buffer->length -= count;
- memmove(buffer->buffer, buffer->buffer + count, buffer->length);
- buffer->error = TTY_NORMAL;
- }
- }
-
- if (!info->first_recv_buffer)
- info->last_recv_buffer = NULL;
-
- local_irq_restore(flags);
-
- /* This includes a check for low-latency */
- tty_flip_buffer_push(&info->port);
-}
-
-static void check_flush_timeout(struct e100_serial *info)
-{
- /* Flip what we've got (if we can) */
- flush_to_flip_buffer(info);
-
- /* We might need to flip later, but not to fast
- * since the system is busy processing input... */
- if (info->first_recv_buffer)
- START_FLUSH_FAST_TIMER_TIME(info, "flip", 2000);
-
- /* Force eop last, since data might have come while we're processing
- * and if we started the slow timer above, we won't start a fast
- * below.
- */
- force_eop_if_needed(info);
-}
-
-#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
-static void flush_timeout_function(unsigned long data)
-{
- struct e100_serial *info = (struct e100_serial *)data;
-
- fast_timers[info->line].function = NULL;
- serial_fast_timer_expired++;
- TIMERD(DEBUG_LOG(info->line, "flush_timeout %i ", info->line));
- TIMERD(DEBUG_LOG(info->line, "num expired: %i\n", serial_fast_timer_expired));
- check_flush_timeout(info);
-}
-
-#else
-
-/* dma fifo/buffer timeout handler
- forces an end-of-packet for the dma input channel if no chars
- have been received for CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS/100 s.
-*/
-
-static struct timer_list flush_timer;
-
-static void
-timed_flush_handler(struct timer_list *unused)
-{
- struct e100_serial *info;
- int i;
-
- for (i = 0; i < NR_PORTS; i++) {
- info = rs_table + i;
- if (info->uses_dma_in)
- check_flush_timeout(info);
- }
-
- /* restart flush timer */
- mod_timer(&flush_timer, jiffies + CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS);
-}
-#endif
-
-#ifdef SERIAL_HANDLE_EARLY_ERRORS
-
-/* If there is an error (ie break) when the DMA is running and
- * there are no bytes in the fifo the DMA is stopped and we get no
- * eop interrupt. Thus we have to monitor the first bytes on a DMA
- * transfer, and if it is without error we can turn the serial
- * interrupts off.
- */
-
-/*
-BREAK handling on ETRAX 100:
-ETRAX will generate interrupt although there is no stop bit between the
-characters.
-
-Depending on how long the break sequence is, the end of the breaksequence
-will look differently:
-| indicates start/end of a character.
-
-B= Break character (0x00) with framing error.
-E= Error byte with parity error received after B characters.
-F= "Faked" valid byte received immediately after B characters.
-V= Valid byte
-
-1.
- B BL ___________________________ V
-.._|__________|__________| |valid data |
-
-Multiple frame errors with data == 0x00 (B),
-the timing matches up "perfectly" so no extra ending char is detected.
-The RXD pin is 1 in the last interrupt, in that case
-we set info->errorcode = ERRCODE_INSERT_BREAK, but we can't really
-know if another byte will come and this really is case 2. below
-(e.g F=0xFF or 0xFE)
-If RXD pin is 0 we can expect another character (see 2. below).
-
-
-2.
-
- B B E or F__________________..__ V
-.._|__________|__________|______ | |valid data
- "valid" or
- parity error
-
-Multiple frame errors with data == 0x00 (B),
-but the part of the break trigs is interpreted as a start bit (and possibly
-some 0 bits followed by a number of 1 bits and a stop bit).
-Depending on parity settings etc. this last character can be either
-a fake "valid" char (F) or have a parity error (E).
-
-If the character is valid it will be put in the buffer,
-we set info->errorcode = ERRCODE_SET_BREAK so the receive interrupt
-will set the flags so the tty will handle it,
-if it's an error byte it will not be put in the buffer
-and we set info->errorcode = ERRCODE_INSERT_BREAK.
-
-To distinguish a V byte in 1. from an F byte in 2. we keep a timestamp
-of the last faulty char (B) and compares it with the current time:
-If the time elapsed time is less then 2*char_time_usec we will assume
-it's a faked F char and not a Valid char and set
-info->errorcode = ERRCODE_SET_BREAK.
-
-Flaws in the above solution:
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-We use the timer to distinguish a F character from a V character,
-if a V character is to close after the break we might make the wrong decision.
-
-TODO: The break will be delayed until an F or V character is received.
-
-*/
-
-static void handle_ser_rx_interrupt_no_dma(struct e100_serial *info)
-{
- unsigned long data_read;
-
- /* Read data and status at the same time */
- data_read = *((unsigned long *)&info->ioport[REG_DATA_STATUS32]);
-more_data:
- if (data_read & IO_MASK(R_SERIAL0_READ, xoff_detect) ) {
- DFLOW(DEBUG_LOG(info->line, "XOFF detect\n", 0));
- }
- DINTR2(DEBUG_LOG(info->line, "ser_rx %c\n", IO_EXTRACT(R_SERIAL0_READ, data_in, data_read)));
-
- if (data_read & ( IO_MASK(R_SERIAL0_READ, framing_err) |
- IO_MASK(R_SERIAL0_READ, par_err) |
- IO_MASK(R_SERIAL0_READ, overrun) )) {
- /* An error */
- info->last_rx_active_usec = GET_JIFFIES_USEC();
- info->last_rx_active = jiffies;
- DINTR1(DEBUG_LOG(info->line, "ser_rx err stat_data %04X\n", data_read));
- DLOG_INT_TRIG(
- if (!log_int_trig1_pos) {
- log_int_trig1_pos = log_int_pos;
- log_int(rdpc(), 0, 0);
- }
- );
-
-
- if ( ((data_read & IO_MASK(R_SERIAL0_READ, data_in)) == 0) &&
- (data_read & IO_MASK(R_SERIAL0_READ, framing_err)) ) {
- /* Most likely a break, but we get interrupts over and
- * over again.
- */
-
- if (!info->break_detected_cnt) {
- DEBUG_LOG(info->line, "#BRK start\n", 0);
- }
- if (data_read & IO_MASK(R_SERIAL0_READ, rxd)) {
- /* The RX pin is high now, so the break
- * must be over, but....
- * we can't really know if we will get another
- * last byte ending the break or not.
- * And we don't know if the byte (if any) will
- * have an error or look valid.
- */
- DEBUG_LOG(info->line, "# BL BRK\n", 0);
- info->errorcode = ERRCODE_INSERT_BREAK;
- }
- info->break_detected_cnt++;
- } else {
- /* The error does not look like a break, but could be
- * the end of one
- */
- if (info->break_detected_cnt) {
- DEBUG_LOG(info->line, "EBRK %i\n", info->break_detected_cnt);
- info->errorcode = ERRCODE_INSERT_BREAK;
- } else {
- unsigned char data = IO_EXTRACT(R_SERIAL0_READ,
- data_in, data_read);
- char flag = TTY_NORMAL;
- if (info->errorcode == ERRCODE_INSERT_BREAK) {
- tty_insert_flip_char(&info->port, 0, flag);
- info->icount.rx++;
- }
-
- if (data_read & IO_MASK(R_SERIAL0_READ, par_err)) {
- info->icount.parity++;
- flag = TTY_PARITY;
- } else if (data_read & IO_MASK(R_SERIAL0_READ, overrun)) {
- info->icount.overrun++;
- flag = TTY_OVERRUN;
- } else if (data_read & IO_MASK(R_SERIAL0_READ, framing_err)) {
- info->icount.frame++;
- flag = TTY_FRAME;
- }
- tty_insert_flip_char(&info->port, data, flag);
- info->errorcode = 0;
- }
- info->break_detected_cnt = 0;
- }
- } else if (data_read & IO_MASK(R_SERIAL0_READ, data_avail)) {
- /* No error */
- DLOG_INT_TRIG(
- if (!log_int_trig1_pos) {
- if (log_int_pos >= log_int_size) {
- log_int_pos = 0;
- }
- log_int_trig0_pos = log_int_pos;
- log_int(rdpc(), 0, 0);
- }
- );
- tty_insert_flip_char(&info->port,
- IO_EXTRACT(R_SERIAL0_READ, data_in, data_read),
- TTY_NORMAL);
- } else {
- DEBUG_LOG(info->line, "ser_rx int but no data_avail %08lX\n", data_read);
- }
-
-
- info->icount.rx++;
- data_read = *((unsigned long *)&info->ioport[REG_DATA_STATUS32]);
- if (data_read & IO_MASK(R_SERIAL0_READ, data_avail)) {
- DEBUG_LOG(info->line, "ser_rx %c in loop\n", IO_EXTRACT(R_SERIAL0_READ, data_in, data_read));
- goto more_data;
- }
-
- tty_flip_buffer_push(&info->port);
-}
-
-static void handle_ser_rx_interrupt(struct e100_serial *info)
-{
- unsigned char rstat;
-
-#ifdef SERIAL_DEBUG_INTR
- printk("Interrupt from serport %d\n", i);
-#endif
-/* DEBUG_LOG(info->line, "ser_interrupt stat %03X\n", rstat | (i << 8)); */
- if (!info->uses_dma_in) {
- handle_ser_rx_interrupt_no_dma(info);
- return;
- }
- /* DMA is used */
- rstat = info->ioport[REG_STATUS];
- if (rstat & IO_MASK(R_SERIAL0_STATUS, xoff_detect) ) {
- DFLOW(DEBUG_LOG(info->line, "XOFF detect\n", 0));
- }
-
- if (rstat & SER_ERROR_MASK) {
- unsigned char data;
-
- info->last_rx_active_usec = GET_JIFFIES_USEC();
- info->last_rx_active = jiffies;
- /* If we got an error, we must reset it by reading the
- * data_in field
- */
- data = info->ioport[REG_DATA];
- DINTR1(DEBUG_LOG(info->line, "ser_rx! %c\n", data));
- DINTR1(DEBUG_LOG(info->line, "ser_rx err stat %02X\n", rstat));
- if (!data && (rstat & SER_FRAMING_ERR_MASK)) {
- /* Most likely a break, but we get interrupts over and
- * over again.
- */
-
- if (!info->break_detected_cnt) {
- DEBUG_LOG(info->line, "#BRK start\n", 0);
- }
- if (rstat & SER_RXD_MASK) {
- /* The RX pin is high now, so the break
- * must be over, but....
- * we can't really know if we will get another
- * last byte ending the break or not.
- * And we don't know if the byte (if any) will
- * have an error or look valid.
- */
- DEBUG_LOG(info->line, "# BL BRK\n", 0);
- info->errorcode = ERRCODE_INSERT_BREAK;
- }
- info->break_detected_cnt++;
- } else {
- /* The error does not look like a break, but could be
- * the end of one
- */
- if (info->break_detected_cnt) {
- DEBUG_LOG(info->line, "EBRK %i\n", info->break_detected_cnt);
- info->errorcode = ERRCODE_INSERT_BREAK;
- } else {
- if (info->errorcode == ERRCODE_INSERT_BREAK) {
- info->icount.brk++;
- add_char_and_flag(info, '\0', TTY_BREAK);
- }
-
- if (rstat & SER_PAR_ERR_MASK) {
- info->icount.parity++;
- add_char_and_flag(info, data, TTY_PARITY);
- } else if (rstat & SER_OVERRUN_MASK) {
- info->icount.overrun++;
- add_char_and_flag(info, data, TTY_OVERRUN);
- } else if (rstat & SER_FRAMING_ERR_MASK) {
- info->icount.frame++;
- add_char_and_flag(info, data, TTY_FRAME);
- }
-
- info->errorcode = 0;
- }
- info->break_detected_cnt = 0;
- DEBUG_LOG(info->line, "#iERR s d %04X\n",
- ((rstat & SER_ERROR_MASK) << 8) | data);
- }
- } else { /* It was a valid byte, now let the DMA do the rest */
- unsigned long curr_time_u = GET_JIFFIES_USEC();
- unsigned long curr_time = jiffies;
-
- if (info->break_detected_cnt) {
- /* Detect if this character is a new valid char or the
- * last char in a break sequence: If LSBits are 0 and
- * MSBits are high AND the time is close to the
- * previous interrupt we should discard it.
- */
- long elapsed_usec =
- (curr_time - info->last_rx_active) * (1000000/HZ) +
- curr_time_u - info->last_rx_active_usec;
- if (elapsed_usec < 2*info->char_time_usec) {
- DEBUG_LOG(info->line, "FBRK %i\n", info->line);
- /* Report as BREAK (error) and let
- * receive_chars_dma() handle it
- */
- info->errorcode = ERRCODE_SET_BREAK;
- } else {
- DEBUG_LOG(info->line, "Not end of BRK (V)%i\n", info->line);
- }
- DEBUG_LOG(info->line, "num brk %i\n", info->break_detected_cnt);
- }
-
-#ifdef SERIAL_DEBUG_INTR
- printk("** OK, disabling ser_interrupts\n");
-#endif
- e100_disable_serial_data_irq(info);
- DINTR2(DEBUG_LOG(info->line, "ser_rx OK %d\n", info->line));
- info->break_detected_cnt = 0;
-
- }
- /* Restarting the DMA never hurts */
- *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart);
- START_FLUSH_FAST_TIMER(info, "ser_int");
-} /* handle_ser_rx_interrupt */
-
-static void handle_ser_tx_interrupt(struct e100_serial *info)
-{
- unsigned long flags;
-
- if (info->x_char) {
- unsigned char rstat;
- DFLOW(DEBUG_LOG(info->line, "tx_int: xchar 0x%02X\n", info->x_char));
- local_irq_save(flags);
- rstat = info->ioport[REG_STATUS];
- DFLOW(DEBUG_LOG(info->line, "stat %x\n", rstat));
-
- info->ioport[REG_TR_DATA] = info->x_char;
- info->icount.tx++;
- info->x_char = 0;
- /* We must enable since it is disabled in ser_interrupt */
- e100_enable_serial_tx_ready_irq(info);
- local_irq_restore(flags);
- return;
- }
- if (info->uses_dma_out) {
- unsigned char rstat;
- int i;
- /* We only use normal tx interrupt when sending x_char */
- DFLOW(DEBUG_LOG(info->line, "tx_int: xchar sent\n", 0));
- local_irq_save(flags);
- rstat = info->ioport[REG_STATUS];
- DFLOW(DEBUG_LOG(info->line, "stat %x\n", rstat));
- e100_disable_serial_tx_ready_irq(info);
- if (info->port.tty->stopped)
- rs_stop(info->port.tty);
- /* Enable the DMA channel and tell it to continue */
- e100_enable_txdma_channel(info);
- /* Wait 12 cycles before doing the DMA command */
- for(i = 6; i > 0; i--)
- nop();
-
- *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, continue);
- local_irq_restore(flags);
- return;
- }
- /* Normal char-by-char interrupt */
- if (info->xmit.head == info->xmit.tail
- || info->port.tty->stopped) {
- DFLOW(DEBUG_LOG(info->line, "tx_int: stopped %i\n",
- info->port.tty->stopped));
- e100_disable_serial_tx_ready_irq(info);
- info->tr_running = 0;
- return;
- }
- DINTR2(DEBUG_LOG(info->line, "tx_int %c\n", info->xmit.buf[info->xmit.tail]));
- /* Send a byte, rs485 timing is critical so turn of ints */
- local_irq_save(flags);
- info->ioport[REG_TR_DATA] = info->xmit.buf[info->xmit.tail];
- info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1);
- info->icount.tx++;
- if (info->xmit.head == info->xmit.tail) {
-#if defined(CONFIG_ETRAX_RS485) && defined(CONFIG_ETRAX_FAST_TIMER)
- if (info->rs485.flags & SER_RS485_ENABLED) {
- /* Set a short timer to toggle RTS */
- start_one_shot_timer(&fast_timers_rs485[info->line],
- rs485_toggle_rts_timer_function,
- (unsigned long)info,
- info->char_time_usec*2,
- "RS-485");
- }
-#endif /* RS485 */
- info->last_tx_active_usec = GET_JIFFIES_USEC();
- info->last_tx_active = jiffies;
- e100_disable_serial_tx_ready_irq(info);
- info->tr_running = 0;
- DFLOW(DEBUG_LOG(info->line, "tx_int: stop2\n", 0));
- } else {
- /* We must enable since it is disabled in ser_interrupt */
- e100_enable_serial_tx_ready_irq(info);
- }
- local_irq_restore(flags);
-
- if (CIRC_CNT(info->xmit.head,
- info->xmit.tail,
- SERIAL_XMIT_SIZE) < WAKEUP_CHARS)
- rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
-
-} /* handle_ser_tx_interrupt */
-
-/* result of time measurements:
- * RX duration 54-60 us when doing something, otherwise 6-9 us
- * ser_int duration: just sending: 8-15 us normally, up to 73 us
- */
-static irqreturn_t
-ser_interrupt(int irq, void *dev_id)
-{
- static volatile int tx_started = 0;
- struct e100_serial *info;
- int i;
- unsigned long flags;
- unsigned long irq_mask1_rd;
- unsigned long data_mask = (1 << (8+2*0)); /* ser0 data_avail */
- int handled = 0;
- static volatile unsigned long reentered_ready_mask = 0;
-
- local_irq_save(flags);
- irq_mask1_rd = *R_IRQ_MASK1_RD;
- /* First handle all rx interrupts with ints disabled */
- info = rs_table;
- irq_mask1_rd &= e100_ser_int_mask;
- for (i = 0; i < NR_PORTS; i++) {
- /* Which line caused the data irq? */
- if (irq_mask1_rd & data_mask) {
- handled = 1;
- handle_ser_rx_interrupt(info);
- }
- info += 1;
- data_mask <<= 2;
- }
- /* Handle tx interrupts with interrupts enabled so we
- * can take care of new data interrupts while transmitting
- * We protect the tx part with the tx_started flag.
- * We disable the tr_ready interrupts we are about to handle and
- * unblock the serial interrupt so new serial interrupts may come.
- *
- * If we get a new interrupt:
- * - it migth be due to synchronous serial ports.
- * - serial irq will be blocked by general irq handler.
- * - async data will be handled above (sync will be ignored).
- * - tx_started flag will prevent us from trying to send again and
- * we will exit fast - no need to unblock serial irq.
- * - Next (sync) serial interrupt handler will be runned with
- * disabled interrupt due to restore_flags() at end of function,
- * so sync handler will not be preempted or reentered.
- */
- if (!tx_started) {
- unsigned long ready_mask;
- unsigned long
- tx_started = 1;
- /* Only the tr_ready interrupts left */
- irq_mask1_rd &= (IO_MASK(R_IRQ_MASK1_RD, ser0_ready) |
- IO_MASK(R_IRQ_MASK1_RD, ser1_ready) |
- IO_MASK(R_IRQ_MASK1_RD, ser2_ready) |
- IO_MASK(R_IRQ_MASK1_RD, ser3_ready));
- while (irq_mask1_rd) {
- /* Disable those we are about to handle */
- *R_IRQ_MASK1_CLR = irq_mask1_rd;
- /* Unblock the serial interrupt */
- *R_VECT_MASK_SET = IO_STATE(R_VECT_MASK_SET, serial, set);
-
- local_irq_enable();
- ready_mask = (1 << (8+1+2*0)); /* ser0 tr_ready */
- info = rs_table;
- for (i = 0; i < NR_PORTS; i++) {
- /* Which line caused the ready irq? */
- if (irq_mask1_rd & ready_mask) {
- handled = 1;
- handle_ser_tx_interrupt(info);
- }
- info += 1;
- ready_mask <<= 2;
- }
- /* handle_ser_tx_interrupt enables tr_ready interrupts */
- local_irq_disable();
- /* Handle reentered TX interrupt */
- irq_mask1_rd = reentered_ready_mask;
- }
- local_irq_disable();
- tx_started = 0;
- } else {
- unsigned long ready_mask;
- ready_mask = irq_mask1_rd & (IO_MASK(R_IRQ_MASK1_RD, ser0_ready) |
- IO_MASK(R_IRQ_MASK1_RD, ser1_ready) |
- IO_MASK(R_IRQ_MASK1_RD, ser2_ready) |
- IO_MASK(R_IRQ_MASK1_RD, ser3_ready));
- if (ready_mask) {
- reentered_ready_mask |= ready_mask;
- /* Disable those we are about to handle */
- *R_IRQ_MASK1_CLR = ready_mask;
- DFLOW(DEBUG_LOG(SERIAL_DEBUG_LINE, "ser_int reentered with TX %X\n", ready_mask));
- }
- }
-
- local_irq_restore(flags);
- return IRQ_RETVAL(handled);
-} /* ser_interrupt */
-#endif
-
-/*
- * -------------------------------------------------------------------
- * Here ends the serial interrupt routines.
- * -------------------------------------------------------------------
- */
-
-/*
- * This routine is used to handle the "bottom half" processing for the
- * serial driver, known also the "software interrupt" processing.
- * This processing is done at the kernel interrupt level, after the
- * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This
- * is where time-consuming activities which can not be done in the
- * interrupt driver proper are done; the interrupt driver schedules
- * them using rs_sched_event(), and they get done here.
- */
-static void
-do_softint(struct work_struct *work)
-{
- struct e100_serial *info;
- struct tty_struct *tty;
-
- info = container_of(work, struct e100_serial, work);
-
- tty = info->port.tty;
- if (!tty)
- return;
-
- if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event))
- tty_wakeup(tty);
-}
-
-static int
-startup(struct e100_serial * info)
-{
- unsigned long flags;
- unsigned long xmit_page;
- int i;
-
- xmit_page = get_zeroed_page(GFP_KERNEL);
- if (!xmit_page)
- return -ENOMEM;
-
- local_irq_save(flags);
-
- /* if it was already initialized, skip this */
-
- if (tty_port_initialized(&info->port)) {
- local_irq_restore(flags);
- free_page(xmit_page);
- return 0;
- }
-
- if (info->xmit.buf)
- free_page(xmit_page);
- else
- info->xmit.buf = (unsigned char *) xmit_page;
-
-#ifdef SERIAL_DEBUG_OPEN
- printk("starting up ttyS%d (xmit_buf 0x%p)...\n", info->line, info->xmit.buf);
-#endif
-
- /*
- * Clear the FIFO buffers and disable them
- * (they will be reenabled in change_speed())
- */
-
- /*
- * Reset the DMA channels and make sure their interrupts are cleared
- */
-
- if (info->dma_in_enabled) {
- info->uses_dma_in = 1;
- e100_enable_rxdma_channel(info);
-
- *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
-
- /* Wait until reset cycle is complete */
- while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) ==
- IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
-
- /* Make sure the irqs are cleared */
- *info->iclrintradr =
- IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
- IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
- } else {
- e100_disable_rxdma_channel(info);
- }
-
- if (info->dma_out_enabled) {
- info->uses_dma_out = 1;
- e100_enable_txdma_channel(info);
- *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
-
- while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->ocmdadr) ==
- IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
-
- /* Make sure the irqs are cleared */
- *info->oclrintradr =
- IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
- IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
- } else {
- e100_disable_txdma_channel(info);
- }
-
- if (info->port.tty)
- clear_bit(TTY_IO_ERROR, &info->port.tty->flags);
-
- info->xmit.head = info->xmit.tail = 0;
- info->first_recv_buffer = info->last_recv_buffer = NULL;
- info->recv_cnt = info->max_recv_cnt = 0;
-
- for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++)
- info->rec_descr[i].buf = 0;
-
- /*
- * and set the speed and other flags of the serial port
- * this will start the rx/tx as well
- */
-#ifdef SERIAL_HANDLE_EARLY_ERRORS
- e100_enable_serial_data_irq(info);
-#endif
- change_speed(info);
-
- /* dummy read to reset any serial errors */
-
- (void)info->ioport[REG_DATA];
-
- /* enable the interrupts */
- if (info->uses_dma_out)
- e100_enable_txdma_irq(info);
-
- e100_enable_rx_irq(info);
-
- info->tr_running = 0; /* to be sure we don't lock up the transmitter */
-
- /* setup the dma input descriptor and start dma */
-
- start_receive(info);
-
- /* for safety, make sure the descriptors last result is 0 bytes written */
-
- info->tr_descr.sw_len = 0;
- info->tr_descr.hw_len = 0;
- info->tr_descr.status = 0;
-
- /* enable RTS/DTR last */
-
- e100_rts(info, 1);
- e100_dtr(info, 1);
-
- tty_port_set_initialized(&info->port, 1);
-
- local_irq_restore(flags);
- return 0;
-}
-
-/*
- * This routine will shutdown a serial port; interrupts are disabled, and
- * DTR is dropped if the hangup on close termio flag is on.
- */
-static void
-shutdown(struct e100_serial * info)
-{
- unsigned long flags;
- struct etrax_dma_descr *descr = info->rec_descr;
- struct etrax_recv_buffer *buffer;
- int i;
-
- /* shut down the transmitter and receiver */
- DFLOW(DEBUG_LOG(info->line, "shutdown %i\n", info->line));
- e100_disable_rx(info);
- info->ioport[REG_TR_CTRL] = (info->tx_ctrl &= ~0x40);
-
- /* disable interrupts, reset dma channels */
- if (info->uses_dma_in) {
- e100_disable_rxdma_irq(info);
- *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
- info->uses_dma_in = 0;
- } else {
- e100_disable_serial_data_irq(info);
- }
-
- if (info->uses_dma_out) {
- e100_disable_txdma_irq(info);
- info->tr_running = 0;
- *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
- info->uses_dma_out = 0;
- } else {
- e100_disable_serial_tx_ready_irq(info);
- info->tr_running = 0;
- }
-
- if (!tty_port_initialized(&info->port))
- return;
-
-#ifdef SERIAL_DEBUG_OPEN
- printk("Shutting down serial port %d (irq %d)....\n", info->line,
- info->irq);
-#endif
-
- local_irq_save(flags);
-
- if (info->xmit.buf) {
- free_page((unsigned long)info->xmit.buf);
- info->xmit.buf = NULL;
- }
-
- for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++)
- if (descr[i].buf) {
- buffer = phys_to_virt(descr[i].buf) - sizeof *buffer;
- kfree(buffer);
- descr[i].buf = 0;
- }
-
- if (!info->port.tty || (info->port.tty->termios.c_cflag & HUPCL)) {
- /* hang up DTR and RTS if HUPCL is enabled */
- e100_dtr(info, 0);
- e100_rts(info, 0); /* could check CRTSCTS before doing this */
- }
-
- if (info->port.tty)
- set_bit(TTY_IO_ERROR, &info->port.tty->flags);
-
- tty_port_set_initialized(&info->port, 0);
- local_irq_restore(flags);
-}
-
-
-/* change baud rate and other assorted parameters */
-
-static void
-change_speed(struct e100_serial *info)
-{
- unsigned int cflag;
- unsigned long xoff;
- unsigned long flags;
- /* first some safety checks */
-
- if (!info->port.tty)
- return;
- if (!info->ioport)
- return;
-
- cflag = info->port.tty->termios.c_cflag;
-
- /* possibly, the tx/rx should be disabled first to do this safely */
-
- /* change baud-rate and write it to the hardware */
- if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) {
- /* Special baudrate */
- u32 mask = 0xFF << (info->line*8); /* Each port has 8 bits */
- unsigned long alt_source =
- IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, normal) |
- IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, normal);
- /* R_ALT_SER_BAUDRATE selects the source */
- DBAUD(printk("Custom baudrate: baud_base/divisor %lu/%i\n",
- (unsigned long)info->baud_base, info->custom_divisor));
- if (info->baud_base == SERIAL_PRESCALE_BASE) {
- /* 0, 2-65535 (0=65536) */
- u16 divisor = info->custom_divisor;
- /* R_SERIAL_PRESCALE (upper 16 bits of R_CLOCK_PRESCALE) */
- /* baudrate is 3.125MHz/custom_divisor */
- alt_source =
- IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, prescale) |
- IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, prescale);
- alt_source = 0x11;
- DBAUD(printk("Writing SERIAL_PRESCALE: divisor %i\n", divisor));
- *R_SERIAL_PRESCALE = divisor;
- info->baud = SERIAL_PRESCALE_BASE/divisor;
- }
- else
- {
- /* Bad baudbase, we don't support using timer0
- * for baudrate.
- */
- printk(KERN_WARNING "Bad baud_base/custom_divisor: %lu/%i\n",
- (unsigned long)info->baud_base, info->custom_divisor);
- }
- r_alt_ser_baudrate_shadow &= ~mask;
- r_alt_ser_baudrate_shadow |= (alt_source << (info->line*8));
- *R_ALT_SER_BAUDRATE = r_alt_ser_baudrate_shadow;
- } else {
- /* Normal baudrate */
- /* Make sure we use normal baudrate */
- u32 mask = 0xFF << (info->line*8); /* Each port has 8 bits */
- unsigned long alt_source =
- IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, normal) |
- IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, normal);
- r_alt_ser_baudrate_shadow &= ~mask;
- r_alt_ser_baudrate_shadow |= (alt_source << (info->line*8));
- *R_ALT_SER_BAUDRATE = r_alt_ser_baudrate_shadow;
-
- info->baud = cflag_to_baud(cflag);
- info->ioport[REG_BAUD] = cflag_to_etrax_baud(cflag);
- }
-
- /* start with default settings and then fill in changes */
- local_irq_save(flags);
- /* 8 bit, no/even parity */
- info->rx_ctrl &= ~(IO_MASK(R_SERIAL0_REC_CTRL, rec_bitnr) |
- IO_MASK(R_SERIAL0_REC_CTRL, rec_par_en) |
- IO_MASK(R_SERIAL0_REC_CTRL, rec_par));
-
- /* 8 bit, no/even parity, 1 stop bit, no cts */
- info->tx_ctrl &= ~(IO_MASK(R_SERIAL0_TR_CTRL, tr_bitnr) |
- IO_MASK(R_SERIAL0_TR_CTRL, tr_par_en) |
- IO_MASK(R_SERIAL0_TR_CTRL, tr_par) |
- IO_MASK(R_SERIAL0_TR_CTRL, stop_bits) |
- IO_MASK(R_SERIAL0_TR_CTRL, auto_cts));
-
- if ((cflag & CSIZE) == CS7) {
- /* set 7 bit mode */
- info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_bitnr, tr_7bit);
- info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_bitnr, rec_7bit);
- }
-
- if (cflag & CSTOPB) {
- /* set 2 stop bit mode */
- info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, stop_bits, two_bits);
- }
-
- if (cflag & PARENB) {
- /* enable parity */
- info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_par_en, enable);
- info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_par_en, enable);
- }
-
- if (cflag & CMSPAR) {
- /* enable stick parity, PARODD mean Mark which matches ETRAX */
- info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_stick_par, stick);
- info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_stick_par, stick);
- }
- if (cflag & PARODD) {
- /* set odd parity (or Mark if CMSPAR) */
- info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_par, odd);
- info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_par, odd);
- }
-
- if (cflag & CRTSCTS) {
- /* enable automatic CTS handling */
- DFLOW(DEBUG_LOG(info->line, "FLOW auto_cts enabled\n", 0));
- info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, auto_cts, active);
- }
-
- /* make sure the tx and rx are enabled */
-
- info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_enable, enable);
- info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_enable, enable);
-
- /* actually write the control regs to the hardware */
-
- info->ioport[REG_TR_CTRL] = info->tx_ctrl;
- info->ioport[REG_REC_CTRL] = info->rx_ctrl;
- xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(info->port.tty));
- xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, enable);
- if (info->port.tty->termios.c_iflag & IXON ) {
- DFLOW(DEBUG_LOG(info->line, "FLOW XOFF enabled 0x%02X\n",
- STOP_CHAR(info->port.tty)));
- xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable);
- }
-
- *((unsigned long *)&info->ioport[REG_XOFF]) = xoff;
- local_irq_restore(flags);
-
- update_char_time(info);
-
-} /* change_speed */
-
-/* start transmitting chars NOW */
-
-static void
-rs_flush_chars(struct tty_struct *tty)
-{
- struct e100_serial *info = (struct e100_serial *)tty->driver_data;
- unsigned long flags;
-
- if (info->tr_running ||
- info->xmit.head == info->xmit.tail ||
- tty->stopped ||
- !info->xmit.buf)
- return;
-
-#ifdef SERIAL_DEBUG_FLOW
- printk("rs_flush_chars\n");
-#endif
-
- /* this protection might not exactly be necessary here */
-
- local_irq_save(flags);
- start_transmit(info);
- local_irq_restore(flags);
-}
-
-static int rs_raw_write(struct tty_struct *tty,
- const unsigned char *buf, int count)
-{
- int c, ret = 0;
- struct e100_serial *info = (struct e100_serial *)tty->driver_data;
- unsigned long flags;
-
- /* first some sanity checks */
-
- if (!info->xmit.buf)
- return 0;
-
-#ifdef SERIAL_DEBUG_DATA
- if (info->line == SERIAL_DEBUG_LINE)
- printk("rs_raw_write (%d), status %d\n",
- count, info->ioport[REG_STATUS]);
-#endif
-
- local_save_flags(flags);
- DFLOW(DEBUG_LOG(info->line, "write count %i ", count));
- DFLOW(DEBUG_LOG(info->line, "ldisc\n"));
-
-
- /* The local_irq_disable/restore_flags pairs below are needed
- * because the DMA interrupt handler moves the info->xmit values.
- * the memcpy needs to be in the critical region unfortunately,
- * because we need to read xmit values, memcpy, write xmit values
- * in one atomic operation... this could perhaps be avoided by
- * more clever design.
- */
- local_irq_disable();
- while (count) {
- c = CIRC_SPACE_TO_END(info->xmit.head,
- info->xmit.tail,
- SERIAL_XMIT_SIZE);
-
- if (count < c)
- c = count;
- if (c <= 0)
- break;
-
- memcpy(info->xmit.buf + info->xmit.head, buf, c);
- info->xmit.head = (info->xmit.head + c) &
- (SERIAL_XMIT_SIZE-1);
- buf += c;
- count -= c;
- ret += c;
- }
- local_irq_restore(flags);
-
- /* enable transmitter if not running, unless the tty is stopped
- * this does not need IRQ protection since if tr_running == 0
- * the IRQ's are not running anyway for this port.
- */
- DFLOW(DEBUG_LOG(info->line, "write ret %i\n", ret));
-
- if (info->xmit.head != info->xmit.tail &&
- !tty->stopped &&
- !info->tr_running) {
- start_transmit(info);
- }
-
- return ret;
-} /* raw_raw_write() */
-
-static int
-rs_write(struct tty_struct *tty,
- const unsigned char *buf, int count)
-{
-#if defined(CONFIG_ETRAX_RS485)
- struct e100_serial *info = (struct e100_serial *)tty->driver_data;
-
- if (info->rs485.flags & SER_RS485_ENABLED)
- {
- /* If we are in RS-485 mode, we need to toggle RTS and disable
- * the receiver before initiating a DMA transfer
- */
-#ifdef CONFIG_ETRAX_FAST_TIMER
- /* Abort any started timer */
- fast_timers_rs485[info->line].function = NULL;
- del_fast_timer(&fast_timers_rs485[info->line]);
-#endif
- e100_rts(info, (info->rs485.flags & SER_RS485_RTS_ON_SEND));
-#if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER)
- e100_disable_rx(info);
- e100_enable_rx_irq(info);
-#endif
- if (info->rs485.delay_rts_before_send > 0)
- msleep(info->rs485.delay_rts_before_send);
- }
-#endif /* CONFIG_ETRAX_RS485 */
-
- count = rs_raw_write(tty, buf, count);
-
-#if defined(CONFIG_ETRAX_RS485)
- if (info->rs485.flags & SER_RS485_ENABLED)
- {
- unsigned int val;
- /* If we are in RS-485 mode the following has to be done:
- * wait until DMA is ready
- * wait on transmit shift register
- * toggle RTS
- * enable the receiver
- */
-
- /* Sleep until all sent */
- tty_wait_until_sent(tty, 0);
-#ifdef CONFIG_ETRAX_FAST_TIMER
- /* Now sleep a little more so that shift register is empty */
- schedule_usleep(info->char_time_usec * 2);
-#endif
- /* wait on transmit shift register */
- do{
- get_lsr_info(info, &val);
- }while (!(val & TIOCSER_TEMT));
-
- e100_rts(info, (info->rs485.flags & SER_RS485_RTS_AFTER_SEND));
-
-#if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER)
- e100_enable_rx(info);
- e100_enable_rxdma_irq(info);
-#endif
- }
-#endif /* CONFIG_ETRAX_RS485 */
-
- return count;
-} /* rs_write */
-
-
-/* how much space is available in the xmit buffer? */
-
-static int
-rs_write_room(struct tty_struct *tty)
-{
- struct e100_serial *info = (struct e100_serial *)tty->driver_data;
-
- return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
-}
-
-/* How many chars are in the xmit buffer?
- * This does not include any chars in the transmitter FIFO.
- * Use wait_until_sent for waiting for FIFO drain.
- */
-
-static int
-rs_chars_in_buffer(struct tty_struct *tty)
-{
- struct e100_serial *info = (struct e100_serial *)tty->driver_data;
-
- return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
-}
-
-/* discard everything in the xmit buffer */
-
-static void
-rs_flush_buffer(struct tty_struct *tty)
-{
- struct e100_serial *info = (struct e100_serial *)tty->driver_data;
- unsigned long flags;
-
- local_irq_save(flags);
- info->xmit.head = info->xmit.tail = 0;
- local_irq_restore(flags);
-
- tty_wakeup(tty);
-}
-
-/*
- * This function is used to send a high-priority XON/XOFF character to
- * the device
- *
- * Since we use DMA we don't check for info->x_char in transmit_chars_dma(),
- * but we do it in handle_ser_tx_interrupt().
- * We disable DMA channel and enable tx ready interrupt and write the
- * character when possible.
- */
-static void rs_send_xchar(struct tty_struct *tty, char ch)
-{
- struct e100_serial *info = (struct e100_serial *)tty->driver_data;
- unsigned long flags;
- local_irq_save(flags);
- if (info->uses_dma_out) {
- /* Put the DMA on hold and disable the channel */
- *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, hold);
- while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->ocmdadr) !=
- IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, hold));
- e100_disable_txdma_channel(info);
- }
-
- /* Must make sure transmitter is not stopped before we can transmit */
- if (tty->stopped)
- rs_start(tty);
-
- /* Enable manual transmit interrupt and send from there */
- DFLOW(DEBUG_LOG(info->line, "rs_send_xchar 0x%02X\n", ch));
- info->x_char = ch;
- e100_enable_serial_tx_ready_irq(info);
- local_irq_restore(flags);
-}
-
-/*
- * ------------------------------------------------------------
- * rs_throttle()
- *
- * This routine is called by the upper-layer tty layer to signal that
- * incoming characters should be throttled.
- * ------------------------------------------------------------
- */
-static void
-rs_throttle(struct tty_struct * tty)
-{
- struct e100_serial *info = (struct e100_serial *)tty->driver_data;
-#ifdef SERIAL_DEBUG_THROTTLE
- printk("throttle %s ....\n", tty_name(tty));
-#endif
- DFLOW(DEBUG_LOG(info->line,"rs_throttle\n"));
-
- /* Do RTS before XOFF since XOFF might take some time */
- if (C_CRTSCTS(tty)) {
- /* Turn off RTS line */
- e100_rts(info, 0);
- }
- if (I_IXOFF(tty))
- rs_send_xchar(tty, STOP_CHAR(tty));
-
-}
-
-static void
-rs_unthrottle(struct tty_struct * tty)
-{
- struct e100_serial *info = (struct e100_serial *)tty->driver_data;
-#ifdef SERIAL_DEBUG_THROTTLE
- printk("unthrottle %s ....\n", tty_name(tty));
-#endif
- DFLOW(DEBUG_LOG(info->line,"rs_unthrottle ldisc\n"));
- DFLOW(DEBUG_LOG(info->line,"rs_unthrottle flip.count: %i\n", tty->flip.count));
- /* Do RTS before XOFF since XOFF might take some time */
- if (C_CRTSCTS(tty)) {
- /* Assert RTS line */
- e100_rts(info, 1);
- }
-
- if (I_IXOFF(tty)) {
- if (info->x_char)
- info->x_char = 0;
- else
- rs_send_xchar(tty, START_CHAR(tty));
- }
-
-}
-
-/*
- * ------------------------------------------------------------
- * rs_ioctl() and friends
- * ------------------------------------------------------------
- */
-
-static int
-get_serial_info(struct e100_serial * info,
- struct serial_struct * retinfo)
-{
- struct serial_struct tmp;
-
- /* this is all probably wrong, there are a lot of fields
- * here that we don't have in e100_serial and maybe we
- * should set them to something else than 0.
- */
-
- memset(&tmp, 0, sizeof(tmp));
- tmp.type = info->type;
- tmp.line = info->line;
- tmp.port = (int)info->ioport;
- tmp.irq = info->irq;
- tmp.flags = info->port.flags;
- tmp.baud_base = info->baud_base;
- tmp.close_delay = info->port.close_delay;
- tmp.closing_wait = info->port.closing_wait;
- tmp.custom_divisor = info->custom_divisor;
- if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
- return -EFAULT;
- return 0;
-}
-
-static int
-set_serial_info(struct e100_serial *info,
- struct serial_struct *new_info)
-{
- struct serial_struct new_serial;
- struct e100_serial old_info;
- int retval = 0;
-
- if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
- return -EFAULT;
-
- old_info = *info;
-
- if (!capable(CAP_SYS_ADMIN)) {
- if ((new_serial.type != info->type) ||
- (new_serial.close_delay != info->port.close_delay) ||
- ((new_serial.flags & ~ASYNC_USR_MASK) !=
- (info->port.flags & ~ASYNC_USR_MASK)))
- return -EPERM;
- info->port.flags = ((info->port.flags & ~ASYNC_USR_MASK) |
- (new_serial.flags & ASYNC_USR_MASK));
- goto check_and_exit;
- }
-
- if (info->port.count > 1)
- return -EBUSY;
-
- /*
- * OK, past this point, all the error checking has been done.
- * At this point, we start making changes.....
- */
-
- info->baud_base = new_serial.baud_base;
- info->port.flags = ((info->port.flags & ~ASYNC_FLAGS) |
- (new_serial.flags & ASYNC_FLAGS));
- info->custom_divisor = new_serial.custom_divisor;
- info->type = new_serial.type;
- info->port.close_delay = new_serial.close_delay;
- info->port.closing_wait = new_serial.closing_wait;
- info->port.low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0;
-
- check_and_exit:
- if (tty_port_initialized(&info->port))
- change_speed(info);
- else
- retval = startup(info);
- return retval;
-}
-
-/*
- * 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
-get_lsr_info(struct e100_serial * info, unsigned int *value)
-{
- unsigned int result = TIOCSER_TEMT;
- unsigned long curr_time = jiffies;
- unsigned long curr_time_usec = GET_JIFFIES_USEC();
- unsigned long elapsed_usec =
- (curr_time - info->last_tx_active) * 1000000/HZ +
- curr_time_usec - info->last_tx_active_usec;
-
- if (info->xmit.head != info->xmit.tail ||
- elapsed_usec < 2*info->char_time_usec) {
- result = 0;
- }
-
- if (copy_to_user(value, &result, sizeof(int)))
- return -EFAULT;
- return 0;
-}
-
-#ifdef SERIAL_DEBUG_IO
-struct state_str
-{
- int state;
- const char *str;
-};
-
-const struct state_str control_state_str[] = {
- {TIOCM_DTR, "DTR" },
- {TIOCM_RTS, "RTS"},
- {TIOCM_ST, "ST?" },
- {TIOCM_SR, "SR?" },
- {TIOCM_CTS, "CTS" },
- {TIOCM_CD, "CD" },
- {TIOCM_RI, "RI" },
- {TIOCM_DSR, "DSR" },
- {0, NULL }
-};
-
-char *get_control_state_str(int MLines, char *s)
-{
- int i = 0;
-
- s[0]='\0';
- while (control_state_str[i].str != NULL) {
- if (MLines & control_state_str[i].state) {
- if (s[0] != '\0') {
- strcat(s, ", ");
- }
- strcat(s, control_state_str[i].str);
- }
- i++;
- }
- return s;
-}
-#endif
-
-static int
-rs_break(struct tty_struct *tty, int break_state)
-{
- struct e100_serial *info = (struct e100_serial *)tty->driver_data;
- unsigned long flags;
-
- if (!info->ioport)
- return -EIO;
-
- local_irq_save(flags);
- if (break_state == -1) {
- /* Go to manual mode and set the txd pin to 0 */
- /* Clear bit 7 (txd) and 6 (tr_enable) */
- info->tx_ctrl &= 0x3F;
- } else {
- /* Set bit 7 (txd) and 6 (tr_enable) */
- info->tx_ctrl |= (0x80 | 0x40);
- }
- info->ioport[REG_TR_CTRL] = info->tx_ctrl;
- local_irq_restore(flags);
- return 0;
-}
-
-static int
-rs_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear)
-{
- struct e100_serial *info = (struct e100_serial *)tty->driver_data;
- unsigned long flags;
-
- local_irq_save(flags);
-
- if (clear & TIOCM_RTS)
- e100_rts(info, 0);
- if (clear & TIOCM_DTR)
- e100_dtr(info, 0);
- /* Handle FEMALE behaviour */
- if (clear & TIOCM_RI)
- e100_ri_out(info, 0);
- if (clear & TIOCM_CD)
- e100_cd_out(info, 0);
-
- if (set & TIOCM_RTS)
- e100_rts(info, 1);
- if (set & TIOCM_DTR)
- e100_dtr(info, 1);
- /* Handle FEMALE behaviour */
- if (set & TIOCM_RI)
- e100_ri_out(info, 1);
- if (set & TIOCM_CD)
- e100_cd_out(info, 1);
-
- local_irq_restore(flags);
- return 0;
-}
-
-static int
-rs_tiocmget(struct tty_struct *tty)
-{
- struct e100_serial *info = (struct e100_serial *)tty->driver_data;
- unsigned int result;
- unsigned long flags;
-
- local_irq_save(flags);
-
- result =
- (!E100_RTS_GET(info) ? TIOCM_RTS : 0)
- | (!E100_DTR_GET(info) ? TIOCM_DTR : 0)
- | (!E100_RI_GET(info) ? TIOCM_RNG : 0)
- | (!E100_DSR_GET(info) ? TIOCM_DSR : 0)
- | (!E100_CD_GET(info) ? TIOCM_CAR : 0)
- | (!E100_CTS_GET(info) ? TIOCM_CTS : 0);
-
- local_irq_restore(flags);
-
-#ifdef SERIAL_DEBUG_IO
- printk(KERN_DEBUG "ser%i: modem state: %i 0x%08X\n",
- info->line, result, result);
- {
- char s[100];
-
- get_control_state_str(result, s);
- printk(KERN_DEBUG "state: %s\n", s);
- }
-#endif
- return result;
-
-}
-
-
-static int
-rs_ioctl(struct tty_struct *tty,
- unsigned int cmd, unsigned long arg)
-{
- struct e100_serial * info = (struct e100_serial *)tty->driver_data;
-
- if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
- (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) &&
- (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) {
- if (tty_io_error(tty))
- return -EIO;
- }
-
- switch (cmd) {
- case TIOCGSERIAL:
- return get_serial_info(info,
- (struct serial_struct *) arg);
- case TIOCSSERIAL:
- return set_serial_info(info,
- (struct serial_struct *) arg);
- case TIOCSERGETLSR: /* Get line status register */
- return get_lsr_info(info, (unsigned int *) arg);
-
- case TIOCSERGSTRUCT:
- if (copy_to_user((struct e100_serial *) arg,
- info, sizeof(struct e100_serial)))
- return -EFAULT;
- return 0;
-
-#if defined(CONFIG_ETRAX_RS485)
- case TIOCSERSETRS485:
- {
- /* In this ioctl we still use the old structure
- * rs485_control for backward compatibility
- * (if we use serial_rs485, then old user-level code
- * wouldn't work anymore...).
- * The use of this ioctl is deprecated: use TIOCSRS485
- * instead.*/
- struct rs485_control rs485ctrl;
- struct serial_rs485 rs485data;
- printk(KERN_DEBUG "The use of this ioctl is deprecated. Use TIOCSRS485 instead\n");
- if (copy_from_user(&rs485ctrl, (struct rs485_control *)arg,
- sizeof(rs485ctrl)))
- return -EFAULT;
-
- rs485data.delay_rts_before_send = rs485ctrl.delay_rts_before_send;
- rs485data.flags = 0;
-
- if (rs485ctrl.enabled)
- rs485data.flags |= SER_RS485_ENABLED;
- else
- rs485data.flags &= ~(SER_RS485_ENABLED);
-
- if (rs485ctrl.rts_on_send)
- rs485data.flags |= SER_RS485_RTS_ON_SEND;
- else
- rs485data.flags &= ~(SER_RS485_RTS_ON_SEND);
-
- if (rs485ctrl.rts_after_sent)
- rs485data.flags |= SER_RS485_RTS_AFTER_SEND;
- else
- rs485data.flags &= ~(SER_RS485_RTS_AFTER_SEND);
-
- return e100_enable_rs485(tty, &rs485data);
- }
-
- case TIOCSRS485:
- {
- /* This is the new version of TIOCSRS485, with new
- * data structure serial_rs485 */
- struct serial_rs485 rs485data;
- if (copy_from_user(&rs485data, (struct rs485_control *)arg,
- sizeof(rs485data)))
- return -EFAULT;
-
- return e100_enable_rs485(tty, &rs485data);
- }
-
- case TIOCGRS485:
- {
- struct serial_rs485 *rs485data =
- &(((struct e100_serial *)tty->driver_data)->rs485);
- /* This is the ioctl to get RS485 data from user-space */
- if (copy_to_user((struct serial_rs485 *) arg,
- rs485data,
- sizeof(struct serial_rs485)))
- return -EFAULT;
- break;
- }
-
- case TIOCSERWRRS485:
- {
- struct rs485_write rs485wr;
- if (copy_from_user(&rs485wr, (struct rs485_write *)arg,
- sizeof(rs485wr)))
- return -EFAULT;
-
- return e100_write_rs485(tty, rs485wr.outc, rs485wr.outc_size);
- }
-#endif
-
- default:
- return -ENOIOCTLCMD;
- }
- return 0;
-}
-
-static void
-rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
-{
- struct e100_serial *info = (struct e100_serial *)tty->driver_data;
-
- change_speed(info);
-
- /* Handle turning off CRTSCTS */
- if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty))
- rs_start(tty);
-
-}
-
-/*
- * ------------------------------------------------------------
- * rs_close()
- *
- * This routine is called when the serial port gets closed. First, we
- * wait for the last remaining data to be sent. Then, we unlink its
- * S structure from the interrupt chain if necessary, and we free
- * that IRQ if nothing is left in the chain.
- * ------------------------------------------------------------
- */
-static void
-rs_close(struct tty_struct *tty, struct file * filp)
-{
- struct e100_serial * info = (struct e100_serial *)tty->driver_data;
- unsigned long flags;
-
- if (!info)
- return;
-
- /* interrupts are disabled for this entire function */
-
- local_irq_save(flags);
-
- if (tty_hung_up_p(filp)) {
- local_irq_restore(flags);
- return;
- }
-
-#ifdef SERIAL_DEBUG_OPEN
- printk("[%d] rs_close ttyS%d, count = %d\n", current->pid,
- info->line, info->count);
-#endif
- if ((tty->count == 1) && (info->port.count != 1)) {
- /*
- * Uh, oh. tty->count is 1, which means that the tty
- * structure will be freed. Info->count should always
- * be one in these conditions. If it's greater than
- * one, we've got real problems, since it means the
- * serial port won't be shutdown.
- */
- printk(KERN_ERR
- "rs_close: bad serial port count; tty->count is 1, "
- "info->count is %d\n", info->port.count);
- info->port.count = 1;
- }
- if (--info->port.count < 0) {
- printk(KERN_ERR "rs_close: bad serial port count for ttyS%d: %d\n",
- info->line, info->port.count);
- info->port.count = 0;
- }
- if (info->port.count) {
- local_irq_restore(flags);
- return;
- }
- /*
- * Now we wait for the transmit buffer to clear; and we notify
- * the line discipline to only process XON/XOFF characters.
- */
- tty->closing = 1;
- if (info->port.closing_wait != ASYNC_CLOSING_WAIT_NONE)
- tty_wait_until_sent(tty, info->port.closing_wait);
- /*
- * At this point we stop accepting input. To do this, we
- * disable the serial receiver and the DMA receive interrupt.
- */
-#ifdef SERIAL_HANDLE_EARLY_ERRORS
- e100_disable_serial_data_irq(info);
-#endif
-
- e100_disable_rx(info);
- e100_disable_rx_irq(info);
-
- if (tty_port_initialized(&info->port)) {
- /*
- * Before we drop DTR, make sure the UART transmitter
- * has completely drained; this is especially
- * important as we have a transmit FIFO!
- */
- rs_wait_until_sent(tty, HZ);
- }
-
- shutdown(info);
- rs_flush_buffer(tty);
- tty_ldisc_flush(tty);
- tty->closing = 0;
- info->event = 0;
- info->port.tty = NULL;
- if (info->port.blocked_open) {
- if (info->port.close_delay)
- schedule_timeout_interruptible(info->port.close_delay);
- wake_up_interruptible(&info->port.open_wait);
- }
- local_irq_restore(flags);
- tty_port_set_active(&info->port, 0);
-
- /* port closed */
-
-#if defined(CONFIG_ETRAX_RS485)
- if (info->rs485.flags & SER_RS485_ENABLED) {
- info->rs485.flags &= ~(SER_RS485_ENABLED);
-#if defined(CONFIG_ETRAX_RS485_ON_PA)
- *R_PORT_PA_DATA = port_pa_data_shadow &= ~(1 << rs485_pa_bit);
-#endif
- }
-#endif
-
- /*
- * Release any allocated DMA irq's.
- */
- if (info->dma_in_enabled) {
- free_irq(info->dma_in_irq_nbr, info);
- cris_free_dma(info->dma_in_nbr, info->dma_in_irq_description);
- info->uses_dma_in = 0;
-#ifdef SERIAL_DEBUG_OPEN
- printk(KERN_DEBUG "DMA irq '%s' freed\n",
- info->dma_in_irq_description);
-#endif
- }
- if (info->dma_out_enabled) {
- free_irq(info->dma_out_irq_nbr, info);
- cris_free_dma(info->dma_out_nbr, info->dma_out_irq_description);
- info->uses_dma_out = 0;
-#ifdef SERIAL_DEBUG_OPEN
- printk(KERN_DEBUG "DMA irq '%s' freed\n",
- info->dma_out_irq_description);
-#endif
- }
-}
-
-/*
- * rs_wait_until_sent() --- wait until the transmitter is empty
- */
-static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
-{
- unsigned long orig_jiffies;
- struct e100_serial *info = (struct e100_serial *)tty->driver_data;
- unsigned long curr_time = jiffies;
- unsigned long curr_time_usec = GET_JIFFIES_USEC();
- long elapsed_usec =
- (curr_time - info->last_tx_active) * (1000000/HZ) +
- curr_time_usec - info->last_tx_active_usec;
-
- /*
- * Check R_DMA_CHx_STATUS bit 0-6=number of available bytes in FIFO
- * R_DMA_CHx_HWSW bit 31-16=nbr of bytes left in DMA buffer (0=64k)
- */
- orig_jiffies = jiffies;
- while (info->xmit.head != info->xmit.tail || /* More in send queue */
- (*info->ostatusadr & 0x007f) || /* more in FIFO */
- (elapsed_usec < 2*info->char_time_usec)) {
- schedule_timeout_interruptible(1);
- if (signal_pending(current))
- break;
- if (timeout && time_after(jiffies, orig_jiffies + timeout))
- break;
- curr_time = jiffies;
- curr_time_usec = GET_JIFFIES_USEC();
- elapsed_usec =
- (curr_time - info->last_tx_active) * (1000000/HZ) +
- curr_time_usec - info->last_tx_active_usec;
- }
- set_current_state(TASK_RUNNING);
-}
-
-/*
- * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
- */
-void
-rs_hangup(struct tty_struct *tty)
-{
- struct e100_serial * info = (struct e100_serial *)tty->driver_data;
-
- rs_flush_buffer(tty);
- shutdown(info);
- info->event = 0;
- info->port.count = 0;
- tty_port_set_active(&info->port, 0);
- info->port.tty = NULL;
- wake_up_interruptible(&info->port.open_wait);
-}
-
-/*
- * ------------------------------------------------------------
- * rs_open() and friends
- * ------------------------------------------------------------
- */
-static int
-block_til_ready(struct tty_struct *tty, struct file * filp,
- struct e100_serial *info)
-{
- DECLARE_WAITQUEUE(wait, current);
- unsigned long flags;
- int retval;
- int do_clocal = 0;
-
- /*
- * If non-blocking mode is set, or the port is not enabled,
- * then make the check up front and then exit.
- */
- if ((filp->f_flags & O_NONBLOCK) || tty_io_error(tty)) {
- tty_port_set_active(&info->port, 1);
- return 0;
- }
-
- if (C_CLOCAL(tty))
- do_clocal = 1;
-
- /*
- * Block waiting for the carrier detect and the line to become
- * free (i.e., not in use by the callout). While we are in
- * this loop, info->port.count is dropped by one, so that
- * rs_close() knows when to free things. We restore it upon
- * exit, either normal or abnormal.
- */
- retval = 0;
- add_wait_queue(&info->port.open_wait, &wait);
-#ifdef SERIAL_DEBUG_OPEN
- printk("block_til_ready before block: ttyS%d, count = %d\n",
- info->line, info->port.count);
-#endif
- local_irq_save(flags);
- info->port.count--;
- local_irq_restore(flags);
- info->port.blocked_open++;
- while (1) {
- local_irq_save(flags);
- /* assert RTS and DTR */
- e100_rts(info, 1);
- e100_dtr(info, 1);
- local_irq_restore(flags);
- set_current_state(TASK_INTERRUPTIBLE);
- if (tty_hung_up_p(filp) || !tty_port_initialized(&info->port)) {
-#ifdef SERIAL_DO_RESTART
- if (info->port.flags & ASYNC_HUP_NOTIFY)
- retval = -EAGAIN;
- else
- retval = -ERESTARTSYS;
-#else
- retval = -EAGAIN;
-#endif
- break;
- }
- if (do_clocal)
- /* && (do_clocal || DCD_IS_ASSERTED) */
- break;
- if (signal_pending(current)) {
- retval = -ERESTARTSYS;
- break;
- }
-#ifdef SERIAL_DEBUG_OPEN
- printk("block_til_ready blocking: ttyS%d, count = %d\n",
- info->line, info->port.count);
-#endif
- tty_unlock(tty);
- schedule();
- tty_lock(tty);
- }
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&info->port.open_wait, &wait);
- if (!tty_hung_up_p(filp))
- info->port.count++;
- info->port.blocked_open--;
-#ifdef SERIAL_DEBUG_OPEN
- printk("block_til_ready after blocking: ttyS%d, count = %d\n",
- info->line, info->port.count);
-#endif
- if (retval)
- return retval;
- tty_port_set_active(&info->port, 1);
- return 0;
-}
-
-static void
-deinit_port(struct e100_serial *info)
-{
- if (info->dma_out_enabled) {
- cris_free_dma(info->dma_out_nbr, info->dma_out_irq_description);
- free_irq(info->dma_out_irq_nbr, info);
- }
- if (info->dma_in_enabled) {
- cris_free_dma(info->dma_in_nbr, info->dma_in_irq_description);
- free_irq(info->dma_in_irq_nbr, info);
- }
-}
-
-/*
- * This routine is called whenever a serial port is opened.
- * It performs the serial-specific initialization for the tty structure.
- */
-static int
-rs_open(struct tty_struct *tty, struct file * filp)
-{
- struct e100_serial *info;
- int retval;
- int allocated_resources = 0;
-
- info = rs_table + tty->index;
- if (!info->enabled)
- return -ENODEV;
-
-#ifdef SERIAL_DEBUG_OPEN
- printk("[%d] rs_open %s, count = %d\n", current->pid, tty->name,
- info->port.count);
-#endif
-
- info->port.count++;
- tty->driver_data = info;
- info->port.tty = tty;
-
- info->port.low_latency = !!(info->port.flags & ASYNC_LOW_LATENCY);
-
- /*
- * If DMA is enabled try to allocate the irq's.
- */
- if (info->port.count == 1) {
- allocated_resources = 1;
- if (info->dma_in_enabled) {
- if (request_irq(info->dma_in_irq_nbr,
- rec_interrupt,
- info->dma_in_irq_flags,
- info->dma_in_irq_description,
- info)) {
- printk(KERN_WARNING "DMA irq '%s' busy; "
- "falling back to non-DMA mode\n",
- info->dma_in_irq_description);
- /* Make sure we never try to use DMA in */
- /* for the port again. */
- info->dma_in_enabled = 0;
- } else if (cris_request_dma(info->dma_in_nbr,
- info->dma_in_irq_description,
- DMA_VERBOSE_ON_ERROR,
- info->dma_owner)) {
- free_irq(info->dma_in_irq_nbr, info);
- printk(KERN_WARNING "DMA '%s' busy; "
- "falling back to non-DMA mode\n",
- info->dma_in_irq_description);
- /* Make sure we never try to use DMA in */
- /* for the port again. */
- info->dma_in_enabled = 0;
- }
-#ifdef SERIAL_DEBUG_OPEN
- else
- printk(KERN_DEBUG "DMA irq '%s' allocated\n",
- info->dma_in_irq_description);
-#endif
- }
- if (info->dma_out_enabled) {
- if (request_irq(info->dma_out_irq_nbr,
- tr_interrupt,
- info->dma_out_irq_flags,
- info->dma_out_irq_description,
- info)) {
- printk(KERN_WARNING "DMA irq '%s' busy; "
- "falling back to non-DMA mode\n",
- info->dma_out_irq_description);
- /* Make sure we never try to use DMA out */
- /* for the port again. */
- info->dma_out_enabled = 0;
- } else if (cris_request_dma(info->dma_out_nbr,
- info->dma_out_irq_description,
- DMA_VERBOSE_ON_ERROR,
- info->dma_owner)) {
- free_irq(info->dma_out_irq_nbr, info);
- printk(KERN_WARNING "DMA '%s' busy; "
- "falling back to non-DMA mode\n",
- info->dma_out_irq_description);
- /* Make sure we never try to use DMA out */
- /* for the port again. */
- info->dma_out_enabled = 0;
- }
-#ifdef SERIAL_DEBUG_OPEN
- else
- printk(KERN_DEBUG "DMA irq '%s' allocated\n",
- info->dma_out_irq_description);
-#endif
- }
- }
-
- /*
- * Start up the serial port
- */
-
- retval = startup(info);
- if (retval) {
- if (allocated_resources)
- deinit_port(info);
-
- /* FIXME Decrease count info->port.count here too? */
- return retval;
- }
-
-
- retval = block_til_ready(tty, filp, info);
- if (retval) {
-#ifdef SERIAL_DEBUG_OPEN
- printk("rs_open returning after block_til_ready with %d\n",
- retval);
-#endif
- if (allocated_resources)
- deinit_port(info);
-
- return retval;
- }
-
-#ifdef SERIAL_DEBUG_OPEN
- printk("rs_open ttyS%d successful...\n", info->line);
-#endif
- DLOG_INT_TRIG( log_int_pos = 0);
-
- DFLIP( if (info->line == SERIAL_DEBUG_LINE) {
- info->icount.rx = 0;
- } );
-
- return 0;
-}
-
-#ifdef CONFIG_PROC_FS
-/*
- * /proc fs routines....
- */
-
-static void seq_line_info(struct seq_file *m, struct e100_serial *info)
-{
- unsigned long tmp;
-
- seq_printf(m, "%d: uart:E100 port:%lX irq:%d",
- info->line, (unsigned long)info->ioport, info->irq);
-
- if (!info->ioport || (info->type == PORT_UNKNOWN)) {
- seq_printf(m, "\n");
- return;
- }
-
- seq_printf(m, " baud:%d", info->baud);
- seq_printf(m, " tx:%lu rx:%lu",
- (unsigned long)info->icount.tx,
- (unsigned long)info->icount.rx);
- tmp = CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
- if (tmp)
- seq_printf(m, " tx_pend:%lu/%lu",
- (unsigned long)tmp,
- (unsigned long)SERIAL_XMIT_SIZE);
-
- seq_printf(m, " rx_pend:%lu/%lu",
- (unsigned long)info->recv_cnt,
- (unsigned long)info->max_recv_cnt);
-
-#if 1
- if (info->port.tty) {
- if (info->port.tty->stopped)
- seq_printf(m, " stopped:%i",
- (int)info->port.tty->stopped);
- }
-
- {
- unsigned char rstat = info->ioport[REG_STATUS];
- if (rstat & IO_MASK(R_SERIAL0_STATUS, xoff_detect))
- seq_printf(m, " xoff_detect:1");
- }
-
-#endif
-
- if (info->icount.frame)
- seq_printf(m, " fe:%lu", (unsigned long)info->icount.frame);
-
- if (info->icount.parity)
- seq_printf(m, " pe:%lu", (unsigned long)info->icount.parity);
-
- if (info->icount.brk)
- seq_printf(m, " brk:%lu", (unsigned long)info->icount.brk);
-
- if (info->icount.overrun)
- seq_printf(m, " oe:%lu", (unsigned long)info->icount.overrun);
-
- /*
- * Last thing is the RS-232 status lines
- */
- if (!E100_RTS_GET(info))
- seq_puts(m, "|RTS");
- if (!E100_CTS_GET(info))
- seq_puts(m, "|CTS");
- if (!E100_DTR_GET(info))
- seq_puts(m, "|DTR");
- if (!E100_DSR_GET(info))
- seq_puts(m, "|DSR");
- if (!E100_CD_GET(info))
- seq_puts(m, "|CD");
- if (!E100_RI_GET(info))
- seq_puts(m, "|RI");
- seq_puts(m, "\n");
-}
-
-
-static int crisv10_proc_show(struct seq_file *m, void *v)
-{
- int i;
-
- seq_printf(m, "serinfo:1.0 driver:%s\n", serial_version);
-
- for (i = 0; i < NR_PORTS; i++) {
- if (!rs_table[i].enabled)
- continue;
- seq_line_info(m, &rs_table[i]);
- }
-#ifdef DEBUG_LOG_INCLUDED
- for (i = 0; i < debug_log_pos; i++) {
- seq_printf(m, "%-4i %lu.%lu ",
- i, debug_log[i].time,
- timer_data_to_ns(debug_log[i].timer_data));
- seq_printf(m, debug_log[i].string, debug_log[i].value);
- }
- seq_printf(m, "debug_log %i/%i\n", i, DEBUG_LOG_SIZE);
- debug_log_pos = 0;
-#endif
- return 0;
-}
-
-static int crisv10_proc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, crisv10_proc_show, NULL);
-}
-
-static const struct file_operations crisv10_proc_fops = {
- .owner = THIS_MODULE,
- .open = crisv10_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-#endif
-
-
-/* Finally, routines used to initialize the serial driver. */
-
-static void show_serial_version(void)
-{
- printk(KERN_INFO
- "ETRAX 100LX serial-driver %s, "
- "(c) 2000-2004 Axis Communications AB\r\n",
- &serial_version[11]); /* "$Revision: x.yy" */
-}
-
-/* rs_init inits the driver at boot (using the initcall chain) */
-
-static const struct tty_operations rs_ops = {
- .open = rs_open,
- .close = rs_close,
- .write = rs_write,
- .flush_chars = rs_flush_chars,
- .write_room = rs_write_room,
- .chars_in_buffer = rs_chars_in_buffer,
- .flush_buffer = rs_flush_buffer,
- .ioctl = rs_ioctl,
- .throttle = rs_throttle,
- .unthrottle = rs_unthrottle,
- .set_termios = rs_set_termios,
- .stop = rs_stop,
- .start = rs_start,
- .hangup = rs_hangup,
- .break_ctl = rs_break,
- .send_xchar = rs_send_xchar,
- .wait_until_sent = rs_wait_until_sent,
- .tiocmget = rs_tiocmget,
- .tiocmset = rs_tiocmset,
-#ifdef CONFIG_PROC_FS
- .proc_fops = &crisv10_proc_fops,
-#endif
-};
-
-static int __init rs_init(void)
-{
- int i;
- struct e100_serial *info;
- struct tty_driver *driver = alloc_tty_driver(NR_PORTS);
-
- if (!driver)
- return -ENOMEM;
-
- show_serial_version();
-
- /* Setup the timed flush handler system */
-
-#if !defined(CONFIG_ETRAX_SERIAL_FAST_TIMER)
- timer_setup(&flush_timer, timed_flush_handler, 0);
- mod_timer(&flush_timer, jiffies + 5);
-#endif
-
-#if defined(CONFIG_ETRAX_RS485)
-#if defined(CONFIG_ETRAX_RS485_ON_PA)
- if (cris_io_interface_allocate_pins(if_serial_0, 'a', rs485_pa_bit,
- rs485_pa_bit)) {
- printk(KERN_ERR "ETRAX100LX serial: Could not allocate "
- "RS485 pin\n");
- put_tty_driver(driver);
- return -EBUSY;
- }
-#endif
-#endif
-
- /* Initialize the tty_driver structure */
-
- driver->driver_name = "serial";
- driver->name = "ttyS";
- driver->major = TTY_MAJOR;
- driver->minor_start = 64;
- driver->type = TTY_DRIVER_TYPE_SERIAL;
- driver->subtype = SERIAL_TYPE_NORMAL;
- driver->init_termios = tty_std_termios;
- driver->init_termios.c_cflag =
- B115200 | CS8 | CREAD | HUPCL | CLOCAL; /* is normally B9600 default... */
- driver->init_termios.c_ispeed = 115200;
- driver->init_termios.c_ospeed = 115200;
- driver->flags = TTY_DRIVER_REAL_RAW;
-
- tty_set_operations(driver, &rs_ops);
- serial_driver = driver;
-
- /* do some initializing for the separate ports */
- for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
- if (info->enabled) {
- if (cris_request_io_interface(info->io_if,
- info->io_if_description)) {
- printk(KERN_ERR "ETRAX100LX async serial: "
- "Could not allocate IO pins for "
- "%s, port %d\n",
- info->io_if_description, i);
- info->enabled = 0;
- }
- }
- tty_port_init(&info->port);
- info->uses_dma_in = 0;
- info->uses_dma_out = 0;
- info->line = i;
- info->port.tty = NULL;
- info->type = PORT_ETRAX;
- info->tr_running = 0;
- info->forced_eop = 0;
- info->baud_base = DEF_BAUD_BASE;
- info->custom_divisor = 0;
- info->x_char = 0;
- info->event = 0;
- info->xmit.buf = NULL;
- info->xmit.tail = info->xmit.head = 0;
- info->first_recv_buffer = info->last_recv_buffer = NULL;
- info->recv_cnt = info->max_recv_cnt = 0;
- info->last_tx_active_usec = 0;
- info->last_tx_active = 0;
-
-#if defined(CONFIG_ETRAX_RS485)
- /* Set sane defaults */
- info->rs485.flags &= ~(SER_RS485_RTS_ON_SEND);
- info->rs485.flags |= SER_RS485_RTS_AFTER_SEND;
- info->rs485.delay_rts_before_send = 0;
- info->rs485.flags &= ~(SER_RS485_ENABLED);
-#endif
- INIT_WORK(&info->work, do_softint);
-
- if (info->enabled) {
- printk(KERN_INFO "%s%d at %p is a builtin UART with DMA\n",
- serial_driver->name, info->line, info->ioport);
- }
- tty_port_link_device(&info->port, driver, i);
- }
-
- if (tty_register_driver(driver))
- panic("Couldn't register serial driver\n");
-
-#ifdef CONFIG_ETRAX_FAST_TIMER
-#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
- memset(fast_timers, 0, sizeof(fast_timers));
-#endif
-#ifdef CONFIG_ETRAX_RS485
- memset(fast_timers_rs485, 0, sizeof(fast_timers_rs485));
-#endif
- fast_timer_init();
-#endif
-
-#ifndef CONFIG_ETRAX_KGDB
- /* Not needed in simulator. May only complicate stuff. */
- /* hook the irq's for DMA channel 6 and 7, serial output and input, and some more... */
-
- if (request_irq(SERIAL_IRQ_NBR, ser_interrupt,
- IRQF_SHARED, "serial ", driver))
- panic("%s: Failed to request irq8", __func__);
-
-#endif
-
- return 0;
-}
-
-/* this makes sure that rs_init is called during kernel boot */
-device_initcall(rs_init);
diff --git a/drivers/tty/serial/crisv10.h b/drivers/tty/serial/crisv10.h
deleted file mode 100644
index 79ba2bc95d3d..000000000000
--- a/drivers/tty/serial/crisv10.h
+++ /dev/null
@@ -1,133 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * serial.h: Arch-dep definitions for the Etrax100 serial driver.
- *
- * Copyright (C) 1998-2007 Axis Communications AB
- */
-
-#ifndef _ETRAX_SERIAL_H
-#define _ETRAX_SERIAL_H
-
-#include <linux/circ_buf.h>
-#include <asm/termios.h>
-#include <asm/dma.h>
-#include <arch/io_interface_mux.h>
-
-/* Software state per channel */
-
-#ifdef __KERNEL__
-/*
- * This is our internal structure for each serial port's state.
- *
- * Many fields are paralleled by the structure used by the serial_struct
- * structure.
- *
- * For definitions of the flags field, see tty.h
- */
-
-#define SERIAL_RECV_DESCRIPTORS 8
-
-struct etrax_recv_buffer {
- struct etrax_recv_buffer *next;
- unsigned short length;
- unsigned char error;
- unsigned char pad;
-
- unsigned char buffer[0];
-};
-
-struct e100_serial {
- struct tty_port port;
- int baud;
- volatile u8 *ioport; /* R_SERIALx_CTRL */
- u32 irq; /* bitnr in R_IRQ_MASK2 for dmaX_descr */
-
- /* Output registers */
- volatile u8 *oclrintradr; /* adr to R_DMA_CHx_CLR_INTR */
- volatile u32 *ofirstadr; /* adr to R_DMA_CHx_FIRST */
- volatile u8 *ocmdadr; /* adr to R_DMA_CHx_CMD */
- const volatile u8 *ostatusadr; /* adr to R_DMA_CHx_STATUS */
-
- /* Input registers */
- volatile u8 *iclrintradr; /* adr to R_DMA_CHx_CLR_INTR */
- volatile u32 *ifirstadr; /* adr to R_DMA_CHx_FIRST */
- volatile u8 *icmdadr; /* adr to R_DMA_CHx_CMD */
- volatile u32 *idescradr; /* adr to R_DMA_CHx_DESCR */
-
- u8 rx_ctrl; /* shadow for R_SERIALx_REC_CTRL */
- u8 tx_ctrl; /* shadow for R_SERIALx_TR_CTRL */
- u8 iseteop; /* bit number for R_SET_EOP for the input dma */
- int enabled; /* Set to 1 if the port is enabled in HW config */
-
- u8 dma_out_enabled; /* Set to 1 if DMA should be used */
- u8 dma_in_enabled; /* Set to 1 if DMA should be used */
-
- /* end of fields defined in rs_table[] in .c-file */
- int dma_owner;
- unsigned int dma_in_nbr;
- unsigned int dma_out_nbr;
- unsigned int dma_in_irq_nbr;
- unsigned int dma_out_irq_nbr;
- unsigned long dma_in_irq_flags;
- unsigned long dma_out_irq_flags;
- char *dma_in_irq_description;
- char *dma_out_irq_description;
-
- enum cris_io_interface io_if;
- char *io_if_description;
-
- u8 uses_dma_in; /* Set to 1 if DMA is used */
- u8 uses_dma_out; /* Set to 1 if DMA is used */
- u8 forced_eop; /* a fifo eop has been forced */
- int baud_base; /* For special baudrates */
- int custom_divisor; /* For special baudrates */
- struct etrax_dma_descr tr_descr;
- struct etrax_dma_descr rec_descr[SERIAL_RECV_DESCRIPTORS];
- int cur_rec_descr;
-
- volatile int tr_running; /* 1 if output is running */
-
- int x_char; /* xon/xoff character */
- unsigned long event;
- int line;
- int type; /* PORT_ETRAX */
- struct circ_buf xmit;
- struct etrax_recv_buffer *first_recv_buffer;
- struct etrax_recv_buffer *last_recv_buffer;
- unsigned int recv_cnt;
- unsigned int max_recv_cnt;
-
- struct work_struct work;
- struct async_icount icount; /* error-statistics etc.*/
-
- unsigned long char_time_usec; /* The time for 1 char, in usecs */
- unsigned long flush_time_usec; /* How often we should flush */
- unsigned long last_tx_active_usec; /* Last tx usec in the jiffies */
- unsigned long last_tx_active; /* Last tx time in jiffies */
- unsigned long last_rx_active_usec; /* Last rx usec in the jiffies */
- unsigned long last_rx_active; /* Last rx time in jiffies */
-
- int break_detected_cnt;
- int errorcode;
-
-#ifdef CONFIG_ETRAX_RS485
- struct serial_rs485 rs485; /* RS-485 support */
-#endif
-};
-
-/* this PORT is not in the standard serial.h. it's not actually used for
- * anything since we only have one type of async serial-port anyway in this
- * system.
- */
-
-#define PORT_ETRAX 1
-
-/*
- * Events are used to schedule things to happen at timer-interrupt
- * time, instead of at rs interrupt time.
- */
-#define RS_EVENT_WRITE_WAKEUP 0
-
-#endif /* __KERNEL__ */
-
-#endif /* !_ETRAX_SERIAL_H */
diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c
index a24278380fec..22683393a0f2 100644
--- a/drivers/tty/serial/earlycon.c
+++ b/drivers/tty/serial/earlycon.c
@@ -169,7 +169,7 @@ static int __init register_earlycon(char *buf, const struct earlycon_id *match)
*/
int __init setup_earlycon(char *buf)
{
- const struct earlycon_id *match;
+ const struct earlycon_id **p_match;
if (!buf || !buf[0])
return -EINVAL;
@@ -177,7 +177,9 @@ int __init setup_earlycon(char *buf)
if (early_con.flags & CON_ENABLED)
return -EALREADY;
- for (match = __earlycon_table; match < __earlycon_table_end; match++) {
+ for (p_match = __earlycon_table; p_match < __earlycon_table_end;
+ p_match++) {
+ const struct earlycon_id *match = *p_match;
size_t len = strlen(match->name);
if (strncmp(buf, match->name, len))
diff --git a/drivers/tty/serial/etraxfs-uart.c b/drivers/tty/serial/etraxfs-uart.c
deleted file mode 100644
index 24bf6bfb29b4..000000000000
--- a/drivers/tty/serial/etraxfs-uart.c
+++ /dev/null
@@ -1,960 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/console.h>
-#include <linux/platform_device.h>
-#include <linux/serial_core.h>
-#include <linux/tty_flip.h>
-#include <linux/of.h>
-#include <linux/gpio.h>
-#include <linux/of_irq.h>
-#include <linux/of_address.h>
-#include <hwregs/ser_defs.h>
-
-#include "serial_mctrl_gpio.h"
-
-#define DRV_NAME "etraxfs-uart"
-#define UART_NR CONFIG_ETRAX_SERIAL_PORTS
-
-#define MODIFY_REG(instance, reg, var) \
- do { \
- if (REG_RD_INT(ser, instance, reg) != \
- REG_TYPE_CONV(int, reg_ser_##reg, var)) \
- REG_WR(ser, instance, reg, var); \
- } while (0)
-
-struct uart_cris_port {
- struct uart_port port;
-
- int initialized;
- int irq;
-
- void __iomem *regi_ser;
-
- struct mctrl_gpios *gpios;
-
- int write_ongoing;
-};
-
-static struct uart_driver etraxfs_uart_driver;
-static struct uart_port *console_port;
-static int console_baud = 115200;
-static struct uart_cris_port *etraxfs_uart_ports[UART_NR];
-
-static void cris_serial_port_init(struct uart_port *port, int line);
-static void etraxfs_uart_stop_rx(struct uart_port *port);
-static inline void etraxfs_uart_start_tx_bottom(struct uart_port *port);
-
-#ifdef CONFIG_SERIAL_ETRAXFS_CONSOLE
-static void
-cris_console_write(struct console *co, const char *s, unsigned int count)
-{
- struct uart_cris_port *up;
- int i;
- reg_ser_r_stat_din stat;
- reg_ser_rw_tr_dma_en tr_dma_en, old;
-
- up = etraxfs_uart_ports[co->index];
-
- if (!up)
- return;
-
- /* Switch to manual mode. */
- tr_dma_en = old = REG_RD(ser, up->regi_ser, rw_tr_dma_en);
- if (tr_dma_en.en == regk_ser_yes) {
- tr_dma_en.en = regk_ser_no;
- REG_WR(ser, up->regi_ser, rw_tr_dma_en, tr_dma_en);
- }
-
- /* Send data. */
- for (i = 0; i < count; i++) {
- /* LF -> CRLF */
- if (s[i] == '\n') {
- do {
- stat = REG_RD(ser, up->regi_ser, r_stat_din);
- } while (!stat.tr_rdy);
- REG_WR_INT(ser, up->regi_ser, rw_dout, '\r');
- }
- /* Wait until transmitter is ready and send. */
- do {
- stat = REG_RD(ser, up->regi_ser, r_stat_din);
- } while (!stat.tr_rdy);
- REG_WR_INT(ser, up->regi_ser, rw_dout, s[i]);
- }
-
- /* Restore mode. */
- if (tr_dma_en.en != old.en)
- REG_WR(ser, up->regi_ser, rw_tr_dma_en, old);
-}
-
-static int __init
-cris_console_setup(struct console *co, char *options)
-{
- struct uart_port *port;
- int baud = 115200;
- int bits = 8;
- int parity = 'n';
- int flow = 'n';
-
- if (co->index < 0 || co->index >= UART_NR)
- co->index = 0;
- port = &etraxfs_uart_ports[co->index]->port;
- console_port = port;
-
- co->flags |= CON_CONSDEV;
-
- if (options)
- uart_parse_options(options, &baud, &parity, &bits, &flow);
- console_baud = baud;
- cris_serial_port_init(port, co->index);
- uart_set_options(port, co, baud, parity, bits, flow);
-
- return 0;
-}
-
-static struct console cris_console = {
- .name = "ttyS",
- .write = cris_console_write,
- .device = uart_console_device,
- .setup = cris_console_setup,
- .flags = CON_PRINTBUFFER,
- .index = -1,
- .data = &etraxfs_uart_driver,
-};
-#endif /* CONFIG_SERIAL_ETRAXFS_CONSOLE */
-
-static struct uart_driver etraxfs_uart_driver = {
- .owner = THIS_MODULE,
- .driver_name = "serial",
- .dev_name = "ttyS",
- .major = TTY_MAJOR,
- .minor = 64,
- .nr = UART_NR,
-#ifdef CONFIG_SERIAL_ETRAXFS_CONSOLE
- .cons = &cris_console,
-#endif /* CONFIG_SERIAL_ETRAXFS_CONSOLE */
-};
-
-static inline int crisv32_serial_get_rts(struct uart_cris_port *up)
-{
- void __iomem *regi_ser = up->regi_ser;
- /*
- * Return what the user has controlled rts to or
- * what the pin is? (if auto_rts is used it differs during tx)
- */
- reg_ser_r_stat_din rstat = REG_RD(ser, regi_ser, r_stat_din);
-
- return !(rstat.rts_n == regk_ser_active);
-}
-
-/*
- * A set = 0 means 3.3V on the pin, bitvalue: 0=active, 1=inactive
- * 0=0V , 1=3.3V
- */
-static inline void crisv32_serial_set_rts(struct uart_cris_port *up,
- int set, int force)
-{
- void __iomem *regi_ser = up->regi_ser;
-
- unsigned long flags;
- reg_ser_rw_rec_ctrl rec_ctrl;
-
- local_irq_save(flags);
- rec_ctrl = REG_RD(ser, regi_ser, rw_rec_ctrl);
-
- if (set)
- rec_ctrl.rts_n = regk_ser_active;
- else
- rec_ctrl.rts_n = regk_ser_inactive;
- REG_WR(ser, regi_ser, rw_rec_ctrl, rec_ctrl);
- local_irq_restore(flags);
-}
-
-static inline int crisv32_serial_get_cts(struct uart_cris_port *up)
-{
- void __iomem *regi_ser = up->regi_ser;
- reg_ser_r_stat_din rstat = REG_RD(ser, regi_ser, r_stat_din);
-
- return (rstat.cts_n == regk_ser_active);
-}
-
-/*
- * Send a single character for XON/XOFF purposes. We do it in this separate
- * function instead of the alternative support port.x_char, in the ...start_tx
- * function, so we don't mix up this case with possibly enabling transmission
- * of queued-up data (in case that's disabled after *receiving* an XOFF or
- * negative CTS). This function is used for both DMA and non-DMA case; see HW
- * docs specifically blessing sending characters manually when DMA for
- * transmission is enabled and running. We may be asked to transmit despite
- * the transmitter being disabled by a ..._stop_tx call so we need to enable
- * it temporarily but restore the state afterwards.
- */
-static void etraxfs_uart_send_xchar(struct uart_port *port, char ch)
-{
- struct uart_cris_port *up = (struct uart_cris_port *)port;
- reg_ser_rw_dout dout = { .data = ch };
- reg_ser_rw_ack_intr ack_intr = { .tr_rdy = regk_ser_yes };
- reg_ser_r_stat_din rstat;
- reg_ser_rw_tr_ctrl prev_tr_ctrl, tr_ctrl;
- void __iomem *regi_ser = up->regi_ser;
- unsigned long flags;
-
- /*
- * Wait for tr_rdy in case a character is already being output. Make
- * sure we have integrity between the register reads and the writes
- * below, but don't busy-wait with interrupts off and the port lock
- * taken.
- */
- spin_lock_irqsave(&port->lock, flags);
- do {
- spin_unlock_irqrestore(&port->lock, flags);
- spin_lock_irqsave(&port->lock, flags);
- prev_tr_ctrl = tr_ctrl = REG_RD(ser, regi_ser, rw_tr_ctrl);
- rstat = REG_RD(ser, regi_ser, r_stat_din);
- } while (!rstat.tr_rdy);
-
- /*
- * Ack an interrupt if one was just issued for the previous character
- * that was output. This is required for non-DMA as the interrupt is
- * used as the only indicator that the transmitter is ready and it
- * isn't while this x_char is being transmitted.
- */
- REG_WR(ser, regi_ser, rw_ack_intr, ack_intr);
-
- /* Enable the transmitter in case it was disabled. */
- tr_ctrl.stop = 0;
- REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl);
-
- /*
- * Finally, send the blessed character; nothing should stop it now,
- * except for an xoff-detected state, which we'll handle below.
- */
- REG_WR(ser, regi_ser, rw_dout, dout);
- up->port.icount.tx++;
-
- /* There might be an xoff state to clear. */
- rstat = REG_RD(ser, up->regi_ser, r_stat_din);
-
- /*
- * Clear any xoff state that *may* have been there to
- * inhibit transmission of the character.
- */
- if (rstat.xoff_detect) {
- reg_ser_rw_xoff_clr xoff_clr = { .clr = 1 };
- reg_ser_rw_tr_dma_en tr_dma_en;
-
- REG_WR(ser, regi_ser, rw_xoff_clr, xoff_clr);
- tr_dma_en = REG_RD(ser, regi_ser, rw_tr_dma_en);
-
- /*
- * If we had an xoff state but cleared it, instead sneak in a
- * disabled state for the transmitter, after the character we
- * sent. Thus we keep the port disabled, just as if the xoff
- * state was still in effect (or actually, as if stop_tx had
- * been called, as we stop DMA too).
- */
- prev_tr_ctrl.stop = 1;
-
- tr_dma_en.en = 0;
- REG_WR(ser, regi_ser, rw_tr_dma_en, tr_dma_en);
- }
-
- /* Restore "previous" enabled/disabled state of the transmitter. */
- REG_WR(ser, regi_ser, rw_tr_ctrl, prev_tr_ctrl);
-
- spin_unlock_irqrestore(&port->lock, flags);
-}
-
-/*
- * Do not spin_lock_irqsave or disable interrupts by other means here; it's
- * already done by the caller.
- */
-static void etraxfs_uart_start_tx(struct uart_port *port)
-{
- struct uart_cris_port *up = (struct uart_cris_port *)port;
-
- /* we have already done below if a write is ongoing */
- if (up->write_ongoing)
- return;
-
- /* Signal that write is ongoing */
- up->write_ongoing = 1;
-
- etraxfs_uart_start_tx_bottom(port);
-}
-
-static inline void etraxfs_uart_start_tx_bottom(struct uart_port *port)
-{
- struct uart_cris_port *up = (struct uart_cris_port *)port;
- void __iomem *regi_ser = up->regi_ser;
- reg_ser_rw_tr_ctrl tr_ctrl;
- reg_ser_rw_intr_mask intr_mask;
-
- tr_ctrl = REG_RD(ser, regi_ser, rw_tr_ctrl);
- tr_ctrl.stop = regk_ser_no;
- REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl);
- intr_mask = REG_RD(ser, regi_ser, rw_intr_mask);
- intr_mask.tr_rdy = regk_ser_yes;
- REG_WR(ser, regi_ser, rw_intr_mask, intr_mask);
-}
-
-/*
- * This function handles both the DMA and non-DMA case by ordering the
- * transmitter to stop of after the current character. We don't need to wait
- * for any such character to be completely transmitted; we do that where it
- * matters, like in etraxfs_uart_set_termios. Don't busy-wait here; see
- * Documentation/serial/driver: this function is called within
- * spin_lock_irq{,save} and thus separate ones would be disastrous (when SMP).
- * There's no documented need to set the txd pin to any particular value;
- * break setting is controlled solely by etraxfs_uart_break_ctl.
- */
-static void etraxfs_uart_stop_tx(struct uart_port *port)
-{
- struct uart_cris_port *up = (struct uart_cris_port *)port;
- void __iomem *regi_ser = up->regi_ser;
- reg_ser_rw_tr_ctrl tr_ctrl;
- reg_ser_rw_intr_mask intr_mask;
- reg_ser_rw_tr_dma_en tr_dma_en = {0};
- reg_ser_rw_xoff_clr xoff_clr = {0};
-
- /*
- * For the non-DMA case, we'd get a tr_rdy interrupt that we're not
- * interested in as we're not transmitting any characters. For the
- * DMA case, that interrupt is already turned off, but no reason to
- * waste code on conditionals here.
- */
- intr_mask = REG_RD(ser, regi_ser, rw_intr_mask);
- intr_mask.tr_rdy = regk_ser_no;
- REG_WR(ser, regi_ser, rw_intr_mask, intr_mask);
-
- tr_ctrl = REG_RD(ser, regi_ser, rw_tr_ctrl);
- tr_ctrl.stop = 1;
- REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl);
-
- /*
- * Always clear possible hardware xoff-detected state here, no need to
- * unnecessary consider mctrl settings and when they change. We clear
- * it here rather than in start_tx: both functions are called as the
- * effect of XOFF processing, but start_tx is also called when upper
- * levels tell the driver that there are more characters to send, so
- * avoid adding code there.
- */
- xoff_clr.clr = 1;
- REG_WR(ser, regi_ser, rw_xoff_clr, xoff_clr);
-
- /*
- * Disable transmitter DMA, so that if we're in XON/XOFF, we can send
- * those single characters without also giving go-ahead for queued up
- * DMA data.
- */
- tr_dma_en.en = 0;
- REG_WR(ser, regi_ser, rw_tr_dma_en, tr_dma_en);
-
- /*
- * Make sure that write_ongoing is reset when stopping tx.
- */
- up->write_ongoing = 0;
-}
-
-static void etraxfs_uart_stop_rx(struct uart_port *port)
-{
- struct uart_cris_port *up = (struct uart_cris_port *)port;
- void __iomem *regi_ser = up->regi_ser;
- reg_ser_rw_rec_ctrl rec_ctrl = REG_RD(ser, regi_ser, rw_rec_ctrl);
-
- rec_ctrl.en = regk_ser_no;
- REG_WR(ser, regi_ser, rw_rec_ctrl, rec_ctrl);
-}
-
-static unsigned int etraxfs_uart_tx_empty(struct uart_port *port)
-{
- struct uart_cris_port *up = (struct uart_cris_port *)port;
- unsigned long flags;
- unsigned int ret;
- reg_ser_r_stat_din rstat = {0};
-
- spin_lock_irqsave(&up->port.lock, flags);
-
- rstat = REG_RD(ser, up->regi_ser, r_stat_din);
- ret = rstat.tr_empty ? TIOCSER_TEMT : 0;
-
- spin_unlock_irqrestore(&up->port.lock, flags);
- return ret;
-}
-static unsigned int etraxfs_uart_get_mctrl(struct uart_port *port)
-{
- struct uart_cris_port *up = (struct uart_cris_port *)port;
- unsigned int ret;
-
- ret = 0;
- if (crisv32_serial_get_rts(up))
- ret |= TIOCM_RTS;
- if (crisv32_serial_get_cts(up))
- ret |= TIOCM_CTS;
- return mctrl_gpio_get(up->gpios, &ret);
-}
-
-static void etraxfs_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
-{
- struct uart_cris_port *up = (struct uart_cris_port *)port;
-
- crisv32_serial_set_rts(up, mctrl & TIOCM_RTS ? 1 : 0, 0);
- mctrl_gpio_set(up->gpios, mctrl);
-}
-
-static void etraxfs_uart_break_ctl(struct uart_port *port, int break_state)
-{
- struct uart_cris_port *up = (struct uart_cris_port *)port;
- unsigned long flags;
- reg_ser_rw_tr_ctrl tr_ctrl;
- reg_ser_rw_tr_dma_en tr_dma_en;
- reg_ser_rw_intr_mask intr_mask;
-
- spin_lock_irqsave(&up->port.lock, flags);
- tr_ctrl = REG_RD(ser, up->regi_ser, rw_tr_ctrl);
- tr_dma_en = REG_RD(ser, up->regi_ser, rw_tr_dma_en);
- intr_mask = REG_RD(ser, up->regi_ser, rw_intr_mask);
-
- if (break_state != 0) { /* Send break */
- /*
- * We need to disable DMA (if used) or tr_rdy interrupts if no
- * DMA. No need to make this conditional on use of DMA;
- * disabling will be a no-op for the other mode.
- */
- intr_mask.tr_rdy = regk_ser_no;
- tr_dma_en.en = 0;
-
- /*
- * Stop transmission and set the txd pin to 0 after the
- * current character. The txd setting will take effect after
- * any current transmission has completed.
- */
- tr_ctrl.stop = 1;
- tr_ctrl.txd = 0;
- } else {
- /* Re-enable the serial interrupt. */
- intr_mask.tr_rdy = regk_ser_yes;
-
- tr_ctrl.stop = 0;
- tr_ctrl.txd = 1;
- }
- REG_WR(ser, up->regi_ser, rw_tr_ctrl, tr_ctrl);
- REG_WR(ser, up->regi_ser, rw_tr_dma_en, tr_dma_en);
- REG_WR(ser, up->regi_ser, rw_intr_mask, intr_mask);
-
- spin_unlock_irqrestore(&up->port.lock, flags);
-}
-
-static void
-transmit_chars_no_dma(struct uart_cris_port *up)
-{
- int max_count;
- struct circ_buf *xmit = &up->port.state->xmit;
-
- void __iomem *regi_ser = up->regi_ser;
- reg_ser_r_stat_din rstat;
- reg_ser_rw_ack_intr ack_intr = { .tr_rdy = regk_ser_yes };
-
- if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
- /* No more to send, so disable the interrupt. */
- reg_ser_rw_intr_mask intr_mask;
-
- intr_mask = REG_RD(ser, regi_ser, rw_intr_mask);
- intr_mask.tr_rdy = 0;
- intr_mask.tr_empty = 0;
- REG_WR(ser, regi_ser, rw_intr_mask, intr_mask);
- up->write_ongoing = 0;
- return;
- }
-
- /* If the serport is fast, we send up to max_count bytes before
- exiting the loop. */
- max_count = 64;
- do {
- reg_ser_rw_dout dout = { .data = xmit->buf[xmit->tail] };
-
- REG_WR(ser, regi_ser, rw_dout, dout);
- REG_WR(ser, regi_ser, rw_ack_intr, ack_intr);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1);
- up->port.icount.tx++;
- if (xmit->head == xmit->tail)
- break;
- rstat = REG_RD(ser, regi_ser, r_stat_din);
- } while ((--max_count > 0) && rstat.tr_rdy);
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(&up->port);
-}
-
-static void receive_chars_no_dma(struct uart_cris_port *up)
-{
- reg_ser_rs_stat_din stat_din;
- reg_ser_r_stat_din rstat;
- struct tty_port *port;
- struct uart_icount *icount;
- int max_count = 16;
- char flag;
- reg_ser_rw_ack_intr ack_intr = { 0 };
-
- rstat = REG_RD(ser, up->regi_ser, r_stat_din);
- icount = &up->port.icount;
- port = &up->port.state->port;
-
- do {
- stat_din = REG_RD(ser, up->regi_ser, rs_stat_din);
-
- flag = TTY_NORMAL;
- ack_intr.dav = 1;
- REG_WR(ser, up->regi_ser, rw_ack_intr, ack_intr);
- icount->rx++;
-
- if (stat_din.framing_err | stat_din.par_err | stat_din.orun) {
- if (stat_din.data == 0x00 &&
- stat_din.framing_err) {
- /* Most likely a break. */
- flag = TTY_BREAK;
- icount->brk++;
- } else if (stat_din.par_err) {
- flag = TTY_PARITY;
- icount->parity++;
- } else if (stat_din.orun) {
- flag = TTY_OVERRUN;
- icount->overrun++;
- } else if (stat_din.framing_err) {
- flag = TTY_FRAME;
- icount->frame++;
- }
- }
-
- /*
- * If this becomes important, we probably *could* handle this
- * gracefully by keeping track of the unhandled character.
- */
- if (!tty_insert_flip_char(port, stat_din.data, flag))
- panic("%s: No tty buffer space", __func__);
- rstat = REG_RD(ser, up->regi_ser, r_stat_din);
- } while (rstat.dav && (max_count-- > 0));
- spin_unlock(&up->port.lock);
- tty_flip_buffer_push(port);
- spin_lock(&up->port.lock);
-}
-
-static irqreturn_t
-ser_interrupt(int irq, void *dev_id)
-{
- struct uart_cris_port *up = (struct uart_cris_port *)dev_id;
- void __iomem *regi_ser;
- int handled = 0;
-
- spin_lock(&up->port.lock);
-
- regi_ser = up->regi_ser;
-
- if (regi_ser) {
- reg_ser_r_masked_intr masked_intr;
-
- masked_intr = REG_RD(ser, regi_ser, r_masked_intr);
- /*
- * Check what interrupts are active before taking
- * actions. If DMA is used the interrupt shouldn't
- * be enabled.
- */
- if (masked_intr.dav) {
- receive_chars_no_dma(up);
- handled = 1;
- }
-
- if (masked_intr.tr_rdy) {
- transmit_chars_no_dma(up);
- handled = 1;
- }
- }
- spin_unlock(&up->port.lock);
- return IRQ_RETVAL(handled);
-}
-
-#ifdef CONFIG_CONSOLE_POLL
-static int etraxfs_uart_get_poll_char(struct uart_port *port)
-{
- reg_ser_rs_stat_din stat;
- reg_ser_rw_ack_intr ack_intr = { 0 };
- struct uart_cris_port *up = (struct uart_cris_port *)port;
-
- do {
- stat = REG_RD(ser, up->regi_ser, rs_stat_din);
- } while (!stat.dav);
-
- /* Ack the data_avail interrupt. */
- ack_intr.dav = 1;
- REG_WR(ser, up->regi_ser, rw_ack_intr, ack_intr);
-
- return stat.data;
-}
-
-static void etraxfs_uart_put_poll_char(struct uart_port *port,
- unsigned char c)
-{
- reg_ser_r_stat_din stat;
- struct uart_cris_port *up = (struct uart_cris_port *)port;
-
- do {
- stat = REG_RD(ser, up->regi_ser, r_stat_din);
- } while (!stat.tr_rdy);
- REG_WR_INT(ser, up->regi_ser, rw_dout, c);
-}
-#endif /* CONFIG_CONSOLE_POLL */
-
-static int etraxfs_uart_startup(struct uart_port *port)
-{
- struct uart_cris_port *up = (struct uart_cris_port *)port;
- unsigned long flags;
- reg_ser_rw_intr_mask ser_intr_mask = {0};
-
- ser_intr_mask.dav = regk_ser_yes;
-
- if (request_irq(etraxfs_uart_ports[port->line]->irq, ser_interrupt,
- 0, DRV_NAME, etraxfs_uart_ports[port->line]))
- panic("irq ser%d", port->line);
-
- spin_lock_irqsave(&up->port.lock, flags);
-
- REG_WR(ser, up->regi_ser, rw_intr_mask, ser_intr_mask);
-
- etraxfs_uart_set_mctrl(&up->port, up->port.mctrl);
-
- spin_unlock_irqrestore(&up->port.lock, flags);
-
- return 0;
-}
-
-static void etraxfs_uart_shutdown(struct uart_port *port)
-{
- struct uart_cris_port *up = (struct uart_cris_port *)port;
- unsigned long flags;
-
- spin_lock_irqsave(&up->port.lock, flags);
-
- etraxfs_uart_stop_tx(port);
- etraxfs_uart_stop_rx(port);
-
- free_irq(etraxfs_uart_ports[port->line]->irq,
- etraxfs_uart_ports[port->line]);
-
- etraxfs_uart_set_mctrl(&up->port, up->port.mctrl);
-
- spin_unlock_irqrestore(&up->port.lock, flags);
-
-}
-
-static void
-etraxfs_uart_set_termios(struct uart_port *port, struct ktermios *termios,
- struct ktermios *old)
-{
- struct uart_cris_port *up = (struct uart_cris_port *)port;
- unsigned long flags;
- reg_ser_rw_xoff xoff;
- reg_ser_rw_xoff_clr xoff_clr = {0};
- reg_ser_rw_tr_ctrl tx_ctrl = {0};
- reg_ser_rw_tr_dma_en tx_dma_en = {0};
- reg_ser_rw_rec_ctrl rx_ctrl = {0};
- reg_ser_rw_tr_baud_div tx_baud_div = {0};
- reg_ser_rw_rec_baud_div rx_baud_div = {0};
- int baud;
-
- if (old &&
- termios->c_cflag == old->c_cflag &&
- termios->c_iflag == old->c_iflag)
- return;
-
- /* Tx: 8 bit, no/even parity, 1 stop bit, no cts. */
- tx_ctrl.base_freq = regk_ser_f29_493;
- tx_ctrl.en = 0;
- tx_ctrl.stop = 0;
- tx_ctrl.auto_rts = regk_ser_no;
- tx_ctrl.txd = 1;
- tx_ctrl.auto_cts = 0;
- /* Rx: 8 bit, no/even parity. */
- rx_ctrl.dma_err = regk_ser_stop;
- rx_ctrl.sampling = regk_ser_majority;
- rx_ctrl.timeout = 1;
-
- rx_ctrl.rts_n = regk_ser_inactive;
-
- /* Common for tx and rx: 8N1. */
- tx_ctrl.data_bits = regk_ser_bits8;
- rx_ctrl.data_bits = regk_ser_bits8;
- tx_ctrl.par = regk_ser_even;
- rx_ctrl.par = regk_ser_even;
- tx_ctrl.par_en = regk_ser_no;
- rx_ctrl.par_en = regk_ser_no;
-
- tx_ctrl.stop_bits = regk_ser_bits1;
-
- /*
- * Change baud-rate and write it to the hardware.
- *
- * baud_clock = base_freq / (divisor*8)
- * divisor = base_freq / (baud_clock * 8)
- * base_freq is either:
- * off, ext, 29.493MHz, 32.000 MHz, 32.768 MHz or 100 MHz
- * 20.493MHz is used for standard baudrates
- */
-
- /*
- * For the console port we keep the original baudrate here. Not very
- * beautiful.
- */
- if ((port != console_port) || old)
- baud = uart_get_baud_rate(port, termios, old, 0,
- port->uartclk / 8);
- else
- baud = console_baud;
-
- tx_baud_div.div = 29493000 / (8 * baud);
- /* Rx uses same as tx. */
- rx_baud_div.div = tx_baud_div.div;
- rx_ctrl.base_freq = tx_ctrl.base_freq;
-
- if ((termios->c_cflag & CSIZE) == CS7) {
- /* Set 7 bit mode. */
- tx_ctrl.data_bits = regk_ser_bits7;
- rx_ctrl.data_bits = regk_ser_bits7;
- }
-
- if (termios->c_cflag & CSTOPB) {
- /* Set 2 stop bit mode. */
- tx_ctrl.stop_bits = regk_ser_bits2;
- }
-
- if (termios->c_cflag & PARENB) {
- /* Enable parity. */
- tx_ctrl.par_en = regk_ser_yes;
- rx_ctrl.par_en = regk_ser_yes;
- }
-
- if (termios->c_cflag & CMSPAR) {
- if (termios->c_cflag & PARODD) {
- /* Set mark parity if PARODD and CMSPAR. */
- tx_ctrl.par = regk_ser_mark;
- rx_ctrl.par = regk_ser_mark;
- } else {
- tx_ctrl.par = regk_ser_space;
- rx_ctrl.par = regk_ser_space;
- }
- } else {
- if (termios->c_cflag & PARODD) {
- /* Set odd parity. */
- tx_ctrl.par = regk_ser_odd;
- rx_ctrl.par = regk_ser_odd;
- }
- }
-
- if (termios->c_cflag & CRTSCTS) {
- /* Enable automatic CTS handling. */
- tx_ctrl.auto_cts = regk_ser_yes;
- }
-
- /* Make sure the tx and rx are enabled. */
- tx_ctrl.en = regk_ser_yes;
- rx_ctrl.en = regk_ser_yes;
-
- spin_lock_irqsave(&port->lock, flags);
-
- tx_dma_en.en = 0;
- REG_WR(ser, up->regi_ser, rw_tr_dma_en, tx_dma_en);
-
- /* Actually write the control regs (if modified) to the hardware. */
- uart_update_timeout(port, termios->c_cflag, port->uartclk/8);
- MODIFY_REG(up->regi_ser, rw_rec_baud_div, rx_baud_div);
- MODIFY_REG(up->regi_ser, rw_rec_ctrl, rx_ctrl);
-
- MODIFY_REG(up->regi_ser, rw_tr_baud_div, tx_baud_div);
- MODIFY_REG(up->regi_ser, rw_tr_ctrl, tx_ctrl);
-
- tx_dma_en.en = 0;
- REG_WR(ser, up->regi_ser, rw_tr_dma_en, tx_dma_en);
-
- xoff = REG_RD(ser, up->regi_ser, rw_xoff);
-
- if (up->port.state && up->port.state->port.tty &&
- (up->port.state->port.tty->termios.c_iflag & IXON)) {
- xoff.chr = STOP_CHAR(up->port.state->port.tty);
- xoff.automatic = regk_ser_yes;
- } else
- xoff.automatic = regk_ser_no;
-
- MODIFY_REG(up->regi_ser, rw_xoff, xoff);
-
- /*
- * Make sure we don't start in an automatically shut-off state due to
- * a previous early exit.
- */
- xoff_clr.clr = 1;
- REG_WR(ser, up->regi_ser, rw_xoff_clr, xoff_clr);
-
- etraxfs_uart_set_mctrl(&up->port, up->port.mctrl);
- spin_unlock_irqrestore(&up->port.lock, flags);
-}
-
-static const char *
-etraxfs_uart_type(struct uart_port *port)
-{
- return "CRISv32";
-}
-
-static void etraxfs_uart_release_port(struct uart_port *port)
-{
-}
-
-static int etraxfs_uart_request_port(struct uart_port *port)
-{
- return 0;
-}
-
-static void etraxfs_uart_config_port(struct uart_port *port, int flags)
-{
- struct uart_cris_port *up = (struct uart_cris_port *)port;
-
- up->port.type = PORT_CRIS;
-}
-
-static const struct uart_ops etraxfs_uart_pops = {
- .tx_empty = etraxfs_uart_tx_empty,
- .set_mctrl = etraxfs_uart_set_mctrl,
- .get_mctrl = etraxfs_uart_get_mctrl,
- .stop_tx = etraxfs_uart_stop_tx,
- .start_tx = etraxfs_uart_start_tx,
- .send_xchar = etraxfs_uart_send_xchar,
- .stop_rx = etraxfs_uart_stop_rx,
- .break_ctl = etraxfs_uart_break_ctl,
- .startup = etraxfs_uart_startup,
- .shutdown = etraxfs_uart_shutdown,
- .set_termios = etraxfs_uart_set_termios,
- .type = etraxfs_uart_type,
- .release_port = etraxfs_uart_release_port,
- .request_port = etraxfs_uart_request_port,
- .config_port = etraxfs_uart_config_port,
-#ifdef CONFIG_CONSOLE_POLL
- .poll_get_char = etraxfs_uart_get_poll_char,
- .poll_put_char = etraxfs_uart_put_poll_char,
-#endif
-};
-
-static void cris_serial_port_init(struct uart_port *port, int line)
-{
- struct uart_cris_port *up = (struct uart_cris_port *)port;
-
- if (up->initialized)
- return;
- up->initialized = 1;
- port->line = line;
- spin_lock_init(&port->lock);
- port->ops = &etraxfs_uart_pops;
- port->irq = up->irq;
- port->iobase = (unsigned long) up->regi_ser;
- port->uartclk = 29493000;
-
- /*
- * We can't fit any more than 255 here (unsigned char), though
- * actually UART_XMIT_SIZE characters could be pending output.
- * At time of this writing, the definition of "fifosize" is here the
- * amount of characters that can be pending output after a start_tx call
- * until tx_empty returns 1: see serial_core.c:uart_wait_until_sent.
- * This matters for timeout calculations unfortunately, but keeping
- * larger amounts at the DMA wouldn't win much so let's just play nice.
- */
- port->fifosize = 255;
- port->flags = UPF_BOOT_AUTOCONF;
-}
-
-static int etraxfs_uart_probe(struct platform_device *pdev)
-{
- struct device_node *np = pdev->dev.of_node;
- struct uart_cris_port *up;
- int dev_id;
-
- if (!np)
- return -ENODEV;
-
- dev_id = of_alias_get_id(np, "serial");
- if (dev_id < 0)
- dev_id = 0;
-
- if (dev_id >= UART_NR)
- return -EINVAL;
-
- if (etraxfs_uart_ports[dev_id])
- return -EBUSY;
-
- up = devm_kzalloc(&pdev->dev, sizeof(struct uart_cris_port),
- GFP_KERNEL);
- if (!up)
- return -ENOMEM;
-
- up->irq = irq_of_parse_and_map(np, 0);
- up->regi_ser = of_iomap(np, 0);
- up->port.dev = &pdev->dev;
-
- up->gpios = mctrl_gpio_init_noauto(&pdev->dev, 0);
- if (IS_ERR(up->gpios))
- return PTR_ERR(up->gpios);
-
- cris_serial_port_init(&up->port, dev_id);
-
- etraxfs_uart_ports[dev_id] = up;
- platform_set_drvdata(pdev, &up->port);
- uart_add_one_port(&etraxfs_uart_driver, &up->port);
-
- return 0;
-}
-
-static int etraxfs_uart_remove(struct platform_device *pdev)
-{
- struct uart_port *port;
-
- port = platform_get_drvdata(pdev);
- uart_remove_one_port(&etraxfs_uart_driver, port);
- etraxfs_uart_ports[port->line] = NULL;
-
- return 0;
-}
-
-static const struct of_device_id etraxfs_uart_dt_ids[] = {
- { .compatible = "axis,etraxfs-uart" },
- { /* sentinel */ }
-};
-
-MODULE_DEVICE_TABLE(of, etraxfs_uart_dt_ids);
-
-static struct platform_driver etraxfs_uart_platform_driver = {
- .driver = {
- .name = DRV_NAME,
- .of_match_table = of_match_ptr(etraxfs_uart_dt_ids),
- },
- .probe = etraxfs_uart_probe,
- .remove = etraxfs_uart_remove,
-};
-
-static int __init etraxfs_uart_init(void)
-{
- int ret;
-
- ret = uart_register_driver(&etraxfs_uart_driver);
- if (ret)
- return ret;
-
- ret = platform_driver_register(&etraxfs_uart_platform_driver);
- if (ret)
- uart_unregister_driver(&etraxfs_uart_driver);
-
- return ret;
-}
-
-static void __exit etraxfs_uart_exit(void)
-{
- platform_driver_unregister(&etraxfs_uart_platform_driver);
- uart_unregister_driver(&etraxfs_uart_driver);
-}
-
-module_init(etraxfs_uart_init);
-module_exit(etraxfs_uart_exit);
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 8cf112f2efc3..51e47a63d61a 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -2145,6 +2145,10 @@ static int lpuart_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
return ret;
}
+ if (ret >= ARRAY_SIZE(lpuart_ports)) {
+ dev_err(&pdev->dev, "serial%d out of range\n", ret);
+ return -EINVAL;
+ }
sport->port.line = ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
sport->port.membase = devm_ioremap_resource(&pdev->dev, res);
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index a33c685af990..c2fc6bef7a6f 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -71,12 +71,12 @@
#define UCR1_IDEN (1<<12) /* Idle condition interrupt */
#define UCR1_ICD_REG(x) (((x) & 3) << 10) /* idle condition detect */
#define UCR1_RRDYEN (1<<9) /* Recv ready interrupt enable */
-#define UCR1_RDMAEN (1<<8) /* Recv ready DMA enable */
+#define UCR1_RXDMAEN (1<<8) /* Recv ready DMA enable */
#define UCR1_IREN (1<<7) /* Infrared interface enable */
#define UCR1_TXMPTYEN (1<<6) /* Transimitter empty interrupt enable */
#define UCR1_RTSDEN (1<<5) /* RTS delta interrupt enable */
#define UCR1_SNDBRK (1<<4) /* Send break */
-#define UCR1_TDMAEN (1<<3) /* Transmitter ready DMA enable */
+#define UCR1_TXDMAEN (1<<3) /* Transmitter ready DMA enable */
#define IMX1_UCR1_UARTCLKEN (1<<2) /* UART clock enabled, i.mx1 only */
#define UCR1_ATDMAEN (1<<2) /* Aging DMA Timer Enable */
#define UCR1_DOZE (1<<1) /* Doze */
@@ -204,8 +204,14 @@ struct imx_port {
struct mctrl_gpios *gpios;
+ /* shadow registers */
+ unsigned int ucr1;
+ unsigned int ucr2;
+ unsigned int ucr3;
+ unsigned int ucr4;
+ unsigned int ufcr;
+
/* DMA fields */
- unsigned int dma_is_inited:1;
unsigned int dma_is_enabled:1;
unsigned int dma_is_rxing:1;
unsigned int dma_is_txing:1;
@@ -274,27 +280,81 @@ static const struct of_device_id imx_uart_dt_ids[] = {
};
MODULE_DEVICE_TABLE(of, imx_uart_dt_ids);
-static inline unsigned uts_reg(struct imx_port *sport)
+static void imx_uart_writel(struct imx_port *sport, u32 val, u32 offset)
+{
+ switch (offset) {
+ case UCR1:
+ sport->ucr1 = val;
+ break;
+ case UCR2:
+ sport->ucr2 = val;
+ break;
+ case UCR3:
+ sport->ucr3 = val;
+ break;
+ case UCR4:
+ sport->ucr4 = val;
+ break;
+ case UFCR:
+ sport->ufcr = val;
+ break;
+ default:
+ break;
+ }
+ writel(val, sport->port.membase + offset);
+}
+
+static u32 imx_uart_readl(struct imx_port *sport, u32 offset)
+{
+ switch (offset) {
+ case UCR1:
+ return sport->ucr1;
+ break;
+ case UCR2:
+ /*
+ * UCR2_SRST is the only bit in the cached registers that might
+ * differ from the value that was last written. As it only
+ * clears after being set, reread conditionally.
+ */
+ if (!(sport->ucr2 & UCR2_SRST))
+ sport->ucr2 = readl(sport->port.membase + offset);
+ return sport->ucr2;
+ break;
+ case UCR3:
+ return sport->ucr3;
+ break;
+ case UCR4:
+ return sport->ucr4;
+ break;
+ case UFCR:
+ return sport->ufcr;
+ break;
+ default:
+ return readl(sport->port.membase + offset);
+ }
+}
+
+static inline unsigned imx_uart_uts_reg(struct imx_port *sport)
{
return sport->devdata->uts_reg;
}
-static inline int is_imx1_uart(struct imx_port *sport)
+static inline int imx_uart_is_imx1(struct imx_port *sport)
{
return sport->devdata->devtype == IMX1_UART;
}
-static inline int is_imx21_uart(struct imx_port *sport)
+static inline int imx_uart_is_imx21(struct imx_port *sport)
{
return sport->devdata->devtype == IMX21_UART;
}
-static inline int is_imx53_uart(struct imx_port *sport)
+static inline int imx_uart_is_imx53(struct imx_port *sport)
{
return sport->devdata->devtype == IMX53_UART;
}
-static inline int is_imx6q_uart(struct imx_port *sport)
+static inline int imx_uart_is_imx6q(struct imx_port *sport)
{
return sport->devdata->devtype == IMX6Q_UART;
}
@@ -302,26 +362,26 @@ static inline int is_imx6q_uart(struct imx_port *sport)
* Save and restore functions for UCR1, UCR2 and UCR3 registers
*/
#if defined(CONFIG_SERIAL_IMX_CONSOLE)
-static void imx_port_ucrs_save(struct uart_port *port,
+static void imx_uart_ucrs_save(struct imx_port *sport,
struct imx_port_ucrs *ucr)
{
/* save control registers */
- ucr->ucr1 = readl(port->membase + UCR1);
- ucr->ucr2 = readl(port->membase + UCR2);
- ucr->ucr3 = readl(port->membase + UCR3);
+ ucr->ucr1 = imx_uart_readl(sport, UCR1);
+ ucr->ucr2 = imx_uart_readl(sport, UCR2);
+ ucr->ucr3 = imx_uart_readl(sport, UCR3);
}
-static void imx_port_ucrs_restore(struct uart_port *port,
+static void imx_uart_ucrs_restore(struct imx_port *sport,
struct imx_port_ucrs *ucr)
{
/* restore control registers */
- writel(ucr->ucr1, port->membase + UCR1);
- writel(ucr->ucr2, port->membase + UCR2);
- writel(ucr->ucr3, port->membase + UCR3);
+ imx_uart_writel(sport, ucr->ucr1, UCR1);
+ imx_uart_writel(sport, ucr->ucr2, UCR2);
+ imx_uart_writel(sport, ucr->ucr3, UCR3);
}
#endif
-static void imx_port_rts_active(struct imx_port *sport, unsigned long *ucr2)
+static void imx_uart_rts_active(struct imx_port *sport, u32 *ucr2)
{
*ucr2 &= ~(UCR2_CTSC | UCR2_CTS);
@@ -329,7 +389,7 @@ static void imx_port_rts_active(struct imx_port *sport, unsigned long *ucr2)
mctrl_gpio_set(sport->gpios, sport->port.mctrl);
}
-static void imx_port_rts_inactive(struct imx_port *sport, unsigned long *ucr2)
+static void imx_uart_rts_inactive(struct imx_port *sport, u32 *ucr2)
{
*ucr2 &= ~UCR2_CTSC;
*ucr2 |= UCR2_CTS;
@@ -338,75 +398,91 @@ static void imx_port_rts_inactive(struct imx_port *sport, unsigned long *ucr2)
mctrl_gpio_set(sport->gpios, sport->port.mctrl);
}
-static void imx_port_rts_auto(struct imx_port *sport, unsigned long *ucr2)
+static void imx_uart_rts_auto(struct imx_port *sport, u32 *ucr2)
{
*ucr2 |= UCR2_CTSC;
}
-/*
- * interrupts disabled on entry
- */
-static void imx_stop_tx(struct uart_port *port)
+/* called with port.lock taken and irqs off */
+static void imx_uart_start_rx(struct uart_port *port)
+{
+ struct imx_port *sport = (struct imx_port *)port;
+ unsigned int ucr1, ucr2;
+
+ ucr1 = imx_uart_readl(sport, UCR1);
+ ucr2 = imx_uart_readl(sport, UCR2);
+
+ ucr2 |= UCR2_RXEN;
+
+ if (sport->dma_is_enabled) {
+ ucr1 |= UCR1_RXDMAEN | UCR1_ATDMAEN;
+ } else {
+ ucr1 |= UCR1_RRDYEN;
+ ucr2 |= UCR2_ATEN;
+ }
+
+ /* Write UCR2 first as it includes RXEN */
+ imx_uart_writel(sport, ucr2, UCR2);
+ imx_uart_writel(sport, ucr1, UCR1);
+}
+
+/* called with port.lock taken and irqs off */
+static void imx_uart_stop_tx(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
- unsigned long temp;
+ u32 ucr1;
/*
* We are maybe in the SMP context, so if the DMA TX thread is running
* on other cpu, we have to wait for it to finish.
*/
- if (sport->dma_is_enabled && sport->dma_is_txing)
+ if (sport->dma_is_txing)
return;
- temp = readl(port->membase + UCR1);
- writel(temp & ~UCR1_TXMPTYEN, port->membase + UCR1);
+ ucr1 = imx_uart_readl(sport, UCR1);
+ imx_uart_writel(sport, ucr1 & ~UCR1_TXMPTYEN, UCR1);
/* in rs485 mode disable transmitter if shifter is empty */
if (port->rs485.flags & SER_RS485_ENABLED &&
- readl(port->membase + USR2) & USR2_TXDC) {
- temp = readl(port->membase + UCR2);
+ imx_uart_readl(sport, USR2) & USR2_TXDC) {
+ u32 ucr2 = imx_uart_readl(sport, UCR2), ucr4;
if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
- imx_port_rts_active(sport, &temp);
+ imx_uart_rts_active(sport, &ucr2);
else
- imx_port_rts_inactive(sport, &temp);
- temp |= UCR2_RXEN;
- writel(temp, port->membase + UCR2);
+ imx_uart_rts_inactive(sport, &ucr2);
+ imx_uart_writel(sport, ucr2, UCR2);
- temp = readl(port->membase + UCR4);
- temp &= ~UCR4_TCEN;
- writel(temp, port->membase + UCR4);
+ imx_uart_start_rx(port);
+
+ ucr4 = imx_uart_readl(sport, UCR4);
+ ucr4 &= ~UCR4_TCEN;
+ imx_uart_writel(sport, ucr4, UCR4);
}
}
-/*
- * interrupts disabled on entry
- */
-static void imx_stop_rx(struct uart_port *port)
+/* called with port.lock taken and irqs off */
+static void imx_uart_stop_rx(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
- unsigned long temp;
+ u32 ucr1, ucr2;
- if (sport->dma_is_enabled && sport->dma_is_rxing) {
- if (sport->port.suspended) {
- dmaengine_terminate_all(sport->dma_chan_rx);
- sport->dma_is_rxing = 0;
- } else {
- return;
- }
- }
+ ucr1 = imx_uart_readl(sport, UCR1);
+ ucr2 = imx_uart_readl(sport, UCR2);
- temp = readl(sport->port.membase + UCR2);
- writel(temp & ~UCR2_RXEN, sport->port.membase + UCR2);
+ if (sport->dma_is_enabled) {
+ ucr1 &= ~(UCR1_RXDMAEN | UCR1_ATDMAEN);
+ } else {
+ ucr1 &= ~UCR1_RRDYEN;
+ ucr2 &= ~UCR2_ATEN;
+ }
+ imx_uart_writel(sport, ucr1, UCR1);
- /* disable the `Receiver Ready Interrrupt` */
- temp = readl(sport->port.membase + UCR1);
- writel(temp & ~UCR1_RRDYEN, sport->port.membase + UCR1);
+ ucr2 &= ~UCR2_RXEN;
+ imx_uart_writel(sport, ucr2, UCR2);
}
-/*
- * Set the modem control timer to fire immediately.
- */
-static void imx_enable_ms(struct uart_port *port)
+/* called with port.lock taken and irqs off */
+static void imx_uart_enable_ms(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
@@ -415,49 +491,50 @@ static void imx_enable_ms(struct uart_port *port)
mctrl_gpio_enable_ms(sport->gpios);
}
-static void imx_dma_tx(struct imx_port *sport);
-static inline void imx_transmit_buffer(struct imx_port *sport)
+static void imx_uart_dma_tx(struct imx_port *sport);
+
+/* called with port.lock taken and irqs off */
+static inline void imx_uart_transmit_buffer(struct imx_port *sport)
{
struct circ_buf *xmit = &sport->port.state->xmit;
- unsigned long temp;
if (sport->port.x_char) {
/* Send next char */
- writel(sport->port.x_char, sport->port.membase + URTX0);
+ imx_uart_writel(sport, sport->port.x_char, URTX0);
sport->port.icount.tx++;
sport->port.x_char = 0;
return;
}
if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) {
- imx_stop_tx(&sport->port);
+ imx_uart_stop_tx(&sport->port);
return;
}
if (sport->dma_is_enabled) {
+ u32 ucr1;
/*
* We've just sent a X-char Ensure the TX DMA is enabled
* and the TX IRQ is disabled.
**/
- temp = readl(sport->port.membase + UCR1);
- temp &= ~UCR1_TXMPTYEN;
+ ucr1 = imx_uart_readl(sport, UCR1);
+ ucr1 &= ~UCR1_TXMPTYEN;
if (sport->dma_is_txing) {
- temp |= UCR1_TDMAEN;
- writel(temp, sport->port.membase + UCR1);
+ ucr1 |= UCR1_TXDMAEN;
+ imx_uart_writel(sport, ucr1, UCR1);
} else {
- writel(temp, sport->port.membase + UCR1);
- imx_dma_tx(sport);
+ imx_uart_writel(sport, ucr1, UCR1);
+ imx_uart_dma_tx(sport);
}
- }
- if (sport->dma_is_txing)
return;
+ }
while (!uart_circ_empty(xmit) &&
- !(readl(sport->port.membase + uts_reg(sport)) & UTS_TXFULL)) {
+ !(imx_uart_readl(sport, imx_uart_uts_reg(sport)) & UTS_TXFULL)) {
/* send xmit->buf[xmit->tail]
* out the port here */
- writel(xmit->buf[xmit->tail], sport->port.membase + URTX0);
+ imx_uart_writel(sport, xmit->buf[xmit->tail], URTX0);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
sport->port.icount.tx++;
}
@@ -466,24 +543,24 @@ static inline void imx_transmit_buffer(struct imx_port *sport)
uart_write_wakeup(&sport->port);
if (uart_circ_empty(xmit))
- imx_stop_tx(&sport->port);
+ imx_uart_stop_tx(&sport->port);
}
-static void dma_tx_callback(void *data)
+static void imx_uart_dma_tx_callback(void *data)
{
struct imx_port *sport = data;
struct scatterlist *sgl = &sport->tx_sgl[0];
struct circ_buf *xmit = &sport->port.state->xmit;
unsigned long flags;
- unsigned long temp;
+ u32 ucr1;
spin_lock_irqsave(&sport->port.lock, flags);
dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
- temp = readl(sport->port.membase + UCR1);
- temp &= ~UCR1_TDMAEN;
- writel(temp, sport->port.membase + UCR1);
+ ucr1 = imx_uart_readl(sport, UCR1);
+ ucr1 &= ~UCR1_TXDMAEN;
+ imx_uart_writel(sport, ucr1, UCR1);
/* update the stat */
xmit->tail = (xmit->tail + sport->tx_bytes) & (UART_XMIT_SIZE - 1);
@@ -497,24 +574,34 @@ static void dma_tx_callback(void *data)
uart_write_wakeup(&sport->port);
if (!uart_circ_empty(xmit) && !uart_tx_stopped(&sport->port))
- imx_dma_tx(sport);
+ imx_uart_dma_tx(sport);
+ else if (sport->port.rs485.flags & SER_RS485_ENABLED) {
+ u32 ucr4 = imx_uart_readl(sport, UCR4);
+ ucr4 |= UCR4_TCEN;
+ imx_uart_writel(sport, ucr4, UCR4);
+ }
spin_unlock_irqrestore(&sport->port.lock, flags);
}
-static void imx_dma_tx(struct imx_port *sport)
+/* called with port.lock taken and irqs off */
+static void imx_uart_dma_tx(struct imx_port *sport)
{
struct circ_buf *xmit = &sport->port.state->xmit;
struct scatterlist *sgl = sport->tx_sgl;
struct dma_async_tx_descriptor *desc;
struct dma_chan *chan = sport->dma_chan_tx;
struct device *dev = sport->port.dev;
- unsigned long temp;
+ u32 ucr1, ucr4;
int ret;
if (sport->dma_is_txing)
return;
+ ucr4 = imx_uart_readl(sport, UCR4);
+ ucr4 &= ~UCR4_TCEN;
+ imx_uart_writel(sport, ucr4, UCR4);
+
sport->tx_bytes = uart_circ_chars_pending(xmit);
if (xmit->tail < xmit->head) {
@@ -541,15 +628,15 @@ static void imx_dma_tx(struct imx_port *sport)
dev_err(dev, "We cannot prepare for the TX slave dma!\n");
return;
}
- desc->callback = dma_tx_callback;
+ desc->callback = imx_uart_dma_tx_callback;
desc->callback_param = sport;
dev_dbg(dev, "TX: prepare to send %lu bytes by DMA.\n",
uart_circ_chars_pending(xmit));
- temp = readl(sport->port.membase + UCR1);
- temp |= UCR1_TDMAEN;
- writel(temp, sport->port.membase + UCR1);
+ ucr1 = imx_uart_readl(sport, UCR1);
+ ucr1 |= UCR1_TXDMAEN;
+ imx_uart_writel(sport, ucr1, UCR1);
/* fire it */
sport->dma_is_txing = 1;
@@ -558,99 +645,110 @@ static void imx_dma_tx(struct imx_port *sport)
return;
}
-/*
- * interrupts disabled on entry
- */
-static void imx_start_tx(struct uart_port *port)
+/* called with port.lock taken and irqs off */
+static void imx_uart_start_tx(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
- unsigned long temp;
+ u32 ucr1;
+
+ if (!sport->port.x_char && uart_circ_empty(&port->state->xmit))
+ return;
if (port->rs485.flags & SER_RS485_ENABLED) {
- temp = readl(port->membase + UCR2);
+ u32 ucr2;
+
+ ucr2 = imx_uart_readl(sport, UCR2);
if (port->rs485.flags & SER_RS485_RTS_ON_SEND)
- imx_port_rts_active(sport, &temp);
+ imx_uart_rts_active(sport, &ucr2);
else
- imx_port_rts_inactive(sport, &temp);
+ imx_uart_rts_inactive(sport, &ucr2);
+ imx_uart_writel(sport, ucr2, UCR2);
+
if (!(port->rs485.flags & SER_RS485_RX_DURING_TX))
- temp &= ~UCR2_RXEN;
- writel(temp, port->membase + UCR2);
+ imx_uart_stop_rx(port);
- /* enable transmitter and shifter empty irq */
- temp = readl(port->membase + UCR4);
- temp |= UCR4_TCEN;
- writel(temp, port->membase + UCR4);
+ /*
+ * Enable transmitter and shifter empty irq only if DMA is off.
+ * In the DMA case this is done in the tx-callback.
+ */
+ if (!sport->dma_is_enabled) {
+ u32 ucr4 = imx_uart_readl(sport, UCR4);
+ ucr4 |= UCR4_TCEN;
+ imx_uart_writel(sport, ucr4, UCR4);
+ }
}
if (!sport->dma_is_enabled) {
- temp = readl(sport->port.membase + UCR1);
- writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1);
+ ucr1 = imx_uart_readl(sport, UCR1);
+ imx_uart_writel(sport, ucr1 | UCR1_TXMPTYEN, UCR1);
}
if (sport->dma_is_enabled) {
if (sport->port.x_char) {
/* We have X-char to send, so enable TX IRQ and
* disable TX DMA to let TX interrupt to send X-char */
- temp = readl(sport->port.membase + UCR1);
- temp &= ~UCR1_TDMAEN;
- temp |= UCR1_TXMPTYEN;
- writel(temp, sport->port.membase + UCR1);
+ ucr1 = imx_uart_readl(sport, UCR1);
+ ucr1 &= ~UCR1_TXDMAEN;
+ ucr1 |= UCR1_TXMPTYEN;
+ imx_uart_writel(sport, ucr1, UCR1);
return;
}
if (!uart_circ_empty(&port->state->xmit) &&
!uart_tx_stopped(port))
- imx_dma_tx(sport);
+ imx_uart_dma_tx(sport);
return;
}
}
-static irqreturn_t imx_rtsint(int irq, void *dev_id)
+static irqreturn_t imx_uart_rtsint(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
- unsigned int val;
+ u32 usr1;
unsigned long flags;
spin_lock_irqsave(&sport->port.lock, flags);
- writel(USR1_RTSD, sport->port.membase + USR1);
- val = readl(sport->port.membase + USR1) & USR1_RTSS;
- uart_handle_cts_change(&sport->port, !!val);
+ imx_uart_writel(sport, USR1_RTSD, USR1);
+ usr1 = imx_uart_readl(sport, USR1) & USR1_RTSS;
+ uart_handle_cts_change(&sport->port, !!usr1);
wake_up_interruptible(&sport->port.state->port.delta_msr_wait);
spin_unlock_irqrestore(&sport->port.lock, flags);
return IRQ_HANDLED;
}
-static irqreturn_t imx_txint(int irq, void *dev_id)
+static irqreturn_t imx_uart_txint(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
unsigned long flags;
spin_lock_irqsave(&sport->port.lock, flags);
- imx_transmit_buffer(sport);
+ imx_uart_transmit_buffer(sport);
spin_unlock_irqrestore(&sport->port.lock, flags);
return IRQ_HANDLED;
}
-static irqreturn_t imx_rxint(int irq, void *dev_id)
+static irqreturn_t imx_uart_rxint(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
unsigned int rx, flg, ignored = 0;
struct tty_port *port = &sport->port.state->port;
- unsigned long flags, temp;
+ unsigned long flags;
spin_lock_irqsave(&sport->port.lock, flags);
- while (readl(sport->port.membase + USR2) & USR2_RDR) {
+ while (imx_uart_readl(sport, USR2) & USR2_RDR) {
+ u32 usr2;
+
flg = TTY_NORMAL;
sport->port.icount.rx++;
- rx = readl(sport->port.membase + URXD0);
+ rx = imx_uart_readl(sport, URXD0);
- temp = readl(sport->port.membase + USR2);
- if (temp & USR2_BRCD) {
- writel(USR2_BRCD, sport->port.membase + USR2);
+ usr2 = imx_uart_readl(sport, USR2);
+ if (usr2 & USR2_BRCD) {
+ imx_uart_writel(sport, USR2_BRCD, USR2);
if (uart_handle_break(&sport->port))
continue;
}
@@ -703,16 +801,16 @@ out:
return IRQ_HANDLED;
}
-static void clear_rx_errors(struct imx_port *sport);
+static void imx_uart_clear_rx_errors(struct imx_port *sport);
/*
* We have a modem side uart, so the meanings of RTS and CTS are inverted.
*/
-static unsigned int imx_get_hwmctrl(struct imx_port *sport)
+static unsigned int imx_uart_get_hwmctrl(struct imx_port *sport)
{
unsigned int tmp = TIOCM_DSR;
- unsigned usr1 = readl(sport->port.membase + USR1);
- unsigned usr2 = readl(sport->port.membase + USR2);
+ unsigned usr1 = imx_uart_readl(sport, USR1);
+ unsigned usr2 = imx_uart_readl(sport, USR2);
if (usr1 & USR1_RTSS)
tmp |= TIOCM_CTS;
@@ -722,7 +820,7 @@ static unsigned int imx_get_hwmctrl(struct imx_port *sport)
tmp |= TIOCM_CAR;
if (sport->dte_mode)
- if (!(readl(sport->port.membase + USR2) & USR2_RIIN))
+ if (!(imx_uart_readl(sport, USR2) & USR2_RIIN))
tmp |= TIOCM_RI;
return tmp;
@@ -731,11 +829,11 @@ static unsigned int imx_get_hwmctrl(struct imx_port *sport)
/*
* Handle any change of modem status signal since we were last called.
*/
-static void imx_mctrl_check(struct imx_port *sport)
+static void imx_uart_mctrl_check(struct imx_port *sport)
{
unsigned int status, changed;
- status = imx_get_hwmctrl(sport);
+ status = imx_uart_get_hwmctrl(sport);
changed = status ^ sport->old_status;
if (changed == 0)
@@ -755,55 +853,79 @@ static void imx_mctrl_check(struct imx_port *sport)
wake_up_interruptible(&sport->port.state->port.delta_msr_wait);
}
-static irqreturn_t imx_int(int irq, void *dev_id)
+static irqreturn_t imx_uart_int(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
- unsigned int sts;
- unsigned int sts2;
+ unsigned int usr1, usr2, ucr1, ucr2, ucr3, ucr4;
irqreturn_t ret = IRQ_NONE;
- sts = readl(sport->port.membase + USR1);
- sts2 = readl(sport->port.membase + USR2);
+ usr1 = imx_uart_readl(sport, USR1);
+ usr2 = imx_uart_readl(sport, USR2);
+ ucr1 = imx_uart_readl(sport, UCR1);
+ ucr2 = imx_uart_readl(sport, UCR2);
+ ucr3 = imx_uart_readl(sport, UCR3);
+ ucr4 = imx_uart_readl(sport, UCR4);
- if (!sport->dma_is_enabled && (sts & (USR1_RRDY | USR1_AGTIM))) {
- imx_rxint(irq, dev_id);
+ /*
+ * Even if a condition is true that can trigger an irq only handle it if
+ * the respective irq source is enabled. This prevents some undesired
+ * actions, for example if a character that sits in the RX FIFO and that
+ * should be fetched via DMA is tried to be fetched using PIO. Or the
+ * receiver is currently off and so reading from URXD0 results in an
+ * exception. So just mask the (raw) status bits for disabled irqs.
+ */
+ if ((ucr1 & UCR1_RRDYEN) == 0)
+ usr1 &= ~USR1_RRDY;
+ if ((ucr2 & UCR2_ATEN) == 0)
+ usr1 &= ~USR1_AGTIM;
+ if ((ucr1 & UCR1_TXMPTYEN) == 0)
+ usr1 &= ~USR1_TRDY;
+ if ((ucr4 & UCR4_TCEN) == 0)
+ usr2 &= ~USR2_TXDC;
+ if ((ucr3 & UCR3_DTRDEN) == 0)
+ usr1 &= ~USR1_DTRD;
+ if ((ucr1 & UCR1_RTSDEN) == 0)
+ usr1 &= ~USR1_RTSD;
+ if ((ucr3 & UCR3_AWAKEN) == 0)
+ usr1 &= ~USR1_AWAKE;
+ if ((ucr4 & UCR4_OREN) == 0)
+ usr2 &= ~USR2_ORE;
+
+ if (usr1 & (USR1_RRDY | USR1_AGTIM)) {
+ imx_uart_rxint(irq, dev_id);
ret = IRQ_HANDLED;
}
- if ((sts & USR1_TRDY &&
- readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN) ||
- (sts2 & USR2_TXDC &&
- readl(sport->port.membase + UCR4) & UCR4_TCEN)) {
- imx_txint(irq, dev_id);
+ if ((usr1 & USR1_TRDY) || (usr2 & USR2_TXDC)) {
+ imx_uart_txint(irq, dev_id);
ret = IRQ_HANDLED;
}
- if (sts & USR1_DTRD) {
+ if (usr1 & USR1_DTRD) {
unsigned long flags;
- if (sts & USR1_DTRD)
- writel(USR1_DTRD, sport->port.membase + USR1);
+ imx_uart_writel(sport, USR1_DTRD, USR1);
spin_lock_irqsave(&sport->port.lock, flags);
- imx_mctrl_check(sport);
+ imx_uart_mctrl_check(sport);
spin_unlock_irqrestore(&sport->port.lock, flags);
ret = IRQ_HANDLED;
}
- if (sts & USR1_RTSD) {
- imx_rtsint(irq, dev_id);
+ if (usr1 & USR1_RTSD) {
+ imx_uart_rtsint(irq, dev_id);
ret = IRQ_HANDLED;
}
- if (sts & USR1_AWAKE) {
- writel(USR1_AWAKE, sport->port.membase + USR1);
+ if (usr1 & USR1_AWAKE) {
+ imx_uart_writel(sport, USR1_AWAKE, USR1);
ret = IRQ_HANDLED;
}
- if (sts2 & USR2_ORE) {
+ if (usr2 & USR2_ORE) {
sport->port.icount.overrun++;
- writel(USR2_ORE, sport->port.membase + USR2);
+ imx_uart_writel(sport, USR2_ORE, USR2);
ret = IRQ_HANDLED;
}
@@ -813,52 +935,56 @@ static irqreturn_t imx_int(int irq, void *dev_id)
/*
* Return TIOCSER_TEMT when transmitter is not busy.
*/
-static unsigned int imx_tx_empty(struct uart_port *port)
+static unsigned int imx_uart_tx_empty(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
unsigned int ret;
- ret = (readl(sport->port.membase + USR2) & USR2_TXDC) ? TIOCSER_TEMT : 0;
+ ret = (imx_uart_readl(sport, USR2) & USR2_TXDC) ? TIOCSER_TEMT : 0;
/* If the TX DMA is working, return 0. */
- if (sport->dma_is_enabled && sport->dma_is_txing)
+ if (sport->dma_is_txing)
ret = 0;
return ret;
}
-static unsigned int imx_get_mctrl(struct uart_port *port)
+/* called with port.lock taken and irqs off */
+static unsigned int imx_uart_get_mctrl(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
- unsigned int ret = imx_get_hwmctrl(sport);
+ unsigned int ret = imx_uart_get_hwmctrl(sport);
mctrl_gpio_get(sport->gpios, &ret);
return ret;
}
-static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl)
+/* called with port.lock taken and irqs off */
+static void imx_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
struct imx_port *sport = (struct imx_port *)port;
- unsigned long temp;
+ u32 ucr3, uts;
if (!(port->rs485.flags & SER_RS485_ENABLED)) {
- temp = readl(sport->port.membase + UCR2);
- temp &= ~(UCR2_CTS | UCR2_CTSC);
+ u32 ucr2;
+
+ ucr2 = imx_uart_readl(sport, UCR2);
+ ucr2 &= ~(UCR2_CTS | UCR2_CTSC);
if (mctrl & TIOCM_RTS)
- temp |= UCR2_CTS | UCR2_CTSC;
- writel(temp, sport->port.membase + UCR2);
+ ucr2 |= UCR2_CTS | UCR2_CTSC;
+ imx_uart_writel(sport, ucr2, UCR2);
}
- temp = readl(sport->port.membase + UCR3) & ~UCR3_DSR;
+ ucr3 = imx_uart_readl(sport, UCR3) & ~UCR3_DSR;
if (!(mctrl & TIOCM_DTR))
- temp |= UCR3_DSR;
- writel(temp, sport->port.membase + UCR3);
+ ucr3 |= UCR3_DSR;
+ imx_uart_writel(sport, ucr3, UCR3);
- temp = readl(sport->port.membase + uts_reg(sport)) & ~UTS_LOOP;
+ uts = imx_uart_readl(sport, imx_uart_uts_reg(sport)) & ~UTS_LOOP;
if (mctrl & TIOCM_LOOP)
- temp |= UTS_LOOP;
- writel(temp, sport->port.membase + uts_reg(sport));
+ uts |= UTS_LOOP;
+ imx_uart_writel(sport, uts, imx_uart_uts_reg(sport));
mctrl_gpio_set(sport->gpios, mctrl);
}
@@ -866,19 +992,20 @@ static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl)
/*
* Interrupts always disabled.
*/
-static void imx_break_ctl(struct uart_port *port, int break_state)
+static void imx_uart_break_ctl(struct uart_port *port, int break_state)
{
struct imx_port *sport = (struct imx_port *)port;
- unsigned long flags, temp;
+ unsigned long flags;
+ u32 ucr1;
spin_lock_irqsave(&sport->port.lock, flags);
- temp = readl(sport->port.membase + UCR1) & ~UCR1_SNDBRK;
+ ucr1 = imx_uart_readl(sport, UCR1) & ~UCR1_SNDBRK;
if (break_state != 0)
- temp |= UCR1_SNDBRK;
+ ucr1 |= UCR1_SNDBRK;
- writel(temp, sport->port.membase + UCR1);
+ imx_uart_writel(sport, ucr1, UCR1);
spin_unlock_irqrestore(&sport->port.lock, flags);
}
@@ -887,14 +1014,14 @@ static void imx_break_ctl(struct uart_port *port, int break_state)
* This is our per-port timeout handler, for checking the
* modem status signals.
*/
-static void imx_timeout(struct timer_list *t)
+static void imx_uart_timeout(struct timer_list *t)
{
struct imx_port *sport = from_timer(sport, t, timer);
unsigned long flags;
if (sport->port.state) {
spin_lock_irqsave(&sport->port.lock, flags);
- imx_mctrl_check(sport);
+ imx_uart_mctrl_check(sport);
spin_unlock_irqrestore(&sport->port.lock, flags);
mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT);
@@ -911,7 +1038,7 @@ static void imx_timeout(struct timer_list *t)
* Condition [2] is triggered when a character has been sitting in the FIFO
* for at least 8 byte durations.
*/
-static void dma_rx_callback(void *data)
+static void imx_uart_dma_rx_callback(void *data)
{
struct imx_port *sport = data;
struct dma_chan *chan = sport->dma_chan_rx;
@@ -927,8 +1054,7 @@ static void dma_rx_callback(void *data)
status = dmaengine_tx_status(chan, (dma_cookie_t)0, &state);
if (status == DMA_ERROR) {
- dev_err(sport->port.dev, "DMA transaction error.\n");
- clear_rx_errors(sport);
+ imx_uart_clear_rx_errors(sport);
return;
}
@@ -988,7 +1114,7 @@ static void dma_rx_callback(void *data)
/* RX DMA buffer periods */
#define RX_DMA_PERIODS 4
-static int start_rx_dma(struct imx_port *sport)
+static int imx_uart_start_rx_dma(struct imx_port *sport)
{
struct scatterlist *sgl = &sport->rx_sgl;
struct dma_chan *chan = sport->dma_chan_rx;
@@ -1016,7 +1142,7 @@ static int start_rx_dma(struct imx_port *sport)
dev_err(dev, "We cannot prepare for the RX slave dma!\n");
return -EINVAL;
}
- desc->callback = dma_rx_callback;
+ desc->callback = imx_uart_dma_rx_callback;
desc->callback_param = sport;
dev_dbg(dev, "RX: prepare for the DMA.\n");
@@ -1026,27 +1152,35 @@ static int start_rx_dma(struct imx_port *sport)
return 0;
}
-static void clear_rx_errors(struct imx_port *sport)
+static void imx_uart_clear_rx_errors(struct imx_port *sport)
{
- unsigned int status_usr1, status_usr2;
+ struct tty_port *port = &sport->port.state->port;
+ u32 usr1, usr2;
- status_usr1 = readl(sport->port.membase + USR1);
- status_usr2 = readl(sport->port.membase + USR2);
+ usr1 = imx_uart_readl(sport, USR1);
+ usr2 = imx_uart_readl(sport, USR2);
- if (status_usr2 & USR2_BRCD) {
+ if (usr2 & USR2_BRCD) {
sport->port.icount.brk++;
- writel(USR2_BRCD, sport->port.membase + USR2);
- } else if (status_usr1 & USR1_FRAMERR) {
- sport->port.icount.frame++;
- writel(USR1_FRAMERR, sport->port.membase + USR1);
- } else if (status_usr1 & USR1_PARITYERR) {
- sport->port.icount.parity++;
- writel(USR1_PARITYERR, sport->port.membase + USR1);
+ imx_uart_writel(sport, USR2_BRCD, USR2);
+ uart_handle_break(&sport->port);
+ if (tty_insert_flip_char(port, 0, TTY_BREAK) == 0)
+ sport->port.icount.buf_overrun++;
+ tty_flip_buffer_push(port);
+ } else {
+ dev_err(sport->port.dev, "DMA transaction error.\n");
+ if (usr1 & USR1_FRAMERR) {
+ sport->port.icount.frame++;
+ imx_uart_writel(sport, USR1_FRAMERR, USR1);
+ } else if (usr1 & USR1_PARITYERR) {
+ sport->port.icount.parity++;
+ imx_uart_writel(sport, USR1_PARITYERR, USR1);
+ }
}
- if (status_usr2 & USR2_ORE) {
+ if (usr2 & USR2_ORE) {
sport->port.icount.overrun++;
- writel(USR2_ORE, sport->port.membase + USR2);
+ imx_uart_writel(sport, USR2_ORE, USR2);
}
}
@@ -1056,15 +1190,15 @@ static void clear_rx_errors(struct imx_port *sport)
#define TXTL_DMA 8 /* DMA burst setting */
#define RXTL_DMA 9 /* DMA burst setting */
-static void imx_setup_ufcr(struct imx_port *sport,
- unsigned char txwl, unsigned char rxwl)
+static void imx_uart_setup_ufcr(struct imx_port *sport,
+ unsigned char txwl, unsigned char rxwl)
{
unsigned int val;
/* set receiver / transmitter trigger level */
- val = readl(sport->port.membase + UFCR) & (UFCR_RFDIV | UFCR_DCEDTE);
+ val = imx_uart_readl(sport, UFCR) & (UFCR_RFDIV | UFCR_DCEDTE);
val |= txwl << UFCR_TXTL_SHF | rxwl;
- writel(val, sport->port.membase + UFCR);
+ imx_uart_writel(sport, val, UFCR);
}
static void imx_uart_dma_exit(struct imx_port *sport)
@@ -1083,8 +1217,6 @@ static void imx_uart_dma_exit(struct imx_port *sport)
dma_release_channel(sport->dma_chan_tx);
sport->dma_chan_tx = NULL;
}
-
- sport->dma_is_inited = 0;
}
static int imx_uart_dma_init(struct imx_port *sport)
@@ -1137,43 +1269,41 @@ static int imx_uart_dma_init(struct imx_port *sport)
goto err;
}
- sport->dma_is_inited = 1;
-
return 0;
err:
imx_uart_dma_exit(sport);
return ret;
}
-static void imx_enable_dma(struct imx_port *sport)
+static void imx_uart_enable_dma(struct imx_port *sport)
{
- unsigned long temp;
+ u32 ucr1;
- /* set UCR1 */
- temp = readl(sport->port.membase + UCR1);
- temp |= UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN;
- writel(temp, sport->port.membase + UCR1);
+ imx_uart_setup_ufcr(sport, TXTL_DMA, RXTL_DMA);
- imx_setup_ufcr(sport, TXTL_DMA, RXTL_DMA);
+ /* set UCR1 */
+ ucr1 = imx_uart_readl(sport, UCR1);
+ ucr1 |= UCR1_RXDMAEN | UCR1_TXDMAEN | UCR1_ATDMAEN;
+ imx_uart_writel(sport, ucr1, UCR1);
sport->dma_is_enabled = 1;
}
-static void imx_disable_dma(struct imx_port *sport)
+static void imx_uart_disable_dma(struct imx_port *sport)
{
- unsigned long temp;
+ u32 ucr1, ucr2;
/* clear UCR1 */
- temp = readl(sport->port.membase + UCR1);
- temp &= ~(UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN);
- writel(temp, sport->port.membase + UCR1);
+ ucr1 = imx_uart_readl(sport, UCR1);
+ ucr1 &= ~(UCR1_RXDMAEN | UCR1_TXDMAEN | UCR1_ATDMAEN);
+ imx_uart_writel(sport, ucr1, UCR1);
/* clear UCR2 */
- temp = readl(sport->port.membase + UCR2);
- temp &= ~(UCR2_CTSC | UCR2_CTS | UCR2_ATEN);
- writel(temp, sport->port.membase + UCR2);
+ ucr2 = imx_uart_readl(sport, UCR2);
+ ucr2 &= ~(UCR2_CTSC | UCR2_CTS | UCR2_ATEN);
+ imx_uart_writel(sport, ucr2, UCR2);
- imx_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT);
+ imx_uart_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT);
sport->dma_is_enabled = 0;
}
@@ -1181,11 +1311,13 @@ static void imx_disable_dma(struct imx_port *sport)
/* half the RX buffer size */
#define CTSTL 16
-static int imx_startup(struct uart_port *port)
+static int imx_uart_startup(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
int retval, i;
- unsigned long flags, temp;
+ unsigned long flags;
+ int dma_is_inited = 0;
+ u32 ucr1, ucr2, ucr4;
retval = clk_prepare_enable(sport->clk_per);
if (retval)
@@ -1196,104 +1328,106 @@ static int imx_startup(struct uart_port *port)
return retval;
}
- imx_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT);
+ imx_uart_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT);
/* disable the DREN bit (Data Ready interrupt enable) before
* requesting IRQs
*/
- temp = readl(sport->port.membase + UCR4);
+ ucr4 = imx_uart_readl(sport, UCR4);
/* set the trigger level for CTS */
- temp &= ~(UCR4_CTSTL_MASK << UCR4_CTSTL_SHF);
- temp |= CTSTL << UCR4_CTSTL_SHF;
+ ucr4 &= ~(UCR4_CTSTL_MASK << UCR4_CTSTL_SHF);
+ ucr4 |= CTSTL << UCR4_CTSTL_SHF;
- writel(temp & ~UCR4_DREN, sport->port.membase + UCR4);
+ imx_uart_writel(sport, ucr4 & ~UCR4_DREN, UCR4);
/* Can we enable the DMA support? */
- if (!uart_console(port) && !sport->dma_is_inited)
- imx_uart_dma_init(sport);
+ if (!uart_console(port) && imx_uart_dma_init(sport) == 0)
+ dma_is_inited = 1;
spin_lock_irqsave(&sport->port.lock, flags);
/* Reset fifo's and state machines */
i = 100;
- temp = readl(sport->port.membase + UCR2);
- temp &= ~UCR2_SRST;
- writel(temp, sport->port.membase + UCR2);
+ ucr2 = imx_uart_readl(sport, UCR2);
+ ucr2 &= ~UCR2_SRST;
+ imx_uart_writel(sport, ucr2, UCR2);
- while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) && (--i > 0))
+ while (!(imx_uart_readl(sport, UCR2) & UCR2_SRST) && (--i > 0))
udelay(1);
/*
* Finally, clear and enable interrupts
*/
- writel(USR1_RTSD | USR1_DTRD, sport->port.membase + USR1);
- writel(USR2_ORE, sport->port.membase + USR2);
-
- if (sport->dma_is_inited && !sport->dma_is_enabled)
- imx_enable_dma(sport);
+ imx_uart_writel(sport, USR1_RTSD | USR1_DTRD, USR1);
+ imx_uart_writel(sport, USR2_ORE, USR2);
- temp = readl(sport->port.membase + UCR1) & ~UCR1_RRDYEN;
- if (!sport->dma_is_enabled)
- temp |= UCR1_RRDYEN;
- temp |= UCR1_UARTEN;
+ ucr1 = imx_uart_readl(sport, UCR1) & ~UCR1_RRDYEN;
+ ucr1 |= UCR1_UARTEN;
if (sport->have_rtscts)
- temp |= UCR1_RTSDEN;
+ ucr1 |= UCR1_RTSDEN;
- writel(temp, sport->port.membase + UCR1);
+ imx_uart_writel(sport, ucr1, UCR1);
- temp = readl(sport->port.membase + UCR4) & ~UCR4_OREN;
+ ucr4 = imx_uart_readl(sport, UCR4) & ~UCR4_OREN;
if (!sport->dma_is_enabled)
- temp |= UCR4_OREN;
- writel(temp, sport->port.membase + UCR4);
+ ucr4 |= UCR4_OREN;
+ imx_uart_writel(sport, ucr4, UCR4);
- temp = readl(sport->port.membase + UCR2) & ~UCR2_ATEN;
- temp |= (UCR2_RXEN | UCR2_TXEN);
+ ucr2 = imx_uart_readl(sport, UCR2) & ~UCR2_ATEN;
+ ucr2 |= (UCR2_RXEN | UCR2_TXEN);
if (!sport->have_rtscts)
- temp |= UCR2_IRTS;
+ ucr2 |= UCR2_IRTS;
/*
* make sure the edge sensitive RTS-irq is disabled,
* we're using RTSD instead.
*/
- if (!is_imx1_uart(sport))
- temp &= ~UCR2_RTSEN;
- writel(temp, sport->port.membase + UCR2);
+ if (!imx_uart_is_imx1(sport))
+ ucr2 &= ~UCR2_RTSEN;
+ imx_uart_writel(sport, ucr2, UCR2);
+
+ if (!imx_uart_is_imx1(sport)) {
+ u32 ucr3;
- if (!is_imx1_uart(sport)) {
- temp = readl(sport->port.membase + UCR3);
+ ucr3 = imx_uart_readl(sport, UCR3);
- temp |= UCR3_DTRDEN | UCR3_RI | UCR3_DCD;
+ ucr3 |= UCR3_DTRDEN | UCR3_RI | UCR3_DCD;
if (sport->dte_mode)
/* disable broken interrupts */
- temp &= ~(UCR3_RI | UCR3_DCD);
+ ucr3 &= ~(UCR3_RI | UCR3_DCD);
- writel(temp, sport->port.membase + UCR3);
+ imx_uart_writel(sport, ucr3, UCR3);
}
/*
* Enable modem status interrupts
*/
- imx_enable_ms(&sport->port);
+ imx_uart_enable_ms(&sport->port);
- /*
- * Start RX DMA immediately instead of waiting for RX FIFO interrupts.
- * In our iMX53 the average delay for the first reception dropped from
- * approximately 35000 microseconds to 1000 microseconds.
- */
- if (sport->dma_is_enabled)
- start_rx_dma(sport);
+ if (dma_is_inited) {
+ imx_uart_enable_dma(sport);
+ imx_uart_start_rx_dma(sport);
+ } else {
+ ucr1 = imx_uart_readl(sport, UCR1);
+ ucr1 |= UCR1_RRDYEN;
+ imx_uart_writel(sport, ucr1, UCR1);
+
+ ucr2 = imx_uart_readl(sport, UCR2);
+ ucr2 |= UCR2_ATEN;
+ imx_uart_writel(sport, ucr2, UCR2);
+ }
spin_unlock_irqrestore(&sport->port.lock, flags);
return 0;
}
-static void imx_shutdown(struct uart_port *port)
+static void imx_uart_shutdown(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
- unsigned long temp;
unsigned long flags;
+ u32 ucr1, ucr2;
if (sport->dma_is_enabled) {
sport->dma_is_rxing = 0;
@@ -1302,9 +1436,9 @@ static void imx_shutdown(struct uart_port *port)
dmaengine_terminate_sync(sport->dma_chan_rx);
spin_lock_irqsave(&sport->port.lock, flags);
- imx_stop_tx(port);
- imx_stop_rx(port);
- imx_disable_dma(sport);
+ imx_uart_stop_tx(port);
+ imx_uart_stop_rx(port);
+ imx_uart_disable_dma(sport);
spin_unlock_irqrestore(&sport->port.lock, flags);
imx_uart_dma_exit(sport);
}
@@ -1312,9 +1446,9 @@ static void imx_shutdown(struct uart_port *port)
mctrl_gpio_disable_ms(sport->gpios);
spin_lock_irqsave(&sport->port.lock, flags);
- temp = readl(sport->port.membase + UCR2);
- temp &= ~(UCR2_TXEN);
- writel(temp, sport->port.membase + UCR2);
+ ucr2 = imx_uart_readl(sport, UCR2);
+ ucr2 &= ~(UCR2_TXEN | UCR2_ATEN);
+ imx_uart_writel(sport, ucr2, UCR2);
spin_unlock_irqrestore(&sport->port.lock, flags);
/*
@@ -1327,21 +1461,22 @@ static void imx_shutdown(struct uart_port *port)
*/
spin_lock_irqsave(&sport->port.lock, flags);
- temp = readl(sport->port.membase + UCR1);
- temp &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN);
+ ucr1 = imx_uart_readl(sport, UCR1);
+ ucr1 &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN | UCR1_RXDMAEN | UCR1_ATDMAEN);
- writel(temp, sport->port.membase + UCR1);
+ imx_uart_writel(sport, ucr1, UCR1);
spin_unlock_irqrestore(&sport->port.lock, flags);
clk_disable_unprepare(sport->clk_per);
clk_disable_unprepare(sport->clk_ipg);
}
-static void imx_flush_buffer(struct uart_port *port)
+/* called with port.lock taken and irqs off */
+static void imx_uart_flush_buffer(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
struct scatterlist *sgl = &sport->tx_sgl[0];
- unsigned long temp;
+ u32 ucr2;
int i = 100, ubir, ubmr, uts;
if (!sport->dma_chan_tx)
@@ -1350,11 +1485,13 @@ static void imx_flush_buffer(struct uart_port *port)
sport->tx_bytes = 0;
dmaengine_terminate_all(sport->dma_chan_tx);
if (sport->dma_is_txing) {
+ u32 ucr1;
+
dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents,
DMA_TO_DEVICE);
- temp = readl(sport->port.membase + UCR1);
- temp &= ~UCR1_TDMAEN;
- writel(temp, sport->port.membase + UCR1);
+ ucr1 = imx_uart_readl(sport, UCR1);
+ ucr1 &= ~UCR1_TXDMAEN;
+ imx_uart_writel(sport, ucr1, UCR1);
sport->dma_is_txing = 0;
}
@@ -1369,33 +1506,33 @@ static void imx_flush_buffer(struct uart_port *port)
* UTXD. UBRC is read only, so only save/restore the other three
* registers.
*/
- ubir = readl(sport->port.membase + UBIR);
- ubmr = readl(sport->port.membase + UBMR);
- uts = readl(sport->port.membase + IMX21_UTS);
+ ubir = imx_uart_readl(sport, UBIR);
+ ubmr = imx_uart_readl(sport, UBMR);
+ uts = imx_uart_readl(sport, IMX21_UTS);
- temp = readl(sport->port.membase + UCR2);
- temp &= ~UCR2_SRST;
- writel(temp, sport->port.membase + UCR2);
+ ucr2 = imx_uart_readl(sport, UCR2);
+ ucr2 &= ~UCR2_SRST;
+ imx_uart_writel(sport, ucr2, UCR2);
- while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) && (--i > 0))
+ while (!(imx_uart_readl(sport, UCR2) & UCR2_SRST) && (--i > 0))
udelay(1);
/* Restore the registers */
- writel(ubir, sport->port.membase + UBIR);
- writel(ubmr, sport->port.membase + UBMR);
- writel(uts, sport->port.membase + IMX21_UTS);
+ imx_uart_writel(sport, ubir, UBIR);
+ imx_uart_writel(sport, ubmr, UBMR);
+ imx_uart_writel(sport, uts, IMX21_UTS);
}
static void
-imx_set_termios(struct uart_port *port, struct ktermios *termios,
- struct ktermios *old)
+imx_uart_set_termios(struct uart_port *port, struct ktermios *termios,
+ struct ktermios *old)
{
struct imx_port *sport = (struct imx_port *)port;
unsigned long flags;
- unsigned long ucr2, old_ucr1, old_ucr2;
+ u32 ucr2, old_ucr1, old_ucr2, ufcr;
unsigned int baud, quot;
unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
- unsigned long div, ufcr;
+ unsigned long div;
unsigned long num, denom;
uint64_t tdiv64;
@@ -1426,11 +1563,11 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
*/
if (port->rs485.flags &
SER_RS485_RTS_AFTER_SEND)
- imx_port_rts_active(sport, &ucr2);
+ imx_uart_rts_active(sport, &ucr2);
else
- imx_port_rts_inactive(sport, &ucr2);
+ imx_uart_rts_inactive(sport, &ucr2);
} else {
- imx_port_rts_auto(sport, &ucr2);
+ imx_uart_rts_auto(sport, &ucr2);
}
} else {
termios->c_cflag &= ~CRTSCTS;
@@ -1438,9 +1575,9 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
} else if (port->rs485.flags & SER_RS485_ENABLED) {
/* disable transmitter */
if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
- imx_port_rts_active(sport, &ucr2);
+ imx_uart_rts_active(sport, &ucr2);
else
- imx_port_rts_inactive(sport, &ucr2);
+ imx_uart_rts_inactive(sport, &ucr2);
}
@@ -1495,17 +1632,18 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
/*
* disable interrupts and drain transmitter
*/
- old_ucr1 = readl(sport->port.membase + UCR1);
- writel(old_ucr1 & ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN),
- sport->port.membase + UCR1);
-
- while (!(readl(sport->port.membase + USR2) & USR2_TXDC))
+ old_ucr1 = imx_uart_readl(sport, UCR1);
+ imx_uart_writel(sport,
+ old_ucr1 & ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN),
+ UCR1);
+ old_ucr2 = imx_uart_readl(sport, UCR2);
+ imx_uart_writel(sport, old_ucr2 & ~UCR2_ATEN, UCR2);
+
+ while (!(imx_uart_readl(sport, USR2) & USR2_TXDC))
barrier();
/* then, disable everything */
- old_ucr2 = readl(sport->port.membase + UCR2);
- writel(old_ucr2 & ~(UCR2_TXEN | UCR2_RXEN),
- sport->port.membase + UCR2);
+ imx_uart_writel(sport, old_ucr2 & ~(UCR2_TXEN | UCR2_RXEN | UCR2_ATEN), UCR2);
old_ucr2 &= (UCR2_TXEN | UCR2_RXEN | UCR2_ATEN);
/* custom-baudrate handling */
@@ -1531,29 +1669,29 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
num -= 1;
denom -= 1;
- ufcr = readl(sport->port.membase + UFCR);
+ ufcr = imx_uart_readl(sport, UFCR);
ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div);
- writel(ufcr, sport->port.membase + UFCR);
+ imx_uart_writel(sport, ufcr, UFCR);
- writel(num, sport->port.membase + UBIR);
- writel(denom, sport->port.membase + UBMR);
+ imx_uart_writel(sport, num, UBIR);
+ imx_uart_writel(sport, denom, UBMR);
- if (!is_imx1_uart(sport))
- writel(sport->port.uartclk / div / 1000,
- sport->port.membase + IMX21_ONEMS);
+ if (!imx_uart_is_imx1(sport))
+ imx_uart_writel(sport, sport->port.uartclk / div / 1000,
+ IMX21_ONEMS);
- writel(old_ucr1, sport->port.membase + UCR1);
+ imx_uart_writel(sport, old_ucr1, UCR1);
/* set the parity, stop bits and data size */
- writel(ucr2 | old_ucr2, sport->port.membase + UCR2);
+ imx_uart_writel(sport, ucr2 | old_ucr2, UCR2);
if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
- imx_enable_ms(&sport->port);
+ imx_uart_enable_ms(&sport->port);
spin_unlock_irqrestore(&sport->port.lock, flags);
}
-static const char *imx_type(struct uart_port *port)
+static const char *imx_uart_type(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
@@ -1563,7 +1701,7 @@ static const char *imx_type(struct uart_port *port)
/*
* Configure/autoconfigure the port.
*/
-static void imx_config_port(struct uart_port *port, int flags)
+static void imx_uart_config_port(struct uart_port *port, int flags)
{
struct imx_port *sport = (struct imx_port *)port;
@@ -1577,7 +1715,7 @@ static void imx_config_port(struct uart_port *port, int flags)
* even then only between PORT_IMX and PORT_UNKNOWN
*/
static int
-imx_verify_port(struct uart_port *port, struct serial_struct *ser)
+imx_uart_verify_port(struct uart_port *port, struct serial_struct *ser)
{
struct imx_port *sport = (struct imx_port *)port;
int ret = 0;
@@ -1601,11 +1739,11 @@ imx_verify_port(struct uart_port *port, struct serial_struct *ser)
#if defined(CONFIG_CONSOLE_POLL)
-static int imx_poll_init(struct uart_port *port)
+static int imx_uart_poll_init(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
unsigned long flags;
- unsigned long temp;
+ u32 ucr1, ucr2;
int retval;
retval = clk_prepare_enable(sport->clk_ipg);
@@ -1615,58 +1753,76 @@ static int imx_poll_init(struct uart_port *port)
if (retval)
clk_disable_unprepare(sport->clk_ipg);
- imx_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT);
+ imx_uart_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT);
spin_lock_irqsave(&sport->port.lock, flags);
- temp = readl(sport->port.membase + UCR1);
- if (is_imx1_uart(sport))
- temp |= IMX1_UCR1_UARTCLKEN;
- temp |= UCR1_UARTEN | UCR1_RRDYEN;
- temp &= ~(UCR1_TXMPTYEN | UCR1_RTSDEN);
- writel(temp, sport->port.membase + UCR1);
+ /*
+ * Be careful about the order of enabling bits here. First enable the
+ * receiver (UARTEN + RXEN) and only then the corresponding irqs.
+ * This prevents that a character that already sits in the RX fifo is
+ * triggering an irq but the try to fetch it from there results in an
+ * exception because UARTEN or RXEN is still off.
+ */
+ ucr1 = imx_uart_readl(sport, UCR1);
+ ucr2 = imx_uart_readl(sport, UCR2);
+
+ if (imx_uart_is_imx1(sport))
+ ucr1 |= IMX1_UCR1_UARTCLKEN;
+
+ ucr1 |= UCR1_UARTEN;
+ ucr1 &= ~(UCR1_TXMPTYEN | UCR1_RTSDEN | UCR1_RRDYEN);
+
+ ucr2 |= UCR2_RXEN;
+ ucr2 &= ~UCR2_ATEN;
- temp = readl(sport->port.membase + UCR2);
- temp |= UCR2_RXEN;
- writel(temp, sport->port.membase + UCR2);
+ imx_uart_writel(sport, ucr1, UCR1);
+ imx_uart_writel(sport, ucr2, UCR2);
+
+ /* now enable irqs */
+ imx_uart_writel(sport, ucr1 | UCR1_RRDYEN, UCR1);
+ imx_uart_writel(sport, ucr2 | UCR2_ATEN, UCR2);
spin_unlock_irqrestore(&sport->port.lock, flags);
return 0;
}
-static int imx_poll_get_char(struct uart_port *port)
+static int imx_uart_poll_get_char(struct uart_port *port)
{
- if (!(readl_relaxed(port->membase + USR2) & USR2_RDR))
+ struct imx_port *sport = (struct imx_port *)port;
+ if (!(imx_uart_readl(sport, USR2) & USR2_RDR))
return NO_POLL_CHAR;
- return readl_relaxed(port->membase + URXD0) & URXD_RX_DATA;
+ return imx_uart_readl(sport, URXD0) & URXD_RX_DATA;
}
-static void imx_poll_put_char(struct uart_port *port, unsigned char c)
+static void imx_uart_poll_put_char(struct uart_port *port, unsigned char c)
{
+ struct imx_port *sport = (struct imx_port *)port;
unsigned int status;
/* drain */
do {
- status = readl_relaxed(port->membase + USR1);
+ status = imx_uart_readl(sport, USR1);
} while (~status & USR1_TRDY);
/* write */
- writel_relaxed(c, port->membase + URTX0);
+ imx_uart_writel(sport, c, URTX0);
/* flush */
do {
- status = readl_relaxed(port->membase + USR2);
+ status = imx_uart_readl(sport, USR2);
} while (~status & USR2_TXDC);
}
#endif
-static int imx_rs485_config(struct uart_port *port,
- struct serial_rs485 *rs485conf)
+/* called with port.lock taken and irqs off or from .probe without locking */
+static int imx_uart_rs485_config(struct uart_port *port,
+ struct serial_rs485 *rs485conf)
{
struct imx_port *sport = (struct imx_port *)port;
- unsigned long temp;
+ u32 ucr2;
/* unimplemented */
rs485conf->delay_rts_before_send = 0;
@@ -1677,71 +1833,73 @@ static int imx_rs485_config(struct uart_port *port,
rs485conf->flags &= ~SER_RS485_ENABLED;
if (rs485conf->flags & SER_RS485_ENABLED) {
+ /* Enable receiver if low-active RTS signal is requested */
+ if (sport->have_rtscts && !sport->have_rtsgpio &&
+ !(rs485conf->flags & SER_RS485_RTS_ON_SEND))
+ rs485conf->flags |= SER_RS485_RX_DURING_TX;
+
/* disable transmitter */
- temp = readl(sport->port.membase + UCR2);
+ ucr2 = imx_uart_readl(sport, UCR2);
if (rs485conf->flags & SER_RS485_RTS_AFTER_SEND)
- imx_port_rts_active(sport, &temp);
+ imx_uart_rts_active(sport, &ucr2);
else
- imx_port_rts_inactive(sport, &temp);
- writel(temp, sport->port.membase + UCR2);
+ imx_uart_rts_inactive(sport, &ucr2);
+ imx_uart_writel(sport, ucr2, UCR2);
}
/* Make sure Rx is enabled in case Tx is active with Rx disabled */
if (!(rs485conf->flags & SER_RS485_ENABLED) ||
- rs485conf->flags & SER_RS485_RX_DURING_TX) {
- temp = readl(sport->port.membase + UCR2);
- temp |= UCR2_RXEN;
- writel(temp, sport->port.membase + UCR2);
- }
+ rs485conf->flags & SER_RS485_RX_DURING_TX)
+ imx_uart_start_rx(port);
port->rs485 = *rs485conf;
return 0;
}
-static const struct uart_ops imx_pops = {
- .tx_empty = imx_tx_empty,
- .set_mctrl = imx_set_mctrl,
- .get_mctrl = imx_get_mctrl,
- .stop_tx = imx_stop_tx,
- .start_tx = imx_start_tx,
- .stop_rx = imx_stop_rx,
- .enable_ms = imx_enable_ms,
- .break_ctl = imx_break_ctl,
- .startup = imx_startup,
- .shutdown = imx_shutdown,
- .flush_buffer = imx_flush_buffer,
- .set_termios = imx_set_termios,
- .type = imx_type,
- .config_port = imx_config_port,
- .verify_port = imx_verify_port,
+static const struct uart_ops imx_uart_pops = {
+ .tx_empty = imx_uart_tx_empty,
+ .set_mctrl = imx_uart_set_mctrl,
+ .get_mctrl = imx_uart_get_mctrl,
+ .stop_tx = imx_uart_stop_tx,
+ .start_tx = imx_uart_start_tx,
+ .stop_rx = imx_uart_stop_rx,
+ .enable_ms = imx_uart_enable_ms,
+ .break_ctl = imx_uart_break_ctl,
+ .startup = imx_uart_startup,
+ .shutdown = imx_uart_shutdown,
+ .flush_buffer = imx_uart_flush_buffer,
+ .set_termios = imx_uart_set_termios,
+ .type = imx_uart_type,
+ .config_port = imx_uart_config_port,
+ .verify_port = imx_uart_verify_port,
#if defined(CONFIG_CONSOLE_POLL)
- .poll_init = imx_poll_init,
- .poll_get_char = imx_poll_get_char,
- .poll_put_char = imx_poll_put_char,
+ .poll_init = imx_uart_poll_init,
+ .poll_get_char = imx_uart_poll_get_char,
+ .poll_put_char = imx_uart_poll_put_char,
#endif
};
-static struct imx_port *imx_ports[UART_NR];
+static struct imx_port *imx_uart_ports[UART_NR];
#ifdef CONFIG_SERIAL_IMX_CONSOLE
-static void imx_console_putchar(struct uart_port *port, int ch)
+static void imx_uart_console_putchar(struct uart_port *port, int ch)
{
struct imx_port *sport = (struct imx_port *)port;
- while (readl(sport->port.membase + uts_reg(sport)) & UTS_TXFULL)
+ while (imx_uart_readl(sport, imx_uart_uts_reg(sport)) & UTS_TXFULL)
barrier();
- writel(ch, sport->port.membase + URTX0);
+ imx_uart_writel(sport, ch, URTX0);
}
/*
* Interrupts are disabled on entering
*/
static void
-imx_console_write(struct console *co, const char *s, unsigned int count)
+imx_uart_console_write(struct console *co, const char *s, unsigned int count)
{
- struct imx_port *sport = imx_ports[co->index];
+ struct imx_port *sport = imx_uart_ports[co->index];
struct imx_port_ucrs old_ucr;
unsigned int ucr1;
unsigned long flags = 0;
@@ -1767,27 +1925,27 @@ imx_console_write(struct console *co, const char *s, unsigned int count)
/*
* First, save UCR1/2/3 and then disable interrupts
*/
- imx_port_ucrs_save(&sport->port, &old_ucr);
+ imx_uart_ucrs_save(sport, &old_ucr);
ucr1 = old_ucr.ucr1;
- if (is_imx1_uart(sport))
+ if (imx_uart_is_imx1(sport))
ucr1 |= IMX1_UCR1_UARTCLKEN;
ucr1 |= UCR1_UARTEN;
ucr1 &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN);
- writel(ucr1, sport->port.membase + UCR1);
+ imx_uart_writel(sport, ucr1, UCR1);
- writel(old_ucr.ucr2 | UCR2_TXEN, sport->port.membase + UCR2);
+ imx_uart_writel(sport, old_ucr.ucr2 | UCR2_TXEN, UCR2);
- uart_console_write(&sport->port, s, count, imx_console_putchar);
+ uart_console_write(&sport->port, s, count, imx_uart_console_putchar);
/*
* Finally, wait for transmitter to become empty
* and restore UCR1/2/3
*/
- while (!(readl(sport->port.membase + USR2) & USR2_TXDC));
+ while (!(imx_uart_readl(sport, USR2) & USR2_TXDC));
- imx_port_ucrs_restore(&sport->port, &old_ucr);
+ imx_uart_ucrs_restore(sport, &old_ucr);
if (locked)
spin_unlock_irqrestore(&sport->port.lock, flags);
@@ -1801,17 +1959,17 @@ imx_console_write(struct console *co, const char *s, unsigned int count)
* try to determine the current setup.
*/
static void __init
-imx_console_get_options(struct imx_port *sport, int *baud,
- int *parity, int *bits)
+imx_uart_console_get_options(struct imx_port *sport, int *baud,
+ int *parity, int *bits)
{
- if (readl(sport->port.membase + UCR1) & UCR1_UARTEN) {
+ if (imx_uart_readl(sport, UCR1) & UCR1_UARTEN) {
/* ok, the port was enabled */
unsigned int ucr2, ubir, ubmr, uartclk;
unsigned int baud_raw;
unsigned int ucfr_rfdiv;
- ucr2 = readl(sport->port.membase + UCR2);
+ ucr2 = imx_uart_readl(sport, UCR2);
*parity = 'n';
if (ucr2 & UCR2_PREN) {
@@ -1826,10 +1984,10 @@ imx_console_get_options(struct imx_port *sport, int *baud,
else
*bits = 7;
- ubir = readl(sport->port.membase + UBIR) & 0xffff;
- ubmr = readl(sport->port.membase + UBMR) & 0xffff;
+ ubir = imx_uart_readl(sport, UBIR) & 0xffff;
+ ubmr = imx_uart_readl(sport, UBMR) & 0xffff;
- ucfr_rfdiv = (readl(sport->port.membase + UFCR) & UFCR_RFDIV) >> 7;
+ ucfr_rfdiv = (imx_uart_readl(sport, UFCR) & UFCR_RFDIV) >> 7;
if (ucfr_rfdiv == 6)
ucfr_rfdiv = 7;
else
@@ -1860,7 +2018,7 @@ imx_console_get_options(struct imx_port *sport, int *baud,
}
static int __init
-imx_console_setup(struct console *co, char *options)
+imx_uart_console_setup(struct console *co, char *options)
{
struct imx_port *sport;
int baud = 9600;
@@ -1874,9 +2032,9 @@ imx_console_setup(struct console *co, char *options)
* if so, search for the first available port that does have
* console support.
*/
- if (co->index == -1 || co->index >= ARRAY_SIZE(imx_ports))
+ if (co->index == -1 || co->index >= ARRAY_SIZE(imx_uart_ports))
co->index = 0;
- sport = imx_ports[co->index];
+ sport = imx_uart_ports[co->index];
if (sport == NULL)
return -ENODEV;
@@ -1888,9 +2046,9 @@ imx_console_setup(struct console *co, char *options)
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
else
- imx_console_get_options(sport, &baud, &parity, &bits);
+ imx_uart_console_get_options(sport, &baud, &parity, &bits);
- imx_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT);
+ imx_uart_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT);
retval = uart_set_options(&sport->port, co, baud, parity, bits, flow);
@@ -1908,34 +2066,36 @@ error_console:
return retval;
}
-static struct uart_driver imx_reg;
-static struct console imx_console = {
+static struct uart_driver imx_uart_uart_driver;
+static struct console imx_uart_console = {
.name = DEV_NAME,
- .write = imx_console_write,
+ .write = imx_uart_console_write,
.device = uart_console_device,
- .setup = imx_console_setup,
+ .setup = imx_uart_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
- .data = &imx_reg,
+ .data = &imx_uart_uart_driver,
};
-#define IMX_CONSOLE &imx_console
+#define IMX_CONSOLE &imx_uart_console
#ifdef CONFIG_OF
-static void imx_console_early_putchar(struct uart_port *port, int ch)
+static void imx_uart_console_early_putchar(struct uart_port *port, int ch)
{
- while (readl_relaxed(port->membase + IMX21_UTS) & UTS_TXFULL)
+ struct imx_port *sport = (struct imx_port *)port;
+
+ while (imx_uart_readl(sport, IMX21_UTS) & UTS_TXFULL)
cpu_relax();
- writel_relaxed(ch, port->membase + URTX0);
+ imx_uart_writel(sport, ch, URTX0);
}
-static void imx_console_early_write(struct console *con, const char *s,
- unsigned count)
+static void imx_uart_console_early_write(struct console *con, const char *s,
+ unsigned count)
{
struct earlycon_device *dev = con->data;
- uart_console_write(&dev->port, s, count, imx_console_early_putchar);
+ uart_console_write(&dev->port, s, count, imx_uart_console_early_putchar);
}
static int __init
@@ -1944,7 +2104,7 @@ imx_console_early_setup(struct earlycon_device *dev, const char *opt)
if (!dev->port.membase)
return -ENODEV;
- dev->con->write = imx_console_early_write;
+ dev->con->write = imx_uart_console_early_write;
return 0;
}
@@ -1956,13 +2116,13 @@ OF_EARLYCON_DECLARE(ec_imx21, "fsl,imx21-uart", imx_console_early_setup);
#define IMX_CONSOLE NULL
#endif
-static struct uart_driver imx_reg = {
+static struct uart_driver imx_uart_uart_driver = {
.owner = THIS_MODULE,
.driver_name = DRIVER_NAME,
.dev_name = DEV_NAME,
.major = SERIAL_IMX_MAJOR,
.minor = MINOR_START,
- .nr = ARRAY_SIZE(imx_ports),
+ .nr = ARRAY_SIZE(imx_uart_ports),
.cons = IMX_CONSOLE,
};
@@ -1971,8 +2131,8 @@ static struct uart_driver imx_reg = {
* This function returns 1 iff pdev isn't a device instatiated by dt, 0 iff it
* could successfully get all information from dt or a negative errno.
*/
-static int serial_imx_probe_dt(struct imx_port *sport,
- struct platform_device *pdev)
+static int imx_uart_probe_dt(struct imx_port *sport,
+ struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
int ret;
@@ -2002,15 +2162,15 @@ static int serial_imx_probe_dt(struct imx_port *sport,
return 0;
}
#else
-static inline int serial_imx_probe_dt(struct imx_port *sport,
- struct platform_device *pdev)
+static inline int imx_uart_probe_dt(struct imx_port *sport,
+ struct platform_device *pdev)
{
return 1;
}
#endif
-static void serial_imx_probe_pdata(struct imx_port *sport,
- struct platform_device *pdev)
+static void imx_uart_probe_pdata(struct imx_port *sport,
+ struct platform_device *pdev)
{
struct imxuart_platform_data *pdata = dev_get_platdata(&pdev->dev);
@@ -2024,11 +2184,12 @@ static void serial_imx_probe_pdata(struct imx_port *sport,
sport->have_rtscts = 1;
}
-static int serial_imx_probe(struct platform_device *pdev)
+static int imx_uart_probe(struct platform_device *pdev)
{
struct imx_port *sport;
void __iomem *base;
- int ret = 0, reg;
+ int ret = 0;
+ u32 ucr1;
struct resource *res;
int txirq, rxirq, rtsirq;
@@ -2036,12 +2197,18 @@ static int serial_imx_probe(struct platform_device *pdev)
if (!sport)
return -ENOMEM;
- ret = serial_imx_probe_dt(sport, pdev);
+ ret = imx_uart_probe_dt(sport, pdev);
if (ret > 0)
- serial_imx_probe_pdata(sport, pdev);
+ imx_uart_probe_pdata(sport, pdev);
else if (ret < 0)
return ret;
+ if (sport->port.line >= ARRAY_SIZE(imx_uart_ports)) {
+ dev_err(&pdev->dev, "serial%d out of range\n",
+ sport->port.line);
+ return -EINVAL;
+ }
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
@@ -2058,10 +2225,10 @@ static int serial_imx_probe(struct platform_device *pdev)
sport->port.iotype = UPIO_MEM;
sport->port.irq = rxirq;
sport->port.fifosize = 32;
- sport->port.ops = &imx_pops;
- sport->port.rs485_config = imx_rs485_config;
+ sport->port.ops = &imx_uart_pops;
+ sport->port.rs485_config = imx_uart_rs485_config;
sport->port.flags = UPF_BOOT_AUTOCONF;
- timer_setup(&sport->timer, imx_timeout, 0);
+ timer_setup(&sport->timer, imx_uart_timeout, 0);
sport->gpios = mctrl_gpio_init(&sport->port, 0);
if (IS_ERR(sport->gpios))
@@ -2090,49 +2257,68 @@ static int serial_imx_probe(struct platform_device *pdev)
return ret;
}
+ /* initialize shadow register values */
+ sport->ucr1 = readl(sport->port.membase + UCR1);
+ sport->ucr2 = readl(sport->port.membase + UCR2);
+ sport->ucr3 = readl(sport->port.membase + UCR3);
+ sport->ucr4 = readl(sport->port.membase + UCR4);
+ sport->ufcr = readl(sport->port.membase + UFCR);
+
uart_get_rs485_mode(&pdev->dev, &sport->port.rs485);
if (sport->port.rs485.flags & SER_RS485_ENABLED &&
(!sport->have_rtscts && !sport->have_rtsgpio))
dev_err(&pdev->dev, "no RTS control, disabling rs485\n");
- imx_rs485_config(&sport->port, &sport->port.rs485);
+ /*
+ * If using the i.MX UART RTS/CTS control then the RTS (CTS_B)
+ * signal cannot be set low during transmission in case the
+ * receiver is off (limitation of the i.MX UART IP).
+ */
+ if (sport->port.rs485.flags & SER_RS485_ENABLED &&
+ sport->have_rtscts && !sport->have_rtsgpio &&
+ (!(sport->port.rs485.flags & SER_RS485_RTS_ON_SEND) &&
+ !(sport->port.rs485.flags & SER_RS485_RX_DURING_TX)))
+ dev_err(&pdev->dev,
+ "low-active RTS not possible when receiver is off, enabling receiver\n");
+
+ imx_uart_rs485_config(&sport->port, &sport->port.rs485);
/* Disable interrupts before requesting them */
- reg = readl_relaxed(sport->port.membase + UCR1);
- reg &= ~(UCR1_ADEN | UCR1_TRDYEN | UCR1_IDEN | UCR1_RRDYEN |
+ ucr1 = imx_uart_readl(sport, UCR1);
+ ucr1 &= ~(UCR1_ADEN | UCR1_TRDYEN | UCR1_IDEN | UCR1_RRDYEN |
UCR1_TXMPTYEN | UCR1_RTSDEN);
- writel_relaxed(reg, sport->port.membase + UCR1);
+ imx_uart_writel(sport, ucr1, UCR1);
- if (!is_imx1_uart(sport) && sport->dte_mode) {
+ if (!imx_uart_is_imx1(sport) && sport->dte_mode) {
/*
* The DCEDTE bit changes the direction of DSR, DCD, DTR and RI
* and influences if UCR3_RI and UCR3_DCD changes the level of RI
* and DCD (when they are outputs) or enables the respective
* irqs. So set this bit early, i.e. before requesting irqs.
*/
- reg = readl(sport->port.membase + UFCR);
- if (!(reg & UFCR_DCEDTE))
- writel(reg | UFCR_DCEDTE, sport->port.membase + UFCR);
+ u32 ufcr = imx_uart_readl(sport, UFCR);
+ if (!(ufcr & UFCR_DCEDTE))
+ imx_uart_writel(sport, ufcr | UFCR_DCEDTE, UFCR);
/*
* Disable UCR3_RI and UCR3_DCD irqs. They are also not
* enabled later because they cannot be cleared
* (confirmed on i.MX25) which makes them unusable.
*/
- writel(IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP | UCR3_DSR,
- sport->port.membase + UCR3);
+ imx_uart_writel(sport,
+ IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP | UCR3_DSR,
+ UCR3);
} else {
- unsigned long ucr3 = UCR3_DSR;
-
- reg = readl(sport->port.membase + UFCR);
- if (reg & UFCR_DCEDTE)
- writel(reg & ~UFCR_DCEDTE, sport->port.membase + UFCR);
+ u32 ucr3 = UCR3_DSR;
+ u32 ufcr = imx_uart_readl(sport, UFCR);
+ if (ufcr & UFCR_DCEDTE)
+ imx_uart_writel(sport, ufcr & ~UFCR_DCEDTE, UFCR);
- if (!is_imx1_uart(sport))
+ if (!imx_uart_is_imx1(sport))
ucr3 |= IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP;
- writel(ucr3, sport->port.membase + UCR3);
+ imx_uart_writel(sport, ucr3, UCR3);
}
clk_disable_unprepare(sport->clk_ipg);
@@ -2142,7 +2328,7 @@ static int serial_imx_probe(struct platform_device *pdev)
* chips only have one interrupt.
*/
if (txirq > 0) {
- ret = devm_request_irq(&pdev->dev, rxirq, imx_rxint, 0,
+ ret = devm_request_irq(&pdev->dev, rxirq, imx_uart_rxint, 0,
dev_name(&pdev->dev), sport);
if (ret) {
dev_err(&pdev->dev, "failed to request rx irq: %d\n",
@@ -2150,7 +2336,7 @@ static int serial_imx_probe(struct platform_device *pdev)
return ret;
}
- ret = devm_request_irq(&pdev->dev, txirq, imx_txint, 0,
+ ret = devm_request_irq(&pdev->dev, txirq, imx_uart_txint, 0,
dev_name(&pdev->dev), sport);
if (ret) {
dev_err(&pdev->dev, "failed to request tx irq: %d\n",
@@ -2158,7 +2344,7 @@ static int serial_imx_probe(struct platform_device *pdev)
return ret;
}
} else {
- ret = devm_request_irq(&pdev->dev, rxirq, imx_int, 0,
+ ret = devm_request_irq(&pdev->dev, rxirq, imx_uart_int, 0,
dev_name(&pdev->dev), sport);
if (ret) {
dev_err(&pdev->dev, "failed to request irq: %d\n", ret);
@@ -2166,90 +2352,90 @@ static int serial_imx_probe(struct platform_device *pdev)
}
}
- imx_ports[sport->port.line] = sport;
+ imx_uart_ports[sport->port.line] = sport;
platform_set_drvdata(pdev, sport);
- return uart_add_one_port(&imx_reg, &sport->port);
+ return uart_add_one_port(&imx_uart_uart_driver, &sport->port);
}
-static int serial_imx_remove(struct platform_device *pdev)
+static int imx_uart_remove(struct platform_device *pdev)
{
struct imx_port *sport = platform_get_drvdata(pdev);
- return uart_remove_one_port(&imx_reg, &sport->port);
+ return uart_remove_one_port(&imx_uart_uart_driver, &sport->port);
}
-static void serial_imx_restore_context(struct imx_port *sport)
+static void imx_uart_restore_context(struct imx_port *sport)
{
if (!sport->context_saved)
return;
- writel(sport->saved_reg[4], sport->port.membase + UFCR);
- writel(sport->saved_reg[5], sport->port.membase + UESC);
- writel(sport->saved_reg[6], sport->port.membase + UTIM);
- writel(sport->saved_reg[7], sport->port.membase + UBIR);
- writel(sport->saved_reg[8], sport->port.membase + UBMR);
- writel(sport->saved_reg[9], sport->port.membase + IMX21_UTS);
- writel(sport->saved_reg[0], sport->port.membase + UCR1);
- writel(sport->saved_reg[1] | UCR2_SRST, sport->port.membase + UCR2);
- writel(sport->saved_reg[2], sport->port.membase + UCR3);
- writel(sport->saved_reg[3], sport->port.membase + UCR4);
+ imx_uart_writel(sport, sport->saved_reg[4], UFCR);
+ imx_uart_writel(sport, sport->saved_reg[5], UESC);
+ imx_uart_writel(sport, sport->saved_reg[6], UTIM);
+ imx_uart_writel(sport, sport->saved_reg[7], UBIR);
+ imx_uart_writel(sport, sport->saved_reg[8], UBMR);
+ imx_uart_writel(sport, sport->saved_reg[9], IMX21_UTS);
+ imx_uart_writel(sport, sport->saved_reg[0], UCR1);
+ imx_uart_writel(sport, sport->saved_reg[1] | UCR2_SRST, UCR2);
+ imx_uart_writel(sport, sport->saved_reg[2], UCR3);
+ imx_uart_writel(sport, sport->saved_reg[3], UCR4);
sport->context_saved = false;
}
-static void serial_imx_save_context(struct imx_port *sport)
+static void imx_uart_save_context(struct imx_port *sport)
{
/* Save necessary regs */
- sport->saved_reg[0] = readl(sport->port.membase + UCR1);
- sport->saved_reg[1] = readl(sport->port.membase + UCR2);
- sport->saved_reg[2] = readl(sport->port.membase + UCR3);
- sport->saved_reg[3] = readl(sport->port.membase + UCR4);
- sport->saved_reg[4] = readl(sport->port.membase + UFCR);
- sport->saved_reg[5] = readl(sport->port.membase + UESC);
- sport->saved_reg[6] = readl(sport->port.membase + UTIM);
- sport->saved_reg[7] = readl(sport->port.membase + UBIR);
- sport->saved_reg[8] = readl(sport->port.membase + UBMR);
- sport->saved_reg[9] = readl(sport->port.membase + IMX21_UTS);
+ sport->saved_reg[0] = imx_uart_readl(sport, UCR1);
+ sport->saved_reg[1] = imx_uart_readl(sport, UCR2);
+ sport->saved_reg[2] = imx_uart_readl(sport, UCR3);
+ sport->saved_reg[3] = imx_uart_readl(sport, UCR4);
+ sport->saved_reg[4] = imx_uart_readl(sport, UFCR);
+ sport->saved_reg[5] = imx_uart_readl(sport, UESC);
+ sport->saved_reg[6] = imx_uart_readl(sport, UTIM);
+ sport->saved_reg[7] = imx_uart_readl(sport, UBIR);
+ sport->saved_reg[8] = imx_uart_readl(sport, UBMR);
+ sport->saved_reg[9] = imx_uart_readl(sport, IMX21_UTS);
sport->context_saved = true;
}
-static void serial_imx_enable_wakeup(struct imx_port *sport, bool on)
+static void imx_uart_enable_wakeup(struct imx_port *sport, bool on)
{
- unsigned int val;
+ u32 ucr3;
- val = readl(sport->port.membase + UCR3);
+ ucr3 = imx_uart_readl(sport, UCR3);
if (on) {
- writel(USR1_AWAKE, sport->port.membase + USR1);
- val |= UCR3_AWAKEN;
+ imx_uart_writel(sport, USR1_AWAKE, USR1);
+ ucr3 |= UCR3_AWAKEN;
+ } else {
+ ucr3 &= ~UCR3_AWAKEN;
}
- else
- val &= ~UCR3_AWAKEN;
- writel(val, sport->port.membase + UCR3);
+ imx_uart_writel(sport, ucr3, UCR3);
if (sport->have_rtscts) {
- val = readl(sport->port.membase + UCR1);
+ u32 ucr1 = imx_uart_readl(sport, UCR1);
if (on)
- val |= UCR1_RTSDEN;
+ ucr1 |= UCR1_RTSDEN;
else
- val &= ~UCR1_RTSDEN;
- writel(val, sport->port.membase + UCR1);
+ ucr1 &= ~UCR1_RTSDEN;
+ imx_uart_writel(sport, ucr1, UCR1);
}
}
-static int imx_serial_port_suspend_noirq(struct device *dev)
+static int imx_uart_suspend_noirq(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct imx_port *sport = platform_get_drvdata(pdev);
- serial_imx_save_context(sport);
+ imx_uart_save_context(sport);
clk_disable(sport->clk_ipg);
return 0;
}
-static int imx_serial_port_resume_noirq(struct device *dev)
+static int imx_uart_resume_noirq(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct imx_port *sport = platform_get_drvdata(pdev);
@@ -2259,18 +2445,18 @@ static int imx_serial_port_resume_noirq(struct device *dev)
if (ret)
return ret;
- serial_imx_restore_context(sport);
+ imx_uart_restore_context(sport);
return 0;
}
-static int imx_serial_port_suspend(struct device *dev)
+static int imx_uart_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct imx_port *sport = platform_get_drvdata(pdev);
int ret;
- uart_suspend_port(&imx_reg, &sport->port);
+ uart_suspend_port(&imx_uart_uart_driver, &sport->port);
disable_irq(sport->port.irq);
ret = clk_prepare_enable(sport->clk_ipg);
@@ -2278,20 +2464,20 @@ static int imx_serial_port_suspend(struct device *dev)
return ret;
/* enable wakeup from i.MX UART */
- serial_imx_enable_wakeup(sport, true);
+ imx_uart_enable_wakeup(sport, true);
return 0;
}
-static int imx_serial_port_resume(struct device *dev)
+static int imx_uart_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct imx_port *sport = platform_get_drvdata(pdev);
/* disable wakeup from i.MX UART */
- serial_imx_enable_wakeup(sport, false);
+ imx_uart_enable_wakeup(sport, false);
- uart_resume_port(&imx_reg, &sport->port);
+ uart_resume_port(&imx_uart_uart_driver, &sport->port);
enable_irq(sport->port.irq);
clk_disable_unprepare(sport->clk_ipg);
@@ -2299,74 +2485,74 @@ static int imx_serial_port_resume(struct device *dev)
return 0;
}
-static int imx_serial_port_freeze(struct device *dev)
+static int imx_uart_freeze(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct imx_port *sport = platform_get_drvdata(pdev);
- uart_suspend_port(&imx_reg, &sport->port);
+ uart_suspend_port(&imx_uart_uart_driver, &sport->port);
return clk_prepare_enable(sport->clk_ipg);
}
-static int imx_serial_port_thaw(struct device *dev)
+static int imx_uart_thaw(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct imx_port *sport = platform_get_drvdata(pdev);
- uart_resume_port(&imx_reg, &sport->port);
+ uart_resume_port(&imx_uart_uart_driver, &sport->port);
clk_disable_unprepare(sport->clk_ipg);
return 0;
}
-static const struct dev_pm_ops imx_serial_port_pm_ops = {
- .suspend_noirq = imx_serial_port_suspend_noirq,
- .resume_noirq = imx_serial_port_resume_noirq,
- .freeze_noirq = imx_serial_port_suspend_noirq,
- .restore_noirq = imx_serial_port_resume_noirq,
- .suspend = imx_serial_port_suspend,
- .resume = imx_serial_port_resume,
- .freeze = imx_serial_port_freeze,
- .thaw = imx_serial_port_thaw,
- .restore = imx_serial_port_thaw,
+static const struct dev_pm_ops imx_uart_pm_ops = {
+ .suspend_noirq = imx_uart_suspend_noirq,
+ .resume_noirq = imx_uart_resume_noirq,
+ .freeze_noirq = imx_uart_suspend_noirq,
+ .restore_noirq = imx_uart_resume_noirq,
+ .suspend = imx_uart_suspend,
+ .resume = imx_uart_resume,
+ .freeze = imx_uart_freeze,
+ .thaw = imx_uart_thaw,
+ .restore = imx_uart_thaw,
};
-static struct platform_driver serial_imx_driver = {
- .probe = serial_imx_probe,
- .remove = serial_imx_remove,
+static struct platform_driver imx_uart_platform_driver = {
+ .probe = imx_uart_probe,
+ .remove = imx_uart_remove,
- .id_table = imx_uart_devtype,
- .driver = {
- .name = "imx-uart",
+ .id_table = imx_uart_devtype,
+ .driver = {
+ .name = "imx-uart",
.of_match_table = imx_uart_dt_ids,
- .pm = &imx_serial_port_pm_ops,
+ .pm = &imx_uart_pm_ops,
},
};
-static int __init imx_serial_init(void)
+static int __init imx_uart_init(void)
{
- int ret = uart_register_driver(&imx_reg);
+ int ret = uart_register_driver(&imx_uart_uart_driver);
if (ret)
return ret;
- ret = platform_driver_register(&serial_imx_driver);
+ ret = platform_driver_register(&imx_uart_platform_driver);
if (ret != 0)
- uart_unregister_driver(&imx_reg);
+ uart_unregister_driver(&imx_uart_uart_driver);
return ret;
}
-static void __exit imx_serial_exit(void)
+static void __exit imx_uart_exit(void)
{
- platform_driver_unregister(&serial_imx_driver);
- uart_unregister_driver(&imx_reg);
+ platform_driver_unregister(&imx_uart_platform_driver);
+ uart_unregister_driver(&imx_uart_uart_driver);
}
-module_init(imx_serial_init);
-module_exit(imx_serial_exit);
+module_init(imx_uart_init);
+module_exit(imx_uart_exit);
MODULE_AUTHOR("Sascha Hauer");
MODULE_DESCRIPTION("IMX generic serial port driver");
diff --git a/drivers/tty/serial/m32r_sio.c b/drivers/tty/serial/m32r_sio.c
deleted file mode 100644
index 7b83a8aab495..000000000000
--- a/drivers/tty/serial/m32r_sio.c
+++ /dev/null
@@ -1,1053 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * m32r_sio.c
- *
- * Driver for M32R serial ports
- *
- * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
- * Based on drivers/serial/8250.c.
- *
- * Copyright (C) 2001 Russell King.
- * Copyright (C) 2004 Hirokazu Takata <takata at linux-m32r.org>
- */
-
-/*
- * A note about mapbase / membase
- *
- * mapbase is the physical address of the IO port. Currently, we don't
- * support this very well, and it may well be dropped from this driver
- * in future. As such, mapbase should be NULL.
- *
- * membase is an 'ioremapped' cookie. This is compatible with the old
- * serial.c driver, and is currently the preferred form.
- */
-
-#if defined(CONFIG_SERIAL_M32R_SIO_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
-#define SUPPORT_SYSRQ
-#endif
-
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-#include <linux/console.h>
-#include <linux/sysrq.h>
-#include <linux/serial.h>
-#include <linux/delay.h>
-
-#include <asm/m32r.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-
-#define BAUD_RATE 115200
-
-#include <linux/serial_core.h>
-#include "m32r_sio_reg.h"
-
-#define PASS_LIMIT 256
-
-static const struct {
- unsigned int port;
- unsigned int irq;
-} old_serial_port[] = {
-#if defined(CONFIG_PLAT_USRV)
- /* PORT IRQ FLAGS */
- { 0x3F8, PLD_IRQ_UART0 }, /* ttyS0 */
- { 0x2F8, PLD_IRQ_UART1 }, /* ttyS1 */
-#elif defined(CONFIG_SERIAL_M32R_PLDSIO)
- { ((unsigned long)PLD_ESIO0CR), PLD_IRQ_SIO0_RCV }, /* ttyS0 */
-#else
- { M32R_SIO_OFFSET, M32R_IRQ_SIO0_R }, /* ttyS0 */
-#endif
-};
-
-#define UART_NR ARRAY_SIZE(old_serial_port)
-
-struct uart_sio_port {
- struct uart_port port;
- struct timer_list timer; /* "no irq" timer */
- struct list_head list; /* ports on this IRQ */
- unsigned char ier;
-};
-
-struct irq_info {
- spinlock_t lock;
- struct list_head *head;
-};
-
-static struct irq_info irq_lists[NR_IRQS];
-
-#ifdef CONFIG_SERIAL_M32R_PLDSIO
-
-#define __sio_in(x) inw((unsigned long)(x))
-#define __sio_out(v,x) outw((v),(unsigned long)(x))
-
-static inline void sio_set_baud_rate(unsigned long baud)
-{
- unsigned short sbaud;
- sbaud = (boot_cpu_data.bus_clock / (baud * 4))-1;
- __sio_out(sbaud, PLD_ESIO0BAUR);
-}
-
-static void sio_reset(void)
-{
- unsigned short tmp;
-
- tmp = __sio_in(PLD_ESIO0RXB);
- tmp = __sio_in(PLD_ESIO0RXB);
- tmp = __sio_in(PLD_ESIO0CR);
- sio_set_baud_rate(BAUD_RATE);
- __sio_out(0x0300, PLD_ESIO0CR);
- __sio_out(0x0003, PLD_ESIO0CR);
-}
-
-static void sio_init(void)
-{
- unsigned short tmp;
-
- tmp = __sio_in(PLD_ESIO0RXB);
- tmp = __sio_in(PLD_ESIO0RXB);
- tmp = __sio_in(PLD_ESIO0CR);
- __sio_out(0x0300, PLD_ESIO0CR);
- __sio_out(0x0003, PLD_ESIO0CR);
-}
-
-static void sio_error(int *status)
-{
- printk("SIO0 error[%04x]\n", *status);
- do {
- sio_init();
- } while ((*status = __sio_in(PLD_ESIO0CR)) != 3);
-}
-
-#else /* not CONFIG_SERIAL_M32R_PLDSIO */
-
-#define __sio_in(x) inl(x)
-#define __sio_out(v,x) outl((v),(x))
-
-static inline void sio_set_baud_rate(unsigned long baud)
-{
- unsigned long i, j;
-
- i = boot_cpu_data.bus_clock / (baud * 16);
- j = (boot_cpu_data.bus_clock - (i * baud * 16)) / baud;
- i -= 1;
- j = (j + 1) >> 1;
-
- __sio_out(i, M32R_SIO0_BAUR_PORTL);
- __sio_out(j, M32R_SIO0_RBAUR_PORTL);
-}
-
-static void sio_reset(void)
-{
- __sio_out(0x00000300, M32R_SIO0_CR_PORTL); /* init status */
- __sio_out(0x00000800, M32R_SIO0_MOD1_PORTL); /* 8bit */
- __sio_out(0x00000080, M32R_SIO0_MOD0_PORTL); /* 1stop non */
- sio_set_baud_rate(BAUD_RATE);
- __sio_out(0x00000000, M32R_SIO0_TRCR_PORTL);
- __sio_out(0x00000003, M32R_SIO0_CR_PORTL); /* RXCEN */
-}
-
-static void sio_init(void)
-{
- unsigned int tmp;
-
- tmp = __sio_in(M32R_SIO0_RXB_PORTL);
- tmp = __sio_in(M32R_SIO0_RXB_PORTL);
- tmp = __sio_in(M32R_SIO0_STS_PORTL);
- __sio_out(0x00000003, M32R_SIO0_CR_PORTL);
-}
-
-static void sio_error(int *status)
-{
- printk("SIO0 error[%04x]\n", *status);
- do {
- sio_init();
- } while ((*status = __sio_in(M32R_SIO0_CR_PORTL)) != 3);
-}
-
-#endif /* CONFIG_SERIAL_M32R_PLDSIO */
-
-static unsigned int sio_in(struct uart_sio_port *up, int offset)
-{
- return __sio_in(up->port.iobase + offset);
-}
-
-static void sio_out(struct uart_sio_port *up, int offset, int value)
-{
- __sio_out(value, up->port.iobase + offset);
-}
-
-static unsigned int serial_in(struct uart_sio_port *up, int offset)
-{
- if (!offset)
- return 0;
-
- return __sio_in(offset);
-}
-
-static void serial_out(struct uart_sio_port *up, int offset, int value)
-{
- if (!offset)
- return;
-
- __sio_out(value, offset);
-}
-
-static void m32r_sio_stop_tx(struct uart_port *port)
-{
- struct uart_sio_port *up =
- container_of(port, struct uart_sio_port, port);
-
- if (up->ier & UART_IER_THRI) {
- up->ier &= ~UART_IER_THRI;
- serial_out(up, UART_IER, up->ier);
- }
-}
-
-static void m32r_sio_start_tx(struct uart_port *port)
-{
-#ifdef CONFIG_SERIAL_M32R_PLDSIO
- struct uart_sio_port *up =
- container_of(port, struct uart_sio_port, port);
- struct circ_buf *xmit = &up->port.state->xmit;
-
- if (!(up->ier & UART_IER_THRI)) {
- up->ier |= UART_IER_THRI;
- serial_out(up, UART_IER, up->ier);
- if (!uart_circ_empty(xmit)) {
- serial_out(up, UART_TX, xmit->buf[xmit->tail]);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- up->port.icount.tx++;
- }
- }
- while((serial_in(up, UART_LSR) & UART_EMPTY) != UART_EMPTY);
-#else
- struct uart_sio_port *up =
- container_of(port, struct uart_sio_port, port);
-
- if (!(up->ier & UART_IER_THRI)) {
- up->ier |= UART_IER_THRI;
- serial_out(up, UART_IER, up->ier);
- }
-#endif
-}
-
-static void m32r_sio_stop_rx(struct uart_port *port)
-{
- struct uart_sio_port *up =
- container_of(port, struct uart_sio_port, port);
-
- up->ier &= ~UART_IER_RLSI;
- up->port.read_status_mask &= ~UART_LSR_DR;
- serial_out(up, UART_IER, up->ier);
-}
-
-static void m32r_sio_enable_ms(struct uart_port *port)
-{
- struct uart_sio_port *up =
- container_of(port, struct uart_sio_port, port);
-
- up->ier |= UART_IER_MSI;
- serial_out(up, UART_IER, up->ier);
-}
-
-static void receive_chars(struct uart_sio_port *up, int *status)
-{
- struct tty_port *port = &up->port.state->port;
- unsigned char ch;
- unsigned char flag;
- int max_count = 256;
-
- do {
- ch = sio_in(up, SIORXB);
- flag = TTY_NORMAL;
- up->port.icount.rx++;
-
- if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
- UART_LSR_FE | UART_LSR_OE))) {
- /*
- * For statistics only
- */
- if (*status & UART_LSR_BI) {
- *status &= ~(UART_LSR_FE | UART_LSR_PE);
- up->port.icount.brk++;
- /*
- * We do the SysRQ and SAK checking
- * here because otherwise the break
- * may get masked by ignore_status_mask
- * or read_status_mask.
- */
- if (uart_handle_break(&up->port))
- goto ignore_char;
- } else if (*status & UART_LSR_PE)
- up->port.icount.parity++;
- else if (*status & UART_LSR_FE)
- up->port.icount.frame++;
- if (*status & UART_LSR_OE)
- up->port.icount.overrun++;
-
- /*
- * Mask off conditions which should be ingored.
- */
- *status &= up->port.read_status_mask;
-
- if (*status & UART_LSR_BI) {
- pr_debug("handling break....\n");
- flag = TTY_BREAK;
- } else if (*status & UART_LSR_PE)
- flag = TTY_PARITY;
- else if (*status & UART_LSR_FE)
- flag = TTY_FRAME;
- }
- if (uart_handle_sysrq_char(&up->port, ch))
- goto ignore_char;
- if ((*status & up->port.ignore_status_mask) == 0)
- tty_insert_flip_char(port, ch, flag);
-
- if (*status & UART_LSR_OE) {
- /*
- * Overrun is special, since it's reported
- * immediately, and doesn't affect the current
- * character.
- */
- tty_insert_flip_char(port, 0, TTY_OVERRUN);
- }
- ignore_char:
- *status = serial_in(up, UART_LSR);
- } while ((*status & UART_LSR_DR) && (max_count-- > 0));
-
- spin_unlock(&up->port.lock);
- tty_flip_buffer_push(port);
- spin_lock(&up->port.lock);
-}
-
-static void transmit_chars(struct uart_sio_port *up)
-{
- struct circ_buf *xmit = &up->port.state->xmit;
- int count;
-
- if (up->port.x_char) {
-#ifndef CONFIG_SERIAL_M32R_PLDSIO /* XXX */
- serial_out(up, UART_TX, up->port.x_char);
-#endif
- up->port.icount.tx++;
- up->port.x_char = 0;
- return;
- }
- if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
- m32r_sio_stop_tx(&up->port);
- return;
- }
-
- count = up->port.fifosize;
- do {
- serial_out(up, UART_TX, xmit->buf[xmit->tail]);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- up->port.icount.tx++;
- if (uart_circ_empty(xmit))
- break;
- while (!(serial_in(up, UART_LSR) & UART_LSR_THRE));
-
- } while (--count > 0);
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(&up->port);
-
- pr_debug("THRE...\n");
-
- if (uart_circ_empty(xmit))
- m32r_sio_stop_tx(&up->port);
-}
-
-/*
- * This handles the interrupt from one port.
- */
-static inline void m32r_sio_handle_port(struct uart_sio_port *up,
- unsigned int status)
-{
- pr_debug("status = %x...\n", status);
-
- if (status & 0x04)
- receive_chars(up, &status);
- if (status & 0x01)
- transmit_chars(up);
-}
-
-/*
- * This is the serial driver's interrupt routine.
- *
- * Arjan thinks the old way was overly complex, so it got simplified.
- * Alan disagrees, saying that need the complexity to handle the weird
- * nature of ISA shared interrupts. (This is a special exception.)
- *
- * In order to handle ISA shared interrupts properly, we need to check
- * that all ports have been serviced, and therefore the ISA interrupt
- * line has been de-asserted.
- *
- * This means we need to loop through all ports. checking that they
- * don't have an interrupt pending.
- */
-static irqreturn_t m32r_sio_interrupt(int irq, void *dev_id)
-{
- struct irq_info *i = dev_id;
- struct list_head *l, *end = NULL;
- int pass_counter = 0;
-
- pr_debug("m32r_sio_interrupt(%d)...\n", irq);
-
-#ifdef CONFIG_SERIAL_M32R_PLDSIO
-// if (irq == PLD_IRQ_SIO0_SND)
-// irq = PLD_IRQ_SIO0_RCV;
-#else
- if (irq == M32R_IRQ_SIO0_S)
- irq = M32R_IRQ_SIO0_R;
-#endif
-
- spin_lock(&i->lock);
-
- l = i->head;
- do {
- struct uart_sio_port *up;
- unsigned int sts;
-
- up = list_entry(l, struct uart_sio_port, list);
-
- sts = sio_in(up, SIOSTS);
- if (sts & 0x5) {
- spin_lock(&up->port.lock);
- m32r_sio_handle_port(up, sts);
- spin_unlock(&up->port.lock);
-
- end = NULL;
- } else if (end == NULL)
- end = l;
-
- l = l->next;
-
- if (l == i->head && pass_counter++ > PASS_LIMIT) {
- if (sts & 0xe0)
- sio_error(&sts);
- break;
- }
- } while (l != end);
-
- spin_unlock(&i->lock);
-
- pr_debug("end.\n");
-
- return IRQ_HANDLED;
-}
-
-/*
- * To support ISA shared interrupts, we need to have one interrupt
- * handler that ensures that the IRQ line has been deasserted
- * before returning. Failing to do this will result in the IRQ
- * line being stuck active, and, since ISA irqs are edge triggered,
- * no more IRQs will be seen.
- */
-static void serial_do_unlink(struct irq_info *i, struct uart_sio_port *up)
-{
- spin_lock_irq(&i->lock);
-
- if (!list_empty(i->head)) {
- if (i->head == &up->list)
- i->head = i->head->next;
- list_del(&up->list);
- } else {
- BUG_ON(i->head != &up->list);
- i->head = NULL;
- }
-
- spin_unlock_irq(&i->lock);
-}
-
-static int serial_link_irq_chain(struct uart_sio_port *up)
-{
- struct irq_info *i = irq_lists + up->port.irq;
- int ret, irq_flags = 0;
-
- spin_lock_irq(&i->lock);
-
- if (i->head) {
- list_add(&up->list, i->head);
- spin_unlock_irq(&i->lock);
-
- ret = 0;
- } else {
- INIT_LIST_HEAD(&up->list);
- i->head = &up->list;
- spin_unlock_irq(&i->lock);
-
- ret = request_irq(up->port.irq, m32r_sio_interrupt,
- irq_flags, "SIO0-RX", i);
- ret |= request_irq(up->port.irq + 1, m32r_sio_interrupt,
- irq_flags, "SIO0-TX", i);
- if (ret < 0)
- serial_do_unlink(i, up);
- }
-
- return ret;
-}
-
-static void serial_unlink_irq_chain(struct uart_sio_port *up)
-{
- struct irq_info *i = irq_lists + up->port.irq;
-
- BUG_ON(i->head == NULL);
-
- if (list_empty(i->head)) {
- free_irq(up->port.irq, i);
- free_irq(up->port.irq + 1, i);
- }
-
- serial_do_unlink(i, up);
-}
-
-/*
- * This function is used to handle ports that do not have an interrupt.
- */
-static void m32r_sio_timeout(struct timer_list *t)
-{
- struct uart_sio_port *up = from_timer(up, t, timer);
- unsigned int timeout;
- unsigned int sts;
-
- sts = sio_in(up, SIOSTS);
- if (sts & 0x5) {
- spin_lock(&up->port.lock);
- m32r_sio_handle_port(up, sts);
- spin_unlock(&up->port.lock);
- }
-
- timeout = up->port.timeout;
- timeout = timeout > 6 ? (timeout / 2 - 2) : 1;
- mod_timer(&up->timer, jiffies + timeout);
-}
-
-static unsigned int m32r_sio_tx_empty(struct uart_port *port)
-{
- struct uart_sio_port *up =
- container_of(port, struct uart_sio_port, port);
- unsigned long flags;
- unsigned int ret;
-
- spin_lock_irqsave(&up->port.lock, flags);
- ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
- spin_unlock_irqrestore(&up->port.lock, flags);
-
- return ret;
-}
-
-static unsigned int m32r_sio_get_mctrl(struct uart_port *port)
-{
- return 0;
-}
-
-static void m32r_sio_set_mctrl(struct uart_port *port, unsigned int mctrl)
-{
-
-}
-
-static void m32r_sio_break_ctl(struct uart_port *port, int break_state)
-{
-
-}
-
-static int m32r_sio_startup(struct uart_port *port)
-{
- struct uart_sio_port *up =
- container_of(port, struct uart_sio_port, port);
- int retval;
-
- sio_init();
-
- /*
- * If the "interrupt" for this port doesn't correspond with any
- * hardware interrupt, we use a timer-based system. The original
- * driver used to do this with IRQ0.
- */
- if (!up->port.irq) {
- unsigned int timeout = up->port.timeout;
-
- timeout = timeout > 6 ? (timeout / 2 - 2) : 1;
-
- mod_timer(&up->timer, jiffies + timeout);
- } else {
- retval = serial_link_irq_chain(up);
- if (retval)
- return retval;
- }
-
- /*
- * Finally, enable interrupts. Note: Modem status interrupts
- * are set via set_termios(), which will be occurring imminently
- * anyway, so we don't enable them here.
- * - M32R_SIO: 0x0c
- * - M32R_PLDSIO: 0x04
- */
- up->ier = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
- sio_out(up, SIOTRCR, up->ier);
-
- /*
- * And clear the interrupt registers again for luck.
- */
- sio_reset();
-
- return 0;
-}
-
-static void m32r_sio_shutdown(struct uart_port *port)
-{
- struct uart_sio_port *up =
- container_of(port, struct uart_sio_port, port);
-
- /*
- * Disable interrupts from this port
- */
- up->ier = 0;
- sio_out(up, SIOTRCR, 0);
-
- /*
- * Disable break condition and FIFOs
- */
-
- sio_init();
-
- if (!up->port.irq)
- del_timer_sync(&up->timer);
- else
- serial_unlink_irq_chain(up);
-}
-
-static unsigned int m32r_sio_get_divisor(struct uart_port *port,
- unsigned int baud)
-{
- return uart_get_divisor(port, baud);
-}
-
-static void m32r_sio_set_termios(struct uart_port *port,
- struct ktermios *termios, struct ktermios *old)
-{
- struct uart_sio_port *up =
- container_of(port, struct uart_sio_port, port);
- unsigned char cval = 0;
- unsigned long flags;
- unsigned int baud, quot;
-
- switch (termios->c_cflag & CSIZE) {
- case CS5:
- cval = UART_LCR_WLEN5;
- break;
- case CS6:
- cval = UART_LCR_WLEN6;
- break;
- case CS7:
- cval = UART_LCR_WLEN7;
- break;
- default:
- case CS8:
- cval = UART_LCR_WLEN8;
- break;
- }
-
- if (termios->c_cflag & CSTOPB)
- cval |= UART_LCR_STOP;
- if (termios->c_cflag & PARENB)
- cval |= UART_LCR_PARITY;
- if (!(termios->c_cflag & PARODD))
- cval |= UART_LCR_EPAR;
-#ifdef CMSPAR
- if (termios->c_cflag & CMSPAR)
- cval |= UART_LCR_SPAR;
-#endif
-
- /*
- * Ask the core to calculate the divisor for us.
- */
-#ifdef CONFIG_SERIAL_M32R_PLDSIO
- baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/4);
-#else
- baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
-#endif
- quot = m32r_sio_get_divisor(port, baud);
-
- /*
- * Ok, we're now changing the port state. Do it with
- * interrupts disabled.
- */
- spin_lock_irqsave(&up->port.lock, flags);
-
- sio_set_baud_rate(baud);
-
- /*
- * Update the per-port timeout.
- */
- uart_update_timeout(port, termios->c_cflag, baud);
-
- up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
- if (termios->c_iflag & INPCK)
- up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
- if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
- up->port.read_status_mask |= UART_LSR_BI;
-
- /*
- * Characteres to ignore
- */
- up->port.ignore_status_mask = 0;
- if (termios->c_iflag & IGNPAR)
- up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
- if (termios->c_iflag & IGNBRK) {
- up->port.ignore_status_mask |= UART_LSR_BI;
- /*
- * If we're ignoring parity and break indicators,
- * ignore overruns too (for real raw support).
- */
- if (termios->c_iflag & IGNPAR)
- up->port.ignore_status_mask |= UART_LSR_OE;
- }
-
- /*
- * ignore all characters if CREAD is not set
- */
- if ((termios->c_cflag & CREAD) == 0)
- up->port.ignore_status_mask |= UART_LSR_DR;
-
- /*
- * CTS flow control flag and modem status interrupts
- */
- up->ier &= ~UART_IER_MSI;
- if (UART_ENABLE_MS(&up->port, termios->c_cflag))
- up->ier |= UART_IER_MSI;
-
- serial_out(up, UART_IER, up->ier);
-
- spin_unlock_irqrestore(&up->port.lock, flags);
-}
-
-/*
- * Resource handling. This is complicated by the fact that resources
- * depend on the port type. Maybe we should be claiming the standard
- * 8250 ports, and then trying to get other resources as necessary?
- */
-static int
-m32r_sio_request_std_resource(struct uart_sio_port *up, struct resource **res)
-{
- unsigned int size = 8 << up->port.regshift;
-#ifndef CONFIG_SERIAL_M32R_PLDSIO
- unsigned long start;
-#endif
- int ret = 0;
-
- switch (up->port.iotype) {
- case UPIO_MEM:
- if (up->port.mapbase) {
-#ifdef CONFIG_SERIAL_M32R_PLDSIO
- *res = request_mem_region(up->port.mapbase, size, "serial");
-#else
- start = up->port.mapbase;
- *res = request_mem_region(start, size, "serial");
-#endif
- if (!*res)
- ret = -EBUSY;
- }
- break;
-
- case UPIO_PORT:
- *res = request_region(up->port.iobase, size, "serial");
- if (!*res)
- ret = -EBUSY;
- break;
- }
- return ret;
-}
-
-static void m32r_sio_release_port(struct uart_port *port)
-{
- struct uart_sio_port *up =
- container_of(port, struct uart_sio_port, port);
- unsigned long start, offset = 0, size = 0;
-
- size <<= up->port.regshift;
-
- switch (up->port.iotype) {
- case UPIO_MEM:
- if (up->port.mapbase) {
- /*
- * Unmap the area.
- */
- iounmap(up->port.membase);
- up->port.membase = NULL;
-
- start = up->port.mapbase;
-
- if (size)
- release_mem_region(start + offset, size);
- release_mem_region(start, 8 << up->port.regshift);
- }
- break;
-
- case UPIO_PORT:
- start = up->port.iobase;
-
- if (size)
- release_region(start + offset, size);
- release_region(start + offset, 8 << up->port.regshift);
- break;
-
- default:
- break;
- }
-}
-
-static int m32r_sio_request_port(struct uart_port *port)
-{
- struct uart_sio_port *up =
- container_of(port, struct uart_sio_port, port);
- struct resource *res = NULL;
- int ret = 0;
-
- ret = m32r_sio_request_std_resource(up, &res);
-
- /*
- * If we have a mapbase, then request that as well.
- */
- if (ret == 0 && up->port.flags & UPF_IOREMAP) {
- int size = resource_size(res);
-
- up->port.membase = ioremap(up->port.mapbase, size);
- if (!up->port.membase)
- ret = -ENOMEM;
- }
-
- if (ret < 0) {
- if (res)
- release_resource(res);
- }
-
- return ret;
-}
-
-static void m32r_sio_config_port(struct uart_port *port, int unused)
-{
- struct uart_sio_port *up =
- container_of(port, struct uart_sio_port, port);
- unsigned long flags;
-
- spin_lock_irqsave(&up->port.lock, flags);
-
- up->port.fifosize = 1;
-
- spin_unlock_irqrestore(&up->port.lock, flags);
-}
-
-static int
-m32r_sio_verify_port(struct uart_port *port, struct serial_struct *ser)
-{
- if (ser->irq >= nr_irqs || ser->irq < 0 || ser->baud_base < 9600)
- return -EINVAL;
- return 0;
-}
-
-static const struct uart_ops m32r_sio_pops = {
- .tx_empty = m32r_sio_tx_empty,
- .set_mctrl = m32r_sio_set_mctrl,
- .get_mctrl = m32r_sio_get_mctrl,
- .stop_tx = m32r_sio_stop_tx,
- .start_tx = m32r_sio_start_tx,
- .stop_rx = m32r_sio_stop_rx,
- .enable_ms = m32r_sio_enable_ms,
- .break_ctl = m32r_sio_break_ctl,
- .startup = m32r_sio_startup,
- .shutdown = m32r_sio_shutdown,
- .set_termios = m32r_sio_set_termios,
- .release_port = m32r_sio_release_port,
- .request_port = m32r_sio_request_port,
- .config_port = m32r_sio_config_port,
- .verify_port = m32r_sio_verify_port,
-};
-
-static struct uart_sio_port m32r_sio_ports[UART_NR];
-
-static void __init m32r_sio_init_ports(void)
-{
- struct uart_sio_port *up;
- static int first = 1;
- int i;
-
- if (!first)
- return;
- first = 0;
-
- for (i = 0, up = m32r_sio_ports; i < UART_NR; i++, up++) {
- up->port.iobase = old_serial_port[i].port;
- up->port.irq = irq_canonicalize(old_serial_port[i].irq);
- up->port.uartclk = BAUD_RATE * 16;
- up->port.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST;
- up->port.membase = 0;
- up->port.iotype = 0;
- up->port.regshift = 0;
- up->port.ops = &m32r_sio_pops;
- }
-}
-
-static void __init m32r_sio_register_ports(struct uart_driver *drv)
-{
- int i;
-
- m32r_sio_init_ports();
-
- for (i = 0; i < UART_NR; i++) {
- struct uart_sio_port *up = &m32r_sio_ports[i];
-
- up->port.line = i;
- up->port.ops = &m32r_sio_pops;
- timer_setup(&up->timer, m32r_sio_timeout, 0);
-
- uart_add_one_port(drv, &up->port);
- }
-}
-
-#ifdef CONFIG_SERIAL_M32R_SIO_CONSOLE
-
-/*
- * Wait for transmitter & holding register to empty
- */
-static void wait_for_xmitr(struct uart_sio_port *up)
-{
- unsigned int status, tmout = 10000;
-
- /* Wait up to 10ms for the character(s) to be sent. */
- do {
- status = sio_in(up, SIOSTS);
-
- if (--tmout == 0)
- break;
- udelay(1);
- } while ((status & UART_EMPTY) != UART_EMPTY);
-
- /* Wait up to 1s for flow control if necessary */
- if (up->port.flags & UPF_CONS_FLOW) {
- tmout = 1000000;
- while (--tmout)
- udelay(1);
- }
-}
-
-static void m32r_sio_console_putchar(struct uart_port *port, int ch)
-{
- struct uart_sio_port *up =
- container_of(port, struct uart_sio_port, port);
-
- wait_for_xmitr(up);
- sio_out(up, SIOTXB, ch);
-}
-
-/*
- * Print a string to the serial port trying not to disturb
- * any possible real use of the port...
- *
- * The console_lock must be held when we get here.
- */
-static void m32r_sio_console_write(struct console *co, const char *s,
- unsigned int count)
-{
- struct uart_sio_port *up = &m32r_sio_ports[co->index];
- unsigned int ier;
-
- /*
- * First save the UER then disable the interrupts
- */
- ier = sio_in(up, SIOTRCR);
- sio_out(up, SIOTRCR, 0);
-
- uart_console_write(&up->port, s, count, m32r_sio_console_putchar);
-
- /*
- * Finally, wait for transmitter to become empty
- * and restore the IER
- */
- wait_for_xmitr(up);
- sio_out(up, SIOTRCR, ier);
-}
-
-static int __init m32r_sio_console_setup(struct console *co, char *options)
-{
- struct uart_port *port;
- int baud = 9600;
- int bits = 8;
- int parity = 'n';
- int flow = 'n';
-
- /*
- * 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 >= UART_NR)
- co->index = 0;
- port = &m32r_sio_ports[co->index].port;
-
- /*
- * Temporary fix.
- */
- spin_lock_init(&port->lock);
-
- if (options)
- uart_parse_options(options, &baud, &parity, &bits, &flow);
-
- return uart_set_options(port, co, baud, parity, bits, flow);
-}
-
-static struct uart_driver m32r_sio_reg;
-static struct console m32r_sio_console = {
- .name = "ttyS",
- .write = m32r_sio_console_write,
- .device = uart_console_device,
- .setup = m32r_sio_console_setup,
- .flags = CON_PRINTBUFFER,
- .index = -1,
- .data = &m32r_sio_reg,
-};
-
-static int __init m32r_sio_console_init(void)
-{
- sio_reset();
- sio_init();
- m32r_sio_init_ports();
- register_console(&m32r_sio_console);
- return 0;
-}
-console_initcall(m32r_sio_console_init);
-
-#define M32R_SIO_CONSOLE &m32r_sio_console
-#else
-#define M32R_SIO_CONSOLE NULL
-#endif
-
-static struct uart_driver m32r_sio_reg = {
- .owner = THIS_MODULE,
- .driver_name = "sio",
- .dev_name = "ttyS",
- .major = TTY_MAJOR,
- .minor = 64,
- .nr = UART_NR,
- .cons = M32R_SIO_CONSOLE,
-};
-
-static int __init m32r_sio_init(void)
-{
- int ret, i;
-
- printk(KERN_INFO "Serial: M32R SIO driver\n");
-
- for (i = 0; i < nr_irqs; i++)
- spin_lock_init(&irq_lists[i].lock);
-
- ret = uart_register_driver(&m32r_sio_reg);
- if (ret >= 0)
- m32r_sio_register_ports(&m32r_sio_reg);
-
- return ret;
-}
-device_initcall(m32r_sio_init);
diff --git a/drivers/tty/serial/m32r_sio_reg.h b/drivers/tty/serial/m32r_sio_reg.h
deleted file mode 100644
index 6eed48828f94..000000000000
--- a/drivers/tty/serial/m32r_sio_reg.h
+++ /dev/null
@@ -1,150 +0,0 @@
-// SPDX-License-Identifier: GPL-1.0+
-/*
- * m32r_sio_reg.h
- *
- * Copyright (C) 1992, 1994 by Theodore Ts'o.
- * Copyright (C) 2004 Hirokazu Takata <takata at linux-m32r.org>
- *
- * These are the UART port assignments, expressed as offsets from the base
- * register. These assignments should hold for any serial port based on
- * a 8250, 16450, or 16550(A).
- */
-
-#ifndef _M32R_SIO_REG_H
-#define _M32R_SIO_REG_H
-
-
-#ifdef CONFIG_SERIAL_M32R_PLDSIO
-
-#define SIOCR 0x000
-#define SIOMOD0 0x002
-#define SIOMOD1 0x004
-#define SIOSTS 0x006
-#define SIOTRCR 0x008
-#define SIOBAUR 0x00a
-// #define SIORBAUR 0x018
-#define SIOTXB 0x00c
-#define SIORXB 0x00e
-
-#define UART_RX ((unsigned long) PLD_ESIO0RXB)
- /* In: Receive buffer (DLAB=0) */
-#define UART_TX ((unsigned long) PLD_ESIO0TXB)
- /* Out: Transmit buffer (DLAB=0) */
-#define UART_DLL 0 /* Out: Divisor Latch Low (DLAB=1) */
-#define UART_TRG 0 /* (LCR=BF) FCTR bit 7 selects Rx or Tx
- * In: Fifo count
- * Out: Fifo custom trigger levels
- * XR16C85x only */
-
-#define UART_DLM 0 /* Out: Divisor Latch High (DLAB=1) */
-#define UART_IER ((unsigned long) PLD_ESIO0INTCR)
- /* Out: Interrupt Enable Register */
-#define UART_FCTR 0 /* (LCR=BF) Feature Control Register
- * XR16C85x only */
-
-#define UART_IIR 0 /* In: Interrupt ID Register */
-#define UART_FCR 0 /* Out: FIFO Control Register */
-#define UART_EFR 0 /* I/O: Extended Features Register */
- /* (DLAB=1, 16C660 only) */
-
-#define UART_LCR 0 /* Out: Line Control Register */
-#define UART_MCR 0 /* Out: Modem Control Register */
-#define UART_LSR ((unsigned long) PLD_ESIO0STS)
- /* In: Line Status Register */
-#define UART_MSR 0 /* In: Modem Status Register */
-#define UART_SCR 0 /* I/O: Scratch Register */
-#define UART_EMSR 0 /* (LCR=BF) Extended Mode Select Register
- * FCTR bit 6 selects SCR or EMSR
- * XR16c85x only */
-
-#else /* not CONFIG_SERIAL_M32R_PLDSIO */
-
-#define SIOCR 0x000
-#define SIOMOD0 0x004
-#define SIOMOD1 0x008
-#define SIOSTS 0x00c
-#define SIOTRCR 0x010
-#define SIOBAUR 0x014
-#define SIORBAUR 0x018
-#define SIOTXB 0x01c
-#define SIORXB 0x020
-
-#define UART_RX M32R_SIO0_RXB_PORTL /* In: Receive buffer (DLAB=0) */
-#define UART_TX M32R_SIO0_TXB_PORTL /* Out: Transmit buffer (DLAB=0) */
-#define UART_DLL 0 /* Out: Divisor Latch Low (DLAB=1) */
-#define UART_TRG 0 /* (LCR=BF) FCTR bit 7 selects Rx or Tx
- * In: Fifo count
- * Out: Fifo custom trigger levels
- * XR16C85x only */
-
-#define UART_DLM 0 /* Out: Divisor Latch High (DLAB=1) */
-#define UART_IER M32R_SIO0_TRCR_PORTL /* Out: Interrupt Enable Register */
-#define UART_FCTR 0 /* (LCR=BF) Feature Control Register
- * XR16C85x only */
-
-#define UART_IIR 0 /* In: Interrupt ID Register */
-#define UART_FCR 0 /* Out: FIFO Control Register */
-#define UART_EFR 0 /* I/O: Extended Features Register */
- /* (DLAB=1, 16C660 only) */
-
-#define UART_LCR 0 /* Out: Line Control Register */
-#define UART_MCR 0 /* Out: Modem Control Register */
-#define UART_LSR M32R_SIO0_STS_PORTL /* In: Line Status Register */
-#define UART_MSR 0 /* In: Modem Status Register */
-#define UART_SCR 0 /* I/O: Scratch Register */
-#define UART_EMSR 0 /* (LCR=BF) Extended Mode Select Register
- * FCTR bit 6 selects SCR or EMSR
- * XR16c85x only */
-
-#endif /* CONFIG_SERIAL_M32R_PLDSIO */
-
-#define UART_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
-
-/*
- * These are the definitions for the Line Control Register
- *
- * Note: if the word length is 5 bits (UART_LCR_WLEN5), then setting
- * UART_LCR_STOP will select 1.5 stop bits, not 2 stop bits.
- */
-#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */
-#define UART_LCR_SBC 0x40 /* Set break control */
-#define UART_LCR_SPAR 0x20 /* Stick parity (?) */
-#define UART_LCR_EPAR 0x10 /* Even parity select */
-#define UART_LCR_PARITY 0x08 /* Parity Enable */
-#define UART_LCR_STOP 0x04 /* Stop bits: 0=1 stop bit, 1= 2 stop bits */
-#define UART_LCR_WLEN5 0x00 /* Wordlength: 5 bits */
-#define UART_LCR_WLEN6 0x01 /* Wordlength: 6 bits */
-#define UART_LCR_WLEN7 0x02 /* Wordlength: 7 bits */
-#define UART_LCR_WLEN8 0x03 /* Wordlength: 8 bits */
-
-/*
- * These are the definitions for the Line Status Register
- */
-#define UART_LSR_TEMT 0x02 /* Transmitter empty */
-#define UART_LSR_THRE 0x01 /* Transmit-hold-register empty */
-#define UART_LSR_BI 0x00 /* Break interrupt indicator */
-#define UART_LSR_FE 0x80 /* Frame error indicator */
-#define UART_LSR_PE 0x40 /* Parity error indicator */
-#define UART_LSR_OE 0x20 /* Overrun error indicator */
-#define UART_LSR_DR 0x04 /* Receiver data ready */
-
-/*
- * These are the definitions for the Interrupt Identification Register
- */
-#define UART_IIR_NO_INT 0x01 /* No interrupts pending */
-#define UART_IIR_ID 0x06 /* Mask for the interrupt ID */
-
-#define UART_IIR_MSI 0x00 /* Modem status interrupt */
-#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */
-#define UART_IIR_RDI 0x04 /* Receiver data interrupt */
-#define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */
-
-/*
- * These are the definitions for the Interrupt Enable Register
- */
-#define UART_IER_MSI 0x00 /* Enable Modem status interrupt */
-#define UART_IER_RLSI 0x08 /* Enable receiver line status interrupt */
-#define UART_IER_THRI 0x03 /* Enable Transmitter holding register int. */
-#define UART_IER_RDI 0x04 /* Enable receiver data interrupt */
-
-#endif /* _M32R_SIO_REG_H */
diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c
index 39f635812077..efe55a1a0615 100644
--- a/drivers/tty/serial/max310x.c
+++ b/drivers/tty/serial/max310x.c
@@ -1318,7 +1318,7 @@ static int max310x_probe(struct device *dev, struct max310x_devtype *devtype,
/* Setup GPIO cotroller */
s->gpio.owner = THIS_MODULE;
s->gpio.parent = dev;
- s->gpio.label = dev_name(dev);
+ s->gpio.label = devtype->name;
s->gpio.direction_input = max310x_gpio_direction_input;
s->gpio.get = max310x_gpio_get;
s->gpio.direction_output= max310x_gpio_direction_output;
diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c
index a100e98259d7..f503fab1e268 100644
--- a/drivers/tty/serial/mvebu-uart.c
+++ b/drivers/tty/serial/mvebu-uart.c
@@ -65,7 +65,7 @@
#define STAT_FRM_ERR BIT(2)
#define STAT_PAR_ERR BIT(1)
#define STAT_OVR_ERR BIT(0)
-#define STAT_BRK_ERR (STAT_BRK_DET | STAT_FRM_ERR | STAT_FRM_ERR\
+#define STAT_BRK_ERR (STAT_BRK_DET | STAT_FRM_ERR \
| STAT_PAR_ERR | STAT_OVR_ERR)
#define UART_BRDV 0x10
@@ -495,7 +495,6 @@ static void mvebu_uart_set_termios(struct uart_port *port,
termios->c_iflag |= old->c_iflag & ~(INPCK | IGNPAR);
termios->c_cflag &= CREAD | CBAUD;
termios->c_cflag |= old->c_cflag & ~(CREAD | CBAUD);
- termios->c_lflag = old->c_lflag;
}
spin_unlock_irqrestore(&port->lock, flags);
@@ -618,7 +617,7 @@ static void wait_for_xmitr(struct uart_port *port)
u32 val;
readl_poll_timeout_atomic(port->membase + UART_STAT, val,
- (val & STAT_TX_EMP), 1, 10000);
+ (val & STAT_TX_RDY(port)), 1, 10000);
}
static void mvebu_uart_console_putchar(struct uart_port *port, int ch)
diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
index 079dc47aa142..76aa289652f7 100644
--- a/drivers/tty/serial/mxs-auart.c
+++ b/drivers/tty/serial/mxs-auart.c
@@ -1663,6 +1663,10 @@ static int mxs_auart_probe(struct platform_device *pdev)
s->port.line = pdev->id < 0 ? 0 : pdev->id;
else if (ret < 0)
return ret;
+ if (s->port.line >= ARRAY_SIZE(auart_port)) {
+ dev_err(&pdev->dev, "serial%d out of range\n", s->port.line);
+ return -EINVAL;
+ }
if (of_id) {
pdev->id_entry = of_id->data;
@@ -1674,8 +1678,10 @@ static int mxs_auart_probe(struct platform_device *pdev)
return ret;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!r)
- return -ENXIO;
+ if (!r) {
+ ret = -ENXIO;
+ goto out_disable_clks;
+ }
s->port.mapbase = r->start;
s->port.membase = ioremap(r->start, resource_size(r));
@@ -1690,21 +1696,23 @@ static int mxs_auart_probe(struct platform_device *pdev)
s->mctrl_prev = 0;
irq = platform_get_irq(pdev, 0);
- if (irq < 0)
- return irq;
+ if (irq < 0) {
+ ret = irq;
+ goto out_disable_clks;
+ }
s->port.irq = irq;
ret = devm_request_irq(&pdev->dev, irq, mxs_auart_irq_handle, 0,
dev_name(&pdev->dev), s);
if (ret)
- return ret;
+ goto out_disable_clks;
platform_set_drvdata(pdev, s);
ret = mxs_auart_init_gpios(s, &pdev->dev);
if (ret) {
dev_err(&pdev->dev, "Failed to initialize GPIOs.\n");
- return ret;
+ goto out_disable_clks;
}
/*
@@ -1712,7 +1720,7 @@ static int mxs_auart_probe(struct platform_device *pdev)
*/
ret = mxs_auart_request_gpio_irq(s);
if (ret)
- return ret;
+ goto out_disable_clks;
auart_port[s->port.line] = s;
@@ -1720,7 +1728,7 @@ static int mxs_auart_probe(struct platform_device *pdev)
ret = uart_add_one_port(&auart_driver, &s->port);
if (ret)
- goto out_disable_clks_free_qpio_irq;
+ goto out_free_qpio_irq;
/* ASM9260 don't have version reg */
if (is_asm9260_auart(s)) {
@@ -1734,13 +1742,15 @@ static int mxs_auart_probe(struct platform_device *pdev)
return 0;
-out_disable_clks_free_qpio_irq:
- if (s->clk)
- clk_disable_unprepare(s->clk_ahb);
- if (s->clk_ahb)
- clk_disable_unprepare(s->clk_ahb);
+out_free_qpio_irq:
mxs_auart_free_gpio_irq(s);
auart_port[pdev->id] = NULL;
+
+out_disable_clks:
+ if (is_asm9260_auart(s)) {
+ clk_disable_unprepare(s->clk);
+ clk_disable_unprepare(s->clk_ahb);
+ }
return ret;
}
@@ -1751,6 +1761,10 @@ static int mxs_auart_remove(struct platform_device *pdev)
uart_remove_one_port(&auart_driver, &s->port);
auart_port[pdev->id] = NULL;
mxs_auart_free_gpio_irq(s);
+ if (is_asm9260_auart(s)) {
+ clk_disable_unprepare(s->clk);
+ clk_disable_unprepare(s->clk_ahb);
+ }
return 0;
}
diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c
index baf552944d56..eda3c7710d6a 100644
--- a/drivers/tty/serial/pxa.c
+++ b/drivers/tty/serial/pxa.c
@@ -885,6 +885,10 @@ static int serial_pxa_probe(struct platform_device *dev)
sport->port.line = dev->id;
else if (ret < 0)
goto err_clk;
+ if (sport->port.line >= ARRAY_SIZE(serial_pxa_ports)) {
+ dev_err(&dev->dev, "serial%d out of range\n", sport->port.line);
+ return -EINVAL;
+ }
snprintf(sport->name, PXA_NAME_LEN - 1, "UART%d", sport->port.line + 1);
sport->port.membase = ioremap(mmres->start, resource_size(mmres));
diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
new file mode 100644
index 000000000000..a1b3eb04cb32
--- /dev/null
+++ b/drivers/tty/serial/qcom_geni_serial.c
@@ -0,0 +1,1160 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
+
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/qcom-geni-se.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+/* UART specific GENI registers */
+#define SE_UART_TX_TRANS_CFG 0x25c
+#define SE_UART_TX_WORD_LEN 0x268
+#define SE_UART_TX_STOP_BIT_LEN 0x26c
+#define SE_UART_TX_TRANS_LEN 0x270
+#define SE_UART_RX_TRANS_CFG 0x280
+#define SE_UART_RX_WORD_LEN 0x28c
+#define SE_UART_RX_STALE_CNT 0x294
+#define SE_UART_TX_PARITY_CFG 0x2a4
+#define SE_UART_RX_PARITY_CFG 0x2a8
+
+/* SE_UART_TRANS_CFG */
+#define UART_TX_PAR_EN BIT(0)
+#define UART_CTS_MASK BIT(1)
+
+/* SE_UART_TX_WORD_LEN */
+#define TX_WORD_LEN_MSK GENMASK(9, 0)
+
+/* SE_UART_TX_STOP_BIT_LEN */
+#define TX_STOP_BIT_LEN_MSK GENMASK(23, 0)
+#define TX_STOP_BIT_LEN_1 0
+#define TX_STOP_BIT_LEN_1_5 1
+#define TX_STOP_BIT_LEN_2 2
+
+/* SE_UART_TX_TRANS_LEN */
+#define TX_TRANS_LEN_MSK GENMASK(23, 0)
+
+/* SE_UART_RX_TRANS_CFG */
+#define UART_RX_INS_STATUS_BIT BIT(2)
+#define UART_RX_PAR_EN BIT(3)
+
+/* SE_UART_RX_WORD_LEN */
+#define RX_WORD_LEN_MASK GENMASK(9, 0)
+
+/* SE_UART_RX_STALE_CNT */
+#define RX_STALE_CNT GENMASK(23, 0)
+
+/* SE_UART_TX_PARITY_CFG/RX_PARITY_CFG */
+#define PAR_CALC_EN BIT(0)
+#define PAR_MODE_MSK GENMASK(2, 1)
+#define PAR_MODE_SHFT 1
+#define PAR_EVEN 0x00
+#define PAR_ODD 0x01
+#define PAR_SPACE 0x10
+#define PAR_MARK 0x11
+
+/* UART M_CMD OP codes */
+#define UART_START_TX 0x1
+#define UART_START_BREAK 0x4
+#define UART_STOP_BREAK 0x5
+/* UART S_CMD OP codes */
+#define UART_START_READ 0x1
+#define UART_PARAM 0x1
+
+#define UART_OVERSAMPLING 32
+#define STALE_TIMEOUT 16
+#define DEFAULT_BITS_PER_CHAR 10
+#define GENI_UART_CONS_PORTS 1
+#define DEF_FIFO_DEPTH_WORDS 16
+#define DEF_TX_WM 2
+#define DEF_FIFO_WIDTH_BITS 32
+#define UART_CONSOLE_RX_WM 2
+
+#ifdef CONFIG_CONSOLE_POLL
+#define RX_BYTES_PW 1
+#else
+#define RX_BYTES_PW 4
+#endif
+
+struct qcom_geni_serial_port {
+ struct uart_port uport;
+ struct geni_se se;
+ char name[20];
+ u32 tx_fifo_depth;
+ u32 tx_fifo_width;
+ u32 rx_fifo_depth;
+ u32 tx_wm;
+ u32 rx_wm;
+ u32 rx_rfr;
+ enum geni_se_xfer_mode xfer_mode;
+ bool setup;
+ int (*handle_rx)(struct uart_port *uport, u32 bytes, bool drop);
+ unsigned int xmit_size;
+ unsigned int baud;
+ unsigned int tx_bytes_pw;
+ unsigned int rx_bytes_pw;
+ bool brk;
+};
+
+static const struct uart_ops qcom_geni_serial_pops;
+static struct uart_driver qcom_geni_console_driver;
+static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop);
+static unsigned int qcom_geni_serial_tx_empty(struct uart_port *port);
+static void qcom_geni_serial_stop_rx(struct uart_port *uport);
+
+static const unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200,
+ 32000000, 48000000, 64000000, 80000000,
+ 96000000, 100000000};
+
+#define to_dev_port(ptr, member) \
+ container_of(ptr, struct qcom_geni_serial_port, member)
+
+static struct qcom_geni_serial_port qcom_geni_console_port;
+
+static int qcom_geni_serial_request_port(struct uart_port *uport)
+{
+ struct platform_device *pdev = to_platform_device(uport->dev);
+ struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ uport->membase = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(uport->membase))
+ return PTR_ERR(uport->membase);
+ port->se.base = uport->membase;
+ return 0;
+}
+
+static void qcom_geni_serial_config_port(struct uart_port *uport, int cfg_flags)
+{
+ if (cfg_flags & UART_CONFIG_TYPE) {
+ uport->type = PORT_MSM;
+ qcom_geni_serial_request_port(uport);
+ }
+}
+
+static unsigned int qcom_geni_cons_get_mctrl(struct uart_port *uport)
+{
+ return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS;
+}
+
+static void qcom_geni_cons_set_mctrl(struct uart_port *uport,
+ unsigned int mctrl)
+{
+}
+
+static const char *qcom_geni_serial_get_type(struct uart_port *uport)
+{
+ return "MSM";
+}
+
+static struct qcom_geni_serial_port *get_port_from_line(int line)
+{
+ if (line < 0 || line >= GENI_UART_CONS_PORTS)
+ return ERR_PTR(-ENXIO);
+ return &qcom_geni_console_port;
+}
+
+static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
+ int offset, int field, bool set)
+{
+ u32 reg;
+ struct qcom_geni_serial_port *port;
+ unsigned int baud;
+ unsigned int fifo_bits;
+ unsigned long timeout_us = 20000;
+
+ /* Ensure polling is not re-ordered before the prior writes/reads */
+ mb();
+
+ if (uport->private_data) {
+ port = to_dev_port(uport, uport);
+ baud = port->baud;
+ if (!baud)
+ baud = 115200;
+ fifo_bits = port->tx_fifo_depth * port->tx_fifo_width;
+ /*
+ * Total polling iterations based on FIFO worth of bytes to be
+ * sent at current baud. Add a little fluff to the wait.
+ */
+ timeout_us = ((fifo_bits * USEC_PER_SEC) / baud) + 500;
+ }
+
+ return !readl_poll_timeout_atomic(uport->membase + offset, reg,
+ (bool)(reg & field) == set, 10, timeout_us);
+}
+
+static void qcom_geni_serial_setup_tx(struct uart_port *uport, u32 xmit_size)
+{
+ u32 m_cmd;
+
+ writel_relaxed(xmit_size, uport->membase + SE_UART_TX_TRANS_LEN);
+ m_cmd = UART_START_TX << M_OPCODE_SHFT;
+ writel(m_cmd, uport->membase + SE_GENI_M_CMD0);
+}
+
+static void qcom_geni_serial_poll_tx_done(struct uart_port *uport)
+{
+ int done;
+ u32 irq_clear = M_CMD_DONE_EN;
+
+ done = qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+ M_CMD_DONE_EN, true);
+ if (!done) {
+ writel_relaxed(M_GENI_CMD_ABORT, uport->membase +
+ SE_GENI_M_CMD_CTRL_REG);
+ irq_clear |= M_CMD_ABORT_EN;
+ qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+ M_CMD_ABORT_EN, true);
+ }
+ writel_relaxed(irq_clear, uport->membase + SE_GENI_M_IRQ_CLEAR);
+}
+
+static void qcom_geni_serial_abort_rx(struct uart_port *uport)
+{
+ u32 irq_clear = S_CMD_DONE_EN | S_CMD_ABORT_EN;
+
+ writel(S_GENI_CMD_ABORT, uport->membase + SE_GENI_S_CMD_CTRL_REG);
+ qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
+ S_GENI_CMD_ABORT, false);
+ writel_relaxed(irq_clear, uport->membase + SE_GENI_S_IRQ_CLEAR);
+ writel_relaxed(FORCE_DEFAULT, uport->membase + GENI_FORCE_DEFAULT_REG);
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+static int qcom_geni_serial_get_char(struct uart_port *uport)
+{
+ u32 rx_fifo;
+ u32 status;
+
+ status = readl_relaxed(uport->membase + SE_GENI_M_IRQ_STATUS);
+ writel_relaxed(status, uport->membase + SE_GENI_M_IRQ_CLEAR);
+
+ status = readl_relaxed(uport->membase + SE_GENI_S_IRQ_STATUS);
+ writel_relaxed(status, uport->membase + SE_GENI_S_IRQ_CLEAR);
+
+ /*
+ * Ensure the writes to clear interrupts is not re-ordered after
+ * reading the data.
+ */
+ mb();
+
+ status = readl_relaxed(uport->membase + SE_GENI_RX_FIFO_STATUS);
+ if (!(status & RX_FIFO_WC_MSK))
+ return NO_POLL_CHAR;
+
+ rx_fifo = readl(uport->membase + SE_GENI_RX_FIFOn);
+ return rx_fifo & 0xff;
+}
+
+static void qcom_geni_serial_poll_put_char(struct uart_port *uport,
+ unsigned char c)
+{
+ struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+
+ writel_relaxed(port->tx_wm, uport->membase + SE_GENI_TX_WATERMARK_REG);
+ qcom_geni_serial_setup_tx(uport, 1);
+ WARN_ON(!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+ M_TX_FIFO_WATERMARK_EN, true));
+ writel_relaxed(c, uport->membase + SE_GENI_TX_FIFOn);
+ writel_relaxed(M_TX_FIFO_WATERMARK_EN, uport->membase +
+ SE_GENI_M_IRQ_CLEAR);
+ qcom_geni_serial_poll_tx_done(uport);
+}
+#endif
+
+#ifdef CONFIG_SERIAL_QCOM_GENI_CONSOLE
+static void qcom_geni_serial_wr_char(struct uart_port *uport, int ch)
+{
+ writel_relaxed(ch, uport->membase + SE_GENI_TX_FIFOn);
+}
+
+static void
+__qcom_geni_serial_console_write(struct uart_port *uport, const char *s,
+ unsigned int count)
+{
+ int i;
+ u32 bytes_to_send = count;
+
+ for (i = 0; i < count; i++) {
+ if (s[i] == '\n')
+ bytes_to_send++;
+ }
+
+ writel_relaxed(DEF_TX_WM, uport->membase + SE_GENI_TX_WATERMARK_REG);
+ qcom_geni_serial_setup_tx(uport, bytes_to_send);
+ for (i = 0; i < count; ) {
+ size_t chars_to_write = 0;
+ size_t avail = DEF_FIFO_DEPTH_WORDS - DEF_TX_WM;
+
+ /*
+ * If the WM bit never set, then the Tx state machine is not
+ * in a valid state, so break, cancel/abort any existing
+ * command. Unfortunately the current data being written is
+ * lost.
+ */
+ if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+ M_TX_FIFO_WATERMARK_EN, true))
+ break;
+ chars_to_write = min_t(size_t, (size_t)(count - i), avail / 2);
+ uart_console_write(uport, s + i, chars_to_write,
+ qcom_geni_serial_wr_char);
+ writel_relaxed(M_TX_FIFO_WATERMARK_EN, uport->membase +
+ SE_GENI_M_IRQ_CLEAR);
+ i += chars_to_write;
+ }
+ qcom_geni_serial_poll_tx_done(uport);
+}
+
+static void qcom_geni_serial_console_write(struct console *co, const char *s,
+ unsigned int count)
+{
+ struct uart_port *uport;
+ struct qcom_geni_serial_port *port;
+ bool locked = true;
+ unsigned long flags;
+
+ WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS);
+
+ port = get_port_from_line(co->index);
+ if (IS_ERR(port))
+ return;
+
+ uport = &port->uport;
+ if (oops_in_progress)
+ locked = spin_trylock_irqsave(&uport->lock, flags);
+ else
+ spin_lock_irqsave(&uport->lock, flags);
+
+ /* Cancel the current write to log the fault */
+ if (!locked) {
+ geni_se_cancel_m_cmd(&port->se);
+ if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+ M_CMD_CANCEL_EN, true)) {
+ geni_se_abort_m_cmd(&port->se);
+ qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+ M_CMD_ABORT_EN, true);
+ writel_relaxed(M_CMD_ABORT_EN, uport->membase +
+ SE_GENI_M_IRQ_CLEAR);
+ }
+ writel_relaxed(M_CMD_CANCEL_EN, uport->membase +
+ SE_GENI_M_IRQ_CLEAR);
+ }
+
+ __qcom_geni_serial_console_write(uport, s, count);
+ if (locked)
+ spin_unlock_irqrestore(&uport->lock, flags);
+}
+
+static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop)
+{
+ u32 i;
+ unsigned char buf[sizeof(u32)];
+ struct tty_port *tport;
+ struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+
+ tport = &uport->state->port;
+ for (i = 0; i < bytes; ) {
+ int c;
+ int chunk = min_t(int, bytes - i, port->rx_bytes_pw);
+
+ ioread32_rep(uport->membase + SE_GENI_RX_FIFOn, buf, 1);
+ i += chunk;
+ if (drop)
+ continue;
+
+ for (c = 0; c < chunk; c++) {
+ int sysrq;
+
+ uport->icount.rx++;
+ if (port->brk && buf[c] == 0) {
+ port->brk = false;
+ if (uart_handle_break(uport))
+ continue;
+ }
+
+ sysrq = uart_handle_sysrq_char(uport, buf[c]);
+ if (!sysrq)
+ tty_insert_flip_char(tport, buf[c], TTY_NORMAL);
+ }
+ }
+ if (!drop)
+ tty_flip_buffer_push(tport);
+ return 0;
+}
+#else
+static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop)
+{
+ return -EPERM;
+}
+
+#endif /* CONFIG_SERIAL_QCOM_GENI_CONSOLE */
+
+static void qcom_geni_serial_start_tx(struct uart_port *uport)
+{
+ u32 irq_en;
+ struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+ u32 status;
+
+ if (port->xfer_mode == GENI_SE_FIFO) {
+ status = readl_relaxed(uport->membase + SE_GENI_STATUS);
+ if (status & M_GENI_CMD_ACTIVE)
+ return;
+
+ if (!qcom_geni_serial_tx_empty(uport))
+ return;
+
+ /*
+ * Ensure writing to IRQ_EN & watermark registers are not
+ * re-ordered before checking the status of the Serial
+ * Engine and TX FIFO
+ */
+ mb();
+
+ irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
+ irq_en |= M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN;
+
+ writel_relaxed(port->tx_wm, uport->membase +
+ SE_GENI_TX_WATERMARK_REG);
+ writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
+ }
+}
+
+static void qcom_geni_serial_stop_tx(struct uart_port *uport)
+{
+ u32 irq_en;
+ u32 status;
+ struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+
+ irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
+ irq_en &= ~M_CMD_DONE_EN;
+ if (port->xfer_mode == GENI_SE_FIFO) {
+ irq_en &= ~M_TX_FIFO_WATERMARK_EN;
+ writel_relaxed(0, uport->membase +
+ SE_GENI_TX_WATERMARK_REG);
+ }
+ port->xmit_size = 0;
+ writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
+ status = readl_relaxed(uport->membase + SE_GENI_STATUS);
+ /* Possible stop tx is called multiple times. */
+ if (!(status & M_GENI_CMD_ACTIVE))
+ return;
+
+ /*
+ * Ensure cancel command write is not re-ordered before checking
+ * the status of the Primary Sequencer.
+ */
+ mb();
+
+ geni_se_cancel_m_cmd(&port->se);
+ if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+ M_CMD_CANCEL_EN, true)) {
+ geni_se_abort_m_cmd(&port->se);
+ qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+ M_CMD_ABORT_EN, true);
+ writel_relaxed(M_CMD_ABORT_EN, uport->membase +
+ SE_GENI_M_IRQ_CLEAR);
+ }
+ writel_relaxed(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR);
+}
+
+static void qcom_geni_serial_start_rx(struct uart_port *uport)
+{
+ u32 irq_en;
+ u32 status;
+ struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+
+ status = readl_relaxed(uport->membase + SE_GENI_STATUS);
+ if (status & S_GENI_CMD_ACTIVE)
+ qcom_geni_serial_stop_rx(uport);
+
+ /*
+ * Ensure setup command write is not re-ordered before checking
+ * the status of the Secondary Sequencer.
+ */
+ mb();
+
+ geni_se_setup_s_cmd(&port->se, UART_START_READ, 0);
+
+ if (port->xfer_mode == GENI_SE_FIFO) {
+ irq_en = readl_relaxed(uport->membase + SE_GENI_S_IRQ_EN);
+ irq_en |= S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN;
+ writel_relaxed(irq_en, uport->membase + SE_GENI_S_IRQ_EN);
+
+ irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
+ irq_en |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN;
+ writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
+ }
+}
+
+static void qcom_geni_serial_stop_rx(struct uart_port *uport)
+{
+ u32 irq_en;
+ u32 status;
+ struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+ u32 irq_clear = S_CMD_DONE_EN;
+
+ if (port->xfer_mode == GENI_SE_FIFO) {
+ irq_en = readl_relaxed(uport->membase + SE_GENI_S_IRQ_EN);
+ irq_en &= ~(S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN);
+ writel_relaxed(irq_en, uport->membase + SE_GENI_S_IRQ_EN);
+
+ irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
+ irq_en &= ~(M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN);
+ writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
+ }
+
+ status = readl_relaxed(uport->membase + SE_GENI_STATUS);
+ /* Possible stop rx is called multiple times. */
+ if (!(status & S_GENI_CMD_ACTIVE))
+ return;
+
+ /*
+ * Ensure cancel command write is not re-ordered before checking
+ * the status of the Secondary Sequencer.
+ */
+ mb();
+
+ geni_se_cancel_s_cmd(&port->se);
+ qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
+ S_GENI_CMD_CANCEL, false);
+ status = readl_relaxed(uport->membase + SE_GENI_STATUS);
+ writel_relaxed(irq_clear, uport->membase + SE_GENI_S_IRQ_CLEAR);
+ if (status & S_GENI_CMD_ACTIVE)
+ qcom_geni_serial_abort_rx(uport);
+}
+
+static void qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop)
+{
+ u32 status;
+ u32 word_cnt;
+ u32 last_word_byte_cnt;
+ u32 last_word_partial;
+ u32 total_bytes;
+ struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+
+ status = readl_relaxed(uport->membase + SE_GENI_RX_FIFO_STATUS);
+ word_cnt = status & RX_FIFO_WC_MSK;
+ last_word_partial = status & RX_LAST;
+ last_word_byte_cnt = (status & RX_LAST_BYTE_VALID_MSK) >>
+ RX_LAST_BYTE_VALID_SHFT;
+
+ if (!word_cnt)
+ return;
+ total_bytes = port->rx_bytes_pw * (word_cnt - 1);
+ if (last_word_partial && last_word_byte_cnt)
+ total_bytes += last_word_byte_cnt;
+ else
+ total_bytes += port->rx_bytes_pw;
+ port->handle_rx(uport, total_bytes, drop);
+}
+
+static void qcom_geni_serial_handle_tx(struct uart_port *uport)
+{
+ struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+ struct circ_buf *xmit = &uport->state->xmit;
+ size_t avail;
+ size_t remaining;
+ int i;
+ u32 status;
+ unsigned int chunk;
+ int tail;
+
+ chunk = uart_circ_chars_pending(xmit);
+ status = readl_relaxed(uport->membase + SE_GENI_TX_FIFO_STATUS);
+ /* Both FIFO and framework buffer are drained */
+ if (chunk == port->xmit_size && !status) {
+ port->xmit_size = 0;
+ uart_circ_clear(xmit);
+ qcom_geni_serial_stop_tx(uport);
+ goto out_write_wakeup;
+ }
+ chunk -= port->xmit_size;
+
+ avail = (port->tx_fifo_depth - port->tx_wm) * port->tx_bytes_pw;
+ tail = (xmit->tail + port->xmit_size) & (UART_XMIT_SIZE - 1);
+ if (chunk > (UART_XMIT_SIZE - tail))
+ chunk = UART_XMIT_SIZE - tail;
+ if (chunk > avail)
+ chunk = avail;
+
+ if (!chunk)
+ goto out_write_wakeup;
+
+ qcom_geni_serial_setup_tx(uport, chunk);
+
+ remaining = chunk;
+ for (i = 0; i < chunk; ) {
+ unsigned int tx_bytes;
+ unsigned int buf = 0;
+ int c;
+
+ tx_bytes = min_t(size_t, remaining, (size_t)port->tx_bytes_pw);
+ for (c = 0; c < tx_bytes ; c++)
+ buf |= (xmit->buf[tail + c] << (c * BITS_PER_BYTE));
+
+ writel_relaxed(buf, uport->membase + SE_GENI_TX_FIFOn);
+
+ i += tx_bytes;
+ tail = (tail + tx_bytes) & (UART_XMIT_SIZE - 1);
+ uport->icount.tx += tx_bytes;
+ remaining -= tx_bytes;
+ }
+ qcom_geni_serial_poll_tx_done(uport);
+ port->xmit_size += chunk;
+out_write_wakeup:
+ uart_write_wakeup(uport);
+}
+
+static irqreturn_t qcom_geni_serial_isr(int isr, void *dev)
+{
+ unsigned int m_irq_status;
+ unsigned int s_irq_status;
+ struct uart_port *uport = dev;
+ unsigned long flags;
+ unsigned int m_irq_en;
+ bool drop_rx = false;
+ struct tty_port *tport = &uport->state->port;
+ struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+
+ if (uport->suspended)
+ return IRQ_HANDLED;
+
+ spin_lock_irqsave(&uport->lock, flags);
+ m_irq_status = readl_relaxed(uport->membase + SE_GENI_M_IRQ_STATUS);
+ s_irq_status = readl_relaxed(uport->membase + SE_GENI_S_IRQ_STATUS);
+ m_irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
+ writel_relaxed(m_irq_status, uport->membase + SE_GENI_M_IRQ_CLEAR);
+ writel_relaxed(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR);
+
+ if (WARN_ON(m_irq_status & M_ILLEGAL_CMD_EN))
+ goto out_unlock;
+
+ if (s_irq_status & S_RX_FIFO_WR_ERR_EN) {
+ uport->icount.overrun++;
+ tty_insert_flip_char(tport, 0, TTY_OVERRUN);
+ }
+
+ if (m_irq_status & (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN) &&
+ m_irq_en & (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN))
+ qcom_geni_serial_handle_tx(uport);
+
+ if (s_irq_status & S_GP_IRQ_0_EN || s_irq_status & S_GP_IRQ_1_EN) {
+ if (s_irq_status & S_GP_IRQ_0_EN)
+ uport->icount.parity++;
+ drop_rx = true;
+ } else if (s_irq_status & S_GP_IRQ_2_EN ||
+ s_irq_status & S_GP_IRQ_3_EN) {
+ uport->icount.brk++;
+ port->brk = true;
+ }
+
+ if (s_irq_status & S_RX_FIFO_WATERMARK_EN ||
+ s_irq_status & S_RX_FIFO_LAST_EN)
+ qcom_geni_serial_handle_rx(uport, drop_rx);
+
+out_unlock:
+ spin_unlock_irqrestore(&uport->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static int get_tx_fifo_size(struct qcom_geni_serial_port *port)
+{
+ struct uart_port *uport;
+
+ if (!port)
+ return -ENODEV;
+
+ uport = &port->uport;
+ port->tx_fifo_depth = geni_se_get_tx_fifo_depth(&port->se);
+ port->tx_fifo_width = geni_se_get_tx_fifo_width(&port->se);
+ port->rx_fifo_depth = geni_se_get_rx_fifo_depth(&port->se);
+ uport->fifosize =
+ (port->tx_fifo_depth * port->tx_fifo_width) / BITS_PER_BYTE;
+ return 0;
+}
+
+static void set_rfr_wm(struct qcom_geni_serial_port *port)
+{
+ /*
+ * Set RFR (Flow off) to FIFO_DEPTH - 2.
+ * RX WM level at 10% RX_FIFO_DEPTH.
+ * TX WM level at 10% TX_FIFO_DEPTH.
+ */
+ port->rx_rfr = port->rx_fifo_depth - 2;
+ port->rx_wm = UART_CONSOLE_RX_WM;
+ port->tx_wm = DEF_TX_WM;
+}
+
+static void qcom_geni_serial_shutdown(struct uart_port *uport)
+{
+ unsigned long flags;
+
+ /* Stop the console before stopping the current tx */
+ console_stop(uport->cons);
+
+ disable_irq(uport->irq);
+ free_irq(uport->irq, uport);
+ spin_lock_irqsave(&uport->lock, flags);
+ qcom_geni_serial_stop_tx(uport);
+ qcom_geni_serial_stop_rx(uport);
+ spin_unlock_irqrestore(&uport->lock, flags);
+}
+
+static int qcom_geni_serial_port_setup(struct uart_port *uport)
+{
+ struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+ unsigned int rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT;
+
+ set_rfr_wm(port);
+ writel_relaxed(rxstale, uport->membase + SE_UART_RX_STALE_CNT);
+ /*
+ * Make an unconditional cancel on the main sequencer to reset
+ * it else we could end up in data loss scenarios.
+ */
+ port->xfer_mode = GENI_SE_FIFO;
+ qcom_geni_serial_poll_tx_done(uport);
+ geni_se_config_packing(&port->se, BITS_PER_BYTE, port->tx_bytes_pw,
+ false, true, false);
+ geni_se_config_packing(&port->se, BITS_PER_BYTE, port->rx_bytes_pw,
+ false, false, true);
+ geni_se_init(&port->se, port->rx_wm, port->rx_rfr);
+ geni_se_select_mode(&port->se, port->xfer_mode);
+ port->setup = true;
+ return 0;
+}
+
+static int qcom_geni_serial_startup(struct uart_port *uport)
+{
+ int ret;
+ u32 proto;
+ struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+
+ scnprintf(port->name, sizeof(port->name),
+ "qcom_serial_geni%d", uport->line);
+
+ proto = geni_se_read_proto(&port->se);
+ if (proto != GENI_SE_UART) {
+ dev_err(uport->dev, "Invalid FW loaded, proto: %d\n", proto);
+ return -ENXIO;
+ }
+
+ get_tx_fifo_size(port);
+ if (!port->setup) {
+ ret = qcom_geni_serial_port_setup(uport);
+ if (ret)
+ return ret;
+ }
+
+ ret = request_irq(uport->irq, qcom_geni_serial_isr, IRQF_TRIGGER_HIGH,
+ port->name, uport);
+ if (ret)
+ dev_err(uport->dev, "Failed to get IRQ ret %d\n", ret);
+ return ret;
+}
+
+static unsigned long get_clk_cfg(unsigned long clk_freq)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(root_freq); i++) {
+ if (!(root_freq[i] % clk_freq))
+ return root_freq[i];
+ }
+ return 0;
+}
+
+static unsigned long get_clk_div_rate(unsigned int baud, unsigned int *clk_div)
+{
+ unsigned long ser_clk;
+ unsigned long desired_clk;
+
+ desired_clk = baud * UART_OVERSAMPLING;
+ ser_clk = get_clk_cfg(desired_clk);
+ if (!ser_clk) {
+ pr_err("%s: Can't find matching DFS entry for baud %d\n",
+ __func__, baud);
+ return ser_clk;
+ }
+
+ *clk_div = ser_clk / desired_clk;
+ return ser_clk;
+}
+
+static void qcom_geni_serial_set_termios(struct uart_port *uport,
+ struct ktermios *termios, struct ktermios *old)
+{
+ unsigned int baud;
+ unsigned int bits_per_char;
+ unsigned int tx_trans_cfg;
+ unsigned int tx_parity_cfg;
+ unsigned int rx_trans_cfg;
+ unsigned int rx_parity_cfg;
+ unsigned int stop_bit_len;
+ unsigned int clk_div;
+ unsigned long ser_clk_cfg;
+ struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+ unsigned long clk_rate;
+
+ qcom_geni_serial_stop_rx(uport);
+ /* baud rate */
+ baud = uart_get_baud_rate(uport, termios, old, 300, 4000000);
+ port->baud = baud;
+ clk_rate = get_clk_div_rate(baud, &clk_div);
+ if (!clk_rate)
+ goto out_restart_rx;
+
+ uport->uartclk = clk_rate;
+ clk_set_rate(port->se.clk, clk_rate);
+ ser_clk_cfg = SER_CLK_EN;
+ ser_clk_cfg |= clk_div << CLK_DIV_SHFT;
+
+ /* parity */
+ tx_trans_cfg = readl_relaxed(uport->membase + SE_UART_TX_TRANS_CFG);
+ tx_parity_cfg = readl_relaxed(uport->membase + SE_UART_TX_PARITY_CFG);
+ rx_trans_cfg = readl_relaxed(uport->membase + SE_UART_RX_TRANS_CFG);
+ rx_parity_cfg = readl_relaxed(uport->membase + SE_UART_RX_PARITY_CFG);
+ if (termios->c_cflag & PARENB) {
+ tx_trans_cfg |= UART_TX_PAR_EN;
+ rx_trans_cfg |= UART_RX_PAR_EN;
+ tx_parity_cfg |= PAR_CALC_EN;
+ rx_parity_cfg |= PAR_CALC_EN;
+ if (termios->c_cflag & PARODD) {
+ tx_parity_cfg |= PAR_ODD;
+ rx_parity_cfg |= PAR_ODD;
+ } else if (termios->c_cflag & CMSPAR) {
+ tx_parity_cfg |= PAR_SPACE;
+ rx_parity_cfg |= PAR_SPACE;
+ } else {
+ tx_parity_cfg |= PAR_EVEN;
+ rx_parity_cfg |= PAR_EVEN;
+ }
+ } else {
+ tx_trans_cfg &= ~UART_TX_PAR_EN;
+ rx_trans_cfg &= ~UART_RX_PAR_EN;
+ tx_parity_cfg &= ~PAR_CALC_EN;
+ rx_parity_cfg &= ~PAR_CALC_EN;
+ }
+
+ /* bits per char */
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+ bits_per_char = 5;
+ break;
+ case CS6:
+ bits_per_char = 6;
+ break;
+ case CS7:
+ bits_per_char = 7;
+ break;
+ case CS8:
+ default:
+ bits_per_char = 8;
+ break;
+ }
+
+ /* stop bits */
+ if (termios->c_cflag & CSTOPB)
+ stop_bit_len = TX_STOP_BIT_LEN_2;
+ else
+ stop_bit_len = TX_STOP_BIT_LEN_1;
+
+ /* flow control, clear the CTS_MASK bit if using flow control. */
+ if (termios->c_cflag & CRTSCTS)
+ tx_trans_cfg &= ~UART_CTS_MASK;
+ else
+ tx_trans_cfg |= UART_CTS_MASK;
+
+ if (baud)
+ uart_update_timeout(uport, termios->c_cflag, baud);
+
+ writel_relaxed(tx_trans_cfg, uport->membase + SE_UART_TX_TRANS_CFG);
+ writel_relaxed(tx_parity_cfg, uport->membase + SE_UART_TX_PARITY_CFG);
+ writel_relaxed(rx_trans_cfg, uport->membase + SE_UART_RX_TRANS_CFG);
+ writel_relaxed(rx_parity_cfg, uport->membase + SE_UART_RX_PARITY_CFG);
+ writel_relaxed(bits_per_char, uport->membase + SE_UART_TX_WORD_LEN);
+ writel_relaxed(bits_per_char, uport->membase + SE_UART_RX_WORD_LEN);
+ writel_relaxed(stop_bit_len, uport->membase + SE_UART_TX_STOP_BIT_LEN);
+ writel_relaxed(ser_clk_cfg, uport->membase + GENI_SER_M_CLK_CFG);
+ writel_relaxed(ser_clk_cfg, uport->membase + GENI_SER_S_CLK_CFG);
+out_restart_rx:
+ qcom_geni_serial_start_rx(uport);
+}
+
+static unsigned int qcom_geni_serial_tx_empty(struct uart_port *uport)
+{
+ return !readl_relaxed(uport->membase + SE_GENI_TX_FIFO_STATUS);
+}
+
+#ifdef CONFIG_SERIAL_QCOM_GENI_CONSOLE
+static int __init qcom_geni_console_setup(struct console *co, char *options)
+{
+ struct uart_port *uport;
+ struct qcom_geni_serial_port *port;
+ int baud;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+
+ if (co->index >= GENI_UART_CONS_PORTS || co->index < 0)
+ return -ENXIO;
+
+ port = get_port_from_line(co->index);
+ if (IS_ERR(port)) {
+ pr_err("Invalid line %d(%d)\n", co->index, (int)PTR_ERR(port));
+ return PTR_ERR(port);
+ }
+
+ uport = &port->uport;
+
+ if (unlikely(!uport->membase))
+ return -ENXIO;
+
+ if (geni_se_resources_on(&port->se)) {
+ dev_err(port->se.dev, "Error turning on resources\n");
+ return -ENXIO;
+ }
+
+ if (unlikely(geni_se_read_proto(&port->se) != GENI_SE_UART)) {
+ geni_se_resources_off(&port->se);
+ return -ENXIO;
+ }
+
+ if (!port->setup) {
+ port->tx_bytes_pw = 1;
+ port->rx_bytes_pw = RX_BYTES_PW;
+ qcom_geni_serial_stop_rx(uport);
+ qcom_geni_serial_port_setup(uport);
+ }
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+ return uart_set_options(uport, co, baud, parity, bits, flow);
+}
+
+static int __init console_register(struct uart_driver *drv)
+{
+ return uart_register_driver(drv);
+}
+
+static void console_unregister(struct uart_driver *drv)
+{
+ uart_unregister_driver(drv);
+}
+
+static struct console cons_ops = {
+ .name = "ttyMSM",
+ .write = qcom_geni_serial_console_write,
+ .device = uart_console_device,
+ .setup = qcom_geni_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &qcom_geni_console_driver,
+};
+
+static struct uart_driver qcom_geni_console_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = "qcom_geni_console",
+ .dev_name = "ttyMSM",
+ .nr = GENI_UART_CONS_PORTS,
+ .cons = &cons_ops,
+};
+#else
+static int console_register(struct uart_driver *drv)
+{
+ return 0;
+}
+
+static void console_unregister(struct uart_driver *drv)
+{
+}
+#endif /* CONFIG_SERIAL_QCOM_GENI_CONSOLE */
+
+static void qcom_geni_serial_cons_pm(struct uart_port *uport,
+ unsigned int new_state, unsigned int old_state)
+{
+ struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+
+ if (unlikely(!uart_console(uport)))
+ return;
+
+ if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF)
+ geni_se_resources_on(&port->se);
+ else if (new_state == UART_PM_STATE_OFF &&
+ old_state == UART_PM_STATE_ON)
+ geni_se_resources_off(&port->se);
+}
+
+static const struct uart_ops qcom_geni_console_pops = {
+ .tx_empty = qcom_geni_serial_tx_empty,
+ .stop_tx = qcom_geni_serial_stop_tx,
+ .start_tx = qcom_geni_serial_start_tx,
+ .stop_rx = qcom_geni_serial_stop_rx,
+ .set_termios = qcom_geni_serial_set_termios,
+ .startup = qcom_geni_serial_startup,
+ .request_port = qcom_geni_serial_request_port,
+ .config_port = qcom_geni_serial_config_port,
+ .shutdown = qcom_geni_serial_shutdown,
+ .type = qcom_geni_serial_get_type,
+ .set_mctrl = qcom_geni_cons_set_mctrl,
+ .get_mctrl = qcom_geni_cons_get_mctrl,
+#ifdef CONFIG_CONSOLE_POLL
+ .poll_get_char = qcom_geni_serial_get_char,
+ .poll_put_char = qcom_geni_serial_poll_put_char,
+#endif
+ .pm = qcom_geni_serial_cons_pm,
+};
+
+static int qcom_geni_serial_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ int line = -1;
+ struct qcom_geni_serial_port *port;
+ struct uart_port *uport;
+ struct resource *res;
+ int irq;
+
+ if (pdev->dev.of_node)
+ line = of_alias_get_id(pdev->dev.of_node, "serial");
+ else
+ line = pdev->id;
+
+ if (line < 0 || line >= GENI_UART_CONS_PORTS)
+ return -ENXIO;
+ port = get_port_from_line(line);
+ if (IS_ERR(port)) {
+ ret = PTR_ERR(port);
+ dev_err(&pdev->dev, "Invalid line %d(%d)\n", line, ret);
+ return ret;
+ }
+
+ uport = &port->uport;
+ /* Don't allow 2 drivers to access the same port */
+ if (uport->private_data)
+ return -ENODEV;
+
+ uport->dev = &pdev->dev;
+ port->se.dev = &pdev->dev;
+ port->se.wrapper = dev_get_drvdata(pdev->dev.parent);
+ port->se.clk = devm_clk_get(&pdev->dev, "se");
+ if (IS_ERR(port->se.clk)) {
+ ret = PTR_ERR(port->se.clk);
+ dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
+ return ret;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+ uport->mapbase = res->start;
+
+ port->tx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
+ port->rx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
+ port->tx_fifo_width = DEF_FIFO_WIDTH_BITS;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "Failed to get IRQ %d\n", irq);
+ return irq;
+ }
+ uport->irq = irq;
+
+ uport->private_data = &qcom_geni_console_driver;
+ platform_set_drvdata(pdev, port);
+ port->handle_rx = handle_rx_console;
+ port->setup = false;
+ return uart_add_one_port(&qcom_geni_console_driver, uport);
+}
+
+static int qcom_geni_serial_remove(struct platform_device *pdev)
+{
+ struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
+ struct uart_driver *drv = port->uport.private_data;
+
+ uart_remove_one_port(drv, &port->uport);
+ return 0;
+}
+
+static int __maybe_unused qcom_geni_serial_sys_suspend_noirq(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
+ struct uart_port *uport = &port->uport;
+
+ uart_suspend_port(uport->private_data, uport);
+ return 0;
+}
+
+static int __maybe_unused qcom_geni_serial_sys_resume_noirq(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
+ struct uart_port *uport = &port->uport;
+
+ if (console_suspend_enabled && uport->suspended) {
+ uart_resume_port(uport->private_data, uport);
+ disable_irq(uport->irq);
+ }
+ return 0;
+}
+
+static const struct dev_pm_ops qcom_geni_serial_pm_ops = {
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(qcom_geni_serial_sys_suspend_noirq,
+ qcom_geni_serial_sys_resume_noirq)
+};
+
+static const struct of_device_id qcom_geni_serial_match_table[] = {
+ { .compatible = "qcom,geni-debug-uart", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, qcom_geni_serial_match_table);
+
+static struct platform_driver qcom_geni_serial_platform_driver = {
+ .remove = qcom_geni_serial_remove,
+ .probe = qcom_geni_serial_probe,
+ .driver = {
+ .name = "qcom_geni_serial",
+ .of_match_table = qcom_geni_serial_match_table,
+ .pm = &qcom_geni_serial_pm_ops,
+ },
+};
+
+static int __init qcom_geni_serial_init(void)
+{
+ int ret;
+
+ qcom_geni_console_port.uport.iotype = UPIO_MEM;
+ qcom_geni_console_port.uport.ops = &qcom_geni_console_pops;
+ qcom_geni_console_port.uport.flags = UPF_BOOT_AUTOCONF;
+ qcom_geni_console_port.uport.line = 0;
+
+ ret = console_register(&qcom_geni_console_driver);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&qcom_geni_serial_platform_driver);
+ if (ret)
+ console_unregister(&qcom_geni_console_driver);
+ return ret;
+}
+module_init(qcom_geni_serial_init);
+
+static void __exit qcom_geni_serial_exit(void)
+{
+ platform_driver_unregister(&qcom_geni_serial_platform_driver);
+ console_unregister(&qcom_geni_console_driver);
+}
+module_exit(qcom_geni_serial_exit);
+
+MODULE_DESCRIPTION("Serial driver for GENI based QUP cores");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c
index f9fecc5ed0ce..3f2f8c118ce0 100644
--- a/drivers/tty/serial/samsung.c
+++ b/drivers/tty/serial/samsung.c
@@ -1818,6 +1818,10 @@ static int s3c24xx_serial_probe(struct platform_device *pdev)
dbg("s3c24xx_serial_probe(%p) %d\n", pdev, index);
+ if (index >= ARRAY_SIZE(s3c24xx_serial_ports)) {
+ dev_err(&pdev->dev, "serial%d out of range\n", index);
+ return -EINVAL;
+ }
ourport = &s3c24xx_serial_ports[index];
ourport->drv_data = s3c24xx_get_driver_data(pdev);
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 35b9201db3b4..0466f9f08a91 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -1786,6 +1786,8 @@ static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i)
seq_printf(m, " brk:%d", uport->icount.brk);
if (uport->icount.overrun)
seq_printf(m, " oe:%d", uport->icount.overrun);
+ if (uport->icount.buf_overrun)
+ seq_printf(m, " bo:%d", uport->icount.buf_overrun);
#define INFOBIT(bit, str) \
if (uport->mctrl & (bit)) \
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index 44adf9db38f8..fdbbff547106 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -33,6 +33,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
+#include <linux/ktime.h>
#include <linux/major.h>
#include <linux/module.h>
#include <linux/mm.h>
@@ -143,8 +144,8 @@ struct sci_port {
void *rx_buf[2];
size_t buf_len_rx;
struct work_struct work_tx;
- struct timer_list rx_timer;
- unsigned int rx_timeout;
+ struct hrtimer rx_timer;
+ unsigned int rx_timeout; /* microseconds */
#endif
unsigned int rx_frame;
int rx_trigger;
@@ -1231,6 +1232,15 @@ static void sci_rx_dma_release(struct sci_port *s, bool enable_pio)
}
}
+static void start_hrtimer_us(struct hrtimer *hrt, unsigned long usec)
+{
+ long sec = usec / 1000000;
+ long nsec = (usec % 1000000) * 1000;
+ ktime_t t = ktime_set(sec, nsec);
+
+ hrtimer_start(hrt, t, HRTIMER_MODE_REL);
+}
+
static void sci_dma_rx_complete(void *arg)
{
struct sci_port *s = arg;
@@ -1249,7 +1259,7 @@ static void sci_dma_rx_complete(void *arg)
if (active >= 0)
count = sci_dma_rx_push(s, s->rx_buf[active], s->buf_len_rx);
- mod_timer(&s->rx_timer, jiffies + s->rx_timeout);
+ start_hrtimer_us(&s->rx_timer, s->rx_timeout);
if (count)
tty_flip_buffer_push(&port->state->port);
@@ -1393,9 +1403,9 @@ static void work_fn_tx(struct work_struct *work)
dma_async_issue_pending(chan);
}
-static void rx_timer_fn(struct timer_list *t)
+static enum hrtimer_restart rx_timer_fn(struct hrtimer *t)
{
- struct sci_port *s = from_timer(s, t, rx_timer);
+ struct sci_port *s = container_of(t, struct sci_port, rx_timer);
struct dma_chan *chan = s->chan_rx;
struct uart_port *port = &s->port;
struct dma_tx_state state;
@@ -1412,7 +1422,7 @@ static void rx_timer_fn(struct timer_list *t)
active = sci_dma_rx_find_active(s);
if (active < 0) {
spin_unlock_irqrestore(&port->lock, flags);
- return;
+ return HRTIMER_NORESTART;
}
status = dmaengine_tx_status(s->chan_rx, s->active_rx, &state);
@@ -1422,7 +1432,7 @@ static void rx_timer_fn(struct timer_list *t)
s->active_rx, active);
/* Let packet complete handler take care of the packet */
- return;
+ return HRTIMER_NORESTART;
}
dmaengine_pause(chan);
@@ -1437,7 +1447,7 @@ static void rx_timer_fn(struct timer_list *t)
if (status == DMA_COMPLETE) {
spin_unlock_irqrestore(&port->lock, flags);
dev_dbg(port->dev, "Transaction complete after DMA engine was stopped");
- return;
+ return HRTIMER_NORESTART;
}
/* Handle incomplete DMA receive */
@@ -1462,6 +1472,8 @@ static void rx_timer_fn(struct timer_list *t)
serial_port_out(port, SCSCR, scr | SCSCR_RIE);
spin_unlock_irqrestore(&port->lock, flags);
+
+ return HRTIMER_NORESTART;
}
static struct dma_chan *sci_request_dma_chan(struct uart_port *port,
@@ -1573,7 +1585,8 @@ static void sci_request_dma(struct uart_port *port)
dma += s->buf_len_rx;
}
- timer_setup(&s->rx_timer, rx_timer_fn, 0);
+ hrtimer_init(&s->rx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ s->rx_timer.function = rx_timer_fn;
if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
sci_submit_rx(s);
@@ -1632,9 +1645,9 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr)
/* Clear current interrupt */
serial_port_out(port, SCxSR,
ssr & ~(SCIF_DR | SCxSR_RDxF(port)));
- dev_dbg(port->dev, "Rx IRQ %lu: setup t-out in %u jiffies\n",
+ dev_dbg(port->dev, "Rx IRQ %lu: setup t-out in %u us\n",
jiffies, s->rx_timeout);
- mod_timer(&s->rx_timer, jiffies + s->rx_timeout);
+ start_hrtimer_us(&s->rx_timer, s->rx_timeout);
return IRQ_HANDLED;
}
@@ -1645,7 +1658,7 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr)
scif_set_rtrg(port, s->rx_trigger);
mod_timer(&s->rx_fifo_timer, jiffies + DIV_ROUND_UP(
- s->rx_frame * s->rx_fifo_timeout, 1000));
+ s->rx_frame * HZ * s->rx_fifo_timeout, 1000000));
}
/* I think sci_receive_chars has to be called irrespective
@@ -2081,7 +2094,7 @@ static void sci_shutdown(struct uart_port *port)
if (s->chan_rx) {
dev_dbg(port->dev, "%s(%d) deleting rx_timer\n", __func__,
port->line);
- del_timer_sync(&s->rx_timer);
+ hrtimer_cancel(&s->rx_timer);
}
#endif
@@ -2482,11 +2495,11 @@ done:
if (termios->c_cflag & PARENB)
bits++;
- s->rx_frame = (100 * bits * HZ) / (baud / 10);
+ s->rx_frame = (10000 * bits) / (baud / 100);
#ifdef CONFIG_SERIAL_SH_SCI_DMA
- s->rx_timeout = DIV_ROUND_UP(s->buf_len_rx * 2 * s->rx_frame, 1000);
- if (s->rx_timeout < msecs_to_jiffies(20))
- s->rx_timeout = msecs_to_jiffies(20);
+ s->rx_timeout = s->buf_len_rx * 2 * s->rx_frame;
+ if (s->rx_timeout < 20)
+ s->rx_timeout = 20;
#endif
if ((termios->c_cflag & CREAD) != 0)
@@ -3098,6 +3111,10 @@ static struct plat_sci_port *sci_parse_dt(struct platform_device *pdev,
dev_err(&pdev->dev, "failed to get alias id (%d)\n", id);
return NULL;
}
+ if (id >= ARRAY_SIZE(sci_ports)) {
+ dev_err(&pdev->dev, "serial%d out of range\n", id);
+ return NULL;
+ }
sp = &sci_ports[id];
*dev_id = id;
diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c
index 9925b00a9777..38622f2a30a9 100644
--- a/drivers/tty/serial/sirfsoc_uart.c
+++ b/drivers/tty/serial/sirfsoc_uart.c
@@ -1283,6 +1283,11 @@ static int sirfsoc_uart_probe(struct platform_device *pdev)
goto err;
}
sirfport->port.line = of_alias_get_id(np, "serial");
+ if (sirfport->port.line >= ARRAY_SIZE(sirf_ports)) {
+ dev_err(&pdev->dev, "serial%d out of range\n",
+ sirfport->port.line);
+ return -EINVAL;
+ }
sirf_ports[sirfport->port.line] = sirfport;
sirfport->port.iotype = UPIO_MEM;
sirfport->port.flags = UPF_BOOT_AUTOCONF;
diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c
index c763253514e9..5f9f01fac6dd 100644
--- a/drivers/tty/serial/st-asc.c
+++ b/drivers/tty/serial/st-asc.c
@@ -782,7 +782,9 @@ static struct asc_port *asc_of_get_asc_port(struct platform_device *pdev)
if (!np)
return NULL;
- id = of_alias_get_id(np, ASC_SERIAL_NAME);
+ id = of_alias_get_id(np, "serial");
+ if (id < 0)
+ id = of_alias_get_id(np, ASC_SERIAL_NAME);
if (id < 0)
id = 0;
diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c
index 0fa735b60f2d..e8d7a7bb4339 100644
--- a/drivers/tty/serial/stm32-usart.c
+++ b/drivers/tty/serial/stm32-usart.c
@@ -62,6 +62,113 @@ static void stm32_clr_bits(struct uart_port *port, u32 reg, u32 bits)
writel_relaxed(val, port->membase + reg);
}
+static void stm32_config_reg_rs485(u32 *cr1, u32 *cr3, u32 delay_ADE,
+ u32 delay_DDE, u32 baud)
+{
+ u32 rs485_deat_dedt;
+ u32 rs485_deat_dedt_max = (USART_CR1_DEAT_MASK >> USART_CR1_DEAT_SHIFT);
+ bool over8;
+
+ *cr3 |= USART_CR3_DEM;
+ over8 = *cr1 & USART_CR1_OVER8;
+
+ if (over8)
+ rs485_deat_dedt = delay_ADE * baud * 8;
+ else
+ rs485_deat_dedt = delay_ADE * baud * 16;
+
+ rs485_deat_dedt = DIV_ROUND_CLOSEST(rs485_deat_dedt, 1000);
+ rs485_deat_dedt = rs485_deat_dedt > rs485_deat_dedt_max ?
+ rs485_deat_dedt_max : rs485_deat_dedt;
+ rs485_deat_dedt = (rs485_deat_dedt << USART_CR1_DEAT_SHIFT) &
+ USART_CR1_DEAT_MASK;
+ *cr1 |= rs485_deat_dedt;
+
+ if (over8)
+ rs485_deat_dedt = delay_DDE * baud * 8;
+ else
+ rs485_deat_dedt = delay_DDE * baud * 16;
+
+ rs485_deat_dedt = DIV_ROUND_CLOSEST(rs485_deat_dedt, 1000);
+ rs485_deat_dedt = rs485_deat_dedt > rs485_deat_dedt_max ?
+ rs485_deat_dedt_max : rs485_deat_dedt;
+ rs485_deat_dedt = (rs485_deat_dedt << USART_CR1_DEDT_SHIFT) &
+ USART_CR1_DEDT_MASK;
+ *cr1 |= rs485_deat_dedt;
+}
+
+static int stm32_config_rs485(struct uart_port *port,
+ struct serial_rs485 *rs485conf)
+{
+ struct stm32_port *stm32_port = to_stm32_port(port);
+ struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
+ struct stm32_usart_config *cfg = &stm32_port->info->cfg;
+ u32 usartdiv, baud, cr1, cr3;
+ bool over8;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+ stm32_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
+
+ port->rs485 = *rs485conf;
+
+ rs485conf->flags |= SER_RS485_RX_DURING_TX;
+
+ if (rs485conf->flags & SER_RS485_ENABLED) {
+ cr1 = readl_relaxed(port->membase + ofs->cr1);
+ cr3 = readl_relaxed(port->membase + ofs->cr3);
+ usartdiv = readl_relaxed(port->membase + ofs->brr);
+ usartdiv = usartdiv & GENMASK(15, 0);
+ over8 = cr1 & USART_CR1_OVER8;
+
+ if (over8)
+ usartdiv = usartdiv | (usartdiv & GENMASK(4, 0))
+ << USART_BRR_04_R_SHIFT;
+
+ baud = DIV_ROUND_CLOSEST(port->uartclk, usartdiv);
+ stm32_config_reg_rs485(&cr1, &cr3,
+ rs485conf->delay_rts_before_send,
+ rs485conf->delay_rts_after_send, baud);
+
+ if (rs485conf->flags & SER_RS485_RTS_ON_SEND) {
+ cr3 &= ~USART_CR3_DEP;
+ rs485conf->flags &= ~SER_RS485_RTS_AFTER_SEND;
+ } else {
+ cr3 |= USART_CR3_DEP;
+ rs485conf->flags |= SER_RS485_RTS_AFTER_SEND;
+ }
+
+ writel_relaxed(cr3, port->membase + ofs->cr3);
+ writel_relaxed(cr1, port->membase + ofs->cr1);
+ } else {
+ stm32_clr_bits(port, ofs->cr3, USART_CR3_DEM | USART_CR3_DEP);
+ stm32_clr_bits(port, ofs->cr1,
+ USART_CR1_DEDT_MASK | USART_CR1_DEAT_MASK);
+ }
+
+ stm32_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ return 0;
+}
+
+static int stm32_init_rs485(struct uart_port *port,
+ struct platform_device *pdev)
+{
+ struct serial_rs485 *rs485conf = &port->rs485;
+
+ rs485conf->flags = 0;
+ rs485conf->delay_rts_before_send = 0;
+ rs485conf->delay_rts_after_send = 0;
+
+ if (!pdev->dev.of_node)
+ return -ENODEV;
+
+ uart_get_rs485_mode(&pdev->dev, rs485conf);
+
+ return 0;
+}
+
static int stm32_pending_rx(struct uart_port *port, u32 *sr, int *last_res,
bool threaded)
{
@@ -498,6 +605,7 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
struct stm32_port *stm32_port = to_stm32_port(port);
struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
struct stm32_usart_config *cfg = &stm32_port->info->cfg;
+ struct serial_rs485 *rs485conf = &port->rs485;
unsigned int baud;
u32 usartdiv, mantissa, fraction, oversampling;
tcflag_t cflag = termios->c_cflag;
@@ -515,7 +623,7 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
writel_relaxed(0, port->membase + ofs->cr1);
cr1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE;
- cr1 |= BIT(cfg->uart_enable_bit);
+
if (stm32_port->fifoen)
cr1 |= USART_CR1_FIFOEN;
cr2 = 0;
@@ -553,9 +661,11 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
*/
if (usartdiv < 16) {
oversampling = 8;
+ cr1 |= USART_CR1_OVER8;
stm32_set_bits(port, ofs->cr1, USART_CR1_OVER8);
} else {
oversampling = 16;
+ cr1 &= ~USART_CR1_OVER8;
stm32_clr_bits(port, ofs->cr1, USART_CR1_OVER8);
}
@@ -592,10 +702,28 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
if (stm32_port->rx_ch)
cr3 |= USART_CR3_DMAR;
+ if (rs485conf->flags & SER_RS485_ENABLED) {
+ stm32_config_reg_rs485(&cr1, &cr3,
+ rs485conf->delay_rts_before_send,
+ rs485conf->delay_rts_after_send, baud);
+ if (rs485conf->flags & SER_RS485_RTS_ON_SEND) {
+ cr3 &= ~USART_CR3_DEP;
+ rs485conf->flags &= ~SER_RS485_RTS_AFTER_SEND;
+ } else {
+ cr3 |= USART_CR3_DEP;
+ rs485conf->flags |= SER_RS485_RTS_AFTER_SEND;
+ }
+
+ } else {
+ cr3 &= ~(USART_CR3_DEM | USART_CR3_DEP);
+ cr1 &= ~(USART_CR1_DEDT_MASK | USART_CR1_DEAT_MASK);
+ }
+
writel_relaxed(cr3, port->membase + ofs->cr3);
writel_relaxed(cr2, port->membase + ofs->cr2);
writel_relaxed(cr1, port->membase + ofs->cr1);
+ stm32_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
spin_unlock_irqrestore(&port->lock, flags);
}
@@ -681,6 +809,10 @@ static int stm32_init_port(struct stm32_port *stm32port,
port->ops = &stm32_uart_ops;
port->dev = &pdev->dev;
port->irq = platform_get_irq(pdev, 0);
+ port->rs485_config = stm32_config_rs485;
+
+ stm32_init_rs485(port, pdev);
+
stm32port->wakeirq = platform_get_irq(pdev, 1);
stm32port->fifoen = stm32port->info->cfg.has_fifo;
diff --git a/drivers/tty/serial/stm32-usart.h b/drivers/tty/serial/stm32-usart.h
index 2294d0f05872..6f294e280ea3 100644
--- a/drivers/tty/serial/stm32-usart.h
+++ b/drivers/tty/serial/stm32-usart.h
@@ -135,6 +135,7 @@ struct stm32_usart_info stm32h7_info = {
#define USART_BRR_DIV_F_MASK GENMASK(3, 0)
#define USART_BRR_DIV_M_MASK GENMASK(15, 4)
#define USART_BRR_DIV_M_SHIFT 4
+#define USART_BRR_04_R_SHIFT 1
/* USART_CR1 */
#define USART_CR1_SBK BIT(0)
@@ -162,6 +163,8 @@ struct stm32_usart_info stm32h7_info = {
#define USART_CR1_M1 BIT(28) /* F7 */
#define USART_CR1_IE_MASK (GENMASK(8, 4) | BIT(14) | BIT(26) | BIT(27))
#define USART_CR1_FIFOEN BIT(29) /* H7 */
+#define USART_CR1_DEAT_SHIFT 21
+#define USART_CR1_DEDT_SHIFT 16
/* USART_CR2 */
#define USART_CR2_ADD_MASK GENMASK(3, 0) /* F4 */
diff --git a/drivers/tty/serial/tilegx.c b/drivers/tty/serial/tilegx.c
deleted file mode 100644
index f0a3ae57f881..000000000000
--- a/drivers/tty/serial/tilegx.c
+++ /dev/null
@@ -1,689 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright 2013 Tilera Corporation. All Rights Reserved.
- *
- * TILEGx UART driver.
- */
-
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/module.h>
-#include <linux/serial_core.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-
-#include <gxio/common.h>
-#include <gxio/iorpc_globals.h>
-#include <gxio/iorpc_uart.h>
-#include <gxio/kiorpc.h>
-
-#include <hv/drv_uart_intf.h>
-
-/*
- * Use device name ttyS, major 4, minor 64-65.
- * This is the usual serial port name, 8250 conventional range.
- */
-#define TILEGX_UART_MAJOR TTY_MAJOR
-#define TILEGX_UART_MINOR 64
-#define TILEGX_UART_NAME "ttyS"
-#define DRIVER_NAME_STRING "TILEGx_Serial"
-#define TILEGX_UART_REF_CLK 125000000; /* REF_CLK is always 125 MHz. */
-
-struct tile_uart_port {
- /* UART port. */
- struct uart_port uart;
-
- /* GXIO device context. */
- gxio_uart_context_t context;
-
- /* UART access mutex. */
- struct mutex mutex;
-
- /* CPU receiving interrupts. */
- int irq_cpu;
-};
-
-static struct tile_uart_port tile_uart_ports[TILEGX_UART_NR];
-static struct uart_driver tilegx_uart_driver;
-
-
-/*
- * Read UART rx fifo, and insert the chars into tty buffer.
- */
-static void receive_chars(struct tile_uart_port *tile_uart,
- struct tty_struct *tty)
-{
- int i;
- char c;
- UART_FIFO_COUNT_t count;
- gxio_uart_context_t *context = &tile_uart->context;
- struct tty_port *port = tty->port;
-
- count.word = gxio_uart_read(context, UART_FIFO_COUNT);
- for (i = 0; i < count.rfifo_count; i++) {
- c = (char)gxio_uart_read(context, UART_RECEIVE_DATA);
- tty_insert_flip_char(port, c, TTY_NORMAL);
- }
-}
-
-
-/*
- * Drain the Rx FIFO, called by interrupt handler.
- */
-static void handle_receive(struct tile_uart_port *tile_uart)
-{
- struct tty_port *port = &tile_uart->uart.state->port;
- struct tty_struct *tty = tty_port_tty_get(port);
- gxio_uart_context_t *context = &tile_uart->context;
-
- if (!tty)
- return;
-
- /* First read UART rx fifo. */
- receive_chars(tile_uart, tty);
-
- /* Reset RFIFO_WE interrupt. */
- gxio_uart_write(context, UART_INTERRUPT_STATUS,
- UART_INTERRUPT_MASK__RFIFO_WE_MASK);
-
- /* Final read, if any chars comes between the first read and
- * the interrupt reset.
- */
- receive_chars(tile_uart, tty);
-
- spin_unlock(&tile_uart->uart.lock);
- tty_flip_buffer_push(port);
- spin_lock(&tile_uart->uart.lock);
- tty_kref_put(tty);
-}
-
-
-/*
- * Push one char to UART Write FIFO.
- * Return 0 on success, -1 if write filo is full.
- */
-static int tilegx_putchar(gxio_uart_context_t *context, char c)
-{
- UART_FLAG_t flag;
- flag.word = gxio_uart_read(context, UART_FLAG);
- if (flag.wfifo_full)
- return -1;
-
- gxio_uart_write(context, UART_TRANSMIT_DATA, (unsigned long)c);
- return 0;
-}
-
-
-/*
- * Send chars to UART Write FIFO; called by interrupt handler.
- */
-static void handle_transmit(struct tile_uart_port *tile_uart)
-{
- unsigned char ch;
- struct uart_port *port;
- struct circ_buf *xmit;
- gxio_uart_context_t *context = &tile_uart->context;
-
- /* First reset WFIFO_RE interrupt. */
- gxio_uart_write(context, UART_INTERRUPT_STATUS,
- UART_INTERRUPT_MASK__WFIFO_RE_MASK);
-
- port = &tile_uart->uart;
- xmit = &port->state->xmit;
- if (port->x_char) {
- if (tilegx_putchar(context, port->x_char))
- return;
- port->x_char = 0;
- port->icount.tx++;
- }
-
- if (uart_circ_empty(xmit) || uart_tx_stopped(port))
- return;
-
- while (!uart_circ_empty(xmit)) {
- ch = xmit->buf[xmit->tail];
- if (tilegx_putchar(context, ch))
- break;
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
- }
-
- /* Reset WFIFO_RE interrupt. */
- gxio_uart_write(context, UART_INTERRUPT_STATUS,
- UART_INTERRUPT_MASK__WFIFO_RE_MASK);
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(port);
-}
-
-
-/*
- * UART Interrupt handler.
- */
-static irqreturn_t tilegx_interrupt(int irq, void *dev_id)
-{
- unsigned long flags;
- UART_INTERRUPT_STATUS_t intr_stat;
- struct tile_uart_port *tile_uart;
- gxio_uart_context_t *context;
- struct uart_port *port = dev_id;
- irqreturn_t ret = IRQ_NONE;
-
- spin_lock_irqsave(&port->lock, flags);
-
- tile_uart = container_of(port, struct tile_uart_port, uart);
- context = &tile_uart->context;
- intr_stat.word = gxio_uart_read(context, UART_INTERRUPT_STATUS);
-
- if (intr_stat.rfifo_we) {
- handle_receive(tile_uart);
- ret = IRQ_HANDLED;
- }
- if (intr_stat.wfifo_re) {
- handle_transmit(tile_uart);
- ret = IRQ_HANDLED;
- }
-
- spin_unlock_irqrestore(&port->lock, flags);
- return ret;
-}
-
-
-/*
- * Return TIOCSER_TEMT when transmitter FIFO is empty.
- */
-static u_int tilegx_tx_empty(struct uart_port *port)
-{
- int ret;
- UART_FLAG_t flag;
- struct tile_uart_port *tile_uart;
- gxio_uart_context_t *context;
-
- tile_uart = container_of(port, struct tile_uart_port, uart);
- if (!mutex_trylock(&tile_uart->mutex))
- return 0;
- context = &tile_uart->context;
-
- flag.word = gxio_uart_read(context, UART_FLAG);
- ret = (flag.wfifo_empty) ? TIOCSER_TEMT : 0;
- mutex_unlock(&tile_uart->mutex);
-
- return ret;
-}
-
-
-/*
- * Set state of the modem control output lines.
- */
-static void tilegx_set_mctrl(struct uart_port *port, u_int mctrl)
-{
- /* N/A */
-}
-
-
-/*
- * Get state of the modem control input lines.
- */
-static u_int tilegx_get_mctrl(struct uart_port *port)
-{
- return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
-}
-
-
-/*
- * Stop transmitting.
- */
-static void tilegx_stop_tx(struct uart_port *port)
-{
- /* N/A */
-}
-
-
-/*
- * Start transmitting.
- */
-static void tilegx_start_tx(struct uart_port *port)
-{
- unsigned char ch;
- struct circ_buf *xmit;
- struct tile_uart_port *tile_uart;
- gxio_uart_context_t *context;
-
- tile_uart = container_of(port, struct tile_uart_port, uart);
- if (!mutex_trylock(&tile_uart->mutex))
- return;
- context = &tile_uart->context;
- xmit = &port->state->xmit;
- if (port->x_char) {
- if (tilegx_putchar(context, port->x_char))
- return;
- port->x_char = 0;
- port->icount.tx++;
- }
-
- if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
- mutex_unlock(&tile_uart->mutex);
- return;
- }
-
- while (!uart_circ_empty(xmit)) {
- ch = xmit->buf[xmit->tail];
- if (tilegx_putchar(context, ch))
- break;
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
- }
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(port);
-
- mutex_unlock(&tile_uart->mutex);
-}
-
-
-/*
- * Stop receiving - port is in process of being closed.
- */
-static void tilegx_stop_rx(struct uart_port *port)
-{
- int err;
- struct tile_uart_port *tile_uart;
- gxio_uart_context_t *context;
- int cpu;
-
- tile_uart = container_of(port, struct tile_uart_port, uart);
- if (!mutex_trylock(&tile_uart->mutex))
- return;
-
- context = &tile_uart->context;
- cpu = tile_uart->irq_cpu;
- err = gxio_uart_cfg_interrupt(context, cpu_x(cpu), cpu_y(cpu),
- KERNEL_PL, -1);
- mutex_unlock(&tile_uart->mutex);
-}
-
-/*
- * Control the transmission of a break signal.
- */
-static void tilegx_break_ctl(struct uart_port *port, int break_state)
-{
- /* N/A */
-}
-
-
-/*
- * Perform initialization and enable port for reception.
- */
-static int tilegx_startup(struct uart_port *port)
-{
- struct tile_uart_port *tile_uart;
- gxio_uart_context_t *context;
- int ret = 0;
- int cpu = raw_smp_processor_id(); /* pick an arbitrary cpu */
-
- tile_uart = container_of(port, struct tile_uart_port, uart);
- if (mutex_lock_interruptible(&tile_uart->mutex))
- return -EBUSY;
- context = &tile_uart->context;
-
- /* Now open the hypervisor device if we haven't already. */
- if (context->fd < 0) {
- UART_INTERRUPT_MASK_t intr_mask;
-
- /* Initialize UART device. */
- ret = gxio_uart_init(context, port->line);
- if (ret) {
- ret = -ENXIO;
- goto err;
- }
-
- /* Create our IRQs. */
- port->irq = irq_alloc_hwirq(-1);
- if (!port->irq)
- goto err_uart_dest;
- tile_irq_activate(port->irq, TILE_IRQ_PERCPU);
-
- /* Register our IRQs. */
- ret = request_irq(port->irq, tilegx_interrupt, 0,
- tilegx_uart_driver.driver_name, port);
- if (ret)
- goto err_dest_irq;
-
- /* Request that the hardware start sending us interrupts. */
- tile_uart->irq_cpu = cpu;
- ret = gxio_uart_cfg_interrupt(context, cpu_x(cpu), cpu_y(cpu),
- KERNEL_PL, port->irq);
- if (ret)
- goto err_free_irq;
-
- /* Enable UART Tx/Rx Interrupt. */
- intr_mask.word = gxio_uart_read(context, UART_INTERRUPT_MASK);
- intr_mask.wfifo_re = 0;
- intr_mask.rfifo_we = 0;
- gxio_uart_write(context, UART_INTERRUPT_MASK, intr_mask.word);
-
- /* Reset the Tx/Rx interrupt in case it's set. */
- gxio_uart_write(context, UART_INTERRUPT_STATUS,
- UART_INTERRUPT_MASK__WFIFO_RE_MASK |
- UART_INTERRUPT_MASK__RFIFO_WE_MASK);
- }
-
- mutex_unlock(&tile_uart->mutex);
- return ret;
-
-err_free_irq:
- free_irq(port->irq, port);
-err_dest_irq:
- irq_free_hwirq(port->irq);
-err_uart_dest:
- gxio_uart_destroy(context);
- ret = -ENXIO;
-err:
- mutex_unlock(&tile_uart->mutex);
- return ret;
-}
-
-
-/*
- * Release kernel resources if it is the last close, disable the port,
- * free IRQ and close the port.
- */
-static void tilegx_shutdown(struct uart_port *port)
-{
- int err;
- UART_INTERRUPT_MASK_t intr_mask;
- struct tile_uart_port *tile_uart;
- gxio_uart_context_t *context;
- int cpu;
-
- tile_uart = container_of(port, struct tile_uart_port, uart);
- if (mutex_lock_interruptible(&tile_uart->mutex))
- return;
- context = &tile_uart->context;
-
- /* Disable UART Tx/Rx Interrupt. */
- intr_mask.word = gxio_uart_read(context, UART_INTERRUPT_MASK);
- intr_mask.wfifo_re = 1;
- intr_mask.rfifo_we = 1;
- gxio_uart_write(context, UART_INTERRUPT_MASK, intr_mask.word);
-
- /* Request that the hardware stop sending us interrupts. */
- cpu = tile_uart->irq_cpu;
- err = gxio_uart_cfg_interrupt(context, cpu_x(cpu), cpu_y(cpu),
- KERNEL_PL, -1);
-
- if (port->irq > 0) {
- free_irq(port->irq, port);
- irq_free_hwirq(port->irq);
- port->irq = 0;
- }
-
- gxio_uart_destroy(context);
-
- mutex_unlock(&tile_uart->mutex);
-}
-
-
-/*
- * Flush the buffer.
- */
-static void tilegx_flush_buffer(struct uart_port *port)
-{
- /* N/A */
-}
-
-
-/*
- * Change the port parameters.
- */
-static void tilegx_set_termios(struct uart_port *port,
- struct ktermios *termios, struct ktermios *old)
-{
- int err;
- UART_DIVISOR_t divisor;
- UART_TYPE_t type;
- unsigned int baud;
- struct tile_uart_port *tile_uart;
- gxio_uart_context_t *context;
-
- tile_uart = container_of(port, struct tile_uart_port, uart);
- if (!mutex_trylock(&tile_uart->mutex))
- return;
- context = &tile_uart->context;
-
- /* Open the hypervisor device if we haven't already. */
- if (context->fd < 0) {
- err = gxio_uart_init(context, port->line);
- if (err) {
- mutex_unlock(&tile_uart->mutex);
- return;
- }
- }
-
- divisor.word = gxio_uart_read(context, UART_DIVISOR);
- type.word = gxio_uart_read(context, UART_TYPE);
-
- /* Divisor. */
- baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
- divisor.divisor = uart_get_divisor(port, baud);
-
- /* Byte size. */
- if ((termios->c_cflag & CSIZE) == CS7)
- type.dbits = UART_TYPE__DBITS_VAL_SEVEN_DBITS;
- else
- type.dbits = UART_TYPE__DBITS_VAL_EIGHT_DBITS;
-
- /* Parity. */
- if (termios->c_cflag & PARENB) {
- /* Mark or Space parity. */
- if (termios->c_cflag & CMSPAR)
- if (termios->c_cflag & PARODD)
- type.ptype = UART_TYPE__PTYPE_VAL_MARK;
- else
- type.ptype = UART_TYPE__PTYPE_VAL_SPACE;
- else if (termios->c_cflag & PARODD)
- type.ptype = UART_TYPE__PTYPE_VAL_ODD;
- else
- type.ptype = UART_TYPE__PTYPE_VAL_EVEN;
- } else
- type.ptype = UART_TYPE__PTYPE_VAL_NONE;
-
- /* Stop bits. */
- if (termios->c_cflag & CSTOPB)
- type.sbits = UART_TYPE__SBITS_VAL_TWO_SBITS;
- else
- type.sbits = UART_TYPE__SBITS_VAL_ONE_SBITS;
-
- /* Set the uart paramters. */
- gxio_uart_write(context, UART_DIVISOR, divisor.word);
- gxio_uart_write(context, UART_TYPE, type.word);
-
- mutex_unlock(&tile_uart->mutex);
-}
-
-
-/*
- * Return string describing the specified port.
- */
-static const char *tilegx_type(struct uart_port *port)
-{
- return port->type == PORT_TILEGX ? DRIVER_NAME_STRING : NULL;
-}
-
-
-/*
- * Release the resources being used by 'port'.
- */
-static void tilegx_release_port(struct uart_port *port)
-{
- /* Nothing to release. */
-}
-
-
-/*
- * Request the resources being used by 'port'.
- */
-static int tilegx_request_port(struct uart_port *port)
-{
- /* Always present. */
- return 0;
-}
-
-
-/*
- * Configure/autoconfigure the port.
- */
-static void tilegx_config_port(struct uart_port *port, int flags)
-{
- if (flags & UART_CONFIG_TYPE)
- port->type = PORT_TILEGX;
-}
-
-
-/*
- * Verify the new serial_struct (for TIOCSSERIAL).
- */
-static int tilegx_verify_port(struct uart_port *port,
- struct serial_struct *ser)
-{
- if ((ser->type != PORT_UNKNOWN) && (ser->type != PORT_TILEGX))
- return -EINVAL;
-
- return 0;
-}
-
-#ifdef CONFIG_CONSOLE_POLL
-
-/*
- * Console polling routines for writing and reading from the uart while
- * in an interrupt or debug context.
- */
-
-static int tilegx_poll_get_char(struct uart_port *port)
-{
- UART_FIFO_COUNT_t count;
- gxio_uart_context_t *context;
- struct tile_uart_port *tile_uart;
-
- tile_uart = container_of(port, struct tile_uart_port, uart);
- context = &tile_uart->context;
- count.word = gxio_uart_read(context, UART_FIFO_COUNT);
- if (count.rfifo_count == 0)
- return NO_POLL_CHAR;
- return (char)gxio_uart_read(context, UART_RECEIVE_DATA);
-}
-
-static void tilegx_poll_put_char(struct uart_port *port, unsigned char c)
-{
- gxio_uart_context_t *context;
- struct tile_uart_port *tile_uart;
-
- tile_uart = container_of(port, struct tile_uart_port, uart);
- context = &tile_uart->context;
- gxio_uart_write(context, UART_TRANSMIT_DATA, (unsigned long)c);
-}
-
-#endif /* CONFIG_CONSOLE_POLL */
-
-
-static const struct uart_ops tilegx_ops = {
- .tx_empty = tilegx_tx_empty,
- .set_mctrl = tilegx_set_mctrl,
- .get_mctrl = tilegx_get_mctrl,
- .stop_tx = tilegx_stop_tx,
- .start_tx = tilegx_start_tx,
- .stop_rx = tilegx_stop_rx,
- .break_ctl = tilegx_break_ctl,
- .startup = tilegx_startup,
- .shutdown = tilegx_shutdown,
- .flush_buffer = tilegx_flush_buffer,
- .set_termios = tilegx_set_termios,
- .type = tilegx_type,
- .release_port = tilegx_release_port,
- .request_port = tilegx_request_port,
- .config_port = tilegx_config_port,
- .verify_port = tilegx_verify_port,
-#ifdef CONFIG_CONSOLE_POLL
- .poll_get_char = tilegx_poll_get_char,
- .poll_put_char = tilegx_poll_put_char,
-#endif
-};
-
-
-static void tilegx_init_ports(void)
-{
- int i;
- struct uart_port *port;
-
- for (i = 0; i < TILEGX_UART_NR; i++) {
- port = &tile_uart_ports[i].uart;
- port->ops = &tilegx_ops;
- port->line = i;
- port->type = PORT_TILEGX;
- port->uartclk = TILEGX_UART_REF_CLK;
- port->flags = UPF_BOOT_AUTOCONF;
-
- tile_uart_ports[i].context.fd = -1;
- mutex_init(&tile_uart_ports[i].mutex);
- }
-}
-
-
-static struct uart_driver tilegx_uart_driver = {
- .owner = THIS_MODULE,
- .driver_name = DRIVER_NAME_STRING,
- .dev_name = TILEGX_UART_NAME,
- .major = TILEGX_UART_MAJOR,
- .minor = TILEGX_UART_MINOR,
- .nr = TILEGX_UART_NR,
-};
-
-
-static int __init tilegx_init(void)
-{
- int i;
- int ret;
- struct tty_driver *tty_drv;
-
- ret = uart_register_driver(&tilegx_uart_driver);
- if (ret)
- return ret;
- tty_drv = tilegx_uart_driver.tty_driver;
- tty_drv->init_termios.c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL;
- tty_drv->init_termios.c_ispeed = 115200;
- tty_drv->init_termios.c_ospeed = 115200;
-
- tilegx_init_ports();
-
- for (i = 0; i < TILEGX_UART_NR; i++) {
- struct uart_port *port = &tile_uart_ports[i].uart;
- ret = uart_add_one_port(&tilegx_uart_driver, port);
- }
-
- return 0;
-}
-
-
-static void __exit tilegx_exit(void)
-{
- int i;
- struct uart_port *port;
-
- for (i = 0; i < TILEGX_UART_NR; i++) {
- port = &tile_uart_ports[i].uart;
- uart_remove_one_port(&tilegx_uart_driver, port);
- }
-
- uart_unregister_driver(&tilegx_uart_driver);
-}
-
-
-module_init(tilegx_init);
-module_exit(tilegx_exit);
-
-MODULE_AUTHOR("Tilera Corporation");
-MODULE_DESCRIPTION("TILEGx serial port driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c
index b9b2bc76bcac..bd72dd843338 100644
--- a/drivers/tty/serial/xilinx_uartps.c
+++ b/drivers/tty/serial/xilinx_uartps.c
@@ -1110,7 +1110,7 @@ static struct uart_port *cdns_uart_get_port(int id)
struct uart_port *port;
/* Try the given port id if failed use default method */
- if (cdns_uart_port[id].mapbase != 0) {
+ if (id < CDNS_UART_NR_PORTS && cdns_uart_port[id].mapbase != 0) {
/* Find the next unused port */
for (id = 0; id < CDNS_UART_NR_PORTS; id++)
if (cdns_uart_port[id].mapbase == 0)
@@ -1181,7 +1181,7 @@ static int __init cdns_early_console_setup(struct earlycon_device *device,
/* only set baud if specified on command line - otherwise
* assume it has been initialized by a boot loader.
*/
- if (device->baud) {
+ if (port->uartclk && device->baud) {
u32 cd = 0, bdiv = 0;
u32 mr;
int div8;
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index b674793be478..6364890575ec 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -660,7 +660,7 @@ static void sysrq_do_reset(struct timer_list *t)
state->reset_requested = true;
- sys_sync();
+ ksys_sync();
kernel_restart(NULL);
}
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 63114ea35ec1..7c838b90a31d 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -2816,7 +2816,10 @@ struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx)
kref_init(&tty->kref);
tty->magic = TTY_MAGIC;
- tty_ldisc_init(tty);
+ if (tty_ldisc_init(tty)) {
+ kfree(tty);
+ return NULL;
+ }
tty->session = NULL;
tty->pgrp = NULL;
mutex_init(&tty->legacy_mutex);
diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c
index 050f4d650891..fb7329ab2b37 100644
--- a/drivers/tty/tty_ldisc.c
+++ b/drivers/tty/tty_ldisc.c
@@ -176,12 +176,11 @@ static struct tty_ldisc *tty_ldisc_get(struct tty_struct *tty, int disc)
return ERR_CAST(ldops);
}
- ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL);
- if (ld == NULL) {
- put_ldops(ldops);
- return ERR_PTR(-ENOMEM);
- }
-
+ /*
+ * There is no way to handle allocation failure of only 16 bytes.
+ * Let's simplify error handling and save more memory.
+ */
+ ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL | __GFP_NOFAIL);
ld->ops = ldops;
ld->tty = tty;
@@ -527,19 +526,16 @@ static int tty_ldisc_failto(struct tty_struct *tty, int ld)
static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
{
/* There is an outstanding reference here so this is safe */
- old = tty_ldisc_get(tty, old->ops->num);
- WARN_ON(IS_ERR(old));
- tty->ldisc = old;
- tty_set_termios_ldisc(tty, old->ops->num);
- if (tty_ldisc_open(tty, old) < 0) {
- tty_ldisc_put(old);
+ if (tty_ldisc_failto(tty, old->ops->num) < 0) {
+ const char *name = tty_name(tty);
+
+ pr_warn("Falling back ldisc for %s.\n", name);
/* The traditional behaviour is to fall back to N_TTY, we
want to avoid falling back to N_NULL unless we have no
choice to avoid the risk of breaking anything */
if (tty_ldisc_failto(tty, N_TTY) < 0 &&
tty_ldisc_failto(tty, N_NULL) < 0)
- panic("Couldn't open N_NULL ldisc for %s.",
- tty_name(tty));
+ panic("Couldn't open N_NULL ldisc for %s.", name);
}
}
@@ -824,12 +820,13 @@ EXPORT_SYMBOL_GPL(tty_ldisc_release);
* the tty structure is not completely set up when this call is made.
*/
-void tty_ldisc_init(struct tty_struct *tty)
+int tty_ldisc_init(struct tty_struct *tty)
{
struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY);
if (IS_ERR(ld))
- panic("n_tty: init_tty");
+ return PTR_ERR(ld);
tty->ldisc = ld;
+ return 0;
}
/**
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index b4e57c5a8bba..f97251f39c26 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -1354,6 +1354,11 @@ static void csi_m(struct vc_data *vc)
case 3:
vc->vc_italic = 1;
break;
+ case 21:
+ /*
+ * No console drivers support double underline, so
+ * convert it to a single underline.
+ */
case 4:
vc->vc_underline = 1;
break;
@@ -1389,7 +1394,6 @@ static void csi_m(struct vc_data *vc)
vc->vc_disp_ctrl = 1;
vc->vc_toggle_meta = 1;
break;
- case 21:
case 22:
vc->vc_intensity = 1;
break;
diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c
index d61be307256a..a78ad10a119b 100644
--- a/drivers/tty/vt/vt_ioctl.c
+++ b/drivers/tty/vt/vt_ioctl.c
@@ -57,7 +57,7 @@ extern struct tty_driver *console_driver;
*/
#ifdef CONFIG_X86
-#include <linux/syscalls.h>
+#include <asm/syscalls.h>
#endif
static void complete_change_console(struct vc_data *vc);
@@ -420,12 +420,12 @@ int vt_ioctl(struct tty_struct *tty,
ret = -EINVAL;
break;
}
- ret = sys_ioperm(arg, 1, (cmd == KDADDIO)) ? -ENXIO : 0;
+ ret = ksys_ioperm(arg, 1, (cmd == KDADDIO)) ? -ENXIO : 0;
break;
case KDENABIO:
case KDDISABIO:
- ret = sys_ioperm(GPFIRST, GPNUM,
+ ret = ksys_ioperm(GPFIRST, GPNUM,
(cmd == KDENABIO)) ? -ENXIO : 0;
break;
#endif
diff --git a/drivers/uio/uio_hv_generic.c b/drivers/uio/uio_hv_generic.c
index 8ca549032c27..c690d100adcd 100644
--- a/drivers/uio/uio_hv_generic.c
+++ b/drivers/uio/uio_hv_generic.c
@@ -19,7 +19,7 @@
* # echo -n "ed963694-e847-4b2a-85af-bc9cfc11d6f3" \
* > /sys/bus/vmbus/drivers/uio_hv_generic/bind
*/
-
+#define DEBUG 1
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/device.h>
@@ -94,10 +94,11 @@ hv_uio_irqcontrol(struct uio_info *info, s32 irq_state)
*/
static void hv_uio_channel_cb(void *context)
{
- struct hv_uio_private_data *pdata = context;
- struct hv_device *dev = pdata->device;
+ struct vmbus_channel *chan = context;
+ struct hv_device *hv_dev = chan->device_obj;
+ struct hv_uio_private_data *pdata = hv_get_drvdata(hv_dev);
- dev->channel->inbound.ring_buffer->interrupt_mask = 1;
+ chan->inbound.ring_buffer->interrupt_mask = 1;
virt_mb();
uio_event_notify(&pdata->info);
@@ -121,6 +122,62 @@ static void hv_uio_rescind(struct vmbus_channel *channel)
uio_event_notify(&pdata->info);
}
+/* Sysfs API to allow mmap of the ring buffers
+ * The ring buffer is allocated as contiguous memory by vmbus_open
+ */
+static int hv_uio_ring_mmap(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ struct vm_area_struct *vma)
+{
+ struct vmbus_channel *channel
+ = container_of(kobj, struct vmbus_channel, kobj);
+ struct hv_device *dev = channel->primary_channel->device_obj;
+ u16 q_idx = channel->offermsg.offer.sub_channel_index;
+
+ dev_dbg(&dev->device, "mmap channel %u pages %#lx at %#lx\n",
+ q_idx, vma_pages(vma), vma->vm_pgoff);
+
+ return vm_iomap_memory(vma, virt_to_phys(channel->ringbuffer_pages),
+ channel->ringbuffer_pagecount << PAGE_SHIFT);
+}
+
+static const struct bin_attribute ring_buffer_bin_attr = {
+ .attr = {
+ .name = "ring",
+ .mode = 0600,
+ },
+ .size = 2 * HV_RING_SIZE * PAGE_SIZE,
+ .mmap = hv_uio_ring_mmap,
+};
+
+/* Callback from VMBUS subsystem when new channel created. */
+static void
+hv_uio_new_channel(struct vmbus_channel *new_sc)
+{
+ struct hv_device *hv_dev = new_sc->primary_channel->device_obj;
+ struct device *device = &hv_dev->device;
+ const size_t ring_bytes = HV_RING_SIZE * PAGE_SIZE;
+ int ret;
+
+ /* Create host communication ring */
+ ret = vmbus_open(new_sc, ring_bytes, ring_bytes, NULL, 0,
+ hv_uio_channel_cb, new_sc);
+ if (ret) {
+ dev_err(device, "vmbus_open subchannel failed: %d\n", ret);
+ return;
+ }
+
+ /* Disable interrupts on sub channel */
+ new_sc->inbound.ring_buffer->interrupt_mask = 1;
+ set_channel_read_mode(new_sc, HV_CALL_ISR);
+
+ ret = sysfs_create_bin_file(&new_sc->kobj, &ring_buffer_bin_attr);
+ if (ret) {
+ dev_err(device, "sysfs create ring bin file failed; %d\n", ret);
+ vmbus_close(new_sc);
+ }
+}
+
static void
hv_uio_cleanup(struct hv_device *dev, struct hv_uio_private_data *pdata)
{
@@ -146,7 +203,7 @@ hv_uio_probe(struct hv_device *dev,
ret = vmbus_open(dev->channel, HV_RING_SIZE * PAGE_SIZE,
HV_RING_SIZE * PAGE_SIZE, NULL, 0,
- hv_uio_channel_cb, pdata);
+ hv_uio_channel_cb, dev->channel);
if (ret)
goto fail;
@@ -236,6 +293,12 @@ hv_uio_probe(struct hv_device *dev,
}
vmbus_set_chn_rescind_callback(dev->channel, hv_uio_rescind);
+ vmbus_set_sc_create_callback(dev->channel, hv_uio_new_channel);
+
+ ret = sysfs_create_bin_file(&dev->channel->kobj, &ring_buffer_bin_attr);
+ if (ret)
+ dev_notice(&dev->device,
+ "sysfs create ring bin file failed; %d\n", ret);
hv_set_drvdata(dev, pdata);
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 148f3ee70286..987fc5ba6321 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -171,6 +171,8 @@ source "drivers/usb/gadget/Kconfig"
source "drivers/usb/typec/Kconfig"
+source "drivers/usb/roles/Kconfig"
+
config USB_LED_TRIG
bool "USB LED Triggers"
depends on LEDS_CLASS && LEDS_TRIGGERS
@@ -203,4 +205,8 @@ config USB_ULPI_BUS
To compile this driver as a module, choose M here: the module will
be called ulpi.
+config USB_ROLE_SWITCH
+ tristate
+ select USB_COMMON
+
endif # USB_SUPPORT
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 060643a1b5c8..7d1b8c82b208 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -65,3 +65,5 @@ obj-$(CONFIG_USB_COMMON) += common/
obj-$(CONFIG_USBIP_CORE) += usbip/
obj-$(CONFIG_TYPEC) += typec/
+
+obj-$(CONFIG_USB_ROLE_SWITCH) += roles/
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c
index 3b45c25f296e..e431c5aafe35 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.c
+++ b/drivers/usb/chipidea/ci_hdrc_imx.c
@@ -20,7 +20,6 @@
struct ci_hdrc_imx_platform_flag {
unsigned int flags;
- bool runtime_pm;
};
static const struct ci_hdrc_imx_platform_flag imx23_usb_data = {
@@ -29,7 +28,7 @@ static const struct ci_hdrc_imx_platform_flag imx23_usb_data = {
};
static const struct ci_hdrc_imx_platform_flag imx27_usb_data = {
- CI_HDRC_DISABLE_STREAMING,
+ .flags = CI_HDRC_DISABLE_STREAMING,
};
static const struct ci_hdrc_imx_platform_flag imx28_usb_data = {
@@ -84,6 +83,7 @@ struct ci_hdrc_imx_data {
struct clk *clk;
struct imx_usbmisc_data *usbmisc_data;
bool supports_runtime_pm;
+ bool override_phy_control;
bool in_lpm;
/* SoC before i.mx6 (except imx23/imx28) needs three clks */
bool need_three_clks;
@@ -255,6 +255,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
int ret;
const struct of_device_id *of_id;
const struct ci_hdrc_imx_platform_flag *imx_platform_flag;
+ struct device_node *np = pdev->dev.of_node;
of_id = of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev);
if (!of_id)
@@ -289,6 +290,14 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
}
pdata.usb_phy = data->phy;
+
+ if (of_device_is_compatible(np, "fsl,imx53-usb") && pdata.usb_phy &&
+ of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI) {
+ pdata.flags |= CI_HDRC_OVERRIDE_PHY_CONTROL;
+ data->override_phy_control = true;
+ usb_phy_init(pdata.usb_phy);
+ }
+
pdata.flags |= imx_platform_flag->flags;
if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM)
data->supports_runtime_pm = true;
@@ -342,6 +351,8 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev)
pm_runtime_put_noidle(&pdev->dev);
}
ci_hdrc_remove_device(data->ci_pdev);
+ if (data->override_phy_control)
+ usb_phy_shutdown(data->phy);
imx_disable_unprepare_clks(&pdev->dev);
return 0;
diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c
index c9e1a165ed82..ce648cb3ed94 100644
--- a/drivers/usb/chipidea/debug.c
+++ b/drivers/usb/chipidea/debug.c
@@ -45,18 +45,7 @@ static int ci_device_show(struct seq_file *s, void *data)
return 0;
}
-
-static int ci_device_open(struct inode *inode, struct file *file)
-{
- return single_open(file, ci_device_show, inode->i_private);
-}
-
-static const struct file_operations ci_device_fops = {
- .open = ci_device_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(ci_device);
/**
* ci_port_test_show: reads port test mode
@@ -156,18 +145,7 @@ static int ci_qheads_show(struct seq_file *s, void *data)
return 0;
}
-
-static int ci_qheads_open(struct inode *inode, struct file *file)
-{
- return single_open(file, ci_qheads_show, inode->i_private);
-}
-
-static const struct file_operations ci_qheads_fops = {
- .open = ci_qheads_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(ci_qheads);
/**
* ci_requests_show: DMA contents of all requests currently queued (all endpts)
@@ -204,18 +182,7 @@ static int ci_requests_show(struct seq_file *s, void *data)
return 0;
}
-
-static int ci_requests_open(struct inode *inode, struct file *file)
-{
- return single_open(file, ci_requests_show, inode->i_private);
-}
-
-static const struct file_operations ci_requests_fops = {
- .open = ci_requests_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(ci_requests);
static int ci_otg_show(struct seq_file *s, void *unused)
{
@@ -278,18 +245,7 @@ static int ci_otg_show(struct seq_file *s, void *unused)
return 0;
}
-
-static int ci_otg_open(struct inode *inode, struct file *file)
-{
- return single_open(file, ci_otg_show, inode->i_private);
-}
-
-static const struct file_operations ci_otg_fops = {
- .open = ci_otg_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(ci_otg);
static int ci_role_show(struct seq_file *s, void *data)
{
@@ -376,18 +332,7 @@ static int ci_registers_show(struct seq_file *s, void *unused)
return 0;
}
-
-static int ci_registers_open(struct inode *inode, struct file *file)
-{
- return single_open(file, ci_registers_show, inode->i_private);
-}
-
-static const struct file_operations ci_registers_fops = {
- .open = ci_registers_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(ci_registers);
/**
* dbg_create_files: initializes the attribute interface
diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c
index 19d60ed7e41f..af45aa3222b5 100644
--- a/drivers/usb/chipidea/host.c
+++ b/drivers/usb/chipidea/host.c
@@ -124,10 +124,8 @@ static int host_start(struct ci_hdrc *ci)
hcd->power_budget = ci->platdata->power_budget;
hcd->tpl_support = ci->platdata->tpl_support;
- if (ci->phy)
- hcd->phy = ci->phy;
- else
- hcd->usb_phy = ci->usb_phy;
+ if (ci->phy || ci->usb_phy)
+ hcd->skip_phy_initialization = 1;
ehci = hcd_to_ehci(hcd);
ehci->caps = ci->hw_bank.cap;
diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c
index 8cdf0af156c6..34ad5bf8acd8 100644
--- a/drivers/usb/chipidea/usbmisc_imx.c
+++ b/drivers/usb/chipidea/usbmisc_imx.c
@@ -148,14 +148,21 @@ static int usbmisc_imx25_post(struct imx_usbmisc_data *data)
if (data->index > 2)
return -EINVAL;
- if (data->evdo) {
- spin_lock_irqsave(&usbmisc->lock, flags);
- reg = usbmisc->base + MX25_USB_PHY_CTRL_OFFSET;
- val = readl(reg);
- writel(val | MX25_BM_EXTERNAL_VBUS_DIVIDER, reg);
- spin_unlock_irqrestore(&usbmisc->lock, flags);
- usleep_range(5000, 10000); /* needed to stabilize voltage */
- }
+ if (data->index)
+ return 0;
+
+ spin_lock_irqsave(&usbmisc->lock, flags);
+ reg = usbmisc->base + MX25_USB_PHY_CTRL_OFFSET;
+ val = readl(reg);
+
+ if (data->evdo)
+ val |= MX25_BM_EXTERNAL_VBUS_DIVIDER;
+ else
+ val &= ~MX25_BM_EXTERNAL_VBUS_DIVIDER;
+
+ writel(val, reg);
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+ usleep_range(5000, 10000); /* needed to stabilize voltage */
return 0;
}
@@ -308,13 +315,12 @@ static int usbmisc_imx6q_set_wakeup
val = readl(usbmisc->base + data->index * 4);
if (enabled) {
val |= wakeup_setting;
- writel(val, usbmisc->base + data->index * 4);
} else {
if (val & MX6_BM_WAKEUP_INTR)
pr_debug("wakeup int at ci_hdrc.%d\n", data->index);
val &= ~wakeup_setting;
- writel(val, usbmisc->base + data->index * 4);
}
+ writel(val, usbmisc->base + data->index * 4);
spin_unlock_irqrestore(&usbmisc->lock, flags);
return ret;
diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile
index 0a7c45e85481..fb4d5ef4165c 100644
--- a/drivers/usb/common/Makefile
+++ b/drivers/usb/common/Makefile
@@ -9,3 +9,4 @@ usb-common-$(CONFIG_USB_LED_TRIG) += led.o
obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o
obj-$(CONFIG_USB_ULPI_BUS) += ulpi.o
+obj-$(CONFIG_USB_ROLE_SWITCH) += roles.o
diff --git a/drivers/usb/common/roles.c b/drivers/usb/common/roles.c
new file mode 100644
index 000000000000..15cc76e22123
--- /dev/null
+++ b/drivers/usb/common/roles.c
@@ -0,0 +1,305 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USB Role Switch Support
+ *
+ * Copyright (C) 2018 Intel Corporation
+ * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+ * Hans de Goede <hdegoede@redhat.com>
+ */
+
+#include <linux/usb/role.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+static struct class *role_class;
+
+struct usb_role_switch {
+ struct device dev;
+ struct mutex lock; /* device lock*/
+ enum usb_role role;
+
+ /* From descriptor */
+ struct device *usb2_port;
+ struct device *usb3_port;
+ struct device *udc;
+ usb_role_switch_set_t set;
+ usb_role_switch_get_t get;
+ bool allow_userspace_control;
+};
+
+#define to_role_switch(d) container_of(d, struct usb_role_switch, dev)
+
+/**
+ * usb_role_switch_set_role - Set USB role for a switch
+ * @sw: USB role switch
+ * @role: USB role to be switched to
+ *
+ * Set USB role @role for @sw.
+ */
+int usb_role_switch_set_role(struct usb_role_switch *sw, enum usb_role role)
+{
+ int ret;
+
+ if (IS_ERR_OR_NULL(sw))
+ return 0;
+
+ mutex_lock(&sw->lock);
+
+ ret = sw->set(sw->dev.parent, role);
+ if (!ret)
+ sw->role = role;
+
+ mutex_unlock(&sw->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_role_switch_set_role);
+
+/**
+ * usb_role_switch_get_role - Get the USB role for a switch
+ * @sw: USB role switch
+ *
+ * Depending on the role-switch-driver this function returns either a cached
+ * value of the last set role, or reads back the actual value from the hardware.
+ */
+enum usb_role usb_role_switch_get_role(struct usb_role_switch *sw)
+{
+ enum usb_role role;
+
+ if (IS_ERR_OR_NULL(sw))
+ return USB_ROLE_NONE;
+
+ mutex_lock(&sw->lock);
+
+ if (sw->get)
+ role = sw->get(sw->dev.parent);
+ else
+ role = sw->role;
+
+ mutex_unlock(&sw->lock);
+
+ return role;
+}
+EXPORT_SYMBOL_GPL(usb_role_switch_get_role);
+
+static int __switch_match(struct device *dev, const void *name)
+{
+ return !strcmp((const char *)name, dev_name(dev));
+}
+
+static void *usb_role_switch_match(struct device_connection *con, int ep,
+ void *data)
+{
+ struct device *dev;
+
+ dev = class_find_device(role_class, NULL, con->endpoint[ep],
+ __switch_match);
+
+ return dev ? to_role_switch(dev) : ERR_PTR(-EPROBE_DEFER);
+}
+
+/**
+ * usb_role_switch_get - Find USB role switch linked with the caller
+ * @dev: The caller device
+ *
+ * Finds and returns role switch linked with @dev. The reference count for the
+ * found switch is incremented.
+ */
+struct usb_role_switch *usb_role_switch_get(struct device *dev)
+{
+ return device_connection_find_match(dev, "usb-role-switch", NULL,
+ usb_role_switch_match);
+}
+EXPORT_SYMBOL_GPL(usb_role_switch_get);
+
+/**
+ * usb_role_switch_put - Release handle to a switch
+ * @sw: USB Role Switch
+ *
+ * Decrement reference count for @sw.
+ */
+void usb_role_switch_put(struct usb_role_switch *sw)
+{
+ if (!IS_ERR_OR_NULL(sw))
+ put_device(&sw->dev);
+}
+EXPORT_SYMBOL_GPL(usb_role_switch_put);
+
+static umode_t
+usb_role_switch_is_visible(struct kobject *kobj, struct attribute *attr, int n)
+{
+ struct device *dev = container_of(kobj, typeof(*dev), kobj);
+ struct usb_role_switch *sw = to_role_switch(dev);
+
+ if (sw->allow_userspace_control)
+ return attr->mode;
+
+ return 0;
+}
+
+static const char * const usb_roles[] = {
+ [USB_ROLE_NONE] = "none",
+ [USB_ROLE_HOST] = "host",
+ [USB_ROLE_DEVICE] = "device",
+};
+
+static ssize_t
+role_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct usb_role_switch *sw = to_role_switch(dev);
+ enum usb_role role = usb_role_switch_get_role(sw);
+
+ return sprintf(buf, "%s\n", usb_roles[role]);
+}
+
+static ssize_t role_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct usb_role_switch *sw = to_role_switch(dev);
+ int ret;
+
+ ret = sysfs_match_string(usb_roles, buf);
+ if (ret < 0) {
+ bool res;
+
+ /* Extra check if the user wants to disable the switch */
+ ret = kstrtobool(buf, &res);
+ if (ret || res)
+ return -EINVAL;
+ }
+
+ ret = usb_role_switch_set_role(sw, ret);
+ if (ret)
+ return ret;
+
+ return size;
+}
+static DEVICE_ATTR_RW(role);
+
+static struct attribute *usb_role_switch_attrs[] = {
+ &dev_attr_role.attr,
+ NULL,
+};
+
+static const struct attribute_group usb_role_switch_group = {
+ .is_visible = usb_role_switch_is_visible,
+ .attrs = usb_role_switch_attrs,
+};
+
+static const struct attribute_group *usb_role_switch_groups[] = {
+ &usb_role_switch_group,
+ NULL,
+};
+
+static int
+usb_role_switch_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ int ret;
+
+ ret = add_uevent_var(env, "USB_ROLE_SWITCH=%s", dev_name(dev));
+ if (ret)
+ dev_err(dev, "failed to add uevent USB_ROLE_SWITCH\n");
+
+ return ret;
+}
+
+static void usb_role_switch_release(struct device *dev)
+{
+ struct usb_role_switch *sw = to_role_switch(dev);
+
+ kfree(sw);
+}
+
+static const struct device_type usb_role_dev_type = {
+ .name = "usb_role_switch",
+ .groups = usb_role_switch_groups,
+ .uevent = usb_role_switch_uevent,
+ .release = usb_role_switch_release,
+};
+
+/**
+ * usb_role_switch_register - Register USB Role Switch
+ * @parent: Parent device for the switch
+ * @desc: Description of the switch
+ *
+ * USB Role Switch is a device capable or choosing the role for USB connector.
+ * On platforms where the USB controller is dual-role capable, the controller
+ * driver will need to register the switch. On platforms where the USB host and
+ * USB device controllers behind the connector are separate, there will be a
+ * mux, and the driver for that mux will need to register the switch.
+ *
+ * Returns handle to a new role switch or ERR_PTR. The content of @desc is
+ * copied.
+ */
+struct usb_role_switch *
+usb_role_switch_register(struct device *parent,
+ const struct usb_role_switch_desc *desc)
+{
+ struct usb_role_switch *sw;
+ int ret;
+
+ if (!desc || !desc->set)
+ return ERR_PTR(-EINVAL);
+
+ sw = kzalloc(sizeof(*sw), GFP_KERNEL);
+ if (!sw)
+ return ERR_PTR(-ENOMEM);
+
+ mutex_init(&sw->lock);
+
+ sw->allow_userspace_control = desc->allow_userspace_control;
+ sw->usb2_port = desc->usb2_port;
+ sw->usb3_port = desc->usb3_port;
+ sw->udc = desc->udc;
+ sw->set = desc->set;
+ sw->get = desc->get;
+
+ sw->dev.parent = parent;
+ sw->dev.class = role_class;
+ sw->dev.type = &usb_role_dev_type;
+ dev_set_name(&sw->dev, "%s-role-switch", dev_name(parent));
+
+ ret = device_register(&sw->dev);
+ if (ret) {
+ put_device(&sw->dev);
+ return ERR_PTR(ret);
+ }
+
+ /* TODO: Symlinks for the host port and the device controller. */
+
+ return sw;
+}
+EXPORT_SYMBOL_GPL(usb_role_switch_register);
+
+/**
+ * usb_role_switch_unregister - Unregsiter USB Role Switch
+ * @sw: USB Role Switch
+ *
+ * Unregister switch that was registered with usb_role_switch_register().
+ */
+void usb_role_switch_unregister(struct usb_role_switch *sw)
+{
+ if (!IS_ERR_OR_NULL(sw))
+ device_unregister(&sw->dev);
+}
+EXPORT_SYMBOL_GPL(usb_role_switch_unregister);
+
+static int __init usb_roles_init(void)
+{
+ role_class = class_create(THIS_MODULE, "usb_role");
+ return PTR_ERR_OR_ZERO(role_class);
+}
+subsys_initcall(usb_roles_init);
+
+static void __exit usb_roles_exit(void)
+{
+ class_destroy(role_class);
+}
+module_exit(usb_roles_exit);
+
+MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("USB Role Class");
diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile
index 92c9cefb4317..18e874b0441e 100644
--- a/drivers/usb/core/Makefile
+++ b/drivers/usb/core/Makefile
@@ -6,7 +6,7 @@
usbcore-y := usb.o hub.o hcd.o urb.o message.o driver.o
usbcore-y += config.o file.o buffer.o sysfs.o endpoint.o
usbcore-y += devio.o notify.o generic.o quirks.o devices.o
-usbcore-y += port.o
+usbcore-y += phy.o port.o
usbcore-$(CONFIG_OF) += of.o
usbcore-$(CONFIG_USB_PCI) += hcd-pci.o
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index d526595bc959..76e16c5251b9 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -65,7 +65,6 @@ struct usb_dev_state {
const struct cred *cred;
void __user *disccontext;
unsigned long ifclaimed;
- u32 secid;
u32 disabled_bulk_eps;
bool privileges_dropped;
unsigned long interface_allowed_mask;
@@ -95,7 +94,6 @@ struct async {
struct usb_memory *usbm;
unsigned int mem_usage;
int status;
- u32 secid;
u8 bulk_addr;
u8 bulk_status;
};
@@ -586,7 +584,6 @@ static void async_completed(struct urb *urb)
struct usb_dev_state *ps = as->ps;
struct siginfo sinfo;
struct pid *pid = NULL;
- u32 secid = 0;
const struct cred *cred = NULL;
int signr;
@@ -602,7 +599,6 @@ static void async_completed(struct urb *urb)
sinfo.si_addr = as->userurb;
pid = get_pid(as->pid);
cred = get_cred(as->cred);
- secid = as->secid;
}
snoop(&urb->dev->dev, "urb complete\n");
snoop_urb(urb->dev, as->userurb, urb->pipe, urb->actual_length,
@@ -618,7 +614,7 @@ static void async_completed(struct urb *urb)
spin_unlock(&ps->lock);
if (signr) {
- kill_pid_info_as_cred(sinfo.si_signo, &sinfo, pid, cred, secid);
+ kill_pid_info_as_cred(sinfo.si_signo, &sinfo, pid, cred);
put_pid(pid);
put_cred(cred);
}
@@ -1013,7 +1009,6 @@ static int usbdev_open(struct inode *inode, struct file *file)
init_waitqueue_head(&ps->wait);
ps->disc_pid = get_pid(task_pid(current));
ps->cred = get_current_cred();
- security_task_getsecid(current, &ps->secid);
smp_wmb();
list_add_tail(&ps->list, &dev->filelist);
file->private_data = ps;
@@ -1727,7 +1722,6 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
as->ifnum = ifnum;
as->pid = get_pid(task_pid(current));
as->cred = get_current_cred();
- security_task_getsecid(current, &as->secid);
snoop_urb(ps->dev, as->userurb, as->urb->pipe,
as->urb->transfer_buffer_length, 0, SUBMIT,
NULL, 0);
@@ -2617,7 +2611,7 @@ static void usbdev_remove(struct usb_device *udev)
sinfo.si_code = SI_ASYNCIO;
sinfo.si_addr = ps->disccontext;
kill_pid_info_as_cred(ps->discsignr, &sinfo,
- ps->disc_pid, ps->cred, ps->secid);
+ ps->disc_pid, ps->cred);
}
}
}
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
index 83c14dda6300..bc8242bc4564 100644
--- a/drivers/usb/core/generic.c
+++ b/drivers/usb/core/generic.c
@@ -210,8 +210,13 @@ static int generic_suspend(struct usb_device *udev, pm_message_t msg)
if (!udev->parent)
rc = hcd_bus_suspend(udev, msg);
- /* Non-root devices don't need to do anything for FREEZE or PRETHAW */
- else if (msg.event == PM_EVENT_FREEZE || msg.event == PM_EVENT_PRETHAW)
+ /*
+ * Non-root USB2 devices don't need to do anything for FREEZE
+ * or PRETHAW. USB3 devices don't support global suspend and
+ * needs to be selectively suspended.
+ */
+ else if ((msg.event == PM_EVENT_FREEZE || msg.event == PM_EVENT_PRETHAW)
+ && (udev->speed < USB_SPEED_SUPER))
rc = 0;
else
rc = usb_port_suspend(udev, msg);
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index fc32391a34d5..0a42c5df3c0f 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -37,6 +37,7 @@
#include <linux/usb/otg.h>
#include "usb.h"
+#include "phy.h"
/*-------------------------------------------------------------------------*/
@@ -2260,6 +2261,10 @@ int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg)
usb_set_device_state(rhdev, USB_STATE_SUSPENDED);
hcd->state = HC_STATE_SUSPENDED;
+ if (!PMSG_IS_AUTO(msg))
+ usb_phy_roothub_suspend(hcd->self.sysdev,
+ hcd->phy_roothub);
+
/* Did we race with a root-hub wakeup event? */
if (rhdev->do_remote_wakeup) {
char buffer[6];
@@ -2296,6 +2301,14 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
dev_dbg(&rhdev->dev, "skipped %s of dead bus\n", "resume");
return 0;
}
+
+ if (!PMSG_IS_AUTO(msg)) {
+ status = usb_phy_roothub_resume(hcd->self.sysdev,
+ hcd->phy_roothub);
+ if (status)
+ return status;
+ }
+
if (!hcd->driver->bus_resume)
return -ENOENT;
if (HCD_RH_RUNNING(hcd))
@@ -2333,6 +2346,7 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
}
} else {
hcd->state = old_state;
+ usb_phy_roothub_suspend(hcd->self.sysdev, hcd->phy_roothub);
dev_dbg(&rhdev->dev, "bus %s fail, err %d\n",
"resume", status);
if (status != -ESHUTDOWN)
@@ -2365,6 +2379,7 @@ void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
spin_lock_irqsave (&hcd_root_hub_lock, flags);
if (hcd->rh_registered) {
+ pm_wakeup_event(&hcd->self.root_hub->dev, 0);
set_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags);
queue_work(pm_wq, &hcd->wakeup_work);
}
@@ -2727,7 +2742,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
int retval;
struct usb_device *rhdev;
- if (IS_ENABLED(CONFIG_USB_PHY) && !hcd->usb_phy) {
+ if (IS_ENABLED(CONFIG_USB_PHY) && !hcd->skip_phy_initialization) {
struct usb_phy *phy = usb_get_phy_dev(hcd->self.sysdev, 0);
if (IS_ERR(phy)) {
@@ -2745,28 +2760,20 @@ int usb_add_hcd(struct usb_hcd *hcd,
}
}
- if (IS_ENABLED(CONFIG_GENERIC_PHY) && !hcd->phy) {
- struct phy *phy = phy_get(hcd->self.sysdev, "usb");
-
- if (IS_ERR(phy)) {
- retval = PTR_ERR(phy);
- if (retval == -EPROBE_DEFER)
- goto err_phy;
- } else {
- retval = phy_init(phy);
- if (retval) {
- phy_put(phy);
- goto err_phy;
- }
- retval = phy_power_on(phy);
- if (retval) {
- phy_exit(phy);
- phy_put(phy);
- goto err_phy;
- }
- hcd->phy = phy;
- hcd->remove_phy = 1;
+ if (!hcd->skip_phy_initialization && usb_hcd_is_primary_hcd(hcd)) {
+ hcd->phy_roothub = usb_phy_roothub_alloc(hcd->self.sysdev);
+ if (IS_ERR(hcd->phy_roothub)) {
+ retval = PTR_ERR(hcd->phy_roothub);
+ goto err_phy_roothub_alloc;
}
+
+ retval = usb_phy_roothub_init(hcd->phy_roothub);
+ if (retval)
+ goto err_phy_roothub_alloc;
+
+ retval = usb_phy_roothub_power_on(hcd->phy_roothub);
+ if (retval)
+ goto err_usb_phy_roothub_power_on;
}
dev_info(hcd->self.controller, "%s\n", hcd->product_desc);
@@ -2933,13 +2940,10 @@ err_allocate_root_hub:
err_register_bus:
hcd_buffer_destroy(hcd);
err_create_buf:
- if (IS_ENABLED(CONFIG_GENERIC_PHY) && hcd->remove_phy && hcd->phy) {
- phy_power_off(hcd->phy);
- phy_exit(hcd->phy);
- phy_put(hcd->phy);
- hcd->phy = NULL;
- }
-err_phy:
+ usb_phy_roothub_power_off(hcd->phy_roothub);
+err_usb_phy_roothub_power_on:
+ usb_phy_roothub_exit(hcd->phy_roothub);
+err_phy_roothub_alloc:
if (hcd->remove_phy && hcd->usb_phy) {
usb_phy_shutdown(hcd->usb_phy);
usb_put_phy(hcd->usb_phy);
@@ -3017,12 +3021,9 @@ void usb_remove_hcd(struct usb_hcd *hcd)
usb_deregister_bus(&hcd->self);
hcd_buffer_destroy(hcd);
- if (IS_ENABLED(CONFIG_GENERIC_PHY) && hcd->remove_phy && hcd->phy) {
- phy_power_off(hcd->phy);
- phy_exit(hcd->phy);
- phy_put(hcd->phy);
- hcd->phy = NULL;
- }
+ usb_phy_roothub_power_off(hcd->phy_roothub);
+ usb_phy_roothub_exit(hcd->phy_roothub);
+
if (hcd->remove_phy && hcd->usb_phy) {
usb_phy_shutdown(hcd->usb_phy);
usb_put_phy(hcd->usb_phy);
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index c5c1f6cf3228..aa9968d90a48 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -653,12 +653,17 @@ void usb_wakeup_notification(struct usb_device *hdev,
unsigned int portnum)
{
struct usb_hub *hub;
+ struct usb_port *port_dev;
if (!hdev)
return;
hub = usb_hub_to_struct_hub(hdev);
if (hub) {
+ port_dev = hub->ports[portnum - 1];
+ if (port_dev && port_dev->child)
+ pm_wakeup_event(&port_dev->child->dev, 0);
+
set_bit(portnum, hub->wakeup_bits);
kick_hub_wq(hub);
}
@@ -2192,9 +2197,13 @@ static void show_string(struct usb_device *udev, char *id, char *string)
static void announce_device(struct usb_device *udev)
{
- dev_info(&udev->dev, "New USB device found, idVendor=%04x, idProduct=%04x\n",
+ u16 bcdDevice = le16_to_cpu(udev->descriptor.bcdDevice);
+
+ dev_info(&udev->dev,
+ "New USB device found, idVendor=%04x, idProduct=%04x, bcdDevice=%2x.%02x\n",
le16_to_cpu(udev->descriptor.idVendor),
- le16_to_cpu(udev->descriptor.idProduct));
+ le16_to_cpu(udev->descriptor.idProduct),
+ bcdDevice >> 8, bcdDevice & 0xff);
dev_info(&udev->dev,
"New USB device strings: Mfr=%d, Product=%d, SerialNumber=%d\n",
udev->descriptor.iManufacturer,
@@ -3430,8 +3439,11 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
/* Skip the initial Clear-Suspend step for a remote wakeup */
status = hub_port_status(hub, port1, &portstatus, &portchange);
- if (status == 0 && !port_is_suspended(hub, portstatus))
+ if (status == 0 && !port_is_suspended(hub, portstatus)) {
+ if (portchange & USB_PORT_STAT_C_SUSPEND)
+ pm_wakeup_event(&udev->dev, 0);
goto SuspendCleared;
+ }
/* see 7.1.7.7; affects power usage, but not budgeting */
if (hub_is_superspeed(hub->hdev))
@@ -3655,7 +3667,7 @@ static int hub_reset_resume(struct usb_interface *intf)
*/
void usb_root_hub_lost_power(struct usb_device *rhdev)
{
- dev_warn(&rhdev->dev, "root hub lost power or was reset\n");
+ dev_notice(&rhdev->dev, "root hub lost power or was reset\n");
rhdev->reset_resume = 1;
}
EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);
@@ -5104,8 +5116,10 @@ static void port_event(struct usb_hub *hub, int port1)
if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
u16 status = 0, unused;
+ port_dev->over_current_count++;
- dev_dbg(&port_dev->dev, "over-current change\n");
+ dev_dbg(&port_dev->dev, "over-current change #%u\n",
+ port_dev->over_current_count);
usb_clear_port_feature(hdev, port1,
USB_PORT_FEAT_C_OVER_CURRENT);
msleep(100); /* Cool down */
@@ -5505,21 +5519,15 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
if (udev->usb2_hw_lpm_enabled == 1)
usb_set_usb2_hardware_lpm(udev, 0);
- /* Disable LPM and LTM while we reset the device and reinstall the alt
- * settings. Device-initiated LPM settings, and system exit latency
- * settings are cleared when the device is reset, so we have to set
- * them up again.
+ /* Disable LPM while we reset the device and reinstall the alt settings.
+ * Device-initiated LPM, and system exit latency settings are cleared
+ * when the device is reset, so we have to set them up again.
*/
ret = usb_unlocked_disable_lpm(udev);
if (ret) {
dev_err(&udev->dev, "%s Failed to disable LPM\n", __func__);
goto re_enumerate_no_bos;
}
- ret = usb_disable_ltm(udev);
- if (ret) {
- dev_err(&udev->dev, "%s Failed to disable LTM\n", __func__);
- goto re_enumerate_no_bos;
- }
bos = udev->bos;
udev->bos = NULL;
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index 2a700ccc868c..4dc769ee9c74 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -96,6 +96,7 @@ struct usb_port {
enum usb_port_connect_type connect_type;
usb_port_location_t location;
struct mutex status_lock;
+ u32 over_current_count;
u8 portnum;
unsigned int is_superspeed:1;
unsigned int usb3_lpm_u1_permit:1;
diff --git a/drivers/usb/core/phy.c b/drivers/usb/core/phy.c
new file mode 100644
index 000000000000..9879767452a2
--- /dev/null
+++ b/drivers/usb/core/phy.c
@@ -0,0 +1,197 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * A wrapper for multiple PHYs which passes all phy_* function calls to
+ * multiple (actual) PHY devices. This is comes handy when initializing
+ * all PHYs on a HCD and to keep them all in the same state.
+ *
+ * Copyright (C) 2018 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ */
+
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/phy/phy.h>
+#include <linux/of.h>
+
+#include "phy.h"
+
+struct usb_phy_roothub {
+ struct phy *phy;
+ struct list_head list;
+};
+
+static int usb_phy_roothub_add_phy(struct device *dev, int index,
+ struct list_head *list)
+{
+ struct usb_phy_roothub *roothub_entry;
+ struct phy *phy = devm_of_phy_get_by_index(dev, dev->of_node, index);
+
+ if (IS_ERR_OR_NULL(phy)) {
+ if (!phy || PTR_ERR(phy) == -ENODEV)
+ return 0;
+ else
+ return PTR_ERR(phy);
+ }
+
+ roothub_entry = devm_kzalloc(dev, sizeof(*roothub_entry), GFP_KERNEL);
+ if (!roothub_entry)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&roothub_entry->list);
+
+ roothub_entry->phy = phy;
+
+ list_add_tail(&roothub_entry->list, list);
+
+ return 0;
+}
+
+struct usb_phy_roothub *usb_phy_roothub_alloc(struct device *dev)
+{
+ struct usb_phy_roothub *phy_roothub;
+ int i, num_phys, err;
+
+ if (!IS_ENABLED(CONFIG_GENERIC_PHY))
+ return NULL;
+
+ num_phys = of_count_phandle_with_args(dev->of_node, "phys",
+ "#phy-cells");
+ if (num_phys <= 0)
+ return NULL;
+
+ phy_roothub = devm_kzalloc(dev, sizeof(*phy_roothub), GFP_KERNEL);
+ if (!phy_roothub)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&phy_roothub->list);
+
+ for (i = 0; i < num_phys; i++) {
+ err = usb_phy_roothub_add_phy(dev, i, &phy_roothub->list);
+ if (err)
+ return ERR_PTR(err);
+ }
+
+ return phy_roothub;
+}
+EXPORT_SYMBOL_GPL(usb_phy_roothub_alloc);
+
+int usb_phy_roothub_init(struct usb_phy_roothub *phy_roothub)
+{
+ struct usb_phy_roothub *roothub_entry;
+ struct list_head *head;
+ int err;
+
+ if (!phy_roothub)
+ return 0;
+
+ head = &phy_roothub->list;
+
+ list_for_each_entry(roothub_entry, head, list) {
+ err = phy_init(roothub_entry->phy);
+ if (err)
+ goto err_exit_phys;
+ }
+
+ return 0;
+
+err_exit_phys:
+ list_for_each_entry_continue_reverse(roothub_entry, head, list)
+ phy_exit(roothub_entry->phy);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(usb_phy_roothub_init);
+
+int usb_phy_roothub_exit(struct usb_phy_roothub *phy_roothub)
+{
+ struct usb_phy_roothub *roothub_entry;
+ struct list_head *head;
+ int err, ret = 0;
+
+ if (!phy_roothub)
+ return 0;
+
+ head = &phy_roothub->list;
+
+ list_for_each_entry(roothub_entry, head, list) {
+ err = phy_exit(roothub_entry->phy);
+ if (err)
+ ret = err;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_phy_roothub_exit);
+
+int usb_phy_roothub_power_on(struct usb_phy_roothub *phy_roothub)
+{
+ struct usb_phy_roothub *roothub_entry;
+ struct list_head *head;
+ int err;
+
+ if (!phy_roothub)
+ return 0;
+
+ head = &phy_roothub->list;
+
+ list_for_each_entry(roothub_entry, head, list) {
+ err = phy_power_on(roothub_entry->phy);
+ if (err)
+ goto err_out;
+ }
+
+ return 0;
+
+err_out:
+ list_for_each_entry_continue_reverse(roothub_entry, head, list)
+ phy_power_off(roothub_entry->phy);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(usb_phy_roothub_power_on);
+
+void usb_phy_roothub_power_off(struct usb_phy_roothub *phy_roothub)
+{
+ struct usb_phy_roothub *roothub_entry;
+
+ if (!phy_roothub)
+ return;
+
+ list_for_each_entry_reverse(roothub_entry, &phy_roothub->list, list)
+ phy_power_off(roothub_entry->phy);
+}
+EXPORT_SYMBOL_GPL(usb_phy_roothub_power_off);
+
+int usb_phy_roothub_suspend(struct device *controller_dev,
+ struct usb_phy_roothub *phy_roothub)
+{
+ usb_phy_roothub_power_off(phy_roothub);
+
+ /* keep the PHYs initialized so the device can wake up the system */
+ if (device_may_wakeup(controller_dev))
+ return 0;
+
+ return usb_phy_roothub_exit(phy_roothub);
+}
+EXPORT_SYMBOL_GPL(usb_phy_roothub_suspend);
+
+int usb_phy_roothub_resume(struct device *controller_dev,
+ struct usb_phy_roothub *phy_roothub)
+{
+ int err;
+
+ /* if the device can't wake up the system _exit was called */
+ if (!device_may_wakeup(controller_dev)) {
+ err = usb_phy_roothub_init(phy_roothub);
+ if (err)
+ return err;
+ }
+
+ err = usb_phy_roothub_power_on(phy_roothub);
+
+ /* undo _init if _power_on failed */
+ if (err && !device_may_wakeup(controller_dev))
+ usb_phy_roothub_exit(phy_roothub);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(usb_phy_roothub_resume);
diff --git a/drivers/usb/core/phy.h b/drivers/usb/core/phy.h
new file mode 100644
index 000000000000..88a3c037e9df
--- /dev/null
+++ b/drivers/usb/core/phy.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * USB roothub wrapper
+ *
+ * Copyright (C) 2018 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ */
+
+#ifndef __USB_CORE_PHY_H_
+#define __USB_CORE_PHY_H_
+
+struct device;
+struct usb_phy_roothub;
+
+struct usb_phy_roothub *usb_phy_roothub_alloc(struct device *dev);
+
+int usb_phy_roothub_init(struct usb_phy_roothub *phy_roothub);
+int usb_phy_roothub_exit(struct usb_phy_roothub *phy_roothub);
+
+int usb_phy_roothub_power_on(struct usb_phy_roothub *phy_roothub);
+void usb_phy_roothub_power_off(struct usb_phy_roothub *phy_roothub);
+
+int usb_phy_roothub_suspend(struct device *controller_dev,
+ struct usb_phy_roothub *phy_roothub);
+int usb_phy_roothub_resume(struct device *controller_dev,
+ struct usb_phy_roothub *phy_roothub);
+
+#endif /* __USB_CORE_PHY_H_ */
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index 1a01e9ad3804..6979bde87d31 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -41,6 +41,15 @@ static ssize_t connect_type_show(struct device *dev,
}
static DEVICE_ATTR_RO(connect_type);
+static ssize_t over_current_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_port *port_dev = to_usb_port(dev);
+
+ return sprintf(buf, "%u\n", port_dev->over_current_count);
+}
+static DEVICE_ATTR_RO(over_current_count);
+
static ssize_t usb3_lpm_permit_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -109,6 +118,7 @@ static DEVICE_ATTR_RW(usb3_lpm_permit);
static struct attribute *port_dev_attrs[] = {
&dev_attr_connect_type.attr,
+ &dev_attr_over_current_count.attr,
NULL,
};
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 54b019e267c5..c55def2f1320 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -6,11 +6,157 @@
* Copyright (c) 2007 Greg Kroah-Hartman <gregkh@suse.de>
*/
+#include <linux/moduleparam.h>
#include <linux/usb.h>
#include <linux/usb/quirks.h>
#include <linux/usb/hcd.h>
#include "usb.h"
+struct quirk_entry {
+ u16 vid;
+ u16 pid;
+ u32 flags;
+};
+
+static DEFINE_MUTEX(quirk_mutex);
+
+static struct quirk_entry *quirk_list;
+static unsigned int quirk_count;
+
+static char quirks_param[128];
+
+static int quirks_param_set(const char *val, const struct kernel_param *kp)
+{
+ char *p, *field;
+ u16 vid, pid;
+ u32 flags;
+ size_t i;
+ int err;
+
+ err = param_set_copystring(val, kp);
+ if (err)
+ return err;
+
+ mutex_lock(&quirk_mutex);
+
+ if (!*val) {
+ quirk_count = 0;
+ kfree(quirk_list);
+ quirk_list = NULL;
+ goto unlock;
+ }
+
+ for (quirk_count = 1, i = 0; val[i]; i++)
+ if (val[i] == ',')
+ quirk_count++;
+
+ if (quirk_list) {
+ kfree(quirk_list);
+ quirk_list = NULL;
+ }
+
+ quirk_list = kcalloc(quirk_count, sizeof(struct quirk_entry),
+ GFP_KERNEL);
+ if (!quirk_list) {
+ mutex_unlock(&quirk_mutex);
+ return -ENOMEM;
+ }
+
+ for (i = 0, p = (char *)val; p && *p;) {
+ /* Each entry consists of VID:PID:flags */
+ field = strsep(&p, ":");
+ if (!field)
+ break;
+
+ if (kstrtou16(field, 16, &vid))
+ break;
+
+ field = strsep(&p, ":");
+ if (!field)
+ break;
+
+ if (kstrtou16(field, 16, &pid))
+ break;
+
+ field = strsep(&p, ",");
+ if (!field || !*field)
+ break;
+
+ /* Collect the flags */
+ for (flags = 0; *field; field++) {
+ switch (*field) {
+ case 'a':
+ flags |= USB_QUIRK_STRING_FETCH_255;
+ break;
+ case 'b':
+ flags |= USB_QUIRK_RESET_RESUME;
+ break;
+ case 'c':
+ flags |= USB_QUIRK_NO_SET_INTF;
+ break;
+ case 'd':
+ flags |= USB_QUIRK_CONFIG_INTF_STRINGS;
+ break;
+ case 'e':
+ flags |= USB_QUIRK_RESET;
+ break;
+ case 'f':
+ flags |= USB_QUIRK_HONOR_BNUMINTERFACES;
+ break;
+ case 'g':
+ flags |= USB_QUIRK_DELAY_INIT;
+ break;
+ case 'h':
+ flags |= USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL;
+ break;
+ case 'i':
+ flags |= USB_QUIRK_DEVICE_QUALIFIER;
+ break;
+ case 'j':
+ flags |= USB_QUIRK_IGNORE_REMOTE_WAKEUP;
+ break;
+ case 'k':
+ flags |= USB_QUIRK_NO_LPM;
+ break;
+ case 'l':
+ flags |= USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL;
+ break;
+ case 'm':
+ flags |= USB_QUIRK_DISCONNECT_SUSPEND;
+ break;
+ case 'n':
+ flags |= USB_QUIRK_DELAY_CTRL_MSG;
+ break;
+ /* Ignore unrecognized flag characters */
+ }
+ }
+
+ quirk_list[i++] = (struct quirk_entry)
+ { .vid = vid, .pid = pid, .flags = flags };
+ }
+
+ if (i < quirk_count)
+ quirk_count = i;
+
+unlock:
+ mutex_unlock(&quirk_mutex);
+
+ return 0;
+}
+
+static const struct kernel_param_ops quirks_param_ops = {
+ .set = quirks_param_set,
+ .get = param_get_string,
+};
+
+static struct kparam_string quirks_param_string = {
+ .maxlen = sizeof(quirks_param),
+ .string = quirks_param,
+};
+
+module_param_cb(quirks, &quirks_param_ops, &quirks_param_string, 0644);
+MODULE_PARM_DESC(quirks, "Add/modify USB quirks by specifying quirks=vendorID:productID:quirks");
+
/* Lists of quirky USB devices, split in device quirks and interface quirks.
* Device quirks are applied at the very beginning of the enumeration process,
* right after reading the device descriptor. They can thus only match on device
@@ -40,6 +186,9 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x03f0, 0x0701), .driver_info =
USB_QUIRK_STRING_FETCH_255 },
+ /* HP v222w 16GB Mini USB Drive */
+ { USB_DEVICE(0x03f0, 0x3f40), .driver_info = USB_QUIRK_DELAY_INIT },
+
/* Creative SB Audigy 2 NX */
{ USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME },
@@ -321,8 +470,8 @@ static int usb_amd_resume_quirk(struct usb_device *udev)
return 0;
}
-static u32 __usb_detect_quirks(struct usb_device *udev,
- const struct usb_device_id *id)
+static u32 usb_detect_static_quirks(struct usb_device *udev,
+ const struct usb_device_id *id)
{
u32 quirks = 0;
@@ -340,21 +489,43 @@ static u32 __usb_detect_quirks(struct usb_device *udev,
return quirks;
}
+static u32 usb_detect_dynamic_quirks(struct usb_device *udev)
+{
+ u16 vid = le16_to_cpu(udev->descriptor.idVendor);
+ u16 pid = le16_to_cpu(udev->descriptor.idProduct);
+ int i, flags = 0;
+
+ mutex_lock(&quirk_mutex);
+
+ for (i = 0; i < quirk_count; i++) {
+ if (vid == quirk_list[i].vid && pid == quirk_list[i].pid) {
+ flags = quirk_list[i].flags;
+ break;
+ }
+ }
+
+ mutex_unlock(&quirk_mutex);
+
+ return flags;
+}
+
/*
* Detect any quirks the device has, and do any housekeeping for it if needed.
*/
void usb_detect_quirks(struct usb_device *udev)
{
- udev->quirks = __usb_detect_quirks(udev, usb_quirk_list);
+ udev->quirks = usb_detect_static_quirks(udev, usb_quirk_list);
/*
* Pixart-based mice would trigger remote wakeup issue on AMD
* Yangtze chipset, so set them as RESET_RESUME flag.
*/
if (usb_amd_resume_quirk(udev))
- udev->quirks |= __usb_detect_quirks(udev,
+ udev->quirks |= usb_detect_static_quirks(udev,
usb_amd_resume_quirk_list);
+ udev->quirks ^= usb_detect_dynamic_quirks(udev);
+
if (udev->quirks)
dev_dbg(&udev->dev, "USB quirks for this device: %x\n",
udev->quirks);
@@ -373,7 +544,7 @@ void usb_detect_interface_quirks(struct usb_device *udev)
{
u32 quirks;
- quirks = __usb_detect_quirks(udev, usb_interface_quirk_list);
+ quirks = usb_detect_static_quirks(udev, usb_interface_quirk_list);
if (quirks == 0)
return;
@@ -381,3 +552,11 @@ void usb_detect_interface_quirks(struct usb_device *udev)
quirks);
udev->quirks |= quirks;
}
+
+void usb_release_quirk_list(void)
+{
+ mutex_lock(&quirk_mutex);
+ kfree(quirk_list);
+ quirk_list = NULL;
+ mutex_unlock(&quirk_mutex);
+}
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index 796c9b149728..f51750bcd152 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -433,6 +433,14 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
max *= mult;
}
+ if (dev->speed == USB_SPEED_SUPER_PLUS &&
+ USB_SS_SSP_ISOC_COMP(ep->ss_ep_comp.bmAttributes)) {
+ struct usb_ssp_isoc_ep_comp_descriptor *isoc_ep_comp;
+
+ isoc_ep_comp = &ep->ssp_isoc_ep_comp;
+ max = le32_to_cpu(isoc_ep_comp->dwBytesPerInterval);
+ }
+
/* "high bandwidth" mode, 1-3 packets/uframe? */
if (dev->speed == USB_SPEED_HIGH)
max *= usb_endpoint_maxp_mult(&ep->desc);
diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c
index 84da17460568..e221861b3187 100644
--- a/drivers/usb/core/usb-acpi.c
+++ b/drivers/usb/core/usb-acpi.c
@@ -90,8 +90,8 @@ static enum usb_port_connect_type usb_acpi_get_connect_type(acpi_handle handle,
acpi_status status;
/*
- * According to ACPI Spec 9.13. PLD indicates whether usb port is
- * user visible and _UPC indicates whether it is connectable. If
+ * According to 9.14 in ACPI Spec 6.2. _PLD indicates whether usb port
+ * is user visible and _UPC indicates whether it is connectable. If
* the port was visible and connectable, it could be freely connected
* and disconnected with USB devices. If no visible and connectable,
* a usb device is directly hard-wired to the port. If no visible and
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 2f5fbc56a9dd..0adb6345ff2e 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -1259,6 +1259,7 @@ static void __exit usb_exit(void)
if (usb_disabled())
return;
+ usb_release_quirk_list();
usb_deregister_device_driver(&usb_generic_driver);
usb_major_cleanup();
usb_deregister(&usbfs_driver);
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 149cc7480971..546a2219454b 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -36,6 +36,7 @@ extern void usb_deauthorize_interface(struct usb_interface *);
extern void usb_authorize_interface(struct usb_interface *);
extern void usb_detect_quirks(struct usb_device *udev);
extern void usb_detect_interface_quirks(struct usb_device *udev);
+extern void usb_release_quirk_list(void);
extern int usb_remove_device(struct usb_device *udev);
extern int usb_get_device_descriptor(struct usb_device *dev,
diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index 82a7d98c3436..18a0a1771289 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -64,10 +64,11 @@
*
* @hsotg: Programming view of the DWC_otg controller
*/
-static int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
+int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
{
struct dwc2_gregs_backup *gr;
- int i;
+
+ dev_dbg(hsotg->dev, "%s\n", __func__);
/* Backup global regs */
gr = &hsotg->gr_backup;
@@ -78,10 +79,11 @@ static int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
gr->gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
gr->grxfsiz = dwc2_readl(hsotg->regs + GRXFSIZ);
gr->gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ);
- gr->hptxfsiz = dwc2_readl(hsotg->regs + HPTXFSIZ);
gr->gdfifocfg = dwc2_readl(hsotg->regs + GDFIFOCFG);
- for (i = 0; i < MAX_EPS_CHANNELS; i++)
- gr->dtxfsiz[i] = dwc2_readl(hsotg->regs + DPTXFSIZN(i));
+ gr->pcgcctl1 = dwc2_readl(hsotg->regs + PCGCCTL1);
+ gr->glpmcfg = dwc2_readl(hsotg->regs + GLPMCFG);
+ gr->gi2cctl = dwc2_readl(hsotg->regs + GI2CCTL);
+ gr->pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
gr->valid = true;
return 0;
@@ -94,10 +96,9 @@ static int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
*
* @hsotg: Programming view of the DWC_otg controller
*/
-static int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg)
+int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg)
{
struct dwc2_gregs_backup *gr;
- int i;
dev_dbg(hsotg->dev, "%s\n", __func__);
@@ -117,26 +118,27 @@ static int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg)
dwc2_writel(gr->gahbcfg, hsotg->regs + GAHBCFG);
dwc2_writel(gr->grxfsiz, hsotg->regs + GRXFSIZ);
dwc2_writel(gr->gnptxfsiz, hsotg->regs + GNPTXFSIZ);
- dwc2_writel(gr->hptxfsiz, hsotg->regs + HPTXFSIZ);
dwc2_writel(gr->gdfifocfg, hsotg->regs + GDFIFOCFG);
- for (i = 0; i < MAX_EPS_CHANNELS; i++)
- dwc2_writel(gr->dtxfsiz[i], hsotg->regs + DPTXFSIZN(i));
+ dwc2_writel(gr->pcgcctl1, hsotg->regs + PCGCCTL1);
+ dwc2_writel(gr->glpmcfg, hsotg->regs + GLPMCFG);
+ dwc2_writel(gr->pcgcctl, hsotg->regs + PCGCTL);
+ dwc2_writel(gr->gi2cctl, hsotg->regs + GI2CCTL);
return 0;
}
/**
- * dwc2_exit_hibernation() - Exit controller from Partial Power Down.
+ * dwc2_exit_partial_power_down() - Exit controller from Partial Power Down.
*
* @hsotg: Programming view of the DWC_otg controller
* @restore: Controller registers need to be restored
*/
-int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore)
+int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, bool restore)
{
u32 pcgcctl;
int ret = 0;
- if (!hsotg->params.hibernation)
+ if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_PARTIAL)
return -ENOTSUPP;
pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
@@ -167,7 +169,7 @@ int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore)
return ret;
}
} else {
- ret = dwc2_restore_device_registers(hsotg);
+ ret = dwc2_restore_device_registers(hsotg, 0);
if (ret) {
dev_err(hsotg->dev, "%s: failed to restore device registers\n",
__func__);
@@ -180,16 +182,16 @@ int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore)
}
/**
- * dwc2_enter_hibernation() - Put controller in Partial Power Down.
+ * dwc2_enter_partial_power_down() - Put controller in Partial Power Down.
*
* @hsotg: Programming view of the DWC_otg controller
*/
-int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg)
+int dwc2_enter_partial_power_down(struct dwc2_hsotg *hsotg)
{
u32 pcgcctl;
int ret = 0;
- if (!hsotg->params.hibernation)
+ if (!hsotg->params.power_down)
return -ENOTSUPP;
/* Backup all registers */
@@ -218,7 +220,7 @@ int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg)
/*
* Clear any pending interrupts since dwc2 will not be able to
- * clear them after entering hibernation.
+ * clear them after entering partial_power_down.
*/
dwc2_writel(0xffffffff, hsotg->regs + GINTSTS);
@@ -240,6 +242,142 @@ int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg)
}
/**
+ * dwc2_restore_essential_regs() - Restore essiential regs of core.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ * @rmode: Restore mode, enabled in case of remote-wakeup.
+ * @is_host: Host or device mode.
+ */
+static void dwc2_restore_essential_regs(struct dwc2_hsotg *hsotg, int rmode,
+ int is_host)
+{
+ u32 pcgcctl;
+ struct dwc2_gregs_backup *gr;
+ struct dwc2_dregs_backup *dr;
+ struct dwc2_hregs_backup *hr;
+
+ gr = &hsotg->gr_backup;
+ dr = &hsotg->dr_backup;
+ hr = &hsotg->hr_backup;
+
+ dev_dbg(hsotg->dev, "%s: restoring essential regs\n", __func__);
+
+ /* Load restore values for [31:14] bits */
+ pcgcctl = (gr->pcgcctl & 0xffffc000);
+ /* If High Speed */
+ if (is_host) {
+ if (!(pcgcctl & PCGCTL_P2HD_PRT_SPD_MASK))
+ pcgcctl |= BIT(17);
+ } else {
+ if (!(pcgcctl & PCGCTL_P2HD_DEV_ENUM_SPD_MASK))
+ pcgcctl |= BIT(17);
+ }
+ dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+
+ /* Umnask global Interrupt in GAHBCFG and restore it */
+ dwc2_writel(gr->gahbcfg | GAHBCFG_GLBL_INTR_EN, hsotg->regs + GAHBCFG);
+
+ /* Clear all pending interupts */
+ dwc2_writel(0xffffffff, hsotg->regs + GINTSTS);
+
+ /* Unmask restore done interrupt */
+ dwc2_writel(GINTSTS_RESTOREDONE, hsotg->regs + GINTMSK);
+
+ /* Restore GUSBCFG and HCFG/DCFG */
+ dwc2_writel(gr->gusbcfg, hsotg->regs + GUSBCFG);
+
+ if (is_host) {
+ dwc2_writel(hr->hcfg, hsotg->regs + HCFG);
+ if (rmode)
+ pcgcctl |= PCGCTL_RESTOREMODE;
+ dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ udelay(10);
+
+ pcgcctl |= PCGCTL_ESS_REG_RESTORED;
+ dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ udelay(10);
+ } else {
+ dwc2_writel(dr->dcfg, hsotg->regs + DCFG);
+ if (!rmode)
+ pcgcctl |= PCGCTL_RESTOREMODE | PCGCTL_RSTPDWNMODULE;
+ dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ udelay(10);
+
+ pcgcctl |= PCGCTL_ESS_REG_RESTORED;
+ dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ udelay(10);
+ }
+}
+
+/**
+ * dwc2_hib_restore_common() - Common part of restore routine.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ * @rem_wakeup: Remote-wakeup, enabled in case of remote-wakeup.
+ * @is_host: Host or device mode.
+ */
+void dwc2_hib_restore_common(struct dwc2_hsotg *hsotg, int rem_wakeup,
+ int is_host)
+{
+ u32 gpwrdn;
+
+ /* Switch-on voltage to the core */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn &= ~GPWRDN_PWRDNSWTCH;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Reset core */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn &= ~GPWRDN_PWRDNRSTN;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Enable restore from PMU */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn |= GPWRDN_RESTORE;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Disable Power Down Clamp */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn &= ~GPWRDN_PWRDNCLMP;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(50);
+
+ if (!is_host && rem_wakeup)
+ udelay(70);
+
+ /* Deassert reset core */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn |= GPWRDN_PWRDNRSTN;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Disable PMU interrupt */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn &= ~GPWRDN_PMUINTSEL;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Set Restore Essential Regs bit in PCGCCTL register */
+ dwc2_restore_essential_regs(hsotg, rem_wakeup, is_host);
+
+ /*
+ * Wait For Restore_done Interrupt. This mechanism of polling the
+ * interrupt is introduced to avoid any possible race conditions
+ */
+ if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS, GINTSTS_RESTOREDONE,
+ 20000)) {
+ dev_dbg(hsotg->dev,
+ "%s: Restore Done wan't generated here\n",
+ __func__);
+ } else {
+ dev_dbg(hsotg->dev, "restore done generated here\n");
+ }
+}
+
+/**
* dwc2_wait_for_mode() - Waits for the controller mode.
* @hsotg: Programming view of the DWC_otg controller.
* @host_mode: If true, waits for host mode, otherwise device mode.
@@ -311,13 +449,50 @@ static bool dwc2_iddig_filter_enabled(struct dwc2_hsotg *hsotg)
}
/*
+ * dwc2_enter_hibernation() - Common function to enter hibernation.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ * @is_host: True if core is in host mode.
+ *
+ * Return: 0 if successful, negative error code otherwise
+ */
+int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg, int is_host)
+{
+ if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_HIBERNATION)
+ return -ENOTSUPP;
+
+ if (is_host)
+ return dwc2_host_enter_hibernation(hsotg);
+ else
+ return dwc2_gadget_enter_hibernation(hsotg);
+}
+
+/*
+ * dwc2_exit_hibernation() - Common function to exit from hibernation.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ * @rem_wakeup: Remote-wakeup, enabled in case of remote-wakeup.
+ * @reset: Enabled in case of restore with reset.
+ * @is_host: True if core is in host mode.
+ *
+ * Return: 0 if successful, negative error code otherwise
+ */
+int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup,
+ int reset, int is_host)
+{
+ if (is_host)
+ return dwc2_host_exit_hibernation(hsotg, rem_wakeup, reset);
+ else
+ return dwc2_gadget_exit_hibernation(hsotg, rem_wakeup, reset);
+}
+
+/*
* Do core a soft reset of the core. Be careful with this because it
* resets all the internal state machines of the core.
*/
int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait)
{
u32 greset;
- int count = 0;
bool wait_for_host_mode = false;
dev_vdbg(hsotg->dev, "%s()\n", __func__);
@@ -346,29 +521,19 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait)
greset = dwc2_readl(hsotg->regs + GRSTCTL);
greset |= GRSTCTL_CSFTRST;
dwc2_writel(greset, hsotg->regs + GRSTCTL);
- do {
- udelay(1);
- greset = dwc2_readl(hsotg->regs + GRSTCTL);
- if (++count > 50) {
- dev_warn(hsotg->dev,
- "%s() HANG! Soft Reset GRSTCTL=%0x\n",
- __func__, greset);
- return -EBUSY;
- }
- } while (greset & GRSTCTL_CSFTRST);
+
+ if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_CSFTRST, 50)) {
+ dev_warn(hsotg->dev, "%s: HANG! Soft Reset timeout GRSTCTL GRSTCTL_CSFTRST\n",
+ __func__);
+ return -EBUSY;
+ }
/* Wait for AHB master IDLE state */
- count = 0;
- do {
- udelay(1);
- greset = dwc2_readl(hsotg->regs + GRSTCTL);
- if (++count > 50) {
- dev_warn(hsotg->dev,
- "%s() HANG! AHB Idle GRSTCTL=%0x\n",
- __func__, greset);
- return -EBUSY;
- }
- } while (!(greset & GRSTCTL_AHBIDLE));
+ if (dwc2_hsotg_wait_bit_set(hsotg, GRSTCTL, GRSTCTL_AHBIDLE, 50)) {
+ dev_warn(hsotg->dev, "%s: HANG! AHB Idle timeout GRSTCTL GRSTCTL_AHBIDLE\n",
+ __func__);
+ return -EBUSY;
+ }
if (wait_for_host_mode && !skip_wait)
dwc2_wait_for_mode(hsotg, true);
@@ -376,14 +541,14 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait)
return 0;
}
-/*
- * Force the mode of the controller.
+/**
+ * dwc2_force_mode() - Force the mode of the controller.
*
* Forcing the mode is needed for two cases:
*
* 1) If the dr_mode is set to either HOST or PERIPHERAL we force the
* controller to stay in a particular mode regardless of ID pin
- * changes. We do this usually after a core reset.
+ * changes. We do this once during probe.
*
* 2) During probe we want to read reset values of the hw
* configuration registers that are only available in either host or
@@ -400,7 +565,7 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait)
* the filter is configured and enabled. We poll the current mode of
* the controller to account for this delay.
*/
-static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
+void dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
{
u32 gusbcfg;
u32 set;
@@ -412,17 +577,17 @@ static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
* Force mode has no effect if the hardware is not OTG.
*/
if (!dwc2_hw_is_otg(hsotg))
- return false;
+ return;
/*
* If dr_mode is either peripheral or host only, there is no
* need to ever force the mode to the opposite mode.
*/
if (WARN_ON(host && hsotg->dr_mode == USB_DR_MODE_PERIPHERAL))
- return false;
+ return;
if (WARN_ON(!host && hsotg->dr_mode == USB_DR_MODE_HOST))
- return false;
+ return;
gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
@@ -434,7 +599,7 @@ static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
dwc2_wait_for_mode(hsotg, host);
- return true;
+ return;
}
/**
@@ -446,10 +611,15 @@ static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
* the force mode. We only need to call this once during probe if
* dr_mode == OTG.
*/
-void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg)
+static void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg)
{
u32 gusbcfg;
+ if (!dwc2_hw_is_otg(hsotg))
+ return;
+
+ dev_dbg(hsotg->dev, "Clearing force mode bits\n");
+
gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
@@ -464,16 +634,13 @@ void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg)
*/
void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg)
{
- bool ret;
-
switch (hsotg->dr_mode) {
case USB_DR_MODE_HOST:
- ret = dwc2_force_mode(hsotg, true);
/*
* NOTE: This is required for some rockchip soc based
* platforms on their host-only dwc2.
*/
- if (!ret)
+ if (!dwc2_hw_is_otg(hsotg))
msleep(50);
break;
@@ -491,22 +658,17 @@ void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg)
}
/*
- * Do core a soft reset of the core. Be careful with this because it
- * resets all the internal state machines of the core.
- *
- * Additionally this will apply force mode as per the hsotg->dr_mode
- * parameter.
+ * dwc2_enable_acg - enable active clock gating feature
*/
-int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg)
+void dwc2_enable_acg(struct dwc2_hsotg *hsotg)
{
- int retval;
+ if (hsotg->params.acg_enable) {
+ u32 pcgcctl1 = dwc2_readl(hsotg->regs + PCGCCTL1);
- retval = dwc2_core_reset(hsotg, false);
- if (retval)
- return retval;
-
- dwc2_force_dr_mode(hsotg);
- return 0;
+ dev_dbg(hsotg->dev, "Enabling Active Clock Gating\n");
+ pcgcctl1 |= PCGCCTL1_GATEEN;
+ dwc2_writel(pcgcctl1, hsotg->regs + PCGCCTL1);
+ }
}
/**
@@ -683,25 +845,21 @@ void dwc2_dump_global_registers(struct dwc2_hsotg *hsotg)
void dwc2_flush_tx_fifo(struct dwc2_hsotg *hsotg, const int num)
{
u32 greset;
- int count = 0;
dev_vdbg(hsotg->dev, "Flush Tx FIFO %d\n", num);
+ /* Wait for AHB master IDLE state */
+ if (dwc2_hsotg_wait_bit_set(hsotg, GRSTCTL, GRSTCTL_AHBIDLE, 10000))
+ dev_warn(hsotg->dev, "%s: HANG! AHB Idle GRSCTL\n",
+ __func__);
+
greset = GRSTCTL_TXFFLSH;
greset |= num << GRSTCTL_TXFNUM_SHIFT & GRSTCTL_TXFNUM_MASK;
dwc2_writel(greset, hsotg->regs + GRSTCTL);
- do {
- greset = dwc2_readl(hsotg->regs + GRSTCTL);
- if (++count > 10000) {
- dev_warn(hsotg->dev,
- "%s() HANG! GRSTCTL=%0x GNPTXSTS=0x%08x\n",
- __func__, greset,
- dwc2_readl(hsotg->regs + GNPTXSTS));
- break;
- }
- udelay(1);
- } while (greset & GRSTCTL_TXFFLSH);
+ if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_TXFFLSH, 10000))
+ dev_warn(hsotg->dev, "%s: HANG! timeout GRSTCTL GRSTCTL_TXFFLSH\n",
+ __func__);
/* Wait for at least 3 PHY Clocks */
udelay(1);
@@ -715,43 +873,26 @@ void dwc2_flush_tx_fifo(struct dwc2_hsotg *hsotg, const int num)
void dwc2_flush_rx_fifo(struct dwc2_hsotg *hsotg)
{
u32 greset;
- int count = 0;
dev_vdbg(hsotg->dev, "%s()\n", __func__);
+ /* Wait for AHB master IDLE state */
+ if (dwc2_hsotg_wait_bit_set(hsotg, GRSTCTL, GRSTCTL_AHBIDLE, 10000))
+ dev_warn(hsotg->dev, "%s: HANG! AHB Idle GRSCTL\n",
+ __func__);
+
greset = GRSTCTL_RXFFLSH;
dwc2_writel(greset, hsotg->regs + GRSTCTL);
- do {
- greset = dwc2_readl(hsotg->regs + GRSTCTL);
- if (++count > 10000) {
- dev_warn(hsotg->dev, "%s() HANG! GRSTCTL=%0x\n",
- __func__, greset);
- break;
- }
- udelay(1);
- } while (greset & GRSTCTL_RXFFLSH);
+ /* Wait for RxFIFO flush done */
+ if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_RXFFLSH, 10000))
+ dev_warn(hsotg->dev, "%s: HANG! timeout GRSTCTL GRSTCTL_RXFFLSH\n",
+ __func__);
/* Wait for at least 3 PHY Clocks */
udelay(1);
}
-/*
- * Forces either host or device mode if the controller is not
- * currently in that mode.
- *
- * Returns true if the mode was forced.
- */
-bool dwc2_force_mode_if_needed(struct dwc2_hsotg *hsotg, bool host)
-{
- if (host && dwc2_is_host_mode(hsotg))
- return false;
- else if (!host && dwc2_is_device_mode(hsotg))
- return false;
-
- return dwc2_force_mode(hsotg, host);
-}
-
bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg)
{
if (dwc2_readl(hsotg->regs + GSNPSID) == 0xffffffff)
@@ -825,6 +966,52 @@ bool dwc2_hw_is_device(struct dwc2_hsotg *hsotg)
(op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE);
}
+/**
+ * dwc2_hsotg_wait_bit_set - Waits for bit to be set.
+ * @hsotg: Programming view of DWC_otg controller.
+ * @offset: Register's offset where bit/bits must be set.
+ * @mask: Mask of the bit/bits which must be set.
+ * @timeout: Timeout to wait.
+ *
+ * Return: 0 if bit/bits are set or -ETIMEDOUT in case of timeout.
+ */
+int dwc2_hsotg_wait_bit_set(struct dwc2_hsotg *hsotg, u32 offset, u32 mask,
+ u32 timeout)
+{
+ u32 i;
+
+ for (i = 0; i < timeout; i++) {
+ if (dwc2_readl(hsotg->regs + offset) & mask)
+ return 0;
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
+/**
+ * dwc2_hsotg_wait_bit_clear - Waits for bit to be clear.
+ * @hsotg: Programming view of DWC_otg controller.
+ * @offset: Register's offset where bit/bits must be set.
+ * @mask: Mask of the bit/bits which must be set.
+ * @timeout: Timeout to wait.
+ *
+ * Return: 0 if bit/bits are set or -ETIMEDOUT in case of timeout.
+ */
+int dwc2_hsotg_wait_bit_clear(struct dwc2_hsotg *hsotg, u32 offset, u32 mask,
+ u32 timeout)
+{
+ u32 i;
+
+ for (i = 0; i < timeout; i++) {
+ if (!(dwc2_readl(hsotg->regs + offset) & mask))
+ return 0;
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
MODULE_DESCRIPTION("DESIGNWARE HS OTG Core");
MODULE_AUTHOR("Synopsys, Inc.");
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index cd77af3b1565..d83be5651f87 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -217,7 +217,7 @@ struct dwc2_hsotg_ep {
unsigned char dir_in;
unsigned char index;
unsigned char mc;
- unsigned char interval;
+ u16 interval;
unsigned int halted:1;
unsigned int periodic:1;
@@ -408,7 +408,7 @@ enum dwc2_ep0_state {
* @ahbcfg: This field allows the default value of the GAHBCFG
* register to be overridden
* -1 - GAHBCFG value will be set to 0x06
- * (INCR4, default)
+ * (INCR, default)
* all others - GAHBCFG value will be overridden with
* this value
* Not all bits can be controlled like this, the
@@ -421,12 +421,26 @@ enum dwc2_ep0_state {
* case.
* 0 - No (default)
* 1 - Yes
- * @hibernation: Specifies whether the controller support hibernation.
- * If hibernation is enabled, the controller will enter
- * hibernation in both peripheral and host mode when
+ * @power_down: Specifies whether the controller support power_down.
+ * If power_down is enabled, the controller will enter
+ * power_down in both peripheral and host mode when
* needed.
* 0 - No (default)
+ * 1 - Partial power down
+ * 2 - Hibernation
+ * @lpm: Enable LPM support.
+ * 0 - No
* 1 - Yes
+ * @lpm_clock_gating: Enable core PHY clock gating.
+ * 0 - No
+ * 1 - Yes
+ * @besl: Enable LPM Errata support.
+ * 0 - No
+ * 1 - Yes
+ * @hird_threshold_en: HIRD or HIRD Threshold enable.
+ * 0 - No
+ * 1 - Yes
+ * @hird_threshold: Value of BESL or HIRD Threshold.
* @activate_stm_fs_transceiver: Activate internal transceiver using GGPIO
* register.
* 0 - Deactivate the transceiver (default)
@@ -479,12 +493,23 @@ struct dwc2_core_params {
bool enable_dynamic_fifo;
bool en_multiple_tx_fifo;
bool i2c_enable;
+ bool acg_enable;
bool ulpi_fs_ls;
bool ts_dline;
bool reload_ctl;
bool uframe_sched;
bool external_id_pin_ctl;
- bool hibernation;
+
+ int power_down;
+#define DWC2_POWER_DOWN_PARAM_NONE 0
+#define DWC2_POWER_DOWN_PARAM_PARTIAL 1
+#define DWC2_POWER_DOWN_PARAM_HIBERNATION 2
+
+ bool lpm;
+ bool lpm_clock_gating;
+ bool besl;
+ bool hird_threshold_en;
+ u8 hird_threshold;
bool activate_stm_fs_transceiver;
u16 max_packet_count;
u32 max_transfer_size;
@@ -560,6 +585,7 @@ struct dwc2_core_params {
* 2 - FS pins shared with UTMI+ pins
* 3 - FS pins shared with ULPI pins
* @total_fifo_size: Total internal RAM for FIFOs (bytes)
+ * @hibernation Is hibernation enabled?
* @utmi_phy_data_width UTMI+ PHY data width
* 0 - 8 bits
* 1 - 16 bits
@@ -587,12 +613,15 @@ struct dwc2_hw_params {
unsigned hs_phy_type:2;
unsigned fs_phy_type:2;
unsigned i2c_enable:1;
+ unsigned acg_enable:1;
unsigned num_dev_ep:4;
unsigned num_dev_in_eps : 4;
unsigned num_dev_perio_in_ep:4;
unsigned total_fifo_size:16;
unsigned power_optimized:1;
+ unsigned hibernation:1;
unsigned utmi_phy_data_width:2;
+ unsigned lpm_mode:1;
u32 snpsid;
u32 dev_ep_dirs;
u32 g_tx_fifo_size[MAX_EPS_CHANNELS];
@@ -611,9 +640,8 @@ struct dwc2_hw_params {
* @grxfsiz: Backup of GRXFSIZ register
* @gnptxfsiz: Backup of GNPTXFSIZ register
* @gi2cctl: Backup of GI2CCTL register
- * @hptxfsiz: Backup of HPTXFSIZ register
+ * @glpmcfg: Backup of GLPMCFG register
* @gdfifocfg: Backup of GDFIFOCFG register
- * @dtxfsiz: Backup of DTXFSIZ registers for each endpoint
* @gpwrdn: Backup of GPWRDN register
*/
struct dwc2_gregs_backup {
@@ -624,10 +652,10 @@ struct dwc2_gregs_backup {
u32 grxfsiz;
u32 gnptxfsiz;
u32 gi2cctl;
- u32 hptxfsiz;
+ u32 glpmcfg;
u32 pcgcctl;
+ u32 pcgcctl1;
u32 gdfifocfg;
- u32 dtxfsiz[MAX_EPS_CHANNELS];
u32 gpwrdn;
bool valid;
};
@@ -646,6 +674,7 @@ struct dwc2_gregs_backup {
* @doepctl: Backup of DOEPCTL register
* @doeptsiz: Backup of DOEPTSIZ register
* @doepdma: Backup of DOEPDMA register
+ * @dtxfsiz: Backup of DTXFSIZ registers for each endpoint
*/
struct dwc2_dregs_backup {
u32 dcfg;
@@ -659,6 +688,7 @@ struct dwc2_dregs_backup {
u32 doepctl[MAX_EPS_CHANNELS];
u32 doeptsiz[MAX_EPS_CHANNELS];
u32 doepdma[MAX_EPS_CHANNELS];
+ u32 dtxfsiz[MAX_EPS_CHANNELS];
bool valid;
};
@@ -670,6 +700,7 @@ struct dwc2_dregs_backup {
* @hcintmsk: Backup of HCINTMSK register
* @hptr0: Backup of HPTR0 register
* @hfir: Backup of HFIR register
+ * @hptxfsiz: Backup of HPTXFSIZ register
*/
struct dwc2_hregs_backup {
u32 hcfg;
@@ -677,6 +708,7 @@ struct dwc2_hregs_backup {
u32 hcintmsk[MAX_EPS_CHANNELS];
u32 hprt0;
u32 hfir;
+ u32 hptxfsiz;
bool valid;
};
@@ -780,12 +812,14 @@ struct dwc2_hregs_backup {
* @hcd_enabled Host mode sub-driver initialization indicator.
* @gadget_enabled Peripheral mode sub-driver initialization indicator.
* @ll_hw_enabled Status of low-level hardware resources.
+ * @hibernated: True if core is hibernated
* @phy: The otg phy transceiver structure for phy control.
* @uphy: The otg phy transceiver structure for old USB phy
* control.
* @plat: The platform specific configuration data. This can be
* removed once all SoCs support usb transceiver.
* @supplies: Definition of USB power supplies
+ * @vbus_supply: Regulator supplying vbus.
* @phyif: PHY interface width
* @lock: Spinlock that protects all the driver data structures
* @priv: Stores a pointer to the struct usb_hcd
@@ -897,6 +931,8 @@ struct dwc2_hregs_backup {
* @ctrl_req: Request for EP0 control packets.
* @ep0_state: EP0 control transfers state
* @test_mode: USB test mode requested by the host
+ * @remote_wakeup_allowed: True if device is allowed to wake-up host by
+ * remote-wakeup signalling
* @setup_desc_dma: EP0 setup stage desc chain DMA address
* @setup_desc: EP0 setup stage desc chain pointer
* @ctrl_in_desc_dma: EP0 IN data phase desc chain DMA address
@@ -917,11 +953,13 @@ struct dwc2_hsotg {
unsigned int hcd_enabled:1;
unsigned int gadget_enabled:1;
unsigned int ll_hw_enabled:1;
+ unsigned int hibernated:1;
struct phy *phy;
struct usb_phy *uphy;
struct dwc2_hsotg_plat *plat;
struct regulator_bulk_data supplies[DWC2_NUM_SUPPLIES];
+ struct regulator *vbus_supply;
u32 phyif;
spinlock_t lock;
@@ -947,6 +985,7 @@ struct dwc2_hsotg {
/* DWC OTG HW Release versions */
#define DWC2_CORE_REV_2_71a 0x4f54271a
+#define DWC2_CORE_REV_2_80a 0x4f54280a
#define DWC2_CORE_REV_2_90a 0x4f54290a
#define DWC2_CORE_REV_2_91a 0x4f54291a
#define DWC2_CORE_REV_2_92a 0x4f54292a
@@ -956,6 +995,11 @@ struct dwc2_hsotg {
#define DWC2_FS_IOT_REV_1_00a 0x5531100a
#define DWC2_HS_IOT_REV_1_00a 0x5532100a
+ /* DWC OTG HW Core ID */
+#define DWC2_OTG_ID 0x4f540000
+#define DWC2_FS_IOT_ID 0x55310000
+#define DWC2_HS_IOT_ID 0x55320000
+
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
union dwc2_hcd_internal_flags {
u32 d32;
@@ -1016,24 +1060,6 @@ struct dwc2_hsotg {
struct kmem_cache *desc_gen_cache;
struct kmem_cache *desc_hsisoc_cache;
-#ifdef DEBUG
- u32 frrem_samples;
- u64 frrem_accum;
-
- u32 hfnum_7_samples_a;
- u64 hfnum_7_frrem_accum_a;
- u32 hfnum_0_samples_a;
- u64 hfnum_0_frrem_accum_a;
- u32 hfnum_other_samples_a;
- u64 hfnum_other_frrem_accum_a;
-
- u32 hfnum_7_samples_b;
- u64 hfnum_7_frrem_accum_b;
- u32 hfnum_0_samples_b;
- u64 hfnum_0_frrem_accum_b;
- u32 hfnum_other_samples_b;
- u64 hfnum_other_frrem_accum_b;
-#endif
#endif /* CONFIG_USB_DWC2_HOST || CONFIG_USB_DWC2_DUAL_ROLE */
#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
@@ -1062,6 +1088,7 @@ struct dwc2_hsotg {
struct usb_gadget gadget;
unsigned int enabled:1;
unsigned int connected:1;
+ unsigned int remote_wakeup_allowed:1;
struct dwc2_hsotg_ep *eps_in[MAX_EPS_CHANNELS];
struct dwc2_hsotg_ep *eps_out[MAX_EPS_CHANNELS];
#endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */
@@ -1106,12 +1133,13 @@ static inline bool dwc2_is_hs_iot(struct dwc2_hsotg *hsotg)
* and the DWC_otg controller
*/
int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait);
-int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg);
-int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg);
-int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore);
+int dwc2_enter_partial_power_down(struct dwc2_hsotg *hsotg);
+int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, bool restore);
+int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg, int is_host);
+int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup,
+ int reset, int is_host);
-bool dwc2_force_mode_if_needed(struct dwc2_hsotg *hsotg, bool host);
-void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg);
+void dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host);
void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg);
bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg);
@@ -1128,6 +1156,13 @@ void dwc2_flush_rx_fifo(struct dwc2_hsotg *hsotg);
void dwc2_enable_global_interrupts(struct dwc2_hsotg *hcd);
void dwc2_disable_global_interrupts(struct dwc2_hsotg *hcd);
+void dwc2_hib_restore_common(struct dwc2_hsotg *hsotg, int rem_wakeup,
+ int is_host);
+int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg);
+int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg);
+
+void dwc2_enable_acg(struct dwc2_hsotg *hsotg);
+
/* This function should be called on every hardware interrupt. */
irqreturn_t dwc2_handle_common_intr(int irq, void *dev);
@@ -1137,6 +1172,11 @@ extern const struct of_device_id dwc2_of_match_table[];
int dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg);
int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg);
+/* Common polling functions */
+int dwc2_hsotg_wait_bit_set(struct dwc2_hsotg *hs_otg, u32 reg, u32 bit,
+ u32 timeout);
+int dwc2_hsotg_wait_bit_clear(struct dwc2_hsotg *hs_otg, u32 reg, u32 bit,
+ u32 timeout);
/* Parameters */
int dwc2_get_hwparams(struct dwc2_hsotg *hsotg);
int dwc2_init_params(struct dwc2_hsotg *hsotg);
@@ -1180,7 +1220,7 @@ void dwc2_dump_global_registers(struct dwc2_hsotg *hsotg);
int dwc2_hsotg_remove(struct dwc2_hsotg *hsotg);
int dwc2_hsotg_suspend(struct dwc2_hsotg *dwc2);
int dwc2_hsotg_resume(struct dwc2_hsotg *dwc2);
-int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq);
+int dwc2_gadget_init(struct dwc2_hsotg *hsotg);
void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2,
bool reset);
void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg);
@@ -1188,10 +1228,14 @@ void dwc2_hsotg_disconnect(struct dwc2_hsotg *dwc2);
int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode);
#define dwc2_is_device_connected(hsotg) (hsotg->connected)
int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg);
-int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg);
+int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup);
+int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg);
+int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
+ int rem_wakeup, int reset);
int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg);
int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg);
int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg);
+void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg);
#else
static inline int dwc2_hsotg_remove(struct dwc2_hsotg *dwc2)
{ return 0; }
@@ -1199,7 +1243,7 @@ static inline int dwc2_hsotg_suspend(struct dwc2_hsotg *dwc2)
{ return 0; }
static inline int dwc2_hsotg_resume(struct dwc2_hsotg *dwc2)
{ return 0; }
-static inline int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
+static inline int dwc2_gadget_init(struct dwc2_hsotg *hsotg)
{ return 0; }
static inline void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2,
bool reset) {}
@@ -1211,7 +1255,13 @@ static inline int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg,
#define dwc2_is_device_connected(hsotg) (0)
static inline int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
{ return 0; }
-static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg)
+static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg,
+ int remote_wakeup)
+{ return 0; }
+static inline int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg)
+{ return 0; }
+static inline int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
+ int rem_wakeup, int reset)
{ return 0; }
static inline int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg)
{ return 0; }
@@ -1219,6 +1269,7 @@ static inline int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg)
{ return 0; }
static inline int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg)
{ return 0; }
+static inline void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg) {}
#endif
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
@@ -1227,8 +1278,12 @@ int dwc2_hcd_get_future_frame_number(struct dwc2_hsotg *hsotg, int us);
void dwc2_hcd_connect(struct dwc2_hsotg *hsotg);
void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force);
void dwc2_hcd_start(struct dwc2_hsotg *hsotg);
+int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup);
int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg);
int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg);
+int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg);
+int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg,
+ int rem_wakeup, int reset);
#else
static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
{ return 0; }
@@ -1239,12 +1294,19 @@ static inline void dwc2_hcd_connect(struct dwc2_hsotg *hsotg) {}
static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force) {}
static inline void dwc2_hcd_start(struct dwc2_hsotg *hsotg) {}
static inline void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) {}
+static inline int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup)
+{ return 0; }
static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
{ return 0; }
static inline int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg)
{ return 0; }
static inline int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
{ return 0; }
+static inline int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg)
+{ return 0; }
+static inline int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg,
+ int rem_wakeup, int reset)
+{ return 0; }
#endif
diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c
index ab3fa1630853..2982a155734d 100644
--- a/drivers/usb/dwc2/core_intr.c
+++ b/drivers/usb/dwc2/core_intr.c
@@ -321,10 +321,10 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
if (dwc2_is_device_mode(hsotg)) {
if (hsotg->lx_state == DWC2_L2) {
- ret = dwc2_exit_hibernation(hsotg, true);
+ ret = dwc2_exit_partial_power_down(hsotg, true);
if (ret && (ret != -ENOTSUPP))
dev_err(hsotg->dev,
- "exit hibernation failed\n");
+ "exit power_down failed\n");
}
/*
@@ -335,6 +335,57 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
}
}
+/**
+ * dwc2_wakeup_from_lpm_l1 - Exit the device from LPM L1 state
+ *
+ * @hsotg: Programming view of DWC_otg controller
+ *
+ */
+static void dwc2_wakeup_from_lpm_l1(struct dwc2_hsotg *hsotg)
+{
+ u32 glpmcfg;
+ u32 i = 0;
+
+ if (hsotg->lx_state != DWC2_L1) {
+ dev_err(hsotg->dev, "Core isn't in DWC2_L1 state\n");
+ return;
+ }
+
+ glpmcfg = dwc2_readl(hsotg->regs + GLPMCFG);
+ if (dwc2_is_device_mode(hsotg)) {
+ dev_dbg(hsotg->dev, "Exit from L1 state\n");
+ glpmcfg &= ~GLPMCFG_ENBLSLPM;
+ glpmcfg &= ~GLPMCFG_HIRD_THRES_EN;
+ dwc2_writel(glpmcfg, hsotg->regs + GLPMCFG);
+
+ do {
+ glpmcfg = dwc2_readl(hsotg->regs + GLPMCFG);
+
+ if (!(glpmcfg & (GLPMCFG_COREL1RES_MASK |
+ GLPMCFG_L1RESUMEOK | GLPMCFG_SLPSTS)))
+ break;
+
+ udelay(1);
+ } while (++i < 200);
+
+ if (i == 200) {
+ dev_err(hsotg->dev, "Failed to exit L1 sleep state in 200us.\n");
+ return;
+ }
+ dwc2_gadget_init_lpm(hsotg);
+ } else {
+ /* TODO */
+ dev_err(hsotg->dev, "Host side LPM is not supported.\n");
+ return;
+ }
+
+ /* Change to L0 state */
+ hsotg->lx_state = DWC2_L0;
+
+ /* Inform gadget to exit from L1 */
+ call_gadget(hsotg, resume);
+}
+
/*
* This interrupt indicates that the DWC_otg controller has detected a
* resume or remote wakeup sequence. If the DWC_otg controller is in
@@ -352,6 +403,11 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
dev_dbg(hsotg->dev, "++Resume or Remote Wakeup Detected Interrupt++\n");
dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state);
+ if (hsotg->lx_state == DWC2_L1) {
+ dwc2_wakeup_from_lpm_l1(hsotg);
+ return;
+ }
+
if (dwc2_is_device_mode(hsotg)) {
dev_dbg(hsotg->dev, "DSTS=0x%0x\n",
dwc2_readl(hsotg->regs + DSTS));
@@ -361,16 +417,16 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
/* Clear Remote Wakeup Signaling */
dctl &= ~DCTL_RMTWKUPSIG;
dwc2_writel(dctl, hsotg->regs + DCTL);
- ret = dwc2_exit_hibernation(hsotg, true);
+ ret = dwc2_exit_partial_power_down(hsotg, true);
if (ret && (ret != -ENOTSUPP))
- dev_err(hsotg->dev, "exit hibernation failed\n");
+ dev_err(hsotg->dev, "exit power_down failed\n");
call_gadget(hsotg, resume);
}
/* Change to L0 state */
hsotg->lx_state = DWC2_L0;
} else {
- if (hsotg->params.hibernation)
+ if (hsotg->params.power_down)
return;
if (hsotg->lx_state != DWC2_L1) {
@@ -428,32 +484,44 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
* state is active
*/
dsts = dwc2_readl(hsotg->regs + DSTS);
- dev_dbg(hsotg->dev, "DSTS=0x%0x\n", dsts);
+ dev_dbg(hsotg->dev, "%s: DSTS=0x%0x\n", __func__, dsts);
dev_dbg(hsotg->dev,
- "DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d\n",
+ "DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d HWCFG4.Hibernation=%d\n",
!!(dsts & DSTS_SUSPSTS),
- hsotg->hw_params.power_optimized);
- if ((dsts & DSTS_SUSPSTS) && hsotg->hw_params.power_optimized) {
- /* Ignore suspend request before enumeration */
- if (!dwc2_is_device_connected(hsotg)) {
- dev_dbg(hsotg->dev,
- "ignore suspend request before enumeration\n");
- return;
+ hsotg->hw_params.power_optimized,
+ hsotg->hw_params.hibernation);
+
+ /* Ignore suspend request before enumeration */
+ if (!dwc2_is_device_connected(hsotg)) {
+ dev_dbg(hsotg->dev,
+ "ignore suspend request before enumeration\n");
+ return;
+ }
+ if (dsts & DSTS_SUSPSTS) {
+ if (hsotg->hw_params.power_optimized) {
+ ret = dwc2_enter_partial_power_down(hsotg);
+ if (ret) {
+ if (ret != -ENOTSUPP)
+ dev_err(hsotg->dev,
+ "%s: enter partial_power_down failed\n",
+ __func__);
+ goto skip_power_saving;
+ }
+
+ udelay(100);
+
+ /* Ask phy to be suspended */
+ if (!IS_ERR_OR_NULL(hsotg->uphy))
+ usb_phy_set_suspend(hsotg->uphy, true);
}
- ret = dwc2_enter_hibernation(hsotg);
- if (ret) {
- if (ret != -ENOTSUPP)
+ if (hsotg->hw_params.hibernation) {
+ ret = dwc2_enter_hibernation(hsotg, 0);
+ if (ret && ret != -ENOTSUPP)
dev_err(hsotg->dev,
- "enter hibernation failed\n");
- goto skip_power_saving;
+ "%s: enter hibernation failed\n",
+ __func__);
}
-
- udelay(100);
-
- /* Ask phy to be suspended */
- if (!IS_ERR_OR_NULL(hsotg->uphy))
- usb_phy_set_suspend(hsotg->uphy, true);
skip_power_saving:
/*
* Change to L2 (suspend) state before releasing
@@ -479,10 +547,75 @@ skip_power_saving:
}
}
+/**
+ * dwc2_handle_lpm_intr - GINTSTS_LPMTRANRCVD Interrupt handler
+ *
+ * @hsotg: Programming view of DWC_otg controller
+ *
+ */
+static void dwc2_handle_lpm_intr(struct dwc2_hsotg *hsotg)
+{
+ u32 glpmcfg;
+ u32 pcgcctl;
+ u32 hird;
+ u32 hird_thres;
+ u32 hird_thres_en;
+ u32 enslpm;
+
+ /* Clear interrupt */
+ dwc2_writel(GINTSTS_LPMTRANRCVD, hsotg->regs + GINTSTS);
+
+ glpmcfg = dwc2_readl(hsotg->regs + GLPMCFG);
+
+ if (!(glpmcfg & GLPMCFG_LPMCAP)) {
+ dev_err(hsotg->dev, "Unexpected LPM interrupt\n");
+ return;
+ }
+
+ hird = (glpmcfg & GLPMCFG_HIRD_MASK) >> GLPMCFG_HIRD_SHIFT;
+ hird_thres = (glpmcfg & GLPMCFG_HIRD_THRES_MASK &
+ ~GLPMCFG_HIRD_THRES_EN) >> GLPMCFG_HIRD_THRES_SHIFT;
+ hird_thres_en = glpmcfg & GLPMCFG_HIRD_THRES_EN;
+ enslpm = glpmcfg & GLPMCFG_ENBLSLPM;
+
+ if (dwc2_is_device_mode(hsotg)) {
+ dev_dbg(hsotg->dev, "HIRD_THRES_EN = %d\n", hird_thres_en);
+
+ if (hird_thres_en && hird >= hird_thres) {
+ dev_dbg(hsotg->dev, "L1 with utmi_l1_suspend_n\n");
+ } else if (enslpm) {
+ dev_dbg(hsotg->dev, "L1 with utmi_sleep_n\n");
+ } else {
+ dev_dbg(hsotg->dev, "Entering Sleep with L1 Gating\n");
+
+ pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
+ pcgcctl |= PCGCTL_ENBL_SLEEP_GATING;
+ dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ }
+ /**
+ * Examine prt_sleep_sts after TL1TokenTetry period max (10 us)
+ */
+ udelay(10);
+
+ glpmcfg = dwc2_readl(hsotg->regs + GLPMCFG);
+
+ if (glpmcfg & GLPMCFG_SLPSTS) {
+ /* Save the current state */
+ hsotg->lx_state = DWC2_L1;
+ dev_dbg(hsotg->dev,
+ "Core is in L1 sleep glpmcfg=%08x\n", glpmcfg);
+
+ /* Inform gadget that we are in L1 state */
+ call_gadget(hsotg, suspend);
+ }
+ }
+}
+
#define GINTMSK_COMMON (GINTSTS_WKUPINT | GINTSTS_SESSREQINT | \
GINTSTS_CONIDSTSCHNG | GINTSTS_OTGINT | \
GINTSTS_MODEMIS | GINTSTS_DISCONNINT | \
- GINTSTS_USBSUSP | GINTSTS_PRTINT)
+ GINTSTS_USBSUSP | GINTSTS_PRTINT | \
+ GINTSTS_LPMTRANRCVD)
/*
* This function returns the Core Interrupt register
@@ -510,6 +643,116 @@ static u32 dwc2_read_common_intr(struct dwc2_hsotg *hsotg)
}
/*
+ * GPWRDN interrupt handler.
+ *
+ * The GPWRDN interrupts are those that occur in both Host and
+ * Device mode while core is in hibernated state.
+ */
+static void dwc2_handle_gpwrdn_intr(struct dwc2_hsotg *hsotg)
+{
+ u32 gpwrdn;
+ int linestate;
+
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ /* clear all interrupt */
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ linestate = (gpwrdn & GPWRDN_LINESTATE_MASK) >> GPWRDN_LINESTATE_SHIFT;
+ dev_dbg(hsotg->dev,
+ "%s: dwc2_handle_gpwrdwn_intr called gpwrdn= %08x\n", __func__,
+ gpwrdn);
+
+ if ((gpwrdn & GPWRDN_DISCONN_DET) &&
+ (gpwrdn & GPWRDN_DISCONN_DET_MSK) && !linestate) {
+ u32 gpwrdn_tmp;
+
+ dev_dbg(hsotg->dev, "%s: GPWRDN_DISCONN_DET\n", __func__);
+
+ /* Switch-on voltage to the core */
+ gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn_tmp &= ~GPWRDN_PWRDNSWTCH;
+ dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Reset core */
+ gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn_tmp &= ~GPWRDN_PWRDNRSTN;
+ dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Disable Power Down Clamp */
+ gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn_tmp &= ~GPWRDN_PWRDNCLMP;
+ dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Deassert reset core */
+ gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn_tmp |= GPWRDN_PWRDNRSTN;
+ dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Disable PMU interrupt */
+ gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn_tmp &= ~GPWRDN_PMUINTSEL;
+ dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN);
+
+ /* De-assert Wakeup Logic */
+ gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn_tmp &= ~GPWRDN_PMUACTV;
+ dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN);
+
+ hsotg->hibernated = 0;
+
+ if (gpwrdn & GPWRDN_IDSTS) {
+ hsotg->op_state = OTG_STATE_B_PERIPHERAL;
+ dwc2_core_init(hsotg, false);
+ dwc2_enable_global_interrupts(hsotg);
+ dwc2_hsotg_core_init_disconnected(hsotg, false);
+ dwc2_hsotg_core_connect(hsotg);
+ } else {
+ hsotg->op_state = OTG_STATE_A_HOST;
+
+ /* Initialize the Core for Host mode */
+ dwc2_core_init(hsotg, false);
+ dwc2_enable_global_interrupts(hsotg);
+ dwc2_hcd_start(hsotg);
+ }
+ }
+
+ if ((gpwrdn & GPWRDN_LNSTSCHG) &&
+ (gpwrdn & GPWRDN_LNSTSCHG_MSK) && linestate) {
+ dev_dbg(hsotg->dev, "%s: GPWRDN_LNSTSCHG\n", __func__);
+ if (hsotg->hw_params.hibernation &&
+ hsotg->hibernated) {
+ if (gpwrdn & GPWRDN_IDSTS) {
+ dwc2_exit_hibernation(hsotg, 0, 0, 0);
+ call_gadget(hsotg, resume);
+ } else {
+ dwc2_exit_hibernation(hsotg, 1, 0, 1);
+ }
+ }
+ }
+ if ((gpwrdn & GPWRDN_RST_DET) && (gpwrdn & GPWRDN_RST_DET_MSK)) {
+ dev_dbg(hsotg->dev, "%s: GPWRDN_RST_DET\n", __func__);
+ if (!linestate && (gpwrdn & GPWRDN_BSESSVLD))
+ dwc2_exit_hibernation(hsotg, 0, 1, 0);
+ }
+ if ((gpwrdn & GPWRDN_STS_CHGINT) &&
+ (gpwrdn & GPWRDN_STS_CHGINT_MSK) && linestate) {
+ dev_dbg(hsotg->dev, "%s: GPWRDN_STS_CHGINT\n", __func__);
+ if (hsotg->hw_params.hibernation &&
+ hsotg->hibernated) {
+ if (gpwrdn & GPWRDN_IDSTS) {
+ dwc2_exit_hibernation(hsotg, 0, 0, 0);
+ call_gadget(hsotg, resume);
+ } else {
+ dwc2_exit_hibernation(hsotg, 1, 0, 1);
+ }
+ }
+ }
+}
+
+/*
* Common interrupt handler
*
* The common interrupts are those that occur in both Host and Device mode.
@@ -539,6 +782,13 @@ irqreturn_t dwc2_handle_common_intr(int irq, void *dev)
if (gintsts & ~GINTSTS_PRTINT)
retval = IRQ_HANDLED;
+ /* In case of hibernated state gintsts must not work */
+ if (hsotg->hibernated) {
+ dwc2_handle_gpwrdn_intr(hsotg);
+ retval = IRQ_HANDLED;
+ goto out;
+ }
+
if (gintsts & GINTSTS_MODEMIS)
dwc2_handle_mode_mismatch_intr(hsotg);
if (gintsts & GINTSTS_OTGINT)
@@ -553,6 +803,8 @@ irqreturn_t dwc2_handle_common_intr(int irq, void *dev)
dwc2_handle_wakeup_detected_intr(hsotg);
if (gintsts & GINTSTS_USBSUSP)
dwc2_handle_usb_suspend_intr(hsotg);
+ if (gintsts & GINTSTS_LPMTRANRCVD)
+ dwc2_handle_lpm_intr(hsotg);
if (gintsts & GINTSTS_PRTINT) {
/*
diff --git a/drivers/usb/dwc2/debugfs.c b/drivers/usb/dwc2/debugfs.c
index f4650a88be78..58c691f882a8 100644
--- a/drivers/usb/dwc2/debugfs.c
+++ b/drivers/usb/dwc2/debugfs.c
@@ -170,19 +170,7 @@ static int state_show(struct seq_file *seq, void *v)
return 0;
}
-
-static int state_open(struct inode *inode, struct file *file)
-{
- return single_open(file, state_show, inode->i_private);
-}
-
-static const struct file_operations state_fops = {
- .owner = THIS_MODULE,
- .open = state_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(state);
/**
* fifo_show - debugfs: show the fifo information
@@ -219,19 +207,7 @@ static int fifo_show(struct seq_file *seq, void *v)
return 0;
}
-
-static int fifo_open(struct inode *inode, struct file *file)
-{
- return single_open(file, fifo_show, inode->i_private);
-}
-
-static const struct file_operations fifo_fops = {
- .owner = THIS_MODULE,
- .open = fifo_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(fifo);
static const char *decode_direction(int is_in)
{
@@ -303,19 +279,7 @@ static int ep_show(struct seq_file *seq, void *v)
return 0;
}
-
-static int ep_open(struct inode *inode, struct file *file)
-{
- return single_open(file, ep_show, inode->i_private);
-}
-
-static const struct file_operations ep_fops = {
- .owner = THIS_MODULE,
- .open = ep_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(ep);
/**
* dwc2_hsotg_create_debug - create debugfs directory and files
@@ -754,7 +718,12 @@ static int params_show(struct seq_file *seq, void *v)
print_param_hex(seq, p, ahbcfg);
print_param(seq, p, uframe_sched);
print_param(seq, p, external_id_pin_ctl);
- print_param(seq, p, hibernation);
+ print_param(seq, p, power_down);
+ print_param(seq, p, lpm);
+ print_param(seq, p, lpm_clock_gating);
+ print_param(seq, p, besl);
+ print_param(seq, p, hird_threshold_en);
+ print_param(seq, p, hird_threshold);
print_param(seq, p, host_dma);
print_param(seq, p, g_dma);
print_param(seq, p, g_dma_desc);
@@ -770,19 +739,7 @@ static int params_show(struct seq_file *seq, void *v)
return 0;
}
-
-static int params_open(struct inode *inode, struct file *file)
-{
- return single_open(file, params_show, inode->i_private);
-}
-
-static const struct file_operations params_fops = {
- .owner = THIS_MODULE,
- .open = params_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(params);
static int hw_params_show(struct seq_file *seq, void *v)
{
@@ -817,19 +774,7 @@ static int hw_params_show(struct seq_file *seq, void *v)
return 0;
}
-
-static int hw_params_open(struct inode *inode, struct file *file)
-{
- return single_open(file, hw_params_show, inode->i_private);
-}
-
-static const struct file_operations hw_params_fops = {
- .owner = THIS_MODULE,
- .open = hw_params_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(hw_params);
static int dr_mode_show(struct seq_file *seq, void *v)
{
@@ -840,19 +785,7 @@ static int dr_mode_show(struct seq_file *seq, void *v)
seq_printf(seq, "%s\n", dr_mode);
return 0;
}
-
-static int dr_mode_open(struct inode *inode, struct file *file)
-{
- return single_open(file, dr_mode_show, inode->i_private);
-}
-
-static const struct file_operations dr_mode_fops = {
- .owner = THIS_MODULE,
- .open = dr_mode_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(dr_mode);
int dwc2_debugfs_init(struct dwc2_hsotg *hsotg)
{
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index 5bcad1d869b5..6c32bf26e48e 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -47,12 +47,12 @@ static inline struct dwc2_hsotg *to_hsotg(struct usb_gadget *gadget)
return container_of(gadget, struct dwc2_hsotg, gadget);
}
-static inline void __orr32(void __iomem *ptr, u32 val)
+static inline void dwc2_set_bit(void __iomem *ptr, u32 val)
{
dwc2_writel(dwc2_readl(ptr) | val, ptr);
}
-static inline void __bic32(void __iomem *ptr, u32 val)
+static inline void dwc2_clear_bit(void __iomem *ptr, u32 val)
{
dwc2_writel(dwc2_readl(ptr) & ~val, ptr);
}
@@ -116,10 +116,10 @@ static inline void dwc2_gadget_incr_frame_num(struct dwc2_hsotg_ep *hs_ep)
{
hs_ep->target_frame += hs_ep->interval;
if (hs_ep->target_frame > DSTS_SOFFN_LIMIT) {
- hs_ep->frame_overrun = 1;
+ hs_ep->frame_overrun = true;
hs_ep->target_frame &= DSTS_SOFFN_LIMIT;
} else {
- hs_ep->frame_overrun = 0;
+ hs_ep->frame_overrun = false;
}
}
@@ -252,6 +252,7 @@ static void dwc2_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
unsigned int ep;
unsigned int addr;
int timeout;
+
u32 val;
u32 *txfsz = hsotg->params.g_tx_fifo_size;
@@ -1296,8 +1297,8 @@ static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
req->zero, req->short_not_ok);
/* Prevent new request submission when controller is suspended */
- if (hs->lx_state == DWC2_L2) {
- dev_dbg(hs->dev, "%s: don't submit request while suspended\n",
+ if (hs->lx_state != DWC2_L0) {
+ dev_dbg(hs->dev, "%s: submit request only in active state\n",
__func__);
return -EAGAIN;
}
@@ -1639,6 +1640,10 @@ static int dwc2_hsotg_process_req_feature(struct dwc2_hsotg *hsotg,
switch (recip) {
case USB_RECIP_DEVICE:
switch (wValue) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ hsotg->remote_wakeup_allowed = 1;
+ break;
+
case USB_DEVICE_TEST_MODE:
if ((wIndex & 0xff) != 0)
return -EINVAL;
@@ -2495,30 +2500,13 @@ bad_mps:
*/
static void dwc2_hsotg_txfifo_flush(struct dwc2_hsotg *hsotg, unsigned int idx)
{
- int timeout;
- int val;
-
dwc2_writel(GRSTCTL_TXFNUM(idx) | GRSTCTL_TXFFLSH,
hsotg->regs + GRSTCTL);
/* wait until the fifo is flushed */
- timeout = 100;
-
- while (1) {
- val = dwc2_readl(hsotg->regs + GRSTCTL);
-
- if ((val & (GRSTCTL_TXFFLSH)) == 0)
- break;
-
- if (--timeout == 0) {
- dev_err(hsotg->dev,
- "%s: timeout flushing fifo (GRSTCTL=%08x)\n",
- __func__, val);
- break;
- }
-
- udelay(1);
- }
+ if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_TXFFLSH, 100))
+ dev_warn(hsotg->dev, "%s: timeout flushing fifo GRSTCTL_TXFFLSH\n",
+ __func__);
}
/**
@@ -3253,7 +3241,7 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
dwc2_hsotg_init_fifo(hsotg);
if (!is_usb_reset)
- __orr32(hsotg->regs + DCTL, DCTL_SFTDISCON);
+ dwc2_set_bit(hsotg->regs + DCTL, DCTL_SFTDISCON);
dcfg |= DCFG_EPMISCNT(1);
@@ -3282,7 +3270,8 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
GINTSTS_GOUTNAKEFF | GINTSTS_GINNAKEFF |
GINTSTS_USBRST | GINTSTS_RESETDET |
GINTSTS_ENUMDONE | GINTSTS_OTGINT |
- GINTSTS_USBSUSP | GINTSTS_WKUPINT;
+ GINTSTS_USBSUSP | GINTSTS_WKUPINT |
+ GINTSTS_LPMTRANRCVD;
if (!using_desc_dma(hsotg))
intmsk |= GINTSTS_INCOMPL_SOIN | GINTSTS_INCOMPL_SOOUT;
@@ -3294,12 +3283,12 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
if (using_dma(hsotg)) {
dwc2_writel(GAHBCFG_GLBL_INTR_EN | GAHBCFG_DMA_EN |
- (GAHBCFG_HBSTLEN_INCR4 << GAHBCFG_HBSTLEN_SHIFT),
+ hsotg->params.ahbcfg,
hsotg->regs + GAHBCFG);
/* Set DDMA mode support in the core if needed */
if (using_desc_dma(hsotg))
- __orr32(hsotg->regs + DCFG, DCFG_DESCDMA_EN);
+ dwc2_set_bit(hsotg->regs + DCFG, DCFG_DESCDMA_EN);
} else {
dwc2_writel(((hsotg->dedicated_fifos) ?
@@ -3332,7 +3321,7 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
/* Enable BNA interrupt for DDMA */
if (using_desc_dma(hsotg))
- __orr32(hsotg->regs + DOEPMSK, DOEPMSK_BNAMSK);
+ dwc2_set_bit(hsotg->regs + DOEPMSK, DOEPMSK_BNAMSK);
dwc2_writel(0, hsotg->regs + DAINTMSK);
@@ -3356,9 +3345,9 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
dwc2_hsotg_ctrl_epint(hsotg, 0, 1, 1);
if (!is_usb_reset) {
- __orr32(hsotg->regs + DCTL, DCTL_PWRONPRGDONE);
+ dwc2_set_bit(hsotg->regs + DCTL, DCTL_PWRONPRGDONE);
udelay(10); /* see openiboot */
- __bic32(hsotg->regs + DCTL, DCTL_PWRONPRGDONE);
+ dwc2_clear_bit(hsotg->regs + DCTL, DCTL_PWRONPRGDONE);
}
dev_dbg(hsotg->dev, "DCTL=0x%08x\n", dwc2_readl(hsotg->regs + DCTL));
@@ -3385,7 +3374,10 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
val = DCTL_CGOUTNAK | DCTL_CGNPINNAK;
if (!is_usb_reset)
val |= DCTL_SFTDISCON;
- __orr32(hsotg->regs + DCTL, val);
+ dwc2_set_bit(hsotg->regs + DCTL, val);
+
+ /* configure the core to support LPM */
+ dwc2_gadget_init_lpm(hsotg);
/* must be at-least 3ms to allow bus to see disconnect */
mdelay(3);
@@ -3402,13 +3394,13 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
static void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg)
{
/* set the soft-disconnect bit */
- __orr32(hsotg->regs + DCTL, DCTL_SFTDISCON);
+ dwc2_set_bit(hsotg->regs + DCTL, DCTL_SFTDISCON);
}
void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg)
{
/* remove the soft-disconnect and let's go */
- __bic32(hsotg->regs + DCTL, DCTL_SFTDISCON);
+ dwc2_clear_bit(hsotg->regs + DCTL, DCTL_SFTDISCON);
}
/**
@@ -3428,14 +3420,21 @@ static void dwc2_gadget_handle_incomplete_isoc_in(struct dwc2_hsotg *hsotg)
{
struct dwc2_hsotg_ep *hs_ep;
u32 epctrl;
+ u32 daintmsk;
u32 idx;
dev_dbg(hsotg->dev, "Incomplete isoc in interrupt received:\n");
+ daintmsk = dwc2_readl(hsotg->regs + DAINTMSK);
+
for (idx = 1; idx <= hsotg->num_of_eps; idx++) {
hs_ep = hsotg->eps_in[idx];
+ /* Proceed only unmasked ISOC EPs */
+ if (!hs_ep->isochronous || (BIT(idx) & ~daintmsk))
+ continue;
+
epctrl = dwc2_readl(hsotg->regs + DIEPCTL(idx));
- if ((epctrl & DXEPCTL_EPENA) && hs_ep->isochronous &&
+ if ((epctrl & DXEPCTL_EPENA) &&
dwc2_gadget_target_frame_elapsed(hs_ep)) {
epctrl |= DXEPCTL_SNAK;
epctrl |= DXEPCTL_EPDIS;
@@ -3464,16 +3463,24 @@ static void dwc2_gadget_handle_incomplete_isoc_out(struct dwc2_hsotg *hsotg)
{
u32 gintsts;
u32 gintmsk;
+ u32 daintmsk;
u32 epctrl;
struct dwc2_hsotg_ep *hs_ep;
int idx;
dev_dbg(hsotg->dev, "%s: GINTSTS_INCOMPL_SOOUT\n", __func__);
+ daintmsk = dwc2_readl(hsotg->regs + DAINTMSK);
+ daintmsk >>= DAINT_OUTEP_SHIFT;
+
for (idx = 1; idx <= hsotg->num_of_eps; idx++) {
hs_ep = hsotg->eps_out[idx];
+ /* Proceed only unmasked ISOC EPs */
+ if (!hs_ep->isochronous || (BIT(idx) & ~daintmsk))
+ continue;
+
epctrl = dwc2_readl(hsotg->regs + DOEPCTL(idx));
- if ((epctrl & DXEPCTL_EPENA) && hs_ep->isochronous &&
+ if ((epctrl & DXEPCTL_EPENA) &&
dwc2_gadget_target_frame_elapsed(hs_ep)) {
/* Unmask GOUTNAKEFF interrupt */
gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
@@ -3481,8 +3488,10 @@ static void dwc2_gadget_handle_incomplete_isoc_out(struct dwc2_hsotg *hsotg)
dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
gintsts = dwc2_readl(hsotg->regs + GINTSTS);
- if (!(gintsts & GINTSTS_GOUTNAKEFF))
- __orr32(hsotg->regs + DCTL, DCTL_SGOUTNAK);
+ if (!(gintsts & GINTSTS_GOUTNAKEFF)) {
+ dwc2_set_bit(hsotg->regs + DCTL, DCTL_SGOUTNAK);
+ break;
+ }
}
}
@@ -3522,7 +3531,7 @@ irq_retry:
/* This event must be used only if controller is suspended */
if (hsotg->lx_state == DWC2_L2) {
- dwc2_exit_hibernation(hsotg, true);
+ dwc2_exit_partial_power_down(hsotg, true);
hsotg->lx_state = DWC2_L0;
}
}
@@ -3541,7 +3550,7 @@ irq_retry:
dwc2_hsotg_disconnect(hsotg);
/* Reset device address to zero */
- __bic32(hsotg->regs + DCFG, DCFG_DEVADDR_MASK);
+ dwc2_clear_bit(hsotg->regs + DCFG, DCFG_DEVADDR_MASK);
if (usb_status & GOTGCTL_BSESVLD && connected)
dwc2_hsotg_core_init_disconnected(hsotg, true);
@@ -3627,8 +3636,11 @@ irq_retry:
u8 idx;
u32 epctrl;
u32 gintmsk;
+ u32 daintmsk;
struct dwc2_hsotg_ep *hs_ep;
+ daintmsk = dwc2_readl(hsotg->regs + DAINTMSK);
+ daintmsk >>= DAINT_OUTEP_SHIFT;
/* Mask this interrupt */
gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
gintmsk &= ~GINTSTS_GOUTNAKEFF;
@@ -3637,9 +3649,13 @@ irq_retry:
dev_dbg(hsotg->dev, "GOUTNakEff triggered\n");
for (idx = 1; idx <= hsotg->num_of_eps; idx++) {
hs_ep = hsotg->eps_out[idx];
+ /* Proceed only unmasked ISOC EPs */
+ if (!hs_ep->isochronous || (BIT(idx) & ~daintmsk))
+ continue;
+
epctrl = dwc2_readl(hsotg->regs + DOEPCTL(idx));
- if ((epctrl & DXEPCTL_EPENA) && hs_ep->isochronous) {
+ if (epctrl & DXEPCTL_EPENA) {
epctrl |= DXEPCTL_SNAK;
epctrl |= DXEPCTL_EPDIS;
dwc2_writel(epctrl, hsotg->regs + DOEPCTL(idx));
@@ -3652,7 +3668,7 @@ irq_retry:
if (gintsts & GINTSTS_GINNAKEFF) {
dev_info(hsotg->dev, "GINNakEff triggered\n");
- __orr32(hsotg->regs + DCTL, DCTL_CGNPINNAK);
+ dwc2_set_bit(hsotg->regs + DCTL, DCTL_CGNPINNAK);
dwc2_hsotg_dump(hsotg);
}
@@ -3676,20 +3692,6 @@ irq_retry:
return IRQ_HANDLED;
}
-static int dwc2_hsotg_wait_bit_set(struct dwc2_hsotg *hs_otg, u32 reg,
- u32 bit, u32 timeout)
-{
- u32 i;
-
- for (i = 0; i < timeout; i++) {
- if (dwc2_readl(hs_otg->regs + reg) & bit)
- return 0;
- udelay(1);
- }
-
- return -ETIMEDOUT;
-}
-
static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
struct dwc2_hsotg_ep *hs_ep)
{
@@ -3706,7 +3708,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
if (hs_ep->dir_in) {
if (hsotg->dedicated_fifos || hs_ep->periodic) {
- __orr32(hsotg->regs + epctrl_reg, DXEPCTL_SNAK);
+ dwc2_set_bit(hsotg->regs + epctrl_reg, DXEPCTL_SNAK);
/* Wait for Nak effect */
if (dwc2_hsotg_wait_bit_set(hsotg, epint_reg,
DXEPINT_INEPNAKEFF, 100))
@@ -3714,7 +3716,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
"%s: timeout DIEPINT.NAKEFF\n",
__func__);
} else {
- __orr32(hsotg->regs + DCTL, DCTL_SGNPINNAK);
+ dwc2_set_bit(hsotg->regs + DCTL, DCTL_SGNPINNAK);
/* Wait for Nak effect */
if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS,
GINTSTS_GINNAKEFF, 100))
@@ -3724,7 +3726,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
}
} else {
if (!(dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_GOUTNAKEFF))
- __orr32(hsotg->regs + DCTL, DCTL_SGOUTNAK);
+ dwc2_set_bit(hsotg->regs + DCTL, DCTL_SGOUTNAK);
/* Wait for global nak to take effect */
if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS,
@@ -3734,7 +3736,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
}
/* Disable ep */
- __orr32(hsotg->regs + epctrl_reg, DXEPCTL_EPDIS | DXEPCTL_SNAK);
+ dwc2_set_bit(hsotg->regs + epctrl_reg, DXEPCTL_EPDIS | DXEPCTL_SNAK);
/* Wait for ep to be disabled */
if (dwc2_hsotg_wait_bit_set(hsotg, epint_reg, DXEPINT_EPDISBLD, 100))
@@ -3742,7 +3744,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
"%s: timeout DOEPCTL.EPDisable\n", __func__);
/* Clear EPDISBLD interrupt */
- __orr32(hsotg->regs + epint_reg, DXEPINT_EPDISBLD);
+ dwc2_set_bit(hsotg->regs + epint_reg, DXEPINT_EPDISBLD);
if (hs_ep->dir_in) {
unsigned short fifo_index;
@@ -3757,11 +3759,11 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
/* Clear Global In NP NAK in Shared FIFO for non periodic ep */
if (!hsotg->dedicated_fifos && !hs_ep->periodic)
- __orr32(hsotg->regs + DCTL, DCTL_CGNPINNAK);
+ dwc2_set_bit(hsotg->regs + DCTL, DCTL_CGNPINNAK);
} else {
/* Remove global NAKs */
- __orr32(hsotg->regs + DCTL, DCTL_CGOUTNAK);
+ dwc2_set_bit(hsotg->regs + DCTL, DCTL_CGOUTNAK);
}
}
@@ -4183,7 +4185,7 @@ static void dwc2_hsotg_init(struct dwc2_hsotg *hsotg)
dwc2_writel(0, hsotg->regs + DAINTMSK);
/* Be in disconnected state until gadget is registered */
- __orr32(hsotg->regs + DCTL, DCTL_SFTDISCON);
+ dwc2_set_bit(hsotg->regs + DCTL, DCTL_SFTDISCON);
/* setup fifos */
@@ -4205,7 +4207,7 @@ static void dwc2_hsotg_init(struct dwc2_hsotg *hsotg)
dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
if (using_dma(hsotg))
- __orr32(hsotg->regs + GAHBCFG, GAHBCFG_DMA_EN);
+ dwc2_set_bit(hsotg->regs + GAHBCFG, GAHBCFG_DMA_EN);
}
/**
@@ -4352,6 +4354,8 @@ static int dwc2_hsotg_pullup(struct usb_gadget *gadget, int is_on)
if (is_on) {
hsotg->enabled = 1;
dwc2_hsotg_core_init_disconnected(hsotg, false);
+ /* Enable ACG feature in device mode,if supported */
+ dwc2_enable_acg(hsotg);
dwc2_hsotg_core_connect(hsotg);
} else {
dwc2_hsotg_core_disconnect(hsotg);
@@ -4374,18 +4378,21 @@ static int dwc2_hsotg_vbus_session(struct usb_gadget *gadget, int is_active)
spin_lock_irqsave(&hsotg->lock, flags);
/*
- * If controller is hibernated, it must exit from hibernation
+ * If controller is hibernated, it must exit from power_down
* before being initialized / de-initialized
*/
if (hsotg->lx_state == DWC2_L2)
- dwc2_exit_hibernation(hsotg, false);
+ dwc2_exit_partial_power_down(hsotg, false);
if (is_active) {
hsotg->op_state = OTG_STATE_B_PERIPHERAL;
dwc2_hsotg_core_init_disconnected(hsotg, false);
- if (hsotg->enabled)
+ if (hsotg->enabled) {
+ /* Enable ACG feature in device mode,if supported */
+ dwc2_enable_acg(hsotg);
dwc2_hsotg_core_connect(hsotg);
+ }
} else {
dwc2_hsotg_core_disconnect(hsotg);
dwc2_hsotg_disconnect(hsotg);
@@ -4606,9 +4613,8 @@ static void dwc2_hsotg_dump(struct dwc2_hsotg *hsotg)
/**
* dwc2_gadget_init - init function for gadget
* @dwc2: The data structure for the DWC2 driver.
- * @irq: The IRQ number for the controller.
*/
-int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
+int dwc2_gadget_init(struct dwc2_hsotg *hsotg)
{
struct device *dev = hsotg->dev;
int epnum;
@@ -4622,6 +4628,11 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
hsotg->gadget.max_speed = USB_SPEED_HIGH;
hsotg->gadget.ops = &dwc2_hsotg_gadget_ops;
hsotg->gadget.name = dev_name(dev);
+ hsotg->remote_wakeup_allowed = 0;
+
+ if (hsotg->params.lpm)
+ hsotg->gadget.lpm_capable = true;
+
if (hsotg->dr_mode == USB_DR_MODE_OTG)
hsotg->gadget.is_otg = 1;
else if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
@@ -4649,8 +4660,8 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
return ret;
}
- ret = devm_request_irq(hsotg->dev, irq, dwc2_hsotg_irq, IRQF_SHARED,
- dev_name(hsotg->dev), hsotg);
+ ret = devm_request_irq(hsotg->dev, hsotg->irq, dwc2_hsotg_irq,
+ IRQF_SHARED, dev_name(hsotg->dev), hsotg);
if (ret < 0) {
dev_err(dev, "cannot claim IRQ for gadget\n");
return ret;
@@ -4751,8 +4762,11 @@ int dwc2_hsotg_resume(struct dwc2_hsotg *hsotg)
spin_lock_irqsave(&hsotg->lock, flags);
dwc2_hsotg_core_init_disconnected(hsotg, false);
- if (hsotg->enabled)
+ if (hsotg->enabled) {
+ /* Enable ACG feature in device mode,if supported */
+ dwc2_enable_acg(hsotg);
dwc2_hsotg_core_connect(hsotg);
+ }
spin_unlock_irqrestore(&hsotg->lock, flags);
}
@@ -4806,6 +4820,7 @@ int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
dr->doeptsiz[i] = dwc2_readl(hsotg->regs + DOEPTSIZ(i));
dr->doepdma[i] = dwc2_readl(hsotg->regs + DOEPDMA(i));
+ dr->dtxfsiz[i] = dwc2_readl(hsotg->regs + DPTXFSIZN(i));
}
dr->valid = true;
return 0;
@@ -4817,11 +4832,13 @@ int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
* if controller power were disabled.
*
* @hsotg: Programming view of the DWC_otg controller
+ * @remote_wakeup: Indicates whether resume is initiated by Device or Host.
+ *
+ * Return: 0 if successful, negative error code otherwise
*/
-int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg)
+int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup)
{
struct dwc2_dregs_backup *dr;
- u32 dctl;
int i;
dev_dbg(hsotg->dev, "%s\n", __func__);
@@ -4835,28 +4852,240 @@ int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg)
}
dr->valid = false;
- dwc2_writel(dr->dcfg, hsotg->regs + DCFG);
- dwc2_writel(dr->dctl, hsotg->regs + DCTL);
+ if (!remote_wakeup)
+ dwc2_writel(dr->dctl, hsotg->regs + DCTL);
+
dwc2_writel(dr->daintmsk, hsotg->regs + DAINTMSK);
dwc2_writel(dr->diepmsk, hsotg->regs + DIEPMSK);
dwc2_writel(dr->doepmsk, hsotg->regs + DOEPMSK);
for (i = 0; i < hsotg->num_of_eps; i++) {
/* Restore IN EPs */
- dwc2_writel(dr->diepctl[i], hsotg->regs + DIEPCTL(i));
dwc2_writel(dr->dieptsiz[i], hsotg->regs + DIEPTSIZ(i));
dwc2_writel(dr->diepdma[i], hsotg->regs + DIEPDMA(i));
-
+ dwc2_writel(dr->doeptsiz[i], hsotg->regs + DOEPTSIZ(i));
+ /** WA for enabled EPx's IN in DDMA mode. On entering to
+ * hibernation wrong value read and saved from DIEPDMAx,
+ * as result BNA interrupt asserted on hibernation exit
+ * by restoring from saved area.
+ */
+ if (hsotg->params.g_dma_desc &&
+ (dr->diepctl[i] & DXEPCTL_EPENA))
+ dr->diepdma[i] = hsotg->eps_in[i]->desc_list_dma;
+ dwc2_writel(dr->dtxfsiz[i], hsotg->regs + DPTXFSIZN(i));
+ dwc2_writel(dr->diepctl[i], hsotg->regs + DIEPCTL(i));
/* Restore OUT EPs */
- dwc2_writel(dr->doepctl[i], hsotg->regs + DOEPCTL(i));
dwc2_writel(dr->doeptsiz[i], hsotg->regs + DOEPTSIZ(i));
+ /* WA for enabled EPx's OUT in DDMA mode. On entering to
+ * hibernation wrong value read and saved from DOEPDMAx,
+ * as result BNA interrupt asserted on hibernation exit
+ * by restoring from saved area.
+ */
+ if (hsotg->params.g_dma_desc &&
+ (dr->doepctl[i] & DXEPCTL_EPENA))
+ dr->doepdma[i] = hsotg->eps_out[i]->desc_list_dma;
dwc2_writel(dr->doepdma[i], hsotg->regs + DOEPDMA(i));
+ dwc2_writel(dr->doepctl[i], hsotg->regs + DOEPCTL(i));
}
- /* Set the Power-On Programming done bit */
- dctl = dwc2_readl(hsotg->regs + DCTL);
- dctl |= DCTL_PWRONPRGDONE;
- dwc2_writel(dctl, hsotg->regs + DCTL);
-
return 0;
}
+
+/**
+ * dwc2_gadget_init_lpm - Configure the core to support LPM in device mode
+ *
+ * @hsotg: Programming view of DWC_otg controller
+ *
+ */
+void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg)
+{
+ u32 val;
+
+ if (!hsotg->params.lpm)
+ return;
+
+ val = GLPMCFG_LPMCAP | GLPMCFG_APPL1RES;
+ val |= hsotg->params.hird_threshold_en ? GLPMCFG_HIRD_THRES_EN : 0;
+ val |= hsotg->params.lpm_clock_gating ? GLPMCFG_ENBLSLPM : 0;
+ val |= hsotg->params.hird_threshold << GLPMCFG_HIRD_THRES_SHIFT;
+ val |= hsotg->params.besl ? GLPMCFG_ENBESL : 0;
+ dwc2_writel(val, hsotg->regs + GLPMCFG);
+ dev_dbg(hsotg->dev, "GLPMCFG=0x%08x\n", dwc2_readl(hsotg->regs
+ + GLPMCFG));
+}
+
+/**
+ * dwc2_gadget_enter_hibernation() - Put controller in Hibernation.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ *
+ * Return non-zero if failed to enter to hibernation.
+ */
+int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg)
+{
+ u32 gpwrdn;
+ int ret = 0;
+
+ /* Change to L2(suspend) state */
+ hsotg->lx_state = DWC2_L2;
+ dev_dbg(hsotg->dev, "Start of hibernation completed\n");
+ ret = dwc2_backup_global_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to backup global registers\n",
+ __func__);
+ return ret;
+ }
+ ret = dwc2_backup_device_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to backup device registers\n",
+ __func__);
+ return ret;
+ }
+
+ gpwrdn = GPWRDN_PWRDNRSTN;
+ gpwrdn |= GPWRDN_PMUACTV;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Set flag to indicate that we are in hibernation */
+ hsotg->hibernated = 1;
+
+ /* Enable interrupts from wake up logic */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn |= GPWRDN_PMUINTSEL;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Unmask device mode interrupts in GPWRDN */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn |= GPWRDN_RST_DET_MSK;
+ gpwrdn |= GPWRDN_LNSTSCHG_MSK;
+ gpwrdn |= GPWRDN_STS_CHGINT_MSK;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Enable Power Down Clamp */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn |= GPWRDN_PWRDNCLMP;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Switch off VDD */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn |= GPWRDN_PWRDNSWTCH;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Save gpwrdn register for further usage if stschng interrupt */
+ hsotg->gr_backup.gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ dev_dbg(hsotg->dev, "Hibernation completed\n");
+
+ return ret;
+}
+
+/**
+ * dwc2_gadget_exit_hibernation()
+ * This function is for exiting from Device mode hibernation by host initiated
+ * resume/reset and device initiated remote-wakeup.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ * @rem_wakeup: indicates whether resume is initiated by Device or Host.
+ * @param reset: indicates whether resume is initiated by Reset.
+ *
+ * Return non-zero if failed to exit from hibernation.
+ */
+int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
+ int rem_wakeup, int reset)
+{
+ u32 pcgcctl;
+ u32 gpwrdn;
+ u32 dctl;
+ int ret = 0;
+ struct dwc2_gregs_backup *gr;
+ struct dwc2_dregs_backup *dr;
+
+ gr = &hsotg->gr_backup;
+ dr = &hsotg->dr_backup;
+
+ if (!hsotg->hibernated) {
+ dev_dbg(hsotg->dev, "Already exited from Hibernation\n");
+ return 1;
+ }
+ dev_dbg(hsotg->dev,
+ "%s: called with rem_wakeup = %d reset = %d\n",
+ __func__, rem_wakeup, reset);
+
+ dwc2_hib_restore_common(hsotg, rem_wakeup, 0);
+
+ if (!reset) {
+ /* Clear all pending interupts */
+ dwc2_writel(0xffffffff, hsotg->regs + GINTSTS);
+ }
+
+ /* De-assert Restore */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn &= ~GPWRDN_RESTORE;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ if (!rem_wakeup) {
+ pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
+ pcgcctl &= ~PCGCTL_RSTPDWNMODULE;
+ dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ }
+
+ /* Restore GUSBCFG, DCFG and DCTL */
+ dwc2_writel(gr->gusbcfg, hsotg->regs + GUSBCFG);
+ dwc2_writel(dr->dcfg, hsotg->regs + DCFG);
+ dwc2_writel(dr->dctl, hsotg->regs + DCTL);
+
+ /* De-assert Wakeup Logic */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn &= ~GPWRDN_PMUACTV;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+
+ if (rem_wakeup) {
+ udelay(10);
+ /* Start Remote Wakeup Signaling */
+ dwc2_writel(dr->dctl | DCTL_RMTWKUPSIG, hsotg->regs + DCTL);
+ } else {
+ udelay(50);
+ /* Set Device programming done bit */
+ dctl = dwc2_readl(hsotg->regs + DCTL);
+ dctl |= DCTL_PWRONPRGDONE;
+ dwc2_writel(dctl, hsotg->regs + DCTL);
+ }
+ /* Wait for interrupts which must be cleared */
+ mdelay(2);
+ /* Clear all pending interupts */
+ dwc2_writel(0xffffffff, hsotg->regs + GINTSTS);
+
+ /* Restore global registers */
+ ret = dwc2_restore_global_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to restore registers\n",
+ __func__);
+ return ret;
+ }
+
+ /* Restore device registers */
+ ret = dwc2_restore_device_registers(hsotg, rem_wakeup);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to restore device registers\n",
+ __func__);
+ return ret;
+ }
+
+ if (rem_wakeup) {
+ mdelay(10);
+ dctl = dwc2_readl(hsotg->regs + DCTL);
+ dctl &= ~DCTL_RMTWKUPSIG;
+ dwc2_writel(dctl, hsotg->regs + DCTL);
+ }
+
+ hsotg->hibernated = 0;
+ hsotg->lx_state = DWC2_L0;
+ dev_dbg(hsotg->dev, "Hibernation recovery completes here\n");
+
+ return ret;
+}
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index a5d72fcd1603..190f95964000 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -91,6 +91,9 @@ static void dwc2_enable_common_interrupts(struct dwc2_hsotg *hsotg)
intmsk |= GINTSTS_WKUPINT | GINTSTS_USBSUSP |
GINTSTS_SESSREQINT;
+ if (dwc2_is_device_mode(hsotg) && hsotg->params.lpm)
+ intmsk |= GINTSTS_LPMTRANRCVD;
+
dwc2_writel(intmsk, hsotg->regs + GINTMSK);
}
@@ -138,7 +141,7 @@ static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
/* Reset after a PHY select */
- retval = dwc2_core_reset_and_force_dr_mode(hsotg);
+ retval = dwc2_core_reset(hsotg, false);
if (retval) {
dev_err(hsotg->dev,
@@ -236,7 +239,7 @@ static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
/* Reset after setting the PHY parameters */
- retval = dwc2_core_reset_and_force_dr_mode(hsotg);
+ retval = dwc2_core_reset(hsotg, false);
if (retval) {
dev_err(hsotg->dev,
"%s: Reset failed, aborting", __func__);
@@ -308,22 +311,10 @@ static int dwc2_gahbcfg_init(struct dwc2_hsotg *hsotg)
break;
}
- dev_dbg(hsotg->dev, "host_dma:%d dma_desc_enable:%d\n",
- hsotg->params.host_dma,
- hsotg->params.dma_desc_enable);
-
- if (hsotg->params.host_dma) {
- if (hsotg->params.dma_desc_enable)
- dev_dbg(hsotg->dev, "Using Descriptor DMA mode\n");
- else
- dev_dbg(hsotg->dev, "Using Buffer DMA mode\n");
- } else {
- dev_dbg(hsotg->dev, "Using Slave mode\n");
- hsotg->params.dma_desc_enable = false;
- }
-
if (hsotg->params.host_dma)
ahbcfg |= GAHBCFG_DMA_EN;
+ else
+ hsotg->params.dma_desc_enable = false;
dwc2_writel(ahbcfg, hsotg->regs + GAHBCFG);
@@ -365,6 +356,23 @@ static void dwc2_gusbcfg_init(struct dwc2_hsotg *hsotg)
dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
}
+static int dwc2_vbus_supply_init(struct dwc2_hsotg *hsotg)
+{
+ hsotg->vbus_supply = devm_regulator_get_optional(hsotg->dev, "vbus");
+ if (IS_ERR(hsotg->vbus_supply))
+ return 0;
+
+ return regulator_enable(hsotg->vbus_supply);
+}
+
+static int dwc2_vbus_supply_exit(struct dwc2_hsotg *hsotg)
+{
+ if (hsotg->vbus_supply)
+ return regulator_disable(hsotg->vbus_supply);
+
+ return 0;
+}
+
/**
* dwc2_enable_host_interrupts() - Enables the Host mode interrupts
*
@@ -989,6 +997,24 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan,
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "%s()\n", __func__);
+
+ /*
+ * In buffer DMA or external DMA mode channel can't be halted
+ * for non-split periodic channels. At the end of the next
+ * uframe/frame (in the worst case), the core generates a channel
+ * halted and disables the channel automatically.
+ */
+ if ((hsotg->params.g_dma && !hsotg->params.g_dma_desc) ||
+ hsotg->hw_params.arch == GHWCFG2_EXT_DMA_ARCH) {
+ if (!chan->do_split &&
+ (chan->ep_type == USB_ENDPOINT_XFER_ISOC ||
+ chan->ep_type == USB_ENDPOINT_XFER_INT)) {
+ dev_err(hsotg->dev, "%s() Channel can't be halted\n",
+ __func__);
+ return;
+ }
+ }
+
if (halt_status == DWC2_HC_XFER_NO_HALT_STATUS)
dev_err(hsotg->dev, "!!! halt_status = %d !!!\n", halt_status);
@@ -2232,7 +2258,7 @@ static int dwc2_hcd_endpoint_reset(struct dwc2_hsotg *hsotg,
* @hsotg: Programming view of the DWC_otg controller
* @initial_setup: If true then this is the first init for this instance.
*/
-static int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup)
+int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup)
{
u32 usbcfg, otgctl;
int retval;
@@ -2261,7 +2287,7 @@ static int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup)
* needed to in order to properly detect various parameters).
*/
if (!initial_setup) {
- retval = dwc2_core_reset_and_force_dr_mode(hsotg);
+ retval = dwc2_core_reset(hsotg, false);
if (retval) {
dev_err(hsotg->dev, "%s(): Reset failed, aborting\n",
__func__);
@@ -2322,10 +2348,22 @@ static int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup)
*/
static void dwc2_core_host_init(struct dwc2_hsotg *hsotg)
{
- u32 hcfg, hfir, otgctl;
+ u32 hcfg, hfir, otgctl, usbcfg;
dev_dbg(hsotg->dev, "%s(%p)\n", __func__, hsotg);
+ /* Set HS/FS Timeout Calibration to 7 (max available value).
+ * The number of PHY clocks that the application programs in
+ * this field is added to the high/full speed interpacket timeout
+ * duration in the core to account for any additional delays
+ * introduced by the PHY. This can be required, because the delay
+ * introduced by the PHY in generating the linestate condition
+ * can vary from one PHY to another.
+ */
+ usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
+ usbcfg |= GUSBCFG_TOUTCAL(7);
+ dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
+
/* Restart the Phy Clock */
dwc2_writel(0, hsotg->regs + PCGCTL);
@@ -2403,27 +2441,24 @@ static void dwc2_core_host_init(struct dwc2_hsotg *hsotg)
/* Halt all channels to put them into a known state */
for (i = 0; i < num_channels; i++) {
- int count = 0;
-
hcchar = dwc2_readl(hsotg->regs + HCCHAR(i));
hcchar |= HCCHAR_CHENA | HCCHAR_CHDIS;
hcchar &= ~HCCHAR_EPDIR;
dwc2_writel(hcchar, hsotg->regs + HCCHAR(i));
dev_dbg(hsotg->dev, "%s: Halt channel %d\n",
__func__, i);
- do {
- hcchar = dwc2_readl(hsotg->regs + HCCHAR(i));
- if (++count > 1000) {
- dev_err(hsotg->dev,
- "Unable to clear enable on channel %d\n",
- i);
- break;
- }
- udelay(1);
- } while (hcchar & HCCHAR_CHENA);
+
+ if (dwc2_hsotg_wait_bit_clear(hsotg, HCCHAR(i),
+ HCCHAR_CHENA, 1000)) {
+ dev_warn(hsotg->dev, "Unable to clear enable on channel %d\n",
+ i);
+ }
}
}
+ /* Enable ACG feature in host mode, if supported */
+ dwc2_enable_acg(hsotg);
+
/* Turn on the vbus power */
dev_dbg(hsotg->dev, "Init: Port Power? op_state=%d\n", hsotg->op_state);
if (hsotg->op_state == OTG_STATE_A_HOST) {
@@ -3257,6 +3292,7 @@ static void dwc2_conn_id_status_change(struct work_struct *work)
/* B-Device connector (Device Mode) */
if (gotgctl & GOTGCTL_CONID_B) {
+ dwc2_vbus_supply_exit(hsotg);
/* Wait for switch to device mode */
dev_dbg(hsotg->dev, "connId B\n");
if (hsotg->bus_suspended) {
@@ -3290,6 +3326,8 @@ static void dwc2_conn_id_status_change(struct work_struct *work)
spin_lock_irqsave(&hsotg->lock, flags);
dwc2_hsotg_core_init_disconnected(hsotg, false);
spin_unlock_irqrestore(&hsotg->lock, flags);
+ /* Enable ACG feature in device mode,if supported */
+ dwc2_enable_acg(hsotg);
dwc2_hsotg_core_connect(hsotg);
} else {
host:
@@ -3377,10 +3415,10 @@ static void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex)
hsotg->bus_suspended = true;
/*
- * If hibernation is supported, Phy clock will be suspended
+ * If power_down is supported, Phy clock will be suspended
* after registers are backuped.
*/
- if (!hsotg->params.hibernation) {
+ if (!hsotg->params.power_down) {
/* Suspend the Phy Clock */
pcgctl = dwc2_readl(hsotg->regs + PCGCTL);
pcgctl |= PCGCTL_STOPPCLK;
@@ -3412,10 +3450,10 @@ static void dwc2_port_resume(struct dwc2_hsotg *hsotg)
spin_lock_irqsave(&hsotg->lock, flags);
/*
- * If hibernation is supported, Phy clock is already resumed
+ * If power_down is supported, Phy clock is already resumed
* after registers restore.
*/
- if (!hsotg->params.hibernation) {
+ if (!hsotg->params.power_down) {
pcgctl = dwc2_readl(hsotg->regs + PCGCTL);
pcgctl &= ~PCGCTL_STOPPCLK;
dwc2_writel(pcgctl, hsotg->regs + PCGCTL);
@@ -3486,8 +3524,12 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
dev_dbg(hsotg->dev,
"ClearPortFeature USB_PORT_FEAT_SUSPEND\n");
- if (hsotg->bus_suspended)
- dwc2_port_resume(hsotg);
+ if (hsotg->bus_suspended) {
+ if (hsotg->hibernated)
+ dwc2_exit_hibernation(hsotg, 0, 0, 1);
+ else
+ dwc2_port_resume(hsotg);
+ }
break;
case USB_PORT_FEAT_POWER:
@@ -3695,7 +3737,10 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
"SetPortFeature - USB_PORT_FEAT_SUSPEND\n");
if (windex != hsotg->otg_port)
goto error;
- dwc2_port_suspend(hsotg, windex);
+ if (hsotg->params.power_down == 2)
+ dwc2_enter_hibernation(hsotg, 1);
+ else
+ dwc2_port_suspend(hsotg, windex);
break;
case USB_PORT_FEAT_POWER:
@@ -3707,6 +3752,9 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
break;
case USB_PORT_FEAT_RESET:
+ if (hsotg->params.power_down == 2 &&
+ hsotg->hibernated)
+ dwc2_exit_hibernation(hsotg, 0, 1, 1);
hprt0 = dwc2_read_hprt0(hsotg);
dev_dbg(hsotg->dev,
"SetPortFeature - USB_PORT_FEAT_RESET\n");
@@ -4002,7 +4050,6 @@ void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg)
(p_tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT);
dev_dbg(hsotg->dev, " P Tx FIFO Space Avail: %d\n",
(p_tx_status & TXSTS_FSPCAVAIL_MASK) >> TXSTS_FSPCAVAIL_SHIFT);
- dwc2_hcd_dump_frrem(hsotg);
dwc2_dump_global_registers(hsotg);
dwc2_dump_host_registers(hsotg);
dev_dbg(hsotg->dev,
@@ -4011,75 +4058,6 @@ void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg)
#endif
}
-/*
- * NOTE: This function will be removed once the peripheral controller code
- * is integrated and the driver is stable
- */
-void dwc2_hcd_dump_frrem(struct dwc2_hsotg *hsotg)
-{
-#ifdef DWC2_DUMP_FRREM
- dev_dbg(hsotg->dev, "Frame remaining at SOF:\n");
- dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n",
- hsotg->frrem_samples, hsotg->frrem_accum,
- hsotg->frrem_samples > 0 ?
- hsotg->frrem_accum / hsotg->frrem_samples : 0);
- dev_dbg(hsotg->dev, "\n");
- dev_dbg(hsotg->dev, "Frame remaining at start_transfer (uframe 7):\n");
- dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n",
- hsotg->hfnum_7_samples,
- hsotg->hfnum_7_frrem_accum,
- hsotg->hfnum_7_samples > 0 ?
- hsotg->hfnum_7_frrem_accum / hsotg->hfnum_7_samples : 0);
- dev_dbg(hsotg->dev, "Frame remaining at start_transfer (uframe 0):\n");
- dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n",
- hsotg->hfnum_0_samples,
- hsotg->hfnum_0_frrem_accum,
- hsotg->hfnum_0_samples > 0 ?
- hsotg->hfnum_0_frrem_accum / hsotg->hfnum_0_samples : 0);
- dev_dbg(hsotg->dev, "Frame remaining at start_transfer (uframe 1-6):\n");
- dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n",
- hsotg->hfnum_other_samples,
- hsotg->hfnum_other_frrem_accum,
- hsotg->hfnum_other_samples > 0 ?
- hsotg->hfnum_other_frrem_accum / hsotg->hfnum_other_samples :
- 0);
- dev_dbg(hsotg->dev, "\n");
- dev_dbg(hsotg->dev, "Frame remaining at sample point A (uframe 7):\n");
- dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n",
- hsotg->hfnum_7_samples_a, hsotg->hfnum_7_frrem_accum_a,
- hsotg->hfnum_7_samples_a > 0 ?
- hsotg->hfnum_7_frrem_accum_a / hsotg->hfnum_7_samples_a : 0);
- dev_dbg(hsotg->dev, "Frame remaining at sample point A (uframe 0):\n");
- dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n",
- hsotg->hfnum_0_samples_a, hsotg->hfnum_0_frrem_accum_a,
- hsotg->hfnum_0_samples_a > 0 ?
- hsotg->hfnum_0_frrem_accum_a / hsotg->hfnum_0_samples_a : 0);
- dev_dbg(hsotg->dev, "Frame remaining at sample point A (uframe 1-6):\n");
- dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n",
- hsotg->hfnum_other_samples_a, hsotg->hfnum_other_frrem_accum_a,
- hsotg->hfnum_other_samples_a > 0 ?
- hsotg->hfnum_other_frrem_accum_a / hsotg->hfnum_other_samples_a
- : 0);
- dev_dbg(hsotg->dev, "\n");
- dev_dbg(hsotg->dev, "Frame remaining at sample point B (uframe 7):\n");
- dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n",
- hsotg->hfnum_7_samples_b, hsotg->hfnum_7_frrem_accum_b,
- hsotg->hfnum_7_samples_b > 0 ?
- hsotg->hfnum_7_frrem_accum_b / hsotg->hfnum_7_samples_b : 0);
- dev_dbg(hsotg->dev, "Frame remaining at sample point B (uframe 0):\n");
- dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n",
- hsotg->hfnum_0_samples_b, hsotg->hfnum_0_frrem_accum_b,
- (hsotg->hfnum_0_samples_b > 0) ?
- hsotg->hfnum_0_frrem_accum_b / hsotg->hfnum_0_samples_b : 0);
- dev_dbg(hsotg->dev, "Frame remaining at sample point B (uframe 1-6):\n");
- dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n",
- hsotg->hfnum_other_samples_b, hsotg->hfnum_other_frrem_accum_b,
- (hsotg->hfnum_other_samples_b > 0) ?
- hsotg->hfnum_other_frrem_accum_b / hsotg->hfnum_other_samples_b
- : 0);
-#endif
-}
-
struct wrapper_priv_data {
struct dwc2_hsotg *hsotg;
};
@@ -4363,6 +4341,9 @@ static int _dwc2_hcd_start(struct usb_hcd *hcd)
}
spin_unlock_irqrestore(&hsotg->lock, flags);
+
+ dwc2_vbus_supply_init(hsotg);
+
return 0;
}
@@ -4390,6 +4371,8 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
spin_unlock_irqrestore(&hsotg->lock, flags);
+ dwc2_vbus_supply_exit(hsotg);
+
usleep_range(1000, 3000);
}
@@ -4414,7 +4397,7 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
if (hsotg->op_state == OTG_STATE_B_PERIPHERAL)
goto unlock;
- if (!hsotg->params.hibernation)
+ if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_PARTIAL)
goto skip_power_saving;
/*
@@ -4426,14 +4409,15 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
hprt0 |= HPRT0_SUSP;
hprt0 &= ~HPRT0_PWR;
dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_vbus_supply_exit(hsotg);
}
- /* Enter hibernation */
- ret = dwc2_enter_hibernation(hsotg);
+ /* Enter partial_power_down */
+ ret = dwc2_enter_partial_power_down(hsotg);
if (ret) {
if (ret != -ENOTSUPP)
dev_err(hsotg->dev,
- "enter hibernation failed\n");
+ "enter partial_power_down failed\n");
goto skip_power_saving;
}
@@ -4444,7 +4428,7 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
spin_lock_irqsave(&hsotg->lock, flags);
}
- /* After entering hibernation, hardware is no more accessible */
+ /* After entering partial_power_down, hardware is no more accessible */
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
skip_power_saving:
@@ -4469,7 +4453,7 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd)
if (hsotg->lx_state != DWC2_L2)
goto unlock;
- if (!hsotg->params.hibernation) {
+ if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_PARTIAL) {
hsotg->lx_state = DWC2_L0;
goto unlock;
}
@@ -4491,10 +4475,10 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd)
spin_lock_irqsave(&hsotg->lock, flags);
}
- /* Exit hibernation */
- ret = dwc2_exit_hibernation(hsotg, true);
+ /* Exit partial_power_down */
+ ret = dwc2_exit_partial_power_down(hsotg, true);
if (ret && (ret != -ENOTSUPP))
- dev_err(hsotg->dev, "exit hibernation failed\n");
+ dev_err(hsotg->dev, "exit partial_power_down failed\n");
hsotg->lx_state = DWC2_L0;
@@ -4506,6 +4490,8 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd)
spin_unlock_irqrestore(&hsotg->lock, flags);
dwc2_port_resume(hsotg);
} else {
+ dwc2_vbus_supply_init(hsotg);
+
/* Wait for controller to correctly update D+/D- level */
usleep_range(3000, 5000);
@@ -5368,6 +5354,7 @@ int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg)
hr->hprt0 = dwc2_read_hprt0(hsotg);
hr->hfir = dwc2_readl(hsotg->regs + HFIR);
+ hr->hptxfsiz = dwc2_readl(hsotg->regs + HPTXFSIZ);
hr->valid = true;
return 0;
@@ -5404,7 +5391,231 @@ int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
dwc2_writel(hr->hprt0, hsotg->regs + HPRT0);
dwc2_writel(hr->hfir, hsotg->regs + HFIR);
+ dwc2_writel(hr->hptxfsiz, hsotg->regs + HPTXFSIZ);
hsotg->frame_number = 0;
return 0;
}
+
+/**
+ * dwc2_host_enter_hibernation() - Put controller in Hibernation.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ */
+int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg)
+{
+ unsigned long flags;
+ int ret = 0;
+ u32 hprt0;
+ u32 pcgcctl;
+ u32 gusbcfg;
+ u32 gpwrdn;
+
+ dev_dbg(hsotg->dev, "Preparing host for hibernation\n");
+ ret = dwc2_backup_global_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to backup global registers\n",
+ __func__);
+ return ret;
+ }
+ ret = dwc2_backup_host_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to backup host registers\n",
+ __func__);
+ return ret;
+ }
+
+ /* Enter USB Suspend Mode */
+ hprt0 = dwc2_readl(hsotg->regs + HPRT0);
+ hprt0 |= HPRT0_SUSP;
+ hprt0 &= ~HPRT0_ENA;
+ dwc2_writel(hprt0, hsotg->regs + HPRT0);
+
+ /* Wait for the HPRT0.PrtSusp register field to be set */
+ if (dwc2_hsotg_wait_bit_set(hsotg, HPRT0, HPRT0_SUSP, 300))
+ dev_warn(hsotg->dev, "Suspend wasn't generated\n");
+
+ /*
+ * We need to disable interrupts to prevent servicing of any IRQ
+ * during going to hibernation
+ */
+ spin_lock_irqsave(&hsotg->lock, flags);
+ hsotg->lx_state = DWC2_L2;
+
+ gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
+ if (gusbcfg & GUSBCFG_ULPI_UTMI_SEL) {
+ /* ULPI interface */
+ /* Suspend the Phy Clock */
+ pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
+ pcgcctl |= PCGCTL_STOPPCLK;
+ dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ udelay(10);
+
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn |= GPWRDN_PMUACTV;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+ } else {
+ /* UTMI+ Interface */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn |= GPWRDN_PMUACTV;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
+ pcgcctl |= PCGCTL_STOPPCLK;
+ dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ udelay(10);
+ }
+
+ /* Enable interrupts from wake up logic */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn |= GPWRDN_PMUINTSEL;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Unmask host mode interrupts in GPWRDN */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn |= GPWRDN_DISCONN_DET_MSK;
+ gpwrdn |= GPWRDN_LNSTSCHG_MSK;
+ gpwrdn |= GPWRDN_STS_CHGINT_MSK;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Enable Power Down Clamp */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn |= GPWRDN_PWRDNCLMP;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Switch off VDD */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn |= GPWRDN_PWRDNSWTCH;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+
+ hsotg->hibernated = 1;
+ hsotg->bus_suspended = 1;
+ dev_dbg(hsotg->dev, "Host hibernation completed\n");
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+ return ret;
+}
+
+/*
+ * dwc2_host_exit_hibernation()
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ * @rem_wakeup: indicates whether resume is initiated by Device or Host.
+ * @param reset: indicates whether resume is initiated by Reset.
+ *
+ * Return: non-zero if failed to enter to hibernation.
+ *
+ * This function is for exiting from Host mode hibernation by
+ * Host Initiated Resume/Reset and Device Initiated Remote-Wakeup.
+ */
+int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup,
+ int reset)
+{
+ u32 gpwrdn;
+ u32 hprt0;
+ int ret = 0;
+ struct dwc2_gregs_backup *gr;
+ struct dwc2_hregs_backup *hr;
+
+ gr = &hsotg->gr_backup;
+ hr = &hsotg->hr_backup;
+
+ dev_dbg(hsotg->dev,
+ "%s: called with rem_wakeup = %d reset = %d\n",
+ __func__, rem_wakeup, reset);
+
+ dwc2_hib_restore_common(hsotg, rem_wakeup, 1);
+ hsotg->hibernated = 0;
+
+ /*
+ * This step is not described in functional spec but if not wait for
+ * this delay, mismatch interrupts occurred because just after restore
+ * core is in Device mode(gintsts.curmode == 0)
+ */
+ mdelay(100);
+
+ /* Clear all pending interupts */
+ dwc2_writel(0xffffffff, hsotg->regs + GINTSTS);
+
+ /* De-assert Restore */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn &= ~GPWRDN_RESTORE;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ /* Restore GUSBCFG, HCFG */
+ dwc2_writel(gr->gusbcfg, hsotg->regs + GUSBCFG);
+ dwc2_writel(hr->hcfg, hsotg->regs + HCFG);
+
+ /* De-assert Wakeup Logic */
+ gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn &= ~GPWRDN_PMUACTV;
+ dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ udelay(10);
+
+ hprt0 = hr->hprt0;
+ hprt0 |= HPRT0_PWR;
+ hprt0 &= ~HPRT0_ENA;
+ hprt0 &= ~HPRT0_SUSP;
+ dwc2_writel(hprt0, hsotg->regs + HPRT0);
+
+ hprt0 = hr->hprt0;
+ hprt0 |= HPRT0_PWR;
+ hprt0 &= ~HPRT0_ENA;
+ hprt0 &= ~HPRT0_SUSP;
+
+ if (reset) {
+ hprt0 |= HPRT0_RST;
+ dwc2_writel(hprt0, hsotg->regs + HPRT0);
+
+ /* Wait for Resume time and then program HPRT again */
+ mdelay(60);
+ hprt0 &= ~HPRT0_RST;
+ dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ } else {
+ hprt0 |= HPRT0_RES;
+ dwc2_writel(hprt0, hsotg->regs + HPRT0);
+
+ /* Wait for Resume time and then program HPRT again */
+ mdelay(100);
+ hprt0 &= ~HPRT0_RES;
+ dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ }
+ /* Clear all interrupt status */
+ hprt0 = dwc2_readl(hsotg->regs + HPRT0);
+ hprt0 |= HPRT0_CONNDET;
+ hprt0 |= HPRT0_ENACHG;
+ hprt0 &= ~HPRT0_ENA;
+ dwc2_writel(hprt0, hsotg->regs + HPRT0);
+
+ hprt0 = dwc2_readl(hsotg->regs + HPRT0);
+
+ /* Clear all pending interupts */
+ dwc2_writel(0xffffffff, hsotg->regs + GINTSTS);
+
+ /* Restore global registers */
+ ret = dwc2_restore_global_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to restore registers\n",
+ __func__);
+ return ret;
+ }
+
+ /* Restore host registers */
+ ret = dwc2_restore_host_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to restore host registers\n",
+ __func__);
+ return ret;
+ }
+
+ hsotg->hibernated = 0;
+ hsotg->bus_suspended = 0;
+ hsotg->lx_state = DWC2_L0;
+ dev_dbg(hsotg->dev, "Host hibernation restore complete\n");
+ return ret;
+}
diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
index ad60e46e66e1..96a9da5fb202 100644
--- a/drivers/usb/dwc2/hcd.h
+++ b/drivers/usb/dwc2/hcd.h
@@ -783,19 +783,6 @@ int dwc2_hcd_is_b_host(struct dwc2_hsotg *hsotg);
*/
void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg);
-/**
- * dwc2_hcd_dump_frrem() - Dumps the average frame remaining at SOF
- *
- * @hsotg: The DWC2 HCD
- *
- * This can be used to determine average interrupt latency. Frame remaining is
- * also shown for start transfer and two additional sample points.
- *
- * NOTE: This function will be removed once the peripheral controller code
- * is integrated and the driver is stable
- */
-void dwc2_hcd_dump_frrem(struct dwc2_hsotg *hsotg);
-
/* URB interface */
/* Transfer flags */
@@ -813,47 +800,4 @@ int dwc2_host_get_speed(struct dwc2_hsotg *hsotg, void *context);
void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
int status);
-#ifdef DEBUG
-/*
- * Macro to sample the remaining PHY clocks left in the current frame. This
- * may be used during debugging to determine the average time it takes to
- * execute sections of code. There are two possible sample points, "a" and
- * "b", so the _letter_ argument must be one of these values.
- *
- * To dump the average sample times, read the "hcd_frrem" sysfs attribute. For
- * example, "cat /sys/devices/lm0/hcd_frrem".
- */
-#define dwc2_sample_frrem(_hcd_, _qh_, _letter_) \
-do { \
- struct hfnum_data _hfnum_; \
- struct dwc2_qtd *_qtd_; \
- \
- _qtd_ = list_entry((_qh_)->qtd_list.next, struct dwc2_qtd, \
- qtd_list_entry); \
- if (usb_pipeint(_qtd_->urb->pipe) && \
- (_qh_)->start_active_frame != 0 && !_qtd_->complete_split) { \
- _hfnum_.d32 = dwc2_readl((_hcd_)->regs + HFNUM); \
- switch (_hfnum_.b.frnum & 0x7) { \
- case 7: \
- (_hcd_)->hfnum_7_samples_##_letter_++; \
- (_hcd_)->hfnum_7_frrem_accum_##_letter_ += \
- _hfnum_.b.frrem; \
- break; \
- case 0: \
- (_hcd_)->hfnum_0_samples_##_letter_++; \
- (_hcd_)->hfnum_0_frrem_accum_##_letter_ += \
- _hfnum_.b.frrem; \
- break; \
- default: \
- (_hcd_)->hfnum_other_samples_##_letter_++; \
- (_hcd_)->hfnum_other_frrem_accum_##_letter_ += \
- _hfnum_.b.frrem; \
- break; \
- } \
- } \
-} while (0)
-#else
-#define dwc2_sample_frrem(_hcd_, _qh_, _letter_) do {} while (0)
-#endif
-
#endif /* __DWC2_HCD_H__ */
diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h
index 2c906d8ee465..38391e48351f 100644
--- a/drivers/usb/dwc2/hw.h
+++ b/drivers/usb/dwc2/hw.h
@@ -231,6 +231,7 @@
#define GUID HSOTG_REG(0x003c)
#define GSNPSID HSOTG_REG(0x0040)
#define GHWCFG1 HSOTG_REG(0x0044)
+#define GSNPSID_ID_MASK GENMASK(31, 16)
#define GHWCFG2 HSOTG_REG(0x0048)
#define GHWCFG2_OTG_ENABLE_IC_USB BIT(31)
@@ -309,6 +310,7 @@
#define GHWCFG4_NUM_DEV_MODE_CTRL_EP_SHIFT 16
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK (0x3 << 14)
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT 14
+#define GHWCFG4_ACG_SUPPORTED BIT(12)
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_8 0
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_16 1
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_8_OR_16 2
@@ -320,28 +322,30 @@
#define GHWCFG4_NUM_DEV_PERIO_IN_EP_SHIFT 0
#define GLPMCFG HSOTG_REG(0x0054)
-#define GLPMCFG_INV_SEL_HSIC BIT(31)
-#define GLPMCFG_HSIC_CONNECT BIT(30)
-#define GLPMCFG_RETRY_COUNT_STS_MASK (0x7 << 25)
-#define GLPMCFG_RETRY_COUNT_STS_SHIFT 25
-#define GLPMCFG_SEND_LPM BIT(24)
-#define GLPMCFG_RETRY_COUNT_MASK (0x7 << 21)
-#define GLPMCFG_RETRY_COUNT_SHIFT 21
-#define GLPMCFG_LPM_CHAN_INDEX_MASK (0xf << 17)
-#define GLPMCFG_LPM_CHAN_INDEX_SHIFT 17
-#define GLPMCFG_SLEEP_STATE_RESUMEOK BIT(16)
-#define GLPMCFG_PRT_SLEEP_STS BIT(15)
-#define GLPMCFG_LPM_RESP_MASK (0x3 << 13)
-#define GLPMCFG_LPM_RESP_SHIFT 13
+#define GLPMCFG_INVSELHSIC BIT(31)
+#define GLPMCFG_HSICCON BIT(30)
+#define GLPMCFG_RSTRSLPSTS BIT(29)
+#define GLPMCFG_ENBESL BIT(28)
+#define GLPMCFG_LPM_RETRYCNT_STS_MASK (0x7 << 25)
+#define GLPMCFG_LPM_RETRYCNT_STS_SHIFT 25
+#define GLPMCFG_SNDLPM BIT(24)
+#define GLPMCFG_RETRY_CNT_MASK (0x7 << 21)
+#define GLPMCFG_RETRY_CNT_SHIFT 21
+#define GLPMCFG_LPM_CHNL_INDX_MASK (0xf << 17)
+#define GLPMCFG_LPM_CHNL_INDX_SHIFT 17
+#define GLPMCFG_L1RESUMEOK BIT(16)
+#define GLPMCFG_SLPSTS BIT(15)
+#define GLPMCFG_COREL1RES_MASK (0x3 << 13)
+#define GLPMCFG_COREL1RES_SHIFT 13
#define GLPMCFG_HIRD_THRES_MASK (0x1f << 8)
#define GLPMCFG_HIRD_THRES_SHIFT 8
-#define GLPMCFG_HIRD_THRES_EN (0x10 << 8)
-#define GLPMCFG_EN_UTMI_SLEEP BIT(7)
-#define GLPMCFG_REM_WKUP_EN BIT(6)
+#define GLPMCFG_HIRD_THRES_EN (0x10 << 8)
+#define GLPMCFG_ENBLSLPM BIT(7)
+#define GLPMCFG_BREMOTEWAKE BIT(6)
#define GLPMCFG_HIRD_MASK (0xf << 2)
#define GLPMCFG_HIRD_SHIFT 2
-#define GLPMCFG_APPL_RESP BIT(1)
-#define GLPMCFG_LPM_CAP_EN BIT(0)
+#define GLPMCFG_APPL1RES BIT(1)
+#define GLPMCFG_LPMCAP BIT(0)
#define GPWRDN HSOTG_REG(0x0058)
#define GPWRDN_MULT_VAL_ID_BC_MASK (0x1f << 24)
@@ -644,6 +648,10 @@
#define PCGCTL_GATEHCLK BIT(1)
#define PCGCTL_STOPPCLK BIT(0)
+#define PCGCCTL1 HSOTG_REG(0xe04)
+#define PCGCCTL1_TIMER (0x3 << 1)
+#define PCGCCTL1_GATEEN BIT(0)
+
#define EPFIFO(_a) HSOTG_REG(0x1000 + ((_a) * 0x1000))
/* Host Mode Registers */
diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c
index c4a47496d2fb..f03e41879224 100644
--- a/drivers/usb/dwc2/params.c
+++ b/drivers/usb/dwc2/params.c
@@ -252,6 +252,20 @@ static void dwc2_set_param_tx_fifo_sizes(struct dwc2_hsotg *hsotg)
p->g_tx_fifo_size[i] = depth_average;
}
+static void dwc2_set_param_power_down(struct dwc2_hsotg *hsotg)
+{
+ int val;
+
+ if (hsotg->hw_params.hibernation)
+ val = 2;
+ else if (hsotg->hw_params.power_optimized)
+ val = 1;
+ else
+ val = 0;
+
+ hsotg->params.power_down = val;
+}
+
/**
* dwc2_set_default_params() - Set all core parameters to their
* auto-detected default values.
@@ -266,21 +280,27 @@ static void dwc2_set_default_params(struct dwc2_hsotg *hsotg)
dwc2_set_param_phy_type(hsotg);
dwc2_set_param_speed(hsotg);
dwc2_set_param_phy_utmi_width(hsotg);
+ dwc2_set_param_power_down(hsotg);
p->phy_ulpi_ddr = false;
p->phy_ulpi_ext_vbus = false;
p->enable_dynamic_fifo = hw->enable_dynamic_fifo;
p->en_multiple_tx_fifo = hw->en_multiple_tx_fifo;
p->i2c_enable = hw->i2c_enable;
+ p->acg_enable = hw->acg_enable;
p->ulpi_fs_ls = false;
p->ts_dline = false;
p->reload_ctl = (hw->snpsid >= DWC2_CORE_REV_2_92a);
p->uframe_sched = true;
p->external_id_pin_ctl = false;
- p->hibernation = false;
+ p->lpm = true;
+ p->lpm_clock_gating = true;
+ p->besl = true;
+ p->hird_threshold_en = true;
+ p->hird_threshold = 4;
p->max_packet_count = hw->max_packet_count;
p->max_transfer_size = hw->max_transfer_size;
- p->ahbcfg = GAHBCFG_HBSTLEN_INCR4 << GAHBCFG_HBSTLEN_SHIFT;
+ p->ahbcfg = GAHBCFG_HBSTLEN_INCR << GAHBCFG_HBSTLEN_SHIFT;
if ((hsotg->dr_mode == USB_DR_MODE_HOST) ||
(hsotg->dr_mode == USB_DR_MODE_OTG)) {
@@ -463,6 +483,38 @@ static void dwc2_check_param_phy_utmi_width(struct dwc2_hsotg *hsotg)
dwc2_set_param_phy_utmi_width(hsotg);
}
+static void dwc2_check_param_power_down(struct dwc2_hsotg *hsotg)
+{
+ int param = hsotg->params.power_down;
+
+ switch (param) {
+ case DWC2_POWER_DOWN_PARAM_NONE:
+ break;
+ case DWC2_POWER_DOWN_PARAM_PARTIAL:
+ if (hsotg->hw_params.power_optimized)
+ break;
+ dev_dbg(hsotg->dev,
+ "Partial power down isn't supported by HW\n");
+ param = DWC2_POWER_DOWN_PARAM_NONE;
+ break;
+ case DWC2_POWER_DOWN_PARAM_HIBERNATION:
+ if (hsotg->hw_params.hibernation)
+ break;
+ dev_dbg(hsotg->dev,
+ "Hibernation isn't supported by HW\n");
+ param = DWC2_POWER_DOWN_PARAM_NONE;
+ break;
+ default:
+ dev_err(hsotg->dev,
+ "%s: Invalid parameter power_down=%d\n",
+ __func__, param);
+ param = DWC2_POWER_DOWN_PARAM_NONE;
+ break;
+ }
+
+ hsotg->params.power_down = param;
+}
+
static void dwc2_check_param_tx_fifo_sizes(struct dwc2_hsotg *hsotg)
{
int fifo_count;
@@ -523,10 +575,19 @@ static void dwc2_check_params(struct dwc2_hsotg *hsotg)
dwc2_check_param_phy_type(hsotg);
dwc2_check_param_speed(hsotg);
dwc2_check_param_phy_utmi_width(hsotg);
+ dwc2_check_param_power_down(hsotg);
CHECK_BOOL(enable_dynamic_fifo, hw->enable_dynamic_fifo);
CHECK_BOOL(en_multiple_tx_fifo, hw->en_multiple_tx_fifo);
CHECK_BOOL(i2c_enable, hw->i2c_enable);
+ CHECK_BOOL(acg_enable, hw->acg_enable);
CHECK_BOOL(reload_ctl, (hsotg->hw_params.snpsid > DWC2_CORE_REV_2_92a));
+ CHECK_BOOL(lpm, (hsotg->hw_params.snpsid >= DWC2_CORE_REV_2_80a));
+ CHECK_BOOL(lpm, hw->lpm_mode);
+ CHECK_BOOL(lpm_clock_gating, hsotg->params.lpm);
+ CHECK_BOOL(besl, hsotg->params.lpm);
+ CHECK_BOOL(besl, (hsotg->hw_params.snpsid >= DWC2_CORE_REV_3_00a));
+ CHECK_BOOL(hird_threshold_en, hsotg->params.lpm);
+ CHECK_RANGE(hird_threshold, 0, hsotg->params.besl ? 12 : 7, 0);
CHECK_RANGE(max_packet_count,
15, hw->max_packet_count,
hw->max_packet_count);
@@ -579,19 +640,15 @@ static void dwc2_get_host_hwparams(struct dwc2_hsotg *hsotg)
struct dwc2_hw_params *hw = &hsotg->hw_params;
u32 gnptxfsiz;
u32 hptxfsiz;
- bool forced;
if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
return;
- forced = dwc2_force_mode_if_needed(hsotg, true);
+ dwc2_force_mode(hsotg, true);
gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ);
hptxfsiz = dwc2_readl(hsotg->regs + HPTXFSIZ);
- if (forced)
- dwc2_clear_force_mode(hsotg);
-
hw->host_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >>
FIFOSIZE_DEPTH_SHIFT;
hw->host_perio_tx_fifo_size = (hptxfsiz & FIFOSIZE_DEPTH_MASK) >>
@@ -606,14 +663,13 @@ static void dwc2_get_host_hwparams(struct dwc2_hsotg *hsotg)
static void dwc2_get_dev_hwparams(struct dwc2_hsotg *hsotg)
{
struct dwc2_hw_params *hw = &hsotg->hw_params;
- bool forced;
u32 gnptxfsiz;
int fifo, fifo_count;
if (hsotg->dr_mode == USB_DR_MODE_HOST)
return;
- forced = dwc2_force_mode_if_needed(hsotg, false);
+ dwc2_force_mode(hsotg, false);
gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ);
@@ -625,9 +681,6 @@ static void dwc2_get_dev_hwparams(struct dwc2_hsotg *hsotg)
FIFOSIZE_DEPTH_MASK) >> FIFOSIZE_DEPTH_SHIFT;
}
- if (forced)
- dwc2_clear_force_mode(hsotg);
-
hw->dev_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >>
FIFOSIZE_DEPTH_SHIFT;
}
@@ -646,14 +699,13 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
/*
* Attempt to ensure this device is really a DWC_otg Controller.
* Read and verify the GSNPSID register contents. The value should be
- * 0x45f42xxx or 0x45f43xxx, which corresponds to either "OT2" or "OT3",
- * as in "OTG version 2.xx" or "OTG version 3.xx".
+ * 0x45f4xxxx, 0x5531xxxx or 0x5532xxxx
*/
+
hw->snpsid = dwc2_readl(hsotg->regs + GSNPSID);
- if ((hw->snpsid & 0xfffff000) != 0x4f542000 &&
- (hw->snpsid & 0xfffff000) != 0x4f543000 &&
- (hw->snpsid & 0xffff0000) != 0x55310000 &&
- (hw->snpsid & 0xffff0000) != 0x55320000) {
+ if ((hw->snpsid & GSNPSID_ID_MASK) != DWC2_OTG_ID &&
+ (hw->snpsid & GSNPSID_ID_MASK) != DWC2_FS_IOT_ID &&
+ (hw->snpsid & GSNPSID_ID_MASK) != DWC2_HS_IOT_ID) {
dev_err(hsotg->dev, "Bad value for GSNPSID: 0x%08x\n",
hw->snpsid);
return -ENODEV;
@@ -706,6 +758,7 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
hw->i2c_enable = !!(hwcfg3 & GHWCFG3_I2C);
hw->total_fifo_size = (hwcfg3 & GHWCFG3_DFIFO_DEPTH_MASK) >>
GHWCFG3_DFIFO_DEPTH_SHIFT;
+ hw->lpm_mode = !!(hwcfg3 & GHWCFG3_OTG_LPM_EN);
/* hwcfg4 */
hw->en_multiple_tx_fifo = !!(hwcfg4 & GHWCFG4_DED_FIFO_EN);
@@ -715,8 +768,10 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
GHWCFG4_NUM_IN_EPS_SHIFT;
hw->dma_desc_enable = !!(hwcfg4 & GHWCFG4_DESC_DMA);
hw->power_optimized = !!(hwcfg4 & GHWCFG4_POWER_OPTIMIZ);
+ hw->hibernation = !!(hwcfg4 & GHWCFG4_HIBER);
hw->utmi_phy_data_width = (hwcfg4 & GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK) >>
GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT;
+ hw->acg_enable = !!(hwcfg4 & GHWCFG4_ACG_SUPPORTED);
/* fifo sizes */
hw->rx_fifo_size = (grxfsiz & GRXFSIZ_DEPTH_MASK) >>
diff --git a/drivers/usb/dwc2/pci.c b/drivers/usb/dwc2/pci.c
index 3ecc951a1aea..7f21747007f1 100644
--- a/drivers/usb/dwc2/pci.c
+++ b/drivers/usb/dwc2/pci.c
@@ -83,7 +83,6 @@ static void dwc2_pci_remove(struct pci_dev *pci)
platform_device_unregister(glue->dwc2);
usb_phy_generic_unregister(glue->phy);
- kfree(glue);
pci_set_drvdata(pci, NULL);
}
@@ -105,10 +104,17 @@ static int dwc2_pci_probe(struct pci_dev *pci,
pci_set_master(pci);
+ phy = usb_phy_generic_register();
+ if (IS_ERR(phy)) {
+ dev_err(dev, "error registering generic PHY (%ld)\n",
+ PTR_ERR(phy));
+ return PTR_ERR(phy);
+ }
+
dwc2 = platform_device_alloc("dwc2", PLATFORM_DEVID_AUTO);
if (!dwc2) {
dev_err(dev, "couldn't allocate dwc2 device\n");
- return -ENOMEM;
+ goto err;
}
memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));
@@ -125,32 +131,25 @@ static int dwc2_pci_probe(struct pci_dev *pci,
ret = platform_device_add_resources(dwc2, res, ARRAY_SIZE(res));
if (ret) {
dev_err(dev, "couldn't add resources to dwc2 device\n");
- return ret;
+ goto err;
}
dwc2->dev.parent = dev;
- phy = usb_phy_generic_register();
- if (IS_ERR(phy)) {
- dev_err(dev, "error registering generic PHY (%ld)\n",
- PTR_ERR(phy));
- return PTR_ERR(phy);
- }
-
ret = dwc2_pci_quirks(pci, dwc2);
if (ret)
goto err;
+ glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL);
+ if (!glue)
+ goto err;
+
ret = platform_device_add(dwc2);
if (ret) {
dev_err(dev, "failed to register dwc2 device\n");
goto err;
}
- glue = kzalloc(sizeof(*glue), GFP_KERNEL);
- if (!glue)
- return -ENOMEM;
-
glue->phy = phy;
glue->dwc2 = dwc2;
pci_set_drvdata(pci, glue);
diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
index 4703478f702f..4c0819554bcd 100644
--- a/drivers/usb/dwc2/platform.c
+++ b/drivers/usb/dwc2/platform.c
@@ -382,8 +382,10 @@ static int dwc2_driver_probe(struct platform_device *dev)
if (!dev->dev.dma_mask)
dev->dev.dma_mask = &dev->dev.coherent_dma_mask;
retval = dma_set_coherent_mask(&dev->dev, DMA_BIT_MASK(32));
- if (retval)
+ if (retval) {
+ dev_err(&dev->dev, "can't set coherent DMA mask: %d\n", retval);
return retval;
+ }
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
hsotg->regs = devm_ioremap_resource(&dev->dev, res);
@@ -425,13 +427,20 @@ static int dwc2_driver_probe(struct platform_device *dev)
* Reset before dwc2_get_hwparams() then it could get power-on real
* reset value form registers.
*/
- dwc2_core_reset_and_force_dr_mode(hsotg);
+ retval = dwc2_core_reset(hsotg, false);
+ if (retval)
+ goto error;
/* Detect config values from hardware */
retval = dwc2_get_hwparams(hsotg);
if (retval)
goto error;
+ /*
+ * For OTG cores, set the force mode bits to reflect the value
+ * of dr_mode. Force mode bits should not be touched at any
+ * other time after this.
+ */
dwc2_force_dr_mode(hsotg);
retval = dwc2_init_params(hsotg);
@@ -439,7 +448,7 @@ static int dwc2_driver_probe(struct platform_device *dev)
goto error;
if (hsotg->dr_mode != USB_DR_MODE_HOST) {
- retval = dwc2_gadget_init(hsotg, hsotg->irq);
+ retval = dwc2_gadget_init(hsotg);
if (retval)
goto error;
hsotg->gadget_enabled = 1;
@@ -456,6 +465,7 @@ static int dwc2_driver_probe(struct platform_device *dev)
}
platform_set_drvdata(dev, hsotg);
+ hsotg->hibernated = 0;
dwc2_debugfs_init(hsotg);
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index 7ac725038f8d..025bc68094fc 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -6,7 +6,7 @@ obj-$(CONFIG_USB_DWC3) += dwc3.o
dwc3-y := core.o
-ifneq ($(CONFIG_FTRACE),)
+ifneq ($(CONFIG_TRACING),)
dwc3-y += trace.o
endif
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index e94bf91cc58a..a15648d25e30 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -89,10 +89,7 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
return 0;
}
-static void dwc3_event_buffers_cleanup(struct dwc3 *dwc);
-static int dwc3_event_buffers_setup(struct dwc3 *dwc);
-
-static void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
+void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
{
u32 reg;
@@ -110,13 +107,19 @@ static void __dwc3_set_mode(struct work_struct *work)
unsigned long flags;
int ret;
+ if (dwc->dr_mode != USB_DR_MODE_OTG)
+ return;
+
+ if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_OTG)
+ dwc3_otg_update(dwc, 0);
+
if (!dwc->desired_dr_role)
return;
if (dwc->desired_dr_role == dwc->current_dr_role)
return;
- if (dwc->dr_mode != USB_DR_MODE_OTG)
+ if (dwc->desired_dr_role == DWC3_GCTL_PRTCAP_OTG && dwc->edev)
return;
switch (dwc->current_dr_role) {
@@ -127,6 +130,13 @@ static void __dwc3_set_mode(struct work_struct *work)
dwc3_gadget_exit(dwc);
dwc3_event_buffers_cleanup(dwc);
break;
+ case DWC3_GCTL_PRTCAP_OTG:
+ dwc3_otg_exit(dwc);
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc->desired_otg_role = DWC3_OTG_ROLE_IDLE;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ dwc3_otg_update(dwc, 1);
+ break;
default:
break;
}
@@ -162,9 +172,14 @@ static void __dwc3_set_mode(struct work_struct *work)
if (ret)
dev_err(dwc->dev, "failed to initialize peripheral\n");
break;
+ case DWC3_GCTL_PRTCAP_OTG:
+ dwc3_otg_init(dwc);
+ dwc3_otg_update(dwc, 0);
+ break;
default:
break;
}
+
}
void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
@@ -229,7 +244,7 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
do {
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (!(reg & DWC3_DCTL_CSFTRST))
- return 0;
+ goto done;
udelay(1);
} while (--retries);
@@ -238,6 +253,17 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
phy_exit(dwc->usb2_generic_phy);
return -ETIMEDOUT;
+
+done:
+ /*
+ * For DWC_usb31 controller, once DWC3_DCTL_CSFTRST bit is cleared,
+ * we must wait at least 50ms before accessing the PHY domain
+ * (synchronization delay). DWC_usb31 programming guide section 1.3.2.
+ */
+ if (dwc3_is_usb31(dwc))
+ msleep(50);
+
+ return 0;
}
/*
@@ -348,7 +374,7 @@ static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
*
* Returns 0 on success otherwise negative errno.
*/
-static int dwc3_event_buffers_setup(struct dwc3 *dwc)
+int dwc3_event_buffers_setup(struct dwc3 *dwc)
{
struct dwc3_event_buffer *evt;
@@ -365,7 +391,7 @@ static int dwc3_event_buffers_setup(struct dwc3 *dwc)
return 0;
}
-static void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
+void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
{
struct dwc3_event_buffer *evt;
@@ -846,6 +872,43 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
}
+ /*
+ * Must config both number of packets and max burst settings to enable
+ * RX and/or TX threshold.
+ */
+ if (dwc3_is_usb31(dwc) && dwc->dr_mode == USB_DR_MODE_HOST) {
+ u8 rx_thr_num = dwc->rx_thr_num_pkt_prd;
+ u8 rx_maxburst = dwc->rx_max_burst_prd;
+ u8 tx_thr_num = dwc->tx_thr_num_pkt_prd;
+ u8 tx_maxburst = dwc->tx_max_burst_prd;
+
+ if (rx_thr_num && rx_maxburst) {
+ reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
+ reg |= DWC31_RXTHRNUMPKTSEL_PRD;
+
+ reg &= ~DWC31_RXTHRNUMPKT_PRD(~0);
+ reg |= DWC31_RXTHRNUMPKT_PRD(rx_thr_num);
+
+ reg &= ~DWC31_MAXRXBURSTSIZE_PRD(~0);
+ reg |= DWC31_MAXRXBURSTSIZE_PRD(rx_maxburst);
+
+ dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
+ }
+
+ if (tx_thr_num && tx_maxburst) {
+ reg = dwc3_readl(dwc->regs, DWC3_GTXTHRCFG);
+ reg |= DWC31_TXTHRNUMPKTSEL_PRD;
+
+ reg &= ~DWC31_TXTHRNUMPKT_PRD(~0);
+ reg |= DWC31_TXTHRNUMPKT_PRD(tx_thr_num);
+
+ reg &= ~DWC31_MAXTXBURSTSIZE_PRD(~0);
+ reg |= DWC31_MAXTXBURSTSIZE_PRD(tx_maxburst);
+
+ dwc3_writel(dwc->regs, DWC3_GTXTHRCFG, reg);
+ }
+ }
+
return 0;
err4:
@@ -1016,6 +1079,10 @@ static void dwc3_get_properties(struct dwc3 *dwc)
u8 lpm_nyet_threshold;
u8 tx_de_emphasis;
u8 hird_threshold;
+ u8 rx_thr_num_pkt_prd;
+ u8 rx_max_burst_prd;
+ u8 tx_thr_num_pkt_prd;
+ u8 tx_max_burst_prd;
/* default to highest possible threshold */
lpm_nyet_threshold = 0xff;
@@ -1050,6 +1117,14 @@ static void dwc3_get_properties(struct dwc3 *dwc)
&hird_threshold);
dwc->usb3_lpm_capable = device_property_read_bool(dev,
"snps,usb3_lpm_capable");
+ device_property_read_u8(dev, "snps,rx-thr-num-pkt-prd",
+ &rx_thr_num_pkt_prd);
+ device_property_read_u8(dev, "snps,rx-max-burst-prd",
+ &rx_max_burst_prd);
+ device_property_read_u8(dev, "snps,tx-thr-num-pkt-prd",
+ &tx_thr_num_pkt_prd);
+ device_property_read_u8(dev, "snps,tx-max-burst-prd",
+ &tx_max_burst_prd);
dwc->disable_scramble_quirk = device_property_read_bool(dev,
"snps,disable_scramble_quirk");
@@ -1100,6 +1175,12 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->hird_threshold = hird_threshold
| (dwc->is_utmi_l1_suspend << 4);
+ dwc->rx_thr_num_pkt_prd = rx_thr_num_pkt_prd;
+ dwc->rx_max_burst_prd = rx_max_burst_prd;
+
+ dwc->tx_thr_num_pkt_prd = tx_thr_num_pkt_prd;
+ dwc->tx_max_burst_prd = tx_max_burst_prd;
+
dwc->imod_interval = 0;
}
@@ -1326,6 +1407,20 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
if (!PMSG_IS_AUTO(msg))
dwc3_core_exit(dwc);
break;
+ case DWC3_GCTL_PRTCAP_OTG:
+ /* do nothing during runtime_suspend */
+ if (PMSG_IS_AUTO(msg))
+ break;
+
+ if (dwc->current_otg_role == DWC3_OTG_ROLE_DEVICE) {
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_gadget_suspend(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ }
+
+ dwc3_otg_exit(dwc);
+ dwc3_core_exit(dwc);
+ break;
default:
/* do nothing */
break;
@@ -1345,6 +1440,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
if (ret)
return ret;
+ dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
spin_lock_irqsave(&dwc->lock, flags);
dwc3_gadget_resume(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -1355,7 +1451,29 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
ret = dwc3_core_init(dwc);
if (ret)
return ret;
+ dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST);
+ }
+ break;
+ case DWC3_GCTL_PRTCAP_OTG:
+ /* nothing to do on runtime_resume */
+ if (PMSG_IS_AUTO(msg))
+ break;
+
+ ret = dwc3_core_init(dwc);
+ if (ret)
+ return ret;
+
+ dwc3_set_prtcap(dwc, dwc->current_dr_role);
+
+ dwc3_otg_init(dwc);
+ if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST) {
+ dwc3_otg_host_init(dwc);
+ } else if (dwc->current_otg_role == DWC3_OTG_ROLE_DEVICE) {
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_gadget_resume(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
}
+
break;
default:
/* do nothing */
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 860d2bc184d1..4f3b43809917 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -58,6 +58,11 @@
#define DWC3_DEVICE_EVENT_CMD_CMPL 10
#define DWC3_DEVICE_EVENT_OVERFLOW 11
+/* Controller's role while using the OTG block */
+#define DWC3_OTG_ROLE_IDLE 0
+#define DWC3_OTG_ROLE_HOST 1
+#define DWC3_OTG_ROLE_DEVICE 2
+
#define DWC3_GEVNTCOUNT_MASK 0xfffc
#define DWC3_GEVNTCOUNT_EHB BIT(31)
#define DWC3_GSNPSID_MASK 0xffff0000
@@ -100,6 +105,11 @@
#define DWC3_GHWPARAMS7 0xc15c
#define DWC3_GDBGFIFOSPACE 0xc160
#define DWC3_GDBGLTSSM 0xc164
+#define DWC3_GDBGBMU 0xc16c
+#define DWC3_GDBGLSPMUX 0xc170
+#define DWC3_GDBGLSP 0xc174
+#define DWC3_GDBGEPINFO0 0xc178
+#define DWC3_GDBGEPINFO1 0xc17c
#define DWC3_GPRTBIMAP_HS0 0xc180
#define DWC3_GPRTBIMAP_HS1 0xc184
#define DWC3_GPRTBIMAP_FS0 0xc188
@@ -173,6 +183,26 @@
#define DWC3_GRXTHRCFG_RXPKTCNT(n) (((n) & 0xf) << 24)
#define DWC3_GRXTHRCFG_PKTCNTSEL BIT(29)
+/* Global RX Threshold Configuration Register for DWC_usb31 only */
+#define DWC31_GRXTHRCFG_MAXRXBURSTSIZE(n) (((n) & 0x1f) << 16)
+#define DWC31_GRXTHRCFG_RXPKTCNT(n) (((n) & 0x1f) << 21)
+#define DWC31_GRXTHRCFG_PKTCNTSEL BIT(26)
+#define DWC31_RXTHRNUMPKTSEL_HS_PRD BIT(15)
+#define DWC31_RXTHRNUMPKT_HS_PRD(n) (((n) & 0x3) << 13)
+#define DWC31_RXTHRNUMPKTSEL_PRD BIT(10)
+#define DWC31_RXTHRNUMPKT_PRD(n) (((n) & 0x1f) << 5)
+#define DWC31_MAXRXBURSTSIZE_PRD(n) ((n) & 0x1f)
+
+/* Global TX Threshold Configuration Register for DWC_usb31 only */
+#define DWC31_GTXTHRCFG_MAXTXBURSTSIZE(n) (((n) & 0x1f) << 16)
+#define DWC31_GTXTHRCFG_TXPKTCNT(n) (((n) & 0x1f) << 21)
+#define DWC31_GTXTHRCFG_PKTCNTSEL BIT(26)
+#define DWC31_TXTHRNUMPKTSEL_HS_PRD BIT(15)
+#define DWC31_TXTHRNUMPKT_HS_PRD(n) (((n) & 0x3) << 13)
+#define DWC31_TXTHRNUMPKTSEL_PRD BIT(10)
+#define DWC31_TXTHRNUMPKT_PRD(n) (((n) & 0x1f) << 5)
+#define DWC31_MAXTXBURSTSIZE_PRD(n) ((n) & 0x1f)
+
/* Global Configuration Register */
#define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19)
#define DWC3_GCTL_U2RSTECN BIT(16)
@@ -201,6 +231,15 @@
#define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28)
#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
+/* Global Status Register */
+#define DWC3_GSTS_OTG_IP BIT(10)
+#define DWC3_GSTS_BC_IP BIT(9)
+#define DWC3_GSTS_ADP_IP BIT(8)
+#define DWC3_GSTS_HOST_IP BIT(7)
+#define DWC3_GSTS_DEVICE_IP BIT(6)
+#define DWC3_GSTS_CSR_TIMEOUT BIT(5)
+#define DWC3_GSTS_BUS_ERR_ADDR_VLD BIT(4)
+
/* Global USB2 PHY Configuration Register */
#define DWC3_GUSB2PHYCFG_PHYSOFTRST BIT(31)
#define DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS BIT(30)
@@ -241,6 +280,8 @@
#define DWC3_GUSB3PIPECTL_TX_DEEPH(n) ((n) << 1)
/* Global TX Fifo Size Register */
+#define DWC31_GTXFIFOSIZ_TXFRAMNUM BIT(15) /* DWC_usb31 only */
+#define DWC31_GTXFIFOSIZ_TXFDEF(n) ((n) & 0x7fff) /* DWC_usb31 only */
#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff)
#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000)
@@ -286,6 +327,11 @@
#define DWC3_MAX_HIBER_SCRATCHBUFS 15
/* Global HWPARAMS6 Register */
+#define DWC3_GHWPARAMS6_BCSUPPORT BIT(14)
+#define DWC3_GHWPARAMS6_OTG3SUPPORT BIT(13)
+#define DWC3_GHWPARAMS6_ADPSUPPORT BIT(12)
+#define DWC3_GHWPARAMS6_HNPSUPPORT BIT(11)
+#define DWC3_GHWPARAMS6_SRPSUPPORT BIT(10)
#define DWC3_GHWPARAMS6_EN_FPGA BIT(7)
/* Global HWPARAMS7 Register */
@@ -467,6 +513,74 @@
#define DWC3_DEV_IMOD_INTERVAL_SHIFT 0
#define DWC3_DEV_IMOD_INTERVAL_MASK (0xffff << 0)
+/* OTG Configuration Register */
+#define DWC3_OCFG_DISPWRCUTTOFF BIT(5)
+#define DWC3_OCFG_HIBDISMASK BIT(4)
+#define DWC3_OCFG_SFTRSTMASK BIT(3)
+#define DWC3_OCFG_OTGVERSION BIT(2)
+#define DWC3_OCFG_HNPCAP BIT(1)
+#define DWC3_OCFG_SRPCAP BIT(0)
+
+/* OTG CTL Register */
+#define DWC3_OCTL_OTG3GOERR BIT(7)
+#define DWC3_OCTL_PERIMODE BIT(6)
+#define DWC3_OCTL_PRTPWRCTL BIT(5)
+#define DWC3_OCTL_HNPREQ BIT(4)
+#define DWC3_OCTL_SESREQ BIT(3)
+#define DWC3_OCTL_TERMSELIDPULSE BIT(2)
+#define DWC3_OCTL_DEVSETHNPEN BIT(1)
+#define DWC3_OCTL_HSTSETHNPEN BIT(0)
+
+/* OTG Event Register */
+#define DWC3_OEVT_DEVICEMODE BIT(31)
+#define DWC3_OEVT_XHCIRUNSTPSET BIT(27)
+#define DWC3_OEVT_DEVRUNSTPSET BIT(26)
+#define DWC3_OEVT_HIBENTRY BIT(25)
+#define DWC3_OEVT_CONIDSTSCHNG BIT(24)
+#define DWC3_OEVT_HRRCONFNOTIF BIT(23)
+#define DWC3_OEVT_HRRINITNOTIF BIT(22)
+#define DWC3_OEVT_ADEVIDLE BIT(21)
+#define DWC3_OEVT_ADEVBHOSTEND BIT(20)
+#define DWC3_OEVT_ADEVHOST BIT(19)
+#define DWC3_OEVT_ADEVHNPCHNG BIT(18)
+#define DWC3_OEVT_ADEVSRPDET BIT(17)
+#define DWC3_OEVT_ADEVSESSENDDET BIT(16)
+#define DWC3_OEVT_BDEVBHOSTEND BIT(11)
+#define DWC3_OEVT_BDEVHNPCHNG BIT(10)
+#define DWC3_OEVT_BDEVSESSVLDDET BIT(9)
+#define DWC3_OEVT_BDEVVBUSCHNG BIT(8)
+#define DWC3_OEVT_BSESSVLD BIT(3)
+#define DWC3_OEVT_HSTNEGSTS BIT(2)
+#define DWC3_OEVT_SESREQSTS BIT(1)
+#define DWC3_OEVT_ERROR BIT(0)
+
+/* OTG Event Enable Register */
+#define DWC3_OEVTEN_XHCIRUNSTPSETEN BIT(27)
+#define DWC3_OEVTEN_DEVRUNSTPSETEN BIT(26)
+#define DWC3_OEVTEN_HIBENTRYEN BIT(25)
+#define DWC3_OEVTEN_CONIDSTSCHNGEN BIT(24)
+#define DWC3_OEVTEN_HRRCONFNOTIFEN BIT(23)
+#define DWC3_OEVTEN_HRRINITNOTIFEN BIT(22)
+#define DWC3_OEVTEN_ADEVIDLEEN BIT(21)
+#define DWC3_OEVTEN_ADEVBHOSTENDEN BIT(20)
+#define DWC3_OEVTEN_ADEVHOSTEN BIT(19)
+#define DWC3_OEVTEN_ADEVHNPCHNGEN BIT(18)
+#define DWC3_OEVTEN_ADEVSRPDETEN BIT(17)
+#define DWC3_OEVTEN_ADEVSESSENDDETEN BIT(16)
+#define DWC3_OEVTEN_BDEVBHOSTENDEN BIT(11)
+#define DWC3_OEVTEN_BDEVHNPCHNGEN BIT(10)
+#define DWC3_OEVTEN_BDEVSESSVLDDETEN BIT(9)
+#define DWC3_OEVTEN_BDEVVBUSCHNGEN BIT(8)
+
+/* OTG Status Register */
+#define DWC3_OSTS_DEVRUNSTP BIT(13)
+#define DWC3_OSTS_XHCIRUNSTP BIT(12)
+#define DWC3_OSTS_PERIPHERALSTATE BIT(4)
+#define DWC3_OSTS_XHCIPRTPOWER BIT(3)
+#define DWC3_OSTS_BSESVLD BIT(2)
+#define DWC3_OSTS_VBUSVLD BIT(1)
+#define DWC3_OSTS_CONIDSTS BIT(0)
+
/* Structures */
struct dwc3_trb;
@@ -781,6 +895,10 @@ struct dwc3_scratchpad_array {
* @regs_size: address space size
* @fladj: frame length adjustment
* @irq_gadget: peripheral controller's IRQ number
+ * @otg_irq: IRQ number for OTG IRQs
+ * @current_otg_role: current role of operation while using the OTG block
+ * @desired_otg_role: desired role of operation while using the OTG block
+ * @otg_restart_host: flag that OTG controller needs to restart host
* @nr_scratch: number of scratch buffers
* @u1u2: only used on revisions <1.83a for workaround
* @maximum_speed: maximum speed requested (mainly for testing purposes)
@@ -816,6 +934,10 @@ struct dwc3_scratchpad_array {
* @test_mode_nr: test feature selector
* @lpm_nyet_threshold: LPM NYET response threshold
* @hird_threshold: HIRD threshold
+ * @rx_thr_num_pkt_prd: periodic ESS receive packet count
+ * @rx_max_burst_prd: max periodic ESS receive burst size
+ * @tx_thr_num_pkt_prd: periodic ESS transmit packet count
+ * @tx_max_burst_prd: max periodic ESS transmit burst size
* @hsphy_interface: "utmi" or "ulpi"
* @connected: true when we're connected to a host, false otherwise
* @delayed_status: true when gadget driver asks for delayed status
@@ -914,6 +1036,10 @@ struct dwc3 {
u32 fladj;
u32 irq_gadget;
+ u32 otg_irq;
+ u32 current_otg_role;
+ u32 desired_otg_role;
+ bool otg_restart_host;
u32 nr_scratch;
u32 u1u2;
u32 maximum_speed;
@@ -979,6 +1105,10 @@ struct dwc3 {
u8 test_mode_nr;
u8 lpm_nyet_threshold;
u8 hird_threshold;
+ u8 rx_thr_num_pkt_prd;
+ u8 rx_max_burst_prd;
+ u8 tx_thr_num_pkt_prd;
+ u8 tx_max_burst_prd;
const char *hsphy_interface;
@@ -1175,6 +1305,7 @@ struct dwc3_gadget_ep_cmd_params {
#define DWC3_HAS_OTG BIT(3)
/* prototypes */
+void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode);
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type);
@@ -1192,6 +1323,9 @@ static inline bool dwc3_is_usb31(struct dwc3 *dwc)
bool dwc3_has_imod(struct dwc3 *dwc);
+int dwc3_event_buffers_setup(struct dwc3 *dwc);
+void dwc3_event_buffers_cleanup(struct dwc3 *dwc);
+
#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_host_init(struct dwc3 *dwc);
void dwc3_host_exit(struct dwc3 *dwc);
@@ -1235,11 +1369,23 @@ static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
#if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_drd_init(struct dwc3 *dwc);
void dwc3_drd_exit(struct dwc3 *dwc);
+void dwc3_otg_init(struct dwc3 *dwc);
+void dwc3_otg_exit(struct dwc3 *dwc);
+void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus);
+void dwc3_otg_host_init(struct dwc3 *dwc);
#else
static inline int dwc3_drd_init(struct dwc3 *dwc)
{ return 0; }
static inline void dwc3_drd_exit(struct dwc3 *dwc)
{ }
+static inline void dwc3_otg_init(struct dwc3 *dwc)
+{ }
+static inline void dwc3_otg_exit(struct dwc3 *dwc)
+{ }
+static inline void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus)
+{ }
+static inline void dwc3_otg_host_init(struct dwc3 *dwc)
+{ }
#endif
/* power management interface */
diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
index 00e65530c81e..2f07be1e1f31 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -81,6 +81,11 @@ static const struct debugfs_reg32 dwc3_regs[] = {
dump_register(GHWPARAMS7),
dump_register(GDBGFIFOSPACE),
dump_register(GDBGLTSSM),
+ dump_register(GDBGBMU),
+ dump_register(GDBGLSPMUX),
+ dump_register(GDBGLSP),
+ dump_register(GDBGEPINFO0),
+ dump_register(GDBGEPINFO1),
dump_register(GPRTBIMAP_HS0),
dump_register(GPRTBIMAP_HS1),
dump_register(GPRTBIMAP_FS0),
@@ -487,8 +492,8 @@ static const struct file_operations dwc3_link_state_fops = {
};
struct dwc3_ep_file_map {
- char name[25];
- int (*show)(struct seq_file *s, void *unused);
+ const char name[25];
+ const struct file_operations *const fops;
};
static int dwc3_tx_fifo_queue_show(struct seq_file *s, void *unused)
@@ -596,7 +601,7 @@ static int dwc3_event_queue_show(struct seq_file *s, void *unused)
return 0;
}
-static int dwc3_ep_transfer_type_show(struct seq_file *s, void *unused)
+static int dwc3_transfer_type_show(struct seq_file *s, void *unused)
{
struct dwc3_ep *dep = s->private;
struct dwc3 *dwc = dep->dwc;
@@ -632,7 +637,7 @@ out:
return 0;
}
-static int dwc3_ep_trb_ring_show(struct seq_file *s, void *unused)
+static int dwc3_trb_ring_show(struct seq_file *s, void *unused)
{
struct dwc3_ep *dep = s->private;
struct dwc3 *dwc = dep->dwc;
@@ -670,58 +675,39 @@ out:
return 0;
}
-static struct dwc3_ep_file_map map[] = {
- { "tx_fifo_queue", dwc3_tx_fifo_queue_show, },
- { "rx_fifo_queue", dwc3_rx_fifo_queue_show, },
- { "tx_request_queue", dwc3_tx_request_queue_show, },
- { "rx_request_queue", dwc3_rx_request_queue_show, },
- { "rx_info_queue", dwc3_rx_info_queue_show, },
- { "descriptor_fetch_queue", dwc3_descriptor_fetch_queue_show, },
- { "event_queue", dwc3_event_queue_show, },
- { "transfer_type", dwc3_ep_transfer_type_show, },
- { "trb_ring", dwc3_ep_trb_ring_show, },
+DEFINE_SHOW_ATTRIBUTE(dwc3_tx_fifo_queue);
+DEFINE_SHOW_ATTRIBUTE(dwc3_rx_fifo_queue);
+DEFINE_SHOW_ATTRIBUTE(dwc3_tx_request_queue);
+DEFINE_SHOW_ATTRIBUTE(dwc3_rx_request_queue);
+DEFINE_SHOW_ATTRIBUTE(dwc3_rx_info_queue);
+DEFINE_SHOW_ATTRIBUTE(dwc3_descriptor_fetch_queue);
+DEFINE_SHOW_ATTRIBUTE(dwc3_event_queue);
+DEFINE_SHOW_ATTRIBUTE(dwc3_transfer_type);
+DEFINE_SHOW_ATTRIBUTE(dwc3_trb_ring);
+
+static const struct dwc3_ep_file_map dwc3_ep_file_map[] = {
+ { "tx_fifo_queue", &dwc3_tx_fifo_queue_fops, },
+ { "rx_fifo_queue", &dwc3_rx_fifo_queue_fops, },
+ { "tx_request_queue", &dwc3_tx_request_queue_fops, },
+ { "rx_request_queue", &dwc3_rx_request_queue_fops, },
+ { "rx_info_queue", &dwc3_rx_info_queue_fops, },
+ { "descriptor_fetch_queue", &dwc3_descriptor_fetch_queue_fops, },
+ { "event_queue", &dwc3_event_queue_fops, },
+ { "transfer_type", &dwc3_transfer_type_fops, },
+ { "trb_ring", &dwc3_trb_ring_fops, },
};
-static int dwc3_endpoint_open(struct inode *inode, struct file *file)
-{
- const char *file_name = file_dentry(file)->d_iname;
- struct dwc3_ep_file_map *f_map;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(map); i++) {
- f_map = &map[i];
-
- if (strcmp(f_map->name, file_name) == 0)
- break;
- }
-
- return single_open(file, f_map->show, inode->i_private);
-}
-
-static const struct file_operations dwc3_endpoint_fops = {
- .open = dwc3_endpoint_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static void dwc3_debugfs_create_endpoint_file(struct dwc3_ep *dep,
- struct dentry *parent, int type)
-{
- struct dentry *file;
- struct dwc3_ep_file_map *ep_file = &map[type];
-
- file = debugfs_create_file(ep_file->name, S_IRUGO, parent, dep,
- &dwc3_endpoint_fops);
-}
-
static void dwc3_debugfs_create_endpoint_files(struct dwc3_ep *dep,
struct dentry *parent)
{
int i;
- for (i = 0; i < ARRAY_SIZE(map); i++)
- dwc3_debugfs_create_endpoint_file(dep, parent, i);
+ for (i = 0; i < ARRAY_SIZE(dwc3_ep_file_map); i++) {
+ const struct file_operations *fops = dwc3_ep_file_map[i].fops;
+ const char *name = dwc3_ep_file_map[i].name;
+
+ debugfs_create_file(name, S_IRUGO, parent, dep, fops);
+ }
}
static void dwc3_debugfs_create_endpoint_dir(struct dwc3_ep *dep,
diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c
index cc8ab9a8e9d2..1d8c557e97e0 100644
--- a/drivers/usb/dwc3/drd.c
+++ b/drivers/usb/dwc3/drd.c
@@ -8,22 +8,423 @@
*/
#include <linux/extcon.h>
+#include <linux/platform_device.h>
#include "debug.h"
#include "core.h"
#include "gadget.h"
-static void dwc3_drd_update(struct dwc3 *dwc)
+static void dwc3_otg_disable_events(struct dwc3 *dwc, u32 disable_mask)
+{
+ u32 reg = dwc3_readl(dwc->regs, DWC3_OEVTEN);
+
+ reg &= ~(disable_mask);
+ dwc3_writel(dwc->regs, DWC3_OEVTEN, reg);
+}
+
+static void dwc3_otg_enable_events(struct dwc3 *dwc, u32 enable_mask)
+{
+ u32 reg = dwc3_readl(dwc->regs, DWC3_OEVTEN);
+
+ reg |= (enable_mask);
+ dwc3_writel(dwc->regs, DWC3_OEVTEN, reg);
+}
+
+static void dwc3_otg_clear_events(struct dwc3 *dwc)
+{
+ u32 reg = dwc3_readl(dwc->regs, DWC3_OEVT);
+
+ dwc3_writel(dwc->regs, DWC3_OEVTEN, reg);
+}
+
+#define DWC3_OTG_ALL_EVENTS (DWC3_OEVTEN_XHCIRUNSTPSETEN | \
+ DWC3_OEVTEN_DEVRUNSTPSETEN | DWC3_OEVTEN_HIBENTRYEN | \
+ DWC3_OEVTEN_CONIDSTSCHNGEN | DWC3_OEVTEN_HRRCONFNOTIFEN | \
+ DWC3_OEVTEN_HRRINITNOTIFEN | DWC3_OEVTEN_ADEVIDLEEN | \
+ DWC3_OEVTEN_ADEVBHOSTENDEN | DWC3_OEVTEN_ADEVHOSTEN | \
+ DWC3_OEVTEN_ADEVHNPCHNGEN | DWC3_OEVTEN_ADEVSRPDETEN | \
+ DWC3_OEVTEN_ADEVSESSENDDETEN | DWC3_OEVTEN_BDEVBHOSTENDEN | \
+ DWC3_OEVTEN_BDEVHNPCHNGEN | DWC3_OEVTEN_BDEVSESSVLDDETEN | \
+ DWC3_OEVTEN_BDEVVBUSCHNGEN)
+
+static irqreturn_t dwc3_otg_thread_irq(int irq, void *_dwc)
{
+ struct dwc3 *dwc = _dwc;
+
+ spin_lock(&dwc->lock);
+ if (dwc->otg_restart_host) {
+ dwc3_otg_host_init(dwc);
+ dwc->otg_restart_host = 0;
+ }
+
+ spin_unlock(&dwc->lock);
+
+ dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t dwc3_otg_irq(int irq, void *_dwc)
+{
+ u32 reg;
+ struct dwc3 *dwc = _dwc;
+ irqreturn_t ret = IRQ_NONE;
+
+ reg = dwc3_readl(dwc->regs, DWC3_OEVT);
+ if (reg) {
+ /* ignore non OTG events, we can't disable them in OEVTEN */
+ if (!(reg & DWC3_OTG_ALL_EVENTS)) {
+ dwc3_writel(dwc->regs, DWC3_OEVT, reg);
+ return IRQ_NONE;
+ }
+
+ if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST &&
+ !(reg & DWC3_OEVT_DEVICEMODE))
+ dwc->otg_restart_host = 1;
+ dwc3_writel(dwc->regs, DWC3_OEVT, reg);
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ return ret;
+}
+
+static void dwc3_otgregs_init(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ /*
+ * Prevent host/device reset from resetting OTG core.
+ * If we don't do this then xhci_reset (USBCMD.HCRST) will reset
+ * the signal outputs sent to the PHY, the OTG FSM logic of the
+ * core and also the resets to the VBUS filters inside the core.
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_OCFG);
+ reg |= DWC3_OCFG_SFTRSTMASK;
+ dwc3_writel(dwc->regs, DWC3_OCFG, reg);
+
+ /* Disable hibernation for simplicity */
+ reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg &= ~DWC3_GCTL_GBLHIBERNATIONEN;
+ dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+
+ /*
+ * Initialize OTG registers as per
+ * Figure 11-4 OTG Driver Overall Programming Flow
+ */
+ /* OCFG.SRPCap = 0, OCFG.HNPCap = 0 */
+ reg = dwc3_readl(dwc->regs, DWC3_OCFG);
+ reg &= ~(DWC3_OCFG_SRPCAP | DWC3_OCFG_HNPCAP);
+ dwc3_writel(dwc->regs, DWC3_OCFG, reg);
+ /* OEVT = FFFF */
+ dwc3_otg_clear_events(dwc);
+ /* OEVTEN = 0 */
+ dwc3_otg_disable_events(dwc, DWC3_OTG_ALL_EVENTS);
+ /* OEVTEN.ConIDStsChngEn = 1. Instead we enable all events */
+ dwc3_otg_enable_events(dwc, DWC3_OTG_ALL_EVENTS);
+ /*
+ * OCTL.PeriMode = 1, OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0,
+ * OCTL.HNPReq = 0
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+ reg |= DWC3_OCTL_PERIMODE;
+ reg &= ~(DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN |
+ DWC3_OCTL_HNPREQ);
+ dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+}
+
+static int dwc3_otg_get_irq(struct dwc3 *dwc)
+{
+ struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
+ int irq;
+
+ irq = platform_get_irq_byname(dwc3_pdev, "otg");
+ if (irq > 0)
+ goto out;
+
+ if (irq == -EPROBE_DEFER)
+ goto out;
+
+ irq = platform_get_irq_byname(dwc3_pdev, "dwc_usb3");
+ if (irq > 0)
+ goto out;
+
+ if (irq == -EPROBE_DEFER)
+ goto out;
+
+ irq = platform_get_irq(dwc3_pdev, 0);
+ if (irq > 0)
+ goto out;
+
+ if (irq != -EPROBE_DEFER)
+ dev_err(dwc->dev, "missing OTG IRQ\n");
+
+ if (!irq)
+ irq = -EINVAL;
+
+out:
+ return irq;
+}
+
+void dwc3_otg_init(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ /*
+ * As per Figure 11-4 OTG Driver Overall Programming Flow,
+ * block "Initialize GCTL for OTG operation".
+ */
+ /* GCTL.PrtCapDir=2'b11 */
+ dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG);
+ /* GUSB2PHYCFG0.SusPHY=0 */
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+
+ /* Initialize OTG registers */
+ dwc3_otgregs_init(dwc);
+}
+
+void dwc3_otg_exit(struct dwc3 *dwc)
+{
+ /* disable all OTG IRQs */
+ dwc3_otg_disable_events(dwc, DWC3_OTG_ALL_EVENTS);
+ /* clear all events */
+ dwc3_otg_clear_events(dwc);
+}
+
+/* should be called before Host controller driver is started */
+void dwc3_otg_host_init(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ /* As per Figure 11-10 A-Device Flow Diagram */
+ /* OCFG.HNPCap = 0, OCFG.SRPCap = 0. Already 0 */
+
+ /*
+ * OCTL.PeriMode=0, OCTL.TermSelDLPulse = 0,
+ * OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+ reg &= ~(DWC3_OCTL_PERIMODE | DWC3_OCTL_TERMSELIDPULSE |
+ DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN);
+ dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+
+ /*
+ * OCFG.DisPrtPwrCutoff = 0/1
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_OCFG);
+ reg &= ~DWC3_OCFG_DISPWRCUTTOFF;
+ dwc3_writel(dwc->regs, DWC3_OCFG, reg);
+
+ /*
+ * OCFG.SRPCap = 1, OCFG.HNPCap = GHWPARAMS6.HNP_CAP
+ * We don't want SRP/HNP for simple dual-role so leave
+ * these disabled.
+ */
+
+ /*
+ * OEVTEN.OTGADevHostEvntEn = 1
+ * OEVTEN.OTGADevSessEndDetEvntEn = 1
+ * We don't want HNP/role-swap so leave these disabled.
+ */
+
+ /* GUSB2PHYCFG.ULPIAutoRes = 1/0, GUSB2PHYCFG.SusPHY = 1 */
+ if (!dwc->dis_u2_susphy_quirk) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg |= DWC3_GUSB2PHYCFG_SUSPHY;
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ }
+
+ /* Set Port Power to enable VBUS: OCTL.PrtPwrCtl = 1 */
+ reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+ reg |= DWC3_OCTL_PRTPWRCTL;
+ dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+}
+
+/* should be called after Host controller driver is stopped */
+static void dwc3_otg_host_exit(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ /*
+ * Exit from A-device flow as per
+ * Figure 11-4 OTG Driver Overall Programming Flow
+ */
+
+ /*
+ * OEVTEN.OTGADevBHostEndEvntEn=0, OEVTEN.OTGADevHNPChngEvntEn=0
+ * OEVTEN.OTGADevSessEndDetEvntEn=0,
+ * OEVTEN.OTGADevHostEvntEn = 0
+ * But we don't disable any OTG events
+ */
+
+ /* OCTL.HstSetHNPEn = 0, OCTL.PrtPwrCtl=0 */
+ reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+ reg &= ~(DWC3_OCTL_HSTSETHNPEN | DWC3_OCTL_PRTPWRCTL);
+ dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+}
+
+/* should be called before the gadget controller driver is started */
+static void dwc3_otg_device_init(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ /* As per Figure 11-20 B-Device Flow Diagram */
+
+ /*
+ * OCFG.HNPCap = GHWPARAMS6.HNP_CAP, OCFG.SRPCap = 1
+ * but we keep them 0 for simple dual-role operation.
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_OCFG);
+ /* OCFG.OTGSftRstMsk = 0/1 */
+ reg |= DWC3_OCFG_SFTRSTMASK;
+ dwc3_writel(dwc->regs, DWC3_OCFG, reg);
+ /*
+ * OCTL.PeriMode = 1
+ * OCTL.TermSelDLPulse = 0/1, OCTL.HNPReq = 0
+ * OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+ reg |= DWC3_OCTL_PERIMODE;
+ reg &= ~(DWC3_OCTL_TERMSELIDPULSE | DWC3_OCTL_HNPREQ |
+ DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN);
+ dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+ /* OEVTEN.OTGBDevSesVldDetEvntEn = 1 */
+ dwc3_otg_enable_events(dwc, DWC3_OEVTEN_BDEVSESSVLDDETEN);
+ /* GUSB2PHYCFG.ULPIAutoRes = 0, GUSB2PHYCFG0.SusPHY = 1 */
+ if (!dwc->dis_u2_susphy_quirk) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg |= DWC3_GUSB2PHYCFG_SUSPHY;
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ }
+ /* GCTL.GblHibernationEn = 0. Already 0. */
+}
+
+/* should be called after the gadget controller driver is stopped */
+static void dwc3_otg_device_exit(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ /*
+ * Exit from B-device flow as per
+ * Figure 11-4 OTG Driver Overall Programming Flow
+ */
+
+ /*
+ * OEVTEN.OTGBDevHNPChngEvntEn = 0
+ * OEVTEN.OTGBDevVBusChngEvntEn = 0
+ * OEVTEN.OTGBDevBHostEndEvntEn = 0
+ */
+ dwc3_otg_disable_events(dwc, DWC3_OEVTEN_BDEVHNPCHNGEN |
+ DWC3_OEVTEN_BDEVVBUSCHNGEN |
+ DWC3_OEVTEN_BDEVBHOSTENDEN);
+
+ /* OCTL.DevSetHNPEn = 0, OCTL.HNPReq = 0, OCTL.PeriMode=1 */
+ reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+ reg &= ~(DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HNPREQ);
+ reg |= DWC3_OCTL_PERIMODE;
+ dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+}
+
+void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus)
+{
+ int ret;
+ u32 reg;
int id;
+ unsigned long flags;
- id = extcon_get_state(dwc->edev, EXTCON_USB_HOST);
- if (id < 0)
- id = 0;
+ if (dwc->dr_mode != USB_DR_MODE_OTG)
+ return;
- dwc3_set_mode(dwc, id ?
- DWC3_GCTL_PRTCAP_HOST :
- DWC3_GCTL_PRTCAP_DEVICE);
+ /* don't do anything if debug user changed role to not OTG */
+ if (dwc->current_dr_role != DWC3_GCTL_PRTCAP_OTG)
+ return;
+
+ if (!ignore_idstatus) {
+ reg = dwc3_readl(dwc->regs, DWC3_OSTS);
+ id = !!(reg & DWC3_OSTS_CONIDSTS);
+
+ dwc->desired_otg_role = id ? DWC3_OTG_ROLE_DEVICE :
+ DWC3_OTG_ROLE_HOST;
+ }
+
+ if (dwc->desired_otg_role == dwc->current_otg_role)
+ return;
+
+ switch (dwc->current_otg_role) {
+ case DWC3_OTG_ROLE_HOST:
+ dwc3_host_exit(dwc);
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_otg_host_exit(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ break;
+ case DWC3_OTG_ROLE_DEVICE:
+ dwc3_gadget_exit(dwc);
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_event_buffers_cleanup(dwc);
+ dwc3_otg_device_exit(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ break;
+ default:
+ break;
+ }
+
+ spin_lock_irqsave(&dwc->lock, flags);
+
+ dwc->current_otg_role = dwc->desired_otg_role;
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ switch (dwc->desired_otg_role) {
+ case DWC3_OTG_ROLE_HOST:
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_otgregs_init(dwc);
+ dwc3_otg_host_init(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ ret = dwc3_host_init(dwc);
+ if (ret) {
+ dev_err(dwc->dev, "failed to initialize host\n");
+ } else {
+ if (dwc->usb2_phy)
+ otg_set_vbus(dwc->usb2_phy->otg, true);
+ if (dwc->usb2_generic_phy)
+ phy_set_mode(dwc->usb2_generic_phy,
+ PHY_MODE_USB_HOST);
+ }
+ break;
+ case DWC3_OTG_ROLE_DEVICE:
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_otgregs_init(dwc);
+ dwc3_otg_device_init(dwc);
+ dwc3_event_buffers_setup(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ if (dwc->usb2_phy)
+ otg_set_vbus(dwc->usb2_phy->otg, false);
+ if (dwc->usb2_generic_phy)
+ phy_set_mode(dwc->usb2_generic_phy,
+ PHY_MODE_USB_DEVICE);
+ ret = dwc3_gadget_init(dwc);
+ if (ret)
+ dev_err(dwc->dev, "failed to initialize peripheral\n");
+ break;
+ default:
+ break;
+ }
+}
+
+static void dwc3_drd_update(struct dwc3 *dwc)
+{
+ int id;
+
+ if (dwc->edev) {
+ id = extcon_get_state(dwc->edev, EXTCON_USB_HOST);
+ if (id < 0)
+ id = 0;
+ dwc3_set_mode(dwc, id ?
+ DWC3_GCTL_PRTCAP_HOST :
+ DWC3_GCTL_PRTCAP_DEVICE);
+ }
}
static int dwc3_drd_notifier(struct notifier_block *nb,
@@ -40,11 +441,11 @@ static int dwc3_drd_notifier(struct notifier_block *nb,
int dwc3_drd_init(struct dwc3 *dwc)
{
- int ret;
+ int ret, irq;
- if (dwc->dev->of_node) {
- if (of_property_read_bool(dwc->dev->of_node, "extcon"))
- dwc->edev = extcon_get_edev_by_phandle(dwc->dev, 0);
+ if (dwc->dev->of_node &&
+ of_property_read_bool(dwc->dev->of_node, "extcon")) {
+ dwc->edev = extcon_get_edev_by_phandle(dwc->dev, 0);
if (IS_ERR(dwc->edev))
return PTR_ERR(dwc->edev);
@@ -56,19 +457,71 @@ int dwc3_drd_init(struct dwc3 *dwc)
dev_err(dwc->dev, "couldn't register cable notifier\n");
return ret;
}
- }
- dwc3_drd_update(dwc);
+ dwc3_drd_update(dwc);
+ } else {
+ dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG);
+ dwc->current_dr_role = DWC3_GCTL_PRTCAP_OTG;
+
+ /* use OTG block to get ID event */
+ irq = dwc3_otg_get_irq(dwc);
+ if (irq < 0)
+ return irq;
+
+ dwc->otg_irq = irq;
+
+ /* disable all OTG IRQs */
+ dwc3_otg_disable_events(dwc, DWC3_OTG_ALL_EVENTS);
+ /* clear all events */
+ dwc3_otg_clear_events(dwc);
+
+ ret = request_threaded_irq(dwc->otg_irq, dwc3_otg_irq,
+ dwc3_otg_thread_irq,
+ IRQF_SHARED, "dwc3-otg", dwc);
+ if (ret) {
+ dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
+ dwc->otg_irq, ret);
+ ret = -ENODEV;
+ return ret;
+ }
+
+ dwc3_otg_init(dwc);
+ dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
+ }
return 0;
}
void dwc3_drd_exit(struct dwc3 *dwc)
{
- extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST,
- &dwc->edev_nb);
+ unsigned long flags;
+
+ if (dwc->edev)
+ extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST,
+ &dwc->edev_nb);
+
+ cancel_work_sync(&dwc->drd_work);
+
+ /* debug user might have changed role, clean based on current role */
+ switch (dwc->current_dr_role) {
+ case DWC3_GCTL_PRTCAP_HOST:
+ dwc3_host_exit(dwc);
+ break;
+ case DWC3_GCTL_PRTCAP_DEVICE:
+ dwc3_gadget_exit(dwc);
+ dwc3_event_buffers_cleanup(dwc);
+ break;
+ case DWC3_GCTL_PRTCAP_OTG:
+ dwc3_otg_exit(dwc);
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc->desired_otg_role = DWC3_OTG_ROLE_IDLE;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ dwc3_otg_update(dwc, 1);
+ break;
+ default:
+ break;
+ }
- dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
- flush_work(&dwc->drd_work);
- dwc3_gadget_exit(dwc);
+ if (!dwc->edev)
+ free_irq(dwc->otg_irq, dwc);
}
diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c
index e54c3622eb28..cb2ee96fd3e8 100644
--- a/drivers/usb/dwc3/dwc3-of-simple.c
+++ b/drivers/usb/dwc3/dwc3-of-simple.c
@@ -27,6 +27,7 @@ struct dwc3_of_simple {
struct clk **clks;
int num_clocks;
struct reset_control *resets;
+ bool pulse_resets;
};
static int dwc3_of_simple_clk_init(struct dwc3_of_simple *simple, int count)
@@ -83,6 +84,7 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
int ret;
int i;
+ bool shared_resets = false;
simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL);
if (!simple)
@@ -91,16 +93,28 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, simple);
simple->dev = dev;
- simple->resets = of_reset_control_array_get_optional_exclusive(np);
+ if (of_device_is_compatible(np, "amlogic,meson-axg-dwc3") ||
+ of_device_is_compatible(np, "amlogic,meson-gxl-dwc3")) {
+ shared_resets = true;
+ simple->pulse_resets = true;
+ }
+
+ simple->resets = of_reset_control_array_get(np, shared_resets, true);
if (IS_ERR(simple->resets)) {
ret = PTR_ERR(simple->resets);
dev_err(dev, "failed to get device resets, err=%d\n", ret);
return ret;
}
- ret = reset_control_deassert(simple->resets);
- if (ret)
- goto err_resetc_put;
+ if (simple->pulse_resets) {
+ ret = reset_control_reset(simple->resets);
+ if (ret)
+ goto err_resetc_put;
+ } else {
+ ret = reset_control_deassert(simple->resets);
+ if (ret)
+ goto err_resetc_put;
+ }
ret = dwc3_of_simple_clk_init(simple, of_count_phandle_with_args(np,
"clocks", "#clock-cells"));
@@ -124,7 +138,8 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
return 0;
err_resetc_assert:
- reset_control_assert(simple->resets);
+ if (!simple->pulse_resets)
+ reset_control_assert(simple->resets);
err_resetc_put:
reset_control_put(simple->resets);
@@ -145,7 +160,9 @@ static int dwc3_of_simple_remove(struct platform_device *pdev)
}
simple->num_clocks = 0;
- reset_control_assert(simple->resets);
+ if (!simple->pulse_resets)
+ reset_control_assert(simple->resets);
+
reset_control_put(simple->resets);
pm_runtime_put_sync(dev);
@@ -196,6 +213,8 @@ static const struct of_device_id of_dwc3_simple_match[] = {
{ .compatible = "xlnx,zynqmp-dwc3" },
{ .compatible = "cavium,octeon-7130-usb-uctl" },
{ .compatible = "sprd,sc9860-dwc3" },
+ { .compatible = "amlogic,meson-axg-dwc3" },
+ { .compatible = "amlogic,meson-gxl-dwc3" },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, of_dwc3_simple_match);
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
index 3ba11136ebf0..c961a94d136b 100644
--- a/drivers/usb/dwc3/dwc3-pci.c
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -222,7 +222,7 @@ static int dwc3_pci_probe(struct pci_dev *pci,
ret = platform_device_add_resources(dwc->dwc3, res, ARRAY_SIZE(res));
if (ret) {
dev_err(dev, "couldn't add resources to dwc3 device\n");
- return ret;
+ goto err;
}
dwc->pci = pci;
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 18be31d5743a..5a991bca8ed7 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -814,7 +814,7 @@ out:
static void dwc3_ep0_complete_data(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
- struct dwc3_request *r = NULL;
+ struct dwc3_request *r;
struct usb_request *ur;
struct dwc3_trb *trb;
struct dwc3_ep *ep0;
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 2bda4eb1e9ac..8796a5ee9bb9 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -166,18 +166,8 @@ static void dwc3_ep_inc_deq(struct dwc3_ep *dep)
dwc3_ep_inc_trb(&dep->trb_dequeue);
}
-/**
- * dwc3_gadget_giveback - call struct usb_request's ->complete callback
- * @dep: The endpoint to whom the request belongs to
- * @req: The request we're giving back
- * @status: completion code for the request
- *
- * Must be called with controller's lock held and interrupts disabled. This
- * function will unmap @req and call its ->complete() callback to notify upper
- * layers that it has completed.
- */
-void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
- int status)
+void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep,
+ struct dwc3_request *req, int status)
{
struct dwc3 *dwc = dep->dwc;
@@ -190,18 +180,35 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
if (req->trb)
usb_gadget_unmap_request_by_dev(dwc->sysdev,
- &req->request, req->direction);
+ &req->request, req->direction);
req->trb = NULL;
-
trace_dwc3_gadget_giveback(req);
+ if (dep->number > 1)
+ pm_runtime_put(dwc->dev);
+}
+
+/**
+ * dwc3_gadget_giveback - call struct usb_request's ->complete callback
+ * @dep: The endpoint to whom the request belongs to
+ * @req: The request we're giving back
+ * @status: completion code for the request
+ *
+ * Must be called with controller's lock held and interrupts disabled. This
+ * function will unmap @req and call its ->complete() callback to notify upper
+ * layers that it has completed.
+ */
+void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
+ int status)
+{
+ struct dwc3 *dwc = dep->dwc;
+
+ dwc3_gadget_del_and_unmap_request(dep, req, status);
+
spin_unlock(&dwc->lock);
usb_gadget_giveback_request(&dep->endpoint, &req->request);
spin_lock(&dwc->lock);
-
- if (dep->number > 1)
- pm_runtime_put(dwc->dev);
}
/**
@@ -1227,7 +1234,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
if (req->trb)
memset(req->trb, 0, sizeof(struct dwc3_trb));
dep->queued_requests--;
- dwc3_gadget_giveback(dep, req, ret);
+ dwc3_gadget_del_and_unmap_request(dep, req, ret);
return ret;
}
@@ -1858,7 +1865,11 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
* bursts of data without going through any sort of endpoint throttling.
*/
reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
- reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL;
+ if (dwc3_is_usb31(dwc))
+ reg &= ~DWC31_GRXTHRCFG_PKTCNTSEL;
+ else
+ reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL;
+
dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
dwc3_gadget_setup_nump(dwc);
@@ -1950,6 +1961,7 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
struct dwc3 *dwc = gadget_to_dwc(g);
unsigned long flags;
int epnum;
+ u32 tmo_eps = 0;
spin_lock_irqsave(&dwc->lock, flags);
@@ -1960,6 +1972,7 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
for (epnum = 2; epnum < DWC3_ENDPOINTS_NUM; epnum++) {
struct dwc3_ep *dep = dwc->eps[epnum];
+ int ret;
if (!dep)
continue;
@@ -1967,9 +1980,24 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
continue;
- wait_event_lock_irq(dep->wait_end_transfer,
- !(dep->flags & DWC3_EP_END_TRANSFER_PENDING),
- dwc->lock);
+ ret = wait_event_interruptible_lock_irq_timeout(dep->wait_end_transfer,
+ !(dep->flags & DWC3_EP_END_TRANSFER_PENDING),
+ dwc->lock, msecs_to_jiffies(5));
+
+ if (ret <= 0) {
+ /* Timed out or interrupted! There's nothing much
+ * we can do so we just log here and print which
+ * endpoints timed out at the end.
+ */
+ tmo_eps |= 1 << epnum;
+ dep->flags &= DWC3_EP_END_TRANSFER_PENDING;
+ }
+ }
+
+ if (tmo_eps) {
+ dev_err(dwc->dev,
+ "end transfer timed out on endpoints 0x%x [bitmap]\n",
+ tmo_eps);
}
out:
@@ -2023,7 +2051,10 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g,
reg |= DWC3_DCFG_SUPERSPEED;
break;
case USB_SPEED_SUPER_PLUS:
- reg |= DWC3_DCFG_SUPERSPEED_PLUS;
+ if (dwc3_is_usb31(dwc))
+ reg |= DWC3_DCFG_SUPERSPEED_PLUS;
+ else
+ reg |= DWC3_DCFG_SUPERSPEED;
break;
default:
dev_err(dwc->dev, "invalid speed (%d)\n", speed);
@@ -2101,7 +2132,10 @@ static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 total)
mdwidth /= 8;
size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(num));
- size = DWC3_GTXFIFOSIZ_TXFDEF(size);
+ if (dwc3_is_usb31(dwc))
+ size = DWC31_GTXFIFOSIZ_TXFDEF(size);
+ else
+ size = DWC3_GTXFIFOSIZ_TXFDEF(size);
/* FIFO Depth is in MDWDITH bytes. Multiply */
size *= mdwidth;
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 77c7ecca816a..63a7cb87514a 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -1422,11 +1422,12 @@ static int count_ext_compat(struct usb_configuration *c)
return res;
}
-static void fill_ext_compat(struct usb_configuration *c, u8 *buf)
+static int fill_ext_compat(struct usb_configuration *c, u8 *buf)
{
int i, count;
count = 16;
+ buf += 16;
for (i = 0; i < c->next_interface_id; ++i) {
struct usb_function *f;
int j;
@@ -1449,10 +1450,12 @@ static void fill_ext_compat(struct usb_configuration *c, u8 *buf)
buf += 23;
}
count += 24;
- if (count >= 4096)
- return;
+ if (count + 24 >= USB_COMP_EP0_OS_DESC_BUFSIZ)
+ return count;
}
}
+
+ return count;
}
static int count_ext_prop(struct usb_configuration *c, int interface)
@@ -1497,25 +1500,21 @@ static int fill_ext_prop(struct usb_configuration *c, int interface, u8 *buf)
struct usb_os_desc *d;
struct usb_os_desc_ext_prop *ext_prop;
int j, count, n, ret;
- u8 *start = buf;
f = c->interface[interface];
+ count = 10; /* header length */
+ buf += 10;
for (j = 0; j < f->os_desc_n; ++j) {
if (interface != f->os_desc_table[j].if_id)
continue;
d = f->os_desc_table[j].os_desc;
if (d)
list_for_each_entry(ext_prop, &d->ext_prop, entry) {
- /* 4kB minus header length */
- n = buf - start;
- if (n >= 4086)
- return 0;
-
- count = ext_prop->data_len +
+ n = ext_prop->data_len +
ext_prop->name_len + 14;
- if (count > 4086 - n)
- return -EINVAL;
- usb_ext_prop_put_size(buf, count);
+ if (count + n >= USB_COMP_EP0_OS_DESC_BUFSIZ)
+ return count;
+ usb_ext_prop_put_size(buf, n);
usb_ext_prop_put_type(buf, ext_prop->type);
ret = usb_ext_prop_put_name(buf, ext_prop->name,
ext_prop->name_len);
@@ -1541,11 +1540,12 @@ static int fill_ext_prop(struct usb_configuration *c, int interface, u8 *buf)
default:
return -EINVAL;
}
- buf += count;
+ buf += n;
+ count += n;
}
}
- return 0;
+ return count;
}
/*
@@ -1827,6 +1827,7 @@ unknown:
req->complete = composite_setup_complete;
buf = req->buf;
os_desc_cfg = cdev->os_desc_config;
+ w_length = min_t(u16, w_length, USB_COMP_EP0_OS_DESC_BUFSIZ);
memset(buf, 0, w_length);
buf[5] = 0x01;
switch (ctrl->bRequestType & USB_RECIP_MASK) {
@@ -1834,24 +1835,16 @@ unknown:
if (w_index != 0x4 || (w_value >> 8))
break;
buf[6] = w_index;
- if (w_length == 0x10) {
- /* Number of ext compat interfaces */
- count = count_ext_compat(os_desc_cfg);
- buf[8] = count;
- count *= 24; /* 24 B/ext compat desc */
- count += 16; /* header */
- put_unaligned_le32(count, buf);
- value = w_length;
- } else {
- /* "extended compatibility ID"s */
- count = count_ext_compat(os_desc_cfg);
- buf[8] = count;
- count *= 24; /* 24 B/ext compat desc */
- count += 16; /* header */
- put_unaligned_le32(count, buf);
- buf += 16;
- fill_ext_compat(os_desc_cfg, buf);
- value = w_length;
+ /* Number of ext compat interfaces */
+ count = count_ext_compat(os_desc_cfg);
+ buf[8] = count;
+ count *= 24; /* 24 B/ext compat desc */
+ count += 16; /* header */
+ put_unaligned_le32(count, buf);
+ value = w_length;
+ if (w_length > 0x10) {
+ value = fill_ext_compat(os_desc_cfg, buf);
+ value = min_t(u16, w_length, value);
}
break;
case USB_RECIP_INTERFACE:
@@ -1859,47 +1852,23 @@ unknown:
break;
interface = w_value & 0xFF;
buf[6] = w_index;
- if (w_length == 0x0A) {
- count = count_ext_prop(os_desc_cfg,
- interface);
- put_unaligned_le16(count, buf + 8);
- count = len_ext_prop(os_desc_cfg,
- interface);
- put_unaligned_le32(count, buf);
-
- value = w_length;
- } else {
- count = count_ext_prop(os_desc_cfg,
- interface);
- put_unaligned_le16(count, buf + 8);
- count = len_ext_prop(os_desc_cfg,
- interface);
- put_unaligned_le32(count, buf);
- buf += 10;
+ count = count_ext_prop(os_desc_cfg,
+ interface);
+ put_unaligned_le16(count, buf + 8);
+ count = len_ext_prop(os_desc_cfg,
+ interface);
+ put_unaligned_le32(count, buf);
+ value = w_length;
+ if (w_length > 0x0A) {
value = fill_ext_prop(os_desc_cfg,
interface, buf);
- if (value < 0)
- return value;
-
- value = w_length;
+ if (value >= 0)
+ value = min_t(u16, w_length, value);
}
break;
}
- if (value >= 0) {
- req->length = value;
- req->context = cdev;
- req->zero = value < w_length;
- value = composite_ep0_queue(cdev, req,
- GFP_ATOMIC);
- if (value < 0) {
- DBG(cdev, "ep_queue --> %d\n", value);
- req->status = 0;
- composite_setup_complete(gadget->ep0,
- req);
- }
- }
- return value;
+ goto check_value;
}
VDBG(cdev,
@@ -1973,6 +1942,7 @@ try_fun_setup:
goto done;
}
+check_value:
/* respond with data transfer before status phase? */
if (value >= 0 && value != USB_GADGET_DELAYED_STATUS) {
req->length = value;
@@ -2156,8 +2126,8 @@ int composite_os_desc_req_prepare(struct usb_composite_dev *cdev,
goto end;
}
- /* OS feature descriptor length <= 4kB */
- cdev->os_desc_req->buf = kmalloc(4096, GFP_KERNEL);
+ cdev->os_desc_req->buf = kmalloc(USB_COMP_EP0_OS_DESC_BUFSIZ,
+ GFP_KERNEL);
if (!cdev->os_desc_req->buf) {
ret = -ENOMEM;
usb_ep_free_request(ep0, cdev->os_desc_req);
@@ -2172,6 +2142,7 @@ end:
void composite_dev_cleanup(struct usb_composite_dev *cdev)
{
struct usb_gadget_string_container *uc, *tmp;
+ struct usb_ep *ep, *tmp_ep;
list_for_each_entry_safe(uc, tmp, &cdev->gstrings, list) {
list_del(&uc->list);
@@ -2193,6 +2164,21 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev)
}
cdev->next_string_id = 0;
device_remove_file(&cdev->gadget->dev, &dev_attr_suspended);
+
+ /*
+ * Some UDC backends have a dynamic EP allocation scheme.
+ *
+ * In that case, the dispose() callback is used to notify the
+ * backend that the EPs are no longer in use.
+ *
+ * Note: The UDC backend can remove the EP from the ep_list as
+ * a result, so we need to use the _safe list iterator.
+ */
+ list_for_each_entry_safe(ep, tmp_ep,
+ &cdev->gadget->ep_list, ep_list) {
+ if (ep->ops->dispose)
+ ep->ops->dispose(ep);
+ }
}
static int composite_bind(struct usb_gadget *gadget,
diff --git a/drivers/usb/gadget/function/f_eem.c b/drivers/usb/gadget/function/f_eem.c
index 37557651b600..c13befa31110 100644
--- a/drivers/usb/gadget/function/f_eem.c
+++ b/drivers/usb/gadget/function/f_eem.c
@@ -507,7 +507,6 @@ static int eem_unwrap(struct gether *port,
0,
GFP_ATOMIC);
if (unlikely(!skb3)) {
- DBG(cdev, "unable to realign EEM packet\n");
dev_kfree_skb_any(skb2);
continue;
}
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index d2428a9e8900..0294e4f18873 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -758,9 +758,13 @@ static void ffs_user_copy_worker(struct work_struct *work)
bool kiocb_has_eventfd = io_data->kiocb->ki_flags & IOCB_EVENTFD;
if (io_data->read && ret > 0) {
+ mm_segment_t oldfs = get_fs();
+
+ set_fs(USER_DS);
use_mm(io_data->mm);
ret = ffs_copy_to_iter(io_data->buf, ret, &io_data->data);
unuse_mm(io_data->mm);
+ set_fs(oldfs);
}
io_data->kiocb->ki_complete(io_data->kiocb, ret, ret);
@@ -3238,7 +3242,7 @@ static int ffs_func_setup(struct usb_function *f,
__ffs_event_add(ffs, FUNCTIONFS_SETUP);
spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags);
- return 0;
+ return USB_GADGET_DELAYED_STATUS;
}
static bool ffs_func_req_match(struct usb_function *f,
diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index 4eb96b91cc40..e8f35db42394 100644
--- a/drivers/usb/gadget/function/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -404,7 +404,8 @@ static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (err) {
ERROR(midi, "%s: couldn't enqueue request: %d\n",
midi->out_ep->name, err);
- free_ep_req(midi->out_ep, req);
+ if (req->buf != NULL)
+ free_ep_req(midi->out_ep, req);
return err;
}
}
diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c
index da81cf16b850..d78dbb73bde8 100644
--- a/drivers/usb/gadget/function/f_tcm.c
+++ b/drivers/usb/gadget/function/f_tcm.c
@@ -1094,7 +1094,7 @@ static int usbg_submit_command(struct f_uas *fu,
struct command_iu *cmd_iu = cmdbuf;
struct usbg_cmd *cmd;
struct usbg_tpg *tpg = fu->tpg;
- struct tcm_usbg_nexus *tv_nexus = tpg->tpg_nexus;
+ struct tcm_usbg_nexus *tv_nexus;
u32 cmd_len;
u16 scsi_tag;
diff --git a/drivers/usb/gadget/function/f_uac1_legacy.c b/drivers/usb/gadget/function/f_uac1_legacy.c
index 04f4b2862256..24c086bcdeaa 100644
--- a/drivers/usb/gadget/function/f_uac1_legacy.c
+++ b/drivers/usb/gadget/function/f_uac1_legacy.c
@@ -4,8 +4,6 @@
*
* Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
* Copyright (C) 2008 Analog Devices, Inc
- *
- * Enter bugs at http://blackfin.uclinux.org/
*/
#include <linux/slab.h>
diff --git a/drivers/usb/gadget/function/u_uac1_legacy.c b/drivers/usb/gadget/function/u_uac1_legacy.c
index cbc868d117af..5393e5c37a4b 100644
--- a/drivers/usb/gadget/function/u_uac1_legacy.c
+++ b/drivers/usb/gadget/function/u_uac1_legacy.c
@@ -4,8 +4,6 @@
*
* Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
* Copyright (C) 2008 Analog Devices, Inc
- *
- * Enter bugs at http://blackfin.uclinux.org/
*/
#include <linux/kernel.h>
diff --git a/drivers/usb/gadget/function/u_uac1_legacy.h b/drivers/usb/gadget/function/u_uac1_legacy.h
index dd69e408a3d9..5c1bdf46fe32 100644
--- a/drivers/usb/gadget/function/u_uac1_legacy.h
+++ b/drivers/usb/gadget/function/u_uac1_legacy.h
@@ -4,8 +4,6 @@
*
* Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
* Copyright (C) 2008 Analog Devices, Inc
- *
- * Enter bugs at http://blackfin.uclinux.org/
*/
#ifndef __U_UAC1_LEGACY_H
diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c
index 7b11dce98b94..dd81fd538cb8 100644
--- a/drivers/usb/gadget/legacy/audio.c
+++ b/drivers/usb/gadget/legacy/audio.c
@@ -4,8 +4,6 @@
*
* Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
* Copyright (C) 2008 Analog Devices, Inc
- *
- * Enter bugs at http://blackfin.uclinux.org/
*/
/* #define VERBOSE_DEBUG */
diff --git a/drivers/usb/gadget/legacy/mass_storage.c b/drivers/usb/gadget/legacy/mass_storage.c
index ef3d25259b0e..fd5595ac5bf7 100644
--- a/drivers/usb/gadget/legacy/mass_storage.c
+++ b/drivers/usb/gadget/legacy/mass_storage.c
@@ -225,7 +225,7 @@ static int msg_unbind(struct usb_composite_dev *cdev)
static struct usb_composite_driver msg_driver = {
.name = "g_mass_storage",
.dev = &msg_device_desc,
- .max_speed = USB_SPEED_SUPER,
+ .max_speed = USB_SPEED_SUPER_PLUS,
.needs_serial = 1,
.strings = dev_strings,
.bind = msg_bind,
diff --git a/drivers/usb/gadget/u_f.h b/drivers/usb/gadget/u_f.h
index c3fbef2bb5db..09f90447fed5 100644
--- a/drivers/usb/gadget/u_f.h
+++ b/drivers/usb/gadget/u_f.h
@@ -61,7 +61,9 @@ struct usb_request *alloc_ep_req(struct usb_ep *ep, size_t len);
/* Frees a usb_request previously allocated by alloc_ep_req() */
static inline void free_ep_req(struct usb_ep *ep, struct usb_request *req)
{
+ WARN_ON(req->buf == NULL);
kfree(req->buf);
+ req->buf = NULL;
usb_ep_free_request(ep, req);
}
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c
index 075eaaa8a408..27c16399c7e8 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.c
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.c
@@ -23,7 +23,8 @@
#include <linux/usb/atmel_usba_udc.h>
#include <linux/delay.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
+#include <linux/irq.h>
+#include <linux/gpio/consumer.h>
#include "atmel_usba_udc.h"
#define USBA_VBUS_IRQFLAGS (IRQF_ONESHOT \
@@ -415,8 +416,8 @@ static inline void usba_int_enb_set(struct usba_udc *udc, u32 val)
static int vbus_is_present(struct usba_udc *udc)
{
- if (gpio_is_valid(udc->vbus_pin))
- return gpio_get_value(udc->vbus_pin) ^ udc->vbus_pin_inverted;
+ if (udc->vbus_pin)
+ return gpiod_get_value(udc->vbus_pin) ^ udc->vbus_pin_inverted;
/* No Vbus detection: Assume always present */
return 1;
@@ -1975,8 +1976,8 @@ static int atmel_usba_start(struct usb_gadget *gadget,
mutex_lock(&udc->vbus_mutex);
- if (gpio_is_valid(udc->vbus_pin))
- enable_irq(gpio_to_irq(udc->vbus_pin));
+ if (udc->vbus_pin)
+ enable_irq(gpiod_to_irq(udc->vbus_pin));
/* If Vbus is present, enable the controller and wait for reset */
udc->vbus_prev = vbus_is_present(udc);
@@ -1990,8 +1991,8 @@ static int atmel_usba_start(struct usb_gadget *gadget,
return 0;
err:
- if (gpio_is_valid(udc->vbus_pin))
- disable_irq(gpio_to_irq(udc->vbus_pin));
+ if (udc->vbus_pin)
+ disable_irq(gpiod_to_irq(udc->vbus_pin));
mutex_unlock(&udc->vbus_mutex);
@@ -2006,8 +2007,8 @@ static int atmel_usba_stop(struct usb_gadget *gadget)
{
struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget);
- if (gpio_is_valid(udc->vbus_pin))
- disable_irq(gpio_to_irq(udc->vbus_pin));
+ if (udc->vbus_pin)
+ disable_irq(gpiod_to_irq(udc->vbus_pin));
if (fifo_mode == 0)
udc->configured_ep = 1;
@@ -2019,7 +2020,6 @@ static int atmel_usba_stop(struct usb_gadget *gadget)
return 0;
}
-#ifdef CONFIG_OF
static void at91sam9rl_toggle_bias(struct usba_udc *udc, int is_on)
{
regmap_update_bits(udc->pmc, AT91_CKGR_UCKR, AT91_PMC_BIASEN,
@@ -2055,7 +2055,6 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
{
u32 val;
const char *name;
- enum of_gpio_flags flags;
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *match;
struct device_node *pp;
@@ -2075,9 +2074,9 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
udc->num_ep = 0;
- udc->vbus_pin = of_get_named_gpio_flags(np, "atmel,vbus-gpio", 0,
- &flags);
- udc->vbus_pin_inverted = (flags & OF_GPIO_ACTIVE_LOW) ? 1 : 0;
+ udc->vbus_pin = devm_gpiod_get_optional(&pdev->dev, "atmel,vbus",
+ GPIOD_IN);
+ udc->vbus_pin_inverted = gpiod_is_active_low(udc->vbus_pin);
if (fifo_mode == 0) {
pp = NULL;
@@ -2204,75 +2203,10 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
err:
return ERR_PTR(ret);
}
-#else
-static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
- struct usba_udc *udc)
-{
- return ERR_PTR(-ENOSYS);
-}
-#endif
-
-static struct usba_ep * usba_udc_pdata(struct platform_device *pdev,
- struct usba_udc *udc)
-{
- struct usba_platform_data *pdata = dev_get_platdata(&pdev->dev);
- struct usba_ep *eps;
- int i;
-
- if (!pdata)
- return ERR_PTR(-ENXIO);
-
- eps = devm_kzalloc(&pdev->dev, sizeof(struct usba_ep) * pdata->num_ep,
- GFP_KERNEL);
- if (!eps)
- return ERR_PTR(-ENOMEM);
-
- udc->gadget.ep0 = &eps[0].ep;
-
- udc->vbus_pin = pdata->vbus_pin;
- udc->vbus_pin_inverted = pdata->vbus_pin_inverted;
- udc->num_ep = pdata->num_ep;
-
- INIT_LIST_HEAD(&eps[0].ep.ep_list);
-
- for (i = 0; i < pdata->num_ep; i++) {
- struct usba_ep *ep = &eps[i];
-
- ep->ep_regs = udc->regs + USBA_EPT_BASE(i);
- ep->dma_regs = udc->regs + USBA_DMA_BASE(i);
- ep->fifo = udc->fifo + USBA_FIFO_BASE(i);
- ep->ep.ops = &usba_ep_ops;
- ep->ep.name = pdata->ep[i].name;
- ep->fifo_size = pdata->ep[i].fifo_size;
- usb_ep_set_maxpacket_limit(&ep->ep, ep->fifo_size);
- ep->udc = udc;
- INIT_LIST_HEAD(&ep->queue);
- ep->nr_banks = pdata->ep[i].nr_banks;
- ep->index = pdata->ep[i].index;
- ep->can_dma = pdata->ep[i].can_dma;
- ep->can_isoc = pdata->ep[i].can_isoc;
-
- if (i == 0) {
- ep->ep.caps.type_control = true;
- } else {
- ep->ep.caps.type_iso = ep->can_isoc;
- ep->ep.caps.type_bulk = true;
- ep->ep.caps.type_int = true;
- }
-
- ep->ep.caps.dir_in = true;
- ep->ep.caps.dir_out = true;
-
- if (i)
- list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
- }
-
- return eps;
-}
static int usba_udc_probe(struct platform_device *pdev)
{
- struct resource *regs, *fifo;
+ struct resource *res;
struct clk *pclk, *hclk;
struct usba_udc *udc;
int irq, ret, i;
@@ -2284,10 +2218,18 @@ static int usba_udc_probe(struct platform_device *pdev)
udc->gadget = usba_gadget_template;
INIT_LIST_HEAD(&udc->gadget.ep_list);
- regs = platform_get_resource(pdev, IORESOURCE_MEM, CTRL_IOMEM_ID);
- fifo = platform_get_resource(pdev, IORESOURCE_MEM, FIFO_IOMEM_ID);
- if (!regs || !fifo)
- return -ENXIO;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, CTRL_IOMEM_ID);
+ udc->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(udc->regs))
+ return PTR_ERR(udc->regs);
+ dev_info(&pdev->dev, "MMIO registers at %pR mapped at %p\n",
+ res, udc->regs);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, FIFO_IOMEM_ID);
+ udc->fifo = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(udc->fifo))
+ return PTR_ERR(udc->fifo);
+ dev_info(&pdev->dev, "FIFO at %pR mapped at %p\n", res, udc->fifo);
irq = platform_get_irq(pdev, 0);
if (irq < 0)
@@ -2305,23 +2247,6 @@ static int usba_udc_probe(struct platform_device *pdev)
udc->pdev = pdev;
udc->pclk = pclk;
udc->hclk = hclk;
- udc->vbus_pin = -ENODEV;
-
- ret = -ENOMEM;
- udc->regs = devm_ioremap(&pdev->dev, regs->start, resource_size(regs));
- if (!udc->regs) {
- dev_err(&pdev->dev, "Unable to map I/O memory, aborting.\n");
- return ret;
- }
- dev_info(&pdev->dev, "MMIO registers at 0x%08lx mapped at %p\n",
- (unsigned long)regs->start, udc->regs);
- udc->fifo = devm_ioremap(&pdev->dev, fifo->start, resource_size(fifo));
- if (!udc->fifo) {
- dev_err(&pdev->dev, "Unable to map FIFO, aborting.\n");
- return ret;
- }
- dev_info(&pdev->dev, "FIFO at 0x%08lx mapped at %p\n",
- (unsigned long)fifo->start, udc->fifo);
platform_set_drvdata(pdev, udc);
@@ -2335,10 +2260,7 @@ static int usba_udc_probe(struct platform_device *pdev)
usba_writel(udc, CTRL, USBA_DISABLE_MASK);
clk_disable_unprepare(pclk);
- if (pdev->dev.of_node)
- udc->usba_ep = atmel_udc_of_init(pdev, udc);
- else
- udc->usba_ep = usba_udc_pdata(pdev, udc);
+ udc->usba_ep = atmel_udc_of_init(pdev, udc);
toggle_bias(udc, 0);
@@ -2354,24 +2276,18 @@ static int usba_udc_probe(struct platform_device *pdev)
}
udc->irq = irq;
- if (gpio_is_valid(udc->vbus_pin)) {
- if (!devm_gpio_request(&pdev->dev, udc->vbus_pin, "atmel_usba_udc")) {
- irq_set_status_flags(gpio_to_irq(udc->vbus_pin),
- IRQ_NOAUTOEN);
- ret = devm_request_threaded_irq(&pdev->dev,
- gpio_to_irq(udc->vbus_pin), NULL,
+ if (udc->vbus_pin) {
+ irq_set_status_flags(gpiod_to_irq(udc->vbus_pin), IRQ_NOAUTOEN);
+ ret = devm_request_threaded_irq(&pdev->dev,
+ gpiod_to_irq(udc->vbus_pin), NULL,
usba_vbus_irq_thread, USBA_VBUS_IRQFLAGS,
"atmel_usba_udc", udc);
if (ret) {
- udc->vbus_pin = -ENODEV;
+ udc->vbus_pin = NULL;
dev_warn(&udc->pdev->dev,
"failed to request vbus irq; "
"assuming always on\n");
}
- } else {
- /* gpio_request fail so use -EINVAL for gpio_is_valid */
- udc->vbus_pin = -EINVAL;
- }
}
ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
@@ -2423,9 +2339,9 @@ static int usba_udc_suspend(struct device *dev)
* Device may wake up. We stay clocked if we failed
* to request vbus irq, assuming always on.
*/
- if (gpio_is_valid(udc->vbus_pin)) {
+ if (udc->vbus_pin) {
usba_stop(udc);
- enable_irq_wake(gpio_to_irq(udc->vbus_pin));
+ enable_irq_wake(gpiod_to_irq(udc->vbus_pin));
}
out:
@@ -2441,8 +2357,8 @@ static int usba_udc_resume(struct device *dev)
if (!udc->driver)
return 0;
- if (device_may_wakeup(dev) && gpio_is_valid(udc->vbus_pin))
- disable_irq_wake(gpio_to_irq(udc->vbus_pin));
+ if (device_may_wakeup(dev) && udc->vbus_pin)
+ disable_irq_wake(gpiod_to_irq(udc->vbus_pin));
/* If Vbus is present, enable the controller and wait for reset */
mutex_lock(&udc->vbus_mutex);
@@ -2462,7 +2378,7 @@ static struct platform_driver udc_driver = {
.driver = {
.name = "atmel_usba_udc",
.pm = &usba_udc_pm_ops,
- .of_match_table = of_match_ptr(atmel_udc_dt_ids),
+ .of_match_table = atmel_udc_dt_ids,
},
};
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h
index 860a00a6fdd0..969ce8f3c3e2 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.h
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.h
@@ -7,6 +7,8 @@
#ifndef __LINUX_USB_GADGET_USBA_UDC_H__
#define __LINUX_USB_GADGET_USBA_UDC_H__
+#include <linux/gpio/consumer.h>
+
/* USB register offsets */
#define USBA_CTRL 0x0000
#define USBA_FNUM 0x0004
@@ -323,7 +325,7 @@ struct usba_udc {
struct platform_device *pdev;
const struct usba_udc_errata *errata;
int irq;
- int vbus_pin;
+ struct gpio_desc *vbus_pin;
int vbus_pin_inverted;
int num_ep;
int configured_ep;
diff --git a/drivers/usb/gadget/udc/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c
index 465ccd1104de..3a8df8601074 100644
--- a/drivers/usb/gadget/udc/bcm63xx_udc.c
+++ b/drivers/usb/gadget/udc/bcm63xx_udc.c
@@ -2158,6 +2158,7 @@ static int bcm63xx_usbd_dbg_show(struct seq_file *s, void *p)
return 0;
}
+DEFINE_SHOW_ATTRIBUTE(bcm63xx_usbd_dbg);
/*
* bcm63xx_iudma_dbg_show - Show IUDMA status and descriptors.
@@ -2238,33 +2239,7 @@ static int bcm63xx_iudma_dbg_show(struct seq_file *s, void *p)
return 0;
}
-
-static int bcm63xx_usbd_dbg_open(struct inode *inode, struct file *file)
-{
- return single_open(file, bcm63xx_usbd_dbg_show, inode->i_private);
-}
-
-static int bcm63xx_iudma_dbg_open(struct inode *inode, struct file *file)
-{
- return single_open(file, bcm63xx_iudma_dbg_show, inode->i_private);
-}
-
-static const struct file_operations usbd_dbg_fops = {
- .owner = THIS_MODULE,
- .open = bcm63xx_usbd_dbg_open,
- .llseek = seq_lseek,
- .read = seq_read,
- .release = single_release,
-};
-
-static const struct file_operations iudma_dbg_fops = {
- .owner = THIS_MODULE,
- .open = bcm63xx_iudma_dbg_open,
- .llseek = seq_lseek,
- .read = seq_read,
- .release = single_release,
-};
-
+DEFINE_SHOW_ATTRIBUTE(bcm63xx_iudma_dbg);
/**
* bcm63xx_udc_init_debugfs - Create debugfs entries.
@@ -2282,11 +2257,11 @@ static void bcm63xx_udc_init_debugfs(struct bcm63xx_udc *udc)
goto err_root;
usbd = debugfs_create_file("usbd", 0400, root, udc,
- &usbd_dbg_fops);
+ &bcm63xx_usbd_dbg_fops);
if (!usbd)
goto err_usbd;
iudma = debugfs_create_file("iudma", 0400, root, udc,
- &iudma_dbg_fops);
+ &bcm63xx_iudma_dbg_fops);
if (!iudma)
goto err_iudma;
diff --git a/drivers/usb/gadget/udc/bdc/bdc_ep.c b/drivers/usb/gadget/udc/bdc/bdc_ep.c
index f40d4c13cfa4..03149b9d7ea7 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_ep.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_ep.c
@@ -151,7 +151,7 @@ static int ep_bd_list_alloc(struct bdc_ep *ep)
if (!bd_table)
goto fail;
- bd_table->start_bd = dma_pool_alloc(bdc->bd_table_pool,
+ bd_table->start_bd = dma_pool_zalloc(bdc->bd_table_pool,
GFP_ATOMIC,
&dma);
if (!bd_table->start_bd) {
@@ -167,7 +167,6 @@ static int ep_bd_list_alloc(struct bdc_ep *ep)
(unsigned long long)bd_table->dma, prev_table);
ep->bd_list.bd_table_array[index] = bd_table;
- memset(bd_table->start_bd, 0, bd_p_tab * sizeof(struct bdc_bd));
if (prev_table)
chain_table(prev_table, bd_table, bd_p_tab);
diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c
index 1f8b19d9cf97..842814bc0e4f 100644
--- a/drivers/usb/gadget/udc/core.c
+++ b/drivers/usb/gadget/udc/core.c
@@ -238,6 +238,9 @@ EXPORT_SYMBOL_GPL(usb_ep_free_request);
* arranges to poll once per interval, and the gadget driver usually will
* have queued some data to transfer at that time.
*
+ * Note that @req's ->complete() callback must never be called from
+ * within usb_ep_queue() as that can create deadlock situations.
+ *
* Returns zero, or a negative error code. Endpoints that are not enabled
* report errors; errors will also be
* reported when the usb peripheral is disconnected.
@@ -1482,7 +1485,7 @@ ssize_t name##_show(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
struct usb_udc *udc = container_of(dev, struct usb_udc, dev); \
- return snprintf(buf, PAGE_SIZE, "%s\n", \
+ return scnprintf(buf, PAGE_SIZE, "%s\n", \
usb_speed_string(udc->gadget->param)); \
} \
static DEVICE_ATTR_RO(name)
@@ -1497,7 +1500,7 @@ ssize_t name##_show(struct device *dev, \
struct usb_udc *udc = container_of(dev, struct usb_udc, dev); \
struct usb_gadget *gadget = udc->gadget; \
\
- return snprintf(buf, PAGE_SIZE, "%d\n", gadget->name); \
+ return scnprintf(buf, PAGE_SIZE, "%d\n", gadget->name); \
} \
static DEVICE_ATTR_RO(name)
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index e744d4b7bfed..baf72f95f0f1 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -2366,7 +2366,7 @@ static inline ssize_t show_urb(char *buf, size_t size, struct urb *urb)
{
int ep = usb_pipeendpoint(urb->pipe);
- return snprintf(buf, size,
+ return scnprintf(buf, size,
"urb/%p %s ep%d%s%s len %d/%d\n",
urb,
({ char *s;
diff --git a/drivers/usb/gadget/udc/goku_udc.h b/drivers/usb/gadget/udc/goku_udc.h
index 26601bf4e7a9..70023d401079 100644
--- a/drivers/usb/gadget/udc/goku_udc.h
+++ b/drivers/usb/gadget/udc/goku_udc.h
@@ -25,7 +25,7 @@ struct goku_udc_regs {
# define INT_EP1DATASET 0x00040
# define INT_EP2DATASET 0x00080
# define INT_EP3DATASET 0x00100
-#define INT_EPnNAK(n) (0x00100 < (n)) /* 0 < n < 4 */
+#define INT_EPnNAK(n) (0x00100 << (n)) /* 0 < n < 4 */
# define INT_EP1NAK 0x00200
# define INT_EP2NAK 0x00400
# define INT_EP3NAK 0x00800
diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c
index b3fb1bbdb854..ca83c15d8ea4 100644
--- a/drivers/usb/gadget/udc/gr_udc.c
+++ b/drivers/usb/gadget/udc/gr_udc.c
@@ -179,8 +179,7 @@ static void gr_seq_ep_show(struct seq_file *seq, struct gr_ep *ep)
seq_puts(seq, "\n");
}
-
-static int gr_seq_show(struct seq_file *seq, void *v)
+static int gr_dfs_show(struct seq_file *seq, void *v)
{
struct gr_udc *dev = seq->private;
u32 control = gr_read32(&dev->regs->control);
@@ -203,19 +202,7 @@ static int gr_seq_show(struct seq_file *seq, void *v)
return 0;
}
-
-static int gr_dfs_open(struct inode *inode, struct file *file)
-{
- return single_open(file, gr_seq_show, inode->i_private);
-}
-
-static const struct file_operations gr_dfs_fops = {
- .owner = THIS_MODULE,
- .open = gr_dfs_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(gr_dfs);
static void gr_dfs_create(struct gr_udc *dev)
{
diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c
index 0e3f5faa000e..d4be53559f2e 100644
--- a/drivers/usb/gadget/udc/pxa25x_udc.c
+++ b/drivers/usb/gadget/udc/pxa25x_udc.c
@@ -1233,8 +1233,7 @@ static const struct usb_gadget_ops pxa25x_udc_ops = {
#ifdef CONFIG_USB_GADGET_DEBUG_FS
-static int
-udc_seq_show(struct seq_file *m, void *_d)
+static int udc_debug_show(struct seq_file *m, void *_d)
{
struct pxa25x_udc *dev = m->private;
unsigned long flags;
@@ -1335,25 +1334,12 @@ done:
local_irq_restore(flags);
return 0;
}
-
-static int
-udc_debugfs_open(struct inode *inode, struct file *file)
-{
- return single_open(file, udc_seq_show, inode->i_private);
-}
-
-static const struct file_operations debug_fops = {
- .open = udc_debugfs_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
-};
+DEFINE_SHOW_ATTRIBUTE(udc_debug);
#define create_debug_files(dev) \
do { \
dev->debugfs_udc = debugfs_create_file(dev->gadget.name, \
- S_IRUGO, NULL, dev, &debug_fops); \
+ S_IRUGO, NULL, dev, &udc_debug_fops); \
} while (0)
#define remove_debug_files(dev) debugfs_remove(dev->debugfs_udc)
diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c
index fadcf2653c3d..a58242e901df 100644
--- a/drivers/usb/gadget/udc/pxa27x_udc.c
+++ b/drivers/usb/gadget/udc/pxa27x_udc.c
@@ -131,6 +131,7 @@ static int state_dbg_show(struct seq_file *s, void *p)
return 0;
}
+DEFINE_SHOW_ATTRIBUTE(state_dbg);
static int queues_dbg_show(struct seq_file *s, void *p)
{
@@ -163,6 +164,7 @@ static int queues_dbg_show(struct seq_file *s, void *p)
return 0;
}
+DEFINE_SHOW_ATTRIBUTE(queues_dbg);
static int eps_dbg_show(struct seq_file *s, void *p)
{
@@ -199,45 +201,7 @@ static int eps_dbg_show(struct seq_file *s, void *p)
return 0;
}
-
-static int eps_dbg_open(struct inode *inode, struct file *file)
-{
- return single_open(file, eps_dbg_show, inode->i_private);
-}
-
-static int queues_dbg_open(struct inode *inode, struct file *file)
-{
- return single_open(file, queues_dbg_show, inode->i_private);
-}
-
-static int state_dbg_open(struct inode *inode, struct file *file)
-{
- return single_open(file, state_dbg_show, inode->i_private);
-}
-
-static const struct file_operations state_dbg_fops = {
- .owner = THIS_MODULE,
- .open = state_dbg_open,
- .llseek = seq_lseek,
- .read = seq_read,
- .release = single_release,
-};
-
-static const struct file_operations queues_dbg_fops = {
- .owner = THIS_MODULE,
- .open = queues_dbg_open,
- .llseek = seq_lseek,
- .read = seq_read,
- .release = single_release,
-};
-
-static const struct file_operations eps_dbg_fops = {
- .owner = THIS_MODULE,
- .open = eps_dbg_open,
- .llseek = seq_lseek,
- .read = seq_read,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(eps_dbg);
static void pxa_init_debugfs(struct pxa_udc *udc)
{
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 4fcfb3084b36..5d958da8e1bc 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -293,19 +293,6 @@ config USB_CNS3XXX_EHCI
It is needed for high-speed (480Mbit/sec) USB 2.0 device
support.
-config USB_EHCI_ATH79
- bool "EHCI support for AR7XXX/AR9XXX SoCs (DEPRECATED)"
- depends on (SOC_AR71XX || SOC_AR724X || SOC_AR913X || SOC_AR933X)
- select USB_EHCI_ROOT_HUB_TT
- select USB_EHCI_HCD_PLATFORM
- default y
- ---help---
- This option is deprecated now and the driver was removed, use
- USB_EHCI_HCD_PLATFORM instead.
-
- Enables support for the built-in EHCI controller present
- on the Atheros AR7XXX/AR9XXX SoCs.
-
config USB_EHCI_HCD_PLATFORM
tristate "Generic EHCI driver for a platform device"
default n
@@ -360,6 +347,7 @@ config USB_ISP116X_HCD
config USB_ISP1362_HCD
tristate "ISP1362 HCD support"
depends on HAS_IOMEM
+ depends on COMPILE_TEST # nothing uses this
---help---
Supports the Philips ISP1362 chip as a host controller
@@ -489,18 +477,6 @@ config USB_OHCI_HCD_DAVINCI
controller. This driver cannot currently be a loadable
module because it lacks a proper PHY abstraction.
-config USB_OHCI_ATH79
- bool "USB OHCI support for the Atheros AR71XX/AR7240 SoCs (DEPRECATED)"
- depends on (SOC_AR71XX || SOC_AR724X)
- select USB_OHCI_HCD_PLATFORM
- default y
- help
- This option is deprecated now and the driver was removed, use
- USB_OHCI_HCD_PLATFORM instead.
-
- Enables support for the built-in OHCI controller present on the
- Atheros AR71XX/AR7240 SoCs.
-
config USB_OHCI_HCD_PPC_OF_BE
bool "OHCI support for OF platform bus (big endian)"
depends on PPC
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 4ede4ce12366..8a8cffe0b445 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -11,7 +11,7 @@ fhci-y += fhci-mem.o fhci-tds.o fhci-sched.o
fhci-$(CONFIG_FHCI_DEBUG) += fhci-dbg.o
-xhci-hcd-y := xhci.o xhci-mem.o
+xhci-hcd-y := xhci.o xhci-mem.o xhci-ext-caps.o
xhci-hcd-y += xhci-ring.o xhci-hub.o xhci-dbg.o
xhci-hcd-y += xhci-trace.o
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c
index c5094cb88cd5..0a9fd2022acf 100644
--- a/drivers/usb/host/ehci-fsl.c
+++ b/drivers/usb/host/ehci-fsl.c
@@ -155,6 +155,8 @@ static int fsl_ehci_drv_probe(struct platform_device *pdev)
retval = -ENODEV;
goto err2;
}
+
+ hcd->skip_phy_initialization = 1;
}
#endif
return retval;
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 7f0737449df7..d927adf3afcd 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -1275,11 +1275,6 @@ MODULE_LICENSE ("GPL");
#define XILINX_OF_PLATFORM_DRIVER ehci_hcd_xilinx_of_driver
#endif
-#ifdef CONFIG_TILE_USB
-#include "ehci-tilegx.c"
-#define PLATFORM_DRIVER ehci_hcd_tilegx_driver
-#endif
-
#ifdef CONFIG_USB_EHCI_HCD_PMC_MSP
#include "ehci-pmcmsp.c"
#define PLATFORM_DRIVER ehci_hcd_msp_driver
diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c
index 21307d862af6..4c6c08b675b5 100644
--- a/drivers/usb/host/ehci-mem.c
+++ b/drivers/usb/host/ehci-mem.c
@@ -73,10 +73,9 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags)
if (!qh)
goto done;
qh->hw = (struct ehci_qh_hw *)
- dma_pool_alloc(ehci->qh_pool, flags, &dma);
+ dma_pool_zalloc(ehci->qh_pool, flags, &dma);
if (!qh->hw)
goto fail;
- memset(qh->hw, 0, sizeof *qh->hw);
qh->qh_dma = dma;
// INIT_LIST_HEAD (&qh->qh_list);
INIT_LIST_HEAD (&qh->qtd_list);
diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c
index b065a960adc2..4c306fb6b069 100644
--- a/drivers/usb/host/ehci-platform.c
+++ b/drivers/usb/host/ehci-platform.c
@@ -27,7 +27,6 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/usb.h>
@@ -44,8 +43,6 @@
struct ehci_platform_priv {
struct clk *clks[EHCI_MAX_CLKS];
struct reset_control *rsts;
- struct phy **phys;
- int num_phys;
bool reset_on_resume;
};
@@ -80,7 +77,7 @@ static int ehci_platform_power_on(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
- int clk, ret, phy_num;
+ int clk, ret;
for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) {
ret = clk_prepare_enable(priv->clks[clk]);
@@ -88,24 +85,8 @@ static int ehci_platform_power_on(struct platform_device *dev)
goto err_disable_clks;
}
- for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
- ret = phy_init(priv->phys[phy_num]);
- if (ret)
- goto err_exit_phy;
- ret = phy_power_on(priv->phys[phy_num]);
- if (ret) {
- phy_exit(priv->phys[phy_num]);
- goto err_exit_phy;
- }
- }
-
return 0;
-err_exit_phy:
- while (--phy_num >= 0) {
- phy_power_off(priv->phys[phy_num]);
- phy_exit(priv->phys[phy_num]);
- }
err_disable_clks:
while (--clk >= 0)
clk_disable_unprepare(priv->clks[clk]);
@@ -117,12 +98,7 @@ static void ehci_platform_power_off(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
- int clk, phy_num;
-
- for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
- phy_power_off(priv->phys[phy_num]);
- phy_exit(priv->phys[phy_num]);
- }
+ int clk;
for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--)
if (priv->clks[clk])
@@ -149,7 +125,7 @@ static int ehci_platform_probe(struct platform_device *dev)
struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
struct ehci_platform_priv *priv;
struct ehci_hcd *ehci;
- int err, irq, phy_num, clk = 0;
+ int err, irq, clk = 0;
if (usb_disabled())
return -ENODEV;
@@ -202,29 +178,6 @@ static int ehci_platform_probe(struct platform_device *dev)
"has-transaction-translator"))
hcd->has_tt = 1;
- priv->num_phys = of_count_phandle_with_args(dev->dev.of_node,
- "phys", "#phy-cells");
-
- if (priv->num_phys > 0) {
- priv->phys = devm_kcalloc(&dev->dev, priv->num_phys,
- sizeof(struct phy *), GFP_KERNEL);
- if (!priv->phys)
- return -ENOMEM;
- } else
- priv->num_phys = 0;
-
- for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
- priv->phys[phy_num] = devm_of_phy_get_by_index(
- &dev->dev, dev->dev.of_node, phy_num);
- if (IS_ERR(priv->phys[phy_num])) {
- err = PTR_ERR(priv->phys[phy_num]);
- goto err_put_hcd;
- } else if (!hcd->phy) {
- /* Avoiding phy_get() in usb_add_hcd() */
- hcd->phy = priv->phys[phy_num];
- }
- }
-
for (clk = 0; clk < EHCI_MAX_CLKS; clk++) {
priv->clks[clk] = of_clk_get(dev->dev.of_node, clk);
if (IS_ERR(priv->clks[clk])) {
@@ -306,7 +259,7 @@ err_reset:
err_put_clks:
while (--clk >= 0)
clk_put(priv->clks[clk]);
-err_put_hcd:
+
if (pdata == &ehci_platform_defaults)
dev->dev.platform_data = NULL;
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index e56db44708bc..28e2a338b481 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -1287,7 +1287,7 @@ itd_urb_transaction(
} else {
alloc_itd:
spin_unlock_irqrestore(&ehci->lock, flags);
- itd = dma_pool_alloc(ehci->itd_pool, mem_flags,
+ itd = dma_pool_zalloc(ehci->itd_pool, mem_flags,
&itd_dma);
spin_lock_irqsave(&ehci->lock, flags);
if (!itd) {
@@ -1297,7 +1297,6 @@ itd_urb_transaction(
}
}
- memset(itd, 0, sizeof(*itd));
itd->itd_dma = itd_dma;
itd->frame = NO_FRAME;
list_add(&itd->itd_list, &sched->td_list);
@@ -2081,7 +2080,7 @@ sitd_urb_transaction(
} else {
alloc_sitd:
spin_unlock_irqrestore(&ehci->lock, flags);
- sitd = dma_pool_alloc(ehci->sitd_pool, mem_flags,
+ sitd = dma_pool_zalloc(ehci->sitd_pool, mem_flags,
&sitd_dma);
spin_lock_irqsave(&ehci->lock, flags);
if (!sitd) {
@@ -2091,7 +2090,6 @@ sitd_urb_transaction(
}
}
- memset(sitd, 0, sizeof(*sitd));
sitd->sitd_dma = sitd_dma;
sitd->frame = NO_FRAME;
list_add(&sitd->sitd_list, &iso_sched->td_list);
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
index c809f7d2f08f..a6f4389f7e88 100644
--- a/drivers/usb/host/ehci-tegra.c
+++ b/drivers/usb/host/ehci-tegra.c
@@ -461,6 +461,7 @@ static int tegra_ehci_probe(struct platform_device *pdev)
goto cleanup_clk_en;
}
hcd->usb_phy = u_phy;
+ hcd->skip_phy_initialization = 1;
tegra->needs_double_reset = of_property_read_bool(pdev->dev.of_node,
"nvidia,needs-double-reset");
diff --git a/drivers/usb/host/ehci-tilegx.c b/drivers/usb/host/ehci-tilegx.c
deleted file mode 100644
index 610ed437ed2c..000000000000
--- a/drivers/usb/host/ehci-tilegx.c
+++ /dev/null
@@ -1,207 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright 2012 Tilera Corporation. All Rights Reserved.
- */
-
-/*
- * Tilera TILE-Gx USB EHCI host controller driver.
- */
-
-#include <linux/irq.h>
-#include <linux/platform_device.h>
-#include <linux/usb/tilegx.h>
-#include <linux/usb.h>
-
-#include <asm/homecache.h>
-
-#include <gxio/iorpc_usb_host.h>
-#include <gxio/usb_host.h>
-
-static void tilegx_start_ehc(void)
-{
-}
-
-static void tilegx_stop_ehc(void)
-{
-}
-
-static int tilegx_ehci_setup(struct usb_hcd *hcd)
-{
- int ret = ehci_init(hcd);
-
- /*
- * Some drivers do:
- *
- * struct ehci_hcd *ehci = hcd_to_ehci(hcd);
- * ehci->need_io_watchdog = 0;
- *
- * here, but since this is a new driver we're going to leave the
- * watchdog enabled. Later we may try to turn it off and see
- * whether we run into any problems.
- */
-
- return ret;
-}
-
-static const struct hc_driver ehci_tilegx_hc_driver = {
- .description = hcd_name,
- .product_desc = "Tile-Gx EHCI",
- .hcd_priv_size = sizeof(struct ehci_hcd),
-
- /*
- * Generic hardware linkage.
- */
- .irq = ehci_irq,
- .flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
-
- /*
- * Basic lifecycle operations.
- */
- .reset = tilegx_ehci_setup,
- .start = ehci_run,
- .stop = ehci_stop,
- .shutdown = ehci_shutdown,
-
- /*
- * Managing I/O requests and associated device resources.
- */
- .urb_enqueue = ehci_urb_enqueue,
- .urb_dequeue = ehci_urb_dequeue,
- .endpoint_disable = ehci_endpoint_disable,
- .endpoint_reset = ehci_endpoint_reset,
-
- /*
- * Scheduling support.
- */
- .get_frame_number = ehci_get_frame,
-
- /*
- * Root hub support.
- */
- .hub_status_data = ehci_hub_status_data,
- .hub_control = ehci_hub_control,
- .bus_suspend = ehci_bus_suspend,
- .bus_resume = ehci_bus_resume,
- .relinquish_port = ehci_relinquish_port,
- .port_handed_over = ehci_port_handed_over,
-
- .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
-};
-
-static int ehci_hcd_tilegx_drv_probe(struct platform_device *pdev)
-{
- struct usb_hcd *hcd;
- struct ehci_hcd *ehci;
- struct tilegx_usb_platform_data *pdata = dev_get_platdata(&pdev->dev);
- pte_t pte = { 0 };
- int my_cpu = smp_processor_id();
- int ret;
-
- if (usb_disabled())
- return -ENODEV;
-
- /*
- * Try to initialize our GXIO context; if we can't, the device
- * doesn't exist.
- */
- if (gxio_usb_host_init(&pdata->usb_ctx, pdata->dev_index, 1) != 0)
- return -ENXIO;
-
- hcd = usb_create_hcd(&ehci_tilegx_hc_driver, &pdev->dev,
- dev_name(&pdev->dev));
- if (!hcd) {
- ret = -ENOMEM;
- goto err_hcd;
- }
-
- /*
- * We don't use rsrc_start to map in our registers, but seems like
- * we ought to set it to something, so we use the register VA.
- */
- hcd->rsrc_start =
- (ulong) gxio_usb_host_get_reg_start(&pdata->usb_ctx);
- hcd->rsrc_len = gxio_usb_host_get_reg_len(&pdata->usb_ctx);
- hcd->regs = gxio_usb_host_get_reg_start(&pdata->usb_ctx);
-
- tilegx_start_ehc();
-
- ehci = hcd_to_ehci(hcd);
- ehci->caps = hcd->regs;
- ehci->regs =
- hcd->regs + HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase));
- /* cache this readonly data; minimize chip reads */
- ehci->hcs_params = readl(&ehci->caps->hcs_params);
-
- /* Create our IRQs and register them. */
- pdata->irq = irq_alloc_hwirq(-1);
- if (!pdata->irq) {
- ret = -ENXIO;
- goto err_no_irq;
- }
-
- tile_irq_activate(pdata->irq, TILE_IRQ_PERCPU);
-
- /* Configure interrupts. */
- ret = gxio_usb_host_cfg_interrupt(&pdata->usb_ctx,
- cpu_x(my_cpu), cpu_y(my_cpu),
- KERNEL_PL, pdata->irq);
- if (ret) {
- ret = -ENXIO;
- goto err_have_irq;
- }
-
- /* Register all of our memory. */
- pte = pte_set_home(pte, PAGE_HOME_HASH);
- ret = gxio_usb_host_register_client_memory(&pdata->usb_ctx, pte, 0);
- if (ret) {
- ret = -ENXIO;
- goto err_have_irq;
- }
-
- ret = usb_add_hcd(hcd, pdata->irq, IRQF_SHARED);
- if (ret == 0) {
- platform_set_drvdata(pdev, hcd);
- device_wakeup_enable(hcd->self.controller);
- return ret;
- }
-
-err_have_irq:
- irq_free_hwirq(pdata->irq);
-err_no_irq:
- tilegx_stop_ehc();
- usb_put_hcd(hcd);
-err_hcd:
- gxio_usb_host_destroy(&pdata->usb_ctx);
- return ret;
-}
-
-static int ehci_hcd_tilegx_drv_remove(struct platform_device *pdev)
-{
- struct usb_hcd *hcd = platform_get_drvdata(pdev);
- struct tilegx_usb_platform_data *pdata = dev_get_platdata(&pdev->dev);
-
- usb_remove_hcd(hcd);
- usb_put_hcd(hcd);
- tilegx_stop_ehc();
- gxio_usb_host_destroy(&pdata->usb_ctx);
- irq_free_hwirq(pdata->irq);
-
- return 0;
-}
-
-static void ehci_hcd_tilegx_drv_shutdown(struct platform_device *pdev)
-{
- usb_hcd_platform_shutdown(pdev);
- ehci_hcd_tilegx_drv_remove(pdev);
-}
-
-static struct platform_driver ehci_hcd_tilegx_driver = {
- .probe = ehci_hcd_tilegx_drv_probe,
- .remove = ehci_hcd_tilegx_drv_remove,
- .shutdown = ehci_hcd_tilegx_drv_shutdown,
- .driver = {
- .name = "tilegx-ehci",
- }
-};
-
-MODULE_ALIAS("platform:tilegx-ehci");
diff --git a/drivers/usb/host/fhci-dbg.c b/drivers/usb/host/fhci-dbg.c
index fafa91189e45..ebf9bb219f75 100644
--- a/drivers/usb/host/fhci-dbg.c
+++ b/drivers/usb/host/fhci-dbg.c
@@ -55,6 +55,7 @@ static int fhci_dfs_regs_show(struct seq_file *s, void *v)
return 0;
}
+DEFINE_SHOW_ATTRIBUTE(fhci_dfs_regs);
static int fhci_dfs_irq_stat_show(struct seq_file *s, void *v)
{
@@ -75,30 +76,7 @@ static int fhci_dfs_irq_stat_show(struct seq_file *s, void *v)
return 0;
}
-
-static int fhci_dfs_regs_open(struct inode *inode, struct file *file)
-{
- return single_open(file, fhci_dfs_regs_show, inode->i_private);
-}
-
-static int fhci_dfs_irq_stat_open(struct inode *inode, struct file *file)
-{
- return single_open(file, fhci_dfs_irq_stat_show, inode->i_private);
-}
-
-static const struct file_operations fhci_dfs_regs_fops = {
- .open = fhci_dfs_regs_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static const struct file_operations fhci_dfs_irq_stat_fops = {
- .open = fhci_dfs_irq_stat_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(fhci_dfs_irq_stat);
void fhci_dfs_create(struct fhci_hcd *fhci)
{
diff --git a/drivers/usb/host/imx21-dbg.c b/drivers/usb/host/imx21-dbg.c
index b964f9a51d87..a213ed6f07b5 100644
--- a/drivers/usb/host/imx21-dbg.c
+++ b/drivers/usb/host/imx21-dbg.c
@@ -245,6 +245,7 @@ static int debug_status_show(struct seq_file *s, void *v)
return 0;
}
+DEFINE_SHOW_ATTRIBUTE(debug_status);
static int debug_dmem_show(struct seq_file *s, void *v)
{
@@ -266,6 +267,7 @@ static int debug_dmem_show(struct seq_file *s, void *v)
return 0;
}
+DEFINE_SHOW_ATTRIBUTE(debug_dmem);
static int debug_etd_show(struct seq_file *s, void *v)
{
@@ -334,6 +336,7 @@ static int debug_etd_show(struct seq_file *s, void *v)
return 0;
}
+DEFINE_SHOW_ATTRIBUTE(debug_etd);
static void debug_statistics_show_one(struct seq_file *s,
const char *name, struct debug_stats *stats)
@@ -368,6 +371,7 @@ static int debug_statistics_show(struct seq_file *s, void *v)
return 0;
}
+DEFINE_SHOW_ATTRIBUTE(debug_statistics);
static void debug_isoc_show_one(struct seq_file *s,
const char *name, int index, struct debug_isoc_trace *trace)
@@ -409,66 +413,7 @@ static int debug_isoc_show(struct seq_file *s, void *v)
return 0;
}
-
-static int debug_status_open(struct inode *inode, struct file *file)
-{
- return single_open(file, debug_status_show, inode->i_private);
-}
-
-static int debug_dmem_open(struct inode *inode, struct file *file)
-{
- return single_open(file, debug_dmem_show, inode->i_private);
-}
-
-static int debug_etd_open(struct inode *inode, struct file *file)
-{
- return single_open(file, debug_etd_show, inode->i_private);
-}
-
-static int debug_statistics_open(struct inode *inode, struct file *file)
-{
- return single_open(file, debug_statistics_show, inode->i_private);
-}
-
-static int debug_isoc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, debug_isoc_show, inode->i_private);
-}
-
-static const struct file_operations debug_status_fops = {
- .open = debug_status_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static const struct file_operations debug_dmem_fops = {
- .open = debug_dmem_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static const struct file_operations debug_etd_fops = {
- .open = debug_etd_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static const struct file_operations debug_statistics_fops = {
- .open = debug_statistics_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static const struct file_operations debug_isoc_fops = {
- .open = debug_isoc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(debug_isoc);
static void create_debug_files(struct imx21 *imx21)
{
diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c
index 5f9234b9cf7b..4602ed801f0a 100644
--- a/drivers/usb/host/isp116x-hcd.c
+++ b/drivers/usb/host/isp116x-hcd.c
@@ -1168,7 +1168,7 @@ static void dump_int(struct seq_file *s, char *label, u32 mask)
mask & HCINT_SF ? " sof" : "", mask & HCINT_SO ? " so" : "");
}
-static int isp116x_show_dbg(struct seq_file *s, void *unused)
+static int isp116x_debug_show(struct seq_file *s, void *unused)
{
struct isp116x *isp116x = s->private;
@@ -1196,18 +1196,7 @@ static int isp116x_show_dbg(struct seq_file *s, void *unused)
return 0;
}
-
-static int isp116x_open_seq(struct inode *inode, struct file *file)
-{
- return single_open(file, isp116x_show_dbg, inode->i_private);
-}
-
-static const struct file_operations isp116x_debug_fops = {
- .open = isp116x_open_seq,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(isp116x_debug);
static int create_debug_file(struct isp116x *isp116x)
{
diff --git a/drivers/usb/host/isp1362.h b/drivers/usb/host/isp1362.h
index da79e36ced89..650240846ee2 100644
--- a/drivers/usb/host/isp1362.h
+++ b/drivers/usb/host/isp1362.h
@@ -6,49 +6,6 @@
*/
/* ------------------------------------------------------------------------- */
-/*
- * Platform specific compile time options
- */
-#if defined(CONFIG_BLACKFIN)
-
-#include <linux/io.h>
-#define USE_32BIT 0
-#define MAX_ROOT_PORTS 2
-#define USE_PLATFORM_DELAY 0
-#define USE_NDELAY 1
-
-#define DUMMY_DELAY_ACCESS \
- do { \
- bfin_read16(ASYNC_BANK0_BASE); \
- bfin_read16(ASYNC_BANK0_BASE); \
- bfin_read16(ASYNC_BANK0_BASE); \
- } while (0)
-
-#undef insw
-#undef outsw
-
-#define insw delayed_insw
-#define outsw delayed_outsw
-
-static inline void delayed_outsw(unsigned int addr, void *buf, int len)
-{
- unsigned short *bp = (unsigned short *)buf;
- while (len--) {
- DUMMY_DELAY_ACCESS;
- outw(*bp++, addr);
- }
-}
-
-static inline void delayed_insw(unsigned int addr, void *buf, int len)
-{
- unsigned short *bp = (unsigned short *)buf;
- while (len--) {
- DUMMY_DELAY_ACCESS;
- *bp++ = inw(addr);
- }
-}
-
-#else
#define MAX_ROOT_PORTS 2
@@ -60,9 +17,6 @@ static inline void delayed_insw(unsigned int addr, void *buf, int len)
#define DUMMY_DELAY_ACCESS do {} while (0)
-#endif
-
-
/* ------------------------------------------------------------------------- */
#define USB_RESET_WIDTH 50
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index d088c340e4d0..4806e0f9e8d4 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -1245,11 +1245,6 @@ MODULE_LICENSE ("GPL");
#define TMIO_OHCI_DRIVER ohci_hcd_tmio_driver
#endif
-#ifdef CONFIG_TILE_USB
-#include "ohci-tilegx.c"
-#define PLATFORM_DRIVER ohci_hcd_tilegx_driver
-#endif
-
static int __init ohci_hcd_mod_init(void)
{
int retval = 0;
@@ -1274,12 +1269,6 @@ static int __init ohci_hcd_mod_init(void)
goto error_ps3;
#endif
-#ifdef PLATFORM_DRIVER
- retval = platform_driver_register(&PLATFORM_DRIVER);
- if (retval < 0)
- goto error_platform;
-#endif
-
#ifdef OF_PLATFORM_DRIVER
retval = platform_driver_register(&OF_PLATFORM_DRIVER);
if (retval < 0)
@@ -1323,10 +1312,6 @@ static int __init ohci_hcd_mod_init(void)
platform_driver_unregister(&OF_PLATFORM_DRIVER);
error_of_platform:
#endif
-#ifdef PLATFORM_DRIVER
- platform_driver_unregister(&PLATFORM_DRIVER);
- error_platform:
-#endif
#ifdef PS3_SYSTEM_BUS_DRIVER
ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
error_ps3:
@@ -1354,9 +1339,6 @@ static void __exit ohci_hcd_mod_exit(void)
#ifdef OF_PLATFORM_DRIVER
platform_driver_unregister(&OF_PLATFORM_DRIVER);
#endif
-#ifdef PLATFORM_DRIVER
- platform_driver_unregister(&PLATFORM_DRIVER);
-#endif
#ifdef PS3_SYSTEM_BUS_DRIVER
ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
#endif
diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c
index 0201c49bc4fc..d8d35d456456 100644
--- a/drivers/usb/host/ohci-omap.c
+++ b/drivers/usb/host/ohci-omap.c
@@ -230,6 +230,7 @@ static int ohci_omap_reset(struct usb_hcd *hcd)
} else {
return -EPROBE_DEFER;
}
+ hcd->skip_phy_initialization = 1;
ohci->start_hnp = start_hnp;
}
#endif
diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c
index 1e6c954f4b3f..65a1c3fdc88c 100644
--- a/drivers/usb/host/ohci-platform.c
+++ b/drivers/usb/host/ohci-platform.c
@@ -21,7 +21,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/err.h>
-#include <linux/phy/phy.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
@@ -38,8 +38,6 @@
struct ohci_platform_priv {
struct clk *clks[OHCI_MAX_CLKS];
struct reset_control *resets;
- struct phy **phys;
- int num_phys;
};
static const char hcd_name[] = "ohci-platform";
@@ -48,7 +46,7 @@ static int ohci_platform_power_on(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd);
- int clk, ret, phy_num;
+ int clk, ret;
for (clk = 0; clk < OHCI_MAX_CLKS && priv->clks[clk]; clk++) {
ret = clk_prepare_enable(priv->clks[clk]);
@@ -56,24 +54,8 @@ static int ohci_platform_power_on(struct platform_device *dev)
goto err_disable_clks;
}
- for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
- ret = phy_init(priv->phys[phy_num]);
- if (ret)
- goto err_exit_phy;
- ret = phy_power_on(priv->phys[phy_num]);
- if (ret) {
- phy_exit(priv->phys[phy_num]);
- goto err_exit_phy;
- }
- }
-
return 0;
-err_exit_phy:
- while (--phy_num >= 0) {
- phy_power_off(priv->phys[phy_num]);
- phy_exit(priv->phys[phy_num]);
- }
err_disable_clks:
while (--clk >= 0)
clk_disable_unprepare(priv->clks[clk]);
@@ -85,12 +67,7 @@ static void ohci_platform_power_off(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd);
- int clk, phy_num;
-
- for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
- phy_power_off(priv->phys[phy_num]);
- phy_exit(priv->phys[phy_num]);
- }
+ int clk;
for (clk = OHCI_MAX_CLKS - 1; clk >= 0; clk--)
if (priv->clks[clk])
@@ -117,7 +94,7 @@ static int ohci_platform_probe(struct platform_device *dev)
struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev);
struct ohci_platform_priv *priv;
struct ohci_hcd *ohci;
- int err, irq, phy_num, clk = 0;
+ int err, irq, clk = 0;
if (usb_disabled())
return -ENODEV;
@@ -169,29 +146,6 @@ static int ohci_platform_probe(struct platform_device *dev)
of_property_read_u32(dev->dev.of_node, "num-ports",
&ohci->num_ports);
- priv->num_phys = of_count_phandle_with_args(dev->dev.of_node,
- "phys", "#phy-cells");
-
- if (priv->num_phys > 0) {
- priv->phys = devm_kcalloc(&dev->dev, priv->num_phys,
- sizeof(struct phy *), GFP_KERNEL);
- if (!priv->phys)
- return -ENOMEM;
- } else
- priv->num_phys = 0;
-
- for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
- priv->phys[phy_num] = devm_of_phy_get_by_index(
- &dev->dev, dev->dev.of_node, phy_num);
- if (IS_ERR(priv->phys[phy_num])) {
- err = PTR_ERR(priv->phys[phy_num]);
- goto err_put_hcd;
- } else if (!hcd->phy) {
- /* Avoiding phy_get() in usb_add_hcd() */
- hcd->phy = priv->phys[phy_num];
- }
- }
-
for (clk = 0; clk < OHCI_MAX_CLKS; clk++) {
priv->clks[clk] = of_clk_get(dev->dev.of_node, clk);
if (IS_ERR(priv->clks[clk])) {
@@ -277,7 +231,7 @@ err_reset:
err_put_clks:
while (--clk >= 0)
clk_put(priv->clks[clk]);
-err_put_hcd:
+
if (pdata == &ohci_platform_defaults)
dev->dev.platform_data = NULL;
diff --git a/drivers/usb/host/ohci-tilegx.c b/drivers/usb/host/ohci-tilegx.c
deleted file mode 100644
index d21ca3ce9a30..000000000000
--- a/drivers/usb/host/ohci-tilegx.c
+++ /dev/null
@@ -1,196 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright 2012 Tilera Corporation. All Rights Reserved.
- */
-
-/*
- * Tilera TILE-Gx USB OHCI host controller driver.
- */
-
-#include <linux/irq.h>
-#include <linux/platform_device.h>
-#include <linux/usb/tilegx.h>
-#include <linux/usb.h>
-
-#include <asm/homecache.h>
-
-#include <gxio/iorpc_usb_host.h>
-#include <gxio/usb_host.h>
-
-static void tilegx_start_ohc(void)
-{
-}
-
-static void tilegx_stop_ohc(void)
-{
-}
-
-static int tilegx_ohci_start(struct usb_hcd *hcd)
-{
- struct ohci_hcd *ohci = hcd_to_ohci(hcd);
- int ret;
-
- ret = ohci_init(ohci);
- if (ret < 0)
- return ret;
-
- ret = ohci_run(ohci);
- if (ret < 0) {
- dev_err(hcd->self.controller, "can't start %s\n",
- hcd->self.bus_name);
- ohci_stop(hcd);
- return ret;
- }
-
- return 0;
-}
-
-static const struct hc_driver ohci_tilegx_hc_driver = {
- .description = hcd_name,
- .product_desc = "Tile-Gx OHCI",
- .hcd_priv_size = sizeof(struct ohci_hcd),
-
- /*
- * Generic hardware linkage.
- */
- .irq = ohci_irq,
- .flags = HCD_MEMORY | HCD_LOCAL_MEM | HCD_USB11,
-
- /*
- * Basic lifecycle operations.
- */
- .start = tilegx_ohci_start,
- .stop = ohci_stop,
- .shutdown = ohci_shutdown,
-
- /*
- * 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,
-};
-
-static int ohci_hcd_tilegx_drv_probe(struct platform_device *pdev)
-{
- struct usb_hcd *hcd;
- struct tilegx_usb_platform_data *pdata = dev_get_platdata(&pdev->dev);
- pte_t pte = { 0 };
- int my_cpu = smp_processor_id();
- int ret;
-
- if (usb_disabled())
- return -ENODEV;
-
- /*
- * Try to initialize our GXIO context; if we can't, the device
- * doesn't exist.
- */
- if (gxio_usb_host_init(&pdata->usb_ctx, pdata->dev_index, 0) != 0)
- return -ENXIO;
-
- hcd = usb_create_hcd(&ohci_tilegx_hc_driver, &pdev->dev,
- dev_name(&pdev->dev));
- if (!hcd) {
- ret = -ENOMEM;
- goto err_hcd;
- }
-
- /*
- * We don't use rsrc_start to map in our registers, but seems like
- * we ought to set it to something, so we use the register VA.
- */
- hcd->rsrc_start =
- (ulong) gxio_usb_host_get_reg_start(&pdata->usb_ctx);
- hcd->rsrc_len = gxio_usb_host_get_reg_len(&pdata->usb_ctx);
- hcd->regs = gxio_usb_host_get_reg_start(&pdata->usb_ctx);
-
- tilegx_start_ohc();
-
- /* Create our IRQs and register them. */
- pdata->irq = irq_alloc_hwirq(-1);
- if (!pdata->irq) {
- ret = -ENXIO;
- goto err_no_irq;
- }
-
- tile_irq_activate(pdata->irq, TILE_IRQ_PERCPU);
-
- /* Configure interrupts. */
- ret = gxio_usb_host_cfg_interrupt(&pdata->usb_ctx,
- cpu_x(my_cpu), cpu_y(my_cpu),
- KERNEL_PL, pdata->irq);
- if (ret) {
- ret = -ENXIO;
- goto err_have_irq;
- }
-
- /* Register all of our memory. */
- pte = pte_set_home(pte, PAGE_HOME_HASH);
- ret = gxio_usb_host_register_client_memory(&pdata->usb_ctx, pte, 0);
- if (ret) {
- ret = -ENXIO;
- goto err_have_irq;
- }
-
- ohci_hcd_init(hcd_to_ohci(hcd));
-
- ret = usb_add_hcd(hcd, pdata->irq, IRQF_SHARED);
- if (ret == 0) {
- platform_set_drvdata(pdev, hcd);
- device_wakeup_enable(hcd->self.controller);
- return ret;
- }
-
-err_have_irq:
- irq_free_hwirq(pdata->irq);
-err_no_irq:
- tilegx_stop_ohc();
- usb_put_hcd(hcd);
-err_hcd:
- gxio_usb_host_destroy(&pdata->usb_ctx);
- return ret;
-}
-
-static int ohci_hcd_tilegx_drv_remove(struct platform_device *pdev)
-{
- struct usb_hcd *hcd = platform_get_drvdata(pdev);
- struct tilegx_usb_platform_data *pdata = dev_get_platdata(&pdev->dev);
-
- usb_remove_hcd(hcd);
- usb_put_hcd(hcd);
- tilegx_stop_ohc();
- gxio_usb_host_destroy(&pdata->usb_ctx);
- irq_free_hwirq(pdata->irq);
-
- return 0;
-}
-
-static void ohci_hcd_tilegx_drv_shutdown(struct platform_device *pdev)
-{
- usb_hcd_platform_shutdown(pdev);
- ohci_hcd_tilegx_drv_remove(pdev);
-}
-
-static struct platform_driver ohci_hcd_tilegx_driver = {
- .probe = ohci_hcd_tilegx_drv_probe,
- .remove = ohci_hcd_tilegx_drv_remove,
- .shutdown = ohci_hcd_tilegx_drv_shutdown,
- .driver = {
- .name = "tilegx-ohci",
- }
-};
-
-MODULE_ALIAS("platform:tilegx-ohci");
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
index fa88a903fa2e..5b061e599948 100644
--- a/drivers/usb/host/sl811-hcd.c
+++ b/drivers/usb/host/sl811-hcd.c
@@ -1381,7 +1381,7 @@ static void dump_irq(struct seq_file *s, char *label, u8 mask)
(mask & SL11H_INTMASK_DP) ? " dp" : "");
}
-static int sl811h_show(struct seq_file *s, void *unused)
+static int sl811h_debug_show(struct seq_file *s, void *unused)
{
struct sl811 *sl811 = s->private;
struct sl811h_ep *ep;
@@ -1491,25 +1491,14 @@ static int sl811h_show(struct seq_file *s, void *unused)
return 0;
}
-
-static int sl811h_open(struct inode *inode, struct file *file)
-{
- return single_open(file, sl811h_show, inode->i_private);
-}
-
-static const struct file_operations debug_ops = {
- .open = sl811h_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(sl811h_debug);
/* expect just one sl811 per system */
static void create_debug_file(struct sl811 *sl811)
{
sl811->debug_file = debugfs_create_file("sl811h", S_IRUGO,
usb_debug_root, sl811,
- &debug_ops);
+ &sl811h_debug_fops);
}
static void remove_debug_file(struct sl811 *sl811)
diff --git a/drivers/usb/host/whci/debug.c b/drivers/usb/host/whci/debug.c
index f154e5791bfd..8ddfe3f1f693 100644
--- a/drivers/usb/host/whci/debug.c
+++ b/drivers/usb/host/whci/debug.c
@@ -72,7 +72,7 @@ static void qset_print(struct seq_file *s, struct whc_qset *qset)
}
}
-static int di_print(struct seq_file *s, void *p)
+static int di_show(struct seq_file *s, void *p)
{
struct whc *whc = s->private;
int d;
@@ -91,8 +91,9 @@ static int di_print(struct seq_file *s, void *p)
}
return 0;
}
+DEFINE_SHOW_ATTRIBUTE(di);
-static int asl_print(struct seq_file *s, void *p)
+static int asl_show(struct seq_file *s, void *p)
{
struct whc *whc = s->private;
struct whc_qset *qset;
@@ -103,8 +104,9 @@ static int asl_print(struct seq_file *s, void *p)
return 0;
}
+DEFINE_SHOW_ATTRIBUTE(asl);
-static int pzl_print(struct seq_file *s, void *p)
+static int pzl_show(struct seq_file *s, void *p)
{
struct whc *whc = s->private;
struct whc_qset *qset;
@@ -118,45 +120,7 @@ static int pzl_print(struct seq_file *s, void *p)
}
return 0;
}
-
-static int di_open(struct inode *inode, struct file *file)
-{
- return single_open(file, di_print, inode->i_private);
-}
-
-static int asl_open(struct inode *inode, struct file *file)
-{
- return single_open(file, asl_print, inode->i_private);
-}
-
-static int pzl_open(struct inode *inode, struct file *file)
-{
- return single_open(file, pzl_print, inode->i_private);
-}
-
-static const struct file_operations di_fops = {
- .open = di_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
-};
-
-static const struct file_operations asl_fops = {
- .open = asl_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
-};
-
-static const struct file_operations pzl_fops = {
- .open = pzl_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
-};
+DEFINE_SHOW_ATTRIBUTE(pzl);
void whc_dbg_init(struct whc *whc)
{
diff --git a/drivers/usb/host/xhci-dbgtty.c b/drivers/usb/host/xhci-dbgtty.c
index 75f0b92694ba..eb494ec547e8 100644
--- a/drivers/usb/host/xhci-dbgtty.c
+++ b/drivers/usb/host/xhci-dbgtty.c
@@ -320,9 +320,11 @@ int xhci_dbc_tty_register_driver(struct xhci_hcd *xhci)
void xhci_dbc_tty_unregister_driver(void)
{
- tty_unregister_driver(dbc_tty_driver);
- put_tty_driver(dbc_tty_driver);
- dbc_tty_driver = NULL;
+ if (dbc_tty_driver) {
+ tty_unregister_driver(dbc_tty_driver);
+ put_tty_driver(dbc_tty_driver);
+ dbc_tty_driver = NULL;
+ }
}
static void dbc_rx_push(unsigned long _port)
@@ -447,9 +449,10 @@ int xhci_dbc_tty_register_device(struct xhci_hcd *xhci)
xhci_dbc_tty_init_port(xhci, port);
tty_dev = tty_port_register_device(&port->port,
dbc_tty_driver, 0, NULL);
- ret = IS_ERR_OR_NULL(tty_dev);
- if (ret)
+ if (IS_ERR(tty_dev)) {
+ ret = PTR_ERR(tty_dev);
goto register_fail;
+ }
ret = kfifo_alloc(&port->write_fifo, DBC_WRITE_BUF_SIZE, GFP_KERNEL);
if (ret)
diff --git a/drivers/usb/host/xhci-ext-caps.c b/drivers/usb/host/xhci-ext-caps.c
new file mode 100644
index 000000000000..399113f9fc5c
--- /dev/null
+++ b/drivers/usb/host/xhci-ext-caps.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * XHCI extended capability handling
+ *
+ * Copyright (c) 2017 Hans de Goede <hdegoede@redhat.com>
+ */
+
+#include <linux/platform_device.h>
+#include "xhci.h"
+
+#define USB_SW_DRV_NAME "intel_xhci_usb_sw"
+#define USB_SW_RESOURCE_SIZE 0x400
+
+static void xhci_intel_unregister_pdev(void *arg)
+{
+ platform_device_unregister(arg);
+}
+
+static int xhci_create_intel_xhci_sw_pdev(struct xhci_hcd *xhci, u32 cap_offset)
+{
+ struct usb_hcd *hcd = xhci_to_hcd(xhci);
+ struct device *dev = hcd->self.controller;
+ struct platform_device *pdev;
+ struct resource res = { 0, };
+ int ret;
+
+ pdev = platform_device_alloc(USB_SW_DRV_NAME, PLATFORM_DEVID_NONE);
+ if (!pdev) {
+ xhci_err(xhci, "couldn't allocate %s platform device\n",
+ USB_SW_DRV_NAME);
+ return -ENOMEM;
+ }
+
+ res.start = hcd->rsrc_start + cap_offset;
+ res.end = res.start + USB_SW_RESOURCE_SIZE - 1;
+ res.name = USB_SW_DRV_NAME;
+ res.flags = IORESOURCE_MEM;
+
+ ret = platform_device_add_resources(pdev, &res, 1);
+ if (ret) {
+ dev_err(dev, "couldn't add resources to intel_xhci_usb_sw pdev\n");
+ platform_device_put(pdev);
+ return ret;
+ }
+
+ pdev->dev.parent = dev;
+
+ ret = platform_device_add(pdev);
+ if (ret) {
+ dev_err(dev, "couldn't register intel_xhci_usb_sw pdev\n");
+ platform_device_put(pdev);
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(dev, xhci_intel_unregister_pdev, pdev);
+ if (ret) {
+ dev_err(dev, "couldn't add unregister action for intel_xhci_usb_sw pdev\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+int xhci_ext_cap_init(struct xhci_hcd *xhci)
+{
+ void __iomem *base = &xhci->cap_regs->hc_capbase;
+ u32 offset, val;
+ int ret;
+
+ offset = xhci_find_next_ext_cap(base, 0, 0);
+
+ while (offset) {
+ val = readl(base + offset);
+
+ switch (XHCI_EXT_CAPS_ID(val)) {
+ case XHCI_EXT_CAPS_VENDOR_INTEL:
+ if (xhci->quirks & XHCI_INTEL_USB_ROLE_SW) {
+ ret = xhci_create_intel_xhci_sw_pdev(xhci,
+ offset);
+ if (ret)
+ return ret;
+ }
+ break;
+ }
+ offset = xhci_find_next_ext_cap(base, offset, 0);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(xhci_ext_cap_init);
diff --git a/drivers/usb/host/xhci-ext-caps.h b/drivers/usb/host/xhci-ext-caps.h
index bf7316e130d3..268328c20681 100644
--- a/drivers/usb/host/xhci-ext-caps.h
+++ b/drivers/usb/host/xhci-ext-caps.h
@@ -39,6 +39,8 @@
#define XHCI_EXT_CAPS_ROUTE 5
/* IDs 6-9 reserved */
#define XHCI_EXT_CAPS_DEBUG 10
+/* Vendor caps */
+#define XHCI_EXT_CAPS_VENDOR_INTEL 192
/* USB Legacy Support Capability - section 7.1.1 */
#define XHCI_HC_BIOS_OWNED (1 << 16)
#define XHCI_HC_OS_OWNED (1 << 24)
@@ -84,7 +86,8 @@
* @base PCI MMIO registers base address.
* @start address at which to start looking, (0 or HCC_PARAMS to start at
* beginning of list)
- * @id Extended capability ID to search for.
+ * @id Extended capability ID to search for, or 0 for the next
+ * capability
*
* Returns the offset of the next matching extended capability structure.
* Some capabilities can occur several times, e.g., the XHCI_EXT_CAPS_PROTOCOL,
@@ -110,7 +113,7 @@ static inline int xhci_find_next_ext_cap(void __iomem *base, u32 start, int id)
val = readl(base + offset);
if (val == ~0)
return 0;
- if (XHCI_EXT_CAPS_ID(val) == id && offset != start)
+ if (offset != start && (id == 0 || XHCI_EXT_CAPS_ID(val) == id))
return offset;
next = XHCI_EXT_CAPS_NEXT(val);
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 332420d10be9..e5ace8995b3b 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -913,6 +913,8 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id)
if (dev->out_ctx)
xhci_free_container_ctx(xhci, dev->out_ctx);
+ if (dev->udev && dev->udev->slot_id)
+ dev->udev->slot_id = 0;
kfree(xhci->devs[slot_id]);
xhci->devs[slot_id] = NULL;
}
diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c
index b0ab4d5e2751..7334da9e9779 100644
--- a/drivers/usb/host/xhci-mtk.c
+++ b/drivers/usb/host/xhci-mtk.c
@@ -14,7 +14,6 @@
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
@@ -352,62 +351,6 @@ static const struct xhci_driver_overrides xhci_mtk_overrides __initconst = {
static struct hc_driver __read_mostly xhci_mtk_hc_driver;
-static int xhci_mtk_phy_init(struct xhci_hcd_mtk *mtk)
-{
- int i;
- int ret;
-
- for (i = 0; i < mtk->num_phys; i++) {
- ret = phy_init(mtk->phys[i]);
- if (ret)
- goto exit_phy;
- }
- return 0;
-
-exit_phy:
- for (; i > 0; i--)
- phy_exit(mtk->phys[i - 1]);
-
- return ret;
-}
-
-static int xhci_mtk_phy_exit(struct xhci_hcd_mtk *mtk)
-{
- int i;
-
- for (i = 0; i < mtk->num_phys; i++)
- phy_exit(mtk->phys[i]);
-
- return 0;
-}
-
-static int xhci_mtk_phy_power_on(struct xhci_hcd_mtk *mtk)
-{
- int i;
- int ret;
-
- for (i = 0; i < mtk->num_phys; i++) {
- ret = phy_power_on(mtk->phys[i]);
- if (ret)
- goto power_off_phy;
- }
- return 0;
-
-power_off_phy:
- for (; i > 0; i--)
- phy_power_off(mtk->phys[i - 1]);
-
- return ret;
-}
-
-static void xhci_mtk_phy_power_off(struct xhci_hcd_mtk *mtk)
-{
- unsigned int i;
-
- for (i = 0; i < mtk->num_phys; i++)
- phy_power_off(mtk->phys[i]);
-}
-
static int xhci_mtk_ldos_enable(struct xhci_hcd_mtk *mtk)
{
int ret;
@@ -488,8 +431,6 @@ static int xhci_mtk_probe(struct platform_device *pdev)
struct xhci_hcd *xhci;
struct resource *res;
struct usb_hcd *hcd;
- struct phy *phy;
- int phy_num;
int ret = -ENODEV;
int irq;
@@ -529,16 +470,6 @@ static int xhci_mtk_probe(struct platform_device *pdev)
return ret;
}
- mtk->num_phys = of_count_phandle_with_args(node,
- "phys", "#phy-cells");
- if (mtk->num_phys > 0) {
- mtk->phys = devm_kcalloc(dev, mtk->num_phys,
- sizeof(*mtk->phys), GFP_KERNEL);
- if (!mtk->phys)
- return -ENOMEM;
- } else {
- mtk->num_phys = 0;
- }
pm_runtime_enable(dev);
pm_runtime_get_sync(dev);
device_enable_async_suspend(dev);
@@ -596,23 +527,6 @@ static int xhci_mtk_probe(struct platform_device *pdev)
mtk->has_ippc = false;
}
- for (phy_num = 0; phy_num < mtk->num_phys; phy_num++) {
- phy = devm_of_phy_get_by_index(dev, node, phy_num);
- if (IS_ERR(phy)) {
- ret = PTR_ERR(phy);
- goto put_usb2_hcd;
- }
- mtk->phys[phy_num] = phy;
- }
-
- ret = xhci_mtk_phy_init(mtk);
- if (ret)
- goto put_usb2_hcd;
-
- ret = xhci_mtk_phy_power_on(mtk);
- if (ret)
- goto exit_phys;
-
device_init_wakeup(dev, true);
xhci = hcd_to_xhci(hcd);
@@ -630,7 +544,7 @@ static int xhci_mtk_probe(struct platform_device *pdev)
dev_name(dev), hcd);
if (!xhci->shared_hcd) {
ret = -ENOMEM;
- goto power_off_phys;
+ goto disable_device_wakeup;
}
ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
@@ -653,13 +567,9 @@ put_usb3_hcd:
xhci_mtk_sch_exit(mtk);
usb_put_hcd(xhci->shared_hcd);
-power_off_phys:
- xhci_mtk_phy_power_off(mtk);
+disable_device_wakeup:
device_init_wakeup(dev, false);
-exit_phys:
- xhci_mtk_phy_exit(mtk);
-
put_usb2_hcd:
usb_put_hcd(hcd);
@@ -682,8 +592,6 @@ static int xhci_mtk_remove(struct platform_device *dev)
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
usb_remove_hcd(xhci->shared_hcd);
- xhci_mtk_phy_power_off(mtk);
- xhci_mtk_phy_exit(mtk);
device_init_wakeup(&dev->dev, false);
usb_remove_hcd(hcd);
@@ -718,7 +626,6 @@ static int __maybe_unused xhci_mtk_suspend(struct device *dev)
del_timer_sync(&xhci->shared_hcd->rh_timer);
xhci_mtk_host_disable(mtk);
- xhci_mtk_phy_power_off(mtk);
xhci_mtk_clks_disable(mtk);
usb_wakeup_set(mtk, true);
return 0;
@@ -732,7 +639,6 @@ static int __maybe_unused xhci_mtk_resume(struct device *dev)
usb_wakeup_set(mtk, false);
xhci_mtk_clks_enable(mtk);
- xhci_mtk_phy_power_on(mtk);
xhci_mtk_host_enable(mtk);
xhci_dbg(xhci, "%s: restart port polling\n", __func__);
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index d9f831b67e57..85ffda85f8ab 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -126,7 +126,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
if (pdev->vendor == PCI_VENDOR_ID_AMD && usb_amd_find_chipset_info())
xhci->quirks |= XHCI_AMD_PLL_FIX;
- if (pdev->vendor == PCI_VENDOR_ID_AMD && pdev->device == 0x43bb)
+ if (pdev->vendor == PCI_VENDOR_ID_AMD &&
+ (pdev->device == 0x15e0 ||
+ pdev->device == 0x15e1 ||
+ pdev->device == 0x43bb))
xhci->quirks |= XHCI_SUSPEND_DELAY;
if (pdev->vendor == PCI_VENDOR_ID_AMD)
@@ -178,6 +181,7 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI) {
xhci->quirks |= XHCI_SSIC_PORT_UNUSED;
+ xhci->quirks |= XHCI_INTEL_USB_ROLE_SW;
}
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
(pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI ||
@@ -311,6 +315,10 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
goto dealloc_usb2_hcd;
}
+ retval = xhci_ext_cap_init(xhci);
+ if (retval)
+ goto put_usb3_hcd;
+
retval = usb_add_hcd(xhci->shared_hcd, dev->irq,
IRQF_SHARED);
if (retval)
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 6652e2d5bd2e..c1b22fc64e38 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -157,6 +157,7 @@ static int xhci_plat_probe(struct platform_device *pdev)
struct resource *res;
struct usb_hcd *hcd;
struct clk *clk;
+ struct clk *reg_clk;
int ret;
int irq;
@@ -226,17 +227,27 @@ static int xhci_plat_probe(struct platform_device *pdev)
hcd->rsrc_len = resource_size(res);
/*
- * Not all platforms have a clk so it is not an error if the
- * clock does not exists.
+ * Not all platforms have clks so it is not an error if the
+ * clock do not exist.
*/
+ reg_clk = devm_clk_get(&pdev->dev, "reg");
+ if (!IS_ERR(reg_clk)) {
+ ret = clk_prepare_enable(reg_clk);
+ if (ret)
+ goto put_hcd;
+ } else if (PTR_ERR(reg_clk) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto put_hcd;
+ }
+
clk = devm_clk_get(&pdev->dev, NULL);
if (!IS_ERR(clk)) {
ret = clk_prepare_enable(clk);
if (ret)
- goto put_hcd;
+ goto disable_reg_clk;
} else if (PTR_ERR(clk) == -EPROBE_DEFER) {
ret = -EPROBE_DEFER;
- goto put_hcd;
+ goto disable_reg_clk;
}
xhci = hcd_to_xhci(hcd);
@@ -252,6 +263,7 @@ static int xhci_plat_probe(struct platform_device *pdev)
device_wakeup_enable(hcd->self.controller);
xhci->clk = clk;
+ xhci->reg_clk = reg_clk;
xhci->main_hcd = hcd;
xhci->shared_hcd = __usb_create_hcd(driver, sysdev, &pdev->dev,
dev_name(&pdev->dev), hcd);
@@ -284,6 +296,7 @@ static int xhci_plat_probe(struct platform_device *pdev)
ret = usb_phy_init(hcd->usb_phy);
if (ret)
goto put_usb3_hcd;
+ hcd->skip_phy_initialization = 1;
}
ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
@@ -319,8 +332,10 @@ put_usb3_hcd:
usb_put_hcd(xhci->shared_hcd);
disable_clk:
- if (!IS_ERR(clk))
- clk_disable_unprepare(clk);
+ clk_disable_unprepare(clk);
+
+disable_reg_clk:
+ clk_disable_unprepare(reg_clk);
put_hcd:
usb_put_hcd(hcd);
@@ -337,6 +352,7 @@ static int xhci_plat_remove(struct platform_device *dev)
struct usb_hcd *hcd = platform_get_drvdata(dev);
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct clk *clk = xhci->clk;
+ struct clk *reg_clk = xhci->reg_clk;
xhci->xhc_state |= XHCI_STATE_REMOVING;
@@ -346,8 +362,8 @@ static int xhci_plat_remove(struct platform_device *dev)
usb_remove_hcd(hcd);
usb_put_hcd(xhci->shared_hcd);
- if (!IS_ERR(clk))
- clk_disable_unprepare(clk);
+ clk_disable_unprepare(clk);
+ clk_disable_unprepare(reg_clk);
usb_put_hcd(hcd);
pm_runtime_set_suspended(&dev->dev);
@@ -419,7 +435,6 @@ MODULE_DEVICE_TABLE(acpi, usb_xhci_acpi_match);
static struct platform_driver usb_xhci_driver = {
.probe = xhci_plat_probe,
.remove = xhci_plat_remove,
- .shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "xhci-hcd",
.pm = &xhci_plat_pm_ops,
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index daa94c3aed80..91a1a824673d 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1436,7 +1436,8 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
case TRB_STOP_RING:
WARN_ON(slot_id != TRB_TO_SLOT_ID(
le32_to_cpu(cmd_trb->generic.field[3])));
- xhci_handle_cmd_stop_ep(xhci, slot_id, cmd_trb, event);
+ if (!cmd->completion)
+ xhci_handle_cmd_stop_ep(xhci, slot_id, cmd_trb, event);
break;
case TRB_SET_DEQ:
WARN_ON(slot_id != TRB_TO_SLOT_ID(
@@ -1815,8 +1816,7 @@ struct xhci_segment *trb_in_td(struct xhci_hcd *xhci,
static void xhci_cleanup_halted_endpoint(struct xhci_hcd *xhci,
unsigned int slot_id, unsigned int ep_index,
- unsigned int stream_id,
- struct xhci_td *td, union xhci_trb *ep_trb,
+ unsigned int stream_id, struct xhci_td *td,
enum xhci_ep_reset_type reset_type)
{
struct xhci_virt_ep *ep = &xhci->devs[slot_id]->eps[ep_index];
@@ -1829,9 +1829,10 @@ static void xhci_cleanup_halted_endpoint(struct xhci_hcd *xhci,
xhci_queue_reset_ep(xhci, command, slot_id, ep_index, reset_type);
- if (reset_type == EP_HARD_RESET)
+ if (reset_type == EP_HARD_RESET) {
+ ep->ep_state |= EP_HARD_CLEAR_TOGGLE;
xhci_cleanup_stalled_ring(xhci, ep_index, stream_id, td);
-
+ }
xhci_ring_cmd_db(xhci);
}
@@ -1922,7 +1923,7 @@ static int xhci_td_cleanup(struct xhci_hcd *xhci, struct xhci_td *td,
}
static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td,
- union xhci_trb *ep_trb, struct xhci_transfer_event *event,
+ struct xhci_transfer_event *event,
struct xhci_virt_ep *ep, int *status)
{
struct xhci_virt_device *xdev;
@@ -1957,8 +1958,7 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td,
* The class driver clears the device side halt later.
*/
xhci_cleanup_halted_endpoint(xhci, slot_id, ep_index,
- ep_ring->stream_id, td, ep_trb,
- EP_HARD_RESET);
+ ep_ring->stream_id, td, EP_HARD_RESET);
} else {
/* Update ring dequeue pointer */
while (ep_ring->dequeue != td->last_trb)
@@ -2083,7 +2083,7 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
td->urb->actual_length = requested;
finish_td:
- return finish_td(xhci, td, ep_trb, event, ep, status);
+ return finish_td(xhci, td, event, ep, status);
}
/*
@@ -2170,7 +2170,7 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
td->urb->actual_length += frame->actual_length;
- return finish_td(xhci, td, ep_trb, event, ep, status);
+ return finish_td(xhci, td, event, ep, status);
}
static int skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
@@ -2260,7 +2260,7 @@ finish_td:
remaining);
td->urb->actual_length = 0;
}
- return finish_td(xhci, td, ep_trb, event, ep, status);
+ return finish_td(xhci, td, event, ep, status);
}
/*
@@ -2318,7 +2318,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
case COMP_INVALID_STREAM_TYPE_ERROR:
case COMP_INVALID_STREAM_ID_ERROR:
xhci_cleanup_halted_endpoint(xhci, slot_id, ep_index, 0,
- NULL, NULL, EP_SOFT_RESET);
+ NULL, EP_SOFT_RESET);
goto cleanup;
case COMP_RING_UNDERRUN:
case COMP_RING_OVERRUN:
@@ -2584,8 +2584,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
xhci_cleanup_halted_endpoint(xhci, slot_id,
ep_index,
ep_ring->stream_id,
- td, ep_trb,
- EP_HARD_RESET);
+ td, EP_HARD_RESET);
goto cleanup;
}
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 5d37700ae4b0..9b27798ecce5 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -1290,7 +1290,8 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
unsigned long flags;
int ret = 0;
- unsigned int slot_id, ep_index, ep_state;
+ unsigned int slot_id, ep_index;
+ unsigned int *ep_state;
struct urb_priv *urb_priv;
int num_tds;
@@ -1300,6 +1301,7 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
slot_id = urb->dev->slot_id;
ep_index = xhci_get_endpoint_index(&urb->ep->desc);
+ ep_state = &xhci->devs[slot_id]->eps[ep_index].ep_state;
if (!HCD_HW_ACCESSIBLE(hcd)) {
if (!in_interrupt())
@@ -1351,6 +1353,17 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
ret = -ESHUTDOWN;
goto free_priv;
}
+ if (*ep_state & (EP_GETTING_STREAMS | EP_GETTING_NO_STREAMS)) {
+ xhci_warn(xhci, "WARN: Can't enqueue URB, ep in streams transition state %x\n",
+ *ep_state);
+ ret = -EINVAL;
+ goto free_priv;
+ }
+ if (*ep_state & EP_SOFT_CLEAR_TOGGLE) {
+ xhci_warn(xhci, "Can't enqueue URB while manually clearing toggle\n");
+ ret = -EINVAL;
+ goto free_priv;
+ }
switch (usb_endpoint_type(&urb->ep->desc)) {
@@ -1359,23 +1372,13 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
slot_id, ep_index);
break;
case USB_ENDPOINT_XFER_BULK:
- ep_state = xhci->devs[slot_id]->eps[ep_index].ep_state;
- if (ep_state & (EP_GETTING_STREAMS | EP_GETTING_NO_STREAMS)) {
- xhci_warn(xhci, "WARN: Can't enqueue URB, ep in streams transition state %x\n",
- ep_state);
- ret = -EINVAL;
- break;
- }
ret = xhci_queue_bulk_tx(xhci, GFP_ATOMIC, urb,
slot_id, ep_index);
break;
-
-
case USB_ENDPOINT_XFER_INT:
ret = xhci_queue_intr_tx(xhci, GFP_ATOMIC, urb,
slot_id, ep_index);
break;
-
case USB_ENDPOINT_XFER_ISOC:
ret = xhci_queue_isoc_tx_prepare(xhci, GFP_ATOMIC, urb,
slot_id, ep_index);
@@ -2874,33 +2877,103 @@ void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, unsigned int ep_index,
}
}
-/* Called when clearing halted device. The core should have sent the control
- * message to clear the device halt condition. The host side of the halt should
- * already be cleared with a reset endpoint command issued when the STALL tx
- * event was received.
+/*
+ * Called after usb core issues a clear halt control message.
+ * The host side of the halt should already be cleared by a reset endpoint
+ * command issued when the STALL event was received.
*
- * Context: in_interrupt
+ * The reset endpoint command may only be issued to endpoints in the halted
+ * state. For software that wishes to reset the data toggle or sequence number
+ * of an endpoint that isn't in the halted state this function will issue a
+ * configure endpoint command with the Drop and Add bits set for the target
+ * endpoint. Refer to the additional note in xhci spcification section 4.6.8.
*/
static void xhci_endpoint_reset(struct usb_hcd *hcd,
- struct usb_host_endpoint *ep)
+ struct usb_host_endpoint *host_ep)
{
struct xhci_hcd *xhci;
+ struct usb_device *udev;
+ struct xhci_virt_device *vdev;
+ struct xhci_virt_ep *ep;
+ struct xhci_input_control_ctx *ctrl_ctx;
+ struct xhci_command *stop_cmd, *cfg_cmd;
+ unsigned int ep_index;
+ unsigned long flags;
+ u32 ep_flag;
xhci = hcd_to_xhci(hcd);
+ if (!host_ep->hcpriv)
+ return;
+ udev = (struct usb_device *) host_ep->hcpriv;
+ vdev = xhci->devs[udev->slot_id];
+ ep_index = xhci_get_endpoint_index(&host_ep->desc);
+ ep = &vdev->eps[ep_index];
+
+ /* Bail out if toggle is already being cleared by a endpoint reset */
+ if (ep->ep_state & EP_HARD_CLEAR_TOGGLE) {
+ ep->ep_state &= ~EP_HARD_CLEAR_TOGGLE;
+ return;
+ }
+ /* Only interrupt and bulk ep's use data toggle, USB2 spec 5.5.4-> */
+ if (usb_endpoint_xfer_control(&host_ep->desc) ||
+ usb_endpoint_xfer_isoc(&host_ep->desc))
+ return;
+
+ ep_flag = xhci_get_endpoint_flag(&host_ep->desc);
+
+ if (ep_flag == SLOT_FLAG || ep_flag == EP0_FLAG)
+ return;
+
+ stop_cmd = xhci_alloc_command(xhci, true, GFP_NOWAIT);
+ if (!stop_cmd)
+ return;
+
+ cfg_cmd = xhci_alloc_command_with_ctx(xhci, true, GFP_NOWAIT);
+ if (!cfg_cmd)
+ goto cleanup;
+
+ spin_lock_irqsave(&xhci->lock, flags);
+
+ /* block queuing new trbs and ringing ep doorbell */
+ ep->ep_state |= EP_SOFT_CLEAR_TOGGLE;
/*
- * We might need to implement the config ep cmd in xhci 4.8.1 note:
- * The Reset Endpoint Command may only be issued to endpoints in the
- * Halted state. If software wishes reset the Data Toggle or Sequence
- * Number of an endpoint that isn't in the Halted state, then software
- * may issue a Configure Endpoint Command with the Drop and Add bits set
- * for the target endpoint. that is in the Stopped state.
+ * Make sure endpoint ring is empty before resetting the toggle/seq.
+ * Driver is required to synchronously cancel all transfer request.
+ * Stop the endpoint to force xHC to update the output context
*/
- /* For now just print debug to follow the situation */
- xhci_dbg(xhci, "Endpoint 0x%x ep reset callback called\n",
- ep->desc.bEndpointAddress);
+ if (!list_empty(&ep->ring->td_list)) {
+ dev_err(&udev->dev, "EP not empty, refuse reset\n");
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ goto cleanup;
+ }
+ xhci_queue_stop_endpoint(xhci, stop_cmd, udev->slot_id, ep_index, 0);
+ xhci_ring_cmd_db(xhci);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+
+ wait_for_completion(stop_cmd->completion);
+
+ spin_lock_irqsave(&xhci->lock, flags);
+
+ /* config ep command clears toggle if add and drop ep flags are set */
+ ctrl_ctx = xhci_get_input_control_ctx(cfg_cmd->in_ctx);
+ xhci_setup_input_ctx_for_config_ep(xhci, cfg_cmd->in_ctx, vdev->out_ctx,
+ ctrl_ctx, ep_flag, ep_flag);
+ xhci_endpoint_copy(xhci, cfg_cmd->in_ctx, vdev->out_ctx, ep_index);
+
+ xhci_queue_configure_endpoint(xhci, cfg_cmd, cfg_cmd->in_ctx->dma,
+ udev->slot_id, false);
+ xhci_ring_cmd_db(xhci);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+
+ wait_for_completion(cfg_cmd->completion);
+
+ ep->ep_state &= ~EP_SOFT_CLEAR_TOGGLE;
+ xhci_free_command(xhci, cfg_cmd);
+cleanup:
+ xhci_free_command(xhci, stop_cmd);
}
static int xhci_check_streams_endpoint(struct xhci_hcd *xhci,
@@ -4768,6 +4841,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
* quirks
*/
struct device *dev = hcd->self.sysdev;
+ unsigned int minor_rev;
int retval;
/* Accept arbitrarily long scatter-gather lists */
@@ -4795,12 +4869,19 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
*/
hcd->has_tt = 1;
} else {
- /* Some 3.1 hosts return sbrn 0x30, can't rely on sbrn alone */
- if (xhci->sbrn == 0x31 || xhci->usb3_rhub.min_rev >= 1) {
- xhci_info(xhci, "Host supports USB 3.1 Enhanced SuperSpeed\n");
+ /*
+ * Some 3.1 hosts return sbrn 0x30, use xhci supported protocol
+ * minor revision instead of sbrn
+ */
+ minor_rev = xhci->usb3_rhub.min_rev;
+ if (minor_rev) {
hcd->speed = HCD_USB31;
hcd->self.root_hub->speed = USB_SPEED_SUPER_PLUS;
}
+ xhci_info(xhci, "Host supports USB 3.%x %s SuperSpeed\n",
+ minor_rev,
+ minor_rev ? "Enhanced" : "");
+
/* xHCI private pointer was set in xhci_pci_probe for the second
* registered roothub.
*/
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 866e141d4972..6dfc4867dbcf 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -922,6 +922,8 @@ struct xhci_virt_ep {
#define EP_HAS_STREAMS (1 << 4)
/* Transitioning the endpoint to not using streams, don't enqueue URBs */
#define EP_GETTING_NO_STREAMS (1 << 5)
+#define EP_HARD_CLEAR_TOGGLE (1 << 6)
+#define EP_SOFT_CLEAR_TOGGLE (1 << 7)
/* ---- Related to URB cancellation ---- */
struct list_head cancelled_td_list;
/* Watchdog timer for stop endpoint command to cancel URBs */
@@ -1727,8 +1729,9 @@ struct xhci_hcd {
int page_shift;
/* msi-x vectors */
int msix_count;
- /* optional clock */
+ /* optional clocks */
struct clk *clk;
+ struct clk *reg_clk;
/* data structures */
struct xhci_device_context_array *dcbaa;
struct xhci_ring *cmd_ring;
@@ -1827,6 +1830,7 @@ struct xhci_hcd {
#define XHCI_ASMEDIA_MODIFY_FLOWCONTROL (1 << 28)
#define XHCI_HW_LPM_DISABLE (1 << 29)
#define XHCI_SUSPEND_DELAY (1 << 30)
+#define XHCI_INTEL_USB_ROLE_SW (1 << 31)
unsigned int num_active_eps;
unsigned int limit_active_eps;
@@ -2022,6 +2026,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks);
void xhci_init_driver(struct hc_driver *drv,
const struct xhci_driver_overrides *over);
int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id);
+int xhci_ext_cap_init(struct xhci_hcd *xhci);
int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup);
int xhci_resume(struct xhci_hcd *xhci, bool hibernated);
diff --git a/drivers/usb/isp1760/isp1760-udc.c b/drivers/usb/isp1760/isp1760-udc.c
index bac4ef5d9512..1714b2258b54 100644
--- a/drivers/usb/isp1760/isp1760-udc.c
+++ b/drivers/usb/isp1760/isp1760-udc.c
@@ -1441,7 +1441,6 @@ int isp1760_udc_register(struct isp1760_device *isp, int irq,
unsigned long irqflags)
{
struct isp1760_udc *udc = &isp->udc;
- const char *devname;
int ret;
udc->irq = -1;
@@ -1455,13 +1454,10 @@ int isp1760_udc_register(struct isp1760_device *isp, int irq,
if (ret < 0)
return ret;
- devname = dev_name(isp->dev);
- udc->irqname = kmalloc(strlen(devname) + 7, GFP_KERNEL);
+ udc->irqname = kasprintf(GFP_KERNEL, "%s (udc)", dev_name(isp->dev));
if (!udc->irqname)
return -ENOMEM;
- sprintf(udc->irqname, "%s (udc)", devname);
-
ret = request_irq(irq, isp1760_udc_irq, IRQF_SHARED | irqflags,
udc->irqname, udc);
if (ret < 0)
diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c
index 4b8712733fc7..b3160afe0458 100644
--- a/drivers/usb/misc/adutux.c
+++ b/drivers/usb/misc/adutux.c
@@ -54,7 +54,7 @@ MODULE_DEVICE_TABLE(usb, device_table);
/* 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 */
+#define COMMAND_TIMEOUT (2*HZ)
/*
* The locking scheme is a vanilla 3-lock:
@@ -132,6 +132,8 @@ static void adu_abort_transfers(struct adu_device *dev)
spin_lock_irqsave(&dev->buflock, flags);
if (!dev->out_urb_finished) {
spin_unlock_irqrestore(&dev->buflock, flags);
+ wait_event_timeout(dev->write_wait, dev->out_urb_finished,
+ COMMAND_TIMEOUT);
usb_kill_urb(dev->interrupt_out_urb);
} else
spin_unlock_irqrestore(&dev->buflock, flags);
diff --git a/drivers/usb/misc/chaoskey.c b/drivers/usb/misc/chaoskey.c
index 716cb515523e..cf5828ce927a 100644
--- a/drivers/usb/misc/chaoskey.c
+++ b/drivers/usb/misc/chaoskey.c
@@ -168,14 +168,10 @@ static int chaoskey_probe(struct usb_interface *interface,
*/
if (udev->product && udev->serial) {
- dev->name = kmalloc(strlen(udev->product) + 1 +
- strlen(udev->serial) + 1, GFP_KERNEL);
+ dev->name = kasprintf(GFP_KERNEL, "%s-%s", udev->product,
+ udev->serial);
if (dev->name == NULL)
goto out;
-
- strcpy(dev->name, udev->product);
- strcat(dev->name, "-");
- strcat(dev->name, udev->serial);
}
dev->interface = interface;
diff --git a/drivers/usb/misc/sisusbvga/sisusb_con.c b/drivers/usb/misc/sisusbvga/sisusb_con.c
index 73f7bde78e11..a0d6e0af957c 100644
--- a/drivers/usb/misc/sisusbvga/sisusb_con.c
+++ b/drivers/usb/misc/sisusbvga/sisusb_con.c
@@ -1217,7 +1217,7 @@ font_op_error:
/* Interface routine */
static int
sisusbcon_font_set(struct vc_data *c, struct console_font *font,
- unsigned flags)
+ unsigned int flags)
{
struct sisusb_usb_data *sisusb;
unsigned charcount = font->charcount;
@@ -1338,29 +1338,65 @@ static void sisusbdummycon_init(struct vc_data *vc, int init)
vc_resize(vc, 80, 25);
}
-static int sisusbdummycon_dummy(void)
+static void sisusbdummycon_deinit(struct vc_data *vc) { }
+static void sisusbdummycon_clear(struct vc_data *vc, int sy, int sx,
+ int height, int width) { }
+static void sisusbdummycon_putc(struct vc_data *vc, int c, int ypos,
+ int xpos) { }
+static void sisusbdummycon_putcs(struct vc_data *vc, const unsigned short *s,
+ int count, int ypos, int xpos) { }
+static void sisusbdummycon_cursor(struct vc_data *vc, int mode) { }
+
+static bool sisusbdummycon_scroll(struct vc_data *vc, unsigned int top,
+ unsigned int bottom, enum con_scroll dir,
+ unsigned int lines)
{
- return 0;
+ return false;
}
-#define SISUSBCONDUMMY (void *)sisusbdummycon_dummy
+static int sisusbdummycon_switch(struct vc_data *vc)
+{
+ return 0;
+}
+
+static int sisusbdummycon_blank(struct vc_data *vc, int blank, int mode_switch)
+{
+ return 0;
+}
+
+static int sisusbdummycon_font_set(struct vc_data *vc,
+ struct console_font *font,
+ unsigned int flags)
+{
+ return 0;
+}
+
+static int sisusbdummycon_font_default(struct vc_data *vc,
+ struct console_font *font, char *name)
+{
+ return 0;
+}
+
+static int sisusbdummycon_font_copy(struct vc_data *vc, int con)
+{
+ return 0;
+}
static const struct consw sisusb_dummy_con = {
.owner = THIS_MODULE,
.con_startup = sisusbdummycon_startup,
.con_init = sisusbdummycon_init,
- .con_deinit = SISUSBCONDUMMY,
- .con_clear = SISUSBCONDUMMY,
- .con_putc = SISUSBCONDUMMY,
- .con_putcs = SISUSBCONDUMMY,
- .con_cursor = SISUSBCONDUMMY,
- .con_scroll = SISUSBCONDUMMY,
- .con_switch = SISUSBCONDUMMY,
- .con_blank = SISUSBCONDUMMY,
- .con_font_set = SISUSBCONDUMMY,
- .con_font_get = SISUSBCONDUMMY,
- .con_font_default = SISUSBCONDUMMY,
- .con_font_copy = SISUSBCONDUMMY,
+ .con_deinit = sisusbdummycon_deinit,
+ .con_clear = sisusbdummycon_clear,
+ .con_putc = sisusbdummycon_putc,
+ .con_putcs = sisusbdummycon_putcs,
+ .con_cursor = sisusbdummycon_cursor,
+ .con_scroll = sisusbdummycon_scroll,
+ .con_switch = sisusbdummycon_switch,
+ .con_blank = sisusbdummycon_blank,
+ .con_font_set = sisusbdummycon_font_set,
+ .con_font_default = sisusbdummycon_font_default,
+ .con_font_copy = sisusbdummycon_font_copy,
};
int
diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c
index 90028ef541e3..9e1142b8b91b 100644
--- a/drivers/usb/misc/usbtest.c
+++ b/drivers/usb/misc/usbtest.c
@@ -2028,11 +2028,14 @@ test_queue(struct usbtest_dev *dev, struct usbtest_param_32 *param,
unsigned i;
unsigned long packets = 0;
int status = 0;
- struct urb *urbs[param->sglen];
+ struct urb *urbs[MAX_SGLEN];
if (!param->sglen || param->iterations > UINT_MAX / param->sglen)
return -EINVAL;
+ if (param->sglen > MAX_SGLEN)
+ return -EINVAL;
+
memset(&context, 0, sizeof(context));
context.count = param->iterations * param->sglen;
context.dev = dev;
diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c
index 263c97fec708..de9a502491c2 100644
--- a/drivers/usb/misc/uss720.c
+++ b/drivers/usb/misc/uss720.c
@@ -769,10 +769,15 @@ static void uss720_disconnect(struct usb_interface *intf)
/* table of cables that work through this driver */
static const struct usb_device_id uss720_table[] = {
{ USB_DEVICE(0x047e, 0x1001) },
+ { USB_DEVICE(0x04b8, 0x0002) },
+ { USB_DEVICE(0x04b8, 0x0003) },
+ { USB_DEVICE(0x050d, 0x0002) },
+ { USB_DEVICE(0x050d, 0x1202) },
{ USB_DEVICE(0x0557, 0x2001) },
+ { USB_DEVICE(0x05ab, 0x0002) },
+ { USB_DEVICE(0x06c6, 0x0100) },
{ USB_DEVICE(0x0729, 0x1284) },
{ USB_DEVICE(0x1293, 0x0002) },
- { USB_DEVICE(0x050d, 0x0002) },
{ } /* Terminating entry */
};
diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
index 5506a9c03c1f..ad08895e78f9 100644
--- a/drivers/usb/musb/Kconfig
+++ b/drivers/usb/musb/Kconfig
@@ -5,7 +5,7 @@
# (M)HDRC = (Multipoint) Highspeed Dual-Role Controller
config USB_MUSB_HDRC
- tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, AW, ...)'
+ tristate 'Inventra Highspeed Dual Role Controller'
depends on (USB || USB_GADGET)
depends on HAS_IOMEM
help
@@ -18,9 +18,6 @@ config USB_MUSB_HDRC
Texas Instruments families using this IP include DaVinci
(35x, 644x ...), OMAP 243x, OMAP 3, and TUSB 6010.
- Analog Devices parts using this IP include Blackfin BF54x,
- BF525 and BF527.
-
Allwinner SoCs using this IP include A10, A13, A20, ...
If you do not know what this is, please say N.
@@ -87,7 +84,7 @@ config USB_MUSB_DA8XX
config USB_MUSB_TUSB6010
tristate "TUSB6010"
depends on HAS_IOMEM
- depends on (ARCH_OMAP2PLUS || COMPILE_TEST) && !BLACKFIN
+ depends on ARCH_OMAP2PLUS || COMPILE_TEST
depends on NOP_USB_XCEIV = USB_MUSB_HDRC # both built-in or both modules
config USB_MUSB_OMAP2PLUS
@@ -107,11 +104,6 @@ config USB_MUSB_DSPS
depends on ARCH_OMAP2PLUS || COMPILE_TEST
depends on OF_IRQ
-config USB_MUSB_BLACKFIN
- tristate "Blackfin"
- depends on (BF54x && !BF544) || (BF52x && ! BF522 && !BF523)
- depends on NOP_USB_XCEIV
-
config USB_MUSB_UX500
tristate "Ux500 platforms"
depends on ARCH_U8500 || COMPILE_TEST
@@ -149,7 +141,7 @@ config USB_UX500_DMA
config USB_INVENTRA_DMA
bool 'Inventra'
- depends on USB_MUSB_OMAP2PLUS || USB_MUSB_BLACKFIN
+ depends on USB_MUSB_OMAP2PLUS
help
Enable DMA transfers using Mentor's engine.
diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile
index 79d4d5439164..3a88c79e650c 100644
--- a/drivers/usb/musb/Makefile
+++ b/drivers/usb/musb/Makefile
@@ -21,7 +21,6 @@ obj-$(CONFIG_USB_MUSB_DSPS) += musb_dsps.o
obj-$(CONFIG_USB_MUSB_TUSB6010) += tusb6010.o
obj-$(CONFIG_USB_MUSB_DAVINCI) += davinci.o
obj-$(CONFIG_USB_MUSB_DA8XX) += da8xx.o
-obj-$(CONFIG_USB_MUSB_BLACKFIN) += blackfin.o
obj-$(CONFIG_USB_MUSB_UX500) += ux500.o
obj-$(CONFIG_USB_MUSB_JZ4740) += jz4740.o
obj-$(CONFIG_USB_MUSB_SUNXI) += sunxi.o
diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c
deleted file mode 100644
index 0a98dcd66d19..000000000000
--- a/drivers/usb/musb/blackfin.c
+++ /dev/null
@@ -1,623 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * MUSB OTG controller driver for Blackfin Processors
- *
- * Copyright 2006-2008 Analog Devices Inc.
- *
- * Enter bugs at http://blackfin.uclinux.org/
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/list.h>
-#include <linux/gpio.h>
-#include <linux/io.h>
-#include <linux/err.h>
-#include <linux/platform_device.h>
-#include <linux/dma-mapping.h>
-#include <linux/prefetch.h>
-#include <linux/usb/usb_phy_generic.h>
-
-#include <asm/cacheflush.h>
-
-#include "musb_core.h"
-#include "musbhsdma.h"
-#include "blackfin.h"
-
-struct bfin_glue {
- struct device *dev;
- struct platform_device *musb;
- struct platform_device *phy;
-};
-#define glue_to_musb(g) platform_get_drvdata(g->musb)
-
-static u32 bfin_fifo_offset(u8 epnum)
-{
- return USB_OFFSET(USB_EP0_FIFO) + (epnum * 8);
-}
-
-static u8 bfin_readb(const void __iomem *addr, unsigned offset)
-{
- return (u8)(bfin_read16(addr + offset));
-}
-
-static u16 bfin_readw(const void __iomem *addr, unsigned offset)
-{
- return bfin_read16(addr + offset);
-}
-
-static u32 bfin_readl(const void __iomem *addr, unsigned offset)
-{
- return (u32)(bfin_read16(addr + offset));
-}
-
-static void bfin_writeb(void __iomem *addr, unsigned offset, u8 data)
-{
- bfin_write16(addr + offset, (u16)data);
-}
-
-static void bfin_writew(void __iomem *addr, unsigned offset, u16 data)
-{
- bfin_write16(addr + offset, data);
-}
-
-static void bfin_writel(void __iomem *addr, unsigned offset, u32 data)
-{
- bfin_write16(addr + offset, (u16)data);
-}
-
-/*
- * Load an endpoint's FIFO
- */
-static void bfin_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src)
-{
- struct musb *musb = hw_ep->musb;
- void __iomem *fifo = hw_ep->fifo;
- void __iomem *epio = hw_ep->regs;
- u8 epnum = hw_ep->epnum;
-
- prefetch((u8 *)src);
-
- musb_writew(epio, MUSB_TXCOUNT, len);
-
- dev_dbg(musb->controller, "TX ep%d fifo %p count %d buf %p, epio %p\n",
- hw_ep->epnum, fifo, len, src, epio);
-
- dump_fifo_data(src, len);
-
- if (!ANOMALY_05000380 && epnum != 0) {
- u16 dma_reg;
-
- flush_dcache_range((unsigned long)src,
- (unsigned long)(src + len));
-
- /* Setup DMA address register */
- dma_reg = (u32)src;
- bfin_write16(USB_DMA_REG(epnum, USB_DMAx_ADDR_LOW), dma_reg);
- SSYNC();
-
- dma_reg = (u32)src >> 16;
- bfin_write16(USB_DMA_REG(epnum, USB_DMAx_ADDR_HIGH), dma_reg);
- SSYNC();
-
- /* Setup DMA count register */
- bfin_write16(USB_DMA_REG(epnum, USB_DMAx_COUNT_LOW), len);
- bfin_write16(USB_DMA_REG(epnum, USB_DMAx_COUNT_HIGH), 0);
- SSYNC();
-
- /* Enable the DMA */
- dma_reg = (epnum << 4) | DMA_ENA | INT_ENA | DIRECTION;
- bfin_write16(USB_DMA_REG(epnum, USB_DMAx_CTRL), dma_reg);
- SSYNC();
-
- /* Wait for complete */
- while (!(bfin_read_USB_DMA_INTERRUPT() & (1 << epnum)))
- cpu_relax();
-
- /* acknowledge dma interrupt */
- bfin_write_USB_DMA_INTERRUPT(1 << epnum);
- SSYNC();
-
- /* Reset DMA */
- bfin_write16(USB_DMA_REG(epnum, USB_DMAx_CTRL), 0);
- SSYNC();
- } else {
- SSYNC();
-
- if (unlikely((unsigned long)src & 0x01))
- outsw_8((unsigned long)fifo, src, (len + 1) >> 1);
- else
- outsw((unsigned long)fifo, src, (len + 1) >> 1);
- }
-}
-/*
- * Unload an endpoint's FIFO
- */
-static void bfin_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
-{
- struct musb *musb = hw_ep->musb;
- void __iomem *fifo = hw_ep->fifo;
- u8 epnum = hw_ep->epnum;
-
- if (ANOMALY_05000467 && epnum != 0) {
- u16 dma_reg;
-
- invalidate_dcache_range((unsigned long)dst,
- (unsigned long)(dst + len));
-
- /* Setup DMA address register */
- dma_reg = (u32)dst;
- bfin_write16(USB_DMA_REG(epnum, USB_DMAx_ADDR_LOW), dma_reg);
- SSYNC();
-
- dma_reg = (u32)dst >> 16;
- bfin_write16(USB_DMA_REG(epnum, USB_DMAx_ADDR_HIGH), dma_reg);
- SSYNC();
-
- /* Setup DMA count register */
- bfin_write16(USB_DMA_REG(epnum, USB_DMAx_COUNT_LOW), len);
- bfin_write16(USB_DMA_REG(epnum, USB_DMAx_COUNT_HIGH), 0);
- SSYNC();
-
- /* Enable the DMA */
- dma_reg = (epnum << 4) | DMA_ENA | INT_ENA;
- bfin_write16(USB_DMA_REG(epnum, USB_DMAx_CTRL), dma_reg);
- SSYNC();
-
- /* Wait for complete */
- while (!(bfin_read_USB_DMA_INTERRUPT() & (1 << epnum)))
- cpu_relax();
-
- /* acknowledge dma interrupt */
- bfin_write_USB_DMA_INTERRUPT(1 << epnum);
- SSYNC();
-
- /* Reset DMA */
- bfin_write16(USB_DMA_REG(epnum, USB_DMAx_CTRL), 0);
- SSYNC();
- } else {
- SSYNC();
- /* Read the last byte of packet with odd size from address fifo + 4
- * to trigger 1 byte access to EP0 FIFO.
- */
- if (len == 1)
- *dst = (u8)inw((unsigned long)fifo + 4);
- else {
- if (unlikely((unsigned long)dst & 0x01))
- insw_8((unsigned long)fifo, dst, len >> 1);
- else
- insw((unsigned long)fifo, dst, len >> 1);
-
- if (len & 0x01)
- *(dst + len - 1) = (u8)inw((unsigned long)fifo + 4);
- }
- }
- dev_dbg(musb->controller, "%cX ep%d fifo %p count %d buf %p\n",
- 'R', hw_ep->epnum, fifo, len, dst);
-
- dump_fifo_data(dst, len);
-}
-
-static irqreturn_t blackfin_interrupt(int irq, void *__hci)
-{
- unsigned long flags;
- irqreturn_t retval = IRQ_NONE;
- struct musb *musb = __hci;
-
- spin_lock_irqsave(&musb->lock, flags);
-
- musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
- musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX);
- musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX);
-
- if (musb->int_usb || musb->int_tx || musb->int_rx) {
- musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb);
- musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx);
- musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx);
- retval = musb_interrupt(musb);
- }
-
- /* Start sampling ID pin, when plug is removed from MUSB */
- if ((musb->xceiv->otg->state == OTG_STATE_B_IDLE
- || musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON) ||
- (musb->int_usb & MUSB_INTR_DISCONNECT && is_host_active(musb))) {
- mod_timer(&musb->dev_timer, jiffies + TIMER_DELAY);
- musb->a_wait_bcon = TIMER_DELAY;
- }
-
- spin_unlock_irqrestore(&musb->lock, flags);
-
- return retval;
-}
-
-static void musb_conn_timer_handler(struct timer_list *t)
-{
- struct musb *musb = from_timer(musb, t, dev_timer);
- unsigned long flags;
- u16 val;
- static u8 toggle;
-
- spin_lock_irqsave(&musb->lock, flags);
- switch (musb->xceiv->otg->state) {
- case OTG_STATE_A_IDLE:
- case OTG_STATE_A_WAIT_BCON:
- /* Start a new session */
- val = musb_readw(musb->mregs, MUSB_DEVCTL);
- val &= ~MUSB_DEVCTL_SESSION;
- musb_writew(musb->mregs, MUSB_DEVCTL, val);
- val |= MUSB_DEVCTL_SESSION;
- musb_writew(musb->mregs, MUSB_DEVCTL, val);
- /* Check if musb is host or peripheral. */
- val = musb_readw(musb->mregs, MUSB_DEVCTL);
-
- if (!(val & MUSB_DEVCTL_BDEVICE)) {
- gpio_set_value(musb->config->gpio_vrsel, 1);
- musb->xceiv->otg->state = OTG_STATE_A_WAIT_BCON;
- } else {
- gpio_set_value(musb->config->gpio_vrsel, 0);
- /* Ignore VBUSERROR and SUSPEND IRQ */
- val = musb_readb(musb->mregs, MUSB_INTRUSBE);
- val &= ~MUSB_INTR_VBUSERROR;
- musb_writeb(musb->mregs, MUSB_INTRUSBE, val);
-
- val = MUSB_INTR_SUSPEND | MUSB_INTR_VBUSERROR;
- musb_writeb(musb->mregs, MUSB_INTRUSB, val);
- musb->xceiv->otg->state = OTG_STATE_B_IDLE;
- }
- mod_timer(&musb->dev_timer, jiffies + TIMER_DELAY);
- break;
- case OTG_STATE_B_IDLE:
- /*
- * Start a new session. It seems that MUSB needs taking
- * some time to recognize the type of the plug inserted?
- */
- val = musb_readw(musb->mregs, MUSB_DEVCTL);
- val |= MUSB_DEVCTL_SESSION;
- musb_writew(musb->mregs, MUSB_DEVCTL, val);
- val = musb_readw(musb->mregs, MUSB_DEVCTL);
-
- if (!(val & MUSB_DEVCTL_BDEVICE)) {
- gpio_set_value(musb->config->gpio_vrsel, 1);
- musb->xceiv->otg->state = OTG_STATE_A_WAIT_BCON;
- } else {
- gpio_set_value(musb->config->gpio_vrsel, 0);
-
- /* Ignore VBUSERROR and SUSPEND IRQ */
- val = musb_readb(musb->mregs, MUSB_INTRUSBE);
- val &= ~MUSB_INTR_VBUSERROR;
- musb_writeb(musb->mregs, MUSB_INTRUSBE, val);
-
- val = MUSB_INTR_SUSPEND | MUSB_INTR_VBUSERROR;
- musb_writeb(musb->mregs, MUSB_INTRUSB, val);
-
- /* Toggle the Soft Conn bit, so that we can response to
- * the inserting of either A-plug or B-plug.
- */
- if (toggle) {
- val = musb_readb(musb->mregs, MUSB_POWER);
- val &= ~MUSB_POWER_SOFTCONN;
- musb_writeb(musb->mregs, MUSB_POWER, val);
- toggle = 0;
- } else {
- val = musb_readb(musb->mregs, MUSB_POWER);
- val |= MUSB_POWER_SOFTCONN;
- musb_writeb(musb->mregs, MUSB_POWER, val);
- toggle = 1;
- }
- /* The delay time is set to 1/4 second by default,
- * shortening it, if accelerating A-plug detection
- * is needed in OTG mode.
- */
- mod_timer(&musb->dev_timer, jiffies + TIMER_DELAY / 4);
- }
- break;
- default:
- dev_dbg(musb->controller, "%s state not handled\n",
- usb_otg_state_string(musb->xceiv->otg->state));
- break;
- }
- spin_unlock_irqrestore(&musb->lock, flags);
-
- dev_dbg(musb->controller, "state is %s\n",
- usb_otg_state_string(musb->xceiv->otg->state));
-}
-
-static void bfin_musb_enable(struct musb *musb)
-{
- /* REVISIT is this really correct ? */
-}
-
-static void bfin_musb_disable(struct musb *musb)
-{
-}
-
-static void bfin_musb_set_vbus(struct musb *musb, int is_on)
-{
- int value = musb->config->gpio_vrsel_active;
- if (!is_on)
- value = !value;
- gpio_set_value(musb->config->gpio_vrsel, value);
-
- dev_dbg(musb->controller, "VBUS %s, devctl %02x "
- /* otg %3x conf %08x prcm %08x */ "\n",
- usb_otg_state_string(musb->xceiv->otg->state),
- musb_readb(musb->mregs, MUSB_DEVCTL));
-}
-
-static int bfin_musb_set_power(struct usb_phy *x, unsigned mA)
-{
- return 0;
-}
-
-static int bfin_musb_vbus_status(struct musb *musb)
-{
- return 0;
-}
-
-static int bfin_musb_set_mode(struct musb *musb, u8 musb_mode)
-{
- return -EIO;
-}
-
-static int bfin_musb_adjust_channel_params(struct dma_channel *channel,
- u16 packet_sz, u8 *mode,
- dma_addr_t *dma_addr, u32 *len)
-{
- struct musb_dma_channel *musb_channel = channel->private_data;
-
- /*
- * Anomaly 05000450 might cause data corruption when using DMA
- * MODE 1 transmits with short packet. So to work around this,
- * we truncate all MODE 1 transfers down to a multiple of the
- * max packet size, and then do the last short packet transfer
- * (if there is any) using MODE 0.
- */
- if (ANOMALY_05000450) {
- if (musb_channel->transmit && *mode == 1)
- *len = *len - (*len % packet_sz);
- }
-
- return 0;
-}
-
-static void bfin_musb_reg_init(struct musb *musb)
-{
- if (ANOMALY_05000346) {
- bfin_write_USB_APHY_CALIB(ANOMALY_05000346_value);
- SSYNC();
- }
-
- if (ANOMALY_05000347) {
- bfin_write_USB_APHY_CNTRL(0x0);
- SSYNC();
- }
-
- /* Configure PLL oscillator register */
- bfin_write_USB_PLLOSC_CTRL(0x3080 |
- ((480/musb->config->clkin) << 1));
- SSYNC();
-
- bfin_write_USB_SRP_CLKDIV((get_sclk()/1000) / 32 - 1);
- SSYNC();
-
- bfin_write_USB_EP_NI0_RXMAXP(64);
- SSYNC();
-
- bfin_write_USB_EP_NI0_TXMAXP(64);
- SSYNC();
-
- /* Route INTRUSB/INTR_RX/INTR_TX to USB_INT0*/
- bfin_write_USB_GLOBINTR(0x7);
- SSYNC();
-
- bfin_write_USB_GLOBAL_CTL(GLOBAL_ENA | EP1_TX_ENA | EP2_TX_ENA |
- EP3_TX_ENA | EP4_TX_ENA | EP5_TX_ENA |
- EP6_TX_ENA | EP7_TX_ENA | EP1_RX_ENA |
- EP2_RX_ENA | EP3_RX_ENA | EP4_RX_ENA |
- EP5_RX_ENA | EP6_RX_ENA | EP7_RX_ENA);
- SSYNC();
-}
-
-static int bfin_musb_init(struct musb *musb)
-{
-
- /*
- * Rev 1.0 BF549 EZ-KITs require PE7 to be high for both DEVICE
- * and OTG HOST modes, while rev 1.1 and greater require PE7 to
- * be low for DEVICE mode and high for HOST mode. We set it high
- * here because we are in host mode
- */
-
- if (gpio_request(musb->config->gpio_vrsel, "USB_VRSEL")) {
- printk(KERN_ERR "Failed ro request USB_VRSEL GPIO_%d\n",
- musb->config->gpio_vrsel);
- return -ENODEV;
- }
- gpio_direction_output(musb->config->gpio_vrsel, 0);
-
- musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2);
- if (IS_ERR_OR_NULL(musb->xceiv)) {
- gpio_free(musb->config->gpio_vrsel);
- return -EPROBE_DEFER;
- }
-
- bfin_musb_reg_init(musb);
-
- timer_setup(&musb->dev_timer, musb_conn_timer_handler, 0);
-
- musb->xceiv->set_power = bfin_musb_set_power;
-
- musb->isr = blackfin_interrupt;
- musb->double_buffer_not_ok = true;
-
- return 0;
-}
-
-static int bfin_musb_exit(struct musb *musb)
-{
- gpio_free(musb->config->gpio_vrsel);
- usb_put_phy(musb->xceiv);
-
- return 0;
-}
-
-static const struct musb_platform_ops bfin_ops = {
- .quirks = MUSB_DMA_INVENTRA,
- .init = bfin_musb_init,
- .exit = bfin_musb_exit,
-
- .fifo_offset = bfin_fifo_offset,
- .readb = bfin_readb,
- .writeb = bfin_writeb,
- .readw = bfin_readw,
- .writew = bfin_writew,
- .readl = bfin_readl,
- .writel = bfin_writel,
- .fifo_mode = 2,
- .read_fifo = bfin_read_fifo,
- .write_fifo = bfin_write_fifo,
-#ifdef CONFIG_USB_INVENTRA_DMA
- .dma_init = musbhs_dma_controller_create,
- .dma_exit = musbhs_dma_controller_destroy,
-#endif
- .enable = bfin_musb_enable,
- .disable = bfin_musb_disable,
-
- .set_mode = bfin_musb_set_mode,
-
- .vbus_status = bfin_musb_vbus_status,
- .set_vbus = bfin_musb_set_vbus,
-
- .adjust_channel_params = bfin_musb_adjust_channel_params,
-};
-
-static u64 bfin_dmamask = DMA_BIT_MASK(32);
-
-static int bfin_probe(struct platform_device *pdev)
-{
- struct resource musb_resources[2];
- struct musb_hdrc_platform_data *pdata = dev_get_platdata(&pdev->dev);
- struct platform_device *musb;
- struct bfin_glue *glue;
-
- int ret = -ENOMEM;
-
- glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
- if (!glue)
- goto err0;
-
- musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO);
- if (!musb)
- goto err0;
-
- musb->dev.parent = &pdev->dev;
- musb->dev.dma_mask = &bfin_dmamask;
- musb->dev.coherent_dma_mask = bfin_dmamask;
-
- glue->dev = &pdev->dev;
- glue->musb = musb;
-
- pdata->platform_ops = &bfin_ops;
-
- glue->phy = usb_phy_generic_register();
- if (IS_ERR(glue->phy))
- goto err1;
- platform_set_drvdata(pdev, glue);
-
- memset(musb_resources, 0x00, sizeof(*musb_resources) *
- ARRAY_SIZE(musb_resources));
-
- musb_resources[0].name = pdev->resource[0].name;
- musb_resources[0].start = pdev->resource[0].start;
- musb_resources[0].end = pdev->resource[0].end;
- musb_resources[0].flags = pdev->resource[0].flags;
-
- musb_resources[1].name = pdev->resource[1].name;
- musb_resources[1].start = pdev->resource[1].start;
- musb_resources[1].end = pdev->resource[1].end;
- musb_resources[1].flags = pdev->resource[1].flags;
-
- ret = platform_device_add_resources(musb, musb_resources,
- ARRAY_SIZE(musb_resources));
- if (ret) {
- dev_err(&pdev->dev, "failed to add resources\n");
- goto err2;
- }
-
- ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
- if (ret) {
- dev_err(&pdev->dev, "failed to add platform_data\n");
- goto err2;
- }
-
- ret = platform_device_add(musb);
- if (ret) {
- dev_err(&pdev->dev, "failed to register musb device\n");
- goto err2;
- }
-
- return 0;
-
-err2:
- usb_phy_generic_unregister(glue->phy);
-
-err1:
- platform_device_put(musb);
-
-err0:
- return ret;
-}
-
-static int bfin_remove(struct platform_device *pdev)
-{
- struct bfin_glue *glue = platform_get_drvdata(pdev);
-
- platform_device_unregister(glue->musb);
- usb_phy_generic_unregister(glue->phy);
-
- return 0;
-}
-
-static int __maybe_unused bfin_suspend(struct device *dev)
-{
- struct bfin_glue *glue = dev_get_drvdata(dev);
- struct musb *musb = glue_to_musb(glue);
-
- if (is_host_active(musb))
- /*
- * During hibernate gpio_vrsel will change from high to low
- * low which will generate wakeup event resume the system
- * immediately. Set it to 0 before hibernate to avoid this
- * wakeup event.
- */
- gpio_set_value(musb->config->gpio_vrsel, 0);
-
- return 0;
-}
-
-static int __maybe_unused bfin_resume(struct device *dev)
-{
- struct bfin_glue *glue = dev_get_drvdata(dev);
- struct musb *musb = glue_to_musb(glue);
-
- bfin_musb_reg_init(musb);
-
- return 0;
-}
-
-static SIMPLE_DEV_PM_OPS(bfin_pm_ops, bfin_suspend, bfin_resume);
-
-static struct platform_driver bfin_driver = {
- .probe = bfin_probe,
- .remove = bfin_remove,
- .driver = {
- .name = "musb-blackfin",
- .pm = &bfin_pm_ops,
- },
-};
-
-MODULE_DESCRIPTION("Blackfin MUSB Glue Layer");
-MODULE_AUTHOR("Bryan Wy <cooloney@kernel.org>");
-MODULE_LICENSE("GPL v2");
-module_platform_driver(bfin_driver);
diff --git a/drivers/usb/musb/blackfin.h b/drivers/usb/musb/blackfin.h
deleted file mode 100644
index 5b149915b0f8..000000000000
--- a/drivers/usb/musb/blackfin.h
+++ /dev/null
@@ -1,81 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright (C) 2007 by Analog Devices, Inc.
- */
-
-#ifndef __MUSB_BLACKFIN_H__
-#define __MUSB_BLACKFIN_H__
-
-/*
- * Blackfin specific definitions
- */
-
-/* Anomalies notes:
- *
- * 05000450 - USB DMA Mode 1 Short Packet Data Corruption:
- * MUSB driver is designed to transfer buffer of N * maxpacket size
- * in DMA mode 1 and leave the rest of the data to the next
- * transfer in DMA mode 0, so we never transmit a short packet in
- * DMA mode 1.
- *
- * 05000463 - This anomaly doesn't affect this driver since it
- * never uses L1 or L2 memory as data destination.
- *
- * 05000464 - This anomaly doesn't affect this driver since it
- * never uses L1 or L2 memory as data source.
- *
- * 05000465 - The anomaly can be seen when SCLK is over 100 MHz, and there is
- * no way to workaround for bulk endpoints. Since the wMaxPackSize
- * of bulk is less than or equal to 512, while the fifo size of
- * endpoint 5, 6, 7 is 1024, the double buffer mode is enabled
- * automatically when these endpoints are used for bulk OUT.
- *
- * 05000466 - This anomaly doesn't affect this driver since it never mixes
- * concurrent DMA and core accesses to the TX endpoint FIFOs.
- *
- * 05000467 - The workaround for this anomaly will introduce another
- * anomaly - 05000465.
- */
-
-/* The Mentor USB DMA engine on BF52x (silicon v0.0 and v0.1) seems to be
- * unstable in host mode. This may be caused by Anomaly 05000380. After
- * digging out the root cause, we will change this number accordingly.
- * So, need to either use silicon v0.2+ or disable DMA mode in MUSB.
- */
-#if ANOMALY_05000380 && defined(CONFIG_BF52x) && \
- !defined(CONFIG_MUSB_PIO_ONLY)
-# error "Please use PIO mode in MUSB driver on bf52x chip v0.0 and v0.1"
-#endif
-
-#undef DUMP_FIFO_DATA
-#ifdef DUMP_FIFO_DATA
-static void dump_fifo_data(u8 *buf, u16 len)
-{
- u8 *tmp = buf;
- int i;
-
- for (i = 0; i < len; i++) {
- if (!(i % 16) && i)
- pr_debug("\n");
- pr_debug("%02x ", *tmp++);
- }
- pr_debug("\n");
-}
-#else
-#define dump_fifo_data(buf, len) do {} while (0)
-#endif
-
-
-#define USB_DMA_BASE USB_DMA_INTERRUPT
-#define USB_DMAx_CTRL 0x04
-#define USB_DMAx_ADDR_LOW 0x08
-#define USB_DMAx_ADDR_HIGH 0x0C
-#define USB_DMAx_COUNT_LOW 0x10
-#define USB_DMAx_COUNT_HIGH 0x14
-
-#define USB_DMA_REG(ep, reg) (USB_DMA_BASE + 0x20 * ep + reg)
-
-/* Almost 1 second */
-#define TIMER_DELAY (1 * HZ)
-
-#endif /* __MUSB_BLACKFIN_H__ */
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 4d723077be2b..fb5e4523dc28 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -126,7 +126,6 @@ EXPORT_SYMBOL_GPL(musb_get_mode);
/*-------------------------------------------------------------------------*/
-#ifndef CONFIG_BLACKFIN
static int musb_ulpi_read(struct usb_phy *phy, u32 reg)
{
void __iomem *addr = phy->io_priv;
@@ -208,10 +207,6 @@ out:
return ret;
}
-#else
-#define musb_ulpi_read NULL
-#define musb_ulpi_write NULL
-#endif
static struct usb_phy_io_ops musb_ulpi_access = {
.read = musb_ulpi_read,
@@ -2173,7 +2168,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
* - initializes musb->xceiv, usually by otg_get_phy()
* - stops powering VBUS
*
- * There are various transceiver configurations. Blackfin,
+ * There are various transceiver configurations.
* DaVinci, TUSB60x0, and others integrate them. OMAP3 uses
* external/discrete ones in various flavors (twl4030 family,
* isp1504, non-OTG, etc) mostly hooking up through ULPI.
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index 385841ee6f46..8a74cb2907f8 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -414,19 +414,6 @@ struct musb {
struct usb_gadget_driver *gadget_driver; /* its driver */
struct usb_hcd *hcd; /* the usb hcd */
- /*
- * FIXME: Remove this flag.
- *
- * This is only added to allow Blackfin to work
- * with current driver. For some unknown reason
- * Blackfin doesn't work with double buffering
- * and that's enabled by default.
- *
- * We added this flag to forcefully disable double
- * buffering until we get it working.
- */
- unsigned double_buffer_not_ok:1;
-
const struct musb_hdrc_config *config;
int xceiv_old_state;
@@ -467,34 +454,6 @@ static inline char *musb_ep_xfertype_string(u8 type)
return s;
}
-#ifdef CONFIG_BLACKFIN
-static inline int musb_read_fifosize(struct musb *musb,
- struct musb_hw_ep *hw_ep, u8 epnum)
-{
- musb->nr_endpoints++;
- musb->epmask |= (1 << epnum);
-
- if (epnum < 5) {
- hw_ep->max_packet_sz_tx = 128;
- hw_ep->max_packet_sz_rx = 128;
- } else {
- hw_ep->max_packet_sz_tx = 1024;
- hw_ep->max_packet_sz_rx = 1024;
- }
- hw_ep->is_shared_fifo = false;
-
- return 0;
-}
-
-static inline void musb_configure_ep0(struct musb *musb)
-{
- musb->endpoints[0].max_packet_sz_tx = MUSB_EP0_FIFOSIZE;
- musb->endpoints[0].max_packet_sz_rx = MUSB_EP0_FIFOSIZE;
- musb->endpoints[0].is_shared_fifo = true;
-}
-
-#else
-
static inline int musb_read_fifosize(struct musb *musb,
struct musb_hw_ep *hw_ep, u8 epnum)
{
@@ -531,8 +490,6 @@ static inline void musb_configure_ep0(struct musb *musb)
musb->endpoints[0].max_packet_sz_rx = MUSB_EP0_FIFOSIZE;
musb->endpoints[0].is_shared_fifo = true;
}
-#endif /* CONFIG_BLACKFIN */
-
/***************************** Glue it together *****************************/
diff --git a/drivers/usb/musb/musb_debugfs.c b/drivers/usb/musb/musb_debugfs.c
index 7cf5a1bbdaff..e2050cac3eae 100644
--- a/drivers/usb/musb/musb_debugfs.c
+++ b/drivers/usb/musb/musb_debugfs.c
@@ -70,7 +70,6 @@ static const struct musb_register_map musb_regmap[] = {
{ "DMA_CNTLch7", 0x274, 16 },
{ "DMA_ADDRch7", 0x278, 32 },
{ "DMA_COUNTch7", 0x27C, 32 },
-#ifndef CONFIG_BLACKFIN
{ "ConfigData", MUSB_CONFIGDATA,8 },
{ "BabbleCtl", MUSB_BABBLE_CTL,8 },
{ "TxFIFOsz", MUSB_TXFIFOSZ, 8 },
@@ -79,7 +78,6 @@ static const struct musb_register_map musb_regmap[] = {
{ "RxFIFOadd", MUSB_RXFIFOADD, 16 },
{ "EPInfo", MUSB_EPINFO, 8 },
{ "RAMInfo", MUSB_RAMINFO, 8 },
-#endif
{ } /* Terminating Entry */
};
@@ -112,11 +110,7 @@ static int musb_regdump_show(struct seq_file *s, void *unused)
pm_runtime_put_autosuspend(musb->controller);
return 0;
}
-
-static int musb_regdump_open(struct inode *inode, struct file *file)
-{
- return single_open(file, musb_regdump_show, inode->i_private);
-}
+DEFINE_SHOW_ATTRIBUTE(musb_regdump);
static int musb_test_mode_show(struct seq_file *s, void *unused)
{
@@ -161,13 +155,6 @@ static int musb_test_mode_show(struct seq_file *s, void *unused)
return 0;
}
-static const struct file_operations musb_regdump_fops = {
- .open = musb_regdump_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
static int musb_test_mode_open(struct inode *inode, struct file *file)
{
return single_open(file, musb_test_mode_show, inode->i_private);
diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h
index a4241f4d430e..0fc8cd0c2a5c 100644
--- a/drivers/usb/musb/musb_dma.h
+++ b/drivers/usb/musb/musb_dma.h
@@ -80,17 +80,6 @@ struct musb_hw_ep;
#define is_cppi_enabled(musb) 0
#endif
-/* Anomaly 05000456 - USB Receive Interrupt Is Not Generated in DMA Mode 1
- * Only allow DMA mode 1 to be used when the USB will actually generate the
- * interrupts we expect.
- */
-#ifdef CONFIG_BLACKFIN
-# undef USE_MODE1
-# if !ANOMALY_05000456
-# define USE_MODE1
-# endif
-#endif
-
/*
* DMA channel status ... updated by the dma controller driver whenever that
* status changes, and protected by the overall controller spinlock.
diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
index 05a679d5e3a2..6a60bc0490c5 100644
--- a/drivers/usb/musb/musb_dsps.c
+++ b/drivers/usb/musb/musb_dsps.c
@@ -451,7 +451,6 @@ static int dsps_musb_init(struct musb *musb)
if (!rev)
return -ENODEV;
- usb_phy_init(musb->xceiv);
if (IS_ERR(musb->phy)) {
musb->phy = NULL;
} else {
@@ -501,7 +500,6 @@ static int dsps_musb_exit(struct musb *musb)
struct dsps_glue *glue = dev_get_drvdata(dev->parent);
del_timer_sync(&musb->dev_timer);
- usb_phy_shutdown(musb->xceiv);
phy_power_off(musb->phy);
phy_exit(musb->phy);
debugfs_remove_recursive(glue->dbgfs_root);
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 293e5b8da565..e564695c6c8d 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -995,15 +995,11 @@ static int musb_gadget_enable(struct usb_ep *ep,
/* Set TXMAXP with the FIFO size of the endpoint
* to disable double buffering mode.
*/
- if (musb->double_buffer_not_ok) {
- musb_writew(regs, MUSB_TXMAXP, hw_ep->max_packet_sz_tx);
- } else {
- if (can_bulk_split(musb, musb_ep->type))
- musb_ep->hb_mult = (hw_ep->max_packet_sz_tx /
- musb_ep->packet_sz) - 1;
- musb_writew(regs, MUSB_TXMAXP, musb_ep->packet_sz
- | (musb_ep->hb_mult << 11));
- }
+ if (can_bulk_split(musb, musb_ep->type))
+ musb_ep->hb_mult = (hw_ep->max_packet_sz_tx /
+ musb_ep->packet_sz) - 1;
+ musb_writew(regs, MUSB_TXMAXP, musb_ep->packet_sz
+ | (musb_ep->hb_mult << 11));
csr = MUSB_TXCSR_MODE | MUSB_TXCSR_CLRDATATOG;
if (musb_readw(regs, MUSB_TXCSR)
@@ -1038,11 +1034,8 @@ static int musb_gadget_enable(struct usb_ep *ep,
/* Set RXMAXP with the FIFO size of the endpoint
* to disable double buffering mode.
*/
- if (musb->double_buffer_not_ok)
- musb_writew(regs, MUSB_RXMAXP, hw_ep->max_packet_sz_tx);
- else
- musb_writew(regs, MUSB_RXMAXP, musb_ep->packet_sz
- | (musb_ep->hb_mult << 11));
+ musb_writew(regs, MUSB_RXMAXP, musb_ep->packet_sz
+ | (musb_ep->hb_mult << 11));
/* force shared fifo to OUT-only mode */
if (hw_ep->is_shared_fifo) {
@@ -1680,40 +1673,6 @@ static int musb_gadget_pullup(struct usb_gadget *gadget, int is_on)
return 0;
}
-#ifdef CONFIG_BLACKFIN
-static struct usb_ep *musb_match_ep(struct usb_gadget *g,
- struct usb_endpoint_descriptor *desc,
- struct usb_ss_ep_comp_descriptor *ep_comp)
-{
- struct usb_ep *ep = NULL;
-
- switch (usb_endpoint_type(desc)) {
- case USB_ENDPOINT_XFER_ISOC:
- case USB_ENDPOINT_XFER_BULK:
- if (usb_endpoint_dir_in(desc))
- ep = gadget_find_ep_by_name(g, "ep5in");
- else
- ep = gadget_find_ep_by_name(g, "ep6out");
- break;
- case USB_ENDPOINT_XFER_INT:
- if (usb_endpoint_dir_in(desc))
- ep = gadget_find_ep_by_name(g, "ep1in");
- else
- ep = gadget_find_ep_by_name(g, "ep2out");
- break;
- default:
- break;
- }
-
- if (ep && usb_gadget_ep_match_desc(g, ep, desc, ep_comp))
- return ep;
-
- return NULL;
-}
-#else
-#define musb_match_ep NULL
-#endif
-
static int musb_gadget_start(struct usb_gadget *g,
struct usb_gadget_driver *driver);
static int musb_gadget_stop(struct usb_gadget *g);
@@ -1727,7 +1686,6 @@ static const struct usb_gadget_ops musb_gadget_operations = {
.pullup = musb_gadget_pullup,
.udc_start = musb_gadget_start,
.udc_stop = musb_gadget_stop,
- .match_ep = musb_match_ep,
};
/* ----------------------------------------------------------------------- */
diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c
index 18da4873e52e..91a5027b5c1f 100644
--- a/drivers/usb/musb/musb_gadget_ep0.c
+++ b/drivers/usb/musb/musb_gadget_ep0.c
@@ -89,15 +89,19 @@ static int service_tx_status_request(
}
is_in = epnum & USB_DIR_IN;
- if (is_in) {
- epnum &= 0x0f;
+ epnum &= 0x0f;
+ if (epnum >= MUSB_C_NUM_EPS) {
+ handled = -EINVAL;
+ break;
+ }
+
+ if (is_in)
ep = &musb->endpoints[epnum].ep_in;
- } else {
+ else
ep = &musb->endpoints[epnum].ep_out;
- }
regs = musb->endpoints[epnum].regs;
- if (epnum >= MUSB_C_NUM_EPS || !ep->desc) {
+ if (!ep->desc) {
handled = -EINVAL;
break;
}
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 45ed32c2cba9..4fa372c845e1 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -574,11 +574,8 @@ musb_rx_reinit(struct musb *musb, struct musb_qh *qh, u8 epnum)
/* Set RXMAXP with the FIFO size of the endpoint
* to disable double buffer mode.
*/
- if (musb->double_buffer_not_ok)
- musb_writew(ep->regs, MUSB_RXMAXP, ep->max_packet_sz_rx);
- else
- musb_writew(ep->regs, MUSB_RXMAXP,
- qh->maxpacket | ((qh->hb_mult - 1) << 11));
+ musb_writew(ep->regs, MUSB_RXMAXP,
+ qh->maxpacket | ((qh->hb_mult - 1) << 11));
ep->rx_reinit = 0;
}
@@ -804,10 +801,7 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
/* protocol/endpoint/interval/NAKlimit */
if (epnum) {
musb_writeb(epio, MUSB_TXTYPE, qh->type_reg);
- if (musb->double_buffer_not_ok) {
- musb_writew(epio, MUSB_TXMAXP,
- hw_ep->max_packet_sz_tx);
- } else if (can_bulk_split(musb, qh->type)) {
+ if (can_bulk_split(musb, qh->type)) {
qh->hb_mult = hw_ep->max_packet_sz_tx
/ packet_sz;
musb_writew(epio, MUSB_TXMAXP, packet_sz
@@ -2760,6 +2754,7 @@ int musb_host_setup(struct musb *musb, int power_budget)
hcd->self.otg_port = 1;
musb->xceiv->otg->host = &hcd->self;
hcd->power_budget = 2 * (power_budget ? : 250);
+ hcd->skip_phy_initialization = 1;
ret = usb_add_hcd(hcd, 0, 0);
if (ret < 0)
diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h
index a4beba184798..88466622c89f 100644
--- a/drivers/usb/musb/musb_regs.h
+++ b/drivers/usb/musb/musb_regs.h
@@ -195,8 +195,6 @@
#define MUSB_HUBADDR_MULTI_TT 0x80
-#ifndef CONFIG_BLACKFIN
-
/*
* Common USB registers
*/
@@ -416,184 +414,4 @@ static inline u8 musb_read_txhubport(struct musb *musb, u8 epnum)
musb->io.busctl_offset(epnum, MUSB_TXHUBPORT));
}
-#else /* CONFIG_BLACKFIN */
-
-#define USB_BASE USB_FADDR
-#define USB_OFFSET(reg) (reg - USB_BASE)
-
-/*
- * Common USB registers
- */
-#define MUSB_FADDR USB_OFFSET(USB_FADDR) /* 8-bit */
-#define MUSB_POWER USB_OFFSET(USB_POWER) /* 8-bit */
-#define MUSB_INTRTX USB_OFFSET(USB_INTRTX) /* 16-bit */
-#define MUSB_INTRRX USB_OFFSET(USB_INTRRX)
-#define MUSB_INTRTXE USB_OFFSET(USB_INTRTXE)
-#define MUSB_INTRRXE USB_OFFSET(USB_INTRRXE)
-#define MUSB_INTRUSB USB_OFFSET(USB_INTRUSB) /* 8 bit */
-#define MUSB_INTRUSBE USB_OFFSET(USB_INTRUSBE)/* 8 bit */
-#define MUSB_FRAME USB_OFFSET(USB_FRAME)
-#define MUSB_INDEX USB_OFFSET(USB_INDEX) /* 8 bit */
-#define MUSB_TESTMODE USB_OFFSET(USB_TESTMODE)/* 8 bit */
-
-/*
- * Additional Control Registers
- */
-
-#define MUSB_DEVCTL USB_OFFSET(USB_OTG_DEV_CTL) /* 8 bit */
-
-#define MUSB_LINKINFO USB_OFFSET(USB_LINKINFO)/* 8 bit */
-#define MUSB_VPLEN USB_OFFSET(USB_VPLEN) /* 8 bit */
-#define MUSB_HS_EOF1 USB_OFFSET(USB_HS_EOF1) /* 8 bit */
-#define MUSB_FS_EOF1 USB_OFFSET(USB_FS_EOF1) /* 8 bit */
-#define MUSB_LS_EOF1 USB_OFFSET(USB_LS_EOF1) /* 8 bit */
-
-/* Offsets to endpoint registers */
-#define MUSB_TXMAXP 0x00
-#define MUSB_TXCSR 0x04
-#define MUSB_CSR0 MUSB_TXCSR /* Re-used for EP0 */
-#define MUSB_RXMAXP 0x08
-#define MUSB_RXCSR 0x0C
-#define MUSB_RXCOUNT 0x10
-#define MUSB_COUNT0 MUSB_RXCOUNT /* Re-used for EP0 */
-#define MUSB_TXTYPE 0x14
-#define MUSB_TYPE0 MUSB_TXTYPE /* Re-used for EP0 */
-#define MUSB_TXINTERVAL 0x18
-#define MUSB_NAKLIMIT0 MUSB_TXINTERVAL /* Re-used for EP0 */
-#define MUSB_RXTYPE 0x1C
-#define MUSB_RXINTERVAL 0x20
-#define MUSB_TXCOUNT 0x28
-
-/* Offsets to endpoint registers in indexed model (using INDEX register) */
-#define MUSB_INDEXED_OFFSET(_epnum, _offset) \
- (0x40 + (_offset))
-
-/* Offsets to endpoint registers in flat models */
-#define MUSB_FLAT_OFFSET(_epnum, _offset) \
- (USB_OFFSET(USB_EP_NI0_TXMAXP) + (0x40 * (_epnum)) + (_offset))
-
-/* Not implemented - HW has separate Tx/Rx FIFO */
-#define MUSB_TXCSR_MODE 0x0000
-
-static inline void musb_write_txfifosz(void __iomem *mbase, u8 c_size)
-{
-}
-
-static inline void musb_write_txfifoadd(void __iomem *mbase, u16 c_off)
-{
-}
-
-static inline void musb_write_rxfifosz(void __iomem *mbase, u8 c_size)
-{
-}
-
-static inline void musb_write_rxfifoadd(void __iomem *mbase, u16 c_off)
-{
-}
-
-static inline void musb_write_ulpi_buscontrol(void __iomem *mbase, u8 val)
-{
-}
-
-static inline u8 musb_read_txfifosz(void __iomem *mbase)
-{
- return 0;
-}
-
-static inline u16 musb_read_txfifoadd(void __iomem *mbase)
-{
- return 0;
-}
-
-static inline u8 musb_read_rxfifosz(void __iomem *mbase)
-{
- return 0;
-}
-
-static inline u16 musb_read_rxfifoadd(void __iomem *mbase)
-{
- return 0;
-}
-
-static inline u8 musb_read_ulpi_buscontrol(void __iomem *mbase)
-{
- return 0;
-}
-
-static inline u8 musb_read_configdata(void __iomem *mbase)
-{
- return 0;
-}
-
-static inline u16 musb_read_hwvers(void __iomem *mbase)
-{
- /*
- * This register is invisible on Blackfin, actually the MUSB
- * RTL version of Blackfin is 1.9, so just hardcode its value.
- */
- return MUSB_HWVERS_1900;
-}
-
-static inline void musb_write_rxfunaddr(void __iomem *mbase, u8 epnum,
- u8 qh_addr_req)
-{
-}
-
-static inline void musb_write_rxhubaddr(void __iomem *mbase, u8 epnum,
- u8 qh_h_addr_reg)
-{
-}
-
-static inline void musb_write_rxhubport(void __iomem *mbase, u8 epnum,
- u8 qh_h_port_reg)
-{
-}
-
-static inline void musb_write_txfunaddr(void __iomem *mbase, u8 epnum,
- u8 qh_addr_reg)
-{
-}
-
-static inline void musb_write_txhubaddr(void __iomem *mbase, u8 epnum,
- u8 qh_addr_reg)
-{
-}
-
-static inline void musb_write_txhubport(void __iomem *mbase, u8 epnum,
- u8 qh_h_port_reg)
-{
-}
-
-static inline u8 musb_read_rxfunaddr(void __iomem *mbase, u8 epnum)
-{
- return 0;
-}
-
-static inline u8 musb_read_rxhubaddr(void __iomem *mbase, u8 epnum)
-{
- return 0;
-}
-
-static inline u8 musb_read_rxhubport(void __iomem *mbase, u8 epnum)
-{
- return 0;
-}
-
-static inline u8 musb_read_txfunaddr(void __iomem *mbase, u8 epnum)
-{
- return 0;
-}
-
-static inline u8 musb_read_txhubaddr(void __iomem *mbase, u8 epnum)
-{
- return 0;
-}
-
-static inline u8 musb_read_txhubport(void __iomem *mbase, u8 epnum)
-{
- return 0;
-}
-
-#endif /* CONFIG_BLACKFIN */
-
#endif /* __MUSB_REGS_H__ */
diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c
index 21fb9e6622f3..4389fc3422bd 100644
--- a/drivers/usb/musb/musbhsdma.c
+++ b/drivers/usb/musb/musbhsdma.c
@@ -235,11 +235,6 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data)
int_hsdma = musb_readb(mbase, MUSB_HSDMA_INTR);
-#ifdef CONFIG_BLACKFIN
- /* Clear DMA interrupt flags */
- musb_writeb(mbase, MUSB_HSDMA_INTR, int_hsdma);
-#endif
-
if (!int_hsdma) {
musb_dbg(musb, "spurious DMA irq");
diff --git a/drivers/usb/musb/musbhsdma.h b/drivers/usb/musb/musbhsdma.h
index 44f7983df0a1..93665135aff1 100644
--- a/drivers/usb/musb/musbhsdma.h
+++ b/drivers/usb/musb/musbhsdma.h
@@ -6,8 +6,6 @@
* Copyright (C) 2005-2007 by Texas Instruments
*/
-#ifndef CONFIG_BLACKFIN
-
#define MUSB_HSDMA_BASE 0x200
#define MUSB_HSDMA_INTR (MUSB_HSDMA_BASE + 0)
#define MUSB_HSDMA_CONTROL 0x4
@@ -34,68 +32,6 @@
musb_writel(mbase, \
MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_COUNT), \
len)
-#else
-
-#define MUSB_HSDMA_BASE 0x400
-#define MUSB_HSDMA_INTR (MUSB_HSDMA_BASE + 0)
-#define MUSB_HSDMA_CONTROL 0x04
-#define MUSB_HSDMA_ADDR_LOW 0x08
-#define MUSB_HSDMA_ADDR_HIGH 0x0C
-#define MUSB_HSDMA_COUNT_LOW 0x10
-#define MUSB_HSDMA_COUNT_HIGH 0x14
-
-#define MUSB_HSDMA_CHANNEL_OFFSET(_bchannel, _offset) \
- (MUSB_HSDMA_BASE + (_bchannel * 0x20) + _offset)
-
-static inline u32 musb_read_hsdma_addr(void __iomem *mbase, u8 bchannel)
-{
- u32 addr = musb_readw(mbase,
- MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_ADDR_HIGH));
-
- addr = addr << 16;
-
- addr |= musb_readw(mbase,
- MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_ADDR_LOW));
-
- return addr;
-}
-
-static inline void musb_write_hsdma_addr(void __iomem *mbase,
- u8 bchannel, dma_addr_t dma_addr)
-{
- musb_writew(mbase,
- MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_ADDR_LOW),
- dma_addr);
- musb_writew(mbase,
- MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_ADDR_HIGH),
- (dma_addr >> 16));
-}
-
-static inline u32 musb_read_hsdma_count(void __iomem *mbase, u8 bchannel)
-{
- u32 count = musb_readw(mbase,
- MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_COUNT_HIGH));
-
- count = count << 16;
-
- count |= musb_readw(mbase,
- MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_COUNT_LOW));
-
- return count;
-}
-
-static inline void musb_write_hsdma_count(void __iomem *mbase,
- u8 bchannel, u32 len)
-{
- musb_writew(mbase,
- MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_COUNT_LOW),len);
- musb_writew(mbase,
- MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_COUNT_HIGH),
- (len >> 16));
-}
-
-#endif /* CONFIG_BLACKFIN */
-
/* control register (16-bit): */
#define MUSB_HSDMA_ENABLE_SHIFT 0
#define MUSB_HSDMA_TRANSMIT_SHIFT 1
diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c
index 87295313a10c..66143ab8c043 100644
--- a/drivers/usb/phy/phy-ab8500-usb.c
+++ b/drivers/usb/phy/phy-ab8500-usb.c
@@ -29,17 +29,12 @@
/* Bank AB8500_USB */
#define AB8500_USB_LINE_STAT_REG 0x80
#define AB8505_USB_LINE_STAT_REG 0x94
-#define AB8540_USB_LINK_STAT_REG 0x94
-#define AB9540_USB_LINK_STAT_REG 0x94
-#define AB8540_USB_OTG_CTL_REG 0x87
#define AB8500_USB_PHY_CTRL_REG 0x8A
-#define AB8540_VBUS_CTRL_REG 0x82
/* Bank AB8500_DEVELOPMENT */
#define AB8500_BANK12_ACCESS 0x00
/* Bank AB8500_DEBUG */
-#define AB8540_DEBUG 0x32
#define AB8500_USB_PHY_TUNE1 0x05
#define AB8500_USB_PHY_TUNE2 0x06
#define AB8500_USB_PHY_TUNE3 0x07
@@ -53,10 +48,6 @@
#define AB8500_BIT_WD_CTRL_ENABLE (1 << 0)
#define AB8500_BIT_WD_CTRL_KICK (1 << 1)
#define AB8500_BIT_SOURCE2_VBUSDET (1 << 7)
-#define AB8540_BIT_OTG_CTL_VBUS_VALID_ENA (1 << 0)
-#define AB8540_BIT_OTG_CTL_ID_HOST_ENA (1 << 1)
-#define AB8540_BIT_OTG_CTL_ID_DEV_ENA (1 << 5)
-#define AB8540_BIT_VBUS_CTRL_CHARG_DET_ENA (1 << 0)
#define AB8500_WD_KICK_DELAY_US 100 /* usec */
#define AB8500_WD_V11_DISABLE_DELAY_US 100 /* usec */
@@ -113,68 +104,6 @@ enum ab8505_usb_link_status {
USB_LINK_MOTOROLA_FACTORY_CBL_PHY_EN_8505,
};
-enum ab8540_usb_link_status {
- USB_LINK_NOT_CONFIGURED_8540 = 0,
- USB_LINK_STD_HOST_NC_8540,
- USB_LINK_STD_HOST_C_NS_8540,
- USB_LINK_STD_HOST_C_S_8540,
- USB_LINK_CDP_8540,
- USB_LINK_RESERVED0_8540,
- USB_LINK_RESERVED1_8540,
- USB_LINK_DEDICATED_CHG_8540,
- USB_LINK_ACA_RID_A_8540,
- USB_LINK_ACA_RID_B_8540,
- USB_LINK_ACA_RID_C_NM_8540,
- USB_LINK_RESERVED2_8540,
- USB_LINK_RESERVED3_8540,
- USB_LINK_HM_IDGND_8540,
- USB_LINK_CHARGERPORT_NOT_OK_8540,
- USB_LINK_CHARGER_DM_HIGH_8540,
- USB_LINK_PHYEN_NO_VBUS_NO_IDGND_8540,
- USB_LINK_STD_UPSTREAM_NO_IDGNG_VBUS_8540,
- USB_LINK_STD_UPSTREAM_8540,
- USB_LINK_CHARGER_SE1_8540,
- USB_LINK_CARKIT_CHGR_1_8540,
- USB_LINK_CARKIT_CHGR_2_8540,
- USB_LINK_ACA_DOCK_CHGR_8540,
- USB_LINK_SAMSUNG_BOOT_CBL_PHY_EN_8540,
- USB_LINK_SAMSUNG_BOOT_CBL_PHY_DISB_8540,
- USB_LINK_SAMSUNG_UART_CBL_PHY_EN_8540,
- USB_LINK_SAMSUNG_UART_CBL_PHY_DISB_8540,
- USB_LINK_MOTOROLA_FACTORY_CBL_PHY_EN_8540
-};
-
-enum ab9540_usb_link_status {
- USB_LINK_NOT_CONFIGURED_9540 = 0,
- USB_LINK_STD_HOST_NC_9540,
- USB_LINK_STD_HOST_C_NS_9540,
- USB_LINK_STD_HOST_C_S_9540,
- USB_LINK_CDP_9540,
- USB_LINK_RESERVED0_9540,
- USB_LINK_RESERVED1_9540,
- USB_LINK_DEDICATED_CHG_9540,
- USB_LINK_ACA_RID_A_9540,
- USB_LINK_ACA_RID_B_9540,
- USB_LINK_ACA_RID_C_NM_9540,
- USB_LINK_RESERVED2_9540,
- USB_LINK_RESERVED3_9540,
- USB_LINK_HM_IDGND_9540,
- USB_LINK_CHARGERPORT_NOT_OK_9540,
- USB_LINK_CHARGER_DM_HIGH_9540,
- USB_LINK_PHYEN_NO_VBUS_NO_IDGND_9540,
- USB_LINK_STD_UPSTREAM_NO_IDGNG_VBUS_9540,
- USB_LINK_STD_UPSTREAM_9540,
- USB_LINK_CHARGER_SE1_9540,
- USB_LINK_CARKIT_CHGR_1_9540,
- USB_LINK_CARKIT_CHGR_2_9540,
- USB_LINK_ACA_DOCK_CHGR_9540,
- USB_LINK_SAMSUNG_BOOT_CBL_PHY_EN_9540,
- USB_LINK_SAMSUNG_BOOT_CBL_PHY_DISB_9540,
- USB_LINK_SAMSUNG_UART_CBL_PHY_EN_9540,
- USB_LINK_SAMSUNG_UART_CBL_PHY_DISB_9540,
- USB_LINK_MOTOROLA_FACTORY_CBL_PHY_EN_9540
-};
-
enum ab8500_usb_mode {
USB_IDLE = 0,
USB_PERIPHERAL,
@@ -192,10 +121,6 @@ enum ab8500_usb_mode {
#define AB8500_USB_FLAG_USE_AB_IDDET (1 << 3)
/* Enable setting regulators voltage */
#define AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE (1 << 4)
-/* Enable the check_vbus_status workaround */
-#define AB8500_USB_FLAG_USE_CHECK_VBUS_STATUS (1 << 5)
-/* Enable the vbus host workaround */
-#define AB8500_USB_FLAG_USE_VBUS_HOST_QUIRK (1 << 6)
struct ab8500_usb {
struct usb_phy phy;
@@ -203,7 +128,6 @@ struct ab8500_usb {
struct ab8500 *ab8500;
unsigned vbus_draw;
struct work_struct phy_dis_work;
- struct work_struct vbus_event_work;
enum ab8500_usb_mode mode;
struct clk *sysclk;
struct regulator *v_ape;
@@ -342,15 +266,6 @@ static void ab8500_usb_phy_enable(struct ab8500_usb *ab, bool sel_host)
abx500_mask_and_set_register_interruptible(ab->dev,
AB8500_USB, AB8500_USB_PHY_CTRL_REG,
bit, bit);
-
- if (ab->flags & AB8500_USB_FLAG_USE_VBUS_HOST_QUIRK) {
- if (sel_host)
- abx500_set_register_interruptible(ab->dev,
- AB8500_USB, AB8540_USB_OTG_CTL_REG,
- AB8540_BIT_OTG_CTL_VBUS_VALID_ENA |
- AB8540_BIT_OTG_CTL_ID_HOST_ENA |
- AB8540_BIT_OTG_CTL_ID_DEV_ENA);
- }
}
static void ab8500_usb_phy_disable(struct ab8500_usb *ab, bool sel_host)
@@ -395,263 +310,6 @@ static void ab8500_usb_phy_disable(struct ab8500_usb *ab, bool sel_host)
#define ab8500_usb_peri_phy_en(ab) ab8500_usb_phy_enable(ab, false)
#define ab8500_usb_peri_phy_dis(ab) ab8500_usb_phy_disable(ab, false)
-static int ab9540_usb_link_status_update(struct ab8500_usb *ab,
- enum ab9540_usb_link_status lsts)
-{
- enum ux500_musb_vbus_id_status event = 0;
-
- dev_dbg(ab->dev, "ab9540_usb_link_status_update %d\n", lsts);
-
- if (ab->previous_link_status_state == USB_LINK_HM_IDGND_9540 &&
- (lsts == USB_LINK_STD_HOST_C_NS_9540 ||
- lsts == USB_LINK_STD_HOST_NC_9540))
- return 0;
-
- if (ab->previous_link_status_state == USB_LINK_ACA_RID_A_9540 &&
- (lsts == USB_LINK_STD_HOST_NC_9540))
- return 0;
-
- ab->previous_link_status_state = lsts;
-
- switch (lsts) {
- case USB_LINK_ACA_RID_B_9540:
- event = UX500_MUSB_RIDB;
- case USB_LINK_NOT_CONFIGURED_9540:
- case USB_LINK_RESERVED0_9540:
- case USB_LINK_RESERVED1_9540:
- case USB_LINK_RESERVED2_9540:
- case USB_LINK_RESERVED3_9540:
- if (ab->mode == USB_PERIPHERAL)
- atomic_notifier_call_chain(&ab->phy.notifier,
- UX500_MUSB_CLEAN, &ab->vbus_draw);
- ab->mode = USB_IDLE;
- ab->phy.otg->default_a = false;
- ab->vbus_draw = 0;
- if (event != UX500_MUSB_RIDB)
- event = UX500_MUSB_NONE;
- /* Fallback to default B_IDLE as nothing is connected. */
- ab->phy.otg->state = OTG_STATE_B_IDLE;
- usb_phy_set_event(&ab->phy, USB_EVENT_NONE);
- break;
-
- case USB_LINK_ACA_RID_C_NM_9540:
- event = UX500_MUSB_RIDC;
- case USB_LINK_STD_HOST_NC_9540:
- case USB_LINK_STD_HOST_C_NS_9540:
- case USB_LINK_STD_HOST_C_S_9540:
- case USB_LINK_CDP_9540:
- if (ab->mode == USB_HOST) {
- ab->mode = USB_PERIPHERAL;
- ab8500_usb_host_phy_dis(ab);
- ab8500_usb_peri_phy_en(ab);
- atomic_notifier_call_chain(&ab->phy.notifier,
- UX500_MUSB_PREPARE, &ab->vbus_draw);
- usb_phy_set_event(&ab->phy, USB_EVENT_ENUMERATED);
- }
- if (ab->mode == USB_IDLE) {
- ab->mode = USB_PERIPHERAL;
- ab8500_usb_peri_phy_en(ab);
- atomic_notifier_call_chain(&ab->phy.notifier,
- UX500_MUSB_PREPARE, &ab->vbus_draw);
- usb_phy_set_event(&ab->phy, USB_EVENT_ENUMERATED);
- }
- if (event != UX500_MUSB_RIDC)
- event = UX500_MUSB_VBUS;
- break;
-
- case USB_LINK_ACA_RID_A_9540:
- event = UX500_MUSB_RIDA;
- case USB_LINK_HM_IDGND_9540:
- case USB_LINK_STD_UPSTREAM_9540:
- if (ab->mode == USB_PERIPHERAL) {
- ab->mode = USB_HOST;
- ab8500_usb_peri_phy_dis(ab);
- ab8500_usb_host_phy_en(ab);
- atomic_notifier_call_chain(&ab->phy.notifier,
- UX500_MUSB_PREPARE, &ab->vbus_draw);
- }
- if (ab->mode == USB_IDLE) {
- ab->mode = USB_HOST;
- ab8500_usb_host_phy_en(ab);
- atomic_notifier_call_chain(&ab->phy.notifier,
- UX500_MUSB_PREPARE, &ab->vbus_draw);
- }
- ab->phy.otg->default_a = true;
- if (event != UX500_MUSB_RIDA)
- event = UX500_MUSB_ID;
-
- atomic_notifier_call_chain(&ab->phy.notifier,
- event, &ab->vbus_draw);
- break;
-
- case USB_LINK_DEDICATED_CHG_9540:
- ab->mode = USB_DEDICATED_CHG;
- event = UX500_MUSB_CHARGER;
- atomic_notifier_call_chain(&ab->phy.notifier,
- event, &ab->vbus_draw);
- usb_phy_set_event(&ab->phy, USB_EVENT_CHARGER);
- break;
-
- case USB_LINK_PHYEN_NO_VBUS_NO_IDGND_9540:
- case USB_LINK_STD_UPSTREAM_NO_IDGNG_VBUS_9540:
- if (!(is_ab9540_2p0_or_earlier(ab->ab8500))) {
- event = UX500_MUSB_NONE;
- if (ab->mode == USB_HOST) {
- ab->phy.otg->default_a = false;
- ab->vbus_draw = 0;
- atomic_notifier_call_chain(&ab->phy.notifier,
- event, &ab->vbus_draw);
- ab8500_usb_host_phy_dis(ab);
- ab->mode = USB_IDLE;
- }
- if (ab->mode == USB_PERIPHERAL) {
- atomic_notifier_call_chain(&ab->phy.notifier,
- event, &ab->vbus_draw);
- ab8500_usb_peri_phy_dis(ab);
- atomic_notifier_call_chain(&ab->phy.notifier,
- UX500_MUSB_CLEAN,
- &ab->vbus_draw);
- ab->mode = USB_IDLE;
- ab->phy.otg->default_a = false;
- ab->vbus_draw = 0;
- usb_phy_set_event(&ab->phy, USB_EVENT_NONE);
- }
- }
- break;
-
- default:
- break;
- }
-
- return 0;
-}
-
-static int ab8540_usb_link_status_update(struct ab8500_usb *ab,
- enum ab8540_usb_link_status lsts)
-{
- enum ux500_musb_vbus_id_status event = 0;
-
- dev_dbg(ab->dev, "ab8540_usb_link_status_update %d\n", lsts);
-
- if (ab->enabled_charging_detection) {
- /* Disable USB Charger detection */
- abx500_mask_and_set_register_interruptible(ab->dev,
- AB8500_USB, AB8540_VBUS_CTRL_REG,
- AB8540_BIT_VBUS_CTRL_CHARG_DET_ENA, 0x00);
- ab->enabled_charging_detection = false;
- }
-
- /*
- * Spurious link_status interrupts are seen in case of a
- * disconnection of a device in IDGND and RIDA stage
- */
- if (ab->previous_link_status_state == USB_LINK_HM_IDGND_8540 &&
- (lsts == USB_LINK_STD_HOST_C_NS_8540 ||
- lsts == USB_LINK_STD_HOST_NC_8540))
- return 0;
-
- if (ab->previous_link_status_state == USB_LINK_ACA_RID_A_8540 &&
- (lsts == USB_LINK_STD_HOST_NC_8540))
- return 0;
-
- ab->previous_link_status_state = lsts;
-
- switch (lsts) {
- case USB_LINK_ACA_RID_B_8540:
- event = UX500_MUSB_RIDB;
- case USB_LINK_NOT_CONFIGURED_8540:
- case USB_LINK_RESERVED0_8540:
- case USB_LINK_RESERVED1_8540:
- case USB_LINK_RESERVED2_8540:
- case USB_LINK_RESERVED3_8540:
- ab->mode = USB_IDLE;
- ab->phy.otg->default_a = false;
- ab->vbus_draw = 0;
- if (event != UX500_MUSB_RIDB)
- event = UX500_MUSB_NONE;
- /*
- * Fallback to default B_IDLE as nothing
- * is connected
- */
- ab->phy.otg->state = OTG_STATE_B_IDLE;
- usb_phy_set_event(&ab->phy, USB_EVENT_NONE);
- break;
-
- case USB_LINK_ACA_RID_C_NM_8540:
- event = UX500_MUSB_RIDC;
- case USB_LINK_STD_HOST_NC_8540:
- case USB_LINK_STD_HOST_C_NS_8540:
- case USB_LINK_STD_HOST_C_S_8540:
- case USB_LINK_CDP_8540:
- if (ab->mode == USB_IDLE) {
- ab->mode = USB_PERIPHERAL;
- ab8500_usb_peri_phy_en(ab);
- atomic_notifier_call_chain(&ab->phy.notifier,
- UX500_MUSB_PREPARE, &ab->vbus_draw);
- usb_phy_set_event(&ab->phy, USB_EVENT_ENUMERATED);
- }
- if (event != UX500_MUSB_RIDC)
- event = UX500_MUSB_VBUS;
- break;
-
- case USB_LINK_ACA_RID_A_8540:
- case USB_LINK_ACA_DOCK_CHGR_8540:
- event = UX500_MUSB_RIDA;
- case USB_LINK_HM_IDGND_8540:
- case USB_LINK_STD_UPSTREAM_8540:
- if (ab->mode == USB_IDLE) {
- ab->mode = USB_HOST;
- ab8500_usb_host_phy_en(ab);
- atomic_notifier_call_chain(&ab->phy.notifier,
- UX500_MUSB_PREPARE, &ab->vbus_draw);
- }
- ab->phy.otg->default_a = true;
- if (event != UX500_MUSB_RIDA)
- event = UX500_MUSB_ID;
- atomic_notifier_call_chain(&ab->phy.notifier,
- event, &ab->vbus_draw);
- break;
-
- case USB_LINK_DEDICATED_CHG_8540:
- ab->mode = USB_DEDICATED_CHG;
- event = UX500_MUSB_CHARGER;
- atomic_notifier_call_chain(&ab->phy.notifier,
- event, &ab->vbus_draw);
- usb_phy_set_event(&ab->phy, USB_EVENT_CHARGER);
- break;
-
- case USB_LINK_PHYEN_NO_VBUS_NO_IDGND_8540:
- case USB_LINK_STD_UPSTREAM_NO_IDGNG_VBUS_8540:
- event = UX500_MUSB_NONE;
- if (ab->mode == USB_HOST) {
- ab->phy.otg->default_a = false;
- ab->vbus_draw = 0;
- atomic_notifier_call_chain(&ab->phy.notifier,
- event, &ab->vbus_draw);
- ab8500_usb_host_phy_dis(ab);
- ab->mode = USB_IDLE;
- }
- if (ab->mode == USB_PERIPHERAL) {
- atomic_notifier_call_chain(&ab->phy.notifier,
- event, &ab->vbus_draw);
- ab8500_usb_peri_phy_dis(ab);
- atomic_notifier_call_chain(&ab->phy.notifier,
- UX500_MUSB_CLEAN, &ab->vbus_draw);
- ab->mode = USB_IDLE;
- ab->phy.otg->default_a = false;
- ab->vbus_draw = 0;
- usb_phy_set_event(&ab->phy, USB_EVENT_NONE);
- }
- break;
-
- default:
- event = UX500_MUSB_NONE;
- break;
- }
-
- return 0;
-}
-
static int ab8505_usb_link_status_update(struct ab8500_usb *ab,
enum ab8505_usb_link_status lsts)
{
@@ -858,20 +516,6 @@ static int abx500_usb_link_status_update(struct ab8500_usb *ab)
AB8500_USB, AB8505_USB_LINE_STAT_REG, &reg);
lsts = (reg >> 3) & 0x1F;
ret = ab8505_usb_link_status_update(ab, lsts);
- } else if (is_ab8540(ab->ab8500)) {
- enum ab8540_usb_link_status lsts;
-
- abx500_get_register_interruptible(ab->dev,
- AB8500_USB, AB8540_USB_LINK_STAT_REG, &reg);
- lsts = (reg >> 3) & 0xFF;
- ret = ab8540_usb_link_status_update(ab, lsts);
- } else if (is_ab9540(ab->ab8500)) {
- enum ab9540_usb_link_status lsts;
-
- abx500_get_register_interruptible(ab->dev,
- AB8500_USB, AB9540_USB_LINK_STAT_REG, &reg);
- lsts = (reg >> 3) & 0xFF;
- ret = ab9540_usb_link_status_update(ab, lsts);
}
return ret;
@@ -889,7 +533,7 @@ static int abx500_usb_link_status_update(struct ab8500_usb *ab)
static irqreturn_t ab8500_usb_disconnect_irq(int irq, void *data)
{
struct ab8500_usb *ab = (struct ab8500_usb *) data;
- enum usb_phy_events event = UX500_MUSB_NONE;
+ enum usb_phy_events event = USB_EVENT_NONE;
/* Link status will not be updated till phy is disabled. */
if (ab->mode == USB_HOST) {
@@ -946,69 +590,6 @@ static void ab8500_usb_phy_disable_work(struct work_struct *work)
ab8500_usb_peri_phy_dis(ab);
}
-/* Check if VBUS is set and linkstatus has not detected a cable. */
-static bool ab8500_usb_check_vbus_status(struct ab8500_usb *ab)
-{
- u8 isource2;
- u8 reg;
- enum ab8540_usb_link_status lsts;
-
- abx500_get_register_interruptible(ab->dev,
- AB8500_INTERRUPT, AB8500_IT_SOURCE2_REG,
- &isource2);
-
- /* If Vbus is below 3.6V abort */
- if (!(isource2 & AB8500_BIT_SOURCE2_VBUSDET))
- return false;
-
- abx500_get_register_interruptible(ab->dev,
- AB8500_USB, AB8540_USB_LINK_STAT_REG,
- &reg);
-
- lsts = (reg >> 3) & 0xFF;
-
- /* Check if linkstatus has detected a cable */
- if (lsts)
- return false;
-
- return true;
-}
-
-/* re-trigger charger detection again with watchdog re-kick. */
-static void ab8500_usb_vbus_turn_on_event_work(struct work_struct *work)
-{
- struct ab8500_usb *ab = container_of(work, struct ab8500_usb,
- vbus_event_work);
-
- if (ab->mode != USB_IDLE)
- return;
-
- abx500_set_register_interruptible(ab->dev,
- AB8500_SYS_CTRL2_BLOCK, AB8500_MAIN_WD_CTRL_REG,
- AB8500_BIT_WD_CTRL_ENABLE);
-
- udelay(100);
-
- abx500_set_register_interruptible(ab->dev,
- AB8500_SYS_CTRL2_BLOCK, AB8500_MAIN_WD_CTRL_REG,
- AB8500_BIT_WD_CTRL_ENABLE | AB8500_BIT_WD_CTRL_KICK);
-
- udelay(100);
-
- /* Disable Main watchdog */
- abx500_set_register_interruptible(ab->dev,
- AB8500_SYS_CTRL2_BLOCK, AB8500_MAIN_WD_CTRL_REG,
- 0x0);
-
- /* Enable USB Charger detection */
- abx500_mask_and_set_register_interruptible(ab->dev,
- AB8500_USB, AB8540_VBUS_CTRL_REG,
- AB8540_BIT_VBUS_CTRL_CHARG_DET_ENA,
- AB8540_BIT_VBUS_CTRL_CHARG_DET_ENA);
-
- ab->enabled_charging_detection = true;
-}
-
static int ab8500_usb_set_suspend(struct usb_phy *x, int suspend)
{
/* TODO */
@@ -1256,66 +837,6 @@ static void ab8500_usb_set_ab8505_tuning_values(struct ab8500_usb *ab)
err);
}
-static void ab8500_usb_set_ab8540_tuning_values(struct ab8500_usb *ab)
-{
- int err;
-
- err = abx500_set_register_interruptible(ab->dev,
- AB8540_DEBUG, AB8500_USB_PHY_TUNE1, 0xCC);
- if (err < 0)
- dev_err(ab->dev, "Failed to set PHY_TUNE1 register ret=%d\n",
- err);
-
- err = abx500_set_register_interruptible(ab->dev,
- AB8540_DEBUG, AB8500_USB_PHY_TUNE2, 0x60);
- if (err < 0)
- dev_err(ab->dev, "Failed to set PHY_TUNE2 register ret=%d\n",
- err);
-
- err = abx500_set_register_interruptible(ab->dev,
- AB8540_DEBUG, AB8500_USB_PHY_TUNE3, 0x90);
- if (err < 0)
- dev_err(ab->dev, "Failed to set PHY_TUNE3 register ret=%d\n",
- err);
-}
-
-static void ab8500_usb_set_ab9540_tuning_values(struct ab8500_usb *ab)
-{
- int err;
-
- /* Enable the PBT/Bank 0x12 access */
- err = abx500_set_register_interruptible(ab->dev,
- AB8500_DEVELOPMENT, AB8500_BANK12_ACCESS, 0x01);
- if (err < 0)
- dev_err(ab->dev, "Failed to enable bank12 access err=%d\n",
- err);
-
- err = abx500_set_register_interruptible(ab->dev,
- AB8500_DEBUG, AB8500_USB_PHY_TUNE1, 0xC8);
- if (err < 0)
- dev_err(ab->dev, "Failed to set PHY_TUNE1 register err=%d\n",
- err);
-
- err = abx500_set_register_interruptible(ab->dev,
- AB8500_DEBUG, AB8500_USB_PHY_TUNE2, 0x60);
- if (err < 0)
- dev_err(ab->dev, "Failed to set PHY_TUNE2 register err=%d\n",
- err);
-
- err = abx500_set_register_interruptible(ab->dev,
- AB8500_DEBUG, AB8500_USB_PHY_TUNE3, 0x80);
- if (err < 0)
- dev_err(ab->dev, "Failed to set PHY_TUNE3 register err=%d\n",
- err);
-
- /* Switch to normal mode/disable Bank 0x12 access */
- err = abx500_set_register_interruptible(ab->dev,
- AB8500_DEVELOPMENT, AB8500_BANK12_ACCESS, 0x00);
- if (err < 0)
- dev_err(ab->dev, "Failed to switch bank12 access err=%d\n",
- err);
-}
-
static int ab8500_usb_probe(struct platform_device *pdev)
{
struct ab8500_usb *ab;
@@ -1362,17 +883,6 @@ static int ab8500_usb_probe(struct platform_device *pdev)
AB8500_USB_FLAG_USE_ID_WAKEUP_IRQ |
AB8500_USB_FLAG_USE_VBUS_DET_IRQ |
AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE;
- } else if (is_ab8540(ab->ab8500)) {
- ab->flags |= AB8500_USB_FLAG_USE_LINK_STATUS_IRQ |
- AB8500_USB_FLAG_USE_CHECK_VBUS_STATUS |
- AB8500_USB_FLAG_USE_VBUS_HOST_QUIRK |
- AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE;
- } else if (is_ab9540(ab->ab8500)) {
- ab->flags |= AB8500_USB_FLAG_USE_LINK_STATUS_IRQ |
- AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE;
- if (is_ab9540_2p0_or_earlier(ab->ab8500))
- ab->flags |= AB8500_USB_FLAG_USE_ID_WAKEUP_IRQ |
- AB8500_USB_FLAG_USE_VBUS_DET_IRQ;
}
/* Disable regulator voltage setting for AB8500 <= v2.0 */
@@ -1384,8 +894,6 @@ static int ab8500_usb_probe(struct platform_device *pdev)
/* all: Disable phy when called from set_host and set_peripheral */
INIT_WORK(&ab->phy_dis_work, ab8500_usb_phy_disable_work);
- INIT_WORK(&ab->vbus_event_work, ab8500_usb_vbus_turn_on_event_work);
-
err = ab8500_usb_regulator_get(ab);
if (err)
return err;
@@ -1412,12 +920,6 @@ static int ab8500_usb_probe(struct platform_device *pdev)
else if (is_ab8505(ab->ab8500))
/* Phy tuning values for AB8505 */
ab8500_usb_set_ab8505_tuning_values(ab);
- else if (is_ab8540(ab->ab8500))
- /* Phy tuning values for AB8540 */
- ab8500_usb_set_ab8540_tuning_values(ab);
- else if (is_ab9540(ab->ab8500))
- /* Phy tuning values for AB9540 */
- ab8500_usb_set_ab9540_tuning_values(ab);
/* Needed to enable ID detection. */
ab8500_usb_wd_workaround(ab);
@@ -1428,11 +930,6 @@ static int ab8500_usb_probe(struct platform_device *pdev)
*/
ab8500_usb_restart_phy(ab);
- if (ab->flags & AB8500_USB_FLAG_USE_CHECK_VBUS_STATUS) {
- if (ab8500_usb_check_vbus_status(ab))
- schedule_work(&ab->vbus_event_work);
- }
-
abx500_usb_link_status_update(ab);
dev_info(&pdev->dev, "revision 0x%2x driver initialized\n", rev);
@@ -1445,7 +942,6 @@ static int ab8500_usb_remove(struct platform_device *pdev)
struct ab8500_usb *ab = platform_get_drvdata(pdev);
cancel_work_sync(&ab->phy_dis_work);
- cancel_work_sync(&ab->vbus_event_work);
usb_remove_phy(&ab->phy);
@@ -1459,8 +955,6 @@ static int ab8500_usb_remove(struct platform_device *pdev)
static const struct platform_device_id ab8500_usb_devtype[] = {
{ .name = "ab8500-usb", },
- { .name = "ab8540-usb", },
- { .name = "ab9540-usb", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, ab8500_usb_devtype);
diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c
index 74ba88297991..a53b89be5324 100644
--- a/drivers/usb/phy/phy-generic.c
+++ b/drivers/usb/phy/phy-generic.c
@@ -63,9 +63,9 @@ static void nop_reset(struct usb_phy_generic *nop)
if (!nop->gpiod_reset)
return;
- gpiod_set_value(nop->gpiod_reset, 1);
+ gpiod_set_value_cansleep(nop->gpiod_reset, 1);
usleep_range(10000, 20000);
- gpiod_set_value(nop->gpiod_reset, 0);
+ gpiod_set_value_cansleep(nop->gpiod_reset, 0);
}
/* interface to regulator framework */
@@ -159,7 +159,7 @@ void usb_gen_phy_shutdown(struct usb_phy *phy)
{
struct usb_phy_generic *nop = dev_get_drvdata(phy->dev);
- gpiod_set_value(nop->gpiod_reset, 1);
+ gpiod_set_value_cansleep(nop->gpiod_reset, 1);
if (!IS_ERR(nop->clk))
clk_disable_unprepare(nop->clk);
diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c
index fbec863350f6..e5aa24c1e4fd 100644
--- a/drivers/usb/phy/phy-mxs-usb.c
+++ b/drivers/usb/phy/phy-mxs-usb.c
@@ -578,7 +578,7 @@ static enum usb_charger_type mxs_charger_primary_detection(struct mxs_phy *x)
* It must be called after DP is pulled up, which is used to
* differentiate DCP and CDP.
*/
-enum usb_charger_type mxs_charger_secondary_detection(struct mxs_phy *x)
+static enum usb_charger_type mxs_charger_secondary_detection(struct mxs_phy *x)
{
struct regmap *regmap = x->regmap_anatop;
int val;
diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c
index f668bfb708d3..0e8d23e51732 100644
--- a/drivers/usb/phy/phy-tegra-usb.c
+++ b/drivers/usb/phy/phy-tegra-usb.c
@@ -16,7 +16,7 @@
#include <linux/export.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_device.h>
@@ -305,14 +305,10 @@ static int utmip_pad_power_off(struct tegra_usb_phy *phy)
static int utmi_wait_register(void __iomem *reg, u32 mask, u32 result)
{
- unsigned long timeout = 2000;
- do {
- if ((readl(reg) & mask) == result)
- return 0;
- udelay(1);
- timeout--;
- } while (timeout);
- return -1;
+ u32 tmp;
+
+ return readl_poll_timeout(reg, tmp, (tmp & mask) == result,
+ 2000, 6000);
}
static void utmi_phy_clk_disable(struct tegra_usb_phy *phy)
diff --git a/drivers/usb/roles/Kconfig b/drivers/usb/roles/Kconfig
new file mode 100644
index 000000000000..f5a5e6f79f1b
--- /dev/null
+++ b/drivers/usb/roles/Kconfig
@@ -0,0 +1,14 @@
+if USB_ROLE_SWITCH
+
+config USB_ROLES_INTEL_XHCI
+ tristate "Intel XHCI USB Role Switch"
+ depends on ACPI && X86
+ help
+ Driver for the internal USB role switch for switching the USB data
+ lines between the xHCI host controller and the dwc3 gadget controller
+ found on various Intel SoCs.
+
+ To compile the driver as a module, choose M here: the module will
+ be called intel-xhci-usb-role-switch.
+
+endif # USB_ROLE_SWITCH
diff --git a/drivers/usb/roles/Makefile b/drivers/usb/roles/Makefile
new file mode 100644
index 000000000000..e44b179ba275
--- /dev/null
+++ b/drivers/usb/roles/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_USB_ROLES_INTEL_XHCI) += intel-xhci-usb-role-switch.o
diff --git a/drivers/usb/roles/intel-xhci-usb-role-switch.c b/drivers/usb/roles/intel-xhci-usb-role-switch.c
new file mode 100644
index 000000000000..de72eedb762e
--- /dev/null
+++ b/drivers/usb/roles/intel-xhci-usb-role-switch.c
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Intel XHCI (Cherry Trail, Broxton and others) USB OTG role switch driver
+ *
+ * Copyright (c) 2016-2017 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Loosely based on android x86 kernel code which is:
+ *
+ * Copyright (C) 2014 Intel Corp.
+ *
+ * Author: Wu, Hao
+ */
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/usb/role.h>
+
+/* register definition */
+#define DUAL_ROLE_CFG0 0x68
+#define SW_VBUS_VALID BIT(24)
+#define SW_IDPIN_EN BIT(21)
+#define SW_IDPIN BIT(20)
+
+#define DUAL_ROLE_CFG1 0x6c
+#define HOST_MODE BIT(29)
+
+#define DUAL_ROLE_CFG1_POLL_TIMEOUT 1000
+
+#define DRV_NAME "intel_xhci_usb_sw"
+
+struct intel_xhci_usb_data {
+ struct usb_role_switch *role_sw;
+ void __iomem *base;
+};
+
+struct intel_xhci_acpi_match {
+ const char *hid;
+ int hrv;
+};
+
+/*
+ * ACPI IDs for PMICs which do not support separate data and power role
+ * detection (USB ACA detection for micro USB OTG), we allow userspace to
+ * change the role manually on these.
+ */
+static const struct intel_xhci_acpi_match allow_userspace_ctrl_ids[] = {
+ { "INT33F4", 3 }, /* X-Powers AXP288 PMIC */
+};
+
+static int intel_xhci_usb_set_role(struct device *dev, enum usb_role role)
+{
+ struct intel_xhci_usb_data *data = dev_get_drvdata(dev);
+ unsigned long timeout;
+ acpi_status status;
+ u32 glk, val;
+
+ /*
+ * On many CHT devices ACPI event (_AEI) handlers read / modify /
+ * write the cfg0 register, just like we do. Take the ACPI lock
+ * to avoid us racing with the AML code.
+ */
+ status = acpi_acquire_global_lock(ACPI_WAIT_FOREVER, &glk);
+ if (ACPI_FAILURE(status) && status != AE_NOT_CONFIGURED) {
+ dev_err(dev, "Error could not acquire lock\n");
+ return -EIO;
+ }
+
+ /* Set idpin value as requested */
+ val = readl(data->base + DUAL_ROLE_CFG0);
+ switch (role) {
+ case USB_ROLE_NONE:
+ val |= SW_IDPIN;
+ val &= ~SW_VBUS_VALID;
+ break;
+ case USB_ROLE_HOST:
+ val &= ~SW_IDPIN;
+ val &= ~SW_VBUS_VALID;
+ break;
+ case USB_ROLE_DEVICE:
+ val |= SW_IDPIN;
+ val |= SW_VBUS_VALID;
+ break;
+ }
+ val |= SW_IDPIN_EN;
+
+ writel(val, data->base + DUAL_ROLE_CFG0);
+
+ acpi_release_global_lock(glk);
+
+ /* In most case it takes about 600ms to finish mode switching */
+ timeout = jiffies + msecs_to_jiffies(DUAL_ROLE_CFG1_POLL_TIMEOUT);
+
+ /* Polling on CFG1 register to confirm mode switch.*/
+ do {
+ val = readl(data->base + DUAL_ROLE_CFG1);
+ if (!!(val & HOST_MODE) == (role == USB_ROLE_HOST))
+ return 0;
+
+ /* Interval for polling is set to about 5 - 10 ms */
+ usleep_range(5000, 10000);
+ } while (time_before(jiffies, timeout));
+
+ dev_warn(dev, "Timeout waiting for role-switch\n");
+ return -ETIMEDOUT;
+}
+
+static enum usb_role intel_xhci_usb_get_role(struct device *dev)
+{
+ struct intel_xhci_usb_data *data = dev_get_drvdata(dev);
+ enum usb_role role;
+ u32 val;
+
+ val = readl(data->base + DUAL_ROLE_CFG0);
+
+ if (!(val & SW_IDPIN))
+ role = USB_ROLE_HOST;
+ else if (val & SW_VBUS_VALID)
+ role = USB_ROLE_DEVICE;
+ else
+ role = USB_ROLE_NONE;
+
+ return role;
+}
+
+static struct usb_role_switch_desc sw_desc = {
+ .set = intel_xhci_usb_set_role,
+ .get = intel_xhci_usb_get_role,
+};
+
+static int intel_xhci_usb_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct intel_xhci_usb_data *data;
+ struct resource *res;
+ int i;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ data->base = devm_ioremap_nocache(dev, res->start, resource_size(res));
+ if (!data->base)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(allow_userspace_ctrl_ids); i++)
+ if (acpi_dev_present(allow_userspace_ctrl_ids[i].hid, "1",
+ allow_userspace_ctrl_ids[i].hrv))
+ sw_desc.allow_userspace_control = true;
+
+ platform_set_drvdata(pdev, data);
+
+ data->role_sw = usb_role_switch_register(dev, &sw_desc);
+ if (IS_ERR(data->role_sw))
+ return PTR_ERR(data->role_sw);
+
+ return 0;
+}
+
+static int intel_xhci_usb_remove(struct platform_device *pdev)
+{
+ struct intel_xhci_usb_data *data = platform_get_drvdata(pdev);
+
+ usb_role_switch_unregister(data->role_sw);
+ return 0;
+}
+
+static const struct platform_device_id intel_xhci_usb_table[] = {
+ { .name = DRV_NAME },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, intel_xhci_usb_table);
+
+static struct platform_driver intel_xhci_usb_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ },
+ .id_table = intel_xhci_usb_table,
+ .probe = intel_xhci_usb_probe,
+ .remove = intel_xhci_usb_remove,
+};
+
+module_platform_driver(intel_xhci_usb_driver);
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Intel XHCI USB role switch driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index a646820f5a78..533f127c30ad 100644
--- a/drivers/usb/serial/Kconfig
+++ b/drivers/usb/serial/Kconfig
@@ -62,6 +62,7 @@ config USB_SERIAL_SIMPLE
- Fundamental Software dongle.
- Google USB serial devices
- HP4x calculators
+ - Libtransistor USB console
- a number of Motorola phones
- Motorola Tetra devices
- Novatel Wireless GPS receivers
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index 06d502b3e913..eb6c26cbe579 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -155,6 +155,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x12B8, 0xEC62) }, /* Link G4+ ECU */
{ USB_DEVICE(0x13AD, 0x9999) }, /* Baltech card reader */
{ USB_DEVICE(0x1555, 0x0004) }, /* Owen AC4 USB-RS485 Converter */
+ { USB_DEVICE(0x155A, 0x1006) }, /* ELDAT Easywave RX09 */
{ USB_DEVICE(0x166A, 0x0201) }, /* Clipsal 5500PACA C-Bus Pascal Automation Controller */
{ USB_DEVICE(0x166A, 0x0301) }, /* Clipsal 5800PC C-Bus Wireless PC Interface */
{ USB_DEVICE(0x166A, 0x0303) }, /* Clipsal 5500PCU C-Bus USB interface */
@@ -213,6 +214,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x3195, 0xF190) }, /* Link Instruments MSO-19 */
{ USB_DEVICE(0x3195, 0xF280) }, /* Link Instruments MSO-28 */
{ USB_DEVICE(0x3195, 0xF281) }, /* Link Instruments MSO-28 */
+ { USB_DEVICE(0x3923, 0x7A0B) }, /* National Instruments USB Serial Console */
{ USB_DEVICE(0x413C, 0x9500) }, /* DW700 GPS USB interface */
{ } /* Terminating Entry */
};
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index f58c4ff6b387..7ea221d42dba 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -769,6 +769,7 @@ static const struct usb_device_id id_table_combined[] = {
.driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk },
{ USB_DEVICE(TELLDUS_VID, TELLDUS_TELLSTICK_PID) },
{ USB_DEVICE(NOVITUS_VID, NOVITUS_BONO_E_PID) },
+ { USB_DEVICE(FTDI_VID, RTSYSTEMS_USB_VX8_PID) },
{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_S03_PID) },
{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_59_PID) },
{ USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_57A_PID) },
@@ -931,6 +932,7 @@ static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_LS_LOGBOOK_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_HS_LOGBOOK_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_CINTERION_MC55I_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_FHE_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_DOTEC_PID) },
{ USB_DEVICE(QIHARDWARE_VID, MILKYMISTONE_JTAGSERIAL_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
@@ -1896,7 +1898,8 @@ static int ftdi_8u2232c_probe(struct usb_serial *serial)
return ftdi_jtag_probe(serial);
if (udev->product &&
- (!strcmp(udev->product, "BeagleBone/XDS100V2") ||
+ (!strcmp(udev->product, "Arrow USB Blaster") ||
+ !strcmp(udev->product, "BeagleBone/XDS100V2") ||
!strcmp(udev->product, "SNAP Connect E10")))
return ftdi_jtag_probe(serial);
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index 8b4ecd2bd297..975d02666c5a 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -923,6 +923,9 @@
/*
* RT Systems programming cables for various ham radios
*/
+/* This device uses the VID of FTDI */
+#define RTSYSTEMS_USB_VX8_PID 0x9e50 /* USB-VX8 USB to 7 pin modular plug for Yaesu VX-8 radio */
+
#define RTSYSTEMS_VID 0x2100 /* Vendor ID */
#define RTSYSTEMS_USB_S03_PID 0x9001 /* RTS-03 USB to Serial Adapter */
#define RTSYSTEMS_USB_59_PID 0x9e50 /* USB-59 USB to 8 pin plug */
@@ -1442,6 +1445,12 @@
#define FTDI_CINTERION_MC55I_PID 0xA951
/*
+ * Product: FirmwareHubEmulator
+ * Manufacturer: Harman Becker Automotive Systems
+ */
+#define FTDI_FHE_PID 0xA9A0
+
+/*
* Product: Comet Caller ID decoder
* Manufacturer: Crucible Technologies
*/
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 2d8d9150da0c..c3f252283ab9 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -548,151 +548,15 @@ static void option_instat_callback(struct urb *urb);
#define WETELECOM_PRODUCT_6802 0x6802
#define WETELECOM_PRODUCT_WMD300 0x6803
-struct option_blacklist_info {
- /* bitmask of interface numbers blacklisted for send_setup */
- const unsigned long sendsetup;
- /* bitmask of interface numbers that are reserved */
- const unsigned long reserved;
-};
-
-static const struct option_blacklist_info four_g_w14_blacklist = {
- .sendsetup = BIT(0) | BIT(1),
-};
-
-static const struct option_blacklist_info four_g_w100_blacklist = {
- .sendsetup = BIT(1) | BIT(2),
- .reserved = BIT(3),
-};
-
-static const struct option_blacklist_info alcatel_x200_blacklist = {
- .sendsetup = BIT(0) | BIT(1),
- .reserved = BIT(4),
-};
-
-static const struct option_blacklist_info zte_0037_blacklist = {
- .sendsetup = BIT(0) | BIT(1),
-};
-
-static const struct option_blacklist_info zte_k3765_z_blacklist = {
- .sendsetup = BIT(0) | BIT(1) | BIT(2),
- .reserved = BIT(4),
-};
-
-static const struct option_blacklist_info zte_ad3812_z_blacklist = {
- .sendsetup = BIT(0) | BIT(1) | BIT(2),
-};
-
-static const struct option_blacklist_info zte_mc2718_z_blacklist = {
- .sendsetup = BIT(1) | BIT(2) | BIT(3) | BIT(4),
-};
-
-static const struct option_blacklist_info zte_mc2716_z_blacklist = {
- .sendsetup = BIT(1) | BIT(2) | BIT(3),
-};
-
-static const struct option_blacklist_info zte_me3620_mbim_blacklist = {
- .reserved = BIT(2) | BIT(3) | BIT(4),
-};
-
-static const struct option_blacklist_info zte_me3620_xl_blacklist = {
- .reserved = BIT(3) | BIT(4) | BIT(5),
-};
-
-static const struct option_blacklist_info zte_zm8620_x_blacklist = {
- .reserved = BIT(3) | BIT(4) | BIT(5),
-};
-
-static const struct option_blacklist_info huawei_cdc12_blacklist = {
- .reserved = BIT(1) | BIT(2),
-};
-
-static const struct option_blacklist_info net_intf0_blacklist = {
- .reserved = BIT(0),
-};
-
-static const struct option_blacklist_info net_intf1_blacklist = {
- .reserved = BIT(1),
-};
-
-static const struct option_blacklist_info net_intf2_blacklist = {
- .reserved = BIT(2),
-};
-
-static const struct option_blacklist_info net_intf3_blacklist = {
- .reserved = BIT(3),
-};
-
-static const struct option_blacklist_info net_intf4_blacklist = {
- .reserved = BIT(4),
-};
-
-static const struct option_blacklist_info net_intf5_blacklist = {
- .reserved = BIT(5),
-};
-
-static const struct option_blacklist_info net_intf6_blacklist = {
- .reserved = BIT(6),
-};
-
-static const struct option_blacklist_info zte_mf626_blacklist = {
- .sendsetup = BIT(0) | BIT(1),
- .reserved = BIT(4),
-};
-
-static const struct option_blacklist_info zte_1255_blacklist = {
- .reserved = BIT(3) | BIT(4),
-};
-
-static const struct option_blacklist_info simcom_sim7100e_blacklist = {
- .reserved = BIT(5) | BIT(6),
-};
-
-static const struct option_blacklist_info telit_me910_blacklist = {
- .sendsetup = BIT(0),
- .reserved = BIT(1) | BIT(3),
-};
-
-static const struct option_blacklist_info telit_me910_dual_modem_blacklist = {
- .sendsetup = BIT(0),
- .reserved = BIT(3),
-};
-
-static const struct option_blacklist_info telit_le910_blacklist = {
- .sendsetup = BIT(0),
- .reserved = BIT(1) | BIT(2),
-};
-static const struct option_blacklist_info telit_le920_blacklist = {
- .sendsetup = BIT(0),
- .reserved = BIT(1) | BIT(5),
-};
+/* Device flags */
-static const struct option_blacklist_info telit_le920a4_blacklist_1 = {
- .sendsetup = BIT(0),
- .reserved = BIT(1),
-};
+/* Interface does not support modem-control requests */
+#define NCTRL(ifnum) ((BIT(ifnum) & 0xff) << 8)
-static const struct option_blacklist_info telit_le922_blacklist_usbcfg0 = {
- .sendsetup = BIT(2),
- .reserved = BIT(0) | BIT(1) | BIT(3),
-};
+/* Interface is reserved */
+#define RSVD(ifnum) ((BIT(ifnum) & 0xff) << 0)
-static const struct option_blacklist_info telit_le922_blacklist_usbcfg3 = {
- .sendsetup = BIT(0),
- .reserved = BIT(1) | BIT(2) | BIT(3),
-};
-
-static const struct option_blacklist_info cinterion_rmnet2_blacklist = {
- .reserved = BIT(4) | BIT(5),
-};
-
-static const struct option_blacklist_info yuga_clm920_nc5_blacklist = {
- .reserved = BIT(1) | BIT(4),
-};
-
-static const struct option_blacklist_info quectel_ep06_blacklist = {
- .reserved = BIT(4) | BIT(5),
-};
static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) },
@@ -726,26 +590,26 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GKE) },
{ USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GLE) },
{ USB_DEVICE(QUANTA_VENDOR_ID, 0xea42),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1c05, USB_CLASS_COMM, 0x02, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1c1f, USB_CLASS_COMM, 0x02, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1c23, USB_CLASS_COMM, 0x02, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E173, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t) &net_intf1_blacklist },
+ .driver_info = RSVD(1) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E173S6, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t) &net_intf1_blacklist },
+ .driver_info = RSVD(1) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1750, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t) &net_intf2_blacklist },
+ .driver_info = RSVD(2) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1441, USB_CLASS_COMM, 0x02, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1442, USB_CLASS_COMM, 0x02, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4505, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist },
+ .driver_info = RSVD(1) | RSVD(2) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3765, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist },
+ .driver_info = RSVD(1) | RSVD(2) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x14ac, 0xff, 0xff, 0xff), /* Huawei E1820 */
- .driver_info = (kernel_ulong_t) &net_intf1_blacklist },
+ .driver_info = RSVD(1) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4605, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist },
+ .driver_info = RSVD(1) | RSVD(2) },
{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0xff, 0xff) },
{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x01) },
{ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x02) },
@@ -1190,67 +1054,67 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_PRODUCT_KPC680) },
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6000)}, /* ZTE AC8700 */
{ USB_DEVICE_AND_INTERFACE_INFO(QUALCOMM_VENDOR_ID, 0x6001, 0xff, 0xff, 0xff), /* 4G LTE usb-modem U901 */
- .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+ .driver_info = RSVD(3) },
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x0023)}, /* ONYX 3G device */
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x9000)}, /* SIMCom SIM5218 */
/* Quectel products using Qualcomm vendor ID */
{ USB_DEVICE(QUALCOMM_VENDOR_ID, QUECTEL_PRODUCT_UC15)},
{ USB_DEVICE(QUALCOMM_VENDOR_ID, QUECTEL_PRODUCT_UC20),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
/* Yuga products use Qualcomm vendor ID */
{ USB_DEVICE(QUALCOMM_VENDOR_ID, YUGA_PRODUCT_CLM920_NC5),
- .driver_info = (kernel_ulong_t)&yuga_clm920_nc5_blacklist },
+ .driver_info = RSVD(1) | RSVD(4) },
/* Quectel products using Quectel vendor ID */
{ USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC21),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC25),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_BG96),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06),
- .driver_info = (kernel_ulong_t)&quectel_ep06_blacklist },
+ .driver_info = RSVD(4) | RSVD(5) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6001) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CMU_300) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6003),
- .driver_info = (kernel_ulong_t)&net_intf0_blacklist },
+ .driver_info = RSVD(0) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6004) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6005) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CGU_628A) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHE_628S),
- .driver_info = (kernel_ulong_t)&net_intf0_blacklist },
+ .driver_info = RSVD(0) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CMU_301),
- .driver_info = (kernel_ulong_t)&net_intf0_blacklist },
+ .driver_info = RSVD(0) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHU_628),
- .driver_info = (kernel_ulong_t)&net_intf0_blacklist },
+ .driver_info = RSVD(0) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHU_628S) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CDU_680) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CDU_685A) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHU_720S),
- .driver_info = (kernel_ulong_t)&net_intf0_blacklist },
+ .driver_info = RSVD(0) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7002),
- .driver_info = (kernel_ulong_t)&net_intf0_blacklist },
+ .driver_info = RSVD(0) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHU_629K),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7004),
- .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+ .driver_info = RSVD(3) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7005) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CGU_629),
- .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+ .driver_info = RSVD(5) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHU_629S),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHU_720I),
- .driver_info = (kernel_ulong_t)&net_intf0_blacklist },
+ .driver_info = RSVD(0) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7212),
- .driver_info = (kernel_ulong_t)&net_intf0_blacklist },
+ .driver_info = RSVD(0) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7213),
- .driver_info = (kernel_ulong_t)&net_intf0_blacklist },
+ .driver_info = RSVD(0) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7251),
- .driver_info = (kernel_ulong_t)&net_intf1_blacklist },
+ .driver_info = RSVD(1) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7252),
- .driver_info = (kernel_ulong_t)&net_intf1_blacklist },
+ .driver_info = RSVD(1) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7253),
- .driver_info = (kernel_ulong_t)&net_intf1_blacklist },
+ .driver_info = RSVD(1) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UC864E) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UC864G) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_CC864_DUAL) },
@@ -1258,38 +1122,38 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_DE910_DUAL) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UE910_V2) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG0),
- .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg0 },
+ .driver_info = RSVD(0) | RSVD(1) | NCTRL(2) | RSVD(3) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG1),
- .driver_info = (kernel_ulong_t)&telit_le910_blacklist },
+ .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG2),
- .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 },
+ .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) | RSVD(3) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG3),
- .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 },
+ .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) | RSVD(3) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG5, 0xff),
- .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg0 },
+ .driver_info = RSVD(0) | RSVD(1) | NCTRL(2) | RSVD(3) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910),
- .driver_info = (kernel_ulong_t)&telit_me910_blacklist },
+ .driver_info = NCTRL(0) | RSVD(1) | RSVD(3) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910_DUAL_MODEM),
- .driver_info = (kernel_ulong_t)&telit_me910_dual_modem_blacklist },
+ .driver_info = NCTRL(0) | RSVD(3) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910),
- .driver_info = (kernel_ulong_t)&telit_le910_blacklist },
+ .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910_USBCFG4),
- .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 },
+ .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) | RSVD(3) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920),
- .driver_info = (kernel_ulong_t)&telit_le920_blacklist },
+ .driver_info = NCTRL(0) | RSVD(1) | RSVD(5) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1207) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1208),
- .driver_info = (kernel_ulong_t)&telit_le920a4_blacklist_1 },
+ .driver_info = NCTRL(0) | RSVD(1) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1211),
- .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 },
+ .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) | RSVD(3) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1212),
- .driver_info = (kernel_ulong_t)&telit_le920a4_blacklist_1 },
+ .driver_info = NCTRL(0) | RSVD(1) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1213, 0xff) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1214),
- .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 },
+ .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) | RSVD(3) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF622, 0xff, 0xff, 0xff) }, /* ZTE WCDMA products */
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0002, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf1_blacklist },
+ .driver_info = RSVD(1) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0003, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0004, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0005, 0xff, 0xff, 0xff) },
@@ -1305,58 +1169,58 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0010, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0011, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0012, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf1_blacklist },
+ .driver_info = RSVD(1) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0013, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF628, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0016, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0017, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+ .driver_info = RSVD(3) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0018, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0019, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+ .driver_info = RSVD(3) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0020, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0021, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0022, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0023, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0024, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0025, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf1_blacklist },
+ .driver_info = RSVD(1) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0028, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0029, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0030, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF626, 0xff,
- 0xff, 0xff), .driver_info = (kernel_ulong_t)&zte_mf626_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF626, 0xff, 0xff, 0xff),
+ .driver_info = NCTRL(0) | NCTRL(1) | RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0032, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0033, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0034, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0037, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&zte_0037_blacklist },
+ .driver_info = NCTRL(0) | NCTRL(1) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0038, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0039, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0040, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0042, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0043, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0044, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0048, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0049, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+ .driver_info = RSVD(5) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0050, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0051, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0052, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0054, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0055, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf1_blacklist },
+ .driver_info = RSVD(1) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0056, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0057, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0058, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0061, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0062, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0063, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0064, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0065, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0066, 0xff, 0xff, 0xff) },
@@ -1381,26 +1245,26 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0096, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0097, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0104, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0105, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0106, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0108, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0113, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+ .driver_info = RSVD(5) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0117, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0118, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+ .driver_info = RSVD(5) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0121, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+ .driver_info = RSVD(5) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0122, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0123, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0124, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+ .driver_info = RSVD(5) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0125, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf6_blacklist },
+ .driver_info = RSVD(6) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0126, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+ .driver_info = RSVD(5) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0128, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0135, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0136, 0xff, 0xff, 0xff) },
@@ -1416,50 +1280,50 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0155, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0156, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0157, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+ .driver_info = RSVD(5) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0158, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+ .driver_info = RSVD(3) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0159, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0161, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0162, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0164, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0165, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0167, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0189, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0191, 0xff, 0xff, 0xff), /* ZTE EuFi890 */
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0196, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0197, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0199, 0xff, 0xff, 0xff), /* ZTE MF820S */
- .driver_info = (kernel_ulong_t)&net_intf1_blacklist },
+ .driver_info = RSVD(1) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0200, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0201, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0254, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0257, 0xff, 0xff, 0xff), /* ZTE MF821 */
- .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+ .driver_info = RSVD(3) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0265, 0xff, 0xff, 0xff), /* ONDA MT8205 */
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0284, 0xff, 0xff, 0xff), /* ZTE MF880 */
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0317, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0326, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0330, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0395, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0412, 0xff, 0xff, 0xff), /* Telewell TW-LTE 4G */
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0414, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0417, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1008, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1010, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1012, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1018, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1021, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf2_blacklist },
+ .driver_info = RSVD(2) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1057, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1058, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1059, 0xff, 0xff, 0xff) },
@@ -1576,23 +1440,23 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1170, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1244, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1245, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1246, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1247, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1248, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1249, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1250, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1251, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1252, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1253, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1254, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1255, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&zte_1255_blacklist },
+ .driver_info = RSVD(3) | RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1256, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1257, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1258, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1259, 0xff, 0xff, 0xff) },
@@ -1607,7 +1471,7 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1268, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1269, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1270, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+ .driver_info = RSVD(5) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1271, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1272, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1273, 0xff, 0xff, 0xff) },
@@ -1643,17 +1507,17 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1303, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1333, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1401, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf2_blacklist },
+ .driver_info = RSVD(2) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1402, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf2_blacklist },
+ .driver_info = RSVD(2) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1424, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf2_blacklist },
+ .driver_info = RSVD(2) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1425, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf2_blacklist },
+ .driver_info = RSVD(2) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1426, 0xff, 0xff, 0xff), /* ZTE MF91 */
- .driver_info = (kernel_ulong_t)&net_intf2_blacklist },
+ .driver_info = RSVD(2) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1428, 0xff, 0xff, 0xff), /* Telewell TW-LTE 4G v2 */
- .driver_info = (kernel_ulong_t)&net_intf2_blacklist },
+ .driver_info = RSVD(2) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1533, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1534, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1535, 0xff, 0xff, 0xff) },
@@ -1671,8 +1535,8 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1596, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1598, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1600, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2002, 0xff,
- 0xff, 0xff), .driver_info = (kernel_ulong_t)&zte_k3765_z_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2002, 0xff, 0xff, 0xff),
+ .driver_info = NCTRL(0) | NCTRL(1) | NCTRL(2) | RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2003, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0014, 0xff, 0xff, 0xff) }, /* ZTE CDMA products */
@@ -1683,20 +1547,20 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0073, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0094, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0130, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf1_blacklist },
+ .driver_info = RSVD(1) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0133, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+ .driver_info = RSVD(3) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0141, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+ .driver_info = RSVD(5) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0147, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0152, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0168, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0170, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0176, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+ .driver_info = RSVD(3) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0178, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+ .driver_info = RSVD(3) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff42, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff43, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff44, 0xff, 0xff, 0xff) },
@@ -1848,19 +1712,19 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC2726, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC8710T, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MC2718, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&zte_mc2718_z_blacklist },
+ .driver_info = NCTRL(1) | NCTRL(2) | NCTRL(3) | NCTRL(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AD3812, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&zte_ad3812_z_blacklist },
+ .driver_info = NCTRL(0) | NCTRL(1) | NCTRL(2) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MC2716, 0xff, 0xff, 0xff),
- .driver_info = (kernel_ulong_t)&zte_mc2716_z_blacklist },
+ .driver_info = NCTRL(1) | NCTRL(2) | NCTRL(3) },
{ USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_ME3620_L),
- .driver_info = (kernel_ulong_t)&zte_me3620_xl_blacklist },
+ .driver_info = RSVD(3) | RSVD(4) | RSVD(5) },
{ USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_ME3620_MBIM),
- .driver_info = (kernel_ulong_t)&zte_me3620_mbim_blacklist },
+ .driver_info = RSVD(2) | RSVD(3) | RSVD(4) },
{ USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_ME3620_X),
- .driver_info = (kernel_ulong_t)&zte_me3620_xl_blacklist },
+ .driver_info = RSVD(3) | RSVD(4) | RSVD(5) },
{ USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_ZM8620_X),
- .driver_info = (kernel_ulong_t)&zte_zm8620_x_blacklist },
+ .driver_info = RSVD(3) | RSVD(4) | RSVD(5) },
{ USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x02, 0x01) },
{ USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x02, 0x05) },
{ USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x86, 0x10) },
@@ -1880,37 +1744,34 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(ALINK_VENDOR_ID, ALINK_PRODUCT_PH300) },
{ USB_DEVICE_AND_INTERFACE_INFO(ALINK_VENDOR_ID, ALINK_PRODUCT_3GU, 0xff, 0xff, 0xff) },
{ USB_DEVICE(ALINK_VENDOR_ID, SIMCOM_PRODUCT_SIM7100E),
- .driver_info = (kernel_ulong_t)&simcom_sim7100e_blacklist },
+ .driver_info = RSVD(5) | RSVD(6) },
{ USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X060S_X200),
- .driver_info = (kernel_ulong_t)&alcatel_x200_blacklist
- },
+ .driver_info = NCTRL(0) | NCTRL(1) | RSVD(4) },
{ USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X220_X500D),
- .driver_info = (kernel_ulong_t)&net_intf6_blacklist },
+ .driver_info = RSVD(6) },
{ USB_DEVICE(ALCATEL_VENDOR_ID, 0x0052),
- .driver_info = (kernel_ulong_t)&net_intf6_blacklist },
+ .driver_info = RSVD(6) },
{ USB_DEVICE(ALCATEL_VENDOR_ID, 0x00b6),
- .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+ .driver_info = RSVD(3) },
{ USB_DEVICE(ALCATEL_VENDOR_ID, 0x00b7),
- .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
+ .driver_info = RSVD(5) },
{ USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_L100V),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_L800MA),
- .driver_info = (kernel_ulong_t)&net_intf2_blacklist },
+ .driver_info = RSVD(2) },
{ USB_DEVICE(AIRPLUS_VENDOR_ID, AIRPLUS_PRODUCT_MCD650) },
{ USB_DEVICE(TLAYTECH_VENDOR_ID, TLAYTECH_PRODUCT_TEU800) },
{ USB_DEVICE(LONGCHEER_VENDOR_ID, FOUR_G_SYSTEMS_PRODUCT_W14),
- .driver_info = (kernel_ulong_t)&four_g_w14_blacklist
- },
+ .driver_info = NCTRL(0) | NCTRL(1) },
{ USB_DEVICE(LONGCHEER_VENDOR_ID, FOUR_G_SYSTEMS_PRODUCT_W100),
- .driver_info = (kernel_ulong_t)&four_g_w100_blacklist
- },
+ .driver_info = NCTRL(1) | NCTRL(2) | RSVD(3) },
{USB_DEVICE(LONGCHEER_VENDOR_ID, FUJISOFT_PRODUCT_FS040U),
- .driver_info = (kernel_ulong_t)&net_intf3_blacklist},
+ .driver_info = RSVD(3)},
{ USB_DEVICE_INTERFACE_CLASS(LONGCHEER_VENDOR_ID, SPEEDUP_PRODUCT_SU9800, 0xff) },
{ USB_DEVICE_INTERFACE_CLASS(LONGCHEER_VENDOR_ID, 0x9801, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
+ .driver_info = RSVD(3) },
{ USB_DEVICE_INTERFACE_CLASS(LONGCHEER_VENDOR_ID, 0x9803, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE(LONGCHEER_VENDOR_ID, ZOOM_PRODUCT_4597) },
{ USB_DEVICE(LONGCHEER_VENDOR_ID, IBALL_3_5G_CONNECT) },
{ USB_DEVICE(HAIER_VENDOR_ID, HAIER_PRODUCT_CE100) },
@@ -1936,14 +1797,14 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_EU3_E) },
{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_EU3_P) },
{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PH8),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AHXX, 0xff) },
{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PLXX),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PH8_2RMNET, 0xff),
- .driver_info = (kernel_ulong_t)&cinterion_rmnet2_blacklist },
+ .driver_info = RSVD(4) | RSVD(5) },
{ USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PH8_AUDIO, 0xff),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AHXX_2RMNET, 0xff) },
{ USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AHXX_AUDIO, 0xff) },
{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_HC28_MDM) },
@@ -1953,20 +1814,20 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(SIEMENS_VENDOR_ID, CINTERION_PRODUCT_HC28_MDM) }, /* HC28 enumerates with Siemens or Cinterion VID depending on FW revision */
{ USB_DEVICE(SIEMENS_VENDOR_ID, CINTERION_PRODUCT_HC28_MDMNET) },
{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD100),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD120),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD140),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD145) },
{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD155),
- .driver_info = (kernel_ulong_t)&net_intf6_blacklist },
+ .driver_info = RSVD(6) },
{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD200),
- .driver_info = (kernel_ulong_t)&net_intf6_blacklist },
+ .driver_info = RSVD(6) },
{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD160),
- .driver_info = (kernel_ulong_t)&net_intf6_blacklist },
+ .driver_info = RSVD(6) },
{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD500),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE(CELOT_VENDOR_ID, CELOT_PRODUCT_CT680M) }, /* CT-650 CDMA 450 1xEVDO modem */
{ USB_DEVICE_AND_INTERFACE_INFO(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_GT_B3730, USB_CLASS_CDC_DATA, 0x00, 0x00) }, /* Samsung GT-B3730 LTE USB modem.*/
{ USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CEM600) },
@@ -2043,9 +1904,9 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(PETATEL_VENDOR_ID, PETATEL_PRODUCT_NP10T_600E) },
{ USB_DEVICE_AND_INTERFACE_INFO(TPLINK_VENDOR_ID, TPLINK_PRODUCT_LTE, 0xff, 0x00, 0x00) }, /* TP-Link LTE Module */
{ USB_DEVICE(TPLINK_VENDOR_ID, TPLINK_PRODUCT_MA180),
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE(TPLINK_VENDOR_ID, 0x9000), /* TP-Link MA260 */
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE(CHANGHONG_VENDOR_ID, CHANGHONG_PRODUCT_CH690) },
{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d01, 0xff) }, /* D-Link DWM-156 (variant) */
{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d02, 0xff) },
@@ -2053,9 +1914,9 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d04, 0xff) }, /* D-Link DWM-158 */
{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d0e, 0xff) }, /* D-Link DWM-157 C1 */
{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e19, 0xff), /* D-Link DWM-221 B1 */
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e35, 0xff), /* D-Link DWM-222 */
- .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ .driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e01, 0xff, 0xff, 0xff) }, /* D-Link DWM-152/C1 */
{ USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e02, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/C1 */
{ USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x7e11, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/A3 */
@@ -2114,11 +1975,10 @@ static int option_probe(struct usb_serial *serial,
{
struct usb_interface_descriptor *iface_desc =
&serial->interface->cur_altsetting->desc;
- struct usb_device_descriptor *dev_desc = &serial->dev->descriptor;
- const struct option_blacklist_info *blacklist;
+ unsigned long device_flags = id->driver_info;
/* Never bind to the CD-Rom emulation interface */
- if (iface_desc->bInterfaceClass == 0x08)
+ if (iface_desc->bInterfaceClass == USB_CLASS_MASS_STORAGE)
return -ENODEV;
/*
@@ -2126,21 +1986,11 @@ static int option_probe(struct usb_serial *serial,
* the same class/subclass/protocol as the serial interfaces. Look at
* the Windows driver .INF files for reserved interface numbers.
*/
- blacklist = (void *)id->driver_info;
- if (blacklist && test_bit(iface_desc->bInterfaceNumber,
- &blacklist->reserved))
- return -ENODEV;
- /*
- * Don't bind network interface on Samsung GT-B3730, it is handled by
- * a separate module.
- */
- if (dev_desc->idVendor == cpu_to_le16(SAMSUNG_VENDOR_ID) &&
- dev_desc->idProduct == cpu_to_le16(SAMSUNG_PRODUCT_GT_B3730) &&
- iface_desc->bInterfaceClass != USB_CLASS_CDC_DATA)
+ if (device_flags & RSVD(iface_desc->bInterfaceNumber))
return -ENODEV;
- /* Store the blacklist info so we can use it during attach. */
- usb_set_serial_data(serial, (void *)blacklist);
+ /* Store the device flags so we can use them during attach. */
+ usb_set_serial_data(serial, (void *)device_flags);
return 0;
}
@@ -2148,22 +1998,21 @@ static int option_probe(struct usb_serial *serial,
static int option_attach(struct usb_serial *serial)
{
struct usb_interface_descriptor *iface_desc;
- const struct option_blacklist_info *blacklist;
struct usb_wwan_intf_private *data;
+ unsigned long device_flags;
data = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL);
if (!data)
return -ENOMEM;
- /* Retrieve blacklist info stored at probe. */
- blacklist = usb_get_serial_data(serial);
+ /* Retrieve device flags stored at probe. */
+ device_flags = (unsigned long)usb_get_serial_data(serial);
iface_desc = &serial->interface->cur_altsetting->desc;
- if (!blacklist || !test_bit(iface_desc->bInterfaceNumber,
- &blacklist->sendsetup)) {
+ if (!(device_flags & NCTRL(iface_desc->bInterfaceNumber)))
data->use_send_setup = 1;
- }
+
spin_lock_init(&data->susp_lock);
usb_set_serial_data(serial, data);
diff --git a/drivers/usb/serial/usb-serial-simple.c b/drivers/usb/serial/usb-serial-simple.c
index 4ef79e29cb26..40864c2bd9dc 100644
--- a/drivers/usb/serial/usb-serial-simple.c
+++ b/drivers/usb/serial/usb-serial-simple.c
@@ -63,6 +63,11 @@ DEVICE(flashloader, FLASHLOADER_IDS);
0x01) }
DEVICE(google, GOOGLE_IDS);
+/* Libtransistor USB console */
+#define LIBTRANSISTOR_IDS() \
+ { USB_DEVICE(0x1209, 0x8b00) }
+DEVICE(libtransistor, LIBTRANSISTOR_IDS);
+
/* ViVOpay USB Serial Driver */
#define VIVOPAY_IDS() \
{ USB_DEVICE(0x1d5f, 0x1004) } /* ViVOpay 8800 */
@@ -110,6 +115,7 @@ static struct usb_serial_driver * const serial_drivers[] = {
&funsoft_device,
&flashloader_device,
&google_device,
+ &libtransistor_device,
&vivopay_device,
&moto_modem_device,
&motorola_tetra_device,
@@ -126,6 +132,7 @@ static const struct usb_device_id id_table[] = {
FUNSOFT_IDS(),
FLASHLOADER_IDS(),
GOOGLE_IDS(),
+ LIBTRANSISTOR_IDS(),
VIVOPAY_IDS(),
MOTO_IDS(),
MOTOROLA_TETRA_IDS(),
diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
index bcb2744c5977..030f88cb0c3f 100644
--- a/drivers/usb/typec/Kconfig
+++ b/drivers/usb/typec/Kconfig
@@ -48,6 +48,7 @@ if TYPEC
config TYPEC_TCPM
tristate "USB Type-C Port Controller Manager"
depends on USB
+ select USB_ROLE_SWITCH
help
The Type-C Port Controller Manager provides a USB PD and USB Type-C
state machine for use with Type-C Port Controllers.
@@ -84,4 +85,6 @@ config TYPEC_TPS6598X
If you choose to build this driver as a dynamically linked module, the
module will be called tps6598x.ko.
+source "drivers/usb/typec/mux/Kconfig"
+
endif # TYPEC
diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
index bb3138a6eaac..1f599a6c30cc 100644
--- a/drivers/usb/typec/Makefile
+++ b/drivers/usb/typec/Makefile
@@ -1,7 +1,9 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_TYPEC) += typec.o
+typec-y := class.o mux.o
obj-$(CONFIG_TYPEC_TCPM) += tcpm.o
obj-y += fusb302/
obj-$(CONFIG_TYPEC_WCOVE) += typec_wcove.o
obj-$(CONFIG_TYPEC_UCSI) += ucsi/
obj-$(CONFIG_TYPEC_TPS6598X) += tps6598x.o
+obj-$(CONFIG_TYPEC) += mux/
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
new file mode 100644
index 000000000000..53df10df2f9d
--- /dev/null
+++ b/drivers/usb/typec/class.c
@@ -0,0 +1,1447 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USB Type-C Connector Class
+ *
+ * Copyright (C) 2017, Intel Corporation
+ * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/usb/typec.h>
+#include <linux/usb/typec_mux.h>
+
+struct typec_mode {
+ int index;
+ u32 vdo;
+ char *desc;
+ enum typec_port_type roles;
+
+ struct typec_altmode *alt_mode;
+
+ unsigned int active:1;
+
+ char group_name[6];
+ struct attribute_group group;
+ struct attribute *attrs[5];
+ struct device_attribute vdo_attr;
+ struct device_attribute desc_attr;
+ struct device_attribute active_attr;
+ struct device_attribute roles_attr;
+};
+
+struct typec_altmode {
+ struct device dev;
+ u16 svid;
+ int n_modes;
+ struct typec_mode modes[ALTMODE_MAX_MODES];
+ const struct attribute_group *mode_groups[ALTMODE_MAX_MODES];
+};
+
+struct typec_plug {
+ struct device dev;
+ enum typec_plug_index index;
+};
+
+struct typec_cable {
+ struct device dev;
+ enum typec_plug_type type;
+ struct usb_pd_identity *identity;
+ unsigned int active:1;
+};
+
+struct typec_partner {
+ struct device dev;
+ unsigned int usb_pd:1;
+ struct usb_pd_identity *identity;
+ enum typec_accessory accessory;
+};
+
+struct typec_port {
+ unsigned int id;
+ struct device dev;
+
+ int prefer_role;
+ enum typec_data_role data_role;
+ enum typec_role pwr_role;
+ enum typec_role vconn_role;
+ enum typec_pwr_opmode pwr_opmode;
+ enum typec_port_type port_type;
+ struct mutex port_type_lock;
+
+ enum typec_orientation orientation;
+ struct typec_switch *sw;
+ struct typec_mux *mux;
+
+ const struct typec_capability *cap;
+};
+
+#define to_typec_port(_dev_) container_of(_dev_, struct typec_port, dev)
+#define to_typec_plug(_dev_) container_of(_dev_, struct typec_plug, dev)
+#define to_typec_cable(_dev_) container_of(_dev_, struct typec_cable, dev)
+#define to_typec_partner(_dev_) container_of(_dev_, struct typec_partner, dev)
+#define to_altmode(_dev_) container_of(_dev_, struct typec_altmode, dev)
+
+static const struct device_type typec_partner_dev_type;
+static const struct device_type typec_cable_dev_type;
+static const struct device_type typec_plug_dev_type;
+static const struct device_type typec_port_dev_type;
+
+#define is_typec_partner(_dev_) (_dev_->type == &typec_partner_dev_type)
+#define is_typec_cable(_dev_) (_dev_->type == &typec_cable_dev_type)
+#define is_typec_plug(_dev_) (_dev_->type == &typec_plug_dev_type)
+#define is_typec_port(_dev_) (_dev_->type == &typec_port_dev_type)
+
+static DEFINE_IDA(typec_index_ida);
+static struct class *typec_class;
+
+/* ------------------------------------------------------------------------- */
+/* Common attributes */
+
+static const char * const typec_accessory_modes[] = {
+ [TYPEC_ACCESSORY_NONE] = "none",
+ [TYPEC_ACCESSORY_AUDIO] = "analog_audio",
+ [TYPEC_ACCESSORY_DEBUG] = "debug",
+};
+
+static struct usb_pd_identity *get_pd_identity(struct device *dev)
+{
+ if (is_typec_partner(dev)) {
+ struct typec_partner *partner = to_typec_partner(dev);
+
+ return partner->identity;
+ } else if (is_typec_cable(dev)) {
+ struct typec_cable *cable = to_typec_cable(dev);
+
+ return cable->identity;
+ }
+ return NULL;
+}
+
+static ssize_t id_header_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_pd_identity *id = get_pd_identity(dev);
+
+ return sprintf(buf, "0x%08x\n", id->id_header);
+}
+static DEVICE_ATTR_RO(id_header);
+
+static ssize_t cert_stat_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_pd_identity *id = get_pd_identity(dev);
+
+ return sprintf(buf, "0x%08x\n", id->cert_stat);
+}
+static DEVICE_ATTR_RO(cert_stat);
+
+static ssize_t product_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_pd_identity *id = get_pd_identity(dev);
+
+ return sprintf(buf, "0x%08x\n", id->product);
+}
+static DEVICE_ATTR_RO(product);
+
+static struct attribute *usb_pd_id_attrs[] = {
+ &dev_attr_id_header.attr,
+ &dev_attr_cert_stat.attr,
+ &dev_attr_product.attr,
+ NULL
+};
+
+static const struct attribute_group usb_pd_id_group = {
+ .name = "identity",
+ .attrs = usb_pd_id_attrs,
+};
+
+static const struct attribute_group *usb_pd_id_groups[] = {
+ &usb_pd_id_group,
+ NULL,
+};
+
+static void typec_report_identity(struct device *dev)
+{
+ sysfs_notify(&dev->kobj, "identity", "id_header");
+ sysfs_notify(&dev->kobj, "identity", "cert_stat");
+ sysfs_notify(&dev->kobj, "identity", "product");
+}
+
+/* ------------------------------------------------------------------------- */
+/* Alternate Modes */
+
+/**
+ * typec_altmode_update_active - Report Enter/Exit mode
+ * @alt: Handle to the alternate mode
+ * @mode: Mode index
+ * @active: True when the mode has been entered
+ *
+ * If a partner or cable plug executes Enter/Exit Mode command successfully, the
+ * drivers use this routine to report the updated state of the mode.
+ */
+void typec_altmode_update_active(struct typec_altmode *alt, int mode,
+ bool active)
+{
+ struct typec_mode *m = &alt->modes[mode];
+ char dir[6];
+
+ if (m->active == active)
+ return;
+
+ m->active = active;
+ snprintf(dir, sizeof(dir), "mode%d", mode);
+ sysfs_notify(&alt->dev.kobj, dir, "active");
+ kobject_uevent(&alt->dev.kobj, KOBJ_CHANGE);
+}
+EXPORT_SYMBOL_GPL(typec_altmode_update_active);
+
+/**
+ * typec_altmode2port - Alternate Mode to USB Type-C port
+ * @alt: The Alternate Mode
+ *
+ * Returns handle to the port that a cable plug or partner with @alt is
+ * connected to.
+ */
+struct typec_port *typec_altmode2port(struct typec_altmode *alt)
+{
+ if (is_typec_plug(alt->dev.parent))
+ return to_typec_port(alt->dev.parent->parent->parent);
+ if (is_typec_partner(alt->dev.parent))
+ return to_typec_port(alt->dev.parent->parent);
+ if (is_typec_port(alt->dev.parent))
+ return to_typec_port(alt->dev.parent);
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(typec_altmode2port);
+
+static ssize_t
+typec_altmode_vdo_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct typec_mode *mode = container_of(attr, struct typec_mode,
+ vdo_attr);
+
+ return sprintf(buf, "0x%08x\n", mode->vdo);
+}
+
+static ssize_t
+typec_altmode_desc_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct typec_mode *mode = container_of(attr, struct typec_mode,
+ desc_attr);
+
+ return sprintf(buf, "%s\n", mode->desc ? mode->desc : "");
+}
+
+static ssize_t
+typec_altmode_active_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct typec_mode *mode = container_of(attr, struct typec_mode,
+ active_attr);
+
+ return sprintf(buf, "%s\n", mode->active ? "yes" : "no");
+}
+
+static ssize_t
+typec_altmode_active_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct typec_mode *mode = container_of(attr, struct typec_mode,
+ active_attr);
+ struct typec_port *port = typec_altmode2port(mode->alt_mode);
+ bool activate;
+ int ret;
+
+ if (!port->cap->activate_mode)
+ return -EOPNOTSUPP;
+
+ ret = kstrtobool(buf, &activate);
+ if (ret)
+ return ret;
+
+ ret = port->cap->activate_mode(port->cap, mode->index, activate);
+ if (ret)
+ return ret;
+
+ return size;
+}
+
+static ssize_t
+typec_altmode_roles_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct typec_mode *mode = container_of(attr, struct typec_mode,
+ roles_attr);
+ ssize_t ret;
+
+ switch (mode->roles) {
+ case TYPEC_PORT_SRC:
+ ret = sprintf(buf, "source\n");
+ break;
+ case TYPEC_PORT_SNK:
+ ret = sprintf(buf, "sink\n");
+ break;
+ case TYPEC_PORT_DRP:
+ default:
+ ret = sprintf(buf, "source sink\n");
+ break;
+ }
+ return ret;
+}
+
+static void typec_init_modes(struct typec_altmode *alt,
+ const struct typec_mode_desc *desc, bool is_port)
+{
+ int i;
+
+ for (i = 0; i < alt->n_modes; i++, desc++) {
+ struct typec_mode *mode = &alt->modes[i];
+
+ /* Not considering the human readable description critical */
+ mode->desc = kstrdup(desc->desc, GFP_KERNEL);
+ if (desc->desc && !mode->desc)
+ dev_err(&alt->dev, "failed to copy mode%d desc\n", i);
+
+ mode->alt_mode = alt;
+ mode->vdo = desc->vdo;
+ mode->roles = desc->roles;
+ mode->index = desc->index;
+ sprintf(mode->group_name, "mode%d", desc->index);
+
+ sysfs_attr_init(&mode->vdo_attr.attr);
+ mode->vdo_attr.attr.name = "vdo";
+ mode->vdo_attr.attr.mode = 0444;
+ mode->vdo_attr.show = typec_altmode_vdo_show;
+
+ sysfs_attr_init(&mode->desc_attr.attr);
+ mode->desc_attr.attr.name = "description";
+ mode->desc_attr.attr.mode = 0444;
+ mode->desc_attr.show = typec_altmode_desc_show;
+
+ sysfs_attr_init(&mode->active_attr.attr);
+ mode->active_attr.attr.name = "active";
+ mode->active_attr.attr.mode = 0644;
+ mode->active_attr.show = typec_altmode_active_show;
+ mode->active_attr.store = typec_altmode_active_store;
+
+ mode->attrs[0] = &mode->vdo_attr.attr;
+ mode->attrs[1] = &mode->desc_attr.attr;
+ mode->attrs[2] = &mode->active_attr.attr;
+
+ /* With ports, list the roles that the mode is supported with */
+ if (is_port) {
+ sysfs_attr_init(&mode->roles_attr.attr);
+ mode->roles_attr.attr.name = "supported_roles";
+ mode->roles_attr.attr.mode = 0444;
+ mode->roles_attr.show = typec_altmode_roles_show;
+
+ mode->attrs[3] = &mode->roles_attr.attr;
+ }
+
+ mode->group.attrs = mode->attrs;
+ mode->group.name = mode->group_name;
+
+ alt->mode_groups[i] = &mode->group;
+ }
+}
+
+static ssize_t svid_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct typec_altmode *alt = to_altmode(dev);
+
+ return sprintf(buf, "%04x\n", alt->svid);
+}
+static DEVICE_ATTR_RO(svid);
+
+static struct attribute *typec_altmode_attrs[] = {
+ &dev_attr_svid.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(typec_altmode);
+
+static void typec_altmode_release(struct device *dev)
+{
+ struct typec_altmode *alt = to_altmode(dev);
+ int i;
+
+ for (i = 0; i < alt->n_modes; i++)
+ kfree(alt->modes[i].desc);
+ kfree(alt);
+}
+
+static const struct device_type typec_altmode_dev_type = {
+ .name = "typec_alternate_mode",
+ .groups = typec_altmode_groups,
+ .release = typec_altmode_release,
+};
+
+static struct typec_altmode *
+typec_register_altmode(struct device *parent,
+ const struct typec_altmode_desc *desc)
+{
+ struct typec_altmode *alt;
+ int ret;
+
+ alt = kzalloc(sizeof(*alt), GFP_KERNEL);
+ if (!alt)
+ return ERR_PTR(-ENOMEM);
+
+ alt->svid = desc->svid;
+ alt->n_modes = desc->n_modes;
+ typec_init_modes(alt, desc->modes, is_typec_port(parent));
+
+ alt->dev.parent = parent;
+ alt->dev.groups = alt->mode_groups;
+ alt->dev.type = &typec_altmode_dev_type;
+ dev_set_name(&alt->dev, "svid-%04x", alt->svid);
+
+ ret = device_register(&alt->dev);
+ if (ret) {
+ dev_err(parent, "failed to register alternate mode (%d)\n",
+ ret);
+ put_device(&alt->dev);
+ return ERR_PTR(ret);
+ }
+
+ return alt;
+}
+
+/**
+ * typec_unregister_altmode - Unregister Alternate Mode
+ * @alt: The alternate mode to be unregistered
+ *
+ * Unregister device created with typec_partner_register_altmode(),
+ * typec_plug_register_altmode() or typec_port_register_altmode().
+ */
+void typec_unregister_altmode(struct typec_altmode *alt)
+{
+ if (!IS_ERR_OR_NULL(alt))
+ device_unregister(&alt->dev);
+}
+EXPORT_SYMBOL_GPL(typec_unregister_altmode);
+
+/* ------------------------------------------------------------------------- */
+/* Type-C Partners */
+
+static ssize_t accessory_mode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct typec_partner *p = to_typec_partner(dev);
+
+ return sprintf(buf, "%s\n", typec_accessory_modes[p->accessory]);
+}
+static DEVICE_ATTR_RO(accessory_mode);
+
+static ssize_t supports_usb_power_delivery_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct typec_partner *p = to_typec_partner(dev);
+
+ return sprintf(buf, "%s\n", p->usb_pd ? "yes" : "no");
+}
+static DEVICE_ATTR_RO(supports_usb_power_delivery);
+
+static struct attribute *typec_partner_attrs[] = {
+ &dev_attr_accessory_mode.attr,
+ &dev_attr_supports_usb_power_delivery.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(typec_partner);
+
+static void typec_partner_release(struct device *dev)
+{
+ struct typec_partner *partner = to_typec_partner(dev);
+
+ kfree(partner);
+}
+
+static const struct device_type typec_partner_dev_type = {
+ .name = "typec_partner",
+ .groups = typec_partner_groups,
+ .release = typec_partner_release,
+};
+
+/**
+ * typec_partner_set_identity - Report result from Discover Identity command
+ * @partner: The partner updated identity values
+ *
+ * This routine is used to report that the result of Discover Identity USB power
+ * delivery command has become available.
+ */
+int typec_partner_set_identity(struct typec_partner *partner)
+{
+ if (!partner->identity)
+ return -EINVAL;
+
+ typec_report_identity(&partner->dev);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(typec_partner_set_identity);
+
+/**
+ * typec_partner_register_altmode - Register USB Type-C Partner Alternate Mode
+ * @partner: USB Type-C Partner that supports the alternate mode
+ * @desc: Description of the alternate mode
+ *
+ * This routine is used to register each alternate mode individually that
+ * @partner has listed in response to Discover SVIDs command. The modes for a
+ * SVID listed in response to Discover Modes command need to be listed in an
+ * array in @desc.
+ *
+ * Returns handle to the alternate mode on success or NULL on failure.
+ */
+struct typec_altmode *
+typec_partner_register_altmode(struct typec_partner *partner,
+ const struct typec_altmode_desc *desc)
+{
+ return typec_register_altmode(&partner->dev, desc);
+}
+EXPORT_SYMBOL_GPL(typec_partner_register_altmode);
+
+/**
+ * typec_register_partner - Register a USB Type-C Partner
+ * @port: The USB Type-C Port the partner is connected to
+ * @desc: Description of the partner
+ *
+ * Registers a device for USB Type-C Partner described in @desc.
+ *
+ * Returns handle to the partner on success or ERR_PTR on failure.
+ */
+struct typec_partner *typec_register_partner(struct typec_port *port,
+ struct typec_partner_desc *desc)
+{
+ struct typec_partner *partner;
+ int ret;
+
+ partner = kzalloc(sizeof(*partner), GFP_KERNEL);
+ if (!partner)
+ return ERR_PTR(-ENOMEM);
+
+ partner->usb_pd = desc->usb_pd;
+ partner->accessory = desc->accessory;
+
+ if (desc->identity) {
+ /*
+ * Creating directory for the identity only if the driver is
+ * able to provide data to it.
+ */
+ partner->dev.groups = usb_pd_id_groups;
+ partner->identity = desc->identity;
+ }
+
+ partner->dev.class = typec_class;
+ partner->dev.parent = &port->dev;
+ partner->dev.type = &typec_partner_dev_type;
+ dev_set_name(&partner->dev, "%s-partner", dev_name(&port->dev));
+
+ ret = device_register(&partner->dev);
+ if (ret) {
+ dev_err(&port->dev, "failed to register partner (%d)\n", ret);
+ put_device(&partner->dev);
+ return ERR_PTR(ret);
+ }
+
+ return partner;
+}
+EXPORT_SYMBOL_GPL(typec_register_partner);
+
+/**
+ * typec_unregister_partner - Unregister a USB Type-C Partner
+ * @partner: The partner to be unregistered
+ *
+ * Unregister device created with typec_register_partner().
+ */
+void typec_unregister_partner(struct typec_partner *partner)
+{
+ if (!IS_ERR_OR_NULL(partner))
+ device_unregister(&partner->dev);
+}
+EXPORT_SYMBOL_GPL(typec_unregister_partner);
+
+/* ------------------------------------------------------------------------- */
+/* Type-C Cable Plugs */
+
+static void typec_plug_release(struct device *dev)
+{
+ struct typec_plug *plug = to_typec_plug(dev);
+
+ kfree(plug);
+}
+
+static const struct device_type typec_plug_dev_type = {
+ .name = "typec_plug",
+ .release = typec_plug_release,
+};
+
+/**
+ * typec_plug_register_altmode - Register USB Type-C Cable Plug Alternate Mode
+ * @plug: USB Type-C Cable Plug that supports the alternate mode
+ * @desc: Description of the alternate mode
+ *
+ * This routine is used to register each alternate mode individually that @plug
+ * has listed in response to Discover SVIDs command. The modes for a SVID that
+ * the plug lists in response to Discover Modes command need to be listed in an
+ * array in @desc.
+ *
+ * Returns handle to the alternate mode on success or ERR_PTR on failure.
+ */
+struct typec_altmode *
+typec_plug_register_altmode(struct typec_plug *plug,
+ const struct typec_altmode_desc *desc)
+{
+ return typec_register_altmode(&plug->dev, desc);
+}
+EXPORT_SYMBOL_GPL(typec_plug_register_altmode);
+
+/**
+ * typec_register_plug - Register a USB Type-C Cable Plug
+ * @cable: USB Type-C Cable with the plug
+ * @desc: Description of the cable plug
+ *
+ * Registers a device for USB Type-C Cable Plug described in @desc. A USB Type-C
+ * Cable Plug represents a plug with electronics in it that can response to USB
+ * Power Delivery SOP Prime or SOP Double Prime packages.
+ *
+ * Returns handle to the cable plug on success or ERR_PTR on failure.
+ */
+struct typec_plug *typec_register_plug(struct typec_cable *cable,
+ struct typec_plug_desc *desc)
+{
+ struct typec_plug *plug;
+ char name[8];
+ int ret;
+
+ plug = kzalloc(sizeof(*plug), GFP_KERNEL);
+ if (!plug)
+ return ERR_PTR(-ENOMEM);
+
+ sprintf(name, "plug%d", desc->index);
+
+ plug->index = desc->index;
+ plug->dev.class = typec_class;
+ plug->dev.parent = &cable->dev;
+ plug->dev.type = &typec_plug_dev_type;
+ dev_set_name(&plug->dev, "%s-%s", dev_name(cable->dev.parent), name);
+
+ ret = device_register(&plug->dev);
+ if (ret) {
+ dev_err(&cable->dev, "failed to register plug (%d)\n", ret);
+ put_device(&plug->dev);
+ return ERR_PTR(ret);
+ }
+
+ return plug;
+}
+EXPORT_SYMBOL_GPL(typec_register_plug);
+
+/**
+ * typec_unregister_plug - Unregister a USB Type-C Cable Plug
+ * @plug: The cable plug to be unregistered
+ *
+ * Unregister device created with typec_register_plug().
+ */
+void typec_unregister_plug(struct typec_plug *plug)
+{
+ if (!IS_ERR_OR_NULL(plug))
+ device_unregister(&plug->dev);
+}
+EXPORT_SYMBOL_GPL(typec_unregister_plug);
+
+/* Type-C Cables */
+
+static ssize_t
+type_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct typec_cable *cable = to_typec_cable(dev);
+
+ return sprintf(buf, "%s\n", cable->active ? "active" : "passive");
+}
+static DEVICE_ATTR_RO(type);
+
+static const char * const typec_plug_types[] = {
+ [USB_PLUG_NONE] = "unknown",
+ [USB_PLUG_TYPE_A] = "type-a",
+ [USB_PLUG_TYPE_B] = "type-b",
+ [USB_PLUG_TYPE_C] = "type-c",
+ [USB_PLUG_CAPTIVE] = "captive",
+};
+
+static ssize_t plug_type_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct typec_cable *cable = to_typec_cable(dev);
+
+ return sprintf(buf, "%s\n", typec_plug_types[cable->type]);
+}
+static DEVICE_ATTR_RO(plug_type);
+
+static struct attribute *typec_cable_attrs[] = {
+ &dev_attr_type.attr,
+ &dev_attr_plug_type.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(typec_cable);
+
+static void typec_cable_release(struct device *dev)
+{
+ struct typec_cable *cable = to_typec_cable(dev);
+
+ kfree(cable);
+}
+
+static const struct device_type typec_cable_dev_type = {
+ .name = "typec_cable",
+ .groups = typec_cable_groups,
+ .release = typec_cable_release,
+};
+
+/**
+ * typec_cable_set_identity - Report result from Discover Identity command
+ * @cable: The cable updated identity values
+ *
+ * This routine is used to report that the result of Discover Identity USB power
+ * delivery command has become available.
+ */
+int typec_cable_set_identity(struct typec_cable *cable)
+{
+ if (!cable->identity)
+ return -EINVAL;
+
+ typec_report_identity(&cable->dev);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(typec_cable_set_identity);
+
+/**
+ * typec_register_cable - Register a USB Type-C Cable
+ * @port: The USB Type-C Port the cable is connected to
+ * @desc: Description of the cable
+ *
+ * Registers a device for USB Type-C Cable described in @desc. The cable will be
+ * parent for the optional cable plug devises.
+ *
+ * Returns handle to the cable on success or ERR_PTR on failure.
+ */
+struct typec_cable *typec_register_cable(struct typec_port *port,
+ struct typec_cable_desc *desc)
+{
+ struct typec_cable *cable;
+ int ret;
+
+ cable = kzalloc(sizeof(*cable), GFP_KERNEL);
+ if (!cable)
+ return ERR_PTR(-ENOMEM);
+
+ cable->type = desc->type;
+ cable->active = desc->active;
+
+ if (desc->identity) {
+ /*
+ * Creating directory for the identity only if the driver is
+ * able to provide data to it.
+ */
+ cable->dev.groups = usb_pd_id_groups;
+ cable->identity = desc->identity;
+ }
+
+ cable->dev.class = typec_class;
+ cable->dev.parent = &port->dev;
+ cable->dev.type = &typec_cable_dev_type;
+ dev_set_name(&cable->dev, "%s-cable", dev_name(&port->dev));
+
+ ret = device_register(&cable->dev);
+ if (ret) {
+ dev_err(&port->dev, "failed to register cable (%d)\n", ret);
+ put_device(&cable->dev);
+ return ERR_PTR(ret);
+ }
+
+ return cable;
+}
+EXPORT_SYMBOL_GPL(typec_register_cable);
+
+/**
+ * typec_unregister_cable - Unregister a USB Type-C Cable
+ * @cable: The cable to be unregistered
+ *
+ * Unregister device created with typec_register_cable().
+ */
+void typec_unregister_cable(struct typec_cable *cable)
+{
+ if (!IS_ERR_OR_NULL(cable))
+ device_unregister(&cable->dev);
+}
+EXPORT_SYMBOL_GPL(typec_unregister_cable);
+
+/* ------------------------------------------------------------------------- */
+/* USB Type-C ports */
+
+static const char * const typec_roles[] = {
+ [TYPEC_SINK] = "sink",
+ [TYPEC_SOURCE] = "source",
+};
+
+static const char * const typec_data_roles[] = {
+ [TYPEC_DEVICE] = "device",
+ [TYPEC_HOST] = "host",
+};
+
+static const char * const typec_port_types[] = {
+ [TYPEC_PORT_SRC] = "source",
+ [TYPEC_PORT_SNK] = "sink",
+ [TYPEC_PORT_DRP] = "dual",
+};
+
+static const char * const typec_port_types_drp[] = {
+ [TYPEC_PORT_SRC] = "dual [source] sink",
+ [TYPEC_PORT_SNK] = "dual source [sink]",
+ [TYPEC_PORT_DRP] = "[dual] source sink",
+};
+
+static ssize_t
+preferred_role_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct typec_port *port = to_typec_port(dev);
+ int role;
+ int ret;
+
+ if (port->cap->type != TYPEC_PORT_DRP) {
+ dev_dbg(dev, "Preferred role only supported with DRP ports\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (!port->cap->try_role) {
+ dev_dbg(dev, "Setting preferred role not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ role = sysfs_match_string(typec_roles, buf);
+ if (role < 0) {
+ if (sysfs_streq(buf, "none"))
+ role = TYPEC_NO_PREFERRED_ROLE;
+ else
+ return -EINVAL;
+ }
+
+ ret = port->cap->try_role(port->cap, role);
+ if (ret)
+ return ret;
+
+ port->prefer_role = role;
+ return size;
+}
+
+static ssize_t
+preferred_role_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct typec_port *port = to_typec_port(dev);
+
+ if (port->cap->type != TYPEC_PORT_DRP)
+ return 0;
+
+ if (port->prefer_role < 0)
+ return 0;
+
+ return sprintf(buf, "%s\n", typec_roles[port->prefer_role]);
+}
+static DEVICE_ATTR_RW(preferred_role);
+
+static ssize_t data_role_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct typec_port *port = to_typec_port(dev);
+ int ret;
+
+ if (!port->cap->dr_set) {
+ dev_dbg(dev, "data role swapping not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ ret = sysfs_match_string(typec_data_roles, buf);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&port->port_type_lock);
+ if (port->cap->data != TYPEC_PORT_DRD) {
+ ret = -EOPNOTSUPP;
+ goto unlock_and_ret;
+ }
+
+ ret = port->cap->dr_set(port->cap, ret);
+ if (ret)
+ goto unlock_and_ret;
+
+ ret = size;
+unlock_and_ret:
+ mutex_unlock(&port->port_type_lock);
+ return ret;
+}
+
+static ssize_t data_role_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct typec_port *port = to_typec_port(dev);
+
+ if (port->cap->data == TYPEC_PORT_DRD)
+ return sprintf(buf, "%s\n", port->data_role == TYPEC_HOST ?
+ "[host] device" : "host [device]");
+
+ return sprintf(buf, "[%s]\n", typec_data_roles[port->data_role]);
+}
+static DEVICE_ATTR_RW(data_role);
+
+static ssize_t power_role_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct typec_port *port = to_typec_port(dev);
+ int ret;
+
+ if (!port->cap->pd_revision) {
+ dev_dbg(dev, "USB Power Delivery not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (!port->cap->pr_set) {
+ dev_dbg(dev, "power role swapping not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (port->pwr_opmode != TYPEC_PWR_MODE_PD) {
+ dev_dbg(dev, "partner unable to swap power role\n");
+ return -EIO;
+ }
+
+ ret = sysfs_match_string(typec_roles, buf);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&port->port_type_lock);
+ if (port->port_type != TYPEC_PORT_DRP) {
+ dev_dbg(dev, "port type fixed at \"%s\"",
+ typec_port_types[port->port_type]);
+ ret = -EOPNOTSUPP;
+ goto unlock_and_ret;
+ }
+
+ ret = port->cap->pr_set(port->cap, ret);
+ if (ret)
+ goto unlock_and_ret;
+
+ ret = size;
+unlock_and_ret:
+ mutex_unlock(&port->port_type_lock);
+ return ret;
+}
+
+static ssize_t power_role_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct typec_port *port = to_typec_port(dev);
+
+ if (port->cap->type == TYPEC_PORT_DRP)
+ return sprintf(buf, "%s\n", port->pwr_role == TYPEC_SOURCE ?
+ "[source] sink" : "source [sink]");
+
+ return sprintf(buf, "[%s]\n", typec_roles[port->pwr_role]);
+}
+static DEVICE_ATTR_RW(power_role);
+
+static ssize_t
+port_type_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct typec_port *port = to_typec_port(dev);
+ int ret;
+ enum typec_port_type type;
+
+ if (!port->cap->port_type_set || port->cap->type != TYPEC_PORT_DRP) {
+ dev_dbg(dev, "changing port type not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ ret = sysfs_match_string(typec_port_types, buf);
+ if (ret < 0)
+ return ret;
+
+ type = ret;
+ mutex_lock(&port->port_type_lock);
+
+ if (port->port_type == type) {
+ ret = size;
+ goto unlock_and_ret;
+ }
+
+ ret = port->cap->port_type_set(port->cap, type);
+ if (ret)
+ goto unlock_and_ret;
+
+ port->port_type = type;
+ ret = size;
+
+unlock_and_ret:
+ mutex_unlock(&port->port_type_lock);
+ return ret;
+}
+
+static ssize_t
+port_type_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct typec_port *port = to_typec_port(dev);
+
+ if (port->cap->type == TYPEC_PORT_DRP)
+ return sprintf(buf, "%s\n",
+ typec_port_types_drp[port->port_type]);
+
+ return sprintf(buf, "[%s]\n", typec_port_types[port->cap->type]);
+}
+static DEVICE_ATTR_RW(port_type);
+
+static const char * const typec_pwr_opmodes[] = {
+ [TYPEC_PWR_MODE_USB] = "default",
+ [TYPEC_PWR_MODE_1_5A] = "1.5A",
+ [TYPEC_PWR_MODE_3_0A] = "3.0A",
+ [TYPEC_PWR_MODE_PD] = "usb_power_delivery",
+};
+
+static ssize_t power_operation_mode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct typec_port *port = to_typec_port(dev);
+
+ return sprintf(buf, "%s\n", typec_pwr_opmodes[port->pwr_opmode]);
+}
+static DEVICE_ATTR_RO(power_operation_mode);
+
+static ssize_t vconn_source_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct typec_port *port = to_typec_port(dev);
+ bool source;
+ int ret;
+
+ if (!port->cap->pd_revision) {
+ dev_dbg(dev, "VCONN swap depends on USB Power Delivery\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (!port->cap->vconn_set) {
+ dev_dbg(dev, "VCONN swapping not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ ret = kstrtobool(buf, &source);
+ if (ret)
+ return ret;
+
+ ret = port->cap->vconn_set(port->cap, (enum typec_role)source);
+ if (ret)
+ return ret;
+
+ return size;
+}
+
+static ssize_t vconn_source_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct typec_port *port = to_typec_port(dev);
+
+ return sprintf(buf, "%s\n",
+ port->vconn_role == TYPEC_SOURCE ? "yes" : "no");
+}
+static DEVICE_ATTR_RW(vconn_source);
+
+static ssize_t supported_accessory_modes_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct typec_port *port = to_typec_port(dev);
+ ssize_t ret = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(port->cap->accessory); i++) {
+ if (port->cap->accessory[i])
+ ret += sprintf(buf + ret, "%s ",
+ typec_accessory_modes[port->cap->accessory[i]]);
+ }
+
+ if (!ret)
+ return sprintf(buf, "none\n");
+
+ buf[ret - 1] = '\n';
+
+ return ret;
+}
+static DEVICE_ATTR_RO(supported_accessory_modes);
+
+static ssize_t usb_typec_revision_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct typec_port *port = to_typec_port(dev);
+ u16 rev = port->cap->revision;
+
+ return sprintf(buf, "%d.%d\n", (rev >> 8) & 0xff, (rev >> 4) & 0xf);
+}
+static DEVICE_ATTR_RO(usb_typec_revision);
+
+static ssize_t usb_power_delivery_revision_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct typec_port *p = to_typec_port(dev);
+
+ return sprintf(buf, "%d\n", (p->cap->pd_revision >> 8) & 0xff);
+}
+static DEVICE_ATTR_RO(usb_power_delivery_revision);
+
+static struct attribute *typec_attrs[] = {
+ &dev_attr_data_role.attr,
+ &dev_attr_power_operation_mode.attr,
+ &dev_attr_power_role.attr,
+ &dev_attr_preferred_role.attr,
+ &dev_attr_supported_accessory_modes.attr,
+ &dev_attr_usb_power_delivery_revision.attr,
+ &dev_attr_usb_typec_revision.attr,
+ &dev_attr_vconn_source.attr,
+ &dev_attr_port_type.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(typec);
+
+static int typec_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ int ret;
+
+ ret = add_uevent_var(env, "TYPEC_PORT=%s", dev_name(dev));
+ if (ret)
+ dev_err(dev, "failed to add uevent TYPEC_PORT\n");
+
+ return ret;
+}
+
+static void typec_release(struct device *dev)
+{
+ struct typec_port *port = to_typec_port(dev);
+
+ ida_simple_remove(&typec_index_ida, port->id);
+ typec_switch_put(port->sw);
+ typec_mux_put(port->mux);
+ kfree(port);
+}
+
+static const struct device_type typec_port_dev_type = {
+ .name = "typec_port",
+ .groups = typec_groups,
+ .uevent = typec_uevent,
+ .release = typec_release,
+};
+
+/* --------------------------------------- */
+/* Driver callbacks to report role updates */
+
+/**
+ * typec_set_data_role - Report data role change
+ * @port: The USB Type-C Port where the role was changed
+ * @role: The new data role
+ *
+ * This routine is used by the port drivers to report data role changes.
+ */
+void typec_set_data_role(struct typec_port *port, enum typec_data_role role)
+{
+ if (port->data_role == role)
+ return;
+
+ port->data_role = role;
+ sysfs_notify(&port->dev.kobj, NULL, "data_role");
+ kobject_uevent(&port->dev.kobj, KOBJ_CHANGE);
+}
+EXPORT_SYMBOL_GPL(typec_set_data_role);
+
+/**
+ * typec_set_pwr_role - Report power role change
+ * @port: The USB Type-C Port where the role was changed
+ * @role: The new data role
+ *
+ * This routine is used by the port drivers to report power role changes.
+ */
+void typec_set_pwr_role(struct typec_port *port, enum typec_role role)
+{
+ if (port->pwr_role == role)
+ return;
+
+ port->pwr_role = role;
+ sysfs_notify(&port->dev.kobj, NULL, "power_role");
+ kobject_uevent(&port->dev.kobj, KOBJ_CHANGE);
+}
+EXPORT_SYMBOL_GPL(typec_set_pwr_role);
+
+/**
+ * typec_set_pwr_role - Report VCONN source change
+ * @port: The USB Type-C Port which VCONN role changed
+ * @role: Source when @port is sourcing VCONN, or Sink when it's not
+ *
+ * This routine is used by the port drivers to report if the VCONN source is
+ * changes.
+ */
+void typec_set_vconn_role(struct typec_port *port, enum typec_role role)
+{
+ if (port->vconn_role == role)
+ return;
+
+ port->vconn_role = role;
+ sysfs_notify(&port->dev.kobj, NULL, "vconn_source");
+ kobject_uevent(&port->dev.kobj, KOBJ_CHANGE);
+}
+EXPORT_SYMBOL_GPL(typec_set_vconn_role);
+
+static int partner_match(struct device *dev, void *data)
+{
+ return is_typec_partner(dev);
+}
+
+/**
+ * typec_set_pwr_opmode - Report changed power operation mode
+ * @port: The USB Type-C Port where the mode was changed
+ * @opmode: New power operation mode
+ *
+ * This routine is used by the port drivers to report changed power operation
+ * mode in @port. The modes are USB (default), 1.5A, 3.0A as defined in USB
+ * Type-C specification, and "USB Power Delivery" when the power levels are
+ * negotiated with methods defined in USB Power Delivery specification.
+ */
+void typec_set_pwr_opmode(struct typec_port *port,
+ enum typec_pwr_opmode opmode)
+{
+ struct device *partner_dev;
+
+ if (port->pwr_opmode == opmode)
+ return;
+
+ port->pwr_opmode = opmode;
+ sysfs_notify(&port->dev.kobj, NULL, "power_operation_mode");
+ kobject_uevent(&port->dev.kobj, KOBJ_CHANGE);
+
+ partner_dev = device_find_child(&port->dev, NULL, partner_match);
+ if (partner_dev) {
+ struct typec_partner *partner = to_typec_partner(partner_dev);
+
+ if (opmode == TYPEC_PWR_MODE_PD && !partner->usb_pd) {
+ partner->usb_pd = 1;
+ sysfs_notify(&partner_dev->kobj, NULL,
+ "supports_usb_power_delivery");
+ }
+ put_device(partner_dev);
+ }
+}
+EXPORT_SYMBOL_GPL(typec_set_pwr_opmode);
+
+/* ------------------------------------------ */
+/* API for Multiplexer/DeMultiplexer Switches */
+
+/**
+ * typec_set_orientation - Set USB Type-C cable plug orientation
+ * @port: USB Type-C Port
+ * @orientation: USB Type-C cable plug orientation
+ *
+ * Set cable plug orientation for @port.
+ */
+int typec_set_orientation(struct typec_port *port,
+ enum typec_orientation orientation)
+{
+ int ret;
+
+ if (port->sw) {
+ ret = port->sw->set(port->sw, orientation);
+ if (ret)
+ return ret;
+ }
+
+ port->orientation = orientation;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(typec_set_orientation);
+
+/**
+ * typec_set_mode - Set mode of operation for USB Type-C connector
+ * @port: USB Type-C port for the connector
+ * @mode: Operation mode for the connector
+ *
+ * Set mode @mode for @port. This function will configure the muxes needed to
+ * enter @mode.
+ */
+int typec_set_mode(struct typec_port *port, int mode)
+{
+ return port->mux ? port->mux->set(port->mux, mode) : 0;
+}
+EXPORT_SYMBOL_GPL(typec_set_mode);
+
+/* --------------------------------------- */
+
+/**
+ * typec_port_register_altmode - Register USB Type-C Port Alternate Mode
+ * @port: USB Type-C Port that supports the alternate mode
+ * @desc: Description of the alternate mode
+ *
+ * This routine is used to register an alternate mode that @port is capable of
+ * supporting.
+ *
+ * Returns handle to the alternate mode on success or ERR_PTR on failure.
+ */
+struct typec_altmode *
+typec_port_register_altmode(struct typec_port *port,
+ const struct typec_altmode_desc *desc)
+{
+ return typec_register_altmode(&port->dev, desc);
+}
+EXPORT_SYMBOL_GPL(typec_port_register_altmode);
+
+/**
+ * typec_register_port - Register a USB Type-C Port
+ * @parent: Parent device
+ * @cap: Description of the port
+ *
+ * Registers a device for USB Type-C Port described in @cap.
+ *
+ * Returns handle to the port on success or ERR_PTR on failure.
+ */
+struct typec_port *typec_register_port(struct device *parent,
+ const struct typec_capability *cap)
+{
+ struct typec_port *port;
+ int ret;
+ int id;
+
+ port = kzalloc(sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return ERR_PTR(-ENOMEM);
+
+ id = ida_simple_get(&typec_index_ida, 0, 0, GFP_KERNEL);
+ if (id < 0) {
+ kfree(port);
+ return ERR_PTR(id);
+ }
+
+ port->sw = typec_switch_get(cap->fwnode ? &port->dev : parent);
+ if (IS_ERR(port->sw)) {
+ ret = PTR_ERR(port->sw);
+ goto err_switch;
+ }
+
+ port->mux = typec_mux_get(cap->fwnode ? &port->dev : parent);
+ if (IS_ERR(port->mux)) {
+ ret = PTR_ERR(port->mux);
+ goto err_mux;
+ }
+
+ switch (cap->type) {
+ case TYPEC_PORT_SRC:
+ port->pwr_role = TYPEC_SOURCE;
+ port->vconn_role = TYPEC_SOURCE;
+ break;
+ case TYPEC_PORT_SNK:
+ port->pwr_role = TYPEC_SINK;
+ port->vconn_role = TYPEC_SINK;
+ break;
+ case TYPEC_PORT_DRP:
+ if (cap->prefer_role != TYPEC_NO_PREFERRED_ROLE)
+ port->pwr_role = cap->prefer_role;
+ else
+ port->pwr_role = TYPEC_SINK;
+ break;
+ }
+
+ switch (cap->data) {
+ case TYPEC_PORT_DFP:
+ port->data_role = TYPEC_HOST;
+ break;
+ case TYPEC_PORT_UFP:
+ port->data_role = TYPEC_DEVICE;
+ break;
+ case TYPEC_PORT_DRD:
+ if (cap->prefer_role == TYPEC_SOURCE)
+ port->data_role = TYPEC_HOST;
+ else
+ port->data_role = TYPEC_DEVICE;
+ break;
+ }
+
+ port->id = id;
+ port->cap = cap;
+ port->port_type = cap->type;
+ mutex_init(&port->port_type_lock);
+ port->prefer_role = cap->prefer_role;
+
+ port->dev.class = typec_class;
+ port->dev.parent = parent;
+ port->dev.fwnode = cap->fwnode;
+ port->dev.type = &typec_port_dev_type;
+ dev_set_name(&port->dev, "port%d", id);
+
+ ret = device_register(&port->dev);
+ if (ret) {
+ dev_err(parent, "failed to register port (%d)\n", ret);
+ put_device(&port->dev);
+ return ERR_PTR(ret);
+ }
+
+ return port;
+
+err_mux:
+ typec_switch_put(port->sw);
+
+err_switch:
+ ida_simple_remove(&typec_index_ida, port->id);
+ kfree(port);
+
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(typec_register_port);
+
+/**
+ * typec_unregister_port - Unregister a USB Type-C Port
+ * @port: The port to be unregistered
+ *
+ * Unregister device created with typec_register_port().
+ */
+void typec_unregister_port(struct typec_port *port)
+{
+ if (!IS_ERR_OR_NULL(port))
+ device_unregister(&port->dev);
+}
+EXPORT_SYMBOL_GPL(typec_unregister_port);
+
+static int __init typec_init(void)
+{
+ typec_class = class_create(THIS_MODULE, "typec");
+ return PTR_ERR_OR_ZERO(typec_class);
+}
+subsys_initcall(typec_init);
+
+static void __exit typec_exit(void)
+{
+ class_destroy(typec_class);
+ ida_destroy(&typec_index_ida);
+}
+module_exit(typec_exit);
+
+MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("USB Type-C Connector Class");
diff --git a/drivers/usb/typec/fusb302/fusb302.c b/drivers/usb/typec/fusb302/fusb302.c
index dcd8ef085b30..703617129067 100644
--- a/drivers/usb/typec/fusb302/fusb302.c
+++ b/drivers/usb/typec/fusb302/fusb302.c
@@ -199,7 +199,7 @@ static void fusb302_log(struct fusb302_chip *chip, const char *fmt, ...)
va_end(args);
}
-static int fusb302_seq_show(struct seq_file *s, void *v)
+static int fusb302_debug_show(struct seq_file *s, void *v)
{
struct fusb302_chip *chip = (struct fusb302_chip *)s->private;
int tail;
@@ -216,18 +216,7 @@ static int fusb302_seq_show(struct seq_file *s, void *v)
return 0;
}
-
-static int fusb302_debug_open(struct inode *inode, struct file *file)
-{
- return single_open(file, fusb302_seq_show, inode->i_private);
-}
-
-static const struct file_operations fusb302_debug_operations = {
- .open = fusb302_debug_open,
- .llseek = seq_lseek,
- .read = seq_read,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(fusb302_debug);
static struct dentry *rootdir;
@@ -242,7 +231,7 @@ static int fusb302_debugfs_init(struct fusb302_chip *chip)
chip->dentry = debugfs_create_file(dev_name(chip->dev),
S_IFREG | 0444, rootdir,
- chip, &fusb302_debug_operations);
+ chip, &fusb302_debug_fops);
return 0;
}
@@ -1230,6 +1219,7 @@ static const struct tcpc_config fusb302_tcpc_config = {
.max_snk_mw = 15000,
.operating_snk_mw = 2500,
.type = TYPEC_PORT_DRP,
+ .data = TYPEC_PORT_DRD,
.default_role = TYPEC_SINK,
.alt_modes = NULL,
};
@@ -1249,7 +1239,6 @@ static void init_tcpc_dev(struct tcpc_dev *fusb302_tcpc_dev)
fusb302_tcpc_dev->set_roles = tcpm_set_roles;
fusb302_tcpc_dev->start_drp_toggling = tcpm_start_drp_toggling;
fusb302_tcpc_dev->pd_transmit = tcpm_pd_transmit;
- fusb302_tcpc_dev->mux = NULL;
}
static const char * const cc_polarity_name[] = {
diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c
new file mode 100644
index 000000000000..f89093bd7185
--- /dev/null
+++ b/drivers/usb/typec/mux.c
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * USB Type-C Multiplexer/DeMultiplexer Switch support
+ *
+ * Copyright (C) 2018 Intel Corporation
+ * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+ * Hans de Goede <hdegoede@redhat.com>
+ */
+
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/usb/typec_mux.h>
+
+static DEFINE_MUTEX(switch_lock);
+static DEFINE_MUTEX(mux_lock);
+static LIST_HEAD(switch_list);
+static LIST_HEAD(mux_list);
+
+static void *typec_switch_match(struct device_connection *con, int ep,
+ void *data)
+{
+ struct typec_switch *sw;
+
+ list_for_each_entry(sw, &switch_list, entry)
+ if (!strcmp(con->endpoint[ep], dev_name(sw->dev)))
+ return sw;
+
+ /*
+ * We only get called if a connection was found, tell the caller to
+ * wait for the switch to show up.
+ */
+ return ERR_PTR(-EPROBE_DEFER);
+}
+
+/**
+ * typec_switch_get - Find USB Type-C orientation switch
+ * @dev: The caller device
+ *
+ * Finds a switch linked with @dev. Returns a reference to the switch on
+ * success, NULL if no matching connection was found, or
+ * ERR_PTR(-EPROBE_DEFER) when a connection was found but the switch
+ * has not been enumerated yet.
+ */
+struct typec_switch *typec_switch_get(struct device *dev)
+{
+ struct typec_switch *sw;
+
+ mutex_lock(&switch_lock);
+ sw = device_connection_find_match(dev, "typec-switch", NULL,
+ typec_switch_match);
+ if (!IS_ERR_OR_NULL(sw))
+ get_device(sw->dev);
+ mutex_unlock(&switch_lock);
+
+ return sw;
+}
+EXPORT_SYMBOL_GPL(typec_switch_get);
+
+/**
+ * typec_put_switch - Release USB Type-C orientation switch
+ * @sw: USB Type-C orientation switch
+ *
+ * Decrement reference count for @sw.
+ */
+void typec_switch_put(struct typec_switch *sw)
+{
+ if (!IS_ERR_OR_NULL(sw))
+ put_device(sw->dev);
+}
+EXPORT_SYMBOL_GPL(typec_switch_put);
+
+/**
+ * typec_switch_register - Register USB Type-C orientation switch
+ * @sw: USB Type-C orientation switch
+ *
+ * This function registers a switch that can be used for routing the correct
+ * data pairs depending on the cable plug orientation from the USB Type-C
+ * connector to the USB controllers. USB Type-C plugs can be inserted
+ * right-side-up or upside-down.
+ */
+int typec_switch_register(struct typec_switch *sw)
+{
+ mutex_lock(&switch_lock);
+ list_add_tail(&sw->entry, &switch_list);
+ mutex_unlock(&switch_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(typec_switch_register);
+
+/**
+ * typec_switch_unregister - Unregister USB Type-C orientation switch
+ * @sw: USB Type-C orientation switch
+ *
+ * Unregister switch that was registered with typec_switch_register().
+ */
+void typec_switch_unregister(struct typec_switch *sw)
+{
+ mutex_lock(&switch_lock);
+ list_del(&sw->entry);
+ mutex_unlock(&switch_lock);
+}
+EXPORT_SYMBOL_GPL(typec_switch_unregister);
+
+/* ------------------------------------------------------------------------- */
+
+static void *typec_mux_match(struct device_connection *con, int ep, void *data)
+{
+ struct typec_mux *mux;
+
+ list_for_each_entry(mux, &mux_list, entry)
+ if (!strcmp(con->endpoint[ep], dev_name(mux->dev)))
+ return mux;
+
+ /*
+ * We only get called if a connection was found, tell the caller to
+ * wait for the switch to show up.
+ */
+ return ERR_PTR(-EPROBE_DEFER);
+}
+
+/**
+ * typec_mux_get - Find USB Type-C Multiplexer
+ * @dev: The caller device
+ *
+ * Finds a mux linked to the caller. This function is primarily meant for the
+ * Type-C drivers. Returns a reference to the mux on success, NULL if no
+ * matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a connection
+ * was found but the mux has not been enumerated yet.
+ */
+struct typec_mux *typec_mux_get(struct device *dev)
+{
+ struct typec_mux *mux;
+
+ mutex_lock(&mux_lock);
+ mux = device_connection_find_match(dev, "typec-mux", NULL,
+ typec_mux_match);
+ if (!IS_ERR_OR_NULL(mux))
+ get_device(mux->dev);
+ mutex_unlock(&mux_lock);
+
+ return mux;
+}
+EXPORT_SYMBOL_GPL(typec_mux_get);
+
+/**
+ * typec_mux_put - Release handle to a Multiplexer
+ * @mux: USB Type-C Connector Multiplexer/DeMultiplexer
+ *
+ * Decrements reference count for @mux.
+ */
+void typec_mux_put(struct typec_mux *mux)
+{
+ if (!IS_ERR_OR_NULL(mux))
+ put_device(mux->dev);
+}
+EXPORT_SYMBOL_GPL(typec_mux_put);
+
+/**
+ * typec_mux_register - Register Multiplexer routing USB Type-C pins
+ * @mux: USB Type-C Connector Multiplexer/DeMultiplexer
+ *
+ * USB Type-C connectors can be used for alternate modes of operation besides
+ * USB when Accessory/Alternate Modes are supported. With some of those modes,
+ * the pins on the connector need to be reconfigured. This function registers
+ * multiplexer switches routing the pins on the connector.
+ */
+int typec_mux_register(struct typec_mux *mux)
+{
+ mutex_lock(&mux_lock);
+ list_add_tail(&mux->entry, &mux_list);
+ mutex_unlock(&mux_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(typec_mux_register);
+
+/**
+ * typec_mux_unregister - Unregister Multiplexer Switch
+ * @sw: USB Type-C Connector Multiplexer/DeMultiplexer
+ *
+ * Unregister mux that was registered with typec_mux_register().
+ */
+void typec_mux_unregister(struct typec_mux *mux)
+{
+ mutex_lock(&mux_lock);
+ list_del(&mux->entry);
+ mutex_unlock(&mux_lock);
+}
+EXPORT_SYMBOL_GPL(typec_mux_unregister);
diff --git a/drivers/usb/typec/mux/Kconfig b/drivers/usb/typec/mux/Kconfig
new file mode 100644
index 000000000000..9a954d2b8d8f
--- /dev/null
+++ b/drivers/usb/typec/mux/Kconfig
@@ -0,0 +1,10 @@
+menu "USB Type-C Multiplexer/DeMultiplexer Switch support"
+
+config TYPEC_MUX_PI3USB30532
+ tristate "Pericom PI3USB30532 Type-C cross switch driver"
+ depends on I2C
+ help
+ Say Y or M if your system has a Pericom PI3USB30532 Type-C cross
+ switch / mux chip found on some devices with a Type-C port.
+
+endmenu
diff --git a/drivers/usb/typec/mux/Makefile b/drivers/usb/typec/mux/Makefile
new file mode 100644
index 000000000000..1332e469b8a0
--- /dev/null
+++ b/drivers/usb/typec/mux/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_TYPEC_MUX_PI3USB30532) += pi3usb30532.o
diff --git a/drivers/usb/typec/mux/pi3usb30532.c b/drivers/usb/typec/mux/pi3usb30532.c
new file mode 100644
index 000000000000..b0e88db60ecf
--- /dev/null
+++ b/drivers/usb/typec/mux/pi3usb30532.c
@@ -0,0 +1,178 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Pericom PI3USB30532 Type-C cross switch / mux driver
+ *
+ * Copyright (c) 2017-2018 Hans de Goede <hdegoede@redhat.com>
+ */
+
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/usb/tcpm.h>
+#include <linux/usb/typec_mux.h>
+
+#define PI3USB30532_CONF 0x00
+
+#define PI3USB30532_CONF_OPEN 0x00
+#define PI3USB30532_CONF_SWAP 0x01
+#define PI3USB30532_CONF_4LANE_DP 0x02
+#define PI3USB30532_CONF_USB3 0x04
+#define PI3USB30532_CONF_USB3_AND_2LANE_DP 0x06
+
+struct pi3usb30532 {
+ struct i2c_client *client;
+ struct mutex lock; /* protects the cached conf register */
+ struct typec_switch sw;
+ struct typec_mux mux;
+ u8 conf;
+};
+
+static int pi3usb30532_set_conf(struct pi3usb30532 *pi, u8 new_conf)
+{
+ int ret = 0;
+
+ if (pi->conf == new_conf)
+ return 0;
+
+ ret = i2c_smbus_write_byte_data(pi->client, PI3USB30532_CONF, new_conf);
+ if (ret) {
+ dev_err(&pi->client->dev, "Error writing conf: %d\n", ret);
+ return ret;
+ }
+
+ pi->conf = new_conf;
+ return 0;
+}
+
+static int pi3usb30532_sw_set(struct typec_switch *sw,
+ enum typec_orientation orientation)
+{
+ struct pi3usb30532 *pi = container_of(sw, struct pi3usb30532, sw);
+ u8 new_conf;
+ int ret;
+
+ mutex_lock(&pi->lock);
+ new_conf = pi->conf;
+
+ switch (orientation) {
+ case TYPEC_ORIENTATION_NONE:
+ new_conf = PI3USB30532_CONF_OPEN;
+ break;
+ case TYPEC_ORIENTATION_NORMAL:
+ new_conf &= ~PI3USB30532_CONF_SWAP;
+ break;
+ case TYPEC_ORIENTATION_REVERSE:
+ new_conf |= PI3USB30532_CONF_SWAP;
+ break;
+ }
+
+ ret = pi3usb30532_set_conf(pi, new_conf);
+ mutex_unlock(&pi->lock);
+
+ return ret;
+}
+
+static int pi3usb30532_mux_set(struct typec_mux *mux, int state)
+{
+ struct pi3usb30532 *pi = container_of(mux, struct pi3usb30532, mux);
+ u8 new_conf;
+ int ret;
+
+ mutex_lock(&pi->lock);
+ new_conf = pi->conf;
+
+ switch (state) {
+ case TYPEC_MUX_NONE:
+ new_conf = PI3USB30532_CONF_OPEN;
+ break;
+ case TYPEC_MUX_USB:
+ new_conf = (new_conf & PI3USB30532_CONF_SWAP) |
+ PI3USB30532_CONF_USB3;
+ break;
+ case TYPEC_MUX_DP:
+ new_conf = (new_conf & PI3USB30532_CONF_SWAP) |
+ PI3USB30532_CONF_4LANE_DP;
+ break;
+ case TYPEC_MUX_DOCK:
+ new_conf = (new_conf & PI3USB30532_CONF_SWAP) |
+ PI3USB30532_CONF_USB3_AND_2LANE_DP;
+ break;
+ }
+
+ ret = pi3usb30532_set_conf(pi, new_conf);
+ mutex_unlock(&pi->lock);
+
+ return ret;
+}
+
+static int pi3usb30532_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct pi3usb30532 *pi;
+ int ret;
+
+ pi = devm_kzalloc(dev, sizeof(*pi), GFP_KERNEL);
+ if (!pi)
+ return -ENOMEM;
+
+ pi->client = client;
+ pi->sw.dev = dev;
+ pi->sw.set = pi3usb30532_sw_set;
+ pi->mux.dev = dev;
+ pi->mux.set = pi3usb30532_mux_set;
+ mutex_init(&pi->lock);
+
+ ret = i2c_smbus_read_byte_data(client, PI3USB30532_CONF);
+ if (ret < 0) {
+ dev_err(dev, "Error reading config register %d\n", ret);
+ return ret;
+ }
+ pi->conf = ret;
+
+ ret = typec_switch_register(&pi->sw);
+ if (ret) {
+ dev_err(dev, "Error registering typec switch: %d\n", ret);
+ return ret;
+ }
+
+ ret = typec_mux_register(&pi->mux);
+ if (ret) {
+ typec_switch_unregister(&pi->sw);
+ dev_err(dev, "Error registering typec mux: %d\n", ret);
+ return ret;
+ }
+
+ i2c_set_clientdata(client, pi);
+ return 0;
+}
+
+static int pi3usb30532_remove(struct i2c_client *client)
+{
+ struct pi3usb30532 *pi = i2c_get_clientdata(client);
+
+ typec_mux_unregister(&pi->mux);
+ typec_switch_unregister(&pi->sw);
+ return 0;
+}
+
+static const struct i2c_device_id pi3usb30532_table[] = {
+ { "pi3usb30532" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pi3usb30532_table);
+
+static struct i2c_driver pi3usb30532_driver = {
+ .driver = {
+ .name = "pi3usb30532",
+ },
+ .probe_new = pi3usb30532_probe,
+ .remove = pi3usb30532_remove,
+ .id_table = pi3usb30532_table,
+};
+
+module_i2c_driver(pi3usb30532_driver);
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Pericom PI3USB30532 Type-C mux driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm.c
index 8b637a4b474b..677d12138dbd 100644
--- a/drivers/usb/typec/tcpm.c
+++ b/drivers/usb/typec/tcpm.c
@@ -20,6 +20,7 @@
#include <linux/usb/pd.h>
#include <linux/usb/pd_bdo.h>
#include <linux/usb/pd_vdo.h>
+#include <linux/usb/role.h>
#include <linux/usb/tcpm.h>
#include <linux/usb/typec.h>
#include <linux/workqueue.h>
@@ -176,6 +177,7 @@ struct tcpm_port {
struct typec_port *typec_port;
struct tcpc_dev *tcpc;
+ struct usb_role_switch *role_sw;
enum typec_role vconn_role;
enum typec_role pwr_role;
@@ -345,7 +347,7 @@ static enum tcpm_state tcpm_default_state(struct tcpm_port *port)
else if (port->tcpc->config->default_role == TYPEC_SINK)
return SNK_UNATTACHED;
/* Fall through to return SRC_UNATTACHED */
- } else if (port->port_type == TYPEC_PORT_UFP) {
+ } else if (port->port_type == TYPEC_PORT_SNK) {
return SNK_UNATTACHED;
}
return SRC_UNATTACHED;
@@ -503,7 +505,7 @@ static void tcpm_log_source_caps(struct tcpm_port *port)
}
}
-static int tcpm_seq_show(struct seq_file *s, void *v)
+static int tcpm_debug_show(struct seq_file *s, void *v)
{
struct tcpm_port *port = (struct tcpm_port *)s->private;
int tail;
@@ -520,18 +522,7 @@ static int tcpm_seq_show(struct seq_file *s, void *v)
return 0;
}
-
-static int tcpm_debug_open(struct inode *inode, struct file *file)
-{
- return single_open(file, tcpm_seq_show, inode->i_private);
-}
-
-static const struct file_operations tcpm_debug_operations = {
- .open = tcpm_debug_open,
- .llseek = seq_lseek,
- .read = seq_read,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(tcpm_debug);
static struct dentry *rootdir;
@@ -547,7 +538,7 @@ static int tcpm_debugfs_init(struct tcpm_port *port)
port->dentry = debugfs_create_file(dev_name(port->dev),
S_IFREG | 0444, rootdir,
- port, &tcpm_debug_operations);
+ port, &tcpm_debug_fops);
return 0;
}
@@ -615,18 +606,25 @@ void tcpm_pd_transmit_complete(struct tcpm_port *port,
EXPORT_SYMBOL_GPL(tcpm_pd_transmit_complete);
static int tcpm_mux_set(struct tcpm_port *port, enum tcpc_mux_mode mode,
- enum tcpc_usb_switch config)
+ enum usb_role usb_role,
+ enum typec_orientation orientation)
{
- int ret = 0;
+ int ret;
- tcpm_log(port, "Requesting mux mode %d, config %d, polarity %d",
- mode, config, port->polarity);
+ tcpm_log(port, "Requesting mux mode %d, usb-role %d, orientation %d",
+ mode, usb_role, orientation);
- if (port->tcpc->mux)
- ret = port->tcpc->mux->set(port->tcpc->mux, mode, config,
- port->polarity);
+ ret = typec_set_orientation(port->typec_port, orientation);
+ if (ret)
+ return ret;
- return ret;
+ if (port->role_sw) {
+ ret = usb_role_switch_set_role(port->role_sw, usb_role);
+ if (ret)
+ return ret;
+ }
+
+ return typec_set_mode(port->typec_port, mode);
}
static int tcpm_set_polarity(struct tcpm_port *port,
@@ -739,14 +737,21 @@ static int tcpm_set_attached_state(struct tcpm_port *port, bool attached)
static int tcpm_set_roles(struct tcpm_port *port, bool attached,
enum typec_role role, enum typec_data_role data)
{
+ enum typec_orientation orientation;
+ enum usb_role usb_role;
int ret;
+ if (port->polarity == TYPEC_POLARITY_CC1)
+ orientation = TYPEC_ORIENTATION_NORMAL;
+ else
+ orientation = TYPEC_ORIENTATION_REVERSE;
+
if (data == TYPEC_HOST)
- ret = tcpm_mux_set(port, TYPEC_MUX_USB,
- TCPC_USB_SWITCH_CONNECT);
+ usb_role = USB_ROLE_HOST;
else
- ret = tcpm_mux_set(port, TYPEC_MUX_NONE,
- TCPC_USB_SWITCH_DISCONNECT);
+ usb_role = USB_ROLE_DEVICE;
+
+ ret = tcpm_mux_set(port, TYPEC_MUX_USB, usb_role, orientation);
if (ret < 0)
return ret;
@@ -1041,7 +1046,7 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt,
break;
case CMDT_RSP_ACK:
/* silently drop message if we are not connected */
- if (!port->partner)
+ if (IS_ERR_OR_NULL(port->partner))
break;
switch (cmd) {
@@ -2039,7 +2044,8 @@ out_disable_vconn:
out_disable_pd:
port->tcpc->set_pd_rx(port->tcpc, false);
out_disable_mux:
- tcpm_mux_set(port, TYPEC_MUX_NONE, TCPC_USB_SWITCH_DISCONNECT);
+ tcpm_mux_set(port, TYPEC_MUX_NONE, USB_ROLE_NONE,
+ TYPEC_ORIENTATION_NONE);
return ret;
}
@@ -2083,6 +2089,8 @@ static void tcpm_reset_port(struct tcpm_port *port)
tcpm_init_vconn(port);
tcpm_set_current_limit(port, 0, 0);
tcpm_set_polarity(port, TYPEC_POLARITY_CC1);
+ tcpm_mux_set(port, TYPEC_MUX_NONE, USB_ROLE_NONE,
+ TYPEC_ORIENTATION_NONE);
tcpm_set_attached_state(port, false);
port->try_src_count = 0;
port->try_snk_count = 0;
@@ -2133,8 +2141,6 @@ static int tcpm_snk_attach(struct tcpm_port *port)
static void tcpm_snk_detach(struct tcpm_port *port)
{
tcpm_detach(port);
-
- /* XXX: (Dis)connect SuperSpeed mux? */
}
static int tcpm_acc_attach(struct tcpm_port *port)
@@ -2190,7 +2196,7 @@ static inline enum tcpm_state unattached_state(struct tcpm_port *port)
return SRC_UNATTACHED;
else
return SNK_UNATTACHED;
- } else if (port->port_type == TYPEC_PORT_DFP) {
+ } else if (port->port_type == TYPEC_PORT_SRC) {
return SRC_UNATTACHED;
}
@@ -3480,11 +3486,11 @@ static int tcpm_port_type_set(const struct typec_capability *cap,
if (!port->connected) {
tcpm_set_state(port, PORT_RESET, 0);
- } else if (type == TYPEC_PORT_UFP) {
+ } else if (type == TYPEC_PORT_SNK) {
if (!(port->pwr_role == TYPEC_SINK &&
port->data_role == TYPEC_DEVICE))
tcpm_set_state(port, PORT_RESET, 0);
- } else if (type == TYPEC_PORT_DFP) {
+ } else if (type == TYPEC_PORT_SRC) {
if (!(port->pwr_role == TYPEC_SOURCE &&
port->data_role == TYPEC_HOST))
tcpm_set_state(port, PORT_RESET, 0);
@@ -3652,6 +3658,7 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
port->typec_caps.prefer_role = tcpc->config->default_role;
port->typec_caps.type = tcpc->config->type;
+ port->typec_caps.data = tcpc->config->data;
port->typec_caps.revision = 0x0120; /* Type-C spec release 1.2 */
port->typec_caps.pd_revision = 0x0200; /* USB-PD spec release 2.0 */
port->typec_caps.dr_set = tcpm_dr_set;
@@ -3663,9 +3670,15 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
port->partner_desc.identity = &port->partner_ident;
port->port_type = tcpc->config->type;
+ port->role_sw = usb_role_switch_get(port->dev);
+ if (IS_ERR(port->role_sw)) {
+ err = PTR_ERR(port->role_sw);
+ goto out_destroy_wq;
+ }
+
port->typec_port = typec_register_port(port->dev, &port->typec_caps);
- if (!port->typec_port) {
- err = -ENOMEM;
+ if (IS_ERR(port->typec_port)) {
+ err = PTR_ERR(port->typec_port);
goto out_destroy_wq;
}
@@ -3674,15 +3687,17 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
i = 0;
while (paltmode->svid && i < ARRAY_SIZE(port->port_altmode)) {
- port->port_altmode[i] =
- typec_port_register_altmode(port->typec_port,
- paltmode);
- if (!port->port_altmode[i]) {
+ struct typec_altmode *alt;
+
+ alt = typec_port_register_altmode(port->typec_port,
+ paltmode);
+ if (IS_ERR(alt)) {
tcpm_log(port,
"%s: failed to register port alternate mode 0x%x",
dev_name(dev), paltmode->svid);
break;
}
+ port->port_altmode[i] = alt;
i++;
paltmode++;
}
@@ -3696,6 +3711,7 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
return port;
out_destroy_wq:
+ usb_role_switch_put(port->role_sw);
destroy_workqueue(port->wq);
return ERR_PTR(err);
}
diff --git a/drivers/usb/typec/tps6598x.c b/drivers/usb/typec/tps6598x.c
index 2719f5d382f7..8b8406867c02 100644
--- a/drivers/usb/typec/tps6598x.c
+++ b/drivers/usb/typec/tps6598x.c
@@ -158,15 +158,15 @@ static int tps6598x_connect(struct tps6598x *tps, u32 status)
desc.identity = &tps->partner_identity;
}
- tps->partner = typec_register_partner(tps->port, &desc);
- if (!tps->partner)
- return -ENODEV;
-
typec_set_pwr_opmode(tps->port, mode);
typec_set_pwr_role(tps->port, TPS_STATUS_PORTROLE(status));
typec_set_vconn_role(tps->port, TPS_STATUS_VCONN(status));
typec_set_data_role(tps->port, TPS_STATUS_DATAROLE(status));
+ tps->partner = typec_register_partner(tps->port, &desc);
+ if (IS_ERR(tps->partner))
+ return PTR_ERR(tps->partner);
+
if (desc.identity)
typec_partner_set_identity(tps->partner);
@@ -175,7 +175,8 @@ static int tps6598x_connect(struct tps6598x *tps, u32 status)
static void tps6598x_disconnect(struct tps6598x *tps, u32 status)
{
- typec_unregister_partner(tps->partner);
+ if (!IS_ERR(tps->partner))
+ typec_unregister_partner(tps->partner);
tps->partner = NULL;
typec_set_pwr_opmode(tps->port, TYPEC_PWR_MODE_USB);
typec_set_pwr_role(tps->port, TPS_STATUS_PORTROLE(status));
@@ -392,34 +393,42 @@ static int tps6598x_probe(struct i2c_client *client)
if (ret < 0)
return ret;
+ tps->typec_cap.revision = USB_TYPEC_REV_1_2;
+ tps->typec_cap.pd_revision = 0x200;
+ tps->typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE;
+ tps->typec_cap.pr_set = tps6598x_pr_set;
+ tps->typec_cap.dr_set = tps6598x_dr_set;
+
switch (TPS_SYSCONF_PORTINFO(conf)) {
case TPS_PORTINFO_SINK_ACCESSORY:
case TPS_PORTINFO_SINK:
- tps->typec_cap.type = TYPEC_PORT_UFP;
+ tps->typec_cap.type = TYPEC_PORT_SNK;
+ tps->typec_cap.data = TYPEC_PORT_UFP;
break;
case TPS_PORTINFO_DRP_UFP_DRD:
case TPS_PORTINFO_DRP_DFP_DRD:
- tps->typec_cap.dr_set = tps6598x_dr_set;
- /* fall through */
+ tps->typec_cap.type = TYPEC_PORT_DRP;
+ tps->typec_cap.data = TYPEC_PORT_DRD;
+ break;
case TPS_PORTINFO_DRP_UFP:
+ tps->typec_cap.type = TYPEC_PORT_DRP;
+ tps->typec_cap.data = TYPEC_PORT_UFP;
+ break;
case TPS_PORTINFO_DRP_DFP:
- tps->typec_cap.pr_set = tps6598x_pr_set;
tps->typec_cap.type = TYPEC_PORT_DRP;
+ tps->typec_cap.data = TYPEC_PORT_DFP;
break;
case TPS_PORTINFO_SOURCE:
- tps->typec_cap.type = TYPEC_PORT_DFP;
+ tps->typec_cap.type = TYPEC_PORT_SRC;
+ tps->typec_cap.data = TYPEC_PORT_DFP;
break;
default:
return -ENODEV;
}
- tps->typec_cap.revision = USB_TYPEC_REV_1_2;
- tps->typec_cap.pd_revision = 0x200;
- tps->typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE;
-
tps->port = typec_register_port(&client->dev, &tps->typec_cap);
- if (!tps->port)
- return -ENODEV;
+ if (IS_ERR(tps->port))
+ return PTR_ERR(tps->port);
if (status & TPS_STATUS_PLUG_PRESENT) {
ret = tps6598x_connect(tps, status);
diff --git a/drivers/usb/typec/typec.c b/drivers/usb/typec/typec.c
deleted file mode 100644
index 735726ced602..000000000000
--- a/drivers/usb/typec/typec.c
+++ /dev/null
@@ -1,1365 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * USB Type-C Connector Class
- *
- * Copyright (C) 2017, Intel Corporation
- * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
- */
-
-#include <linux/device.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/slab.h>
-#include <linux/usb/typec.h>
-
-struct typec_mode {
- int index;
- u32 vdo;
- char *desc;
- enum typec_port_type roles;
-
- struct typec_altmode *alt_mode;
-
- unsigned int active:1;
-
- char group_name[6];
- struct attribute_group group;
- struct attribute *attrs[5];
- struct device_attribute vdo_attr;
- struct device_attribute desc_attr;
- struct device_attribute active_attr;
- struct device_attribute roles_attr;
-};
-
-struct typec_altmode {
- struct device dev;
- u16 svid;
- int n_modes;
- struct typec_mode modes[ALTMODE_MAX_MODES];
- const struct attribute_group *mode_groups[ALTMODE_MAX_MODES];
-};
-
-struct typec_plug {
- struct device dev;
- enum typec_plug_index index;
-};
-
-struct typec_cable {
- struct device dev;
- enum typec_plug_type type;
- struct usb_pd_identity *identity;
- unsigned int active:1;
-};
-
-struct typec_partner {
- struct device dev;
- unsigned int usb_pd:1;
- struct usb_pd_identity *identity;
- enum typec_accessory accessory;
-};
-
-struct typec_port {
- unsigned int id;
- struct device dev;
-
- int prefer_role;
- enum typec_data_role data_role;
- enum typec_role pwr_role;
- enum typec_role vconn_role;
- enum typec_pwr_opmode pwr_opmode;
- enum typec_port_type port_type;
- struct mutex port_type_lock;
-
- const struct typec_capability *cap;
-};
-
-#define to_typec_port(_dev_) container_of(_dev_, struct typec_port, dev)
-#define to_typec_plug(_dev_) container_of(_dev_, struct typec_plug, dev)
-#define to_typec_cable(_dev_) container_of(_dev_, struct typec_cable, dev)
-#define to_typec_partner(_dev_) container_of(_dev_, struct typec_partner, dev)
-#define to_altmode(_dev_) container_of(_dev_, struct typec_altmode, dev)
-
-static const struct device_type typec_partner_dev_type;
-static const struct device_type typec_cable_dev_type;
-static const struct device_type typec_plug_dev_type;
-static const struct device_type typec_port_dev_type;
-
-#define is_typec_partner(_dev_) (_dev_->type == &typec_partner_dev_type)
-#define is_typec_cable(_dev_) (_dev_->type == &typec_cable_dev_type)
-#define is_typec_plug(_dev_) (_dev_->type == &typec_plug_dev_type)
-#define is_typec_port(_dev_) (_dev_->type == &typec_port_dev_type)
-
-static DEFINE_IDA(typec_index_ida);
-static struct class *typec_class;
-
-/* Common attributes */
-
-static const char * const typec_accessory_modes[] = {
- [TYPEC_ACCESSORY_NONE] = "none",
- [TYPEC_ACCESSORY_AUDIO] = "analog_audio",
- [TYPEC_ACCESSORY_DEBUG] = "debug",
-};
-
-static struct usb_pd_identity *get_pd_identity(struct device *dev)
-{
- if (is_typec_partner(dev)) {
- struct typec_partner *partner = to_typec_partner(dev);
-
- return partner->identity;
- } else if (is_typec_cable(dev)) {
- struct typec_cable *cable = to_typec_cable(dev);
-
- return cable->identity;
- }
- return NULL;
-}
-
-static ssize_t id_header_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct usb_pd_identity *id = get_pd_identity(dev);
-
- return sprintf(buf, "0x%08x\n", id->id_header);
-}
-static DEVICE_ATTR_RO(id_header);
-
-static ssize_t cert_stat_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct usb_pd_identity *id = get_pd_identity(dev);
-
- return sprintf(buf, "0x%08x\n", id->cert_stat);
-}
-static DEVICE_ATTR_RO(cert_stat);
-
-static ssize_t product_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct usb_pd_identity *id = get_pd_identity(dev);
-
- return sprintf(buf, "0x%08x\n", id->product);
-}
-static DEVICE_ATTR_RO(product);
-
-static struct attribute *usb_pd_id_attrs[] = {
- &dev_attr_id_header.attr,
- &dev_attr_cert_stat.attr,
- &dev_attr_product.attr,
- NULL
-};
-
-static const struct attribute_group usb_pd_id_group = {
- .name = "identity",
- .attrs = usb_pd_id_attrs,
-};
-
-static const struct attribute_group *usb_pd_id_groups[] = {
- &usb_pd_id_group,
- NULL,
-};
-
-static void typec_report_identity(struct device *dev)
-{
- sysfs_notify(&dev->kobj, "identity", "id_header");
- sysfs_notify(&dev->kobj, "identity", "cert_stat");
- sysfs_notify(&dev->kobj, "identity", "product");
-}
-
-/* ------------------------------------------------------------------------- */
-/* Alternate Modes */
-
-/**
- * typec_altmode_update_active - Report Enter/Exit mode
- * @alt: Handle to the alternate mode
- * @mode: Mode index
- * @active: True when the mode has been entered
- *
- * If a partner or cable plug executes Enter/Exit Mode command successfully, the
- * drivers use this routine to report the updated state of the mode.
- */
-void typec_altmode_update_active(struct typec_altmode *alt, int mode,
- bool active)
-{
- struct typec_mode *m = &alt->modes[mode];
- char dir[6];
-
- if (m->active == active)
- return;
-
- m->active = active;
- snprintf(dir, sizeof(dir), "mode%d", mode);
- sysfs_notify(&alt->dev.kobj, dir, "active");
- kobject_uevent(&alt->dev.kobj, KOBJ_CHANGE);
-}
-EXPORT_SYMBOL_GPL(typec_altmode_update_active);
-
-/**
- * typec_altmode2port - Alternate Mode to USB Type-C port
- * @alt: The Alternate Mode
- *
- * Returns handle to the port that a cable plug or partner with @alt is
- * connected to.
- */
-struct typec_port *typec_altmode2port(struct typec_altmode *alt)
-{
- if (is_typec_plug(alt->dev.parent))
- return to_typec_port(alt->dev.parent->parent->parent);
- if (is_typec_partner(alt->dev.parent))
- return to_typec_port(alt->dev.parent->parent);
- if (is_typec_port(alt->dev.parent))
- return to_typec_port(alt->dev.parent);
-
- return NULL;
-}
-EXPORT_SYMBOL_GPL(typec_altmode2port);
-
-static ssize_t
-typec_altmode_vdo_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct typec_mode *mode = container_of(attr, struct typec_mode,
- vdo_attr);
-
- return sprintf(buf, "0x%08x\n", mode->vdo);
-}
-
-static ssize_t
-typec_altmode_desc_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct typec_mode *mode = container_of(attr, struct typec_mode,
- desc_attr);
-
- return sprintf(buf, "%s\n", mode->desc ? mode->desc : "");
-}
-
-static ssize_t
-typec_altmode_active_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct typec_mode *mode = container_of(attr, struct typec_mode,
- active_attr);
-
- return sprintf(buf, "%s\n", mode->active ? "yes" : "no");
-}
-
-static ssize_t
-typec_altmode_active_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t size)
-{
- struct typec_mode *mode = container_of(attr, struct typec_mode,
- active_attr);
- struct typec_port *port = typec_altmode2port(mode->alt_mode);
- bool activate;
- int ret;
-
- if (!port->cap->activate_mode)
- return -EOPNOTSUPP;
-
- ret = kstrtobool(buf, &activate);
- if (ret)
- return ret;
-
- ret = port->cap->activate_mode(port->cap, mode->index, activate);
- if (ret)
- return ret;
-
- return size;
-}
-
-static ssize_t
-typec_altmode_roles_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct typec_mode *mode = container_of(attr, struct typec_mode,
- roles_attr);
- ssize_t ret;
-
- switch (mode->roles) {
- case TYPEC_PORT_DFP:
- ret = sprintf(buf, "source\n");
- break;
- case TYPEC_PORT_UFP:
- ret = sprintf(buf, "sink\n");
- break;
- case TYPEC_PORT_DRP:
- default:
- ret = sprintf(buf, "source sink\n");
- break;
- }
- return ret;
-}
-
-static void typec_init_modes(struct typec_altmode *alt,
- const struct typec_mode_desc *desc, bool is_port)
-{
- int i;
-
- for (i = 0; i < alt->n_modes; i++, desc++) {
- struct typec_mode *mode = &alt->modes[i];
-
- /* Not considering the human readable description critical */
- mode->desc = kstrdup(desc->desc, GFP_KERNEL);
- if (desc->desc && !mode->desc)
- dev_err(&alt->dev, "failed to copy mode%d desc\n", i);
-
- mode->alt_mode = alt;
- mode->vdo = desc->vdo;
- mode->roles = desc->roles;
- mode->index = desc->index;
- sprintf(mode->group_name, "mode%d", desc->index);
-
- sysfs_attr_init(&mode->vdo_attr.attr);
- mode->vdo_attr.attr.name = "vdo";
- mode->vdo_attr.attr.mode = 0444;
- mode->vdo_attr.show = typec_altmode_vdo_show;
-
- sysfs_attr_init(&mode->desc_attr.attr);
- mode->desc_attr.attr.name = "description";
- mode->desc_attr.attr.mode = 0444;
- mode->desc_attr.show = typec_altmode_desc_show;
-
- sysfs_attr_init(&mode->active_attr.attr);
- mode->active_attr.attr.name = "active";
- mode->active_attr.attr.mode = 0644;
- mode->active_attr.show = typec_altmode_active_show;
- mode->active_attr.store = typec_altmode_active_store;
-
- mode->attrs[0] = &mode->vdo_attr.attr;
- mode->attrs[1] = &mode->desc_attr.attr;
- mode->attrs[2] = &mode->active_attr.attr;
-
- /* With ports, list the roles that the mode is supported with */
- if (is_port) {
- sysfs_attr_init(&mode->roles_attr.attr);
- mode->roles_attr.attr.name = "supported_roles";
- mode->roles_attr.attr.mode = 0444;
- mode->roles_attr.show = typec_altmode_roles_show;
-
- mode->attrs[3] = &mode->roles_attr.attr;
- }
-
- mode->group.attrs = mode->attrs;
- mode->group.name = mode->group_name;
-
- alt->mode_groups[i] = &mode->group;
- }
-}
-
-static ssize_t svid_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct typec_altmode *alt = to_altmode(dev);
-
- return sprintf(buf, "%04x\n", alt->svid);
-}
-static DEVICE_ATTR_RO(svid);
-
-static struct attribute *typec_altmode_attrs[] = {
- &dev_attr_svid.attr,
- NULL
-};
-ATTRIBUTE_GROUPS(typec_altmode);
-
-static void typec_altmode_release(struct device *dev)
-{
- struct typec_altmode *alt = to_altmode(dev);
- int i;
-
- for (i = 0; i < alt->n_modes; i++)
- kfree(alt->modes[i].desc);
- kfree(alt);
-}
-
-static const struct device_type typec_altmode_dev_type = {
- .name = "typec_alternate_mode",
- .groups = typec_altmode_groups,
- .release = typec_altmode_release,
-};
-
-static struct typec_altmode *
-typec_register_altmode(struct device *parent,
- const struct typec_altmode_desc *desc)
-{
- struct typec_altmode *alt;
- int ret;
-
- alt = kzalloc(sizeof(*alt), GFP_KERNEL);
- if (!alt)
- return NULL;
-
- alt->svid = desc->svid;
- alt->n_modes = desc->n_modes;
- typec_init_modes(alt, desc->modes, is_typec_port(parent));
-
- alt->dev.parent = parent;
- alt->dev.groups = alt->mode_groups;
- alt->dev.type = &typec_altmode_dev_type;
- dev_set_name(&alt->dev, "svid-%04x", alt->svid);
-
- ret = device_register(&alt->dev);
- if (ret) {
- dev_err(parent, "failed to register alternate mode (%d)\n",
- ret);
- put_device(&alt->dev);
- return NULL;
- }
-
- return alt;
-}
-
-/**
- * typec_unregister_altmode - Unregister Alternate Mode
- * @alt: The alternate mode to be unregistered
- *
- * Unregister device created with typec_partner_register_altmode(),
- * typec_plug_register_altmode() or typec_port_register_altmode().
- */
-void typec_unregister_altmode(struct typec_altmode *alt)
-{
- if (alt)
- device_unregister(&alt->dev);
-}
-EXPORT_SYMBOL_GPL(typec_unregister_altmode);
-
-/* ------------------------------------------------------------------------- */
-/* Type-C Partners */
-
-static ssize_t accessory_mode_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct typec_partner *p = to_typec_partner(dev);
-
- return sprintf(buf, "%s\n", typec_accessory_modes[p->accessory]);
-}
-static DEVICE_ATTR_RO(accessory_mode);
-
-static ssize_t supports_usb_power_delivery_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct typec_partner *p = to_typec_partner(dev);
-
- return sprintf(buf, "%s\n", p->usb_pd ? "yes" : "no");
-}
-static DEVICE_ATTR_RO(supports_usb_power_delivery);
-
-static struct attribute *typec_partner_attrs[] = {
- &dev_attr_accessory_mode.attr,
- &dev_attr_supports_usb_power_delivery.attr,
- NULL
-};
-ATTRIBUTE_GROUPS(typec_partner);
-
-static void typec_partner_release(struct device *dev)
-{
- struct typec_partner *partner = to_typec_partner(dev);
-
- kfree(partner);
-}
-
-static const struct device_type typec_partner_dev_type = {
- .name = "typec_partner",
- .groups = typec_partner_groups,
- .release = typec_partner_release,
-};
-
-/**
- * typec_partner_set_identity - Report result from Discover Identity command
- * @partner: The partner updated identity values
- *
- * This routine is used to report that the result of Discover Identity USB power
- * delivery command has become available.
- */
-int typec_partner_set_identity(struct typec_partner *partner)
-{
- if (!partner->identity)
- return -EINVAL;
-
- typec_report_identity(&partner->dev);
- return 0;
-}
-EXPORT_SYMBOL_GPL(typec_partner_set_identity);
-
-/**
- * typec_partner_register_altmode - Register USB Type-C Partner Alternate Mode
- * @partner: USB Type-C Partner that supports the alternate mode
- * @desc: Description of the alternate mode
- *
- * This routine is used to register each alternate mode individually that
- * @partner has listed in response to Discover SVIDs command. The modes for a
- * SVID listed in response to Discover Modes command need to be listed in an
- * array in @desc.
- *
- * Returns handle to the alternate mode on success or NULL on failure.
- */
-struct typec_altmode *
-typec_partner_register_altmode(struct typec_partner *partner,
- const struct typec_altmode_desc *desc)
-{
- return typec_register_altmode(&partner->dev, desc);
-}
-EXPORT_SYMBOL_GPL(typec_partner_register_altmode);
-
-/**
- * typec_register_partner - Register a USB Type-C Partner
- * @port: The USB Type-C Port the partner is connected to
- * @desc: Description of the partner
- *
- * Registers a device for USB Type-C Partner described in @desc.
- *
- * Returns handle to the partner on success or NULL on failure.
- */
-struct typec_partner *typec_register_partner(struct typec_port *port,
- struct typec_partner_desc *desc)
-{
- struct typec_partner *partner;
- int ret;
-
- partner = kzalloc(sizeof(*partner), GFP_KERNEL);
- if (!partner)
- return NULL;
-
- partner->usb_pd = desc->usb_pd;
- partner->accessory = desc->accessory;
-
- if (desc->identity) {
- /*
- * Creating directory for the identity only if the driver is
- * able to provide data to it.
- */
- partner->dev.groups = usb_pd_id_groups;
- partner->identity = desc->identity;
- }
-
- partner->dev.class = typec_class;
- partner->dev.parent = &port->dev;
- partner->dev.type = &typec_partner_dev_type;
- dev_set_name(&partner->dev, "%s-partner", dev_name(&port->dev));
-
- ret = device_register(&partner->dev);
- if (ret) {
- dev_err(&port->dev, "failed to register partner (%d)\n", ret);
- put_device(&partner->dev);
- return NULL;
- }
-
- return partner;
-}
-EXPORT_SYMBOL_GPL(typec_register_partner);
-
-/**
- * typec_unregister_partner - Unregister a USB Type-C Partner
- * @partner: The partner to be unregistered
- *
- * Unregister device created with typec_register_partner().
- */
-void typec_unregister_partner(struct typec_partner *partner)
-{
- if (partner)
- device_unregister(&partner->dev);
-}
-EXPORT_SYMBOL_GPL(typec_unregister_partner);
-
-/* ------------------------------------------------------------------------- */
-/* Type-C Cable Plugs */
-
-static void typec_plug_release(struct device *dev)
-{
- struct typec_plug *plug = to_typec_plug(dev);
-
- kfree(plug);
-}
-
-static const struct device_type typec_plug_dev_type = {
- .name = "typec_plug",
- .release = typec_plug_release,
-};
-
-/**
- * typec_plug_register_altmode - Register USB Type-C Cable Plug Alternate Mode
- * @plug: USB Type-C Cable Plug that supports the alternate mode
- * @desc: Description of the alternate mode
- *
- * This routine is used to register each alternate mode individually that @plug
- * has listed in response to Discover SVIDs command. The modes for a SVID that
- * the plug lists in response to Discover Modes command need to be listed in an
- * array in @desc.
- *
- * Returns handle to the alternate mode on success or NULL on failure.
- */
-struct typec_altmode *
-typec_plug_register_altmode(struct typec_plug *plug,
- const struct typec_altmode_desc *desc)
-{
- return typec_register_altmode(&plug->dev, desc);
-}
-EXPORT_SYMBOL_GPL(typec_plug_register_altmode);
-
-/**
- * typec_register_plug - Register a USB Type-C Cable Plug
- * @cable: USB Type-C Cable with the plug
- * @desc: Description of the cable plug
- *
- * Registers a device for USB Type-C Cable Plug described in @desc. A USB Type-C
- * Cable Plug represents a plug with electronics in it that can response to USB
- * Power Delivery SOP Prime or SOP Double Prime packages.
- *
- * Returns handle to the cable plug on success or NULL on failure.
- */
-struct typec_plug *typec_register_plug(struct typec_cable *cable,
- struct typec_plug_desc *desc)
-{
- struct typec_plug *plug;
- char name[8];
- int ret;
-
- plug = kzalloc(sizeof(*plug), GFP_KERNEL);
- if (!plug)
- return NULL;
-
- sprintf(name, "plug%d", desc->index);
-
- plug->index = desc->index;
- plug->dev.class = typec_class;
- plug->dev.parent = &cable->dev;
- plug->dev.type = &typec_plug_dev_type;
- dev_set_name(&plug->dev, "%s-%s", dev_name(cable->dev.parent), name);
-
- ret = device_register(&plug->dev);
- if (ret) {
- dev_err(&cable->dev, "failed to register plug (%d)\n", ret);
- put_device(&plug->dev);
- return NULL;
- }
-
- return plug;
-}
-EXPORT_SYMBOL_GPL(typec_register_plug);
-
-/**
- * typec_unregister_plug - Unregister a USB Type-C Cable Plug
- * @plug: The cable plug to be unregistered
- *
- * Unregister device created with typec_register_plug().
- */
-void typec_unregister_plug(struct typec_plug *plug)
-{
- if (plug)
- device_unregister(&plug->dev);
-}
-EXPORT_SYMBOL_GPL(typec_unregister_plug);
-
-/* Type-C Cables */
-
-static ssize_t
-type_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct typec_cable *cable = to_typec_cable(dev);
-
- return sprintf(buf, "%s\n", cable->active ? "active" : "passive");
-}
-static DEVICE_ATTR_RO(type);
-
-static const char * const typec_plug_types[] = {
- [USB_PLUG_NONE] = "unknown",
- [USB_PLUG_TYPE_A] = "type-a",
- [USB_PLUG_TYPE_B] = "type-b",
- [USB_PLUG_TYPE_C] = "type-c",
- [USB_PLUG_CAPTIVE] = "captive",
-};
-
-static ssize_t plug_type_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct typec_cable *cable = to_typec_cable(dev);
-
- return sprintf(buf, "%s\n", typec_plug_types[cable->type]);
-}
-static DEVICE_ATTR_RO(plug_type);
-
-static struct attribute *typec_cable_attrs[] = {
- &dev_attr_type.attr,
- &dev_attr_plug_type.attr,
- NULL
-};
-ATTRIBUTE_GROUPS(typec_cable);
-
-static void typec_cable_release(struct device *dev)
-{
- struct typec_cable *cable = to_typec_cable(dev);
-
- kfree(cable);
-}
-
-static const struct device_type typec_cable_dev_type = {
- .name = "typec_cable",
- .groups = typec_cable_groups,
- .release = typec_cable_release,
-};
-
-/**
- * typec_cable_set_identity - Report result from Discover Identity command
- * @cable: The cable updated identity values
- *
- * This routine is used to report that the result of Discover Identity USB power
- * delivery command has become available.
- */
-int typec_cable_set_identity(struct typec_cable *cable)
-{
- if (!cable->identity)
- return -EINVAL;
-
- typec_report_identity(&cable->dev);
- return 0;
-}
-EXPORT_SYMBOL_GPL(typec_cable_set_identity);
-
-/**
- * typec_register_cable - Register a USB Type-C Cable
- * @port: The USB Type-C Port the cable is connected to
- * @desc: Description of the cable
- *
- * Registers a device for USB Type-C Cable described in @desc. The cable will be
- * parent for the optional cable plug devises.
- *
- * Returns handle to the cable on success or NULL on failure.
- */
-struct typec_cable *typec_register_cable(struct typec_port *port,
- struct typec_cable_desc *desc)
-{
- struct typec_cable *cable;
- int ret;
-
- cable = kzalloc(sizeof(*cable), GFP_KERNEL);
- if (!cable)
- return NULL;
-
- cable->type = desc->type;
- cable->active = desc->active;
-
- if (desc->identity) {
- /*
- * Creating directory for the identity only if the driver is
- * able to provide data to it.
- */
- cable->dev.groups = usb_pd_id_groups;
- cable->identity = desc->identity;
- }
-
- cable->dev.class = typec_class;
- cable->dev.parent = &port->dev;
- cable->dev.type = &typec_cable_dev_type;
- dev_set_name(&cable->dev, "%s-cable", dev_name(&port->dev));
-
- ret = device_register(&cable->dev);
- if (ret) {
- dev_err(&port->dev, "failed to register cable (%d)\n", ret);
- put_device(&cable->dev);
- return NULL;
- }
-
- return cable;
-}
-EXPORT_SYMBOL_GPL(typec_register_cable);
-
-/**
- * typec_unregister_cable - Unregister a USB Type-C Cable
- * @cable: The cable to be unregistered
- *
- * Unregister device created with typec_register_cable().
- */
-void typec_unregister_cable(struct typec_cable *cable)
-{
- if (cable)
- device_unregister(&cable->dev);
-}
-EXPORT_SYMBOL_GPL(typec_unregister_cable);
-
-/* ------------------------------------------------------------------------- */
-/* USB Type-C ports */
-
-static const char * const typec_roles[] = {
- [TYPEC_SINK] = "sink",
- [TYPEC_SOURCE] = "source",
-};
-
-static const char * const typec_data_roles[] = {
- [TYPEC_DEVICE] = "device",
- [TYPEC_HOST] = "host",
-};
-
-static const char * const typec_port_types[] = {
- [TYPEC_PORT_DFP] = "source",
- [TYPEC_PORT_UFP] = "sink",
- [TYPEC_PORT_DRP] = "dual",
-};
-
-static const char * const typec_port_types_drp[] = {
- [TYPEC_PORT_DFP] = "dual [source] sink",
- [TYPEC_PORT_UFP] = "dual source [sink]",
- [TYPEC_PORT_DRP] = "[dual] source sink",
-};
-
-static ssize_t
-preferred_role_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t size)
-{
- struct typec_port *port = to_typec_port(dev);
- int role;
- int ret;
-
- if (port->cap->type != TYPEC_PORT_DRP) {
- dev_dbg(dev, "Preferred role only supported with DRP ports\n");
- return -EOPNOTSUPP;
- }
-
- if (!port->cap->try_role) {
- dev_dbg(dev, "Setting preferred role not supported\n");
- return -EOPNOTSUPP;
- }
-
- role = sysfs_match_string(typec_roles, buf);
- if (role < 0) {
- if (sysfs_streq(buf, "none"))
- role = TYPEC_NO_PREFERRED_ROLE;
- else
- return -EINVAL;
- }
-
- ret = port->cap->try_role(port->cap, role);
- if (ret)
- return ret;
-
- port->prefer_role = role;
- return size;
-}
-
-static ssize_t
-preferred_role_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct typec_port *port = to_typec_port(dev);
-
- if (port->cap->type != TYPEC_PORT_DRP)
- return 0;
-
- if (port->prefer_role < 0)
- return 0;
-
- return sprintf(buf, "%s\n", typec_roles[port->prefer_role]);
-}
-static DEVICE_ATTR_RW(preferred_role);
-
-static ssize_t data_role_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t size)
-{
- struct typec_port *port = to_typec_port(dev);
- int ret;
-
- if (!port->cap->dr_set) {
- dev_dbg(dev, "data role swapping not supported\n");
- return -EOPNOTSUPP;
- }
-
- ret = sysfs_match_string(typec_data_roles, buf);
- if (ret < 0)
- return ret;
-
- mutex_lock(&port->port_type_lock);
- if (port->port_type != TYPEC_PORT_DRP) {
- dev_dbg(dev, "port type fixed at \"%s\"",
- typec_port_types[port->port_type]);
- ret = -EOPNOTSUPP;
- goto unlock_and_ret;
- }
-
- ret = port->cap->dr_set(port->cap, ret);
- if (ret)
- goto unlock_and_ret;
-
- ret = size;
-unlock_and_ret:
- mutex_unlock(&port->port_type_lock);
- return ret;
-}
-
-static ssize_t data_role_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct typec_port *port = to_typec_port(dev);
-
- if (port->cap->type == TYPEC_PORT_DRP)
- return sprintf(buf, "%s\n", port->data_role == TYPEC_HOST ?
- "[host] device" : "host [device]");
-
- return sprintf(buf, "[%s]\n", typec_data_roles[port->data_role]);
-}
-static DEVICE_ATTR_RW(data_role);
-
-static ssize_t power_role_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t size)
-{
- struct typec_port *port = to_typec_port(dev);
- int ret;
-
- if (!port->cap->pd_revision) {
- dev_dbg(dev, "USB Power Delivery not supported\n");
- return -EOPNOTSUPP;
- }
-
- if (!port->cap->pr_set) {
- dev_dbg(dev, "power role swapping not supported\n");
- return -EOPNOTSUPP;
- }
-
- if (port->pwr_opmode != TYPEC_PWR_MODE_PD) {
- dev_dbg(dev, "partner unable to swap power role\n");
- return -EIO;
- }
-
- ret = sysfs_match_string(typec_roles, buf);
- if (ret < 0)
- return ret;
-
- mutex_lock(&port->port_type_lock);
- if (port->port_type != TYPEC_PORT_DRP) {
- dev_dbg(dev, "port type fixed at \"%s\"",
- typec_port_types[port->port_type]);
- ret = -EOPNOTSUPP;
- goto unlock_and_ret;
- }
-
- ret = port->cap->pr_set(port->cap, ret);
- if (ret)
- goto unlock_and_ret;
-
- ret = size;
-unlock_and_ret:
- mutex_unlock(&port->port_type_lock);
- return ret;
-}
-
-static ssize_t power_role_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct typec_port *port = to_typec_port(dev);
-
- if (port->cap->type == TYPEC_PORT_DRP)
- return sprintf(buf, "%s\n", port->pwr_role == TYPEC_SOURCE ?
- "[source] sink" : "source [sink]");
-
- return sprintf(buf, "[%s]\n", typec_roles[port->pwr_role]);
-}
-static DEVICE_ATTR_RW(power_role);
-
-static ssize_t
-port_type_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t size)
-{
- struct typec_port *port = to_typec_port(dev);
- int ret;
- enum typec_port_type type;
-
- if (!port->cap->port_type_set || port->cap->type != TYPEC_PORT_DRP) {
- dev_dbg(dev, "changing port type not supported\n");
- return -EOPNOTSUPP;
- }
-
- ret = sysfs_match_string(typec_port_types, buf);
- if (ret < 0)
- return ret;
-
- type = ret;
- mutex_lock(&port->port_type_lock);
-
- if (port->port_type == type) {
- ret = size;
- goto unlock_and_ret;
- }
-
- ret = port->cap->port_type_set(port->cap, type);
- if (ret)
- goto unlock_and_ret;
-
- port->port_type = type;
- ret = size;
-
-unlock_and_ret:
- mutex_unlock(&port->port_type_lock);
- return ret;
-}
-
-static ssize_t
-port_type_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct typec_port *port = to_typec_port(dev);
-
- if (port->cap->type == TYPEC_PORT_DRP)
- return sprintf(buf, "%s\n",
- typec_port_types_drp[port->port_type]);
-
- return sprintf(buf, "[%s]\n", typec_port_types[port->cap->type]);
-}
-static DEVICE_ATTR_RW(port_type);
-
-static const char * const typec_pwr_opmodes[] = {
- [TYPEC_PWR_MODE_USB] = "default",
- [TYPEC_PWR_MODE_1_5A] = "1.5A",
- [TYPEC_PWR_MODE_3_0A] = "3.0A",
- [TYPEC_PWR_MODE_PD] = "usb_power_delivery",
-};
-
-static ssize_t power_operation_mode_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct typec_port *port = to_typec_port(dev);
-
- return sprintf(buf, "%s\n", typec_pwr_opmodes[port->pwr_opmode]);
-}
-static DEVICE_ATTR_RO(power_operation_mode);
-
-static ssize_t vconn_source_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t size)
-{
- struct typec_port *port = to_typec_port(dev);
- bool source;
- int ret;
-
- if (!port->cap->pd_revision) {
- dev_dbg(dev, "VCONN swap depends on USB Power Delivery\n");
- return -EOPNOTSUPP;
- }
-
- if (!port->cap->vconn_set) {
- dev_dbg(dev, "VCONN swapping not supported\n");
- return -EOPNOTSUPP;
- }
-
- ret = kstrtobool(buf, &source);
- if (ret)
- return ret;
-
- ret = port->cap->vconn_set(port->cap, (enum typec_role)source);
- if (ret)
- return ret;
-
- return size;
-}
-
-static ssize_t vconn_source_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct typec_port *port = to_typec_port(dev);
-
- return sprintf(buf, "%s\n",
- port->vconn_role == TYPEC_SOURCE ? "yes" : "no");
-}
-static DEVICE_ATTR_RW(vconn_source);
-
-static ssize_t supported_accessory_modes_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct typec_port *port = to_typec_port(dev);
- ssize_t ret = 0;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(port->cap->accessory); i++) {
- if (port->cap->accessory[i])
- ret += sprintf(buf + ret, "%s ",
- typec_accessory_modes[port->cap->accessory[i]]);
- }
-
- if (!ret)
- return sprintf(buf, "none\n");
-
- buf[ret - 1] = '\n';
-
- return ret;
-}
-static DEVICE_ATTR_RO(supported_accessory_modes);
-
-static ssize_t usb_typec_revision_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct typec_port *port = to_typec_port(dev);
- u16 rev = port->cap->revision;
-
- return sprintf(buf, "%d.%d\n", (rev >> 8) & 0xff, (rev >> 4) & 0xf);
-}
-static DEVICE_ATTR_RO(usb_typec_revision);
-
-static ssize_t usb_power_delivery_revision_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct typec_port *p = to_typec_port(dev);
-
- return sprintf(buf, "%d\n", (p->cap->pd_revision >> 8) & 0xff);
-}
-static DEVICE_ATTR_RO(usb_power_delivery_revision);
-
-static struct attribute *typec_attrs[] = {
- &dev_attr_data_role.attr,
- &dev_attr_power_operation_mode.attr,
- &dev_attr_power_role.attr,
- &dev_attr_preferred_role.attr,
- &dev_attr_supported_accessory_modes.attr,
- &dev_attr_usb_power_delivery_revision.attr,
- &dev_attr_usb_typec_revision.attr,
- &dev_attr_vconn_source.attr,
- &dev_attr_port_type.attr,
- NULL,
-};
-ATTRIBUTE_GROUPS(typec);
-
-static int typec_uevent(struct device *dev, struct kobj_uevent_env *env)
-{
- int ret;
-
- ret = add_uevent_var(env, "TYPEC_PORT=%s", dev_name(dev));
- if (ret)
- dev_err(dev, "failed to add uevent TYPEC_PORT\n");
-
- return ret;
-}
-
-static void typec_release(struct device *dev)
-{
- struct typec_port *port = to_typec_port(dev);
-
- ida_simple_remove(&typec_index_ida, port->id);
- kfree(port);
-}
-
-static const struct device_type typec_port_dev_type = {
- .name = "typec_port",
- .groups = typec_groups,
- .uevent = typec_uevent,
- .release = typec_release,
-};
-
-/* --------------------------------------- */
-/* Driver callbacks to report role updates */
-
-/**
- * typec_set_data_role - Report data role change
- * @port: The USB Type-C Port where the role was changed
- * @role: The new data role
- *
- * This routine is used by the port drivers to report data role changes.
- */
-void typec_set_data_role(struct typec_port *port, enum typec_data_role role)
-{
- if (port->data_role == role)
- return;
-
- port->data_role = role;
- sysfs_notify(&port->dev.kobj, NULL, "data_role");
- kobject_uevent(&port->dev.kobj, KOBJ_CHANGE);
-}
-EXPORT_SYMBOL_GPL(typec_set_data_role);
-
-/**
- * typec_set_pwr_role - Report power role change
- * @port: The USB Type-C Port where the role was changed
- * @role: The new data role
- *
- * This routine is used by the port drivers to report power role changes.
- */
-void typec_set_pwr_role(struct typec_port *port, enum typec_role role)
-{
- if (port->pwr_role == role)
- return;
-
- port->pwr_role = role;
- sysfs_notify(&port->dev.kobj, NULL, "power_role");
- kobject_uevent(&port->dev.kobj, KOBJ_CHANGE);
-}
-EXPORT_SYMBOL_GPL(typec_set_pwr_role);
-
-/**
- * typec_set_pwr_role - Report VCONN source change
- * @port: The USB Type-C Port which VCONN role changed
- * @role: Source when @port is sourcing VCONN, or Sink when it's not
- *
- * This routine is used by the port drivers to report if the VCONN source is
- * changes.
- */
-void typec_set_vconn_role(struct typec_port *port, enum typec_role role)
-{
- if (port->vconn_role == role)
- return;
-
- port->vconn_role = role;
- sysfs_notify(&port->dev.kobj, NULL, "vconn_source");
- kobject_uevent(&port->dev.kobj, KOBJ_CHANGE);
-}
-EXPORT_SYMBOL_GPL(typec_set_vconn_role);
-
-static int partner_match(struct device *dev, void *data)
-{
- return is_typec_partner(dev);
-}
-
-/**
- * typec_set_pwr_opmode - Report changed power operation mode
- * @port: The USB Type-C Port where the mode was changed
- * @opmode: New power operation mode
- *
- * This routine is used by the port drivers to report changed power operation
- * mode in @port. The modes are USB (default), 1.5A, 3.0A as defined in USB
- * Type-C specification, and "USB Power Delivery" when the power levels are
- * negotiated with methods defined in USB Power Delivery specification.
- */
-void typec_set_pwr_opmode(struct typec_port *port,
- enum typec_pwr_opmode opmode)
-{
- struct device *partner_dev;
-
- if (port->pwr_opmode == opmode)
- return;
-
- port->pwr_opmode = opmode;
- sysfs_notify(&port->dev.kobj, NULL, "power_operation_mode");
- kobject_uevent(&port->dev.kobj, KOBJ_CHANGE);
-
- partner_dev = device_find_child(&port->dev, NULL, partner_match);
- if (partner_dev) {
- struct typec_partner *partner = to_typec_partner(partner_dev);
-
- if (opmode == TYPEC_PWR_MODE_PD && !partner->usb_pd) {
- partner->usb_pd = 1;
- sysfs_notify(&partner_dev->kobj, NULL,
- "supports_usb_power_delivery");
- }
- put_device(partner_dev);
- }
-}
-EXPORT_SYMBOL_GPL(typec_set_pwr_opmode);
-
-/* --------------------------------------- */
-
-/**
- * typec_port_register_altmode - Register USB Type-C Port Alternate Mode
- * @port: USB Type-C Port that supports the alternate mode
- * @desc: Description of the alternate mode
- *
- * This routine is used to register an alternate mode that @port is capable of
- * supporting.
- *
- * Returns handle to the alternate mode on success or NULL on failure.
- */
-struct typec_altmode *
-typec_port_register_altmode(struct typec_port *port,
- const struct typec_altmode_desc *desc)
-{
- return typec_register_altmode(&port->dev, desc);
-}
-EXPORT_SYMBOL_GPL(typec_port_register_altmode);
-
-/**
- * typec_register_port - Register a USB Type-C Port
- * @parent: Parent device
- * @cap: Description of the port
- *
- * Registers a device for USB Type-C Port described in @cap.
- *
- * Returns handle to the port on success or NULL on failure.
- */
-struct typec_port *typec_register_port(struct device *parent,
- const struct typec_capability *cap)
-{
- struct typec_port *port;
- int role;
- int ret;
- int id;
-
- port = kzalloc(sizeof(*port), GFP_KERNEL);
- if (!port)
- return NULL;
-
- id = ida_simple_get(&typec_index_ida, 0, 0, GFP_KERNEL);
- if (id < 0) {
- kfree(port);
- return NULL;
- }
-
- if (cap->type == TYPEC_PORT_DFP)
- role = TYPEC_SOURCE;
- else if (cap->type == TYPEC_PORT_UFP)
- role = TYPEC_SINK;
- else
- role = cap->prefer_role;
-
- if (role == TYPEC_SOURCE) {
- port->data_role = TYPEC_HOST;
- port->pwr_role = TYPEC_SOURCE;
- port->vconn_role = TYPEC_SOURCE;
- } else {
- port->data_role = TYPEC_DEVICE;
- port->pwr_role = TYPEC_SINK;
- port->vconn_role = TYPEC_SINK;
- }
-
- port->id = id;
- port->cap = cap;
- port->port_type = cap->type;
- mutex_init(&port->port_type_lock);
- port->prefer_role = cap->prefer_role;
-
- port->dev.class = typec_class;
- port->dev.parent = parent;
- port->dev.fwnode = cap->fwnode;
- port->dev.type = &typec_port_dev_type;
- dev_set_name(&port->dev, "port%d", id);
-
- ret = device_register(&port->dev);
- if (ret) {
- dev_err(parent, "failed to register port (%d)\n", ret);
- put_device(&port->dev);
- return NULL;
- }
-
- return port;
-}
-EXPORT_SYMBOL_GPL(typec_register_port);
-
-/**
- * typec_unregister_port - Unregister a USB Type-C Port
- * @port: The port to be unregistered
- *
- * Unregister device created with typec_register_port().
- */
-void typec_unregister_port(struct typec_port *port)
-{
- if (port)
- device_unregister(&port->dev);
-}
-EXPORT_SYMBOL_GPL(typec_unregister_port);
-
-static int __init typec_init(void)
-{
- typec_class = class_create(THIS_MODULE, "typec");
- return PTR_ERR_OR_ZERO(typec_class);
-}
-subsys_initcall(typec_init);
-
-static void __exit typec_exit(void)
-{
- class_destroy(typec_class);
- ida_destroy(&typec_index_ida);
-}
-module_exit(typec_exit);
-
-MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("USB Type-C Connector Class");
diff --git a/drivers/usb/typec/typec_wcove.c b/drivers/usb/typec/typec_wcove.c
index 2e990e0d917d..19cca7f1b2c5 100644
--- a/drivers/usb/typec/typec_wcove.c
+++ b/drivers/usb/typec/typec_wcove.c
@@ -572,6 +572,7 @@ static struct tcpc_config wcove_typec_config = {
.operating_snk_mw = 15000,
.type = TYPEC_PORT_DRP,
+ .data = TYPEC_PORT_DRD,
.default_role = TYPEC_SINK,
};
diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile
index b57891c1fd31..7afbea512207 100644
--- a/drivers/usb/typec/ucsi/Makefile
+++ b/drivers/usb/typec/ucsi/Makefile
@@ -5,6 +5,6 @@ obj-$(CONFIG_TYPEC_UCSI) += typec_ucsi.o
typec_ucsi-y := ucsi.o
-typec_ucsi-$(CONFIG_FTRACE) += trace.o
+typec_ucsi-$(CONFIG_TRACING) += trace.o
obj-$(CONFIG_UCSI_ACPI) += ucsi_acpi.o
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index 79046fe66426..bd5cca5632b3 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -28,7 +28,7 @@
* difficult to estimate the time it takes for the system to process the command
* before it is actually passed to the PPM.
*/
-#define UCSI_TIMEOUT_MS 1000
+#define UCSI_TIMEOUT_MS 5000
/*
* UCSI_SWAP_TIMEOUT_MS - Timeout for role swap requests
@@ -260,38 +260,45 @@ static void ucsi_pwr_opmode_change(struct ucsi_connector *con)
static int ucsi_register_partner(struct ucsi_connector *con)
{
- struct typec_partner_desc partner;
+ struct typec_partner_desc desc;
+ struct typec_partner *partner;
if (con->partner)
return 0;
- memset(&partner, 0, sizeof(partner));
+ memset(&desc, 0, sizeof(desc));
switch (con->status.partner_type) {
case UCSI_CONSTAT_PARTNER_TYPE_DEBUG:
- partner.accessory = TYPEC_ACCESSORY_DEBUG;
+ desc.accessory = TYPEC_ACCESSORY_DEBUG;
break;
case UCSI_CONSTAT_PARTNER_TYPE_AUDIO:
- partner.accessory = TYPEC_ACCESSORY_AUDIO;
+ desc.accessory = TYPEC_ACCESSORY_AUDIO;
break;
default:
break;
}
- partner.usb_pd = con->status.pwr_op_mode == UCSI_CONSTAT_PWR_OPMODE_PD;
+ desc.usb_pd = con->status.pwr_op_mode == UCSI_CONSTAT_PWR_OPMODE_PD;
- con->partner = typec_register_partner(con->port, &partner);
- if (!con->partner) {
- dev_err(con->ucsi->dev, "con%d: failed to register partner\n",
- con->num);
- return -ENODEV;
+ partner = typec_register_partner(con->port, &desc);
+ if (IS_ERR(partner)) {
+ dev_err(con->ucsi->dev,
+ "con%d: failed to register partner (%ld)\n", con->num,
+ PTR_ERR(partner));
+ return PTR_ERR(partner);
}
+ con->partner = partner;
+
return 0;
}
static void ucsi_unregister_partner(struct ucsi_connector *con)
{
+ if (!con->partner)
+ return;
+
typec_unregister_partner(con->partner);
con->partner = NULL;
}
@@ -585,11 +592,18 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
return ret;
if (con->cap.op_mode & UCSI_CONCAP_OPMODE_DRP)
- cap->type = TYPEC_PORT_DRP;
+ cap->data = TYPEC_PORT_DRD;
else if (con->cap.op_mode & UCSI_CONCAP_OPMODE_DFP)
- cap->type = TYPEC_PORT_DFP;
+ cap->data = TYPEC_PORT_DFP;
else if (con->cap.op_mode & UCSI_CONCAP_OPMODE_UFP)
- cap->type = TYPEC_PORT_UFP;
+ cap->data = TYPEC_PORT_UFP;
+
+ if (con->cap.provider && con->cap.consumer)
+ cap->type = TYPEC_PORT_DRP;
+ else if (con->cap.provider)
+ cap->type = TYPEC_PORT_SRC;
+ else if (con->cap.consumer)
+ cap->type = TYPEC_PORT_SNK;
cap->revision = ucsi->cap.typec_version;
cap->pd_revision = ucsi->cap.pd_version;
@@ -606,8 +620,8 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
/* Register the connector */
con->port = typec_register_port(ucsi->dev, cap);
- if (!con->port)
- return -ENODEV;
+ if (IS_ERR(con->port))
+ return PTR_ERR(con->port);
/* Get the status */
UCSI_CMD_GET_CONNECTOR_STATUS(ctrl, con->num);
diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c
index 26ca0ec01fd5..c3ddd0f1f449 100644
--- a/drivers/usb/usb-skeleton.c
+++ b/drivers/usb/usb-skeleton.c
@@ -640,4 +640,4 @@ static struct usb_driver skel_driver = {
module_usb_driver(skel_driver);
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/usbip/Kconfig b/drivers/usb/usbip/Kconfig
index eeefa29f8aa2..a20b65cb6678 100644
--- a/drivers/usb/usbip/Kconfig
+++ b/drivers/usb/usbip/Kconfig
@@ -27,7 +27,7 @@ config USBIP_VHCI_HCD
config USBIP_VHCI_HC_PORTS
int "Number of ports per USB/IP virtual host controller"
- range 1 31
+ range 1 15
default 8
depends on USBIP_VHCI_HCD
---help---
diff --git a/drivers/usb/usbip/stub_main.c b/drivers/usb/usbip/stub_main.c
index c31c8402a0c5..d41d0cdeec0f 100644
--- a/drivers/usb/usbip/stub_main.c
+++ b/drivers/usb/usbip/stub_main.c
@@ -186,7 +186,12 @@ static ssize_t rebind_store(struct device_driver *dev, const char *buf,
if (!bid)
return -ENODEV;
+ /* device_attach() callers should hold parent lock for USB */
+ if (bid->udev->dev.parent)
+ device_lock(bid->udev->dev.parent);
ret = device_attach(&bid->udev->dev);
+ if (bid->udev->dev.parent)
+ device_unlock(bid->udev->dev.parent);
if (ret < 0) {
dev_err(&bid->udev->dev, "rebind failed\n");
return ret;
diff --git a/drivers/usb/usbip/usbip_common.h b/drivers/usb/usbip/usbip_common.h
index 473fb8a87289..bf8afe9b5883 100644
--- a/drivers/usb/usbip/usbip_common.h
+++ b/drivers/usb/usbip/usbip_common.h
@@ -243,7 +243,7 @@ enum usbip_side {
#define VUDC_EVENT_ERROR_USB (USBIP_EH_SHUTDOWN | USBIP_EH_UNUSABLE)
#define VUDC_EVENT_ERROR_MALLOC (USBIP_EH_SHUTDOWN | USBIP_EH_UNUSABLE)
-#define VDEV_EVENT_REMOVED (USBIP_EH_SHUTDOWN | USBIP_EH_BYE)
+#define VDEV_EVENT_REMOVED (USBIP_EH_SHUTDOWN | USBIP_EH_RESET | USBIP_EH_BYE)
#define VDEV_EVENT_DOWN (USBIP_EH_SHUTDOWN | USBIP_EH_RESET)
#define VDEV_EVENT_ERROR_TCP (USBIP_EH_SHUTDOWN | USBIP_EH_RESET)
#define VDEV_EVENT_ERROR_MALLOC (USBIP_EH_SHUTDOWN | USBIP_EH_UNUSABLE)
diff --git a/drivers/usb/usbip/usbip_event.c b/drivers/usb/usbip/usbip_event.c
index 5b4c0864ad92..5d88917c9631 100644
--- a/drivers/usb/usbip/usbip_event.c
+++ b/drivers/usb/usbip/usbip_event.c
@@ -91,10 +91,6 @@ static void event_handler(struct work_struct *work)
unset_event(ud, USBIP_EH_UNUSABLE);
}
- /* Stop the error handler. */
- if (ud->event & USBIP_EH_BYE)
- usbip_dbg_eh("removed %p\n", ud);
-
wake_up(&ud->eh_waitq);
}
}
diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c
index 20e3d4609583..d11f3f8dad40 100644
--- a/drivers/usb/usbip/vhci_hcd.c
+++ b/drivers/usb/usbip/vhci_hcd.c
@@ -354,6 +354,8 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
usbip_dbg_vhci_rh(" ClearHubFeature\n");
break;
case ClearPortFeature:
+ if (rhport < 0)
+ goto error;
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
if (hcd->speed == HCD_USB3) {
@@ -511,11 +513,16 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
goto error;
}
+ if (rhport < 0)
+ goto error;
+
vhci_hcd->port_status[rhport] |= USB_PORT_STAT_SUSPEND;
break;
case USB_PORT_FEAT_POWER:
usbip_dbg_vhci_rh(
" SetPortFeature: USB_PORT_FEAT_POWER\n");
+ if (rhport < 0)
+ goto error;
if (hcd->speed == HCD_USB3)
vhci_hcd->port_status[rhport] |= USB_SS_PORT_STAT_POWER;
else
@@ -524,6 +531,8 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
case USB_PORT_FEAT_BH_PORT_RESET:
usbip_dbg_vhci_rh(
" SetPortFeature: USB_PORT_FEAT_BH_PORT_RESET\n");
+ if (rhport < 0)
+ goto error;
/* Applicable only for USB3.0 hub */
if (hcd->speed != HCD_USB3) {
pr_err("USB_PORT_FEAT_BH_PORT_RESET req not "
@@ -534,6 +543,8 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
case USB_PORT_FEAT_RESET:
usbip_dbg_vhci_rh(
" SetPortFeature: USB_PORT_FEAT_RESET\n");
+ if (rhport < 0)
+ goto error;
/* if it's already enabled, disable */
if (hcd->speed == HCD_USB3) {
vhci_hcd->port_status[rhport] = 0;
@@ -554,6 +565,8 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
default:
usbip_dbg_vhci_rh(" SetPortFeature: default %d\n",
wValue);
+ if (rhport < 0)
+ goto error;
if (hcd->speed == HCD_USB3) {
if ((vhci_hcd->port_status[rhport] &
USB_SS_PORT_STAT_POWER) != 0) {
diff --git a/drivers/usb/wusbcore/crypto.c b/drivers/usb/wusbcore/crypto.c
index 4c00be2d1993..aff50eb09ca9 100644
--- a/drivers/usb/wusbcore/crypto.c
+++ b/drivers/usb/wusbcore/crypto.c
@@ -202,7 +202,7 @@ static int wusb_ccm_mac(struct crypto_skcipher *tfm_cbc,
struct scatterlist sg[4], sg_dst;
void *dst_buf;
size_t dst_size;
- u8 iv[crypto_skcipher_ivsize(tfm_cbc)];
+ u8 *iv;
size_t zero_padding;
/*
@@ -224,7 +224,9 @@ static int wusb_ccm_mac(struct crypto_skcipher *tfm_cbc,
if (!dst_buf)
goto error_dst_buf;
- memset(iv, 0, sizeof(iv));
+ iv = kzalloc(crypto_skcipher_ivsize(tfm_cbc), GFP_KERNEL);
+ if (!iv)
+ goto error_iv;
/* Setup B0 */
scratch->b0.flags = 0x59; /* Format B0 */
@@ -276,6 +278,8 @@ static int wusb_ccm_mac(struct crypto_skcipher *tfm_cbc,
bytewise_xor(mic, &scratch->ax, iv, 8);
result = 8;
error_cbc_crypt:
+ kfree(iv);
+error_iv:
kfree(dst_buf);
error_dst_buf:
return result;
diff --git a/drivers/usb/wusbcore/wa-nep.c b/drivers/usb/wusbcore/wa-nep.c
index 9fdcb6b84abf..5f0656db5482 100644
--- a/drivers/usb/wusbcore/wa-nep.c
+++ b/drivers/usb/wusbcore/wa-nep.c
@@ -93,7 +93,6 @@ static void wa_notif_dispatch(struct work_struct *ws)
goto out; /* screw it */
#endif
atomic_dec(&wa->notifs_queued); /* Throttling ctl */
- dev = &wa->usb_iface->dev;
size = nw->size;
itr = nw->data;
diff --git a/drivers/uwb/uwb-debug.c b/drivers/uwb/uwb-debug.c
index 991374b13571..f1622bae13be 100644
--- a/drivers/uwb/uwb-debug.c
+++ b/drivers/uwb/uwb-debug.c
@@ -206,7 +206,7 @@ static const struct file_operations command_fops = {
.owner = THIS_MODULE,
};
-static int reservations_print(struct seq_file *s, void *p)
+static int reservations_show(struct seq_file *s, void *p)
{
struct uwb_rc *rc = s->private;
struct uwb_rsv *rsv;
@@ -240,21 +240,9 @@ static int reservations_print(struct seq_file *s, void *p)
return 0;
}
+DEFINE_SHOW_ATTRIBUTE(reservations);
-static int reservations_open(struct inode *inode, struct file *file)
-{
- return single_open(file, reservations_print, inode->i_private);
-}
-
-static const struct file_operations reservations_fops = {
- .open = reservations_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
-};
-
-static int drp_avail_print(struct seq_file *s, void *p)
+static int drp_avail_show(struct seq_file *s, void *p)
{
struct uwb_rc *rc = s->private;
@@ -264,19 +252,7 @@ static int drp_avail_print(struct seq_file *s, void *p)
return 0;
}
-
-static int drp_avail_open(struct inode *inode, struct file *file)
-{
- return single_open(file, drp_avail_print, inode->i_private);
-}
-
-static const struct file_operations drp_avail_fops = {
- .open = drp_avail_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
-};
+DEFINE_SHOW_ATTRIBUTE(drp_avail);
static void uwb_dbg_channel_changed(struct uwb_pal *pal, int channel)
{
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index 8a1508a8e481..b423a309a6e0 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -302,6 +302,7 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev)
{
struct pci_dev *pdev = vdev->pdev;
struct vfio_pci_dummy_resource *dummy_res, *tmp;
+ struct vfio_pci_ioeventfd *ioeventfd, *ioeventfd_tmp;
int i, bar;
/* Stop the device from further DMA */
@@ -311,6 +312,15 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev)
VFIO_IRQ_SET_ACTION_TRIGGER,
vdev->irq_type, 0, 0, NULL);
+ /* Device closed, don't need mutex here */
+ list_for_each_entry_safe(ioeventfd, ioeventfd_tmp,
+ &vdev->ioeventfds_list, next) {
+ vfio_virqfd_disable(&ioeventfd->virqfd);
+ list_del(&ioeventfd->next);
+ kfree(ioeventfd);
+ }
+ vdev->ioeventfds_nr = 0;
+
vdev->virq_disabled = false;
for (i = 0; i < vdev->num_regions; i++)
@@ -1009,6 +1019,28 @@ hot_reset_release:
kfree(groups);
return ret;
+ } else if (cmd == VFIO_DEVICE_IOEVENTFD) {
+ struct vfio_device_ioeventfd ioeventfd;
+ int count;
+
+ minsz = offsetofend(struct vfio_device_ioeventfd, fd);
+
+ if (copy_from_user(&ioeventfd, (void __user *)arg, minsz))
+ return -EFAULT;
+
+ if (ioeventfd.argsz < minsz)
+ return -EINVAL;
+
+ if (ioeventfd.flags & ~VFIO_DEVICE_IOEVENTFD_SIZE_MASK)
+ return -EINVAL;
+
+ count = ioeventfd.flags & VFIO_DEVICE_IOEVENTFD_SIZE_MASK;
+
+ if (hweight8(count) != 1 || ioeventfd.fd < -1)
+ return -EINVAL;
+
+ return vfio_pci_ioeventfd(vdev, ioeventfd.offset,
+ ioeventfd.data, count, ioeventfd.fd);
}
return -ENOTTY;
@@ -1171,6 +1203,8 @@ static int vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
vdev->irq_type = VFIO_PCI_NUM_IRQS;
mutex_init(&vdev->igate);
spin_lock_init(&vdev->irqlock);
+ mutex_init(&vdev->ioeventfds_lock);
+ INIT_LIST_HEAD(&vdev->ioeventfds_list);
ret = vfio_add_group_dev(&pdev->dev, &vfio_pci_ops, vdev);
if (ret) {
@@ -1212,6 +1246,7 @@ static void vfio_pci_remove(struct pci_dev *pdev)
vfio_iommu_group_put(pdev->dev.iommu_group, &pdev->dev);
kfree(vdev->region);
+ mutex_destroy(&vdev->ioeventfds_lock);
kfree(vdev);
if (vfio_pci_is_vga(pdev)) {
diff --git a/drivers/vfio/pci/vfio_pci_private.h b/drivers/vfio/pci/vfio_pci_private.h
index f561ac1c78a0..cde3b5d3441a 100644
--- a/drivers/vfio/pci/vfio_pci_private.h
+++ b/drivers/vfio/pci/vfio_pci_private.h
@@ -29,6 +29,19 @@
#define PCI_CAP_ID_INVALID 0xFF /* default raw access */
#define PCI_CAP_ID_INVALID_VIRT 0xFE /* default virt access */
+/* Cap maximum number of ioeventfds per device (arbitrary) */
+#define VFIO_PCI_IOEVENTFD_MAX 1000
+
+struct vfio_pci_ioeventfd {
+ struct list_head next;
+ struct virqfd *virqfd;
+ void __iomem *addr;
+ uint64_t data;
+ loff_t pos;
+ int bar;
+ int count;
+};
+
struct vfio_pci_irq_ctx {
struct eventfd_ctx *trigger;
struct virqfd *unmask;
@@ -92,9 +105,12 @@ struct vfio_pci_device {
bool nointx;
struct pci_saved_state *pci_saved_state;
int refcnt;
+ int ioeventfds_nr;
struct eventfd_ctx *err_trigger;
struct eventfd_ctx *req_trigger;
struct list_head dummy_resources_list;
+ struct mutex ioeventfds_lock;
+ struct list_head ioeventfds_list;
};
#define is_intx(vdev) (vdev->irq_type == VFIO_PCI_INTX_IRQ_INDEX)
@@ -120,6 +136,9 @@ extern ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf,
extern ssize_t vfio_pci_vga_rw(struct vfio_pci_device *vdev, char __user *buf,
size_t count, loff_t *ppos, bool iswrite);
+extern long vfio_pci_ioeventfd(struct vfio_pci_device *vdev, loff_t offset,
+ uint64_t data, int count, int fd);
+
extern int vfio_pci_init_perm_bits(void);
extern void vfio_pci_uninit_perm_bits(void);
diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c
index 357243d76f10..a6029d0a5524 100644
--- a/drivers/vfio/pci/vfio_pci_rdwr.c
+++ b/drivers/vfio/pci/vfio_pci_rdwr.c
@@ -17,10 +17,29 @@
#include <linux/pci.h>
#include <linux/uaccess.h>
#include <linux/io.h>
+#include <linux/vfio.h>
#include <linux/vgaarb.h>
#include "vfio_pci_private.h"
+#ifdef __LITTLE_ENDIAN
+#define vfio_ioread64 ioread64
+#define vfio_iowrite64 iowrite64
+#define vfio_ioread32 ioread32
+#define vfio_iowrite32 iowrite32
+#define vfio_ioread16 ioread16
+#define vfio_iowrite16 iowrite16
+#else
+#define vfio_ioread64 ioread64be
+#define vfio_iowrite64 iowrite64be
+#define vfio_ioread32 ioread32be
+#define vfio_iowrite32 iowrite32be
+#define vfio_ioread16 ioread16be
+#define vfio_iowrite16 iowrite16be
+#endif
+#define vfio_ioread8 ioread8
+#define vfio_iowrite8 iowrite8
+
/*
* Read or write from an __iomem region (MMIO or I/O port) with an excluded
* range which is inaccessible. The excluded range drops writes and fills
@@ -44,15 +63,15 @@ static ssize_t do_io_rw(void __iomem *io, char __user *buf,
fillable = 0;
if (fillable >= 4 && !(off % 4)) {
- __le32 val;
+ u32 val;
if (iswrite) {
if (copy_from_user(&val, buf, 4))
return -EFAULT;
- iowrite32(le32_to_cpu(val), io + off);
+ vfio_iowrite32(val, io + off);
} else {
- val = cpu_to_le32(ioread32(io + off));
+ val = vfio_ioread32(io + off);
if (copy_to_user(buf, &val, 4))
return -EFAULT;
@@ -60,15 +79,15 @@ static ssize_t do_io_rw(void __iomem *io, char __user *buf,
filled = 4;
} else if (fillable >= 2 && !(off % 2)) {
- __le16 val;
+ u16 val;
if (iswrite) {
if (copy_from_user(&val, buf, 2))
return -EFAULT;
- iowrite16(le16_to_cpu(val), io + off);
+ vfio_iowrite16(val, io + off);
} else {
- val = cpu_to_le16(ioread16(io + off));
+ val = vfio_ioread16(io + off);
if (copy_to_user(buf, &val, 2))
return -EFAULT;
@@ -82,9 +101,9 @@ static ssize_t do_io_rw(void __iomem *io, char __user *buf,
if (copy_from_user(&val, buf, 1))
return -EFAULT;
- iowrite8(val, io + off);
+ vfio_iowrite8(val, io + off);
} else {
- val = ioread8(io + off);
+ val = vfio_ioread8(io + off);
if (copy_to_user(buf, &val, 1))
return -EFAULT;
@@ -113,6 +132,30 @@ static ssize_t do_io_rw(void __iomem *io, char __user *buf,
return done;
}
+static int vfio_pci_setup_barmap(struct vfio_pci_device *vdev, int bar)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ int ret;
+ void __iomem *io;
+
+ if (vdev->barmap[bar])
+ return 0;
+
+ ret = pci_request_selected_regions(pdev, 1 << bar, "vfio");
+ if (ret)
+ return ret;
+
+ io = pci_iomap(pdev, bar, 0);
+ if (!io) {
+ pci_release_selected_regions(pdev, 1 << bar);
+ return -ENOMEM;
+ }
+
+ vdev->barmap[bar] = io;
+
+ return 0;
+}
+
ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf,
size_t count, loff_t *ppos, bool iswrite)
{
@@ -147,22 +190,13 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf,
if (!io)
return -ENOMEM;
x_end = end;
- } else if (!vdev->barmap[bar]) {
- int ret;
-
- ret = pci_request_selected_regions(pdev, 1 << bar, "vfio");
+ } else {
+ int ret = vfio_pci_setup_barmap(vdev, bar);
if (ret)
return ret;
- io = pci_iomap(pdev, bar, 0);
- if (!io) {
- pci_release_selected_regions(pdev, 1 << bar);
- return -ENOMEM;
- }
-
- vdev->barmap[bar] = io;
- } else
io = vdev->barmap[bar];
+ }
if (bar == vdev->msix_bar) {
x_start = vdev->msix_offset;
@@ -242,3 +276,113 @@ ssize_t vfio_pci_vga_rw(struct vfio_pci_device *vdev, char __user *buf,
return done;
}
+
+static int vfio_pci_ioeventfd_handler(void *opaque, void *unused)
+{
+ struct vfio_pci_ioeventfd *ioeventfd = opaque;
+
+ switch (ioeventfd->count) {
+ case 1:
+ vfio_iowrite8(ioeventfd->data, ioeventfd->addr);
+ break;
+ case 2:
+ vfio_iowrite16(ioeventfd->data, ioeventfd->addr);
+ break;
+ case 4:
+ vfio_iowrite32(ioeventfd->data, ioeventfd->addr);
+ break;
+#ifdef iowrite64
+ case 8:
+ vfio_iowrite64(ioeventfd->data, ioeventfd->addr);
+ break;
+#endif
+ }
+
+ return 0;
+}
+
+long vfio_pci_ioeventfd(struct vfio_pci_device *vdev, loff_t offset,
+ uint64_t data, int count, int fd)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ loff_t pos = offset & VFIO_PCI_OFFSET_MASK;
+ int ret, bar = VFIO_PCI_OFFSET_TO_INDEX(offset);
+ struct vfio_pci_ioeventfd *ioeventfd;
+
+ /* Only support ioeventfds into BARs */
+ if (bar > VFIO_PCI_BAR5_REGION_INDEX)
+ return -EINVAL;
+
+ if (pos + count > pci_resource_len(pdev, bar))
+ return -EINVAL;
+
+ /* Disallow ioeventfds working around MSI-X table writes */
+ if (bar == vdev->msix_bar &&
+ !(pos + count <= vdev->msix_offset ||
+ pos >= vdev->msix_offset + vdev->msix_size))
+ return -EINVAL;
+
+#ifndef iowrite64
+ if (count == 8)
+ return -EINVAL;
+#endif
+
+ ret = vfio_pci_setup_barmap(vdev, bar);
+ if (ret)
+ return ret;
+
+ mutex_lock(&vdev->ioeventfds_lock);
+
+ list_for_each_entry(ioeventfd, &vdev->ioeventfds_list, next) {
+ if (ioeventfd->pos == pos && ioeventfd->bar == bar &&
+ ioeventfd->data == data && ioeventfd->count == count) {
+ if (fd == -1) {
+ vfio_virqfd_disable(&ioeventfd->virqfd);
+ list_del(&ioeventfd->next);
+ vdev->ioeventfds_nr--;
+ kfree(ioeventfd);
+ ret = 0;
+ } else
+ ret = -EEXIST;
+
+ goto out_unlock;
+ }
+ }
+
+ if (fd < 0) {
+ ret = -ENODEV;
+ goto out_unlock;
+ }
+
+ if (vdev->ioeventfds_nr >= VFIO_PCI_IOEVENTFD_MAX) {
+ ret = -ENOSPC;
+ goto out_unlock;
+ }
+
+ ioeventfd = kzalloc(sizeof(*ioeventfd), GFP_KERNEL);
+ if (!ioeventfd) {
+ ret = -ENOMEM;
+ goto out_unlock;
+ }
+
+ ioeventfd->addr = vdev->barmap[bar] + pos;
+ ioeventfd->data = data;
+ ioeventfd->pos = pos;
+ ioeventfd->bar = bar;
+ ioeventfd->count = count;
+
+ ret = vfio_virqfd_enable(ioeventfd, vfio_pci_ioeventfd_handler,
+ NULL, NULL, &ioeventfd->virqfd, fd);
+ if (ret) {
+ kfree(ioeventfd);
+ goto out_unlock;
+ }
+
+ list_add(&ioeventfd->next, &vdev->ioeventfds_list);
+ vdev->ioeventfds_nr++;
+
+out_unlock:
+ mutex_unlock(&vdev->ioeventfds_lock);
+
+ return ret;
+}
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index 45657e2b1ff7..5c212bf29640 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -102,6 +102,13 @@ struct vfio_pfn {
atomic_t ref_count;
};
+struct vfio_regions {
+ struct list_head list;
+ dma_addr_t iova;
+ phys_addr_t phys;
+ size_t len;
+};
+
#define IS_IOMMU_CAP_DOMAIN_IN_CONTAINER(iommu) \
(!list_empty(&iommu->domain_list))
@@ -397,7 +404,6 @@ static long vfio_pin_pages_remote(struct vfio_dma *dma, unsigned long vaddr,
{
unsigned long pfn = 0;
long ret, pinned = 0, lock_acct = 0;
- bool rsvd;
dma_addr_t iova = vaddr - dma->vaddr + dma->iova;
/* This code path is only user initiated */
@@ -408,14 +414,23 @@ static long vfio_pin_pages_remote(struct vfio_dma *dma, unsigned long vaddr,
if (ret)
return ret;
+ if (is_invalid_reserved_pfn(*pfn_base)) {
+ struct vm_area_struct *vma;
+
+ down_read(&current->mm->mmap_sem);
+ vma = find_vma_intersection(current->mm, vaddr, vaddr + 1);
+ pinned = min_t(long, npage, vma_pages(vma));
+ up_read(&current->mm->mmap_sem);
+ return pinned;
+ }
+
pinned++;
- rsvd = is_invalid_reserved_pfn(*pfn_base);
/*
* Reserved pages aren't counted against the user, externally pinned
* pages are already counted against the user.
*/
- if (!rsvd && !vfio_find_vpfn(dma, iova)) {
+ if (!vfio_find_vpfn(dma, iova)) {
if (!lock_cap && current->mm->locked_vm + 1 > limit) {
put_pfn(*pfn_base, dma->prot);
pr_warn("%s: RLIMIT_MEMLOCK (%ld) exceeded\n", __func__,
@@ -435,13 +450,12 @@ static long vfio_pin_pages_remote(struct vfio_dma *dma, unsigned long vaddr,
if (ret)
break;
- if (pfn != *pfn_base + pinned ||
- rsvd != is_invalid_reserved_pfn(pfn)) {
+ if (pfn != *pfn_base + pinned) {
put_pfn(pfn, dma->prot);
break;
}
- if (!rsvd && !vfio_find_vpfn(dma, iova)) {
+ if (!vfio_find_vpfn(dma, iova)) {
if (!lock_cap &&
current->mm->locked_vm + lock_acct + 1 > limit) {
put_pfn(pfn, dma->prot);
@@ -459,10 +473,8 @@ out:
unpin_out:
if (ret) {
- if (!rsvd) {
- for (pfn = *pfn_base ; pinned ; pfn++, pinned--)
- put_pfn(pfn, dma->prot);
- }
+ for (pfn = *pfn_base ; pinned ; pfn++, pinned--)
+ put_pfn(pfn, dma->prot);
return ret;
}
@@ -660,11 +672,102 @@ unpin_exit:
return i > npage ? npage : (i > 0 ? i : -EINVAL);
}
+static long vfio_sync_unpin(struct vfio_dma *dma, struct vfio_domain *domain,
+ struct list_head *regions)
+{
+ long unlocked = 0;
+ struct vfio_regions *entry, *next;
+
+ iommu_tlb_sync(domain->domain);
+
+ list_for_each_entry_safe(entry, next, regions, list) {
+ unlocked += vfio_unpin_pages_remote(dma,
+ entry->iova,
+ entry->phys >> PAGE_SHIFT,
+ entry->len >> PAGE_SHIFT,
+ false);
+ list_del(&entry->list);
+ kfree(entry);
+ }
+
+ cond_resched();
+
+ return unlocked;
+}
+
+/*
+ * Generally, VFIO needs to unpin remote pages after each IOTLB flush.
+ * Therefore, when using IOTLB flush sync interface, VFIO need to keep track
+ * of these regions (currently using a list).
+ *
+ * This value specifies maximum number of regions for each IOTLB flush sync.
+ */
+#define VFIO_IOMMU_TLB_SYNC_MAX 512
+
+static size_t unmap_unpin_fast(struct vfio_domain *domain,
+ struct vfio_dma *dma, dma_addr_t *iova,
+ size_t len, phys_addr_t phys, long *unlocked,
+ struct list_head *unmapped_list,
+ int *unmapped_cnt)
+{
+ size_t unmapped = 0;
+ struct vfio_regions *entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+
+ if (entry) {
+ unmapped = iommu_unmap_fast(domain->domain, *iova, len);
+
+ if (!unmapped) {
+ kfree(entry);
+ } else {
+ iommu_tlb_range_add(domain->domain, *iova, unmapped);
+ entry->iova = *iova;
+ entry->phys = phys;
+ entry->len = unmapped;
+ list_add_tail(&entry->list, unmapped_list);
+
+ *iova += unmapped;
+ (*unmapped_cnt)++;
+ }
+ }
+
+ /*
+ * Sync if the number of fast-unmap regions hits the limit
+ * or in case of errors.
+ */
+ if (*unmapped_cnt >= VFIO_IOMMU_TLB_SYNC_MAX || !unmapped) {
+ *unlocked += vfio_sync_unpin(dma, domain,
+ unmapped_list);
+ *unmapped_cnt = 0;
+ }
+
+ return unmapped;
+}
+
+static size_t unmap_unpin_slow(struct vfio_domain *domain,
+ struct vfio_dma *dma, dma_addr_t *iova,
+ size_t len, phys_addr_t phys,
+ long *unlocked)
+{
+ size_t unmapped = iommu_unmap(domain->domain, *iova, len);
+
+ if (unmapped) {
+ *unlocked += vfio_unpin_pages_remote(dma, *iova,
+ phys >> PAGE_SHIFT,
+ unmapped >> PAGE_SHIFT,
+ false);
+ *iova += unmapped;
+ cond_resched();
+ }
+ return unmapped;
+}
+
static long vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma,
bool do_accounting)
{
dma_addr_t iova = dma->iova, end = dma->iova + dma->size;
struct vfio_domain *domain, *d;
+ LIST_HEAD(unmapped_region_list);
+ int unmapped_region_cnt = 0;
long unlocked = 0;
if (!dma->size)
@@ -710,20 +813,26 @@ static long vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma,
break;
}
- unmapped = iommu_unmap(domain->domain, iova, len);
- if (WARN_ON(!unmapped))
- break;
-
- unlocked += vfio_unpin_pages_remote(dma, iova,
- phys >> PAGE_SHIFT,
- unmapped >> PAGE_SHIFT,
- false);
- iova += unmapped;
-
- cond_resched();
+ /*
+ * First, try to use fast unmap/unpin. In case of failure,
+ * switch to slow unmap/unpin path.
+ */
+ unmapped = unmap_unpin_fast(domain, dma, &iova, len, phys,
+ &unlocked, &unmapped_region_list,
+ &unmapped_region_cnt);
+ if (!unmapped) {
+ unmapped = unmap_unpin_slow(domain, dma, &iova, len,
+ phys, &unlocked);
+ if (WARN_ON(!unmapped))
+ break;
+ }
}
dma->iommu_mapped = false;
+
+ if (unmapped_region_cnt)
+ unlocked += vfio_sync_unpin(dma, domain, &unmapped_region_list);
+
if (do_accounting) {
vfio_lock_acct(dma->task, -unlocked, NULL);
return 0;
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
index 8139bc70ad7d..986058a57917 100644
--- a/drivers/vhost/net.c
+++ b/drivers/vhost/net.c
@@ -44,6 +44,10 @@ MODULE_PARM_DESC(experimental_zcopytx, "Enable Zero Copy TX;"
* Using this limit prevents one virtqueue from starving others. */
#define VHOST_NET_WEIGHT 0x80000
+/* Max number of packets transferred before requeueing the job.
+ * Using this limit prevents one virtqueue from starving rx. */
+#define VHOST_NET_PKT_WEIGHT(vq) ((vq)->num * 2)
+
/* MAX number of TX used buffers for outstanding zerocopy */
#define VHOST_MAX_PEND 128
#define VHOST_GOODCOPY_LEN 256
@@ -473,6 +477,7 @@ static void handle_tx(struct vhost_net *net)
struct socket *sock;
struct vhost_net_ubuf_ref *uninitialized_var(ubufs);
bool zcopy, zcopy_used;
+ int sent_pkts = 0;
mutex_lock(&vq->mutex);
sock = vq->private_data;
@@ -580,7 +585,8 @@ static void handle_tx(struct vhost_net *net)
else
vhost_zerocopy_signal_used(net, vq);
vhost_net_tx_packet(net);
- if (unlikely(total_len >= VHOST_NET_WEIGHT)) {
+ if (unlikely(total_len >= VHOST_NET_WEIGHT) ||
+ unlikely(++sent_pkts >= VHOST_NET_PKT_WEIGHT(vq))) {
vhost_poll_queue(&vq->poll);
break;
}
@@ -630,7 +636,7 @@ static int vhost_net_rx_peek_head_len(struct vhost_net *net, struct sock *sk)
if (!len && vq->busyloop_timeout) {
/* Both tx vq and rx socket were polled here */
- mutex_lock(&vq->mutex);
+ mutex_lock_nested(&vq->mutex, 1);
vhost_disable_notify(&net->dev, vq);
preempt_disable();
@@ -763,7 +769,7 @@ static void handle_rx(struct vhost_net *net)
struct iov_iter fixup;
__virtio16 num_buffers;
- mutex_lock(&vq->mutex);
+ mutex_lock_nested(&vq->mutex, 0);
sock = vq->private_data;
if (!sock)
goto out;
@@ -1040,7 +1046,7 @@ static struct socket *get_raw_socket(int fd)
struct sockaddr_ll sa;
char buf[MAX_ADDR_LEN];
} uaddr;
- int uaddr_len = sizeof uaddr, r;
+ int r;
struct socket *sock = sockfd_lookup(fd, &r);
if (!sock)
@@ -1052,9 +1058,8 @@ static struct socket *get_raw_socket(int fd)
goto err;
}
- r = sock->ops->getname(sock, (struct sockaddr *)&uaddr.sa,
- &uaddr_len, 0);
- if (r)
+ r = sock->ops->getname(sock, (struct sockaddr *)&uaddr.sa, 0);
+ if (r < 0)
goto err;
if (uaddr.sa.sll_family != AF_PACKET) {
diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
index 1b3e8d2d5c8b..f3bd8e941224 100644
--- a/drivers/vhost/vhost.c
+++ b/drivers/vhost/vhost.c
@@ -212,8 +212,7 @@ int vhost_poll_start(struct vhost_poll *poll, struct file *file)
if (mask)
vhost_poll_wakeup(&poll->wait, 0, 0, poll_to_key(mask));
if (mask & EPOLLERR) {
- if (poll->wqh)
- remove_wait_queue(poll->wqh, &poll->wait);
+ vhost_poll_stop(poll);
ret = -EINVAL;
}
@@ -642,14 +641,14 @@ void vhost_dev_cleanup(struct vhost_dev *dev)
}
EXPORT_SYMBOL_GPL(vhost_dev_cleanup);
-static int log_access_ok(void __user *log_base, u64 addr, unsigned long sz)
+static bool log_access_ok(void __user *log_base, u64 addr, unsigned long sz)
{
u64 a = addr / VHOST_PAGE_SIZE / 8;
/* Make sure 64 bit math will not overflow. */
if (a > ULONG_MAX - (unsigned long)log_base ||
a + (unsigned long)log_base > ULONG_MAX)
- return 0;
+ return false;
return access_ok(VERIFY_WRITE, log_base + a,
(sz + VHOST_PAGE_SIZE * 8 - 1) / VHOST_PAGE_SIZE / 8);
@@ -662,30 +661,30 @@ static bool vhost_overflow(u64 uaddr, u64 size)
}
/* Caller should have vq mutex and device mutex. */
-static int vq_memory_access_ok(void __user *log_base, struct vhost_umem *umem,
- int log_all)
+static bool vq_memory_access_ok(void __user *log_base, struct vhost_umem *umem,
+ int log_all)
{
struct vhost_umem_node *node;
if (!umem)
- return 0;
+ return false;
list_for_each_entry(node, &umem->umem_list, link) {
unsigned long a = node->userspace_addr;
if (vhost_overflow(node->userspace_addr, node->size))
- return 0;
+ return false;
if (!access_ok(VERIFY_WRITE, (void __user *)a,
node->size))
- return 0;
+ return false;
else if (log_all && !log_access_ok(log_base,
node->start,
node->size))
- return 0;
+ return false;
}
- return 1;
+ return true;
}
static inline void __user *vhost_vq_meta_fetch(struct vhost_virtqueue *vq,
@@ -702,13 +701,13 @@ static inline void __user *vhost_vq_meta_fetch(struct vhost_virtqueue *vq,
/* Can we switch to this memory table? */
/* Caller should have device mutex but not vq mutex */
-static int memory_access_ok(struct vhost_dev *d, struct vhost_umem *umem,
- int log_all)
+static bool memory_access_ok(struct vhost_dev *d, struct vhost_umem *umem,
+ int log_all)
{
int i;
for (i = 0; i < d->nvqs; ++i) {
- int ok;
+ bool ok;
bool log;
mutex_lock(&d->vqs[i]->mutex);
@@ -718,12 +717,12 @@ static int memory_access_ok(struct vhost_dev *d, struct vhost_umem *umem,
ok = vq_memory_access_ok(d->vqs[i]->log_base,
umem, log);
else
- ok = 1;
+ ok = true;
mutex_unlock(&d->vqs[i]->mutex);
if (!ok)
- return 0;
+ return false;
}
- return 1;
+ return true;
}
static int translate_desc(struct vhost_virtqueue *vq, u64 addr, u32 len,
@@ -745,7 +744,7 @@ static int vhost_copy_to_user(struct vhost_virtqueue *vq, void __user *to,
struct iov_iter t;
void __user *uaddr = vhost_vq_meta_fetch(vq,
(u64)(uintptr_t)to, size,
- VHOST_ADDR_DESC);
+ VHOST_ADDR_USED);
if (uaddr)
return __copy_to_user(uaddr, from, size);
@@ -960,21 +959,21 @@ static void vhost_iotlb_notify_vq(struct vhost_dev *d,
spin_unlock(&d->iotlb_lock);
}
-static int umem_access_ok(u64 uaddr, u64 size, int access)
+static bool umem_access_ok(u64 uaddr, u64 size, int access)
{
unsigned long a = uaddr;
/* Make sure 64 bit math will not overflow. */
if (vhost_overflow(uaddr, size))
- return -EFAULT;
+ return false;
if ((access & VHOST_ACCESS_RO) &&
!access_ok(VERIFY_READ, (void __user *)a, size))
- return -EFAULT;
+ return false;
if ((access & VHOST_ACCESS_WO) &&
!access_ok(VERIFY_WRITE, (void __user *)a, size))
- return -EFAULT;
- return 0;
+ return false;
+ return true;
}
static int vhost_process_iotlb_msg(struct vhost_dev *dev,
@@ -989,7 +988,7 @@ static int vhost_process_iotlb_msg(struct vhost_dev *dev,
ret = -EFAULT;
break;
}
- if (umem_access_ok(msg->uaddr, msg->size, msg->perm)) {
+ if (!umem_access_ok(msg->uaddr, msg->size, msg->perm)) {
ret = -EFAULT;
break;
}
@@ -1136,10 +1135,10 @@ static int vhost_iotlb_miss(struct vhost_virtqueue *vq, u64 iova, int access)
return 0;
}
-static int vq_access_ok(struct vhost_virtqueue *vq, unsigned int num,
- struct vring_desc __user *desc,
- struct vring_avail __user *avail,
- struct vring_used __user *used)
+static bool vq_access_ok(struct vhost_virtqueue *vq, unsigned int num,
+ struct vring_desc __user *desc,
+ struct vring_avail __user *avail,
+ struct vring_used __user *used)
{
size_t s = vhost_has_feature(vq, VIRTIO_RING_F_EVENT_IDX) ? 2 : 0;
@@ -1162,8 +1161,8 @@ static void vhost_vq_meta_update(struct vhost_virtqueue *vq,
vq->meta_iotlb[type] = node;
}
-static int iotlb_access_ok(struct vhost_virtqueue *vq,
- int access, u64 addr, u64 len, int type)
+static bool iotlb_access_ok(struct vhost_virtqueue *vq,
+ int access, u64 addr, u64 len, int type)
{
const struct vhost_umem_node *node;
struct vhost_umem *umem = vq->iotlb;
@@ -1221,7 +1220,7 @@ EXPORT_SYMBOL_GPL(vq_iotlb_prefetch);
/* Can we log writes? */
/* Caller should have device mutex but not vq mutex */
-int vhost_log_access_ok(struct vhost_dev *dev)
+bool vhost_log_access_ok(struct vhost_dev *dev)
{
return memory_access_ok(dev, dev->umem, 1);
}
@@ -1229,8 +1228,8 @@ EXPORT_SYMBOL_GPL(vhost_log_access_ok);
/* Verify access for write logging. */
/* Caller should have vq mutex and device mutex */
-static int vq_log_access_ok(struct vhost_virtqueue *vq,
- void __user *log_base)
+static bool vq_log_access_ok(struct vhost_virtqueue *vq,
+ void __user *log_base)
{
size_t s = vhost_has_feature(vq, VIRTIO_RING_F_EVENT_IDX) ? 2 : 0;
@@ -1243,16 +1242,16 @@ static int vq_log_access_ok(struct vhost_virtqueue *vq,
/* Can we start vq? */
/* Caller should have vq mutex and device mutex */
-int vhost_vq_access_ok(struct vhost_virtqueue *vq)
+bool vhost_vq_access_ok(struct vhost_virtqueue *vq)
{
- if (vq->iotlb) {
- /* When device IOTLB was used, the access validation
- * will be validated during prefetching.
- */
- return 1;
- }
- return vq_access_ok(vq, vq->num, vq->desc, vq->avail, vq->used) &&
- vq_log_access_ok(vq, vq->log_base);
+ if (!vq_log_access_ok(vq, vq->log_base))
+ return false;
+
+ /* Access validation occurs at prefetch time with IOTLB */
+ if (vq->iotlb)
+ return true;
+
+ return vq_access_ok(vq, vq->num, vq->desc, vq->avail, vq->used);
}
EXPORT_SYMBOL_GPL(vhost_vq_access_ok);
@@ -1337,7 +1336,7 @@ err:
return -EFAULT;
}
-long vhost_vring_ioctl(struct vhost_dev *d, int ioctl, void __user *argp)
+long vhost_vring_ioctl(struct vhost_dev *d, unsigned int ioctl, void __user *argp)
{
struct file *eventfp, *filep = NULL;
bool pollstart = false, pollstop = false;
diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h
index ac4b6056f19a..6c844b90a168 100644
--- a/drivers/vhost/vhost.h
+++ b/drivers/vhost/vhost.h
@@ -45,7 +45,7 @@ void vhost_poll_stop(struct vhost_poll *poll);
void vhost_poll_flush(struct vhost_poll *poll);
void vhost_poll_queue(struct vhost_poll *poll);
void vhost_work_flush(struct vhost_dev *dev, struct vhost_work *work);
-long vhost_vring_ioctl(struct vhost_dev *d, int ioctl, void __user *argp);
+long vhost_vring_ioctl(struct vhost_dev *d, unsigned int ioctl, void __user *argp);
struct vhost_log {
u64 addr;
@@ -177,9 +177,9 @@ void vhost_dev_reset_owner(struct vhost_dev *, struct vhost_umem *);
void vhost_dev_cleanup(struct vhost_dev *);
void vhost_dev_stop(struct vhost_dev *);
long vhost_dev_ioctl(struct vhost_dev *, unsigned int ioctl, void __user *argp);
-long vhost_vring_ioctl(struct vhost_dev *d, int ioctl, void __user *argp);
-int vhost_vq_access_ok(struct vhost_virtqueue *vq);
-int vhost_log_access_ok(struct vhost_dev *);
+long vhost_vring_ioctl(struct vhost_dev *d, unsigned int ioctl, void __user *argp);
+bool vhost_vq_access_ok(struct vhost_virtqueue *vq);
+bool vhost_log_access_ok(struct vhost_dev *);
int vhost_get_vq_desc(struct vhost_virtqueue *,
struct iovec iov[], unsigned int iov_count,
diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c
index 0d14e2ff19f1..34bc3ab40c6d 100644
--- a/drivers/vhost/vsock.c
+++ b/drivers/vhost/vsock.c
@@ -61,9 +61,9 @@ static struct vhost_vsock *__vhost_vsock_get(u32 guest_cid)
if (other_cid == 0)
continue;
- if (other_cid == guest_cid) {
+ if (other_cid == guest_cid)
return vsock;
- }
+
}
return NULL;
@@ -699,12 +699,23 @@ static long vhost_vsock_dev_ioctl(struct file *f, unsigned int ioctl,
}
}
+#ifdef CONFIG_COMPAT
+static long vhost_vsock_dev_compat_ioctl(struct file *f, unsigned int ioctl,
+ unsigned long arg)
+{
+ return vhost_vsock_dev_ioctl(f, ioctl, (unsigned long)compat_ptr(arg));
+}
+#endif
+
static const struct file_operations vhost_vsock_fops = {
.owner = THIS_MODULE,
.open = vhost_vsock_dev_open,
.release = vhost_vsock_dev_release,
.llseek = noop_llseek,
.unlocked_ioctl = vhost_vsock_dev_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = vhost_vsock_dev_compat_ioctl,
+#endif
};
static struct miscdevice vhost_vsock_misc = {
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 3c20af999893..83d3d271ca15 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -3,12 +3,10 @@
#
menu "Graphics support"
- depends on HAS_IOMEM
-config HAVE_FB_ATMEL
- bool
+if HAS_IOMEM
-config SH_LCD_MIPI_DSI
+config HAVE_FB_ATMEL
bool
source "drivers/char/agp/Kconfig"
@@ -36,6 +34,8 @@ config VIDEOMODE_HELPERS
config HDMI
bool
+endif # HAS_IOMEM
+
if VT
source "drivers/video/console/Kconfig"
endif
diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig
index 7f1f1fbcef9e..4110ba7d7ca9 100644
--- a/drivers/video/console/Kconfig
+++ b/drivers/video/console/Kconfig
@@ -6,10 +6,9 @@ menu "Console display driver support"
config VGA_CONSOLE
bool "VGA text console" if EXPERT || !X86
- depends on !4xx && !PPC_8xx && !SPARC && !M68K && !PARISC && !FRV && \
- !SUPERH && !BLACKFIN && !AVR32 && !MN10300 && !CRIS && \
+ depends on !4xx && !PPC_8xx && !SPARC && !M68K && !PARISC && !SUPERH && \
(!ARM || ARCH_FOOTBRIDGE || ARCH_INTEGRATOR || ARCH_NETWINDER) && \
- !ARM64 && !ARC && !MICROBLAZE && !OPENRISC
+ !ARM64 && !ARC && !MICROBLAZE && !OPENRISC && !NDS32 && !S390
default y
help
Saying Y here will allow you to use Linux in text mode through a
@@ -85,7 +84,7 @@ config MDA_CONSOLE
config SGI_NEWPORT_CONSOLE
tristate "SGI Newport Console support"
- depends on SGI_IP22
+ depends on SGI_IP22 && HAS_IOMEM
select FONT_SUPPORT
help
Say Y here if you want the console on the Newport aka XL graphics
@@ -153,7 +152,7 @@ config FRAMEBUFFER_CONSOLE_ROTATION
config STI_CONSOLE
bool "STI text console"
- depends on PARISC
+ depends on PARISC && HAS_IOMEM
select FONT_SUPPORT
default y
help
diff --git a/drivers/video/console/dummycon.c b/drivers/video/console/dummycon.c
index b90ef96e43d6..f2eafe2ed980 100644
--- a/drivers/video/console/dummycon.c
+++ b/drivers/video/console/dummycon.c
@@ -41,12 +41,47 @@ static void dummycon_init(struct vc_data *vc, int init)
vc_resize(vc, DUMMY_COLUMNS, DUMMY_ROWS);
}
-static int dummycon_dummy(void)
+static void dummycon_deinit(struct vc_data *vc) { }
+static void dummycon_clear(struct vc_data *vc, int sy, int sx, int height,
+ int width) { }
+static void dummycon_putc(struct vc_data *vc, int c, int ypos, int xpos) { }
+static void dummycon_putcs(struct vc_data *vc, const unsigned short *s,
+ int count, int ypos, int xpos) { }
+static void dummycon_cursor(struct vc_data *vc, int mode) { }
+
+static bool dummycon_scroll(struct vc_data *vc, unsigned int top,
+ unsigned int bottom, enum con_scroll dir,
+ unsigned int lines)
+{
+ return false;
+}
+
+static int dummycon_switch(struct vc_data *vc)
{
- return 0;
+ return 0;
}
-#define DUMMY (void *)dummycon_dummy
+static int dummycon_blank(struct vc_data *vc, int blank, int mode_switch)
+{
+ return 0;
+}
+
+static int dummycon_font_set(struct vc_data *vc, struct console_font *font,
+ unsigned int flags)
+{
+ return 0;
+}
+
+static int dummycon_font_default(struct vc_data *vc,
+ struct console_font *font, char *name)
+{
+ return 0;
+}
+
+static int dummycon_font_copy(struct vc_data *vc, int con)
+{
+ return 0;
+}
/*
* The console `switch' structure for the dummy console
@@ -55,19 +90,19 @@ static int dummycon_dummy(void)
*/
const struct consw dummy_con = {
- .owner = THIS_MODULE,
- .con_startup = dummycon_startup,
- .con_init = dummycon_init,
- .con_deinit = DUMMY,
- .con_clear = DUMMY,
- .con_putc = DUMMY,
- .con_putcs = DUMMY,
- .con_cursor = DUMMY,
- .con_scroll = DUMMY,
- .con_switch = DUMMY,
- .con_blank = DUMMY,
- .con_font_set = DUMMY,
- .con_font_default = DUMMY,
- .con_font_copy = DUMMY,
+ .owner = THIS_MODULE,
+ .con_startup = dummycon_startup,
+ .con_init = dummycon_init,
+ .con_deinit = dummycon_deinit,
+ .con_clear = dummycon_clear,
+ .con_putc = dummycon_putc,
+ .con_putcs = dummycon_putcs,
+ .con_cursor = dummycon_cursor,
+ .con_scroll = dummycon_scroll,
+ .con_switch = dummycon_switch,
+ .con_blank = dummycon_blank,
+ .con_font_set = dummycon_font_set,
+ .con_font_default = dummycon_font_default,
+ .con_font_copy = dummycon_font_copy,
};
EXPORT_SYMBOL_GPL(dummy_con);
diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c
index 42d02a206059..7f2526b43b33 100644
--- a/drivers/video/console/newport_con.c
+++ b/drivers/video/console/newport_con.c
@@ -673,12 +673,12 @@ static bool newport_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
return true;
}
-static int newport_dummy(struct vc_data *c)
+static int newport_set_origin(struct vc_data *vc)
{
return 0;
}
-#define DUMMY (void *) newport_dummy
+static void newport_save_screen(struct vc_data *vc) { }
const struct consw newport_con = {
.owner = THIS_MODULE,
@@ -694,8 +694,8 @@ const struct consw newport_con = {
.con_blank = newport_blank,
.con_font_set = newport_font_set,
.con_font_default = newport_font_default,
- .con_set_origin = DUMMY,
- .con_save_screen = DUMMY
+ .con_set_origin = newport_set_origin,
+ .con_save_screen = newport_save_screen
};
static int newport_probe(struct gio_device *dev,
diff --git a/drivers/video/console/sticore.c b/drivers/video/console/sticore.c
index d1d3796773aa..08b822656846 100644
--- a/drivers/video/console/sticore.c
+++ b/drivers/video/console/sticore.c
@@ -827,10 +827,8 @@ static struct sti_struct *sti_try_rom_generic(unsigned long address,
}
sti = kzalloc(sizeof(*sti), GFP_KERNEL);
- if (!sti) {
- printk(KERN_ERR "Not enough memory !\n");
+ if (!sti)
return NULL;
- }
spin_lock_init(&sti->lock);
diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c
index a17ba1465815..f09e17b60e45 100644
--- a/drivers/video/console/vgacon.c
+++ b/drivers/video/console/vgacon.c
@@ -1272,7 +1272,8 @@ static int vgacon_adjust_height(struct vc_data *vc, unsigned fontheight)
return 0;
}
-static int vgacon_font_set(struct vc_data *c, struct console_font *font, unsigned flags)
+static int vgacon_font_set(struct vc_data *c, struct console_font *font,
+ unsigned int flags)
{
unsigned charcount = font->charcount;
int rc;
@@ -1407,21 +1408,20 @@ static bool vgacon_scroll(struct vc_data *c, unsigned int t, unsigned int b,
* The console `switch' structure for the VGA based console
*/
-static int vgacon_dummy(struct vc_data *c)
-{
- return 0;
-}
-
-#define DUMMY (void *) vgacon_dummy
+static void vgacon_clear(struct vc_data *vc, int sy, int sx, int height,
+ int width) { }
+static void vgacon_putc(struct vc_data *vc, int c, int ypos, int xpos) { }
+static void vgacon_putcs(struct vc_data *vc, const unsigned short *s,
+ int count, int ypos, int xpos) { }
const struct consw vga_con = {
.owner = THIS_MODULE,
.con_startup = vgacon_startup,
.con_init = vgacon_init,
.con_deinit = vgacon_deinit,
- .con_clear = DUMMY,
- .con_putc = DUMMY,
- .con_putcs = DUMMY,
+ .con_clear = vgacon_clear,
+ .con_putc = vgacon_putc,
+ .con_putcs = vgacon_putcs,
.con_cursor = vgacon_cursor,
.con_scroll = vgacon_scroll,
.con_switch = vgacon_switch,
diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
index 11e699f1062b..d94254263ea5 100644
--- a/drivers/video/fbdev/Kconfig
+++ b/drivers/video/fbdev/Kconfig
@@ -580,109 +580,6 @@ config FB_VGA16
To compile this driver as a module, choose M here: the
module will be called vga16fb.
-config FB_BF54X_LQ043
- tristate "SHARP LQ043 TFT LCD (BF548 EZKIT)"
- depends on FB && (BF54x) && !BF542
- select FB_CFB_FILLRECT
- select FB_CFB_COPYAREA
- select FB_CFB_IMAGEBLIT
- help
- This is the framebuffer device driver for a SHARP LQ043T1DG01 TFT LCD
-
-config FB_BFIN_T350MCQB
- tristate "Varitronix COG-T350MCQB TFT LCD display (BF527 EZKIT)"
- depends on FB && BLACKFIN
- select BFIN_GPTIMERS
- select FB_CFB_FILLRECT
- select FB_CFB_COPYAREA
- select FB_CFB_IMAGEBLIT
- help
- This is the framebuffer device driver for a Varitronix VL-PS-COG-T350MCQB-01 display TFT LCD
- This display is a QVGA 320x240 24-bit RGB display interfaced by an 8-bit wide PPI
- It uses PPI[0..7] PPI_FS1, PPI_FS2 and PPI_CLK.
-
-config FB_BFIN_LQ035Q1
- tristate "SHARP LQ035Q1DH02 TFT LCD"
- depends on FB && BLACKFIN && SPI
- select FB_CFB_FILLRECT
- select FB_CFB_COPYAREA
- select FB_CFB_IMAGEBLIT
- select BFIN_GPTIMERS
- help
- This is the framebuffer device driver for a SHARP LQ035Q1DH02 TFT display found on
- the Blackfin Landscape LCD EZ-Extender Card.
- This display is a QVGA 320x240 18-bit RGB display interfaced by an 16-bit wide PPI
- It uses PPI[0..15] PPI_FS1, PPI_FS2 and PPI_CLK.
-
- To compile this driver as a module, choose M here: the
- module will be called bfin-lq035q1-fb.
-
-config FB_BF537_LQ035
- tristate "SHARP LQ035 TFT LCD (BF537 STAMP)"
- depends on FB && (BF534 || BF536 || BF537) && I2C_BLACKFIN_TWI
- select FB_CFB_FILLRECT
- select FB_CFB_COPYAREA
- select FB_CFB_IMAGEBLIT
- select BFIN_GPTIMERS
- help
- This is the framebuffer device for a SHARP LQ035Q7DB03 TFT LCD
- attached to a BF537.
-
- To compile this driver as a module, choose M here: the
- module will be called bf537-lq035.
-
-config FB_BFIN_7393
- tristate "Blackfin ADV7393 Video encoder"
- depends on FB && BLACKFIN
- select I2C
- select FB_CFB_FILLRECT
- select FB_CFB_COPYAREA
- select FB_CFB_IMAGEBLIT
- help
- This is the framebuffer device for a ADV7393 video encoder
- attached to a Blackfin on the PPI port.
- If your Blackfin board has a ADV7393 select Y.
-
- To compile this driver as a module, choose M here: the
- module will be called bfin_adv7393fb.
-
-choice
- prompt "Video mode support"
- depends on FB_BFIN_7393
- default NTSC
-
-config NTSC
- bool 'NTSC 720x480'
-
-config PAL
- bool 'PAL 720x576'
-
-config NTSC_640x480
- bool 'NTSC 640x480 (Experimental)'
-
-config PAL_640x480
- bool 'PAL 640x480 (Experimental)'
-
-config NTSC_YCBCR
- bool 'NTSC 720x480 YCbCR input'
-
-config PAL_YCBCR
- bool 'PAL 720x576 YCbCR input'
-
-endchoice
-
-choice
- prompt "Size of ADV7393 frame buffer memory Single/Double Size"
- depends on (FB_BFIN_7393)
- default ADV7393_1XMEM
-
-config ADV7393_1XMEM
- bool 'Single'
-
-config ADV7393_2XMEM
- bool 'Double'
-endchoice
-
config FB_STI
tristate "HP STI frame buffer device support"
depends on FB && PARISC
@@ -1156,6 +1053,11 @@ config FB_I810_I2C
bool "Enable DDC Support"
depends on FB_I810 && FB_I810_GTF
select FB_DDC
+ help
+ Add DDC/I2C support for i810fb. This will allow the driver to get
+ display information, especially for monitors with fickle timings.
+
+ If unsure, say Y.
config FB_LE80578
tristate "Intel LE80578 (Vermilion) support"
@@ -2020,8 +1922,7 @@ config FB_TMIO_ACCELL
config FB_S3C
tristate "Samsung S3C framebuffer support"
- depends on FB && (CPU_S3C2416 || ARCH_S3C64XX || \
- ARCH_S5PV210 || ARCH_EXYNOS)
+ depends on FB && (CPU_S3C2416 || ARCH_S3C64XX)
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile
index 115961e0721b..55282a21b500 100644
--- a/drivers/video/fbdev/Makefile
+++ b/drivers/video/fbdev/Makefile
@@ -136,11 +136,6 @@ obj-$(CONFIG_FB_VESA) += vesafb.o
obj-$(CONFIG_FB_EFI) += efifb.o
obj-$(CONFIG_FB_VGA16) += vga16fb.o
obj-$(CONFIG_FB_OF) += offb.o
-obj-$(CONFIG_FB_BF537_LQ035) += bf537-lq035.o
-obj-$(CONFIG_FB_BF54X_LQ043) += bf54x-lq043fb.o
-obj-$(CONFIG_FB_BFIN_LQ035Q1) += bfin-lq035q1-fb.o
-obj-$(CONFIG_FB_BFIN_T350MCQB) += bfin-t350mcqb-fb.o
-obj-$(CONFIG_FB_BFIN_7393) += bfin_adv7393fb.o
obj-$(CONFIG_FB_MX3) += mx3fb.o
obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o
obj-$(CONFIG_FB_MXS) += mxsfb.o
diff --git a/drivers/video/fbdev/amba-clcd.c b/drivers/video/fbdev/amba-clcd.c
index 36d25190b48c..38c1f324ce15 100644
--- a/drivers/video/fbdev/amba-clcd.c
+++ b/drivers/video/fbdev/amba-clcd.c
@@ -967,9 +967,8 @@ static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id)
goto out;
}
- fb = kzalloc(sizeof(struct clcd_fb), GFP_KERNEL);
+ fb = kzalloc(sizeof(*fb), GFP_KERNEL);
if (!fb) {
- printk(KERN_INFO "CLCD: could not allocate new clcd_fb struct\n");
ret = -ENOMEM;
goto free_region;
}
diff --git a/drivers/video/fbdev/atmel_lcdfb.c b/drivers/video/fbdev/atmel_lcdfb.c
index 3dee267d7c75..076d24afbd72 100644
--- a/drivers/video/fbdev/atmel_lcdfb.c
+++ b/drivers/video/fbdev/atmel_lcdfb.c
@@ -18,10 +18,10 @@
#include <linux/delay.h>
#include <linux/backlight.h>
#include <linux/gfp.h>
+#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
-#include <linux/of_gpio.h>
#include <video/of_display_timing.h>
#include <linux/regulator/consumer.h>
#include <video/videomode.h>
@@ -61,8 +61,7 @@ struct atmel_lcdfb_info {
};
struct atmel_lcdfb_power_ctrl_gpio {
- int gpio;
- int active_low;
+ struct gpio_desc *gpiod;
struct list_head list;
};
@@ -1018,7 +1017,7 @@ static void atmel_lcdfb_power_control_gpio(struct atmel_lcdfb_pdata *pdata, int
struct atmel_lcdfb_power_ctrl_gpio *og;
list_for_each_entry(og, &pdata->pwr_gpios, list)
- gpio_set_value(og->gpio, on);
+ gpiod_set_value(og->gpiod, on);
}
static int atmel_lcdfb_of_init(struct atmel_lcdfb_info *sinfo)
@@ -1031,11 +1030,11 @@ static int atmel_lcdfb_of_init(struct atmel_lcdfb_info *sinfo)
struct device_node *display_np;
struct device_node *timings_np;
struct display_timings *timings;
- enum of_gpio_flags flags;
struct atmel_lcdfb_power_ctrl_gpio *og;
bool is_gpio_power = false;
+ struct gpio_desc *gpiod;
int ret = -ENOENT;
- int i, gpio;
+ int i;
sinfo->config = (struct atmel_lcdfb_config*)
of_match_device(atmel_lcdfb_dt_ids, dev)->data;
@@ -1072,28 +1071,22 @@ static int atmel_lcdfb_of_init(struct atmel_lcdfb_info *sinfo)
INIT_LIST_HEAD(&pdata->pwr_gpios);
ret = -ENOMEM;
- for (i = 0; i < of_gpio_named_count(display_np, "atmel,power-control-gpio"); i++) {
- gpio = of_get_named_gpio_flags(display_np, "atmel,power-control-gpio",
- i, &flags);
- if (gpio < 0)
+ for (i = 0; i < gpiod_count(dev, "atmel,power-control"); i++) {
+ gpiod = devm_gpiod_get_index(dev, "atmel,power-control",
+ i, GPIOD_ASIS);
+ if (IS_ERR(gpiod))
continue;
og = devm_kzalloc(dev, sizeof(*og), GFP_KERNEL);
if (!og)
goto put_display_node;
- og->gpio = gpio;
- og->active_low = flags & OF_GPIO_ACTIVE_LOW;
+ og->gpiod = gpiod;
is_gpio_power = true;
- ret = devm_gpio_request(dev, gpio, "lcd-power-control-gpio");
- if (ret) {
- dev_err(dev, "request gpio %d failed\n", gpio);
- goto put_display_node;
- }
- ret = gpio_direction_output(gpio, og->active_low);
+ ret = gpiod_direction_output(gpiod, gpiod_is_active_low(gpiod));
if (ret) {
- dev_err(dev, "set direction output gpio %d failed\n", gpio);
+ dev_err(dev, "set direction output gpio atmel,power-control[%d] failed\n", i);
goto put_display_node;
}
list_add(&og->list, &pdata->pwr_gpios);
diff --git a/drivers/video/fbdev/aty/aty128fb.c b/drivers/video/fbdev/aty/aty128fb.c
index db18474607c9..09b0e558dce8 100644
--- a/drivers/video/fbdev/aty/aty128fb.c
+++ b/drivers/video/fbdev/aty/aty128fb.c
@@ -1716,7 +1716,7 @@ static int aty128fb_setup(char *options)
continue;
}
if(!strncmp(this_opt, "nomtrr", 6)) {
- mtrr = 0;
+ mtrr = false;
continue;
}
#ifdef CONFIG_PPC_PMAC
diff --git a/drivers/video/fbdev/aty/mach64_ct.c b/drivers/video/fbdev/aty/mach64_ct.c
index 7d3bd723d3d5..74a62aa193c0 100644
--- a/drivers/video/fbdev/aty/mach64_ct.c
+++ b/drivers/video/fbdev/aty/mach64_ct.c
@@ -180,7 +180,7 @@ static int aty_dsp_gt(const struct fb_info *info, u32 bpp, struct pll_ct *pll)
dsp_on = ((multiplier << vshift) + divider) / divider;
tmp = ((ras_multiplier << xshift) + ras_divider) / ras_divider;
if (dsp_on < tmp)
- dsp_on = tmp;
+ dsp_on = tmp;
dsp_on = dsp_on + (tmp * 2) + (pll->xclkpagefaultdelay << xshift);
}
diff --git a/drivers/video/fbdev/aty/radeon_base.c b/drivers/video/fbdev/aty/radeon_base.c
index 87608c0b2351..e8594bbaea60 100644
--- a/drivers/video/fbdev/aty/radeon_base.c
+++ b/drivers/video/fbdev/aty/radeon_base.c
@@ -2255,6 +2255,23 @@ static const struct bin_attribute edid2_attr = {
.read = radeon_show_edid2,
};
+static int radeon_kick_out_firmware_fb(struct pci_dev *pdev)
+{
+ struct apertures_struct *ap;
+
+ ap = alloc_apertures(1);
+ if (!ap)
+ return -ENOMEM;
+
+ ap->ranges[0].base = pci_resource_start(pdev, 0);
+ ap->ranges[0].size = pci_resource_len(pdev, 0);
+
+ remove_conflicting_framebuffers(ap, KBUILD_MODNAME, false);
+
+ kfree(ap);
+
+ return 0;
+}
static int radeonfb_pci_register(struct pci_dev *pdev,
const struct pci_device_id *ent)
@@ -2308,6 +2325,10 @@ static int radeonfb_pci_register(struct pci_dev *pdev,
rinfo->fb_base_phys = pci_resource_start (pdev, 0);
rinfo->mmio_base_phys = pci_resource_start (pdev, 2);
+ ret = radeon_kick_out_firmware_fb(pdev);
+ if (ret)
+ return ret;
+
/* request the mem regions */
ret = pci_request_region(pdev, 0, "radeonfb framebuffer");
if (ret < 0) {
diff --git a/drivers/video/fbdev/au1100fb.c b/drivers/video/fbdev/au1100fb.c
index 8de42f617d16..7c9a672e9811 100644
--- a/drivers/video/fbdev/au1100fb.c
+++ b/drivers/video/fbdev/au1100fb.c
@@ -410,18 +410,15 @@ static int au1100fb_setup(struct au1100fb_device *fbdev)
static int au1100fb_drv_probe(struct platform_device *dev)
{
- struct au1100fb_device *fbdev = NULL;
+ struct au1100fb_device *fbdev;
struct resource *regs_res;
unsigned long page;
struct clk *c;
/* Allocate new device private */
- fbdev = devm_kzalloc(&dev->dev, sizeof(struct au1100fb_device),
- GFP_KERNEL);
- if (!fbdev) {
- print_err("fail to allocate device private record");
+ fbdev = devm_kzalloc(&dev->dev, sizeof(*fbdev), GFP_KERNEL);
+ if (!fbdev)
return -ENOMEM;
- }
if (au1100fb_setup(fbdev))
goto failed;
diff --git a/drivers/video/fbdev/bf537-lq035.c b/drivers/video/fbdev/bf537-lq035.c
deleted file mode 100644
index ef29fb425122..000000000000
--- a/drivers/video/fbdev/bf537-lq035.c
+++ /dev/null
@@ -1,891 +0,0 @@
-/*
- * Analog Devices Blackfin(BF537 STAMP) + SHARP TFT LCD.
- * http://docs.blackfin.uclinux.org/doku.php?id=hw:cards:tft-lcd
- *
- * Copyright 2006-2010 Analog Devices Inc.
- * Licensed under the GPL-2.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/delay.h>
-#include <linux/fb.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/gpio.h>
-#include <linux/interrupt.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/device.h>
-#include <linux/backlight.h>
-#include <linux/lcd.h>
-#include <linux/i2c.h>
-#include <linux/spinlock.h>
-#include <linux/dma-mapping.h>
-#include <linux/slab.h>
-#include <linux/platform_device.h>
-
-#include <asm/blackfin.h>
-#include <asm/irq.h>
-#include <asm/dpmc.h>
-#include <asm/dma.h>
-#include <asm/portmux.h>
-
-#define NO_BL 1
-
-#define MAX_BRIGHENESS 95
-#define MIN_BRIGHENESS 5
-#define NBR_PALETTE 256
-
-static const unsigned short ppi_pins[] = {
- P_PPI0_CLK, P_PPI0_D0, P_PPI0_D1, P_PPI0_D2, P_PPI0_D3,
- P_PPI0_D4, P_PPI0_D5, P_PPI0_D6, P_PPI0_D7,
- P_PPI0_D8, P_PPI0_D9, P_PPI0_D10, P_PPI0_D11,
- P_PPI0_D12, P_PPI0_D13, P_PPI0_D14, P_PPI0_D15, 0
-};
-
-static unsigned char *fb_buffer; /* RGB Buffer */
-static unsigned long *dma_desc_table;
-static int t_conf_done, lq035_open_cnt;
-static DEFINE_SPINLOCK(bfin_lq035_lock);
-
-static int landscape;
-module_param(landscape, int, 0);
-MODULE_PARM_DESC(landscape,
- "LANDSCAPE use 320x240 instead of Native 240x320 Resolution");
-
-static int bgr;
-module_param(bgr, int, 0);
-MODULE_PARM_DESC(bgr,
- "BGR use 16-bit BGR-565 instead of RGB-565");
-
-static int nocursor = 1;
-module_param(nocursor, int, 0644);
-MODULE_PARM_DESC(nocursor, "cursor enable/disable");
-
-static unsigned long current_brightness; /* backlight */
-
-/* AD5280 vcomm */
-static unsigned char vcomm_value = 150;
-static struct i2c_client *ad5280_client;
-
-static void set_vcomm(void)
-{
- int nr;
-
- if (!ad5280_client)
- return;
-
- nr = i2c_smbus_write_byte_data(ad5280_client, 0x00, vcomm_value);
- if (nr)
- pr_err("i2c_smbus_write_byte_data fail: %d\n", nr);
-}
-
-static int ad5280_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- int ret;
- if (!i2c_check_functionality(client->adapter,
- I2C_FUNC_SMBUS_BYTE_DATA)) {
- dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
- return -EIO;
- }
-
- ret = i2c_smbus_write_byte_data(client, 0x00, vcomm_value);
- if (ret) {
- dev_err(&client->dev, "write fail: %d\n", ret);
- return ret;
- }
-
- ad5280_client = client;
-
- return 0;
-}
-
-static int ad5280_remove(struct i2c_client *client)
-{
- ad5280_client = NULL;
- return 0;
-}
-
-static const struct i2c_device_id ad5280_id[] = {
- {"bf537-lq035-ad5280", 0},
- {}
-};
-
-MODULE_DEVICE_TABLE(i2c, ad5280_id);
-
-static struct i2c_driver ad5280_driver = {
- .driver = {
- .name = "bf537-lq035-ad5280",
- },
- .probe = ad5280_probe,
- .remove = ad5280_remove,
- .id_table = ad5280_id,
-};
-
-#ifdef CONFIG_PNAV10
-#define MOD GPIO_PH13
-
-#define bfin_write_TIMER_LP_CONFIG bfin_write_TIMER0_CONFIG
-#define bfin_write_TIMER_LP_WIDTH bfin_write_TIMER0_WIDTH
-#define bfin_write_TIMER_LP_PERIOD bfin_write_TIMER0_PERIOD
-#define bfin_read_TIMER_LP_COUNTER bfin_read_TIMER0_COUNTER
-#define TIMDIS_LP TIMDIS0
-#define TIMEN_LP TIMEN0
-
-#define bfin_write_TIMER_SPS_CONFIG bfin_write_TIMER1_CONFIG
-#define bfin_write_TIMER_SPS_WIDTH bfin_write_TIMER1_WIDTH
-#define bfin_write_TIMER_SPS_PERIOD bfin_write_TIMER1_PERIOD
-#define TIMDIS_SPS TIMDIS1
-#define TIMEN_SPS TIMEN1
-
-#define bfin_write_TIMER_SP_CONFIG bfin_write_TIMER5_CONFIG
-#define bfin_write_TIMER_SP_WIDTH bfin_write_TIMER5_WIDTH
-#define bfin_write_TIMER_SP_PERIOD bfin_write_TIMER5_PERIOD
-#define TIMDIS_SP TIMDIS5
-#define TIMEN_SP TIMEN5
-
-#define bfin_write_TIMER_PS_CLS_CONFIG bfin_write_TIMER2_CONFIG
-#define bfin_write_TIMER_PS_CLS_WIDTH bfin_write_TIMER2_WIDTH
-#define bfin_write_TIMER_PS_CLS_PERIOD bfin_write_TIMER2_PERIOD
-#define TIMDIS_PS_CLS TIMDIS2
-#define TIMEN_PS_CLS TIMEN2
-
-#define bfin_write_TIMER_REV_CONFIG bfin_write_TIMER3_CONFIG
-#define bfin_write_TIMER_REV_WIDTH bfin_write_TIMER3_WIDTH
-#define bfin_write_TIMER_REV_PERIOD bfin_write_TIMER3_PERIOD
-#define TIMDIS_REV TIMDIS3
-#define TIMEN_REV TIMEN3
-#define bfin_read_TIMER_REV_COUNTER bfin_read_TIMER3_COUNTER
-
-#define FREQ_PPI_CLK (5*1024*1024) /* PPI_CLK 5MHz */
-
-#define TIMERS {P_TMR0, P_TMR1, P_TMR2, P_TMR3, P_TMR5, 0}
-
-#else
-
-#define UD GPIO_PF13 /* Up / Down */
-#define MOD GPIO_PF10
-#define LBR GPIO_PF14 /* Left Right */
-
-#define bfin_write_TIMER_LP_CONFIG bfin_write_TIMER6_CONFIG
-#define bfin_write_TIMER_LP_WIDTH bfin_write_TIMER6_WIDTH
-#define bfin_write_TIMER_LP_PERIOD bfin_write_TIMER6_PERIOD
-#define bfin_read_TIMER_LP_COUNTER bfin_read_TIMER6_COUNTER
-#define TIMDIS_LP TIMDIS6
-#define TIMEN_LP TIMEN6
-
-#define bfin_write_TIMER_SPS_CONFIG bfin_write_TIMER1_CONFIG
-#define bfin_write_TIMER_SPS_WIDTH bfin_write_TIMER1_WIDTH
-#define bfin_write_TIMER_SPS_PERIOD bfin_write_TIMER1_PERIOD
-#define TIMDIS_SPS TIMDIS1
-#define TIMEN_SPS TIMEN1
-
-#define bfin_write_TIMER_SP_CONFIG bfin_write_TIMER0_CONFIG
-#define bfin_write_TIMER_SP_WIDTH bfin_write_TIMER0_WIDTH
-#define bfin_write_TIMER_SP_PERIOD bfin_write_TIMER0_PERIOD
-#define TIMDIS_SP TIMDIS0
-#define TIMEN_SP TIMEN0
-
-#define bfin_write_TIMER_PS_CLS_CONFIG bfin_write_TIMER7_CONFIG
-#define bfin_write_TIMER_PS_CLS_WIDTH bfin_write_TIMER7_WIDTH
-#define bfin_write_TIMER_PS_CLS_PERIOD bfin_write_TIMER7_PERIOD
-#define TIMDIS_PS_CLS TIMDIS7
-#define TIMEN_PS_CLS TIMEN7
-
-#define bfin_write_TIMER_REV_CONFIG bfin_write_TIMER5_CONFIG
-#define bfin_write_TIMER_REV_WIDTH bfin_write_TIMER5_WIDTH
-#define bfin_write_TIMER_REV_PERIOD bfin_write_TIMER5_PERIOD
-#define TIMDIS_REV TIMDIS5
-#define TIMEN_REV TIMEN5
-#define bfin_read_TIMER_REV_COUNTER bfin_read_TIMER5_COUNTER
-
-#define FREQ_PPI_CLK (6*1000*1000) /* PPI_CLK 6MHz */
-#define TIMERS {P_TMR0, P_TMR1, P_TMR5, P_TMR6, P_TMR7, 0}
-
-#endif
-
-#define LCD_X_RES 240 /* Horizontal Resolution */
-#define LCD_Y_RES 320 /* Vertical Resolution */
-
-#define LCD_BBP 16 /* Bit Per Pixel */
-
-/* the LCD and the DMA start counting differently;
- * since one starts at 0 and the other starts at 1,
- * we have a difference of 1 between START_LINES
- * and U_LINES.
- */
-#define START_LINES 8 /* lines for field flyback or field blanking signal */
-#define U_LINES 9 /* number of undisplayed blanking lines */
-
-#define FRAMES_PER_SEC (60)
-
-#define DCLKS_PER_FRAME (FREQ_PPI_CLK/FRAMES_PER_SEC)
-#define DCLKS_PER_LINE (DCLKS_PER_FRAME/(LCD_Y_RES+U_LINES))
-
-#define PPI_CONFIG_VALUE (PORT_DIR|XFR_TYPE|DLEN_16|POLS)
-#define PPI_DELAY_VALUE (0)
-#define TIMER_CONFIG (PWM_OUT|PERIOD_CNT|TIN_SEL|CLK_SEL)
-
-#define ACTIVE_VIDEO_MEM_OFFSET (LCD_X_RES*START_LINES*(LCD_BBP/8))
-#define ACTIVE_VIDEO_MEM_SIZE (LCD_Y_RES*LCD_X_RES*(LCD_BBP/8))
-#define TOTAL_VIDEO_MEM_SIZE ((LCD_Y_RES+U_LINES)*LCD_X_RES*(LCD_BBP/8))
-#define TOTAL_DMA_DESC_SIZE (2 * sizeof(u32) * (LCD_Y_RES + U_LINES))
-
-static void start_timers(void) /* CHECK with HW */
-{
- unsigned long flags;
-
- local_irq_save(flags);
-
- bfin_write_TIMER_ENABLE(TIMEN_REV);
- SSYNC();
-
- while (bfin_read_TIMER_REV_COUNTER() <= 11)
- continue;
- bfin_write_TIMER_ENABLE(TIMEN_LP);
- SSYNC();
-
- while (bfin_read_TIMER_LP_COUNTER() < 3)
- continue;
- bfin_write_TIMER_ENABLE(TIMEN_SP|TIMEN_SPS|TIMEN_PS_CLS);
- SSYNC();
- t_conf_done = 1;
- local_irq_restore(flags);
-}
-
-static void config_timers(void)
-{
- /* Stop timers */
- bfin_write_TIMER_DISABLE(TIMDIS_SP|TIMDIS_SPS|TIMDIS_REV|
- TIMDIS_LP|TIMDIS_PS_CLS);
- SSYNC();
-
- /* LP, timer 6 */
- bfin_write_TIMER_LP_CONFIG(TIMER_CONFIG|PULSE_HI);
- bfin_write_TIMER_LP_WIDTH(1);
-
- bfin_write_TIMER_LP_PERIOD(DCLKS_PER_LINE);
- SSYNC();
-
- /* SPS, timer 1 */
- bfin_write_TIMER_SPS_CONFIG(TIMER_CONFIG|PULSE_HI);
- bfin_write_TIMER_SPS_WIDTH(DCLKS_PER_LINE*2);
- bfin_write_TIMER_SPS_PERIOD((DCLKS_PER_LINE * (LCD_Y_RES+U_LINES)));
- SSYNC();
-
- /* SP, timer 0 */
- bfin_write_TIMER_SP_CONFIG(TIMER_CONFIG|PULSE_HI);
- bfin_write_TIMER_SP_WIDTH(1);
- bfin_write_TIMER_SP_PERIOD(DCLKS_PER_LINE);
- SSYNC();
-
- /* PS & CLS, timer 7 */
- bfin_write_TIMER_PS_CLS_CONFIG(TIMER_CONFIG);
- bfin_write_TIMER_PS_CLS_WIDTH(LCD_X_RES + START_LINES);
- bfin_write_TIMER_PS_CLS_PERIOD(DCLKS_PER_LINE);
-
- SSYNC();
-
-#ifdef NO_BL
- /* REV, timer 5 */
- bfin_write_TIMER_REV_CONFIG(TIMER_CONFIG|PULSE_HI);
-
- bfin_write_TIMER_REV_WIDTH(DCLKS_PER_LINE);
- bfin_write_TIMER_REV_PERIOD(DCLKS_PER_LINE*2);
-
- SSYNC();
-#endif
-}
-
-static void config_ppi(void)
-{
- bfin_write_PPI_DELAY(PPI_DELAY_VALUE);
- bfin_write_PPI_COUNT(LCD_X_RES-1);
- /* 0x10 -> PORT_CFG -> 2 or 3 frame syncs */
- bfin_write_PPI_CONTROL((PPI_CONFIG_VALUE|0x10) & (~POLS));
-}
-
-static int config_dma(void)
-{
- u32 i;
-
- if (landscape) {
-
- for (i = 0; i < U_LINES; ++i) {
- /* blanking lines point to first line of fb_buffer */
- dma_desc_table[2*i] = (unsigned long)&dma_desc_table[2*i+2];
- dma_desc_table[2*i+1] = (unsigned long)fb_buffer;
- }
-
- for (i = U_LINES; i < U_LINES + LCD_Y_RES; ++i) {
- /* visible lines */
- dma_desc_table[2*i] = (unsigned long)&dma_desc_table[2*i+2];
- dma_desc_table[2*i+1] = (unsigned long)fb_buffer +
- (LCD_Y_RES+U_LINES-1-i)*2;
- }
-
- /* last descriptor points to first */
- dma_desc_table[2*(LCD_Y_RES+U_LINES-1)] = (unsigned long)&dma_desc_table[0];
-
- set_dma_x_count(CH_PPI, LCD_X_RES);
- set_dma_x_modify(CH_PPI, LCD_Y_RES * (LCD_BBP / 8));
- set_dma_y_count(CH_PPI, 0);
- set_dma_y_modify(CH_PPI, 0);
- set_dma_next_desc_addr(CH_PPI, (void *)dma_desc_table[0]);
- set_dma_config(CH_PPI, DMAFLOW_LARGE | NDSIZE_4 | WDSIZE_16);
-
- } else {
-
- set_dma_config(CH_PPI, set_bfin_dma_config(DIR_READ,
- DMA_FLOW_AUTO,
- INTR_DISABLE,
- DIMENSION_2D,
- DATA_SIZE_16,
- DMA_NOSYNC_KEEP_DMA_BUF));
- set_dma_x_count(CH_PPI, LCD_X_RES);
- set_dma_x_modify(CH_PPI, LCD_BBP / 8);
- set_dma_y_count(CH_PPI, LCD_Y_RES+U_LINES);
- set_dma_y_modify(CH_PPI, LCD_BBP / 8);
- set_dma_start_addr(CH_PPI, (unsigned long) fb_buffer);
- }
-
- return 0;
-}
-
-static int request_ports(void)
-{
- u16 tmr_req[] = TIMERS;
-
- /*
- UD: PF13
- MOD: PF10
- LBR: PF14
- PPI_CLK: PF15
- */
-
- if (peripheral_request_list(ppi_pins, KBUILD_MODNAME)) {
- pr_err("requesting PPI peripheral failed\n");
- return -EBUSY;
- }
-
- if (peripheral_request_list(tmr_req, KBUILD_MODNAME)) {
- peripheral_free_list(ppi_pins);
- pr_err("requesting timer peripheral failed\n");
- return -EBUSY;
- }
-
-#if (defined(UD) && defined(LBR))
- if (gpio_request_one(UD, GPIOF_OUT_INIT_LOW, KBUILD_MODNAME)) {
- pr_err("requesting GPIO %d failed\n", UD);
- return -EBUSY;
- }
-
- if (gpio_request_one(LBR, GPIOF_OUT_INIT_HIGH, KBUILD_MODNAME)) {
- pr_err("requesting GPIO %d failed\n", LBR);
- gpio_free(UD);
- return -EBUSY;
- }
-#endif
-
- if (gpio_request_one(MOD, GPIOF_OUT_INIT_HIGH, KBUILD_MODNAME)) {
- pr_err("requesting GPIO %d failed\n", MOD);
-#if (defined(UD) && defined(LBR))
- gpio_free(LBR);
- gpio_free(UD);
-#endif
- return -EBUSY;
- }
-
- SSYNC();
- return 0;
-}
-
-static void free_ports(void)
-{
- u16 tmr_req[] = TIMERS;
-
- peripheral_free_list(ppi_pins);
- peripheral_free_list(tmr_req);
-
-#if defined(UD) && defined(LBR)
- gpio_free(LBR);
- gpio_free(UD);
-#endif
- gpio_free(MOD);
-}
-
-static struct fb_info bfin_lq035_fb;
-
-static struct fb_var_screeninfo bfin_lq035_fb_defined = {
- .bits_per_pixel = LCD_BBP,
- .activate = FB_ACTIVATE_TEST,
- .xres = LCD_X_RES, /*default portrait mode RGB*/
- .yres = LCD_Y_RES,
- .xres_virtual = LCD_X_RES,
- .yres_virtual = LCD_Y_RES,
- .height = -1,
- .width = -1,
- .left_margin = 0,
- .right_margin = 0,
- .upper_margin = 0,
- .lower_margin = 0,
- .red = {11, 5, 0},
- .green = {5, 6, 0},
- .blue = {0, 5, 0},
- .transp = {0, 0, 0},
-};
-
-static struct fb_fix_screeninfo bfin_lq035_fb_fix = {
- .id = KBUILD_MODNAME,
- .smem_len = ACTIVE_VIDEO_MEM_SIZE,
- .type = FB_TYPE_PACKED_PIXELS,
- .visual = FB_VISUAL_TRUECOLOR,
- .xpanstep = 0,
- .ypanstep = 0,
- .line_length = LCD_X_RES*(LCD_BBP/8),
- .accel = FB_ACCEL_NONE,
-};
-
-
-static int bfin_lq035_fb_open(struct fb_info *info, int user)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&bfin_lq035_lock, flags);
- lq035_open_cnt++;
- spin_unlock_irqrestore(&bfin_lq035_lock, flags);
-
- if (lq035_open_cnt <= 1) {
- bfin_write_PPI_CONTROL(0);
- SSYNC();
-
- set_vcomm();
- config_dma();
- config_ppi();
-
- /* start dma */
- enable_dma(CH_PPI);
- SSYNC();
- bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() | PORT_EN);
- SSYNC();
-
- if (!t_conf_done) {
- config_timers();
- start_timers();
- }
- /* gpio_set_value(MOD,1); */
- }
-
- return 0;
-}
-
-static int bfin_lq035_fb_release(struct fb_info *info, int user)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&bfin_lq035_lock, flags);
- lq035_open_cnt--;
- spin_unlock_irqrestore(&bfin_lq035_lock, flags);
-
-
- if (lq035_open_cnt <= 0) {
-
- bfin_write_PPI_CONTROL(0);
- SSYNC();
-
- disable_dma(CH_PPI);
- }
-
- return 0;
-}
-
-
-static int bfin_lq035_fb_check_var(struct fb_var_screeninfo *var,
- struct fb_info *info)
-{
- switch (var->bits_per_pixel) {
- case 16:/* DIRECTCOLOUR, 64k */
- var->red.offset = info->var.red.offset;
- var->green.offset = info->var.green.offset;
- var->blue.offset = info->var.blue.offset;
- var->red.length = info->var.red.length;
- var->green.length = info->var.green.length;
- var->blue.length = info->var.blue.length;
- var->transp.offset = 0;
- var->transp.length = 0;
- var->transp.msb_right = 0;
- var->red.msb_right = 0;
- var->green.msb_right = 0;
- var->blue.msb_right = 0;
- break;
- default:
- pr_debug("%s: depth not supported: %u BPP\n", __func__,
- var->bits_per_pixel);
- return -EINVAL;
- }
-
- if (info->var.xres != var->xres ||
- info->var.yres != var->yres ||
- info->var.xres_virtual != var->xres_virtual ||
- info->var.yres_virtual != var->yres_virtual) {
- pr_debug("%s: Resolution not supported: X%u x Y%u\n",
- __func__, var->xres, var->yres);
- return -EINVAL;
- }
-
- /*
- * Memory limit
- */
-
- if ((info->fix.line_length * var->yres_virtual) > info->fix.smem_len) {
- pr_debug("%s: Memory Limit requested yres_virtual = %u\n",
- __func__, var->yres_virtual);
- return -ENOMEM;
- }
-
- return 0;
-}
-
-static int bfin_lq035_fb_cursor(struct fb_info *info, struct fb_cursor *cursor)
-{
- if (nocursor)
- return 0;
- else
- return -EINVAL; /* just to force soft_cursor() call */
-}
-
-static int bfin_lq035_fb_setcolreg(u_int regno, u_int red, u_int green,
- u_int blue, u_int transp,
- struct fb_info *info)
-{
- if (regno >= NBR_PALETTE)
- return -EINVAL;
-
- if (info->var.grayscale)
- /* grayscale = 0.30*R + 0.59*G + 0.11*B */
- red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
-
- if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
-
- u32 value;
- /* Place color in the pseudopalette */
- if (regno > 16)
- return -EINVAL;
-
- red >>= (16 - info->var.red.length);
- green >>= (16 - info->var.green.length);
- blue >>= (16 - info->var.blue.length);
-
- value = (red << info->var.red.offset) |
- (green << info->var.green.offset)|
- (blue << info->var.blue.offset);
- value &= 0xFFFF;
-
- ((u32 *) (info->pseudo_palette))[regno] = value;
-
- }
-
- return 0;
-}
-
-static struct fb_ops bfin_lq035_fb_ops = {
- .owner = THIS_MODULE,
- .fb_open = bfin_lq035_fb_open,
- .fb_release = bfin_lq035_fb_release,
- .fb_check_var = bfin_lq035_fb_check_var,
- .fb_fillrect = cfb_fillrect,
- .fb_copyarea = cfb_copyarea,
- .fb_imageblit = cfb_imageblit,
- .fb_cursor = bfin_lq035_fb_cursor,
- .fb_setcolreg = bfin_lq035_fb_setcolreg,
-};
-
-static int bl_get_brightness(struct backlight_device *bd)
-{
- return current_brightness;
-}
-
-static const struct backlight_ops bfin_lq035fb_bl_ops = {
- .get_brightness = bl_get_brightness,
-};
-
-static struct backlight_device *bl_dev;
-
-static int bfin_lcd_get_power(struct lcd_device *dev)
-{
- return 0;
-}
-
-static int bfin_lcd_set_power(struct lcd_device *dev, int power)
-{
- return 0;
-}
-
-static int bfin_lcd_get_contrast(struct lcd_device *dev)
-{
- return (int)vcomm_value;
-}
-
-static int bfin_lcd_set_contrast(struct lcd_device *dev, int contrast)
-{
- if (contrast > 255)
- contrast = 255;
- if (contrast < 0)
- contrast = 0;
-
- vcomm_value = (unsigned char)contrast;
- set_vcomm();
- return 0;
-}
-
-static int bfin_lcd_check_fb(struct lcd_device *lcd, struct fb_info *fi)
-{
- if (!fi || (fi == &bfin_lq035_fb))
- return 1;
- return 0;
-}
-
-static struct lcd_ops bfin_lcd_ops = {
- .get_power = bfin_lcd_get_power,
- .set_power = bfin_lcd_set_power,
- .get_contrast = bfin_lcd_get_contrast,
- .set_contrast = bfin_lcd_set_contrast,
- .check_fb = bfin_lcd_check_fb,
-};
-
-static struct lcd_device *lcd_dev;
-
-static int bfin_lq035_probe(struct platform_device *pdev)
-{
- struct backlight_properties props;
- dma_addr_t dma_handle;
- int ret;
-
- if (request_dma(CH_PPI, KBUILD_MODNAME)) {
- pr_err("couldn't request PPI DMA\n");
- return -EFAULT;
- }
-
- if (request_ports()) {
- pr_err("couldn't request gpio port\n");
- ret = -EFAULT;
- goto out_ports;
- }
-
- fb_buffer = dma_alloc_coherent(NULL, TOTAL_VIDEO_MEM_SIZE,
- &dma_handle, GFP_KERNEL);
- if (fb_buffer == NULL) {
- pr_err("couldn't allocate dma buffer\n");
- ret = -ENOMEM;
- goto out_dma_coherent;
- }
-
- if (L1_DATA_A_LENGTH)
- dma_desc_table = l1_data_sram_zalloc(TOTAL_DMA_DESC_SIZE);
- else
- dma_desc_table = dma_alloc_coherent(NULL, TOTAL_DMA_DESC_SIZE,
- &dma_handle, 0);
-
- if (dma_desc_table == NULL) {
- pr_err("couldn't allocate dma descriptor\n");
- ret = -ENOMEM;
- goto out_table;
- }
-
- bfin_lq035_fb.screen_base = (void *)fb_buffer;
- bfin_lq035_fb_fix.smem_start = (int)fb_buffer;
- if (landscape) {
- bfin_lq035_fb_defined.xres = LCD_Y_RES;
- bfin_lq035_fb_defined.yres = LCD_X_RES;
- bfin_lq035_fb_defined.xres_virtual = LCD_Y_RES;
- bfin_lq035_fb_defined.yres_virtual = LCD_X_RES;
-
- bfin_lq035_fb_fix.line_length = LCD_Y_RES*(LCD_BBP/8);
- } else {
- bfin_lq035_fb.screen_base += ACTIVE_VIDEO_MEM_OFFSET;
- bfin_lq035_fb_fix.smem_start += ACTIVE_VIDEO_MEM_OFFSET;
- }
-
- bfin_lq035_fb_defined.green.msb_right = 0;
- bfin_lq035_fb_defined.red.msb_right = 0;
- bfin_lq035_fb_defined.blue.msb_right = 0;
- bfin_lq035_fb_defined.green.offset = 5;
- bfin_lq035_fb_defined.green.length = 6;
- bfin_lq035_fb_defined.red.length = 5;
- bfin_lq035_fb_defined.blue.length = 5;
-
- if (bgr) {
- bfin_lq035_fb_defined.red.offset = 0;
- bfin_lq035_fb_defined.blue.offset = 11;
- } else {
- bfin_lq035_fb_defined.red.offset = 11;
- bfin_lq035_fb_defined.blue.offset = 0;
- }
-
- bfin_lq035_fb.fbops = &bfin_lq035_fb_ops;
- bfin_lq035_fb.var = bfin_lq035_fb_defined;
-
- bfin_lq035_fb.fix = bfin_lq035_fb_fix;
- bfin_lq035_fb.flags = FBINFO_DEFAULT;
-
-
- bfin_lq035_fb.pseudo_palette = devm_kzalloc(&pdev->dev,
- sizeof(u32) * 16,
- GFP_KERNEL);
- if (bfin_lq035_fb.pseudo_palette == NULL) {
- pr_err("failed to allocate pseudo_palette\n");
- ret = -ENOMEM;
- goto out_table;
- }
-
- if (fb_alloc_cmap(&bfin_lq035_fb.cmap, NBR_PALETTE, 0) < 0) {
- pr_err("failed to allocate colormap (%d entries)\n",
- NBR_PALETTE);
- ret = -EFAULT;
- goto out_table;
- }
-
- if (register_framebuffer(&bfin_lq035_fb) < 0) {
- pr_err("unable to register framebuffer\n");
- ret = -EINVAL;
- goto out_reg;
- }
-
- i2c_add_driver(&ad5280_driver);
-
- memset(&props, 0, sizeof(props));
- props.type = BACKLIGHT_RAW;
- props.max_brightness = MAX_BRIGHENESS;
- bl_dev = backlight_device_register("bf537-bl", NULL, NULL,
- &bfin_lq035fb_bl_ops, &props);
-
- lcd_dev = lcd_device_register(KBUILD_MODNAME, &pdev->dev, NULL,
- &bfin_lcd_ops);
- if (IS_ERR(lcd_dev)) {
- pr_err("unable to register lcd\n");
- ret = PTR_ERR(lcd_dev);
- goto out_lcd;
- }
- lcd_dev->props.max_contrast = 255,
-
- pr_info("initialized");
-
- return 0;
-out_lcd:
- unregister_framebuffer(&bfin_lq035_fb);
-out_reg:
- fb_dealloc_cmap(&bfin_lq035_fb.cmap);
-out_table:
- dma_free_coherent(NULL, TOTAL_VIDEO_MEM_SIZE, fb_buffer, 0);
- fb_buffer = NULL;
-out_dma_coherent:
- free_ports();
-out_ports:
- free_dma(CH_PPI);
- return ret;
-}
-
-static int bfin_lq035_remove(struct platform_device *pdev)
-{
- if (fb_buffer != NULL)
- dma_free_coherent(NULL, TOTAL_VIDEO_MEM_SIZE, fb_buffer, 0);
-
- if (L1_DATA_A_LENGTH)
- l1_data_sram_free(dma_desc_table);
- else
- dma_free_coherent(NULL, TOTAL_DMA_DESC_SIZE, NULL, 0);
-
- bfin_write_TIMER_DISABLE(TIMEN_SP|TIMEN_SPS|TIMEN_PS_CLS|
- TIMEN_LP|TIMEN_REV);
- t_conf_done = 0;
-
- free_dma(CH_PPI);
-
-
- fb_dealloc_cmap(&bfin_lq035_fb.cmap);
-
-
- lcd_device_unregister(lcd_dev);
- backlight_device_unregister(bl_dev);
-
- unregister_framebuffer(&bfin_lq035_fb);
- i2c_del_driver(&ad5280_driver);
-
- free_ports();
-
- pr_info("unregistered LCD driver\n");
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int bfin_lq035_suspend(struct platform_device *pdev, pm_message_t state)
-{
- if (lq035_open_cnt > 0) {
- bfin_write_PPI_CONTROL(0);
- SSYNC();
- disable_dma(CH_PPI);
- }
-
- return 0;
-}
-
-static int bfin_lq035_resume(struct platform_device *pdev)
-{
- if (lq035_open_cnt > 0) {
- bfin_write_PPI_CONTROL(0);
- SSYNC();
-
- config_dma();
- config_ppi();
-
- enable_dma(CH_PPI);
- bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() | PORT_EN);
- SSYNC();
-
- config_timers();
- start_timers();
- } else {
- t_conf_done = 0;
- }
-
- return 0;
-}
-#else
-# define bfin_lq035_suspend NULL
-# define bfin_lq035_resume NULL
-#endif
-
-static struct platform_driver bfin_lq035_driver = {
- .probe = bfin_lq035_probe,
- .remove = bfin_lq035_remove,
- .suspend = bfin_lq035_suspend,
- .resume = bfin_lq035_resume,
- .driver = {
- .name = KBUILD_MODNAME,
- },
-};
-
-static int __init bfin_lq035_driver_init(void)
-{
- request_module("i2c-bfin-twi");
- return platform_driver_register(&bfin_lq035_driver);
-}
-module_init(bfin_lq035_driver_init);
-
-static void __exit bfin_lq035_driver_cleanup(void)
-{
- platform_driver_unregister(&bfin_lq035_driver);
-}
-module_exit(bfin_lq035_driver_cleanup);
-
-MODULE_DESCRIPTION("SHARP LQ035Q7DB03 TFT LCD Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/video/fbdev/bf54x-lq043fb.c b/drivers/video/fbdev/bf54x-lq043fb.c
deleted file mode 100644
index 8f1f97c75619..000000000000
--- a/drivers/video/fbdev/bf54x-lq043fb.c
+++ /dev/null
@@ -1,764 +0,0 @@
-/*
- * File: drivers/video/bf54x-lq043.c
- * Based on:
- * Author: Michael Hennerich <hennerich@blackfin.uclinux.org>
- *
- * Created:
- * Description: ADSP-BF54x Framebuffer driver
- *
- *
- * Modified:
- * Copyright 2007-2008 Analog Devices Inc.
- *
- * Bugs: Enter bugs at http://blackfin.uclinux.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, see the file COPYING, or write
- * to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/tty.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/fb.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/interrupt.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/device.h>
-#include <linux/backlight.h>
-#include <linux/lcd.h>
-#include <linux/spinlock.h>
-#include <linux/dma-mapping.h>
-#include <linux/platform_device.h>
-#include <linux/gpio.h>
-
-#include <asm/blackfin.h>
-#include <asm/irq.h>
-#include <asm/dpmc.h>
-#include <asm/dma-mapping.h>
-#include <asm/dma.h>
-#include <asm/portmux.h>
-
-#include <mach/bf54x-lq043.h>
-
-#define NO_BL_SUPPORT
-
-#define DRIVER_NAME "bf54x-lq043"
-static char driver_name[] = DRIVER_NAME;
-
-#define BFIN_LCD_NBR_PALETTE_ENTRIES 256
-
-#define EPPI0_18 {P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2, P_PPI0_D0, P_PPI0_D1, P_PPI0_D2, P_PPI0_D3, \
- P_PPI0_D4, P_PPI0_D5, P_PPI0_D6, P_PPI0_D7, P_PPI0_D8, P_PPI0_D9, P_PPI0_D10, \
- P_PPI0_D11, P_PPI0_D12, P_PPI0_D13, P_PPI0_D14, P_PPI0_D15, P_PPI0_D16, P_PPI0_D17, 0}
-
-#define EPPI0_24 {P_PPI0_D18, P_PPI0_D19, P_PPI0_D20, P_PPI0_D21, P_PPI0_D22, P_PPI0_D23, 0}
-
-struct bfin_bf54xfb_info {
- struct fb_info *fb;
- struct device *dev;
-
- struct bfin_bf54xfb_mach_info *mach_info;
-
- unsigned char *fb_buffer; /* RGB Buffer */
-
- dma_addr_t dma_handle;
- int lq043_open_cnt;
- int irq;
- spinlock_t lock; /* lock */
-};
-
-static int nocursor;
-module_param(nocursor, int, 0644);
-MODULE_PARM_DESC(nocursor, "cursor enable/disable");
-
-static int outp_rgb666;
-module_param(outp_rgb666, int, 0);
-MODULE_PARM_DESC(outp_rgb666, "Output 18-bit RGB666");
-
-#define LCD_X_RES 480 /*Horizontal Resolution */
-#define LCD_Y_RES 272 /* Vertical Resolution */
-
-#define LCD_BPP 24 /* Bit Per Pixel */
-#define DMA_BUS_SIZE 32
-
-/* -- Horizontal synchronizing --
- *
- * Timing characteristics taken from the SHARP LQ043T1DG01 datasheet
- * (LCY-W-06602A Page 9 of 22)
- *
- * Clock Frequency 1/Tc Min 7.83 Typ 9.00 Max 9.26 MHz
- *
- * Period TH - 525 - Clock
- * Pulse width THp - 41 - Clock
- * Horizontal period THd - 480 - Clock
- * Back porch THb - 2 - Clock
- * Front porch THf - 2 - Clock
- *
- * -- Vertical synchronizing --
- * Period TV - 286 - Line
- * Pulse width TVp - 10 - Line
- * Vertical period TVd - 272 - Line
- * Back porch TVb - 2 - Line
- * Front porch TVf - 2 - Line
- */
-
-#define LCD_CLK (8*1000*1000) /* 8MHz */
-
-/* # active data to transfer after Horizontal Delay clock */
-#define EPPI_HCOUNT LCD_X_RES
-
-/* # active lines to transfer after Vertical Delay clock */
-#define EPPI_VCOUNT LCD_Y_RES
-
-/* Samples per Line = 480 (active data) + 45 (padding) */
-#define EPPI_LINE 525
-
-/* Lines per Frame = 272 (active data) + 14 (padding) */
-#define EPPI_FRAME 286
-
-/* FS1 (Hsync) Width (Typical)*/
-#define EPPI_FS1W_HBL 41
-
-/* FS1 (Hsync) Period (Typical) */
-#define EPPI_FS1P_AVPL EPPI_LINE
-
-/* Horizontal Delay clock after assertion of Hsync (Typical) */
-#define EPPI_HDELAY 43
-
-/* FS2 (Vsync) Width = FS1 (Hsync) Period * 10 */
-#define EPPI_FS2W_LVB (EPPI_LINE * 10)
-
- /* FS2 (Vsync) Period = FS1 (Hsync) Period * Lines per Frame */
-#define EPPI_FS2P_LAVF (EPPI_LINE * EPPI_FRAME)
-
-/* Vertical Delay after assertion of Vsync (2 Lines) */
-#define EPPI_VDELAY 12
-
-#define EPPI_CLIP 0xFF00FF00
-
-/* EPPI Control register configuration value for RGB out
- * - EPPI as Output
- * GP 2 frame sync mode,
- * Internal Clock generation disabled, Internal FS generation enabled,
- * Receives samples on EPPI_CLK raising edge, Transmits samples on EPPI_CLK falling edge,
- * FS1 & FS2 are active high,
- * DLEN = 6 (24 bits for RGB888 out) or 5 (18 bits for RGB666 out)
- * DMA Unpacking disabled when RGB Formating is enabled, otherwise DMA unpacking enabled
- * Swapping Enabled,
- * One (DMA) Channel Mode,
- * RGB Formatting Enabled for RGB666 output, disabled for RGB888 output
- * Regular watermark - when FIFO is 100% full,
- * Urgent watermark - when FIFO is 75% full
- */
-
-#define EPPI_CONTROL (0x20136E2E | SWAPEN)
-
-static inline u16 get_eppi_clkdiv(u32 target_ppi_clk)
-{
- u32 sclk = get_sclk();
-
- /* EPPI_CLK = (SCLK) / (2 * (EPPI_CLKDIV[15:0] + 1)) */
-
- return (((sclk / target_ppi_clk) / 2) - 1);
-}
-
-static void config_ppi(struct bfin_bf54xfb_info *fbi)
-{
-
- u16 eppi_clkdiv = get_eppi_clkdiv(LCD_CLK);
-
- bfin_write_EPPI0_FS1W_HBL(EPPI_FS1W_HBL);
- bfin_write_EPPI0_FS1P_AVPL(EPPI_FS1P_AVPL);
- bfin_write_EPPI0_FS2W_LVB(EPPI_FS2W_LVB);
- bfin_write_EPPI0_FS2P_LAVF(EPPI_FS2P_LAVF);
- bfin_write_EPPI0_CLIP(EPPI_CLIP);
-
- bfin_write_EPPI0_FRAME(EPPI_FRAME);
- bfin_write_EPPI0_LINE(EPPI_LINE);
-
- bfin_write_EPPI0_HCOUNT(EPPI_HCOUNT);
- bfin_write_EPPI0_HDELAY(EPPI_HDELAY);
- bfin_write_EPPI0_VCOUNT(EPPI_VCOUNT);
- bfin_write_EPPI0_VDELAY(EPPI_VDELAY);
-
- bfin_write_EPPI0_CLKDIV(eppi_clkdiv);
-
-/*
- * DLEN = 6 (24 bits for RGB888 out) or 5 (18 bits for RGB666 out)
- * RGB Formatting Enabled for RGB666 output, disabled for RGB888 output
- */
- if (outp_rgb666)
- bfin_write_EPPI0_CONTROL((EPPI_CONTROL & ~DLENGTH) | DLEN_18 |
- RGB_FMT_EN);
- else
- bfin_write_EPPI0_CONTROL(((EPPI_CONTROL & ~DLENGTH) | DLEN_24) &
- ~RGB_FMT_EN);
-
-
-}
-
-static int config_dma(struct bfin_bf54xfb_info *fbi)
-{
-
- set_dma_config(CH_EPPI0,
- set_bfin_dma_config(DIR_READ, DMA_FLOW_AUTO,
- INTR_DISABLE, DIMENSION_2D,
- DATA_SIZE_32,
- DMA_NOSYNC_KEEP_DMA_BUF));
- set_dma_x_count(CH_EPPI0, (LCD_X_RES * LCD_BPP) / DMA_BUS_SIZE);
- set_dma_x_modify(CH_EPPI0, DMA_BUS_SIZE / 8);
- set_dma_y_count(CH_EPPI0, LCD_Y_RES);
- set_dma_y_modify(CH_EPPI0, DMA_BUS_SIZE / 8);
- set_dma_start_addr(CH_EPPI0, (unsigned long)fbi->fb_buffer);
-
- return 0;
-}
-
-static int request_ports(struct bfin_bf54xfb_info *fbi)
-{
-
- u16 eppi_req_18[] = EPPI0_18;
- u16 disp = fbi->mach_info->disp;
-
- if (gpio_request_one(disp, GPIOF_OUT_INIT_HIGH, DRIVER_NAME)) {
- printk(KERN_ERR "Requesting GPIO %d failed\n", disp);
- return -EFAULT;
- }
-
- if (peripheral_request_list(eppi_req_18, DRIVER_NAME)) {
- printk(KERN_ERR "Requesting Peripherals failed\n");
- gpio_free(disp);
- return -EFAULT;
- }
-
- if (!outp_rgb666) {
-
- u16 eppi_req_24[] = EPPI0_24;
-
- if (peripheral_request_list(eppi_req_24, DRIVER_NAME)) {
- printk(KERN_ERR "Requesting Peripherals failed\n");
- peripheral_free_list(eppi_req_18);
- gpio_free(disp);
- return -EFAULT;
- }
- }
-
- return 0;
-}
-
-static void free_ports(struct bfin_bf54xfb_info *fbi)
-{
-
- u16 eppi_req_18[] = EPPI0_18;
-
- gpio_free(fbi->mach_info->disp);
-
- peripheral_free_list(eppi_req_18);
-
- if (!outp_rgb666) {
- u16 eppi_req_24[] = EPPI0_24;
- peripheral_free_list(eppi_req_24);
- }
-}
-
-static int bfin_bf54x_fb_open(struct fb_info *info, int user)
-{
- struct bfin_bf54xfb_info *fbi = info->par;
-
- spin_lock(&fbi->lock);
- fbi->lq043_open_cnt++;
-
- if (fbi->lq043_open_cnt <= 1) {
-
- bfin_write_EPPI0_CONTROL(0);
- SSYNC();
-
- config_dma(fbi);
- config_ppi(fbi);
-
- /* start dma */
- enable_dma(CH_EPPI0);
- bfin_write_EPPI0_CONTROL(bfin_read_EPPI0_CONTROL() | EPPI_EN);
- }
-
- spin_unlock(&fbi->lock);
-
- return 0;
-}
-
-static int bfin_bf54x_fb_release(struct fb_info *info, int user)
-{
- struct bfin_bf54xfb_info *fbi = info->par;
-
- spin_lock(&fbi->lock);
-
- fbi->lq043_open_cnt--;
-
- if (fbi->lq043_open_cnt <= 0) {
-
- bfin_write_EPPI0_CONTROL(0);
- SSYNC();
- disable_dma(CH_EPPI0);
- }
-
- spin_unlock(&fbi->lock);
-
- return 0;
-}
-
-static int bfin_bf54x_fb_check_var(struct fb_var_screeninfo *var,
- struct fb_info *info)
-{
-
- switch (var->bits_per_pixel) {
- case 24:/* TRUECOLOUR, 16m */
- var->red.offset = 16;
- var->green.offset = 8;
- var->blue.offset = 0;
- var->red.length = var->green.length = var->blue.length = 8;
- var->transp.offset = 0;
- var->transp.length = 0;
- var->transp.msb_right = 0;
- var->red.msb_right = 0;
- var->green.msb_right = 0;
- var->blue.msb_right = 0;
- break;
- default:
- pr_debug("%s: depth not supported: %u BPP\n", __func__,
- var->bits_per_pixel);
- return -EINVAL;
- }
-
- if (info->var.xres != var->xres || info->var.yres != var->yres ||
- info->var.xres_virtual != var->xres_virtual ||
- info->var.yres_virtual != var->yres_virtual) {
- pr_debug("%s: Resolution not supported: X%u x Y%u \n",
- __func__, var->xres, var->yres);
- return -EINVAL;
- }
-
- /*
- * Memory limit
- */
-
- if ((info->fix.line_length * var->yres_virtual) > info->fix.smem_len) {
- pr_debug("%s: Memory Limit requested yres_virtual = %u\n",
- __func__, var->yres_virtual);
- return -ENOMEM;
- }
-
- return 0;
-}
-
-int bfin_bf54x_fb_cursor(struct fb_info *info, struct fb_cursor *cursor)
-{
- if (nocursor)
- return 0;
- else
- return -EINVAL; /* just to force soft_cursor() call */
-}
-
-static int bfin_bf54x_fb_setcolreg(u_int regno, u_int red, u_int green,
- u_int blue, u_int transp,
- struct fb_info *info)
-{
- if (regno >= BFIN_LCD_NBR_PALETTE_ENTRIES)
- return -EINVAL;
-
- if (info->var.grayscale) {
- /* grayscale = 0.30*R + 0.59*G + 0.11*B */
- red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
- }
-
- if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
-
- u32 value;
- /* Place color in the pseudopalette */
- if (regno > 16)
- return -EINVAL;
-
- red >>= (16 - info->var.red.length);
- green >>= (16 - info->var.green.length);
- blue >>= (16 - info->var.blue.length);
-
- value = (red << info->var.red.offset) |
- (green << info->var.green.offset) |
- (blue << info->var.blue.offset);
- value &= 0xFFFFFF;
-
- ((u32 *) (info->pseudo_palette))[regno] = value;
-
- }
-
- return 0;
-}
-
-static struct fb_ops bfin_bf54x_fb_ops = {
- .owner = THIS_MODULE,
- .fb_open = bfin_bf54x_fb_open,
- .fb_release = bfin_bf54x_fb_release,
- .fb_check_var = bfin_bf54x_fb_check_var,
- .fb_fillrect = cfb_fillrect,
- .fb_copyarea = cfb_copyarea,
- .fb_imageblit = cfb_imageblit,
- .fb_cursor = bfin_bf54x_fb_cursor,
- .fb_setcolreg = bfin_bf54x_fb_setcolreg,
-};
-
-#ifndef NO_BL_SUPPORT
-static int bl_get_brightness(struct backlight_device *bd)
-{
- return 0;
-}
-
-static const struct backlight_ops bfin_lq043fb_bl_ops = {
- .get_brightness = bl_get_brightness,
-};
-
-static struct backlight_device *bl_dev;
-
-static int bfin_lcd_get_power(struct lcd_device *dev)
-{
- return 0;
-}
-
-static int bfin_lcd_set_power(struct lcd_device *dev, int power)
-{
- return 0;
-}
-
-static int bfin_lcd_get_contrast(struct lcd_device *dev)
-{
- return 0;
-}
-
-static int bfin_lcd_set_contrast(struct lcd_device *dev, int contrast)
-{
-
- return 0;
-}
-
-static int bfin_lcd_check_fb(struct lcd_device *dev, struct fb_info *fi)
-{
- if (!fi || (fi == &bfin_bf54x_fb))
- return 1;
- return 0;
-}
-
-static struct lcd_ops bfin_lcd_ops = {
- .get_power = bfin_lcd_get_power,
- .set_power = bfin_lcd_set_power,
- .get_contrast = bfin_lcd_get_contrast,
- .set_contrast = bfin_lcd_set_contrast,
- .check_fb = bfin_lcd_check_fb,
-};
-
-static struct lcd_device *lcd_dev;
-#endif
-
-static irqreturn_t bfin_bf54x_irq_error(int irq, void *dev_id)
-{
- /*struct bfin_bf54xfb_info *info = dev_id;*/
-
- u16 status = bfin_read_EPPI0_STATUS();
-
- bfin_write_EPPI0_STATUS(0xFFFF);
-
- if (status) {
- bfin_write_EPPI0_CONTROL(bfin_read_EPPI0_CONTROL() & ~EPPI_EN);
- disable_dma(CH_EPPI0);
-
- /* start dma */
- enable_dma(CH_EPPI0);
- bfin_write_EPPI0_CONTROL(bfin_read_EPPI0_CONTROL() | EPPI_EN);
- bfin_write_EPPI0_STATUS(0xFFFF);
- }
-
- return IRQ_HANDLED;
-}
-
-static int bfin_bf54x_probe(struct platform_device *pdev)
-{
-#ifndef NO_BL_SUPPORT
- struct backlight_properties props;
-#endif
- struct bfin_bf54xfb_info *info;
- struct fb_info *fbinfo;
- int ret;
-
- printk(KERN_INFO DRIVER_NAME ": FrameBuffer initializing...\n");
-
- if (request_dma(CH_EPPI0, "CH_EPPI0") < 0) {
- printk(KERN_ERR DRIVER_NAME
- ": couldn't request CH_EPPI0 DMA\n");
- ret = -EFAULT;
- goto out1;
- }
-
- fbinfo =
- framebuffer_alloc(sizeof(struct bfin_bf54xfb_info), &pdev->dev);
- if (!fbinfo) {
- ret = -ENOMEM;
- goto out2;
- }
-
- info = fbinfo->par;
- info->fb = fbinfo;
- info->dev = &pdev->dev;
- spin_lock_init(&info->lock);
-
- platform_set_drvdata(pdev, fbinfo);
-
- strcpy(fbinfo->fix.id, driver_name);
-
- info->mach_info = pdev->dev.platform_data;
-
- if (info->mach_info == NULL) {
- dev_err(&pdev->dev,
- "no platform data for lcd, cannot attach\n");
- ret = -EINVAL;
- goto out3;
- }
-
- fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
- fbinfo->fix.type_aux = 0;
- fbinfo->fix.xpanstep = 0;
- fbinfo->fix.ypanstep = 0;
- fbinfo->fix.ywrapstep = 0;
- fbinfo->fix.accel = FB_ACCEL_NONE;
- fbinfo->fix.visual = FB_VISUAL_TRUECOLOR;
-
- fbinfo->var.nonstd = 0;
- fbinfo->var.activate = FB_ACTIVATE_NOW;
- fbinfo->var.height = info->mach_info->height;
- fbinfo->var.width = info->mach_info->width;
- fbinfo->var.accel_flags = 0;
- fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
-
- fbinfo->fbops = &bfin_bf54x_fb_ops;
- fbinfo->flags = FBINFO_FLAG_DEFAULT;
-
- fbinfo->var.xres = info->mach_info->xres.defval;
- fbinfo->var.xres_virtual = info->mach_info->xres.defval;
- fbinfo->var.yres = info->mach_info->yres.defval;
- fbinfo->var.yres_virtual = info->mach_info->yres.defval;
- fbinfo->var.bits_per_pixel = info->mach_info->bpp.defval;
-
- fbinfo->var.upper_margin = 0;
- fbinfo->var.lower_margin = 0;
- fbinfo->var.vsync_len = 0;
-
- fbinfo->var.left_margin = 0;
- fbinfo->var.right_margin = 0;
- fbinfo->var.hsync_len = 0;
-
- fbinfo->var.red.offset = 16;
- fbinfo->var.green.offset = 8;
- fbinfo->var.blue.offset = 0;
- fbinfo->var.transp.offset = 0;
- fbinfo->var.red.length = 8;
- fbinfo->var.green.length = 8;
- fbinfo->var.blue.length = 8;
- fbinfo->var.transp.length = 0;
- fbinfo->fix.smem_len = info->mach_info->xres.max *
- info->mach_info->yres.max * info->mach_info->bpp.max / 8;
-
- fbinfo->fix.line_length = fbinfo->var.xres_virtual *
- fbinfo->var.bits_per_pixel / 8;
-
- info->fb_buffer =
- dma_alloc_coherent(NULL, fbinfo->fix.smem_len, &info->dma_handle,
- GFP_KERNEL);
-
- if (NULL == info->fb_buffer) {
- printk(KERN_ERR DRIVER_NAME
- ": couldn't allocate dma buffer.\n");
- ret = -ENOMEM;
- goto out3;
- }
-
- fbinfo->screen_base = (void *)info->fb_buffer;
- fbinfo->fix.smem_start = (int)info->fb_buffer;
-
- fbinfo->fbops = &bfin_bf54x_fb_ops;
-
- fbinfo->pseudo_palette = devm_kzalloc(&pdev->dev, sizeof(u32) * 16,
- GFP_KERNEL);
- if (!fbinfo->pseudo_palette) {
- printk(KERN_ERR DRIVER_NAME
- "Fail to allocate pseudo_palette\n");
-
- ret = -ENOMEM;
- goto out4;
- }
-
- if (fb_alloc_cmap(&fbinfo->cmap, BFIN_LCD_NBR_PALETTE_ENTRIES, 0)
- < 0) {
- printk(KERN_ERR DRIVER_NAME
- "Fail to allocate colormap (%d entries)\n",
- BFIN_LCD_NBR_PALETTE_ENTRIES);
- ret = -EFAULT;
- goto out4;
- }
-
- if (request_ports(info)) {
- printk(KERN_ERR DRIVER_NAME ": couldn't request gpio port.\n");
- ret = -EFAULT;
- goto out6;
- }
-
- info->irq = platform_get_irq(pdev, 0);
- if (info->irq < 0) {
- ret = -EINVAL;
- goto out7;
- }
-
- if (request_irq(info->irq, bfin_bf54x_irq_error, 0,
- "PPI ERROR", info) < 0) {
- printk(KERN_ERR DRIVER_NAME
- ": unable to request PPI ERROR IRQ\n");
- ret = -EFAULT;
- goto out7;
- }
-
- if (register_framebuffer(fbinfo) < 0) {
- printk(KERN_ERR DRIVER_NAME
- ": unable to register framebuffer.\n");
- ret = -EINVAL;
- goto out8;
- }
-#ifndef NO_BL_SUPPORT
- memset(&props, 0, sizeof(struct backlight_properties));
- props.type = BACKLIGHT_RAW;
- props.max_brightness = 255;
- bl_dev = backlight_device_register("bf54x-bl", NULL, NULL,
- &bfin_lq043fb_bl_ops, &props);
- if (IS_ERR(bl_dev)) {
- printk(KERN_ERR DRIVER_NAME
- ": unable to register backlight.\n");
- ret = -EINVAL;
- unregister_framebuffer(fbinfo);
- goto out8;
- }
-
- lcd_dev = lcd_device_register(DRIVER_NAME, &pdev->dev, NULL, &bfin_lcd_ops);
- lcd_dev->props.max_contrast = 255, printk(KERN_INFO "Done.\n");
-#endif
-
- return 0;
-
-out8:
- free_irq(info->irq, info);
-out7:
- free_ports(info);
-out6:
- fb_dealloc_cmap(&fbinfo->cmap);
-out4:
- dma_free_coherent(NULL, fbinfo->fix.smem_len, info->fb_buffer,
- info->dma_handle);
-out3:
- framebuffer_release(fbinfo);
-out2:
- free_dma(CH_EPPI0);
-out1:
-
- return ret;
-}
-
-static int bfin_bf54x_remove(struct platform_device *pdev)
-{
-
- struct fb_info *fbinfo = platform_get_drvdata(pdev);
- struct bfin_bf54xfb_info *info = fbinfo->par;
-
- free_dma(CH_EPPI0);
- free_irq(info->irq, info);
-
- if (info->fb_buffer != NULL)
- dma_free_coherent(NULL, fbinfo->fix.smem_len, info->fb_buffer,
- info->dma_handle);
-
- fb_dealloc_cmap(&fbinfo->cmap);
-
-#ifndef NO_BL_SUPPORT
- lcd_device_unregister(lcd_dev);
- backlight_device_unregister(bl_dev);
-#endif
-
- unregister_framebuffer(fbinfo);
-
- free_ports(info);
-
- printk(KERN_INFO DRIVER_NAME ": Unregister LCD driver.\n");
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int bfin_bf54x_suspend(struct platform_device *pdev, pm_message_t state)
-{
- bfin_write_EPPI0_CONTROL(bfin_read_EPPI0_CONTROL() & ~EPPI_EN);
- disable_dma(CH_EPPI0);
- bfin_write_EPPI0_STATUS(0xFFFF);
-
- return 0;
-}
-
-static int bfin_bf54x_resume(struct platform_device *pdev)
-{
- struct fb_info *fbinfo = platform_get_drvdata(pdev);
- struct bfin_bf54xfb_info *info = fbinfo->par;
-
- if (info->lq043_open_cnt) {
-
- bfin_write_EPPI0_CONTROL(0);
- SSYNC();
-
- config_dma(info);
- config_ppi(info);
-
- /* start dma */
- enable_dma(CH_EPPI0);
- bfin_write_EPPI0_CONTROL(bfin_read_EPPI0_CONTROL() | EPPI_EN);
- }
-
- return 0;
-}
-#else
-#define bfin_bf54x_suspend NULL
-#define bfin_bf54x_resume NULL
-#endif
-
-static struct platform_driver bfin_bf54x_driver = {
- .probe = bfin_bf54x_probe,
- .remove = bfin_bf54x_remove,
- .suspend = bfin_bf54x_suspend,
- .resume = bfin_bf54x_resume,
- .driver = {
- .name = DRIVER_NAME,
- },
-};
-module_platform_driver(bfin_bf54x_driver);
-
-MODULE_DESCRIPTION("Blackfin BF54x TFT LCD Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/video/fbdev/bfin-lq035q1-fb.c b/drivers/video/fbdev/bfin-lq035q1-fb.c
deleted file mode 100644
index b459354ad940..000000000000
--- a/drivers/video/fbdev/bfin-lq035q1-fb.c
+++ /dev/null
@@ -1,864 +0,0 @@
-/*
- * Blackfin LCD Framebuffer driver SHARP LQ035Q1DH02
- *
- * Copyright 2008-2009 Analog Devices Inc.
- * Licensed under the GPL-2 or later.
- */
-
-#define DRIVER_NAME "bfin-lq035q1"
-#define pr_fmt(fmt) DRIVER_NAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/fb.h>
-#include <linux/gpio.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/backlight.h>
-#include <linux/lcd.h>
-#include <linux/dma-mapping.h>
-#include <linux/platform_device.h>
-#include <linux/spi/spi.h>
-
-#include <asm/blackfin.h>
-#include <asm/irq.h>
-#include <asm/dma.h>
-#include <asm/portmux.h>
-#include <asm/gptimers.h>
-
-#include <asm/bfin-lq035q1.h>
-
-#if defined(BF533_FAMILY) || defined(BF538_FAMILY)
-#define TIMER_HSYNC_id TIMER1_id
-#define TIMER_HSYNCbit TIMER1bit
-#define TIMER_HSYNC_STATUS_TRUN TIMER_STATUS_TRUN1
-#define TIMER_HSYNC_STATUS_TIMIL TIMER_STATUS_TIMIL1
-#define TIMER_HSYNC_STATUS_TOVF TIMER_STATUS_TOVF1
-
-#define TIMER_VSYNC_id TIMER2_id
-#define TIMER_VSYNCbit TIMER2bit
-#define TIMER_VSYNC_STATUS_TRUN TIMER_STATUS_TRUN2
-#define TIMER_VSYNC_STATUS_TIMIL TIMER_STATUS_TIMIL2
-#define TIMER_VSYNC_STATUS_TOVF TIMER_STATUS_TOVF2
-#else
-#define TIMER_HSYNC_id TIMER0_id
-#define TIMER_HSYNCbit TIMER0bit
-#define TIMER_HSYNC_STATUS_TRUN TIMER_STATUS_TRUN0
-#define TIMER_HSYNC_STATUS_TIMIL TIMER_STATUS_TIMIL0
-#define TIMER_HSYNC_STATUS_TOVF TIMER_STATUS_TOVF0
-
-#define TIMER_VSYNC_id TIMER1_id
-#define TIMER_VSYNCbit TIMER1bit
-#define TIMER_VSYNC_STATUS_TRUN TIMER_STATUS_TRUN1
-#define TIMER_VSYNC_STATUS_TIMIL TIMER_STATUS_TIMIL1
-#define TIMER_VSYNC_STATUS_TOVF TIMER_STATUS_TOVF1
-#endif
-
-#define LCD_X_RES 320 /* Horizontal Resolution */
-#define LCD_Y_RES 240 /* Vertical Resolution */
-#define DMA_BUS_SIZE 16
-#define U_LINE 4 /* Blanking Lines */
-
-
-/* Interface 16/18-bit TFT over an 8-bit wide PPI using a small Programmable Logic Device (CPLD)
- * http://blackfin.uclinux.org/gf/project/stamp/frs/?action=FrsReleaseBrowse&frs_package_id=165
- */
-
-
-#define BFIN_LCD_NBR_PALETTE_ENTRIES 256
-
-#define PPI_TX_MODE 0x2
-#define PPI_XFER_TYPE_11 0xC
-#define PPI_PORT_CFG_01 0x10
-#define PPI_POLS_1 0x8000
-
-#define LQ035_INDEX 0x74
-#define LQ035_DATA 0x76
-
-#define LQ035_DRIVER_OUTPUT_CTL 0x1
-#define LQ035_SHUT_CTL 0x11
-
-#define LQ035_DRIVER_OUTPUT_MASK (LQ035_LR | LQ035_TB | LQ035_BGR | LQ035_REV)
-#define LQ035_DRIVER_OUTPUT_DEFAULT (0x2AEF & ~LQ035_DRIVER_OUTPUT_MASK)
-
-#define LQ035_SHUT (1 << 0) /* Shutdown */
-#define LQ035_ON (0 << 0) /* Shutdown */
-
-struct bfin_lq035q1fb_info {
- struct fb_info *fb;
- struct device *dev;
- struct spi_driver spidrv;
- struct bfin_lq035q1fb_disp_info *disp_info;
- unsigned char *fb_buffer; /* RGB Buffer */
- dma_addr_t dma_handle;
- int lq035_open_cnt;
- int irq;
- spinlock_t lock; /* lock */
- u32 pseudo_pal[16];
-
- u32 lcd_bpp;
- u32 h_actpix;
- u32 h_period;
- u32 h_pulse;
- u32 h_start;
- u32 v_lines;
- u32 v_pulse;
- u32 v_period;
-};
-
-static int nocursor;
-module_param(nocursor, int, 0644);
-MODULE_PARM_DESC(nocursor, "cursor enable/disable");
-
-struct spi_control {
- unsigned short mode;
-};
-
-static int lq035q1_control(struct spi_device *spi, unsigned char reg, unsigned short value)
-{
- int ret;
- u8 regs[3] = { LQ035_INDEX, 0, 0 };
- u8 dat[3] = { LQ035_DATA, 0, 0 };
-
- if (!spi)
- return -ENODEV;
-
- regs[2] = reg;
- dat[1] = value >> 8;
- dat[2] = value & 0xFF;
-
- ret = spi_write(spi, regs, ARRAY_SIZE(regs));
- ret |= spi_write(spi, dat, ARRAY_SIZE(dat));
- return ret;
-}
-
-static int lq035q1_spidev_probe(struct spi_device *spi)
-{
- int ret;
- struct spi_control *ctl;
- struct bfin_lq035q1fb_info *info = container_of(spi->dev.driver,
- struct bfin_lq035q1fb_info,
- spidrv.driver);
-
- ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
-
- if (!ctl)
- return -ENOMEM;
-
- ctl->mode = (info->disp_info->mode &
- LQ035_DRIVER_OUTPUT_MASK) | LQ035_DRIVER_OUTPUT_DEFAULT;
-
- ret = lq035q1_control(spi, LQ035_SHUT_CTL, LQ035_ON);
- ret |= lq035q1_control(spi, LQ035_DRIVER_OUTPUT_CTL, ctl->mode);
- if (ret) {
- kfree(ctl);
- return ret;
- }
-
- spi_set_drvdata(spi, ctl);
-
- return 0;
-}
-
-static int lq035q1_spidev_remove(struct spi_device *spi)
-{
- return lq035q1_control(spi, LQ035_SHUT_CTL, LQ035_SHUT);
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int lq035q1_spidev_suspend(struct device *dev)
-{
- struct spi_device *spi = to_spi_device(dev);
-
- return lq035q1_control(spi, LQ035_SHUT_CTL, LQ035_SHUT);
-}
-
-static int lq035q1_spidev_resume(struct device *dev)
-{
- struct spi_device *spi = to_spi_device(dev);
- struct spi_control *ctl = spi_get_drvdata(spi);
- int ret;
-
- ret = lq035q1_control(spi, LQ035_DRIVER_OUTPUT_CTL, ctl->mode);
- if (ret)
- return ret;
-
- return lq035q1_control(spi, LQ035_SHUT_CTL, LQ035_ON);
-}
-
-static SIMPLE_DEV_PM_OPS(lq035q1_spidev_pm_ops, lq035q1_spidev_suspend,
- lq035q1_spidev_resume);
-#define LQ035Q1_SPIDEV_PM_OPS (&lq035q1_spidev_pm_ops)
-
-#else
-#define LQ035Q1_SPIDEV_PM_OPS NULL
-#endif
-
-/* Power down all displays on reboot, poweroff or halt */
-static void lq035q1_spidev_shutdown(struct spi_device *spi)
-{
- lq035q1_control(spi, LQ035_SHUT_CTL, LQ035_SHUT);
-}
-
-static int lq035q1_backlight(struct bfin_lq035q1fb_info *info, unsigned arg)
-{
- if (info->disp_info->use_bl)
- gpio_set_value(info->disp_info->gpio_bl, arg);
-
- return 0;
-}
-
-static int bfin_lq035q1_calc_timing(struct bfin_lq035q1fb_info *fbi)
-{
- unsigned long clocks_per_pix, cpld_pipeline_delay_cor;
-
- /*
- * Interface 16/18-bit TFT over an 8-bit wide PPI using a small
- * Programmable Logic Device (CPLD)
- * http://blackfin.uclinux.org/gf/project/stamp/frs/?action=FrsReleaseBrowse&frs_package_id=165
- */
-
- switch (fbi->disp_info->ppi_mode) {
- case USE_RGB565_16_BIT_PPI:
- fbi->lcd_bpp = 16;
- clocks_per_pix = 1;
- cpld_pipeline_delay_cor = 0;
- break;
- case USE_RGB565_8_BIT_PPI:
- fbi->lcd_bpp = 16;
- clocks_per_pix = 2;
- cpld_pipeline_delay_cor = 3;
- break;
- case USE_RGB888_8_BIT_PPI:
- fbi->lcd_bpp = 24;
- clocks_per_pix = 3;
- cpld_pipeline_delay_cor = 5;
- break;
- default:
- return -EINVAL;
- }
-
- /*
- * HS and VS timing parameters (all in number of PPI clk ticks)
- */
-
- fbi->h_actpix = (LCD_X_RES * clocks_per_pix); /* active horizontal pixel */
- fbi->h_period = (336 * clocks_per_pix); /* HS period */
- fbi->h_pulse = (2 * clocks_per_pix); /* HS pulse width */
- fbi->h_start = (7 * clocks_per_pix + cpld_pipeline_delay_cor); /* first valid pixel */
-
- fbi->v_lines = (LCD_Y_RES + U_LINE); /* total vertical lines */
- fbi->v_pulse = (2 * clocks_per_pix); /* VS pulse width (1-5 H_PERIODs) */
- fbi->v_period = (fbi->h_period * fbi->v_lines); /* VS period */
-
- return 0;
-}
-
-static void bfin_lq035q1_config_ppi(struct bfin_lq035q1fb_info *fbi)
-{
- unsigned ppi_pmode;
-
- if (fbi->disp_info->ppi_mode == USE_RGB565_16_BIT_PPI)
- ppi_pmode = DLEN_16;
- else
- ppi_pmode = (DLEN_8 | PACK_EN);
-
- bfin_write_PPI_DELAY(fbi->h_start);
- bfin_write_PPI_COUNT(fbi->h_actpix - 1);
- bfin_write_PPI_FRAME(fbi->v_lines);
-
- bfin_write_PPI_CONTROL(PPI_TX_MODE | /* output mode , PORT_DIR */
- PPI_XFER_TYPE_11 | /* sync mode XFR_TYPE */
- PPI_PORT_CFG_01 | /* two frame sync PORT_CFG */
- ppi_pmode | /* 8/16 bit data length / PACK_EN? */
- PPI_POLS_1); /* faling edge syncs POLS */
-}
-
-static inline void bfin_lq035q1_disable_ppi(void)
-{
- bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() & ~PORT_EN);
-}
-
-static inline void bfin_lq035q1_enable_ppi(void)
-{
- bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() | PORT_EN);
-}
-
-static void bfin_lq035q1_start_timers(void)
-{
- enable_gptimers(TIMER_VSYNCbit | TIMER_HSYNCbit);
-}
-
-static void bfin_lq035q1_stop_timers(void)
-{
- disable_gptimers(TIMER_HSYNCbit | TIMER_VSYNCbit);
-
- set_gptimer_status(0, TIMER_HSYNC_STATUS_TRUN | TIMER_VSYNC_STATUS_TRUN |
- TIMER_HSYNC_STATUS_TIMIL | TIMER_VSYNC_STATUS_TIMIL |
- TIMER_HSYNC_STATUS_TOVF | TIMER_VSYNC_STATUS_TOVF);
-
-}
-
-static void bfin_lq035q1_init_timers(struct bfin_lq035q1fb_info *fbi)
-{
-
- bfin_lq035q1_stop_timers();
-
- set_gptimer_period(TIMER_HSYNC_id, fbi->h_period);
- set_gptimer_pwidth(TIMER_HSYNC_id, fbi->h_pulse);
- set_gptimer_config(TIMER_HSYNC_id, TIMER_MODE_PWM | TIMER_PERIOD_CNT |
- TIMER_TIN_SEL | TIMER_CLK_SEL|
- TIMER_EMU_RUN);
-
- set_gptimer_period(TIMER_VSYNC_id, fbi->v_period);
- set_gptimer_pwidth(TIMER_VSYNC_id, fbi->v_pulse);
- set_gptimer_config(TIMER_VSYNC_id, TIMER_MODE_PWM | TIMER_PERIOD_CNT |
- TIMER_TIN_SEL | TIMER_CLK_SEL |
- TIMER_EMU_RUN);
-
-}
-
-static void bfin_lq035q1_config_dma(struct bfin_lq035q1fb_info *fbi)
-{
-
-
- set_dma_config(CH_PPI,
- set_bfin_dma_config(DIR_READ, DMA_FLOW_AUTO,
- INTR_DISABLE, DIMENSION_2D,
- DATA_SIZE_16,
- DMA_NOSYNC_KEEP_DMA_BUF));
- set_dma_x_count(CH_PPI, (LCD_X_RES * fbi->lcd_bpp) / DMA_BUS_SIZE);
- set_dma_x_modify(CH_PPI, DMA_BUS_SIZE / 8);
- set_dma_y_count(CH_PPI, fbi->v_lines);
-
- set_dma_y_modify(CH_PPI, DMA_BUS_SIZE / 8);
- set_dma_start_addr(CH_PPI, (unsigned long)fbi->fb_buffer);
-
-}
-
-static const u16 ppi0_req_16[] = {P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2,
- P_PPI0_D0, P_PPI0_D1, P_PPI0_D2,
- P_PPI0_D3, P_PPI0_D4, P_PPI0_D5,
- P_PPI0_D6, P_PPI0_D7, P_PPI0_D8,
- P_PPI0_D9, P_PPI0_D10, P_PPI0_D11,
- P_PPI0_D12, P_PPI0_D13, P_PPI0_D14,
- P_PPI0_D15, 0};
-
-static const u16 ppi0_req_8[] = {P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2,
- P_PPI0_D0, P_PPI0_D1, P_PPI0_D2,
- P_PPI0_D3, P_PPI0_D4, P_PPI0_D5,
- P_PPI0_D6, P_PPI0_D7, 0};
-
-static inline void bfin_lq035q1_free_ports(unsigned ppi16)
-{
- if (ppi16)
- peripheral_free_list(ppi0_req_16);
- else
- peripheral_free_list(ppi0_req_8);
-
- if (ANOMALY_05000400)
- gpio_free(P_IDENT(P_PPI0_FS3));
-}
-
-static int bfin_lq035q1_request_ports(struct platform_device *pdev,
- unsigned ppi16)
-{
- int ret;
- /* ANOMALY_05000400 - PPI Does Not Start Properly In Specific Mode:
- * Drive PPI_FS3 Low
- */
- if (ANOMALY_05000400) {
- int ret = gpio_request_one(P_IDENT(P_PPI0_FS3),
- GPIOF_OUT_INIT_LOW, "PPI_FS3");
- if (ret)
- return ret;
- }
-
- if (ppi16)
- ret = peripheral_request_list(ppi0_req_16, DRIVER_NAME);
- else
- ret = peripheral_request_list(ppi0_req_8, DRIVER_NAME);
-
- if (ret) {
- dev_err(&pdev->dev, "requesting peripherals failed\n");
- return -EFAULT;
- }
-
- return 0;
-}
-
-static int bfin_lq035q1_fb_open(struct fb_info *info, int user)
-{
- struct bfin_lq035q1fb_info *fbi = info->par;
-
- spin_lock(&fbi->lock);
- fbi->lq035_open_cnt++;
-
- if (fbi->lq035_open_cnt <= 1) {
-
- bfin_lq035q1_disable_ppi();
- SSYNC();
-
- bfin_lq035q1_config_dma(fbi);
- bfin_lq035q1_config_ppi(fbi);
- bfin_lq035q1_init_timers(fbi);
-
- /* start dma */
- enable_dma(CH_PPI);
- bfin_lq035q1_enable_ppi();
- bfin_lq035q1_start_timers();
- lq035q1_backlight(fbi, 1);
- }
-
- spin_unlock(&fbi->lock);
-
- return 0;
-}
-
-static int bfin_lq035q1_fb_release(struct fb_info *info, int user)
-{
- struct bfin_lq035q1fb_info *fbi = info->par;
-
- spin_lock(&fbi->lock);
-
- fbi->lq035_open_cnt--;
-
- if (fbi->lq035_open_cnt <= 0) {
- lq035q1_backlight(fbi, 0);
- bfin_lq035q1_disable_ppi();
- SSYNC();
- disable_dma(CH_PPI);
- bfin_lq035q1_stop_timers();
- }
-
- spin_unlock(&fbi->lock);
-
- return 0;
-}
-
-static int bfin_lq035q1_fb_check_var(struct fb_var_screeninfo *var,
- struct fb_info *info)
-{
- struct bfin_lq035q1fb_info *fbi = info->par;
-
- if (var->bits_per_pixel == fbi->lcd_bpp) {
- var->red.offset = info->var.red.offset;
- var->green.offset = info->var.green.offset;
- var->blue.offset = info->var.blue.offset;
- var->red.length = info->var.red.length;
- var->green.length = info->var.green.length;
- var->blue.length = info->var.blue.length;
- var->transp.offset = 0;
- var->transp.length = 0;
- var->transp.msb_right = 0;
- var->red.msb_right = 0;
- var->green.msb_right = 0;
- var->blue.msb_right = 0;
- } else {
- pr_debug("%s: depth not supported: %u BPP\n", __func__,
- var->bits_per_pixel);
- return -EINVAL;
- }
-
- if (info->var.xres != var->xres || info->var.yres != var->yres ||
- info->var.xres_virtual != var->xres_virtual ||
- info->var.yres_virtual != var->yres_virtual) {
- pr_debug("%s: Resolution not supported: X%u x Y%u \n",
- __func__, var->xres, var->yres);
- return -EINVAL;
- }
-
- /*
- * Memory limit
- */
-
- if ((info->fix.line_length * var->yres_virtual) > info->fix.smem_len) {
- pr_debug("%s: Memory Limit requested yres_virtual = %u\n",
- __func__, var->yres_virtual);
- return -ENOMEM;
- }
-
-
- return 0;
-}
-
-int bfin_lq035q1_fb_cursor(struct fb_info *info, struct fb_cursor *cursor)
-{
- if (nocursor)
- return 0;
- else
- return -EINVAL; /* just to force soft_cursor() call */
-}
-
-static int bfin_lq035q1_fb_setcolreg(u_int regno, u_int red, u_int green,
- u_int blue, u_int transp,
- struct fb_info *info)
-{
- if (regno >= BFIN_LCD_NBR_PALETTE_ENTRIES)
- return -EINVAL;
-
- if (info->var.grayscale) {
- /* grayscale = 0.30*R + 0.59*G + 0.11*B */
- red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
- }
-
- if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
-
- u32 value;
- /* Place color in the pseudopalette */
- if (regno > 16)
- return -EINVAL;
-
- red >>= (16 - info->var.red.length);
- green >>= (16 - info->var.green.length);
- blue >>= (16 - info->var.blue.length);
-
- value = (red << info->var.red.offset) |
- (green << info->var.green.offset) |
- (blue << info->var.blue.offset);
- value &= 0xFFFFFF;
-
- ((u32 *) (info->pseudo_palette))[regno] = value;
-
- }
-
- return 0;
-}
-
-static struct fb_ops bfin_lq035q1_fb_ops = {
- .owner = THIS_MODULE,
- .fb_open = bfin_lq035q1_fb_open,
- .fb_release = bfin_lq035q1_fb_release,
- .fb_check_var = bfin_lq035q1_fb_check_var,
- .fb_fillrect = cfb_fillrect,
- .fb_copyarea = cfb_copyarea,
- .fb_imageblit = cfb_imageblit,
- .fb_cursor = bfin_lq035q1_fb_cursor,
- .fb_setcolreg = bfin_lq035q1_fb_setcolreg,
-};
-
-static irqreturn_t bfin_lq035q1_irq_error(int irq, void *dev_id)
-{
- /*struct bfin_lq035q1fb_info *info = (struct bfin_lq035q1fb_info *)dev_id;*/
-
- u16 status = bfin_read_PPI_STATUS();
- bfin_write_PPI_STATUS(-1);
-
- if (status) {
- bfin_lq035q1_disable_ppi();
- disable_dma(CH_PPI);
-
- /* start dma */
- enable_dma(CH_PPI);
- bfin_lq035q1_enable_ppi();
- bfin_write_PPI_STATUS(-1);
- }
-
- return IRQ_HANDLED;
-}
-
-static int bfin_lq035q1_probe(struct platform_device *pdev)
-{
- struct bfin_lq035q1fb_info *info;
- struct fb_info *fbinfo;
- u32 active_video_mem_offset;
- int ret;
-
- ret = request_dma(CH_PPI, DRIVER_NAME"_CH_PPI");
- if (ret < 0) {
- dev_err(&pdev->dev, "PPI DMA unavailable\n");
- goto out1;
- }
-
- fbinfo = framebuffer_alloc(sizeof(*info), &pdev->dev);
- if (!fbinfo) {
- ret = -ENOMEM;
- goto out2;
- }
-
- info = fbinfo->par;
- info->fb = fbinfo;
- info->dev = &pdev->dev;
- spin_lock_init(&info->lock);
-
- info->disp_info = pdev->dev.platform_data;
-
- platform_set_drvdata(pdev, fbinfo);
-
- ret = bfin_lq035q1_calc_timing(info);
- if (ret < 0) {
- dev_err(&pdev->dev, "Failed PPI Mode\n");
- goto out3;
- }
-
- strcpy(fbinfo->fix.id, DRIVER_NAME);
-
- fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
- fbinfo->fix.type_aux = 0;
- fbinfo->fix.xpanstep = 0;
- fbinfo->fix.ypanstep = 0;
- fbinfo->fix.ywrapstep = 0;
- fbinfo->fix.accel = FB_ACCEL_NONE;
- fbinfo->fix.visual = FB_VISUAL_TRUECOLOR;
-
- fbinfo->var.nonstd = 0;
- fbinfo->var.activate = FB_ACTIVATE_NOW;
- fbinfo->var.height = -1;
- fbinfo->var.width = -1;
- fbinfo->var.accel_flags = 0;
- fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
-
- fbinfo->var.xres = LCD_X_RES;
- fbinfo->var.xres_virtual = LCD_X_RES;
- fbinfo->var.yres = LCD_Y_RES;
- fbinfo->var.yres_virtual = LCD_Y_RES;
- fbinfo->var.bits_per_pixel = info->lcd_bpp;
-
- if (info->disp_info->mode & LQ035_BGR) {
- if (info->lcd_bpp == 24) {
- fbinfo->var.red.offset = 0;
- fbinfo->var.green.offset = 8;
- fbinfo->var.blue.offset = 16;
- } else {
- fbinfo->var.red.offset = 0;
- fbinfo->var.green.offset = 5;
- fbinfo->var.blue.offset = 11;
- }
- } else {
- if (info->lcd_bpp == 24) {
- fbinfo->var.red.offset = 16;
- fbinfo->var.green.offset = 8;
- fbinfo->var.blue.offset = 0;
- } else {
- fbinfo->var.red.offset = 11;
- fbinfo->var.green.offset = 5;
- fbinfo->var.blue.offset = 0;
- }
- }
-
- fbinfo->var.transp.offset = 0;
-
- if (info->lcd_bpp == 24) {
- fbinfo->var.red.length = 8;
- fbinfo->var.green.length = 8;
- fbinfo->var.blue.length = 8;
- } else {
- fbinfo->var.red.length = 5;
- fbinfo->var.green.length = 6;
- fbinfo->var.blue.length = 5;
- }
-
- fbinfo->var.transp.length = 0;
-
- active_video_mem_offset = ((U_LINE / 2) * LCD_X_RES * (info->lcd_bpp / 8));
-
- fbinfo->fix.smem_len = LCD_X_RES * LCD_Y_RES * info->lcd_bpp / 8
- + active_video_mem_offset;
-
- fbinfo->fix.line_length = fbinfo->var.xres_virtual *
- fbinfo->var.bits_per_pixel / 8;
-
-
- fbinfo->fbops = &bfin_lq035q1_fb_ops;
- fbinfo->flags = FBINFO_FLAG_DEFAULT;
-
- info->fb_buffer =
- dma_alloc_coherent(NULL, fbinfo->fix.smem_len, &info->dma_handle,
- GFP_KERNEL);
-
- if (NULL == info->fb_buffer) {
- dev_err(&pdev->dev, "couldn't allocate dma buffer\n");
- ret = -ENOMEM;
- goto out3;
- }
-
- fbinfo->screen_base = (void *)info->fb_buffer + active_video_mem_offset;
- fbinfo->fix.smem_start = (int)info->fb_buffer + active_video_mem_offset;
-
- fbinfo->fbops = &bfin_lq035q1_fb_ops;
-
- fbinfo->pseudo_palette = &info->pseudo_pal;
-
- ret = fb_alloc_cmap(&fbinfo->cmap, BFIN_LCD_NBR_PALETTE_ENTRIES, 0);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to allocate colormap (%d entries)\n",
- BFIN_LCD_NBR_PALETTE_ENTRIES);
- goto out4;
- }
-
- ret = bfin_lq035q1_request_ports(pdev,
- info->disp_info->ppi_mode == USE_RGB565_16_BIT_PPI);
- if (ret) {
- dev_err(&pdev->dev, "couldn't request gpio port\n");
- goto out6;
- }
-
- info->irq = platform_get_irq(pdev, 0);
- if (info->irq < 0) {
- ret = -EINVAL;
- goto out7;
- }
-
- ret = request_irq(info->irq, bfin_lq035q1_irq_error, 0,
- DRIVER_NAME" PPI ERROR", info);
- if (ret < 0) {
- dev_err(&pdev->dev, "unable to request PPI ERROR IRQ\n");
- goto out7;
- }
-
- info->spidrv.driver.name = DRIVER_NAME"-spi";
- info->spidrv.probe = lq035q1_spidev_probe;
- info->spidrv.remove = lq035q1_spidev_remove;
- info->spidrv.shutdown = lq035q1_spidev_shutdown;
- info->spidrv.driver.pm = LQ035Q1_SPIDEV_PM_OPS;
-
- ret = spi_register_driver(&info->spidrv);
- if (ret < 0) {
- dev_err(&pdev->dev, "couldn't register SPI Interface\n");
- goto out8;
- }
-
- if (info->disp_info->use_bl) {
- ret = gpio_request_one(info->disp_info->gpio_bl,
- GPIOF_OUT_INIT_LOW, "LQ035 Backlight");
-
- if (ret) {
- dev_err(&pdev->dev, "failed to request GPIO %d\n",
- info->disp_info->gpio_bl);
- goto out9;
- }
- }
-
- ret = register_framebuffer(fbinfo);
- if (ret < 0) {
- dev_err(&pdev->dev, "unable to register framebuffer\n");
- goto out10;
- }
-
- dev_info(&pdev->dev, "%dx%d %d-bit RGB FrameBuffer initialized\n",
- LCD_X_RES, LCD_Y_RES, info->lcd_bpp);
-
- return 0;
-
- out10:
- if (info->disp_info->use_bl)
- gpio_free(info->disp_info->gpio_bl);
- out9:
- spi_unregister_driver(&info->spidrv);
- out8:
- free_irq(info->irq, info);
- out7:
- bfin_lq035q1_free_ports(info->disp_info->ppi_mode ==
- USE_RGB565_16_BIT_PPI);
- out6:
- fb_dealloc_cmap(&fbinfo->cmap);
- out4:
- dma_free_coherent(NULL, fbinfo->fix.smem_len, info->fb_buffer,
- info->dma_handle);
- out3:
- framebuffer_release(fbinfo);
- out2:
- free_dma(CH_PPI);
- out1:
-
- return ret;
-}
-
-static int bfin_lq035q1_remove(struct platform_device *pdev)
-{
- struct fb_info *fbinfo = platform_get_drvdata(pdev);
- struct bfin_lq035q1fb_info *info = fbinfo->par;
-
- if (info->disp_info->use_bl)
- gpio_free(info->disp_info->gpio_bl);
-
- spi_unregister_driver(&info->spidrv);
-
- unregister_framebuffer(fbinfo);
-
- free_dma(CH_PPI);
- free_irq(info->irq, info);
-
- if (info->fb_buffer != NULL)
- dma_free_coherent(NULL, fbinfo->fix.smem_len, info->fb_buffer,
- info->dma_handle);
-
- fb_dealloc_cmap(&fbinfo->cmap);
-
- bfin_lq035q1_free_ports(info->disp_info->ppi_mode ==
- USE_RGB565_16_BIT_PPI);
-
- framebuffer_release(fbinfo);
-
- dev_info(&pdev->dev, "unregistered LCD driver\n");
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int bfin_lq035q1_suspend(struct device *dev)
-{
- struct fb_info *fbinfo = dev_get_drvdata(dev);
- struct bfin_lq035q1fb_info *info = fbinfo->par;
-
- if (info->lq035_open_cnt) {
- lq035q1_backlight(info, 0);
- bfin_lq035q1_disable_ppi();
- SSYNC();
- disable_dma(CH_PPI);
- bfin_lq035q1_stop_timers();
- bfin_write_PPI_STATUS(-1);
- }
-
- return 0;
-}
-
-static int bfin_lq035q1_resume(struct device *dev)
-{
- struct fb_info *fbinfo = dev_get_drvdata(dev);
- struct bfin_lq035q1fb_info *info = fbinfo->par;
-
- if (info->lq035_open_cnt) {
- bfin_lq035q1_disable_ppi();
- SSYNC();
-
- bfin_lq035q1_config_dma(info);
- bfin_lq035q1_config_ppi(info);
- bfin_lq035q1_init_timers(info);
-
- /* start dma */
- enable_dma(CH_PPI);
- bfin_lq035q1_enable_ppi();
- bfin_lq035q1_start_timers();
- lq035q1_backlight(info, 1);
- }
-
- return 0;
-}
-
-static const struct dev_pm_ops bfin_lq035q1_dev_pm_ops = {
- .suspend = bfin_lq035q1_suspend,
- .resume = bfin_lq035q1_resume,
-};
-#endif
-
-static struct platform_driver bfin_lq035q1_driver = {
- .probe = bfin_lq035q1_probe,
- .remove = bfin_lq035q1_remove,
- .driver = {
- .name = DRIVER_NAME,
-#ifdef CONFIG_PM
- .pm = &bfin_lq035q1_dev_pm_ops,
-#endif
- },
-};
-
-module_platform_driver(bfin_lq035q1_driver);
-
-MODULE_DESCRIPTION("Blackfin TFT LCD Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/video/fbdev/bfin-t350mcqb-fb.c b/drivers/video/fbdev/bfin-t350mcqb-fb.c
deleted file mode 100644
index e5ee4d9677f7..000000000000
--- a/drivers/video/fbdev/bfin-t350mcqb-fb.c
+++ /dev/null
@@ -1,669 +0,0 @@
-/*
- * File: drivers/video/bfin-t350mcqb-fb.c
- * Based on:
- * Author: Michael Hennerich <hennerich@blackfin.uclinux.org>
- *
- * Created:
- * Description: Blackfin LCD Framebuffer driver
- *
- *
- * Modified:
- * Copyright 2004-2007 Analog Devices Inc.
- *
- * Bugs: Enter bugs at http://blackfin.uclinux.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, see the file COPYING, or write
- * to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/gfp.h>
-#include <linux/fb.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/backlight.h>
-#include <linux/lcd.h>
-#include <linux/dma-mapping.h>
-#include <linux/platform_device.h>
-
-#include <asm/blackfin.h>
-#include <asm/irq.h>
-#include <asm/dma-mapping.h>
-#include <asm/dma.h>
-#include <asm/portmux.h>
-#include <asm/gptimers.h>
-
-#define NO_BL_SUPPORT
-
-#define LCD_X_RES 320 /* Horizontal Resolution */
-#define LCD_Y_RES 240 /* Vertical Resolution */
-#define LCD_BPP 24 /* Bit Per Pixel */
-
-#define DMA_BUS_SIZE 16
-#define LCD_CLK (12*1000*1000) /* 12MHz */
-
-#define CLOCKS_PER_PIX 3
-
- /*
- * HS and VS timing parameters (all in number of PPI clk ticks)
- */
-
-#define U_LINE 1 /* Blanking Lines */
-
-#define H_ACTPIX (LCD_X_RES * CLOCKS_PER_PIX) /* active horizontal pixel */
-#define H_PERIOD (408 * CLOCKS_PER_PIX) /* HS period */
-#define H_PULSE 90 /* HS pulse width */
-#define H_START 204 /* first valid pixel */
-
-#define V_LINES (LCD_Y_RES + U_LINE) /* total vertical lines */
-#define V_PULSE (3 * H_PERIOD) /* VS pulse width (1-5 H_PERIODs) */
-#define V_PERIOD (H_PERIOD * V_LINES) /* VS period */
-
-#define ACTIVE_VIDEO_MEM_OFFSET (U_LINE * H_ACTPIX)
-
-#define BFIN_LCD_NBR_PALETTE_ENTRIES 256
-
-#define DRIVER_NAME "bfin-t350mcqb"
-static char driver_name[] = DRIVER_NAME;
-
-struct bfin_t350mcqbfb_info {
- struct fb_info *fb;
- struct device *dev;
- unsigned char *fb_buffer; /* RGB Buffer */
- dma_addr_t dma_handle;
- int lq043_open_cnt;
- int irq;
- spinlock_t lock; /* lock */
- u32 pseudo_pal[16];
-};
-
-static int nocursor;
-module_param(nocursor, int, 0644);
-MODULE_PARM_DESC(nocursor, "cursor enable/disable");
-
-#define PPI_TX_MODE 0x2
-#define PPI_XFER_TYPE_11 0xC
-#define PPI_PORT_CFG_01 0x10
-#define PPI_PACK_EN 0x80
-#define PPI_POLS_1 0x8000
-
-static void bfin_t350mcqb_config_ppi(struct bfin_t350mcqbfb_info *fbi)
-{
- bfin_write_PPI_DELAY(H_START);
- bfin_write_PPI_COUNT(H_ACTPIX-1);
- bfin_write_PPI_FRAME(V_LINES);
-
- bfin_write_PPI_CONTROL(PPI_TX_MODE | /* output mode , PORT_DIR */
- PPI_XFER_TYPE_11 | /* sync mode XFR_TYPE */
- PPI_PORT_CFG_01 | /* two frame sync PORT_CFG */
- PPI_PACK_EN | /* packing enabled PACK_EN */
- PPI_POLS_1); /* faling edge syncs POLS */
-}
-
-static inline void bfin_t350mcqb_disable_ppi(void)
-{
- bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() & ~PORT_EN);
-}
-
-static inline void bfin_t350mcqb_enable_ppi(void)
-{
- bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() | PORT_EN);
-}
-
-static void bfin_t350mcqb_start_timers(void)
-{
- unsigned long flags;
-
- local_irq_save(flags);
- enable_gptimers(TIMER1bit);
- enable_gptimers(TIMER0bit);
- local_irq_restore(flags);
-}
-
-static void bfin_t350mcqb_stop_timers(void)
-{
- disable_gptimers(TIMER0bit | TIMER1bit);
-
- set_gptimer_status(0, TIMER_STATUS_TRUN0 | TIMER_STATUS_TRUN1 |
- TIMER_STATUS_TIMIL0 | TIMER_STATUS_TIMIL1 |
- TIMER_STATUS_TOVF0 | TIMER_STATUS_TOVF1);
-
-}
-
-static void bfin_t350mcqb_init_timers(void)
-{
-
- bfin_t350mcqb_stop_timers();
-
- set_gptimer_period(TIMER0_id, H_PERIOD);
- set_gptimer_pwidth(TIMER0_id, H_PULSE);
- set_gptimer_config(TIMER0_id, TIMER_MODE_PWM | TIMER_PERIOD_CNT |
- TIMER_TIN_SEL | TIMER_CLK_SEL|
- TIMER_EMU_RUN);
-
- set_gptimer_period(TIMER1_id, V_PERIOD);
- set_gptimer_pwidth(TIMER1_id, V_PULSE);
- set_gptimer_config(TIMER1_id, TIMER_MODE_PWM | TIMER_PERIOD_CNT |
- TIMER_TIN_SEL | TIMER_CLK_SEL |
- TIMER_EMU_RUN);
-
-}
-
-static void bfin_t350mcqb_config_dma(struct bfin_t350mcqbfb_info *fbi)
-{
-
- set_dma_config(CH_PPI,
- set_bfin_dma_config(DIR_READ, DMA_FLOW_AUTO,
- INTR_DISABLE, DIMENSION_2D,
- DATA_SIZE_16,
- DMA_NOSYNC_KEEP_DMA_BUF));
- set_dma_x_count(CH_PPI, (LCD_X_RES * LCD_BPP) / DMA_BUS_SIZE);
- set_dma_x_modify(CH_PPI, DMA_BUS_SIZE / 8);
- set_dma_y_count(CH_PPI, V_LINES);
-
- set_dma_y_modify(CH_PPI, DMA_BUS_SIZE / 8);
- set_dma_start_addr(CH_PPI, (unsigned long)fbi->fb_buffer);
-
-}
-
-static u16 ppi0_req_8[] = {P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2,
- P_PPI0_D0, P_PPI0_D1, P_PPI0_D2,
- P_PPI0_D3, P_PPI0_D4, P_PPI0_D5,
- P_PPI0_D6, P_PPI0_D7, 0};
-
-static int bfin_t350mcqb_request_ports(int action)
-{
- if (action) {
- if (peripheral_request_list(ppi0_req_8, DRIVER_NAME)) {
- printk(KERN_ERR "Requesting Peripherals failed\n");
- return -EFAULT;
- }
- } else
- peripheral_free_list(ppi0_req_8);
-
- return 0;
-}
-
-static int bfin_t350mcqb_fb_open(struct fb_info *info, int user)
-{
- struct bfin_t350mcqbfb_info *fbi = info->par;
-
- spin_lock(&fbi->lock);
- fbi->lq043_open_cnt++;
-
- if (fbi->lq043_open_cnt <= 1) {
-
- bfin_t350mcqb_disable_ppi();
- SSYNC();
-
- bfin_t350mcqb_config_dma(fbi);
- bfin_t350mcqb_config_ppi(fbi);
- bfin_t350mcqb_init_timers();
-
- /* start dma */
- enable_dma(CH_PPI);
- bfin_t350mcqb_enable_ppi();
- bfin_t350mcqb_start_timers();
- }
-
- spin_unlock(&fbi->lock);
-
- return 0;
-}
-
-static int bfin_t350mcqb_fb_release(struct fb_info *info, int user)
-{
- struct bfin_t350mcqbfb_info *fbi = info->par;
-
- spin_lock(&fbi->lock);
-
- fbi->lq043_open_cnt--;
-
- if (fbi->lq043_open_cnt <= 0) {
- bfin_t350mcqb_disable_ppi();
- SSYNC();
- disable_dma(CH_PPI);
- bfin_t350mcqb_stop_timers();
- }
-
- spin_unlock(&fbi->lock);
-
- return 0;
-}
-
-static int bfin_t350mcqb_fb_check_var(struct fb_var_screeninfo *var,
- struct fb_info *info)
-{
-
- switch (var->bits_per_pixel) {
- case 24:/* TRUECOLOUR, 16m */
- var->red.offset = 0;
- var->green.offset = 8;
- var->blue.offset = 16;
- var->red.length = var->green.length = var->blue.length = 8;
- var->transp.offset = 0;
- var->transp.length = 0;
- var->transp.msb_right = 0;
- var->red.msb_right = 0;
- var->green.msb_right = 0;
- var->blue.msb_right = 0;
- break;
- default:
- pr_debug("%s: depth not supported: %u BPP\n", __func__,
- var->bits_per_pixel);
- return -EINVAL;
- }
-
- if (info->var.xres != var->xres || info->var.yres != var->yres ||
- info->var.xres_virtual != var->xres_virtual ||
- info->var.yres_virtual != var->yres_virtual) {
- pr_debug("%s: Resolution not supported: X%u x Y%u \n",
- __func__, var->xres, var->yres);
- return -EINVAL;
- }
-
- /*
- * Memory limit
- */
-
- if ((info->fix.line_length * var->yres_virtual) > info->fix.smem_len) {
- pr_debug("%s: Memory Limit requested yres_virtual = %u\n",
- __func__, var->yres_virtual);
- return -ENOMEM;
- }
-
- return 0;
-}
-
-int bfin_t350mcqb_fb_cursor(struct fb_info *info, struct fb_cursor *cursor)
-{
- if (nocursor)
- return 0;
- else
- return -EINVAL; /* just to force soft_cursor() call */
-}
-
-static int bfin_t350mcqb_fb_setcolreg(u_int regno, u_int red, u_int green,
- u_int blue, u_int transp,
- struct fb_info *info)
-{
- if (regno >= BFIN_LCD_NBR_PALETTE_ENTRIES)
- return -EINVAL;
-
- if (info->var.grayscale) {
- /* grayscale = 0.30*R + 0.59*G + 0.11*B */
- red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
- }
-
- if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
-
- u32 value;
- /* Place color in the pseudopalette */
- if (regno > 16)
- return -EINVAL;
-
- red >>= (16 - info->var.red.length);
- green >>= (16 - info->var.green.length);
- blue >>= (16 - info->var.blue.length);
-
- value = (red << info->var.red.offset) |
- (green << info->var.green.offset) |
- (blue << info->var.blue.offset);
- value &= 0xFFFFFF;
-
- ((u32 *) (info->pseudo_palette))[regno] = value;
-
- }
-
- return 0;
-}
-
-static struct fb_ops bfin_t350mcqb_fb_ops = {
- .owner = THIS_MODULE,
- .fb_open = bfin_t350mcqb_fb_open,
- .fb_release = bfin_t350mcqb_fb_release,
- .fb_check_var = bfin_t350mcqb_fb_check_var,
- .fb_fillrect = cfb_fillrect,
- .fb_copyarea = cfb_copyarea,
- .fb_imageblit = cfb_imageblit,
- .fb_cursor = bfin_t350mcqb_fb_cursor,
- .fb_setcolreg = bfin_t350mcqb_fb_setcolreg,
-};
-
-#ifndef NO_BL_SUPPORT
-static int bl_get_brightness(struct backlight_device *bd)
-{
- return 0;
-}
-
-static const struct backlight_ops bfin_lq043fb_bl_ops = {
- .get_brightness = bl_get_brightness,
-};
-
-static struct backlight_device *bl_dev;
-
-static int bfin_lcd_get_power(struct lcd_device *dev)
-{
- return 0;
-}
-
-static int bfin_lcd_set_power(struct lcd_device *dev, int power)
-{
- return 0;
-}
-
-static int bfin_lcd_get_contrast(struct lcd_device *dev)
-{
- return 0;
-}
-
-static int bfin_lcd_set_contrast(struct lcd_device *dev, int contrast)
-{
-
- return 0;
-}
-
-static int bfin_lcd_check_fb(struct lcd_device *dev, struct fb_info *fi)
-{
- if (!fi || (fi == &bfin_t350mcqb_fb))
- return 1;
- return 0;
-}
-
-static struct lcd_ops bfin_lcd_ops = {
- .get_power = bfin_lcd_get_power,
- .set_power = bfin_lcd_set_power,
- .get_contrast = bfin_lcd_get_contrast,
- .set_contrast = bfin_lcd_set_contrast,
- .check_fb = bfin_lcd_check_fb,
-};
-
-static struct lcd_device *lcd_dev;
-#endif
-
-static irqreturn_t bfin_t350mcqb_irq_error(int irq, void *dev_id)
-{
- /*struct bfin_t350mcqbfb_info *info = (struct bfin_t350mcqbfb_info *)dev_id;*/
-
- u16 status = bfin_read_PPI_STATUS();
- bfin_write_PPI_STATUS(0xFFFF);
-
- if (status) {
- bfin_t350mcqb_disable_ppi();
- disable_dma(CH_PPI);
-
- /* start dma */
- enable_dma(CH_PPI);
- bfin_t350mcqb_enable_ppi();
- bfin_write_PPI_STATUS(0xFFFF);
- }
-
- return IRQ_HANDLED;
-}
-
-static int bfin_t350mcqb_probe(struct platform_device *pdev)
-{
-#ifndef NO_BL_SUPPORT
- struct backlight_properties props;
-#endif
- struct bfin_t350mcqbfb_info *info;
- struct fb_info *fbinfo;
- int ret;
-
- printk(KERN_INFO DRIVER_NAME ": %dx%d %d-bit RGB FrameBuffer initializing...\n",
- LCD_X_RES, LCD_Y_RES, LCD_BPP);
-
- if (request_dma(CH_PPI, "CH_PPI") < 0) {
- printk(KERN_ERR DRIVER_NAME
- ": couldn't request CH_PPI DMA\n");
- ret = -EFAULT;
- goto out1;
- }
-
- fbinfo =
- framebuffer_alloc(sizeof(struct bfin_t350mcqbfb_info), &pdev->dev);
- if (!fbinfo) {
- ret = -ENOMEM;
- goto out2;
- }
-
- info = fbinfo->par;
- info->fb = fbinfo;
- info->dev = &pdev->dev;
- spin_lock_init(&info->lock);
-
- platform_set_drvdata(pdev, fbinfo);
-
- strcpy(fbinfo->fix.id, driver_name);
-
- fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
- fbinfo->fix.type_aux = 0;
- fbinfo->fix.xpanstep = 0;
- fbinfo->fix.ypanstep = 0;
- fbinfo->fix.ywrapstep = 0;
- fbinfo->fix.accel = FB_ACCEL_NONE;
- fbinfo->fix.visual = FB_VISUAL_TRUECOLOR;
-
- fbinfo->var.nonstd = 0;
- fbinfo->var.activate = FB_ACTIVATE_NOW;
- fbinfo->var.height = 53;
- fbinfo->var.width = 70;
- fbinfo->var.accel_flags = 0;
- fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
-
- fbinfo->var.xres = LCD_X_RES;
- fbinfo->var.xres_virtual = LCD_X_RES;
- fbinfo->var.yres = LCD_Y_RES;
- fbinfo->var.yres_virtual = LCD_Y_RES;
- fbinfo->var.bits_per_pixel = LCD_BPP;
-
- fbinfo->var.red.offset = 0;
- fbinfo->var.green.offset = 8;
- fbinfo->var.blue.offset = 16;
- fbinfo->var.transp.offset = 0;
- fbinfo->var.red.length = 8;
- fbinfo->var.green.length = 8;
- fbinfo->var.blue.length = 8;
- fbinfo->var.transp.length = 0;
- fbinfo->fix.smem_len = LCD_X_RES * LCD_Y_RES * LCD_BPP / 8;
-
- fbinfo->fix.line_length = fbinfo->var.xres_virtual *
- fbinfo->var.bits_per_pixel / 8;
-
-
- fbinfo->fbops = &bfin_t350mcqb_fb_ops;
- fbinfo->flags = FBINFO_FLAG_DEFAULT;
-
- info->fb_buffer = dma_alloc_coherent(NULL, fbinfo->fix.smem_len +
- ACTIVE_VIDEO_MEM_OFFSET,
- &info->dma_handle, GFP_KERNEL);
-
- if (NULL == info->fb_buffer) {
- printk(KERN_ERR DRIVER_NAME
- ": couldn't allocate dma buffer.\n");
- ret = -ENOMEM;
- goto out3;
- }
-
- fbinfo->screen_base = (void *)info->fb_buffer + ACTIVE_VIDEO_MEM_OFFSET;
- fbinfo->fix.smem_start = (int)info->fb_buffer + ACTIVE_VIDEO_MEM_OFFSET;
-
- fbinfo->fbops = &bfin_t350mcqb_fb_ops;
-
- fbinfo->pseudo_palette = &info->pseudo_pal;
-
- if (fb_alloc_cmap(&fbinfo->cmap, BFIN_LCD_NBR_PALETTE_ENTRIES, 0)
- < 0) {
- printk(KERN_ERR DRIVER_NAME
- "Fail to allocate colormap (%d entries)\n",
- BFIN_LCD_NBR_PALETTE_ENTRIES);
- ret = -EFAULT;
- goto out4;
- }
-
- if (bfin_t350mcqb_request_ports(1)) {
- printk(KERN_ERR DRIVER_NAME ": couldn't request gpio port.\n");
- ret = -EFAULT;
- goto out6;
- }
-
- info->irq = platform_get_irq(pdev, 0);
- if (info->irq < 0) {
- ret = -EINVAL;
- goto out7;
- }
-
- ret = request_irq(info->irq, bfin_t350mcqb_irq_error, 0,
- "PPI ERROR", info);
- if (ret < 0) {
- printk(KERN_ERR DRIVER_NAME
- ": unable to request PPI ERROR IRQ\n");
- goto out7;
- }
-
- if (register_framebuffer(fbinfo) < 0) {
- printk(KERN_ERR DRIVER_NAME
- ": unable to register framebuffer.\n");
- ret = -EINVAL;
- goto out8;
- }
-#ifndef NO_BL_SUPPORT
- memset(&props, 0, sizeof(struct backlight_properties));
- props.type = BACKLIGHT_RAW;
- props.max_brightness = 255;
- bl_dev = backlight_device_register("bf52x-bl", NULL, NULL,
- &bfin_lq043fb_bl_ops, &props);
- if (IS_ERR(bl_dev)) {
- printk(KERN_ERR DRIVER_NAME
- ": unable to register backlight.\n");
- ret = -EINVAL;
- unregister_framebuffer(fbinfo);
- goto out8;
- }
-
- lcd_dev = lcd_device_register(DRIVER_NAME, NULL, &bfin_lcd_ops);
- lcd_dev->props.max_contrast = 255, printk(KERN_INFO "Done.\n");
-#endif
-
- return 0;
-
-out8:
- free_irq(info->irq, info);
-out7:
- bfin_t350mcqb_request_ports(0);
-out6:
- fb_dealloc_cmap(&fbinfo->cmap);
-out4:
- dma_free_coherent(NULL, fbinfo->fix.smem_len + ACTIVE_VIDEO_MEM_OFFSET,
- info->fb_buffer, info->dma_handle);
-out3:
- framebuffer_release(fbinfo);
-out2:
- free_dma(CH_PPI);
-out1:
-
- return ret;
-}
-
-static int bfin_t350mcqb_remove(struct platform_device *pdev)
-{
-
- struct fb_info *fbinfo = platform_get_drvdata(pdev);
- struct bfin_t350mcqbfb_info *info = fbinfo->par;
-
- unregister_framebuffer(fbinfo);
-
- free_dma(CH_PPI);
- free_irq(info->irq, info);
-
- if (info->fb_buffer != NULL)
- dma_free_coherent(NULL, fbinfo->fix.smem_len +
- ACTIVE_VIDEO_MEM_OFFSET, info->fb_buffer,
- info->dma_handle);
-
- fb_dealloc_cmap(&fbinfo->cmap);
-
-#ifndef NO_BL_SUPPORT
- lcd_device_unregister(lcd_dev);
- backlight_device_unregister(bl_dev);
-#endif
-
- bfin_t350mcqb_request_ports(0);
-
- framebuffer_release(fbinfo);
-
- printk(KERN_INFO DRIVER_NAME ": Unregister LCD driver.\n");
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int bfin_t350mcqb_suspend(struct platform_device *pdev, pm_message_t state)
-{
- struct fb_info *fbinfo = platform_get_drvdata(pdev);
- struct bfin_t350mcqbfb_info *fbi = fbinfo->par;
-
- if (fbi->lq043_open_cnt) {
- bfin_t350mcqb_disable_ppi();
- disable_dma(CH_PPI);
- bfin_t350mcqb_stop_timers();
- bfin_write_PPI_STATUS(-1);
- }
-
-
- return 0;
-}
-
-static int bfin_t350mcqb_resume(struct platform_device *pdev)
-{
- struct fb_info *fbinfo = platform_get_drvdata(pdev);
- struct bfin_t350mcqbfb_info *fbi = fbinfo->par;
-
- if (fbi->lq043_open_cnt) {
- bfin_t350mcqb_config_dma(fbi);
- bfin_t350mcqb_config_ppi(fbi);
- bfin_t350mcqb_init_timers();
-
- /* start dma */
- enable_dma(CH_PPI);
- bfin_t350mcqb_enable_ppi();
- bfin_t350mcqb_start_timers();
- }
-
- return 0;
-}
-#else
-#define bfin_t350mcqb_suspend NULL
-#define bfin_t350mcqb_resume NULL
-#endif
-
-static struct platform_driver bfin_t350mcqb_driver = {
- .probe = bfin_t350mcqb_probe,
- .remove = bfin_t350mcqb_remove,
- .suspend = bfin_t350mcqb_suspend,
- .resume = bfin_t350mcqb_resume,
- .driver = {
- .name = DRIVER_NAME,
- },
-};
-module_platform_driver(bfin_t350mcqb_driver);
-
-MODULE_DESCRIPTION("Blackfin TFT LCD Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/video/fbdev/bfin_adv7393fb.c b/drivers/video/fbdev/bfin_adv7393fb.c
deleted file mode 100644
index 542ffaddc6ab..000000000000
--- a/drivers/video/fbdev/bfin_adv7393fb.c
+++ /dev/null
@@ -1,828 +0,0 @@
-/*
- * Frame buffer driver for ADV7393/2 video encoder
- *
- * Copyright 2006-2009 Analog Devices Inc.
- * Licensed under the GPL-2 or late.
- */
-
-/*
- * TODO: Remove Globals
- * TODO: Code Cleanup
- */
-
-#define DRIVER_NAME "bfin-adv7393"
-
-#define pr_fmt(fmt) DRIVER_NAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/tty.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/fb.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/interrupt.h>
-#include <linux/sched.h>
-#include <asm/blackfin.h>
-#include <asm/irq.h>
-#include <asm/dma.h>
-#include <linux/uaccess.h>
-#include <linux/gpio.h>
-#include <asm/portmux.h>
-
-#include <linux/dma-mapping.h>
-#include <linux/proc_fs.h>
-#include <linux/platform_device.h>
-#include <linux/i2c.h>
-
-#include "bfin_adv7393fb.h"
-
-static int mode = VMODE;
-static int mem = VMEM;
-static int nocursor = 1;
-
-static const unsigned short ppi_pins[] = {
- P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2,
- P_PPI0_D0, P_PPI0_D1, P_PPI0_D2, P_PPI0_D3,
- P_PPI0_D4, P_PPI0_D5, P_PPI0_D6, P_PPI0_D7,
- P_PPI0_D8, P_PPI0_D9, P_PPI0_D10, P_PPI0_D11,
- P_PPI0_D12, P_PPI0_D13, P_PPI0_D14, P_PPI0_D15,
- 0
-};
-
-/*
- * card parameters
- */
-
-static struct bfin_adv7393_fb_par {
- /* structure holding blackfin / adv7393 parameters when
- screen is blanked */
- struct {
- u8 Mode; /* ntsc/pal/? */
- } vga_state;
- atomic_t ref_count;
-} bfin_par;
-
-/* --------------------------------------------------------------------- */
-
-static struct fb_var_screeninfo bfin_adv7393_fb_defined = {
- .xres = 720,
- .yres = 480,
- .xres_virtual = 720,
- .yres_virtual = 480,
- .bits_per_pixel = 16,
- .activate = FB_ACTIVATE_TEST,
- .height = -1,
- .width = -1,
- .left_margin = 0,
- .right_margin = 0,
- .upper_margin = 0,
- .lower_margin = 0,
- .vmode = FB_VMODE_INTERLACED,
- .red = {11, 5, 0},
- .green = {5, 6, 0},
- .blue = {0, 5, 0},
- .transp = {0, 0, 0},
-};
-
-static struct fb_fix_screeninfo bfin_adv7393_fb_fix = {
- .id = "BFIN ADV7393",
- .smem_len = 720 * 480 * 2,
- .type = FB_TYPE_PACKED_PIXELS,
- .visual = FB_VISUAL_TRUECOLOR,
- .xpanstep = 0,
- .ypanstep = 0,
- .line_length = 720 * 2,
- .accel = FB_ACCEL_NONE
-};
-
-static struct fb_ops bfin_adv7393_fb_ops = {
- .owner = THIS_MODULE,
- .fb_open = bfin_adv7393_fb_open,
- .fb_release = bfin_adv7393_fb_release,
- .fb_check_var = bfin_adv7393_fb_check_var,
- .fb_pan_display = bfin_adv7393_fb_pan_display,
- .fb_blank = bfin_adv7393_fb_blank,
- .fb_fillrect = cfb_fillrect,
- .fb_copyarea = cfb_copyarea,
- .fb_imageblit = cfb_imageblit,
- .fb_cursor = bfin_adv7393_fb_cursor,
- .fb_setcolreg = bfin_adv7393_fb_setcolreg,
-};
-
-static int dma_desc_list(struct adv7393fb_device *fbdev, u16 arg)
-{
- if (arg == BUILD) { /* Build */
- fbdev->vb1 = l1_data_sram_zalloc(sizeof(struct dmasg));
- if (fbdev->vb1 == NULL)
- goto error;
-
- fbdev->av1 = l1_data_sram_zalloc(sizeof(struct dmasg));
- if (fbdev->av1 == NULL)
- goto error;
-
- fbdev->vb2 = l1_data_sram_zalloc(sizeof(struct dmasg));
- if (fbdev->vb2 == NULL)
- goto error;
-
- fbdev->av2 = l1_data_sram_zalloc(sizeof(struct dmasg));
- if (fbdev->av2 == NULL)
- goto error;
-
- /* Build linked DMA descriptor list */
- fbdev->vb1->next_desc_addr = fbdev->av1;
- fbdev->av1->next_desc_addr = fbdev->vb2;
- fbdev->vb2->next_desc_addr = fbdev->av2;
- fbdev->av2->next_desc_addr = fbdev->vb1;
-
- /* Save list head */
- fbdev->descriptor_list_head = fbdev->av2;
-
- /* Vertical Blanking Field 1 */
- fbdev->vb1->start_addr = VB_DUMMY_MEMORY_SOURCE;
- fbdev->vb1->cfg = DMA_CFG_VAL;
-
- fbdev->vb1->x_count =
- fbdev->modes[mode].xres + fbdev->modes[mode].boeft_blank;
-
- fbdev->vb1->x_modify = 0;
- fbdev->vb1->y_count = fbdev->modes[mode].vb1_lines;
- fbdev->vb1->y_modify = 0;
-
- /* Active Video Field 1 */
-
- fbdev->av1->start_addr = (unsigned long)fbdev->fb_mem;
- fbdev->av1->cfg = DMA_CFG_VAL;
- fbdev->av1->x_count =
- fbdev->modes[mode].xres + fbdev->modes[mode].boeft_blank;
- fbdev->av1->x_modify = fbdev->modes[mode].bpp / 8;
- fbdev->av1->y_count = fbdev->modes[mode].a_lines;
- fbdev->av1->y_modify =
- (fbdev->modes[mode].xres - fbdev->modes[mode].boeft_blank +
- 1) * (fbdev->modes[mode].bpp / 8);
-
- /* Vertical Blanking Field 2 */
-
- fbdev->vb2->start_addr = VB_DUMMY_MEMORY_SOURCE;
- fbdev->vb2->cfg = DMA_CFG_VAL;
- fbdev->vb2->x_count =
- fbdev->modes[mode].xres + fbdev->modes[mode].boeft_blank;
-
- fbdev->vb2->x_modify = 0;
- fbdev->vb2->y_count = fbdev->modes[mode].vb2_lines;
- fbdev->vb2->y_modify = 0;
-
- /* Active Video Field 2 */
-
- fbdev->av2->start_addr =
- (unsigned long)fbdev->fb_mem + fbdev->line_len;
-
- fbdev->av2->cfg = DMA_CFG_VAL;
-
- fbdev->av2->x_count =
- fbdev->modes[mode].xres + fbdev->modes[mode].boeft_blank;
-
- fbdev->av2->x_modify = (fbdev->modes[mode].bpp / 8);
- fbdev->av2->y_count = fbdev->modes[mode].a_lines;
-
- fbdev->av2->y_modify =
- (fbdev->modes[mode].xres - fbdev->modes[mode].boeft_blank +
- 1) * (fbdev->modes[mode].bpp / 8);
-
- return 1;
- }
-
-error:
- l1_data_sram_free(fbdev->vb1);
- l1_data_sram_free(fbdev->av1);
- l1_data_sram_free(fbdev->vb2);
- l1_data_sram_free(fbdev->av2);
-
- return 0;
-}
-
-static int bfin_config_dma(struct adv7393fb_device *fbdev)
-{
- BUG_ON(!(fbdev->fb_mem));
-
- set_dma_x_count(CH_PPI, fbdev->descriptor_list_head->x_count);
- set_dma_x_modify(CH_PPI, fbdev->descriptor_list_head->x_modify);
- set_dma_y_count(CH_PPI, fbdev->descriptor_list_head->y_count);
- set_dma_y_modify(CH_PPI, fbdev->descriptor_list_head->y_modify);
- set_dma_start_addr(CH_PPI, fbdev->descriptor_list_head->start_addr);
- set_dma_next_desc_addr(CH_PPI,
- fbdev->descriptor_list_head->next_desc_addr);
- set_dma_config(CH_PPI, fbdev->descriptor_list_head->cfg);
-
- return 1;
-}
-
-static void bfin_disable_dma(void)
-{
- bfin_write_DMA0_CONFIG(bfin_read_DMA0_CONFIG() & ~DMAEN);
-}
-
-static void bfin_config_ppi(struct adv7393fb_device *fbdev)
-{
- if (ANOMALY_05000183) {
- bfin_write_TIMER2_CONFIG(WDTH_CAP);
- bfin_write_TIMER_ENABLE(TIMEN2);
- }
-
- bfin_write_PPI_CONTROL(0x381E);
- bfin_write_PPI_FRAME(fbdev->modes[mode].tot_lines);
- bfin_write_PPI_COUNT(fbdev->modes[mode].xres +
- fbdev->modes[mode].boeft_blank - 1);
- bfin_write_PPI_DELAY(fbdev->modes[mode].aoeft_blank - 1);
-}
-
-static void bfin_enable_ppi(void)
-{
- bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() | PORT_EN);
-}
-
-static void bfin_disable_ppi(void)
-{
- bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() & ~PORT_EN);
-}
-
-static inline int adv7393_write(struct i2c_client *client, u8 reg, u8 value)
-{
- return i2c_smbus_write_byte_data(client, reg, value);
-}
-
-static inline int adv7393_read(struct i2c_client *client, u8 reg)
-{
- return i2c_smbus_read_byte_data(client, reg);
-}
-
-static int
-adv7393_write_block(struct i2c_client *client,
- const u8 *data, unsigned int len)
-{
- int ret = -1;
- u8 reg;
-
- while (len >= 2) {
- reg = *data++;
- ret = adv7393_write(client, reg, *data++);
- if (ret < 0)
- break;
- len -= 2;
- }
-
- return ret;
-}
-
-static int adv7393_mode(struct i2c_client *client, u16 mode)
-{
- switch (mode) {
- case POWER_ON: /* ADV7393 Sleep mode OFF */
- adv7393_write(client, 0x00, 0x1E);
- break;
- case POWER_DOWN: /* ADV7393 Sleep mode ON */
- adv7393_write(client, 0x00, 0x1F);
- break;
- case BLANK_OFF: /* Pixel Data Valid */
- adv7393_write(client, 0x82, 0xCB);
- break;
- case BLANK_ON: /* Pixel Data Invalid */
- adv7393_write(client, 0x82, 0x8B);
- break;
- default:
- return -EINVAL;
- break;
- }
- return 0;
-}
-
-static irqreturn_t ppi_irq_error(int irq, void *dev_id)
-{
-
- struct adv7393fb_device *fbdev = (struct adv7393fb_device *)dev_id;
-
- u16 status = bfin_read_PPI_STATUS();
-
- pr_debug("%s: PPI Status = 0x%X\n", __func__, status);
-
- if (status) {
- bfin_disable_dma(); /* TODO: Check Sequence */
- bfin_disable_ppi();
- bfin_clear_PPI_STATUS();
- bfin_config_dma(fbdev);
- bfin_enable_ppi();
- }
-
- return IRQ_HANDLED;
-
-}
-
-static int proc_output(char *buf)
-{
- char *p = buf;
-
- p += sprintf(p,
- "Usage:\n"
- "echo 0x[REG][Value] > adv7393\n"
- "example: echo 0x1234 >adv7393\n"
- "writes 0x34 into Register 0x12\n");
-
- return p - buf;
-}
-
-static ssize_t
-adv7393_read_proc(struct file *file, char __user *buf,
- size_t size, loff_t *ppos)
-{
- static const char message[] = "Usage:\n"
- "echo 0x[REG][Value] > adv7393\n"
- "example: echo 0x1234 >adv7393\n"
- "writes 0x34 into Register 0x12\n";
- return simple_read_from_buffer(buf, size, ppos, message,
- sizeof(message));
-}
-
-static ssize_t
-adv7393_write_proc(struct file *file, const char __user * buffer,
- size_t count, loff_t *ppos)
-{
- struct adv7393fb_device *fbdev = PDE_DATA(file_inode(file));
- unsigned int val;
- int ret;
-
- ret = kstrtouint_from_user(buffer, count, 0, &val);
- if (ret)
- return -EFAULT;
-
- adv7393_write(fbdev->client, val >> 8, val & 0xff);
-
- return count;
-}
-
-static const struct file_operations fops = {
- .read = adv7393_read_proc,
- .write = adv7393_write_proc,
- .llseek = default_llseek,
-};
-
-static int bfin_adv7393_fb_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- int ret = 0;
- struct proc_dir_entry *entry;
-
- struct adv7393fb_device *fbdev = NULL;
-
- if (mem > 2) {
- dev_err(&client->dev, "mem out of allowed range [1;2]\n");
- return -EINVAL;
- }
-
- if (mode >= ARRAY_SIZE(known_modes)) {
- dev_err(&client->dev, "mode %d: not supported", mode);
- return -EFAULT;
- }
-
- fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
- if (!fbdev) {
- dev_err(&client->dev, "failed to allocate device private record");
- return -ENOMEM;
- }
-
- i2c_set_clientdata(client, fbdev);
-
- fbdev->modes = known_modes;
- fbdev->client = client;
-
- fbdev->fb_len =
- mem * fbdev->modes[mode].xres * fbdev->modes[mode].xres *
- (fbdev->modes[mode].bpp / 8);
-
- fbdev->line_len =
- fbdev->modes[mode].xres * (fbdev->modes[mode].bpp / 8);
-
- /* Workaround "PPI Does Not Start Properly In Specific Mode" */
- if (ANOMALY_05000400) {
- ret = gpio_request_one(P_IDENT(P_PPI0_FS3), GPIOF_OUT_INIT_LOW,
- "PPI0_FS3");
- if (ret) {
- dev_err(&client->dev, "PPI0_FS3 GPIO request failed\n");
- ret = -EBUSY;
- goto free_fbdev;
- }
- }
-
- if (peripheral_request_list(ppi_pins, DRIVER_NAME)) {
- dev_err(&client->dev, "requesting PPI peripheral failed\n");
- ret = -EFAULT;
- goto free_gpio;
- }
-
- fbdev->fb_mem =
- dma_alloc_coherent(NULL, fbdev->fb_len, &fbdev->dma_handle,
- GFP_KERNEL);
-
- if (NULL == fbdev->fb_mem) {
- dev_err(&client->dev, "couldn't allocate dma buffer (%d bytes)\n",
- (u32) fbdev->fb_len);
- ret = -ENOMEM;
- goto free_ppi_pins;
- }
-
- fbdev->info.screen_base = (void *)fbdev->fb_mem;
- bfin_adv7393_fb_fix.smem_start = (int)fbdev->fb_mem;
-
- bfin_adv7393_fb_fix.smem_len = fbdev->fb_len;
- bfin_adv7393_fb_fix.line_length = fbdev->line_len;
-
- if (mem > 1)
- bfin_adv7393_fb_fix.ypanstep = 1;
-
- bfin_adv7393_fb_defined.red.length = 5;
- bfin_adv7393_fb_defined.green.length = 6;
- bfin_adv7393_fb_defined.blue.length = 5;
-
- bfin_adv7393_fb_defined.xres = fbdev->modes[mode].xres;
- bfin_adv7393_fb_defined.yres = fbdev->modes[mode].yres;
- bfin_adv7393_fb_defined.xres_virtual = fbdev->modes[mode].xres;
- bfin_adv7393_fb_defined.yres_virtual = mem * fbdev->modes[mode].yres;
- bfin_adv7393_fb_defined.bits_per_pixel = fbdev->modes[mode].bpp;
-
- fbdev->info.fbops = &bfin_adv7393_fb_ops;
- fbdev->info.var = bfin_adv7393_fb_defined;
- fbdev->info.fix = bfin_adv7393_fb_fix;
- fbdev->info.par = &bfin_par;
- fbdev->info.flags = FBINFO_DEFAULT;
-
- fbdev->info.pseudo_palette = kzalloc(sizeof(u32) * 16, GFP_KERNEL);
- if (!fbdev->info.pseudo_palette) {
- dev_err(&client->dev, "failed to allocate pseudo_palette\n");
- ret = -ENOMEM;
- goto free_fb_mem;
- }
-
- if (fb_alloc_cmap(&fbdev->info.cmap, BFIN_LCD_NBR_PALETTE_ENTRIES, 0) < 0) {
- dev_err(&client->dev, "failed to allocate colormap (%d entries)\n",
- BFIN_LCD_NBR_PALETTE_ENTRIES);
- ret = -EFAULT;
- goto free_palette;
- }
-
- if (request_dma(CH_PPI, "BF5xx_PPI_DMA") < 0) {
- dev_err(&client->dev, "unable to request PPI DMA\n");
- ret = -EFAULT;
- goto free_cmap;
- }
-
- if (request_irq(IRQ_PPI_ERROR, ppi_irq_error, 0,
- "PPI ERROR", fbdev) < 0) {
- dev_err(&client->dev, "unable to request PPI ERROR IRQ\n");
- ret = -EFAULT;
- goto free_ch_ppi;
- }
-
- fbdev->open = 0;
-
- ret = adv7393_write_block(client, fbdev->modes[mode].adv7393_i2c_initd,
- fbdev->modes[mode].adv7393_i2c_initd_len);
-
- if (ret) {
- dev_err(&client->dev, "i2c attach: init error\n");
- goto free_irq_ppi;
- }
-
-
- if (register_framebuffer(&fbdev->info) < 0) {
- dev_err(&client->dev, "unable to register framebuffer\n");
- ret = -EFAULT;
- goto free_irq_ppi;
- }
-
- dev_info(&client->dev, "fb%d: %s frame buffer device\n",
- fbdev->info.node, fbdev->info.fix.id);
- dev_info(&client->dev, "fb memory address : 0x%p\n", fbdev->fb_mem);
-
- entry = proc_create_data("driver/adv7393", 0, NULL, &fops, fbdev);
- if (!entry) {
- dev_err(&client->dev, "unable to create /proc entry\n");
- ret = -EFAULT;
- goto free_fb;
- }
- return 0;
-
-free_fb:
- unregister_framebuffer(&fbdev->info);
-free_irq_ppi:
- free_irq(IRQ_PPI_ERROR, fbdev);
-free_ch_ppi:
- free_dma(CH_PPI);
-free_cmap:
- fb_dealloc_cmap(&fbdev->info.cmap);
-free_palette:
- kfree(fbdev->info.pseudo_palette);
-free_fb_mem:
- dma_free_coherent(NULL, fbdev->fb_len, fbdev->fb_mem,
- fbdev->dma_handle);
-free_ppi_pins:
- peripheral_free_list(ppi_pins);
-free_gpio:
- if (ANOMALY_05000400)
- gpio_free(P_IDENT(P_PPI0_FS3));
-free_fbdev:
- kfree(fbdev);
-
- return ret;
-}
-
-static int bfin_adv7393_fb_open(struct fb_info *info, int user)
-{
- struct adv7393fb_device *fbdev = to_adv7393fb_device(info);
-
- fbdev->info.screen_base = (void *)fbdev->fb_mem;
- if (!fbdev->info.screen_base) {
- dev_err(&fbdev->client->dev, "unable to map device\n");
- return -ENOMEM;
- }
-
- fbdev->open = 1;
- dma_desc_list(fbdev, BUILD);
- adv7393_mode(fbdev->client, BLANK_OFF);
- bfin_config_ppi(fbdev);
- bfin_config_dma(fbdev);
- bfin_enable_ppi();
-
- return 0;
-}
-
-static int bfin_adv7393_fb_release(struct fb_info *info, int user)
-{
- struct adv7393fb_device *fbdev = to_adv7393fb_device(info);
-
- adv7393_mode(fbdev->client, BLANK_ON);
- bfin_disable_dma();
- bfin_disable_ppi();
- dma_desc_list(fbdev, DESTRUCT);
- fbdev->open = 0;
- return 0;
-}
-
-static int
-bfin_adv7393_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
-{
-
- switch (var->bits_per_pixel) {
- case 16:/* DIRECTCOLOUR, 64k */
- var->red.offset = info->var.red.offset;
- var->green.offset = info->var.green.offset;
- var->blue.offset = info->var.blue.offset;
- var->red.length = info->var.red.length;
- var->green.length = info->var.green.length;
- var->blue.length = info->var.blue.length;
- var->transp.offset = 0;
- var->transp.length = 0;
- var->transp.msb_right = 0;
- var->red.msb_right = 0;
- var->green.msb_right = 0;
- var->blue.msb_right = 0;
- break;
- default:
- pr_debug("%s: depth not supported: %u BPP\n", __func__,
- var->bits_per_pixel);
- return -EINVAL;
- }
-
- if (info->var.xres != var->xres ||
- info->var.yres != var->yres ||
- info->var.xres_virtual != var->xres_virtual ||
- info->var.yres_virtual != var->yres_virtual) {
- pr_debug("%s: Resolution not supported: X%u x Y%u\n",
- __func__, var->xres, var->yres);
- return -EINVAL;
- }
-
- /*
- * Memory limit
- */
-
- if ((info->fix.line_length * var->yres_virtual) > info->fix.smem_len) {
- pr_debug("%s: Memory Limit requested yres_virtual = %u\n",
- __func__, var->yres_virtual);
- return -ENOMEM;
- }
-
- return 0;
-}
-
-static int
-bfin_adv7393_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
-{
- int dy;
- u32 dmaaddr;
- struct adv7393fb_device *fbdev = to_adv7393fb_device(info);
-
- if (!var || !info)
- return -EINVAL;
-
- if (var->xoffset - info->var.xoffset) {
- /* No support for X panning for now! */
- return -EINVAL;
- }
- dy = var->yoffset - info->var.yoffset;
-
- if (dy) {
- pr_debug("%s: Panning screen of %d lines\n", __func__, dy);
-
- dmaaddr = fbdev->av1->start_addr;
- dmaaddr += (info->fix.line_length * dy);
- /* TODO: Wait for current frame to finished */
-
- fbdev->av1->start_addr = (unsigned long)dmaaddr;
- fbdev->av2->start_addr = (unsigned long)dmaaddr + fbdev->line_len;
- }
-
- return 0;
-
-}
-
-/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
-static int bfin_adv7393_fb_blank(int blank, struct fb_info *info)
-{
- struct adv7393fb_device *fbdev = to_adv7393fb_device(info);
-
- switch (blank) {
-
- case VESA_NO_BLANKING:
- /* Turn on panel */
- adv7393_mode(fbdev->client, BLANK_OFF);
- break;
-
- case VESA_VSYNC_SUSPEND:
- case VESA_HSYNC_SUSPEND:
- case VESA_POWERDOWN:
- /* Turn off panel */
- adv7393_mode(fbdev->client, BLANK_ON);
- break;
-
- default:
- return -EINVAL;
- break;
- }
- return 0;
-}
-
-int bfin_adv7393_fb_cursor(struct fb_info *info, struct fb_cursor *cursor)
-{
- if (nocursor)
- return 0;
- else
- return -EINVAL; /* just to force soft_cursor() call */
-}
-
-static int bfin_adv7393_fb_setcolreg(u_int regno, u_int red, u_int green,
- u_int blue, u_int transp,
- struct fb_info *info)
-{
- if (regno >= BFIN_LCD_NBR_PALETTE_ENTRIES)
- return -EINVAL;
-
- if (info->var.grayscale)
- /* grayscale = 0.30*R + 0.59*G + 0.11*B */
- red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
-
- if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
- u32 value;
- /* Place color in the pseudopalette */
- if (regno > 16)
- return -EINVAL;
-
- red >>= (16 - info->var.red.length);
- green >>= (16 - info->var.green.length);
- blue >>= (16 - info->var.blue.length);
-
- value = (red << info->var.red.offset) |
- (green << info->var.green.offset)|
- (blue << info->var.blue.offset);
- value &= 0xFFFF;
-
- ((u32 *) (info->pseudo_palette))[regno] = value;
- }
-
- return 0;
-}
-
-static int bfin_adv7393_fb_remove(struct i2c_client *client)
-{
- struct adv7393fb_device *fbdev = i2c_get_clientdata(client);
-
- adv7393_mode(client, POWER_DOWN);
-
- if (fbdev->fb_mem)
- dma_free_coherent(NULL, fbdev->fb_len, fbdev->fb_mem, fbdev->dma_handle);
- free_dma(CH_PPI);
- free_irq(IRQ_PPI_ERROR, fbdev);
- unregister_framebuffer(&fbdev->info);
- remove_proc_entry("driver/adv7393", NULL);
- fb_dealloc_cmap(&fbdev->info.cmap);
- kfree(fbdev->info.pseudo_palette);
-
- if (ANOMALY_05000400)
- gpio_free(P_IDENT(P_PPI0_FS3)); /* FS3 */
- peripheral_free_list(ppi_pins);
- kfree(fbdev);
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int bfin_adv7393_fb_suspend(struct device *dev)
-{
- struct adv7393fb_device *fbdev = dev_get_drvdata(dev);
-
- if (fbdev->open) {
- bfin_disable_dma();
- bfin_disable_ppi();
- dma_desc_list(fbdev, DESTRUCT);
- }
- adv7393_mode(fbdev->client, POWER_DOWN);
-
- return 0;
-}
-
-static int bfin_adv7393_fb_resume(struct device *dev)
-{
- struct adv7393fb_device *fbdev = dev_get_drvdata(dev);
-
- adv7393_mode(fbdev->client, POWER_ON);
-
- if (fbdev->open) {
- dma_desc_list(fbdev, BUILD);
- bfin_config_ppi(fbdev);
- bfin_config_dma(fbdev);
- bfin_enable_ppi();
- }
-
- return 0;
-}
-
-static const struct dev_pm_ops bfin_adv7393_dev_pm_ops = {
- .suspend = bfin_adv7393_fb_suspend,
- .resume = bfin_adv7393_fb_resume,
-};
-#endif
-
-static const struct i2c_device_id bfin_adv7393_id[] = {
- {DRIVER_NAME, 0},
- {}
-};
-
-MODULE_DEVICE_TABLE(i2c, bfin_adv7393_id);
-
-static struct i2c_driver bfin_adv7393_fb_driver = {
- .driver = {
- .name = DRIVER_NAME,
-#ifdef CONFIG_PM
- .pm = &bfin_adv7393_dev_pm_ops,
-#endif
- },
- .probe = bfin_adv7393_fb_probe,
- .remove = bfin_adv7393_fb_remove,
- .id_table = bfin_adv7393_id,
-};
-
-static int __init bfin_adv7393_fb_driver_init(void)
-{
-#if IS_ENABLED(CONFIG_I2C_BLACKFIN_TWI)
- request_module("i2c-bfin-twi");
-#else
- request_module("i2c-gpio");
-#endif
-
- return i2c_add_driver(&bfin_adv7393_fb_driver);
-}
-module_init(bfin_adv7393_fb_driver_init);
-
-static void __exit bfin_adv7393_fb_driver_cleanup(void)
-{
- i2c_del_driver(&bfin_adv7393_fb_driver);
-}
-module_exit(bfin_adv7393_fb_driver_cleanup);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
-MODULE_DESCRIPTION("Frame buffer driver for ADV7393/2 Video Encoder");
-
-module_param(mode, int, 0);
-MODULE_PARM_DESC(mode,
- "Video Mode (0=NTSC,1=PAL,2=NTSC 640x480,3=PAL 640x480,4=NTSC YCbCr input,5=PAL YCbCr input)");
-
-module_param(mem, int, 0);
-MODULE_PARM_DESC(mem,
- "Size of frame buffer memory 1=Single 2=Double Size (allows y-panning / frame stacking)");
-
-module_param(nocursor, int, 0644);
-MODULE_PARM_DESC(nocursor, "cursor enable/disable");
diff --git a/drivers/video/fbdev/bfin_adv7393fb.h b/drivers/video/fbdev/bfin_adv7393fb.h
deleted file mode 100644
index afd0380e19e1..000000000000
--- a/drivers/video/fbdev/bfin_adv7393fb.h
+++ /dev/null
@@ -1,319 +0,0 @@
-/*
- * Frame buffer driver for ADV7393/2 video encoder
- *
- * Copyright 2006-2009 Analog Devices Inc.
- * Licensed under the GPL-2 or late.
- */
-
-#ifndef __BFIN_ADV7393FB_H__
-#define __BFIN_ADV7393FB_H__
-
-#define BFIN_LCD_NBR_PALETTE_ENTRIES 256
-
-#ifdef CONFIG_NTSC
-# define VMODE 0
-#endif
-#ifdef CONFIG_PAL
-# define VMODE 1
-#endif
-#ifdef CONFIG_NTSC_640x480
-# define VMODE 2
-#endif
-#ifdef CONFIG_PAL_640x480
-# define VMODE 3
-#endif
-#ifdef CONFIG_NTSC_YCBCR
-# define VMODE 4
-#endif
-#ifdef CONFIG_PAL_YCBCR
-# define VMODE 5
-#endif
-
-#ifndef VMODE
-# define VMODE 1
-#endif
-
-#ifdef CONFIG_ADV7393_2XMEM
-# define VMEM 2
-#else
-# define VMEM 1
-#endif
-
-#if defined(CONFIG_BF537) || defined(CONFIG_BF536) || defined(CONFIG_BF534)
-# define DMA_CFG_VAL 0x7935 /* Set Sync Bit */
-# define VB_DUMMY_MEMORY_SOURCE L1_DATA_B_START
-#else
-# define DMA_CFG_VAL 0x7915
-# define VB_DUMMY_MEMORY_SOURCE BOOT_ROM_START
-#endif
-
-enum {
- DESTRUCT,
- BUILD,
-};
-
-enum {
- POWER_ON,
- POWER_DOWN,
- BLANK_ON,
- BLANK_OFF,
-};
-
-struct adv7393fb_modes {
- const s8 name[25]; /* Full name */
- u16 xres; /* Active Horizonzal Pixels */
- u16 yres; /* Active Vertical Pixels */
- u16 bpp;
- u16 vmode;
- u16 a_lines; /* Active Lines per Field */
- u16 vb1_lines; /* Vertical Blanking Field 1 Lines */
- u16 vb2_lines; /* Vertical Blanking Field 2 Lines */
- u16 tot_lines; /* Total Lines per Frame */
- u16 boeft_blank; /* Before Odd/Even Field Transition No. of Blank Pixels */
- u16 aoeft_blank; /* After Odd/Even Field Transition No. of Blank Pixels */
- const s8 *adv7393_i2c_initd;
- u16 adv7393_i2c_initd_len;
-};
-
-static const u8 init_NTSC_TESTPATTERN[] = {
- 0x00, 0x1E, /* Power up all DACs and PLL */
- 0x01, 0x00, /* SD-Only Mode */
- 0x80, 0x10, /* SSAF Luma Filter Enabled, NTSC Mode */
- 0x82, 0xCB, /* Step control on, pixel data valid, pedestal on, PrPb SSAF on, CVBS/YC output */
- 0x84, 0x40, /* SD Color Bar Test Pattern Enabled, DAC 2 = Luma, DAC 3 = Chroma */
-};
-
-static const u8 init_NTSC[] = {
- 0x00, 0x1E, /* Power up all DACs and PLL */
- 0xC3, 0x26, /* Program RGB->YCrCb Color Space conversion matrix */
- 0xC5, 0x12, /* Program RGB->YCrCb Color Space conversion matrix */
- 0xC2, 0x4A, /* Program RGB->YCrCb Color Space conversion matrix */
- 0xC6, 0x5E, /* Program RGB->YCrCb Color Space conversion matrix */
- 0xBD, 0x19, /* Program RGB->YCrCb Color Space conversion matrix */
- 0xBF, 0x42, /* Program RGB->YCrCb Color Space conversion matrix */
- 0x8C, 0x1F, /* NTSC Subcarrier Frequency */
- 0x8D, 0x7C, /* NTSC Subcarrier Frequency */
- 0x8E, 0xF0, /* NTSC Subcarrier Frequency */
- 0x8F, 0x21, /* NTSC Subcarrier Frequency */
- 0x01, 0x00, /* SD-Only Mode */
- 0x80, 0x30, /* SSAF Luma Filter Enabled, NTSC Mode */
- 0x82, 0x8B, /* Step control on, pixel data invalid, pedestal on, PrPb SSAF on, CVBS/YC output */
- 0x87, 0x80, /* SD Color Bar Test Pattern Enabled, DAC 2 = Luma, DAC 3 = Chroma */
- 0x86, 0x82,
- 0x8B, 0x11,
- 0x88, 0x20,
- 0x8A, 0x0d,
-};
-
-static const u8 init_PAL[] = {
- 0x00, 0x1E, /* Power up all DACs and PLL */
- 0xC3, 0x26, /* Program RGB->YCrCb Color Space conversion matrix */
- 0xC5, 0x12, /* Program RGB->YCrCb Color Space conversion matrix */
- 0xC2, 0x4A, /* Program RGB->YCrCb Color Space conversion matrix */
- 0xC6, 0x5E, /* Program RGB->YCrCb Color Space conversion matrix */
- 0xBD, 0x19, /* Program RGB->YCrCb Color Space conversion matrix */
- 0xBF, 0x42, /* Program RGB->YCrCb Color Space conversion matrix */
- 0x8C, 0xCB, /* PAL Subcarrier Frequency */
- 0x8D, 0x8A, /* PAL Subcarrier Frequency */
- 0x8E, 0x09, /* PAL Subcarrier Frequency */
- 0x8F, 0x2A, /* PAL Subcarrier Frequency */
- 0x01, 0x00, /* SD-Only Mode */
- 0x80, 0x11, /* SSAF Luma Filter Enabled, PAL Mode */
- 0x82, 0x8B, /* Step control on, pixel data invalid, pedestal on, PrPb SSAF on, CVBS/YC output */
- 0x87, 0x80, /* SD Color Bar Test Pattern Enabled, DAC 2 = Luma, DAC 3 = Chroma */
- 0x86, 0x82,
- 0x8B, 0x11,
- 0x88, 0x20,
- 0x8A, 0x0d,
-};
-
-static const u8 init_NTSC_YCbCr[] = {
- 0x00, 0x1E, /* Power up all DACs and PLL */
- 0x8C, 0x1F, /* NTSC Subcarrier Frequency */
- 0x8D, 0x7C, /* NTSC Subcarrier Frequency */
- 0x8E, 0xF0, /* NTSC Subcarrier Frequency */
- 0x8F, 0x21, /* NTSC Subcarrier Frequency */
- 0x01, 0x00, /* SD-Only Mode */
- 0x80, 0x30, /* SSAF Luma Filter Enabled, NTSC Mode */
- 0x82, 0x8B, /* Step control on, pixel data invalid, pedestal on, PrPb SSAF on, CVBS/YC output */
- 0x87, 0x00, /* DAC 2 = Luma, DAC 3 = Chroma */
- 0x86, 0x82,
- 0x8B, 0x11,
- 0x88, 0x08,
- 0x8A, 0x0d,
-};
-
-static const u8 init_PAL_YCbCr[] = {
- 0x00, 0x1E, /* Power up all DACs and PLL */
- 0x8C, 0xCB, /* PAL Subcarrier Frequency */
- 0x8D, 0x8A, /* PAL Subcarrier Frequency */
- 0x8E, 0x09, /* PAL Subcarrier Frequency */
- 0x8F, 0x2A, /* PAL Subcarrier Frequency */
- 0x01, 0x00, /* SD-Only Mode */
- 0x80, 0x11, /* SSAF Luma Filter Enabled, PAL Mode */
- 0x82, 0x8B, /* Step control on, pixel data invalid, pedestal on, PrPb SSAF on, CVBS/YC output */
- 0x87, 0x00, /* DAC 2 = Luma, DAC 3 = Chroma */
- 0x86, 0x82,
- 0x8B, 0x11,
- 0x88, 0x08,
- 0x8A, 0x0d,
-};
-
-static struct adv7393fb_modes known_modes[] = {
- /* NTSC 720x480 CRT */
- {
- .name = "NTSC 720x480",
- .xres = 720,
- .yres = 480,
- .bpp = 16,
- .vmode = FB_VMODE_INTERLACED,
- .a_lines = 240,
- .vb1_lines = 22,
- .vb2_lines = 23,
- .tot_lines = 525,
- .boeft_blank = 16,
- .aoeft_blank = 122,
- .adv7393_i2c_initd = init_NTSC,
- .adv7393_i2c_initd_len = sizeof(init_NTSC)
- },
- /* PAL 720x480 CRT */
- {
- .name = "PAL 720x576",
- .xres = 720,
- .yres = 576,
- .bpp = 16,
- .vmode = FB_VMODE_INTERLACED,
- .a_lines = 288,
- .vb1_lines = 24,
- .vb2_lines = 25,
- .tot_lines = 625,
- .boeft_blank = 12,
- .aoeft_blank = 132,
- .adv7393_i2c_initd = init_PAL,
- .adv7393_i2c_initd_len = sizeof(init_PAL)
- },
- /* NTSC 640x480 CRT Experimental */
- {
- .name = "NTSC 640x480",
- .xres = 640,
- .yres = 480,
- .bpp = 16,
- .vmode = FB_VMODE_INTERLACED,
- .a_lines = 240,
- .vb1_lines = 22,
- .vb2_lines = 23,
- .tot_lines = 525,
- .boeft_blank = 16 + 40,
- .aoeft_blank = 122 + 40,
- .adv7393_i2c_initd = init_NTSC,
- .adv7393_i2c_initd_len = sizeof(init_NTSC)
- },
- /* PAL 640x480 CRT Experimental */
- {
- .name = "PAL 640x480",
- .xres = 640,
- .yres = 480,
- .bpp = 16,
- .vmode = FB_VMODE_INTERLACED,
- .a_lines = 288 - 20,
- .vb1_lines = 24 + 20,
- .vb2_lines = 25 + 20,
- .tot_lines = 625,
- .boeft_blank = 12 + 40,
- .aoeft_blank = 132 + 40,
- .adv7393_i2c_initd = init_PAL,
- .adv7393_i2c_initd_len = sizeof(init_PAL)
- },
- /* NTSC 720x480 YCbCR */
- {
- .name = "NTSC 720x480 YCbCR",
- .xres = 720,
- .yres = 480,
- .bpp = 16,
- .vmode = FB_VMODE_INTERLACED,
- .a_lines = 240,
- .vb1_lines = 22,
- .vb2_lines = 23,
- .tot_lines = 525,
- .boeft_blank = 16,
- .aoeft_blank = 122,
- .adv7393_i2c_initd = init_NTSC_YCbCr,
- .adv7393_i2c_initd_len = sizeof(init_NTSC_YCbCr)
- },
- /* PAL 720x480 CRT */
- {
- .name = "PAL 720x576 YCbCR",
- .xres = 720,
- .yres = 576,
- .bpp = 16,
- .vmode = FB_VMODE_INTERLACED,
- .a_lines = 288,
- .vb1_lines = 24,
- .vb2_lines = 25,
- .tot_lines = 625,
- .boeft_blank = 12,
- .aoeft_blank = 132,
- .adv7393_i2c_initd = init_PAL_YCbCr,
- .adv7393_i2c_initd_len = sizeof(init_PAL_YCbCr)
- }
-};
-
-struct adv7393fb_regs {
-
-};
-
-struct adv7393fb_device {
- struct fb_info info; /* FB driver info record */
-
- struct i2c_client *client;
-
- struct dmasg *descriptor_list_head;
- struct dmasg *vb1;
- struct dmasg *av1;
- struct dmasg *vb2;
- struct dmasg *av2;
-
- dma_addr_t dma_handle;
-
- struct fb_info bfin_adv7393_fb;
-
- struct adv7393fb_modes *modes;
-
- struct adv7393fb_regs *regs; /* Registers memory map */
- size_t regs_len;
- size_t fb_len;
- size_t line_len;
- u16 open;
- u16 *fb_mem; /* RGB Buffer */
-
-};
-
-#define to_adv7393fb_device(_info) \
- (_info ? container_of(_info, struct adv7393fb_device, info) : NULL);
-
-static int bfin_adv7393_fb_open(struct fb_info *info, int user);
-static int bfin_adv7393_fb_release(struct fb_info *info, int user);
-static int bfin_adv7393_fb_check_var(struct fb_var_screeninfo *var,
- struct fb_info *info);
-
-static int bfin_adv7393_fb_pan_display(struct fb_var_screeninfo *var,
- struct fb_info *info);
-
-static int bfin_adv7393_fb_blank(int blank, struct fb_info *info);
-
-static void bfin_config_ppi(struct adv7393fb_device *fbdev);
-static int bfin_config_dma(struct adv7393fb_device *fbdev);
-static void bfin_disable_dma(void);
-static void bfin_enable_ppi(void);
-static void bfin_disable_ppi(void);
-
-static inline int adv7393_write(struct i2c_client *client, u8 reg, u8 value);
-static inline int adv7393_read(struct i2c_client *client, u8 reg);
-static int adv7393_write_block(struct i2c_client *client, const u8 *data,
- unsigned int len);
-
-int bfin_adv7393_fb_cursor(struct fb_info *info, struct fb_cursor *cursor);
-static int bfin_adv7393_fb_setcolreg(u_int, u_int, u_int, u_int,
- u_int, struct fb_info *info);
-
-#endif
diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c
index 5baf7bc054e1..3e330e0f56ed 100644
--- a/drivers/video/fbdev/core/fbcon.c
+++ b/drivers/video/fbdev/core/fbcon.c
@@ -2595,7 +2595,8 @@ static int fbcon_copy_font(struct vc_data *vc, int con)
* is ever implemented.
*/
-static int fbcon_set_font(struct vc_data *vc, struct console_font *font, unsigned flags)
+static int fbcon_set_font(struct vc_data *vc, struct console_font *font,
+ unsigned int flags)
{
struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
unsigned charcount = font->charcount;
diff --git a/drivers/video/fbdev/fsl-diu-fb.c b/drivers/video/fbdev/fsl-diu-fb.c
index 25abbcf38913..1bfd13cbd4e3 100644
--- a/drivers/video/fbdev/fsl-diu-fb.c
+++ b/drivers/video/fbdev/fsl-diu-fb.c
@@ -1960,12 +1960,8 @@ static int __init fsl_diu_init(void)
of_node_put(np);
coherence_data = vmalloc(coherence_data_size);
- if (!coherence_data) {
- pr_err("fsl-diu-fb: could not allocate coherence data "
- "(size=%zu)\n", coherence_data_size);
+ if (!coherence_data)
return -ENOMEM;
- }
-
#endif
ret = platform_driver_register(&fsl_diu_driver);
diff --git a/drivers/video/fbdev/matrox/matroxfb_crtc2.c b/drivers/video/fbdev/matrox/matroxfb_crtc2.c
index 02796a4317a9..f64e1d55d7a1 100644
--- a/drivers/video/fbdev/matrox/matroxfb_crtc2.c
+++ b/drivers/video/fbdev/matrox/matroxfb_crtc2.c
@@ -696,10 +696,9 @@ static void* matroxfb_crtc2_probe(struct matrox_fb_info* minfo) {
if (!minfo->devflags.crtc2)
return NULL;
m2info = kzalloc(sizeof(*m2info), GFP_KERNEL);
- if (!m2info) {
- printk(KERN_ERR "matroxfb_crtc2: Not enough memory for CRTC2 control structs\n");
+ if (!m2info)
return NULL;
- }
+
m2info->primary_dev = minfo;
if (matroxfb_dh_registerfb(m2info)) {
kfree(m2info);
diff --git a/drivers/video/fbdev/offb.c b/drivers/video/fbdev/offb.c
index 90d38de34479..77c0a2f45b3b 100644
--- a/drivers/video/fbdev/offb.c
+++ b/drivers/video/fbdev/offb.c
@@ -280,6 +280,7 @@ static void offb_destroy(struct fb_info *info)
if (info->screen_base)
iounmap(info->screen_base);
release_mem_region(info->apertures->ranges[0].base, info->apertures->ranges[0].size);
+ fb_dealloc_cmap(&info->cmap);
framebuffer_release(info);
}
@@ -518,6 +519,7 @@ static void __init offb_init_fb(const char *name,
return;
out_err:
+ fb_dealloc_cmap(&info->cmap);
iounmap(info->screen_base);
out_aper:
iounmap(par->cmap_adr);
diff --git a/drivers/video/fbdev/s1d13xxxfb.c b/drivers/video/fbdev/s1d13xxxfb.c
index 5d6179ef0298..e04efb567b5c 100644
--- a/drivers/video/fbdev/s1d13xxxfb.c
+++ b/drivers/video/fbdev/s1d13xxxfb.c
@@ -96,18 +96,12 @@ static const struct fb_fix_screeninfo s1d13xxxfb_fix = {
static inline u8
s1d13xxxfb_readreg(struct s1d13xxxfb_par *par, u16 regno)
{
-#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_OPSPUT) || defined(CONFIG_PLAT_MAPPI3)
- regno=((regno & 1) ? (regno & ~1L) : (regno + 1));
-#endif
return readb(par->regs + regno);
}
static inline void
s1d13xxxfb_writereg(struct s1d13xxxfb_par *par, u16 regno, u8 value)
{
-#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_OPSPUT) || defined(CONFIG_PLAT_MAPPI3)
- regno=((regno & 1) ? (regno & ~1L) : (regno + 1));
-#endif
writeb(value, par->regs + regno);
}
@@ -296,11 +290,7 @@ s1d13xxxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
dbg("s1d13xxxfb_setcolreg: pseudo %d, val %08x\n",
regno, pseudo_val);
-#if defined(CONFIG_PLAT_MAPPI)
- ((u32 *)info->pseudo_palette)[regno] = cpu_to_le16(pseudo_val);
-#else
((u32 *)info->pseudo_palette)[regno] = pseudo_val;
-#endif
break;
case FB_VISUAL_PSEUDOCOLOR:
diff --git a/drivers/video/fbdev/s3c-fb.c b/drivers/video/fbdev/s3c-fb.c
index 5f4f696c2ecf..9ec85ccd0ce9 100644
--- a/drivers/video/fbdev/s3c-fb.c
+++ b/drivers/video/fbdev/s3c-fb.c
@@ -1383,11 +1383,9 @@ static int s3c_fb_probe(struct platform_device *pdev)
return -EINVAL;
}
- sfb = devm_kzalloc(dev, sizeof(struct s3c_fb), GFP_KERNEL);
- if (!sfb) {
- dev_err(dev, "no memory for framebuffers\n");
+ sfb = devm_kzalloc(dev, sizeof(*sfb), GFP_KERNEL);
+ if (!sfb)
return -ENOMEM;
- }
dev_dbg(dev, "allocate new framebuffer %p\n", sfb);
@@ -1716,63 +1714,6 @@ static struct s3c_fb_win_variant s3c_fb_data_64xx_wins[] = {
},
};
-static struct s3c_fb_win_variant s3c_fb_data_s5p_wins[] = {
- [0] = {
- .has_osd_c = 1,
- .osd_size_off = 0x8,
- .palette_sz = 256,
- .valid_bpp = (VALID_BPP1248 | VALID_BPP(13) |
- VALID_BPP(15) | VALID_BPP(16) |
- VALID_BPP(18) | VALID_BPP(19) |
- VALID_BPP(24) | VALID_BPP(25) |
- VALID_BPP(32)),
- },
- [1] = {
- .has_osd_c = 1,
- .has_osd_d = 1,
- .osd_size_off = 0xc,
- .has_osd_alpha = 1,
- .palette_sz = 256,
- .valid_bpp = (VALID_BPP1248 | VALID_BPP(13) |
- VALID_BPP(15) | VALID_BPP(16) |
- VALID_BPP(18) | VALID_BPP(19) |
- VALID_BPP(24) | VALID_BPP(25) |
- VALID_BPP(32)),
- },
- [2] = {
- .has_osd_c = 1,
- .has_osd_d = 1,
- .osd_size_off = 0xc,
- .has_osd_alpha = 1,
- .palette_sz = 256,
- .valid_bpp = (VALID_BPP1248 | VALID_BPP(13) |
- VALID_BPP(15) | VALID_BPP(16) |
- VALID_BPP(18) | VALID_BPP(19) |
- VALID_BPP(24) | VALID_BPP(25) |
- VALID_BPP(32)),
- },
- [3] = {
- .has_osd_c = 1,
- .has_osd_alpha = 1,
- .palette_sz = 256,
- .valid_bpp = (VALID_BPP1248 | VALID_BPP(13) |
- VALID_BPP(15) | VALID_BPP(16) |
- VALID_BPP(18) | VALID_BPP(19) |
- VALID_BPP(24) | VALID_BPP(25) |
- VALID_BPP(32)),
- },
- [4] = {
- .has_osd_c = 1,
- .has_osd_alpha = 1,
- .palette_sz = 256,
- .valid_bpp = (VALID_BPP1248 | VALID_BPP(13) |
- VALID_BPP(15) | VALID_BPP(16) |
- VALID_BPP(18) | VALID_BPP(19) |
- VALID_BPP(24) | VALID_BPP(25) |
- VALID_BPP(32)),
- },
-};
-
static struct s3c_fb_driverdata s3c_fb_data_64xx = {
.variant = {
.nr_windows = 5,
@@ -1804,102 +1745,6 @@ static struct s3c_fb_driverdata s3c_fb_data_64xx = {
.win[4] = &s3c_fb_data_64xx_wins[4],
};
-static struct s3c_fb_driverdata s3c_fb_data_s5pv210 = {
- .variant = {
- .nr_windows = 5,
- .vidtcon = VIDTCON0,
- .wincon = WINCON(0),
- .winmap = WINxMAP(0),
- .keycon = WKEYCON,
- .osd = VIDOSD_BASE,
- .osd_stride = 16,
- .buf_start = VIDW_BUF_START(0),
- .buf_size = VIDW_BUF_SIZE(0),
- .buf_end = VIDW_BUF_END(0),
-
- .palette = {
- [0] = 0x2400,
- [1] = 0x2800,
- [2] = 0x2c00,
- [3] = 0x3000,
- [4] = 0x3400,
- },
-
- .has_shadowcon = 1,
- .has_blendcon = 1,
- .has_clksel = 1,
- .has_fixvclk = 1,
- },
- .win[0] = &s3c_fb_data_s5p_wins[0],
- .win[1] = &s3c_fb_data_s5p_wins[1],
- .win[2] = &s3c_fb_data_s5p_wins[2],
- .win[3] = &s3c_fb_data_s5p_wins[3],
- .win[4] = &s3c_fb_data_s5p_wins[4],
-};
-
-static struct s3c_fb_driverdata s3c_fb_data_exynos4 = {
- .variant = {
- .nr_windows = 5,
- .vidtcon = VIDTCON0,
- .wincon = WINCON(0),
- .winmap = WINxMAP(0),
- .keycon = WKEYCON,
- .osd = VIDOSD_BASE,
- .osd_stride = 16,
- .buf_start = VIDW_BUF_START(0),
- .buf_size = VIDW_BUF_SIZE(0),
- .buf_end = VIDW_BUF_END(0),
-
- .palette = {
- [0] = 0x2400,
- [1] = 0x2800,
- [2] = 0x2c00,
- [3] = 0x3000,
- [4] = 0x3400,
- },
-
- .has_shadowcon = 1,
- .has_blendcon = 1,
- .has_fixvclk = 1,
- },
- .win[0] = &s3c_fb_data_s5p_wins[0],
- .win[1] = &s3c_fb_data_s5p_wins[1],
- .win[2] = &s3c_fb_data_s5p_wins[2],
- .win[3] = &s3c_fb_data_s5p_wins[3],
- .win[4] = &s3c_fb_data_s5p_wins[4],
-};
-
-static struct s3c_fb_driverdata s3c_fb_data_exynos5 = {
- .variant = {
- .nr_windows = 5,
- .vidtcon = FIMD_V8_VIDTCON0,
- .wincon = WINCON(0),
- .winmap = WINxMAP(0),
- .keycon = WKEYCON,
- .osd = VIDOSD_BASE,
- .osd_stride = 16,
- .buf_start = VIDW_BUF_START(0),
- .buf_size = VIDW_BUF_SIZE(0),
- .buf_end = VIDW_BUF_END(0),
-
- .palette = {
- [0] = 0x2400,
- [1] = 0x2800,
- [2] = 0x2c00,
- [3] = 0x3000,
- [4] = 0x3400,
- },
- .has_shadowcon = 1,
- .has_blendcon = 1,
- .has_fixvclk = 1,
- },
- .win[0] = &s3c_fb_data_s5p_wins[0],
- .win[1] = &s3c_fb_data_s5p_wins[1],
- .win[2] = &s3c_fb_data_s5p_wins[2],
- .win[3] = &s3c_fb_data_s5p_wins[3],
- .win[4] = &s3c_fb_data_s5p_wins[4],
-};
-
/* S3C2443/S3C2416 style hardware */
static struct s3c_fb_driverdata s3c_fb_data_s3c2443 = {
.variant = {
@@ -1942,15 +1787,6 @@ static const struct platform_device_id s3c_fb_driver_ids[] = {
.name = "s3c-fb",
.driver_data = (unsigned long)&s3c_fb_data_64xx,
}, {
- .name = "s5pv210-fb",
- .driver_data = (unsigned long)&s3c_fb_data_s5pv210,
- }, {
- .name = "exynos4-fb",
- .driver_data = (unsigned long)&s3c_fb_data_exynos4,
- }, {
- .name = "exynos5-fb",
- .driver_data = (unsigned long)&s3c_fb_data_exynos5,
- }, {
.name = "s3c2443-fb",
.driver_data = (unsigned long)&s3c_fb_data_s3c2443,
},
diff --git a/drivers/video/fbdev/sis/init.h b/drivers/video/fbdev/sis/init.h
index 85d6738b6c64..400b0e5681b2 100644
--- a/drivers/video/fbdev/sis/init.h
+++ b/drivers/video/fbdev/sis/init.h
@@ -1461,81 +1461,5 @@ static const struct SiS_LVDSCRT1Data SiS_LVDSCRT1640x480_1_H[] =
0x00}}
};
-bool SiSInitPtr(struct SiS_Private *SiS_Pr);
-unsigned short SiS_GetModeID_LCD(int VGAEngine, unsigned int VBFlags, int HDisplay,
- int VDisplay, int Depth, bool FSTN,
- unsigned short CustomT, int LCDwith, int LCDheight,
- unsigned int VBFlags2);
-unsigned short SiS_GetModeID_TV(int VGAEngine, unsigned int VBFlags, int HDisplay,
- int VDisplay, int Depth, unsigned int VBFlags2);
-unsigned short SiS_GetModeID_VGA2(int VGAEngine, unsigned int VBFlags, int HDisplay,
- int VDisplay, int Depth, unsigned int VBFlags2);
-
-void SiS_DisplayOn(struct SiS_Private *SiS_Pr);
-void SiS_DisplayOff(struct SiS_Private *SiS_Pr);
-void SiSRegInit(struct SiS_Private *SiS_Pr, SISIOADDRESS BaseAddr);
-void SiS_SetEnableDstn(struct SiS_Private *SiS_Pr, int enable);
-void SiS_SetEnableFstn(struct SiS_Private *SiS_Pr, int enable);
-unsigned short SiS_GetModeFlag(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
- unsigned short ModeIdIndex);
-bool SiSDetermineROMLayout661(struct SiS_Private *SiS_Pr);
-
-bool SiS_SearchModeID(struct SiS_Private *SiS_Pr, unsigned short *ModeNo,
- unsigned short *ModeIdIndex);
-unsigned short SiS_GetModePtr(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
- unsigned short ModeIdIndex);
-unsigned short SiS_GetRefCRTVCLK(struct SiS_Private *SiS_Pr, unsigned short Index, int UseWide);
-unsigned short SiS_GetRefCRT1CRTC(struct SiS_Private *SiS_Pr, unsigned short Index, int UseWide);
-unsigned short SiS_GetColorDepth(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
- unsigned short ModeIdIndex);
-unsigned short SiS_GetOffset(struct SiS_Private *SiS_Pr,unsigned short ModeNo,
- unsigned short ModeIdIndex, unsigned short RRTI);
-#ifdef CONFIG_FB_SIS_300
-void SiS_GetFIFOThresholdIndex300(struct SiS_Private *SiS_Pr, unsigned short *idx1,
- unsigned short *idx2);
-unsigned short SiS_GetFIFOThresholdB300(unsigned short idx1, unsigned short idx2);
-unsigned short SiS_GetLatencyFactor630(struct SiS_Private *SiS_Pr, unsigned short index);
-#endif
-void SiS_LoadDAC(struct SiS_Private *SiS_Pr, unsigned short ModeNo, unsigned short ModeIdIndex);
-bool SiSSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo);
-void SiS_CalcCRRegisters(struct SiS_Private *SiS_Pr, int depth);
-void SiS_CalcLCDACRT1Timing(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
- unsigned short ModeIdIndex);
-void SiS_Generic_ConvertCRData(struct SiS_Private *SiS_Pr, unsigned char *crdata, int xres,
- int yres, struct fb_var_screeninfo *var, bool writeres);
-
-/* From init301.c: */
-extern void SiS_GetVBInfo(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
- unsigned short ModeIdIndex, int chkcrt2mode);
-extern void SiS_GetLCDResInfo(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
- unsigned short ModeIdIndex);
-extern void SiS_SetYPbPr(struct SiS_Private *SiS_Pr);
-extern void SiS_SetTVMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
- unsigned short ModeIdIndex);
-extern void SiS_UnLockCRT2(struct SiS_Private *SiS_Pr);
-extern void SiS_DisableBridge(struct SiS_Private *);
-extern bool SiS_SetCRT2Group(struct SiS_Private *, unsigned short);
-extern unsigned short SiS_GetRatePtr(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
- unsigned short ModeIdIndex);
-extern void SiS_WaitRetrace1(struct SiS_Private *SiS_Pr);
-extern unsigned short SiS_GetResInfo(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
- unsigned short ModeIdIndex);
-extern unsigned short SiS_GetCH700x(struct SiS_Private *SiS_Pr, unsigned short tempax);
-extern unsigned short SiS_GetVCLK2Ptr(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
- unsigned short ModeIdIndex, unsigned short RRTI);
-extern bool SiS_IsVAMode(struct SiS_Private *);
-extern bool SiS_IsDualEdge(struct SiS_Private *);
-
-#ifdef CONFIG_FB_SIS_300
-extern unsigned int sisfb_read_nbridge_pci_dword(struct SiS_Private *SiS_Pr, int reg);
-extern void sisfb_write_nbridge_pci_dword(struct SiS_Private *SiS_Pr, int reg,
- unsigned int val);
-#endif
-#ifdef CONFIG_FB_SIS_315
-extern void sisfb_write_nbridge_pci_byte(struct SiS_Private *SiS_Pr, int reg,
- unsigned char val);
-extern unsigned int sisfb_read_mio_pci_word(struct SiS_Private *SiS_Pr, int reg);
-#endif
-
#endif
diff --git a/drivers/video/fbdev/sis/init301.c b/drivers/video/fbdev/sis/init301.c
index 02ee752d5000..27a2b72e50e8 100644
--- a/drivers/video/fbdev/sis/init301.c
+++ b/drivers/video/fbdev/sis/init301.c
@@ -82,6 +82,332 @@
#define SiS_I2CDELAY 1000
#define SiS_I2CDELAYSHORT 150
+static const unsigned char SiS_YPbPrTable[3][64] = {
+ {
+ 0x17,0x1d,0x03,0x09,0x05,0x06,0x0c,0x0c,
+ 0x94,0x49,0x01,0x0a,0x06,0x0d,0x04,0x0a,
+ 0x06,0x14,0x0d,0x04,0x0a,0x00,0x85,0x1b,
+ 0x0c,0x50,0x00,0x97,0x00,0xda,0x4a,0x17,
+ 0x7d,0x05,0x4b,0x00,0x00,0xe2,0x00,0x02,
+ 0x03,0x0a,0x65,0x9d /*0x8d*/,0x08,0x92,0x8f,0x40,
+ 0x60,0x80,0x14,0x90,0x8c,0x60,0x14,0x53 /*0x50*/,
+ 0x00,0x40,0x44,0x00,0xdb,0x02,0x3b,0x00
+ },
+ {
+ 0x33,0x06,0x06,0x09,0x0b,0x0c,0x0c,0x0c,
+ 0x98,0x0a,0x01,0x0d,0x06,0x0d,0x04,0x0a,
+ 0x06,0x14,0x0d,0x04,0x0a,0x00,0x85,0x3f,
+ 0x0c,0x50,0xb2,0x9f,0x16,0x59,0x4f,0x13,
+ 0xad,0x11,0xad,0x1d,0x40,0x8a,0x3d,0xb8,
+ 0x51,0x5e,0x60,0x49,0x7d,0x92,0x0f,0x40,
+ 0x60,0x80,0x14,0x90,0x8c,0x60,0x14,0x4e,
+ 0x43,0x41,0x11,0x00,0xfc,0xff,0x32,0x00
+ },
+ {
+#if 0 /* OK, but sticks to left edge */
+ 0x13,0x1d,0xe8,0x09,0x09,0xed,0x0c,0x0c,
+ 0x98,0x0a,0x01,0x0c,0x06,0x0d,0x04,0x0a,
+ 0x06,0x14,0x0d,0x04,0x0a,0x00,0x85,0x3f,
+ 0xed,0x50,0x70,0x9f,0x16,0x59,0x21 /*0x2b*/,0x13,
+ 0x27,0x0b,0x27,0xfc,0x30,0x27,0x1c,0xb0,
+ 0x4b,0x4b,0x65 /*0x6f*/,0x2f,0x63,0x92,0x0f,0x40,
+ 0x60,0x80,0x14,0x90,0x8c,0x60,0x14,0x27,
+ 0x00,0x40,0x11,0x00,0xfc,0xff,0x32,0x00
+#endif
+#if 1 /* Perfect */
+ 0x23,0x2d,0xe8,0x09,0x09,0xed,0x0c,0x0c,
+ 0x98,0x0a,0x01,0x0c,0x06,0x0d,0x04,0x0a,
+ 0x06,0x14,0x0d,0x04,0x0a,0x00,0x85,0x3f,
+ 0xed,0x50,0x70,0x9f,0x16,0x59,0x60,0x13,
+ 0x27,0x0b,0x27,0xfc,0x30,0x27,0x1c,0xb0,
+ 0x4b,0x4b,0x6f,0x2f,0x63,0x92,0x0f,0x40,
+ 0x60,0x80,0x14,0x90,0x8c,0x60,0x14,0x73,
+ 0x00,0x40,0x11,0x00,0xfc,0xff,0x32,0x00
+#endif
+ }
+};
+
+static const unsigned char SiS_TVPhase[] =
+{
+ 0x21,0xED,0xBA,0x08, /* 0x00 SiS_NTSCPhase */
+ 0x2A,0x05,0xE3,0x00, /* 0x01 SiS_PALPhase */
+ 0x21,0xE4,0x2E,0x9B, /* 0x02 SiS_PALMPhase */
+ 0x21,0xF4,0x3E,0xBA, /* 0x03 SiS_PALNPhase */
+ 0x1E,0x8B,0xA2,0xA7,
+ 0x1E,0x83,0x0A,0xE0, /* 0x05 SiS_SpecialPhaseM */
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x21,0xF0,0x7B,0xD6, /* 0x08 SiS_NTSCPhase2 */
+ 0x2A,0x09,0x86,0xE9, /* 0x09 SiS_PALPhase2 */
+ 0x21,0xE6,0xEF,0xA4, /* 0x0a SiS_PALMPhase2 */
+ 0x21,0xF6,0x94,0x46, /* 0x0b SiS_PALNPhase2 */
+ 0x1E,0x8B,0xA2,0xA7,
+ 0x1E,0x83,0x0A,0xE0, /* 0x0d SiS_SpecialPhaseM */
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x1e,0x8c,0x5c,0x7a, /* 0x10 SiS_SpecialPhase */
+ 0x25,0xd4,0xfd,0x5e /* 0x11 SiS_SpecialPhaseJ */
+};
+
+static const unsigned char SiS_HiTVGroup3_1[] = {
+ 0x00, 0x14, 0x15, 0x25, 0x55, 0x15, 0x0b, 0x13,
+ 0xb1, 0x41, 0x62, 0x62, 0xff, 0xf4, 0x45, 0xa6,
+ 0x25, 0x2f, 0x67, 0xf6, 0xbf, 0xff, 0x8e, 0x20,
+ 0xac, 0xda, 0x60, 0xfe, 0x6a, 0x9a, 0x06, 0x10,
+ 0xd1, 0x04, 0x18, 0x0a, 0xff, 0x80, 0x00, 0x80,
+ 0x3b, 0x77, 0x00, 0xef, 0xe0, 0x10, 0xb0, 0xe0,
+ 0x10, 0x4f, 0x0f, 0x0f, 0x05, 0x0f, 0x08, 0x6e,
+ 0x1a, 0x1f, 0x25, 0x2a, 0x4c, 0xaa, 0x01
+};
+
+static const unsigned char SiS_HiTVGroup3_2[] = {
+ 0x00, 0x14, 0x15, 0x25, 0x55, 0x15, 0x0b, 0x7a,
+ 0x54, 0x41, 0xe7, 0xe7, 0xff, 0xf4, 0x45, 0xa6,
+ 0x25, 0x2f, 0x67, 0xf6, 0xbf, 0xff, 0x8e, 0x20,
+ 0xac, 0x6a, 0x60, 0x2b, 0x52, 0xcd, 0x61, 0x10,
+ 0x51, 0x04, 0x18, 0x0a, 0x1f, 0x80, 0x00, 0x80,
+ 0xff, 0xa4, 0x04, 0x2b, 0x94, 0x21, 0x72, 0x94,
+ 0x26, 0x05, 0x01, 0x0f, 0xed, 0x0f, 0x0a, 0x64,
+ 0x18, 0x1d, 0x23, 0x28, 0x4c, 0xaa, 0x01
+};
+
+/* 301C / 302ELV extended Part2 TV registers (4 tap scaler) */
+
+static const unsigned char SiS_Part2CLVX_1[] = {
+ 0x00,0x00,
+ 0x00,0x20,0x00,0x00,0x7F,0x20,0x02,0x7F,0x7D,0x20,0x04,0x7F,0x7D,0x1F,0x06,0x7E,
+ 0x7C,0x1D,0x09,0x7E,0x7C,0x1B,0x0B,0x7E,0x7C,0x19,0x0E,0x7D,0x7C,0x17,0x11,0x7C,
+ 0x7C,0x14,0x14,0x7C,0x7C,0x11,0x17,0x7C,0x7D,0x0E,0x19,0x7C,0x7E,0x0B,0x1B,0x7C,
+ 0x7E,0x09,0x1D,0x7C,0x7F,0x06,0x1F,0x7C,0x7F,0x04,0x20,0x7D,0x00,0x02,0x20,0x7E
+};
+
+static const unsigned char SiS_Part2CLVX_2[] = {
+ 0x00,0x00,
+ 0x00,0x20,0x00,0x00,0x7F,0x20,0x02,0x7F,0x7D,0x20,0x04,0x7F,0x7D,0x1F,0x06,0x7E,
+ 0x7C,0x1D,0x09,0x7E,0x7C,0x1B,0x0B,0x7E,0x7C,0x19,0x0E,0x7D,0x7C,0x17,0x11,0x7C,
+ 0x7C,0x14,0x14,0x7C,0x7C,0x11,0x17,0x7C,0x7D,0x0E,0x19,0x7C,0x7E,0x0B,0x1B,0x7C,
+ 0x7E,0x09,0x1D,0x7C,0x7F,0x06,0x1F,0x7C,0x7F,0x04,0x20,0x7D,0x00,0x02,0x20,0x7E
+};
+
+static const unsigned char SiS_Part2CLVX_3[] = { /* NTSC, 525i, 525p */
+ 0xE0,0x01,
+ 0x04,0x1A,0x04,0x7E,0x03,0x1A,0x06,0x7D,0x01,0x1A,0x08,0x7D,0x00,0x19,0x0A,0x7D,
+ 0x7F,0x19,0x0C,0x7C,0x7E,0x18,0x0E,0x7C,0x7E,0x17,0x10,0x7B,0x7D,0x15,0x12,0x7C,
+ 0x7D,0x13,0x13,0x7D,0x7C,0x12,0x15,0x7D,0x7C,0x10,0x17,0x7D,0x7C,0x0E,0x18,0x7E,
+ 0x7D,0x0C,0x19,0x7E,0x7D,0x0A,0x19,0x00,0x7D,0x08,0x1A,0x01,0x7E,0x06,0x1A,0x02,
+ 0x58,0x02,
+ 0x07,0x14,0x07,0x7E,0x06,0x14,0x09,0x7D,0x05,0x14,0x0A,0x7D,0x04,0x13,0x0B,0x7E,
+ 0x03,0x13,0x0C,0x7E,0x02,0x12,0x0D,0x7F,0x01,0x12,0x0E,0x7F,0x01,0x11,0x0F,0x7F,
+ 0x00,0x10,0x10,0x00,0x7F,0x0F,0x11,0x01,0x7F,0x0E,0x12,0x01,0x7E,0x0D,0x12,0x03,
+ 0x7E,0x0C,0x13,0x03,0x7E,0x0B,0x13,0x04,0x7E,0x0A,0x14,0x04,0x7D,0x09,0x14,0x06,
+ 0x00,0x03,
+ 0x09,0x0F,0x09,0x7F,0x08,0x0F,0x09,0x00,0x07,0x0F,0x0A,0x00,0x06,0x0F,0x0A,0x01,
+ 0x06,0x0E,0x0B,0x01,0x05,0x0E,0x0B,0x02,0x04,0x0E,0x0C,0x02,0x04,0x0D,0x0C,0x03,
+ 0x03,0x0D,0x0D,0x03,0x02,0x0C,0x0D,0x05,0x02,0x0C,0x0E,0x04,0x01,0x0B,0x0E,0x06,
+ 0x01,0x0B,0x0E,0x06,0x00,0x0A,0x0F,0x07,0x00,0x0A,0x0F,0x07,0x00,0x09,0x0F,0x08,
+ 0xFF,0xFF
+};
+
+static const unsigned char SiS_Part2CLVX_4[] = { /* PAL */
+ 0x58,0x02,
+ 0x05,0x19,0x05,0x7D,0x03,0x19,0x06,0x7E,0x02,0x19,0x08,0x7D,0x01,0x18,0x0A,0x7D,
+ 0x00,0x18,0x0C,0x7C,0x7F,0x17,0x0E,0x7C,0x7E,0x16,0x0F,0x7D,0x7E,0x14,0x11,0x7D,
+ 0x7D,0x13,0x13,0x7D,0x7D,0x11,0x14,0x7E,0x7D,0x0F,0x16,0x7E,0x7D,0x0E,0x17,0x7E,
+ 0x7D,0x0C,0x18,0x7F,0x7D,0x0A,0x18,0x01,0x7D,0x08,0x19,0x02,0x7D,0x06,0x19,0x04,
+ 0x00,0x03,
+ 0x08,0x12,0x08,0x7E,0x07,0x12,0x09,0x7E,0x06,0x12,0x0A,0x7E,0x05,0x11,0x0B,0x7F,
+ 0x04,0x11,0x0C,0x7F,0x03,0x11,0x0C,0x00,0x03,0x10,0x0D,0x00,0x02,0x0F,0x0E,0x01,
+ 0x01,0x0F,0x0F,0x01,0x01,0x0E,0x0F,0x02,0x00,0x0D,0x10,0x03,0x7F,0x0C,0x11,0x04,
+ 0x7F,0x0C,0x11,0x04,0x7F,0x0B,0x11,0x05,0x7E,0x0A,0x12,0x06,0x7E,0x09,0x12,0x07,
+ 0x40,0x02,
+ 0x04,0x1A,0x04,0x7E,0x02,0x1B,0x05,0x7E,0x01,0x1A,0x07,0x7E,0x00,0x1A,0x09,0x7D,
+ 0x7F,0x19,0x0B,0x7D,0x7E,0x18,0x0D,0x7D,0x7D,0x17,0x10,0x7C,0x7D,0x15,0x12,0x7C,
+ 0x7C,0x14,0x14,0x7C,0x7C,0x12,0x15,0x7D,0x7C,0x10,0x17,0x7D,0x7C,0x0D,0x18,0x7F,
+ 0x7D,0x0B,0x19,0x7F,0x7D,0x09,0x1A,0x00,0x7D,0x07,0x1A,0x02,0x7E,0x05,0x1B,0x02,
+ 0xFF,0xFF
+};
+
+static const unsigned char SiS_Part2CLVX_5[] = { /* 750p */
+ 0x00,0x03,
+ 0x05,0x19,0x05,0x7D,0x03,0x19,0x06,0x7E,0x02,0x19,0x08,0x7D,0x01,0x18,0x0A,0x7D,
+ 0x00,0x18,0x0C,0x7C,0x7F,0x17,0x0E,0x7C,0x7E,0x16,0x0F,0x7D,0x7E,0x14,0x11,0x7D,
+ 0x7D,0x13,0x13,0x7D,0x7D,0x11,0x14,0x7E,0x7D,0x0F,0x16,0x7E,0x7D,0x0E,0x17,0x7E,
+ 0x7D,0x0C,0x18,0x7F,0x7D,0x0A,0x18,0x01,0x7D,0x08,0x19,0x02,0x7D,0x06,0x19,0x04,
+ 0xFF,0xFF
+};
+
+static const unsigned char SiS_Part2CLVX_6[] = { /* 1080i */
+ 0x00,0x04,
+ 0x04,0x1A,0x04,0x7E,0x02,0x1B,0x05,0x7E,0x01,0x1A,0x07,0x7E,0x00,0x1A,0x09,0x7D,
+ 0x7F,0x19,0x0B,0x7D,0x7E,0x18,0x0D,0x7D,0x7D,0x17,0x10,0x7C,0x7D,0x15,0x12,0x7C,
+ 0x7C,0x14,0x14,0x7C,0x7C,0x12,0x15,0x7D,0x7C,0x10,0x17,0x7D,0x7C,0x0D,0x18,0x7F,
+ 0x7D,0x0B,0x19,0x7F,0x7D,0x09,0x1A,0x00,0x7D,0x07,0x1A,0x02,0x7E,0x05,0x1B,0x02,
+ 0xFF,0xFF,
+};
+
+#ifdef CONFIG_FB_SIS_315
+/* 661 et al LCD data structure (2.03.00) */
+static const unsigned char SiS_LCDStruct661[] = {
+ /* 1024x768 */
+/* type|CR37| HDE | VDE | HT | VT | hss | hse */
+ 0x02,0xC0,0x00,0x04,0x00,0x03,0x40,0x05,0x26,0x03,0x10,0x00,0x88,
+ 0x00,0x02,0x00,0x06,0x00,0x41,0x5A,0x64,0x00,0x00,0x00,0x00,0x04,
+ /* | vss | vse |clck| clock |CRT2DataP|CRT2DataP|idx */
+ /* VESA non-VESA noscale */
+ /* 1280x1024 */
+ 0x03,0xC0,0x00,0x05,0x00,0x04,0x98,0x06,0x2A,0x04,0x30,0x00,0x70,
+ 0x00,0x01,0x00,0x03,0x00,0x6C,0xF8,0x2F,0x00,0x00,0x00,0x00,0x08,
+ /* 1400x1050 */
+ 0x09,0x20,0x78,0x05,0x1A,0x04,0x98,0x06,0x2A,0x04,0x18,0x00,0x38,
+ 0x00,0x01,0x00,0x03,0x00,0x6C,0xF8,0x2F,0x00,0x00,0x00,0x00,0x09,
+ /* 1600x1200 */
+ 0x0B,0xE0,0x40,0x06,0xB0,0x04,0x70,0x08,0xE2,0x04,0x40,0x00,0xC0,
+ 0x00,0x01,0x00,0x03,0x00,0xA2,0x70,0x24,0x00,0x00,0x00,0x00,0x0A,
+ /* 1280x768 (_2) */
+ 0x0A,0xE0,0x00,0x05,0x00,0x03,0x7C,0x06,0x26,0x03,0x30,0x00,0x70,
+ 0x00,0x03,0x00,0x06,0x00,0x4D,0xC8,0x48,0x00,0x00,0x00,0x00,0x06,
+ /* 1280x720 */
+ 0x0E,0xE0,0x00,0x05,0xD0,0x02,0x80,0x05,0x26,0x03,0x10,0x00,0x20,
+ 0x00,0x01,0x00,0x06,0x00,0x45,0x9C,0x62,0x00,0x00,0x00,0x00,0x05,
+ /* 1280x800 (_2) */
+ 0x0C,0xE0,0x00,0x05,0x20,0x03,0x10,0x06,0x2C,0x03,0x30,0x00,0x70,
+ 0x00,0x04,0x00,0x03,0x00,0x49,0xCE,0x1E,0x00,0x00,0x00,0x00,0x09,
+ /* 1680x1050 */
+ 0x0D,0xE0,0x90,0x06,0x1A,0x04,0x6C,0x07,0x2A,0x04,0x1A,0x00,0x4C,
+ 0x00,0x03,0x00,0x06,0x00,0x79,0xBE,0x44,0x00,0x00,0x00,0x00,0x06,
+ /* 1280x800_3 */
+ 0x0C,0xE0,0x00,0x05,0x20,0x03,0xAA,0x05,0x2E,0x03,0x30,0x00,0x50,
+ 0x00,0x04,0x00,0x03,0x00,0x47,0xA9,0x10,0x00,0x00,0x00,0x00,0x07,
+ /* 800x600 */
+ 0x01,0xC0,0x20,0x03,0x58,0x02,0x20,0x04,0x74,0x02,0x2A,0x00,0x80,
+ 0x00,0x06,0x00,0x04,0x00,0x28,0x63,0x4B,0x00,0x00,0x00,0x00,0x00,
+ /* 1280x854 */
+ 0x08,0xE0,0x00,0x05,0x56,0x03,0x80,0x06,0x5d,0x03,0x10,0x00,0x70,
+ 0x00,0x01,0x00,0x03,0x00,0x54,0x75,0x13,0x00,0x00,0x00,0x00,0x08
+};
+#endif
+
+#ifdef CONFIG_FB_SIS_300
+static unsigned char SiS300_TrumpionData[14][80] = {
+ { 0x02,0x0A,0x0A,0x01,0x04,0x01,0x00,0x03,0x0D,0x00,0x0D,0x10,0x7F,0x00,0x80,0x02,
+ 0x20,0x03,0x0B,0x00,0x90,0x01,0xC1,0x01,0x60,0x0C,0x30,0x10,0x00,0x00,0x04,0x23,
+ 0x00,0x00,0x03,0x28,0x03,0x10,0x05,0x08,0x40,0x10,0x00,0x10,0x04,0x23,0x00,0x23,
+ 0x03,0x11,0x60,0xBC,0x01,0xFF,0x03,0xFF,0x19,0x01,0x00,0x05,0x09,0x04,0x04,0x05,
+ 0x04,0x0C,0x09,0x05,0x02,0xB0,0x00,0x00,0x02,0xBA,0xF0,0x5A,0x01,0xBE,0x01,0x00 },
+ { 0x02,0x0A,0x0A,0x01,0x04,0x01,0x00,0x03,0x0D,0x00,0x0D,0x10,0x27,0x00,0x80,0x02,
+ 0x20,0x03,0x07,0x00,0x5E,0x01,0x0D,0x02,0x60,0x0C,0x30,0x11,0x00,0x00,0x04,0x23,
+ 0x00,0x00,0x03,0x80,0x03,0x28,0x06,0x08,0x40,0x11,0x00,0x11,0x04,0x23,0x00,0x23,
+ 0x03,0x11,0x60,0x90,0x01,0xFF,0x0F,0xF4,0x19,0x01,0x00,0x05,0x01,0x00,0x04,0x05,
+ 0x04,0x0C,0x02,0x01,0x02,0xB0,0x00,0x00,0x02,0xBA,0xEC,0x57,0x01,0xBE,0x01,0x00 },
+ { 0x02,0x0A,0x0A,0x01,0x04,0x01,0x00,0x03,0x0D,0x00,0x0D,0x10,0x8A,0x00,0xD8,0x02,
+ 0x84,0x03,0x16,0x00,0x90,0x01,0xC1,0x01,0x60,0x0C,0x30,0x1C,0x00,0x20,0x04,0x23,
+ 0x00,0x01,0x03,0x53,0x03,0x28,0x06,0x08,0x40,0x1C,0x00,0x16,0x04,0x23,0x00,0x23,
+ 0x03,0x11,0x60,0xD9,0x01,0xFF,0x0F,0xF4,0x18,0x07,0x05,0x05,0x13,0x04,0x04,0x05,
+ 0x01,0x0B,0x13,0x0A,0x02,0xB0,0x00,0x00,0x02,0xBA,0xF0,0x59,0x01,0xBE,0x01,0x00 },
+ { 0x02,0x0A,0x0A,0x01,0x04,0x01,0x00,0x03,0x0D,0x00,0x0D,0x10,0x72,0x00,0xD8,0x02,
+ 0x84,0x03,0x16,0x00,0x90,0x01,0xC1,0x01,0x60,0x0C,0x30,0x1C,0x00,0x20,0x04,0x23,
+ 0x00,0x01,0x03,0x53,0x03,0x28,0x06,0x08,0x40,0x1C,0x00,0x16,0x04,0x23,0x00,0x23,
+ 0x03,0x11,0x60,0xDA,0x01,0xFF,0x0F,0xF4,0x18,0x07,0x05,0x05,0x13,0x04,0x04,0x05,
+ 0x01,0x0B,0x13,0x0A,0x02,0xB0,0x00,0x00,0x02,0xBA,0xF0,0x55,0x01,0xBE,0x01,0x00 },
+ { 0x02,0x0A,0x02,0x00,0x04,0x01,0x00,0x03,0x0D,0x00,0x0D,0x10,0x7F,0x00,0x80,0x02,
+ 0x20,0x03,0x16,0x00,0xE0,0x01,0x0D,0x02,0x60,0x0C,0x30,0x98,0x00,0x00,0x04,0x23,
+ 0x00,0x01,0x03,0x45,0x03,0x48,0x06,0x08,0x40,0x98,0x00,0x98,0x04,0x23,0x00,0x23,
+ 0x03,0x11,0x60,0xF4,0x01,0xFF,0x0F,0xF4,0x18,0x01,0x00,0x05,0x01,0x00,0x05,0x05,
+ 0x04,0x0C,0x08,0x05,0x02,0xB0,0x00,0x00,0x02,0xBA,0xF0,0x5B,0x01,0xBE,0x01,0x00 },
+ { 0x02,0x0A,0x02,0x01,0x04,0x01,0x00,0x03,0x0D,0x00,0x0D,0x10,0xBF,0x00,0x20,0x03,
+ 0x20,0x04,0x0D,0x00,0x58,0x02,0x71,0x02,0x80,0x0C,0x30,0x9A,0x00,0xFA,0x03,0x1D,
+ 0x00,0x01,0x03,0x22,0x03,0x28,0x06,0x08,0x40,0x98,0x00,0x98,0x04,0x1D,0x00,0x1D,
+ 0x03,0x11,0x60,0x39,0x03,0x40,0x05,0xF4,0x18,0x07,0x02,0x06,0x04,0x01,0x06,0x0B,
+ 0x02,0x0A,0x20,0x19,0x02,0xB0,0x00,0x00,0x02,0xBA,0xF0,0x5B,0x01,0xBE,0x01,0x00 },
+ { 0x02,0x0A,0x0A,0x01,0x04,0x01,0x00,0x03,0x0D,0x00,0x0D,0x10,0xEF,0x00,0x00,0x04,
+ 0x40,0x05,0x13,0x00,0x00,0x03,0x26,0x03,0x88,0x0C,0x30,0x90,0x00,0x00,0x04,0x23,
+ 0x00,0x01,0x03,0x24,0x03,0x28,0x06,0x08,0x40,0x90,0x00,0x90,0x04,0x23,0x00,0x23,
+ 0x03,0x11,0x60,0x40,0x05,0xFF,0x0F,0xF4,0x18,0x01,0x00,0x08,0x01,0x00,0x08,0x01,
+ 0x00,0x08,0x01,0x01,0x02,0xB0,0x00,0x00,0x02,0xBA,0xF0,0x5B,0x01,0xBE,0x01,0x00 },
+ /* variant 2 */
+ { 0x02,0x0A,0x0A,0x01,0x04,0x01,0x00,0x03,0x11,0x00,0x0D,0x10,0x7F,0x00,0x80,0x02,
+ 0x20,0x03,0x15,0x00,0x90,0x01,0xC1,0x01,0x60,0x0C,0x30,0x18,0x00,0x00,0x04,0x23,
+ 0x00,0x01,0x03,0x44,0x03,0x28,0x06,0x08,0x40,0x18,0x00,0x18,0x04,0x23,0x00,0x23,
+ 0x03,0x11,0x60,0xA6,0x01,0xFF,0x03,0xFF,0x19,0x01,0x00,0x05,0x13,0x04,0x04,0x05,
+ 0x04,0x0C,0x13,0x0A,0x02,0xB0,0x00,0x00,0x02,0xBA,0xF0,0x55,0x01,0xBE,0x01,0x00 },
+ { 0x02,0x0A,0x0A,0x01,0x04,0x01,0x00,0x03,0x11,0x00,0x0D,0x10,0x7F,0x00,0x80,0x02,
+ 0x20,0x03,0x15,0x00,0x90,0x01,0xC1,0x01,0x60,0x0C,0x30,0x18,0x00,0x00,0x04,0x23,
+ 0x00,0x01,0x03,0x44,0x03,0x28,0x06,0x08,0x40,0x18,0x00,0x18,0x04,0x23,0x00,0x23,
+ 0x03,0x11,0x60,0xA6,0x01,0xFF,0x03,0xFF,0x19,0x01,0x00,0x05,0x13,0x04,0x04,0x05,
+ 0x04,0x0C,0x13,0x0A,0x02,0xB0,0x00,0x00,0x02,0xBA,0xF0,0x55,0x01,0xBE,0x01,0x00 },
+ { 0x02,0x0A,0x0A,0x01,0x04,0x01,0x00,0x03,0x11,0x00,0x0D,0x10,0x8A,0x00,0xD8,0x02,
+ 0x84,0x03,0x16,0x00,0x90,0x01,0xC1,0x01,0x60,0x0C,0x30,0x1C,0x00,0x20,0x04,0x23,
+ 0x00,0x01,0x03,0x53,0x03,0x28,0x06,0x08,0x40,0x1C,0x00,0x16,0x04,0x23,0x00,0x23,
+ 0x03,0x11,0x60,0xDA,0x01,0xFF,0x0F,0xF4,0x18,0x07,0x05,0x05,0x13,0x04,0x04,0x05,
+ 0x01,0x0B,0x13,0x0A,0x02,0xB0,0x00,0x00,0x02,0xBA,0xF0,0x55,0x01,0xBE,0x01,0x00 },
+ { 0x02,0x0A,0x0A,0x01,0x04,0x01,0x00,0x03,0x11,0x00,0x0D,0x10,0x72,0x00,0xD8,0x02,
+ 0x84,0x03,0x16,0x00,0x90,0x01,0xC1,0x01,0x60,0x0C,0x30,0x1C,0x00,0x20,0x04,0x23,
+ 0x00,0x01,0x03,0x53,0x03,0x28,0x06,0x08,0x40,0x1C,0x00,0x16,0x04,0x23,0x00,0x23,
+ 0x03,0x11,0x60,0xDA,0x01,0xFF,0x0F,0xF4,0x18,0x07,0x05,0x05,0x13,0x04,0x04,0x05,
+ 0x01,0x0B,0x13,0x0A,0x02,0xB0,0x00,0x00,0x02,0xBA,0xF0,0x55,0x01,0xBE,0x01,0x00 },
+ { 0x02,0x0A,0x02,0x00,0x04,0x01,0x00,0x03,0x11,0x00,0x0D,0x10,0x7F,0x00,0x80,0x02,
+ 0x20,0x03,0x16,0x00,0xE0,0x01,0x0D,0x02,0x60,0x0C,0x30,0x98,0x00,0x00,0x04,0x23,
+ 0x00,0x01,0x03,0x45,0x03,0x48,0x06,0x08,0x40,0x98,0x00,0x98,0x04,0x23,0x00,0x23,
+ 0x03,0x11,0x60,0xF4,0x01,0xFF,0x0F,0xF4,0x18,0x01,0x00,0x05,0x01,0x00,0x05,0x05,
+ 0x04,0x0C,0x08,0x05,0x02,0xB0,0x00,0x00,0x02,0xBA,0xEA,0x58,0x01,0xBE,0x01,0x00 },
+ { 0x02,0x0A,0x02,0x01,0x04,0x01,0x00,0x03,0x11,0x00,0x0D,0x10,0xBF,0x00,0x20,0x03,
+ 0x20,0x04,0x0D,0x00,0x58,0x02,0x71,0x02,0x80,0x0C,0x30,0x9A,0x00,0xFA,0x03,0x1D,
+ 0x00,0x01,0x03,0x22,0x03,0x28,0x06,0x08,0x40,0x98,0x00,0x98,0x04,0x1D,0x00,0x1D,
+ 0x03,0x11,0x60,0x39,0x03,0x40,0x05,0xF4,0x18,0x07,0x02,0x06,0x04,0x01,0x06,0x0B,
+ 0x02,0x0A,0x20,0x19,0x02,0xB0,0x00,0x00,0x02,0xBA,0xEA,0x58,0x01,0xBE,0x01,0x00 },
+ { 0x02,0x0A,0x0A,0x01,0x04,0x01,0x00,0x03,0x11,0x00,0x0D,0x10,0xEF,0x00,0x00,0x04,
+ 0x40,0x05,0x13,0x00,0x00,0x03,0x26,0x03,0x88,0x0C,0x30,0x90,0x00,0x00,0x04,0x23,
+ 0x00,0x01,0x03,0x24,0x03,0x28,0x06,0x08,0x40,0x90,0x00,0x90,0x04,0x23,0x00,0x23,
+ 0x03,0x11,0x60,0x40,0x05,0xFF,0x0F,0xF4,0x18,0x01,0x00,0x08,0x01,0x00,0x08,0x01,
+ 0x00,0x08,0x01,0x01,0x02,0xB0,0x00,0x00,0x02,0xBA,0xEA,0x58,0x01,0xBE,0x01,0x00 }
+};
+#endif
+
+#ifdef CONFIG_FB_SIS_315
+static void SiS_Chrontel701xOn(struct SiS_Private *SiS_Pr);
+static void SiS_Chrontel701xOff(struct SiS_Private *SiS_Pr);
+static void SiS_ChrontelInitTVVSync(struct SiS_Private *SiS_Pr);
+static void SiS_ChrontelDoSomething1(struct SiS_Private *SiS_Pr);
+#endif /* 315 */
+
+#ifdef CONFIG_FB_SIS_300
+static bool SiS_SetTrumpionBlock(struct SiS_Private *SiS_Pr, unsigned char *dataptr);
+#endif
+
+static unsigned short SiS_InitDDCRegs(struct SiS_Private *SiS_Pr, unsigned int VBFlags,
+ int VGAEngine, unsigned short adaptnum, unsigned short DDCdatatype,
+ bool checkcr32, unsigned int VBFlags2);
+static unsigned short SiS_ProbeDDC(struct SiS_Private *SiS_Pr);
+static unsigned short SiS_ReadDDC(struct SiS_Private *SiS_Pr, unsigned short DDCdatatype,
+ unsigned char *buffer);
+static void SiS_SetSwitchDDC2(struct SiS_Private *SiS_Pr);
+static unsigned short SiS_SetStart(struct SiS_Private *SiS_Pr);
+static unsigned short SiS_SetStop(struct SiS_Private *SiS_Pr);
+static unsigned short SiS_SetSCLKLow(struct SiS_Private *SiS_Pr);
+static unsigned short SiS_SetSCLKHigh(struct SiS_Private *SiS_Pr);
+static unsigned short SiS_ReadDDC2Data(struct SiS_Private *SiS_Pr);
+static unsigned short SiS_WriteDDC2Data(struct SiS_Private *SiS_Pr, unsigned short tempax);
+static unsigned short SiS_CheckACK(struct SiS_Private *SiS_Pr);
+static unsigned short SiS_WriteDABDDC(struct SiS_Private *SiS_Pr);
+static unsigned short SiS_PrepareReadDDC(struct SiS_Private *SiS_Pr);
+static unsigned short SiS_PrepareDDC(struct SiS_Private *SiS_Pr);
+static void SiS_SendACK(struct SiS_Private *SiS_Pr, unsigned short yesno);
+static unsigned short SiS_DoProbeDDC(struct SiS_Private *SiS_Pr);
+
+#ifdef CONFIG_FB_SIS_300
+static void SiS_OEM300Setting(struct SiS_Private *SiS_Pr,
+ unsigned short ModeNo, unsigned short ModeIdIndex, unsigned short RefTabindex);
+static void SetOEMLCDData2(struct SiS_Private *SiS_Pr,
+ unsigned short ModeNo, unsigned short ModeIdIndex,unsigned short RefTableIndex);
+#endif
+#ifdef CONFIG_FB_SIS_315
+static void SiS_OEM310Setting(struct SiS_Private *SiS_Pr,
+ unsigned short ModeNo,unsigned short ModeIdIndex, unsigned short RRTI);
+static void SiS_OEM661Setting(struct SiS_Private *SiS_Pr,
+ unsigned short ModeNo,unsigned short ModeIdIndex, unsigned short RRTI);
+static void SiS_FinalizeLCD(struct SiS_Private *, unsigned short, unsigned short);
+#endif
+
static unsigned short SiS_GetBIOSLCDResInfo(struct SiS_Private *SiS_Pr);
static void SiS_SetCH70xx(struct SiS_Private *SiS_Pr, unsigned short reg, unsigned char val);
diff --git a/drivers/video/fbdev/sis/init301.h b/drivers/video/fbdev/sis/init301.h
index 2112d6d7feda..6e5cf14c4ce4 100644
--- a/drivers/video/fbdev/sis/init301.h
+++ b/drivers/video/fbdev/sis/init301.h
@@ -66,287 +66,6 @@
#include "sis.h"
#include <video/sisfb.h>
-static const unsigned char SiS_YPbPrTable[3][64] = {
- {
- 0x17,0x1d,0x03,0x09,0x05,0x06,0x0c,0x0c,
- 0x94,0x49,0x01,0x0a,0x06,0x0d,0x04,0x0a,
- 0x06,0x14,0x0d,0x04,0x0a,0x00,0x85,0x1b,
- 0x0c,0x50,0x00,0x97,0x00,0xda,0x4a,0x17,
- 0x7d,0x05,0x4b,0x00,0x00,0xe2,0x00,0x02,
- 0x03,0x0a,0x65,0x9d /*0x8d*/,0x08,0x92,0x8f,0x40,
- 0x60,0x80,0x14,0x90,0x8c,0x60,0x14,0x53 /*0x50*/,
- 0x00,0x40,0x44,0x00,0xdb,0x02,0x3b,0x00
- },
- {
- 0x33,0x06,0x06,0x09,0x0b,0x0c,0x0c,0x0c,
- 0x98,0x0a,0x01,0x0d,0x06,0x0d,0x04,0x0a,
- 0x06,0x14,0x0d,0x04,0x0a,0x00,0x85,0x3f,
- 0x0c,0x50,0xb2,0x9f,0x16,0x59,0x4f,0x13,
- 0xad,0x11,0xad,0x1d,0x40,0x8a,0x3d,0xb8,
- 0x51,0x5e,0x60,0x49,0x7d,0x92,0x0f,0x40,
- 0x60,0x80,0x14,0x90,0x8c,0x60,0x14,0x4e,
- 0x43,0x41,0x11,0x00,0xfc,0xff,0x32,0x00
- },
- {
-#if 0 /* OK, but sticks to left edge */
- 0x13,0x1d,0xe8,0x09,0x09,0xed,0x0c,0x0c,
- 0x98,0x0a,0x01,0x0c,0x06,0x0d,0x04,0x0a,
- 0x06,0x14,0x0d,0x04,0x0a,0x00,0x85,0x3f,
- 0xed,0x50,0x70,0x9f,0x16,0x59,0x21 /*0x2b*/,0x13,
- 0x27,0x0b,0x27,0xfc,0x30,0x27,0x1c,0xb0,
- 0x4b,0x4b,0x65 /*0x6f*/,0x2f,0x63,0x92,0x0f,0x40,
- 0x60,0x80,0x14,0x90,0x8c,0x60,0x14,0x27,
- 0x00,0x40,0x11,0x00,0xfc,0xff,0x32,0x00
-#endif
-#if 1 /* Perfect */
- 0x23,0x2d,0xe8,0x09,0x09,0xed,0x0c,0x0c,
- 0x98,0x0a,0x01,0x0c,0x06,0x0d,0x04,0x0a,
- 0x06,0x14,0x0d,0x04,0x0a,0x00,0x85,0x3f,
- 0xed,0x50,0x70,0x9f,0x16,0x59,0x60,0x13,
- 0x27,0x0b,0x27,0xfc,0x30,0x27,0x1c,0xb0,
- 0x4b,0x4b,0x6f,0x2f,0x63,0x92,0x0f,0x40,
- 0x60,0x80,0x14,0x90,0x8c,0x60,0x14,0x73,
- 0x00,0x40,0x11,0x00,0xfc,0xff,0x32,0x00
-#endif
- }
-};
-
-static const unsigned char SiS_TVPhase[] =
-{
- 0x21,0xED,0xBA,0x08, /* 0x00 SiS_NTSCPhase */
- 0x2A,0x05,0xE3,0x00, /* 0x01 SiS_PALPhase */
- 0x21,0xE4,0x2E,0x9B, /* 0x02 SiS_PALMPhase */
- 0x21,0xF4,0x3E,0xBA, /* 0x03 SiS_PALNPhase */
- 0x1E,0x8B,0xA2,0xA7,
- 0x1E,0x83,0x0A,0xE0, /* 0x05 SiS_SpecialPhaseM */
- 0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,
- 0x21,0xF0,0x7B,0xD6, /* 0x08 SiS_NTSCPhase2 */
- 0x2A,0x09,0x86,0xE9, /* 0x09 SiS_PALPhase2 */
- 0x21,0xE6,0xEF,0xA4, /* 0x0a SiS_PALMPhase2 */
- 0x21,0xF6,0x94,0x46, /* 0x0b SiS_PALNPhase2 */
- 0x1E,0x8B,0xA2,0xA7,
- 0x1E,0x83,0x0A,0xE0, /* 0x0d SiS_SpecialPhaseM */
- 0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,
- 0x1e,0x8c,0x5c,0x7a, /* 0x10 SiS_SpecialPhase */
- 0x25,0xd4,0xfd,0x5e /* 0x11 SiS_SpecialPhaseJ */
-};
-
-static const unsigned char SiS_HiTVGroup3_1[] = {
- 0x00, 0x14, 0x15, 0x25, 0x55, 0x15, 0x0b, 0x13,
- 0xb1, 0x41, 0x62, 0x62, 0xff, 0xf4, 0x45, 0xa6,
- 0x25, 0x2f, 0x67, 0xf6, 0xbf, 0xff, 0x8e, 0x20,
- 0xac, 0xda, 0x60, 0xfe, 0x6a, 0x9a, 0x06, 0x10,
- 0xd1, 0x04, 0x18, 0x0a, 0xff, 0x80, 0x00, 0x80,
- 0x3b, 0x77, 0x00, 0xef, 0xe0, 0x10, 0xb0, 0xe0,
- 0x10, 0x4f, 0x0f, 0x0f, 0x05, 0x0f, 0x08, 0x6e,
- 0x1a, 0x1f, 0x25, 0x2a, 0x4c, 0xaa, 0x01
-};
-
-static const unsigned char SiS_HiTVGroup3_2[] = {
- 0x00, 0x14, 0x15, 0x25, 0x55, 0x15, 0x0b, 0x7a,
- 0x54, 0x41, 0xe7, 0xe7, 0xff, 0xf4, 0x45, 0xa6,
- 0x25, 0x2f, 0x67, 0xf6, 0xbf, 0xff, 0x8e, 0x20,
- 0xac, 0x6a, 0x60, 0x2b, 0x52, 0xcd, 0x61, 0x10,
- 0x51, 0x04, 0x18, 0x0a, 0x1f, 0x80, 0x00, 0x80,
- 0xff, 0xa4, 0x04, 0x2b, 0x94, 0x21, 0x72, 0x94,
- 0x26, 0x05, 0x01, 0x0f, 0xed, 0x0f, 0x0a, 0x64,
- 0x18, 0x1d, 0x23, 0x28, 0x4c, 0xaa, 0x01
-};
-
-/* 301C / 302ELV extended Part2 TV registers (4 tap scaler) */
-
-static const unsigned char SiS_Part2CLVX_1[] = {
- 0x00,0x00,
- 0x00,0x20,0x00,0x00,0x7F,0x20,0x02,0x7F,0x7D,0x20,0x04,0x7F,0x7D,0x1F,0x06,0x7E,
- 0x7C,0x1D,0x09,0x7E,0x7C,0x1B,0x0B,0x7E,0x7C,0x19,0x0E,0x7D,0x7C,0x17,0x11,0x7C,
- 0x7C,0x14,0x14,0x7C,0x7C,0x11,0x17,0x7C,0x7D,0x0E,0x19,0x7C,0x7E,0x0B,0x1B,0x7C,
- 0x7E,0x09,0x1D,0x7C,0x7F,0x06,0x1F,0x7C,0x7F,0x04,0x20,0x7D,0x00,0x02,0x20,0x7E
-};
-
-static const unsigned char SiS_Part2CLVX_2[] = {
- 0x00,0x00,
- 0x00,0x20,0x00,0x00,0x7F,0x20,0x02,0x7F,0x7D,0x20,0x04,0x7F,0x7D,0x1F,0x06,0x7E,
- 0x7C,0x1D,0x09,0x7E,0x7C,0x1B,0x0B,0x7E,0x7C,0x19,0x0E,0x7D,0x7C,0x17,0x11,0x7C,
- 0x7C,0x14,0x14,0x7C,0x7C,0x11,0x17,0x7C,0x7D,0x0E,0x19,0x7C,0x7E,0x0B,0x1B,0x7C,
- 0x7E,0x09,0x1D,0x7C,0x7F,0x06,0x1F,0x7C,0x7F,0x04,0x20,0x7D,0x00,0x02,0x20,0x7E
-};
-
-static const unsigned char SiS_Part2CLVX_3[] = { /* NTSC, 525i, 525p */
- 0xE0,0x01,
- 0x04,0x1A,0x04,0x7E,0x03,0x1A,0x06,0x7D,0x01,0x1A,0x08,0x7D,0x00,0x19,0x0A,0x7D,
- 0x7F,0x19,0x0C,0x7C,0x7E,0x18,0x0E,0x7C,0x7E,0x17,0x10,0x7B,0x7D,0x15,0x12,0x7C,
- 0x7D,0x13,0x13,0x7D,0x7C,0x12,0x15,0x7D,0x7C,0x10,0x17,0x7D,0x7C,0x0E,0x18,0x7E,
- 0x7D,0x0C,0x19,0x7E,0x7D,0x0A,0x19,0x00,0x7D,0x08,0x1A,0x01,0x7E,0x06,0x1A,0x02,
- 0x58,0x02,
- 0x07,0x14,0x07,0x7E,0x06,0x14,0x09,0x7D,0x05,0x14,0x0A,0x7D,0x04,0x13,0x0B,0x7E,
- 0x03,0x13,0x0C,0x7E,0x02,0x12,0x0D,0x7F,0x01,0x12,0x0E,0x7F,0x01,0x11,0x0F,0x7F,
- 0x00,0x10,0x10,0x00,0x7F,0x0F,0x11,0x01,0x7F,0x0E,0x12,0x01,0x7E,0x0D,0x12,0x03,
- 0x7E,0x0C,0x13,0x03,0x7E,0x0B,0x13,0x04,0x7E,0x0A,0x14,0x04,0x7D,0x09,0x14,0x06,
- 0x00,0x03,
- 0x09,0x0F,0x09,0x7F,0x08,0x0F,0x09,0x00,0x07,0x0F,0x0A,0x00,0x06,0x0F,0x0A,0x01,
- 0x06,0x0E,0x0B,0x01,0x05,0x0E,0x0B,0x02,0x04,0x0E,0x0C,0x02,0x04,0x0D,0x0C,0x03,
- 0x03,0x0D,0x0D,0x03,0x02,0x0C,0x0D,0x05,0x02,0x0C,0x0E,0x04,0x01,0x0B,0x0E,0x06,
- 0x01,0x0B,0x0E,0x06,0x00,0x0A,0x0F,0x07,0x00,0x0A,0x0F,0x07,0x00,0x09,0x0F,0x08,
- 0xFF,0xFF
-};
-
-static const unsigned char SiS_Part2CLVX_4[] = { /* PAL */
- 0x58,0x02,
- 0x05,0x19,0x05,0x7D,0x03,0x19,0x06,0x7E,0x02,0x19,0x08,0x7D,0x01,0x18,0x0A,0x7D,
- 0x00,0x18,0x0C,0x7C,0x7F,0x17,0x0E,0x7C,0x7E,0x16,0x0F,0x7D,0x7E,0x14,0x11,0x7D,
- 0x7D,0x13,0x13,0x7D,0x7D,0x11,0x14,0x7E,0x7D,0x0F,0x16,0x7E,0x7D,0x0E,0x17,0x7E,
- 0x7D,0x0C,0x18,0x7F,0x7D,0x0A,0x18,0x01,0x7D,0x08,0x19,0x02,0x7D,0x06,0x19,0x04,
- 0x00,0x03,
- 0x08,0x12,0x08,0x7E,0x07,0x12,0x09,0x7E,0x06,0x12,0x0A,0x7E,0x05,0x11,0x0B,0x7F,
- 0x04,0x11,0x0C,0x7F,0x03,0x11,0x0C,0x00,0x03,0x10,0x0D,0x00,0x02,0x0F,0x0E,0x01,
- 0x01,0x0F,0x0F,0x01,0x01,0x0E,0x0F,0x02,0x00,0x0D,0x10,0x03,0x7F,0x0C,0x11,0x04,
- 0x7F,0x0C,0x11,0x04,0x7F,0x0B,0x11,0x05,0x7E,0x0A,0x12,0x06,0x7E,0x09,0x12,0x07,
- 0x40,0x02,
- 0x04,0x1A,0x04,0x7E,0x02,0x1B,0x05,0x7E,0x01,0x1A,0x07,0x7E,0x00,0x1A,0x09,0x7D,
- 0x7F,0x19,0x0B,0x7D,0x7E,0x18,0x0D,0x7D,0x7D,0x17,0x10,0x7C,0x7D,0x15,0x12,0x7C,
- 0x7C,0x14,0x14,0x7C,0x7C,0x12,0x15,0x7D,0x7C,0x10,0x17,0x7D,0x7C,0x0D,0x18,0x7F,
- 0x7D,0x0B,0x19,0x7F,0x7D,0x09,0x1A,0x00,0x7D,0x07,0x1A,0x02,0x7E,0x05,0x1B,0x02,
- 0xFF,0xFF
-};
-
-static const unsigned char SiS_Part2CLVX_5[] = { /* 750p */
- 0x00,0x03,
- 0x05,0x19,0x05,0x7D,0x03,0x19,0x06,0x7E,0x02,0x19,0x08,0x7D,0x01,0x18,0x0A,0x7D,
- 0x00,0x18,0x0C,0x7C,0x7F,0x17,0x0E,0x7C,0x7E,0x16,0x0F,0x7D,0x7E,0x14,0x11,0x7D,
- 0x7D,0x13,0x13,0x7D,0x7D,0x11,0x14,0x7E,0x7D,0x0F,0x16,0x7E,0x7D,0x0E,0x17,0x7E,
- 0x7D,0x0C,0x18,0x7F,0x7D,0x0A,0x18,0x01,0x7D,0x08,0x19,0x02,0x7D,0x06,0x19,0x04,
- 0xFF,0xFF
-};
-
-static const unsigned char SiS_Part2CLVX_6[] = { /* 1080i */
- 0x00,0x04,
- 0x04,0x1A,0x04,0x7E,0x02,0x1B,0x05,0x7E,0x01,0x1A,0x07,0x7E,0x00,0x1A,0x09,0x7D,
- 0x7F,0x19,0x0B,0x7D,0x7E,0x18,0x0D,0x7D,0x7D,0x17,0x10,0x7C,0x7D,0x15,0x12,0x7C,
- 0x7C,0x14,0x14,0x7C,0x7C,0x12,0x15,0x7D,0x7C,0x10,0x17,0x7D,0x7C,0x0D,0x18,0x7F,
- 0x7D,0x0B,0x19,0x7F,0x7D,0x09,0x1A,0x00,0x7D,0x07,0x1A,0x02,0x7E,0x05,0x1B,0x02,
- 0xFF,0xFF,
-};
-
-#ifdef CONFIG_FB_SIS_315
-/* 661 et al LCD data structure (2.03.00) */
-static const unsigned char SiS_LCDStruct661[] = {
- /* 1024x768 */
-/* type|CR37| HDE | VDE | HT | VT | hss | hse */
- 0x02,0xC0,0x00,0x04,0x00,0x03,0x40,0x05,0x26,0x03,0x10,0x00,0x88,
- 0x00,0x02,0x00,0x06,0x00,0x41,0x5A,0x64,0x00,0x00,0x00,0x00,0x04,
- /* | vss | vse |clck| clock |CRT2DataP|CRT2DataP|idx */
- /* VESA non-VESA noscale */
- /* 1280x1024 */
- 0x03,0xC0,0x00,0x05,0x00,0x04,0x98,0x06,0x2A,0x04,0x30,0x00,0x70,
- 0x00,0x01,0x00,0x03,0x00,0x6C,0xF8,0x2F,0x00,0x00,0x00,0x00,0x08,
- /* 1400x1050 */
- 0x09,0x20,0x78,0x05,0x1A,0x04,0x98,0x06,0x2A,0x04,0x18,0x00,0x38,
- 0x00,0x01,0x00,0x03,0x00,0x6C,0xF8,0x2F,0x00,0x00,0x00,0x00,0x09,
- /* 1600x1200 */
- 0x0B,0xE0,0x40,0x06,0xB0,0x04,0x70,0x08,0xE2,0x04,0x40,0x00,0xC0,
- 0x00,0x01,0x00,0x03,0x00,0xA2,0x70,0x24,0x00,0x00,0x00,0x00,0x0A,
- /* 1280x768 (_2) */
- 0x0A,0xE0,0x00,0x05,0x00,0x03,0x7C,0x06,0x26,0x03,0x30,0x00,0x70,
- 0x00,0x03,0x00,0x06,0x00,0x4D,0xC8,0x48,0x00,0x00,0x00,0x00,0x06,
- /* 1280x720 */
- 0x0E,0xE0,0x00,0x05,0xD0,0x02,0x80,0x05,0x26,0x03,0x10,0x00,0x20,
- 0x00,0x01,0x00,0x06,0x00,0x45,0x9C,0x62,0x00,0x00,0x00,0x00,0x05,
- /* 1280x800 (_2) */
- 0x0C,0xE0,0x00,0x05,0x20,0x03,0x10,0x06,0x2C,0x03,0x30,0x00,0x70,
- 0x00,0x04,0x00,0x03,0x00,0x49,0xCE,0x1E,0x00,0x00,0x00,0x00,0x09,
- /* 1680x1050 */
- 0x0D,0xE0,0x90,0x06,0x1A,0x04,0x6C,0x07,0x2A,0x04,0x1A,0x00,0x4C,
- 0x00,0x03,0x00,0x06,0x00,0x79,0xBE,0x44,0x00,0x00,0x00,0x00,0x06,
- /* 1280x800_3 */
- 0x0C,0xE0,0x00,0x05,0x20,0x03,0xAA,0x05,0x2E,0x03,0x30,0x00,0x50,
- 0x00,0x04,0x00,0x03,0x00,0x47,0xA9,0x10,0x00,0x00,0x00,0x00,0x07,
- /* 800x600 */
- 0x01,0xC0,0x20,0x03,0x58,0x02,0x20,0x04,0x74,0x02,0x2A,0x00,0x80,
- 0x00,0x06,0x00,0x04,0x00,0x28,0x63,0x4B,0x00,0x00,0x00,0x00,0x00,
- /* 1280x854 */
- 0x08,0xE0,0x00,0x05,0x56,0x03,0x80,0x06,0x5d,0x03,0x10,0x00,0x70,
- 0x00,0x01,0x00,0x03,0x00,0x54,0x75,0x13,0x00,0x00,0x00,0x00,0x08
-};
-#endif
-
-#ifdef CONFIG_FB_SIS_300
-static unsigned char SiS300_TrumpionData[14][80] = {
- { 0x02,0x0A,0x0A,0x01,0x04,0x01,0x00,0x03,0x0D,0x00,0x0D,0x10,0x7F,0x00,0x80,0x02,
- 0x20,0x03,0x0B,0x00,0x90,0x01,0xC1,0x01,0x60,0x0C,0x30,0x10,0x00,0x00,0x04,0x23,
- 0x00,0x00,0x03,0x28,0x03,0x10,0x05,0x08,0x40,0x10,0x00,0x10,0x04,0x23,0x00,0x23,
- 0x03,0x11,0x60,0xBC,0x01,0xFF,0x03,0xFF,0x19,0x01,0x00,0x05,0x09,0x04,0x04,0x05,
- 0x04,0x0C,0x09,0x05,0x02,0xB0,0x00,0x00,0x02,0xBA,0xF0,0x5A,0x01,0xBE,0x01,0x00 },
- { 0x02,0x0A,0x0A,0x01,0x04,0x01,0x00,0x03,0x0D,0x00,0x0D,0x10,0x27,0x00,0x80,0x02,
- 0x20,0x03,0x07,0x00,0x5E,0x01,0x0D,0x02,0x60,0x0C,0x30,0x11,0x00,0x00,0x04,0x23,
- 0x00,0x00,0x03,0x80,0x03,0x28,0x06,0x08,0x40,0x11,0x00,0x11,0x04,0x23,0x00,0x23,
- 0x03,0x11,0x60,0x90,0x01,0xFF,0x0F,0xF4,0x19,0x01,0x00,0x05,0x01,0x00,0x04,0x05,
- 0x04,0x0C,0x02,0x01,0x02,0xB0,0x00,0x00,0x02,0xBA,0xEC,0x57,0x01,0xBE,0x01,0x00 },
- { 0x02,0x0A,0x0A,0x01,0x04,0x01,0x00,0x03,0x0D,0x00,0x0D,0x10,0x8A,0x00,0xD8,0x02,
- 0x84,0x03,0x16,0x00,0x90,0x01,0xC1,0x01,0x60,0x0C,0x30,0x1C,0x00,0x20,0x04,0x23,
- 0x00,0x01,0x03,0x53,0x03,0x28,0x06,0x08,0x40,0x1C,0x00,0x16,0x04,0x23,0x00,0x23,
- 0x03,0x11,0x60,0xD9,0x01,0xFF,0x0F,0xF4,0x18,0x07,0x05,0x05,0x13,0x04,0x04,0x05,
- 0x01,0x0B,0x13,0x0A,0x02,0xB0,0x00,0x00,0x02,0xBA,0xF0,0x59,0x01,0xBE,0x01,0x00 },
- { 0x02,0x0A,0x0A,0x01,0x04,0x01,0x00,0x03,0x0D,0x00,0x0D,0x10,0x72,0x00,0xD8,0x02,
- 0x84,0x03,0x16,0x00,0x90,0x01,0xC1,0x01,0x60,0x0C,0x30,0x1C,0x00,0x20,0x04,0x23,
- 0x00,0x01,0x03,0x53,0x03,0x28,0x06,0x08,0x40,0x1C,0x00,0x16,0x04,0x23,0x00,0x23,
- 0x03,0x11,0x60,0xDA,0x01,0xFF,0x0F,0xF4,0x18,0x07,0x05,0x05,0x13,0x04,0x04,0x05,
- 0x01,0x0B,0x13,0x0A,0x02,0xB0,0x00,0x00,0x02,0xBA,0xF0,0x55,0x01,0xBE,0x01,0x00 },
- { 0x02,0x0A,0x02,0x00,0x04,0x01,0x00,0x03,0x0D,0x00,0x0D,0x10,0x7F,0x00,0x80,0x02,
- 0x20,0x03,0x16,0x00,0xE0,0x01,0x0D,0x02,0x60,0x0C,0x30,0x98,0x00,0x00,0x04,0x23,
- 0x00,0x01,0x03,0x45,0x03,0x48,0x06,0x08,0x40,0x98,0x00,0x98,0x04,0x23,0x00,0x23,
- 0x03,0x11,0x60,0xF4,0x01,0xFF,0x0F,0xF4,0x18,0x01,0x00,0x05,0x01,0x00,0x05,0x05,
- 0x04,0x0C,0x08,0x05,0x02,0xB0,0x00,0x00,0x02,0xBA,0xF0,0x5B,0x01,0xBE,0x01,0x00 },
- { 0x02,0x0A,0x02,0x01,0x04,0x01,0x00,0x03,0x0D,0x00,0x0D,0x10,0xBF,0x00,0x20,0x03,
- 0x20,0x04,0x0D,0x00,0x58,0x02,0x71,0x02,0x80,0x0C,0x30,0x9A,0x00,0xFA,0x03,0x1D,
- 0x00,0x01,0x03,0x22,0x03,0x28,0x06,0x08,0x40,0x98,0x00,0x98,0x04,0x1D,0x00,0x1D,
- 0x03,0x11,0x60,0x39,0x03,0x40,0x05,0xF4,0x18,0x07,0x02,0x06,0x04,0x01,0x06,0x0B,
- 0x02,0x0A,0x20,0x19,0x02,0xB0,0x00,0x00,0x02,0xBA,0xF0,0x5B,0x01,0xBE,0x01,0x00 },
- { 0x02,0x0A,0x0A,0x01,0x04,0x01,0x00,0x03,0x0D,0x00,0x0D,0x10,0xEF,0x00,0x00,0x04,
- 0x40,0x05,0x13,0x00,0x00,0x03,0x26,0x03,0x88,0x0C,0x30,0x90,0x00,0x00,0x04,0x23,
- 0x00,0x01,0x03,0x24,0x03,0x28,0x06,0x08,0x40,0x90,0x00,0x90,0x04,0x23,0x00,0x23,
- 0x03,0x11,0x60,0x40,0x05,0xFF,0x0F,0xF4,0x18,0x01,0x00,0x08,0x01,0x00,0x08,0x01,
- 0x00,0x08,0x01,0x01,0x02,0xB0,0x00,0x00,0x02,0xBA,0xF0,0x5B,0x01,0xBE,0x01,0x00 },
- /* variant 2 */
- { 0x02,0x0A,0x0A,0x01,0x04,0x01,0x00,0x03,0x11,0x00,0x0D,0x10,0x7F,0x00,0x80,0x02,
- 0x20,0x03,0x15,0x00,0x90,0x01,0xC1,0x01,0x60,0x0C,0x30,0x18,0x00,0x00,0x04,0x23,
- 0x00,0x01,0x03,0x44,0x03,0x28,0x06,0x08,0x40,0x18,0x00,0x18,0x04,0x23,0x00,0x23,
- 0x03,0x11,0x60,0xA6,0x01,0xFF,0x03,0xFF,0x19,0x01,0x00,0x05,0x13,0x04,0x04,0x05,
- 0x04,0x0C,0x13,0x0A,0x02,0xB0,0x00,0x00,0x02,0xBA,0xF0,0x55,0x01,0xBE,0x01,0x00 },
- { 0x02,0x0A,0x0A,0x01,0x04,0x01,0x00,0x03,0x11,0x00,0x0D,0x10,0x7F,0x00,0x80,0x02,
- 0x20,0x03,0x15,0x00,0x90,0x01,0xC1,0x01,0x60,0x0C,0x30,0x18,0x00,0x00,0x04,0x23,
- 0x00,0x01,0x03,0x44,0x03,0x28,0x06,0x08,0x40,0x18,0x00,0x18,0x04,0x23,0x00,0x23,
- 0x03,0x11,0x60,0xA6,0x01,0xFF,0x03,0xFF,0x19,0x01,0x00,0x05,0x13,0x04,0x04,0x05,
- 0x04,0x0C,0x13,0x0A,0x02,0xB0,0x00,0x00,0x02,0xBA,0xF0,0x55,0x01,0xBE,0x01,0x00 },
- { 0x02,0x0A,0x0A,0x01,0x04,0x01,0x00,0x03,0x11,0x00,0x0D,0x10,0x8A,0x00,0xD8,0x02,
- 0x84,0x03,0x16,0x00,0x90,0x01,0xC1,0x01,0x60,0x0C,0x30,0x1C,0x00,0x20,0x04,0x23,
- 0x00,0x01,0x03,0x53,0x03,0x28,0x06,0x08,0x40,0x1C,0x00,0x16,0x04,0x23,0x00,0x23,
- 0x03,0x11,0x60,0xDA,0x01,0xFF,0x0F,0xF4,0x18,0x07,0x05,0x05,0x13,0x04,0x04,0x05,
- 0x01,0x0B,0x13,0x0A,0x02,0xB0,0x00,0x00,0x02,0xBA,0xF0,0x55,0x01,0xBE,0x01,0x00 },
- { 0x02,0x0A,0x0A,0x01,0x04,0x01,0x00,0x03,0x11,0x00,0x0D,0x10,0x72,0x00,0xD8,0x02,
- 0x84,0x03,0x16,0x00,0x90,0x01,0xC1,0x01,0x60,0x0C,0x30,0x1C,0x00,0x20,0x04,0x23,
- 0x00,0x01,0x03,0x53,0x03,0x28,0x06,0x08,0x40,0x1C,0x00,0x16,0x04,0x23,0x00,0x23,
- 0x03,0x11,0x60,0xDA,0x01,0xFF,0x0F,0xF4,0x18,0x07,0x05,0x05,0x13,0x04,0x04,0x05,
- 0x01,0x0B,0x13,0x0A,0x02,0xB0,0x00,0x00,0x02,0xBA,0xF0,0x55,0x01,0xBE,0x01,0x00 },
- { 0x02,0x0A,0x02,0x00,0x04,0x01,0x00,0x03,0x11,0x00,0x0D,0x10,0x7F,0x00,0x80,0x02,
- 0x20,0x03,0x16,0x00,0xE0,0x01,0x0D,0x02,0x60,0x0C,0x30,0x98,0x00,0x00,0x04,0x23,
- 0x00,0x01,0x03,0x45,0x03,0x48,0x06,0x08,0x40,0x98,0x00,0x98,0x04,0x23,0x00,0x23,
- 0x03,0x11,0x60,0xF4,0x01,0xFF,0x0F,0xF4,0x18,0x01,0x00,0x05,0x01,0x00,0x05,0x05,
- 0x04,0x0C,0x08,0x05,0x02,0xB0,0x00,0x00,0x02,0xBA,0xEA,0x58,0x01,0xBE,0x01,0x00 },
- { 0x02,0x0A,0x02,0x01,0x04,0x01,0x00,0x03,0x11,0x00,0x0D,0x10,0xBF,0x00,0x20,0x03,
- 0x20,0x04,0x0D,0x00,0x58,0x02,0x71,0x02,0x80,0x0C,0x30,0x9A,0x00,0xFA,0x03,0x1D,
- 0x00,0x01,0x03,0x22,0x03,0x28,0x06,0x08,0x40,0x98,0x00,0x98,0x04,0x1D,0x00,0x1D,
- 0x03,0x11,0x60,0x39,0x03,0x40,0x05,0xF4,0x18,0x07,0x02,0x06,0x04,0x01,0x06,0x0B,
- 0x02,0x0A,0x20,0x19,0x02,0xB0,0x00,0x00,0x02,0xBA,0xEA,0x58,0x01,0xBE,0x01,0x00 },
- { 0x02,0x0A,0x0A,0x01,0x04,0x01,0x00,0x03,0x11,0x00,0x0D,0x10,0xEF,0x00,0x00,0x04,
- 0x40,0x05,0x13,0x00,0x00,0x03,0x26,0x03,0x88,0x0C,0x30,0x90,0x00,0x00,0x04,0x23,
- 0x00,0x01,0x03,0x24,0x03,0x28,0x06,0x08,0x40,0x90,0x00,0x90,0x04,0x23,0x00,0x23,
- 0x03,0x11,0x60,0x40,0x05,0xFF,0x0F,0xF4,0x18,0x01,0x00,0x08,0x01,0x00,0x08,0x01,
- 0x00,0x08,0x01,0x01,0x02,0xB0,0x00,0x00,0x02,0xBA,0xEA,0x58,0x01,0xBE,0x01,0x00 }
-};
-#endif
-
void SiS_UnLockCRT2(struct SiS_Private *SiS_Pr);
void SiS_EnableCRT2(struct SiS_Private *SiS_Pr);
unsigned short SiS_GetRatePtr(struct SiS_Private *SiS_Pr, unsigned short ModeNo, unsigned short ModeIdIndex);
@@ -375,16 +94,11 @@ unsigned short SiS_GetCH701x(struct SiS_Private *SiS_Pr, unsigned short tempax);
void SiS_SetCH70xxANDOR(struct SiS_Private *SiS_Pr, unsigned short reg,
unsigned char orval,unsigned short andval);
#ifdef CONFIG_FB_SIS_315
-static void SiS_Chrontel701xOn(struct SiS_Private *SiS_Pr);
-static void SiS_Chrontel701xOff(struct SiS_Private *SiS_Pr);
-static void SiS_ChrontelInitTVVSync(struct SiS_Private *SiS_Pr);
-static void SiS_ChrontelDoSomething1(struct SiS_Private *SiS_Pr);
void SiS_Chrontel701xBLOn(struct SiS_Private *SiS_Pr);
void SiS_Chrontel701xBLOff(struct SiS_Private *SiS_Pr);
#endif /* 315 */
#ifdef CONFIG_FB_SIS_300
-static bool SiS_SetTrumpionBlock(struct SiS_Private *SiS_Pr, unsigned char *dataptr);
void SiS_SetChrontelGPIO(struct SiS_Private *SiS_Pr, unsigned short myvbinfo);
#endif
@@ -394,40 +108,6 @@ unsigned short SiS_HandleDDC(struct SiS_Private *SiS_Pr, unsigned int VBFlags, i
unsigned short adaptnum, unsigned short DDCdatatype,
unsigned char *buffer, unsigned int VBFlags2);
-static unsigned short SiS_InitDDCRegs(struct SiS_Private *SiS_Pr, unsigned int VBFlags,
- int VGAEngine, unsigned short adaptnum, unsigned short DDCdatatype,
- bool checkcr32, unsigned int VBFlags2);
-static unsigned short SiS_ProbeDDC(struct SiS_Private *SiS_Pr);
-static unsigned short SiS_ReadDDC(struct SiS_Private *SiS_Pr, unsigned short DDCdatatype,
- unsigned char *buffer);
-static void SiS_SetSwitchDDC2(struct SiS_Private *SiS_Pr);
-static unsigned short SiS_SetStart(struct SiS_Private *SiS_Pr);
-static unsigned short SiS_SetStop(struct SiS_Private *SiS_Pr);
-static unsigned short SiS_SetSCLKLow(struct SiS_Private *SiS_Pr);
-static unsigned short SiS_SetSCLKHigh(struct SiS_Private *SiS_Pr);
-static unsigned short SiS_ReadDDC2Data(struct SiS_Private *SiS_Pr);
-static unsigned short SiS_WriteDDC2Data(struct SiS_Private *SiS_Pr, unsigned short tempax);
-static unsigned short SiS_CheckACK(struct SiS_Private *SiS_Pr);
-static unsigned short SiS_WriteDABDDC(struct SiS_Private *SiS_Pr);
-static unsigned short SiS_PrepareReadDDC(struct SiS_Private *SiS_Pr);
-static unsigned short SiS_PrepareDDC(struct SiS_Private *SiS_Pr);
-static void SiS_SendACK(struct SiS_Private *SiS_Pr, unsigned short yesno);
-static unsigned short SiS_DoProbeDDC(struct SiS_Private *SiS_Pr);
-
-#ifdef CONFIG_FB_SIS_300
-static void SiS_OEM300Setting(struct SiS_Private *SiS_Pr,
- unsigned short ModeNo, unsigned short ModeIdIndex, unsigned short RefTabindex);
-static void SetOEMLCDData2(struct SiS_Private *SiS_Pr,
- unsigned short ModeNo, unsigned short ModeIdIndex,unsigned short RefTableIndex);
-#endif
-#ifdef CONFIG_FB_SIS_315
-static void SiS_OEM310Setting(struct SiS_Private *SiS_Pr,
- unsigned short ModeNo,unsigned short ModeIdIndex, unsigned short RRTI);
-static void SiS_OEM661Setting(struct SiS_Private *SiS_Pr,
- unsigned short ModeNo,unsigned short ModeIdIndex, unsigned short RRTI);
-static void SiS_FinalizeLCD(struct SiS_Private *, unsigned short, unsigned short);
-#endif
-
extern void SiS_DisplayOff(struct SiS_Private *SiS_Pr);
extern void SiS_DisplayOn(struct SiS_Private *SiS_Pr);
extern bool SiS_SearchModeID(struct SiS_Private *, unsigned short *, unsigned short *);
diff --git a/drivers/video/fbdev/sis/sis.h b/drivers/video/fbdev/sis/sis.h
index ea1d1c9640bf..d04982b0cd6f 100644
--- a/drivers/video/fbdev/sis/sis.h
+++ b/drivers/video/fbdev/sis/sis.h
@@ -28,6 +28,7 @@
#include "vgatypes.h"
#include "vstruct.h"
+#include "init.h"
#define VER_MAJOR 1
#define VER_MINOR 8
@@ -321,6 +322,85 @@ u8 SiS_GetRegByte(SISIOADDRESS);
u16 SiS_GetRegShort(SISIOADDRESS);
u32 SiS_GetRegLong(SISIOADDRESS);
+/* Chrontel TV, DDC and DPMS functions */
+/* from init.c */
+bool SiSInitPtr(struct SiS_Private *SiS_Pr);
+unsigned short SiS_GetModeID_LCD(int VGAEngine, unsigned int VBFlags, int HDisplay,
+ int VDisplay, int Depth, bool FSTN,
+ unsigned short CustomT, int LCDwith, int LCDheight,
+ unsigned int VBFlags2);
+unsigned short SiS_GetModeID_TV(int VGAEngine, unsigned int VBFlags, int HDisplay,
+ int VDisplay, int Depth, unsigned int VBFlags2);
+unsigned short SiS_GetModeID_VGA2(int VGAEngine, unsigned int VBFlags, int HDisplay,
+ int VDisplay, int Depth, unsigned int VBFlags2);
+
+void SiS_DisplayOn(struct SiS_Private *SiS_Pr);
+void SiS_DisplayOff(struct SiS_Private *SiS_Pr);
+void SiSRegInit(struct SiS_Private *SiS_Pr, SISIOADDRESS BaseAddr);
+void SiS_SetEnableDstn(struct SiS_Private *SiS_Pr, int enable);
+void SiS_SetEnableFstn(struct SiS_Private *SiS_Pr, int enable);
+unsigned short SiS_GetModeFlag(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
+ unsigned short ModeIdIndex);
+bool SiSDetermineROMLayout661(struct SiS_Private *SiS_Pr);
+
+bool SiS_SearchModeID(struct SiS_Private *SiS_Pr, unsigned short *ModeNo,
+ unsigned short *ModeIdIndex);
+unsigned short SiS_GetModePtr(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
+ unsigned short ModeIdIndex);
+unsigned short SiS_GetRefCRTVCLK(struct SiS_Private *SiS_Pr, unsigned short Index, int UseWide);
+unsigned short SiS_GetRefCRT1CRTC(struct SiS_Private *SiS_Pr, unsigned short Index, int UseWide);
+unsigned short SiS_GetColorDepth(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
+ unsigned short ModeIdIndex);
+unsigned short SiS_GetOffset(struct SiS_Private *SiS_Pr,unsigned short ModeNo,
+ unsigned short ModeIdIndex, unsigned short RRTI);
+#ifdef CONFIG_FB_SIS_300
+void SiS_GetFIFOThresholdIndex300(struct SiS_Private *SiS_Pr, unsigned short *idx1,
+ unsigned short *idx2);
+unsigned short SiS_GetFIFOThresholdB300(unsigned short idx1, unsigned short idx2);
+unsigned short SiS_GetLatencyFactor630(struct SiS_Private *SiS_Pr, unsigned short index);
+#endif
+void SiS_LoadDAC(struct SiS_Private *SiS_Pr, unsigned short ModeNo, unsigned short ModeIdIndex);
+bool SiSSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo);
+void SiS_CalcCRRegisters(struct SiS_Private *SiS_Pr, int depth);
+void SiS_CalcLCDACRT1Timing(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
+ unsigned short ModeIdIndex);
+void SiS_Generic_ConvertCRData(struct SiS_Private *SiS_Pr, unsigned char *crdata, int xres,
+ int yres, struct fb_var_screeninfo *var, bool writeres);
+
+/* From init301.c: */
+extern void SiS_GetVBInfo(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
+ unsigned short ModeIdIndex, int chkcrt2mode);
+extern void SiS_GetLCDResInfo(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
+ unsigned short ModeIdIndex);
+extern void SiS_SetYPbPr(struct SiS_Private *SiS_Pr);
+extern void SiS_SetTVMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
+ unsigned short ModeIdIndex);
+extern void SiS_UnLockCRT2(struct SiS_Private *SiS_Pr);
+extern void SiS_DisableBridge(struct SiS_Private *);
+extern bool SiS_SetCRT2Group(struct SiS_Private *, unsigned short);
+extern unsigned short SiS_GetRatePtr(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
+ unsigned short ModeIdIndex);
+extern void SiS_WaitRetrace1(struct SiS_Private *SiS_Pr);
+extern unsigned short SiS_GetResInfo(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
+ unsigned short ModeIdIndex);
+extern unsigned short SiS_GetCH700x(struct SiS_Private *SiS_Pr, unsigned short tempax);
+extern unsigned short SiS_GetVCLK2Ptr(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
+ unsigned short ModeIdIndex, unsigned short RRTI);
+extern bool SiS_IsVAMode(struct SiS_Private *);
+extern bool SiS_IsDualEdge(struct SiS_Private *);
+
+#ifdef CONFIG_FB_SIS_300
+extern unsigned int sisfb_read_nbridge_pci_dword(struct SiS_Private *SiS_Pr, int reg);
+extern void sisfb_write_nbridge_pci_dword(struct SiS_Private *SiS_Pr, int reg,
+ unsigned int val);
+#endif
+#ifdef CONFIG_FB_SIS_315
+extern void sisfb_write_nbridge_pci_byte(struct SiS_Private *SiS_Pr, int reg,
+ unsigned char val);
+extern unsigned int sisfb_read_mio_pci_word(struct SiS_Private *SiS_Pr, int reg);
+#endif
+
+
/* MMIO access macros */
#define MMIO_IN8(base, offset) readb((base+offset))
#define MMIO_IN16(base, offset) readw((base+offset))
@@ -583,4 +663,55 @@ struct sis_video_info {
struct sis_video_info *next;
};
+/* from sis_accel.c */
+extern void fbcon_sis_fillrect(struct fb_info *info,
+ const struct fb_fillrect *rect);
+extern void fbcon_sis_copyarea(struct fb_info *info,
+ const struct fb_copyarea *area);
+extern int fbcon_sis_sync(struct fb_info *info);
+
+/* Internal 2D accelerator functions */
+extern int sisfb_initaccel(struct sis_video_info *ivideo);
+extern void sisfb_syncaccel(struct sis_video_info *ivideo);
+
+/* Internal general routines */
+#ifdef CONFIG_FB_SIS_300
+unsigned int sisfb_read_nbridge_pci_dword(struct SiS_Private *SiS_Pr, int reg);
+void sisfb_write_nbridge_pci_dword(struct SiS_Private *SiS_Pr, int reg, unsigned int val);
+unsigned int sisfb_read_lpc_pci_dword(struct SiS_Private *SiS_Pr, int reg);
+#endif
+#ifdef CONFIG_FB_SIS_315
+void sisfb_write_nbridge_pci_byte(struct SiS_Private *SiS_Pr, int reg, unsigned char val);
+unsigned int sisfb_read_mio_pci_word(struct SiS_Private *SiS_Pr, int reg);
+#endif
+
+/* SiS-specific exported functions */
+void sis_malloc(struct sis_memreq *req);
+void sis_malloc_new(struct pci_dev *pdev, struct sis_memreq *req);
+void sis_free(u32 base);
+void sis_free_new(struct pci_dev *pdev, u32 base);
+
+/* Routines from init.c/init301.c */
+extern unsigned short SiS_GetModeID_LCD(int VGAEngine, unsigned int VBFlags, int HDisplay,
+ int VDisplay, int Depth, bool FSTN, unsigned short CustomT,
+ int LCDwith, int LCDheight, unsigned int VBFlags2);
+extern unsigned short SiS_GetModeID_TV(int VGAEngine, unsigned int VBFlags, int HDisplay,
+ int VDisplay, int Depth, unsigned int VBFlags2);
+extern unsigned short SiS_GetModeID_VGA2(int VGAEngine, unsigned int VBFlags, int HDisplay,
+ int VDisplay, int Depth, unsigned int VBFlags2);
+extern void SiSRegInit(struct SiS_Private *SiS_Pr, SISIOADDRESS BaseAddr);
+extern bool SiSSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo);
+extern void SiS_SetEnableDstn(struct SiS_Private *SiS_Pr, int enable);
+extern void SiS_SetEnableFstn(struct SiS_Private *SiS_Pr, int enable);
+
+extern bool SiSDetermineROMLayout661(struct SiS_Private *SiS_Pr);
+
+extern bool sisfb_gettotalfrommode(struct SiS_Private *SiS_Pr, unsigned char modeno,
+ int *htotal, int *vtotal, unsigned char rateindex);
+extern int sisfb_mode_rate_to_dclock(struct SiS_Private *SiS_Pr,
+ unsigned char modeno, unsigned char rateindex);
+extern int sisfb_mode_rate_to_ddata(struct SiS_Private *SiS_Pr, unsigned char modeno,
+ unsigned char rateindex, struct fb_var_screeninfo *var);
+
+
#endif
diff --git a/drivers/video/fbdev/sis/sis_main.c b/drivers/video/fbdev/sis/sis_main.c
index ecdd054d8951..20aff9005978 100644
--- a/drivers/video/fbdev/sis/sis_main.c
+++ b/drivers/video/fbdev/sis/sis_main.c
@@ -56,15 +56,66 @@
#include "sis.h"
#include "sis_main.h"
+#include "init301.h"
#if !defined(CONFIG_FB_SIS_300) && !defined(CONFIG_FB_SIS_315)
#warning Neither CONFIG_FB_SIS_300 nor CONFIG_FB_SIS_315 is set
#warning sisfb will not work!
#endif
+/* ---------------------- Prototypes ------------------------- */
+
+/* Interface used by the world */
+#ifndef MODULE
+static int sisfb_setup(char *options);
+#endif
+
+/* Interface to the low level console driver */
+static int sisfb_init(void);
+
+/* fbdev routines */
+static int sisfb_get_fix(struct fb_fix_screeninfo *fix, int con,
+ struct fb_info *info);
+
+static int sisfb_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg);
+static int sisfb_set_par(struct fb_info *info);
+static int sisfb_blank(int blank,
+ struct fb_info *info);
+
static void sisfb_handle_command(struct sis_video_info *ivideo,
struct sisfb_cmd *sisfb_command);
+static void sisfb_search_mode(char *name, bool quiet);
+static int sisfb_validate_mode(struct sis_video_info *ivideo, int modeindex, u32 vbflags);
+static u8 sisfb_search_refresh_rate(struct sis_video_info *ivideo, unsigned int rate,
+ int index);
+static int sisfb_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp,
+ struct fb_info *fb_info);
+static int sisfb_do_set_var(struct fb_var_screeninfo *var, int isactive,
+ struct fb_info *info);
+static void sisfb_pre_setmode(struct sis_video_info *ivideo);
+static void sisfb_post_setmode(struct sis_video_info *ivideo);
+static bool sisfb_CheckVBRetrace(struct sis_video_info *ivideo);
+static bool sisfbcheckvretracecrt2(struct sis_video_info *ivideo);
+static bool sisfbcheckvretracecrt1(struct sis_video_info *ivideo);
+static bool sisfb_bridgeisslave(struct sis_video_info *ivideo);
+static void sisfb_detect_VB_connect(struct sis_video_info *ivideo);
+static void sisfb_get_VB_type(struct sis_video_info *ivideo);
+static void sisfb_set_TVxposoffset(struct sis_video_info *ivideo, int val);
+static void sisfb_set_TVyposoffset(struct sis_video_info *ivideo, int val);
+
+/* Internal heap routines */
+static int sisfb_heap_init(struct sis_video_info *ivideo);
+static struct SIS_OH * sisfb_poh_new_node(struct SIS_HEAP *memheap);
+static struct SIS_OH * sisfb_poh_allocate(struct SIS_HEAP *memheap, u32 size);
+static void sisfb_delete_node(struct SIS_OH *poh);
+static void sisfb_insert_node(struct SIS_OH *pohList, struct SIS_OH *poh);
+static struct SIS_OH * sisfb_poh_free(struct SIS_HEAP *memheap, u32 base);
+static void sisfb_free_node(struct SIS_HEAP *memheap, struct SIS_OH *poh);
+
+
/* ------------------ Internal helper routines ----------------- */
static void __init
diff --git a/drivers/video/fbdev/sis/sis_main.h b/drivers/video/fbdev/sis/sis_main.h
index 32e23c209430..d8ba07061f1e 100644
--- a/drivers/video/fbdev/sis/sis_main.h
+++ b/drivers/video/fbdev/sis/sis_main.h
@@ -661,121 +661,4 @@ static struct _customttable {
}
};
-/* ---------------------- Prototypes ------------------------- */
-
-/* Interface used by the world */
-#ifndef MODULE
-static int sisfb_setup(char *options);
#endif
-
-/* Interface to the low level console driver */
-static int sisfb_init(void);
-
-/* fbdev routines */
-static int sisfb_get_fix(struct fb_fix_screeninfo *fix, int con,
- struct fb_info *info);
-
-static int sisfb_ioctl(struct fb_info *info, unsigned int cmd,
- unsigned long arg);
-static int sisfb_set_par(struct fb_info *info);
-static int sisfb_blank(int blank,
- struct fb_info *info);
-extern void fbcon_sis_fillrect(struct fb_info *info,
- const struct fb_fillrect *rect);
-extern void fbcon_sis_copyarea(struct fb_info *info,
- const struct fb_copyarea *area);
-extern int fbcon_sis_sync(struct fb_info *info);
-
-/* Internal 2D accelerator functions */
-extern int sisfb_initaccel(struct sis_video_info *ivideo);
-extern void sisfb_syncaccel(struct sis_video_info *ivideo);
-
-/* Internal general routines */
-static void sisfb_search_mode(char *name, bool quiet);
-static int sisfb_validate_mode(struct sis_video_info *ivideo, int modeindex, u32 vbflags);
-static u8 sisfb_search_refresh_rate(struct sis_video_info *ivideo, unsigned int rate,
- int index);
-static int sisfb_setcolreg(unsigned regno, unsigned red, unsigned green,
- unsigned blue, unsigned transp,
- struct fb_info *fb_info);
-static int sisfb_do_set_var(struct fb_var_screeninfo *var, int isactive,
- struct fb_info *info);
-static void sisfb_pre_setmode(struct sis_video_info *ivideo);
-static void sisfb_post_setmode(struct sis_video_info *ivideo);
-static bool sisfb_CheckVBRetrace(struct sis_video_info *ivideo);
-static bool sisfbcheckvretracecrt2(struct sis_video_info *ivideo);
-static bool sisfbcheckvretracecrt1(struct sis_video_info *ivideo);
-static bool sisfb_bridgeisslave(struct sis_video_info *ivideo);
-static void sisfb_detect_VB_connect(struct sis_video_info *ivideo);
-static void sisfb_get_VB_type(struct sis_video_info *ivideo);
-static void sisfb_set_TVxposoffset(struct sis_video_info *ivideo, int val);
-static void sisfb_set_TVyposoffset(struct sis_video_info *ivideo, int val);
-#ifdef CONFIG_FB_SIS_300
-unsigned int sisfb_read_nbridge_pci_dword(struct SiS_Private *SiS_Pr, int reg);
-void sisfb_write_nbridge_pci_dword(struct SiS_Private *SiS_Pr, int reg, unsigned int val);
-unsigned int sisfb_read_lpc_pci_dword(struct SiS_Private *SiS_Pr, int reg);
-#endif
-#ifdef CONFIG_FB_SIS_315
-void sisfb_write_nbridge_pci_byte(struct SiS_Private *SiS_Pr, int reg, unsigned char val);
-unsigned int sisfb_read_mio_pci_word(struct SiS_Private *SiS_Pr, int reg);
-#endif
-
-/* SiS-specific exported functions */
-void sis_malloc(struct sis_memreq *req);
-void sis_malloc_new(struct pci_dev *pdev, struct sis_memreq *req);
-void sis_free(u32 base);
-void sis_free_new(struct pci_dev *pdev, u32 base);
-
-/* Internal heap routines */
-static int sisfb_heap_init(struct sis_video_info *ivideo);
-static struct SIS_OH * sisfb_poh_new_node(struct SIS_HEAP *memheap);
-static struct SIS_OH * sisfb_poh_allocate(struct SIS_HEAP *memheap, u32 size);
-static void sisfb_delete_node(struct SIS_OH *poh);
-static void sisfb_insert_node(struct SIS_OH *pohList, struct SIS_OH *poh);
-static struct SIS_OH * sisfb_poh_free(struct SIS_HEAP *memheap, u32 base);
-static void sisfb_free_node(struct SIS_HEAP *memheap, struct SIS_OH *poh);
-
-/* Routines from init.c/init301.c */
-extern unsigned short SiS_GetModeID_LCD(int VGAEngine, unsigned int VBFlags, int HDisplay,
- int VDisplay, int Depth, bool FSTN, unsigned short CustomT,
- int LCDwith, int LCDheight, unsigned int VBFlags2);
-extern unsigned short SiS_GetModeID_TV(int VGAEngine, unsigned int VBFlags, int HDisplay,
- int VDisplay, int Depth, unsigned int VBFlags2);
-extern unsigned short SiS_GetModeID_VGA2(int VGAEngine, unsigned int VBFlags, int HDisplay,
- int VDisplay, int Depth, unsigned int VBFlags2);
-extern void SiSRegInit(struct SiS_Private *SiS_Pr, SISIOADDRESS BaseAddr);
-extern bool SiSSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo);
-extern void SiS_SetEnableDstn(struct SiS_Private *SiS_Pr, int enable);
-extern void SiS_SetEnableFstn(struct SiS_Private *SiS_Pr, int enable);
-
-extern bool SiSDetermineROMLayout661(struct SiS_Private *SiS_Pr);
-
-extern bool sisfb_gettotalfrommode(struct SiS_Private *SiS_Pr, unsigned char modeno,
- int *htotal, int *vtotal, unsigned char rateindex);
-extern int sisfb_mode_rate_to_dclock(struct SiS_Private *SiS_Pr,
- unsigned char modeno, unsigned char rateindex);
-extern int sisfb_mode_rate_to_ddata(struct SiS_Private *SiS_Pr, unsigned char modeno,
- unsigned char rateindex, struct fb_var_screeninfo *var);
-
-/* Chrontel TV, DDC and DPMS functions */
-extern unsigned short SiS_GetCH700x(struct SiS_Private *SiS_Pr, unsigned short reg);
-extern void SiS_SetCH700x(struct SiS_Private *SiS_Pr, unsigned short reg, unsigned char val);
-extern unsigned short SiS_GetCH701x(struct SiS_Private *SiS_Pr, unsigned short reg);
-extern void SiS_SetCH701x(struct SiS_Private *SiS_Pr, unsigned short reg, unsigned char val);
-extern void SiS_SetCH70xxANDOR(struct SiS_Private *SiS_Pr, unsigned short reg,
- unsigned char myor, unsigned char myand);
-extern void SiS_DDC2Delay(struct SiS_Private *SiS_Pr, unsigned int delaytime);
-extern void SiS_SetChrontelGPIO(struct SiS_Private *SiS_Pr, unsigned short myvbinfo);
-extern unsigned short SiS_HandleDDC(struct SiS_Private *SiS_Pr, unsigned int VBFlags, int VGAEngine,
- unsigned short adaptnum, unsigned short DDCdatatype, unsigned char *buffer,
- unsigned int VBFlags2);
-extern unsigned short SiS_ReadDDC1Bit(struct SiS_Private *SiS_Pr);
-#ifdef CONFIG_FB_SIS_315
-extern void SiS_Chrontel701xBLOn(struct SiS_Private *SiS_Pr);
-extern void SiS_Chrontel701xBLOff(struct SiS_Private *SiS_Pr);
-#endif
-extern void SiS_SiS30xBLOn(struct SiS_Private *SiS_Pr);
-extern void SiS_SiS30xBLOff(struct SiS_Private *SiS_Pr);
-#endif
-
-
diff --git a/drivers/video/fbdev/smscufx.c b/drivers/video/fbdev/smscufx.c
index 8db7085e5d1a..22b606af0a87 100644
--- a/drivers/video/fbdev/smscufx.c
+++ b/drivers/video/fbdev/smscufx.c
@@ -1293,7 +1293,6 @@ static struct fb_ops ufx_ops = {
* Assumes no active clients have framebuffer open */
static int ufx_realloc_framebuffer(struct ufx_data *dev, struct fb_info *info)
{
- int retval = -ENOMEM;
int old_len = info->fix.smem_len;
int new_len;
unsigned char *old_fb = info->screen_base;
@@ -1308,10 +1307,8 @@ static int ufx_realloc_framebuffer(struct ufx_data *dev, struct fb_info *info)
* Alloc system memory for virtual framebuffer
*/
new_fb = vmalloc(new_len);
- if (!new_fb) {
- pr_err("Virtual framebuffer alloc failed");
- goto error;
- }
+ if (!new_fb)
+ return -ENOMEM;
if (info->screen_base) {
memcpy(new_fb, old_fb, old_len);
@@ -1323,11 +1320,7 @@ static int ufx_realloc_framebuffer(struct ufx_data *dev, struct fb_info *info)
info->fix.smem_start = (unsigned long) new_fb;
info->flags = smscufx_info_flags;
}
-
- retval = 0;
-
-error:
- return retval;
+ return 0;
}
/* sets up I2C Controller for 100 Kbps, std. speed, 7-bit addr, master,
@@ -1620,8 +1613,8 @@ static int ufx_usb_probe(struct usb_interface *interface,
{
struct usb_device *usbdev;
struct ufx_data *dev;
- struct fb_info *info = NULL;
- int retval = -ENOMEM;
+ struct fb_info *info;
+ int retval;
u32 id_rev, fpga_rev;
/* usb initialization */
@@ -1631,7 +1624,7 @@ static int ufx_usb_probe(struct usb_interface *interface,
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
dev_err(&usbdev->dev, "ufx_usb_probe: failed alloc of dev struct\n");
- goto error;
+ return -ENOMEM;
}
/* we need to wait for both usb and fbdev to spin down on disconnect */
@@ -1652,9 +1645,8 @@ static int ufx_usb_probe(struct usb_interface *interface,
dev_dbg(dev->gdev, "fb_defio enable=%d\n", fb_defio);
if (!ufx_alloc_urb_list(dev, WRITES_IN_FLIGHT, MAX_TRANSFER)) {
- retval = -ENOMEM;
dev_err(dev->gdev, "ufx_alloc_urb_list failed\n");
- goto error;
+ goto e_nomem;
}
/* We don't register a new USB class. Our client interface is fbdev */
@@ -1662,9 +1654,8 @@ static int ufx_usb_probe(struct usb_interface *interface,
/* allocates framebuffer driver structure, not framebuffer memory */
info = framebuffer_alloc(0, &usbdev->dev);
if (!info) {
- retval = -ENOMEM;
dev_err(dev->gdev, "framebuffer_alloc failed\n");
- goto error;
+ goto e_nomem;
}
dev->info = info;
@@ -1675,7 +1666,7 @@ static int ufx_usb_probe(struct usb_interface *interface,
retval = fb_alloc_cmap(&info->cmap, 256, 0);
if (retval < 0) {
dev_err(dev->gdev, "fb_alloc_cmap failed %x\n", retval);
- goto error;
+ goto destroy_modedb;
}
INIT_DELAYED_WORK(&dev->free_framebuffer_work,
@@ -1736,26 +1727,20 @@ static int ufx_usb_probe(struct usb_interface *interface,
return 0;
error:
- if (dev) {
- if (info) {
- if (info->cmap.len != 0)
- fb_dealloc_cmap(&info->cmap);
- if (info->monspecs.modedb)
- fb_destroy_modedb(info->monspecs.modedb);
- vfree(info->screen_base);
-
- fb_destroy_modelist(&info->modelist);
-
- framebuffer_release(info);
- }
-
- kref_put(&dev->kref, ufx_free); /* ref for framebuffer */
- kref_put(&dev->kref, ufx_free); /* last ref from kref_init */
-
- /* dev has been deallocated. Do not dereference */
- }
-
+ fb_dealloc_cmap(&info->cmap);
+destroy_modedb:
+ fb_destroy_modedb(info->monspecs.modedb);
+ vfree(info->screen_base);
+ fb_destroy_modelist(&info->modelist);
+ framebuffer_release(info);
+put_ref:
+ kref_put(&dev->kref, ufx_free); /* ref for framebuffer */
+ kref_put(&dev->kref, ufx_free); /* last ref from kref_init */
return retval;
+
+e_nomem:
+ retval = -ENOMEM;
+ goto put_ref;
}
static void ufx_usb_disconnect(struct usb_interface *interface)
diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c
index f599520374dd..6439231f2db2 100644
--- a/drivers/video/fbdev/ssd1307fb.c
+++ b/drivers/video/fbdev/ssd1307fb.c
@@ -628,7 +628,8 @@ static int ssd1307fb_probe(struct i2c_client *client,
goto fb_alloc_error;
}
- ssd1307fb_defio = devm_kzalloc(&client->dev, sizeof(struct fb_deferred_io), GFP_KERNEL);
+ ssd1307fb_defio = devm_kzalloc(&client->dev, sizeof(*ssd1307fb_defio),
+ GFP_KERNEL);
if (!ssd1307fb_defio) {
dev_err(&client->dev, "Couldn't allocate deferred io.\n");
ret = -ENOMEM;
diff --git a/drivers/video/fbdev/stifb.c b/drivers/video/fbdev/stifb.c
index 6ded5c198998..045e8afe398b 100644
--- a/drivers/video/fbdev/stifb.c
+++ b/drivers/video/fbdev/stifb.c
@@ -527,7 +527,7 @@ rattlerSetupPlanes(struct stifb_info *fb)
fb->id = saved_id;
for (y = 0; y < fb->info.var.yres; ++y)
- memset(fb->info.screen_base + y * fb->info.fix.line_length,
+ fb_memset(fb->info.screen_base + y * fb->info.fix.line_length,
0xff, fb->info.var.xres * fb->info.var.bits_per_pixel/8);
CRX24_SET_OVLY_MASK(fb);
@@ -1126,10 +1126,8 @@ static int __init stifb_init_fb(struct sti_struct *sti, int bpp_pref)
int bpp, xres, yres;
fb = kzalloc(sizeof(*fb), GFP_ATOMIC);
- if (!fb) {
- printk(KERN_ERR "stifb: Could not allocate stifb structure\n");
- return -ENODEV;
- }
+ if (!fb)
+ return -ENOMEM;
info = &fb->info;
diff --git a/drivers/video/fbdev/udlfb.c b/drivers/video/fbdev/udlfb.c
index 452a4207ac1b..f365d4862015 100644
--- a/drivers/video/fbdev/udlfb.c
+++ b/drivers/video/fbdev/udlfb.c
@@ -428,7 +428,6 @@ static void dlfb_compress_hline(
const uint16_t *pixel = *pixel_start_ptr;
uint32_t dev_addr = *device_address_ptr;
uint8_t *cmd = *command_buffer_ptr;
- const int bpp = 2;
while ((pixel_end > pixel) &&
(cmd_buffer_end - MIN_RLX_CMD_BYTES > cmd)) {
@@ -441,9 +440,9 @@ static void dlfb_compress_hline(
*cmd++ = 0xAF;
*cmd++ = 0x6B;
- *cmd++ = (uint8_t) ((dev_addr >> 16) & 0xFF);
- *cmd++ = (uint8_t) ((dev_addr >> 8) & 0xFF);
- *cmd++ = (uint8_t) ((dev_addr) & 0xFF);
+ *cmd++ = dev_addr >> 16;
+ *cmd++ = dev_addr >> 8;
+ *cmd++ = dev_addr;
cmd_pixels_count_byte = cmd++; /* we'll know this later */
cmd_pixel_start = pixel;
@@ -453,15 +452,15 @@ static void dlfb_compress_hline(
cmd_pixel_end = pixel + min(MAX_CMD_PIXELS + 1,
min((int)(pixel_end - pixel),
- (int)(cmd_buffer_end - cmd) / bpp));
+ (int)(cmd_buffer_end - cmd) / BPP));
- prefetch_range((void *) pixel, (cmd_pixel_end - pixel) * bpp);
+ prefetch_range((void *) pixel, (cmd_pixel_end - pixel) * BPP);
while (pixel < cmd_pixel_end) {
const uint16_t * const repeating_pixel = pixel;
- *(uint16_t *)cmd = cpu_to_be16p(pixel);
- cmd += 2;
+ *cmd++ = *pixel >> 8;
+ *cmd++ = *pixel;
pixel++;
if (unlikely((pixel < cmd_pixel_end) &&
@@ -490,7 +489,7 @@ static void dlfb_compress_hline(
}
*cmd_pixels_count_byte = (pixel - cmd_pixel_start) & 0xFF;
- dev_addr += (pixel - cmd_pixel_start) * bpp;
+ dev_addr += (pixel - cmd_pixel_start) * BPP;
}
if (cmd_buffer_end <= MIN_RLX_CMD_BYTES + cmd) {
@@ -1136,7 +1135,6 @@ static struct fb_ops dlfb_ops = {
*/
static int dlfb_realloc_framebuffer(struct dlfb_data *dlfb, struct fb_info *info)
{
- int retval = -ENOMEM;
int old_len = info->fix.smem_len;
int new_len;
unsigned char *old_fb = info->screen_base;
@@ -1152,7 +1150,7 @@ static int dlfb_realloc_framebuffer(struct dlfb_data *dlfb, struct fb_info *info
new_fb = vmalloc(new_len);
if (!new_fb) {
dev_err(info->dev, "Virtual framebuffer alloc failed\n");
- goto error;
+ return -ENOMEM;
}
if (info->screen_base) {
@@ -1181,11 +1179,7 @@ static int dlfb_realloc_framebuffer(struct dlfb_data *dlfb, struct fb_info *info
dlfb->backing_buffer = new_back;
}
}
-
- retval = 0;
-
-error:
- return retval;
+ return 0;
}
/*
@@ -1531,15 +1525,16 @@ static int dlfb_parse_vendor_descriptor(struct dlfb_data *dlfb,
u8 length;
u16 key;
- key = le16_to_cpu(*((u16 *) desc));
- desc += sizeof(u16);
- length = *desc;
- desc++;
+ key = *desc++;
+ key |= (u16)*desc++ << 8;
+ length = *desc++;
switch (key) {
case 0x0200: { /* max_area */
- u32 max_area;
- max_area = le32_to_cpu(*((u32 *)desc));
+ u32 max_area = *desc++;
+ max_area |= (u32)*desc++ << 8;
+ max_area |= (u32)*desc++ << 16;
+ max_area |= (u32)*desc++ << 24;
dev_warn(&intf->dev,
"DL chip limited to %d pixel modes\n",
max_area);
diff --git a/drivers/video/fbdev/vermilion/vermilion.c b/drivers/video/fbdev/vermilion/vermilion.c
index 6f8d444eb0e3..5172fa581147 100644
--- a/drivers/video/fbdev/vermilion/vermilion.c
+++ b/drivers/video/fbdev/vermilion/vermilion.c
@@ -651,7 +651,7 @@ static int vmlfb_check_var_locked(struct fb_var_screeninfo *var,
}
pitch = ALIGN((var->xres * var->bits_per_pixel) >> 3, 0x40);
- mem = pitch * var->yres_virtual;
+ mem = (u64)pitch * var->yres_virtual;
if (mem > vinfo->vram_contig_size) {
return -ENOMEM;
}
diff --git a/drivers/video/fbdev/via/via_aux_sii164.c b/drivers/video/fbdev/via/via_aux_sii164.c
index ca1b35f033b1..c27f62c2c75a 100644
--- a/drivers/video/fbdev/via/via_aux_sii164.c
+++ b/drivers/video/fbdev/via/via_aux_sii164.c
@@ -36,7 +36,7 @@ static void probe(struct via_aux_bus *bus, u8 addr)
.name = name};
/* check vendor id and device id */
const u8 id[] = {0x01, 0x00, 0x06, 0x00}, len = ARRAY_SIZE(id);
- u8 tmp[len];
+ u8 tmp[ARRAY_SIZE(id)];
if (!via_aux_read(&drv, 0x00, tmp, len) || memcmp(id, tmp, len))
return;
diff --git a/drivers/video/fbdev/via/via_aux_vt1631.c b/drivers/video/fbdev/via/via_aux_vt1631.c
index 06e742f1f723..32978a0ccfd7 100644
--- a/drivers/video/fbdev/via/via_aux_vt1631.c
+++ b/drivers/video/fbdev/via/via_aux_vt1631.c
@@ -36,7 +36,7 @@ void via_aux_vt1631_probe(struct via_aux_bus *bus)
.name = name};
/* check vendor id and device id */
const u8 id[] = {0x06, 0x11, 0x91, 0x31}, len = ARRAY_SIZE(id);
- u8 tmp[len];
+ u8 tmp[ARRAY_SIZE(id)];
if (!via_aux_read(&drv, 0x00, tmp, len) || memcmp(id, tmp, len))
return;
diff --git a/drivers/video/fbdev/via/via_aux_vt1632.c b/drivers/video/fbdev/via/via_aux_vt1632.c
index d24f4cd97401..cec8cc43d524 100644
--- a/drivers/video/fbdev/via/via_aux_vt1632.c
+++ b/drivers/video/fbdev/via/via_aux_vt1632.c
@@ -36,7 +36,7 @@ static void probe(struct via_aux_bus *bus, u8 addr)
.name = name};
/* check vendor id and device id */
const u8 id[] = {0x06, 0x11, 0x92, 0x31}, len = ARRAY_SIZE(id);
- u8 tmp[len];
+ u8 tmp[ARRAY_SIZE(id)];
if (!via_aux_read(&drv, 0x00, tmp, len) || memcmp(id, tmp, len))
return;
diff --git a/drivers/video/fbdev/via/via_aux_vt1636.c b/drivers/video/fbdev/via/via_aux_vt1636.c
index 9e015c101d4d..2b10bc21ab79 100644
--- a/drivers/video/fbdev/via/via_aux_vt1636.c
+++ b/drivers/video/fbdev/via/via_aux_vt1636.c
@@ -36,7 +36,7 @@ void via_aux_vt1636_probe(struct via_aux_bus *bus)
.name = name};
/* check vendor id and device id */
const u8 id[] = {0x06, 0x11, 0x45, 0x33}, len = ARRAY_SIZE(id);
- u8 tmp[len];
+ u8 tmp[ARRAY_SIZE(id)];
if (!via_aux_read(&drv, 0x00, tmp, len) || memcmp(id, tmp, len))
return;
diff --git a/drivers/video/logo/Kconfig b/drivers/video/logo/Kconfig
index 0037104d66ac..d1f6196c8b9a 100644
--- a/drivers/video/logo/Kconfig
+++ b/drivers/video/logo/Kconfig
@@ -27,16 +27,6 @@ config LOGO_LINUX_CLUT224
bool "Standard 224-color Linux logo"
default y
-config LOGO_BLACKFIN_VGA16
- bool "16-colour Blackfin Processor Linux logo"
- depends on BLACKFIN
- default y
-
-config LOGO_BLACKFIN_CLUT224
- bool "224-colour Blackfin Processor Linux logo"
- depends on BLACKFIN
- default y
-
config LOGO_DEC_CLUT224
bool "224-color Digital Equipment Corporation Linux logo"
depends on MACH_DECSTATION || ALPHA
@@ -77,9 +67,4 @@ config LOGO_SUPERH_CLUT224
depends on SUPERH
default y
-config LOGO_M32R_CLUT224
- bool "224-color M32R Linux logo"
- depends on M32R
- default y
-
endif # LOGO
diff --git a/drivers/video/logo/Makefile b/drivers/video/logo/Makefile
index 6194373ee424..228a89b9bdd1 100644
--- a/drivers/video/logo/Makefile
+++ b/drivers/video/logo/Makefile
@@ -5,8 +5,6 @@ obj-$(CONFIG_LOGO) += logo.o
obj-$(CONFIG_LOGO_LINUX_MONO) += logo_linux_mono.o
obj-$(CONFIG_LOGO_LINUX_VGA16) += logo_linux_vga16.o
obj-$(CONFIG_LOGO_LINUX_CLUT224) += logo_linux_clut224.o
-obj-$(CONFIG_LOGO_BLACKFIN_CLUT224) += logo_blackfin_clut224.o
-obj-$(CONFIG_LOGO_BLACKFIN_VGA16) += logo_blackfin_vga16.o
obj-$(CONFIG_LOGO_DEC_CLUT224) += logo_dec_clut224.o
obj-$(CONFIG_LOGO_MAC_CLUT224) += logo_mac_clut224.o
obj-$(CONFIG_LOGO_PARISC_CLUT224) += logo_parisc_clut224.o
@@ -15,7 +13,6 @@ obj-$(CONFIG_LOGO_SUN_CLUT224) += logo_sun_clut224.o
obj-$(CONFIG_LOGO_SUPERH_MONO) += logo_superh_mono.o
obj-$(CONFIG_LOGO_SUPERH_VGA16) += logo_superh_vga16.o
obj-$(CONFIG_LOGO_SUPERH_CLUT224) += logo_superh_clut224.o
-obj-$(CONFIG_LOGO_M32R_CLUT224) += logo_m32r_clut224.o
obj-$(CONFIG_SPU_BASE) += logo_spe_clut224.o
diff --git a/drivers/video/logo/logo.c b/drivers/video/logo/logo.c
index 4d50bfd13e7c..36aa050f9a21 100644
--- a/drivers/video/logo/logo.c
+++ b/drivers/video/logo/logo.c
@@ -63,10 +63,6 @@ const struct linux_logo * __ref fb_find_logo(int depth)
/* Generic Linux logo */
logo = &logo_linux_vga16;
#endif
-#ifdef CONFIG_LOGO_BLACKFIN_VGA16
- /* Blackfin processor logo */
- logo = &logo_blackfin_vga16;
-#endif
#ifdef CONFIG_LOGO_SUPERH_VGA16
/* SuperH Linux logo */
logo = &logo_superh_vga16;
@@ -78,10 +74,6 @@ const struct linux_logo * __ref fb_find_logo(int depth)
/* Generic Linux logo */
logo = &logo_linux_clut224;
#endif
-#ifdef CONFIG_LOGO_BLACKFIN_CLUT224
- /* Blackfin Linux logo */
- logo = &logo_blackfin_clut224;
-#endif
#ifdef CONFIG_LOGO_DEC_CLUT224
/* DEC Linux logo on MIPS/MIPS64 or ALPHA */
logo = &logo_dec_clut224;
@@ -107,10 +99,6 @@ const struct linux_logo * __ref fb_find_logo(int depth)
/* SuperH Linux logo */
logo = &logo_superh_clut224;
#endif
-#ifdef CONFIG_LOGO_M32R_CLUT224
- /* M32R Linux logo */
- logo = &logo_m32r_clut224;
-#endif
}
return logo;
}
diff --git a/drivers/video/logo/logo_blackfin_clut224.ppm b/drivers/video/logo/logo_blackfin_clut224.ppm
deleted file mode 100644
index dc9a50a14477..000000000000
--- a/drivers/video/logo/logo_blackfin_clut224.ppm
+++ /dev/null
@@ -1,1127 +0,0 @@
-P3
-# This was generated by the GIMP & Netpbm tools
-# gimp linux_bf.svg (create 80x80 save as linux_bf.ppm)
-# pnmquant 224 linux_bf.ppm | pnmnoraw > logo_blackfin_clut224.ppm
-#
-80 80
-255
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-1 1 1 3 3 3 4 6 6 6 6 6 4 6 6 3 3 3
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 2 2 2 10 10 10 26 26 27
-44 44 45 66 66 66 78 81 81 78 81 81 75 75 76 60 60 60
-39 39 39 20 20 20 6 6 6 1 1 1 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 2 2 2 14 14 14 47 47 47 84 84 84 75 75 76
-47 47 47 12 12 12 0 0 0 0 0 0 0 0 0 20 20 20
-53 54 54 81 81 82 74 74 74 31 31 31 6 6 6 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-4 4 4 34 34 35 84 84 84 60 60 60 4 4 4 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 17 18 18 75 75 76 66 66 66 17 18 18
-1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 3 3
-42 42 43 84 84 84 8 8 8 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 3 3 36 40 40 10 16 16 0 0 0 31 31 31 84 84 84
-29 29 30 2 2 2 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 26 27 27
-84 84 84 3 3 3 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-15 19 19 114 115 115 110 114 114 44 46 46 0 0 0 12 12 12
-90 87 86 24 24 24 1 1 1 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 8 8 8 75 75 76
-14 14 14 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-30 40 40 133 133 133 129 130 130 78 85 85 23 31 30 0 0 0
-19 19 19 78 81 81 13 13 13 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 26 27 27 81 81 82
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-36 40 40 89 90 91 55 63 63 23 31 30 4 6 6 0 0 0
-0 0 0 60 60 60 47 47 47 2 2 2 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 2 2 2 53 54 54 34 34 35
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-4 10 10 7 9 9 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 1 1 1 84 84 84 13 13 13 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 4 6 6 78 81 81 2 2 2
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 65 64 64 36 36 36 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 10 11 11 81 81 82 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 12 12 12 67 70 70 4 4 4 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 16 16 16 81 81 82 0 0 0
-0 0 0 0 0 0 4 10 10 44 50 50 18 21 21 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 1 1 78 85 85 120 121 122 7 9 9 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 82 82 81 12 12 12 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 19 19 19 81 81 82 0 0 0
-0 0 0 2 2 2 8 8 8 55 63 63 108 110 110 52 58 58
-0 0 0 0 0 0 0 0 0 0 0 0 42 42 43 129 130 130
-140 142 143 114 115 115 110 114 114 129 130 130 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 75 75 76 24 24 24 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 19 19 19 74 74 74 0 0 0
-4 6 6 167 168 167 196 196 197 196 196 197 61 65 66 78 85 85
-0 0 0 0 0 0 0 0 0 118 118 118 202 202 203 219 219 219
-219 219 219 214 214 215 187 187 188 78 85 85 29 33 34 0 0 0
-0 0 0 0 0 0 0 0 0 60 60 60 39 39 39 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 19 19 19 72 71 71 0 0 0
-185 185 184 244 245 245 250 251 252 251 251 252 247 248 249 36 36 36
-0 0 0 0 0 0 13 13 13 243 243 241 252 252 252 253 253 253
-253 253 253 252 252 252 247 247 246 193 193 194 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 42 42 43 50 51 51 1 1 1
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 19 19 19 78 81 81 0 0 0
-247 247 246 193 193 194 95 97 97 193 193 194 255 255 255 237 237 238
-0 0 0 0 0 0 202 202 203 255 255 255 247 247 246 108 107 107
-82 85 86 167 168 167 255 255 255 248 248 249 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 34 34 35 56 56 56 2 2 2
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 19 19 19 78 81 81 0 0 0
-250 250 251 50 51 51 153 154 155 150 151 151 244 245 245 244 245 245
-44 50 50 84 89 89 153 154 155 255 255 255 140 142 143 0 0 0
-149 149 150 156 155 156 237 237 238 254 254 254 67 70 70 0 0 0
-0 0 0 0 0 0 0 0 0 39 39 39 47 47 47 1 1 1
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 19 19 19 81 81 82 0 0 0
-248 248 249 34 34 35 72 71 71 165 165 165 202 202 203 244 245 245
-10 16 16 82 85 86 89 90 91 255 255 255 95 97 97 0 0 0
-0 0 0 53 54 54 177 177 174 255 255 255 127 127 126 0 0 0
-0 0 0 0 0 0 0 0 0 39 39 39 36 36 36 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 14 14 14 78 81 81 0 0 0
-243 243 243 89 90 91 0 0 0 36 40 40 201 147 55 241 205 27
-241 205 27 241 205 27 241 205 27 238 192 33 108 110 110 0 0 0
-0 0 0 0 0 0 191 190 190 254 254 254 34 34 35 0 0 0
-0 0 0 0 0 0 0 0 0 42 42 43 42 42 43 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 10 10 10 75 75 76 0 0 0
-202 202 203 218 217 217 21 19 17 230 165 41 199 129 48 213 157 40
-244 212 23 243 206 27 180 121 62 243 206 27 244 209 25 226 179 40
-15 10 7 103 103 103 254 254 254 251 251 252 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 17 18 18 58 58 58 2 2 2
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 9 9 9 84 84 84 0 0 0
-0 0 0 226 226 219 213 157 40 244 209 25 245 211 23 245 211 23
-245 214 38 245 214 38 245 211 23 245 211 23 245 211 23 244 212 23
-244 212 23 241 205 27 226 179 40 196 196 197 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 74 74 74 4 6 6
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 7 7 7 84 84 84 0 0 0
-54 42 32 213 157 40 243 206 27 245 211 23 245 211 23 245 211 23
-245 215 41 245 214 35 245 211 23 245 211 23 245 214 35 245 215 41
-245 214 35 245 211 23 245 211 23 238 204 29 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 81 81 82 12 12 12
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 4 6 6 74 74 74 0 0 0
-201 147 55 241 205 27 245 211 23 245 211 23 245 211 23 245 213 29
-245 214 38 245 211 23 245 211 23 245 214 35 245 215 41 245 215 41
-245 213 29 142 83 36 142 83 36 244 209 25 1 1 1 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 74 74 74 25 25 26
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 4 4 4 72 71 71 6 6 6
-213 157 40 244 209 25 245 211 23 245 211 23 245 211 23 245 213 29
-244 212 23 245 211 23 245 214 35 245 215 41 245 215 41 245 213 29
-142 83 36 142 83 36 238 192 33 241 205 27 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 44 44 44 49 50 50
-2 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 3 3 3 65 64 64 17 18 18
-199 129 48 199 129 48 245 211 23 245 211 23 245 211 23 245 211 23
-245 211 23 244 212 23 245 214 38 245 214 38 142 83 36 142 83 36
-142 83 36 245 211 23 244 210 23 230 165 41 0 0 0 0 0 0
-78 81 81 114 115 115 73 79 79 0 0 0 3 3 3 81 81 82
-9 9 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 1 1 1 49 50 50 29 29 30
-90 87 86 199 129 48 173 101 51 173 101 51 245 211 23 245 211 23
-245 211 23 230 165 41 142 83 36 142 83 36 142 83 36 245 211 23
-244 210 23 241 205 27 230 165 41 175 173 165 3 3 3 0 0 0
-44 46 46 118 118 118 118 118 118 108 110 110 0 0 0 75 75 76
-28 28 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 1 1 1 52 53 53 26 26 27
-118 118 118 175 173 165 199 129 48 173 101 51 173 101 51 173 101 51
-173 101 51 142 83 36 173 101 51 245 211 23 244 209 25 238 204 29
-213 157 40 214 196 166 227 227 227 214 214 215 120 121 122 0 0 0
-0 0 0 108 110 110 118 118 118 118 118 118 0 0 0 23 23 23
-66 66 66 4 6 6 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 7 7 7 75 75 76 4 4 4
-127 127 126 205 205 205 181 181 181 199 129 48 226 179 40 244 209 25
-244 209 25 244 209 25 243 206 27 238 192 33 213 157 40 187 166 103
-234 234 234 248 248 249 251 252 252 248 248 249 214 214 215 0 0 0
-0 0 0 0 0 0 103 103 103 100 103 103 0 0 0 0 0 0
-78 81 81 24 24 24 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 26 27 27 82 82 81 0 0 0
-146 146 147 234 234 234 222 221 221 178 178 179 180 121 62 213 157 40
-213 157 40 213 157 40 201 147 55 180 121 62 219 219 219 243 243 241
-253 253 253 255 255 255 255 255 255 255 255 255 250 250 251 120 121 122
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-20 20 20 72 71 71 8 8 8 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 10 10 10 75 75 76 22 22 22 0 0 0
-205 205 205 253 253 253 247 248 249 212 211 212 178 178 179 161 161 162
-165 165 165 181 181 181 205 205 205 227 227 227 244 245 245 254 254 254
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 239 239 240
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 67 70 70 39 39 39 2 2 2 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 4 4 4 50 51 51 60 60 60 0 0 0 16 16 16
-249 250 251 255 255 255 255 255 255 240 240 240 209 210 210 193 193 194
-200 200 197 212 211 212 231 231 231 246 247 248 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 253 253 253
-153 154 155 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 3 3 3 84 84 84 20 20 20 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-2 2 2 33 33 34 81 81 82 0 0 0 0 0 0 231 231 231
-255 255 255 255 255 255 255 255 255 253 253 253 234 234 234 222 221 221
-227 227 227 237 237 238 250 250 251 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-240 240 240 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 26 27 27 72 71 71 8 8 8 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1
-21 21 22 84 84 84 7 7 7 0 0 0 150 151 151 252 252 252
-255 255 255 255 255 255 255 255 255 255 255 255 252 252 252 244 245 245
-246 247 248 253 253 253 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-251 251 252 9 9 9 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 65 64 64 47 47 47 3 3 3
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 12 12
-75 75 76 26 26 27 0 0 0 1 1 1 239 239 240 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 202 202 203 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 84 84 84 28 28 29
-1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 4 4 4 55 55 55
-60 60 60 0 0 0 0 0 0 95 97 97 248 248 249 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 244 245 245 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 14 14 14 82 82 81
-15 15 15 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 1 1 1 29 29 30 84 84 84
-0 0 0 0 0 0 0 0 0 156 155 156 247 247 246 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 247 247 246 240 240 240 232 232 233 232 232 233
-243 243 243 253 253 253 53 54 54 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 44 44 44
-60 60 60 6 6 6 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 10 10 10 81 81 82 14 14 14
-0 0 0 0 0 0 6 6 6 150 151 151 214 214 215 250 251 252
-255 255 255 255 255 255 255 255 255 246 247 248 218 217 217 214 214 215
-218 217 217 244 245 245 255 255 255 255 255 255 255 255 255 250 248 249
-232 232 233 214 214 215 196 196 197 182 183 184 181 181 181 181 181 181
-187 187 188 240 240 240 232 232 233 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-78 81 81 34 34 35 1 1 1 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 1 1 1 39 39 39 74 74 74 0 0 0
-0 0 0 0 0 0 60 60 60 161 161 162 200 200 197 229 229 230
-251 251 252 255 255 255 255 255 255 255 255 255 243 243 241 214 214 215
-248 248 249 255 255 255 255 255 255 255 255 255 255 255 255 254 254 254
-239 239 240 214 214 215 193 193 194 182 183 184 178 178 179 176 177 177
-176 177 177 182 183 184 248 248 249 14 14 14 0 0 0 61 65 66
-10 16 16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-10 10 10 84 84 84 13 13 13 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 10 11 11 82 82 81 7 7 7 0 0 0
-0 0 0 0 0 0 165 165 165 229 229 230 249 250 251 254 254 254
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 253 253 253 240 240 240 227 227 227 205 205 205
-181 181 181 176 177 177 191 190 190 227 227 227 0 0 0 44 50 50
-84 89 89 61 65 66 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 58 58 58 49 50 50 3 3 3 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 1 1 1 36 36 36 66 66 66 0 0 0 29 33 34
-0 3 3 26 27 27 234 234 234 254 254 254 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-254 254 254 253 253 254 252 253 253 253 253 254 253 254 254 253 254 254
-254 254 254 255 255 255 255 255 255 255 255 255 255 255 255 251 251 252
-227 227 227 187 187 188 176 177 177 222 221 221 13 13 13 0 0 0
-12 15 14 73 79 79 36 40 40 0 0 0 0 0 0 0 0 0
-0 0 0 1 1 1 90 87 86 17 18 18 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 7 7 7 78 81 81 12 12 12 23 31 30 52 58 58
-0 0 0 209 210 210 253 253 253 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 254 254 254
-251 251 252 150 151 151 103 103 103 129 130 130 196 196 197 250 250 251
-252 252 253 254 254 254 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 240 240 240 193 193 194 196 196 197 229 229 230 0 0 0
-0 0 0 4 10 10 30 40 40 0 3 3 0 0 0 0 0 0
-0 0 0 0 0 0 47 47 47 53 54 54 3 3 3 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 23 23 23 81 81 82 0 0 0 52 58 58 36 40 40
-42 42 43 250 250 251 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 254 254 254
-227 227 227 7 7 7 7 7 7 7 7 7 7 7 7 44 44 45
-156 155 156 249 250 251 253 253 253 254 254 254 255 255 255 255 255 255
-255 255 255 255 255 255 247 247 246 222 221 221 239 239 240 0 0 0
-30 40 40 44 50 50 23 31 30 29 33 34 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 90 87 86 16 16 16 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-2 2 2 50 51 51 42 42 43 29 33 34 52 58 58 0 0 0
-232 232 233 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 254 254 254
-250 251 252 44 44 44 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 56 56 56 209 210 210 252 252 253 254 254 254 255 255 255
-255 255 255 255 255 255 255 255 255 254 253 253 249 250 251 146 146 147
-36 40 40 44 50 50 36 40 40 67 70 70 61 65 66 0 0 0
-0 0 0 0 0 0 0 0 0 55 55 55 44 44 45 1 1 1
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-10 10 10 81 81 82 1 1 1 52 58 58 44 50 50 52 53 53
-251 251 252 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 254 254 254
-253 253 253 187 187 188 8 8 8 7 7 7 7 7 7 7 7 7
-7 7 7 7 7 7 19 19 19 178 178 179 252 252 253 254 254 254
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 237 237 238
-10 16 16 30 40 40 0 3 3 23 31 30 84 89 89 0 0 0
-0 0 0 0 0 0 0 0 0 3 3 3 81 81 82 9 9 9
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-29 29 30 72 71 71 10 16 16 52 58 58 0 0 0 222 221 221
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-254 254 254 251 251 252 95 97 97 7 7 7 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 10 10 10 161 161 162 251 252 252
-254 254 254 255 255 255 255 255 255 255 255 255 255 255 255 248 248 249
-0 0 0 0 0 0 0 0 0 0 0 0 84 89 89 0 3 3
-0 0 0 0 0 0 0 0 0 0 0 0 74 74 74 26 27 27
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 4 4
-65 64 64 20 20 20 20 25 25 30 40 40 0 0 0 247 247 246
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 253 253 254 222 221 221 9 9 9 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 8 8 8 149 149 150
-252 252 253 254 254 254 255 255 255 255 255 255 255 255 255 252 252 252
-0 0 0 0 0 0 0 0 0 0 0 0 73 79 79 12 15 14
-0 0 0 0 0 0 0 0 0 0 0 0 36 36 36 58 58 58
-3 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 20 20 20
-74 74 74 0 0 0 4 10 10 4 10 10 36 36 36 252 252 252
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 227 227 227 253 253 253 255 255 255
-255 255 255 254 254 254 250 251 252 65 64 64 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 8 8 8
-146 146 147 251 252 252 254 254 254 255 255 255 255 255 255 253 254 254
-0 0 0 0 0 0 0 0 0 0 0 0 52 58 58 10 16 16
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 82 82 81
-9 9 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 4 6 6 65 64 64
-25 25 25 0 3 3 30 40 40 0 0 0 187 187 188 254 254 254
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 193 193 194 253 252 252 255 255 255
-255 255 255 255 255 255 252 253 253 129 130 130 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-8 8 8 149 149 150 252 252 253 254 254 254 255 255 255 254 254 254
-52 53 53 0 0 0 0 0 0 0 0 0 20 25 25 2 5 4
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 81 81 82
-20 20 20 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 26 26 27 81 81 82
-0 0 0 18 21 21 73 79 79 0 0 0 237 237 238 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 182 183 184 255 255 255 255 255 255
-255 255 255 255 255 255 253 253 253 176 177 177 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 8 8 8 153 154 155 251 252 252 254 254 254 255 255 255
-150 151 151 0 0 0 0 0 0 0 0 0 20 25 25 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 65 64 64
-33 33 34 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 6 6 6 67 70 70 20 20 20
-0 0 0 23 31 30 82 85 86 0 0 0 247 247 246 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 182 183 184 255 255 255 255 255 255
-255 255 255 255 255 255 253 254 254 214 214 215 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 7 7 7 8 8 8 156 155 156 252 252 253 254 254 254
-167 168 167 0 0 0 0 0 0 0 0 0 67 70 70 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 47 47 47
-44 44 44 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 21 21 22 75 75 76 0 0 0
-0 0 0 29 33 34 84 89 89 0 0 0 248 248 249 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 248 248 249 181 181 181 255 255 255 255 255 255
-255 255 255 255 255 255 254 254 254 240 240 240 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 8 8 8 161 161 162 251 252 252
-185 185 184 4 4 4 0 0 0 10 11 11 100 103 103 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 36 36 36
-55 55 55 2 2 2 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 33 33 34 50 51 51 0 0 0
-0 0 0 9 11 11 82 85 86 10 16 16 248 248 249 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 245 244 245 179 180 181 255 255 255 255 255 255
-255 255 255 255 255 255 254 254 254 251 252 252 20 20 20 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 10 10 10 161 161 162
-205 205 205 17 18 18 0 0 0 95 97 97 78 81 81 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 36 36 36
-53 54 54 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 31 31 31 58 58 58 0 0 0
-0 0 0 0 0 0 67 70 70 78 81 81 248 248 249 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 234 234 234 179 180 181 255 255 255 255 255 255
-255 255 255 255 255 255 254 254 254 251 252 252 23 23 23 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-10 11 11 84 84 84 161 161 162 209 210 210 229 229 230 237 237 238
-202 202 203 26 26 27 9 11 11 44 50 50 0 0 0 4 6 6
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 52 53 53
-39 39 39 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 23 23 23 78 81 81 213 157 40
-243 206 27 243 206 27 54 42 32 73 79 79 222 221 221 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 238 238 236 178 178 179 255 255 255 255 255 255
-255 255 255 255 255 255 254 254 254 251 252 253 36 36 36 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 84 84 84
-222 221 221 251 252 252 252 253 253 253 253 253 253 254 254 252 252 253
-146 146 147 140 142 143 156 155 156 110 114 114 26 27 27 82 85 86
-84 89 89 95 97 97 36 40 40 0 0 0 0 0 0 74 74 74
-23 23 23 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 2 2 2 14 14 14
-24 24 24 26 26 27 26 26 27 26 26 27 25 25 26 21 21 22
-7 7 7 0 0 0 1 1 1 34 34 35 238 192 33 244 210 23
-244 212 23 244 212 23 244 210 23 88 79 47 200 200 197 254 254 254
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 244 245 245 179 180 181 255 255 255 255 255 255
-255 255 255 255 255 255 254 254 254 252 252 253 36 36 36 7 7 7
-7 7 7 7 7 7 7 7 7 8 8 8 149 149 150 251 251 252
-252 252 253 253 253 253 253 253 253 250 248 249 239 223 156 239 223 156
-120 121 122 182 183 184 176 177 177 120 121 122 33 33 34 3 3 3
-0 0 0 67 70 70 146 146 147 20 25 25 1 1 1 82 82 81
-9 9 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 19 19 19 89 90 91
-146 146 147 150 151 151 150 151 151 150 151 151 150 151 151 129 130 130
-58 58 58 6 6 6 14 14 14 201 147 55 245 211 23 245 213 29
-245 214 35 245 215 41 245 213 29 244 210 23 142 83 36 232 232 233
-254 254 254 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 185 185 184 255 255 255 255 255 255
-255 255 255 255 255 255 254 254 254 251 252 252 50 51 51 7 7 7
-7 7 7 7 7 7 7 7 7 146 146 147 251 252 252 252 253 253
-251 252 253 239 239 240 171 168 154 129 130 130 137 136 134 175 173 165
-221 218 200 65 64 64 22 22 22 186 186 187 114 115 115 26 26 27
-2 2 2 0 0 0 61 65 66 31 33 27 238 192 33 108 96 91
-9 9 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 2 2 2 52 53 53 178 178 179
-21 21 22 7 7 7 7 7 7 7 7 7 7 7 7 118 118 118
-137 136 134 36 36 36 65 64 64 243 206 27 244 212 23 245 215 41
-245 215 41 245 215 41 245 215 41 244 209 25 244 209 25 1 1 1
-219 219 219 253 253 253 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 214 214 215 255 255 255 255 255 255
-255 255 255 255 255 255 254 254 254 252 252 253 50 51 51 7 7 7
-7 7 7 7 7 7 84 84 84 250 251 252 252 253 253 251 251 252
-167 168 167 22 22 22 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 34 34 35 187 187 188 103 103 103
-29 29 30 3 3 3 7 9 9 238 204 29 245 215 41 245 214 35
-28 28 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 7 7 7 90 87 86 178 178 179
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 16 16 16
-193 193 194 133 133 133 187 166 103 245 218 76 245 218 76 245 216 51
-245 216 51 245 218 76 246 224 96 245 218 76 245 218 76 245 218 76
-25 25 25 186 186 187 252 252 252 254 254 254 254 254 254 253 254 254
-254 254 254 254 254 254 254 254 254 246 247 248 254 254 254 253 254 254
-254 254 254 254 254 254 253 254 254 251 252 252 36 36 36 7 7 7
-7 7 7 20 20 20 229 229 230 253 253 253 252 253 253 178 178 179
-10 10 10 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 42 42 43 196 196 197
-118 118 118 33 33 34 238 204 29 245 215 41 245 215 41 245 215 41
-49 50 50 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 17 18 18 120 121 122 137 136 134
-7 7 7 7 7 7 34 34 35 20 20 20 7 7 7 7 7 7
-202 202 203 209 206 202 193 187 162 193 187 162 248 234 156 245 218 76
-245 218 76 248 234 156 193 187 162 193 187 162 193 187 162 214 196 166
-240 219 129 95 97 97 196 196 197 186 186 187 187 187 188 196 196 197
-252 252 253 251 252 253 212 211 212 187 187 188 196 196 197 251 252 252
-218 217 217 187 187 188 191 190 190 250 251 252 24 24 24 7 7 7
-7 7 7 110 114 114 252 252 253 253 254 254 250 251 252 89 90 91
-89 90 91 129 130 130 127 127 126 44 44 44 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 49 50 50
-202 202 203 214 196 166 245 216 51 245 214 38 245 214 35 245 214 38
-58 58 58 2 2 2 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 31 31 31 156 155 156 82 82 81
-7 7 7 10 10 10 237 237 238 66 66 66 7 7 7 25 25 25
-247 248 249 81 81 82 7 7 7 31 31 31 247 237 174 245 218 76
-246 226 108 200 200 197 7 7 7 7 7 7 7 7 7 137 136 134
-247 237 174 193 193 194 72 71 71 7 7 7 7 7 7 8 8 8
-196 196 197 250 251 252 67 70 70 7 7 7 84 84 84 244 245 245
-47 47 47 7 7 7 118 118 118 249 250 251 12 12 12 7 7 7
-9 9 9 218 217 217 253 253 253 254 254 254 252 253 253 251 251 252
-249 250 251 237 237 238 95 97 97 9 9 9 15 15 15 95 97 97
-47 47 47 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-66 66 66 240 230 197 246 226 108 245 214 38 245 211 23 244 212 23
-65 64 64 3 3 3 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 2 2 2 52 53 53 185 185 184 25 25 25
-7 7 7 60 60 60 240 240 240 14 14 14 7 7 7 84 84 84
-247 248 249 23 23 23 7 7 7 94 91 88 248 234 156 245 218 76
-248 234 156 127 127 126 7 7 7 7 7 7 7 7 7 167 168 167
-251 248 240 65 64 64 7 7 7 7 7 7 7 7 7 7 7 7
-84 84 84 243 243 243 15 15 15 7 7 7 140 142 143 146 146 147
-7 7 7 33 33 34 237 237 238 243 243 243 21 21 22 120 121 122
-218 217 217 252 252 253 254 254 254 253 253 254 252 253 253 251 252 252
-247 248 249 72 71 71 7 7 7 58 58 58 222 221 221 248 248 249
-75 75 76 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 82 82 81 246 239 193 246 226 108 245 216 51 245 214 38
-238 192 33 21 21 22 1 1 1 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 8 8 8 90 87 86 182 183 184 7 7 7
-7 7 7 120 121 122 187 187 188 7 7 7 7 7 7 146 146 147
-205 205 205 7 7 7 7 7 7 153 153 148 240 219 129 246 224 96
-246 239 193 39 39 39 60 60 60 108 110 110 7 7 7 202 202 203
-227 227 227 7 7 7 7 7 7 205 205 205 89 90 91 7 7 7
-120 121 122 193 193 194 7 7 7 7 7 7 186 186 187 25 25 25
-7 7 7 167 168 167 251 251 252 243 243 243 214 214 215 250 251 252
-251 252 253 254 254 254 253 253 253 219 219 219 140 140 139 140 140 139
-118 118 118 7 7 7 52 53 53 237 237 238 247 247 246 176 177 177
-8 8 8 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 7 7 7 95 97 97 246 239 193 246 226 108 245 216 51
-245 214 38 201 147 55 31 31 31 103 103 103 103 103 103 72 71 71
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 17 18 18 127 127 126 140 140 139 7 7 7
-7 7 7 17 18 18 17 18 18 7 7 7 95 97 97 244 245 245
-146 146 147 7 7 7 7 7 7 200 200 197 246 226 108 240 219 129
-194 194 184 7 7 7 140 140 139 89 90 91 7 7 7 232 232 233
-165 165 165 7 7 7 31 31 31 249 250 251 39 39 39 7 7 7
-176 177 177 133 133 133 7 7 7 22 22 22 108 110 110 7 7 7
-72 71 71 251 252 252 252 253 253 250 251 252 247 248 249 205 205 205
-251 252 253 254 254 254 252 252 253 84 84 84 7 7 7 7 7 7
-7 7 7 7 7 7 140 142 143 247 248 249 140 140 139 14 14 14
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 16 16 16
-14 14 14 7 7 7 7 7 7 114 115 115 246 239 193 246 224 96
-245 216 51 245 216 51 243 235 220 176 177 177 185 185 184 229 229 230
-47 47 47 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 31 31 31 156 155 156 90 87 86 7 7 7
-7 7 7 7 7 7 7 7 7 31 31 31 243 243 241 247 247 246
-84 84 84 7 7 7 26 27 27 246 239 193 246 226 108 248 234 156
-108 110 110 7 7 7 212 211 212 44 44 44 22 22 22 249 250 251
-108 107 107 7 7 7 89 90 91 238 238 236 114 115 115 118 118 118
-231 231 231 75 75 76 7 7 7 34 34 35 10 11 11 12 12 12
-214 214 215 253 253 253 253 253 253 200 200 197 31 31 31 103 103 103
-252 252 253 252 253 253 218 217 217 9 9 9 7 7 7 7 7 7
-7 7 7 7 7 7 25 25 25 39 39 39 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 103 103 103 234 234 234
-181 181 181 7 7 7 7 7 7 7 7 7 133 133 133 247 237 174
-246 224 96 246 226 108 185 185 184 177 177 174 153 154 155 181 181 181
-140 140 139 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 1 1 1 49 50 50 186 186 187 28 28 28 7 7 7
-12 12 12 22 22 22 7 7 7 7 7 7 108 107 107 247 247 246
-25 25 25 7 7 7 90 87 86 247 237 174 246 226 108 246 239 193
-28 28 28 44 44 44 237 237 238 9 9 9 53 54 54 249 250 251
-49 50 50 7 7 7 153 153 148 249 241 199 214 196 166 185 185 184
-229 229 230 19 19 19 7 7 7 7 7 7 7 7 7 103 103 103
-251 252 253 254 254 254 253 253 253 150 151 151 7 7 7 187 187 188
-252 252 253 251 251 252 103 103 103 7 7 7 7 7 7 7 7 7
-7 7 7 23 23 23 17 18 18 7 7 7 7 7 7 7 7 7
-7 7 7 7 7 7 12 12 12 153 153 148 246 239 193 249 241 199
-161 161 162 9 9 9 84 84 84 108 110 110 25 25 25 153 153 148
-247 237 174 246 224 96 218 217 217 165 165 165 182 183 184 193 193 194
-114 115 115 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 4 4 4 74 74 74 181 181 181 7 7 7 7 7 7
-110 114 114 200 200 197 7 7 7 7 7 7 60 60 60 209 210 210
-7 7 7 7 7 7 146 146 147 248 234 156 248 234 156 177 177 174
-7 7 7 118 118 118 193 193 194 7 7 7 84 84 84 232 232 233
-8 8 8 7 7 7 209 210 210 221 218 200 193 187 162 219 219 219
-200 200 197 7 7 7 7 7 7 7 7 7 7 7 7 95 97 97
-251 252 252 254 254 254 252 253 253 118 118 118 29 29 30 247 248 249
-252 252 253 227 227 227 16 16 16 7 7 7 7 7 7 7 7 7
-100 103 103 218 217 217 219 218 214 7 7 7 7 7 7 7 7 7
-7 7 7 21 21 22 185 185 184 246 239 193 248 234 156 240 230 197
-60 60 60 194 194 184 246 239 193 249 241 199 137 136 134 10 10 10
-171 168 154 248 234 156 248 234 156 226 226 219 209 210 210 249 241 199
-28 28 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 13 13 13 108 110 110 146 146 147 7 7 7 7 7 7
-167 168 167 140 140 139 7 7 7 7 7 7 120 121 122 146 146 147
-7 7 7 7 7 7 194 194 184 240 219 129 247 237 174 95 97 97
-7 7 7 95 97 97 90 87 86 7 7 7 118 118 118 176 177 177
-7 7 7 28 28 28 248 248 249 44 44 45 7 7 7 167 168 167
-140 140 139 7 7 7 36 36 36 74 74 74 7 7 7 65 64 64
-251 252 253 254 254 254 251 252 252 81 81 82 108 110 110 251 252 252
-251 251 252 127 127 126 7 7 7 7 7 7 8 8 8 140 140 139
-181 181 181 140 140 139 221 218 200 7 7 7 7 7 7 7 7 7
-34 34 35 209 210 210 231 231 231 246 239 193 247 237 174 194 194 184
-227 227 227 249 241 199 240 219 129 248 234 156 153 153 148 7 7 7
-13 13 13 185 185 184 248 234 156 245 218 76 245 216 51 245 214 38
-31 31 31 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 31 31 31 153 154 155 89 90 91 7 7 7 8 8 8
-232 232 233 82 82 81 7 7 7 7 7 7 179 180 181 89 90 91
-7 7 7 24 24 24 243 235 220 248 234 156 240 230 197 20 20 20
-7 7 7 7 7 7 7 7 7 7 7 7 149 149 150 118 118 118
-7 7 7 90 87 86 229 229 230 7 7 7 7 7 7 229 229 230
-82 82 81 7 7 7 95 97 97 100 103 103 7 7 7 34 34 35
-251 252 252 253 253 254 251 251 252 47 47 47 193 193 194 251 252 252
-239 239 240 23 23 23 7 7 7 13 13 13 165 165 165 234 234 234
-149 149 150 146 114 101 200 200 197 7 7 7 7 7 7 52 53 53
-227 227 227 167 168 167 16 16 16 214 196 166 248 234 156 243 235 220
-219 219 219 156 155 156 247 237 174 246 239 193 75 75 76 7 7 7
-60 60 60 227 227 227 243 235 220 240 219 129 245 218 76 245 213 29
-16 16 16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-1 1 1 49 50 50 185 185 184 33 33 34 7 7 7 10 11 11
-56 56 56 16 16 16 7 7 7 10 10 10 237 237 238 26 27 27
-7 7 7 55 55 55 185 185 184 221 218 200 167 168 167 7 7 7
-20 20 20 39 39 39 10 11 11 7 7 7 181 181 181 58 58 58
-7 7 7 103 103 103 133 133 133 7 7 7 44 44 44 247 248 249
-24 24 24 7 7 7 156 155 156 129 130 130 7 7 7 9 9 9
-244 245 245 252 253 253 237 237 238 34 34 35 248 248 249 251 251 252
-161 161 162 7 7 7 24 24 24 187 187 188 212 211 212 67 70 70
-187 187 188 173 170 143 209 206 202 10 10 10 95 97 97 237 237 238
-129 130 130 8 8 8 89 90 91 246 239 193 247 237 174 177 177 174
-17 18 18 137 136 134 249 241 199 219 218 214 10 10 10 95 97 97
-243 243 243 150 151 151 31 31 31 221 218 200 240 219 129 53 54 54
-3 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-4 4 4 72 71 71 182 183 184 7 7 7 7 7 7 7 7 7
-7 7 7 7 7 7 12 12 12 161 161 162 209 210 210 7 7 7
-7 7 7 7 7 7 7 7 7 187 187 188 82 82 81 7 7 7
-146 146 147 247 248 249 17 18 18 7 7 7 212 211 212 47 47 47
-7 7 7 7 7 7 7 7 7 8 8 8 146 146 147 205 205 205
-7 7 7 7 7 7 214 214 215 156 155 156 7 7 7 7 7 7
-218 217 217 251 252 252 186 186 187 110 114 114 249 250 251 248 248 249
-75 75 76 34 34 35 205 205 205 129 130 130 16 16 16 7 7 7
-156 155 156 214 196 166 240 230 197 243 243 241 227 227 227 74 74 74
-7 7 7 29 29 30 226 226 219 249 241 199 175 173 165 14 14 14
-9 9 9 221 218 200 246 239 193 153 153 148 146 146 147 246 247 248
-110 114 114 7 7 7 7 7 7 42 42 43 193 193 194 95 97 97
-19 19 19 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-6 6 6 84 84 84 140 142 143 7 7 7 7 7 7 7 7 7
-7 7 7 20 20 20 177 177 174 249 241 199 149 149 150 7 7 7
-7 7 7 7 7 7 10 11 11 226 226 219 13 13 13 8 8 8
-219 218 214 219 218 214 7 7 7 8 8 8 238 238 236 200 200 197
-13 13 13 7 7 7 13 13 13 161 161 162 243 235 220 146 146 147
-7 7 7 29 29 30 232 232 233 176 177 177 7 7 7 7 7 7
-182 183 184 237 237 238 129 130 130 167 168 167 176 177 177 202 202 203
-10 11 11 95 97 97 44 44 45 7 7 7 7 7 7 7 7 7
-75 75 76 226 226 219 243 235 220 156 155 156 24 24 24 7 7 7
-7 7 7 176 177 177 247 247 246 200 200 197 17 18 18 7 7 7
-49 50 50 246 239 193 248 234 156 251 248 240 239 239 240 84 84 84
-7 7 7 7 7 7 7 7 7 7 7 7 60 60 60 187 187 188
-84 84 84 14 14 14 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-4 4 4 53 54 54 137 136 134 156 155 156 161 161 162 161 161 162
-167 168 167 239 223 156 240 219 129 246 226 108 239 223 156 239 223 156
-239 223 156 239 223 156 214 196 166 239 223 156 193 187 162 193 187 162
-248 234 156 239 223 156 193 187 162 193 187 162 248 234 156 248 234 156
-214 196 166 193 187 162 214 196 166 248 234 156 240 219 129 214 196 166
-193 187 162 193 187 162 171 168 154 146 146 147 137 136 134 137 136 134
-161 161 162 209 210 210 65 64 64 202 202 203 179 180 181 140 140 139
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 60 60 60 39 39 39 7 7 7 7 7 7 7 7 7
-66 66 66 249 250 251 202 202 203 16 16 16 7 7 7 7 7 7
-23 23 23 243 235 220 246 239 193 226 226 219 52 53 53 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 75 75 76
-176 177 177 66 66 66 9 9 9 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 10 10 10 28 28 29 34 34 35 36 36 36 36 36 36
-44 44 45 146 114 101 241 207 50 241 207 50 241 207 50 241 211 63
-241 211 63 241 211 63 241 211 63 241 211 63 241 211 63 245 216 51
-245 216 51 245 216 51 241 211 63 241 211 63 245 216 51 241 211 63
-245 218 76 245 218 76 245 216 51 245 215 41 245 214 38 241 207 50
-241 211 63 201 147 55 88 79 47 29 29 30 34 34 35 42 42 43
-103 103 103 191 190 190 75 75 76 196 196 197 200 200 197 65 64 64
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-90 87 86 146 146 147 19 19 19 7 7 7 7 7 7 7 7 7
-7 7 7 90 87 86 140 140 139 31 31 31 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-103 103 103 161 161 162 53 54 54 7 7 7 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 12 12 12 50 51 51 146 114 101 180 121 62 199 129 48
-201 147 55 213 157 40 213 157 40 230 165 41 226 179 40 226 179 40
-238 192 33 241 205 27 244 209 25 244 210 23 244 212 23 245 211 23
-245 211 23 245 211 23 245 211 23 244 209 25 238 204 29 226 179 40
-213 157 40 199 129 48 54 42 32 0 0 0 4 6 6 44 44 45
-150 151 151 129 130 130 137 136 134 205 205 205 202 202 203 8 8 8
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 129 130 130 146 146 147 47 47 47 4 4 4 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 2 2 2 12 12 12 28 28 29 49 50 50
-74 74 74 108 96 91 180 121 62 180 121 62 199 129 48 201 147 55
-213 157 40 230 165 41 226 179 40 238 192 33 241 205 27 241 205 27
-243 206 27 243 206 27 241 205 27 238 204 29 226 179 40 213 157 40
-199 129 48 199 129 48 21 19 17 65 64 64 103 103 103 167 168 167
-202 202 203 24 24 24 193 193 194 229 229 230 140 140 139 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 8 8 8 156 155 156 133 133 133 36 36 36 3 3 3
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1
-4 4 4 10 11 11 21 21 22 39 39 39 60 60 60 108 96 91
-180 121 62 199 129 48 199 129 48 213 157 40 230 165 41 226 179 40
-226 179 40 226 179 40 226 179 40 226 179 40 213 157 40 199 129 48
-180 121 62 99 91 79 72 71 71 56 56 56 129 130 130 167 168 167
-21 21 22 17 18 18 231 231 231 229 229 230 52 53 53 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 7 7 7 13 13 13 176 177 177 120 121 122 33 33 34
-2 2 2 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 2 2 2 8 8 8
-21 21 22 47 47 47 99 91 79 180 121 62 199 129 48 199 129 48
-201 147 55 213 157 40 213 157 40 201 147 55 199 129 48 180 121 62
-99 91 79 26 26 27 9 9 9 60 60 60 186 186 187 31 31 31
-7 7 7 60 60 60 243 243 243 209 210 210 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
-7 7 7 7 7 7 7 7 7 26 27 27 193 193 194 108 110 110
-22 22 22 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 1 1 1 8 8 8 24 24 24 58 58 58 108 96 91
-180 121 62 180 121 62 180 121 62 180 121 62 180 121 62 72 71 71
-15 15 15 0 0 0 4 6 6 75 75 76 156 155 156 24 24 24
-24 24 24 108 107 107 232 232 233 137 136 134 24 24 24 24 24 24
-24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24
-24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24
-24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24
-24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24
-24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24
-24 24 24 24 24 24 24 24 24 24 24 24 58 58 58 176 177 177
-60 60 60 3 3 3
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 2 2 2 12 12 12
-26 27 27 44 44 44 55 55 55 50 51 51 29 29 30 8 8 8
-0 0 0 0 0 0 3 3 3 47 47 47 127 127 126 150 151 151
-150 151 151 140 142 143 129 130 130 140 142 143 150 151 151 150 151 151
-150 151 151 150 151 151 150 151 151 150 151 151 150 151 151 150 151 151
-150 151 151 150 151 151 153 154 155 161 161 162 165 165 165 167 168 167
-177 177 174 167 168 167 161 161 162 156 155 156 150 151 151 150 151 151
-150 151 151 150 151 151 150 151 151 150 151 151 150 151 151 150 151 151
-150 151 151 150 151 151 150 151 151 150 151 151 150 151 151 150 151 151
-150 151 151 150 151 151 150 151 151 150 151 151 149 149 150 127 127 126
-44 44 45 2 2 2
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 2 2 2 1 1 1 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 7 7 7 21 21 22 25 25 26
-25 25 26 24 24 24 20 20 20 23 23 24 25 25 26 26 26 27
-26 26 27 26 26 27 26 26 27 26 26 27 26 26 27 26 26 27
-26 26 27 26 26 27 26 26 27 26 26 27 26 26 27 26 27 27
-28 28 29 26 27 27 26 26 27 26 26 27 26 26 27 26 26 27
-26 26 27 26 26 27 26 26 27 26 26 27 26 26 27 26 26 27
-26 26 27 26 26 27 26 26 27 26 26 27 26 26 27 26 26 27
-26 26 27 26 26 27 26 26 27 26 26 27 25 25 26 21 21 22
-7 7 7 0 0 0
diff --git a/drivers/video/logo/logo_blackfin_vga16.ppm b/drivers/video/logo/logo_blackfin_vga16.ppm
deleted file mode 100644
index 1352b02a9d93..000000000000
--- a/drivers/video/logo/logo_blackfin_vga16.ppm
+++ /dev/null
@@ -1,1127 +0,0 @@
-P3
-# This was generated by the GIMP & Netpbm tools
-# gimp linux_bf.svg (create 80x80 save as linux_bf.ppm)
-# ppmquant -mapfile clut_vga16.ppm linux_bf.ppm | pnmnoraw > logo_blackfin_vga16.ppm
-#
-80 80
-255
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 85 85 85 85 85 85
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 85 85 85 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 85 85 85 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 170 170 170 170 170 170 85 85 85 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 85 85 85 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 170 170 170
-170 170 170 85 85 85 85 85 85 170 170 170 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 170 170 170 170 170 170 170 170 170 85 85 85 85 85 85
-0 0 0 0 0 0 0 0 0 85 85 85 170 170 170 255 255 255
-255 255 255 255 255 255 170 170 170 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-170 170 170 255 255 255 255 255 255 255 255 255 255 255 255 0 0 0
-0 0 0 0 0 0 0 0 0 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 170 170 170 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-255 255 255 170 170 170 85 85 85 170 170 170 255 255 255 255 255 255
-0 0 0 0 0 0 170 170 170 255 255 255 255 255 255 85 85 85
-85 85 85 170 170 170 255 255 255 255 255 255 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-255 255 255 85 85 85 170 170 170 170 170 170 255 255 255 255 255 255
-85 85 85 85 85 85 170 170 170 255 255 255 170 170 170 0 0 0
-170 170 170 170 170 170 255 255 255 255 255 255 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-255 255 255 0 0 0 85 85 85 170 170 170 170 170 170 255 255 255
-0 0 0 85 85 85 85 85 85 255 255 255 85 85 85 0 0 0
-0 0 0 85 85 85 170 170 170 255 255 255 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-255 255 255 85 85 85 0 0 0 0 0 0 255 85 85 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 85 85 85 0 0 0
-0 0 0 0 0 0 170 170 170 255 255 255 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-170 170 170 255 255 255 0 0 0 255 85 85 170 85 0 170 85 0
-255 255 85 255 255 85 170 85 0 255 255 85 255 255 85 255 255 85
-0 0 0 85 85 85 255 255 255 255 255 255 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 255 255 255 255 85 85 255 255 85 255 255 85 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 255 255 85 255 255 85
-255 255 85 255 255 85 255 255 85 170 170 170 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 255 85 85 255 255 85 255 255 85 255 255 85 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 255 255 85 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-170 85 0 255 255 85 255 255 85 255 255 85 255 255 85 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 255 255 85 255 255 85
-255 255 85 170 85 0 85 85 85 255 255 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-255 85 85 255 255 85 255 255 85 255 255 85 255 255 85 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 255 255 85 255 255 85
-170 85 0 85 85 85 255 255 85 255 255 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-170 85 0 170 85 0 255 255 85 255 255 85 255 255 85 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 170 85 0 170 85 0
-170 85 0 255 255 85 255 255 85 255 85 85 0 0 0 0 0 0
-85 85 85 85 85 85 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-85 85 85 170 85 0 170 85 0 170 85 0 255 255 85 255 255 85
-255 255 85 255 85 85 170 85 0 170 85 0 170 85 0 255 255 85
-255 255 85 255 255 85 255 85 85 170 170 170 0 0 0 0 0 0
-85 85 85 85 85 85 85 85 85 85 85 85 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-85 85 85 170 170 170 170 85 0 170 85 0 170 85 0 170 85 0
-170 85 0 170 85 0 170 85 0 255 255 85 255 255 85 255 255 85
-255 85 85 170 170 170 255 255 255 255 255 255 85 85 85 0 0 0
-0 0 0 85 85 85 85 85 85 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-170 170 170 170 170 170 170 170 170 170 85 0 255 255 85 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 255 85 85 170 170 170
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 0 0 0
-0 0 0 0 0 0 85 85 85 85 85 85 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-170 170 170 255 255 255 255 255 255 170 170 170 170 85 0 255 85 85
-255 85 85 255 85 85 255 85 85 255 85 85 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-170 170 170 255 255 255 255 255 255 170 170 170 170 170 170 170 170 170
-170 170 170 170 170 170 170 170 170 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 85 85 85 0 0 0 0 0 0
-255 255 255 255 255 255 255 255 255 255 255 255 170 170 170 170 170 170
-170 170 170 170 170 170 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-170 170 170 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 170 170 170 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 170 170 170 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-85 85 85 0 0 0 0 0 0 85 85 85 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 170 170 170 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 170 170 170 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 170 170 170 170 170 170 170 170 170 170 170 170
-170 170 170 255 255 255 255 255 255 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 85 85 85 170 170 170 170 170 170 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 170 170 170 170 170 170 170 170 170 170 170 170
-170 170 170 170 170 170 255 255 255 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 170 170 170 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 170 170 170
-170 170 170 170 170 170 170 170 170 255 255 255 0 0 0 85 85 85
-85 85 85 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 170 170 170 170 170 170 255 255 255 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 0 0 0 85 85 85
-0 0 0 170 170 170 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 170 170 170 85 85 85 170 170 170 170 170 170 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 170 170 170 170 170 170 255 255 255 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 85 85 85 0 0 0
-0 0 0 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-170 170 170 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 85 85 85 0 0 0
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 170 170 170 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 170 170 170
-0 0 0 85 85 85 0 0 0 85 85 85 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 85 85 85 85 85 85 85 85 85
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 170 170 170 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 170 170 170 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 85 85 85 0 0 0 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 170 170 170 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 170 170 170
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-170 170 170 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 170 170 170 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 170 170 170 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 170 170 170 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 170 170 170 255 255 255 255 255 255 255 255 255 255 255 255
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 85 85 85 0 0 0 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 170 170 170 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 170 170 170 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 170 170 170 255 255 255 255 255 255 255 255 255
-170 170 170 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 170 170 170 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 170 170 170 255 255 255 255 255 255
-170 170 170 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 170 170 170 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 170 170 170 255 255 255
-170 170 170 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 170 170 170 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 170 170 170
-170 170 170 0 0 0 0 0 0 85 85 85 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 0 0 0
-0 0 0 0 0 0 85 85 85 85 85 85 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 170 170 170 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 170 170 170 170 170 170 255 255 255 255 255 255
-170 170 170 0 0 0 0 0 0 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 170 85 0
-255 255 85 255 255 85 0 0 0 85 85 85 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 170 170 170 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-170 170 170 170 170 170 170 170 170 85 85 85 0 0 0 85 85 85
-85 85 85 85 85 85 0 0 0 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 255 255 85 255 255 85
-255 255 85 255 255 85 255 255 85 85 85 85 170 170 170 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 170 170 170 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 170 170 170 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 85 170 170 170
-85 85 85 170 170 170 170 170 170 85 85 85 0 0 0 0 0 0
-0 0 0 85 85 85 170 170 170 0 0 0 0 0 0 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170
-85 85 85 0 0 0 0 0 0 170 85 0 255 255 85 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 170 85 0 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 170 170 170 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 170 170 170 255 255 255 255 255 255
-255 255 255 255 255 255 170 170 170 170 170 170 170 170 170 170 170 170
-255 255 255 85 85 85 0 0 0 170 170 170 85 85 85 0 0 0
-0 0 0 0 0 0 85 85 85 0 0 0 255 255 85 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 170 170 170
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-170 170 170 0 0 0 85 85 85 255 255 85 255 255 85 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 255 255 85 0 0 0
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 85 85 85 0 0 0
-0 0 0 0 0 0 85 85 85 255 255 255 255 255 255 255 255 255
-170 170 170 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 170 170 170 85 85 85
-0 0 0 0 0 0 0 0 0 255 255 85 255 255 85 255 255 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 170 170 170
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-170 170 170 170 170 170 255 255 85 255 255 85 255 255 85 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 255 255 85 255 255 85
-0 0 0 170 170 170 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 0 0 0 0 0 0
-0 0 0 0 0 0 255 255 255 255 255 255 255 255 255 170 170 170
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 170 170 170
-85 85 85 0 0 0 255 255 85 255 255 85 255 255 85 255 255 85
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 170 170 170
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-170 170 170 170 170 170 170 170 170 170 170 170 255 255 85 255 255 85
-255 255 85 255 255 85 170 170 170 170 170 170 170 170 170 170 170 170
-255 255 85 85 85 85 170 170 170 170 170 170 170 170 170 170 170 170
-255 255 255 255 255 255 170 170 170 170 170 170 170 170 170 255 255 255
-255 255 255 170 170 170 170 170 170 255 255 255 0 0 0 0 0 0
-0 0 0 85 85 85 255 255 255 255 255 255 255 255 255 85 85 85
-85 85 85 170 170 170 170 170 170 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-170 170 170 170 170 170 255 255 85 255 255 85 255 255 85 255 255 85
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 170 170 170 85 85 85
-0 0 0 0 0 0 255 255 255 85 85 85 0 0 0 0 0 0
-255 255 255 85 85 85 0 0 0 0 0 0 255 255 255 255 255 85
-255 255 85 170 170 170 0 0 0 0 0 0 0 0 0 170 170 170
-255 255 255 170 170 170 85 85 85 0 0 0 0 0 0 0 0 0
-170 170 170 255 255 255 85 85 85 0 0 0 85 85 85 255 255 255
-85 85 85 0 0 0 85 85 85 255 255 255 0 0 0 0 0 0
-0 0 0 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 85 85 85 0 0 0 0 0 0 85 85 85
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 255 255 255 255 255 85 255 255 85 255 255 85 255 255 85
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 170 170 170 0 0 0
-0 0 0 85 85 85 255 255 255 0 0 0 0 0 0 85 85 85
-255 255 255 0 0 0 0 0 0 85 85 85 255 255 85 255 255 85
-255 255 85 85 85 85 0 0 0 0 0 0 0 0 0 170 170 170
-255 255 255 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 255 255 255 0 0 0 0 0 0 170 170 170 170 170 170
-0 0 0 0 0 0 255 255 255 255 255 255 0 0 0 85 85 85
-255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 85 85 85 0 0 0 85 85 85 255 255 255 255 255 255
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 255 255 255 255 255 85 255 255 85 255 255 85
-255 255 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 170 170 170 0 0 0
-0 0 0 85 85 85 170 170 170 0 0 0 0 0 0 170 170 170
-170 170 170 0 0 0 0 0 0 170 170 170 255 255 85 255 255 85
-255 255 255 0 0 0 85 85 85 85 85 85 0 0 0 170 170 170
-255 255 255 0 0 0 0 0 0 170 170 170 85 85 85 0 0 0
-85 85 85 170 170 170 0 0 0 0 0 0 170 170 170 0 0 0
-0 0 0 170 170 170 255 255 255 255 255 255 255 255 255 255 255 255
-255 255 255 255 255 255 255 255 255 255 255 255 170 170 170 170 170 170
-85 85 85 0 0 0 85 85 85 255 255 255 255 255 255 170 170 170
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 255 255 255 255 255 85 255 255 85
-255 255 85 170 85 0 0 0 0 85 85 85 85 85 85 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 170 170 170 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 255 255 255
-170 170 170 0 0 0 0 0 0 170 170 170 255 255 85 255 255 85
-170 170 170 0 0 0 170 170 170 85 85 85 0 0 0 255 255 255
-170 170 170 0 0 0 0 0 0 255 255 255 0 0 0 0 0 0
-170 170 170 170 170 170 0 0 0 0 0 0 85 85 85 0 0 0
-85 85 85 255 255 255 255 255 255 255 255 255 255 255 255 170 170 170
-255 255 255 255 255 255 255 255 255 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 170 170 170 255 255 255 170 170 170 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 255 255 255 255 255 85
-255 255 85 255 255 85 255 255 255 170 170 170 170 170 170 255 255 255
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 170 170 170 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 255 255 255 255 255 255
-85 85 85 0 0 0 0 0 0 255 255 255 255 255 85 255 255 85
-85 85 85 0 0 0 255 255 255 85 85 85 0 0 0 255 255 255
-85 85 85 0 0 0 85 85 85 255 255 255 85 85 85 85 85 85
-255 255 255 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-255 255 255 255 255 255 255 255 255 170 170 170 0 0 0 85 85 85
-255 255 255 255 255 255 255 255 255 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 255 255 255
-170 170 170 0 0 0 0 0 0 0 0 0 170 170 170 255 255 255
-255 255 85 255 255 85 170 170 170 170 170 170 170 170 170 170 170 170
-170 170 170 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 170 170 170 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 255 255 255
-0 0 0 0 0 0 85 85 85 255 255 85 255 255 85 255 255 255
-0 0 0 85 85 85 255 255 255 0 0 0 85 85 85 255 255 255
-85 85 85 0 0 0 170 170 170 255 255 255 170 170 170 170 170 170
-255 255 255 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-255 255 255 255 255 255 255 255 255 170 170 170 0 0 0 170 170 170
-255 255 255 255 255 255 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 170 170 170 255 255 255 255 255 255
-170 170 170 0 0 0 85 85 85 85 85 85 0 0 0 170 170 170
-255 255 85 255 255 85 255 255 255 170 170 170 170 170 170 170 170 170
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 170 170 170 0 0 0 0 0 0
-85 85 85 170 170 170 0 0 0 0 0 0 85 85 85 170 170 170
-0 0 0 0 0 0 170 170 170 255 255 85 255 255 85 170 170 170
-0 0 0 85 85 85 170 170 170 0 0 0 85 85 85 255 255 255
-0 0 0 0 0 0 170 170 170 170 170 170 170 170 170 255 255 255
-170 170 170 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-255 255 255 255 255 255 255 255 255 85 85 85 0 0 0 255 255 255
-255 255 255 255 255 255 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 255 255 255 255 255 255 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 170 170 170 255 255 255 255 255 85 255 255 255
-85 85 85 170 170 170 255 255 255 255 255 255 170 170 170 0 0 0
-170 170 170 255 255 85 255 255 85 255 255 255 170 170 170 255 255 255
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 170 170 170 0 0 0 0 0 0
-170 170 170 170 170 170 0 0 0 0 0 0 85 85 85 170 170 170
-0 0 0 0 0 0 170 170 170 255 255 85 255 255 255 85 85 85
-0 0 0 85 85 85 85 85 85 0 0 0 85 85 85 170 170 170
-0 0 0 0 0 0 255 255 255 85 85 85 0 0 0 170 170 170
-170 170 170 0 0 0 0 0 0 85 85 85 0 0 0 85 85 85
-255 255 255 255 255 255 255 255 255 85 85 85 85 85 85 255 255 255
-255 255 255 85 85 85 0 0 0 0 0 0 0 0 0 170 170 170
-170 170 170 170 170 170 255 255 255 0 0 0 0 0 0 0 0 0
-0 0 0 170 170 170 255 255 255 255 255 255 255 255 85 170 170 170
-255 255 255 255 255 255 255 255 85 255 255 85 170 170 170 0 0 0
-0 0 0 170 170 170 255 255 85 255 255 85 255 255 85 255 255 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 170 170 170 85 85 85 0 0 0 0 0 0
-255 255 255 85 85 85 0 0 0 0 0 0 170 170 170 85 85 85
-0 0 0 0 0 0 255 255 255 255 255 85 255 255 255 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 170 170 170 85 85 85
-0 0 0 85 85 85 255 255 255 0 0 0 0 0 0 255 255 255
-85 85 85 0 0 0 85 85 85 85 85 85 0 0 0 0 0 0
-255 255 255 255 255 255 255 255 255 85 85 85 170 170 170 255 255 255
-255 255 255 0 0 0 0 0 0 0 0 0 170 170 170 255 255 255
-170 170 170 85 85 85 170 170 170 0 0 0 0 0 0 85 85 85
-255 255 255 170 170 170 0 0 0 170 170 170 255 255 85 255 255 255
-255 255 255 170 170 170 255 255 255 255 255 255 85 85 85 0 0 0
-85 85 85 255 255 255 255 255 255 255 255 85 255 255 85 255 255 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 85 85 85 170 170 170 0 0 0 0 0 0 0 0 0
-85 85 85 0 0 0 0 0 0 0 0 0 255 255 255 0 0 0
-0 0 0 85 85 85 170 170 170 255 255 255 170 170 170 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 170 170 170 85 85 85
-0 0 0 85 85 85 170 170 170 0 0 0 85 85 85 255 255 255
-0 0 0 0 0 0 170 170 170 170 170 170 0 0 0 0 0 0
-255 255 255 255 255 255 255 255 255 0 0 0 255 255 255 255 255 255
-170 170 170 0 0 0 0 0 0 170 170 170 255 255 255 85 85 85
-170 170 170 170 170 170 170 170 170 0 0 0 85 85 85 255 255 255
-170 170 170 0 0 0 85 85 85 255 255 255 255 255 85 170 170 170
-0 0 0 170 170 170 255 255 255 255 255 255 0 0 0 85 85 85
-255 255 255 170 170 170 0 0 0 170 170 170 255 255 85 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 85 85 85 170 170 170 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 170 170 170 170 170 170 0 0 0
-0 0 0 0 0 0 0 0 0 170 170 170 85 85 85 0 0 0
-170 170 170 255 255 255 0 0 0 0 0 0 170 170 170 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 170 170 170 170 170 170
-0 0 0 0 0 0 255 255 255 170 170 170 0 0 0 0 0 0
-255 255 255 255 255 255 170 170 170 85 85 85 255 255 255 255 255 255
-85 85 85 0 0 0 170 170 170 170 170 170 0 0 0 0 0 0
-170 170 170 170 170 170 255 255 255 255 255 255 255 255 255 85 85 85
-0 0 0 0 0 0 255 255 255 255 255 255 170 170 170 0 0 0
-0 0 0 170 170 170 255 255 255 170 170 170 170 170 170 255 255 255
-85 85 85 0 0 0 0 0 0 0 0 0 170 170 170 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 85 85 85 170 170 170 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 170 170 170 255 255 255 170 170 170 0 0 0
-0 0 0 0 0 0 0 0 0 255 255 255 0 0 0 0 0 0
-255 255 255 255 255 255 0 0 0 0 0 0 255 255 255 170 170 170
-0 0 0 0 0 0 0 0 0 170 170 170 255 255 255 170 170 170
-0 0 0 0 0 0 255 255 255 170 170 170 0 0 0 0 0 0
-170 170 170 255 255 255 170 170 170 170 170 170 170 170 170 170 170 170
-0 0 0 85 85 85 85 85 85 0 0 0 0 0 0 0 0 0
-85 85 85 255 255 255 255 255 255 170 170 170 0 0 0 0 0 0
-0 0 0 170 170 170 255 255 255 170 170 170 0 0 0 0 0 0
-85 85 85 255 255 255 255 255 85 255 255 255 255 255 255 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 170 170 170
-85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 85 85 85 170 170 170 170 170 170 170 170 170 170 170 170
-170 170 170 170 170 170 255 255 85 255 255 85 255 255 85 170 170 170
-170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170
-255 255 85 170 170 170 170 170 170 170 170 170 255 255 85 255 255 85
-170 170 170 170 170 170 170 170 170 255 255 85 255 255 85 170 170 170
-170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170
-170 170 170 170 170 170 85 85 85 170 170 170 170 170 170 170 170 170
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 255 255 255 170 170 170 0 0 0 0 0 0 0 0 0
-0 0 0 255 255 255 255 255 255 255 255 255 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-170 170 170 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 85 85 85 255 255 85 255 255 85 255 255 85 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 255 255 85 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 255 255 85 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 255 255 85 255 255 85
-255 255 85 255 85 85 85 85 85 0 0 0 0 0 0 0 0 0
-85 85 85 170 170 170 85 85 85 170 170 170 170 170 170 85 85 85
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 170 170 170 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 170 170 170 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-85 85 85 170 170 170 85 85 85 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 85 85 85 85 85 85 170 85 0 170 85 0
-170 85 0 255 85 85 255 85 85 255 85 85 255 255 85 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 255 255 85 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 255 255 85 255 255 85
-255 85 85 170 85 0 85 85 85 0 0 0 0 0 0 85 85 85
-170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 170 170 170 170 170 170 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 85 85 85
-85 85 85 85 85 85 170 85 0 170 85 0 170 85 0 170 85 0
-255 85 85 255 85 85 255 255 85 255 255 85 255 255 85 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 255 255 85 255 85 85
-170 85 0 170 85 0 0 0 0 85 85 85 85 85 85 170 170 170
-170 170 170 0 0 0 170 170 170 255 255 255 170 170 170 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 170 170 170 170 170 170 0 0 0 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 85 85 85
-170 85 0 170 85 0 170 85 0 255 85 85 255 85 85 255 255 85
-255 255 85 255 255 85 255 255 85 255 255 85 255 85 85 170 85 0
-170 85 0 85 85 85 85 85 85 85 85 85 170 170 170 170 170 170
-0 0 0 0 0 0 255 255 255 255 255 255 85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 170 170 170 85 85 85 0 0 0
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 85 85 85 170 85 0 170 85 0 170 85 0
-170 85 0 255 85 85 255 85 85 255 85 85 170 85 0 170 85 0
-85 85 85 0 0 0 0 0 0 85 85 85 170 170 170 0 0 0
-0 0 0 85 85 85 255 255 255 170 170 170 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 170 170 170 85 85 85
-0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 85 85 85
-170 85 0 170 85 0 170 85 0 170 85 0 170 85 0 85 85 85
-0 0 0 0 0 0 0 0 0 85 85 85 170 170 170 0 0 0
-0 0 0 85 85 85 255 255 255 170 170 170 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 85 85 85 170 170 170
-85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 85 85 85 85 85 85 85 85 85 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 85 85 85 85 85 85 170 170 170
-170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170
-170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170
-170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170
-170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170
-170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170
-170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 170
-170 170 170 170 170 170 170 170 170 170 170 170 170 170 170 85 85 85
-85 85 85 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-0 0 0 0 0 0
diff --git a/drivers/video/logo/logo_m32r_clut224.ppm b/drivers/video/logo/logo_m32r_clut224.ppm
deleted file mode 100644
index 8b2983c5a0bd..000000000000
--- a/drivers/video/logo/logo_m32r_clut224.ppm
+++ /dev/null
@@ -1,1292 +0,0 @@
-P3
-# CREATOR: The GIMP's PNM Filter Version 1.0
-#
-# Note: how to convert ppm to pnm(ascii).
-# $ convert -posterize 224 m32r.ppm - | pnm2asc -f5 >logo_m32r_clut224.ppm
-#
-# convert - imagemagick: /usr/bin/convert
-# pnm2asc - pnm to ascii-pnm format converter
-# http://www.is.aist.go.jp/etlcdb/util/p2a.htm#English
-
-80 80
-255
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 43 43 43 75 75 75 27 27 27 2 2 3
- 2 2 3 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 59 59 59 123 123 123 67 67 67 27 27 27
- 2 2 3 2 2 3 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 10 6 3 59 59 59 80 80 80 43 43 43 27 27 27
- 2 2 3 2 2 3 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 19 19 19 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 2 2 3
- 2 2 3 2 2 3 10 6 3 10 6 3 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 10 6 3 11 11 11 11 11 11 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 2 2 3
- 2 2 3 2 2 3 2 2 3 27 27 27 10 6 3
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 19 19 19 2 2 3 2 2 3 51 51 51 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 2 2 3
- 2 2 3 123 123 123 196 196 196 115 115 115 2 2 3
- 2 2 3 2 2 3 2 2 3 75 75 75 141 141 140
- 172 172 172 196 196 196 190 189 188 2 2 3 11 11 11
- 2 2 3 2 2 3 2 2 3 2 2 3 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 2 2 3
- 27 27 27 164 164 164 228 228 228 221 221 220 10 6 3
- 2 2 3 2 2 3 2 2 3 172 172 172 245 245 245
- 254 254 252 254 254 252 221 221 220 35 35 35 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 2 2 3
- 164 164 164 228 228 228 35 35 35 236 236 236 236 236 236
- 2 2 3 11 11 11 2 2 3 254 254 252 245 245 245
- 2 2 3 75 75 75 245 245 245 245 245 245 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 2 2 3
- 212 212 212 2 2 3 51 51 51 11 11 11 245 245 245
- 27 27 27 80 80 80 10 6 3 254 254 252 2 2 3
- 2 2 3 91 91 91 19 19 19 254 254 252 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 2 2 3
- 196 196 196 10 6 3 2 2 3 11 11 11 107 107 107
- 49 35 5 57 42 11 31 22 3 236 236 236 2 2 3
- 2 2 3 2 2 3 2 2 3 254 254 252 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 2 2 3
- 107 107 107 221 221 220 2 2 3 64 43 7 194 148 10
- 236 188 10 225 180 10 170 126 10 236 188 10 94 86 67
- 2 2 3 2 2 3 204 204 204 236 236 236 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 2 2 3
- 2 2 3 228 228 228 182 126 10 218 164 9 236 188 10
- 236 188 10 237 204 14 236 205 40 246 214 48 246 214 48
- 245 189 11 209 156 9 196 196 196 11 11 11 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 2 2 3
- 2 2 3 165 114 10 207 148 7 229 172 9 236 180 10
- 236 196 11 237 204 14 242 218 43 246 218 75 246 218 19
- 246 213 13 246 218 19 244 205 11 218 164 9 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 2 2 3
- 164 109 5 192 133 7 224 165 9 236 180 10 236 188 10
- 236 196 11 241 212 42 246 218 75 246 218 19 246 218 19
- 246 218 19 236 196 11 150 114 10 229 172 9 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 2 2 3
- 165 114 10 201 142 7 229 172 9 242 182 11 236 188 10
- 237 204 14 245 213 67 246 218 19 246 213 13 246 213 13
- 154 119 10 207 148 7 218 164 9 216 156 8 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 2 2 3
- 2 2 3 120 78 3 225 180 10 245 189 11 236 205 40
- 241 212 42 241 212 17 237 204 14 148 107 9 182 126 10
- 216 156 8 218 164 9 207 148 7 82 70 43 2 2 3
- 2 2 3 123 123 123 35 35 35 2 2 3 2 2 3
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 2 2 3
- 10 6 3 180 180 180 156 102 5 135 88 5 142 106 7
- 126 98 11 165 114 10 185 132 9 207 148 7 215 150 13
- 199 140 8 188 148 71 196 196 196 190 189 188 2 2 3
- 2 2 3 11 11 11 132 132 132 75 75 75 2 2 3
- 2 2 3 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 2 2 3
- 10 6 3 190 189 188 190 189 188 151 97 5 192 133 7
- 207 148 7 206 142 8 199 140 8 180 121 7 180 132 31
- 190 189 188 190 189 188 212 212 212 212 212 212 107 107 107
- 2 2 3 2 2 3 99 99 99 51 51 51 2 2 3
- 2 2 3 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 2 2 3
- 2 2 3 190 189 188 190 189 188 190 189 188 136 95 7
- 151 97 5 151 97 5 151 97 5 183 156 91 190 189 188
- 190 189 188 228 228 228 254 254 252 254 254 252 221 221 220
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 10 6 3 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 2 2 3 2 2 3
- 75 75 75 245 245 245 196 196 196 190 189 188 190 189 188
- 190 189 188 196 196 196 190 189 188 190 189 188 204 204 204
- 236 236 236 254 254 252 254 254 252 254 254 252 254 254 252
- 35 35 35 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 2 2 3 27 27 27 2 2 3
- 245 245 245 254 254 252 245 245 245 190 189 188 190 189 188
- 190 189 188 190 189 188 190 189 188 212 212 212 245 245 245
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 10 6 3 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 2 2 3 2 2 3 132 132 132
- 254 254 252 254 254 252 254 254 252 236 236 236 196 196 196
- 190 189 188 204 204 204 245 245 245 245 245 245 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 80 80 80 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 2 2 3 2 2 3 2 2 3 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 245 245 245
- 245 245 245 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 2 2 3 2 2 3 2 2 3 212 212 212 245 245 245
- 254 254 252 254 254 252 254 254 252 254 254 252 245 245 245
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 2 2 3
- 2 2 3 2 2 3 2 2 3 204 204 204 245 245 245
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 245 245 245 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 245 245 245 236 236 236 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 2 2 3 2 2 3
- 2 2 3 2 2 3 11 11 11 164 164 164 212 212 212
- 236 236 236 245 245 245 254 254 252 236 236 236 221 221 220
- 221 221 220 228 228 228 245 245 245 245 245 245 245 245 245
- 236 236 236 221 221 220 212 212 212 204 204 204 204 204 204
- 196 196 196 204 204 204 59 59 59 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 2 2 3 2 2 3
- 2 2 3 2 2 3 27 27 27 172 172 172 212 212 212
- 236 236 236 254 254 252 254 254 252 254 254 252 228 228 228
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 245 245 245 221 221 220 204 204 204 196 196 196
- 196 196 196 196 196 196 228 228 228 19 19 19 2 2 3
- 80 80 80 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 2 2 3 2 2 3 2 2 3
- 11 11 11 2 2 3 164 164 164 236 236 236 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 245 245 245
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 236 236 236 212 212 212 196 196 196 245 245 245 2 2 3
- 2 2 3 11 11 11 51 51 51 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 2 2 3 2 2 3 86 86 83
- 2 2 3 27 27 27 236 236 236 254 254 252 254 254 252
- 245 245 245 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 212 212 212 196 196 196 91 91 91
- 2 2 3 2 2 3 2 2 3 11 11 11 2 2 3
- 2 2 3 2 2 3 2 2 3 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 2 2 3 2 2 3 2 2 3
- 2 2 3 245 245 245 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 245 245 245
- 254 254 252 245 245 245 254 254 252 254 254 252 254 254 252
- 254 254 252 245 245 245 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 254 254 252 221 221 220 245 245 245
- 2 2 3 11 11 11 43 43 43 19 19 19 10 6 3
- 2 2 3 2 2 3 2 2 3 2 2 3 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 2 2 3 80 80 80 2 2 3
- 2 2 3 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 245 245 245 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 43 43 43 27 27 27 80 80 80 19 19 19 80 80 80
- 2 2 3 2 2 3 2 2 3 2 2 3 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 2 2 3 2 2 3 2 2 3 2 2 3
- 245 245 245 254 254 252 254 254 252 17 11 233 254 254 252
- 254 254 252 254 254 252 254 254 252 236 236 236 17 11 233
- 17 11 233 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 245 245 245
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 11 11 11 11 11 11 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 2 2 3 67 67 67 2 2 3 19 19 19
- 254 254 252 254 254 252 245 245 245 17 11 233 245 245 245
- 254 254 252 254 254 252 17 11 233 228 228 228 17 11 233
- 17 11 233 17 11 233 17 11 233 254 254 252 17 11 233
- 17 11 233 254 254 252 254 254 252 17 11 233 17 11 233
- 17 11 233 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 2 2 3 2 2 3 2 2 3 2 2 3
- 11 11 11 2 2 3 2 2 3 2 2 3 2 2 3
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 2 2 3 10 6 3 11 11 11 2 2 3 228 228 228
- 254 254 252 254 254 252 254 254 252 17 11 233 254 254 252
- 254 254 252 17 11 233 17 11 233 17 11 233 245 245 245
- 254 254 252 254 254 252 17 11 233 17 11 233 17 11 233
- 17 11 233 17 11 233 254 254 252 17 11 233 17 11 233
- 17 11 233 17 11 233 254 254 252 254 254 252 254 254 252
- 254 254 252 2 2 3 2 2 3 2 2 3 2 2 3
- 27 27 27 2 2 3 2 2 3 2 2 3 2 2 3
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 2 2 3 2 2 3 2 2 3 2 2 3 254 254 252
- 254 254 252 254 254 252 254 254 252 17 11 233 17 11 233
- 17 11 233 17 11 233 17 11 233 17 11 233 254 254 252
- 17 11 233 17 11 233 17 11 233 254 254 252 254 254 252
- 17 11 233 17 11 233 254 254 252 17 11 233 17 11 233
- 254 254 252 17 11 233 254 254 252 254 254 252 254 254 252
- 254 254 252 2 2 3 2 2 3 2 2 3 2 2 3
- 11 11 11 2 2 3 2 2 3 2 2 3 2 2 3
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 2 2 3
- 2 2 3 19 19 19 2 2 3 2 2 3 254 254 252
- 254 254 252 254 254 252 17 11 233 245 245 245 17 11 233
- 17 11 233 245 245 245 254 254 252 17 11 233 254 254 252
- 17 11 233 17 11 233 17 11 233 254 254 252 254 254 252
- 17 11 233 17 11 233 254 254 252 17 11 233 17 11 233
- 17 11 233 17 11 233 254 254 252 254 254 252 254 254 252
- 254 254 252 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 2 2 3
- 2 2 3 19 19 19 2 2 3 19 19 19 254 254 252
- 254 254 252 245 245 245 17 11 233 254 254 252 17 11 233
- 17 11 233 254 254 252 254 254 252 17 11 233 254 254 252
- 254 254 252 254 254 252 17 11 233 17 11 233 254 254 252
- 17 11 233 17 11 233 254 254 252 17 11 233 17 11 233
- 17 11 233 17 11 233 17 11 233 254 254 252 254 254 252
- 254 254 252 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 2 2 3 2 2 3
- 2 2 3 43 43 43 2 2 3 43 43 43 254 254 252
- 245 245 245 254 254 252 17 11 233 254 254 252 17 11 233
- 254 254 252 254 254 252 254 254 252 17 11 233 17 11 233
- 17 11 233 17 11 233 17 11 233 254 254 252 17 11 233
- 17 11 233 17 11 233 17 11 233 17 11 233 17 11 233
- 245 245 245 254 254 252 17 11 233 254 254 252 254 254 252
- 245 245 245 2 2 3 2 2 3 2 2 3 11 11 11
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 2 2 3 2 2 3
- 2 2 3 75 75 75 2 2 3 99 99 99 254 254 252
- 254 254 252 254 254 252 17 11 233 254 254 252 254 254 252
- 254 254 252 254 254 252 245 245 245 228 228 228 254 254 252
- 254 254 252 17 11 233 245 245 245 254 254 252 254 254 252
- 254 254 252 254 254 252 254 254 252 17 11 233 17 11 233
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 2 2 3 2 2 3 2 2 3 75 75 75
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 2 2 3 2 2 3
- 2 2 3 2 2 3 11 11 11 107 107 107 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 245 245 245 254 254 252 245 245 245 236 236 236 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 245 245 245
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 2 2 3 2 2 3 11 11 11 19 19 19
- 11 11 11 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 2 2 3 11 11 11
- 140 102 3 11 11 11 10 6 3 67 67 67 254 254 252
- 245 245 245 245 245 245 254 254 252 254 254 252 245 245 245
- 254 254 252 254 254 252 245 245 245 228 228 228 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 245 245 245 254 254 252 254 254 252 245 245 245
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 2 2 3 43 43 43 2 2 3 2 2 3
- 2 2 3 11 11 11 67 67 67 11 11 11 2 2 3
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 185 132 9 242 182 11
- 245 189 11 245 189 11 49 35 5 2 2 3 228 228 228
- 254 254 252 254 254 252 254 254 252 245 245 245 254 254 252
- 254 254 252 254 254 252 254 254 252 228 228 228 245 245 245
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 254 254 252 245 238 222 232 189 94
- 226 186 99 43 43 43 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 59 59 59 2 2 3
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 216 156 8 236 180 22
- 245 189 11 245 189 11 245 189 11 49 35 5 11 11 11
- 212 212 212 245 245 245 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 245 245 245 228 228 228 254 254 252
- 245 245 245 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 245 245 245 254 254 252 254 254 252 229 172 9 246 218 19
- 246 218 19 41 27 3 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 19 19 19 27 27 27 196 154 14
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 199 140 8 229 172 9 242 182 11
- 245 189 11 245 189 11 245 189 11 244 196 10 2 2 3
- 2 2 3 115 115 115 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 245 245 245 228 228 228 254 254 252
- 254 254 252 245 245 245 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 254 254 252 224 165 9 245 189 11
- 236 196 11 19 19 19 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 11 11 11 236 196 11
- 244 205 11 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 182 126 10 209 156 9 215 150 13
- 193 140 10 207 148 24 216 156 8 242 182 11 245 189 11
- 245 189 11 245 189 11 245 189 11 245 189 11 209 156 9
- 2 2 3 2 2 3 43 43 43 254 254 252 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 245 245 245
- 254 254 252 254 254 252 254 254 252 254 254 252 245 245 245
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 236 236 236 216 156 8 245 189 11
- 229 172 9 64 43 7 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 207 148 7 236 188 10
- 245 189 11 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 180 121 7 216 156 8 242 182 11 236 180 10
- 229 172 9 242 182 11 242 182 11 245 189 11 245 189 11
- 245 189 11 245 189 11 245 189 11 245 189 11 237 204 14
- 170 126 10 2 2 3 2 2 3 11 11 11 236 236 236
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 204 204 204 196 196 196 216 156 8 236 180 10
- 224 165 9 182 126 10 73 48 6 2 2 3 2 2 3
- 2 2 3 41 27 3 199 140 8 229 172 9 236 180 10
- 245 189 11 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 185 132 9 229 172 9 245 189 11 245 189 11
- 245 189 11 245 189 11 245 189 11 245 189 11 245 189 11
- 245 189 11 245 189 11 245 189 11 245 189 11 245 189 11
- 226 188 11 2 2 3 2 2 3 2 2 3 11 11 11
- 245 245 245 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 245 245 245 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 196 196 196 196 196 196 215 150 13 236 180 10
- 229 172 9 201 142 7 185 132 9 180 121 7 173 120 10
- 180 121 7 192 133 7 229 172 9 242 182 11 245 189 11
- 245 189 11 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 180 126 47 224 165 9 245 189 11 245 189 11
- 245 189 11 245 189 11 245 189 11 245 189 11 245 189 11
- 245 189 11 245 189 11 245 189 11 245 189 11 245 189 11
- 236 188 10 193 140 10 2 2 3 2 2 3 2 2 3
- 2 2 3 212 212 212 254 254 252 245 245 245 245 245 245
- 254 254 252 254 254 252 254 254 252 245 245 245 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 204 204 204 196 196 196 199 140 8 229 172 9
- 236 180 10 218 164 9 215 150 13 207 148 7 207 148 7
- 216 156 8 229 172 9 245 189 11 245 189 11 245 189 11
- 245 189 11 242 182 11 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 185 132 9 216 156 8 242 182 11 245 189 11
- 245 189 11 245 189 11 245 189 11 245 189 11 245 189 11
- 245 189 11 245 189 11 245 189 11 245 189 11 245 189 11
- 245 189 11 236 196 11 19 19 19 2 2 3 2 2 3
- 2 2 3 11 11 11 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 245 245 245 254 254 252 254 254 252
- 245 245 245 221 221 220 196 196 196 185 132 9 229 172 9
- 242 182 11 229 172 9 224 165 9 218 164 9 224 165 9
- 229 172 9 236 180 10 245 189 11 245 189 11 245 189 11
- 245 189 11 236 180 22 242 182 11 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 236 180 22 245 189 11
- 245 189 11 245 189 11 245 189 11 245 189 11 245 189 11
- 245 189 11 245 189 11 245 189 11 245 189 11 245 189 11
- 245 189 11 236 188 10 225 180 10 2 2 3 2 2 3
- 2 2 3 11 11 11 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 221 221 220 19 19 19 185 132 9 224 165 9
- 245 189 11 245 189 11 242 182 11 236 180 10 236 180 10
- 242 182 11 242 182 11 245 189 11 245 189 11 245 189 11
- 245 189 11 245 189 11 245 189 11 245 189 11 196 154 14
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 207 148 7 236 180 22 245 189 11
- 245 189 11 245 189 11 245 189 11 245 189 11 245 189 11
- 245 189 11 245 189 11 245 189 11 245 189 11 242 182 11
- 245 189 11 245 189 11 237 204 14 135 88 5 2 2 3
- 27 27 27 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 245 245 245 254 254 252 254 254 252 245 245 245
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 67 67 67 19 13 3 185 132 9 229 172 9
- 242 182 11 245 189 11 245 189 11 245 189 11 245 189 11
- 245 189 11 245 189 11 245 189 11 245 189 11 245 189 11
- 245 189 11 245 189 11 245 189 11 245 189 11 245 189 11
- 236 180 22 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 245 189 11 242 182 11
- 242 182 11 245 189 11 245 189 11 245 189 11 245 189 11
- 245 189 11 245 189 11 245 189 11 245 189 11 245 189 11
- 245 189 11 245 189 11 236 188 10 226 188 11 104 83 48
- 254 254 252 254 254 252 254 254 252 254 254 252 245 245 245
- 254 254 252 254 254 252 245 245 245 254 254 252 245 245 245
- 254 254 252 245 245 245 254 254 252 254 254 252 254 254 252
- 2 2 3 2 2 3 56 38 5 185 132 9 229 172 9
- 245 189 11 245 189 11 245 189 11 245 189 11 245 189 11
- 245 189 11 245 189 11 245 189 11 245 189 11 245 189 11
- 245 189 11 245 189 11 245 189 11 245 189 11 242 182 11
- 229 172 9 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 182 126 10 215 150 13 242 182 11 245 189 11
- 245 189 11 245 189 11 245 189 11 245 189 11 245 189 11
- 245 189 11 245 189 11 245 189 11 245 189 11 245 189 11
- 245 189 11 242 182 11 245 189 11 236 196 11 216 156 8
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 254 254 252 245 245 245 2 2 3
- 2 2 3 2 2 3 75 54 3 182 126 10 229 172 9
- 242 182 11 245 189 11 245 189 11 245 189 11 245 189 11
- 245 189 11 245 189 11 245 189 11 245 189 11 245 189 11
- 245 189 11 245 189 11 245 189 11 245 189 11 229 172 9
- 207 148 24 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 192 133 7 229 172 9 242 182 11 245 189 11
- 245 189 11 245 189 11 245 189 11 245 189 11 245 189 11
- 245 189 11 245 189 11 245 189 11 245 189 11 245 189 11
- 245 189 11 245 189 11 242 182 11 225 180 10 224 165 9
- 107 69 5 245 245 245 254 254 252 254 254 252 254 254 252
- 254 254 252 254 254 252 254 254 252 254 254 252 254 254 252
- 254 254 252 236 236 236 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 91 67 9 182 126 10 229 172 9
- 245 189 11 245 189 11 245 189 11 245 189 11 245 189 11
- 245 189 11 245 189 11 245 189 11 245 189 11 245 189 11
- 245 189 11 242 182 11 242 182 11 216 156 8 180 126 47
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 206 142 8 224 165 9 245 189 11 242 182 11
- 245 189 11 245 189 11 245 189 11 245 189 11 245 189 11
- 245 189 11 245 189 11 245 189 11 245 189 11 242 182 11
- 245 189 11 245 189 11 242 182 11 242 182 11 216 156 8
- 156 102 5 19 13 3 43 43 43 196 196 196 254 254 252
- 245 245 245 254 254 252 254 254 252 204 204 204 51 51 51
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 95 62 5 185 132 9 229 172 9
- 242 182 11 245 189 11 245 189 11 245 189 11 245 189 11
- 245 189 11 245 189 11 242 182 11 245 189 11 245 189 11
- 236 180 22 216 156 8 206 142 8 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 192 133 7 215 150 13 229 172 9 229 172 9
- 236 180 10 236 180 22 242 182 11 242 182 11 245 189 11
- 245 189 11 245 189 11 245 189 11 245 189 11 245 189 11
- 245 189 11 245 189 11 245 189 11 229 172 9 216 156 8
- 156 102 5 83 54 6 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 115 73 3 185 132 9 229 172 9
- 242 182 11 245 189 11 245 189 11 245 189 11 245 189 11
- 245 189 11 242 182 11 229 172 9 229 172 9 216 156 8
- 180 121 7 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 180 121 7 182 126 10 192 133 7 199 140 8
- 207 148 7 215 150 13 216 156 8 224 165 9 229 172 9
- 236 180 22 245 189 11 242 182 11 245 189 11 242 182 11
- 245 189 11 245 189 11 242 182 11 229 172 9 199 140 8
- 151 97 5 101 67 7 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 115 73 3 180 121 7 216 156 8
- 236 180 22 242 182 11 245 189 11 245 189 11 242 182 11
- 236 180 10 224 165 9 215 150 13 206 142 8 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 156 102 5 164 109 5 172 114 5 180 121 7 180 121 7
- 192 133 7 201 142 7 216 156 8 224 165 9 236 180 22
- 245 189 11 242 182 11 229 172 9 201 142 7 172 114 5
- 125 83 5 83 54 6 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 2 2 3 2 2 3 2 2 3
- 2 2 3 2 2 3 91 58 5 156 102 5 192 133 7
- 216 156 8 229 172 9 236 180 10 236 180 10 229 172 9
- 215 150 13 199 140 8 164 109 5 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 120 78 3 132 82 3
- 151 97 5 157 106 7 180 121 7 185 132 9 193 140 10
- 207 148 7 207 148 7 192 133 7 172 114 5 132 82 3
- 101 67 7 41 27 3 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 73 48 6 143 90 3 180 121 7
- 192 133 7 207 148 7 207 148 7 201 142 7 185 132 9
- 173 120 10 136 95 7 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 91 58 5 125 83 5 135 88 5
- 144 95 7 151 97 5 132 82 3 115 73 3 95 62 5
- 64 43 7 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 64 43 7 91 58 5 151 97 5
- 157 106 7 172 114 5 172 114 5 164 109 5 151 97 5
- 85 59 6 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 73 48 6
- 91 58 5 95 62 5 95 62 5 91 58 5 56 38 5
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 83 54 6
- 107 69 5 132 82 3 125 83 5 101 67 7 71 47 31
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
- 215 150 13 215 150 13 215 150 13 215 150 13 215 150 13
diff --git a/drivers/video/of_display_timing.c b/drivers/video/of_display_timing.c
index 8ce0a99bf17c..83b8963c9657 100644
--- a/drivers/video/of_display_timing.c
+++ b/drivers/video/of_display_timing.c
@@ -244,23 +244,3 @@ dispfail:
return NULL;
}
EXPORT_SYMBOL_GPL(of_get_display_timings);
-
-/**
- * of_display_timings_exist - check if a display-timings node is provided
- * @np: device_node with the timing
- **/
-int of_display_timings_exist(const struct device_node *np)
-{
- struct device_node *timings_np;
-
- if (!np)
- return -EINVAL;
-
- timings_np = of_parse_phandle(np, "display-timings", 0);
- if (!timings_np)
- return -EINVAL;
-
- of_node_put(timings_np);
- return 1;
-}
-EXPORT_SYMBOL_GPL(of_display_timings_exist);
diff --git a/drivers/virt/vboxguest/vboxguest_core.c b/drivers/virt/vboxguest/vboxguest_core.c
index 190dbf8cfcb5..2f3856a95856 100644
--- a/drivers/virt/vboxguest/vboxguest_core.c
+++ b/drivers/virt/vboxguest/vboxguest_core.c
@@ -114,7 +114,7 @@ static void vbg_guest_mappings_init(struct vbg_dev *gdev)
}
out:
- kfree(req);
+ vbg_req_free(req, sizeof(*req));
kfree(pages);
}
@@ -144,7 +144,7 @@ static void vbg_guest_mappings_exit(struct vbg_dev *gdev)
rc = vbg_req_perform(gdev, req);
- kfree(req);
+ vbg_req_free(req, sizeof(*req));
if (rc < 0) {
vbg_err("%s error: %d\n", __func__, rc);
@@ -214,8 +214,8 @@ static int vbg_report_guest_info(struct vbg_dev *gdev)
ret = vbg_status_code_to_errno(rc);
out_free:
- kfree(req2);
- kfree(req1);
+ vbg_req_free(req2, sizeof(*req2));
+ vbg_req_free(req1, sizeof(*req1));
return ret;
}
@@ -245,7 +245,7 @@ static int vbg_report_driver_status(struct vbg_dev *gdev, bool active)
if (rc == VERR_NOT_IMPLEMENTED) /* Compatibility with older hosts. */
rc = VINF_SUCCESS;
- kfree(req);
+ vbg_req_free(req, sizeof(*req));
return vbg_status_code_to_errno(rc);
}
@@ -431,7 +431,7 @@ static int vbg_heartbeat_host_config(struct vbg_dev *gdev, bool enabled)
rc = vbg_req_perform(gdev, req);
do_div(req->interval_ns, 1000000); /* ns -> ms */
gdev->heartbeat_interval_ms = req->interval_ns;
- kfree(req);
+ vbg_req_free(req, sizeof(*req));
return vbg_status_code_to_errno(rc);
}
@@ -454,12 +454,6 @@ static int vbg_heartbeat_init(struct vbg_dev *gdev)
if (ret < 0)
return ret;
- /*
- * Preallocate the request to use it from the timer callback because:
- * 1) on Windows vbg_req_alloc must be called at IRQL <= APC_LEVEL
- * and the timer callback runs at DISPATCH_LEVEL;
- * 2) avoid repeated allocations.
- */
gdev->guest_heartbeat_req = vbg_req_alloc(
sizeof(*gdev->guest_heartbeat_req),
VMMDEVREQ_GUEST_HEARTBEAT);
@@ -481,8 +475,8 @@ static void vbg_heartbeat_exit(struct vbg_dev *gdev)
{
del_timer_sync(&gdev->heartbeat_timer);
vbg_heartbeat_host_config(gdev, false);
- kfree(gdev->guest_heartbeat_req);
-
+ vbg_req_free(gdev->guest_heartbeat_req,
+ sizeof(*gdev->guest_heartbeat_req));
}
/**
@@ -543,7 +537,7 @@ static int vbg_reset_host_event_filter(struct vbg_dev *gdev,
if (rc < 0)
vbg_err("%s error, rc: %d\n", __func__, rc);
- kfree(req);
+ vbg_req_free(req, sizeof(*req));
return vbg_status_code_to_errno(rc);
}
@@ -617,7 +611,7 @@ static int vbg_set_session_event_filter(struct vbg_dev *gdev,
out:
mutex_unlock(&gdev->session_mutex);
- kfree(req);
+ vbg_req_free(req, sizeof(*req));
return ret;
}
@@ -642,7 +636,7 @@ static int vbg_reset_host_capabilities(struct vbg_dev *gdev)
if (rc < 0)
vbg_err("%s error, rc: %d\n", __func__, rc);
- kfree(req);
+ vbg_req_free(req, sizeof(*req));
return vbg_status_code_to_errno(rc);
}
@@ -712,7 +706,7 @@ static int vbg_set_session_capabilities(struct vbg_dev *gdev,
out:
mutex_unlock(&gdev->session_mutex);
- kfree(req);
+ vbg_req_free(req, sizeof(*req));
return ret;
}
@@ -733,8 +727,10 @@ static int vbg_query_host_version(struct vbg_dev *gdev)
rc = vbg_req_perform(gdev, req);
ret = vbg_status_code_to_errno(rc);
- if (ret)
+ if (ret) {
+ vbg_err("%s error: %d\n", __func__, rc);
goto out;
+ }
snprintf(gdev->host_version, sizeof(gdev->host_version), "%u.%u.%ur%u",
req->major, req->minor, req->build, req->revision);
@@ -749,7 +745,7 @@ static int vbg_query_host_version(struct vbg_dev *gdev)
}
out:
- kfree(req);
+ vbg_req_free(req, sizeof(*req));
return ret;
}
@@ -847,11 +843,16 @@ int vbg_core_init(struct vbg_dev *gdev, u32 fixed_events)
return 0;
err_free_reqs:
- kfree(gdev->mouse_status_req);
- kfree(gdev->ack_events_req);
- kfree(gdev->cancel_req);
- kfree(gdev->mem_balloon.change_req);
- kfree(gdev->mem_balloon.get_req);
+ vbg_req_free(gdev->mouse_status_req,
+ sizeof(*gdev->mouse_status_req));
+ vbg_req_free(gdev->ack_events_req,
+ sizeof(*gdev->ack_events_req));
+ vbg_req_free(gdev->cancel_req,
+ sizeof(*gdev->cancel_req));
+ vbg_req_free(gdev->mem_balloon.change_req,
+ sizeof(*gdev->mem_balloon.change_req));
+ vbg_req_free(gdev->mem_balloon.get_req,
+ sizeof(*gdev->mem_balloon.get_req));
return ret;
}
@@ -872,11 +873,16 @@ void vbg_core_exit(struct vbg_dev *gdev)
vbg_reset_host_capabilities(gdev);
vbg_core_set_mouse_status(gdev, 0);
- kfree(gdev->mouse_status_req);
- kfree(gdev->ack_events_req);
- kfree(gdev->cancel_req);
- kfree(gdev->mem_balloon.change_req);
- kfree(gdev->mem_balloon.get_req);
+ vbg_req_free(gdev->mouse_status_req,
+ sizeof(*gdev->mouse_status_req));
+ vbg_req_free(gdev->ack_events_req,
+ sizeof(*gdev->ack_events_req));
+ vbg_req_free(gdev->cancel_req,
+ sizeof(*gdev->cancel_req));
+ vbg_req_free(gdev->mem_balloon.change_req,
+ sizeof(*gdev->mem_balloon.change_req));
+ vbg_req_free(gdev->mem_balloon.get_req,
+ sizeof(*gdev->mem_balloon.get_req));
}
/**
@@ -1415,7 +1421,7 @@ static int vbg_ioctl_write_core_dump(struct vbg_dev *gdev,
req->flags = dump->u.in.flags;
dump->hdr.rc = vbg_req_perform(gdev, req);
- kfree(req);
+ vbg_req_free(req, sizeof(*req));
return 0;
}
@@ -1513,7 +1519,7 @@ int vbg_core_set_mouse_status(struct vbg_dev *gdev, u32 features)
if (rc < 0)
vbg_err("%s error, rc: %d\n", __func__, rc);
- kfree(req);
+ vbg_req_free(req, sizeof(*req));
return vbg_status_code_to_errno(rc);
}
diff --git a/drivers/virt/vboxguest/vboxguest_core.h b/drivers/virt/vboxguest/vboxguest_core.h
index 6c784bf4fa6d..7ad9ec45bfa9 100644
--- a/drivers/virt/vboxguest/vboxguest_core.h
+++ b/drivers/virt/vboxguest/vboxguest_core.h
@@ -171,4 +171,13 @@ irqreturn_t vbg_core_isr(int irq, void *dev_id);
void vbg_linux_mouse_event(struct vbg_dev *gdev);
+/* Private (non exported) functions form vboxguest_utils.c */
+void *vbg_req_alloc(size_t len, enum vmmdev_request_type req_type);
+void vbg_req_free(void *req, size_t len);
+int vbg_req_perform(struct vbg_dev *gdev, void *req);
+int vbg_hgcm_call32(
+ struct vbg_dev *gdev, u32 client_id, u32 function, u32 timeout_ms,
+ struct vmmdev_hgcm_function_parameter32 *parm32, u32 parm_count,
+ int *vbox_status);
+
#endif
diff --git a/drivers/virt/vboxguest/vboxguest_linux.c b/drivers/virt/vboxguest/vboxguest_linux.c
index 82e280d38cc2..398d22693234 100644
--- a/drivers/virt/vboxguest/vboxguest_linux.c
+++ b/drivers/virt/vboxguest/vboxguest_linux.c
@@ -87,6 +87,7 @@ static long vbg_misc_device_ioctl(struct file *filp, unsigned int req,
struct vbg_session *session = filp->private_data;
size_t returned_size, size;
struct vbg_ioctl_hdr hdr;
+ bool is_vmmdev_req;
int ret = 0;
void *buf;
@@ -106,8 +107,17 @@ static long vbg_misc_device_ioctl(struct file *filp, unsigned int req,
if (size > SZ_16M)
return -E2BIG;
- /* __GFP_DMA32 because IOCTL_VMMDEV_REQUEST passes this to the host */
- buf = kmalloc(size, GFP_KERNEL | __GFP_DMA32);
+ /*
+ * IOCTL_VMMDEV_REQUEST needs the buffer to be below 4G to avoid
+ * the need for a bounce-buffer and another copy later on.
+ */
+ is_vmmdev_req = (req & ~IOCSIZE_MASK) == VBG_IOCTL_VMMDEV_REQUEST(0) ||
+ req == VBG_IOCTL_VMMDEV_REQUEST_BIG;
+
+ if (is_vmmdev_req)
+ buf = vbg_req_alloc(size, VBG_IOCTL_HDR_TYPE_DEFAULT);
+ else
+ buf = kmalloc(size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -132,7 +142,10 @@ static long vbg_misc_device_ioctl(struct file *filp, unsigned int req,
ret = -EFAULT;
out:
- kfree(buf);
+ if (is_vmmdev_req)
+ vbg_req_free(buf, size);
+ else
+ kfree(buf);
return ret;
}
diff --git a/drivers/virt/vboxguest/vboxguest_utils.c b/drivers/virt/vboxguest/vboxguest_utils.c
index 0f0dab8023cf..bf4474214b4d 100644
--- a/drivers/virt/vboxguest/vboxguest_utils.c
+++ b/drivers/virt/vboxguest/vboxguest_utils.c
@@ -65,8 +65,9 @@ VBG_LOG(vbg_debug, pr_debug);
void *vbg_req_alloc(size_t len, enum vmmdev_request_type req_type)
{
struct vmmdev_request_header *req;
+ int order = get_order(PAGE_ALIGN(len));
- req = kmalloc(len, GFP_KERNEL | __GFP_DMA32);
+ req = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA32, order);
if (!req)
return NULL;
@@ -82,6 +83,14 @@ void *vbg_req_alloc(size_t len, enum vmmdev_request_type req_type)
return req;
}
+void vbg_req_free(void *req, size_t len)
+{
+ if (!req)
+ return;
+
+ free_pages((unsigned long)req, get_order(PAGE_ALIGN(len)));
+}
+
/* Note this function returns a VBox status code, not a negative errno!! */
int vbg_req_perform(struct vbg_dev *gdev, void *req)
{
@@ -137,7 +146,7 @@ int vbg_hgcm_connect(struct vbg_dev *gdev,
rc = hgcm_connect->header.result;
}
- kfree(hgcm_connect);
+ vbg_req_free(hgcm_connect, sizeof(*hgcm_connect));
*vbox_status = rc;
return 0;
@@ -166,7 +175,7 @@ int vbg_hgcm_disconnect(struct vbg_dev *gdev, u32 client_id, int *vbox_status)
if (rc >= 0)
rc = hgcm_disconnect->header.result;
- kfree(hgcm_disconnect);
+ vbg_req_free(hgcm_disconnect, sizeof(*hgcm_disconnect));
*vbox_status = rc;
return 0;
@@ -623,7 +632,7 @@ int vbg_hgcm_call(struct vbg_dev *gdev, u32 client_id, u32 function,
}
if (!leak_it)
- kfree(call);
+ vbg_req_free(call, size);
free_bounce_bufs:
if (bounce_bufs) {
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index dfe5684000be..6b237e3f4983 100644
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -272,6 +272,12 @@ static unsigned int update_balloon_stats(struct virtio_balloon *vb)
pages_to_bytes(events[PSWPOUT]));
update_stat(vb, idx++, VIRTIO_BALLOON_S_MAJFLT, events[PGMAJFAULT]);
update_stat(vb, idx++, VIRTIO_BALLOON_S_MINFLT, events[PGFAULT]);
+#ifdef CONFIG_HUGETLB_PAGE
+ update_stat(vb, idx++, VIRTIO_BALLOON_S_HTLB_PGALLOC,
+ events[HTLB_BUDDY_PGALLOC]);
+ update_stat(vb, idx++, VIRTIO_BALLOON_S_HTLB_PGFAIL,
+ events[HTLB_BUDDY_PGALLOC_FAIL]);
+#endif
#endif
update_stat(vb, idx++, VIRTIO_BALLOON_S_MEMFREE,
pages_to_bytes(i.freeram));
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index 71458f493cf8..21d464a29cf8 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -23,7 +23,6 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/hrtimer.h>
-#include <linux/kmemleak.h>
#include <linux/dma-mapping.h>
#include <xen/xen.h>
diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c
index 0c2a5a8327bd..80a778b02f28 100644
--- a/drivers/w1/w1.c
+++ b/drivers/w1/w1.c
@@ -706,6 +706,7 @@ static int __w1_attach_slave_device(struct w1_slave *sl)
dev_err(&sl->dev,
"Device registration [%s] failed. err=%d\n",
dev_name(&sl->dev), err);
+ put_device(&sl->dev);
return err;
}
w1_family_notify(BUS_NOTIFY_ADD_DEVICE, sl);
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 37460cd6cabb..9af07fd92763 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -514,6 +514,17 @@ config COH901327_WATCHDOG
This watchdog is used to reset the system and thus cannot be
compiled as a module.
+config NPCM7XX_WATCHDOG
+ bool "Nuvoton NPCM750 watchdog"
+ depends on ARCH_NPCM || COMPILE_TEST
+ default y if ARCH_NPCM750
+ select WATCHDOG_CORE
+ help
+ Say Y here to include Watchdog timer support for the
+ watchdog embedded into the NPCM7xx.
+ This watchdog is used to reset the system and thus cannot be
+ compiled as a module.
+
config TWL4030_WATCHDOG
tristate "TWL4030 Watchdog"
depends on TWL4030_CORE
@@ -815,23 +826,6 @@ config SPRD_WATCHDOG
Say Y here to include watchdog timer supported
by Spreadtrum system.
-# BLACKFIN Architecture
-
-config BFIN_WDT
- tristate "Blackfin On-Chip Watchdog Timer"
- depends on BLACKFIN
- ---help---
- If you say yes here you will get support for the Blackfin On-Chip
- Watchdog Timer. If you have one of these processors and wish to
- have watchdog support enabled, say Y, otherwise say N.
-
- To compile this driver as a module, choose M here: the
- module will be called bfin_wdt.
-
-# CRIS Architecture
-
-# FRV Architecture
-
# X86 (i386 + ia64 + x86_64) Architecture
config ACQUIRE_WDT
@@ -883,7 +877,8 @@ config ALIM7101_WDT
config EBC_C384_WDT
tristate "WinSystems EBC-C384 Watchdog Timer"
- depends on X86 && ISA_BUS_API
+ depends on X86
+ select ISA_BUS_API
select WATCHDOG_CORE
help
Enables watchdog timer support for the watchdog timer on the
@@ -1118,6 +1113,7 @@ config IT87_WDT
config HP_WATCHDOG
tristate "HP ProLiant iLO2+ Hardware Watchdog Timer"
+ select WATCHDOG_CORE
depends on X86 && PCI
help
A software monitoring watchdog and NMI sourcing driver. This driver
@@ -1431,8 +1427,6 @@ config NIC7018_WDT
To compile this driver as a module, choose M here: the module will be
called nic7018_wdt.
-# M32R Architecture
-
# M68K Architecture
config M54xx_WATCHDOG
@@ -1605,7 +1599,7 @@ config BCM7038_WDT
config IMGPDC_WDT
tristate "Imagination Technologies PDC Watchdog Timer"
depends on HAS_IOMEM
- depends on METAG || MIPS || COMPILE_TEST
+ depends on MIPS || COMPILE_TEST
select WATCHDOG_CORE
help
Driver for Imagination Technologies PowerDown Controller
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 0474d38aa854..1d3c6b094fe5 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o
obj-$(CONFIG_SUNXI_WATCHDOG) += sunxi_wdt.o
obj-$(CONFIG_RN5T618_WATCHDOG) += rn5t618_wdt.o
obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o
+obj-$(CONFIG_NPCM7XX_WATCHDOG) += npcm_wdt.o
obj-$(CONFIG_STMP3XXX_RTC_WATCHDOG) += stmp3xxx_rtc_wdt.o
obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o
obj-$(CONFIG_TS4800_WATCHDOG) += ts4800_wdt.o
@@ -91,13 +92,6 @@ obj-$(CONFIG_UNIPHIER_WATCHDOG) += uniphier_wdt.o
obj-$(CONFIG_RTD119X_WATCHDOG) += rtd119x_wdt.o
obj-$(CONFIG_SPRD_WATCHDOG) += sprd_wdt.o
-# BLACKFIN Architecture
-obj-$(CONFIG_BFIN_WDT) += bfin_wdt.o
-
-# CRIS Architecture
-
-# FRV Architecture
-
# X86 (i386 + ia64 + x86_64) Architecture
obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o
obj-$(CONFIG_ADVANTECH_WDT) += advantechwdt.o
@@ -146,8 +140,6 @@ obj-$(CONFIG_INTEL_MEI_WDT) += mei_wdt.o
obj-$(CONFIG_NI903X_WDT) += ni903x_wdt.o
obj-$(CONFIG_NIC7018_WDT) += nic7018_wdt.o
-# M32R Architecture
-
# M68K Architecture
obj-$(CONFIG_M54xx_WATCHDOG) += m54xx_wdt.o
diff --git a/drivers/watchdog/ar7_wdt.c b/drivers/watchdog/ar7_wdt.c
index 6d5ae251e309..ee1ab12ab04f 100644
--- a/drivers/watchdog/ar7_wdt.c
+++ b/drivers/watchdog/ar7_wdt.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* drivers/watchdog/ar7_wdt.c
*
@@ -8,19 +9,6 @@
* National Semiconductor SCx200 Watchdog support
* Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
diff --git a/drivers/watchdog/asm9260_wdt.c b/drivers/watchdog/asm9260_wdt.c
index 7dd0da644a7f..2cf56b459d84 100644
--- a/drivers/watchdog/asm9260_wdt.c
+++ b/drivers/watchdog/asm9260_wdt.c
@@ -292,14 +292,14 @@ static int asm9260_wdt_probe(struct platform_device *pdev)
if (IS_ERR(priv->iobase))
return PTR_ERR(priv->iobase);
- ret = asm9260_wdt_get_dt_clks(priv);
- if (ret)
- return ret;
-
priv->rst = devm_reset_control_get_exclusive(&pdev->dev, "wdt_rst");
if (IS_ERR(priv->rst))
return PTR_ERR(priv->rst);
+ ret = asm9260_wdt_get_dt_clks(priv);
+ if (ret)
+ return ret;
+
wdd = &priv->wdd;
wdd->info = &asm9260_wdt_ident;
wdd->ops = &asm9260_wdt_ops;
diff --git a/drivers/watchdog/aspeed_wdt.c b/drivers/watchdog/aspeed_wdt.c
index ca5b91e2eb92..1abe4d021fd2 100644
--- a/drivers/watchdog/aspeed_wdt.c
+++ b/drivers/watchdog/aspeed_wdt.c
@@ -46,6 +46,7 @@ MODULE_DEVICE_TABLE(of, aspeed_wdt_of_table);
#define WDT_RELOAD_VALUE 0x04
#define WDT_RESTART 0x08
#define WDT_CTRL 0x0C
+#define WDT_CTRL_BOOT_SECONDARY BIT(7)
#define WDT_CTRL_RESET_MODE_SOC (0x00 << 5)
#define WDT_CTRL_RESET_MODE_FULL_CHIP (0x01 << 5)
#define WDT_CTRL_RESET_MODE_ARM_CPU (0x10 << 5)
@@ -54,6 +55,8 @@ MODULE_DEVICE_TABLE(of, aspeed_wdt_of_table);
#define WDT_CTRL_WDT_INTR BIT(2)
#define WDT_CTRL_RESET_SYSTEM BIT(1)
#define WDT_CTRL_ENABLE BIT(0)
+#define WDT_TIMEOUT_STATUS 0x10
+#define WDT_TIMEOUT_STATUS_BOOT_SECONDARY BIT(1)
/*
* WDT_RESET_WIDTH controls the characteristics of the external pulse (if
@@ -158,6 +161,7 @@ static int aspeed_wdt_restart(struct watchdog_device *wdd,
{
struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
+ wdt->ctrl &= ~WDT_CTRL_BOOT_SECONDARY;
aspeed_wdt_enable(wdt, 128 * WDT_RATE_1MHZ / 1000);
mdelay(1000);
@@ -190,6 +194,7 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
struct device_node *np;
const char *reset_type;
u32 duration;
+ u32 status;
int ret;
wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
@@ -232,16 +237,21 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
wdt->ctrl |= WDT_CTRL_RESET_MODE_SOC | WDT_CTRL_RESET_SYSTEM;
} else {
if (!strcmp(reset_type, "cpu"))
- wdt->ctrl |= WDT_CTRL_RESET_MODE_ARM_CPU;
+ wdt->ctrl |= WDT_CTRL_RESET_MODE_ARM_CPU |
+ WDT_CTRL_RESET_SYSTEM;
else if (!strcmp(reset_type, "soc"))
- wdt->ctrl |= WDT_CTRL_RESET_MODE_SOC;
+ wdt->ctrl |= WDT_CTRL_RESET_MODE_SOC |
+ WDT_CTRL_RESET_SYSTEM;
else if (!strcmp(reset_type, "system"))
- wdt->ctrl |= WDT_CTRL_RESET_SYSTEM;
+ wdt->ctrl |= WDT_CTRL_RESET_MODE_FULL_CHIP |
+ WDT_CTRL_RESET_SYSTEM;
else if (strcmp(reset_type, "none"))
return -EINVAL;
}
if (of_property_read_bool(np, "aspeed,external-signal"))
wdt->ctrl |= WDT_CTRL_WDT_EXT;
+ if (of_property_read_bool(np, "aspeed,alt-boot"))
+ wdt->ctrl |= WDT_CTRL_BOOT_SECONDARY;
if (readl(wdt->base + WDT_CTRL) & WDT_CTRL_ENABLE) {
/*
@@ -300,6 +310,10 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
writel(duration - 1, wdt->base + WDT_RESET_WIDTH);
}
+ status = readl(wdt->base + WDT_TIMEOUT_STATUS);
+ if (status & WDT_TIMEOUT_STATUS_BOOT_SECONDARY)
+ wdt->wdd.bootstatus = WDIOF_CARDRESET;
+
ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdd);
if (ret) {
dev_err(&pdev->dev, "failed to register\n");
diff --git a/drivers/watchdog/at91rm9200_wdt.c b/drivers/watchdog/at91rm9200_wdt.c
index e12a797cb820..b45fc0aee667 100644
--- a/drivers/watchdog/at91rm9200_wdt.c
+++ b/drivers/watchdog/at91rm9200_wdt.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Watchdog driver for Atmel AT91RM9200 (Thunder)
*
* Copyright (C) 2003 SAN People (Pty) Ltd
*
- * 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.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c
index 88c05d0448b2..f4050a229eb5 100644
--- a/drivers/watchdog/at91sam9_wdt.c
+++ b/drivers/watchdog/at91sam9_wdt.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Watchdog driver for Atmel AT91SAM9x processors.
*
* Copyright (C) 2008 Renaud CERRATO r.cerrato@til-technologies.fr
*
- * 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.
*/
/*
diff --git a/drivers/watchdog/at91sam9_wdt.h b/drivers/watchdog/at91sam9_wdt.h
index b79a83b467ce..390941c65eee 100644
--- a/drivers/watchdog/at91sam9_wdt.h
+++ b/drivers/watchdog/at91sam9_wdt.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* drivers/watchdog/at91sam9_wdt.h
*
@@ -7,10 +8,6 @@
* Watchdog Timer (WDT) - System peripherals regsters.
* Based on AT91SAM9261 datasheet revision D.
*
- * 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 AT91_WDT_H
diff --git a/drivers/watchdog/bcm2835_wdt.c b/drivers/watchdog/bcm2835_wdt.c
index b339e0e67b4c..ed05514cc2dc 100644
--- a/drivers/watchdog/bcm2835_wdt.c
+++ b/drivers/watchdog/bcm2835_wdt.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Watchdog driver for Broadcom BCM2835
*
@@ -7,10 +8,6 @@
*
* Copyright (C) 2013 Lubomir Rintel <lkundrak@v3.sk>
*
- * 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/delay.h>
diff --git a/drivers/watchdog/bcm47xx_wdt.c b/drivers/watchdog/bcm47xx_wdt.c
index f41b756d6dd5..05425c1dfd4c 100644
--- a/drivers/watchdog/bcm47xx_wdt.c
+++ b/drivers/watchdog/bcm47xx_wdt.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Watchdog driver for Broadcom BCM47XX
*
@@ -5,10 +6,6 @@
* Copyright (C) 2009 Matthieu CASTET <castet.matthieu@free.fr>
* Copyright (C) 2012-2013 Hauke Mehrtens <hauke@hauke-m.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 option) any later version.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index 8555afc70f9b..d3c1113e774c 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Broadcom BCM63xx SoC watchdog driver
*
* Copyright (C) 2007, Miguel Gaio <miguel.gaio@efixo.com>
* Copyright (C) 2008, Florian Fainelli <florian@openwrt.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.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
diff --git a/drivers/watchdog/bcm7038_wdt.c b/drivers/watchdog/bcm7038_wdt.c
index f88f546e8050..ce3f646e8077 100644
--- a/drivers/watchdog/bcm7038_wdt.c
+++ b/drivers/watchdog/bcm7038_wdt.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2015 Broadcom Corporation
*
- * 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.
*/
#include <linux/clk.h>
@@ -235,6 +227,6 @@ module_platform_driver(bcm7038_wdt_driver);
module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Driver for Broadcom 7038 SoCs Watchdog");
MODULE_AUTHOR("Justin Chen");
diff --git a/drivers/watchdog/bcm_kona_wdt.c b/drivers/watchdog/bcm_kona_wdt.c
index a5775dfd8d5f..1462be9e6fc5 100644
--- a/drivers/watchdog/bcm_kona_wdt.c
+++ b/drivers/watchdog/bcm_kona_wdt.c
@@ -1,14 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2013 Broadcom Corporation
*
- * 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 program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/debugfs.h>
diff --git a/drivers/watchdog/bfin_wdt.c b/drivers/watchdog/bfin_wdt.c
deleted file mode 100644
index aa4d2e8a8ef9..000000000000
--- a/drivers/watchdog/bfin_wdt.c
+++ /dev/null
@@ -1,476 +0,0 @@
-/*
- * Blackfin On-Chip Watchdog Driver
- *
- * Originally based on softdog.c
- * Copyright 2006-2010 Analog Devices Inc.
- * Copyright 2006-2007 Michele d'Amico
- * Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>
- *
- * Enter bugs at http://blackfin.uclinux.org/
- *
- * Licensed under the GPL-2 or later.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/platform_device.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/types.h>
-#include <linux/timer.h>
-#include <linux/miscdevice.h>
-#include <linux/watchdog.h>
-#include <linux/fs.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/uaccess.h>
-#include <asm/blackfin.h>
-#include <asm/bfin_watchdog.h>
-
-#define stamp(fmt, args...) \
- pr_debug("%s:%i: " fmt "\n", __func__, __LINE__, ## args)
-#define stampit() stamp("here i am")
-
-#define WATCHDOG_NAME "bfin-wdt"
-
-/* The BF561 has two watchdogs (one per core), but since Linux
- * only runs on core A, we'll just work with that one.
- */
-#ifdef BF561_FAMILY
-# define bfin_read_WDOG_CTL() bfin_read_WDOGA_CTL()
-# define bfin_read_WDOG_CNT() bfin_read_WDOGA_CNT()
-# define bfin_read_WDOG_STAT() bfin_read_WDOGA_STAT()
-# define bfin_write_WDOG_CTL(x) bfin_write_WDOGA_CTL(x)
-# define bfin_write_WDOG_CNT(x) bfin_write_WDOGA_CNT(x)
-# define bfin_write_WDOG_STAT(x) bfin_write_WDOGA_STAT(x)
-#endif
-
-/* some defaults */
-#define WATCHDOG_TIMEOUT 20
-
-static unsigned int timeout = WATCHDOG_TIMEOUT;
-static bool nowayout = WATCHDOG_NOWAYOUT;
-static const struct watchdog_info bfin_wdt_info;
-static unsigned long open_check;
-static char expect_close;
-static DEFINE_SPINLOCK(bfin_wdt_spinlock);
-
-/**
- * bfin_wdt_keepalive - Keep the Userspace Watchdog Alive
- *
- * The Userspace watchdog got a KeepAlive: schedule the next timeout.
- */
-static int bfin_wdt_keepalive(void)
-{
- stampit();
- bfin_write_WDOG_STAT(0);
- return 0;
-}
-
-/**
- * bfin_wdt_stop - Stop the Watchdog
- *
- * Stops the on-chip watchdog.
- */
-static int bfin_wdt_stop(void)
-{
- stampit();
- bfin_write_WDOG_CTL(WDEN_DISABLE);
- return 0;
-}
-
-/**
- * bfin_wdt_start - Start the Watchdog
- *
- * Starts the on-chip watchdog. Automatically loads WDOG_CNT
- * into WDOG_STAT for us.
- */
-static int bfin_wdt_start(void)
-{
- stampit();
- bfin_write_WDOG_CTL(WDEN_ENABLE | ICTL_RESET);
- return 0;
-}
-
-/**
- * bfin_wdt_running - Check Watchdog status
- *
- * See if the watchdog is running.
- */
-static int bfin_wdt_running(void)
-{
- stampit();
- return ((bfin_read_WDOG_CTL() & WDEN_MASK) != WDEN_DISABLE);
-}
-
-/**
- * bfin_wdt_set_timeout - Set the Userspace Watchdog timeout
- * @t: new timeout value (in seconds)
- *
- * Translate the specified timeout in seconds into System Clock
- * terms which is what the on-chip Watchdog requires.
- */
-static int bfin_wdt_set_timeout(unsigned long t)
-{
- u32 cnt, max_t, sclk;
- unsigned long flags;
-
- sclk = get_sclk();
- max_t = -1 / sclk;
- cnt = t * sclk;
- stamp("maxtimeout=%us newtimeout=%lus (cnt=%#x)", max_t, t, cnt);
-
- if (t > max_t) {
- pr_warn("timeout value is too large\n");
- return -EINVAL;
- }
-
- spin_lock_irqsave(&bfin_wdt_spinlock, flags);
- {
- int run = bfin_wdt_running();
- bfin_wdt_stop();
- bfin_write_WDOG_CNT(cnt);
- if (run)
- bfin_wdt_start();
- }
- spin_unlock_irqrestore(&bfin_wdt_spinlock, flags);
-
- timeout = t;
-
- return 0;
-}
-
-/**
- * bfin_wdt_open - Open the Device
- * @inode: inode of device
- * @file: file handle of device
- *
- * Watchdog device is opened and started.
- */
-static int bfin_wdt_open(struct inode *inode, struct file *file)
-{
- stampit();
-
- if (test_and_set_bit(0, &open_check))
- return -EBUSY;
-
- if (nowayout)
- __module_get(THIS_MODULE);
-
- bfin_wdt_keepalive();
- bfin_wdt_start();
-
- return nonseekable_open(inode, file);
-}
-
-/**
- * bfin_wdt_close - Close the Device
- * @inode: inode of device
- * @file: file handle of device
- *
- * Watchdog device is closed and stopped.
- */
-static int bfin_wdt_release(struct inode *inode, struct file *file)
-{
- stampit();
-
- if (expect_close == 42)
- bfin_wdt_stop();
- else {
- pr_crit("Unexpected close, not stopping watchdog!\n");
- bfin_wdt_keepalive();
- }
- expect_close = 0;
- clear_bit(0, &open_check);
- return 0;
-}
-
-/**
- * bfin_wdt_write - Write to Device
- * @file: file handle of device
- * @buf: buffer to write
- * @count: length of buffer
- * @ppos: offset
- *
- * Pings the watchdog on write.
- */
-static ssize_t bfin_wdt_write(struct file *file, const char __user *data,
- size_t len, loff_t *ppos)
-{
- stampit();
-
- if (len) {
- if (!nowayout) {
- size_t i;
-
- /* In case it was set long ago */
- expect_close = 0;
-
- for (i = 0; i != len; i++) {
- char c;
- if (get_user(c, data + i))
- return -EFAULT;
- if (c == 'V')
- expect_close = 42;
- }
- }
- bfin_wdt_keepalive();
- }
-
- return len;
-}
-
-/**
- * bfin_wdt_ioctl - Query Device
- * @file: file handle of device
- * @cmd: watchdog command
- * @arg: argument
- *
- * Query basic information from the device or ping it, as outlined by the
- * watchdog API.
- */
-static long bfin_wdt_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- void __user *argp = (void __user *)arg;
- int __user *p = argp;
-
- stampit();
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- if (copy_to_user(argp, &bfin_wdt_info, sizeof(bfin_wdt_info)))
- return -EFAULT;
- else
- return 0;
- case WDIOC_GETSTATUS:
- case WDIOC_GETBOOTSTATUS:
- return put_user(!!(_bfin_swrst & SWRST_RESET_WDOG), p);
- case WDIOC_SETOPTIONS: {
- unsigned long flags;
- int options, ret = -EINVAL;
-
- if (get_user(options, p))
- return -EFAULT;
-
- spin_lock_irqsave(&bfin_wdt_spinlock, flags);
- if (options & WDIOS_DISABLECARD) {
- bfin_wdt_stop();
- ret = 0;
- }
- if (options & WDIOS_ENABLECARD) {
- bfin_wdt_start();
- ret = 0;
- }
- spin_unlock_irqrestore(&bfin_wdt_spinlock, flags);
- return ret;
- }
- case WDIOC_KEEPALIVE:
- bfin_wdt_keepalive();
- return 0;
- case WDIOC_SETTIMEOUT: {
- int new_timeout;
-
- if (get_user(new_timeout, p))
- return -EFAULT;
- if (bfin_wdt_set_timeout(new_timeout))
- return -EINVAL;
- }
- /* Fall */
- case WDIOC_GETTIMEOUT:
- return put_user(timeout, p);
- default:
- return -ENOTTY;
- }
-}
-
-#ifdef CONFIG_PM
-static int state_before_suspend;
-
-/**
- * bfin_wdt_suspend - suspend the watchdog
- * @pdev: device being suspended
- * @state: requested suspend state
- *
- * Remember if the watchdog was running and stop it.
- * TODO: is this even right? Doesn't seem to be any
- * standard in the watchdog world ...
- */
-static int bfin_wdt_suspend(struct platform_device *pdev, pm_message_t state)
-{
- stampit();
-
- state_before_suspend = bfin_wdt_running();
- bfin_wdt_stop();
-
- return 0;
-}
-
-/**
- * bfin_wdt_resume - resume the watchdog
- * @pdev: device being resumed
- *
- * If the watchdog was running, turn it back on.
- */
-static int bfin_wdt_resume(struct platform_device *pdev)
-{
- stampit();
-
- if (state_before_suspend) {
- bfin_wdt_set_timeout(timeout);
- bfin_wdt_start();
- }
-
- return 0;
-}
-#else
-# define bfin_wdt_suspend NULL
-# define bfin_wdt_resume NULL
-#endif
-
-static const struct file_operations bfin_wdt_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .write = bfin_wdt_write,
- .unlocked_ioctl = bfin_wdt_ioctl,
- .open = bfin_wdt_open,
- .release = bfin_wdt_release,
-};
-
-static struct miscdevice bfin_wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &bfin_wdt_fops,
-};
-
-static const struct watchdog_info bfin_wdt_info = {
- .identity = "Blackfin Watchdog",
- .options = WDIOF_SETTIMEOUT |
- WDIOF_KEEPALIVEPING |
- WDIOF_MAGICCLOSE,
-};
-
-/**
- * bfin_wdt_probe - Initialize module
- *
- * Registers the misc device. Actual device
- * initialization is handled by bfin_wdt_open().
- */
-static int bfin_wdt_probe(struct platform_device *pdev)
-{
- int ret;
-
- ret = misc_register(&bfin_wdt_miscdev);
- if (ret) {
- pr_err("cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
- return ret;
- }
-
- pr_info("initialized: timeout=%d sec (nowayout=%d)\n",
- timeout, nowayout);
-
- return 0;
-}
-
-/**
- * bfin_wdt_remove - Initialize module
- *
- * Unregisters the misc device. Actual device
- * deinitialization is handled by bfin_wdt_close().
- */
-static int bfin_wdt_remove(struct platform_device *pdev)
-{
- misc_deregister(&bfin_wdt_miscdev);
- return 0;
-}
-
-/**
- * bfin_wdt_shutdown - Soft Shutdown Handler
- *
- * Handles the soft shutdown event.
- */
-static void bfin_wdt_shutdown(struct platform_device *pdev)
-{
- stampit();
-
- bfin_wdt_stop();
-}
-
-static struct platform_device *bfin_wdt_device;
-
-static struct platform_driver bfin_wdt_driver = {
- .probe = bfin_wdt_probe,
- .remove = bfin_wdt_remove,
- .shutdown = bfin_wdt_shutdown,
- .suspend = bfin_wdt_suspend,
- .resume = bfin_wdt_resume,
- .driver = {
- .name = WATCHDOG_NAME,
- },
-};
-
-/**
- * bfin_wdt_init - Initialize module
- *
- * Checks the module params and registers the platform device & driver.
- * Real work is in the platform probe function.
- */
-static int __init bfin_wdt_init(void)
-{
- int ret;
-
- stampit();
-
- /* Check that the timeout value is within range */
- if (bfin_wdt_set_timeout(timeout))
- return -EINVAL;
-
- /* Since this is an on-chip device and needs no board-specific
- * resources, we'll handle all the platform device stuff here.
- */
- ret = platform_driver_register(&bfin_wdt_driver);
- if (ret) {
- pr_err("unable to register driver\n");
- return ret;
- }
-
- bfin_wdt_device = platform_device_register_simple(WATCHDOG_NAME,
- -1, NULL, 0);
- if (IS_ERR(bfin_wdt_device)) {
- pr_err("unable to register device\n");
- platform_driver_unregister(&bfin_wdt_driver);
- return PTR_ERR(bfin_wdt_device);
- }
-
- return 0;
-}
-
-/**
- * bfin_wdt_exit - Deinitialize module
- *
- * Back out the platform device & driver steps. Real work is in the
- * platform remove function.
- */
-static void __exit bfin_wdt_exit(void)
-{
- platform_device_unregister(bfin_wdt_device);
- platform_driver_unregister(&bfin_wdt_driver);
-}
-
-module_init(bfin_wdt_init);
-module_exit(bfin_wdt_exit);
-
-MODULE_AUTHOR("Michele d'Amico, Mike Frysinger <vapier@gentoo.org>");
-MODULE_DESCRIPTION("Blackfin Watchdog Device Driver");
-MODULE_LICENSE("GPL");
-
-module_param(timeout, uint, 0);
-MODULE_PARM_DESC(timeout,
- "Watchdog timeout in seconds. (1<=timeout<=((2^32)/SCLK), default="
- __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
-
-module_param(nowayout, bool, 0);
-MODULE_PARM_DESC(nowayout,
- "Watchdog cannot be stopped once started (default="
- __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
diff --git a/drivers/watchdog/cadence_wdt.c b/drivers/watchdog/cadence_wdt.c
index 064cf7b6c1c5..3ec1f418837d 100644
--- a/drivers/watchdog/cadence_wdt.c
+++ b/drivers/watchdog/cadence_wdt.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Cadence WDT driver - Used by Xilinx Zynq
*
* Copyright (C) 2010 - 2014 Xilinx, Inc.
*
- * 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/clk.h>
diff --git a/drivers/watchdog/coh901327_wdt.c b/drivers/watchdog/coh901327_wdt.c
index 4410337f4f7f..e3a78f927f83 100644
--- a/drivers/watchdog/coh901327_wdt.c
+++ b/drivers/watchdog/coh901327_wdt.c
@@ -1,8 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* coh901327_wdt.c
*
* Copyright (C) 2008-2009 ST-Ericsson AB
- * License terms: GNU General Public License (GPL) version 2
* Watchdog driver for the ST-Ericsson AB COH 901 327 IP core
* Author: Linus Walleij <linus.walleij@stericsson.com>
*/
@@ -67,7 +67,9 @@
#define U300_WDOG_IFR_WILL_BARK_IRQ_FORCE_ENABLE 0x0001U
/* Default timeout in seconds = 1 minute */
-static unsigned int margin = 60;
+#define U300_WDOG_DEFAULT_TIMEOUT 60
+
+static unsigned int margin;
static int irq;
static void __iomem *virtbase;
static struct device *parent;
@@ -235,8 +237,9 @@ static struct watchdog_device coh901327_wdt = {
* timeout register is max
* 0x7FFF = 327670ms ~= 327s.
*/
- .min_timeout = 0,
+ .min_timeout = 1,
.max_timeout = 327,
+ .timeout = U300_WDOG_DEFAULT_TIMEOUT,
};
static int __exit coh901327_remove(struct platform_device *pdev)
@@ -315,16 +318,15 @@ static int __init coh901327_probe(struct platform_device *pdev)
goto out_no_irq;
}
- ret = watchdog_init_timeout(&coh901327_wdt, margin, dev);
- if (ret < 0)
- coh901327_wdt.timeout = 60;
+ watchdog_init_timeout(&coh901327_wdt, margin, dev);
coh901327_wdt.parent = dev;
ret = watchdog_register_device(&coh901327_wdt);
if (ret)
goto out_no_wdog;
- dev_info(dev, "initialized. timer margin=%d sec\n", margin);
+ dev_info(dev, "initialized. (timeout=%d sec)\n",
+ coh901327_wdt.timeout);
return 0;
out_no_wdog:
@@ -419,5 +421,5 @@ MODULE_DESCRIPTION("COH 901 327 Watchdog");
module_param(margin, uint, 0);
MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:coh901327-watchdog");
diff --git a/drivers/watchdog/da9052_wdt.c b/drivers/watchdog/da9052_wdt.c
index d6d5006efa71..e263bad99574 100644
--- a/drivers/watchdog/da9052_wdt.c
+++ b/drivers/watchdog/da9052_wdt.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* System monitoring driver for DA9052 PMICs.
*
@@ -5,11 +6,6 @@
*
* Author: Anthony Olech <Anthony.Olech@diasemi.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.
- *
*/
#include <linux/module.h>
diff --git a/drivers/watchdog/da9055_wdt.c b/drivers/watchdog/da9055_wdt.c
index 50bdd1022186..26a5b2984094 100644
--- a/drivers/watchdog/da9055_wdt.c
+++ b/drivers/watchdog/da9055_wdt.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* System monitoring driver for DA9055 PMICs.
*
@@ -5,11 +6,6 @@
*
* Author: David Dajun Chen <dchen@diasemi.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.
- *
*/
#include <linux/module.h>
diff --git a/drivers/watchdog/da9062_wdt.c b/drivers/watchdog/da9062_wdt.c
index 814dff6045a4..a001782bbfdb 100644
--- a/drivers/watchdog/da9062_wdt.c
+++ b/drivers/watchdog/da9062_wdt.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Watchdog device driver for DA9062 and DA9061 PMICs
* Copyright (C) 2015 Dialog Semiconductor Ltd.
*
- * 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.
*/
#include <linux/kernel.h>
diff --git a/drivers/watchdog/da9063_wdt.c b/drivers/watchdog/da9063_wdt.c
index 2a20fc163ed0..b17ac1bb1f28 100644
--- a/drivers/watchdog/da9063_wdt.c
+++ b/drivers/watchdog/da9063_wdt.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Watchdog driver for DA9063 PMICs.
*
@@ -5,10 +6,6 @@
*
* Author: Mariusz Wojtasik <mariusz.wojtasik@diasemi.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.
*/
#include <linux/kernel.h>
diff --git a/drivers/watchdog/davinci_wdt.c b/drivers/watchdog/davinci_wdt.c
index 3e4c592c239f..6c6594261cb7 100644
--- a/drivers/watchdog/davinci_wdt.c
+++ b/drivers/watchdog/davinci_wdt.c
@@ -236,15 +236,22 @@ static int davinci_wdt_probe(struct platform_device *pdev)
wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
davinci_wdt->base = devm_ioremap_resource(dev, wdt_mem);
- if (IS_ERR(davinci_wdt->base))
- return PTR_ERR(davinci_wdt->base);
+ if (IS_ERR(davinci_wdt->base)) {
+ ret = PTR_ERR(davinci_wdt->base);
+ goto err_clk_disable;
+ }
ret = watchdog_register_device(wdd);
- if (ret < 0) {
- clk_disable_unprepare(davinci_wdt->clk);
+ if (ret) {
dev_err(dev, "cannot register watchdog device\n");
+ goto err_clk_disable;
}
+ return 0;
+
+err_clk_disable:
+ clk_disable_unprepare(davinci_wdt->clk);
+
return ret;
}
diff --git a/drivers/watchdog/digicolor_wdt.c b/drivers/watchdog/digicolor_wdt.c
index 5e4ef93caa02..a9e11df155b8 100644
--- a/drivers/watchdog/digicolor_wdt.c
+++ b/drivers/watchdog/digicolor_wdt.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Watchdog driver for Conexant Digicolor
*
* Copyright (C) 2015 Paradox Innovation Ltd.
*
- * 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/types.h>
diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c
index c2f4ff516230..501aebb5b81f 100644
--- a/drivers/watchdog/dw_wdt.c
+++ b/drivers/watchdog/dw_wdt.c
@@ -34,6 +34,7 @@
#define WDOG_CONTROL_REG_OFFSET 0x00
#define WDOG_CONTROL_REG_WDT_EN_MASK 0x01
+#define WDOG_CONTROL_REG_RESP_MODE_MASK 0x02
#define WDOG_TIMEOUT_RANGE_REG_OFFSET 0x04
#define WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT 4
#define WDOG_CURRENT_COUNT_REG_OFFSET 0x08
@@ -56,6 +57,9 @@ struct dw_wdt {
unsigned long rate;
struct watchdog_device wdd;
struct reset_control *rst;
+ /* Save/restore */
+ u32 control;
+ u32 timeout;
};
#define to_dw_wdt(wdd) container_of(wdd, struct dw_wdt, wdd)
@@ -121,14 +125,23 @@ static int dw_wdt_set_timeout(struct watchdog_device *wdd, unsigned int top_s)
return 0;
}
+static void dw_wdt_arm_system_reset(struct dw_wdt *dw_wdt)
+{
+ u32 val = readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET);
+
+ /* Disable interrupt mode; always perform system reset. */
+ val &= ~WDOG_CONTROL_REG_RESP_MODE_MASK;
+ /* Enable watchdog. */
+ val |= WDOG_CONTROL_REG_WDT_EN_MASK;
+ writel(val, dw_wdt->regs + WDOG_CONTROL_REG_OFFSET);
+}
+
static int dw_wdt_start(struct watchdog_device *wdd)
{
struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
dw_wdt_set_timeout(wdd, wdd->timeout);
-
- writel(WDOG_CONTROL_REG_WDT_EN_MASK,
- dw_wdt->regs + WDOG_CONTROL_REG_OFFSET);
+ dw_wdt_arm_system_reset(dw_wdt);
return 0;
}
@@ -152,16 +165,13 @@ static int dw_wdt_restart(struct watchdog_device *wdd,
unsigned long action, void *data)
{
struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
- u32 val;
writel(0, dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET);
- val = readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET);
- if (val & WDOG_CONTROL_REG_WDT_EN_MASK)
+ if (dw_wdt_is_enabled(dw_wdt))
writel(WDOG_COUNTER_RESTART_KICK_VALUE,
dw_wdt->regs + WDOG_COUNTER_RESTART_REG_OFFSET);
else
- writel(WDOG_CONTROL_REG_WDT_EN_MASK,
- dw_wdt->regs + WDOG_CONTROL_REG_OFFSET);
+ dw_wdt_arm_system_reset(dw_wdt);
/* wait for reset to assert... */
mdelay(500);
@@ -198,6 +208,9 @@ static int dw_wdt_suspend(struct device *dev)
{
struct dw_wdt *dw_wdt = dev_get_drvdata(dev);
+ dw_wdt->control = readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET);
+ dw_wdt->timeout = readl(dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET);
+
clk_disable_unprepare(dw_wdt->clk);
return 0;
@@ -211,6 +224,9 @@ static int dw_wdt_resume(struct device *dev)
if (err)
return err;
+ writel(dw_wdt->timeout, dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET);
+ writel(dw_wdt->control, dw_wdt->regs + WDOG_CONTROL_REG_OFFSET);
+
dw_wdt_ping(&dw_wdt->wdd);
return 0;
diff --git a/drivers/watchdog/ebc-c384_wdt.c b/drivers/watchdog/ebc-c384_wdt.c
index 2170b275ea01..4c4c8ce78021 100644
--- a/drivers/watchdog/ebc-c384_wdt.c
+++ b/drivers/watchdog/ebc-c384_wdt.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Watchdog timer driver for the WinSystems EBC-C384
* Copyright (C) 2016 William Breathitt Gray
diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c
index 3a33c5344bd5..9a1c761258ce 100644
--- a/drivers/watchdog/f71808e_wdt.c
+++ b/drivers/watchdog/f71808e_wdt.c
@@ -496,7 +496,7 @@ static bool watchdog_is_running(void)
is_running = (superio_inb(watchdog.sioaddr, SIO_REG_ENABLE) & BIT(0))
&& (superio_inb(watchdog.sioaddr, F71808FG_REG_WDT_CONF)
- & F71808FG_FLAG_WD_EN);
+ & BIT(F71808FG_FLAG_WD_EN));
superio_exit(watchdog.sioaddr);
diff --git a/drivers/watchdog/gpio_wdt.c b/drivers/watchdog/gpio_wdt.c
index 3ade28190341..ea77cae03c9d 100644
--- a/drivers/watchdog/gpio_wdt.c
+++ b/drivers/watchdog/gpio_wdt.c
@@ -152,9 +152,9 @@ static int gpio_wdt_probe(struct platform_device *pdev)
priv->wdd.min_timeout = SOFT_TIMEOUT_MIN;
priv->wdd.max_hw_heartbeat_ms = hw_margin;
priv->wdd.parent = dev;
+ priv->wdd.timeout = SOFT_TIMEOUT_DEF;
- if (watchdog_init_timeout(&priv->wdd, 0, dev) < 0)
- priv->wdd.timeout = SOFT_TIMEOUT_DEF;
+ watchdog_init_timeout(&priv->wdd, 0, &pdev->dev);
watchdog_stop_on_reboot(&priv->wdd);
diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c
index b0a158073abd..a43ab2cecca2 100644
--- a/drivers/watchdog/hpwdt.c
+++ b/drivers/watchdog/hpwdt.c
@@ -4,7 +4,7 @@
*
* SoftDog 0.05: A Software Watchdog Device
*
- * (c) Copyright 2015 Hewlett Packard Enterprise Development LP
+ * (c) Copyright 2018 Hewlett Packard Enterprise Development LP
* Thomas Mingarelli <thomas.mingarelli@hpe.com>
*
* This program is free software; you can redistribute it and/or
@@ -16,34 +16,27 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/device.h>
-#include <linux/fs.h>
#include <linux/io.h>
-#include <linux/bitops.h>
#include <linux/kernel.h>
-#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/types.h>
-#include <linux/uaccess.h>
#include <linux/watchdog.h>
#include <asm/nmi.h>
-#define HPWDT_VERSION "1.4.0"
+#define HPWDT_VERSION "2.0.0"
#define SECS_TO_TICKS(secs) ((secs) * 1000 / 128)
#define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000)
#define HPWDT_MAX_TIMER TICKS_TO_SECS(65535)
#define DEFAULT_MARGIN 30
+#define PRETIMEOUT_SEC 9
+static bool ilo5;
static unsigned int soft_margin = DEFAULT_MARGIN; /* in seconds */
-static unsigned int reload; /* the computed soft_margin */
static bool nowayout = WATCHDOG_NOWAYOUT;
-#ifdef CONFIG_HPWDT_NMI_DECODING
-static unsigned int allow_kdump = 1;
-#endif
-static char expect_release;
-static unsigned long hpwdt_is_open;
+static bool pretimeout = IS_ENABLED(CONFIG_HPWDT_NMI_DECODING);
static void __iomem *pci_mem_addr; /* the PCI-memory address */
static unsigned long __iomem *hpwdt_nmistat;
@@ -61,48 +54,92 @@ MODULE_DEVICE_TABLE(pci, hpwdt_devices);
/*
* Watchdog operations
*/
-static void hpwdt_start(void)
+static int hpwdt_start(struct watchdog_device *wdd)
{
- reload = SECS_TO_TICKS(soft_margin);
+ int control = 0x81 | (pretimeout ? 0x4 : 0);
+ int reload = SECS_TO_TICKS(wdd->timeout);
+
+ dev_dbg(wdd->parent, "start watchdog 0x%08x:0x%02x\n", reload, control);
iowrite16(reload, hpwdt_timer_reg);
- iowrite8(0x85, hpwdt_timer_con);
+ iowrite8(control, hpwdt_timer_con);
+
+ return 0;
}
static void hpwdt_stop(void)
{
unsigned long data;
+ pr_debug("stop watchdog\n");
+
data = ioread8(hpwdt_timer_con);
data &= 0xFE;
iowrite8(data, hpwdt_timer_con);
}
-static void hpwdt_ping(void)
+static int hpwdt_stop_core(struct watchdog_device *wdd)
{
- iowrite16(reload, hpwdt_timer_reg);
+ hpwdt_stop();
+
+ return 0;
}
-static int hpwdt_change_timer(int new_margin)
+static int hpwdt_ping(struct watchdog_device *wdd)
{
- if (new_margin < 1 || new_margin > HPWDT_MAX_TIMER) {
- pr_warn("New value passed in is invalid: %d seconds\n",
- new_margin);
- return -EINVAL;
- }
+ int reload = SECS_TO_TICKS(wdd->timeout);
- soft_margin = new_margin;
- pr_debug("New timer passed in is %d seconds\n", new_margin);
- reload = SECS_TO_TICKS(soft_margin);
+ dev_dbg(wdd->parent, "ping watchdog 0x%08x\n", reload);
+ iowrite16(reload, hpwdt_timer_reg);
return 0;
}
-static int hpwdt_time_left(void)
+static unsigned int hpwdt_gettimeleft(struct watchdog_device *wdd)
{
return TICKS_TO_SECS(ioread16(hpwdt_timer_reg));
}
+static int hpwdt_settimeout(struct watchdog_device *wdd, unsigned int val)
+{
+ dev_dbg(wdd->parent, "set_timeout = %d\n", val);
+
+ wdd->timeout = val;
+ if (val <= wdd->pretimeout) {
+ dev_dbg(wdd->parent, "pretimeout < timeout. Setting to zero\n");
+ wdd->pretimeout = 0;
+ pretimeout = 0;
+ if (watchdog_active(wdd))
+ hpwdt_start(wdd);
+ }
+ hpwdt_ping(wdd);
+
+ return 0;
+}
+
#ifdef CONFIG_HPWDT_NMI_DECODING
+static int hpwdt_set_pretimeout(struct watchdog_device *wdd, unsigned int req)
+{
+ unsigned int val = 0;
+
+ dev_dbg(wdd->parent, "set_pretimeout = %d\n", req);
+ if (req) {
+ val = PRETIMEOUT_SEC;
+ if (val >= wdd->timeout)
+ return -EINVAL;
+ }
+
+ if (val != req)
+ dev_dbg(wdd->parent, "Rounding pretimeout to: %d\n", val);
+
+ wdd->pretimeout = val;
+ pretimeout = !!val;
+
+ if (watchdog_active(wdd))
+ hpwdt_start(wdd);
+
+ return 0;
+}
+
static int hpwdt_my_nmi(void)
{
return ioread8(hpwdt_nmistat) & 0x6;
@@ -113,178 +150,71 @@ static int hpwdt_my_nmi(void)
*/
static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs)
{
- if ((ulReason == NMI_UNKNOWN) && !hpwdt_my_nmi())
- return NMI_DONE;
-
- if (allow_kdump)
- hpwdt_stop();
-
- nmi_panic(regs, "An NMI occurred. Depending on your system the reason "
- "for the NMI is logged in any one of the following "
- "resources:\n"
+ unsigned int mynmi = hpwdt_my_nmi();
+ static char panic_msg[] =
+ "00: An NMI occurred. Depending on your system the reason "
+ "for the NMI is logged in any one of the following resources:\n"
"1. Integrated Management Log (IML)\n"
"2. OA Syslog\n"
"3. OA Forward Progress Log\n"
- "4. iLO Event Log");
-
- return NMI_HANDLED;
-}
-#endif /* CONFIG_HPWDT_NMI_DECODING */
-
-/*
- * /dev/watchdog handling
- */
-static int hpwdt_open(struct inode *inode, struct file *file)
-{
- /* /dev/watchdog can only be opened once */
- if (test_and_set_bit(0, &hpwdt_is_open))
- return -EBUSY;
+ "4. iLO Event Log";
- /* Start the watchdog */
- hpwdt_start();
- hpwdt_ping();
-
- return nonseekable_open(inode, file);
-}
+ if (ilo5 && ulReason == NMI_UNKNOWN && mynmi)
+ return NMI_DONE;
-static int hpwdt_release(struct inode *inode, struct file *file)
-{
- /* Stop the watchdog */
- if (expect_release == 42) {
- hpwdt_stop();
- } else {
- pr_crit("Unexpected close, not stopping watchdog!\n");
- hpwdt_ping();
- }
+ if (ilo5 && !pretimeout)
+ return NMI_DONE;
- expect_release = 0;
+ hpwdt_stop();
- /* /dev/watchdog is being closed, make sure it can be re-opened */
- clear_bit(0, &hpwdt_is_open);
+ hex_byte_pack(panic_msg, mynmi);
+ nmi_panic(regs, panic_msg);
- return 0;
+ return NMI_HANDLED;
}
+#endif /* CONFIG_HPWDT_NMI_DECODING */
-static ssize_t hpwdt_write(struct file *file, const char __user *data,
- size_t len, loff_t *ppos)
-{
- /* See if we got the magic character 'V' and reload the timer */
- if (len) {
- if (!nowayout) {
- size_t i;
-
- /* note: just in case someone wrote the magic character
- * five months ago... */
- expect_release = 0;
-
- /* scan to see whether or not we got the magic char. */
- for (i = 0; i != len; i++) {
- char c;
- if (get_user(c, data + i))
- return -EFAULT;
- if (c == 'V')
- expect_release = 42;
- }
- }
-
- /* someone wrote to us, we should reload the timer */
- hpwdt_ping();
- }
-
- return len;
-}
static const struct watchdog_info ident = {
- .options = WDIOF_SETTIMEOUT |
+ .options = WDIOF_PRETIMEOUT |
+ WDIOF_SETTIMEOUT |
WDIOF_KEEPALIVEPING |
WDIOF_MAGICCLOSE,
.identity = "HPE iLO2+ HW Watchdog Timer",
};
-static long hpwdt_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- void __user *argp = (void __user *)arg;
- int __user *p = argp;
- int new_margin, options;
- int ret = -ENOTTY;
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- ret = 0;
- if (copy_to_user(argp, &ident, sizeof(ident)))
- ret = -EFAULT;
- break;
-
- case WDIOC_GETSTATUS:
- case WDIOC_GETBOOTSTATUS:
- ret = put_user(0, p);
- break;
-
- case WDIOC_KEEPALIVE:
- hpwdt_ping();
- ret = 0;
- break;
-
- case WDIOC_SETOPTIONS:
- ret = get_user(options, p);
- if (ret)
- break;
-
- if (options & WDIOS_DISABLECARD)
- hpwdt_stop();
-
- if (options & WDIOS_ENABLECARD) {
- hpwdt_start();
- hpwdt_ping();
- }
- break;
-
- case WDIOC_SETTIMEOUT:
- ret = get_user(new_margin, p);
- if (ret)
- break;
-
- ret = hpwdt_change_timer(new_margin);
- if (ret)
- break;
-
- hpwdt_ping();
- /* Fall */
- case WDIOC_GETTIMEOUT:
- ret = put_user(soft_margin, p);
- break;
-
- case WDIOC_GETTIMELEFT:
- ret = put_user(hpwdt_time_left(), p);
- break;
- }
- return ret;
-}
-
/*
* Kernel interfaces
*/
-static const struct file_operations hpwdt_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .write = hpwdt_write,
- .unlocked_ioctl = hpwdt_ioctl,
- .open = hpwdt_open,
- .release = hpwdt_release,
+
+static const struct watchdog_ops hpwdt_ops = {
+ .owner = THIS_MODULE,
+ .start = hpwdt_start,
+ .stop = hpwdt_stop_core,
+ .ping = hpwdt_ping,
+ .set_timeout = hpwdt_settimeout,
+ .get_timeleft = hpwdt_gettimeleft,
+#ifdef CONFIG_HPWDT_NMI_DECODING
+ .set_pretimeout = hpwdt_set_pretimeout,
+#endif
};
-static struct miscdevice hpwdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &hpwdt_fops,
+static struct watchdog_device hpwdt_dev = {
+ .info = &ident,
+ .ops = &hpwdt_ops,
+ .min_timeout = 1,
+ .max_timeout = HPWDT_MAX_TIMER,
+ .timeout = DEFAULT_MARGIN,
+#ifdef CONFIG_HPWDT_NMI_DECODING
+ .pretimeout = PRETIMEOUT_SEC,
+#endif
};
+
/*
* Init & Exit
*/
-
static int hpwdt_init_nmi_decoding(struct pci_dev *dev)
{
#ifdef CONFIG_HPWDT_NMI_DECODING
@@ -303,9 +233,8 @@ static int hpwdt_init_nmi_decoding(struct pci_dev *dev)
goto error2;
dev_info(&dev->dev,
- "HPE Watchdog Timer Driver: NMI decoding initialized"
- ", allow kernel dump: %s (default = 1/ON)\n",
- (allow_kdump == 0) ? "OFF" : "ON");
+ "HPE Watchdog Timer Driver: NMI decoding initialized\n");
+
return 0;
error2:
@@ -375,29 +304,32 @@ static int hpwdt_init_one(struct pci_dev *dev,
/* Make sure that timer is disabled until /dev/watchdog is opened */
hpwdt_stop();
- /* Make sure that we have a valid soft_margin */
- if (hpwdt_change_timer(soft_margin))
- hpwdt_change_timer(DEFAULT_MARGIN);
-
/* Initialize NMI Decoding functionality */
retval = hpwdt_init_nmi_decoding(dev);
if (retval != 0)
goto error_init_nmi_decoding;
- retval = misc_register(&hpwdt_miscdev);
+ watchdog_set_nowayout(&hpwdt_dev, nowayout);
+ if (watchdog_init_timeout(&hpwdt_dev, soft_margin, NULL))
+ dev_warn(&dev->dev, "Invalid soft_margin: %d.\n", soft_margin);
+
+ hpwdt_dev.parent = &dev->dev;
+ retval = watchdog_register_device(&hpwdt_dev);
if (retval < 0) {
- dev_warn(&dev->dev,
- "Unable to register miscdev on minor=%d (err=%d).\n",
- WATCHDOG_MINOR, retval);
- goto error_misc_register;
+ dev_err(&dev->dev, "watchdog register failed: %d.\n", retval);
+ goto error_wd_register;
}
dev_info(&dev->dev, "HPE Watchdog Timer Driver: %s"
", timer margin: %d seconds (nowayout=%d).\n",
- HPWDT_VERSION, soft_margin, nowayout);
+ HPWDT_VERSION, hpwdt_dev.timeout, nowayout);
+
+ if (dev->subsystem_vendor == PCI_VENDOR_ID_HP_3PAR)
+ ilo5 = true;
+
return 0;
-error_misc_register:
+error_wd_register:
hpwdt_exit_nmi_decoding();
error_init_nmi_decoding:
pci_iounmap(dev, pci_mem_addr);
@@ -411,7 +343,7 @@ static void hpwdt_exit(struct pci_dev *dev)
if (!nowayout)
hpwdt_stop();
- misc_deregister(&hpwdt_miscdev);
+ watchdog_unregister_device(&hpwdt_dev);
hpwdt_exit_nmi_decoding();
pci_iounmap(dev, pci_mem_addr);
pci_disable_device(dev);
@@ -425,7 +357,7 @@ static struct pci_driver hpwdt_driver = {
};
MODULE_AUTHOR("Tom Mingarelli");
-MODULE_DESCRIPTION("hp watchdog driver");
+MODULE_DESCRIPTION("hpe watchdog driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(HPWDT_VERSION);
@@ -437,8 +369,8 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
#ifdef CONFIG_HPWDT_NMI_DECODING
-module_param(allow_kdump, int, 0);
-MODULE_PARM_DESC(allow_kdump, "Start a kernel dump after NMI occurs");
-#endif /* CONFIG_HPWDT_NMI_DECODING */
+module_param(pretimeout, bool, 0);
+MODULE_PARM_DESC(pretimeout, "Watchdog pretimeout enabled");
+#endif
module_pci_driver(hpwdt_driver);
diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c
index 518dfa1047cb..f07850d2c977 100644
--- a/drivers/watchdog/imx2_wdt.c
+++ b/drivers/watchdog/imx2_wdt.c
@@ -76,7 +76,7 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
-static unsigned timeout = IMX2_WDT_DEFAULT_TIME;
+static unsigned timeout;
module_param(timeout, uint, 0);
MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default="
__MODULE_STRING(IMX2_WDT_DEFAULT_TIME) ")");
@@ -281,6 +281,7 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
wdog->info = &imx2_wdt_info;
wdog->ops = &imx2_wdt_ops;
wdog->min_timeout = 1;
+ wdog->timeout = IMX2_WDT_DEFAULT_TIME;
wdog->max_hw_heartbeat_ms = IMX2_WDT_MAX_TIME * 1000;
wdog->parent = &pdev->dev;
@@ -299,11 +300,6 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
wdev->ext_reset = of_property_read_bool(pdev->dev.of_node,
"fsl,ext-reset-output");
- wdog->timeout = clamp_t(unsigned, timeout, 1, IMX2_WDT_MAX_TIME);
- if (wdog->timeout != timeout)
- dev_warn(&pdev->dev, "Initial timeout out of range! Clamped from %u to %u\n",
- timeout, wdog->timeout);
-
platform_set_drvdata(pdev, wdog);
watchdog_set_drvdata(wdog, wdev);
watchdog_set_nowayout(wdog, nowayout);
diff --git a/drivers/watchdog/lpc18xx_wdt.c b/drivers/watchdog/lpc18xx_wdt.c
index b4221f43cd94..331cadb459ac 100644
--- a/drivers/watchdog/lpc18xx_wdt.c
+++ b/drivers/watchdog/lpc18xx_wdt.c
@@ -265,7 +265,7 @@ static int lpc18xx_wdt_probe(struct platform_device *pdev)
lpc18xx_wdt->wdt_dev.parent = dev;
watchdog_set_drvdata(&lpc18xx_wdt->wdt_dev, lpc18xx_wdt);
- ret = watchdog_init_timeout(&lpc18xx_wdt->wdt_dev, heartbeat, dev);
+ watchdog_init_timeout(&lpc18xx_wdt->wdt_dev, heartbeat, dev);
__lpc18xx_wdt_set_timeout(lpc18xx_wdt);
diff --git a/drivers/watchdog/mei_wdt.c b/drivers/watchdog/mei_wdt.c
index b8194b02abe0..8023cf28657a 100644
--- a/drivers/watchdog/mei_wdt.c
+++ b/drivers/watchdog/mei_wdt.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Intel Management Engine Interface (Intel MEI) Linux driver
* Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
*/
#include <linux/module.h>
@@ -687,5 +679,5 @@ static struct mei_cl_driver mei_wdt_driver = {
module_mei_cl_driver(mei_wdt_driver);
MODULE_AUTHOR("Intel Corporation");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Device driver for Intel MEI iAMT watchdog");
diff --git a/drivers/watchdog/mena21_wdt.c b/drivers/watchdog/mena21_wdt.c
index 045201a6fdb3..25d5d2b8cfbe 100644
--- a/drivers/watchdog/mena21_wdt.c
+++ b/drivers/watchdog/mena21_wdt.c
@@ -1,11 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Watchdog driver for the A21 VME CPU Boards
*
* Copyright (C) 2013 MEN Mikro Elektronik Nuernberg GmbH
*
- * 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
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
diff --git a/drivers/watchdog/meson_gxbb_wdt.c b/drivers/watchdog/meson_gxbb_wdt.c
index 69a5a57f1446..69adeab3fde7 100644
--- a/drivers/watchdog/meson_gxbb_wdt.c
+++ b/drivers/watchdog/meson_gxbb_wdt.c
@@ -1,56 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
* Copyright (c) 2016 BayLibre, SAS.
* Author: Neil Armstrong <narmstrong@baylibre.com>
*
- * 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.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * BSD LICENSE
- *
- * Copyright (c) 2016 BayLibre, SAS.
- * Author: Neil Armstrong <narmstrong@baylibre.com>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "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 COPYRIGHT
- * OWNER OR CONTRIBUTORS 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.
*/
#include <linux/clk.h>
#include <linux/err.h>
diff --git a/drivers/watchdog/meson_wdt.c b/drivers/watchdog/meson_wdt.c
index 304274c67735..cd0275a6cdac 100644
--- a/drivers/watchdog/meson_wdt.c
+++ b/drivers/watchdog/meson_wdt.c
@@ -36,7 +36,7 @@
#define MESON_SEC_TO_TC(s, c) ((s) * (c))
static bool nowayout = WATCHDOG_NOWAYOUT;
-static unsigned int timeout = MESON_WDT_TIMEOUT;
+static unsigned int timeout;
struct meson_wdt_data {
unsigned int enable;
diff --git a/drivers/watchdog/mtk_wdt.c b/drivers/watchdog/mtk_wdt.c
index 7ed417a765c7..4baf64f21aa1 100644
--- a/drivers/watchdog/mtk_wdt.c
+++ b/drivers/watchdog/mtk_wdt.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Mediatek Watchdog Driver
*
@@ -5,16 +6,6 @@
*
* Matthias Brugger <matthias.bgg@gmail.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.
- *
* Based on sunxi_wdt.c
*/
@@ -57,7 +48,7 @@
#define DRV_VERSION "1.0"
static bool nowayout = WATCHDOG_NOWAYOUT;
-static unsigned int timeout = WDT_MAX_TIMEOUT;
+static unsigned int timeout;
struct mtk_wdt_dev {
struct watchdog_device wdt_dev;
diff --git a/drivers/watchdog/mtx-1_wdt.c b/drivers/watchdog/mtx-1_wdt.c
index ca360d204548..1fa7d2b32494 100644
--- a/drivers/watchdog/mtx-1_wdt.c
+++ b/drivers/watchdog/mtx-1_wdt.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Driver for the MTX-1 Watchdog.
*
@@ -6,16 +7,6 @@
* http://www.4g-systems.biz
*
* (C) Copyright 2007 OpenWrt.org, Florian Fainelli <florian@openwrt.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.
- *
- * Neither Michael Stickel nor 4G Systems admit liability nor provide
- * warranty for any of this software. This material is provided
- * "AS-IS" and at no charge.
- *
* (c) Copyright 2005 4G Systems <info@4g-systems.biz>
*
* Release 0.01.
diff --git a/drivers/watchdog/npcm_wdt.c b/drivers/watchdog/npcm_wdt.c
new file mode 100644
index 000000000000..0d4213652ecc
--- /dev/null
+++ b/drivers/watchdog/npcm_wdt.c
@@ -0,0 +1,254 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018 Nuvoton Technology corporation.
+// Copyright (c) 2018 IBM Corp.
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/watchdog.h>
+
+#define NPCM_WTCR 0x1C
+
+#define NPCM_WTCLK (BIT(10) | BIT(11)) /* Clock divider */
+#define NPCM_WTE BIT(7) /* Enable */
+#define NPCM_WTIE BIT(6) /* Enable irq */
+#define NPCM_WTIS (BIT(4) | BIT(5)) /* Interval selection */
+#define NPCM_WTIF BIT(3) /* Interrupt flag*/
+#define NPCM_WTRF BIT(2) /* Reset flag */
+#define NPCM_WTRE BIT(1) /* Reset enable */
+#define NPCM_WTR BIT(0) /* Reset counter */
+
+/*
+ * Watchdog timeouts
+ *
+ * 170 msec: WTCLK=01 WTIS=00 VAL= 0x400
+ * 670 msec: WTCLK=01 WTIS=01 VAL= 0x410
+ * 1360 msec: WTCLK=10 WTIS=00 VAL= 0x800
+ * 2700 msec: WTCLK=01 WTIS=10 VAL= 0x420
+ * 5360 msec: WTCLK=10 WTIS=01 VAL= 0x810
+ * 10700 msec: WTCLK=01 WTIS=11 VAL= 0x430
+ * 21600 msec: WTCLK=10 WTIS=10 VAL= 0x820
+ * 43000 msec: WTCLK=11 WTIS=00 VAL= 0xC00
+ * 85600 msec: WTCLK=10 WTIS=11 VAL= 0x830
+ * 172000 msec: WTCLK=11 WTIS=01 VAL= 0xC10
+ * 687000 msec: WTCLK=11 WTIS=10 VAL= 0xC20
+ * 2750000 msec: WTCLK=11 WTIS=11 VAL= 0xC30
+ */
+
+struct npcm_wdt {
+ struct watchdog_device wdd;
+ void __iomem *reg;
+};
+
+static inline struct npcm_wdt *to_npcm_wdt(struct watchdog_device *wdd)
+{
+ return container_of(wdd, struct npcm_wdt, wdd);
+}
+
+static int npcm_wdt_ping(struct watchdog_device *wdd)
+{
+ struct npcm_wdt *wdt = to_npcm_wdt(wdd);
+ u32 val;
+
+ val = readl(wdt->reg);
+ writel(val | NPCM_WTR, wdt->reg);
+
+ return 0;
+}
+
+static int npcm_wdt_start(struct watchdog_device *wdd)
+{
+ struct npcm_wdt *wdt = to_npcm_wdt(wdd);
+ u32 val;
+
+ if (wdd->timeout < 2)
+ val = 0x800;
+ else if (wdd->timeout < 3)
+ val = 0x420;
+ else if (wdd->timeout < 6)
+ val = 0x810;
+ else if (wdd->timeout < 11)
+ val = 0x430;
+ else if (wdd->timeout < 22)
+ val = 0x820;
+ else if (wdd->timeout < 44)
+ val = 0xC00;
+ else if (wdd->timeout < 87)
+ val = 0x830;
+ else if (wdd->timeout < 173)
+ val = 0xC10;
+ else if (wdd->timeout < 688)
+ val = 0xC20;
+ else
+ val = 0xC30;
+
+ val |= NPCM_WTRE | NPCM_WTE | NPCM_WTR | NPCM_WTIE;
+
+ writel(val, wdt->reg);
+
+ return 0;
+}
+
+static int npcm_wdt_stop(struct watchdog_device *wdd)
+{
+ struct npcm_wdt *wdt = to_npcm_wdt(wdd);
+
+ writel(0, wdt->reg);
+
+ return 0;
+}
+
+
+static int npcm_wdt_set_timeout(struct watchdog_device *wdd,
+ unsigned int timeout)
+{
+ if (timeout < 2)
+ wdd->timeout = 1;
+ else if (timeout < 3)
+ wdd->timeout = 2;
+ else if (timeout < 6)
+ wdd->timeout = 5;
+ else if (timeout < 11)
+ wdd->timeout = 10;
+ else if (timeout < 22)
+ wdd->timeout = 21;
+ else if (timeout < 44)
+ wdd->timeout = 43;
+ else if (timeout < 87)
+ wdd->timeout = 86;
+ else if (timeout < 173)
+ wdd->timeout = 172;
+ else if (timeout < 688)
+ wdd->timeout = 687;
+ else
+ wdd->timeout = 2750;
+
+ if (watchdog_active(wdd))
+ npcm_wdt_start(wdd);
+
+ return 0;
+}
+
+static irqreturn_t npcm_wdt_interrupt(int irq, void *data)
+{
+ struct npcm_wdt *wdt = data;
+
+ watchdog_notify_pretimeout(&wdt->wdd);
+
+ return IRQ_HANDLED;
+}
+
+static int npcm_wdt_restart(struct watchdog_device *wdd,
+ unsigned long action, void *data)
+{
+ struct npcm_wdt *wdt = to_npcm_wdt(wdd);
+
+ writel(NPCM_WTR | NPCM_WTRE | NPCM_WTE, wdt->reg);
+ udelay(1000);
+
+ return 0;
+}
+
+static bool npcm_is_running(struct watchdog_device *wdd)
+{
+ struct npcm_wdt *wdt = to_npcm_wdt(wdd);
+
+ return readl(wdt->reg) & NPCM_WTE;
+}
+
+static const struct watchdog_info npcm_wdt_info = {
+ .identity = KBUILD_MODNAME,
+ .options = WDIOF_SETTIMEOUT
+ | WDIOF_KEEPALIVEPING
+ | WDIOF_MAGICCLOSE,
+};
+
+static const struct watchdog_ops npcm_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = npcm_wdt_start,
+ .stop = npcm_wdt_stop,
+ .ping = npcm_wdt_ping,
+ .set_timeout = npcm_wdt_set_timeout,
+ .restart = npcm_wdt_restart,
+};
+
+static int npcm_wdt_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct npcm_wdt *wdt;
+ struct resource *res;
+ int irq;
+ int ret;
+
+ wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
+ if (!wdt)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ wdt->reg = devm_ioremap_resource(dev, res);
+ if (IS_ERR(wdt->reg))
+ return PTR_ERR(wdt->reg);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ wdt->wdd.info = &npcm_wdt_info;
+ wdt->wdd.ops = &npcm_wdt_ops;
+ wdt->wdd.min_timeout = 1;
+ wdt->wdd.max_timeout = 2750;
+ wdt->wdd.parent = dev;
+
+ wdt->wdd.timeout = 86;
+ watchdog_init_timeout(&wdt->wdd, 0, dev);
+
+ /* Ensure timeout is able to be represented by the hardware */
+ npcm_wdt_set_timeout(&wdt->wdd, wdt->wdd.timeout);
+
+ if (npcm_is_running(&wdt->wdd)) {
+ /* Restart with the default or device-tree specified timeout */
+ npcm_wdt_start(&wdt->wdd);
+ set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
+ }
+
+ ret = devm_request_irq(dev, irq, npcm_wdt_interrupt, 0,
+ "watchdog", wdt);
+ if (ret)
+ return ret;
+
+ ret = devm_watchdog_register_device(dev, &wdt->wdd);
+ if (ret) {
+ dev_err(dev, "failed to register watchdog\n");
+ return ret;
+ }
+
+ dev_info(dev, "NPCM watchdog driver enabled\n");
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id npcm_wdt_match[] = {
+ {.compatible = "nuvoton,npcm750-wdt"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, npcm_wdt_match);
+#endif
+
+static struct platform_driver npcm_wdt_driver = {
+ .probe = npcm_wdt_probe,
+ .driver = {
+ .name = "npcm-wdt",
+ .of_match_table = of_match_ptr(npcm_wdt_match),
+ },
+};
+module_platform_driver(npcm_wdt_driver);
+
+MODULE_AUTHOR("Joel Stanley");
+MODULE_DESCRIPTION("Watchdog driver for NPCM");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/watchdog/of_xilinx_wdt.c b/drivers/watchdog/of_xilinx_wdt.c
index 1cf286945b7a..4acbe05e27bb 100644
--- a/drivers/watchdog/of_xilinx_wdt.c
+++ b/drivers/watchdog/of_xilinx_wdt.c
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Watchdog Device Driver for Xilinx axi/xps_timebase_wdt
*
* (C) Copyright 2013 - 2014 Xilinx, Inc.
* (C) Copyright 2011 (Alejandro Cabrera <aldaya@gmail.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.
*/
#include <linux/clk.h>
@@ -323,4 +319,4 @@ module_platform_driver(xwdt_driver);
MODULE_AUTHOR("Alejandro Cabrera <aldaya@gmail.com>");
MODULE_DESCRIPTION("Xilinx Watchdog driver");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
index 1b02bfa81b29..ae77112ce97f 100644
--- a/drivers/watchdog/omap_wdt.c
+++ b/drivers/watchdog/omap_wdt.c
@@ -253,10 +253,10 @@ static int omap_wdt_probe(struct platform_device *pdev)
wdev->wdog.ops = &omap_wdt_ops;
wdev->wdog.min_timeout = TIMER_MARGIN_MIN;
wdev->wdog.max_timeout = TIMER_MARGIN_MAX;
+ wdev->wdog.timeout = TIMER_MARGIN_DEFAULT;
wdev->wdog.parent = &pdev->dev;
- if (watchdog_init_timeout(&wdev->wdog, timer_margin, &pdev->dev) < 0)
- wdev->wdog.timeout = TIMER_MARGIN_DEFAULT;
+ watchdog_init_timeout(&wdev->wdog, timer_margin, &pdev->dev);
watchdog_set_nowayout(&wdev->wdog, nowayout);
diff --git a/drivers/watchdog/pnx4008_wdt.c b/drivers/watchdog/pnx4008_wdt.c
index 0529aed158a4..8e261799c84e 100644
--- a/drivers/watchdog/pnx4008_wdt.c
+++ b/drivers/watchdog/pnx4008_wdt.c
@@ -78,7 +78,7 @@
#define WDOG_COUNTER_RATE 13000000 /*the counter clock is 13 MHz fixed */
static bool nowayout = WATCHDOG_NOWAYOUT;
-static unsigned int heartbeat = DEFAULT_HEARTBEAT;
+static unsigned int heartbeat;
static DEFINE_SPINLOCK(io_lock);
static void __iomem *wdt_base;
diff --git a/drivers/watchdog/renesas_wdt.c b/drivers/watchdog/renesas_wdt.c
index 831ef83f6de1..514db5cc1595 100644
--- a/drivers/watchdog/renesas_wdt.c
+++ b/drivers/watchdog/renesas_wdt.c
@@ -16,6 +16,8 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/smp.h>
+#include <linux/sys_soc.h>
#include <linux/watchdog.h>
#define RWTCNT 0
@@ -49,6 +51,7 @@ struct rwdt_priv {
void __iomem *base;
struct watchdog_device wdev;
unsigned long clk_rate;
+ u16 time_left;
u8 cks;
};
@@ -107,8 +110,19 @@ static unsigned int rwdt_get_timeleft(struct watchdog_device *wdev)
return DIV_BY_CLKS_PER_SEC(priv, 65536 - val);
}
+static int rwdt_restart(struct watchdog_device *wdev, unsigned long action,
+ void *data)
+{
+ struct rwdt_priv *priv = watchdog_get_drvdata(wdev);
+
+ rwdt_start(wdev);
+ rwdt_write(priv, 0xffff, RWTCNT);
+ return 0;
+}
+
static const struct watchdog_info rwdt_ident = {
- .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
+ .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
+ WDIOF_CARDRESET,
.identity = "Renesas WDT Watchdog",
};
@@ -118,8 +132,47 @@ static const struct watchdog_ops rwdt_ops = {
.stop = rwdt_stop,
.ping = rwdt_init_timeout,
.get_timeleft = rwdt_get_timeleft,
+ .restart = rwdt_restart,
+};
+
+#if defined(CONFIG_ARCH_RCAR_GEN2) && defined(CONFIG_SMP)
+/*
+ * Watchdog-reset integration is broken on early revisions of R-Car Gen2 SoCs
+ */
+static const struct soc_device_attribute rwdt_quirks_match[] = {
+ {
+ .soc_id = "r8a7790",
+ .revision = "ES1.*",
+ .data = (void *)1, /* needs single CPU */
+ }, {
+ .soc_id = "r8a7791",
+ .revision = "ES[12].*",
+ .data = (void *)1, /* needs single CPU */
+ }, {
+ .soc_id = "r8a7792",
+ .revision = "*",
+ .data = (void *)0, /* needs SMP disabled */
+ },
+ { /* sentinel */ }
};
+static bool rwdt_blacklisted(struct device *dev)
+{
+ const struct soc_device_attribute *attr;
+
+ attr = soc_device_match(rwdt_quirks_match);
+ if (attr && setup_max_cpus > (uintptr_t)attr->data) {
+ dev_info(dev, "Watchdog blacklisted on %s %s\n", attr->soc_id,
+ attr->revision);
+ return true;
+ }
+
+ return false;
+}
+#else /* !CONFIG_ARCH_RCAR_GEN2 || !CONFIG_SMP */
+static inline bool rwdt_blacklisted(struct device *dev) { return false; }
+#endif /* !CONFIG_ARCH_RCAR_GEN2 || !CONFIG_SMP */
+
static int rwdt_probe(struct platform_device *pdev)
{
struct rwdt_priv *priv;
@@ -128,6 +181,9 @@ static int rwdt_probe(struct platform_device *pdev)
unsigned long clks_per_sec;
int ret, i;
+ if (rwdt_blacklisted(&pdev->dev))
+ return -ENODEV;
+
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
@@ -142,9 +198,10 @@ static int rwdt_probe(struct platform_device *pdev)
return PTR_ERR(clk);
pm_runtime_enable(&pdev->dev);
-
pm_runtime_get_sync(&pdev->dev);
priv->clk_rate = clk_get_rate(clk);
+ priv->wdev.bootstatus = (readb_relaxed(priv->base + RWTCSRA) &
+ RWTCSRA_WOVF) ? WDIOF_CARDRESET : 0;
pm_runtime_put(&pdev->dev);
if (!priv->clk_rate) {
@@ -176,6 +233,7 @@ static int rwdt_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, priv);
watchdog_set_drvdata(&priv->wdev, priv);
watchdog_set_nowayout(&priv->wdev, nowayout);
+ watchdog_set_restart_priority(&priv->wdev, 0);
/* This overrides the default timeout only if DT configuration was found */
ret = watchdog_init_timeout(&priv->wdev, 0, &pdev->dev);
@@ -203,12 +261,32 @@ static int rwdt_remove(struct platform_device *pdev)
return 0;
}
-/*
- * This driver does also fit for R-Car Gen2 (r8a779[0-4]) WDT. However, for SMP
- * to work there, one also needs a RESET (RST) driver which does not exist yet
- * due to HW issues. This needs to be solved before adding compatibles here.
- */
+static int __maybe_unused rwdt_suspend(struct device *dev)
+{
+ struct rwdt_priv *priv = dev_get_drvdata(dev);
+
+ if (watchdog_active(&priv->wdev)) {
+ priv->time_left = readw(priv->base + RWTCNT);
+ rwdt_stop(&priv->wdev);
+ }
+ return 0;
+}
+
+static int __maybe_unused rwdt_resume(struct device *dev)
+{
+ struct rwdt_priv *priv = dev_get_drvdata(dev);
+
+ if (watchdog_active(&priv->wdev)) {
+ rwdt_start(&priv->wdev);
+ rwdt_write(priv, priv->time_left, RWTCNT);
+ }
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(rwdt_pm_ops, rwdt_suspend, rwdt_resume);
+
static const struct of_device_id rwdt_ids[] = {
+ { .compatible = "renesas,rcar-gen2-wdt", },
{ .compatible = "renesas,rcar-gen3-wdt", },
{ /* sentinel */ }
};
@@ -218,6 +296,7 @@ static struct platform_driver rwdt_driver = {
.driver = {
.name = "renesas_wdt",
.of_match_table = rwdt_ids,
+ .pm = &rwdt_pm_ops,
},
.probe = rwdt_probe,
.remove = rwdt_remove,
diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c
index 0ae947c3d7bc..255169916dbb 100644
--- a/drivers/watchdog/sama5d4_wdt.c
+++ b/drivers/watchdog/sama5d4_wdt.c
@@ -33,7 +33,7 @@ struct sama5d4_wdt {
unsigned long last_ping;
};
-static int wdt_timeout = WDT_DEFAULT_TIMEOUT;
+static int wdt_timeout;
static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(wdt_timeout, int, 0);
@@ -212,7 +212,7 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
return -ENOMEM;
wdd = &wdt->wdd;
- wdd->timeout = wdt_timeout;
+ wdd->timeout = WDT_DEFAULT_TIMEOUT;
wdd->info = &sama5d4_wdt_info;
wdd->ops = &sama5d4_wdt_ops;
wdd->min_timeout = MIN_WDT_TIMEOUT;
@@ -273,7 +273,7 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, wdt);
dev_info(&pdev->dev, "initialized (timeout = %d sec, nowayout = %d)\n",
- wdt_timeout, nowayout);
+ wdd->timeout, nowayout);
return 0;
}
diff --git a/drivers/watchdog/sch311x_wdt.c b/drivers/watchdog/sch311x_wdt.c
index 43d0cbb7ba0b..814cdf539b0f 100644
--- a/drivers/watchdog/sch311x_wdt.c
+++ b/drivers/watchdog/sch311x_wdt.c
@@ -299,7 +299,7 @@ static long sch311x_wdt_ioctl(struct file *file, unsigned int cmd,
if (sch311x_wdt_set_heartbeat(new_timeout))
return -EINVAL;
sch311x_wdt_keepalive();
- /* Fall */
+ /* Fall through */
case WDIOC_GETTIMEOUT:
return put_user(timeout, p);
default:
diff --git a/drivers/watchdog/sirfsoc_wdt.c b/drivers/watchdog/sirfsoc_wdt.c
index 4eea351e09b0..ac0c9d2c4aee 100644
--- a/drivers/watchdog/sirfsoc_wdt.c
+++ b/drivers/watchdog/sirfsoc_wdt.c
@@ -29,7 +29,7 @@
#define SIRFSOC_WDT_MAX_TIMEOUT (10 * 60) /* 10 mins */
#define SIRFSOC_WDT_DEFAULT_TIMEOUT 30 /* 30 secs */
-static unsigned int timeout = SIRFSOC_WDT_DEFAULT_TIMEOUT;
+static unsigned int timeout;
static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(timeout, uint, 0);
diff --git a/drivers/watchdog/sprd_wdt.c b/drivers/watchdog/sprd_wdt.c
index a8b280ff33e0..b4d484a42b70 100644
--- a/drivers/watchdog/sprd_wdt.c
+++ b/drivers/watchdog/sprd_wdt.c
@@ -154,8 +154,10 @@ static int sprd_wdt_enable(struct sprd_wdt *wdt)
if (ret)
return ret;
ret = clk_prepare_enable(wdt->rtc_enable);
- if (ret)
+ if (ret) {
+ clk_disable_unprepare(wdt->enable);
return ret;
+ }
sprd_wdt_unlock(wdt->base);
val = readl_relaxed(wdt->base + SPRD_WDT_CTRL);
diff --git a/drivers/watchdog/st_lpc_wdt.c b/drivers/watchdog/st_lpc_wdt.c
index e6100e447dd8..177829b379da 100644
--- a/drivers/watchdog/st_lpc_wdt.c
+++ b/drivers/watchdog/st_lpc_wdt.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* ST's LPC Watchdog
*
@@ -5,11 +6,6 @@
*
* Author: David Paris <david.paris@st.com> for STMicroelectronics
* Lee Jones <lee.jones@linaro.org> for STMicroelectronics
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public Licence
- * as published by the Free Software Foundation; either version
- * 2 of the Licence, or (at your option) any later version.
*/
#include <linux/clk.h>
diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c
index 802e31b1416d..c6c73656997e 100644
--- a/drivers/watchdog/sunxi_wdt.c
+++ b/drivers/watchdog/sunxi_wdt.c
@@ -39,7 +39,7 @@
#define DRV_VERSION "1.0"
static bool nowayout = WATCHDOG_NOWAYOUT;
-static unsigned int timeout = WDT_MAX_TIMEOUT;
+static unsigned int timeout;
/*
* This structure stores the register offsets for different variants
diff --git a/drivers/watchdog/tangox_wdt.c b/drivers/watchdog/tangox_wdt.c
index d5fcce062920..b1de8297fa40 100644
--- a/drivers/watchdog/tangox_wdt.c
+++ b/drivers/watchdog/tangox_wdt.c
@@ -1,11 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2015 Mans Rullgard <mans@mansr.com>
* SMP86xx/SMP87xx Watchdog driver
- *
- * 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/bitops.h>
diff --git a/drivers/watchdog/tegra_wdt.c b/drivers/watchdog/tegra_wdt.c
index 9403c08816e3..877dd39bd41f 100644
--- a/drivers/watchdog/tegra_wdt.c
+++ b/drivers/watchdog/tegra_wdt.c
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions 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.
*/
#include <linux/kernel.h>
diff --git a/drivers/watchdog/uniphier_wdt.c b/drivers/watchdog/uniphier_wdt.c
index 0ea2339d9702..e20a7a459d69 100644
--- a/drivers/watchdog/uniphier_wdt.c
+++ b/drivers/watchdog/uniphier_wdt.c
@@ -1,18 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Watchdog driver for the UniPhier watchdog timer
*
* (c) Copyright 2014 Panasonic Corporation
* (c) Copyright 2016 Socionext Inc.
* All rights reserved.
- *
- * 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.
- *
- * 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.
*/
#include <linux/bitops.h>
@@ -212,11 +204,10 @@ static int uniphier_wdt_probe(struct platform_device *pdev)
wdev->wdt_dev.ops = &uniphier_wdt_ops;
wdev->wdt_dev.max_timeout = WDT_PERIOD_MAX;
wdev->wdt_dev.min_timeout = WDT_PERIOD_MIN;
+ wdev->wdt_dev.timeout = WDT_DEFAULT_TIMEOUT;
wdev->wdt_dev.parent = dev;
- if (watchdog_init_timeout(&wdev->wdt_dev, timeout, dev) < 0) {
- wdev->wdt_dev.timeout = WDT_DEFAULT_TIMEOUT;
- }
+ watchdog_init_timeout(&wdev->wdt_dev, timeout, dev);
watchdog_set_nowayout(&wdev->wdt_dev, nowayout);
watchdog_stop_on_reboot(&wdev->wdt_dev);
diff --git a/drivers/watchdog/w83977f_wdt.c b/drivers/watchdog/w83977f_wdt.c
index 20e2bba10400..672b61a7f9a3 100644
--- a/drivers/watchdog/w83977f_wdt.c
+++ b/drivers/watchdog/w83977f_wdt.c
@@ -427,7 +427,7 @@ static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
return -EINVAL;
wdt_keepalive();
- /* Fall */
+ /* Fall through */
case WDIOC_GETTIMEOUT:
return put_user(timeout, uarg.i);
diff --git a/drivers/watchdog/wafer5823wdt.c b/drivers/watchdog/wafer5823wdt.c
index db0da7ea4fd8..93c5b610e264 100644
--- a/drivers/watchdog/wafer5823wdt.c
+++ b/drivers/watchdog/wafer5823wdt.c
@@ -178,7 +178,7 @@ static long wafwdt_ioctl(struct file *file, unsigned int cmd,
timeout = new_timeout;
wafwdt_stop();
wafwdt_start();
- /* Fall */
+ /* Fall through */
case WDIOC_GETTIMEOUT:
return put_user(timeout, p);
diff --git a/drivers/watchdog/wm831x_wdt.c b/drivers/watchdog/wm831x_wdt.c
index 1ddc1f742cd4..116c2f47b463 100644
--- a/drivers/watchdog/wm831x_wdt.c
+++ b/drivers/watchdog/wm831x_wdt.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Watchdog driver for the wm831x PMICs
*
* Copyright (C) 2009 Wolfson Microelectronics
- *
- * 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
*/
#include <linux/module.h>
diff --git a/drivers/watchdog/wm8350_wdt.c b/drivers/watchdog/wm8350_wdt.c
index 4ab4b8347d45..33c62d51f00a 100644
--- a/drivers/watchdog/wm8350_wdt.c
+++ b/drivers/watchdog/wm8350_wdt.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Watchdog driver for the wm8350
*
* Copyright (C) 2007, 2008 Wolfson Microelectronics <linux@wolfsonmicro.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
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c
index 5bb72d3f8337..e1c60899fdbc 100644
--- a/drivers/xen/swiotlb-xen.c
+++ b/drivers/xen/swiotlb-xen.c
@@ -53,20 +53,6 @@
* API.
*/
-#ifndef CONFIG_X86
-static unsigned long dma_alloc_coherent_mask(struct device *dev,
- gfp_t gfp)
-{
- unsigned long dma_mask = 0;
-
- dma_mask = dev->coherent_dma_mask;
- if (!dma_mask)
- dma_mask = (gfp & GFP_DMA) ? DMA_BIT_MASK(24) : DMA_BIT_MASK(32);
-
- return dma_mask;
-}
-#endif
-
#define XEN_SWIOTLB_ERROR_CODE (~(dma_addr_t)0x0)
static char *xen_io_tlb_start, *xen_io_tlb_end;
@@ -328,7 +314,7 @@ xen_swiotlb_alloc_coherent(struct device *hwdev, size_t size,
return ret;
if (hwdev && hwdev->coherent_dma_mask)
- dma_mask = dma_alloc_coherent_mask(hwdev, flags);
+ dma_mask = hwdev->coherent_dma_mask;
/* At this point dma_handle is the physical address, next we are
* going to set it to the machine address.
diff --git a/drivers/xen/xen-acpi-processor.c b/drivers/xen/xen-acpi-processor.c
index 23e391d3ec01..b29f4e40851f 100644
--- a/drivers/xen/xen-acpi-processor.c
+++ b/drivers/xen/xen-acpi-processor.c
@@ -53,6 +53,8 @@ static unsigned long *acpi_ids_done;
static unsigned long *acpi_id_present;
/* And if there is an _CST definition (or a PBLK) for the ACPI IDs */
static unsigned long *acpi_id_cst_present;
+/* Which ACPI P-State dependencies for a enumerated processor */
+static struct acpi_psd_package *acpi_psd;
static int push_cxx_to_hypervisor(struct acpi_processor *_pr)
{
@@ -362,9 +364,9 @@ read_acpi_id(acpi_handle handle, u32 lvl, void *context, void **rv)
}
/* There are more ACPI Processor objects than in x2APIC or MADT.
* This can happen with incorrect ACPI SSDT declerations. */
- if (acpi_id > nr_acpi_bits) {
- pr_debug("We only have %u, trying to set %u\n",
- nr_acpi_bits, acpi_id);
+ if (acpi_id >= nr_acpi_bits) {
+ pr_debug("max acpi id %u, trying to set %u\n",
+ nr_acpi_bits - 1, acpi_id);
return AE_OK;
}
/* OK, There is a ACPI Processor object */
@@ -372,6 +374,13 @@ read_acpi_id(acpi_handle handle, u32 lvl, void *context, void **rv)
pr_debug("ACPI CPU%u w/ PBLK:0x%lx\n", acpi_id, (unsigned long)pblk);
+ /* It has P-state dependencies */
+ if (!acpi_processor_get_psd(handle, &acpi_psd[acpi_id])) {
+ pr_debug("ACPI CPU%u w/ PST:coord_type = %llu domain = %llu\n",
+ acpi_id, acpi_psd[acpi_id].coord_type,
+ acpi_psd[acpi_id].domain);
+ }
+
status = acpi_evaluate_object(handle, "_CST", NULL, &buffer);
if (ACPI_FAILURE(status)) {
if (!pblk)
@@ -405,6 +414,14 @@ static int check_acpi_ids(struct acpi_processor *pr_backup)
return -ENOMEM;
}
+ acpi_psd = kcalloc(nr_acpi_bits, sizeof(struct acpi_psd_package),
+ GFP_KERNEL);
+ if (!acpi_psd) {
+ kfree(acpi_id_present);
+ kfree(acpi_id_cst_present);
+ return -ENOMEM;
+ }
+
acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX,
read_acpi_id, NULL, NULL, NULL);
@@ -417,6 +434,12 @@ upload:
pr_backup->acpi_id = i;
/* Mask out C-states if there are no _CST or PBLK */
pr_backup->flags.power = test_bit(i, acpi_id_cst_present);
+ /* num_entries is non-zero if we evaluated _PSD */
+ if (acpi_psd[i].num_entries) {
+ memcpy(&pr_backup->performance->domain_info,
+ &acpi_psd[i],
+ sizeof(struct acpi_psd_package));
+ }
(void)upload_pm_data(pr_backup);
}
}
@@ -566,6 +589,7 @@ static void __exit xen_acpi_processor_exit(void)
kfree(acpi_ids_done);
kfree(acpi_id_present);
kfree(acpi_id_cst_present);
+ kfree(acpi_psd);
for_each_possible_cpu(i)
acpi_processor_unregister_performance(i);
diff --git a/drivers/xen/xen-pciback/conf_space_quirks.c b/drivers/xen/xen-pciback/conf_space_quirks.c
index 89d9744ece61..ed593d1042a6 100644
--- a/drivers/xen/xen-pciback/conf_space_quirks.c
+++ b/drivers/xen/xen-pciback/conf_space_quirks.c
@@ -95,7 +95,7 @@ int xen_pcibk_config_quirks_init(struct pci_dev *dev)
struct xen_pcibk_config_quirk *quirk;
int ret = 0;
- quirk = kzalloc(sizeof(*quirk), GFP_ATOMIC);
+ quirk = kzalloc(sizeof(*quirk), GFP_KERNEL);
if (!quirk) {
ret = -ENOMEM;
goto out;
diff --git a/drivers/xen/xen-pciback/pci_stub.c b/drivers/xen/xen-pciback/pci_stub.c
index 9e480fdebe1f..59661db144e5 100644
--- a/drivers/xen/xen-pciback/pci_stub.c
+++ b/drivers/xen/xen-pciback/pci_stub.c
@@ -71,7 +71,7 @@ static struct pcistub_device *pcistub_device_alloc(struct pci_dev *dev)
dev_dbg(&dev->dev, "pcistub_device_alloc\n");
- psdev = kzalloc(sizeof(*psdev), GFP_ATOMIC);
+ psdev = kzalloc(sizeof(*psdev), GFP_KERNEL);
if (!psdev)
return NULL;
@@ -364,7 +364,7 @@ static int pcistub_init_device(struct pci_dev *dev)
* here and then to call kfree(pci_get_drvdata(psdev->dev)).
*/
dev_data = kzalloc(sizeof(*dev_data) + strlen(DRV_NAME "[]")
- + strlen(pci_name(dev)) + 1, GFP_ATOMIC);
+ + strlen(pci_name(dev)) + 1, GFP_KERNEL);
if (!dev_data) {
err = -ENOMEM;
goto out;
@@ -577,7 +577,7 @@ static int pcistub_probe(struct pci_dev *dev, const struct pci_device_id *id)
}
if (!match) {
- pci_dev_id = kmalloc(sizeof(*pci_dev_id), GFP_ATOMIC);
+ pci_dev_id = kmalloc(sizeof(*pci_dev_id), GFP_KERNEL);
if (!pci_dev_id) {
err = -ENOMEM;
goto out;
@@ -1149,7 +1149,7 @@ static int pcistub_reg_add(int domain, int bus, int slot, int func,
}
dev = psdev->dev;
- field = kzalloc(sizeof(*field), GFP_ATOMIC);
+ field = kzalloc(sizeof(*field), GFP_KERNEL);
if (!field) {
err = -ENOMEM;
goto out;
diff --git a/drivers/xen/xenbus/xenbus_dev_frontend.c b/drivers/xen/xenbus/xenbus_dev_frontend.c
index a493e99bed21..c3e201025ef0 100644
--- a/drivers/xen/xenbus/xenbus_dev_frontend.c
+++ b/drivers/xen/xenbus/xenbus_dev_frontend.c
@@ -365,7 +365,7 @@ void xenbus_dev_queue_reply(struct xb_req_data *req)
if (WARN_ON(rc))
goto out;
}
- } else if (req->msg.type == XS_TRANSACTION_END) {
+ } else if (req->type == XS_TRANSACTION_END) {
trans = xenbus_get_transaction(u, req->msg.tx_id);
if (WARN_ON(!trans))
goto out;
@@ -403,7 +403,7 @@ static int xenbus_command_reply(struct xenbus_file_priv *u,
{
struct {
struct xsd_sockmsg hdr;
- const char body[16];
+ char body[16];
} msg;
int rc;
@@ -412,6 +412,7 @@ static int xenbus_command_reply(struct xenbus_file_priv *u,
msg.hdr.len = strlen(reply) + 1;
if (msg.hdr.len > sizeof(msg.body))
return -E2BIG;
+ memcpy(&msg.body, reply, msg.hdr.len);
mutex_lock(&u->reply_mutex);
rc = queue_reply(&u->read_buffers, &msg, sizeof(msg.hdr) + msg.hdr.len);
@@ -429,6 +430,10 @@ static int xenbus_write_transaction(unsigned msg_type,
{
int rc;
struct xenbus_transaction_holder *trans = NULL;
+ struct {
+ struct xsd_sockmsg hdr;
+ char body[];
+ } *msg = (void *)u->u.buffer;
if (msg_type == XS_TRANSACTION_START) {
trans = kzalloc(sizeof(*trans), GFP_KERNEL);
@@ -437,11 +442,15 @@ static int xenbus_write_transaction(unsigned msg_type,
goto out;
}
list_add(&trans->list, &u->transactions);
- } else if (u->u.msg.tx_id != 0 &&
- !xenbus_get_transaction(u, u->u.msg.tx_id))
+ } else if (msg->hdr.tx_id != 0 &&
+ !xenbus_get_transaction(u, msg->hdr.tx_id))
return xenbus_command_reply(u, XS_ERROR, "ENOENT");
+ else if (msg_type == XS_TRANSACTION_END &&
+ !(msg->hdr.len == 2 &&
+ (!strcmp(msg->body, "T") || !strcmp(msg->body, "F"))))
+ return xenbus_command_reply(u, XS_ERROR, "EINVAL");
- rc = xenbus_dev_request_and_reply(&u->u.msg, u);
+ rc = xenbus_dev_request_and_reply(&msg->hdr, u);
if (rc && trans) {
list_del(&trans->list);
kfree(trans);
diff --git a/drivers/xen/xenbus/xenbus_xs.c b/drivers/xen/xenbus/xenbus_xs.c
index 3f3b29398ab8..49a3874ae6bb 100644
--- a/drivers/xen/xenbus/xenbus_xs.c
+++ b/drivers/xen/xenbus/xenbus_xs.c
@@ -140,7 +140,9 @@ void xs_request_exit(struct xb_req_data *req)
spin_lock(&xs_state_lock);
xs_state_users--;
if ((req->type == XS_TRANSACTION_START && req->msg.type == XS_ERROR) ||
- req->type == XS_TRANSACTION_END)
+ (req->type == XS_TRANSACTION_END &&
+ !WARN_ON_ONCE(req->msg.type == XS_ERROR &&
+ !strcmp(req->body, "ENOENT"))))
xs_state_users--;
spin_unlock(&xs_state_lock);
diff --git a/drivers/zorro/zorro.c b/drivers/zorro/zorro.c
index cc1b1ac57d61..47728477297e 100644
--- a/drivers/zorro/zorro.c
+++ b/drivers/zorro/zorro.c
@@ -16,6 +16,7 @@
#include <linux/bitops.h>
#include <linux/string.h>
#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <asm/byteorder.h>
@@ -185,6 +186,17 @@ static int __init amiga_zorro_probe(struct platform_device *pdev)
z->dev.parent = &bus->dev;
z->dev.bus = &zorro_bus_type;
z->dev.id = i;
+ switch (z->rom.er_Type & ERT_TYPEMASK) {
+ case ERT_ZORROIII:
+ z->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+ break;
+
+ case ERT_ZORROII:
+ default:
+ z->dev.coherent_dma_mask = DMA_BIT_MASK(24);
+ break;
+ }
+ z->dev.dma_mask = &z->dev.coherent_dma_mask;
}
/* ... then register them */